@miso.ai/client-sdk-ui 1.11.0-beta.1 → 1.11.0-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.
Files changed (118) hide show
  1. package/dist/cjs/actor/data.js +6 -7
  2. package/dist/cjs/actor/feedback.js +6 -39
  3. package/dist/cjs/actor/fields.js +14 -6
  4. package/dist/cjs/actor/hub.js +1 -2
  5. package/dist/cjs/actor/index.js +0 -7
  6. package/dist/cjs/actor/interactions.js +4 -15
  7. package/dist/cjs/actor/sessions.js +2 -33
  8. package/dist/cjs/actor/tracker.js +25 -26
  9. package/dist/cjs/actor/utils.js +0 -49
  10. package/dist/cjs/actor/view.js +40 -40
  11. package/dist/cjs/actor/views.js +32 -8
  12. package/dist/cjs/asset/svgs.js +18 -8
  13. package/dist/cjs/constants.js +18 -19
  14. package/dist/cjs/defaults/hybrid-search/answer-box.js +19 -3
  15. package/dist/cjs/defaults/hybrid-search/phrases.js +5 -5
  16. package/dist/cjs/defaults/hybrid-search/templates.js +37 -9
  17. package/dist/cjs/element/role/index.js +12 -5
  18. package/dist/cjs/element/role/{miso-hits.js → miso-more.js} +4 -4
  19. package/dist/cjs/element/role/miso-total.js +20 -0
  20. package/dist/cjs/layout/affiliation/affiliation.js +0 -3
  21. package/dist/cjs/layout/banner/banner.js +0 -4
  22. package/dist/cjs/layout/button/more-button.js +73 -0
  23. package/dist/cjs/layout/container/container.js +0 -3
  24. package/dist/cjs/layout/error/error.js +0 -4
  25. package/dist/cjs/layout/index.js +7 -0
  26. package/dist/cjs/layout/input/facets.js +5 -5
  27. package/dist/cjs/layout/input/option-list.js +0 -4
  28. package/dist/cjs/layout/input/search-box.js +33 -12
  29. package/dist/cjs/layout/list/cards.js +0 -3
  30. package/dist/cjs/layout/list/carousel.js +0 -3
  31. package/dist/cjs/layout/list/collection.js +25 -7
  32. package/dist/cjs/layout/list/list.js +0 -3
  33. package/dist/cjs/layout/radio/feedback.js +0 -3
  34. package/dist/cjs/layout/radio/radio.js +0 -4
  35. package/dist/cjs/layout/raf.js +4 -2
  36. package/dist/cjs/layout/text/text.js +0 -4
  37. package/dist/cjs/layout/text/typewriter/index.js +0 -3
  38. package/dist/cjs/layout/triggerable.js +79 -0
  39. package/dist/cjs/util/states.js +0 -15
  40. package/dist/cjs/version.js +1 -1
  41. package/dist/cjs/workflow/answer-based.js +109 -111
  42. package/dist/cjs/workflow/ask.js +35 -27
  43. package/dist/cjs/workflow/asks.js +1 -1
  44. package/dist/cjs/workflow/autocompletable.js +33 -0
  45. package/dist/cjs/workflow/autocomplete.js +122 -0
  46. package/dist/cjs/workflow/base.js +169 -102
  47. package/dist/cjs/workflow/explore.js +26 -19
  48. package/dist/cjs/workflow/hybrid-search-answer.js +99 -0
  49. package/dist/cjs/workflow/hybrid-search-results.js +70 -0
  50. package/dist/cjs/workflow/hybrid-search-views.js +122 -0
  51. package/dist/cjs/workflow/hybrid-search.js +98 -103
  52. package/dist/cjs/workflow/options.js +32 -17
  53. package/dist/cjs/workflow/processors.js +185 -17
  54. package/dist/cjs/workflow/recommendation.js +36 -49
  55. package/dist/cjs/workflow/search-based.js +186 -0
  56. package/dist/cjs/workflow/search.js +21 -68
  57. package/package.json +2 -2
  58. package/src/actor/data.js +2 -4
  59. package/src/actor/feedback.js +6 -23
  60. package/src/actor/fields.js +14 -6
  61. package/src/actor/hub.js +1 -2
  62. package/src/actor/index.js +0 -1
  63. package/src/actor/interactions.js +4 -10
  64. package/src/actor/sessions.js +3 -30
  65. package/src/actor/tracker.js +25 -19
  66. package/src/actor/utils.js +0 -47
  67. package/src/actor/view.js +27 -15
  68. package/src/actor/views.js +36 -8
  69. package/src/asset/svgs.js +19 -7
  70. package/src/constants.js +25 -16
  71. package/src/defaults/hybrid-search/answer-box.js +17 -5
  72. package/src/defaults/hybrid-search/phrases.js +4 -4
  73. package/src/defaults/hybrid-search/templates.js +25 -9
  74. package/src/element/role/index.js +2 -1
  75. package/src/element/role/{miso-hits.js → miso-more.js} +3 -3
  76. package/src/element/role/miso-total.js +16 -0
  77. package/src/layout/affiliation/affiliation.js +1 -5
  78. package/src/layout/banner/banner.js +0 -5
  79. package/src/layout/button/more-button.js +69 -0
  80. package/src/layout/container/container.js +1 -5
  81. package/src/layout/error/error.js +0 -5
  82. package/src/layout/index.js +2 -1
  83. package/src/layout/input/facets.js +5 -6
  84. package/src/layout/input/option-list.js +0 -5
  85. package/src/layout/input/search-box.js +27 -14
  86. package/src/layout/list/cards.js +0 -4
  87. package/src/layout/list/carousel.js +0 -4
  88. package/src/layout/list/collection.js +22 -9
  89. package/src/layout/list/list.js +0 -4
  90. package/src/layout/radio/feedback.js +0 -4
  91. package/src/layout/radio/radio.js +0 -5
  92. package/src/layout/raf.js +2 -2
  93. package/src/layout/text/text.js +0 -5
  94. package/src/layout/text/typewriter/index.js +1 -5
  95. package/src/layout/trackable.js +1 -1
  96. package/src/layout/triggerable.js +76 -0
  97. package/src/util/states.js +0 -14
  98. package/src/version.js +1 -1
  99. package/src/workflow/answer-based.js +101 -77
  100. package/src/workflow/ask.js +33 -25
  101. package/src/workflow/asks.js +1 -1
  102. package/src/workflow/autocompletable.js +29 -0
  103. package/src/workflow/autocomplete.js +113 -0
  104. package/src/workflow/base.js +147 -93
  105. package/src/workflow/explore.js +23 -8
  106. package/src/workflow/hybrid-search-answer.js +92 -0
  107. package/src/workflow/hybrid-search-results.js +63 -0
  108. package/src/workflow/hybrid-search-views.js +124 -0
  109. package/src/workflow/hybrid-search.js +102 -73
  110. package/src/workflow/options.js +26 -11
  111. package/src/workflow/processors.js +165 -14
  112. package/src/workflow/recommendation.js +32 -33
  113. package/src/workflow/search-based.js +183 -0
  114. package/src/workflow/search.js +21 -65
  115. package/dist/cjs/actor/autocomplete.js +0 -160
  116. package/dist/cjs/workflow/utils.js +0 -42
  117. package/src/actor/autocomplete.js +0 -133
  118. package/src/workflow/utils.js +0 -29
@@ -5,14 +5,9 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.default = void 0;
7
7
  var _commons = require("@miso.ai/commons");
8
- var _base = _interopRequireDefault(require("./base.js"));
9
- var _options = require("./options.js");
10
- var sources = _interopRequireWildcard(require("../source.js"));
11
- var _index = require("../actor/index.js");
12
8
  var _constants = require("../constants.js");
13
- var _index2 = require("../layout/index.js");
14
- function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
15
- function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
9
+ var _searchBased = _interopRequireDefault(require("./search-based.js"));
10
+ var _autocompletable = require("./autocompletable.js");
16
11
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
17
12
  const DEFAULT_API_OPTIONS = Object.freeze({
18
13
  group: _commons.API.GROUP.SEARCH,
@@ -22,7 +17,6 @@ const DEFAULT_API_OPTIONS = Object.freeze({
22
17
  }
23
18
  });
24
19
  const DEFAULT_AUTOCOMPLETE_OPTIONS = Object.freeze({
25
- actor: false,
26
20
  api: {
27
21
  group: _commons.API.GROUP.SEARCH,
28
22
  name: _commons.API.NAME.AUTOCOMPLETE,
@@ -30,45 +24,31 @@ const DEFAULT_AUTOCOMPLETE_OPTIONS = Object.freeze({
30
24
  completion_fields: ['suggested_queries', 'title'],
31
25
  fl: ['title', 'url', 'cover_image']
32
26
  }
33
- },
34
- throttle: 300
35
- });
36
- const DEFAULT_LAYOUTS = Object.freeze({
37
- [_constants.ROLE.QUERY]: _index2.SearchBoxLayout.type,
38
- [_constants.ROLE.PRODUCTS]: _index2.ListLayout.type
39
- });
40
- const DEFAULT_TRACKERS = Object.freeze({
41
- [_constants.ROLE.PRODUCTS]: {}
27
+ }
42
28
  });
43
- const DEFAULT_OPTIONS = Object.freeze({
29
+ const DEFAULT_LAYOUTS = Object.freeze(Object.assign({}, _searchBased.default.DEFAULT_LAYOUTS));
30
+ const DEFAULT_TRACKERS = Object.freeze(Object.assign({}, _searchBased.default.DEFAULT_TRACKERS));
31
+ const DEFAULT_OPTIONS = Object.freeze(Object.assign({}, _searchBased.default.DEFAULT_OPTIONS, {
44
32
  api: DEFAULT_API_OPTIONS,
45
33
  autocomplete: DEFAULT_AUTOCOMPLETE_OPTIONS,
46
34
  layouts: DEFAULT_LAYOUTS,
47
35
  trackers: DEFAULT_TRACKERS
48
- });
49
- class Search extends _base.default {
36
+ }));
37
+ const ROLES_OPTIONS = _searchBased.default.ROLES_OPTIONS;
38
+ class Search extends _searchBased.default {
50
39
  constructor(plugin, client) {
51
40
  super({
52
41
  name: 'search',
53
42
  plugin,
54
43
  client,
55
- roles: Object.keys(DEFAULT_LAYOUTS),
44
+ roles: ROLES_OPTIONS,
56
45
  defaults: DEFAULT_OPTIONS
57
46
  });
58
47
  }
59
- _initActors(args) {
60
- super._initActors(args);
61
- this._autocomplete = new _index.AutocompleteActor(this._hub, {
62
- source: sources.api(this._client),
63
- options: this._options
64
- });
65
- }
66
- _initSubscriptions(args) {
67
- super._initSubscriptions(args);
68
- this._unsubscribes = [...this._unsubscribes, this._hub.on(_index.fields.query(), args => this._query(args))];
48
+ _initProperties(args) {
49
+ super._initProperties(args);
50
+ this._initAutocomplete(args);
69
51
  }
70
-
71
- // lifecycle //
72
52
  autoQuery() {
73
53
  let {
74
54
  setValue = true,
@@ -92,40 +72,6 @@ class Search extends _base.default {
92
72
  });
93
73
  }
94
74
  }
95
- query(args) {
96
- if (!args.q) {
97
- throw new Error(`q is required in query() call`);
98
- }
99
- this._hub.update(_index.fields.query(), args);
100
- }
101
- _query(args) {
102
- // start a new session
103
- this.restart();
104
- const {
105
- session
106
- } = this;
107
-
108
- // build payload and trigger request
109
- const payload = this._buildPayload(args);
110
- const event = (0, _options.mergeApiOptions)(this._options.resolved.api, {
111
- payload,
112
- session
113
- });
114
- this._request(event);
115
- }
116
- _buildPayload(args) {
117
- return args;
118
- }
119
- _request(event) {
120
- this._hub.update(_index.fields.request(), event);
121
- }
122
- updateCompletions(event) {
123
- // TODO: verify
124
- this._hub.update(_index.fields.data(_constants.DATA_ASPECT.AUTOCOMPLETE), Object.assign({}, event, {
125
- source: 'manual'
126
- }));
127
- return this;
128
- }
129
75
  notifyViewUpdate() {
130
76
  let role = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _constants.ROLE.PRODUCTS;
131
77
  for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
@@ -134,6 +80,13 @@ class Search extends _base.default {
134
80
  super.notifyViewUpdate(role, ...args);
135
81
  return this;
136
82
  }
83
+
84
+ // destroy //
85
+ _destroy(options) {
86
+ this._feedback._destroy();
87
+ this._destroyAutocomplete();
88
+ super._destroy(options);
89
+ }
137
90
  }
138
91
  exports.default = Search;
139
- (0, _options.makeConfigurable)(Search.prototype, ['autocomplete']);
92
+ (0, _autocompletable.makeAutocompletable)(Search.prototype);
package/package.json CHANGED
@@ -26,7 +26,7 @@
26
26
  "url": "https://github.com/MisoAI/miso-client-js-sdk/issues"
27
27
  },
28
28
  "dependencies": {
29
- "@miso.ai/commons": "1.11.0-beta.1"
29
+ "@miso.ai/commons": "1.11.0-beta.10"
30
30
  },
31
- "version": "1.11.0-beta.1"
31
+ "version": "1.11.0-beta.10"
32
32
  }
package/src/actor/data.js CHANGED
@@ -50,10 +50,8 @@ export default class DataActor {
50
50
  const { session } = this._hub.states;
51
51
  const { session: _, ...request } = event;
52
52
 
53
- if (!event._skipLoading) {
54
- // emit response to update request
55
- this._emitResponse({ session, request });
56
- }
53
+ // emit response to update request
54
+ this._emitResponse({ session, request });
57
55
 
58
56
  try {
59
57
  const { signal } = this._ac || {};
@@ -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
- const { [fields.data()]: data } = this._hub.states;
14
- const { question_id } = data && data.value || {};
15
- if (!question_id) {
16
- return;
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() {
@@ -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(aspect) {
18
- return aspect ? `request:${aspect}` : 'request';
21
+ export function request() {
22
+ return 'request';
19
23
  }
20
24
 
21
- export function response(aspect) {
22
- return aspect ? `response:${aspect}` : 'response';
25
+ export function response() {
26
+ return 'response';
23
27
  }
24
28
 
25
- export function data(aspect) {
26
- return aspect ? `data:${aspect}` : 'data';
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/hub.js CHANGED
@@ -1,5 +1,4 @@
1
1
  import { defineValues, EventEmitter } from '@miso.ai/commons';
2
- import { removeDataInstructions } from '../workflow/processors.js';
3
2
 
4
3
  export default class Hub {
5
4
 
@@ -14,7 +13,7 @@ export default class Hub {
14
13
  }
15
14
 
16
15
  update(name, state, { silent = false } = {}) {
17
- this._states[name] = Object.freeze(removeDataInstructions(state));
16
+ this._states[name] = Object.freeze(state);
18
17
  if (!silent) {
19
18
  this._events.emit(name, state);
20
19
  }
@@ -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
- const { preprocess = [], handle } = this._options.resolved.interactions;
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
- if (handle) {
25
- handle(payload);
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() {
@@ -1,4 +1,4 @@
1
- import { uuidv4, delegateGetters } from '@miso.ai/commons';
1
+ import { uuidv4 } from '@miso.ai/commons';
2
2
  import * as fields from './fields.js';
3
3
 
4
4
  export default class SessionMaker {
@@ -6,42 +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
- const { session } = this._hub.states;
14
- return !!(session && session.active);
15
- }
16
-
17
- new({ force = false } = {}) {
18
- const currentSession = this._hub.states.session;
19
- if (currentSession && !currentSession.active && !force) {
20
- return;
21
- }
22
- const session = this._create();
23
- this._hub.update(fields.session(), session);
24
- }
25
-
26
- start() {
27
- const session = this._hub.states.session || this._create();
28
- if (session.active) {
29
- return;
30
- }
31
- this._hub.update(fields.session(), { ...session, active: true });
32
9
  }
33
10
 
34
11
  restart() {
35
- this._hub.update(fields.session(), this._create(true));
36
- }
37
-
38
- _create(active = false) {
39
- return Object.freeze({
40
- active,
12
+ const session = Object.freeze({
41
13
  uuid: uuidv4(),
42
14
  index: this._sessionIndex++,
43
15
  meta: {},
44
16
  });
17
+ this._hub.update(fields.session(), session);
45
18
  }
46
19
 
47
20
  }
@@ -1,8 +1,7 @@
1
- import { asArray } from '@miso.ai/commons';
2
- import { TRACKING_EVENT_TYPES, TRACKING_STATUS, EVENT_TYPE, validateEventType } from '../constants.js';
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, meta = {}) {
28
+ _trigger(type, items) {
29
+ validateEventType(type);
30
30
  if (this._valueless) {
31
- meta = items;
32
31
  items = VALUELESS_ITEMS;
33
32
  }
34
- validateEventType(type);
33
+
35
34
  this._syncSession();
36
35
 
37
- if (type !== EVENT_TYPE.SUBMIT) {
38
- items = this._states.untriggered(asArray(items), type);
39
- if (items.length === 0) {
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
- const property = this._role === 'results' ? 'products' : this._role; // TODO: ad-hoc, see #83
46
- const misoId = this._getMisoId();
47
- const request = this._getRequestPayload();
48
- const values = this._valueless ? undefined : items;
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
- // TODO: should trigger 'tracker' and let workflow translate to interactions
51
- this._hub.trigger(fields.interaction(), toInteraction({ property, misoId, request }, { event: type, values, meta }));
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 TRACKING_EVENT_TYPES) {
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
  };
@@ -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._config = views._rolesConfig[role] || {};
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({ session, status, ongoing, request, meta, ...state }, options);
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({ session, status, ongoing, request, meta, error }, { silent = false } = {}) {
150
- if (error) {
151
- status = STATUS.ERRONEOUS;
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: asMappingFunction(this._config.mapping || this.role)(data),
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
+ }
@@ -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._rolesConfig = rolesConfig;
23
+ this._roles = roles;
25
24
  this._containers = new Map();
26
25
  this._containerTracker = undefined;
27
26
  this._views = {};
28
- this._trackers = new Trackers(this, roles);
27
+ this._trackers = new Trackers(this, roles.members);
29
28
  this._filters = new Filters(this);
30
29
 
31
- for (const role of roles) {
30
+ for (const role of roles.members) {
31
+ if (role === ROLE.CONTAINER) {
32
+ continue;
33
+ }
32
34
  this._views[role] = new ViewActor(this, role);
33
35
  }
34
36
 
@@ -42,10 +44,14 @@ export default class ViewsActor {
42
44
  this._unsubscribes = [
43
45
  () => window.removeEventListener('resize', syncSize),
44
46
  hub.on(fields.data(), data => this.refresh({ data })),
47
+ options.on('autocomplete', () => this._syncAutocompleteOptions()),
48
+ options.on('pagination', () => this._syncPaginationOptions()),
45
49
  options.on('trackers', () => this._syncTrackers()),
46
50
  options.on('layouts', () => this._syncLayouts()),
47
51
  ];
48
52
 
53
+ this._syncAutocompleteOptions();
54
+ this._syncPaginationOptions();
49
55
  this._syncTrackers();
50
56
  this._syncLayouts();
51
57
  }
@@ -99,6 +105,10 @@ export default class ViewsActor {
99
105
  // TODO
100
106
  }
101
107
 
108
+ containsElement(element) {
109
+ return this._containers.has(element) || this._views[element.role];
110
+ }
111
+
102
112
  refreshElement(element) {
103
113
  const view = element.isContainer ? this._containers.get(element) : this._getViewByElement(element);
104
114
  view && view.refresh({ force: true });
@@ -140,7 +150,7 @@ export default class ViewsActor {
140
150
  } else {
141
151
  for (let [role, [name, options]] of Object.entries(layouts)) {
142
152
  // TODO: try to omit if unchanged
143
- const fn = () => this._layoutFactory.create(name, { ...options, role, workflow: this._workflowName });
153
+ const fn = () => this._layoutFactory.create(name, { ...options, role, workflow: this._workflowName, workflowOptions: this._getWorkflowOptions() });
144
154
  if (role === ROLE.CONTAINER) {
145
155
  for (const view of this._containers.values()) {
146
156
  view.layout = fn();
@@ -158,7 +168,7 @@ export default class ViewsActor {
158
168
  return undefined;
159
169
  }
160
170
  const [name, options] = args;
161
- return this._layoutFactory.create(name, { ...options, role, workflow: this._workflowName });
171
+ return this._layoutFactory.create(name, { ...options, role, workflow: this._workflowName, workflowOptions: this._getWorkflowOptions() });
162
172
  }
163
173
 
164
174
  get(role) {
@@ -225,6 +235,21 @@ export default class ViewsActor {
225
235
  return this._containerTracker;
226
236
  }
227
237
 
238
+ _syncAutocompleteOptions() {
239
+ this._autocompleteOptions = this._options.resolved.autocomplete;
240
+ }
241
+
242
+ _syncPaginationOptions() {
243
+ this._paginationOptions = this._options.resolved.pagination;
244
+ }
245
+
246
+ _getWorkflowOptions() {
247
+ return trimObj({
248
+ autocomplete: this._autocompleteOptions,
249
+ pagination: this._paginationOptions,
250
+ });
251
+ }
252
+
228
253
  _error(e) {
229
254
  // TODO: hub trigger error event
230
255
  console.error(e);
@@ -267,6 +292,9 @@ class Trackers {
267
292
  constructor(views, roles) {
268
293
  this._views = views;
269
294
  for (const role of roles) {
295
+ if (role === ROLE.CONTAINER) {
296
+ continue;
297
+ }
270
298
  Object.defineProperty(this, role, {
271
299
  get: () => views.get(role).tracker,
272
300
  });