From fdce5d0c8c8cb242ff57c253bb8e5d4590e87a94 Mon Sep 17 00:00:00 2001 From: SerhiiTsybulskyi Date: Fri, 1 Mar 2024 15:32:49 +0200 Subject: [PATCH] tailwind css theme POC --- .storybook/main.ts | 3 +- .storybook/preview-head.html | 20 +- .storybook/preview.tsx | 20 +- package-lock.json | 618 ++++++++++++++++++++++++++- package.json | 5 +- postcss.config.cjs | 4 +- src/elements/Button/Button.story.tsx | 47 ++ src/elements/Button/Button.tsx | 43 +- src/elements/Button/ButtonTheme.ts | 121 ++++++ src/index.css | 6 + src/utils/Theme/TW/ThemeProvider.tsx | 53 +++ src/utils/Theme/TW/darkTheme.ts | 25 ++ src/utils/Theme/TW/index.ts | 3 + src/utils/Theme/TW/theme.ts | 21 + src/utils/helpers/cloneDeep.ts | 15 + src/utils/helpers/isObject.ts | 10 + src/utils/helpers/mergeDeep.ts | 36 ++ tailwind.config.ts | 18 + 18 files changed, 1027 insertions(+), 41 deletions(-) create mode 100644 src/elements/Button/ButtonTheme.ts create mode 100644 src/index.css create mode 100644 src/utils/Theme/TW/ThemeProvider.tsx create mode 100644 src/utils/Theme/TW/darkTheme.ts create mode 100644 src/utils/Theme/TW/index.ts create mode 100644 src/utils/Theme/TW/theme.ts create mode 100644 src/utils/helpers/cloneDeep.ts create mode 100644 src/utils/helpers/isObject.ts create mode 100644 src/utils/helpers/mergeDeep.ts create mode 100644 tailwind.config.ts diff --git a/.storybook/main.ts b/.storybook/main.ts index 1995bc38..0ed73e97 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -7,7 +7,8 @@ const config: StorybookConfig = { ], addons: [ '@storybook/addon-storysource', - '@storybook/addon-essentials' + '@storybook/addon-essentials', + '@storybook/addon-themes' ], framework: { name: '@storybook/react-vite', diff --git a/.storybook/preview-head.html b/.storybook/preview-head.html index 13322db5..1524be7e 100644 --- a/.storybook/preview-head.html +++ b/.storybook/preview-head.html @@ -8,13 +8,25 @@ -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; color: var(--body-color) !important; - background: var(--body-background) !important; --icon-block-color: #fff; --icon-block-background: #000; + + &.light { + background: #e6e6e6; + } + &.dark { + background: var(--body-background); + } } - .sb-show-main.sb-main-centered { - color: var(--body-color) !important; - background: var(--body-background) !important; + + .light .sb-show-main.sb-main-centered { + color: var(--body-color); + background: #e6e6e6; + } + + .dark .sb-show-main.sb-main-centered { + color: var(--body-color); + background: var(--body-background); } svg { overflow: visible; diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index beb656b9..2e2ecbb1 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -1,16 +1,30 @@ -import { ThemeProvider, darkTheme } from '../src/utils/Theme'; import theme from './theme'; import { Preview } from '@storybook/react'; +import { withThemeByClassName } from '@storybook/addon-themes'; + +import { ThemeProvider, darkTheme } from '../src/utils/Theme'; +import { ThemeProvider as TWThemeProvider } from '../src/utils/Theme/TW'; + +import '../src/index.css'; const withProvider = (Story, context) => ( - + + + ); const preview: Preview = { decorators: [ - withProvider + withProvider, + withThemeByClassName({ + themes: { + light: 'light', + dark: 'dark', + }, + defaultTheme: 'dark', + }), ], parameters: { layout: 'centered', diff --git a/package-lock.json b/package-lock.json index 5f1d6965..12427bda 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "reablocks", - "version": "5.8.9", + "version": "5.9.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "reablocks", - "version": "5.8.9", + "version": "5.9.0", "license": "Apache-2.0", "dependencies": { "@marko19907/string-to-color": "^1.0.0", @@ -28,13 +28,15 @@ "react-18-input-autosize": "^3.0.0", "react-fast-compare": "^3.2.2", "react-highlight-words": "^0.20.0", - "react-textarea-autosize": "^8.5.3" + "react-textarea-autosize": "^8.5.3", + "tailwind-merge": "^2.2.1" }, "devDependencies": { "@storybook/addon-docs": "^7.4.6", "@storybook/addon-essentials": "^7.4.6", "@storybook/addon-mdx-gfm": "^7.4.6", "@storybook/addon-storysource": "^7.4.6", + "@storybook/addon-themes": "^7.6.17", "@storybook/addons": "^7.4.6", "@storybook/react": "^7.4.6", "@storybook/react-vite": "^7.4.6", @@ -64,6 +66,7 @@ "react-hook-form": "^7.47.0", "rollup-plugin-peer-deps-external": "2.2.4", "storybook": "^7.4.6", + "tailwindcss": "^3.4.1", "typescript": "^4.9.5", "vite": "^4.4.11", "vite-plugin-checker": "^0.6.2", @@ -87,6 +90,18 @@ "node": ">=0.10.0" } }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@ampproject/remapping": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", @@ -2174,11 +2189,11 @@ "dev": true }, "node_modules/@babel/runtime": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.6.tgz", - "integrity": "sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.0.tgz", + "integrity": "sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==", "dependencies": { - "regenerator-runtime": "^0.13.11" + "regenerator-runtime": "^0.14.0" }, "engines": { "node": ">=6.9.0" @@ -5342,6 +5357,19 @@ } } }, + "node_modules/@storybook/addon-themes": { + "version": "7.6.17", + "resolved": "https://registry.npmjs.org/@storybook/addon-themes/-/addon-themes-7.6.17.tgz", + "integrity": "sha512-i/dI3GKlJmQv0Di9HM3fKJvFIab55kvva0vXCGBkqSw3wYoZuq++npkWeDnjjZUvggV12H2RTdGqYwMCXB/GHg==", + "dev": true, + "dependencies": { + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, "node_modules/@storybook/addon-toolbars": { "version": "7.4.6", "resolved": "https://registry.npmjs.org/@storybook/addon-toolbars/-/addon-toolbars-7.4.6.tgz", @@ -7837,6 +7865,12 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -7856,6 +7890,12 @@ "integrity": "sha512-jlpIfsOoNoafl92Sz//64uQHGSyMrD2vYG5d8o2a4qGvyNCvXur7bzIsWtAC/6flI2RYAp3kv8rsfBtaLm7w0g==", "dev": true }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true + }, "node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -8507,6 +8547,15 @@ "node": ">=6" } }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001549", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001549.tgz", @@ -9697,6 +9746,12 @@ "detect-port": "bin/detect-port.js" } }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true + }, "node_modules/diff": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", @@ -9727,6 +9782,12 @@ "node": ">=8" } }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -12891,6 +12952,15 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/jiti": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", + "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", + "dev": true, + "bin": { + "jiti": "bin/jiti.js" + } + }, "node_modules/jju": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", @@ -14536,6 +14606,17 @@ "integrity": "sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==", "dev": true }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, "node_modules/name-initials": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/name-initials/-/name-initials-0.1.3.tgz", @@ -14749,6 +14830,15 @@ "node": ">=0.10.0" } }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/object-inspect": { "version": "1.12.3", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", @@ -15721,6 +15811,59 @@ "postcss": "^8.4" } }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-import/node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dev": true, + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, "node_modules/postcss-lab-function": { "version": "6.0.7", "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-6.0.7.tgz", @@ -15749,6 +15892,65 @@ "postcss": "^8.4" } }, + "node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-load-config/node_modules/lilconfig": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz", + "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/postcss-load-config/node_modules/yaml": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.0.tgz", + "integrity": "sha512-j9iR8g+/t0lArF4V6NE/QCfT+CO7iLqrXAHZbJdo+LfjqP1vR8Fg5bSiaq6Q2lOD1AUEVrEVIgABvBFYojJVYQ==", + "dev": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/postcss-logical": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-7.0.0.tgz", @@ -16719,6 +16921,24 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/read-cache/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", @@ -16938,9 +17158,9 @@ } }, "node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, "node_modules/regenerator-transform": { "version": "0.15.2", @@ -17952,6 +18172,37 @@ "node": ">=0.4.0" } }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -17999,6 +18250,72 @@ "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-5.3.3.tgz", "integrity": "sha512-QD9qKY3StfbZqWOPLp0++pOrAVb/HbUi5xCc8cUo4XjP19808oaMiDzn0leBY5mCespIBM0CIZePzZjgzR83kA==" }, + "node_modules/tailwind-merge": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.2.1.tgz", + "integrity": "sha512-o+2GTLkthfa5YUt4JxPfzMIpQzZ3adD1vLVkvKE1Twl9UAhGsEbIZhHHZVRttyW177S8PDJI3bTQNaebyofK3Q==", + "dependencies": { + "@babel/runtime": "^7.23.7" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.1.tgz", + "integrity": "sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==", + "dev": true, + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.0", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.19.1", + "lilconfig": "^2.1.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.23", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.1", + "postcss-nested": "^6.0.1", + "postcss-selector-parser": "^6.0.11", + "resolve": "^1.22.2", + "sucrase": "^3.32.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss/node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/tar": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", @@ -18222,6 +18539,27 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", @@ -18407,6 +18745,12 @@ "node": ">=6.10" } }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true + }, "node_modules/tsconfck": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-2.1.2.tgz", @@ -20168,6 +20512,12 @@ "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", "dev": true }, + "@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true + }, "@ampproject/remapping": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", @@ -21612,11 +21962,11 @@ "dev": true }, "@babel/runtime": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.6.tgz", - "integrity": "sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.0.tgz", + "integrity": "sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==", "requires": { - "regenerator-runtime": "^0.13.11" + "regenerator-runtime": "^0.14.0" } }, "@babel/template": { @@ -23390,6 +23740,15 @@ "tiny-invariant": "^1.3.1" } }, + "@storybook/addon-themes": { + "version": "7.6.17", + "resolved": "https://registry.npmjs.org/@storybook/addon-themes/-/addon-themes-7.6.17.tgz", + "integrity": "sha512-i/dI3GKlJmQv0Di9HM3fKJvFIab55kvva0vXCGBkqSw3wYoZuq++npkWeDnjjZUvggV12H2RTdGqYwMCXB/GHg==", + "dev": true, + "requires": { + "ts-dedent": "^2.0.0" + } + }, "@storybook/addon-toolbars": { "version": "7.4.6", "resolved": "https://registry.npmjs.org/@storybook/addon-toolbars/-/addon-toolbars-7.4.6.tgz", @@ -25252,6 +25611,12 @@ "color-convert": "^2.0.1" } }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true + }, "anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -25268,6 +25633,12 @@ "integrity": "sha512-jlpIfsOoNoafl92Sz//64uQHGSyMrD2vYG5d8o2a4qGvyNCvXur7bzIsWtAC/6flI2RYAp3kv8rsfBtaLm7w0g==", "dev": true }, + "arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -25738,6 +26109,12 @@ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, + "camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true + }, "caniuse-lite": { "version": "1.0.30001549", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001549.tgz", @@ -26582,6 +26959,12 @@ "debug": "4" } }, + "didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true + }, "diff": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", @@ -26603,6 +26986,12 @@ "path-type": "^4.0.0" } }, + "dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true + }, "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -28918,6 +29307,12 @@ } } }, + "jiti": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", + "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", + "dev": true + }, "jju": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", @@ -30065,6 +30460,17 @@ "integrity": "sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==", "dev": true }, + "mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "requires": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, "name-initials": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/name-initials/-/name-initials-0.1.3.tgz", @@ -30225,6 +30631,12 @@ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" }, + "object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true + }, "object-inspect": { "version": "1.12.3", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", @@ -30807,6 +31219,39 @@ "postcss-value-parser": "^4.2.0" } }, + "postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "dependencies": { + "resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "requires": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + } + } + }, + "postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dev": true, + "requires": { + "camelcase-css": "^2.0.1" + } + }, "postcss-lab-function": { "version": "6.0.7", "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-6.0.7.tgz", @@ -30819,6 +31264,30 @@ "@csstools/postcss-progressive-custom-properties": "^3.0.2" } }, + "postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "dev": true, + "requires": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "dependencies": { + "lilconfig": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz", + "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==", + "dev": true + }, + "yaml": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.0.tgz", + "integrity": "sha512-j9iR8g+/t0lArF4V6NE/QCfT+CO7iLqrXAHZbJdo+LfjqP1vR8Fg5bSiaq6Q2lOD1AUEVrEVIgABvBFYojJVYQ==", + "dev": true + } + } + }, "postcss-logical": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-7.0.0.tgz", @@ -31465,6 +31934,23 @@ "use-latest": "^1.2.1" } }, + "read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "requires": { + "pify": "^2.3.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true + } + } + }, "read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", @@ -31634,9 +32120,9 @@ } }, "regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, "regenerator-transform": { "version": "0.15.2", @@ -32395,6 +32881,29 @@ } } }, + "sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "dependencies": { + "commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true + } + } + }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -32433,6 +32942,57 @@ "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-5.3.3.tgz", "integrity": "sha512-QD9qKY3StfbZqWOPLp0++pOrAVb/HbUi5xCc8cUo4XjP19808oaMiDzn0leBY5mCespIBM0CIZePzZjgzR83kA==" }, + "tailwind-merge": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.2.1.tgz", + "integrity": "sha512-o+2GTLkthfa5YUt4JxPfzMIpQzZ3adD1vLVkvKE1Twl9UAhGsEbIZhHHZVRttyW177S8PDJI3bTQNaebyofK3Q==", + "requires": { + "@babel/runtime": "^7.23.7" + } + }, + "tailwindcss": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.1.tgz", + "integrity": "sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==", + "dev": true, + "requires": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.0", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.19.1", + "lilconfig": "^2.1.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.23", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.1", + "postcss-nested": "^6.0.1", + "postcss-selector-parser": "^6.0.11", + "resolve": "^1.22.2", + "sucrase": "^3.32.0" + }, + "dependencies": { + "resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "requires": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + } + } + }, "tar": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", @@ -32609,6 +33169,24 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "requires": { + "any-promise": "^1.0.0" + } + }, + "thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "requires": { + "thenify": ">= 3.1.0 < 4" + } + }, "through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", @@ -32762,6 +33340,12 @@ "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", "dev": true }, + "ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true + }, "tsconfck": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-2.1.2.tgz", diff --git a/package.json b/package.json index 8ecc31b9..9fb92e02 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,8 @@ "react-18-input-autosize": "^3.0.0", "react-fast-compare": "^3.2.2", "react-highlight-words": "^0.20.0", - "react-textarea-autosize": "^8.5.3" + "react-textarea-autosize": "^8.5.3", + "tailwind-merge": "^2.2.1" }, "peerDependencies": { "react": ">=16", @@ -77,6 +78,7 @@ "@storybook/addon-essentials": "^7.4.6", "@storybook/addon-mdx-gfm": "^7.4.6", "@storybook/addon-storysource": "^7.4.6", + "@storybook/addon-themes": "^7.6.17", "@storybook/addons": "^7.4.6", "@storybook/react": "^7.4.6", "@storybook/react-vite": "^7.4.6", @@ -106,6 +108,7 @@ "react-hook-form": "^7.47.0", "rollup-plugin-peer-deps-external": "2.2.4", "storybook": "^7.4.6", + "tailwindcss": "^3.4.1", "typescript": "^4.9.5", "vite": "^4.4.11", "vite-plugin-checker": "^0.6.2", diff --git a/postcss.config.cjs b/postcss.config.cjs index 8448aa7c..7101f10e 100644 --- a/postcss.config.cjs +++ b/postcss.config.cjs @@ -2,6 +2,8 @@ module.exports = { plugins: [ require('postcss-nested'), require('postcss-preset-env')({ stage: 1 }), - require('autoprefixer') + require('autoprefixer'), + require('tailwindcss/nesting')('postcss-nesting'), + require('tailwindcss'), ] }; diff --git a/src/elements/Button/Button.story.tsx b/src/elements/Button/Button.story.tsx index 1188793d..959d611c 100644 --- a/src/elements/Button/Button.story.tsx +++ b/src/elements/Button/Button.story.tsx @@ -1,4 +1,5 @@ import { Button } from './Button'; +import { ReaBlocksTheme, ThemeProvider } from '../../utils/Theme/TW'; export default { title: 'Components/Elements/Button', @@ -33,6 +34,14 @@ export const Sizes = () => ( ); +export const FullWidth = () => ( +
+ +
+); + const BellIcon = () => ( ( ); + +export const CustomTheme = () => { + const customTheme: ReaBlocksTheme = { + components: { + button: { + base: 'bg-green-400 text-black dark:text-white', + variants: { + filled: + 'bg-purple-100 hover:bg-purple-50 dark:bg-green-500 dark:hover:bg-green-200', + outline: + 'bg-transparent dark:border-green-400 border dark:text-green-100', + text: 'bg-transparent border-0 dark:text-green-100' + }, + sizes: { + small: 'p-2', + medium: 'p-3', + large: 'p-4' + } + } + } + }; + + return ( + +
+ + + +
+
+
+ + + +
+ + ); +}; diff --git a/src/elements/Button/Button.tsx b/src/elements/Button/Button.tsx index eb096b5a..12edda4b 100644 --- a/src/elements/Button/Button.tsx +++ b/src/elements/Button/Button.tsx @@ -1,8 +1,9 @@ import React, { FC, forwardRef, Ref, useContext } from 'react'; import classNames from 'classnames'; import { motion } from 'framer-motion'; -import css from './Button.module.css'; import { ButtonGroupContext } from './ButtonGroupContext'; +import { useComponentTheme } from '../../utils/Theme/TW'; +import { twMerge } from 'tailwind-merge'; export interface ButtonProps extends Omit< @@ -78,39 +79,53 @@ export const Button: FC = forwardRef( }: ButtonProps, ref: Ref ) => { + const theme = useComponentTheme('button'); + const { variant: groupVariant, size: groupSize } = useContext(ButtonGroupContext); + const isGroup = !!groupVariant && !!groupSize; + return ( {startAdornment && (
{startAdornment}
)} {children} {endAdornment && ( -
+
{endAdornment}
)} diff --git a/src/elements/Button/ButtonTheme.ts b/src/elements/Button/ButtonTheme.ts new file mode 100644 index 00000000..aa08ca89 --- /dev/null +++ b/src/elements/Button/ButtonTheme.ts @@ -0,0 +1,121 @@ +export interface ReaBlocksButtonTheme { + base?: string; + disabled?: string; + fullWidth?: string; + group?: string; + groupText?: string; + adornment?: { + base?: string; + start?: string; + end?: string; + sizes?: { + small?: string; + medium?: string; + large?: string; + }; + }; + variants?: { + filled?: string; + outline?: string; + text?: string; + }; + colors?: { + primary?: string; + secondary?: string; + success?: string; + warning?: string; + error?: string; + }; + sizes?: { + small?: string; + medium?: string; + large?: string; + }; +} + +const baseTheme: ReaBlocksButtonTheme = { + base: 'inline-flex whitespace-no-wrap select-none items-center justify-center px-2.5 py-1 rounded border', + disabled: 'disabled:cursor-not-allowed', + fullWidth: 'block w-full', + group: 'rounded-none first:rounded-s last:rounded-e', + groupText: + 'border border-y-transparent border-l-transparent last:border-r-transparent hover:bg-initial', + adornment: { + base: 'flex', + sizes: { + small: '[&>svg]:w-3 [&>svg]:h-3', + medium: '[&>svg]:w-4 [&>svg]:h-4', + large: '[&>svg]:w-5 [&>svg]:h-5' + } + }, + sizes: { + small: 'text-xs px-5px py-0.5 leading-[normal]', + medium: 'text-base px-2.5 py-5px leading-[normal]', + large: 'text-xl px-5 py-2.5 leading-[normal]' + } +}; + +// Just make 2 different themes for light and dark instead theme mode +const lightTheme: ReaBlocksButtonTheme = { + base: 'text-black', + disabled: 'disabled:bg-gray-400 disabled:text-gray-50', + variants: { + filled: 'bg-blue-50 border-blue-500 hover:bg-blue-100', + outline: 'bg-transparent border-grey border', + text: 'bg-transparent border-0' + }, + colors: { + primary: 'bg-blue-50 hover:bg-blue-100', + secondary: 'bg-gray-100 hover:bg-gray-100', + success: 'bg-green-50 hover:bg-green-100', + warning: 'bg-orange-50 hover:bg-orange-100', + error: 'bg-red-50 hover:bg-red-100' + } +}; + +// Just make 2 different themes for light and dark instead theme mode +const darkTheme: ReaBlocksButtonTheme = { + base: 'dark:border-gray-500 dark:bg-gray-200 dark:hover:bg-gray-100 dark:text-gray-100', + disabled: 'dark:disabled:bg-gray-500 dark:disabled:text-gray-50', + variants: { + filled: 'dark:bg-gray-200 dark:text-white dark:bg-blue-300', + outline: 'dark:bg-transparent dark:border-grey dark:border', + text: 'dark:bg-transparent dark:border-0' + }, + colors: { + primary: 'dark:bg-blue-400 dark:text-white dark:border-blue-400', + secondary: 'dark:bg-blue-500 dark:text-white dark:border-blue-500', + success: 'dark:bg-green-100 dark:text-white dark:border-green-100', + warning: 'dark:bg-orange-100 dark:text-white dark:border-orange-100', + error: 'dark:bg-red-100 dark:text-white dark:border-red-100' + } +}; + +// TODO write utility to merge values from base, light and dark themes +export const buttonTheme = { + base: [baseTheme.base, lightTheme.base, darkTheme.base].join(' '), + disabled: [baseTheme.disabled, lightTheme.disabled, darkTheme.disabled].join( + ' ' + ), + fullWidth: baseTheme.fullWidth, + group: baseTheme.group, + groupText: baseTheme.groupText, + adornment: baseTheme.adornment, + variants: { + filled: [lightTheme.variants.filled, darkTheme.variants.filled].join(' '), + outline: [lightTheme.variants.outline, darkTheme.variants.outline].join( + ' ' + ), + text: [lightTheme.variants.text, darkTheme.variants.text].join(' ') + }, + colors: { + primary: [lightTheme.colors.primary, darkTheme.colors.primary].join(' '), + secondary: [lightTheme.colors.secondary, darkTheme.colors.secondary].join( + ' ' + ), + success: [lightTheme.colors.success, darkTheme.colors.success].join(' '), + warning: [lightTheme.colors.warning, darkTheme.colors.warning].join(' '), + error: [lightTheme.colors.error, darkTheme.colors.error].join(' ') + }, + sizes: baseTheme.sizes +}; diff --git a/src/index.css b/src/index.css new file mode 100644 index 00000000..e8fa7ef3 --- /dev/null +++ b/src/index.css @@ -0,0 +1,6 @@ +/* Need to generate styles for predefined themes */ +/* npx tailwindcss -i ./src/input.css -o ./src/output.css */ + +@import 'tailwindcss/base'; +@import 'tailwindcss/components'; +@import 'tailwindcss/utilities'; diff --git a/src/utils/Theme/TW/ThemeProvider.tsx b/src/utils/Theme/TW/ThemeProvider.tsx new file mode 100644 index 00000000..6404ab32 --- /dev/null +++ b/src/utils/Theme/TW/ThemeProvider.tsx @@ -0,0 +1,53 @@ +import React, { createContext, FC, useContext, useState } from 'react'; +import { ReaBlocksComponents, ReaBlocksTheme } from './theme'; +import { theme as defaultTheme } from './theme'; +import { mergeDeep } from '../../helpers/mergeDeep'; + +interface ThemeContextProps { + activeTheme: ReaBlocksTheme; + updateTheme: (newTheme: ReaBlocksTheme) => void; +} + +const ThemeContext = createContext(null); + +interface ThemeProviderProps { + theme?: ReaBlocksTheme; + children: React.ReactNode; +} + +export const ThemeProvider: FC = ({ children, theme }) => { + const [activeTheme, setActiveTheme] = useState( + theme ? mergeDeep(defaultTheme, theme) : defaultTheme + ); + + const updateTheme = (newTheme: ReaBlocksTheme) => { + setActiveTheme({ ...activeTheme, ...newTheme }); + }; + + return ( + + {children} + + ); +}; + +export const useTheme = () => { + const context = useContext(ThemeContext); + + if (!context) { + throw new Error('useTheme must be used within a ThemeProvider'); + } + + return context; +}; + +export const useComponentTheme = (component: keyof ReaBlocksComponents) => { + const { activeTheme } = useTheme(); + + const componentTheme = activeTheme.components[component]; + if (!componentTheme) { + throw new Error(`component ${component} does not exist in theme`); + } + + return componentTheme; +}; diff --git a/src/utils/Theme/TW/darkTheme.ts b/src/utils/Theme/TW/darkTheme.ts new file mode 100644 index 00000000..5ff70bac --- /dev/null +++ b/src/utils/Theme/TW/darkTheme.ts @@ -0,0 +1,25 @@ +import { darkColors } from '../themes'; +import { RecursiveKeyValuePair } from 'tailwindcss/types/config'; + +export const twAdditionalConfiguration = { + // Remove it and use default TailwindCSS configuration + colors: { + black: darkColors.black, + white: darkColors.white, + red: darkColors.red as RecursiveKeyValuePair, + purple: darkColors.purple as RecursiveKeyValuePair, + blue: darkColors.blue as RecursiveKeyValuePair, + green: darkColors.green as RecursiveKeyValuePair, + yellow: darkColors.yellow as RecursiveKeyValuePair, + orange: darkColors.orange as RecursiveKeyValuePair, + gray: darkColors.gray as RecursiveKeyValuePair, + pink: darkColors.pink as RecursiveKeyValuePair, + slate: darkColors.slate as RecursiveKeyValuePair, + primary: darkColors.primary as RecursiveKeyValuePair, + secondary: darkColors.secondary as RecursiveKeyValuePair, + overlay: darkColors.overlay as RecursiveKeyValuePair + }, + spacing: { + '5px': '5px' + } +}; diff --git a/src/utils/Theme/TW/index.ts b/src/utils/Theme/TW/index.ts new file mode 100644 index 00000000..ee0a4477 --- /dev/null +++ b/src/utils/Theme/TW/index.ts @@ -0,0 +1,3 @@ +export * from './theme'; +export * from './ThemeProvider'; +export * from './darkTheme'; diff --git a/src/utils/Theme/TW/theme.ts b/src/utils/Theme/TW/theme.ts new file mode 100644 index 00000000..e0408183 --- /dev/null +++ b/src/utils/Theme/TW/theme.ts @@ -0,0 +1,21 @@ +import { + buttonTheme, + ReaBlocksButtonTheme +} from '../../../elements/Button/ButtonTheme'; + +export interface ReaBlocksComponents { + button: string; +} + +export interface ReaBlocksTheme { + components: { + button: ReaBlocksButtonTheme; + // select: ReaBlockSelectTheme; + }; +} + +export const theme: ReaBlocksTheme = { + components: { + button: buttonTheme + } +}; diff --git a/src/utils/helpers/cloneDeep.ts b/src/utils/helpers/cloneDeep.ts new file mode 100644 index 00000000..04482a8a --- /dev/null +++ b/src/utils/helpers/cloneDeep.ts @@ -0,0 +1,15 @@ +import { isObject } from './isObject'; + +export function cloneDeep(source: T): T { + if (!isObject(source)) { + return source; + } + + const output: Record = {}; + + for (const key in source) { + output[key] = cloneDeep(source[key]); + } + + return output as T; +} diff --git a/src/utils/helpers/isObject.ts b/src/utils/helpers/isObject.ts new file mode 100644 index 00000000..48c30564 --- /dev/null +++ b/src/utils/helpers/isObject.ts @@ -0,0 +1,10 @@ +/** + * Check if provided parameter is plain object + * @param item + * @returns boolean + */ +export function isObject(item: unknown): item is Record { + return ( + item !== null && typeof item === 'object' && item.constructor === Object + ); +} diff --git a/src/utils/helpers/mergeDeep.ts b/src/utils/helpers/mergeDeep.ts new file mode 100644 index 00000000..d91bc184 --- /dev/null +++ b/src/utils/helpers/mergeDeep.ts @@ -0,0 +1,36 @@ +import { cloneDeep } from './cloneDeep'; +import { isObject } from './isObject'; + +/** + * Merge and deep copy the values of all the enumerable own properties of target object from source object to a new object + * @param target The target object to get properties from. + * @param source The source object from which to copy properties. + * @return A new merged and deep copied object. + */ +export function mergeDeep( + target: T, + source: S +): T & S { + if (isObject(source) && Object.keys(source).length === 0) { + return cloneDeep({ ...target, ...source }); + } + + const output = { ...target, ...source }; + + if (isObject(source) && isObject(target)) { + for (const key in source) { + if (isObject(source[key]) && key in target && isObject(target[key])) { + (output as Record)[key] = mergeDeep( + target[key] as object, + source[key] as object + ); + } else { + (output as Record)[key] = isObject(source[key]) + ? cloneDeep(source[key]) + : source[key]; + } + } + } + + return output; +} diff --git a/tailwind.config.ts b/tailwind.config.ts new file mode 100644 index 00000000..ebbdb484 --- /dev/null +++ b/tailwind.config.ts @@ -0,0 +1,18 @@ +import { type Config } from 'tailwindcss'; +import { twAdditionalConfiguration } from './src/utils/Theme/TW'; + +const config: Config = { + content: [ + './.storybook/**/*.{js,jsx,ts,tsx}', + './src/**/*.{js,jsx,ts,tsx}', + ], + darkMode: 'selector', + theme: { + extend: { + ...twAdditionalConfiguration, + }, + }, + plugins: [], +}; + +export default config;