@miso.ai/client-sdk-ui 1.11.4-beta.0 → 1.11.4-beta.2
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/fields.js +0 -4
- package/dist/cjs/actor/filters.js +57 -11
- package/dist/cjs/actor/tracker.js +1 -6
- package/dist/cjs/actor/view.js +6 -8
- package/dist/cjs/actor/views.js +10 -26
- package/dist/cjs/constants.js +12 -2
- package/dist/cjs/element/role/index.js +7 -0
- package/dist/cjs/element/role/miso-sort.js +20 -0
- package/dist/cjs/layout/affiliation/affiliation.js +1 -1
- package/dist/cjs/layout/container/container.js +2 -2
- package/dist/cjs/layout/index.js +7 -0
- package/dist/cjs/layout/input/facets.js +2 -2
- package/dist/cjs/layout/input/search-box.js +1 -1
- package/dist/cjs/layout/input/select.js +150 -0
- package/dist/cjs/layout/raf.js +2 -4
- package/dist/cjs/layout/text/typewriter/index.js +3 -2
- package/dist/cjs/layout/trackable.js +3 -3
- package/dist/cjs/layout/triggerable.js +1 -1
- package/dist/cjs/util/trackers.js +2 -0
- package/dist/cjs/version.js +1 -1
- package/dist/cjs/workflow/answer-based.js +19 -3
- package/dist/cjs/workflow/ask.js +4 -1
- package/dist/cjs/workflow/base.js +27 -4
- package/dist/cjs/workflow/context.js +3 -3
- package/dist/cjs/workflow/explore.js +2 -2
- package/dist/cjs/workflow/hybrid-search-answer.js +9 -7
- package/dist/cjs/workflow/hybrid-search-results.js +18 -16
- package/dist/cjs/workflow/hybrid-search-views.js +5 -12
- package/dist/cjs/workflow/hybrid-search.js +9 -1
- package/dist/cjs/workflow/options/api.js +56 -0
- package/dist/cjs/workflow/options/configurable.js +49 -0
- package/dist/cjs/workflow/options/data-processor.js +23 -0
- package/dist/cjs/workflow/options/filters.js +18 -0
- package/dist/cjs/workflow/options/index.js +115 -0
- package/dist/cjs/workflow/options/interactions.js +44 -0
- package/dist/cjs/workflow/options/layouts.js +83 -0
- package/dist/cjs/workflow/options/options.js +154 -0
- package/dist/cjs/workflow/options/pagination.js +28 -0
- package/dist/cjs/workflow/options/roles.js +17 -0
- package/dist/cjs/workflow/options/trackers.js +39 -0
- package/dist/cjs/workflow/options/utils.js +33 -0
- package/dist/cjs/workflow/processors.js +39 -33
- package/dist/cjs/workflow/recommendation.js +2 -2
- package/dist/cjs/workflow/search-based.js +59 -22
- package/package.json +2 -2
- package/src/actor/fields.js +0 -4
- package/src/actor/filters.js +53 -12
- package/src/actor/tracker.js +1 -6
- package/src/actor/view.js +7 -8
- package/src/actor/views.js +9 -30
- package/src/constants.js +11 -0
- package/src/element/role/index.js +1 -0
- package/src/element/role/miso-sort.js +16 -0
- package/src/layout/affiliation/affiliation.js +1 -1
- package/src/layout/container/container.js +2 -2
- package/src/layout/index.js +1 -0
- package/src/layout/input/facets.js +3 -2
- package/src/layout/input/search-box.js +1 -1
- package/src/layout/input/select.js +142 -0
- package/src/layout/raf.js +2 -2
- package/src/layout/text/typewriter/index.js +3 -2
- package/src/layout/trackable.js +3 -3
- package/src/layout/triggerable.js +1 -1
- package/src/util/trackers.js +2 -0
- package/src/version.js +1 -1
- package/src/workflow/answer-based.js +16 -2
- package/src/workflow/ask.js +4 -1
- package/src/workflow/base.js +22 -1
- package/src/workflow/context.js +1 -1
- package/src/workflow/explore.js +1 -1
- package/src/workflow/hybrid-search-answer.js +8 -3
- package/src/workflow/hybrid-search-results.js +21 -7
- package/src/workflow/hybrid-search-views.js +4 -12
- package/src/workflow/hybrid-search.js +12 -1
- package/src/workflow/options/api.js +46 -0
- package/src/workflow/options/configurable.js +48 -0
- package/src/workflow/options/data-processor.js +15 -0
- package/src/workflow/options/filters.js +10 -0
- package/src/workflow/options/index.js +10 -0
- package/src/workflow/options/interactions.js +36 -0
- package/src/workflow/options/layouts.js +73 -0
- package/src/workflow/options/options.js +169 -0
- package/src/workflow/options/pagination.js +18 -0
- package/src/workflow/options/roles.js +12 -0
- package/src/workflow/options/trackers.js +32 -0
- package/src/workflow/options/utils.js +26 -0
- package/src/workflow/processors.js +30 -34
- package/src/workflow/recommendation.js +1 -1
- package/src/workflow/search-based.js +58 -13
- package/dist/cjs/workflow/options.js +0 -448
- package/src/workflow/options.js +0 -433
package/src/actor/filters.js
CHANGED
|
@@ -12,43 +12,84 @@ function mergeState(state = {}, update = {}) {
|
|
|
12
12
|
}));
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
function optionsToState({ sort } = {}) {
|
|
16
|
+
// TODO: facets
|
|
17
|
+
return trimObj({
|
|
18
|
+
facets: Object.freeze({}),
|
|
19
|
+
sort: sortOptionsToState(sort),
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function sortOptionsToState(sort) {
|
|
24
|
+
if (!sort) {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
const { options = [] } = sort;
|
|
28
|
+
for (const option of options) {
|
|
29
|
+
if (option.default) {
|
|
30
|
+
return Object.freeze(option);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return Object.freeze(options[0]);
|
|
34
|
+
}
|
|
35
|
+
|
|
15
36
|
export default class Filters {
|
|
16
37
|
|
|
17
38
|
constructor(views, { facets: facetsOptions } = {}) {
|
|
18
39
|
this._views = views;
|
|
19
40
|
this._hub = views._hub;
|
|
20
|
-
this._states = this._initialStates = mergeState({}); // TODO: support custom initial states
|
|
21
41
|
const error = this._error = this._error.bind(this);
|
|
22
42
|
this._events = new EventEmitter({ target: this, error });
|
|
23
43
|
defineValues(this, { facets: new Facets(this, facetsOptions) });
|
|
44
|
+
|
|
45
|
+
this._unsubscribes = [
|
|
46
|
+
views._options.on('filters', () => this._syncOptions()),
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
this._syncOptions();
|
|
50
|
+
this._states = undefined;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
_syncOptions() {
|
|
54
|
+
const options = this._views._options.resolved.filters;
|
|
55
|
+
this._initialStates = optionsToState(options);
|
|
56
|
+
this.apply({ silent: true });
|
|
24
57
|
}
|
|
25
58
|
|
|
26
|
-
|
|
27
|
-
return this._states;
|
|
59
|
+
_getStates() {
|
|
60
|
+
return this._states || this._initialStates;
|
|
28
61
|
}
|
|
29
62
|
|
|
30
63
|
update(updates) {
|
|
31
|
-
const oldStates = this.
|
|
32
|
-
|
|
64
|
+
const oldStates = this._getStates();
|
|
65
|
+
this._states = mergeState({ ...oldStates, ...updates });
|
|
66
|
+
const states = this._getStates();
|
|
33
67
|
this._events.emit('update', { updates, states, oldStates });
|
|
34
68
|
}
|
|
35
69
|
|
|
36
70
|
reset() {
|
|
37
|
-
const oldStates = this.
|
|
38
|
-
|
|
71
|
+
const oldStates = this.states;
|
|
72
|
+
this._states = undefined;
|
|
73
|
+
const states = this._getStates();
|
|
39
74
|
this._events.emit('reset', { states, oldStates });
|
|
40
75
|
}
|
|
41
76
|
|
|
42
|
-
apply() {
|
|
43
|
-
this.
|
|
44
|
-
this.
|
|
77
|
+
apply({ silent = false } = {}) {
|
|
78
|
+
const states = this._getStates();
|
|
79
|
+
this._hub.update(fields.filters(), states, { silent });
|
|
80
|
+
!silent && this._events.emit('apply');
|
|
45
81
|
}
|
|
46
82
|
|
|
47
83
|
_error(error) {
|
|
48
84
|
this._views._error(error);
|
|
49
85
|
}
|
|
50
86
|
|
|
51
|
-
_destroy() {
|
|
87
|
+
_destroy() {
|
|
88
|
+
for (const unsubscribe of this._unsubscribes) {
|
|
89
|
+
unsubscribe();
|
|
90
|
+
}
|
|
91
|
+
this._unsubscribes = [];
|
|
92
|
+
}
|
|
52
93
|
|
|
53
94
|
}
|
|
54
95
|
|
|
@@ -87,7 +128,7 @@ class Facets {
|
|
|
87
128
|
}
|
|
88
129
|
|
|
89
130
|
_select(field, value) {
|
|
90
|
-
const values = this._options.
|
|
131
|
+
const values = this._options.multivalued ? [...(this._filters._states.facets[field] || []), value] : [value];
|
|
91
132
|
values.sort();
|
|
92
133
|
this._filters.update({ facets: { [field]: values } });
|
|
93
134
|
}
|
package/src/actor/tracker.js
CHANGED
|
@@ -7,20 +7,15 @@ const VALUELESS_ITEMS = [Symbol('valueless')];
|
|
|
7
7
|
|
|
8
8
|
export default class Tracker {
|
|
9
9
|
|
|
10
|
-
constructor({ hub, role, valueless = false
|
|
10
|
+
constructor({ hub, role, valueless = false }) {
|
|
11
11
|
this._hub = hub;
|
|
12
12
|
this._role = role;
|
|
13
13
|
this._valueless = !!valueless;
|
|
14
|
-
this._options = options;
|
|
15
14
|
// TODO: know item type
|
|
16
15
|
|
|
17
16
|
this._states = new States(this._getSessionId());
|
|
18
17
|
}
|
|
19
18
|
|
|
20
|
-
get options() {
|
|
21
|
-
return typeof this._options === 'function' ? this._options() : this._options;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
19
|
getState(item) {
|
|
25
20
|
return this._states.getFullState(item);
|
|
26
21
|
}
|
package/src/actor/view.js
CHANGED
|
@@ -97,13 +97,17 @@ export default class ViewActor {
|
|
|
97
97
|
}
|
|
98
98
|
if (!this._tracker) {
|
|
99
99
|
const { hub } = this;
|
|
100
|
-
this._tracker = new Tracker({ hub, role
|
|
100
|
+
this._tracker = new Tracker({ hub, role });
|
|
101
101
|
}
|
|
102
102
|
return this._tracker;
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
get workflowOptions() {
|
|
106
|
-
return this._views.
|
|
106
|
+
return this._views._options.resolved;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
get trackerOptions() {
|
|
110
|
+
return this._views._getTrackerOptions(this.role);
|
|
107
111
|
}
|
|
108
112
|
|
|
109
113
|
get filters() {
|
|
@@ -154,15 +158,10 @@ export default class ViewActor {
|
|
|
154
158
|
this.hub.update(fields.view(role), state, { silent });
|
|
155
159
|
}
|
|
156
160
|
|
|
157
|
-
_getTrackerOptions() {
|
|
158
|
-
// used by tracker to expose tracker options to layout
|
|
159
|
-
return this._views._getTrackerOptions(this.role);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
161
|
_sliceData(data) {
|
|
163
162
|
const { value, error, status, meta, ...rest } = data;
|
|
164
163
|
const sliced = {
|
|
165
|
-
value: this._mapping(data),
|
|
164
|
+
value: this._mapping(data, this),
|
|
166
165
|
status,
|
|
167
166
|
data: value,
|
|
168
167
|
meta,
|
package/src/actor/views.js
CHANGED
|
@@ -44,15 +44,9 @@ export default class ViewsActor {
|
|
|
44
44
|
this._unsubscribes = [
|
|
45
45
|
() => window.removeEventListener('resize', syncSize),
|
|
46
46
|
hub.on(fields.data(), data => this.refresh({ data })),
|
|
47
|
-
options.on('autocomplete', () => this._syncAutocompleteOptions()),
|
|
48
|
-
options.on('pagination', () => this._syncPaginationOptions()),
|
|
49
|
-
options.on('trackers', () => this._syncTrackers()),
|
|
50
47
|
options.on('layouts', () => this._syncLayouts()),
|
|
51
48
|
];
|
|
52
49
|
|
|
53
|
-
this._syncAutocompleteOptions();
|
|
54
|
-
this._syncPaginationOptions();
|
|
55
|
-
this._syncTrackers();
|
|
56
50
|
this._syncLayouts();
|
|
57
51
|
}
|
|
58
52
|
|
|
@@ -150,13 +144,16 @@ export default class ViewsActor {
|
|
|
150
144
|
} else {
|
|
151
145
|
for (let [role, [name, options]] of Object.entries(layouts)) {
|
|
152
146
|
// TODO: try to omit if unchanged
|
|
153
|
-
const fn = () => this._layoutFactory.create(name, { ...options, role, workflow: this._workflowName
|
|
147
|
+
const fn = () => this._layoutFactory.create(name, { ...options, role, workflow: this._workflowName });
|
|
154
148
|
if (role === ROLE.CONTAINER) {
|
|
155
149
|
for (const view of this._containers.values()) {
|
|
156
150
|
view.layout = fn();
|
|
157
151
|
}
|
|
158
152
|
} else {
|
|
159
|
-
this.get(role)
|
|
153
|
+
const view = this.get(role);
|
|
154
|
+
if (view) {
|
|
155
|
+
view.layout = fn();
|
|
156
|
+
}
|
|
160
157
|
}
|
|
161
158
|
}
|
|
162
159
|
}
|
|
@@ -168,11 +165,11 @@ export default class ViewsActor {
|
|
|
168
165
|
return undefined;
|
|
169
166
|
}
|
|
170
167
|
const [name, options] = args;
|
|
171
|
-
return this._layoutFactory.create(name, { ...options, role, workflow: this._workflowName
|
|
168
|
+
return this._layoutFactory.create(name, { ...options, role, workflow: this._workflowName });
|
|
172
169
|
}
|
|
173
170
|
|
|
174
171
|
get(role) {
|
|
175
|
-
return this._views[role]
|
|
172
|
+
return this._views[role];
|
|
176
173
|
}
|
|
177
174
|
|
|
178
175
|
get views() {
|
|
@@ -218,12 +215,9 @@ export default class ViewsActor {
|
|
|
218
215
|
return data || this._hub.states[fields.data()];
|
|
219
216
|
}
|
|
220
217
|
|
|
221
|
-
_syncTrackers() {
|
|
222
|
-
this._trackerOptions = this._options.resolved.trackers;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
218
|
_getTrackerOptions(role) {
|
|
226
|
-
|
|
219
|
+
const allTrackerOptions = this._options.resolved.trackers;
|
|
220
|
+
return normalizeTrackerOptions((allTrackerOptions && allTrackerOptions[role]) || false);
|
|
227
221
|
}
|
|
228
222
|
|
|
229
223
|
_getContainerTracker() {
|
|
@@ -235,21 +229,6 @@ export default class ViewsActor {
|
|
|
235
229
|
return this._containerTracker;
|
|
236
230
|
}
|
|
237
231
|
|
|
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
|
-
|
|
253
232
|
_error(e) {
|
|
254
233
|
// TODO: hub trigger error event
|
|
255
234
|
console.error(e);
|
package/src/constants.js
CHANGED
|
@@ -24,6 +24,7 @@ export const ROLE = Object.freeze({
|
|
|
24
24
|
FACETS: 'facets',
|
|
25
25
|
TOTAL: 'total',
|
|
26
26
|
MORE: 'more',
|
|
27
|
+
SORT: 'sort',
|
|
27
28
|
ERROR: 'error',
|
|
28
29
|
});
|
|
29
30
|
|
|
@@ -114,3 +115,13 @@ export function validateTrackingStatus(value) {
|
|
|
114
115
|
throw new Error(`Unrecognized tracking status: ${value}`);
|
|
115
116
|
}
|
|
116
117
|
}
|
|
118
|
+
|
|
119
|
+
export const WORKFLOW_CONFIGURABLE = Object.freeze({
|
|
120
|
+
API: 'api',
|
|
121
|
+
LAYOUTS: 'layouts',
|
|
122
|
+
DATA_PROCESSOR: 'dataProcessor',
|
|
123
|
+
TRACKERS: 'trackers',
|
|
124
|
+
INTERACTIONS: 'interactions',
|
|
125
|
+
FILTERS: 'filters',
|
|
126
|
+
PAGINATION: 'pagination',
|
|
127
|
+
});
|
|
@@ -14,4 +14,5 @@ export { default as MisoKeywordsElement } from './miso-keywords.js';
|
|
|
14
14
|
export { default as MisoTotalElement } from './miso-total.js';
|
|
15
15
|
export { default as MisoFacetsElement } from './miso-facets.js';
|
|
16
16
|
export { default as MisoMoreElement } from './miso-more.js';
|
|
17
|
+
export { default as MisoSortElement } from './miso-sort.js';
|
|
17
18
|
export { default as MisoErrorElement } from './miso-error.js';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import MisoComponentElement from './miso-component.js';
|
|
2
|
+
import { ROLE } from '../../constants.js';
|
|
3
|
+
|
|
4
|
+
export default class MisoSortElement extends MisoComponentElement {
|
|
5
|
+
|
|
6
|
+
constructor() {
|
|
7
|
+
super({
|
|
8
|
+
role: ROLE.SORT,
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
static get tagName() {
|
|
13
|
+
return 'miso-sort';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
}
|
|
@@ -165,7 +165,7 @@ export default class AffiliationLayout extends CollectionLayout {
|
|
|
165
165
|
|
|
166
166
|
initialize(view) {
|
|
167
167
|
super.initialize(view);
|
|
168
|
-
const { viewable: options } = this._view.
|
|
168
|
+
const { viewable: options } = this._view.trackerOptions || {};
|
|
169
169
|
this._viewable = new CarouselItemViewabilityObserver(this._onViewable.bind(this), options);
|
|
170
170
|
}
|
|
171
171
|
|
|
@@ -82,7 +82,7 @@ export default class ContainerLayout extends RafLayout {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
_trackImpression() {
|
|
85
|
-
const { impression: options } = this._view.
|
|
85
|
+
const { impression: options } = this._view.trackerOptions || {};
|
|
86
86
|
if (!options) {
|
|
87
87
|
return;
|
|
88
88
|
}
|
|
@@ -94,7 +94,7 @@ export default class ContainerLayout extends RafLayout {
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
async _trackViewable(element) {
|
|
97
|
-
const { viewable: options } = this._view.
|
|
97
|
+
const { viewable: options } = this._view.trackerOptions || {};
|
|
98
98
|
if (!options) {
|
|
99
99
|
return;
|
|
100
100
|
}
|
package/src/layout/index.js
CHANGED
|
@@ -13,4 +13,5 @@ export { default as TextLayout } from './text/text.js';
|
|
|
13
13
|
export { default as TypewriterLayout } from './text/typewriter/index.js';
|
|
14
14
|
export { default as AffiliationLayout } from './affiliation/affiliation.js';
|
|
15
15
|
export { default as FacetsLayout } from './input/facets.js';
|
|
16
|
+
export { default as SelectLayout } from './input/select.js';
|
|
16
17
|
export { default as MoreButtonLayout } from './button/more-button.js';
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { escapeHtml } from '@miso.ai/commons';
|
|
2
|
+
import { fields } from '../../actor/index.js';
|
|
2
3
|
import TemplateBasedLayout from '../template.js';
|
|
3
4
|
import Bindings from '../../util/bindings.js';
|
|
4
5
|
|
|
@@ -109,7 +110,7 @@ export default class FacetsLayout extends TemplateBasedLayout {
|
|
|
109
110
|
}
|
|
110
111
|
|
|
111
112
|
initialize(view) {
|
|
112
|
-
const { proxyElement,
|
|
113
|
+
const { proxyElement, filters } = view;
|
|
113
114
|
this._unsubscribes = [
|
|
114
115
|
...this._unsubscribes,
|
|
115
116
|
proxyElement.on('click', (e) => this._handleClick(e)),
|
|
@@ -132,7 +133,7 @@ export default class FacetsLayout extends TemplateBasedLayout {
|
|
|
132
133
|
}
|
|
133
134
|
|
|
134
135
|
_syncSelections() {
|
|
135
|
-
const selectedValues = getSelectedValues(this._view.
|
|
136
|
+
const selectedValues = getSelectedValues(this._view.hub.states[fields.filters()].facets);
|
|
136
137
|
for (const { value: { field, value }, element } of this._bindings.entries) {
|
|
137
138
|
this._setSelected(element, selectedValues[field] && selectedValues[field].has(value));
|
|
138
139
|
}
|
|
@@ -338,7 +338,7 @@ export default class SearchBoxLayout extends TemplateBasedLayout {
|
|
|
338
338
|
}
|
|
339
339
|
|
|
340
340
|
_trackSubmit(value) {
|
|
341
|
-
const { [EVENT_TYPE.SUBMIT]: options } = this._view.
|
|
341
|
+
const { [EVENT_TYPE.SUBMIT]: options } = this._view.trackerOptions || {};
|
|
342
342
|
if (!options) {
|
|
343
343
|
return;
|
|
344
344
|
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { escapeHtml, kebabOrSnakeToHuman } from '@miso.ai/commons';
|
|
2
|
+
import TemplateBasedLayout from '../template.js';
|
|
3
|
+
import { makeTrackable } from '../trackable.js';
|
|
4
|
+
|
|
5
|
+
const TYPE = 'select';
|
|
6
|
+
const DEFAULT_CLASSNAME = 'miso-select';
|
|
7
|
+
|
|
8
|
+
function root(layout, state) {
|
|
9
|
+
const { className, role, templates } = layout;
|
|
10
|
+
const roleAttr = role ? `data-role="${role}"` : '';
|
|
11
|
+
const values = layout._getItems(state);
|
|
12
|
+
const selected = values.find(value => value.selected) || values[0];
|
|
13
|
+
return `
|
|
14
|
+
<div class="${className}" ${roleAttr}>
|
|
15
|
+
${templates.button(layout, selected)}
|
|
16
|
+
${templates.options(layout, state)}
|
|
17
|
+
</div>`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function button(layout, value) {
|
|
21
|
+
const { className, templates } = layout;
|
|
22
|
+
return `<button class="${className}__button" data-role="button" tabindex="0">${templates.text(layout, value)}</button>`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function options(layout, state) {
|
|
26
|
+
const { className, templates } = layout;
|
|
27
|
+
const values = layout._getItems(state);
|
|
28
|
+
return `<ul class="${className}__options">${values.map((value, index) => templates.option(layout, value, { index })).join('')}</ul>`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function option(layout, value) {
|
|
32
|
+
const { className, templates } = layout;
|
|
33
|
+
const selectedClass = value.selected ? 'selected' : '';
|
|
34
|
+
return `<li class="${className}__option ${selectedClass}" data-role="option" tabindex="0">${templates.text(layout, value)}</li>`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function text(layout, value) {
|
|
38
|
+
return escapeHtml(value && value.text && kebabOrSnakeToHuman(value.text) || value || '');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const DEFAULT_TEMPLATES = Object.freeze({
|
|
42
|
+
root,
|
|
43
|
+
button,
|
|
44
|
+
options,
|
|
45
|
+
option,
|
|
46
|
+
text,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
export default class SelectLayout extends TemplateBasedLayout {
|
|
50
|
+
|
|
51
|
+
static get type() {
|
|
52
|
+
return TYPE;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
static get defaultTemplates() {
|
|
56
|
+
return DEFAULT_TEMPLATES;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
static get defaultClassName() {
|
|
60
|
+
return DEFAULT_CLASSNAME;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
constructor({ className = DEFAULT_CLASSNAME, templates, ...options } = {}) {
|
|
64
|
+
super({
|
|
65
|
+
className,
|
|
66
|
+
templates: { ...DEFAULT_TEMPLATES, ...templates },
|
|
67
|
+
...options,
|
|
68
|
+
});
|
|
69
|
+
this._initTrackable();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
initialize(view) {
|
|
73
|
+
const { proxyElement } = view;
|
|
74
|
+
this._unsubscribes = [
|
|
75
|
+
...this._unsubscribes,
|
|
76
|
+
proxyElement.on('click', (e) => this._handleClick(e)),
|
|
77
|
+
];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
_afterRender(element, state) {
|
|
81
|
+
this._syncBindings(element, state);
|
|
82
|
+
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
_unrender() {
|
|
86
|
+
this._clearBindings();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
_getItems(state) {
|
|
90
|
+
return state.value || [];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
_getOptionsElement(element) {
|
|
94
|
+
return element.querySelector(`[data-role="options"]`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
_getItemElements(element) {
|
|
98
|
+
return element ? Array.from(element.querySelectorAll(`[data-role="option"]`)) : [];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
_handleClick(event) {
|
|
102
|
+
if (!this._element) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
for (let element = event.target; element && element !== this._element; element = element.parentElement) {
|
|
106
|
+
const { role } = element.dataset;
|
|
107
|
+
switch (role) {
|
|
108
|
+
case 'button':
|
|
109
|
+
this._toggle();
|
|
110
|
+
break;
|
|
111
|
+
case 'option':
|
|
112
|
+
const binding = this._bindings.get(element);
|
|
113
|
+
this._select(binding && binding.value);
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
_toggle() {
|
|
120
|
+
if (!this._element) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const child = this._element.children[0];
|
|
124
|
+
child && child.classList.toggle('open');
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async _select(value) {
|
|
128
|
+
if (!value) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
// TODO: reflect selection right away
|
|
132
|
+
this._view._events.emit('select', { value });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
destroy() {
|
|
136
|
+
this._destroyTrackable();
|
|
137
|
+
super.destroy();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
makeTrackable(SelectLayout.prototype);
|
package/src/layout/raf.js
CHANGED
|
@@ -2,8 +2,8 @@ import { defineValues, requestAnimationFrame as raf, capture } from '@miso.ai/co
|
|
|
2
2
|
|
|
3
3
|
export default class RafLayout {
|
|
4
4
|
|
|
5
|
-
constructor({ role, workflow,
|
|
6
|
-
defineValues(this, { role, workflow,
|
|
5
|
+
constructor({ role, workflow, ...options } = {}) {
|
|
6
|
+
defineValues(this, { role, workflow, options });
|
|
7
7
|
this._unsubscribes = [];
|
|
8
8
|
this._rendered = new WeakMap();
|
|
9
9
|
this._element = undefined;
|
|
@@ -130,7 +130,8 @@ export default class TypewriterLayout extends ProgressiveLayout {
|
|
|
130
130
|
const container = containerElement(this, element, state);
|
|
131
131
|
// TODO: distinguish between first render and element replacement
|
|
132
132
|
let result;
|
|
133
|
-
|
|
133
|
+
const newStreak = !rendered || state.streak !== rendered.streak;
|
|
134
|
+
if (newStreak) {
|
|
134
135
|
// new typing streak
|
|
135
136
|
result = this._renderer.clear(container, rendered);
|
|
136
137
|
} else {
|
|
@@ -141,7 +142,7 @@ export default class TypewriterLayout extends ProgressiveLayout {
|
|
|
141
142
|
|
|
142
143
|
const ongoing = state.status === STATUS.READY && !result.done;
|
|
143
144
|
// silence on consecutive ongoing renders
|
|
144
|
-
const silent = ongoing && (!rendered || rendered.status === STATUS.READY);
|
|
145
|
+
const silent = ongoing && (!rendered || rendered.status === STATUS.READY) && !newStreak;
|
|
145
146
|
notifyUpdate({ ongoing }, { silent });
|
|
146
147
|
|
|
147
148
|
ongoing && loop();
|
package/src/layout/trackable.js
CHANGED
|
@@ -51,7 +51,7 @@ export class TrackableMixin {
|
|
|
51
51
|
if (entries.length === 0) {
|
|
52
52
|
return;
|
|
53
53
|
}
|
|
54
|
-
const { impression: options } = this._view.
|
|
54
|
+
const { impression: options } = this._view.trackerOptions || {};
|
|
55
55
|
if (!options) {
|
|
56
56
|
return;
|
|
57
57
|
}
|
|
@@ -68,7 +68,7 @@ export class TrackableMixin {
|
|
|
68
68
|
if (entries.length === 0) {
|
|
69
69
|
return;
|
|
70
70
|
}
|
|
71
|
-
const { viewable: options } = this._view.
|
|
71
|
+
const { viewable: options } = this._view.trackerOptions || {};
|
|
72
72
|
if (!options) {
|
|
73
73
|
return;
|
|
74
74
|
}
|
|
@@ -78,7 +78,7 @@ export class TrackableMixin {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
_trackClick(event, binding) {
|
|
81
|
-
const { click: options } = this._view.
|
|
81
|
+
const { click: options } = this._view.trackerOptions || {};
|
|
82
82
|
if (validateClick(options, event, binding)) {
|
|
83
83
|
this._view.tracker.click([binding.value]);
|
|
84
84
|
}
|
|
@@ -16,7 +16,7 @@ export class TriggerableMixin {
|
|
|
16
16
|
if (!this.options.infiniteScroll) {
|
|
17
17
|
return;
|
|
18
18
|
}
|
|
19
|
-
const workflowPaginationOptions = this.workflowOptions.pagination;
|
|
19
|
+
const workflowPaginationOptions = this._view.workflowOptions.pagination;
|
|
20
20
|
if (!workflowPaginationOptions.active || workflowPaginationOptions.mode !== 'infiniteScroll') {
|
|
21
21
|
return;
|
|
22
22
|
}
|
package/src/util/trackers.js
CHANGED
package/src/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export default '1.11.4-beta.
|
|
1
|
+
export default '1.11.4-beta.2';
|
|
@@ -4,7 +4,7 @@ import { fields, FeedbackActor } from '../actor/index.js';
|
|
|
4
4
|
import { ROLE, STATUS, ORGANIC_QUESTION_SOURCE } from '../constants.js';
|
|
5
5
|
import { SearchBoxLayout, TextLayout, ListLayout, TypewriterLayout, FeedbackLayout, AffiliationLayout } from '../layout/index.js';
|
|
6
6
|
import { processData as processAffiliationData } from '../affiliation/index.js';
|
|
7
|
-
import { mergeRolesOptions } from './options.js';
|
|
7
|
+
import { mergeRolesOptions } from './options/index.js';
|
|
8
8
|
import { writeAnswerStageToMeta, mergeInteraction } from './processors.js';
|
|
9
9
|
import { enableUseLink } from './use-link.js';
|
|
10
10
|
|
|
@@ -91,6 +91,10 @@ export default class AnswerBasedWorkflow extends Workflow {
|
|
|
91
91
|
super.restart();
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
_shallEmitLifecycleEvent(name, event) {
|
|
95
|
+
return name === 'answer-stage' || super._shallEmitLifecycleEvent(name, event);
|
|
96
|
+
}
|
|
97
|
+
|
|
94
98
|
_emitInterruptEventIfNecessary() {
|
|
95
99
|
const state = this._hub.states[fields.view(this._roles.main)] || {};
|
|
96
100
|
if (state.session && state.status !== STATUS.INITIAL && (state.status !== STATUS.READY || state.ongoing)) {
|
|
@@ -101,12 +105,22 @@ export default class AnswerBasedWorkflow extends Workflow {
|
|
|
101
105
|
}
|
|
102
106
|
|
|
103
107
|
_onMainViewUpdate(state) {
|
|
104
|
-
const { status } = state;
|
|
108
|
+
const { status, session } = state;
|
|
105
109
|
const done = status === STATUS.READY && !state.ongoing;
|
|
106
110
|
const erroneous = status === STATUS.ERRONEOUS;
|
|
107
111
|
const eventName = done ? 'done' : erroneous ? 'error' : status;
|
|
112
|
+
// answer-stage
|
|
113
|
+
const context = this._getSessionContext(session);
|
|
114
|
+
const oldAnswerStage = context.answerStage;
|
|
115
|
+
const newAnswerStage = state.meta && state.meta.answer_stage;
|
|
116
|
+
if (newAnswerStage && newAnswerStage !== oldAnswerStage) {
|
|
117
|
+
this._emitLifecycleEvent('answer-stage', Object.freeze({ ...state, answer_stage: newAnswerStage }));
|
|
118
|
+
context.answerStage = newAnswerStage;
|
|
119
|
+
}
|
|
120
|
+
// ready, done, or error
|
|
108
121
|
this._emitLifecycleEvent(eventName, state);
|
|
109
122
|
if (done || erroneous) {
|
|
123
|
+
// finally
|
|
110
124
|
this._emitLifecycleEvent('finally', state);
|
|
111
125
|
}
|
|
112
126
|
}
|
package/src/workflow/ask.js
CHANGED
|
@@ -4,6 +4,7 @@ import { fields } from '../actor/index.js';
|
|
|
4
4
|
import { ROLE, ORGANIC_QUESTION_SOURCE } from '../constants.js';
|
|
5
5
|
import { OptionListLayout, ListLayout, SearchBoxLayout } from '../layout/index.js';
|
|
6
6
|
import { mergeInteraction } from './processors.js';
|
|
7
|
+
import { mergeRolesOptions } from './options/index.js';
|
|
7
8
|
|
|
8
9
|
const DEFAULT_API_OPTIONS = Object.freeze({
|
|
9
10
|
...AnswerBasedWorkflow.DEFAULT_API_OPTIONS,
|
|
@@ -38,7 +39,9 @@ const DEFAULT_OPTIONS = Object.freeze({
|
|
|
38
39
|
trackers: DEFAULT_TRACKERS,
|
|
39
40
|
});
|
|
40
41
|
|
|
41
|
-
const ROLES_OPTIONS = AnswerBasedWorkflow.ROLES_OPTIONS
|
|
42
|
+
const ROLES_OPTIONS = mergeRolesOptions(AnswerBasedWorkflow.ROLES_OPTIONS, {
|
|
43
|
+
members: Object.keys(DEFAULT_LAYOUTS),
|
|
44
|
+
});
|
|
42
45
|
|
|
43
46
|
export default class Ask extends AnswerBasedWorkflow {
|
|
44
47
|
|