tyrell-components 1.0.0-TC51 → 1.0.0-TC53

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/css/tyrell-theme.css +25 -2
  2. package/css/tyrell.css +4 -0
  3. package/dist/tyrell-colors-static.css +1 -316
  4. package/dist/tyrell-theme.css +1 -1443
  5. package/dist/tyrell.css +1 -1390
  6. package/dist/tyrell.js +1 -1
  7. package/lib/base/ty-component.js +196 -196
  8. package/lib/components/button.d.ts +1 -1
  9. package/lib/components/button.js +260 -259
  10. package/lib/components/calendar-month.d.ts +2 -2
  11. package/lib/components/calendar-month.js +426 -416
  12. package/lib/components/calendar-navigation.js +238 -238
  13. package/lib/components/calendar.js +605 -602
  14. package/lib/components/checkbox.d.ts +1 -1
  15. package/lib/components/checkbox.js +273 -272
  16. package/lib/components/copy.d.ts +1 -1
  17. package/lib/components/copy.js +279 -278
  18. package/lib/components/date-picker.d.ts +1 -1
  19. package/lib/components/date-picker.js +1103 -1108
  20. package/lib/components/file-upload.js +404 -404
  21. package/lib/components/icon.js +141 -141
  22. package/lib/components/input.d.ts +1 -1
  23. package/lib/components/input.js +593 -596
  24. package/lib/components/modal.js +319 -319
  25. package/lib/components/option.js +167 -167
  26. package/lib/components/popup.js +233 -233
  27. package/lib/components/radio.d.ts +2 -2
  28. package/lib/components/radio.js +407 -406
  29. package/lib/components/resize-observer.js +67 -67
  30. package/lib/components/scroll-container.js +294 -294
  31. package/lib/components/select.d.ts +1 -1
  32. package/lib/components/select.js +1524 -1532
  33. package/lib/components/selected-tags.js +184 -184
  34. package/lib/components/step.js +62 -62
  35. package/lib/components/switch.d.ts +1 -1
  36. package/lib/components/switch.js +214 -213
  37. package/lib/components/tab.js +62 -62
  38. package/lib/components/tabs.d.ts +1 -0
  39. package/lib/components/tabs.js +507 -479
  40. package/lib/components/tag.d.ts +1 -1
  41. package/lib/components/tag.js +236 -235
  42. package/lib/components/textarea.d.ts +1 -1
  43. package/lib/components/textarea.js +457 -456
  44. package/lib/components/tooltip.js +283 -283
  45. package/lib/components/wizard.js +384 -384
  46. package/lib/icons/heroicons/micro.js +392 -392
  47. package/lib/icons/heroicons/mini.js +382 -382
  48. package/lib/icons/heroicons/outline.js +337 -337
  49. package/lib/icons/heroicons/solid.js +407 -407
  50. package/lib/icons/lucide.js +22843 -22843
  51. package/lib/icons/material/filled.js +9653 -9653
  52. package/lib/icons/material/outlined.js +10271 -10271
  53. package/lib/icons/material/round.js +10495 -10495
  54. package/lib/icons/material/sharp.js +9905 -9905
  55. package/lib/icons/material/two-tone.js +12792 -12792
  56. package/lib/index.js +21 -21
  57. package/lib/styles/button.js +176 -288
  58. package/lib/styles/calendar-month.js +123 -183
  59. package/lib/styles/calendar-navigation.js +60 -79
  60. package/lib/styles/calendar.js +8 -9
  61. package/lib/styles/checkbox.js +45 -72
  62. package/lib/styles/copy.js +67 -91
  63. package/lib/styles/custom-scrollbar.js +80 -100
  64. package/lib/styles/date-picker.js +261 -331
  65. package/lib/styles/field-size.d.ts +6 -0
  66. package/lib/styles/field-size.js +91 -0
  67. package/lib/styles/file-upload.js +139 -169
  68. package/lib/styles/icon.js +92 -156
  69. package/lib/styles/input.js +125 -241
  70. package/lib/styles/modal.js +67 -102
  71. package/lib/styles/option.d.ts +1 -1
  72. package/lib/styles/option.js +47 -67
  73. package/lib/styles/popup.js +34 -65
  74. package/lib/styles/radio.js +69 -96
  75. package/lib/styles/resize-observer.js +2 -5
  76. package/lib/styles/scroll-container.js +100 -130
  77. package/lib/styles/select-base.js +525 -778
  78. package/lib/styles/select.js +140 -228
  79. package/lib/styles/step.js +22 -29
  80. package/lib/styles/switch.js +34 -55
  81. package/lib/styles/tab.d.ts +1 -1
  82. package/lib/styles/tab.js +22 -29
  83. package/lib/styles/tabs.d.ts +1 -1
  84. package/lib/styles/tabs.js +175 -237
  85. package/lib/styles/tag.js +135 -188
  86. package/lib/styles/textarea.d.ts +1 -1
  87. package/lib/styles/textarea.js +205 -271
  88. package/lib/styles/tooltip.js +3 -3
  89. package/lib/styles/wizard.js +170 -226
  90. package/lib/types/common.d.ts +2 -0
  91. package/lib/types/common.js +6 -5
  92. package/lib/utils/calendar-utils.js +100 -100
  93. package/lib/utils/custom-scrollbar.js +272 -272
  94. package/lib/utils/flavor-sheet.js +14 -14
  95. package/lib/utils/icon-registry.js +127 -127
  96. package/lib/utils/loader-registry.js +3 -3
  97. package/lib/utils/locale.js +34 -34
  98. package/lib/utils/mobile.js +1 -1
  99. package/lib/utils/number-format.js +54 -54
  100. package/lib/utils/positioning.js +212 -212
  101. package/lib/utils/property-capture.js +36 -36
  102. package/lib/utils/property-manager.js +125 -125
  103. package/lib/utils/resize-observer.js +23 -23
  104. package/lib/utils/scroll-lock.js +57 -57
  105. package/lib/utils/styles.js +17 -17
  106. package/lib/version.d.ts +1 -1
  107. package/lib/version.js +1 -1
  108. package/package.json +4 -4
@@ -4,432 +4,442 @@ import { calendarMonthStyles, calendarMonthCustomFlavorCss } from '../styles/cal
4
4
  import { getCalendarMonthDays, getLocalizedWeekdays, parseISODate } from '../utils/calendar-utils.js';
5
5
  import { getEffectiveLocale, observeLocaleChanges } from '../utils/locale.js';
6
6
  function defaultDayContent(dayContext) {
7
- return dayContext.dayInMonth.toString();
7
+ return dayContext.dayInMonth.toString();
8
8
  }
9
9
  function getDefaultDayClasses(dayContext) {
10
- const classes = ['calendar-day'];
11
- if (dayContext.today)
12
- classes.push('today');
13
- if (dayContext.weekend)
14
- classes.push('weekend');
15
- if (dayContext.otherMonth)
16
- classes.push('other-month');
17
- if (dayContext.isSelected)
18
- classes.push('selected');
19
- return classes;
10
+ const classes = ['calendar-day'];
11
+ if (dayContext.today)
12
+ classes.push('today');
13
+ if (dayContext.weekend)
14
+ classes.push('weekend');
15
+ if (dayContext.otherMonth)
16
+ classes.push('other-month');
17
+ if (dayContext.isSelected)
18
+ classes.push('selected');
19
+ return classes;
20
20
  }
21
21
  function isDOMElement(value) {
22
- return value instanceof HTMLElement;
22
+ return value instanceof HTMLElement;
23
23
  }
24
24
  function isoToUTCTimestamp(iso) {
25
- if (!iso)
26
- return null;
27
- const parsed = parseISODate(iso);
28
- if (!parsed)
29
- return null;
30
- return Date.UTC(parsed.year, parsed.month - 1, parsed.day);
25
+ if (!iso)
26
+ return null;
27
+ const parsed = parseISODate(iso);
28
+ if (!parsed)
29
+ return null;
30
+ return Date.UTC(parsed.year, parsed.month - 1, parsed.day);
31
31
  }
32
32
  function normalizeSizeValue(value) {
33
- if (!value || value === '')
34
- return null;
35
- const strValue = String(value);
36
- if (/^\d+(\.\d+)?$/.test(strValue)) {
37
- return `${strValue}px`;
38
- }
39
- return strValue;
33
+ if (!value || value === '')
34
+ return null;
35
+ const strValue = String(value);
36
+ if (/^\d+(\.\d+)?$/.test(strValue)) {
37
+ return `${strValue}px`;
38
+ }
39
+ return strValue;
40
40
  }
41
41
  export class TyCalendarMonth extends HTMLElement {
42
- static get observedAttributes() {
43
- return ['locale', 'size', 'min', 'max', 'flavor'];
44
- }
45
- constructor() {
46
- super();
47
- this._locale = 'en-US';
48
- this._size = 'md';
49
- this._customFlavorSheet = null;
50
- this._focusedIndex = null;
51
- this._cellElements = [];
52
- const today = new Date();
53
- this._displayYear = today.getFullYear();
54
- this._displayMonth = today.getMonth() + 1;
55
- this.attachShadow({ mode: 'open' });
56
- }
57
- connectedCallback() {
58
- if (!this._displayMonth) {
59
- this._displayMonth = new Date().getMonth() + 1;
60
- }
61
- if (!this._displayYear) {
62
- this._displayYear = new Date().getFullYear();
63
- }
64
- if (!this._locale) {
65
- this._locale = this.getAttribute('locale') || 'en-US';
66
- }
67
- const sizeAttr = this.getAttribute('size');
68
- if (sizeAttr && (sizeAttr === 'sm' || sizeAttr === 'md' || sizeAttr === 'lg')) {
69
- this._size = sizeAttr;
70
- }
71
- const plainDayContentFn = this.dayContentFn;
72
- if (plainDayContentFn && !this._dayContentFn) {
73
- this._dayContentFn = plainDayContentFn;
74
- delete this.dayContentFn;
75
- }
76
- const plainCustomCSS = this.customCSS;
77
- if (plainCustomCSS && !this._customCSS) {
78
- this._customCSS = plainCustomCSS;
79
- delete this.customCSS;
80
- }
81
- this._localeObserver = observeLocaleChanges(this, () => {
82
- this.render();
83
- });
84
- this._syncCustomFlavor();
85
- this.render();
86
- }
87
- disconnectedCallback() {
88
- if (this._localeObserver) {
89
- this._localeObserver();
90
- this._localeObserver = undefined;
91
- }
92
- }
93
- attributeChangedCallback(name, _oldValue, newValue) {
94
- if (name === 'locale') {
95
- this._locale = newValue || 'en-US';
96
- this.render();
97
- }
98
- else if (name === 'size') {
99
- if (newValue === 'sm' || newValue === 'md' || newValue === 'lg') {
100
- this._size = newValue;
101
- this.render();
102
- }
103
- }
104
- else if (name === 'min' || name === 'max') {
105
- this[name === 'min' ? '_min' : '_max'] = newValue ?? undefined;
106
- this.render();
107
- }
108
- else if (name === 'flavor') {
109
- this._syncCustomFlavor();
110
- }
111
- }
112
- _syncCustomFlavor() {
113
- this._customFlavorSheet = syncCustomFlavorSheet(this.shadowRoot, this._customFlavorSheet, this.getAttribute('flavor'), ({ base }) => calendarMonthCustomFlavorCss(base));
114
- }
115
- get displayYear() {
116
- return this._displayYear;
117
- }
118
- set displayYear(value) {
119
- if (this._displayYear !== value) {
120
- this._displayYear = value;
121
- this._focusedIndex = null;
122
- this.render();
123
- }
124
- }
125
- get displayMonth() {
126
- return this._displayMonth;
127
- }
128
- set displayMonth(value) {
129
- if (this._displayMonth !== value) {
130
- this._displayMonth = value;
131
- this._focusedIndex = null;
132
- this.render();
133
- }
134
- }
135
- get locale() {
136
- return getEffectiveLocale(this, this.getAttribute('locale'));
137
- }
138
- set locale(value) {
139
- if (this._locale !== value) {
140
- this._locale = value;
141
- this.render();
142
- }
143
- }
144
- get size() {
145
- return this._size;
146
- }
147
- set size(value) {
148
- if (this._size !== value) {
149
- this._size = value;
150
- this.render();
151
- }
152
- }
153
- get flavor() {
154
- return this.getAttribute('flavor') || 'primary';
155
- }
156
- set flavor(value) {
157
- if (value)
158
- this.setAttribute('flavor', value);
159
- else
160
- this.removeAttribute('flavor');
161
- }
162
- get width() {
163
- return this._width;
164
- }
165
- set width(value) {
166
- if (this._width !== value) {
167
- this._width = value;
168
- this.applyWidthProperties();
169
- }
170
- }
171
- get minWidth() {
172
- return this._minWidth;
173
- }
174
- set minWidth(value) {
175
- if (this._minWidth !== value) {
176
- this._minWidth = value;
177
- this.applyWidthProperties();
178
- }
179
- }
180
- get maxWidth() {
181
- return this._maxWidth;
182
- }
183
- set maxWidth(value) {
184
- if (this._maxWidth !== value) {
185
- this._maxWidth = value;
186
- this.applyWidthProperties();
187
- }
188
- }
189
- get dayContentFn() {
190
- return this._dayContentFn;
191
- }
192
- set dayContentFn(fn) {
193
- if (this._dayContentFn !== fn) {
194
- this._dayContentFn = fn;
195
- this.render();
196
- }
197
- }
198
- get customCSS() {
199
- return this._customCSS;
200
- }
201
- set customCSS(sheet) {
202
- if (this._customCSS !== sheet) {
203
- this._customCSS = sheet;
204
- this.applyCustomCSS();
205
- }
206
- }
207
- get min() {
208
- return this._min;
209
- }
210
- set min(value) {
211
- if (this._min !== value) {
212
- this._min = value;
213
- this.render();
214
- }
215
- }
216
- get max() {
217
- return this._max;
218
- }
219
- set max(value) {
220
- if (this._max !== value) {
221
- this._max = value;
222
- this.render();
223
- }
224
- }
225
- get value() {
226
- return this._value;
227
- }
228
- set value(timestamp) {
229
- const newValue = timestamp ?? undefined;
230
- if (this._value !== newValue) {
231
- this._value = newValue;
232
- this.render();
233
- }
234
- }
235
- applyWidthProperties() {
236
- const normalizedWidth = normalizeSizeValue(this._width);
237
- const normalizedMinWidth = normalizeSizeValue(this._minWidth);
238
- const normalizedMaxWidth = normalizeSizeValue(this._maxWidth);
239
- if (normalizedWidth) {
240
- this.style.setProperty('--calendar-width', normalizedWidth);
241
- }
242
- else {
243
- this.style.removeProperty('--calendar-width');
244
- }
245
- if (normalizedMinWidth) {
246
- this.style.setProperty('--calendar-min-width', normalizedMinWidth);
247
- }
248
- else {
249
- this.style.removeProperty('--calendar-min-width');
250
- }
251
- if (normalizedMaxWidth) {
252
- this.style.setProperty('--calendar-max-width', normalizedMaxWidth);
253
- }
254
- else {
255
- this.style.removeProperty('--calendar-max-width');
256
- }
257
- }
258
- applyCustomCSS() {
259
- if (!this._customCSS || !this.shadowRoot)
260
- return;
261
- const existing = this.shadowRoot.adoptedStyleSheets;
262
- if (!existing.includes(this._customCSS)) {
263
- this.shadowRoot.adoptedStyleSheets = [...existing, this._customCSS];
264
- }
265
- }
266
- refresh() {
267
- this.render();
268
- }
269
- dispatchDayClick(dayContext, _domEvent) {
270
- const detail = {
271
- dayContext,
272
- value: dayContext.value,
273
- year: dayContext.year,
274
- month: dayContext.month,
275
- day: dayContext.dayInMonth,
276
- isHoliday: dayContext.holiday,
277
- isToday: dayContext.today,
278
- isWeekend: dayContext.weekend,
279
- isOtherMonth: dayContext.otherMonth,
280
- };
281
- for (const eventType of ['day-click', 'day-select']) {
282
- const event = new CustomEvent(eventType, {
283
- detail,
284
- bubbles: true,
285
- cancelable: true,
286
- });
287
- this.dispatchEvent(event);
288
- }
289
- }
290
- renderDayCell(dayContext, disabled, isRoving) {
291
- const dayElement = document.createElement('div');
292
- const content = this._dayContentFn
293
- ? this._dayContentFn(dayContext)
294
- : defaultDayContent(dayContext);
295
- if (this._dayContentFn && content && !isDOMElement(content) && typeof content !== 'string') {
296
- throw new Error(`Custom dayContentFn must return a DOM element or string, got: ${typeof content}. ` +
297
- `Use document.createElement() to create DOM elements.`);
298
- }
299
- const classes = getDefaultDayClasses(dayContext);
300
- if (disabled)
301
- classes.push('disabled');
302
- dayElement.className = classes.join(' ');
303
- if (typeof content === 'string') {
304
- dayElement.textContent = content;
305
- }
306
- else if (isDOMElement(content)) {
307
- dayElement.appendChild(content);
308
- }
309
- dayElement.setAttribute('role', 'gridcell');
310
- dayElement.setAttribute('aria-label', new Intl.DateTimeFormat(this._locale, { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })
311
- .format(new Date(dayContext.localValue)));
312
- dayElement.setAttribute('aria-selected', String(!!dayContext.isSelected));
313
- if (dayContext.today)
314
- dayElement.setAttribute('aria-current', 'date');
315
- if (disabled) {
316
- dayElement.setAttribute('aria-disabled', 'true');
317
- dayElement.setAttribute('tabindex', '-1');
318
- }
319
- else {
320
- dayElement.setAttribute('tabindex', isRoving ? '0' : '-1');
321
- dayElement.addEventListener('pointerdown', (event) => {
322
- event.preventDefault();
323
- this.dispatchDayClick(dayContext, event);
324
- });
325
- dayElement.addEventListener('keydown', (event) => {
326
- const ke = event;
327
- if (ke.key === 'Enter' || ke.key === ' ') {
328
- ke.preventDefault();
329
- this.dispatchDayClick(dayContext, event);
330
- }
331
- });
332
- }
333
- return dayElement;
334
- }
335
- render() {
336
- const root = this.shadowRoot;
337
- if (!root)
338
- return;
339
- ensureStyles(root, { css: calendarMonthStyles, id: 'ty-calendar-month' });
340
- this.applyCustomCSS();
341
- this.applyWidthProperties();
342
- let selection;
343
- if (this._value) {
344
- const date = new Date(this._value);
345
- selection = {
346
- year: date.getFullYear(),
347
- month: date.getMonth() + 1,
348
- day: date.getDate()
349
- };
350
- }
351
- const days = getCalendarMonthDays(this._displayYear, this._displayMonth, selection);
352
- const weekdays = getLocalizedWeekdays(this._locale, 'short');
353
- root.innerHTML = '';
354
- const calendarContainer = document.createElement('div');
355
- calendarContainer.className = `calendar-flex-container calendar-size-${this._size}`;
356
- calendarContainer.setAttribute('role', 'grid');
357
- calendarContainer.setAttribute('aria-label', new Intl.DateTimeFormat(this._locale, { month: 'long', year: 'numeric' })
358
- .format(new Date(this._displayYear, this._displayMonth - 1, 1)));
359
- const headerRow = document.createElement('div');
360
- headerRow.className = 'calendar-row calendar-header-row';
361
- headerRow.setAttribute('role', 'row');
362
- weekdays.forEach(weekday => {
363
- const headerCell = document.createElement('div');
364
- headerCell.className = 'calendar-cell calendar-header-cell';
365
- headerCell.setAttribute('role', 'columnheader');
366
- headerCell.textContent = weekday;
367
- headerRow.appendChild(headerCell);
368
- });
369
- calendarContainer.appendChild(headerRow);
370
- const dayWeeks = [];
371
- for (let i = 0; i < 42; i += 7) {
372
- dayWeeks.push(days.slice(i, i + 7));
373
- }
374
- const minTs = isoToUTCTimestamp(this._min);
375
- const maxTs = isoToUTCTimestamp(this._max);
376
- const disabledAt = (i) => {
377
- const v = days[i].value;
378
- return (minTs !== null && v < minTs) || (maxTs !== null && v > maxTs);
379
- };
380
- let defaultIndex = days.findIndex(d => d.isSelected);
381
- if (defaultIndex === -1)
382
- defaultIndex = days.findIndex(d => d.today);
383
- if (defaultIndex === -1)
384
- defaultIndex = 0;
385
- if (this._focusedIndex === null ||
386
- this._focusedIndex < 0 ||
387
- this._focusedIndex > 41 ||
388
- disabledAt(this._focusedIndex)) {
389
- this._focusedIndex = defaultIndex;
390
- }
391
- this._cellElements = [];
392
- let flatIndex = 0;
393
- dayWeeks.forEach(week => {
394
- const dayRow = document.createElement('div');
395
- dayRow.className = 'calendar-row calendar-day-row';
396
- dayRow.setAttribute('role', 'row');
397
- week.forEach(dayContext => {
398
- const disabled = (minTs !== null && dayContext.value < minTs) ||
399
- (maxTs !== null && dayContext.value > maxTs);
400
- const dayCell = this.renderDayCell(dayContext, disabled, flatIndex === this._focusedIndex);
401
- dayCell.className = `${dayCell.className} calendar-cell calendar-day-cell`;
402
- dayRow.appendChild(dayCell);
403
- this._cellElements[flatIndex] = dayCell;
404
- flatIndex++;
405
- });
406
- calendarContainer.appendChild(dayRow);
407
- });
408
- calendarContainer.addEventListener('keydown', (event) => {
409
- const ke = event;
410
- const delta = ke.key === 'ArrowLeft' ? -1 :
411
- ke.key === 'ArrowRight' ? 1 :
412
- ke.key === 'ArrowUp' ? -7 :
413
- ke.key === 'ArrowDown' ? 7 :
414
- 0;
415
- if (delta === 0)
416
- return;
417
- const currentIndex = this._focusedIndex ?? defaultIndex;
418
- const nextIndex = currentIndex + delta;
419
- if (nextIndex < 0 || nextIndex > 41 || disabledAt(nextIndex))
420
- return;
421
- ke.preventDefault();
422
- this._cellElements[currentIndex]?.setAttribute('tabindex', '-1');
423
- this._focusedIndex = nextIndex;
424
- const nextCell = this._cellElements[nextIndex];
425
- if (nextCell) {
426
- nextCell.setAttribute('tabindex', '0');
427
- nextCell.focus();
428
- }
429
- });
430
- root.appendChild(calendarContainer);
431
- }
42
+ static get observedAttributes() {
43
+ return ['display-year', 'display-month', 'locale', 'size', 'min', 'max', 'flavor'];
44
+ }
45
+ constructor() {
46
+ super();
47
+ this._locale = 'en-US';
48
+ this._size = 'md';
49
+ this._customFlavorSheet = null;
50
+ this._focusedIndex = null;
51
+ this._cellElements = [];
52
+ const today = new Date();
53
+ this._displayYear = today.getFullYear();
54
+ this._displayMonth = today.getMonth() + 1;
55
+ this.attachShadow({ mode: 'open' });
56
+ }
57
+ connectedCallback() {
58
+ if (!this._displayMonth) {
59
+ this._displayMonth = new Date().getMonth() + 1;
60
+ }
61
+ if (!this._displayYear) {
62
+ this._displayYear = new Date().getFullYear();
63
+ }
64
+ if (!this._locale) {
65
+ this._locale = this.getAttribute('locale') || 'en-US';
66
+ }
67
+ const sizeAttr = this.getAttribute('size');
68
+ if (sizeAttr && (sizeAttr === 'sm' || sizeAttr === 'md' || sizeAttr === 'lg')) {
69
+ this._size = sizeAttr;
70
+ }
71
+ const plainDayContentFn = this.dayContentFn;
72
+ if (plainDayContentFn && !this._dayContentFn) {
73
+ this._dayContentFn = plainDayContentFn;
74
+ delete this.dayContentFn;
75
+ }
76
+ const plainCustomCSS = this.customCSS;
77
+ if (plainCustomCSS && !this._customCSS) {
78
+ this._customCSS = plainCustomCSS;
79
+ delete this.customCSS;
80
+ }
81
+ this._localeObserver = observeLocaleChanges(this, () => {
82
+ this.render();
83
+ });
84
+ this._syncCustomFlavor();
85
+ this.render();
86
+ }
87
+ disconnectedCallback() {
88
+ if (this._localeObserver) {
89
+ this._localeObserver();
90
+ this._localeObserver = undefined;
91
+ }
92
+ }
93
+ attributeChangedCallback(name, _oldValue, newValue) {
94
+ if (name === 'display-year' || name === 'display-month') {
95
+ const n = Number(newValue);
96
+ const valid = Number.isInteger(n) && (name === 'display-year' ? n > 0 : n >= 1 && n <= 12);
97
+ if (valid) {
98
+ if (name === 'display-year')
99
+ this.displayYear = n;
100
+ else
101
+ this.displayMonth = n;
102
+ }
103
+ }
104
+ else if (name === 'locale') {
105
+ this._locale = newValue || 'en-US';
106
+ this.render();
107
+ }
108
+ else if (name === 'size') {
109
+ if (newValue === 'sm' || newValue === 'md' || newValue === 'lg') {
110
+ this._size = newValue;
111
+ this.render();
112
+ }
113
+ }
114
+ else if (name === 'min' || name === 'max') {
115
+ this[name === 'min' ? '_min' : '_max'] = newValue ?? undefined;
116
+ this.render();
117
+ }
118
+ else if (name === 'flavor') {
119
+ this._syncCustomFlavor();
120
+ }
121
+ }
122
+ _syncCustomFlavor() {
123
+ this._customFlavorSheet = syncCustomFlavorSheet(this.shadowRoot, this._customFlavorSheet, this.getAttribute('flavor'), ({ base }) => calendarMonthCustomFlavorCss(base));
124
+ }
125
+ get displayYear() {
126
+ return this._displayYear;
127
+ }
128
+ set displayYear(value) {
129
+ if (this._displayYear !== value) {
130
+ this._displayYear = value;
131
+ this._focusedIndex = null;
132
+ this.render();
133
+ }
134
+ }
135
+ get displayMonth() {
136
+ return this._displayMonth;
137
+ }
138
+ set displayMonth(value) {
139
+ if (this._displayMonth !== value) {
140
+ this._displayMonth = value;
141
+ this._focusedIndex = null;
142
+ this.render();
143
+ }
144
+ }
145
+ get locale() {
146
+ return getEffectiveLocale(this, this.getAttribute('locale'));
147
+ }
148
+ set locale(value) {
149
+ if (this._locale !== value) {
150
+ this._locale = value;
151
+ this.render();
152
+ }
153
+ }
154
+ get size() {
155
+ return this._size;
156
+ }
157
+ set size(value) {
158
+ if (this._size !== value) {
159
+ this._size = value;
160
+ this.render();
161
+ }
162
+ }
163
+ get flavor() {
164
+ return this.getAttribute('flavor') || 'primary';
165
+ }
166
+ set flavor(value) {
167
+ if (value)
168
+ this.setAttribute('flavor', value);
169
+ else
170
+ this.removeAttribute('flavor');
171
+ }
172
+ get width() {
173
+ return this._width;
174
+ }
175
+ set width(value) {
176
+ if (this._width !== value) {
177
+ this._width = value;
178
+ this.applyWidthProperties();
179
+ }
180
+ }
181
+ get minWidth() {
182
+ return this._minWidth;
183
+ }
184
+ set minWidth(value) {
185
+ if (this._minWidth !== value) {
186
+ this._minWidth = value;
187
+ this.applyWidthProperties();
188
+ }
189
+ }
190
+ get maxWidth() {
191
+ return this._maxWidth;
192
+ }
193
+ set maxWidth(value) {
194
+ if (this._maxWidth !== value) {
195
+ this._maxWidth = value;
196
+ this.applyWidthProperties();
197
+ }
198
+ }
199
+ get dayContentFn() {
200
+ return this._dayContentFn;
201
+ }
202
+ set dayContentFn(fn) {
203
+ if (this._dayContentFn !== fn) {
204
+ this._dayContentFn = fn;
205
+ this.render();
206
+ }
207
+ }
208
+ get customCSS() {
209
+ return this._customCSS;
210
+ }
211
+ set customCSS(sheet) {
212
+ if (this._customCSS !== sheet) {
213
+ this._customCSS = sheet;
214
+ this.applyCustomCSS();
215
+ }
216
+ }
217
+ get min() {
218
+ return this._min;
219
+ }
220
+ set min(value) {
221
+ if (this._min !== value) {
222
+ this._min = value;
223
+ this.render();
224
+ }
225
+ }
226
+ get max() {
227
+ return this._max;
228
+ }
229
+ set max(value) {
230
+ if (this._max !== value) {
231
+ this._max = value;
232
+ this.render();
233
+ }
234
+ }
235
+ get value() {
236
+ return this._value;
237
+ }
238
+ set value(timestamp) {
239
+ const newValue = timestamp ?? undefined;
240
+ if (this._value !== newValue) {
241
+ this._value = newValue;
242
+ this.render();
243
+ }
244
+ }
245
+ applyWidthProperties() {
246
+ const normalizedWidth = normalizeSizeValue(this._width);
247
+ const normalizedMinWidth = normalizeSizeValue(this._minWidth);
248
+ const normalizedMaxWidth = normalizeSizeValue(this._maxWidth);
249
+ if (normalizedWidth) {
250
+ this.style.setProperty('--calendar-width', normalizedWidth);
251
+ }
252
+ else {
253
+ this.style.removeProperty('--calendar-width');
254
+ }
255
+ if (normalizedMinWidth) {
256
+ this.style.setProperty('--calendar-min-width', normalizedMinWidth);
257
+ }
258
+ else {
259
+ this.style.removeProperty('--calendar-min-width');
260
+ }
261
+ if (normalizedMaxWidth) {
262
+ this.style.setProperty('--calendar-max-width', normalizedMaxWidth);
263
+ }
264
+ else {
265
+ this.style.removeProperty('--calendar-max-width');
266
+ }
267
+ }
268
+ applyCustomCSS() {
269
+ if (!this._customCSS || !this.shadowRoot)
270
+ return;
271
+ const existing = this.shadowRoot.adoptedStyleSheets;
272
+ if (!existing.includes(this._customCSS)) {
273
+ this.shadowRoot.adoptedStyleSheets = [...existing, this._customCSS];
274
+ }
275
+ }
276
+ refresh() {
277
+ this.render();
278
+ }
279
+ dispatchDayClick(dayContext, _domEvent) {
280
+ const detail = {
281
+ dayContext,
282
+ value: dayContext.value,
283
+ year: dayContext.year,
284
+ month: dayContext.month,
285
+ day: dayContext.dayInMonth,
286
+ isHoliday: dayContext.holiday,
287
+ isToday: dayContext.today,
288
+ isWeekend: dayContext.weekend,
289
+ isOtherMonth: dayContext.otherMonth,
290
+ };
291
+ for (const eventType of ['day-click', 'day-select']) {
292
+ const event = new CustomEvent(eventType, {
293
+ detail,
294
+ bubbles: true,
295
+ cancelable: true,
296
+ });
297
+ this.dispatchEvent(event);
298
+ }
299
+ }
300
+ renderDayCell(dayContext, disabled, isRoving) {
301
+ const dayElement = document.createElement('div');
302
+ const content = this._dayContentFn
303
+ ? this._dayContentFn(dayContext)
304
+ : defaultDayContent(dayContext);
305
+ if (this._dayContentFn && content && !isDOMElement(content) && typeof content !== 'string') {
306
+ throw new Error(`Custom dayContentFn must return a DOM element or string, got: ${typeof content}. ` +
307
+ `Use document.createElement() to create DOM elements.`);
308
+ }
309
+ const classes = getDefaultDayClasses(dayContext);
310
+ if (disabled)
311
+ classes.push('disabled');
312
+ dayElement.className = classes.join(' ');
313
+ if (typeof content === 'string') {
314
+ dayElement.textContent = content;
315
+ }
316
+ else if (isDOMElement(content)) {
317
+ dayElement.appendChild(content);
318
+ }
319
+ dayElement.setAttribute('role', 'gridcell');
320
+ dayElement.setAttribute('aria-label', new Intl.DateTimeFormat(this._locale, { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })
321
+ .format(new Date(dayContext.localValue)));
322
+ dayElement.setAttribute('aria-selected', String(!!dayContext.isSelected));
323
+ if (dayContext.today)
324
+ dayElement.setAttribute('aria-current', 'date');
325
+ if (disabled) {
326
+ dayElement.setAttribute('aria-disabled', 'true');
327
+ dayElement.setAttribute('tabindex', '-1');
328
+ }
329
+ else {
330
+ dayElement.setAttribute('tabindex', isRoving ? '0' : '-1');
331
+ dayElement.addEventListener('pointerdown', (event) => {
332
+ event.preventDefault();
333
+ this.dispatchDayClick(dayContext, event);
334
+ });
335
+ dayElement.addEventListener('keydown', (event) => {
336
+ const ke = event;
337
+ if (ke.key === 'Enter' || ke.key === ' ') {
338
+ ke.preventDefault();
339
+ this.dispatchDayClick(dayContext, event);
340
+ }
341
+ });
342
+ }
343
+ return dayElement;
344
+ }
345
+ render() {
346
+ const root = this.shadowRoot;
347
+ if (!root)
348
+ return;
349
+ ensureStyles(root, { css: calendarMonthStyles, id: 'ty-calendar-month' });
350
+ this.applyCustomCSS();
351
+ this.applyWidthProperties();
352
+ let selection;
353
+ if (this._value) {
354
+ const date = new Date(this._value);
355
+ selection = {
356
+ year: date.getFullYear(),
357
+ month: date.getMonth() + 1,
358
+ day: date.getDate()
359
+ };
360
+ }
361
+ const days = getCalendarMonthDays(this._displayYear, this._displayMonth, selection);
362
+ const weekdays = getLocalizedWeekdays(this._locale, 'short');
363
+ root.innerHTML = '';
364
+ const calendarContainer = document.createElement('div');
365
+ calendarContainer.className = `calendar-flex-container calendar-size-${this._size}`;
366
+ calendarContainer.setAttribute('role', 'grid');
367
+ calendarContainer.setAttribute('aria-label', new Intl.DateTimeFormat(this._locale, { month: 'long', year: 'numeric' })
368
+ .format(new Date(this._displayYear, this._displayMonth - 1, 1)));
369
+ const headerRow = document.createElement('div');
370
+ headerRow.className = 'calendar-row calendar-header-row';
371
+ headerRow.setAttribute('role', 'row');
372
+ weekdays.forEach(weekday => {
373
+ const headerCell = document.createElement('div');
374
+ headerCell.className = 'calendar-cell calendar-header-cell';
375
+ headerCell.setAttribute('role', 'columnheader');
376
+ headerCell.textContent = weekday;
377
+ headerRow.appendChild(headerCell);
378
+ });
379
+ calendarContainer.appendChild(headerRow);
380
+ const dayWeeks = [];
381
+ for (let i = 0; i < 42; i += 7) {
382
+ dayWeeks.push(days.slice(i, i + 7));
383
+ }
384
+ const minTs = isoToUTCTimestamp(this._min);
385
+ const maxTs = isoToUTCTimestamp(this._max);
386
+ const disabledAt = (i) => {
387
+ const v = days[i].value;
388
+ return (minTs !== null && v < minTs) || (maxTs !== null && v > maxTs);
389
+ };
390
+ let defaultIndex = days.findIndex(d => d.isSelected);
391
+ if (defaultIndex === -1)
392
+ defaultIndex = days.findIndex(d => d.today);
393
+ if (defaultIndex === -1)
394
+ defaultIndex = 0;
395
+ if (this._focusedIndex === null ||
396
+ this._focusedIndex < 0 ||
397
+ this._focusedIndex > 41 ||
398
+ disabledAt(this._focusedIndex)) {
399
+ this._focusedIndex = defaultIndex;
400
+ }
401
+ this._cellElements = [];
402
+ let flatIndex = 0;
403
+ dayWeeks.forEach(week => {
404
+ const dayRow = document.createElement('div');
405
+ dayRow.className = 'calendar-row calendar-day-row';
406
+ dayRow.setAttribute('role', 'row');
407
+ week.forEach(dayContext => {
408
+ const disabled = (minTs !== null && dayContext.value < minTs) ||
409
+ (maxTs !== null && dayContext.value > maxTs);
410
+ const dayCell = this.renderDayCell(dayContext, disabled, flatIndex === this._focusedIndex);
411
+ dayCell.className = `${dayCell.className} calendar-cell calendar-day-cell`;
412
+ dayRow.appendChild(dayCell);
413
+ this._cellElements[flatIndex] = dayCell;
414
+ flatIndex++;
415
+ });
416
+ calendarContainer.appendChild(dayRow);
417
+ });
418
+ calendarContainer.addEventListener('keydown', (event) => {
419
+ const ke = event;
420
+ const delta = ke.key === 'ArrowLeft' ? -1 :
421
+ ke.key === 'ArrowRight' ? 1 :
422
+ ke.key === 'ArrowUp' ? -7 :
423
+ ke.key === 'ArrowDown' ? 7 :
424
+ 0;
425
+ if (delta === 0)
426
+ return;
427
+ const currentIndex = this._focusedIndex ?? defaultIndex;
428
+ const nextIndex = currentIndex + delta;
429
+ if (nextIndex < 0 || nextIndex > 41 || disabledAt(nextIndex))
430
+ return;
431
+ ke.preventDefault();
432
+ this._cellElements[currentIndex]?.setAttribute('tabindex', '-1');
433
+ this._focusedIndex = nextIndex;
434
+ const nextCell = this._cellElements[nextIndex];
435
+ if (nextCell) {
436
+ nextCell.setAttribute('tabindex', '0');
437
+ nextCell.focus();
438
+ }
439
+ });
440
+ root.appendChild(calendarContainer);
441
+ }
432
442
  }
433
443
  if (!customElements.get('ty-calendar-month')) {
434
- customElements.define('ty-calendar-month', TyCalendarMonth);
444
+ customElements.define('ty-calendar-month', TyCalendarMonth);
435
445
  }