@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.
- package/dist/Space.mjs +3 -1
- package/dist/Space.mjs.map +1 -1
- package/dist/components/molecules/collapse.mjs +1 -1
- package/dist/components/molecules/collapse.mjs.map +1 -1
- package/dist/components/molecules/marquees/item.d.mts.map +1 -1
- package/dist/components/molecules/marquees/item.mjs +3 -8
- package/dist/components/molecules/marquees/item.mjs.map +1 -1
- package/dist/components/molecules/reveals/reveals.d.mts +5 -0
- package/dist/components/molecules/reveals/reveals.d.mts.map +1 -1
- package/dist/components/molecules/reveals/reveals.mjs +6 -1
- package/dist/components/molecules/reveals/reveals.mjs.map +1 -1
- package/dist/components/molecules/upload.d.mts +10 -1
- package/dist/components/molecules/upload.d.mts.map +1 -1
- package/dist/components/molecules/upload.mjs +23 -6
- package/dist/components/molecules/upload.mjs.map +1 -1
- package/dist/components/organisms/modal.mjs +1 -1
- package/dist/components/organisms/toast.mjs +1 -1
- package/dist/components/templates/layout/header.mjs +1 -1
- package/dist/components/templates/layout/header.mjs.map +1 -1
- package/dist/components/templates/layout/layout.d.mts.map +1 -1
- package/dist/components/templates/layout/layout.mjs +23 -7
- package/dist/components/templates/layout/layout.mjs.map +1 -1
- package/dist/components/templates/layout/sider-context.mjs +20 -0
- package/dist/components/templates/layout/sider-context.mjs.map +1 -0
- package/dist/components/templates/layout/sider.d.mts.map +1 -1
- package/dist/components/templates/layout/sider.mjs +4 -2
- package/dist/components/templates/layout/sider.mjs.map +1 -1
- package/dist/providers/Config/index.mjs +6 -2
- package/dist/providers/Config/index.mjs.map +1 -1
- package/dist/style.css +72 -6
- 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", {
|
package/dist/Space.mjs.map
CHANGED
|
@@ -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
|
|
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 }
|
|
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 }
|
|
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":";;;
|
|
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" ?
|
|
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" ?
|
|
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 {
|
|
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"}
|
|
@@ -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,
|
|
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;
|
|
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":";;;
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
|
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":";;;
|
|
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
|
|
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
|
|
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(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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 {
|
|
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":";;;
|
|
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-
|
|
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
|
|
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
|
|
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,
|
|
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:
|
|
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 [
|
|
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.
|
|
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",
|