composite-select 1.0.0

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 (105) hide show
  1. package/README.md +5 -0
  2. package/commitlint.config.js +3 -0
  3. package/composition/composite-select/CompositeManager.js +43 -0
  4. package/composition/composite-select/composite-select.js +199 -0
  5. package/composition/composite-select/debounce.js +10 -0
  6. package/composition/composite-select/helpers.js +96 -0
  7. package/composition/composite-select/react.js +189 -0
  8. package/composition/container/ContainerManager.js +76 -0
  9. package/composition/img/ai.png +0 -0
  10. package/composition/img/chatgpt.png +0 -0
  11. package/composition/img/claude.png +0 -0
  12. package/composition/img/gemini.png +0 -0
  13. package/composition/img/gmail.png +0 -0
  14. package/composition/img/google_calendar.png +0 -0
  15. package/composition/img/google_drive.png +0 -0
  16. package/composition/img/google_keep.png +0 -0
  17. package/composition/img/img.json +5 -0
  18. package/composition/img/perplexity.png +0 -0
  19. package/composition/img/t3chat.png +0 -0
  20. package/composition/img/timeanddate.png +0 -0
  21. package/composition/img/tools.png +0 -0
  22. package/composition/img/youtube.png +0 -0
  23. package/composition/options-section/OptionsSectionManager.css +263 -0
  24. package/composition/options-section/OptionsSectionManager.js +486 -0
  25. package/composition/options-section/options-section.js +245 -0
  26. package/composition/options-section/react.js +90 -0
  27. package/composition/selected-section/SelectedSectionManager.css +214 -0
  28. package/composition/selected-section/SelectedSectionManager.js +336 -0
  29. package/composition/selected-section/react.js +91 -0
  30. package/composition/selected-section/selected-section.js +207 -0
  31. package/composition/unbind/clickOutside.js +17 -0
  32. package/diff/coreBundle.patch +13 -0
  33. package/diff/recorderApp.patch +13 -0
  34. package/dist/cjs/Module.cjs +15 -0
  35. package/dist/cjs/composite-select/CompositeManager.js +43 -0
  36. package/dist/cjs/composite-select/composite-select.js +199 -0
  37. package/dist/cjs/composite-select/debounce.js +10 -0
  38. package/dist/cjs/composite-select/helpers.js +96 -0
  39. package/dist/cjs/composite-select/react.js +189 -0
  40. package/dist/cjs/container/ContainerManager.js +76 -0
  41. package/dist/cjs/createSubscriber.cjs +48 -0
  42. package/dist/cjs/options-section/OptionsSectionManager.js +486 -0
  43. package/dist/cjs/options-section/options-section.js +245 -0
  44. package/dist/cjs/options-section/react.js +90 -0
  45. package/dist/cjs/selected-section/SelectedSectionManager.js +336 -0
  46. package/dist/cjs/selected-section/react.js +91 -0
  47. package/dist/cjs/selected-section/selected-section.js +207 -0
  48. package/dist/cjs/types.cjs +1 -0
  49. package/dist/cjs/unbind/clickOutside.js +17 -0
  50. package/dist/esm/Module.js +15 -0
  51. package/dist/esm/composite-select/CompositeManager.js +43 -0
  52. package/dist/esm/composite-select/composite-select.js +199 -0
  53. package/dist/esm/composite-select/debounce.js +10 -0
  54. package/dist/esm/composite-select/helpers.js +96 -0
  55. package/dist/esm/composite-select/react.js +189 -0
  56. package/dist/esm/container/ContainerManager.js +76 -0
  57. package/dist/esm/createSubscriber.js +48 -0
  58. package/dist/esm/options-section/OptionsSectionManager.js +486 -0
  59. package/dist/esm/options-section/options-section.js +245 -0
  60. package/dist/esm/options-section/react.js +90 -0
  61. package/dist/esm/selected-section/SelectedSectionManager.js +336 -0
  62. package/dist/esm/selected-section/react.js +91 -0
  63. package/dist/esm/selected-section/selected-section.js +207 -0
  64. package/dist/esm/types.js +1 -0
  65. package/dist/esm/unbind/clickOutside.js +17 -0
  66. package/dist/types/Module.d.ts +15 -0
  67. package/dist/types/composite-select/CompositeManager.d.ts +21 -0
  68. package/dist/types/composite-select/ContainerManager.html.d.ts +1 -0
  69. package/dist/types/composite-select/composite-select.d.ts +26 -0
  70. package/dist/types/composite-select/composite-select.html.d.ts +1 -0
  71. package/dist/types/composite-select/debounce.d.ts +1 -0
  72. package/dist/types/composite-select/helpers.d.ts +38 -0
  73. package/dist/types/composite-select/namesSource.d.ts +4 -0
  74. package/dist/types/composite-select/react.d.ts +61 -0
  75. package/dist/types/composite-select/urlManager.d.ts +49 -0
  76. package/dist/types/composite-select/urlManagerWc.d.ts +44 -0
  77. package/dist/types/container/ContainerManager.d.ts +33 -0
  78. package/dist/types/createSubscriber.d.ts +26 -0
  79. package/dist/types/options-section/OptionsSectionManager.d.ts +117 -0
  80. package/dist/types/options-section/OptionsSectionManager.html.d.ts +1 -0
  81. package/dist/types/options-section/OptionsSectionManagerWebComponent.attributes.html.d.ts +1 -0
  82. package/dist/types/options-section/OptionsSectionManagerWebComponent.html.d.ts +1 -0
  83. package/dist/types/options-section/OptionsSectionManagerWebComponent.nocssrequest.html.d.ts +1 -0
  84. package/dist/types/options-section/options-section.d.ts +67 -0
  85. package/dist/types/options-section/react.d.ts +27 -0
  86. package/dist/types/options-section/urlManager.d.ts +28 -0
  87. package/dist/types/selected-section/SelectedSectionManager.d.ts +89 -0
  88. package/dist/types/selected-section/SelectedSectionManager.html.d.ts +1 -0
  89. package/dist/types/selected-section/SelectedSectionManager.templates.html.d.ts +1 -0
  90. package/dist/types/selected-section/SelectedSectionManagerWebComponent.attributes.html.d.ts +1 -0
  91. package/dist/types/selected-section/SelectedSectionManagerWebComponent.html.d.ts +1 -0
  92. package/dist/types/selected-section/SelectedSectionManagerWebComponent.nocssrequest.html.d.ts +1 -0
  93. package/dist/types/selected-section/react.d.ts +32 -0
  94. package/dist/types/selected-section/selected-section.d.ts +54 -0
  95. package/dist/types/selected-section/urlManager.d.ts +25 -0
  96. package/dist/types/types.d.ts +9 -0
  97. package/dist/types/unbind/clickOutside.d.ts +1 -0
  98. package/floating-label-pattern.css +502 -0
  99. package/js/CenterAndHeightResizer.js +263 -0
  100. package/js/CenterResizer.js +190 -0
  101. package/madooei.tar.gz +0 -0
  102. package/package.json +28 -0
  103. package/release.config.js +3 -0
  104. package/test/lib.d.ts +6 -0
  105. package/test/lib.js +30 -0
@@ -0,0 +1,91 @@
1
+ // @ts-ignore
2
+ import React from "react";
3
+ import "./selected-section.js";
4
+ export const SelectedSection = React.forwardRef((props, ref) => {
5
+ const { label, "show-input": showInput, value, disabled, error, loading, selected: selectedSelected, children, onDelete, onClear, onChange, onInputChange, onFocus, ...rest } = props;
6
+ const internalRef = React.useRef(null);
7
+ const setRef = React.useCallback((node) => {
8
+ internalRef.current = node;
9
+ if (typeof ref === "function") {
10
+ ref(node);
11
+ }
12
+ else if (ref) {
13
+ ref.current = node;
14
+ }
15
+ }, [ref]);
16
+ React.useLayoutEffect(() => {
17
+ const el = internalRef.current;
18
+ if (el && selectedSelected !== undefined) {
19
+ if (el.getManager && el.getManager()) {
20
+ el.getManager().setSelected(typeof selectedSelected === "string" ? JSON.parse(selectedSelected) : selectedSelected);
21
+ }
22
+ else {
23
+ el.setAttribute("selected", typeof selectedSelected === "string" ? selectedSelected : JSON.stringify(selectedSelected));
24
+ }
25
+ }
26
+ }, [selectedSelected]);
27
+ React.useLayoutEffect(() => {
28
+ const el = internalRef.current;
29
+ if (el && el.getManager && el.getManager()) {
30
+ const mgr = el.getManager();
31
+ const sub = mgr.getSubscriber();
32
+ if (sub) {
33
+ const unbinds = [];
34
+ if (onDelete) {
35
+ unbinds.push(sub.bind("onDelete", (id) => onDelete(id)));
36
+ }
37
+ if (onClear) {
38
+ unbinds.push(sub.bind("onClear", () => onClear()));
39
+ }
40
+ if (onChange) {
41
+ unbinds.push(sub.bind("onChange", (s) => onChange(s)));
42
+ }
43
+ if (onInputChange) {
44
+ unbinds.push(sub.bind("onInputChange", (e, previousValue) => onInputChange({
45
+ originalEvent: e,
46
+ value: e.target.value,
47
+ key: e.key || "",
48
+ previousValue,
49
+ })));
50
+ }
51
+ if (onFocus) {
52
+ unbinds.push(sub.bind("onFocus", (e) => onFocus({ originalEvent: e })));
53
+ }
54
+ return () => {
55
+ unbinds.forEach((u) => u());
56
+ };
57
+ }
58
+ }
59
+ }, [onDelete, onClear, onChange, onInputChange, onFocus]);
60
+ const wcProps = { ...rest, ref: setRef };
61
+ if (label !== undefined)
62
+ wcProps.label = label;
63
+ if (value !== undefined)
64
+ wcProps.value = value;
65
+ if (String(showInput) === "true") {
66
+ wcProps["show-input"] = "true";
67
+ }
68
+ else {
69
+ delete wcProps["show-input"];
70
+ }
71
+ if (String(disabled) === "true") {
72
+ wcProps.disabled = "true";
73
+ }
74
+ else {
75
+ delete wcProps.disabled;
76
+ }
77
+ if (String(error) === "true") {
78
+ wcProps.error = "true";
79
+ }
80
+ else {
81
+ delete wcProps.error;
82
+ }
83
+ if (String(loading) === "true") {
84
+ wcProps.loading = "true";
85
+ }
86
+ else {
87
+ delete wcProps.loading;
88
+ }
89
+ return React.createElement("selected-section", wcProps, children);
90
+ });
91
+ export default SelectedSection;
@@ -0,0 +1,207 @@
1
+ import { SelectedSectionManager } from "./SelectedSectionManager.js";
2
+ /**
3
+ * Injects CSS into the Shadow DOM.
4
+ * Priority:
5
+ * 1. SelectedSection.cssText (Bundler string injection)
6
+ * 2. <meta name="select-component" content="/path1.css, /path2.css"> (Global HTML declaration in main document)
7
+ * 3. SelectedSection.defaultCssUrls (Global JS property)
8
+ *
9
+ * Example of Global HTML Declaration in the main document <head>:
10
+ * <head>
11
+ * <meta name="select-component" content="SelectedSectionManager.css">
12
+ * </head>
13
+ */
14
+ export class SelectedSection extends HTMLElement {
15
+ _manager = null;
16
+ _options = {};
17
+ _mountPoint;
18
+ _stylesInjected = false;
19
+ // 1. For Bundlers: Assign CSS string directly (e.g. import css from './style.css?raw')
20
+ static cssText = "";
21
+ // 2. For Vanilla JS: Default URLs (Vite/Webpack5 will also process import.meta.url)
22
+ static defaultCssUrls = [];
23
+ static get observedAttributes() {
24
+ return ["label", "show-input", "value", "disabled", "error", "loading", "selected"];
25
+ }
26
+ constructor() {
27
+ super();
28
+ this.attachShadow({ mode: "open" });
29
+ this.shadowRoot.innerHTML = `<style></style><div></div>`;
30
+ this._mountPoint = this.shadowRoot.querySelector("div");
31
+ }
32
+ connectedCallback() {
33
+ this._injectStyles();
34
+ if (this._manager)
35
+ return;
36
+ this._options = {
37
+ label: this.getAttribute("label") || "",
38
+ showInput: this.hasAttribute("show-input"),
39
+ value: this.getAttribute("value") || "",
40
+ disabled: this.hasAttribute("disabled"),
41
+ error: this.hasAttribute("error"),
42
+ loading: this.hasAttribute("loading"),
43
+ selected: this._parseJSON(this.getAttribute("selected")) ?? [],
44
+ };
45
+ this._manager = new SelectedSectionManager(this._mountPoint, this._options);
46
+ }
47
+ disconnectedCallback() {
48
+ this._manager?.destroy();
49
+ this._manager = null;
50
+ }
51
+ attributeChangedCallback(name, _oldValue, newValue) {
52
+ if (!this._manager)
53
+ return;
54
+ const isTrue = this.hasAttribute(name);
55
+ switch (name) {
56
+ case "label":
57
+ this._manager.setLabel(newValue);
58
+ break;
59
+ case "show-input":
60
+ this._manager.setShowInput(isTrue);
61
+ break;
62
+ case "value":
63
+ this._manager.setValue(newValue);
64
+ break;
65
+ case "disabled":
66
+ this._manager.setDisabled(isTrue);
67
+ break;
68
+ case "error":
69
+ this._manager.setError(isTrue);
70
+ break;
71
+ case "loading":
72
+ this._manager.setLoading(isTrue);
73
+ break;
74
+ case "selected": {
75
+ const parsed = this._parseJSON(newValue);
76
+ if (parsed !== undefined) {
77
+ this._manager.setSelected(parsed);
78
+ }
79
+ break;
80
+ }
81
+ }
82
+ }
83
+ _injectStyles() {
84
+ if (this._stylesInjected)
85
+ return;
86
+ this._stylesInjected = true;
87
+ const style = document.createElement("style");
88
+ // Scenario A: Bundler injected raw CSS string directly
89
+ if (SelectedSection.cssText) {
90
+ style.textContent = SelectedSection.cssText;
91
+ }
92
+ // Scenario B: Load from URLs (Global Meta Tag > Default Static Property)
93
+ else {
94
+ let urls = [];
95
+ const metaTag = document.querySelector('meta[name="select-component"]');
96
+ if (metaTag && metaTag.getAttribute("content")) {
97
+ urls = metaTag
98
+ .getAttribute("content")
99
+ .split(",")
100
+ .map((s) => s.trim());
101
+ }
102
+ else {
103
+ urls = SelectedSection.defaultCssUrls;
104
+ }
105
+ urls.forEach((url) => {
106
+ if (!url)
107
+ return;
108
+ style.textContent += `@import url("${url}");\n`;
109
+ });
110
+ }
111
+ // Remove existing injected CSS if updating dynamically
112
+ const existingStyle = this.shadowRoot.querySelector("style");
113
+ if (existingStyle) {
114
+ existingStyle.remove();
115
+ }
116
+ this.shadowRoot.appendChild(style);
117
+ }
118
+ // Proxied methods
119
+ setSelected(list) {
120
+ this._manager?.setSelected(list);
121
+ }
122
+ setValue(value) {
123
+ this._manager?.setValue(value);
124
+ }
125
+ clearSearch(triggerOnChange = true) {
126
+ this._manager?.clearSearch(triggerOnChange);
127
+ }
128
+ setError(state) {
129
+ this.toggleAttribute("error", state);
130
+ }
131
+ setDisabled(state) {
132
+ this.toggleAttribute("disabled", state);
133
+ }
134
+ setLoading(state) {
135
+ this.toggleAttribute("loading", state);
136
+ }
137
+ setLabel(text) {
138
+ this.setAttribute("label", text);
139
+ }
140
+ setShowInput(state) {
141
+ this.toggleAttribute("show-input", state);
142
+ }
143
+ setRenderItem(renderer) {
144
+ this._manager?.setRenderItem(renderer);
145
+ }
146
+ setRenderList(renderer) {
147
+ this._manager?.setRenderList(renderer);
148
+ }
149
+ render() {
150
+ this._manager?.render();
151
+ }
152
+ // Getters and setters for properties
153
+ get selected() {
154
+ return this._manager?.getSelected() || [];
155
+ }
156
+ set selected(val) {
157
+ this.setSelected(val);
158
+ }
159
+ get value() {
160
+ return this._manager?.propInputElement?.value || "";
161
+ }
162
+ set value(val) {
163
+ this.setValue(val);
164
+ }
165
+ get label() {
166
+ return this.getAttribute("label") || "";
167
+ }
168
+ set label(val) {
169
+ this.setLabel(val);
170
+ }
171
+ get error() {
172
+ return this.hasAttribute("error");
173
+ }
174
+ set error(val) {
175
+ this.setError(val);
176
+ }
177
+ get disabled() {
178
+ return this.hasAttribute("disabled");
179
+ }
180
+ set disabled(val) {
181
+ this.setDisabled(val);
182
+ }
183
+ get loading() {
184
+ return this.hasAttribute("loading");
185
+ }
186
+ set loading(val) {
187
+ this.setLoading(val);
188
+ }
189
+ setFocus() {
190
+ this._manager?.setFocus();
191
+ }
192
+ getManager() {
193
+ return this._manager;
194
+ }
195
+ _parseJSON(val) {
196
+ if (!val)
197
+ return undefined;
198
+ try {
199
+ return JSON.parse(val);
200
+ }
201
+ catch (e) {
202
+ console.error(`SelectedSection: failed to parse JSON:`, val, e);
203
+ return undefined;
204
+ }
205
+ }
206
+ }
207
+ customElements.define("selected-section", SelectedSection);
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,17 @@
1
+ export function clickOutside(targets, callback) {
2
+ const targetList = Array.isArray(targets) ? targets : [targets];
3
+ const handler = (e) => {
4
+ const node = e.target;
5
+ const isInside = targetList.some((target) => target && target.contains(node));
6
+ if (!isInside) {
7
+ callback(e);
8
+ }
9
+ };
10
+ // Use capturing phase so we don't miss events if some child stops propagation
11
+ document.addEventListener("click", handler, true);
12
+ document.addEventListener("touchstart", handler, { capture: true, passive: true });
13
+ return function unbind() {
14
+ document.removeEventListener("click", handler, true);
15
+ document.removeEventListener("touchstart", handler, true);
16
+ };
17
+ }
@@ -0,0 +1,15 @@
1
+ /** @es.ts
2
+ {
3
+ mode: "bundle",
4
+ extension: ".js",
5
+ options: {
6
+ }
7
+ }
8
+ @es.ts */
9
+ export * from "./container/ContainerManager.js";
10
+ export * from "./options-section/OptionsSectionManager.js";
11
+ export * from "./selected-section/SelectedSectionManager.js";
12
+ export * from "./unbind/clickOutside.js";
13
+ export * from "./composite-select/helpers.js";
14
+ export * from "./composite-select/CompositeManager.js";
15
+ export * from "./types.js";
@@ -0,0 +1,21 @@
1
+ import { ContainerManager } from "../container/ContainerManager.js";
2
+ import type { ContainerManagerOptions } from "../container/ContainerManager.js";
3
+ import { OptionsSectionManager } from "../options-section/OptionsSectionManager.js";
4
+ import type { OptionsSectionManagerOptions } from "../options-section/OptionsSectionManager.js";
5
+ import { SelectedSectionManager } from "../selected-section/SelectedSectionManager.js";
6
+ import type { SelectedSectionManagerOptions } from "../selected-section/SelectedSectionManager.js";
7
+ import type { Item } from "../types.js";
8
+ export type CompositeManagerOptions<T extends Item> = {
9
+ select?: SelectedSectionManagerOptions<T>;
10
+ options?: OptionsSectionManagerOptions<T>;
11
+ container?: ContainerManagerOptions;
12
+ };
13
+ export declare class CompositeManager<T extends Item> {
14
+ container: ContainerManager;
15
+ selected: SelectedSectionManager<T>;
16
+ options: OptionsSectionManager<T>;
17
+ private _unbindClickOutside;
18
+ private _parent;
19
+ constructor(parent: HTMLDivElement, options?: CompositeManagerOptions<T>);
20
+ destroy(): void;
21
+ }
@@ -0,0 +1 @@
1
+ import "../../../js/CenterAndHeightResizer.js";
@@ -0,0 +1,26 @@
1
+ import { CompositeManager } from "./CompositeManager.js";
2
+ import type { Item } from "../types.js";
3
+ /**
4
+ * CompositeSelect web component — wraps CompositeManager.
5
+ *
6
+ * Does NOT use shadow DOM because ContainerManager creates a <div popover>
7
+ * whose positioning CSS classes (cover-bottom, bottom, etc. from popover.css)
8
+ * are global and cannot penetrate shadow DOM boundaries.
9
+ * The host page must load the required CSS via <link> tags, exactly as ContainerManager.html does.
10
+ *
11
+ * Attribute prefixes:
12
+ * selected-* → SelectedSectionManager properties / events
13
+ * options-* → OptionsSectionManager properties / events
14
+ * container-* → ContainerManager properties / events
15
+ */
16
+ export declare class CompositeSelect<T extends Item = Item> extends HTMLElement {
17
+ private _manager;
18
+ private _mountPoint;
19
+ static get observedAttributes(): string[];
20
+ constructor();
21
+ connectedCallback(): void;
22
+ disconnectedCallback(): void;
23
+ attributeChangedCallback(name: string, _oldValue: string, newValue: string): void;
24
+ getManager<TT extends Item = T>(): CompositeManager<TT> | null;
25
+ private _parseJSON;
26
+ }
@@ -0,0 +1 @@
1
+ import "../../../js/CenterAndHeightResizer.js";
@@ -0,0 +1 @@
1
+ export default function debounce<Args extends any[]>(fn: (...args: Args) => any, delay: number): (...args: Args) => void;
@@ -0,0 +1,38 @@
1
+ import type { Item } from "../types.js";
2
+ /**
3
+ * Toggles an item in the selected list: adds if missing, removes if present.
4
+ */
5
+ export declare function togglePresenceOnTheList<T extends Item>(selected: T[], item: T): T[];
6
+ /**
7
+ * Resolves a list of IDs into full option objects and merges them into the current selected list (seed).
8
+ * This function is designed to preserve any existing items in the selected list (including "extra" items
9
+ * that might not exist in the current options array) while ensuring the final result is unique and deduplicated by ID.
10
+ */
11
+ export declare function selectedFindDeduplicatedInOptionsByIds<T extends Item>(options: T[], ids: (string | number)[], seed?: T[]): T[];
12
+ /**
13
+ * Updates the 'selected' flag on each option based on whether its ID exists in the provided selected list.
14
+ */
15
+ export declare function markSelectedByIds<T extends Item>(options: T[], selectedIds: number[]): T[];
16
+ /**
17
+ * Returns a new array with duplicate items removed based on their 'id' property.
18
+ * Interesting trick,
19
+ * if you know you will have more than one instance of object by the same id then pass these which you want to keep first.
20
+ * It is usefull in certain circumstances:
21
+ * For example when onFocus is fired we can unselect objects from selected by placing
22
+ * [...options, ...selected] in this order
23
+ * then unselected options will win over selected
24
+ * and then after that we can filter just these which are selected still and update manager.selected.setSelected()
25
+ * this way we will unselect
26
+ */
27
+ export declare function deduplicateArrayById<T extends {
28
+ id: string | number;
29
+ }>(arr: T[]): T[];
30
+ /**
31
+ * Sorts a list of elements by their ID string value.
32
+ */
33
+ export declare function sortById<T extends Item>(list: T[], asc?: boolean): T[];
34
+ /**
35
+ * Wraps matches of search terms in the provided text with a highlight span.
36
+ * Supports multi-word search and case-insensitive matching.
37
+ */
38
+ export declare function markSearchWithSpan(text: string, search: string): string;
@@ -0,0 +1,4 @@
1
+ import type { Item } from "../types.js";
2
+ export declare function getSafeFreeOffset(): number;
3
+ export declare function randomNames(name: string, num?: number): Item[];
4
+ export declare function searchNames<T extends Item>(name: string | undefined, num?: number): T[];
@@ -0,0 +1,61 @@
1
+ import React from "react";
2
+ import "./composite-select.js";
3
+ import type { Item } from "../types.js";
4
+ import type { OptionsSectionManagerOptions } from "../options-section/OptionsSectionManager.js";
5
+ import type { SelectedSectionManagerOptions } from "../selected-section/SelectedSectionManager.js";
6
+ type BaseCompositeSelectProps<T extends Item = Item> = {
7
+ className?: string;
8
+ style?: React.CSSProperties;
9
+ id?: string;
10
+ key?: React.Key;
11
+ ref?: React.Ref<HTMLElement>;
12
+ "selected-selected"?: T[] | string;
13
+ "selected-show-input"?: boolean;
14
+ "selected-value"?: string;
15
+ "selected-label"?: string;
16
+ "selected-disabled"?: boolean;
17
+ "selected-error"?: boolean;
18
+ "selected-loading"?: boolean;
19
+ "selected-show-delete"?: boolean;
20
+ "selected-onDelete"?: (id: string) => void;
21
+ "selected-onClear"?: () => void;
22
+ "selected-onInputChange"?: (detail: {
23
+ originalEvent: Event;
24
+ value: string;
25
+ key: string;
26
+ previousValue?: string;
27
+ }) => void;
28
+ "selected-onFocus"?: (detail: {
29
+ originalEvent: FocusEvent;
30
+ }) => void;
31
+ "selected-onChange"?: (selected: T[]) => void;
32
+ "selected-onComponentChange"?: (options: SelectedSectionManagerOptions<T>, reason: string) => void;
33
+ "options-options"?: T[] | string;
34
+ "options-loading"?: boolean;
35
+ "options-value"?: string;
36
+ "options-label"?: string;
37
+ "options-disabled"?: boolean;
38
+ "options-max-height"?: string;
39
+ "options-show-footer"?: boolean;
40
+ "options-show-filter"?: boolean;
41
+ "options-onItemPick"?: (item: T) => void;
42
+ "options-onInputChange"?: (detail: {
43
+ originalEvent: Event;
44
+ value: string;
45
+ previousValue?: string;
46
+ }) => void;
47
+ "options-onCancel"?: () => void;
48
+ "options-onOk"?: () => void;
49
+ "options-onHighlightChange"?: (id: string | number | null) => void;
50
+ "options-onClear"?: () => void;
51
+ "options-onComponentChange"?: (options: OptionsSectionManagerOptions<T>, reason: string) => void;
52
+ "container-onClose"?: () => void;
53
+ "container-position"?: string;
54
+ "container-offset"?: string;
55
+ children?: React.ReactNode;
56
+ };
57
+ export type CompositeSelectProps<T extends Item = Item> = BaseCompositeSelectProps<T> & {
58
+ [K in string as K extends `${string}-${string}` ? K extends keyof BaseCompositeSelectProps<T> ? never : K : never]?: never;
59
+ };
60
+ export declare const CompositeSelect: <T extends Item = Item>(props: CompositeSelectProps<T> & React.RefAttributes<HTMLElement>) => React.ReactElement;
61
+ export default CompositeSelect;
@@ -0,0 +1,49 @@
1
+ import type { PositionType } from "../container/ContainerManager.js";
2
+ import type { Item } from "../types.js";
3
+ export interface DemoItem extends Item {
4
+ color?: string;
5
+ img?: string;
6
+ }
7
+ export type DemoState = {
8
+ selected: DemoItem[];
9
+ left: string;
10
+ center: string;
11
+ height: string;
12
+ disabledSel: boolean;
13
+ disabledOpt: boolean;
14
+ loadingSel: boolean;
15
+ loadingOpt: boolean;
16
+ labelSel: string;
17
+ labelOpt: string;
18
+ errorSel: boolean;
19
+ showInputSel: boolean;
20
+ showFilter: boolean;
21
+ showFooter: boolean;
22
+ position: PositionType;
23
+ filter: string;
24
+ selectedValue: string;
25
+ maxHeight: string;
26
+ emptyList: boolean;
27
+ };
28
+ export type UrlDef<T> = {
29
+ toUrl: (url: URL, id: string | number, value: T) => void;
30
+ fromUrl: (urlParams: URLSearchParams, id: string | number) => T | undefined;
31
+ remove: (urlParams: URLSearchParams, id: string | number) => void;
32
+ };
33
+ export declare function urlManipulationFactory<State extends Record<string, any>>(config: {
34
+ [K in keyof State]: UrlDef<State[K]>;
35
+ }): {
36
+ toUrl: (url: URL, id: string | number, state: Partial<State>) => URL;
37
+ fromUrl: (urlParams: URLSearchParams, id: string | number) => Partial<State>;
38
+ getAllIds: (urlParams: URLSearchParams, idRegex?: RegExp) => number[];
39
+ removeId: (urlParams: URLSearchParams, id: string | number) => void;
40
+ };
41
+ export declare const createStringParam: (prefix: string, defaultValue?: string) => UrlDef<string>;
42
+ export declare const createBooleanParam: (prefix: string, trueVal?: string, falseVal?: string, defaultValue?: boolean) => UrlDef<boolean>;
43
+ export declare const createArrayParam: <T>(prefix: string, serialize: (item: T) => string, deserialize: (val: string) => T) => UrlDef<T[]>;
44
+ export declare const urlStateConfig: {
45
+ toUrl: (url: URL, id: string | number, state: Partial<DemoState>) => URL;
46
+ fromUrl: (urlParams: URLSearchParams, id: string | number) => Partial<DemoState>;
47
+ getAllIds: (urlParams: URLSearchParams, idRegex?: RegExp) => number[];
48
+ removeId: (urlParams: URLSearchParams, id: string | number) => void;
49
+ };
@@ -0,0 +1,44 @@
1
+ import type { PositionType } from "../container/ContainerManager.js";
2
+ import type { Item } from "../types.js";
3
+ export type DemoItem = Item;
4
+ export type DemoState = {
5
+ selected: DemoItem[];
6
+ left: string;
7
+ center: string;
8
+ height: string;
9
+ disabledSel: boolean;
10
+ disabledOpt: boolean;
11
+ loadingSel: boolean;
12
+ loadingOpt: boolean;
13
+ labelSel: string;
14
+ labelOpt: string;
15
+ errorSel: boolean;
16
+ showInputSel: boolean;
17
+ setShowFilter: boolean;
18
+ setShowFooter: boolean;
19
+ position: PositionType;
20
+ filter: string;
21
+ selectedValue: string;
22
+ };
23
+ export type UrlDef<T> = {
24
+ toUrl: (url: URL, id: string | number, value: T) => void;
25
+ fromUrl: (urlParams: URLSearchParams, id: string | number) => T | undefined;
26
+ remove: (urlParams: URLSearchParams, id: string | number) => void;
27
+ };
28
+ export declare function urlManipulationFactory<State extends Record<string, any>>(config: {
29
+ [K in keyof State]: UrlDef<State[K]>;
30
+ }): {
31
+ toUrl: (url: URL, id: string | number, state: Partial<State>) => URL;
32
+ fromUrl: (urlParams: URLSearchParams, id: string | number) => Partial<State>;
33
+ getAllIds: (urlParams: URLSearchParams, idRegex?: RegExp) => number[];
34
+ removeId: (urlParams: URLSearchParams, id: string | number) => void;
35
+ };
36
+ export declare const createStringParam: (prefix: string, defaultValue?: string) => UrlDef<string>;
37
+ export declare const createBooleanParam: (prefix: string, trueVal?: string, falseVal?: string, defaultValue?: boolean) => UrlDef<boolean>;
38
+ export declare const createArrayParam: <T>(prefix: string, serialize: (item: T) => string, deserialize: (val: string) => T) => UrlDef<T[]>;
39
+ export declare const urlStateConfig: {
40
+ toUrl: (url: URL, id: string | number, state: Partial<DemoState>) => URL;
41
+ fromUrl: (urlParams: URLSearchParams, id: string | number) => Partial<DemoState>;
42
+ getAllIds: (urlParams: URLSearchParams, idRegex?: RegExp) => number[];
43
+ removeId: (urlParams: URLSearchParams, id: string | number) => void;
44
+ };
@@ -0,0 +1,33 @@
1
+ export type PositionType = "topleft" | "top-left" | "top" | "top-right" | "topright" | "left-top" | "cover-top-left" | "cover-top" | "cover-top-right" | "right-top" | "left" | "cover-left" | "center" | "cover-right" | "right" | "left-bottom" | "cover-bottom-left" | "cover-bottom" | "cover-bottom-right" | "right-bottom" | "bottomleft" | "bottom-left" | "bottom" | "bottom-right" | "bottomright";
2
+ export type ContainerManagerOptions = {
3
+ onClose?: () => void;
4
+ };
5
+ export type ContainerManagerEvents = {
6
+ onClose: [];
7
+ };
8
+ export declare class ContainerManager {
9
+ private parent;
10
+ private target;
11
+ private popover;
12
+ private currentPosition;
13
+ propOptions: ContainerManagerOptions;
14
+ private _subscriber;
15
+ constructor(parent: HTMLDivElement, options?: ContainerManagerOptions);
16
+ getSubscriber(): {
17
+ bind: <K extends "onClose">(event: K, handler: (...args: ContainerManagerEvents[K]) => void) => () => void;
18
+ unbind: <K extends "onClose">(event: K, handler: (...args: ContainerManagerEvents[K]) => void) => void;
19
+ unbindGroup: <K extends "onClose">(event: K) => void;
20
+ trigger: <K extends "onClose">(event: K, ...args: ContainerManagerEvents[K]) => void;
21
+ destroy: () => void;
22
+ getCount: () => number;
23
+ };
24
+ show(): void;
25
+ hide(): void;
26
+ getParent(): HTMLDivElement;
27
+ getTarget(): HTMLDivElement;
28
+ getPopover(): HTMLDivElement;
29
+ getPosition(): PositionType;
30
+ setPosition(position: PositionType): void;
31
+ setOffset(offset: string): void;
32
+ destroy(): void;
33
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ type Events = {
3
+ login: [user: { id: string; name: string }];
4
+ logout: [];
5
+ };
6
+
7
+ const subscriber = createSubscriber<Events>();
8
+
9
+ const loginHandler = () => {}
10
+ const logoutHandler = () => {}
11
+
12
+ subscriber.bind("login", loginHandler);
13
+ subscriber.bind("logout", logoutHandler);
14
+
15
+ subscriber.trigger("login", { id: "1", name: "John" });
16
+
17
+ subscriber.trigger("logout");
18
+ */
19
+ export default function createSubscriber<EventsSpecs extends Record<string, any[]> = Record<string, any[]>>(): {
20
+ bind: <K extends keyof EventsSpecs>(event: K, handler: (...args: EventsSpecs[K]) => void) => () => void;
21
+ unbind: <K extends keyof EventsSpecs>(event: K, handler: (...args: EventsSpecs[K]) => void) => void;
22
+ unbindGroup: <K extends keyof EventsSpecs>(event: K) => void;
23
+ trigger: <K extends keyof EventsSpecs>(event: K, ...args: EventsSpecs[K]) => void;
24
+ destroy: () => void;
25
+ getCount: () => number;
26
+ };