[REF] web: adaptations to use simple tap to add an event on mobile
Before this commit, the only way to add an event was using a long tap. After this commit, a short tap will be added. So to summarize on Mobile there is two methods to add an event using the calendar: short tap and long tap. For month/year views when a user tap on a "Free Zone" (no event) the view is changed in the "day view" for this clicked day. Task ID: 1891957 closes odoo/odoo#64615 Related: odoo/enterprise#15785 Signed-off-by: Adrien Dieudonné (adr) <adr@odoo.com>
This commit is contained in:
@@ -236,6 +236,9 @@ var CalendarController = AbstractController.extend({
|
||||
// When clicking on a random day of a random other week, switch to week view
|
||||
this.model.setScale('week');
|
||||
}
|
||||
if (event.data.scale) {
|
||||
this.model.setScale(event.data.scale);
|
||||
}
|
||||
this.model.setDate(event.data.date);
|
||||
this.reload();
|
||||
},
|
||||
|
||||
@@ -338,6 +338,7 @@ return AbstractRenderer.extend({
|
||||
r_end: fc4Event.extendedProps.r_end && moment(fc4Event.extendedProps.r_end).utcOffset(0, true),
|
||||
record: fc4Event.extendedProps.record,
|
||||
attendees: fc4Event.extendedProps.attendees,
|
||||
disableQuickCreate: fc4Event.extendedProps.disableQuickCreate,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -375,6 +376,20 @@ return AbstractRenderer.extend({
|
||||
return field_utils.format[field.type](record[fieldName], field, {forceString: true});
|
||||
}
|
||||
},
|
||||
_preOpenCreate: function (data) {
|
||||
if (this.$('.o_cw_popover').length) {
|
||||
this._unselectEvent();
|
||||
}
|
||||
if (this.state.context.default_name) {
|
||||
data.title = this.state.context.default_name;
|
||||
}
|
||||
this.trigger_up('openCreate', this._convertEventToFC3Event(data));
|
||||
if (this.state.scale === 'year') {
|
||||
this.calendar.view.unselect();
|
||||
} else {
|
||||
this.calendar.unselect();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Return the Object options for FullCalendar
|
||||
*
|
||||
@@ -405,7 +420,7 @@ return AbstractRenderer.extend({
|
||||
eventClickInfo.jsEvent.stopPropagation();
|
||||
var eventData = eventClickInfo.event;
|
||||
self._unselectEvent();
|
||||
$(self.calendarElement).find(_.str.sprintf('[data-event-id=%s]', eventData.id)).addClass('o_cw_custom_highlight');
|
||||
$(self.calendarElement).find(`[data-event-id=${eventData.id}]`).addClass('o_cw_custom_highlight');
|
||||
self._renderEventPopover(eventData, $(eventClickInfo.el));
|
||||
},
|
||||
yearDateClick: function (info) {
|
||||
@@ -427,20 +442,8 @@ return AbstractRenderer.extend({
|
||||
}
|
||||
},
|
||||
select: function (selectionInfo) {
|
||||
// Clicking on the view, dispose any visible popover. Otherwise create a new event.
|
||||
if (self.$('.o_cw_popover').length) {
|
||||
self._unselectEvent();
|
||||
}
|
||||
var data = {start: selectionInfo.start, end: selectionInfo.end, allDay: selectionInfo.allDay};
|
||||
if (self.state.context.default_name) {
|
||||
data.title = self.state.context.default_name;
|
||||
}
|
||||
self.trigger_up('openCreate', self._convertEventToFC3Event(data));
|
||||
if (self.state.scale === 'year') {
|
||||
self.calendar.view.unselect();
|
||||
} else {
|
||||
self.calendar.unselect();
|
||||
}
|
||||
self._preOpenCreate(data);
|
||||
},
|
||||
eventRender: function (info) {
|
||||
var event = info.event;
|
||||
@@ -503,20 +506,20 @@ return AbstractRenderer.extend({
|
||||
// The css ":hover" selector can't be used because these events
|
||||
// are rendered using multiple elements.
|
||||
eventMouseEnter: function (mouseEnterInfo) {
|
||||
$(self.calendarElement).find(_.str.sprintf('[data-event-id=%s]', mouseEnterInfo.event.id)).addClass('o_cw_custom_hover');
|
||||
$(self.calendarElement).find(`[data-event-id=${mouseEnterInfo.event.id}]`).addClass('o_cw_custom_hover');
|
||||
},
|
||||
eventMouseLeave: function (mouseLeaveInfo) {
|
||||
if (!mouseLeaveInfo.event.id) {
|
||||
return;
|
||||
}
|
||||
$(self.calendarElement).find(_.str.sprintf('[data-event-id=%s]', mouseLeaveInfo.event.id)).removeClass('o_cw_custom_hover');
|
||||
$(self.calendarElement).find(`[data-event-id=${mouseLeaveInfo.event.id}]`).removeClass('o_cw_custom_hover');
|
||||
},
|
||||
eventDragStart: function (mouseDragInfo) {
|
||||
$(self.calendarElement).find(_.str.sprintf('[data-event-id=%s]', mouseDragInfo.event.id)).addClass('o_cw_custom_hover');
|
||||
$(self.calendarElement).find(`[data-event-id=${mouseDragInfo.event.id}]`).addClass('o_cw_custom_hover');
|
||||
self._unselectEvent();
|
||||
},
|
||||
eventResizeStart: function (mouseResizeInfo) {
|
||||
$(self.calendarElement).find(_.str.sprintf('[data-event-id=%s]', mouseResizeInfo.event.id)).addClass('o_cw_custom_hover');
|
||||
$(self.calendarElement).find(`[data-event-id=${mouseResizeInfo.event.id}]`).addClass('o_cw_custom_hover');
|
||||
self._unselectEvent();
|
||||
},
|
||||
eventLimitClick: function () {
|
||||
@@ -709,7 +712,7 @@ return AbstractRenderer.extend({
|
||||
if (options.avatar_field) {
|
||||
_.each(options.filters, function (filter) {
|
||||
if (!['all', false].includes(filter.value)) {
|
||||
var selector = _.str.sprintf('.o_calendar_filter_item[data-value=%s]', filter.value);
|
||||
var selector = `.o_calendar_filter_item[data-value=${filter.value}]`;
|
||||
sidebarFilter.$el.find(selector).popover({
|
||||
animation: false,
|
||||
trigger: 'hover',
|
||||
@@ -719,7 +722,7 @@ return AbstractRenderer.extend({
|
||||
delay: {show: 300, hide: 0},
|
||||
content: function () {
|
||||
return $('<img>', {
|
||||
src: _.str.sprintf('/web/image/%s/%s/%s', options.avatar_model, filter.value, options.avatar_field),
|
||||
src: `/web/image/${options.avatar_model}/${filter.value}/${options.avatar_field}}`,
|
||||
class: 'mx-auto',
|
||||
});
|
||||
},
|
||||
|
||||
@@ -197,6 +197,7 @@ odoo.define('web.test_utils', async function (require) {
|
||||
triggerKeypressEvent: testUtilsDom.triggerKeypressEvent,
|
||||
triggerMouseEvent: testUtilsDom.triggerMouseEvent,
|
||||
triggerPositionalMouseEvent: testUtilsDom.triggerPositionalMouseEvent,
|
||||
triggerPositionalTapEvents: testUtilsDom.triggerPositionalTapEvents,
|
||||
dragAndDrop: testUtilsDom.dragAndDrop,
|
||||
find: testUtilsDom.findItem,
|
||||
getNode: testUtilsDom.getNode,
|
||||
|
||||
@@ -33,6 +33,17 @@ odoo.define('web.test_utils_dom', function (require) {
|
||||
clientY: args ? args.pageY : undefined,
|
||||
view: window,
|
||||
});
|
||||
const touchEventMapping = args => Object.assign({}, args, {
|
||||
cancelable: true,
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
view: window,
|
||||
rotation: 0.0,
|
||||
zoom: 1.0,
|
||||
});
|
||||
const touchEventCancelMapping = args => Object.assign({}, touchEventMapping(args), {
|
||||
cancelable: false,
|
||||
});
|
||||
const noBubble = args => Object.assign({}, args, { bubbles: false });
|
||||
const onlyBubble = args => Object.assign({}, args, { bubbles: true });
|
||||
// TriggerEvent constructor/args processor mapping
|
||||
@@ -70,6 +81,11 @@ odoo.define('web.test_utils_dom', function (require) {
|
||||
dragover: { constructor: DragEvent, processParameters: onlyBubble },
|
||||
drop: { constructor: DragEvent, processParameters: onlyBubble },
|
||||
|
||||
touchstart: { constructor: TouchEvent, processParameters: touchEventMapping },
|
||||
touchend: { constructor: TouchEvent, processParameters: touchEventMapping },
|
||||
touchmove: { constructor: TouchEvent, processParameters: touchEventMapping },
|
||||
touchcancel: { constructor: TouchEvent, processParameters: touchEventCancelMapping },
|
||||
|
||||
input: { constructor: InputEvent, processParameters: onlyBubble },
|
||||
|
||||
compositionstart: { constructor: CompositionEvent, processParameters: onlyBubble },
|
||||
@@ -533,6 +549,39 @@ odoo.define('web.test_utils_dom', function (require) {
|
||||
return el;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulate a "TAP" (touch) event with a custom position x and y.
|
||||
*
|
||||
* @param {number} x
|
||||
* @param {number} y
|
||||
* @returns {HTMLElement}
|
||||
*/
|
||||
async function triggerPositionalTapEvents(x, y) {
|
||||
const element = document.elementFromPoint(x, y);
|
||||
const touch = new Touch({
|
||||
identifier: 0,
|
||||
target: element,
|
||||
clientX: x,
|
||||
clientY: y,
|
||||
pageX: x,
|
||||
pageY: y,
|
||||
});
|
||||
await triggerEvent(element, 'touchstart', {
|
||||
touches: [touch],
|
||||
targetTouches: [touch],
|
||||
changedTouches: [touch],
|
||||
});
|
||||
await triggerEvent(element, 'touchmove', {
|
||||
touches: [touch],
|
||||
targetTouches: [touch],
|
||||
changedTouches: [touch],
|
||||
});
|
||||
await triggerEvent(element, 'touchend', {
|
||||
changedTouches: [touch],
|
||||
});
|
||||
return element;
|
||||
}
|
||||
|
||||
return {
|
||||
click,
|
||||
clickFirst,
|
||||
@@ -547,5 +596,6 @@ odoo.define('web.test_utils_dom', function (require) {
|
||||
triggerKeypressEvent,
|
||||
triggerMouseEvent,
|
||||
triggerPositionalMouseEvent,
|
||||
triggerPositionalTapEvents,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -3938,6 +3938,56 @@ QUnit.module('Views', {
|
||||
|
||||
calendar.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('calendar: disableQuickCreate in data event', async function (assert) {
|
||||
assert.expect(3);
|
||||
|
||||
testUtils.mock.patch(CalendarRenderer, {
|
||||
_preOpenCreate: function (data) {
|
||||
data = Object.assign({}, data, {
|
||||
extendedProps: {disableQuickCreate: true},
|
||||
});
|
||||
return this._super.call(this, data);
|
||||
},
|
||||
});
|
||||
|
||||
let calendar = await testUtils.createCalendarView({
|
||||
View: CalendarView,
|
||||
model: 'event',
|
||||
data: this.data,
|
||||
arch: `<calendar mode="day" date_start="start" date_stop="stop"></calendar>`,
|
||||
viewOptions: {
|
||||
initialDate: initialDate,
|
||||
},
|
||||
intercepts: {
|
||||
do_action(ev) {
|
||||
assert.step('do_action');
|
||||
assert.deepEqual(ev.data.action.context, {
|
||||
default_start: "2016-12-12 06:30:00",
|
||||
default_stop: "2016-12-12 11:00:00",
|
||||
}, "should send the correct data to create events");
|
||||
},
|
||||
},
|
||||
}, {positionalClicks: true});
|
||||
|
||||
// Create event
|
||||
var $initCell = calendar.$('.fc-time-grid .fc-minor[data-time="06:30:00"] .fc-widget-content:last-child');
|
||||
var $endCell = calendar.$('.fc-time-grid .fc-minor[data-time="10:30:00"] .fc-widget-content:last-child');
|
||||
|
||||
var left = $initCell.offset().left;
|
||||
var top = $initCell.offset().top;
|
||||
testUtils.dom.triggerPositionalMouseEvent(left, top, "mousedown");
|
||||
top = $endCell.offset().top;
|
||||
testUtils.dom.triggerPositionalMouseEvent(left, top, "mousemove");
|
||||
testUtils.dom.triggerPositionalMouseEvent(left, top, "mouseup");
|
||||
await testUtils.nextTick();
|
||||
|
||||
assert.verifySteps(['do_action']);
|
||||
|
||||
calendar.destroy();
|
||||
|
||||
testUtils.mock.unpatch(CalendarRenderer);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user