composite-select 1.0.9 → 1.0.10
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/dist/cjs/CenterAndHeightResizer.cjs +268 -0
- package/dist/cjs/CenterResizer.cjs +195 -0
- package/dist/cjs/composite-select/ContainerManager.html.cjs +650 -0
- package/dist/cjs/composite-select/composite-select.html.cjs +651 -0
- package/dist/cjs/options-section/OptionsSectionManager.html.cjs +362 -0
- package/dist/cjs/options-section/OptionsSectionManagerWebComponent.attributes.html.cjs +346 -0
- package/dist/cjs/options-section/OptionsSectionManagerWebComponent.html.cjs +354 -0
- package/dist/cjs/options-section/OptionsSectionManagerWebComponent.nocssrequest.html.cjs +345 -0
- package/dist/cjs/selected-section/SelectedSectionManager.html.cjs +336 -0
- package/dist/cjs/selected-section/SelectedSectionManager.templates.html.cjs +353 -0
- package/dist/cjs/selected-section/SelectedSectionManagerWebComponent.attributes.html.cjs +353 -0
- package/dist/cjs/selected-section/SelectedSectionManagerWebComponent.html.cjs +335 -0
- package/dist/cjs/selected-section/SelectedSectionManagerWebComponent.nocssrequest.html.cjs +341 -0
- package/dist/esm/CenterAndHeightResizer.js +268 -0
- package/dist/esm/CenterResizer.js +195 -0
- package/dist/types/CenterAndHeightResizer.d.ts +18 -0
- package/dist/types/CenterResizer.d.ts +16 -0
- package/dist/types/composite-select/ContainerManager.html.d.ts +1 -1
- package/dist/types/composite-select/composite-select.html.d.ts +1 -1
- package/dist/types/options-section/OptionsSectionManager.html.d.ts +1 -1
- package/dist/types/options-section/OptionsSectionManagerWebComponent.attributes.html.d.ts +1 -1
- package/dist/types/options-section/OptionsSectionManagerWebComponent.html.d.ts +1 -1
- package/dist/types/options-section/OptionsSectionManagerWebComponent.nocssrequest.html.d.ts +1 -1
- package/dist/types/selected-section/SelectedSectionManager.html.d.ts +1 -1
- package/dist/types/selected-section/SelectedSectionManager.templates.html.d.ts +1 -1
- package/dist/types/selected-section/SelectedSectionManagerWebComponent.attributes.html.d.ts +1 -1
- package/dist/types/selected-section/SelectedSectionManagerWebComponent.html.d.ts +1 -1
- package/package.json +1 -1
- package/js/CenterAndHeightResizer.js +0 -263
- package/js/CenterResizer.js +0 -190
- /package/dist/cjs/composite-select/{CompositeManager.js → CompositeManager.cjs} +0 -0
- /package/dist/cjs/composite-select/{composite-select.js → composite-select.cjs} +0 -0
- /package/dist/cjs/composite-select/{debounce.js → debounce.cjs} +0 -0
- /package/dist/cjs/composite-select/{helpers.js → helpers.cjs} +0 -0
- /package/dist/cjs/composite-select/{react.js → react.cjs} +0 -0
- /package/dist/cjs/container/{ContainerManager.js → ContainerManager.cjs} +0 -0
- /package/dist/cjs/options-section/{OptionsSectionManager.js → OptionsSectionManager.cjs} +0 -0
- /package/dist/cjs/options-section/{options-section.js → options-section.cjs} +0 -0
- /package/dist/cjs/options-section/{react.js → react.cjs} +0 -0
- /package/dist/cjs/selected-section/{SelectedSectionManager.js → SelectedSectionManager.cjs} +0 -0
- /package/dist/cjs/selected-section/{react.js → react.cjs} +0 -0
- /package/dist/cjs/selected-section/{selected-section.js → selected-section.cjs} +0 -0
- /package/dist/cjs/unbind/{clickOutside.js → clickOutside.cjs} +0 -0
|
@@ -0,0 +1,651 @@
|
|
|
1
|
+
import "../CenterAndHeightResizer.js";
|
|
2
|
+
import {} from "../types.js";
|
|
3
|
+
import { CompositeSelect } from "./composite-select.js";
|
|
4
|
+
import { deduplicateArrayById, sortById, togglePresenceOnTheList, markSelectedByIds } from "./helpers.js";
|
|
5
|
+
import { searchNames as rawSearchNames, getSafeFreeOffset } from "./namesSource.js";
|
|
6
|
+
import debounce from "./debounce.js";
|
|
7
|
+
import { urlStateConfig } from "./urlManager.js";
|
|
8
|
+
const reloadLink = document.getElementById("reload-link");
|
|
9
|
+
if (reloadLink) {
|
|
10
|
+
reloadLink.href = window.location.pathname;
|
|
11
|
+
}
|
|
12
|
+
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
13
|
+
let instanceCounter = 0;
|
|
14
|
+
let globalIdCounter = getSafeFreeOffset();
|
|
15
|
+
const imgData = await fetch("../img/img.json").then((r) => r.json());
|
|
16
|
+
const updateUrlDisplay = (url = window.location.href) => {
|
|
17
|
+
const el = document.getElementById("url-display");
|
|
18
|
+
if (el)
|
|
19
|
+
el.textContent = url;
|
|
20
|
+
};
|
|
21
|
+
const init = (initialSelected = [], states = {}) => {
|
|
22
|
+
// check if in initialSelected ids are numbers - have to be
|
|
23
|
+
initialSelected.forEach((item) => {
|
|
24
|
+
if (typeof item.id !== "number") {
|
|
25
|
+
throw new Error(`initialSelected item.id must be a number, got ${typeof item.id} (${item.id})`);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
instanceCounter++;
|
|
29
|
+
const id = instanceCounter;
|
|
30
|
+
const section = document.createElement("div");
|
|
31
|
+
section.className = "demo-section";
|
|
32
|
+
section.innerHTML = `
|
|
33
|
+
<h2>Instance #${id}</h2>
|
|
34
|
+
<button class="gcp-css white destroy-btn" data-role="destroy">Destroy</button>
|
|
35
|
+
|
|
36
|
+
<div class="resizer-container">
|
|
37
|
+
<center-and-height-resizer
|
|
38
|
+
data-role="resizer"
|
|
39
|
+
left="${states.left || "100px"}"
|
|
40
|
+
center="${states.center || "600px"}"
|
|
41
|
+
height="${states.height || "auto"}"
|
|
42
|
+
style="padding: 12px;"
|
|
43
|
+
>
|
|
44
|
+
<composite-select
|
|
45
|
+
selected-label="${states.labelSel || "Select Fruit"}"
|
|
46
|
+
options-label="${states.labelOpt || "Search fruits..."}"
|
|
47
|
+
options-max-height="300px"
|
|
48
|
+
${states.showFooter !== false ? "options-show-footer" : ""}
|
|
49
|
+
${states.showFilter !== false ? "options-show-filter" : ""}
|
|
50
|
+
${states.disabledSel ? "selected-disabled" : ""}
|
|
51
|
+
${states.loadingSel ? "selected-loading" : ""}
|
|
52
|
+
${states.errorSel ? "selected-error" : ""}
|
|
53
|
+
${states.showInputSel !== false ? "selected-show-input" : ""}
|
|
54
|
+
${states.disabledOpt ? "options-disabled" : ""}
|
|
55
|
+
${states.loadingOpt ? "options-loading" : ""}
|
|
56
|
+
container-position="${states.position || "cover-bottom"}"
|
|
57
|
+
></composite-select>
|
|
58
|
+
</center-and-height-resizer>
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
<div class="controls gcp-css" style="margin-bottom: 8px;">
|
|
62
|
+
<div class="controls-label">SelectedSectionManager (Top Section)</div>
|
|
63
|
+
|
|
64
|
+
<div class="gcp-css checkbox-wrapper">
|
|
65
|
+
<div class="checkbox-row">
|
|
66
|
+
<input type="checkbox" id="disabled-sel-${id}" data-role="disabled-sel" ${states.disabledSel ? "checked" : ""}>
|
|
67
|
+
<div class="content-cell"><label for="disabled-sel-${id}">Disabled</label></div>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
<div class="gcp-css checkbox-wrapper">
|
|
72
|
+
<div class="checkbox-row">
|
|
73
|
+
<input type="checkbox" id="loading-sel-${id}" data-role="loading-sel" ${states.loadingSel ? "checked" : ""}>
|
|
74
|
+
<div class="content-cell"><label for="loading-sel-${id}">Loading</label></div>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
<div class="gcp-css checkbox-wrapper">
|
|
79
|
+
<div class="checkbox-row">
|
|
80
|
+
<input type="checkbox" id="error-sel-${id}" data-role="error-sel" ${states.errorSel ? "checked" : ""}>
|
|
81
|
+
<div class="content-cell"><label for="error-sel-${id}">Error</label></div>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
|
|
85
|
+
<div class="gcp-css checkbox-wrapper">
|
|
86
|
+
<div class="checkbox-row">
|
|
87
|
+
<input type="checkbox" id="show-input-sel-${id}" data-role="show-input-sel" ${states.showInputSel !== false ? "checked" : ""}>
|
|
88
|
+
<div class="content-cell"><label for="show-input-sel-${id}">Show Input</label></div>
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
<div class="gcp-css input-wrapper">
|
|
93
|
+
<input type="text" id="label-input-sel-${id}" data-role="label-input-sel" placeholder=" " value="${states.labelSel || ""}">
|
|
94
|
+
<label for="label-input-sel-${id}">Top Label</label>
|
|
95
|
+
<button class="gcp-css white" data-role="focus-btn" style="margin-left: 5px;">Focus</button>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
<div class="controls gcp-css">
|
|
100
|
+
<div class="controls-label">OptionsSectionManager (Dropdown Section)</div>
|
|
101
|
+
|
|
102
|
+
<div class="gcp-css checkbox-wrapper">
|
|
103
|
+
<div class="checkbox-row">
|
|
104
|
+
<input type="checkbox" id="disabled-opt-${id}" data-role="disabled-opt" ${states.disabledOpt ? "checked" : ""}>
|
|
105
|
+
<div class="content-cell"><label for="disabled-opt-${id}">Disabled</label></div>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
<div class="gcp-css checkbox-wrapper">
|
|
110
|
+
<div class="checkbox-row">
|
|
111
|
+
<input type="checkbox" id="loading-opt-${id}" data-role="loading-opt" ${states.loadingOpt ? "checked" : ""}>
|
|
112
|
+
<div class="content-cell"><label for="loading-opt-${id}">Loading</label></div>
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
|
|
116
|
+
<div class="gcp-css checkbox-wrapper">
|
|
117
|
+
<div class="checkbox-row">
|
|
118
|
+
<input type="checkbox" id="show-filter-opt-${id}" data-role="show-filter-opt" ${states.showFilter !== false ? "checked" : ""}>
|
|
119
|
+
<div class="content-cell"><label for="show-filter-opt-${id}">Filter</label></div>
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
<div class="gcp-css checkbox-wrapper">
|
|
124
|
+
<div class="checkbox-row">
|
|
125
|
+
<input type="checkbox" id="show-footer-opt-${id}" data-role="show-footer-opt" ${states.showFooter !== false ? "checked" : ""}>
|
|
126
|
+
<div class="content-cell"><label for="show-footer-opt-${id}">Show Footer</label></div>
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
|
|
130
|
+
<div class="input-wrapper">
|
|
131
|
+
<select id="position-${id}" data-role="position" class="gcp-css white" required>
|
|
132
|
+
<option value="cover-bottom">cover-bottom</option>
|
|
133
|
+
<option value="bottom">bottom</option>
|
|
134
|
+
<option value="top">top</option>
|
|
135
|
+
<option value="left">left</option>
|
|
136
|
+
<option value="right">right</option>
|
|
137
|
+
</select>
|
|
138
|
+
<label for="position-${id}">Position</label>
|
|
139
|
+
</div>
|
|
140
|
+
|
|
141
|
+
<div class="gcp-css input-wrapper">
|
|
142
|
+
<input type="text" id="label-input-opt-${id}" data-role="label-input-opt" placeholder=" " value="${states.labelOpt || ""}">
|
|
143
|
+
<label for="label-input-opt-${id}">Dropdown Label</label>
|
|
144
|
+
</div>
|
|
145
|
+
|
|
146
|
+
<div style="display: flex; gap: 5px; align-items: center; width: 100%; margin-top: 10px;">
|
|
147
|
+
<span style="min-width: 120px;">🔍 <strong>Input & List</strong>:</span>
|
|
148
|
+
<button class="gcp-css white" data-role="opt-focus-btn">Focus Input</button>
|
|
149
|
+
|
|
150
|
+
<div class="gcp-css checkbox-wrapper">
|
|
151
|
+
<div class="checkbox-row">
|
|
152
|
+
<input type="checkbox" id="empty-list-${id}" data-role="empty-list" ${states.emptyList === true ? "checked" : ""}>
|
|
153
|
+
<div class="content-cell"><label for="empty-list-${id}">Empty list</label></div>
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
|
|
158
|
+
<div style="display: flex; gap: 10px; flex-wrap: wrap; align-items: center; width: 100%; margin-top: 10px;">
|
|
159
|
+
<div style="display: flex; gap: 5px; align-items: center;" data-role="mh-presets-container">
|
|
160
|
+
<span style="min-width: 120px;">📏 <strong>Max Height</strong>:</span>
|
|
161
|
+
<div style="display: flex; gap: 2px;">
|
|
162
|
+
<button class="gcp-css white" data-role="maxheight-down" style="padding: 0 8px; min-width: auto;">▼</button>
|
|
163
|
+
<div class="gcp-css input-wrapper" style="max-width: 150px; margin-bottom: 0;">
|
|
164
|
+
<input type="text" data-role="maxheight-input" id="maxheight-input-${id}" placeholder=" " value="${states.maxHeight || ""}">
|
|
165
|
+
<label for="maxheight-input-${id}">Max height</label>
|
|
166
|
+
</div>
|
|
167
|
+
<button class="gcp-css white" data-role="maxheight-up" style="padding: 0 8px; min-width: auto;">▲</button>
|
|
168
|
+
</div>
|
|
169
|
+
<button data-role="maxheight-btn" class="gcp-css">Set</button>
|
|
170
|
+
<button class="gcp-css white" data-role="mh-preset" data-value="200px">200px</button>
|
|
171
|
+
<button class="gcp-css white" data-role="mh-preset" data-value="300px">300px</button>
|
|
172
|
+
<button class="gcp-css white" data-role="mh-preset" data-value="400px">400px</button>
|
|
173
|
+
<button class="gcp-css white" data-role="mh-preset" data-value="600px">600px</button>
|
|
174
|
+
<button class="gcp-css white" data-role="mh-preset" data-value="">Reset</button>
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
177
|
+
|
|
178
|
+
<div style="display: flex; gap: 5px; align-items: center; width: 100%; margin-top: 10px;">
|
|
179
|
+
<span style="min-width: 120px;">🎨 <strong>Render</strong>:</span>
|
|
180
|
+
<button class="gcp-css white" data-role="opt-render-btn">Set Custom Render</button>
|
|
181
|
+
<button class="gcp-css white" data-role="opt-string-render-btn">Set String Render</button>
|
|
182
|
+
<button class="gcp-css white" data-role="opt-default-render-btn">Set Default Render</button>
|
|
183
|
+
<button class="gcp-css white" data-role="opt-empty-btn">Set Custom Empty</button>
|
|
184
|
+
<button class="gcp-css white" data-role="opt-default-empty-btn">Set Default Empty</button>
|
|
185
|
+
</div>
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
<div style="width: 100%; margin-top: 10px;">
|
|
189
|
+
<pre data-role="search-val-out" style="display: inline; padding: 2px 5px; margin: 0; background: #eee; width: auto; max-height: none;">-</pre>
|
|
190
|
+
(onChange triggers: <span data-role="onchange-count" style="font-weight: bold;">0</span>,
|
|
191
|
+
onOk triggers: <span data-role="onok-count" style="font-weight: bold;">0</span>,
|
|
192
|
+
onCancel triggers: <span data-role="oncancel-count" style="font-weight: bold;">0</span>,
|
|
193
|
+
onItemPick triggers: <span data-role="onpick-count" style="font-weight: bold;">0</span>,
|
|
194
|
+
onSelectedComponentChange triggers: <span data-role="on-selected-component-change-count" style="font-weight: bold;">0</span>,
|
|
195
|
+
onOptionsComponentChange triggers: <span data-role="on-options-component-change-count" style="font-weight: bold;">0</span>)
|
|
196
|
+
</div>
|
|
197
|
+
</div>
|
|
198
|
+
|
|
199
|
+
<div class="controls gcp-css">
|
|
200
|
+
<div class="controls-label">Templates</div>
|
|
201
|
+
<div data-role="templates-area" style="display: flex; gap: 5px; flex-wrap: wrap; margin-bottom: 8px;"></div>
|
|
202
|
+
</div>
|
|
203
|
+
<div class="controls gcp-css" style="margin-bottom: 8px;">
|
|
204
|
+
<button class="white gcp-css" data-role="custom-render-btn">Set Custom Render Item</button>
|
|
205
|
+
<button class="white gcp-css" data-role="custom-list-render-btn">Set Custom Render List</button>
|
|
206
|
+
<button class="white gcp-css" data-role="reset-templates-btn">Reset Templates</button>
|
|
207
|
+
</div>
|
|
208
|
+
|
|
209
|
+
<pre data-role="dump" style="background:#f8f8f8;padding:10px;border:1px solid #eee;border-radius:4px;font-size:12px;margin:0;overflow:auto;"></pre>
|
|
210
|
+
`;
|
|
211
|
+
const instancesArea = document.getElementById("instances-area");
|
|
212
|
+
instancesArea?.appendChild(section);
|
|
213
|
+
const templatesArea = section.querySelector('[data-role="templates-area"]');
|
|
214
|
+
if (templatesArea) {
|
|
215
|
+
for (const [color, images] of Object.entries(imgData)) {
|
|
216
|
+
images.forEach((img) => {
|
|
217
|
+
const btn = document.createElement("button");
|
|
218
|
+
btn.className = "template-btn white gcp-css";
|
|
219
|
+
btn.dataset.color = color;
|
|
220
|
+
btn.dataset.img = img;
|
|
221
|
+
btn.style.color = color;
|
|
222
|
+
btn.style.padding = "4px 8px";
|
|
223
|
+
btn.style.margin = "0";
|
|
224
|
+
btn.textContent = img;
|
|
225
|
+
templatesArea.appendChild(btn);
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
// this is pretty important because this is where we are deciding about type for the web component
|
|
230
|
+
const wc = section.querySelector("composite-select");
|
|
231
|
+
// this is pretty important because this is where we are deciding about type for the web component
|
|
232
|
+
const resizer = section.querySelector('[data-role="resizer"]');
|
|
233
|
+
const destroyBtn = section.querySelector('[data-role="destroy"]');
|
|
234
|
+
const dump = section.querySelector('[data-role="dump"]');
|
|
235
|
+
const positionSelect = section.querySelector('[data-role="position"]');
|
|
236
|
+
const disabledSelCb = section.querySelector('[data-role="disabled-sel"]');
|
|
237
|
+
const loadingSelCb = section.querySelector('[data-role="loading-sel"]');
|
|
238
|
+
const errorSelCb = section.querySelector('[data-role="error-sel"]');
|
|
239
|
+
const showInputSelCb = section.querySelector('[data-role="show-input-sel"]');
|
|
240
|
+
const labelInputSel = section.querySelector('[data-role="label-input-sel"]');
|
|
241
|
+
const emptyListCb = section.querySelector('[data-role="empty-list"]');
|
|
242
|
+
const focusBtn = section.querySelector('[data-role="focus-btn"]');
|
|
243
|
+
const disabledOptCb = section.querySelector('[data-role="disabled-opt"]');
|
|
244
|
+
const loadingOptCb = section.querySelector('[data-role="loading-opt"]');
|
|
245
|
+
const setShowFilterOptCb = section.querySelector('[data-role="show-filter-opt"]');
|
|
246
|
+
const setShowFooterOptCb = section.querySelector('[data-role="show-footer-opt"]');
|
|
247
|
+
const labelInputOpt = section.querySelector('[data-role="label-input-opt"]');
|
|
248
|
+
const optFocusBtn = section.querySelector('[data-role="opt-focus-btn"]');
|
|
249
|
+
const maxHeightInput = section.querySelector('[data-role="maxheight-input"]');
|
|
250
|
+
const maxHeightBtn = section.querySelector('[data-role="maxheight-btn"]');
|
|
251
|
+
const customRenderBtn = section.querySelector('[data-role="custom-render-btn"]');
|
|
252
|
+
const customListRenderBtn = section.querySelector('[data-role="custom-list-render-btn"]');
|
|
253
|
+
const resetTemplatesBtn = section.querySelector('[data-role="reset-templates-btn"]');
|
|
254
|
+
const optRenderBtn = section.querySelector('[data-role="opt-render-btn"]');
|
|
255
|
+
const optStringRenderBtn = section.querySelector('[data-role="opt-string-render-btn"]');
|
|
256
|
+
const optDefaultRenderBtn = section.querySelector('[data-role="opt-default-render-btn"]');
|
|
257
|
+
const optEmptyBtn = section.querySelector('[data-role="opt-empty-btn"]');
|
|
258
|
+
const optDefaultEmptyBtn = section.querySelector('[data-role="opt-default-empty-btn"]');
|
|
259
|
+
const searchValOut = section.querySelector('[data-role="search-val-out"]');
|
|
260
|
+
// export function searchNames<T extends Item>(name: string | undefined, num: number = 10): T[] {
|
|
261
|
+
function searchNames(name, num = 10) {
|
|
262
|
+
if (emptyListCb.checked) {
|
|
263
|
+
return [];
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
return rawSearchNames(name, num);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
const inc = (role) => {
|
|
270
|
+
const el = section.querySelector(`[data-role="${role}"]`);
|
|
271
|
+
if (el) {
|
|
272
|
+
el.textContent = String(parseInt(el.textContent || "0", 10) + 1);
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
const mgr = wc.getManager();
|
|
276
|
+
const updateDump = (list) => {
|
|
277
|
+
dump.textContent = JSON.stringify(list, null, 2);
|
|
278
|
+
};
|
|
279
|
+
const syncUrl = () => {
|
|
280
|
+
const url = new URL(window.location.href);
|
|
281
|
+
urlStateConfig.toUrl(url, id, {
|
|
282
|
+
selected: mgr?.selected.getSelected() ?? [],
|
|
283
|
+
left: resizer.getAttribute("left") || "100px",
|
|
284
|
+
center: resizer.getAttribute("center") || "600px",
|
|
285
|
+
height: resizer.getAttribute("height") || "auto",
|
|
286
|
+
disabledSel: disabledSelCb.checked,
|
|
287
|
+
disabledOpt: disabledOptCb.checked,
|
|
288
|
+
loadingSel: loadingSelCb.checked,
|
|
289
|
+
loadingOpt: loadingOptCb.checked,
|
|
290
|
+
labelSel: labelInputSel.value || "",
|
|
291
|
+
labelOpt: labelInputOpt.value || "",
|
|
292
|
+
errorSel: errorSelCb.checked,
|
|
293
|
+
showInputSel: showInputSelCb.checked,
|
|
294
|
+
showFilter: setShowFilterOptCb.checked,
|
|
295
|
+
showFooter: setShowFooterOptCb.checked,
|
|
296
|
+
position: positionSelect.value,
|
|
297
|
+
filter: mgr?.options.getValue() || "",
|
|
298
|
+
selectedValue: mgr?.selected.propOptions.value || "",
|
|
299
|
+
maxHeight: maxHeightInput.value || "",
|
|
300
|
+
emptyList: emptyListCb.checked,
|
|
301
|
+
});
|
|
302
|
+
window.history.replaceState({}, "", url);
|
|
303
|
+
updateUrlDisplay(url.toString());
|
|
304
|
+
};
|
|
305
|
+
/**
|
|
306
|
+
* Determines if we should use input from SelectedSectionManager or OptionsSectionManager
|
|
307
|
+
* if popupInput = true -> use then it means from OptionsSectionManager
|
|
308
|
+
* else use from SelectedSectionManager
|
|
309
|
+
*/
|
|
310
|
+
function localDetermineSearch() {
|
|
311
|
+
const showFilter = mgr.options.getShowFilter();
|
|
312
|
+
const position = mgr.container.getPosition();
|
|
313
|
+
if (!showFilter && position === "bottom") {
|
|
314
|
+
return { search: mgr.selected.getValue() || "", popupInput: false };
|
|
315
|
+
}
|
|
316
|
+
return { search: mgr.options.getValue() || "", popupInput: true };
|
|
317
|
+
}
|
|
318
|
+
mgr.selected.setSelected(initialSelected);
|
|
319
|
+
const { search } = localDetermineSearch();
|
|
320
|
+
mgr.options.setValue(search);
|
|
321
|
+
const found = deduplicateArrayById(searchNames(search));
|
|
322
|
+
mgr.options.setOptions(sortById(found));
|
|
323
|
+
mgr.selected.propOptions.onFocus = (e) => {
|
|
324
|
+
const { search } = localDetermineSearch();
|
|
325
|
+
const selected = mgr.selected.getSelected();
|
|
326
|
+
const selIds = selected.map((i) => i.id);
|
|
327
|
+
const combined = searchNames(search);
|
|
328
|
+
const options = markSelectedByIds(combined, selIds);
|
|
329
|
+
mgr.options.setOptions(sortById(options));
|
|
330
|
+
mgr.selected.setShowDelete(false);
|
|
331
|
+
mgr.options.setFocus();
|
|
332
|
+
};
|
|
333
|
+
const onChangeEventFactory = (stopPopupInput) => debounce(async (e, previousValue) => {
|
|
334
|
+
const { search, popupInput } = localDetermineSearch();
|
|
335
|
+
if (popupInput === true) {
|
|
336
|
+
inc("onchange-count");
|
|
337
|
+
searchValOut.textContent = search || "-";
|
|
338
|
+
}
|
|
339
|
+
if (stopPopupInput === popupInput) {
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
const value = e.target.value;
|
|
343
|
+
if (!popupInput && e.type === "keydown") {
|
|
344
|
+
const key = e.key;
|
|
345
|
+
if (key === "Backspace" && value === "" && mgr.selected.getSelected().length > 0) {
|
|
346
|
+
const selItems = mgr.selected.getSelected();
|
|
347
|
+
selItems.pop();
|
|
348
|
+
mgr.selected.setSelected(selItems);
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
if (search === previousValue)
|
|
353
|
+
return;
|
|
354
|
+
mgr.options.setLoading(true);
|
|
355
|
+
mgr.options.setDisabled(true);
|
|
356
|
+
mgr.selected.setLoading(true);
|
|
357
|
+
mgr.selected.setDisabled(true);
|
|
358
|
+
await delay(1000);
|
|
359
|
+
const found = deduplicateArrayById(searchNames(search));
|
|
360
|
+
const sorted = sortById(found);
|
|
361
|
+
mgr.options.setOptions(sorted);
|
|
362
|
+
mgr.options.setDisabled(false);
|
|
363
|
+
mgr.options.setLoading(false);
|
|
364
|
+
mgr.selected.setDisabled(false);
|
|
365
|
+
mgr.selected.setLoading(false);
|
|
366
|
+
syncUrl();
|
|
367
|
+
}, 500);
|
|
368
|
+
mgr.selected.propOptions.onInputChange = onChangeEventFactory(true);
|
|
369
|
+
mgr.options.propOptions.onInputChange = onChangeEventFactory(false);
|
|
370
|
+
mgr.selected.propOptions.onDelete = (id) => {
|
|
371
|
+
let selected = mgr.selected.getSelected();
|
|
372
|
+
selected = selected.filter((i) => String(i.id) !== String(id));
|
|
373
|
+
mgr.selected.setSelected(selected);
|
|
374
|
+
syncUrl();
|
|
375
|
+
};
|
|
376
|
+
mgr.selected.propOptions.onClear = () => {
|
|
377
|
+
mgr.selected.setSelected([]);
|
|
378
|
+
syncUrl();
|
|
379
|
+
};
|
|
380
|
+
mgr.options.propOptions.onItemPick = (item) => {
|
|
381
|
+
inc("onpick-count");
|
|
382
|
+
if (mgr.options.getShowFooter()) {
|
|
383
|
+
const options = [...mgr.options.getOptions()];
|
|
384
|
+
const found = options.find((opt) => opt.id === item.id);
|
|
385
|
+
found.selected = !found.selected;
|
|
386
|
+
mgr.options.setOptions(options);
|
|
387
|
+
}
|
|
388
|
+
else {
|
|
389
|
+
const selectedToggled = togglePresenceOnTheList(mgr.selected.getSelected(), item);
|
|
390
|
+
mgr.selected.setSelected(selectedToggled);
|
|
391
|
+
mgr.selected.setShowDelete(true);
|
|
392
|
+
mgr.container.hide();
|
|
393
|
+
}
|
|
394
|
+
syncUrl();
|
|
395
|
+
};
|
|
396
|
+
mgr.options.propOptions.onCancel = () => {
|
|
397
|
+
inc("oncancel-count");
|
|
398
|
+
mgr.container.hide();
|
|
399
|
+
};
|
|
400
|
+
mgr.options.propOptions.onOk = () => {
|
|
401
|
+
inc("onok-count");
|
|
402
|
+
// this is where we prepare new list for mgr.selected.setSelected(...)
|
|
403
|
+
// by passing options first
|
|
404
|
+
// this way these from options will win
|
|
405
|
+
const options = deduplicateArrayById([...mgr.options.getOptions(), ...mgr.selected.getSelected()]);
|
|
406
|
+
// now we have to just filter out selected
|
|
407
|
+
const optionsSelected = options.filter((i) => i.selected);
|
|
408
|
+
mgr.selected.setSelected(optionsSelected);
|
|
409
|
+
mgr.container.hide();
|
|
410
|
+
syncUrl();
|
|
411
|
+
};
|
|
412
|
+
mgr.container.propOptions.onClose = () => {
|
|
413
|
+
mgr.selected.setShowDelete(true);
|
|
414
|
+
};
|
|
415
|
+
mgr.selected.propOptions.onChange = (items) => {
|
|
416
|
+
updateDump(items);
|
|
417
|
+
};
|
|
418
|
+
/**
|
|
419
|
+
* Track all changes to update ui around component in these two events
|
|
420
|
+
*/
|
|
421
|
+
mgr.selected.propOptions.onComponentChange = (opt, reason) => {
|
|
422
|
+
inc("on-selected-component-change-count");
|
|
423
|
+
disabledSelCb.checked = !!opt.disabled;
|
|
424
|
+
loadingSelCb.checked = !!opt.loading;
|
|
425
|
+
errorSelCb.checked = !!opt.error;
|
|
426
|
+
showInputSelCb.checked = opt.showInput !== false;
|
|
427
|
+
labelInputSel.value = opt.label || "";
|
|
428
|
+
};
|
|
429
|
+
mgr.options.propOptions.onComponentChange = (opt, reason) => {
|
|
430
|
+
inc("on-options-component-change-count");
|
|
431
|
+
setShowFilterOptCb.checked = !!opt.showFilter;
|
|
432
|
+
setShowFooterOptCb.checked = !!opt.showFooter;
|
|
433
|
+
disabledOptCb.checked = !!opt.disabled;
|
|
434
|
+
loadingOptCb.checked = !!opt.loading;
|
|
435
|
+
labelInputOpt.value = opt.label || "";
|
|
436
|
+
};
|
|
437
|
+
disabledSelCb.addEventListener("change", () => {
|
|
438
|
+
mgr?.selected.setDisabled(disabledSelCb.checked);
|
|
439
|
+
syncUrl();
|
|
440
|
+
});
|
|
441
|
+
loadingSelCb.addEventListener("change", () => {
|
|
442
|
+
mgr?.selected.setLoading(loadingSelCb.checked);
|
|
443
|
+
syncUrl();
|
|
444
|
+
});
|
|
445
|
+
errorSelCb.addEventListener("change", () => {
|
|
446
|
+
mgr?.selected.setError(errorSelCb.checked);
|
|
447
|
+
syncUrl();
|
|
448
|
+
});
|
|
449
|
+
showInputSelCb.addEventListener("change", () => {
|
|
450
|
+
mgr?.selected.setShowInput(showInputSelCb.checked);
|
|
451
|
+
syncUrl();
|
|
452
|
+
});
|
|
453
|
+
emptyListCb.addEventListener("change", () => {
|
|
454
|
+
const { search } = localDetermineSearch();
|
|
455
|
+
const found = deduplicateArrayById(searchNames(search));
|
|
456
|
+
mgr.options.setOptions(sortById(found));
|
|
457
|
+
syncUrl();
|
|
458
|
+
});
|
|
459
|
+
labelInputSel.addEventListener("input", () => {
|
|
460
|
+
mgr?.selected.setLabel(labelInputSel.value);
|
|
461
|
+
syncUrl();
|
|
462
|
+
});
|
|
463
|
+
disabledOptCb.addEventListener("change", () => {
|
|
464
|
+
mgr?.options.setDisabled(disabledOptCb.checked);
|
|
465
|
+
syncUrl();
|
|
466
|
+
});
|
|
467
|
+
loadingOptCb.addEventListener("change", () => {
|
|
468
|
+
mgr?.options.setLoading(loadingOptCb.checked);
|
|
469
|
+
syncUrl();
|
|
470
|
+
});
|
|
471
|
+
setShowFilterOptCb.addEventListener("change", () => {
|
|
472
|
+
mgr?.options.setShowFilter(setShowFilterOptCb.checked);
|
|
473
|
+
syncUrl();
|
|
474
|
+
});
|
|
475
|
+
setShowFooterOptCb.addEventListener("change", () => {
|
|
476
|
+
mgr?.options.setShowFooter(setShowFooterOptCb.checked);
|
|
477
|
+
syncUrl();
|
|
478
|
+
});
|
|
479
|
+
labelInputOpt.addEventListener("input", () => {
|
|
480
|
+
mgr?.options.setLabel(labelInputOpt.value);
|
|
481
|
+
syncUrl();
|
|
482
|
+
});
|
|
483
|
+
optFocusBtn.addEventListener("click", () => mgr.selected.setFocus());
|
|
484
|
+
const setMH = (val) => {
|
|
485
|
+
maxHeightInput.value = val || "";
|
|
486
|
+
mgr.options.setMaxHeight(val || "");
|
|
487
|
+
syncUrl();
|
|
488
|
+
};
|
|
489
|
+
maxHeightBtn.addEventListener("click", () => setMH(maxHeightInput.value));
|
|
490
|
+
section.querySelectorAll('[data-role="mh-preset"]').forEach((btn) => {
|
|
491
|
+
btn.addEventListener("click", () => setMH(btn.dataset.value));
|
|
492
|
+
});
|
|
493
|
+
const stepMH = (delta) => {
|
|
494
|
+
let current = parseInt(maxHeightInput.value) || 0;
|
|
495
|
+
current += delta;
|
|
496
|
+
if (current < 0)
|
|
497
|
+
current = 0;
|
|
498
|
+
setMH(current + "px");
|
|
499
|
+
};
|
|
500
|
+
section.querySelector('[data-role="maxheight-up"]')?.addEventListener("click", () => stepMH(10));
|
|
501
|
+
section.querySelector('[data-role="maxheight-down"]')?.addEventListener("click", () => stepMH(-10));
|
|
502
|
+
positionSelect.value = states.position || "cover-bottom";
|
|
503
|
+
positionSelect.addEventListener("change", () => {
|
|
504
|
+
mgr?.container.setPosition(positionSelect.value);
|
|
505
|
+
syncUrl();
|
|
506
|
+
});
|
|
507
|
+
resizer.addEventListener("onLeft", () => syncUrl());
|
|
508
|
+
resizer.addEventListener("onCenter", () => syncUrl());
|
|
509
|
+
resizer.addEventListener("onHeight", () => syncUrl());
|
|
510
|
+
destroyBtn.addEventListener("click", () => {
|
|
511
|
+
mgr?.destroy();
|
|
512
|
+
section.remove();
|
|
513
|
+
});
|
|
514
|
+
if (templatesArea) {
|
|
515
|
+
templatesArea.querySelectorAll(".template-btn").forEach((btn) => {
|
|
516
|
+
btn.addEventListener("click", () => {
|
|
517
|
+
const color = btn.dataset.color;
|
|
518
|
+
const img = btn.dataset.img;
|
|
519
|
+
const newItem = {
|
|
520
|
+
id: globalIdCounter++,
|
|
521
|
+
label: img.split(".")[0],
|
|
522
|
+
color: color,
|
|
523
|
+
img: img,
|
|
524
|
+
};
|
|
525
|
+
const selected = deduplicateArrayById([...mgr.selected.getSelected(), newItem]);
|
|
526
|
+
mgr.selected.setSelected(selected);
|
|
527
|
+
console.log("add custom", newItem, "selected after", mgr.selected.getSelected());
|
|
528
|
+
syncUrl();
|
|
529
|
+
updateDump(selected);
|
|
530
|
+
});
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
customRenderBtn.addEventListener("click", () => {
|
|
534
|
+
mgr.selected.setRenderItem(function (item) {
|
|
535
|
+
const el = document.createElement("div");
|
|
536
|
+
el.className = "element";
|
|
537
|
+
el.dataset.id = String(item.id);
|
|
538
|
+
el.style.border = `2px solid ${item.color || "black"}`;
|
|
539
|
+
el.style.display = "flex";
|
|
540
|
+
el.style.alignItems = "center";
|
|
541
|
+
el.style.gap = "5px";
|
|
542
|
+
el.style.padding = "5px";
|
|
543
|
+
el.style.background = "#fff";
|
|
544
|
+
if (item.img) {
|
|
545
|
+
const img = document.createElement("img");
|
|
546
|
+
img.src = `../img/${item.img}`;
|
|
547
|
+
img.style.width = "20px";
|
|
548
|
+
img.style.height = "20px";
|
|
549
|
+
img.style.objectFit = "contain";
|
|
550
|
+
el.appendChild(img);
|
|
551
|
+
}
|
|
552
|
+
const label = document.createElement("label");
|
|
553
|
+
label.textContent = item.label;
|
|
554
|
+
const del = document.createElement("div");
|
|
555
|
+
del.dataset.remove = String(item.id);
|
|
556
|
+
el.appendChild(label);
|
|
557
|
+
el.appendChild(del);
|
|
558
|
+
return el;
|
|
559
|
+
});
|
|
560
|
+
});
|
|
561
|
+
customListRenderBtn.addEventListener("click", () => {
|
|
562
|
+
mgr.selected.setRenderList(function (selected, defaultRenderList) {
|
|
563
|
+
const elements = defaultRenderList(selected);
|
|
564
|
+
const groups = [];
|
|
565
|
+
for (let i = 0; i < elements.length; i += 3) {
|
|
566
|
+
const groupDiv = document.createElement("div");
|
|
567
|
+
groupDiv.style.border = "1px solid #1a73e8";
|
|
568
|
+
groupDiv.style.borderRadius = "8px";
|
|
569
|
+
groupDiv.style.padding = "8px";
|
|
570
|
+
groupDiv.style.margin = "4px";
|
|
571
|
+
groupDiv.style.display = "flex";
|
|
572
|
+
groupDiv.style.gap = "8px";
|
|
573
|
+
groupDiv.style.flexWrap = "wrap";
|
|
574
|
+
groupDiv.style.background = "#e8f0fe";
|
|
575
|
+
groupDiv.style.boxShadow = "0 2px 5px rgba(0,0,0,0.05)";
|
|
576
|
+
const chunk = elements.slice(i, i + 3);
|
|
577
|
+
chunk.forEach((el) => groupDiv.appendChild(el));
|
|
578
|
+
groups.push(groupDiv);
|
|
579
|
+
}
|
|
580
|
+
return groups;
|
|
581
|
+
});
|
|
582
|
+
});
|
|
583
|
+
resetTemplatesBtn.addEventListener("click", () => {
|
|
584
|
+
mgr.selected.setRenderItem();
|
|
585
|
+
mgr.selected.setRenderList();
|
|
586
|
+
});
|
|
587
|
+
optRenderBtn.addEventListener("click", () => {
|
|
588
|
+
mgr.options.setRenderItem((item) => {
|
|
589
|
+
const el = document.createElement("div");
|
|
590
|
+
el.className = "element";
|
|
591
|
+
el.style.padding = "12px";
|
|
592
|
+
el.style.background = item.selected ? "#fff3e0" : "transparent";
|
|
593
|
+
el.style.color = item.selected ? "#e65100" : "inherit";
|
|
594
|
+
el.dataset.id = String(item.id);
|
|
595
|
+
const icon = document.createElement("span");
|
|
596
|
+
icon.textContent = item.selected ? "🔥 " : "❄️ ";
|
|
597
|
+
icon.style.marginRight = "10px";
|
|
598
|
+
const label = document.createElement("label");
|
|
599
|
+
label.textContent = item.label || "";
|
|
600
|
+
el.appendChild(icon);
|
|
601
|
+
el.appendChild(label);
|
|
602
|
+
return el;
|
|
603
|
+
});
|
|
604
|
+
});
|
|
605
|
+
optStringRenderBtn.addEventListener("click", () => {
|
|
606
|
+
mgr.options.setRenderItem((item) => `
|
|
607
|
+
<div class="element" data-id="${item.id}" style="border: 1px solid #ccc; margin: 2px; border-radius: 20px; padding: 5px 15px; background: ${item.selected ? "#e8f5e9" : "white"}; color: ${item.selected ? "#2e7d32" : "#333"};">
|
|
608
|
+
<span style="font-size: 1.2em; vertical-align: middle;">${item.selected ? "✅" : "⬜"}</span>
|
|
609
|
+
<strong style="margin-left: 10px;">${item.label}</strong>
|
|
610
|
+
<small style="margin-left: auto; opacity: 0.5;">#${item.id}</small>
|
|
611
|
+
</div>
|
|
612
|
+
`);
|
|
613
|
+
});
|
|
614
|
+
optDefaultRenderBtn.addEventListener("click", () => {
|
|
615
|
+
mgr?.options.setRenderItem();
|
|
616
|
+
});
|
|
617
|
+
optEmptyBtn.addEventListener("click", () => {
|
|
618
|
+
mgr?.options.setRenderEmpty(() => `<div style="padding: 40px; text-align: center; color: #ff5252; font-weight: bold; border: 2px dashed #ff5252; border-radius: 8px;">⚠️ Custom Empty State!</div>`);
|
|
619
|
+
});
|
|
620
|
+
optDefaultEmptyBtn.addEventListener("click", () => {
|
|
621
|
+
mgr?.options.setRenderEmpty();
|
|
622
|
+
});
|
|
623
|
+
focusBtn.addEventListener("click", () => mgr.selected.setFocus());
|
|
624
|
+
};
|
|
625
|
+
const initBtn = document.getElementById("init-btn");
|
|
626
|
+
initBtn?.addEventListener("click", () => init());
|
|
627
|
+
const loadFromUrl = () => {
|
|
628
|
+
const instancesArea = document.getElementById("instances-area");
|
|
629
|
+
if (instancesArea)
|
|
630
|
+
instancesArea.innerHTML = "";
|
|
631
|
+
instanceCounter = 0;
|
|
632
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
633
|
+
const allIds = urlStateConfig.getAllIds(urlParams);
|
|
634
|
+
if (allIds.length === 0) {
|
|
635
|
+
init([], {});
|
|
636
|
+
}
|
|
637
|
+
else {
|
|
638
|
+
allIds.forEach((id) => {
|
|
639
|
+
const state = urlStateConfig.fromUrl(urlParams, id);
|
|
640
|
+
const restored = (state.selected || []).map((e) => {
|
|
641
|
+
e.id = parseInt(e.id, 10);
|
|
642
|
+
return e;
|
|
643
|
+
});
|
|
644
|
+
console.log("restored", restored);
|
|
645
|
+
init(restored, state);
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
updateUrlDisplay();
|
|
649
|
+
};
|
|
650
|
+
window.addEventListener("popstate", loadFromUrl);
|
|
651
|
+
loadFromUrl();
|