@miso.ai/client-sdk-ui 1.9.0 → 1.9.1-beta.1

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 (50) hide show
  1. package/dist/cjs/actor/views.js +1 -1
  2. package/dist/cjs/combo/ask/elements.js +6 -16
  3. package/dist/cjs/combo/ask/index.js +34 -49
  4. package/dist/cjs/combo/ask/options.js +3 -7
  5. package/dist/cjs/combo/base.js +5 -0
  6. package/dist/cjs/defaults/ask/constants.js +7 -0
  7. package/dist/cjs/defaults/ask/index.js +14 -0
  8. package/dist/cjs/{combo → defaults}/ask/phrases.js +15 -0
  9. package/dist/cjs/defaults/ask/templates.js +205 -0
  10. package/dist/cjs/defaults/index.js +10 -0
  11. package/dist/cjs/element/role/miso-component.js +0 -1
  12. package/dist/cjs/layout/input/search-box.js +49 -1
  13. package/dist/cjs/layout/list/carousel.js +0 -1
  14. package/dist/cjs/layout/radio/radio.js +0 -1
  15. package/dist/cjs/layout/raf.js +0 -2
  16. package/dist/cjs/layout/templates.js +0 -1
  17. package/dist/cjs/plugin.js +13 -10
  18. package/dist/cjs/styles.js +38 -18
  19. package/dist/cjs/util/bindings.js +0 -1
  20. package/dist/cjs/util/proxy.js +0 -1
  21. package/dist/cjs/util/states.js +0 -1
  22. package/dist/cjs/util/tracker.js +0 -1
  23. package/dist/cjs/version.js +1 -1
  24. package/dist/cjs/workflow/ask.js +23 -2
  25. package/dist/cjs/workflow/asks.js +0 -2
  26. package/dist/cjs/workflow/base.js +3 -1
  27. package/dist/cjs/workflow/search.js +23 -0
  28. package/package.json +2 -2
  29. package/src/actor/views.js +1 -1
  30. package/src/combo/ask/elements.js +4 -19
  31. package/src/combo/ask/index.js +24 -35
  32. package/src/combo/ask/options.js +2 -4
  33. package/src/combo/base.js +7 -1
  34. package/src/defaults/ask/constants.js +1 -0
  35. package/src/defaults/ask/index.js +7 -0
  36. package/src/defaults/ask/phrases.js +19 -0
  37. package/src/defaults/ask/templates.js +154 -0
  38. package/src/defaults/index.js +3 -0
  39. package/src/layout/input/search-box.js +51 -0
  40. package/src/plugin.js +16 -7
  41. package/src/styles.js +42 -19
  42. package/src/version.js +1 -1
  43. package/src/workflow/ask.js +16 -0
  44. package/src/workflow/base.js +4 -0
  45. package/src/workflow/search.js +16 -0
  46. package/dist/cjs/combo/ask/constants.js +0 -7
  47. package/dist/cjs/combo/ask/templates.js +0 -87
  48. package/src/combo/ask/constants.js +0 -1
  49. package/src/combo/ask/phrases.js +0 -7
  50. package/src/combo/ask/templates.js +0 -72
@@ -11,6 +11,7 @@ var _layouts = _interopRequireDefault(require("./layouts.js"));
11
11
  var elements = _interopRequireWildcard(require("./element/index.js"));
12
12
  var layouts = _interopRequireWildcard(require("./layout/index.js"));
13
13
  var sources = _interopRequireWildcard(require("./source/index.js"));
14
+ var defaults = _interopRequireWildcard(require("./defaults/index.js"));
14
15
  var _styles = require("./styles.js");
15
16
  var _misoContainer = _interopRequireDefault(require("./element/container/miso-container.js"));
16
17
  var _misoCombo = _interopRequireDefault(require("./element/combo/miso-combo.js"));
@@ -30,12 +31,15 @@ class UiPlugin extends _commons.Component {
30
31
  this._recommendations = new WeakMap();
31
32
  this._asks = new WeakMap();
32
33
  this._extensions = new WeakMap();
34
+ this._ready = new _commons.Resolution();
33
35
  }
34
36
  async install(MisoClient, context) {
35
37
  context.addSubtree(this);
36
38
  MisoClient.on('create', this._injectClient.bind(this));
37
- const ui = {};
38
- (0, _commons.delegateGetters)(ui, this, ['layouts', 'combo']);
39
+ const ui = {
40
+ defaults
41
+ };
42
+ (0, _commons.delegateGetters)(ui, this, ['layouts', 'combo', 'ready']);
39
43
  (0, _commons.defineValues)(this, {
40
44
  MisoClient
41
45
  });
@@ -51,7 +55,6 @@ class UiPlugin extends _commons.Component {
51
55
  for (const LayoutClass of LayoutClasses) {
52
56
  LayoutClass.MisoClient = MisoClient; // TODO: find better way
53
57
  }
54
-
55
58
  for (const LayoutClass of LayoutClasses) {
56
59
  this.layouts.register(LayoutClass);
57
60
  }
@@ -78,6 +81,7 @@ class UiPlugin extends _commons.Component {
78
81
  for (const ElementClass of ElementClasses) {
79
82
  (0, _commons.defineAndUpgrade)(ElementClass);
80
83
  }
84
+ this._ready.resolve();
81
85
  }
82
86
  async requireExtension(name) {
83
87
  switch (name) {
@@ -87,6 +91,10 @@ class UiPlugin extends _commons.Component {
87
91
  throw new Error(`Unknown extension: ${name}`);
88
92
  }
89
93
  }
94
+ get ready() {
95
+ return Promise.all([this._ready.promise, MisoClient.any() // TODO: we need to wait for element connection
96
+ ]).then(() => {});
97
+ }
90
98
  _injectClient(client) {
91
99
  (0, _commons.defineValues)(client, {
92
100
  ui: new Ui(this, client)
@@ -147,7 +155,6 @@ class Ui {
147
155
  get recommendation() {
148
156
  return this.recommendations.get(); // get default recommendation unit
149
157
  }
150
-
151
158
  get search() {
152
159
  return this._search || (this._search = new _index.Search(this._plugin, this._client));
153
160
  }
@@ -160,14 +167,10 @@ class Ui {
160
167
  get explore() {
161
168
  return this._explore || (this._explore = new _index.Explore(this._plugin, this._client));
162
169
  }
163
-
164
- /*
165
- async whenStylesLoaded() {
166
- // TODO
170
+ get ready() {
171
+ return this._plugin._ready.promise;
167
172
  }
168
- */
169
173
  }
170
-
171
174
  class Combo {
172
175
  constructor(plugin, MisoClient) {
173
176
  this._plugin = plugin;
@@ -18,17 +18,13 @@ async function loadStylesIfNecessary() {
18
18
  async function loadStyles() {
19
19
  try {
20
20
  let link = getExistedLinkElement();
21
- if (link) {
22
- return waitForLinkLoaded(link);
23
- }
24
- link = document.createElement('link');
25
- link.rel = 'stylesheet';
26
- link.href = getCssUrl();
27
- return new Promise((resolve, reject) => {
28
- link.onload = resolve;
29
- link.onerror = reject;
21
+ if (!link) {
22
+ link = document.createElement('link');
23
+ link.rel = 'stylesheet';
24
+ link.href = getCssUrl();
30
25
  document.head.appendChild(link);
31
- });
26
+ }
27
+ return waitForStylesApplied(link);
32
28
  } catch (e) {
33
29
  console.error(e);
34
30
  }
@@ -53,25 +49,49 @@ function resolveCssUrl() {
53
49
  function getExistedLinkElement() {
54
50
  return document.head.querySelector(`link[href="${getCssUrl()}"]`);
55
51
  }
56
- async function waitForLinkLoaded(element) {
52
+ async function waitForStylesApplied(element) {
53
+ await waitForSheetAvailable(element);
54
+ await waitForSheetIncluded(element);
55
+ }
56
+ async function waitForSheetAvailable(element) {
57
57
  if (element.sheet) {
58
58
  return;
59
59
  }
60
60
  return new Promise((resolve, reject) => {
61
61
  let intervalId;
62
- element.onload = () => {
63
- intervalId && clearInterval(intervalId);
64
- resolve();
65
- };
66
62
  element.onerror = () => {
67
63
  intervalId && clearInterval(intervalId);
68
64
  reject();
69
65
  };
70
66
  intervalId = setInterval(() => {
71
- if (element.sheet) {
72
- intervalId && clearInterval(intervalId);
73
- resolve();
67
+ if (!element.sheet) {
68
+ return;
74
69
  }
70
+ intervalId && clearInterval(intervalId);
71
+ resolve();
75
72
  }, 100);
76
73
  });
74
+ }
75
+ async function waitForSheetIncluded(element) {
76
+ if (isSheetIncluded(element)) {
77
+ return;
78
+ }
79
+ return new Promise(resolve => {
80
+ let intervalId;
81
+ intervalId = setInterval(() => {
82
+ if (!isSheetIncluded(element)) {
83
+ return;
84
+ }
85
+ intervalId && clearInterval(intervalId);
86
+ resolve();
87
+ }, 100);
88
+ });
89
+ }
90
+ function isSheetIncluded(element) {
91
+ for (const sheet of document.styleSheets) {
92
+ if (sheet.ownerNode === element) {
93
+ return true;
94
+ }
95
+ }
96
+ return false;
77
97
  }
@@ -14,7 +14,6 @@ function fallbackListFunction(keyAttrName) {
14
14
  }
15
15
  }));
16
16
  }
17
-
18
17
  class Bindings {
19
18
  constructor() {
20
19
  let {
@@ -72,7 +72,6 @@ class ProxyElement {
72
72
  }
73
73
  }
74
74
  }
75
-
76
75
  _offAllFromCurrentElement() {
77
76
  if (this._element) {
78
77
  for (const {
@@ -37,7 +37,6 @@ class States {
37
37
  this.sessionId = sessionId;
38
38
  this._states = new Map(); // productId -> {impression, viewable, click}
39
39
  }
40
-
41
40
  getFullState(productId) {
42
41
  // when we receive a bind event, impression is trigger, making an entry here
43
42
  // so if the state is not found, the product is not present yet => all untracked
@@ -42,7 +42,6 @@ function normalizeOptions(options) {
42
42
  if (options === false) {
43
43
  return false; // turn off all tracking
44
44
  }
45
-
46
45
  if (options === true) {
47
46
  options = {};
48
47
  }
@@ -4,4 +4,4 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.default = void 0;
7
- var _default = exports.default = '1.9.0';
7
+ var _default = exports.default = '1.9.1-beta.1';
@@ -56,7 +56,6 @@ const DEFAULT_TRACKERS = Object.freeze({
56
56
  }
57
57
  }
58
58
  });
59
-
60
59
  const DEFAULT_OPTIONS = Object.freeze({
61
60
  api: DEFAULT_API_OPTIONS,
62
61
  layouts: DEFAULT_LAYOUTS,
@@ -94,7 +93,6 @@ class Ask extends _base.default {
94
93
  context._events.emit('create', this);
95
94
  this.reset(); // create a session so query suggestions can be tracked
96
95
  }
97
-
98
96
  get questionId() {
99
97
  return this._questionId;
100
98
  }
@@ -123,6 +121,29 @@ class Ask extends _base.default {
123
121
  }
124
122
 
125
123
  // lifecycle //
124
+ autoQuery() {
125
+ let {
126
+ setValue = true,
127
+ focus = true
128
+ } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
129
+ const q = new URLSearchParams(window.location.search).get('q');
130
+ const {
131
+ layout
132
+ } = this._views.get(_constants.ROLE.QUERY);
133
+ if (layout) {
134
+ if (q && setValue) {
135
+ layout.value = q;
136
+ }
137
+ if (!q && focus) {
138
+ layout.focus();
139
+ }
140
+ }
141
+ if (q) {
142
+ this.query({
143
+ q
144
+ });
145
+ }
146
+ }
126
147
  query() {
127
148
  let _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
128
149
  let {
@@ -16,7 +16,6 @@ class Asks extends _context.default {
16
16
  this._byPqid = new Map();
17
17
  this._root = new _ask.default(this); // initialize with default workflow
18
18
  }
19
-
20
19
  get root() {
21
20
  return this._root;
22
21
  }
@@ -56,7 +55,6 @@ class Asks extends _context.default {
56
55
  if (events === undefined) {
57
56
  events = !!root; // events are default to be true only when root = true
58
57
  }
59
-
60
58
  if (events) {
61
59
  this._events.clear();
62
60
  }
@@ -98,6 +98,9 @@ class Workflow extends _commons.Component {
98
98
  get states() {
99
99
  return this._hub.states;
100
100
  }
101
+ get views() {
102
+ return this._views.interface;
103
+ }
101
104
 
102
105
  // lifecycle //
103
106
  reset() {
@@ -131,7 +134,6 @@ class Workflow extends _commons.Component {
131
134
  if (session.uuid !== this.session.uuid) {
132
135
  return; // ignore data for old session or inactive session
133
136
  }
134
-
135
137
  this._sessions.start(); // in case session not started yet
136
138
 
137
139
  data = this._defaultProcessData(data);
@@ -43,6 +43,29 @@ class Search extends _base.default {
43
43
  }
44
44
 
45
45
  // lifecycle //
46
+ autoQuery() {
47
+ let {
48
+ setValue = true,
49
+ focus = true
50
+ } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
51
+ const q = new URLSearchParams(window.location.search).get('q');
52
+ const {
53
+ layout
54
+ } = this._views.get(_constants.ROLE.QUERY);
55
+ if (layout) {
56
+ if (q && setValue) {
57
+ layout.value = q;
58
+ }
59
+ if (!q && focus) {
60
+ layout.focus();
61
+ }
62
+ }
63
+ if (q) {
64
+ this.query({
65
+ q
66
+ });
67
+ }
68
+ }
46
69
  query(payload) {
47
70
  this.restart();
48
71
  const {
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.9.0"
29
+ "@miso.ai/commons": "1.9.1-beta.1"
30
30
  },
31
- "version": "1.9.0"
31
+ "version": "1.9.1-beta.1"
32
32
  }
@@ -215,7 +215,7 @@ class Views {
215
215
  return this._actor.get(role).interface;
216
216
  }
217
217
 
218
- get views() {
218
+ get all() {
219
219
  return this._actor.views.map(view => view.interface);
220
220
  }
221
221
 
@@ -1,32 +1,17 @@
1
1
  import { defineValues } from '@miso.ai/commons';
2
- import { PREFIX } from './constants.js';
2
+ import { constants } from '../../defaults/ask/index.js';
3
3
 
4
4
  export default class AskComboElements {
5
5
 
6
- constructor(root) {
6
+ constructor(root, { classPrefix = constants.CLASS_PREFIX } = {}) {
7
7
  if (!root) {
8
8
  throw new Error('Root element is required');
9
9
  }
10
10
  defineValues(this, {
11
11
  root,
12
- rootQuery: root.querySelector(`#${PREFIX}__question miso-query`),
13
- followUpsSection: root.querySelector(`#${PREFIX}__follow-ups`),
14
- relatedResourcesContainer: root.querySelector(`#${PREFIX}__related-resources miso-ask`),
12
+ followUpsSection: root.querySelector(`.${classPrefix}__follow-ups`),
13
+ relatedResourcesContainer: root.querySelector(`.${classPrefix}__related-resources miso-ask`),
15
14
  });
16
-
17
- if (!this.rootQuery) {
18
- throw new Error(`Root question query element not found.`);
19
- }
20
- if (!this.followUpsSection) {
21
- throw new Error(`Follow-ups section element not found.`);
22
- }
23
- if (!this.relatedResourcesContainer) {
24
- throw new Error(`Related resources container element not found.`);
25
- }
26
- }
27
-
28
- get rootInput() {
29
- return this.rootQuery.querySelector(`[data-role="input"]`);
30
15
  }
31
16
 
32
17
  }
@@ -1,6 +1,4 @@
1
1
  import { defineValues, waitForDomContentLoaded } from '@miso.ai/commons';
2
- import { fields } from '../../actor/index.js';
3
- import { ROLE } from '../../constants.js';
4
2
  import Combo from '../base.js';
5
3
  import AskComboOptions from './options.js';
6
4
  import AskComboElements from './elements.js';
@@ -47,7 +45,7 @@ export default class AskCombo extends Combo {
47
45
 
48
46
  async _start() {
49
47
  await waitForDomContentLoaded();
50
- await this.MisoClient.cmdDone;
48
+ await this.waitForElement();
51
49
 
52
50
  // setup MisoClient
53
51
  await this._setupMisoClient();
@@ -91,7 +89,7 @@ export default class AskCombo extends Combo {
91
89
  const { element, resolvedOptions: options } = this;
92
90
  const { templates = {} } = options;
93
91
  element.innerHTML = templates.root(options);
94
- this._elements = new AskComboElements(element);
92
+ this._elements = new AskComboElements(element, options);
95
93
  }
96
94
 
97
95
  async _createClientInstance() {
@@ -100,45 +98,36 @@ export default class AskCombo extends Combo {
100
98
 
101
99
  async _setupWorkflows() {
102
100
  const { client, elements, resolvedOptions: options } = this;
103
- const { templates = {} } = options;
101
+ const { templates = {}, features = {} } = options;
104
102
 
105
103
  const context = client.ui.asks;
106
104
  const rootWorkflow = client.ui.ask;
107
105
 
108
- // TODO: set placeholder is present in options
109
-
110
- // when a answer is fully populated, insert a new section for the follow-up question
111
- context.on('done', ({ workflow }) => {
112
- elements.followUpsSection.insertAdjacentHTML('beforeend', templates.followUp(options, { parentQuestionId: workflow.questionId }));
113
- });
114
-
115
- // when a new query starts, associate the last section container (for related resources) to that workflow
116
- context.on('loading', ({ workflow }) => {
117
- elements.relatedResourcesContainer.workflow = workflow;
118
- });
119
-
120
- // if user starts over, clean up current follow-up questions
121
- rootWorkflow.on('loading', () => {
122
- // clean up the entire follow-ups section
123
- elements.followUpsSection.innerHTML = '';
124
- // destroy all follow-up workflows
125
- context.reset({ root: false });
126
- });
106
+ // TODO: set placeholder if present in options
107
+
108
+ if (elements.followUpsSection && features.followUpQuestions !== false) {
109
+ // when a answer is fully populated, insert a new section for the follow-up question
110
+ context.on('done', ({ workflow }) => {
111
+ elements.followUpsSection.insertAdjacentHTML('beforeend', templates.followUp({ ...options, parentQuestionId: workflow.questionId }));
112
+ });
113
+ // if user starts over, clean up current follow-up questions
114
+ rootWorkflow.on('loading', () => {
115
+ // clean up the entire follow-ups section
116
+ elements.followUpsSection.innerHTML = '';
117
+ // destroy all follow-up workflows
118
+ context.reset({ root: false });
119
+ });
120
+ }
127
121
 
128
- // wait for miso-query content to be populated
129
- const QUERY = fields.view(ROLE.QUERY);
130
- if (!rootWorkflow.states[QUERY]) {
131
- await rootWorkflow._hub.once(QUERY);
122
+ if (elements.relatedResourcesContainer && features.relatedResources !== false && features.followUpQuestions !== false) {
123
+ // when a new query starts, associate the last section container (for related resources) to that workflow
124
+ context.on('loading', ({ workflow }) => {
125
+ elements.relatedResourcesContainer.workflow = workflow;
126
+ });
132
127
  }
133
128
 
134
129
  // start query if specified in URL
135
- const { q } = options;
136
- if (q) {
137
- elements.rootInput.value = q;
138
- rootWorkflow.query({ q });
139
- } else {
140
- elements.rootInput.focus();
141
- }
130
+ rootWorkflow.autoQuery();
142
131
  }
143
132
 
144
133
  }
@@ -1,6 +1,5 @@
1
1
  import { trimObj } from '@miso.ai/commons';
2
- import * as templates from './templates.js';
3
- import * as phrases from './phrases.js';
2
+ import { templates } from '../../defaults/ask/index.js';
4
3
 
5
4
  // TODO: generalize
6
5
 
@@ -8,7 +7,6 @@ const { currentScript } = document;
8
7
 
9
8
  const DEFAULT_OPTIONS = Object.freeze({
10
9
  templates,
11
- phrases,
12
10
  autostart: true,
13
11
  });
14
12
 
@@ -48,7 +46,7 @@ function normalizeOptions({ api_key: apiKey, api_host: apiHost, templates, phras
48
46
  apiKey,
49
47
  apiHost,
50
48
  templates: templateAsFunction(templates),
51
- phrases: templateAsFunction(phrases),
49
+ phrases,
52
50
  ...options,
53
51
  });
54
52
  }
package/src/combo/base.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Component } from '@miso.ai/commons';
1
+ import { Component, Resolution } from '@miso.ai/commons';
2
2
 
3
3
  export default class Combo extends Component {
4
4
 
@@ -23,8 +23,14 @@ export default class Combo extends Component {
23
23
  return this._element;
24
24
  }
25
25
 
26
+ async waitForElement() {
27
+ return this._element || (this._elementRes || (this._elementRes = new Resolution())).promise;
28
+ }
29
+
26
30
  set element(element) {
31
+ // TODO: guard against multiple element assignment
27
32
  this._element = element;
33
+ this._elementRes && this._elementRes.resolve(element);
28
34
  }
29
35
 
30
36
  start(options) {
@@ -0,0 +1 @@
1
+ export const CLASS_PREFIX = 'miso-ask-combo';
@@ -0,0 +1,7 @@
1
+ import * as _constants from './constants.js';
2
+ import * as _phrases from './phrases.js';
3
+ import * as _templates from './templates.js';
4
+
5
+ export const constants = Object.freeze(_constants);
6
+ export const phrases = Object.freeze(_phrases);
7
+ export const templates = Object.freeze(_templates);
@@ -0,0 +1,19 @@
1
+ /**
2
+ * The phrase displayed right before <miso-question>
3
+ */
4
+ export const question = 'You asked...';
5
+
6
+ /**
7
+ * The phrase displayed right before <miso-sources>
8
+ */
9
+ export const sources = 'My reply is based on the following';
10
+
11
+ /**
12
+ * The phrase displayed right before <miso-query-suggestions>
13
+ */
14
+ export const relatedQuestions = 'Related questions you can explore';
15
+
16
+ /**
17
+ * The phrase displayed right before <miso-related-resources>
18
+ */
19
+ export const relatedResources = 'Go beyond, and learn more about this topic';