@jbpark/ui-kit 10.0.2 → 10.2.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.
Files changed (176) hide show
  1. package/README.ko.md +7 -0
  2. package/README.md +7 -0
  3. package/dist/Alert.d.mts +24 -0
  4. package/dist/Alert.d.mts.map +1 -0
  5. package/dist/Alert.mjs +68 -0
  6. package/dist/Alert.mjs.map +1 -0
  7. package/dist/Avatar.d.mts +19 -0
  8. package/dist/Avatar.d.mts.map +1 -0
  9. package/dist/Avatar.mjs +43 -0
  10. package/dist/Avatar.mjs.map +1 -0
  11. package/dist/Badge.d.mts +23 -0
  12. package/dist/Badge.d.mts.map +1 -0
  13. package/dist/Badge.mjs +41 -0
  14. package/dist/Badge.mjs.map +1 -0
  15. package/dist/Breadcrumb.d.mts +39 -0
  16. package/dist/Breadcrumb.d.mts.map +1 -0
  17. package/dist/Breadcrumb.mjs +103 -0
  18. package/dist/Breadcrumb.mjs.map +1 -0
  19. package/dist/Button.mjs +1 -1
  20. package/dist/Pagination.d.mts +53 -0
  21. package/dist/Pagination.d.mts.map +1 -0
  22. package/dist/Pagination.mjs +208 -0
  23. package/dist/Pagination.mjs.map +1 -0
  24. package/dist/Segmented.d.mts +26 -0
  25. package/dist/Segmented.d.mts.map +1 -0
  26. package/dist/Segmented.mjs +57 -0
  27. package/dist/Segmented.mjs.map +1 -0
  28. package/dist/Steps.d.mts +28 -0
  29. package/dist/Steps.d.mts.map +1 -0
  30. package/dist/Steps.mjs +70 -0
  31. package/dist/Steps.mjs.map +1 -0
  32. package/dist/Table.d.mts +48 -0
  33. package/dist/Table.d.mts.map +1 -0
  34. package/dist/Table.mjs +119 -0
  35. package/dist/Table.mjs.map +1 -0
  36. package/dist/components/atoms/checkbox/checkbox.mjs +3 -2
  37. package/dist/components/atoms/checkbox/checkbox.mjs.map +1 -1
  38. package/dist/components/atoms/checkbox/group.mjs +2 -1
  39. package/dist/components/atoms/checkbox/group.mjs.map +1 -1
  40. package/dist/components/atoms/color-picker.mjs +1 -1
  41. package/dist/components/atoms/date-picker.mjs +2 -2
  42. package/dist/components/atoms/float-button/back-top.mjs +2 -2
  43. package/dist/components/atoms/index.d.mts +6 -1
  44. package/dist/components/atoms/input/index.d.mts +3 -1
  45. package/dist/components/atoms/input/index.d.mts.map +1 -1
  46. package/dist/components/atoms/input/index.mjs +3 -1
  47. package/dist/components/atoms/input/index.mjs.map +1 -1
  48. package/dist/components/atoms/input/number.d.mts +23 -0
  49. package/dist/components/atoms/input/number.d.mts.map +1 -0
  50. package/dist/components/atoms/input/number.mjs +71 -0
  51. package/dist/components/atoms/input/number.mjs.map +1 -0
  52. package/dist/components/atoms/input/search.d.mts +0 -5
  53. package/dist/components/atoms/input/search.d.mts.map +1 -1
  54. package/dist/components/atoms/input/search.mjs +8 -8
  55. package/dist/components/atoms/input/search.mjs.map +1 -1
  56. package/dist/components/atoms/popover.mjs +1 -1
  57. package/dist/components/atoms/progress.d.mts +3 -1
  58. package/dist/components/atoms/progress.d.mts.map +1 -1
  59. package/dist/components/atoms/progress.mjs +2 -2
  60. package/dist/components/atoms/progress.mjs.map +1 -1
  61. package/dist/components/atoms/radio/group.mjs +1 -1
  62. package/dist/components/atoms/radio/radio.mjs +3 -2
  63. package/dist/components/atoms/radio/radio.mjs.map +1 -1
  64. package/dist/components/atoms/rich-text-editor/rich-text-editor.d.mts +6 -1
  65. package/dist/components/atoms/rich-text-editor/rich-text-editor.d.mts.map +1 -1
  66. package/dist/components/atoms/rich-text-editor/rich-text-editor.mjs +7 -2
  67. package/dist/components/atoms/rich-text-editor/rich-text-editor.mjs.map +1 -1
  68. package/dist/components/atoms/rich-text-editor/toolbar-presets.mjs +1 -1
  69. package/dist/components/atoms/select-multiple.d.mts +29 -0
  70. package/dist/components/atoms/select-multiple.d.mts.map +1 -0
  71. package/dist/components/atoms/select-multiple.mjs +97 -0
  72. package/dist/components/atoms/select-multiple.mjs.map +1 -0
  73. package/dist/components/atoms/select-searchable-options.d.mts +21 -0
  74. package/dist/components/atoms/select-searchable-options.d.mts.map +1 -0
  75. package/dist/components/atoms/select-searchable-options.mjs +62 -0
  76. package/dist/components/atoms/select-searchable-options.mjs.map +1 -0
  77. package/dist/components/atoms/select-searchable.d.mts +22 -0
  78. package/dist/components/atoms/select-searchable.d.mts.map +1 -0
  79. package/dist/components/atoms/select-searchable.mjs +65 -0
  80. package/dist/components/atoms/select-searchable.mjs.map +1 -0
  81. package/dist/components/atoms/select.d.mts +8 -2
  82. package/dist/components/atoms/select.d.mts.map +1 -1
  83. package/dist/components/atoms/select.mjs +12 -2
  84. package/dist/components/atoms/select.mjs.map +1 -1
  85. package/dist/components/atoms/skeleton/skeleton.d.mts +3 -1
  86. package/dist/components/atoms/skeleton/skeleton.d.mts.map +1 -1
  87. package/dist/components/atoms/skeleton/skeleton.mjs +2 -2
  88. package/dist/components/atoms/skeleton/skeleton.mjs.map +1 -1
  89. package/dist/components/atoms/slider/slider.mjs +1 -1
  90. package/dist/components/atoms/switch.mjs +1 -1
  91. package/dist/components/atoms/tooltip.d.mts +21 -0
  92. package/dist/components/atoms/tooltip.d.mts.map +1 -0
  93. package/dist/components/atoms/tooltip.mjs +78 -0
  94. package/dist/components/atoms/tooltip.mjs.map +1 -0
  95. package/dist/components/atoms/typography/paragraph.mjs +1 -0
  96. package/dist/components/atoms/typography/paragraph.mjs.map +1 -1
  97. package/dist/components/atoms/typography/title.mjs +1 -0
  98. package/dist/components/atoms/typography/title.mjs.map +1 -1
  99. package/dist/components/molecules/collapse.d.mts +8 -8
  100. package/dist/components/molecules/collapse.d.mts.map +1 -1
  101. package/dist/components/molecules/collapse.mjs +11 -2
  102. package/dist/components/molecules/collapse.mjs.map +1 -1
  103. package/dist/components/molecules/dropdown.mjs +1 -1
  104. package/dist/components/molecules/index.d.mts +15 -10
  105. package/dist/components/molecules/marquees/marquees.mjs +1 -1
  106. package/dist/components/molecules/menu/item/item.mjs +1 -1
  107. package/dist/components/molecules/menu/menu.d.mts +1 -1
  108. package/dist/components/molecules/menu/menu.d.mts.map +1 -1
  109. package/dist/components/molecules/menu/menu.mjs +5 -2
  110. package/dist/components/molecules/menu/menu.mjs.map +1 -1
  111. package/dist/components/molecules/tabs.d.mts +41 -0
  112. package/dist/components/molecules/tabs.d.mts.map +1 -0
  113. package/dist/components/molecules/tabs.mjs +55 -0
  114. package/dist/components/molecules/tabs.mjs.map +1 -0
  115. package/dist/components/molecules/upload.mjs +3 -2
  116. package/dist/components/molecules/upload.mjs.map +1 -1
  117. package/dist/components/organisms/drawer.d.mts +1 -1
  118. package/dist/components/organisms/drawer.d.mts.map +1 -1
  119. package/dist/components/organisms/drawer.mjs +10 -4
  120. package/dist/components/organisms/drawer.mjs.map +1 -1
  121. package/dist/components/organisms/index.d.mts +6 -5
  122. package/dist/components/organisms/list/list.mjs +1 -1
  123. package/dist/components/organisms/modal.d.mts.map +1 -1
  124. package/dist/components/organisms/modal.mjs +8 -5
  125. package/dist/components/organisms/modal.mjs.map +1 -1
  126. package/dist/components/organisms/toast.d.mts.map +1 -1
  127. package/dist/components/organisms/toast.mjs +3 -2
  128. package/dist/components/organisms/toast.mjs.map +1 -1
  129. package/dist/components/templates/empty.mjs +1 -0
  130. package/dist/components/templates/empty.mjs.map +1 -1
  131. package/dist/components/templates/layout/content.mjs +1 -0
  132. package/dist/components/templates/layout/content.mjs.map +1 -1
  133. package/dist/components/templates/layout/footer.mjs +1 -0
  134. package/dist/components/templates/layout/footer.mjs.map +1 -1
  135. package/dist/components/templates/layout/header.mjs +1 -0
  136. package/dist/components/templates/layout/header.mjs.map +1 -1
  137. package/dist/components/templates/layout/layout.mjs +1 -0
  138. package/dist/components/templates/layout/layout.mjs.map +1 -1
  139. package/dist/components/templates/layout/sider.mjs +4 -2
  140. package/dist/components/templates/layout/sider.mjs.map +1 -1
  141. package/dist/components/templates/page-header.mjs +1 -0
  142. package/dist/components/templates/page-header.mjs.map +1 -1
  143. package/dist/components/templates/result.mjs +1 -0
  144. package/dist/components/templates/result.mjs.map +1 -1
  145. package/dist/core/accordion.mjs +1 -1
  146. package/dist/core/button.d.mts +1 -1
  147. package/dist/core/calendar.mjs +1 -1
  148. package/dist/core/checkbox.mjs +1 -1
  149. package/dist/core/combobox.d.mts +7 -0
  150. package/dist/core/combobox.d.mts.map +1 -0
  151. package/dist/core/combobox.mjs +99 -0
  152. package/dist/core/combobox.mjs.map +1 -0
  153. package/dist/core/dialog.mjs +1 -1
  154. package/dist/core/dialog.mjs.map +1 -1
  155. package/dist/core/drawer.mjs.map +1 -1
  156. package/dist/core/field.mjs +2 -4
  157. package/dist/core/field.mjs.map +1 -1
  158. package/dist/core/index.d.mts +4 -1
  159. package/dist/core/radio-group.mjs +1 -1
  160. package/dist/core/select.mjs +28 -23
  161. package/dist/core/select.mjs.map +1 -1
  162. package/dist/core/tabs.d.mts +9 -0
  163. package/dist/core/tabs.d.mts.map +1 -0
  164. package/dist/core/tabs.mjs +59 -0
  165. package/dist/core/tabs.mjs.map +1 -0
  166. package/dist/core/tooltip.d.mts +2 -0
  167. package/dist/core/tooltip.mjs +41 -0
  168. package/dist/core/tooltip.mjs.map +1 -0
  169. package/dist/index.d.mts +40 -29
  170. package/dist/index.mjs +12 -2
  171. package/dist/lib/z-layers.mjs +21 -0
  172. package/dist/lib/z-layers.mjs.map +1 -1
  173. package/dist/providers/config/dark-mode.mjs +2 -10
  174. package/dist/providers/config/dark-mode.mjs.map +1 -1
  175. package/dist/style.css +388 -16
  176. package/package.json +42 -2
@@ -3,8 +3,8 @@ import { cn } from "../../utils.mjs";
3
3
  import { DEFAULT_LOCALE, useConfig } from "../../providers/config/context.mjs";
4
4
  import { drawer_exports } from "../../core/drawer.mjs";
5
5
  import Button from "../../Button.mjs";
6
- import { useEffect } from "react";
7
6
  import { X } from "lucide-react";
7
+ import { useEffect } from "react";
8
8
  import { jsx, jsxs } from "react/jsx-runtime";
9
9
  //#region src/components/organisms/drawer.tsx
10
10
  const { DrawerContent, Drawer: DrawerCore, DrawerDescription, DrawerFooter, DrawerHeader, DrawerTitle } = drawer_exports;
@@ -22,6 +22,7 @@ const getMaskLessState = (doc) => {
22
22
  };
23
23
  const SIZES = {
24
24
  small: "30%",
25
+ middle: "50%",
25
26
  medium: "50%",
26
27
  large: "75%",
27
28
  full: "90%"
@@ -32,11 +33,16 @@ const ROUNDED_CLASSES = {
32
33
  left: "rounded-r-[30px]!",
33
34
  right: "rounded-l-[30px]!"
34
35
  };
36
+ const SIDE_MIN_WIDTH = "20rem";
35
37
  const getSizeStyles = (direction, size) => {
36
- const value = SIZES[size] || size;
37
- return direction === "top" || direction === "bottom" ? { height: value } : { width: value };
38
+ if (direction === "top" || direction === "bottom") return { height: SIZES[size] || size };
39
+ const preset = SIZES[size];
40
+ return {
41
+ width: preset ? `max(${preset}, ${SIDE_MIN_WIDTH})` : size,
42
+ maxWidth: "100%"
43
+ };
38
44
  };
39
- const Drawer = ({ open, title, extra, footer, closable = true, closeIcon, direction = "bottom", size = "medium", maskClosable = true, handlebar = true, rounded = false, mask = true, className, classNames, style, children, container, onClose, ...props }) => {
45
+ const Drawer = ({ open, title, extra, footer, closable = true, closeIcon, direction = "bottom", size = "middle", maskClosable = true, handlebar = true, rounded = false, mask = true, className, classNames, style, children, container, onClose, ...props }) => {
40
46
  const { locale } = useConfig();
41
47
  useEffect(() => {
42
48
  if (!open || mask) return;
@@ -1 +1 @@
1
- {"version":3,"file":"drawer.mjs","names":["drawer"],"sources":["../../../src/components/organisms/drawer.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect } from 'react';\n\nimport { X } from 'lucide-react';\n\nimport { DEFAULT_LOCALE, useConfig } from '@repo/ui/providers';\nimport { cn } from '@repo/ui/utils';\n\nimport { drawer } from '../../core';\nimport Button from '../atoms/button';\n\nconst {\n DrawerContent,\n Drawer: DrawerCore,\n DrawerDescription,\n DrawerFooter,\n DrawerHeader,\n DrawerTitle,\n} = drawer;\n\n// `body.style.pointerEvents` is global state (per document), so several\n// mask-less overlays opening at once would otherwise race: each instance\n// would capture whatever the previous one had already written and the\n// last cleanup would restore a stale value. Reference counting makes only\n// the first instance capture the original value and only the last one\n// restore it. Keyed by document (not a single module-level counter) so\n// drawers portaled into different documents — e.g. live-editor's iframe\n// Renderer — don't share state with the host document.\ntype MaskLessState = { count: number; originalPointerEvents: string | null };\nconst maskLessStates = new WeakMap<Document, MaskLessState>();\n\nconst getMaskLessState = (doc: Document): MaskLessState => {\n let state = maskLessStates.get(doc);\n\n if (!state) {\n state = { count: 0, originalPointerEvents: null };\n maskLessStates.set(doc, state);\n }\n\n return state;\n};\n\nexport interface Props {\n open: boolean;\n children?: React.ReactNode;\n title?: React.ReactNode;\n extra?: React.ReactNode;\n footer?: React.ReactNode;\n closable?: boolean;\n closeIcon?: React.ReactNode;\n direction?: 'top' | 'bottom' | 'left' | 'right';\n size?: 'small' | 'medium' | 'large' | 'full' | string;\n maskClosable?: boolean;\n handlebar?: boolean;\n rounded?: boolean;\n mask?: boolean;\n className?: string;\n style?: React.CSSProperties;\n classNames?: {\n mask?: string;\n content?: string;\n handlebar?: string;\n header?: string;\n title?: string;\n close?: string;\n extra?: string;\n body?: string;\n footer?: string;\n };\n container?: HTMLElement;\n onClose: () => void;\n}\n\nconst SIZES: Record<string, string> = {\n small: '30%',\n medium: '50%',\n large: '75%',\n full: '90%',\n};\n\n// Rounds the edge facing into the viewport — the opposite edge from\n// whichever side the drawer is anchored to.\nconst ROUNDED_CLASSES: Record<'top' | 'bottom' | 'left' | 'right', string> = {\n top: 'rounded-b-[30px]!',\n bottom: 'rounded-t-[30px]!',\n left: 'rounded-r-[30px]!',\n right: 'rounded-l-[30px]!',\n};\n\nconst getSizeStyles = (\n direction: 'top' | 'bottom' | 'left' | 'right',\n size: string,\n): React.CSSProperties => {\n const value = SIZES[size] || size;\n\n return direction === 'top' || direction === 'bottom'\n ? { height: value }\n : { width: value };\n};\n\nconst Drawer = ({\n open,\n title,\n extra,\n footer,\n closable = true,\n closeIcon,\n direction = 'bottom',\n size = 'medium',\n maskClosable = true,\n handlebar = true,\n rounded = false,\n mask = true,\n className,\n classNames,\n style,\n children,\n container,\n onClose,\n ...props\n}: Props) => {\n const { locale } = useConfig();\n\n useEffect(() => {\n if (!open || mask) {\n return;\n }\n\n const targetDocument = container?.ownerDocument ?? document;\n const targetWindow = targetDocument.defaultView ?? window;\n const body = targetDocument.body;\n const state = getMaskLessState(targetDocument);\n\n if (state.count === 0) {\n state.originalPointerEvents = body.style.pointerEvents;\n }\n state.count += 1;\n\n const raf = targetWindow.requestAnimationFrame(() => {\n body.style.pointerEvents = 'auto';\n });\n\n return () => {\n targetWindow.cancelAnimationFrame(raf);\n state.count -= 1;\n\n if (state.count === 0) {\n body.style.pointerEvents = state.originalPointerEvents ?? '';\n state.originalPointerEvents = null;\n }\n };\n }, [open, mask, container]);\n\n return (\n <DrawerCore\n open={open}\n // antd's mask ⇒ Base UI's modal (focus trap + scroll lock); maskClosable\n // gates outside-press dismissal at the root, replacing vaul's\n // Content-level onPointerDownOutside handler.\n modal={mask}\n disablePointerDismissal={!maskClosable}\n onOpenChange={open => {\n if (!open) {\n onClose();\n }\n }}\n {...props}\n >\n <DrawerContent\n direction={direction}\n container={container}\n className={cn(\n 'border-none outline-none',\n rounded ? ROUNDED_CLASSES[direction] : 'rounded-none!',\n classNames?.content,\n className,\n //\n )}\n handlebar={handlebar}\n mask={mask}\n classNames={{\n mask: classNames?.mask || '',\n handlebar: classNames?.handlebar || '',\n }}\n style={{\n ...getSizeStyles(direction, size),\n ...style,\n }}\n >\n <DrawerHeader\n className={cn(\n classNames?.header,\n !title && 'p-0',\n //\n )}\n >\n <div className=\"flex items-start gap-2\">\n {closable && (\n <Button\n variant=\"text\"\n shape=\"circle\"\n size=\"small\"\n aria-label={locale.close ?? DEFAULT_LOCALE.close}\n icon={closeIcon || <X />}\n onClick={onClose}\n className={cn(classNames?.close)}\n />\n )}\n <div className=\"flex flex-1 items-start justify-between gap-2\">\n <DrawerTitle className={cn(classNames?.title)}>\n {title}\n </DrawerTitle>\n {extra != null && (\n <div className={cn('shrink-0', classNames?.extra)}>{extra}</div>\n )}\n </div>\n </div>\n <DrawerDescription className=\"hidden\" />\n </DrawerHeader>\n <div\n className={cn(\n 'overflow-auto p-5',\n classNames?.body,\n //\n )}\n >\n {children}\n </div>\n {footer != null && (\n <DrawerFooter className={cn(classNames?.footer)}>\n {footer}\n </DrawerFooter>\n )}\n </DrawerContent>\n </DrawerCore>\n );\n};\n\nexport default Drawer;\n"],"mappings":"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"}
1
+ {"version":3,"file":"drawer.mjs","names":["drawer"],"sources":["../../../src/components/organisms/drawer.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect } from 'react';\n\nimport { X } from 'lucide-react';\n\nimport { DEFAULT_LOCALE, useConfig } from '@repo/ui/providers';\nimport { cn } from '@repo/ui/utils';\n\nimport { drawer } from '../../core';\nimport Button from '../atoms/button';\n\nconst {\n DrawerContent,\n Drawer: DrawerCore,\n DrawerDescription,\n DrawerFooter,\n DrawerHeader,\n DrawerTitle,\n} = drawer;\n\n// `body.style.pointerEvents` is global state (per document), so several\n// mask-less overlays opening at once would otherwise race: each instance\n// would capture whatever the previous one had already written and the\n// last cleanup would restore a stale value. Reference counting makes only\n// the first instance capture the original value and only the last one\n// restore it. Keyed by document (not a single module-level counter) so\n// drawers portaled into different documents — e.g. live-editor's iframe\n// Renderer — don't share state with the host document.\ntype MaskLessState = { count: number; originalPointerEvents: string | null };\nconst maskLessStates = new WeakMap<Document, MaskLessState>();\n\nconst getMaskLessState = (doc: Document): MaskLessState => {\n let state = maskLessStates.get(doc);\n\n if (!state) {\n state = { count: 0, originalPointerEvents: null };\n maskLessStates.set(doc, state);\n }\n\n return state;\n};\n\nexport interface Props {\n open: boolean;\n children?: React.ReactNode;\n title?: React.ReactNode;\n extra?: React.ReactNode;\n footer?: React.ReactNode;\n closable?: boolean;\n closeIcon?: React.ReactNode;\n direction?: 'top' | 'bottom' | 'left' | 'right';\n size?: 'small' | 'middle' | 'medium' | 'large' | 'full' | string;\n maskClosable?: boolean;\n handlebar?: boolean;\n rounded?: boolean;\n mask?: boolean;\n className?: string;\n style?: React.CSSProperties;\n classNames?: {\n mask?: string;\n content?: string;\n handlebar?: string;\n header?: string;\n title?: string;\n close?: string;\n extra?: string;\n body?: string;\n footer?: string;\n };\n container?: HTMLElement;\n onClose: () => void;\n}\n\nconst SIZES: Record<string, string> = {\n small: '30%',\n middle: '50%',\n medium: '50%',\n large: '75%',\n full: '90%',\n};\n\n// Rounds the edge facing into the viewport — the opposite edge from\n// whichever side the drawer is anchored to.\nconst ROUNDED_CLASSES: Record<'top' | 'bottom' | 'left' | 'right', string> = {\n top: 'rounded-b-[30px]!',\n bottom: 'rounded-t-[30px]!',\n left: 'rounded-r-[30px]!',\n right: 'rounded-l-[30px]!',\n};\n\n// A side drawer sized by a preset is sized by a percentage of the viewport,\n// which collapses on a phone: `small` is 30%, so it lands at ~112px on a 375px\n// screen — a sliver too narrow to read. Floor the preset widths so they stay\n// usable on small screens. A custom `size` is passed through untouched; an\n// explicit value is the caller's decision, not something to second-guess.\nconst SIDE_MIN_WIDTH = '20rem';\n\nconst getSizeStyles = (\n direction: 'top' | 'bottom' | 'left' | 'right',\n size: string,\n): React.CSSProperties => {\n if (direction === 'top' || direction === 'bottom') {\n return { height: SIZES[size] || size };\n }\n\n const preset = SIZES[size];\n\n return {\n width: preset ? `max(${preset}, ${SIDE_MIN_WIDTH})` : size,\n // Set inline so it beats `core/drawer`'s `sm:max-w-sm`, which would\n // otherwise hold every side drawer at 24rem from the `sm` breakpoint up\n // and leave `size` with no effect on anything wider than a phone. It also\n // keeps the floor above from overflowing a container narrower than it.\n maxWidth: '100%',\n };\n};\n\nconst Drawer = ({\n open,\n title,\n extra,\n footer,\n closable = true,\n closeIcon,\n direction = 'bottom',\n size = 'middle',\n maskClosable = true,\n handlebar = true,\n rounded = false,\n mask = true,\n className,\n classNames,\n style,\n children,\n container,\n onClose,\n ...props\n}: Props) => {\n const { locale } = useConfig();\n\n useEffect(() => {\n if (!open || mask) {\n return;\n }\n\n const targetDocument = container?.ownerDocument ?? document;\n const targetWindow = targetDocument.defaultView ?? window;\n const body = targetDocument.body;\n const state = getMaskLessState(targetDocument);\n\n if (state.count === 0) {\n state.originalPointerEvents = body.style.pointerEvents;\n }\n state.count += 1;\n\n const raf = targetWindow.requestAnimationFrame(() => {\n body.style.pointerEvents = 'auto';\n });\n\n return () => {\n targetWindow.cancelAnimationFrame(raf);\n state.count -= 1;\n\n if (state.count === 0) {\n body.style.pointerEvents = state.originalPointerEvents ?? '';\n state.originalPointerEvents = null;\n }\n };\n }, [open, mask, container]);\n\n return (\n <DrawerCore\n open={open}\n // antd's mask ⇒ Base UI's modal (focus trap + scroll lock); maskClosable\n // gates outside-press dismissal at the root, replacing vaul's\n // Content-level onPointerDownOutside handler.\n modal={mask}\n disablePointerDismissal={!maskClosable}\n onOpenChange={open => {\n if (!open) {\n onClose();\n }\n }}\n {...props}\n >\n <DrawerContent\n direction={direction}\n container={container}\n className={cn(\n 'border-none outline-none',\n rounded ? ROUNDED_CLASSES[direction] : 'rounded-none!',\n classNames?.content,\n className,\n //\n )}\n handlebar={handlebar}\n mask={mask}\n classNames={{\n mask: classNames?.mask || '',\n handlebar: classNames?.handlebar || '',\n }}\n style={{\n ...getSizeStyles(direction, size),\n ...style,\n }}\n >\n <DrawerHeader\n className={cn(\n classNames?.header,\n !title && 'p-0',\n //\n )}\n >\n <div className=\"flex items-start gap-2\">\n {closable && (\n <Button\n variant=\"text\"\n shape=\"circle\"\n size=\"small\"\n aria-label={locale.close ?? DEFAULT_LOCALE.close}\n icon={closeIcon || <X />}\n onClick={onClose}\n className={cn(classNames?.close)}\n />\n )}\n <div className=\"flex flex-1 items-start justify-between gap-2\">\n <DrawerTitle className={cn(classNames?.title)}>\n {title}\n </DrawerTitle>\n {extra != null && (\n <div className={cn('shrink-0', classNames?.extra)}>{extra}</div>\n )}\n </div>\n </div>\n <DrawerDescription className=\"hidden\" />\n </DrawerHeader>\n <div\n className={cn(\n 'overflow-auto p-5',\n classNames?.body,\n //\n )}\n >\n {children}\n </div>\n {footer != null && (\n <DrawerFooter className={cn(classNames?.footer)}>\n {footer}\n </DrawerFooter>\n )}\n </DrawerContent>\n </DrawerCore>\n );\n};\n\nexport default Drawer;\n"],"mappings":"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"}
@@ -1,8 +1,9 @@
1
- import Drawer, { Props } from "./drawer.mjs";
2
- import { Props as Props$1 } from "./list/list.mjs";
1
+ import Table, { Column, PaginationOptions, Props } from "../../Table.mjs";
2
+ import Drawer, { Props as Props$1 } from "./drawer.mjs";
3
+ import { Props as Props$2 } from "./list/list.mjs";
3
4
  import List from "./list/index.mjs";
4
- import Modal, { Props as Props$2 } from "./modal.mjs";
5
- import { Props as Props$3 } from "./swiper/swiper.mjs";
5
+ import Modal, { Props as Props$3 } from "./modal.mjs";
6
+ import { Props as Props$4 } from "./swiper/swiper.mjs";
6
7
  import Swiper from "./swiper/index.mjs";
7
8
  import Toast, { ToastOptions as StaticProps } from "./toast.mjs";
8
- export { Drawer, type Props as DrawerProps, List, type Props$1 as ListProps, Modal, type Props$2 as ModalProps, Swiper, type Props$3 as SwiperProps, Toast, type StaticProps as ToastOptions };
9
+ export { Drawer, type Props$1 as DrawerProps, List, type Props$2 as ListProps, Modal, type Props$3 as ModalProps, Swiper, type Props$4 as SwiperProps, Table, type Column as TableColumn, type PaginationOptions as TablePaginationOptions, type Props as TableProps, Toast, type StaticProps as ToastOptions };
@@ -3,8 +3,8 @@ import { cn, renderConditional } from "../../../utils.mjs";
3
3
  import Title from "../../atoms/typography/title.mjs";
4
4
  import Skeleton from "../../atoms/skeleton/index.mjs";
5
5
  import { useEffect, useRef } from "react";
6
- import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
7
6
  import { useIntersectionObserver } from "@jbpark/use-hooks";
7
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
8
8
  //#region src/components/organisms/list/list.tsx
9
9
  const List = ({ loading, loaderProps, loader, header, title, titleLevel = 2, empty, scroll, data, className, classNames, renderItem = () => null, itemKey, ...props }) => {
10
10
  const [loaderRef, { isIntersecting }] = useIntersectionObserver({
@@ -1 +1 @@
1
- {"version":3,"file":"modal.d.mts","names":[],"sources":["../../../src/components/organisms/modal.tsx"],"mappings":";;UAiCiB;EACf;EACA;EACA;IAGM,YAAY,MAAM;IAClB;;EAEN,YAAY,MAAM;EAClB,QAAQ,MAAM;EACd,UAAU,MAAM;EAChB,SAAS,MAAM;EACf;EACA;IACE;IACA;IACA;IACA;;EAEF,QAAQ,MAAM;EACd;EACA;EACA,YAAY;EACZ,WAAW,MAAM;EACjB;EACA;;KAGG;UAEK,oBAAoB;EAC5B,SAAS;;;;;;EAMT;EACA;EACA,OAAO,MAAM;EACb,YAAY;;cAGR;KAkBH,MAAA,cAAA,UAAA,WAAA,WAAA,YAAA,OAAA,OAAA,SAAA,QAAA,WAAA,UAAA,QAAA,YAAA,MAAA,aAAA,SAAA,QAAK,MAAA,IAAA;;;EAwNa,KAAA,OAAA;EAEG,QAAA,OAAA;EAEF,MAAA,OAAA;EAEE,QAAA,OAAA;EAIA,QAAA,OAAA"}
1
+ {"version":3,"file":"modal.d.mts","names":[],"sources":["../../../src/components/organisms/modal.tsx"],"mappings":";;UAiCiB;EACf;EACA;EACA;IAGM,YAAY,MAAM;IAClB;;EAEN,YAAY,MAAM;EAClB,QAAQ,MAAM;EACd,UAAU,MAAM;EAChB,SAAS,MAAM;EACf;EACA;IACE;IACA;IACA;IACA;;EAEF,QAAQ,MAAM;EACd;EACA;EACA,YAAY;EACZ,WAAW,MAAM;EACjB;EACA;;KAGG;UAEK,oBAAoB;EAC5B,SAAS;;;;;;EAMT;EACA;EACA,OAAO,MAAM;EACb,YAAY;;cAGR;KAkBH,MAAA,cAAA,UAAA,WAAA,WAAA,YAAA,OAAA,OAAA,SAAA,QAAA,WAAA,UAAA,QAAA,YAAA,MAAA,aAAA,SAAA,QAAK,MAAA,IAAA;;;EAuOa,KAAA,OAAA;EAEG,QAAA,OAAA;EAEF,MAAA,OAAA;EAEE,QAAA,OAAA;EAIA,QAAA,OAAA"}
@@ -4,8 +4,8 @@ import { DEFAULT_LOCALE, useConfig } from "../../providers/config/context.mjs";
4
4
  import { dialog_exports } from "../../core/dialog.mjs";
5
5
  import Button from "../../Button.mjs";
6
6
  import { createImperativeStack, isBrowser } from "./imperative-stack.mjs";
7
- import { useState } from "react";
8
7
  import { Check, CircleQuestionMark, Info, OctagonAlert, OctagonX } from "lucide-react";
8
+ import { useState } from "react";
9
9
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
10
10
  import { createPortal } from "react-dom";
11
11
  //#region src/components/organisms/modal.tsx
@@ -44,6 +44,7 @@ const Modal = ({ open = false, maskClosable, closable = false, closeIcon, classN
44
44
  footer !== null && /* @__PURE__ */ jsx(DialogFooter, {
45
45
  className: cn("flex-row justify-end gap-x-2", classNames?.footer),
46
46
  children: footer || /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Button, {
47
+ type: "primary",
47
48
  onClick: onOk,
48
49
  children: okText ?? locale.ok ?? DEFAULT_LOCALE.ok
49
50
  }), /* @__PURE__ */ jsx(Button, {
@@ -79,16 +80,18 @@ const StaticModal = ({ id, status, mode, title, content, okText, cancelText, con
79
80
  const footer = isConfirm ? /* @__PURE__ */ jsxs("div", {
80
81
  className: "grid w-full grid-cols-5 gap-x-2",
81
82
  children: [/* @__PURE__ */ jsx(Button, {
83
+ type: "primary",
84
+ className: "col-span-3",
85
+ onClick: () => closeModal(onOk),
86
+ children: resolvedOkText
87
+ }), /* @__PURE__ */ jsx(Button, {
82
88
  variant: "outlined",
83
89
  className: "col-span-2",
84
90
  onClick: () => closeModal(onCancel),
85
91
  children: resolvedCancelText
86
- }), /* @__PURE__ */ jsx(Button, {
87
- className: "col-span-3",
88
- onClick: () => closeModal(onOk),
89
- children: resolvedOkText
90
92
  })]
91
93
  }) : /* @__PURE__ */ jsx(Button, {
94
+ type: "primary",
92
95
  className: cn("grow"),
93
96
  onClick: () => closeModal(onOk),
94
97
  children: resolvedOkText
@@ -1 +1 @@
1
- {"version":3,"file":"modal.mjs","names":["dialog"],"sources":["../../../src/components/organisms/modal.tsx"],"sourcesContent":["'use client';\n\nimport React, { useState } from 'react';\nimport { createPortal } from 'react-dom';\n\nimport {\n Check,\n CircleQuestionMark,\n Info,\n OctagonAlert,\n OctagonX,\n} from 'lucide-react';\n\nimport { DEFAULT_LOCALE, useConfig } from '@repo/ui/providers';\nimport { cn } from '@repo/ui/utils';\n\nimport { dialog } from '../../core';\nimport Button from '../atoms/button';\nimport {\n type ImperativeStack,\n createImperativeStack,\n isBrowser,\n} from './imperative-stack';\n\nconst {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} = dialog;\n\nexport interface Props {\n open?: boolean;\n maskClosable?: boolean;\n closable?:\n | boolean\n | {\n closeIcon?: React.ReactNode;\n disabled?: boolean;\n };\n closeIcon?: React.ReactNode;\n title?: React.ReactNode;\n content?: React.ReactNode;\n footer?: React.ReactNode;\n className?: string;\n classNames?: {\n mask?: string;\n header?: string;\n body?: string;\n footer?: string;\n };\n style?: React.CSSProperties;\n okText?: string;\n cancelText?: string;\n container?: HTMLElement;\n children?: React.ReactNode;\n onOk?: () => void;\n onCancel?: () => void;\n}\n\ntype ModalStatus = 'info' | 'success' | 'error' | 'warning';\n\ninterface StaticProps extends Props {\n status?: ModalStatus;\n /**\n * Interaction mode set by `Modal.confirm()` to request the two-button\n * footer. `confirm` is a mode, not a status, so it rides its own axis rather\n * than `status` — call `Modal.confirm()` rather than setting this directly.\n */\n mode?: 'confirm';\n id?: string;\n icon?: React.ReactNode;\n container?: HTMLElement;\n}\n\nconst Modal = ({\n open = false,\n maskClosable,\n closable = false,\n closeIcon,\n className,\n classNames,\n style,\n title,\n content,\n footer,\n container,\n children,\n okText,\n cancelText,\n onOk,\n onCancel,\n ...props\n}: Props) => {\n const { locale, defaultProps } = useConfig();\n const modalDefaults = defaultProps?.modal as\n Partial<Pick<Props, 'maskClosable'>> | undefined;\n // Explicit prop > `Config` default > built-in, matching Button/Tag.\n const resolvedMaskClosable =\n maskClosable ?? modalDefaults?.maskClosable ?? false;\n\n if (typeof window === 'undefined') {\n return null;\n }\n\n return (\n <Dialog\n open={open}\n onOpenChange={(open: boolean) => {\n if (!open) {\n onCancel?.();\n }\n }}\n // antd's `maskClosable` is a root-level dismissal concern in Base UI:\n // blocking the outside (mask) press keeps the dialog open on backdrop\n // click while Escape still closes it, matching the previous behaviour.\n disablePointerDismissal={!resolvedMaskClosable}\n {...props}\n >\n <DialogContent\n className={cn(\n 'rounded-lg p-4 max-sm:max-w-[80%]',\n 'border-none outline-none',\n className,\n )}\n classNames={{\n ...classNames,\n mask: cn('bg-black/60', classNames?.mask),\n }}\n style={style}\n closable={closable}\n closeIcon={closeIcon}\n container={container}\n >\n {/**\n * @todo [Dialog & AlertDialog] fix: can't get id correctly in shadow dom\n *\n * Filed while `core/dialog` was still Radix-based; the linked fix is\n * Radix's, and the repo no longer depends on it. Revalidate against\n * Base UI's Dialog before acting on this — the bug may already be gone.\n * @see https://github.com/radix-ui/primitives/pull/3384\n **/}\n <DialogHeader\n className={cn(\n classNames?.header,\n !title && 'hidden',\n //\n )}\n >\n <DialogTitle>{title}</DialogTitle>\n <DialogDescription className=\"hidden\" />\n </DialogHeader>\n <div className={cn('break-all', classNames?.body)}>\n {children ?? content}\n </div>\n {footer !== null && (\n <DialogFooter\n className={cn('flex-row justify-end gap-x-2', classNames?.footer)}\n >\n {footer || (\n <>\n <Button onClick={onOk}>\n {okText ?? locale.ok ?? DEFAULT_LOCALE.ok}\n </Button>\n <Button variant=\"outlined\" onClick={onCancel}>\n {cancelText ?? locale.cancel ?? DEFAULT_LOCALE.cancel}\n </Button>\n </>\n )}\n </DialogFooter>\n )}\n </DialogContent>\n </Dialog>\n );\n};\n\nconst STATIC_ICONS = {\n info: <Info className=\"text-blue-400\" />,\n success: <Check className=\"text-green-400\" />,\n error: <OctagonX className=\"text-red-400\" />,\n warning: <OctagonAlert className=\"text-yellow-400\" />,\n confirm: <CircleQuestionMark />,\n};\n\nconst StaticModal = ({\n id,\n status,\n mode,\n title,\n content,\n okText,\n cancelText,\n container,\n icon,\n onOk,\n onCancel,\n ...props\n}: StaticProps & { id: string }): React.ReactPortal | null => {\n const { locale } = useConfig();\n const [open, setOpen] = useState(true);\n const resolvedOkText = okText ?? locale.ok ?? DEFAULT_LOCALE.ok;\n const resolvedCancelText =\n cancelText ?? locale.cancel ?? DEFAULT_LOCALE.cancel;\n // `confirm` is an interaction mode (two-button footer), not a status; the\n // semantic state comes from `status`.\n const isConfirm = mode === 'confirm';\n\n const closeModal = (callback?: () => void) => {\n callback?.();\n setOpen(false);\n setTimeout(() => {\n modalStack.destroy(id);\n }, 200);\n };\n\n const footer = isConfirm ? (\n <div className=\"grid w-full grid-cols-5 gap-x-2\">\n <Button\n variant=\"outlined\"\n className=\"col-span-2\"\n onClick={() => closeModal(onCancel)}\n >\n {resolvedCancelText}\n </Button>\n <Button className=\"col-span-3\" onClick={() => closeModal(onOk)}>\n {resolvedOkText}\n </Button>\n </div>\n ) : (\n <Button\n className={cn(\n 'grow',\n //\n )}\n onClick={() => closeModal(onOk)}\n >\n {resolvedOkText}\n </Button>\n );\n\n if (!isBrowser) {\n return null;\n }\n\n return createPortal(\n <Modal\n open={open}\n footer={footer}\n className={cn(\n 'z-10000',\n 'box-content w-100',\n 'rounded-3xl p-4',\n 'gap-y-0',\n )}\n classNames={{\n mask: 'z-10000 bg-black/[.6]',\n body: cn(\n 'text-black-70 whitespace-pre-wrap text-center',\n content && 'mt-3',\n ),\n footer: 'mt-9',\n }}\n title={\n <p\n className={cn(\n 'text-center text-lg leading-normal whitespace-pre-wrap',\n 'flex items-center justify-center gap-x-2',\n //\n )}\n >\n {icon ||\n (isConfirm ? STATIC_ICONS.confirm : status && STATIC_ICONS[status])}\n {title}\n </p>\n }\n container={container}\n onCancel={() => closeModal(onCancel)}\n {...props}\n >\n {content}\n </Modal>,\n modalStack.getRootElement(container)!,\n );\n};\n\n// No `role`/`aria-modal` here — this element is only a mount point for the\n// imperative stack, not the dialog itself. Base UI already renders its own\n// `role=\"dialog\"`/`aria-modal=\"true\"` on the actual `DialogContent` via\n// portal once a modal is open. Setting it here as well would (a) leave\n// `aria-modal=\"true\"` on the page permanently, even with zero modals open,\n// hiding the rest of the document from screen readers, and (b) duplicate\n// Base UI's own dialog role once one is open.\nconst modalStack: ImperativeStack<StaticProps> =\n createImperativeStack<StaticProps>({\n createRootElement: () => {\n const el = document.createElement('div');\n el.style.zIndex = '10000';\n el.style.position = 'absolute';\n return el;\n },\n StackItem: StaticModal,\n });\n\nModal.destroy = modalStack.destroy;\n\nModal.destroyAll = () => {\n modalStack.destroy();\n};\n\nModal.info = (props: StaticProps) =>\n modalStack.render({ status: 'info', ...props });\nModal.success = (props: StaticProps) =>\n modalStack.render({ status: 'success', ...props });\nModal.error = (props: StaticProps) =>\n modalStack.render({ status: 'error', ...props });\nModal.warning = (props: StaticProps) =>\n modalStack.render({ status: 'warning', ...props });\n// `confirm` is a mode, not a status, so it rides its own `mode` axis rather\n// than `status`.\nModal.confirm = (props: StaticProps) =>\n modalStack.render({ mode: 'confirm', ...props });\n\nexport default Modal;\n"],"mappings":"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"}
1
+ {"version":3,"file":"modal.mjs","names":["dialog"],"sources":["../../../src/components/organisms/modal.tsx"],"sourcesContent":["'use client';\n\nimport React, { useState } from 'react';\nimport { createPortal } from 'react-dom';\n\nimport {\n Check,\n CircleQuestionMark,\n Info,\n OctagonAlert,\n OctagonX,\n} from 'lucide-react';\n\nimport { DEFAULT_LOCALE, useConfig } from '@repo/ui/providers';\nimport { cn } from '@repo/ui/utils';\n\nimport { dialog } from '../../core';\nimport Button from '../atoms/button';\nimport {\n type ImperativeStack,\n createImperativeStack,\n isBrowser,\n} from './imperative-stack';\n\nconst {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} = dialog;\n\nexport interface Props {\n open?: boolean;\n maskClosable?: boolean;\n closable?:\n | boolean\n | {\n closeIcon?: React.ReactNode;\n disabled?: boolean;\n };\n closeIcon?: React.ReactNode;\n title?: React.ReactNode;\n content?: React.ReactNode;\n footer?: React.ReactNode;\n className?: string;\n classNames?: {\n mask?: string;\n header?: string;\n body?: string;\n footer?: string;\n };\n style?: React.CSSProperties;\n okText?: string;\n cancelText?: string;\n container?: HTMLElement;\n children?: React.ReactNode;\n onOk?: () => void;\n onCancel?: () => void;\n}\n\ntype ModalStatus = 'info' | 'success' | 'error' | 'warning';\n\ninterface StaticProps extends Props {\n status?: ModalStatus;\n /**\n * Interaction mode set by `Modal.confirm()` to request the two-button\n * footer. `confirm` is a mode, not a status, so it rides its own axis rather\n * than `status` — call `Modal.confirm()` rather than setting this directly.\n */\n mode?: 'confirm';\n id?: string;\n icon?: React.ReactNode;\n container?: HTMLElement;\n}\n\nconst Modal = ({\n open = false,\n maskClosable,\n closable = false,\n closeIcon,\n className,\n classNames,\n style,\n title,\n content,\n footer,\n container,\n children,\n okText,\n cancelText,\n onOk,\n onCancel,\n ...props\n}: Props) => {\n const { locale, defaultProps } = useConfig();\n const modalDefaults = defaultProps?.modal as\n Partial<Pick<Props, 'maskClosable'>> | undefined;\n // Explicit prop > `Config` default > built-in, matching Button/Tag.\n const resolvedMaskClosable =\n maskClosable ?? modalDefaults?.maskClosable ?? false;\n\n if (typeof window === 'undefined') {\n return null;\n }\n\n return (\n <Dialog\n open={open}\n onOpenChange={(open: boolean) => {\n if (!open) {\n onCancel?.();\n }\n }}\n // antd's `maskClosable` is a root-level dismissal concern in Base UI:\n // blocking the outside (mask) press keeps the dialog open on backdrop\n // click while Escape still closes it, matching the previous behaviour.\n disablePointerDismissal={!resolvedMaskClosable}\n {...props}\n >\n <DialogContent\n className={cn(\n 'rounded-lg p-4 max-sm:max-w-[80%]',\n 'border-none outline-none',\n className,\n )}\n classNames={{\n ...classNames,\n mask: cn('bg-black/60', classNames?.mask),\n }}\n style={style}\n closable={closable}\n closeIcon={closeIcon}\n container={container}\n >\n {/**\n * @todo [Dialog & AlertDialog] fix: can't get id correctly in shadow dom\n *\n * Filed while `core/dialog` was still Radix-based; the linked fix is\n * Radix's, and the repo no longer depends on it. Revalidate against\n * Base UI's Dialog before acting on this — the bug may already be gone.\n * @see https://github.com/radix-ui/primitives/pull/3384\n **/}\n <DialogHeader\n className={cn(\n classNames?.header,\n !title && 'hidden',\n //\n )}\n >\n <DialogTitle>{title}</DialogTitle>\n <DialogDescription className=\"hidden\" />\n </DialogHeader>\n <div className={cn('break-all', classNames?.body)}>\n {children ?? content}\n </div>\n {footer !== null && (\n <DialogFooter\n className={cn('flex-row justify-end gap-x-2', classNames?.footer)}\n >\n {footer || (\n <>\n {/*\n * The confirming action is solid primary and the dismissing one\n * stays outlined, so the two read as different weights. Both\n * used to resolve to `outlined` — Button defaults to that\n * variant, so the unstyled OK button rendered identically to\n * the explicitly outlined Cancel beside it.\n */}\n <Button type=\"primary\" onClick={onOk}>\n {okText ?? locale.ok ?? DEFAULT_LOCALE.ok}\n </Button>\n <Button variant=\"outlined\" onClick={onCancel}>\n {cancelText ?? locale.cancel ?? DEFAULT_LOCALE.cancel}\n </Button>\n </>\n )}\n </DialogFooter>\n )}\n </DialogContent>\n </Dialog>\n );\n};\n\nconst STATIC_ICONS = {\n info: <Info className=\"text-blue-400\" />,\n success: <Check className=\"text-green-400\" />,\n error: <OctagonX className=\"text-red-400\" />,\n warning: <OctagonAlert className=\"text-yellow-400\" />,\n confirm: <CircleQuestionMark />,\n};\n\nconst StaticModal = ({\n id,\n status,\n mode,\n title,\n content,\n okText,\n cancelText,\n container,\n icon,\n onOk,\n onCancel,\n ...props\n}: StaticProps & { id: string }): React.ReactPortal | null => {\n const { locale } = useConfig();\n const [open, setOpen] = useState(true);\n const resolvedOkText = okText ?? locale.ok ?? DEFAULT_LOCALE.ok;\n const resolvedCancelText =\n cancelText ?? locale.cancel ?? DEFAULT_LOCALE.cancel;\n // `confirm` is an interaction mode (two-button footer), not a status; the\n // semantic state comes from `status`.\n const isConfirm = mode === 'confirm';\n\n const closeModal = (callback?: () => void) => {\n callback?.();\n setOpen(false);\n setTimeout(() => {\n modalStack.destroy(id);\n }, 200);\n };\n\n // Confirming action first, dismissing action second — the same order as the\n // controlled footer above, which used to run the other way round here and\n // put the two footers of one component in opposite orders.\n const footer = isConfirm ? (\n <div className=\"grid w-full grid-cols-5 gap-x-2\">\n <Button\n type=\"primary\"\n className=\"col-span-3\"\n onClick={() => closeModal(onOk)}\n >\n {resolvedOkText}\n </Button>\n <Button\n variant=\"outlined\"\n className=\"col-span-2\"\n onClick={() => closeModal(onCancel)}\n >\n {resolvedCancelText}\n </Button>\n </div>\n ) : (\n <Button\n type=\"primary\"\n className={cn(\n 'grow',\n //\n )}\n onClick={() => closeModal(onOk)}\n >\n {resolvedOkText}\n </Button>\n );\n\n if (!isBrowser) {\n return null;\n }\n\n return createPortal(\n <Modal\n open={open}\n footer={footer}\n className={cn(\n 'z-10000',\n 'box-content w-100',\n 'rounded-3xl p-4',\n 'gap-y-0',\n )}\n classNames={{\n mask: 'z-10000 bg-black/[.6]',\n body: cn(\n 'text-black-70 whitespace-pre-wrap text-center',\n content && 'mt-3',\n ),\n footer: 'mt-9',\n }}\n title={\n <p\n className={cn(\n 'text-center text-lg leading-normal whitespace-pre-wrap',\n 'flex items-center justify-center gap-x-2',\n //\n )}\n >\n {icon ||\n (isConfirm ? STATIC_ICONS.confirm : status && STATIC_ICONS[status])}\n {title}\n </p>\n }\n container={container}\n onCancel={() => closeModal(onCancel)}\n {...props}\n >\n {content}\n </Modal>,\n modalStack.getRootElement(container)!,\n );\n};\n\n// No `role`/`aria-modal` here — this element is only a mount point for the\n// imperative stack, not the dialog itself. Base UI already renders its own\n// `role=\"dialog\"`/`aria-modal=\"true\"` on the actual `DialogContent` via\n// portal once a modal is open. Setting it here as well would (a) leave\n// `aria-modal=\"true\"` on the page permanently, even with zero modals open,\n// hiding the rest of the document from screen readers, and (b) duplicate\n// Base UI's own dialog role once one is open.\nconst modalStack: ImperativeStack<StaticProps> =\n createImperativeStack<StaticProps>({\n createRootElement: () => {\n const el = document.createElement('div');\n el.style.zIndex = '10000';\n el.style.position = 'absolute';\n return el;\n },\n StackItem: StaticModal,\n });\n\nModal.destroy = modalStack.destroy;\n\nModal.destroyAll = () => {\n modalStack.destroy();\n};\n\nModal.info = (props: StaticProps) =>\n modalStack.render({ status: 'info', ...props });\nModal.success = (props: StaticProps) =>\n modalStack.render({ status: 'success', ...props });\nModal.error = (props: StaticProps) =>\n modalStack.render({ status: 'error', ...props });\nModal.warning = (props: StaticProps) =>\n modalStack.render({ status: 'warning', ...props });\n// `confirm` is a mode, not a status, so it rides its own `mode` axis rather\n// than `status`.\nModal.confirm = (props: StaticProps) =>\n modalStack.render({ mode: 'confirm', ...props });\n\nexport default Modal;\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"toast.d.mts","names":[],"sources":["../../../src/components/organisms/toast.tsx"],"mappings":";KAkBK;UAEY;EACf,SAAS;EACT,OAAO,MAAM;EACb,cAAc,MAAM;EACpB,OAAO,MAAM;EACb;EACA,SAAS,MAAM;EACf;EACA;;UAGQ,oBAAoB;EAC5B;EACA;EACA,YAAY;;cAUR;KASH,QAAA,OAAA,aAAA,MAAA,UAAA,QAAA,WAAA,WAAA,wBAAK,IAAA;;;EAoHa,KAAA,OAAA,MAAM,WAAS,QAAU;EAEtB,QAAA,OAAA,MAAM,WAAS,QAAU;EAE3B,MAAA,OAAA,MAAM,WAAS,QAAU;EAEvB,QAAA,OAAA,MAAM,WAAS,QAAU;;KAR5C,iBAAiB,KAAK"}
1
+ {"version":3,"file":"toast.d.mts","names":[],"sources":["../../../src/components/organisms/toast.tsx"],"mappings":";KAkBK;UAEY;EACf,SAAS;EACT,OAAO,MAAM;EACb,cAAc,MAAM;EACpB,OAAO,MAAM;EACb;EACA,SAAS,MAAM;EACf;EACA;;UAGQ,oBAAoB;EAC5B;EACA;EACA,YAAY;;cAUR;KASH,QAAA,OAAA,aAAA,MAAA,UAAA,QAAA,WAAA,WAAA,wBAAK,IAAA;;;EAqHa,KAAA,OAAA,MAAM,WAAS,QAAU;EAEtB,QAAA,OAAA,MAAM,WAAS,QAAU;EAE3B,MAAA,OAAA,MAAM,WAAS,QAAU;EAEvB,QAAA,OAAA,MAAM,WAAS,QAAU;;KAR5C,iBAAiB,KAAK"}
@@ -3,10 +3,10 @@ import { cn } from "../../utils.mjs";
3
3
  import { DEFAULT_LOCALE, useConfig } from "../../providers/config/context.mjs";
4
4
  import Button from "../../Button.mjs";
5
5
  import { createImperativeStack, isBrowser } from "./imperative-stack.mjs";
6
- import { useState } from "react";
7
6
  import { Check, Info, OctagonAlert, OctagonX, X } from "lucide-react";
8
- import { jsx, jsxs } from "react/jsx-runtime";
7
+ import { useState } from "react";
9
8
  import { useTimeout } from "@jbpark/use-hooks";
9
+ import { jsx, jsxs } from "react/jsx-runtime";
10
10
  import { createPortal } from "react-dom";
11
11
  //#region src/components/organisms/toast.tsx
12
12
  const STATUS_ICONS = {
@@ -18,6 +18,7 @@ const STATUS_ICONS = {
18
18
  const Toast = ({ status, title, description, icon, closable = true, action, className, onClose }) => {
19
19
  const { locale } = useConfig();
20
20
  return /* @__PURE__ */ jsxs("div", {
21
+ "data-slot": "toast",
21
22
  role: status === "error" ? "alert" : "status",
22
23
  className: cn("pointer-events-auto flex w-80 items-start gap-3 rounded-lg", "bg-background text-foreground border p-4 shadow-lg", className),
23
24
  children: [
@@ -1 +1 @@
1
- {"version":3,"file":"toast.mjs","names":[],"sources":["../../../src/components/organisms/toast.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { createPortal } from 'react-dom';\n\nimport { useTimeout } from '@jbpark/use-hooks';\nimport { Check, Info, OctagonAlert, OctagonX, X } from 'lucide-react';\n\nimport { DEFAULT_LOCALE, useConfig } from '@repo/ui/providers';\nimport { cn } from '@repo/ui/utils';\n\nimport Button from '../atoms/button';\nimport {\n type ImperativeStack,\n createImperativeStack,\n isBrowser,\n} from './imperative-stack';\n\ntype ToastStatus = 'info' | 'success' | 'error' | 'warning';\n\nexport interface Props {\n status?: ToastStatus;\n title: React.ReactNode;\n description?: React.ReactNode;\n icon?: React.ReactNode;\n closable?: boolean;\n action?: React.ReactNode;\n className?: string;\n onClose?: () => void;\n}\n\ninterface StaticProps extends Props {\n id?: string;\n duration?: number;\n container?: HTMLElement;\n}\n\nconst STATUS_ICONS: Record<ToastStatus, React.ReactNode> = {\n info: <Info className=\"text-blue-400\" />,\n success: <Check className=\"text-green-400\" />,\n error: <OctagonX className=\"text-red-400\" />,\n warning: <OctagonAlert className=\"text-yellow-400\" />,\n};\n\nconst Toast = ({\n status,\n title,\n description,\n icon,\n closable = true,\n action,\n className,\n onClose,\n}: Props) => {\n const { locale } = useConfig();\n\n return (\n <div\n role={status === 'error' ? 'alert' : 'status'}\n className={cn(\n 'pointer-events-auto flex w-80 items-start gap-3 rounded-lg',\n 'bg-background text-foreground border p-4 shadow-lg',\n className,\n //\n )}\n >\n {(icon || status) && (\n <div className=\"mt-0.5 shrink-0 [&_svg]:size-5\">\n {icon || (status && STATUS_ICONS[status])}\n </div>\n )}\n <div className=\"flex-1 space-y-1\">\n <p className=\"text-sm font-medium\">{title}</p>\n {description && (\n <p className=\"text-muted-foreground text-sm\">{description}</p>\n )}\n {action && <div className=\"pt-1\">{action}</div>}\n </div>\n {closable && (\n <Button\n variant=\"text\"\n shape=\"circle\"\n size=\"small\"\n aria-label={locale.close ?? DEFAULT_LOCALE.close}\n icon={<X />}\n onClick={onClose}\n />\n )}\n </div>\n );\n};\n\nconst DEFAULT_DURATION = 4000;\n\nconst StaticToast = ({\n id,\n duration = DEFAULT_DURATION,\n container,\n onClose,\n ...props\n}: StaticProps & { id: string }): React.ReactPortal | null => {\n const [visible, setVisible] = useState(true);\n\n const close = () => {\n onClose?.();\n setVisible(false);\n setTimeout(() => {\n toastStack.destroy(id);\n }, 200);\n };\n\n const { reset, clear } = useTimeout(close, duration || null);\n\n if (!isBrowser) {\n return null;\n }\n\n return createPortal(\n <div\n className={cn(\n 'transition-all duration-200',\n visible ? 'opacity-100' : 'translate-x-2 opacity-0',\n )}\n // Without this, a toast with a closable/action button (or one the\n // user is mid-read on) could auto-dismiss itself out from under\n // them — hovering or focusing anything inside pauses the timer,\n // moving away restarts it from the full duration.\n onMouseEnter={clear}\n onMouseLeave={reset}\n onFocus={clear}\n onBlur={reset}\n >\n <Toast {...props} onClose={close} />\n </div>,\n toastStack.getRootElement(container)!,\n );\n};\n\nconst toastStack: ImperativeStack<StaticProps> =\n createImperativeStack<StaticProps>({\n createRootElement: () => {\n const el = document.createElement('div');\n el.setAttribute('role', 'region');\n // Plain DOM, created outside any React tree — no Config to read a\n // locale override from until #226 gives the imperative stack a way\n // to inherit one.\n el.setAttribute('aria-label', 'Notifications');\n el.style.zIndex = '10000';\n el.style.position = 'fixed';\n el.style.right = '0';\n el.style.bottom = '0';\n el.style.display = 'flex';\n el.style.flexDirection = 'column-reverse';\n el.style.gap = '8px';\n el.style.padding = '16px';\n el.style.pointerEvents = 'none';\n return el;\n },\n StackItem: StaticToast,\n });\n\nToast.destroy = toastStack.destroy;\n\nToast.destroyAll = () => {\n toastStack.destroy();\n};\n\ntype TriggerOptions = Omit<StaticProps, 'status' | 'title'>;\n\nToast.info = (title: React.ReactNode, props?: TriggerOptions) =>\n toastStack.render({ status: 'info', title, ...props });\nToast.success = (title: React.ReactNode, props?: TriggerOptions) =>\n toastStack.render({ status: 'success', title, ...props });\nToast.error = (title: React.ReactNode, props?: TriggerOptions) =>\n toastStack.render({ status: 'error', title, ...props });\nToast.warning = (title: React.ReactNode, props?: TriggerOptions) =>\n toastStack.render({ status: 'warning', title, ...props });\n\nexport default Toast;\nexport type { StaticProps as ToastOptions };\n"],"mappings":";;;;;;;;;;;AAqCA,MAAM,eAAqD;CACzD,MAAM,oBAAC,MAAD,EAAM,WAAU,gBAAiB,CAAA;CACvC,SAAS,oBAAC,OAAD,EAAO,WAAU,iBAAkB,CAAA;CAC5C,OAAO,oBAAC,UAAD,EAAU,WAAU,eAAgB,CAAA;CAC3C,SAAS,oBAAC,cAAD,EAAc,WAAU,kBAAmB,CAAA;AACtD;AAEA,MAAM,SAAS,EACb,QACA,OACA,aACA,MACA,WAAW,MACX,QACA,WACA,cACW;CACX,MAAM,EAAE,WAAW,UAAU;CAE7B,OACE,qBAAC,OAAD;EACE,MAAM,WAAW,UAAU,UAAU;EACrC,WAAW,GACT,8DACA,sDACA,SAEF;EAPF,UAAA;IASI,QAAQ,WACR,oBAAC,OAAD;IAAK,WAAU;IACZ,UAAA,QAAS,UAAU,aAAa;GAC9B,CAAA;GAEP,qBAAC,OAAD;IAAK,WAAU;IAAf,UAAA;KACE,oBAAC,KAAD;MAAG,WAAU;MAAuB,UAAA;KAAS,CAAA;KAC5C,eACC,oBAAC,KAAD;MAAG,WAAU;MAAiC,UAAA;KAAe,CAAA;KAE9D,UAAU,oBAAC,OAAD;MAAK,WAAU;MAAQ,UAAA;KAAY,CAAA;IAC3C;;GACJ,YACC,oBAAC,QAAD;IACE,SAAQ;IACR,OAAM;IACN,MAAK;IACL,cAAY,OAAO,SAAS,eAAe;IAC3C,MAAM,oBAAC,GAAD,CAAI,CAAA;IACV,SAAS;GACV,CAAA;EAEA;;AAET;AAEA,MAAM,mBAAmB;AAEzB,MAAM,eAAe,EACnB,IACA,WAAW,kBACX,WACA,SACA,GAAG,YACyD;CAC5D,MAAM,CAAC,SAAS,cAAc,SAAS,IAAI;CAE3C,MAAM,cAAc;EAClB,UAAU;EACV,WAAW,KAAK;EAChB,iBAAiB;GACf,WAAW,QAAQ,EAAE;EACvB,GAAG,GAAG;CACR;CAEA,MAAM,EAAE,OAAO,UAAU,WAAW,OAAO,YAAY,IAAI;CAE3D,IAAI,CAAC,WACH,OAAO;CAGT,OAAO,aACL,oBAAC,OAAD;EACE,WAAW,GACT,+BACA,UAAU,gBAAgB,yBAC5B;EAKA,cAAc;EACd,cAAc;EACd,SAAS;EACT,QAAQ;EAER,UAAA,oBAAC,OAAD;GAAO,GAAI;GAAO,SAAS;EAAQ,CAAA;CAChC,CAAA,GACL,WAAW,eAAe,SAAS,CACrC;AACF;AAEA,MAAM,aACJ,sBAAmC;CACjC,yBAAyB;EACvB,MAAM,KAAK,SAAS,cAAc,KAAK;EACvC,GAAG,aAAa,QAAQ,QAAQ;EAIhC,GAAG,aAAa,cAAc,eAAe;EAC7C,GAAG,MAAM,SAAS;EAClB,GAAG,MAAM,WAAW;EACpB,GAAG,MAAM,QAAQ;EACjB,GAAG,MAAM,SAAS;EAClB,GAAG,MAAM,UAAU;EACnB,GAAG,MAAM,gBAAgB;EACzB,GAAG,MAAM,MAAM;EACf,GAAG,MAAM,UAAU;EACnB,GAAG,MAAM,gBAAgB;EACzB,OAAO;CACT;CACA,WAAW;AACb,CAAC;AAEH,MAAM,UAAU,WAAW;AAE3B,MAAM,mBAAmB;CACvB,WAAW,QAAQ;AACrB;AAIA,MAAM,QAAQ,OAAwB,UACpC,WAAW,OAAO;CAAE,QAAQ;CAAQ;CAAO,GAAG;AAAM,CAAC;AACvD,MAAM,WAAW,OAAwB,UACvC,WAAW,OAAO;CAAE,QAAQ;CAAW;CAAO,GAAG;AAAM,CAAC;AAC1D,MAAM,SAAS,OAAwB,UACrC,WAAW,OAAO;CAAE,QAAQ;CAAS;CAAO,GAAG;AAAM,CAAC;AACxD,MAAM,WAAW,OAAwB,UACvC,WAAW,OAAO;CAAE,QAAQ;CAAW;CAAO,GAAG;AAAM,CAAC"}
1
+ {"version":3,"file":"toast.mjs","names":[],"sources":["../../../src/components/organisms/toast.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { createPortal } from 'react-dom';\n\nimport { useTimeout } from '@jbpark/use-hooks';\nimport { Check, Info, OctagonAlert, OctagonX, X } from 'lucide-react';\n\nimport { DEFAULT_LOCALE, useConfig } from '@repo/ui/providers';\nimport { cn } from '@repo/ui/utils';\n\nimport Button from '../atoms/button';\nimport {\n type ImperativeStack,\n createImperativeStack,\n isBrowser,\n} from './imperative-stack';\n\ntype ToastStatus = 'info' | 'success' | 'error' | 'warning';\n\nexport interface Props {\n status?: ToastStatus;\n title: React.ReactNode;\n description?: React.ReactNode;\n icon?: React.ReactNode;\n closable?: boolean;\n action?: React.ReactNode;\n className?: string;\n onClose?: () => void;\n}\n\ninterface StaticProps extends Props {\n id?: string;\n duration?: number;\n container?: HTMLElement;\n}\n\nconst STATUS_ICONS: Record<ToastStatus, React.ReactNode> = {\n info: <Info className=\"text-blue-400\" />,\n success: <Check className=\"text-green-400\" />,\n error: <OctagonX className=\"text-red-400\" />,\n warning: <OctagonAlert className=\"text-yellow-400\" />,\n};\n\nconst Toast = ({\n status,\n title,\n description,\n icon,\n closable = true,\n action,\n className,\n onClose,\n}: Props) => {\n const { locale } = useConfig();\n\n return (\n <div\n data-slot=\"toast\"\n role={status === 'error' ? 'alert' : 'status'}\n className={cn(\n 'pointer-events-auto flex w-80 items-start gap-3 rounded-lg',\n 'bg-background text-foreground border p-4 shadow-lg',\n className,\n //\n )}\n >\n {(icon || status) && (\n <div className=\"mt-0.5 shrink-0 [&_svg]:size-5\">\n {icon || (status && STATUS_ICONS[status])}\n </div>\n )}\n <div className=\"flex-1 space-y-1\">\n <p className=\"text-sm font-medium\">{title}</p>\n {description && (\n <p className=\"text-muted-foreground text-sm\">{description}</p>\n )}\n {action && <div className=\"pt-1\">{action}</div>}\n </div>\n {closable && (\n <Button\n variant=\"text\"\n shape=\"circle\"\n size=\"small\"\n aria-label={locale.close ?? DEFAULT_LOCALE.close}\n icon={<X />}\n onClick={onClose}\n />\n )}\n </div>\n );\n};\n\nconst DEFAULT_DURATION = 4000;\n\nconst StaticToast = ({\n id,\n duration = DEFAULT_DURATION,\n container,\n onClose,\n ...props\n}: StaticProps & { id: string }): React.ReactPortal | null => {\n const [visible, setVisible] = useState(true);\n\n const close = () => {\n onClose?.();\n setVisible(false);\n setTimeout(() => {\n toastStack.destroy(id);\n }, 200);\n };\n\n const { reset, clear } = useTimeout(close, duration || null);\n\n if (!isBrowser) {\n return null;\n }\n\n return createPortal(\n <div\n className={cn(\n 'transition-all duration-200',\n visible ? 'opacity-100' : 'translate-x-2 opacity-0',\n )}\n // Without this, a toast with a closable/action button (or one the\n // user is mid-read on) could auto-dismiss itself out from under\n // them — hovering or focusing anything inside pauses the timer,\n // moving away restarts it from the full duration.\n onMouseEnter={clear}\n onMouseLeave={reset}\n onFocus={clear}\n onBlur={reset}\n >\n <Toast {...props} onClose={close} />\n </div>,\n toastStack.getRootElement(container)!,\n );\n};\n\nconst toastStack: ImperativeStack<StaticProps> =\n createImperativeStack<StaticProps>({\n createRootElement: () => {\n const el = document.createElement('div');\n el.setAttribute('role', 'region');\n // Plain DOM, created outside any React tree — no Config to read a\n // locale override from until #226 gives the imperative stack a way\n // to inherit one.\n el.setAttribute('aria-label', 'Notifications');\n el.style.zIndex = '10000';\n el.style.position = 'fixed';\n el.style.right = '0';\n el.style.bottom = '0';\n el.style.display = 'flex';\n el.style.flexDirection = 'column-reverse';\n el.style.gap = '8px';\n el.style.padding = '16px';\n el.style.pointerEvents = 'none';\n return el;\n },\n StackItem: StaticToast,\n });\n\nToast.destroy = toastStack.destroy;\n\nToast.destroyAll = () => {\n toastStack.destroy();\n};\n\ntype TriggerOptions = Omit<StaticProps, 'status' | 'title'>;\n\nToast.info = (title: React.ReactNode, props?: TriggerOptions) =>\n toastStack.render({ status: 'info', title, ...props });\nToast.success = (title: React.ReactNode, props?: TriggerOptions) =>\n toastStack.render({ status: 'success', title, ...props });\nToast.error = (title: React.ReactNode, props?: TriggerOptions) =>\n toastStack.render({ status: 'error', title, ...props });\nToast.warning = (title: React.ReactNode, props?: TriggerOptions) =>\n toastStack.render({ status: 'warning', title, ...props });\n\nexport default Toast;\nexport type { StaticProps as ToastOptions };\n"],"mappings":";;;;;;;;;;;AAqCA,MAAM,eAAqD;CACzD,MAAM,oBAAC,MAAD,EAAM,WAAU,gBAAiB,CAAA;CACvC,SAAS,oBAAC,OAAD,EAAO,WAAU,iBAAkB,CAAA;CAC5C,OAAO,oBAAC,UAAD,EAAU,WAAU,eAAgB,CAAA;CAC3C,SAAS,oBAAC,cAAD,EAAc,WAAU,kBAAmB,CAAA;AACtD;AAEA,MAAM,SAAS,EACb,QACA,OACA,aACA,MACA,WAAW,MACX,QACA,WACA,cACW;CACX,MAAM,EAAE,WAAW,UAAU;CAE7B,OACE,qBAAC,OAAD;EACE,aAAU;EACV,MAAM,WAAW,UAAU,UAAU;EACrC,WAAW,GACT,8DACA,sDACA,SAEF;EARF,UAAA;IAUI,QAAQ,WACR,oBAAC,OAAD;IAAK,WAAU;IACZ,UAAA,QAAS,UAAU,aAAa;GAC9B,CAAA;GAEP,qBAAC,OAAD;IAAK,WAAU;IAAf,UAAA;KACE,oBAAC,KAAD;MAAG,WAAU;MAAuB,UAAA;KAAS,CAAA;KAC5C,eACC,oBAAC,KAAD;MAAG,WAAU;MAAiC,UAAA;KAAe,CAAA;KAE9D,UAAU,oBAAC,OAAD;MAAK,WAAU;MAAQ,UAAA;KAAY,CAAA;IAC3C;;GACJ,YACC,oBAAC,QAAD;IACE,SAAQ;IACR,OAAM;IACN,MAAK;IACL,cAAY,OAAO,SAAS,eAAe;IAC3C,MAAM,oBAAC,GAAD,CAAI,CAAA;IACV,SAAS;GACV,CAAA;EAEA;;AAET;AAEA,MAAM,mBAAmB;AAEzB,MAAM,eAAe,EACnB,IACA,WAAW,kBACX,WACA,SACA,GAAG,YACyD;CAC5D,MAAM,CAAC,SAAS,cAAc,SAAS,IAAI;CAE3C,MAAM,cAAc;EAClB,UAAU;EACV,WAAW,KAAK;EAChB,iBAAiB;GACf,WAAW,QAAQ,EAAE;EACvB,GAAG,GAAG;CACR;CAEA,MAAM,EAAE,OAAO,UAAU,WAAW,OAAO,YAAY,IAAI;CAE3D,IAAI,CAAC,WACH,OAAO;CAGT,OAAO,aACL,oBAAC,OAAD;EACE,WAAW,GACT,+BACA,UAAU,gBAAgB,yBAC5B;EAKA,cAAc;EACd,cAAc;EACd,SAAS;EACT,QAAQ;EAER,UAAA,oBAAC,OAAD;GAAO,GAAI;GAAO,SAAS;EAAQ,CAAA;CAChC,CAAA,GACL,WAAW,eAAe,SAAS,CACrC;AACF;AAEA,MAAM,aACJ,sBAAmC;CACjC,yBAAyB;EACvB,MAAM,KAAK,SAAS,cAAc,KAAK;EACvC,GAAG,aAAa,QAAQ,QAAQ;EAIhC,GAAG,aAAa,cAAc,eAAe;EAC7C,GAAG,MAAM,SAAS;EAClB,GAAG,MAAM,WAAW;EACpB,GAAG,MAAM,QAAQ;EACjB,GAAG,MAAM,SAAS;EAClB,GAAG,MAAM,UAAU;EACnB,GAAG,MAAM,gBAAgB;EACzB,GAAG,MAAM,MAAM;EACf,GAAG,MAAM,UAAU;EACnB,GAAG,MAAM,gBAAgB;EACzB,OAAO;CACT;CACA,WAAW;AACb,CAAC;AAEH,MAAM,UAAU,WAAW;AAE3B,MAAM,mBAAmB;CACvB,WAAW,QAAQ;AACrB;AAIA,MAAM,QAAQ,OAAwB,UACpC,WAAW,OAAO;CAAE,QAAQ;CAAQ;CAAO,GAAG;AAAM,CAAC;AACvD,MAAM,WAAW,OAAwB,UACvC,WAAW,OAAO;CAAE,QAAQ;CAAW;CAAO,GAAG;AAAM,CAAC;AAC1D,MAAM,SAAS,OAAwB,UACrC,WAAW,OAAO;CAAE,QAAQ;CAAS;CAAO,GAAG;AAAM,CAAC;AACxD,MAAM,WAAW,OAAwB,UACvC,WAAW,OAAO;CAAE,QAAQ;CAAW;CAAO,GAAG;AAAM,CAAC"}
@@ -4,6 +4,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
4
4
  //#region src/components/templates/empty.tsx
5
5
  const Empty = ({ icon, title, description = "No data", action, className, ...props }) => {
6
6
  return /* @__PURE__ */ jsxs("div", {
7
+ "data-slot": "empty",
7
8
  role: "status",
8
9
  className: cn("flex flex-col items-center justify-center gap-3 py-12 text-center", className),
9
10
  ...props,
@@ -1 +1 @@
1
- {"version":3,"file":"empty.mjs","names":[],"sources":["../../../src/components/templates/empty.tsx"],"sourcesContent":["import { Inbox } from 'lucide-react';\n\nimport { cn } from '@repo/ui/utils';\n\nexport interface Props extends Omit<React.ComponentProps<'div'>, 'title'> {\n icon?: React.ReactNode;\n title?: React.ReactNode;\n description?: React.ReactNode;\n action?: React.ReactNode;\n}\n\n// The counterpart List's `empty` prop was asking for — List/Upload/Swiper\n// each render arbitrary content for their empty state today with nothing\n// in the library actually meant to go there. Pairs with Skeleton the same\n// way Skeleton pairs with real content: one placeholder for \"nothing\n// loaded yet\", one for \"loaded, and there's nothing\".\nconst Empty = ({\n icon,\n title,\n description = 'No data',\n action,\n className,\n ...props\n}: Props) => {\n return (\n <div\n role=\"status\"\n className={cn(\n 'flex flex-col items-center justify-center gap-3 py-12 text-center',\n className,\n //\n )}\n {...props}\n >\n <div className=\"text-muted-foreground [&_svg]:size-12\">\n {icon ?? <Inbox />}\n </div>\n {title && <p className=\"text-sm font-medium\">{title}</p>}\n {description && (\n <p className=\"text-muted-foreground text-sm\">{description}</p>\n )}\n {action && <div className=\"mt-1\">{action}</div>}\n </div>\n );\n};\n\nexport default Empty;\n"],"mappings":";;;;AAgBA,MAAM,SAAS,EACb,MACA,OACA,cAAc,WACd,QACA,WACA,GAAG,YACQ;CACX,OACE,qBAAC,OAAD;EACE,MAAK;EACL,WAAW,GACT,qEACA,SAEF;EACA,GAAI;EAPN,UAAA;GASE,oBAAC,OAAD;IAAK,WAAU;IACZ,UAAA,QAAQ,oBAAC,OAAD,CAAQ,CAAA;GACd,CAAA;GACJ,SAAS,oBAAC,KAAD;IAAG,WAAU;IAAuB,UAAA;GAAS,CAAA;GACtD,eACC,oBAAC,KAAD;IAAG,WAAU;IAAiC,UAAA;GAAe,CAAA;GAE9D,UAAU,oBAAC,OAAD;IAAK,WAAU;IAAQ,UAAA;GAAY,CAAA;EAC3C;;AAET"}
1
+ {"version":3,"file":"empty.mjs","names":[],"sources":["../../../src/components/templates/empty.tsx"],"sourcesContent":["import { Inbox } from 'lucide-react';\n\nimport { cn } from '@repo/ui/utils';\n\nexport interface Props extends Omit<React.ComponentProps<'div'>, 'title'> {\n icon?: React.ReactNode;\n title?: React.ReactNode;\n description?: React.ReactNode;\n action?: React.ReactNode;\n}\n\n// The counterpart List's `empty` prop was asking for — List/Upload/Swiper\n// each render arbitrary content for their empty state today with nothing\n// in the library actually meant to go there. Pairs with Skeleton the same\n// way Skeleton pairs with real content: one placeholder for \"nothing\n// loaded yet\", one for \"loaded, and there's nothing\".\nconst Empty = ({\n icon,\n title,\n description = 'No data',\n action,\n className,\n ...props\n}: Props) => {\n return (\n <div\n data-slot=\"empty\"\n role=\"status\"\n className={cn(\n 'flex flex-col items-center justify-center gap-3 py-12 text-center',\n className,\n //\n )}\n {...props}\n >\n <div className=\"text-muted-foreground [&_svg]:size-12\">\n {icon ?? <Inbox />}\n </div>\n {title && <p className=\"text-sm font-medium\">{title}</p>}\n {description && (\n <p className=\"text-muted-foreground text-sm\">{description}</p>\n )}\n {action && <div className=\"mt-1\">{action}</div>}\n </div>\n );\n};\n\nexport default Empty;\n"],"mappings":";;;;AAgBA,MAAM,SAAS,EACb,MACA,OACA,cAAc,WACd,QACA,WACA,GAAG,YACQ;CACX,OACE,qBAAC,OAAD;EACE,aAAU;EACV,MAAK;EACL,WAAW,GACT,qEACA,SAEF;EACA,GAAI;EARN,UAAA;GAUE,oBAAC,OAAD;IAAK,WAAU;IACZ,UAAA,QAAQ,oBAAC,OAAD,CAAQ,CAAA;GACd,CAAA;GACJ,SAAS,oBAAC,KAAD;IAAG,WAAU;IAAuB,UAAA;GAAS,CAAA;GACtD,eACC,oBAAC,KAAD;IAAG,WAAU;IAAiC,UAAA;GAAe,CAAA;GAE9D,UAAU,oBAAC,OAAD;IAAK,WAAU;IAAQ,UAAA;GAAY,CAAA;EAC3C;;AAET"}
@@ -9,6 +9,7 @@ const Content = ({ children, className, render, ...props }) => {
9
9
  defaultTagName: "main",
10
10
  render,
11
11
  props: {
12
+ "data-slot": "layout-content",
12
13
  ...props,
13
14
  className: cn("min-w-0 shrink grow basis-auto", className),
14
15
  children: /* @__PURE__ */ jsx(SiderRegistryContext.Provider, {
@@ -1 +1 @@
1
- {"version":3,"file":"content.mjs","names":[],"sources":["../../../../src/components/templates/layout/content.tsx"],"sourcesContent":["'use client';\n\nimport { useRender } from '@base-ui/react/use-render';\n\nimport { cn } from '@repo/ui/utils';\n\nimport { SiderRegistryContext } from './sider-context';\n\nexport interface Props extends Omit<\n useRender.ComponentProps<'main'>,\n 'className'\n> {\n className?: string;\n}\n\nconst Content = ({ children, className, render, ...props }: Props) => {\n // Only one <main> landmark should exist per page — a Layout nested\n // inside a page that already has its own <main> (or a Content nested\n // inside another Content) needs a way to opt out of rendering a second\n // one. Base UI's render contract merges these props/classes onto the\n // caller's own element instead.\n return useRender({\n defaultTagName: 'main',\n render,\n props: {\n ...props,\n className: cn(\n 'min-w-0 shrink grow basis-auto',\n // basis-auto (not basis-0) sizes Content from its actual content\n // height first, so flex-grow only adds extra space when content is\n // shorter than the viewport — when content is taller, Content\n // keeps its natural (larger) height instead of clipping to\n // available space and pushes Footer below it. min-h-0 is\n // intentionally omitted here: with basis-auto, the default\n // min-height:auto (content-based) is what makes that possible; on\n // the Sider row's cross axis it's harmless since min-height there\n // governs stretch, not main-axis growth.\n className,\n ),\n children: (\n <SiderRegistryContext.Provider value={null}>\n {children}\n </SiderRegistryContext.Provider>\n ),\n },\n });\n};\n\nexport default Content;\n"],"mappings":";;;;;;AAeA,MAAM,WAAW,EAAE,UAAU,WAAW,QAAQ,GAAG,YAAmB;CAMpE,OAAO,UAAU;EACf,gBAAgB;EAChB;EACA,OAAO;GACL,GAAG;GACH,WAAW,GACT,kCAUA,SACF;GACA,UACE,oBAAC,qBAAqB,UAAtB;IAA+B,OAAO;IACnC;GAC4B,CAAA;EAEnC;CACF,CAAC;AACH"}
1
+ {"version":3,"file":"content.mjs","names":[],"sources":["../../../../src/components/templates/layout/content.tsx"],"sourcesContent":["'use client';\n\nimport { useRender } from '@base-ui/react/use-render';\n\nimport { cn } from '@repo/ui/utils';\n\nimport { SiderRegistryContext } from './sider-context';\n\nexport interface Props extends Omit<\n useRender.ComponentProps<'main'>,\n 'className'\n> {\n className?: string;\n}\n\nconst Content = ({ children, className, render, ...props }: Props) => {\n // Only one <main> landmark should exist per page — a Layout nested\n // inside a page that already has its own <main> (or a Content nested\n // inside another Content) needs a way to opt out of rendering a second\n // one. Base UI's render contract merges these props/classes onto the\n // caller's own element instead.\n return useRender({\n defaultTagName: 'main',\n render,\n props: {\n 'data-slot': 'layout-content',\n ...props,\n className: cn(\n 'min-w-0 shrink grow basis-auto',\n // basis-auto (not basis-0) sizes Content from its actual content\n // height first, so flex-grow only adds extra space when content is\n // shorter than the viewport — when content is taller, Content\n // keeps its natural (larger) height instead of clipping to\n // available space and pushes Footer below it. min-h-0 is\n // intentionally omitted here: with basis-auto, the default\n // min-height:auto (content-based) is what makes that possible; on\n // the Sider row's cross axis it's harmless since min-height there\n // governs stretch, not main-axis growth.\n className,\n ),\n children: (\n <SiderRegistryContext.Provider value={null}>\n {children}\n </SiderRegistryContext.Provider>\n ),\n },\n });\n};\n\nexport default Content;\n"],"mappings":";;;;;;AAeA,MAAM,WAAW,EAAE,UAAU,WAAW,QAAQ,GAAG,YAAmB;CAMpE,OAAO,UAAU;EACf,gBAAgB;EAChB;EACA,OAAO;GACL,aAAa;GACb,GAAG;GACH,WAAW,GACT,kCAUA,SACF;GACA,UACE,oBAAC,qBAAqB,UAAtB;IAA+B,OAAO;IACnC;GAC4B,CAAA;EAEnC;CACF,CAAC;AACH"}
@@ -3,6 +3,7 @@ import { jsx } from "react/jsx-runtime";
3
3
  //#region src/components/templates/layout/footer.tsx
4
4
  const Footer = ({ children, className, ...props }) => {
5
5
  return /* @__PURE__ */ jsx("footer", {
6
+ "data-slot": "layout-footer",
6
7
  className: cn("w-full", className),
7
8
  ...props,
8
9
  children
@@ -1 +1 @@
1
- {"version":3,"file":"footer.mjs","names":[],"sources":["../../../../src/components/templates/layout/footer.tsx"],"sourcesContent":["import { cn } from '@repo/ui/utils';\n\nexport interface Props extends React.ComponentProps<'footer'> {}\n\nconst Footer = ({ children, className, ...props }: Props) => {\n return (\n <footer className={cn('w-full', className)} {...props}>\n {children}\n </footer>\n );\n};\n\nexport default Footer;\n"],"mappings":";;;AAIA,MAAM,UAAU,EAAE,UAAU,WAAW,GAAG,YAAmB;CAC3D,OACE,oBAAC,UAAD;EAAQ,WAAW,GAAG,UAAU,SAAS;EAAG,GAAI;EAC7C;CACK,CAAA;AAEZ"}
1
+ {"version":3,"file":"footer.mjs","names":[],"sources":["../../../../src/components/templates/layout/footer.tsx"],"sourcesContent":["import { cn } from '@repo/ui/utils';\n\nexport interface Props extends React.ComponentProps<'footer'> {}\n\nconst Footer = ({ children, className, ...props }: Props) => {\n return (\n <footer\n data-slot=\"layout-footer\"\n className={cn('w-full', className)}\n {...props}\n >\n {children}\n </footer>\n );\n};\n\nexport default Footer;\n"],"mappings":";;;AAIA,MAAM,UAAU,EAAE,UAAU,WAAW,GAAG,YAAmB;CAC3D,OACE,oBAAC,UAAD;EACE,aAAU;EACV,WAAW,GAAG,UAAU,SAAS;EACjC,GAAI;EAEH;CACK,CAAA;AAEZ"}
@@ -4,6 +4,7 @@ import { jsx } from "react/jsx-runtime";
4
4
  //#region src/components/templates/layout/header.tsx
5
5
  const Header = ({ children, className, position = "sticky", ...props }) => {
6
6
  return /* @__PURE__ */ jsx("header", {
7
+ "data-slot": "layout-header",
7
8
  className: cn(position !== "static" && "z-1000", position === "sticky" && "sticky top-0", position === "fixed" && "fixed inset-x-0 top-0", "bg-background", "flex h-16 w-full items-center px-5", className),
8
9
  ...props,
9
10
  children
@@ -1 +1 @@
1
- {"version":3,"file":"header.mjs","names":[],"sources":["../../../../src/components/templates/layout/header.tsx"],"sourcesContent":["import { cn } from '@repo/ui/utils';\n\nimport { OVERLAY_LAYER } from '../../../lib/z-layers';\n\nexport interface Props extends React.ComponentProps<'header'> {\n position?: 'sticky' | 'static' | 'fixed';\n}\n\nconst Header = ({\n children,\n className,\n position = 'sticky',\n ...props\n}: Props) => {\n return (\n <header\n className={cn(\n position !== 'static' && OVERLAY_LAYER,\n position === 'sticky' && 'sticky top-0',\n position === 'fixed' && 'fixed inset-x-0 top-0',\n 'bg-background',\n 'flex h-16 w-full items-center px-5',\n className,\n //\n )}\n {...props}\n >\n {children}\n </header>\n );\n};\n\nexport default Header;\n"],"mappings":";;;;AAQA,MAAM,UAAU,EACd,UACA,WACA,WAAW,UACX,GAAG,YACQ;CACX,OACE,oBAAC,UAAD;EACE,WAAW,GACT,aAAa,YAAA,UACb,aAAa,YAAY,gBACzB,aAAa,WAAW,yBACxB,iBACA,sCACA,SAEF;EACA,GAAI;EAEH;CACK,CAAA;AAEZ"}
1
+ {"version":3,"file":"header.mjs","names":[],"sources":["../../../../src/components/templates/layout/header.tsx"],"sourcesContent":["import { cn } from '@repo/ui/utils';\n\nimport { OVERLAY_LAYER } from '../../../lib/z-layers';\n\nexport interface Props extends React.ComponentProps<'header'> {\n position?: 'sticky' | 'static' | 'fixed';\n}\n\nconst Header = ({\n children,\n className,\n position = 'sticky',\n ...props\n}: Props) => {\n return (\n <header\n data-slot=\"layout-header\"\n className={cn(\n position !== 'static' && OVERLAY_LAYER,\n position === 'sticky' && 'sticky top-0',\n position === 'fixed' && 'fixed inset-x-0 top-0',\n 'bg-background',\n 'flex h-16 w-full items-center px-5',\n className,\n //\n )}\n {...props}\n >\n {children}\n </header>\n );\n};\n\nexport default Header;\n"],"mappings":";;;;AAQA,MAAM,UAAU,EACd,UACA,WACA,WAAW,UACX,GAAG,YACQ;CACX,OACE,oBAAC,UAAD;EACE,aAAU;EACV,WAAW,GACT,aAAa,YAAA,UACb,aAAa,YAAY,gBACzB,aAAa,WAAW,yBACxB,iBACA,sCACA,SAEF;EACA,GAAI;EAEH;CACK,CAAA;AAEZ"}
@@ -26,6 +26,7 @@ const Layout = ({ children, className, ...props }) => {
26
26
  children: /* @__PURE__ */ jsx(SiderRegistryContext.Provider, {
27
27
  value: registry,
28
28
  children: /* @__PURE__ */ jsx("div", {
29
+ "data-slot": "layout",
29
30
  className: cn("flex w-full flex-1", isNested ? "min-h-0" : "min-h-screen", isRow ? "flex-row" : "flex-col", className),
30
31
  ...props,
31
32
  children
@@ -1 +1 @@
1
- {"version":3,"file":"layout.mjs","names":[],"sources":["../../../../src/components/templates/layout/layout.tsx"],"sourcesContent":["import {\n Children,\n createContext,\n isValidElement,\n useContext,\n useMemo,\n useState,\n} from 'react';\n\nimport { cn } from '@repo/ui/utils';\n\nimport Sider from './sider';\nimport { SiderRegistryContext } from './sider-context';\n\nexport interface Props extends React.ComponentProps<'div'> {}\n\nconst LayoutNestedContext = createContext(false);\n\n// Effects (including the registry-based detection in sider-context.ts)\n// never run during SSR, so registry state alone would render flex-col on\n// the server and every first paint, then flip to flex-row after hydration\n// for even the common, non-wrapped case. This direct-children check seeds\n// the correct initial guess for that common case so it's right immediately;\n// the registry then stays authoritative for Fragment-wrapped/custom-wrapped\n// Siders it can't see, correcting the guess after mount if needed.\nconst hasDirectSiderChild = (children: React.ReactNode) =>\n Children.toArray(children).some(\n child => isValidElement(child) && child.type === Sider,\n );\n\nconst Layout = ({ children, className, ...props }: Props) => {\n const isNested = useContext(LayoutNestedContext);\n const [siderIds] = useState(() => new Set<object>());\n const [siderCount, setSiderCount] = useState(() =>\n hasDirectSiderChild(children) ? 1 : 0,\n );\n\n const registry = useMemo(\n () => ({\n registerSider: (id: object) => {\n siderIds.add(id);\n setSiderCount(siderIds.size);\n },\n unregisterSider: (id: object) => {\n siderIds.delete(id);\n setSiderCount(siderIds.size);\n },\n }),\n [siderIds],\n );\n\n const isRow = siderCount > 0;\n\n return (\n <LayoutNestedContext.Provider value>\n <SiderRegistryContext.Provider value={registry}>\n <div\n className={cn(\n 'flex w-full flex-1',\n // A nested Layout (the Sider row wrapper) must stay free to\n // shrink/grow inside its parent's flex column, so it keeps\n // min-h-0. The outermost Layout has no ancestor to size against,\n // so it needs its own height anchor or flex-1/grow are no-ops\n // and Content collapses under Footer.\n isNested ? 'min-h-0' : 'min-h-screen',\n isRow ? 'flex-row' : 'flex-col',\n className,\n )}\n {...props}\n >\n {children}\n </div>\n </SiderRegistryContext.Provider>\n </LayoutNestedContext.Provider>\n );\n};\n\nexport default Layout;\n"],"mappings":";;;;;;AAgBA,MAAM,sBAAsB,cAAc,KAAK;AAS/C,MAAM,uBAAuB,aAC3B,SAAS,QAAQ,QAAQ,CAAC,CAAC,MACzB,UAAS,eAAe,KAAK,KAAK,MAAM,SAAS,KACnD;AAEF,MAAM,UAAU,EAAE,UAAU,WAAW,GAAG,YAAmB;CAC3D,MAAM,WAAW,WAAW,mBAAmB;CAC/C,MAAM,CAAC,YAAY,+BAAe,IAAI,IAAY,CAAC;CACnD,MAAM,CAAC,YAAY,iBAAiB,eAClC,oBAAoB,QAAQ,IAAI,IAAI,CACtC;CAEA,MAAM,WAAW,eACR;EACL,gBAAgB,OAAe;GAC7B,SAAS,IAAI,EAAE;GACf,cAAc,SAAS,IAAI;EAC7B;EACA,kBAAkB,OAAe;GAC/B,SAAS,OAAO,EAAE;GAClB,cAAc,SAAS,IAAI;EAC7B;CACF,IACA,CAAC,QAAQ,CACX;CAEA,MAAM,QAAQ,aAAa;CAE3B,OACE,oBAAC,oBAAoB,UAArB;EAA8B,OAAA;EAC5B,UAAA,oBAAC,qBAAqB,UAAtB;GAA+B,OAAO;GACpC,UAAA,oBAAC,OAAD;IACE,WAAW,GACT,sBAMA,WAAW,YAAY,gBACvB,QAAQ,aAAa,YACrB,SACF;IACA,GAAI;IAEH;GACE,CAAA;EACwB,CAAA;CACH,CAAA;AAElC"}
1
+ {"version":3,"file":"layout.mjs","names":[],"sources":["../../../../src/components/templates/layout/layout.tsx"],"sourcesContent":["import {\n Children,\n createContext,\n isValidElement,\n useContext,\n useMemo,\n useState,\n} from 'react';\n\nimport { cn } from '@repo/ui/utils';\n\nimport Sider from './sider';\nimport { SiderRegistryContext } from './sider-context';\n\nexport interface Props extends React.ComponentProps<'div'> {}\n\nconst LayoutNestedContext = createContext(false);\n\n// Effects (including the registry-based detection in sider-context.ts)\n// never run during SSR, so registry state alone would render flex-col on\n// the server and every first paint, then flip to flex-row after hydration\n// for even the common, non-wrapped case. This direct-children check seeds\n// the correct initial guess for that common case so it's right immediately;\n// the registry then stays authoritative for Fragment-wrapped/custom-wrapped\n// Siders it can't see, correcting the guess after mount if needed.\nconst hasDirectSiderChild = (children: React.ReactNode) =>\n Children.toArray(children).some(\n child => isValidElement(child) && child.type === Sider,\n );\n\nconst Layout = ({ children, className, ...props }: Props) => {\n const isNested = useContext(LayoutNestedContext);\n const [siderIds] = useState(() => new Set<object>());\n const [siderCount, setSiderCount] = useState(() =>\n hasDirectSiderChild(children) ? 1 : 0,\n );\n\n const registry = useMemo(\n () => ({\n registerSider: (id: object) => {\n siderIds.add(id);\n setSiderCount(siderIds.size);\n },\n unregisterSider: (id: object) => {\n siderIds.delete(id);\n setSiderCount(siderIds.size);\n },\n }),\n [siderIds],\n );\n\n const isRow = siderCount > 0;\n\n return (\n <LayoutNestedContext.Provider value>\n <SiderRegistryContext.Provider value={registry}>\n <div\n data-slot=\"layout\"\n className={cn(\n 'flex w-full flex-1',\n // A nested Layout (the Sider row wrapper) must stay free to\n // shrink/grow inside its parent's flex column, so it keeps\n // min-h-0. The outermost Layout has no ancestor to size against,\n // so it needs its own height anchor or flex-1/grow are no-ops\n // and Content collapses under Footer.\n isNested ? 'min-h-0' : 'min-h-screen',\n isRow ? 'flex-row' : 'flex-col',\n className,\n )}\n {...props}\n >\n {children}\n </div>\n </SiderRegistryContext.Provider>\n </LayoutNestedContext.Provider>\n );\n};\n\nexport default Layout;\n"],"mappings":";;;;;;AAgBA,MAAM,sBAAsB,cAAc,KAAK;AAS/C,MAAM,uBAAuB,aAC3B,SAAS,QAAQ,QAAQ,CAAC,CAAC,MACzB,UAAS,eAAe,KAAK,KAAK,MAAM,SAAS,KACnD;AAEF,MAAM,UAAU,EAAE,UAAU,WAAW,GAAG,YAAmB;CAC3D,MAAM,WAAW,WAAW,mBAAmB;CAC/C,MAAM,CAAC,YAAY,+BAAe,IAAI,IAAY,CAAC;CACnD,MAAM,CAAC,YAAY,iBAAiB,eAClC,oBAAoB,QAAQ,IAAI,IAAI,CACtC;CAEA,MAAM,WAAW,eACR;EACL,gBAAgB,OAAe;GAC7B,SAAS,IAAI,EAAE;GACf,cAAc,SAAS,IAAI;EAC7B;EACA,kBAAkB,OAAe;GAC/B,SAAS,OAAO,EAAE;GAClB,cAAc,SAAS,IAAI;EAC7B;CACF,IACA,CAAC,QAAQ,CACX;CAEA,MAAM,QAAQ,aAAa;CAE3B,OACE,oBAAC,oBAAoB,UAArB;EAA8B,OAAA;EAC5B,UAAA,oBAAC,qBAAqB,UAAtB;GAA+B,OAAO;GACpC,UAAA,oBAAC,OAAD;IACE,aAAU;IACV,WAAW,GACT,sBAMA,WAAW,YAAY,gBACvB,QAAQ,aAAa,YACrB,SACF;IACA,GAAI;IAEH;GACE,CAAA;EACwB,CAAA;CACH,CAAA;AAElC"}
@@ -2,10 +2,10 @@
2
2
  import { cn } from "../../../utils.mjs";
3
3
  import { DEFAULT_LOCALE, useConfig } from "../../../providers/config/context.mjs";
4
4
  import { useRegisterSider } from "./sider-context.mjs";
5
- import { useEffect, useId, useRef } from "react";
6
5
  import { PanelLeftClose, PanelLeftOpen } from "lucide-react";
7
- import { jsx, jsxs } from "react/jsx-runtime";
6
+ import { useEffect, useId, useRef } from "react";
8
7
  import { useControllableState, useResponsiveSize } from "@jbpark/use-hooks";
8
+ import { jsx, jsxs } from "react/jsx-runtime";
9
9
  //#region src/components/templates/layout/sider.tsx
10
10
  const BREAKPOINT_ORDER = [
11
11
  "xs",
@@ -57,6 +57,7 @@ const Sider = ({ children, className, classNames, style, width = 200, collapsedW
57
57
  };
58
58
  const TriggerIcon = collapsed !== (reverseArrow !== (placement === "right")) ? PanelLeftOpen : PanelLeftClose;
59
59
  return /* @__PURE__ */ jsxs("aside", {
60
+ "data-slot": "layout-sider",
60
61
  className: cn("z-10 flex h-full shrink-0 flex-col overflow-hidden", "transition-[width] duration-200", placement === "right" && "order-last", className),
61
62
  style: {
62
63
  width: toCssSize(collapsed ? collapsedWidth : width),
@@ -69,6 +70,7 @@ const Sider = ({ children, className, classNames, style, width = 200, collapsedW
69
70
  children
70
71
  }), collapsible && trigger !== null && /* @__PURE__ */ jsx("button", {
71
72
  type: "button",
73
+ "data-slot": "layout-sider-trigger",
72
74
  "aria-label": collapsed ? locale.expand ?? DEFAULT_LOCALE.expand : locale.collapse ?? DEFAULT_LOCALE.collapse,
73
75
  "aria-expanded": !collapsed,
74
76
  "aria-controls": contentId,
@@ -1 +1 @@
1
- {"version":3,"file":"sider.mjs","names":[],"sources":["../../../../src/components/templates/layout/sider.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useId, useRef } from 'react';\n\nimport { useControllableState, useResponsiveSize } from '@jbpark/use-hooks';\nimport { PanelLeftClose, PanelLeftOpen } from 'lucide-react';\n\nimport { DEFAULT_LOCALE, useConfig } from '@repo/ui/providers';\nimport { cn } from '@repo/ui/utils';\n\nimport { useRegisterSider } from './sider-context';\n\ntype Breakpoint = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';\n\n// Order matters here, not the values — used to compare the current\n// viewport tier against the configured `breakpoint` ordinally, since\n// `useResponsiveSize` reports which named tier the viewport is currently\n// in rather than a raw pixel width.\nconst BREAKPOINT_ORDER: Breakpoint[] = ['xs', 'sm', 'md', 'lg', 'xl', '2xl'];\n\nexport interface Props extends Omit<\n React.ComponentProps<'aside'>,\n 'onCollapse'\n> {\n width?: number | string;\n collapsedWidth?: number | string;\n collapsible?: boolean;\n defaultCollapsed?: boolean;\n collapsed?: boolean;\n placement?: 'left' | 'right';\n reverseArrow?: boolean;\n trigger?: React.ReactNode;\n classNames?: {\n trigger?: string;\n };\n breakpoint?: Breakpoint;\n onCollapse?: (collapsed: boolean) => void;\n onBreakpoint?: (broken: boolean) => void;\n}\n\nconst toCssSize = (value: number | string) =>\n typeof value === 'number' ? `${value}px` : value;\n\nconst Sider = ({\n children,\n className,\n classNames,\n style,\n width = 200,\n collapsedWidth = 80,\n collapsible = false,\n defaultCollapsed = false,\n collapsed: _collapsed,\n placement = 'left',\n reverseArrow = false,\n trigger,\n breakpoint,\n onCollapse,\n onBreakpoint,\n ...props\n}: Props) => {\n useRegisterSider();\n\n const { locale } = useConfig();\n const contentId = useId();\n const [collapsed, setCollapsed] = useControllableState<boolean>({\n value: _collapsed,\n defaultValue: defaultCollapsed,\n onChange: onCollapse,\n });\n\n const { breakpoint: viewportBreakpoint } = useResponsiveSize({\n viewport: true,\n });\n const broken = breakpoint\n ? BREAKPOINT_ORDER.indexOf(viewportBreakpoint.current) <\n BREAKPOINT_ORDER.indexOf(breakpoint)\n : false;\n\n const latestBrokenRef = useRef(broken);\n const previousBrokenRef = useRef<boolean | null>(null);\n const microtaskScheduledRef = useRef(false);\n const onBreakpointRef = useRef(onBreakpoint);\n\n useEffect(() => {\n onBreakpointRef.current = onBreakpoint;\n });\n\n // `useResponsiveSize`'s own initial measurement resolves through its own\n // layout effect, which (since it's called earlier in this component)\n // commits and — if the real viewport differs from its SSR-safe 'xs'\n // placeholder default — triggers a synchronous corrective re-render, all\n // before the browser paints. Both the placeholder-derived render and the\n // corrected one get their own effect pass (React doesn't coalesce\n // same-tick synchronous re-commits into a single effect flush), so\n // reacting to `broken` directly — in a layout or passive effect either\n // way — fires once with the wrong placeholder value, then again with the\n // corrected one.\n //\n // Coalescing through a microtask sidesteps that: every effect pass just\n // records its `broken` into a ref and schedules (at most once) a\n // microtask that reads whatever ended up latest once the synchronous\n // cascade has fully settled, and acts on that single, correct value.\n //\n // The `previousBrokenRef` guard is the separate fix for #227 point 3:\n // without it, an inline `onCollapse`/`onBreakpoint` (a fresh identity\n // every render) would change `setCollapsed`'s own identity and re-run\n // this effect on every unrelated parent re-render, reapplying `broken`\n // and undoing a user's manual toggle. Only a genuine change should act.\n useEffect(() => {\n if (!breakpoint) {\n return;\n }\n\n latestBrokenRef.current = broken;\n\n if (microtaskScheduledRef.current) {\n return;\n }\n microtaskScheduledRef.current = true;\n\n queueMicrotask(() => {\n microtaskScheduledRef.current = false;\n\n const settledBroken = latestBrokenRef.current;\n\n if (previousBrokenRef.current === settledBroken) {\n return;\n }\n\n previousBrokenRef.current = settledBroken;\n setCollapsed(settledBroken);\n onBreakpointRef.current?.(settledBroken);\n });\n }, [broken, breakpoint, setCollapsed]);\n\n const onTriggerClick = () => {\n setCollapsed(!collapsed);\n };\n\n // A right-placed Sider's collapse icon should default to pointing the\n // opposite way from a left-placed one (it's opening/closing a panel on\n // the other side of the content), so `placement` flips the effective\n // direction on top of whatever `reverseArrow` already requests —\n // `reverseArrow` still layers on as a manual override either way.\n const effectiveReverseArrow = reverseArrow !== (placement === 'right');\n const TriggerIcon =\n collapsed !== effectiveReverseArrow ? PanelLeftOpen : PanelLeftClose;\n\n return (\n <aside\n className={cn(\n // Below Header — and below every other floating/overlay primitive\n // in this library, which all share OVERLAY_LAYER (lib/z-layers.ts).\n // Sider is a static layout column, not floating chrome, and\n // previously outranked Header with an arbitrary z-100, covering it\n // once Header stuck to the viewport top on scroll. Kept as a small\n // absolute value rather than tracking OVERLAY_LAYER: it only has to\n // stay under that layer, and raising it in step would defeat this.\n 'z-10 flex h-full shrink-0 flex-col overflow-hidden',\n 'transition-[width] duration-200',\n // Lets a Sider visually sit on the right without needing to\n // reorder JSX/children — useful since Sider's presence is\n // detected via context (sider-context.ts) rather than requiring\n // it to be a specific direct child in a specific position.\n placement === 'right' && 'order-last',\n className,\n //\n )}\n style={{\n width: toCssSize(collapsed ? collapsedWidth : width),\n ...style,\n }}\n {...props}\n >\n <div id={contentId} className=\"min-h-0 flex-1 overflow-auto\">\n {children}\n </div>\n {collapsible && trigger !== null && (\n <button\n type=\"button\"\n aria-label={\n collapsed\n ? (locale.expand ?? DEFAULT_LOCALE.expand)\n : (locale.collapse ?? DEFAULT_LOCALE.collapse)\n }\n aria-expanded={!collapsed}\n aria-controls={contentId}\n onClick={onTriggerClick}\n className={cn(\n 'flex w-full shrink-0 items-center justify-center py-2',\n 'cursor-pointer transition-colors hover:bg-black/5',\n classNames?.trigger,\n //\n )}\n >\n {trigger === undefined ? <TriggerIcon className=\"size-4\" /> : trigger}\n </button>\n )}\n </aside>\n );\n};\n\nexport default Sider;\n"],"mappings":";;;;;;;;;AAkBA,MAAM,mBAAiC;CAAC;CAAM;CAAM;CAAM;CAAM;CAAM;AAAK;AAsB3E,MAAM,aAAa,UACjB,OAAO,UAAU,WAAW,GAAG,MAAM,MAAM;AAE7C,MAAM,SAAS,EACb,UACA,WACA,YACA,OACA,QAAQ,KACR,iBAAiB,IACjB,cAAc,OACd,mBAAmB,OACnB,WAAW,YACX,YAAY,QACZ,eAAe,OACf,SACA,YACA,YACA,cACA,GAAG,YACQ;CACX,iBAAiB;CAEjB,MAAM,EAAE,WAAW,UAAU;CAC7B,MAAM,YAAY,MAAM;CACxB,MAAM,CAAC,WAAW,gBAAgB,qBAA8B;EAC9D,OAAO;EACP,cAAc;EACd,UAAU;CACZ,CAAC;CAED,MAAM,EAAE,YAAY,uBAAuB,kBAAkB,EAC3D,UAAU,KACZ,CAAC;CACD,MAAM,SAAS,aACX,iBAAiB,QAAQ,mBAAmB,OAAO,IACnD,iBAAiB,QAAQ,UAAU,IACnC;CAEJ,MAAM,kBAAkB,OAAO,MAAM;CACrC,MAAM,oBAAoB,OAAuB,IAAI;CACrD,MAAM,wBAAwB,OAAO,KAAK;CAC1C,MAAM,kBAAkB,OAAO,YAAY;CAE3C,gBAAgB;EACd,gBAAgB,UAAU;CAC5B,CAAC;CAuBD,gBAAgB;EACd,IAAI,CAAC,YACH;EAGF,gBAAgB,UAAU;EAE1B,IAAI,sBAAsB,SACxB;EAEF,sBAAsB,UAAU;EAEhC,qBAAqB;GACnB,sBAAsB,UAAU;GAEhC,MAAM,gBAAgB,gBAAgB;GAEtC,IAAI,kBAAkB,YAAY,eAChC;GAGF,kBAAkB,UAAU;GAC5B,aAAa,aAAa;GAC1B,gBAAgB,UAAU,aAAa;EACzC,CAAC;CACH,GAAG;EAAC;EAAQ;EAAY;CAAY,CAAC;CAErC,MAAM,uBAAuB;EAC3B,aAAa,CAAC,SAAS;CACzB;CAQA,MAAM,cACJ,eAF4B,kBAAkB,cAAc,YAEtB,gBAAgB;CAExD,OACE,qBAAC,SAAD;EACE,WAAW,GAQT,sDACA,mCAKA,cAAc,WAAW,cACzB,SAEF;EACA,OAAO;GACL,OAAO,UAAU,YAAY,iBAAiB,KAAK;GACnD,GAAG;EACL;EACA,GAAI;EAvBN,UAAA,CAyBE,oBAAC,OAAD;GAAK,IAAI;GAAW,WAAU;GAC3B;EACE,CAAA,GACJ,eAAe,YAAY,QAC1B,oBAAC,UAAD;GACE,MAAK;GACL,cACE,YACK,OAAO,UAAU,eAAe,SAChC,OAAO,YAAY,eAAe;GAEzC,iBAAe,CAAC;GAChB,iBAAe;GACf,SAAS;GACT,WAAW,GACT,yDACA,qDACA,YAAY,OAEd;GAEC,UAAA,YAAY,KAAA,IAAY,oBAAC,aAAD,EAAa,WAAU,SAAU,CAAA,IAAI;EACxD,CAAA,CAEL;;AAEX"}
1
+ {"version":3,"file":"sider.mjs","names":[],"sources":["../../../../src/components/templates/layout/sider.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useId, useRef } from 'react';\n\nimport { useControllableState, useResponsiveSize } from '@jbpark/use-hooks';\nimport { PanelLeftClose, PanelLeftOpen } from 'lucide-react';\n\nimport { DEFAULT_LOCALE, useConfig } from '@repo/ui/providers';\nimport { cn } from '@repo/ui/utils';\n\nimport { useRegisterSider } from './sider-context';\n\ntype Breakpoint = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';\n\n// Order matters here, not the values — used to compare the current\n// viewport tier against the configured `breakpoint` ordinally, since\n// `useResponsiveSize` reports which named tier the viewport is currently\n// in rather than a raw pixel width.\nconst BREAKPOINT_ORDER: Breakpoint[] = ['xs', 'sm', 'md', 'lg', 'xl', '2xl'];\n\nexport interface Props extends Omit<\n React.ComponentProps<'aside'>,\n 'onCollapse'\n> {\n width?: number | string;\n collapsedWidth?: number | string;\n collapsible?: boolean;\n defaultCollapsed?: boolean;\n collapsed?: boolean;\n placement?: 'left' | 'right';\n reverseArrow?: boolean;\n trigger?: React.ReactNode;\n classNames?: {\n trigger?: string;\n };\n breakpoint?: Breakpoint;\n onCollapse?: (collapsed: boolean) => void;\n onBreakpoint?: (broken: boolean) => void;\n}\n\nconst toCssSize = (value: number | string) =>\n typeof value === 'number' ? `${value}px` : value;\n\nconst Sider = ({\n children,\n className,\n classNames,\n style,\n width = 200,\n collapsedWidth = 80,\n collapsible = false,\n defaultCollapsed = false,\n collapsed: _collapsed,\n placement = 'left',\n reverseArrow = false,\n trigger,\n breakpoint,\n onCollapse,\n onBreakpoint,\n ...props\n}: Props) => {\n useRegisterSider();\n\n const { locale } = useConfig();\n const contentId = useId();\n const [collapsed, setCollapsed] = useControllableState<boolean>({\n value: _collapsed,\n defaultValue: defaultCollapsed,\n onChange: onCollapse,\n });\n\n const { breakpoint: viewportBreakpoint } = useResponsiveSize({\n viewport: true,\n });\n const broken = breakpoint\n ? BREAKPOINT_ORDER.indexOf(viewportBreakpoint.current) <\n BREAKPOINT_ORDER.indexOf(breakpoint)\n : false;\n\n const latestBrokenRef = useRef(broken);\n const previousBrokenRef = useRef<boolean | null>(null);\n const microtaskScheduledRef = useRef(false);\n const onBreakpointRef = useRef(onBreakpoint);\n\n useEffect(() => {\n onBreakpointRef.current = onBreakpoint;\n });\n\n // `useResponsiveSize`'s own initial measurement resolves through its own\n // layout effect, which (since it's called earlier in this component)\n // commits and — if the real viewport differs from its SSR-safe 'xs'\n // placeholder default — triggers a synchronous corrective re-render, all\n // before the browser paints. Both the placeholder-derived render and the\n // corrected one get their own effect pass (React doesn't coalesce\n // same-tick synchronous re-commits into a single effect flush), so\n // reacting to `broken` directly — in a layout or passive effect either\n // way — fires once with the wrong placeholder value, then again with the\n // corrected one.\n //\n // Coalescing through a microtask sidesteps that: every effect pass just\n // records its `broken` into a ref and schedules (at most once) a\n // microtask that reads whatever ended up latest once the synchronous\n // cascade has fully settled, and acts on that single, correct value.\n //\n // The `previousBrokenRef` guard is the separate fix for #227 point 3:\n // without it, an inline `onCollapse`/`onBreakpoint` (a fresh identity\n // every render) would change `setCollapsed`'s own identity and re-run\n // this effect on every unrelated parent re-render, reapplying `broken`\n // and undoing a user's manual toggle. Only a genuine change should act.\n useEffect(() => {\n if (!breakpoint) {\n return;\n }\n\n latestBrokenRef.current = broken;\n\n if (microtaskScheduledRef.current) {\n return;\n }\n microtaskScheduledRef.current = true;\n\n queueMicrotask(() => {\n microtaskScheduledRef.current = false;\n\n const settledBroken = latestBrokenRef.current;\n\n if (previousBrokenRef.current === settledBroken) {\n return;\n }\n\n previousBrokenRef.current = settledBroken;\n setCollapsed(settledBroken);\n onBreakpointRef.current?.(settledBroken);\n });\n }, [broken, breakpoint, setCollapsed]);\n\n const onTriggerClick = () => {\n setCollapsed(!collapsed);\n };\n\n // A right-placed Sider's collapse icon should default to pointing the\n // opposite way from a left-placed one (it's opening/closing a panel on\n // the other side of the content), so `placement` flips the effective\n // direction on top of whatever `reverseArrow` already requests —\n // `reverseArrow` still layers on as a manual override either way.\n const effectiveReverseArrow = reverseArrow !== (placement === 'right');\n const TriggerIcon =\n collapsed !== effectiveReverseArrow ? PanelLeftOpen : PanelLeftClose;\n\n return (\n <aside\n data-slot=\"layout-sider\"\n className={cn(\n // Below Header — and below every other floating/overlay primitive\n // in this library, which all share OVERLAY_LAYER (lib/z-layers.ts).\n // Sider is a static layout column, not floating chrome, and\n // previously outranked Header with an arbitrary z-100, covering it\n // once Header stuck to the viewport top on scroll. Kept as a small\n // absolute value rather than tracking OVERLAY_LAYER: it only has to\n // stay under that layer, and raising it in step would defeat this.\n 'z-10 flex h-full shrink-0 flex-col overflow-hidden',\n 'transition-[width] duration-200',\n // Lets a Sider visually sit on the right without needing to\n // reorder JSX/children — useful since Sider's presence is\n // detected via context (sider-context.ts) rather than requiring\n // it to be a specific direct child in a specific position.\n placement === 'right' && 'order-last',\n className,\n //\n )}\n style={{\n width: toCssSize(collapsed ? collapsedWidth : width),\n ...style,\n }}\n {...props}\n >\n <div id={contentId} className=\"min-h-0 flex-1 overflow-auto\">\n {children}\n </div>\n {collapsible && trigger !== null && (\n <button\n type=\"button\"\n data-slot=\"layout-sider-trigger\"\n aria-label={\n collapsed\n ? (locale.expand ?? DEFAULT_LOCALE.expand)\n : (locale.collapse ?? DEFAULT_LOCALE.collapse)\n }\n aria-expanded={!collapsed}\n aria-controls={contentId}\n onClick={onTriggerClick}\n className={cn(\n 'flex w-full shrink-0 items-center justify-center py-2',\n 'cursor-pointer transition-colors hover:bg-black/5',\n classNames?.trigger,\n //\n )}\n >\n {trigger === undefined ? <TriggerIcon className=\"size-4\" /> : trigger}\n </button>\n )}\n </aside>\n );\n};\n\nexport default Sider;\n"],"mappings":";;;;;;;;;AAkBA,MAAM,mBAAiC;CAAC;CAAM;CAAM;CAAM;CAAM;CAAM;AAAK;AAsB3E,MAAM,aAAa,UACjB,OAAO,UAAU,WAAW,GAAG,MAAM,MAAM;AAE7C,MAAM,SAAS,EACb,UACA,WACA,YACA,OACA,QAAQ,KACR,iBAAiB,IACjB,cAAc,OACd,mBAAmB,OACnB,WAAW,YACX,YAAY,QACZ,eAAe,OACf,SACA,YACA,YACA,cACA,GAAG,YACQ;CACX,iBAAiB;CAEjB,MAAM,EAAE,WAAW,UAAU;CAC7B,MAAM,YAAY,MAAM;CACxB,MAAM,CAAC,WAAW,gBAAgB,qBAA8B;EAC9D,OAAO;EACP,cAAc;EACd,UAAU;CACZ,CAAC;CAED,MAAM,EAAE,YAAY,uBAAuB,kBAAkB,EAC3D,UAAU,KACZ,CAAC;CACD,MAAM,SAAS,aACX,iBAAiB,QAAQ,mBAAmB,OAAO,IACnD,iBAAiB,QAAQ,UAAU,IACnC;CAEJ,MAAM,kBAAkB,OAAO,MAAM;CACrC,MAAM,oBAAoB,OAAuB,IAAI;CACrD,MAAM,wBAAwB,OAAO,KAAK;CAC1C,MAAM,kBAAkB,OAAO,YAAY;CAE3C,gBAAgB;EACd,gBAAgB,UAAU;CAC5B,CAAC;CAuBD,gBAAgB;EACd,IAAI,CAAC,YACH;EAGF,gBAAgB,UAAU;EAE1B,IAAI,sBAAsB,SACxB;EAEF,sBAAsB,UAAU;EAEhC,qBAAqB;GACnB,sBAAsB,UAAU;GAEhC,MAAM,gBAAgB,gBAAgB;GAEtC,IAAI,kBAAkB,YAAY,eAChC;GAGF,kBAAkB,UAAU;GAC5B,aAAa,aAAa;GAC1B,gBAAgB,UAAU,aAAa;EACzC,CAAC;CACH,GAAG;EAAC;EAAQ;EAAY;CAAY,CAAC;CAErC,MAAM,uBAAuB;EAC3B,aAAa,CAAC,SAAS;CACzB;CAQA,MAAM,cACJ,eAF4B,kBAAkB,cAAc,YAEtB,gBAAgB;CAExD,OACE,qBAAC,SAAD;EACE,aAAU;EACV,WAAW,GAQT,sDACA,mCAKA,cAAc,WAAW,cACzB,SAEF;EACA,OAAO;GACL,OAAO,UAAU,YAAY,iBAAiB,KAAK;GACnD,GAAG;EACL;EACA,GAAI;EAxBN,UAAA,CA0BE,oBAAC,OAAD;GAAK,IAAI;GAAW,WAAU;GAC3B;EACE,CAAA,GACJ,eAAe,YAAY,QAC1B,oBAAC,UAAD;GACE,MAAK;GACL,aAAU;GACV,cACE,YACK,OAAO,UAAU,eAAe,SAChC,OAAO,YAAY,eAAe;GAEzC,iBAAe,CAAC;GAChB,iBAAe;GACf,SAAS;GACT,WAAW,GACT,yDACA,qDACA,YAAY,OAEd;GAEC,UAAA,YAAY,KAAA,IAAY,oBAAC,aAAD,EAAa,WAAU,SAAU,CAAA,IAAI;EACxD,CAAA,CAEL;;AAEX"}
@@ -8,6 +8,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
8
8
  const PageHeader = ({ title, subTitle, extra, backIcon, onBack, className, classNames, children, ...props }) => {
9
9
  const { locale } = useConfig();
10
10
  return /* @__PURE__ */ jsxs("div", {
11
+ "data-slot": "page-header",
11
12
  className: cn("flex flex-col gap-2", className),
12
13
  ...props,
13
14
  children: [/* @__PURE__ */ jsxs("div", {
@@ -1 +1 @@
1
- {"version":3,"file":"page-header.mjs","names":[],"sources":["../../../src/components/templates/page-header.tsx"],"sourcesContent":["'use client';\n\nimport { ArrowLeft } from 'lucide-react';\n\nimport { DEFAULT_LOCALE, useConfig } from '@repo/ui/providers';\nimport { cn } from '@repo/ui/utils';\n\nimport Button from '../atoms/button';\n\nexport interface Props extends Omit<React.ComponentProps<'div'>, 'title'> {\n title?: React.ReactNode;\n subTitle?: React.ReactNode;\n extra?: React.ReactNode;\n backIcon?: React.ReactNode;\n onBack?: () => void;\n classNames?: {\n back?: string;\n title?: string;\n subTitle?: string;\n extra?: string;\n };\n}\n\n// Title + back button + extra action area — same shape as Drawer's header\n// (title/extra/closable), which this borrows the layout from wholesale;\n// PageHeader just swaps Drawer's close button for a back button.\nconst PageHeader = ({\n title,\n subTitle,\n extra,\n backIcon,\n onBack,\n className,\n classNames,\n children,\n ...props\n}: Props) => {\n const { locale } = useConfig();\n\n return (\n <div className={cn('flex flex-col gap-2', className)} {...props}>\n <div className=\"flex items-start gap-2\">\n {onBack && (\n <Button\n variant=\"text\"\n shape=\"circle\"\n size=\"small\"\n aria-label={locale.back ?? DEFAULT_LOCALE.back}\n icon={backIcon || <ArrowLeft />}\n onClick={onBack}\n className={cn(classNames?.back)}\n />\n )}\n <div className=\"flex flex-1 items-start justify-between gap-2\">\n <div className=\"flex flex-col gap-0.5\">\n {title && (\n <p className={cn('text-lg font-medium', classNames?.title)}>\n {title}\n </p>\n )}\n {subTitle && (\n <p\n className={cn(\n 'text-muted-foreground text-sm',\n classNames?.subTitle,\n )}\n >\n {subTitle}\n </p>\n )}\n </div>\n {extra != null && (\n <div className={cn('shrink-0', classNames?.extra)}>{extra}</div>\n )}\n </div>\n </div>\n {children}\n </div>\n );\n};\n\nexport default PageHeader;\n"],"mappings":";;;;;;;AA0BA,MAAM,cAAc,EAClB,OACA,UACA,OACA,UACA,QACA,WACA,YACA,UACA,GAAG,YACQ;CACX,MAAM,EAAE,WAAW,UAAU;CAE7B,OACE,qBAAC,OAAD;EAAK,WAAW,GAAG,uBAAuB,SAAS;EAAG,GAAI;EAA1D,UAAA,CACE,qBAAC,OAAD;GAAK,WAAU;GAAf,UAAA,CACG,UACC,oBAAC,QAAD;IACE,SAAQ;IACR,OAAM;IACN,MAAK;IACL,cAAY,OAAO,QAAQ,eAAe;IAC1C,MAAM,YAAY,oBAAC,WAAD,CAAY,CAAA;IAC9B,SAAS;IACT,WAAW,GAAG,YAAY,IAAI;GAC/B,CAAA,GAEH,qBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,qBAAC,OAAD;KAAK,WAAU;KAAf,UAAA,CACG,SACC,oBAAC,KAAD;MAAG,WAAW,GAAG,uBAAuB,YAAY,KAAK;MACtD,UAAA;KACA,CAAA,GAEJ,YACC,oBAAC,KAAD;MACE,WAAW,GACT,iCACA,YAAY,QACd;MAEC,UAAA;KACA,CAAA,CAEF;IACJ,CAAA,GAAA,SAAS,QACR,oBAAC,OAAD;KAAK,WAAW,GAAG,YAAY,YAAY,KAAK;KAAI,UAAA;IAAW,CAAA,CAE9D;GACF,CAAA,CAAA;EACJ,CAAA,GAAA,QACE;;AAET"}
1
+ {"version":3,"file":"page-header.mjs","names":[],"sources":["../../../src/components/templates/page-header.tsx"],"sourcesContent":["'use client';\n\nimport { ArrowLeft } from 'lucide-react';\n\nimport { DEFAULT_LOCALE, useConfig } from '@repo/ui/providers';\nimport { cn } from '@repo/ui/utils';\n\nimport Button from '../atoms/button';\n\nexport interface Props extends Omit<React.ComponentProps<'div'>, 'title'> {\n title?: React.ReactNode;\n subTitle?: React.ReactNode;\n extra?: React.ReactNode;\n backIcon?: React.ReactNode;\n onBack?: () => void;\n classNames?: {\n back?: string;\n title?: string;\n subTitle?: string;\n extra?: string;\n };\n}\n\n// Title + back button + extra action area — same shape as Drawer's header\n// (title/extra/closable), which this borrows the layout from wholesale;\n// PageHeader just swaps Drawer's close button for a back button.\nconst PageHeader = ({\n title,\n subTitle,\n extra,\n backIcon,\n onBack,\n className,\n classNames,\n children,\n ...props\n}: Props) => {\n const { locale } = useConfig();\n\n return (\n <div\n data-slot=\"page-header\"\n className={cn('flex flex-col gap-2', className)}\n {...props}\n >\n <div className=\"flex items-start gap-2\">\n {onBack && (\n <Button\n variant=\"text\"\n shape=\"circle\"\n size=\"small\"\n aria-label={locale.back ?? DEFAULT_LOCALE.back}\n icon={backIcon || <ArrowLeft />}\n onClick={onBack}\n className={cn(classNames?.back)}\n />\n )}\n <div className=\"flex flex-1 items-start justify-between gap-2\">\n <div className=\"flex flex-col gap-0.5\">\n {title && (\n <p className={cn('text-lg font-medium', classNames?.title)}>\n {title}\n </p>\n )}\n {subTitle && (\n <p\n className={cn(\n 'text-muted-foreground text-sm',\n classNames?.subTitle,\n )}\n >\n {subTitle}\n </p>\n )}\n </div>\n {extra != null && (\n <div className={cn('shrink-0', classNames?.extra)}>{extra}</div>\n )}\n </div>\n </div>\n {children}\n </div>\n );\n};\n\nexport default PageHeader;\n"],"mappings":";;;;;;;AA0BA,MAAM,cAAc,EAClB,OACA,UACA,OACA,UACA,QACA,WACA,YACA,UACA,GAAG,YACQ;CACX,MAAM,EAAE,WAAW,UAAU;CAE7B,OACE,qBAAC,OAAD;EACE,aAAU;EACV,WAAW,GAAG,uBAAuB,SAAS;EAC9C,GAAI;EAHN,UAAA,CAKE,qBAAC,OAAD;GAAK,WAAU;GAAf,UAAA,CACG,UACC,oBAAC,QAAD;IACE,SAAQ;IACR,OAAM;IACN,MAAK;IACL,cAAY,OAAO,QAAQ,eAAe;IAC1C,MAAM,YAAY,oBAAC,WAAD,CAAY,CAAA;IAC9B,SAAS;IACT,WAAW,GAAG,YAAY,IAAI;GAC/B,CAAA,GAEH,qBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,qBAAC,OAAD;KAAK,WAAU;KAAf,UAAA,CACG,SACC,oBAAC,KAAD;MAAG,WAAW,GAAG,uBAAuB,YAAY,KAAK;MACtD,UAAA;KACA,CAAA,GAEJ,YACC,oBAAC,KAAD;MACE,WAAW,GACT,iCACA,YAAY,QACd;MAEC,UAAA;KACA,CAAA,CAEF;IACJ,CAAA,GAAA,SAAS,QACR,oBAAC,OAAD;KAAK,WAAW,GAAG,YAAY,YAAY,KAAK;KAAI,UAAA;IAAW,CAAA,CAE9D;GACF,CAAA,CAAA;EACJ,CAAA,GAAA,QACE;;AAET"}
@@ -13,6 +13,7 @@ const STATUS_ICONS = {
13
13
  };
14
14
  const Result = ({ status = "info", icon, title, subTitle, extra, className, children, ...props }) => {
15
15
  return /* @__PURE__ */ jsxs("div", {
16
+ "data-slot": "result",
16
17
  className: cn("flex flex-col items-center justify-center gap-2 py-16 text-center", className),
17
18
  ...props,
18
19
  children: [
@@ -1 +1 @@
1
- {"version":3,"file":"result.mjs","names":[],"sources":["../../../src/components/templates/result.tsx"],"sourcesContent":["import {\n Check,\n FileQuestion,\n Info,\n OctagonAlert,\n OctagonX,\n ServerCrash,\n ShieldAlert,\n} from 'lucide-react';\n\nimport { cn } from '@repo/ui/utils';\n\ntype Status = 'success' | 'error' | 'info' | 'warning' | '404' | '403' | '500';\n\n// Reuses Modal's exact STATIC_ICONS set (Check/OctagonX/Info/OctagonAlert,\n// same colors) for the 4 statuses they share, so a Result page matches the\n// tone of a Modal.success()/error()/etc a user may have just seen.\nconst STATUS_ICONS: Record<Status, React.ReactNode> = {\n success: <Check className=\"text-green-400\" />,\n error: <OctagonX className=\"text-red-400\" />,\n info: <Info className=\"text-blue-400\" />,\n warning: <OctagonAlert className=\"text-yellow-400\" />,\n '404': <FileQuestion className=\"text-muted-foreground\" />,\n '403': <ShieldAlert className=\"text-muted-foreground\" />,\n '500': <ServerCrash className=\"text-muted-foreground\" />,\n};\n\nexport interface Props extends Omit<React.ComponentProps<'div'>, 'title'> {\n status?: Status;\n icon?: React.ReactNode;\n title?: React.ReactNode;\n subTitle?: React.ReactNode;\n extra?: React.ReactNode;\n}\n\n// Full-page success/failure/404/403 state screen — Empty's larger,\n// more prominent sibling (Empty says \"nothing here yet\" inline within a\n// list/table; Result says \"here's what happened\" for an entire page/view).\nconst Result = ({\n status = 'info',\n icon,\n title,\n subTitle,\n extra,\n className,\n children,\n ...props\n}: Props) => {\n return (\n <div\n className={cn(\n 'flex flex-col items-center justify-center gap-2 py-16 text-center',\n className,\n //\n )}\n {...props}\n >\n <div className=\"mb-2 [&_svg]:size-16\">{icon ?? STATUS_ICONS[status]}</div>\n {title && <p className=\"text-xl font-medium\">{title}</p>}\n {subTitle && <p className=\"text-muted-foreground text-sm\">{subTitle}</p>}\n {extra && (\n <div className=\"mt-4 flex items-center justify-center gap-2\">\n {extra}\n </div>\n )}\n {children && <div className=\"mt-4 w-full\">{children}</div>}\n </div>\n );\n};\n\nexport default Result;\n"],"mappings":";;;;AAiBA,MAAM,eAAgD;CACpD,SAAS,oBAAC,OAAD,EAAO,WAAU,iBAAkB,CAAA;CAC5C,OAAO,oBAAC,UAAD,EAAU,WAAU,eAAgB,CAAA;CAC3C,MAAM,oBAAC,MAAD,EAAM,WAAU,gBAAiB,CAAA;CACvC,SAAS,oBAAC,cAAD,EAAc,WAAU,kBAAmB,CAAA;CACpD,OAAO,oBAAC,cAAD,EAAc,WAAU,wBAAyB,CAAA;CACxD,OAAO,oBAAC,aAAD,EAAa,WAAU,wBAAyB,CAAA;CACvD,OAAO,oBAAC,aAAD,EAAa,WAAU,wBAAyB,CAAA;AACzD;AAaA,MAAM,UAAU,EACd,SAAS,QACT,MACA,OACA,UACA,OACA,WACA,UACA,GAAG,YACQ;CACX,OACE,qBAAC,OAAD;EACE,WAAW,GACT,qEACA,SAEF;EACA,GAAI;EANN,UAAA;GAQE,oBAAC,OAAD;IAAK,WAAU;IAAwB,UAAA,QAAQ,aAAa;GAAa,CAAA;GACxE,SAAS,oBAAC,KAAD;IAAG,WAAU;IAAuB,UAAA;GAAS,CAAA;GACtD,YAAY,oBAAC,KAAD;IAAG,WAAU;IAAiC,UAAA;GAAY,CAAA;GACtE,SACC,oBAAC,OAAD;IAAK,WAAU;IACZ,UAAA;GACE,CAAA;GAEN,YAAY,oBAAC,OAAD;IAAK,WAAU;IAAe;GAAc,CAAA;EACtD;;AAET"}
1
+ {"version":3,"file":"result.mjs","names":[],"sources":["../../../src/components/templates/result.tsx"],"sourcesContent":["import {\n Check,\n FileQuestion,\n Info,\n OctagonAlert,\n OctagonX,\n ServerCrash,\n ShieldAlert,\n} from 'lucide-react';\n\nimport { cn } from '@repo/ui/utils';\n\ntype Status = 'success' | 'error' | 'info' | 'warning' | '404' | '403' | '500';\n\n// Reuses Modal's exact STATIC_ICONS set (Check/OctagonX/Info/OctagonAlert,\n// same colors) for the 4 statuses they share, so a Result page matches the\n// tone of a Modal.success()/error()/etc a user may have just seen.\nconst STATUS_ICONS: Record<Status, React.ReactNode> = {\n success: <Check className=\"text-green-400\" />,\n error: <OctagonX className=\"text-red-400\" />,\n info: <Info className=\"text-blue-400\" />,\n warning: <OctagonAlert className=\"text-yellow-400\" />,\n '404': <FileQuestion className=\"text-muted-foreground\" />,\n '403': <ShieldAlert className=\"text-muted-foreground\" />,\n '500': <ServerCrash className=\"text-muted-foreground\" />,\n};\n\nexport interface Props extends Omit<React.ComponentProps<'div'>, 'title'> {\n status?: Status;\n icon?: React.ReactNode;\n title?: React.ReactNode;\n subTitle?: React.ReactNode;\n extra?: React.ReactNode;\n}\n\n// Full-page success/failure/404/403 state screen — Empty's larger,\n// more prominent sibling (Empty says \"nothing here yet\" inline within a\n// list/table; Result says \"here's what happened\" for an entire page/view).\nconst Result = ({\n status = 'info',\n icon,\n title,\n subTitle,\n extra,\n className,\n children,\n ...props\n}: Props) => {\n return (\n <div\n data-slot=\"result\"\n className={cn(\n 'flex flex-col items-center justify-center gap-2 py-16 text-center',\n className,\n //\n )}\n {...props}\n >\n <div className=\"mb-2 [&_svg]:size-16\">{icon ?? STATUS_ICONS[status]}</div>\n {title && <p className=\"text-xl font-medium\">{title}</p>}\n {subTitle && <p className=\"text-muted-foreground text-sm\">{subTitle}</p>}\n {extra && (\n <div className=\"mt-4 flex items-center justify-center gap-2\">\n {extra}\n </div>\n )}\n {children && <div className=\"mt-4 w-full\">{children}</div>}\n </div>\n );\n};\n\nexport default Result;\n"],"mappings":";;;;AAiBA,MAAM,eAAgD;CACpD,SAAS,oBAAC,OAAD,EAAO,WAAU,iBAAkB,CAAA;CAC5C,OAAO,oBAAC,UAAD,EAAU,WAAU,eAAgB,CAAA;CAC3C,MAAM,oBAAC,MAAD,EAAM,WAAU,gBAAiB,CAAA;CACvC,SAAS,oBAAC,cAAD,EAAc,WAAU,kBAAmB,CAAA;CACpD,OAAO,oBAAC,cAAD,EAAc,WAAU,wBAAyB,CAAA;CACxD,OAAO,oBAAC,aAAD,EAAa,WAAU,wBAAyB,CAAA;CACvD,OAAO,oBAAC,aAAD,EAAa,WAAU,wBAAyB,CAAA;AACzD;AAaA,MAAM,UAAU,EACd,SAAS,QACT,MACA,OACA,UACA,OACA,WACA,UACA,GAAG,YACQ;CACX,OACE,qBAAC,OAAD;EACE,aAAU;EACV,WAAW,GACT,qEACA,SAEF;EACA,GAAI;EAPN,UAAA;GASE,oBAAC,OAAD;IAAK,WAAU;IAAwB,UAAA,QAAQ,aAAa;GAAa,CAAA;GACxE,SAAS,oBAAC,KAAD;IAAG,WAAU;IAAuB,UAAA;GAAS,CAAA;GACtD,YAAY,oBAAC,KAAD;IAAG,WAAU;IAAiC,UAAA;GAAY,CAAA;GACtE,SACC,oBAAC,OAAD;IAAK,WAAU;IACZ,UAAA;GACE,CAAA;GAEN,YAAY,oBAAC,OAAD;IAAK,WAAU;IAAe;GAAc,CAAA;EACtD;;AAET"}
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  import { __exportAll } from "../_virtual/_rolldown/runtime.mjs";
3
3
  import { cn } from "../utils.mjs";
4
- import "react";
5
4
  import { ChevronDownIcon } from "lucide-react";
5
+ import "react";
6
6
  import { jsx, jsxs } from "react/jsx-runtime";
7
7
  import { Accordion } from "@base-ui/react/accordion";
8
8
  //#region src/core/accordion.tsx
@@ -4,7 +4,7 @@ import { Button } from "@base-ui/react/button";
4
4
  //#region src/core/button.d.ts
5
5
  declare const buttonVariants: (props?: ({
6
6
  variant?: "link" | "outline" | "default" | "secondary" | "destructive" | "ghost" | null | undefined;
7
- size?: "default" | "icon" | "xs" | "sm" | "lg" | "icon-xs" | "icon-sm" | "icon-lg" | null | undefined;
7
+ size?: "icon" | "default" | "xs" | "sm" | "lg" | "icon-xs" | "icon-sm" | "icon-lg" | null | undefined;
8
8
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
9
9
  type Props = Omit<React$1.ComponentProps<typeof Button>, 'className'> & VariantProps<typeof buttonVariants> & {
10
10
  className?: string;
@@ -2,8 +2,8 @@
2
2
  import { __exportAll } from "../_virtual/_rolldown/runtime.mjs";
3
3
  import { cn } from "../utils.mjs";
4
4
  import { Button, buttonVariants } from "./button.mjs";
5
- import * as React$1 from "react";
6
5
  import { ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
6
+ import * as React$1 from "react";
7
7
  import { jsx } from "react/jsx-runtime";
8
8
  import { DayPicker, getDefaultClassNames } from "react-day-picker";
9
9
  //#region src/core/calendar.tsx