diff --git a/package-lock.json b/package-lock.json index f724eda7..4414a6f5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "chaya-ui", - "version": "1.0.0-beta.76", + "version": "1.0.0-beta.77", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "chaya-ui", - "version": "1.0.0-beta.76", + "version": "1.0.0-beta.77", "license": "GPL-3.0-only", "dependencies": { "nanoid": "^5.0.3" @@ -40,6 +40,7 @@ "@storybook/testing-library": "^0.2.2", "@storybook/theming": "^7.6.4", "@types/color": "^3.0.3", + "@types/papaparse": "^5.3.14", "@types/react": "^18.0.26", "@types/react-dom": "^18.0.10", "@types/webappsec-credential-management": "^0.6.4", @@ -56,9 +57,10 @@ "eslint-plugin-import": "^2.27.5", "eslint-plugin-react": "^7.32.1", "eslint-plugin-storybook": "^0.6.15", + "flatten-anything": "^3.0.5", "immer": "^9.0.21", "loadash": "^1.0.0", - "nanoid": "5.0.3", + "papaparse": "^5.4.1", "postcss": "^8.4.21", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -13426,6 +13428,15 @@ "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", "dev": true }, + "node_modules/@types/papaparse": { + "version": "5.3.14", + "resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.3.14.tgz", + "integrity": "sha512-LxJ4iEFcpqc6METwp9f6BV6VVc43m6MfH0VqFosHvrUgfXiFe6ww7R3itkOQ+TCK6Y+Iv/+RnnvtRZnkc5Kc9g==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", @@ -17581,6 +17592,22 @@ "node": ">=8" } }, + "node_modules/filter-anything": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/filter-anything/-/filter-anything-3.0.7.tgz", + "integrity": "sha512-t2pgu8OiqvmlZPxmf/iIBmGUhkzry7M/WieYJ63YffCw3+rlNnIY7ZDVtf/n3iqVfN0iwLogAIP/EGm8hCsYGg==", + "dev": true, + "dependencies": { + "is-what": "^4.1.8", + "ts-toolbelt": "^9.6.0" + }, + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, "node_modules/finalhandler": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", @@ -17736,6 +17763,22 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, + "node_modules/flatten-anything": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/flatten-anything/-/flatten-anything-3.0.5.tgz", + "integrity": "sha512-1KccKXVhW4lhEjCz4QOGMb5lEfIUbWV6t/N9XbVrDiOV8MS5kv0zkB33cPqth3CjKuy9YjniWpn0ABz3+Bw2OA==", + "dev": true, + "dependencies": { + "filter-anything": "^3.0.5", + "is-what": "^4.1.8" + }, + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, "node_modules/flow-parser": { "version": "0.224.0", "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.224.0.tgz", @@ -19121,6 +19164,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-what": { + "version": "4.1.16", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz", + "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==", + "dev": true, + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, "node_modules/is-wsl": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", @@ -21232,7 +21287,6 @@ "version": "5.0.3", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.3.tgz", "integrity": "sha512-I7X2b22cxA4LIHXPSqbBCEQSL+1wv8TuoefejsX4HFWyC6jc5JG7CEaxOltiKjc1M+YCS2YkrZZcj4+dytw9GA==", - "dev": true, "funding": [ { "type": "github", @@ -21788,6 +21842,12 @@ "node": ">=6" } }, + "node_modules/papaparse": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.4.1.tgz", + "integrity": "sha512-HipMsgJkZu8br23pW15uvo6sib6wne/4woLZPlFf3rpDyMe9ywEXUsuD7+6K9PRkJlVT51j/sCOYDKGGS3ZJrw==", + "dev": true + }, "node_modules/param-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", @@ -25380,6 +25440,12 @@ "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", "dev": true }, + "node_modules/ts-toolbelt": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/ts-toolbelt/-/ts-toolbelt-9.6.0.tgz", + "integrity": "sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==", + "dev": true + }, "node_modules/tsconfig-paths": { "version": "3.14.2", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", diff --git a/package.json b/package.json index e7a1f43a..c0952e18 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "chaya-ui", - "version": "1.0.0-beta.76", + "version": "1.0.0-beta.77", "description": "Modern, Functional Design System & Components for React", "scripts": { "tailwind": "tailwindcss -i ./src/style.scss -o ./dist/style.css -c ./tailwind.config.cjs", @@ -68,6 +68,7 @@ "@storybook/testing-library": "^0.2.2", "@storybook/theming": "^7.6.4", "@types/color": "^3.0.3", + "@types/papaparse": "^5.3.14", "@types/react": "^18.0.26", "@types/react-dom": "^18.0.10", "@types/webappsec-credential-management": "^0.6.4", @@ -84,9 +85,10 @@ "eslint-plugin-import": "^2.27.5", "eslint-plugin-react": "^7.32.1", "eslint-plugin-storybook": "^0.6.15", + "flatten-anything": "^3.0.5", "immer": "^9.0.21", "loadash": "^1.0.0", - "nanoid": "5.0.3", + "papaparse": "^5.4.1", "postcss": "^8.4.21", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/src/components/DataTableManager.tsx b/src/components/DataTableManager.tsx index da5c5457..84c664e9 100644 --- a/src/components/DataTableManager.tsx +++ b/src/components/DataTableManager.tsx @@ -1,5 +1,7 @@ 'use client'; import React, { useState, useEffect } from 'react'; +import Papa from 'papaparse'; +import { flatten } from 'flatten-anything'; import Close from '../utils/icons/close'; import Filter from '../utils/icons/filter'; @@ -58,7 +60,7 @@ export type DataTableManagerProps = { isFilteringInitialised?: boolean, // download - onDownload?: () => void, + onDownload?: () => Promise | object[], isDownloadLoading?: boolean, // create @@ -109,6 +111,21 @@ const DataTableManager = ({ const [_keyword, _setKeyword] = useState(keyword || ''); const [showFilters, setShowFilters] = useState(isFilteringInitialised); + const handleDownload = async () => { + if (typeof onDownload !== 'function') + return; + const data = await onDownload(); + if (!data) return; + const csv = Papa.unparse(data?.map((i) => flatten(i))); + const csvData = new Blob([csv], { type: 'text/csv;charset=utf-8;' }); + const csvURL = window.URL.createObjectURL(csvData); + const link = document.createElement('a'); + link.setAttribute('href', csvURL); + link.setAttribute('download', `${labels.label || 'data'}_${new Date().getTime()}.csv`); + document.body.appendChild(link); + link.click(); + }; + useEffect(() => { _setKeyword(keyword || ''); }, [keyword]); @@ -199,7 +216,7 @@ const DataTableManager = ({ type="button" variant="minimal" color="shade" - onClick={onDownload} + onClick={handleDownload} isLoading={isDownloadLoading} isDisabled={isDownloadLoading} > @@ -297,7 +314,7 @@ const DataTableManager = ({ onClick={onCancelSelections} /> -
+
{selectionActions?.filter((a) => !a.isHidden).map((a) => (
-
- - {labels.optionsTitle} - -
-
+ {labels.optionsTitle?.length > 0 ? ( +
+ + {labels.optionsTitle} + +
+ ) : null} +
0) && 'dsr-border-t dsr-border-neutral-200/50', + 'dsr-max-h-[30vh] dsr-overflow-y-auto', + ])} + > {isFetching ? ( -
- Fetching -
+
    + {Array.from({ length: 4 }).map((_, index) => ( +
    + + +
    + ))} +
) : (
    {availableOptions.map((field, index) => ( @@ -245,7 +261,7 @@ const DropdownFilter = ({ try { const fetchedOptions = await onFetch(searchKeyword); setOptions((prev) => { - const newOptions = fetchedOptions.filter((option) => !prev.find((prevOption) => prevOption.value === option.value)); + const newOptions = (fetchedOptions || []).filter((option) => !prev.find((prevOption) => prevOption.value === option.value)); return [...prev, ...newOptions]; }); } catch (error) { diff --git a/src/components/SkeletonItem/index.tsx b/src/components/SkeletonItem/index.tsx index c8c49917..34cd6853 100644 --- a/src/components/SkeletonItem/index.tsx +++ b/src/components/SkeletonItem/index.tsx @@ -9,10 +9,11 @@ export type SkeletonItemProps = { variant?: 'wave' | 'pulse' w?: string | number h?: string | number - className?: string + className?: string, + children?: React.ReactNode, }; -const SkeletonItem = ({ circular, minWidth, w, h, className, variant = 'wave' } : SkeletonItemProps) => ( +const SkeletonItem = ({ circular, minWidth, w, h, className, variant = 'wave', children } : SkeletonItemProps) => (
    + > + {children} +
    ); diff --git a/stories/components/display/DataTable.stories.tsx b/stories/components/display/DataTable.stories.tsx index 2ab8c63f..6db83927 100644 --- a/stories/components/display/DataTable.stories.tsx +++ b/stories/components/display/DataTable.stories.tsx @@ -399,7 +399,7 @@ const DataTableManagerTemplate: Story> = (args) => { window.alert('Download')} + onDownload={() => { window.alert('Download'); return []; }} onCreate={() => window.alert('Create')} tabs={[ { label: 'All', key: 'all' }, @@ -416,7 +416,6 @@ const DataTableManagerTemplate: Story> = (args) => { labels: { label: 'Category', searchLabel: 'Search Category', - optionsTitle: 'Category', }, options: [ { value: 'hardware', label: 'Hardware' },