@miso.ai/client-sdk-ui 1.11.0-beta.1 → 1.11.0-beta.11
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.
- package/dist/cjs/actor/data.js +6 -7
- package/dist/cjs/actor/feedback.js +6 -39
- package/dist/cjs/actor/fields.js +14 -6
- package/dist/cjs/actor/hub.js +1 -2
- package/dist/cjs/actor/index.js +0 -7
- package/dist/cjs/actor/interactions.js +4 -15
- package/dist/cjs/actor/sessions.js +2 -33
- package/dist/cjs/actor/tracker.js +25 -26
- package/dist/cjs/actor/utils.js +0 -49
- package/dist/cjs/actor/view.js +40 -40
- package/dist/cjs/actor/views.js +32 -8
- package/dist/cjs/asset/svgs.js +18 -8
- package/dist/cjs/constants.js +18 -19
- package/dist/cjs/defaults/hybrid-search/answer-box.js +19 -3
- package/dist/cjs/defaults/hybrid-search/phrases.js +5 -5
- package/dist/cjs/defaults/hybrid-search/templates.js +37 -9
- package/dist/cjs/element/role/index.js +12 -5
- package/dist/cjs/element/role/{miso-hits.js → miso-more.js} +4 -4
- package/dist/cjs/element/role/miso-total.js +20 -0
- package/dist/cjs/layout/affiliation/affiliation.js +0 -3
- package/dist/cjs/layout/banner/banner.js +0 -4
- package/dist/cjs/layout/button/more-button.js +73 -0
- package/dist/cjs/layout/container/container.js +0 -3
- package/dist/cjs/layout/error/error.js +0 -4
- package/dist/cjs/layout/index.js +7 -0
- package/dist/cjs/layout/input/facets.js +5 -5
- package/dist/cjs/layout/input/option-list.js +0 -4
- package/dist/cjs/layout/input/search-box.js +35 -17
- package/dist/cjs/layout/list/cards.js +0 -3
- package/dist/cjs/layout/list/carousel.js +0 -3
- package/dist/cjs/layout/list/collection.js +22 -7
- package/dist/cjs/layout/list/list.js +0 -3
- package/dist/cjs/layout/radio/feedback.js +0 -3
- package/dist/cjs/layout/radio/radio.js +0 -4
- package/dist/cjs/layout/raf.js +4 -2
- package/dist/cjs/layout/text/text.js +0 -4
- package/dist/cjs/layout/text/typewriter/index.js +0 -3
- package/dist/cjs/layout/triggerable.js +79 -0
- package/dist/cjs/util/states.js +0 -15
- package/dist/cjs/version.js +1 -1
- package/dist/cjs/workflow/answer-based.js +109 -111
- package/dist/cjs/workflow/ask.js +35 -27
- package/dist/cjs/workflow/asks.js +1 -1
- package/dist/cjs/workflow/autocompletable.js +33 -0
- package/dist/cjs/workflow/autocomplete.js +122 -0
- package/dist/cjs/workflow/base.js +169 -102
- package/dist/cjs/workflow/explore.js +26 -19
- package/dist/cjs/workflow/hybrid-search-answer.js +99 -0
- package/dist/cjs/workflow/hybrid-search-results.js +66 -0
- package/dist/cjs/workflow/hybrid-search-views.js +122 -0
- package/dist/cjs/workflow/hybrid-search.js +98 -103
- package/dist/cjs/workflow/options.js +32 -17
- package/dist/cjs/workflow/processors.js +191 -17
- package/dist/cjs/workflow/recommendation.js +36 -49
- package/dist/cjs/workflow/search-based.js +228 -0
- package/dist/cjs/workflow/search.js +21 -68
- package/package.json +2 -2
- package/src/actor/data.js +2 -4
- package/src/actor/feedback.js +6 -23
- package/src/actor/fields.js +14 -6
- package/src/actor/hub.js +1 -2
- package/src/actor/index.js +0 -1
- package/src/actor/interactions.js +4 -10
- package/src/actor/sessions.js +3 -30
- package/src/actor/tracker.js +25 -19
- package/src/actor/utils.js +0 -47
- package/src/actor/view.js +27 -15
- package/src/actor/views.js +36 -8
- package/src/asset/svgs.js +19 -7
- package/src/constants.js +25 -16
- package/src/defaults/hybrid-search/answer-box.js +17 -5
- package/src/defaults/hybrid-search/phrases.js +4 -4
- package/src/defaults/hybrid-search/templates.js +25 -9
- package/src/element/role/index.js +2 -1
- package/src/element/role/{miso-hits.js → miso-more.js} +3 -3
- package/src/element/role/miso-total.js +16 -0
- package/src/layout/affiliation/affiliation.js +1 -5
- package/src/layout/banner/banner.js +0 -5
- package/src/layout/button/more-button.js +69 -0
- package/src/layout/container/container.js +1 -5
- package/src/layout/error/error.js +0 -5
- package/src/layout/index.js +2 -1
- package/src/layout/input/facets.js +5 -6
- package/src/layout/input/option-list.js +0 -5
- package/src/layout/input/search-box.js +29 -19
- package/src/layout/list/cards.js +0 -4
- package/src/layout/list/carousel.js +0 -4
- package/src/layout/list/collection.js +21 -9
- package/src/layout/list/list.js +0 -4
- package/src/layout/radio/feedback.js +0 -4
- package/src/layout/radio/radio.js +0 -5
- package/src/layout/raf.js +2 -2
- package/src/layout/text/text.js +0 -5
- package/src/layout/text/typewriter/index.js +1 -5
- package/src/layout/trackable.js +1 -1
- package/src/layout/triggerable.js +76 -0
- package/src/util/states.js +0 -14
- package/src/version.js +1 -1
- package/src/workflow/answer-based.js +101 -77
- package/src/workflow/ask.js +33 -25
- package/src/workflow/asks.js +1 -1
- package/src/workflow/autocompletable.js +29 -0
- package/src/workflow/autocomplete.js +113 -0
- package/src/workflow/base.js +147 -93
- package/src/workflow/explore.js +23 -8
- package/src/workflow/hybrid-search-answer.js +92 -0
- package/src/workflow/hybrid-search-results.js +62 -0
- package/src/workflow/hybrid-search-views.js +124 -0
- package/src/workflow/hybrid-search.js +102 -73
- package/src/workflow/options.js +26 -11
- package/src/workflow/processors.js +169 -14
- package/src/workflow/recommendation.js +32 -33
- package/src/workflow/search-based.js +222 -0
- package/src/workflow/search.js +21 -65
- package/dist/cjs/actor/autocomplete.js +0 -160
- package/dist/cjs/workflow/utils.js +0 -42
- package/src/actor/autocomplete.js +0 -133
- package/src/workflow/utils.js +0 -29
package/src/actor/utils.js
CHANGED
|
@@ -1,50 +1,3 @@
|
|
|
1
|
-
import { trimObj } from '@miso.ai/commons';
|
|
2
|
-
import { ROLE, EVENT_TYPE } from '../constants.js';
|
|
3
|
-
|
|
4
|
-
const PRODUCT_TYPED_PROPERTIES = new Set([
|
|
5
|
-
ROLE.PRODUCTS,
|
|
6
|
-
ROLE.SOURCES,
|
|
7
|
-
ROLE.RELATED_RESOURCES,
|
|
8
|
-
]);
|
|
9
|
-
|
|
10
|
-
// TODO: move away
|
|
11
|
-
export function isProduct(property) {
|
|
12
|
-
return PRODUCT_TYPED_PROPERTIES.has(property);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function toInteraction({ property, misoId, request }, { event, values, manual }) {
|
|
16
|
-
// TODO: ad-hoc
|
|
17
|
-
const question_source = request && request._meta && request._meta.question_source;
|
|
18
|
-
const isProductType = isProduct(property);
|
|
19
|
-
const isSubmit = event === EVENT_TYPE.SUBMIT;
|
|
20
|
-
const isAffiliation = property === ROLE.AFFILIATION;
|
|
21
|
-
|
|
22
|
-
const product_ids = isProductType && values ? values.map(v => typeof v === 'string' ? v : v.product_id).filter(v => v) : !isSubmit ? [] : undefined;
|
|
23
|
-
const items = !isProductType && !isSubmit && values ? values.map(v => v.text || v.id || v) : undefined;
|
|
24
|
-
const value = isSubmit ? values : undefined;
|
|
25
|
-
|
|
26
|
-
const channel = isAffiliation ? values[0] && values[0].channel : undefined;
|
|
27
|
-
const positions = isAffiliation ? values.map(v => v.position) : undefined;
|
|
28
|
-
|
|
29
|
-
const payload = trimObj({
|
|
30
|
-
type: event === 'viewable' ? 'viewable_impression' : event,
|
|
31
|
-
product_ids,
|
|
32
|
-
miso_id: misoId,
|
|
33
|
-
context: {
|
|
34
|
-
custom_context: trimObj({
|
|
35
|
-
property,
|
|
36
|
-
channel,
|
|
37
|
-
items,
|
|
38
|
-
value,
|
|
39
|
-
positions,
|
|
40
|
-
trigger: manual ? 'manual' : 'auto',
|
|
41
|
-
question_source,
|
|
42
|
-
}),
|
|
43
|
-
},
|
|
44
|
-
});
|
|
45
|
-
return payload;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
1
|
export function isCurrentSession(hub, session) {
|
|
49
2
|
if (!session) {
|
|
50
3
|
return false;
|
package/src/actor/view.js
CHANGED
|
@@ -22,7 +22,7 @@ export default class ViewActor {
|
|
|
22
22
|
constructor(views, role) {
|
|
23
23
|
this._events = new EventEmitter({ target: this });
|
|
24
24
|
this._views = views;
|
|
25
|
-
this.
|
|
25
|
+
this._mapping = asMappingFunction((views._roles.mappings || {})[role] || role);
|
|
26
26
|
|
|
27
27
|
defineValues(this, {
|
|
28
28
|
role,
|
|
@@ -101,6 +101,10 @@ export default class ViewActor {
|
|
|
101
101
|
}
|
|
102
102
|
return this._tracker;
|
|
103
103
|
}
|
|
104
|
+
|
|
105
|
+
get workflowOptions() {
|
|
106
|
+
return this._views._getWorkflowOptions();
|
|
107
|
+
}
|
|
104
108
|
|
|
105
109
|
get filters() {
|
|
106
110
|
return this._views.filters;
|
|
@@ -122,21 +126,17 @@ export default class ViewActor {
|
|
|
122
126
|
const previousData = this._data;
|
|
123
127
|
this._data = data = this._sliceData(data || this._views._getData());
|
|
124
128
|
|
|
125
|
-
// protocol: no reaction until session starts
|
|
126
|
-
const { session, status, ongoing, request, meta } = data;
|
|
127
|
-
const active = session && session.active;
|
|
128
|
-
if (!force && !active) {
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
129
|
// if data is unchanged, skip unless forced
|
|
133
130
|
if (!force && statesEqual(data, previousData)) {
|
|
134
131
|
return;
|
|
135
132
|
}
|
|
136
133
|
|
|
134
|
+
const { session, status, ongoing, request, meta, value } = data;
|
|
135
|
+
const baseState = { session, status, ongoing, request, meta, data: value, ...getDataEmptyness(data) };
|
|
136
|
+
|
|
137
137
|
// render
|
|
138
138
|
const notifyUpdate = (state = {}, options) => {
|
|
139
|
-
state !== false && this.updateState({
|
|
139
|
+
state !== false && this.updateState({ ...baseState, ...state }, options);
|
|
140
140
|
};
|
|
141
141
|
try {
|
|
142
142
|
await this._layout.render(element, data, { notifyUpdate });
|
|
@@ -146,11 +146,10 @@ export default class ViewActor {
|
|
|
146
146
|
}
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
-
updateState(
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
}
|
|
153
|
-
const state = this._state = Object.freeze(trimObj({ session, status, ongoing, request, meta, error }));
|
|
149
|
+
updateState(state, { silent = false } = {}) {
|
|
150
|
+
// there might be error from notifyUpdate() call
|
|
151
|
+
const status = state.error ? STATUS.ERRONEOUS : state.status;
|
|
152
|
+
state = this._state = Object.freeze(trimObj({ ...state, status }));
|
|
154
153
|
const { role } = this;
|
|
155
154
|
this.hub.update(fields.view(role), state, { silent });
|
|
156
155
|
}
|
|
@@ -163,7 +162,7 @@ export default class ViewActor {
|
|
|
163
162
|
_sliceData(data) {
|
|
164
163
|
const { value, error, status, meta, ...rest } = data;
|
|
165
164
|
const sliced = {
|
|
166
|
-
value:
|
|
165
|
+
value: this._mapping(data),
|
|
167
166
|
status,
|
|
168
167
|
data: value,
|
|
169
168
|
meta,
|
|
@@ -247,3 +246,16 @@ function asMappingFunction(fn) {
|
|
|
247
246
|
throw new Error(`Invalid mapping function: ${fn}`);
|
|
248
247
|
}
|
|
249
248
|
}
|
|
249
|
+
|
|
250
|
+
function getDataEmptyness(data) {
|
|
251
|
+
return isDataStatusRelevantToEmptyness(data) ? { empty: !!isDateValueEmpty(data) } : {};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function isDataStatusRelevantToEmptyness(data) {
|
|
255
|
+
return data.status === STATUS.READY && !data.ongoing;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function isDateValueEmpty(data) {
|
|
259
|
+
const { value } = data;
|
|
260
|
+
return value === undefined || ((Array.isArray(value) || typeof value === 'string') && !value.length);
|
|
261
|
+
}
|
package/src/actor/views.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defineValues, delegateGetters } from '@miso.ai/commons';
|
|
1
|
+
import { defineValues, delegateGetters, trimObj } from '@miso.ai/commons';
|
|
2
2
|
import { ROLE } from '../constants.js';
|
|
3
3
|
import * as fields from './fields.js';
|
|
4
4
|
import ViewActor from './view.js';
|
|
@@ -12,8 +12,7 @@ export default class ViewsActor {
|
|
|
12
12
|
workflow,
|
|
13
13
|
extensions,
|
|
14
14
|
layouts,
|
|
15
|
-
roles
|
|
16
|
-
rolesConfig = {},
|
|
15
|
+
roles,
|
|
17
16
|
options,
|
|
18
17
|
}) {
|
|
19
18
|
this._hub = hub;
|
|
@@ -21,14 +20,17 @@ export default class ViewsActor {
|
|
|
21
20
|
this._extensions = extensions;
|
|
22
21
|
this._layoutFactory = layouts;
|
|
23
22
|
this._options = options;
|
|
24
|
-
this.
|
|
23
|
+
this._roles = roles;
|
|
25
24
|
this._containers = new Map();
|
|
26
25
|
this._containerTracker = undefined;
|
|
27
26
|
this._views = {};
|
|
28
|
-
this._trackers = new Trackers(this, roles);
|
|
27
|
+
this._trackers = new Trackers(this, roles.members);
|
|
29
28
|
this._filters = new Filters(this);
|
|
30
29
|
|
|
31
|
-
for (const role of roles) {
|
|
30
|
+
for (const role of roles.members) {
|
|
31
|
+
if (role === ROLE.CONTAINER) {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
32
34
|
this._views[role] = new ViewActor(this, role);
|
|
33
35
|
}
|
|
34
36
|
|
|
@@ -42,10 +44,14 @@ export default class ViewsActor {
|
|
|
42
44
|
this._unsubscribes = [
|
|
43
45
|
() => window.removeEventListener('resize', syncSize),
|
|
44
46
|
hub.on(fields.data(), data => this.refresh({ data })),
|
|
47
|
+
options.on('autocomplete', () => this._syncAutocompleteOptions()),
|
|
48
|
+
options.on('pagination', () => this._syncPaginationOptions()),
|
|
45
49
|
options.on('trackers', () => this._syncTrackers()),
|
|
46
50
|
options.on('layouts', () => this._syncLayouts()),
|
|
47
51
|
];
|
|
48
52
|
|
|
53
|
+
this._syncAutocompleteOptions();
|
|
54
|
+
this._syncPaginationOptions();
|
|
49
55
|
this._syncTrackers();
|
|
50
56
|
this._syncLayouts();
|
|
51
57
|
}
|
|
@@ -99,6 +105,10 @@ export default class ViewsActor {
|
|
|
99
105
|
// TODO
|
|
100
106
|
}
|
|
101
107
|
|
|
108
|
+
containsElement(element) {
|
|
109
|
+
return this._containers.has(element) || this._views[element.role];
|
|
110
|
+
}
|
|
111
|
+
|
|
102
112
|
refreshElement(element) {
|
|
103
113
|
const view = element.isContainer ? this._containers.get(element) : this._getViewByElement(element);
|
|
104
114
|
view && view.refresh({ force: true });
|
|
@@ -140,7 +150,7 @@ export default class ViewsActor {
|
|
|
140
150
|
} else {
|
|
141
151
|
for (let [role, [name, options]] of Object.entries(layouts)) {
|
|
142
152
|
// TODO: try to omit if unchanged
|
|
143
|
-
const fn = () => this._layoutFactory.create(name, { ...options, role, workflow: this._workflowName });
|
|
153
|
+
const fn = () => this._layoutFactory.create(name, { ...options, role, workflow: this._workflowName, workflowOptions: this._getWorkflowOptions() });
|
|
144
154
|
if (role === ROLE.CONTAINER) {
|
|
145
155
|
for (const view of this._containers.values()) {
|
|
146
156
|
view.layout = fn();
|
|
@@ -158,7 +168,7 @@ export default class ViewsActor {
|
|
|
158
168
|
return undefined;
|
|
159
169
|
}
|
|
160
170
|
const [name, options] = args;
|
|
161
|
-
return this._layoutFactory.create(name, { ...options, role, workflow: this._workflowName });
|
|
171
|
+
return this._layoutFactory.create(name, { ...options, role, workflow: this._workflowName, workflowOptions: this._getWorkflowOptions() });
|
|
162
172
|
}
|
|
163
173
|
|
|
164
174
|
get(role) {
|
|
@@ -225,6 +235,21 @@ export default class ViewsActor {
|
|
|
225
235
|
return this._containerTracker;
|
|
226
236
|
}
|
|
227
237
|
|
|
238
|
+
_syncAutocompleteOptions() {
|
|
239
|
+
this._autocompleteOptions = this._options.resolved.autocomplete;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
_syncPaginationOptions() {
|
|
243
|
+
this._paginationOptions = this._options.resolved.pagination;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
_getWorkflowOptions() {
|
|
247
|
+
return trimObj({
|
|
248
|
+
autocomplete: this._autocompleteOptions,
|
|
249
|
+
pagination: this._paginationOptions,
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
|
|
228
253
|
_error(e) {
|
|
229
254
|
// TODO: hub trigger error event
|
|
230
255
|
console.error(e);
|
|
@@ -267,6 +292,9 @@ class Trackers {
|
|
|
267
292
|
constructor(views, roles) {
|
|
268
293
|
this._views = views;
|
|
269
294
|
for (const role of roles) {
|
|
295
|
+
if (role === ROLE.CONTAINER) {
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
270
298
|
Object.defineProperty(this, role, {
|
|
271
299
|
get: () => views.get(role).tracker,
|
|
272
300
|
});
|
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}
|
|
15
|
-
const ICON_SEARCH = `<symbol id="${PREFIX}
|
|
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}
|
|
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
|
|
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
|
|
41
|
-
|
|
42
|
-
export
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
const PRODUCT_ROLE_SET = new Set([
|
|
52
|
+
ROLE.PRODUCTS,
|
|
53
|
+
ROLE.SOURCES,
|
|
54
|
+
ROLE.RELATED_RESOURCES,
|
|
55
|
+
]);
|
|
53
56
|
|
|
54
|
-
export
|
|
55
|
-
|
|
56
|
-
|
|
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(() =>
|
|
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
|
-
|
|
39
|
-
workflow.on(
|
|
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
|
|
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-
|
|
17
|
+
* The phrase to display <miso-total>
|
|
18
18
|
*/
|
|
19
|
-
export const
|
|
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.
|
|
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
|
-
|
|
66
|
+
total(options) {
|
|
61
67
|
const { phrase } = this._helpers;
|
|
62
|
-
return phrase(options, { name: '
|
|
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
|
-
|
|
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
|
|
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
|
|
4
|
+
export default class MisoMoreElement extends MisoComponentElement {
|
|
5
5
|
|
|
6
6
|
constructor() {
|
|
7
7
|
super({
|
|
8
|
-
role: ROLE.
|
|
8
|
+
role: ROLE.MORE,
|
|
9
9
|
});
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
static get tagName() {
|
|
13
|
-
return 'miso-
|
|
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
|
|
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
|
+
}
|