@sia-ui/cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +51 -0
- package/dist/diff.mjs +91 -0
- package/dist/index.js +325 -0
- package/dist/lifecycle.mjs +242 -0
- package/dist/lock.mjs +92 -0
- package/dist/registry/package/components/Accordion/index.tsx +172 -0
- package/dist/registry/package/components/Accordion/styles.css +97 -0
- package/dist/registry/package/components/Alert/index.tsx +31 -0
- package/dist/registry/package/components/Alert/styles.css +12 -0
- package/dist/registry/package/components/AmountDisplay/index.tsx +11 -0
- package/dist/registry/package/components/AmountDisplay/styles.css +9 -0
- package/dist/registry/package/components/AppShell/index.tsx +6 -0
- package/dist/registry/package/components/AppShell/styles.css +7 -0
- package/dist/registry/package/components/AspectRatio/index.tsx +29 -0
- package/dist/registry/package/components/AspectRatio/styles.css +15 -0
- package/dist/registry/package/components/AsyncState/index.tsx +22 -0
- package/dist/registry/package/components/AsyncState/styles.css +1 -0
- package/dist/registry/package/components/AuditMeta/index.tsx +44 -0
- package/dist/registry/package/components/AuditMeta/styles.css +12 -0
- package/dist/registry/package/components/AuthLayout/index.tsx +39 -0
- package/dist/registry/package/components/AuthLayout/styles.css +6 -0
- package/dist/registry/package/components/Autocomplete/index.tsx +8 -0
- package/dist/registry/package/components/Avatar/index.tsx +23 -0
- package/dist/registry/package/components/Avatar/styles.css +5 -0
- package/dist/registry/package/components/Badge/index.tsx +62 -0
- package/dist/registry/package/components/Badge/styles.css +26 -0
- package/dist/registry/package/components/Breadcrumb/index.tsx +131 -0
- package/dist/registry/package/components/Breadcrumb/styles.css +76 -0
- package/dist/registry/package/components/Button/index.tsx +91 -0
- package/dist/registry/package/components/Button/styles.css +111 -0
- package/dist/registry/package/components/Calendar/index.tsx +192 -0
- package/dist/registry/package/components/Card/index.tsx +8 -0
- package/dist/registry/package/components/Card/styles.css +4 -0
- package/dist/registry/package/components/Chart/index.tsx +437 -0
- package/dist/registry/package/components/Chart/styles.css +182 -0
- package/dist/registry/package/components/Checkbox/index.tsx +193 -0
- package/dist/registry/package/components/Checkbox/styles.css +39 -0
- package/dist/registry/package/components/ColorPicker/index.tsx +6 -0
- package/dist/registry/package/components/ColorPicker/styles.css +2 -0
- package/dist/registry/package/components/CommandPalette/index.tsx +7 -0
- package/dist/registry/package/components/CommandPalette/styles.css +8 -0
- package/dist/registry/package/components/ConfirmDialog/index.tsx +24 -0
- package/dist/registry/package/components/Container/index.tsx +11 -0
- package/dist/registry/package/components/Container/styles.css +1 -0
- package/dist/registry/package/components/Countdown/index.tsx +3 -0
- package/dist/registry/package/components/CrudPage/index.tsx +9 -0
- package/dist/registry/package/components/CrudPage/styles.css +1 -0
- package/dist/registry/package/components/CurrencyInput/index.tsx +15 -0
- package/dist/registry/package/components/CurrencyInput/styles.css +1 -0
- package/dist/registry/package/components/DataTable/index.tsx +240 -0
- package/dist/registry/package/components/DataTable/styles.css +98 -0
- package/dist/registry/package/components/DatePicker/index.tsx +102 -0
- package/dist/registry/package/components/DateRangeFilter/index.tsx +8 -0
- package/dist/registry/package/components/DateRangeFilter/styles.css +4 -0
- package/dist/registry/package/components/DateRangePicker/index.tsx +36 -0
- package/dist/registry/package/components/DateTimePicker/index.tsx +32 -0
- package/dist/registry/package/components/Descriptions/index.tsx +7 -0
- package/dist/registry/package/components/Descriptions/styles.css +8 -0
- package/dist/registry/package/components/Divider/index.tsx +12 -0
- package/dist/registry/package/components/Divider/styles.css +3 -0
- package/dist/registry/package/components/Drawer/context.ts +14 -0
- package/dist/registry/package/components/Drawer/index.tsx +209 -0
- package/dist/registry/package/components/Drawer/styles.css +151 -0
- package/dist/registry/package/components/DropdownMenu/index.tsx +350 -0
- package/dist/registry/package/components/DropdownMenu/styles.css +100 -0
- package/dist/registry/package/components/DurationDisplay/index.tsx +3 -0
- package/dist/registry/package/components/EmptyState/index.tsx +23 -0
- package/dist/registry/package/components/EmptyState/styles.css +6 -0
- package/dist/registry/package/components/EntityMeta/index.tsx +65 -0
- package/dist/registry/package/components/EntityMeta/styles.css +9 -0
- package/dist/registry/package/components/ErrorState/index.tsx +30 -0
- package/dist/registry/package/components/ErrorState/styles.css +6 -0
- package/dist/registry/package/components/EventCalendar/index.tsx +8 -0
- package/dist/registry/package/components/EventCalendar/styles.css +8 -0
- package/dist/registry/package/components/Field/index.tsx +664 -0
- package/dist/registry/package/components/Field/styles.css +14 -0
- package/dist/registry/package/components/FileUpload/index.tsx +68 -0
- package/dist/registry/package/components/FileUpload/styles.css +4 -0
- package/dist/registry/package/components/FiltersBar/index.tsx +20 -0
- package/dist/registry/package/components/FiltersBar/styles.css +6 -0
- package/dist/registry/package/components/Form/index.tsx +211 -0
- package/dist/registry/package/components/Form/styles.css +80 -0
- package/dist/registry/package/components/FormField/index.tsx +2 -0
- package/dist/registry/package/components/HoverCard/index.tsx +121 -0
- package/dist/registry/package/components/HoverCard/styles.css +26 -0
- package/dist/registry/package/components/IconButton/index.tsx +27 -0
- package/dist/registry/package/components/IconButton/styles.css +7 -0
- package/dist/registry/package/components/Icons/index.tsx +185 -0
- package/dist/registry/package/components/ImageUpload/index.tsx +6 -0
- package/dist/registry/package/components/ImageUpload/styles.css +4 -0
- package/dist/registry/package/components/Input/index.tsx +103 -0
- package/dist/registry/package/components/Input/styles.css +8 -0
- package/dist/registry/package/components/InputNumber/index.tsx +196 -0
- package/dist/registry/package/components/InputNumber/styles.css +54 -0
- package/dist/registry/package/components/JsonEditor/index.tsx +6 -0
- package/dist/registry/package/components/JsonEditor/styles.css +2 -0
- package/dist/registry/package/components/KpiGrid/index.tsx +11 -0
- package/dist/registry/package/components/KpiGrid/styles.css +5 -0
- package/dist/registry/package/components/Label/index.tsx +43 -0
- package/dist/registry/package/components/Label/styles.css +25 -0
- package/dist/registry/package/components/MarkdownEditor/index.tsx +5 -0
- package/dist/registry/package/components/MiniCalendar/index.tsx +3 -0
- package/dist/registry/package/components/Modal/index.tsx +104 -0
- package/dist/registry/package/components/Modal/styles.css +20 -0
- package/dist/registry/package/components/MonthPicker/index.tsx +5 -0
- package/dist/registry/package/components/MultiSelect/index.tsx +133 -0
- package/dist/registry/package/components/MultiSelect/styles.css +3 -0
- package/dist/registry/package/components/OtpInput/index.tsx +5 -0
- package/dist/registry/package/components/OtpInput/styles.css +2 -0
- package/dist/registry/package/components/Overlay/index.tsx +140 -0
- package/dist/registry/package/components/Overlay/styles.css +1 -0
- package/dist/registry/package/components/PageHeader/index.tsx +24 -0
- package/dist/registry/package/components/PageHeader/styles.css +5 -0
- package/dist/registry/package/components/Pagination/index.tsx +155 -0
- package/dist/registry/package/components/Pagination/styles.css +103 -0
- package/dist/registry/package/components/PasswordInput/index.tsx +125 -0
- package/dist/registry/package/components/PasswordInput/styles.css +58 -0
- package/dist/registry/package/components/PermissionGate/index.tsx +3 -0
- package/dist/registry/package/components/PhoneInput/index.tsx +189 -0
- package/dist/registry/package/components/PhoneInput/styles.css +35 -0
- package/dist/registry/package/components/Popover/index.tsx +12 -0
- package/dist/registry/package/components/Popover/styles.css +12 -0
- package/dist/registry/package/components/Progress/index.tsx +149 -0
- package/dist/registry/package/components/Progress/styles.css +101 -0
- package/dist/registry/package/components/QrCode/index.tsx +6 -0
- package/dist/registry/package/components/QrCode/styles.css +5 -0
- package/dist/registry/package/components/RadioGroup/index.tsx +29 -0
- package/dist/registry/package/components/RadioGroup/styles.css +20 -0
- package/dist/registry/package/components/Rating/index.tsx +82 -0
- package/dist/registry/package/components/Rating/styles.css +41 -0
- package/dist/registry/package/components/ReferenceSelect/index.tsx +7 -0
- package/dist/registry/package/components/ReferenceSelect/styles.css +2 -0
- package/dist/registry/package/components/RelativeTime/index.tsx +22 -0
- package/dist/registry/package/components/RelativeTime/styles.css +1 -0
- package/dist/registry/package/components/RequireSession/index.tsx +127 -0
- package/dist/registry/package/components/RequireSession/styles.css +13 -0
- package/dist/registry/package/components/ResizablePanel/index.tsx +6 -0
- package/dist/registry/package/components/ResizablePanel/styles.css +5 -0
- package/dist/registry/package/components/RichTextEditor/index.tsx +5 -0
- package/dist/registry/package/components/RichTextEditor/styles.css +2 -0
- package/dist/registry/package/components/ScrollArea/index.tsx +114 -0
- package/dist/registry/package/components/ScrollArea/styles.css +74 -0
- package/dist/registry/package/components/SearchInput/index.tsx +121 -0
- package/dist/registry/package/components/SearchInput/styles.css +6 -0
- package/dist/registry/package/components/Select/index.tsx +324 -0
- package/dist/registry/package/components/Select/styles.css +58 -0
- package/dist/registry/package/components/Sidebar/index.tsx +232 -0
- package/dist/registry/package/components/Sidebar/styles.css +140 -0
- package/dist/registry/package/components/Skeleton/index.tsx +21 -0
- package/dist/registry/package/components/Skeleton/styles.css +2 -0
- package/dist/registry/package/components/Slider/index.tsx +193 -0
- package/dist/registry/package/components/Slider/styles.css +53 -0
- package/dist/registry/package/components/Spinner/index.tsx +28 -0
- package/dist/registry/package/components/Spinner/styles.css +8 -0
- package/dist/registry/package/components/StatCard/index.tsx +15 -0
- package/dist/registry/package/components/StatCard/styles.css +3 -0
- package/dist/registry/package/components/Statistic/index.tsx +23 -0
- package/dist/registry/package/components/Statistic/styles.css +6 -0
- package/dist/registry/package/components/Steps/index.tsx +108 -0
- package/dist/registry/package/components/Steps/styles.css +138 -0
- package/dist/registry/package/components/Switch/index.tsx +46 -0
- package/dist/registry/package/components/Switch/styles.css +26 -0
- package/dist/registry/package/components/Tabs/index.tsx +44 -0
- package/dist/registry/package/components/Tabs/styles.css +41 -0
- package/dist/registry/package/components/TagsInput/index.tsx +6 -0
- package/dist/registry/package/components/TagsInput/styles.css +2 -0
- package/dist/registry/package/components/Textarea/index.tsx +21 -0
- package/dist/registry/package/components/Textarea/styles.css +7 -0
- package/dist/registry/package/components/TimePicker/index.tsx +76 -0
- package/dist/registry/package/components/TimePicker/styles.css +13 -0
- package/dist/registry/package/components/TimeRangePicker/index.tsx +32 -0
- package/dist/registry/package/components/TimeRangePicker/styles.css +1 -0
- package/dist/registry/package/components/Timeline/index.tsx +8 -0
- package/dist/registry/package/components/Timeline/styles.css +12 -0
- package/dist/registry/package/components/Timer/index.tsx +9 -0
- package/dist/registry/package/components/Toast/index.tsx +426 -0
- package/dist/registry/package/components/Toast/styles.css +175 -0
- package/dist/registry/package/components/Toggle/index.tsx +181 -0
- package/dist/registry/package/components/Toggle/styles.css +124 -0
- package/dist/registry/package/components/Tooltip/index.tsx +163 -0
- package/dist/registry/package/components/Tooltip/styles.css +57 -0
- package/dist/registry/package/components/Tour/index.tsx +8 -0
- package/dist/registry/package/components/Tour/styles.css +6 -0
- package/dist/registry/package/components/Transfer/index.tsx +7 -0
- package/dist/registry/package/components/Transfer/styles.css +6 -0
- package/dist/registry/package/components/Tree/index.tsx +8 -0
- package/dist/registry/package/components/Tree/styles.css +6 -0
- package/dist/registry/package/components/TreeSelect/index.tsx +10 -0
- package/dist/registry/package/components/TreeSelect/styles.css +3 -0
- package/dist/registry/package/components/Typography/index.tsx +27 -0
- package/dist/registry/package/components/Typography/styles.css +16 -0
- package/dist/registry/package/components/UnsavedChangesGuard/index.tsx +4 -0
- package/dist/registry/package/components/WeekPicker/index.tsx +5 -0
- package/dist/registry/package/components/YearPicker/index.tsx +3 -0
- package/dist/registry/package/lib/form-adapter-rhf.tsx +141 -0
- package/dist/registry/registry.json +1780 -0
- package/dist/registry/styles/sia-ui.css +361 -0
- package/dist/source.mjs +93 -0
- package/package.json +55 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/* Bouton
|
|
2
|
+
==========================================================================
|
|
3
|
+
La version précédente déclarait une transition sur trois propriétés puis
|
|
4
|
+
n'écrivait aucune règle `:hover` ni `:active` : il n'y avait rien à animer.
|
|
5
|
+
C'est ce qui donnait l'impression de composants plats. */
|
|
6
|
+
|
|
7
|
+
.sia-button {
|
|
8
|
+
display: inline-flex;
|
|
9
|
+
align-items: center;
|
|
10
|
+
justify-content: center;
|
|
11
|
+
gap: 0.5rem;
|
|
12
|
+
min-height: 2.5rem;
|
|
13
|
+
padding: 0 1rem;
|
|
14
|
+
border: 1px solid transparent;
|
|
15
|
+
border-radius: var(--sia-radius);
|
|
16
|
+
font: inherit;
|
|
17
|
+
line-height: 1.25;
|
|
18
|
+
font-weight: 600;
|
|
19
|
+
cursor: pointer;
|
|
20
|
+
user-select: none;
|
|
21
|
+
|
|
22
|
+
/* Chaque ton fixe sa couleur une fois; les variantes s'y réfèrent. */
|
|
23
|
+
--sia-button-accent: var(--sia-primary);
|
|
24
|
+
--sia-button-on-accent: var(--sia-primary-foreground);
|
|
25
|
+
|
|
26
|
+
transition:
|
|
27
|
+
transform var(--sia-duration-fast) var(--sia-easing-standard),
|
|
28
|
+
box-shadow var(--sia-duration-base) var(--sia-easing-standard),
|
|
29
|
+
background-color var(--sia-duration-fast) var(--sia-easing-standard),
|
|
30
|
+
border-color var(--sia-duration-fast) var(--sia-easing-standard),
|
|
31
|
+
color var(--sia-duration-fast) var(--sia-easing-standard);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/* Tons ---------------------------------------------------------------- */
|
|
35
|
+
|
|
36
|
+
.sia-button--primary { --sia-button-accent: var(--sia-primary); }
|
|
37
|
+
.sia-button--neutral { --sia-button-accent: var(--sia-foreground); --sia-button-on-accent: var(--sia-background); }
|
|
38
|
+
.sia-button--info { --sia-button-accent: var(--sia-info); }
|
|
39
|
+
.sia-button--success { --sia-button-accent: var(--sia-success); }
|
|
40
|
+
.sia-button--warning { --sia-button-accent: var(--sia-warning); }
|
|
41
|
+
.sia-button--danger { --sia-button-accent: var(--sia-danger); }
|
|
42
|
+
|
|
43
|
+
/* Variantes ----------------------------------------------------------- */
|
|
44
|
+
|
|
45
|
+
.sia-button--solid {
|
|
46
|
+
color: var(--sia-button-on-accent);
|
|
47
|
+
/* Un dégradé d'un seul degré : la surface cesse d'être un aplat mort sans
|
|
48
|
+
pour autant attirer l'œil. */
|
|
49
|
+
background-image: linear-gradient(
|
|
50
|
+
180deg,
|
|
51
|
+
color-mix(in srgb, var(--sia-button-accent) 92%, #fff) 0%,
|
|
52
|
+
var(--sia-button-accent) 100%
|
|
53
|
+
);
|
|
54
|
+
box-shadow: 0 1px 2px color-mix(in srgb, var(--sia-button-accent) 20%, transparent);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.sia-button--outline {
|
|
58
|
+
color: var(--sia-foreground);
|
|
59
|
+
background: var(--sia-background);
|
|
60
|
+
border-color: var(--sia-border-strong);
|
|
61
|
+
box-shadow: var(--sia-elevation-rest);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.sia-button--ghost {
|
|
65
|
+
color: var(--sia-button-accent);
|
|
66
|
+
background: transparent;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/* États ---------------------------------------------------------------- */
|
|
70
|
+
|
|
71
|
+
.sia-button:hover:not(:disabled) {
|
|
72
|
+
/* Un pixel. Assez pour que le bouton réponde, trop peu pour déplacer la
|
|
73
|
+
mise en page autour de lui. */
|
|
74
|
+
transform: translateY(-1px);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.sia-button--solid:hover:not(:disabled) {
|
|
78
|
+
filter: brightness(1.03);
|
|
79
|
+
box-shadow: 0 2px 6px color-mix(in srgb, var(--sia-button-accent) 22%, transparent);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.sia-button--outline:hover:not(:disabled) {
|
|
83
|
+
border-color: var(--sia-button-accent);
|
|
84
|
+
color: var(--sia-button-accent);
|
|
85
|
+
background: color-mix(in srgb, var(--sia-button-accent) 5%, var(--sia-background));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.sia-button--ghost:hover:not(:disabled) {
|
|
89
|
+
background: color-mix(in srgb, var(--sia-button-accent) 10%, transparent);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.sia-button:active:not(:disabled) {
|
|
93
|
+
transform: translateY(0) scale(0.985);
|
|
94
|
+
box-shadow: var(--sia-elevation-press);
|
|
95
|
+
transition-duration: var(--sia-duration-instant);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.sia-button:disabled {
|
|
99
|
+
cursor: not-allowed;
|
|
100
|
+
opacity: 0.55;
|
|
101
|
+
box-shadow: none;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.sia-button[aria-busy="true"] {
|
|
105
|
+
cursor: progress;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/* Tailles -------------------------------------------------------------- */
|
|
109
|
+
|
|
110
|
+
.sia-button--sm { min-height: 2rem; padding: 0 0.75rem; font-size: 0.875rem; }
|
|
111
|
+
.sia-button--lg { min-height: 3rem; padding: 0 1.25rem; }
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { useMemo, useState, type ReactNode } from "react";
|
|
2
|
+
import { cn, toneClass } from "@sia-ui/utils";
|
|
3
|
+
import type { ComponentTone } from "@sia-ui/tokens";
|
|
4
|
+
import { ChevronLeftIcon, ChevronRightIcon } from "../Icons";
|
|
5
|
+
|
|
6
|
+
export interface CalendarDay {
|
|
7
|
+
date: string;
|
|
8
|
+
day: number;
|
|
9
|
+
inCurrentMonth: boolean;
|
|
10
|
+
isToday: boolean;
|
|
11
|
+
disabled: boolean;
|
|
12
|
+
selected: boolean;
|
|
13
|
+
inRange: boolean;
|
|
14
|
+
rangeEdge?: "start" | "end";
|
|
15
|
+
}
|
|
16
|
+
export interface CalendarRangeValue { start: string; end: string; }
|
|
17
|
+
export type CalendarMode = "month" | "year";
|
|
18
|
+
export interface CalendarProps {
|
|
19
|
+
value?: string;
|
|
20
|
+
defaultValue?: string;
|
|
21
|
+
range?: CalendarRangeValue;
|
|
22
|
+
defaultRange?: CalendarRangeValue;
|
|
23
|
+
selectionMode?: "single" | "range";
|
|
24
|
+
month?: string;
|
|
25
|
+
defaultMonth?: string;
|
|
26
|
+
mode?: CalendarMode;
|
|
27
|
+
defaultMode?: CalendarMode;
|
|
28
|
+
onValueChange?: (value: string) => void;
|
|
29
|
+
onRangeChange?: (value: CalendarRangeValue) => void;
|
|
30
|
+
onSelect?: (value: string, source: "date" | "month" | "year") => void;
|
|
31
|
+
onMonthChange?: (month: string) => void;
|
|
32
|
+
onPanelChange?: (month: string, mode: CalendarMode) => void;
|
|
33
|
+
disabledDate?: (date: string) => boolean;
|
|
34
|
+
validRange?: [string, string];
|
|
35
|
+
renderDay?: (day: CalendarDay) => ReactNode;
|
|
36
|
+
cellRender?: (day: CalendarDay, originNode: ReactNode) => ReactNode;
|
|
37
|
+
headerRender?: (props: { month: string; mode: CalendarMode; onMonthChange: (month: string) => void; onModeChange: (mode: CalendarMode) => void }) => ReactNode;
|
|
38
|
+
renderExtraFooter?: () => ReactNode;
|
|
39
|
+
locale?: string;
|
|
40
|
+
firstDayOfWeek?: 0 | 1;
|
|
41
|
+
showWeek?: boolean;
|
|
42
|
+
fullscreen?: boolean;
|
|
43
|
+
tone?: ComponentTone;
|
|
44
|
+
className?: string;
|
|
45
|
+
}
|
|
46
|
+
const pad = (value: number) => String(value).padStart(2, "0");
|
|
47
|
+
const iso = (date: Date) => `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
|
|
48
|
+
const monthIso = (date: Date) => `${date.getFullYear()}-${pad(date.getMonth() + 1)}`;
|
|
49
|
+
const parseMonth = (value?: string) => {
|
|
50
|
+
const source = value ? new Date(`${value.slice(0, 7)}-01T12:00:00`) : new Date();
|
|
51
|
+
return Number.isNaN(source.getTime()) ? new Date() : source;
|
|
52
|
+
};
|
|
53
|
+
function weekNumber(value: string) {
|
|
54
|
+
const date = new Date(`${value}T12:00:00`);
|
|
55
|
+
const target = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
|
|
56
|
+
const day = target.getUTCDay() || 7;
|
|
57
|
+
target.setUTCDate(target.getUTCDate() + 4 - day);
|
|
58
|
+
const yearStart = new Date(Date.UTC(target.getUTCFullYear(), 0, 1));
|
|
59
|
+
return Math.ceil((((target.getTime() - yearStart.getTime()) / 86400000) + 1) / 7);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function Calendar({
|
|
63
|
+
value,
|
|
64
|
+
defaultValue,
|
|
65
|
+
range,
|
|
66
|
+
defaultRange = { start: "", end: "" },
|
|
67
|
+
selectionMode = "single",
|
|
68
|
+
month,
|
|
69
|
+
defaultMonth,
|
|
70
|
+
mode,
|
|
71
|
+
defaultMode = "month",
|
|
72
|
+
onValueChange,
|
|
73
|
+
onRangeChange,
|
|
74
|
+
onSelect,
|
|
75
|
+
onMonthChange,
|
|
76
|
+
onPanelChange,
|
|
77
|
+
disabledDate,
|
|
78
|
+
validRange,
|
|
79
|
+
renderDay,
|
|
80
|
+
cellRender,
|
|
81
|
+
headerRender,
|
|
82
|
+
renderExtraFooter,
|
|
83
|
+
locale = "fr-FR",
|
|
84
|
+
firstDayOfWeek = 1,
|
|
85
|
+
showWeek = false,
|
|
86
|
+
fullscreen = false,
|
|
87
|
+
tone = "primary",
|
|
88
|
+
className,
|
|
89
|
+
}: CalendarProps) {
|
|
90
|
+
const [selected, setSelected] = useState(defaultValue ?? "");
|
|
91
|
+
const [selectedRange, setSelectedRange] = useState(defaultRange);
|
|
92
|
+
const [view, setView] = useState(() => parseMonth(month ?? defaultMonth ?? value ?? defaultValue));
|
|
93
|
+
const [panelMode, setPanelMode] = useState(defaultMode);
|
|
94
|
+
const currentValue = value ?? selected;
|
|
95
|
+
const currentRange = range ?? selectedRange;
|
|
96
|
+
const currentMode = mode ?? panelMode;
|
|
97
|
+
const controlledMonth = month ? parseMonth(month) : view;
|
|
98
|
+
const year = controlledMonth.getFullYear();
|
|
99
|
+
const monthIndex = controlledMonth.getMonth();
|
|
100
|
+
const setMonth = (next: Date, sourceMode = currentMode) => {
|
|
101
|
+
if (!month) setView(next);
|
|
102
|
+
const nextMonth = monthIso(next);
|
|
103
|
+
onMonthChange?.(nextMonth);
|
|
104
|
+
onPanelChange?.(nextMonth, sourceMode);
|
|
105
|
+
};
|
|
106
|
+
const changeMonth = (offset: number) => setMonth(new Date(year, monthIndex + offset, 1, 12));
|
|
107
|
+
const changeMode = (next: CalendarMode) => {
|
|
108
|
+
if (mode === undefined) setPanelMode(next);
|
|
109
|
+
onPanelChange?.(monthIso(controlledMonth), next);
|
|
110
|
+
};
|
|
111
|
+
const isDisabled = (date: string) => Boolean(
|
|
112
|
+
disabledDate?.(date) ||
|
|
113
|
+
(validRange && (date < validRange[0] || date > validRange[1])),
|
|
114
|
+
);
|
|
115
|
+
const selectDate = (date: string) => {
|
|
116
|
+
if (isDisabled(date)) return;
|
|
117
|
+
if (selectionMode === "range") {
|
|
118
|
+
const next = !currentRange.start || currentRange.end
|
|
119
|
+
? { start: date, end: "" }
|
|
120
|
+
: date < currentRange.start
|
|
121
|
+
? { start: date, end: currentRange.start }
|
|
122
|
+
: { start: currentRange.start, end: date };
|
|
123
|
+
if (range === undefined) setSelectedRange(next);
|
|
124
|
+
onRangeChange?.(next);
|
|
125
|
+
} else {
|
|
126
|
+
if (value === undefined) setSelected(date);
|
|
127
|
+
onValueChange?.(date);
|
|
128
|
+
}
|
|
129
|
+
onSelect?.(date, "date");
|
|
130
|
+
};
|
|
131
|
+
const days = useMemo(() => {
|
|
132
|
+
const first = new Date(year, monthIndex, 1, 12);
|
|
133
|
+
const offset = (first.getDay() - firstDayOfWeek + 7) % 7;
|
|
134
|
+
const start = new Date(year, monthIndex, 1 - offset, 12);
|
|
135
|
+
const today = iso(new Date());
|
|
136
|
+
return Array.from({ length: 42 }, (_, index): CalendarDay => {
|
|
137
|
+
const date = new Date(start);
|
|
138
|
+
date.setDate(start.getDate() + index);
|
|
139
|
+
const dateString = iso(date);
|
|
140
|
+
const rangeEdge = currentRange.start === dateString ? "start" : currentRange.end === dateString ? "end" : undefined;
|
|
141
|
+
return {
|
|
142
|
+
date: dateString,
|
|
143
|
+
day: date.getDate(),
|
|
144
|
+
inCurrentMonth: date.getMonth() === monthIndex,
|
|
145
|
+
isToday: dateString === today,
|
|
146
|
+
disabled: isDisabled(dateString),
|
|
147
|
+
selected: currentValue === dateString || Boolean(rangeEdge),
|
|
148
|
+
inRange: Boolean(currentRange.start && currentRange.end && dateString >= currentRange.start && dateString <= currentRange.end),
|
|
149
|
+
...(rangeEdge ? { rangeEdge } : {}),
|
|
150
|
+
};
|
|
151
|
+
});
|
|
152
|
+
}, [year, monthIndex, firstDayOfWeek, disabledDate, validRange, currentValue, currentRange.start, currentRange.end]);
|
|
153
|
+
const weekdays = Array.from({ length: 7 }, (_, index) => new Intl.DateTimeFormat(locale, { weekday: "short" }).format(new Date(2024, 0, 7 + firstDayOfWeek + index)));
|
|
154
|
+
const monthName = new Intl.DateTimeFormat(locale, { month: "long" }).format(controlledMonth);
|
|
155
|
+
const updateFromString = (next: string) => setMonth(parseMonth(next));
|
|
156
|
+
|
|
157
|
+
return (
|
|
158
|
+
<section className={cn("sia-calendar", `sia-calendar--${currentMode}`, fullscreen && "sia-calendar--fullscreen", toneClass("sia-tone", tone), className)}>
|
|
159
|
+
{headerRender?.({ month: monthIso(controlledMonth), mode: currentMode, onMonthChange: updateFromString, onModeChange: changeMode }) ?? (
|
|
160
|
+
<header className="sia-calendar__header">
|
|
161
|
+
<button type="button" className="sia-calendar__nav" aria-label={currentMode === "month" ? "Mois précédent" : "Année précédente"} onClick={() => currentMode === "month" ? changeMonth(-1) : setMonth(new Date(year - 1, monthIndex, 1, 12))}><ChevronLeftIcon /></button>
|
|
162
|
+
<div className="sia-calendar__heading">
|
|
163
|
+
<button type="button" onClick={() => changeMode(currentMode === "month" ? "year" : "month")}>{currentMode === "month" ? monthName : year}</button>
|
|
164
|
+
{currentMode === "month" && <button type="button" onClick={() => changeMode("year")}>{year}</button>}
|
|
165
|
+
</div>
|
|
166
|
+
<button type="button" className="sia-calendar__nav" aria-label={currentMode === "month" ? "Mois suivant" : "Année suivante"} onClick={() => currentMode === "month" ? changeMonth(1) : setMonth(new Date(year + 1, monthIndex, 1, 12))}><ChevronRightIcon /></button>
|
|
167
|
+
</header>
|
|
168
|
+
)}
|
|
169
|
+
{currentMode === "year" ? (
|
|
170
|
+
<div className="sia-calendar__months">
|
|
171
|
+
{Array.from({ length: 12 }, (_, index) => {
|
|
172
|
+
const date = new Date(year, index, 1, 12);
|
|
173
|
+
return <button type="button" key={index} data-selected={index === monthIndex || undefined} onClick={() => { setMonth(date, "month"); changeMode("month"); onSelect?.(monthIso(date), "month"); }}>{new Intl.DateTimeFormat(locale, { month: "short" }).format(date)}</button>;
|
|
174
|
+
})}
|
|
175
|
+
</div>
|
|
176
|
+
) : (
|
|
177
|
+
<>
|
|
178
|
+
<div className={cn("sia-calendar__weekdays", showWeek && "sia-calendar__with-week")}>
|
|
179
|
+
{weekdays.map((day) => <span key={day}>{day}</span>)}
|
|
180
|
+
</div>
|
|
181
|
+
<div className={cn("sia-calendar__grid", showWeek && "sia-calendar__with-week")}>
|
|
182
|
+
{days.map((day, index) => {
|
|
183
|
+
const originNode = <button type="button" disabled={day.disabled} aria-label={day.date} aria-pressed={day.selected} onClick={() => selectDate(day.date)}>{day.day}</button>;
|
|
184
|
+
return <div className="sia-calendar__cell" key={day.date} data-outside={!day.inCurrentMonth || undefined} data-today={day.isToday || undefined} data-in-range={day.inRange || undefined} data-range-edge={day.rangeEdge}>{showWeek && index % 7 === 0 && <span className="sia-calendar__week-number">{weekNumber(day.date)}</span>}{cellRender?.(day, originNode) ?? originNode}{renderDay?.(day)}</div>;
|
|
185
|
+
})}
|
|
186
|
+
</div>
|
|
187
|
+
</>
|
|
188
|
+
)}
|
|
189
|
+
{renderExtraFooter && <footer className="sia-calendar__footer">{renderExtraFooter()}</footer>}
|
|
190
|
+
</section>
|
|
191
|
+
);
|
|
192
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type HTMLAttributes } from "react";
|
|
2
|
+
import { cn } from "@sia-ui/utils";
|
|
3
|
+
import "./styles.css";
|
|
4
|
+
|
|
5
|
+
export function Card({ className, ...props }: HTMLAttributes<HTMLDivElement>) { return <div className={cn("sia-card", className)} {...props} />; }
|
|
6
|
+
export function CardHeader({ className, ...props }: HTMLAttributes<HTMLDivElement>) { return <div className={cn("sia-card__header", className)} {...props} />; }
|
|
7
|
+
export function CardBody({ className, ...props }: HTMLAttributes<HTMLDivElement>) { return <div className={cn("sia-card__body", className)} {...props} />; }
|
|
8
|
+
export function CardFooter({ className, ...props }: HTMLAttributes<HTMLDivElement>) { return <div className={cn("sia-card__footer", className)} {...props} />; }
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
.sia-card { overflow:hidden; border:1px solid var(--sia-border); border-radius:calc(var(--sia-radius) * 1.25); background:var(--sia-background); box-shadow:0 1px 2px rgb(15 23 42 / .06); }
|
|
2
|
+
.sia-card__header,.sia-card__body,.sia-card__footer { padding:1rem 1.25rem; }
|
|
3
|
+
.sia-card__header { border-bottom:1px solid var(--sia-border); }
|
|
4
|
+
.sia-card__footer { border-top:1px solid var(--sia-border); background:var(--sia-surface); }
|