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

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 (111) hide show
  1. package/dist/cjs/actor/feedback.js +6 -39
  2. package/dist/cjs/actor/fields.js +14 -6
  3. package/dist/cjs/actor/index.js +0 -7
  4. package/dist/cjs/actor/interactions.js +4 -15
  5. package/dist/cjs/actor/sessions.js +2 -32
  6. package/dist/cjs/actor/tracker.js +25 -26
  7. package/dist/cjs/actor/utils.js +0 -49
  8. package/dist/cjs/actor/view.js +40 -40
  9. package/dist/cjs/actor/views.js +29 -8
  10. package/dist/cjs/constants.js +18 -19
  11. package/dist/cjs/defaults/hybrid-search/phrases.js +3 -3
  12. package/dist/cjs/defaults/hybrid-search/templates.js +22 -7
  13. package/dist/cjs/element/role/index.js +12 -5
  14. package/dist/cjs/element/role/{miso-hits.js → miso-more.js} +4 -4
  15. package/dist/cjs/element/role/miso-total.js +20 -0
  16. package/dist/cjs/layout/affiliation/affiliation.js +0 -3
  17. package/dist/cjs/layout/banner/banner.js +0 -4
  18. package/dist/cjs/layout/button/more-button.js +73 -0
  19. package/dist/cjs/layout/container/container.js +0 -3
  20. package/dist/cjs/layout/error/error.js +0 -4
  21. package/dist/cjs/layout/index.js +7 -0
  22. package/dist/cjs/layout/input/facets.js +0 -4
  23. package/dist/cjs/layout/input/option-list.js +0 -4
  24. package/dist/cjs/layout/input/search-box.js +13 -12
  25. package/dist/cjs/layout/list/cards.js +0 -3
  26. package/dist/cjs/layout/list/carousel.js +0 -3
  27. package/dist/cjs/layout/list/collection.js +22 -7
  28. package/dist/cjs/layout/list/list.js +0 -3
  29. package/dist/cjs/layout/radio/feedback.js +0 -3
  30. package/dist/cjs/layout/radio/radio.js +0 -4
  31. package/dist/cjs/layout/raf.js +4 -2
  32. package/dist/cjs/layout/text/text.js +0 -4
  33. package/dist/cjs/layout/text/typewriter/index.js +0 -3
  34. package/dist/cjs/layout/triggerable.js +79 -0
  35. package/dist/cjs/util/states.js +0 -15
  36. package/dist/cjs/version.js +1 -1
  37. package/dist/cjs/workflow/answer-based.js +70 -105
  38. package/dist/cjs/workflow/ask.js +29 -27
  39. package/dist/cjs/workflow/asks.js +1 -1
  40. package/dist/cjs/workflow/autocompletable.js +33 -0
  41. package/dist/cjs/workflow/autocomplete.js +122 -0
  42. package/dist/cjs/workflow/base.js +156 -98
  43. package/dist/cjs/workflow/explore.js +48 -46
  44. package/dist/cjs/workflow/hybrid-search-answer.js +17 -7
  45. package/dist/cjs/workflow/hybrid-search-results.js +17 -87
  46. package/dist/cjs/workflow/hybrid-search-views.js +122 -0
  47. package/dist/cjs/workflow/hybrid-search.js +47 -168
  48. package/dist/cjs/workflow/options.js +32 -17
  49. package/dist/cjs/workflow/processors.js +166 -2
  50. package/dist/cjs/workflow/recommendation.js +36 -49
  51. package/dist/cjs/workflow/search-based.js +228 -0
  52. package/dist/cjs/workflow/search.js +21 -68
  53. package/dist/cjs/workflow/use-link.js +44 -0
  54. package/package.json +2 -2
  55. package/src/actor/feedback.js +6 -23
  56. package/src/actor/fields.js +14 -6
  57. package/src/actor/index.js +0 -1
  58. package/src/actor/interactions.js +4 -10
  59. package/src/actor/sessions.js +3 -31
  60. package/src/actor/tracker.js +25 -19
  61. package/src/actor/utils.js +0 -47
  62. package/src/actor/view.js +27 -15
  63. package/src/actor/views.js +32 -8
  64. package/src/constants.js +25 -16
  65. package/src/defaults/hybrid-search/phrases.js +2 -2
  66. package/src/defaults/hybrid-search/templates.js +17 -5
  67. package/src/element/role/index.js +2 -1
  68. package/src/element/role/{miso-hits.js → miso-more.js} +3 -3
  69. package/src/element/role/miso-total.js +16 -0
  70. package/src/layout/affiliation/affiliation.js +1 -5
  71. package/src/layout/banner/banner.js +0 -5
  72. package/src/layout/button/more-button.js +69 -0
  73. package/src/layout/container/container.js +1 -5
  74. package/src/layout/error/error.js +0 -5
  75. package/src/layout/index.js +2 -1
  76. package/src/layout/input/facets.js +0 -5
  77. package/src/layout/input/option-list.js +0 -5
  78. package/src/layout/input/search-box.js +11 -14
  79. package/src/layout/list/cards.js +0 -4
  80. package/src/layout/list/carousel.js +0 -4
  81. package/src/layout/list/collection.js +21 -9
  82. package/src/layout/list/list.js +0 -4
  83. package/src/layout/radio/feedback.js +0 -4
  84. package/src/layout/radio/radio.js +0 -5
  85. package/src/layout/raf.js +2 -2
  86. package/src/layout/text/text.js +0 -5
  87. package/src/layout/text/typewriter/index.js +1 -5
  88. package/src/layout/triggerable.js +76 -0
  89. package/src/util/states.js +0 -14
  90. package/src/version.js +1 -1
  91. package/src/workflow/answer-based.js +61 -72
  92. package/src/workflow/ask.js +30 -24
  93. package/src/workflow/asks.js +1 -1
  94. package/src/workflow/autocompletable.js +29 -0
  95. package/src/workflow/autocomplete.js +113 -0
  96. package/src/workflow/base.js +133 -87
  97. package/src/workflow/explore.js +38 -30
  98. package/src/workflow/hybrid-search-answer.js +17 -6
  99. package/src/workflow/hybrid-search-results.js +17 -77
  100. package/src/workflow/hybrid-search-views.js +124 -0
  101. package/src/workflow/hybrid-search.js +47 -157
  102. package/src/workflow/options.js +26 -11
  103. package/src/workflow/processors.js +151 -3
  104. package/src/workflow/recommendation.js +32 -33
  105. package/src/workflow/search-based.js +222 -0
  106. package/src/workflow/search.js +21 -65
  107. package/src/workflow/use-link.js +40 -0
  108. package/dist/cjs/actor/autocomplete.js +0 -160
  109. package/dist/cjs/workflow/utils.js +0 -42
  110. package/src/actor/autocomplete.js +0 -133
  111. package/src/workflow/utils.js +0 -29
@@ -3,35 +3,45 @@ import { Hub, SessionMaker, DataActor, ViewsActor, InteractionsActor, fields } f
3
3
  import * as sources from '../source.js';
4
4
  import { STATUS, ROLE } from '../constants.js';
5
5
  import { ContainerLayout, ErrorLayout } from '../layout/index.js';
6
- import { injectLogger } from './utils.js';
7
- import { WorkflowOptions, mergeLayoutsOptions, mergeInteractionsOptions, makeConfigurable } from './options.js';
8
- import { writeDataStatus, writeMisoIdToMeta } from './processors.js';
6
+ import { WorkflowOptions, mergeApiOptions, makeConfigurable } from './options.js';
7
+ import { getRevision, writeDataStatus, writeMisoIdToMeta, buildBaseInteraction, writeAffiliationInfoToInteraction, mergeInteraction } from './processors.js';
8
+
9
+ // TODO: just bury this into hub
10
+ function injectLogger(hub, callback) {
11
+ const { update, trigger } = hub;
12
+ hub.update = (name, state, options) => {
13
+ if (!options || !options.silent) {
14
+ callback('update', name, state, options);
15
+ }
16
+ return update.call(hub, name, state, options);
17
+ }
18
+ hub.trigger = (...args) => {
19
+ callback('trigger', ...args);
20
+ return trigger.apply(hub, args);
21
+ }
22
+ return hub;
23
+ }
24
+
25
+ const DEFAULT_API_OPTIONS = Object.freeze({});
9
26
 
10
27
  const DEFAULT_LAYOUTS = Object.freeze({
11
28
  [ROLE.CONTAINER]: ContainerLayout.type,
12
29
  [ROLE.ERROR]: ErrorLayout.type,
13
30
  });
14
31
 
15
- const ROLES_CONFIG = Object.freeze({
16
- [ROLE.ERROR]: {
17
- mapping: data => data.error,
18
- },
19
- });
32
+ const DEFAULT_TRACKERS = Object.freeze({});
20
33
 
21
- function mergeDefaults(workflow, { layouts, interactions, ...defaults } = {}) {
22
- const DEFAULT_INTERACTIONS = {
23
- preprocess: [payload => workflow._preprocessInteraction(payload)],
24
- };
25
- return {
26
- ...defaults,
27
- layouts: mergeLayoutsOptions(DEFAULT_LAYOUTS, layouts),
28
- interactions: mergeInteractionsOptions(DEFAULT_INTERACTIONS, interactions),
29
- };
30
- }
34
+ const DEFAULT_OPTIONS = Object.freeze({
35
+ api: DEFAULT_API_OPTIONS,
36
+ layouts: DEFAULT_LAYOUTS,
37
+ trackers: DEFAULT_TRACKERS,
38
+ });
31
39
 
32
- function getRevision(data) {
33
- return data && data.value && data.value.revision;
34
- }
40
+ const ROLES_OPTIONS = Object.freeze({
41
+ mappings: {
42
+ [ROLE.ERROR]: data => data.error,
43
+ },
44
+ });
35
45
 
36
46
  export default class Workflow extends Component {
37
47
 
@@ -46,43 +56,45 @@ export default class Workflow extends Component {
46
56
 
47
57
  this._name = args.name;
48
58
  this._roles = args.roles;
49
- this._rolesConfig = { ...ROLES_CONFIG, ...args.rolesConfig };
50
59
 
51
60
  this._extensions = plugin._getExtensions(client);
52
- this._options = options || new WorkflowOptions(context && context._options, mergeDefaults(this, args.defaults));
61
+ this._defaults = args.defaults;
62
+ this._options = options || new WorkflowOptions(context && context._options, args.defaults);
53
63
  this._hub = injectLogger(new Hub(), (...args) => this._log(...args));
54
64
 
55
65
  this._initProperties(args);
56
66
  this._initActors(args);
57
67
  this._initSubscriptions(args);
58
- this._initReset(args);
68
+ this._initSession(args);
59
69
  }
60
70
 
61
71
  _initProperties() {}
62
72
 
63
- _initActors() {
73
+ _initActors({ name, roles }) {
64
74
  const hub = this._hub;
65
75
  const client = this._client;
66
- const roles = this._roles;
67
- const rolesConfig = this._rolesConfig;
68
76
  const options = this._options;
69
77
  const extensions = this._extensions;
70
78
  const layouts = this._plugin.layouts;
71
79
 
72
80
  this._sessions = new SessionMaker(hub);
73
81
  this._data = new DataActor(hub, { source: sources.api(client), options });
74
- this._views = new ViewsActor(hub, { extensions, layouts, roles, rolesConfig, options, workflow: this._name });
82
+ this._views = new ViewsActor(hub, { extensions, layouts, roles, options, workflow: name });
75
83
  this._interactions = new InteractionsActor(hub, { client, options });
76
84
  }
77
85
 
78
- _initSubscriptions() {
86
+ _initSubscriptions({ roles }) {
79
87
  this._unsubscribes = [
80
88
  this._hub.on(fields.response(), data => this.updateData(data)),
89
+ this._hub.on(fields.tracker(), args => this._onTracker(args)),
81
90
  ];
91
+ if (roles.main) {
92
+ this._unsubscribes.push(this._hub.on(fields.view(roles.main), state => this._onMainViewUpdate(state)));
93
+ }
82
94
  }
83
95
 
84
- _initReset() {
85
- this.reset();
96
+ _initSession() {
97
+ this.restart();
86
98
  }
87
99
 
88
100
  get uuid() {
@@ -93,11 +105,6 @@ export default class Workflow extends Component {
93
105
  return this._hub.states[fields.session()];
94
106
  }
95
107
 
96
- get active() {
97
- const { session } = this;
98
- return !!session && session.active;
99
- }
100
-
101
108
  get states() {
102
109
  return this._hub.states;
103
110
  }
@@ -112,22 +119,51 @@ export default class Workflow extends Component {
112
119
  }
113
120
 
114
121
  // lifecycle //
115
- reset() {
116
- this._sessions.new();
122
+ restart() {
123
+ this._emitInterruptEventIfNecessary();
124
+ this._sessions.restart();
117
125
  return this;
118
126
  }
119
127
 
120
- start() {
121
- this._sessions.start();
122
- return this;
128
+ _emitInterruptEventIfNecessary() {
129
+ const { main } = this._roles;
130
+ if (!main) {
131
+ return;
132
+ }
133
+ const state = this._hub.states[fields.view(main)] || {};
134
+ if (state.session && state.status === STATUS.LOADING) {
135
+ // it's interrupted by a new question
136
+ this._emitLifecycleEvent('interrupt', state);
137
+ this._emitLifecycleEvent('finally', state);
138
+ }
123
139
  }
124
140
 
125
- restart() {
126
- this._sessions.restart();
127
- return this;
141
+ _onMainViewUpdate(state) {
142
+ const { status } = state;
143
+ const done = status === STATUS.READY;
144
+ const erroneous = status === STATUS.ERRONEOUS;
145
+ const eventName = erroneous ? 'error' : status;
146
+ this._emitLifecycleEvent(eventName, state);
147
+ if (done) {
148
+ this._emitLifecycleEvent('done', state);
149
+ }
150
+ if (done || erroneous) {
151
+ this._emitLifecycleEvent('finally', state);
152
+ }
153
+ }
154
+
155
+ _emitLifecycleEvent(name, event) {
156
+ this._emit(name, event);
128
157
  }
129
158
 
130
- // states //
159
+ // request //
160
+ _request(options = {}) {
161
+ const { session } = this;
162
+ const event = mergeApiOptions(this._options.resolved.api, { ...options, session });
163
+ this._hub.update(fields.request(), event);
164
+ }
165
+
166
+ // data //
131
167
  updateData(data) {
132
168
  if (!data) {
133
169
  throw new Error(`Data is required.`);
@@ -158,8 +194,6 @@ export default class Workflow extends Component {
158
194
  }
159
195
 
160
196
  _updateData(data) {
161
- this._sessions.start(); // in case session not started yet
162
-
163
197
  data = this._defaultProcessData(data);
164
198
  for (const process of this._options.resolved.dataProcessor) {
165
199
  data = process(data);
@@ -178,24 +212,9 @@ export default class Workflow extends Component {
178
212
  this._hub.update(fields.data(), data);
179
213
  }
180
214
 
181
- /*
182
- _mergeDataIfNecessary(data) {
183
- if (!data._inst || !data._inst.merge) {
184
- return data;
185
- }
186
- return {
187
- ...this._hub.states[fields.data()],
188
- ...data,
189
- };
190
- }
191
-
192
- _handleResponseObject() {}
193
- */
194
-
195
215
  // TODO: notifyViewUpdateAll()
196
216
 
197
217
  notifyViewUpdate(role, state) {
198
- this._assertActive();
199
218
  state = {
200
219
  status: STATUS.READY,
201
220
  session: this.session,
@@ -210,29 +229,54 @@ export default class Workflow extends Component {
210
229
  return this._views.trackers;
211
230
  }
212
231
 
213
- // TODO: extract to a separate function
214
- _preprocessInteraction({
215
- context: {
216
- custom_context,
217
- ...context
218
- } = {},
219
- ...payload
220
- } = {}) {
221
- const {
222
- group: api_group,
223
- name: api_name,
224
- } = this._options.resolved.api;
225
- return {
226
- ...payload,
232
+ _onTracker(args) {
233
+ const payload = this._buildInteraction(args);
234
+ this._sendInteraction(payload);
235
+ }
236
+
237
+ _buildInteraction(args) {
238
+ let payload = buildBaseInteraction(args);
239
+ payload = this._defaultProcessInteraction(payload, args);
240
+ const { preprocess = [] } = this._options.resolved.interactions;
241
+ for (const p of preprocess) {
242
+ payload = p(payload, args);
243
+ }
244
+ return payload;
245
+ }
246
+
247
+ _defaultProcessInteraction(payload, args) {
248
+ payload = this._writeApiInfoToInteraction(payload, args);
249
+ payload = this._writeMisoIdToInteraction(payload, args);
250
+ payload = writeAffiliationInfoToInteraction(payload, args);
251
+ return payload;
252
+ }
253
+
254
+ _writeApiInfoToInteraction(payload) {
255
+ const { group, name } = this._options.resolved.api;
256
+ return mergeInteraction(payload, {
227
257
  context: {
228
- ...context,
229
258
  custom_context: {
230
- api_group,
231
- api_name,
232
- ...custom_context,
259
+ api_group: group,
260
+ api_name: name,
233
261
  },
234
262
  },
235
- };
263
+ });
264
+ }
265
+
266
+ _writeMisoIdToInteraction(payload, args) {
267
+ // TODO: ad-hoc, try to pass info from tracker
268
+ const state = this._hub.states[fields.view(args.role)] || {};
269
+ const miso_id = (state.meta && state.meta.miso_id) || undefined;
270
+ return miso_id ? mergeInteraction(payload, { miso_id }) : payload;
271
+ }
272
+
273
+ _sendInteraction(payload) {
274
+ const { handle } = this._options.resolved.interactions;
275
+ if (typeof handle === 'function') {
276
+ handle(payload);
277
+ } else {
278
+ this._hub.trigger(fields.interaction(), payload);
279
+ }
236
280
  }
237
281
 
238
282
  // destroy //
@@ -263,12 +307,14 @@ export default class Workflow extends Component {
263
307
  }
264
308
  }
265
309
 
266
- _assertActive() {
267
- if (!this.active) {
268
- throw new Error(`Unit is not active yet. Call unit.start() to activate it.`)
269
- }
270
- }
271
-
272
310
  }
273
311
 
274
312
  makeConfigurable(Workflow.prototype);
313
+
314
+ Object.assign(Workflow, {
315
+ DEFAULT_API_OPTIONS,
316
+ DEFAULT_LAYOUTS,
317
+ DEFAULT_TRACKERS,
318
+ DEFAULT_OPTIONS,
319
+ ROLES_OPTIONS,
320
+ });
@@ -1,21 +1,25 @@
1
1
  import { API } from '@miso.ai/commons';
2
2
  import Workflow from './base.js';
3
- import { mergeApiOptions } from './options.js';
4
3
  import { fields } from '../actor/index.js';
5
4
  import { ROLE, EVENT_TYPE } from '../constants.js';
6
5
  import { ListLayout, SearchBoxLayout } from '../layout/index.js';
6
+ import { mergeRolesOptions } from './options.js';
7
+ import { enableUseLink, UseLinkMixin } from './use-link.js';
7
8
 
8
9
  const DEFAULT_API_OPTIONS = Object.freeze({
10
+ ...Workflow.DEFAULT_API_OPTIONS,
9
11
  group: API.GROUP.ASK,
10
12
  name: API.NAME.RELATED_QUESTIONS,
11
13
  });
12
14
 
13
15
  const DEFAULT_LAYOUTS = Object.freeze({
16
+ ...Workflow.DEFAULT_LAYOUTS,
14
17
  [ROLE.RELATED_QUESTIONS]: [ListLayout.type, { itemType: 'question', link: { rel: 'noopener nofollow' } }],
15
18
  [ROLE.QUERY]: [SearchBoxLayout.type, { placeholder: 'Ask a question' }],
16
19
  });
17
20
 
18
21
  const DEFAULT_TRACKERS = Object.freeze({
22
+ ...Workflow.DEFAULT_TRACKERS,
19
23
  [ROLE.RELATED_QUESTIONS]: {},
20
24
  [ROLE.CONTAINER]: {},
21
25
  [ROLE.QUERY]: {
@@ -24,11 +28,17 @@ const DEFAULT_TRACKERS = Object.freeze({
24
28
  });
25
29
 
26
30
  const DEFAULT_OPTIONS = Object.freeze({
31
+ ...Workflow.DEFAULT_OPTIONS,
27
32
  api: DEFAULT_API_OPTIONS,
28
33
  layouts: DEFAULT_LAYOUTS,
29
34
  trackers: DEFAULT_TRACKERS,
30
35
  });
31
36
 
37
+ const ROLES_OPTIONS = mergeRolesOptions(Workflow.ROLES_OPTIONS, {
38
+ main: ROLE.RELATED_QUESTIONS,
39
+ members: Object.keys(DEFAULT_LAYOUTS),
40
+ });
41
+
32
42
  export default class Explore extends Workflow {
33
43
 
34
44
  constructor(plugin, client) {
@@ -36,7 +46,7 @@ export default class Explore extends Workflow {
36
46
  name: 'explore',
37
47
  plugin,
38
48
  client,
39
- roles: Object.keys(DEFAULT_LAYOUTS),
49
+ roles: ROLES_OPTIONS,
40
50
  defaults: DEFAULT_OPTIONS,
41
51
  });
42
52
  }
@@ -52,7 +62,7 @@ export default class Explore extends Workflow {
52
62
  this._unsubscribes = [
53
63
  ...this._unsubscribes,
54
64
  this._views.get(ROLE.RELATED_QUESTIONS).on('click', event => this._handleRelatedQuestionClick(event)),
55
- this._hub.on(fields.query(), payload => this.query(payload)),
65
+ this._hub.on(fields.query(), args => this._query(args)),
56
66
  ];
57
67
  }
58
68
 
@@ -72,27 +82,18 @@ export default class Explore extends Workflow {
72
82
  return super.useApi(options);
73
83
  }
74
84
 
75
- useLink(fn) {
76
- if (typeof fn !== 'function' && fn !== false) {
77
- throw new Error('useLink(fn) expects fn to be a function or false');
78
- }
79
- this._linkFn = fn;
80
- return this;
81
- }
82
-
83
85
  // lifecycle //
84
86
  start({ relatedQuestions = true } = {}) {
85
- if (relatedQuestions && this._linkFn === undefined) {
86
- throw new Error('Define link mapping function before calling start()');
87
+ if (this._linkFn === undefined) {
88
+ throw new Error('Must define link mapping function with useLink(fn) before calling start()');
87
89
  }
88
- this._sessions.start();
89
90
  // in explore workflow, start() triggers query
90
91
  // TODO: we should still make the query lifecycle
91
- const { session } = this;
92
92
  if (relatedQuestions) {
93
- this._hub.update(fields.request(), mergeApiOptions(this._options.resolved.api, { session }));
93
+ this._request();
94
94
  } else {
95
95
  // TODO: ad-hoc
96
+ const { session } = this;
96
97
  this.updateData({ session, request: {}, value: { related_questions: [] } });
97
98
  }
98
99
  return this;
@@ -105,12 +106,17 @@ export default class Explore extends Workflow {
105
106
 
106
107
  _defaultProcessData(data) {
107
108
  data = super._defaultProcessData(data);
108
- let { value } = data;
109
- if (!value) {
109
+ data = this._addUrlToRelatedQuestions(data);
110
+ return data;
111
+ }
112
+
113
+ _addUrlToRelatedQuestions(data) {
114
+ const { value } = data;
115
+ if (!value || !value.related_questions) {
110
116
  return data;
111
117
  }
112
- // patch value with links
113
- const related_questions = value.related_questions.map(this._linkFn ? (text => ({ text, url: this._getAnswersUrl(text, true) })) : (text => ({ text })));
118
+ const linkFn = this._linkFn && this._linkFn[0];
119
+ const related_questions = value.related_questions.map(linkFn ? (text => ({ text, url: this._getSubmitUrl({ q: text }, { generated: true }) })) : (text => ({ text })));
114
120
  return {
115
121
  ...data,
116
122
  value: {
@@ -124,19 +130,19 @@ export default class Explore extends Workflow {
124
130
  this._events.emit('select', Object.freeze({ ...event, question }));
125
131
  }
126
132
 
127
- query({ q } = {}) {
128
- if (!this._linkFn) {
129
- return;
133
+ query(args) {
134
+ if (!args.q) {
135
+ throw new Error(`q is required in query() call`);
130
136
  }
131
- const url = this._getAnswersUrl(q);
132
- window.open(url, '_blank');
137
+ this._hub.update(fields.query(), args);
133
138
  }
134
139
 
135
- _getAnswersUrl(text, generated = false) {
136
- if (!this._linkFn) {
137
- return;
138
- }
139
- const url = this._linkFn(text);
140
+ _query(args) {
141
+ this._submitToPage(args);
142
+ }
143
+
144
+ _getSubmitUrl(args, { generated = false } = {}) {
145
+ let url = UseLinkMixin.prototype._getSubmitUrl.call(this, args);
140
146
  if (!generated || !this._productId) {
141
147
  return url;
142
148
  }
@@ -144,3 +150,5 @@ export default class Explore extends Workflow {
144
150
  }
145
151
 
146
152
  }
153
+
154
+ enableUseLink(Explore.prototype);
@@ -1,10 +1,12 @@
1
1
  import AnswerBasedWorkflow from './answer-based.js';
2
2
  import { ROLE, STATUS, ORGANIC_QUESTION_SOURCE } from '../constants.js';
3
+ import { mergeRolesOptions } from './options.js';
3
4
  import { writeKeywordsToData } from './processors.js';
5
+ import { makeAutocompletable } from './autocompletable.js';
4
6
 
5
- const ROLES_CONFIG = Object.freeze({
6
- [ROLE.QUESTION]: {
7
- mapping: ROLE.KEYWORDS,
7
+ const ROLES_OPTIONS = mergeRolesOptions(AnswerBasedWorkflow.ROLES_OPTIONS, {
8
+ mappings: {
9
+ [ROLE.QUESTION]: ROLE.KEYWORDS,
8
10
  },
9
11
  });
10
12
 
@@ -16,18 +18,19 @@ export default class HybridSearchAnswer extends AnswerBasedWorkflow {
16
18
  plugin: superworkflow._plugin,
17
19
  client: superworkflow._client,
18
20
  options: superworkflow._options,
19
- roles: Object.keys(AnswerBasedWorkflow.DEFAULT_LAYOUTS),
20
- rolesConfig: ROLES_CONFIG,
21
+ defaults: superworkflow._defaults,
22
+ roles: ROLES_OPTIONS,
21
23
  superworkflow,
22
24
  });
23
25
  }
24
26
 
25
27
  _initProperties(args) {
26
28
  super._initProperties(args);
29
+ this._initAutocomplete(args);
27
30
  this._superworkflow = args.superworkflow;
28
31
  }
29
32
 
30
- _initReset() {} // no reset here, will manually reset later
33
+ _initSession() {} // no reset here, will manually reset later
31
34
 
32
35
  restart() {
33
36
  super.restart();
@@ -78,4 +81,12 @@ export default class HybridSearchAnswer extends AnswerBasedWorkflow {
78
81
  this._resultsSession && this._superworkflow._results.updateData({ ...data, session: this._resultsSession });
79
82
  }
80
83
 
84
+ // destroy //
85
+ _destroy(options) {
86
+ this._destroyAutocomplete();
87
+ super._destroy(options);
88
+ }
89
+
81
90
  }
91
+
92
+ makeAutocompletable(HybridSearchAnswer.prototype);
@@ -1,17 +1,14 @@
1
1
  import { trimObj } from '@miso.ai/commons';
2
- import Workflow from './base.js';
2
+ import SearchBasedWorkflow from './search-based.js';
3
3
  import { fields } from '../actor/index.js';
4
- import { ROLE, STATUS } from '../constants.js';
5
- import { mergeApiOptions } from './options.js';
6
- import { writeKeywordsToData, composeFiltersPayload } from './processors.js';
4
+ import { ROLE } from '../constants.js';
5
+ import { mergeRolesOptions } from './options.js';
7
6
 
8
- const ROLES_CONFIG = Object.freeze({
9
- [ROLE.FACETS]: {
10
- mapping: 'facet_counts',
11
- },
7
+ const ROLES_OPTIONS = mergeRolesOptions(SearchBasedWorkflow.ROLES_OPTIONS, {
8
+ members: [ROLE.PRODUCTS, ROLE.KEYWORDS, ROLE.TOTAL, ROLE.FACETS, ROLE.MORE],
12
9
  });
13
10
 
14
- export default class HybridSearchResults extends Workflow {
11
+ export default class HybridSearchResults extends SearchBasedWorkflow {
15
12
 
16
13
  constructor(superworkflow) {
17
14
  super({
@@ -19,8 +16,8 @@ export default class HybridSearchResults extends Workflow {
19
16
  plugin: superworkflow._plugin,
20
17
  client: superworkflow._client,
21
18
  options: superworkflow._options,
22
- roles: [ROLE.PRODUCTS, ROLE.KEYWORDS, ROLE.HITS, ROLE.FACETS],
23
- rolesConfig: ROLES_CONFIG,
19
+ defaults: superworkflow._defaults,
20
+ roles: ROLES_OPTIONS,
24
21
  superworkflow,
25
22
  });
26
23
  }
@@ -30,49 +27,28 @@ export default class HybridSearchResults extends Workflow {
30
27
  this._superworkflow = args.superworkflow;
31
28
  }
32
29
 
33
- _initSubscriptions(args) {
34
- super._initSubscriptions(args);
35
- this._unsubscribes = [
36
- ...this._unsubscribes,
37
- this._hub.on(fields.filters(), filters => this._refine(filters)),
38
- ];
39
- }
30
+ _initSession() {} // no reset here, will manually reset later
40
31
 
41
- _initReset() {} // no reset here, will manually reset later
32
+ // lifecycle //
33
+ _emitLifecycleEvent(name, event) {
34
+ super._emitLifecycleEvent(name, event);
35
+ this._superworkflow._emitLifecycleEvent(name, event);
36
+ }
42
37
 
43
38
  // query //
44
- _refine(filters) {
45
- // remember current facet_counts
46
- const { facet_counts } = this._hub.states[fields.data()].value || {};
47
- this._previousFacetCounts = facet_counts;
48
-
49
- // start a new session
50
- this.restart();
51
-
39
+ _getQuery() {
52
40
  // get stored query from sibling
53
- const query = this._superworkflow._answer._hub.states[fields.query()];
54
- const payload = this._buildPayload({ ...query, filters });
55
- const { session } = this;
56
- const event = mergeApiOptions(this._options.resolved.api, { payload, session });
57
-
58
- this._request(event);
41
+ return this._superworkflow._answer._hub.states[fields.query()];
59
42
  }
60
43
 
61
44
  _buildPayload({ filters, ...payload } = {}) {
62
45
  // borrow the work from the sibling
63
46
  payload = this._superworkflow._answer._buildPayload(payload);
64
- payload = this._writeFiltersToPayload({ ...payload, filters });
47
+ payload = SearchBasedWorkflow.prototype._buildPayload.call(this, payload);
65
48
  payload = this._writeQuestionIdToPayload(payload);
66
49
  return { ...payload, answer: false };
67
50
  }
68
51
 
69
- _writeFiltersToPayload({ filters, ...payload } = {}) {
70
- return trimObj({
71
- ...payload,
72
- ...composeFiltersPayload(filters),
73
- });
74
- }
75
-
76
52
  _writeQuestionIdToPayload(payload) {
77
53
  return {
78
54
  ...payload,
@@ -83,40 +59,4 @@ export default class HybridSearchResults extends Workflow {
83
59
  };
84
60
  }
85
61
 
86
- _request(event) {
87
- this._hub.update(fields.request(), event);
88
- }
89
-
90
- // data //
91
- _defaultProcessData(data) {
92
- data = super._defaultProcessData(data);
93
- data = writeKeywordsToData(data);
94
- data = this._retainFacetCounts(data);
95
- return data;
96
- }
97
-
98
- _retainFacetCounts(data) {
99
- if (!this._previousFacetCounts) {
100
- return data;
101
- }
102
- const { status, value } = data;
103
- switch (status) {
104
- case STATUS.INITIAL:
105
- case STATUS.LOADING:
106
- break;
107
- default:
108
- return data;
109
- }
110
- if (value && value.facet_counts) {
111
- return data;
112
- }
113
- return {
114
- ...data,
115
- value: {
116
- ...value,
117
- facet_counts: this._previousFacetCounts,
118
- },
119
- };
120
- }
121
-
122
62
  }