@jbpark/ui-kit 6.0.1 → 6.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Button.d.mts +21 -22
- package/dist/Button.d.mts.map +1 -1
- package/dist/Button.mjs +33 -10
- package/dist/Button.mjs.map +1 -1
- package/dist/Space.d.mts.map +1 -1
- package/dist/Space.mjs +6 -2
- package/dist/Space.mjs.map +1 -1
- package/dist/Tag.d.mts +17 -3
- package/dist/Tag.d.mts.map +1 -1
- package/dist/Tag.mjs +14 -2
- package/dist/Tag.mjs.map +1 -1
- package/dist/components/atoms/checkbox/group.mjs +2 -2
- package/dist/components/atoms/checkbox/group.mjs.map +1 -1
- package/dist/components/atoms/index.d.mts +2 -1
- package/dist/components/atoms/radio/group.mjs +1 -1
- package/dist/components/atoms/radio/group.mjs.map +1 -1
- package/dist/components/atoms/slider/index.mjs +7 -0
- package/dist/components/atoms/slider/index.mjs.map +1 -0
- package/dist/components/atoms/slider/slider.d.mts +17 -0
- package/dist/components/atoms/slider/slider.d.mts.map +1 -0
- package/dist/components/atoms/slider/slider.mjs +36 -0
- package/dist/components/atoms/slider/slider.mjs.map +1 -0
- package/dist/components/molecules/menu/item/item.mjs +1 -1
- package/dist/components/molecules/menu/item/item.mjs.map +1 -1
- package/dist/components/molecules/upload.mjs +1 -1
- package/dist/components/molecules/upload.mjs.map +1 -1
- package/dist/components/organisms/modal.d.mts +9 -1
- package/dist/components/organisms/modal.d.mts.map +1 -1
- package/dist/components/organisms/modal.mjs +14 -10
- package/dist/components/organisms/modal.mjs.map +1 -1
- package/dist/components/organisms/toast.d.mts +10 -4
- package/dist/components/organisms/toast.d.mts.map +1 -1
- package/dist/components/organisms/toast.mjs +10 -9
- package/dist/components/organisms/toast.mjs.map +1 -1
- package/dist/core/index.d.mts +1 -0
- package/dist/core/slider.d.mts +2 -0
- package/dist/core/slider.mjs +54 -0
- package/dist/core/slider.mjs.map +1 -0
- package/dist/index.d.mts +2 -1
- package/dist/index.mjs +2 -1
- package/dist/lib/colors.d.mts +12 -0
- package/dist/lib/colors.d.mts.map +1 -0
- package/dist/lib/colors.mjs +28 -0
- package/dist/lib/colors.mjs.map +1 -0
- package/dist/style.css +95 -0
- package/package.json +2 -1
|
@@ -81,7 +81,7 @@ const Upload$1 = ({ defaultValue = [], value: _value, multiple = true, accept =
|
|
|
81
81
|
}), files.length > 0 && /* @__PURE__ */ jsx("ul", {
|
|
82
82
|
className: cn("m-0 flex list-none flex-col gap-2 p-0", classNames?.list),
|
|
83
83
|
children: files.map((file) => /* @__PURE__ */ jsxs("li", {
|
|
84
|
-
className: cn("flex items-center gap-2 rounded-md border p-2", classNames?.item),
|
|
84
|
+
className: cn("m-0 flex items-center gap-2 rounded-md border p-2", classNames?.item),
|
|
85
85
|
children: [
|
|
86
86
|
isImage(file) && /* @__PURE__ */ jsx("img", {
|
|
87
87
|
src: file.url,
|
|
@@ -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\n// Monotonic per-session counter used as a uniqueness tiebreaker for file\n// uids. Guarantees a unique suffix even for the same file (identical\n// name/lastModified/size) added twice, which name+lastModified+size alone\n// can't. These uids are internal React keys, not cryptographic, so a plain\n// counter is enough — and it avoids a runtime `uuid` dependency.\nlet uidCounter = 0;\nconst nextUid = () => (uidCounter += 1);\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 // `nextUid()` 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}-${nextUid()}`,\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 'm-0 flex list-none flex-col gap-2 p-0',\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 variant=\"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":";;;;;;;;AAoBA,IAAI,aAAa;AACjB,MAAM,gBAAiB,cAAc;AA6BrC,MAAM,WAAW,SACf,gBAAgB,KAAK,KAAK,GAAG,KAC7B,+BAA+B,KAAK,KAAK,IAAI;AAE/C,MAAMA,YAAU,EACd,eAAe,CAAC,GAChB,OAAO,QACP,WAAW,MACX,SAAS,WACT,UACA,UACA,WACA,YACA,UAAU,kBAAkB,CAAC,GAC7B,eACW;CACX,MAAM,UAAU,MAAM;CACtB,MAAM,gBAAgB,iBAAiB;CACvC,MAAM,CAAC,OAAO,YAAY,qBAAmC;EAC3D,OAAO;EACP;EACA,UAAU;CACZ,CAAC;CAED,MAAM,WAAW,OAAO,aAAqB;EAC3C,MAAM,YACJ,OAAO,aAAa,WAChB,KAAK,IAAI,GAAG,WAAW,MAAM,MAAM,IACnC,SAAS;EAEf,MAAM,WAAW,SAAS,MAAM,GAAG,SAAS;EAG5C,SAF0B,MAAM,SAEzB,CAAC,CAAC,SAAQ,SAAQ,WAAW;GAAE;GAAM,QAAQ;EAAY,CAAC,CAAC;EAElE,IAAI,SAAS,WAAW,GACtB;EAGF,MAAM,YACJ,MAAM,QAAQ,IACZ,SAAS,IAAI,OAAM,SAAQ;GACzB,IAAI;IACF,OAAO;KAML,KAAK,GAAG,KAAK,KAAK,GAAG,KAAK,aAAa,GAAG,KAAK,KAAK,GAAG,QAAQ;KAC/D,MAAM,KAAK;KACX,KAAK,MAAM,cAAc,IAAI;IAC/B;GACF,QAAQ;IACN,WAAW;KAAE;KAAM,QAAQ;IAAa,CAAC;IACzC,OAAO;GACT;EACF,CAAC,CACH,EAAA,CACA,QAAQ,SAA6B,SAAS,IAAI;EAEpD,IAAI,SAAS,WAAW,GACtB;EAGF,SAAS,CAAC,GAAG,OAAO,GAAG,QAAQ,CAAC;CAClC;CAEA,MAAM,cAAc,QAAgB;EAClC,SAAS,MAAM,QAAO,SAAQ,KAAK,QAAQ,GAAG,CAAC;CACjD;CAEA,MAAM,EAAE,SAAS,eAAe,YAA8B;EAC5D;EACA;EACA;EACA,QAAQ;CACV,CAAC;CAED,OACE,qBAAC,OAAD;EACE,WAAW,GACT,uBACA,SAEF;EALF,UAAA,CAOE,qBAAC,SAAD;GACE,KAAK;GACL,SAAS;GACT,WAAW,GACT,kEACA,mDACA,iCACA,qBACA,cAAc,4BACd,YAAY,qDACZ,YAAY,QAEd;GAZF,UAAA;IAcE,oBAACC,QAAD,EAAY,WAAU,SAAU,CAAA;IAChC,qBAAC,QAAD,EAAA,UAAA;KAAM;KAAmB,WAAW,MAAM;KAAG;IAAgB,EAAA,CAAA;IAC7D,oBAAC,SAAD;KACE,IAAI;KACJ,MAAK;KACG;KACE;KACA;KACV,WAAU;KACV,WAAU,MAAK;MACb,IAAI,EAAE,OAAO,OAAO,QAClB,SAAS,MAAM,KAAK,EAAE,OAAO,KAAK,CAAC;MAErC,EAAE,OAAO,QAAQ;KACnB;IACD,CAAA;GACI;EAEN,CAAA,GAAA,MAAM,SAAS,KACd,oBAAC,MAAD;GACE,WAAW,GACT,yCACA,YAAY,IAEd;GAEC,UAAA,MAAM,KAAI,SACT,qBAAC,MAAD;IAEE,WAAW,
|
|
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\n// Monotonic per-session counter used as a uniqueness tiebreaker for file\n// uids. Guarantees a unique suffix even for the same file (identical\n// name/lastModified/size) added twice, which name+lastModified+size alone\n// can't. These uids are internal React keys, not cryptographic, so a plain\n// counter is enough — and it avoids a runtime `uuid` dependency.\nlet uidCounter = 0;\nconst nextUid = () => (uidCounter += 1);\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 // `nextUid()` 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}-${nextUid()}`,\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 'm-0 flex list-none flex-col gap-2 p-0',\n classNames?.list,\n //\n )}\n >\n {files.map(file => (\n <li\n key={file.uid}\n className={cn(\n // `m-0` completes the list reset the `ul` above starts — see\n // Radio.Group for the host rule this guards against.\n 'm-0 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 variant=\"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":"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"}
|
|
@@ -26,8 +26,16 @@ interface Props {
|
|
|
26
26
|
onOk?: () => void;
|
|
27
27
|
onCancel?: () => void;
|
|
28
28
|
}
|
|
29
|
+
type ModalStatus = 'info' | 'success' | 'error' | 'warning';
|
|
29
30
|
interface StaticProps extends Props {
|
|
30
|
-
|
|
31
|
+
status?: ModalStatus;
|
|
32
|
+
/**
|
|
33
|
+
* @deprecated Use `status` for the semantic state. Kept as an alias so
|
|
34
|
+
* existing `type="info|success|error|warning"` calls keep working. `confirm`
|
|
35
|
+
* is an interaction mode, not a status — call `Modal.confirm()` instead
|
|
36
|
+
* (still accepted here as `type="confirm"` for back-compat).
|
|
37
|
+
*/
|
|
38
|
+
type?: ModalStatus | 'confirm';
|
|
31
39
|
id?: string;
|
|
32
40
|
icon?: React.ReactNode;
|
|
33
41
|
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;;
|
|
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;;;;;;;EAOT,OAAO;EACP;EACA,OAAO,MAAM;EACb,YAAY;;cAGR;KAkBH,MAAA,cAAA,UAAA,WAAA,WAAA,YAAA,OAAA,OAAA,SAAA,QAAA,WAAA,UAAA,QAAA,YAAA,MAAA,aAAA,SAAA,QAAK,MAAA,IAAA;;;EAwNa,KAAA,OAAA;EAEG,QAAA,OAAA;EAEF,MAAA,OAAA;EAEE,QAAA,OAAA;EAIA,QAAA,OAAA"}
|
|
@@ -10,8 +10,10 @@ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
|
10
10
|
import { createPortal } from "react-dom";
|
|
11
11
|
//#region src/components/organisms/modal.tsx
|
|
12
12
|
const { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } = dialog_exports;
|
|
13
|
-
const Modal = ({ open = false, maskClosable
|
|
14
|
-
const { locale } = useConfig();
|
|
13
|
+
const Modal = ({ open = false, maskClosable, closable = false, closeIcon, className, classNames, style, title, content, footer, container, children, okText, cancelText, onOk, onCancel, ...props }) => {
|
|
14
|
+
const { locale, defaultProps } = useConfig();
|
|
15
|
+
const modalDefaults = defaultProps?.modal;
|
|
16
|
+
const resolvedMaskClosable = maskClosable ?? modalDefaults?.maskClosable ?? false;
|
|
15
17
|
if (typeof window === "undefined") return null;
|
|
16
18
|
return /* @__PURE__ */ jsx(Dialog, {
|
|
17
19
|
open,
|
|
@@ -30,7 +32,7 @@ const Modal = ({ open = false, maskClosable = false, closable = false, closeIcon
|
|
|
30
32
|
closeIcon,
|
|
31
33
|
container,
|
|
32
34
|
onPointerDownOutside: (event) => {
|
|
33
|
-
if (!
|
|
35
|
+
if (!resolvedMaskClosable) event.preventDefault();
|
|
34
36
|
},
|
|
35
37
|
children: [
|
|
36
38
|
/* @__PURE__ */ jsxs(DialogHeader, {
|
|
@@ -63,11 +65,13 @@ const STATIC_ICONS = {
|
|
|
63
65
|
warning: /* @__PURE__ */ jsx(OctagonAlert, { className: "text-yellow-400" }),
|
|
64
66
|
confirm: /* @__PURE__ */ jsx(CircleQuestionMark, {})
|
|
65
67
|
};
|
|
66
|
-
const StaticModal = ({ id, type, title, content, okText, cancelText, container, icon, onOk, onCancel, ...props }) => {
|
|
68
|
+
const StaticModal = ({ id, status, type, title, content, okText, cancelText, container, icon, onOk, onCancel, ...props }) => {
|
|
67
69
|
const { locale } = useConfig();
|
|
68
70
|
const [open, setOpen] = useState(true);
|
|
69
71
|
const resolvedOkText = okText ?? locale.ok ?? DEFAULT_LOCALE.ok;
|
|
70
72
|
const resolvedCancelText = cancelText ?? locale.cancel ?? DEFAULT_LOCALE.cancel;
|
|
73
|
+
const isConfirm = type === "confirm";
|
|
74
|
+
const resolvedStatus = status ?? (isConfirm ? void 0 : type);
|
|
71
75
|
const closeModal = (callback) => {
|
|
72
76
|
callback?.();
|
|
73
77
|
setOpen(false);
|
|
@@ -75,7 +79,7 @@ const StaticModal = ({ id, type, title, content, okText, cancelText, container,
|
|
|
75
79
|
modalStack.destroy(id);
|
|
76
80
|
}, 200);
|
|
77
81
|
};
|
|
78
|
-
const footer =
|
|
82
|
+
const footer = isConfirm ? /* @__PURE__ */ jsxs("div", {
|
|
79
83
|
className: "grid w-full grid-cols-5 gap-x-2",
|
|
80
84
|
children: [/* @__PURE__ */ jsx(Button, {
|
|
81
85
|
variant: "outlined",
|
|
@@ -104,7 +108,7 @@ const StaticModal = ({ id, type, title, content, okText, cancelText, container,
|
|
|
104
108
|
},
|
|
105
109
|
title: /* @__PURE__ */ jsxs("p", {
|
|
106
110
|
className: cn("text-center text-lg leading-normal whitespace-pre-wrap", "flex items-center justify-center gap-x-2"),
|
|
107
|
-
children: [icon ||
|
|
111
|
+
children: [icon || (isConfirm ? STATIC_ICONS.confirm : resolvedStatus && STATIC_ICONS[resolvedStatus]), title]
|
|
108
112
|
}),
|
|
109
113
|
container,
|
|
110
114
|
onCancel: () => closeModal(onCancel),
|
|
@@ -126,19 +130,19 @@ Modal.destroyAll = () => {
|
|
|
126
130
|
modalStack.destroy();
|
|
127
131
|
};
|
|
128
132
|
Modal.info = (props) => modalStack.render({
|
|
129
|
-
|
|
133
|
+
status: "info",
|
|
130
134
|
...props
|
|
131
135
|
});
|
|
132
136
|
Modal.success = (props) => modalStack.render({
|
|
133
|
-
|
|
137
|
+
status: "success",
|
|
134
138
|
...props
|
|
135
139
|
});
|
|
136
140
|
Modal.error = (props) => modalStack.render({
|
|
137
|
-
|
|
141
|
+
status: "error",
|
|
138
142
|
...props
|
|
139
143
|
});
|
|
140
144
|
Modal.warning = (props) => modalStack.render({
|
|
141
|
-
|
|
145
|
+
status: "warning",
|
|
142
146
|
...props
|
|
143
147
|
});
|
|
144
148
|
Modal.confirm = (props) => modalStack.render({
|
|
@@ -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\ninterface StaticProps extends Props {\n type?: 'info' | 'success' | 'error' | 'warning' | 'confirm';\n id?: string;\n icon?: React.ReactNode;\n container?: HTMLElement;\n}\n\nconst Modal = ({\n open = false,\n maskClosable = false,\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 } = useConfig();\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 (!maskClosable) {\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 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\n const closeModal = (callback?: () => void) => {\n callback?.();\n setOpen(false);\n setTimeout(() => {\n modalStack.destroy(id);\n }, 200);\n };\n\n const footer =\n type === 'confirm' ? (\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 || (type && STATIC_ICONS[type])}\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({ type: 'info', ...props });\nModal.success = (props: StaticProps) =>\n modalStack.render({ type: 'success', ...props });\nModal.error = (props: StaticProps) =>\n modalStack.render({ type: 'error', ...props });\nModal.warning = (props: StaticProps) =>\n modalStack.render({ type: 'warning', ...props });\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 * @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,7 +1,13 @@
|
|
|
1
1
|
//#region src/components/organisms/toast.d.ts
|
|
2
|
-
type
|
|
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;
|
|
5
11
|
title: React.ReactNode;
|
|
6
12
|
description?: React.ReactNode;
|
|
7
13
|
icon?: React.ReactNode;
|
|
@@ -16,7 +22,7 @@ interface StaticProps extends Props {
|
|
|
16
22
|
container?: HTMLElement;
|
|
17
23
|
}
|
|
18
24
|
declare const Toast: {
|
|
19
|
-
({ type, title, description, icon, closable, action, className, onClose }: Props): import("react").JSX.Element;
|
|
25
|
+
({ status, type, title, description, icon, closable, action, className, onClose }: Props): import("react").JSX.Element;
|
|
20
26
|
destroy: (id?: string) => void;
|
|
21
27
|
destroyAll(): void;
|
|
22
28
|
info(title: React.ReactNode, props?: TriggerOptions): string | undefined;
|
|
@@ -24,7 +30,7 @@ declare const Toast: {
|
|
|
24
30
|
error(title: React.ReactNode, props?: TriggerOptions): string | undefined;
|
|
25
31
|
warning(title: React.ReactNode, props?: TriggerOptions): string | undefined;
|
|
26
32
|
};
|
|
27
|
-
type TriggerOptions = Omit<StaticProps, 'type' | 'title'>;
|
|
33
|
+
type TriggerOptions = Omit<StaticProps, 'status' | 'type' | 'title'>;
|
|
28
34
|
//#endregion
|
|
29
35
|
export { Props, type StaticProps as ToastOptions, Toast as default };
|
|
30
36
|
//# 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,OAAO;EACP,OAAO,MAAM;EACb,cAAc,MAAM;EACpB,OAAO,MAAM;EACb;EACA,SAAS,MAAM;EACf;EACA;;UAGQ,oBAAoB;EAC5B;EACA;EACA,YAAY;;cAUR;
|
|
1
|
+
{"version":3,"file":"toast.d.mts","names":[],"sources":["../../../src/components/organisms/toast.tsx"],"mappings":";KAkBK;UAEY;EACf,SAAS;;;;;;EAMT,OAAO;EACP,OAAO,MAAM;EACb,cAAc,MAAM;EACpB,OAAO,MAAM;EACb;EACA,SAAS,MAAM;EACf;EACA;;UAGQ,oBAAoB;EAC5B;EACA;EACA,YAAY;;cAUR;KAUH,QAAA,MAAA,OAAA,aAAA,MAAA,UAAA,QAAA,WAAA,WAAA,wBAAK,IAAA;;;EAsHa,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"}
|
|
@@ -9,21 +9,22 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
9
9
|
import { useTimeout } from "@jbpark/use-hooks";
|
|
10
10
|
import { createPortal } from "react-dom";
|
|
11
11
|
//#region src/components/organisms/toast.tsx
|
|
12
|
-
const
|
|
12
|
+
const STATUS_ICONS = {
|
|
13
13
|
info: /* @__PURE__ */ jsx(Info, { className: "text-blue-400" }),
|
|
14
14
|
success: /* @__PURE__ */ jsx(Check, { className: "text-green-400" }),
|
|
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 = ({ type, title, description, icon, closable = true, action, className, onClose }) => {
|
|
18
|
+
const Toast = ({ status, type, title, description, icon, closable = true, action, className, onClose }) => {
|
|
19
19
|
const { locale } = useConfig();
|
|
20
|
+
const resolvedStatus = status ?? type;
|
|
20
21
|
return /* @__PURE__ */ jsxs("div", {
|
|
21
|
-
role:
|
|
22
|
+
role: resolvedStatus === "error" ? "alert" : "status",
|
|
22
23
|
className: cn("pointer-events-auto flex w-80 items-start gap-3 rounded-lg", "bg-background text-foreground border p-4 shadow-lg", className),
|
|
23
24
|
children: [
|
|
24
|
-
(icon ||
|
|
25
|
+
(icon || resolvedStatus) && /* @__PURE__ */ jsx("div", {
|
|
25
26
|
className: "mt-0.5 shrink-0 [&_svg]:size-5",
|
|
26
|
-
children: icon ||
|
|
27
|
+
children: icon || resolvedStatus && STATUS_ICONS[resolvedStatus]
|
|
27
28
|
}),
|
|
28
29
|
/* @__PURE__ */ jsxs("div", {
|
|
29
30
|
className: "flex-1 space-y-1",
|
|
@@ -100,22 +101,22 @@ Toast.destroyAll = () => {
|
|
|
100
101
|
toastStack.destroy();
|
|
101
102
|
};
|
|
102
103
|
Toast.info = (title, props) => toastStack.render({
|
|
103
|
-
|
|
104
|
+
status: "info",
|
|
104
105
|
title,
|
|
105
106
|
...props
|
|
106
107
|
});
|
|
107
108
|
Toast.success = (title, props) => toastStack.render({
|
|
108
|
-
|
|
109
|
+
status: "success",
|
|
109
110
|
title,
|
|
110
111
|
...props
|
|
111
112
|
});
|
|
112
113
|
Toast.error = (title, props) => toastStack.render({
|
|
113
|
-
|
|
114
|
+
status: "error",
|
|
114
115
|
title,
|
|
115
116
|
...props
|
|
116
117
|
});
|
|
117
118
|
Toast.warning = (title, props) => toastStack.render({
|
|
118
|
-
|
|
119
|
+
status: "warning",
|
|
119
120
|
title,
|
|
120
121
|
...props
|
|
121
122
|
});
|
|
@@ -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
|
|
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 /**\n * @deprecated Use `status`. Kept as an alias so existing\n * `type=\"info|success|error|warning\"` usage keeps working; `status` matches\n * the shared semantic-state vocabulary used by `Result`.\n */\n type?: 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 type,\n title,\n description,\n icon,\n closable = true,\n action,\n className,\n onClose,\n}: Props) => {\n const { locale } = useConfig();\n // `type` is the deprecated alias for `status`; prefer `status` when both set.\n const resolvedStatus = status ?? type;\n\n return (\n <div\n role={resolvedStatus === '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 || resolvedStatus) && (\n <div className=\"mt-0.5 shrink-0 [&_svg]:size-5\">\n {icon || (resolvedStatus && STATUS_ICONS[resolvedStatus])}\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' | 'type' | '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":"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"}
|
package/dist/core/index.d.mts
CHANGED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { __exportAll } from "../_virtual/_rolldown/runtime.mjs";
|
|
3
|
+
import { cn } from "../utils.mjs";
|
|
4
|
+
import * as React$1 from "react";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
import * as SliderPrimitive from "@radix-ui/react-slider";
|
|
7
|
+
//#region src/core/slider.tsx
|
|
8
|
+
var slider_exports = /* @__PURE__ */ __exportAll({ Slider: () => Slider });
|
|
9
|
+
function Slider({ className, defaultValue, value, min = 0, max = 100, ...props }) {
|
|
10
|
+
const thumbs = React$1.useMemo(() => {
|
|
11
|
+
if (Array.isArray(value)) return value;
|
|
12
|
+
if (Array.isArray(defaultValue)) return defaultValue;
|
|
13
|
+
return [min, max];
|
|
14
|
+
}, [
|
|
15
|
+
value,
|
|
16
|
+
defaultValue,
|
|
17
|
+
min,
|
|
18
|
+
max
|
|
19
|
+
]);
|
|
20
|
+
return /* @__PURE__ */ jsxs(SliderPrimitive.Root, {
|
|
21
|
+
"data-slot": "slider",
|
|
22
|
+
defaultValue,
|
|
23
|
+
value,
|
|
24
|
+
min,
|
|
25
|
+
max,
|
|
26
|
+
className: cn(`relative flex w-full touch-none items-center select-none
|
|
27
|
+
data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50
|
|
28
|
+
data-[orientation=vertical]:h-full data-[orientation=vertical]:min-h-44
|
|
29
|
+
data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col`, className),
|
|
30
|
+
...props,
|
|
31
|
+
children: [/* @__PURE__ */ jsx(SliderPrimitive.Track, {
|
|
32
|
+
"data-slot": "slider-track",
|
|
33
|
+
className: cn(`bg-input relative grow overflow-hidden rounded-full
|
|
34
|
+
data-[orientation=horizontal]:h-1.5
|
|
35
|
+
data-[orientation=horizontal]:w-full
|
|
36
|
+
data-[orientation=vertical]:h-full data-[orientation=vertical]:w-1.5`),
|
|
37
|
+
children: /* @__PURE__ */ jsx(SliderPrimitive.Range, {
|
|
38
|
+
"data-slot": "slider-range",
|
|
39
|
+
className: cn(`bg-primary absolute data-[orientation=horizontal]:h-full
|
|
40
|
+
data-[orientation=vertical]:w-full`)
|
|
41
|
+
})
|
|
42
|
+
}), Array.from({ length: thumbs.length }, (_, index) => /* @__PURE__ */ jsx(SliderPrimitive.Thumb, {
|
|
43
|
+
"data-slot": "slider-thumb",
|
|
44
|
+
className: cn(`border-primary bg-background ring-ring/50 block size-4 shrink-0
|
|
45
|
+
rounded-full border shadow-sm transition-[color,box-shadow]
|
|
46
|
+
hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden
|
|
47
|
+
disabled:pointer-events-none disabled:opacity-50`)
|
|
48
|
+
}, index))]
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
//#endregion
|
|
52
|
+
export { Slider, slider_exports };
|
|
53
|
+
|
|
54
|
+
//# sourceMappingURL=slider.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slider.mjs","names":["React"],"sources":["../../src/core/slider.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\n\nimport * as SliderPrimitive from '@radix-ui/react-slider';\n\nimport { cn } from '@repo/ui/utils';\n\nfunction Slider({\n className,\n defaultValue,\n value,\n min = 0,\n max = 100,\n ...props\n}: React.ComponentProps<typeof SliderPrimitive.Root>) {\n const thumbs = React.useMemo(() => {\n if (Array.isArray(value)) {\n return value;\n }\n if (Array.isArray(defaultValue)) {\n return defaultValue;\n }\n return [min, max];\n }, [value, defaultValue, min, max]);\n\n return (\n <SliderPrimitive.Root\n data-slot=\"slider\"\n defaultValue={defaultValue}\n value={value}\n min={min}\n max={max}\n className={cn(\n `relative flex w-full touch-none items-center select-none\n data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50\n data-[orientation=vertical]:h-full data-[orientation=vertical]:min-h-44\n data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col`,\n className,\n )}\n {...props}\n >\n <SliderPrimitive.Track\n data-slot=\"slider-track\"\n className={cn(\n `bg-input relative grow overflow-hidden rounded-full\n data-[orientation=horizontal]:h-1.5\n data-[orientation=horizontal]:w-full\n data-[orientation=vertical]:h-full data-[orientation=vertical]:w-1.5`,\n )}\n >\n <SliderPrimitive.Range\n data-slot=\"slider-range\"\n className={cn(\n `bg-primary absolute data-[orientation=horizontal]:h-full\n data-[orientation=vertical]:w-full`,\n )}\n />\n </SliderPrimitive.Track>\n {Array.from({ length: thumbs.length }, (_, index) => (\n <SliderPrimitive.Thumb\n data-slot=\"slider-thumb\"\n key={index}\n className={cn(\n `border-primary bg-background ring-ring/50 block size-4 shrink-0\n rounded-full border shadow-sm transition-[color,box-shadow]\n hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden\n disabled:pointer-events-none disabled:opacity-50`,\n )}\n />\n ))}\n </SliderPrimitive.Root>\n );\n}\n\nexport { Slider };\n"],"mappings":";;;;;;;;AAQA,SAAS,OAAO,EACd,WACA,cACA,OACA,MAAM,GACN,MAAM,KACN,GAAG,SACiD;CACpD,MAAM,SAASA,QAAM,cAAc;EACjC,IAAI,MAAM,QAAQ,KAAK,GACrB,OAAO;EAET,IAAI,MAAM,QAAQ,YAAY,GAC5B,OAAO;EAET,OAAO,CAAC,KAAK,GAAG;CAClB,GAAG;EAAC;EAAO;EAAc;EAAK;CAAG,CAAC;CAElC,OACE,qBAAC,gBAAgB,MAAjB;EACE,aAAU;EACI;EACP;EACF;EACA;EACL,WAAW,GACT;;;kFAIA,SACF;EACA,GAAI;EAbN,UAAA,CAeE,oBAAC,gBAAgB,OAAjB;GACE,aAAU;GACV,WAAW,GACT;;;+EAIF;GAEA,UAAA,oBAAC,gBAAgB,OAAjB;IACE,aAAU;IACV,WAAW,GACT;+CAEF;GACD,CAAA;EACoB,CAAA,GACtB,MAAM,KAAK,EAAE,QAAQ,OAAO,OAAO,IAAI,GAAG,UACzC,oBAAC,gBAAgB,OAAjB;GACE,aAAU;GAEV,WAAW,GACT;;;6DAIF;EACD,GAPM,KAON,CACF,CACmB;;AAE1B"}
|
package/dist/index.d.mts
CHANGED
|
@@ -22,6 +22,7 @@ import Progress from "./components/atoms/progress.mjs";
|
|
|
22
22
|
import Radio from "./components/atoms/radio/index.mjs";
|
|
23
23
|
import Select from "./components/atoms/select.mjs";
|
|
24
24
|
import Skeleton from "./components/atoms/skeleton/index.mjs";
|
|
25
|
+
import Slider from "./components/atoms/slider/slider.mjs";
|
|
25
26
|
import Spin from "./components/atoms/spin.mjs";
|
|
26
27
|
import Switch from "./components/atoms/switch.mjs";
|
|
27
28
|
import "./components/atoms/index.mjs";
|
|
@@ -51,4 +52,4 @@ import { ComponentSize, DefaultProps, Locale, ThemeConfig, ThemeToken } from "./
|
|
|
51
52
|
import { DEFAULT_LOCALE, useConfig } from "./providers/config/context.mjs";
|
|
52
53
|
import Config, { Props as Props$3 } from "./providers/config/config.mjs";
|
|
53
54
|
import { ConfigSnapshotProvider, getRootConfigValue } from "./providers/config/registry.mjs";
|
|
54
|
-
export { Button, Card, type Props as CardProps, Checkbox, Col, type Props$1 as ColProps, Collapse, type Props$2 as CollapseProps, ColorPicker, type ComponentSize, Config, type Props$3 as ConfigProps, ConfigSnapshotProvider, Container, type Props$4 as ContainerProps, type Props$5 as ContentProps, DEFAULT_LOCALE, DatePicker, type DefaultProps, Drawer, type Props$6 as DrawerProps, Dropdown, type Props$7 as DropdownProps, Empty, type Props$8 as EmptyProps, FloatButton, type Props$9 as FooterProps, type Props$10 as HeaderProps, Input, Layout, type Props$11 as LayoutProps, List, type Props$12 as ListProps, type Locale, Marquees, type Props$13 as MarqueesProps, Menu, type Props$14 as MenuProps, Modal, type Props$15 as ModalProps, PageHeader, type Props$16 as PageHeaderProps, Popover, Progress, Radio, Result, type Props$17 as ResultProps, Reveals, type Props$18 as RevealsProps, Row, type Props$19 as RowProps, Select, type Props$20 as SiderProps, Skeleton, Space, type Props$21 as SpaceProps, Spin, Splitter, type Props$22 as SplitterProps, Swiper, type Props$23 as SwiperProps, Switch, Tag, type ThemeConfig, type ThemeToken, Toast, type StaticProps as ToastOptions, Typography, Upload, type UploadFile, type Props$24 as UploadProps, getRootConfigValue, useConfig };
|
|
55
|
+
export { Button, Card, type Props as CardProps, Checkbox, Col, type Props$1 as ColProps, Collapse, type Props$2 as CollapseProps, ColorPicker, type ComponentSize, Config, type Props$3 as ConfigProps, ConfigSnapshotProvider, Container, type Props$4 as ContainerProps, type Props$5 as ContentProps, DEFAULT_LOCALE, DatePicker, type DefaultProps, Drawer, type Props$6 as DrawerProps, Dropdown, type Props$7 as DropdownProps, Empty, type Props$8 as EmptyProps, FloatButton, type Props$9 as FooterProps, type Props$10 as HeaderProps, Input, Layout, type Props$11 as LayoutProps, List, type Props$12 as ListProps, type Locale, Marquees, type Props$13 as MarqueesProps, Menu, type Props$14 as MenuProps, Modal, type Props$15 as ModalProps, PageHeader, type Props$16 as PageHeaderProps, Popover, Progress, Radio, Result, type Props$17 as ResultProps, Reveals, type Props$18 as RevealsProps, Row, type Props$19 as RowProps, Select, type Props$20 as SiderProps, Skeleton, Slider, Space, type Props$21 as SpaceProps, Spin, Splitter, type Props$22 as SplitterProps, Swiper, type Props$23 as SwiperProps, Switch, Tag, type ThemeConfig, type ThemeToken, Toast, type StaticProps as ToastOptions, Typography, Upload, type UploadFile, type Props$24 as UploadProps, getRootConfigValue, useConfig };
|
package/dist/index.mjs
CHANGED
|
@@ -14,6 +14,7 @@ import Progress from "./components/atoms/progress.mjs";
|
|
|
14
14
|
import Radio from "./components/atoms/radio/index.mjs";
|
|
15
15
|
import Select from "./components/atoms/select.mjs";
|
|
16
16
|
import Skeleton from "./components/atoms/skeleton/index.mjs";
|
|
17
|
+
import slider_default from "./components/atoms/slider/index.mjs";
|
|
17
18
|
import Spin from "./components/atoms/spin.mjs";
|
|
18
19
|
import Switch from "./components/atoms/switch.mjs";
|
|
19
20
|
import Tag from "./Tag.mjs";
|
|
@@ -38,4 +39,4 @@ import Result from "./components/templates/result.mjs";
|
|
|
38
39
|
import PageHeader from "./components/templates/page-header.mjs";
|
|
39
40
|
import Row from "./components/templates/grid/row.mjs";
|
|
40
41
|
import Col from "./components/templates/grid/col.mjs";
|
|
41
|
-
export { Button, Card, Checkbox, Col, Collapse, ColorPicker, Config, ConfigSnapshotProvider, Container, DEFAULT_LOCALE, DatePicker, Drawer, Dropdown, Empty, FloatButton, Input, Layout, List, Marquees, Menu, Modal, PageHeader, Popover, Progress, Radio, Result, Reveals, Row, Select, Skeleton, Space, Spin, Splitter, Swiper, Switch, Tag, Toast, Typography, Upload, getRootConfigValue, useConfig };
|
|
42
|
+
export { Button, Card, Checkbox, Col, Collapse, ColorPicker, Config, ConfigSnapshotProvider, Container, DEFAULT_LOCALE, DatePicker, Drawer, Dropdown, Empty, FloatButton, Input, Layout, List, Marquees, Menu, Modal, PageHeader, Popover, Progress, Radio, Result, Reveals, Row, Select, Skeleton, slider_default as Slider, Space, Spin, Splitter, Swiper, Switch, Tag, Toast, Typography, Upload, getRootConfigValue, useConfig };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//#region src/lib/colors.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* The shared preset colour palette. `Button` and `Tag` both express these via
|
|
4
|
+
* the `--btn-*` / `--tag-*` custom-property systems in `globals.css` (keyed off
|
|
5
|
+
* `data-color`), so the same word means the same hue on either component
|
|
6
|
+
* (#320). Keep this list in sync with the `[data-color='…']` rules there.
|
|
7
|
+
*/
|
|
8
|
+
declare const PRESET_COLORS: readonly ["blue", "purple", "cyan", "green", "magenta", "pink", "red", "orange", "yellow", "volcano", "geekblue", "lime", "gold"];
|
|
9
|
+
type PresetColor = (typeof PRESET_COLORS)[number];
|
|
10
|
+
//#endregion
|
|
11
|
+
export { PRESET_COLORS, PresetColor };
|
|
12
|
+
//# sourceMappingURL=colors.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"colors.d.mts","names":[],"sources":["../../src/lib/colors.ts"],"mappings":";;;;;;;cAMa;KAgBD,sBAAsB"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
//#region src/lib/colors.ts
|
|
2
|
+
/**
|
|
3
|
+
* The shared preset colour palette. `Button` and `Tag` both express these via
|
|
4
|
+
* the `--btn-*` / `--tag-*` custom-property systems in `globals.css` (keyed off
|
|
5
|
+
* `data-color`), so the same word means the same hue on either component
|
|
6
|
+
* (#320). Keep this list in sync with the `[data-color='…']` rules there.
|
|
7
|
+
*/
|
|
8
|
+
const PRESET_COLORS = [
|
|
9
|
+
"blue",
|
|
10
|
+
"purple",
|
|
11
|
+
"cyan",
|
|
12
|
+
"green",
|
|
13
|
+
"magenta",
|
|
14
|
+
"pink",
|
|
15
|
+
"red",
|
|
16
|
+
"orange",
|
|
17
|
+
"yellow",
|
|
18
|
+
"volcano",
|
|
19
|
+
"geekblue",
|
|
20
|
+
"lime",
|
|
21
|
+
"gold"
|
|
22
|
+
];
|
|
23
|
+
/** Runtime membership test — true when `color` is one of the shared presets. */
|
|
24
|
+
const isPresetColor = (color) => PRESET_COLORS.includes(color);
|
|
25
|
+
//#endregion
|
|
26
|
+
export { PRESET_COLORS, isPresetColor };
|
|
27
|
+
|
|
28
|
+
//# sourceMappingURL=colors.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"colors.mjs","names":[],"sources":["../../src/lib/colors.ts"],"sourcesContent":["/**\n * The shared preset colour palette. `Button` and `Tag` both express these via\n * the `--btn-*` / `--tag-*` custom-property systems in `globals.css` (keyed off\n * `data-color`), so the same word means the same hue on either component\n * (#320). Keep this list in sync with the `[data-color='…']` rules there.\n */\nexport const PRESET_COLORS = [\n 'blue',\n 'purple',\n 'cyan',\n 'green',\n 'magenta',\n 'pink',\n 'red',\n 'orange',\n 'yellow',\n 'volcano',\n 'geekblue',\n 'lime',\n 'gold',\n] as const;\n\nexport type PresetColor = (typeof PRESET_COLORS)[number];\n\n/** Runtime membership test — true when `color` is one of the shared presets. */\nexport const isPresetColor = (color: string): color is PresetColor =>\n (PRESET_COLORS as readonly string[]).includes(color);\n"],"mappings":";;;;;;;AAMA,MAAa,gBAAgB;CAC3B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;AAKA,MAAa,iBAAiB,UAC3B,cAAoC,SAAS,KAAK"}
|