@miso.ai/client-sdk-ui 1.11.0-beta.6 → 1.11.0-beta.8

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.
@@ -3,7 +3,8 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.TRIANGLE = exports.SEND = exports.SEARCH = void 0;
6
+ exports.TRIANGLE = exports.SEND = exports.SEARCH = exports.ICON_NAMES = void 0;
7
+ exports.getIcon = getIcon;
7
8
  var _version = _interopRequireDefault(require("../version.js"));
8
9
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
9
10
  function trianglePath(r) {
@@ -14,13 +15,18 @@ function trianglePath(r) {
14
15
  }
15
16
  const PREFIX = 'miso-ui-icon';
16
17
  const ID = `${PREFIX}-svg-${_version.default.replaceAll('.', '_')}`;
18
+ const ICON_NAME = {
19
+ SEND: 'send',
20
+ SEARCH: 'search',
21
+ TRIANGLE: 'triangle'
22
+ };
17
23
 
18
24
  // CC0 SVGs
19
- 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>`;
20
- 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>`;
25
+ 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>`;
26
+ 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>`;
21
27
 
22
28
  // Custom SVGs
23
- const ICON_TRIANGLE = `<symbol id="${PREFIX}-triangle" viewBox="-5 -5 105 105"><path d="${trianglePath(5)}"/></symbol>`;
29
+ const ICON_TRIANGLE = `<symbol id="${PREFIX}-${ICON_NAME.TRIANGLE}" viewBox="-5 -5 105 105"><path d="${trianglePath(5)}"/></symbol>`;
24
30
  const ICONS = [ICON_SEND, ICON_SEARCH, ICON_TRIANGLE];
25
31
  const SPRITES_SVG = `<svg id="${ID}" style="display:none;">${ICONS.join('')}</svg>`;
26
32
  let loaded = false;
@@ -32,9 +38,13 @@ function loadSvgSprites() {
32
38
  document.head.insertAdjacentHTML('beforeend', SPRITES_SVG);
33
39
  }
34
40
  loadSvgSprites();
35
- function getSvg(name) {
41
+ function _icon(name) {
36
42
  return `<svg class="miso-ui-icon miso-ui-icon-${name}"><use href="#${PREFIX}-${name}"></use></svg>`;
37
43
  }
38
- const SEND = exports.SEND = getSvg('send');
39
- const SEARCH = exports.SEARCH = getSvg('search');
40
- const TRIANGLE = exports.TRIANGLE = getSvg('triangle');
44
+ const ICON_NAMES = exports.ICON_NAMES = new Set(Object.values(ICON_NAME));
45
+ function getIcon(name) {
46
+ return ICON_NAMES.has(name) ? _icon(name) : '';
47
+ }
48
+ const SEND = exports.SEND = _icon(ICON_NAME.SEND);
49
+ const SEARCH = exports.SEARCH = _icon(ICON_NAME.SEARCH);
50
+ const TRIANGLE = exports.TRIANGLE = _icon(ICON_NAME.TRIANGLE);
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.default = void 0;
7
7
  var _constants = require("./constants.js");
8
8
  var _constants2 = require("../../constants.js");
9
+ var _index = require("../../actor/index.js");
9
10
  var _phrases = require("./phrases.js");
10
11
  const CLASS_OPEN = 'answer-box-open';
11
12
  const CLASS_SHOWN = 'answer-box-shown';
@@ -29,7 +30,12 @@ class AnswerBox {
29
30
 
30
31
  // expose widget API
31
32
  workflow.answerBox = this;
32
- workflow._unsubscribes.push(() => this.destroy()); // TODO: review
33
+ workflow._unsubscribes.push(() => {
34
+ if (workflow.answerBox === this) {
35
+ this.destroy();
36
+ workflow.answerBox = undefined;
37
+ }
38
+ });
33
39
 
34
40
  // handle toggle button clicks
35
41
  const callback = event => this._handleClick(event);
@@ -38,7 +44,9 @@ class AnswerBox {
38
44
  // sync state
39
45
  this._syncWofkflowStatus();
40
46
  this._syncButtonText();
41
- this._unsubscribes = [() => element.removeEventListener('click', callback), workflow._answer.on('loading', () => this.hide()), workflow._answer.on('error', () => this.show()), workflow._answer.on('ready', () => this.show())];
47
+ this._unsubscribes = [() => element.removeEventListener('click', callback),
48
+ // cling to data rather than view, as we may miss view event when tab is inactive due to raf
49
+ workflow._answer._hub.on(_index.fields.data(), () => this._syncWofkflowStatus())];
42
50
  }
43
51
  _handleClick(_ref) {
44
52
  let {
@@ -75,6 +83,13 @@ class AnswerBox {
75
83
  button.textContent = phrase;
76
84
  }
77
85
  }
86
+ _syncScroll() {
87
+ if (!this.isOpen) {
88
+ this._element.scrollIntoView({
89
+ behavior: 'smooth'
90
+ });
91
+ }
92
+ }
78
93
  destroy() {
79
94
  for (const unsubscribe of this._unsubscribes) {
80
95
  unsubscribe();
@@ -110,6 +125,7 @@ for (const [method, [action, className]] of Object.entries(METHODS)) {
110
125
  this._updateBoxStatus(action, className);
111
126
  if (className === CLASS_OPEN) {
112
127
  this._syncButtonText();
128
+ this._syncScroll();
113
129
  }
114
130
  };
115
131
  }
@@ -19,26 +19,40 @@ const DEFAULT_AUTOCOMPLETE_CLASSNAME = 'miso-autocomplete';
19
19
  function root(layout) {
20
20
  const {
21
21
  className,
22
+ templates,
22
23
  role,
23
24
  options,
24
- workflow,
25
25
  autocomplete
26
26
  } = layout;
27
27
  const {
28
- placeholder,
29
- buttonText
28
+ placeholder
30
29
  } = options;
31
30
  const roleAttr = role ? `data-role="${role}"` : '';
32
- const buttonContent = buttonText || workflow === 'search' ? _svgs.SEARCH : _svgs.SEND;
33
31
  return `
34
32
  <div class="${className}" ${roleAttr}>
35
33
  <div class="${className}__input-group">
36
34
  <input class="${className}__input" type="text" data-role="input" ${placeholder ? `placeholder="${placeholder}"` : ''}>
37
- <button class="${className}__button" type="submit" data-role="submit">${buttonContent}</button>
35
+ <button class="${className}__button" type="submit" data-role="submit">${templates.buttonContent(layout)}</button>
38
36
  </div>
39
37
  <div class="${autocomplete.className}" data-role="autocomplete"></div>
40
38
  </div>`.trim();
41
39
  }
40
+ function buttonContent(layout) {
41
+ const {
42
+ templates,
43
+ options
44
+ } = layout;
45
+ if (options.buttonText) {
46
+ return options.buttonText; // legacy
47
+ }
48
+ const {
49
+ buttonText,
50
+ buttonIcon
51
+ } = templates;
52
+ const text = typeof buttonText === 'function' ? buttonText(layout) : `${buttonText}`;
53
+ const icon = typeof buttonIcon === 'function' ? buttonIcon(layout) : (0, _svgs.getIcon)(buttonIcon) || `${buttonIcon}`;
54
+ return text + icon;
55
+ }
42
56
  function completions(layout, items) {
43
57
  const {
44
58
  templates,
@@ -110,6 +124,9 @@ function completionItem(layout, type, item) {
110
124
  }
111
125
  const DEFAULT_TEMPLATES = Object.freeze({
112
126
  root,
127
+ buttonContent,
128
+ buttonText: '',
129
+ buttonIcon: 'search',
113
130
  completions,
114
131
  completionList,
115
132
  completionItem,
@@ -4,4 +4,4 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.default = void 0;
7
- var _default = exports.default = '1.11.0-beta.6';
7
+ var _default = exports.default = '1.11.0-beta.8';
@@ -57,7 +57,8 @@ const DEFAULT_LAYOUTS = Object.freeze({
57
57
  });
58
58
  const DEFAULT_TRACKERS = Object.freeze({
59
59
  [_constants.ROLE.SOURCES]: {},
60
- [_constants.ROLE.AFFILIATION]: {}
60
+ [_constants.ROLE.AFFILIATION]: {},
61
+ [_constants.ROLE.PRODUCTS]: {}
61
62
  });
62
63
  const DEFAULT_OPTIONS = Object.freeze({
63
64
  api: DEFAULT_API_OPTIONS,
@@ -20,6 +20,11 @@ const DEFAULT_API_OPTIONS = Object.freeze(Object.assign({}, _answerBased.default
20
20
  })
21
21
  }));
22
22
  const DEFAULT_LAYOUTS = Object.freeze(Object.assign({}, _answerBased.default.DEFAULT_LAYOUTS, {
23
+ [_constants.ROLE.QUERY]: [_index2.SearchBoxLayout.type, {
24
+ templates: {
25
+ buttonIcon: 'send'
26
+ }
27
+ }],
23
28
  [_constants.ROLE.RELATED_RESOURCES]: [_index2.ListLayout.type, {
24
29
  incremental: true,
25
30
  itemType: 'article'
@@ -26,7 +26,6 @@ class HybridSearchAnswer extends _answerBased.default {
26
26
  rolesConfig: ROLES_CONFIG,
27
27
  superworkflow
28
28
  });
29
- this.useInteractions(false);
30
29
  }
31
30
  _initProperties(args) {
32
31
  super._initProperties(args);
@@ -38,6 +37,7 @@ class HybridSearchAnswer extends _answerBased.default {
38
37
  super.restart();
39
38
  // cascade restart to the sibling
40
39
  const results = this._superworkflow._results;
40
+ results._views.filters.reset();
41
41
  results.restart();
42
42
  this._resultsSession = results.session; // keep track of the session
43
43
  }
@@ -30,7 +30,6 @@ class HybridSearchResults extends _base.default {
30
30
  rolesConfig: ROLES_CONFIG,
31
31
  superworkflow
32
32
  });
33
- this.useInteractions(false);
34
33
  }
35
34
  _initProperties(args) {
36
35
  super._initProperties(args);
@@ -75,7 +74,7 @@ class HybridSearchResults extends _base.default {
75
74
  payload = _objectWithoutPropertiesLoose(_ref, _excluded);
76
75
  // borrow the work from the sibling
77
76
  payload = this._superworkflow._answer._buildPayload(payload);
78
- payload = this._writeFqToPayload(Object.assign({}, payload, {
77
+ payload = this._writeFiltersToPayload(Object.assign({}, payload, {
79
78
  filters
80
79
  }));
81
80
  payload = this._writeQuestionIdToPayload(payload);
@@ -83,16 +82,13 @@ class HybridSearchResults extends _base.default {
83
82
  answer: false
84
83
  });
85
84
  }
86
- _writeFqToPayload() {
85
+ _writeFiltersToPayload() {
87
86
  let _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
88
87
  let {
89
88
  filters
90
89
  } = _ref2,
91
90
  payload = _objectWithoutPropertiesLoose(_ref2, _excluded2);
92
- const fq = (0, _processors.composeFq)(filters); // TODO: combine with fq in payload?
93
- return (0, _commons.trimObj)(Object.assign({}, payload, {
94
- fq
95
- }));
91
+ return (0, _commons.trimObj)(Object.assign({}, payload, (0, _processors.composeFiltersPayload)(filters)));
96
92
  }
97
93
  _writeQuestionIdToPayload(payload) {
98
94
  return Object.assign({}, payload, {
@@ -16,7 +16,8 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
16
16
  const DEFAULT_API_OPTIONS = Object.freeze(Object.assign({}, _answerBased.default.DEFAULT_API_OPTIONS, {
17
17
  name: _commons.API.NAME.SEARCH,
18
18
  payload: Object.assign({}, _answerBased.default.DEFAULT_API_OPTIONS.payload, {
19
- fl: ['cover_image', 'url', 'created_at', 'updated_at', 'published_at']
19
+ source_fl: ['cover_image', 'url', 'created_at', 'updated_at', 'published_at', 'title'],
20
+ fl: ['cover_image', 'url', 'created_at', 'updated_at', 'published_at', 'title']
20
21
  })
21
22
  }));
22
23
  const DEFAULT_LAYOUTS = Object.freeze(Object.assign({}, _answerBased.default.DEFAULT_LAYOUTS, {
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.composeFq = composeFq;
6
+ exports.composeFiltersPayload = composeFiltersPayload;
7
7
  exports.writeAnswerStageToMeta = writeAnswerStageToMeta;
8
8
  exports.writeDataStatus = writeDataStatus;
9
9
  exports.writeKeywordsToData = writeKeywordsToData;
@@ -94,21 +94,30 @@ function writeAnswerStageToMeta(data) {
94
94
  })
95
95
  });
96
96
  }
97
- function composeFq(filters) {
97
+ function composeFiltersPayload(filters) {
98
98
  if (!filters) {
99
99
  return undefined;
100
100
  }
101
- const clauses = [];
102
101
  const {
103
102
  facets
104
103
  } = filters;
104
+ return (0, _commons.trimObj)({
105
+ facet_filters: composeFacetFilters(facets)
106
+ });
107
+ }
108
+ function composeFacetFilters(facets) {
109
+ if (!facets) {
110
+ return undefined;
111
+ }
112
+ const filters = {};
105
113
  for (const field in facets) {
106
114
  const values = facets[field];
107
115
  if (!values || !values.length) {
108
116
  continue; // just in case
109
117
  }
110
- const vstr = values.length === 1 ? `"${values[0]}"` : `(${values.map(v => `"${v}"`).join(' OR ')})`;
111
- clauses.push(`${field}:${vstr}`);
118
+ filters[field] = {
119
+ terms: values
120
+ };
112
121
  }
113
- return clauses.map(c => `(${c})`).join(' AND ') || undefined;
122
+ return Object.keys(filters).length ? filters : undefined;
114
123
  }
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.0-beta.6"
29
+ "@miso.ai/commons": "1.11.0-beta.8"
30
30
  },
31
- "version": "1.11.0-beta.6"
31
+ "version": "1.11.0-beta.8"
32
32
  }
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);
@@ -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._answer.on('loading', () => this.hide()),
39
- workflow._answer.on('error', () => this.show()),
40
- workflow._answer.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
 
@@ -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
  }
@@ -3,27 +3,37 @@ import { LAYOUT_CATEGORY, STATUS, EVENT_TYPE, DATA_ASPECT } from '../../constant
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,
package/src/version.js CHANGED
@@ -1 +1 @@
1
- export default '1.11.0-beta.6';
1
+ export default '1.11.0-beta.8';
@@ -38,6 +38,7 @@ const DEFAULT_LAYOUTS = Object.freeze({
38
38
  const DEFAULT_TRACKERS = Object.freeze({
39
39
  [ROLE.SOURCES]: {},
40
40
  [ROLE.AFFILIATION]: {},
41
+ [ROLE.PRODUCTS]: {},
41
42
  });
42
43
 
43
44
  const DEFAULT_OPTIONS = Object.freeze({
@@ -2,7 +2,7 @@ import { defineValues, trimObj, API } from '@miso.ai/commons';
2
2
  import AnswerBasedWorkflow from './answer-based.js';
3
3
  import { fields } from '../actor/index.js';
4
4
  import { ROLE, ORGANIC_QUESTION_SOURCE } from '../constants.js';
5
- import { OptionListLayout, ListLayout } from '../layout/index.js';
5
+ import { OptionListLayout, ListLayout, SearchBoxLayout } from '../layout/index.js';
6
6
 
7
7
  const DEFAULT_API_OPTIONS = Object.freeze({
8
8
  ...AnswerBasedWorkflow.DEFAULT_API_OPTIONS,
@@ -15,6 +15,7 @@ const DEFAULT_API_OPTIONS = Object.freeze({
15
15
 
16
16
  const DEFAULT_LAYOUTS = Object.freeze({
17
17
  ...AnswerBasedWorkflow.DEFAULT_LAYOUTS,
18
+ [ROLE.QUERY]: [SearchBoxLayout.type, { templates: { buttonIcon: 'send' } }],
18
19
  [ROLE.RELATED_RESOURCES]: [ListLayout.type, { incremental: true, itemType: 'article' }],
19
20
  [ROLE.QUERY_SUGGESTIONS]: OptionListLayout.type,
20
21
  });
@@ -20,7 +20,6 @@ export default class HybridSearchAnswer extends AnswerBasedWorkflow {
20
20
  rolesConfig: ROLES_CONFIG,
21
21
  superworkflow,
22
22
  });
23
- this.useInteractions(false);
24
23
  }
25
24
 
26
25
  _initProperties(args) {
@@ -34,6 +33,7 @@ export default class HybridSearchAnswer extends AnswerBasedWorkflow {
34
33
  super.restart();
35
34
  // cascade restart to the sibling
36
35
  const results = this._superworkflow._results;
36
+ results._views.filters.reset();
37
37
  results.restart();
38
38
  this._resultsSession = results.session; // keep track of the session
39
39
  }
@@ -3,7 +3,7 @@ import Workflow from './base.js';
3
3
  import { fields } from '../actor/index.js';
4
4
  import { ROLE, STATUS } from '../constants.js';
5
5
  import { mergeApiOptions } from './options.js';
6
- import { writeKeywordsToData, composeFq } from './processors.js';
6
+ import { writeKeywordsToData, composeFiltersPayload } from './processors.js';
7
7
 
8
8
  const ROLES_CONFIG = Object.freeze({
9
9
  [ROLE.FACETS]: {
@@ -23,7 +23,6 @@ export default class HybridSearchResults extends Workflow {
23
23
  rolesConfig: ROLES_CONFIG,
24
24
  superworkflow,
25
25
  });
26
- this.useInteractions(false);
27
26
  }
28
27
 
29
28
  _initProperties(args) {
@@ -62,16 +61,15 @@ export default class HybridSearchResults extends Workflow {
62
61
  _buildPayload({ filters, ...payload } = {}) {
63
62
  // borrow the work from the sibling
64
63
  payload = this._superworkflow._answer._buildPayload(payload);
65
- payload = this._writeFqToPayload({ ...payload, filters });
64
+ payload = this._writeFiltersToPayload({ ...payload, filters });
66
65
  payload = this._writeQuestionIdToPayload(payload);
67
66
  return { ...payload, answer: false };
68
67
  }
69
68
 
70
- _writeFqToPayload({ filters, ...payload } = {}) {
71
- const fq = composeFq(filters); // TODO: combine with fq in payload?
69
+ _writeFiltersToPayload({ filters, ...payload } = {}) {
72
70
  return trimObj({
73
71
  ...payload,
74
- fq,
72
+ ...composeFiltersPayload(filters),
75
73
  });
76
74
  }
77
75
 
@@ -12,7 +12,8 @@ const DEFAULT_API_OPTIONS = Object.freeze({
12
12
  name: API.NAME.SEARCH,
13
13
  payload: {
14
14
  ...AnswerBasedWorkflow.DEFAULT_API_OPTIONS.payload,
15
- fl: ['cover_image', 'url', 'created_at', 'updated_at', 'published_at'],
15
+ source_fl: ['cover_image', 'url', 'created_at', 'updated_at', 'published_at', 'title'],
16
+ fl: ['cover_image', 'url', 'created_at', 'updated_at', 'published_at', 'title'],
16
17
  },
17
18
  });
18
19
 
@@ -74,19 +74,29 @@ export function writeAnswerStageToMeta(data) {
74
74
  };
75
75
  }
76
76
 
77
- export function composeFq(filters) {
77
+ export function composeFiltersPayload(filters) {
78
78
  if (!filters) {
79
79
  return undefined;
80
80
  }
81
- const clauses = [];
82
81
  const { facets } = filters;
82
+ return trimObj({
83
+ facet_filters: composeFacetFilters(facets),
84
+ });
85
+ }
86
+
87
+ function composeFacetFilters(facets) {
88
+ if (!facets) {
89
+ return undefined;
90
+ }
91
+ const filters = {};
83
92
  for (const field in facets) {
84
93
  const values = facets[field];
85
94
  if (!values || !values.length) {
86
95
  continue; // just in case
87
96
  }
88
- const vstr = values.length === 1 ? `"${values[0]}"` : `(${values.map(v => `"${v}"`).join(' OR ')})`;
89
- clauses.push(`${field}:${vstr}`);
97
+ filters[field] = {
98
+ terms: values,
99
+ };
90
100
  }
91
- return clauses.map(c => `(${c})`).join(' AND ') || undefined;
101
+ return Object.keys(filters).length ? filters : undefined;
92
102
  }