diff --git a/CHANGELOG.md b/CHANGELOG.md index 61791b4e..bef8bd7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,25 @@ +# 7.60 - 5/13/24 +- [feature] breadcrumbs component + +# 7.5.7 - 5/13/24 +- [chore] autogen prop types + +# 7.5.6 - 5/13/24 +- [fix] improve range styles +- [chore] autogen prop types + +# 7.5.5 - 5/13/24 +- [chore] change stories export format + +# 7.5.4 - 5/13/24 +- [chore] change stories export format + +# 7.5.3 - 5/13/24 +- [fix] Update button theme +- [fix] Update secondary colors +- [chore] Remove unused dateFormat from CalendarRange +- [chore] export stories directory + # 7.5.2 - 5/8/24 - [chore] add storybook stories to dist - [chore] migrate @reaviz/ctrl-keys to ctrl-keys 1.0.2 diff --git a/docs/blocks/Introduction.mdx b/docs/blocks/Introduction.mdx index 2a3ee895..ddeb8fcf 100644 --- a/docs/blocks/Introduction.mdx +++ b/docs/blocks/Introduction.mdx @@ -22,3 +22,4 @@ We offer the following blocks: - [Profile](?path=/docs/blocks-administration-profile--docs) - [Pricing](?path=/docs/blocks-administration-pricing--docs) - [Billing](?path=/docs/blocks-administration-billing--docs) + - [Team](?path=/docs/blocks-administration-team--docs) diff --git a/docs/blocks/administration/Introduction.mdx b/docs/blocks/administration/Introduction.mdx index 851c4db8..a58fc9e9 100644 --- a/docs/blocks/administration/Introduction.mdx +++ b/docs/blocks/administration/Introduction.mdx @@ -9,3 +9,4 @@ We offer the following blocks to help you build your administration pages: - [Profile](?path=/docs/blocks-administration-profile--docs) - [Pricing](?path=/docs/blocks-administration-pricing--docs) - [Billing](?path=/docs/blocks-administration-billing--docs) +- [Team](?path=/docs/blocks-administration-team--docs) diff --git a/docs/blocks/administration/Team.mdx b/docs/blocks/administration/Team.mdx new file mode 100644 index 00000000..2cc0c403 --- /dev/null +++ b/docs/blocks/administration/Team.mdx @@ -0,0 +1,851 @@ +import { Meta, Unstyled } from '@storybook/addon-docs'; +import { TeamGeneral } from './TeamGeneral'; +import { TeamRoles } from './TeamRoles'; +import { TeamMinimal } from './TeamMinimal'; +import { Tabs, TabPanel, TabList, Tab } from '../../../src/layout/Tabs'; + + + +# Team +The Team blocks provide a comprehensive view of team members and their roles within the organization. This block can be used manage team and assign roles and permissions. + + + + + + Demo + + + Code + + + +
+ +
+
+ + ```tsx +import React, { FC, useState } from 'react'; +import { motion } from 'framer-motion'; +import { Card, Tabs, Tab, TabList, TabPanel, Avatar, Button, IconButton, Checkbox, CheckboxTheme, checkboxTheme as defaultCheckboxTheme, Stack } from 'reablocks'; + +export const TeamGeneral: FC = () => { + const [checkedUsers, setCheckedUsers] = useState([]); + + const checkboxTheme: CheckboxTheme = { + ...defaultCheckboxTheme, + check: 'stroke-white', + boxVariants: { + ...defaultCheckboxTheme.boxVariants, + checked: { + fill: 'hsl(var(--twc-primary))', + stroke: 'hsla(var(--twc-primary))' + }, + unchecked: { + fill: 'transparent', + stroke: 'hsl(var(--twc-surface))' + } + } + }; + + return ( + + +
+
+

General

+

+ Effectively manage your team's workflow with our intuitive + application. Streamline task delegation, monitor progress, and + foster seamless collaboration among team members. Stay informed, + stay connected, and drive productivity with ease. +

+
+ + + Account Details + Users + Billing + Integrations + Notifications + + + +
+
+ Users + + Manage user accounts, permissions, and access levels within + the app to ensure smooth operation and security. + +
+ +
+
+ + + {Array(10) + .fill({ + name: 'Austin McDaniel', + email: 'austin@goodcode.us', + role: 'Admin' + }) + .map((user, index) => ( + <> + + + + + + + + ))} + +
+ { + if (checked) { + setCheckedUsers([...checkedUsers, index]); + } else { + setCheckedUsers( + checkedUsers.filter(i => i !== index) + ); + } + }} + /> + +
+ + {user.name} +
+
+
+ + + + {user.email} +
+
+
+ + + + {user.role} +
+
+
+
+ + Showing 10 of 25 + + + + + + + + + + 1 + + + 2 + + + 3 + + + + + + + + +
+
+
+
+
+ ); +}; + + ``` +
+
+
+
+ + + + + Demo + + + Code + + + +
+ +
+
+ + ```tsx +import { motion } from 'framer-motion'; +import React, { FC, useState } from 'react'; +import { Avatar, Button, Card, Checkbox, CheckboxTheme, checkboxTheme as defaultCheckboxTheme, Divider, IconButton, Stack } from 'reablocks'; + +export const TeamRoles: FC = () => { + const [checkedUsers, setCheckedUsers] = useState([]); + const [checkedAdmins, setCheckedAdmins] = useState([]); + + const checkboxTheme: CheckboxTheme = { + ...defaultCheckboxTheme, + check: 'stroke-white', + boxVariants: { + ...defaultCheckboxTheme.boxVariants, + checked: { + fill: 'hsl(var(--twc-primary))', + stroke: 'hsla(var(--twc-primary))' + }, + unchecked: { + fill: 'transparent', + stroke: 'hsl(var(--twc-surface))' + } + } + }; + + return ( + + +
+
+
+

Team Members

+ + Manage user accounts, permissions, and access levels within the + app to ensure smooth operation and security. + +
+ +
+ +
+
+ Admins (3) +

+ Manage user accounts, permissions, and access levels within the + app to ensure smooth operation and security. +

+
+
+ + + + + + + + + + + + {Array(3) + .fill({ + name: 'Austin McDaniel', + email: 'austin@goodcode.us', + dateAdded: new Date().toLocaleDateString() + }) + .map((user, index) => ( + <> + + + + + + + + + ))} + +
+ + setCheckedAdmins( + checked + ? Array(3) + .fill(0) + .map((_, i) => i) + : [] + ) + } + /> + NameEmailDate Added
+ { + if (checked) { + setCheckedAdmins([...checkedAdmins, index]); + } else { + setCheckedAdmins( + checkedAdmins.filter(i => i !== index) + ); + } + }} + /> + +
+ + {user.name} +
+
+
+ + + + {user.email} +
+
+
+ + + + {user.dateAdded} +
+
+ + + + + + + + + + + + +
+
+
+ +
+
+ Users (12) +

+ Manage user accounts, permissions, and access levels within the + app to ensure smooth operation and security. +

+
+
+ + + + + + + + + + + + {Array(12) + .fill({ + name: 'Austin McDaniel', + email: 'austin@goodcode.us', + dateAdded: new Date().toLocaleDateString() + }) + .map((user, index) => ( + <> + + + + + + + + + ))} + +
+ + setCheckedUsers( + checked + ? Array(12) + .fill(0) + .map((_, i) => i) + : [] + ) + } + /> + NameEmailDate Added
+ { + if (checked) { + setCheckedUsers([...checkedUsers, index]); + } else { + setCheckedUsers( + checkedUsers.filter(i => i !== index) + ); + } + }} + /> + +
+ + {user.name} +
+
+
+ + + + {user.email} +
+
+
+ + + + {user.dateAdded} +
+
+ + + + + + + + + + + + +
+
+
+
+
+
+ ); +}; + + ``` +
+
+
+
+ + + + + Demo + + + Code + + + +
+ +
+
+ + ```tsx +import { motion } from 'framer-motion'; +import React, { FC } from 'react'; +import { Avatar, Button, IconButton, Input, Card, Divider, Stack, Tab, TabList, TabPanel, Tabs } from 'reablocks' + +const GoodCodeLogo = () => ( + + + + + + + + + + + + + + + + + + +); + +export const TeamMinimal: FC = () => { + return ( + + +
+ + + Account + Users + Billing + Integrations + Notifications + + + + + + } + containerClassname="max-w-[200px]" + placeholder="Search teams, users..." + /> +
+ +
+
+ On teams + + You're currently on these teams. + +
+ +
+
+
+
+ + Good Code +
+ +
+ +
+
+ + Good Code +
+ +
+
+
+ Your team + + Manage your existing team and change roles/permissions. + +
+
+ + + + + + + + + + {Array(12) + .fill({ + name: 'Austin McDaniel', + email: 'austin@goodcode.us', + dateAdded: new Date().toLocaleDateString() + }) + .map((user, index) => ( + <> + + + + + + + ))} + +
NameEmail
+
+ + {user.name} +
+
+
+ + + + {user.email} +
+
+ + + + +
+
+
+ + Showing 10 of 25 + + + + + + + + + + 1 + + + 2 + + + 3 + + + + + + + + +
+
+
+
+ ); +}; + + ``` +
+
+
diff --git a/docs/blocks/administration/TeamGeneral.tsx b/docs/blocks/administration/TeamGeneral.tsx new file mode 100644 index 00000000..89b2a115 --- /dev/null +++ b/docs/blocks/administration/TeamGeneral.tsx @@ -0,0 +1,210 @@ +import React, { FC, useState } from 'react'; +import { motion } from 'framer-motion'; + +import { Card } from '../../../src/layout/Card'; +import { Tabs, Tab, TabList, TabPanel } from '../../../src/layout/Tabs'; +import { Avatar } from '../../../src/elements/Avatar'; +import { Button } from '../../../src/elements/Button'; +import { IconButton } from '../../../src/elements/IconButton'; +import { + Checkbox, + CheckboxTheme, + checkboxTheme as defaultCheckboxTheme +} from '../../../src/form/Checkbox'; +import { Stack } from '../../../src/layout/Stack'; + +export const TeamGeneral: FC = () => { + const [checkedUsers, setCheckedUsers] = useState([]); + + const checkboxTheme: CheckboxTheme = { + ...defaultCheckboxTheme, + check: 'stroke-white', + boxVariants: { + ...defaultCheckboxTheme.boxVariants, + checked: { + fill: 'hsl(var(--twc-primary))', + stroke: 'hsla(var(--twc-primary))' + }, + unchecked: { + fill: 'transparent', + stroke: 'hsl(var(--twc-surface))' + } + } + }; + + return ( + + +
+
+

General

+

+ Effectively manage your team's workflow with our intuitive + application. Streamline task delegation, monitor progress, and + foster seamless collaboration among team members. Stay informed, + stay connected, and drive productivity with ease. +

+
+ + + Account Details + Users + Billing + Integrations + Notifications + + + +
+
+ Users + + Manage user accounts, permissions, and access levels within + the app to ensure smooth operation and security. + +
+ +
+
+ + + {Array(10) + .fill({ + name: 'Austin McDaniel', + email: 'austin@goodcode.us', + role: 'Admin' + }) + .map((user, index) => ( + <> + + + + + + + + ))} + +
+ { + if (checked) { + setCheckedUsers([...checkedUsers, index]); + } else { + setCheckedUsers( + checkedUsers.filter(i => i !== index) + ); + } + }} + /> + +
+ + {user.name} +
+
+
+ + + + {user.email} +
+
+
+ + + + {user.role} +
+
+
+
+ + Showing 10 of 25 + + + + + + + + + + 1 + + + 2 + + + 3 + + + + + + + + +
+
+
+
+
+ ); +}; diff --git a/docs/blocks/administration/TeamMinimal.tsx b/docs/blocks/administration/TeamMinimal.tsx new file mode 100644 index 00000000..ed213886 --- /dev/null +++ b/docs/blocks/administration/TeamMinimal.tsx @@ -0,0 +1,249 @@ +import { motion } from 'framer-motion'; +import React, { FC } from 'react'; + +import { Avatar } from '../../../src/elements/Avatar'; +import { Button } from '../../../src/elements/Button'; +import { IconButton } from '../../../src/elements/IconButton'; +import { Input } from '../../../src/form/Input'; +import { Card } from '../../../src/layout/Card'; +import { Divider } from '../../../src/layout/Divider'; +import { Stack } from '../../../src/layout/Stack'; +import { Tab, TabList, TabPanel, Tabs } from '../../../src/layout/Tabs'; + +const GoodCodeLogo = () => ( + + + + + + + + + + + + + + + + + + +); + +export const TeamMinimal: FC = () => { + return ( + + +
+ + + Account + Users + Billing + Integrations + Notifications + + + + + + } + containerClassname="max-w-[200px]" + placeholder="Search teams, users..." + /> +
+ +
+
+ On teams + + You're currently on these teams. + +
+ +
+
+
+
+ + Good Code +
+ +
+ +
+
+ + Good Code +
+ +
+
+
+ Your team + + Manage your existing team and change roles/permissions. + +
+
+ + + + + + + + + + {Array(12) + .fill({ + name: 'Austin McDaniel', + email: 'austin@goodcode.us', + dateAdded: new Date().toLocaleDateString() + }) + .map((user, index) => ( + <> + + + + + + + ))} + +
NameEmail
+
+ + {user.name} +
+
+
+ + + + {user.email} +
+
+ + + + +
+
+
+ + Showing 10 of 25 + + + + + + + + + + 1 + + + 2 + + + 3 + + + + + + + + +
+
+
+
+ ); +}; diff --git a/docs/blocks/administration/TeamRoles.tsx b/docs/blocks/administration/TeamRoles.tsx new file mode 100644 index 00000000..7034cb8f --- /dev/null +++ b/docs/blocks/administration/TeamRoles.tsx @@ -0,0 +1,343 @@ +import { motion } from 'framer-motion'; +import React, { FC, useState } from 'react'; + +import { Avatar } from '../../../src/elements/Avatar'; +import { Button } from '../../../src/elements/Button'; +import { IconButton } from '../../../src/elements/IconButton'; +import { + Checkbox, + CheckboxTheme, + checkboxTheme as defaultCheckboxTheme +} from '../../../src/form/Checkbox'; +import { Card } from '../../../src/layout/Card'; +import { Divider } from '../../../src/layout/Divider'; +import { Stack } from '../../../src/layout/Stack'; + +export const TeamRoles: FC = () => { + const [checkedUsers, setCheckedUsers] = useState([]); + const [checkedAdmins, setCheckedAdmins] = useState([]); + + const checkboxTheme: CheckboxTheme = { + ...defaultCheckboxTheme, + check: 'stroke-white', + boxVariants: { + ...defaultCheckboxTheme.boxVariants, + checked: { + fill: 'hsl(var(--twc-primary))', + stroke: 'hsla(var(--twc-primary))' + }, + unchecked: { + fill: 'transparent', + stroke: 'hsl(var(--twc-surface))' + } + } + }; + + return ( + + +
+
+
+

Team Members

+ + Manage user accounts, permissions, and access levels within the + app to ensure smooth operation and security. + +
+ +
+ +
+
+ Admins (3) +

+ Manage user accounts, permissions, and access levels within the + app to ensure smooth operation and security. +

+
+
+ + + + + + + + + + + + {Array(3) + .fill({ + name: 'Austin McDaniel', + email: 'austin@goodcode.us', + dateAdded: new Date().toLocaleDateString() + }) + .map((user, index) => ( + <> + + + + + + + + + ))} + +
+ + setCheckedAdmins( + checked + ? Array(3) + .fill(0) + .map((_, i) => i) + : [] + ) + } + /> + NameEmailDate Added
+ { + if (checked) { + setCheckedAdmins([...checkedAdmins, index]); + } else { + setCheckedAdmins( + checkedAdmins.filter(i => i !== index) + ); + } + }} + /> + +
+ + {user.name} +
+
+
+ + + + {user.email} +
+
+
+ + + + {user.dateAdded} +
+
+ + + + + + + + + + + + +
+
+
+ +
+
+ Users (12) +

+ Manage user accounts, permissions, and access levels within the + app to ensure smooth operation and security. +

+
+
+ + + + + + + + + + + + {Array(12) + .fill({ + name: 'Austin McDaniel', + email: 'austin@goodcode.us', + dateAdded: new Date().toLocaleDateString() + }) + .map((user, index) => ( + <> + + + + + + + + + ))} + +
+ + setCheckedUsers( + checked + ? Array(12) + .fill(0) + .map((_, i) => i) + : [] + ) + } + /> + NameEmailDate Added
+ { + if (checked) { + setCheckedUsers([...checkedUsers, index]); + } else { + setCheckedUsers( + checkedUsers.filter(i => i !== index) + ); + } + }} + /> + +
+ + {user.name} +
+
+
+ + + + {user.email} +
+
+
+ + + + {user.dateAdded} +
+
+ + + + + + + + + + + + +
+
+
+
+
+
+ ); +}; diff --git a/docs/components/layout/Breadcrumbs.mdx b/docs/components/layout/Breadcrumbs.mdx new file mode 100644 index 00000000..4d904760 --- /dev/null +++ b/docs/components/layout/Breadcrumbs.mdx @@ -0,0 +1,33 @@ +import { Meta, Canvas, ArgTypes } from '@storybook/addon-docs'; +import { Breadcrumbs, BreadcrumbList, BreadcrumbItem, BreadcrumbPage, BreadcrumbLink, BreadcrumbSeparator, breadcrumbsTheme } from '../../../src/layout/Breadcrumbs'; +import * as BreadcrumbsStories from '../../../src/layout/Breadcrumbs/Breadcrumbs.story'; +import ThemeRender from '../../ThemeRender.tsx'; + + + +# Breadcrumbs +Breadcrumbs are a navigation component that shows the user's location within a website or application. + +## Example + + +## Theme +This component uses the following default theme: + + + +Learn more about how to customize in the [Theme documentation](?path=/docs/docs-theme-getting-started--docs). + +## API + +### Breadcrumbs + + +### BreadcrumbList + + +### BreadcrumbLink + + +### BreadcrumbSeparator + diff --git a/package-lock.json b/package-lock.json index baa9e482..44146aaf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "reablocks", - "version": "7.5.2", + "version": "7.5.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "reablocks", - "version": "7.5.2", + "version": "7.5.6", "license": "Apache-2.0", "dependencies": { "@marko19907/string-to-color": "^1.0.0", @@ -67,6 +67,7 @@ "postcss-preset-env": "^9.5.2", "prettier": "^3.2.5", "react": "^18.0.0", + "react-docgen": "^7.0.3", "react-dom": "^18.0.0", "react-hook-form": "^7.51.1", "rollup-plugin-peer-deps-external": "2.2.4", diff --git a/package.json b/package.json index 66a8a3d7..905eed74 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,14 @@ { "name": "reablocks", - "version": "7.5.2", + "version": "7.6.0", "description": "Component library for React", "scripts": { - "build": "npm run build:js && npm run build:styles && npm run rewrite", + "build": "npm run build:js && npm run build:styles && npm run rewrite && npm run build:stories && npm run build:docs", "build-storybook": "storybook build", "build:js": "vite build --mode library", + "build:stories": "tsc dist/stories/*.tsx --module esnext --jsx react-jsx --skipLibCheck true || exit 0;", "build:styles": "tailwindcss -i ./src/index.css -o ./dist/index.css", + "build:docs": "node scripts/docs.js", "rewrite": "node scripts/rewrite.cjs", "lint": "eslint --ext js,ts,tsx", "lint:fix": "eslint --ext js,ts,tsx --fix src", @@ -27,7 +29,8 @@ "import": "./dist/index.js", "require": "./dist/index.umd.cjs", "types": "./dist/index.d.ts" - } + }, + "./stories/*": "./dist/stories/*" }, "browser": "dist/index.js", "typings": "dist/index.d.ts", @@ -56,13 +59,13 @@ "homepage": "https://github.com/reaviz/reablocks#readme", "dependencies": { "@marko19907/string-to-color": "^1.0.0", - "ctrl-keys": "^1.0.2", "@reaviz/react-use-fuzzy": "^1.0.3", "body-scroll-lock-upgrade": "^1.1.0", "chroma-js": "^2.4.2", "classnames": "^2.5.1", "coverup": "^0.1.1", "create-global-state-hook": "^0.0.2", + "ctrl-keys": "^1.0.2", "date-fns": "^3.6.0", "ellipsize": "^0.5.1", "focus-trap-react": "^10.2.3", @@ -117,6 +120,7 @@ "postcss-preset-env": "^9.5.2", "prettier": "^3.2.5", "react": "^18.0.0", + "react-docgen": "^7.0.3", "react-dom": "^18.0.0", "react-hook-form": "^7.51.1", "rollup-plugin-peer-deps-external": "2.2.4", diff --git a/scripts/docs.js b/scripts/docs.js new file mode 100644 index 00000000..35a67d59 --- /dev/null +++ b/scripts/docs.js @@ -0,0 +1,86 @@ +import { readFileSync, writeFileSync } from 'fs'; +import fg from 'fast-glob'; +import { resolve } from 'path'; +import { parse, builtinResolvers } from 'react-docgen'; + +const { + ChainResolver, + FindAllDefinitionsResolver, + FindAnnotatedDefinitionsResolver, +} = builtinResolvers; + +const resolver = new ChainResolver( + [new FindAnnotatedDefinitionsResolver(), new FindAllDefinitionsResolver()], + { chainingLogic: ChainResolver.Logic.ALL }, +); + +const defaultPlugins = [ + 'jsx', + 'asyncDoExpressions', + 'decimal', + 'decorators', + 'decoratorAutoAccessors', + 'destructuringPrivate', + 'doExpressions', + 'explicitResourceManagement', + 'exportDefaultFrom', + 'functionBind', + 'functionSent', + 'importAssertions', + 'importReflection', + 'moduleBlocks', + 'partialApplication', + ['pipelineOperator', { proposal: 'minimal' }], + 'recordAndTuple', + 'regexpUnicodeSets', + 'throwExpressions', +]; + +/** + * Builds the doc types. + */ +function buildDocs() { + const files = fg.sync('src/**/!(*.story).tsx'); + + const result = []; + let count = 0; + let fail = 0; + + files.forEach((file) => { + console.log('Reading', file); + + try { + const code = readFileSync(file, { encoding: 'utf-8' }); + const documentation = parse(code, { + // Stolen from website + // https://github.com/reactjs/react-docgen/blob/main/packages/website/src/components/playground/Playground.tsx#L85 + resolver, + babelOptions: { + babelrc: false, + babelrcRoots: false, + configFile: false, + filename: 'playground.js', + parserOpts: { + plugins: [...defaultPlugins, 'typescript'], + }, + } + }); + if (documentation) { + result.push(...documentation); + count++; + } + } catch (e) { + fail++; + console.error('Error reading', file, e); + } + }); + + const fileName = resolve('dist', 'stories', 'docs.json'); + writeFileSync(fileName, JSON.stringify(result, null, 2)); + + console.info('Docs created!', fileName); + console.info('Failed:', fail); + console.info('Total Doc:', count); +} + +buildDocs(); diff --git a/scripts/rewrite.cjs b/scripts/rewrite.cjs index c47be944..ba6a41b1 100644 --- a/scripts/rewrite.cjs +++ b/scripts/rewrite.cjs @@ -1,20 +1,29 @@ const fs = require('fs'); const fg = require('fast-glob'); const { rewritePaths } = require('typescript-rewrite-paths'); +const path = require('path'); -const files = fg.sync('dist/stories/*.tsx'); +/** + * Replace all the paths in the stories from `./Block` to `reablocks` + */ +function replacePaths() { + // Grep all the stories + const files = fg.sync('dist/stories/*.tsx'); -files.forEach((file) => { - const code = fs.readFileSync(file, { encoding: 'utf-8' }); + files.forEach((file) => { + const code = fs.readFileSync(file, { encoding: 'utf-8' }); - const output = rewritePaths(code, path => { - if (path.startsWith('./') || path.startsWith('../')) { - console.info(`Replacing ${path} with reablocks`); - return 'reablocks'; - } + const output = rewritePaths(code, path => { + if (path.startsWith('./') || path.startsWith('../')) { + console.info(`Replacing ${path} with reablocks`); + return 'reablocks'; + } - return path; + return path; + }); + + fs.writeFileSync(file, output); }); +} - fs.writeFileSync(file, output); -}); +replacePaths(); diff --git a/src/data/DateFormat/DateFormat.tsx b/src/data/DateFormat/DateFormat.tsx index ca8cf124..40240b5a 100644 --- a/src/data/DateFormat/DateFormat.tsx +++ b/src/data/DateFormat/DateFormat.tsx @@ -93,7 +93,7 @@ export const DateFormat: FC = ({ should = cache === 'true'; } - const [isRelative, setIsRelative] = useState(should as boolean); + const [isRelative, setIsRelative] = useState(should); const timeout = useRef(null); const { dateObj, formatted, relative } = useMemo( () => safeFormat(date, { format, includeSeconds, addSuffix }), diff --git a/src/elements/Avatar/Avatar.tsx b/src/elements/Avatar/Avatar.tsx index 41374e8f..666fb2b0 100644 --- a/src/elements/Avatar/Avatar.tsx +++ b/src/elements/Avatar/Avatar.tsx @@ -1,4 +1,4 @@ -import React, { useMemo } from 'react'; +import React, { forwardRef, useMemo } from 'react'; import getInitials from 'name-initials'; import { generateColor } from '@marko19907/string-to-color'; import { cn, useComponentTheme } from '@/utils'; @@ -61,7 +61,7 @@ export interface AvatarProps extends React.HTMLAttributes { theme?: AvatarTheme; } -export const Avatar = React.forwardRef( +export const Avatar = forwardRef( ( { name, diff --git a/src/form/Checkbox/CheckboxTheme.ts b/src/form/Checkbox/CheckboxTheme.ts index c949b016..2e9ca70f 100644 --- a/src/form/Checkbox/CheckboxTheme.ts +++ b/src/form/Checkbox/CheckboxTheme.ts @@ -57,7 +57,7 @@ const baseTheme: Partial = { 'flex items-center justify-center cursor-pointer focus-visible:outline-none', disabled: 'opacity-60 cursor-not-allowed', sizes: { - small: '[&>svg]:w-3 [&>svg]:min-h-3', + small: '[&>svg]:w-3 [&>svg]:h-3', medium: '[&>svg]:w-4 [&>svg]:h-4', large: '[&>svg]:w-5 [&>svg]:h-5' } diff --git a/src/form/Range/Range.story.tsx b/src/form/Range/Range.story.tsx index c53a3519..3407f288 100644 --- a/src/form/Range/Range.story.tsx +++ b/src/form/Range/Range.story.tsx @@ -5,7 +5,14 @@ import { debounce } from 'lodash'; export default { title: 'Components/Form/Range', - component: Range + component: Range, + decorators: [ + Story => ( +
+ +
+ ) + ] }; export const Single = () => { @@ -21,6 +28,20 @@ export const Single = () => { ); }; +export const SingleShowHighlight = () => { + const [state, setState] = useState(20); + return ( + + ); +}; + export const SingleDisabled = () => { const [state, setState] = useState(20); return ( diff --git a/src/form/Range/RangeDouble.tsx b/src/form/Range/RangeDouble.tsx index 048295b7..fbdfd9bb 100644 --- a/src/form/Range/RangeDouble.tsx +++ b/src/form/Range/RangeDouble.tsx @@ -10,7 +10,7 @@ import React, { import { motion, useMotionValue } from 'framer-motion'; import { RangeProps, RangeTooltip } from './RangeTooltip'; import { twMerge } from 'tailwind-merge'; -import { useComponentTheme } from '@/utils'; +import { cn, useComponentTheme } from '@/utils'; import { RangeTheme } from './RangeTheme'; export const RangeDouble: FC> = ({ @@ -230,7 +230,9 @@ export const RangeDouble: FC> = ({ )}
> = ({ +export interface RangeSingleProps extends RangeProps { + /** + * Display the highlight when true + */ + showHighlight?: boolean; +} + +export const RangeSingle: FC = ({ disabled, style, handleClassName, @@ -24,6 +31,7 @@ export const RangeSingle: FC> = ({ valueDisplay = 'hover', valueFormat = value => value.toLocaleString(), step = 1, + showHighlight = false, theme: customTheme }) => { const [currentValue, setCurrentValue] = useState(value); @@ -79,6 +87,7 @@ export const RangeSingle: FC> = ({ const [hovering, setHovering] = useState(false); const [focused, setFocused] = useState(false); const tooltipVisible = dragging || focused || hovering; + const maxPercentage = ((currentValue - min) / (max - min)) * 100; const theme: RangeTheme = useComponentTheme('range', customTheme); @@ -133,6 +142,16 @@ export const RangeSingle: FC> = ({ valueFormat(currentValue) )} + {showHighlight && ( +
+ )}
); }; diff --git a/src/form/Range/RangeTheme.ts b/src/form/Range/RangeTheme.ts index 7977afa1..317f73f3 100644 --- a/src/form/Range/RangeTheme.ts +++ b/src/form/Range/RangeTheme.ts @@ -1,7 +1,10 @@ export interface RangeTheme { base: string; drag: string; - rangeHighlight: string; + rangeHighlight: { + base: string; + disabled: string; + }; disabled: string; inputWrapper: { base: string; @@ -13,13 +16,16 @@ export interface RangeTheme { const baseTheme: RangeTheme = { base: 'relative box-border w-full h-0.5', - drag: 'absolute w-3.5 h-3.5 -left-[7px] -top-[7px] rounded-full', + drag: 'absolute w-4 h-4 -left-2 -top-2 rounded-full', inputWrapper: { base: 'cursor-pointer inline-block relative h-full w-full rounded-full', disabled: 'cursor-not-allowed' }, - rangeHighlight: 'pointer-events-none h-0.5', - disabled: 'cursor-not-allowed opacity-60', + rangeHighlight: { + base: 'pointer-events-none h-0.5', + disabled: 'cursor-not-allowed' + }, + disabled: 'cursor-not-allowed', input: 'absolute left-[-9999px]', // The hidden input used for keyboard controls tooltip: 'absolute top-[-45px] -translate-x-2/4 whitespace-nowrap text-center left-2/4 rounded-lg p-2.5' @@ -27,12 +33,24 @@ const baseTheme: RangeTheme = { export const rangeTheme: RangeTheme = { ...baseTheme, - base: [baseTheme.base, 'bg-surface'].join(' '), + base: [baseTheme.base, 'bg-surface light:bg-gray-200'].join(' '), inputWrapper: { ...baseTheme.inputWrapper, - base: [baseTheme.inputWrapper.base, 'bg-primary'].join(' ') + base: [ + baseTheme.inputWrapper.base, + 'bg-primary-active hover:bg-primary-hover shadow-[0px_4px_4px_0px_rgba(0,0,0,0.20)]' + ].join(' '), + disabled: [ + baseTheme.inputWrapper.disabled, + 'bg-secondary-inactive hover:bg-secondary-inactive' + ].join(' ') + }, + rangeHighlight: { + base: [baseTheme.rangeHighlight.base, 'bg-primary-active'].join(' '), + disabled: [baseTheme.rangeHighlight.disabled, 'bg-secondary-inactive'].join( + ' ' + ) }, - rangeHighlight: [baseTheme.rangeHighlight, 'bg-primary'].join(' '), tooltip: [baseTheme.tooltip, 'text-surface-content bg-surface'].join(' ') }; @@ -46,10 +64,13 @@ export const legacyRangeTheme: RangeTheme = { baseTheme.drag, 'top-[calc(-1_*_(var(--range-handle-size)_-_var(--range-track-size))_/_2)] left-[calc(-1_*_var(--range-handle-size)_/_2)] w-[var(--range-handle-size)] h-[var(--range-handle-size)] bg-[var(--range-handle-background)] rounded-[var(--range-handle-border-radius)]' ].join(' '), - rangeHighlight: [ - baseTheme.rangeHighlight, - 'h-[var(--range-track-size)] bg-[var(--range-track-active-background)]' - ].join(' '), + rangeHighlight: { + ...baseTheme.rangeHighlight, + base: [ + baseTheme.rangeHighlight.base, + 'h-[var(--range-track-size)] bg-[var(--range-track-active-background)]' + ].join(' ') + }, tooltip: [ baseTheme.tooltip, 'rounded-[var(--border-radius-md)] [padding:_var(--spacing-md)] bg-[var(--tooltip-background)] text-[var(--tooltip-color)]' diff --git a/src/layout/Breadcrumbs/BreadcrumbItem.tsx b/src/layout/Breadcrumbs/BreadcrumbItem.tsx new file mode 100644 index 00000000..83daab94 --- /dev/null +++ b/src/layout/Breadcrumbs/BreadcrumbItem.tsx @@ -0,0 +1,7 @@ +import React, { FC } from 'react'; +import { cn } from '@/utils'; + +export const BreadcrumbItem: FC> = ({ + className, + ...rest +}) =>
  • ; diff --git a/src/layout/Breadcrumbs/BreadcrumbLink.tsx b/src/layout/Breadcrumbs/BreadcrumbLink.tsx new file mode 100644 index 00000000..c03a53bd --- /dev/null +++ b/src/layout/Breadcrumbs/BreadcrumbLink.tsx @@ -0,0 +1,19 @@ +import React, { FC } from 'react'; +import { cn, useComponentTheme } from '@/utils'; +import { BreadcrumbsTheme } from './BreadcrumbsTheme'; + +export interface BreadcrumbLinkProps + extends React.AnchorHTMLAttributes { + theme?: BreadcrumbsTheme; +} + +export const BreadcrumbLink: FC = ({ + className, + theme: customTheme, + href, + ...rest +}) => { + const theme = useComponentTheme('breadcrumbs', customTheme); + + return ; +}; diff --git a/src/layout/Breadcrumbs/BreadcrumbList.tsx b/src/layout/Breadcrumbs/BreadcrumbList.tsx new file mode 100644 index 00000000..a41cf534 --- /dev/null +++ b/src/layout/Breadcrumbs/BreadcrumbList.tsx @@ -0,0 +1,18 @@ +import React, { FC } from 'react'; +import { cn, useComponentTheme } from '@/utils'; +import { BreadcrumbsTheme } from './BreadcrumbsTheme'; + +export interface BreadcrumbListProps + extends React.OlHTMLAttributes { + theme?: BreadcrumbsTheme; +} + +export const BreadcrumbList: FC = ({ + className, + theme: customTheme, + ...rest +}) => { + const theme = useComponentTheme('breadcrumbs', customTheme); + + return
      ; +}; diff --git a/src/layout/Breadcrumbs/BreadcrumbPage.tsx b/src/layout/Breadcrumbs/BreadcrumbPage.tsx new file mode 100644 index 00000000..8dfeef25 --- /dev/null +++ b/src/layout/Breadcrumbs/BreadcrumbPage.tsx @@ -0,0 +1,26 @@ +import { cn, useComponentTheme } from '@/utils'; +import React, { FC } from 'react'; +import { BreadcrumbsTheme } from './BreadcrumbsTheme'; + +export interface BreadcrumbPageProps + extends React.HTMLAttributes { + theme?: BreadcrumbsTheme; +} + +export const BreadcrumbPage: FC = ({ + theme: customTheme, + className, + ...rest +}) => { + const theme = useComponentTheme('breadcrumbs', customTheme); + + return ( + + ); +}; diff --git a/src/layout/Breadcrumbs/BreadcrumbSeparator.tsx b/src/layout/Breadcrumbs/BreadcrumbSeparator.tsx new file mode 100644 index 00000000..bc36b5a0 --- /dev/null +++ b/src/layout/Breadcrumbs/BreadcrumbSeparator.tsx @@ -0,0 +1,29 @@ +import { Arrow } from '@/elements'; +import { cn, useComponentTheme } from '@/utils'; +import React, { FC } from 'react'; +import { BreadcrumbsTheme } from './BreadcrumbsTheme'; + +export interface BreadcrumbSeparatorProps + extends React.LiHTMLAttributes { + theme?: BreadcrumbsTheme; +} + +export const BreadcrumbSeparator: FC = ({ + children = , + className, + theme: customTheme, + ...rest +}) => { + const theme = useComponentTheme('breadcrumbs', customTheme); + + return ( +
    1. + {children} +
    2. + ); +}; diff --git a/src/layout/Breadcrumbs/Breadcrumbs.story.tsx b/src/layout/Breadcrumbs/Breadcrumbs.story.tsx new file mode 100644 index 00000000..9ca27530 --- /dev/null +++ b/src/layout/Breadcrumbs/Breadcrumbs.story.tsx @@ -0,0 +1,49 @@ +import { + BreadcrumbItem, + BreadcrumbLink, + BreadcrumbList, + BreadcrumbPage, + BreadcrumbSeparator, + Breadcrumbs +} from './'; + +export default { + title: 'Components/Layout/Breadcrumbs', + component: Breadcrumbs +}; + +export const Basic = () => ( + + + + Home + + + + Docs + + + + Breadcrumbs + + + +); + +export const WithCustomSeparator = () => ( + + + + Home + + / + + Docs + + / + + Breadcrumbs + + + +); diff --git a/src/layout/Breadcrumbs/Breadcrumbs.tsx b/src/layout/Breadcrumbs/Breadcrumbs.tsx new file mode 100644 index 00000000..efb56665 --- /dev/null +++ b/src/layout/Breadcrumbs/Breadcrumbs.tsx @@ -0,0 +1,23 @@ +import { cn, useComponentTheme } from '@/utils'; +import React, { FC } from 'react'; +import { BreadcrumbsTheme } from './BreadcrumbsTheme'; + +export interface BreadcrumbsProps extends React.HTMLAttributes { + theme?: BreadcrumbsTheme; +} + +export const Breadcrumbs: FC = ({ + theme: customTheme, + className, + ...rest +}) => { + const theme = useComponentTheme('breadcrumbs', customTheme); + + return ( +