@miso.ai/client-sdk-ui 1.11.0-beta.1 → 1.11.0-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 (118) hide show
  1. package/dist/cjs/actor/data.js +6 -7
  2. package/dist/cjs/actor/feedback.js +6 -39
  3. package/dist/cjs/actor/fields.js +14 -6
  4. package/dist/cjs/actor/hub.js +1 -2
  5. package/dist/cjs/actor/index.js +0 -7
  6. package/dist/cjs/actor/interactions.js +4 -15
  7. package/dist/cjs/actor/sessions.js +2 -33
  8. package/dist/cjs/actor/tracker.js +25 -26
  9. package/dist/cjs/actor/utils.js +0 -49
  10. package/dist/cjs/actor/view.js +40 -40
  11. package/dist/cjs/actor/views.js +32 -8
  12. package/dist/cjs/asset/svgs.js +18 -8
  13. package/dist/cjs/constants.js +18 -19
  14. package/dist/cjs/defaults/hybrid-search/answer-box.js +19 -3
  15. package/dist/cjs/defaults/hybrid-search/phrases.js +5 -5
  16. package/dist/cjs/defaults/hybrid-search/templates.js +37 -9
  17. package/dist/cjs/element/role/index.js +12 -5
  18. package/dist/cjs/element/role/{miso-hits.js → miso-more.js} +4 -4
  19. package/dist/cjs/element/role/miso-total.js +20 -0
  20. package/dist/cjs/layout/affiliation/affiliation.js +0 -3
  21. package/dist/cjs/layout/banner/banner.js +0 -4
  22. package/dist/cjs/layout/button/more-button.js +73 -0
  23. package/dist/cjs/layout/container/container.js +0 -3
  24. package/dist/cjs/layout/error/error.js +0 -4
  25. package/dist/cjs/layout/index.js +7 -0
  26. package/dist/cjs/layout/input/facets.js +5 -5
  27. package/dist/cjs/layout/input/option-list.js +0 -4
  28. package/dist/cjs/layout/input/search-box.js +33 -12
  29. package/dist/cjs/layout/list/cards.js +0 -3
  30. package/dist/cjs/layout/list/carousel.js +0 -3
  31. package/dist/cjs/layout/list/collection.js +25 -7
  32. package/dist/cjs/layout/list/list.js +0 -3
  33. package/dist/cjs/layout/radio/feedback.js +0 -3
  34. package/dist/cjs/layout/radio/radio.js +0 -4
  35. package/dist/cjs/layout/raf.js +4 -2
  36. package/dist/cjs/layout/text/text.js +0 -4
  37. package/dist/cjs/layout/text/typewriter/index.js +0 -3
  38. package/dist/cjs/layout/triggerable.js +79 -0
  39. package/dist/cjs/util/states.js +0 -15
  40. package/dist/cjs/version.js +1 -1
  41. package/dist/cjs/workflow/answer-based.js +109 -111
  42. package/dist/cjs/workflow/ask.js +35 -27
  43. package/dist/cjs/workflow/asks.js +1 -1
  44. package/dist/cjs/workflow/autocompletable.js +33 -0
  45. package/dist/cjs/workflow/autocomplete.js +122 -0
  46. package/dist/cjs/workflow/base.js +169 -102
  47. package/dist/cjs/workflow/explore.js +26 -19
  48. package/dist/cjs/workflow/hybrid-search-answer.js +99 -0
  49. package/dist/cjs/workflow/hybrid-search-results.js +70 -0
  50. package/dist/cjs/workflow/hybrid-search-views.js +122 -0
  51. package/dist/cjs/workflow/hybrid-search.js +98 -103
  52. package/dist/cjs/workflow/options.js +32 -17
  53. package/dist/cjs/workflow/processors.js +185 -17
  54. package/dist/cjs/workflow/recommendation.js +36 -49
  55. package/dist/cjs/workflow/search-based.js +186 -0
  56. package/dist/cjs/workflow/search.js +21 -68
  57. package/package.json +2 -2
  58. package/src/actor/data.js +2 -4
  59. package/src/actor/feedback.js +6 -23
  60. package/src/actor/fields.js +14 -6
  61. package/src/actor/hub.js +1 -2
  62. package/src/actor/index.js +0 -1
  63. package/src/actor/interactions.js +4 -10
  64. package/src/actor/sessions.js +3 -30
  65. package/src/actor/tracker.js +25 -19
  66. package/src/actor/utils.js +0 -47
  67. package/src/actor/view.js +27 -15
  68. package/src/actor/views.js +36 -8
  69. package/src/asset/svgs.js +19 -7
  70. package/src/constants.js +25 -16
  71. package/src/defaults/hybrid-search/answer-box.js +17 -5
  72. package/src/defaults/hybrid-search/phrases.js +4 -4
  73. package/src/defaults/hybrid-search/templates.js +25 -9
  74. package/src/element/role/index.js +2 -1
  75. package/src/element/role/{miso-hits.js → miso-more.js} +3 -3
  76. package/src/element/role/miso-total.js +16 -0
  77. package/src/layout/affiliation/affiliation.js +1 -5
  78. package/src/layout/banner/banner.js +0 -5
  79. package/src/layout/button/more-button.js +69 -0
  80. package/src/layout/container/container.js +1 -5
  81. package/src/layout/error/error.js +0 -5
  82. package/src/layout/index.js +2 -1
  83. package/src/layout/input/facets.js +5 -6
  84. package/src/layout/input/option-list.js +0 -5
  85. package/src/layout/input/search-box.js +27 -14
  86. package/src/layout/list/cards.js +0 -4
  87. package/src/layout/list/carousel.js +0 -4
  88. package/src/layout/list/collection.js +22 -9
  89. package/src/layout/list/list.js +0 -4
  90. package/src/layout/radio/feedback.js +0 -4
  91. package/src/layout/radio/radio.js +0 -5
  92. package/src/layout/raf.js +2 -2
  93. package/src/layout/text/text.js +0 -5
  94. package/src/layout/text/typewriter/index.js +1 -5
  95. package/src/layout/trackable.js +1 -1
  96. package/src/layout/triggerable.js +76 -0
  97. package/src/util/states.js +0 -14
  98. package/src/version.js +1 -1
  99. package/src/workflow/answer-based.js +101 -77
  100. package/src/workflow/ask.js +33 -25
  101. package/src/workflow/asks.js +1 -1
  102. package/src/workflow/autocompletable.js +29 -0
  103. package/src/workflow/autocomplete.js +113 -0
  104. package/src/workflow/base.js +147 -93
  105. package/src/workflow/explore.js +23 -8
  106. package/src/workflow/hybrid-search-answer.js +92 -0
  107. package/src/workflow/hybrid-search-results.js +63 -0
  108. package/src/workflow/hybrid-search-views.js +124 -0
  109. package/src/workflow/hybrid-search.js +102 -73
  110. package/src/workflow/options.js +26 -11
  111. package/src/workflow/processors.js +165 -14
  112. package/src/workflow/recommendation.js +32 -33
  113. package/src/workflow/search-based.js +183 -0
  114. package/src/workflow/search.js +21 -65
  115. package/dist/cjs/actor/autocomplete.js +0 -160
  116. package/dist/cjs/workflow/utils.js +0 -42
  117. package/src/actor/autocomplete.js +0 -133
  118. package/src/workflow/utils.js +0 -29
package/src/asset/svgs.js CHANGED
@@ -10,12 +10,18 @@ function trianglePath(r) {
10
10
  const PREFIX = 'miso-ui-icon';
11
11
  const ID = `${PREFIX}-svg-${version.replaceAll('.', '_')}`;
12
12
 
13
+ const ICON_NAME = {
14
+ SEND: 'send',
15
+ SEARCH: 'search',
16
+ TRIANGLE: 'triangle',
17
+ };
18
+
13
19
  // CC0 SVGs
14
- const ICON_SEND = `<symbol id="${PREFIX}-send" viewBox="0 0 32 32"><path fill="currentColor" d="M11.499,19.173l5.801,-5.849c0.389,-0.392 1.022,-0.394 1.414,-0.006c0.392,0.389 0.395,1.022 0.006,1.414l-5.798,5.847l5.306,8.002c0.207,0.313 0.572,0.483 0.945,0.441c0.373,-0.042 0.691,-0.289 0.824,-0.64l9.024,-23.904c0.138,-0.366 0.05,-0.78 -0.226,-1.058c-0.276,-0.278 -0.689,-0.369 -1.057,-0.233l-24.004,8.892c-0.353,0.13 -0.602,0.448 -0.646,0.821c-0.044,0.373 0.125,0.74 0.438,0.948l7.973,5.325Z"/></symbol>`;
15
- const ICON_SEARCH = `<symbol id="${PREFIX}-search" viewBox="0 0 24 24"><path fill="currentColor" d="M4 11C4 7.13401 7.13401 4 11 4C14.866 4 18 7.13401 18 11C18 14.866 14.866 18 11 18C7.13401 18 4 14.866 4 11ZM11 2C6.02944 2 2 6.02944 2 11C2 15.9706 6.02944 20 11 20C13.125 20 15.078 19.2635 16.6177 18.0319L20.2929 21.7071C20.6834 22.0976 21.3166 22.0976 21.7071 21.7071C22.0976 21.3166 22.0976 20.6834 21.7071 20.2929L18.0319 16.6177C19.2635 15.078 20 13.125 20 11C20 6.02944 15.9706 2 11 2Z"/></symbol>`;
20
+ const ICON_SEND = `<symbol id="${PREFIX}-${ICON_NAME.SEND}" viewBox="0 0 32 32"><path fill="currentColor" d="M11.499,19.173l5.801,-5.849c0.389,-0.392 1.022,-0.394 1.414,-0.006c0.392,0.389 0.395,1.022 0.006,1.414l-5.798,5.847l5.306,8.002c0.207,0.313 0.572,0.483 0.945,0.441c0.373,-0.042 0.691,-0.289 0.824,-0.64l9.024,-23.904c0.138,-0.366 0.05,-0.78 -0.226,-1.058c-0.276,-0.278 -0.689,-0.369 -1.057,-0.233l-24.004,8.892c-0.353,0.13 -0.602,0.448 -0.646,0.821c-0.044,0.373 0.125,0.74 0.438,0.948l7.973,5.325Z"/></symbol>`;
21
+ const ICON_SEARCH = `<symbol id="${PREFIX}-${ICON_NAME.SEARCH}" viewBox="0 0 24 24"><path fill="currentColor" d="M4 11C4 7.13401 7.13401 4 11 4C14.866 4 18 7.13401 18 11C18 14.866 14.866 18 11 18C7.13401 18 4 14.866 4 11ZM11 2C6.02944 2 2 6.02944 2 11C2 15.9706 6.02944 20 11 20C13.125 20 15.078 19.2635 16.6177 18.0319L20.2929 21.7071C20.6834 22.0976 21.3166 22.0976 21.7071 21.7071C22.0976 21.3166 22.0976 20.6834 21.7071 20.2929L18.0319 16.6177C19.2635 15.078 20 13.125 20 11C20 6.02944 15.9706 2 11 2Z"/></symbol>`;
16
22
 
17
23
  // Custom SVGs
18
- const ICON_TRIANGLE = `<symbol id="${PREFIX}-triangle" viewBox="-5 -5 105 105"><path d="${trianglePath(5)}"/></symbol>`;
24
+ const ICON_TRIANGLE = `<symbol id="${PREFIX}-${ICON_NAME.TRIANGLE}" viewBox="-5 -5 105 105"><path d="${trianglePath(5)}"/></symbol>`;
19
25
 
20
26
  const ICONS = [ICON_SEND, ICON_SEARCH, ICON_TRIANGLE];
21
27
 
@@ -33,10 +39,16 @@ function loadSvgSprites() {
33
39
 
34
40
  loadSvgSprites();
35
41
 
36
- function getSvg(name) {
42
+ function _icon(name) {
37
43
  return `<svg class="miso-ui-icon miso-ui-icon-${name}"><use href="#${PREFIX}-${name}"></use></svg>`;
38
44
  }
39
45
 
40
- export const SEND = getSvg('send');
41
- export const SEARCH = getSvg('search');
42
- export const TRIANGLE = getSvg('triangle');
46
+ export const ICON_NAMES = new Set(Object.values(ICON_NAME));
47
+
48
+ export function getIcon(name) {
49
+ return ICON_NAMES.has(name) ? _icon(name) : '';
50
+ }
51
+
52
+ export const SEND = _icon(ICON_NAME.SEND);
53
+ export const SEARCH = _icon(ICON_NAME.SEARCH);
54
+ export const TRIANGLE = _icon(ICON_NAME.TRIANGLE);
package/src/constants.js CHANGED
@@ -22,7 +22,8 @@ export const ROLE = Object.freeze({
22
22
  AFFILIATION: 'affiliation',
23
23
  KEYWORDS: 'keywords',
24
24
  FACETS: 'facets',
25
- HITS: 'hits',
25
+ TOTAL: 'total',
26
+ MORE: 'more',
26
27
  ERROR: 'error',
27
28
  });
28
29
 
@@ -40,34 +41,29 @@ const DATA_ROLE_SET = new Set([
40
41
  ROLE.RELATED_QUESTIONS,
41
42
  ROLE.AFFILIATION,
42
43
  ROLE.FACETS,
43
- ROLE.HITS,
44
+ ROLE.TOTAL,
44
45
  ]);
45
46
 
46
47
  export function isDataRole(role) {
47
48
  return DATA_ROLE_SET.has(role);
48
49
  }
49
50
 
50
- export const DATA_ASPECT = Object.freeze({
51
- AUTOCOMPLETE: 'autocomplete',
52
- });
51
+ const PRODUCT_ROLE_SET = new Set([
52
+ ROLE.PRODUCTS,
53
+ ROLE.SOURCES,
54
+ ROLE.RELATED_RESOURCES,
55
+ ]);
53
56
 
54
- export const LAYOUT_CATEGORY = Object.freeze({
55
- CONTAINER: 'container',
56
- BANNER: 'banner',
57
- LIST: 'list',
58
- QUERY: 'query',
59
- RADIO: 'radio',
60
- FILTERS: 'filters',
61
- TEXT: 'text',
62
- AFFILIATION: 'affiliation',
63
- ERROR: 'error',
64
- });
57
+ export function isProductRole(role) {
58
+ return PRODUCT_ROLE_SET.has(role);
59
+ }
65
60
 
66
61
  export const EVENT_TYPE = Object.freeze({
67
62
  IMPRESSION: 'impression',
68
63
  VIEWABLE: 'viewable',
69
64
  CLICK: 'click',
70
65
  SUBMIT: 'submit',
66
+ FEEDBACK: 'feedback',
71
67
  });
72
68
 
73
69
  export const TRACKING_EVENT_TYPES = [
@@ -75,8 +71,21 @@ export const TRACKING_EVENT_TYPES = [
75
71
  EVENT_TYPE.VIEWABLE,
76
72
  EVENT_TYPE.CLICK,
77
73
  EVENT_TYPE.SUBMIT,
74
+ EVENT_TYPE.FEEDBACK,
75
+ ];
76
+
77
+ export const PERFORMANCE_EVENT_TYPES = [
78
+ EVENT_TYPE.IMPRESSION,
79
+ EVENT_TYPE.VIEWABLE,
80
+ EVENT_TYPE.CLICK,
78
81
  ];
79
82
 
83
+ const PERFORMANCE_EVENT_TYPE_SET = new Set(PERFORMANCE_EVENT_TYPES);
84
+
85
+ export function isPerformanceEventType(type) {
86
+ return PERFORMANCE_EVENT_TYPE_SET.has(type);
87
+ }
88
+
80
89
  export const TRACKING_STATUS = Object.freeze({
81
90
  UNTRACKED: 'untracked',
82
91
  TRACKING: 'tracking',
@@ -1,5 +1,6 @@
1
1
  import { CLASS_PREFIX } from './constants.js';
2
2
  import { STATUS } from '../../constants.js';
3
+ import { fields } from '../../actor/index.js';
3
4
  import { showMore as DEFAULT_PHRASE_SHOW_MORE, showLess as DEFAULT_PHRASE_SHOW_LESS } from './phrases.js';
4
5
 
5
6
  const CLASS_OPEN = 'answer-box-open';
@@ -23,7 +24,12 @@ export default class AnswerBox {
23
24
 
24
25
  // expose widget API
25
26
  workflow.answerBox = this;
26
- workflow._unsubscribes.push(() => this.destroy()); // TODO: review
27
+ workflow._unsubscribes.push(() => {
28
+ if (workflow.answerBox === this) {
29
+ this.destroy();
30
+ workflow.answerBox = undefined;
31
+ }
32
+ });
27
33
 
28
34
  // handle toggle button clicks
29
35
  const callback = event => this._handleClick(event);
@@ -35,9 +41,8 @@ export default class AnswerBox {
35
41
 
36
42
  this._unsubscribes = [
37
43
  () => element.removeEventListener('click', callback),
38
- workflow.on('loading', () => this.hide()),
39
- workflow.on('error', () => this.show()),
40
- workflow.on('ready', () => this.show()),
44
+ // cling to data rather than view, as we may miss view event when tab is inactive due to raf
45
+ workflow._answer._hub.on(fields.data(), () => this._syncWofkflowStatus()),
41
46
  ];
42
47
  }
43
48
 
@@ -52,7 +57,7 @@ export default class AnswerBox {
52
57
  }
53
58
 
54
59
  _syncWofkflowStatus() {
55
- const { status } = this._workflow;
60
+ const { status } = this._workflow._answer;
56
61
  switch (status) {
57
62
  case STATUS.ERRONEOUS:
58
63
  case STATUS.READY:
@@ -74,6 +79,12 @@ export default class AnswerBox {
74
79
  }
75
80
  }
76
81
 
82
+ _syncScroll() {
83
+ if (!this.isOpen) {
84
+ this._element.scrollIntoView({ behavior: 'smooth' });
85
+ }
86
+ }
87
+
77
88
  destroy() {
78
89
  for (const unsubscribe of this._unsubscribes) {
79
90
  unsubscribe();
@@ -113,6 +124,7 @@ for (const [method, [action, className]] of Object.entries(METHODS)) {
113
124
  this._updateBoxStatus(action, className);
114
125
  if (className === CLASS_OPEN) {
115
126
  this._syncButtonText();
127
+ this._syncScroll();
116
128
  }
117
129
  };
118
130
  }
@@ -1,7 +1,7 @@
1
1
  /**
2
- * The phrase displayed right before <miso-question>
2
+ * The phrase to display <miso-question>
3
3
  */
4
- export const question = 'You asked...';
4
+ export const question = 'You asked “${value}”';
5
5
 
6
6
  /**
7
7
  * The phrase displayed right before <miso-sources>
@@ -14,9 +14,9 @@ export const sources = 'Sources';
14
14
  export const keywords = 'You searched for “${value}”';
15
15
 
16
16
  /**
17
- * The phrase to display <miso-hits>
17
+ * The phrase to display <miso-total>
18
18
  */
19
- export const hits = '${value} Results';
19
+ export const total = '${value} Results';
20
20
 
21
21
  export const showMore = 'Show more';
22
22
  export const showLess = 'Show less';
@@ -18,6 +18,11 @@ class Sections extends _Sections {
18
18
  ]);
19
19
  }
20
20
 
21
+ question(options) {
22
+ const { phrase } = this._helpers;
23
+ return phrase(options, { name: 'question', tag: 'div' }).replaceAll('${value}', '<miso-question></miso-question>');
24
+ }
25
+
21
26
  answerBoxToggle(options) {
22
27
  const { element } = this._helpers;
23
28
  const { classPrefix } = options;
@@ -28,10 +33,11 @@ class Sections extends _Sections {
28
33
 
29
34
  searchResultsGroup(options) {
30
35
  const { container } = this._helpers;
31
- return container(options, { name: 'search-results', visibleWhen: 'ready' }, [
36
+ return container(options, { name: 'search-results', visibleWhen: 'loading ready' }, [
32
37
  this.searchResultsInfoGroup(options),
33
38
  this.searchResultsFilters(options),
34
39
  '<miso-products></miso-products>',
40
+ this.more(options),
35
41
  ]);
36
42
  }
37
43
 
@@ -40,7 +46,7 @@ class Sections extends _Sections {
40
46
  const { classPrefix } = options;
41
47
  return element('div', { classes: [`${classPrefix}__search-results-info`] }, [
42
48
  this.keywords(options),
43
- this.hits(options),
49
+ this.total(options),
44
50
  ]);
45
51
  }
46
52
 
@@ -57,9 +63,20 @@ class Sections extends _Sections {
57
63
  return phrase(options, { name: 'keywords', tag: 'div', inline: true }).replaceAll('${value}', '<miso-keywords></miso-keywords>');
58
64
  }
59
65
 
60
- hits(options) {
66
+ total(options) {
61
67
  const { phrase } = this._helpers;
62
- return phrase(options, { name: 'hits', tag: 'div', inline: true }).replaceAll('${value}', '<miso-hits></miso-hits>');
68
+ return phrase(options, { name: 'total', tag: 'div', inline: true }).replaceAll('${value}', '<miso-total></miso-total>');
69
+ }
70
+
71
+ more(options) {
72
+ if (!options.moreButton) {
73
+ return '';
74
+ }
75
+ const { element } = this._helpers;
76
+ const { classPrefix } = options;
77
+ return element('div', { classes: [`${classPrefix}__search-results-more-container`] }, [
78
+ '<miso-more></miso-more>',
79
+ ]);
63
80
  }
64
81
 
65
82
  }
@@ -70,14 +87,13 @@ export const sections = externalize(new Sections({ helpers }));
70
87
  export function root(options = {}) {
71
88
  const { normalizeOptions, section, container } = helpers;
72
89
  options = normalizeOptions(options);
73
- const { answerBox } = sections;
90
+ const { answerBox, answerGroup, searchResultsGroup } = sections;
91
+ const { answerBox: answerBoxOptions = true } = options;
74
92
  return [
75
93
  section(options, { name: 'question' }, [
76
94
  container(options, { name: 'query' }, '<miso-query></miso-query>'),
77
95
  ]),
78
- section(options, { name: 'answer' }, [
79
- answerBox(options),
80
- ]),
81
- section(options, { name: 'search-results' }, sections.searchResultsGroup(options)),
96
+ section(options, { name: 'answer' }, answerBoxOptions ? [ answerBox(options) ] : answerGroup(options)),
97
+ section(options, { name: 'search-results' }, searchResultsGroup(options)),
82
98
  ].join('');
83
99
  };
@@ -11,6 +11,7 @@ export { default as MisoRelatedResourcesElement } from './miso-related-resources
11
11
  export { default as MisoRelatedQuestionsElement } from './miso-related-questions.js';
12
12
  export { default as MisoSovrnAffiliationElement } from './miso-affiliation.js';
13
13
  export { default as MisoKeywordsElement } from './miso-keywords.js';
14
- export { default as MisoHitsElement } from './miso-hits.js';
14
+ export { default as MisoTotalElement } from './miso-total.js';
15
15
  export { default as MisoFacetsElement } from './miso-facets.js';
16
+ export { default as MisoMoreElement } from './miso-more.js';
16
17
  export { default as MisoErrorElement } from './miso-error.js';
@@ -1,16 +1,16 @@
1
1
  import MisoComponentElement from './miso-component.js';
2
2
  import { ROLE } from '../../constants.js';
3
3
 
4
- export default class MisoHitsElement extends MisoComponentElement {
4
+ export default class MisoMoreElement extends MisoComponentElement {
5
5
 
6
6
  constructor() {
7
7
  super({
8
- role: ROLE.HITS,
8
+ role: ROLE.MORE,
9
9
  });
10
10
  }
11
11
 
12
12
  static get tagName() {
13
- return 'miso-hits';
13
+ return 'miso-more';
14
14
  }
15
15
 
16
16
  }
@@ -0,0 +1,16 @@
1
+ import MisoComponentElement from './miso-component.js';
2
+ import { ROLE } from '../../constants.js';
3
+
4
+ export default class MisoTotalElement extends MisoComponentElement {
5
+
6
+ constructor() {
7
+ super({
8
+ role: ROLE.TOTAL,
9
+ });
10
+ }
11
+
12
+ static get tagName() {
13
+ return 'miso-total';
14
+ }
15
+
16
+ }
@@ -1,5 +1,5 @@
1
1
  import { CarouselItemViewabilityObserver, requestAnimationFrame as raf } from '@miso.ai/commons';
2
- import { STATUS, LAYOUT_CATEGORY } from '../../constants.js';
2
+ 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';
@@ -136,10 +136,6 @@ function autoplayOptionsEquals(a, b) {
136
136
 
137
137
  export default class AffiliationLayout extends CollectionLayout {
138
138
 
139
- static get category() {
140
- return LAYOUT_CATEGORY.AFFILIATION;
141
- }
142
-
143
139
  static get type() {
144
140
  return TYPE;
145
141
  }
@@ -1,4 +1,3 @@
1
- import { LAYOUT_CATEGORY } from '../../constants.js';
2
1
  import TemplateBasedLayout from '../template.js';
3
2
  import { LOGO } from '../svgs.js';
4
3
 
@@ -19,10 +18,6 @@ const DEFAULT_TEMPLATES = Object.freeze({
19
18
 
20
19
  export default class BannerLayout extends TemplateBasedLayout {
21
20
 
22
- static get category() {
23
- return LAYOUT_CATEGORY.BANNER;
24
- }
25
-
26
21
  static get type() {
27
22
  return TYPE;
28
23
  }
@@ -0,0 +1,69 @@
1
+ import { STATUS } from '../../constants.js';
2
+ import { fields } from '../../actor/index.js';
3
+ import TemplateBasedLayout from '../template.js';
4
+
5
+ const TYPE = 'more-button';
6
+ const DEFAULT_CLASSNAME = 'miso-more-button';
7
+
8
+ function root(layout, state) {
9
+ const { className, role, templates } = layout;
10
+ const roleAttr = role ? ` data-role="${role}"` : '';
11
+ const disabledAttr = state.status !== STATUS.READY ? ' disabled' : '';
12
+ return `
13
+ <div class="${className}"${roleAttr}${disabledAttr}>
14
+ ${templates.text(layout)}
15
+ </div>
16
+ `;
17
+ }
18
+
19
+ const DEFAULT_TEMPLATES = Object.freeze({
20
+ root,
21
+ text,
22
+ });
23
+
24
+ function text(layout) {
25
+ return 'More';
26
+ }
27
+
28
+ export default class MoreButtonLayout extends TemplateBasedLayout {
29
+
30
+ static get type() {
31
+ return TYPE;
32
+ }
33
+
34
+ static get defaultTemplates() {
35
+ return DEFAULT_TEMPLATES;
36
+ }
37
+
38
+ static get defaultClassName() {
39
+ return DEFAULT_CLASSNAME;
40
+ }
41
+
42
+ constructor({ className = DEFAULT_CLASSNAME, templates, ...options } = {}) {
43
+ super({
44
+ className,
45
+ templates: { ...DEFAULT_TEMPLATES, ...templates },
46
+ ...options,
47
+ });
48
+ }
49
+
50
+ initialize(view) {
51
+ this._unsubscribes = [
52
+ ...this._unsubscribes,
53
+ view.proxyElement.on('click', (e) => this._handleClick(e)),
54
+ ];
55
+ }
56
+
57
+ _handleClick(event) {
58
+ // only left click
59
+ if (event.button !== 0) {
60
+ return;
61
+ }
62
+ const button = event.target.closest('data-role="more"');
63
+ if (!button || button.disabled) {
64
+ return;
65
+ }
66
+ this._view.hub.trigger(fields.more());
67
+ }
68
+
69
+ }
@@ -1,4 +1,4 @@
1
- import { ROLE, STATUS, LAYOUT_CATEGORY } from '../../constants.js';
1
+ import { ROLE, STATUS } from '../../constants.js';
2
2
  import RafLayout from '../raf.js';
3
3
  import MisoBannerElement from '../../element/miso-banner.js';
4
4
  import Viewables from '../../util/viewables.js';
@@ -7,10 +7,6 @@ const TYPE = 'container';
7
7
 
8
8
  export default class ContainerLayout extends RafLayout {
9
9
 
10
- static get category() {
11
- return LAYOUT_CATEGORY.CONTAINER;
12
- }
13
-
14
10
  static get type() {
15
11
  return TYPE;
16
12
  }
@@ -1,4 +1,3 @@
1
- import { LAYOUT_CATEGORY } from '../../constants.js';
2
1
  import TemplateBasedLayout from '../template.js';
3
2
 
4
3
  const TYPE = 'error';
@@ -24,10 +23,6 @@ const DEFAULT_TEMPLATES = Object.freeze({
24
23
 
25
24
  export default class ErrorLayout extends TemplateBasedLayout {
26
25
 
27
- static get category() {
28
- return LAYOUT_CATEGORY.ERROR;
29
- }
30
-
31
26
  static get type() {
32
27
  return TYPE;
33
28
  }
@@ -12,4 +12,5 @@ export { default as FeedbackLayout } from './radio/feedback.js';
12
12
  export { default as TextLayout } from './text/text.js';
13
13
  export { default as TypewriterLayout } from './text/typewriter/index.js';
14
14
  export { default as AffiliationLayout } from './affiliation/affiliation.js';
15
- export { default as FacetsLayout } from './input/facets.js';
15
+ export { default as FacetsLayout } from './input/facets.js';
16
+ export { default as MoreButtonLayout } from './button/more-button.js';
@@ -1,5 +1,4 @@
1
1
  import { escapeHtml } from '@miso.ai/commons';
2
- import { LAYOUT_CATEGORY } from '../../constants.js';
3
2
  import TemplateBasedLayout from '../template.js';
4
3
  import Bindings from '../../util/bindings.js';
5
4
 
@@ -88,10 +87,6 @@ function getSelectedValues(facets) {
88
87
 
89
88
  export default class FacetsLayout extends TemplateBasedLayout {
90
89
 
91
- static get category() {
92
- return LAYOUT_CATEGORY.FILTERS;
93
- }
94
-
95
90
  static get type() {
96
91
  return TYPE;
97
92
  }
@@ -125,11 +120,11 @@ export default class FacetsLayout extends TemplateBasedLayout {
125
120
 
126
121
  _render(element, data, controls) {
127
122
  super._render(element, data, controls);
128
- this._syncSelections();
129
123
  }
130
124
 
131
125
  _afterRender(element, state) {
132
126
  this._syncBindings(element, state);
127
+ this._syncSelections();
133
128
  }
134
129
 
135
130
  _unrender() {
@@ -220,6 +215,10 @@ export default class FacetsLayout extends TemplateBasedLayout {
220
215
  }
221
216
 
222
217
  _handleClick(event) {
218
+ // only left click
219
+ if (event.button !== 0) {
220
+ return;
221
+ }
223
222
  const element = event.target.closest(`[data-role="option"]`);
224
223
  if (!element) {
225
224
  return;
@@ -1,5 +1,4 @@
1
1
  import { escapeHtml } from '@miso.ai/commons';
2
- import { LAYOUT_CATEGORY } from '../../constants.js';
3
2
  import { fields } from '../../actor/index.js';
4
3
  import TemplateBasedLayout from '../template.js';
5
4
  import { makeTrackable } from '../trackable.js';
@@ -41,10 +40,6 @@ const DEFAULT_TEMPLATES = Object.freeze({
41
40
 
42
41
  export default class OptionListLayout extends TemplateBasedLayout {
43
42
 
44
- static get category() {
45
- return LAYOUT_CATEGORY.QUERY;
46
- }
47
-
48
43
  static get type() {
49
44
  return TYPE;
50
45
  }
@@ -1,29 +1,39 @@
1
1
  import { defineValues, escapeHtml, findInAncestors, debounce } from '@miso.ai/commons';
2
- import { LAYOUT_CATEGORY, STATUS, EVENT_TYPE, DATA_ASPECT } from '../../constants.js';
2
+ import { STATUS, EVENT_TYPE } from '../../constants.js';
3
3
  import { fields } from '../../actor/index.js';
4
4
  import TemplateBasedLayout from '../template.js';
5
5
  import { imageBlock, productInfoBlock } from '../templates.js';
6
- import { SEND, SEARCH } from '../../asset/svgs.js';
6
+ import { getIcon } from '../../asset/svgs.js';
7
7
 
8
8
  const TYPE = 'search-box';
9
9
  const DEFAULT_CLASSNAME = 'miso-search-box';
10
10
  const DEFAULT_AUTOCOMPLETE_CLASSNAME = 'miso-autocomplete';
11
11
 
12
12
  function root(layout) {
13
- const { className, role, options, workflow, autocomplete } = layout;
14
- const { placeholder, buttonText } = options;
13
+ const { className, templates, role, options, autocomplete } = layout;
14
+ const { placeholder } = options;
15
15
  const roleAttr = role ? `data-role="${role}"` : '';
16
- const buttonContent = buttonText || workflow === 'search' ? SEARCH : SEND;
17
16
  return `
18
17
  <div class="${className}" ${roleAttr}>
19
18
  <div class="${className}__input-group">
20
19
  <input class="${className}__input" type="text" data-role="input" ${placeholder ? `placeholder="${placeholder}"` : ''}>
21
- <button class="${className}__button" type="submit" data-role="submit">${buttonContent}</button>
20
+ <button class="${className}__button" type="submit" data-role="submit">${templates.buttonContent(layout)}</button>
22
21
  </div>
23
22
  <div class="${autocomplete.className}" data-role="autocomplete"></div>
24
23
  </div>`.trim();
25
24
  }
26
25
 
26
+ function buttonContent(layout) {
27
+ const { templates, options } = layout;
28
+ if (options.buttonText) {
29
+ return options.buttonText; // legacy
30
+ }
31
+ const { buttonText, buttonIcon } = templates;
32
+ const text = typeof buttonText === 'function' ? buttonText(layout) : `${buttonText}`;
33
+ const icon = typeof buttonIcon === 'function' ? buttonIcon(layout) : (getIcon(buttonIcon) || `${buttonIcon}`);
34
+ return text + icon;
35
+ }
36
+
27
37
  function completions(layout, items) {
28
38
  const { templates, workflow, autocomplete } = layout;
29
39
  // use options under layout.autocomplete from now on
@@ -84,6 +94,9 @@ function completionItem(layout, type, item) {
84
94
 
85
95
  const DEFAULT_TEMPLATES = Object.freeze({
86
96
  root,
97
+ buttonContent,
98
+ buttonText: '',
99
+ buttonIcon: 'search',
87
100
  completions,
88
101
  completionList,
89
102
  completionItem,
@@ -102,10 +115,6 @@ const DEFAULT_AUTOCOMPLETE_OPTIONS = Object.freeze({
102
115
 
103
116
  export default class SearchBoxLayout extends TemplateBasedLayout {
104
117
 
105
- static get category() {
106
- return LAYOUT_CATEGORY.QUERY;
107
- }
108
-
109
118
  static get type() {
110
119
  return TYPE;
111
120
  }
@@ -141,7 +150,7 @@ export default class SearchBoxLayout extends TemplateBasedLayout {
141
150
  proxyElement.on('click', (e) => this._handleClick(e)),
142
151
  proxyElement.on('focusin', (e) => this._handleFocusIn(e)),
143
152
  proxyElement.on('focusout', (e) => this._handleFocusOut(e)),
144
- hub.on(fields.data(DATA_ASPECT.AUTOCOMPLETE), () => view.refresh({ force: true })),
153
+ hub.on(fields.completions(), () => view.refresh({ force: true })),
145
154
  ];
146
155
  }
147
156
 
@@ -175,7 +184,7 @@ export default class SearchBoxLayout extends TemplateBasedLayout {
175
184
  state = super._preprocess({ state });
176
185
  return {
177
186
  ...state,
178
- completions: this._view.hub.states[fields.data(DATA_ASPECT.AUTOCOMPLETE)],
187
+ completions: this._view.hub.states[fields.completions()],
179
188
  };
180
189
  }
181
190
 
@@ -185,6 +194,9 @@ export default class SearchBoxLayout extends TemplateBasedLayout {
185
194
  super._render(element, data, controls);
186
195
  }
187
196
  this._updateCompletions(element, data, controls);
197
+ }
198
+
199
+ _afterRender(element, state) {
188
200
  this._fullfillFocusRequest(element);
189
201
  this._fullfillValueRequest(element);
190
202
  }
@@ -193,7 +205,8 @@ export default class SearchBoxLayout extends TemplateBasedLayout {
193
205
  if (!state.completions) {
194
206
  return;
195
207
  }
196
- const { status = STATUS.INITIAL, index, value: completions } = state.completions;
208
+ const { status = STATUS.INITIAL, session, value: completions } = state.completions;
209
+ const { index } = session;
197
210
  if (rendered && rendered.completions.index === index && rendered.completions.status === status) {
198
211
  return;
199
212
  }
@@ -332,7 +345,7 @@ export default class SearchBoxLayout extends TemplateBasedLayout {
332
345
  if (!options) {
333
346
  return;
334
347
  }
335
- this._view.tracker.submit(value);
348
+ this._view.tracker.submit({ value }); // not array
336
349
  }
337
350
 
338
351
  }
@@ -5,10 +5,6 @@ const DEFAULT_CLASSNAME = 'miso-cards';
5
5
 
6
6
  export default class CardsLayout extends CollectionLayout {
7
7
 
8
- static get category() {
9
- return super.category;
10
- }
11
-
12
8
  static get type() {
13
9
  return TYPE;
14
10
  }
@@ -35,10 +35,6 @@ function chevron(layout) {
35
35
 
36
36
  export default class CarouselLayout extends CollectionLayout {
37
37
 
38
- static get category() {
39
- return super.category;
40
- }
41
-
42
38
  static get type() {
43
39
  return TYPE;
44
40
  }