@miso.ai/client-sdk-ui 1.11.4 → 1.11.5-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/actor/views.js +1 -0
- package/dist/cjs/asset/svgs.js +10 -4
- package/dist/cjs/constants.js +3 -2
- package/dist/cjs/element/role/index.js +7 -0
- package/dist/cjs/element/role/miso-images.js +20 -0
- package/dist/cjs/layout/input/option-list.js +1 -1
- package/dist/cjs/layout/input/select.js +1 -1
- package/dist/cjs/layout/list/carousel.js +43 -84
- package/dist/cjs/layout/list/collection.js +11 -5
- package/dist/cjs/layout/mixin/swipeable.js +165 -0
- package/dist/cjs/layout/{trackable.js → mixin/trackable.js} +4 -3
- package/dist/cjs/layout/{triggerable.js → mixin/triggerable.js} +2 -2
- package/dist/cjs/layout/radio/radio.js +11 -7
- package/dist/cjs/layout/templates.js +24 -5
- package/dist/cjs/layout/text/typewriter/index.js +2 -0
- package/dist/cjs/util/trackers.js +18 -5
- package/dist/cjs/version.js +1 -1
- package/dist/cjs/workflow/answer-based.js +23 -4
- package/dist/cjs/workflow/search.js +0 -1
- package/package.json +2 -2
- package/src/actor/views.js +1 -0
- package/src/asset/svgs.js +9 -1
- package/src/constants.js +3 -0
- package/src/element/role/index.js +1 -0
- package/src/element/role/miso-images.js +16 -0
- package/src/layout/input/option-list.js +1 -1
- package/src/layout/input/select.js +1 -1
- package/src/layout/list/carousel.js +39 -86
- package/src/layout/list/collection.js +13 -6
- package/src/layout/mixin/swipeable.js +162 -0
- package/src/layout/{trackable.js → mixin/trackable.js} +4 -3
- package/src/layout/{triggerable.js → mixin/triggerable.js} +2 -2
- package/src/layout/radio/radio.js +7 -7
- package/src/layout/templates.js +26 -5
- package/src/layout/text/typewriter/index.js +2 -1
- package/src/util/trackers.js +17 -4
- package/src/version.js +1 -1
- package/src/workflow/answer-based.js +14 -4
- package/src/workflow/search.js +0 -1
|
@@ -3,13 +3,20 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
+
exports.isTracked = isTracked;
|
|
7
|
+
exports.markAsTracked = markAsTracked;
|
|
6
8
|
exports.normalizeTrackerOptions = normalizeTrackerOptions;
|
|
7
9
|
exports.validateClick = validateClick;
|
|
8
10
|
var _commons = require("@miso.ai/commons");
|
|
9
11
|
const _excluded = ["impression", "viewable", "click"];
|
|
10
12
|
function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
const TRACKED = Symbol.for('miso:tracked');
|
|
14
|
+
function isTracked(event) {
|
|
15
|
+
return !!event[TRACKED];
|
|
16
|
+
}
|
|
17
|
+
function markAsTracked(event) {
|
|
18
|
+
event[TRACKED] = true;
|
|
19
|
+
}
|
|
13
20
|
const DEFAULT_TRACKER_OPTIONS = Object.freeze({
|
|
14
21
|
impression: Object.freeze({}),
|
|
15
22
|
viewable: Object.freeze({
|
|
@@ -24,6 +31,8 @@ function mergeOptions(def, opt) {
|
|
|
24
31
|
// if opt is falsy then return false, merge otherwise
|
|
25
32
|
return !!opt && Object.assign({}, def, opt);
|
|
26
33
|
}
|
|
34
|
+
|
|
35
|
+
// TODO: don't need this anymore
|
|
27
36
|
function normalizeTrackerOptions(options) {
|
|
28
37
|
// TODO: make this { active: false } to keep it an object
|
|
29
38
|
if (options === false) {
|
|
@@ -61,15 +70,19 @@ function validateClick() {
|
|
|
61
70
|
}
|
|
62
71
|
|
|
63
72
|
// standard criteria
|
|
64
|
-
// 1.
|
|
73
|
+
// 1. each event can only be tracked once
|
|
74
|
+
if (isTracked(event)) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
// 2. it must be a left click
|
|
65
78
|
if (event.button !== 0) {
|
|
66
79
|
return false;
|
|
67
80
|
}
|
|
68
|
-
//
|
|
81
|
+
// 3. event default must not be prevented
|
|
69
82
|
if (event.defaultPrevented) {
|
|
70
83
|
return false;
|
|
71
84
|
}
|
|
72
|
-
//
|
|
85
|
+
// 4. must go through a real link
|
|
73
86
|
if (!(0, _commons.findInAncestors)(event.target, element => element.tagName === 'A' && element.href || undefined)) {
|
|
74
87
|
return false;
|
|
75
88
|
}
|
package/dist/cjs/version.js
CHANGED
|
@@ -13,7 +13,9 @@ var _index3 = require("../affiliation/index.js");
|
|
|
13
13
|
var _index4 = require("./options/index.js");
|
|
14
14
|
var _processors = require("./processors.js");
|
|
15
15
|
var _useLink = require("./use-link.js");
|
|
16
|
-
|
|
16
|
+
var _trackers = require("../util/trackers.js");
|
|
17
|
+
const _excluded = ["setValue", "focus", "param", "sourceParam"],
|
|
18
|
+
_excluded2 = ["event"];
|
|
17
19
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
18
20
|
function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
|
|
19
21
|
const DEFAULT_API_OPTIONS = Object.freeze(Object.assign({}, _base.default.DEFAULT_API_OPTIONS, {
|
|
@@ -41,6 +43,10 @@ const DEFAULT_LAYOUTS = Object.freeze(Object.assign({}, _base.default.DEFAULT_LA
|
|
|
41
43
|
}],
|
|
42
44
|
[_constants.ROLE.ANSWER]: _index2.TypewriterLayout.type,
|
|
43
45
|
[_constants.ROLE.FEEDBACK]: _index2.FeedbackLayout.type,
|
|
46
|
+
[_constants.ROLE.IMAGES]: [_index2.CarouselLayout.type, {
|
|
47
|
+
incremental: true,
|
|
48
|
+
itemType: 'image'
|
|
49
|
+
}],
|
|
44
50
|
[_constants.ROLE.SOURCES]: [_index2.ListLayout.type, {
|
|
45
51
|
incremental: true,
|
|
46
52
|
itemType: 'article',
|
|
@@ -63,6 +69,7 @@ const DEFAULT_TRACKERS = Object.freeze(Object.assign({}, _base.default.DEFAULT_T
|
|
|
63
69
|
deduplicated: false,
|
|
64
70
|
click: _index4.DEFAULT_TRACKER_OPTIONS.click // click only
|
|
65
71
|
},
|
|
72
|
+
[_constants.ROLE.IMAGES]: _index4.DEFAULT_TRACKER_OPTIONS,
|
|
66
73
|
[_constants.ROLE.SOURCES]: _index4.DEFAULT_TRACKER_OPTIONS,
|
|
67
74
|
[_constants.ROLE.AFFILIATION]: _index4.DEFAULT_TRACKER_OPTIONS,
|
|
68
75
|
[_constants.ROLE.PRODUCTS]: _index4.DEFAULT_TRACKER_OPTIONS
|
|
@@ -306,7 +313,6 @@ class AnswerBasedWorkflow extends _base.default {
|
|
|
306
313
|
if (args.role !== _constants.ROLE.ANSWER) {
|
|
307
314
|
return payload;
|
|
308
315
|
}
|
|
309
|
-
console.log('writeAnswerClickInteraction', payload, args);
|
|
310
316
|
const {
|
|
311
317
|
items = []
|
|
312
318
|
} = args;
|
|
@@ -334,8 +340,12 @@ class AnswerBasedWorkflow extends _base.default {
|
|
|
334
340
|
// handlers //
|
|
335
341
|
_onCitationClick(_ref4) {
|
|
336
342
|
let {
|
|
337
|
-
index
|
|
343
|
+
index,
|
|
344
|
+
event
|
|
338
345
|
} = _ref4;
|
|
346
|
+
if ((0, _trackers.isTracked)(event)) {
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
339
349
|
const {
|
|
340
350
|
value
|
|
341
351
|
} = this.states[_index.fields.data()] || {};
|
|
@@ -348,9 +358,18 @@ class AnswerBasedWorkflow extends _base.default {
|
|
|
348
358
|
return;
|
|
349
359
|
}
|
|
350
360
|
// TODO: should we track impression as well?
|
|
361
|
+
(0, _trackers.markAsTracked)(event);
|
|
351
362
|
this._views.trackers.sources.click([source.product_id]);
|
|
352
363
|
}
|
|
353
|
-
_onAnswerLinkClick(
|
|
364
|
+
_onAnswerLinkClick(_ref5) {
|
|
365
|
+
let {
|
|
366
|
+
event
|
|
367
|
+
} = _ref5,
|
|
368
|
+
item = _objectWithoutPropertiesLoose(_ref5, _excluded2);
|
|
369
|
+
if ((0, _trackers.isTracked)(event)) {
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
(0, _trackers.markAsTracked)(event);
|
|
354
373
|
// put everything into args and let _defaultProcessInteraction() handles it
|
|
355
374
|
// for it's hard to make it a standard item
|
|
356
375
|
this._views.trackers.answer.click([], {
|
package/package.json
CHANGED
package/src/actor/views.js
CHANGED
|
@@ -213,6 +213,7 @@ export default class ViewsActor {
|
|
|
213
213
|
|
|
214
214
|
_getTrackerOptions(role) {
|
|
215
215
|
const allTrackerOptions = this._options.resolved.trackers;
|
|
216
|
+
// TODO: don't need this anymore
|
|
216
217
|
return normalizeTrackerOptions((allTrackerOptions && allTrackerOptions[role]) || false);
|
|
217
218
|
}
|
|
218
219
|
|
package/src/asset/svgs.js
CHANGED
|
@@ -14,6 +14,8 @@ const ICON_NAME = {
|
|
|
14
14
|
SEND: 'send',
|
|
15
15
|
SEARCH: 'search',
|
|
16
16
|
TRIANGLE: 'triangle',
|
|
17
|
+
TRIANGLE_EQ: 'triangle-eq',
|
|
18
|
+
CHEVRON_NEGATIVE: 'chevron-negative',
|
|
17
19
|
};
|
|
18
20
|
|
|
19
21
|
// CC0 SVGs
|
|
@@ -23,7 +25,11 @@ const ICON_SEARCH = `<symbol id="${PREFIX}-${ICON_NAME.SEARCH}" viewBox="0 0 24
|
|
|
23
25
|
// Custom SVGs
|
|
24
26
|
const ICON_TRIANGLE = `<symbol id="${PREFIX}-${ICON_NAME.TRIANGLE}" viewBox="-5 -5 105 105"><path d="${trianglePath(5)}"/></symbol>`;
|
|
25
27
|
|
|
26
|
-
const
|
|
28
|
+
const ICON_TRIANGLE_EQ = `<symbol id="${PREFIX}-${ICON_NAME.TRIANGLE_EQ}" viewBox="-100 -100 200 200"><path d="M12.99 -77.5A15 15,0,0,0,-12.99 -77.5L-73.61 27.5A15 15,0,0,0,-60.62 50L60.62 50A15 15,0,0,0,73.61 27.5L12.99 -77.5Z"/></symbol>`;
|
|
29
|
+
|
|
30
|
+
const ICON_CHEVRON_NEGATIVE = `<symbol id="${PREFIX}-${ICON_NAME.CHEVRON_NEGATIVE}" viewBox="-100 -100 200 200"><path d="M100 100V-200H-200V200H200ZM-10.61 -43.94A15 15,0,0,1,10.61 -43.94L60.61 6.06A15 15,0,0,1,39.39 27.27L0 -12.12L-39.39 27.27A15 15,0,0,1,-60.61 6.06L-10.61 -43.94Z"/></symbol>`;
|
|
31
|
+
|
|
32
|
+
const ICONS = [ICON_SEND, ICON_SEARCH, ICON_TRIANGLE, ICON_TRIANGLE_EQ, ICON_CHEVRON_NEGATIVE];
|
|
27
33
|
|
|
28
34
|
const SPRITES_SVG = `<svg id="${ID}" style="display:none;">${ICONS.join('')}</svg>`;
|
|
29
35
|
|
|
@@ -52,3 +58,5 @@ export function getIcon(name) {
|
|
|
52
58
|
export const SEND = _icon(ICON_NAME.SEND);
|
|
53
59
|
export const SEARCH = _icon(ICON_NAME.SEARCH);
|
|
54
60
|
export const TRIANGLE = _icon(ICON_NAME.TRIANGLE);
|
|
61
|
+
export const TRIANGLE_EQ = _icon(ICON_NAME.TRIANGLE_EQ);
|
|
62
|
+
export const CHEVRON_NEGATIVE = _icon(ICON_NAME.CHEVRON_NEGATIVE);
|
package/src/constants.js
CHANGED
|
@@ -15,6 +15,7 @@ export const ROLE = Object.freeze({
|
|
|
15
15
|
ITEMS: 'items',
|
|
16
16
|
QUESTION: 'question',
|
|
17
17
|
ANSWER: 'answer',
|
|
18
|
+
IMAGES: 'images',
|
|
18
19
|
SOURCES: 'sources',
|
|
19
20
|
RELATED_RESOURCES: 'related_resources',
|
|
20
21
|
QUERY_SUGGESTIONS: 'query_suggestions',
|
|
@@ -36,6 +37,7 @@ const DATA_ROLE_SET = new Set([
|
|
|
36
37
|
ROLE.ITEMS,
|
|
37
38
|
ROLE.QUESTION,
|
|
38
39
|
ROLE.ANSWER,
|
|
40
|
+
ROLE.IMAGES,
|
|
39
41
|
ROLE.SOURCES,
|
|
40
42
|
ROLE.RELATED_RESOURCES,
|
|
41
43
|
ROLE.QUERY_SUGGESTIONS,
|
|
@@ -51,6 +53,7 @@ export function isDataRole(role) {
|
|
|
51
53
|
|
|
52
54
|
const PRODUCT_ROLE_SET = new Set([
|
|
53
55
|
ROLE.PRODUCTS,
|
|
56
|
+
ROLE.IMAGES,
|
|
54
57
|
ROLE.SOURCES,
|
|
55
58
|
ROLE.RELATED_RESOURCES,
|
|
56
59
|
]);
|
|
@@ -7,6 +7,7 @@ export { default as MisoProductsElement } from './miso-products.js';
|
|
|
7
7
|
export { default as MisoQuestionElement } from './miso-question.js';
|
|
8
8
|
export { default as MisoAnswerElement } from './miso-answer.js';
|
|
9
9
|
export { default as MisoSourcesElement } from './miso-sources.js';
|
|
10
|
+
export { default as MisoImagesElement } from './miso-images.js';
|
|
10
11
|
export { default as MisoRelatedResourcesElement } from './miso-related-resources.js';
|
|
11
12
|
export { default as MisoRelatedQuestionsElement } from './miso-related-questions.js';
|
|
12
13
|
export { default as MisoSovrnAffiliationElement } from './miso-affiliation.js';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import MisoComponentElement from './miso-component.js';
|
|
2
|
+
import { ROLE } from '../../constants.js';
|
|
3
|
+
|
|
4
|
+
export default class MisoImagesElement extends MisoComponentElement {
|
|
5
|
+
|
|
6
|
+
constructor() {
|
|
7
|
+
super({
|
|
8
|
+
role: ROLE.IMAGES,
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
static get tagName() {
|
|
13
|
+
return 'miso-images';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { escapeHtml } from '@miso.ai/commons';
|
|
2
2
|
import { fields } from '../../actor/index.js';
|
|
3
3
|
import TemplateBasedLayout from '../template.js';
|
|
4
|
-
import { makeTrackable } from '../trackable.js';
|
|
4
|
+
import { makeTrackable } from '../mixin/trackable.js';
|
|
5
5
|
|
|
6
6
|
const TYPE = 'option-list';
|
|
7
7
|
const DEFAULT_CLASSNAME = 'miso-option-list';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { escapeHtml, kebabOrSnakeToHuman } from '@miso.ai/commons';
|
|
2
2
|
import TemplateBasedLayout from '../template.js';
|
|
3
|
-
import { makeTrackable } from '../trackable.js';
|
|
3
|
+
import { makeTrackable } from '../mixin/trackable.js';
|
|
4
4
|
|
|
5
5
|
const TYPE = 'select';
|
|
6
6
|
const DEFAULT_CLASSNAME = 'miso-select';
|
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
import CollectionLayout from './collection.js';
|
|
2
|
+
import { makeSwipeable } from '../mixin/swipeable.js';
|
|
3
|
+
import { CHEVRON_NEGATIVE } from '../../asset/svgs.js';
|
|
2
4
|
|
|
3
5
|
const TYPE = 'carousel';
|
|
4
6
|
const DEFAULT_CLASSNAME = 'miso-carousel';
|
|
5
7
|
|
|
6
8
|
const DEFAULT_TEMPLATES = Object.freeze({
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
body,
|
|
10
|
+
controls,
|
|
11
|
+
controlContainer,
|
|
12
|
+
control,
|
|
13
|
+
controlIcon,
|
|
14
|
+
indexIndicator,
|
|
15
|
+
indexIndicatorItem: () => ``,
|
|
16
|
+
trigger: () => ``, // no trigger element
|
|
11
17
|
});
|
|
12
18
|
|
|
13
19
|
const INHERITED_DEFAULT_TEMPLATES = Object.freeze({
|
|
@@ -15,22 +21,33 @@ const INHERITED_DEFAULT_TEMPLATES = Object.freeze({
|
|
|
15
21
|
...DEFAULT_TEMPLATES,
|
|
16
22
|
});
|
|
17
23
|
|
|
18
|
-
function
|
|
24
|
+
function body(layout, state, values) {
|
|
25
|
+
const { className, templates } = layout;
|
|
26
|
+
return `<div class="${className}__viewport"><div class="${className}__viewport-inner">${templates.list(layout, state, values)}</div></div>${templates.controls(layout, state, values)}`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function controls(layout, state, values) {
|
|
19
30
|
const { templates } = layout;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
31
|
+
return `${templates.controlContainer(layout, state, 'previous')}${templates.controlContainer(layout, state, 'next')}${templates.indexIndicator(layout, state, values)}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function controlContainer(layout, state, direction) {
|
|
35
|
+
const { className, templates } = layout;
|
|
36
|
+
return `<div class="${className}__control-container-${direction}">${templates.control(layout, state, direction)}</div>`;
|
|
24
37
|
}
|
|
25
38
|
|
|
26
|
-
function
|
|
39
|
+
function control(layout, state, direction) {
|
|
27
40
|
const { className, templates } = layout;
|
|
28
|
-
return `<div class="${className}__control
|
|
41
|
+
return `<div class="${className}__control-${direction}" data-role="${direction}">${templates.controlIcon(layout, state, direction)}</div>`;
|
|
29
42
|
}
|
|
30
43
|
|
|
31
|
-
function
|
|
32
|
-
|
|
33
|
-
|
|
44
|
+
function controlIcon(layout, state, direction) {
|
|
45
|
+
return CHEVRON_NEGATIVE;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function indexIndicator(layout, state, values) {
|
|
49
|
+
const { className, templates } = layout;
|
|
50
|
+
return `<ul class="${className}__index-indicator" data-role="index-indicator">${values.map(value => `<li class="${className}__index-indicator-item">${templates.indexIndicatorItem(layout, state, value)}</li>`).join('')}</ul>`;
|
|
34
51
|
}
|
|
35
52
|
|
|
36
53
|
export default class CarouselLayout extends CollectionLayout {
|
|
@@ -49,88 +66,24 @@ export default class CarouselLayout extends CollectionLayout {
|
|
|
49
66
|
|
|
50
67
|
constructor({ className = DEFAULT_CLASSNAME, templates, ...options } = {}) {
|
|
51
68
|
super({ className, templates: { ...DEFAULT_TEMPLATES, ...templates }, ...options });
|
|
52
|
-
|
|
53
|
-
this._page = 0;
|
|
54
|
-
this._unsubscribes = [];
|
|
69
|
+
this._initSwipeable();
|
|
55
70
|
}
|
|
56
71
|
|
|
57
72
|
_render(element, states, controls) {
|
|
58
73
|
super._render(element, states, controls);
|
|
59
|
-
|
|
60
|
-
const items = this._getItems(state);
|
|
61
|
-
this._itemCount = items ? items.length : undefined;
|
|
62
|
-
this.syncSize();
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
get page() {
|
|
66
|
-
return this._page;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
get pageSize() {
|
|
70
|
-
return this._pageSize;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
next() {
|
|
74
|
-
this._setPage(this._page + 1);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
previous() {
|
|
78
|
-
this._setPage(this._page - 1);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
syncSize() {
|
|
82
|
-
if (this._itemCount === undefined) {
|
|
83
|
-
return; // data not ready yet
|
|
84
|
-
}
|
|
85
|
-
const grid = this._findGridElement();
|
|
86
|
-
if (!grid) {
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
// update page size, page count and new page index
|
|
90
|
-
const firstShownItemIndex = this._pageSize === undefined ? 0 : this._page * this._pageSize;
|
|
91
|
-
const pageSize = this._pageSize = window.getComputedStyle(grid).getPropertyValue('grid-template-columns').split(' ').length;
|
|
92
|
-
this._pageCount = Math.ceil(this._itemCount / this._pageSize);
|
|
93
|
-
this._setPage(Math.floor(firstShownItemIndex / pageSize));
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
_setPage(page) {
|
|
97
|
-
if (page < 0) {
|
|
98
|
-
page = this._pageCount - 1;
|
|
99
|
-
} else if (page >= this._pageCount) {
|
|
100
|
-
page = 0;
|
|
101
|
-
}
|
|
102
|
-
if (page === this._page) {
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
this._page = page;
|
|
106
|
-
const grid = this._findGridElement();
|
|
107
|
-
if (grid) {
|
|
108
|
-
const offset = page === 0 ? '' : `repeat(${page}, 0)`;
|
|
109
|
-
grid.style.setProperty('grid-template-rows', `${offset} 1fr repeat(10, 0)`);
|
|
110
|
-
}
|
|
74
|
+
this._renderSwipeable(element, states, controls);
|
|
111
75
|
}
|
|
112
76
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
77
|
+
_afterRender(element, state) {
|
|
78
|
+
super._afterRender(element, state);
|
|
79
|
+
this._afterRenderSwipeable(element, state);
|
|
116
80
|
}
|
|
117
81
|
|
|
118
82
|
_onClick(event) {
|
|
83
|
+
this._onClickSwipeable(event);
|
|
119
84
|
super._onClick(event);
|
|
120
|
-
const element = event.target;
|
|
121
|
-
if (element.closest(`[data-role="previous"]`)) {
|
|
122
|
-
this.previous();
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
if (element.closest(`[data-role="next"]`)) {
|
|
126
|
-
this.next();
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
destroy() {
|
|
132
|
-
window.removeEventListener('resize', this.syncSize);
|
|
133
|
-
super.destroy();
|
|
134
85
|
}
|
|
135
86
|
|
|
136
87
|
}
|
|
88
|
+
|
|
89
|
+
makeSwipeable(CarouselLayout.prototype);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { STATUS } from '../../constants.js';
|
|
2
2
|
import TemplateBasedLayout from '../template.js';
|
|
3
|
-
import { makeTrackable } from '../trackable.js';
|
|
4
|
-
import { makeTriggerable } from '../triggerable.js';
|
|
5
|
-
import { product, article, question, productInfoBlock, articleInfoBlock, titleBlock, brandBlock, descriptionBlock, dateBlock, priceBlock, discountRateText, ctaBlock, cta, imageBlock, indexBlock } from '../templates.js';
|
|
3
|
+
import { makeTrackable } from '../mixin/trackable.js';
|
|
4
|
+
import { makeTriggerable } from '../mixin/triggerable.js';
|
|
5
|
+
import { product, article, image, question, productInfoBlock, articleInfoBlock, imageInfoBlock, titleBlock, brandBlock, descriptionBlock, dateBlock, priceBlock, discountRateText, ctaBlock, cta, imageBlock, indexBlock } from '../templates.js';
|
|
6
6
|
|
|
7
7
|
function root(layout, state) {
|
|
8
8
|
const { className, role, templates } = layout;
|
|
@@ -13,16 +13,20 @@ function root(layout, state) {
|
|
|
13
13
|
|
|
14
14
|
function ready(layout, state) {
|
|
15
15
|
const { templates } = layout;
|
|
16
|
-
const values = layout._getItems(state);
|
|
16
|
+
const values = layout._getItems(state) || [];
|
|
17
17
|
|
|
18
18
|
// TODO: handle categories, attributes, etc. by introducing sublayout
|
|
19
|
-
if (
|
|
20
|
-
return templates.
|
|
19
|
+
if (values.length > 0 || state.ongoing) { // TODO: ad-hoc ongoing?
|
|
20
|
+
return templates.body(layout, state, values);
|
|
21
21
|
} else {
|
|
22
22
|
return templates.empty(layout, state);
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
function body(layout, state, values) {
|
|
27
|
+
return layout.templates.list(layout, state, values);
|
|
28
|
+
}
|
|
29
|
+
|
|
26
30
|
function list(layout, state, values) {
|
|
27
31
|
const { className, templates, options } = layout;
|
|
28
32
|
const listTag = templates.ordered ? 'ol' : 'ul';
|
|
@@ -57,16 +61,19 @@ function loading() {
|
|
|
57
61
|
const DEFAULT_TEMPLATES = Object.freeze({
|
|
58
62
|
product,
|
|
59
63
|
article,
|
|
64
|
+
image,
|
|
60
65
|
question,
|
|
61
66
|
root,
|
|
62
67
|
[STATUS.READY]: ready,
|
|
63
68
|
empty: () => ``,
|
|
69
|
+
body,
|
|
64
70
|
list,
|
|
65
71
|
ordered: false,
|
|
66
72
|
items,
|
|
67
73
|
item,
|
|
68
74
|
productInfoBlock,
|
|
69
75
|
articleInfoBlock,
|
|
76
|
+
imageInfoBlock,
|
|
70
77
|
brandBlock,
|
|
71
78
|
titleBlock,
|
|
72
79
|
descriptionBlock,
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { mixin, requestAnimationFrame as raf } from '@miso.ai/commons';
|
|
2
|
+
|
|
3
|
+
export function makeSwipeable(prototype) {
|
|
4
|
+
mixin(prototype, SwipeableMixin.prototype);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export class SwipeableMixin {
|
|
8
|
+
|
|
9
|
+
_initSwipeable() {
|
|
10
|
+
this._swipeableContext = {
|
|
11
|
+
itemCount: undefined,
|
|
12
|
+
itemIndex: undefined,
|
|
13
|
+
displayedCount: undefined,
|
|
14
|
+
displayedIndex: undefined,
|
|
15
|
+
displayEventIndex: undefined,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// TODO: autoplay
|
|
20
|
+
|
|
21
|
+
get itemIndex() {
|
|
22
|
+
return this._swipeableContext.itemIndex;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
set itemIndex(index) {
|
|
26
|
+
const context = this._swipeableContext;
|
|
27
|
+
if (context.itemCount === undefined) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (index < 0) {
|
|
31
|
+
index = context.itemCount - 1;
|
|
32
|
+
} else if (index >= context.itemCount) {
|
|
33
|
+
index = 0;
|
|
34
|
+
}
|
|
35
|
+
context.itemIndex = index;
|
|
36
|
+
raf(() => this._syncSwipeableItemIndex());
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
next() {
|
|
40
|
+
if (this._swipeableContext.itemCount === undefined) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
this.itemIndex++;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
previous() {
|
|
47
|
+
if (this._swipeableContext.itemCount === undefined) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
this.itemIndex--;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
_renderSwipeable(element, states, controls) {
|
|
54
|
+
this._syncSwipeableItemCount(states);
|
|
55
|
+
this._syncSwipeableItemIndex();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
_afterRenderSwipeable() {
|
|
59
|
+
this._emitSwipeableDisplayEvent();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
_syncSwipeableItemCount({ state } = {}) {
|
|
63
|
+
const context = this._swipeableContext;
|
|
64
|
+
const items = this._getItems(state);
|
|
65
|
+
const oldItemCount = context.itemCount;
|
|
66
|
+
const itemCount = context.itemCount = items ? items.length : undefined;
|
|
67
|
+
|
|
68
|
+
if (itemCount === oldItemCount) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
// TODO: concern count === 0
|
|
72
|
+
if (itemCount === undefined) {
|
|
73
|
+
context.itemIndex = undefined;
|
|
74
|
+
} else if (oldItemCount === undefined) {
|
|
75
|
+
context.itemIndex = 0;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
this._syncDisplayedSwipeableItemCount();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
_syncDisplayedSwipeableItemCount() {
|
|
82
|
+
const context = this._swipeableContext;
|
|
83
|
+
const { itemCount } = context;
|
|
84
|
+
if (itemCount === context.displayedCount) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
let { element } = this._view;
|
|
88
|
+
element = element.firstElementChild;
|
|
89
|
+
if (!element) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
context.displayedCount = itemCount;
|
|
93
|
+
if (itemCount === undefined) {
|
|
94
|
+
element.removeAttribute('data-item-count');
|
|
95
|
+
} else {
|
|
96
|
+
element.setAttribute('data-item-count', itemCount);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
_syncSwipeableItemIndex() {
|
|
101
|
+
const context = this._swipeableContext;
|
|
102
|
+
const index = context.itemIndex;
|
|
103
|
+
if (index === context.displayedIndex) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
context.displayedIndex = index;
|
|
107
|
+
|
|
108
|
+
this._syncSwipeableListIndex(index);
|
|
109
|
+
this._syncSwipeableIndicatorIndex(index);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
_syncSwipeableListIndex(index) {
|
|
113
|
+
const { element } = this._view;
|
|
114
|
+
const listElement = element && this._getListElement(element);
|
|
115
|
+
if (!listElement) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
listElement.style.left = `${-index * 100}%`;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
_syncSwipeableIndicatorIndex(index) {
|
|
122
|
+
const { element } = this._view;
|
|
123
|
+
const indicatorElement = element && this._getSwipeableIndicatorElement(element);
|
|
124
|
+
if (!indicatorElement) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
let i = 0;
|
|
128
|
+
for (const item of indicatorElement.children) {
|
|
129
|
+
item.classList[ i === index ? 'add' : 'remove' ]('active');
|
|
130
|
+
i++;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
_getSwipeableIndicatorElement(element) {
|
|
135
|
+
return element.querySelector('[data-role="index-indicator"]');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
_emitSwipeableDisplayEvent() {
|
|
139
|
+
const context = this._swipeableContext;
|
|
140
|
+
const index = context.displayedIndex;
|
|
141
|
+
if (context.displayEventIndex === index) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
context.displayEventIndex = index;
|
|
145
|
+
this._view._events.emit('display', { index });
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
_onClickSwipeable(event) {
|
|
149
|
+
const { target } = event;
|
|
150
|
+
if (target.closest(`[data-role="previous"]`)) {
|
|
151
|
+
event.preventDefault();
|
|
152
|
+
this.previous();
|
|
153
|
+
}
|
|
154
|
+
if (target.closest(`[data-role="next"]`)) {
|
|
155
|
+
event.preventDefault();
|
|
156
|
+
this.next();
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// TODO: swipe gesture
|
|
161
|
+
|
|
162
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { mixin } from '@miso.ai/commons';
|
|
2
|
-
import Bindings from '
|
|
3
|
-
import Viewables from '
|
|
4
|
-
import { validateClick } from '
|
|
2
|
+
import Bindings from '../../util/bindings.js';
|
|
3
|
+
import Viewables from '../../util/viewables.js';
|
|
4
|
+
import { validateClick, markAsTracked } from '../../util/trackers.js';
|
|
5
5
|
|
|
6
6
|
export function makeTrackable(prototype) {
|
|
7
7
|
mixin(prototype, TrackableMixin.prototype);
|
|
@@ -80,6 +80,7 @@ export class TrackableMixin {
|
|
|
80
80
|
_trackClick(event, binding) {
|
|
81
81
|
const { click: options } = this._view.tracker.options || {};
|
|
82
82
|
if (validateClick(options, event, binding)) {
|
|
83
|
+
markAsTracked(event);
|
|
83
84
|
this._view.tracker.click([binding.value]);
|
|
84
85
|
}
|
|
85
86
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { mixin, viewable as whenViewable } from '@miso.ai/commons';
|
|
2
|
-
import { STATUS } from '
|
|
3
|
-
import { fields } from '
|
|
2
|
+
import { STATUS } from '../../constants.js';
|
|
3
|
+
import { fields } from '../../actor/index.js';
|
|
4
4
|
|
|
5
5
|
export function makeTriggerable(prototype) {
|
|
6
6
|
mixin(prototype, TriggerableMixin.prototype);
|