@miso.ai/client-sdk-ui 1.11.5-beta.0 → 1.11.5-beta.10
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 +4 -2
- package/dist/cjs/actor/view.js +3 -2
- package/dist/cjs/asset/svgs.js +5 -2
- package/dist/cjs/constants.js +2 -1
- package/dist/cjs/defaults/hybrid-search/answer-box.js +13 -2
- package/dist/cjs/defaults/hybrid-search/controls.js +2 -1
- package/dist/cjs/defaults/hybrid-search/templates.js +9 -0
- package/dist/cjs/layout/affiliation/affiliation.js +6 -7
- package/dist/cjs/layout/container/container.js +5 -1
- package/dist/cjs/layout/index.js +7 -0
- package/dist/cjs/layout/input/option-list.js +2 -1
- package/dist/cjs/layout/input/search-box.js +2 -1
- package/dist/cjs/layout/list/carousel.js +2 -2
- package/dist/cjs/layout/list/collection.js +7 -4
- package/dist/cjs/layout/list/gallery.js +270 -0
- package/dist/cjs/layout/mixin/swipeable.js +14 -15
- package/dist/cjs/layout/raf.js +2 -0
- package/dist/cjs/layout/templates.js +19 -8
- package/dist/cjs/util/dom.js +101 -0
- package/dist/cjs/version.js +1 -1
- package/dist/cjs/workflow/answer-based.js +49 -77
- package/dist/cjs/workflow/ask.js +5 -3
- package/dist/cjs/workflow/base.js +4 -2
- package/dist/cjs/workflow/explore.js +17 -7
- package/dist/cjs/workflow/hybrid-search-answer.js +13 -0
- package/dist/cjs/workflow/hybrid-search-results.js +16 -0
- package/dist/cjs/workflow/hybrid-search.js +3 -8
- package/dist/cjs/workflow/options/auto-query.js +62 -0
- package/dist/cjs/workflow/options/index.js +11 -0
- package/dist/cjs/workflow/processors.js +73 -0
- package/dist/cjs/workflow/search-based.js +4 -0
- package/dist/cjs/workflow/search.js +0 -25
- package/dist/cjs/workflow/use-link.js +9 -2
- package/package.json +2 -2
- package/src/actor/data.js +4 -2
- package/src/actor/view.js +3 -2
- package/src/asset/svgs.js +5 -1
- package/src/constants.js +1 -0
- package/src/defaults/hybrid-search/answer-box.js +9 -2
- package/src/defaults/hybrid-search/controls.js +2 -2
- package/src/defaults/hybrid-search/templates.js +9 -0
- package/src/layout/affiliation/affiliation.js +5 -7
- package/src/layout/container/container.js +4 -1
- package/src/layout/index.js +1 -0
- package/src/layout/input/option-list.js +2 -1
- package/src/layout/input/search-box.js +2 -1
- package/src/layout/list/carousel.js +3 -3
- package/src/layout/list/collection.js +6 -4
- package/src/layout/list/gallery.js +229 -0
- package/src/layout/mixin/swipeable.js +12 -16
- package/src/layout/raf.js +1 -0
- package/src/layout/templates.js +15 -3
- package/src/util/dom.js +103 -0
- package/src/version.js +1 -1
- package/src/workflow/answer-based.js +42 -42
- package/src/workflow/ask.js +6 -4
- package/src/workflow/base.js +4 -2
- package/src/workflow/explore.js +15 -4
- package/src/workflow/hybrid-search-answer.js +15 -1
- package/src/workflow/hybrid-search-results.js +17 -0
- package/src/workflow/hybrid-search.js +3 -6
- package/src/workflow/options/auto-query.js +29 -0
- package/src/workflow/options/index.js +1 -0
- package/src/workflow/processors.js +60 -0
- package/src/workflow/search-based.js +5 -1
- package/src/workflow/search.js +0 -17
- package/src/workflow/use-link.js +2 -2
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { trimObj } from '@miso.ai/commons';
|
|
2
|
+
import CollectionLayout from './collection.js';
|
|
3
|
+
import { addOrRemoveClass, resolveCssLength, cssAspectRatio } from '../../util/dom.js';
|
|
4
|
+
|
|
5
|
+
function calculateItemSizes(itemCount, columnCount) {
|
|
6
|
+
const totalSpaces = columnCount * 2;
|
|
7
|
+
let remainingSpaces = Math.max(totalSpaces - itemCount, 0);
|
|
8
|
+
|
|
9
|
+
let small = itemCount;
|
|
10
|
+
// turning a small block into a large block takes 3 extra spaces
|
|
11
|
+
const large = Math.min(Math.floor(remainingSpaces / 3), small);
|
|
12
|
+
small -= large;
|
|
13
|
+
remainingSpaces -= large * 3;
|
|
14
|
+
// turning a small block into a medium block takes 1 extra space
|
|
15
|
+
const medium = Math.min(remainingSpaces, small);
|
|
16
|
+
small -= medium;
|
|
17
|
+
remainingSpaces -= medium;
|
|
18
|
+
|
|
19
|
+
return [large, medium, small];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function item(layout, state, value, index) {
|
|
23
|
+
const { className, templates, options } = layout;
|
|
24
|
+
const { itemType } = options;
|
|
25
|
+
const body = templates[itemType](layout, state, value, { index });
|
|
26
|
+
const sizeClass = value._size ? ` ${className}__item-${value._size}` : '';
|
|
27
|
+
return `<li class="${className}__item${sizeClass}">${body}</li>`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const DEFAULT_HEIGHT = '16rem';
|
|
31
|
+
|
|
32
|
+
const DEFAULT_TEMPLATES = Object.freeze({
|
|
33
|
+
...CollectionLayout.defaultTemplates,
|
|
34
|
+
item,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const TYPE = 'gallery';
|
|
38
|
+
const DEFAULT_CLASSNAME = 'miso-gallery';
|
|
39
|
+
|
|
40
|
+
export default class GalleryLayout extends CollectionLayout {
|
|
41
|
+
|
|
42
|
+
static get type() {
|
|
43
|
+
return TYPE;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static get defaultTemplates() {
|
|
47
|
+
return DEFAULT_TEMPLATES;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
static get defaultClassName() {
|
|
51
|
+
return DEFAULT_CLASSNAME;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
constructor({ className = DEFAULT_CLASSNAME, templates, ...options } = {}) {
|
|
55
|
+
super({
|
|
56
|
+
className,
|
|
57
|
+
templates: { ...DEFAULT_TEMPLATES, ...templates },
|
|
58
|
+
...options,
|
|
59
|
+
});
|
|
60
|
+
this._galleryContext = {
|
|
61
|
+
itemAspectRatio: undefined,
|
|
62
|
+
itemCount: undefined,
|
|
63
|
+
columnCount: undefined,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
syncSize() {
|
|
68
|
+
const { element } = this._view;
|
|
69
|
+
const listElement = element && this._getListElement(element);
|
|
70
|
+
if (!listElement) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
this._syncListAspectRatio(listElement);
|
|
74
|
+
this._syncItemSizes(listElement);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
_preprocess({ state, rendered }) {
|
|
78
|
+
const incremental = this._shallRenderIncrementally(state, rendered);
|
|
79
|
+
const bodyHtml = incremental ? undefined : this._bodyHtml(state);
|
|
80
|
+
const itemsHtml = this._itemsHtml(state, rendered);
|
|
81
|
+
return {
|
|
82
|
+
...state,
|
|
83
|
+
...trimObj({
|
|
84
|
+
incremental,
|
|
85
|
+
bodyHtml,
|
|
86
|
+
itemsHtml,
|
|
87
|
+
}),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
_bodyHtml(state) {
|
|
92
|
+
return this.templates.root(this, { ...state, value: [] });
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
_itemsHtml(state, rendered) {
|
|
96
|
+
const offset = (rendered && rendered.value && rendered.value.length) || 0;
|
|
97
|
+
const values = (this._getItems(state) || []).slice(offset);
|
|
98
|
+
return values.length > 0 ? this.templates.items(this, state, values, { offset }) : '';
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
_render(element, states, controls) {
|
|
102
|
+
this._syncItemCount(states);
|
|
103
|
+
this._syncCssVariables();
|
|
104
|
+
const listElement = this._renderListElement(element, states, controls);
|
|
105
|
+
if (!listElement) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
this._syncListAspectRatio(listElement);
|
|
109
|
+
this._renderItems(listElement, states, controls);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
_renderListElement(element, { state }) {
|
|
113
|
+
const { incremental, bodyHtml } = state;
|
|
114
|
+
if (!incremental) {
|
|
115
|
+
element.innerHTML = bodyHtml;
|
|
116
|
+
}
|
|
117
|
+
return this._getListElement(element);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
_renderItems(listElement, states) {
|
|
121
|
+
if (states.state.incremental) {
|
|
122
|
+
this._appendItems(listElement, states);
|
|
123
|
+
} else {
|
|
124
|
+
this._updateItems(listElement, states);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
_updateItems(listElement, { state }) {
|
|
129
|
+
const { itemSizes } = this._galleryContext;
|
|
130
|
+
const values = this._writeItemSizes(this._getItems(state) || [], itemSizes);
|
|
131
|
+
listElement.innerHTML = this.templates.items(this, state, values);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
_appendItems(listElement, { state }) {
|
|
135
|
+
const { itemSizes } = this._galleryContext;
|
|
136
|
+
// update existing items
|
|
137
|
+
this._syncItemSizes(listElement);
|
|
138
|
+
// append new items
|
|
139
|
+
const offset = listElement.children.length;
|
|
140
|
+
const values = this._writeItemSizes((this._getItems(state) || []).slice(offset), itemSizes, offset);
|
|
141
|
+
listElement.insertAdjacentHTML('beforeend', this.templates.items(this, state, values, { offset }));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
_writeItemSizes(values, [large = 0, medium = 0] = [], offset = 0) {
|
|
145
|
+
let omitted = 0;
|
|
146
|
+
if (offset > 0) {
|
|
147
|
+
omitted = Math.min(offset, large);
|
|
148
|
+
offset -= omitted;
|
|
149
|
+
large -= omitted;
|
|
150
|
+
}
|
|
151
|
+
if (offset > 0) {
|
|
152
|
+
omitted = Math.min(offset, medium);
|
|
153
|
+
offset -= omitted;
|
|
154
|
+
medium -= omitted;
|
|
155
|
+
}
|
|
156
|
+
const results = [];
|
|
157
|
+
for (const value of values) {
|
|
158
|
+
let _size = 'small';
|
|
159
|
+
if (large > 0) {
|
|
160
|
+
_size = 'large';
|
|
161
|
+
large--;
|
|
162
|
+
} else if (medium > 0) {
|
|
163
|
+
_size = 'medium';
|
|
164
|
+
medium--;
|
|
165
|
+
}
|
|
166
|
+
results.push({ ...value, _size });
|
|
167
|
+
}
|
|
168
|
+
return results;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
_syncCssVariables() {
|
|
172
|
+
const context = this._galleryContext;
|
|
173
|
+
let { itemAspectRatio = 1 } = this.options;
|
|
174
|
+
itemAspectRatio = context.itemAspectRatio = cssAspectRatio(itemAspectRatio);
|
|
175
|
+
const { element } = this._view;
|
|
176
|
+
if (!element) {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
// TODO: skip if equal to default values
|
|
180
|
+
element.style.setProperty('--miso-gallery-item-1x1-aspect-ratio', `${itemAspectRatio}`);
|
|
181
|
+
element.style.setProperty('--miso-gallery-item-1x2-aspect-ratio', `${itemAspectRatio.scaleBy([1, 2])}`);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
_syncListAspectRatio(listElement) {
|
|
185
|
+
const context = this._galleryContext;
|
|
186
|
+
const { itemCount, itemAspectRatio } = context;
|
|
187
|
+
const { height = DEFAULT_HEIGHT } = this.options;
|
|
188
|
+
const resolvedHeight = resolveCssLength(height);
|
|
189
|
+
const width = listElement.clientWidth;
|
|
190
|
+
if (width === 0) {
|
|
191
|
+
context.columnCount = undefined;
|
|
192
|
+
context.itemSizes = undefined;
|
|
193
|
+
return; // probably not visible yet
|
|
194
|
+
}
|
|
195
|
+
const columnCount = context.columnCount = Math.round((width * itemAspectRatio.width * 2) / (resolvedHeight * itemAspectRatio.height));
|
|
196
|
+
context.itemSizes = calculateItemSizes(itemCount, columnCount);
|
|
197
|
+
|
|
198
|
+
// update DOM
|
|
199
|
+
listElement.style.aspectRatio = `${columnCount} / 2`;
|
|
200
|
+
listElement.style.height = itemCount > 0 ? 'auto' : '0';
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
_syncItemCount({ state } = {}) {
|
|
204
|
+
const items = this._getItems(state);
|
|
205
|
+
this._galleryContext.itemCount = items ? items.length : undefined;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
_syncItemSizes(listElement) {
|
|
209
|
+
const { itemSizes = [] } = this._galleryContext;
|
|
210
|
+
let [large = 0, medium = 0] = itemSizes;
|
|
211
|
+
const { className } = this;
|
|
212
|
+
|
|
213
|
+
// update DOM
|
|
214
|
+
for (const itemElement of listElement.children) {
|
|
215
|
+
let lg = false, md = false;
|
|
216
|
+
if (large > 0) {
|
|
217
|
+
lg = true;
|
|
218
|
+
large--;
|
|
219
|
+
} else if (medium > 0) {
|
|
220
|
+
md = true;
|
|
221
|
+
medium--;
|
|
222
|
+
}
|
|
223
|
+
addOrRemoveClass(itemElement, `${className}__item-large`, lg);
|
|
224
|
+
addOrRemoveClass(itemElement, `${className}__item-medium`, md);
|
|
225
|
+
addOrRemoveClass(itemElement, `${className}__item-small`, !lg && !md);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { mixin, requestAnimationFrame as raf } from '@miso.ai/commons';
|
|
2
|
+
import { setOrRemoveAttribute, addOrRemoveClass } from '../../util/dom.js';
|
|
2
3
|
|
|
3
4
|
export function makeSwipeable(prototype) {
|
|
4
5
|
mixin(prototype, SwipeableMixin.prototype);
|
|
@@ -33,7 +34,7 @@ export class SwipeableMixin {
|
|
|
33
34
|
index = 0;
|
|
34
35
|
}
|
|
35
36
|
context.itemIndex = index;
|
|
36
|
-
raf(() => this.
|
|
37
|
+
raf(() => this._syncDisplayedSwipeableItemIndex());
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
next() {
|
|
@@ -52,7 +53,8 @@ export class SwipeableMixin {
|
|
|
52
53
|
|
|
53
54
|
_renderSwipeable(element, states, controls) {
|
|
54
55
|
this._syncSwipeableItemCount(states);
|
|
55
|
-
this.
|
|
56
|
+
this._syncDisplayedSwipeableItemCount();
|
|
57
|
+
this._syncDisplayedSwipeableItemIndex();
|
|
56
58
|
}
|
|
57
59
|
|
|
58
60
|
_afterRenderSwipeable() {
|
|
@@ -74,8 +76,6 @@ export class SwipeableMixin {
|
|
|
74
76
|
} else if (oldItemCount === undefined) {
|
|
75
77
|
context.itemIndex = 0;
|
|
76
78
|
}
|
|
77
|
-
|
|
78
|
-
this._syncDisplayedSwipeableItemCount();
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
_syncDisplayedSwipeableItemCount() {
|
|
@@ -89,24 +89,20 @@ export class SwipeableMixin {
|
|
|
89
89
|
if (!element) {
|
|
90
90
|
return;
|
|
91
91
|
}
|
|
92
|
+
setOrRemoveAttribute(element, 'data-item-count', itemCount);
|
|
92
93
|
context.displayedCount = itemCount;
|
|
93
|
-
if (itemCount === undefined) {
|
|
94
|
-
element.removeAttribute('data-item-count');
|
|
95
|
-
} else {
|
|
96
|
-
element.setAttribute('data-item-count', itemCount);
|
|
97
|
-
}
|
|
98
94
|
}
|
|
99
95
|
|
|
100
|
-
|
|
96
|
+
_syncDisplayedSwipeableItemIndex() {
|
|
101
97
|
const context = this._swipeableContext;
|
|
102
|
-
const
|
|
103
|
-
if (
|
|
98
|
+
const { itemIndex } = context;
|
|
99
|
+
if (itemIndex === context.displayedIndex) {
|
|
104
100
|
return;
|
|
105
101
|
}
|
|
106
|
-
context.displayedIndex =
|
|
102
|
+
context.displayedIndex = itemIndex;
|
|
107
103
|
|
|
108
|
-
this._syncSwipeableListIndex(
|
|
109
|
-
this._syncSwipeableIndicatorIndex(
|
|
104
|
+
this._syncSwipeableListIndex(itemIndex);
|
|
105
|
+
this._syncSwipeableIndicatorIndex(itemIndex);
|
|
110
106
|
}
|
|
111
107
|
|
|
112
108
|
_syncSwipeableListIndex(index) {
|
|
@@ -126,7 +122,7 @@ export class SwipeableMixin {
|
|
|
126
122
|
}
|
|
127
123
|
let i = 0;
|
|
128
124
|
for (const item of indicatorElement.children) {
|
|
129
|
-
item
|
|
125
|
+
addOrRemoveClass(item, 'active', i === index);
|
|
130
126
|
i++;
|
|
131
127
|
}
|
|
132
128
|
}
|
package/src/layout/raf.js
CHANGED
package/src/layout/templates.js
CHANGED
|
@@ -187,15 +187,27 @@ export function cta(layout, data, meta) {
|
|
|
187
187
|
|
|
188
188
|
// helpers //
|
|
189
189
|
function tagPair({ className, options = {} }, { product_id, url }, { classSuffix = 'item-body', role = 'item', ...meta } = {}) {
|
|
190
|
-
const { link = {} } = options;
|
|
191
|
-
const { target = '_blank', rel = 'noopener' } = link; // TODO: other properties
|
|
192
190
|
const tag = meta.link !== false && url ? 'a' : 'div';
|
|
193
|
-
const urlAttrs = meta.link !== false && url ? `href="${url}"
|
|
191
|
+
const urlAttrs = meta.link !== false && url ? `href="${url}" ${linkAttrs(options.link)}` : '';
|
|
194
192
|
const productAttrs = product_id ? `${ATTR_DATA_MISO_PRODUCT_ID}="${product_id}"` : '';
|
|
195
193
|
const roleAttrs = role ? `data-role="${role}"` : '';
|
|
196
194
|
return [`<${tag} class="${className}__${classSuffix}" ${roleAttrs} ${productAttrs} ${urlAttrs}>`, `</${tag}>`];
|
|
197
195
|
}
|
|
198
196
|
|
|
197
|
+
function linkAttrs(link = {}) {
|
|
198
|
+
const { target = '_blank', rel = 'noopener', open = true } = link; // TODO: other properties
|
|
199
|
+
let attrs = '';
|
|
200
|
+
if (open) {
|
|
201
|
+
if (target) {
|
|
202
|
+
attrs += ` target="${target}"`;
|
|
203
|
+
}
|
|
204
|
+
if (rel) {
|
|
205
|
+
attrs += ` rel="${rel}"`;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return attrs;
|
|
209
|
+
}
|
|
210
|
+
|
|
199
211
|
const DEFAULT_DATE_OPTIONS = Object.freeze({ locale: 'en-US', year: 'numeric', month: 'short', day: 'numeric' });
|
|
200
212
|
const DEFAULT_NUMBER_OPTIONS = 'en-US';
|
|
201
213
|
|
package/src/util/dom.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
export function setOrRemoveAttribute(element, name, value) {
|
|
2
|
+
if (value === undefined) {
|
|
3
|
+
element.removeAttribute(name);
|
|
4
|
+
} else {
|
|
5
|
+
element.setAttribute(name, value);
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function addOrRemoveClass(element, name, add) {
|
|
10
|
+
if (add) {
|
|
11
|
+
element.classList.add(name);
|
|
12
|
+
} else {
|
|
13
|
+
element.classList.remove(name);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function resolveCssLength(str) {
|
|
18
|
+
if (typeof str === 'number') {
|
|
19
|
+
return str;
|
|
20
|
+
}
|
|
21
|
+
const [value, unit] = parseLength(str);
|
|
22
|
+
switch (unit) {
|
|
23
|
+
case '':
|
|
24
|
+
case 'px':
|
|
25
|
+
return value;
|
|
26
|
+
case 'rem':
|
|
27
|
+
return remToPx(value);
|
|
28
|
+
default:
|
|
29
|
+
throw new Error(`Unsupported unit: ${unit}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function parseLength(str) {
|
|
34
|
+
const match = str.match(/^([+-]?(?:\d+|\d*\.\d+))([a-z]*)$/);
|
|
35
|
+
if (!match) {
|
|
36
|
+
throw new Error(`Invalid length: ${str}`);
|
|
37
|
+
}
|
|
38
|
+
const value = parseFloat(match[1]);
|
|
39
|
+
const unit = match[2] || '';
|
|
40
|
+
return [value, unit];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function remToPx(value = 1) {
|
|
44
|
+
return value * parseFloat(getComputedStyle(document.documentElement).fontSize);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function cssAspectRatio(value) {
|
|
48
|
+
return (value === undefined || value instanceof CssAspectRatio) ? value : new CssAspectRatio(value);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
class CssAspectRatio {
|
|
52
|
+
|
|
53
|
+
constructor(value) {
|
|
54
|
+
this._width = NaN;
|
|
55
|
+
this._height = NaN;
|
|
56
|
+
switch (typeof value) {
|
|
57
|
+
case 'number':
|
|
58
|
+
this._width = value;
|
|
59
|
+
this._height = 1;
|
|
60
|
+
break;
|
|
61
|
+
case 'object':
|
|
62
|
+
if (Array.isArray(value)) {
|
|
63
|
+
this._width = parseFloat(value[0]);
|
|
64
|
+
this._height = parseFloat(value[1]);
|
|
65
|
+
}
|
|
66
|
+
break;
|
|
67
|
+
case 'string':
|
|
68
|
+
const i = value.indexOf('/');
|
|
69
|
+
if (i < 0) {
|
|
70
|
+
this._width = parseFloat(value);
|
|
71
|
+
this._height = 1;
|
|
72
|
+
} else {
|
|
73
|
+
this._width = parseFloat(value.slice(0, i).trim());
|
|
74
|
+
this._height = parseFloat(value.slice(i + 1).trim());
|
|
75
|
+
}
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
if (this._width === NaN || this._height === NaN) {
|
|
79
|
+
throw new Error(`Invalid aspect ratio value: ${value}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
get width() {
|
|
84
|
+
return this._width;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
get height() {
|
|
88
|
+
return this._height;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
get floatValue() {
|
|
92
|
+
return this._width / this._height;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
scaleBy(factor) {
|
|
96
|
+
factor = cssAspectRatio(factor);
|
|
97
|
+
return cssAspectRatio([factor._width * this._width, factor._height * this._height]);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
toString() {
|
|
101
|
+
return this._height === 1 ? `${this._width}` : `${this._width} / ${this._height}`;
|
|
102
|
+
}
|
|
103
|
+
}
|
package/src/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export default '1.11.5-beta.
|
|
1
|
+
export default '1.11.5-beta.10';
|
|
@@ -2,10 +2,10 @@ import { API } from '@miso.ai/commons';
|
|
|
2
2
|
import Workflow from './base.js';
|
|
3
3
|
import { fields, FeedbackActor } from '../actor/index.js';
|
|
4
4
|
import { ROLE, STATUS, ORGANIC_QUESTION_SOURCE } from '../constants.js';
|
|
5
|
-
import { SearchBoxLayout, TextLayout, ListLayout,
|
|
5
|
+
import { SearchBoxLayout, TextLayout, ListLayout, GalleryLayout, TypewriterLayout, FeedbackLayout, AffiliationLayout } from '../layout/index.js';
|
|
6
6
|
import { processData as processAffiliationData } from '../affiliation/index.js';
|
|
7
|
-
import { mergeRolesOptions, DEFAULT_TRACKER_OPTIONS } from './options/index.js';
|
|
8
|
-
import { writeAnswerStageToMeta, mergeInteraction } from './processors.js';
|
|
7
|
+
import { mergeRolesOptions, autoQuery as autoQueryFn, DEFAULT_AUTO_QUERY_PARAM, DEFAULT_TRACKER_OPTIONS } from './options/index.js';
|
|
8
|
+
import { writeAnswerStageToMeta, writeEventTargetToInteraction, mergeInteraction } from './processors.js';
|
|
9
9
|
import { enableUseLink } from './use-link.js';
|
|
10
10
|
import { isTracked, markAsTracked } from '../util/trackers.js';
|
|
11
11
|
|
|
@@ -35,7 +35,7 @@ const DEFAULT_LAYOUTS = Object.freeze({
|
|
|
35
35
|
[ROLE.QUESTION]: [TextLayout.type, { tag: 'h2' }],
|
|
36
36
|
[ROLE.ANSWER]: TypewriterLayout.type,
|
|
37
37
|
[ROLE.FEEDBACK]: FeedbackLayout.type,
|
|
38
|
-
[ROLE.IMAGES]: [
|
|
38
|
+
[ROLE.IMAGES]: [GalleryLayout.type, { incremental: true, itemType: 'image' }],
|
|
39
39
|
[ROLE.SOURCES]: [ListLayout.type, { incremental: true, itemType: 'article', templates: { ordered: true } }],
|
|
40
40
|
[ROLE.AFFILIATION]: [AffiliationLayout.type, { incremental: true, itemType: 'affiliation', link: { rel: 'noopener nofollow' } }],
|
|
41
41
|
});
|
|
@@ -67,10 +67,6 @@ const ROLES_OPTIONS = mergeRolesOptions(Workflow.ROLES_OPTIONS, {
|
|
|
67
67
|
members: Object.keys(DEFAULT_LAYOUTS),
|
|
68
68
|
});
|
|
69
69
|
|
|
70
|
-
function normalizeAutoQueryOptions({ setValue = true, focus = true, param = 'q', sourceParam = 'qs', ...options } = {}) {
|
|
71
|
-
return { setValue, focus, param, sourceParam, ...options };
|
|
72
|
-
}
|
|
73
|
-
|
|
74
70
|
export default class AnswerBasedWorkflow extends Workflow {
|
|
75
71
|
|
|
76
72
|
// constructor //
|
|
@@ -146,22 +142,7 @@ export default class AnswerBasedWorkflow extends Workflow {
|
|
|
146
142
|
|
|
147
143
|
// query //
|
|
148
144
|
autoQuery(options = {}) {
|
|
149
|
-
|
|
150
|
-
const searchParams = new URLSearchParams(window.location.search);
|
|
151
|
-
const q = searchParams.get(param);
|
|
152
|
-
const qs = searchParams.get(sourceParam);
|
|
153
|
-
const { layout } = this._views.get(ROLE.QUERY);
|
|
154
|
-
if (layout) {
|
|
155
|
-
if (q && setValue) {
|
|
156
|
-
layout.value = q;
|
|
157
|
-
}
|
|
158
|
-
if (!q && focus) {
|
|
159
|
-
layout.focus();
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
if (q) {
|
|
163
|
-
this.query({ q, qs });
|
|
164
|
-
}
|
|
145
|
+
autoQueryFn.call(this, options);
|
|
165
146
|
}
|
|
166
147
|
|
|
167
148
|
query(args) {
|
|
@@ -196,8 +177,21 @@ export default class AnswerBasedWorkflow extends Workflow {
|
|
|
196
177
|
throw new Error(`Not implemented`);
|
|
197
178
|
}
|
|
198
179
|
|
|
180
|
+
_writeWikiLinkTemplateToPayload(payload) {
|
|
181
|
+
if (!this._autoQuery || !this._autoQuery.param || this._autoQuery.param === DEFAULT_AUTO_QUERY_PARAM) {
|
|
182
|
+
return payload;
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
...payload,
|
|
186
|
+
wiki_link_template: `?${this._autoQuery.param}=%s`,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
199
190
|
// data //
|
|
200
191
|
_updateData(data) {
|
|
192
|
+
// capture question ID and register at context
|
|
193
|
+
this._writeQuestionIdFromData(data);
|
|
194
|
+
|
|
201
195
|
// if it's the head response, write question id and return
|
|
202
196
|
if (data.value && !data.value.answer_stage) {
|
|
203
197
|
this._handleHeadResponse(data);
|
|
@@ -209,10 +203,7 @@ export default class AnswerBasedWorkflow extends Workflow {
|
|
|
209
203
|
this._updateUrlIfNecessary(data);
|
|
210
204
|
}
|
|
211
205
|
|
|
212
|
-
_handleHeadResponse(data) {
|
|
213
|
-
// capture question ID and register at context
|
|
214
|
-
this._writeQuestionIdFromData(data);
|
|
215
|
-
}
|
|
206
|
+
_handleHeadResponse(data) {}
|
|
216
207
|
|
|
217
208
|
_defaultProcessData(data) {
|
|
218
209
|
data = super._defaultProcessData(data);
|
|
@@ -222,28 +213,34 @@ export default class AnswerBasedWorkflow extends Workflow {
|
|
|
222
213
|
}
|
|
223
214
|
|
|
224
215
|
_writeQuestionIdFromData({ value }) {
|
|
225
|
-
|
|
226
|
-
return;
|
|
227
|
-
}
|
|
228
|
-
const { question_id: questionId } = value;
|
|
229
|
-
this._writeQuestionId(questionId);
|
|
216
|
+
value && this._writeQuestionId(value.question_id);
|
|
230
217
|
}
|
|
231
218
|
|
|
232
219
|
_updateUrlIfNecessary({ value, request }) {
|
|
233
|
-
if (!
|
|
220
|
+
if (!this._autoQuery || !this._autoQuery.updateUrl) {
|
|
234
221
|
return;
|
|
235
222
|
}
|
|
236
|
-
const { param } = this._autoQuery;
|
|
237
|
-
const
|
|
238
|
-
const
|
|
223
|
+
const { param, sourceParam } = this._autoQuery;
|
|
224
|
+
const question = (value && value.question) || (request && request.payload && request.payload.question);
|
|
225
|
+
const questionSource = request && request.payload && request.payload._meta && request.payload._meta.question_source;
|
|
226
|
+
|
|
227
|
+
if (!question) {
|
|
228
|
+
return; // at initial phase
|
|
229
|
+
}
|
|
239
230
|
|
|
240
231
|
const url = new URL(window.location);
|
|
241
232
|
const currentQuestion = url.searchParams.get(param);
|
|
242
|
-
|
|
243
|
-
if (question
|
|
244
|
-
|
|
245
|
-
|
|
233
|
+
const currentQuestionSource = url.searchParams.get(sourceParam) || ORGANIC_QUESTION_SOURCE;
|
|
234
|
+
if (question === currentQuestion && questionSource === currentQuestionSource) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
url.searchParams.set(param, question);
|
|
238
|
+
if (questionSource === ORGANIC_QUESTION_SOURCE) {
|
|
239
|
+
url.searchParams.delete(sourceParam);
|
|
240
|
+
} else {
|
|
241
|
+
url.searchParams.set(sourceParam, questionSource);
|
|
246
242
|
}
|
|
243
|
+
window.history.replaceState({}, '', url);
|
|
247
244
|
}
|
|
248
245
|
|
|
249
246
|
// interactions //
|
|
@@ -251,6 +248,7 @@ export default class AnswerBasedWorkflow extends Workflow {
|
|
|
251
248
|
payload = super._defaultProcessInteraction(payload, args);
|
|
252
249
|
payload = this._writeAskPropertiesToInteraction(payload, args);
|
|
253
250
|
payload = this._writeAnswerClickInteraction(payload, args);
|
|
251
|
+
payload = writeEventTargetToInteraction(payload, args);
|
|
254
252
|
return payload;
|
|
255
253
|
}
|
|
256
254
|
|
|
@@ -304,7 +302,9 @@ export default class AnswerBasedWorkflow extends Workflow {
|
|
|
304
302
|
}
|
|
305
303
|
// TODO: should we track impression as well?
|
|
306
304
|
markAsTracked(event);
|
|
307
|
-
|
|
305
|
+
// distinguish from regular sources element click
|
|
306
|
+
// TODO
|
|
307
|
+
this._views.trackers.sources.click([source.product_id], { event_target: 'citation-link' });
|
|
308
308
|
}
|
|
309
309
|
|
|
310
310
|
_onAnswerLinkClick({ event, ...item }) {
|
package/src/workflow/ask.js
CHANGED
|
@@ -115,17 +115,19 @@ export default class Ask extends AnswerBasedWorkflow {
|
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
// query //
|
|
118
|
-
_buildPayload({ q, qs, ...
|
|
118
|
+
_buildPayload({ q, qs, ...options } = {}) {
|
|
119
119
|
const { parentQuestionId } = this;
|
|
120
|
-
|
|
121
|
-
...
|
|
120
|
+
let payload = trimObj({
|
|
121
|
+
...options,
|
|
122
122
|
question: q, // question, not q
|
|
123
123
|
parent_question_id: parentQuestionId,
|
|
124
124
|
_meta: {
|
|
125
|
-
...
|
|
125
|
+
...options._meta,
|
|
126
126
|
question_source: qs || ORGANIC_QUESTION_SOURCE, // might be null, not undefined
|
|
127
127
|
},
|
|
128
128
|
});
|
|
129
|
+
payload = this._writeWikiLinkTemplateToPayload(payload);
|
|
130
|
+
return payload;
|
|
129
131
|
}
|
|
130
132
|
|
|
131
133
|
// interactions //
|
package/src/workflow/base.js
CHANGED
|
@@ -10,14 +10,16 @@ import { getRevision, writeDataStatus, writeMisoIdToMeta, buildBaseInteraction,
|
|
|
10
10
|
function injectLogger(hub, callback) {
|
|
11
11
|
const { update, trigger } = hub;
|
|
12
12
|
hub.update = (name, state, options) => {
|
|
13
|
+
const result = update.call(hub, name, state, options);
|
|
13
14
|
if (!options || !options.silent) {
|
|
14
15
|
callback('update', name, state, options);
|
|
15
16
|
}
|
|
16
|
-
return
|
|
17
|
+
return result;
|
|
17
18
|
}
|
|
18
19
|
hub.trigger = (...args) => {
|
|
20
|
+
const result = trigger.apply(hub, args);
|
|
19
21
|
callback('trigger', ...args);
|
|
20
|
-
return
|
|
22
|
+
return result;
|
|
21
23
|
}
|
|
22
24
|
return hub;
|
|
23
25
|
}
|
package/src/workflow/explore.js
CHANGED
|
@@ -87,6 +87,17 @@ export default class Explore extends Workflow {
|
|
|
87
87
|
return super.useApi(options);
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
useLink(fn, options) {
|
|
91
|
+
UseLinkMixin.prototype.useLink.call(this, fn, options);
|
|
92
|
+
// also put options to layouts
|
|
93
|
+
if (options) {
|
|
94
|
+
this.useLayouts({
|
|
95
|
+
[ROLE.RELATED_QUESTIONS]: { link: options },
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
return this;
|
|
99
|
+
}
|
|
100
|
+
|
|
90
101
|
// lifecycle //
|
|
91
102
|
start({ relatedQuestions = true } = {}) {
|
|
92
103
|
if (this._linkFn === undefined) {
|
|
@@ -121,7 +132,7 @@ export default class Explore extends Workflow {
|
|
|
121
132
|
return data;
|
|
122
133
|
}
|
|
123
134
|
const linkFn = this._linkFn && this._linkFn[0];
|
|
124
|
-
const related_questions = value.related_questions.map(linkFn ? (text => ({ text, url: this._getSubmitUrl({ q: text
|
|
135
|
+
const related_questions = value.related_questions.map(linkFn ? (text => ({ text, url: this._getSubmitUrl({ q: text, generated: true }) })) : (text => ({ text })));
|
|
125
136
|
return {
|
|
126
137
|
...data,
|
|
127
138
|
value: {
|
|
@@ -143,12 +154,12 @@ export default class Explore extends Workflow {
|
|
|
143
154
|
}
|
|
144
155
|
|
|
145
156
|
_query(args) {
|
|
146
|
-
this._submitToPage(args);
|
|
157
|
+
this._submitToPage({ ...args, generated: false });
|
|
147
158
|
}
|
|
148
159
|
|
|
149
|
-
_getSubmitUrl(args
|
|
160
|
+
_getSubmitUrl(args) {
|
|
150
161
|
let url = UseLinkMixin.prototype._getSubmitUrl.call(this, args);
|
|
151
|
-
if (!generated || !this._productId) {
|
|
162
|
+
if (!args.generated || !this._productId) {
|
|
152
163
|
return url;
|
|
153
164
|
}
|
|
154
165
|
return `${url}&qs=${encodeURIComponent(this._productId)}`;
|