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.
- package/README.md +5 -0
- package/commitlint.config.js +3 -0
- package/composition/composite-select/CompositeManager.js +43 -0
- package/composition/composite-select/composite-select.js +199 -0
- package/composition/composite-select/debounce.js +10 -0
- package/composition/composite-select/helpers.js +96 -0
- package/composition/composite-select/react.js +189 -0
- package/composition/container/ContainerManager.js +76 -0
- package/composition/img/ai.png +0 -0
- package/composition/img/chatgpt.png +0 -0
- package/composition/img/claude.png +0 -0
- package/composition/img/gemini.png +0 -0
- package/composition/img/gmail.png +0 -0
- package/composition/img/google_calendar.png +0 -0
- package/composition/img/google_drive.png +0 -0
- package/composition/img/google_keep.png +0 -0
- package/composition/img/img.json +5 -0
- package/composition/img/perplexity.png +0 -0
- package/composition/img/t3chat.png +0 -0
- package/composition/img/timeanddate.png +0 -0
- package/composition/img/tools.png +0 -0
- package/composition/img/youtube.png +0 -0
- package/composition/options-section/OptionsSectionManager.css +263 -0
- package/composition/options-section/OptionsSectionManager.js +486 -0
- package/composition/options-section/options-section.js +245 -0
- package/composition/options-section/react.js +90 -0
- package/composition/selected-section/SelectedSectionManager.css +214 -0
- package/composition/selected-section/SelectedSectionManager.js +336 -0
- package/composition/selected-section/react.js +91 -0
- package/composition/selected-section/selected-section.js +207 -0
- package/composition/unbind/clickOutside.js +17 -0
- package/diff/coreBundle.patch +13 -0
- package/diff/recorderApp.patch +13 -0
- package/dist/cjs/Module.cjs +15 -0
- package/dist/cjs/composite-select/CompositeManager.js +43 -0
- package/dist/cjs/composite-select/composite-select.js +199 -0
- package/dist/cjs/composite-select/debounce.js +10 -0
- package/dist/cjs/composite-select/helpers.js +96 -0
- package/dist/cjs/composite-select/react.js +189 -0
- package/dist/cjs/container/ContainerManager.js +76 -0
- package/dist/cjs/createSubscriber.cjs +48 -0
- package/dist/cjs/options-section/OptionsSectionManager.js +486 -0
- package/dist/cjs/options-section/options-section.js +245 -0
- package/dist/cjs/options-section/react.js +90 -0
- package/dist/cjs/selected-section/SelectedSectionManager.js +336 -0
- package/dist/cjs/selected-section/react.js +91 -0
- package/dist/cjs/selected-section/selected-section.js +207 -0
- package/dist/cjs/types.cjs +1 -0
- package/dist/cjs/unbind/clickOutside.js +17 -0
- package/dist/esm/Module.js +15 -0
- package/dist/esm/composite-select/CompositeManager.js +43 -0
- package/dist/esm/composite-select/composite-select.js +199 -0
- package/dist/esm/composite-select/debounce.js +10 -0
- package/dist/esm/composite-select/helpers.js +96 -0
- package/dist/esm/composite-select/react.js +189 -0
- package/dist/esm/container/ContainerManager.js +76 -0
- package/dist/esm/createSubscriber.js +48 -0
- package/dist/esm/options-section/OptionsSectionManager.js +486 -0
- package/dist/esm/options-section/options-section.js +245 -0
- package/dist/esm/options-section/react.js +90 -0
- package/dist/esm/selected-section/SelectedSectionManager.js +336 -0
- package/dist/esm/selected-section/react.js +91 -0
- package/dist/esm/selected-section/selected-section.js +207 -0
- package/dist/esm/types.js +1 -0
- package/dist/esm/unbind/clickOutside.js +17 -0
- package/dist/types/Module.d.ts +15 -0
- package/dist/types/composite-select/CompositeManager.d.ts +21 -0
- package/dist/types/composite-select/ContainerManager.html.d.ts +1 -0
- package/dist/types/composite-select/composite-select.d.ts +26 -0
- package/dist/types/composite-select/composite-select.html.d.ts +1 -0
- package/dist/types/composite-select/debounce.d.ts +1 -0
- package/dist/types/composite-select/helpers.d.ts +38 -0
- package/dist/types/composite-select/namesSource.d.ts +4 -0
- package/dist/types/composite-select/react.d.ts +61 -0
- package/dist/types/composite-select/urlManager.d.ts +49 -0
- package/dist/types/composite-select/urlManagerWc.d.ts +44 -0
- package/dist/types/container/ContainerManager.d.ts +33 -0
- package/dist/types/createSubscriber.d.ts +26 -0
- package/dist/types/options-section/OptionsSectionManager.d.ts +117 -0
- package/dist/types/options-section/OptionsSectionManager.html.d.ts +1 -0
- package/dist/types/options-section/OptionsSectionManagerWebComponent.attributes.html.d.ts +1 -0
- package/dist/types/options-section/OptionsSectionManagerWebComponent.html.d.ts +1 -0
- package/dist/types/options-section/OptionsSectionManagerWebComponent.nocssrequest.html.d.ts +1 -0
- package/dist/types/options-section/options-section.d.ts +67 -0
- package/dist/types/options-section/react.d.ts +27 -0
- package/dist/types/options-section/urlManager.d.ts +28 -0
- package/dist/types/selected-section/SelectedSectionManager.d.ts +89 -0
- package/dist/types/selected-section/SelectedSectionManager.html.d.ts +1 -0
- package/dist/types/selected-section/SelectedSectionManager.templates.html.d.ts +1 -0
- package/dist/types/selected-section/SelectedSectionManagerWebComponent.attributes.html.d.ts +1 -0
- package/dist/types/selected-section/SelectedSectionManagerWebComponent.html.d.ts +1 -0
- package/dist/types/selected-section/SelectedSectionManagerWebComponent.nocssrequest.html.d.ts +1 -0
- package/dist/types/selected-section/react.d.ts +32 -0
- package/dist/types/selected-section/selected-section.d.ts +54 -0
- package/dist/types/selected-section/urlManager.d.ts +25 -0
- package/dist/types/types.d.ts +9 -0
- package/dist/types/unbind/clickOutside.d.ts +1 -0
- package/floating-label-pattern.css +502 -0
- package/js/CenterAndHeightResizer.js +263 -0
- package/js/CenterResizer.js +190 -0
- package/madooei.tar.gz +0 -0
- package/package.json +28 -0
- package/release.config.js +3 -0
- package/test/lib.d.ts +6 -0
- package/test/lib.js +30 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Toggles an item in the selected list: adds if missing, removes if present.
|
|
3
|
+
*/
|
|
4
|
+
export function togglePresenceOnTheList(selected, item) {
|
|
5
|
+
const tmp = [...selected];
|
|
6
|
+
const newList = tmp.filter((i) => String(i.id) !== String(item.id));
|
|
7
|
+
if (newList.length === tmp.length) {
|
|
8
|
+
tmp.push(item);
|
|
9
|
+
return tmp;
|
|
10
|
+
}
|
|
11
|
+
return newList;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Resolves a list of IDs into full option objects and merges them into the current selected list (seed).
|
|
15
|
+
* This function is designed to preserve any existing items in the selected list (including "extra" items
|
|
16
|
+
* that might not exist in the current options array) while ensuring the final result is unique and deduplicated by ID.
|
|
17
|
+
*/
|
|
18
|
+
export function selectedFindDeduplicatedInOptionsByIds(options, ids, seed) {
|
|
19
|
+
let tmp = [];
|
|
20
|
+
if (seed) {
|
|
21
|
+
tmp = [...seed];
|
|
22
|
+
}
|
|
23
|
+
ids.forEach((id) => {
|
|
24
|
+
const found = tmp.some((i) => String(i.id) === String(id));
|
|
25
|
+
if (!found) {
|
|
26
|
+
tmp.push(options.find((o) => String(o.id) === String(id)));
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
tmp = deduplicateArrayById(tmp);
|
|
30
|
+
return tmp;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Updates the 'selected' flag on each option based on whether its ID exists in the provided selected list.
|
|
34
|
+
*/
|
|
35
|
+
export function markSelectedByIds(options, selectedIds) {
|
|
36
|
+
if (selectedIds.length === 0) {
|
|
37
|
+
return [...options];
|
|
38
|
+
}
|
|
39
|
+
return options.map((option) => {
|
|
40
|
+
const opt = { ...option };
|
|
41
|
+
opt.selected = selectedIds.includes(option.id);
|
|
42
|
+
return opt;
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Returns a new array with duplicate items removed based on their 'id' property.
|
|
47
|
+
* Interesting trick,
|
|
48
|
+
* 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.
|
|
49
|
+
* It is usefull in certain circumstances:
|
|
50
|
+
* For example when onFocus is fired we can unselect objects from selected by placing
|
|
51
|
+
* [...options, ...selected] in this order
|
|
52
|
+
* then unselected options will win over selected
|
|
53
|
+
* and then after that we can filter just these which are selected still and update manager.selected.setSelected()
|
|
54
|
+
* this way we will unselect
|
|
55
|
+
*/
|
|
56
|
+
export function deduplicateArrayById(arr) {
|
|
57
|
+
return arr.filter((item, index) => arr.findIndex((i) => String(i.id) === String(item.id)) === index);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Sorts a list of elements by their ID string value.
|
|
61
|
+
*/
|
|
62
|
+
export function sortById(list, asc = true) {
|
|
63
|
+
return [...list].sort((a, b) => {
|
|
64
|
+
const aId = String(a.id);
|
|
65
|
+
const bId = String(b.id);
|
|
66
|
+
if (aId < bId) {
|
|
67
|
+
return asc ? -1 : 1;
|
|
68
|
+
}
|
|
69
|
+
if (aId > bId) {
|
|
70
|
+
return asc ? 1 : -1;
|
|
71
|
+
}
|
|
72
|
+
return 0;
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Wraps matches of search terms in the provided text with a highlight span.
|
|
77
|
+
* Supports multi-word search and case-insensitive matching.
|
|
78
|
+
*/
|
|
79
|
+
export function markSearchWithSpan(text, search) {
|
|
80
|
+
if (!search || !search.trim()) {
|
|
81
|
+
return text;
|
|
82
|
+
}
|
|
83
|
+
// Split by whitespace, deduplicate, and remove empty strings
|
|
84
|
+
const words = Array.from(new Set(search.trim().split(/\s+/).filter(Boolean)));
|
|
85
|
+
if (words.length === 0) {
|
|
86
|
+
return text;
|
|
87
|
+
}
|
|
88
|
+
// Escape special regex characters in each word
|
|
89
|
+
const escapedWords = words.map((word) => word.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
|
|
90
|
+
// Sort by length descending to match longest words first (greedy matching)
|
|
91
|
+
escapedWords.sort((a, b) => b.length - a.length);
|
|
92
|
+
// Create a regex to match any of the words, case-insensitively, globally
|
|
93
|
+
const regex = new RegExp(`(${escapedWords.join("|")})`, "gi");
|
|
94
|
+
// Replace matches with the highlighted span
|
|
95
|
+
return text.replace(regex, '<span class="highlight">$1</span>');
|
|
96
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
// @ts-ignore
|
|
2
|
+
import React from "react";
|
|
3
|
+
import "./composite-select.js";
|
|
4
|
+
export const CompositeSelect = React.forwardRef((props, ref) => {
|
|
5
|
+
const { "selected-selected": selectedSelected, "selected-show-input": selectedShowInput, "selected-value": selectedValue, "selected-label": selectedLabel, "selected-disabled": selectedDisabled, "selected-error": selectedError, "selected-loading": selectedLoading, "selected-show-delete": selectedShowDelete, "selected-onDelete": selectedOnDelete, "selected-onClear": selectedOnClear, "selected-onInputChange": selectedOnChangeValue, "selected-onFocus": selectedOnFocus, "selected-onChange": selectedOnChange, "selected-onComponentChange": selectedOnSelectedItemsChanged, "options-options": optionsOptions, "options-loading": optionsLoading, "options-value": optionsValue, "options-label": optionsLabel, "options-disabled": optionsDisabled, "options-max-height": optionsMaxHeight, "options-show-footer": optionsShowFooter, "options-show-filter": optionsFilter, "options-onItemPick": optionsOnItemPick, "options-onInputChange": optionsOnInputChange, "options-onCancel": optionsOnCancel, "options-onOk": optionsOnOk, "options-onHighlightChange": optionsOnHighlightChange, "options-onClear": optionsOnClear, "options-onComponentChange": optionsOnOptionsChanged, "container-position": containerPosition, "container-offset": containerOffset, "container-onClose": containerOnClose, children, ...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 && el.getManager && el.getManager()) {
|
|
19
|
+
const mgr = el.getManager();
|
|
20
|
+
const unbinds = [];
|
|
21
|
+
// SelectedSection events
|
|
22
|
+
const selSub = mgr.selected.getSubscriber();
|
|
23
|
+
if (selSub) {
|
|
24
|
+
if (selectedOnDelete) {
|
|
25
|
+
unbinds.push(selSub.bind("onDelete", (id) => selectedOnDelete(id)));
|
|
26
|
+
}
|
|
27
|
+
if (selectedOnClear) {
|
|
28
|
+
unbinds.push(selSub.bind("onClear", () => selectedOnClear()));
|
|
29
|
+
}
|
|
30
|
+
if (selectedOnChangeValue) {
|
|
31
|
+
unbinds.push(selSub.bind("onInputChange", (e, previousValue) => selectedOnChangeValue({
|
|
32
|
+
originalEvent: e,
|
|
33
|
+
value: e.target.value,
|
|
34
|
+
key: e.key || "",
|
|
35
|
+
previousValue,
|
|
36
|
+
})));
|
|
37
|
+
}
|
|
38
|
+
if (selectedOnFocus) {
|
|
39
|
+
unbinds.push(selSub.bind("onFocus", (e) => selectedOnFocus({ originalEvent: e })));
|
|
40
|
+
}
|
|
41
|
+
if (selectedOnChange) {
|
|
42
|
+
unbinds.push(selSub.bind("onChange", (selected) => selectedOnChange(selected)));
|
|
43
|
+
}
|
|
44
|
+
if (selectedOnSelectedItemsChanged) {
|
|
45
|
+
unbinds.push(selSub.bind("onComponentChange", (options, reason) => selectedOnSelectedItemsChanged(options, reason)));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// OptionsSection events
|
|
49
|
+
const optSub = mgr.options.getSubscriber();
|
|
50
|
+
if (optSub) {
|
|
51
|
+
if (optionsOnItemPick) {
|
|
52
|
+
unbinds.push(optSub.bind("onItemPick", (item) => optionsOnItemPick(item)));
|
|
53
|
+
}
|
|
54
|
+
if (optionsOnInputChange) {
|
|
55
|
+
unbinds.push(optSub.bind("onInputChange", (e, previousValue) => optionsOnInputChange({
|
|
56
|
+
originalEvent: e,
|
|
57
|
+
value: e.target.value,
|
|
58
|
+
previousValue,
|
|
59
|
+
})));
|
|
60
|
+
}
|
|
61
|
+
if (optionsOnCancel) {
|
|
62
|
+
unbinds.push(optSub.bind("onCancel", () => optionsOnCancel()));
|
|
63
|
+
}
|
|
64
|
+
if (optionsOnOk) {
|
|
65
|
+
unbinds.push(optSub.bind("onOk", () => optionsOnOk()));
|
|
66
|
+
}
|
|
67
|
+
if (optionsOnHighlightChange) {
|
|
68
|
+
unbinds.push(optSub.bind("onHighlightChange", (id) => optionsOnHighlightChange(id)));
|
|
69
|
+
}
|
|
70
|
+
if (optionsOnClear) {
|
|
71
|
+
unbinds.push(optSub.bind("onClear", () => optionsOnClear()));
|
|
72
|
+
}
|
|
73
|
+
if (optionsOnOptionsChanged) {
|
|
74
|
+
unbinds.push(optSub.bind("onComponentChange", (options, reason) => optionsOnOptionsChanged(options, reason)));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// Container events
|
|
78
|
+
const conSub = mgr.container.getSubscriber();
|
|
79
|
+
if (conSub) {
|
|
80
|
+
if (containerOnClose) {
|
|
81
|
+
unbinds.push(conSub.bind("onClose", () => containerOnClose()));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return () => {
|
|
85
|
+
unbinds.forEach((u) => u());
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}, [
|
|
89
|
+
selectedOnDelete,
|
|
90
|
+
selectedOnClear,
|
|
91
|
+
selectedOnChangeValue,
|
|
92
|
+
selectedOnFocus,
|
|
93
|
+
selectedOnChange,
|
|
94
|
+
selectedOnSelectedItemsChanged,
|
|
95
|
+
optionsOnItemPick,
|
|
96
|
+
optionsOnInputChange,
|
|
97
|
+
optionsOnCancel,
|
|
98
|
+
optionsOnOk,
|
|
99
|
+
optionsOnHighlightChange,
|
|
100
|
+
optionsOnClear,
|
|
101
|
+
optionsOnOptionsChanged,
|
|
102
|
+
containerOnClose,
|
|
103
|
+
]);
|
|
104
|
+
React.useLayoutEffect(() => {
|
|
105
|
+
const el = internalRef.current;
|
|
106
|
+
if (el && el.getManager && el.getManager()) {
|
|
107
|
+
if (selectedSelected !== undefined) {
|
|
108
|
+
el.getManager().selected.setSelected(typeof selectedSelected === "string" ? JSON.parse(selectedSelected) : selectedSelected);
|
|
109
|
+
}
|
|
110
|
+
if (optionsOptions !== undefined) {
|
|
111
|
+
el.getManager().options.setOptions(typeof optionsOptions === "string" ? JSON.parse(optionsOptions) : optionsOptions);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}, [selectedSelected, optionsOptions]);
|
|
115
|
+
const wcProps = { ...rest, ref: setRef };
|
|
116
|
+
// Map selected-* attributes
|
|
117
|
+
if (String(selectedShowInput) === "true") {
|
|
118
|
+
wcProps["selected-show-input"] = "true";
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
delete wcProps["selected-show-input"];
|
|
122
|
+
}
|
|
123
|
+
if (selectedValue !== undefined)
|
|
124
|
+
wcProps["selected-value"] = selectedValue;
|
|
125
|
+
if (selectedLabel !== undefined)
|
|
126
|
+
wcProps["selected-label"] = selectedLabel;
|
|
127
|
+
if (String(selectedDisabled) === "true") {
|
|
128
|
+
wcProps["selected-disabled"] = "true";
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
delete wcProps["selected-disabled"];
|
|
132
|
+
}
|
|
133
|
+
if (String(selectedError) === "true") {
|
|
134
|
+
wcProps["selected-error"] = "true";
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
delete wcProps["selected-error"];
|
|
138
|
+
}
|
|
139
|
+
if (String(selectedLoading) === "true") {
|
|
140
|
+
wcProps["selected-loading"] = "true";
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
delete wcProps["selected-loading"];
|
|
144
|
+
}
|
|
145
|
+
if (String(selectedShowDelete) === "true") {
|
|
146
|
+
wcProps["selected-show-delete"] = "true";
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
delete wcProps["selected-show-delete"];
|
|
150
|
+
}
|
|
151
|
+
// Map options-* attributes
|
|
152
|
+
if (String(optionsLoading) === "true") {
|
|
153
|
+
wcProps["options-loading"] = "true";
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
delete wcProps["options-loading"];
|
|
157
|
+
}
|
|
158
|
+
if (optionsValue !== undefined)
|
|
159
|
+
wcProps["options-value"] = optionsValue;
|
|
160
|
+
if (optionsLabel !== undefined)
|
|
161
|
+
wcProps["options-label"] = optionsLabel;
|
|
162
|
+
if (String(optionsDisabled) === "true") {
|
|
163
|
+
wcProps["options-disabled"] = "true";
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
delete wcProps["options-disabled"];
|
|
167
|
+
}
|
|
168
|
+
if (optionsMaxHeight !== undefined)
|
|
169
|
+
wcProps["options-max-height"] = optionsMaxHeight;
|
|
170
|
+
if (String(optionsShowFooter) === "true") {
|
|
171
|
+
wcProps["options-show-footer"] = "true";
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
delete wcProps["options-show-footer"];
|
|
175
|
+
}
|
|
176
|
+
if (String(optionsFilter) === "true") {
|
|
177
|
+
wcProps["options-show-filter"] = "true";
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
delete wcProps["options-show-filter"];
|
|
181
|
+
}
|
|
182
|
+
// Map container-* attributes
|
|
183
|
+
if (containerPosition !== undefined)
|
|
184
|
+
wcProps["container-position"] = containerPosition;
|
|
185
|
+
if (containerOffset !== undefined)
|
|
186
|
+
wcProps["container-offset"] = containerOffset;
|
|
187
|
+
return React.createElement("composite-select", wcProps, children);
|
|
188
|
+
});
|
|
189
|
+
export default CompositeSelect;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import createSubscriber from "../createSubscriber.js";
|
|
2
|
+
export class ContainerManager {
|
|
3
|
+
parent;
|
|
4
|
+
target;
|
|
5
|
+
popover;
|
|
6
|
+
currentPosition = "cover-bottom";
|
|
7
|
+
propOptions;
|
|
8
|
+
_subscriber = createSubscriber();
|
|
9
|
+
constructor(parent, options = {}) {
|
|
10
|
+
this.parent = parent;
|
|
11
|
+
this.propOptions = {
|
|
12
|
+
...options,
|
|
13
|
+
};
|
|
14
|
+
this.target = document.createElement("div");
|
|
15
|
+
this.popover = document.createElement("div");
|
|
16
|
+
this.popover.setAttribute("popover", "manual");
|
|
17
|
+
this.popover.setAttribute("data-popover", "");
|
|
18
|
+
this.popover.style.width = "anchor-size(width)";
|
|
19
|
+
this.popover.style.boxSizing = "border-box";
|
|
20
|
+
this.popover.style.border = "none";
|
|
21
|
+
this.setPosition(this.currentPosition);
|
|
22
|
+
this.setOffset("0px");
|
|
23
|
+
this.parent.appendChild(this.target);
|
|
24
|
+
this.parent.appendChild(this.popover);
|
|
25
|
+
this.popover.addEventListener("beforetoggle", (event) => {
|
|
26
|
+
if (event.newState === "closed") {
|
|
27
|
+
if (this.propOptions.onClose) {
|
|
28
|
+
this.propOptions.onClose();
|
|
29
|
+
}
|
|
30
|
+
this._subscriber.trigger("onClose");
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
getSubscriber() {
|
|
35
|
+
return this._subscriber;
|
|
36
|
+
}
|
|
37
|
+
show() {
|
|
38
|
+
this.popover.showPopover({ source: this.target });
|
|
39
|
+
}
|
|
40
|
+
hide() {
|
|
41
|
+
this.popover.hidePopover();
|
|
42
|
+
}
|
|
43
|
+
getParent() {
|
|
44
|
+
return this.parent;
|
|
45
|
+
}
|
|
46
|
+
getTarget() {
|
|
47
|
+
return this.target;
|
|
48
|
+
}
|
|
49
|
+
getPopover() {
|
|
50
|
+
return this.popover;
|
|
51
|
+
}
|
|
52
|
+
getPosition() {
|
|
53
|
+
return this.currentPosition;
|
|
54
|
+
}
|
|
55
|
+
setPosition(position) {
|
|
56
|
+
if (this.currentPosition) {
|
|
57
|
+
this.popover.classList.remove(this.currentPosition);
|
|
58
|
+
}
|
|
59
|
+
this.currentPosition = position;
|
|
60
|
+
if (this.currentPosition) {
|
|
61
|
+
this.popover.classList.add(this.currentPosition);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
setOffset(offset) {
|
|
65
|
+
this.popover.style.setProperty("--popover-offset", offset);
|
|
66
|
+
}
|
|
67
|
+
destroy() {
|
|
68
|
+
if (this.target && this.target.parentNode) {
|
|
69
|
+
this.target.parentNode.removeChild(this.target);
|
|
70
|
+
}
|
|
71
|
+
if (this.popover && this.popover.parentNode) {
|
|
72
|
+
this.popover.parentNode.removeChild(this.popover);
|
|
73
|
+
}
|
|
74
|
+
this._subscriber.destroy();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
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() {
|
|
20
|
+
const bindings = new Map();
|
|
21
|
+
function bind(event, handler) {
|
|
22
|
+
if (!bindings.has(event)) {
|
|
23
|
+
bindings.set(event, new Set());
|
|
24
|
+
}
|
|
25
|
+
bindings.get(event).add(handler);
|
|
26
|
+
return () => unbind(event, handler);
|
|
27
|
+
}
|
|
28
|
+
function unbind(event, handler) {
|
|
29
|
+
bindings.get(event)?.delete(handler);
|
|
30
|
+
}
|
|
31
|
+
function unbindGroup(event) {
|
|
32
|
+
bindings.delete(event);
|
|
33
|
+
}
|
|
34
|
+
function trigger(event, ...args) {
|
|
35
|
+
bindings.get(event)?.forEach((handler) => {
|
|
36
|
+
handler(...args);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
function destroy() {
|
|
40
|
+
bindings.clear();
|
|
41
|
+
}
|
|
42
|
+
function getCount() {
|
|
43
|
+
let count = 0;
|
|
44
|
+
bindings.forEach((handlers) => (count += handlers.size));
|
|
45
|
+
return count;
|
|
46
|
+
}
|
|
47
|
+
return { bind, unbind, unbindGroup, trigger, destroy, getCount };
|
|
48
|
+
}
|