@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.
- package/dist/cjs/actor/feedback.js +6 -39
- package/dist/cjs/actor/fields.js +14 -6
- package/dist/cjs/actor/index.js +0 -7
- package/dist/cjs/actor/interactions.js +4 -15
- package/dist/cjs/actor/sessions.js +2 -32
- 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 +29 -8
- package/dist/cjs/constants.js +18 -19
- package/dist/cjs/defaults/hybrid-search/phrases.js +3 -3
- package/dist/cjs/defaults/hybrid-search/templates.js +22 -7
- 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 +0 -4
- package/dist/cjs/layout/input/option-list.js +0 -4
- package/dist/cjs/layout/input/search-box.js +13 -12
- 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 +70 -105
- package/dist/cjs/workflow/ask.js +29 -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 +156 -98
- package/dist/cjs/workflow/explore.js +48 -46
- package/dist/cjs/workflow/hybrid-search-answer.js +17 -7
- package/dist/cjs/workflow/hybrid-search-results.js +17 -87
- package/dist/cjs/workflow/hybrid-search-views.js +122 -0
- package/dist/cjs/workflow/hybrid-search.js +47 -168
- package/dist/cjs/workflow/options.js +32 -17
- package/dist/cjs/workflow/processors.js +166 -2
- 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/dist/cjs/workflow/use-link.js +44 -0
- package/package.json +2 -2
- package/src/actor/feedback.js +6 -23
- package/src/actor/fields.js +14 -6
- package/src/actor/index.js +0 -1
- package/src/actor/interactions.js +4 -10
- package/src/actor/sessions.js +3 -31
- 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 +32 -8
- package/src/constants.js +25 -16
- package/src/defaults/hybrid-search/phrases.js +2 -2
- package/src/defaults/hybrid-search/templates.js +17 -5
- 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 +0 -5
- package/src/layout/input/option-list.js +0 -5
- package/src/layout/input/search-box.js +11 -14
- 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/triggerable.js +76 -0
- package/src/util/states.js +0 -14
- package/src/version.js +1 -1
- package/src/workflow/answer-based.js +61 -72
- package/src/workflow/ask.js +30 -24
- 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 +133 -87
- package/src/workflow/explore.js +38 -30
- package/src/workflow/hybrid-search-answer.js +17 -6
- package/src/workflow/hybrid-search-results.js +17 -77
- package/src/workflow/hybrid-search-views.js +124 -0
- package/src/workflow/hybrid-search.js +47 -157
- package/src/workflow/options.js +26 -11
- package/src/workflow/processors.js +151 -3
- package/src/workflow/recommendation.js +32 -33
- package/src/workflow/search-based.js +222 -0
- package/src/workflow/search.js +21 -65
- package/src/workflow/use-link.js +40 -0
- 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
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.UseLinkMixin = void 0;
|
|
7
|
+
exports.enableUseLink = enableUseLink;
|
|
8
|
+
var _commons = require("@miso.ai/commons");
|
|
9
|
+
function enableUseLink(prototype) {
|
|
10
|
+
(0, _commons.mixin)(prototype, UseLinkMixin.prototype);
|
|
11
|
+
}
|
|
12
|
+
class UseLinkMixin {
|
|
13
|
+
useLink(fn) {
|
|
14
|
+
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
15
|
+
if (typeof fn !== 'function' && fn !== false) {
|
|
16
|
+
throw new Error('useLink(fn) expects fn to be a function or false');
|
|
17
|
+
}
|
|
18
|
+
this._linkFn = fn ? [fn, options] : false;
|
|
19
|
+
return this;
|
|
20
|
+
}
|
|
21
|
+
_submitToPage() {
|
|
22
|
+
let args = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
23
|
+
if (!this._linkFn) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
// TODO: we want a submit event
|
|
27
|
+
const url = this._getSubmitUrl(args);
|
|
28
|
+
const [_, options = {}] = this._linkFn;
|
|
29
|
+
if (options.open === false) {
|
|
30
|
+
window.location.href = url;
|
|
31
|
+
} else {
|
|
32
|
+
const target = options.target || '_blank';
|
|
33
|
+
const windowFeatures = options.windowFeatures || '';
|
|
34
|
+
window.open(url, target, windowFeatures);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
_getSubmitUrl(args) {
|
|
38
|
+
if (!this._linkFn) {
|
|
39
|
+
throw new Error('useLink(fn) must be called before _getSubmitUrl()');
|
|
40
|
+
}
|
|
41
|
+
return this._linkFn[0](args.q);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
exports.UseLinkMixin = UseLinkMixin;
|
package/package.json
CHANGED
package/src/actor/feedback.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as fields from './fields.js';
|
|
2
|
+
import { EVENT_TYPE } from '../constants.js';
|
|
2
3
|
|
|
3
4
|
export default class FeedbackActor {
|
|
4
5
|
|
|
@@ -10,29 +11,11 @@ export default class FeedbackActor {
|
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
_trigger(state) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
// TODO: ad-hoc
|
|
19
|
-
const { request } = data || {};
|
|
20
|
-
const { payload } = request || {};
|
|
21
|
-
const question_source = payload && payload._meta && payload._meta.question_source;
|
|
22
|
-
this._hub.trigger(fields.interaction(), this._buildInteraction(state, { question_source }));
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
_buildInteraction({ value, unselected }, { question_source }) {
|
|
26
|
-
return {
|
|
27
|
-
type: 'feedback',
|
|
28
|
-
context: {
|
|
29
|
-
custom_context: {
|
|
30
|
-
result_type: 'answer',
|
|
31
|
-
question_source,
|
|
32
|
-
value: unselected ? 'unselected' : value,
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
};
|
|
14
|
+
this._hub.trigger(fields.tracker(), {
|
|
15
|
+
type: EVENT_TYPE.FEEDBACK,
|
|
16
|
+
result_type: 'answer',
|
|
17
|
+
value: state.unselected ? 'unselected' : state.value,
|
|
18
|
+
});
|
|
36
19
|
}
|
|
37
20
|
|
|
38
21
|
_destroy() {
|
package/src/actor/fields.js
CHANGED
|
@@ -6,6 +6,10 @@ export function suggestions() {
|
|
|
6
6
|
return 'suggestions';
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
export function completions() {
|
|
10
|
+
return 'completions';
|
|
11
|
+
}
|
|
12
|
+
|
|
9
13
|
export function filters() {
|
|
10
14
|
return 'filters';
|
|
11
15
|
}
|
|
@@ -14,16 +18,16 @@ export function query() {
|
|
|
14
18
|
return 'query';
|
|
15
19
|
}
|
|
16
20
|
|
|
17
|
-
export function request(
|
|
18
|
-
return
|
|
21
|
+
export function request() {
|
|
22
|
+
return 'request';
|
|
19
23
|
}
|
|
20
24
|
|
|
21
|
-
export function response(
|
|
22
|
-
return
|
|
25
|
+
export function response() {
|
|
26
|
+
return 'response';
|
|
23
27
|
}
|
|
24
28
|
|
|
25
|
-
export function data(
|
|
26
|
-
return
|
|
29
|
+
export function data() {
|
|
30
|
+
return 'data';
|
|
27
31
|
}
|
|
28
32
|
|
|
29
33
|
export function view(role) {
|
|
@@ -49,3 +53,7 @@ export function feedback() {
|
|
|
49
53
|
export function input() {
|
|
50
54
|
return 'input';
|
|
51
55
|
}
|
|
56
|
+
|
|
57
|
+
export function more() {
|
|
58
|
+
return 'more';
|
|
59
|
+
}
|
package/src/actor/index.js
CHANGED
|
@@ -6,5 +6,4 @@ export { default as SessionMaker } from './sessions.js';
|
|
|
6
6
|
export { default as DataActor } from './data.js';
|
|
7
7
|
export { default as ViewsActor } from './views.js';
|
|
8
8
|
export { default as FeedbackActor } from './feedback.js';
|
|
9
|
-
export { default as AutocompleteActor } from './autocomplete.js';
|
|
10
9
|
export { default as InteractionsActor } from './interactions.js';
|
|
@@ -12,20 +12,14 @@ export default class InteractionsActor {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
_handle(payload) {
|
|
15
|
+
// TODO: omit when option == false
|
|
15
16
|
payload = asArray(payload);
|
|
16
|
-
|
|
17
|
-
for (const p of preprocess) {
|
|
18
|
-
payload = payload.map(p);
|
|
19
|
-
}
|
|
20
|
-
// TODO: support filter?
|
|
17
|
+
|
|
21
18
|
if (payload.length === 0) {
|
|
22
19
|
return;
|
|
23
20
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
} else {
|
|
27
|
-
this._client.api.interactions.upload(payload, { merge: true });
|
|
28
|
-
}
|
|
21
|
+
|
|
22
|
+
this._client.api.interactions.upload(payload, { merge: true });
|
|
29
23
|
}
|
|
30
24
|
|
|
31
25
|
destroy() {
|
package/src/actor/sessions.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { uuidv4
|
|
1
|
+
import { uuidv4 } from '@miso.ai/commons';
|
|
2
2
|
import * as fields from './fields.js';
|
|
3
3
|
|
|
4
4
|
export default class SessionMaker {
|
|
@@ -6,43 +6,15 @@ export default class SessionMaker {
|
|
|
6
6
|
constructor(hub) {
|
|
7
7
|
this._hub = hub;
|
|
8
8
|
this._sessionIndex = 0;
|
|
9
|
-
delegateGetters(hub, this, ['active']);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
get active() {
|
|
13
|
-
// TODO: may eliminate active flag?
|
|
14
|
-
const session = this._hub.states[fields.session()];
|
|
15
|
-
return !!(session && session.active);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
new({ force = false } = {}) {
|
|
19
|
-
const currentSession = this._hub.states[fields.session()];
|
|
20
|
-
if (currentSession && !currentSession.active && !force) {
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
|
-
const session = this._create();
|
|
24
|
-
this._hub.update(fields.session(), session);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
start() {
|
|
28
|
-
const session = this._hub.states[fields.session()] || this._create();
|
|
29
|
-
if (session.active) {
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
this._hub.update(fields.session(), { ...session, active: true });
|
|
33
9
|
}
|
|
34
10
|
|
|
35
11
|
restart() {
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
_create(active = false) {
|
|
40
|
-
return Object.freeze({
|
|
41
|
-
active,
|
|
12
|
+
const session = Object.freeze({
|
|
42
13
|
uuid: uuidv4(),
|
|
43
14
|
index: this._sessionIndex++,
|
|
44
15
|
meta: {},
|
|
45
16
|
});
|
|
17
|
+
this._hub.update(fields.session(), session);
|
|
46
18
|
}
|
|
47
19
|
|
|
48
20
|
}
|
package/src/actor/tracker.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { asArray } from '@miso.ai/commons';
|
|
2
|
-
import {
|
|
1
|
+
import { asArray, trimObj } from '@miso.ai/commons';
|
|
2
|
+
import { PERFORMANCE_EVENT_TYPES, TRACKING_STATUS, EVENT_TYPE, validateEventType } from '../constants.js';
|
|
3
3
|
import * as fields from './fields.js';
|
|
4
4
|
import States from '../util/states.js';
|
|
5
|
-
import { toInteraction } from './utils.js';
|
|
6
5
|
|
|
7
6
|
const VALUELESS_ITEMS = [Symbol('valueless')];
|
|
8
7
|
|
|
@@ -26,29 +25,36 @@ export default class Tracker {
|
|
|
26
25
|
return this._states.getFullState(item);
|
|
27
26
|
}
|
|
28
27
|
|
|
29
|
-
_trigger(type, items
|
|
28
|
+
_trigger(type, items) {
|
|
29
|
+
validateEventType(type);
|
|
30
30
|
if (this._valueless) {
|
|
31
|
-
meta = items;
|
|
32
31
|
items = VALUELESS_ITEMS;
|
|
33
32
|
}
|
|
34
|
-
|
|
33
|
+
|
|
35
34
|
this._syncSession();
|
|
36
35
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
this._states.set(items, type, TRACKING_STATUS.TRIGGERED);
|
|
36
|
+
items = this._states.untriggered(asArray(items), type);
|
|
37
|
+
if (items.length === 0) {
|
|
38
|
+
return;
|
|
43
39
|
}
|
|
40
|
+
this._states.set(items, type, TRACKING_STATUS.TRIGGERED);
|
|
44
41
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
42
|
+
this._sendToHub({
|
|
43
|
+
type,
|
|
44
|
+
items: this._valueless ? undefined : items,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
submit({ value }) {
|
|
49
|
+
this._syncSession();
|
|
50
|
+
this._sendToHub({
|
|
51
|
+
type: EVENT_TYPE.SUBMIT,
|
|
52
|
+
value,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
49
55
|
|
|
50
|
-
|
|
51
|
-
this._hub.trigger(fields.
|
|
56
|
+
_sendToHub(args) {
|
|
57
|
+
this._hub.trigger(fields.tracker(), Object.freeze(trimObj({ role: this._role, ...args })));
|
|
52
58
|
}
|
|
53
59
|
|
|
54
60
|
_syncSession() {
|
|
@@ -82,7 +88,7 @@ export default class Tracker {
|
|
|
82
88
|
|
|
83
89
|
}
|
|
84
90
|
|
|
85
|
-
for (const type of
|
|
91
|
+
for (const type of PERFORMANCE_EVENT_TYPES) {
|
|
86
92
|
Tracker.prototype[type] = function(items, meta) {
|
|
87
93
|
this._trigger(type, items, meta);
|
|
88
94
|
};
|
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
|
}
|
|
@@ -144,7 +150,7 @@ export default class ViewsActor {
|
|
|
144
150
|
} else {
|
|
145
151
|
for (let [role, [name, options]] of Object.entries(layouts)) {
|
|
146
152
|
// TODO: try to omit if unchanged
|
|
147
|
-
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() });
|
|
148
154
|
if (role === ROLE.CONTAINER) {
|
|
149
155
|
for (const view of this._containers.values()) {
|
|
150
156
|
view.layout = fn();
|
|
@@ -162,7 +168,7 @@ export default class ViewsActor {
|
|
|
162
168
|
return undefined;
|
|
163
169
|
}
|
|
164
170
|
const [name, options] = args;
|
|
165
|
-
return this._layoutFactory.create(name, { ...options, role, workflow: this._workflowName });
|
|
171
|
+
return this._layoutFactory.create(name, { ...options, role, workflow: this._workflowName, workflowOptions: this._getWorkflowOptions() });
|
|
166
172
|
}
|
|
167
173
|
|
|
168
174
|
get(role) {
|
|
@@ -229,6 +235,21 @@ export default class ViewsActor {
|
|
|
229
235
|
return this._containerTracker;
|
|
230
236
|
}
|
|
231
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
|
+
|
|
232
253
|
_error(e) {
|
|
233
254
|
// TODO: hub trigger error event
|
|
234
255
|
console.error(e);
|
|
@@ -271,6 +292,9 @@ class Trackers {
|
|
|
271
292
|
constructor(views, roles) {
|
|
272
293
|
this._views = views;
|
|
273
294
|
for (const role of roles) {
|
|
295
|
+
if (role === ROLE.CONTAINER) {
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
274
298
|
Object.defineProperty(this, role, {
|
|
275
299
|
get: () => views.get(role).tracker,
|
|
276
300
|
});
|
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',
|
|
@@ -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';
|
|
@@ -37,6 +37,7 @@ class Sections extends _Sections {
|
|
|
37
37
|
this.searchResultsInfoGroup(options),
|
|
38
38
|
this.searchResultsFilters(options),
|
|
39
39
|
'<miso-products></miso-products>',
|
|
40
|
+
this.more(options),
|
|
40
41
|
]);
|
|
41
42
|
}
|
|
42
43
|
|
|
@@ -45,7 +46,7 @@ class Sections extends _Sections {
|
|
|
45
46
|
const { classPrefix } = options;
|
|
46
47
|
return element('div', { classes: [`${classPrefix}__search-results-info`] }, [
|
|
47
48
|
this.keywords(options),
|
|
48
|
-
this.
|
|
49
|
+
this.total(options),
|
|
49
50
|
]);
|
|
50
51
|
}
|
|
51
52
|
|
|
@@ -62,9 +63,20 @@ class Sections extends _Sections {
|
|
|
62
63
|
return phrase(options, { name: 'keywords', tag: 'div', inline: true }).replaceAll('${value}', '<miso-keywords></miso-keywords>');
|
|
63
64
|
}
|
|
64
65
|
|
|
65
|
-
|
|
66
|
+
total(options) {
|
|
66
67
|
const { phrase } = this._helpers;
|
|
67
|
-
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
|
+
]);
|
|
68
80
|
}
|
|
69
81
|
|
|
70
82
|
}
|
|
@@ -75,13 +87,13 @@ export const sections = externalize(new Sections({ helpers }));
|
|
|
75
87
|
export function root(options = {}) {
|
|
76
88
|
const { normalizeOptions, section, container } = helpers;
|
|
77
89
|
options = normalizeOptions(options);
|
|
78
|
-
const { answerBox, answerGroup } = sections;
|
|
90
|
+
const { answerBox, answerGroup, searchResultsGroup } = sections;
|
|
79
91
|
const { answerBox: answerBoxOptions = true } = options;
|
|
80
92
|
return [
|
|
81
93
|
section(options, { name: 'question' }, [
|
|
82
94
|
container(options, { name: 'query' }, '<miso-query></miso-query>'),
|
|
83
95
|
]),
|
|
84
96
|
section(options, { name: 'answer' }, answerBoxOptions ? [ answerBox(options) ] : answerGroup(options)),
|
|
85
|
-
section(options, { name: 'search-results' },
|
|
97
|
+
section(options, { name: 'search-results' }, searchResultsGroup(options)),
|
|
86
98
|
].join('');
|
|
87
99
|
};
|