@vertexvis/ui 0.1.0-canary.7 → 0.1.0-canary.9

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 (67) hide show
  1. package/dist/cjs/components.cjs.js +1 -1
  2. package/dist/cjs/{icon-460fd0f5.js → icon-21deee4d.js} +1 -1
  3. package/dist/cjs/{icon-button-786427d6.js → icon-button-b7ed3b48.js} +1 -1
  4. package/dist/cjs/{icon-helper-ba408f49.js → icon-helper-31ee6ce0.js} +5 -0
  5. package/dist/cjs/index.cjs.js +5 -5
  6. package/dist/cjs/loader.cjs.js +1 -1
  7. package/dist/cjs/{result-list-1e592c3c.js → result-list-241ffe8d.js} +46 -2
  8. package/dist/cjs/search-bar-91cbcd07.js +421 -0
  9. package/dist/cjs/vertex-icon-button.cjs.entry.js +2 -2
  10. package/dist/cjs/vertex-icon.cjs.entry.js +2 -2
  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/collection/collection-manifest.json +1 -1
  14. package/dist/collection/components/icon/icon-helper.js +3 -0
  15. package/dist/collection/components/icon/icon.js +1 -1
  16. package/dist/collection/components/icon/icons/compare.js +2 -0
  17. package/dist/collection/components/icon-button/icon-button.js +1 -1
  18. package/dist/collection/components/result-list/result-list.js +52 -5
  19. package/dist/collection/components/result-list/types.js +1 -0
  20. package/dist/collection/components/search-bar/dom.js +12 -0
  21. package/dist/collection/components/search-bar/lib.js +35 -15
  22. package/dist/collection/components/search-bar/search-bar.css +0 -16
  23. package/dist/collection/components/search-bar/search-bar.js +380 -324
  24. package/dist/collection/types/icon.js +1 -0
  25. package/dist/collection/util/templates/element-pool.js +19 -1
  26. package/dist/components/components.esm.js +1 -1
  27. package/dist/components/index.esm.js +1 -1
  28. package/dist/components/p-1c7dc57e.js +1 -0
  29. package/dist/components/p-646ac7e7.entry.js +1 -0
  30. package/dist/components/p-6b862967.js +1 -0
  31. package/dist/components/p-cfe369bf.entry.js +1 -0
  32. package/dist/components/{p-103249b4.js → p-daabd39c.js} +1 -1
  33. package/dist/components/p-db34f10c.js +1 -0
  34. package/dist/components/p-ee496965.entry.js +1 -0
  35. package/dist/components/p-f03fe304.entry.js +1 -0
  36. package/dist/components/{p-ca52a423.js → p-fe0f0f64.js} +1 -1
  37. package/dist/esm/components.js +1 -1
  38. package/dist/esm/{icon-d37150b4.js → icon-8b8d7617.js} +1 -1
  39. package/dist/esm/{icon-button-aad3c0e7.js → icon-button-192f2555.js} +1 -1
  40. package/dist/esm/{icon-helper-83f10f73.js → icon-helper-a6be8de9.js} +5 -0
  41. package/dist/esm/index.js +5 -5
  42. package/dist/esm/loader.js +1 -1
  43. package/dist/esm/{result-list-36cfb08a.js → result-list-16c6afbd.js} +46 -2
  44. package/dist/esm/search-bar-f12a3599.js +419 -0
  45. package/dist/esm/vertex-icon-button.entry.js +2 -2
  46. package/dist/esm/vertex-icon.entry.js +2 -2
  47. package/dist/esm/vertex-result-list.entry.js +1 -1
  48. package/dist/esm/vertex-search-bar.entry.js +1 -1
  49. package/dist/types/components/icon/icons/compare.d.ts +3 -0
  50. package/dist/types/components/result-list/result-list.d.ts +6 -1
  51. package/dist/types/components/result-list/types.d.ts +4 -0
  52. package/dist/types/components/search-bar/dom.d.ts +3 -0
  53. package/dist/types/components/search-bar/lib.d.ts +24 -6
  54. package/dist/types/components/search-bar/search-bar.d.ts +124 -42
  55. package/dist/types/components.d.ts +81 -20
  56. package/dist/types/types/icon.d.ts +1 -0
  57. package/dist/types/util/templates/element-pool.d.ts +10 -1
  58. package/package.json +4 -3
  59. package/dist/cjs/search-bar-bb40cfa7.js +0 -290
  60. package/dist/components/p-03dbb28c.js +0 -1
  61. package/dist/components/p-19318fee.entry.js +0 -1
  62. package/dist/components/p-4224c2ad.js +0 -1
  63. package/dist/components/p-52739247.js +0 -1
  64. package/dist/components/p-7dba2574.entry.js +0 -1
  65. package/dist/components/p-ae6a3c46.entry.js +0 -1
  66. package/dist/components/p-f71fc166.entry.js +0 -1
  67. package/dist/esm/search-bar-59cc151d.js +0 -288
@@ -1,94 +1,84 @@
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 { createResultUri, 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();
26
- };
27
- this.handleKeyUp = (event) => {
28
- if (event.key === 'Backspace') {
29
- this.updateTriggerState(this.getSelectionSubstring());
30
- }
31
- if (event.key === 'Enter') {
32
- this.clearTriggerState();
33
- }
34
- this.valueChanged.emit();
9
+ this.rawElements = [];
10
+ this.isIdenticalElement = (child, other) => {
11
+ return (child === this.triggeredElement ||
12
+ this.getTextContent(child) === this.getTextContent(other));
35
13
  };
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);
51
- }
14
+ this.getTextContent = (node) => {
15
+ var _a;
16
+ if (node instanceof HTMLElement) {
17
+ return node.innerText;
52
18
  }
19
+ return (_a = node.textContent) !== null && _a !== void 0 ? _a : '';
53
20
  };
54
- this.triggerText = () => {
21
+ /**
22
+ * Inspects the cursor position for the following conditions:
23
+ *
24
+ * 1. The cursor has moved to text that contains a valid string
25
+ * and trigger character, indicating we need to dispatch a
26
+ * `searchChanged` event and display the results.
27
+ *
28
+ * 2. The cursor has moved within text that contains a valid string
29
+ * and trigger character, indicating we need to dispatch a
30
+ * `searchChanged` event to update the currently displayed results.
31
+ *
32
+ * 3. The cursor has moved to text that does not contain a valid
33
+ * string and trigger character, indicating we need to hide any
34
+ * displayed search results.
35
+ */
36
+ this.handleCursorPositionUpdate = () => {
55
37
  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 : '');
38
+ const windowRange = (_a = getWindowSelection()) === null || _a === void 0 ? void 0 : _a.getRangeAt(0);
39
+ if (windowRange != null) {
40
+ const triggerText = this.readTriggerValue((_b = windowRange.commonAncestorContainer.textContent) !== null && _b !== void 0 ? _b : '', windowRange.startOffset);
41
+ if (!this.hasTriggered && triggerText != null) {
42
+ this.hasTriggered = true;
43
+ this.triggeredRange = windowRange;
44
+ this.triggeredElement = windowRange.commonAncestorContainer;
45
+ this.searchChanged.emit(triggerText.replace(this.triggerCharacter, ''));
46
+ }
47
+ else if (this.hasTriggered && triggerText != null) {
48
+ this.triggeredRange = windowRange;
49
+ this.triggeredElement = windowRange.commonAncestorContainer;
50
+ this.searchChanged.emit(triggerText.replace(this.triggerCharacter, ''));
51
+ }
52
+ else {
53
+ this.hasTriggered = false;
54
+ this.triggeredRange = undefined;
55
+ this.triggeredElement = undefined;
56
+ }
61
57
  }
62
- return '';
58
+ this.cursorPosition = this.getCursorPosition();
63
59
  };
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);
60
+ this.readTriggerValue = (value, fromIndex) => {
61
+ const adjustedValue = value.replace(String.fromCharCode(160), ' ');
62
+ const beforeSubstr = adjustedValue.substring(0, fromIndex);
63
+ const afterSubstr = adjustedValue.substring(fromIndex);
64
+ const adjustedBeforeSubstr = beforeSubstr.includes(this.triggerCharacter)
65
+ ? beforeSubstr.substring(beforeSubstr.lastIndexOf(this.triggerCharacter))
66
+ : '';
67
+ const adjustedAfterSubstr = afterSubstr.substring(0, this.firstIndexOfBreakCharacter(afterSubstr));
68
+ const result = `${adjustedBeforeSubstr}${adjustedAfterSubstr}`;
69
+ return result.includes(this.triggerCharacter) &&
70
+ !this.includesBreakCharacter(result)
71
+ ? result
72
+ : undefined;
72
73
  };
73
- this.clearTriggerTimeout = () => {
74
- if (this.triggerTimeout != null) {
75
- clearTimeout(this.triggerTimeout);
76
- this.triggerTimeout = undefined;
77
- }
74
+ this.includesBreakCharacter = (value) => {
75
+ return this.breakCharacters.some((bc) => value.includes(bc));
78
76
  };
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
- }
77
+ this.firstIndexOfBreakCharacter = (value) => {
78
+ const indices = this.breakCharacters
79
+ .map((bc) => value.indexOf(bc))
80
+ .filter((i) => i >= 0);
81
+ return indices.length > 0 ? Math.min(...indices) : value.length;
92
82
  };
93
83
  this.moveCursorToNodeEnd = (node, collapseToStart = false) => {
94
84
  const selection = getWindowSelection();
@@ -100,143 +90,126 @@ export class SearchBar {
100
90
  selection.addRange(range);
101
91
  }
102
92
  };
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
- }
93
+ this.getContentAsString = () => {
94
+ if (this.contentEl != null) {
95
+ return trimNonstandardSpaces(Array.from(this.contentEl.childNodes).reduce((res, n) => {
96
+ var _a;
97
+ if (n instanceof HTMLElement &&
98
+ n.getAttribute('data-replaced') === 'true') {
99
+ return `${res}${n.getAttribute('data-original')}`;
100
+ }
101
+ else if (n instanceof HTMLElement) {
102
+ return `${res}${n.innerText}`;
103
+ }
104
+ else {
105
+ return `${res}${(_a = n.textContent) !== null && _a !== void 0 ? _a : ''}`;
106
+ }
107
+ }, ''));
111
108
  }
109
+ return '';
112
110
  };
113
- this.createReplacedElement = (data) => {
111
+ this.createReplacedElement = (original, data) => {
114
112
  const template = this.hostEl.querySelector('template[slot="replaced"]');
115
113
  if (template != null) {
116
114
  const instance = generateInstanceFromTemplate(template);
117
115
  instance.bindings.bind(data);
116
+ instance.element.id = data.id;
118
117
  instance.element.style.display = 'inline-block';
119
118
  instance.element.contentEditable = 'false';
119
+ instance.element.tabIndex = -1;
120
120
  instance.element.setAttribute('data-replaced', 'true');
121
+ instance.element.setAttribute('data-original', original);
121
122
  return instance.element;
122
123
  }
123
124
  else {
124
125
  throw new Error('Replaced template not defined.');
125
126
  }
126
127
  };
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
128
  this.variant = 'standard';
129
+ this.disabled = false;
130
+ this.triggerCharacter = '@';
131
+ this.breakCharacters = [' ', '\n'];
214
132
  this.resultItems = undefined;
215
- this.triggerCharacters = [];
216
- this.triggerCharacter = undefined;
217
- this.breakCharacters = [' '];
218
- this.debounce = 100;
219
- this.placeholder = undefined;
220
133
  this.placement = 'bottom-start';
134
+ this.value = undefined;
135
+ this.placeholder = undefined;
136
+ this.replacements = [];
137
+ this.replacementUriType = 'user';
221
138
  this.cursorPosition = undefined;
222
- this.open = false;
223
- this.triggerKey = undefined;
224
- this.triggerRange = undefined;
139
+ this.displayedElements = [];
140
+ this.hasTriggered = false;
141
+ this.handleKeyDown = this.handleKeyDown.bind(this);
142
+ this.handleKeyUp = this.handleKeyUp.bind(this);
143
+ this.handleResultClick = this.handleResultClick.bind(this);
144
+ this.handleClick = this.handleClick.bind(this);
145
+ this.handleWindowClick = this.handleWindowClick.bind(this);
146
+ this.handleInput = this.handleInput.bind(this);
147
+ this.handleBlur = this.handleBlur.bind(this);
148
+ this.handleFocus = this.handleFocus.bind(this);
149
+ this.handleCursorPositionUpdate =
150
+ this.handleCursorPositionUpdate.bind(this);
151
+ this.updateContent = this.updateContent.bind(this);
152
+ this.replaceContent = this.replaceContent.bind(this);
153
+ }
154
+ componentDidLoad() {
155
+ this.replaceContent(this.value);
225
156
  }
226
- componentWillLoad() {
227
- document.addEventListener('selectionchange', this.handleSelectionChange);
157
+ connectedCallback() {
158
+ window.addEventListener('click', this.handleWindowClick);
228
159
  }
229
160
  disconnectedCallback() {
230
- document.removeEventListener('selectionchange', this.handleSelectionChange);
161
+ window.removeEventListener('click', this.handleWindowClick);
231
162
  }
232
- async replaceTriggeredValue(data) {
233
- this.updateTriggerValue(data);
234
- this.clearTriggerState();
235
- this.valueChanged.emit();
163
+ replaceContent(newValue, oldValue) {
164
+ if (newValue != null && newValue !== oldValue) {
165
+ const pattern = `${this.replacementUriType}:[0-9a-z-]{36}`;
166
+ const matches = newValue.match(new RegExp(pattern, 'g'));
167
+ const replacementsMap = this.replacements.reduce((map, r) => (Object.assign(Object.assign({}, map), { [createResultUri(r)]: r })), {});
168
+ let nextSubstr = newValue;
169
+ const parts = matches != null
170
+ ? matches === null || matches === void 0 ? void 0 : matches.reduce((res, m) => {
171
+ if (replacementsMap[m] != null) {
172
+ const urn = createResultUri(replacementsMap[m]);
173
+ const index = nextSubstr.indexOf(urn);
174
+ const before = nextSubstr.substring(0, index);
175
+ const after = nextSubstr.substring(index + urn.length);
176
+ const replacement = createSearchResultReplacement(replacementsMap[m], before);
177
+ nextSubstr = after;
178
+ return [
179
+ ...res,
180
+ replacement.before,
181
+ replacement.beforeSpace,
182
+ replacement.result,
183
+ ];
184
+ }
185
+ return res;
186
+ }, [])
187
+ : [];
188
+ this.rawElements = [...parts, createTextNode(nextSubstr)];
189
+ this.updateContent(this.replacements);
190
+ }
236
191
  }
237
- async getEditableContent() {
238
- var _a;
239
- return (_a = this.contentEl) === null || _a === void 0 ? void 0 : _a.childNodes;
192
+ updateContent(newValue, oldValue) {
193
+ if (this.contentEl != null && !deepEqual(newValue, oldValue)) {
194
+ this.contentEl.innerHTML = '';
195
+ this.displayedElements = this.rawElements.map((el) => {
196
+ const raw = el instanceof HTMLElement ? el.innerText : el.textContent;
197
+ const replacement = this.replacements.find((r) => raw === null || raw === void 0 ? void 0 : raw.includes(createResultUri(r)));
198
+ if (raw != null && replacement != null) {
199
+ const replacementElement = this.createReplacedElement(raw, replacement);
200
+ return replacementElement;
201
+ }
202
+ return el;
203
+ });
204
+ this.displayedElements.forEach((el) => {
205
+ var _a;
206
+ (_a = this.contentEl) === null || _a === void 0 ? void 0 : _a.appendChild(typeof el === 'string' ? createTextNode(el) : el);
207
+ });
208
+ if (this.lastReplacedSpace != null) {
209
+ this.moveCursorToNodeEnd(this.lastReplacedSpace);
210
+ }
211
+ this.inputChanged.emit(this.getContentAsString());
212
+ }
240
213
  }
241
214
  render() {
242
215
  var _a;
@@ -245,11 +218,90 @@ export class SearchBar {
245
218
  filled: this.variant === 'filled',
246
219
  underlined: this.variant === 'underlined',
247
220
  blank: this.variant === 'blank',
221
+ disabled: this.disabled,
248
222
  });
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 &&
223
+ 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
224
  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" })));
225
+ 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" })));
226
+ }
227
+ handleKeyDown(event) {
228
+ if (this.hasTriggered && this.breakCharacters.includes(event.key)) {
229
+ this.hasTriggered = false;
230
+ this.triggeredRange = undefined;
231
+ this.triggeredElement = undefined;
232
+ }
233
+ }
234
+ handleKeyUp(event) {
235
+ this.handleCursorPositionUpdate();
236
+ this.cursorPosition = this.getCursorPosition();
237
+ }
238
+ async handleInput() {
239
+ this.inputChanged.emit(this.getContentAsString());
240
+ }
241
+ handleClick() {
242
+ this.handleCursorPositionUpdate();
243
+ }
244
+ handleWindowClick(event) {
245
+ if (event.target instanceof HTMLElement &&
246
+ event.target.getAttribute('data-replaced') === 'true' &&
247
+ event.target.nextSibling != null) {
248
+ this.moveCursorToNodeEnd(event.target.nextSibling, true);
249
+ }
250
+ }
251
+ handleFocus(event) {
252
+ this.inputFocus.emit(event);
253
+ }
254
+ handleBlur(event) {
255
+ this.hasTriggered = false;
256
+ this.inputBlur.emit(event);
257
+ }
258
+ handleResultClick(event) {
259
+ var _a;
260
+ const triggeredRange = this.triggeredRange;
261
+ const triggeredElement = this.triggeredElement;
262
+ const value = triggeredElement instanceof HTMLElement
263
+ ? triggeredElement.innerText
264
+ : triggeredElement === null || triggeredElement === void 0 ? void 0 : triggeredElement.textContent;
265
+ if (this.contentEl != null &&
266
+ triggeredRange != null &&
267
+ triggeredElement != null &&
268
+ value != null) {
269
+ const triggeredValue = (_a = this.readTriggerValue(value, triggeredRange.startOffset)) !== null && _a !== void 0 ? _a : '';
270
+ const split = value.split(triggeredValue);
271
+ const before = split[0];
272
+ const after = split[1];
273
+ const replacement = createSearchResultReplacement(event.detail, before, after);
274
+ this.lastReplacedSpace = replacement.afterSpace;
275
+ this.rawElements = Array.from(this.contentEl.childNodes).reduce((re, e) => {
276
+ if (this.isIdenticalElement(e, triggeredElement)) {
277
+ return [...re, ...getNodesForSearchResultReplacement(replacement)];
278
+ }
279
+ else {
280
+ return [...re, e];
281
+ }
282
+ }, []);
283
+ this.hasTriggered = false;
284
+ this.resultReplaced.emit(event.detail);
285
+ this.replacements = [
286
+ ...this.replacements.filter((r) => r.id !== event.detail.id),
287
+ event.detail,
288
+ ];
289
+ }
290
+ }
291
+ getCursorPosition() {
292
+ var _a;
293
+ const selection = getWindowSelection();
294
+ if (selection != null && selection.rangeCount > 0) {
295
+ const cursorBounds = selection.getRangeAt(0).getBoundingClientRect();
296
+ const contentBounds = (_a = this.contentEl) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
297
+ const cursorBottom = cursorBounds.bottom || (contentBounds === null || contentBounds === void 0 ? void 0 : contentBounds.bottom) || 0;
298
+ const cursorTop = cursorBounds.top || (contentBounds === null || contentBounds === void 0 ? void 0 : contentBounds.top) || 0;
299
+ return {
300
+ x: cursorBounds.left || (contentBounds === null || contentBounds === void 0 ? void 0 : contentBounds.left) || 0,
301
+ y: this.placement.includes('top') ? cursorTop : cursorBottom,
302
+ };
303
+ }
304
+ throw new Error('Unable to retrieve window selection.');
253
305
  }
254
306
  static get is() { return "vertex-search-bar"; }
255
307
  static get encapsulation() { return "scoped"; }
@@ -281,64 +333,47 @@ export class SearchBar {
281
333
  "optional": false,
282
334
  "docs": {
283
335
  "tags": [],
284
- "text": ""
336
+ "text": "The search bar variant to display.\n\nPossible options are:\n- 'standard' (default)\n- 'filled'\n- 'underlined'\n- 'blank"
285
337
  },
286
338
  "attribute": "variant",
287
339
  "reflect": false,
288
340
  "defaultValue": "'standard'"
289
341
  },
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",
342
+ "disabled": {
343
+ "type": "boolean",
312
344
  "mutable": false,
313
345
  "complexType": {
314
- "original": "string[]",
315
- "resolved": "string[]",
346
+ "original": "boolean",
347
+ "resolved": "boolean",
316
348
  "references": {}
317
349
  },
318
350
  "required": false,
319
351
  "optional": false,
320
352
  "docs": {
321
353
  "tags": [],
322
- "text": ""
354
+ "text": "Whether this search bar is disabled."
323
355
  },
324
- "defaultValue": "[]"
356
+ "attribute": "disabled",
357
+ "reflect": false,
358
+ "defaultValue": "false"
325
359
  },
326
360
  "triggerCharacter": {
327
361
  "type": "string",
328
362
  "mutable": false,
329
363
  "complexType": {
330
364
  "original": "string",
331
- "resolved": "string | undefined",
365
+ "resolved": "string",
332
366
  "references": {}
333
367
  },
334
368
  "required": false,
335
- "optional": true,
369
+ "optional": false,
336
370
  "docs": {
337
371
  "tags": [],
338
- "text": ""
372
+ "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
373
  },
340
374
  "attribute": "trigger-character",
341
- "reflect": false
375
+ "reflect": false,
376
+ "defaultValue": "'@'"
342
377
  },
343
378
  "breakCharacters": {
344
379
  "type": "unknown",
@@ -352,29 +387,57 @@ export class SearchBar {
352
387
  "optional": false,
353
388
  "docs": {
354
389
  "tags": [],
355
- "text": ""
390
+ "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
391
  },
357
- "defaultValue": "[' ']"
392
+ "defaultValue": "[' ', '\\n']"
358
393
  },
359
- "debounce": {
360
- "type": "number",
394
+ "resultItems": {
395
+ "type": "unknown",
361
396
  "mutable": false,
362
397
  "complexType": {
363
- "original": "number",
364
- "resolved": "number",
365
- "references": {}
398
+ "original": "Result[]",
399
+ "resolved": "Result[] | undefined",
400
+ "references": {
401
+ "Result": {
402
+ "location": "import",
403
+ "path": "../result-list/types"
404
+ }
405
+ }
366
406
  },
367
407
  "required": false,
368
- "optional": false,
408
+ "optional": true,
369
409
  "docs": {
370
410
  "tags": [],
371
- "text": ""
411
+ "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."
412
+ }
413
+ },
414
+ "placement": {
415
+ "type": "string",
416
+ "mutable": false,
417
+ "complexType": {
418
+ "original": "PopoverPlacement",
419
+ "resolved": "\"bottom\" | \"bottom-end\" | \"bottom-start\" | \"left\" | \"left-end\" | \"left-start\" | \"right\" | \"right-end\" | \"right-start\" | \"top\" | \"top-end\" | \"top-start\"",
420
+ "references": {
421
+ "PopoverPlacement": {
422
+ "location": "import",
423
+ "path": "../popover/popover"
424
+ }
425
+ }
426
+ },
427
+ "required": false,
428
+ "optional": false,
429
+ "docs": {
430
+ "tags": [{
431
+ "name": "see",
432
+ "text": " (PopoverPlacement)"
433
+ }],
434
+ "text": "The placement of the result list for this search bar.\n\nCorresponds to the value for the underlying <vertex-popover>\nplacement value."
372
435
  },
373
- "attribute": "debounce",
436
+ "attribute": "placement",
374
437
  "reflect": false,
375
- "defaultValue": "100"
438
+ "defaultValue": "'bottom-start'"
376
439
  },
377
- "placeholder": {
440
+ "value": {
378
441
  "type": "string",
379
442
  "mutable": false,
380
443
  "complexType": {
@@ -386,21 +449,38 @@ export class SearchBar {
386
449
  "optional": true,
387
450
  "docs": {
388
451
  "tags": [],
389
- "text": "Placeholder for text input."
452
+ "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
453
  },
391
- "attribute": "placeholder",
454
+ "attribute": "value",
392
455
  "reflect": false
393
456
  },
394
- "placement": {
457
+ "placeholder": {
395
458
  "type": "string",
396
459
  "mutable": false,
397
460
  "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\"",
461
+ "original": "string",
462
+ "resolved": "string | undefined",
463
+ "references": {}
464
+ },
465
+ "required": false,
466
+ "optional": true,
467
+ "docs": {
468
+ "tags": [],
469
+ "text": "A placeholder to display when no text has been entered."
470
+ },
471
+ "attribute": "placeholder",
472
+ "reflect": false
473
+ },
474
+ "replacements": {
475
+ "type": "unknown",
476
+ "mutable": true,
477
+ "complexType": {
478
+ "original": "Result[]",
479
+ "resolved": "Result[]",
400
480
  "references": {
401
- "PopoverPlacement": {
481
+ "Result": {
402
482
  "location": "import",
403
- "path": "../popover/popover"
483
+ "path": "../result-list/types"
404
484
  }
405
485
  }
406
486
  },
@@ -408,32 +488,47 @@ export class SearchBar {
408
488
  "optional": false,
409
489
  "docs": {
410
490
  "tags": [],
411
- "text": ""
491
+ "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
492
  },
413
- "attribute": "placement",
493
+ "defaultValue": "[]"
494
+ },
495
+ "replacementUriType": {
496
+ "type": "string",
497
+ "mutable": false,
498
+ "complexType": {
499
+ "original": "string",
500
+ "resolved": "string",
501
+ "references": {}
502
+ },
503
+ "required": false,
504
+ "optional": false,
505
+ "docs": {
506
+ "tags": [],
507
+ "text": "The URI type of the values to replace. When `replacements` are\nevaluated, this value will be used as the scheme portion of the\nURI to detect matches.\n\nDefaults to `user`, and will match URIs composed with the `user`\nscheme and a UUID. E.g.\n`user:00000000-0000-0000-0000-000000000000`"
508
+ },
509
+ "attribute": "replacement-uri-type",
414
510
  "reflect": false,
415
- "defaultValue": "'bottom-start'"
511
+ "defaultValue": "'user'"
416
512
  }
417
513
  };
418
514
  }
419
515
  static get states() {
420
516
  return {
421
517
  "cursorPosition": {},
422
- "open": {},
423
- "triggerKey": {},
424
- "triggerRange": {}
518
+ "displayedElements": {},
519
+ "hasTriggered": {}
425
520
  };
426
521
  }
427
522
  static get events() {
428
523
  return [{
429
- "method": "triggerCharacterPressed",
430
- "name": "triggerCharacterPressed",
524
+ "method": "searchChanged",
525
+ "name": "searchChanged",
431
526
  "bubbles": true,
432
527
  "cancelable": true,
433
528
  "composed": true,
434
529
  "docs": {
435
530
  "tags": [],
436
- "text": ""
531
+ "text": "Emitted when the value of the current search triggered by\nthe specified `triggerCharacter` has changed."
437
532
  },
438
533
  "complexType": {
439
534
  "original": "string",
@@ -441,48 +536,49 @@ export class SearchBar {
441
536
  "references": {}
442
537
  }
443
538
  }, {
444
- "method": "valueChanged",
445
- "name": "valueChanged",
539
+ "method": "inputChanged",
540
+ "name": "inputChanged",
446
541
  "bubbles": true,
447
542
  "cancelable": true,
448
543
  "composed": true,
449
544
  "docs": {
450
545
  "tags": [],
451
- "text": "Emitted when the value has changed."
546
+ "text": "Emitted when the value of the input has changed."
452
547
  },
453
548
  "complexType": {
454
- "original": "void",
455
- "resolved": "void",
549
+ "original": "string",
550
+ "resolved": "string",
456
551
  "references": {}
457
552
  }
458
553
  }, {
459
- "method": "inputFocus",
460
- "name": "inputFocus",
554
+ "method": "resultReplaced",
555
+ "name": "resultReplaced",
461
556
  "bubbles": true,
462
557
  "cancelable": true,
463
558
  "composed": true,
464
559
  "docs": {
465
560
  "tags": [],
466
- "text": "Emitted when input focused"
561
+ "text": "Emitted when a result has been selected to replace the trigger text.\n\nIncludes the ID of the `Result` selected."
467
562
  },
468
563
  "complexType": {
469
- "original": "FocusEvent",
470
- "resolved": "FocusEvent",
564
+ "original": "Result",
565
+ "resolved": "{ id: string; type: string; } & Record<string, unknown>",
471
566
  "references": {
472
- "FocusEvent": {
473
- "location": "global"
567
+ "Result": {
568
+ "location": "import",
569
+ "path": "../result-list/types"
474
570
  }
475
571
  }
476
572
  }
477
573
  }, {
478
- "method": "inputBlur",
479
- "name": "inputBlur",
574
+ "method": "inputFocus",
575
+ "name": "inputFocus",
480
576
  "bubbles": true,
481
577
  "cancelable": true,
482
578
  "composed": true,
483
579
  "docs": {
484
580
  "tags": [],
485
- "text": "Emitted when input blurred"
581
+ "text": "Emitted when the input is focused."
486
582
  },
487
583
  "complexType": {
488
584
  "original": "FocusEvent",
@@ -494,74 +590,34 @@ export class SearchBar {
494
590
  }
495
591
  }
496
592
  }, {
497
- "method": "resultsEnterPressed",
498
- "name": "resultsEnterPressed",
593
+ "method": "inputBlur",
594
+ "name": "inputBlur",
499
595
  "bubbles": true,
500
596
  "cancelable": true,
501
597
  "composed": true,
502
598
  "docs": {
503
599
  "tags": [],
504
- "text": "Emitted when enter is pressed an a result is highlighted."
600
+ "text": "Emitted when the input is blurred."
505
601
  },
506
602
  "complexType": {
507
- "original": "Result",
508
- "resolved": "{ [x: string]: unknown; }",
603
+ "original": "FocusEvent",
604
+ "resolved": "FocusEvent",
509
605
  "references": {
510
- "Result": {
511
- "location": "import",
512
- "path": "../result-list/result-list"
606
+ "FocusEvent": {
607
+ "location": "global"
513
608
  }
514
609
  }
515
610
  }
516
611
  }];
517
612
  }
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
613
  static get elementRef() { return "hostEl"; }
614
+ static get watchers() {
615
+ return [{
616
+ "propName": "value",
617
+ "methodName": "replaceContent"
618
+ }, {
619
+ "propName": "replacements",
620
+ "methodName": "updateContent"
621
+ }];
622
+ }
567
623
  }