@fangxinyan/lumina 0.6.14 → 0.6.16

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 (41) hide show
  1. package/dist/{Calendar-RUTPKREW.css → Calendar-I6NHCXIB.css} +65 -0
  2. package/dist/Calendar.cjs +2 -2
  3. package/dist/Calendar.d.cts +2 -0
  4. package/dist/Calendar.d.ts +2 -0
  5. package/dist/Calendar.js +1 -1
  6. package/dist/DatePicker-VDVSIARF.css +60 -0
  7. package/dist/DatePicker.cjs +16 -0
  8. package/dist/DatePicker.d.cts +49 -0
  9. package/dist/DatePicker.d.ts +49 -0
  10. package/dist/DatePicker.js +7 -0
  11. package/dist/DateTimePicker-DRX7XA74.css +224 -0
  12. package/dist/DateTimePicker.cjs +16 -0
  13. package/dist/DateTimePicker.d.cts +61 -0
  14. package/dist/DateTimePicker.d.ts +61 -0
  15. package/dist/DateTimePicker.js +7 -0
  16. package/dist/TimePicker-ZBJOXH3U.css +151 -0
  17. package/dist/TimePicker.cjs +15 -0
  18. package/dist/TimePicker.d.cts +63 -0
  19. package/dist/TimePicker.d.ts +63 -0
  20. package/dist/TimePicker.js +6 -0
  21. package/dist/chunk-32FWB5NO.cjs +505 -0
  22. package/dist/chunk-4KDABJSY.cjs +241 -0
  23. package/dist/chunk-DU4VSUBF.js +393 -0
  24. package/dist/chunk-FKP5D5IS.js +271 -0
  25. package/dist/chunk-I7TOBKJO.js +219 -0
  26. package/dist/chunk-KHRYPNUV.cjs +293 -0
  27. package/dist/chunk-SFEWMO45.cjs +415 -0
  28. package/dist/chunk-YPE6CDXQ.js +483 -0
  29. package/dist/index.cjs +66 -51
  30. package/dist/index.d.cts +4 -1
  31. package/dist/index.d.ts +4 -1
  32. package/dist/index.js +15 -12
  33. package/dist/styles.css +439 -0
  34. package/docs/llms/calendar.md +2 -1
  35. package/docs/llms/datepicker.md +69 -0
  36. package/docs/llms/datetimepicker.md +83 -0
  37. package/docs/llms/timepicker.md +81 -0
  38. package/docs/llms.md +5 -2
  39. package/package.json +7 -3
  40. package/dist/chunk-XG5D777V.js +0 -90
  41. package/dist/chunk-ZBTGYTQE.cjs +0 -112
@@ -0,0 +1,271 @@
1
+ import { useFloating } from './chunk-DUFRASGO.js';
2
+ import { Input } from './chunk-IOAOG5L7.js';
3
+ import { Button } from './chunk-IPSNC5VZ.js';
4
+ import { Calendar } from './chunk-I7TOBKJO.js';
5
+ import './tokens-RYJ3DFTU.css';
6
+ import './shared-NJHRNLRH.css';
7
+ import './DatePicker-VDVSIARF.css';
8
+ import * as React from 'react';
9
+ import { createPortal } from 'react-dom';
10
+ import { jsxs, jsx } from 'react/jsx-runtime';
11
+
12
+ var pad = (value) => String(value).padStart(2, "0");
13
+ var startOfDay = (date) => new Date(date.getFullYear(), date.getMonth(), date.getDate());
14
+ var isValidDate = (date) => date instanceof Date && !Number.isNaN(date.getTime());
15
+ var formatDate = (date, format) => {
16
+ if (!isValidDate(date)) return "";
17
+ if (typeof format === "function") return format(date);
18
+ const year = String(date.getFullYear());
19
+ const month = pad(date.getMonth() + 1);
20
+ const day = pad(date.getDate());
21
+ if (format === "YYYY/MM/DD") return `${year}/${month}/${day}`;
22
+ if (format === "YYYY\u5E74MM\u6708DD\u65E5") return `${year}\u5E74${month}\u6708${day}\u65E5`;
23
+ return `${year}-${month}-${day}`;
24
+ };
25
+ var parseDateInput = (input) => {
26
+ const text = input.trim();
27
+ if (!text) return null;
28
+ const match = text.match(/^(\d{4})[-/.年](\d{1,2})[-/.月](\d{1,2})日?$/);
29
+ if (!match) return null;
30
+ const year = Number(match[1]);
31
+ const month = Number(match[2]);
32
+ const day = Number(match[3]);
33
+ const date = new Date(year, month - 1, day);
34
+ if (date.getFullYear() !== year || date.getMonth() !== month - 1 || date.getDate() !== day) {
35
+ return null;
36
+ }
37
+ return date;
38
+ };
39
+ var DatePicker = React.forwardRef(
40
+ ({
41
+ value,
42
+ defaultValue = null,
43
+ onChange,
44
+ format = "YYYY-MM-DD",
45
+ placeholder = "\u8BF7\u9009\u62E9\u65E5\u671F",
46
+ disabled,
47
+ readOnly,
48
+ invalid,
49
+ size = "md",
50
+ allowClear,
51
+ min,
52
+ max,
53
+ disabledDate,
54
+ placement = "bottom",
55
+ open: openProp,
56
+ defaultOpen = false,
57
+ onOpenChange,
58
+ popupClassName = "",
59
+ dropdownClassName = "",
60
+ className = "",
61
+ onKeyDown,
62
+ ...rest
63
+ }, ref) => {
64
+ const controlled = value !== void 0;
65
+ const [inner, setInner] = React.useState(
66
+ isValidDate(defaultValue) ? startOfDay(defaultValue) : null
67
+ );
68
+ const current = controlled ? isValidDate(value) ? startOfDay(value) : null : inner;
69
+ const currentTime = current?.getTime() ?? null;
70
+ const formattedCurrent = React.useMemo(
71
+ () => currentTime == null ? "" : formatDate(new Date(currentTime), format),
72
+ [currentTime, format]
73
+ );
74
+ const [draft, setDraft] = React.useState(formattedCurrent);
75
+ React.useEffect(() => {
76
+ setDraft(formattedCurrent);
77
+ }, [formattedCurrent]);
78
+ const openControlled = openProp !== void 0;
79
+ const [innerOpen, setInnerOpen] = React.useState(defaultOpen);
80
+ const open = openControlled ? !!openProp : innerOpen;
81
+ const setOpen = React.useCallback(
82
+ (next) => {
83
+ if (disabled || readOnly) return;
84
+ if (!openControlled) setInnerOpen(next);
85
+ onOpenChange?.(next);
86
+ },
87
+ [disabled, onOpenChange, openControlled, readOnly]
88
+ );
89
+ const rootRef = React.useRef(null);
90
+ const panelRef = React.useRef(null);
91
+ const { triggerRef, floatingStyle } = useFloating({
92
+ open,
93
+ placement,
94
+ panelWidth: 344,
95
+ panelHeight: 404
96
+ });
97
+ const setRootRef = React.useCallback(
98
+ (node) => {
99
+ rootRef.current = node;
100
+ triggerRef.current = node;
101
+ if (typeof ref === "function") ref(node);
102
+ else if (ref) ref.current = node;
103
+ },
104
+ [ref, triggerRef]
105
+ );
106
+ const normalizedMin = React.useMemo(() => isValidDate(min) ? startOfDay(min) : void 0, [min]);
107
+ const normalizedMax = React.useMemo(() => isValidDate(max) ? startOfDay(max) : void 0, [max]);
108
+ const isDisabledDate = React.useCallback(
109
+ (date) => {
110
+ const day = startOfDay(date);
111
+ if (normalizedMin && day < normalizedMin) return true;
112
+ if (normalizedMax && day > normalizedMax) return true;
113
+ return disabledDate?.(day) ?? false;
114
+ },
115
+ [disabledDate, normalizedMax, normalizedMin]
116
+ );
117
+ const commit = React.useCallback(
118
+ (next, close = false) => {
119
+ if (!next) {
120
+ if (!controlled) setInner(null);
121
+ setDraft("");
122
+ onChange?.(null, "");
123
+ if (close) setOpen(false);
124
+ return;
125
+ }
126
+ const normalized = startOfDay(next);
127
+ if (isDisabledDate(normalized)) return;
128
+ if (!controlled) setInner(normalized);
129
+ const nextString = formatDate(normalized, format);
130
+ setDraft(nextString);
131
+ onChange?.(normalized, nextString);
132
+ if (close) setOpen(false);
133
+ },
134
+ [controlled, format, isDisabledDate, onChange, setOpen]
135
+ );
136
+ React.useEffect(() => {
137
+ if (!open) return;
138
+ const onDown = (event) => {
139
+ const target = event.target;
140
+ if (rootRef.current?.contains(target)) return;
141
+ if (panelRef.current?.contains(target)) return;
142
+ setOpen(false);
143
+ };
144
+ const onDocKey = (event) => {
145
+ if (event.key === "Escape") setOpen(false);
146
+ };
147
+ document.addEventListener("mousedown", onDown);
148
+ document.addEventListener("keydown", onDocKey);
149
+ return () => {
150
+ document.removeEventListener("mousedown", onDown);
151
+ document.removeEventListener("keydown", onDocKey);
152
+ };
153
+ }, [open, setOpen]);
154
+ const handleInputChange = (next) => {
155
+ setDraft(next);
156
+ if (next.trim() === "") commit(null);
157
+ };
158
+ const handleInputBlur = () => {
159
+ if (!draft.trim()) {
160
+ setDraft(formattedCurrent);
161
+ return;
162
+ }
163
+ if (typeof format === "function") {
164
+ setDraft(formattedCurrent);
165
+ return;
166
+ }
167
+ const parsed = parseDateInput(draft);
168
+ if (!parsed || isDisabledDate(parsed)) {
169
+ setDraft(formattedCurrent);
170
+ return;
171
+ }
172
+ commit(parsed);
173
+ };
174
+ const handleRootKeyDown = (event) => {
175
+ onKeyDown?.(event);
176
+ if (event.defaultPrevented) return;
177
+ if ((event.key === "ArrowDown" || event.key === "Enter") && !open) {
178
+ event.preventDefault();
179
+ setOpen(true);
180
+ }
181
+ if (event.key === "Escape" && open) {
182
+ event.preventDefault();
183
+ setOpen(false);
184
+ }
185
+ };
186
+ const today = startOfDay(/* @__PURE__ */ new Date());
187
+ const calendarValue = current ?? today;
188
+ const mergedPanelClassName = [popupClassName, dropdownClassName].filter(Boolean).join(" ");
189
+ const rootClassName = [
190
+ "date-picker",
191
+ size,
192
+ open && "open",
193
+ disabled && "disabled",
194
+ readOnly && "readonly",
195
+ invalid && "invalid",
196
+ className
197
+ ].filter(Boolean).join(" ");
198
+ return /* @__PURE__ */ jsxs("div", { ref: setRootRef, className: rootClassName, onKeyDown: handleRootKeyDown, ...rest, children: [
199
+ /* @__PURE__ */ jsx(
200
+ Input,
201
+ {
202
+ value: draft,
203
+ size,
204
+ placeholder,
205
+ disabled,
206
+ readOnly,
207
+ invalid,
208
+ allowClear,
209
+ leadingIcon: "calendar",
210
+ onFocus: () => setOpen(true),
211
+ onClick: () => setOpen(true),
212
+ onValueChange: handleInputChange,
213
+ onBlur: handleInputBlur,
214
+ inputMode: "numeric",
215
+ "aria-expanded": open,
216
+ "aria-haspopup": "dialog"
217
+ }
218
+ ),
219
+ open && !disabled && !readOnly && typeof document !== "undefined" && createPortal(
220
+ /* @__PURE__ */ jsxs(
221
+ "div",
222
+ {
223
+ ref: panelRef,
224
+ className: `date-picker-panel ${mergedPanelClassName}`,
225
+ role: "dialog",
226
+ style: floatingStyle,
227
+ children: [
228
+ /* @__PURE__ */ jsx(
229
+ Calendar,
230
+ {
231
+ value: calendarValue,
232
+ min: normalizedMin,
233
+ max: normalizedMax,
234
+ disabledDate: isDisabledDate,
235
+ onChange: (next) => commit(next, true)
236
+ }
237
+ ),
238
+ /* @__PURE__ */ jsxs("div", { className: "date-picker-footer", children: [
239
+ /* @__PURE__ */ jsx(
240
+ Button,
241
+ {
242
+ size: "sm",
243
+ variant: "ghost",
244
+ disabled: isDisabledDate(today),
245
+ onClick: () => commit(today, true),
246
+ children: "\u4ECA\u5929"
247
+ }
248
+ ),
249
+ /* @__PURE__ */ jsx("span", { className: "date-picker-footer-spacer" }),
250
+ allowClear && /* @__PURE__ */ jsx(
251
+ Button,
252
+ {
253
+ size: "sm",
254
+ variant: "ghost",
255
+ onClick: () => commit(null, true),
256
+ children: "\u6E05\u7A7A"
257
+ }
258
+ ),
259
+ /* @__PURE__ */ jsx(Button, { size: "sm", variant: "primary", onClick: () => setOpen(false), children: "\u786E\u5B9A" })
260
+ ] })
261
+ ]
262
+ }
263
+ ),
264
+ document.body
265
+ )
266
+ ] });
267
+ }
268
+ );
269
+ DatePicker.displayName = "DatePicker";
270
+
271
+ export { DatePicker };
@@ -0,0 +1,219 @@
1
+ import { Icon } from './chunk-FDZG27IG.js';
2
+ import './tokens-RYJ3DFTU.css';
3
+ import './shared-NJHRNLRH.css';
4
+ import './Calendar-I6NHCXIB.css';
5
+ import * as React from 'react';
6
+ import { jsxs, jsx } from 'react/jsx-runtime';
7
+
8
+ var WEEKDAYS = ["\u65E5", "\u4E00", "\u4E8C", "\u4E09", "\u56DB", "\u4E94", "\u516D"];
9
+ var MONTHS = Array.from({ length: 12 }, (_, index) => `${index + 1} \u6708`);
10
+ var YEARS_PER_PAGE = 12;
11
+ var isSameDay = (a, b) => a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
12
+ var isValidDate = (date) => date instanceof Date && !Number.isNaN(date.getTime());
13
+ var Calendar = React.forwardRef(({
14
+ value,
15
+ defaultValue,
16
+ onChange,
17
+ viewDate,
18
+ min,
19
+ max,
20
+ disabledDate,
21
+ className = "",
22
+ ...rest
23
+ }, ref) => {
24
+ const [inner, setInner] = React.useState(defaultValue ?? /* @__PURE__ */ new Date());
25
+ const isControlled = value !== void 0;
26
+ const sel = isControlled ? value : inner;
27
+ const [view, setView] = React.useState(new Date(sel.getFullYear(), sel.getMonth(), 1));
28
+ const [mode, setMode] = React.useState("date");
29
+ const viewDateTime = isValidDate(viewDate) ? viewDate.getTime() : null;
30
+ const viewDateYear = isValidDate(viewDate) ? viewDate.getFullYear() : null;
31
+ const viewDateMonth = isValidDate(viewDate) ? viewDate.getMonth() : null;
32
+ React.useEffect(() => {
33
+ if (viewDateTime == null || viewDateYear == null || viewDateMonth == null) return;
34
+ setView(new Date(viewDateYear, viewDateMonth, 1));
35
+ setMode("date");
36
+ }, [viewDateMonth, viewDateTime, viewDateYear]);
37
+ const first = new Date(view.getFullYear(), view.getMonth(), 1);
38
+ const startDay = first.getDay();
39
+ const daysInMonth = new Date(view.getFullYear(), view.getMonth() + 1, 0).getDate();
40
+ const cells = [];
41
+ for (let i = 0; i < startDay; i++) cells.push(null);
42
+ for (let d = 1; d <= daysInMonth; d++) cells.push(d);
43
+ const isDisabledDay = React.useCallback(
44
+ (date) => min && date < min || max && date > max || (disabledDate?.(date) ?? false),
45
+ [disabledDate, max, min]
46
+ );
47
+ const isMonthDisabled = React.useCallback(
48
+ (year, month) => {
49
+ const count = new Date(year, month + 1, 0).getDate();
50
+ for (let day = 1; day <= count; day++) {
51
+ if (!isDisabledDay(new Date(year, month, day))) return false;
52
+ }
53
+ return true;
54
+ },
55
+ [isDisabledDay]
56
+ );
57
+ const isYearDisabled = React.useCallback(
58
+ (year) => Array.from({ length: 12 }, (_, month) => month).every(
59
+ (month) => isMonthDisabled(year, month)
60
+ ),
61
+ [isMonthDisabled]
62
+ );
63
+ const pick = (d) => {
64
+ const next = new Date(view.getFullYear(), view.getMonth(), d);
65
+ if (isDisabledDay(next)) return;
66
+ if (!isControlled) setInner(next);
67
+ onChange?.(next);
68
+ };
69
+ const shiftView = (step) => {
70
+ if (mode === "year") {
71
+ setView(new Date(view.getFullYear() + step * YEARS_PER_PAGE, view.getMonth(), 1));
72
+ return;
73
+ }
74
+ if (mode === "month") {
75
+ setView(new Date(view.getFullYear() + step, view.getMonth(), 1));
76
+ return;
77
+ }
78
+ setView(new Date(view.getFullYear(), view.getMonth() + step, 1));
79
+ };
80
+ const pickMonth = (month) => {
81
+ if (isMonthDisabled(view.getFullYear(), month)) return;
82
+ setView(new Date(view.getFullYear(), month, 1));
83
+ setMode("date");
84
+ };
85
+ const pickYear = (year) => {
86
+ if (isYearDisabled(year)) return;
87
+ setView(new Date(year, view.getMonth(), 1));
88
+ setMode("month");
89
+ };
90
+ const yearPageStart = Math.floor(view.getFullYear() / YEARS_PER_PAGE) * YEARS_PER_PAGE;
91
+ const years = Array.from({ length: YEARS_PER_PAGE }, (_, index) => yearPageStart + index);
92
+ const renderTitle = () => {
93
+ if (mode === "year") {
94
+ return /* @__PURE__ */ jsxs("span", { className: "calendar-title static", children: [
95
+ yearPageStart,
96
+ " - ",
97
+ yearPageStart + YEARS_PER_PAGE - 1
98
+ ] });
99
+ }
100
+ if (mode === "month") {
101
+ return /* @__PURE__ */ jsxs(
102
+ "button",
103
+ {
104
+ type: "button",
105
+ className: "calendar-title",
106
+ onClick: () => setMode("year"),
107
+ "aria-label": "\u9009\u62E9\u5E74\u4EFD",
108
+ children: [
109
+ view.getFullYear(),
110
+ " \u5E74"
111
+ ]
112
+ }
113
+ );
114
+ }
115
+ return /* @__PURE__ */ jsxs("div", { className: "calendar-title-group", children: [
116
+ /* @__PURE__ */ jsxs(
117
+ "button",
118
+ {
119
+ type: "button",
120
+ className: "calendar-title-part",
121
+ onClick: () => setMode("year"),
122
+ "aria-label": "\u9009\u62E9\u5E74\u4EFD",
123
+ children: [
124
+ view.getFullYear(),
125
+ " \u5E74"
126
+ ]
127
+ }
128
+ ),
129
+ /* @__PURE__ */ jsxs(
130
+ "button",
131
+ {
132
+ type: "button",
133
+ className: "calendar-title-part",
134
+ onClick: () => setMode("month"),
135
+ "aria-label": "\u9009\u62E9\u6708\u4EFD",
136
+ children: [
137
+ view.getMonth() + 1,
138
+ " \u6708"
139
+ ]
140
+ }
141
+ )
142
+ ] });
143
+ };
144
+ return /* @__PURE__ */ jsxs("div", { ref, className: `calendar ${className}`, ...rest, children: [
145
+ /* @__PURE__ */ jsxs("div", { className: "calendar-head", children: [
146
+ /* @__PURE__ */ jsx(
147
+ "button",
148
+ {
149
+ type: "button",
150
+ className: "btn icon sm ghost",
151
+ onClick: () => shiftView(-1),
152
+ "aria-label": mode === "year" ? "Previous years" : mode === "month" ? "Previous year" : "Previous month",
153
+ children: /* @__PURE__ */ jsx(Icon, { name: "chevLeft", size: 14 })
154
+ }
155
+ ),
156
+ renderTitle(),
157
+ /* @__PURE__ */ jsx(
158
+ "button",
159
+ {
160
+ type: "button",
161
+ className: "btn icon sm ghost",
162
+ onClick: () => shiftView(1),
163
+ "aria-label": mode === "year" ? "Next years" : mode === "month" ? "Next year" : "Next month",
164
+ children: /* @__PURE__ */ jsx(Icon, { name: "chevRight", size: 14 })
165
+ }
166
+ )
167
+ ] }),
168
+ mode === "date" && /* @__PURE__ */ jsxs("div", { className: "calendar-grid", children: [
169
+ WEEKDAYS.map((w) => /* @__PURE__ */ jsx("div", { className: "calendar-dow", children: w }, w)),
170
+ cells.map((d, i) => {
171
+ if (d === null) return /* @__PURE__ */ jsx("div", { className: "calendar-cell out" }, i);
172
+ const date = new Date(view.getFullYear(), view.getMonth(), d);
173
+ const disabled = isDisabledDay(date);
174
+ return /* @__PURE__ */ jsx(
175
+ "button",
176
+ {
177
+ type: "button",
178
+ disabled: !!disabled,
179
+ className: `calendar-cell ${isSameDay(date, sel) ? "selected" : ""} ${isSameDay(date, /* @__PURE__ */ new Date()) ? "today" : ""} ${disabled ? "disabled" : ""}`,
180
+ onClick: () => pick(d),
181
+ children: d
182
+ },
183
+ i
184
+ );
185
+ })
186
+ ] }),
187
+ mode === "month" && /* @__PURE__ */ jsx("div", { className: "calendar-select-grid month", children: MONTHS.map((month, index) => {
188
+ const disabled = isMonthDisabled(view.getFullYear(), index);
189
+ return /* @__PURE__ */ jsx(
190
+ "button",
191
+ {
192
+ type: "button",
193
+ disabled,
194
+ className: `calendar-select-option ${index === view.getMonth() ? "selected" : ""} ${disabled ? "disabled" : ""}`,
195
+ onClick: () => pickMonth(index),
196
+ children: month
197
+ },
198
+ month
199
+ );
200
+ }) }),
201
+ mode === "year" && /* @__PURE__ */ jsx("div", { className: "calendar-select-grid year", children: years.map((year) => {
202
+ const disabled = isYearDisabled(year);
203
+ return /* @__PURE__ */ jsx(
204
+ "button",
205
+ {
206
+ type: "button",
207
+ disabled,
208
+ className: `calendar-select-option ${year === view.getFullYear() ? "selected" : ""} ${disabled ? "disabled" : ""}`,
209
+ onClick: () => pickYear(year),
210
+ children: year
211
+ },
212
+ year
213
+ );
214
+ }) })
215
+ ] });
216
+ });
217
+ Calendar.displayName = "Calendar";
218
+
219
+ export { Calendar };