@nepdatejs/react 0.0.0-stage → 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.
Files changed (46) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +74 -2
  3. package/dist/date-text-BT82cZ7i.cjs +84 -0
  4. package/dist/date-text-BT82cZ7i.cjs.map +1 -0
  5. package/dist/date-text-Df1gFksR.js +61 -0
  6. package/dist/date-text-Df1gFksR.js.map +1 -0
  7. package/dist/date-text-Dipetpbp.d.cts +37 -0
  8. package/dist/date-text-Dipetpbp.d.ts +37 -0
  9. package/dist/headless-6BuUOtOR.d.cts +194 -0
  10. package/dist/headless-BK0XRHKb.js +513 -0
  11. package/dist/headless-BK0XRHKb.js.map +1 -0
  12. package/dist/headless-CFZ06zLn.d.ts +194 -0
  13. package/dist/headless-HxTkvgbz.cjs +572 -0
  14. package/dist/headless-HxTkvgbz.cjs.map +1 -0
  15. package/dist/headless.cjs +11 -0
  16. package/dist/headless.d.cts +2 -0
  17. package/dist/headless.d.ts +2 -0
  18. package/dist/headless.js +2 -0
  19. package/dist/index.cjs +891 -0
  20. package/dist/index.cjs.map +1 -0
  21. package/dist/index.d.cts +245 -0
  22. package/dist/index.d.ts +245 -0
  23. package/dist/index.js +871 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/server.cjs +6 -0
  26. package/dist/server.d.cts +2 -0
  27. package/dist/server.d.ts +2 -0
  28. package/dist/server.js +1 -0
  29. package/package.json +110 -3
  30. package/src/bindings.ts +247 -0
  31. package/src/calendar-body.tsx +133 -0
  32. package/src/calendar.tsx +34 -0
  33. package/src/date-picker.tsx +350 -0
  34. package/src/date-range-picker.tsx +223 -0
  35. package/src/date-text.tsx +110 -0
  36. package/src/date-time-picker.tsx +287 -0
  37. package/src/headless.ts +21 -0
  38. package/src/hooks.ts +474 -0
  39. package/src/icons.tsx +35 -0
  40. package/src/index.ts +21 -0
  41. package/src/multi-date-picker.tsx +243 -0
  42. package/src/panel.ts +67 -0
  43. package/src/presets.tsx +44 -0
  44. package/src/server.ts +8 -0
  45. package/src/time-columns.tsx +60 -0
  46. package/src/use-store.ts +113 -0
package/dist/index.cjs ADDED
@@ -0,0 +1,891 @@
1
+ "use client";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const require_headless = require("./headless-HxTkvgbz.cjs");
4
+ const require_date_text = require("./date-text-BT82cZ7i.cjs");
5
+ let react_jsx_runtime = require("react/jsx-runtime");
6
+ let react = require("react");
7
+ let _nepdatejs_core = require("@nepdatejs/core");
8
+ let _nepdatejs_dom = require("@nepdatejs/dom");
9
+ //#region src/icons.tsx
10
+ const PATHS = {
11
+ "chevron-left": ["M10 3.5 5.5 8l4.5 4.5"],
12
+ "chevron-right": ["M6 3.5 10.5 8 6 12.5"],
13
+ calendar: [
14
+ "M2 6.5h12",
15
+ "M5.5 1.5v3",
16
+ "M10.5 1.5v3",
17
+ "M4 3h8a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2Z"
18
+ ],
19
+ close: ["M4 4l8 8", "M12 4l-8 8"]
20
+ };
21
+ /** The Figma icon set, 16px, stroked with currentColor. */
22
+ function Icon({ name }) {
23
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
24
+ viewBox: "0 0 16 16",
25
+ fill: "none",
26
+ stroke: "currentColor",
27
+ strokeWidth: 1.5,
28
+ strokeLinecap: "round",
29
+ strokeLinejoin: "round",
30
+ "aria-hidden": "true",
31
+ focusable: "false",
32
+ children: PATHS[name].map((d) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d }, d))
33
+ });
34
+ }
35
+ //#endregion
36
+ //#region src/calendar-body.tsx
37
+ /** The calendar markup (@nepdatejs/styles contract) for any set of bindings. */
38
+ function CalendarBody({ bindings, className, theme }) {
39
+ const { state } = bindings;
40
+ const tiles = state.view === "month" ? state.months : state.view === "year" ? state.years : state.fiscalYears;
41
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
42
+ className: className ? `ndp-calendar ${className}` : "ndp-calendar",
43
+ "data-ndp-theme": theme,
44
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
45
+ className: "ndp-calendar__header",
46
+ children: [
47
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
48
+ className: "ndp-icon-button",
49
+ ...bindings.getPrevProps(),
50
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { name: "chevron-left" })
51
+ }),
52
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
53
+ className: "ndp-calendar__title",
54
+ "aria-live": "polite",
55
+ children: bindings.getTitleButtons().map((button) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
56
+ className: "ndp-calendar__title-button",
57
+ ...button.props,
58
+ children: button.label
59
+ }, button.key))
60
+ }),
61
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
62
+ className: "ndp-icon-button",
63
+ ...bindings.getNextProps(),
64
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { name: "chevron-right" })
65
+ })
66
+ ]
67
+ }), state.view === "day" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("table", {
68
+ className: "ndp-grid",
69
+ ...bindings.getGridProps(),
70
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tr", {
71
+ role: "row",
72
+ children: state.weekdays.map((weekday) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", {
73
+ className: "ndp-weekday",
74
+ scope: "col",
75
+ role: "columnheader",
76
+ abbr: weekday.longLabel,
77
+ "data-weekend": weekday.isWeekend || void 0,
78
+ children: weekday.label
79
+ }, weekday.index))
80
+ }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tbody", { children: state.weeks.map((week, row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tr", {
81
+ role: "row",
82
+ children: week.map((cell, column) => cell ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
83
+ role: "gridcell",
84
+ "aria-selected": cell.isSelected || void 0,
85
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
86
+ className: "ndp-day",
87
+ ...bindings.getDayProps(cell),
88
+ children: [
89
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
90
+ className: "ndp-sr-only",
91
+ children: cell.ariaLabel
92
+ }),
93
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
94
+ className: "ndp-day__label",
95
+ "aria-hidden": "true",
96
+ children: cell.label
97
+ }),
98
+ cell.subLabel !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
99
+ className: "ndp-day__sub",
100
+ "aria-hidden": "true",
101
+ children: cell.subLabel
102
+ }),
103
+ cell.events[0] && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
104
+ className: "ndp-day__dot",
105
+ "aria-hidden": "true",
106
+ style: cell.events[0].color ? { "--ndp-dot-color": cell.events[0].color } : void 0
107
+ })
108
+ ]
109
+ })
110
+ }, cell.key) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { role: "gridcell" }, `empty-${column}`))
111
+ }, row)) })]
112
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
113
+ className: "ndp-options",
114
+ role: "group",
115
+ "aria-label": state.title,
116
+ children: tiles.map((tile, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
117
+ className: "ndp-option",
118
+ ...bindings.getOptionProps(tile, index, tiles),
119
+ children: [
120
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
121
+ className: "ndp-sr-only",
122
+ children: tile.subLabel ? `${tile.label} (${tile.subLabel})` : tile.label
123
+ }),
124
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
125
+ className: "ndp-option__label",
126
+ "aria-hidden": "true",
127
+ children: tile.label
128
+ }),
129
+ tile.subLabel !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
130
+ className: "ndp-option__sub",
131
+ "aria-hidden": "true",
132
+ children: tile.subLabel
133
+ })
134
+ ]
135
+ }, tile.value))
136
+ })]
137
+ });
138
+ }
139
+ //#endregion
140
+ //#region src/calendar.tsx
141
+ /** A standalone BS/AD month view with keyboard support. */
142
+ function Calendar({ month, onMonthChange, className, theme, ...options }) {
143
+ const bindings = require_headless.useCalendar({
144
+ ...options,
145
+ visibleMonth: month,
146
+ onVisibleMonthChange: onMonthChange
147
+ });
148
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CalendarBody, {
149
+ bindings,
150
+ className,
151
+ theme
152
+ });
153
+ }
154
+ //#endregion
155
+ //#region src/panel.ts
156
+ /**
157
+ * While `open`: show the panel in the top layer beside the anchor, close on an
158
+ * outside pointer, a tap on a bottom sheet's backdrop, or Escape (returning focus to `returnFocus`), and move focus
159
+ * into the calendar.
160
+ */
161
+ function usePanel(open, refs, close, focusGrid, width = 320, layout = "auto") {
162
+ const closeRef = (0, react.useRef)(close);
163
+ closeRef.current = close;
164
+ require_headless.useIsomorphicLayoutEffect(() => {
165
+ const anchor = refs.anchor.current;
166
+ const panel = refs.panel.current;
167
+ if (!open || !anchor || !panel) return;
168
+ const stopTracking = (0, _nepdatejs_dom.trackPosition)(anchor, panel, {
169
+ width,
170
+ layout
171
+ });
172
+ const stopOutside = (0, _nepdatejs_dom.onOutsidePointer)(() => [refs.field.current, refs.panel.current], () => {
173
+ closeRef.current();
174
+ });
175
+ const stopBackdrop = (0, _nepdatejs_dom.onBackdropPointer)(panel, () => {
176
+ closeRef.current();
177
+ });
178
+ const stopEscape = (0, _nepdatejs_dom.onEscape)(() => {
179
+ closeRef.current();
180
+ (0, _nepdatejs_dom.restoreFocus)(refs.returnFocus.current);
181
+ });
182
+ return () => {
183
+ stopTracking();
184
+ stopOutside();
185
+ stopBackdrop();
186
+ stopEscape();
187
+ };
188
+ }, [
189
+ open,
190
+ width,
191
+ layout
192
+ ]);
193
+ (0, react.useEffect)(() => {
194
+ if (open) focusGrid();
195
+ }, [open]);
196
+ }
197
+ //#endregion
198
+ //#region src/presets.tsx
199
+ /** The panel body: the shortcut list (when there are presets), then the calendar and anything else. */
200
+ function PanelBody({ presets, label, onPreset, children }) {
201
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
202
+ className: "ndp-panel__body",
203
+ children: [presets.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
204
+ className: "ndp-presets",
205
+ role: "group",
206
+ "aria-label": label,
207
+ children: presets.map((preset) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
208
+ type: "button",
209
+ className: "ndp-preset",
210
+ "aria-pressed": preset.isActive,
211
+ disabled: preset.isDisabled,
212
+ onClick: () => {
213
+ onPreset(preset.id);
214
+ },
215
+ children: preset.label
216
+ }, preset.id))
217
+ }), children]
218
+ });
219
+ }
220
+ //#endregion
221
+ //#region src/date-picker.tsx
222
+ /**
223
+ * A BS/AD date field: type a date or pick it from a calendar. The value is
224
+ * always the AD day as "YYYY-MM-DD", so forms, validation and storage are
225
+ * unchanged and a BS date can never be sent as if it were AD.
226
+ */
227
+ function DatePicker(props) {
228
+ const { name, placeholder, required, disabled = false, clearable = true, showEquivalent = true, persistCalendar, panelLayout, theme, className, "aria-label": ariaLabel, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedBy, locale: localeProp, onCalendarChange, ...options } = props;
229
+ const locale = localeProp ?? pageLocale();
230
+ const picker = require_headless.useDatePicker({
231
+ ...options,
232
+ locale,
233
+ onCalendarChange: (calendar) => {
234
+ remember(persistCalendar, calendar);
235
+ onCalendarChange?.(calendar);
236
+ }
237
+ });
238
+ const { state, store, calendar } = picker;
239
+ const labels = (0, _nepdatejs_core.getLocale)(locale);
240
+ const field = (0, react.useRef)(null);
241
+ const box = (0, react.useRef)(null);
242
+ const panel = (0, react.useRef)(null);
243
+ const input = (0, react.useRef)(null);
244
+ const hidden = (0, react.useRef)(null);
245
+ const panelWidth = state.presets.length > 0 ? 492 : 320;
246
+ usePanel(state.open && !state.inline, {
247
+ anchor: box,
248
+ panel,
249
+ field,
250
+ returnFocus: input
251
+ }, () => {
252
+ store.actions.close();
253
+ }, calendar.focusGrid, panelWidth, panelLayout);
254
+ (0, react.useEffect)(() => {
255
+ const stored = read(persistCalendar);
256
+ if (stored) store.actions.switchCalendar(stored);
257
+ }, [persistCalendar, store]);
258
+ (0, react.useEffect)(() => {
259
+ const form = hidden.current?.form ?? input.current?.form;
260
+ if (!form) return;
261
+ const reset = () => {
262
+ store.actions.reset();
263
+ };
264
+ form.addEventListener("reset", reset);
265
+ return () => {
266
+ form.removeEventListener("reset", reset);
267
+ };
268
+ }, [store]);
269
+ (0, react.useEffect)(() => {
270
+ input.current?.setCustomValidity(state.error?.message ?? "");
271
+ }, [state.error]);
272
+ const hint = state.error?.message ?? (showEquivalent ? state.equivalentText : null);
273
+ const inputProps = picker.getInputProps();
274
+ const describedBy = [ariaDescribedBy, hint ? picker.ids.hint : void 0].filter(Boolean).join(" ") || void 0;
275
+ const invalid = ariaInvalid === true || state.error !== null;
276
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
277
+ ref: field,
278
+ className: className ? `ndp-field ${className}` : "ndp-field",
279
+ "data-ndp-theme": theme,
280
+ children: [
281
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
282
+ ref: box,
283
+ className: "ndp-input",
284
+ "data-invalid": invalid || void 0,
285
+ "data-disabled": disabled || void 0,
286
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
287
+ ref: input,
288
+ className: "ndp-input__control",
289
+ ...inputProps,
290
+ "aria-describedby": describedBy,
291
+ "aria-invalid": invalid || void 0,
292
+ "aria-label": ariaLabel,
293
+ placeholder: placeholder ?? state.placeholder,
294
+ required,
295
+ disabled
296
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
297
+ className: "ndp-input__actions",
298
+ children: [
299
+ clearable && state.text !== "" && !disabled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
300
+ className: "ndp-icon-button",
301
+ ...picker.getClearProps(),
302
+ onClick: () => {
303
+ store.actions.clear();
304
+ input.current?.focus();
305
+ },
306
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { name: "close" })
307
+ }),
308
+ state.canSwitchCalendar && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
309
+ className: "ndp-input__switch",
310
+ disabled,
311
+ ...picker.getSwitchProps(),
312
+ children: labels.calendarNames[state.calendar]
313
+ }),
314
+ !state.inline && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
315
+ className: "ndp-icon-button",
316
+ disabled,
317
+ ...picker.getToggleProps(),
318
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { name: "calendar" })
319
+ })
320
+ ]
321
+ })]
322
+ }),
323
+ hint !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
324
+ id: picker.ids.hint,
325
+ className: "ndp-hint",
326
+ "data-error": state.error ? true : void 0,
327
+ "aria-live": "polite",
328
+ children: hint
329
+ }),
330
+ name !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
331
+ ref: hidden,
332
+ type: "hidden",
333
+ name,
334
+ value: state.value,
335
+ disabled
336
+ }),
337
+ state.open && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
338
+ ref: panel,
339
+ className: "ndp-panel",
340
+ popover: state.inline ? void 0 : "manual",
341
+ "data-layout": state.inline ? "inline" : void 0,
342
+ style: state.inline ? { "--ndp-panel-width": `${String(panelWidth)}px` } : void 0,
343
+ "data-ndp-theme": theme,
344
+ ...picker.getPanelProps(),
345
+ "aria-label": ariaLabel ? `${ariaLabel}: ${labels.labels.calendar}` : labels.labels.chooseDate,
346
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PanelBody, {
347
+ presets: state.presets,
348
+ label: labels.labels.presets,
349
+ onPreset: (id) => {
350
+ if (store.actions.applyPreset(id) && !store.getState().open) (0, _nepdatejs_dom.restoreFocus)(input.current);
351
+ },
352
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CalendarBody, { bindings: {
353
+ ...calendar,
354
+ getDayProps: (cell) => ({
355
+ ...calendar.getDayProps(cell),
356
+ onClick: () => {
357
+ if (!cell.isDisabled && store.actions.select(cell.date) && !store.getState().open) (0, _nepdatejs_dom.restoreFocus)(input.current);
358
+ }
359
+ })
360
+ } })
361
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
362
+ className: "ndp-panel__footer",
363
+ children: [state.canSwitchCalendar ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
364
+ className: "ndp-segment",
365
+ role: "group",
366
+ "aria-label": labels.labels.calendar,
367
+ children: ["BS", "AD"].map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
368
+ className: "ndp-segment__item",
369
+ ...picker.getCalendarOptionProps(option),
370
+ children: labels.calendarNames[option]
371
+ }, option))
372
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
373
+ className: "ndp-button",
374
+ ...picker.getTodayProps(),
375
+ onClick: () => {
376
+ if (store.actions.selectToday()) (0, _nepdatejs_dom.restoreFocus)(input.current);
377
+ },
378
+ children: labels.labels.today
379
+ })]
380
+ })]
381
+ })
382
+ ]
383
+ });
384
+ }
385
+ /** Nepali names and digits when the page language is Nepali. */
386
+ function pageLocale() {
387
+ return typeof document !== "undefined" && document.documentElement.lang.toLowerCase().startsWith("ne") ? "ne" : "en";
388
+ }
389
+ function read(key) {
390
+ if (!key) return null;
391
+ try {
392
+ const stored = window.localStorage.getItem(key);
393
+ return stored === "AD" || stored === "BS" ? stored : null;
394
+ } catch {
395
+ return null;
396
+ }
397
+ }
398
+ function remember(key, calendar) {
399
+ if (!key) return;
400
+ try {
401
+ window.localStorage.setItem(key, calendar);
402
+ } catch {}
403
+ }
404
+ //#endregion
405
+ //#region src/date-range-picker.tsx
406
+ /** Two BS/AD date fields sharing one calendar panel in range mode. */
407
+ function DateRangePicker({ startName, endName, startLabel, endLabel, fiscalYearView = false, disabled = false, panelLayout, theme, className, ...options }) {
408
+ const picker = require_headless.useDateRangePicker(options);
409
+ const { state, store, calendar } = picker;
410
+ const labels = (0, _nepdatejs_core.getLocale)(options.locale);
411
+ const field = (0, react.useRef)(null);
412
+ const row = (0, react.useRef)(null);
413
+ const panel = (0, react.useRef)(null);
414
+ const start = (0, react.useRef)(null);
415
+ const panelWidth = state.presets.length > 0 ? 492 : 320;
416
+ usePanel(state.open && !state.inline, {
417
+ anchor: row,
418
+ panel,
419
+ field,
420
+ returnFocus: start
421
+ }, () => {
422
+ store.actions.close();
423
+ }, calendar.focusGrid, panelWidth, panelLayout);
424
+ const invalid = (which) => state.error !== null && (state.error.field === which || state.error.field === "range");
425
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
426
+ ref: field,
427
+ className: className ? `ndp-field ${className}` : "ndp-field",
428
+ "data-ndp-theme": theme,
429
+ children: [
430
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
431
+ ref: row,
432
+ className: "ndp-range",
433
+ children: [
434
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
435
+ className: "ndp-input",
436
+ "data-invalid": invalid("start") || void 0,
437
+ "data-disabled": disabled || void 0,
438
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
439
+ ref: start,
440
+ className: "ndp-input__control",
441
+ ...picker.getInputProps("start"),
442
+ "aria-label": startLabel,
443
+ disabled
444
+ })
445
+ }),
446
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
447
+ className: "ndp-range__separator",
448
+ "aria-hidden": "true",
449
+ children: "→"
450
+ }),
451
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
452
+ className: "ndp-input",
453
+ "data-invalid": invalid("end") || void 0,
454
+ "data-disabled": disabled || void 0,
455
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
456
+ className: "ndp-input__control",
457
+ ...picker.getInputProps("end"),
458
+ "aria-label": endLabel,
459
+ disabled
460
+ }), !state.inline && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
461
+ className: "ndp-input__actions",
462
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
463
+ className: "ndp-icon-button",
464
+ disabled,
465
+ ...picker.getToggleProps(),
466
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { name: "calendar" })
467
+ })
468
+ })]
469
+ })
470
+ ]
471
+ }),
472
+ state.error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
473
+ id: picker.ids.hint,
474
+ className: "ndp-hint",
475
+ "data-error": true,
476
+ "aria-live": "polite",
477
+ children: state.error.message
478
+ }),
479
+ startName !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
480
+ type: "hidden",
481
+ name: startName,
482
+ value: state.value.start,
483
+ disabled
484
+ }),
485
+ endName !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
486
+ type: "hidden",
487
+ name: endName,
488
+ value: state.value.end,
489
+ disabled
490
+ }),
491
+ state.open && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
492
+ ref: panel,
493
+ className: "ndp-panel",
494
+ popover: state.inline ? void 0 : "manual",
495
+ "data-layout": state.inline ? "inline" : void 0,
496
+ style: state.inline ? { "--ndp-panel-width": `${String(panelWidth)}px` } : void 0,
497
+ "data-ndp-theme": theme,
498
+ ...picker.getPanelProps(),
499
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PanelBody, {
500
+ presets: state.presets,
501
+ label: labels.labels.presets,
502
+ onPreset: (id) => {
503
+ if (store.actions.applyPreset(id) && !store.getState().open) (0, _nepdatejs_dom.restoreFocus)(start.current);
504
+ },
505
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CalendarBody, { bindings: calendar })
506
+ }), (state.canSwitchCalendar || fiscalYearView) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
507
+ className: "ndp-panel__footer",
508
+ children: [state.canSwitchCalendar ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
509
+ className: "ndp-segment",
510
+ role: "group",
511
+ "aria-label": labels.labels.calendar,
512
+ children: ["BS", "AD"].map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
513
+ className: "ndp-segment__item",
514
+ ...picker.getCalendarOptionProps(option),
515
+ children: labels.calendarNames[option]
516
+ }, option))
517
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {}), fiscalYearView && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
518
+ type: "button",
519
+ className: "ndp-button",
520
+ "aria-pressed": state.calendarState.view === "fiscal",
521
+ onClick: () => {
522
+ store.calendar.actions.setView(state.calendarState.view === "fiscal" ? "day" : "fiscal");
523
+ calendar.focusGrid();
524
+ },
525
+ children: labels.labels.fiscalYear
526
+ })]
527
+ })]
528
+ })
529
+ ]
530
+ });
531
+ }
532
+ //#endregion
533
+ //#region src/time-columns.tsx
534
+ /** Hour and minute columns (Figma "Time Item"); the selected items scroll into view. */
535
+ function TimeColumns({ hours, minutes, locale, onHour, onMinute }) {
536
+ const root = (0, react.useRef)(null);
537
+ const labels = (0, _nepdatejs_core.getLocale)(locale).labels;
538
+ (0, react.useEffect)(() => {
539
+ for (const item of root.current?.querySelectorAll("[aria-pressed=\"true\"]") ?? []) if (typeof item.scrollIntoView === "function") item.scrollIntoView({ block: "nearest" });
540
+ }, []);
541
+ const column = (label, cells, pick) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
542
+ className: "ndp-time__column",
543
+ role: "group",
544
+ "aria-label": label,
545
+ children: cells.map((cell) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
546
+ type: "button",
547
+ className: "ndp-time-item",
548
+ "aria-pressed": cell.isSelected,
549
+ onClick: () => {
550
+ pick(cell.value);
551
+ },
552
+ children: cell.label
553
+ }, cell.value))
554
+ });
555
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
556
+ ref: root,
557
+ className: "ndp-time",
558
+ role: "group",
559
+ "aria-label": labels.time,
560
+ children: [column(labels.hour, hours, onHour), column(labels.minute, minutes, onMinute)]
561
+ });
562
+ }
563
+ //#endregion
564
+ //#region src/date-time-picker.tsx
565
+ /** A BS/AD date and time field. The value is one instant, with the zone's offset. */
566
+ function DateTimePicker({ name, required, disabled = false, panelLayout, theme, className, "aria-label": ariaLabel, timeLabel, ...options }) {
567
+ const { state, store, calendar, ids } = require_headless.useDateTimePicker(options);
568
+ const labels = (0, _nepdatejs_core.getLocale)(options.locale);
569
+ const field = (0, react.useRef)(null);
570
+ const row = (0, react.useRef)(null);
571
+ const panel = (0, react.useRef)(null);
572
+ const dateInput = (0, react.useRef)(null);
573
+ const panelWidth = (state.presets.length > 0 ? 492 : 320) + 136;
574
+ usePanel(state.open && !state.inline, {
575
+ anchor: row,
576
+ panel,
577
+ field,
578
+ returnFocus: dateInput
579
+ }, () => {
580
+ store.actions.close();
581
+ }, calendar.focusGrid, panelWidth, panelLayout);
582
+ const hint = state.error?.message ?? state.equivalentText;
583
+ const invalid = (which) => state.error?.field === which;
584
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
585
+ ref: field,
586
+ className: className ? `ndp-field ${className}` : "ndp-field",
587
+ "data-ndp-theme": theme,
588
+ children: [
589
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
590
+ ref: row,
591
+ className: "ndp-datetime",
592
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
593
+ className: "ndp-input",
594
+ "data-invalid": invalid("date") || void 0,
595
+ "data-disabled": disabled || void 0,
596
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
597
+ ref: dateInput,
598
+ id: ids.date,
599
+ className: "ndp-input__control",
600
+ ...require_headless.popupProps(state),
601
+ type: "text",
602
+ inputMode: "numeric",
603
+ autoComplete: "off",
604
+ value: state.dateText,
605
+ placeholder: state.placeholder,
606
+ "aria-label": ariaLabel,
607
+ "aria-controls": ids.panel,
608
+ "aria-invalid": invalid("date") || void 0,
609
+ "aria-describedby": hint ? ids.hint : void 0,
610
+ required,
611
+ disabled,
612
+ onChange: (event) => {
613
+ store.actions.typeDate(event.target.value);
614
+ },
615
+ onBlur: () => {
616
+ store.actions.blurDate();
617
+ },
618
+ onKeyDown: (event) => {
619
+ if (event.key === "ArrowDown" && (event.altKey || state.dateText === "")) {
620
+ event.preventDefault();
621
+ store.actions.open();
622
+ }
623
+ }
624
+ }), !state.inline && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
625
+ className: "ndp-input__actions",
626
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
627
+ type: "button",
628
+ className: "ndp-icon-button",
629
+ disabled,
630
+ "aria-label": labels.labels.openCalendar,
631
+ "aria-expanded": state.open,
632
+ "aria-controls": ids.panel,
633
+ onClick: () => {
634
+ store.actions.toggle();
635
+ },
636
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { name: "calendar" })
637
+ })
638
+ })]
639
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
640
+ className: "ndp-input ndp-input--time",
641
+ "data-invalid": invalid("time") || void 0,
642
+ "data-disabled": disabled || void 0,
643
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
644
+ id: ids.time,
645
+ className: "ndp-input__control",
646
+ type: "text",
647
+ inputMode: "text",
648
+ autoComplete: "off",
649
+ value: state.timeText,
650
+ placeholder: state.timePlaceholder,
651
+ "aria-label": timeLabel ?? labels.labels.time,
652
+ "aria-invalid": invalid("time") || void 0,
653
+ "aria-describedby": hint ? ids.hint : void 0,
654
+ disabled,
655
+ onChange: (event) => {
656
+ store.actions.typeTime(event.target.value);
657
+ },
658
+ onBlur: () => {
659
+ store.actions.blurTime();
660
+ }
661
+ })
662
+ })]
663
+ }),
664
+ hint !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
665
+ id: ids.hint,
666
+ className: "ndp-hint",
667
+ "data-error": state.error ? true : void 0,
668
+ "aria-live": "polite",
669
+ children: hint
670
+ }),
671
+ name !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
672
+ type: "hidden",
673
+ name,
674
+ value: state.value,
675
+ disabled
676
+ }),
677
+ state.open && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
678
+ ref: panel,
679
+ id: ids.panel,
680
+ className: "ndp-panel",
681
+ popover: state.inline ? void 0 : "manual",
682
+ "data-layout": state.inline ? "inline" : void 0,
683
+ style: state.inline ? { "--ndp-panel-width": `${String(panelWidth)}px` } : void 0,
684
+ role: state.inline ? "group" : "dialog",
685
+ "aria-label": labels.labels.chooseDate,
686
+ "data-ndp-theme": theme,
687
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(PanelBody, {
688
+ presets: state.presets,
689
+ label: labels.labels.presets,
690
+ onPreset: (presetId) => {
691
+ store.actions.applyPreset(presetId);
692
+ },
693
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(CalendarBody, { bindings: calendar }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TimeColumns, {
694
+ hours: state.hours,
695
+ minutes: state.minutes,
696
+ locale: options.locale,
697
+ onHour: (hour) => {
698
+ store.actions.selectHour(hour);
699
+ },
700
+ onMinute: (minute) => {
701
+ store.actions.selectMinute(minute);
702
+ }
703
+ })]
704
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
705
+ className: "ndp-panel__footer",
706
+ children: [state.canSwitchCalendar ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
707
+ className: "ndp-segment",
708
+ role: "group",
709
+ "aria-label": labels.labels.calendar,
710
+ children: ["BS", "AD"].map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
711
+ type: "button",
712
+ className: "ndp-segment__item",
713
+ "aria-pressed": state.calendar === option,
714
+ onClick: () => {
715
+ store.actions.switchCalendar(option);
716
+ },
717
+ children: labels.calendarNames[option]
718
+ }, option))
719
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
720
+ type: "button",
721
+ className: "ndp-button",
722
+ onClick: () => {
723
+ store.actions.selectNow();
724
+ },
725
+ children: labels.labels.now
726
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
727
+ type: "button",
728
+ className: "ndp-button",
729
+ "data-variant": "primary",
730
+ onClick: () => {
731
+ store.actions.close();
732
+ dateInput.current?.focus();
733
+ },
734
+ children: labels.labels.done
735
+ })] })]
736
+ })]
737
+ })
738
+ ]
739
+ });
740
+ }
741
+ //#endregion
742
+ //#region src/multi-date-picker.tsx
743
+ /**
744
+ * Several separate BS/AD days in one field (PICK-001): click days in the
745
+ * panel to add or remove them, or type them separated by commas. The value is
746
+ * the sorted AD days as "YYYY-MM-DD" strings.
747
+ */
748
+ function MultiDatePicker({ name, placeholder, required, disabled = false, clearable = true, panelLayout, theme, className, "aria-label": ariaLabel, ...options }) {
749
+ const picker = require_headless.useMultiDatePicker(options);
750
+ const { state, store, calendar } = picker;
751
+ const labels = (0, _nepdatejs_core.getLocale)(options.locale);
752
+ const field = (0, react.useRef)(null);
753
+ const box = (0, react.useRef)(null);
754
+ const panel = (0, react.useRef)(null);
755
+ const input = (0, react.useRef)(null);
756
+ usePanel(state.open && !state.inline, {
757
+ anchor: box,
758
+ panel,
759
+ field,
760
+ returnFocus: input
761
+ }, () => {
762
+ store.actions.close();
763
+ }, calendar.focusGrid, 320, panelLayout);
764
+ (0, react.useEffect)(() => {
765
+ const form = input.current?.form;
766
+ if (!form) return;
767
+ const reset = () => {
768
+ store.actions.reset();
769
+ };
770
+ form.addEventListener("reset", reset);
771
+ return () => {
772
+ form.removeEventListener("reset", reset);
773
+ };
774
+ }, [store]);
775
+ (0, react.useEffect)(() => {
776
+ input.current?.setCustomValidity(state.error?.message ?? "");
777
+ }, [state.error]);
778
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
779
+ ref: field,
780
+ className: className ? `ndp-field ${className}` : "ndp-field",
781
+ "data-ndp-theme": theme,
782
+ children: [
783
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
784
+ ref: box,
785
+ className: "ndp-input",
786
+ "data-invalid": state.error !== null || void 0,
787
+ "data-disabled": disabled || void 0,
788
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
789
+ ref: input,
790
+ className: "ndp-input__control",
791
+ ...picker.getInputProps(),
792
+ "aria-label": ariaLabel,
793
+ placeholder: placeholder ?? state.placeholder,
794
+ required,
795
+ disabled
796
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
797
+ className: "ndp-input__actions",
798
+ children: [clearable && state.text !== "" && !disabled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
799
+ className: "ndp-icon-button",
800
+ ...picker.getClearProps(),
801
+ onClick: () => {
802
+ store.actions.clear();
803
+ input.current?.focus();
804
+ },
805
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { name: "close" })
806
+ }), !state.inline && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
807
+ className: "ndp-icon-button",
808
+ disabled,
809
+ ...picker.getToggleProps(),
810
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { name: "calendar" })
811
+ })]
812
+ })]
813
+ }),
814
+ state.error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
815
+ id: picker.ids.hint,
816
+ className: "ndp-hint",
817
+ "data-error": true,
818
+ "aria-live": "polite",
819
+ children: state.error.message
820
+ }),
821
+ name !== void 0 && state.value.map((day) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
822
+ type: "hidden",
823
+ name,
824
+ value: day,
825
+ disabled
826
+ }, day)),
827
+ state.open && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
828
+ ref: panel,
829
+ className: "ndp-panel",
830
+ popover: state.inline ? void 0 : "manual",
831
+ "data-layout": state.inline ? "inline" : void 0,
832
+ style: state.inline ? { "--ndp-panel-width": `${String(320)}px` } : void 0,
833
+ "data-ndp-theme": theme,
834
+ ...picker.getPanelProps(),
835
+ "aria-label": ariaLabel ? `${ariaLabel}: ${labels.labels.calendar}` : labels.labels.chooseDate,
836
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(CalendarBody, { bindings: {
837
+ ...calendar,
838
+ getDayProps: (cell) => ({
839
+ ...calendar.getDayProps(cell),
840
+ onClick: () => {
841
+ if (!cell.isDisabled && store.actions.select(cell.date) && !store.getState().open) (0, _nepdatejs_dom.restoreFocus)(input.current);
842
+ }
843
+ })
844
+ } }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
845
+ className: "ndp-panel__footer",
846
+ children: [state.canSwitchCalendar ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
847
+ className: "ndp-segment",
848
+ role: "group",
849
+ "aria-label": labels.labels.calendar,
850
+ children: ["BS", "AD"].map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
851
+ className: "ndp-segment__item",
852
+ ...picker.getCalendarOptionProps(option),
853
+ children: labels.calendarNames[option]
854
+ }, option))
855
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {}), !state.inline && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
856
+ type: "button",
857
+ className: "ndp-button",
858
+ onClick: () => {
859
+ store.actions.close();
860
+ (0, _nepdatejs_dom.restoreFocus)(input.current);
861
+ },
862
+ children: labels.labels.done
863
+ })]
864
+ })]
865
+ })
866
+ ]
867
+ });
868
+ }
869
+ //#endregion
870
+ exports.Calendar = Calendar;
871
+ exports.CalendarBody = CalendarBody;
872
+ exports.DatePicker = DatePicker;
873
+ exports.DateRangePicker = DateRangePicker;
874
+ exports.DateText = require_date_text.DateText;
875
+ exports.DateTimePicker = DateTimePicker;
876
+ exports.Icon = Icon;
877
+ exports.MultiDatePicker = MultiDatePicker;
878
+ exports.ShortDate = require_date_text.ShortDate;
879
+ exports.TimeColumns = TimeColumns;
880
+ exports.bothDates = require_date_text.bothDates;
881
+ exports.deepEqual = require_headless.deepEqual;
882
+ exports.shortDate = require_date_text.shortDate;
883
+ exports.useCalendar = require_headless.useCalendar;
884
+ exports.useCalendarBindings = require_headless.useCalendarBindings;
885
+ exports.useDatePicker = require_headless.useDatePicker;
886
+ exports.useDateRangePicker = require_headless.useDateRangePicker;
887
+ exports.useDateTimePicker = require_headless.useDateTimePicker;
888
+ exports.useMultiDatePicker = require_headless.useMultiDatePicker;
889
+ exports.useStore = require_headless.useStore;
890
+
891
+ //# sourceMappingURL=index.cjs.map