@miso.ai/client-sdk-ui 1.10.3 → 1.11.0-beta.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/autocomplete.js +4 -1
- package/dist/cjs/actor/data.js +15 -6
- package/dist/cjs/actor/fields.js +10 -10
- package/dist/cjs/actor/filters.js +114 -0
- package/dist/cjs/actor/hub.js +2 -1
- package/dist/cjs/actor/sessions.js +3 -5
- package/dist/cjs/actor/view.js +19 -6
- package/dist/cjs/actor/views.js +32 -19
- package/dist/cjs/constants.js +9 -2
- package/dist/cjs/defaults/ask/constants.js +3 -2
- package/dist/cjs/defaults/ask/templates.js +112 -219
- package/dist/cjs/defaults/common/templates.js +234 -0
- package/dist/cjs/defaults/hybrid-search/answer-box.js +115 -0
- package/dist/cjs/defaults/hybrid-search/constants.js +8 -0
- package/dist/cjs/defaults/hybrid-search/controls.js +17 -0
- package/dist/cjs/defaults/hybrid-search/index.js +31 -0
- package/dist/cjs/defaults/hybrid-search/phrases.js +27 -0
- package/dist/cjs/defaults/hybrid-search/templates.js +127 -0
- package/dist/cjs/defaults/index.js +4 -2
- package/dist/cjs/element/container/index.js +7 -0
- package/dist/cjs/element/container/miso-hybrid-search.js +21 -0
- package/dist/cjs/element/role/index.js +21 -0
- package/dist/cjs/element/role/miso-facets.js +20 -0
- package/dist/cjs/element/role/miso-hits.js +20 -0
- package/dist/cjs/element/role/miso-keywords.js +20 -0
- package/dist/cjs/layout/index.js +7 -0
- package/dist/cjs/layout/input/facets.js +294 -0
- package/dist/cjs/layout/input/option-list.js +4 -4
- package/dist/cjs/layout/input/search-box.js +6 -8
- package/dist/cjs/layout/list/collection.js +1 -2
- package/dist/cjs/layout/radio/radio.js +1 -1
- package/dist/cjs/layout/raf.js +1 -0
- package/dist/cjs/layout/template.js +3 -1
- package/dist/cjs/layout/templates.js +15 -0
- package/dist/cjs/layout/text/text.js +38 -5
- package/dist/cjs/layout/trackable.js +2 -16
- package/dist/cjs/plugin.js +4 -1
- package/dist/cjs/{source/index.js → source.js} +7 -4
- package/dist/cjs/util/bindings.js +3 -0
- package/dist/cjs/version.js +1 -1
- package/dist/cjs/workflow/answer-based.js +335 -0
- package/dist/cjs/workflow/ask.js +98 -271
- package/dist/cjs/workflow/base.js +69 -35
- package/dist/cjs/workflow/explore.js +10 -0
- package/dist/cjs/workflow/hybrid-search.js +140 -0
- package/dist/cjs/workflow/index.js +7 -0
- package/dist/cjs/workflow/processors.js +113 -0
- package/dist/cjs/workflow/recommendation.js +12 -3
- package/dist/cjs/workflow/search.js +34 -12
- package/package.json +2 -2
- package/src/actor/autocomplete.js +4 -2
- package/src/actor/data.js +8 -2
- package/src/actor/fields.js +10 -10
- package/src/actor/filters.js +103 -0
- package/src/actor/hub.js +2 -1
- package/src/actor/sessions.js +3 -4
- package/src/actor/view.js +19 -2
- package/src/actor/views.js +30 -16
- package/src/constants.js +10 -0
- package/src/defaults/ask/constants.js +1 -0
- package/src/defaults/ask/templates.js +70 -179
- package/src/defaults/common/templates.js +173 -0
- package/src/defaults/hybrid-search/answer-box.js +118 -0
- package/src/defaults/hybrid-search/constants.js +2 -0
- package/src/defaults/hybrid-search/controls.js +11 -0
- package/src/defaults/hybrid-search/index.js +9 -0
- package/src/defaults/hybrid-search/phrases.js +22 -0
- package/src/defaults/hybrid-search/templates.js +83 -0
- package/src/defaults/index.js +2 -0
- package/src/element/container/index.js +1 -0
- package/src/element/container/miso-hybrid-search.js +19 -0
- package/src/element/role/index.js +3 -0
- package/src/element/role/miso-facets.js +16 -0
- package/src/element/role/miso-hits.js +16 -0
- package/src/element/role/miso-keywords.js +16 -0
- package/src/layout/index.js +1 -0
- package/src/layout/input/facets.js +242 -0
- package/src/layout/input/option-list.js +3 -4
- package/src/layout/input/search-box.js +6 -7
- package/src/layout/list/collection.js +1 -2
- package/src/layout/radio/radio.js +1 -1
- package/src/layout/raf.js +1 -0
- package/src/layout/template.js +2 -1
- package/src/layout/templates.js +15 -0
- package/src/layout/text/text.js +28 -3
- package/src/layout/trackable.js +2 -15
- package/src/plugin.js +6 -2
- package/src/{source/index.js → source.js} +7 -4
- package/src/util/bindings.js +4 -0
- package/src/version.js +1 -1
- package/src/workflow/answer-based.js +280 -0
- package/src/workflow/ask.js +82 -204
- package/src/workflow/base.js +64 -27
- package/src/workflow/explore.js +10 -1
- package/src/workflow/hybrid-search.js +125 -0
- package/src/workflow/index.js +1 -0
- package/src/workflow/processors.js +95 -0
- package/src/workflow/recommendation.js +9 -2
- package/src/workflow/search.js +35 -7
- package/dist/cjs/source/api.js +0 -23
- package/src/source/api.js +0 -12
package/src/layout/text/text.js
CHANGED
|
@@ -5,10 +5,35 @@ import TemplateBasedLayout from '../template.js';
|
|
|
5
5
|
const TYPE = 'text';
|
|
6
6
|
const DEFAULT_CLASSNAME = 'miso-text';
|
|
7
7
|
|
|
8
|
-
function root(layout,
|
|
9
|
-
const { className, role, options: { tag } } = layout;
|
|
8
|
+
function root(layout, { value }) {
|
|
9
|
+
const { className, role, options: { raw = false, tag } } = layout;
|
|
10
10
|
const roleAttr = role ? `data-role="${role}"` : '';
|
|
11
|
-
|
|
11
|
+
const content = value !== undefined ? escapeHtml(getFormatFunction(layout)(value)) : '';
|
|
12
|
+
return raw ? content : `<${tag} class="${className}" ${roleAttr}>${content}</${tag}>`;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function getFormatFunction(layout) {
|
|
16
|
+
let { format } = layout.options;
|
|
17
|
+
const { formatDate, formatNumber } = layout.templates.helpers;
|
|
18
|
+
if (!format) {
|
|
19
|
+
return v => `${v}`;
|
|
20
|
+
}
|
|
21
|
+
switch (typeof format) {
|
|
22
|
+
case 'function':
|
|
23
|
+
return format;
|
|
24
|
+
case 'string':
|
|
25
|
+
format = [format];
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
let [type, args] = format;
|
|
29
|
+
switch (type) {
|
|
30
|
+
case 'date':
|
|
31
|
+
return v => formatDate(v, args);
|
|
32
|
+
case 'number':
|
|
33
|
+
return v => formatNumber(v, args);
|
|
34
|
+
default:
|
|
35
|
+
return v => `${v}`;
|
|
36
|
+
}
|
|
12
37
|
}
|
|
13
38
|
|
|
14
39
|
const DEFAULT_TEMPLATES = Object.freeze({
|
package/src/layout/trackable.js
CHANGED
|
@@ -1,23 +1,10 @@
|
|
|
1
|
+
import { mixin } from '@miso.ai/commons';
|
|
1
2
|
import Bindings from '../util/bindings.js';
|
|
2
3
|
import Viewables from '../util/viewables.js';
|
|
3
4
|
import { validateClick } from '../util/trackers.js';
|
|
4
5
|
|
|
5
6
|
export function makeTrackable(prototype) {
|
|
6
|
-
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
function _mixin(target, source) {
|
|
10
|
-
const descriptors = Object.getOwnPropertyDescriptors(source);
|
|
11
|
-
for (const key in descriptors) {
|
|
12
|
-
if (key === 'constructor') {
|
|
13
|
-
continue;
|
|
14
|
-
}
|
|
15
|
-
const { value } = descriptors[key];
|
|
16
|
-
if (target[key] === undefined && typeof value === 'function') {
|
|
17
|
-
target[key] = value;
|
|
18
|
-
}
|
|
19
|
-
// TODO: getter, setter
|
|
20
|
-
}
|
|
7
|
+
mixin(prototype, TrackableMixin.prototype);
|
|
21
8
|
}
|
|
22
9
|
|
|
23
10
|
export class TrackableMixin {
|
package/src/plugin.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Component, Resolution, defineAndUpgrade, delegateGetters, defineValues } from '@miso.ai/commons';
|
|
2
|
-
import { Asks, Explore, Search, Recommendations } from './workflow/index.js';
|
|
2
|
+
import { Asks, HybridSearch, Explore, Search, Recommendations } from './workflow/index.js';
|
|
3
3
|
import { AskCombo } from './combo/index.js';
|
|
4
4
|
import Layouts from './layouts.js';
|
|
5
5
|
import * as elements from './element/index.js';
|
|
6
6
|
import * as layouts from './layout/index.js';
|
|
7
|
-
import * as sources from './source
|
|
7
|
+
import * as sources from './source.js';
|
|
8
8
|
import * as defaults from './defaults/index.js';
|
|
9
9
|
import { loadStylesIfNecessary } from './styles.js';
|
|
10
10
|
|
|
@@ -176,6 +176,10 @@ class Ui {
|
|
|
176
176
|
return this.asks.root;
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
+
get hybridSearch() {
|
|
180
|
+
return this._hybridSearch || (this._hybridSearch = new HybridSearch(this._plugin, this._client));
|
|
181
|
+
}
|
|
182
|
+
|
|
179
183
|
get explore() {
|
|
180
184
|
return this._explore || (this._explore = new Explore(this._plugin, this._client));
|
|
181
185
|
}
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { API } from '@miso.ai/commons';
|
|
2
2
|
|
|
3
|
-
//export { default as api } from './api.js';
|
|
4
|
-
|
|
5
3
|
export function api(client) {
|
|
6
4
|
// TODO: send uuid & unit id
|
|
7
5
|
return async ({ group, name, payload, options }) => {
|
|
8
|
-
if (group === API.GROUP.ASK
|
|
9
|
-
|
|
6
|
+
if (group === API.GROUP.ASK) {
|
|
7
|
+
switch (name) {
|
|
8
|
+
case API.NAME.QUESTIONS:
|
|
9
|
+
return client.api.ask.questions(payload, options);
|
|
10
|
+
case API.NAME.SEARCH:
|
|
11
|
+
return client.api.ask.search(payload, options);
|
|
12
|
+
}
|
|
10
13
|
}
|
|
11
14
|
// because name is in snake case
|
|
12
15
|
return client.api[group]._run(name, payload, options);
|
package/src/util/bindings.js
CHANGED
|
@@ -41,6 +41,10 @@ export default class Bindings {
|
|
|
41
41
|
return this._e2b.get(element) || undefined;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
find(key) {
|
|
45
|
+
return this._k2b.get(key) || undefined;
|
|
46
|
+
}
|
|
47
|
+
|
|
44
48
|
update(keys, values, elements) {
|
|
45
49
|
const newTuple = tuple(keys, values, elements);
|
|
46
50
|
const oldTuple = { k2b: this._k2b, e2b: this._e2b, entries: this._entries };
|
package/src/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export default '1.
|
|
1
|
+
export default '1.11.0-beta.0';
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import { trimObj, API } from '@miso.ai/commons';
|
|
2
|
+
import Workflow from './base.js';
|
|
3
|
+
import { mergeApiOptions } from './options.js';
|
|
4
|
+
// import * as sources from '../source.js';
|
|
5
|
+
import { fields, FeedbackActor, AutocompleteActor } from '../actor/index.js';
|
|
6
|
+
import { ROLE, STATUS, ORGANIC_QUESTION_SOURCE, DATA_ASPECT } from '../constants.js';
|
|
7
|
+
import { SearchBoxLayout, TextLayout, ListLayout, TypewriterLayout, FeedbackLayout, AffiliationLayout } from '../layout/index.js';
|
|
8
|
+
import { processData as processAffiliationData } from '../affiliation/index.js';
|
|
9
|
+
import { writeAnswerStageToMeta } from './processors.js';
|
|
10
|
+
|
|
11
|
+
const DEFAULT_API_OPTIONS = Object.freeze({
|
|
12
|
+
group: API.GROUP.ASK,
|
|
13
|
+
payload: {
|
|
14
|
+
source_fl: ['cover_image', 'url', 'created_at', 'updated_at', 'published_at'],
|
|
15
|
+
cite_link: 1,
|
|
16
|
+
cite_start: '[',
|
|
17
|
+
cite_end: ']',
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const DEFAULT_AUTOCOMPLETE_OPTIONS = Object.freeze({
|
|
22
|
+
actor: false,
|
|
23
|
+
api: {
|
|
24
|
+
group: API.GROUP.ASK,
|
|
25
|
+
name: API.NAME.AUTOCOMPLETE,
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const DEFAULT_LAYOUTS = Object.freeze({
|
|
30
|
+
[ROLE.QUERY]: [SearchBoxLayout.type, { placeholder: 'Ask a question' }],
|
|
31
|
+
[ROLE.QUESTION]: [TextLayout.type, { tag: 'h2' }],
|
|
32
|
+
[ROLE.ANSWER]: TypewriterLayout.type,
|
|
33
|
+
[ROLE.FEEDBACK]: FeedbackLayout.type,
|
|
34
|
+
[ROLE.SOURCES]: [ListLayout.type, { incremental: true, itemType: 'article', templates: { ordered: true } }],
|
|
35
|
+
[ROLE.AFFILIATION]: [AffiliationLayout.type, { incremental: true, itemType: 'affiliation', link: { rel: 'noopener nofollow' } }],
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const DEFAULT_TRACKERS = Object.freeze({
|
|
39
|
+
[ROLE.SOURCES]: {},
|
|
40
|
+
[ROLE.AFFILIATION]: {},
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const DEFAULT_OPTIONS = Object.freeze({
|
|
44
|
+
api: DEFAULT_API_OPTIONS,
|
|
45
|
+
autocomplete: DEFAULT_AUTOCOMPLETE_OPTIONS,
|
|
46
|
+
layouts: DEFAULT_LAYOUTS,
|
|
47
|
+
trackers: DEFAULT_TRACKERS,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
export default class AnswerBasedWorkflow extends Workflow {
|
|
51
|
+
|
|
52
|
+
// constructor //
|
|
53
|
+
_initProperties(args) {
|
|
54
|
+
super._initProperties(args);
|
|
55
|
+
this._questionId = undefined;
|
|
56
|
+
this._autoQuery = false;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
_initActors(args) {
|
|
60
|
+
super._initActors(args);
|
|
61
|
+
this._feedback = new FeedbackActor(this._hub);
|
|
62
|
+
/*
|
|
63
|
+
this._autocomplete = new AutocompleteActor(this._hub, {
|
|
64
|
+
source: sources.api(this._client),
|
|
65
|
+
options: this._options,
|
|
66
|
+
});
|
|
67
|
+
*/
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
_initSubscriptions(args) {
|
|
71
|
+
super._initSubscriptions(args);
|
|
72
|
+
this._unsubscribes = [
|
|
73
|
+
...this._unsubscribes,
|
|
74
|
+
this._hub.on(fields.query(), args => this._query(args)),
|
|
75
|
+
this._hub.on(fields.view(ROLE.ANSWER), data => this._onAnswerViewUpdate(data)),
|
|
76
|
+
this._views.get(ROLE.ANSWER).on('citation-click', event => this._onCitationClick(event)),
|
|
77
|
+
];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// lifecycle //
|
|
81
|
+
restart() {
|
|
82
|
+
// clear question id from previous session
|
|
83
|
+
if (this._questionId) {
|
|
84
|
+
this._questionId = undefined;
|
|
85
|
+
}
|
|
86
|
+
// interrupt event
|
|
87
|
+
// TODO: move to workflow base
|
|
88
|
+
const { session, status, ongoing } = this._hub.states[fields.view(ROLE.ANSWER)] || {};
|
|
89
|
+
if (session && status !== STATUS.INITIAL && (status !== STATUS.READY || ongoing)) {
|
|
90
|
+
// it's interrupted by a new question
|
|
91
|
+
const state = Object.freeze({ session, status, ongoing });
|
|
92
|
+
this._emit('interrupt', state);
|
|
93
|
+
this._emit('finally', state);
|
|
94
|
+
}
|
|
95
|
+
// restart
|
|
96
|
+
super.restart();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// properties //
|
|
100
|
+
get questionId() {
|
|
101
|
+
return this._questionId;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// query //
|
|
105
|
+
autoQuery(options = {}) {
|
|
106
|
+
const { setValue = true, focus = true } = this._autoQuery = options;
|
|
107
|
+
const searchParams = new URLSearchParams(window.location.search);
|
|
108
|
+
const q = searchParams.get('q');
|
|
109
|
+
const qs = searchParams.get('qs');
|
|
110
|
+
const { layout } = this._views.get(ROLE.QUERY);
|
|
111
|
+
if (layout) {
|
|
112
|
+
if (q && setValue) {
|
|
113
|
+
layout.value = q;
|
|
114
|
+
}
|
|
115
|
+
if (!q && focus) {
|
|
116
|
+
layout.focus();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (q) {
|
|
120
|
+
this.query({ q, qs });
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
query(args) {
|
|
125
|
+
// TODO: also accept "question", "questionSource"?
|
|
126
|
+
if (!args.q) {
|
|
127
|
+
throw new Error(`q is required in query() call`);
|
|
128
|
+
}
|
|
129
|
+
this._hub.update(fields.query(), args);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
_query(args = {}) {
|
|
133
|
+
// start a new session
|
|
134
|
+
this.restart();
|
|
135
|
+
|
|
136
|
+
// keep track of question source on this session
|
|
137
|
+
const { session } = this;
|
|
138
|
+
session.meta.question_source = args.questionSource || ORGANIC_QUESTION_SOURCE; // might be null, not undefined
|
|
139
|
+
|
|
140
|
+
// build payload and trigger request
|
|
141
|
+
const payload = this._buildPayload(args);
|
|
142
|
+
const event = mergeApiOptions(this._options.resolved.api, { payload, session });
|
|
143
|
+
this._request(event);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
_request(event) {
|
|
147
|
+
this._hub.update(fields.request(), event);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
_buildPayload() {
|
|
151
|
+
throw new Error(`Not implemented`);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// autocomplete //
|
|
155
|
+
/*
|
|
156
|
+
updateCompletions(event) {
|
|
157
|
+
// TODO: verify
|
|
158
|
+
this._hub.update(fields.data(DATA_ASPECT.AUTOCOMPLETE), {
|
|
159
|
+
...event,
|
|
160
|
+
source: 'manual',
|
|
161
|
+
});
|
|
162
|
+
return this;
|
|
163
|
+
}
|
|
164
|
+
*/
|
|
165
|
+
|
|
166
|
+
// data //
|
|
167
|
+
_defaultProcessData(data) {
|
|
168
|
+
data = super._defaultProcessData(data);
|
|
169
|
+
|
|
170
|
+
// capture question ID and register at context
|
|
171
|
+
this._writeQuestionIdFromData(data);
|
|
172
|
+
|
|
173
|
+
// update URL if autoQuery.updateUrl is not false
|
|
174
|
+
this._updateUrlIfNecessary(data);
|
|
175
|
+
|
|
176
|
+
// affiliation
|
|
177
|
+
data = processAffiliationData(data);
|
|
178
|
+
|
|
179
|
+
return writeAnswerStageToMeta(data);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
_writeQuestionIdFromData({ value }) {
|
|
183
|
+
if (!value) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
const { question_id: questionId } = value;
|
|
187
|
+
this._writeQuestionId(questionId);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
_updateUrlIfNecessary({ value, request }) {
|
|
191
|
+
if (!value || !this._autoQuery || this._autoQuery.updateUrl === false) { // explicitly false
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
const { question } = value;
|
|
195
|
+
const organic = request && request.payload && request.payload._meta && request.payload._meta.question_source === ORGANIC_QUESTION_SOURCE;
|
|
196
|
+
|
|
197
|
+
const url = new URL(window.location);
|
|
198
|
+
const currentQuestion = url.searchParams.get('q');
|
|
199
|
+
// TODO: review this, handle &qs=
|
|
200
|
+
if (question !== currentQuestion && (!organic || this._autoQuery.updateUrl === true)) {
|
|
201
|
+
url.searchParams.set('q', question);
|
|
202
|
+
window.history.replaceState({}, '', url);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// interactions //
|
|
207
|
+
_preprocessInteraction(payload) {
|
|
208
|
+
payload = super._preprocessInteraction(payload) || {};
|
|
209
|
+
const { context = {} } = payload;
|
|
210
|
+
const { custom_context = {} } = context;
|
|
211
|
+
let { questionId } = this;
|
|
212
|
+
return {
|
|
213
|
+
...payload,
|
|
214
|
+
context: {
|
|
215
|
+
...context,
|
|
216
|
+
custom_context: trimObj({
|
|
217
|
+
question_id: questionId,
|
|
218
|
+
...custom_context,
|
|
219
|
+
}),
|
|
220
|
+
},
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// handlers //
|
|
225
|
+
_onAnswerViewUpdate({ session, status, ongoing }) {
|
|
226
|
+
if (status === STATUS.INITIAL) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
const done = status === STATUS.READY && !ongoing;
|
|
230
|
+
const erroneous = status === STATUS.ERRONEOUS;
|
|
231
|
+
const eventName = done ? 'done' : erroneous ? 'error' : status;
|
|
232
|
+
const state = Object.freeze({ session, status, ongoing });
|
|
233
|
+
this._emit(eventName, state);
|
|
234
|
+
if (done || erroneous) {
|
|
235
|
+
this._emit('finally', state);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
_onCitationClick({ index }) {
|
|
240
|
+
const { value } = this.states[fields.data()] || {};
|
|
241
|
+
const { sources } = value || {};
|
|
242
|
+
// index is 1-based
|
|
243
|
+
const source = sources && sources[index - 1];
|
|
244
|
+
if (!source || !source.product_id) {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
// TODO: should we track impression as well?
|
|
248
|
+
this._trackers.sources.click([source.product_id], { manual: false });
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// helpers //
|
|
252
|
+
_writeQuestionId(questionId) {
|
|
253
|
+
if (!this._questionId && questionId) {
|
|
254
|
+
this._questionId = questionId;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
_clearQuestionId() {
|
|
259
|
+
// clear question id from previous session
|
|
260
|
+
this._questionId = undefined;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// destroy //
|
|
264
|
+
_destroy(options) {
|
|
265
|
+
this._feedback._destroy();
|
|
266
|
+
// this._autocomplete._destroy();
|
|
267
|
+
super._destroy(options);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// makeConfigurable(AnswerBasedWorkflow.prototype, ['autocomplete']);
|
|
273
|
+
|
|
274
|
+
Object.assign(AnswerBasedWorkflow, {
|
|
275
|
+
DEFAULT_API_OPTIONS,
|
|
276
|
+
DEFAULT_AUTOCOMPLETE_OPTIONS,
|
|
277
|
+
DEFAULT_LAYOUTS,
|
|
278
|
+
DEFAULT_TRACKERS,
|
|
279
|
+
DEFAULT_OPTIONS,
|
|
280
|
+
});
|