@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,199 @@
1
+ "use client";
2
+
3
+ import type { DialogProps } from "@/packages/Frameworks/Dialog/Dialog.types";
4
+
5
+ export interface DialogInstance extends Promise<any> {
6
+ id: string;
7
+ instanceId: string;
8
+ props: DialogProps;
9
+ close: (value?: any) => void;
10
+ }
11
+
12
+ type Listener = (stack: DialogInstance[]) => void;
13
+
14
+ let nextId = 0;
15
+ let nextAnchorId = 0;
16
+ let stack: DialogInstance[] = [];
17
+ let listeners: Listener[] = [];
18
+ const anchorIds = new WeakMap<HTMLElement, string>();
19
+
20
+ function closeOpenInstances(mode: DialogProps["mode"], exceptId?: string) {
21
+ for (const item of stack) {
22
+ if (item.props.open === false) continue;
23
+ if (item.props.mode !== mode) continue;
24
+ if (exceptId && item.id === exceptId) continue;
25
+ item.close();
26
+ }
27
+ }
28
+
29
+ function resolveModeId(
30
+ mode: DialogProps["mode"],
31
+ props: Omit<DialogProps, "mode">,
32
+ ) {
33
+ if (props.id) return props.id;
34
+
35
+ if (props.funnel?.id) {
36
+ return `${mode}-funnel:${props.funnel.id}`;
37
+ }
38
+
39
+ if (mode === "modal") {
40
+ const title =
41
+ props.modal && "header" in props.modal
42
+ ? props.modal.header?.title
43
+ : undefined;
44
+ if (typeof title === "string" && title.trim()) {
45
+ return `dialog-modal:${title.trim()}`;
46
+ }
47
+ }
48
+
49
+ if (mode === "sheet") {
50
+ const title =
51
+ props.sheet && "header" in props.sheet
52
+ ? props.sheet.header?.title
53
+ : undefined;
54
+ if (typeof title === "string" && title.trim()) {
55
+ return `dialog-sheet:${title.trim()}`;
56
+ }
57
+ }
58
+
59
+ return `dialog-${mode}`;
60
+ }
61
+
62
+ function notify() {
63
+ for (const listener of listeners) {
64
+ listener([...stack]);
65
+ }
66
+ }
67
+
68
+ export function dialog(props: DialogProps): DialogInstance {
69
+ const id = props.id ?? `dialog-${++nextId}`;
70
+ const instanceId = `dialog-inst-${++nextId}`;
71
+
72
+ const existing = stack.find((item) => item.id === id);
73
+ if (existing && existing.props.open !== false) return existing;
74
+
75
+ let resolvePromise: (value: any) => void;
76
+
77
+ const promise = new Promise<any>((resolve) => {
78
+ resolvePromise = resolve;
79
+ });
80
+
81
+ const close = (value?: any) => {
82
+ instance.props = {
83
+ ...instance.props,
84
+ open: false,
85
+ };
86
+ notify();
87
+ resolvePromise(value);
88
+
89
+ setTimeout(() => {
90
+ stack = stack.filter(
91
+ (item) => item !== instance || item.props.open !== false,
92
+ );
93
+ notify();
94
+ }, 350);
95
+ };
96
+
97
+ const instance = Object.assign(promise, {
98
+ id,
99
+ instanceId,
100
+ props: {
101
+ ...props,
102
+ id,
103
+ open: true,
104
+ onOpenChange: (open: boolean) => {
105
+ if (!open) close();
106
+ },
107
+ },
108
+ close,
109
+ }) as DialogInstance;
110
+
111
+ stack.push(instance);
112
+ notify();
113
+
114
+ return instance;
115
+ }
116
+
117
+ dialog.popover = (
118
+ anchor: HTMLElement,
119
+ popoverProps: Omit<DialogProps, "mode">,
120
+ ) => {
121
+ if (!anchorIds.has(anchor)) {
122
+ anchorIds.set(anchor, `dialog-popover-${++nextAnchorId}`);
123
+ }
124
+ const id = popoverProps.id || anchorIds.get(anchor)!;
125
+
126
+ const existing = stack.find(
127
+ (item) => item.id === id && item.props.open !== false,
128
+ );
129
+ if (existing) {
130
+ const closeOnClickTrigger =
131
+ (popoverProps.popover as any)?.closeOnClickTrigger !== false;
132
+ if (closeOnClickTrigger) existing.close();
133
+ return existing;
134
+ }
135
+
136
+ return dialog({
137
+ ...popoverProps,
138
+ id,
139
+ mode: "popover",
140
+ popover: {
141
+ anchorRef: { current: anchor },
142
+ ...(popoverProps.popover as any),
143
+ },
144
+ } as DialogProps);
145
+ };
146
+
147
+ dialog.modal = (modalProps: Omit<DialogProps, "mode">) => {
148
+ const id = resolveModeId("modal", modalProps);
149
+ const existing = stack.find(
150
+ (item) => item.id === id && item.props.open !== false,
151
+ );
152
+ if (existing) {
153
+ existing.close();
154
+ return existing;
155
+ }
156
+
157
+ closeOpenInstances("modal", id);
158
+
159
+ return dialog({
160
+ ...modalProps,
161
+ id,
162
+ mode: "modal",
163
+ } as DialogProps);
164
+ };
165
+
166
+ dialog.sheet = (sheetProps: Omit<DialogProps, "mode">) => {
167
+ const id = resolveModeId("sheet", sheetProps);
168
+ const existing = stack.find(
169
+ (item) => item.id === id && item.props.open !== false,
170
+ );
171
+ if (existing) {
172
+ existing.close();
173
+ return existing;
174
+ }
175
+
176
+ closeOpenInstances("sheet", id);
177
+
178
+ return dialog({
179
+ ...sheetProps,
180
+ id,
181
+ mode: "sheet",
182
+ } as DialogProps);
183
+ };
184
+
185
+ export const DialogStore = {
186
+ getStack() {
187
+ return [...stack];
188
+ },
189
+ subscribe(listener: Listener) {
190
+ listeners.push(listener);
191
+ return () => {
192
+ listeners = listeners.filter((l) => l !== listener);
193
+ };
194
+ },
195
+ clear() {
196
+ stack = [];
197
+ notify();
198
+ },
199
+ };
@@ -0,0 +1,185 @@
1
+ "use client";
2
+
3
+ import { buildContext, useViewportMatch } from "@musecat/functionkit";
4
+ import { useMemo } from "react";
5
+ import DialogFunnel from "@/packages/Frameworks/Dialog/contents/Dialog.funnel";
6
+ import type {
7
+ DialogContextValue,
8
+ DialogMode,
9
+ DialogProps,
10
+ } from "@/packages/Frameworks/Dialog/Dialog.types";
11
+ import RenderModal from "@/packages/Frameworks/Dialog/renderers/renderModal";
12
+ import RenderPopover from "@/packages/Frameworks/Dialog/renderers/renderPopover";
13
+ import RenderSheet from "@/packages/Frameworks/Dialog/renderers/renderSheet";
14
+
15
+ const [DialogProvider, useRawDialogContext] =
16
+ buildContext<DialogContextValue | null>(null);
17
+
18
+ export function useDialog() {
19
+ return useRawDialogContext();
20
+ }
21
+
22
+ export default function Dialog(props: DialogProps) {
23
+ const {
24
+ open = false,
25
+ onOpenChange = () => {},
26
+ zIndex,
27
+ id,
28
+ width,
29
+ height,
30
+ mode,
31
+ mobileMode,
32
+ role,
33
+ popover,
34
+ modal,
35
+ sheet,
36
+ funnel,
37
+ exit,
38
+ children,
39
+ content,
40
+ "data-color-mode": dataColorMode,
41
+ } = props;
42
+
43
+ const resolvedChildren = children ?? content;
44
+
45
+ const isMobile = useViewportMatch("(max-width: 549.98px)");
46
+
47
+ const resolvedMode = useMemo<DialogMode>(() => {
48
+ if (mode === "sheet") return "sheet";
49
+ if (isMobile && mobileMode) {
50
+ return mobileMode;
51
+ }
52
+ return mode;
53
+ }, [mode, isMobile, mobileMode]);
54
+
55
+ const resolvedHeader = useMemo(() => {
56
+ if (funnel) return undefined;
57
+ if (resolvedMode === "popover") return popover?.header;
58
+ if (resolvedMode === "modal") return modal?.header ?? popover?.header;
59
+ return sheet?.header ?? popover?.header;
60
+ }, [resolvedMode, popover, modal, sheet, funnel]);
61
+
62
+ const resolvedFooter = useMemo(() => {
63
+ if (funnel) return undefined;
64
+ if (resolvedMode === "popover") return popover?.footer;
65
+ if (resolvedMode === "modal") return modal?.footer ?? popover?.footer;
66
+ return sheet?.footer ?? popover?.footer;
67
+ }, [resolvedMode, popover, modal, sheet, funnel]);
68
+
69
+ const resolvedExit = useMemo(() => {
70
+ if (funnel) return undefined;
71
+ if (resolvedMode === "popover") return popover?.exit ?? exit;
72
+ if (resolvedMode === "modal") return modal?.exit ?? popover?.exit ?? exit;
73
+ return sheet?.exit ?? popover?.exit ?? exit;
74
+ }, [resolvedMode, popover, modal, sheet, funnel, exit]);
75
+
76
+ const close = () => {
77
+ onOpenChange(false);
78
+ };
79
+
80
+ const contextValue = useMemo<DialogContextValue>(
81
+ () => ({
82
+ id: id || "",
83
+ mode: resolvedMode,
84
+ open,
85
+ closeDialog: close,
86
+ }),
87
+ [id, resolvedMode, open, close],
88
+ );
89
+
90
+ const renderFunnelContent = () => {
91
+ if (!funnel) return null;
92
+ return <DialogFunnel config={funnel} onClose={close} isMobile={isMobile} />;
93
+ };
94
+
95
+ const renderContent = () => {
96
+ if (resolvedMode === "popover" && popover) {
97
+ const popoverConfig = {
98
+ ...popover,
99
+ header: resolvedHeader,
100
+ footer: resolvedFooter,
101
+ exit: resolvedExit,
102
+ };
103
+
104
+ return (
105
+ <RenderPopover
106
+ open={open}
107
+ onOpenChange={onOpenChange}
108
+ config={popoverConfig}
109
+ id={id}
110
+ zIndex={zIndex}
111
+ width={width}
112
+ height={height}
113
+ role={role}
114
+ data-color-mode={dataColorMode}
115
+ >
116
+ {resolvedChildren}
117
+ </RenderPopover>
118
+ );
119
+ }
120
+
121
+ if (resolvedMode === "modal") {
122
+ const modalConfig = funnel
123
+ ? {
124
+ ...modal,
125
+ custom: renderFunnelContent(),
126
+ popoverOwnerId: popover?.popoverOwnerId,
127
+ }
128
+ : {
129
+ ...modal,
130
+ header: resolvedHeader,
131
+ footer: resolvedFooter,
132
+ exit: resolvedExit,
133
+ popoverOwnerId: popover?.popoverOwnerId,
134
+ };
135
+
136
+ return (
137
+ <RenderModal
138
+ open={open}
139
+ onOpenChange={onOpenChange}
140
+ config={modalConfig}
141
+ id={id}
142
+ zIndex={zIndex}
143
+ width={width}
144
+ height={height}
145
+ role={role}
146
+ data-color-mode={dataColorMode}
147
+ >
148
+ {resolvedChildren}
149
+ </RenderModal>
150
+ );
151
+ }
152
+
153
+ if (resolvedMode === "sheet") {
154
+ const sheetConfig = {
155
+ ...sheet,
156
+ header: resolvedHeader,
157
+ footer: resolvedFooter,
158
+ exit: resolvedExit,
159
+ popoverOwnerId: popover?.popoverOwnerId,
160
+ };
161
+
162
+ return (
163
+ <RenderSheet
164
+ open={open}
165
+ onOpenChange={onOpenChange}
166
+ config={sheetConfig}
167
+ id={id}
168
+ zIndex={zIndex}
169
+ width={width}
170
+ height={height}
171
+ role={role}
172
+ data-color-mode={dataColorMode}
173
+ >
174
+ {funnel ? renderFunnelContent() : resolvedChildren}
175
+ </RenderSheet>
176
+ );
177
+ }
178
+
179
+ return null;
180
+ };
181
+
182
+ return (
183
+ <DialogProvider value={contextValue}>{renderContent()}</DialogProvider>
184
+ );
185
+ }
@@ -0,0 +1,264 @@
1
+ import type { ReactNode, RefObject } from "react";
2
+ import type { ButtonProps } from "@/packages/Components/Button/Button.types";
3
+ import type { IconProps } from "@/packages/Components/Icon/Icon.types";
4
+ import type { RadiusProps } from "@/packages/Frameworks/Theme/Radius.types";
5
+ import type {
6
+ BackgroundBlurValue,
7
+ BorderProps,
8
+ ThemeBackgroundPaint,
9
+ ThemeSystemProps,
10
+ } from "@/packages/Frameworks/Theme/Theme.types";
11
+
12
+ export type DialogMode = "popover" | "modal" | "sheet";
13
+ export type DialogMobileMode = "modal" | "sheet";
14
+
15
+ export interface ExitConfirmOptions {
16
+ title?: string;
17
+ caption?: string;
18
+ confirmText?: string;
19
+ cancelText?: string;
20
+ }
21
+
22
+ export interface ExitConfig {
23
+ overlay?: boolean;
24
+ escape?: boolean;
25
+ confirm?: ExitConfirmOptions;
26
+ icon?: boolean | ReactNode;
27
+ footerButton?: boolean | ReactNode;
28
+ }
29
+
30
+ export interface OffsetConfig {
31
+ x?: number | string;
32
+ y?: number | string;
33
+ width?: number | string;
34
+ }
35
+
36
+ export interface DialogHeaderConfig {
37
+ icon?: IconProps;
38
+ title?: ReactNode;
39
+ caption?: ReactNode;
40
+ content?: ReactNode;
41
+ }
42
+
43
+ export interface DialogFooterConfig {
44
+ columnLayout?: boolean;
45
+ content?: ReactNode;
46
+ buttons?: (ButtonProps & { key?: string })[];
47
+ buttonCondensed?: boolean | "pc";
48
+ }
49
+
50
+ export type PopoverHeaderConfig = DialogHeaderConfig;
51
+ export type PopoverFooterConfig = DialogFooterConfig;
52
+
53
+ export interface ModalHeaderConfig extends DialogHeaderConfig {
54
+ centered?: boolean;
55
+ }
56
+ export type ModalFooterConfig = DialogFooterConfig;
57
+
58
+ export interface SheetHeaderConfig extends DialogHeaderConfig {
59
+ iconReversed?: boolean;
60
+ }
61
+ export type SheetFooterConfig = DialogFooterConfig;
62
+
63
+ type DialogBaseConfig = ThemeSystemProps & BorderProps & RadiusProps;
64
+
65
+ export interface PopoverConfig extends DialogBaseConfig {
66
+ anchorRef: RefObject<HTMLElement | null>;
67
+ placement?:
68
+ | "top"
69
+ | "top-start"
70
+ | "top-end"
71
+ | "bottom"
72
+ | "bottom-start"
73
+ | "bottom-end"
74
+ | "left"
75
+ | "right";
76
+ strategy?: "anchored" | "center-selected";
77
+ selectedItemSelector?: string;
78
+ matchAnchorWidth?: boolean;
79
+ coverAnchor?: boolean;
80
+ gap?: number;
81
+ margin?: number;
82
+ stopInteractionPropagation?: boolean;
83
+ recalcKey?: string | number;
84
+ popoverOwnerId?: string;
85
+ header?: PopoverHeaderConfig;
86
+ footer?: PopoverFooterConfig;
87
+ exit?: ExitConfig;
88
+
89
+ closeOnClickTrigger?: boolean;
90
+ outside?: DialogOutsideConfig;
91
+ offset?: OffsetConfig;
92
+ maxHeight?: number | string;
93
+ className?: string;
94
+ style?: React.CSSProperties;
95
+ }
96
+
97
+ export interface ModalConfig extends DialogBaseConfig {
98
+ header?: ModalHeaderConfig;
99
+ footer?: ModalFooterConfig;
100
+ exit?: ExitConfig;
101
+ custom?: ReactNode;
102
+ defaultEnterAction?: () => void;
103
+ maintainWhenRouting?: boolean;
104
+ immersive?: boolean;
105
+ outside?: DialogOutsideConfig;
106
+ portalClassName?: string;
107
+ portalStyle?: React.CSSProperties;
108
+ contentClassName?: string;
109
+ contentStyle?: React.CSSProperties;
110
+ className?: string;
111
+ style?: React.CSSProperties;
112
+ popoverOwnerId?: string;
113
+ }
114
+
115
+ export interface SheetConfig extends DialogBaseConfig {
116
+ header?: SheetHeaderConfig;
117
+ footer?: SheetFooterConfig;
118
+ exit?: ExitConfig;
119
+ snapPoints?: number[];
120
+ defaultSnap?: number;
121
+ min?: number | string;
122
+ max?: number | string;
123
+ freeDrag?: boolean;
124
+ disableDrag?: boolean;
125
+ outside?: DialogOutsideConfig;
126
+ className?: string;
127
+ style?: React.CSSProperties;
128
+ popoverOwnerId?: string;
129
+ }
130
+
131
+ export interface FunnelStepConfig {
132
+ name: string;
133
+ title?: ReactNode;
134
+ caption?: ReactNode;
135
+ icon?: IconProps;
136
+ content: ReactNode;
137
+ nextDisabled?: boolean;
138
+ onNext?: () => boolean | Promise<boolean> | string;
139
+ }
140
+
141
+ export interface FunnelHistory {
142
+ push: (step: string, context?: any) => void;
143
+ replace: (step: string, context?: any) => void;
144
+ back: () => void;
145
+ go?: (index: number) => void;
146
+ }
147
+
148
+ export interface FunnelNavigationConfig {
149
+ back?: boolean | ReactNode;
150
+ backLocation?:
151
+ | "header"
152
+ | "footer"
153
+ | { pc?: "header" | "footer"; mobile?: "header" | "footer" };
154
+ next?: boolean | ReactNode;
155
+ nextLocation?:
156
+ | "header"
157
+ | "footer"
158
+ | { pc?: "header" | "footer"; mobile?: "header" | "footer" };
159
+ exit?: boolean | ReactNode;
160
+ exitLocation?:
161
+ | "header"
162
+ | "footer"
163
+ | { pc?: "header" | "footer"; mobile?: "header" | "footer" };
164
+ }
165
+
166
+ export interface FunnelConfig {
167
+ id: string;
168
+ steps: FunnelStepConfig[];
169
+ onFinish?: () => void;
170
+ onMount?: (history: FunnelHistory) => void;
171
+ loading?: boolean;
172
+ isSubmitted?: boolean;
173
+ onRestart?: () => void;
174
+ navigation?: FunnelNavigationConfig;
175
+ preset?: "classic" | "oobe";
176
+ footer?: {
177
+ content?: ReactNode;
178
+ columnLayout?: boolean;
179
+ buttonCondensed?: boolean | "pc";
180
+ };
181
+ }
182
+
183
+ export interface DialogContextValue {
184
+ id: string;
185
+ mode: DialogMode;
186
+ open: boolean;
187
+ closeDialog: () => void;
188
+ loading?: boolean;
189
+ funnel?: {
190
+ currentStep: string;
191
+ currentIndex: number;
192
+ totalSteps: number;
193
+ history: FunnelHistory;
194
+ };
195
+ }
196
+
197
+ export interface DialogOutsideOptions {
198
+ lockBodyScroll?: boolean;
199
+ background?: ThemeBackgroundPaint;
200
+ backgroundBlur?: BackgroundBlurValue;
201
+ className?: string;
202
+ style?: React.CSSProperties;
203
+ }
204
+
205
+ export type DialogOutsideConfig = boolean | DialogOutsideOptions;
206
+
207
+ export interface CommonDialogProps {
208
+ open?: boolean;
209
+ onOpenChange?: (open: boolean) => void;
210
+ zIndex?: number;
211
+ id?: string;
212
+ width?: number | string;
213
+ height?: number | string;
214
+ content?: ReactNode;
215
+ role?: string;
216
+ exit?: ExitConfig;
217
+ "data-color-mode"?: string;
218
+ }
219
+
220
+ export type DialogProps =
221
+ | (CommonDialogProps & {
222
+ mode: "popover" | "modal";
223
+ children?: ReactNode;
224
+ content?: ReactNode;
225
+ offset?: OffsetConfig;
226
+ mobileMode?: DialogMobileMode;
227
+ popover?: PopoverConfig;
228
+ modal?: ModalConfig;
229
+ sheet?: SheetConfig;
230
+ funnel?: never;
231
+ })
232
+ | (CommonDialogProps & {
233
+ mode: "sheet";
234
+ children?: ReactNode;
235
+ content?: ReactNode;
236
+ offset?: never;
237
+ mobileMode?: never;
238
+ popover?: never;
239
+ modal?: never;
240
+ sheet?: SheetConfig;
241
+ funnel?: never;
242
+ })
243
+ | (CommonDialogProps & {
244
+ mode: "popover" | "modal";
245
+ children?: never;
246
+ content?: never;
247
+ offset?: OffsetConfig;
248
+ mobileMode?: DialogMobileMode;
249
+ popover?: Omit<PopoverConfig, "header" | "footer" | "exit">;
250
+ modal?: Omit<ModalConfig, "header" | "footer" | "custom" | "exit">;
251
+ sheet?: Omit<SheetConfig, "header" | "footer" | "exit">;
252
+ funnel: FunnelConfig;
253
+ })
254
+ | (CommonDialogProps & {
255
+ mode: "sheet";
256
+ children?: never;
257
+ content?: never;
258
+ offset?: never;
259
+ mobileMode?: never;
260
+ popover?: never;
261
+ modal?: never;
262
+ sheet?: Omit<SheetConfig, "header" | "footer" | "exit">;
263
+ funnel: FunnelConfig;
264
+ });
@@ -0,0 +1,22 @@
1
+ "use client";
2
+
3
+ import { type ReactNode, useEffect } from "react";
4
+ import { createPortal } from "react-dom";
5
+
6
+ export function useEscapeClose(active: boolean, onClose: () => void) {
7
+ useEffect(() => {
8
+ if (!active) return;
9
+
10
+ const handleKeyDown = (event: KeyboardEvent) => {
11
+ if (event.key === "Escape") onClose();
12
+ };
13
+
14
+ document.addEventListener("keydown", handleKeyDown);
15
+ return () => document.removeEventListener("keydown", handleKeyDown);
16
+ }, [active, onClose]);
17
+ }
18
+
19
+ export function DialogPortal({ children }: { children: ReactNode }) {
20
+ if (typeof document === "undefined") return null;
21
+ return createPortal(children, document.body);
22
+ }