diff --git a/package-lock.json b/package-lock.json index a64b142e..c2d0c774 100644 --- a/package-lock.json +++ b/package-lock.json @@ -77,8 +77,6 @@ "integrity": "sha512-cL9tllhqvsQ6r1+d9Invf7nNXg/3BlfL1vvvL/AdH9fZ2l5j0CeBcoq6UjsqHpvyN1v5nXSZgqJZoGeK+ZOAbw==", "dev": true, "dependencies": { - "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents", - "chokidar": "^3.4.0", "commander": "^4.0.1", "convert-source-map": "^1.1.0", "fs-readdir-recursive": "^1.1.0", @@ -7293,7 +7291,6 @@ "dependencies": { "anymatch": "~3.1.1", "braces": "~3.0.2", - "fsevents": "~2.3.1", "glob-parent": "~5.1.0", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", @@ -7513,7 +7510,6 @@ "integrity": "sha512-gnB85c3MGC7Nm9I/FkiasNBOKjOiO1RNuXXarQms37q4QMpWdlbBgD/VnOStA2faG1dpXMv31RFApjX1/QdgWQ==", "dev": true, "dependencies": { - "colors": "^1.1.2", "object-assign": "^4.1.0", "string-width": "^4.2.0" }, @@ -9740,8 +9736,7 @@ "esprima": "^4.0.1", "estraverse": "^5.2.0", "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" + "optionator": "^0.8.1" }, "bin": { "escodegen": "bin/escodegen.js", @@ -10395,9 +10390,6 @@ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", "dev": true, - "dependencies": { - "graceful-fs": "^4.1.6" - }, "optionalDependencies": { "graceful-fs": "^4.1.6" } @@ -11302,7 +11294,6 @@ "minimist": "^1.2.5", "neo-async": "^2.6.0", "source-map": "^0.6.1", - "uglify-js": "^3.1.4", "wordwrap": "^1.0.0" }, "bin": { @@ -12870,7 +12861,6 @@ "@types/node": "*", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", - "fsevents": "^2.1.2", "graceful-fs": "^4.2.4", "jest-regex-util": "^26.0.0", "jest-serializer": "^26.6.2", @@ -13104,7 +13094,6 @@ "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, "dependencies": { - "graceful-fs": "^4.1.6", "universalify": "^2.0.0" }, "optionalDependencies": { @@ -13134,9 +13123,6 @@ "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", "dev": true, - "dependencies": { - "graceful-fs": "^4.1.9" - }, "optionalDependencies": { "graceful-fs": "^4.1.9" } @@ -16976,9 +16962,6 @@ "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.23.0.tgz", "integrity": "sha512-c29LVsqOaLbBHuIbsTxaKENh1N2EQBOHaWv7gkHN4dgRbxSREqDnDbtFJYdpPauS4YCplMSNCABQ6Eeor69bAA==", "dev": true, - "dependencies": { - "clipboard": "^2.0.0" - }, "optionalDependencies": { "clipboard": "^2.0.0" } @@ -19039,9 +19022,6 @@ "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.45.2.tgz", "integrity": "sha512-kRRU7wXzFHUzBIv0GfoFFIN3m9oteY4uAsKllIpQDId5cfnkWF2J130l+27dzDju0E6MScKiV0ZM5Bw8m4blYQ==", "dev": true, - "dependencies": { - "fsevents": "~2.3.1" - }, "bin": { "rollup": "dist/bin/rollup" }, @@ -19332,9 +19312,6 @@ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", "dev": true, - "dependencies": { - "graceful-fs": "^4.1.6" - }, "optionalDependencies": { "graceful-fs": "^4.1.6" } @@ -22145,10 +22122,8 @@ "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==", "dev": true, "dependencies": { - "chokidar": "^3.4.1", "graceful-fs": "^4.1.2", - "neo-async": "^2.5.0", - "watchpack-chokidar2": "^2.0.1" + "neo-async": "^2.5.0" }, "optionalDependencies": { "chokidar": "^3.4.1", @@ -22176,7 +22151,6 @@ "anymatch": "^2.0.0", "async-each": "^1.0.1", "braces": "^2.3.2", - "fsevents": "^1.2.7", "glob-parent": "^3.1.0", "inherits": "^2.0.3", "is-binary-path": "^1.0.0", @@ -24256,8 +24230,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.1.0.tgz", "integrity": "sha512-cauadRPiC3lDxudbgLkHThAhfqLIzp7159z2tNjODxrpOnckOk809rfAj63YdXWreRbLETNOupq1pC4i1NX9xA==", - "dev": true, - "requires": {} + "dev": true }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", @@ -24520,8 +24493,7 @@ "version": "1.6.22", "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-1.6.22.tgz", "integrity": "sha512-TDoPum4SHdfPiGSAaRBw7ECyI8VaHpK8GJugbJIJuqyh6kzw9ZLJZW3HGL3NNrJGxcAixUvqROm+YuQOo5eXtg==", - "dev": true, - "requires": {} + "dev": true }, "@mdx-js/util": { "version": "1.6.22", @@ -26487,8 +26459,7 @@ "version": "0.3.1", "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.3.1.tgz", "integrity": "sha512-HyZ+3Eay8SGaPq7kcFoANZLr4EjeXQ19yjjb9fp6B0PHHpvZoe00jdsnpnooMEbx9J5rQ93nxPUG3MQmXVxGMQ==", - "dev": true, - "requires": {} + "dev": true }, "@testing-library/dom": { "version": "7.30.4", @@ -27215,8 +27186,7 @@ "version": "5.3.1", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", - "dev": true, - "requires": {} + "dev": true }, "acorn-node": { "version": "1.8.2", @@ -27292,15 +27262,13 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", - "dev": true, - "requires": {} + "dev": true }, "ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "requires": {} + "dev": true }, "alphanum-sort": { "version": "1.0.2", @@ -27980,8 +27948,7 @@ "version": "0.3.7", "resolved": "https://registry.npmjs.org/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.7.tgz", "integrity": "sha512-squySRkf+6JGnvjoUtDEjSREJEBirnXi9NqP6rjSYsylxQxqBTz+pkmf395i9E2zsvmYUaI40BHo6SqZUdydlw==", - "dev": true, - "requires": {} + "dev": true }, "babel-plugin-polyfill-corejs2": { "version": "0.2.0", @@ -32391,8 +32358,7 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "dev": true, - "requires": {} + "dev": true }, "ieee754": { "version": "1.2.1", @@ -33581,8 +33547,7 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-7.1.2.tgz", "integrity": "sha512-O8DMCl32V34RrD+ZHxcAPc2+kYytuDIoQYjY36RVdsLK7uHjgNVvFec4yv0X6LgB4YEZgSvK5QtFi5YVqEpoMA==", - "dev": true, - "requires": {} + "dev": true }, "marked": { "version": "1.2.9", @@ -35844,8 +35809,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", - "dev": true, - "requires": {} + "dev": true }, "postcss-modules-local-by-default": { "version": "4.0.0", @@ -36696,8 +36660,7 @@ "version": "5.1.4", "resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.1.4.tgz", "integrity": "sha512-WOEpRNz8Oo2SEU4eYQ279jEKFSjpFPa9Vi2U/K0DGwP9wOQ8wYkJcNSd5Qbv1L8OFvyKDCbWekjftXaU5mbmtg==", - "dev": true, - "requires": {} + "dev": true }, "react-dev-utils": { "version": "11.0.4", @@ -36955,8 +36918,7 @@ "version": "1.22.0", "resolved": "https://registry.npmjs.org/react-docgen-typescript/-/react-docgen-typescript-1.22.0.tgz", "integrity": "sha512-MPLbF8vzRwAG3GcjdL+OHQlhgtWsLTXs+7uJiHfEeT3Ur7IsZaNYqRTLQ9sj2nB6M6jylcPCeCmH7qbszJmecg==", - "dev": true, - "requires": {} + "dev": true }, "react-docgen-typescript-plugin": { "version": "0.6.3", @@ -38047,8 +38009,7 @@ "version": "2.2.4", "resolved": "https://registry.npmjs.org/rollup-plugin-peer-deps-external/-/rollup-plugin-peer-deps-external-2.2.4.tgz", "integrity": "sha512-AWdukIM1+k5JDdAqV/Cxd+nejvno2FVLVeZ74NKggm3Q5s9cbbcOgUPGdbxPi4BXu7xGaZ8HG12F+thImYu/0g==", - "dev": true, - "requires": {} + "dev": true }, "rollup-plugin-postcss": { "version": "3.1.8", @@ -40236,8 +40197,7 @@ "version": "1.2.5", "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.2.5.tgz", "integrity": "sha512-gN3vgMISAgacF7sqsLPByqoePooY3n2emTH59Ur5d/M8eg4WTWu1xp8i8DHjohftIyEx0S08RiYxbffr4j8Peg==", - "dev": true, - "requires": {} + "dev": true }, "use-composed-ref": { "version": "1.1.0", @@ -40252,8 +40212,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.1.tgz", "integrity": "sha512-L7Evj8FGcwo/wpbv/qvSfrkHFtOpCzvM5yl2KVyDJoylVuSvzphiiasmjgQPttIGBAy2WKiBNR98q8w7PiNgKQ==", - "dev": true, - "requires": {} + "dev": true }, "use-latest": { "version": "1.2.0", @@ -40749,8 +40708,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/webpack-filter-warnings-plugin/-/webpack-filter-warnings-plugin-1.2.1.tgz", "integrity": "sha512-Ez6ytc9IseDMLPo0qCuNNYzgtUl8NovOqjIq4uAU8LTD4uoa1w1KpZyyzFtLTEMZpkkOkLfL9eN+KGYdk1Qtwg==", - "dev": true, - "requires": {} + "dev": true }, "webpack-hot-middleware": { "version": "2.25.0", diff --git a/rollup.config.js b/rollup.config.js index 62b0d055..770d915c 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -28,6 +28,7 @@ export default { card: 'src/components/Card/index.tsx', badge: 'src/components/Badge/index.tsx', alert: 'src/components/Alert/index.tsx', + accordian: 'src/components/Accordian/index.tsx', tabs: 'src/components/Tabs/index.tsx', menu: 'src/components/Menu/index.tsx', modal: 'src/components/Modal/index.tsx', diff --git a/src/components/Accordion/Accordion.module.css b/src/components/Accordion/Accordion.module.css new file mode 100644 index 00000000..dce8da93 --- /dev/null +++ b/src/components/Accordion/Accordion.module.css @@ -0,0 +1,72 @@ +.sbui-accordion-container { + @apply flex flex-col rounded-md; +} + +.sbui-accordion-container--bordered { + @apply border border-solid; + @apply bg-white border-gray-200; + @apply dark:bg-dark-700 dark:border-darkmode; +} + +.sbui-accordion-item { + @apply w-full; +} + +.sbui-accordion-item__button { + @apply flex justify-between w-full text-left cursor-pointer px-6 py-4 border-0 border-solid font-medium text-base rounded-t-md bg-transparent; + + @apply border-gray-200 text-gray-500 hover:text-gray-600; + @apply dark:border-darkmode dark:text-dark-200 dark:hover:text-white; + + font-family: inherit; + font-weight: inherit; +} + +.sbui-accordion-item__button .sbui-typography-text { + @apply w-full max-w-none; +} + +.sbui-accordion-item__button .sbui-typography-text:last-child { + @apply pl-2; +} + +.sbui-accordion-item__button .sbui-icon { + @apply mt-px; +} + +.sbui-accordion-item__button--open .sbui-icon { + @apply transform rotate-180; +} + +.sbui-accordion-container--bordered + .sbui-accordion-item__button:not(:first-child) { + @apply rounded-none border-t border-gray-200 dark:border-darkmode; +} + +.sbui-accordion-item__panel { + @apply px-6 py-4; +} + +.sbui-accordion-item__panel--enter { + @apply transition-max-height ease-in-out duration-700 overflow-hidden; +} + +.sbui-accordion-item__panel--enterFrom { + @apply max-h-0; +} + +.sbui-accordion-item__panel--enterTo { + @apply max-h-screen; +} + +.sbui-accordion-item__panel--leave { + @apply transition-max-height ease-in-out duration-300 overflow-hidden; +} + +.sbui-accordion-item__panel--leaveFrom { + @apply max-h-screen; +} + +.sbui-accordion-item__panel--leaveTo { + @apply max-h-0; +} diff --git a/src/components/Accordion/Accordion.stories.tsx b/src/components/Accordion/Accordion.stories.tsx new file mode 100644 index 00000000..70e7aa35 --- /dev/null +++ b/src/components/Accordion/Accordion.stories.tsx @@ -0,0 +1,63 @@ +import React from 'react' + +import { Accordion } from '.' +import { Typography } from '../Typography' +import { IconArrowUp } from '../Icon/icons/IconArrowUp' + +export default { + title: 'Displays/Accordion', + component: Accordion, +} + +export const Default = (args: any) => ( + + + + Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur + amet labore. + + + +) + +Default.args = {} + +const Multiple = (args: any) => ( + + + + Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur + amet labore. + + + + + Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur + amet labore. + + + +) + +export const withMultiple = Multiple.bind({}) +withMultiple.args = {} + +export const withBorder = Multiple.bind({}) +withBorder.args = { + bordered: true, +} + +export const withDefaultActive = Multiple.bind({}) +withDefaultActive.args = { + defaultActiveId: [1], +} + +export const withIconLeft = Multiple.bind({}) +withIconLeft.args = { + iconPosition: 'left', +} + +export const withCustomIcon = Multiple.bind({}) +withCustomIcon.args = { + icon: , +} diff --git a/src/components/Accordion/Accordion.tsx b/src/components/Accordion/Accordion.tsx new file mode 100644 index 00000000..f79d7af0 --- /dev/null +++ b/src/components/Accordion/Accordion.tsx @@ -0,0 +1,131 @@ +import React, { createContext, useCallback, useContext, useEffect } from 'react' +import { Disclosure, Transition } from '@headlessui/react' +// @ts-ignore +import AccordionStyles from './Accordion.module.css' +import { IconChevronUp } from '../Icon/icons/IconChevronUp' +import Typography from '../Typography' + +type ContextValue = Required< + Pick +> + +const AccordionContext = createContext({ + defaultActiveId: [], + icon: , + iconPosition: 'right', + onChange: undefined, +}) + +interface AccordionProps { + children?: React.ReactNode + className?: string + defaultActiveId?: (string | number)[] + icon?: React.ReactNode + iconPosition?: 'left' | 'right' + bordered?: boolean + onChange?: (item: { + label: string + id?: string | number + open: boolean + }) => void +} + +function Accordion({ + children, + className, + defaultActiveId = [], + icon = , + iconPosition = 'right', + bordered, + onChange, +}: AccordionProps) { + let containerClasses = [AccordionStyles['sbui-accordion-container']] + if (bordered) { + containerClasses.push(AccordionStyles['sbui-accordion-container--bordered']) + } + if (className) { + containerClasses.push(className) + } + + const contextValue = { + defaultActiveId, + icon, + iconPosition, + onChange, + } + + return ( + +
{children}
+
+ ) +} + +interface ItemProps { + children?: React.ReactNode + className?: string + label: string + id?: string | number +} + +export function Item({ children, className, label, id }: ItemProps) { + const { defaultActiveId, icon, iconPosition, onChange } = useContext( + AccordionContext + ) + + let panelClasses = [AccordionStyles['sbui-accordion-item__panel']] + + let buttonClasses = [AccordionStyles['sbui-accordion-item__button']] + if (className) { + buttonClasses.push(className) + } + + const isDefaultActive = defaultActiveId.includes(id) + + const handleOnChange = useCallback( + (open: boolean) => () => { + if (onChange) { + onChange({ id, label, open }) + } + }, + [onChange, id, label] + ) + + return ( + + {({ open }) => ( + <> + + {iconPosition === 'left' && icon} + {label} + {iconPosition === 'right' && icon} + + + + {children} + + + + )} + + ) +} + +Accordion.Item = Item +export default Accordion diff --git a/src/components/Accordion/index.tsx b/src/components/Accordion/index.tsx new file mode 100644 index 00000000..971fd8af --- /dev/null +++ b/src/components/Accordion/index.tsx @@ -0,0 +1 @@ +export { default as Accordion } from './Accordion' diff --git a/src/components/Icon/Icon.tsx b/src/components/Icon/Icon.tsx index 6d43b308..89600729 100644 --- a/src/components/Icon/Icon.tsx +++ b/src/components/Icon/Icon.tsx @@ -50,7 +50,7 @@ function Icon({ }: Props) { return ( - {({ contextSize }) => { + {({ contextSize, className: contextClassName }) => { const defaultSizes: StringMap = { tiny: 14, small: 18, @@ -88,6 +88,11 @@ function Icon({ // default these icons to use 'currentColor' ie, the text color const noColor = !color && !fill && !stroke + let classes = ['sbui-icon', className] + if (contextClassName) { + classes.push(contextClassName) + } + const Icon = ( // custom SVG file diff --git a/src/components/Icon/IconBase.tsx b/src/components/Icon/IconBase.tsx index 7a11853a..3ccf1de2 100644 --- a/src/components/Icon/IconBase.tsx +++ b/src/components/Icon/IconBase.tsx @@ -52,7 +52,7 @@ function IconBase({ }: Props) { return ( - {({ contextSize }) => { + {({ contextSize, className: contextClassName }) => { const defaultSizes: StringMap = { tiny: 14, small: 18, @@ -93,11 +93,16 @@ function IconBase({ // default these icons to use 'currentColor' ie, the text color const noColor = !color && !fill && !stroke + let classes = ['sbui-icon', className] + if (contextClassName) { + classes.push(contextClassName) + } + const IconComponent = () => ( diff --git a/src/components/Icon/IconContext.tsx b/src/components/Icon/IconContext.tsx index 77d4cc25..4d2198bd 100644 --- a/src/components/Icon/IconContext.tsx +++ b/src/components/Icon/IconContext.tsx @@ -1,7 +1,10 @@ import { createContext } from 'react' +type ContextValue = { contextSize?: string; className?: string } + // Make sure the shape of the default value passed to // createContext matches the shape that the consumers expect! -export const IconContext = createContext({ +export const IconContext = createContext({ contextSize: '', + className: '', }) diff --git a/src/index.tsx b/src/index.tsx index a0d0ac89..557ce5dd 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -10,6 +10,7 @@ export * from './components/Card/index' export * from './components/Badge/index' export * from './components/Tabs/index' export * from './components/Alert/index' +export * from './components/Accordion/index' // NAV diff --git a/tailwind.config.js b/tailwind.config.js index f6373ff7..21cb877f 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -182,6 +182,10 @@ module.exports = { dark: brand[300], }, }, + + transitionProperty: { + 'max-height': 'max-height', + }, }, }, plugins: [require('@tailwindcss/forms'), require('@tailwindcss/typography')],