@design.estate/dees-catalog 3.57.0 → 3.59.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/dist_bundle/bundle.js +2721 -2518
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/elements/00group-input/dees-input-code/dees-input-code.d.ts +5 -0
- package/dist_ts_web/elements/00group-input/dees-input-code/dees-input-code.js +196 -29
- package/dist_ts_web/elements/00group-input/dees-input-datepicker/component.d.ts +10 -27
- package/dist_ts_web/elements/00group-input/dees-input-datepicker/component.js +118 -281
- package/dist_ts_web/elements/00group-input/dees-input-datepicker/datepicker-popup.d.ts +55 -0
- package/dist_ts_web/elements/00group-input/dees-input-datepicker/datepicker-popup.js +876 -0
- package/dist_ts_web/elements/00group-input/dees-input-datepicker/index.d.ts +1 -0
- package/dist_ts_web/elements/00group-input/dees-input-datepicker/index.js +2 -1
- package/dist_ts_web/elements/00group-input/dees-input-datepicker/styles.js +1 -408
- package/dist_ts_web/elements/00group-input/dees-input-datepicker/template.js +1 -143
- package/dist_watch/bundle.js +2721 -2518
- 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-code/dees-input-code.ts +194 -30
- package/ts_web/elements/00group-input/dees-input-datepicker/component.ts +125 -324
- package/ts_web/elements/00group-input/dees-input-datepicker/datepicker-popup.ts +758 -0
- package/ts_web/elements/00group-input/dees-input-datepicker/index.ts +1 -0
- package/ts_web/elements/00group-input/dees-input-datepicker/styles.ts +1 -411
- package/ts_web/elements/00group-input/dees-input-datepicker/template.ts +1 -147
|
@@ -0,0 +1,758 @@
|
|
|
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 { themeDefaultStyles } from '../../00theme.js';
|
|
14
|
+
import { DeesWindowLayer } from '../../00group-overlay/dees-windowlayer/dees-windowlayer.js';
|
|
15
|
+
import '../../00group-utility/dees-icon/dees-icon.js';
|
|
16
|
+
import type { IDateEvent } from './types.js';
|
|
17
|
+
|
|
18
|
+
declare global {
|
|
19
|
+
interface HTMLElementTagNameMap {
|
|
20
|
+
'dees-input-datepicker-popup': DeesInputDatepickerPopup;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@customElement('dees-input-datepicker-popup')
|
|
25
|
+
export class DeesInputDatepickerPopup extends DeesElement {
|
|
26
|
+
// Properties set by the parent
|
|
27
|
+
@property({ attribute: false })
|
|
28
|
+
accessor triggerRect: DOMRect | null = null;
|
|
29
|
+
|
|
30
|
+
@property({ attribute: false })
|
|
31
|
+
accessor ownerComponent: HTMLElement | null = null;
|
|
32
|
+
|
|
33
|
+
@property({ type: Boolean })
|
|
34
|
+
accessor enableTime: boolean = false;
|
|
35
|
+
|
|
36
|
+
@property({ type: String })
|
|
37
|
+
accessor timeFormat: '24h' | '12h' = '24h';
|
|
38
|
+
|
|
39
|
+
@property({ type: Number })
|
|
40
|
+
accessor minuteIncrement: number = 1;
|
|
41
|
+
|
|
42
|
+
@property({ type: Number })
|
|
43
|
+
accessor weekStartsOn: 0 | 1 = 1;
|
|
44
|
+
|
|
45
|
+
@property({ type: String })
|
|
46
|
+
accessor minDate: string = '';
|
|
47
|
+
|
|
48
|
+
@property({ type: String })
|
|
49
|
+
accessor maxDate: string = '';
|
|
50
|
+
|
|
51
|
+
@property({ type: Array })
|
|
52
|
+
accessor disabledDates: string[] = [];
|
|
53
|
+
|
|
54
|
+
@property({ type: Boolean })
|
|
55
|
+
accessor enableTimezone: boolean = false;
|
|
56
|
+
|
|
57
|
+
@property({ type: String })
|
|
58
|
+
accessor timezone: string = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
59
|
+
|
|
60
|
+
@property({ type: Array })
|
|
61
|
+
accessor events: IDateEvent[] = [];
|
|
62
|
+
|
|
63
|
+
@property({ type: Boolean })
|
|
64
|
+
accessor opensToTop: boolean = false;
|
|
65
|
+
|
|
66
|
+
// Internal state
|
|
67
|
+
@state()
|
|
68
|
+
accessor selectedDate: Date | null = null;
|
|
69
|
+
|
|
70
|
+
@state()
|
|
71
|
+
accessor viewDate: Date = new Date();
|
|
72
|
+
|
|
73
|
+
@state()
|
|
74
|
+
accessor selectedHour: number = 0;
|
|
75
|
+
|
|
76
|
+
@state()
|
|
77
|
+
accessor selectedMinute: number = 0;
|
|
78
|
+
|
|
79
|
+
@state()
|
|
80
|
+
accessor menuZIndex: number = 1000;
|
|
81
|
+
|
|
82
|
+
@state()
|
|
83
|
+
accessor visible: boolean = false;
|
|
84
|
+
|
|
85
|
+
private windowLayer: DeesWindowLayer | null = null;
|
|
86
|
+
private isDestroying: boolean = false;
|
|
87
|
+
|
|
88
|
+
public static styles = [
|
|
89
|
+
themeDefaultStyles,
|
|
90
|
+
cssManager.defaultStyles,
|
|
91
|
+
css`
|
|
92
|
+
:host {
|
|
93
|
+
position: fixed;
|
|
94
|
+
top: 0;
|
|
95
|
+
left: 0;
|
|
96
|
+
width: 0;
|
|
97
|
+
height: 0;
|
|
98
|
+
pointer-events: none;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
* {
|
|
102
|
+
box-sizing: border-box;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.calendar-popup {
|
|
106
|
+
position: fixed;
|
|
107
|
+
pointer-events: auto;
|
|
108
|
+
will-change: transform, opacity;
|
|
109
|
+
transition: all 0.15s ease;
|
|
110
|
+
opacity: 0;
|
|
111
|
+
transform: translateY(-4px);
|
|
112
|
+
background: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(224 71.4% 4.1%)')};
|
|
113
|
+
border: 1px solid ${cssManager.bdTheme('hsl(214.3 31.8% 91.4%)', 'hsl(217.2 32.6% 17.5%)')};
|
|
114
|
+
box-shadow: ${cssManager.bdTheme(
|
|
115
|
+
'0 10px 15px -3px hsl(0 0% 0% / 0.1), 0 4px 6px -4px hsl(0 0% 0% / 0.1)',
|
|
116
|
+
'0 10px 15px -3px hsl(0 0% 0% / 0.2), 0 4px 6px -4px hsl(0 0% 0% / 0.2)'
|
|
117
|
+
)};
|
|
118
|
+
border-radius: 6px;
|
|
119
|
+
padding: 12px;
|
|
120
|
+
user-select: none;
|
|
121
|
+
min-width: 280px;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.calendar-popup.top {
|
|
125
|
+
transform: translateY(4px);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.calendar-popup.show {
|
|
129
|
+
transform: translateY(0);
|
|
130
|
+
opacity: 1;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/* Calendar Header */
|
|
134
|
+
.calendar-header {
|
|
135
|
+
display: flex;
|
|
136
|
+
align-items: center;
|
|
137
|
+
justify-content: space-between;
|
|
138
|
+
margin-bottom: 16px;
|
|
139
|
+
gap: 8px;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.month-year-display {
|
|
143
|
+
font-weight: 500;
|
|
144
|
+
font-size: 14px;
|
|
145
|
+
color: ${cssManager.bdTheme('hsl(224 71.4% 4.1%)', 'hsl(210 20% 98%)')};
|
|
146
|
+
flex: 1;
|
|
147
|
+
text-align: center;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.nav-button {
|
|
151
|
+
width: 28px;
|
|
152
|
+
height: 28px;
|
|
153
|
+
border: none;
|
|
154
|
+
background: transparent;
|
|
155
|
+
cursor: pointer;
|
|
156
|
+
border-radius: 6px;
|
|
157
|
+
display: flex;
|
|
158
|
+
align-items: center;
|
|
159
|
+
justify-content: center;
|
|
160
|
+
color: ${cssManager.bdTheme('hsl(220 8.9% 46.1%)', 'hsl(215 20.2% 65.1%)')};
|
|
161
|
+
transition: all 0.2s ease;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.nav-button:hover {
|
|
165
|
+
background: ${cssManager.bdTheme('hsl(210 20% 98%)', 'hsl(215 27.9% 16.9%)')};
|
|
166
|
+
color: ${cssManager.bdTheme('hsl(224 71.4% 4.1%)', 'hsl(210 20% 98%)')};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/* Weekday headers */
|
|
170
|
+
.weekdays {
|
|
171
|
+
display: grid;
|
|
172
|
+
grid-template-columns: repeat(7, 1fr);
|
|
173
|
+
margin-bottom: 4px;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.weekday {
|
|
177
|
+
text-align: center;
|
|
178
|
+
font-size: 12px;
|
|
179
|
+
font-weight: 400;
|
|
180
|
+
color: ${cssManager.bdTheme('hsl(220 8.9% 46.1%)', 'hsl(215 20.2% 65.1%)')};
|
|
181
|
+
padding: 0 0 8px 0;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/* Days grid */
|
|
185
|
+
.days-grid {
|
|
186
|
+
display: grid;
|
|
187
|
+
grid-template-columns: repeat(7, 1fr);
|
|
188
|
+
gap: 2px;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.day {
|
|
192
|
+
aspect-ratio: 1;
|
|
193
|
+
display: flex;
|
|
194
|
+
align-items: center;
|
|
195
|
+
justify-content: center;
|
|
196
|
+
cursor: pointer;
|
|
197
|
+
border-radius: 6px;
|
|
198
|
+
font-size: 14px;
|
|
199
|
+
transition: all 0.2s ease;
|
|
200
|
+
color: ${cssManager.bdTheme('hsl(224 71.4% 4.1%)', 'hsl(210 20% 98%)')};
|
|
201
|
+
border: none;
|
|
202
|
+
width: 36px;
|
|
203
|
+
height: 36px;
|
|
204
|
+
background: transparent;
|
|
205
|
+
position: relative;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.day:hover:not(.disabled) {
|
|
209
|
+
background: ${cssManager.bdTheme('hsl(210 20% 98%)', 'hsl(215 27.9% 16.9%)')};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.day.other-month {
|
|
213
|
+
color: ${cssManager.bdTheme('hsl(220 8.9% 46.1%)', 'hsl(215 20.2% 65.1%)')};
|
|
214
|
+
opacity: 0.5;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.day.today {
|
|
218
|
+
background: ${cssManager.bdTheme('hsl(210 20% 98%)', 'hsl(215 27.9% 16.9%)')};
|
|
219
|
+
font-weight: 500;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.day.selected {
|
|
223
|
+
background: ${cssManager.bdTheme('hsl(222.2 47.4% 11.2%)', 'hsl(210 20% 98%)')};
|
|
224
|
+
color: ${cssManager.bdTheme('hsl(210 20% 98%)', 'hsl(222.2 47.4% 11.2%)')};
|
|
225
|
+
font-weight: 500;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.day.disabled {
|
|
229
|
+
color: ${cssManager.bdTheme('hsl(220 8.9% 46.1%)', 'hsl(215 20.2% 65.1%)')};
|
|
230
|
+
cursor: not-allowed;
|
|
231
|
+
opacity: 0.3;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/* Event indicators */
|
|
235
|
+
.event-indicator {
|
|
236
|
+
position: absolute;
|
|
237
|
+
bottom: 4px;
|
|
238
|
+
left: 50%;
|
|
239
|
+
transform: translateX(-50%);
|
|
240
|
+
display: flex;
|
|
241
|
+
gap: 2px;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.event-dot {
|
|
245
|
+
width: 4px;
|
|
246
|
+
height: 4px;
|
|
247
|
+
border-radius: 50%;
|
|
248
|
+
background: ${cssManager.bdTheme('hsl(220 8.9% 46.1%)', 'hsl(215 20.2% 65.1%)')};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.event-dot.info { background: ${cssManager.bdTheme('hsl(211 70% 52%)', 'hsl(211 70% 62%)')}; }
|
|
252
|
+
.event-dot.warning { background: ${cssManager.bdTheme('hsl(45 90% 45%)', 'hsl(45 90% 55%)')}; }
|
|
253
|
+
.event-dot.success { background: ${cssManager.bdTheme('hsl(142 69% 45%)', 'hsl(142 69% 55%)')}; }
|
|
254
|
+
.event-dot.error { background: ${cssManager.bdTheme('hsl(0 72% 51%)', 'hsl(0 72% 61%)')}; }
|
|
255
|
+
|
|
256
|
+
.event-count {
|
|
257
|
+
position: absolute;
|
|
258
|
+
top: 2px;
|
|
259
|
+
right: 2px;
|
|
260
|
+
min-width: 16px;
|
|
261
|
+
height: 16px;
|
|
262
|
+
padding: 0 4px;
|
|
263
|
+
background: ${cssManager.bdTheme('hsl(0 72% 51%)', 'hsl(0 72% 61%)')};
|
|
264
|
+
color: white;
|
|
265
|
+
border-radius: 8px;
|
|
266
|
+
font-size: 10px;
|
|
267
|
+
font-weight: 600;
|
|
268
|
+
display: flex;
|
|
269
|
+
align-items: center;
|
|
270
|
+
justify-content: center;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
.event-tooltip {
|
|
274
|
+
position: absolute;
|
|
275
|
+
bottom: calc(100% + 8px);
|
|
276
|
+
left: 50%;
|
|
277
|
+
transform: translateX(-50%);
|
|
278
|
+
background: ${cssManager.bdTheme('hsl(0 0% 20%)', 'hsl(0 0% 90%)')};
|
|
279
|
+
color: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(0 0% 0%)')};
|
|
280
|
+
padding: 8px 12px;
|
|
281
|
+
border-radius: 6px;
|
|
282
|
+
font-size: 12px;
|
|
283
|
+
white-space: nowrap;
|
|
284
|
+
pointer-events: none;
|
|
285
|
+
opacity: 0;
|
|
286
|
+
transition: opacity 0.2s ease;
|
|
287
|
+
z-index: 10;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.day.has-event:hover .event-tooltip { opacity: 1; }
|
|
291
|
+
|
|
292
|
+
/* Time selector */
|
|
293
|
+
.time-selector {
|
|
294
|
+
margin-top: 12px;
|
|
295
|
+
padding-top: 12px;
|
|
296
|
+
border-top: 1px solid ${cssManager.bdTheme('hsl(214.3 31.8% 91.4%)', 'hsl(217.2 32.6% 17.5%)')};
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
.time-selector-title {
|
|
300
|
+
font-size: 12px;
|
|
301
|
+
font-weight: 500;
|
|
302
|
+
margin-bottom: 8px;
|
|
303
|
+
color: ${cssManager.bdTheme('hsl(220 8.9% 46.1%)', 'hsl(215 20.2% 65.1%)')};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
.time-inputs {
|
|
307
|
+
display: flex;
|
|
308
|
+
gap: 8px;
|
|
309
|
+
align-items: center;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.time-input {
|
|
313
|
+
width: 65px;
|
|
314
|
+
height: 36px;
|
|
315
|
+
border: 1px solid ${cssManager.bdTheme('hsl(214.3 31.8% 91.4%)', 'hsl(217.2 32.6% 17.5%)')};
|
|
316
|
+
border-radius: 6px;
|
|
317
|
+
padding: 0 12px;
|
|
318
|
+
font-size: 14px;
|
|
319
|
+
text-align: center;
|
|
320
|
+
background: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(224 71.4% 4.1%)')};
|
|
321
|
+
color: ${cssManager.bdTheme('hsl(224 71.4% 4.1%)', 'hsl(210 20% 98%)')};
|
|
322
|
+
transition: all 0.2s ease;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
.time-input:focus {
|
|
326
|
+
outline: none;
|
|
327
|
+
border-color: ${cssManager.bdTheme('hsl(222.2 47.4% 11.2%)', 'hsl(210 20% 98%)')};
|
|
328
|
+
box-shadow: 0 0 0 2px ${cssManager.bdTheme('hsl(222.2 47.4% 11.2% / 0.1)', 'hsl(210 20% 98% / 0.1)')};
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
.time-separator {
|
|
332
|
+
font-size: 14px;
|
|
333
|
+
font-weight: 500;
|
|
334
|
+
color: ${cssManager.bdTheme('hsl(220 8.9% 46.1%)', 'hsl(215 20.2% 65.1%)')};
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
.am-pm-selector { display: flex; gap: 4px; margin-left: 8px; }
|
|
338
|
+
|
|
339
|
+
.am-pm-button {
|
|
340
|
+
padding: 6px 12px;
|
|
341
|
+
border: 1px solid ${cssManager.bdTheme('hsl(214.3 31.8% 91.4%)', 'hsl(217.2 32.6% 17.5%)')};
|
|
342
|
+
background: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(224 71.4% 4.1%)')};
|
|
343
|
+
border-radius: 6px;
|
|
344
|
+
font-size: 12px;
|
|
345
|
+
font-weight: 500;
|
|
346
|
+
cursor: pointer;
|
|
347
|
+
transition: all 0.2s ease;
|
|
348
|
+
color: ${cssManager.bdTheme('hsl(220 8.9% 46.1%)', 'hsl(215 20.2% 65.1%)')};
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
.am-pm-button.selected {
|
|
352
|
+
background: ${cssManager.bdTheme('hsl(222.2 47.4% 11.2%)', 'hsl(210 20% 98%)')};
|
|
353
|
+
color: ${cssManager.bdTheme('hsl(210 20% 98%)', 'hsl(222.2 47.4% 11.2%)')};
|
|
354
|
+
border-color: ${cssManager.bdTheme('hsl(222.2 47.4% 11.2%)', 'hsl(210 20% 98%)')};
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
.am-pm-button:hover:not(.selected) {
|
|
358
|
+
background: ${cssManager.bdTheme('hsl(210 20% 98%)', 'hsl(215 27.9% 16.9%)')};
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/* Timezone selector */
|
|
362
|
+
.timezone-selector {
|
|
363
|
+
margin-top: 12px;
|
|
364
|
+
padding-top: 12px;
|
|
365
|
+
border-top: 1px solid ${cssManager.bdTheme('hsl(214.3 31.8% 91.4%)', 'hsl(217.2 32.6% 17.5%)')};
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.timezone-selector-title {
|
|
369
|
+
font-size: 12px;
|
|
370
|
+
font-weight: 500;
|
|
371
|
+
margin-bottom: 8px;
|
|
372
|
+
color: ${cssManager.bdTheme('hsl(220 8.9% 46.1%)', 'hsl(215 20.2% 65.1%)')};
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
.timezone-select {
|
|
376
|
+
width: 100%;
|
|
377
|
+
height: 36px;
|
|
378
|
+
border: 1px solid ${cssManager.bdTheme('hsl(214.3 31.8% 91.4%)', 'hsl(217.2 32.6% 17.5%)')};
|
|
379
|
+
border-radius: 6px;
|
|
380
|
+
padding: 0 12px;
|
|
381
|
+
font-size: 14px;
|
|
382
|
+
background: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(224 71.4% 4.1%)')};
|
|
383
|
+
color: ${cssManager.bdTheme('hsl(224 71.4% 4.1%)', 'hsl(210 20% 98%)')};
|
|
384
|
+
cursor: pointer;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/* Action buttons */
|
|
388
|
+
.calendar-actions {
|
|
389
|
+
display: flex;
|
|
390
|
+
gap: 8px;
|
|
391
|
+
margin-top: 12px;
|
|
392
|
+
padding-top: 12px;
|
|
393
|
+
border-top: 1px solid ${cssManager.bdTheme('hsl(214.3 31.8% 91.4%)', 'hsl(217.2 32.6% 17.5%)')};
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
.action-button {
|
|
397
|
+
flex: 1;
|
|
398
|
+
height: 36px;
|
|
399
|
+
border: none;
|
|
400
|
+
border-radius: 6px;
|
|
401
|
+
font-size: 14px;
|
|
402
|
+
font-weight: 500;
|
|
403
|
+
cursor: pointer;
|
|
404
|
+
transition: all 0.2s ease;
|
|
405
|
+
display: flex;
|
|
406
|
+
align-items: center;
|
|
407
|
+
justify-content: center;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
.today-button {
|
|
411
|
+
background: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(224 71.4% 4.1%)')};
|
|
412
|
+
border: 1px solid ${cssManager.bdTheme('hsl(214.3 31.8% 91.4%)', 'hsl(217.2 32.6% 17.5%)')};
|
|
413
|
+
color: ${cssManager.bdTheme('hsl(224 71.4% 4.1%)', 'hsl(210 20% 98%)')};
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
.today-button:hover {
|
|
417
|
+
background: ${cssManager.bdTheme('hsl(210 20% 98%)', 'hsl(215 27.9% 16.9%)')};
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
.clear-action-button {
|
|
421
|
+
background: transparent;
|
|
422
|
+
border: 1px solid transparent;
|
|
423
|
+
color: ${cssManager.bdTheme('hsl(220 8.9% 46.1%)', 'hsl(215 20.2% 65.1%)')};
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
.clear-action-button:hover {
|
|
427
|
+
background: ${cssManager.bdTheme('hsl(0 72.2% 50.6% / 0.1)', 'hsl(0 62.8% 30.6% / 0.1)')};
|
|
428
|
+
color: ${cssManager.bdTheme('hsl(0 72.2% 50.6%)', 'hsl(0 62.8% 30.6%)')};
|
|
429
|
+
}
|
|
430
|
+
`,
|
|
431
|
+
];
|
|
432
|
+
|
|
433
|
+
private static readonly MONTH_NAMES = [
|
|
434
|
+
'January', 'February', 'March', 'April', 'May', 'June',
|
|
435
|
+
'July', 'August', 'September', 'October', 'November', 'December',
|
|
436
|
+
];
|
|
437
|
+
|
|
438
|
+
private static readonly TIMEZONES = [
|
|
439
|
+
{ value: 'UTC', label: 'UTC (Coordinated Universal Time)' },
|
|
440
|
+
{ value: 'America/New_York', label: 'Eastern Time (US & Canada)' },
|
|
441
|
+
{ value: 'America/Chicago', label: 'Central Time (US & Canada)' },
|
|
442
|
+
{ value: 'America/Denver', label: 'Mountain Time (US & Canada)' },
|
|
443
|
+
{ value: 'America/Los_Angeles', label: 'Pacific Time (US & Canada)' },
|
|
444
|
+
{ value: 'Europe/London', label: 'London' },
|
|
445
|
+
{ value: 'Europe/Paris', label: 'Paris' },
|
|
446
|
+
{ value: 'Europe/Berlin', label: 'Berlin' },
|
|
447
|
+
{ value: 'Europe/Moscow', label: 'Moscow' },
|
|
448
|
+
{ value: 'Asia/Dubai', label: 'Dubai' },
|
|
449
|
+
{ value: 'Asia/Kolkata', label: 'India Standard Time' },
|
|
450
|
+
{ value: 'Asia/Shanghai', label: 'China Standard Time' },
|
|
451
|
+
{ value: 'Asia/Tokyo', label: 'Tokyo' },
|
|
452
|
+
{ value: 'Australia/Sydney', label: 'Sydney' },
|
|
453
|
+
{ value: 'Pacific/Auckland', label: 'Auckland' },
|
|
454
|
+
];
|
|
455
|
+
|
|
456
|
+
public render(): TemplateResult {
|
|
457
|
+
if (!this.triggerRect) return html``;
|
|
458
|
+
|
|
459
|
+
const posStyle = this.computePositionStyle();
|
|
460
|
+
const weekDays = this.weekStartsOn === 1
|
|
461
|
+
? ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su']
|
|
462
|
+
: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];
|
|
463
|
+
const days = this.getDaysInMonth();
|
|
464
|
+
const isAM = this.selectedHour < 12;
|
|
465
|
+
|
|
466
|
+
return html`
|
|
467
|
+
<div
|
|
468
|
+
class="calendar-popup ${this.visible ? 'show' : ''} ${this.opensToTop ? 'top' : 'bottom'}"
|
|
469
|
+
style="${posStyle}; z-index: ${this.menuZIndex};"
|
|
470
|
+
>
|
|
471
|
+
<div class="calendar-header">
|
|
472
|
+
<button class="nav-button" @click=${this.previousMonth}>
|
|
473
|
+
<dees-icon icon="lucide:chevronLeft" iconSize="16"></dees-icon>
|
|
474
|
+
</button>
|
|
475
|
+
<div class="month-year-display">
|
|
476
|
+
${DeesInputDatepickerPopup.MONTH_NAMES[this.viewDate.getMonth()]} ${this.viewDate.getFullYear()}
|
|
477
|
+
</div>
|
|
478
|
+
<button class="nav-button" @click=${this.nextMonth}>
|
|
479
|
+
<dees-icon icon="lucide:chevronRight" iconSize="16"></dees-icon>
|
|
480
|
+
</button>
|
|
481
|
+
</div>
|
|
482
|
+
|
|
483
|
+
<div class="weekdays">
|
|
484
|
+
${weekDays.map(day => html`<div class="weekday">${day}</div>`)}
|
|
485
|
+
</div>
|
|
486
|
+
|
|
487
|
+
<div class="days-grid">
|
|
488
|
+
${days.map(day => {
|
|
489
|
+
const isToday = this.isToday(day);
|
|
490
|
+
const isSelected = this.isSelected(day);
|
|
491
|
+
const isOtherMonth = day.getMonth() !== this.viewDate.getMonth();
|
|
492
|
+
const isDayDisabled = this.isDayDisabled(day);
|
|
493
|
+
const dayEvents = this.getEventsForDate(day);
|
|
494
|
+
const hasEvents = dayEvents.length > 0;
|
|
495
|
+
const totalEventCount = dayEvents.reduce((sum, event) => sum + (event.count || 1), 0);
|
|
496
|
+
|
|
497
|
+
return html`
|
|
498
|
+
<div
|
|
499
|
+
class="day ${isOtherMonth ? 'other-month' : ''} ${isToday ? 'today' : ''} ${isSelected ? 'selected' : ''} ${isDayDisabled ? 'disabled' : ''} ${hasEvents ? 'has-event' : ''}"
|
|
500
|
+
@click=${() => !isDayDisabled && this.handleSelectDate(day)}
|
|
501
|
+
>
|
|
502
|
+
${day.getDate()}
|
|
503
|
+
${hasEvents ? html`
|
|
504
|
+
${totalEventCount > 3 ? html`
|
|
505
|
+
<div class="event-count">${totalEventCount}</div>
|
|
506
|
+
` : html`
|
|
507
|
+
<div class="event-indicator">
|
|
508
|
+
${dayEvents.slice(0, 3).map(event => html`
|
|
509
|
+
<div class="event-dot ${event.type || 'info'}"></div>
|
|
510
|
+
`)}
|
|
511
|
+
</div>
|
|
512
|
+
`}
|
|
513
|
+
${dayEvents[0].title ? html`
|
|
514
|
+
<div class="event-tooltip">
|
|
515
|
+
${dayEvents[0].title}
|
|
516
|
+
${totalEventCount > 1 ? html` (+${totalEventCount - 1} more)` : ''}
|
|
517
|
+
</div>
|
|
518
|
+
` : ''}
|
|
519
|
+
` : ''}
|
|
520
|
+
</div>
|
|
521
|
+
`;
|
|
522
|
+
})}
|
|
523
|
+
</div>
|
|
524
|
+
|
|
525
|
+
${this.enableTime ? html`
|
|
526
|
+
<div class="time-selector">
|
|
527
|
+
<div class="time-selector-title">Time</div>
|
|
528
|
+
<div class="time-inputs">
|
|
529
|
+
<input type="number" class="time-input"
|
|
530
|
+
.value=${this.timeFormat === '12h'
|
|
531
|
+
? (this.selectedHour === 0 ? 12 : this.selectedHour > 12 ? this.selectedHour - 12 : this.selectedHour).toString().padStart(2, '0')
|
|
532
|
+
: this.selectedHour.toString().padStart(2, '0')}
|
|
533
|
+
@input=${this.handleHourInput}
|
|
534
|
+
min="${this.timeFormat === '12h' ? 1 : 0}"
|
|
535
|
+
max="${this.timeFormat === '12h' ? 12 : 23}"
|
|
536
|
+
/>
|
|
537
|
+
<span class="time-separator">:</span>
|
|
538
|
+
<input type="number" class="time-input"
|
|
539
|
+
.value=${this.selectedMinute.toString().padStart(2, '0')}
|
|
540
|
+
@input=${this.handleMinuteInput}
|
|
541
|
+
min="0" max="59" step="${this.minuteIncrement || 1}"
|
|
542
|
+
/>
|
|
543
|
+
${this.timeFormat === '12h' ? html`
|
|
544
|
+
<div class="am-pm-selector">
|
|
545
|
+
<button class="am-pm-button ${isAM ? 'selected' : ''}" @click=${() => this.setAMPM('am')}>AM</button>
|
|
546
|
+
<button class="am-pm-button ${!isAM ? 'selected' : ''}" @click=${() => this.setAMPM('pm')}>PM</button>
|
|
547
|
+
</div>
|
|
548
|
+
` : ''}
|
|
549
|
+
</div>
|
|
550
|
+
</div>
|
|
551
|
+
` : ''}
|
|
552
|
+
|
|
553
|
+
${this.enableTimezone ? html`
|
|
554
|
+
<div class="timezone-selector">
|
|
555
|
+
<div class="timezone-selector-title">Timezone</div>
|
|
556
|
+
<select class="timezone-select" .value=${this.timezone} @change=${this.handleTimezoneChange}>
|
|
557
|
+
${DeesInputDatepickerPopup.TIMEZONES.map(tz => html`
|
|
558
|
+
<option value="${tz.value}" ?selected=${tz.value === this.timezone}>${tz.label}</option>
|
|
559
|
+
`)}
|
|
560
|
+
</select>
|
|
561
|
+
</div>
|
|
562
|
+
` : ''}
|
|
563
|
+
|
|
564
|
+
<div class="calendar-actions">
|
|
565
|
+
<button class="action-button today-button" @click=${this.handleSelectToday}>Today</button>
|
|
566
|
+
<button class="action-button clear-action-button" @click=${this.handleClear}>Clear</button>
|
|
567
|
+
</div>
|
|
568
|
+
</div>
|
|
569
|
+
`;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
private computePositionStyle(): string {
|
|
573
|
+
const rect = this.triggerRect!;
|
|
574
|
+
const left = rect.left;
|
|
575
|
+
|
|
576
|
+
if (this.opensToTop) {
|
|
577
|
+
const bottom = window.innerHeight - rect.top + 4;
|
|
578
|
+
return `left: ${left}px; bottom: ${bottom}px; top: auto`;
|
|
579
|
+
} else {
|
|
580
|
+
const top = rect.bottom + 4;
|
|
581
|
+
return `left: ${left}px; top: ${top}px`;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// Calendar logic
|
|
586
|
+
private getDaysInMonth(): Date[] {
|
|
587
|
+
const year = this.viewDate.getFullYear();
|
|
588
|
+
const month = this.viewDate.getMonth();
|
|
589
|
+
const firstDay = new Date(year, month, 1);
|
|
590
|
+
const lastDay = new Date(year, month + 1, 0);
|
|
591
|
+
const days: Date[] = [];
|
|
592
|
+
|
|
593
|
+
const startOffset = this.weekStartsOn === 1
|
|
594
|
+
? (firstDay.getDay() === 0 ? 6 : firstDay.getDay() - 1)
|
|
595
|
+
: firstDay.getDay();
|
|
596
|
+
|
|
597
|
+
for (let i = startOffset; i > 0; i--) days.push(new Date(year, month, 1 - i));
|
|
598
|
+
for (let i = 1; i <= lastDay.getDate(); i++) days.push(new Date(year, month, i));
|
|
599
|
+
const remaining = 42 - days.length;
|
|
600
|
+
for (let i = 1; i <= remaining; i++) days.push(new Date(year, month + 1, i));
|
|
601
|
+
|
|
602
|
+
return days;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
private isToday(date: Date): boolean {
|
|
606
|
+
const today = new Date();
|
|
607
|
+
return date.getDate() === today.getDate() && date.getMonth() === today.getMonth() && date.getFullYear() === today.getFullYear();
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
private isSelected(date: Date): boolean {
|
|
611
|
+
if (!this.selectedDate) return false;
|
|
612
|
+
return date.getDate() === this.selectedDate.getDate() && date.getMonth() === this.selectedDate.getMonth() && date.getFullYear() === this.selectedDate.getFullYear();
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
private isDayDisabled(date: Date): boolean {
|
|
616
|
+
if (this.minDate) { const min = new Date(this.minDate); if (date < min) return true; }
|
|
617
|
+
if (this.maxDate) { const max = new Date(this.maxDate); if (date > max) return true; }
|
|
618
|
+
if (this.disabledDates?.length) {
|
|
619
|
+
return this.disabledDates.some(ds => {
|
|
620
|
+
try { const d = new Date(ds); return date.getDate() === d.getDate() && date.getMonth() === d.getMonth() && date.getFullYear() === d.getFullYear(); }
|
|
621
|
+
catch { return false; }
|
|
622
|
+
});
|
|
623
|
+
}
|
|
624
|
+
return false;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
private getEventsForDate(date: Date): IDateEvent[] {
|
|
628
|
+
if (!this.events?.length) return [];
|
|
629
|
+
const dateStr = `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`;
|
|
630
|
+
return this.events.filter(e => e.date === dateStr);
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
private previousMonth(): void {
|
|
634
|
+
this.viewDate = new Date(this.viewDate.getFullYear(), this.viewDate.getMonth() - 1, 1);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
private nextMonth(): void {
|
|
638
|
+
this.viewDate = new Date(this.viewDate.getFullYear(), this.viewDate.getMonth() + 1, 1);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
// Event dispatching
|
|
642
|
+
private handleSelectDate(day: Date): void {
|
|
643
|
+
this.selectedDate = new Date(day.getFullYear(), day.getMonth(), day.getDate(), this.selectedHour, this.selectedMinute);
|
|
644
|
+
this.dispatchEvent(new CustomEvent('date-selected', { detail: this.selectedDate }));
|
|
645
|
+
if (!this.enableTime) {
|
|
646
|
+
this.dispatchEvent(new CustomEvent('close-request'));
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
private handleSelectToday(): void {
|
|
651
|
+
const today = new Date();
|
|
652
|
+
this.selectedDate = today;
|
|
653
|
+
this.viewDate = new Date(today);
|
|
654
|
+
this.selectedHour = today.getHours();
|
|
655
|
+
this.selectedMinute = today.getMinutes();
|
|
656
|
+
this.dispatchEvent(new CustomEvent('date-selected', { detail: this.selectedDate }));
|
|
657
|
+
if (!this.enableTime) {
|
|
658
|
+
this.dispatchEvent(new CustomEvent('close-request'));
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
private handleClear(): void {
|
|
663
|
+
this.dispatchEvent(new CustomEvent('date-cleared'));
|
|
664
|
+
this.dispatchEvent(new CustomEvent('close-request'));
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
private handleHourInput = (e: InputEvent): void => {
|
|
668
|
+
const input = e.target as HTMLInputElement;
|
|
669
|
+
let value = parseInt(input.value) || 0;
|
|
670
|
+
if (this.timeFormat === '12h') {
|
|
671
|
+
value = Math.max(1, Math.min(12, value));
|
|
672
|
+
if (this.selectedHour >= 12 && value !== 12) this.selectedHour = value + 12;
|
|
673
|
+
else if (this.selectedHour < 12 && value === 12) this.selectedHour = 0;
|
|
674
|
+
else this.selectedHour = value;
|
|
675
|
+
} else {
|
|
676
|
+
this.selectedHour = Math.max(0, Math.min(23, value));
|
|
677
|
+
}
|
|
678
|
+
this.emitTimeUpdate();
|
|
679
|
+
};
|
|
680
|
+
|
|
681
|
+
private handleMinuteInput = (e: InputEvent): void => {
|
|
682
|
+
const input = e.target as HTMLInputElement;
|
|
683
|
+
let value = parseInt(input.value) || 0;
|
|
684
|
+
value = Math.max(0, Math.min(59, value));
|
|
685
|
+
if (this.minuteIncrement > 1) value = Math.round(value / this.minuteIncrement) * this.minuteIncrement;
|
|
686
|
+
this.selectedMinute = value;
|
|
687
|
+
this.emitTimeUpdate();
|
|
688
|
+
};
|
|
689
|
+
|
|
690
|
+
private setAMPM(period: 'am' | 'pm'): void {
|
|
691
|
+
if (period === 'am' && this.selectedHour >= 12) this.selectedHour -= 12;
|
|
692
|
+
else if (period === 'pm' && this.selectedHour < 12) this.selectedHour += 12;
|
|
693
|
+
this.emitTimeUpdate();
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
private handleTimezoneChange = (e: Event): void => {
|
|
697
|
+
this.timezone = (e.target as HTMLSelectElement).value;
|
|
698
|
+
this.emitTimeUpdate();
|
|
699
|
+
};
|
|
700
|
+
|
|
701
|
+
private emitTimeUpdate(): void {
|
|
702
|
+
if (this.selectedDate) {
|
|
703
|
+
this.selectedDate = new Date(this.selectedDate.getFullYear(), this.selectedDate.getMonth(), this.selectedDate.getDate(), this.selectedHour, this.selectedMinute);
|
|
704
|
+
this.dispatchEvent(new CustomEvent('date-selected', { detail: this.selectedDate }));
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
// Show/hide lifecycle
|
|
709
|
+
public async show(): Promise<void> {
|
|
710
|
+
this.windowLayer = await DeesWindowLayer.createAndShow();
|
|
711
|
+
this.windowLayer.addEventListener('click', () => {
|
|
712
|
+
this.dispatchEvent(new CustomEvent('close-request'));
|
|
713
|
+
});
|
|
714
|
+
|
|
715
|
+
this.menuZIndex = zIndexRegistry.getNextZIndex();
|
|
716
|
+
zIndexRegistry.register(this, this.menuZIndex);
|
|
717
|
+
this.style.zIndex = this.menuZIndex.toString();
|
|
718
|
+
|
|
719
|
+
document.body.appendChild(this);
|
|
720
|
+
|
|
721
|
+
await domtools.plugins.smartdelay.delayFor(0);
|
|
722
|
+
this.visible = true;
|
|
723
|
+
|
|
724
|
+
window.addEventListener('scroll', this.handleScrollOrResize, { capture: true, passive: true });
|
|
725
|
+
window.addEventListener('resize', this.handleScrollOrResize, { passive: true });
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
public async hide(): Promise<void> {
|
|
729
|
+
if (this.isDestroying) return;
|
|
730
|
+
this.isDestroying = true;
|
|
731
|
+
|
|
732
|
+
window.removeEventListener('scroll', this.handleScrollOrResize, { capture: true } as EventListenerOptions);
|
|
733
|
+
window.removeEventListener('resize', this.handleScrollOrResize);
|
|
734
|
+
zIndexRegistry.unregister(this);
|
|
735
|
+
|
|
736
|
+
if (this.windowLayer) {
|
|
737
|
+
this.windowLayer.destroy();
|
|
738
|
+
this.windowLayer = null;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
this.visible = false;
|
|
742
|
+
await domtools.plugins.smartdelay.delayFor(150);
|
|
743
|
+
|
|
744
|
+
if (this.parentElement) this.parentElement.removeChild(this);
|
|
745
|
+
this.isDestroying = false;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
private handleScrollOrResize = (): void => {
|
|
749
|
+
this.dispatchEvent(new CustomEvent('reposition-request'));
|
|
750
|
+
};
|
|
751
|
+
|
|
752
|
+
async disconnectedCallback() {
|
|
753
|
+
await super.disconnectedCallback();
|
|
754
|
+
window.removeEventListener('scroll', this.handleScrollOrResize, { capture: true } as EventListenerOptions);
|
|
755
|
+
window.removeEventListener('resize', this.handleScrollOrResize);
|
|
756
|
+
zIndexRegistry.unregister(this);
|
|
757
|
+
}
|
|
758
|
+
}
|