@plexui/ui 0.7.81 → 0.7.83

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.
@@ -40,6 +40,13 @@ function splitFileName(name) {
40
40
  ext: name.slice(lastDot),
41
41
  };
42
42
  }
43
+ export const FileCardGroup = ({ children, className }) => {
44
+ return _jsx("div", { className: clsx(s.FileCardGroup, className), children: children });
45
+ };
46
+ export const FileCard = ({ file, onRemove, disabled, className }) => {
47
+ const { stem, ext } = splitFileName(file.name);
48
+ return (_jsxs("div", { className: clsx(s.FileItem, className), children: [_jsx("div", { className: s.FileIcon, children: getFileIcon(file) }), _jsxs("div", { className: s.FileInfo, children: [_jsxs("div", { className: s.FileNameRow, children: [_jsx("span", { className: s.FileNameStem, children: stem }), ext && _jsx("span", { className: s.FileNameExt, children: ext })] }), _jsx("span", { className: s.FileSize, children: formatFileSize(file.size) })] }), onRemove && (_jsx("button", { type: "button", className: s.FileRemove, onClick: onRemove, "aria-label": `Remove ${file.name}`, disabled: disabled, children: _jsx(X, {}) }))] }));
49
+ };
43
50
  function matchesAccept(file, accept) {
44
51
  const acceptTypes = accept.split(",").map((t) => t.trim().toLowerCase());
45
52
  const fileName = file.name.toLowerCase();
@@ -104,9 +111,6 @@ export const FileUpload = (props) => {
104
111
  inputRef.current?.click();
105
112
  }, []);
106
113
  const atLimit = !!maxFiles && files.length >= maxFiles;
107
- return (_jsxs("div", { className: clsx(s.Root, className), children: [variant === "area" ? (_jsxs("button", { type: "button", className: s.AreaTrigger, "data-invalid": invalid ? "" : undefined, onClick: openPicker, disabled: disabled || atLimit, children: [_jsx(UploadDocuments, { className: s.AreaIcon }), _jsx("span", { className: s.AreaLabel, children: label || "Tap to upload files" }), description && _jsx("span", { className: s.AreaDescription, children: description })] })) : (_jsx(Button, { color: "secondary", variant: "soft", size: "3xl", pill: true, block: true, disabled: disabled || atLimit, onClick: openPicker, className: "h-[3.25rem]", children: label || "Upload file" })), files.length > 0 && (_jsx("ul", { className: s.FileList, "data-variant": variant, "aria-label": "Attached files", children: files.map((file, index) => {
108
- const { stem, ext } = splitFileName(file.name);
109
- return (_jsxs("li", { className: s.FileItem, children: [_jsx("div", { className: s.FileIcon, children: getFileIcon(file) }), _jsxs("div", { className: s.FileInfo, children: [_jsxs("div", { className: s.FileNameRow, children: [_jsx("span", { className: s.FileNameStem, children: stem }), ext && _jsx("span", { className: s.FileNameExt, children: ext })] }), _jsx("span", { className: s.FileSize, children: formatFileSize(file.size) })] }), _jsx("button", { type: "button", className: s.FileRemove, onClick: () => removeFile(index), "aria-label": `Remove ${file.name}`, disabled: disabled, children: _jsx(X, {}) })] }, `${file.name}-${file.size}-${file.lastModified}`));
110
- }) })), _jsx("input", { ref: inputRef, type: "file", className: s.HiddenInput, accept: accept, multiple: multiple, onChange: handleFileSelect, disabled: disabled, id: inputId, "aria-describedby": ariaDescribedBy, tabIndex: -1 }), errorMessage && (_jsx(FieldError, { id: errorId, className: s.ErrorMessage, children: errorMessage }))] }));
114
+ return (_jsxs("div", { className: clsx(s.Root, className), children: [variant === "area" ? (_jsxs("button", { type: "button", className: s.AreaTrigger, "data-invalid": invalid ? "" : undefined, onClick: openPicker, disabled: disabled || atLimit, children: [_jsx(UploadDocuments, { className: s.AreaIcon }), _jsx("span", { className: s.AreaLabel, children: label || "Tap to upload files" }), description && _jsx("span", { className: s.AreaDescription, children: description })] })) : (_jsx(Button, { color: "secondary", variant: "soft", size: "3xl", pill: true, block: true, disabled: disabled || atLimit, onClick: openPicker, className: "h-[3.25rem]", children: label || "Upload file" })), files.length > 0 && (_jsx("ul", { className: s.FileList, "data-variant": variant, "aria-label": "Attached files", children: files.map((file, index) => (_jsx("li", { children: _jsx(FileCard, { file: file, onRemove: () => removeFile(index), disabled: disabled }) }, `${file.name}-${file.size}-${file.lastModified}`))) })), _jsx("input", { ref: inputRef, type: "file", className: s.HiddenInput, accept: accept, multiple: multiple, onChange: handleFileSelect, disabled: disabled, id: inputId, "aria-describedby": ariaDescribedBy, tabIndex: -1 }), errorMessage && (_jsx(FieldError, { id: errorId, className: s.ErrorMessage, children: errorMessage }))] }));
111
115
  };
112
116
  //# sourceMappingURL=FileUpload.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"FileUpload.js","sourceRoot":"","sources":["../../../../src/components/FileUpload/FileUpload.tsx"],"names":[],"mappings":"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","sourcesContent":["\"use client\"\n\nimport clsx from \"clsx\"\nimport { useCallback, useId, useRef, useState } from \"react\"\nimport {\n FileDocument,\n ImageSquare,\n FileBlank,\n UploadDocuments,\n X,\n} from \"../Icon\"\nimport { Button } from \"../Button\"\nimport { FieldError } from \"../FieldError\"\nimport s from \"./FileUpload.module.css\"\n\nexport type FileUploadProps = {\n variant?: \"compact\" | \"area\"\n label?: string\n description?: string\n accept?: string\n multiple?: boolean\n maxFiles?: number\n maxSize?: number\n value?: File[]\n onValueChange?: (files: File[]) => void\n onFileReject?: (file: File, reason: string) => void\n errorMessage?: string\n invalid?: boolean\n disabled?: boolean\n id?: string\n className?: string\n \"aria-describedby\"?: string\n \"aria-invalid\"?: boolean\n}\n\nfunction getFileIcon(file: File) {\n const type = file.type\n const name = file.name.toLowerCase()\n\n if (\n type === \"application/pdf\" ||\n name.endsWith(\".pdf\") ||\n name.endsWith(\".doc\") ||\n name.endsWith(\".docx\") ||\n name.endsWith(\".txt\") ||\n name.endsWith(\".rtf\")\n ) {\n return <FileDocument />\n }\n\n if (type.startsWith(\"image/\") || /\\.(png|jpe?g|gif|svg|webp|bmp|ico)$/.test(name)) {\n return <ImageSquare />\n }\n\n return <FileBlank />\n}\n\nfunction formatFileSize(bytes: number): string {\n if (bytes === 0) return \"0 B\"\n if (bytes < 1024) return `${bytes} B`\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)} KB`\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`\n}\n\nfunction splitFileName(name: string): { stem: string; ext: string } {\n const lastDot = name.lastIndexOf(\".\")\n if (lastDot <= 0) return { stem: name, ext: \"\" }\n return {\n stem: name.slice(0, lastDot),\n ext: name.slice(lastDot),\n }\n}\n\nfunction matchesAccept(file: File, accept: string): boolean {\n const acceptTypes = accept.split(\",\").map((t) => t.trim().toLowerCase())\n const fileName = file.name.toLowerCase()\n const fileType = file.type.toLowerCase()\n\n return acceptTypes.some((acceptType) => {\n if (acceptType.startsWith(\".\")) {\n return fileName.endsWith(acceptType)\n }\n if (acceptType.endsWith(\"/*\")) {\n const prefix = acceptType.slice(0, -1)\n return fileType.startsWith(prefix)\n }\n return fileType === acceptType\n })\n}\n\nexport const FileUpload = (props: FileUploadProps) => {\n const {\n variant = \"compact\",\n label,\n description,\n accept,\n multiple = false,\n maxFiles,\n maxSize,\n value: controlledValue,\n onValueChange,\n onFileReject,\n errorMessage,\n \"invalid\": invalidProp,\n disabled = false,\n className,\n id: idProp,\n \"aria-describedby\": ariaDescribedByProp,\n } = props\n\n const [internalFiles, setInternalFiles] = useState<File[]>([])\n const inputRef = useRef<HTMLInputElement>(null)\n const generatedId = useId()\n const inputId = idProp || `file-upload-${generatedId}`\n const errorId = `${inputId}-error`\n\n const files = controlledValue ?? internalFiles\n const isControlled = controlledValue !== undefined\n const invalid = invalidProp ?? !!errorMessage\n\n const ariaDescribedBy =\n [ariaDescribedByProp, errorMessage ? errorId : undefined].filter(Boolean).join(\" \") ||\n undefined\n\n const updateFiles = useCallback(\n (next: File[]) => {\n onValueChange?.(next)\n if (!isControlled) {\n setInternalFiles(next)\n }\n },\n [onValueChange, isControlled],\n )\n\n const handleFileSelect = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n const selected = Array.from(e.target.files || [])\n const accepted: File[] = []\n\n for (const file of selected) {\n if (accept && !matchesAccept(file, accept)) {\n onFileReject?.(file, `File type not accepted: ${file.name}`)\n continue\n }\n if (maxSize && file.size > maxSize) {\n onFileReject?.(file, `File too large: ${file.name} (${formatFileSize(file.size)})`)\n continue\n }\n if (maxFiles && files.length + accepted.length >= maxFiles) {\n onFileReject?.(file, `Maximum ${maxFiles} files allowed`)\n continue\n }\n accepted.push(file)\n }\n\n if (accepted.length > 0) {\n updateFiles([...files, ...accepted])\n }\n\n e.target.value = \"\"\n },\n [accept, maxSize, maxFiles, files, updateFiles, onFileReject],\n )\n\n const removeFile = useCallback(\n (index: number) => {\n const next = files.filter((_, i) => i !== index)\n updateFiles(next)\n },\n [files, updateFiles],\n )\n\n const openPicker = useCallback(() => {\n inputRef.current?.click()\n }, [])\n\n const atLimit = !!maxFiles && files.length >= maxFiles\n\n return (\n <div className={clsx(s.Root, className)}>\n {variant === \"area\" ? (\n <button\n type=\"button\"\n className={s.AreaTrigger}\n data-invalid={invalid ? \"\" : undefined}\n onClick={openPicker}\n disabled={disabled || atLimit}\n >\n <UploadDocuments className={s.AreaIcon} />\n <span className={s.AreaLabel}>{label || \"Tap to upload files\"}</span>\n {description && <span className={s.AreaDescription}>{description}</span>}\n </button>\n ) : (\n <Button\n color=\"secondary\"\n variant=\"soft\"\n size=\"3xl\"\n pill\n block\n disabled={disabled || atLimit}\n onClick={openPicker}\n className=\"h-[3.25rem]\"\n >\n {label || \"Upload file\"}\n </Button>\n )}\n\n {files.length > 0 && (\n <ul\n className={s.FileList}\n data-variant={variant}\n aria-label=\"Attached files\"\n >\n {files.map((file, index) => {\n const { stem, ext } = splitFileName(file.name)\n return (\n <li\n key={`${file.name}-${file.size}-${file.lastModified}`}\n className={s.FileItem}\n >\n <div className={s.FileIcon}>{getFileIcon(file)}</div>\n <div className={s.FileInfo}>\n <div className={s.FileNameRow}>\n <span className={s.FileNameStem}>{stem}</span>\n {ext && <span className={s.FileNameExt}>{ext}</span>}\n </div>\n <span className={s.FileSize}>{formatFileSize(file.size)}</span>\n </div>\n <button\n type=\"button\"\n className={s.FileRemove}\n onClick={() => removeFile(index)}\n aria-label={`Remove ${file.name}`}\n disabled={disabled}\n >\n <X />\n </button>\n </li>\n )\n })}\n </ul>\n )}\n\n <input\n ref={inputRef}\n type=\"file\"\n className={s.HiddenInput}\n accept={accept}\n multiple={multiple}\n onChange={handleFileSelect}\n disabled={disabled}\n id={inputId}\n aria-describedby={ariaDescribedBy}\n tabIndex={-1}\n />\n\n {errorMessage && (\n <FieldError id={errorId} className={s.ErrorMessage}>\n {errorMessage}\n </FieldError>\n )}\n </div>\n )\n}\n"]}
1
+ {"version":3,"file":"FileUpload.js","sourceRoot":"","sources":["../../../../src/components/FileUpload/FileUpload.tsx"],"names":[],"mappings":"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","sourcesContent":["\"use client\"\n\nimport clsx from \"clsx\"\nimport { useCallback, useId, useRef, useState } from \"react\"\nimport {\n FileDocument,\n ImageSquare,\n FileBlank,\n UploadDocuments,\n X,\n} from \"../Icon\"\nimport { Button } from \"../Button\"\nimport { FieldError } from \"../FieldError\"\nimport s from \"./FileUpload.module.css\"\n\nexport type FileUploadProps = {\n variant?: \"compact\" | \"area\"\n label?: string\n description?: string\n accept?: string\n multiple?: boolean\n maxFiles?: number\n maxSize?: number\n value?: File[]\n onValueChange?: (files: File[]) => void\n onFileReject?: (file: File, reason: string) => void\n errorMessage?: string\n invalid?: boolean\n disabled?: boolean\n id?: string\n className?: string\n \"aria-describedby\"?: string\n \"aria-invalid\"?: boolean\n}\n\nfunction getFileIcon(file: File) {\n const type = file.type\n const name = file.name.toLowerCase()\n\n if (\n type === \"application/pdf\" ||\n name.endsWith(\".pdf\") ||\n name.endsWith(\".doc\") ||\n name.endsWith(\".docx\") ||\n name.endsWith(\".txt\") ||\n name.endsWith(\".rtf\")\n ) {\n return <FileDocument />\n }\n\n if (type.startsWith(\"image/\") || /\\.(png|jpe?g|gif|svg|webp|bmp|ico)$/.test(name)) {\n return <ImageSquare />\n }\n\n return <FileBlank />\n}\n\nfunction formatFileSize(bytes: number): string {\n if (bytes === 0) return \"0 B\"\n if (bytes < 1024) return `${bytes} B`\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)} KB`\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`\n}\n\nfunction splitFileName(name: string): { stem: string; ext: string } {\n const lastDot = name.lastIndexOf(\".\")\n if (lastDot <= 0) return { stem: name, ext: \"\" }\n return {\n stem: name.slice(0, lastDot),\n ext: name.slice(lastDot),\n }\n}\n\nexport type FileCardGroupProps = {\n children: React.ReactNode\n className?: string\n}\n\nexport const FileCardGroup = ({ children, className }: FileCardGroupProps) => {\n return <div className={clsx(s.FileCardGroup, className)}>{children}</div>\n}\n\nexport type FileCardProps = {\n file: File\n onRemove?: () => void\n disabled?: boolean\n className?: string\n}\n\nexport const FileCard = ({ file, onRemove, disabled, className }: FileCardProps) => {\n const { stem, ext } = splitFileName(file.name)\n\n return (\n <div className={clsx(s.FileItem, className)}>\n <div className={s.FileIcon}>{getFileIcon(file)}</div>\n <div className={s.FileInfo}>\n <div className={s.FileNameRow}>\n <span className={s.FileNameStem}>{stem}</span>\n {ext && <span className={s.FileNameExt}>{ext}</span>}\n </div>\n <span className={s.FileSize}>{formatFileSize(file.size)}</span>\n </div>\n {onRemove && (\n <button\n type=\"button\"\n className={s.FileRemove}\n onClick={onRemove}\n aria-label={`Remove ${file.name}`}\n disabled={disabled}\n >\n <X />\n </button>\n )}\n </div>\n )\n}\n\nfunction matchesAccept(file: File, accept: string): boolean {\n const acceptTypes = accept.split(\",\").map((t) => t.trim().toLowerCase())\n const fileName = file.name.toLowerCase()\n const fileType = file.type.toLowerCase()\n\n return acceptTypes.some((acceptType) => {\n if (acceptType.startsWith(\".\")) {\n return fileName.endsWith(acceptType)\n }\n if (acceptType.endsWith(\"/*\")) {\n const prefix = acceptType.slice(0, -1)\n return fileType.startsWith(prefix)\n }\n return fileType === acceptType\n })\n}\n\nexport const FileUpload = (props: FileUploadProps) => {\n const {\n variant = \"compact\",\n label,\n description,\n accept,\n multiple = false,\n maxFiles,\n maxSize,\n value: controlledValue,\n onValueChange,\n onFileReject,\n errorMessage,\n \"invalid\": invalidProp,\n disabled = false,\n className,\n id: idProp,\n \"aria-describedby\": ariaDescribedByProp,\n } = props\n\n const [internalFiles, setInternalFiles] = useState<File[]>([])\n const inputRef = useRef<HTMLInputElement>(null)\n const generatedId = useId()\n const inputId = idProp || `file-upload-${generatedId}`\n const errorId = `${inputId}-error`\n\n const files = controlledValue ?? internalFiles\n const isControlled = controlledValue !== undefined\n const invalid = invalidProp ?? !!errorMessage\n\n const ariaDescribedBy =\n [ariaDescribedByProp, errorMessage ? errorId : undefined].filter(Boolean).join(\" \") ||\n undefined\n\n const updateFiles = useCallback(\n (next: File[]) => {\n onValueChange?.(next)\n if (!isControlled) {\n setInternalFiles(next)\n }\n },\n [onValueChange, isControlled],\n )\n\n const handleFileSelect = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n const selected = Array.from(e.target.files || [])\n const accepted: File[] = []\n\n for (const file of selected) {\n if (accept && !matchesAccept(file, accept)) {\n onFileReject?.(file, `File type not accepted: ${file.name}`)\n continue\n }\n if (maxSize && file.size > maxSize) {\n onFileReject?.(file, `File too large: ${file.name} (${formatFileSize(file.size)})`)\n continue\n }\n if (maxFiles && files.length + accepted.length >= maxFiles) {\n onFileReject?.(file, `Maximum ${maxFiles} files allowed`)\n continue\n }\n accepted.push(file)\n }\n\n if (accepted.length > 0) {\n updateFiles([...files, ...accepted])\n }\n\n e.target.value = \"\"\n },\n [accept, maxSize, maxFiles, files, updateFiles, onFileReject],\n )\n\n const removeFile = useCallback(\n (index: number) => {\n const next = files.filter((_, i) => i !== index)\n updateFiles(next)\n },\n [files, updateFiles],\n )\n\n const openPicker = useCallback(() => {\n inputRef.current?.click()\n }, [])\n\n const atLimit = !!maxFiles && files.length >= maxFiles\n\n return (\n <div className={clsx(s.Root, className)}>\n {variant === \"area\" ? (\n <button\n type=\"button\"\n className={s.AreaTrigger}\n data-invalid={invalid ? \"\" : undefined}\n onClick={openPicker}\n disabled={disabled || atLimit}\n >\n <UploadDocuments className={s.AreaIcon} />\n <span className={s.AreaLabel}>{label || \"Tap to upload files\"}</span>\n {description && <span className={s.AreaDescription}>{description}</span>}\n </button>\n ) : (\n <Button\n color=\"secondary\"\n variant=\"soft\"\n size=\"3xl\"\n pill\n block\n disabled={disabled || atLimit}\n onClick={openPicker}\n className=\"h-[3.25rem]\"\n >\n {label || \"Upload file\"}\n </Button>\n )}\n\n {files.length > 0 && (\n <ul\n className={s.FileList}\n data-variant={variant}\n aria-label=\"Attached files\"\n >\n {files.map((file, index) => (\n <li key={`${file.name}-${file.size}-${file.lastModified}`}>\n <FileCard\n file={file}\n onRemove={() => removeFile(index)}\n disabled={disabled}\n />\n </li>\n ))}\n </ul>\n )}\n\n <input\n ref={inputRef}\n type=\"file\"\n className={s.HiddenInput}\n accept={accept}\n multiple={multiple}\n onChange={handleFileSelect}\n disabled={disabled}\n id={inputId}\n aria-describedby={ariaDescribedBy}\n tabIndex={-1}\n />\n\n {errorMessage && (\n <FieldError id={errorId} className={s.ErrorMessage}>\n {errorMessage}\n </FieldError>\n )}\n </div>\n )\n}\n"]}
@@ -41,6 +41,10 @@
41
41
  overflow: hidden;
42
42
  border: 1px solid var(--color-border-primary-outline);
43
43
  border-radius: 26px;
44
+ }.FileCardGroup {
45
+ overflow: hidden;
46
+ border: 1px solid var(--color-border-primary-outline);
47
+ border-radius: 26px;
44
48
  }.FileItem {
45
49
  display: flex;
46
50
  align-items: center;
@@ -48,13 +52,15 @@
48
52
  padding: 12px 21px;
49
53
  }.FileItem + .FileItem {
50
54
  border-top: 1px solid var(--color-border-primary-outline);
55
+ }.FileList li + li > .FileItem {
56
+ border-top: 1px solid var(--color-border-primary-outline);
51
57
  }.FileIcon {
52
58
  display: flex;
53
59
  flex-shrink: 0;
54
60
  width: 16px;
55
61
  height: 16px;
56
62
  margin-left: -4px;
57
- color: var(--color-text);
63
+ color: var(--input-text-color);
58
64
  }.FileIcon svg {
59
65
  width: 16px;
60
66
  height: 16px;
@@ -83,7 +89,7 @@
83
89
  }.FileSize {
84
90
  font-size: 12px;
85
91
  line-height: 1.5;
86
- color: var(--color-text-secondary);
92
+ color: var(--color-text-tertiary);
87
93
  }.FileRemove {
88
94
  display: flex;
89
95
  align-items: center;
@@ -1,2 +1,2 @@
1
- export { FileUpload } from "./FileUpload";
1
+ export { FileUpload, FileCard, FileCardGroup, } from "./FileUpload";
2
2
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/components/FileUpload/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAwB,MAAM,cAAc,CAAA","sourcesContent":["export { FileUpload, type FileUploadProps } from \"./FileUpload\"\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/components/FileUpload/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EAEV,QAAQ,EAER,aAAa,GAEd,MAAM,cAAc,CAAA","sourcesContent":["export {\n FileUpload,\n type FileUploadProps,\n FileCard,\n type FileCardProps,\n FileCardGroup,\n type FileCardGroupProps,\n} from \"./FileUpload\"\n"]}
@@ -17,4 +17,16 @@ export type FileUploadProps = {
17
17
  "aria-describedby"?: string;
18
18
  "aria-invalid"?: boolean;
19
19
  };
20
+ export type FileCardGroupProps = {
21
+ children: React.ReactNode;
22
+ className?: string;
23
+ };
24
+ export declare const FileCardGroup: ({ children, className }: FileCardGroupProps) => import("react/jsx-runtime").JSX.Element;
25
+ export type FileCardProps = {
26
+ file: File;
27
+ onRemove?: () => void;
28
+ disabled?: boolean;
29
+ className?: string;
30
+ };
31
+ export declare const FileCard: ({ file, onRemove, disabled, className }: FileCardProps) => import("react/jsx-runtime").JSX.Element;
20
32
  export declare const FileUpload: (props: FileUploadProps) => import("react/jsx-runtime").JSX.Element;
@@ -1 +1 @@
1
- export { FileUpload, type FileUploadProps } from "./FileUpload";
1
+ export { FileUpload, type FileUploadProps, FileCard, type FileCardProps, FileCardGroup, type FileCardGroupProps, } from "./FileUpload";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plexui/ui",
3
- "version": "0.7.81",
3
+ "version": "0.7.83",
4
4
  "description": "Modern design system for building high-quality applications",
5
5
  "type": "module",
6
6
  "license": "MIT",