@miso.ai/client-sdk-ui 1.11.5-beta.1 → 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
@@ -28,11 +28,12 @@ class DataActor {
28
28
  return this._source;
29
29
  }
30
30
  _handleSession(session) {
31
- // protocol: inactive -> no reaction
31
+ // this is mandatory in any case
32
+ /*
32
33
  if (!this.active) {
33
34
  return;
34
35
  }
35
-
36
+ */
36
37
  // abort ongoing data fetch if any
37
38
  if (!this._session || session.index !== this._session.index) {
38
39
  // new session, abort preview data fetch if necessary
@@ -44,6 +45,7 @@ class DataActor {
44
45
  }
45
46
  this._session = session;
46
47
 
48
+ // TODO: move this to workflow
47
49
  // reflect session update
48
50
  this._emitResponse({
49
51
  session
@@ -231,13 +231,14 @@ class ViewActor {
231
231
  for (const {
232
232
  role
233
233
  } of element.components) {
234
- // not a data role -> nonempty
234
+ // not a data role -> irrelevant
235
235
  if (!(0, _constants.isDataRole)(role)) {
236
- return false;
236
+ continue;
237
237
  }
238
238
  let sliced = value && value[role];
239
239
  if (role === _constants.ROLE.QUERY_SUGGESTIONS) {
240
240
  // special case: query suggestions -> look up value from hub
241
+ // TODO: adhoc
241
242
  const data = this.hub.states[fields.suggestions()];
242
243
  sliced = data && data.value;
243
244
  }
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.TRIANGLE_EQ = exports.TRIANGLE = exports.SEND = exports.SEARCH = exports.ICON_NAMES = exports.CHEVRON_NEGATIVE = void 0;
6
+ exports.TRIANGLE_EQ = exports.TRIANGLE = exports.SEND = exports.SEARCH = exports.ICON_NAMES = exports.CHEVRON_NEGATIVE = exports.CHEVRON = void 0;
7
7
  exports.getIcon = getIcon;
8
8
  var _version = _interopRequireDefault(require("../version.js"));
9
9
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
@@ -20,6 +20,7 @@ const ICON_NAME = {
20
20
  SEARCH: 'search',
21
21
  TRIANGLE: 'triangle',
22
22
  TRIANGLE_EQ: 'triangle-eq',
23
+ CHEVRON: 'chevron',
23
24
  CHEVRON_NEGATIVE: 'chevron-negative'
24
25
  };
25
26
 
@@ -30,8 +31,9 @@ const ICON_SEARCH = `<symbol id="${PREFIX}-${ICON_NAME.SEARCH}" viewBox="0 0 24
30
31
  // Custom SVGs
31
32
  const ICON_TRIANGLE = `<symbol id="${PREFIX}-${ICON_NAME.TRIANGLE}" viewBox="-5 -5 105 105"><path d="${trianglePath(5)}"/></symbol>`;
32
33
  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>`;
34
+ 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>`;
33
35
  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>`;
34
- const ICONS = [ICON_SEND, ICON_SEARCH, ICON_TRIANGLE, ICON_TRIANGLE_EQ, ICON_CHEVRON_NEGATIVE];
36
+ const ICONS = [ICON_SEND, ICON_SEARCH, ICON_TRIANGLE, ICON_TRIANGLE_EQ, ICON_CHEVRON, ICON_CHEVRON_NEGATIVE];
35
37
  const SPRITES_SVG = `<svg id="${ID}" style="display:none;">${ICONS.join('')}</svg>`;
36
38
  let loaded = false;
37
39
  function loadSvgSprites() {
@@ -53,4 +55,5 @@ const SEND = exports.SEND = _icon(ICON_NAME.SEND);
53
55
  const SEARCH = exports.SEARCH = _icon(ICON_NAME.SEARCH);
54
56
  const TRIANGLE = exports.TRIANGLE = _icon(ICON_NAME.TRIANGLE);
55
57
  const TRIANGLE_EQ = exports.TRIANGLE_EQ = _icon(ICON_NAME.TRIANGLE_EQ);
58
+ const CHEVRON = exports.CHEVRON = _icon(ICON_NAME.CHEVRON);
56
59
  const CHEVRON_NEGATIVE = exports.CHEVRON_NEGATIVE = _icon(ICON_NAME.CHEVRON_NEGATIVE);
@@ -72,7 +72,8 @@ const STATUS = exports.STATUS = Object.freeze({
72
72
  ONGOING: 'ongoing',
73
73
  DONE: 'done',
74
74
  EMPTY: 'empty',
75
- NONEMPTY: 'nonempty'
75
+ NONEMPTY: 'nonempty',
76
+ UNANSWERABLE: 'unanswerable'
76
77
  });
77
78
  function validateEventType(value) {
78
79
  if (TRACKING_EVENT_TYPES.indexOf(value) < 0) {
@@ -15,13 +15,15 @@ class AnswerBox {
15
15
  constructor(client, element) {
16
16
  let {
17
17
  classPrefix = _constants.CLASS_PREFIX,
18
- phrases = {}
18
+ phrases = {},
19
+ hideWhenUnanswerable = true
19
20
  } = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
20
21
  this._client = client;
21
22
  this._workflow = client.ui.hybridSearch;
22
23
  this._element = element;
23
24
  this._classPrefix = classPrefix;
24
25
  this._phrases = phrases;
26
+ this._hideWhenUnanswerable = hideWhenUnanswerable;
25
27
  this._init();
26
28
  }
27
29
  _init() {
@@ -65,10 +67,19 @@ class AnswerBox {
65
67
  const {
66
68
  status
67
69
  } = this._workflow._answer;
70
+ const {
71
+ meta: {
72
+ unanswerable = false
73
+ } = {}
74
+ } = this._workflow._answer._hub.states[_index.fields.data()];
68
75
  switch (status) {
69
76
  case _constants2.STATUS.ERRONEOUS:
70
77
  case _constants2.STATUS.READY:
71
- this.show();
78
+ if (unanswerable && this._hideWhenUnanswerable) {
79
+ this.hide();
80
+ } else {
81
+ this.show();
82
+ }
72
83
  break;
73
84
  default:
74
85
  this.hide();
@@ -7,11 +7,12 @@ exports.wireAnswerBox = wireAnswerBox;
7
7
  var _answerBox = _interopRequireDefault(require("./answer-box.js"));
8
8
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
9
9
  function wireAnswerBox(client, element) {
10
+ let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
10
11
  if (!client) {
11
12
  throw new Error('client is required');
12
13
  }
13
14
  if (!element) {
14
15
  return;
15
16
  }
16
- new _answerBox.default(client, element);
17
+ new _answerBox.default(client, element, options);
17
18
  }
@@ -18,6 +18,15 @@ class Sections extends _templates.Sections {
18
18
  helpers
19
19
  });
20
20
  }
21
+ answer(options) {
22
+ const {
23
+ container
24
+ } = this._helpers;
25
+ return container(options, {
26
+ name: 'answer',
27
+ visibleWhen: 'ready !unanswerable'
28
+ }, [...this.question(options), '<miso-answer></miso-answer>', '<miso-feedback></miso-feedback>']);
29
+ }
21
30
  answerBox(options) {
22
31
  const {
23
32
  element
@@ -9,6 +9,7 @@ var _constants = require("../../constants.js");
9
9
  var _collection = _interopRequireDefault(require("../list/collection.js"));
10
10
  var _templates = require("../templates.js");
11
11
  var _svgs = require("../../asset/svgs.js");
12
+ var _dom = require("../../util/dom.js");
12
13
  const _excluded = ["className", "templates", "autoplay"];
13
14
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
14
15
  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; }
@@ -18,7 +19,8 @@ function root(layout, state) {
18
19
  const {
19
20
  className,
20
21
  role,
21
- templates
22
+ templates,
23
+ options
22
24
  } = layout;
23
25
  const {
24
26
  status
@@ -29,7 +31,8 @@ function root(layout, state) {
29
31
  const items = layout._getItems(state);
30
32
  const itemCount = items ? items.length : undefined;
31
33
  const itemCountAttr = itemCount !== undefined ? ` data-item-count="${itemCount}"` : '';
32
- return `<div class="${className} ${status}"${roleAttr}${channelAttr}${itemCountAttr}>${status === _constants.STATUS.READY ? templates[status](layout, state) : ''}</div>`;
34
+ const itemTypeAttr = options.itemType ? ` data-item-type="${options.itemType}"` : '';
35
+ return `<div class="${className} ${status}"${roleAttr}${channelAttr}${itemTypeAttr}${itemCountAttr}>${status === _constants.STATUS.READY ? templates[status](layout, state) : ''}</div>`;
33
36
  }
34
37
  function ready(layout, state) {
35
38
  const {
@@ -293,12 +296,8 @@ class AffiliationLayout extends _collection.default {
293
296
  if (!element) {
294
297
  return;
295
298
  }
299
+ (0, _dom.setOrRemoveAttribute)(element, 'data-item-count', itemCount);
296
300
  this._displayedCount = itemCount;
297
- if (itemCount === undefined) {
298
- element.removeAttribute('data-item-count');
299
- } else {
300
- element.setAttribute('data-item-count', itemCount);
301
- }
302
301
  }
303
302
  _syncItemIndex() {
304
303
  const index = this._itemIndex;
@@ -80,13 +80,17 @@ class ContainerLayout extends _raf.default {
80
80
  ongoing,
81
81
  meta: {
82
82
  miso_id,
83
- empty = false
83
+ empty = false,
84
+ unanswerable = false
84
85
  } = {}
85
86
  } = state;
86
87
  const statuses = [status, empty ? _constants.STATUS.EMPTY : _constants.STATUS.NONEMPTY];
87
88
  if (status === _constants.STATUS.READY) {
88
89
  statuses.push(ongoing ? _constants.STATUS.ONGOING : _constants.STATUS.DONE);
89
90
  }
91
+ if (unanswerable) {
92
+ statuses.push(_constants.STATUS.UNANSWERABLE);
93
+ }
90
94
  element.setAttribute('status', statuses.join(' '));
91
95
  if (miso_id) {
92
96
  element.setAttribute('miso-id', miso_id);
@@ -57,6 +57,12 @@ Object.defineProperty(exports, "FeedbackLayout", {
57
57
  return _feedback.default;
58
58
  }
59
59
  });
60
+ Object.defineProperty(exports, "GalleryLayout", {
61
+ enumerable: true,
62
+ get: function () {
63
+ return _gallery.default;
64
+ }
65
+ });
60
66
  Object.defineProperty(exports, "ListLayout", {
61
67
  enumerable: true,
62
68
  get: function () {
@@ -113,6 +119,7 @@ var _collection = _interopRequireDefault(require("./list/collection.js"));
113
119
  var _list = _interopRequireDefault(require("./list/list.js"));
114
120
  var _cards = _interopRequireDefault(require("./list/cards.js"));
115
121
  var _carousel = _interopRequireDefault(require("./list/carousel.js"));
122
+ var _gallery = _interopRequireDefault(require("./list/gallery.js"));
116
123
  var _searchBox = _interopRequireDefault(require("./input/search-box.js"));
117
124
  var _optionList = _interopRequireDefault(require("./input/option-list.js"));
118
125
  var _feedback = _interopRequireDefault(require("./radio/feedback.js"));
@@ -8,6 +8,7 @@ var _commons = require("@miso.ai/commons");
8
8
  var _index = require("../../actor/index.js");
9
9
  var _template = _interopRequireDefault(require("../template.js"));
10
10
  var _trackable = require("../mixin/trackable.js");
11
+ var _dom = require("../../util/dom.js");
11
12
  const _excluded = ["className", "templates"];
12
13
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
13
14
  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; }
@@ -107,7 +108,7 @@ class OptionListLayout extends _template.default {
107
108
  return;
108
109
  }
109
110
  const optionValues = this._getItems(state) || [];
110
- optionsElement.classList[optionValues.length === 0 ? 'add' : 'remove']('empty');
111
+ (0, _dom.addOrRemoveClass)(optionsElement, 'empty', optionValues.length === 0);
111
112
  }
112
113
  _getItems(state) {
113
114
  return state.value;
@@ -10,6 +10,7 @@ var _index = require("../../actor/index.js");
10
10
  var _template = _interopRequireDefault(require("../template.js"));
11
11
  var _templates = require("../templates.js");
12
12
  var _svgs = require("../../asset/svgs.js");
13
+ var _dom = require("../../util/dom.js");
13
14
  const _excluded = ["className", "templates", "autocomplete"];
14
15
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
15
16
  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; }
@@ -250,7 +251,7 @@ class SearchBoxLayout extends _template.default {
250
251
  return;
251
252
  }
252
253
  const items = context.trackItems(completions);
253
- element.classList[items.length > 0 ? 'add' : 'remove']('nonempty');
254
+ (0, _dom.addOrRemoveClass)(element, 'nonempty', items.length > 0);
254
255
  autocompleteElement.setAttribute('data-status', status);
255
256
  autocompleteElement.innerHTML = this.templates.completions(this, items);
256
257
  writeToState({
@@ -50,8 +50,8 @@ function control(layout, state, direction) {
50
50
  } = layout;
51
51
  return `<div class="${className}__control-${direction}" data-role="${direction}">${templates.controlIcon(layout, state, direction)}</div>`;
52
52
  }
53
- function controlIcon(layout, state, direction) {
54
- return _svgs.CHEVRON_NEGATIVE;
53
+ function controlIcon(layout) {
54
+ return layout.options.itemType === 'image' ? _svgs.CHEVRON_NEGATIVE : _svgs.CHEVRON;
55
55
  }
56
56
  function indexIndicator(layout, state, values) {
57
57
  const {
@@ -16,13 +16,15 @@ function root(layout, state) {
16
16
  const {
17
17
  className,
18
18
  role,
19
- templates
19
+ templates,
20
+ options
20
21
  } = layout;
21
22
  const {
22
23
  status
23
24
  } = state;
24
- const roleAttr = role ? `data-role="${role}"` : '';
25
- return `<div class="${className} ${status}" ${roleAttr}>${status === _constants.STATUS.READY ? templates[status](layout, state) : ''}${templates.trigger(layout, state)}${templates.loading(layout, state)}</div>`;
25
+ const roleAttr = role ? ` data-role="${role}"` : '';
26
+ const itemTypeAttr = options.itemType ? ` data-item-type="${options.itemType}"` : '';
27
+ return `<div class="${className} ${status}"${roleAttr}${itemTypeAttr}>${status === _constants.STATUS.READY ? templates[status](layout, state) : ''}${templates.trigger(layout, state)}${templates.loading(layout, state)}</div>`;
26
28
  }
27
29
  function ready(layout, state) {
28
30
  const {
@@ -48,8 +50,9 @@ function list(layout, state, values) {
48
50
  options
49
51
  } = layout;
50
52
  const listTag = templates.ordered ? 'ol' : 'ul';
53
+ const itemTypeAttr = options.itemType ? ` data-item-type="${options.itemType}"` : ''; // backward compatible
51
54
  // TODO: support separator?
52
- return `<${listTag} class="${className}__list" data-role="list" data-item-type="${options.itemType}">${templates.items(layout, state, values)}</${listTag}>`;
55
+ return `<${listTag} class="${className}__list" data-role="list"${itemTypeAttr}>${templates.items(layout, state, values)}</${listTag}>`;
53
56
  }
54
57
  function items(layout, state, values) {
55
58
  let {
@@ -0,0 +1,270 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _commons = require("@miso.ai/commons");
8
+ var _collection = _interopRequireDefault(require("./collection.js"));
9
+ var _dom = require("../../util/dom.js");
10
+ const _excluded = ["className", "templates"];
11
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
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; }
13
+ function calculateItemSizes(itemCount, columnCount) {
14
+ const totalSpaces = columnCount * 2;
15
+ let remainingSpaces = Math.max(totalSpaces - itemCount, 0);
16
+ let small = itemCount;
17
+ // turning a small block into a large block takes 3 extra spaces
18
+ const large = Math.min(Math.floor(remainingSpaces / 3), small);
19
+ small -= large;
20
+ remainingSpaces -= large * 3;
21
+ // turning a small block into a medium block takes 1 extra space
22
+ const medium = Math.min(remainingSpaces, small);
23
+ small -= medium;
24
+ remainingSpaces -= medium;
25
+ return [large, medium, small];
26
+ }
27
+ function item(layout, state, value, index) {
28
+ const {
29
+ className,
30
+ templates,
31
+ options
32
+ } = layout;
33
+ const {
34
+ itemType
35
+ } = options;
36
+ const body = templates[itemType](layout, state, value, {
37
+ index
38
+ });
39
+ const sizeClass = value._size ? ` ${className}__item-${value._size}` : '';
40
+ return `<li class="${className}__item${sizeClass}">${body}</li>`;
41
+ }
42
+ const DEFAULT_HEIGHT = '16rem';
43
+ const DEFAULT_TEMPLATES = Object.freeze(Object.assign({}, _collection.default.defaultTemplates, {
44
+ item
45
+ }));
46
+ const TYPE = 'gallery';
47
+ const DEFAULT_CLASSNAME = 'miso-gallery';
48
+ class GalleryLayout extends _collection.default {
49
+ static get type() {
50
+ return TYPE;
51
+ }
52
+ static get defaultTemplates() {
53
+ return DEFAULT_TEMPLATES;
54
+ }
55
+ static get defaultClassName() {
56
+ return DEFAULT_CLASSNAME;
57
+ }
58
+ constructor() {
59
+ let _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
60
+ let {
61
+ className = DEFAULT_CLASSNAME,
62
+ templates
63
+ } = _ref,
64
+ options = _objectWithoutPropertiesLoose(_ref, _excluded);
65
+ super(Object.assign({
66
+ className,
67
+ templates: Object.assign({}, DEFAULT_TEMPLATES, templates)
68
+ }, options));
69
+ this._galleryContext = {
70
+ itemAspectRatio: undefined,
71
+ itemCount: undefined,
72
+ columnCount: undefined
73
+ };
74
+ }
75
+ syncSize() {
76
+ const {
77
+ element
78
+ } = this._view;
79
+ const listElement = element && this._getListElement(element);
80
+ if (!listElement) {
81
+ return;
82
+ }
83
+ this._syncListAspectRatio(listElement);
84
+ this._syncItemSizes(listElement);
85
+ }
86
+ _preprocess(_ref2) {
87
+ let {
88
+ state,
89
+ rendered
90
+ } = _ref2;
91
+ const incremental = this._shallRenderIncrementally(state, rendered);
92
+ const bodyHtml = incremental ? undefined : this._bodyHtml(state);
93
+ const itemsHtml = this._itemsHtml(state, rendered);
94
+ return Object.assign({}, state, (0, _commons.trimObj)({
95
+ incremental,
96
+ bodyHtml,
97
+ itemsHtml
98
+ }));
99
+ }
100
+ _bodyHtml(state) {
101
+ return this.templates.root(this, Object.assign({}, state, {
102
+ value: []
103
+ }));
104
+ }
105
+ _itemsHtml(state, rendered) {
106
+ const offset = rendered && rendered.value && rendered.value.length || 0;
107
+ const values = (this._getItems(state) || []).slice(offset);
108
+ return values.length > 0 ? this.templates.items(this, state, values, {
109
+ offset
110
+ }) : '';
111
+ }
112
+ _render(element, states, controls) {
113
+ this._syncItemCount(states);
114
+ this._syncCssVariables();
115
+ const listElement = this._renderListElement(element, states, controls);
116
+ if (!listElement) {
117
+ return;
118
+ }
119
+ this._syncListAspectRatio(listElement);
120
+ this._renderItems(listElement, states, controls);
121
+ }
122
+ _renderListElement(element, _ref3) {
123
+ let {
124
+ state
125
+ } = _ref3;
126
+ const {
127
+ incremental,
128
+ bodyHtml
129
+ } = state;
130
+ if (!incremental) {
131
+ element.innerHTML = bodyHtml;
132
+ }
133
+ return this._getListElement(element);
134
+ }
135
+ _renderItems(listElement, states) {
136
+ if (states.state.incremental) {
137
+ this._appendItems(listElement, states);
138
+ } else {
139
+ this._updateItems(listElement, states);
140
+ }
141
+ }
142
+ _updateItems(listElement, _ref4) {
143
+ let {
144
+ state
145
+ } = _ref4;
146
+ const {
147
+ itemSizes
148
+ } = this._galleryContext;
149
+ const values = this._writeItemSizes(this._getItems(state) || [], itemSizes);
150
+ listElement.innerHTML = this.templates.items(this, state, values);
151
+ }
152
+ _appendItems(listElement, _ref5) {
153
+ let {
154
+ state
155
+ } = _ref5;
156
+ const {
157
+ itemSizes
158
+ } = this._galleryContext;
159
+ // update existing items
160
+ this._syncItemSizes(listElement);
161
+ // append new items
162
+ const offset = listElement.children.length;
163
+ const values = this._writeItemSizes((this._getItems(state) || []).slice(offset), itemSizes, offset);
164
+ listElement.insertAdjacentHTML('beforeend', this.templates.items(this, state, values, {
165
+ offset
166
+ }));
167
+ }
168
+ _writeItemSizes(values) {
169
+ let [large = 0, medium = 0] = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
170
+ let offset = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
171
+ let omitted = 0;
172
+ if (offset > 0) {
173
+ omitted = Math.min(offset, large);
174
+ offset -= omitted;
175
+ large -= omitted;
176
+ }
177
+ if (offset > 0) {
178
+ omitted = Math.min(offset, medium);
179
+ offset -= omitted;
180
+ medium -= omitted;
181
+ }
182
+ const results = [];
183
+ for (const value of values) {
184
+ let _size = 'small';
185
+ if (large > 0) {
186
+ _size = 'large';
187
+ large--;
188
+ } else if (medium > 0) {
189
+ _size = 'medium';
190
+ medium--;
191
+ }
192
+ results.push(Object.assign({}, value, {
193
+ _size
194
+ }));
195
+ }
196
+ return results;
197
+ }
198
+ _syncCssVariables() {
199
+ const context = this._galleryContext;
200
+ let {
201
+ itemAspectRatio = 1
202
+ } = this.options;
203
+ itemAspectRatio = context.itemAspectRatio = (0, _dom.cssAspectRatio)(itemAspectRatio);
204
+ const {
205
+ element
206
+ } = this._view;
207
+ if (!element) {
208
+ return;
209
+ }
210
+ // TODO: skip if equal to default values
211
+ element.style.setProperty('--miso-gallery-item-1x1-aspect-ratio', `${itemAspectRatio}`);
212
+ element.style.setProperty('--miso-gallery-item-1x2-aspect-ratio', `${itemAspectRatio.scaleBy([1, 2])}`);
213
+ }
214
+ _syncListAspectRatio(listElement) {
215
+ const context = this._galleryContext;
216
+ const {
217
+ itemCount,
218
+ itemAspectRatio
219
+ } = context;
220
+ const {
221
+ height = DEFAULT_HEIGHT
222
+ } = this.options;
223
+ const resolvedHeight = (0, _dom.resolveCssLength)(height);
224
+ const width = listElement.clientWidth;
225
+ if (width === 0) {
226
+ context.columnCount = undefined;
227
+ context.itemSizes = undefined;
228
+ return; // probably not visible yet
229
+ }
230
+ const columnCount = context.columnCount = Math.round(width * itemAspectRatio.width * 2 / (resolvedHeight * itemAspectRatio.height));
231
+ context.itemSizes = calculateItemSizes(itemCount, columnCount);
232
+
233
+ // update DOM
234
+ listElement.style.aspectRatio = `${columnCount} / 2`;
235
+ listElement.style.height = itemCount > 0 ? 'auto' : '0';
236
+ }
237
+ _syncItemCount() {
238
+ let {
239
+ state
240
+ } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
241
+ const items = this._getItems(state);
242
+ this._galleryContext.itemCount = items ? items.length : undefined;
243
+ }
244
+ _syncItemSizes(listElement) {
245
+ const {
246
+ itemSizes = []
247
+ } = this._galleryContext;
248
+ let [large = 0, medium = 0] = itemSizes;
249
+ const {
250
+ className
251
+ } = this;
252
+
253
+ // update DOM
254
+ for (const itemElement of listElement.children) {
255
+ let lg = false,
256
+ md = false;
257
+ if (large > 0) {
258
+ lg = true;
259
+ large--;
260
+ } else if (medium > 0) {
261
+ md = true;
262
+ medium--;
263
+ }
264
+ (0, _dom.addOrRemoveClass)(itemElement, `${className}__item-large`, lg);
265
+ (0, _dom.addOrRemoveClass)(itemElement, `${className}__item-medium`, md);
266
+ (0, _dom.addOrRemoveClass)(itemElement, `${className}__item-small`, !lg && !md);
267
+ }
268
+ }
269
+ }
270
+ exports.default = GalleryLayout;
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.SwipeableMixin = void 0;
7
7
  exports.makeSwipeable = makeSwipeable;
8
8
  var _commons = require("@miso.ai/commons");
9
+ var _dom = require("../../util/dom.js");
9
10
  function makeSwipeable(prototype) {
10
11
  (0, _commons.mixin)(prototype, SwipeableMixin.prototype);
11
12
  }
@@ -36,7 +37,7 @@ class SwipeableMixin {
36
37
  index = 0;
37
38
  }
38
39
  context.itemIndex = index;
39
- (0, _commons.requestAnimationFrame)(() => this._syncSwipeableItemIndex());
40
+ (0, _commons.requestAnimationFrame)(() => this._syncDisplayedSwipeableItemIndex());
40
41
  }
41
42
  next() {
42
43
  if (this._swipeableContext.itemCount === undefined) {
@@ -52,7 +53,8 @@ class SwipeableMixin {
52
53
  }
53
54
  _renderSwipeable(element, states, controls) {
54
55
  this._syncSwipeableItemCount(states);
55
- this._syncSwipeableItemIndex();
56
+ this._syncDisplayedSwipeableItemCount();
57
+ this._syncDisplayedSwipeableItemIndex();
56
58
  }
57
59
  _afterRenderSwipeable() {
58
60
  this._emitSwipeableDisplayEvent();
@@ -74,7 +76,6 @@ class SwipeableMixin {
74
76
  } else if (oldItemCount === undefined) {
75
77
  context.itemIndex = 0;
76
78
  }
77
- this._syncDisplayedSwipeableItemCount();
78
79
  }
79
80
  _syncDisplayedSwipeableItemCount() {
80
81
  const context = this._swipeableContext;
@@ -91,22 +92,20 @@ class SwipeableMixin {
91
92
  if (!element) {
92
93
  return;
93
94
  }
95
+ (0, _dom.setOrRemoveAttribute)(element, 'data-item-count', itemCount);
94
96
  context.displayedCount = itemCount;
95
- if (itemCount === undefined) {
96
- element.removeAttribute('data-item-count');
97
- } else {
98
- element.setAttribute('data-item-count', itemCount);
99
- }
100
97
  }
101
- _syncSwipeableItemIndex() {
98
+ _syncDisplayedSwipeableItemIndex() {
102
99
  const context = this._swipeableContext;
103
- const index = context.itemIndex;
104
- if (index === context.displayedIndex) {
100
+ const {
101
+ itemIndex
102
+ } = context;
103
+ if (itemIndex === context.displayedIndex) {
105
104
  return;
106
105
  }
107
- context.displayedIndex = index;
108
- this._syncSwipeableListIndex(index);
109
- this._syncSwipeableIndicatorIndex(index);
106
+ context.displayedIndex = itemIndex;
107
+ this._syncSwipeableListIndex(itemIndex);
108
+ this._syncSwipeableIndicatorIndex(itemIndex);
110
109
  }
111
110
  _syncSwipeableListIndex(index) {
112
111
  const {
@@ -128,7 +127,7 @@ class SwipeableMixin {
128
127
  }
129
128
  let i = 0;
130
129
  for (const item of indicatorElement.children) {
131
- item.classList[i === index ? 'add' : 'remove']('active');
130
+ (0, _dom.addOrRemoveClass)(item, 'active', i === index);
132
131
  i++;
133
132
  }
134
133
  }
@@ -115,6 +115,8 @@ class RafLayout {
115
115
  state = Object.freeze(Object.assign({}, state, stateExtra.value));
116
116
  this._rendered.set(this._element, state);
117
117
  notifyUpdate && notifyUpdate(...argsForNotifyUpdate.value);
118
+
119
+ // TODO: should the signature be aligned with _render?
118
120
  this._afterRender(this._element, state);
119
121
  }
120
122
  _render() {