@neuronection/assistant-ui 0.2.0 → 0.3.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/CHANGELOG.md +40 -0
- package/README.md +25 -5
- package/dist/chip-input.d.ts +14 -0
- package/dist/chip-input.js +3 -0
- package/dist/chip-input.js.map +1 -0
- package/dist/chip-list.d.ts +15 -0
- package/dist/chip-list.js +3 -0
- package/dist/chip-list.js.map +1 -0
- package/dist/chunk-2KO2YIZN.js +357 -0
- package/dist/chunk-2KO2YIZN.js.map +1 -0
- package/dist/chunk-5AVBKEDQ.js +74 -0
- package/dist/chunk-5AVBKEDQ.js.map +1 -0
- package/dist/chunk-5X3RYDPC.js +54 -0
- package/dist/chunk-5X3RYDPC.js.map +1 -0
- package/dist/chunk-TPLQL62V.js +124 -0
- package/dist/chunk-TPLQL62V.js.map +1 -0
- package/dist/chunk-UIUALJNR.js +110 -0
- package/dist/chunk-UIUALJNR.js.map +1 -0
- package/dist/chunk-XLD2BPTT.js +82 -0
- package/dist/chunk-XLD2BPTT.js.map +1 -0
- package/dist/date-picker.d.ts +26 -0
- package/dist/date-picker.js +3 -0
- package/dist/date-picker.js.map +1 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.js +16 -10
- package/dist/range-bar.d.ts +15 -0
- package/dist/range-bar.js +3 -0
- package/dist/range-bar.js.map +1 -0
- package/dist/scale-slider.d.ts +18 -0
- package/dist/scale-slider.js +3 -0
- package/dist/scale-slider.js.map +1 -0
- package/dist/styles.css +1 -1
- package/dist/table.d.ts +10 -0
- package/dist/table.js +3 -0
- package/dist/table.js.map +1 -0
- package/package.json +28 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# @neuronection/assistant-ui
|
|
2
|
+
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`e8a56d3`](https://github.com/neuronection/assistant-ui/commit/e8a56d33427fc2e8e03784fe7959037f24efb95a) Thanks [@constLiakos](https://github.com/constLiakos)! - Add the six components career-assistant's Phase-2 adoption needs, all shared
|
|
8
|
+
with health-assistant (catalog T1): `ChipInput`, `ChipList` (superset with
|
|
9
|
+
variants + clickable chips), `Table` (tokenized, with empty state), `RangeBar`
|
|
10
|
+
(with optional `label`/`valueLabel`), `ScaleSlider` (+ `scaleColorForValue`),
|
|
11
|
+
and `DatePicker` (Radix popover calendar with days/months/years views, min/max,
|
|
12
|
+
allowClear, and a roving-tabindex arrow-key day grid — an a11y upgrade over
|
|
13
|
+
both app copies). New dependency: `date-fns`.
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- [`e5252f2`](https://github.com/neuronection/assistant-ui/commit/e5252f26d6f8732a2f5ba4a3fe849b4df50b30c2) Thanks [@constLiakos](https://github.com/constLiakos)! - Ship CHANGELOG.md inside the published package so consuming apps (and their agents) can read what changed without visiting GitHub.
|
|
18
|
+
|
|
19
|
+
## 0.2.0
|
|
20
|
+
|
|
21
|
+
### Minor Changes
|
|
22
|
+
|
|
23
|
+
- [`ec2a8bf`](https://github.com/neuronection/assistant-ui/commit/ec2a8bf7d49bb96435990e9b8cca4d74fc31748b) Thanks [@constLiakos](https://github.com/constLiakos)! - Phase 1: 16 new modules for study-assistant full adoption
|
|
24
|
+
|
|
25
|
+
- PopoverButton: self-contained trigger+panel popover on Radix (hover-open, lazy children, closeSignal, controlled mode)
|
|
26
|
+
- InfoButton + FieldLabel: info affordances with popover help
|
|
27
|
+
- Menu: Radix DropdownMenu primitives (Menu, MenuItem, MenuContent…) + items-driven ActionMenu
|
|
28
|
+
- ContextMenu: coordinate-anchored menu with full Radix keyboard/typeahead semantics
|
|
29
|
+
- Combobox + ComboboxMulti: WAI-ARIA listbox pattern, grouping, async mode (onSearchChange + loading), multi-select, clearable
|
|
30
|
+
- FormModal: form-in-modal shell (submit/cancel/busy/disabled) on Modal primitives
|
|
31
|
+
- Wizard + Stepper: wizard state machine with per-step validation gates, modal and drawer variants; dots/labels steppers
|
|
32
|
+
- Ported from study-assistant ui/: CheckIndicator, SearchInput, ExpandableSearch, SelectionBar, ViewToggle, ErrorBanner, UndoNotice, Marquee (useMarquee/MarqueeBand/MarqueeSurface)
|
|
33
|
+
|
|
34
|
+
All components: forwardRef where DOM-emitting, semantic --as-\* tokens only, data-as hooks, English-default label props, keyboard-nav + axe tests.
|
|
35
|
+
|
|
36
|
+
## 0.1.0
|
|
37
|
+
|
|
38
|
+
### Minor Changes
|
|
39
|
+
|
|
40
|
+
- [`a211003`](https://github.com/neuronection/assistant-ui/commit/a211003348a5248f65a7995806500325408b6f92) Thanks [@constLiakos](https://github.com/constLiakos)! - Initial release (Phase 0): Button, Badge, Card, Modal, Popover, Tooltip + InfoTooltip, Input, Spinner, EmptyState, ConfirmationModal, Portal, ThemeScope and `cn()` — CSS-variable design tokens, precompiled `styles.css`, React 18/19 and Tailwind 3/4 compatible.
|
package/README.md
CHANGED
|
@@ -65,11 +65,31 @@ without configuration. Do **not** add this package to your Tailwind
|
|
|
65
65
|
|
|
66
66
|
## Components
|
|
67
67
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
[gallery](https://neuronection.github.io/assistant-ui/)
|
|
68
|
+
Every module has its own entry point (`import { Menu } from
|
|
69
|
+
'@neuronection/assistant-ui/menu'`) and everything is re-exported from the
|
|
70
|
+
package root. Authoritative props for your installed version: the `.d.ts`
|
|
71
|
+
files in the package's `dist/`. Visual reference:
|
|
72
|
+
[gallery](https://neuronection.github.io/assistant-ui/).
|
|
73
|
+
|
|
74
|
+
| Module | Exports |
|
|
75
|
+
|---|---|
|
|
76
|
+
| `button` / `badge` / `card` | `Button`, `buttonVariants` · `Badge`, `badgeVariants` · `Card`, `CardHeader/Title/Description/Content/Footer` |
|
|
77
|
+
| `modal` / `confirmation-modal` / `form-modal` | `Modal` + parts, `modalContentVariants` · `ConfirmationModal` · `FormModal` (form-in-modal shell) |
|
|
78
|
+
| `popover` / `popover-button` | compound Radix `Popover` + parts · `PopoverButton` (self-contained trigger + panel, hover-open, lazy children, `closeSignal`) |
|
|
79
|
+
| `menu` / `context-menu` | `Menu`, `MenuTrigger/Content/Item/Separator/Label`, `ActionMenu` (items-driven) · `ContextMenu` (coordinate-anchored, `{x, y, items, onClose}`) |
|
|
80
|
+
| `combobox` | `Combobox`, `ComboboxMulti` — async mode (`onSearchChange` + `loading`), grouping, full keyboard nav |
|
|
81
|
+
| `tooltip` | `Tooltip` + parts, `InfoTooltip` |
|
|
82
|
+
| `wizard` | `Wizard` (steps config + `renderStep`, per-step validation gates, modal/drawer variants), `Stepper` (dots/labels) |
|
|
83
|
+
| `input` / `search-input` / `expandable-search` | `Input` (label/hint/error wiring) · `SearchInput` · `ExpandableSearch` |
|
|
84
|
+
| `chip-input` / `chip-list` | `ChipInput` (Enter/comma commit, paste-splits, Backspace-removes) · `ChipList` (variant pills, clickable, removable) |
|
|
85
|
+
| `date-picker` | `DatePicker` (popover calendar: days/months/years views, min/max, `allowClear`, arrow-key day grid) |
|
|
86
|
+
| `range-bar` / `scale-slider` | `RangeBar` (low–high band + value dot) · `ScaleSlider` (+ `scaleColorForValue`) |
|
|
87
|
+
| `table` | `Table` (headers/rows, built-in empty state) |
|
|
88
|
+
| `check-indicator` / `selection-bar` / `view-toggle` | `CheckIndicator` (tri-state) · `SelectionBar` (bulk-select bar) · `ViewToggle` (grid/list) |
|
|
89
|
+
| `error-banner` / `undo-notice` / `empty-state` / `spinner` | presentational feedback: alert banner with `action` slot · undo toast · `EmptyState` · `Spinner` |
|
|
90
|
+
| `info-button` / `field-label` | `InfoButton` (info popover) · `FieldLabel` (label + info affordance) |
|
|
91
|
+
| `marquee` | `useMarquee`, `MarqueeSurface`, `MarqueeBand` (rubber-band selection) |
|
|
92
|
+
| `portal` / `theme-scope` / `tokens` | `Portal` · `ThemeScope` · token name lists + types |
|
|
73
93
|
|
|
74
94
|
API rules: controlled-first, `className` merges (never replaces), `asChild`
|
|
75
95
|
where it makes sense, refs forwarded everywhere, English label props with
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
interface ChipInputProps extends Omit<React.ComponentProps<'div'>, 'onChange'> {
|
|
4
|
+
value: string[];
|
|
5
|
+
onChange: (value: string[]) => void;
|
|
6
|
+
placeholder?: string;
|
|
7
|
+
separators?: string[];
|
|
8
|
+
inputLabel?: string;
|
|
9
|
+
removeLabel?: string;
|
|
10
|
+
disabled?: boolean;
|
|
11
|
+
}
|
|
12
|
+
declare const ChipInput: React.ForwardRefExoticComponent<Omit<ChipInputProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
13
|
+
|
|
14
|
+
export { ChipInput, type ChipInputProps };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"chip-input.js"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
type ChipVariant = 'neutral' | 'primary' | 'success' | 'warning' | 'danger';
|
|
4
|
+
interface ChipListProps extends React.ComponentProps<'div'> {
|
|
5
|
+
items: ReadonlyArray<string | null | undefined>;
|
|
6
|
+
onRemove?: (item: string) => void;
|
|
7
|
+
onItemClick?: (value: string, index: number) => void;
|
|
8
|
+
variant?: ChipVariant;
|
|
9
|
+
emptyText?: string;
|
|
10
|
+
showChevron?: boolean;
|
|
11
|
+
removeLabel?: string;
|
|
12
|
+
}
|
|
13
|
+
declare const ChipList: React.ForwardRefExoticComponent<Omit<ChipListProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
14
|
+
|
|
15
|
+
export { ChipList, type ChipListProps, type ChipVariant };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"chip-list.js"}
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
import { Popover, PopoverTrigger, PopoverContent } from './chunk-CI56SSO2.js';
|
|
2
|
+
import { cn } from './chunk-BLJWR4ZV.js';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { parseISO, isValid, startOfDay, startOfMonth, endOfMonth, startOfWeek, endOfWeek, addDays, format, isSameDay, isSameMonth, setMonth, setYear, subMonths, subYears, addMonths, addYears } from 'date-fns';
|
|
5
|
+
import { Calendar, X, ChevronLeft, ChevronDown, ChevronRight } from 'lucide-react';
|
|
6
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
7
|
+
|
|
8
|
+
function dayKey(date) {
|
|
9
|
+
return format(date, "yyyy-MM-dd");
|
|
10
|
+
}
|
|
11
|
+
var DatePicker = React.forwardRef(
|
|
12
|
+
function DatePicker2({
|
|
13
|
+
value,
|
|
14
|
+
onChange,
|
|
15
|
+
onClear,
|
|
16
|
+
allowClear = false,
|
|
17
|
+
clearLabel = "Clear",
|
|
18
|
+
placeholder = "Select date",
|
|
19
|
+
displayFormat = "dd/MM/yyyy",
|
|
20
|
+
minDate,
|
|
21
|
+
maxDate,
|
|
22
|
+
weekStartsOn = 1,
|
|
23
|
+
disabled = false,
|
|
24
|
+
required,
|
|
25
|
+
id,
|
|
26
|
+
name,
|
|
27
|
+
label = "Choose date",
|
|
28
|
+
prevLabel = "Previous",
|
|
29
|
+
nextLabel = "Next",
|
|
30
|
+
className,
|
|
31
|
+
panelClassName
|
|
32
|
+
}, ref) {
|
|
33
|
+
const autoId = React.useId();
|
|
34
|
+
const hiddenId = id ?? `date-picker-${autoId}`;
|
|
35
|
+
const [open, setOpen] = React.useState(false);
|
|
36
|
+
const [viewMode, setViewMode] = React.useState("days");
|
|
37
|
+
const [currentDate, setCurrentDate] = React.useState(() => /* @__PURE__ */ new Date());
|
|
38
|
+
const [focusedDay, setFocusedDay] = React.useState(() => /* @__PURE__ */ new Date());
|
|
39
|
+
const [yearPage, setYearPage] = React.useState(() => (/* @__PURE__ */ new Date()).getFullYear());
|
|
40
|
+
const gridRef = React.useRef(null);
|
|
41
|
+
const selectedDate = React.useMemo(() => {
|
|
42
|
+
if (!value) return null;
|
|
43
|
+
const parsed = parseISO(value);
|
|
44
|
+
return isValid(parsed) ? parsed : null;
|
|
45
|
+
}, [value]);
|
|
46
|
+
const isDisabled = React.useCallback(
|
|
47
|
+
(date) => Boolean(minDate && date < startOfDay(minDate)) || Boolean(maxDate && date > startOfDay(maxDate)),
|
|
48
|
+
[minDate, maxDate]
|
|
49
|
+
);
|
|
50
|
+
const handleOpenChange = (next) => {
|
|
51
|
+
setOpen(next);
|
|
52
|
+
if (next) {
|
|
53
|
+
const base = selectedDate ?? /* @__PURE__ */ new Date();
|
|
54
|
+
setCurrentDate(base);
|
|
55
|
+
setFocusedDay(base);
|
|
56
|
+
setYearPage(base.getFullYear());
|
|
57
|
+
setViewMode("days");
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
const handleSelect = (date) => {
|
|
61
|
+
if (isDisabled(date)) return;
|
|
62
|
+
onChange(format(date, "yyyy-MM-dd"));
|
|
63
|
+
setOpen(false);
|
|
64
|
+
};
|
|
65
|
+
const handleClear = (event) => {
|
|
66
|
+
event.stopPropagation();
|
|
67
|
+
onChange("");
|
|
68
|
+
onClear?.();
|
|
69
|
+
setOpen(false);
|
|
70
|
+
};
|
|
71
|
+
const moveFocus = (next) => {
|
|
72
|
+
if (isDisabled(next)) return;
|
|
73
|
+
setFocusedDay(next);
|
|
74
|
+
setCurrentDate(
|
|
75
|
+
(current) => isSameMonth(current, next) ? current : startOfMonth(next)
|
|
76
|
+
);
|
|
77
|
+
};
|
|
78
|
+
const handleGridKeyDown = (event) => {
|
|
79
|
+
const step = event.key === "ArrowLeft" ? -1 : event.key === "ArrowRight" ? 1 : event.key === "ArrowUp" ? -7 : event.key === "ArrowDown" ? 7 : 0;
|
|
80
|
+
if (step !== 0) {
|
|
81
|
+
event.preventDefault();
|
|
82
|
+
moveFocus(addDays(focusedDay, step));
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (event.key === "PageUp" || event.key === "PageDown") {
|
|
86
|
+
event.preventDefault();
|
|
87
|
+
const direction = event.key === "PageUp" ? -1 : 1;
|
|
88
|
+
moveFocus(addMonths(focusedDay, direction));
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (event.key === "Home") {
|
|
92
|
+
event.preventDefault();
|
|
93
|
+
moveFocus(startOfMonth(focusedDay));
|
|
94
|
+
} else if (event.key === "End") {
|
|
95
|
+
event.preventDefault();
|
|
96
|
+
moveFocus(endOfMonth(focusedDay));
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
React.useEffect(() => {
|
|
100
|
+
if (!open || viewMode !== "days") return;
|
|
101
|
+
const target = gridRef.current?.querySelector(
|
|
102
|
+
`[data-day="${dayKey(focusedDay)}"]`
|
|
103
|
+
);
|
|
104
|
+
target?.focus();
|
|
105
|
+
}, [open, viewMode, focusedDay]);
|
|
106
|
+
const monthStart = startOfMonth(currentDate);
|
|
107
|
+
const monthEnd = endOfMonth(monthStart);
|
|
108
|
+
const gridStart = startOfWeek(monthStart, { weekStartsOn });
|
|
109
|
+
const gridEnd = endOfWeek(monthEnd, { weekStartsOn });
|
|
110
|
+
const weeks = [];
|
|
111
|
+
let cursor = gridStart;
|
|
112
|
+
while (cursor <= gridEnd) {
|
|
113
|
+
const week = [];
|
|
114
|
+
for (let i = 0; i < 7; i++) {
|
|
115
|
+
week.push(cursor);
|
|
116
|
+
cursor = addDays(cursor, 1);
|
|
117
|
+
}
|
|
118
|
+
weeks.push(week);
|
|
119
|
+
}
|
|
120
|
+
const today = /* @__PURE__ */ new Date();
|
|
121
|
+
const weekdayStart = startOfWeek(/* @__PURE__ */ new Date(), { weekStartsOn });
|
|
122
|
+
const headerTitle = viewMode === "days" ? format(currentDate, "MMMM yyyy") : viewMode === "months" ? format(currentDate, "yyyy") : `${Math.floor(yearPage / 12) * 12} \u2013 ${Math.floor(yearPage / 12) * 12 + 11}`;
|
|
123
|
+
const handlePrev = () => {
|
|
124
|
+
if (viewMode === "days") setCurrentDate(subMonths(currentDate, 1));
|
|
125
|
+
else if (viewMode === "months") setCurrentDate(subYears(currentDate, 1));
|
|
126
|
+
else setYearPage(yearPage - 12);
|
|
127
|
+
};
|
|
128
|
+
const handleNext = () => {
|
|
129
|
+
if (viewMode === "days") setCurrentDate(addMonths(currentDate, 1));
|
|
130
|
+
else if (viewMode === "months") setCurrentDate(addYears(currentDate, 1));
|
|
131
|
+
else setYearPage(yearPage + 12);
|
|
132
|
+
};
|
|
133
|
+
const toggleViewMode = () => {
|
|
134
|
+
if (viewMode !== "years") setViewMode("years");
|
|
135
|
+
};
|
|
136
|
+
const navButtonClass = "cursor-pointer rounded-[var(--as-radius-sm)] p-1.5 text-[var(--as-muted-fg)] transition-colors hover:bg-[var(--as-muted)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]";
|
|
137
|
+
return /* @__PURE__ */ jsx(
|
|
138
|
+
"div",
|
|
139
|
+
{
|
|
140
|
+
ref,
|
|
141
|
+
"data-as": "date-picker",
|
|
142
|
+
className: cn("relative w-full", className),
|
|
143
|
+
children: /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: handleOpenChange, children: [
|
|
144
|
+
/* @__PURE__ */ jsxs("span", { className: "relative inline-flex w-full", children: [
|
|
145
|
+
/* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, disabled, children: /* @__PURE__ */ jsxs(
|
|
146
|
+
"button",
|
|
147
|
+
{
|
|
148
|
+
type: "button",
|
|
149
|
+
"aria-label": label,
|
|
150
|
+
className: cn(
|
|
151
|
+
"flex h-9 w-full items-center gap-2 rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface)] px-3 py-2 text-left text-sm text-[var(--as-fg)] transition-colors hover:border-[var(--as-fg)]/30 focus-visible:border-[var(--as-focus-ring)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)] disabled:cursor-not-allowed disabled:opacity-50",
|
|
152
|
+
allowClear && selectedDate && !disabled && "pr-8"
|
|
153
|
+
),
|
|
154
|
+
children: [
|
|
155
|
+
/* @__PURE__ */ jsx(
|
|
156
|
+
Calendar,
|
|
157
|
+
{
|
|
158
|
+
className: "size-4 shrink-0 text-[var(--as-muted-fg)]",
|
|
159
|
+
"aria-hidden": true
|
|
160
|
+
}
|
|
161
|
+
),
|
|
162
|
+
/* @__PURE__ */ jsx(
|
|
163
|
+
"span",
|
|
164
|
+
{
|
|
165
|
+
className: cn(
|
|
166
|
+
"flex-1 truncate text-left",
|
|
167
|
+
!selectedDate && "text-[var(--as-muted-fg)]"
|
|
168
|
+
),
|
|
169
|
+
children: selectedDate ? format(selectedDate, displayFormat) : placeholder
|
|
170
|
+
}
|
|
171
|
+
)
|
|
172
|
+
]
|
|
173
|
+
}
|
|
174
|
+
) }),
|
|
175
|
+
allowClear && selectedDate && !disabled ? /* @__PURE__ */ jsx(
|
|
176
|
+
"button",
|
|
177
|
+
{
|
|
178
|
+
type: "button",
|
|
179
|
+
"aria-label": clearLabel,
|
|
180
|
+
onClick: handleClear,
|
|
181
|
+
className: "absolute right-2 top-1/2 -translate-y-1/2 cursor-pointer rounded-full p-0.5 text-[var(--as-muted-fg)] transition-colors hover:bg-[var(--as-muted)] hover:text-[var(--as-fg)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]",
|
|
182
|
+
children: /* @__PURE__ */ jsx(X, { className: "size-3.5", "aria-hidden": true })
|
|
183
|
+
}
|
|
184
|
+
) : null
|
|
185
|
+
] }),
|
|
186
|
+
/* @__PURE__ */ jsx(
|
|
187
|
+
"input",
|
|
188
|
+
{
|
|
189
|
+
type: "hidden",
|
|
190
|
+
id: hiddenId,
|
|
191
|
+
name: name ?? hiddenId,
|
|
192
|
+
value: value ?? "",
|
|
193
|
+
required
|
|
194
|
+
}
|
|
195
|
+
),
|
|
196
|
+
/* @__PURE__ */ jsxs(
|
|
197
|
+
PopoverContent,
|
|
198
|
+
{
|
|
199
|
+
align: "start",
|
|
200
|
+
sideOffset: 4,
|
|
201
|
+
collisionPadding: 8,
|
|
202
|
+
"aria-label": label,
|
|
203
|
+
onOpenAutoFocus: (event) => {
|
|
204
|
+
event.preventDefault();
|
|
205
|
+
const panel = event.currentTarget;
|
|
206
|
+
const target = panel?.querySelector(
|
|
207
|
+
'[data-day][tabindex="0"]'
|
|
208
|
+
);
|
|
209
|
+
target?.focus();
|
|
210
|
+
},
|
|
211
|
+
className: cn(
|
|
212
|
+
"as-anim-pop z-50 w-[280px] rounded-[var(--as-radius-lg)] border border-[var(--as-border)] bg-[var(--as-surface-raised)] p-2 text-[var(--as-fg)] shadow-[var(--as-shadow-3)] outline-none",
|
|
213
|
+
panelClassName
|
|
214
|
+
),
|
|
215
|
+
children: [
|
|
216
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-between px-1 pt-1", children: [
|
|
217
|
+
/* @__PURE__ */ jsx(
|
|
218
|
+
"button",
|
|
219
|
+
{
|
|
220
|
+
type: "button",
|
|
221
|
+
"aria-label": prevLabel,
|
|
222
|
+
onClick: handlePrev,
|
|
223
|
+
className: navButtonClass,
|
|
224
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "size-5", "aria-hidden": true })
|
|
225
|
+
}
|
|
226
|
+
),
|
|
227
|
+
/* @__PURE__ */ jsxs(
|
|
228
|
+
"button",
|
|
229
|
+
{
|
|
230
|
+
type: "button",
|
|
231
|
+
onClick: toggleViewMode,
|
|
232
|
+
className: "flex cursor-pointer items-center rounded-[var(--as-radius-sm)] px-3 py-1.5 text-sm font-semibold transition-colors hover:bg-[var(--as-muted)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]",
|
|
233
|
+
children: [
|
|
234
|
+
/* @__PURE__ */ jsx("span", { children: headerTitle }),
|
|
235
|
+
viewMode !== "years" ? /* @__PURE__ */ jsx(ChevronDown, { className: "ml-1 size-4", "aria-hidden": true }) : null
|
|
236
|
+
]
|
|
237
|
+
}
|
|
238
|
+
),
|
|
239
|
+
/* @__PURE__ */ jsx(
|
|
240
|
+
"button",
|
|
241
|
+
{
|
|
242
|
+
type: "button",
|
|
243
|
+
"aria-label": nextLabel,
|
|
244
|
+
onClick: handleNext,
|
|
245
|
+
className: navButtonClass,
|
|
246
|
+
children: /* @__PURE__ */ jsx(ChevronRight, { className: "size-5", "aria-hidden": true })
|
|
247
|
+
}
|
|
248
|
+
)
|
|
249
|
+
] }),
|
|
250
|
+
viewMode === "days" ? /* @__PURE__ */ jsx("div", { className: "px-2 pb-2", children: /* @__PURE__ */ jsxs(
|
|
251
|
+
"div",
|
|
252
|
+
{
|
|
253
|
+
ref: gridRef,
|
|
254
|
+
role: "grid",
|
|
255
|
+
"aria-label": label,
|
|
256
|
+
onKeyDown: handleGridKeyDown,
|
|
257
|
+
children: [
|
|
258
|
+
/* @__PURE__ */ jsx("div", { role: "row", className: "mb-2 grid grid-cols-7 gap-1", children: Array.from({ length: 7 }, (_, i) => /* @__PURE__ */ jsx(
|
|
259
|
+
"div",
|
|
260
|
+
{
|
|
261
|
+
role: "columnheader",
|
|
262
|
+
className: "py-1 text-center text-xs font-medium text-[var(--as-muted-fg)]",
|
|
263
|
+
children: format(addDays(weekdayStart, i), "EEEEE")
|
|
264
|
+
},
|
|
265
|
+
i
|
|
266
|
+
)) }),
|
|
267
|
+
weeks.map((week, weekIndex) => /* @__PURE__ */ jsx("div", { role: "row", className: "grid grid-cols-7 gap-1", children: week.map((day) => {
|
|
268
|
+
const isSelected = selectedDate ? isSameDay(day, selectedDate) : false;
|
|
269
|
+
const isFocused = isSameDay(day, focusedDay);
|
|
270
|
+
const outside = !isSameMonth(day, monthStart);
|
|
271
|
+
const isToday = isSameDay(day, today);
|
|
272
|
+
const dayDisabled = isDisabled(day);
|
|
273
|
+
return /* @__PURE__ */ jsx(
|
|
274
|
+
"div",
|
|
275
|
+
{
|
|
276
|
+
role: "gridcell",
|
|
277
|
+
"aria-selected": isSelected,
|
|
278
|
+
className: "p-0",
|
|
279
|
+
children: /* @__PURE__ */ jsx(
|
|
280
|
+
"button",
|
|
281
|
+
{
|
|
282
|
+
type: "button",
|
|
283
|
+
"data-day": dayKey(day),
|
|
284
|
+
tabIndex: isFocused ? 0 : -1,
|
|
285
|
+
disabled: dayDisabled,
|
|
286
|
+
"aria-current": isToday ? "date" : void 0,
|
|
287
|
+
onClick: () => handleSelect(day),
|
|
288
|
+
className: cn(
|
|
289
|
+
"flex size-8 cursor-pointer items-center justify-center rounded-full text-sm transition-colors focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]",
|
|
290
|
+
outside ? "text-[var(--as-muted-fg)] opacity-50" : "text-[var(--as-fg)]",
|
|
291
|
+
isSelected && "bg-[var(--as-primary)] font-medium text-[var(--as-primary-fg)] hover:brightness-110",
|
|
292
|
+
!isSelected && isToday && "bg-[color-mix(in_srgb,var(--as-primary)_12%,transparent)] font-medium text-[var(--as-primary)]",
|
|
293
|
+
!isSelected && !dayDisabled && "hover:bg-[var(--as-muted)]",
|
|
294
|
+
dayDisabled && "cursor-not-allowed opacity-30 hover:bg-transparent"
|
|
295
|
+
),
|
|
296
|
+
children: format(day, "d")
|
|
297
|
+
}
|
|
298
|
+
)
|
|
299
|
+
},
|
|
300
|
+
dayKey(day)
|
|
301
|
+
);
|
|
302
|
+
}) }, weekIndex))
|
|
303
|
+
]
|
|
304
|
+
}
|
|
305
|
+
) }) : null,
|
|
306
|
+
viewMode === "months" ? /* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 gap-2 p-2", children: Array.from({ length: 12 }, (_, i) => {
|
|
307
|
+
const isSelected = selectedDate?.getMonth() === i && selectedDate?.getFullYear() === currentDate.getFullYear();
|
|
308
|
+
return /* @__PURE__ */ jsx(
|
|
309
|
+
"button",
|
|
310
|
+
{
|
|
311
|
+
type: "button",
|
|
312
|
+
onClick: () => {
|
|
313
|
+
setCurrentDate(setMonth(currentDate, i));
|
|
314
|
+
setViewMode("days");
|
|
315
|
+
},
|
|
316
|
+
className: cn(
|
|
317
|
+
"cursor-pointer rounded-[var(--as-radius)] py-3 text-sm font-medium transition-colors focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]",
|
|
318
|
+
isSelected ? "bg-[var(--as-primary)] text-[var(--as-primary-fg)]" : "text-[var(--as-fg)] hover:bg-[var(--as-muted)]"
|
|
319
|
+
),
|
|
320
|
+
children: format(setMonth(/* @__PURE__ */ new Date(), i), "MMM")
|
|
321
|
+
},
|
|
322
|
+
i
|
|
323
|
+
);
|
|
324
|
+
}) }) : null,
|
|
325
|
+
viewMode === "years" ? /* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 gap-2 p-2", children: Array.from({ length: 12 }, (_, i) => {
|
|
326
|
+
const year = Math.floor(yearPage / 12) * 12 + i;
|
|
327
|
+
const isSelected = selectedDate?.getFullYear() === year;
|
|
328
|
+
return /* @__PURE__ */ jsx(
|
|
329
|
+
"button",
|
|
330
|
+
{
|
|
331
|
+
type: "button",
|
|
332
|
+
onClick: () => {
|
|
333
|
+
setCurrentDate(setYear(currentDate, year));
|
|
334
|
+
setFocusedDay((current) => setYear(current, year));
|
|
335
|
+
setViewMode("months");
|
|
336
|
+
},
|
|
337
|
+
className: cn(
|
|
338
|
+
"cursor-pointer rounded-[var(--as-radius)] py-3 text-sm font-medium transition-colors focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]",
|
|
339
|
+
isSelected ? "bg-[var(--as-primary)] text-[var(--as-primary-fg)]" : "text-[var(--as-fg)] hover:bg-[var(--as-muted)]"
|
|
340
|
+
),
|
|
341
|
+
children: year
|
|
342
|
+
},
|
|
343
|
+
year
|
|
344
|
+
);
|
|
345
|
+
}) }) : null
|
|
346
|
+
]
|
|
347
|
+
}
|
|
348
|
+
)
|
|
349
|
+
] })
|
|
350
|
+
}
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
);
|
|
354
|
+
|
|
355
|
+
export { DatePicker };
|
|
356
|
+
//# sourceMappingURL=chunk-2KO2YIZN.js.map
|
|
357
|
+
//# sourceMappingURL=chunk-2KO2YIZN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/date-picker/DatePicker.tsx"],"names":["DatePicker","CalendarIcon"],"mappings":"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","file":"chunk-2KO2YIZN.js","sourcesContent":["import * as React from 'react'\nimport {\n addDays,\n addMonths,\n addYears,\n endOfMonth,\n endOfWeek,\n format,\n isSameDay,\n isSameMonth,\n isValid,\n parseISO,\n setMonth,\n setYear,\n startOfDay,\n startOfMonth,\n startOfWeek,\n subMonths,\n subYears,\n} from 'date-fns'\nimport {\n Calendar as CalendarIcon,\n ChevronDown,\n ChevronLeft,\n ChevronRight,\n X,\n} from 'lucide-react'\nimport { cn } from '../../lib/utils'\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from '../popover'\n\ntype ViewMode = 'days' | 'months' | 'years'\n\nexport interface DatePickerProps {\n value: string | null | undefined\n onChange: (value: string) => void\n onClear?: () => void\n allowClear?: boolean\n clearLabel?: string\n placeholder?: string\n displayFormat?: string\n minDate?: Date\n maxDate?: Date\n weekStartsOn?: 0 | 1\n disabled?: boolean\n required?: boolean\n id?: string\n name?: string\n label?: string\n prevLabel?: string\n nextLabel?: string\n className?: string\n panelClassName?: string\n}\n\nfunction dayKey(date: Date): string {\n return format(date, 'yyyy-MM-dd')\n}\n\nexport const DatePicker = React.forwardRef<HTMLDivElement, DatePickerProps>(\n function DatePicker(\n {\n value,\n onChange,\n onClear,\n allowClear = false,\n clearLabel = 'Clear',\n placeholder = 'Select date',\n displayFormat = 'dd/MM/yyyy',\n minDate,\n maxDate,\n weekStartsOn = 1,\n disabled = false,\n required,\n id,\n name,\n label = 'Choose date',\n prevLabel = 'Previous',\n nextLabel = 'Next',\n className,\n panelClassName,\n },\n ref,\n ) {\n const autoId = React.useId()\n const hiddenId = id ?? `date-picker-${autoId}`\n const [open, setOpen] = React.useState(false)\n const [viewMode, setViewMode] = React.useState<ViewMode>('days')\n const [currentDate, setCurrentDate] = React.useState<Date>(() => new Date())\n const [focusedDay, setFocusedDay] = React.useState<Date>(() => new Date())\n const [yearPage, setYearPage] = React.useState(() => new Date().getFullYear())\n const gridRef = React.useRef<HTMLDivElement>(null)\n\n const selectedDate = React.useMemo(() => {\n if (!value) return null\n const parsed = parseISO(value)\n return isValid(parsed) ? parsed : null\n }, [value])\n\n const isDisabled = React.useCallback(\n (date: Date) =>\n Boolean(minDate && date < startOfDay(minDate)) ||\n Boolean(maxDate && date > startOfDay(maxDate)),\n [minDate, maxDate],\n )\n\n const handleOpenChange = (next: boolean) => {\n setOpen(next)\n if (next) {\n const base = selectedDate ?? new Date()\n setCurrentDate(base)\n setFocusedDay(base)\n setYearPage(base.getFullYear())\n setViewMode('days')\n }\n }\n\n const handleSelect = (date: Date) => {\n if (isDisabled(date)) return\n onChange(format(date, 'yyyy-MM-dd'))\n setOpen(false)\n }\n\n const handleClear = (event: React.MouseEvent) => {\n event.stopPropagation()\n onChange('')\n onClear?.()\n setOpen(false)\n }\n\n const moveFocus = (next: Date) => {\n if (isDisabled(next)) return\n setFocusedDay(next)\n setCurrentDate((current) =>\n isSameMonth(current, next) ? current : startOfMonth(next),\n )\n }\n\n const handleGridKeyDown = (event: React.KeyboardEvent) => {\n const step =\n event.key === 'ArrowLeft'\n ? -1\n : event.key === 'ArrowRight'\n ? 1\n : event.key === 'ArrowUp'\n ? -7\n : event.key === 'ArrowDown'\n ? 7\n : 0\n if (step !== 0) {\n event.preventDefault()\n moveFocus(addDays(focusedDay, step))\n return\n }\n if (event.key === 'PageUp' || event.key === 'PageDown') {\n event.preventDefault()\n const direction = event.key === 'PageUp' ? -1 : 1\n moveFocus(addMonths(focusedDay, direction))\n return\n }\n if (event.key === 'Home') {\n event.preventDefault()\n moveFocus(startOfMonth(focusedDay))\n } else if (event.key === 'End') {\n event.preventDefault()\n moveFocus(endOfMonth(focusedDay))\n }\n }\n\n React.useEffect(() => {\n if (!open || viewMode !== 'days') return\n const target = gridRef.current?.querySelector<HTMLButtonElement>(\n `[data-day=\"${dayKey(focusedDay)}\"]`,\n )\n target?.focus()\n }, [open, viewMode, focusedDay])\n\n const monthStart = startOfMonth(currentDate)\n const monthEnd = endOfMonth(monthStart)\n const gridStart = startOfWeek(monthStart, { weekStartsOn })\n const gridEnd = endOfWeek(monthEnd, { weekStartsOn })\n\n const weeks: Date[][] = []\n let cursor = gridStart\n while (cursor <= gridEnd) {\n const week: Date[] = []\n for (let i = 0; i < 7; i++) {\n week.push(cursor)\n cursor = addDays(cursor, 1)\n }\n weeks.push(week)\n }\n\n const today = new Date()\n const weekdayStart = startOfWeek(new Date(), { weekStartsOn })\n\n const headerTitle =\n viewMode === 'days'\n ? format(currentDate, 'MMMM yyyy')\n : viewMode === 'months'\n ? format(currentDate, 'yyyy')\n : `${Math.floor(yearPage / 12) * 12} – ${Math.floor(yearPage / 12) * 12 + 11}`\n\n const handlePrev = () => {\n if (viewMode === 'days') setCurrentDate(subMonths(currentDate, 1))\n else if (viewMode === 'months') setCurrentDate(subYears(currentDate, 1))\n else setYearPage(yearPage - 12)\n }\n\n const handleNext = () => {\n if (viewMode === 'days') setCurrentDate(addMonths(currentDate, 1))\n else if (viewMode === 'months') setCurrentDate(addYears(currentDate, 1))\n else setYearPage(yearPage + 12)\n }\n\n const toggleViewMode = () => {\n if (viewMode !== 'years') setViewMode('years')\n }\n\n const navButtonClass =\n 'cursor-pointer rounded-[var(--as-radius-sm)] p-1.5 text-[var(--as-muted-fg)] transition-colors hover:bg-[var(--as-muted)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]'\n\n return (\n <div\n ref={ref}\n data-as=\"date-picker\"\n className={cn('relative w-full', className)}\n >\n <Popover open={open} onOpenChange={handleOpenChange}>\n <span className=\"relative inline-flex w-full\">\n <PopoverTrigger asChild disabled={disabled}>\n <button\n type=\"button\"\n aria-label={label}\n className={cn(\n 'flex h-9 w-full items-center gap-2 rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface)] px-3 py-2 text-left text-sm text-[var(--as-fg)] transition-colors hover:border-[var(--as-fg)]/30 focus-visible:border-[var(--as-focus-ring)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)] disabled:cursor-not-allowed disabled:opacity-50',\n allowClear && selectedDate && !disabled && 'pr-8',\n )}\n >\n <CalendarIcon\n className=\"size-4 shrink-0 text-[var(--as-muted-fg)]\"\n aria-hidden\n />\n <span\n className={cn(\n 'flex-1 truncate text-left',\n !selectedDate && 'text-[var(--as-muted-fg)]',\n )}\n >\n {selectedDate\n ? format(selectedDate, displayFormat)\n : placeholder}\n </span>\n </button>\n </PopoverTrigger>\n {allowClear && selectedDate && !disabled ? (\n <button\n type=\"button\"\n aria-label={clearLabel}\n onClick={handleClear}\n className=\"absolute right-2 top-1/2 -translate-y-1/2 cursor-pointer rounded-full p-0.5 text-[var(--as-muted-fg)] transition-colors hover:bg-[var(--as-muted)] hover:text-[var(--as-fg)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]\"\n >\n <X className=\"size-3.5\" aria-hidden />\n </button>\n ) : null}\n </span>\n\n <input\n type=\"hidden\"\n id={hiddenId}\n name={name ?? hiddenId}\n value={value ?? ''}\n required={required}\n />\n\n <PopoverContent\n align=\"start\"\n sideOffset={4}\n collisionPadding={8}\n aria-label={label}\n onOpenAutoFocus={(event) => {\n event.preventDefault()\n const panel = event.currentTarget as HTMLElement | null\n const target = panel?.querySelector<HTMLButtonElement>(\n '[data-day][tabindex=\"0\"]',\n )\n target?.focus()\n }}\n className={cn(\n 'as-anim-pop z-50 w-[280px] rounded-[var(--as-radius-lg)] border border-[var(--as-border)] bg-[var(--as-surface-raised)] p-2 text-[var(--as-fg)] shadow-[var(--as-shadow-3)] outline-none',\n panelClassName,\n )}\n >\n <div className=\"mb-2 flex items-center justify-between px-1 pt-1\">\n <button\n type=\"button\"\n aria-label={prevLabel}\n onClick={handlePrev}\n className={navButtonClass}\n >\n <ChevronLeft className=\"size-5\" aria-hidden />\n </button>\n <button\n type=\"button\"\n onClick={toggleViewMode}\n className=\"flex cursor-pointer items-center rounded-[var(--as-radius-sm)] px-3 py-1.5 text-sm font-semibold transition-colors hover:bg-[var(--as-muted)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]\"\n >\n <span>{headerTitle}</span>\n {viewMode !== 'years' ? (\n <ChevronDown className=\"ml-1 size-4\" aria-hidden />\n ) : null}\n </button>\n <button\n type=\"button\"\n aria-label={nextLabel}\n onClick={handleNext}\n className={navButtonClass}\n >\n <ChevronRight className=\"size-5\" aria-hidden />\n </button>\n </div>\n\n {viewMode === 'days' ? (\n <div className=\"px-2 pb-2\">\n <div\n ref={gridRef}\n role=\"grid\"\n aria-label={label}\n onKeyDown={handleGridKeyDown}\n >\n <div role=\"row\" className=\"mb-2 grid grid-cols-7 gap-1\">\n {Array.from({ length: 7 }, (_, i) => (\n <div\n key={i}\n role=\"columnheader\"\n className=\"py-1 text-center text-xs font-medium text-[var(--as-muted-fg)]\"\n >\n {format(addDays(weekdayStart, i), 'EEEEE')}\n </div>\n ))}\n </div>\n {weeks.map((week, weekIndex) => (\n <div key={weekIndex} role=\"row\" className=\"grid grid-cols-7 gap-1\">\n {week.map((day) => {\n const isSelected = selectedDate\n ? isSameDay(day, selectedDate)\n : false\n const isFocused = isSameDay(day, focusedDay)\n const outside = !isSameMonth(day, monthStart)\n const isToday = isSameDay(day, today)\n const dayDisabled = isDisabled(day)\n return (\n <div\n key={dayKey(day)}\n role=\"gridcell\"\n aria-selected={isSelected}\n className=\"p-0\"\n >\n <button\n type=\"button\"\n data-day={dayKey(day)}\n tabIndex={isFocused ? 0 : -1}\n disabled={dayDisabled}\n aria-current={isToday ? 'date' : undefined}\n onClick={() => handleSelect(day)}\n className={cn(\n 'flex size-8 cursor-pointer items-center justify-center rounded-full text-sm transition-colors focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]',\n outside\n ? 'text-[var(--as-muted-fg)] opacity-50'\n : 'text-[var(--as-fg)]',\n isSelected &&\n 'bg-[var(--as-primary)] font-medium text-[var(--as-primary-fg)] hover:brightness-110',\n !isSelected &&\n isToday &&\n 'bg-[color-mix(in_srgb,var(--as-primary)_12%,transparent)] font-medium text-[var(--as-primary)]',\n !isSelected && !dayDisabled && 'hover:bg-[var(--as-muted)]',\n dayDisabled &&\n 'cursor-not-allowed opacity-30 hover:bg-transparent',\n )}\n >\n {format(day, 'd')}\n </button>\n </div>\n )\n })}\n </div>\n ))}\n </div>\n </div>\n ) : null}\n\n {viewMode === 'months' ? (\n <div className=\"grid grid-cols-3 gap-2 p-2\">\n {Array.from({ length: 12 }, (_, i) => {\n const isSelected =\n selectedDate?.getMonth() === i &&\n selectedDate?.getFullYear() === currentDate.getFullYear()\n return (\n <button\n key={i}\n type=\"button\"\n onClick={() => {\n setCurrentDate(setMonth(currentDate, i))\n setViewMode('days')\n }}\n className={cn(\n 'cursor-pointer rounded-[var(--as-radius)] py-3 text-sm font-medium transition-colors focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]',\n isSelected\n ? 'bg-[var(--as-primary)] text-[var(--as-primary-fg)]'\n : 'text-[var(--as-fg)] hover:bg-[var(--as-muted)]',\n )}\n >\n {format(setMonth(new Date(), i), 'MMM')}\n </button>\n )\n })}\n </div>\n ) : null}\n\n {viewMode === 'years' ? (\n <div className=\"grid grid-cols-3 gap-2 p-2\">\n {Array.from({ length: 12 }, (_, i) => {\n const year = Math.floor(yearPage / 12) * 12 + i\n const isSelected = selectedDate?.getFullYear() === year\n return (\n <button\n key={year}\n type=\"button\"\n onClick={() => {\n setCurrentDate(setYear(currentDate, year))\n setFocusedDay((current) => setYear(current, year))\n setViewMode('months')\n }}\n className={cn(\n 'cursor-pointer rounded-[var(--as-radius)] py-3 text-sm font-medium transition-colors focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]',\n isSelected\n ? 'bg-[var(--as-primary)] text-[var(--as-primary-fg)]'\n : 'text-[var(--as-fg)] hover:bg-[var(--as-muted)]',\n )}\n >\n {year}\n </button>\n )\n })}\n </div>\n ) : null}\n </PopoverContent>\n </Popover>\n </div>\n )\n },\n)\n"]}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { cn } from './chunk-BLJWR4ZV.js';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
function pct(value, domainMin, domainMax) {
|
|
6
|
+
if (domainMax === domainMin) return 0;
|
|
7
|
+
return (value - domainMin) / (domainMax - domainMin) * 100;
|
|
8
|
+
}
|
|
9
|
+
function fmt(value) {
|
|
10
|
+
return Number.isInteger(value) ? value.toLocaleString() : value.toFixed(1).replace(/\.0$/, "");
|
|
11
|
+
}
|
|
12
|
+
var RangeBar = React.forwardRef(
|
|
13
|
+
function RangeBar2({ low, high, min, max, value, unit, label, valueLabel = "You", className, ...props }, ref) {
|
|
14
|
+
if (!Number.isFinite(low) || !Number.isFinite(high) || high <= low) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
const span = high - low;
|
|
18
|
+
let domainMin = min ?? low - span;
|
|
19
|
+
let domainMax = max ?? high + span;
|
|
20
|
+
if (domainMin < 0 && low >= 0) {
|
|
21
|
+
const shift = -domainMin;
|
|
22
|
+
domainMin = 0;
|
|
23
|
+
domainMax += shift;
|
|
24
|
+
}
|
|
25
|
+
if (domainMax <= domainMin) domainMax = domainMin + span;
|
|
26
|
+
const left = Math.max(0, Math.min(100, pct(low, domainMin, domainMax)));
|
|
27
|
+
const right = Math.max(0, Math.min(100, pct(high, domainMin, domainMax)));
|
|
28
|
+
const valuePct = value !== null && value !== void 0 && Number.isFinite(value) ? Math.max(0, Math.min(100, pct(value, domainMin, domainMax))) : null;
|
|
29
|
+
return /* @__PURE__ */ jsxs(
|
|
30
|
+
"div",
|
|
31
|
+
{
|
|
32
|
+
ref,
|
|
33
|
+
"data-as": "range-bar",
|
|
34
|
+
className: cn("w-full", className),
|
|
35
|
+
...props,
|
|
36
|
+
children: [
|
|
37
|
+
label ? /* @__PURE__ */ jsx("p", { className: "mb-1 text-xs text-[var(--as-muted-fg)]", children: label }) : null,
|
|
38
|
+
/* @__PURE__ */ jsxs("div", { className: "relative h-3.5 w-full rounded-full bg-[var(--as-muted)]", children: [
|
|
39
|
+
/* @__PURE__ */ jsx(
|
|
40
|
+
"div",
|
|
41
|
+
{
|
|
42
|
+
className: "absolute h-full rounded-full bg-[color-mix(in_srgb,var(--as-primary)_30%,transparent)]",
|
|
43
|
+
style: { left: `${left}%`, width: `${Math.max(0, right - left)}%` },
|
|
44
|
+
title: `${label ? `${label}: ` : ""}${fmt(low)} \u2013 ${fmt(high)}${unit ? ` ${unit}` : ""}`
|
|
45
|
+
}
|
|
46
|
+
),
|
|
47
|
+
valuePct !== null ? /* @__PURE__ */ jsx(
|
|
48
|
+
"div",
|
|
49
|
+
{
|
|
50
|
+
className: "absolute top-1/2 size-2.5 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-[var(--as-surface-raised)] bg-[var(--as-primary)] shadow-[var(--as-shadow-1)]",
|
|
51
|
+
style: { left: `${valuePct}%` },
|
|
52
|
+
title: `${valueLabel}: ${fmt(value)}${unit ? ` ${unit}` : ""}`
|
|
53
|
+
}
|
|
54
|
+
) : null
|
|
55
|
+
] }),
|
|
56
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-0.5 flex justify-between text-[10px] text-[var(--as-muted-fg)]", children: [
|
|
57
|
+
/* @__PURE__ */ jsx("span", { children: fmt(domainMin) }),
|
|
58
|
+
/* @__PURE__ */ jsxs("span", { className: "font-medium", children: [
|
|
59
|
+
fmt(low),
|
|
60
|
+
" \u2013 ",
|
|
61
|
+
fmt(high),
|
|
62
|
+
unit ? ` ${unit}` : ""
|
|
63
|
+
] }),
|
|
64
|
+
/* @__PURE__ */ jsx("span", { children: fmt(domainMax) })
|
|
65
|
+
] })
|
|
66
|
+
]
|
|
67
|
+
}
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
export { RangeBar };
|
|
73
|
+
//# sourceMappingURL=chunk-5AVBKEDQ.js.map
|
|
74
|
+
//# sourceMappingURL=chunk-5AVBKEDQ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/range-bar/RangeBar.tsx"],"names":["RangeBar"],"mappings":"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","file":"chunk-5AVBKEDQ.js","sourcesContent":["import * as React from 'react'\nimport { cn } from '../../lib/utils'\n\nexport interface RangeBarProps extends React.ComponentProps<'div'> {\n low: number\n high: number\n min?: number\n max?: number\n value?: number | null\n unit?: string\n label?: string\n valueLabel?: string\n}\n\nfunction pct(value: number, domainMin: number, domainMax: number): number {\n if (domainMax === domainMin) return 0\n return ((value - domainMin) / (domainMax - domainMin)) * 100\n}\n\nfunction fmt(value: number): string {\n return Number.isInteger(value)\n ? value.toLocaleString()\n : value.toFixed(1).replace(/\\.0$/, '')\n}\n\nexport const RangeBar = React.forwardRef<HTMLDivElement, RangeBarProps>(\n function RangeBar(\n { low, high, min, max, value, unit, label, valueLabel = 'You', className, ...props },\n ref,\n ) {\n if (!Number.isFinite(low) || !Number.isFinite(high) || high <= low) {\n return null\n }\n\n const span = high - low\n let domainMin = min ?? low - span\n let domainMax = max ?? high + span\n if (domainMin < 0 && low >= 0) {\n const shift = -domainMin\n domainMin = 0\n domainMax += shift\n }\n if (domainMax <= domainMin) domainMax = domainMin + span\n\n const left = Math.max(0, Math.min(100, pct(low, domainMin, domainMax)))\n const right = Math.max(0, Math.min(100, pct(high, domainMin, domainMax)))\n const valuePct =\n value !== null && value !== undefined && Number.isFinite(value)\n ? Math.max(0, Math.min(100, pct(value, domainMin, domainMax)))\n : null\n\n return (\n <div\n ref={ref}\n data-as=\"range-bar\"\n className={cn('w-full', className)}\n {...props}\n >\n {label ? (\n <p className=\"mb-1 text-xs text-[var(--as-muted-fg)]\">{label}</p>\n ) : null}\n <div className=\"relative h-3.5 w-full rounded-full bg-[var(--as-muted)]\">\n <div\n className=\"absolute h-full rounded-full bg-[color-mix(in_srgb,var(--as-primary)_30%,transparent)]\"\n style={{ left: `${left}%`, width: `${Math.max(0, right - left)}%` }}\n title={`${label ? `${label}: ` : ''}${fmt(low)} – ${fmt(high)}${unit ? ` ${unit}` : ''}`}\n />\n {valuePct !== null ? (\n <div\n className=\"absolute top-1/2 size-2.5 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-[var(--as-surface-raised)] bg-[var(--as-primary)] shadow-[var(--as-shadow-1)]\"\n style={{ left: `${valuePct}%` }}\n title={`${valueLabel}: ${fmt(value as number)}${unit ? ` ${unit}` : ''}`}\n />\n ) : null}\n </div>\n <div className=\"mt-0.5 flex justify-between text-[10px] text-[var(--as-muted-fg)]\">\n <span>{fmt(domainMin)}</span>\n <span className=\"font-medium\">\n {fmt(low)} – {fmt(high)}\n {unit ? ` ${unit}` : ''}\n </span>\n <span>{fmt(domainMax)}</span>\n </div>\n </div>\n )\n },\n)\n"]}
|