@miso.ai/client-sdk-ui 1.11.5-beta.0 → 1.11.5-beta.10

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.
Files changed (67) hide show
  1. package/dist/cjs/actor/data.js +4 -2
  2. package/dist/cjs/actor/view.js +3 -2
  3. package/dist/cjs/asset/svgs.js +5 -2
  4. package/dist/cjs/constants.js +2 -1
  5. package/dist/cjs/defaults/hybrid-search/answer-box.js +13 -2
  6. package/dist/cjs/defaults/hybrid-search/controls.js +2 -1
  7. package/dist/cjs/defaults/hybrid-search/templates.js +9 -0
  8. package/dist/cjs/layout/affiliation/affiliation.js +6 -7
  9. package/dist/cjs/layout/container/container.js +5 -1
  10. package/dist/cjs/layout/index.js +7 -0
  11. package/dist/cjs/layout/input/option-list.js +2 -1
  12. package/dist/cjs/layout/input/search-box.js +2 -1
  13. package/dist/cjs/layout/list/carousel.js +2 -2
  14. package/dist/cjs/layout/list/collection.js +7 -4
  15. package/dist/cjs/layout/list/gallery.js +270 -0
  16. package/dist/cjs/layout/mixin/swipeable.js +14 -15
  17. package/dist/cjs/layout/raf.js +2 -0
  18. package/dist/cjs/layout/templates.js +19 -8
  19. package/dist/cjs/util/dom.js +101 -0
  20. package/dist/cjs/version.js +1 -1
  21. package/dist/cjs/workflow/answer-based.js +49 -77
  22. package/dist/cjs/workflow/ask.js +5 -3
  23. package/dist/cjs/workflow/base.js +4 -2
  24. package/dist/cjs/workflow/explore.js +17 -7
  25. package/dist/cjs/workflow/hybrid-search-answer.js +13 -0
  26. package/dist/cjs/workflow/hybrid-search-results.js +16 -0
  27. package/dist/cjs/workflow/hybrid-search.js +3 -8
  28. package/dist/cjs/workflow/options/auto-query.js +62 -0
  29. package/dist/cjs/workflow/options/index.js +11 -0
  30. package/dist/cjs/workflow/processors.js +73 -0
  31. package/dist/cjs/workflow/search-based.js +4 -0
  32. package/dist/cjs/workflow/search.js +0 -25
  33. package/dist/cjs/workflow/use-link.js +9 -2
  34. package/package.json +2 -2
  35. package/src/actor/data.js +4 -2
  36. package/src/actor/view.js +3 -2
  37. package/src/asset/svgs.js +5 -1
  38. package/src/constants.js +1 -0
  39. package/src/defaults/hybrid-search/answer-box.js +9 -2
  40. package/src/defaults/hybrid-search/controls.js +2 -2
  41. package/src/defaults/hybrid-search/templates.js +9 -0
  42. package/src/layout/affiliation/affiliation.js +5 -7
  43. package/src/layout/container/container.js +4 -1
  44. package/src/layout/index.js +1 -0
  45. package/src/layout/input/option-list.js +2 -1
  46. package/src/layout/input/search-box.js +2 -1
  47. package/src/layout/list/carousel.js +3 -3
  48. package/src/layout/list/collection.js +6 -4
  49. package/src/layout/list/gallery.js +229 -0
  50. package/src/layout/mixin/swipeable.js +12 -16
  51. package/src/layout/raf.js +1 -0
  52. package/src/layout/templates.js +15 -3
  53. package/src/util/dom.js +103 -0
  54. package/src/version.js +1 -1
  55. package/src/workflow/answer-based.js +42 -42
  56. package/src/workflow/ask.js +6 -4
  57. package/src/workflow/base.js +4 -2
  58. package/src/workflow/explore.js +15 -4
  59. package/src/workflow/hybrid-search-answer.js +15 -1
  60. package/src/workflow/hybrid-search-results.js +17 -0
  61. package/src/workflow/hybrid-search.js +3 -6
  62. package/src/workflow/options/auto-query.js +29 -0
  63. package/src/workflow/options/index.js +1 -0
  64. package/src/workflow/processors.js +60 -0
  65. package/src/workflow/search-based.js +5 -1
  66. package/src/workflow/search.js +0 -17
  67. package/src/workflow/use-link.js +2 -2
@@ -12,9 +12,13 @@ exports.retainFacetCountsInData = retainFacetCountsInData;
12
12
  exports.writeAffiliationInfoToInteraction = writeAffiliationInfoToInteraction;
13
13
  exports.writeAnswerStageToMeta = writeAnswerStageToMeta;
14
14
  exports.writeDataStatus = writeDataStatus;
15
+ exports.writeEventTargetToInteraction = writeEventTargetToInteraction;
15
16
  exports.writeExhaustionToData = writeExhaustionToData;
16
17
  exports.writeKeywordsToData = writeKeywordsToData;
18
+ exports.writeMisoIdFromSession = writeMisoIdFromSession;
17
19
  exports.writeMisoIdToMeta = writeMisoIdToMeta;
20
+ exports.writeMisoIdToSession = writeMisoIdToSession;
21
+ exports.writeUnanswerableToMeta = writeUnanswerableToMeta;
18
22
  var _commons = require("@miso.ai/commons");
19
23
  var _constants = require("../constants.js");
20
24
  var _index = require("../actor/index.js");
@@ -97,6 +101,44 @@ function writeMisoIdToMeta(data) {
97
101
  })
98
102
  });
99
103
  }
104
+ function writeMisoIdToSession(data) {
105
+ const {
106
+ session,
107
+ value
108
+ } = data;
109
+ if (!session || !value) {
110
+ return;
111
+ }
112
+ const {
113
+ miso_id
114
+ } = value;
115
+ if (!miso_id) {
116
+ return;
117
+ }
118
+ session.meta.miso_id = miso_id;
119
+ }
120
+ function writeMisoIdFromSession(data) {
121
+ const {
122
+ session,
123
+ value
124
+ } = data;
125
+ if (!session || !value) {
126
+ return data;
127
+ }
128
+ const {
129
+ meta: {
130
+ miso_id
131
+ } = {}
132
+ } = session;
133
+ if (!miso_id) {
134
+ return data;
135
+ }
136
+ return Object.assign({}, data, {
137
+ meta: Object.assign({}, data.meta, {
138
+ miso_id
139
+ })
140
+ });
141
+ }
100
142
  function writeKeywordsToData(data) {
101
143
  const {
102
144
  request,
@@ -282,6 +324,21 @@ function writeAffiliationInfoToInteraction(payload, args) {
282
324
  }
283
325
  });
284
326
  }
327
+ function writeEventTargetToInteraction(payload) {
328
+ let {
329
+ event_target
330
+ } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
331
+ if (!event_target) {
332
+ return payload;
333
+ }
334
+ return mergeInteraction(payload, {
335
+ context: {
336
+ custom_context: {
337
+ event_target
338
+ }
339
+ }
340
+ });
341
+ }
285
342
  function mergeInteraction() {
286
343
  let base = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
287
344
  let patch = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
@@ -290,4 +347,20 @@ function mergeInteraction() {
290
347
  custom_context: (0, _commons.trimObj)(Object.assign({}, base.context && base.context.custom_context, patch.context && patch.context.custom_context))
291
348
  }))
292
349
  }));
350
+ }
351
+ function writeUnanswerableToMeta(data) {
352
+ const {
353
+ value
354
+ } = data;
355
+ if (!value) {
356
+ return data;
357
+ }
358
+ if (!value.finished || value.finish_reason !== 'success' || value.sources && value.sources.length > 0) {
359
+ return data;
360
+ }
361
+ return Object.assign({}, data, {
362
+ meta: Object.assign({}, data.meta, {
363
+ unanswerable: true
364
+ })
365
+ });
293
366
  }
@@ -80,6 +80,10 @@ class SearchBasedWorkflow extends _base.default {
80
80
  }
81
81
 
82
82
  // query //
83
+ autoQuery() {
84
+ let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
85
+ _index3.autoQuery.call(this, options);
86
+ }
83
87
  query(args) {
84
88
  if (!args.q) {
85
89
  throw new Error(`q is required in query() call`);
@@ -49,31 +49,6 @@ class Search extends _searchBased.default {
49
49
  super._initProperties(args);
50
50
  this._initAutocomplete(args);
51
51
  }
52
- autoQuery() {
53
- let {
54
- setValue = true,
55
- focus = true,
56
- param = 'q'
57
- } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
58
- // TODO: align implementation with AnswerBasedWorkflow
59
- const q = new URLSearchParams(window.location.search).get(param);
60
- const {
61
- layout
62
- } = this._views.get(_constants.ROLE.QUERY);
63
- if (layout) {
64
- if (q && setValue) {
65
- layout.value = q;
66
- }
67
- if (!q && focus) {
68
- layout.focus();
69
- }
70
- }
71
- if (q) {
72
- this.query({
73
- q
74
- });
75
- }
76
- }
77
52
  notifyViewUpdate() {
78
53
  let role = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _constants.ROLE.PRODUCTS;
79
54
  for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
@@ -6,6 +6,8 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.UseLinkMixin = void 0;
7
7
  exports.enableUseLink = enableUseLink;
8
8
  var _commons = require("@miso.ai/commons");
9
+ const _excluded = ["q"];
10
+ 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; }
9
11
  function enableUseLink(prototype) {
10
12
  (0, _commons.mixin)(prototype, UseLinkMixin.prototype);
11
13
  }
@@ -34,11 +36,16 @@ class UseLinkMixin {
34
36
  window.open(url, target, windowFeatures);
35
37
  }
36
38
  }
37
- _getSubmitUrl(args) {
39
+ _getSubmitUrl() {
40
+ let _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
41
+ let {
42
+ q
43
+ } = _ref,
44
+ args = _objectWithoutPropertiesLoose(_ref, _excluded);
38
45
  if (!this._linkFn) {
39
46
  throw new Error('useLink(fn) must be called before _getSubmitUrl()');
40
47
  }
41
- return this._linkFn[0](args.q);
48
+ return this._linkFn[0](q, args);
42
49
  }
43
50
  }
44
51
  exports.UseLinkMixin = UseLinkMixin;
package/package.json CHANGED
@@ -26,7 +26,7 @@
26
26
  "url": "https://github.com/MisoAI/miso-client-js-sdk/issues"
27
27
  },
28
28
  "dependencies": {
29
- "@miso.ai/commons": "1.11.5-beta.0"
29
+ "@miso.ai/commons": "1.11.5-beta.10"
30
30
  },
31
- "version": "1.11.5-beta.0"
31
+ "version": "1.11.5-beta.10"
32
32
  }
package/src/actor/data.js CHANGED
@@ -22,11 +22,12 @@ export default class DataActor {
22
22
  }
23
23
 
24
24
  _handleSession(session) {
25
- // protocol: inactive -> no reaction
25
+ // this is mandatory in any case
26
+ /*
26
27
  if (!this.active) {
27
28
  return;
28
29
  }
29
-
30
+ */
30
31
  // abort ongoing data fetch if any
31
32
  if (!this._session || (session.index !== this._session.index)) {
32
33
  // new session, abort preview data fetch if necessary
@@ -38,6 +39,7 @@ export default class DataActor {
38
39
  }
39
40
  this._session = session;
40
41
 
42
+ // TODO: move this to workflow
41
43
  // reflect session update
42
44
  this._emitResponse({ session });
43
45
  }
package/src/actor/view.js CHANGED
@@ -185,13 +185,14 @@ export default class ViewActor {
185
185
  }
186
186
  // iterate over all member components' roles
187
187
  for (const { role } of element.components) {
188
- // not a data role -> nonempty
188
+ // not a data role -> irrelevant
189
189
  if (!isDataRole(role)) {
190
- return false;
190
+ continue;
191
191
  }
192
192
  let sliced = value && value[role];
193
193
  if (role === ROLE.QUERY_SUGGESTIONS) {
194
194
  // special case: query suggestions -> look up value from hub
195
+ // TODO: adhoc
195
196
  const data = this.hub.states[fields.suggestions()];
196
197
  sliced = data && data.value;
197
198
  }
package/src/asset/svgs.js CHANGED
@@ -15,6 +15,7 @@ const ICON_NAME = {
15
15
  SEARCH: 'search',
16
16
  TRIANGLE: 'triangle',
17
17
  TRIANGLE_EQ: 'triangle-eq',
18
+ CHEVRON: 'chevron',
18
19
  CHEVRON_NEGATIVE: 'chevron-negative',
19
20
  };
20
21
 
@@ -27,9 +28,11 @@ const ICON_TRIANGLE = `<symbol id="${PREFIX}-${ICON_NAME.TRIANGLE}" viewBox="-5
27
28
 
28
29
  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
 
31
+ const ICON_CHEVRON = `<symbol id="${PREFIX}-${ICON_NAME.CHEVRON}" viewBox="-100 -100 200 200"><path d="M10.61 -43.94A15 15,0,0,0,-10.61 -43.94L-60.61 6.06A15 15,0,0,0,-39.39 27.27L0 -12.12L39.39 27.27A15 15,0,0,0,60.61 6.06L10.61 -43.94Z"/></symbol>`;
32
+
30
33
  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
34
 
32
- const ICONS = [ICON_SEND, ICON_SEARCH, ICON_TRIANGLE, ICON_TRIANGLE_EQ, ICON_CHEVRON_NEGATIVE];
35
+ const ICONS = [ICON_SEND, ICON_SEARCH, ICON_TRIANGLE, ICON_TRIANGLE_EQ, ICON_CHEVRON, ICON_CHEVRON_NEGATIVE];
33
36
 
34
37
  const SPRITES_SVG = `<svg id="${ID}" style="display:none;">${ICONS.join('')}</svg>`;
35
38
 
@@ -59,4 +62,5 @@ export const SEND = _icon(ICON_NAME.SEND);
59
62
  export const SEARCH = _icon(ICON_NAME.SEARCH);
60
63
  export const TRIANGLE = _icon(ICON_NAME.TRIANGLE);
61
64
  export const TRIANGLE_EQ = _icon(ICON_NAME.TRIANGLE_EQ);
65
+ export const CHEVRON = _icon(ICON_NAME.CHEVRON);
62
66
  export const CHEVRON_NEGATIVE = _icon(ICON_NAME.CHEVRON_NEGATIVE);
package/src/constants.js CHANGED
@@ -105,6 +105,7 @@ export const STATUS = Object.freeze({
105
105
  DONE: 'done',
106
106
  EMPTY: 'empty',
107
107
  NONEMPTY: 'nonempty',
108
+ UNANSWERABLE: 'unanswerable',
108
109
  });
109
110
 
110
111
  export function validateEventType(value) {
@@ -9,12 +9,13 @@ const TOGGLE_BUTTON_SELECTOR = '[data-role="answer-box-toggle"]';
9
9
 
10
10
  export default class AnswerBox {
11
11
 
12
- constructor(client, element, { classPrefix = CLASS_PREFIX, phrases = {} } = {}) {
12
+ constructor(client, element, { classPrefix = CLASS_PREFIX, phrases = {}, hideWhenUnanswerable = true } = {}) {
13
13
  this._client = client;
14
14
  this._workflow = client.ui.hybridSearch;
15
15
  this._element = element;
16
16
  this._classPrefix = classPrefix;
17
17
  this._phrases = phrases;
18
+ this._hideWhenUnanswerable = hideWhenUnanswerable;
18
19
  this._init();
19
20
  }
20
21
 
@@ -58,10 +59,16 @@ export default class AnswerBox {
58
59
 
59
60
  _syncWofkflowStatus() {
60
61
  const { status } = this._workflow._answer;
62
+ const { meta: { unanswerable = false } = {} } = this._workflow._answer._hub.states[fields.data()];
63
+
61
64
  switch (status) {
62
65
  case STATUS.ERRONEOUS:
63
66
  case STATUS.READY:
64
- this.show();
67
+ if (unanswerable && this._hideWhenUnanswerable) {
68
+ this.hide();
69
+ } else {
70
+ this.show();
71
+ }
65
72
  break;
66
73
  default:
67
74
  this.hide();
@@ -1,11 +1,11 @@
1
1
  import AnswerBox from './answer-box.js';
2
2
 
3
- export function wireAnswerBox(client, element) {
3
+ export function wireAnswerBox(client, element, options = {}) {
4
4
  if (!client) {
5
5
  throw new Error('client is required');
6
6
  }
7
7
  if (!element) {
8
8
  return;
9
9
  }
10
- new AnswerBox(client, element);
10
+ new AnswerBox(client, element, options);
11
11
  }
@@ -9,6 +9,15 @@ class Sections extends _Sections {
9
9
  super({ helpers });
10
10
  }
11
11
 
12
+ answer(options) {
13
+ const { container } = this._helpers;
14
+ return container(options, { name: 'answer', visibleWhen: 'ready !unanswerable' }, [
15
+ ...this.question(options),
16
+ '<miso-answer></miso-answer>',
17
+ '<miso-feedback></miso-feedback>',
18
+ ]);
19
+ }
20
+
12
21
  answerBox(options) {
13
22
  const { element } = this._helpers;
14
23
  const { classPrefix } = options;
@@ -3,12 +3,13 @@ import { STATUS } from '../../constants.js';
3
3
  import CollectionLayout from '../list/collection.js';
4
4
  import { affiliation, helpers } from '../templates.js';
5
5
  import { TRIANGLE } from '../../asset/svgs.js';
6
+ import { setOrRemoveAttribute } from '../../util/dom.js';
6
7
 
7
8
  const TYPE = 'affiliation';
8
9
  const DEFAULT_CLASSNAME = 'miso-affiliation';
9
10
 
10
11
  function root(layout, state) {
11
- const { className, role, templates } = layout;
12
+ const { className, role, templates, options } = layout;
12
13
  const { status } = state;
13
14
  const roleAttr = role ? ` data-role="${role}"` : '';
14
15
  const data = state.value || {};
@@ -16,7 +17,8 @@ function root(layout, state) {
16
17
  const items = layout._getItems(state);
17
18
  const itemCount = items ? items.length : undefined;
18
19
  const itemCountAttr = itemCount !== undefined ? ` data-item-count="${itemCount}"` : '';
19
- return `<div class="${className} ${status}"${roleAttr}${channelAttr}${itemCountAttr}>${status === STATUS.READY ? templates[status](layout, state) : ''}</div>`;
20
+ const itemTypeAttr = options.itemType ? ` data-item-type="${options.itemType}"` : '';
21
+ return `<div class="${className} ${status}"${roleAttr}${channelAttr}${itemTypeAttr}${itemCountAttr}>${status === STATUS.READY ? templates[status](layout, state) : ''}</div>`;
20
22
  }
21
23
 
22
24
  function ready(layout, state) {
@@ -256,12 +258,8 @@ export default class AffiliationLayout extends CollectionLayout {
256
258
  if (!element) {
257
259
  return;
258
260
  }
261
+ setOrRemoveAttribute(element, 'data-item-count', itemCount);
259
262
  this._displayedCount = itemCount;
260
- if (itemCount === undefined) {
261
- element.removeAttribute('data-item-count');
262
- } else {
263
- element.setAttribute('data-item-count', itemCount);
264
- }
265
263
  }
266
264
 
267
265
  _syncItemIndex() {
@@ -60,11 +60,14 @@ export default class ContainerLayout extends RafLayout {
60
60
  if (!element) {
61
61
  return;
62
62
  }
63
- const { status, ongoing, meta: { miso_id, empty = false } = {} } = state;
63
+ const { status, ongoing, meta: { miso_id, empty = false, unanswerable = false } = {} } = state;
64
64
  const statuses = [status, empty ? STATUS.EMPTY : STATUS.NONEMPTY];
65
65
  if (status === STATUS.READY) {
66
66
  statuses.push(ongoing ? STATUS.ONGOING : STATUS.DONE);
67
67
  }
68
+ if (unanswerable) {
69
+ statuses.push(STATUS.UNANSWERABLE);
70
+ }
68
71
  element.setAttribute('status', statuses.join(' '));
69
72
  if (miso_id) {
70
73
  element.setAttribute('miso-id', miso_id);
@@ -6,6 +6,7 @@ export { default as CollectionLayout } from './list/collection.js';
6
6
  export { default as ListLayout } from './list/list.js';
7
7
  export { default as CardsLayout } from './list/cards.js';
8
8
  export { default as CarouselLayout } from './list/carousel.js';
9
+ export { default as GalleryLayout } from './list/gallery.js';
9
10
  export { default as SearchBoxLayout } from './input/search-box.js';
10
11
  export { default as OptionListLayout } from './input/option-list.js';
11
12
  export { default as FeedbackLayout } from './radio/feedback.js';
@@ -2,6 +2,7 @@ import { escapeHtml } from '@miso.ai/commons';
2
2
  import { fields } from '../../actor/index.js';
3
3
  import TemplateBasedLayout from '../template.js';
4
4
  import { makeTrackable } from '../mixin/trackable.js';
5
+ import { addOrRemoveClass } from '../../util/dom.js';
5
6
 
6
7
  const TYPE = 'option-list';
7
8
  const DEFAULT_CLASSNAME = 'miso-option-list';
@@ -96,7 +97,7 @@ export default class OptionListLayout extends TemplateBasedLayout {
96
97
  return;
97
98
  }
98
99
  const optionValues = this._getItems(state) || [];
99
- optionsElement.classList[optionValues.length === 0 ? 'add' : 'remove']('empty');
100
+ addOrRemoveClass(optionsElement, 'empty', optionValues.length === 0);
100
101
  }
101
102
 
102
103
  _getItems(state) {
@@ -4,6 +4,7 @@ import { fields } from '../../actor/index.js';
4
4
  import TemplateBasedLayout from '../template.js';
5
5
  import { imageBlock, productInfoBlock } from '../templates.js';
6
6
  import { getIcon } from '../../asset/svgs.js';
7
+ import { addOrRemoveClass } from '../../util/dom.js';
7
8
 
8
9
  const TYPE = 'search-box';
9
10
  const DEFAULT_CLASSNAME = 'miso-search-box';
@@ -215,7 +216,7 @@ export default class SearchBoxLayout extends TemplateBasedLayout {
215
216
 
216
217
  const items = context.trackItems(completions);
217
218
 
218
- element.classList[items.length > 0 ? 'add' : 'remove']('nonempty');
219
+ addOrRemoveClass(element, 'nonempty', items.length > 0);
219
220
  autocompleteElement.setAttribute('data-status', status);
220
221
  autocompleteElement.innerHTML = this.templates.completions(this, items);
221
222
 
@@ -1,6 +1,6 @@
1
1
  import CollectionLayout from './collection.js';
2
2
  import { makeSwipeable } from '../mixin/swipeable.js';
3
- import { CHEVRON_NEGATIVE } from '../../asset/svgs.js';
3
+ import { CHEVRON, CHEVRON_NEGATIVE } from '../../asset/svgs.js';
4
4
 
5
5
  const TYPE = 'carousel';
6
6
  const DEFAULT_CLASSNAME = 'miso-carousel';
@@ -41,8 +41,8 @@ function control(layout, state, direction) {
41
41
  return `<div class="${className}__control-${direction}" data-role="${direction}">${templates.controlIcon(layout, state, direction)}</div>`;
42
42
  }
43
43
 
44
- function controlIcon(layout, state, direction) {
45
- return CHEVRON_NEGATIVE;
44
+ function controlIcon(layout) {
45
+ return layout.options.itemType === 'image' ? CHEVRON_NEGATIVE : CHEVRON;
46
46
  }
47
47
 
48
48
  function indexIndicator(layout, state, values) {
@@ -5,10 +5,11 @@ import { makeTriggerable } from '../mixin/triggerable.js';
5
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
- const { className, role, templates } = layout;
8
+ const { className, role, templates, options } = layout;
9
9
  const { status } = state;
10
- const roleAttr = role ? `data-role="${role}"` : '';
11
- return `<div class="${className} ${status}" ${roleAttr}>${status === STATUS.READY ? templates[status](layout, state) : ''}${templates.trigger(layout, state)}${templates.loading(layout, state)}</div>`;
10
+ const roleAttr = role ? ` data-role="${role}"` : '';
11
+ const itemTypeAttr = options.itemType ? ` data-item-type="${options.itemType}"` : '';
12
+ return `<div class="${className} ${status}"${roleAttr}${itemTypeAttr}>${status === STATUS.READY ? templates[status](layout, state) : ''}${templates.trigger(layout, state)}${templates.loading(layout, state)}</div>`;
12
13
  }
13
14
 
14
15
  function ready(layout, state) {
@@ -30,8 +31,9 @@ function body(layout, state, values) {
30
31
  function list(layout, state, values) {
31
32
  const { className, templates, options } = layout;
32
33
  const listTag = templates.ordered ? 'ol' : 'ul';
34
+ const itemTypeAttr = options.itemType ? ` data-item-type="${options.itemType}"` : ''; // backward compatible
33
35
  // TODO: support separator?
34
- return `<${listTag} class="${className}__list" data-role="list" data-item-type="${options.itemType}">${templates.items(layout, state, values)}</${listTag}>`;
36
+ return `<${listTag} class="${className}__list" data-role="list"${itemTypeAttr}>${templates.items(layout, state, values)}</${listTag}>`;
35
37
  }
36
38
 
37
39
  function items(layout, state, values, { offset = 0 } = {}) {