@design.estate/dees-catalog 3.55.6 → 3.56.1
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_bundle/bundle.js +2188 -2043
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/elements/00group-input/dees-input-dropdown/dees-input-dropdown-popup.d.ts +44 -0
- package/dist_ts_web/elements/00group-input/dees-input-dropdown/dees-input-dropdown-popup.js +434 -0
- package/dist_ts_web/elements/00group-input/dees-input-dropdown/dees-input-dropdown.d.ts +5 -13
- package/dist_ts_web/elements/00group-input/dees-input-dropdown/dees-input-dropdown.js +74 -275
- package/dist_ts_web/elements/00group-input/dees-input-dropdown/index.d.ts +1 -0
- package/dist_ts_web/elements/00group-input/dees-input-dropdown/index.js +2 -1
- package/dist_watch/bundle.js +2188 -2043
- package/dist_watch/bundle.js.map +4 -4
- package/package.json +1 -1
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/elements/00group-input/dees-input-dropdown/dees-input-dropdown-popup.ts +374 -0
- package/ts_web/elements/00group-input/dees-input-dropdown/dees-input-dropdown.ts +88 -263
- package/ts_web/elements/00group-input/dees-input-dropdown/index.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@design.estate/dees-catalog",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.56.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.",
|
|
6
6
|
"main": "dist_ts_web/index.js",
|
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@design.estate/dees-catalog',
|
|
6
|
-
version: '3.
|
|
6
|
+
version: '3.56.1',
|
|
7
7
|
description: 'A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.'
|
|
8
8
|
}
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
import {
|
|
2
|
+
customElement,
|
|
3
|
+
type TemplateResult,
|
|
4
|
+
property,
|
|
5
|
+
state,
|
|
6
|
+
html,
|
|
7
|
+
css,
|
|
8
|
+
cssManager,
|
|
9
|
+
DeesElement,
|
|
10
|
+
} from '@design.estate/dees-element';
|
|
11
|
+
import * as domtools from '@design.estate/dees-domtools';
|
|
12
|
+
import { zIndexRegistry } from '../../00zindex.js';
|
|
13
|
+
import { cssGeistFontFamily } from '../../00fonts.js';
|
|
14
|
+
import { themeDefaultStyles } from '../../00theme.js';
|
|
15
|
+
import { DeesWindowLayer } from '../../00group-overlay/dees-windowlayer/dees-windowlayer.js';
|
|
16
|
+
|
|
17
|
+
declare global {
|
|
18
|
+
interface HTMLElementTagNameMap {
|
|
19
|
+
'dees-input-dropdown-popup': DeesInputDropdownPopup;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@customElement('dees-input-dropdown-popup')
|
|
24
|
+
export class DeesInputDropdownPopup extends DeesElement {
|
|
25
|
+
@property({ type: Array })
|
|
26
|
+
accessor options: { option: string; key: string; payload?: any }[] = [];
|
|
27
|
+
|
|
28
|
+
@property({ type: Boolean })
|
|
29
|
+
accessor enableSearch: boolean = true;
|
|
30
|
+
|
|
31
|
+
@property({ type: Boolean })
|
|
32
|
+
accessor opensToTop: boolean = false;
|
|
33
|
+
|
|
34
|
+
@property({ attribute: false })
|
|
35
|
+
accessor triggerRect: DOMRect | null = null;
|
|
36
|
+
|
|
37
|
+
@property({ attribute: false })
|
|
38
|
+
accessor ownerComponent: HTMLElement | null = null;
|
|
39
|
+
|
|
40
|
+
@state()
|
|
41
|
+
accessor filteredOptions: { option: string; key: string; payload?: any }[] = [];
|
|
42
|
+
|
|
43
|
+
@state()
|
|
44
|
+
accessor highlightedIndex: number = 0;
|
|
45
|
+
|
|
46
|
+
@state()
|
|
47
|
+
accessor searchValue: string = '';
|
|
48
|
+
|
|
49
|
+
@state()
|
|
50
|
+
accessor menuZIndex: number = 1000;
|
|
51
|
+
|
|
52
|
+
@state()
|
|
53
|
+
accessor visible: boolean = false;
|
|
54
|
+
|
|
55
|
+
private windowLayer: DeesWindowLayer | null = null;
|
|
56
|
+
private isDestroying: boolean = false;
|
|
57
|
+
|
|
58
|
+
public static styles = [
|
|
59
|
+
themeDefaultStyles,
|
|
60
|
+
cssManager.defaultStyles,
|
|
61
|
+
css`
|
|
62
|
+
:host {
|
|
63
|
+
position: fixed;
|
|
64
|
+
top: 0;
|
|
65
|
+
left: 0;
|
|
66
|
+
width: 0;
|
|
67
|
+
height: 0;
|
|
68
|
+
pointer-events: none;
|
|
69
|
+
font-family: ${cssGeistFontFamily};
|
|
70
|
+
color: ${cssManager.bdTheme('hsl(0 0% 15%)', 'hsl(0 0% 90%)')};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
* {
|
|
74
|
+
box-sizing: border-box;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.selectionBox {
|
|
78
|
+
position: fixed;
|
|
79
|
+
pointer-events: auto;
|
|
80
|
+
will-change: transform, opacity;
|
|
81
|
+
transition: all 0.15s ease;
|
|
82
|
+
opacity: 0;
|
|
83
|
+
transform: translateY(-8px) scale(0.98);
|
|
84
|
+
background: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(0 0% 3.9%)')};
|
|
85
|
+
border: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')};
|
|
86
|
+
box-shadow: 0 4px 6px -1px hsl(0 0% 0% / 0.1), 0 2px 4px -2px hsl(0 0% 0% / 0.1);
|
|
87
|
+
min-height: 40px;
|
|
88
|
+
max-height: 300px;
|
|
89
|
+
overflow: hidden;
|
|
90
|
+
border-radius: 6px;
|
|
91
|
+
user-select: none;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.selectionBox.top {
|
|
95
|
+
transform: translateY(8px) scale(0.98);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.selectionBox.show {
|
|
99
|
+
pointer-events: auto;
|
|
100
|
+
transform: translateY(0) scale(1);
|
|
101
|
+
opacity: 1;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.options-container {
|
|
105
|
+
max-height: 250px;
|
|
106
|
+
overflow-y: auto;
|
|
107
|
+
padding: 4px;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.option {
|
|
111
|
+
transition: all 0.15s ease;
|
|
112
|
+
line-height: 32px;
|
|
113
|
+
padding: 0 8px;
|
|
114
|
+
border-radius: 4px;
|
|
115
|
+
margin: 2px 0;
|
|
116
|
+
cursor: pointer;
|
|
117
|
+
font-size: 14px;
|
|
118
|
+
color: ${cssManager.bdTheme('hsl(0 0% 15%)', 'hsl(0 0% 90%)')};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.option.highlighted {
|
|
122
|
+
background: ${cssManager.bdTheme('hsl(0 0% 95.1%)', 'hsl(0 0% 14.9%)')};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.option:hover {
|
|
126
|
+
background: ${cssManager.bdTheme('hsl(0 0% 95.1%)', 'hsl(0 0% 14.9%)')};
|
|
127
|
+
color: ${cssManager.bdTheme('hsl(0 0% 9%)', 'hsl(0 0% 95%)')};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.no-options {
|
|
131
|
+
padding: 8px;
|
|
132
|
+
text-align: center;
|
|
133
|
+
font-size: 14px;
|
|
134
|
+
color: ${cssManager.bdTheme('hsl(0 0% 45.1%)', 'hsl(0 0% 63.9%)')};
|
|
135
|
+
font-style: italic;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.search {
|
|
139
|
+
padding: 4px;
|
|
140
|
+
border-bottom: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')};
|
|
141
|
+
margin-bottom: 4px;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.search input {
|
|
145
|
+
display: block;
|
|
146
|
+
width: 100%;
|
|
147
|
+
height: 32px;
|
|
148
|
+
padding: 0 8px;
|
|
149
|
+
background: transparent;
|
|
150
|
+
border: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')};
|
|
151
|
+
border-radius: 4px;
|
|
152
|
+
color: inherit;
|
|
153
|
+
font-size: 14px;
|
|
154
|
+
font-family: inherit;
|
|
155
|
+
outline: none;
|
|
156
|
+
transition: border-color 0.15s ease;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.search input::placeholder {
|
|
160
|
+
color: ${cssManager.bdTheme('hsl(0 0% 63.9%)', 'hsl(0 0% 45.1%)')};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.search input:focus {
|
|
164
|
+
border-color: ${cssManager.bdTheme('hsl(222.2 47.4% 51.2%)', 'hsl(217.2 91.2% 59.8%)')};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.options-container::-webkit-scrollbar {
|
|
168
|
+
width: 8px;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.options-container::-webkit-scrollbar-track {
|
|
172
|
+
background: transparent;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.options-container::-webkit-scrollbar-thumb {
|
|
176
|
+
background: ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')};
|
|
177
|
+
border-radius: 4px;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.options-container::-webkit-scrollbar-thumb:hover {
|
|
181
|
+
background: ${cssManager.bdTheme('hsl(0 0% 79.8%)', 'hsl(0 0% 20.9%)')};
|
|
182
|
+
}
|
|
183
|
+
`,
|
|
184
|
+
];
|
|
185
|
+
|
|
186
|
+
public render(): TemplateResult {
|
|
187
|
+
if (!this.triggerRect) return html``;
|
|
188
|
+
|
|
189
|
+
const posStyle = this.computePositionStyle();
|
|
190
|
+
|
|
191
|
+
return html`
|
|
192
|
+
<div
|
|
193
|
+
class="selectionBox ${this.visible ? 'show' : ''} ${this.opensToTop ? 'top' : 'bottom'}"
|
|
194
|
+
style="${posStyle}; z-index: ${this.menuZIndex};"
|
|
195
|
+
>
|
|
196
|
+
${this.enableSearch
|
|
197
|
+
? html`
|
|
198
|
+
<div class="search">
|
|
199
|
+
<input
|
|
200
|
+
type="text"
|
|
201
|
+
placeholder="Search options..."
|
|
202
|
+
.value="${this.searchValue}"
|
|
203
|
+
@input="${this.handleSearch}"
|
|
204
|
+
@click="${(e: Event) => e.stopPropagation()}"
|
|
205
|
+
@keydown="${this.handleSearchKeydown}"
|
|
206
|
+
/>
|
|
207
|
+
</div>
|
|
208
|
+
`
|
|
209
|
+
: null}
|
|
210
|
+
<div class="options-container">
|
|
211
|
+
${this.filteredOptions.length === 0
|
|
212
|
+
? html`<div class="no-options">No options found</div>`
|
|
213
|
+
: this.filteredOptions.map((option, index) => {
|
|
214
|
+
const isHighlighted = this.highlightedIndex === index;
|
|
215
|
+
return html`
|
|
216
|
+
<div
|
|
217
|
+
class="option ${isHighlighted ? 'highlighted' : ''}"
|
|
218
|
+
@click="${() => this.selectOption(option)}"
|
|
219
|
+
@mouseenter="${() => (this.highlightedIndex = index)}"
|
|
220
|
+
>
|
|
221
|
+
${option.option}
|
|
222
|
+
</div>
|
|
223
|
+
`;
|
|
224
|
+
})}
|
|
225
|
+
</div>
|
|
226
|
+
</div>
|
|
227
|
+
`;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
private computePositionStyle(): string {
|
|
231
|
+
const rect = this.triggerRect!;
|
|
232
|
+
const left = rect.left;
|
|
233
|
+
const width = rect.width;
|
|
234
|
+
|
|
235
|
+
if (this.opensToTop) {
|
|
236
|
+
const bottom = window.innerHeight - rect.top + 4;
|
|
237
|
+
return `left: ${left}px; width: ${width}px; bottom: ${bottom}px; top: auto`;
|
|
238
|
+
} else {
|
|
239
|
+
const top = rect.bottom + 4;
|
|
240
|
+
return `left: ${left}px; width: ${width}px; top: ${top}px`;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
public async show(): Promise<void> {
|
|
245
|
+
this.filteredOptions = this.options;
|
|
246
|
+
this.highlightedIndex = 0;
|
|
247
|
+
this.searchValue = '';
|
|
248
|
+
|
|
249
|
+
// Create window layer (transparent, no blur)
|
|
250
|
+
this.windowLayer = await DeesWindowLayer.createAndShow();
|
|
251
|
+
this.windowLayer.addEventListener('click', () => {
|
|
252
|
+
this.dispatchEvent(new CustomEvent('close-request'));
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
// Set z-index above the window layer
|
|
256
|
+
this.menuZIndex = zIndexRegistry.getNextZIndex();
|
|
257
|
+
zIndexRegistry.register(this, this.menuZIndex);
|
|
258
|
+
this.style.zIndex = this.menuZIndex.toString();
|
|
259
|
+
|
|
260
|
+
document.body.appendChild(this);
|
|
261
|
+
|
|
262
|
+
// Animate in on next frame
|
|
263
|
+
await domtools.plugins.smartdelay.delayFor(0);
|
|
264
|
+
this.visible = true;
|
|
265
|
+
|
|
266
|
+
// Add scroll/resize listeners for repositioning
|
|
267
|
+
window.addEventListener('scroll', this.handleScrollOrResize, { capture: true, passive: true });
|
|
268
|
+
window.addEventListener('resize', this.handleScrollOrResize, { passive: true });
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
public async hide(): Promise<void> {
|
|
272
|
+
// Guard against double-destruction
|
|
273
|
+
if (this.isDestroying) {
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
this.isDestroying = true;
|
|
277
|
+
|
|
278
|
+
// Remove scroll/resize listeners
|
|
279
|
+
window.removeEventListener('scroll', this.handleScrollOrResize, { capture: true } as EventListenerOptions);
|
|
280
|
+
window.removeEventListener('resize', this.handleScrollOrResize);
|
|
281
|
+
|
|
282
|
+
zIndexRegistry.unregister(this);
|
|
283
|
+
|
|
284
|
+
this.searchValue = '';
|
|
285
|
+
this.filteredOptions = this.options;
|
|
286
|
+
this.highlightedIndex = 0;
|
|
287
|
+
|
|
288
|
+
// Don't await - let window layer cleanup happen in background for instant visual feedback
|
|
289
|
+
if (this.windowLayer) {
|
|
290
|
+
this.windowLayer.destroy();
|
|
291
|
+
this.windowLayer = null;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Animate out via CSS transition
|
|
295
|
+
this.visible = false;
|
|
296
|
+
await domtools.plugins.smartdelay.delayFor(150);
|
|
297
|
+
|
|
298
|
+
if (this.parentElement) {
|
|
299
|
+
this.parentElement.removeChild(this);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
this.isDestroying = false;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
public async focusSearchInput(): Promise<void> {
|
|
306
|
+
await this.updateComplete;
|
|
307
|
+
const input = this.shadowRoot!.querySelector('.search input') as HTMLInputElement;
|
|
308
|
+
if (input) input.focus();
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
public updateOptions(options: { option: string; key: string; payload?: any }[]): void {
|
|
312
|
+
this.options = options;
|
|
313
|
+
// Re-filter with current search value
|
|
314
|
+
if (this.searchValue) {
|
|
315
|
+
const searchLower = this.searchValue.toLowerCase();
|
|
316
|
+
this.filteredOptions = this.options.filter((opt) =>
|
|
317
|
+
opt.option.toLowerCase().includes(searchLower)
|
|
318
|
+
);
|
|
319
|
+
} else {
|
|
320
|
+
this.filteredOptions = this.options;
|
|
321
|
+
}
|
|
322
|
+
this.highlightedIndex = 0;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
private selectOption(option: { option: string; key: string; payload?: any }): void {
|
|
326
|
+
this.dispatchEvent(
|
|
327
|
+
new CustomEvent('option-selected', {
|
|
328
|
+
detail: option,
|
|
329
|
+
})
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
private handleSearch = (event: Event): void => {
|
|
334
|
+
const searchTerm = (event.target as HTMLInputElement).value;
|
|
335
|
+
this.searchValue = searchTerm;
|
|
336
|
+
const searchLower = searchTerm.toLowerCase();
|
|
337
|
+
this.filteredOptions = this.options.filter((option) =>
|
|
338
|
+
option.option.toLowerCase().includes(searchLower)
|
|
339
|
+
);
|
|
340
|
+
this.highlightedIndex = 0;
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
private handleSearchKeydown = (event: KeyboardEvent): void => {
|
|
344
|
+
const key = event.key;
|
|
345
|
+
const maxIndex = this.filteredOptions.length - 1;
|
|
346
|
+
|
|
347
|
+
if (key === 'ArrowDown') {
|
|
348
|
+
event.preventDefault();
|
|
349
|
+
this.highlightedIndex = this.highlightedIndex + 1 > maxIndex ? 0 : this.highlightedIndex + 1;
|
|
350
|
+
} else if (key === 'ArrowUp') {
|
|
351
|
+
event.preventDefault();
|
|
352
|
+
this.highlightedIndex = this.highlightedIndex - 1 < 0 ? maxIndex : this.highlightedIndex - 1;
|
|
353
|
+
} else if (key === 'Enter') {
|
|
354
|
+
event.preventDefault();
|
|
355
|
+
if (this.filteredOptions[this.highlightedIndex]) {
|
|
356
|
+
this.selectOption(this.filteredOptions[this.highlightedIndex]);
|
|
357
|
+
}
|
|
358
|
+
} else if (key === 'Escape') {
|
|
359
|
+
event.preventDefault();
|
|
360
|
+
this.dispatchEvent(new CustomEvent('close-request'));
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
private handleScrollOrResize = (): void => {
|
|
365
|
+
this.dispatchEvent(new CustomEvent('reposition-request'));
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
async disconnectedCallback() {
|
|
369
|
+
await super.disconnectedCallback();
|
|
370
|
+
window.removeEventListener('scroll', this.handleScrollOrResize, { capture: true } as EventListenerOptions);
|
|
371
|
+
window.removeEventListener('resize', this.handleScrollOrResize);
|
|
372
|
+
zIndexRegistry.unregister(this);
|
|
373
|
+
}
|
|
374
|
+
}
|