glassdate-rn 0.1.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/index.js ADDED
@@ -0,0 +1,2215 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ GlassDatePicker: () => GlassDatePicker
24
+ });
25
+ module.exports = __toCommonJS(index_exports);
26
+
27
+ // src/GlassDatePicker.tsx
28
+ var import_react2 = require("react");
29
+ var import_react_native7 = require("react-native");
30
+
31
+ // src/logic/calendar.ts
32
+ function daysInMonth(year, month) {
33
+ return new Date(year, month, 0).getDate();
34
+ }
35
+ function firstWeekdayOf(year, month) {
36
+ return new Date(year, month - 1, 1).getDay();
37
+ }
38
+ function previousMonth(year, month) {
39
+ if (month === 1) {
40
+ return { year: year - 1, month: 12 };
41
+ }
42
+ return { year, month: month - 1 };
43
+ }
44
+ function nextMonth(year, month) {
45
+ if (month === 12) {
46
+ return { year: year + 1, month: 1 };
47
+ }
48
+ return { year, month: month + 1 };
49
+ }
50
+
51
+ // src/logic/constraints.ts
52
+ var DEFAULT_YEAR_RANGE = 100;
53
+ function makeDate(year, month, day) {
54
+ return new Date(year, month - 1, day);
55
+ }
56
+ function dateYear(d) {
57
+ return d.getFullYear();
58
+ }
59
+ function dateMonth(d) {
60
+ return d.getMonth() + 1;
61
+ }
62
+ function dateDay(d) {
63
+ return d.getDate();
64
+ }
65
+ function isLeapYear(year) {
66
+ return year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;
67
+ }
68
+ function computeAdultMaxDate(today) {
69
+ const year = dateYear(today) - 18;
70
+ const month = dateMonth(today);
71
+ const day = dateDay(today);
72
+ if (month === 2 && day === 29 && !isLeapYear(year)) {
73
+ return makeDate(year, 3, 1);
74
+ }
75
+ return makeDate(year, month, day);
76
+ }
77
+ function dateToNumber(d) {
78
+ return dateYear(d) * 1e4 + dateMonth(d) * 100 + dateDay(d);
79
+ }
80
+ function compareDates(a, b) {
81
+ return dateToNumber(a) - dateToNumber(b);
82
+ }
83
+ function resolveConstraintBounds(config, today) {
84
+ const range = config.yearRange ?? DEFAULT_YEAR_RANGE;
85
+ const defaultMin = makeDate(dateYear(today) - range, 1, 1);
86
+ const defaultMax = makeDate(dateYear(today) + range, 12, 31);
87
+ switch (config.preset) {
88
+ case "none":
89
+ return { minDate: defaultMin, maxDate: defaultMax };
90
+ case "birthday":
91
+ return { minDate: defaultMin, maxDate: today };
92
+ case "adult":
93
+ return { minDate: defaultMin, maxDate: computeAdultMaxDate(today) };
94
+ case "future": {
95
+ const tomorrow = makeDate(dateYear(today), dateMonth(today), dateDay(today) + 1);
96
+ return { minDate: tomorrow, maxDate: defaultMax };
97
+ }
98
+ case "custom":
99
+ return {
100
+ minDate: config.minDate ?? defaultMin,
101
+ maxDate: config.maxDate ?? makeDate(9999, 12, 31)
102
+ };
103
+ }
104
+ }
105
+ function isConstraintConfigInvalid(config) {
106
+ return config.preset === "custom" && config.minDate !== void 0 && config.maxDate !== void 0 && compareDates(config.minDate, config.maxDate) > 0;
107
+ }
108
+ function isDateSelectableFromBounds(date, bounds) {
109
+ if (date == null) return false;
110
+ return compareDates(date, bounds.minDate) >= 0 && compareDates(date, bounds.maxDate) <= 0;
111
+ }
112
+ function isDateSelectable(date, config, today) {
113
+ const bounds = resolveConstraintBounds(config, today);
114
+ return isDateSelectableFromBounds(date, bounds);
115
+ }
116
+ function isMonthSelectable(year, month, config, today) {
117
+ const bounds = resolveConstraintBounds(config, today);
118
+ const firstOfMonth = makeDate(year, month, 1);
119
+ const lastOfMonth = makeDate(year, month, daysInMonth(year, month));
120
+ return compareDates(lastOfMonth, bounds.minDate) >= 0 && compareDates(firstOfMonth, bounds.maxDate) <= 0;
121
+ }
122
+ function isYearSelectable(year, config, today) {
123
+ const bounds = resolveConstraintBounds(config, today);
124
+ const firstOfYear = makeDate(year, 1, 1);
125
+ const lastOfYear = makeDate(year, 12, 31);
126
+ return compareDates(lastOfYear, bounds.minDate) >= 0 && compareDates(firstOfYear, bounds.maxDate) <= 0;
127
+ }
128
+ function clampDate(date, config, today) {
129
+ if (isConstraintConfigInvalid(config)) return null;
130
+ const bounds = resolveConstraintBounds(config, today);
131
+ if (isDateSelectableFromBounds(date, bounds)) return date;
132
+ if (compareDates(date, bounds.minDate) < 0) return bounds.minDate;
133
+ if (compareDates(date, bounds.maxDate) > 0) return bounds.maxDate;
134
+ return null;
135
+ }
136
+
137
+ // src/utils/colourUtils.ts
138
+ var HEX6_REGEX = /^#[0-9A-Fa-f]{6}$/;
139
+ var HEX8_REGEX = /^#[0-9A-Fa-f]{8}$/;
140
+ function validateHexColour(s) {
141
+ return HEX6_REGEX.test(s) || HEX8_REGEX.test(s);
142
+ }
143
+
144
+ // src/logic/mono.ts
145
+ var MONO_DEFAULT_ACCENT = "#4FC3F7";
146
+ function hexToRgb(hex) {
147
+ const h = hex.replace("#", "");
148
+ return {
149
+ r: parseInt(h.slice(0, 2), 16),
150
+ g: parseInt(h.slice(2, 4), 16),
151
+ b: parseInt(h.slice(4, 6), 16)
152
+ };
153
+ }
154
+ function rgbToHex8(r, g, b, a) {
155
+ const clamp = (v) => Math.max(0, Math.min(255, Math.round(v)));
156
+ return "#" + [r, g, b, a].map((v) => clamp(v).toString(16).padStart(2, "0")).join("");
157
+ }
158
+ function withAlpha(hex, opacity) {
159
+ const { r, g, b } = hexToRgb(hex);
160
+ return rgbToHex8(r, g, b, Math.round(opacity * 255));
161
+ }
162
+ function relativeLuminance(hex) {
163
+ const { r, g, b } = hexToRgb(hex);
164
+ const linearise = (c) => {
165
+ const s = c / 255;
166
+ return s <= 0.03928 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);
167
+ };
168
+ return 0.2126 * linearise(r) + 0.7152 * linearise(g) + 0.0722 * linearise(b);
169
+ }
170
+ function computeMonoTokens(accent) {
171
+ const lum = relativeLuminance(accent);
172
+ const useDarkText = lum > 0.179;
173
+ const textOnSurface = useDarkText ? "#1A1A2E" : "#F5F5FF";
174
+ const textOnAccent = useDarkText ? "#1A1A2E" : "#FFFFFF";
175
+ const popupBg = useDarkText ? "#FAFAFA" : "#1A1A28";
176
+ const popupText = useDarkText ? "#1A1A2E" : "#F5F5FF";
177
+ return {
178
+ scene: { backgroundColour: withAlpha(accent, 0.13) },
179
+ container: {
180
+ backgroundColour: withAlpha(accent, 0.06),
181
+ borderColour: withAlpha(accent, 0.22),
182
+ borderWidth: 1,
183
+ cornerRadius: 18,
184
+ shadowColour: accent,
185
+ shadowOffsetX: 0,
186
+ shadowOffsetY: 6,
187
+ shadowBlurRadius: 24,
188
+ shadowOpacity: 0.15,
189
+ blurRadius: 0
190
+ },
191
+ pill: {
192
+ backgroundColour: withAlpha(accent, 0.1),
193
+ borderColour: withAlpha(accent, 0.25),
194
+ borderWidth: 1,
195
+ cornerRadius: 10,
196
+ textColour: accent,
197
+ fontWeight: 500,
198
+ hoverBackgroundColour: withAlpha(accent, 0.18),
199
+ todayBackgroundColour: accent,
200
+ todayBorderColour: accent,
201
+ todayTextColour: textOnAccent
202
+ },
203
+ popup: {
204
+ backgroundColour: popupBg,
205
+ borderColour: withAlpha(accent, 0.2),
206
+ borderWidth: 1,
207
+ cornerRadius: 14,
208
+ itemTextColour: popupText,
209
+ itemFontWeight: 500,
210
+ itemHoverBackgroundColour: withAlpha(accent, 0.08),
211
+ itemActiveBackgroundColour: accent,
212
+ itemActiveTextColour: textOnAccent,
213
+ itemSelectedBackgroundColour: withAlpha(accent, 0.18),
214
+ itemSelectedTextColour: accent,
215
+ itemDisabledOpacity: 0.22
216
+ },
217
+ nav: {
218
+ backgroundColour: withAlpha(accent, 0.1),
219
+ borderColour: withAlpha(accent, 0.22),
220
+ borderWidth: 1,
221
+ cornerRadius: 8,
222
+ arrowColour: accent,
223
+ hoverBackgroundColour: withAlpha(accent, 0.18),
224
+ disabledOpacity: 0.22
225
+ },
226
+ grid: {
227
+ weekdayLabelColour: withAlpha(accent, 0.45),
228
+ weekdayFontWeight: 600,
229
+ dayTextColour: textOnSurface,
230
+ dayFontWeight: 400,
231
+ dayHoverBackgroundColour: withAlpha(accent, 0.08),
232
+ daySelectedBackgroundColour: accent,
233
+ daySelectedTextColour: textOnAccent,
234
+ dayTodayFontWeight: 700,
235
+ dayTodayDotColour: accent,
236
+ dayOutOfRangeOpacity: 0.18,
237
+ dayAdjacentMonthOpacity: 0.18
238
+ },
239
+ footer: {
240
+ cancelBackgroundColour: withAlpha(accent, 0.07),
241
+ cancelBorderColour: withAlpha(accent, 0.18),
242
+ cancelBorderWidth: 1,
243
+ cancelTextColour: withAlpha(accent, 0.6),
244
+ cancelCornerRadius: 12,
245
+ cancelHoverBackgroundColour: withAlpha(accent, 0.14),
246
+ confirmBackgroundColour: accent,
247
+ confirmBorderColour: withAlpha(accent, 0.5),
248
+ confirmBorderWidth: 1,
249
+ confirmTextColour: textOnAccent,
250
+ confirmCornerRadius: 12,
251
+ confirmHoverBackgroundColour: withAlpha(accent, 0.87),
252
+ disabledOpacity: 0.38,
253
+ selectedDateTextColour: textOnSurface
254
+ },
255
+ meta: { name: "mono", mode: "single", defaultAccent: accent }
256
+ };
257
+ }
258
+
259
+ // ../tokens/themes/default.dark.json
260
+ var default_dark_default = {
261
+ scene: { backgroundColour: "#000000" },
262
+ container: {
263
+ backgroundColour: "#1C1C1E",
264
+ borderColour: "#38383A",
265
+ borderWidth: 1,
266
+ cornerRadius: 16,
267
+ shadowColour: "#000000",
268
+ shadowOffsetX: 0,
269
+ shadowOffsetY: 4,
270
+ shadowBlurRadius: 16,
271
+ shadowOpacity: 0.3,
272
+ blurRadius: 0
273
+ },
274
+ pill: {
275
+ backgroundColour: "#2C2C2E",
276
+ borderColour: "#38383A",
277
+ borderWidth: 1,
278
+ cornerRadius: 10,
279
+ textColour: "#FFFFFF",
280
+ fontWeight: 600,
281
+ hoverBackgroundColour: "#3A3A3C",
282
+ todayBackgroundColour: "accent",
283
+ todayBorderColour: "accent",
284
+ todayTextColour: "#FFFFFF"
285
+ },
286
+ popup: {
287
+ backgroundColour: "#2C2C2E",
288
+ borderColour: "#38383A",
289
+ borderWidth: 1,
290
+ cornerRadius: 12,
291
+ itemTextColour: "#FFFFFF",
292
+ itemFontWeight: 500,
293
+ itemHoverBackgroundColour: "#3A3A3C",
294
+ itemActiveBackgroundColour: "accent",
295
+ itemActiveTextColour: "#FFFFFF",
296
+ itemSelectedBackgroundColour: "#FFFFFF1A",
297
+ itemSelectedTextColour: "#FFFFFF",
298
+ itemDisabledOpacity: 0.3
299
+ },
300
+ nav: {
301
+ backgroundColour: "#2C2C2E",
302
+ borderColour: "#38383A",
303
+ borderWidth: 1,
304
+ cornerRadius: 8,
305
+ arrowColour: "#FFFFFF",
306
+ hoverBackgroundColour: "#3A3A3C",
307
+ disabledOpacity: 0.3
308
+ },
309
+ grid: {
310
+ weekdayLabelColour: "#8E8E93",
311
+ weekdayFontWeight: 600,
312
+ dayTextColour: "#FFFFFF",
313
+ dayFontWeight: 400,
314
+ dayHoverBackgroundColour: "#3A3A3C",
315
+ daySelectedBackgroundColour: "accent",
316
+ daySelectedTextColour: "#FFFFFF",
317
+ dayTodayFontWeight: 700,
318
+ dayTodayDotColour: "accent",
319
+ dayOutOfRangeOpacity: 0.25,
320
+ dayAdjacentMonthOpacity: 0.25
321
+ },
322
+ footer: {
323
+ cancelBackgroundColour: "#2C2C2E",
324
+ cancelBorderColour: "#38383A",
325
+ cancelBorderWidth: 1,
326
+ cancelTextColour: "#8E8E93",
327
+ cancelCornerRadius: 12,
328
+ cancelHoverBackgroundColour: "#3A3A3C",
329
+ confirmBackgroundColour: "accent",
330
+ confirmBorderColour: "accent",
331
+ confirmBorderWidth: 0,
332
+ confirmTextColour: "#FFFFFF",
333
+ confirmCornerRadius: 12,
334
+ confirmHoverBackgroundColour: "accent",
335
+ disabledOpacity: 0.4,
336
+ selectedDateTextColour: "#EBEBF5"
337
+ },
338
+ meta: { name: "default", mode: "dark", defaultAccent: "#007AFF" }
339
+ };
340
+
341
+ // ../tokens/themes/default.light.json
342
+ var default_light_default = {
343
+ scene: { backgroundColour: "#F2F2F7" },
344
+ container: {
345
+ backgroundColour: "#FFFFFF",
346
+ borderColour: "#D1D1D6",
347
+ borderWidth: 1,
348
+ cornerRadius: 16,
349
+ shadowColour: "#000000",
350
+ shadowOffsetX: 0,
351
+ shadowOffsetY: 4,
352
+ shadowBlurRadius: 16,
353
+ shadowOpacity: 0.08,
354
+ blurRadius: 0
355
+ },
356
+ pill: {
357
+ backgroundColour: "#E5E5EA",
358
+ borderColour: "#D1D1D6",
359
+ borderWidth: 1,
360
+ cornerRadius: 10,
361
+ textColour: "#1C1C1E",
362
+ fontWeight: 600,
363
+ hoverBackgroundColour: "#D1D1D6",
364
+ todayBackgroundColour: "accent",
365
+ todayBorderColour: "accent",
366
+ todayTextColour: "#FFFFFF"
367
+ },
368
+ popup: {
369
+ backgroundColour: "#FFFFFF",
370
+ borderColour: "#D1D1D6",
371
+ borderWidth: 1,
372
+ cornerRadius: 12,
373
+ itemTextColour: "#1C1C1E",
374
+ itemFontWeight: 500,
375
+ itemHoverBackgroundColour: "#E5E5EA",
376
+ itemActiveBackgroundColour: "accent",
377
+ itemActiveTextColour: "#FFFFFF",
378
+ itemSelectedBackgroundColour: "#00000014",
379
+ itemSelectedTextColour: "#1C1C1E",
380
+ itemDisabledOpacity: 0.3
381
+ },
382
+ nav: {
383
+ backgroundColour: "#E5E5EA",
384
+ borderColour: "#D1D1D6",
385
+ borderWidth: 1,
386
+ cornerRadius: 8,
387
+ arrowColour: "#1C1C1E",
388
+ hoverBackgroundColour: "#D1D1D6",
389
+ disabledOpacity: 0.3
390
+ },
391
+ grid: {
392
+ weekdayLabelColour: "#8E8E93",
393
+ weekdayFontWeight: 600,
394
+ dayTextColour: "#1C1C1E",
395
+ dayFontWeight: 400,
396
+ dayHoverBackgroundColour: "#E5E5EA",
397
+ daySelectedBackgroundColour: "accent",
398
+ daySelectedTextColour: "#FFFFFF",
399
+ dayTodayFontWeight: 700,
400
+ dayTodayDotColour: "accent",
401
+ dayOutOfRangeOpacity: 0.25,
402
+ dayAdjacentMonthOpacity: 0.25
403
+ },
404
+ footer: {
405
+ cancelBackgroundColour: "#E5E5EA",
406
+ cancelBorderColour: "#D1D1D6",
407
+ cancelBorderWidth: 1,
408
+ cancelTextColour: "#8E8E93",
409
+ cancelCornerRadius: 12,
410
+ cancelHoverBackgroundColour: "#D1D1D6",
411
+ confirmBackgroundColour: "accent",
412
+ confirmBorderColour: "accent",
413
+ confirmBorderWidth: 0,
414
+ confirmTextColour: "#FFFFFF",
415
+ confirmCornerRadius: 12,
416
+ confirmHoverBackgroundColour: "accent",
417
+ disabledOpacity: 0.4,
418
+ selectedDateTextColour: "#3C3C43"
419
+ },
420
+ meta: { name: "default", mode: "light", defaultAccent: "#007AFF" }
421
+ };
422
+
423
+ // ../tokens/themes/ios-liquid.dark.json
424
+ var ios_liquid_dark_default = {
425
+ scene: { backgroundColour: "#000000" },
426
+ container: {
427
+ backgroundColour: "#1C1C1E80",
428
+ borderColour: "#FFFFFF1A",
429
+ borderWidth: 0.5,
430
+ cornerRadius: 22,
431
+ shadowColour: "#000000",
432
+ shadowOffsetX: 0,
433
+ shadowOffsetY: 8,
434
+ shadowBlurRadius: 32,
435
+ shadowOpacity: 0.4,
436
+ blurRadius: 20
437
+ },
438
+ pill: {
439
+ backgroundColour: "#FFFFFF14",
440
+ borderColour: "#FFFFFF1A",
441
+ borderWidth: 0.5,
442
+ cornerRadius: 12,
443
+ textColour: "#EBEBF5",
444
+ fontWeight: 600,
445
+ hoverBackgroundColour: "#FFFFFF24",
446
+ todayBackgroundColour: "accent",
447
+ todayBorderColour: "accent",
448
+ todayTextColour: "#FFFFFF"
449
+ },
450
+ popup: {
451
+ backgroundColour: "#2C2C2ECC",
452
+ borderColour: "#FFFFFF1A",
453
+ borderWidth: 0.5,
454
+ cornerRadius: 14,
455
+ itemTextColour: "#EBEBF5",
456
+ itemFontWeight: 500,
457
+ itemHoverBackgroundColour: "#FFFFFF14",
458
+ itemActiveBackgroundColour: "accent",
459
+ itemActiveTextColour: "#FFFFFF",
460
+ itemSelectedBackgroundColour: "#FFFFFF1A",
461
+ itemSelectedTextColour: "#FFFFFF",
462
+ itemDisabledOpacity: 0.25
463
+ },
464
+ nav: {
465
+ backgroundColour: "#FFFFFF14",
466
+ borderColour: "#FFFFFF1A",
467
+ borderWidth: 0.5,
468
+ cornerRadius: 10,
469
+ arrowColour: "#EBEBF5",
470
+ hoverBackgroundColour: "#FFFFFF24",
471
+ disabledOpacity: 0.25
472
+ },
473
+ grid: {
474
+ weekdayLabelColour: "#EBEBF599",
475
+ weekdayFontWeight: 600,
476
+ dayTextColour: "#EBEBF5",
477
+ dayFontWeight: 400,
478
+ dayHoverBackgroundColour: "#FFFFFF14",
479
+ daySelectedBackgroundColour: "accent",
480
+ daySelectedTextColour: "#FFFFFF",
481
+ dayTodayFontWeight: 700,
482
+ dayTodayDotColour: "accent",
483
+ dayOutOfRangeOpacity: 0.2,
484
+ dayAdjacentMonthOpacity: 0.2
485
+ },
486
+ footer: {
487
+ cancelBackgroundColour: "#FFFFFF14",
488
+ cancelBorderColour: "#FFFFFF1A",
489
+ cancelBorderWidth: 0.5,
490
+ cancelTextColour: "#EBEBF599",
491
+ cancelCornerRadius: 14,
492
+ cancelHoverBackgroundColour: "#FFFFFF24",
493
+ confirmBackgroundColour: "accent",
494
+ confirmBorderColour: "accent",
495
+ confirmBorderWidth: 0,
496
+ confirmTextColour: "#FFFFFF",
497
+ confirmCornerRadius: 14,
498
+ confirmHoverBackgroundColour: "accent",
499
+ disabledOpacity: 0.35,
500
+ selectedDateTextColour: "#EBEBF599"
501
+ },
502
+ meta: { name: "ios-liquid", mode: "dark", defaultAccent: "#0A84FF" }
503
+ };
504
+
505
+ // ../tokens/themes/ios-liquid.light.json
506
+ var ios_liquid_light_default = {
507
+ scene: { backgroundColour: "#F2F2F7" },
508
+ container: {
509
+ backgroundColour: "#FFFFFF99",
510
+ borderColour: "#0000001A",
511
+ borderWidth: 0.5,
512
+ cornerRadius: 22,
513
+ shadowColour: "#000000",
514
+ shadowOffsetX: 0,
515
+ shadowOffsetY: 8,
516
+ shadowBlurRadius: 32,
517
+ shadowOpacity: 0.12,
518
+ blurRadius: 20
519
+ },
520
+ pill: {
521
+ backgroundColour: "#7878801F",
522
+ borderColour: "#7878801A",
523
+ borderWidth: 0.5,
524
+ cornerRadius: 12,
525
+ textColour: "#1C1C1E",
526
+ fontWeight: 600,
527
+ hoverBackgroundColour: "#78788033",
528
+ todayBackgroundColour: "accent",
529
+ todayBorderColour: "accent",
530
+ todayTextColour: "#FFFFFF"
531
+ },
532
+ popup: {
533
+ backgroundColour: "#FFFFFFCC",
534
+ borderColour: "#0000001A",
535
+ borderWidth: 0.5,
536
+ cornerRadius: 14,
537
+ itemTextColour: "#1C1C1E",
538
+ itemFontWeight: 500,
539
+ itemHoverBackgroundColour: "#78788014",
540
+ itemActiveBackgroundColour: "accent",
541
+ itemActiveTextColour: "#FFFFFF",
542
+ itemSelectedBackgroundColour: "#00000014",
543
+ itemSelectedTextColour: "#1C1C1E",
544
+ itemDisabledOpacity: 0.25
545
+ },
546
+ nav: {
547
+ backgroundColour: "#7878801F",
548
+ borderColour: "#7878801A",
549
+ borderWidth: 0.5,
550
+ cornerRadius: 10,
551
+ arrowColour: "#1C1C1E",
552
+ hoverBackgroundColour: "#78788033",
553
+ disabledOpacity: 0.25
554
+ },
555
+ grid: {
556
+ weekdayLabelColour: "#3C3C4399",
557
+ weekdayFontWeight: 600,
558
+ dayTextColour: "#1C1C1E",
559
+ dayFontWeight: 400,
560
+ dayHoverBackgroundColour: "#78788014",
561
+ daySelectedBackgroundColour: "accent",
562
+ daySelectedTextColour: "#FFFFFF",
563
+ dayTodayFontWeight: 700,
564
+ dayTodayDotColour: "accent",
565
+ dayOutOfRangeOpacity: 0.2,
566
+ dayAdjacentMonthOpacity: 0.2
567
+ },
568
+ footer: {
569
+ cancelBackgroundColour: "#7878801F",
570
+ cancelBorderColour: "#7878801A",
571
+ cancelBorderWidth: 0.5,
572
+ cancelTextColour: "#3C3C4399",
573
+ cancelCornerRadius: 14,
574
+ cancelHoverBackgroundColour: "#78788033",
575
+ confirmBackgroundColour: "accent",
576
+ confirmBorderColour: "accent",
577
+ confirmBorderWidth: 0,
578
+ confirmTextColour: "#FFFFFF",
579
+ confirmCornerRadius: 14,
580
+ confirmHoverBackgroundColour: "accent",
581
+ disabledOpacity: 0.35,
582
+ selectedDateTextColour: "#3C3C4399"
583
+ },
584
+ meta: { name: "ios-liquid", mode: "light", defaultAccent: "#007AFF" }
585
+ };
586
+
587
+ // ../tokens/themes/material3.dark.json
588
+ var material3_dark_default = {
589
+ scene: { backgroundColour: "#121212" },
590
+ container: {
591
+ backgroundColour: "#1E1E1E",
592
+ borderColour: "#333333",
593
+ borderWidth: 1,
594
+ cornerRadius: 28,
595
+ shadowColour: "#000000",
596
+ shadowOffsetX: 0,
597
+ shadowOffsetY: 2,
598
+ shadowBlurRadius: 8,
599
+ shadowOpacity: 0.3,
600
+ blurRadius: 0
601
+ },
602
+ pill: {
603
+ backgroundColour: "#2D2D2D",
604
+ borderColour: "#404040",
605
+ borderWidth: 0,
606
+ cornerRadius: 8,
607
+ textColour: "#E6E1E5",
608
+ fontWeight: 500,
609
+ hoverBackgroundColour: "#383838",
610
+ todayBackgroundColour: "accent",
611
+ todayBorderColour: "accent",
612
+ todayTextColour: "#FFFFFF"
613
+ },
614
+ popup: {
615
+ backgroundColour: "#2D2D2D",
616
+ borderColour: "#404040",
617
+ borderWidth: 0,
618
+ cornerRadius: 16,
619
+ itemTextColour: "#E6E1E5",
620
+ itemFontWeight: 500,
621
+ itemHoverBackgroundColour: "#383838",
622
+ itemActiveBackgroundColour: "accent",
623
+ itemActiveTextColour: "#FFFFFF",
624
+ itemSelectedBackgroundColour: "#FFFFFF1A",
625
+ itemSelectedTextColour: "#FFFFFF",
626
+ itemDisabledOpacity: 0.38
627
+ },
628
+ nav: {
629
+ backgroundColour: "#2D2D2D",
630
+ borderColour: "#404040",
631
+ borderWidth: 0,
632
+ cornerRadius: 20,
633
+ arrowColour: "#E6E1E5",
634
+ hoverBackgroundColour: "#383838",
635
+ disabledOpacity: 0.38
636
+ },
637
+ grid: {
638
+ weekdayLabelColour: "#CAC4D0",
639
+ weekdayFontWeight: 500,
640
+ dayTextColour: "#E6E1E5",
641
+ dayFontWeight: 400,
642
+ dayHoverBackgroundColour: "#383838",
643
+ daySelectedBackgroundColour: "accent",
644
+ daySelectedTextColour: "#FFFFFF",
645
+ dayTodayFontWeight: 600,
646
+ dayTodayDotColour: "accent",
647
+ dayOutOfRangeOpacity: 0.38,
648
+ dayAdjacentMonthOpacity: 0.38
649
+ },
650
+ footer: {
651
+ cancelBackgroundColour: "#2D2D2D",
652
+ cancelBorderColour: "#49454F",
653
+ cancelBorderWidth: 1,
654
+ cancelTextColour: "#CAC4D0",
655
+ cancelCornerRadius: 20,
656
+ cancelHoverBackgroundColour: "#383838",
657
+ confirmBackgroundColour: "accent",
658
+ confirmBorderColour: "accent",
659
+ confirmBorderWidth: 0,
660
+ confirmTextColour: "#FFFFFF",
661
+ confirmCornerRadius: 20,
662
+ confirmHoverBackgroundColour: "accent",
663
+ disabledOpacity: 0.38,
664
+ selectedDateTextColour: "#CAC4D0"
665
+ },
666
+ meta: { name: "material3", mode: "dark", defaultAccent: "#D0BCFF" }
667
+ };
668
+
669
+ // ../tokens/themes/material3.light.json
670
+ var material3_light_default = {
671
+ scene: { backgroundColour: "#FFFBFE" },
672
+ container: {
673
+ backgroundColour: "#FFFBFE",
674
+ borderColour: "#CAC4D0",
675
+ borderWidth: 1,
676
+ cornerRadius: 28,
677
+ shadowColour: "#000000",
678
+ shadowOffsetX: 0,
679
+ shadowOffsetY: 2,
680
+ shadowBlurRadius: 8,
681
+ shadowOpacity: 0.08,
682
+ blurRadius: 0
683
+ },
684
+ pill: {
685
+ backgroundColour: "#E8DEF8",
686
+ borderColour: "#CAC4D0",
687
+ borderWidth: 0,
688
+ cornerRadius: 8,
689
+ textColour: "#1C1B1F",
690
+ fontWeight: 500,
691
+ hoverBackgroundColour: "#D0BCFF66",
692
+ todayBackgroundColour: "accent",
693
+ todayBorderColour: "accent",
694
+ todayTextColour: "#FFFFFF"
695
+ },
696
+ popup: {
697
+ backgroundColour: "#FFFBFE",
698
+ borderColour: "#CAC4D0",
699
+ borderWidth: 0,
700
+ cornerRadius: 16,
701
+ itemTextColour: "#1C1B1F",
702
+ itemFontWeight: 500,
703
+ itemHoverBackgroundColour: "#E8DEF8",
704
+ itemActiveBackgroundColour: "accent",
705
+ itemActiveTextColour: "#FFFFFF",
706
+ itemSelectedBackgroundColour: "#00000014",
707
+ itemSelectedTextColour: "#1C1C1E",
708
+ itemDisabledOpacity: 0.38
709
+ },
710
+ nav: {
711
+ backgroundColour: "#E8DEF8",
712
+ borderColour: "#CAC4D0",
713
+ borderWidth: 0,
714
+ cornerRadius: 20,
715
+ arrowColour: "#1C1B1F",
716
+ hoverBackgroundColour: "#D0BCFF66",
717
+ disabledOpacity: 0.38
718
+ },
719
+ grid: {
720
+ weekdayLabelColour: "#49454F",
721
+ weekdayFontWeight: 500,
722
+ dayTextColour: "#1C1B1F",
723
+ dayFontWeight: 400,
724
+ dayHoverBackgroundColour: "#E8DEF8",
725
+ daySelectedBackgroundColour: "accent",
726
+ daySelectedTextColour: "#FFFFFF",
727
+ dayTodayFontWeight: 600,
728
+ dayTodayDotColour: "accent",
729
+ dayOutOfRangeOpacity: 0.38,
730
+ dayAdjacentMonthOpacity: 0.38
731
+ },
732
+ footer: {
733
+ cancelBackgroundColour: "#E8DEF8",
734
+ cancelBorderColour: "#79747E",
735
+ cancelBorderWidth: 1,
736
+ cancelTextColour: "#49454F",
737
+ cancelCornerRadius: 20,
738
+ cancelHoverBackgroundColour: "#D0BCFF66",
739
+ confirmBackgroundColour: "accent",
740
+ confirmBorderColour: "accent",
741
+ confirmBorderWidth: 0,
742
+ confirmTextColour: "#FFFFFF",
743
+ confirmCornerRadius: 20,
744
+ confirmHoverBackgroundColour: "accent",
745
+ disabledOpacity: 0.38,
746
+ selectedDateTextColour: "#49454F"
747
+ },
748
+ meta: { name: "material3", mode: "light", defaultAccent: "#6750A4" }
749
+ };
750
+
751
+ // ../tokens/themes/toon.json
752
+ var toon_default = {
753
+ scene: { backgroundColour: "#FFF8E1" },
754
+ container: {
755
+ backgroundColour: "#FFFDE7",
756
+ borderColour: "#1A1A1A",
757
+ borderWidth: 3,
758
+ cornerRadius: 20,
759
+ shadowColour: "#1A1A1A",
760
+ shadowOffsetX: 4,
761
+ shadowOffsetY: 4,
762
+ shadowBlurRadius: 0,
763
+ shadowOpacity: 1,
764
+ blurRadius: 0
765
+ },
766
+ pill: {
767
+ backgroundColour: "#FFE082",
768
+ borderColour: "#1A1A1A",
769
+ borderWidth: 2,
770
+ cornerRadius: 12,
771
+ textColour: "#1A1A1A",
772
+ fontWeight: 800,
773
+ hoverBackgroundColour: "#FFD54F",
774
+ todayBackgroundColour: "accent",
775
+ todayBorderColour: "accent",
776
+ todayTextColour: "#FFFFFF"
777
+ },
778
+ popup: {
779
+ backgroundColour: "#FFF9C4",
780
+ borderColour: "#1A1A1A",
781
+ borderWidth: 2,
782
+ cornerRadius: 14,
783
+ itemTextColour: "#1A1A1A",
784
+ itemFontWeight: 700,
785
+ itemHoverBackgroundColour: "#FFE082",
786
+ itemActiveBackgroundColour: "accent",
787
+ itemActiveTextColour: "#FFFFFF",
788
+ itemSelectedBackgroundColour: "#FFE082",
789
+ itemSelectedTextColour: "#1A1A1A",
790
+ itemDisabledOpacity: 0.3
791
+ },
792
+ nav: {
793
+ backgroundColour: "#FFE082",
794
+ borderColour: "#1A1A1A",
795
+ borderWidth: 2,
796
+ cornerRadius: 10,
797
+ arrowColour: "#1A1A1A",
798
+ hoverBackgroundColour: "#FFD54F",
799
+ disabledOpacity: 0.3
800
+ },
801
+ grid: {
802
+ weekdayLabelColour: "#5D4037",
803
+ weekdayFontWeight: 700,
804
+ dayTextColour: "#1A1A1A",
805
+ dayFontWeight: 600,
806
+ dayHoverBackgroundColour: "#FFE082",
807
+ daySelectedBackgroundColour: "accent",
808
+ daySelectedTextColour: "#FFFFFF",
809
+ dayTodayFontWeight: 800,
810
+ dayTodayDotColour: "accent",
811
+ dayOutOfRangeOpacity: 0.25,
812
+ dayAdjacentMonthOpacity: 0.25
813
+ },
814
+ footer: {
815
+ cancelBackgroundColour: "#FFE082",
816
+ cancelBorderColour: "#1A1A1A",
817
+ cancelBorderWidth: 2,
818
+ cancelTextColour: "#5D4037",
819
+ cancelCornerRadius: 14,
820
+ cancelHoverBackgroundColour: "#FFD54F",
821
+ confirmBackgroundColour: "accent",
822
+ confirmBorderColour: "accent",
823
+ confirmBorderWidth: 2,
824
+ confirmTextColour: "#FFFFFF",
825
+ confirmCornerRadius: 14,
826
+ confirmHoverBackgroundColour: "accent",
827
+ disabledOpacity: 0.35,
828
+ selectedDateTextColour: "#4E342E"
829
+ },
830
+ meta: { name: "toon", mode: "single", defaultAccent: "#FF6D00" }
831
+ };
832
+
833
+ // ../tokens/themes/flat.json
834
+ var flat_default = {
835
+ scene: { backgroundColour: "#ECEFF1" },
836
+ container: {
837
+ backgroundColour: "#FFFFFF",
838
+ borderColour: "#FFFFFF",
839
+ borderWidth: 0,
840
+ cornerRadius: 8,
841
+ shadowColour: "#000000",
842
+ shadowOffsetX: 0,
843
+ shadowOffsetY: 0,
844
+ shadowBlurRadius: 0,
845
+ shadowOpacity: 0,
846
+ blurRadius: 0
847
+ },
848
+ pill: {
849
+ backgroundColour: "#ECEFF1",
850
+ borderColour: "#ECEFF1",
851
+ borderWidth: 0,
852
+ cornerRadius: 6,
853
+ textColour: "#37474F",
854
+ fontWeight: 600,
855
+ hoverBackgroundColour: "#CFD8DC",
856
+ todayBackgroundColour: "accent",
857
+ todayBorderColour: "accent",
858
+ todayTextColour: "#FFFFFF"
859
+ },
860
+ popup: {
861
+ backgroundColour: "#ECEFF1",
862
+ borderColour: "#ECEFF1",
863
+ borderWidth: 0,
864
+ cornerRadius: 6,
865
+ itemTextColour: "#37474F",
866
+ itemFontWeight: 500,
867
+ itemHoverBackgroundColour: "#CFD8DC",
868
+ itemActiveBackgroundColour: "accent",
869
+ itemActiveTextColour: "#FFFFFF",
870
+ itemSelectedBackgroundColour: "#CFD8DC",
871
+ itemSelectedTextColour: "#37474F",
872
+ itemDisabledOpacity: 0.3
873
+ },
874
+ nav: {
875
+ backgroundColour: "#ECEFF1",
876
+ borderColour: "#ECEFF1",
877
+ borderWidth: 0,
878
+ cornerRadius: 6,
879
+ arrowColour: "#37474F",
880
+ hoverBackgroundColour: "#CFD8DC",
881
+ disabledOpacity: 0.3
882
+ },
883
+ grid: {
884
+ weekdayLabelColour: "#78909C",
885
+ weekdayFontWeight: 500,
886
+ dayTextColour: "#37474F",
887
+ dayFontWeight: 400,
888
+ dayHoverBackgroundColour: "#ECEFF1",
889
+ daySelectedBackgroundColour: "accent",
890
+ daySelectedTextColour: "#FFFFFF",
891
+ dayTodayFontWeight: 700,
892
+ dayTodayDotColour: "accent",
893
+ dayOutOfRangeOpacity: 0.25,
894
+ dayAdjacentMonthOpacity: 0.25
895
+ },
896
+ footer: {
897
+ cancelBackgroundColour: "#ECEFF1",
898
+ cancelBorderColour: "#ECEFF1",
899
+ cancelBorderWidth: 0,
900
+ cancelTextColour: "#78909C",
901
+ cancelCornerRadius: 6,
902
+ cancelHoverBackgroundColour: "#CFD8DC",
903
+ confirmBackgroundColour: "accent",
904
+ confirmBorderColour: "accent",
905
+ confirmBorderWidth: 0,
906
+ confirmTextColour: "#FFFFFF",
907
+ confirmCornerRadius: 6,
908
+ confirmHoverBackgroundColour: "accent",
909
+ disabledOpacity: 0.35,
910
+ selectedDateTextColour: "#546E7A"
911
+ },
912
+ meta: { name: "flat", mode: "single", defaultAccent: "#2196F3" }
913
+ };
914
+
915
+ // ../tokens/themes/plain.json
916
+ var plain_default = {
917
+ scene: { backgroundColour: "#FAFAFA" },
918
+ container: {
919
+ backgroundColour: "#FFFFFF",
920
+ borderColour: "#E0E0E0",
921
+ borderWidth: 1,
922
+ cornerRadius: 4,
923
+ shadowColour: "#000000",
924
+ shadowOffsetX: 0,
925
+ shadowOffsetY: 1,
926
+ shadowBlurRadius: 3,
927
+ shadowOpacity: 0.06,
928
+ blurRadius: 0
929
+ },
930
+ pill: {
931
+ backgroundColour: "#F5F5F5",
932
+ borderColour: "#E0E0E0",
933
+ borderWidth: 1,
934
+ cornerRadius: 4,
935
+ textColour: "#212121",
936
+ fontWeight: 500,
937
+ hoverBackgroundColour: "#EEEEEE",
938
+ todayBackgroundColour: "accent",
939
+ todayBorderColour: "accent",
940
+ todayTextColour: "#FFFFFF"
941
+ },
942
+ popup: {
943
+ backgroundColour: "#FFFFFF",
944
+ borderColour: "#E0E0E0",
945
+ borderWidth: 1,
946
+ cornerRadius: 4,
947
+ itemTextColour: "#212121",
948
+ itemFontWeight: 400,
949
+ itemHoverBackgroundColour: "#F5F5F5",
950
+ itemActiveBackgroundColour: "accent",
951
+ itemActiveTextColour: "#FFFFFF",
952
+ itemSelectedBackgroundColour: "#EEEEEE",
953
+ itemSelectedTextColour: "#212121",
954
+ itemDisabledOpacity: 0.3
955
+ },
956
+ nav: {
957
+ backgroundColour: "#F5F5F5",
958
+ borderColour: "#E0E0E0",
959
+ borderWidth: 1,
960
+ cornerRadius: 4,
961
+ arrowColour: "#212121",
962
+ hoverBackgroundColour: "#EEEEEE",
963
+ disabledOpacity: 0.3
964
+ },
965
+ grid: {
966
+ weekdayLabelColour: "#757575",
967
+ weekdayFontWeight: 500,
968
+ dayTextColour: "#212121",
969
+ dayFontWeight: 400,
970
+ dayHoverBackgroundColour: "#F5F5F5",
971
+ daySelectedBackgroundColour: "accent",
972
+ daySelectedTextColour: "#FFFFFF",
973
+ dayTodayFontWeight: 600,
974
+ dayTodayDotColour: "accent",
975
+ dayOutOfRangeOpacity: 0.25,
976
+ dayAdjacentMonthOpacity: 0.25
977
+ },
978
+ footer: {
979
+ cancelBackgroundColour: "#F5F5F5",
980
+ cancelBorderColour: "#E0E0E0",
981
+ cancelBorderWidth: 1,
982
+ cancelTextColour: "#757575",
983
+ cancelCornerRadius: 4,
984
+ cancelHoverBackgroundColour: "#EEEEEE",
985
+ confirmBackgroundColour: "accent",
986
+ confirmBorderColour: "accent",
987
+ confirmBorderWidth: 0,
988
+ confirmTextColour: "#FFFFFF",
989
+ confirmCornerRadius: 4,
990
+ confirmHoverBackgroundColour: "accent",
991
+ disabledOpacity: 0.35,
992
+ selectedDateTextColour: "#616161"
993
+ },
994
+ meta: { name: "plain", mode: "single", defaultAccent: "#424242" }
995
+ };
996
+
997
+ // src/logic/theme.ts
998
+ var THEME_MAP = {
999
+ "default.dark": default_dark_default,
1000
+ "default.light": default_light_default,
1001
+ "ios-liquid.dark": ios_liquid_dark_default,
1002
+ "ios-liquid.light": ios_liquid_light_default,
1003
+ "material3.dark": material3_dark_default,
1004
+ "material3.light": material3_light_default,
1005
+ "toon": toon_default,
1006
+ "flat": flat_default,
1007
+ "plain": plain_default
1008
+ };
1009
+ var SINGLE_MODE_THEMES = /* @__PURE__ */ new Set(["toon", "flat", "plain"]);
1010
+ function deepMerge(base, override) {
1011
+ const result = { ...base };
1012
+ for (const key of Object.keys(override)) {
1013
+ const ov = override[key];
1014
+ const bv = result[key];
1015
+ if (ov !== void 0 && ov !== null && typeof ov === "object" && !Array.isArray(ov) && typeof bv === "object" && bv !== null) {
1016
+ result[key] = deepMerge(
1017
+ bv,
1018
+ ov
1019
+ );
1020
+ } else if (ov !== void 0) {
1021
+ result[key] = ov;
1022
+ }
1023
+ }
1024
+ return result;
1025
+ }
1026
+ function replaceAccent(tokens, resolvedAccent) {
1027
+ const result = JSON.parse(JSON.stringify(tokens));
1028
+ const replace = (obj) => {
1029
+ for (const key of Object.keys(obj)) {
1030
+ const val = obj[key];
1031
+ if (val === "accent") {
1032
+ obj[key] = resolvedAccent;
1033
+ } else if (typeof val === "object" && val !== null) {
1034
+ replace(val);
1035
+ }
1036
+ }
1037
+ };
1038
+ replace(result);
1039
+ return result;
1040
+ }
1041
+ function mapDayShapeToRadius(dayShape) {
1042
+ switch (dayShape) {
1043
+ case "circle":
1044
+ return 9999;
1045
+ case "round-square":
1046
+ return 14;
1047
+ case "subtle":
1048
+ return 10;
1049
+ }
1050
+ }
1051
+ function resolveTokens(theme, colorScheme, accent, tokenOverrides) {
1052
+ let tokens;
1053
+ if (theme === "mono") {
1054
+ const resolvedAccent2 = accent !== void 0 && validateHexColour(accent) ? accent : MONO_DEFAULT_ACCENT;
1055
+ tokens = computeMonoTokens(resolvedAccent2);
1056
+ if (tokenOverrides !== void 0) {
1057
+ tokens = deepMerge(
1058
+ tokens,
1059
+ tokenOverrides
1060
+ );
1061
+ }
1062
+ return tokens;
1063
+ }
1064
+ if (SINGLE_MODE_THEMES.has(theme)) {
1065
+ const loaded = THEME_MAP[theme];
1066
+ if (!loaded) {
1067
+ throw new Error(`Unknown theme: ${theme}`);
1068
+ }
1069
+ tokens = JSON.parse(JSON.stringify(loaded));
1070
+ } else {
1071
+ const key = `${theme}.${colorScheme}`;
1072
+ const loaded = THEME_MAP[key];
1073
+ if (!loaded) {
1074
+ throw new Error(`Unknown theme/colorScheme: ${key}`);
1075
+ }
1076
+ tokens = JSON.parse(JSON.stringify(loaded));
1077
+ }
1078
+ const resolvedAccent = accent !== void 0 && validateHexColour(accent) ? accent : tokens.meta.defaultAccent;
1079
+ tokens = replaceAccent(tokens, resolvedAccent);
1080
+ if (tokenOverrides !== void 0) {
1081
+ tokens = deepMerge(
1082
+ tokens,
1083
+ tokenOverrides
1084
+ );
1085
+ }
1086
+ return tokens;
1087
+ }
1088
+
1089
+ // src/GlassContainerRN.tsx
1090
+ var import_react_native = require("react-native");
1091
+ var import_jsx_runtime = require("react/jsx-runtime");
1092
+ var GlassView = null;
1093
+ var isLiquidGlassAvailable = null;
1094
+ var isGlassEffectAPIAvailable = null;
1095
+ try {
1096
+ const pkg = require("expo-glass-effect");
1097
+ GlassView = pkg.GlassView;
1098
+ isLiquidGlassAvailable = pkg.isLiquidGlassAvailable;
1099
+ isGlassEffectAPIAvailable = pkg.isGlassEffectAPIAvailable;
1100
+ } catch {
1101
+ }
1102
+ function GlassContainer({
1103
+ blurRadius,
1104
+ backgroundColour,
1105
+ cornerRadius,
1106
+ style,
1107
+ children
1108
+ }) {
1109
+ const useGlass = blurRadius > 0 && GlassView !== null && isLiquidGlassAvailable?.() === true && isGlassEffectAPIAvailable?.() === true;
1110
+ if (useGlass && GlassView) {
1111
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1112
+ GlassView,
1113
+ {
1114
+ style: [
1115
+ style,
1116
+ {
1117
+ borderRadius: cornerRadius,
1118
+ ...import_react_native.Platform.select({ web: { userSelect: "none" } })
1119
+ }
1120
+ ],
1121
+ glassEffectStyle: "regular",
1122
+ tintColor: backgroundColour,
1123
+ children
1124
+ }
1125
+ );
1126
+ }
1127
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1128
+ import_react_native.View,
1129
+ {
1130
+ style: [
1131
+ style,
1132
+ {
1133
+ borderRadius: cornerRadius,
1134
+ backgroundColor: backgroundColour,
1135
+ ...import_react_native.Platform.select({ web: { userSelect: "none" } })
1136
+ }
1137
+ ],
1138
+ children
1139
+ }
1140
+ );
1141
+ }
1142
+
1143
+ // src/CalendarGrid.tsx
1144
+ var import_react_native2 = require("react-native");
1145
+ var import_jsx_runtime2 = require("react/jsx-runtime");
1146
+ var WEEKDAY_LABELS = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
1147
+ var FULL_MONTH_NAMES = [
1148
+ "January",
1149
+ "February",
1150
+ "March",
1151
+ "April",
1152
+ "May",
1153
+ "June",
1154
+ "July",
1155
+ "August",
1156
+ "September",
1157
+ "October",
1158
+ "November",
1159
+ "December"
1160
+ ];
1161
+ var TOTAL_CELLS = 42;
1162
+ function buildAccessibilityLabel(day, month, year, isToday, isSelected, isOutOfRange, isAdjacentMonth) {
1163
+ const dateStr = `${FULL_MONTH_NAMES[month - 1]} ${day}, ${year}`;
1164
+ const suffixes = [];
1165
+ if (isSelected) suffixes.push("selected");
1166
+ if (isToday) suffixes.push("today");
1167
+ if (isOutOfRange || isAdjacentMonth) suffixes.push("not available");
1168
+ return suffixes.length > 0 ? `${dateStr}, ${suffixes.join(", ")}` : dateStr;
1169
+ }
1170
+ function buildCells(year, month, firstDayOfWeek) {
1171
+ const cells = [];
1172
+ const daysThisMonth = daysInMonth(year, month);
1173
+ const startWeekday = firstWeekdayOf(year, month);
1174
+ const offset = (startWeekday - firstDayOfWeek + 7) % 7;
1175
+ const prev = previousMonth(year, month);
1176
+ const daysPrevMonth = daysInMonth(prev.year, prev.month);
1177
+ for (let i = 0; i < offset; i++) {
1178
+ cells.push({
1179
+ day: daysPrevMonth - offset + 1 + i,
1180
+ month: prev.month,
1181
+ year: prev.year,
1182
+ isAdjacentMonth: true
1183
+ });
1184
+ }
1185
+ for (let d = 1; d <= daysThisMonth; d++) {
1186
+ cells.push({ day: d, month, year, isAdjacentMonth: false });
1187
+ }
1188
+ const next = nextMonth(year, month);
1189
+ let nextDay = 1;
1190
+ while (cells.length < TOTAL_CELLS) {
1191
+ cells.push({ day: nextDay++, month: next.month, year: next.year, isAdjacentMonth: true });
1192
+ }
1193
+ const lastRow = cells.slice(35, 42);
1194
+ if (lastRow.every((c) => c.isAdjacentMonth)) {
1195
+ cells.length = 35;
1196
+ }
1197
+ return cells;
1198
+ }
1199
+ function isSameDateParts(a, year, month, day) {
1200
+ if (!a) return false;
1201
+ return a.getFullYear() === year && a.getMonth() + 1 === month && a.getDate() === day;
1202
+ }
1203
+ var webInteractive = import_react_native2.Platform.select({
1204
+ web: { cursor: "pointer", outlineStyle: "none" }
1205
+ });
1206
+ function CalendarGrid({
1207
+ viewYear,
1208
+ viewMonth,
1209
+ selectedDate,
1210
+ today,
1211
+ constraintConfig,
1212
+ resolvedTokens,
1213
+ dayShapeRadius,
1214
+ firstDayOfWeek,
1215
+ onDayPress
1216
+ }) {
1217
+ const bounds = resolveConstraintBounds(constraintConfig, today);
1218
+ const cells = buildCells(viewYear, viewMonth, firstDayOfWeek);
1219
+ const t = resolvedTokens.grid;
1220
+ const shiftedLabels = [];
1221
+ for (let i = 0; i < 7; i++) {
1222
+ shiftedLabels.push(WEEKDAY_LABELS[(firstDayOfWeek + i) % 7]);
1223
+ }
1224
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: [styles.container, import_react_native2.Platform.select({ web: { userSelect: "none" } })], children: [
1225
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.View, { style: styles.weekdayRow, children: shiftedLabels.map((label, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.View, { style: styles.weekdayCell, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1226
+ import_react_native2.Text,
1227
+ {
1228
+ style: {
1229
+ color: t.weekdayLabelColour,
1230
+ fontWeight: String(t.weekdayFontWeight),
1231
+ fontSize: 13,
1232
+ textAlign: "center"
1233
+ },
1234
+ children: label
1235
+ }
1236
+ ) }, i)) }),
1237
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.View, { style: styles.gridContainer, children: cells.map((cell, index) => {
1238
+ const cellDate = new Date(cell.year, cell.month - 1, cell.day);
1239
+ const isSelectable = !cell.isAdjacentMonth && isDateSelectableFromBounds(cellDate, bounds);
1240
+ const isSelected = isSameDateParts(selectedDate, cell.year, cell.month, cell.day);
1241
+ const isToday = !cell.isAdjacentMonth && today.getFullYear() === cell.year && today.getMonth() + 1 === cell.month && today.getDate() === cell.day;
1242
+ const isOutOfRange = !cell.isAdjacentMonth && !isSelectable;
1243
+ const a11yLabel = buildAccessibilityLabel(
1244
+ cell.day,
1245
+ cell.month,
1246
+ cell.year,
1247
+ isToday,
1248
+ isSelected,
1249
+ isOutOfRange,
1250
+ cell.isAdjacentMonth
1251
+ );
1252
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1253
+ import_react_native2.Pressable,
1254
+ {
1255
+ disabled: !isSelectable,
1256
+ accessibilityRole: "button",
1257
+ accessibilityLabel: a11yLabel,
1258
+ accessibilityState: {
1259
+ selected: isSelected,
1260
+ disabled: !isSelectable
1261
+ },
1262
+ onPress: () => {
1263
+ if (isSelectable) {
1264
+ onDayPress(cellDate);
1265
+ }
1266
+ },
1267
+ style: (({ hovered }) => [
1268
+ styles.dayCell,
1269
+ {
1270
+ borderRadius: dayShapeRadius
1271
+ },
1272
+ isSelected && {
1273
+ backgroundColor: t.daySelectedBackgroundColour
1274
+ },
1275
+ !isSelected && hovered && isSelectable && {
1276
+ backgroundColor: t.dayHoverBackgroundColour
1277
+ },
1278
+ !isSelected && hovered && isToday && !isOutOfRange && {
1279
+ backgroundColor: t.dayHoverBackgroundColour
1280
+ },
1281
+ isSelectable && webInteractive
1282
+ ]),
1283
+ children: [
1284
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1285
+ import_react_native2.Text,
1286
+ {
1287
+ style: [
1288
+ {
1289
+ fontSize: 16,
1290
+ textAlign: "center",
1291
+ color: isSelected ? t.daySelectedTextColour : t.dayTextColour,
1292
+ fontWeight: isToday && !isSelected ? String(t.dayTodayFontWeight) : String(t.dayFontWeight),
1293
+ opacity: cell.isAdjacentMonth ? t.dayAdjacentMonthOpacity : isOutOfRange ? t.dayOutOfRangeOpacity : 1
1294
+ }
1295
+ ],
1296
+ children: cell.day
1297
+ }
1298
+ ),
1299
+ isToday && !isSelected && !cell.isAdjacentMonth && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1300
+ import_react_native2.View,
1301
+ {
1302
+ style: {
1303
+ width: 5,
1304
+ height: 5,
1305
+ borderRadius: 2.5,
1306
+ backgroundColor: t.dayTodayDotColour,
1307
+ alignSelf: "center",
1308
+ marginTop: 2
1309
+ }
1310
+ }
1311
+ )
1312
+ ]
1313
+ },
1314
+ index
1315
+ );
1316
+ }) })
1317
+ ] });
1318
+ }
1319
+ var styles = import_react_native2.StyleSheet.create({
1320
+ container: {
1321
+ flex: 1
1322
+ },
1323
+ weekdayRow: {
1324
+ flexDirection: "row",
1325
+ marginBottom: 4
1326
+ },
1327
+ weekdayCell: {
1328
+ flex: 1,
1329
+ alignItems: "center",
1330
+ paddingVertical: 4
1331
+ },
1332
+ gridContainer: {
1333
+ flexDirection: "row",
1334
+ flexWrap: "wrap"
1335
+ },
1336
+ dayCell: {
1337
+ width: "14.285%",
1338
+ height: 44,
1339
+ justifyContent: "center",
1340
+ alignItems: "center"
1341
+ }
1342
+ });
1343
+
1344
+ // src/MonthYearHeader.tsx
1345
+ var import_react_native3 = require("react-native");
1346
+ var import_jsx_runtime3 = require("react/jsx-runtime");
1347
+ var MONTH_NAMES = [
1348
+ "January",
1349
+ "February",
1350
+ "March",
1351
+ "April",
1352
+ "May",
1353
+ "June",
1354
+ "July",
1355
+ "August",
1356
+ "September",
1357
+ "October",
1358
+ "November",
1359
+ "December"
1360
+ ];
1361
+ var webInteractive2 = import_react_native3.Platform.select({
1362
+ web: { cursor: "pointer", outlineStyle: "none" }
1363
+ });
1364
+ function MonthYearHeader({
1365
+ viewYear,
1366
+ viewMonth,
1367
+ today,
1368
+ openDropdown,
1369
+ isPrevArrowDisabled,
1370
+ isNextArrowDisabled,
1371
+ resolvedTokens,
1372
+ monthNames: customMonthNames,
1373
+ onMonthPillPress,
1374
+ onYearPillPress,
1375
+ onPrevPress,
1376
+ onNextPress
1377
+ }) {
1378
+ const pill = resolvedTokens.pill;
1379
+ const displayMonthNames = customMonthNames ?? MONTH_NAMES;
1380
+ const nav = resolvedTokens.nav;
1381
+ const isMonthPillHighlighted = viewYear === today.getFullYear() && viewMonth === today.getMonth() + 1;
1382
+ const isYearPillHighlighted = viewYear === today.getFullYear();
1383
+ const prevDisabled = isPrevArrowDisabled || openDropdown !== "none";
1384
+ const nextDisabled = isNextArrowDisabled || openDropdown !== "none";
1385
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: styles2.container, children: [
1386
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: styles2.pillContainer, children: [
1387
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1388
+ import_react_native3.Pressable,
1389
+ {
1390
+ onPress: onMonthPillPress,
1391
+ accessibilityRole: "button",
1392
+ accessibilityLabel: `${MONTH_NAMES[viewMonth - 1]}, select month`,
1393
+ style: (({ pressed, hovered }) => [
1394
+ styles2.pill,
1395
+ {
1396
+ backgroundColor: isMonthPillHighlighted ? pill.todayBackgroundColour : hovered ? pill.hoverBackgroundColour : pill.backgroundColour,
1397
+ borderColor: isMonthPillHighlighted ? pill.todayBorderColour : pill.borderColour,
1398
+ borderWidth: pill.borderWidth,
1399
+ borderRadius: pill.cornerRadius,
1400
+ opacity: pressed ? 0.7 : 1
1401
+ },
1402
+ webInteractive2
1403
+ ]),
1404
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1405
+ import_react_native3.Text,
1406
+ {
1407
+ style: {
1408
+ color: isMonthPillHighlighted ? pill.todayTextColour : pill.textColour,
1409
+ fontWeight: String(pill.fontWeight),
1410
+ fontSize: 16
1411
+ },
1412
+ children: [
1413
+ displayMonthNames[viewMonth - 1],
1414
+ " ",
1415
+ "\u25BC"
1416
+ ]
1417
+ }
1418
+ )
1419
+ }
1420
+ ),
1421
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1422
+ import_react_native3.Pressable,
1423
+ {
1424
+ onPress: onYearPillPress,
1425
+ accessibilityRole: "button",
1426
+ accessibilityLabel: `${viewYear}, select year`,
1427
+ style: (({ pressed, hovered }) => [
1428
+ styles2.pill,
1429
+ {
1430
+ backgroundColor: isYearPillHighlighted ? pill.todayBackgroundColour : hovered ? pill.hoverBackgroundColour : pill.backgroundColour,
1431
+ borderColor: isYearPillHighlighted ? pill.todayBorderColour : pill.borderColour,
1432
+ borderWidth: pill.borderWidth,
1433
+ borderRadius: pill.cornerRadius,
1434
+ opacity: pressed ? 0.7 : 1
1435
+ },
1436
+ webInteractive2
1437
+ ]),
1438
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1439
+ import_react_native3.Text,
1440
+ {
1441
+ style: {
1442
+ color: isYearPillHighlighted ? pill.todayTextColour : pill.textColour,
1443
+ fontWeight: String(pill.fontWeight),
1444
+ fontSize: 16
1445
+ },
1446
+ children: [
1447
+ viewYear,
1448
+ " ",
1449
+ "\u25BC"
1450
+ ]
1451
+ }
1452
+ )
1453
+ }
1454
+ )
1455
+ ] }),
1456
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: styles2.arrowGroup, children: [
1457
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1458
+ import_react_native3.Pressable,
1459
+ {
1460
+ disabled: prevDisabled,
1461
+ onPress: onPrevPress,
1462
+ accessibilityRole: "button",
1463
+ accessibilityLabel: "Previous month",
1464
+ accessibilityState: { disabled: prevDisabled },
1465
+ style: (({ pressed, hovered }) => [
1466
+ styles2.arrowButton,
1467
+ {
1468
+ backgroundColor: hovered && !prevDisabled ? nav.hoverBackgroundColour : nav.backgroundColour,
1469
+ borderColor: nav.borderColour,
1470
+ borderWidth: nav.borderWidth,
1471
+ borderRadius: nav.cornerRadius,
1472
+ opacity: prevDisabled ? nav.disabledOpacity : pressed ? 0.6 : 1
1473
+ },
1474
+ !prevDisabled && webInteractive2
1475
+ ]),
1476
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: { color: nav.arrowColour, fontSize: 18 }, children: "\u25C0" })
1477
+ }
1478
+ ),
1479
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1480
+ import_react_native3.Pressable,
1481
+ {
1482
+ disabled: nextDisabled,
1483
+ onPress: onNextPress,
1484
+ accessibilityRole: "button",
1485
+ accessibilityLabel: "Next month",
1486
+ accessibilityState: { disabled: nextDisabled },
1487
+ style: (({ pressed, hovered }) => [
1488
+ styles2.arrowButton,
1489
+ {
1490
+ backgroundColor: hovered && !nextDisabled ? nav.hoverBackgroundColour : nav.backgroundColour,
1491
+ borderColor: nav.borderColour,
1492
+ borderWidth: nav.borderWidth,
1493
+ borderRadius: nav.cornerRadius,
1494
+ opacity: nextDisabled ? nav.disabledOpacity : pressed ? 0.6 : 1
1495
+ },
1496
+ !nextDisabled && webInteractive2
1497
+ ]),
1498
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: { color: nav.arrowColour, fontSize: 18 }, children: "\u25B6" })
1499
+ }
1500
+ )
1501
+ ] })
1502
+ ] });
1503
+ }
1504
+ var styles2 = import_react_native3.StyleSheet.create({
1505
+ container: {
1506
+ flexDirection: "row",
1507
+ alignItems: "center",
1508
+ justifyContent: "space-between",
1509
+ paddingVertical: 8
1510
+ },
1511
+ pillContainer: {
1512
+ flexDirection: "row",
1513
+ gap: 6
1514
+ },
1515
+ pill: {
1516
+ flexDirection: "row",
1517
+ paddingHorizontal: 10,
1518
+ paddingVertical: 8,
1519
+ minHeight: 44,
1520
+ justifyContent: "center",
1521
+ alignItems: "center"
1522
+ },
1523
+ arrowGroup: {
1524
+ flexDirection: "row",
1525
+ gap: 2
1526
+ },
1527
+ arrowButton: {
1528
+ width: 44,
1529
+ height: 44,
1530
+ justifyContent: "center",
1531
+ alignItems: "center"
1532
+ }
1533
+ });
1534
+
1535
+ // src/MonthSelector.tsx
1536
+ var import_react_native4 = require("react-native");
1537
+ var import_jsx_runtime4 = require("react/jsx-runtime");
1538
+ var MONTH_ABBREVIATIONS = [
1539
+ "Jan",
1540
+ "Feb",
1541
+ "Mar",
1542
+ "Apr",
1543
+ "May",
1544
+ "Jun",
1545
+ "Jul",
1546
+ "Aug",
1547
+ "Sep",
1548
+ "Oct",
1549
+ "Nov",
1550
+ "Dec"
1551
+ ];
1552
+ var FULL_MONTH_NAMES2 = [
1553
+ "January",
1554
+ "February",
1555
+ "March",
1556
+ "April",
1557
+ "May",
1558
+ "June",
1559
+ "July",
1560
+ "August",
1561
+ "September",
1562
+ "October",
1563
+ "November",
1564
+ "December"
1565
+ ];
1566
+ var webInteractive3 = import_react_native4.Platform.select({
1567
+ web: { cursor: "pointer", outlineStyle: "none" }
1568
+ });
1569
+ function MonthSelector({
1570
+ viewYear,
1571
+ viewMonth,
1572
+ selectedDate,
1573
+ today,
1574
+ constraintConfig,
1575
+ resolvedTokens,
1576
+ monthNames: customMonthNames,
1577
+ onMonthSelect,
1578
+ onDismiss
1579
+ }) {
1580
+ const popup = resolvedTokens.popup;
1581
+ const selectedMonth = selectedDate !== null && selectedDate.getFullYear() === viewYear ? selectedDate.getMonth() + 1 : null;
1582
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.Pressable, { onPress: onDismiss, style: [styles3.overlay, import_react_native4.Platform.select({ web: { userSelect: "none" } })], children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1583
+ import_react_native4.View,
1584
+ {
1585
+ style: [
1586
+ styles3.grid,
1587
+ {
1588
+ backgroundColor: popup.backgroundColour,
1589
+ borderColor: popup.borderColour,
1590
+ borderWidth: popup.borderWidth,
1591
+ borderRadius: popup.cornerRadius
1592
+ }
1593
+ ],
1594
+ children: (customMonthNames ?? MONTH_ABBREVIATIONS).map((label, index) => {
1595
+ const month = index + 1;
1596
+ const isActive = month === viewMonth;
1597
+ const isSelected = !isActive && month === selectedMonth;
1598
+ const isSelectable = isMonthSelectable(viewYear, month, constraintConfig, today);
1599
+ const bgColor = isActive ? popup.itemActiveBackgroundColour : isSelected ? popup.itemSelectedBackgroundColour : "transparent";
1600
+ const textColor = isActive ? popup.itemActiveTextColour : isSelected ? popup.itemSelectedTextColour : popup.itemTextColour;
1601
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1602
+ import_react_native4.Pressable,
1603
+ {
1604
+ disabled: !isSelectable,
1605
+ accessibilityRole: "button",
1606
+ accessibilityLabel: FULL_MONTH_NAMES2[index],
1607
+ accessibilityState: {
1608
+ selected: isActive,
1609
+ disabled: !isSelectable
1610
+ },
1611
+ onPress: (e) => {
1612
+ e.stopPropagation();
1613
+ if (isSelectable) {
1614
+ onMonthSelect(month);
1615
+ }
1616
+ },
1617
+ style: (({ hovered }) => [
1618
+ styles3.monthItem,
1619
+ {
1620
+ backgroundColor: hovered && isSelectable && !isActive && !isSelected ? popup.itemHoverBackgroundColour : bgColor,
1621
+ borderRadius: popup.cornerRadius / 2,
1622
+ opacity: isSelectable ? 1 : popup.itemDisabledOpacity
1623
+ },
1624
+ isSelectable && webInteractive3
1625
+ ]),
1626
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1627
+ import_react_native4.Text,
1628
+ {
1629
+ style: {
1630
+ color: textColor,
1631
+ fontWeight: String(popup.itemFontWeight),
1632
+ fontSize: 15,
1633
+ textAlign: "center"
1634
+ },
1635
+ children: label
1636
+ }
1637
+ )
1638
+ },
1639
+ month
1640
+ );
1641
+ })
1642
+ }
1643
+ ) });
1644
+ }
1645
+ var styles3 = import_react_native4.StyleSheet.create({
1646
+ overlay: {
1647
+ flex: 1
1648
+ },
1649
+ grid: {
1650
+ flex: 1,
1651
+ flexDirection: "row",
1652
+ flexWrap: "wrap",
1653
+ padding: 8,
1654
+ justifyContent: "center",
1655
+ alignContent: "center"
1656
+ },
1657
+ monthItem: {
1658
+ width: "33.333%",
1659
+ paddingVertical: 16,
1660
+ justifyContent: "center",
1661
+ alignItems: "center",
1662
+ minHeight: 44
1663
+ }
1664
+ });
1665
+
1666
+ // src/YearSelector.tsx
1667
+ var import_react = require("react");
1668
+ var import_react_native5 = require("react-native");
1669
+ var import_jsx_runtime5 = require("react/jsx-runtime");
1670
+ var ITEM_HEIGHT = 44;
1671
+ var webInteractive4 = import_react_native5.Platform.select({
1672
+ web: { cursor: "pointer", outlineStyle: "none" }
1673
+ });
1674
+ function YearSelector({
1675
+ viewYear,
1676
+ selectedDate,
1677
+ today,
1678
+ constraintConfig,
1679
+ resolvedTokens,
1680
+ onYearSelect,
1681
+ onDismiss
1682
+ }) {
1683
+ const popup = resolvedTokens.popup;
1684
+ const scrollRef = (0, import_react.useRef)(null);
1685
+ const scrolled = (0, import_react.useRef)(false);
1686
+ const bounds = resolveConstraintBounds(constraintConfig, today);
1687
+ const minYear = bounds.minDate.getFullYear();
1688
+ const maxYear = Math.min(bounds.maxDate.getFullYear(), 9999);
1689
+ const years = (0, import_react.useMemo)(() => {
1690
+ const arr = [];
1691
+ for (let y = minYear; y <= maxYear; y++) {
1692
+ arr.push(y);
1693
+ }
1694
+ return arr;
1695
+ }, [minYear, maxYear]);
1696
+ const selectedYear = selectedDate !== null ? selectedDate.getFullYear() : null;
1697
+ const targetIndex = (0, import_react.useMemo)(() => {
1698
+ if (years.indexOf(viewYear) >= 0) return years.indexOf(viewYear);
1699
+ let best = 0;
1700
+ let bestDist = Infinity;
1701
+ for (let i = 0; i < years.length; i++) {
1702
+ if (isYearSelectable(years[i], constraintConfig, today)) {
1703
+ const dist = Math.abs(years[i] - viewYear);
1704
+ if (dist < bestDist) {
1705
+ best = i;
1706
+ bestDist = dist;
1707
+ }
1708
+ }
1709
+ }
1710
+ return best;
1711
+ }, [viewYear, constraintConfig, today, years]);
1712
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.Pressable, { onPress: onDismiss, style: [styles4.overlay, import_react_native5.Platform.select({ web: { userSelect: "none" } })], children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1713
+ import_react_native5.View,
1714
+ {
1715
+ style: [
1716
+ styles4.listContainer,
1717
+ {
1718
+ backgroundColor: popup.backgroundColour,
1719
+ borderColor: popup.borderColour,
1720
+ borderWidth: popup.borderWidth,
1721
+ borderRadius: popup.cornerRadius
1722
+ }
1723
+ ],
1724
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1725
+ import_react_native5.ScrollView,
1726
+ {
1727
+ ref: scrollRef,
1728
+ showsVerticalScrollIndicator: false,
1729
+ onContentSizeChange: () => {
1730
+ if (scrolled.current) return;
1731
+ scrolled.current = true;
1732
+ const offset = Math.max(0, targetIndex * ITEM_HEIGHT - 3 * ITEM_HEIGHT);
1733
+ scrollRef.current?.scrollTo({ y: offset, animated: false });
1734
+ },
1735
+ children: years.map((year) => {
1736
+ const isActive = year === viewYear;
1737
+ const isSelected = !isActive && year === selectedYear;
1738
+ const selectable = isYearSelectable(year, constraintConfig, today);
1739
+ const bgColor = isActive ? popup.itemActiveBackgroundColour : isSelected ? popup.itemSelectedBackgroundColour : "transparent";
1740
+ const textColor = isActive ? popup.itemActiveTextColour : isSelected ? popup.itemSelectedTextColour : popup.itemTextColour;
1741
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1742
+ import_react_native5.Pressable,
1743
+ {
1744
+ disabled: !selectable,
1745
+ accessibilityRole: "button",
1746
+ accessibilityLabel: String(year),
1747
+ accessibilityState: {
1748
+ selected: isActive,
1749
+ disabled: !selectable
1750
+ },
1751
+ onPress: (e) => {
1752
+ e.stopPropagation();
1753
+ if (selectable) {
1754
+ onYearSelect(year);
1755
+ }
1756
+ },
1757
+ style: (({ hovered }) => [
1758
+ styles4.yearItem,
1759
+ {
1760
+ backgroundColor: hovered && selectable && !isActive && !isSelected ? popup.itemHoverBackgroundColour : bgColor,
1761
+ borderRadius: popup.cornerRadius / 2,
1762
+ opacity: selectable ? 1 : popup.itemDisabledOpacity
1763
+ },
1764
+ selectable && webInteractive4
1765
+ ]),
1766
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1767
+ import_react_native5.Text,
1768
+ {
1769
+ style: {
1770
+ color: textColor,
1771
+ fontWeight: String(popup.itemFontWeight),
1772
+ fontSize: 16,
1773
+ textAlign: "center"
1774
+ },
1775
+ children: year
1776
+ }
1777
+ )
1778
+ },
1779
+ year
1780
+ );
1781
+ })
1782
+ }
1783
+ )
1784
+ }
1785
+ ) });
1786
+ }
1787
+ var styles4 = import_react_native5.StyleSheet.create({
1788
+ overlay: {
1789
+ flex: 1
1790
+ },
1791
+ listContainer: {
1792
+ flex: 1,
1793
+ overflow: "hidden"
1794
+ },
1795
+ yearItem: {
1796
+ height: ITEM_HEIGHT,
1797
+ justifyContent: "center",
1798
+ alignItems: "center",
1799
+ paddingHorizontal: 16
1800
+ }
1801
+ });
1802
+
1803
+ // src/ConfirmFooter.tsx
1804
+ var import_react_native6 = require("react-native");
1805
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1806
+ function defaultConfirmLabel(_date) {
1807
+ return "Done";
1808
+ }
1809
+ var webInteractive5 = import_react_native6.Platform.select({
1810
+ web: { cursor: "pointer", outlineStyle: "none" }
1811
+ });
1812
+ function ConfirmFooter({
1813
+ selectedDate,
1814
+ isConfirmEnabled,
1815
+ resolvedTokens,
1816
+ confirmLabel: confirmLabelFn,
1817
+ placeholderLabel = "Select a date",
1818
+ onCancel,
1819
+ onConfirm
1820
+ }) {
1821
+ const footer = resolvedTokens.footer;
1822
+ const labelText = selectedDate ? (confirmLabelFn ?? defaultConfirmLabel)(selectedDate) : placeholderLabel;
1823
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react_native6.View, { style: styles5.container, children: [
1824
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1825
+ import_react_native6.Pressable,
1826
+ {
1827
+ onPress: onCancel,
1828
+ accessibilityRole: "button",
1829
+ accessibilityLabel: "Cancel",
1830
+ style: (({ pressed, hovered }) => [
1831
+ styles5.button,
1832
+ {
1833
+ backgroundColor: hovered ? footer.cancelHoverBackgroundColour : footer.cancelBackgroundColour,
1834
+ borderColor: footer.cancelBorderColour,
1835
+ borderWidth: footer.cancelBorderWidth,
1836
+ borderRadius: footer.cancelCornerRadius,
1837
+ opacity: pressed ? 0.7 : 1
1838
+ },
1839
+ webInteractive5
1840
+ ]),
1841
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1842
+ import_react_native6.Text,
1843
+ {
1844
+ style: {
1845
+ color: footer.cancelTextColour,
1846
+ fontSize: 16,
1847
+ fontWeight: "500",
1848
+ textAlign: "center"
1849
+ },
1850
+ children: "Cancel"
1851
+ }
1852
+ )
1853
+ }
1854
+ ),
1855
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native6.View, { style: styles5.gap }),
1856
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1857
+ import_react_native6.Pressable,
1858
+ {
1859
+ disabled: !isConfirmEnabled,
1860
+ accessibilityRole: "button",
1861
+ accessibilityLabel: labelText,
1862
+ accessibilityState: { disabled: !isConfirmEnabled },
1863
+ onPress: () => {
1864
+ if (isConfirmEnabled) {
1865
+ onConfirm();
1866
+ }
1867
+ },
1868
+ style: (({ pressed, hovered }) => [
1869
+ styles5.button,
1870
+ {
1871
+ backgroundColor: hovered && isConfirmEnabled ? footer.confirmHoverBackgroundColour : footer.confirmBackgroundColour,
1872
+ borderColor: footer.confirmBorderColour,
1873
+ borderWidth: footer.confirmBorderWidth,
1874
+ borderRadius: footer.confirmCornerRadius,
1875
+ opacity: !isConfirmEnabled ? footer.disabledOpacity : pressed ? 0.7 : 1
1876
+ },
1877
+ isConfirmEnabled && webInteractive5
1878
+ ]),
1879
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1880
+ import_react_native6.Text,
1881
+ {
1882
+ style: {
1883
+ color: footer.confirmTextColour,
1884
+ fontSize: 16,
1885
+ fontWeight: "600",
1886
+ textAlign: "center"
1887
+ },
1888
+ children: labelText
1889
+ }
1890
+ )
1891
+ }
1892
+ )
1893
+ ] });
1894
+ }
1895
+ var styles5 = import_react_native6.StyleSheet.create({
1896
+ container: {
1897
+ flexDirection: "row",
1898
+ paddingTop: 8
1899
+ },
1900
+ button: {
1901
+ flex: 1,
1902
+ paddingVertical: 14,
1903
+ alignItems: "center",
1904
+ justifyContent: "center",
1905
+ minHeight: 44
1906
+ },
1907
+ gap: {
1908
+ width: 8
1909
+ }
1910
+ });
1911
+
1912
+ // src/GlassDatePicker.tsx
1913
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1914
+ function defaultSelectedDateLabel(date) {
1915
+ return new Intl.DateTimeFormat(void 0, { dateStyle: "full" }).format(date);
1916
+ }
1917
+ function getDefaultTheme() {
1918
+ const os = import_react_native7.Platform.OS;
1919
+ if (os === "ios") return "ios-liquid";
1920
+ if (os === "android") return "material3";
1921
+ return "default";
1922
+ }
1923
+ function GlassDatePicker({
1924
+ value,
1925
+ hintDate,
1926
+ onChange,
1927
+ onConfirm,
1928
+ onCancel,
1929
+ constraint = "none",
1930
+ minDate,
1931
+ maxDate,
1932
+ yearRange,
1933
+ theme,
1934
+ colorScheme: colorSchemeProp = "auto",
1935
+ accent,
1936
+ dayShape = "circle",
1937
+ tokens: tokenOverrides,
1938
+ firstDayOfWeek: firstDayOfWeekProp = 0,
1939
+ monthNames: monthNamesProp,
1940
+ confirmLabel: confirmLabelProp,
1941
+ placeholderLabel: placeholderLabelProp,
1942
+ showSelectedDate = false,
1943
+ selectedDateLabel: selectedDateLabelProp
1944
+ }) {
1945
+ const [today] = (0, import_react2.useState)(() => /* @__PURE__ */ new Date());
1946
+ const initialView = hintDate ?? today;
1947
+ const [viewYear, setViewYear] = (0, import_react2.useState)(initialView.getFullYear());
1948
+ const [viewMonth, setViewMonth] = (0, import_react2.useState)(initialView.getMonth() + 1);
1949
+ const [selectedDate, setSelectedDate] = (0, import_react2.useState)(null);
1950
+ const [openDropdown, setOpenDropdown] = (0, import_react2.useState)("none");
1951
+ const activeTheme = theme ?? getDefaultTheme();
1952
+ const systemColorScheme = (0, import_react_native7.useColorScheme)();
1953
+ const resolvedColorScheme = colorSchemeProp === "auto" ? systemColorScheme === "dark" ? "dark" : "light" : colorSchemeProp;
1954
+ let firstDayOfWeek = firstDayOfWeekProp;
1955
+ if (firstDayOfWeek < 0 || firstDayOfWeek > 6) {
1956
+ console.warn(
1957
+ `GlassDate: firstDayOfWeek must be 0\u20136, got ${firstDayOfWeek} \u2014 defaulting to 0`
1958
+ );
1959
+ firstDayOfWeek = 0;
1960
+ }
1961
+ let monthNames;
1962
+ if (monthNamesProp) {
1963
+ if (monthNamesProp.length === 12) {
1964
+ monthNames = monthNamesProp;
1965
+ } else {
1966
+ console.warn(
1967
+ `GlassDate: monthNames must have exactly 12 elements, got ${monthNamesProp.length} \u2014 using English defaults`
1968
+ );
1969
+ }
1970
+ }
1971
+ const resolvedTokens = resolveTokens(activeTheme, resolvedColorScheme, accent, tokenOverrides);
1972
+ const dayShapeRadius = mapDayShapeToRadius(dayShape);
1973
+ const constraintConfig = {
1974
+ preset: constraint,
1975
+ ...constraint === "custom" && minDate !== void 0 && { minDate },
1976
+ ...constraint === "custom" && maxDate !== void 0 && { maxDate },
1977
+ ...yearRange !== void 0 && { yearRange }
1978
+ };
1979
+ const resolvedBounds = resolveConstraintBounds(constraintConfig, today);
1980
+ const isConfirmEnabled = selectedDate !== null && isDateSelectableFromBounds(selectedDate, resolvedBounds);
1981
+ const prev = previousMonth(viewYear, viewMonth);
1982
+ const nxt = nextMonth(viewYear, viewMonth);
1983
+ const isPrevArrowDisabled = !isMonthSelectable(prev.year, prev.month, constraintConfig, today);
1984
+ const isNextArrowDisabled = !isMonthSelectable(nxt.year, nxt.month, constraintConfig, today);
1985
+ (0, import_react2.useEffect)(() => {
1986
+ if (isConstraintConfigInvalid(constraintConfig)) {
1987
+ console.warn("GlassDate: minDate is after maxDate");
1988
+ }
1989
+ }, []);
1990
+ (0, import_react2.useEffect)(() => {
1991
+ setOpenDropdown("none");
1992
+ if (value === null || value === void 0) {
1993
+ setSelectedDate(null);
1994
+ return;
1995
+ }
1996
+ if (isDateSelectable(value, constraintConfig, today)) {
1997
+ setSelectedDate(value);
1998
+ setViewYear(value.getFullYear());
1999
+ setViewMonth(value.getMonth() + 1);
2000
+ } else {
2001
+ console.warn("GlassDate: updated value is outside constraint range");
2002
+ const clamped = clampDate(value, constraintConfig, today);
2003
+ if (clamped) {
2004
+ setSelectedDate(clamped);
2005
+ setViewYear(clamped.getFullYear());
2006
+ setViewMonth(clamped.getMonth() + 1);
2007
+ } else {
2008
+ setSelectedDate(null);
2009
+ }
2010
+ }
2011
+ }, [value?.getTime()]);
2012
+ (0, import_react2.useEffect)(() => {
2013
+ setOpenDropdown("none");
2014
+ if (isConstraintConfigInvalid(constraintConfig)) {
2015
+ setSelectedDate(null);
2016
+ console.warn("GlassDate: minDate is after maxDate");
2017
+ return;
2018
+ }
2019
+ if (selectedDate !== null && !isDateSelectable(selectedDate, constraintConfig, today)) {
2020
+ setSelectedDate(null);
2021
+ }
2022
+ if (!isMonthSelectable(viewYear, viewMonth, constraintConfig, today)) {
2023
+ const bounds = resolveConstraintBounds(constraintConfig, today);
2024
+ setViewYear(bounds.maxDate.getFullYear());
2025
+ setViewMonth(bounds.maxDate.getMonth() + 1);
2026
+ }
2027
+ }, [constraint, minDate?.getTime(), maxDate?.getTime()]);
2028
+ const handleDayPress = (0, import_react2.useCallback)(
2029
+ (date) => {
2030
+ if (selectedDate && selectedDate.getFullYear() === date.getFullYear() && selectedDate.getMonth() === date.getMonth() && selectedDate.getDate() === date.getDate()) {
2031
+ return;
2032
+ }
2033
+ setSelectedDate(date);
2034
+ setOpenDropdown("none");
2035
+ onChange?.(date);
2036
+ },
2037
+ [selectedDate, onChange]
2038
+ );
2039
+ const handleMonthPillPress = (0, import_react2.useCallback)(() => {
2040
+ setOpenDropdown((prev2) => prev2 === "month" ? "none" : "month");
2041
+ }, []);
2042
+ const handleYearPillPress = (0, import_react2.useCallback)(() => {
2043
+ setOpenDropdown((prev2) => prev2 === "year" ? "none" : "year");
2044
+ }, []);
2045
+ const handlePrevPress = (0, import_react2.useCallback)(() => {
2046
+ const p = previousMonth(viewYear, viewMonth);
2047
+ setViewYear(p.year);
2048
+ setViewMonth(p.month);
2049
+ setOpenDropdown("none");
2050
+ }, [viewYear, viewMonth]);
2051
+ const handleNextPress = (0, import_react2.useCallback)(() => {
2052
+ const n = nextMonth(viewYear, viewMonth);
2053
+ setViewYear(n.year);
2054
+ setViewMonth(n.month);
2055
+ setOpenDropdown("none");
2056
+ }, [viewYear, viewMonth]);
2057
+ const handleMonthSelect = (0, import_react2.useCallback)((month) => {
2058
+ setViewMonth(month);
2059
+ setOpenDropdown("none");
2060
+ }, []);
2061
+ const handleYearSelect = (0, import_react2.useCallback)((year) => {
2062
+ setViewYear(year);
2063
+ setOpenDropdown("none");
2064
+ if (selectedDate) {
2065
+ const projected = new Date(year, selectedDate.getMonth(), selectedDate.getDate());
2066
+ if (!isDateSelectableFromBounds(projected, resolvedBounds)) {
2067
+ setSelectedDate(null);
2068
+ }
2069
+ }
2070
+ }, [selectedDate, resolvedBounds]);
2071
+ const handleConfirm = (0, import_react2.useCallback)(() => {
2072
+ if (isConfirmEnabled && selectedDate) {
2073
+ onConfirm?.(selectedDate);
2074
+ }
2075
+ }, [isConfirmEnabled, selectedDate, onConfirm]);
2076
+ const handleCancel = (0, import_react2.useCallback)(() => {
2077
+ onCancel?.();
2078
+ }, [onCancel]);
2079
+ const handleDismissDropdown = (0, import_react2.useCallback)(() => {
2080
+ setOpenDropdown("none");
2081
+ }, []);
2082
+ const containerTokens = resolvedTokens.container;
2083
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2084
+ import_react_native7.View,
2085
+ {
2086
+ style: [
2087
+ styles6.scene,
2088
+ { backgroundColor: resolvedTokens.scene.backgroundColour },
2089
+ import_react_native7.Platform.select({ web: { userSelect: "none" } })
2090
+ ],
2091
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2092
+ GlassContainer,
2093
+ {
2094
+ blurRadius: containerTokens.blurRadius,
2095
+ backgroundColour: containerTokens.backgroundColour,
2096
+ cornerRadius: containerTokens.cornerRadius,
2097
+ style: [
2098
+ styles6.container,
2099
+ {
2100
+ borderColor: containerTokens.borderColour,
2101
+ borderWidth: containerTokens.borderWidth,
2102
+ shadowColor: containerTokens.shadowColour,
2103
+ shadowOffset: {
2104
+ width: containerTokens.shadowOffsetX,
2105
+ height: containerTokens.shadowOffsetY
2106
+ },
2107
+ shadowRadius: containerTokens.shadowBlurRadius,
2108
+ shadowOpacity: containerTokens.shadowOpacity
2109
+ }
2110
+ ],
2111
+ children: [
2112
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2113
+ MonthYearHeader,
2114
+ {
2115
+ viewYear,
2116
+ viewMonth,
2117
+ today,
2118
+ openDropdown,
2119
+ isPrevArrowDisabled,
2120
+ isNextArrowDisabled,
2121
+ resolvedTokens,
2122
+ monthNames,
2123
+ onMonthPillPress: handleMonthPillPress,
2124
+ onYearPillPress: handleYearPillPress,
2125
+ onPrevPress: handlePrevPress,
2126
+ onNextPress: handleNextPress
2127
+ }
2128
+ ),
2129
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native7.View, { style: styles6.contentArea, children: [
2130
+ openDropdown === "none" && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2131
+ CalendarGrid,
2132
+ {
2133
+ viewYear,
2134
+ viewMonth,
2135
+ selectedDate,
2136
+ today,
2137
+ constraintConfig,
2138
+ resolvedTokens,
2139
+ dayShapeRadius,
2140
+ firstDayOfWeek,
2141
+ onDayPress: handleDayPress
2142
+ }
2143
+ ),
2144
+ openDropdown === "month" && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2145
+ MonthSelector,
2146
+ {
2147
+ viewYear,
2148
+ viewMonth,
2149
+ selectedDate,
2150
+ today,
2151
+ constraintConfig,
2152
+ resolvedTokens,
2153
+ monthNames,
2154
+ onMonthSelect: handleMonthSelect,
2155
+ onDismiss: handleDismissDropdown
2156
+ }
2157
+ ),
2158
+ openDropdown === "year" && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2159
+ YearSelector,
2160
+ {
2161
+ viewYear,
2162
+ selectedDate,
2163
+ today,
2164
+ constraintConfig,
2165
+ resolvedTokens,
2166
+ onYearSelect: handleYearSelect,
2167
+ onDismiss: handleDismissDropdown
2168
+ }
2169
+ )
2170
+ ] }),
2171
+ showSelectedDate && selectedDate && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native7.Text, { style: [
2172
+ styles6.selectedDateText,
2173
+ { color: resolvedTokens.footer.selectedDateTextColour }
2174
+ ], children: (selectedDateLabelProp ?? defaultSelectedDateLabel)(selectedDate) }),
2175
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2176
+ ConfirmFooter,
2177
+ {
2178
+ selectedDate,
2179
+ isConfirmEnabled,
2180
+ resolvedTokens,
2181
+ confirmLabel: confirmLabelProp,
2182
+ placeholderLabel: placeholderLabelProp,
2183
+ onCancel: handleCancel,
2184
+ onConfirm: handleConfirm
2185
+ }
2186
+ )
2187
+ ]
2188
+ }
2189
+ )
2190
+ }
2191
+ );
2192
+ }
2193
+ var styles6 = import_react_native7.StyleSheet.create({
2194
+ scene: {
2195
+ padding: 16
2196
+ },
2197
+ container: {
2198
+ padding: 16,
2199
+ overflow: "hidden",
2200
+ height: 480
2201
+ },
2202
+ contentArea: {
2203
+ flex: 1
2204
+ },
2205
+ selectedDateText: {
2206
+ fontSize: 15,
2207
+ textAlign: "center",
2208
+ paddingVertical: 8
2209
+ }
2210
+ });
2211
+ // Annotate the CommonJS export names for ESM import in node:
2212
+ 0 && (module.exports = {
2213
+ GlassDatePicker
2214
+ });
2215
+ //# sourceMappingURL=index.js.map