@musecat/uikit 0.1.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.
Files changed (180) hide show
  1. package/LICENSE +9 -0
  2. package/README.md +51 -0
  3. package/index.ts +78 -0
  4. package/package.json +103 -0
  5. package/packages/Components/Box/Box.tsx +173 -0
  6. package/packages/Components/Box/Box.types.ts +48 -0
  7. package/packages/Components/Button/Button.tsx +275 -0
  8. package/packages/Components/Button/Button.types.ts +44 -0
  9. package/packages/Components/Card/Card.default.tsx +180 -0
  10. package/packages/Components/Card/Card.foldable.tsx +383 -0
  11. package/packages/Components/Card/Card.module.scss +23 -0
  12. package/packages/Components/Card/Card.tsx +8 -0
  13. package/packages/Components/Card/Card.types.ts +91 -0
  14. package/packages/Components/Card/Card.utils.ts +66 -0
  15. package/packages/Components/Checkbox/Checkbox.module.scss +11 -0
  16. package/packages/Components/Checkbox/Checkbox.tsx +104 -0
  17. package/packages/Components/Checkbox/Checkbox.types.ts +24 -0
  18. package/packages/Components/CodeBox/CodeBox.copy.tsx +43 -0
  19. package/packages/Components/CodeBox/CodeBox.module.scss +72 -0
  20. package/packages/Components/CodeBox/CodeBox.tsx +91 -0
  21. package/packages/Components/CodeBox/CodeBox.types.ts +17 -0
  22. package/packages/Components/ContextMenu/ContextMenu.module.scss +8 -0
  23. package/packages/Components/ContextMenu/ContextMenu.options.tsx +126 -0
  24. package/packages/Components/ContextMenu/ContextMenu.tsx +381 -0
  25. package/packages/Components/ContextMenu/ContextMenu.types.ts +46 -0
  26. package/packages/Components/ContributionGraph/ContributionGraph.module.scss +42 -0
  27. package/packages/Components/ContributionGraph/ContributionGraph.tsx +454 -0
  28. package/packages/Components/ContributionGraph/ContributionGraph.types.ts +27 -0
  29. package/packages/Components/DatePicker/DatePicker.calendar.tsx +161 -0
  30. package/packages/Components/DatePicker/DatePicker.core.tsx +582 -0
  31. package/packages/Components/DatePicker/DatePicker.module.scss +26 -0
  32. package/packages/Components/DatePicker/DatePicker.tsx +786 -0
  33. package/packages/Components/DatePicker/DatePicker.types.ts +36 -0
  34. package/packages/Components/DatePicker/DatePicker.utils.ts +80 -0
  35. package/packages/Components/DatePicker/hooks/useCalendar.ts +61 -0
  36. package/packages/Components/DatePicker/hooks/useSelection.ts +208 -0
  37. package/packages/Components/Divider/Divider.tsx +46 -0
  38. package/packages/Components/Divider/Divider.types.ts +12 -0
  39. package/packages/Components/Header/Header.module.scss +74 -0
  40. package/packages/Components/Header/Header.tsx +47 -0
  41. package/packages/Components/Header/Header.types.ts +8 -0
  42. package/packages/Components/Icon/Icon.group.tsx +122 -0
  43. package/packages/Components/Icon/Icon.group.types.ts +20 -0
  44. package/packages/Components/Icon/Icon.tsx +341 -0
  45. package/packages/Components/Icon/Icon.types.ts +59 -0
  46. package/packages/Components/Icon/SVG/data.tsx +232 -0
  47. package/packages/Components/Input/Input.module.scss +5 -0
  48. package/packages/Components/Input/Input.tsx +87 -0
  49. package/packages/Components/Input/Input.types.ts +14 -0
  50. package/packages/Components/Label/Label.module.scss +19 -0
  51. package/packages/Components/Label/Label.tsx +115 -0
  52. package/packages/Components/Label/Label.types.ts +28 -0
  53. package/packages/Components/Layout/Layout.docs.module.scss +42 -0
  54. package/packages/Components/Layout/Layout.docs.tsx +150 -0
  55. package/packages/Components/Layout/Layout.docs.types.ts +18 -0
  56. package/packages/Components/Layout/Layout.module.scss +91 -0
  57. package/packages/Components/Layout/Layout.tsx +306 -0
  58. package/packages/Components/Layout/Layout.types.ts +71 -0
  59. package/packages/Components/Maps/OSM/MapOSM.module.scss +11 -0
  60. package/packages/Components/Maps/OSM/MapOSM.shared.ts +121 -0
  61. package/packages/Components/Maps/OSM/MapOSM.tsx +279 -0
  62. package/packages/Components/Maps/OSM/MapOSM.types.ts +18 -0
  63. package/packages/Components/Nav/Nav.module.scss +137 -0
  64. package/packages/Components/Nav/Nav.tsx +304 -0
  65. package/packages/Components/Nav/Nav.types.ts +32 -0
  66. package/packages/Components/Nav/hooks/useNavDrag.ts +374 -0
  67. package/packages/Components/Nav/hooks/useNavIndicator.ts +137 -0
  68. package/packages/Components/Pagination/Pagination.tsx +254 -0
  69. package/packages/Components/Pagination/Pagination.types.ts +18 -0
  70. package/packages/Components/Pill/Pill.tsx +127 -0
  71. package/packages/Components/Pill/Pill.types.ts +33 -0
  72. package/packages/Components/Profile/Profile.shared.ts +74 -0
  73. package/packages/Components/Profile/Profile.tsx +382 -0
  74. package/packages/Components/Profile/Profile.types.ts +55 -0
  75. package/packages/Components/Progress/Progress.module.scss +15 -0
  76. package/packages/Components/Progress/Progress.tsx +88 -0
  77. package/packages/Components/Progress/Progress.types.ts +22 -0
  78. package/packages/Components/Radio/Radio.module.scss +11 -0
  79. package/packages/Components/Radio/Radio.tsx +121 -0
  80. package/packages/Components/Radio/Radio.types.ts +20 -0
  81. package/packages/Components/Select/Select.keyboard.tsx +76 -0
  82. package/packages/Components/Select/Select.trigger.tsx +88 -0
  83. package/packages/Components/Select/Select.tsx +11 -0
  84. package/packages/Components/Select/Select.types.ts +38 -0
  85. package/packages/Components/Select/Select.utils.ts +84 -0
  86. package/packages/Components/Select/hooks/useSelectNavigation.ts +461 -0
  87. package/packages/Components/Select/hooks/useSelectSelection.ts +142 -0
  88. package/packages/Components/Select/hooks/useSelectState.ts +314 -0
  89. package/packages/Components/Select/select.control.tsx +228 -0
  90. package/packages/Components/Select/select.inner.tsx +312 -0
  91. package/packages/Components/Skeleton/Skeleton.tsx +30 -0
  92. package/packages/Components/Skeleton/Skeleton.types.ts +12 -0
  93. package/packages/Components/Spinner/Spinner.module.scss +78 -0
  94. package/packages/Components/Spinner/Spinner.tsx +125 -0
  95. package/packages/Components/Spinner/Spinner.types.ts +19 -0
  96. package/packages/Components/Text/Text.module.scss +7 -0
  97. package/packages/Components/Text/Text.tsx +49 -0
  98. package/packages/Components/Text/Text.types.ts +33 -0
  99. package/packages/Components/TimePicker/TimePicker.core.tsx +448 -0
  100. package/packages/Components/TimePicker/TimePicker.module.scss +30 -0
  101. package/packages/Components/TimePicker/TimePicker.tsx +511 -0
  102. package/packages/Components/TimePicker/TimePicker.types.ts +22 -0
  103. package/packages/Components/Timeline/Timeline.tsx +111 -0
  104. package/packages/Components/Timeline/Timeline.types.ts +23 -0
  105. package/packages/Components/Title/Title.tsx +305 -0
  106. package/packages/Components/Title/Title.types.ts +64 -0
  107. package/packages/Components/Toggle/Toggle.module.scss +32 -0
  108. package/packages/Components/Toggle/Toggle.tsx +272 -0
  109. package/packages/Components/Toggle/Toggle.types.ts +17 -0
  110. package/packages/Components/Tooltip/Tooltip.module.scss +44 -0
  111. package/packages/Components/Tooltip/Tooltip.tsx +340 -0
  112. package/packages/Components/Tooltip/Tooltip.types.ts +38 -0
  113. package/packages/Frameworks/Dialog/Dialog.background.tsx +88 -0
  114. package/packages/Frameworks/Dialog/Dialog.boot.tsx +32 -0
  115. package/packages/Frameworks/Dialog/Dialog.store.ts +199 -0
  116. package/packages/Frameworks/Dialog/Dialog.tsx +185 -0
  117. package/packages/Frameworks/Dialog/Dialog.types.ts +264 -0
  118. package/packages/Frameworks/Dialog/Dialog.utils.ts +22 -0
  119. package/packages/Frameworks/Dialog/contents/Dialog.footer.tsx +79 -0
  120. package/packages/Frameworks/Dialog/contents/Dialog.funnel.tsx +260 -0
  121. package/packages/Frameworks/Dialog/contents/Dialog.header.tsx +137 -0
  122. package/packages/Frameworks/Dialog/hooks/useDialogPosition.ts +298 -0
  123. package/packages/Frameworks/Dialog/hooks/useSheetDrag.ts +325 -0
  124. package/packages/Frameworks/Dialog/hooks/useSheetGeometry.ts +201 -0
  125. package/packages/Frameworks/Dialog/hooks/useSheetProgressive.ts +98 -0
  126. package/packages/Frameworks/Dialog/renderers/renderModal.tsx +228 -0
  127. package/packages/Frameworks/Dialog/renderers/renderPopover.tsx +220 -0
  128. package/packages/Frameworks/Dialog/renderers/renderSheet.tsx +571 -0
  129. package/packages/Frameworks/EdgeEffect/EdgeEffect.tsx +52 -0
  130. package/packages/Frameworks/EdgeEffect/EdgeEffect.types.ts +5 -0
  131. package/packages/Frameworks/Motion/Motion.presets.ts +49 -0
  132. package/packages/Frameworks/Pressable/Pressable.module.scss +19 -0
  133. package/packages/Frameworks/Pressable/Pressable.tsx +651 -0
  134. package/packages/Frameworks/Pressable/Pressable.types.ts +73 -0
  135. package/packages/Frameworks/Squircle/Squircle.tsx +263 -0
  136. package/packages/Frameworks/Squircle/Squircle.types.ts +15 -0
  137. package/packages/Frameworks/Theme/Radius.types.ts +55 -0
  138. package/packages/Frameworks/Theme/Theme.types.ts +325 -0
  139. package/packages/Frameworks/Toaster/Toaster.boot.tsx +47 -0
  140. package/packages/Frameworks/Toaster/Toaster.module.scss +168 -0
  141. package/packages/Frameworks/Toaster/Toaster.types.ts +13 -0
  142. package/packages/Frameworks/View/DNDView/DNDView.module.scss +8 -0
  143. package/packages/Frameworks/View/DNDView/DNDView.tsx +470 -0
  144. package/packages/Frameworks/View/DNDView/DNDView.types.ts +61 -0
  145. package/packages/Frameworks/View/HScrollView/HScrollView.module.scss +59 -0
  146. package/packages/Frameworks/View/HScrollView/HScrollView.tsx +386 -0
  147. package/packages/Frameworks/View/HScrollView/HScrollView.types.ts +46 -0
  148. package/packages/Frameworks/View/ImageView/Dialog/ImageDialog.counter.tsx +27 -0
  149. package/packages/Frameworks/View/ImageView/Dialog/ImageDialog.footer.tsx +76 -0
  150. package/packages/Frameworks/View/ImageView/Dialog/ImageDialog.header.tsx +49 -0
  151. package/packages/Frameworks/View/ImageView/Dialog/ImageDialog.list.tsx +74 -0
  152. package/packages/Frameworks/View/ImageView/Dialog/ImageDialog.slide.tsx +71 -0
  153. package/packages/Frameworks/View/ImageView/Dialog/ImageDialog.tsx +175 -0
  154. package/packages/Frameworks/View/ImageView/Image.controls.tsx +39 -0
  155. package/packages/Frameworks/View/ImageView/Image.hooks.ts +82 -0
  156. package/packages/Frameworks/View/ImageView/Image.module.scss +7 -0
  157. package/packages/Frameworks/View/ImageView/Image.tsx +375 -0
  158. package/packages/Frameworks/View/ImageView/Image.types.ts +71 -0
  159. package/packages/Frameworks/View/ImageView/Image.utils.ts +79 -0
  160. package/packages/Frameworks/View/View.module.scss +34 -0
  161. package/packages/Frameworks/View/View.tsx +200 -0
  162. package/packages/Frameworks/View/View.types.ts +36 -0
  163. package/packages/Frameworks/_shared/Padding.types.ts +52 -0
  164. package/packages/Frameworks/_shared/StopParentInteraction.tsx +45 -0
  165. package/packages/Frameworks/_shared/Wind.types.ts +17 -0
  166. package/packages/Frameworks/_shared/bodyScrollLock.ts +127 -0
  167. package/packages/Frameworks/_shared/layer.constants.ts +8 -0
  168. package/packages/Frameworks/_shared/normalize.ts +39 -0
  169. package/packages/Frameworks/_shared/sizing.ts +55 -0
  170. package/packages/Frameworks/_shared/useControllableState.ts +29 -0
  171. package/packages/Styles/_animation.scss +42 -0
  172. package/packages/Styles/_color.scss +131 -0
  173. package/packages/Styles/_font.scss +30 -0
  174. package/packages/Styles/_icon.scss +782 -0
  175. package/packages/Styles/_importer.scss +9 -0
  176. package/packages/Styles/_system.scss +69 -0
  177. package/packages/Styles/_textstyle.scss +111 -0
  178. package/packages/Styles/_theme.scss +452 -0
  179. package/packages/Styles/_viewport-global.scss +99 -0
  180. package/packages/Styles/_viewport.scss +108 -0
@@ -0,0 +1,36 @@
1
+ import type { LabelProps } from "@/packages/Components/Label/Label.types";
2
+
3
+ export type DatePickerMode = "single" | "range";
4
+
5
+ export interface DatePickerProps {
6
+ "data-color-mode"?: string;
7
+ className?: string;
8
+ style?: React.CSSProperties;
9
+ title?: string;
10
+ required?: boolean;
11
+ readOnly?: boolean;
12
+ disabled?: boolean;
13
+ hint?: LabelProps["hint"];
14
+
15
+ mode?: DatePickerMode;
16
+
17
+ value?: string;
18
+ defaultValue?: string;
19
+
20
+ startDate?: string;
21
+ endDate?: string;
22
+ defaultStartDate?: string;
23
+ defaultEndDate?: string;
24
+
25
+ placeholder?: string;
26
+
27
+ minDate?: string;
28
+ maxDate?: string;
29
+ disabledDaysOfWeek?: number[];
30
+
31
+ showTime?: boolean;
32
+ use12h?: boolean;
33
+
34
+ onRangeChange?: (start: string | null, end: string | null) => void;
35
+ onChange?: (value: string | null) => void;
36
+ }
@@ -0,0 +1,80 @@
1
+ export function parseDate(str: string | undefined | null): Date | null {
2
+ if (!str) return null;
3
+ if (str.includes(" ")) {
4
+ const [datePart, timePart] = str.split(" ");
5
+ const d = new Date(`${datePart}T${timePart}:00`);
6
+ return Number.isNaN(d.getTime()) ? null : d;
7
+ }
8
+ const d = new Date(`${str}T00:00:00`);
9
+ return Number.isNaN(d.getTime()) ? null : d;
10
+ }
11
+
12
+ export function toYMD(d: Date, showTime?: boolean): string {
13
+ const dateStr = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
14
+ if (!showTime) return dateStr;
15
+ return `${dateStr} ${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}`;
16
+ }
17
+
18
+ export function isSameDay(a: Date | null, b: Date | null): boolean {
19
+ if (!a || !b) return false;
20
+ return (
21
+ a.getFullYear() === b.getFullYear() &&
22
+ a.getMonth() === b.getMonth() &&
23
+ a.getDate() === b.getDate()
24
+ );
25
+ }
26
+
27
+ export function isBefore(a: Date, b: Date): boolean {
28
+ return a.getTime() < b.getTime();
29
+ }
30
+
31
+ export function formatDisplay(d: Date | null): string {
32
+ if (!d) return "";
33
+ return `${d.getFullYear()}. ${d.getMonth() + 1}. ${d.getDate()}.`;
34
+ }
35
+
36
+ export function formatRangeDisplay(
37
+ start: Date | null,
38
+ end: Date | null,
39
+ ): string {
40
+ if (!start) return "";
41
+ if (!end) return formatDisplay(start);
42
+ return `${formatDisplay(start)} – ${formatDisplay(end)}`;
43
+ }
44
+
45
+ export function getCalendarDays(year: number, month: number): Date[] {
46
+ const first = new Date(year, month, 1);
47
+ const startOffset = first.getDay();
48
+ const days: Date[] = [];
49
+ for (let i = 0; i < startOffset; i++) {
50
+ days.push(new Date(year, month, 1 - (startOffset - i)));
51
+ }
52
+ const daysInMonth = new Date(year, month + 1, 0).getDate();
53
+ for (let d = 1; d <= daysInMonth; d++) {
54
+ days.push(new Date(year, month, d));
55
+ }
56
+ while (days.length % 7 !== 0) {
57
+ days.push(
58
+ new Date(year, month + 1, days.length - startOffset - daysInMonth + 1),
59
+ );
60
+ }
61
+ return days;
62
+ }
63
+
64
+ export function isDateDisabled(
65
+ date: Date,
66
+ minDate?: string | null,
67
+ maxDate?: string | null,
68
+ disabledDaysOfWeek?: number[],
69
+ ): boolean {
70
+ if (minDate) {
71
+ const min = parseDate(minDate);
72
+ if (min && date.getTime() < min.getTime()) return true;
73
+ }
74
+ if (maxDate) {
75
+ const max = parseDate(maxDate);
76
+ if (max && date.getTime() > max.getTime()) return true;
77
+ }
78
+ if (disabledDaysOfWeek?.includes(date.getDay())) return true;
79
+ return false;
80
+ }
@@ -0,0 +1,61 @@
1
+ "use client";
2
+ import { useCallback, useMemo, useState } from "react";
3
+ import { getCalendarDays } from "@/packages/Components/DatePicker/DatePicker.utils";
4
+
5
+ interface UseCalendarOptions {
6
+ initialYear: number;
7
+ initialMonth: number;
8
+ }
9
+
10
+ interface UseCalendarReturn {
11
+ viewYear: number;
12
+ viewMonth: number;
13
+ setViewYear: React.Dispatch<React.SetStateAction<number>>;
14
+ setViewMonth: React.Dispatch<React.SetStateAction<number>>;
15
+ prevMonth: () => void;
16
+ nextMonth: () => void;
17
+ days: Date[];
18
+ }
19
+
20
+ export function useCalendar({
21
+ initialYear,
22
+ initialMonth,
23
+ }: UseCalendarOptions): UseCalendarReturn {
24
+ const [viewYear, setViewYear] = useState(initialYear);
25
+ const [viewMonth, setViewMonth] = useState(initialMonth);
26
+
27
+ const prevMonth = useCallback(() => {
28
+ setViewMonth((m) => {
29
+ if (m === 0) {
30
+ setViewYear((y) => y - 1);
31
+ return 11;
32
+ }
33
+ return m - 1;
34
+ });
35
+ }, []);
36
+
37
+ const nextMonth = useCallback(() => {
38
+ setViewMonth((m) => {
39
+ if (m === 11) {
40
+ setViewYear((y) => y + 1);
41
+ return 0;
42
+ }
43
+ return m + 1;
44
+ });
45
+ }, []);
46
+
47
+ const days = useMemo(
48
+ () => getCalendarDays(viewYear, viewMonth),
49
+ [viewYear, viewMonth],
50
+ );
51
+
52
+ return {
53
+ viewYear,
54
+ viewMonth,
55
+ setViewYear,
56
+ setViewMonth,
57
+ prevMonth,
58
+ nextMonth,
59
+ days,
60
+ };
61
+ }
@@ -0,0 +1,208 @@
1
+ "use client";
2
+ import { useCallback, useEffect, useMemo, useState } from "react";
3
+ import type { DatePickerMode } from "@/packages/Components/DatePicker/DatePicker.types";
4
+ import {
5
+ formatDisplay,
6
+ formatRangeDisplay,
7
+ isBefore,
8
+ isDateDisabled,
9
+ isSameDay,
10
+ parseDate,
11
+ toYMD,
12
+ } from "@/packages/Components/DatePicker/DatePicker.utils";
13
+
14
+ interface UseSelectionOptions {
15
+ mode: DatePickerMode;
16
+ isControlled: boolean;
17
+ value?: string | null;
18
+ defaultValue?: string | null;
19
+ startDate?: string | null;
20
+ endDate?: string | null;
21
+ defaultStartDate?: string | null;
22
+ defaultEndDate?: string | null;
23
+ onChange?: (value: string | null) => void;
24
+ onRangeChange?: (start: string | null, end: string | null) => void;
25
+ onClose?: () => void;
26
+ minDate?: string | null;
27
+ maxDate?: string | null;
28
+ disabledDaysOfWeek?: number[];
29
+ showTime?: boolean;
30
+ }
31
+
32
+ interface UseSelectionReturn {
33
+ selected: Date | null;
34
+ rangeStart: Date | null;
35
+ rangeEnd: Date | null;
36
+ hoverDay: Date | null;
37
+ setHoverDay: React.Dispatch<React.SetStateAction<Date | null>>;
38
+ handleDayClick: (day: Date) => void;
39
+ isInRange: (day: Date) => boolean;
40
+ isRangeStart: (day: Date) => boolean;
41
+ isRangeEnd: (day: Date) => boolean;
42
+ setSelected: React.Dispatch<React.SetStateAction<Date | null>>;
43
+ setRangeStart: React.Dispatch<React.SetStateAction<Date | null>>;
44
+ setRangeEnd: React.Dispatch<React.SetStateAction<Date | null>>;
45
+ displayText: string | null;
46
+ }
47
+
48
+ export function useSelection({
49
+ mode,
50
+ isControlled,
51
+ value,
52
+ defaultValue,
53
+ startDate,
54
+ endDate,
55
+ defaultStartDate,
56
+ defaultEndDate,
57
+ onChange,
58
+ onRangeChange,
59
+ onClose,
60
+ minDate,
61
+ maxDate,
62
+ disabledDaysOfWeek,
63
+ showTime,
64
+ }: UseSelectionOptions): UseSelectionReturn {
65
+ const [selected, setSelected] = useState<Date | null>(() =>
66
+ parseDate(value ?? defaultValue),
67
+ );
68
+ const [rangeStart, setRangeStart] = useState<Date | null>(() =>
69
+ parseDate(startDate ?? defaultStartDate),
70
+ );
71
+ const [rangeEnd, setRangeEnd] = useState<Date | null>(() =>
72
+ parseDate(endDate ?? defaultEndDate),
73
+ );
74
+ const [hoverDay, setHoverDay] = useState<Date | null>(null);
75
+
76
+ useEffect(() => {
77
+ if (!isControlled) return;
78
+ if (mode === "single") setSelected(parseDate(value));
79
+ else {
80
+ setRangeStart(parseDate(startDate));
81
+ setRangeEnd(parseDate(endDate));
82
+ }
83
+ }, [value, startDate, endDate, mode, isControlled]);
84
+
85
+ const handleDayClick = useCallback(
86
+ (day: Date) => {
87
+ if (isDateDisabled(day, minDate, maxDate, disabledDaysOfWeek)) return;
88
+ if (mode === "single") {
89
+ const next = isSameDay(day, selected) ? null : new Date(day);
90
+ if (next && selected) {
91
+ next.setHours(
92
+ selected.getHours(),
93
+ selected.getMinutes(),
94
+ selected.getSeconds(),
95
+ selected.getMilliseconds(),
96
+ );
97
+ }
98
+ if (!isControlled) setSelected(next);
99
+ onChange?.(next ? toYMD(next, showTime) : null);
100
+ onClose?.();
101
+ return;
102
+ }
103
+ if (!rangeStart || (rangeStart && rangeEnd)) {
104
+ const next = new Date(day);
105
+ if (rangeStart) {
106
+ next.setHours(
107
+ rangeStart.getHours(),
108
+ rangeStart.getMinutes(),
109
+ rangeStart.getSeconds(),
110
+ rangeStart.getMilliseconds(),
111
+ );
112
+ }
113
+ if (!isControlled) {
114
+ setRangeStart(next);
115
+ setRangeEnd(null);
116
+ }
117
+ onRangeChange?.(toYMD(next, showTime), null);
118
+ } else {
119
+ let start = new Date(rangeStart);
120
+ let end = new Date(day);
121
+ if (rangeEnd) {
122
+ end.setHours(
123
+ rangeEnd.getHours(),
124
+ rangeEnd.getMinutes(),
125
+ rangeEnd.getSeconds(),
126
+ rangeEnd.getMilliseconds(),
127
+ );
128
+ }
129
+ if (isBefore(end, start)) {
130
+ [start, end] = [end, start];
131
+ }
132
+ if (!isControlled) {
133
+ setRangeStart(start);
134
+ setRangeEnd(end);
135
+ }
136
+ onRangeChange?.(toYMD(start, showTime), toYMD(end, showTime));
137
+ onClose?.();
138
+ }
139
+ },
140
+ [
141
+ mode,
142
+ selected,
143
+ rangeStart,
144
+ rangeEnd,
145
+ isControlled,
146
+ onChange,
147
+ onRangeChange,
148
+ onClose,
149
+ minDate,
150
+ maxDate,
151
+ disabledDaysOfWeek,
152
+ showTime,
153
+ ],
154
+ );
155
+
156
+ const isInRange = useCallback(
157
+ (day: Date): boolean => {
158
+ if (mode !== "range") return false;
159
+ const end = rangeEnd ?? hoverDay;
160
+ if (!rangeStart || !end) return false;
161
+ const lo = isBefore(rangeStart, end) ? rangeStart : end;
162
+ const hi = isBefore(rangeStart, end) ? end : rangeStart;
163
+ return day.getTime() > lo.getTime() && day.getTime() < hi.getTime();
164
+ },
165
+ [mode, rangeStart, rangeEnd, hoverDay],
166
+ );
167
+
168
+ const isRangeStart = useCallback(
169
+ (day: Date): boolean => {
170
+ const eff = rangeEnd ?? hoverDay;
171
+ if (!rangeStart || !eff) return isSameDay(day, rangeStart);
172
+ const lo = isBefore(rangeStart, eff) ? rangeStart : eff;
173
+ return isSameDay(day, lo);
174
+ },
175
+ [rangeStart, rangeEnd, hoverDay],
176
+ );
177
+
178
+ const isRangeEnd = useCallback(
179
+ (day: Date): boolean => {
180
+ const eff = rangeEnd ?? hoverDay;
181
+ if (!rangeStart || !eff) return false;
182
+ const hi = isBefore(rangeStart, eff) ? eff : rangeStart;
183
+ return isSameDay(day, hi);
184
+ },
185
+ [rangeStart, rangeEnd, hoverDay],
186
+ );
187
+
188
+ const displayText = useMemo(() => {
189
+ if (mode === "single") return selected ? formatDisplay(selected) : null;
190
+ return rangeStart ? formatRangeDisplay(rangeStart, rangeEnd) : null;
191
+ }, [mode, selected, rangeStart, rangeEnd]);
192
+
193
+ return {
194
+ selected,
195
+ rangeStart,
196
+ rangeEnd,
197
+ hoverDay,
198
+ setHoverDay,
199
+ handleDayClick,
200
+ isInRange,
201
+ isRangeStart,
202
+ isRangeEnd,
203
+ setSelected,
204
+ setRangeStart,
205
+ setRangeEnd,
206
+ displayText,
207
+ };
208
+ }
@@ -0,0 +1,46 @@
1
+ import type { DividerProps } from "@/packages/Components/Divider/Divider.types";
2
+ import { Size } from "@/packages/Frameworks/_shared/sizing";
3
+
4
+ export default function Divider({
5
+ vertical = false,
6
+ margin,
7
+ marginHorizontal,
8
+ marginVertical,
9
+ gradient = false,
10
+ className,
11
+ style,
12
+ "data-color-mode": dataTheme,
13
+ }: DividerProps) {
14
+ const resolvedMargin = Size(margin);
15
+ const resolvedVertical = Size(marginVertical);
16
+ const resolvedHorizontal = Size(marginHorizontal);
17
+
18
+ const computedStyle = {
19
+ display: "block",
20
+ border: "none",
21
+ margin: 0,
22
+ borderRadius: "var(--radius-circle)",
23
+ width: vertical ? ".15rem" : "100%",
24
+ height: vertical ? undefined : ".1rem",
25
+ alignSelf: vertical ? "stretch" : undefined,
26
+ flex: vertical ? "0 0 auto" : undefined,
27
+ minHeight: vertical ? "1.8rem" : undefined,
28
+ background: gradient
29
+ ? `linear-gradient(${vertical ? "180deg" : "90deg"}, transparent 0%, color-mix(in srgb, var(--color-base-reversed-6) 10%, transparent) 50%, transparent 100%)`
30
+ : "color-mix(in srgb, var(--color-base-reversed-6) 10%, transparent)",
31
+ marginTop: resolvedVertical ?? resolvedMargin,
32
+ marginBottom: resolvedVertical ?? resolvedMargin,
33
+ marginLeft: resolvedHorizontal ?? resolvedMargin,
34
+ marginRight: resolvedHorizontal ?? resolvedMargin,
35
+ ...style,
36
+ } as React.CSSProperties;
37
+
38
+ return (
39
+ <hr
40
+ aria-orientation={vertical ? "vertical" : undefined}
41
+ data-color-mode={dataTheme}
42
+ className={className}
43
+ style={computedStyle}
44
+ />
45
+ );
46
+ }
@@ -0,0 +1,12 @@
1
+ import type { UIKitSizeValue } from "@/packages/Frameworks/_shared/sizing";
2
+
3
+ export interface DividerProps {
4
+ "data-color-mode"?: string;
5
+ className?: string;
6
+ style?: React.CSSProperties;
7
+ vertical?: boolean;
8
+ margin?: UIKitSizeValue;
9
+ marginHorizontal?: UIKitSizeValue;
10
+ marginVertical?: UIKitSizeValue;
11
+ gradient?: boolean;
12
+ }
@@ -0,0 +1,74 @@
1
+ @use "../../Styles/viewport" as viewport;
2
+
3
+ .Wrapper {
4
+ max-width: 1280px;
5
+ width: 100%;
6
+ margin-top: 3.6rem;
7
+ padding-inline: 3.6rem;
8
+ height: 4rem;
9
+ position: relative;
10
+
11
+ @include viewport.down(w3) {
12
+ margin-top: 1.6rem;
13
+ padding-inline: 1.6rem;
14
+ }
15
+
16
+ .LeftGroup {
17
+ flex: 1 1 auto;
18
+ min-width: 0;
19
+ }
20
+
21
+ .LeftContent {
22
+ display: flex;
23
+ align-items: center;
24
+ gap: 0.8rem;
25
+ flex: 1 1 auto;
26
+ min-width: 0;
27
+ padding: 0.6rem 0.8rem;
28
+ margin: -0.8rem;
29
+ transition: var(--transition-surface);
30
+
31
+ @include viewport.down(w3) {
32
+ padding: 0.4rem 0.6rem;
33
+ margin: -0.4rem -0.6rem;
34
+ }
35
+ }
36
+
37
+ .LeftContentLink {
38
+ flex: 0 1 auto;
39
+ width: fit-content;
40
+ max-width: 100%;
41
+ }
42
+
43
+ .LogoLink {
44
+ display: flex;
45
+ align-items: center;
46
+ padding: 0.4rem;
47
+ margin: -0.4rem;
48
+ }
49
+
50
+ .RightContent {
51
+ display: flex;
52
+ align-items: center;
53
+ gap: 0;
54
+ padding: 0.2rem;
55
+ position: relative;
56
+ background: color-mix(in srgb, var(--color-base-light-1) 70%, transparent);
57
+ -webkit-backdrop-filter: blur(8px);
58
+ backdrop-filter: blur(8px);
59
+ }
60
+ }
61
+
62
+ .LinearBlur {
63
+ position: fixed;
64
+ top: 0;
65
+ left: 0;
66
+ right: 0;
67
+ height: 10.6rem;
68
+ width: 100%;
69
+ z-index: 1100;
70
+
71
+ @include viewport.down(w3) {
72
+ height: 7.2rem;
73
+ }
74
+ }
@@ -0,0 +1,47 @@
1
+ import clsx from "clsx";
2
+ import styles from "@/packages/Components/Header/Header.module.scss";
3
+ import type { HeaderProps } from "@/packages/Components/Header/Header.types";
4
+ import EdgeEffect from "@/packages/Frameworks/EdgeEffect/EdgeEffect";
5
+ import View from "@/packages/Frameworks/View/View";
6
+
7
+ export default function Header({
8
+ "data-color-mode": dataTheme,
9
+ left,
10
+ center,
11
+ right,
12
+ className,
13
+ wrapperClassName,
14
+ }: HeaderProps) {
15
+ return (
16
+ <>
17
+ <EdgeEffect side="top" className={styles.LinearBlur} />
18
+ <header
19
+ className={className}
20
+ data-color-mode={dataTheme}
21
+ style={{
22
+ display: "flex",
23
+ flexDirection: "column",
24
+ alignItems: "center",
25
+ justifyContent: "center",
26
+ position: "fixed",
27
+ top: 0,
28
+ left: 0,
29
+ right: 0,
30
+ width: "100%",
31
+ zIndex: 1200,
32
+ }}
33
+ >
34
+ <View
35
+ className={clsx(styles.Wrapper, wrapperClassName)}
36
+ alignItems="center"
37
+ justifyContent="space-between"
38
+ gap={4}
39
+ >
40
+ {left}
41
+ {center}
42
+ {right}
43
+ </View>
44
+ </header>
45
+ </>
46
+ );
47
+ }
@@ -0,0 +1,8 @@
1
+ export interface HeaderProps {
2
+ "data-color-mode"?: string;
3
+ left?: React.ReactNode;
4
+ center?: React.ReactNode;
5
+ right?: React.ReactNode;
6
+ className?: string;
7
+ wrapperClassName?: string;
8
+ }
@@ -0,0 +1,122 @@
1
+ import { Children } from "react";
2
+ import type { IconGroupProps } from "@/packages/Components/Icon/Icon.group.types";
3
+ import Icon from "@/packages/Components/Icon/Icon";
4
+ import type { IconProps } from "@/packages/Components/Icon/Icon.types";
5
+ import {
6
+ Size,
7
+ type UIKitSizeValue,
8
+ } from "@/packages/Frameworks/_shared/sizing";
9
+ import View from "@/packages/Frameworks/View/View";
10
+
11
+ const GROUP_PADDING = 2;
12
+ const ICON_PADDING = 6;
13
+
14
+ function getIconKey(icon: IconProps, index: number) {
15
+ const title = typeof icon.title === "string" ? icon.title : undefined;
16
+
17
+ return [
18
+ title,
19
+ icon.icon,
20
+ icon.svg,
21
+ icon.image,
22
+ icon.width ?? 0,
23
+ icon.height ?? 0,
24
+ icon.color ?? "default",
25
+ index,
26
+ ].join("-");
27
+ }
28
+
29
+ function addSizeValues(a: UIKitSizeValue, b: UIKitSizeValue) {
30
+ if (typeof a === "number" && typeof b === "number") {
31
+ return a + b;
32
+ }
33
+
34
+ return `calc(${Size(a)} + ${Size(b)})`;
35
+ }
36
+
37
+ function resolveIcon(
38
+ icon: IconProps,
39
+ {
40
+ single,
41
+ groupPadding,
42
+ iconBoxOptions,
43
+ iconSize,
44
+ iconWidth,
45
+ iconHeight,
46
+ }: {
47
+ single: boolean;
48
+ groupPadding: UIKitSizeValue;
49
+ iconBoxOptions?: IconProps["boxOptions"];
50
+ iconSize?: IconProps["size"];
51
+ iconWidth?: IconProps["width"];
52
+ iconHeight?: IconProps["height"];
53
+ },
54
+ ): IconProps {
55
+ const boxed = icon.box ?? true;
56
+ const resolvedPadding = boxed
57
+ ? (icon.boxOptions?.padding ??
58
+ iconBoxOptions?.padding ??
59
+ (single ? addSizeValues(ICON_PADDING, groupPadding) : ICON_PADDING))
60
+ : (icon.boxOptions?.padding ?? iconBoxOptions?.padding);
61
+
62
+ return {
63
+ box: true,
64
+ border: icon.border,
65
+ size: icon.size ?? iconSize,
66
+ width: icon.width ?? iconWidth,
67
+ height: icon.height ?? iconHeight,
68
+ ...icon,
69
+ boxOptions: {
70
+ ...iconBoxOptions,
71
+ ...icon.boxOptions,
72
+ padding: resolvedPadding,
73
+ },
74
+ };
75
+ }
76
+
77
+ export default function IconGroup({
78
+ className,
79
+ style,
80
+ children,
81
+ icons,
82
+ padding = GROUP_PADDING,
83
+ iconBoxOptions,
84
+ iconSize,
85
+ iconWidth,
86
+ iconHeight,
87
+ background,
88
+ border,
89
+ radius,
90
+ }: IconGroupProps) {
91
+ const iconItems = icons ? (Array.isArray(icons) ? icons : [icons]) : [];
92
+ const itemCount = iconItems.length + Children.count(children);
93
+ const single = itemCount === 1;
94
+
95
+ return (
96
+ <View
97
+ alignItems="center"
98
+ background={background ?? "Base1TP2"}
99
+ radius={radius ?? "Circle"}
100
+ border={border}
101
+ className={className}
102
+ style={{ backdropFilter: "blur(2px)", width: "max-content", ...style }}
103
+ gap={2}
104
+ padding={single ? 0 : padding}
105
+ >
106
+ {iconItems.map((icon, index) => (
107
+ <Icon
108
+ key={getIconKey(icon, index)}
109
+ {...resolveIcon(icon, {
110
+ single,
111
+ groupPadding: padding,
112
+ iconBoxOptions,
113
+ iconSize,
114
+ iconWidth,
115
+ iconHeight,
116
+ })}
117
+ />
118
+ ))}
119
+ {children}
120
+ </View>
121
+ );
122
+ }