@vertexvis/ui 0.1.0-testing.4 → 0.1.0-testing.6

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 (82) hide show
  1. package/dist/cjs/components.cjs.js +1 -1
  2. package/dist/cjs/index.cjs.js +6 -6
  3. package/dist/cjs/loader.cjs.js +1 -1
  4. package/dist/cjs/{popover-d9ec8e10.js → popover-942209b8.js} +23 -1
  5. package/dist/cjs/{result-list-1e592c3c.js → result-list-241ffe8d.js} +46 -2
  6. package/dist/cjs/search-bar-e101ba43.js +398 -0
  7. package/dist/cjs/{select-0eb7203f.js → select-5f8aecfe.js} +27 -4
  8. package/dist/cjs/{text-field-0397fb34.js → text-field-bccbde1f.js} +1 -0
  9. package/dist/cjs/{tooltip-02e24a48.js → tooltip-e9f63631.js} +58 -19
  10. package/dist/cjs/vertex-popover.cjs.entry.js +1 -1
  11. package/dist/cjs/vertex-result-list.cjs.entry.js +1 -1
  12. package/dist/cjs/vertex-search-bar.cjs.entry.js +1 -1
  13. package/dist/cjs/vertex-select.cjs.entry.js +1 -1
  14. package/dist/cjs/vertex-textfield.cjs.entry.js +1 -1
  15. package/dist/cjs/vertex-tooltip.cjs.entry.js +1 -1
  16. package/dist/collection/components/menu/menu.js +1 -1
  17. package/dist/collection/components/popover/popover.js +51 -0
  18. package/dist/collection/components/result-list/result-list.js +47 -3
  19. package/dist/collection/components/search-bar/dom.js +12 -0
  20. package/dist/collection/components/search-bar/lib.js +23 -15
  21. package/dist/collection/components/search-bar/search-bar.css +2 -29
  22. package/dist/collection/components/search-bar/search-bar.js +353 -326
  23. package/dist/collection/components/select/select.css +16 -0
  24. package/dist/collection/components/select/select.js +49 -2
  25. package/dist/collection/components/text-field/text-field.js +1 -0
  26. package/dist/collection/components/tooltip/tooltip.css +0 -4
  27. package/dist/collection/components/tooltip/tooltip.js +62 -18
  28. package/dist/collection/util/templates/element-pool.js +19 -1
  29. package/dist/components/components.css +1 -1
  30. package/dist/components/components.esm.js +1 -1
  31. package/dist/components/index.esm.js +1 -1
  32. package/dist/components/p-0b1cdc8a.entry.js +1 -0
  33. package/dist/components/p-406e73da.entry.js +1 -0
  34. package/dist/components/p-43b1b3f9.js +1 -0
  35. package/dist/components/p-606596de.entry.js +1 -0
  36. package/dist/components/p-6b862967.js +1 -0
  37. package/dist/components/p-7cfb3736.entry.js +1 -0
  38. package/dist/components/p-8393b6a7.js +1 -0
  39. package/dist/components/p-912f6e24.js +1 -0
  40. package/dist/components/p-92930f2a.js +1 -0
  41. package/dist/components/p-c2706288.js +1 -0
  42. package/dist/components/p-ee496965.entry.js +1 -0
  43. package/dist/components/p-f51fef1d.entry.js +1 -0
  44. package/dist/esm/components.js +1 -1
  45. package/dist/esm/index.js +6 -6
  46. package/dist/esm/loader.js +1 -1
  47. package/dist/esm/{popover-67c88e4b.js → popover-6e806354.js} +23 -1
  48. package/dist/esm/{result-list-36cfb08a.js → result-list-16c6afbd.js} +46 -2
  49. package/dist/esm/search-bar-12d91ad5.js +396 -0
  50. package/dist/esm/{select-75ed5653.js → select-d4e135b7.js} +27 -4
  51. package/dist/esm/{text-field-e542da25.js → text-field-32ac877e.js} +1 -0
  52. package/dist/esm/{tooltip-b4d2a889.js → tooltip-933da261.js} +58 -19
  53. package/dist/esm/vertex-popover.entry.js +1 -1
  54. package/dist/esm/vertex-result-list.entry.js +1 -1
  55. package/dist/esm/vertex-search-bar.entry.js +1 -1
  56. package/dist/esm/vertex-select.entry.js +1 -1
  57. package/dist/esm/vertex-textfield.entry.js +1 -1
  58. package/dist/esm/vertex-tooltip.entry.js +1 -1
  59. package/dist/types/components/popover/popover.d.ts +7 -0
  60. package/dist/types/components/result-list/result-list.d.ts +9 -1
  61. package/dist/types/components/search-bar/dom.d.ts +3 -0
  62. package/dist/types/components/search-bar/lib.d.ts +12 -6
  63. package/dist/types/components/search-bar/search-bar.d.ts +98 -41
  64. package/dist/types/components/select/select.d.ts +8 -0
  65. package/dist/types/components/tooltip/tooltip.d.ts +7 -0
  66. package/dist/types/components.d.ts +81 -16
  67. package/dist/types/util/templates/element-pool.d.ts +10 -1
  68. package/package.json +4 -3
  69. package/dist/cjs/search-bar-bb40cfa7.js +0 -290
  70. package/dist/components/p-19318fee.entry.js +0 -1
  71. package/dist/components/p-209db2ba.entry.js +0 -1
  72. package/dist/components/p-2bb3b235.js +0 -1
  73. package/dist/components/p-4224c2ad.js +0 -1
  74. package/dist/components/p-52739247.js +0 -1
  75. package/dist/components/p-552c128f.js +0 -1
  76. package/dist/components/p-6505cdb3.js +0 -1
  77. package/dist/components/p-ae6a3c46.entry.js +0 -1
  78. package/dist/components/p-bd11e7d1.js +0 -1
  79. package/dist/components/p-c8dd68a1.entry.js +0 -1
  80. package/dist/components/p-e576818b.entry.js +0 -1
  81. package/dist/components/p-ebabee40.entry.js +0 -1
  82. package/dist/esm/search-bar-59cc151d.js +0 -288
@@ -1,94 +1,74 @@
1
1
  import { h, Host, } from '@stencil/core';
2
2
  import classNames from 'classnames';
3
+ import deepEqual from 'fast-deep-equal';
3
4
  import { generateInstanceFromTemplate } from '../../util/templates/templates';
4
- import { createDocumentRange, createTextNode, getWindowSelection, isHtmlElement, isReplacedElement, isTextNode, } from './lib';
5
+ import { createDocumentRange, createTextNode, getWindowSelection } from './dom';
6
+ import { createResultUrn, createSearchResultReplacement, getNodesForSearchResultReplacement, trimNonstandardSpaces, } from './lib';
5
7
  export class SearchBar {
6
8
  constructor() {
7
- this.handleKeyDown = (event) => {
8
- if ((this.triggerCharacters.includes(event.key) ||
9
- this.triggerCharacter === event.key) &&
10
- this.triggerKey == null) {
11
- this.triggerKey = event.key;
12
- this.updateCursorPosition();
13
- this.triggerCharacterPressed.emit('');
14
- }
15
- else {
16
- this.restartTriggerInput();
17
- }
18
- if (event.key === 'Backspace') {
19
- const selection = getWindowSelection();
20
- if (selection && (selection === null || selection === void 0 ? void 0 : selection.rangeCount) > 0) {
21
- const range = selection.getRangeAt(0);
22
- range.deleteContents();
23
- }
24
- }
25
- this.valueChanged.emit();
9
+ this.rawElements = [];
10
+ this.isIdenticalElement = (child, other) => {
11
+ return (child === this.triggeredElement ||
12
+ this.getTextContent(child) === this.getTextContent(other));
26
13
  };
27
- this.handleKeyUp = (event) => {
28
- if (event.key === 'Backspace') {
29
- this.updateTriggerState(this.getSelectionSubstring());
30
- }
31
- if (event.key === 'Enter') {
32
- this.clearTriggerState();
14
+ this.getTextContent = (node) => {
15
+ var _a;
16
+ if (node instanceof HTMLElement) {
17
+ return node.innerText;
33
18
  }
34
- this.valueChanged.emit();
19
+ return (_a = node.textContent) !== null && _a !== void 0 ? _a : '';
35
20
  };
36
- this.updateTriggerValue = (data) => {
37
- var _a, _b, _c, _d, _e;
38
- if (this.triggerKey != null &&
39
- this.triggerRange != null &&
40
- this.triggerRange.startContainer === this.triggerRange.endContainer) {
41
- const triggerRangeNode = this.triggerRange.startContainer;
42
- if (isTextNode(triggerRangeNode)) {
43
- const before = createTextNode((_a = triggerRangeNode.textContent) === null || _a === void 0 ? void 0 : _a.slice(0, this.triggerRange.startOffset - 1));
44
- const after = createTextNode((_b = triggerRangeNode.textContent) === null || _b === void 0 ? void 0 : _b.slice(this.triggerRange.endOffset));
45
- const replaced = this.createReplacedElement(data);
46
- (_c = triggerRangeNode.parentElement) === null || _c === void 0 ? void 0 : _c.insertBefore(before, triggerRangeNode);
47
- (_d = triggerRangeNode.parentElement) === null || _d === void 0 ? void 0 : _d.insertBefore(after, triggerRangeNode.nextSibling);
48
- (_e = triggerRangeNode.parentElement) === null || _e === void 0 ? void 0 : _e.insertBefore(replaced, after);
49
- triggerRangeNode.remove();
50
- this.moveCursorToNodeEnd(after, true);
21
+ this.handleCursorPositionUpdate = () => {
22
+ var _a, _b, _c;
23
+ const windowRange = (_a = getWindowSelection()) === null || _a === void 0 ? void 0 : _a.getRangeAt(0);
24
+ if (windowRange != null) {
25
+ if (!this.hasTriggered) {
26
+ const triggerText = this.readTriggerValue((_b = windowRange.commonAncestorContainer.textContent) !== null && _b !== void 0 ? _b : '', windowRange.startOffset);
27
+ if (triggerText != null) {
28
+ this.hasTriggered = true;
29
+ this.triggeredRange = windowRange;
30
+ this.triggeredElement = windowRange.commonAncestorContainer;
31
+ this.searchChanged.emit(triggerText.replace(this.triggerCharacter, ''));
32
+ }
33
+ }
34
+ else if (this.hasTriggered) {
35
+ const triggerText = this.readTriggerValue((_c = windowRange.commonAncestorContainer.textContent) !== null && _c !== void 0 ? _c : '', windowRange.startOffset);
36
+ if (triggerText != null) {
37
+ this.triggeredRange = windowRange;
38
+ this.triggeredElement = windowRange.commonAncestorContainer;
39
+ this.searchChanged.emit(triggerText.replace(this.triggerCharacter, ''));
40
+ }
41
+ else {
42
+ this.hasTriggered = false;
43
+ this.triggeredRange = undefined;
44
+ this.triggeredElement = undefined;
45
+ }
51
46
  }
52
47
  }
48
+ this.cursorPosition = this.getCursorPosition();
53
49
  };
54
- this.triggerText = () => {
55
- var _a, _b;
56
- if (this.triggerKey != null &&
57
- this.triggerRange != null &&
58
- this.triggerRange.startContainer === this.triggerRange.endContainer) {
59
- const triggerRangeNode = this.triggerRange.startContainer;
60
- return ((_b = (_a = triggerRangeNode.textContent) === null || _a === void 0 ? void 0 : _a.slice(this.triggerRange.startOffset, this.triggerRange.endOffset)) !== null && _b !== void 0 ? _b : '');
61
- }
62
- return '';
50
+ this.readTriggerValue = (value, fromIndex) => {
51
+ const adjustedValue = value.replace(String.fromCharCode(160), ' ');
52
+ const beforeSubstr = adjustedValue.substring(0, fromIndex);
53
+ const afterSubstr = adjustedValue.substring(fromIndex);
54
+ const adjustedBeforeSubstr = beforeSubstr.includes(this.triggerCharacter)
55
+ ? beforeSubstr.substring(beforeSubstr.lastIndexOf(this.triggerCharacter))
56
+ : '';
57
+ const adjustedAfterSubstr = afterSubstr.substring(0, this.firstIndexOfBreakCharacter(afterSubstr));
58
+ const result = `${adjustedBeforeSubstr}${adjustedAfterSubstr}`;
59
+ return result.includes(this.triggerCharacter) &&
60
+ !this.includesBreakCharacter(result)
61
+ ? result
62
+ : undefined;
63
63
  };
64
- this.restartTriggerInput = () => {
65
- this.clearTriggerTimeout();
66
- this.triggerTimeout = setTimeout(() => {
67
- if (this.triggerRange != null && this.triggerKey != null) {
68
- this.triggerCharacterPressed.emit(this.triggerText());
69
- }
70
- this.triggerTimeout = undefined;
71
- }, this.debounce);
64
+ this.includesBreakCharacter = (value) => {
65
+ return this.breakCharacters.some((bc) => value.includes(bc));
72
66
  };
73
- this.clearTriggerTimeout = () => {
74
- if (this.triggerTimeout != null) {
75
- clearTimeout(this.triggerTimeout);
76
- this.triggerTimeout = undefined;
77
- }
78
- };
79
- this.updateCursorPosition = () => {
80
- var _a;
81
- const selection = getWindowSelection();
82
- if (selection != null && selection.rangeCount > 0) {
83
- const cursorBounds = selection.getRangeAt(0).getBoundingClientRect();
84
- const inputBounds = (_a = this.contentEl) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
85
- const cursorBottom = cursorBounds.bottom || (inputBounds === null || inputBounds === void 0 ? void 0 : inputBounds.bottom) || 0;
86
- const cursorTop = cursorBounds.top || (inputBounds === null || inputBounds === void 0 ? void 0 : inputBounds.top) || 0;
87
- this.cursorPosition = {
88
- x: cursorBounds.left || (inputBounds === null || inputBounds === void 0 ? void 0 : inputBounds.left) || 0,
89
- y: this.placement.includes('top') ? cursorTop : cursorBottom,
90
- };
91
- }
67
+ this.firstIndexOfBreakCharacter = (value) => {
68
+ const indices = this.breakCharacters
69
+ .map((bc) => value.indexOf(bc))
70
+ .filter((i) => i >= 0);
71
+ return indices.length > 0 ? Math.min(...indices) : value.length;
92
72
  };
93
73
  this.moveCursorToNodeEnd = (node, collapseToStart = false) => {
94
74
  const selection = getWindowSelection();
@@ -100,143 +80,124 @@ export class SearchBar {
100
80
  selection.addRange(range);
101
81
  }
102
82
  };
103
- this.updateInputCursorPosition = (target) => {
104
- if (isHtmlElement(target)) {
105
- const targetElement = isReplacedElement(target.parentElement)
106
- ? target.parentElement
107
- : target;
108
- if (isReplacedElement(targetElement)) {
109
- this.insertAdjacentTextNode(targetElement);
110
- }
83
+ this.getContentAsString = () => {
84
+ if (this.contentEl != null) {
85
+ return trimNonstandardSpaces(Array.from(this.contentEl.childNodes).reduce((res, n) => {
86
+ var _a;
87
+ if (n instanceof HTMLElement &&
88
+ n.getAttribute('data-replaced') === 'true') {
89
+ return `${res}${n.getAttribute('data-original')}`;
90
+ }
91
+ else if (n instanceof HTMLElement) {
92
+ return `${res}${n.innerText}`;
93
+ }
94
+ else {
95
+ return `${res}${(_a = n.textContent) !== null && _a !== void 0 ? _a : ''}`;
96
+ }
97
+ }, ''));
111
98
  }
99
+ return '';
112
100
  };
113
- this.createReplacedElement = (data) => {
101
+ this.createReplacedElement = (original, data) => {
114
102
  const template = this.hostEl.querySelector('template[slot="replaced"]');
115
103
  if (template != null) {
116
104
  const instance = generateInstanceFromTemplate(template);
117
105
  instance.bindings.bind(data);
106
+ instance.element.id = data.id;
118
107
  instance.element.style.display = 'inline-block';
119
108
  instance.element.contentEditable = 'false';
109
+ instance.element.tabIndex = -1;
120
110
  instance.element.setAttribute('data-replaced', 'true');
111
+ instance.element.setAttribute('data-original', original);
121
112
  return instance.element;
122
113
  }
123
114
  else {
124
115
  throw new Error('Replaced template not defined.');
125
116
  }
126
117
  };
127
- this.handleSelectionChange = () => {
128
- this.updateTriggerState(this.getSelectionSubstring());
129
- };
130
- this.getSelectionSubstring = () => {
131
- var _a;
132
- const selection = getWindowSelection();
133
- if (selection != null && selection.rangeCount > 0) {
134
- const range = selection.getRangeAt(0);
135
- this.updateTriggerRange();
136
- if (range != null &&
137
- range.startOffset === range.endOffset &&
138
- isTextNode(range.startContainer) &&
139
- range.startContainer.textContent != null &&
140
- !isReplacedElement(range.startContainer.parentElement) &&
141
- ((_a = this.contentEl) === null || _a === void 0 ? void 0 : _a.contains(range.startContainer))) {
142
- return range.startContainer.textContent.slice(this.lastIndexOfBreakCharacter(range.startContainer.textContent.slice(0, range.startOffset)) + 1, range.startOffset);
143
- }
144
- }
145
- return '';
146
- };
147
- this.insertAdjacentTextNode = (el) => {
148
- el.insertAdjacentText('afterend', '');
149
- if (el.nextSibling != null) {
150
- this.moveCursorToNodeEnd(el.nextSibling);
151
- }
152
- };
153
- this.updateTriggerState = (text) => {
154
- const triggers = this.triggerCharacter != null
155
- ? [...this.triggerCharacters, this.triggerCharacter]
156
- : this.triggerCharacters;
157
- const trigger = triggers.find((tc) => text.includes(tc));
158
- if (trigger != null) {
159
- this.restartTriggerInput();
160
- this.triggerKey = trigger;
161
- this.updateTriggerRange();
162
- if (this.cursorPosition == null) {
163
- this.updateCursorPosition();
164
- }
165
- this.open = true;
166
- }
167
- else {
168
- this.clearTriggerState();
169
- }
170
- };
171
- this.updateTriggerRange = () => {
172
- var _a;
173
- const selection = getWindowSelection();
174
- if (selection != null && selection.rangeCount > 0) {
175
- const range = selection.getRangeAt(0);
176
- if (this.triggerKey != null) {
177
- this.triggerRange = (_a = this.triggerRange) !== null && _a !== void 0 ? _a : range.cloneRange();
178
- this.triggerRange.setEnd(range.endContainer, range.endOffset);
179
- }
180
- }
181
- };
182
- this.clearTriggerState = () => {
183
- this.triggerKey = undefined;
184
- this.triggerRange = undefined;
185
- this.cursorPosition = undefined;
186
- this.open = false;
187
- this.clearTriggerTimeout();
188
- };
189
- this.lastIndexOfBreakCharacter = (search) => {
190
- const index = this.breakCharacters.reduce((index, bc) => {
191
- const searchIndex = search
192
- .replace(String.fromCharCode(160), ' ')
193
- .lastIndexOf(bc);
194
- return searchIndex > index ? searchIndex : index;
195
- }, -1);
196
- return index;
197
- };
198
- this.handleFocus = (ev) => {
199
- this.inputFocus.emit(ev);
200
- };
201
- this.handleBlur = (ev) => {
202
- this.open = false;
203
- this.inputBlur.emit(ev);
204
- };
205
- this.handleInputPointerEvent = (ev) => {
206
- if (ev.target != null) {
207
- this.updateInputCursorPosition(ev.target);
208
- }
209
- };
210
- this.handleResultPointerDown = (ev) => {
211
- ev.preventDefault();
212
- };
213
118
  this.variant = 'standard';
119
+ this.disabled = false;
120
+ this.triggerCharacter = '@';
121
+ this.breakCharacters = [' ', '\n'];
214
122
  this.resultItems = undefined;
215
- this.triggerCharacters = [];
216
- this.triggerCharacter = undefined;
217
- this.breakCharacters = [' '];
218
- this.debounce = 100;
219
- this.placeholder = undefined;
220
123
  this.placement = 'bottom-start';
221
- this.cursorPosition = undefined;
222
- this.open = false;
223
- this.triggerKey = undefined;
224
- this.triggerRange = undefined;
124
+ this.value = undefined;
125
+ this.placeholder = undefined;
126
+ this.replacements = [];
127
+ this.cursorPosition = { x: 0, y: 0 };
128
+ this.displayedElements = [];
129
+ this.hasTriggered = false;
130
+ this.handleKeyDown = this.handleKeyDown.bind(this);
131
+ this.handleKeyUp = this.handleKeyUp.bind(this);
132
+ this.handleResultClick = this.handleResultClick.bind(this);
133
+ this.handleClick = this.handleClick.bind(this);
134
+ this.handleWindowClick = this.handleWindowClick.bind(this);
135
+ this.handleInput = this.handleInput.bind(this);
136
+ this.handleBlur = this.handleBlur.bind(this);
137
+ this.handleFocus = this.handleFocus.bind(this);
138
+ this.handleCursorPositionUpdate =
139
+ this.handleCursorPositionUpdate.bind(this);
140
+ this.updateContent = this.updateContent.bind(this);
141
+ this.replaceContent = this.replaceContent.bind(this);
142
+ }
143
+ componentDidLoad() {
144
+ this.replaceContent(this.value);
225
145
  }
226
- componentWillLoad() {
227
- document.addEventListener('selectionchange', this.handleSelectionChange);
146
+ connectedCallback() {
147
+ window.addEventListener('click', this.handleWindowClick);
228
148
  }
229
149
  disconnectedCallback() {
230
- document.removeEventListener('selectionchange', this.handleSelectionChange);
150
+ window.removeEventListener('click', this.handleWindowClick);
231
151
  }
232
- async replaceTriggeredValue(data) {
233
- this.updateTriggerValue(data);
234
- this.clearTriggerState();
235
- this.valueChanged.emit();
152
+ replaceContent(newValue, oldValue) {
153
+ if (newValue != null && newValue !== oldValue) {
154
+ const matches = newValue.match(/[a-z]*:[0-9a-z-]{36}/g);
155
+ const replacementsMap = this.replacements.reduce((map, r) => (Object.assign(Object.assign({}, map), { [createResultUrn(r)]: r })), {});
156
+ let nextSubstr = newValue;
157
+ const parts = matches != null
158
+ ? matches === null || matches === void 0 ? void 0 : matches.reduce((res, m) => {
159
+ if (replacementsMap[m] != null) {
160
+ const urn = createResultUrn(replacementsMap[m]);
161
+ const index = nextSubstr.indexOf(urn);
162
+ const before = nextSubstr.substring(0, index);
163
+ const after = nextSubstr.substring(index + urn.length);
164
+ const replacement = createSearchResultReplacement(replacementsMap[m], before);
165
+ nextSubstr = after;
166
+ return [
167
+ ...res,
168
+ replacement.before,
169
+ replacement.beforeSpace,
170
+ replacement.result,
171
+ ];
172
+ }
173
+ return res;
174
+ }, [])
175
+ : [];
176
+ this.rawElements = [...parts, createTextNode(nextSubstr)];
177
+ this.updateContent(this.replacements);
178
+ }
236
179
  }
237
- async getEditableContent() {
238
- var _a;
239
- return (_a = this.contentEl) === null || _a === void 0 ? void 0 : _a.childNodes;
180
+ updateContent(newValue, oldValue) {
181
+ if (this.contentEl != null && !deepEqual(newValue, oldValue)) {
182
+ this.contentEl.innerHTML = '';
183
+ this.displayedElements = this.rawElements.map((el) => {
184
+ const raw = el instanceof HTMLElement ? el.innerText : el.textContent;
185
+ const replacement = this.replacements.find((r) => raw === null || raw === void 0 ? void 0 : raw.includes(r.id));
186
+ if (raw != null && replacement != null) {
187
+ const replacementElement = this.createReplacedElement(raw, replacement);
188
+ return replacementElement;
189
+ }
190
+ return el;
191
+ });
192
+ this.displayedElements.forEach((el) => {
193
+ var _a;
194
+ (_a = this.contentEl) === null || _a === void 0 ? void 0 : _a.appendChild(typeof el === 'string' ? createTextNode(el) : el);
195
+ });
196
+ if (this.lastReplacedSpace != null) {
197
+ this.moveCursorToNodeEnd(this.lastReplacedSpace);
198
+ }
199
+ this.inputChanged.emit(this.getContentAsString());
200
+ }
240
201
  }
241
202
  render() {
242
203
  var _a;
@@ -245,11 +206,91 @@ export class SearchBar {
245
206
  filled: this.variant === 'filled',
246
207
  underlined: this.variant === 'underlined',
247
208
  blank: this.variant === 'blank',
209
+ disabled: this.disabled,
248
210
  });
249
- return (h(Host, null, h("div", { class: classes }, h("span", { class: "content-input", ref: (ref) => (this.contentEl = ref), role: "textbox", contenteditable: "true", "aria-multiline": "true", "data-placeholder": this.placeholder, onKeyDown: this.handleKeyDown, onKeyUp: this.handleKeyUp, onBlur: this.handleBlur, onFocus: this.handleFocus, onPointerDown: this.handleInputPointerEvent, onPointerUp: this.handleInputPointerEvent })), h("vertex-result-list", { position: this.cursorPosition, placement: this.placement, open: this.open &&
211
+ return (h(Host, null, h("div", { class: classes }, h("span", { class: "content-input", role: "textbox", contentEditable: "true", "aria-multiline": "true", "data-placeholder": this.placeholder, ref: (el) => (this.contentEl = el), onKeyDown: this.handleKeyDown, onKeyUp: this.handleCursorPositionUpdate, onClick: this.handleCursorPositionUpdate, onInput: this.handleInput, onFocus: this.handleFocus, onBlur: this.handleBlur })), h("vertex-result-list", { position: this.cursorPosition, placement: this.placement, open: this.hasTriggered &&
250
212
  this.resultItems != null &&
251
- this.resultItems.length > 0 &&
252
- this.cursorPosition != null, items: (_a = this.resultItems) !== null && _a !== void 0 ? _a : [], onPointerDown: this.handleResultPointerDown, onEnterPressed: (event) => this.resultsEnterPressed.emit(event.detail) }, h("slot", { name: "results" })), h("slot", { name: "replaced" })));
213
+ this.resultItems.length > 0, items: (_a = this.resultItems) !== null && _a !== void 0 ? _a : [], onEnterPressed: this.handleResultClick, onResultClick: this.handleResultClick }, h("slot", { name: "results" })), h("slot", { name: "replaced" })));
214
+ }
215
+ handleKeyDown(event) {
216
+ if (this.hasTriggered && this.breakCharacters.includes(event.key)) {
217
+ this.hasTriggered = false;
218
+ this.triggeredRange = undefined;
219
+ this.triggeredElement = undefined;
220
+ }
221
+ }
222
+ handleKeyUp(event) {
223
+ this.handleCursorPositionUpdate();
224
+ this.cursorPosition = this.getCursorPosition();
225
+ }
226
+ async handleInput() {
227
+ this.inputChanged.emit(this.getContentAsString());
228
+ }
229
+ handleClick() {
230
+ this.handleCursorPositionUpdate();
231
+ }
232
+ handleWindowClick(event) {
233
+ if (event.target instanceof HTMLElement &&
234
+ event.target.getAttribute('data-replaced') === 'true' &&
235
+ event.target.nextSibling != null) {
236
+ this.moveCursorToNodeEnd(event.target.nextSibling, true);
237
+ }
238
+ }
239
+ handleFocus(event) {
240
+ this.inputFocus.emit(event);
241
+ }
242
+ handleBlur(event) {
243
+ this.hasTriggered = false;
244
+ this.inputBlur.emit(event);
245
+ }
246
+ handleResultClick(event) {
247
+ var _a;
248
+ const triggeredRange = this.triggeredRange;
249
+ const triggeredElement = this.triggeredElement;
250
+ const value = triggeredElement instanceof HTMLElement
251
+ ? triggeredElement.innerText
252
+ : triggeredElement === null || triggeredElement === void 0 ? void 0 : triggeredElement.textContent;
253
+ if (this.contentEl != null &&
254
+ triggeredRange != null &&
255
+ triggeredElement != null &&
256
+ value != null) {
257
+ const triggeredValue = (_a = this.readTriggerValue(value, triggeredRange.startOffset)) !== null && _a !== void 0 ? _a : '';
258
+ const split = value.split(triggeredValue);
259
+ const before = split[0];
260
+ const after = split[1];
261
+ const replacement = createSearchResultReplacement(event.detail, before, after);
262
+ this.lastReplacedSpace = replacement.afterSpace;
263
+ this.rawElements = Array.from(this.contentEl.childNodes).reduce((re, e) => {
264
+ // TODO: see if we can do anything about the collapse to single line in safari
265
+ if (this.isIdenticalElement(e, triggeredElement)) {
266
+ return [...re, ...getNodesForSearchResultReplacement(replacement)];
267
+ }
268
+ else {
269
+ return [...re, e];
270
+ }
271
+ }, []);
272
+ this.hasTriggered = false;
273
+ this.replacements = [
274
+ ...this.replacements.filter((r) => r.id !== event.detail.id),
275
+ event.detail,
276
+ ];
277
+ this.resultReplaced.emit(event.detail);
278
+ }
279
+ }
280
+ getCursorPosition() {
281
+ var _a;
282
+ const selection = getWindowSelection();
283
+ if (selection != null && selection.rangeCount > 0) {
284
+ const cursorBounds = selection.getRangeAt(0).getBoundingClientRect();
285
+ const contentBounds = (_a = this.contentEl) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
286
+ const cursorBottom = cursorBounds.bottom || (contentBounds === null || contentBounds === void 0 ? void 0 : contentBounds.bottom) || 0;
287
+ const cursorTop = cursorBounds.top || (contentBounds === null || contentBounds === void 0 ? void 0 : contentBounds.top) || 0;
288
+ return {
289
+ x: cursorBounds.left || (contentBounds === null || contentBounds === void 0 ? void 0 : contentBounds.left) || 0,
290
+ y: this.placement.includes('top') ? cursorTop : cursorBottom,
291
+ };
292
+ }
293
+ throw new Error('Unable to retrieve window selection.');
253
294
  }
254
295
  static get is() { return "vertex-search-bar"; }
255
296
  static get encapsulation() { return "scoped"; }
@@ -281,64 +322,47 @@ export class SearchBar {
281
322
  "optional": false,
282
323
  "docs": {
283
324
  "tags": [],
284
- "text": ""
325
+ "text": "The search bar variant to display.\n\nPossible options are:\n- 'standard' (default)\n- 'filled'\n- 'underlined'\n- 'blank"
285
326
  },
286
327
  "attribute": "variant",
287
328
  "reflect": false,
288
329
  "defaultValue": "'standard'"
289
330
  },
290
- "resultItems": {
291
- "type": "unknown",
292
- "mutable": false,
293
- "complexType": {
294
- "original": "Result[]",
295
- "resolved": "Result[] | undefined",
296
- "references": {
297
- "Result": {
298
- "location": "import",
299
- "path": "../result-list/result-list"
300
- }
301
- }
302
- },
303
- "required": false,
304
- "optional": true,
305
- "docs": {
306
- "tags": [],
307
- "text": ""
308
- }
309
- },
310
- "triggerCharacters": {
311
- "type": "unknown",
331
+ "disabled": {
332
+ "type": "boolean",
312
333
  "mutable": false,
313
334
  "complexType": {
314
- "original": "string[]",
315
- "resolved": "string[]",
335
+ "original": "boolean",
336
+ "resolved": "boolean",
316
337
  "references": {}
317
338
  },
318
339
  "required": false,
319
340
  "optional": false,
320
341
  "docs": {
321
342
  "tags": [],
322
- "text": ""
343
+ "text": "Whether this search bar is disabled."
323
344
  },
324
- "defaultValue": "[]"
345
+ "attribute": "disabled",
346
+ "reflect": false,
347
+ "defaultValue": "false"
325
348
  },
326
349
  "triggerCharacter": {
327
350
  "type": "string",
328
351
  "mutable": false,
329
352
  "complexType": {
330
353
  "original": "string",
331
- "resolved": "string | undefined",
354
+ "resolved": "string",
332
355
  "references": {}
333
356
  },
334
357
  "required": false,
335
- "optional": true,
358
+ "optional": false,
336
359
  "docs": {
337
360
  "tags": [],
338
- "text": ""
361
+ "text": "The character indicating that we should trigger a search.\n\nWhen this character is encountered, the text following it\n(omitting `breakCharacters`) will be emitted as a `searchChanged`\nevent."
339
362
  },
340
363
  "attribute": "trigger-character",
341
- "reflect": false
364
+ "reflect": false,
365
+ "defaultValue": "'@'"
342
366
  },
343
367
  "breakCharacters": {
344
368
  "type": "unknown",
@@ -352,29 +376,57 @@ export class SearchBar {
352
376
  "optional": false,
353
377
  "docs": {
354
378
  "tags": [],
355
- "text": ""
379
+ "text": "Characters that should trigger a \"break\" of the search. If\na value in this list is encountered when trying to parse back\nto the `triggerCharacter`, this will hide the result list."
356
380
  },
357
- "defaultValue": "[' ']"
381
+ "defaultValue": "[' ', '\\n']"
358
382
  },
359
- "debounce": {
360
- "type": "number",
383
+ "resultItems": {
384
+ "type": "unknown",
361
385
  "mutable": false,
362
386
  "complexType": {
363
- "original": "number",
364
- "resolved": "number",
365
- "references": {}
387
+ "original": "Result[]",
388
+ "resolved": "Result[] | undefined",
389
+ "references": {
390
+ "Result": {
391
+ "location": "import",
392
+ "path": "../result-list/result-list"
393
+ }
394
+ }
366
395
  },
367
396
  "required": false,
368
- "optional": false,
397
+ "optional": true,
369
398
  "docs": {
370
399
  "tags": [],
371
- "text": ""
400
+ "text": "The items to display in this search bar's `<vertex-result-list>`.\n\nThis can be used alongside the `searchChanged` event to query for\nresults to display."
401
+ }
402
+ },
403
+ "placement": {
404
+ "type": "string",
405
+ "mutable": false,
406
+ "complexType": {
407
+ "original": "PopoverPlacement",
408
+ "resolved": "\"bottom\" | \"bottom-end\" | \"bottom-start\" | \"left\" | \"left-end\" | \"left-start\" | \"right\" | \"right-end\" | \"right-start\" | \"top\" | \"top-end\" | \"top-start\"",
409
+ "references": {
410
+ "PopoverPlacement": {
411
+ "location": "import",
412
+ "path": "../popover/popover"
413
+ }
414
+ }
372
415
  },
373
- "attribute": "debounce",
416
+ "required": false,
417
+ "optional": false,
418
+ "docs": {
419
+ "tags": [{
420
+ "name": "see",
421
+ "text": " (PopoverPlacement)"
422
+ }],
423
+ "text": "The placement of the result list for this search bar.\n\nCorresponds to the value for the underlying <vertex-popover>\nplacement value."
424
+ },
425
+ "attribute": "placement",
374
426
  "reflect": false,
375
- "defaultValue": "100"
427
+ "defaultValue": "'bottom-start'"
376
428
  },
377
- "placeholder": {
429
+ "value": {
378
430
  "type": "string",
379
431
  "mutable": false,
380
432
  "complexType": {
@@ -386,21 +438,38 @@ export class SearchBar {
386
438
  "optional": true,
387
439
  "docs": {
388
440
  "tags": [],
389
- "text": "Placeholder for text input."
441
+ "text": "The initial value of this search bar. This will be used in\ncombination with the value of `replacements` to display existing\nreplaced values."
390
442
  },
391
- "attribute": "placeholder",
443
+ "attribute": "value",
392
444
  "reflect": false
393
445
  },
394
- "placement": {
446
+ "placeholder": {
395
447
  "type": "string",
396
448
  "mutable": false,
397
449
  "complexType": {
398
- "original": "PopoverPlacement",
399
- "resolved": "\"bottom\" | \"bottom-end\" | \"bottom-start\" | \"left\" | \"left-end\" | \"left-start\" | \"right\" | \"right-end\" | \"right-start\" | \"top\" | \"top-end\" | \"top-start\"",
450
+ "original": "string",
451
+ "resolved": "string | undefined",
452
+ "references": {}
453
+ },
454
+ "required": false,
455
+ "optional": true,
456
+ "docs": {
457
+ "tags": [],
458
+ "text": "A placeholder to display when no text has been entered."
459
+ },
460
+ "attribute": "placeholder",
461
+ "reflect": false
462
+ },
463
+ "replacements": {
464
+ "type": "unknown",
465
+ "mutable": true,
466
+ "complexType": {
467
+ "original": "Result[]",
468
+ "resolved": "Result[]",
400
469
  "references": {
401
- "PopoverPlacement": {
470
+ "Result": {
402
471
  "location": "import",
403
- "path": "../popover/popover"
472
+ "path": "../result-list/result-list"
404
473
  }
405
474
  }
406
475
  },
@@ -408,32 +477,29 @@ export class SearchBar {
408
477
  "optional": false,
409
478
  "docs": {
410
479
  "tags": [],
411
- "text": ""
480
+ "text": "Replaced result values. This will be used in combination with the\nvalue of `value` to display existing replaced values.\n\nThis value will be automatically updated as `Result`s are clicked\nwithin this search bar's `<vertex-result-list>`."
412
481
  },
413
- "attribute": "placement",
414
- "reflect": false,
415
- "defaultValue": "'bottom-start'"
482
+ "defaultValue": "[]"
416
483
  }
417
484
  };
418
485
  }
419
486
  static get states() {
420
487
  return {
421
488
  "cursorPosition": {},
422
- "open": {},
423
- "triggerKey": {},
424
- "triggerRange": {}
489
+ "displayedElements": {},
490
+ "hasTriggered": {}
425
491
  };
426
492
  }
427
493
  static get events() {
428
494
  return [{
429
- "method": "triggerCharacterPressed",
430
- "name": "triggerCharacterPressed",
495
+ "method": "searchChanged",
496
+ "name": "searchChanged",
431
497
  "bubbles": true,
432
498
  "cancelable": true,
433
499
  "composed": true,
434
500
  "docs": {
435
501
  "tags": [],
436
- "text": ""
502
+ "text": "Emitted when the value of the current search triggered by\nthe specified `triggerCharacter` has changed."
437
503
  },
438
504
  "complexType": {
439
505
  "original": "string",
@@ -441,48 +507,49 @@ export class SearchBar {
441
507
  "references": {}
442
508
  }
443
509
  }, {
444
- "method": "valueChanged",
445
- "name": "valueChanged",
510
+ "method": "inputChanged",
511
+ "name": "inputChanged",
446
512
  "bubbles": true,
447
513
  "cancelable": true,
448
514
  "composed": true,
449
515
  "docs": {
450
516
  "tags": [],
451
- "text": "Emitted when the value has changed."
517
+ "text": "Emitted when the value of the input has changed."
452
518
  },
453
519
  "complexType": {
454
- "original": "void",
455
- "resolved": "void",
520
+ "original": "string",
521
+ "resolved": "string",
456
522
  "references": {}
457
523
  }
458
524
  }, {
459
- "method": "inputFocus",
460
- "name": "inputFocus",
525
+ "method": "resultReplaced",
526
+ "name": "resultReplaced",
461
527
  "bubbles": true,
462
528
  "cancelable": true,
463
529
  "composed": true,
464
530
  "docs": {
465
531
  "tags": [],
466
- "text": "Emitted when input focused"
532
+ "text": "Emitted when a result has been selected to replace the trigger text.\n\nIncludes the ID of the `Result` selected."
467
533
  },
468
534
  "complexType": {
469
- "original": "FocusEvent",
470
- "resolved": "FocusEvent",
535
+ "original": "Result",
536
+ "resolved": "{ id: string; type: string; } & Record<string, unknown>",
471
537
  "references": {
472
- "FocusEvent": {
473
- "location": "global"
538
+ "Result": {
539
+ "location": "import",
540
+ "path": "../result-list/result-list"
474
541
  }
475
542
  }
476
543
  }
477
544
  }, {
478
- "method": "inputBlur",
479
- "name": "inputBlur",
545
+ "method": "inputFocus",
546
+ "name": "inputFocus",
480
547
  "bubbles": true,
481
548
  "cancelable": true,
482
549
  "composed": true,
483
550
  "docs": {
484
551
  "tags": [],
485
- "text": "Emitted when input blurred"
552
+ "text": "Emitted when the input is focused."
486
553
  },
487
554
  "complexType": {
488
555
  "original": "FocusEvent",
@@ -494,74 +561,34 @@ export class SearchBar {
494
561
  }
495
562
  }
496
563
  }, {
497
- "method": "resultsEnterPressed",
498
- "name": "resultsEnterPressed",
564
+ "method": "inputBlur",
565
+ "name": "inputBlur",
499
566
  "bubbles": true,
500
567
  "cancelable": true,
501
568
  "composed": true,
502
569
  "docs": {
503
570
  "tags": [],
504
- "text": "Emitted when enter is pressed an a result is highlighted."
571
+ "text": "Emitted when the input is blurred."
505
572
  },
506
573
  "complexType": {
507
- "original": "Result",
508
- "resolved": "{ [x: string]: unknown; }",
574
+ "original": "FocusEvent",
575
+ "resolved": "FocusEvent",
509
576
  "references": {
510
- "Result": {
511
- "location": "import",
512
- "path": "../result-list/result-list"
577
+ "FocusEvent": {
578
+ "location": "global"
513
579
  }
514
580
  }
515
581
  }
516
582
  }];
517
583
  }
518
- static get methods() {
519
- return {
520
- "replaceTriggeredValue": {
521
- "complexType": {
522
- "signature": "(data: Record<string, unknown>) => Promise<void>",
523
- "parameters": [{
524
- "tags": [],
525
- "text": ""
526
- }],
527
- "references": {
528
- "Promise": {
529
- "location": "global"
530
- },
531
- "Record": {
532
- "location": "global"
533
- }
534
- },
535
- "return": "Promise<void>"
536
- },
537
- "docs": {
538
- "text": "",
539
- "tags": []
540
- }
541
- },
542
- "getEditableContent": {
543
- "complexType": {
544
- "signature": "() => Promise<NodeListOf<ChildNode> | undefined>",
545
- "parameters": [],
546
- "references": {
547
- "Promise": {
548
- "location": "global"
549
- },
550
- "NodeListOf": {
551
- "location": "global"
552
- },
553
- "ChildNode": {
554
- "location": "global"
555
- }
556
- },
557
- "return": "Promise<NodeListOf<ChildNode> | undefined>"
558
- },
559
- "docs": {
560
- "text": "",
561
- "tags": []
562
- }
563
- }
564
- };
565
- }
566
584
  static get elementRef() { return "hostEl"; }
585
+ static get watchers() {
586
+ return [{
587
+ "propName": "value",
588
+ "methodName": "replaceContent"
589
+ }, {
590
+ "propName": "replacements",
591
+ "methodName": "updateContent"
592
+ }];
593
+ }
567
594
  }