@api7/ui-components 0.7.0 → 0.8.1
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/README.md +169 -8
- package/dist/components/anchor/anchor.d.ts +28 -0
- package/dist/components/anchor/anchor.js +249 -0
- package/dist/components/breadcrumb/breadcrumb.d.ts +15 -0
- package/dist/components/breadcrumb/breadcrumb.js +60 -0
- package/dist/components/button/button.d.ts +1 -1
- package/dist/components/button/button.js +1 -1
- package/dist/components/calendar/calendar.js +24 -3
- package/dist/components/cascader/cascader-data.d.ts +26 -0
- package/dist/components/cascader/cascader-data.js +110 -0
- package/dist/components/cascader/cascader.d.ts +41 -0
- package/dist/components/cascader/cascader.js +513 -0
- package/dist/components/code/code.d.ts +9 -0
- package/dist/components/code/code.js +20 -0
- package/dist/components/code/copy-button.js +42 -0
- package/dist/components/code-block/code-block.d.ts +16 -0
- package/dist/components/code-block/code-block.js +45 -0
- package/dist/components/code-block/highlight.js +67 -0
- package/dist/components/collapsible/collapsible.d.ts +14 -0
- package/dist/components/collapsible/collapsible.js +36 -0
- package/dist/components/combobox/combobox-loading.d.ts +18 -0
- package/dist/components/combobox/combobox-loading.js +148 -0
- package/dist/components/combobox/combobox.d.ts +13 -6
- package/dist/components/combobox/combobox.js +58 -10
- package/dist/components/data-table/data-table.d.ts +27 -3
- package/dist/components/data-table/data-table.js +155 -40
- package/dist/components/data-table/expandable-row.js +40 -0
- package/dist/components/data-table/filter-header.d.ts +28 -5
- package/dist/components/data-table/filter-header.js +103 -40
- package/dist/components/data-table/index.d.ts +3 -3
- package/dist/components/data-table/use-table-layout.js +84 -0
- package/dist/components/date-picker/date-picker.js +11 -2
- package/dist/components/description-list/description-list.d.ts +1 -1
- package/dist/components/dialog/dialog.js +1 -1
- package/dist/components/drawer/drawer.js +1 -1
- package/dist/components/empty/empty.d.ts +1 -1
- package/dist/components/field/labeled-field.js +1 -1
- package/dist/components/file-upload/file-upload.d.ts +44 -0
- package/dist/components/file-upload/file-upload.js +195 -0
- package/dist/components/form/choice-fields.js +2 -2
- package/dist/components/form/date-fields.d.ts +1 -1
- package/dist/components/form/date-fields.js +1 -1
- package/dist/components/form/form-item.js +1 -1
- package/dist/components/form/form.js +1 -1
- package/dist/components/form/index.d.ts +2 -2
- package/dist/components/form/index.js +4 -4
- package/dist/components/form/text-field.js +2 -2
- package/dist/components/form/toggle-fields.d.ts +1 -1
- package/dist/components/form/toggle-fields.js +3 -3
- package/dist/components/pagination/pagination.d.ts +20 -4
- package/dist/components/pagination/pagination.js +83 -32
- package/dist/components/relative-date-range-picker/relative-date-range-picker.d.ts +14 -0
- package/dist/components/relative-date-range-picker/relative-date-range-picker.js +245 -0
- package/dist/components/relative-date-range-picker/relative-date-range.d.ts +26 -0
- package/dist/components/relative-date-range-picker/relative-date-range.js +109 -0
- package/dist/components/select/select.js +1 -1
- package/dist/components/sidebar/sidebar.js +2 -2
- package/dist/components/steps/steps.d.ts +23 -0
- package/dist/components/steps/steps.js +86 -0
- package/dist/components/table/table.d.ts +2 -1
- package/dist/components/table/table.js +2 -1
- package/dist/components/toast/toast.d.ts +1 -1
- package/dist/components/toast/toast.js +1 -1
- package/dist/config/config-provider.d.ts +2 -2
- package/dist/config/messages.d.ts +115 -1
- package/dist/config/messages.js +140 -2
- package/dist/hooks/use-clipboard.d.ts +11 -0
- package/dist/hooks/use-clipboard.js +75 -0
- package/dist/hooks/use-field-label-with-text.js +32 -0
- package/dist/index.d.ts +26 -13
- package/dist/index.js +15 -4
- package/dist/tokens/index.d.ts +1 -1
- package/dist/tokens/index.js +1 -1
- package/package.json +7 -6
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { describeRelativeDateRange, normalizeRelativeTime, parseRelativeTime, resolveRelativeDateRange } from "./relative-date-range.js";
|
|
3
|
+
import { Button } from "../button/button.js";
|
|
4
|
+
import { Calendar } from "../calendar/calendar.js";
|
|
5
|
+
import { InputGroup, InputGroupAddon } from "../input-group/input-group.js";
|
|
6
|
+
import { Input } from "../input/input.js";
|
|
7
|
+
import { Popover, PopoverContent, PopoverTrigger } from "../popover/popover.js";
|
|
8
|
+
import { useMessages } from "../../config/config-provider.js";
|
|
9
|
+
import { useState } from "react";
|
|
10
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
11
|
+
import { CalendarIcon, SearchIcon, XIcon } from "lucide-react";
|
|
12
|
+
// src/components/relative-date-range-picker/relative-date-range-picker.tsx
|
|
13
|
+
const CALENDAR_YEARS = 5;
|
|
14
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
15
|
+
const localText = (seconds) => {
|
|
16
|
+
const d = /* @__PURE__ */ new Date(seconds * 1e3);
|
|
17
|
+
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
|
|
18
|
+
};
|
|
19
|
+
const zoneName = () => new Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
20
|
+
const zoneOffset = () => {
|
|
21
|
+
const minutes = -(/* @__PURE__ */ new Date()).getTimezoneOffset();
|
|
22
|
+
return `UTC${minutes < 0 ? "-" : "+"}${pad(Math.floor(Math.abs(minutes) / 60))}:${pad(Math.abs(minutes) % 60)}`;
|
|
23
|
+
};
|
|
24
|
+
const typed = (end) => {
|
|
25
|
+
const read = parseRelativeTime(end);
|
|
26
|
+
return "error" in read || read.kind === "now" ? end : localText(read.seconds);
|
|
27
|
+
};
|
|
28
|
+
function RelativeDateRangePicker({ value, onChange, presets, maxSpanSeconds, now, className }) {
|
|
29
|
+
const t = useMessages().relativeDateRangePicker;
|
|
30
|
+
const [open, setOpen] = useState(false);
|
|
31
|
+
const [from, setFrom] = useState(value.from);
|
|
32
|
+
const [to, setTo] = useState(value.to);
|
|
33
|
+
const [problem, setProblem] = useState(null);
|
|
34
|
+
const [calendarOpen, setCalendarOpen] = useState(false);
|
|
35
|
+
const [anchor, setAnchor] = useState(null);
|
|
36
|
+
const [query, setQuery] = useState("");
|
|
37
|
+
const apply = (range) => {
|
|
38
|
+
const resolved = resolveRelativeDateRange(range, now(), maxSpanSeconds, t);
|
|
39
|
+
if ("error" in resolved) {
|
|
40
|
+
setProblem(resolved.error);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
onChange({
|
|
44
|
+
from: normalizeRelativeTime(range.from),
|
|
45
|
+
to: normalizeRelativeTime(range.to)
|
|
46
|
+
});
|
|
47
|
+
setOpen(false);
|
|
48
|
+
};
|
|
49
|
+
const show = (next) => {
|
|
50
|
+
if (next) {
|
|
51
|
+
setFrom(typed(value.from));
|
|
52
|
+
setTo(typed(value.to));
|
|
53
|
+
setProblem(null);
|
|
54
|
+
setCalendarOpen(false);
|
|
55
|
+
setAnchor(null);
|
|
56
|
+
setQuery("");
|
|
57
|
+
}
|
|
58
|
+
setOpen(next);
|
|
59
|
+
};
|
|
60
|
+
const edit = (end, text) => {
|
|
61
|
+
(end === "start" ? setFrom : setTo)(text);
|
|
62
|
+
setProblem(null);
|
|
63
|
+
};
|
|
64
|
+
const day = (text) => {
|
|
65
|
+
const read = parseRelativeTime(text);
|
|
66
|
+
if ("error" in read) return void 0;
|
|
67
|
+
return /* @__PURE__ */ new Date((read.kind === "now" ? now() - read.offsetSeconds : read.seconds) * 1e3);
|
|
68
|
+
};
|
|
69
|
+
const onDay = (end, date) => {
|
|
70
|
+
const read = parseRelativeTime(end === "start" ? from : to);
|
|
71
|
+
const clock = "error" in read || read.kind !== "absolute" ? null : /* @__PURE__ */ new Date(read.seconds * 1e3);
|
|
72
|
+
const edge = new Date(date);
|
|
73
|
+
const [h, m, sec] = clock ? [
|
|
74
|
+
clock.getHours(),
|
|
75
|
+
clock.getMinutes(),
|
|
76
|
+
clock.getSeconds()
|
|
77
|
+
] : end === "start" ? [
|
|
78
|
+
0,
|
|
79
|
+
0,
|
|
80
|
+
0
|
|
81
|
+
] : [
|
|
82
|
+
23,
|
|
83
|
+
59,
|
|
84
|
+
59
|
|
85
|
+
];
|
|
86
|
+
edge.setHours(h, m, sec, 0);
|
|
87
|
+
return end === "end" && edge.getTime() / 1e3 > now() ? "now" : localText(edge.getTime() / 1e3);
|
|
88
|
+
};
|
|
89
|
+
const pickDay = (date) => {
|
|
90
|
+
const [first, last] = anchor && anchor.getTime() < date.getTime() ? [anchor, date] : anchor ? [date, anchor] : [date, date];
|
|
91
|
+
setFrom(onDay("start", first));
|
|
92
|
+
setTo(onDay("end", last));
|
|
93
|
+
setProblem(null);
|
|
94
|
+
setAnchor(anchor ? null : date);
|
|
95
|
+
};
|
|
96
|
+
const shown = presets.filter((preset) => preset.label.toLowerCase().includes(query.trim().toLowerCase()));
|
|
97
|
+
const current = {
|
|
98
|
+
from: normalizeRelativeTime(value.from),
|
|
99
|
+
to: normalizeRelativeTime(value.to)
|
|
100
|
+
};
|
|
101
|
+
const field = (end, label) => /* @__PURE__ */ jsxs("div", {
|
|
102
|
+
className: "flex flex-col gap-1.5",
|
|
103
|
+
children: [/* @__PURE__ */ jsx("label", {
|
|
104
|
+
htmlFor: `relative-date-range-${end}`,
|
|
105
|
+
className: "text-sm text-muted-foreground",
|
|
106
|
+
children: label
|
|
107
|
+
}), /* @__PURE__ */ jsxs(InputGroup, { children: [/* @__PURE__ */ jsx(Input, {
|
|
108
|
+
id: `relative-date-range-${end}`,
|
|
109
|
+
"aria-label": label,
|
|
110
|
+
value: end === "start" ? from : to,
|
|
111
|
+
placeholder: end === "start" ? "now-5m" : "now",
|
|
112
|
+
onValueChange: (text) => edit(end, text)
|
|
113
|
+
}), /* @__PURE__ */ jsx(InputGroupAddon, { children: /* @__PURE__ */ jsx(Button, {
|
|
114
|
+
variant: "ghost",
|
|
115
|
+
size: "icon",
|
|
116
|
+
"aria-label": calendarOpen ? t.closeCalendarFor({ label }) : t.openCalendarFor({ label }),
|
|
117
|
+
"aria-expanded": calendarOpen,
|
|
118
|
+
onClick: () => setCalendarOpen(!calendarOpen),
|
|
119
|
+
children: /* @__PURE__ */ jsx(CalendarIcon, {
|
|
120
|
+
"aria-hidden": true,
|
|
121
|
+
className: "size-4"
|
|
122
|
+
})
|
|
123
|
+
}) })] })]
|
|
124
|
+
});
|
|
125
|
+
return /* @__PURE__ */ jsxs(Popover, {
|
|
126
|
+
open,
|
|
127
|
+
onOpenChange: show,
|
|
128
|
+
children: [/* @__PURE__ */ jsxs(PopoverTrigger, {
|
|
129
|
+
render: /* @__PURE__ */ jsx(Button, {
|
|
130
|
+
variant: "outline",
|
|
131
|
+
className
|
|
132
|
+
}),
|
|
133
|
+
children: [/* @__PURE__ */ jsx(CalendarIcon, {
|
|
134
|
+
"aria-hidden": true,
|
|
135
|
+
className: "size-4 shrink-0 text-muted-foreground"
|
|
136
|
+
}), describeRelativeDateRange(value, localText, t)]
|
|
137
|
+
}), /* @__PURE__ */ jsxs(PopoverContent, {
|
|
138
|
+
align: "end",
|
|
139
|
+
className: "w-auto",
|
|
140
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
141
|
+
className: "flex flex-wrap gap-3",
|
|
142
|
+
children: [
|
|
143
|
+
calendarOpen && /* @__PURE__ */ jsxs("div", {
|
|
144
|
+
className: "flex shrink-0 flex-col gap-2 border-r border-border pr-3",
|
|
145
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
146
|
+
className: "flex items-center justify-between",
|
|
147
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
148
|
+
className: "text-sm font-medium",
|
|
149
|
+
children: t.calendar
|
|
150
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
151
|
+
variant: "ghost",
|
|
152
|
+
size: "icon",
|
|
153
|
+
"aria-label": t.closeCalendar,
|
|
154
|
+
onClick: () => setCalendarOpen(false),
|
|
155
|
+
children: /* @__PURE__ */ jsx(XIcon, {
|
|
156
|
+
"aria-hidden": true,
|
|
157
|
+
className: "size-4"
|
|
158
|
+
})
|
|
159
|
+
})]
|
|
160
|
+
}), /* @__PURE__ */ jsx(Calendar, {
|
|
161
|
+
mode: "range",
|
|
162
|
+
selected: {
|
|
163
|
+
from: day(from),
|
|
164
|
+
to: day(to)
|
|
165
|
+
},
|
|
166
|
+
onDayClick: pickDay,
|
|
167
|
+
disabled: { after: /* @__PURE__ */ new Date(now() * 1e3) },
|
|
168
|
+
defaultMonth: day(to),
|
|
169
|
+
captionLayout: "dropdown",
|
|
170
|
+
startMonth: new Date((/* @__PURE__ */ new Date(now() * 1e3)).getFullYear() - CALENDAR_YEARS, 0),
|
|
171
|
+
endMonth: /* @__PURE__ */ new Date(now() * 1e3),
|
|
172
|
+
classNames: { caption_label: "[&_svg]:hidden" }
|
|
173
|
+
})]
|
|
174
|
+
}),
|
|
175
|
+
/* @__PURE__ */ jsxs("div", {
|
|
176
|
+
className: "flex w-72 shrink-0 flex-col gap-3",
|
|
177
|
+
children: [
|
|
178
|
+
/* @__PURE__ */ jsx("p", {
|
|
179
|
+
className: "text-sm font-medium",
|
|
180
|
+
children: t.absolute
|
|
181
|
+
}),
|
|
182
|
+
field("start", t.from),
|
|
183
|
+
field("end", t.to),
|
|
184
|
+
problem && /* @__PURE__ */ jsx("p", {
|
|
185
|
+
role: "alert",
|
|
186
|
+
className: "text-sm text-destructive",
|
|
187
|
+
children: problem
|
|
188
|
+
}),
|
|
189
|
+
/* @__PURE__ */ jsx(Button, {
|
|
190
|
+
className: "self-start",
|
|
191
|
+
onClick: () => apply({
|
|
192
|
+
from,
|
|
193
|
+
to
|
|
194
|
+
}),
|
|
195
|
+
children: t.apply
|
|
196
|
+
})
|
|
197
|
+
]
|
|
198
|
+
}),
|
|
199
|
+
/* @__PURE__ */ jsxs("div", {
|
|
200
|
+
className: "flex w-56 shrink-0 flex-col gap-2 border-l border-border pl-3",
|
|
201
|
+
children: [/* @__PURE__ */ jsxs(InputGroup, { children: [/* @__PURE__ */ jsx(InputGroupAddon, { children: /* @__PURE__ */ jsx(SearchIcon, {
|
|
202
|
+
"aria-hidden": true,
|
|
203
|
+
className: "size-4"
|
|
204
|
+
}) }), /* @__PURE__ */ jsx(Input, {
|
|
205
|
+
"aria-label": t.search,
|
|
206
|
+
placeholder: t.search,
|
|
207
|
+
value: query,
|
|
208
|
+
onValueChange: setQuery
|
|
209
|
+
})] }), /* @__PURE__ */ jsxs("ul", {
|
|
210
|
+
"aria-label": t.presets,
|
|
211
|
+
className: "flex max-h-64 flex-col gap-1 overflow-y-auto",
|
|
212
|
+
children: [shown.map((preset) => {
|
|
213
|
+
const selected = current.from === normalizeRelativeTime(preset.from) && current.to === normalizeRelativeTime(preset.to);
|
|
214
|
+
return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("button", {
|
|
215
|
+
type: "button",
|
|
216
|
+
"aria-pressed": selected,
|
|
217
|
+
onClick: () => apply(preset),
|
|
218
|
+
className: `h-8 w-full rounded-sm px-3 text-left text-sm whitespace-nowrap outline-none hover:bg-accent focus-visible:ring-2 focus-visible:ring-ring/40 ${selected ? "bg-accent font-medium text-primary-text" : "text-foreground"}`,
|
|
219
|
+
children: preset.label
|
|
220
|
+
}) }, preset.label);
|
|
221
|
+
}), shown.length === 0 && /* @__PURE__ */ jsx("li", {
|
|
222
|
+
className: "px-3 py-2 text-sm text-muted-foreground",
|
|
223
|
+
children: t.none
|
|
224
|
+
})]
|
|
225
|
+
})]
|
|
226
|
+
})
|
|
227
|
+
]
|
|
228
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
229
|
+
className: "mt-3 flex items-center justify-between gap-3 border-t border-border pt-3 text-xs text-muted-foreground",
|
|
230
|
+
children: [/* @__PURE__ */ jsxs("span", { children: [
|
|
231
|
+
t.browserTime,
|
|
232
|
+
" ",
|
|
233
|
+
/* @__PURE__ */ jsx("span", {
|
|
234
|
+
className: "ml-1",
|
|
235
|
+
children: zoneName()
|
|
236
|
+
})
|
|
237
|
+
] }), /* @__PURE__ */ jsx("span", {
|
|
238
|
+
className: "rounded-sm bg-muted px-2 py-1",
|
|
239
|
+
children: zoneOffset()
|
|
240
|
+
})]
|
|
241
|
+
})]
|
|
242
|
+
})]
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
export { RelativeDateRangePicker };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Messages } from "../../config/messages.js";
|
|
2
|
+
export type RelativeDateRangeMessages = Messages['relativeDateRangePicker'];
|
|
3
|
+
export type RelativeDateRangeValue = {
|
|
4
|
+
from: string;
|
|
5
|
+
to: string;
|
|
6
|
+
};
|
|
7
|
+
export type ParsedRelativeTime = {
|
|
8
|
+
kind: 'now';
|
|
9
|
+
offsetSeconds: number;
|
|
10
|
+
} | {
|
|
11
|
+
kind: 'absolute';
|
|
12
|
+
seconds: number;
|
|
13
|
+
};
|
|
14
|
+
export declare function parseRelativeTime(text: string, messages?: RelativeDateRangeMessages): ParsedRelativeTime | {
|
|
15
|
+
error: string;
|
|
16
|
+
};
|
|
17
|
+
export declare function normalizeRelativeTime(text: string, messages?: RelativeDateRangeMessages): string | {
|
|
18
|
+
error: string;
|
|
19
|
+
};
|
|
20
|
+
export declare function resolveRelativeDateRange(value: RelativeDateRangeValue, nowSeconds: number, maxSpanSeconds: number, messages?: RelativeDateRangeMessages): {
|
|
21
|
+
start: number;
|
|
22
|
+
end: number;
|
|
23
|
+
} | {
|
|
24
|
+
error: string;
|
|
25
|
+
};
|
|
26
|
+
export declare function describeRelativeDateRange(value: RelativeDateRangeValue, formatMoment: (seconds: number) => string, messages?: RelativeDateRangeMessages): string;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { enUS } from "../../config/messages.js";
|
|
2
|
+
// src/components/relative-date-range-picker/relative-date-range.ts
|
|
3
|
+
const MIN_YEAR = 1970;
|
|
4
|
+
const UNIT_SECONDS = {
|
|
5
|
+
m: 60,
|
|
6
|
+
h: 3600,
|
|
7
|
+
d: 86400,
|
|
8
|
+
w: 604800
|
|
9
|
+
};
|
|
10
|
+
const FUTURE_SLACK_SECONDS = 60;
|
|
11
|
+
const NOW = /^now(?:\s*-\s*(\d+)\s*([mhdw]))?$/i;
|
|
12
|
+
const WITH_ZONE = /^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d))?(Z|[+-](?:[01]\d|2[0-3]):[0-5]\d)$/i;
|
|
13
|
+
const LOCAL = /^(\d{4})-(\d\d)-(\d\d)(?:[ T](\d\d):(\d\d)(?::(\d\d))?)?$/;
|
|
14
|
+
function parseRelativeTime(text, messages = enUS.relativeDateRangePicker) {
|
|
15
|
+
const fail = () => ({ error: messages.understood });
|
|
16
|
+
const value = text.trim();
|
|
17
|
+
const now = NOW.exec(value);
|
|
18
|
+
if (now) {
|
|
19
|
+
if (now[1] === void 0) return {
|
|
20
|
+
kind: "now",
|
|
21
|
+
offsetSeconds: 0
|
|
22
|
+
};
|
|
23
|
+
const count = Number(now[1]);
|
|
24
|
+
return count > 0 ? {
|
|
25
|
+
kind: "now",
|
|
26
|
+
offsetSeconds: count * UNIT_SECONDS[now[2].toLowerCase()]
|
|
27
|
+
} : fail();
|
|
28
|
+
}
|
|
29
|
+
const zoned = WITH_ZONE.exec(value);
|
|
30
|
+
if (zoned) {
|
|
31
|
+
const [, y, mo, d, h, mi, s, zone] = zoned;
|
|
32
|
+
if (Number(y) < MIN_YEAR) return fail();
|
|
33
|
+
const calendar = Date.UTC(Number(y), Number(mo) - 1, Number(d), Number(h), Number(mi), Number(s ?? 0));
|
|
34
|
+
const check = new Date(calendar);
|
|
35
|
+
if (check.getUTCMonth() !== Number(mo) - 1 || check.getUTCDate() !== Number(d) || Number(h) > 23 || Number(mi) > 59 || Number(s ?? 0) > 59) return fail();
|
|
36
|
+
return {
|
|
37
|
+
kind: "absolute",
|
|
38
|
+
seconds: (calendar - (zone.toUpperCase() === "Z" ? 0 : (zone[0] === "-" ? -1 : 1) * (Number(zone.slice(1, 3)) * 60 + Number(zone.slice(4, 6))) * 6e4)) / 1e3
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
const local = LOCAL.exec(value);
|
|
42
|
+
if (local) {
|
|
43
|
+
const [, y, mo, d, h = "0", mi = "0", s = "0"] = local;
|
|
44
|
+
if (Number(y) < MIN_YEAR) return fail();
|
|
45
|
+
const date = new Date(Number(y), Number(mo) - 1, Number(d), Number(h), Number(mi), Number(s));
|
|
46
|
+
if (date.getMonth() !== Number(mo) - 1 || date.getDate() !== Number(d) || Number(h) > 23 || Number(mi) > 59 || Number(s) > 59) return fail();
|
|
47
|
+
return {
|
|
48
|
+
kind: "absolute",
|
|
49
|
+
seconds: date.getTime() / 1e3
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
return fail();
|
|
53
|
+
}
|
|
54
|
+
function offsetText(seconds) {
|
|
55
|
+
const unit = [
|
|
56
|
+
"w",
|
|
57
|
+
"d",
|
|
58
|
+
"h",
|
|
59
|
+
"m"
|
|
60
|
+
].find((one) => seconds % UNIT_SECONDS[one] === 0);
|
|
61
|
+
return `${seconds / UNIT_SECONDS[unit]}${unit}`;
|
|
62
|
+
}
|
|
63
|
+
const isoUtc = (seconds) => (/* @__PURE__ */ new Date(seconds * 1e3)).toISOString().replace(".000Z", "Z");
|
|
64
|
+
function normalizeRelativeTime(text, messages = enUS.relativeDateRangePicker) {
|
|
65
|
+
const read = parseRelativeTime(text, messages);
|
|
66
|
+
if ("error" in read) return read;
|
|
67
|
+
if (read.kind === "now") return read.offsetSeconds === 0 ? "now" : `now-${offsetText(read.offsetSeconds)}`;
|
|
68
|
+
return isoUtc(read.seconds);
|
|
69
|
+
}
|
|
70
|
+
function resolveRelativeDateRange(value, nowSeconds, maxSpanSeconds, messages = enUS.relativeDateRangePicker) {
|
|
71
|
+
const from = parseRelativeTime(value.from, messages);
|
|
72
|
+
if ("error" in from) return { error: messages.endError({
|
|
73
|
+
label: messages.from,
|
|
74
|
+
reason: from.error
|
|
75
|
+
}) };
|
|
76
|
+
const to = parseRelativeTime(value.to, messages);
|
|
77
|
+
if ("error" in to) return { error: messages.endError({
|
|
78
|
+
label: messages.to,
|
|
79
|
+
reason: to.error
|
|
80
|
+
}) };
|
|
81
|
+
const at = (time) => time.kind === "now" ? nowSeconds - time.offsetSeconds : time.seconds;
|
|
82
|
+
const start = at(from);
|
|
83
|
+
const end = at(to);
|
|
84
|
+
if (end > nowSeconds + FUTURE_SLACK_SECONDS) return { error: messages.future };
|
|
85
|
+
if (end <= start) return { error: messages.endBeforeStart };
|
|
86
|
+
if (end - start > maxSpanSeconds) return { error: messages.tooLong({ days: String(maxSpanSeconds / 86400) }) };
|
|
87
|
+
return {
|
|
88
|
+
start,
|
|
89
|
+
end
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function describeRelativeDateRange(value, formatMoment, messages = enUS.relativeDateRangePicker) {
|
|
93
|
+
const from = normalizeRelativeTime(value.from, messages);
|
|
94
|
+
const to = normalizeRelativeTime(value.to, messages);
|
|
95
|
+
if (typeof from === "string" && typeof to === "string" && to === "now" && from.startsWith("now-")) {
|
|
96
|
+
const text = offsetText(parseRelativeTime(from, messages).offsetSeconds);
|
|
97
|
+
return messages.last({
|
|
98
|
+
count: text.slice(0, -1),
|
|
99
|
+
unit: text.slice(-1)
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
const show = (end, original) => {
|
|
103
|
+
if (typeof end !== "string") return original;
|
|
104
|
+
const read = parseRelativeTime(end, messages);
|
|
105
|
+
return "error" in read || read.kind === "now" ? end : formatMoment(read.seconds);
|
|
106
|
+
};
|
|
107
|
+
return `${show(from, value.from)} ~ ${show(to, value.to)}`;
|
|
108
|
+
}
|
|
109
|
+
export { describeRelativeDateRange, normalizeRelativeTime, parseRelativeTime, resolveRelativeDateRange };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useMessages } from "../../config/config-provider.js";
|
|
3
3
|
import { cn } from "../../lib/cn.js";
|
|
4
|
-
import { listItemClassName, listboxClassName, popupLayerClassName } from "../../lib/popup.js";
|
|
5
4
|
import { controlFocus, controlSurface } from "../../lib/control.js";
|
|
5
|
+
import { listItemClassName, listboxClassName, popupLayerClassName } from "../../lib/popup.js";
|
|
6
6
|
import { createContext, useCallback, useContext, useMemo, useRef, useState } from "react";
|
|
7
7
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
8
|
import { CheckIcon, ChevronDownIcon, XIcon } from "lucide-react";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { cn } from "../../lib/cn.js";
|
|
3
|
-
import { Tooltip, TooltipContent, TooltipTrigger } from "../tooltip/tooltip.js";
|
|
4
2
|
import { Separator } from "../separator/separator.js";
|
|
3
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from "../tooltip/tooltip.js";
|
|
4
|
+
import { cn } from "../../lib/cn.js";
|
|
5
5
|
import { cloneElement, useId } from "react";
|
|
6
6
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
7
7
|
// src/components/sidebar/sidebar.tsx
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ComponentProps, Key, ReactNode } from "react";
|
|
2
|
+
export type StepsStatus = 'wait' | 'process' | 'finish' | 'error';
|
|
3
|
+
export interface StepsItem {
|
|
4
|
+
key?: Key;
|
|
5
|
+
title: ReactNode;
|
|
6
|
+
description?: ReactNode;
|
|
7
|
+
icon?: ReactNode;
|
|
8
|
+
status?: StepsStatus;
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface StepsProps extends Omit<ComponentProps<'ol'>, 'children'> {
|
|
12
|
+
items: readonly StepsItem[];
|
|
13
|
+
/** Zero-based active step. Set to items.length to mark every step complete. */
|
|
14
|
+
current?: number;
|
|
15
|
+
/** Status of the current step. An item's status takes precedence. */
|
|
16
|
+
status?: StepsStatus;
|
|
17
|
+
orientation?: 'horizontal' | 'vertical';
|
|
18
|
+
/** Enables step buttons. The caller decides when to update current. */
|
|
19
|
+
onStepChange?: (index: number) => void;
|
|
20
|
+
disabled?: boolean;
|
|
21
|
+
}
|
|
22
|
+
declare function Steps({ items, current, status, orientation, onStepChange, disabled, className, ...props }: StepsProps): import("react").JSX.Element;
|
|
23
|
+
export { Steps };
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useMessages } from "../../config/config-provider.js";
|
|
3
|
+
import { cn } from "../../lib/cn.js";
|
|
4
|
+
import { useId } from "react";
|
|
5
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
import { Check, X } from "lucide-react";
|
|
7
|
+
// src/components/steps/steps.tsx
|
|
8
|
+
function Steps({ items, current = 0, status = "process", orientation = "horizontal", onStepChange, disabled = false, className, ...props }) {
|
|
9
|
+
const messages = useMessages();
|
|
10
|
+
const id = useId();
|
|
11
|
+
const active = Number.isFinite(current) ? Math.min(items.length, Math.max(0, Math.floor(current))) : 0;
|
|
12
|
+
const vertical = orientation === "vertical";
|
|
13
|
+
return /* @__PURE__ */ jsx("ol", {
|
|
14
|
+
"aria-label": messages.steps.label,
|
|
15
|
+
role: "list",
|
|
16
|
+
className: cn("m-0 flex list-none p-1 text-sm", vertical && "flex-col", className),
|
|
17
|
+
...props,
|
|
18
|
+
"data-slot": "steps",
|
|
19
|
+
"data-orientation": orientation,
|
|
20
|
+
children: items.map((item, index) => {
|
|
21
|
+
const itemStatus = item.status ?? (index < active ? "finish" : index === active ? status : "wait");
|
|
22
|
+
const isCurrent = index === active;
|
|
23
|
+
const isDisabled = disabled || item.disabled;
|
|
24
|
+
const titleId = `${id}-${index}-title`;
|
|
25
|
+
const statusId = `${id}-${index}-status`;
|
|
26
|
+
const descriptionId = `${id}-${index}-description`;
|
|
27
|
+
const hasDescription = item.description != null;
|
|
28
|
+
const contentClassName = cn("relative flex min-w-0 gap-2", vertical ? "items-start text-start" : "flex-col items-center text-center", itemStatus === "process" ? "text-primary-text" : itemStatus === "error" ? "text-destructive-text" : itemStatus === "finish" ? "text-foreground" : "text-muted-foreground");
|
|
29
|
+
const content = /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
|
|
30
|
+
"aria-hidden": "true",
|
|
31
|
+
className: cn("relative flex size-6 shrink-0 items-center justify-center rounded-full border-2 border-border bg-background text-muted-foreground [&>svg]:size-3.5", (itemStatus === "process" || itemStatus === "finish") && "border-primary-text bg-info-subtle text-primary-text", itemStatus === "process" && "ring-4 ring-info-subtle", itemStatus === "error" && "border-destructive-text bg-destructive-subtle text-destructive-text"),
|
|
32
|
+
"data-slot": "steps-indicator",
|
|
33
|
+
children: item.icon ?? (itemStatus === "finish" ? /* @__PURE__ */ jsx(Check, {}) : itemStatus === "error" ? /* @__PURE__ */ jsx(X, {}) : /* @__PURE__ */ jsx("span", { className: "size-2 rounded-full bg-current" }))
|
|
34
|
+
}), /* @__PURE__ */ jsxs("span", {
|
|
35
|
+
className: cn("min-w-0 break-words", vertical ? "pt-0.5" : "w-full px-2"),
|
|
36
|
+
children: [
|
|
37
|
+
/* @__PURE__ */ jsx("span", {
|
|
38
|
+
id: titleId,
|
|
39
|
+
className: cn("block", isCurrent && "font-medium"),
|
|
40
|
+
"data-slot": "steps-title",
|
|
41
|
+
children: item.title
|
|
42
|
+
}),
|
|
43
|
+
/* @__PURE__ */ jsx("span", {
|
|
44
|
+
id: statusId,
|
|
45
|
+
className: "sr-only",
|
|
46
|
+
children: messages.steps[itemStatus]
|
|
47
|
+
}),
|
|
48
|
+
hasDescription && /* @__PURE__ */ jsx("span", {
|
|
49
|
+
id: descriptionId,
|
|
50
|
+
className: "mt-1 block text-xs text-muted-foreground",
|
|
51
|
+
"data-slot": "steps-description",
|
|
52
|
+
children: item.description
|
|
53
|
+
})
|
|
54
|
+
]
|
|
55
|
+
})] });
|
|
56
|
+
return /* @__PURE__ */ jsxs("li", {
|
|
57
|
+
className: cn("relative min-w-0", vertical ? "pb-6 last:pb-0" : "flex-1"),
|
|
58
|
+
"aria-current": !onStepChange && isCurrent ? "step" : void 0,
|
|
59
|
+
"data-slot": "steps-item",
|
|
60
|
+
"data-status": itemStatus,
|
|
61
|
+
"data-disabled": isDisabled || void 0,
|
|
62
|
+
children: [index < items.length - 1 && /* @__PURE__ */ jsx("span", {
|
|
63
|
+
"aria-hidden": "true",
|
|
64
|
+
className: cn("absolute bg-border", vertical ? "start-3 top-3 h-full w-0.5 -translate-x-1/2 rtl:translate-x-1/2" : "start-1/2 top-3 h-0.5 w-full -translate-y-1/2", itemStatus === "finish" && "bg-primary"),
|
|
65
|
+
"data-slot": "steps-separator"
|
|
66
|
+
}), onStepChange ? /* @__PURE__ */ jsx("button", {
|
|
67
|
+
type: "button",
|
|
68
|
+
className: cn(contentClassName, "w-full rounded-md border-0 bg-transparent p-0 outline-none enabled:cursor-pointer enabled:hover:underline focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"),
|
|
69
|
+
disabled: isDisabled,
|
|
70
|
+
"aria-current": isCurrent ? "step" : void 0,
|
|
71
|
+
"aria-labelledby": titleId,
|
|
72
|
+
"aria-describedby": [statusId, hasDescription && descriptionId].filter(Boolean).join(" "),
|
|
73
|
+
onClick: () => {
|
|
74
|
+
if (!isCurrent) onStepChange(index);
|
|
75
|
+
},
|
|
76
|
+
"data-slot": "steps-trigger",
|
|
77
|
+
children: content
|
|
78
|
+
}) : /* @__PURE__ */ jsx("div", {
|
|
79
|
+
className: contentClassName,
|
|
80
|
+
children: content
|
|
81
|
+
})]
|
|
82
|
+
}, item.key ?? index);
|
|
83
|
+
})
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
export { Steps };
|
|
@@ -2,8 +2,9 @@ import { ComponentProps } from "react";
|
|
|
2
2
|
export type TableProps = Omit<ComponentProps<'table'>, 'className'> & {
|
|
3
3
|
className?: string;
|
|
4
4
|
containerClassName?: string;
|
|
5
|
+
containerRef?: ComponentProps<'div'>['ref'];
|
|
5
6
|
};
|
|
6
|
-
declare function Table({ className, containerClassName, ...props }: TableProps): import("react").JSX.Element;
|
|
7
|
+
declare function Table({ className, containerClassName, containerRef, ...props }: TableProps): import("react").JSX.Element;
|
|
7
8
|
export type TableHeaderProps = Omit<ComponentProps<'thead'>, 'className'> & {
|
|
8
9
|
className?: string;
|
|
9
10
|
};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { cn } from "../../lib/cn.js";
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
3
|
// src/components/table/table.tsx
|
|
4
|
-
function Table({ className, containerClassName, ...props }) {
|
|
4
|
+
function Table({ className, containerClassName, containerRef, ...props }) {
|
|
5
5
|
return /* @__PURE__ */ jsx("div", {
|
|
6
|
+
ref: containerRef,
|
|
6
7
|
"data-slot": "table-container",
|
|
7
8
|
className: cn("relative w-full overflow-x-auto", containerClassName),
|
|
8
9
|
children: /* @__PURE__ */ jsx("table", {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import { buttonVariants } from "../button/button.js";
|
|
2
3
|
import { useMessages } from "../../config/config-provider.js";
|
|
3
4
|
import { cn } from "../../lib/cn.js";
|
|
4
|
-
import { buttonVariants } from "../button/button.js";
|
|
5
5
|
import { useEffect, useState, useSyncExternalStore } from "react";
|
|
6
6
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
7
|
import { CircleAlert, CircleCheck, Info, LoaderCircle, TriangleAlert, X } from "lucide-react";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Locale,
|
|
1
|
+
import { Locale, MessagesOverride, ResolvedMessages } from "./messages.js";
|
|
2
2
|
import { ReactNode } from "react";
|
|
3
3
|
export interface ConfigProviderProps {
|
|
4
4
|
locale?: Locale;
|
|
@@ -8,5 +8,5 @@ export interface ConfigProviderProps {
|
|
|
8
8
|
}
|
|
9
9
|
/** Global configuration entry for the library; currently carries the locale and built-in message overrides used by components. */
|
|
10
10
|
export declare function ConfigProvider({ locale, messages, children }: ConfigProviderProps): import("react").JSX.Element;
|
|
11
|
-
export declare function useMessages():
|
|
11
|
+
export declare function useMessages(): ResolvedMessages;
|
|
12
12
|
export declare function useLocale(): Locale;
|