@jbpark/ui-kit 3.6.0 → 3.7.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 (31) hide show
  1. package/dist/Space.mjs +3 -1
  2. package/dist/Space.mjs.map +1 -1
  3. package/dist/components/molecules/collapse.mjs +1 -1
  4. package/dist/components/molecules/collapse.mjs.map +1 -1
  5. package/dist/components/molecules/marquees/item.d.mts.map +1 -1
  6. package/dist/components/molecules/marquees/item.mjs +3 -8
  7. package/dist/components/molecules/marquees/item.mjs.map +1 -1
  8. package/dist/components/molecules/reveals/reveals.d.mts +5 -0
  9. package/dist/components/molecules/reveals/reveals.d.mts.map +1 -1
  10. package/dist/components/molecules/reveals/reveals.mjs +6 -1
  11. package/dist/components/molecules/reveals/reveals.mjs.map +1 -1
  12. package/dist/components/molecules/upload.d.mts +10 -1
  13. package/dist/components/molecules/upload.d.mts.map +1 -1
  14. package/dist/components/molecules/upload.mjs +23 -6
  15. package/dist/components/molecules/upload.mjs.map +1 -1
  16. package/dist/components/organisms/modal.mjs +1 -1
  17. package/dist/components/organisms/toast.mjs +1 -1
  18. package/dist/components/templates/layout/header.mjs +1 -1
  19. package/dist/components/templates/layout/header.mjs.map +1 -1
  20. package/dist/components/templates/layout/layout.d.mts.map +1 -1
  21. package/dist/components/templates/layout/layout.mjs +23 -7
  22. package/dist/components/templates/layout/layout.mjs.map +1 -1
  23. package/dist/components/templates/layout/sider-context.mjs +20 -0
  24. package/dist/components/templates/layout/sider-context.mjs.map +1 -0
  25. package/dist/components/templates/layout/sider.d.mts.map +1 -1
  26. package/dist/components/templates/layout/sider.mjs +4 -2
  27. package/dist/components/templates/layout/sider.mjs.map +1 -1
  28. package/dist/providers/Config/index.mjs +6 -2
  29. package/dist/providers/Config/index.mjs.map +1 -1
  30. package/dist/style.css +72 -6
  31. package/package.json +1 -1
package/dist/Space.mjs CHANGED
@@ -19,7 +19,9 @@ const Space = ({ loading, loader, children, size = "middle", orientation = "hori
19
19
  const [x, y] = typeof size === "string" ? sizes[size] || [0, 0] : Array.isArray(size) ? size : [size, size];
20
20
  const count = Children.count(children);
21
21
  if (loading) return /* @__PURE__ */ jsx("div", {
22
- className: cn(className),
22
+ className: cn(hidden && "hidden", className),
23
+ style,
24
+ ...props,
23
25
  children: loader || /* @__PURE__ */ jsx(Skeleton, {})
24
26
  });
25
27
  return /* @__PURE__ */ jsx("div", {
@@ -1 +1 @@
1
- {"version":3,"file":"Space.mjs","names":[],"sources":["../src/components/molecules/space.tsx"],"sourcesContent":["import { Children } from 'react';\n\nimport { cn } from '@repo/ui/utils';\n\nimport Skeleton from '../atoms/skeleton';\n\ninterface Props extends React.ComponentPropsWithRef<'div'> {\n loading?: boolean;\n loader?: React.ReactNode;\n orientation?: 'horizontal' | 'vertical';\n size?: 'small' | 'middle' | 'large' | number | number[];\n align?: 'start' | 'end' | 'center' | 'baseline';\n wrap?: boolean;\n split?: React.ReactNode;\n grid?: {\n rows?: number;\n cols?: number;\n };\n}\n\nconst sizes: Record<string, number[]> = {\n small: [4, 4],\n middle: [8, 8],\n large: [16, 16],\n};\n\nconst aligns: Record<string, string> = {\n start: 'items-start text-start',\n end: 'items-end text-end',\n center: 'items-center text-center',\n baseline: 'items-baseline text-baseline',\n};\n\nconst Space = ({\n loading,\n loader,\n children,\n size = 'middle',\n orientation = 'horizontal',\n align = 'center',\n wrap = false,\n split,\n className,\n grid,\n hidden,\n style,\n ...props\n}: Props) => {\n const [x, y] =\n typeof size === 'string'\n ? sizes[size] || [0, 0]\n : Array.isArray(size)\n ? size\n : [size, size];\n const count = Children.count(children);\n\n if (loading) {\n return <div className={cn(className)}>{loader || <Skeleton />}</div>;\n }\n\n return (\n <div\n className={cn(\n 'flex flex-nowrap',\n orientation === 'horizontal' ? 'flex-row' : 'flex-col',\n wrap && 'flex-wrap',\n aligns[align],\n grid && 'grid',\n hidden && 'hidden',\n className,\n )}\n style={{\n columnGap: x,\n rowGap: y,\n gridTemplateRows: grid?.rows\n ? `repeat(${grid.rows}, minmax(0, 1fr))`\n : undefined,\n gridTemplateColumns: grid?.cols\n ? `repeat(${grid.cols}, minmax(0, 1fr))`\n : undefined,\n ...style,\n }}\n {...props}\n >\n {Children.map(children, (child, i) => (\n <>\n {child}\n {split && i < count - 1 && split}\n </>\n ))}\n </div>\n );\n};\n\nexport default Space;\n"],"mappings":";;;;;;AAoBA,MAAM,QAAkC;CACtC,OAAO,CAAC,GAAG,EAAE;CACb,QAAQ,CAAC,GAAG,EAAE;CACd,OAAO,CAAC,IAAI,GAAG;CAChB;AAED,MAAM,SAAiC;CACrC,OAAO;CACP,KAAK;CACL,QAAQ;CACR,UAAU;CACX;AAED,MAAM,SAAS,EACb,SACA,QACA,UACA,OAAO,UACP,cAAc,cACd,QAAQ,UACR,OAAO,OACP,OACA,WACA,MACA,QACA,OACA,GAAG,YACQ;CACX,MAAM,CAAC,GAAG,KACR,OAAO,SAAS,WACZ,MAAM,SAAS,CAAC,GAAG,EAAE,GACrB,MAAM,QAAQ,KAAK,GACjB,OACA,CAAC,MAAM,KAAK;CACpB,MAAM,QAAQ,SAAS,MAAM,SAAS;AAEtC,KAAI,QACF,QAAO,oBAAC;EAAI,WAAW,GAAG,UAAU;YAAG,UAAU,oBAAC,aAAW;GAAO;AAGtE,QACE,oBAAC;EACC,WAAW,GACT,oBACA,gBAAgB,eAAe,aAAa,YAC5C,QAAQ,aACR,OAAO,QACP,QAAQ,QACR,UAAU,UACV,UACD;EACD,OAAO;GACL,WAAW;GACX,QAAQ;GACR,kBAAkB,MAAM,OACpB,UAAU,KAAK,KAAK,qBACpB;GACJ,qBAAqB,MAAM,OACvB,UAAU,KAAK,KAAK,qBACpB;GACJ,GAAG;GACJ;EACD,GAAI;YAEH,SAAS,IAAI,WAAW,OAAO,MAC9B,8CACG,OACA,SAAS,IAAI,QAAQ,KAAK,SAC1B,CACH;GACE"}
1
+ {"version":3,"file":"Space.mjs","names":[],"sources":["../src/components/molecules/space.tsx"],"sourcesContent":["import { Children } from 'react';\n\nimport { cn } from '@repo/ui/utils';\n\nimport Skeleton from '../atoms/skeleton';\n\ninterface Props extends React.ComponentPropsWithRef<'div'> {\n loading?: boolean;\n loader?: React.ReactNode;\n orientation?: 'horizontal' | 'vertical';\n size?: 'small' | 'middle' | 'large' | number | number[];\n align?: 'start' | 'end' | 'center' | 'baseline';\n wrap?: boolean;\n split?: React.ReactNode;\n grid?: {\n rows?: number;\n cols?: number;\n };\n}\n\nconst sizes: Record<string, number[]> = {\n small: [4, 4],\n middle: [8, 8],\n large: [16, 16],\n};\n\nconst aligns: Record<string, string> = {\n start: 'items-start text-start',\n end: 'items-end text-end',\n center: 'items-center text-center',\n baseline: 'items-baseline text-baseline',\n};\n\nconst Space = ({\n loading,\n loader,\n children,\n size = 'middle',\n orientation = 'horizontal',\n align = 'center',\n wrap = false,\n split,\n className,\n grid,\n hidden,\n style,\n ...props\n}: Props) => {\n const [x, y] =\n typeof size === 'string'\n ? sizes[size] || [0, 0]\n : Array.isArray(size)\n ? size\n : [size, size];\n const count = Children.count(children);\n\n if (loading) {\n return (\n <div\n className={cn(hidden && 'hidden', className)}\n style={style}\n {...props}\n >\n {loader || <Skeleton />}\n </div>\n );\n }\n\n return (\n <div\n className={cn(\n 'flex flex-nowrap',\n orientation === 'horizontal' ? 'flex-row' : 'flex-col',\n wrap && 'flex-wrap',\n aligns[align],\n grid && 'grid',\n hidden && 'hidden',\n className,\n )}\n style={{\n columnGap: x,\n rowGap: y,\n gridTemplateRows: grid?.rows\n ? `repeat(${grid.rows}, minmax(0, 1fr))`\n : undefined,\n gridTemplateColumns: grid?.cols\n ? `repeat(${grid.cols}, minmax(0, 1fr))`\n : undefined,\n ...style,\n }}\n {...props}\n >\n {Children.map(children, (child, i) => (\n <>\n {child}\n {split && i < count - 1 && split}\n </>\n ))}\n </div>\n );\n};\n\nexport default Space;\n"],"mappings":";;;;;;AAoBA,MAAM,QAAkC;CACtC,OAAO,CAAC,GAAG,EAAE;CACb,QAAQ,CAAC,GAAG,EAAE;CACd,OAAO,CAAC,IAAI,GAAG;CAChB;AAED,MAAM,SAAiC;CACrC,OAAO;CACP,KAAK;CACL,QAAQ;CACR,UAAU;CACX;AAED,MAAM,SAAS,EACb,SACA,QACA,UACA,OAAO,UACP,cAAc,cACd,QAAQ,UACR,OAAO,OACP,OACA,WACA,MACA,QACA,OACA,GAAG,YACQ;CACX,MAAM,CAAC,GAAG,KACR,OAAO,SAAS,WACZ,MAAM,SAAS,CAAC,GAAG,EAAE,GACrB,MAAM,QAAQ,KAAK,GACjB,OACA,CAAC,MAAM,KAAK;CACpB,MAAM,QAAQ,SAAS,MAAM,SAAS;AAEtC,KAAI,QACF,QACE,oBAAC;EACC,WAAW,GAAG,UAAU,UAAU,UAAU;EACrC;EACP,GAAI;YAEH,UAAU,oBAAC,aAAW;GACnB;AAIV,QACE,oBAAC;EACC,WAAW,GACT,oBACA,gBAAgB,eAAe,aAAa,YAC5C,QAAQ,aACR,OAAO,QACP,QAAQ,QACR,UAAU,UACV,UACD;EACD,OAAO;GACL,WAAW;GACX,QAAQ;GACR,kBAAkB,MAAM,OACpB,UAAU,KAAK,KAAK,qBACpB;GACJ,qBAAqB,MAAM,OACvB,UAAU,KAAK,KAAK,qBACpB;GACJ,GAAG;GACJ;EACD,GAAI;YAEH,SAAS,IAAI,WAAW,OAAO,MAC9B,8CACG,OACA,SAAS,IAAI,QAAQ,KAAK,SAC1B,CACH;GACE"}
@@ -26,7 +26,7 @@ const Collapse = ({ expandIcon, accordion = false, items = [], className, classN
26
26
  ...props,
27
27
  className,
28
28
  ...accordionProps,
29
- children: items.map(({ label, children, disabled }, key) => /* @__PURE__ */ jsxs(AccordionItem, {
29
+ children: items.map(({ key, label, children, disabled }) => /* @__PURE__ */ jsxs(AccordionItem, {
30
30
  value: `${key}`,
31
31
  className: cn("border-none", classNames?.item),
32
32
  disabled,
@@ -1 +1 @@
1
- {"version":3,"file":"collapse.mjs","names":["accordion"],"sources":["../../../src/components/molecules/collapse.tsx"],"sourcesContent":["'use client';\n\nimport React from 'react';\n\nimport { accordion } from '@repo/ui/core';\nimport { cn } from '@repo/ui/utils';\n\nconst { Accordion, AccordionItem, AccordionTrigger, AccordionContent } =\n accordion;\n\ninterface Item {\n key: React.Key;\n label: React.ReactNode;\n disabled?: boolean;\n children: React.ReactNode;\n}\n\ninterface Props extends Omit<\n React.ComponentPropsWithoutRef<'div'>,\n 'onChange' | 'defaultValue' | 'value' | 'dir'\n> {\n items?: Item[];\n accordion?: boolean;\n expandIcon?: React.ReactNode;\n classNames?: {\n item?: string;\n header?: string;\n body?: string;\n };\n defaultActiveKey?: string[] | number[];\n activeKey?: string[] | number[];\n onChange?: (keys: string[]) => void;\n}\n\nconst Collapse = ({\n expandIcon,\n accordion = false,\n items = [],\n className,\n classNames,\n defaultActiveKey,\n activeKey: _activeKey,\n onChange: _onChange,\n ...props\n}: Props) => {\n const controlled = _activeKey !== undefined;\n\n const accordionProps = accordion\n ? {\n type: 'single' as const,\n onValueChange: (value: string) => {\n _onChange?.(value ? [value] : []);\n },\n ...(controlled\n ? { value: `${_activeKey?.[0] ?? ''}` }\n : { defaultValue: `${defaultActiveKey?.[0] ?? ''}` }),\n }\n : {\n type: 'multiple' as const,\n onValueChange: (values: string[]) => {\n _onChange?.(values);\n },\n ...(controlled\n ? { value: _activeKey?.map(key => `${key}`) }\n : { defaultValue: defaultActiveKey?.map(key => `${key}`) }),\n };\n\n return (\n <Accordion {...props} className={className} {...accordionProps}>\n {items.map(({ label, children, disabled }, key) => (\n <AccordionItem\n key={key}\n value={`${key}`}\n className={cn(\n 'border-none',\n classNames?.item,\n //\n )}\n disabled={disabled}\n >\n <AccordionTrigger\n className={cn(\n 'hover:no-underline',\n classNames?.header,\n disabled && 'cursor-no-drop opacity-50',\n //\n )}\n expandIcon={expandIcon}\n disabled={disabled}\n >\n {label}\n </AccordionTrigger>\n <AccordionContent\n className={cn(\n classNames?.body,\n //\n )}\n >\n {children}\n </AccordionContent>\n </AccordionItem>\n ))}\n </Accordion>\n );\n};\n\nexport default Collapse;\n"],"mappings":";;;;;;;;AAOA,MAAM,EAAE,WAAW,eAAe,kBAAkB,qBAClDA;AA0BF,MAAM,YAAY,EAChB,YACA,YAAY,OACZ,QAAQ,EAAE,EACV,WACA,YACA,kBACA,WAAW,YACX,UAAU,WACV,GAAG,YACQ;CACX,MAAM,aAAa,eAAe;CAElC,MAAM,iBAAiB,YACnB;EACE,MAAM;EACN,gBAAgB,UAAkB;AAChC,eAAY,QAAQ,CAAC,MAAM,GAAG,EAAE,CAAC;;EAEnC,GAAI,aACA,EAAE,OAAO,GAAG,aAAa,MAAM,MAAM,GACrC,EAAE,cAAc,GAAG,mBAAmB,MAAM,MAAM;EACvD,GACD;EACE,MAAM;EACN,gBAAgB,WAAqB;AACnC,eAAY,OAAO;;EAErB,GAAI,aACA,EAAE,OAAO,YAAY,KAAI,QAAO,GAAG,MAAM,EAAE,GAC3C,EAAE,cAAc,kBAAkB,KAAI,QAAO,GAAG,MAAM,EAAE;EAC7D;AAEL,QACE,oBAAC;EAAU,GAAI;EAAkB;EAAW,GAAI;YAC7C,MAAM,KAAK,EAAE,OAAO,UAAU,YAAY,QACzC,qBAAC;GAEC,OAAO,GAAG;GACV,WAAW,GACT,eACA,YAAY,KAEb;GACS;cAEV,oBAAC;IACC,WAAW,GACT,sBACA,YAAY,QACZ,YAAY,4BAEb;IACW;IACF;cAET;KACgB,EACnB,oBAAC;IACC,WAAW,GACT,YAAY,KAEb;IAEA;KACgB;KA5Bd,IA6BS,CAChB;GACQ"}
1
+ {"version":3,"file":"collapse.mjs","names":["accordion"],"sources":["../../../src/components/molecules/collapse.tsx"],"sourcesContent":["'use client';\n\nimport React from 'react';\n\nimport { accordion } from '@repo/ui/core';\nimport { cn } from '@repo/ui/utils';\n\nconst { Accordion, AccordionItem, AccordionTrigger, AccordionContent } =\n accordion;\n\ninterface Item {\n key: React.Key;\n label: React.ReactNode;\n disabled?: boolean;\n children: React.ReactNode;\n}\n\ninterface Props extends Omit<\n React.ComponentPropsWithoutRef<'div'>,\n 'onChange' | 'defaultValue' | 'value' | 'dir'\n> {\n items?: Item[];\n accordion?: boolean;\n expandIcon?: React.ReactNode;\n classNames?: {\n item?: string;\n header?: string;\n body?: string;\n };\n defaultActiveKey?: string[] | number[];\n activeKey?: string[] | number[];\n onChange?: (keys: string[]) => void;\n}\n\nconst Collapse = ({\n expandIcon,\n accordion = false,\n items = [],\n className,\n classNames,\n defaultActiveKey,\n activeKey: _activeKey,\n onChange: _onChange,\n ...props\n}: Props) => {\n const controlled = _activeKey !== undefined;\n\n const accordionProps = accordion\n ? {\n type: 'single' as const,\n onValueChange: (value: string) => {\n _onChange?.(value ? [value] : []);\n },\n ...(controlled\n ? { value: `${_activeKey?.[0] ?? ''}` }\n : { defaultValue: `${defaultActiveKey?.[0] ?? ''}` }),\n }\n : {\n type: 'multiple' as const,\n onValueChange: (values: string[]) => {\n _onChange?.(values);\n },\n ...(controlled\n ? { value: _activeKey?.map(key => `${key}`) }\n : { defaultValue: defaultActiveKey?.map(key => `${key}`) }),\n };\n\n return (\n <Accordion {...props} className={className} {...accordionProps}>\n {items.map(({ key, label, children, disabled }) => (\n <AccordionItem\n key={key}\n value={`${key}`}\n className={cn(\n 'border-none',\n classNames?.item,\n //\n )}\n disabled={disabled}\n >\n <AccordionTrigger\n className={cn(\n 'hover:no-underline',\n classNames?.header,\n disabled && 'cursor-no-drop opacity-50',\n //\n )}\n expandIcon={expandIcon}\n disabled={disabled}\n >\n {label}\n </AccordionTrigger>\n <AccordionContent\n className={cn(\n classNames?.body,\n //\n )}\n >\n {children}\n </AccordionContent>\n </AccordionItem>\n ))}\n </Accordion>\n );\n};\n\nexport default Collapse;\n"],"mappings":";;;;;;;;AAOA,MAAM,EAAE,WAAW,eAAe,kBAAkB,qBAClDA;AA0BF,MAAM,YAAY,EAChB,YACA,YAAY,OACZ,QAAQ,EAAE,EACV,WACA,YACA,kBACA,WAAW,YACX,UAAU,WACV,GAAG,YACQ;CACX,MAAM,aAAa,eAAe;CAElC,MAAM,iBAAiB,YACnB;EACE,MAAM;EACN,gBAAgB,UAAkB;AAChC,eAAY,QAAQ,CAAC,MAAM,GAAG,EAAE,CAAC;;EAEnC,GAAI,aACA,EAAE,OAAO,GAAG,aAAa,MAAM,MAAM,GACrC,EAAE,cAAc,GAAG,mBAAmB,MAAM,MAAM;EACvD,GACD;EACE,MAAM;EACN,gBAAgB,WAAqB;AACnC,eAAY,OAAO;;EAErB,GAAI,aACA,EAAE,OAAO,YAAY,KAAI,QAAO,GAAG,MAAM,EAAE,GAC3C,EAAE,cAAc,kBAAkB,KAAI,QAAO,GAAG,MAAM,EAAE;EAC7D;AAEL,QACE,oBAAC;EAAU,GAAI;EAAkB;EAAW,GAAI;YAC7C,MAAM,KAAK,EAAE,KAAK,OAAO,UAAU,eAClC,qBAAC;GAEC,OAAO,GAAG;GACV,WAAW,GACT,eACA,YAAY,KAEb;GACS;cAEV,oBAAC;IACC,WAAW,GACT,sBACA,YAAY,QACZ,YAAY,4BAEb;IACW;IACF;cAET;KACgB,EACnB,oBAAC;IACC,WAAW,GACT,YAAY,KAEb;IAEA;KACgB;KA5Bd,IA6BS,CAChB;GACQ"}
@@ -1 +1 @@
1
- {"version":3,"file":"item.d.mts","names":[],"sources":["../../../../src/components/molecules/marquees/item.tsx"],"mappings":";;;UAQiB,SAAA;EACf,GAAA,GAAM,KAAA,CAAM,GAAA;EACZ,KAAA;EACA,QAAA;EACA,KAAA;EACA,YAAA;EACA,QAAA,GAAW,KAAA,CAAM,SAAA;AAAA;AAAA,UAGF,KAAA,SACP,IAAA,CAAK,KAAA,CAAM,wBAAA,iBAAyC,SAAA;EAC5D,KAAA;AAAA;AAAA,cAGI,IAAA;EAAQ,KAAA,EAAA,MAAA;EAAA,KAAA;EAAA,QAAA;EAAA,KAAA,EAAA,MAAA;EAAA,YAAA;EAAA;AAAA,GAOX,KAAA,KAAK,kBAAA,CAAA,GAAA,CAAA,OAAA"}
1
+ {"version":3,"file":"item.d.mts","names":[],"sources":["../../../../src/components/molecules/marquees/item.tsx"],"mappings":";;;UAOiB,SAAA;EACf,GAAA,GAAM,KAAA,CAAM,GAAA;EACZ,KAAA;EACA,QAAA;EACA,KAAA;EACA,YAAA;EACA,QAAA,GAAW,KAAA,CAAM,SAAA;AAAA;AAAA,UAGF,KAAA,SACP,IAAA,CAAK,KAAA,CAAM,wBAAA,iBAAyC,SAAA;EAC5D,KAAA;AAAA;AAAA,cAYI,IAAA;EAAQ,KAAA,EAAA,MAAA;EAAA,KAAA;EAAA,QAAA;EAAA,KAAA,EAAA,MAAA;EAAA,YAAA;EAAA;AAAA,GAOX,KAAA,KAAK,kBAAA,CAAA,GAAA,CAAA,OAAA"}
@@ -2,18 +2,18 @@
2
2
 
3
3
  import { useEffect, useRef, useState } from "react";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";
5
- import { useEventListener } from "@jbpark/use-hooks";
6
5
  import { useGSAP } from "@gsap/react";
7
6
  import { gsap } from "gsap";
8
7
 
9
8
  //#region src/components/molecules/marquees/item.tsx
9
+ const INITIAL_AUTO_FILL_GUESS = 10;
10
10
  const Item = ({ width: _width, speed = 100, autoFill = false, pause: _pause = false, pauseOnHover = false, children }) => {
11
11
  const itemRefs = useRef([]);
12
12
  const containerRef = useRef(null);
13
13
  const tweenRef = useRef(null);
14
14
  const [width, setWidth] = useState(_width);
15
15
  const [pause, setPause] = useState(false);
16
- const [repeatCount, setRepeatCount] = useState(autoFill ? typeof autoFill === "boolean" ? 100 : autoFill : 0);
16
+ const [repeatCount, setRepeatCount] = useState(autoFill ? typeof autoFill === "boolean" ? INITIAL_AUTO_FILL_GUESS : autoFill : 0);
17
17
  const isPaused = _pause || pause;
18
18
  const hoverEvents = pauseOnHover ? {
19
19
  onMouseEnter: () => {
@@ -69,13 +69,8 @@ const Item = ({ width: _width, speed = 100, autoFill = false, pause: _pause = fa
69
69
  else tweenRef.current.play();
70
70
  }, [isPaused]);
71
71
  useEffect(() => {
72
- setRepeatCount(autoFill ? typeof autoFill === "boolean" ? 100 : autoFill : 0);
72
+ setRepeatCount(autoFill ? typeof autoFill === "boolean" ? INITIAL_AUTO_FILL_GUESS : autoFill : 0);
73
73
  }, [autoFill]);
74
- useEventListener("resize", () => {
75
- if (!tweenRef.current || !containerRef.current) return;
76
- if (isPaused) tweenRef.current.pause();
77
- else tweenRef.current.play();
78
- });
79
74
  return /* @__PURE__ */ jsx("div", {
80
75
  className: "flex overflow-hidden",
81
76
  ...hoverEvents,
@@ -1 +1 @@
1
- {"version":3,"file":"item.mjs","names":[],"sources":["../../../../src/components/molecules/marquees/item.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useRef, useState } from 'react';\n\nimport { useGSAP } from '@gsap/react';\nimport { useEventListener } from '@jbpark/use-hooks';\nimport { gsap } from 'gsap';\n\nexport interface ItemProps {\n key?: React.Key;\n speed?: number;\n autoFill?: boolean | number;\n pause?: boolean;\n pauseOnHover?: boolean;\n children?: React.ReactNode;\n}\n\nexport interface Props\n extends Omit<React.ComponentPropsWithoutRef<'div'>, 'key'>, ItemProps {\n width: string | number;\n}\n\nconst Item = ({\n width: _width,\n speed = 100,\n autoFill = false,\n pause: _pause = false,\n pauseOnHover = false,\n children,\n}: Props) => {\n const itemRefs = useRef<HTMLDivElement[]>([]);\n const containerRef = useRef<HTMLDivElement>(null);\n const tweenRef = useRef<gsap.core.Tween | null>(null);\n\n const [width, setWidth] = useState<string | number>(_width);\n const [pause, setPause] = useState(false);\n const [repeatCount, setRepeatCount] = useState(\n autoFill ? (typeof autoFill === 'boolean' ? 100 : autoFill) : 0,\n );\n\n const isPaused = _pause || pause;\n\n const hoverEvents = pauseOnHover\n ? {\n onMouseEnter: () => {\n setPause(true);\n },\n onMouseLeave: () => {\n setPause(false);\n },\n }\n : {};\n\n useEffect(() => {\n if (\n !autoFill ||\n !children ||\n !itemRefs.current[0] ||\n typeof width === 'string'\n ) {\n return;\n }\n\n const itemWidth = itemRefs.current[0].offsetWidth;\n\n if (itemWidth >= width) {\n setWidth(itemWidth);\n setRepeatCount(0);\n return;\n }\n\n let totalWidth = 0;\n let repeatCount = 0;\n\n while (totalWidth < width) {\n totalWidth += itemWidth;\n repeatCount += 1;\n }\n\n setWidth(totalWidth);\n setRepeatCount(repeatCount - 1);\n }, [autoFill, width, children]);\n\n useEffect(() => {\n setWidth(_width);\n }, [_width]);\n\n useGSAP(\n () => {\n if (!containerRef.current || typeof width === 'string') {\n return;\n }\n\n if (tweenRef.current) {\n tweenRef.current.kill();\n }\n\n tweenRef.current = gsap.fromTo(\n containerRef.current,\n { x: 0 },\n {\n x: -width,\n duration: width / speed,\n repeat: -1,\n ease: 'linear',\n },\n );\n\n return () => {\n tweenRef.current?.kill();\n };\n },\n {\n scope: containerRef,\n dependencies: [width, speed],\n },\n );\n\n useEffect(() => {\n if (!tweenRef.current) {\n return;\n }\n\n if (isPaused) {\n tweenRef.current.pause();\n } else {\n tweenRef.current.play();\n }\n }, [isPaused]);\n\n useEffect(() => {\n setRepeatCount(\n autoFill ? (typeof autoFill === 'boolean' ? 100 : autoFill) : 0,\n );\n }, [autoFill]);\n\n useEventListener('resize', () => {\n if (!tweenRef.current || !containerRef.current) {\n return;\n }\n\n if (isPaused) {\n tweenRef.current.pause();\n } else {\n tweenRef.current.play();\n }\n });\n\n return (\n <div\n className=\"flex overflow-hidden\"\n {...hoverEvents}\n //\n >\n <div ref={containerRef} className=\"flex flex-nowrap\">\n {[...Array(2)].map((_, index) => (\n <div\n key={index}\n style={{ minWidth: width }}\n className=\"flex flex-nowrap\"\n >\n <div\n ref={el => {\n if (el) {\n itemRefs.current[index] = el;\n }\n }}\n >\n {children}\n </div>\n {[\n ...Array(typeof autoFill === 'number' ? autoFill : repeatCount),\n ].map((_, i) => (\n <div key={i}>{children}</div>\n ))}\n </div>\n ))}\n </div>\n </div>\n );\n};\n\nexport default Item;\n"],"mappings":";;;;;;;;;AAsBA,MAAM,QAAQ,EACZ,OAAO,QACP,QAAQ,KACR,WAAW,OACX,OAAO,SAAS,OAChB,eAAe,OACf,eACW;CACX,MAAM,WAAW,OAAyB,EAAE,CAAC;CAC7C,MAAM,eAAe,OAAuB,KAAK;CACjD,MAAM,WAAW,OAA+B,KAAK;CAErD,MAAM,CAAC,OAAO,YAAY,SAA0B,OAAO;CAC3D,MAAM,CAAC,OAAO,YAAY,SAAS,MAAM;CACzC,MAAM,CAAC,aAAa,kBAAkB,SACpC,WAAY,OAAO,aAAa,YAAY,MAAM,WAAY,EAC/D;CAED,MAAM,WAAW,UAAU;CAE3B,MAAM,cAAc,eAChB;EACE,oBAAoB;AAClB,YAAS,KAAK;;EAEhB,oBAAoB;AAClB,YAAS,MAAM;;EAElB,GACD,EAAE;AAEN,iBAAgB;AACd,MACE,CAAC,YACD,CAAC,YACD,CAAC,SAAS,QAAQ,MAClB,OAAO,UAAU,SAEjB;EAGF,MAAM,YAAY,SAAS,QAAQ,GAAG;AAEtC,MAAI,aAAa,OAAO;AACtB,YAAS,UAAU;AACnB,kBAAe,EAAE;AACjB;;EAGF,IAAI,aAAa;EACjB,IAAI,cAAc;AAElB,SAAO,aAAa,OAAO;AACzB,iBAAc;AACd,kBAAe;;AAGjB,WAAS,WAAW;AACpB,iBAAe,cAAc,EAAE;IAC9B;EAAC;EAAU;EAAO;EAAS,CAAC;AAE/B,iBAAgB;AACd,WAAS,OAAO;IACf,CAAC,OAAO,CAAC;AAEZ,eACQ;AACJ,MAAI,CAAC,aAAa,WAAW,OAAO,UAAU,SAC5C;AAGF,MAAI,SAAS,QACX,UAAS,QAAQ,MAAM;AAGzB,WAAS,UAAU,KAAK,OACtB,aAAa,SACb,EAAE,GAAG,GAAG,EACR;GACE,GAAG,CAAC;GACJ,UAAU,QAAQ;GAClB,QAAQ;GACR,MAAM;GACP,CACF;AAED,eAAa;AACX,YAAS,SAAS,MAAM;;IAG5B;EACE,OAAO;EACP,cAAc,CAAC,OAAO,MAAM;EAC7B,CACF;AAED,iBAAgB;AACd,MAAI,CAAC,SAAS,QACZ;AAGF,MAAI,SACF,UAAS,QAAQ,OAAO;MAExB,UAAS,QAAQ,MAAM;IAExB,CAAC,SAAS,CAAC;AAEd,iBAAgB;AACd,iBACE,WAAY,OAAO,aAAa,YAAY,MAAM,WAAY,EAC/D;IACA,CAAC,SAAS,CAAC;AAEd,kBAAiB,gBAAgB;AAC/B,MAAI,CAAC,SAAS,WAAW,CAAC,aAAa,QACrC;AAGF,MAAI,SACF,UAAS,QAAQ,OAAO;MAExB,UAAS,QAAQ,MAAM;GAEzB;AAEF,QACE,oBAAC;EACC,WAAU;EACV,GAAI;YAGJ,oBAAC;GAAI,KAAK;GAAc,WAAU;aAC/B,CAAC,GAAG,MAAM,EAAE,CAAC,CAAC,KAAK,GAAG,UACrB,qBAAC;IAEC,OAAO,EAAE,UAAU,OAAO;IAC1B,WAAU;eAEV,oBAAC;KACC,MAAK,OAAM;AACT,UAAI,GACF,UAAS,QAAQ,SAAS;;KAI7B;MACG,EACL,CACC,GAAG,MAAM,OAAO,aAAa,WAAW,WAAW,YAAY,CAChE,CAAC,KAAK,GAAG,MACR,oBAAC,SAAa,YAAJ,EAAmB,CAC7B;MAjBG,MAkBD,CACN;IACE;GACF"}
1
+ {"version":3,"file":"item.mjs","names":[],"sources":["../../../../src/components/molecules/marquees/item.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useRef, useState } from 'react';\n\nimport { useGSAP } from '@gsap/react';\nimport { gsap } from 'gsap';\n\nexport interface ItemProps {\n key?: React.Key;\n speed?: number;\n autoFill?: boolean | number;\n pause?: boolean;\n pauseOnHover?: boolean;\n children?: React.ReactNode;\n}\n\nexport interface Props\n extends Omit<React.ComponentPropsWithoutRef<'div'>, 'key'>, ItemProps {\n width: string | number;\n}\n\n// `autoFill={true}` (the default on `Marquees`) needs *some* repeat count\n// before the item can be measured and the real count computed — this is\n// only ever visible for that one render, but rendering 100 copies (200\n// with the 2x loop duplication) up front was needlessly heavy for the\n// common case. A modest guess still avoids visible gaps for typical\n// item/container width ratios while cutting the worst-case default\n// render by ~90%.\nconst INITIAL_AUTO_FILL_GUESS = 10;\n\nconst Item = ({\n width: _width,\n speed = 100,\n autoFill = false,\n pause: _pause = false,\n pauseOnHover = false,\n children,\n}: Props) => {\n const itemRefs = useRef<HTMLDivElement[]>([]);\n const containerRef = useRef<HTMLDivElement>(null);\n const tweenRef = useRef<gsap.core.Tween | null>(null);\n\n const [width, setWidth] = useState<string | number>(_width);\n const [pause, setPause] = useState(false);\n const [repeatCount, setRepeatCount] = useState(\n autoFill\n ? typeof autoFill === 'boolean'\n ? INITIAL_AUTO_FILL_GUESS\n : autoFill\n : 0,\n );\n\n const isPaused = _pause || pause;\n\n const hoverEvents = pauseOnHover\n ? {\n onMouseEnter: () => {\n setPause(true);\n },\n onMouseLeave: () => {\n setPause(false);\n },\n }\n : {};\n\n useEffect(() => {\n if (\n !autoFill ||\n !children ||\n !itemRefs.current[0] ||\n typeof width === 'string'\n ) {\n return;\n }\n\n const itemWidth = itemRefs.current[0].offsetWidth;\n\n if (itemWidth >= width) {\n setWidth(itemWidth);\n setRepeatCount(0);\n return;\n }\n\n let totalWidth = 0;\n let repeatCount = 0;\n\n while (totalWidth < width) {\n totalWidth += itemWidth;\n repeatCount += 1;\n }\n\n setWidth(totalWidth);\n setRepeatCount(repeatCount - 1);\n }, [autoFill, width, children]);\n\n useEffect(() => {\n setWidth(_width);\n }, [_width]);\n\n useGSAP(\n () => {\n if (!containerRef.current || typeof width === 'string') {\n return;\n }\n\n if (tweenRef.current) {\n tweenRef.current.kill();\n }\n\n tweenRef.current = gsap.fromTo(\n containerRef.current,\n { x: 0 },\n {\n x: -width,\n duration: width / speed,\n repeat: -1,\n ease: 'linear',\n },\n );\n\n return () => {\n tweenRef.current?.kill();\n };\n },\n {\n scope: containerRef,\n dependencies: [width, speed],\n },\n );\n\n useEffect(() => {\n if (!tweenRef.current) {\n return;\n }\n\n if (isPaused) {\n tweenRef.current.pause();\n } else {\n tweenRef.current.play();\n }\n }, [isPaused]);\n\n useEffect(() => {\n setRepeatCount(\n autoFill\n ? typeof autoFill === 'boolean'\n ? INITIAL_AUTO_FILL_GUESS\n : autoFill\n : 0,\n );\n }, [autoFill]);\n\n return (\n <div\n className=\"flex overflow-hidden\"\n {...hoverEvents}\n //\n >\n <div ref={containerRef} className=\"flex flex-nowrap\">\n {[...Array(2)].map((_, index) => (\n <div\n key={index}\n style={{ minWidth: width }}\n className=\"flex flex-nowrap\"\n >\n <div\n ref={el => {\n if (el) {\n itemRefs.current[index] = el;\n }\n }}\n >\n {children}\n </div>\n {[\n ...Array(typeof autoFill === 'number' ? autoFill : repeatCount),\n ].map((_, i) => (\n <div key={i}>{children}</div>\n ))}\n </div>\n ))}\n </div>\n </div>\n );\n};\n\nexport default Item;\n"],"mappings":";;;;;;;;AA4BA,MAAM,0BAA0B;AAEhC,MAAM,QAAQ,EACZ,OAAO,QACP,QAAQ,KACR,WAAW,OACX,OAAO,SAAS,OAChB,eAAe,OACf,eACW;CACX,MAAM,WAAW,OAAyB,EAAE,CAAC;CAC7C,MAAM,eAAe,OAAuB,KAAK;CACjD,MAAM,WAAW,OAA+B,KAAK;CAErD,MAAM,CAAC,OAAO,YAAY,SAA0B,OAAO;CAC3D,MAAM,CAAC,OAAO,YAAY,SAAS,MAAM;CACzC,MAAM,CAAC,aAAa,kBAAkB,SACpC,WACI,OAAO,aAAa,YAClB,0BACA,WACF,EACL;CAED,MAAM,WAAW,UAAU;CAE3B,MAAM,cAAc,eAChB;EACE,oBAAoB;AAClB,YAAS,KAAK;;EAEhB,oBAAoB;AAClB,YAAS,MAAM;;EAElB,GACD,EAAE;AAEN,iBAAgB;AACd,MACE,CAAC,YACD,CAAC,YACD,CAAC,SAAS,QAAQ,MAClB,OAAO,UAAU,SAEjB;EAGF,MAAM,YAAY,SAAS,QAAQ,GAAG;AAEtC,MAAI,aAAa,OAAO;AACtB,YAAS,UAAU;AACnB,kBAAe,EAAE;AACjB;;EAGF,IAAI,aAAa;EACjB,IAAI,cAAc;AAElB,SAAO,aAAa,OAAO;AACzB,iBAAc;AACd,kBAAe;;AAGjB,WAAS,WAAW;AACpB,iBAAe,cAAc,EAAE;IAC9B;EAAC;EAAU;EAAO;EAAS,CAAC;AAE/B,iBAAgB;AACd,WAAS,OAAO;IACf,CAAC,OAAO,CAAC;AAEZ,eACQ;AACJ,MAAI,CAAC,aAAa,WAAW,OAAO,UAAU,SAC5C;AAGF,MAAI,SAAS,QACX,UAAS,QAAQ,MAAM;AAGzB,WAAS,UAAU,KAAK,OACtB,aAAa,SACb,EAAE,GAAG,GAAG,EACR;GACE,GAAG,CAAC;GACJ,UAAU,QAAQ;GAClB,QAAQ;GACR,MAAM;GACP,CACF;AAED,eAAa;AACX,YAAS,SAAS,MAAM;;IAG5B;EACE,OAAO;EACP,cAAc,CAAC,OAAO,MAAM;EAC7B,CACF;AAED,iBAAgB;AACd,MAAI,CAAC,SAAS,QACZ;AAGF,MAAI,SACF,UAAS,QAAQ,OAAO;MAExB,UAAS,QAAQ,MAAM;IAExB,CAAC,SAAS,CAAC;AAEd,iBAAgB;AACd,iBACE,WACI,OAAO,aAAa,YAClB,0BACA,WACF,EACL;IACA,CAAC,SAAS,CAAC;AAEd,QACE,oBAAC;EACC,WAAU;EACV,GAAI;YAGJ,oBAAC;GAAI,KAAK;GAAc,WAAU;aAC/B,CAAC,GAAG,MAAM,EAAE,CAAC,CAAC,KAAK,GAAG,UACrB,qBAAC;IAEC,OAAO,EAAE,UAAU,OAAO;IAC1B,WAAU;eAEV,oBAAC;KACC,MAAK,OAAM;AACT,UAAI,GACF,UAAS,QAAQ,SAAS;;KAI7B;MACG,EACL,CACC,GAAG,MAAM,OAAO,aAAa,WAAW,WAAW,YAAY,CAChE,CAAC,KAAK,GAAG,MACR,oBAAC,SAAa,YAAJ,EAAmB,CAC7B;MAjBG,MAkBD,CACN;IACE;GACF"}
@@ -22,6 +22,11 @@ declare const Reveals: ({
22
22
  cascade,
23
23
  duration,
24
24
  delay,
25
+ effect,
26
+ once,
27
+ viewport,
28
+ variants,
29
+ transition,
25
30
  ...props
26
31
  }: Props) => react_jsx_runtime0.JSX.Element;
27
32
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"reveals.d.mts","names":[],"sources":["../../../../src/components/molecules/reveals/reveals.tsx"],"mappings":";;;;UAMiB,KAAA,SACP,KAAA,CAAM,wBAAA,SAAiC,SAAA;EAC/C,OAAA;EACA,UAAA;IACE,IAAA;IACA,IAAA;EAAA;EAEF,IAAA;EACA,KAAA,GAAQ,SAAA;AAAA;AAAA,cAGG,QAAA;AAAA,cACA,KAAA;AAAA,cACA,OAAA;AAAA,cAEP,OAAA;EAAW,QAAA;EAAA,SAAA;EAAA,UAAA;EAAA,KAAA;EAAA,OAAA;EAAA,QAAA;EAAA,KAAA;EAAA,GAAA;AAAA,GASd,KAAA,KAAK,kBAAA,CAAA,GAAA,CAAA,OAAA"}
1
+ {"version":3,"file":"reveals.d.mts","names":[],"sources":["../../../../src/components/molecules/reveals/reveals.tsx"],"mappings":";;;;UAMiB,KAAA,SACP,KAAA,CAAM,wBAAA,SAAiC,SAAA;EAC/C,OAAA;EACA,UAAA;IACE,IAAA;IACA,IAAA;EAAA;EAEF,IAAA;EACA,KAAA,GAAQ,SAAA;AAAA;AAAA,cAGG,QAAA;AAAA,cACA,KAAA;AAAA,cACA,OAAA;AAAA,cAEP,OAAA;EAAW,QAAA;EAAA,SAAA;EAAA,UAAA;EAAA,KAAA;EAAA,OAAA;EAAA,QAAA;EAAA,KAAA;EAAA,MAAA;EAAA,IAAA;EAAA,QAAA;EAAA,QAAA;EAAA,UAAA;EAAA,GAAA;AAAA,GAcd,KAAA,KAAK,kBAAA,CAAA,GAAA,CAAA,OAAA"}
@@ -7,10 +7,15 @@ import { jsx } from "react/jsx-runtime";
7
7
  const DURATION = .6;
8
8
  const DELAY = 0;
9
9
  const CASCADE = .1;
10
- const Reveals = ({ children, className, classNames, items = [], cascade = CASCADE, duration = DURATION, delay = DELAY, ...props }) => {
10
+ const Reveals = ({ children, className, classNames, items = [], cascade = CASCADE, duration = DURATION, delay = DELAY, effect, once, viewport, variants, transition, ...props }) => {
11
11
  const renderItems = (items) => items.map((item, index) => /* @__PURE__ */ jsx(Item, {
12
+ effect,
12
13
  duration,
13
14
  delay: delay + cascade * index,
15
+ once,
16
+ viewport,
17
+ variants,
18
+ transition,
14
19
  ...item,
15
20
  className: cn(item.className, classNames?.item),
16
21
  children: item.children
@@ -1 +1 @@
1
- {"version":3,"file":"reveals.mjs","names":[],"sources":["../../../../src/components/molecules/reveals/reveals.tsx"],"sourcesContent":["import { Children } from 'react';\n\nimport { cn } from '@repo/ui/utils';\n\nimport Item, { type ItemProps } from './item';\n\nexport interface Props\n extends React.ComponentPropsWithoutRef<'div'>, ItemProps {\n cascade?: number;\n classNames?: {\n root?: string;\n item?: string;\n };\n once?: boolean;\n items?: ItemProps[];\n}\n\nexport const DURATION = 0.6;\nexport const DELAY = 0;\nexport const CASCADE = 0.1;\n\nconst Reveals = ({\n children,\n className,\n classNames,\n items = [],\n cascade = CASCADE,\n duration = DURATION,\n delay = DELAY,\n ...props\n}: Props) => {\n const renderItems = (items: ItemProps[]) =>\n items.map((item, index) => (\n <Item\n key={index}\n duration={duration}\n delay={delay + cascade * index}\n {...item}\n className={cn(\n item.className,\n classNames?.item,\n //\n )}\n >\n {item.children}\n </Item>\n ));\n\n return (\n <div\n className={cn(\n 'flex gap-5',\n className,\n classNames?.root,\n //\n )}\n {...props}\n >\n {renderItems(\n children\n ? Children.toArray(children).map(child => ({\n children: child,\n }))\n : items,\n )}\n </div>\n );\n};\n\nexport default Reveals;\n"],"mappings":";;;;;;AAiBA,MAAa,WAAW;AACxB,MAAa,QAAQ;AACrB,MAAa,UAAU;AAEvB,MAAM,WAAW,EACf,UACA,WACA,YACA,QAAQ,EAAE,EACV,UAAU,SACV,WAAW,UACX,QAAQ,OACR,GAAG,YACQ;CACX,MAAM,eAAe,UACnB,MAAM,KAAK,MAAM,UACf,oBAAC;EAEW;EACV,OAAO,QAAQ,UAAU;EACzB,GAAI;EACJ,WAAW,GACT,KAAK,WACL,YAAY,KAEb;YAEA,KAAK;IAVD,MAWA,CACP;AAEJ,QACE,oBAAC;EACC,WAAW,GACT,cACA,WACA,YAAY,KAEb;EACD,GAAI;YAEH,YACC,WACI,SAAS,QAAQ,SAAS,CAAC,KAAI,WAAU,EACvC,UAAU,OACX,EAAE,GACH,MACL;GACG"}
1
+ {"version":3,"file":"reveals.mjs","names":[],"sources":["../../../../src/components/molecules/reveals/reveals.tsx"],"sourcesContent":["import { Children } from 'react';\n\nimport { cn } from '@repo/ui/utils';\n\nimport Item, { type ItemProps } from './item';\n\nexport interface Props\n extends React.ComponentPropsWithoutRef<'div'>, ItemProps {\n cascade?: number;\n classNames?: {\n root?: string;\n item?: string;\n };\n once?: boolean;\n items?: ItemProps[];\n}\n\nexport const DURATION = 0.6;\nexport const DELAY = 0;\nexport const CASCADE = 0.1;\n\nconst Reveals = ({\n children,\n className,\n classNames,\n items = [],\n cascade = CASCADE,\n duration = DURATION,\n delay = DELAY,\n effect,\n once,\n viewport,\n variants,\n transition,\n ...props\n}: Props) => {\n const renderItems = (items: ItemProps[]) =>\n items.map((item, index) => (\n <Item\n key={index}\n effect={effect}\n duration={duration}\n delay={delay + cascade * index}\n once={once}\n viewport={viewport}\n variants={variants}\n transition={transition}\n {...item}\n className={cn(\n item.className,\n classNames?.item,\n //\n )}\n >\n {item.children}\n </Item>\n ));\n\n return (\n <div\n className={cn(\n 'flex gap-5',\n className,\n classNames?.root,\n //\n )}\n {...props}\n >\n {renderItems(\n children\n ? Children.toArray(children).map(child => ({\n children: child,\n }))\n : items,\n )}\n </div>\n );\n};\n\nexport default Reveals;\n"],"mappings":";;;;;;AAiBA,MAAa,WAAW;AACxB,MAAa,QAAQ;AACrB,MAAa,UAAU;AAEvB,MAAM,WAAW,EACf,UACA,WACA,YACA,QAAQ,EAAE,EACV,UAAU,SACV,WAAW,UACX,QAAQ,OACR,QACA,MACA,UACA,UACA,YACA,GAAG,YACQ;CACX,MAAM,eAAe,UACnB,MAAM,KAAK,MAAM,UACf,oBAAC;EAES;EACE;EACV,OAAO,QAAQ,UAAU;EACnB;EACI;EACA;EACE;EACZ,GAAI;EACJ,WAAW,GACT,KAAK,WACL,YAAY,KAEb;YAEA,KAAK;IAfD,MAgBA,CACP;AAEJ,QACE,oBAAC;EACC,WAAW,GACT,cACA,WACA,YAAY,KAEb;EACD,GAAI;YAEH,YACC,WACI,SAAS,QAAQ,SAAS,CAAC,KAAI,WAAU,EACvC,UAAU,OACX,EAAE,GACH,MACL;GACG"}
@@ -20,6 +20,14 @@ interface Props {
20
20
  item?: string;
21
21
  };
22
22
  onChange?: (files: UploadFile[]) => void;
23
+ /**
24
+ * Called once per file that didn't make it in — either bumped by
25
+ * `maxCount` or failed to read.
26
+ */
27
+ onReject?: (info: {
28
+ file: File;
29
+ reason: 'max-count' | 'read-error';
30
+ }) => void;
23
31
  }
24
32
  declare const Upload: ({
25
33
  defaultValue,
@@ -30,7 +38,8 @@ declare const Upload: ({
30
38
  disabled,
31
39
  className,
32
40
  classNames,
33
- onChange: _onChange
41
+ onChange: _onChange,
42
+ onReject
34
43
  }: Props) => react_jsx_runtime0.JSX.Element;
35
44
  //#endregion
36
45
  export { Upload, UploadFile };
@@ -1 +1 @@
1
- {"version":3,"file":"upload.d.mts","names":[],"sources":["../../../src/components/molecules/upload.tsx"],"mappings":";;;UAeiB,UAAA;EACf,GAAA;EACA,IAAA;EACA,GAAA;AAAA;AAAA,UAGe,KAAA;EACf,YAAA,GAAe,UAAA;EACf,KAAA,GAAQ,UAAA;EACR,QAAA;EACA,MAAA;EACA,QAAA;EACA,QAAA;EACA,SAAA;EACA,UAAA;IACE,QAAA;IACA,IAAA;IACA,IAAA;EAAA;EAEF,QAAA,IAAY,KAAA,EAAO,UAAA;AAAA;AAAA,cAOf,MAAA;EAAU,YAAA;EAAA,KAAA,EAAA,MAAA;EAAA,QAAA;EAAA,MAAA;EAAA,QAAA;EAAA,QAAA;EAAA,SAAA;EAAA,UAAA;EAAA,QAAA,EAAA;AAAA,GAUb,KAAA,KAAK,kBAAA,CAAA,GAAA,CAAA,OAAA"}
1
+ {"version":3,"file":"upload.d.mts","names":[],"sources":["../../../src/components/molecules/upload.tsx"],"mappings":";;;UAgBiB,UAAA;EACf,GAAA;EACA,IAAA;EACA,GAAA;AAAA;AAAA,UAGe,KAAA;EACf,YAAA,GAAe,UAAA;EACf,KAAA,GAAQ,UAAA;EACR,QAAA;EACA,MAAA;EACA,QAAA;EACA,QAAA;EACA,SAAA;EACA,UAAA;IACE,QAAA;IACA,IAAA;IACA,IAAA;EAAA;EAEF,QAAA,IAAY,KAAA,EAAO,UAAA;EAKW;;;;EAA9B,QAAA,IAAY,IAAA;IAAQ,IAAA,EAAM,IAAA;IAAM,MAAA;EAAA;AAAA;AAAA,cAO5B,MAAA;EAAU,YAAA;EAAA,KAAA,EAAA,MAAA;EAAA,QAAA;EAAA,MAAA;EAAA,QAAA;EAAA,QAAA;EAAA,SAAA;EAAA,UAAA;EAAA,QAAA,EAAA,SAAA;EAAA;AAAA,GAWb,KAAA,KAAK,kBAAA,CAAA,GAAA,CAAA,OAAA"}
@@ -6,10 +6,11 @@ import { Upload, X } from "lucide-react";
6
6
  import { useId } from "react";
7
7
  import { jsx, jsxs } from "react/jsx-runtime";
8
8
  import { useControllableState, useFileDrop, useFileToDataUrl } from "@jbpark/use-hooks";
9
+ import { v4 } from "uuid";
9
10
 
10
11
  //#region src/components/molecules/upload.tsx
11
12
  const isImage = (file) => /^data:image\//.test(file.url) || /\.(png|jpe?g|gif|webp|svg)$/i.test(file.name);
12
- const Upload$1 = ({ defaultValue = [], value: _value, multiple = true, accept = "image/*", maxCount, disabled, className, classNames, onChange: _onChange = () => {} }) => {
13
+ const Upload$1 = ({ defaultValue = [], value: _value, multiple = true, accept = "image/*", maxCount, disabled, className, classNames, onChange: _onChange = () => {}, onReject }) => {
13
14
  const inputId = useId();
14
15
  const readAsDataUrl = useFileToDataUrl();
15
16
  const [files, setFiles] = useControllableState({
@@ -20,11 +21,27 @@ const Upload$1 = ({ defaultValue = [], value: _value, multiple = true, accept =
20
21
  const addFiles = async (incoming) => {
21
22
  const remaining = typeof maxCount === "number" ? Math.max(0, maxCount - files.length) : incoming.length;
22
23
  const accepted = incoming.slice(0, remaining);
23
- const uploaded = await Promise.all(accepted.map(async (file) => ({
24
- uid: `${file.name}-${file.lastModified}-${file.size}`,
25
- name: file.name,
26
- url: await readAsDataUrl(file)
27
- })));
24
+ incoming.slice(remaining).forEach((file) => onReject?.({
25
+ file,
26
+ reason: "max-count"
27
+ }));
28
+ if (accepted.length === 0) return;
29
+ const uploaded = (await Promise.all(accepted.map(async (file) => {
30
+ try {
31
+ return {
32
+ uid: `${file.name}-${file.lastModified}-${file.size}-${v4()}`,
33
+ name: file.name,
34
+ url: await readAsDataUrl(file)
35
+ };
36
+ } catch {
37
+ onReject?.({
38
+ file,
39
+ reason: "read-error"
40
+ });
41
+ return null;
42
+ }
43
+ }))).filter((file) => file !== null);
44
+ if (uploaded.length === 0) return;
28
45
  setFiles([...files, ...uploaded]);
29
46
  };
30
47
  const removeFile = (uid) => {
@@ -1 +1 @@
1
- {"version":3,"file":"upload.mjs","names":["Upload","UploadIcon"],"sources":["../../../src/components/molecules/upload.tsx"],"sourcesContent":["'use client';\n\nimport { useId } from 'react';\n\nimport {\n useControllableState,\n useFileDrop,\n useFileToDataUrl,\n} from '@jbpark/use-hooks';\nimport { Upload as UploadIcon, X } from 'lucide-react';\n\nimport { cn } from '@repo/ui/utils';\n\nimport Button from '../atoms/button';\n\nexport interface UploadFile {\n uid: string;\n name: string;\n url: string;\n}\n\nexport interface Props {\n defaultValue?: UploadFile[];\n value?: UploadFile[];\n multiple?: boolean;\n accept?: string;\n maxCount?: number;\n disabled?: boolean;\n className?: string;\n classNames?: {\n dropzone?: string;\n list?: string;\n item?: string;\n };\n onChange?: (files: UploadFile[]) => void;\n}\n\nconst isImage = (file: UploadFile) =>\n /^data:image\\//.test(file.url) ||\n /\\.(png|jpe?g|gif|webp|svg)$/i.test(file.name);\n\nconst Upload = ({\n defaultValue = [],\n value: _value,\n multiple = true,\n accept = 'image/*',\n maxCount,\n disabled,\n className,\n classNames,\n onChange: _onChange = () => {},\n}: Props) => {\n const inputId = useId();\n const readAsDataUrl = useFileToDataUrl();\n const [files, setFiles] = useControllableState<UploadFile[]>({\n value: _value,\n defaultValue,\n onChange: _onChange,\n });\n\n const addFiles = async (incoming: File[]) => {\n const remaining =\n typeof maxCount === 'number'\n ? Math.max(0, maxCount - files.length)\n : incoming.length;\n\n const accepted = incoming.slice(0, remaining);\n\n const uploaded = await Promise.all(\n accepted.map(async file => ({\n uid: `${file.name}-${file.lastModified}-${file.size}`,\n name: file.name,\n url: await readAsDataUrl(file),\n })),\n );\n\n setFiles([...files, ...uploaded]);\n };\n\n const removeFile = (uid: string) => {\n setFiles(files.filter(file => file.uid !== uid));\n };\n\n const { dropRef, isDragging } = useFileDrop<HTMLLabelElement>({\n accept,\n multiple,\n disabled,\n onDrop: addFiles,\n });\n\n return (\n <div\n className={cn(\n 'flex flex-col gap-3',\n className,\n //\n )}\n >\n <label\n ref={dropRef}\n htmlFor={inputId}\n className={cn(\n 'flex cursor-pointer flex-col items-center justify-center gap-2',\n 'rounded-md border border-dashed p-6 text-center',\n 'text-muted-foreground text-sm',\n 'transition-colors',\n isDragging && 'border-primary bg-accent',\n disabled && 'pointer-events-none cursor-not-allowed opacity-50',\n classNames?.dropzone,\n //\n )}\n >\n <UploadIcon className=\"size-6\" />\n <span>Click or drag file{multiple ? 's' : ''} to upload</span>\n <input\n id={inputId}\n type=\"file\"\n accept={accept}\n multiple={multiple}\n disabled={disabled}\n className=\"hidden\"\n onChange={e => {\n if (e.target.files?.length) {\n addFiles(Array.from(e.target.files));\n }\n e.target.value = '';\n }}\n />\n </label>\n\n {files.length > 0 && (\n <ul\n className={cn(\n 'flex flex-col gap-2',\n classNames?.list,\n //\n )}\n >\n {files.map(file => (\n <li\n key={file.uid}\n className={cn(\n 'flex items-center gap-2 rounded-md border p-2',\n classNames?.item,\n //\n )}\n >\n {isImage(file) && (\n <img\n src={file.url}\n alt=\"\"\n className=\"size-10 shrink-0 rounded object-cover\"\n />\n )}\n <span className=\"flex-1 truncate text-sm\">{file.name}</span>\n <Button\n type=\"text\"\n shape=\"circle\"\n size=\"small\"\n icon={<X className=\"size-4\" />}\n onClick={() => removeFile(file.uid)}\n className=\"text-muted-foreground hover:text-foreground shrink-0\"\n aria-label={`Remove ${file.name}`}\n />\n </li>\n ))}\n </ul>\n )}\n </div>\n );\n};\n\nexport default Upload;\n"],"mappings":";;;;;;;;;;AAqCA,MAAM,WAAW,SACf,gBAAgB,KAAK,KAAK,IAAI,IAC9B,+BAA+B,KAAK,KAAK,KAAK;AAEhD,MAAMA,YAAU,EACd,eAAe,EAAE,EACjB,OAAO,QACP,WAAW,MACX,SAAS,WACT,UACA,UACA,WACA,YACA,UAAU,kBAAkB,SACjB;CACX,MAAM,UAAU,OAAO;CACvB,MAAM,gBAAgB,kBAAkB;CACxC,MAAM,CAAC,OAAO,YAAY,qBAAmC;EAC3D,OAAO;EACP;EACA,UAAU;EACX,CAAC;CAEF,MAAM,WAAW,OAAO,aAAqB;EAC3C,MAAM,YACJ,OAAO,aAAa,WAChB,KAAK,IAAI,GAAG,WAAW,MAAM,OAAO,GACpC,SAAS;EAEf,MAAM,WAAW,SAAS,MAAM,GAAG,UAAU;EAE7C,MAAM,WAAW,MAAM,QAAQ,IAC7B,SAAS,IAAI,OAAM,UAAS;GAC1B,KAAK,GAAG,KAAK,KAAK,GAAG,KAAK,aAAa,GAAG,KAAK;GAC/C,MAAM,KAAK;GACX,KAAK,MAAM,cAAc,KAAK;GAC/B,EAAE,CACJ;AAED,WAAS,CAAC,GAAG,OAAO,GAAG,SAAS,CAAC;;CAGnC,MAAM,cAAc,QAAgB;AAClC,WAAS,MAAM,QAAO,SAAQ,KAAK,QAAQ,IAAI,CAAC;;CAGlD,MAAM,EAAE,SAAS,eAAe,YAA8B;EAC5D;EACA;EACA;EACA,QAAQ;EACT,CAAC;AAEF,QACE,qBAAC;EACC,WAAW,GACT,uBACA,UAED;aAED,qBAAC;GACC,KAAK;GACL,SAAS;GACT,WAAW,GACT,kEACA,mDACA,iCACA,qBACA,cAAc,4BACd,YAAY,qDACZ,YAAY,SAEb;;IAED,oBAACC,UAAW,WAAU,WAAW;IACjC,qBAAC;KAAK;KAAmB,WAAW,MAAM;KAAG;QAAiB;IAC9D,oBAAC;KACC,IAAI;KACJ,MAAK;KACG;KACE;KACA;KACV,WAAU;KACV,WAAU,MAAK;AACb,UAAI,EAAE,OAAO,OAAO,OAClB,UAAS,MAAM,KAAK,EAAE,OAAO,MAAM,CAAC;AAEtC,QAAE,OAAO,QAAQ;;MAEnB;;IACI,EAEP,MAAM,SAAS,KACd,oBAAC;GACC,WAAW,GACT,uBACA,YAAY,KAEb;aAEA,MAAM,KAAI,SACT,qBAAC;IAEC,WAAW,GACT,iDACA,YAAY,KAEb;;KAEA,QAAQ,KAAK,IACZ,oBAAC;MACC,KAAK,KAAK;MACV,KAAI;MACJ,WAAU;OACV;KAEJ,oBAAC;MAAK,WAAU;gBAA2B,KAAK;OAAY;KAC5D,oBAAC;MACC,MAAK;MACL,OAAM;MACN,MAAK;MACL,MAAM,oBAAC,KAAE,WAAU,WAAW;MAC9B,eAAe,WAAW,KAAK,IAAI;MACnC,WAAU;MACV,cAAY,UAAU,KAAK;OAC3B;;MAvBG,KAAK,IAwBP,CACL;IACC;GAEH"}
1
+ {"version":3,"file":"upload.mjs","names":["Upload","uuid","UploadIcon"],"sources":["../../../src/components/molecules/upload.tsx"],"sourcesContent":["'use client';\n\nimport { useId } from 'react';\n\nimport {\n useControllableState,\n useFileDrop,\n useFileToDataUrl,\n} from '@jbpark/use-hooks';\nimport { Upload as UploadIcon, X } from 'lucide-react';\nimport { v4 as uuid } from 'uuid';\n\nimport { cn } from '@repo/ui/utils';\n\nimport Button from '../atoms/button';\n\nexport interface UploadFile {\n uid: string;\n name: string;\n url: string;\n}\n\nexport interface Props {\n defaultValue?: UploadFile[];\n value?: UploadFile[];\n multiple?: boolean;\n accept?: string;\n maxCount?: number;\n disabled?: boolean;\n className?: string;\n classNames?: {\n dropzone?: string;\n list?: string;\n item?: string;\n };\n onChange?: (files: UploadFile[]) => void;\n /**\n * Called once per file that didn't make it in — either bumped by\n * `maxCount` or failed to read.\n */\n onReject?: (info: { file: File; reason: 'max-count' | 'read-error' }) => void;\n}\n\nconst isImage = (file: UploadFile) =>\n /^data:image\\//.test(file.url) ||\n /\\.(png|jpe?g|gif|webp|svg)$/i.test(file.name);\n\nconst Upload = ({\n defaultValue = [],\n value: _value,\n multiple = true,\n accept = 'image/*',\n maxCount,\n disabled,\n className,\n classNames,\n onChange: _onChange = () => {},\n onReject,\n}: Props) => {\n const inputId = useId();\n const readAsDataUrl = useFileToDataUrl();\n const [files, setFiles] = useControllableState<UploadFile[]>({\n value: _value,\n defaultValue,\n onChange: _onChange,\n });\n\n const addFiles = async (incoming: File[]) => {\n const remaining =\n typeof maxCount === 'number'\n ? Math.max(0, maxCount - files.length)\n : incoming.length;\n\n const accepted = incoming.slice(0, remaining);\n const rejected = incoming.slice(remaining);\n\n rejected.forEach(file => onReject?.({ file, reason: 'max-count' }));\n\n if (accepted.length === 0) {\n return;\n }\n\n const uploaded = (\n await Promise.all(\n accepted.map(async file => {\n try {\n return {\n // `uuid()` guarantees uniqueness even for the same file\n // added twice (identical name/lastModified/size), which\n // name+lastModified+size alone can't — a collision there\n // corrupted React's item keys and made removeFile's\n // uid-based filter delete both copies at once.\n uid: `${file.name}-${file.lastModified}-${file.size}-${uuid()}`,\n name: file.name,\n url: await readAsDataUrl(file),\n };\n } catch {\n onReject?.({ file, reason: 'read-error' });\n return null;\n }\n }),\n )\n ).filter((file): file is UploadFile => file !== null);\n\n if (uploaded.length === 0) {\n return;\n }\n\n setFiles([...files, ...uploaded]);\n };\n\n const removeFile = (uid: string) => {\n setFiles(files.filter(file => file.uid !== uid));\n };\n\n const { dropRef, isDragging } = useFileDrop<HTMLLabelElement>({\n accept,\n multiple,\n disabled,\n onDrop: addFiles,\n });\n\n return (\n <div\n className={cn(\n 'flex flex-col gap-3',\n className,\n //\n )}\n >\n <label\n ref={dropRef}\n htmlFor={inputId}\n className={cn(\n 'flex cursor-pointer flex-col items-center justify-center gap-2',\n 'rounded-md border border-dashed p-6 text-center',\n 'text-muted-foreground text-sm',\n 'transition-colors',\n isDragging && 'border-primary bg-accent',\n disabled && 'pointer-events-none cursor-not-allowed opacity-50',\n classNames?.dropzone,\n //\n )}\n >\n <UploadIcon className=\"size-6\" />\n <span>Click or drag file{multiple ? 's' : ''} to upload</span>\n <input\n id={inputId}\n type=\"file\"\n accept={accept}\n multiple={multiple}\n disabled={disabled}\n className=\"hidden\"\n onChange={e => {\n if (e.target.files?.length) {\n addFiles(Array.from(e.target.files));\n }\n e.target.value = '';\n }}\n />\n </label>\n\n {files.length > 0 && (\n <ul\n className={cn(\n 'flex flex-col gap-2',\n classNames?.list,\n //\n )}\n >\n {files.map(file => (\n <li\n key={file.uid}\n className={cn(\n 'flex items-center gap-2 rounded-md border p-2',\n classNames?.item,\n //\n )}\n >\n {isImage(file) && (\n <img\n src={file.url}\n alt=\"\"\n className=\"size-10 shrink-0 rounded object-cover\"\n />\n )}\n <span className=\"flex-1 truncate text-sm\">{file.name}</span>\n <Button\n type=\"text\"\n shape=\"circle\"\n size=\"small\"\n icon={<X className=\"size-4\" />}\n onClick={() => removeFile(file.uid)}\n className=\"text-muted-foreground hover:text-foreground shrink-0\"\n aria-label={`Remove ${file.name}`}\n />\n </li>\n ))}\n </ul>\n )}\n </div>\n );\n};\n\nexport default Upload;\n"],"mappings":";;;;;;;;;;;AA2CA,MAAM,WAAW,SACf,gBAAgB,KAAK,KAAK,IAAI,IAC9B,+BAA+B,KAAK,KAAK,KAAK;AAEhD,MAAMA,YAAU,EACd,eAAe,EAAE,EACjB,OAAO,QACP,WAAW,MACX,SAAS,WACT,UACA,UACA,WACA,YACA,UAAU,kBAAkB,IAC5B,eACW;CACX,MAAM,UAAU,OAAO;CACvB,MAAM,gBAAgB,kBAAkB;CACxC,MAAM,CAAC,OAAO,YAAY,qBAAmC;EAC3D,OAAO;EACP;EACA,UAAU;EACX,CAAC;CAEF,MAAM,WAAW,OAAO,aAAqB;EAC3C,MAAM,YACJ,OAAO,aAAa,WAChB,KAAK,IAAI,GAAG,WAAW,MAAM,OAAO,GACpC,SAAS;EAEf,MAAM,WAAW,SAAS,MAAM,GAAG,UAAU;AAG7C,EAFiB,SAAS,MAAM,UAAU,CAEjC,SAAQ,SAAQ,WAAW;GAAE;GAAM,QAAQ;GAAa,CAAC,CAAC;AAEnE,MAAI,SAAS,WAAW,EACtB;EAGF,MAAM,YACJ,MAAM,QAAQ,IACZ,SAAS,IAAI,OAAM,SAAQ;AACzB,OAAI;AACF,WAAO;KAML,KAAK,GAAG,KAAK,KAAK,GAAG,KAAK,aAAa,GAAG,KAAK,KAAK,GAAGC,IAAM;KAC7D,MAAM,KAAK;KACX,KAAK,MAAM,cAAc,KAAK;KAC/B;WACK;AACN,eAAW;KAAE;KAAM,QAAQ;KAAc,CAAC;AAC1C,WAAO;;IAET,CACH,EACD,QAAQ,SAA6B,SAAS,KAAK;AAErD,MAAI,SAAS,WAAW,EACtB;AAGF,WAAS,CAAC,GAAG,OAAO,GAAG,SAAS,CAAC;;CAGnC,MAAM,cAAc,QAAgB;AAClC,WAAS,MAAM,QAAO,SAAQ,KAAK,QAAQ,IAAI,CAAC;;CAGlD,MAAM,EAAE,SAAS,eAAe,YAA8B;EAC5D;EACA;EACA;EACA,QAAQ;EACT,CAAC;AAEF,QACE,qBAAC;EACC,WAAW,GACT,uBACA,UAED;aAED,qBAAC;GACC,KAAK;GACL,SAAS;GACT,WAAW,GACT,kEACA,mDACA,iCACA,qBACA,cAAc,4BACd,YAAY,qDACZ,YAAY,SAEb;;IAED,oBAACC,UAAW,WAAU,WAAW;IACjC,qBAAC;KAAK;KAAmB,WAAW,MAAM;KAAG;QAAiB;IAC9D,oBAAC;KACC,IAAI;KACJ,MAAK;KACG;KACE;KACA;KACV,WAAU;KACV,WAAU,MAAK;AACb,UAAI,EAAE,OAAO,OAAO,OAClB,UAAS,MAAM,KAAK,EAAE,OAAO,MAAM,CAAC;AAEtC,QAAE,OAAO,QAAQ;;MAEnB;;IACI,EAEP,MAAM,SAAS,KACd,oBAAC;GACC,WAAW,GACT,uBACA,YAAY,KAEb;aAEA,MAAM,KAAI,SACT,qBAAC;IAEC,WAAW,GACT,iDACA,YAAY,KAEb;;KAEA,QAAQ,KAAK,IACZ,oBAAC;MACC,KAAK,KAAK;MACV,KAAI;MACJ,WAAU;OACV;KAEJ,oBAAC;MAAK,WAAU;gBAA2B,KAAK;OAAY;KAC5D,oBAAC;MACC,MAAK;MACL,OAAM;MACN,MAAK;MACL,MAAM,oBAAC,KAAE,WAAU,WAAW;MAC9B,eAAe,WAAW,KAAK,IAAI;MACnC,WAAU;MACV,cAAY,UAAU,KAAK;OAC3B;;MAvBG,KAAK,IAwBP,CACL;IACC;GAEH"}
@@ -6,9 +6,9 @@ import Button from "../../Button.mjs";
6
6
  import { Check, CircleQuestionMark, Info, OctagonAlert, OctagonX } from "lucide-react";
7
7
  import React, { useEffect, useState } from "react";
8
8
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
9
+ import { v4 } from "uuid";
9
10
  import { createPortal } from "react-dom";
10
11
  import { createRoot } from "react-dom/client";
11
- import { v4 } from "uuid";
12
12
 
13
13
  //#region src/components/organisms/modal.tsx
14
14
  const { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } = dialog_exports;
@@ -6,9 +6,9 @@ import { Check, Info, OctagonAlert, OctagonX, X } from "lucide-react";
6
6
  import { useEffect, useState } from "react";
7
7
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
8
8
  import { useTimeout } from "@jbpark/use-hooks";
9
+ import { v4 } from "uuid";
9
10
  import { createPortal } from "react-dom";
10
11
  import { createRoot } from "react-dom/client";
11
- import { v4 } from "uuid";
12
12
 
13
13
  //#region src/components/organisms/toast.tsx
14
14
  const isBrowser = typeof window !== "undefined" && typeof document !== "undefined";
@@ -4,7 +4,7 @@ import { jsx } from "react/jsx-runtime";
4
4
  //#region src/components/templates/layout/header.tsx
5
5
  const Header = ({ children, className, ...props }) => {
6
6
  return /* @__PURE__ */ jsx("header", {
7
- className: cn("sticky top-0 z-50", "flex h-16 w-full items-center px-5", className),
7
+ className: cn("sticky top-0 z-50", "bg-background", "flex h-16 w-full items-center px-5", className),
8
8
  ...props,
9
9
  children
10
10
  });
@@ -1 +1 @@
1
- {"version":3,"file":"header.mjs","names":[],"sources":["../../../../src/components/templates/layout/header.tsx"],"sourcesContent":["import { cn } from '@repo/ui/utils';\n\nexport interface Props extends React.ComponentPropsWithoutRef<'header'> {}\n\nconst Header = ({ children, className, ...props }: Props) => {\n return (\n <header\n className={cn(\n 'sticky top-0 z-50',\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":";;;;AAIA,MAAM,UAAU,EAAE,UAAU,WAAW,GAAG,YAAmB;AAC3D,QACE,oBAAC;EACC,WAAW,GACT,qBACA,sCACA,UAED;EACD,GAAI;EAEH;GACM"}
1
+ {"version":3,"file":"header.mjs","names":[],"sources":["../../../../src/components/templates/layout/header.tsx"],"sourcesContent":["import { cn } from '@repo/ui/utils';\n\nexport interface Props extends React.ComponentPropsWithoutRef<'header'> {}\n\nconst Header = ({ children, className, ...props }: Props) => {\n return (\n <header\n className={cn(\n 'sticky top-0 z-50',\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":";;;;AAIA,MAAM,UAAU,EAAE,UAAU,WAAW,GAAG,YAAmB;AAC3D,QACE,oBAAC;EACC,WAAW,GACT,qBACA,iBACA,sCACA,UAED;EACD,GAAI;EAEH;GACM"}
@@ -1 +1 @@
1
- {"version":3,"file":"layout.d.mts","names":[],"sources":["../../../../src/components/templates/layout/layout.tsx"],"mappings":";;;UAMiB,KAAA,SAAc,KAAA,CAAM,wBAAA;AAAA,cAS/B,MAAA;EAAU,QAAA;EAAA,SAAA;EAAA,GAAA;AAAA,GAAmC,KAAA,KAAK,kBAAA,CAAA,GAAA,CAAA,OAAA"}
1
+ {"version":3,"file":"layout.d.mts","names":[],"sources":["../../../../src/components/templates/layout/layout.tsx"],"mappings":";;;UAciB,KAAA,SAAc,KAAA,CAAM,wBAAA;AAAA,cAgB/B,MAAA;EAAU,QAAA;EAAA,SAAA;EAAA,GAAA;AAAA,GAAmC,KAAA,KAAK,kBAAA,CAAA,GAAA,CAAA,OAAA"}
@@ -1,20 +1,36 @@
1
1
  import { cn } from "../../../utils.mjs";
2
+ import { SiderRegistryContext } from "./sider-context.mjs";
2
3
  import Sider from "./sider.mjs";
3
- import { Children, createContext, isValidElement, useContext } from "react";
4
+ import { Children, createContext, isValidElement, useContext, useMemo, useState } from "react";
4
5
  import { jsx } from "react/jsx-runtime";
5
6
 
6
7
  //#region src/components/templates/layout/layout.tsx
7
8
  const LayoutNestedContext = createContext(false);
8
- const hasSiderChild = (children) => Children.toArray(children).some((child) => isValidElement(child) && child.type === Sider);
9
+ const hasDirectSiderChild = (children) => Children.toArray(children).some((child) => isValidElement(child) && child.type === Sider);
9
10
  const Layout = ({ children, className, ...props }) => {
10
11
  const isNested = useContext(LayoutNestedContext);
11
- const isRow = hasSiderChild(children);
12
+ const [siderIds] = useState(() => /* @__PURE__ */ new Set());
13
+ const [siderCount, setSiderCount] = useState(() => hasDirectSiderChild(children) ? 1 : 0);
14
+ const registry = useMemo(() => ({
15
+ registerSider: (id) => {
16
+ siderIds.add(id);
17
+ setSiderCount(siderIds.size);
18
+ },
19
+ unregisterSider: (id) => {
20
+ siderIds.delete(id);
21
+ setSiderCount(siderIds.size);
22
+ }
23
+ }), [siderIds]);
24
+ const isRow = siderCount > 0;
12
25
  return /* @__PURE__ */ jsx(LayoutNestedContext.Provider, {
13
26
  value: true,
14
- children: /* @__PURE__ */ jsx("div", {
15
- className: cn("flex w-full flex-1", isNested ? "min-h-0" : "min-h-screen", isRow ? "flex-row" : "flex-col", className),
16
- ...props,
17
- children
27
+ children: /* @__PURE__ */ jsx(SiderRegistryContext.Provider, {
28
+ value: registry,
29
+ children: /* @__PURE__ */ jsx("div", {
30
+ className: cn("flex w-full flex-1", isNested ? "min-h-0" : "min-h-screen", isRow ? "flex-row" : "flex-col", className),
31
+ ...props,
32
+ children
33
+ })
18
34
  })
19
35
  });
20
36
  };
@@ -1 +1 @@
1
- {"version":3,"file":"layout.mjs","names":[],"sources":["../../../../src/components/templates/layout/layout.tsx"],"sourcesContent":["import { Children, createContext, isValidElement, useContext } from 'react';\n\nimport { cn } from '@repo/ui/utils';\n\nimport Sider from './sider';\n\nexport interface Props extends React.ComponentPropsWithoutRef<'div'> {}\n\nconst LayoutNestedContext = createContext(false);\n\nconst hasSiderChild = (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 isRow = hasSiderChild(children);\n\n return (\n <LayoutNestedContext.Provider value>\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 </LayoutNestedContext.Provider>\n );\n};\n\nexport default Layout;\n"],"mappings":";;;;;;AAQA,MAAM,sBAAsB,cAAc,MAAM;AAEhD,MAAM,iBAAiB,aACrB,SAAS,QAAQ,SAAS,CAAC,MACzB,UAAS,eAAe,MAAM,IAAI,MAAM,SAAS,MAClD;AAEH,MAAM,UAAU,EAAE,UAAU,WAAW,GAAG,YAAmB;CAC3D,MAAM,WAAW,WAAW,oBAAoB;CAChD,MAAM,QAAQ,cAAc,SAAS;AAErC,QACE,oBAAC,oBAAoB;EAAS;YAC5B,oBAAC;GACC,WAAW,GACT,sBAMA,WAAW,YAAY,gBACvB,QAAQ,aAAa,YACrB,UACD;GACD,GAAI;GAEH;IACG;GACuB"}
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.ComponentPropsWithoutRef<'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,MAAM;AAShD,MAAM,uBAAuB,aAC3B,SAAS,QAAQ,SAAS,CAAC,MACzB,UAAS,eAAe,MAAM,IAAI,MAAM,SAAS,MAClD;AAEH,MAAM,UAAU,EAAE,UAAU,WAAW,GAAG,YAAmB;CAC3D,MAAM,WAAW,WAAW,oBAAoB;CAChD,MAAM,CAAC,YAAY,+BAAe,IAAI,KAAa,CAAC;CACpD,MAAM,CAAC,YAAY,iBAAiB,eAClC,oBAAoB,SAAS,GAAG,IAAI,EACrC;CAED,MAAM,WAAW,eACR;EACL,gBAAgB,OAAe;AAC7B,YAAS,IAAI,GAAG;AAChB,iBAAc,SAAS,KAAK;;EAE9B,kBAAkB,OAAe;AAC/B,YAAS,OAAO,GAAG;AACnB,iBAAc,SAAS,KAAK;;EAE/B,GACD,CAAC,SAAS,CACX;CAED,MAAM,QAAQ,aAAa;AAE3B,QACE,oBAAC,oBAAoB;EAAS;YAC5B,oBAAC,qBAAqB;GAAS,OAAO;aACpC,oBAAC;IACC,WAAW,GACT,sBAMA,WAAW,YAAY,gBACvB,QAAQ,aAAa,YACrB,UACD;IACD,GAAI;IAEH;KACG;IACwB;GACH"}
@@ -0,0 +1,20 @@
1
+ import { createContext, useContext, useLayoutEffect, useRef } from "react";
2
+
3
+ //#region src/components/templates/layout/sider-context.ts
4
+ const SiderRegistryContext = createContext(null);
5
+ const useRegisterSider = () => {
6
+ const registry = useContext(SiderRegistryContext);
7
+ const idRef = useRef({});
8
+ useLayoutEffect(() => {
9
+ if (!registry) return;
10
+ const id = idRef.current;
11
+ registry.registerSider(id);
12
+ return () => {
13
+ registry.unregisterSider(id);
14
+ };
15
+ }, [registry]);
16
+ };
17
+
18
+ //#endregion
19
+ export { SiderRegistryContext, useRegisterSider };
20
+ //# sourceMappingURL=sider-context.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sider-context.mjs","names":[],"sources":["../../../../src/components/templates/layout/sider-context.ts"],"sourcesContent":["import { createContext, useContext, useLayoutEffect, useRef } from 'react';\n\n// `Layout` needs to know whether a `Sider` exists anywhere in its subtree\n// to decide flex-row vs flex-col — but `Children.toArray(children).some(...)`\n// only ever sees direct children, and doesn't flatten Fragments, so a\n// `Sider` wrapped in a Fragment (or a custom wrapper component) went\n// undetected. Registering presence via context instead survives any\n// amount of wrapping, since it's `Sider` itself reporting in, not `Layout`\n// inspecting its children's shape.\nexport interface SiderRegistry {\n registerSider: (id: object) => void;\n unregisterSider: (id: object) => void;\n}\n\nexport const SiderRegistryContext = createContext<SiderRegistry | null>(null);\n\nexport const useRegisterSider = () => {\n const registry = useContext(SiderRegistryContext);\n const idRef = useRef({});\n\n // `useLayoutEffect` (not `useEffect`) so the registration — and the\n // resulting flex-row switch on `Layout` — commits before the browser\n // paints, avoiding a flash of the flex-col fallback layout on mount.\n useLayoutEffect(() => {\n if (!registry) {\n return;\n }\n\n const id = idRef.current;\n registry.registerSider(id);\n\n return () => {\n registry.unregisterSider(id);\n };\n }, [registry]);\n};\n"],"mappings":";;;AAcA,MAAa,uBAAuB,cAAoC,KAAK;AAE7E,MAAa,yBAAyB;CACpC,MAAM,WAAW,WAAW,qBAAqB;CACjD,MAAM,QAAQ,OAAO,EAAE,CAAC;AAKxB,uBAAsB;AACpB,MAAI,CAAC,SACH;EAGF,MAAM,KAAK,MAAM;AACjB,WAAS,cAAc,GAAG;AAE1B,eAAa;AACX,YAAS,gBAAgB,GAAG;;IAE7B,CAAC,SAAS,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"sider.d.mts","names":[],"sources":["../../../../src/components/templates/layout/sider.tsx"],"mappings":";;;UAQiB,KAAA,SAAc,IAAA,CAC7B,KAAA,CAAM,wBAAA;EAGN,KAAA;EACA,cAAA;EACA,WAAA;EACA,gBAAA;EACA,SAAA;EACA,YAAA;EACA,OAAA,GAAU,KAAA,CAAM,SAAA;EAChB,UAAA;IACE,OAAA;EAAA;EAEF,UAAA,IAAc,SAAA;AAAA;AAAA,cAMV,KAAA;EAAS,QAAA;EAAA,SAAA;EAAA,UAAA;EAAA,KAAA;EAAA,KAAA;EAAA,cAAA;EAAA,WAAA;EAAA,gBAAA;EAAA,SAAA,EAAA,UAAA;EAAA,YAAA;EAAA,OAAA;EAAA,UAAA,EAAA,WAAA;EAAA,GAAA;AAAA,GAcZ,KAAA,KAAK,kBAAA,CAAA,GAAA,CAAA,OAAA"}
1
+ {"version":3,"file":"sider.d.mts","names":[],"sources":["../../../../src/components/templates/layout/sider.tsx"],"mappings":";;;UAUiB,KAAA,SAAc,IAAA,CAC7B,KAAA,CAAM,wBAAA;EAGN,KAAA;EACA,cAAA;EACA,WAAA;EACA,gBAAA;EACA,SAAA;EACA,YAAA;EACA,OAAA,GAAU,KAAA,CAAM,SAAA;EAChB,UAAA;IACE,OAAA;EAAA;EAEF,UAAA,IAAc,SAAA;AAAA;AAAA,cAMV,KAAA;EAAS,QAAA;EAAA,SAAA;EAAA,UAAA;EAAA,KAAA;EAAA,KAAA;EAAA,cAAA;EAAA,WAAA;EAAA,gBAAA;EAAA,SAAA,EAAA,UAAA;EAAA,YAAA;EAAA,OAAA;EAAA,UAAA,EAAA,WAAA;EAAA,GAAA;AAAA,GAcZ,KAAA,KAAK,kBAAA,CAAA,GAAA,CAAA,OAAA"}
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { cn } from "../../../utils.mjs";
4
+ import { useRegisterSider } from "./sider-context.mjs";
4
5
  import { PanelLeftClose, PanelLeftOpen } from "lucide-react";
5
6
  import { useState } from "react";
6
7
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -8,6 +9,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
8
9
  //#region src/components/templates/layout/sider.tsx
9
10
  const toCssSize = (value) => typeof value === "number" ? `${value}px` : value;
10
11
  const Sider = ({ children, className, classNames, style, width = 200, collapsedWidth = 80, collapsible = false, defaultCollapsed = false, collapsed: _collapsed, reverseArrow = false, trigger, onCollapse: _onCollapse = () => {}, ...props }) => {
12
+ useRegisterSider();
11
13
  const [uncontrolledCollapsed, setUncontrolledCollapsed] = useState(defaultCollapsed);
12
14
  const controlled = _collapsed !== void 0;
13
15
  const collapsed = controlled ? _collapsed : uncontrolledCollapsed;
@@ -18,7 +20,7 @@ const Sider = ({ children, className, classNames, style, width = 200, collapsedW
18
20
  };
19
21
  const TriggerIcon = collapsed !== reverseArrow ? PanelLeftOpen : PanelLeftClose;
20
22
  return /* @__PURE__ */ jsxs("aside", {
21
- className: cn("z-100 flex h-full shrink-0 flex-col overflow-hidden", "transition-[width] duration-200", className),
23
+ className: cn("z-10 flex h-full shrink-0 flex-col overflow-hidden", "transition-[width] duration-200", className),
22
24
  style: {
23
25
  width: toCssSize(collapsed ? collapsedWidth : width),
24
26
  ...style
@@ -32,7 +34,7 @@ const Sider = ({ children, className, classNames, style, width = 200, collapsedW
32
34
  "aria-label": collapsed ? "펼치기" : "접기",
33
35
  onClick: onCollapse,
34
36
  className: cn("flex w-full shrink-0 items-center justify-center py-2", "cursor-pointer transition-colors hover:bg-black/5", classNames?.trigger),
35
- children: trigger ?? /* @__PURE__ */ jsx(TriggerIcon, { className: "size-4" })
37
+ children: trigger === void 0 ? /* @__PURE__ */ jsx(TriggerIcon, { className: "size-4" }) : trigger
36
38
  })]
37
39
  });
38
40
  };
@@ -1 +1 @@
1
- {"version":3,"file":"sider.mjs","names":[],"sources":["../../../../src/components/templates/layout/sider.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\n\nimport { PanelLeftClose, PanelLeftOpen } from 'lucide-react';\n\nimport { cn } from '@repo/ui/utils';\n\nexport interface Props extends Omit<\n React.ComponentPropsWithoutRef<'aside'>,\n 'onCollapse'\n> {\n width?: number | string;\n collapsedWidth?: number | string;\n collapsible?: boolean;\n defaultCollapsed?: boolean;\n collapsed?: boolean;\n reverseArrow?: boolean;\n trigger?: React.ReactNode;\n classNames?: {\n trigger?: string;\n };\n onCollapse?: (collapsed: 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 reverseArrow = false,\n trigger,\n onCollapse: _onCollapse = () => {},\n ...props\n}: Props) => {\n const [uncontrolledCollapsed, setUncontrolledCollapsed] =\n useState(defaultCollapsed);\n\n const controlled = _collapsed !== undefined;\n const collapsed = controlled ? _collapsed : uncontrolledCollapsed;\n\n const onCollapse = () => {\n const next = !collapsed;\n\n if (!controlled) {\n setUncontrolledCollapsed(next);\n }\n _onCollapse(next);\n };\n\n const TriggerIcon =\n collapsed !== reverseArrow ? PanelLeftOpen : PanelLeftClose;\n\n return (\n <aside\n className={cn(\n 'z-100 flex h-full shrink-0 flex-col overflow-hidden',\n 'transition-[width] duration-200',\n className,\n //\n )}\n style={{\n width: toCssSize(collapsed ? collapsedWidth : width),\n ...style,\n }}\n {...props}\n >\n <div className=\"min-h-0 flex-1 overflow-auto\">{children}</div>\n {collapsible && (\n <button\n type=\"button\"\n aria-label={collapsed ? '펼치기' : '접기'}\n onClick={onCollapse}\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 ?? <TriggerIcon className=\"size-4\" />}\n </button>\n )}\n </aside>\n );\n};\n\nexport default Sider;\n"],"mappings":";;;;;;;;AAyBA,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,eAAe,OACf,SACA,YAAY,oBAAoB,IAChC,GAAG,YACQ;CACX,MAAM,CAAC,uBAAuB,4BAC5B,SAAS,iBAAiB;CAE5B,MAAM,aAAa,eAAe;CAClC,MAAM,YAAY,aAAa,aAAa;CAE5C,MAAM,mBAAmB;EACvB,MAAM,OAAO,CAAC;AAEd,MAAI,CAAC,WACH,0BAAyB,KAAK;AAEhC,cAAY,KAAK;;CAGnB,MAAM,cACJ,cAAc,eAAe,gBAAgB;AAE/C,QACE,qBAAC;EACC,WAAW,GACT,uDACA,mCACA,UAED;EACD,OAAO;GACL,OAAO,UAAU,YAAY,iBAAiB,MAAM;GACpD,GAAG;GACJ;EACD,GAAI;aAEJ,oBAAC;GAAI,WAAU;GAAgC;IAAe,EAC7D,eACC,oBAAC;GACC,MAAK;GACL,cAAY,YAAY,QAAQ;GAChC,SAAS;GACT,WAAW,GACT,yDACA,qDACA,YAAY,QAEb;aAEA,WAAW,oBAAC,eAAY,WAAU,WAAW;IACvC;GAEL"}
1
+ {"version":3,"file":"sider.mjs","names":[],"sources":["../../../../src/components/templates/layout/sider.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\n\nimport { PanelLeftClose, PanelLeftOpen } from 'lucide-react';\n\nimport { cn } from '@repo/ui/utils';\n\nimport { useRegisterSider } from './sider-context';\n\nexport interface Props extends Omit<\n React.ComponentPropsWithoutRef<'aside'>,\n 'onCollapse'\n> {\n width?: number | string;\n collapsedWidth?: number | string;\n collapsible?: boolean;\n defaultCollapsed?: boolean;\n collapsed?: boolean;\n reverseArrow?: boolean;\n trigger?: React.ReactNode;\n classNames?: {\n trigger?: string;\n };\n onCollapse?: (collapsed: 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 reverseArrow = false,\n trigger,\n onCollapse: _onCollapse = () => {},\n ...props\n}: Props) => {\n useRegisterSider();\n\n const [uncontrolledCollapsed, setUncontrolledCollapsed] =\n useState(defaultCollapsed);\n\n const controlled = _collapsed !== undefined;\n const collapsed = controlled ? _collapsed : uncontrolledCollapsed;\n\n const onCollapse = () => {\n const next = !collapsed;\n\n if (!controlled) {\n setUncontrolledCollapsed(next);\n }\n _onCollapse(next);\n };\n\n const TriggerIcon =\n collapsed !== reverseArrow ? PanelLeftOpen : PanelLeftClose;\n\n return (\n <aside\n className={cn(\n // Below Header's z-50 (and every other floating/overlay primitive\n // in this library, which all use z-50) — Sider is a static layout\n // column, not floating chrome, and previously outranked Header\n // with an arbitrary z-100, covering it once Header stuck to the\n // viewport top on scroll.\n 'z-10 flex h-full shrink-0 flex-col overflow-hidden',\n 'transition-[width] duration-200',\n className,\n //\n )}\n style={{\n width: toCssSize(collapsed ? collapsedWidth : width),\n ...style,\n }}\n {...props}\n >\n <div className=\"min-h-0 flex-1 overflow-auto\">{children}</div>\n {collapsible && (\n <button\n type=\"button\"\n aria-label={collapsed ? '펼치기' : '접기'}\n onClick={onCollapse}\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":";;;;;;;;;AA2BA,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,eAAe,OACf,SACA,YAAY,oBAAoB,IAChC,GAAG,YACQ;AACX,mBAAkB;CAElB,MAAM,CAAC,uBAAuB,4BAC5B,SAAS,iBAAiB;CAE5B,MAAM,aAAa,eAAe;CAClC,MAAM,YAAY,aAAa,aAAa;CAE5C,MAAM,mBAAmB;EACvB,MAAM,OAAO,CAAC;AAEd,MAAI,CAAC,WACH,0BAAyB,KAAK;AAEhC,cAAY,KAAK;;CAGnB,MAAM,cACJ,cAAc,eAAe,gBAAgB;AAE/C,QACE,qBAAC;EACC,WAAW,GAMT,sDACA,mCACA,UAED;EACD,OAAO;GACL,OAAO,UAAU,YAAY,iBAAiB,MAAM;GACpD,GAAG;GACJ;EACD,GAAI;aAEJ,oBAAC;GAAI,WAAU;GAAgC;IAAe,EAC7D,eACC,oBAAC;GACC,MAAK;GACL,cAAY,YAAY,QAAQ;GAChC,SAAS;GACT,WAAW,GACT,yDACA,qDACA,YAAY,QAEb;aAEA,YAAY,SAAY,oBAAC,eAAY,WAAU,WAAW,GAAG;IACvD;GAEL"}
@@ -39,6 +39,7 @@ const buildCssVars = (token) => {
39
39
  };
40
40
  const Config = ({ theme = {}, className, children }) => {
41
41
  const parent = useConfig();
42
+ const themeKey = JSON.stringify(theme);
42
43
  const mergedTheme = useMemo(() => ({
43
44
  ...parent.theme,
44
45
  ...theme,
@@ -46,7 +47,7 @@ const Config = ({ theme = {}, className, children }) => {
46
47
  ...parent.theme.token,
47
48
  ...theme.token
48
49
  }
49
- }), [parent.theme, theme]);
50
+ }), [JSON.stringify(parent.theme), themeKey]);
50
51
  const contextValue = useMemo(() => ({ theme: mergedTheme }), [mergedTheme]);
51
52
  const cssVars = useMemo(() => buildCssVars(mergedTheme.token), [mergedTheme.token]);
52
53
  const hasCssVars = Object.keys(cssVars).length > 0;
@@ -56,7 +57,10 @@ const Config = ({ theme = {}, className, children }) => {
56
57
  value: contextValue,
57
58
  children: needsWrapper ? /* @__PURE__ */ jsx("div", {
58
59
  className: cn(mergedTheme.dark && "dark", className),
59
- style: hasCssVars ? cssVars : void 0,
60
+ style: {
61
+ ...hasCssVars ? cssVars : void 0,
62
+ display: "contents"
63
+ },
60
64
  children
61
65
  }) : children
62
66
  });
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/providers/Config/index.tsx"],"sourcesContent":["'use client';\n\nimport { useMemo } from 'react';\n\nimport { cn } from '@repo/ui/utils';\n\nimport { Context, useConfig } from './context';\nimport type { ThemeConfig, ThemeToken } from './types';\n\nconst tokenToCssVar: Record<keyof ThemeToken, string> = {\n colorPrimary: '--primary',\n colorPrimaryForeground: '--primary-foreground',\n colorBackground: '--background',\n colorForeground: '--foreground',\n colorCard: '--card',\n colorCardForeground: '--card-foreground',\n colorPopover: '--popover',\n colorPopoverForeground: '--popover-foreground',\n colorSecondary: '--secondary',\n colorSecondaryForeground: '--secondary-foreground',\n colorMuted: '--muted',\n colorMutedForeground: '--muted-foreground',\n colorAccent: '--accent',\n colorAccentForeground: '--accent-foreground',\n colorDestructive: '--destructive',\n colorBorder: '--border',\n colorInput: '--input',\n colorRing: '--ring',\n borderRadius: '--radius',\n};\n\nconst buildCssVars = (token?: ThemeToken): React.CSSProperties => {\n if (!token) {\n return {};\n }\n\n const vars: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(token)) {\n if (value === undefined) {\n continue;\n }\n const cssVar = tokenToCssVar[key as keyof ThemeToken];\n if (cssVar) {\n vars[cssVar] = value;\n }\n }\n\n return vars as React.CSSProperties;\n};\n\ninterface Props {\n theme?: ThemeConfig;\n className?: string;\n children: React.ReactNode;\n}\n\nconst Config = ({ theme = {}, className, children }: Props) => {\n const parent = useConfig();\n\n const mergedTheme = useMemo<ThemeConfig>(\n () => ({\n ...parent.theme,\n ...theme,\n token: {\n ...parent.theme.token,\n ...theme.token,\n },\n }),\n [parent.theme, theme],\n );\n\n const contextValue = useMemo(() => ({ theme: mergedTheme }), [mergedTheme]);\n\n const cssVars = useMemo(\n () => buildCssVars(mergedTheme.token),\n [mergedTheme.token],\n );\n\n const hasCssVars = Object.keys(cssVars).length > 0;\n const hasDarkMode = mergedTheme.dark !== undefined;\n const needsWrapper = hasCssVars || hasDarkMode || className;\n\n return (\n <Context.Provider value={contextValue}>\n {needsWrapper ? (\n <div\n className={cn(mergedTheme.dark && 'dark', className)}\n style={hasCssVars ? cssVars : undefined}\n >\n {children}\n </div>\n ) : (\n children\n )}\n </Context.Provider>\n );\n};\n\nexport default Config;\nexport { useConfig };\nexport type { Props, ThemeConfig, ThemeToken };\n"],"mappings":";;;;;;;;AASA,MAAM,gBAAkD;CACtD,cAAc;CACd,wBAAwB;CACxB,iBAAiB;CACjB,iBAAiB;CACjB,WAAW;CACX,qBAAqB;CACrB,cAAc;CACd,wBAAwB;CACxB,gBAAgB;CAChB,0BAA0B;CAC1B,YAAY;CACZ,sBAAsB;CACtB,aAAa;CACb,uBAAuB;CACvB,kBAAkB;CAClB,aAAa;CACb,YAAY;CACZ,WAAW;CACX,cAAc;CACf;AAED,MAAM,gBAAgB,UAA4C;AAChE,KAAI,CAAC,MACH,QAAO,EAAE;CAGX,MAAM,OAA+B,EAAE;AAEvC,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,EAAE;AAChD,MAAI,UAAU,OACZ;EAEF,MAAM,SAAS,cAAc;AAC7B,MAAI,OACF,MAAK,UAAU;;AAInB,QAAO;;AAST,MAAM,UAAU,EAAE,QAAQ,EAAE,EAAE,WAAW,eAAsB;CAC7D,MAAM,SAAS,WAAW;CAE1B,MAAM,cAAc,eACX;EACL,GAAG,OAAO;EACV,GAAG;EACH,OAAO;GACL,GAAG,OAAO,MAAM;GAChB,GAAG,MAAM;GACV;EACF,GACD,CAAC,OAAO,OAAO,MAAM,CACtB;CAED,MAAM,eAAe,eAAe,EAAE,OAAO,aAAa,GAAG,CAAC,YAAY,CAAC;CAE3E,MAAM,UAAU,cACR,aAAa,YAAY,MAAM,EACrC,CAAC,YAAY,MAAM,CACpB;CAED,MAAM,aAAa,OAAO,KAAK,QAAQ,CAAC,SAAS;CACjD,MAAM,cAAc,YAAY,SAAS;CACzC,MAAM,eAAe,cAAc,eAAe;AAElD,QACE,oBAAC,QAAQ;EAAS,OAAO;YACtB,eACC,oBAAC;GACC,WAAW,GAAG,YAAY,QAAQ,QAAQ,UAAU;GACpD,OAAO,aAAa,UAAU;GAE7B;IACG,GAEN;GAEe"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/providers/Config/index.tsx"],"sourcesContent":["'use client';\n\nimport { useMemo } from 'react';\n\nimport { cn } from '@repo/ui/utils';\n\nimport { Context, useConfig } from './context';\nimport type { ThemeConfig, ThemeToken } from './types';\n\nconst tokenToCssVar: Record<keyof ThemeToken, string> = {\n colorPrimary: '--primary',\n colorPrimaryForeground: '--primary-foreground',\n colorBackground: '--background',\n colorForeground: '--foreground',\n colorCard: '--card',\n colorCardForeground: '--card-foreground',\n colorPopover: '--popover',\n colorPopoverForeground: '--popover-foreground',\n colorSecondary: '--secondary',\n colorSecondaryForeground: '--secondary-foreground',\n colorMuted: '--muted',\n colorMutedForeground: '--muted-foreground',\n colorAccent: '--accent',\n colorAccentForeground: '--accent-foreground',\n colorDestructive: '--destructive',\n colorBorder: '--border',\n colorInput: '--input',\n colorRing: '--ring',\n borderRadius: '--radius',\n};\n\nconst buildCssVars = (token?: ThemeToken): React.CSSProperties => {\n if (!token) {\n return {};\n }\n\n const vars: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(token)) {\n if (value === undefined) {\n continue;\n }\n const cssVar = tokenToCssVar[key as keyof ThemeToken];\n if (cssVar) {\n vars[cssVar] = value;\n }\n }\n\n return vars as React.CSSProperties;\n};\n\ninterface Props {\n theme?: ThemeConfig;\n className?: string;\n children: React.ReactNode;\n}\n\nconst Config = ({ theme = {}, className, children }: Props) => {\n const parent = useConfig();\n\n // `theme` is typically a fresh inline object literal at the call site\n // (and `parent.theme` inherits the same problem from its own Config\n // ancestor), so depending on them directly meant mergedTheme/contextValue\n // got a new identity on every render regardless of whether the actual\n // values changed — every useConfig() consumer in the tree re-rendered on\n // every render of whatever's above this Config, which for the common\n // app-root usage is everything. Depend on a serialized key instead so\n // the memo only invalidates when the content actually changes; these\n // are small, string/boolean-only objects, so JSON.stringify is cheap.\n const themeKey = JSON.stringify(theme);\n const parentThemeKey = JSON.stringify(parent.theme);\n\n const mergedTheme = useMemo<ThemeConfig>(\n () => ({\n ...parent.theme,\n ...theme,\n token: {\n ...parent.theme.token,\n ...theme.token,\n },\n }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [parentThemeKey, themeKey],\n );\n\n const contextValue = useMemo(() => ({ theme: mergedTheme }), [mergedTheme]);\n\n const cssVars = useMemo(\n () => buildCssVars(mergedTheme.token),\n [mergedTheme.token],\n );\n\n const hasCssVars = Object.keys(cssVars).length > 0;\n const hasDarkMode = mergedTheme.dark !== undefined;\n const needsWrapper = hasCssVars || hasDarkMode || className;\n\n return (\n <Context.Provider value={contextValue}>\n {needsWrapper ? (\n <div\n className={cn(mergedTheme.dark && 'dark', className)}\n style={{\n ...(hasCssVars ? cssVars : undefined),\n // Keeps this element out of the layout box tree — so it\n // doesn't intercept flex/grid child relationships or break\n // height chains like min-h-screen — while custom properties\n // and the `dark` class selector still cascade to children\n // normally (inheritance and box generation are separate\n // mechanisms in CSS). Note this means className shouldn't be\n // used here for anything that needs a real box (padding,\n // background, borders); Config's className is meant for\n // theming hooks (the `dark` class, arbitrary selector hooks),\n // not layout.\n display: 'contents',\n }}\n >\n {children}\n </div>\n ) : (\n children\n )}\n </Context.Provider>\n );\n};\n\nexport default Config;\nexport { useConfig };\nexport type { Props, ThemeConfig, ThemeToken };\n"],"mappings":";;;;;;;;AASA,MAAM,gBAAkD;CACtD,cAAc;CACd,wBAAwB;CACxB,iBAAiB;CACjB,iBAAiB;CACjB,WAAW;CACX,qBAAqB;CACrB,cAAc;CACd,wBAAwB;CACxB,gBAAgB;CAChB,0BAA0B;CAC1B,YAAY;CACZ,sBAAsB;CACtB,aAAa;CACb,uBAAuB;CACvB,kBAAkB;CAClB,aAAa;CACb,YAAY;CACZ,WAAW;CACX,cAAc;CACf;AAED,MAAM,gBAAgB,UAA4C;AAChE,KAAI,CAAC,MACH,QAAO,EAAE;CAGX,MAAM,OAA+B,EAAE;AAEvC,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,EAAE;AAChD,MAAI,UAAU,OACZ;EAEF,MAAM,SAAS,cAAc;AAC7B,MAAI,OACF,MAAK,UAAU;;AAInB,QAAO;;AAST,MAAM,UAAU,EAAE,QAAQ,EAAE,EAAE,WAAW,eAAsB;CAC7D,MAAM,SAAS,WAAW;CAW1B,MAAM,WAAW,KAAK,UAAU,MAAM;CAGtC,MAAM,cAAc,eACX;EACL,GAAG,OAAO;EACV,GAAG;EACH,OAAO;GACL,GAAG,OAAO,MAAM;GAChB,GAAG,MAAM;GACV;EACF,GAED,CAZqB,KAAK,UAAU,OAAO,MAAM,EAYhC,SAAS,CAC3B;CAED,MAAM,eAAe,eAAe,EAAE,OAAO,aAAa,GAAG,CAAC,YAAY,CAAC;CAE3E,MAAM,UAAU,cACR,aAAa,YAAY,MAAM,EACrC,CAAC,YAAY,MAAM,CACpB;CAED,MAAM,aAAa,OAAO,KAAK,QAAQ,CAAC,SAAS;CACjD,MAAM,cAAc,YAAY,SAAS;CACzC,MAAM,eAAe,cAAc,eAAe;AAElD,QACE,oBAAC,QAAQ;EAAS,OAAO;YACtB,eACC,oBAAC;GACC,WAAW,GAAG,YAAY,QAAQ,QAAQ,UAAU;GACpD,OAAO;IACL,GAAI,aAAa,UAAU;IAW3B,SAAS;IACV;GAEA;IACG,GAEN;GAEe"}
package/dist/style.css CHANGED
@@ -311,9 +311,6 @@
311
311
  .z-50 {
312
312
  z-index: 50;
313
313
  }
314
- .z-100 {
315
- z-index: 100;
316
- }
317
314
  .z-10000 {
318
315
  z-index: 10000;
319
316
  }
@@ -416,6 +413,9 @@
416
413
  .block {
417
414
  display: block;
418
415
  }
416
+ .contents {
417
+ display: contents;
418
+ }
419
419
  .flex {
420
420
  display: flex;
421
421
  }
@@ -732,9 +732,6 @@
732
732
  .cursor-pointer {
733
733
  cursor: pointer;
734
734
  }
735
- .resize {
736
- resize: both;
737
- }
738
735
  .scroll-my-1 {
739
736
  scroll-margin-block: calc(var(--spacing) * 1);
740
737
  }
@@ -1407,6 +1404,9 @@
1407
1404
  outline-style: var(--tw-outline-style);
1408
1405
  outline-width: 1px;
1409
1406
  }
1407
+ .filter {
1408
+ filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
1409
+ }
1410
1410
  .transition {
1411
1411
  transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter, display, content-visibility, overlay, pointer-events;
1412
1412
  transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
@@ -3605,6 +3605,59 @@
3605
3605
  inherits: false;
3606
3606
  initial-value: solid;
3607
3607
  }
3608
+ @property --tw-blur {
3609
+ syntax: "*";
3610
+ inherits: false;
3611
+ }
3612
+ @property --tw-brightness {
3613
+ syntax: "*";
3614
+ inherits: false;
3615
+ }
3616
+ @property --tw-contrast {
3617
+ syntax: "*";
3618
+ inherits: false;
3619
+ }
3620
+ @property --tw-grayscale {
3621
+ syntax: "*";
3622
+ inherits: false;
3623
+ }
3624
+ @property --tw-hue-rotate {
3625
+ syntax: "*";
3626
+ inherits: false;
3627
+ }
3628
+ @property --tw-invert {
3629
+ syntax: "*";
3630
+ inherits: false;
3631
+ }
3632
+ @property --tw-opacity {
3633
+ syntax: "*";
3634
+ inherits: false;
3635
+ }
3636
+ @property --tw-saturate {
3637
+ syntax: "*";
3638
+ inherits: false;
3639
+ }
3640
+ @property --tw-sepia {
3641
+ syntax: "*";
3642
+ inherits: false;
3643
+ }
3644
+ @property --tw-drop-shadow {
3645
+ syntax: "*";
3646
+ inherits: false;
3647
+ }
3648
+ @property --tw-drop-shadow-color {
3649
+ syntax: "*";
3650
+ inherits: false;
3651
+ }
3652
+ @property --tw-drop-shadow-alpha {
3653
+ syntax: "<percentage>";
3654
+ inherits: false;
3655
+ initial-value: 100%;
3656
+ }
3657
+ @property --tw-drop-shadow-size {
3658
+ syntax: "*";
3659
+ inherits: false;
3660
+ }
3608
3661
  @property --tw-duration {
3609
3662
  syntax: "*";
3610
3663
  inherits: false;
@@ -3654,6 +3707,19 @@
3654
3707
  --tw-ring-offset-color: #fff;
3655
3708
  --tw-ring-offset-shadow: 0 0 #0000;
3656
3709
  --tw-outline-style: solid;
3710
+ --tw-blur: initial;
3711
+ --tw-brightness: initial;
3712
+ --tw-contrast: initial;
3713
+ --tw-grayscale: initial;
3714
+ --tw-hue-rotate: initial;
3715
+ --tw-invert: initial;
3716
+ --tw-opacity: initial;
3717
+ --tw-saturate: initial;
3718
+ --tw-sepia: initial;
3719
+ --tw-drop-shadow: initial;
3720
+ --tw-drop-shadow-color: initial;
3721
+ --tw-drop-shadow-alpha: 100%;
3722
+ --tw-drop-shadow-size: initial;
3657
3723
  --tw-duration: initial;
3658
3724
  --tw-content: "";
3659
3725
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jbpark/ui-kit",
3
- "version": "3.6.0",
3
+ "version": "3.7.0",
4
4
  "description": "Modern React UI component library built with TypeScript, Tailwind CSS, and Radix UI. Featuring atoms, molecules, organisms and layout templates for building beautiful interfaces.",
5
5
  "keywords": [
6
6
  "react",