@jbpark/ui-kit 8.0.0 → 9.0.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/Button.d.mts +48 -12
- package/dist/Button.d.mts.map +1 -1
- package/dist/Button.mjs +62 -11
- package/dist/Button.mjs.map +1 -1
- package/dist/Tag.d.mts +8 -11
- package/dist/Tag.d.mts.map +1 -1
- package/dist/Tag.mjs +8 -7
- package/dist/Tag.mjs.map +1 -1
- package/dist/components/atoms/date-picker.mjs +1 -1
- package/dist/components/atoms/float-button/float-button.d.mts.map +1 -1
- package/dist/components/atoms/float-button/float-button.mjs +2 -1
- package/dist/components/atoms/float-button/float-button.mjs.map +1 -1
- package/dist/components/atoms/input/search.mjs +1 -1
- package/dist/components/atoms/radio/group.mjs +1 -1
- package/dist/components/atoms/radio/radio.d.mts +1 -1
- package/dist/components/molecules/dropdown.d.mts.map +1 -1
- package/dist/components/molecules/dropdown.mjs +2 -1
- package/dist/components/molecules/dropdown.mjs.map +1 -1
- package/dist/components/molecules/marquees/item.d.mts +2 -1
- package/dist/components/molecules/marquees/item.d.mts.map +1 -1
- package/dist/components/molecules/marquees/item.mjs +20 -5
- package/dist/components/molecules/marquees/item.mjs.map +1 -1
- package/dist/components/molecules/marquees/marquees.d.mts +2 -1
- package/dist/components/molecules/marquees/marquees.d.mts.map +1 -1
- package/dist/components/molecules/marquees/marquees.mjs +3 -2
- package/dist/components/molecules/marquees/marquees.mjs.map +1 -1
- package/dist/components/organisms/drawer.mjs +8 -8
- package/dist/components/organisms/drawer.mjs.map +1 -1
- package/dist/components/organisms/modal.d.mts +4 -5
- package/dist/components/organisms/modal.d.mts.map +1 -1
- package/dist/components/organisms/modal.mjs +5 -6
- package/dist/components/organisms/modal.mjs.map +1 -1
- package/dist/components/organisms/toast.d.mts +2 -8
- package/dist/components/organisms/toast.d.mts.map +1 -1
- package/dist/components/organisms/toast.mjs +4 -5
- package/dist/components/organisms/toast.mjs.map +1 -1
- package/dist/components/templates/layout/header.d.mts.map +1 -1
- package/dist/components/templates/layout/header.mjs +2 -1
- package/dist/components/templates/layout/header.mjs.map +1 -1
- package/dist/components/templates/layout/sider.mjs.map +1 -1
- package/dist/components/templates/page-header.mjs +4 -4
- package/dist/components/templates/page-header.mjs.map +1 -1
- package/dist/core/badge.d.mts +25 -2
- package/dist/core/badge.d.mts.map +1 -0
- package/dist/core/badge.mjs +47 -0
- package/dist/core/badge.mjs.map +1 -0
- package/dist/core/button.d.mts +14 -2
- package/dist/core/button.d.mts.map +1 -0
- package/dist/core/dialog.mjs +5 -5
- package/dist/core/dialog.mjs.map +1 -1
- package/dist/core/drawer.mjs +3 -2
- package/dist/core/drawer.mjs.map +1 -1
- package/dist/core/field.d.mts +1 -1
- package/dist/core/popover.mjs +3 -2
- package/dist/core/popover.mjs.map +1 -1
- package/dist/core/select.d.mts.map +1 -1
- package/dist/core/select.mjs +3 -2
- package/dist/core/select.mjs.map +1 -1
- package/dist/lib/colors.d.mts +6 -31
- package/dist/lib/colors.d.mts.map +1 -1
- package/dist/lib/z-layers.mjs +33 -0
- package/dist/lib/z-layers.mjs.map +1 -0
- package/dist/style.css +32 -34
- package/dist/utils.d.mts +12 -0
- package/dist/utils.d.mts.map +1 -1
- package/dist/utils.mjs +12 -0
- package/dist/utils.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/lib/chassis.mjs +0 -23
- package/dist/lib/chassis.mjs.map +0 -1
|
@@ -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 { gsap } from 'gsap';\n\nimport usePauseOnHover from './use-pause-on-hover';\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, hoverEvents } = usePauseOnHover(pauseOnHover);\n const [repeatCount, setRepeatCount] = useState(\n autoFill
|
|
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\nimport usePauseOnHover from './use-pause-on-hover';\n\nexport interface ItemProps {\n key?: React.Key;\n speed?: number;\n autoFill?: boolean | number;\n pause?: boolean;\n pauseOnHover?: boolean;\n gap?: number;\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 resolveInitialRepeatCount = (autoFill: boolean | number) => {\n if (!autoFill) {\n return 0;\n }\n\n return typeof autoFill === 'boolean' ? INITIAL_AUTO_FILL_GUESS : autoFill;\n};\n\nconst Item = ({\n width: _width,\n speed = 100,\n autoFill = false,\n pause: _pause = false,\n pauseOnHover = false,\n gap = 0,\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, hoverEvents } = usePauseOnHover(pauseOnHover);\n const [repeatCount, setRepeatCount] = useState(\n resolveInitialRepeatCount(autoFill),\n );\n const [prevWidthProp, setPrevWidthProp] = useState(_width);\n const [prevAutoFill, setPrevAutoFill] = useState(autoFill);\n const [prevGap, setPrevGap] = useState(gap);\n\n // Adjusted directly in render (React's \"adjust state during render\"\n // pattern) instead of an effect, so `width`/`repeatCount` re-sync with\n // the prop in the same render it changes rather than the render after.\n if (_width !== prevWidthProp) {\n setPrevWidthProp(_width);\n setWidth(_width);\n }\n\n if (autoFill !== prevAutoFill) {\n setPrevAutoFill(autoFill);\n setRepeatCount(resolveInitialRepeatCount(autoFill));\n }\n\n // `gap` widens every copy, so both the measured item width and the repeat\n // count derived from it are stale. Rewind `width` to the container width\n // so the measuring effect below recomputes the loop distance from scratch.\n if (gap !== prevGap) {\n setPrevGap(gap);\n setWidth(_width);\n setRepeatCount(resolveInitialRepeatCount(autoFill));\n }\n\n const isPaused = _pause || pause;\n\n useEffect(() => {\n if (\n !autoFill ||\n !children ||\n !itemRefs.current[0] ||\n typeof width === 'string'\n ) {\n return;\n }\n\n // `offsetWidth` is the border-box width, so it already includes the\n // `gap` padding each copy carries — the loop distance stays exact\n // without adding `gap` back in here.\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, gap]);\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 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 {/* Every copy carries the same trailing `gap`, so the spacing is\n uniform at the seam between copies and between the two loop\n halves — no special-casing at either boundary. */}\n <div\n ref={el => {\n if (el) {\n itemRefs.current[index] = el;\n }\n }}\n style={{ paddingRight: gap }}\n >\n {children}\n </div>\n {[\n ...Array(typeof autoFill === 'number' ? autoFill : repeatCount),\n ].map((_, i) => (\n <div key={i} style={{ paddingRight: gap }}>\n {children}\n </div>\n ))}\n </div>\n ))}\n </div>\n </div>\n );\n};\n\nexport default Item;\n"],"mappings":";;;;;;;AA+BA,MAAM,0BAA0B;AAEhC,MAAM,6BAA6B,aAA+B;CAChE,IAAI,CAAC,UACH,OAAO;CAGT,OAAO,OAAO,aAAa,YAAY,0BAA0B;AACnE;AAEA,MAAM,QAAQ,EACZ,OAAO,QACP,QAAQ,KACR,WAAW,OACX,OAAO,SAAS,OAChB,eAAe,OACf,MAAM,GACN,eACW;CACX,MAAM,WAAW,OAAyB,CAAC,CAAC;CAC5C,MAAM,eAAe,OAAuB,IAAI;CAChD,MAAM,WAAW,OAA+B,IAAI;CAEpD,MAAM,CAAC,OAAO,YAAY,SAA0B,MAAM;CAC1D,MAAM,EAAE,OAAO,gBAAgB,gBAAgB,YAAY;CAC3D,MAAM,CAAC,aAAa,kBAAkB,SACpC,0BAA0B,QAAQ,CACpC;CACA,MAAM,CAAC,eAAe,oBAAoB,SAAS,MAAM;CACzD,MAAM,CAAC,cAAc,mBAAmB,SAAS,QAAQ;CACzD,MAAM,CAAC,SAAS,cAAc,SAAS,GAAG;CAK1C,IAAI,WAAW,eAAe;EAC5B,iBAAiB,MAAM;EACvB,SAAS,MAAM;CACjB;CAEA,IAAI,aAAa,cAAc;EAC7B,gBAAgB,QAAQ;EACxB,eAAe,0BAA0B,QAAQ,CAAC;CACpD;CAKA,IAAI,QAAQ,SAAS;EACnB,WAAW,GAAG;EACd,SAAS,MAAM;EACf,eAAe,0BAA0B,QAAQ,CAAC;CACpD;CAEA,MAAM,WAAW,UAAU;CAE3B,gBAAgB;EACd,IACE,CAAC,YACD,CAAC,YACD,CAAC,SAAS,QAAQ,MAClB,OAAO,UAAU,UAEjB;EAMF,MAAM,YAAY,SAAS,QAAQ,EAAE,CAAC;EAEtC,IAAI,aAAa,OAAO;GACtB,SAAS,SAAS;GAClB,eAAe,CAAC;GAChB;EACF;EAEA,IAAI,aAAa;EACjB,IAAI,cAAc;EAElB,OAAO,aAAa,OAAO;GACzB,cAAc;GACd,eAAe;EACjB;EAEA,SAAS,UAAU;EACnB,eAAe,cAAc,CAAC;CAChC,GAAG;EAAC;EAAU;EAAO;EAAU;CAAG,CAAC;CAEnC,cACQ;EACJ,IAAI,CAAC,aAAa,WAAW,OAAO,UAAU,UAC5C;EAGF,IAAI,SAAS,SACX,SAAS,QAAQ,KAAK;EAGxB,SAAS,UAAU,KAAK,OACtB,aAAa,SACb,EAAE,GAAG,EAAE,GACP;GACE,GAAG,CAAC;GACJ,UAAU,QAAQ;GAClB,QAAQ;GACR,MAAM;EACR,CACF;EAEA,aAAa;GACX,SAAS,SAAS,KAAK;EACzB;CACF,GACA;EACE,OAAO;EACP,cAAc,CAAC,OAAO,KAAK;CAC7B,CACF;CAEA,gBAAgB;EACd,IAAI,CAAC,SAAS,SACZ;EAGF,IAAI,UACF,SAAS,QAAQ,MAAM;OAEvB,SAAS,QAAQ,KAAK;CAE1B,GAAG,CAAC,QAAQ,CAAC;CAEb,OACE,oBAAC,OAAD;EACE,WAAU;EACV,GAAI;EAGJ,UAAA,oBAAC,OAAD;GAAK,KAAK;GAAc,WAAU;GAC/B,UAAA,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,UACrB,qBAAC,OAAD;IAEE,OAAO,EAAE,UAAU,MAAM;IACzB,WAAU;IAHZ,UAAA,CAQE,oBAAC,OAAD;KACE,MAAK,OAAM;MACT,IAAI,IACF,SAAS,QAAQ,SAAS;KAE9B;KACA,OAAO,EAAE,cAAc,IAAI;KAE1B;IACE,CAAA,GACJ,CACC,GAAG,MAAM,OAAO,aAAa,WAAW,WAAW,WAAW,CAChE,CAAC,CAAC,KAAK,GAAG,MACR,oBAAC,OAAD;KAAa,OAAO,EAAE,cAAc,IAAI;KACrC;IACE,GAFK,CAEL,CACN,CACE;GAxBE,GAAA,KAwBF,CACN;EACE,CAAA;CACF,CAAA;AAET"}
|
|
@@ -4,9 +4,10 @@ interface Props extends React.ComponentPropsWithoutRef<'div'> {
|
|
|
4
4
|
speed?: number;
|
|
5
5
|
autoFill?: boolean | number;
|
|
6
6
|
pauseOnHover?: boolean;
|
|
7
|
+
gap?: number;
|
|
7
8
|
items?: ItemProps[];
|
|
8
9
|
}
|
|
9
|
-
declare const Marquees: ({ className, items, speed, pauseOnHover, autoFill, ...props }: Props) => import("react").JSX.Element;
|
|
10
|
+
declare const Marquees: ({ className, items, speed, pauseOnHover, autoFill, gap, ...props }: Props) => import("react").JSX.Element;
|
|
10
11
|
//#endregion
|
|
11
12
|
export { Props, Marquees as default };
|
|
12
13
|
//# sourceMappingURL=marquees.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"marquees.d.mts","names":[],"sources":["../../../../src/components/molecules/marquees/marquees.tsx"],"mappings":";;UAeiB,cAAc,MAAM;EACnC;EACA;EACA;EACA,QAAQ;;cAGJ,aAAY,WAAA,OAAA,OAAA,cAAA,
|
|
1
|
+
{"version":3,"file":"marquees.d.mts","names":[],"sources":["../../../../src/components/molecules/marquees/marquees.tsx"],"mappings":";;UAeiB,cAAc,MAAM;EACnC;EACA;EACA;EACA;EACA,QAAQ;;cAGJ,aAAY,WAAA,OAAA,OAAA,cAAA,UAAA,QAAA,SAQf,0BAAK,IAAA"}
|
|
@@ -6,8 +6,8 @@ import { useEffect, useRef, useState } from "react";
|
|
|
6
6
|
import { jsx } from "react/jsx-runtime";
|
|
7
7
|
import { useMergedRef, useResponsiveSize, useThrottledValue } from "@jbpark/use-hooks";
|
|
8
8
|
//#region src/components/molecules/marquees/marquees.tsx
|
|
9
|
-
const Marquees = ({ className, items, speed, pauseOnHover = true, autoFill = true, ...props }) => {
|
|
10
|
-
const [width, setWidth] = useState("
|
|
9
|
+
const Marquees = ({ className, items, speed, pauseOnHover = true, autoFill = true, gap, ...props }) => {
|
|
10
|
+
const [width, setWidth] = useState("100%");
|
|
11
11
|
const [padding, setPadding] = useState(0);
|
|
12
12
|
const { pause, hoverEvents } = usePauseOnHover(pauseOnHover);
|
|
13
13
|
const throttledWidth = useThrottledValue(width, 200);
|
|
@@ -35,6 +35,7 @@ const Marquees = ({ className, items, speed, pauseOnHover = true, autoFill = tru
|
|
|
35
35
|
pause,
|
|
36
36
|
speed,
|
|
37
37
|
autoFill,
|
|
38
|
+
gap,
|
|
38
39
|
...item,
|
|
39
40
|
children
|
|
40
41
|
}, itemKey || key))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"marquees.mjs","names":[],"sources":["../../../../src/components/molecules/marquees/marquees.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useRef, useState } from 'react';\n\nimport {\n useMergedRef,\n useResponsiveSize,\n useThrottledValue,\n} from '@jbpark/use-hooks';\n\nimport { cn } from '@repo/ui/utils';\n\nimport Item, { type ItemProps } from './item';\nimport usePauseOnHover from './use-pause-on-hover';\n\nexport interface Props extends React.ComponentPropsWithoutRef<'div'> {\n speed?: number;\n autoFill?: boolean | number;\n pauseOnHover?: boolean;\n items?: ItemProps[];\n}\n\nconst Marquees = ({\n className,\n items,\n speed,\n pauseOnHover = true,\n autoFill = true,\n ...props\n}: Props) => {\n const [width, setWidth] = useState<string | number>(\n '
|
|
1
|
+
{"version":3,"file":"marquees.mjs","names":[],"sources":["../../../../src/components/molecules/marquees/marquees.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useRef, useState } from 'react';\n\nimport {\n useMergedRef,\n useResponsiveSize,\n useThrottledValue,\n} from '@jbpark/use-hooks';\n\nimport { cn } from '@repo/ui/utils';\n\nimport Item, { type ItemProps } from './item';\nimport usePauseOnHover from './use-pause-on-hover';\n\nexport interface Props extends React.ComponentPropsWithoutRef<'div'> {\n speed?: number;\n autoFill?: boolean | number;\n pauseOnHover?: boolean;\n gap?: number;\n items?: ItemProps[];\n}\n\nconst Marquees = ({\n className,\n items,\n speed,\n pauseOnHover = true,\n autoFill = true,\n gap,\n ...props\n}: Props) => {\n // The container's own width, not the viewport's. `useResponsiveSize`\n // measures in a layout effect, but this component copies the result into\n // state in a passive effect and then throttles it, so the seed is what\n // paints for the first ~200ms. `100vw` overflowed every padded or columned\n // layout for that window — on the docs page it drew the track 1280px wide\n // inside a 703px column, briefly showing the whole track and giving the\n // page a horizontal scrollbar. `100%` resolves to the same value the\n // measurement lands on, so there is nothing left to snap.\n const [width, setWidth] = useState<string | number>(\n '100%',\n //\n );\n const [padding, setPadding] = useState(0);\n const { pause, hoverEvents } = usePauseOnHover(pauseOnHover);\n\n const throttledWidth = useThrottledValue(width, 200);\n\n const { size, ref: responsiveRef } = useResponsiveSize<HTMLDivElement>();\n const containerRef = useRef<HTMLDivElement | null>(null);\n const setContainerRef = useMergedRef(containerRef, responsiveRef);\n\n useEffect(() => {\n if (!size.width || !containerRef.current) {\n return;\n }\n\n const computedStyle = getComputedStyle(containerRef.current);\n const paddingLeft = parseFloat(computedStyle.paddingLeft) || 0;\n const paddingRight = parseFloat(computedStyle.paddingRight) || 0;\n\n setPadding(paddingLeft + paddingRight);\n\n setWidth(size.width);\n }, [size]);\n\n return (\n <div\n ref={setContainerRef}\n className={cn(\n className,\n //\n )}\n {...hoverEvents}\n {...props}\n >\n <div\n className={cn(\n 'flex flex-col gap-y-5',\n 'overflow-hidden',\n 'whitespace-nowrap',\n 'bg-inherit',\n //\n )}\n style={{\n width:\n typeof throttledWidth === 'number'\n ? throttledWidth - padding\n : throttledWidth,\n }}\n >\n {items?.map(({ children, key: itemKey, ...item }: ItemProps, key) => (\n <Item\n key={itemKey || key}\n width={throttledWidth}\n pause={pause}\n speed={speed}\n autoFill={autoFill}\n gap={gap}\n {...item}\n >\n {children}\n </Item>\n ))}\n </div>\n </div>\n );\n};\n\nexport default Marquees;\n"],"mappings":";;;;;;;;AAuBA,MAAM,YAAY,EAChB,WACA,OACA,OACA,eAAe,MACf,WAAW,MACX,KACA,GAAG,YACQ;CASX,MAAM,CAAC,OAAO,YAAY,SACxB,MAEF;CACA,MAAM,CAAC,SAAS,cAAc,SAAS,CAAC;CACxC,MAAM,EAAE,OAAO,gBAAgB,gBAAgB,YAAY;CAE3D,MAAM,iBAAiB,kBAAkB,OAAO,GAAG;CAEnD,MAAM,EAAE,MAAM,KAAK,kBAAkB,kBAAkC;CACvE,MAAM,eAAe,OAA8B,IAAI;CACvD,MAAM,kBAAkB,aAAa,cAAc,aAAa;CAEhE,gBAAgB;EACd,IAAI,CAAC,KAAK,SAAS,CAAC,aAAa,SAC/B;EAGF,MAAM,gBAAgB,iBAAiB,aAAa,OAAO;EAC3D,MAAM,cAAc,WAAW,cAAc,WAAW,KAAK;EAC7D,MAAM,eAAe,WAAW,cAAc,YAAY,KAAK;EAE/D,WAAW,cAAc,YAAY;EAErC,SAAS,KAAK,KAAK;CACrB,GAAG,CAAC,IAAI,CAAC;CAET,OACE,oBAAC,OAAD;EACE,KAAK;EACL,WAAW,GACT,SAEF;EACA,GAAI;EACJ,GAAI;EAEJ,UAAA,oBAAC,OAAD;GACE,WAAW,GACT,yBACA,mBACA,qBACA,YAEF;GACA,OAAO,EACL,OACE,OAAO,mBAAmB,WACtB,iBAAiB,UACjB,eACR;GAEC,UAAA,OAAO,KAAK,EAAE,UAAU,KAAK,SAAS,GAAG,QAAmB,QAC3D,oBAAC,MAAD;IAEE,OAAO;IACA;IACA;IACG;IACL;IACL,GAAI;IAEH;GACG,GATC,WAAW,GASZ,CACP;EACE,CAAA;CACF,CAAA;AAET"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { cn
|
|
2
|
+
import { cn } from "../../utils.mjs";
|
|
3
3
|
import { DEFAULT_LOCALE, useConfig } from "../../providers/config/context.mjs";
|
|
4
|
-
import Button from "../../Button.mjs";
|
|
5
4
|
import { drawer_exports } from "../../core/drawer.mjs";
|
|
5
|
+
import Button from "../../Button.mjs";
|
|
6
6
|
import { useEffect } from "react";
|
|
7
7
|
import { X } from "lucide-react";
|
|
8
8
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -104,10 +104,10 @@ const Drawer = ({ open, title, extra, footer, closable = true, closeIcon, direct
|
|
|
104
104
|
children: [/* @__PURE__ */ jsx(DrawerTitle, {
|
|
105
105
|
className: cn(classNames?.title),
|
|
106
106
|
children: title
|
|
107
|
-
}),
|
|
107
|
+
}), extra != null && /* @__PURE__ */ jsx("div", {
|
|
108
108
|
className: cn("shrink-0", classNames?.extra),
|
|
109
|
-
children:
|
|
110
|
-
})
|
|
109
|
+
children: extra
|
|
110
|
+
})]
|
|
111
111
|
})]
|
|
112
112
|
}), /* @__PURE__ */ jsx(DrawerDescription, { className: "hidden" })]
|
|
113
113
|
}),
|
|
@@ -115,10 +115,10 @@ const Drawer = ({ open, title, extra, footer, closable = true, closeIcon, direct
|
|
|
115
115
|
className: cn("overflow-auto p-5", classNames?.body),
|
|
116
116
|
children
|
|
117
117
|
}),
|
|
118
|
-
|
|
118
|
+
footer != null && /* @__PURE__ */ jsx(DrawerFooter, {
|
|
119
119
|
className: cn(classNames?.footer),
|
|
120
|
-
children:
|
|
121
|
-
})
|
|
120
|
+
children: footer
|
|
121
|
+
})
|
|
122
122
|
]
|
|
123
123
|
})
|
|
124
124
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drawer.mjs","names":["drawer"],"sources":["../../../src/components/organisms/drawer.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect } from 'react';\n\nimport { X } from 'lucide-react';\n\nimport { DEFAULT_LOCALE, useConfig } from '@repo/ui/providers';\nimport { cn
|
|
1
|
+
{"version":3,"file":"drawer.mjs","names":["drawer"],"sources":["../../../src/components/organisms/drawer.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect } from 'react';\n\nimport { X } from 'lucide-react';\n\nimport { DEFAULT_LOCALE, useConfig } from '@repo/ui/providers';\nimport { cn } from '@repo/ui/utils';\n\nimport { drawer } from '../../core';\nimport Button from '../atoms/button';\n\nconst {\n DrawerContent,\n Drawer: DrawerCore,\n DrawerDescription,\n DrawerFooter,\n DrawerHeader,\n DrawerTitle,\n} = drawer;\n\n// `body.style.pointerEvents` is global state (per document), so several\n// mask-less overlays opening at once would otherwise race: each instance\n// would capture whatever the previous one had already written and the\n// last cleanup would restore a stale value. Reference counting makes only\n// the first instance capture the original value and only the last one\n// restore it. Keyed by document (not a single module-level counter) so\n// drawers portaled into different documents — e.g. live-editor's iframe\n// Renderer — don't share state with the host document.\ntype MaskLessState = { count: number; originalPointerEvents: string | null };\nconst maskLessStates = new WeakMap<Document, MaskLessState>();\n\nconst getMaskLessState = (doc: Document): MaskLessState => {\n let state = maskLessStates.get(doc);\n\n if (!state) {\n state = { count: 0, originalPointerEvents: null };\n maskLessStates.set(doc, state);\n }\n\n return state;\n};\n\nexport interface Props {\n open: boolean;\n children?: React.ReactNode;\n title?: React.ReactNode;\n extra?: React.ReactNode;\n footer?: React.ReactNode;\n closable?: boolean;\n closeIcon?: React.ReactNode;\n direction?: 'top' | 'bottom' | 'left' | 'right';\n size?: 'small' | 'medium' | 'large' | 'full' | string;\n maskClosable?: boolean;\n handlebar?: boolean;\n draggable?: boolean;\n rounded?: boolean;\n mask?: boolean;\n className?: string;\n style?: React.CSSProperties;\n classNames?: {\n mask?: string;\n content?: string;\n handlebar?: string;\n header?: string;\n title?: string;\n close?: string;\n extra?: string;\n body?: string;\n footer?: string;\n };\n container?: HTMLElement;\n onClose: () => void;\n}\n\nconst SIZES: Record<string, string> = {\n small: '30%',\n medium: '50%',\n large: '75%',\n full: '90%',\n};\n\n// Rounds the edge facing into the viewport — the opposite edge from\n// whichever side the drawer is anchored to.\nconst ROUNDED_CLASSES: Record<'top' | 'bottom' | 'left' | 'right', string> = {\n top: 'rounded-b-[30px]!',\n bottom: 'rounded-t-[30px]!',\n left: 'rounded-r-[30px]!',\n right: 'rounded-l-[30px]!',\n};\n\nconst getSizeStyles = (\n direction: 'top' | 'bottom' | 'left' | 'right',\n size: string,\n): React.CSSProperties => {\n const value = SIZES[size] || size;\n\n return direction === 'top' || direction === 'bottom'\n ? { height: value }\n : { width: value };\n};\n\nconst Drawer = ({\n open,\n title,\n extra,\n footer,\n closable = true,\n closeIcon,\n direction = 'bottom',\n size = 'medium',\n maskClosable = true,\n handlebar = true,\n draggable = false,\n rounded = false,\n mask = true,\n className,\n classNames,\n style,\n children,\n container,\n onClose,\n ...props\n}: Props) => {\n const { locale } = useConfig();\n\n useEffect(() => {\n if (!open || mask) {\n return;\n }\n\n const targetDocument = container?.ownerDocument ?? document;\n const targetWindow = targetDocument.defaultView ?? window;\n const body = targetDocument.body;\n const state = getMaskLessState(targetDocument);\n\n if (state.count === 0) {\n state.originalPointerEvents = body.style.pointerEvents;\n }\n state.count += 1;\n\n const raf = targetWindow.requestAnimationFrame(() => {\n body.style.pointerEvents = 'auto';\n });\n\n return () => {\n targetWindow.cancelAnimationFrame(raf);\n state.count -= 1;\n\n if (state.count === 0) {\n body.style.pointerEvents = state.originalPointerEvents ?? '';\n state.originalPointerEvents = null;\n }\n };\n }, [open, mask, container]);\n\n return (\n <DrawerCore\n open={open}\n direction={direction}\n handleOnly={!draggable}\n container={container}\n onOpenChange={open => {\n if (!open) {\n onClose();\n }\n }}\n {...props}\n >\n <DrawerContent\n className={cn(\n 'border-none outline-none',\n rounded ? ROUNDED_CLASSES[direction] : 'rounded-none!',\n classNames?.content,\n className,\n //\n )}\n handlebar={handlebar}\n mask={mask}\n onPointerDownOutside={event => {\n if (!maskClosable) {\n event.preventDefault();\n }\n }}\n classNames={{\n mask: classNames?.mask || '',\n handlebar: classNames?.handlebar || '',\n }}\n style={{\n ...getSizeStyles(direction, size),\n ...style,\n }}\n >\n <DrawerHeader\n className={cn(\n classNames?.header,\n !title && 'p-0',\n //\n )}\n >\n <div className=\"flex items-start gap-2\">\n {closable && (\n <Button\n variant=\"text\"\n shape=\"circle\"\n size=\"small\"\n aria-label={locale.close ?? DEFAULT_LOCALE.close}\n icon={closeIcon || <X />}\n onClick={onClose}\n className={cn(classNames?.close)}\n />\n )}\n <div className=\"flex flex-1 items-start justify-between gap-2\">\n <DrawerTitle className={cn(classNames?.title)}>\n {title}\n </DrawerTitle>\n {extra != null && (\n <div className={cn('shrink-0', classNames?.extra)}>{extra}</div>\n )}\n </div>\n </div>\n <DrawerDescription className=\"hidden\" />\n </DrawerHeader>\n <div\n className={cn(\n 'overflow-auto p-5',\n classNames?.body,\n //\n )}\n >\n {children}\n </div>\n {footer != null && (\n <DrawerFooter className={cn(classNames?.footer)}>\n {footer}\n </DrawerFooter>\n )}\n </DrawerContent>\n </DrawerCore>\n );\n};\n\nexport default Drawer;\n"],"mappings":"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"}
|
|
@@ -30,12 +30,11 @@ type ModalStatus = 'info' | 'success' | 'error' | 'warning';
|
|
|
30
30
|
interface StaticProps extends Props {
|
|
31
31
|
status?: ModalStatus;
|
|
32
32
|
/**
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* (still accepted here as `type="confirm"` for back-compat).
|
|
33
|
+
* Interaction mode set by `Modal.confirm()` to request the two-button
|
|
34
|
+
* footer. `confirm` is a mode, not a status, so it rides its own axis rather
|
|
35
|
+
* than `status` — call `Modal.confirm()` rather than setting this directly.
|
|
37
36
|
*/
|
|
38
|
-
|
|
37
|
+
mode?: 'confirm';
|
|
39
38
|
id?: string;
|
|
40
39
|
icon?: React.ReactNode;
|
|
41
40
|
container?: HTMLElement;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"modal.d.mts","names":[],"sources":["../../../src/components/organisms/modal.tsx"],"mappings":";;UAiCiB;EACf;EACA;EACA;IAGM,YAAY,MAAM;IAClB;;EAEN,YAAY,MAAM;EAClB,QAAQ,MAAM;EACd,UAAU,MAAM;EAChB,SAAS,MAAM;EACf;EACA;IACE;IACA;IACA;IACA;;EAEF,QAAQ,MAAM;EACd;EACA;EACA,YAAY;EACZ,WAAW,MAAM;EACjB;EACA;;KAGG;UAEK,oBAAoB;EAC5B,SAAS
|
|
1
|
+
{"version":3,"file":"modal.d.mts","names":[],"sources":["../../../src/components/organisms/modal.tsx"],"mappings":";;UAiCiB;EACf;EACA;EACA;IAGM,YAAY,MAAM;IAClB;;EAEN,YAAY,MAAM;EAClB,QAAQ,MAAM;EACd,UAAU,MAAM;EAChB,SAAS,MAAM;EACf;EACA;IACE;IACA;IACA;IACA;;EAEF,QAAQ,MAAM;EACd;EACA;EACA,YAAY;EACZ,WAAW,MAAM;EACjB;EACA;;KAGG;UAEK,oBAAoB;EAC5B,SAAS;;;;;;EAMT;EACA;EACA,OAAO,MAAM;EACb,YAAY;;cAGR;KAkBH,MAAA,cAAA,UAAA,WAAA,WAAA,YAAA,OAAA,OAAA,SAAA,QAAA,WAAA,UAAA,QAAA,YAAA,MAAA,aAAA,SAAA,QAAK,MAAA,IAAA;;;EAqNa,KAAA,OAAA;EAEG,QAAA,OAAA;EAEF,MAAA,OAAA;EAEE,QAAA,OAAA;EAIA,QAAA,OAAA"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn } from "../../utils.mjs";
|
|
3
3
|
import { DEFAULT_LOCALE, useConfig } from "../../providers/config/context.mjs";
|
|
4
|
-
import Button from "../../Button.mjs";
|
|
5
4
|
import { dialog_exports } from "../../core/dialog.mjs";
|
|
5
|
+
import Button from "../../Button.mjs";
|
|
6
6
|
import { createImperativeStack, isBrowser } from "./imperative-stack.mjs";
|
|
7
7
|
import { useState } from "react";
|
|
8
8
|
import { Check, CircleQuestionMark, Info, OctagonAlert, OctagonX } from "lucide-react";
|
|
@@ -65,13 +65,12 @@ const STATIC_ICONS = {
|
|
|
65
65
|
warning: /* @__PURE__ */ jsx(OctagonAlert, { className: "text-yellow-400" }),
|
|
66
66
|
confirm: /* @__PURE__ */ jsx(CircleQuestionMark, {})
|
|
67
67
|
};
|
|
68
|
-
const StaticModal = ({ id, status,
|
|
68
|
+
const StaticModal = ({ id, status, mode, title, content, okText, cancelText, container, icon, onOk, onCancel, ...props }) => {
|
|
69
69
|
const { locale } = useConfig();
|
|
70
70
|
const [open, setOpen] = useState(true);
|
|
71
71
|
const resolvedOkText = okText ?? locale.ok ?? DEFAULT_LOCALE.ok;
|
|
72
72
|
const resolvedCancelText = cancelText ?? locale.cancel ?? DEFAULT_LOCALE.cancel;
|
|
73
|
-
const isConfirm =
|
|
74
|
-
const resolvedStatus = status ?? (isConfirm ? void 0 : type);
|
|
73
|
+
const isConfirm = mode === "confirm";
|
|
75
74
|
const closeModal = (callback) => {
|
|
76
75
|
callback?.();
|
|
77
76
|
setOpen(false);
|
|
@@ -108,7 +107,7 @@ const StaticModal = ({ id, status, type, title, content, okText, cancelText, con
|
|
|
108
107
|
},
|
|
109
108
|
title: /* @__PURE__ */ jsxs("p", {
|
|
110
109
|
className: cn("text-center text-lg leading-normal whitespace-pre-wrap", "flex items-center justify-center gap-x-2"),
|
|
111
|
-
children: [icon || (isConfirm ? STATIC_ICONS.confirm :
|
|
110
|
+
children: [icon || (isConfirm ? STATIC_ICONS.confirm : status && STATIC_ICONS[status]), title]
|
|
112
111
|
}),
|
|
113
112
|
container,
|
|
114
113
|
onCancel: () => closeModal(onCancel),
|
|
@@ -146,7 +145,7 @@ Modal.warning = (props) => modalStack.render({
|
|
|
146
145
|
...props
|
|
147
146
|
});
|
|
148
147
|
Modal.confirm = (props) => modalStack.render({
|
|
149
|
-
|
|
148
|
+
mode: "confirm",
|
|
150
149
|
...props
|
|
151
150
|
});
|
|
152
151
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"modal.mjs","names":["dialog"],"sources":["../../../src/components/organisms/modal.tsx"],"sourcesContent":["'use client';\n\nimport React, { useState } from 'react';\nimport { createPortal } from 'react-dom';\n\nimport {\n Check,\n CircleQuestionMark,\n Info,\n OctagonAlert,\n OctagonX,\n} from 'lucide-react';\n\nimport { DEFAULT_LOCALE, useConfig } from '@repo/ui/providers';\nimport { cn } from '@repo/ui/utils';\n\nimport { dialog } from '../../core';\nimport Button from '../atoms/button';\nimport {\n type ImperativeStack,\n createImperativeStack,\n isBrowser,\n} from './imperative-stack';\n\nconst {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} = dialog;\n\nexport interface Props {\n open?: boolean;\n maskClosable?: boolean;\n closable?:\n | boolean\n | {\n closeIcon?: React.ReactNode;\n disabled?: boolean;\n };\n closeIcon?: React.ReactNode;\n title?: React.ReactNode;\n content?: React.ReactNode;\n footer?: React.ReactNode;\n className?: string;\n classNames?: {\n mask?: string;\n header?: string;\n body?: string;\n footer?: string;\n };\n style?: React.CSSProperties;\n okText?: string;\n cancelText?: string;\n container?: HTMLElement;\n children?: React.ReactNode;\n onOk?: () => void;\n onCancel?: () => void;\n}\n\ntype ModalStatus = 'info' | 'success' | 'error' | 'warning';\n\ninterface StaticProps extends Props {\n status?: ModalStatus;\n /**\n * @deprecated Use `status` for the semantic state. Kept as an alias so\n * existing `type=\"info|success|error|warning\"` calls keep working. `confirm`\n * is an interaction mode, not a status — call `Modal.confirm()` instead\n * (still accepted here as `type=\"confirm\"` for back-compat).\n */\n type?: ModalStatus | 'confirm';\n id?: string;\n icon?: React.ReactNode;\n container?: HTMLElement;\n}\n\nconst Modal = ({\n open = false,\n maskClosable,\n closable = false,\n closeIcon,\n className,\n classNames,\n style,\n title,\n content,\n footer,\n container,\n children,\n okText,\n cancelText,\n onOk,\n onCancel,\n ...props\n}: Props) => {\n const { locale, defaultProps } = useConfig();\n const modalDefaults = defaultProps?.modal as\n Partial<Pick<Props, 'maskClosable'>> | undefined;\n // Explicit prop > `Config` default > built-in, matching Button/Tag.\n const resolvedMaskClosable =\n maskClosable ?? modalDefaults?.maskClosable ?? false;\n\n if (typeof window === 'undefined') {\n return null;\n }\n\n return (\n <Dialog\n open={open}\n onOpenChange={(open: boolean) => {\n if (!open) {\n onCancel?.();\n }\n }}\n {...props}\n >\n <DialogContent\n className={cn(\n 'rounded-lg p-4 max-sm:max-w-[80%]',\n 'border-none outline-none',\n className,\n )}\n classNames={{\n ...classNames,\n mask: cn('bg-black/60', classNames?.mask),\n }}\n style={style}\n closable={closable}\n closeIcon={closeIcon}\n container={container}\n onPointerDownOutside={event => {\n if (!resolvedMaskClosable) {\n event.preventDefault();\n }\n }}\n >\n {/**\n * @todo [Dialog & AlertDialog] fix: can't get id correctly in shadow dom\n * @see https://github.com/radix-ui/primitives/pull/3384\n **/}\n <DialogHeader\n className={cn(\n classNames?.header,\n !title && 'hidden',\n //\n )}\n >\n <DialogTitle>{title}</DialogTitle>\n <DialogDescription className=\"hidden\" />\n </DialogHeader>\n <div className={cn('break-all', classNames?.body)}>\n {children ?? content}\n </div>\n {footer !== null && (\n <DialogFooter\n className={cn('flex-row justify-end gap-x-2', classNames?.footer)}\n >\n {footer || (\n <>\n <Button onClick={onOk}>\n {okText ?? locale.ok ?? DEFAULT_LOCALE.ok}\n </Button>\n <Button variant=\"outlined\" onClick={onCancel}>\n {cancelText ?? locale.cancel ?? DEFAULT_LOCALE.cancel}\n </Button>\n </>\n )}\n </DialogFooter>\n )}\n </DialogContent>\n </Dialog>\n );\n};\n\nconst STATIC_ICONS = {\n info: <Info className=\"text-blue-400\" />,\n success: <Check className=\"text-green-400\" />,\n error: <OctagonX className=\"text-red-400\" />,\n warning: <OctagonAlert className=\"text-yellow-400\" />,\n confirm: <CircleQuestionMark />,\n};\n\nconst StaticModal = ({\n id,\n status,\n type,\n title,\n content,\n okText,\n cancelText,\n container,\n icon,\n onOk,\n onCancel,\n ...props\n}: StaticProps & { id: string }): React.ReactPortal | null => {\n const { locale } = useConfig();\n const [open, setOpen] = useState(true);\n const resolvedOkText = okText ?? locale.ok ?? DEFAULT_LOCALE.ok;\n const resolvedCancelText =\n cancelText ?? locale.cancel ?? DEFAULT_LOCALE.cancel;\n // `confirm` is an interaction mode (two-button footer), not a status. The\n // semantic state comes from `status`, with `type` as its deprecated alias.\n const isConfirm = type === 'confirm';\n const resolvedStatus = status ?? (isConfirm ? undefined : type);\n\n const closeModal = (callback?: () => void) => {\n callback?.();\n setOpen(false);\n setTimeout(() => {\n modalStack.destroy(id);\n }, 200);\n };\n\n const footer = isConfirm ? (\n <div className=\"grid w-full grid-cols-5 gap-x-2\">\n <Button\n variant=\"outlined\"\n className=\"col-span-2\"\n onClick={() => closeModal(onCancel)}\n >\n {resolvedCancelText}\n </Button>\n <Button className=\"col-span-3\" onClick={() => closeModal(onOk)}>\n {resolvedOkText}\n </Button>\n </div>\n ) : (\n <Button\n className={cn(\n 'grow',\n //\n )}\n onClick={() => closeModal(onOk)}\n >\n {resolvedOkText}\n </Button>\n );\n\n if (!isBrowser) {\n return null;\n }\n\n return createPortal(\n <Modal\n open={open}\n footer={footer}\n className={cn(\n 'z-10000',\n 'box-content w-100',\n 'rounded-3xl p-4',\n 'gap-y-0',\n )}\n classNames={{\n mask: 'z-10000 bg-black/[.6]',\n body: cn(\n 'text-black-70 whitespace-pre-wrap text-center',\n content && 'mt-3',\n ),\n footer: 'mt-9',\n }}\n title={\n <p\n className={cn(\n 'text-center text-lg leading-normal whitespace-pre-wrap',\n 'flex items-center justify-center gap-x-2',\n //\n )}\n >\n {icon ||\n (isConfirm\n ? STATIC_ICONS.confirm\n : resolvedStatus && STATIC_ICONS[resolvedStatus])}\n {title}\n </p>\n }\n container={container}\n onCancel={() => closeModal(onCancel)}\n {...props}\n >\n {content}\n </Modal>,\n modalStack.getRootElement(container)!,\n );\n};\n\n// No `role`/`aria-modal` here — this element is only a mount point for the\n// imperative stack, not the dialog itself. Radix already renders its own\n// `role=\"dialog\"`/`aria-modal=\"true\"` on the actual `DialogContent` via\n// portal once a modal is open. Setting it here as well would (a) leave\n// `aria-modal=\"true\"` on the page permanently, even with zero modals open,\n// hiding the rest of the document from screen readers, and (b) duplicate\n// Radix's own dialog role once one is open.\nconst modalStack: ImperativeStack<StaticProps> =\n createImperativeStack<StaticProps>({\n createRootElement: () => {\n const el = document.createElement('div');\n el.style.zIndex = '10000';\n el.style.position = 'absolute';\n return el;\n },\n StackItem: StaticModal,\n });\n\nModal.destroy = modalStack.destroy;\n\nModal.destroyAll = () => {\n modalStack.destroy();\n};\n\nModal.info = (props: StaticProps) =>\n modalStack.render({ status: 'info', ...props });\nModal.success = (props: StaticProps) =>\n modalStack.render({ status: 'success', ...props });\nModal.error = (props: StaticProps) =>\n modalStack.render({ status: 'error', ...props });\nModal.warning = (props: StaticProps) =>\n modalStack.render({ status: 'warning', ...props });\n// `confirm` is a mode, not a status, so it rides the `type` axis (the only\n// place it lives) rather than `status`.\nModal.confirm = (props: StaticProps) =>\n modalStack.render({ type: 'confirm', ...props });\n\nexport default Modal;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"modal.mjs","names":["dialog"],"sources":["../../../src/components/organisms/modal.tsx"],"sourcesContent":["'use client';\n\nimport React, { useState } from 'react';\nimport { createPortal } from 'react-dom';\n\nimport {\n Check,\n CircleQuestionMark,\n Info,\n OctagonAlert,\n OctagonX,\n} from 'lucide-react';\n\nimport { DEFAULT_LOCALE, useConfig } from '@repo/ui/providers';\nimport { cn } from '@repo/ui/utils';\n\nimport { dialog } from '../../core';\nimport Button from '../atoms/button';\nimport {\n type ImperativeStack,\n createImperativeStack,\n isBrowser,\n} from './imperative-stack';\n\nconst {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} = dialog;\n\nexport interface Props {\n open?: boolean;\n maskClosable?: boolean;\n closable?:\n | boolean\n | {\n closeIcon?: React.ReactNode;\n disabled?: boolean;\n };\n closeIcon?: React.ReactNode;\n title?: React.ReactNode;\n content?: React.ReactNode;\n footer?: React.ReactNode;\n className?: string;\n classNames?: {\n mask?: string;\n header?: string;\n body?: string;\n footer?: string;\n };\n style?: React.CSSProperties;\n okText?: string;\n cancelText?: string;\n container?: HTMLElement;\n children?: React.ReactNode;\n onOk?: () => void;\n onCancel?: () => void;\n}\n\ntype ModalStatus = 'info' | 'success' | 'error' | 'warning';\n\ninterface StaticProps extends Props {\n status?: ModalStatus;\n /**\n * Interaction mode set by `Modal.confirm()` to request the two-button\n * footer. `confirm` is a mode, not a status, so it rides its own axis rather\n * than `status` — call `Modal.confirm()` rather than setting this directly.\n */\n mode?: 'confirm';\n id?: string;\n icon?: React.ReactNode;\n container?: HTMLElement;\n}\n\nconst Modal = ({\n open = false,\n maskClosable,\n closable = false,\n closeIcon,\n className,\n classNames,\n style,\n title,\n content,\n footer,\n container,\n children,\n okText,\n cancelText,\n onOk,\n onCancel,\n ...props\n}: Props) => {\n const { locale, defaultProps } = useConfig();\n const modalDefaults = defaultProps?.modal as\n Partial<Pick<Props, 'maskClosable'>> | undefined;\n // Explicit prop > `Config` default > built-in, matching Button/Tag.\n const resolvedMaskClosable =\n maskClosable ?? modalDefaults?.maskClosable ?? false;\n\n if (typeof window === 'undefined') {\n return null;\n }\n\n return (\n <Dialog\n open={open}\n onOpenChange={(open: boolean) => {\n if (!open) {\n onCancel?.();\n }\n }}\n {...props}\n >\n <DialogContent\n className={cn(\n 'rounded-lg p-4 max-sm:max-w-[80%]',\n 'border-none outline-none',\n className,\n )}\n classNames={{\n ...classNames,\n mask: cn('bg-black/60', classNames?.mask),\n }}\n style={style}\n closable={closable}\n closeIcon={closeIcon}\n container={container}\n onPointerDownOutside={event => {\n if (!resolvedMaskClosable) {\n event.preventDefault();\n }\n }}\n >\n {/**\n * @todo [Dialog & AlertDialog] fix: can't get id correctly in shadow dom\n * @see https://github.com/radix-ui/primitives/pull/3384\n **/}\n <DialogHeader\n className={cn(\n classNames?.header,\n !title && 'hidden',\n //\n )}\n >\n <DialogTitle>{title}</DialogTitle>\n <DialogDescription className=\"hidden\" />\n </DialogHeader>\n <div className={cn('break-all', classNames?.body)}>\n {children ?? content}\n </div>\n {footer !== null && (\n <DialogFooter\n className={cn('flex-row justify-end gap-x-2', classNames?.footer)}\n >\n {footer || (\n <>\n <Button onClick={onOk}>\n {okText ?? locale.ok ?? DEFAULT_LOCALE.ok}\n </Button>\n <Button variant=\"outlined\" onClick={onCancel}>\n {cancelText ?? locale.cancel ?? DEFAULT_LOCALE.cancel}\n </Button>\n </>\n )}\n </DialogFooter>\n )}\n </DialogContent>\n </Dialog>\n );\n};\n\nconst STATIC_ICONS = {\n info: <Info className=\"text-blue-400\" />,\n success: <Check className=\"text-green-400\" />,\n error: <OctagonX className=\"text-red-400\" />,\n warning: <OctagonAlert className=\"text-yellow-400\" />,\n confirm: <CircleQuestionMark />,\n};\n\nconst StaticModal = ({\n id,\n status,\n mode,\n title,\n content,\n okText,\n cancelText,\n container,\n icon,\n onOk,\n onCancel,\n ...props\n}: StaticProps & { id: string }): React.ReactPortal | null => {\n const { locale } = useConfig();\n const [open, setOpen] = useState(true);\n const resolvedOkText = okText ?? locale.ok ?? DEFAULT_LOCALE.ok;\n const resolvedCancelText =\n cancelText ?? locale.cancel ?? DEFAULT_LOCALE.cancel;\n // `confirm` is an interaction mode (two-button footer), not a status; the\n // semantic state comes from `status`.\n const isConfirm = mode === 'confirm';\n\n const closeModal = (callback?: () => void) => {\n callback?.();\n setOpen(false);\n setTimeout(() => {\n modalStack.destroy(id);\n }, 200);\n };\n\n const footer = isConfirm ? (\n <div className=\"grid w-full grid-cols-5 gap-x-2\">\n <Button\n variant=\"outlined\"\n className=\"col-span-2\"\n onClick={() => closeModal(onCancel)}\n >\n {resolvedCancelText}\n </Button>\n <Button className=\"col-span-3\" onClick={() => closeModal(onOk)}>\n {resolvedOkText}\n </Button>\n </div>\n ) : (\n <Button\n className={cn(\n 'grow',\n //\n )}\n onClick={() => closeModal(onOk)}\n >\n {resolvedOkText}\n </Button>\n );\n\n if (!isBrowser) {\n return null;\n }\n\n return createPortal(\n <Modal\n open={open}\n footer={footer}\n className={cn(\n 'z-10000',\n 'box-content w-100',\n 'rounded-3xl p-4',\n 'gap-y-0',\n )}\n classNames={{\n mask: 'z-10000 bg-black/[.6]',\n body: cn(\n 'text-black-70 whitespace-pre-wrap text-center',\n content && 'mt-3',\n ),\n footer: 'mt-9',\n }}\n title={\n <p\n className={cn(\n 'text-center text-lg leading-normal whitespace-pre-wrap',\n 'flex items-center justify-center gap-x-2',\n //\n )}\n >\n {icon ||\n (isConfirm ? STATIC_ICONS.confirm : status && STATIC_ICONS[status])}\n {title}\n </p>\n }\n container={container}\n onCancel={() => closeModal(onCancel)}\n {...props}\n >\n {content}\n </Modal>,\n modalStack.getRootElement(container)!,\n );\n};\n\n// No `role`/`aria-modal` here — this element is only a mount point for the\n// imperative stack, not the dialog itself. Radix already renders its own\n// `role=\"dialog\"`/`aria-modal=\"true\"` on the actual `DialogContent` via\n// portal once a modal is open. Setting it here as well would (a) leave\n// `aria-modal=\"true\"` on the page permanently, even with zero modals open,\n// hiding the rest of the document from screen readers, and (b) duplicate\n// Radix's own dialog role once one is open.\nconst modalStack: ImperativeStack<StaticProps> =\n createImperativeStack<StaticProps>({\n createRootElement: () => {\n const el = document.createElement('div');\n el.style.zIndex = '10000';\n el.style.position = 'absolute';\n return el;\n },\n StackItem: StaticModal,\n });\n\nModal.destroy = modalStack.destroy;\n\nModal.destroyAll = () => {\n modalStack.destroy();\n};\n\nModal.info = (props: StaticProps) =>\n modalStack.render({ status: 'info', ...props });\nModal.success = (props: StaticProps) =>\n modalStack.render({ status: 'success', ...props });\nModal.error = (props: StaticProps) =>\n modalStack.render({ status: 'error', ...props });\nModal.warning = (props: StaticProps) =>\n modalStack.render({ status: 'warning', ...props });\n// `confirm` is a mode, not a status, so it rides its own `mode` axis rather\n// than `status`.\nModal.confirm = (props: StaticProps) =>\n modalStack.render({ mode: 'confirm', ...props });\n\nexport default Modal;\n"],"mappings":"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"}
|
|
@@ -2,12 +2,6 @@
|
|
|
2
2
|
type ToastStatus = 'info' | 'success' | 'error' | 'warning';
|
|
3
3
|
interface Props {
|
|
4
4
|
status?: ToastStatus;
|
|
5
|
-
/**
|
|
6
|
-
* @deprecated Use `status`. Kept as an alias so existing
|
|
7
|
-
* `type="info|success|error|warning"` usage keeps working; `status` matches
|
|
8
|
-
* the shared semantic-state vocabulary used by `Result`.
|
|
9
|
-
*/
|
|
10
|
-
type?: ToastStatus;
|
|
11
5
|
title: React.ReactNode;
|
|
12
6
|
description?: React.ReactNode;
|
|
13
7
|
icon?: React.ReactNode;
|
|
@@ -22,7 +16,7 @@ interface StaticProps extends Props {
|
|
|
22
16
|
container?: HTMLElement;
|
|
23
17
|
}
|
|
24
18
|
declare const Toast: {
|
|
25
|
-
({ status,
|
|
19
|
+
({ status, title, description, icon, closable, action, className, onClose }: Props): import("react").JSX.Element;
|
|
26
20
|
destroy: (id?: string) => void;
|
|
27
21
|
destroyAll(): void;
|
|
28
22
|
info(title: React.ReactNode, props?: TriggerOptions): string | undefined;
|
|
@@ -30,7 +24,7 @@ declare const Toast: {
|
|
|
30
24
|
error(title: React.ReactNode, props?: TriggerOptions): string | undefined;
|
|
31
25
|
warning(title: React.ReactNode, props?: TriggerOptions): string | undefined;
|
|
32
26
|
};
|
|
33
|
-
type TriggerOptions = Omit<StaticProps, 'status' | '
|
|
27
|
+
type TriggerOptions = Omit<StaticProps, 'status' | 'title'>;
|
|
34
28
|
//#endregion
|
|
35
29
|
export { Props, type StaticProps as ToastOptions, Toast as default };
|
|
36
30
|
//# sourceMappingURL=toast.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toast.d.mts","names":[],"sources":["../../../src/components/organisms/toast.tsx"],"mappings":";KAkBK;UAEY;EACf,SAAS
|
|
1
|
+
{"version":3,"file":"toast.d.mts","names":[],"sources":["../../../src/components/organisms/toast.tsx"],"mappings":";KAkBK;UAEY;EACf,SAAS;EACT,OAAO,MAAM;EACb,cAAc,MAAM;EACpB,OAAO,MAAM;EACb;EACA,SAAS,MAAM;EACf;EACA;;UAGQ,oBAAoB;EAC5B;EACA;EACA,YAAY;;cAUR;KASH,QAAA,OAAA,aAAA,MAAA,UAAA,QAAA,WAAA,WAAA,wBAAK,IAAA;;;EAoHa,KAAA,OAAA,MAAM,WAAS,QAAU;EAEtB,QAAA,OAAA,MAAM,WAAS,QAAU;EAE3B,MAAA,OAAA,MAAM,WAAS,QAAU;EAEvB,QAAA,OAAA,MAAM,WAAS,QAAU;;KAR5C,iBAAiB,KAAK"}
|
|
@@ -15,16 +15,15 @@ const STATUS_ICONS = {
|
|
|
15
15
|
error: /* @__PURE__ */ jsx(OctagonX, { className: "text-red-400" }),
|
|
16
16
|
warning: /* @__PURE__ */ jsx(OctagonAlert, { className: "text-yellow-400" })
|
|
17
17
|
};
|
|
18
|
-
const Toast = ({ status,
|
|
18
|
+
const Toast = ({ status, title, description, icon, closable = true, action, className, onClose }) => {
|
|
19
19
|
const { locale } = useConfig();
|
|
20
|
-
const resolvedStatus = status ?? type;
|
|
21
20
|
return /* @__PURE__ */ jsxs("div", {
|
|
22
|
-
role:
|
|
21
|
+
role: status === "error" ? "alert" : "status",
|
|
23
22
|
className: cn("pointer-events-auto flex w-80 items-start gap-3 rounded-lg", "bg-background text-foreground border p-4 shadow-lg", className),
|
|
24
23
|
children: [
|
|
25
|
-
(icon ||
|
|
24
|
+
(icon || status) && /* @__PURE__ */ jsx("div", {
|
|
26
25
|
className: "mt-0.5 shrink-0 [&_svg]:size-5",
|
|
27
|
-
children: icon ||
|
|
26
|
+
children: icon || status && STATUS_ICONS[status]
|
|
28
27
|
}),
|
|
29
28
|
/* @__PURE__ */ jsxs("div", {
|
|
30
29
|
className: "flex-1 space-y-1",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toast.mjs","names":[],"sources":["../../../src/components/organisms/toast.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { createPortal } from 'react-dom';\n\nimport { useTimeout } from '@jbpark/use-hooks';\nimport { Check, Info, OctagonAlert, OctagonX, X } from 'lucide-react';\n\nimport { DEFAULT_LOCALE, useConfig } from '@repo/ui/providers';\nimport { cn } from '@repo/ui/utils';\n\nimport Button from '../atoms/button';\nimport {\n type ImperativeStack,\n createImperativeStack,\n isBrowser,\n} from './imperative-stack';\n\ntype ToastStatus = 'info' | 'success' | 'error' | 'warning';\n\nexport interface Props {\n status?: ToastStatus;\n
|
|
1
|
+
{"version":3,"file":"toast.mjs","names":[],"sources":["../../../src/components/organisms/toast.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { createPortal } from 'react-dom';\n\nimport { useTimeout } from '@jbpark/use-hooks';\nimport { Check, Info, OctagonAlert, OctagonX, X } from 'lucide-react';\n\nimport { DEFAULT_LOCALE, useConfig } from '@repo/ui/providers';\nimport { cn } from '@repo/ui/utils';\n\nimport Button from '../atoms/button';\nimport {\n type ImperativeStack,\n createImperativeStack,\n isBrowser,\n} from './imperative-stack';\n\ntype ToastStatus = 'info' | 'success' | 'error' | 'warning';\n\nexport interface Props {\n status?: ToastStatus;\n title: React.ReactNode;\n description?: React.ReactNode;\n icon?: React.ReactNode;\n closable?: boolean;\n action?: React.ReactNode;\n className?: string;\n onClose?: () => void;\n}\n\ninterface StaticProps extends Props {\n id?: string;\n duration?: number;\n container?: HTMLElement;\n}\n\nconst STATUS_ICONS: Record<ToastStatus, React.ReactNode> = {\n info: <Info className=\"text-blue-400\" />,\n success: <Check className=\"text-green-400\" />,\n error: <OctagonX className=\"text-red-400\" />,\n warning: <OctagonAlert className=\"text-yellow-400\" />,\n};\n\nconst Toast = ({\n status,\n title,\n description,\n icon,\n closable = true,\n action,\n className,\n onClose,\n}: Props) => {\n const { locale } = useConfig();\n\n return (\n <div\n role={status === 'error' ? 'alert' : 'status'}\n className={cn(\n 'pointer-events-auto flex w-80 items-start gap-3 rounded-lg',\n 'bg-background text-foreground border p-4 shadow-lg',\n className,\n //\n )}\n >\n {(icon || status) && (\n <div className=\"mt-0.5 shrink-0 [&_svg]:size-5\">\n {icon || (status && STATUS_ICONS[status])}\n </div>\n )}\n <div className=\"flex-1 space-y-1\">\n <p className=\"text-sm font-medium\">{title}</p>\n {description && (\n <p className=\"text-muted-foreground text-sm\">{description}</p>\n )}\n {action && <div className=\"pt-1\">{action}</div>}\n </div>\n {closable && (\n <Button\n variant=\"text\"\n shape=\"circle\"\n size=\"small\"\n aria-label={locale.close ?? DEFAULT_LOCALE.close}\n icon={<X />}\n onClick={onClose}\n />\n )}\n </div>\n );\n};\n\nconst DEFAULT_DURATION = 4000;\n\nconst StaticToast = ({\n id,\n duration = DEFAULT_DURATION,\n container,\n onClose,\n ...props\n}: StaticProps & { id: string }): React.ReactPortal | null => {\n const [visible, setVisible] = useState(true);\n\n const close = () => {\n onClose?.();\n setVisible(false);\n setTimeout(() => {\n toastStack.destroy(id);\n }, 200);\n };\n\n const { reset, clear } = useTimeout(close, duration || null);\n\n if (!isBrowser) {\n return null;\n }\n\n return createPortal(\n <div\n className={cn(\n 'transition-all duration-200',\n visible ? 'opacity-100' : 'translate-x-2 opacity-0',\n )}\n // Without this, a toast with a closable/action button (or one the\n // user is mid-read on) could auto-dismiss itself out from under\n // them — hovering or focusing anything inside pauses the timer,\n // moving away restarts it from the full duration.\n onMouseEnter={clear}\n onMouseLeave={reset}\n onFocus={clear}\n onBlur={reset}\n >\n <Toast {...props} onClose={close} />\n </div>,\n toastStack.getRootElement(container)!,\n );\n};\n\nconst toastStack: ImperativeStack<StaticProps> =\n createImperativeStack<StaticProps>({\n createRootElement: () => {\n const el = document.createElement('div');\n el.setAttribute('role', 'region');\n // Plain DOM, created outside any React tree — no Config to read a\n // locale override from until #226 gives the imperative stack a way\n // to inherit one.\n el.setAttribute('aria-label', 'Notifications');\n el.style.zIndex = '10000';\n el.style.position = 'fixed';\n el.style.right = '0';\n el.style.bottom = '0';\n el.style.display = 'flex';\n el.style.flexDirection = 'column-reverse';\n el.style.gap = '8px';\n el.style.padding = '16px';\n el.style.pointerEvents = 'none';\n return el;\n },\n StackItem: StaticToast,\n });\n\nToast.destroy = toastStack.destroy;\n\nToast.destroyAll = () => {\n toastStack.destroy();\n};\n\ntype TriggerOptions = Omit<StaticProps, 'status' | 'title'>;\n\nToast.info = (title: React.ReactNode, props?: TriggerOptions) =>\n toastStack.render({ status: 'info', title, ...props });\nToast.success = (title: React.ReactNode, props?: TriggerOptions) =>\n toastStack.render({ status: 'success', title, ...props });\nToast.error = (title: React.ReactNode, props?: TriggerOptions) =>\n toastStack.render({ status: 'error', title, ...props });\nToast.warning = (title: React.ReactNode, props?: TriggerOptions) =>\n toastStack.render({ status: 'warning', title, ...props });\n\nexport default Toast;\nexport type { StaticProps as ToastOptions };\n"],"mappings":";;;;;;;;;;;AAqCA,MAAM,eAAqD;CACzD,MAAM,oBAAC,MAAD,EAAM,WAAU,gBAAiB,CAAA;CACvC,SAAS,oBAAC,OAAD,EAAO,WAAU,iBAAkB,CAAA;CAC5C,OAAO,oBAAC,UAAD,EAAU,WAAU,eAAgB,CAAA;CAC3C,SAAS,oBAAC,cAAD,EAAc,WAAU,kBAAmB,CAAA;AACtD;AAEA,MAAM,SAAS,EACb,QACA,OACA,aACA,MACA,WAAW,MACX,QACA,WACA,cACW;CACX,MAAM,EAAE,WAAW,UAAU;CAE7B,OACE,qBAAC,OAAD;EACE,MAAM,WAAW,UAAU,UAAU;EACrC,WAAW,GACT,8DACA,sDACA,SAEF;EAPF,UAAA;IASI,QAAQ,WACR,oBAAC,OAAD;IAAK,WAAU;IACZ,UAAA,QAAS,UAAU,aAAa;GAC9B,CAAA;GAEP,qBAAC,OAAD;IAAK,WAAU;IAAf,UAAA;KACE,oBAAC,KAAD;MAAG,WAAU;MAAuB,UAAA;KAAS,CAAA;KAC5C,eACC,oBAAC,KAAD;MAAG,WAAU;MAAiC,UAAA;KAAe,CAAA;KAE9D,UAAU,oBAAC,OAAD;MAAK,WAAU;MAAQ,UAAA;KAAY,CAAA;IAC3C;;GACJ,YACC,oBAAC,QAAD;IACE,SAAQ;IACR,OAAM;IACN,MAAK;IACL,cAAY,OAAO,SAAS,eAAe;IAC3C,MAAM,oBAAC,GAAD,CAAI,CAAA;IACV,SAAS;GACV,CAAA;EAEA;;AAET;AAEA,MAAM,mBAAmB;AAEzB,MAAM,eAAe,EACnB,IACA,WAAW,kBACX,WACA,SACA,GAAG,YACyD;CAC5D,MAAM,CAAC,SAAS,cAAc,SAAS,IAAI;CAE3C,MAAM,cAAc;EAClB,UAAU;EACV,WAAW,KAAK;EAChB,iBAAiB;GACf,WAAW,QAAQ,EAAE;EACvB,GAAG,GAAG;CACR;CAEA,MAAM,EAAE,OAAO,UAAU,WAAW,OAAO,YAAY,IAAI;CAE3D,IAAI,CAAC,WACH,OAAO;CAGT,OAAO,aACL,oBAAC,OAAD;EACE,WAAW,GACT,+BACA,UAAU,gBAAgB,yBAC5B;EAKA,cAAc;EACd,cAAc;EACd,SAAS;EACT,QAAQ;EAER,UAAA,oBAAC,OAAD;GAAO,GAAI;GAAO,SAAS;EAAQ,CAAA;CAChC,CAAA,GACL,WAAW,eAAe,SAAS,CACrC;AACF;AAEA,MAAM,aACJ,sBAAmC;CACjC,yBAAyB;EACvB,MAAM,KAAK,SAAS,cAAc,KAAK;EACvC,GAAG,aAAa,QAAQ,QAAQ;EAIhC,GAAG,aAAa,cAAc,eAAe;EAC7C,GAAG,MAAM,SAAS;EAClB,GAAG,MAAM,WAAW;EACpB,GAAG,MAAM,QAAQ;EACjB,GAAG,MAAM,SAAS;EAClB,GAAG,MAAM,UAAU;EACnB,GAAG,MAAM,gBAAgB;EACzB,GAAG,MAAM,MAAM;EACf,GAAG,MAAM,UAAU;EACnB,GAAG,MAAM,gBAAgB;EACzB,OAAO;CACT;CACA,WAAW;AACb,CAAC;AAEH,MAAM,UAAU,WAAW;AAE3B,MAAM,mBAAmB;CACvB,WAAW,QAAQ;AACrB;AAIA,MAAM,QAAQ,OAAwB,UACpC,WAAW,OAAO;CAAE,QAAQ;CAAQ;CAAO,GAAG;AAAM,CAAC;AACvD,MAAM,WAAW,OAAwB,UACvC,WAAW,OAAO;CAAE,QAAQ;CAAW;CAAO,GAAG;AAAM,CAAC;AAC1D,MAAM,SAAS,OAAwB,UACrC,WAAW,OAAO;CAAE,QAAQ;CAAS;CAAO,GAAG;AAAM,CAAC;AACxD,MAAM,WAAW,OAAwB,UACvC,WAAW,OAAO;CAAE,QAAQ;CAAW;CAAO,GAAG;AAAM,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"header.d.mts","names":[],"sources":["../../../../src/components/templates/layout/header.tsx"],"mappings":";
|
|
1
|
+
{"version":3,"file":"header.d.mts","names":[],"sources":["../../../../src/components/templates/layout/header.tsx"],"mappings":";UAIiB,cAAc,MAAM;EACnC;;cAGI,WAAU,UAAA,WAAA,aAAA,SAKb,0BAAK,IAAA"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { cn } from "../../../utils.mjs";
|
|
2
|
+
import "../../../lib/z-layers.mjs";
|
|
2
3
|
import { jsx } from "react/jsx-runtime";
|
|
3
4
|
//#region src/components/templates/layout/header.tsx
|
|
4
5
|
const Header = ({ children, className, position = "sticky", ...props }) => {
|
|
5
6
|
return /* @__PURE__ */ jsx("header", {
|
|
6
|
-
className: cn(position === "sticky" && "sticky top-0
|
|
7
|
+
className: cn(position !== "static" && "z-1000", position === "sticky" && "sticky top-0", position === "fixed" && "fixed inset-x-0 top-0", "bg-background", "flex h-16 w-full items-center px-5", className),
|
|
7
8
|
...props,
|
|
8
9
|
children
|
|
9
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.ComponentProps<'header'> {\n position?: 'sticky' | 'static' | 'fixed';\n}\n\nconst Header = ({\n children,\n className,\n position = 'sticky',\n ...props\n}: Props) => {\n return (\n <header\n className={cn(\n position === 'sticky' && 'sticky top-0
|
|
1
|
+
{"version":3,"file":"header.mjs","names":[],"sources":["../../../../src/components/templates/layout/header.tsx"],"sourcesContent":["import { cn } from '@repo/ui/utils';\n\nimport { OVERLAY_LAYER } from '../../../lib/z-layers';\n\nexport interface Props extends React.ComponentProps<'header'> {\n position?: 'sticky' | 'static' | 'fixed';\n}\n\nconst Header = ({\n children,\n className,\n position = 'sticky',\n ...props\n}: Props) => {\n return (\n <header\n className={cn(\n position !== 'static' && OVERLAY_LAYER,\n position === 'sticky' && 'sticky top-0',\n position === 'fixed' && 'fixed inset-x-0 top-0',\n 'bg-background',\n 'flex h-16 w-full items-center px-5',\n className,\n //\n )}\n {...props}\n >\n {children}\n </header>\n );\n};\n\nexport default Header;\n"],"mappings":";;;;AAQA,MAAM,UAAU,EACd,UACA,WACA,WAAW,UACX,GAAG,YACQ;CACX,OACE,oBAAC,UAAD;EACE,WAAW,GACT,aAAa,YAAA,UACb,aAAa,YAAY,gBACzB,aAAa,WAAW,yBACxB,iBACA,sCACA,SAEF;EACA,GAAI;EAEH;CACK,CAAA;AAEZ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sider.mjs","names":[],"sources":["../../../../src/components/templates/layout/sider.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useId, useRef } from 'react';\n\nimport { useControllableState, useResponsiveSize } from '@jbpark/use-hooks';\nimport { PanelLeftClose, PanelLeftOpen } from 'lucide-react';\n\nimport { DEFAULT_LOCALE, useConfig } from '@repo/ui/providers';\nimport { cn } from '@repo/ui/utils';\n\nimport { useRegisterSider } from './sider-context';\n\ntype Breakpoint = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';\n\n// Order matters here, not the values — used to compare the current\n// viewport tier against the configured `breakpoint` ordinally, since\n// `useResponsiveSize` reports which named tier the viewport is currently\n// in rather than a raw pixel width.\nconst BREAKPOINT_ORDER: Breakpoint[] = ['xs', 'sm', 'md', 'lg', 'xl', '2xl'];\n\nexport interface Props extends Omit<\n React.ComponentProps<'aside'>,\n 'onCollapse'\n> {\n width?: number | string;\n collapsedWidth?: number | string;\n collapsible?: boolean;\n defaultCollapsed?: boolean;\n collapsed?: boolean;\n placement?: 'left' | 'right';\n reverseArrow?: boolean;\n trigger?: React.ReactNode;\n classNames?: {\n trigger?: string;\n };\n breakpoint?: Breakpoint;\n onCollapse?: (collapsed: boolean) => void;\n onBreakpoint?: (broken: boolean) => void;\n}\n\nconst toCssSize = (value: number | string) =>\n typeof value === 'number' ? `${value}px` : value;\n\nconst Sider = ({\n children,\n className,\n classNames,\n style,\n width = 200,\n collapsedWidth = 80,\n collapsible = false,\n defaultCollapsed = false,\n collapsed: _collapsed,\n placement = 'left',\n reverseArrow = false,\n trigger,\n breakpoint,\n onCollapse,\n onBreakpoint,\n ...props\n}: Props) => {\n useRegisterSider();\n\n const { locale } = useConfig();\n const contentId = useId();\n const [collapsed, setCollapsed] = useControllableState<boolean>({\n value: _collapsed,\n defaultValue: defaultCollapsed,\n onChange: onCollapse,\n });\n\n const { breakpoint: viewportBreakpoint } = useResponsiveSize({\n viewport: true,\n });\n const broken = breakpoint\n ? BREAKPOINT_ORDER.indexOf(viewportBreakpoint.current) <\n BREAKPOINT_ORDER.indexOf(breakpoint)\n : false;\n\n const latestBrokenRef = useRef(broken);\n const previousBrokenRef = useRef<boolean | null>(null);\n const microtaskScheduledRef = useRef(false);\n const onBreakpointRef = useRef(onBreakpoint);\n\n useEffect(() => {\n onBreakpointRef.current = onBreakpoint;\n });\n\n // `useResponsiveSize`'s own initial measurement resolves through its own\n // layout effect, which (since it's called earlier in this component)\n // commits and — if the real viewport differs from its SSR-safe 'xs'\n // placeholder default — triggers a synchronous corrective re-render, all\n // before the browser paints. Both the placeholder-derived render and the\n // corrected one get their own effect pass (React doesn't coalesce\n // same-tick synchronous re-commits into a single effect flush), so\n // reacting to `broken` directly — in a layout or passive effect either\n // way — fires once with the wrong placeholder value, then again with the\n // corrected one.\n //\n // Coalescing through a microtask sidesteps that: every effect pass just\n // records its `broken` into a ref and schedules (at most once) a\n // microtask that reads whatever ended up latest once the synchronous\n // cascade has fully settled, and acts on that single, correct value.\n //\n // The `previousBrokenRef` guard is the separate fix for #227 point 3:\n // without it, an inline `onCollapse`/`onBreakpoint` (a fresh identity\n // every render) would change `setCollapsed`'s own identity and re-run\n // this effect on every unrelated parent re-render, reapplying `broken`\n // and undoing a user's manual toggle. Only a genuine change should act.\n useEffect(() => {\n if (!breakpoint) {\n return;\n }\n\n latestBrokenRef.current = broken;\n\n if (microtaskScheduledRef.current) {\n return;\n }\n microtaskScheduledRef.current = true;\n\n queueMicrotask(() => {\n microtaskScheduledRef.current = false;\n\n const settledBroken = latestBrokenRef.current;\n\n if (previousBrokenRef.current === settledBroken) {\n return;\n }\n\n previousBrokenRef.current = settledBroken;\n setCollapsed(settledBroken);\n onBreakpointRef.current?.(settledBroken);\n });\n }, [broken, breakpoint, setCollapsed]);\n\n const onTriggerClick = () => {\n setCollapsed(!collapsed);\n };\n\n // A right-placed Sider's collapse icon should default to pointing the\n // opposite way from a left-placed one (it's opening/closing a panel on\n // the other side of the content), so `placement` flips the effective\n // direction on top of whatever `reverseArrow` already requests —\n // `reverseArrow` still layers on as a manual override either way.\n const effectiveReverseArrow = reverseArrow !== (placement === 'right');\n const TriggerIcon =\n collapsed !== effectiveReverseArrow ? PanelLeftOpen : PanelLeftClose;\n\n return (\n <aside\n className={cn(\n // Below Header
|
|
1
|
+
{"version":3,"file":"sider.mjs","names":[],"sources":["../../../../src/components/templates/layout/sider.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useId, useRef } from 'react';\n\nimport { useControllableState, useResponsiveSize } from '@jbpark/use-hooks';\nimport { PanelLeftClose, PanelLeftOpen } from 'lucide-react';\n\nimport { DEFAULT_LOCALE, useConfig } from '@repo/ui/providers';\nimport { cn } from '@repo/ui/utils';\n\nimport { useRegisterSider } from './sider-context';\n\ntype Breakpoint = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';\n\n// Order matters here, not the values — used to compare the current\n// viewport tier against the configured `breakpoint` ordinally, since\n// `useResponsiveSize` reports which named tier the viewport is currently\n// in rather than a raw pixel width.\nconst BREAKPOINT_ORDER: Breakpoint[] = ['xs', 'sm', 'md', 'lg', 'xl', '2xl'];\n\nexport interface Props extends Omit<\n React.ComponentProps<'aside'>,\n 'onCollapse'\n> {\n width?: number | string;\n collapsedWidth?: number | string;\n collapsible?: boolean;\n defaultCollapsed?: boolean;\n collapsed?: boolean;\n placement?: 'left' | 'right';\n reverseArrow?: boolean;\n trigger?: React.ReactNode;\n classNames?: {\n trigger?: string;\n };\n breakpoint?: Breakpoint;\n onCollapse?: (collapsed: boolean) => void;\n onBreakpoint?: (broken: boolean) => void;\n}\n\nconst toCssSize = (value: number | string) =>\n typeof value === 'number' ? `${value}px` : value;\n\nconst Sider = ({\n children,\n className,\n classNames,\n style,\n width = 200,\n collapsedWidth = 80,\n collapsible = false,\n defaultCollapsed = false,\n collapsed: _collapsed,\n placement = 'left',\n reverseArrow = false,\n trigger,\n breakpoint,\n onCollapse,\n onBreakpoint,\n ...props\n}: Props) => {\n useRegisterSider();\n\n const { locale } = useConfig();\n const contentId = useId();\n const [collapsed, setCollapsed] = useControllableState<boolean>({\n value: _collapsed,\n defaultValue: defaultCollapsed,\n onChange: onCollapse,\n });\n\n const { breakpoint: viewportBreakpoint } = useResponsiveSize({\n viewport: true,\n });\n const broken = breakpoint\n ? BREAKPOINT_ORDER.indexOf(viewportBreakpoint.current) <\n BREAKPOINT_ORDER.indexOf(breakpoint)\n : false;\n\n const latestBrokenRef = useRef(broken);\n const previousBrokenRef = useRef<boolean | null>(null);\n const microtaskScheduledRef = useRef(false);\n const onBreakpointRef = useRef(onBreakpoint);\n\n useEffect(() => {\n onBreakpointRef.current = onBreakpoint;\n });\n\n // `useResponsiveSize`'s own initial measurement resolves through its own\n // layout effect, which (since it's called earlier in this component)\n // commits and — if the real viewport differs from its SSR-safe 'xs'\n // placeholder default — triggers a synchronous corrective re-render, all\n // before the browser paints. Both the placeholder-derived render and the\n // corrected one get their own effect pass (React doesn't coalesce\n // same-tick synchronous re-commits into a single effect flush), so\n // reacting to `broken` directly — in a layout or passive effect either\n // way — fires once with the wrong placeholder value, then again with the\n // corrected one.\n //\n // Coalescing through a microtask sidesteps that: every effect pass just\n // records its `broken` into a ref and schedules (at most once) a\n // microtask that reads whatever ended up latest once the synchronous\n // cascade has fully settled, and acts on that single, correct value.\n //\n // The `previousBrokenRef` guard is the separate fix for #227 point 3:\n // without it, an inline `onCollapse`/`onBreakpoint` (a fresh identity\n // every render) would change `setCollapsed`'s own identity and re-run\n // this effect on every unrelated parent re-render, reapplying `broken`\n // and undoing a user's manual toggle. Only a genuine change should act.\n useEffect(() => {\n if (!breakpoint) {\n return;\n }\n\n latestBrokenRef.current = broken;\n\n if (microtaskScheduledRef.current) {\n return;\n }\n microtaskScheduledRef.current = true;\n\n queueMicrotask(() => {\n microtaskScheduledRef.current = false;\n\n const settledBroken = latestBrokenRef.current;\n\n if (previousBrokenRef.current === settledBroken) {\n return;\n }\n\n previousBrokenRef.current = settledBroken;\n setCollapsed(settledBroken);\n onBreakpointRef.current?.(settledBroken);\n });\n }, [broken, breakpoint, setCollapsed]);\n\n const onTriggerClick = () => {\n setCollapsed(!collapsed);\n };\n\n // A right-placed Sider's collapse icon should default to pointing the\n // opposite way from a left-placed one (it's opening/closing a panel on\n // the other side of the content), so `placement` flips the effective\n // direction on top of whatever `reverseArrow` already requests —\n // `reverseArrow` still layers on as a manual override either way.\n const effectiveReverseArrow = reverseArrow !== (placement === 'right');\n const TriggerIcon =\n collapsed !== effectiveReverseArrow ? PanelLeftOpen : PanelLeftClose;\n\n return (\n <aside\n className={cn(\n // Below Header — and below every other floating/overlay primitive\n // in this library, which all share OVERLAY_LAYER (lib/z-layers.ts).\n // Sider is a static layout column, not floating chrome, and\n // previously outranked Header with an arbitrary z-100, covering it\n // once Header stuck to the viewport top on scroll. Kept as a small\n // absolute value rather than tracking OVERLAY_LAYER: it only has to\n // stay under that layer, and raising it in step would defeat this.\n 'z-10 flex h-full shrink-0 flex-col overflow-hidden',\n 'transition-[width] duration-200',\n // Lets a Sider visually sit on the right without needing to\n // reorder JSX/children — useful since Sider's presence is\n // detected via context (sider-context.ts) rather than requiring\n // it to be a specific direct child in a specific position.\n placement === 'right' && 'order-last',\n className,\n //\n )}\n style={{\n width: toCssSize(collapsed ? collapsedWidth : width),\n ...style,\n }}\n {...props}\n >\n <div id={contentId} className=\"min-h-0 flex-1 overflow-auto\">\n {children}\n </div>\n {collapsible && trigger !== null && (\n <button\n type=\"button\"\n aria-label={\n collapsed\n ? (locale.expand ?? DEFAULT_LOCALE.expand)\n : (locale.collapse ?? DEFAULT_LOCALE.collapse)\n }\n aria-expanded={!collapsed}\n aria-controls={contentId}\n onClick={onTriggerClick}\n className={cn(\n 'flex w-full shrink-0 items-center justify-center py-2',\n 'cursor-pointer transition-colors hover:bg-black/5',\n classNames?.trigger,\n //\n )}\n >\n {trigger === undefined ? <TriggerIcon className=\"size-4\" /> : trigger}\n </button>\n )}\n </aside>\n );\n};\n\nexport default Sider;\n"],"mappings":";;;;;;;;;AAkBA,MAAM,mBAAiC;CAAC;CAAM;CAAM;CAAM;CAAM;CAAM;AAAK;AAsB3E,MAAM,aAAa,UACjB,OAAO,UAAU,WAAW,GAAG,MAAM,MAAM;AAE7C,MAAM,SAAS,EACb,UACA,WACA,YACA,OACA,QAAQ,KACR,iBAAiB,IACjB,cAAc,OACd,mBAAmB,OACnB,WAAW,YACX,YAAY,QACZ,eAAe,OACf,SACA,YACA,YACA,cACA,GAAG,YACQ;CACX,iBAAiB;CAEjB,MAAM,EAAE,WAAW,UAAU;CAC7B,MAAM,YAAY,MAAM;CACxB,MAAM,CAAC,WAAW,gBAAgB,qBAA8B;EAC9D,OAAO;EACP,cAAc;EACd,UAAU;CACZ,CAAC;CAED,MAAM,EAAE,YAAY,uBAAuB,kBAAkB,EAC3D,UAAU,KACZ,CAAC;CACD,MAAM,SAAS,aACX,iBAAiB,QAAQ,mBAAmB,OAAO,IACnD,iBAAiB,QAAQ,UAAU,IACnC;CAEJ,MAAM,kBAAkB,OAAO,MAAM;CACrC,MAAM,oBAAoB,OAAuB,IAAI;CACrD,MAAM,wBAAwB,OAAO,KAAK;CAC1C,MAAM,kBAAkB,OAAO,YAAY;CAE3C,gBAAgB;EACd,gBAAgB,UAAU;CAC5B,CAAC;CAuBD,gBAAgB;EACd,IAAI,CAAC,YACH;EAGF,gBAAgB,UAAU;EAE1B,IAAI,sBAAsB,SACxB;EAEF,sBAAsB,UAAU;EAEhC,qBAAqB;GACnB,sBAAsB,UAAU;GAEhC,MAAM,gBAAgB,gBAAgB;GAEtC,IAAI,kBAAkB,YAAY,eAChC;GAGF,kBAAkB,UAAU;GAC5B,aAAa,aAAa;GAC1B,gBAAgB,UAAU,aAAa;EACzC,CAAC;CACH,GAAG;EAAC;EAAQ;EAAY;CAAY,CAAC;CAErC,MAAM,uBAAuB;EAC3B,aAAa,CAAC,SAAS;CACzB;CAQA,MAAM,cACJ,eAF4B,kBAAkB,cAAc,YAEtB,gBAAgB;CAExD,OACE,qBAAC,SAAD;EACE,WAAW,GAQT,sDACA,mCAKA,cAAc,WAAW,cACzB,SAEF;EACA,OAAO;GACL,OAAO,UAAU,YAAY,iBAAiB,KAAK;GACnD,GAAG;EACL;EACA,GAAI;EAvBN,UAAA,CAyBE,oBAAC,OAAD;GAAK,IAAI;GAAW,WAAU;GAC3B;EACE,CAAA,GACJ,eAAe,YAAY,QAC1B,oBAAC,UAAD;GACE,MAAK;GACL,cACE,YACK,OAAO,UAAU,eAAe,SAChC,OAAO,YAAY,eAAe;GAEzC,iBAAe,CAAC;GAChB,iBAAe;GACf,SAAS;GACT,WAAW,GACT,yDACA,qDACA,YAAY,OAEd;GAEC,UAAA,YAAY,KAAA,IAAY,oBAAC,aAAD,EAAa,WAAU,SAAU,CAAA,IAAI;EACxD,CAAA,CAEL;;AAEX"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { cn
|
|
2
|
+
import { cn } from "../../utils.mjs";
|
|
3
3
|
import { DEFAULT_LOCALE, useConfig } from "../../providers/config/context.mjs";
|
|
4
4
|
import Button from "../../Button.mjs";
|
|
5
5
|
import { ArrowLeft } from "lucide-react";
|
|
@@ -31,10 +31,10 @@ const PageHeader = ({ title, subTitle, extra, backIcon, onBack, className, class
|
|
|
31
31
|
className: cn("text-muted-foreground text-sm", classNames?.subTitle),
|
|
32
32
|
children: subTitle
|
|
33
33
|
})]
|
|
34
|
-
}),
|
|
34
|
+
}), extra != null && /* @__PURE__ */ jsx("div", {
|
|
35
35
|
className: cn("shrink-0", classNames?.extra),
|
|
36
|
-
children:
|
|
37
|
-
})
|
|
36
|
+
children: extra
|
|
37
|
+
})]
|
|
38
38
|
})]
|
|
39
39
|
}), children]
|
|
40
40
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"page-header.mjs","names":[],"sources":["../../../src/components/templates/page-header.tsx"],"sourcesContent":["'use client';\n\nimport { ArrowLeft } from 'lucide-react';\n\nimport { DEFAULT_LOCALE, useConfig } from '@repo/ui/providers';\nimport { cn
|
|
1
|
+
{"version":3,"file":"page-header.mjs","names":[],"sources":["../../../src/components/templates/page-header.tsx"],"sourcesContent":["'use client';\n\nimport { ArrowLeft } from 'lucide-react';\n\nimport { DEFAULT_LOCALE, useConfig } from '@repo/ui/providers';\nimport { cn } from '@repo/ui/utils';\n\nimport Button from '../atoms/button';\n\nexport interface Props extends Omit<React.ComponentProps<'div'>, 'title'> {\n title?: React.ReactNode;\n subTitle?: React.ReactNode;\n extra?: React.ReactNode;\n backIcon?: React.ReactNode;\n onBack?: () => void;\n classNames?: {\n back?: string;\n title?: string;\n subTitle?: string;\n extra?: string;\n };\n}\n\n// Title + back button + extra action area — same shape as Drawer's header\n// (title/extra/closable), which this borrows the layout from wholesale;\n// PageHeader just swaps Drawer's close button for a back button.\nconst PageHeader = ({\n title,\n subTitle,\n extra,\n backIcon,\n onBack,\n className,\n classNames,\n children,\n ...props\n}: Props) => {\n const { locale } = useConfig();\n\n return (\n <div className={cn('flex flex-col gap-2', className)} {...props}>\n <div className=\"flex items-start gap-2\">\n {onBack && (\n <Button\n variant=\"text\"\n shape=\"circle\"\n size=\"small\"\n aria-label={locale.back ?? DEFAULT_LOCALE.back}\n icon={backIcon || <ArrowLeft />}\n onClick={onBack}\n className={cn(classNames?.back)}\n />\n )}\n <div className=\"flex flex-1 items-start justify-between gap-2\">\n <div className=\"flex flex-col gap-0.5\">\n {title && (\n <p className={cn('text-lg font-medium', classNames?.title)}>\n {title}\n </p>\n )}\n {subTitle && (\n <p\n className={cn(\n 'text-muted-foreground text-sm',\n classNames?.subTitle,\n )}\n >\n {subTitle}\n </p>\n )}\n </div>\n {extra != null && (\n <div className={cn('shrink-0', classNames?.extra)}>{extra}</div>\n )}\n </div>\n </div>\n {children}\n </div>\n );\n};\n\nexport default PageHeader;\n"],"mappings":";;;;;;;AA0BA,MAAM,cAAc,EAClB,OACA,UACA,OACA,UACA,QACA,WACA,YACA,UACA,GAAG,YACQ;CACX,MAAM,EAAE,WAAW,UAAU;CAE7B,OACE,qBAAC,OAAD;EAAK,WAAW,GAAG,uBAAuB,SAAS;EAAG,GAAI;EAA1D,UAAA,CACE,qBAAC,OAAD;GAAK,WAAU;GAAf,UAAA,CACG,UACC,oBAAC,QAAD;IACE,SAAQ;IACR,OAAM;IACN,MAAK;IACL,cAAY,OAAO,QAAQ,eAAe;IAC1C,MAAM,YAAY,oBAAC,WAAD,CAAY,CAAA;IAC9B,SAAS;IACT,WAAW,GAAG,YAAY,IAAI;GAC/B,CAAA,GAEH,qBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,qBAAC,OAAD;KAAK,WAAU;KAAf,UAAA,CACG,SACC,oBAAC,KAAD;MAAG,WAAW,GAAG,uBAAuB,YAAY,KAAK;MACtD,UAAA;KACA,CAAA,GAEJ,YACC,oBAAC,KAAD;MACE,WAAW,GACT,iCACA,YAAY,QACd;MAEC,UAAA;KACA,CAAA,CAEF;IACJ,CAAA,GAAA,SAAS,QACR,oBAAC,OAAD;KAAK,WAAW,GAAG,YAAY,YAAY,KAAK;KAAI,UAAA;IAAW,CAAA,CAE9D;GACF,CAAA,CAAA;EACJ,CAAA,GAAA,QACE;;AAET"}
|
package/dist/core/badge.d.mts
CHANGED
|
@@ -1,2 +1,25 @@
|
|
|
1
|
-
import "react";
|
|
2
|
-
import "class-variance-authority";
|
|
1
|
+
import * as React$1 from "react";
|
|
2
|
+
import { VariantProps } from "class-variance-authority";
|
|
3
|
+
//#region src/core/badge.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Vendored from shadcn's `new-york-v4` badge registry entry.
|
|
6
|
+
*
|
|
7
|
+
* Two deliberate local adaptations, both of which must survive the next sync:
|
|
8
|
+
* - upstream imports `Slot` from the unified `radix-ui` package; this repo
|
|
9
|
+
* installs the individual `@radix-ui/*` packages (13 of them) and has no
|
|
10
|
+
* `radix-ui` dependency, so the import stays on `@radix-ui/react-slot`.
|
|
11
|
+
* - `cn` resolves through `@repo/ui/utils` rather than upstream's `cn` alias.
|
|
12
|
+
*
|
|
13
|
+
* Everything else is upstream verbatim, including the `rounded-full` /
|
|
14
|
+
* `border-transparent` base and the `ghost` / `link` variants.
|
|
15
|
+
*/
|
|
16
|
+
declare const badgeVariants: (props?: ({
|
|
17
|
+
variant?: "link" | "default" | "destructive" | "outline" | "secondary" | "ghost" | null | undefined;
|
|
18
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
19
|
+
type Props = React$1.ComponentProps<'span'> & VariantProps<typeof badgeVariants> & {
|
|
20
|
+
asChild?: boolean;
|
|
21
|
+
};
|
|
22
|
+
declare function Badge({ className, variant, asChild, ...props }: Props): React$1.JSX.Element;
|
|
23
|
+
//#endregion
|
|
24
|
+
export { Badge, type Props, badgeVariants };
|
|
25
|
+
//# sourceMappingURL=badge.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"badge.d.mts","names":[],"sources":["../../src/core/badge.tsx"],"mappings":";;;;;;;;;;;;;;;cAmBM,gBAAa;;6CA0BlB;KAEI,QAAQ,QAAM,yBACjB,oBAAoB;EAClB;;iBAGK,QACP,WACA,SACA,YACG,SACF,QAAK,QAAA,IAAA"}
|