composite-select 1.0.2 → 1.0.4

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 (37) hide show
  1. package/README.md +4 -0
  2. package/composition/list-up-down-navi/ListManager.css +203 -0
  3. package/package.json +1 -1
  4. package/commitlint.config.js +0 -3
  5. package/composition/composite-select/CompositeManager.js +0 -43
  6. package/composition/composite-select/composite-select.js +0 -199
  7. package/composition/composite-select/debounce.js +0 -10
  8. package/composition/composite-select/helpers.js +0 -96
  9. package/composition/composite-select/react.js +0 -189
  10. package/composition/container/ContainerManager.js +0 -76
  11. package/composition/img/ai.png +0 -0
  12. package/composition/img/chatgpt.png +0 -0
  13. package/composition/img/claude.png +0 -0
  14. package/composition/img/gemini.png +0 -0
  15. package/composition/img/gmail.png +0 -0
  16. package/composition/img/google_calendar.png +0 -0
  17. package/composition/img/google_drive.png +0 -0
  18. package/composition/img/google_keep.png +0 -0
  19. package/composition/img/img.json +0 -5
  20. package/composition/img/perplexity.png +0 -0
  21. package/composition/img/t3chat.png +0 -0
  22. package/composition/img/timeanddate.png +0 -0
  23. package/composition/img/tools.png +0 -0
  24. package/composition/img/youtube.png +0 -0
  25. package/composition/options-section/OptionsSectionManager.js +0 -486
  26. package/composition/options-section/options-section.js +0 -245
  27. package/composition/options-section/react.js +0 -90
  28. package/composition/selected-section/SelectedSectionManager.js +0 -336
  29. package/composition/selected-section/react.js +0 -91
  30. package/composition/selected-section/selected-section.js +0 -207
  31. package/composition/unbind/clickOutside.js +0 -17
  32. package/diff/coreBundle.patch +0 -13
  33. package/diff/recorderApp.patch +0 -13
  34. package/madooei.tar.gz +0 -0
  35. package/release.config.js +0 -3
  36. package/test/lib.d.ts +0 -6
  37. package/test/lib.js +0 -30
@@ -1,486 +0,0 @@
1
- import { markSearchWithSpan } from "../composite-select/helpers.js";
2
- import createSubscriber from "../createSubscriber.js";
3
- export class OptionsSectionManager {
4
- propOptions;
5
- propParentElement;
6
- propFilterContainer;
7
- propOptionsContainer;
8
- propFooterContainer;
9
- propInputElement = null;
10
- propSpinnerElement = null;
11
- propLabelElement = null;
12
- propOkButton;
13
- propLeftSpace;
14
- propCancelButton;
15
- propClearButton;
16
- propHighlightedId = null;
17
- _skipNextFocusEvent = false;
18
- _attachedElements = new Map();
19
- _subscriber = createSubscriber();
20
- constructor(bindElement, options = {}) {
21
- this.propParentElement = bindElement;
22
- this.propParentElement.classList.add("options-section-manager");
23
- const { options: opt = [], loading = false, disabled = false, value = "", showFooter = true, showFilter = true, renderItem = (item, def, searchValue) => def(item, searchValue), renderList = (list, def) => def(list), renderEmpty = (def) => def(), onClear = () => { }, ...rest } = options;
24
- this.propOptions = {
25
- options: opt,
26
- loading,
27
- disabled,
28
- value,
29
- showFooter,
30
- showFilter,
31
- renderItem,
32
- renderList,
33
- renderEmpty,
34
- onClear,
35
- ...rest,
36
- };
37
- this.render();
38
- Promise.resolve().then(() => {
39
- if (this.propOptions.maxHeight) {
40
- this.setMaxHeight(this.propOptions.maxHeight);
41
- }
42
- this._bindEvents();
43
- });
44
- }
45
- getSubscriber() {
46
- return this._subscriber;
47
- }
48
- _triggerOnComponentChange(reason) {
49
- if (this.propOptions.onComponentChange) {
50
- this.propOptions.onComponentChange.call(this, this.propOptions, reason);
51
- }
52
- this._subscriber.trigger("onComponentChange", this.propOptions, reason);
53
- }
54
- _scrollToHighlighted() {
55
- if (this.propHighlightedId === null)
56
- return;
57
- const el = this.propOptionsContainer.querySelector(`.element[data-id="${this.propHighlightedId}"]`);
58
- if (el) {
59
- el.scrollIntoView({ block: "nearest" });
60
- }
61
- }
62
- _defaultRenderEmpty() {
63
- return `<div class="empty-msg">No options to display</div>`;
64
- }
65
- _defaultRenderItem(item, searchValue) {
66
- const el = document.createElement("div");
67
- el.className = "element";
68
- el.classList.toggle("selected", item.selected === true);
69
- el.dataset.id = String(item.id);
70
- const label = document.createElement("label");
71
- label.innerHTML = markSearchWithSpan(item.label, searchValue || "");
72
- el.appendChild(label);
73
- return el;
74
- }
75
- _defaultRenderList(list) {
76
- return list.map((item) => this.propOptions.renderItem(item, this._defaultRenderItem.bind(this), this.propOptions.value));
77
- }
78
- _bindEvents() {
79
- if (this.propInputElement) {
80
- this.propInputElement.addEventListener("input", (e) => {
81
- const previousValue = this.propOptions.value;
82
- this.propOptions.value = this.propInputElement.value;
83
- this._triggerOnInputChange(e, previousValue, "input");
84
- });
85
- this.propInputElement.addEventListener("focus", (e) => {
86
- if (this._skipNextFocusEvent) {
87
- this._skipNextFocusEvent = false;
88
- return;
89
- }
90
- if (this.propOptions.onFocus) {
91
- this.propOptions.onFocus.call(this, e);
92
- }
93
- this._subscriber.trigger("onFocus", e);
94
- this.attachArrowsUpAndDown();
95
- });
96
- this.propInputElement.addEventListener("blur", () => {
97
- this.detachArrowsUpAndDown();
98
- });
99
- }
100
- this.propClearButton.addEventListener("click", (e) => {
101
- e.preventDefault();
102
- this.clearSearch();
103
- });
104
- this.propOkButton.addEventListener("click", () => {
105
- if (this.propOptions.onOk) {
106
- this.propOptions.onOk.call(this);
107
- }
108
- this._subscriber.trigger("onOk");
109
- });
110
- this.propCancelButton.addEventListener("click", () => {
111
- if (this.propOptions.onCancel) {
112
- this.propOptions.onCancel.call(this);
113
- }
114
- this._subscriber.trigger("onCancel");
115
- });
116
- this.propOptionsContainer.addEventListener("click", (e) => {
117
- if (this.propOptions.disabled)
118
- return;
119
- const target = e.target;
120
- const element = target.closest(".element");
121
- if (element) {
122
- const id = element.dataset.id;
123
- const item = this.propOptions.options?.find((opt) => String(opt.id) === id);
124
- if (item) {
125
- this.setFocus();
126
- this.highlightAndScrollToElementOnTheList(item.id);
127
- this._triggerOnItemPick(item);
128
- }
129
- }
130
- });
131
- }
132
- _handleKeyDown(e) {
133
- const options = this.propOptions.options || [];
134
- const currentIndex = options.findIndex((item) => String(item.id) === String(this.propHighlightedId));
135
- if (e.key === "ArrowDown") {
136
- e.preventDefault();
137
- e.stopPropagation();
138
- if (currentIndex === -1) {
139
- this.highlightAndScrollToElementOnTheList(options[0]?.id);
140
- }
141
- else if (currentIndex < options.length - 1) {
142
- this.highlightAndScrollToElementOnTheList(options[currentIndex + 1].id);
143
- }
144
- }
145
- else if (e.key === "ArrowUp") {
146
- e.preventDefault();
147
- e.stopPropagation();
148
- if (currentIndex === -1) {
149
- this.highlightAndScrollToElementOnTheList(options[options.length - 1]?.id);
150
- }
151
- else if (currentIndex > 0) {
152
- this.highlightAndScrollToElementOnTheList(options[currentIndex - 1].id);
153
- }
154
- }
155
- else if (e.key === "Enter") {
156
- if (this.propHighlightedId !== null) {
157
- e.preventDefault();
158
- e.stopPropagation();
159
- this.pickHighlighted();
160
- }
161
- else if (this.propInputElement.value === "") {
162
- e.stopPropagation();
163
- this._triggerOnInputChange(e, this.propOptions.value, "enter");
164
- }
165
- }
166
- else if (e.key === "Escape") {
167
- e.preventDefault();
168
- e.stopPropagation();
169
- this.highlightAndScrollToElementOnTheList(null);
170
- }
171
- else if (e.key === "Backspace" && this.propInputElement.value === "") {
172
- e.stopPropagation();
173
- this._triggerOnInputChange(e, this.propOptions.value, "backspace");
174
- }
175
- }
176
- _updateOptionsDisplay() {
177
- const container = this.propOptionsContainer;
178
- if (!container)
179
- return;
180
- const options = this.propOptions.options || [];
181
- if (options.length === 0) {
182
- const result = this.propOptions.renderEmpty(this._defaultRenderEmpty.bind(this));
183
- if (typeof result === "string") {
184
- container.innerHTML = result;
185
- }
186
- else {
187
- container.innerHTML = "";
188
- container.appendChild(result);
189
- }
190
- return;
191
- }
192
- const renderedItems = this.propOptions.renderList(options, this._defaultRenderList.bind(this));
193
- container.innerHTML = "";
194
- renderedItems.forEach((item, index) => {
195
- const dataItem = options[index];
196
- let el = null;
197
- if (typeof item === "string") {
198
- const temp = document.createElement("div");
199
- temp.innerHTML = item;
200
- el = temp.firstElementChild;
201
- el.classList.add("element");
202
- el.dataset.id = String(dataItem.id);
203
- el.classList.toggle("selected", dataItem.selected === true);
204
- }
205
- else {
206
- el = item;
207
- }
208
- if (el) {
209
- el.classList.toggle("highlighted", this.propHighlightedId !== null && String(dataItem.id) === String(this.propHighlightedId));
210
- container.appendChild(el);
211
- }
212
- });
213
- }
214
- _updateLoadingDisplay() {
215
- if (this.propSpinnerElement) {
216
- this.propSpinnerElement.classList.toggle("loading", Boolean(this.propOptions.loading));
217
- }
218
- if (this.propClearButton) {
219
- this.propClearButton.style.display = this.propOptions.loading ? "none" : "flex";
220
- }
221
- }
222
- _updateDisabledDisplay() {
223
- if (this.propOptionsContainer) {
224
- this.propOptionsContainer.classList.toggle("disabled", Boolean(this.propOptions.disabled));
225
- }
226
- if (this.propClearButton) {
227
- this.propClearButton.disabled = Boolean(this.propOptions.disabled);
228
- }
229
- }
230
- _updateFooterDisplay() {
231
- if (this.propFooterContainer) {
232
- this.propFooterContainer.style.display = this.propOptions.showFooter !== false ? "flex" : "none";
233
- }
234
- }
235
- _updateFilterDisplay() {
236
- if (this.propFilterContainer) {
237
- this.propFilterContainer.style.display = this.propOptions.showFilter !== false ? "flex" : "none";
238
- }
239
- }
240
- /**
241
- * Internal helper to notify both the callback provided in options and the internal subscriber
242
- * when the input value changes.
243
- */
244
- _triggerOnInputChange(e, previousValue, origin) {
245
- if (this.propOptions.onInputChange) {
246
- this.propOptions.onInputChange.call(this, e, previousValue, origin);
247
- }
248
- this._subscriber.trigger("onInputChange", e, previousValue, origin);
249
- }
250
- /**
251
- * Internal helper to notify both the callback provided in options and the internal subscriber
252
- * when an item is picked (e.g. via click or Enter key).
253
- */
254
- _triggerOnItemPick(item) {
255
- if (this.propOptions.disabled)
256
- return;
257
- if (this.propOptions.onItemPick) {
258
- this.propOptions.onItemPick.call(this, item);
259
- }
260
- this._subscriber.trigger("onItemPick", item);
261
- }
262
- // setters
263
- setMaxHeight(maxHeight) {
264
- this.propOptions.maxHeight = maxHeight || "";
265
- if (this.propParentElement) {
266
- this.propParentElement.style.maxHeight = maxHeight || "none";
267
- }
268
- this._triggerOnComponentChange("setMaxHeight");
269
- }
270
- setDisabled(disabled) {
271
- this.propOptions.disabled = Boolean(disabled);
272
- this._updateDisabledDisplay();
273
- this._triggerOnComponentChange("setDisabled");
274
- }
275
- setShowFooter(show) {
276
- this.propOptions.showFooter = Boolean(show);
277
- this._updateFooterDisplay();
278
- this._triggerOnComponentChange("showFooter");
279
- }
280
- setShowFilter(show) {
281
- this.propOptions.showFilter = Boolean(show);
282
- this._updateFilterDisplay();
283
- this._triggerOnComponentChange("showFilter");
284
- }
285
- setOptions(options) {
286
- this.propOptions.options = options;
287
- this._updateOptionsDisplay();
288
- this._triggerOnComponentChange("setOptions");
289
- }
290
- setLoading(loading) {
291
- this.propOptions.loading = Boolean(loading);
292
- this._updateLoadingDisplay();
293
- this._triggerOnComponentChange("setLoading");
294
- }
295
- setValue(value, triggerOnChange = true) {
296
- const previousValue = this.propOptions.value;
297
- this.propOptions.value = value;
298
- if (this.propInputElement) {
299
- this.propInputElement.value = value;
300
- }
301
- if (triggerOnChange) {
302
- const event = new Event("input");
303
- Object.defineProperty(event, "target", { writable: false, value: this.propInputElement });
304
- this._triggerOnInputChange(event, previousValue, "setValue");
305
- }
306
- this._triggerOnComponentChange("setValue");
307
- }
308
- // getters
309
- getShowFilter() {
310
- return this.propOptions.showFilter;
311
- }
312
- getShowFooter() {
313
- return this.propOptions.showFooter;
314
- }
315
- getDisabled() {
316
- return this.propOptions.disabled;
317
- }
318
- getLabel() {
319
- return this.propOptions.label;
320
- }
321
- getLoading() {
322
- return this.propOptions.loading;
323
- }
324
- getValue() {
325
- return this.propOptions.value;
326
- }
327
- getOptions() {
328
- return this.propOptions.options || [];
329
- }
330
- getMaxHeight() {
331
- return this.propOptions.maxHeight;
332
- }
333
- getHighlightedId() {
334
- return this.propHighlightedId;
335
- }
336
- clearSearch(triggerOnClear = true, triggerOnChange = true) {
337
- const previousValue = this.propOptions.value;
338
- this.setValue("", triggerOnChange);
339
- if (triggerOnClear) {
340
- if (this.propOptions.onClear) {
341
- this.propOptions.onClear();
342
- }
343
- this._subscriber.trigger("onClear");
344
- const event = new Event("input");
345
- Object.defineProperty(event, "target", { writable: false, value: this.propInputElement });
346
- this._triggerOnInputChange(event, previousValue, "clear");
347
- }
348
- }
349
- setLabel(label) {
350
- this.propOptions.label = label;
351
- if (this.propLabelElement) {
352
- this.propLabelElement.textContent = label || "";
353
- }
354
- this._triggerOnComponentChange("setLabel");
355
- }
356
- highlightAndScrollToElementOnTheList(id) {
357
- this.propHighlightedId = id ?? null;
358
- this._updateOptionsDisplay();
359
- if (this.propHighlightedId !== null) {
360
- this._scrollToHighlighted();
361
- }
362
- if (this.propOptions.onHighlightChange) {
363
- this.propOptions.onHighlightChange.call(this, this.propHighlightedId);
364
- }
365
- this._subscriber.trigger("onHighlightChange", this.propHighlightedId);
366
- }
367
- pickHighlighted() {
368
- if (this.propHighlightedId === null)
369
- return;
370
- const item = this.propOptions.options?.find((opt) => String(opt.id) === String(this.propHighlightedId));
371
- if (item) {
372
- this._triggerOnItemPick(item);
373
- }
374
- }
375
- setRenderEmpty(renderEmpty) {
376
- this.propOptions.renderEmpty = renderEmpty || ((def) => def());
377
- this._updateOptionsDisplay();
378
- this._triggerOnComponentChange("setRenderEmpty");
379
- }
380
- setRenderItem(renderItem) {
381
- this.propOptions.renderItem =
382
- renderItem || ((item, defaultRender, searchValue) => defaultRender(item, searchValue));
383
- this._updateOptionsDisplay();
384
- this._triggerOnComponentChange("setRenderItem");
385
- }
386
- setRenderList(renderList) {
387
- this.propOptions.renderList = renderList || ((list, def) => def(list));
388
- this._updateOptionsDisplay();
389
- this._triggerOnComponentChange("setRenderList");
390
- }
391
- setFocus(triggerOnFocus = true) {
392
- if (!triggerOnFocus) {
393
- this._skipNextFocusEvent = true;
394
- }
395
- this.propInputElement.focus();
396
- }
397
- setBlur() {
398
- this.propInputElement.blur();
399
- }
400
- attachArrowsUpAndDown() {
401
- const element = this.propInputElement;
402
- if (this._attachedElements.has(element)) {
403
- return;
404
- }
405
- const listener = (e) => this._handleKeyDown(e);
406
- this._attachedElements.set(element, listener);
407
- element.addEventListener("keydown", listener);
408
- }
409
- detachArrowsUpAndDown() {
410
- const element = this.propInputElement;
411
- const listener = this._attachedElements.get(element);
412
- if (listener) {
413
- element.removeEventListener("keydown", listener);
414
- this._attachedElements.delete(element);
415
- }
416
- }
417
- destroy() {
418
- // This is the trick: we track all elements we've attached to and detach them here.
419
- // This is especially important for the 'window' object which persists.
420
- for (const [element] of this._attachedElements) {
421
- const listener = this._attachedElements.get(element);
422
- if (listener) {
423
- element.removeEventListener("keydown", listener);
424
- }
425
- }
426
- this._attachedElements.clear();
427
- this._subscriber.destroy();
428
- }
429
- render() {
430
- if (!this.propFilterContainer) {
431
- this.propFilterContainer = document.createElement("div");
432
- this.propFilterContainer.className = "filter";
433
- const inputWrapper = document.createElement("div");
434
- inputWrapper.className = "input-wrapper";
435
- this.propInputElement = document.createElement("input");
436
- this.propInputElement.type = "text";
437
- this.propInputElement.id = "search-input-" + Math.random().toString(36).substr(2, 9);
438
- this.propInputElement.placeholder = " ";
439
- this.propInputElement.autocomplete = "off";
440
- this.propLabelElement = document.createElement("label");
441
- this.propLabelElement.setAttribute("for", this.propInputElement.id);
442
- this.propLabelElement.textContent = this.propOptions.label || "Search...";
443
- inputWrapper.appendChild(this.propInputElement);
444
- inputWrapper.appendChild(this.propLabelElement);
445
- this.propSpinnerElement = document.createElement("div");
446
- this.propSpinnerElement.className = "spinner";
447
- inputWrapper.appendChild(this.propSpinnerElement);
448
- this.propClearButton = document.createElement("button");
449
- this.propClearButton.type = "button";
450
- this.propClearButton.className = "clear-btn";
451
- this.propClearButton.textContent = "✕";
452
- inputWrapper.appendChild(this.propClearButton);
453
- this.propFilterContainer.appendChild(inputWrapper);
454
- this.propOptionsContainer = document.createElement("div");
455
- this.propOptionsContainer.className = "options";
456
- this.propOptionsContainer.tabIndex = 0;
457
- this.propFooterContainer = document.createElement("div");
458
- this.propFooterContainer.className = "footer";
459
- this.propCancelButton = document.createElement("button");
460
- this.propCancelButton.type = "button";
461
- this.propCancelButton.dataset.role = "cancel";
462
- this.propCancelButton.className = "gcp-css white";
463
- this.propCancelButton.textContent = "Cancel";
464
- this.propOkButton = document.createElement("button");
465
- this.propOkButton.type = "button";
466
- this.propOkButton.dataset.role = "ok";
467
- this.propOkButton.className = "gcp-css";
468
- this.propOkButton.textContent = "OK";
469
- this.propLeftSpace = document.createElement("span");
470
- this.propLeftSpace.className = "left-space";
471
- this.propLeftSpace.textContent = "";
472
- this.propFooterContainer.appendChild(this.propLeftSpace);
473
- this.propFooterContainer.appendChild(this.propCancelButton);
474
- this.propFooterContainer.appendChild(this.propOkButton);
475
- this.propParentElement.appendChild(this.propFilterContainer);
476
- this.propParentElement.appendChild(this.propOptionsContainer);
477
- this.propParentElement.appendChild(this.propFooterContainer);
478
- }
479
- this._updateOptionsDisplay();
480
- this._updateLoadingDisplay();
481
- this._updateDisabledDisplay();
482
- this._updateFooterDisplay();
483
- this._updateFilterDisplay();
484
- // this.setValue(this.propOptions.value || "");
485
- }
486
- }