├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .prettierignore ├── .prettierrc ├── .storybook ├── main.js └── preview.js ├── .vscode └── settings.json ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── jest.config.js ├── lerna.json ├── package.json ├── packages ├── chakra-ui-colors │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ ├── ThemeEditorColorItem.tsx │ │ ├── ThemeEditorColors.tsx │ │ ├── ThemeEditorPalette.tsx │ │ ├── ThemeEditorPaletteColorItem.tsx │ │ ├── ThemeEditorPaletteDrawer.tsx │ │ ├── ThemeEditorPaletteDrawerHeader.tsx │ │ ├── ThemeEditorPalettePopoverForm.tsx │ │ ├── constants.ts │ │ ├── generateColorPalette.ts │ │ └── index.ts │ ├── stories │ │ └── ThemeEditorColors.stories.tsx │ └── tsconfig.json ├── chakra-ui-core │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ ├── components │ │ │ ├── base │ │ │ │ ├── BaseList.tsx │ │ │ │ ├── BaseListItem.tsx │ │ │ │ ├── BaseMenu.tsx │ │ │ │ ├── BaseMenuItem.tsx │ │ │ │ ├── ColorModeToggle.tsx │ │ │ │ ├── ConfirmModal.tsx │ │ │ │ ├── CreditsBox.tsx │ │ │ │ ├── ElementsHighlighter.tsx │ │ │ │ ├── EmptyBox.tsx │ │ │ │ ├── IconSwitch.tsx │ │ │ │ ├── RadioBox.tsx │ │ │ │ ├── ThemeIcon.tsx │ │ │ │ ├── ThemeResetPopup.tsx │ │ │ │ ├── ThemesList.tsx │ │ │ │ ├── UpgradeBanner.tsx │ │ │ │ └── index.ts │ │ │ ├── config │ │ │ │ ├── ThemeEditorConfig.tsx │ │ │ │ ├── ThemeEditorConfigItem.tsx │ │ │ │ └── index.ts │ │ │ ├── theme-editor-provider │ │ │ │ ├── ThemeEditorProvider.tsx │ │ │ │ ├── ThemeStateInitializer.tsx │ │ │ │ └── index.ts │ │ │ └── theme-editor │ │ │ │ ├── ThemeDownloadButton.tsx │ │ │ │ ├── ThemeEditor.tsx │ │ │ │ ├── ThemeEditorAccordion.tsx │ │ │ │ ├── ThemeEditorAccordionItem.tsx │ │ │ │ ├── ThemeEditorButton.tsx │ │ │ │ ├── ThemeEditorDrawer.tsx │ │ │ │ ├── ThemeEditorDrawerFooter.tsx │ │ │ │ ├── ThemeEditorDrawerHeader.tsx │ │ │ │ ├── ThemeEditorRootPanel.tsx │ │ │ │ ├── ThemeExportDrawer.tsx │ │ │ │ ├── ThemeExportDrawerFooter.tsx │ │ │ │ ├── ThemeProperty.tsx │ │ │ │ ├── ThemeSwitchDrawer.tsx │ │ │ │ ├── ThemeSwitchDrawerButton.tsx │ │ │ │ ├── ThemeSwitchDrawerHeader.tsx │ │ │ │ └── index.ts │ │ ├── constants.ts │ │ ├── hooks │ │ │ ├── useAccordionState.ts │ │ │ └── useThemeEditor.ts │ │ ├── index.ts │ │ └── utils │ │ │ ├── ElementsMap.ts │ │ │ ├── GoogleFontFamiliesStateInitializer.tsx │ │ │ ├── cssBoxShadow.ts │ │ │ ├── defaultThemeColorKeys.ts │ │ │ ├── googleFontFamiliesState.ts │ │ │ ├── isBrowserCompatible.ts │ │ │ ├── isColor.ts │ │ │ ├── isMobile.ts │ │ │ ├── managedAttributes.ts │ │ │ ├── safeJsonParse.ts │ │ │ ├── timeSince.ts │ │ │ └── updateThemeTokens.ts │ ├── stories │ │ ├── ColorModeToggle.stories.tsx │ │ ├── CustomEditorPanel.tsx │ │ ├── CustomResetThemeButton.tsx │ │ ├── CustomSaveButton.tsx │ │ ├── CustomUndoRedoButtons.tsx │ │ ├── ThemeColorBox.tsx │ │ ├── ThemeEditor.stories.tsx │ │ ├── ThemeEditorButton.stories.tsx │ │ ├── ThemeEditorDrawer.stories.tsx │ │ ├── ThemeEditorDrawerFooter.stories.tsx │ │ ├── ThemeEditorDrawerHeader.stories.tsx │ │ ├── ThemeIcon.stories.tsx │ │ ├── ThemeResetPopup.stories.tsx │ │ └── useThemeEditor.stories.mdx │ ├── tests │ │ └── unit │ │ │ └── utils │ │ │ ├── isColor.test.ts │ │ │ ├── safeJsonParse.test.ts │ │ │ └── updateThemeTokens.test.ts │ └── tsconfig.json ├── chakra-ui-font-sizes │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ ├── ThemeEditorFontSizes.tsx │ │ ├── ThemeEditorFontSizesItem.tsx │ │ └── index.ts │ ├── stories │ │ └── ThemeEditorFontSizes.stories.tsx │ └── tsconfig.json ├── chakra-ui-theme │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ ├── ThemeProvider.tsx │ │ ├── components │ │ │ └── index.ts │ │ ├── foundations │ │ │ ├── colors.ts │ │ │ ├── config.ts │ │ │ ├── fonts.ts │ │ │ ├── radii.ts │ │ │ ├── shadows.ts │ │ │ └── sizes.ts │ │ ├── index.ts │ │ ├── styles.ts │ │ └── utils │ │ │ └── colors.ts │ └── tsconfig.json └── hypertheme-chakra-ui │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── rollup.config.js │ ├── src │ ├── HyperThemeEditor.tsx │ └── index.ts │ ├── stories │ ├── Test.stories.tsx │ └── ThemeEditor.stories.tsx │ └── tsconfig.json ├── setupTests.ts ├── tsconfig.json └── yarn.lock /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .next 3 | build 4 | webpack.config.js 5 | rollup.config.js 6 | build/ 7 | src/core/graphql.ts -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | module.exports = { 3 | ignorePatterns: ['.eslintrc.js'], 4 | plugins: ['prettier', '@typescript-eslint'], 5 | extends: ['airbnb-typescript', 'react-app', 'prettier'], 6 | parser: '@typescript-eslint/parser', 7 | parserOptions: { 8 | createDefaultProgram: true, 9 | project: './tsconfig.json', 10 | tsconfigRootDir: __dirname, 11 | include: ['stories/*.ts', 'stories/*.tsx'], 12 | }, 13 | settings: { 14 | 'import/resolver': { 15 | typescript: { 16 | alwaysTryTypes: true, 17 | }, 18 | }, 19 | }, 20 | rules: { 21 | 'object-curly-spacing': ['warn', 'always'], 22 | 'no-unused-vars': [ 23 | 'warn', 24 | { 25 | vars: 'all', 26 | args: 'none', 27 | }, 28 | ], 29 | '@typescript-eslint/no-unused-vars': [ 30 | 'warn', 31 | { 32 | vars: 'all', 33 | args: 'none', 34 | }, 35 | ], 36 | '@typescript-eslint/no-explicit-any': [ 37 | 'warn', 38 | { 39 | ignoreRestArgs: true, 40 | }, 41 | ], 42 | 'max-len': [ 43 | 'warn', 44 | { 45 | code: 100, 46 | ignoreStrings: true, 47 | ignoreTemplateLiterals: true, 48 | ignoreComments: true, 49 | }, 50 | ], 51 | semi: ['warn', 'never'], 52 | '@typescript-eslint/semi': ['warn', 'never'], 53 | 'no-plusplus': [ 54 | 'error', 55 | { 56 | allowForLoopAfterthoughts: true, 57 | }, 58 | ], 59 | 'react/jsx-key': 'error', 60 | 'import/no-extraneous-dependencies': [ 61 | 'error', 62 | { 63 | devDependencies: [ 64 | '**/*.test.js', 65 | '**/*.test.jsx', 66 | '**/*.test.ts', 67 | '**/*.test.tsx', 68 | 'src/tests/**/*', 69 | ], 70 | }, 71 | ], 72 | 'class-methods-use-this': ['off'], 73 | 'react/jsx-props-no-spreading': 'off', 74 | 'import/prefer-default-export': 'off', 75 | 'react/jsx-boolean-value': 'off', 76 | 'react/prop-types': 'off', 77 | 'react/no-unescaped-entities': 'off', 78 | 'react/jsx-one-expression-per-line': 'off', 79 | 'react/jsx-wrap-multilines': 'off', 80 | 'react/destructuring-assignment': 'off', 81 | 'react/button-has-type': 'off', 82 | 'no-else-return': 'off', 83 | 'no-console': 'off', 84 | 'arrow-body-style': 'off', 85 | 'no-param-reassign': 'off', 86 | 'object-shorthand': 'warn', 87 | 'lines-between-class-members': 'warn', 88 | 'prefer-template': 'warn', 89 | '@typescript-eslint/lines-between-class-members': 'off', 90 | 'react/no-children-prop': 'off', 91 | '@typescript-eslint/no-shadow': 'off', 92 | 'react/no-array-index-key': 'off', 93 | 'spaced-comment': 'warn', 94 | 'no-useless-return': 'off', 95 | 'import/no-named-as-default': 'off', 96 | 'import/no-cycle': 'off', 97 | 'no-nested-ternary': 'off', 98 | 'react/jsx-curly-brace-presence': 'warn', 99 | 'react/self-closing-comp': 'off', 100 | 'consistent-return': 'off', 101 | '@typescript-eslint/naming-convention': 'off', 102 | '@typescript-eslint/indent': 'off', 103 | '@typescript-eslint/comma-dangle': [ 104 | 'off', 105 | { 106 | arrays: 'only-multiline', 107 | objects: 'only-multiline', 108 | imports: 'only-multiline', 109 | exports: 'only-multiline', 110 | functions: 'never', 111 | }, 112 | ], 113 | 'prettier/prettier': 'error', 114 | 'comma-dangle': 'off', 115 | }, 116 | } 117 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | lib 3 | storybook-static 4 | .env -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | package-lock.json 2 | .next 3 | node_modules/ 4 | yarn.lock 5 | build/ -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "tabWidth": 2, 4 | "semi": false, 5 | "printWidth": 100 6 | } -------------------------------------------------------------------------------- /.storybook/main.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | const toPath = (_path) => path.join(process.cwd(), _path); 4 | 5 | module.exports = { 6 | stories: [ 7 | "../packages/**/*.stories.mdx", 8 | "../packages/**/*.stories.@(ts|tsx|js|jsx)", 9 | ], 10 | addons: ["@storybook/addon-links", "@storybook/addon-essentials"], 11 | // https://storybook.js.org/docs/react/configure/typescript#mainjs-configuration 12 | typescript: { 13 | check: true, // type-check stories during Storybook build 14 | }, 15 | webpackFinal: async (config) => { 16 | return { 17 | ...config, 18 | resolve: { 19 | ...config.resolve, 20 | alias: { 21 | ...config.resolve.alias, 22 | "@emotion/core": toPath("node_modules/@emotion/react"), 23 | "@emotion/styled": toPath("node_modules/@emotion/styled"), 24 | "emotion-theming": toPath("node_modules/@emotion/react"), 25 | }, 26 | }, 27 | }; 28 | }, 29 | }; 30 | -------------------------------------------------------------------------------- /.storybook/preview.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { ChakraProvider } from '@chakra-ui/react' 3 | 4 | // import '@fontsource/sora/200.css' 5 | // import '@fontsource/sora/400.css' 6 | // import '@fontsource/sora/600.css' 7 | // import '@fontsource/sora/800.css' 8 | 9 | // https://storybook.js.org/docs/react/writing-stories/parameters#global-parameters 10 | export const parameters = { 11 | // https://storybook.js.org/docs/react/essentials/actions#automatically-matching-args 12 | actions: { argTypesRegex: '^on[A-Z].*' }, 13 | controls: { 14 | matchers: { 15 | color: /(background|color)$/i, 16 | date: /Date$/, 17 | }, 18 | }, 19 | } 20 | 21 | export const decorators = [ 22 | (Story) => ( 23 | 24 | 25 | 26 | ), 27 | ] 28 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "*.jsx": "javascriptreact", 4 | "*.tsx": "typescriptreact", 5 | "*.ts": "typescript", 6 | }, 7 | "editor.insertSpaces": true, 8 | "editor.detectIndentation": false, 9 | "editor.formatOnSave": true, 10 | "editor.codeActionsOnSave": { 11 | "source.fixAll": true, 12 | "source.fixAll.eslint": true 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.2.2 (2024-06-27) 2 | 3 | ## 0.2.1 (2024-06-18) 4 | 5 | ## 0.2.0 (2024-06-18) 6 | 7 | ## 0.2.0-rc.3 (2024-06-18) 8 | 9 | ## 0.2.0-dev.5 (2024-06-18) 10 | 11 | ## 0.2.0-dev.2 (2022-08-18) 12 | 13 | 14 | ## 0.2.0-dev.1 (2022-08-18) 15 | 16 | 17 | ## 0.1.5 (2022-04-04) 18 | 19 | chore: bumping version to 0.1.5 20 | 21 | ## 0.1.4 (2022-04-04) 22 | 23 | - feat: controlled ThemeEditor with `useDisclosure()` from Chakra UI (thanks to: @panbenson) 24 | - fix: removed not necessary API calls during initialization 25 | 26 | ## 0.1.4-rc.4 (2022-04-04) 27 | 28 | ## 0.1.4-rc.3 (2022-04-03) 29 | 30 | chore: revert recoil version 31 | 32 | ## 0.1.4-rc.2 (2022-04-03) 33 | 34 | chore: removed wrong api call 35 | 36 | ## 0.1.4-rc.1 (2022-04-03) 37 | 38 | - chore: update recoil dependency 39 | 40 | ## 0.1.4-rc.0 (2022-02-25) 41 | 42 | feat: controlled ThemeEditorDrawer 43 | 44 | ## 0.1.3 (2022-01-11) 45 | 46 | fix: React 17 support 🎉 47 | 48 | ## 0.1.3-rc.0 (2022-01-10) 49 | 50 | Fix: react v17 support 51 | 52 | ## 0.2.0-rc.2 (2021-11-20) 53 | 54 | fix: highlighter return object 55 | 56 | 57 | ## 0.2.0-rc.1 (2021-11-20) 58 | 59 | feat: multiple highlighter theme keys 60 | 61 | 62 | ## 0.2.0-rc.0 (2021-11-19) 63 | 64 | ## 0.1.2 (2021-09-22) 65 | 66 | fix: isOpen prop is passed to ThemeEditorButton causing a warning 67 | 68 | ## 0.1.1 (2021-09-22) 69 | 70 | docs: fix packages README images 71 | 72 | ## 0.1.0 (2021-09-22) 73 | 74 | First Open Source version! 75 | 76 | - customizable ThemeEditor 77 | - create your custom panel editors 78 | 79 | ## 0.1.0-rc.10 (2021-09-22) 80 | 81 | docs: added README 82 | docs: basic Contributing Guide 83 | fix: reset button style 84 | 85 | ## 0.1.0-rc.9 (2021-09-21) 86 | 87 | - Removed unused "Add Theme" button 88 | - Removed unneccesary Menu on ThemeEditorHeader 89 | - removed unused imports from ThemeEditorButton 90 | 91 | ## 0.1.0-rc.8 (2021-09-21) 92 | 93 | Renamed DefaultThemeEditor to HyperThemeEditor 94 | 95 | ## 0.1.0-rc.7 (2021-09-15) 96 | 97 | fix: chakra-ui-theme deps 98 | 99 | ## 0.1.0-rc.6 (2021-09-15) 100 | 101 | fix: chakra-ui-theme font dependencies 102 | 103 | ## 0.1.0-rc.5 (2021-09-14) 104 | 105 | fix: chakra-ui-theme dependencies 106 | 107 | ## 0.1.0-rc.4 (2021-09-14) 108 | 109 | fix: DefaultThemeEditorProps export 110 | 111 | ## 0.1.0-rc.3 (2021-09-14) 112 | 113 | First Open Source version. 114 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Thanks for your interest in contributing to HyperTheme Editor. 4 | 5 | We're so glad you want to help! 💖 6 | 7 | ## Project Overview 8 | 9 | This project is a monorepo managed with `lerna`. 10 | 11 | It uses `yarn` to manage dependecies. 12 | 13 | ## Setup the Project 14 | 15 | 1. Fork this repository 16 | 2. Clone your fork to your local machine 17 | 18 | ```bash 19 | git clone https://github.com//hypertheme-editor.git 20 | cd hypertheme-editor 21 | ``` 22 | 23 | 3. Install all dependencies by running: 24 | 25 | ```bash 26 | yarn 27 | ``` 28 | 29 | 4. Build the project: 30 | 31 | ```bash 32 | yarn build 33 | ``` 34 | 35 | 5. Start Storybook: 36 | 37 | ```bash 38 | yarn storybook 39 | ``` 40 | 41 | ## Project Commands 42 | 43 | Here's a list of the commands provided by this project: 44 | 45 | `yarn build`: build all the packages 46 | 47 | `yarn storybook`: run Storybook for components development 48 | 49 | ## Commit convention 50 | 51 | HyperTheme follows the [Convential Commits specification](https://www.conventionalcommits.org/). 52 | 53 | Write the commit like this: 54 | 55 | ``` 56 | category(scope or module): your commit message 57 | ``` 58 | 59 | Where category is one of: 60 | 61 | `feat`: changes that introduce new code or features 62 | 63 | `fix`: changes that fix a bug (reference an issue if present) 64 | 65 | `refactor`: any code related change that is not a fix, nor a feature 66 | 67 | `docs:` changing existing or creating new documentation (README.md, etc.) 68 | 69 | `chore`: all changes to the repository that do not fit into any of the above categories. 70 | 71 | ## Todo list 72 | 73 | HyperTheme Editor is in a `preview` state, it will be awesome if you want to help with one of these tasks: 74 | 75 | - [ ] **Tests**: we need tests, we just started working on it: check the [setup-tests](https://github.com/Hyperting/hypertheme-editor/tree/tests-setup) branch for more information. 76 | - [ ] **Recoil issue**: when importing from `@hypertheme-editor/chakra-ui-core` and not from `@hypertheme-editor/chakra-ui` recoil causes a crash. 77 | - [ ] **Stories**: Storybook stories are just a _draft_, we need a better structure and better stories. 78 | - [ ] **Material UI**: mui version is not implemented. 79 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Hyperting S.r.l. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HyperTheme Editor 2 | 3 | ![HyperTheme Editor screen shot](https://www.hyperthe.me/images/social-banner.jpg) 4 | 5 | Powerful visual theme editor for your next Chakra UI project. 6 | 7 | ## Features 8 | 9 | - Chakra-UI Theme Foundation Color Editor 10 | - Chakra-UI Theme Foundation Font Sizes Editor 11 | - Undo/Redo 12 | - Customizable Editor Drawer 13 | - Custom Panel Editors 14 | - Unlimited exports 15 | 16 | 17 | ## PRO Version 18 | 19 | HyperTheme Editor has also a PRO version with more features: 20 | - Font Family Editor from Google Fonts 21 | - Line Heights Editor 22 | - Letter Spacing Editor 23 | - Shadows Editor 24 | - Radii Editor 25 | - Spacing Editor 26 | 27 | Visit [`hyperthe.me`](https://hyperthe.me) for more info. 28 | 29 | ## Documentation 30 | 31 | Documentation and guides can be found [here](https://hyperthe.me/documentation). 32 | 33 | ## Getting Started 34 | ### 1. Installation 35 | 36 | Install with NPM: 37 | 38 | ```bash 39 | npm i @hypertheme-editor/chakra-ui 40 | ``` 41 | 42 | or with Yarn: 43 | 44 | ```bash 45 | yarn add @hypertheme-editor/chakra-ui 46 | ``` 47 | 48 | ### 2. Setup 49 | 50 | Installation is super easy and fast: 51 | 52 | - Add the component `` just below the `` component. 53 | - Add the component ``. 54 | 55 | Here's an example: 56 | 57 | ```jsx 58 | import * as React from 'react' 59 | import { ChakraProvider } from '@chakra-ui/react' 60 | import { ThemeEditorProvider, HyperThemeEditor } from '@hypertheme-editor/chakra-ui' 61 | import theme from './my-theme' 62 | 63 | function App() { 64 | return ( 65 | 66 | 67 | 68 | 69 | 70 | ) 71 | } 72 | ``` 73 | 74 | ### 3. Next Steps 75 | 76 | Congratulations! You have a working **theme editor** on your application. 77 | 78 | HyperTheme Editor comes also with all the building blocks necessary to create custom theme editor that works with Chakra UI. 79 | 80 | To learn more read the [documentation](https://hyperthe.me/documentation). 81 | 82 | ## Contributing 83 | 84 | If you want to contribute to HyperTheme Editor, make sure to read the [contribution guide](CONTRIBUTING.md) first. 85 | 86 | ## License 87 | 88 | HyperTheme Editor is licensed under the [MIT License](https://github.com/Hyperting/hypertheme-editor/blob/main/LICENSE) by [Hyperting S.r.l.](https://hyperting.com). 89 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | transform: { 3 | '^.+\\.tsx?$': 'ts-jest', 4 | }, 5 | setupFilesAfterEnv: ['/setupTests.ts'], 6 | snapshotSerializers: ['enzyme-to-json/serializer'], 7 | collectCoverageFrom: ['/packages/*/src/**/*.{js,jsx,ts,tsx}'], 8 | testMatch: ['/packages/*/tests/**/*.(test|spec).(ts|tsx)'], 9 | testEnvironment: 'jsdom', 10 | } 11 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [ 3 | "packages/*" 4 | ], 5 | "version": "independent", 6 | "npmClient": "yarn", 7 | "useWorkspaces": true, 8 | "$schema": "node_modules/lerna/schemas/lerna-schema.json", 9 | "useNx": false 10 | } 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "root", 3 | "private": true, 4 | "workspaces": { 5 | "packages": [ 6 | "packages/*" 7 | ], 8 | "nohoist": [ 9 | "**/recoil" 10 | ] 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/Hyperting/hypertheme-editor.git" 15 | }, 16 | "dependencies": { 17 | "@babel/core": "^7.16.0", 18 | "@babel/preset-env": "^7.16.0", 19 | "@babel/preset-typescript": "^7.16.0", 20 | "@chakra-ui/react": "^1.6.7", 21 | "@emotion/react": "^11.4.0", 22 | "@emotion/styled": "^11.3.0", 23 | "@rollup/plugin-commonjs": "^19.0.0", 24 | "@rollup/plugin-node-resolve": "^13.0.0", 25 | "@storybook/addon-actions": "^6.3.8", 26 | "@storybook/addon-essentials": "^6.3.8", 27 | "@storybook/addon-links": "^6.3.8", 28 | "@storybook/react": "^6.3.8", 29 | "@types/enzyme": "^3.10.10", 30 | "@types/jest": "^27.0.2", 31 | "@types/react": "^17.0.14", 32 | "@typescript-eslint/eslint-plugin": "4.12.0", 33 | "@typescript-eslint/parser": "4.12.0", 34 | "@wojtekmaj/enzyme-adapter-react-17": "^0.6.5", 35 | "babel-eslint": "10.1.0", 36 | "babel-jest": "^27.3.1", 37 | "babel-loader": "^8.2.2", 38 | "cpy-cli": "^3.1.1", 39 | "enzyme": "^3.11.0", 40 | "enzyme-to-json": "^3.6.2", 41 | "eslint-config-airbnb": "18.2.1", 42 | "eslint-config-airbnb-typescript": "12.0.0", 43 | "eslint-config-prettier": "7.1.0", 44 | "eslint-config-react-app": "6.0.0", 45 | "eslint-import-resolver-typescript": "2.3.0", 46 | "eslint-loader": "4.0.2", 47 | "eslint-plugin-flowtype": "5.2.0", 48 | "eslint-plugin-import": "2.22.1", 49 | "eslint-plugin-jest": "^24.1.3", 50 | "eslint-plugin-json": "^2.1.2", 51 | "eslint-plugin-jsx-a11y": "6.4.1", 52 | "eslint-plugin-prettier": "3.3.1", 53 | "eslint-plugin-react": "7.22.0", 54 | "eslint-plugin-react-hooks": "4.2.0", 55 | "framer-motion": "^4.1.17", 56 | "jest": "^27.3.1", 57 | "prettier": "2.2.1", 58 | "prettier-eslint": "12.0.0", 59 | "prettier-eslint-cli": "5.0.0", 60 | "react": "^17.0.2", 61 | "react-dom": "^17.0.2", 62 | "recoil": "^0.5.2", 63 | "release-it": "^15.3.0", 64 | "release-it-lerna-changelog": "^5.0.0", 65 | "release-it-yarn-workspaces": "^3.0.0", 66 | "rollup": "^2.53.0", 67 | "rollup-plugin-peer-deps-external": "^2.2.4", 68 | "rollup-plugin-typescript2": "^0.31.0", 69 | "ts-jest": "^27.0.7", 70 | "typescript": "^4.3.5" 71 | }, 72 | "resolutions": { 73 | "**/react": "17.0.1", 74 | "**/react-dom": "17.0.1" 75 | }, 76 | "scripts": { 77 | "copy-readme": "cpy ./README.md ./packages/hypertheme-chakra-ui", 78 | "storybook": "start-storybook -p 6006", 79 | "build-storybook": "build-storybook", 80 | "clean": "lerna run clean", 81 | "build": "lerna run build --include-dependencies --stream && npm run copy-readme", 82 | "release": "dotenv -- release-it", 83 | "release-rc": "dotenv -- release-it --preRelease=rc", 84 | "release-dev": "dotenv -- release-it --preRelease=dev", 85 | "deploy": "yarn build && yarn release", 86 | "deploy-rc": "yarn build && yarn release-rc", 87 | "deploy-dev": "yarn build && yarn release-dev", 88 | "test:unit": "jest", 89 | "test-watch:unit": "jest --watch", 90 | "test-watch": "yarn test-watch:unit", 91 | "test": "yarn test:unit" 92 | }, 93 | "devDependencies": { 94 | "lerna": "^5.5.2", 95 | "dotenv-cli": "^4.0.0" 96 | }, 97 | "release-it": { 98 | "plugins": { 99 | "release-it-yarn-workspaces": true, 100 | "release-it-lerna-changelog": { 101 | "infile": "CHANGELOG.md", 102 | "launchEditor": false 103 | } 104 | }, 105 | "npm": { 106 | "allowSameVersion": true 107 | }, 108 | "git": { 109 | "requireCleanWorkingDir": false, 110 | "requireBranch": [ 111 | "main", 112 | "support/0.1.x", 113 | "release/0.2.0" 114 | ], 115 | "push": false 116 | }, 117 | "github": { 118 | "release": true, 119 | "tokenRef": "NODE_ENV=GITHUB_AUTH" 120 | } 121 | }, 122 | "version": "0.2.2" 123 | } 124 | -------------------------------------------------------------------------------- /packages/chakra-ui-colors/.gitignore: -------------------------------------------------------------------------------- 1 | .env -------------------------------------------------------------------------------- /packages/chakra-ui-colors/README.md: -------------------------------------------------------------------------------- 1 | # @hypertheme-editor/chakra-ui-colors 2 | 3 | Change all [Chakra UI](https://chakra-ui.com/) theme colors, create custom custom colors and generate color palettes with [HyperTheme Editor](#hypertheme-editor). 4 | 5 | ![Colors Editor Panel](https://hyperthe.me/images/documentation/colors-editor-screen.png) 6 | 7 | This module is part of **[HyperTheme Editor](#hypertheme-editor)**, check it out before installing and using it. 8 | 9 | ## Import 10 | 11 | ```jsx 12 | import { ThemeEditorFontSizes } from '@hypertheme-editor/chakra-ui-font-sizes' 13 | ``` 14 | 15 | ## Usage 16 | 17 | ThemeEditorColors works with the [`ThemeEditorDrawer` component](https://hyperthe.me/documentation/components/ThemeEditorDrawer). 18 | 19 | Put the `ThemeEditColors` component as a children of `ThemeEditorDrawer` and set `icon` and `title` props like in this example: 20 | 21 | ```jsx live=true 22 | function MyThemeEditor(props) { 23 | return ( 24 | 25 | 26 | 27 | 28 | 29 | 30 | ) 31 | } 32 | ``` 33 | 34 | ## HyperTheme Editor 35 | 36 | ![HyperTheme Editor screen shot](https://www.hyperthe.me/images/social-banner.jpg) 37 | 38 | Powerful visual theme editor for your next Chakra UI project. 39 | 40 | Learn more about it on the website: [`hyperthe.me`](https://hyperthe.me). 41 | 42 | ### Documentation 43 | 44 | Read the HyperTheme Editor [documentation here](https://hyperthe.me/documentation). 45 | 46 | ## License 47 | 48 | HyperTheme Editor is licensed under the [MIT License](https://github.com/Hyperting/hypertheme-editor/blob/main/LICENSE) by [Hyperting S.r.l.](https://hyperting.com). 49 | -------------------------------------------------------------------------------- /packages/chakra-ui-colors/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hypertheme-editor/chakra-ui-colors", 3 | "version": "0.2.2", 4 | "description": "", 5 | "main": "lib/index.js", 6 | "module": "lib/index.esm.js", 7 | "types": "lib/index.d.ts", 8 | "files": [ 9 | "lib" 10 | ], 11 | "scripts": { 12 | "test": "echo \"Error: no test specified\" && exit 1", 13 | "build": "rollup -c", 14 | "start": "rollup -c -w" 15 | }, 16 | "author": "marco", 17 | "license": "ISC", 18 | "peerDependencies": { 19 | "@chakra-ui/react": ">= 1.0.0", 20 | "@hypertheme-editor/chakra-ui-core": "0.2.2", 21 | "framer-motion": ">= 4", 22 | "react": ">=16.8.6", 23 | "react-dom": ">=16.8.6", 24 | "immer": "^9.0.6", 25 | "react-icons": "^4.2.0", 26 | "use-debouncy": "^4.2.0" 27 | }, 28 | "dependencies": { 29 | "colord": "^2.1.0", 30 | "react-colorful": "^5.2.3" 31 | }, 32 | "devDependencies": { 33 | "@hypertheme-editor/chakra-ui-core": "0.2.2" 34 | }, 35 | "publishConfig": { 36 | "access": "public", 37 | "registry": "https://registry.npmjs.org" 38 | } 39 | } -------------------------------------------------------------------------------- /packages/chakra-ui-colors/rollup.config.js: -------------------------------------------------------------------------------- 1 | import peerDepsExternal from 'rollup-plugin-peer-deps-external' 2 | import resolve from '@rollup/plugin-node-resolve' 3 | import commonjs from '@rollup/plugin-commonjs' 4 | import typescript from 'rollup-plugin-typescript2' 5 | 6 | const packageJson = require('./package.json') 7 | 8 | export default { 9 | input: 'src/index.ts', 10 | output: [ 11 | { 12 | file: packageJson.main, 13 | format: 'cjs', 14 | sourcemap: true, 15 | }, 16 | { 17 | file: packageJson.module, 18 | format: 'esm', 19 | sourcemap: true, 20 | }, 21 | ], 22 | plugins: [ 23 | peerDepsExternal(), 24 | resolve(), 25 | commonjs(), 26 | typescript({ 27 | useTsconfigDeclarationDir: true, 28 | }), 29 | ], 30 | } 31 | -------------------------------------------------------------------------------- /packages/chakra-ui-colors/src/ThemeEditorColorItem.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useCallback, useState, useEffect, useMemo } from 'react' 2 | import { 3 | Box, 4 | Flex, 5 | BoxProps, 6 | useColorModeValue, 7 | Input, 8 | Text, 9 | Popover, 10 | PopoverTrigger, 11 | PopoverContent, 12 | PopoverBody, 13 | } from '@chakra-ui/react' 14 | import { useDebouncyEffect } from 'use-debouncy' 15 | import { RgbaStringColorPicker } from 'react-colorful' 16 | import { colord, extend } from 'colord' 17 | import namesPlugin from 'colord/plugins/names' 18 | import { COLOR_PICKER_TRANSPARENT_SAFE_BG_B64 } from './constants' 19 | import { ElementsHighlighter } from '@hypertheme-editor/chakra-ui-core' 20 | 21 | extend([namesPlugin]) 22 | 23 | export type ThemeEditorColorItemProps = { 24 | title: string 25 | value?: string 26 | token: string 27 | colorIndex: number 28 | onChange: (value: { token: string; colorIndex: number; value: string }) => void 29 | } & Omit 30 | 31 | const ThemeEditorColorItem: FC = ({ 32 | p, 33 | px, 34 | title = 'Primary', 35 | token, 36 | colorIndex, 37 | value, 38 | onChange, 39 | ...rest 40 | }) => { 41 | const [currentValue, setCurrentValue] = useState(value) 42 | 43 | const rgbaString = useMemo(() => { 44 | if (!currentValue) { 45 | return '#ffffff' 46 | } 47 | return currentValue.startsWith('rgba') ? currentValue : colord(currentValue).toRgbString() 48 | }, [currentValue]) 49 | 50 | const shadow = useColorModeValue('surface', 'surfaceDark') 51 | const safeB64Bg = useColorModeValue( 52 | COLOR_PICKER_TRANSPARENT_SAFE_BG_B64[0], 53 | COLOR_PICKER_TRANSPARENT_SAFE_BG_B64[1] 54 | ) 55 | 56 | const handleValueChange: React.ChangeEventHandler = useCallback((event) => { 57 | setCurrentValue(event.target.value) 58 | }, []) 59 | 60 | useDebouncyEffect( 61 | () => { 62 | if (colord(currentValue as any).isValid() && currentValue !== value) { 63 | onChange({ token, colorIndex, value: currentValue! }) 64 | } 65 | }, 66 | 500, 67 | [currentValue] 68 | ) 69 | 70 | useEffect(() => { 71 | setCurrentValue(value) 72 | }, [value]) 73 | 74 | return ( 75 | 76 | 77 | {title} 78 | 79 | 80 | 81 | 82 | 83 | 94 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 127 | 128 | 129 | 130 | ) 131 | } 132 | 133 | export default ThemeEditorColorItem 134 | -------------------------------------------------------------------------------- /packages/chakra-ui-colors/src/ThemeEditorPalette.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react' 2 | import { 3 | BoxProps, 4 | useColorModeValue, 5 | Center, 6 | Text, 7 | SimpleGrid, 8 | useDisclosure, 9 | Tooltip, 10 | SimpleGridProps, 11 | } from '@chakra-ui/react' 12 | import { extend } from 'colord' 13 | import namesPlugin from 'colord/plugins/names' 14 | import ThemeEditorPaletteDrawer from './ThemeEditorPaletteDrawer' 15 | 16 | extend([namesPlugin]) 17 | 18 | export type ThemeEditorPaletteProps = { 19 | palette: Record 20 | scale?: (string | number)[] 21 | value?: string 22 | token: string 23 | showIndex?: boolean 24 | disableEditDrawer?: boolean 25 | } & Omit 26 | 27 | const ThemeEditorPalette: FC = ({ 28 | p, 29 | px, 30 | palette, 31 | token, 32 | scale = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900], 33 | value, 34 | showIndex, 35 | disableEditDrawer, 36 | ...rest 37 | }) => { 38 | const { onOpen, onClose, isOpen } = useDisclosure() 39 | const shadow = useColorModeValue('surface', 'surfaceDark') 40 | const borderColor = useColorModeValue('blackAlpha.100', 'whiteAlpha.100') 41 | 42 | return ( 43 | <> 44 | 52 | 66 | {scale.map((paletteIndex, key) => ( 67 |
75 | {showIndex && ( 76 | = 0 ? 'gray.500' : palette[scale[9 - key]]} 78 | size="xs" 79 | d={{ base: 'none', md: 'inline' }} 80 | > 81 | {paletteIndex} 82 | 83 | )} 84 |
85 | ))} 86 |
87 |
88 | {!disableEditDrawer && ( 89 | 95 | )} 96 | 97 | ) 98 | } 99 | 100 | export default ThemeEditorPalette 101 | -------------------------------------------------------------------------------- /packages/chakra-ui-colors/src/ThemeEditorPaletteDrawer.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useCallback, useRef } from 'react' 2 | import { 3 | Button, 4 | Drawer, 5 | DrawerBody, 6 | DrawerContent, 7 | DrawerProps, 8 | useColorModeValue, 9 | DrawerFooter, 10 | Box, 11 | SimpleGrid, 12 | } from '@chakra-ui/react' 13 | import { BaseListItem, useThemeEditor } from '@hypertheme-editor/chakra-ui-core' 14 | import ThemeEditorColorItem from './ThemeEditorColorItem' 15 | import { ThemeEditorPaletteDrawerHeader } from './ThemeEditorPaletteDrawerHeader' 16 | 17 | export type ThemeEditorPaletteDrawerProps = { 18 | palette: Record 19 | scale?: (string | number)[] 20 | token: string 21 | showIndex?: boolean 22 | } & Omit 23 | 24 | const ThemeEditorPaletteDrawer: FC = (props) => { 25 | const { 26 | onClose, 27 | palette, 28 | scale = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900], 29 | token, 30 | ...rest 31 | } = props 32 | const { theme, setTheme } = useThemeEditor() 33 | 34 | const btnRef = useRef() 35 | const closeBtnRef = useRef() 36 | 37 | const bgColor = useColorModeValue('white', 'gray.900') 38 | const bgColor2 = useColorModeValue('white', 'gray.800') 39 | 40 | const shadow = useColorModeValue('surface', 'surfaceDark') 41 | 42 | const handleChangeColor = useCallback( 43 | ({ token, index, value }) => { 44 | if (theme && theme.colors && theme.colors[token] && theme.colors[token][index]) { 45 | if ( 46 | typeof theme.colors[token][index] === 'string' && 47 | theme.colors[token][index] !== value 48 | ) { 49 | try { 50 | const newTheme = { 51 | ...theme, 52 | colors: { 53 | ...theme.colors, 54 | [token]: { 55 | ...theme.colors[token], 56 | [index]: value, 57 | }, 58 | }, 59 | } as any 60 | // setThemeColor(token, index, value) 61 | setTheme(newTheme) 62 | } catch (error) { 63 | // 64 | } 65 | } 66 | // } else { 67 | // try { 68 | // const newColorPalette = generatePalette(value) 69 | // const newTheme = { 70 | // ...theme, 71 | // colors: { 72 | // ...theme.colors, 73 | // [token]: newColorPalette, 74 | // }, 75 | // } as any 76 | // setThemeColorPalette(token, newColorPalette) 77 | // setTheme(newTheme) 78 | // } catch (error) { 79 | // // 80 | // } 81 | // } 82 | } 83 | }, 84 | [setTheme, theme] 85 | ) 86 | 87 | return ( 88 | 97 | 98 | 103 | 104 | 105 | {scale.map((paletteIndex, key) => ( 106 | 112 | { 118 | handleChangeColor({ token, index: colorIndex, value }) 119 | }} 120 | /> 121 | 122 | ))} 123 | 124 | 125 | 132 | 133 | 136 | 137 | 138 | 139 | 140 | ) 141 | } 142 | 143 | export default ThemeEditorPaletteDrawer 144 | -------------------------------------------------------------------------------- /packages/chakra-ui-colors/src/ThemeEditorPaletteDrawerHeader.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, MutableRefObject } from 'react' 2 | import { 3 | DrawerHeader, 4 | Flex, 5 | Box, 6 | IconButton, 7 | Divider, 8 | ButtonGroup, 9 | Button, 10 | Text, 11 | } from '@chakra-ui/react' 12 | import { MdClose } from 'react-icons/md' 13 | import { RiArrowGoBackFill, RiArrowGoForwardFill } from 'react-icons/ri' 14 | import { ColorModeToggle, useThemeEditor } from '@hypertheme-editor/chakra-ui-core' 15 | import ThemeEditorPalette from './ThemeEditorPalette' 16 | 17 | type Props = { 18 | onClose: () => void 19 | initialFocusRef: MutableRefObject 20 | token: string 21 | } 22 | 23 | export const ThemeEditorPaletteDrawerHeader: FC = ({ onClose, initialFocusRef, token }) => { 24 | const { canUndo, canRedo, undo, redo, theme } = useThemeEditor() 25 | 26 | return ( 27 | 33 | 34 | {theme?.colors && ( 35 | 45 | )} 46 | 47 | 48 | {token} palette 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | } 59 | aria-label="undo" 60 | disabled={!canUndo} 61 | onClick={undo} 62 | /> 63 | 64 | 65 | } 67 | aria-label="redo" 68 | disabled={!canRedo} 69 | onClick={redo} 70 | /> 71 | 72 | 73 | 84 | 85 | ) 86 | } 87 | -------------------------------------------------------------------------------- /packages/chakra-ui-colors/src/constants.ts: -------------------------------------------------------------------------------- 1 | export const COLOR_PICKER_TRANSPARENT_SAFE_BG_B64 = [ 2 | 'iVBORw0KGgoAAAANSUhEUgAAAHgAAAB4CAYAAAA5ZDbSAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAFXSURBVHgB7d3BCcJAFEXRiViH4MJ2LNZ2sgikEa0iM3A9p4PhMsvP2/bj/IwFXs/Heyzwb++9DdIEjhM4TuA4geMEjhM4TuA4geMEjhM4TuA4geMEjhM4TuA4geMEjhM4TuA4geMEjhM4TuA4geMEjhM4TuA4geO2sYirxjn84DiB4wSOEzhO4DiB4wSOEzhO4DiB4wSOEzhO4DiB4wSOEzhO4DiB4wSOEzhO4DiB4wSOEzhO4DiB4wSOEzhO4LjNld8ctgu5hMBxAscJHCdwnMBxAscJHCdwnMBxAscJHCdwnMBxAscJHCdwnMBxAscJHCdwnMBxAscJHCdwnMBxAscJHHcfLLEf55RZQduFk9gu5BICxwkcJ3CcwHECxwkcJ3CcwHECxwkcJ3CcwHECxwkcJ3CcwHECxwkcJ3CcwHECxwkcJ3CcwHECxwkcJ3Cc68J5vmOBH2JAIyklQWkKAAAAAElFTkSuQmCC', 3 | 'iVBORw0KGgoAAAANSUhEUgAAAHgAAAB4CAYAAAA5ZDbSAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAFYSURBVHgB7d2xDQJBDADBe0Q9hISUQLGUQEhIQ1AFZ2mZ6cBaObR8rCGX6+2xBrxfz/saMDXvaZEmcJzAcQLHCRwncJzAcQLHCRwncJzAcQLHCRwncJzAcQLHCRwncJzAcQLHCRwncJzAcQLHCRwncJzAcQLHCRx3uPLbY2peGxwncJzAcQLHCRwncJzAcQLHCRwncJzAcQLHCRwncJzAcQLHCRwncJzAcQLHCRwncJzAcQLHCRwncJzAcQLHCRx3rCGuGvewwXECxwkcJ3CcwHECxwkcJ3CcwHECxwkcJ3CcwHECxwkcJ3CcwHECxwkcJ3CcwHECxwkcJ3CcwHECxwkcJ3DcebHbZ23kd+EmfhfyEwLHCRwncJzAcQLHCRwncJzAcQLHCRwncJzAcQLHCRwncJzAcQLHCRwncJzAcQLHCRwncJzAcQLHCRwncNy/XxduvfSb8AX+bh2Xn9jf+AAAAABJRU5ErkJggg==', 4 | ] 5 | export const COLOR_EXPANDED_INDEX_LOCAL_STORAGE_KEY = 'hypertheme-color-expanded-index' 6 | -------------------------------------------------------------------------------- /packages/chakra-ui-colors/src/generateColorPalette.ts: -------------------------------------------------------------------------------- 1 | import { colord } from 'colord' 2 | 3 | export const generatePalette = (baseColor?: string) => { 4 | if (!baseColor) { 5 | throw new Error('color not provided') 6 | } 7 | 8 | return { 9 | 900: colord(baseColor).darken(0.3).toHex(), 10 | 800: colord(baseColor).darken(0.2).toHex(), 11 | 700: colord(baseColor).darken(0.15).toHex(), 12 | 600: colord(baseColor).darken(0.1).toHex(), 13 | 500: baseColor, 14 | 400: colord(baseColor).lighten(0.1).toHex(), 15 | 300: colord(baseColor).lighten(0.15).toHex(), 16 | 200: colord(baseColor).lighten(0.2).toHex(), 17 | 100: colord(baseColor).lighten(0.25).toHex(), 18 | 50: colord(baseColor).lighten(0.35).toHex(), 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/chakra-ui-colors/src/index.ts: -------------------------------------------------------------------------------- 1 | import { ThemeEditorColors } from './ThemeEditorColors' 2 | import ThemeEditorColorItem from './ThemeEditorColorItem' 3 | import ThemeEditorPalette from './ThemeEditorPalette' 4 | 5 | import ThemeEditorPaletteDrawer from './ThemeEditorPaletteDrawer' 6 | import { ThemeEditorPalettePopoverForm } from './ThemeEditorPalettePopoverForm' 7 | import { generatePalette } from './generateColorPalette' 8 | 9 | export { 10 | ThemeEditorColors, 11 | ThemeEditorColorItem, 12 | ThemeEditorPalettePopoverForm, 13 | ThemeEditorPaletteDrawer, 14 | ThemeEditorPalette, 15 | generatePalette, 16 | } 17 | -------------------------------------------------------------------------------- /packages/chakra-ui-colors/stories/ThemeEditorColors.stories.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies */ 2 | import React from 'react' 3 | import { ComponentMeta } from '@storybook/react' 4 | import { Button } from '@chakra-ui/react' 5 | import { CgColorPicker } from 'react-icons/cg' 6 | import { 7 | ThemeEditorButton, 8 | ThemeEditorProvider, 9 | ThemeEditorRootPanel, 10 | ThemeEditorDrawer, 11 | ThemeEditor, 12 | } from '@hypertheme-editor/chakra-ui-core' 13 | import { ThemeEditorColors } from '../src' 14 | 15 | export default { 16 | title: 'HyperThemeEditor/chakra-ui-colors/Colors Panel', 17 | component: ThemeEditor, 18 | argTypes: {}, 19 | } as ComponentMeta 20 | 21 | export const ThemeEditorColorsWithRootPanel = (args) => ( 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | ) 34 | 35 | export const ThemeEditorColorsWithoutRootPanel = (args) => ( 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | ) 46 | -------------------------------------------------------------------------------- /packages/chakra-ui-colors/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "outDir": "lib", 5 | "lib": [ 6 | "dom", 7 | "dom.iterable", 8 | "esnext" 9 | ], 10 | "declaration": true, 11 | "declarationDir": "lib", 12 | "allowJs": true, 13 | "skipLibCheck": true, 14 | "esModuleInterop": true, 15 | "allowSyntheticDefaultImports": true, 16 | "strict": true, 17 | "forceConsistentCasingInFileNames": true, 18 | "module": "esnext", 19 | "moduleResolution": "node", 20 | "resolveJsonModule": true, 21 | "isolatedModules": true, 22 | "noImplicitAny": false, 23 | "noUnusedParameters": false, 24 | "noUnusedLocals": false, 25 | "noEmit": true, 26 | "jsx": "react" 27 | }, 28 | "include": [ 29 | "src" 30 | ], 31 | "exclude": [ 32 | "node_modules", 33 | "lib", 34 | "stories" 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/.gitignore: -------------------------------------------------------------------------------- 1 | .env -------------------------------------------------------------------------------- /packages/chakra-ui-core/README.md: -------------------------------------------------------------------------------- 1 | # @hypertheme-editor/chakra-ui-core 2 | 3 | React components and hooks of [HyperTheme Editor](#hypertheme-editor). 4 | 5 | This module contains all the building blocks necessary to create a **theme editor** that works with [Chakra UI](https://chakra-ui.com/). 6 | 7 | **Don't use this package directly, instead use the [@hypertheme-editor/chakra-ui](https://www.npmjs.com/package/@hypertheme-editor/chakra-ui) package.** 8 | 9 | Here's a list of the components and hooks provided by this module with a link to their documentation: 10 | 11 | #### Components: 12 | 13 | - [`ThemeEditorProvider`](https://hyperthe.me/documentation/components/ThemeEditorProvider) 14 | - [`ThemeEditor`](https://hyperthe.me/documentation/components/ThemeEditor) 15 | - [`ThemeEditorDrawer`](https://hyperthe.me/documentation/components/ThemeEditorDrawer) 16 | - [`ThemeEditorDrawerHeader`](https://hyperthe.me/documentation/components/ThemeEditorDrawerHeader) 17 | - [`ThemeEditorDrawerFooter`](https://hyperthe.me/documentation/components/ThemeEditorDrawerFooter) 18 | - [`ThemeEditorRootPanel`](https://hyperthe.me/documentation/components/ThemeEditorRootPanel) 19 | - [`ThemeEditorButton`](https://hyperthe.me/documentation/components/ThemeEditorButton) 20 | - [`ColorModeToggle`](https://hyperthe.me/documentation/components/ColorModeToggle) 21 | - [`ThemeIcon`](https://hyperthe.me/documentation/components/ThemeIcon) 22 | 23 | #### Hooks: 24 | - [`useThemeEditor`](https://hyperthe.me/documentation/hooks/useThemeEditor) 25 | 26 | ## HyperTheme Editor 27 | 28 | ![HyperTheme Editor screen shot](https://www.hyperthe.me/images/social-banner.jpg) 29 | 30 | Powerful visual theme editor for your next Chakra UI project. 31 | 32 | Learn more about it on the website: [`hyperthe.me`](https://hyperthe.me). 33 | 34 | ### Documentation 35 | 36 | Read the HyperTheme Editor [documentation here](https://hyperthe.me/documentation). 37 | 38 | ## License 39 | 40 | HyperTheme Editor is licensed under the [MIT License](https://github.com/Hyperting/hypertheme-editor/blob/main/LICENSE) by [Hyperting S.r.l.](https://hyperting.com). 41 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hypertheme-editor/chakra-ui-core", 3 | "version": "0.2.2", 4 | "description": "", 5 | "main": "lib/index.js", 6 | "module": "lib/index.esm.js", 7 | "types": "lib/index.d.ts", 8 | "files": [ 9 | "lib" 10 | ], 11 | "scripts": { 12 | "test": "echo \"Error: no test specified\" && exit 1", 13 | "build": "rollup -c", 14 | "start": "rollup -c -w" 15 | }, 16 | "author": "marco", 17 | "license": "ISC", 18 | "peerDependencies": { 19 | "@chakra-ui/react": ">= 1.0.0", 20 | "framer-motion": ">= 4", 21 | "immer": "^9.0.6", 22 | "react": ">=16.8.6", 23 | "react-dom": ">=16.8.6", 24 | "react-icons": "^4.2.0" 25 | }, 26 | "dependencies": { 27 | "@hypertheme-editor/chakra-ui-theme": "^0.2.2", 28 | "react-google-font-loader": "^1.1.0", 29 | "recoil": "^0.5.2" 30 | }, 31 | "publishConfig": { 32 | "access": "public", 33 | "registry": "https://registry.npmjs.org/" 34 | } 35 | } -------------------------------------------------------------------------------- /packages/chakra-ui-core/rollup.config.js: -------------------------------------------------------------------------------- 1 | import peerDepsExternal from 'rollup-plugin-peer-deps-external' 2 | import resolve from '@rollup/plugin-node-resolve' 3 | import commonjs from '@rollup/plugin-commonjs' 4 | import typescript from 'rollup-plugin-typescript2' 5 | 6 | const packageJson = require('./package.json') 7 | 8 | export default { 9 | input: 'src/index.ts', 10 | output: [ 11 | { 12 | file: packageJson.main, 13 | format: 'cjs', 14 | sourcemap: true, 15 | }, 16 | { 17 | file: packageJson.module, 18 | format: 'esm', 19 | sourcemap: true, 20 | }, 21 | ], 22 | plugins: [ 23 | peerDepsExternal(), 24 | resolve(), 25 | commonjs(), 26 | typescript({ 27 | useTsconfigDeclarationDir: true, 28 | }), 29 | ], 30 | } 31 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/components/base/BaseList.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react' 2 | import { useColorModeValue, Stack, StackProps } from '@chakra-ui/react' 3 | 4 | export type BaseListProps = {} & StackProps 5 | 6 | const BaseList: FC = (props) => { 7 | const { children, ...rest } = props 8 | const bgColor = useColorModeValue('white', 'gray.800') 9 | const shadow = useColorModeValue('surface', 'surfaceDark') 10 | 11 | return ( 12 | 20 | {children} 21 | 22 | ) 23 | } 24 | 25 | export default BaseList 26 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/components/base/BaseListItem.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react' 2 | import { 3 | useColorModeValue, 4 | Box, 5 | Text, 6 | Circle, 7 | Stack, 8 | Flex, 9 | ThemingProps, 10 | As, 11 | BoxProps, 12 | } from '@chakra-ui/react' 13 | import { FaArrowRight } from 'react-icons/fa' 14 | 15 | export type BaseListItemProps = { 16 | subtitle?: string 17 | bgImage?: string 18 | icon?: As 19 | endIcon?: React.ReactElement 20 | isClickable?: boolean 21 | } & Pick & 22 | BoxProps 23 | 24 | const BaseListItem: FC = ({ 25 | title, 26 | subtitle, 27 | colorScheme = 'gray', 28 | children, 29 | icon: Icon, 30 | endIcon = , 31 | isClickable, 32 | ...rest 33 | }) => { 34 | const focusBgColor = useColorModeValue(`${colorScheme}.100`, `${colorScheme}.800`) 35 | 36 | return ( 37 | 49 | 50 | 51 | {Icon && ( 52 | 59 | 60 | 61 | )} 62 | {title && ( 63 | 71 | {title} 72 | 73 | )} 74 | {children} 75 | 76 | {subtitle && ( 77 | 78 | {subtitle} 79 | 80 | )} 81 | 82 | 83 | {endIcon && isClickable && ( 84 | 93 | {React.cloneElement(endIcon, {})} 94 | 95 | )} 96 | 97 | ) 98 | } 99 | 100 | export default BaseListItem 101 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/components/base/BaseMenu.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, ReactElement, useCallback } from 'react' 2 | import { 3 | Flex, 4 | Text, 5 | Menu, 6 | MenuButton, 7 | MenuList, 8 | Stack, 9 | MenuProps, 10 | Divider, 11 | Portal, 12 | useColorModeValue, 13 | MenuButtonProps, 14 | } from '@chakra-ui/react' 15 | import { FaChevronDown } from 'react-icons/fa' 16 | 17 | type BaseMenuProps = { 18 | title?: string 19 | subtitle?: string 20 | trigger?: ReactElement 21 | buttonProps?: MenuButtonProps 22 | hasPortal?: boolean 23 | } & MenuProps 24 | 25 | const BaseMenu: FC = (props) => { 26 | const { children, title = 'Menu 4', subtitle, trigger, buttonProps, hasPortal, ...rest } = props 27 | const shadow = useColorModeValue('lg', 'xl') 28 | 29 | const List = useCallback(() => { 30 | return ( 31 | 32 | {subtitle && ( 33 | <> 34 | 35 | {subtitle} 36 | 37 | 38 | 39 | )} 40 | 41 | {children} 42 | 43 | 44 | ) 45 | }, [subtitle, children]) 46 | 47 | return ( 48 | 49 | 50 | {trigger || ( 51 | 52 | {title} 53 | 54 | 55 | )} 56 | 57 | {hasPortal ? ( 58 | 59 | 60 | 61 | ) : ( 62 | 63 | )} 64 | 65 | ) 66 | } 67 | 68 | export default BaseMenu 69 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/components/base/BaseMenuItem.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react' 2 | import { 3 | Text, 4 | MenuItem, 5 | Circle, 6 | ThemingProps, 7 | Flex, 8 | Heading, 9 | Stack, 10 | MenuItemProps, 11 | useColorModeValue, 12 | As, 13 | } from '@chakra-ui/react' 14 | import { FaArrowRight } from 'react-icons/fa' 15 | 16 | export type BaseMenuItemProps = { 17 | subtitle?: string 18 | children?: any 19 | bgImage?: string 20 | icon?: As 21 | endIcon?: As 22 | } & Omit & 23 | Pick 24 | 25 | const BaseMenuItem: FC = ({ 26 | subtitle, 27 | colorScheme = 'gray', 28 | children, 29 | icon: Icon, 30 | endIcon: EndIcon = FaArrowRight, 31 | ...rest 32 | }) => { 33 | const focusBgColor = useColorModeValue(`${colorScheme}.100`, `${colorScheme}.800`) 34 | 35 | return ( 36 | 51 | 52 | 53 | {Icon && ( 54 | 61 | 62 | 63 | )} 64 | 65 | 72 | {children} 73 | 74 | 75 | 76 | {subtitle} 77 | 78 | 79 | 80 | {EndIcon && ( 81 | 90 | 91 | 92 | )} 93 | 94 | ) 95 | } 96 | 97 | export default BaseMenuItem 98 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/components/base/ColorModeToggle.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Stack, 3 | Text, 4 | useColorMode, 5 | StackProps, 6 | ThemingProps, 7 | Tabs, 8 | TabList, 9 | Tab, 10 | Icon, 11 | HStack, 12 | } from '@chakra-ui/react' 13 | import React, { FC } from 'react' 14 | import { ImSun } from 'react-icons/im' 15 | import { RiMoonClearLine } from 'react-icons/ri' 16 | import { IconSwitch } from './IconSwitch' 17 | 18 | type ColorModeToggleProps = { 19 | size?: 'sm' | 'md' | 'lg' 20 | showLabel?: boolean 21 | } & StackProps & 22 | ThemingProps 23 | 24 | const ColorModeToggle: FC = (props) => { 25 | const { size = 'md', showLabel, colorScheme = 'gray', fontSize, ...rest } = props 26 | const { colorMode, setColorMode } = useColorMode() 27 | const colorModeValues = ['light', 'dark'] 28 | 29 | return ( 30 | 37 | {showLabel && ( 38 | 39 | {colorMode} 40 | 41 | )} 42 | {/* */} 53 | 61 | 62 | {colorModeValues.map((value) => ( 63 | setColorMode(value)} 79 | > 80 | 94 | 95 | ))} 96 | 97 | 98 | 99 | ) 100 | } 101 | 102 | export default ColorModeToggle 103 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/components/base/ConfirmModal.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, ReactNode } from 'react' 2 | import { 3 | As, 4 | Button, 5 | Heading, 6 | Modal, 7 | ModalBody, 8 | ModalCloseButton, 9 | ModalContent, 10 | ModalFooter, 11 | ModalHeader, 12 | ModalOverlay, 13 | ModalProps, 14 | ThemingProps, 15 | useColorModeValue, 16 | } from '@chakra-ui/react' 17 | 18 | type Props = { 19 | title?: string 20 | description?: string 21 | onConfirm: () => void 22 | onClose: () => void 23 | children?: ReactNode 24 | confirmIcon?: As 25 | avoidIcon?: As 26 | } & Omit & 27 | Pick 28 | 29 | export const ConfirmModal: FC = ({ 30 | title = 'Confirm action', 31 | description = 'If your are sure, please confirm this action.', 32 | isOpen, 33 | onClose, 34 | onConfirm, 35 | confirmIcon: ConfirmIcon, 36 | avoidIcon: AvoidIcon, 37 | children, 38 | colorScheme, 39 | ...rest 40 | }) => { 41 | const shadow = useColorModeValue('surface', 'surfaceDark') 42 | const initialFocusRef = React.useRef(null) 43 | 44 | return ( 45 | 53 | 54 | 55 | 56 | 57 | {title} 58 | 59 | 60 | 61 | 62 | {description && {description}} 63 | 64 | 65 | 76 | 85 | 86 | 87 | 88 | ) 89 | } 90 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/components/base/CreditsBox.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, ReactNode } from 'react' 2 | import { Text, useColorModeValue, Stack, Link, StackProps, As, Flex } from '@chakra-ui/react' 3 | import { FaHeart, FaSearch } from 'react-icons/fa' 4 | 5 | type Props = { 6 | icon?: As 7 | title?: string 8 | company?: string 9 | product?: string 10 | children?: ReactNode 11 | } & StackProps 12 | 13 | const CreditsBox: FC = ({ 14 | icon: IconComp = FaHeart, 15 | title = 'is made with 💖 by ', 16 | company = 'Hyperting', 17 | product = 'HyperTheme', 18 | children, 19 | ...rest 20 | }) => { 21 | const bgColor = useColorModeValue('white', 'gray.900') 22 | 23 | return ( 24 | 37 | 38 | 39 | {product} 40 | 41 | {title} 42 | 43 | {company} 44 | 45 | 46 | 47 | {children && {children}} 48 | 49 | ) 50 | } 51 | 52 | export default CreditsBox 53 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/components/base/ElementsHighlighter.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useCallback, useMemo } from 'react' 2 | import { Box, BoxProps } from '@chakra-ui/react' 3 | import { ElementsMap } from '../../utils/ElementsMap' 4 | 5 | const replaceDotsWithDash = (str: string) => str.replace(/\./g, '-') 6 | 7 | type Props = { 8 | themeKeys: string | string[] 9 | } & BoxProps 10 | 11 | export const ElementsHighlighter: FC = ({ themeKeys, ...rest }) => { 12 | const cssVarNames = useMemo( 13 | () => 14 | typeof themeKeys === 'string' 15 | ? [replaceDotsWithDash(themeKeys)] 16 | : themeKeys.map((k) => replaceDotsWithDash(k)), 17 | [themeKeys] 18 | ) 19 | 20 | const onHover = useCallback((e) => { 21 | const toRenderElements = {} 22 | for (const cssVarName of cssVarNames) { 23 | if (ElementsMap.getInstance().cssVarElementsMap['--chakra-' + cssVarName]) { 24 | toRenderElements[cssVarName] = ElementsMap.getInstance().cssVarElementsMap[ 25 | '--chakra-' + cssVarName 26 | ] 27 | } 28 | } 29 | 30 | ElementsMap.getInstance().toRenderElements = toRenderElements 31 | }, []) 32 | 33 | const onLeave = useCallback((e) => { 34 | ElementsMap.getInstance().toRenderElements = {} 35 | }, []) 36 | 37 | return 38 | } 39 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/components/base/EmptyBox.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, ReactNode } from 'react' 2 | import { 3 | Text, 4 | useColorModeValue, 5 | Heading, 6 | Stack, 7 | Icon, 8 | StackProps, 9 | As, 10 | Flex, 11 | } from '@chakra-ui/react' 12 | import { FaSearch } from 'react-icons/fa' 13 | 14 | type Props = { 15 | icon?: As 16 | title?: string 17 | subtitle?: string 18 | children?: ReactNode 19 | } & StackProps 20 | 21 | const EmptyBox: FC = ({ 22 | icon: IconComp = FaSearch, 23 | title = 'Search font family', 24 | subtitle = 'No font family selected', 25 | children, 26 | ...rest 27 | }) => { 28 | const bgColor = useColorModeValue('gray.100', 'gray.900') 29 | 30 | return ( 31 | 42 | 43 | {title} 44 | {subtitle} 45 | {children && {children}} 46 | 47 | ) 48 | } 49 | 50 | export default EmptyBox 51 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/components/base/IconSwitch.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { 3 | useCheckbox, 4 | UseCheckboxProps, 5 | chakra, 6 | forwardRef, 7 | omitThemingProps, 8 | SystemStyleObject, 9 | useMultiStyleConfig, 10 | SwitchProps, 11 | Icon, 12 | As, 13 | } from '@chakra-ui/react' 14 | import { FaMoon, FaSun } from 'react-icons/fa' 15 | 16 | const cx = (...classNames: any[]) => classNames.filter(Boolean).join(' ') 17 | const dataAttr = (condition: boolean | undefined) => 18 | (condition ? '' : undefined) as boolean | 'true' | 'false' 19 | 20 | type IconSwitchProps = { 21 | trueIcon: As 22 | falseIcon: As 23 | } & SwitchProps 24 | 25 | export const IconSwitch = forwardRef((props, ref) => { 26 | const { colorScheme = 'gray', color, bgColor, ...rest } = props 27 | const styles = useMultiStyleConfig('Switch', rest) 28 | 29 | const { 30 | spacing = '0.5rem', 31 | children, 32 | trueIcon: TrueIcon, 33 | falseIcon: FalseIcon, 34 | ...ownProps 35 | } = omitThemingProps(rest) 36 | 37 | const { state, getInputProps, getCheckboxProps, getRootProps, getLabelProps } = useCheckbox( 38 | ownProps 39 | ) 40 | 41 | const containerStyles: SystemStyleObject = React.useMemo( 42 | () => ({ 43 | display: 'inline-block', 44 | verticalAlign: 'middle', 45 | lineHeight: 'normal', 46 | ...styles.container, 47 | }), 48 | [styles.container] 49 | ) 50 | 51 | const trackStyles: SystemStyleObject = React.useMemo( 52 | () => ({ 53 | display: 'inline-flex', 54 | flexShrink: 0, 55 | justifyContent: 'flex-start', 56 | boxSizing: 'content-box', 57 | cursor: 'pointer', 58 | ...styles.track, 59 | color, 60 | bgColor, 61 | '&[data-checked]': { 62 | bgColor, 63 | }, 64 | }), 65 | [styles.track, color, bgColor] 66 | ) 67 | 68 | const labelStyles: SystemStyleObject = React.useMemo( 69 | () => ({ 70 | userSelect: 'none', 71 | marginStart: spacing, 72 | ...styles.label, 73 | }), 74 | [spacing, styles.label] 75 | ) 76 | 77 | const thumbStyles: SystemStyleObject = React.useMemo( 78 | () => ({ 79 | alignItems: 'center', 80 | justifyContent: 'center', 81 | d: 'flex', 82 | ...styles.thumb, 83 | bgColor: props.value == 'light' ? 'gray.100' : 'gray.800', 84 | }), 85 | [styles.thumb] 86 | ) 87 | 88 | const iconStyle = React.useMemo( 89 | () => ({ 90 | color: props.color 91 | ? props.color 92 | : state.isChecked 93 | ? `${colorScheme}.400` 94 | : `${colorScheme}.700`, 95 | }), 96 | [props.color, state.isChecked] 97 | ) 98 | 99 | const iconSize = React.useMemo(() => { 100 | if (!props.size || props.size == 'lg') { 101 | return '1rem' 102 | } else if (props.size == 'sm') { 103 | return '0.6rem' 104 | } else { 105 | return '0.75rem' 106 | } 107 | }, [props.size]) 108 | 109 | return ( 110 | 115 | 116 | 117 | 123 | {!state.isChecked ? ( 124 | 125 | ) : ( 126 | 127 | )} 128 | 129 | 130 | {children && ( 131 | 132 | {children} 133 | 134 | )} 135 | 136 | ) 137 | }) 138 | 139 | { 140 | /* 145 | 146 | 147 | 153 | {!state.isChecked ? ( 154 | 155 | ) : ( 156 | 157 | )} 158 | 159 | 160 | {children && ( 161 | 162 | {children} 163 | 164 | )} 165 | */ 166 | } 167 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/components/base/RadioBox.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, HTMLProps } from 'react' 2 | import { Box, Flex, useRadio, Radio, useColorModeValue } from '@chakra-ui/react' 3 | 4 | const RadioBox: FC = (props) => { 5 | const { colorScheme = 'gray', p, py = 5, px = 5, pb, pt, isDisabled, ...rest } = props 6 | const { getInputProps, getCheckboxProps } = useRadio(props) 7 | 8 | const input = getInputProps() 9 | const checkbox = getCheckboxProps() 10 | 11 | const color = useColorModeValue('gray.700', 'whiteAlpha.800') 12 | const bgColor = useColorModeValue('white', 'gray.900') 13 | const shadow = useColorModeValue('surface', 'surfaceDark') 14 | const radioColorScheme = colorScheme || useColorModeValue('whiteAlpha', 'blackAlpha') 15 | const checkedBgColor = useColorModeValue(`${colorScheme}.300`, `${colorScheme}.700`) 16 | 17 | return ( 18 | 27 | 28 | 46 | 55 | {props.children} 56 | 57 | 58 | ) 59 | } 60 | 61 | export default RadioBox 62 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/components/base/ThemeIcon.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useMemo } from 'react' 2 | import { 3 | Box, 4 | SimpleGrid, 5 | useColorModeValue, 6 | SimpleGridProps, 7 | Theme, 8 | Skeleton, 9 | useTheme as useChakraTheme, 10 | } from '@chakra-ui/react' 11 | import { COLOR_PICKER_TRANSPARENT_SAFE_BG_B64 } from '../../constants' 12 | import { useThemeEditor } from '../../hooks/useThemeEditor' 13 | 14 | export type ThemeIconProps = { 15 | theme?: Theme 16 | colors?: {}[] // Pick[] 17 | size?: 'sm' | 'md' | 'lg' | 'xs' 18 | } & SimpleGridProps 19 | 20 | const ThemeIcon: FC = (props) => { 21 | const { theme } = useThemeEditor() 22 | const chakraTheme = useChakraTheme() 23 | const { colors, size, ...rest } = props 24 | const safeB64Bg = useColorModeValue( 25 | COLOR_PICKER_TRANSPARENT_SAFE_BG_B64[0], 26 | COLOR_PICKER_TRANSPARENT_SAFE_BG_B64[1] 27 | ) 28 | 29 | const iconSize = useMemo(() => { 30 | switch (size) { 31 | case 'xs': 32 | return 6 33 | case 'sm': 34 | return 8 35 | case 'lg': 36 | return 14 37 | default: 38 | return 12 39 | } 40 | }, [size]) 41 | 42 | const iconSpacing = useMemo(() => { 43 | switch (size) { 44 | case 'xs': 45 | return 1 46 | case 'sm': 47 | return 1 48 | case 'lg': 49 | return 2 50 | default: 51 | return 2 52 | } 53 | }, [size]) 54 | 55 | const iconBorder = useMemo(() => { 56 | switch (size) { 57 | case 'xs': 58 | return 0.5 59 | case 'sm': 60 | return 1 61 | case 'lg': 62 | return 2 63 | default: 64 | return 2 65 | } 66 | }, [size]) 67 | 68 | const primaryColor = useMemo(() => { 69 | if (theme?.colors?.primary) return theme.colors.primary 70 | return chakraTheme?.colors?.primary || chakraTheme?.colors?.purple 71 | }, [chakraTheme, theme]) 72 | 73 | const secondaryColor = useMemo(() => { 74 | if (theme?.colors?.secondary) return theme.colors.secondary 75 | return chakraTheme?.colors?.secondary || chakraTheme?.colors?.green 76 | }, [chakraTheme, theme]) 77 | 78 | const iconColors = useMemo(() => { 79 | if (colors && colors.length >= 3) return colors 80 | return [primaryColor, secondaryColor, theme?.colors?.gray, theme?.colors?.blue] 81 | }, [theme, colors, primaryColor, secondaryColor]) 82 | 83 | if (!theme) { 84 | return ( 85 | 93 | 94 | 95 | 96 | 97 | 98 | ) 99 | } 100 | 101 | return ( 102 | 110 | 117 | 124 | 125 | 126 | 133 | 140 | 141 | 142 | 149 | 156 | 157 | 158 | 165 | 172 | 173 | 174 | ) 175 | } 176 | 177 | export default ThemeIcon 178 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/components/base/ThemeResetPopup.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useCallback } from 'react' 2 | import { 3 | Button, 4 | StackProps, 5 | Modal, 6 | ModalContent, 7 | ModalHeader, 8 | ModalCloseButton, 9 | ModalBody, 10 | ModalFooter, 11 | Text, 12 | ModalProps, 13 | } from '@chakra-ui/react' 14 | import { GrPowerReset } from 'react-icons/gr' 15 | 16 | type ThemeResetPopupProps = { 17 | isOpen: boolean 18 | onReset?: () => void 19 | onClose: () => void 20 | } & StackProps & 21 | ModalProps 22 | 23 | const ThemeResetPopup: FC = ({ isOpen, onReset, onClose, ...rest }) => { 24 | const handleReset = useCallback( 25 | (id) => { 26 | onReset && onReset() 27 | }, 28 | [onReset] 29 | ) 30 | 31 | return ( 32 | 33 | 34 | Reset theme? 35 | 36 | 37 | 38 | You are using a modified version of the original theme, woud you like to reset the theme 39 | to original one? 40 | 41 | 42 | 43 | 46 | 49 | 50 | 51 | 52 | ) 53 | } 54 | 55 | export default ThemeResetPopup 56 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/components/base/ThemesList.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useCallback, useEffect, useState } from 'react' 2 | import { 3 | useRadioGroup, 4 | Stack, 5 | Box, 6 | Text, 7 | Button, 8 | StackProps, 9 | Flex, 10 | Icon, 11 | As, 12 | Editable, 13 | EditablePreview, 14 | EditableInput, 15 | useColorModeValue, 16 | Theme, 17 | useDisclosure, 18 | useBoolean, 19 | } from '@chakra-ui/react' 20 | import RadioBox from './RadioBox' 21 | import ThemeIcon from './ThemeIcon' 22 | import { timeSince } from '../../utils/timeSince' 23 | import { FaTrashAlt } from 'react-icons/fa' 24 | import { ConfirmModal } from './ConfirmModal' 25 | 26 | type ThemeItem = { 27 | title: string 28 | value: string 29 | updated: Date 30 | id: string 31 | } 32 | 33 | type ThemesListProps = { 34 | themes: ThemeItem[] 35 | onChange?: (nextValue: string) => void 36 | isEditable?: boolean 37 | hasDelete?: boolean 38 | onDelete?: (id: string) => void 39 | } & StackProps 40 | 41 | const ThemesList: FC = ({ 42 | onChange, 43 | themes: defaultThemes, 44 | isEditable = true, 45 | hasDelete = true, 46 | onDelete, 47 | ...rest 48 | }) => { 49 | const [themes, setThemes] = useState(defaultThemes) 50 | const { getRootProps, getRadioProps } = useRadioGroup({ 51 | name: 'themes-list-radio-group', 52 | defaultValue: themes && themes.length && themes[0].id, 53 | onChange: onChange, 54 | }) 55 | 56 | const group = getRootProps() 57 | 58 | const handleDelete = useCallback( 59 | (id) => { 60 | themes && setThemes(themes.slice(0, themes.length - 1)) 61 | onDelete && onDelete(id) 62 | }, 63 | [onDelete] 64 | ) 65 | 66 | return ( 67 | 68 | {themes.map((themeitem) => { 69 | const radio = getRadioProps({ value: themeitem.id }) 70 | return ( 71 | 72 | 78 | 79 | ) 80 | })} 81 | 82 | ) 83 | } 84 | 85 | const ThemesListItem = ({ title, updated, isEditable, onDelete, hasDelete, value }) => { 86 | const bgColor = useColorModeValue('white', 'gray.800') 87 | const editableSelectedColor = useColorModeValue('gray.200', 'gray.800') 88 | const { onOpen, onClose, isOpen } = useDisclosure() 89 | 90 | const handleDelete = useCallback( 91 | (event: React.MouseEvent) => { 92 | event.preventDefault() 93 | event.stopPropagation() 94 | onOpen() 95 | }, 96 | [onDelete] 97 | ) 98 | 99 | const handleConfirm = useCallback(() => { 100 | onClose() 101 | if (onDelete) { 102 | onDelete(value) 103 | } 104 | }, [onClose, onDelete]) 105 | 106 | return ( 107 | <> 108 | 115 | 122 | 123 | 124 | 134 | 135 | 136 | 137 | {timeSince(updated)} ago 138 | 139 | 140 | {hasDelete && ( 141 | 156 | )} 157 | 158 | 165 | 166 | ) 167 | } 168 | 169 | export default ThemesList 170 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/components/base/UpgradeBanner.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, ReactNode } from 'react' 2 | import { 3 | Text, 4 | useColorModeValue, 5 | Box, 6 | Stack, 7 | Button, 8 | StackProps, 9 | As, 10 | Flex, 11 | ThemingProps, 12 | Heading, 13 | Link, 14 | } from '@chakra-ui/react' 15 | import { FaLockOpen, FaUserTie } from 'react-icons/fa' 16 | 17 | type Props = { 18 | icon?: As 19 | title?: string 20 | subtitle?: string 21 | children?: ReactNode 22 | } & StackProps & 23 | Pick 24 | 25 | const UpgradeBanner: FC = ({ 26 | icon: IconComp = FaUserTie, 27 | title = 'Unlock Pro features', 28 | subtitle = 'Learn more', 29 | colorScheme = 'primary', 30 | children, 31 | ...rest 32 | }) => { 33 | const bgColor = useColorModeValue('white', 'gray.800') 34 | const shadow = useColorModeValue('surface', 'surfaceDark') 35 | 36 | return ( 37 | 50 | 51 | 61 | 62 | 63 | 64 | 65 | {title} 66 | 67 | 68 | {subtitle && {subtitle}} 69 | 70 | 71 | 72 | 77 | 80 | 81 | 82 | ) 83 | } 84 | 85 | export default UpgradeBanner 86 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/components/base/index.ts: -------------------------------------------------------------------------------- 1 | import ColorModeToggle from './ColorModeToggle' 2 | import EmptyBox from './EmptyBox' 3 | import BaseList from './BaseList' 4 | import BaseListItem from './BaseListItem' 5 | import BaseMenu from './BaseMenu' 6 | import BaseMenuItem from './BaseMenuItem' 7 | import ThemeIcon from './ThemeIcon' 8 | import ThemeResetPopup from './ThemeResetPopup' 9 | import { ElementsHighlighter } from './ElementsHighlighter' 10 | 11 | export { 12 | ColorModeToggle, 13 | EmptyBox, 14 | BaseList, 15 | BaseListItem, 16 | BaseMenu, 17 | BaseMenuItem, 18 | ThemeIcon, 19 | ThemeResetPopup, 20 | ElementsHighlighter, 21 | } 22 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/components/config/ThemeEditorConfig.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useCallback, useState } from 'react' 2 | import { 3 | Box, 4 | Flex, 5 | Stack, 6 | Divider, 7 | Switch, 8 | Text, 9 | Tag, 10 | Center, 11 | Input, 12 | useColorMode, 13 | } from '@chakra-ui/react' 14 | import { FaSun } from 'react-icons/fa' 15 | import { RiMoonClearFill } from 'react-icons/ri' 16 | import { IconSwitch } from '../base/IconSwitch' 17 | import { useThemeEditor } from '../../hooks/useThemeEditor' 18 | 19 | type Props = { 20 | // 21 | } 22 | 23 | export const ThemeEditorConfig: FC = () => { 24 | const { theme, setTheme } = useThemeEditor() 25 | const { colorMode } = useColorMode() 26 | 27 | const [initialColorMode, setInitalColorMode] = useState(theme?.config?.initialColorMode) 28 | 29 | const toggleInitialColorMode = useCallback(() => { 30 | setInitalColorMode(initialColorMode == 'light' ? 'dark' : 'light') 31 | }, [initialColorMode]) 32 | 33 | return ( 34 | }> 35 | 36 | Use system color mode 37 | 38 | 39 | 40 | Initial color mode 41 |
42 | {initialColorMode} 43 | 55 |
56 |
57 | 58 | 59 | CSS var prefix 60 | 61 | Add a custom prefix for CSS variables 62 | 63 | 64 |
65 | 72 |
73 |
74 |
75 | ) 76 | } 77 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/components/config/ThemeEditorConfigItem.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react' 2 | import { Box, Flex, Stack, Divider } from '@chakra-ui/react' 3 | import { useThemeEditor } from '../../hooks/useThemeEditor' 4 | 5 | type Props = { 6 | scale?: string[] 7 | } 8 | 9 | export const ThemeEditorConfigItem: FC = ({ 10 | scale = ['useSystemColorMode', 'initialColorMode', 'cssVarPrefix'], 11 | }) => { 12 | const { theme, setTheme } = useThemeEditor() 13 | 14 | return ( 15 | 16 | 17 | }> 18 | {/* {scale.map((size) => ( 19 | 20 | ))} */} 21 | 22 | 23 | 24 | ) 25 | } 26 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/components/config/index.ts: -------------------------------------------------------------------------------- 1 | import { ThemeEditorConfig } from './ThemeEditorConfig' 2 | 3 | export { ThemeEditorConfig } 4 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/components/theme-editor-provider/ThemeEditorProvider.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react' 2 | import { useTheme } from '@chakra-ui/react' 3 | import { RecoilRoot } from 'recoil' 4 | import { ThemeStateInitializer } from './ThemeStateInitializer' 5 | import { GoogleFontFamiliesStateInitializer } from '../../utils/GoogleFontFamiliesStateInitializer' 6 | 7 | type Props = { 8 | children: React.ReactNode | React.ReactNode[] 9 | disableGoogleFonts?: boolean 10 | } 11 | 12 | export const ThemeEditorProvider: FC = ({ children, disableGoogleFonts = true }) => { 13 | const chakraTheme = useTheme() 14 | 15 | return ( 16 | 17 | 18 | {!disableGoogleFonts && } 19 | {children} 20 | 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/components/theme-editor-provider/ThemeStateInitializer.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useEffect } from 'react' 2 | import { ThemeOverride } from '@chakra-ui/react' 3 | import { useRecoilState } from 'recoil' 4 | import { safeJsonParse } from '../../utils/safeJsonParse' 5 | import { THEME_STATE_LOCAL_STORAGE_KEY } from '../../constants' 6 | import { setThemeTokens } from '../../utils/updateThemeTokens' 7 | import { themeEditorState as hyperkitThemeState } from '../../hooks/useThemeEditor' 8 | 9 | type Props = { 10 | theme: ThemeOverride 11 | } 12 | 13 | export const ThemeStateInitializer: FC = ({ theme }) => { 14 | const [themeState, setThemeState] = useRecoilState(hyperkitThemeState) 15 | 16 | useEffect(() => { 17 | if (!themeState.currentTheme && !themeState.initialTheme) { 18 | const localStorageValue = localStorage.getItem(THEME_STATE_LOCAL_STORAGE_KEY) 19 | if (localStorageValue) { 20 | const parsedValue = safeJsonParse(localStorageValue) 21 | if (!parsedValue.error && parsedValue.value) { 22 | const currentTheme = 23 | parsedValue.value.currentTheme && Object.keys(parsedValue.value.currentTheme).length > 0 24 | ? parsedValue.value.currentTheme 25 | : (theme as any) 26 | setThemeState({ 27 | ...(parsedValue.value as any), 28 | undoable: parsedValue.value.undoable || [], 29 | undone: parsedValue.value.undone || [], 30 | initialTheme: theme as any, 31 | currentTheme, 32 | }) 33 | setThemeTokens(currentTheme) 34 | return 35 | } 36 | } 37 | 38 | setThemeState({ 39 | ...themeState, 40 | undoable: themeState.undoable || [], 41 | undone: themeState.undone || [], 42 | currentTheme: 43 | themeState.currentTheme && Object.keys(themeState.currentTheme).length > 0 44 | ? themeState.currentTheme 45 | : (theme as any), 46 | initialTheme: theme as any, 47 | }) 48 | } 49 | // eslint-disable-next-line react-hooks/exhaustive-deps 50 | }, []) 51 | 52 | return null 53 | } 54 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/components/theme-editor-provider/index.ts: -------------------------------------------------------------------------------- 1 | import { ThemeEditorProvider } from './ThemeEditorProvider' 2 | import { ThemeStateInitializer } from './ThemeStateInitializer' 3 | 4 | export { ThemeStateInitializer, ThemeEditorProvider } 5 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/components/theme-editor/ThemeDownloadButton.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useCallback, useEffect, useState } from 'react' 2 | import { 3 | Button, 4 | ButtonProps, 5 | Icon, 6 | CircularProgress, 7 | Divider, 8 | useToast, 9 | Text, 10 | Box, 11 | } from '@chakra-ui/react' 12 | import { FaChevronDown, FaDownload } from 'react-icons/fa' 13 | import { HiArrowDown } from 'react-icons/hi' 14 | 15 | import { SiJavascript, SiTypescript } from 'react-icons/si' 16 | import BaseMenu from '../base/BaseMenu' 17 | import BaseMenuItem from '../base/BaseMenuItem' 18 | import { API_ENDPOINT } from '../../constants' 19 | import { Theme, useThemeEditor } from '../../hooks/useThemeEditor' 20 | import { transform } from '@babel/core' 21 | import { BsArrowRight } from 'react-icons/bs' 22 | 23 | const GENERATE_THEME_ENDPOINT = `${API_ENDPOINT}/generate-theme` 24 | 25 | type Props = { 26 | baseTheme: any 27 | selectedProperties: string[] | number[] 28 | selectedLanguage: string 29 | } & ButtonProps 30 | 31 | export const ThemeDownloadButton: FC = ({ 32 | baseTheme, 33 | selectedProperties, 34 | selectedLanguage, 35 | ...rest 36 | }) => { 37 | const [downloading, setDownloading] = useState(false) 38 | let { theme } = useThemeEditor() 39 | const [editableTheme, setEditableTheme] = useState(baseTheme) 40 | const toast = useToast() 41 | 42 | const objectify = (array) => { 43 | return array.reduce((obj, item) => { 44 | if (theme !== undefined) { 45 | obj[item] = theme[item] 46 | return obj 47 | } 48 | }, {}) 49 | } 50 | 51 | useEffect(() => { 52 | const selected = objectify(selectedProperties) 53 | setEditableTheme({ ...selected, ...baseTheme }) 54 | }, [selectedProperties]) 55 | 56 | const handleDownload = useCallback( 57 | (language: string) => async () => { 58 | try { 59 | setDownloading(true) 60 | const result = await fetch(GENERATE_THEME_ENDPOINT, { 61 | method: 'POST', 62 | headers: { 63 | Accept: 'application/json', 64 | 'Content-Type': 'application/json', 65 | }, 66 | body: JSON.stringify({ theme: { ...editableTheme }, language }), 67 | }) 68 | 69 | if (!result.ok) { 70 | const jsonResult = await result.json() 71 | toast({ 72 | title: 'Error during the download.', 73 | description: jsonResult.error, 74 | status: 'error', 75 | duration: 5000, 76 | isClosable: true, 77 | }) 78 | return 79 | } 80 | const resultBlob = await result.blob() 81 | const url = window.URL.createObjectURL(resultBlob) 82 | const a = document.createElement('a') 83 | a.href = url 84 | a.download = 'theme.zip' 85 | document.body.appendChild(a) // we need to append the element to the dom -> otherwise it will not work in firefox 86 | a.click() 87 | a.remove() 88 | } catch (error) { 89 | console.log('wee error', error) 90 | // show an alert here 91 | // toast({ 92 | // title: 'Error during the download.', 93 | // description: <>{error?.messagge} || <>Unexpected error, 94 | // status: 'error', 95 | // duration: 5000, 96 | // isClosable: true, 97 | // }) 98 | } finally { 99 | setDownloading(false) 100 | } 101 | }, 102 | [theme, editableTheme, toast] 103 | ) 104 | 105 | return ( 106 | 120 | ) 121 | } 122 | 123 | /* 124 | return ( 125 | } 130 | buttonProps={{ w: { base: '100%', md: 'initial' } }} 131 | > 132 | 139 | Export TypeScript {downloading && } 140 | 141 | 142 | 149 | Export JavaScript {downloading && } 150 | 151 | 152 | ) 153 | */ 154 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/components/theme-editor/ThemeEditor.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react' 2 | import { ChakraProvider, useDisclosure, UseDisclosureProps } from '@chakra-ui/react' 3 | import { ThemeEditorDrawerProps } from './ThemeEditorDrawer' 4 | import { ThemeEditorButtonProps } from './ThemeEditorButton' 5 | import { theme } from '@hypertheme-editor/chakra-ui-theme' 6 | 7 | type ThemeEditorChild = React.ReactElement< 8 | ThemeEditorButtonProps | ThemeEditorDrawerProps, 9 | React.JSXElementConstructor 10 | > 11 | 12 | export type ThemeEditorProps = { 13 | children: ThemeEditorChild[] | ThemeEditorChild 14 | } & UseDisclosureProps 15 | 16 | export const ThemeEditor: FC = ({ children, ...disclosureProps }) => { 17 | const { isOpen, onOpen, onClose } = useDisclosure(disclosureProps) 18 | 19 | return ( 20 | 21 | {React.Children.map(React.Children.toArray(children), (child, index) => { 22 | return React.cloneElement(child as any, { 23 | isOpen, 24 | onOpen, 25 | onClose, 26 | }) 27 | })} 28 | 29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/components/theme-editor/ThemeEditorAccordion.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react' 2 | import { AccordionProps, Accordion, useColorModeValue, Stack } from '@chakra-ui/react' 3 | 4 | export type ThemeEditorAccordionProps = { 5 | children?: any 6 | } & AccordionProps 7 | 8 | export const ThemeEditorAccordion: FC = (props) => { 9 | const { children, ...rest } = props 10 | const shadow = useColorModeValue('surface', 'surfaceDark') 11 | const bgColor = useColorModeValue('white', 'gray.800') 12 | 13 | return ( 14 | 15 | {React.Children.map(children, (child, key) => { 16 | return React.cloneElement(child, { 17 | shadow, 18 | bgColor, 19 | borderRadius: rest.borderRadius || 'md', 20 | borderWidth: 0, 21 | key, 22 | border: 'none', 23 | }) 24 | })} 25 | 26 | ) 27 | } 28 | 29 | export default ThemeEditorAccordion 30 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/components/theme-editor/ThemeEditorAccordionItem.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react' 2 | import { 3 | Box, 4 | Text, 5 | AccordionItemProps, 6 | AccordionButton, 7 | AccordionItem, 8 | AccordionIcon, 9 | Circle, 10 | useColorModeValue, 11 | ThemingProps, 12 | As, 13 | Tag, 14 | Flex, 15 | Collapse, 16 | Icon as ChakraIcon, 17 | } from '@chakra-ui/react' 18 | import { FaLock } from 'react-icons/fa' 19 | 20 | export type ThemeEditorAccordionItemProps = { 21 | title?: string 22 | subtitle?: string 23 | icon?: As 24 | endIcon?: As 25 | tag?: string 26 | unmountOnExit?: boolean 27 | } & AccordionItemProps & 28 | Pick 29 | 30 | export const ThemeEditorAccordionItem: FC = (props) => { 31 | const { 32 | title = 'Accordion title', 33 | subtitle, 34 | colorScheme = 'gray', 35 | icon: Icon, 36 | tag, 37 | children, 38 | isDisabled, 39 | unmountOnExit = true, 40 | ...rest 41 | } = props 42 | const hoverBgColor = useColorModeValue('gray.200', 'gray.700') 43 | const bgColor = useColorModeValue(`${colorScheme}.200`, `${colorScheme}.900`) 44 | 45 | return ( 46 | 47 | {({ isExpanded }) => ( 48 | <> 49 | 54 | {Icon && ( 55 | 62 | 63 | 64 | )} 65 | 66 | 67 | {title} 68 | {tag && ( 69 | 76 | {tag} 77 | 78 | )} 79 | 80 | {subtitle && ( 81 | 82 | {subtitle} 83 | 84 | )} 85 | 86 | 87 | {!isDisabled ? : } 88 | 89 | 90 | *': { 95 | w: 'full', 96 | px: '0.5rem', 97 | py: '0.5rem', 98 | borderRadius: 'none', 99 | }, 100 | }} 101 | > 102 | {children} 103 | 104 | 105 | 106 | )} 107 | 108 | ) 109 | } 110 | 111 | export default ThemeEditorAccordionItem 112 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/components/theme-editor/ThemeEditorButton.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useMemo } from 'react' 2 | import { Button, ButtonProps, ThemeProvider, Text, Divider } from '@chakra-ui/react' 3 | import ThemeIcon from '../base/ThemeIcon' 4 | 5 | export type ThemeEditorButtonProps = { 6 | label?: string 7 | onOpen?: () => void 8 | isOpen?: boolean 9 | } & Omit 10 | 11 | export const ThemeEditorButton: FC = ({ 12 | label, 13 | size, 14 | onOpen, 15 | isOpen, 16 | ...rest 17 | }) => { 18 | const themeIconSize = useMemo(() => (typeof size === 'undefined' ? undefined : size), [size]) 19 | 20 | return ( 21 | 32 | ) 33 | } 34 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/components/theme-editor/ThemeEditorDrawer.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useRef, ReactElement } from 'react' 2 | import { 3 | Alert, 4 | AlertIcon, 5 | Drawer, 6 | DrawerBody, 7 | DrawerContent, 8 | DrawerProps, 9 | useColorModeValue, 10 | Text, 11 | } from '@chakra-ui/react' 12 | import { IconType } from 'react-icons' 13 | import ThemeEditorAccordion from './ThemeEditorAccordion' 14 | import ThemeEditorAccordionItem from './ThemeEditorAccordionItem' 15 | import { useIsMobile } from '../../utils/isMobile' 16 | import { ThemeEditorDrawerHeader, ThemeEditorDrawerHeaderProps } from './ThemeEditorDrawerHeader' 17 | import { ThemeEditorDrawerFooter, ThemeEditorDrawerFooterProps } from './ThemeEditorDrawerFooter' 18 | import CreditsBox from '../base/CreditsBox' 19 | import UpgradeBanner from '../base/UpgradeBanner' 20 | import { useIsBrowserCompatible } from '../../utils/isBrowserCompatible' 21 | import { ThemeEditorRootPanel } from './ThemeEditorRootPanel' 22 | import { DRAWER_EXPANDED_INDEX_LOCAL_STORAGE_KEY } from '../../constants' 23 | import { useAccordionState } from '../../hooks/useAccordionState' 24 | 25 | const mobileReadyItems = ['Colors'] 26 | 27 | export type ThemeEditorDrawerProps = Omit & { 28 | children?: 29 | | ReactElement<{ icon: IconType; title: string } & Record>[] 30 | | ReactElement<{ icon: IconType; title: string } & Record> 31 | hideUpgradeToPro?: boolean 32 | hideCredits?: boolean 33 | headerComponent?: React.ReactElement< 34 | ThemeEditorDrawerHeaderProps, 35 | React.JSXElementConstructor 36 | > 37 | footerComponent?: React.ReactElement< 38 | ThemeEditorDrawerFooterProps, 39 | React.JSXElementConstructor 40 | > 41 | // additionalBody?: React.ReactNode 42 | isOpen?: boolean 43 | onClose?: () => void 44 | } 45 | 46 | export const ThemeEditorDrawer: FC = ({ 47 | hideUpgradeToPro, 48 | hideCredits, 49 | onClose, 50 | isOpen, 51 | children, 52 | headerComponent = , 53 | footerComponent = , 54 | ...rest 55 | }) => { 56 | const bgColor = useColorModeValue('white', 'rgba(23,25,35,1)') 57 | const btnRef = useRef() 58 | const initialFocusRef = useRef() 59 | const isMobile = useIsMobile() 60 | const isCompatible = useIsBrowserCompatible() 61 | const [defaultIndex, setDefaultIndex] = useAccordionState(DRAWER_EXPANDED_INDEX_LOCAL_STORAGE_KEY) 62 | 63 | return ( 64 | 73 | 80 | {React.isValidElement(headerComponent) && 81 | React.cloneElement( 82 | headerComponent as React.ReactElement, 83 | { 84 | onClose, 85 | initialFocusRef, 86 | } 87 | )} 88 | 95 | {!isCompatible && ( 96 | 97 | 98 | This browser is not compatible, most of the features will not work. 99 | 100 | )} 101 | 107 | {React.Children.map(React.Children.toArray(children), (child, index) => { 108 | const { icon, title, children, ...panelProps } = (child as ReactElement).props 109 | return ( 110 | 118 | {(child as ReactElement).type === ThemeEditorRootPanel 119 | ? children 120 | : React.cloneElement( 121 | child as ReactElement, 122 | { 123 | ...panelProps, 124 | }, 125 | children 126 | )} 127 | 128 | ) 129 | })} 130 | 131 | {!hideUpgradeToPro && } 132 | {!hideCredits && } 133 | 134 | {React.isValidElement(footerComponent) && 135 | React.cloneElement( 136 | footerComponent as React.ReactElement, 137 | { 138 | // onClose, 139 | isMobile, 140 | } 141 | )} 142 | 143 | 144 | ) 145 | } 146 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/components/theme-editor/ThemeEditorDrawerFooter.tsx: -------------------------------------------------------------------------------- 1 | import { Button, Flex, Icon, useDisclosure } from '@chakra-ui/react' 2 | import React, { FC } from 'react' 3 | import { AiOutlineArrowDown } from 'react-icons/ai' 4 | import { ThemeDownloadButton } from '.' 5 | import { ThemeExportDrawer } from './ThemeExportDrawer' 6 | 7 | export type ThemeEditorDrawerFooterProps = { 8 | // onClose?: () => void 9 | isMobile?: boolean 10 | // actionButton?: React.ReactNode 11 | } 12 | export const ThemeEditorDrawerFooter: FC = ({ 13 | isMobile, 14 | // actionButton = , 15 | }) => { 16 | // const bgColor = useColorModeValue('whiteAlpha.600', 'gray.900') 17 | const { isOpen, onOpen, onClose } = useDisclosure() 18 | 19 | return ( 20 | 28 | 41 | 42 | 43 | ) 44 | } 45 | 46 | /* 47 | 54 | 64 | {actionButton} 65 | */ 66 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/components/theme-editor/ThemeEditorRootPanel.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react' 2 | import { IconType } from 'react-icons' 3 | import { ThemeEditorAccordionItemProps } from './ThemeEditorAccordionItem' 4 | 5 | export type ThemeEditorRootPanelProps = { 6 | icon: IconType 7 | title: string 8 | } & ThemeEditorAccordionItemProps 9 | 10 | export const ThemeEditorRootPanel: FC = () => { 11 | return null 12 | } 13 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/components/theme-editor/ThemeExportDrawer.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Drawer, 3 | DrawerBody, 4 | DrawerContent, 5 | Flex, 6 | Grid, 7 | GridItem, 8 | Heading, 9 | useColorModeValue, 10 | Text, 11 | useCheckboxGroup, 12 | Button, 13 | useColorMode, 14 | } from '@chakra-ui/react' 15 | import React, { FC, useState } from 'react' 16 | import { ThemeEditorDrawerHeaderProps } from '.' 17 | 18 | import { useThemeEditor } from '../../hooks/useThemeEditor' 19 | import { ThemeEditorDrawerHeader } from './ThemeEditorDrawerHeader' 20 | import { ThemeProperty } from './ThemeProperty' 21 | import { ThemeExportDrawerFooter } from './ThemeExportDrawerFooter' 22 | import { BiPaint } from 'react-icons/bi' 23 | import { AiOutlineFontSize, AiOutlineRadiusUpright } from 'react-icons/ai' 24 | import { BsFonts } from 'react-icons/bs' 25 | import { CgEditShadows, CgFontSpacing, CgFormatLineHeight, CgSpaceBetween } from 'react-icons/cg' 26 | import { FaBold } from 'react-icons/fa' 27 | 28 | export type ThemeExportDrawerProps = { 29 | isOpen: boolean 30 | onClose: () => void 31 | headerComponent?: React.ReactElement< 32 | ThemeEditorDrawerHeaderProps, 33 | React.JSXElementConstructor 34 | > 35 | } 36 | 37 | export const ThemeExportDrawer: FC = ({ 38 | isOpen, 39 | onClose, 40 | headerComponent = , 41 | }) => { 42 | const bgColor = useColorModeValue('white', 'gray.900') 43 | const { theme } = useThemeEditor() 44 | const { 45 | colors, 46 | fonts, 47 | fontSizes, 48 | fontWeights, 49 | lineHeights, 50 | letterSpacings, 51 | shadows, 52 | radii, 53 | space, 54 | ...baseTheme 55 | } = theme as any 56 | const themeLabels = [ 57 | { title: 'Colors', icon: BiPaint }, 58 | { title: 'Font', icon: BsFonts }, 59 | { title: 'Font Sizes', icon: AiOutlineFontSize }, 60 | { title: 'Font Weight', icon: FaBold }, 61 | { title: 'Line Height', icon: CgFormatLineHeight }, 62 | { title: 'Letter Spacing', icon: CgFontSpacing }, 63 | { title: 'Shadows', icon: CgEditShadows }, 64 | { title: 'Radii', icon: AiOutlineRadiusUpright }, 65 | { title: 'Space', icon: CgSpaceBetween }, 66 | ] 67 | const editableProperties = { 68 | colors, 69 | fonts, 70 | fontSizes, 71 | fontWeights, 72 | lineHeights, 73 | letterSpacings, 74 | shadows, 75 | radii, 76 | space, 77 | } 78 | const getKeys = (obj) => { 79 | return Object.keys(obj) 80 | } 81 | console.log(' getKeys') 82 | const { value, setValue, getCheckboxProps } = useCheckboxGroup({ 83 | defaultValue: [...getKeys(editableProperties)], 84 | }) 85 | console.log('theme', theme) 86 | console.log('editable', editableProperties) 87 | console.log('values', value) 88 | 89 | return ( 90 | 91 | 92 | {React.isValidElement(headerComponent) && 93 | React.cloneElement( 94 | headerComponent as React.ReactElement, 95 | { 96 | onClose, 97 | children: ( 98 | 99 | 100 | Export 101 | 102 | 110 | Hyper Theme 111 | 112 | 113 | ), 114 | } 115 | )} 116 | 117 | 118 | Export 119 | 135 | 136 | 137 | {getKeys(editableProperties).map((key, index) => { 138 | return ( 139 | 140 | 147 | 148 | ) 149 | })} 150 | 151 | 152 | 153 | 154 | 155 | ) 156 | } 157 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/components/theme-editor/ThemeExportDrawerFooter.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Button, 3 | DrawerFooter, 4 | Flex, 5 | HStack, 6 | Icon, 7 | Menu, 8 | MenuButton, 9 | MenuItem, 10 | MenuList, 11 | useColorMode, 12 | Text, 13 | } from '@chakra-ui/react' 14 | import React, { FC, useState } from 'react' 15 | import { MdKeyboardArrowDown } from 'react-icons/md' 16 | import { SiTypescript, SiJavascript } from 'react-icons/si' 17 | import { ThemeDownloadButton } from './ThemeDownloadButton' 18 | 19 | type ThemeExportDrawerFooterProps = { 20 | baseTheme: any 21 | value: any 22 | } 23 | 24 | export const ThemeExportDrawerFooter: FC = (props) => { 25 | const [selectedLanguage, setSelectedLanguage] = useState('ts') 26 | const { colorMode } = useColorMode() 27 | return ( 28 | 33 | 34 | 35 | } 42 | > 43 | 44 | 53 | 59 | 60 | {selectedLanguage === 'ts' ? 'TypeScript' : 'JavaScript'} 61 | 62 | 63 | 64 | setSelectedLanguage((e.target as HTMLInputElement).value)} 68 | > 69 | TypeScript 70 | 71 | setSelectedLanguage((e.target as HTMLInputElement).value)} 75 | > 76 | JavaScript 77 | 78 | 79 | 80 | 81 | 86 | 87 | ) 88 | } 89 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/components/theme-editor/ThemeProperty.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | useCheckbox, 3 | useColorMode, 4 | useColorModeValue, 5 | chakra, 6 | Flex, 7 | Text, 8 | Icon, 9 | } from '@chakra-ui/react' 10 | import React from 'react' 11 | 12 | export const ThemeProperty = (props) => { 13 | const { state, getCheckboxProps, getInputProps, getLabelProps, htmlProps } = useCheckbox(props) 14 | const { colorMode } = useColorMode() 15 | const bgColor = useColorModeValue('white', 'gray.800') 16 | const checkboxBgColor = useColorModeValue('gray.100', 'gray.900') 17 | const checked = state.isChecked || props.selected 18 | 19 | return ( 20 | 35 | 36 | 48 | 54 | 55 | 61 | {props.label} 62 | 63 | 64 | ) 65 | } 66 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/components/theme-editor/ThemeSwitchDrawer.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useMemo, useRef } from 'react' 2 | import { 3 | Icon, 4 | Button, 5 | Drawer, 6 | DrawerBody, 7 | DrawerContent, 8 | DrawerProps, 9 | useColorModeValue, 10 | DrawerFooter, 11 | Box, 12 | Text, 13 | } from '@chakra-ui/react' 14 | import { FaPlus } from 'react-icons/fa' 15 | import ThemesList from '../base/ThemesList' 16 | import { ThemeSwitchDrawerHeader } from './ThemeSwitchDrawerHeader' 17 | 18 | export type ThemeSwitchDrawerProps = {} & DrawerProps 19 | 20 | const ThemeSwitchDrawer: FC = (props) => { 21 | const { onClose, ...rest } = props 22 | const btnRef = useRef() 23 | const closeBtnRef = useRef() 24 | const bgColor = useColorModeValue('white', 'gray.900') 25 | 26 | const themes = useMemo(() => { 27 | return [ 28 | { title: 'Theme 1', value: 'theme1', updated: new Date(), id: '1' }, 29 | { title: 'Theme 2', value: 'theme2', updated: new Date(), id: '2' }, 30 | { title: 'Theme 3', value: 'theme3', updated: new Date(), id: '3' }, 31 | ] 32 | }, []) 33 | 34 | return ( 35 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 54 | 57 | 58 | 59 | 60 | 61 | ) 62 | } 63 | 64 | export default ThemeSwitchDrawer 65 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/components/theme-editor/ThemeSwitchDrawerButton.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useRef } from 'react' 2 | import { Icon, Button, useDisclosure, useColorModeValue, ButtonProps } from '@chakra-ui/react' 3 | import { CgSelect } from 'react-icons/cg' 4 | import ThemeSwitchDrawer from './ThemeSwitchDrawer' 5 | 6 | export type ThemeSwitchDrawerButtonProps = {} & ButtonProps 7 | 8 | export const ThemeSwitchDrawerButton: FC = (props) => { 9 | const { children, ...rest } = props 10 | const { isOpen, onOpen, onClose } = useDisclosure() 11 | const btnRef = useRef() 12 | const shadow = useColorModeValue('surface', 'surfaceDark') 13 | const bgColor = useColorModeValue('white', 'gray.900') 14 | 15 | return ( 16 | <> 17 | 28 | 29 | 30 | ) 31 | } 32 | 33 | export default ThemeSwitchDrawerButton 34 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/components/theme-editor/ThemeSwitchDrawerHeader.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react' 2 | import { DrawerHeader, Button, Text, useColorModeValue, Box, Flex } from '@chakra-ui/react' 3 | import { MdClose } from 'react-icons/md' 4 | import { ThemeIcon } from '../base' 5 | 6 | type Props = { 7 | onClose: () => void 8 | } 9 | 10 | export const ThemeSwitchDrawerHeader: FC = ({ onClose }) => { 11 | const bgColor = useColorModeValue('white', 'gray.900') 12 | 13 | return ( 14 | 20 | 21 | Switch theme 22 | 23 | 24 | 25 | Theme 1 26 | 27 | 28 | 29 | 38 | 39 | ) 40 | } 41 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/components/theme-editor/index.ts: -------------------------------------------------------------------------------- 1 | import { ThemeDownloadButton } from './ThemeDownloadButton' 2 | import { ThemeEditorDrawer } from './ThemeEditorDrawer' 3 | import { 4 | ThemeEditorButton, 5 | ThemeEditorButtonProps as ThemeEditorButtonPropsToExport, 6 | } from './ThemeEditorButton' 7 | import { 8 | ThemeEditorDrawerHeader, 9 | ThemeEditorDrawerHeaderProps as ThemeEditorDrawerHeaderPropsToExport, 10 | } from './ThemeEditorDrawerHeader' 11 | import { 12 | ThemeEditorRootPanel, 13 | ThemeEditorRootPanelProps as ThemeEditorRootPanelPropsToExport, 14 | } from './ThemeEditorRootPanel' 15 | import ThemeEditorAccordion from './ThemeEditorAccordion' 16 | import ThemeEditorAccordionItem from './ThemeEditorAccordionItem' 17 | import { ThemeEditor, ThemeEditorProps as ThemeEditorPropsToExport } from './ThemeEditor' 18 | import { 19 | ThemeEditorDrawerFooter, 20 | ThemeEditorDrawerFooterProps as ThemeEditorDrawerFooterPropsToExport, 21 | } from './ThemeEditorDrawerFooter' 22 | 23 | export { 24 | ThemeEditor, 25 | ThemeDownloadButton, 26 | ThemeEditorDrawerHeader, 27 | ThemeEditorDrawerFooter, 28 | ThemeEditorDrawer, 29 | ThemeEditorButton, 30 | ThemeEditorRootPanel, 31 | ThemeEditorAccordion, 32 | ThemeEditorAccordionItem, 33 | } 34 | 35 | export type ThemeEditorProps = ThemeEditorPropsToExport 36 | export type ThemeEditorButtonProps = ThemeEditorButtonPropsToExport 37 | export type ThemeEditorRootPanelProps = ThemeEditorRootPanelPropsToExport 38 | export type ThemeEditorDrawerHeaderProps = ThemeEditorDrawerHeaderPropsToExport 39 | export type ThemeEditorDrawerFooterProps = ThemeEditorDrawerFooterPropsToExport 40 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/constants.ts: -------------------------------------------------------------------------------- 1 | // export const API_ENDPOINT = 'http://localhost:4000' 2 | export const API_ENDPOINT = 'https://api.hyperthe.me' 3 | export const THEME_STATE_LOCAL_STORAGE_KEY = 'hypertheme-edit-state' 4 | export const GOOGLE_FONTS_LOCAL_STORAGE_KEY = 'hypertheme-google-fonts-state' 5 | export const DRAWER_EXPANDED_INDEX_LOCAL_STORAGE_KEY = 'hypertheme-drawer-expanded-index' 6 | export const COLOR_PICKER_TRANSPARENT_SAFE_BG_B64 = [ 7 | 'iVBORw0KGgoAAAANSUhEUgAAAHgAAAB4CAYAAAA5ZDbSAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAFXSURBVHgB7d3BCcJAFEXRiViH4MJ2LNZ2sgikEa0iM3A9p4PhMsvP2/bj/IwFXs/Heyzwb++9DdIEjhM4TuA4geMEjhM4TuA4geMEjhM4TuA4geMEjhM4TuA4geMEjhM4TuA4geMEjhM4TuA4geMEjhM4TuA4geO2sYirxjn84DiB4wSOEzhO4DiB4wSOEzhO4DiB4wSOEzhO4DiB4wSOEzhO4DiB4wSOEzhO4DiB4wSOEzhO4DiB4wSOEzhO4LjNld8ctgu5hMBxAscJHCdwnMBxAscJHCdwnMBxAscJHCdwnMBxAscJHCdwnMBxAscJHCdwnMBxAscJHCdwnMBxAscJHHcfLLEf55RZQduFk9gu5BICxwkcJ3CcwHECxwkcJ3CcwHECxwkcJ3CcwHECxwkcJ3CcwHECxwkcJ3CcwHECxwkcJ3CcwHECxwkcJ3Cc68J5vmOBH2JAIyklQWkKAAAAAElFTkSuQmCC', 8 | 'iVBORw0KGgoAAAANSUhEUgAAAHgAAAB4CAYAAAA5ZDbSAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAFYSURBVHgB7d2xDQJBDADBe0Q9hISUQLGUQEhIQ1AFZ2mZ6cBaObR8rCGX6+2xBrxfz/saMDXvaZEmcJzAcQLHCRwncJzAcQLHCRwncJzAcQLHCRwncJzAcQLHCRwncJzAcQLHCRwncJzAcQLHCRwncJzAcQLHCRx3uPLbY2peGxwncJzAcQLHCRwncJzAcQLHCRwncJzAcQLHCRwncJzAcQLHCRwncJzAcQLHCRwncJzAcQLHCRwncJzAcQLHCRx3rCGuGvewwXECxwkcJ3CcwHECxwkcJ3CcwHECxwkcJ3CcwHECxwkcJ3CcwHECxwkcJ3CcwHECxwkcJ3CcwHECxwkcJ3DcebHbZ23kd+EmfhfyEwLHCRwncJzAcQLHCRwncJzAcQLHCRwncJzAcQLHCRwncJzAcQLHCRwncJzAcQLHCRwncJzAcQLHCRwncNy/XxduvfSb8AX+bh2Xn9jf+AAAAABJRU5ErkJggg==', 9 | ] 10 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/hooks/useAccordionState.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react' 2 | import { ExpandedIndex } from '@chakra-ui/react' 3 | 4 | type UseAccordionStateProps = ( 5 | localStorageKey: string 6 | ) => [ExpandedIndex, (index: ExpandedIndex) => void] 7 | 8 | export const useAccordionState: UseAccordionStateProps = (localStorageKey) => { 9 | const [defaultIndex, setDefaultIndex] = useState(0) 10 | 11 | useEffect(() => { 12 | try { 13 | const savedExpandedIndex = localStorage.getItem(localStorageKey) 14 | 15 | if (!savedExpandedIndex) { 16 | return 17 | } 18 | 19 | const expandedIndex = JSON.parse(savedExpandedIndex) 20 | 21 | if ( 22 | typeof expandedIndex !== 'number' && 23 | !Array.isArray(expandedIndex) && 24 | expandedIndex.some((index: unknown) => typeof index !== 'number') 25 | ) { 26 | throw new Error('ExpandedIndex should be a number or an array of number') 27 | } 28 | 29 | setDefaultIndex(expandedIndex) 30 | } catch { 31 | localStorage.removeItem(localStorageKey) 32 | } 33 | }, []) 34 | 35 | const onChange = (expandedIndex: ExpandedIndex) => { 36 | setDefaultIndex(expandedIndex) 37 | localStorage.setItem(localStorageKey, JSON.stringify(expandedIndex)) 38 | } 39 | 40 | return [defaultIndex, onChange] 41 | } 42 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/index.ts: -------------------------------------------------------------------------------- 1 | import { theme } from '@hypertheme-editor/chakra-ui-theme' 2 | import { themeEditorState, ThemeEditorState, useThemeEditor } from './hooks/useThemeEditor' 3 | import { useAccordionState } from './hooks/useAccordionState' 4 | import { themeColorKeys } from './utils/defaultThemeColorKeys' 5 | 6 | export * from './utils/ElementsMap' 7 | export * from './utils/updateThemeTokens' 8 | export * from './utils/googleFontFamiliesState' 9 | export * from './utils/safeJsonParse' 10 | export * from './components/base' 11 | export * from './components/config' 12 | export * from './components/theme-editor' 13 | export * from './components/theme-editor-provider' 14 | 15 | export { theme, useThemeEditor, themeEditorState, themeColorKeys, useAccordionState } 16 | export type { ThemeEditorState } 17 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/utils/ElementsMap.ts: -------------------------------------------------------------------------------- 1 | export interface ElementsMapEventData { 2 | toRenderElements: Record 3 | } 4 | 5 | export class ElementsMap { 6 | private static instance: ElementsMap 7 | private handlers: { (data: ElementsMapEventData): void }[] = [] 8 | 9 | public cssVarElementsMap: Record = {} 10 | private _toRenderElements: Record = {} 11 | 12 | private constructor() {} 13 | 14 | public static getInstance(): ElementsMap { 15 | if (!ElementsMap.instance) { 16 | ElementsMap.instance = new ElementsMap() 17 | } 18 | 19 | return ElementsMap.instance 20 | } 21 | 22 | public on(handler: (data: ElementsMapEventData) => void): void { 23 | this.handlers.push(handler) 24 | } 25 | public off(handler: (data: ElementsMapEventData) => void): void { 26 | this.handlers = this.handlers.filter((h) => h !== handler) 27 | } 28 | 29 | public trigger(data: ElementsMapEventData) { 30 | this.handlers.slice(0).forEach((h) => h(data)) 31 | } 32 | 33 | public get toRenderElements(): Record { 34 | return this._toRenderElements 35 | } 36 | 37 | public set toRenderElements(value: Record) { 38 | this._toRenderElements = value 39 | this.trigger({ 40 | toRenderElements: value, 41 | }) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/utils/GoogleFontFamiliesStateInitializer.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useEffect, useMemo } from 'react' 2 | import GoogleFontLoader, { Font } from 'react-google-font-loader' 3 | import { useRecoilState } from 'recoil' 4 | import { API_ENDPOINT, GOOGLE_FONTS_LOCAL_STORAGE_KEY } from '../constants' 5 | import { useThemeEditor } from '../hooks/useThemeEditor' 6 | import { GoogleFont, googleFontFamiliesState } from './googleFontFamiliesState' 7 | import { safeJsonParse } from './safeJsonParse' 8 | 9 | const GOOGLE_FONTS_API_ENDPOINT = `${API_ENDPOINT}/google-fonts` 10 | 11 | type Props = { 12 | // 13 | } 14 | 15 | export const GoogleFontFamiliesStateInitializer: FC = () => { 16 | const [googleFontsState, setGoogleFontsState] = useRecoilState(googleFontFamiliesState) 17 | const { theme } = useThemeEditor() 18 | 19 | useEffect(() => { 20 | const init = async () => { 21 | try { 22 | const result = await fetch(GOOGLE_FONTS_API_ENDPOINT) 23 | const googleFonts = await result.json() 24 | const fontFamilies = googleFonts.items.map((item) => item.family) 25 | 26 | const savedValue = localStorage.getItem(GOOGLE_FONTS_LOCAL_STORAGE_KEY) 27 | if (savedValue) { 28 | const parsedValue = safeJsonParse(savedValue) 29 | if (!parsedValue.error) { 30 | setGoogleFontsState({ 31 | ...parsedValue.value, 32 | fontFamilies, 33 | fonts: googleFonts.items, 34 | initialized: true, 35 | toLoadFonts: parsedValue.value.toLoadFonts || [], 36 | toLoadVariants: parsedValue.value.toLoadVariants || {}, 37 | }) 38 | } 39 | return 40 | } 41 | 42 | setGoogleFontsState({ 43 | fontFamilies, 44 | fonts: googleFonts.items, 45 | initialized: true, 46 | toLoadFonts: googleFontsState.toLoadFonts || [], 47 | toLoadVariants: googleFontsState.toLoadVariants || {}, 48 | }) 49 | } catch (error) { 50 | // 51 | } 52 | } 53 | 54 | if (!googleFontsState.initialized) { 55 | init() 56 | } 57 | // eslint-disable-next-line react-hooks/exhaustive-deps 58 | }, []) 59 | 60 | useEffect(() => { 61 | if (!theme || !theme.fonts) { 62 | return 63 | } 64 | 65 | const newToLoadFonts: GoogleFont[] = [] 66 | const fontKeys = Object.keys(theme.fonts) 67 | for (const fontKey of fontKeys) { 68 | const font = theme.fonts[fontKey] 69 | const splittedFontFamily = font.split(',').map((item) => item.trim()) 70 | const firstFontFamilyName = 71 | splittedFontFamily[0].startsWith('"') && splittedFontFamily[0].endsWith('"') 72 | ? splittedFontFamily[0].substring(1, splittedFontFamily[0].length - 1) 73 | : splittedFontFamily[0] 74 | 75 | const googleFontIndex = 76 | !googleFontsState.fontFamilies || 77 | googleFontsState.fontFamilies.length === 0 || 78 | !splittedFontFamily || 79 | splittedFontFamily.length === 0 80 | ? -1 81 | : googleFontsState.fontFamilies.indexOf(firstFontFamilyName) 82 | 83 | if (googleFontIndex > -1 && googleFontsState.fonts[googleFontIndex]) { 84 | newToLoadFonts.push(googleFontsState.fonts[googleFontIndex]) 85 | } 86 | } 87 | 88 | setGoogleFontsState({ 89 | ...googleFontsState, 90 | toLoadFonts: newToLoadFonts, 91 | }) 92 | // eslint-disable-next-line react-hooks/exhaustive-deps 93 | }, [theme?.fonts]) 94 | 95 | const currentGoogleFonts = useMemo(() => { 96 | return googleFontsState.toLoadFonts.map((item, index) => { 97 | return { 98 | font: item.family, 99 | weights: 100 | googleFontsState.toLoadVariants && googleFontsState.toLoadVariants[item.family] 101 | ? googleFontsState.toLoadVariants[item.family] 102 | : item.variants, 103 | } 104 | }) 105 | }, [googleFontsState.toLoadFonts, googleFontsState.toLoadVariants]) 106 | 107 | const currentGoogleFontSubset = useMemo(() => { 108 | return googleFontsState.toLoadFonts.reduce((acc, item): any => { 109 | return [...acc, ...item.subsets] 110 | }, []) 111 | }, [googleFontsState.toLoadFonts]) 112 | 113 | if (!googleFontsState.initialized) { 114 | return null 115 | } 116 | 117 | return 118 | } 119 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/utils/cssBoxShadow.ts: -------------------------------------------------------------------------------- 1 | const VALUES_REG = /,(?![^\(]*\))/ 2 | const PARTS_REG = /\s(?![^(]*\))/ 3 | const LENGTH_REG = /^[0-9]+[a-zA-Z%]+?$/ 4 | 5 | const parseValue = (str) => { 6 | const parts = str.split(PARTS_REG) 7 | const inset = parts.includes('inset') 8 | const last = parts.slice(-1)[0] 9 | const color = !isLength(last) ? last : undefined 10 | 11 | const nums = parts 12 | .filter((n) => n !== 'inset') 13 | .filter((n) => n !== color) 14 | .map(toNum) 15 | const [offsetX, offsetY, blurRadius, spreadRadius] = nums 16 | 17 | return { 18 | inset, 19 | offsetX, 20 | offsetY, 21 | blurRadius, 22 | spreadRadius, 23 | color, 24 | } 25 | } 26 | 27 | const stringifyValue = (obj) => { 28 | const { inset, offsetX = 0, offsetY = 0, blurRadius = 0, spreadRadius, color } = obj || {} 29 | 30 | return [inset ? 'inset' : null, offsetX, offsetY, blurRadius, spreadRadius, color] 31 | .filter((v) => v !== null && v !== undefined) 32 | .map(toPx) 33 | .map((s) => `${s}`.trim()) 34 | .join(' ') 35 | } 36 | 37 | const isLength = (v) => v === '0' || LENGTH_REG.test(v) 38 | const toNum = (v) => { 39 | if (!/px$/.test(v) && v !== '0') return v 40 | const n = parseFloat(v) 41 | return !isNaN(n) ? n : v 42 | } 43 | const toPx = (n) => (typeof n === 'number' && n !== 0 ? `${n}px` : n) 44 | 45 | export const parse = (str) => 46 | str 47 | .split(VALUES_REG) 48 | .map((s) => s.trim()) 49 | .map(parseValue) 50 | 51 | export const stringify = (arr) => arr.map(stringifyValue).join(', ') 52 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/utils/defaultThemeColorKeys.ts: -------------------------------------------------------------------------------- 1 | export const themeColorKeys = [ 2 | 'transparent', 3 | 'current', 4 | 'black', 5 | 'white', 6 | 'whiteAlpha', 7 | 'blackAlpha', 8 | 'gray', 9 | 'red', 10 | 'orange', 11 | 'yellow', 12 | 'green', 13 | 'teal', 14 | 'blue', 15 | 'cyan', 16 | 'purple', 17 | 'pink', 18 | 'linkedin', 19 | 'facebook', 20 | 'messenger', 21 | 'whatsapp', 22 | 'twitter', 23 | 'telegram', 24 | ] 25 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/utils/googleFontFamiliesState.ts: -------------------------------------------------------------------------------- 1 | import { atom, DefaultValue, useRecoilState, useRecoilValue } from 'recoil' 2 | import { GOOGLE_FONTS_LOCAL_STORAGE_KEY } from '../constants' 3 | 4 | export type GoogleFont = { 5 | family: string 6 | variants: string[] 7 | subsets: string[] 8 | version: string 9 | lastModified: string 10 | files: Record 11 | category: string 12 | kind: string 13 | } 14 | 15 | export type GoogleFontsState = { 16 | fonts: GoogleFont[] 17 | fontFamilies: string[] 18 | toLoadFonts: GoogleFont[] 19 | toLoadVariants: Record 20 | initialized: boolean 21 | } 22 | 23 | export const googleFontFamiliesState = atom({ 24 | key: 'googleFontFamiliesState', 25 | default: { 26 | initialized: false, 27 | toLoadFonts: [], 28 | fonts: [], 29 | fontFamilies: [], 30 | toLoadVariants: {}, 31 | }, 32 | effects_UNSTABLE: [ 33 | ({ onSet, setSelf }) => { 34 | onSet((newValue) => { 35 | if (newValue instanceof DefaultValue) { 36 | // 37 | } else if (newValue.initialized) { 38 | localStorage.setItem( 39 | GOOGLE_FONTS_LOCAL_STORAGE_KEY, 40 | JSON.stringify({ 41 | toLoadFonts: newValue.toLoadFonts, 42 | toLoadVariants: newValue.toLoadVariants, 43 | }) 44 | ) 45 | } 46 | }) 47 | }, 48 | ], 49 | }) 50 | 51 | export const useGoogleFontFamiliesState = () => useRecoilState(googleFontFamiliesState) 52 | export const useGoogleFontFamiliesValue = () => useRecoilValue(googleFontFamiliesState) 53 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/utils/isBrowserCompatible.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react' 2 | 3 | function isSafariBrowser() { 4 | const is_chrome = navigator.userAgent.indexOf('Chrome') > -1 5 | const is_safari = navigator.userAgent.indexOf('Safari') > -1 6 | 7 | if (is_safari) { 8 | if (is_chrome) 9 | // Chrome seems to have both Chrome and Safari userAgents 10 | return false 11 | else return true 12 | } 13 | return false 14 | } 15 | 16 | export const isBrowserCompatible = (): boolean => { 17 | // Firefox 1.0+ 18 | // eslint-disable-next-line @typescript-eslint/dot-notation 19 | const isFirefox = typeof window['InstallTrigger'] !== 'undefined' 20 | 21 | // Safari 3.0+ "[object HTMLElementConstructor]" 22 | const isSafari = isSafariBrowser() 23 | 24 | // Internet Explorer 6-11 25 | const isIE = /* @cc_on!@ */ false || !!(document as any).documentMode 26 | 27 | if (isFirefox || isSafari || isIE) { 28 | return false 29 | } 30 | 31 | return true 32 | } 33 | 34 | export const useIsBrowserCompatible = (): boolean => { 35 | const [isCompatible, setIsCompatible] = useState(true) 36 | 37 | useEffect(() => { 38 | setIsCompatible(isBrowserCompatible()) 39 | }, []) 40 | 41 | return isCompatible 42 | } 43 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/utils/isColor.ts: -------------------------------------------------------------------------------- 1 | export const isColor = (strColor: string): boolean => { 2 | const s = new Option().style 3 | s.color = strColor 4 | 5 | // // eslint-disable-next-line eqeqeq 6 | // return s.color == strColor || /^#[0-9A-F]{6}$/i.test(strColor) 7 | return s.color?.length > 0 || false 8 | } 9 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/utils/isMobile.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react' 2 | 3 | export const isMobile = () => { 4 | if (typeof window !== 'undefined' && typeof navigator !== 'undefined') { 5 | return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test( 6 | navigator.userAgent 7 | ) 8 | } 9 | return false 10 | } 11 | 12 | export const useIsMobile = (): boolean => { 13 | const [isMobileState, setIsMobile] = useState(false) 14 | 15 | useEffect(() => { 16 | setIsMobile(isMobile()) 17 | }, []) 18 | 19 | return isMobileState 20 | } 21 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/utils/managedAttributes.ts: -------------------------------------------------------------------------------- 1 | export type ComponentProps = T extends React.ComponentType | React.Component 2 | ? JSX.LibraryManagedAttributes 3 | : never 4 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/utils/safeJsonParse.ts: -------------------------------------------------------------------------------- 1 | export const safeJsonParse = (text) => { 2 | try { 3 | return { 4 | value: JSON.parse(text), 5 | } 6 | } catch (ex) { 7 | return { 8 | error: { 9 | name: (ex as Error).name, 10 | message: (ex as Error).message, 11 | stack: (ex as Error).stack, 12 | }, 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/utils/timeSince.ts: -------------------------------------------------------------------------------- 1 | export const timeSince = (date: Date) => { 2 | const seconds = Math.floor(((new Date() as any) - (date as any)) / 1000) 3 | 4 | let interval = seconds / 31536000 5 | 6 | if (interval > 1) { 7 | return `${Math.floor(interval)} years` 8 | } 9 | interval = seconds / 2592000 10 | if (interval > 1) { 11 | return `${Math.floor(interval)} months` 12 | } 13 | interval = seconds / 86400 14 | if (interval > 1) { 15 | return `${Math.floor(interval)} days` 16 | } 17 | interval = seconds / 3600 18 | if (interval > 1) { 19 | return `${Math.floor(interval)} hours` 20 | } 21 | interval = seconds / 60 22 | if (interval > 1) { 23 | return `${Math.floor(interval)} minutes` 24 | } 25 | return `${Math.floor(seconds)} seconds` 26 | } 27 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/src/utils/updateThemeTokens.ts: -------------------------------------------------------------------------------- 1 | import { Theme } from '@chakra-ui/react' 2 | 3 | // theme.colors 4 | export const setThemeSingleColor = (token: string, value: string) => { 5 | document.documentElement.style.setProperty(`--chakra-colors-${token}`, value) 6 | } 7 | 8 | export const setThemePaletteColor = (token: string, paletteKey: string | number, value: string) => { 9 | document.documentElement.style.setProperty(`--chakra-colors-${token}-${paletteKey}`, value) 10 | } 11 | 12 | export const setThemeColorsOfPalette = ( 13 | token: string, 14 | newPalette: Record 15 | ): void => { 16 | const paletteKeys = Object.keys(newPalette) 17 | for (const paletteKey of paletteKeys) { 18 | setThemePaletteColor(token, paletteKey, newPalette[paletteKey]) 19 | } 20 | } 21 | 22 | export const setThemeColors = (colors: Theme['colors']) => { 23 | const tokens = Object.keys(colors) 24 | for (const token of tokens) { 25 | if (typeof colors[token] === 'string') { 26 | setThemeSingleColor(token, colors[token]) 27 | } else { 28 | setThemeColorsOfPalette(token, colors[token]) 29 | } 30 | } 31 | } 32 | 33 | // theme.fonts 34 | export const setThemeTypographyFont = (section: string, value: string): void => { 35 | document.documentElement.style.setProperty(`--chakra-fonts-${section}`, value) 36 | } 37 | 38 | export const setThemeTypographyFonts = (fonts: Theme['fonts']): void => { 39 | const sections = Object.keys(fonts) 40 | for (const section of sections) { 41 | setThemeTypographyFont(section, fonts[section]) 42 | } 43 | } 44 | 45 | // theme.fontSizes 46 | export const setThemeTypographyFontSize = (size: string, value: string): void => { 47 | document.documentElement.style.setProperty(`--chakra-fontSizes-${size}`, value) 48 | } 49 | 50 | export const setThemeTypograghyFontSizes = (fontSizes: Theme['fontSizes']): void => { 51 | const sizes = Object.keys(fontSizes) 52 | for (const size of sizes) { 53 | setThemeTypographyFontSize(size, fontSizes[size]) 54 | } 55 | } 56 | 57 | // theme.letterSpacings 58 | export const setThemeTypographyLetterSpacing = (size: string, value: string): void => { 59 | document.documentElement.style.setProperty(`--chakra-letterSpacings-${size}`, value) 60 | } 61 | 62 | export const setThemeTypographyLetterSpacings = (letterSpacings: Theme['letterSpacings']): void => { 63 | const sizes = Object.keys(letterSpacings) 64 | for (const size of sizes) { 65 | setThemeTypographyLetterSpacing(size, letterSpacings[size]) 66 | } 67 | } 68 | 69 | // theme.lineHeights 70 | export const setThemeTypographyLineHeight = (size: string, value: string): void => { 71 | document.documentElement.style.setProperty(`--chakra-lineHeights-${size}`, value) 72 | } 73 | 74 | export const setThemeTypographyLineHeights = (lineHeights: Theme['lineHeights']): void => { 75 | const sizes = Object.keys(lineHeights) 76 | for (const size of sizes) { 77 | setThemeTypographyLineHeight(size, lineHeights[size]) 78 | } 79 | } 80 | 81 | // theme.shadows 82 | export const setThemeShadow = (size: string, value: string): void => { 83 | document.documentElement.style.setProperty(`--chakra-shadows-${size}`, value) 84 | } 85 | 86 | export const setThemeShadows = (shadows: Theme['shadows']): void => { 87 | const sizes = Object.keys(shadows) 88 | 89 | for (const size of sizes) { 90 | setThemeShadow(size, shadows[size]) 91 | } 92 | } 93 | 94 | // theme.space 95 | export const setThemeSpacing = (size: string, value: string): void => { 96 | document.documentElement.style.setProperty(`--chakra-space-${size}`, value) 97 | } 98 | 99 | export const setThemeSpacings = (space: Theme['space']): void => { 100 | const sizes = Object.keys(space) 101 | 102 | for (const size of sizes) { 103 | setThemeSpacing(size, space[size]) 104 | } 105 | } 106 | 107 | // theme.radii 108 | export const setThemeBorderRadius = (size: string, value: string): void => { 109 | document.documentElement.style.setProperty(`--chakra-radii-${size}`, value) 110 | } 111 | 112 | export const setThemeBorderRadiuses = (radii: Theme['radii']): void => { 113 | const sizes = Object.keys(radii) 114 | 115 | for (const size of sizes) { 116 | setThemeBorderRadius(size, radii[size]) 117 | } 118 | } 119 | 120 | export const setThemeTokens = (theme: Theme) => { 121 | setThemeColors(theme.colors) 122 | setThemeTypograghyFontSizes(theme.fontSizes) 123 | setThemeTypographyFonts(theme.fonts) 124 | setThemeTypographyLetterSpacings(theme.letterSpacings) 125 | setThemeTypographyLineHeights(theme.lineHeights) 126 | setThemeShadows(theme.shadows) 127 | setThemeBorderRadiuses(theme.radii) 128 | setThemeSpacings(theme.space) 129 | } 130 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/stories/ColorModeToggle.stories.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies */ 2 | import React from 'react' 3 | import { ComponentMeta } from '@storybook/react' 4 | import { Stack } from '@chakra-ui/react' 5 | import { ThemeEditorProvider } from '../src' 6 | import ColorModeToggle from '../src/components/base/ColorModeToggle' 7 | 8 | export default { 9 | title: 'HyperThemeEditor/chakra-ui-core/ColorModeToggle', 10 | component: ColorModeToggle, 11 | argTypes: {}, 12 | } as ComponentMeta 13 | 14 | export const Sizes = (args) => { 15 | return ( 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | ) 24 | } 25 | 26 | export const ShowLabel = (args) => { 27 | return ( 28 | 29 | 30 | 31 | 32 | 33 | ) 34 | } 35 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/stories/CustomEditorPanel.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies */ 2 | import React, { useCallback, useEffect, useState } from 'react' 3 | import { HStack, Input } from '@chakra-ui/react' 4 | import { colord } from 'colord' 5 | import { useDebouncyEffect } from 'use-debouncy' 6 | import { useThemeEditor } from '../src' 7 | import { ThemeColorBox } from './ThemeColorBox' 8 | 9 | export const CustomEditorPanel = () => { 10 | const { theme, setTheme } = useThemeEditor() 11 | const [inputValue, setInputValue] = useState(theme?.colors?.blue[500] || '') 12 | 13 | const handleOnChange = useCallback>((event) => { 14 | setInputValue(event.target.value) 15 | }, []) 16 | 17 | // update this value if another panel change this value 18 | useEffect(() => { 19 | if (theme?.colors && theme.colors.blue[500] !== inputValue) { 20 | setInputValue(theme.colors.blue[500]) 21 | } 22 | // eslint-disable-next-line react-hooks/exhaustive-deps 23 | }, [theme?.colors?.blue[500]]) 24 | 25 | useDebouncyEffect( 26 | () => { 27 | if (colord(inputValue).isValid() && inputValue !== theme?.colors?.blue[500]) { 28 | setTheme({ 29 | ...theme, 30 | colors: { 31 | ...theme?.colors || {}, 32 | blue: { 33 | ...theme?.colors?.blue || {}, 34 | 500: inputValue, 35 | }, 36 | }, 37 | }) 38 | } 39 | }, 40 | 500, 41 | [inputValue] 42 | ) 43 | 44 | return ( 45 | 46 | 47 | 48 | 49 | ) 50 | } 51 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/stories/CustomResetThemeButton.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies */ 2 | import React, { useCallback } from 'react' 3 | import { Button } from '@chakra-ui/react' 4 | import { useRecoilState } from 'recoil' 5 | import { setThemeTokens, themeEditorState } from '../src' 6 | 7 | export const CustomResetThemeButton = () => { 8 | const [themeState, setThemeState] = useRecoilState(themeEditorState) 9 | 10 | const handleClick = useCallback(() => { 11 | // reset the theme 12 | setThemeState({ 13 | ...themeState, 14 | undoable: [], 15 | undone: [], 16 | currentTheme: themeState.initialTheme, 17 | }) 18 | setThemeTokens(themeState.initialTheme as any) 19 | }, [themeState, setThemeState]) 20 | 21 | return 22 | } 23 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/stories/CustomSaveButton.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Button } from '@chakra-ui/react' 3 | import { useThemeEditor } from '../src' 4 | 5 | export const CustomSaveButton = () => { 6 | const { theme } = useThemeEditor() 7 | 8 | const handleSave = () => { 9 | // save the current theme 10 | console.log('saved theme:', theme) 11 | alert(JSON.stringify(theme, null, 2)) 12 | } 13 | 14 | return 15 | } 16 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/stories/CustomUndoRedoButtons.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Button, ButtonGroup } from '@chakra-ui/react' 3 | import { useThemeEditor } from '../src' 4 | 5 | export const CustomUndoRedoButtons = () => { 6 | const { canUndo, undo, canRedo, redo } = useThemeEditor() 7 | 8 | return ( 9 | 10 | 13 | 16 | 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/stories/ThemeColorBox.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useMemo } from 'react' 2 | import { Box, BoxProps } from '@chakra-ui/react' 3 | import { useThemeEditor } from '../src' 4 | 5 | type Props = { 6 | token: string 7 | paletteIndex?: number 8 | } & BoxProps 9 | 10 | export const ThemeColorBox: FC = ({ token, paletteIndex, ...props }) => { 11 | const { theme } = useThemeEditor() 12 | 13 | const color = useMemo(() => { 14 | if (theme?.colors?.[token]) { 15 | if (typeof theme.colors[token] === 'string') { 16 | return theme.colors[token] 17 | } else if (theme?.colors?.[token] && paletteIndex && theme.colors[token][paletteIndex]) { 18 | return theme.colors[token][paletteIndex] 19 | } 20 | } 21 | 22 | return 'gray' 23 | }, [theme, token, paletteIndex]) 24 | 25 | return 26 | } 27 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/stories/ThemeEditor.stories.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies */ 2 | import React, { useCallback, useState } from 'react' 3 | import { ComponentMeta } from '@storybook/react' 4 | import { Button } from '@chakra-ui/react' 5 | import { CgColorPicker, CgEditShadows, CgSpaceBetween } from 'react-icons/cg' 6 | import { ImFontSize } from 'react-icons/im' 7 | import { BiText } from 'react-icons/bi' 8 | import { MdRoundedCorner } from 'react-icons/md' 9 | import ColorModeToggle from '../src/components/base/ColorModeToggle' 10 | import { 11 | ThemeEditorButton, 12 | ThemeEditorProvider, 13 | ThemeEditorRootPanel, 14 | ThemeEditorDrawer, 15 | ThemeEditor, 16 | } from '../src' 17 | 18 | export default { 19 | title: 'HyperThemeEditor/chakra-ui-core/ThemeEditor', 20 | component: ThemeEditor, 21 | argTypes: {}, 22 | disclosureProps: {}, 23 | } as ComponentMeta 24 | 25 | export const FreeVersionWithRootPanel = (args) => ( 26 | 27 | 28 | 29 | 30 | 31 | {/* 32 | 33 | 34 | 35 | 36 | */} 37 | 38 | 39 | 40 | ) 41 | 42 | export const FreeVersionWithoutRootPanel = (args) => ( 43 | 44 | 45 | 46 | 47 | 48 | {/* 49 | */} 50 | 51 | 52 | 53 | ) 54 | 55 | export const ProVersion = (args) => ( 56 | 57 | 60 | 61 | 62 | 63 | 64 | 65 | {/* 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | */} 84 | 85 | 86 | 87 | 88 | ) 89 | 90 | export const ControlledThemeEditor = (args) => { 91 | const [isOpen, setIsOpen] = useState(true) 92 | const onClick = useCallback(() => setIsOpen(!isOpen), [isOpen]) 93 | const onClose = () => setIsOpen(false) 94 | 95 | return ( 96 | 97 | 100 | 101 | 102 | 103 | 104 | 105 | ) 106 | } 107 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/stories/ThemeEditorButton.stories.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies */ 2 | import React from 'react' 3 | import { ComponentMeta } from '@storybook/react' 4 | import { VStack } from '@chakra-ui/react' 5 | import { ThemeEditorButton, ThemeEditorProvider } from '../src' 6 | 7 | export default { 8 | title: 'HyperThemeEditor/chakra-ui-core/ThemeEditorButton', 9 | component: ThemeEditorButton, 10 | argTypes: {}, 11 | } as ComponentMeta 12 | 13 | export const AllSizes = (args) => ( 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | ) 23 | 24 | export const AllSizesWithLabel = (args) => ( 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | ) 34 | 35 | export const LgSize = (args) => ( 36 | 37 | 38 | 39 | 40 | 41 | 42 | ) 43 | 44 | export const MdSize = (args) => ( 45 | 46 | 47 | 48 | 49 | 50 | 51 | ) 52 | 53 | export const SmSize = (args) => ( 54 | 55 | 56 | 57 | 58 | 59 | 60 | ) 61 | 62 | export const XsSize = (args) => ( 63 | 64 | 65 | 66 | 67 | 68 | 69 | ) 70 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/stories/ThemeEditorDrawer.stories.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies */ 2 | import React from 'react' 3 | import { ComponentMeta } from '@storybook/react' 4 | 5 | import { Box } from '@chakra-ui/react' 6 | import { CgColorPicker } from 'react-icons/cg' 7 | import ColorModeToggle from '../src/components/base/ColorModeToggle' 8 | import { 9 | ThemeEditorButton, 10 | ThemeEditorProvider, 11 | ThemeEditorRootPanel, 12 | ThemeEditorDrawer, 13 | ThemeEditor, 14 | } from '../src' 15 | 16 | export default { 17 | title: 'HyperThemeEditor/chakra-ui-core/ThemeEditorDrawer', 18 | component: ThemeEditorDrawer, 19 | argTypes: {}, 20 | } as ComponentMeta 21 | 22 | export const Default = (args) => ( 23 | 24 | 25 | 26 | 27 | {/* */} 28 | 29 | 30 | 31 | ) 32 | 33 | export const DefaultWithRootPanel = (args) => ( 34 | 35 | 36 | 37 | 38 | 39 | {/* */} 40 | 41 | 42 | 43 | 44 | ) 45 | 46 | export const WithAllEditorsWithRootPanel = (args) => ( 47 | 48 | 49 | 50 | 51 | 52 | {/* 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | */} 67 | 68 | 69 | 70 | 71 | ) 72 | 73 | export const WithAllEditorsWithoutRootPanel = (args) => ( 74 | 75 | 76 | 77 | 78 | 79 | {/* 80 | 81 | 82 | 83 | */} 84 | 85 | 86 | 87 | 88 | ) 89 | 90 | export const WithCustomHeader = (args) => ( 91 | 92 | 93 | 94 | 97 | CUSTOM HEADER 98 | 99 | } 100 | > 101 | 102 | {/* */} 103 | 104 | 105 | 106 |
107 | ) 108 | 109 | export const WithCustomFooter = (args) => ( 110 | 111 | 112 | 113 | 116 | CUSTOM FOOTER 117 | 118 | } 119 | > 120 | 121 | {/* */} 122 | 123 | 124 | 125 | 126 | ) 127 | 128 | export const WithCustomPanel = (args) => ( 129 | 130 | 131 | 132 | 133 | 134 | 135 | CUSTOM PANEL 136 | 137 | 138 | 139 | 140 | 141 | ) 142 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/stories/ThemeEditorDrawerFooter.stories.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies */ 2 | import React from 'react' 3 | import { ComponentMeta } from '@storybook/react' 4 | import { Button } from '@chakra-ui/react' 5 | import { 6 | ThemeEditorButton, 7 | ThemeEditorProvider, 8 | ThemeEditorDrawer, 9 | ThemeEditor, 10 | ThemeEditorDrawerFooter, 11 | useThemeEditor, 12 | } from '../src' 13 | 14 | export default { 15 | title: 'HyperThemeEditor/chakra-ui-core/ThemeEditorDrawerFooter', 16 | component: ThemeEditorDrawerFooter, 17 | argTypes: {}, 18 | } as ComponentMeta 19 | 20 | export const DefaultFooter = (args) => ( 21 | 22 | 23 | 24 | } /> 25 | 26 | 27 | ) 28 | 29 | export const WithCustomActionButton = (args) => { 30 | const CustomSaveButton = () => { 31 | const { theme } = useThemeEditor() 32 | 33 | const handleClick = () => { 34 | // eslint-disable-next-line no-alert 35 | alert(JSON.stringify(theme, null, 2)) 36 | } 37 | 38 | return 39 | } 40 | 41 | return ( 42 | 43 | 44 | 45 | } {...args} /> 48 | } 49 | /> 50 | 51 | 52 | ) 53 | } 54 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/stories/ThemeEditorDrawerHeader.stories.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies */ 2 | import React from 'react' 3 | import { ComponentMeta } from '@storybook/react' 4 | import { 5 | ThemeEditorButton, 6 | ThemeEditorProvider, 7 | ThemeEditorDrawer, 8 | ThemeEditor, 9 | ThemeEditorDrawerHeader, 10 | } from '../src' 11 | 12 | export default { 13 | title: 'HyperThemeEditor/chakra-ui-core/ThemeEditorDrawerHeader', 14 | component: ThemeEditorDrawerHeader, 15 | argTypes: {}, 16 | } as ComponentMeta 17 | 18 | export const DefaultHeader = (args) => ( 19 | 20 | 21 | 22 | } /> 23 | 24 | 25 | ) 26 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/stories/ThemeIcon.stories.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies */ 2 | import React from 'react' 3 | import { ComponentMeta } from '@storybook/react' 4 | import { Stack } from '@chakra-ui/react' 5 | import { ThemeEditorProvider, ThemeIcon } from '../src' 6 | import { theme as defaultTheme } from '@hypertheme-editor/chakra-ui-theme' 7 | 8 | export default { 9 | title: 'HyperThemeEditor/chakra-ui-core/ThemeIcon', 10 | component: ThemeIcon, 11 | argTypes: {}, 12 | } as ComponentMeta 13 | 14 | export const Basic = (args) => { 15 | return ( 16 | 17 | 18 | 19 | ) 20 | } 21 | 22 | export const Sizes = (args) => { 23 | return ( 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | ) 33 | } 34 | 35 | export const CustomColors = (args) => { 36 | return ( 37 | 38 | 46 | 47 | ) 48 | } 49 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/stories/ThemeResetPopup.stories.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies */ 2 | import React from 'react' 3 | import { ComponentMeta } from '@storybook/react' 4 | import { Button, useDisclosure } from '@chakra-ui/react' 5 | import { ThemeEditorProvider, ThemeIcon, ThemeResetPopup } from '../src' 6 | 7 | export default { 8 | title: 'HyperThemeEditor/chakra-ui-core/ThemeResetPopup', 9 | component: ThemeResetPopup, 10 | argTypes: {}, 11 | } as ComponentMeta 12 | 13 | export const Basic = (args) => { 14 | const { isOpen, onOpen, onClose } = useDisclosure() 15 | 16 | return ( 17 | 18 | 19 | {/* */} 20 | 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/tests/unit/utils/safeJsonParse.test.ts: -------------------------------------------------------------------------------- 1 | import { safeJsonParse } from '../../../src/utils/safeJsonParse' 2 | 3 | describe('unit: safeJsonParse()', function () { 4 | it('should return a valid object for a valid json input', function () { 5 | var input = '{"a": "b"}' 6 | var output = safeJsonParse(input) 7 | expect(output).toMatchObject({ value: { a: 'b' } }) 8 | }) 9 | it('should return an error field describing the error for an invalid json input', function () { 10 | var input = '{"a": "b}' 11 | var output = safeJsonParse(input) 12 | expect(output.error).toBeDefined() 13 | expect(output.value).toBeUndefined() 14 | expect(output.error?.name).toBe('SyntaxError') 15 | expect(output.error?.message).toBe('Unexpected end of JSON input') 16 | expect(output.error?.stack).toBeDefined() 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /packages/chakra-ui-core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "outDir": "lib", 5 | "lib": [ 6 | "dom", 7 | "dom.iterable", 8 | "esnext" 9 | ], 10 | "declaration": true, 11 | "declarationDir": "lib", 12 | "allowJs": true, 13 | "skipLibCheck": true, 14 | "esModuleInterop": true, 15 | "allowSyntheticDefaultImports": true, 16 | "strict": true, 17 | "forceConsistentCasingInFileNames": true, 18 | "module": "esnext", 19 | "moduleResolution": "node", 20 | "resolveJsonModule": true, 21 | "isolatedModules": true, 22 | "noImplicitAny": false, 23 | "noUnusedParameters": false, 24 | "noUnusedLocals": false, 25 | "noEmit": true, 26 | "jsx": "react" 27 | }, 28 | "include": [ 29 | "src" 30 | ], 31 | "exclude": [ 32 | "node_modules", 33 | "lib", 34 | "stories" 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /packages/chakra-ui-font-sizes/.gitignore: -------------------------------------------------------------------------------- 1 | .env -------------------------------------------------------------------------------- /packages/chakra-ui-font-sizes/README.md: -------------------------------------------------------------------------------- 1 | # @hypertheme-editor/chakra-ui-font-sizes 2 | 3 | Edit [Chakra UI](https://chakra-ui.com/) theme **fontSizes** with [HyperTheme Editor](#hypertheme-editor). 4 | 5 | ![Font Sizes Panel](https://hyperthe.me/images/documentation/font-sizes-screen.png) 6 | 7 | This module is part of **[HyperTheme Editor](#hypertheme-editor)**, check it out before installing and using it. 8 | 9 | ## Import 10 | 11 | ```jsx 12 | import { ThemeEditorFontSizes } from '@hypertheme-editor/chakra-ui-font-sizes' 13 | ``` 14 | 15 | ## Usage 16 | 17 | ThemeEditorFontSizes works with the [`ThemeEditorDrawer` component](https://hyperthe.me/documentation/components/ThemeEditorDrawer). 18 | 19 | Put the `ThemeEditFontSizes` component as a children of `ThemeEditorDrawer` and set `icon` and `title` props like in this example: 20 | 21 | ```jsx live=true 22 | function MyThemeEditor(props) { 23 | return ( 24 | 25 | 26 | 27 | 28 | 29 | 30 | ) 31 | } 32 | ``` 33 | 34 | ## HyperTheme Editor 35 | 36 | ![HyperTheme Editor screen shot](https://www.hyperthe.me/images/social-banner.jpg) 37 | 38 | Powerful visual theme editor for your next Chakra UI project. 39 | 40 | Learn more about it on the website: [`hyperthe.me`](https://hyperthe.me). 41 | 42 | ### Documentation 43 | 44 | Read the HyperTheme Editor [documentation here](https://hyperthe.me/documentation). 45 | 46 | ## License 47 | 48 | HyperTheme Editor is licensed under the [MIT License](https://github.com/Hyperting/hypertheme-editor/blob/main/LICENSE) by [Hyperting S.r.l.](https://hyperting.com). 49 | -------------------------------------------------------------------------------- /packages/chakra-ui-font-sizes/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hypertheme-editor/chakra-ui-font-sizes", 3 | "version": "0.2.2", 4 | "description": "", 5 | "main": "lib/index.js", 6 | "module": "lib/index.esm.js", 7 | "types": "lib/index.d.ts", 8 | "files": [ 9 | "lib" 10 | ], 11 | "scripts": { 12 | "test": "echo \"Error: no test specified\" && exit 1", 13 | "build": "rollup -c", 14 | "start": "rollup -c -w" 15 | }, 16 | "author": "marco", 17 | "license": "ISC", 18 | "peerDependencies": { 19 | "@chakra-ui/react": ">= 1.0.0", 20 | "@hypertheme-editor/chakra-ui-core": "0.2.2", 21 | "framer-motion": ">= 4", 22 | "react": ">=16.8.6", 23 | "react-dom": ">=16.8.6", 24 | "recoil": "^0.5.2", 25 | "immer": "^9.0.6", 26 | "react-icons": "^4.2.0", 27 | "use-debouncy": "^4.2.0" 28 | }, 29 | "dependencies": {}, 30 | "devDependencies": { 31 | "@hypertheme-editor/chakra-ui-core": "0.2.2" 32 | }, 33 | "publishConfig": { 34 | "access": "public", 35 | "registry": "https://registry.npmjs.org/" 36 | } 37 | } -------------------------------------------------------------------------------- /packages/chakra-ui-font-sizes/rollup.config.js: -------------------------------------------------------------------------------- 1 | import peerDepsExternal from 'rollup-plugin-peer-deps-external' 2 | import resolve from '@rollup/plugin-node-resolve' 3 | import commonjs from '@rollup/plugin-commonjs' 4 | import typescript from 'rollup-plugin-typescript2' 5 | 6 | const packageJson = require('./package.json') 7 | 8 | export default { 9 | input: 'src/index.ts', 10 | output: [ 11 | { 12 | file: packageJson.main, 13 | format: 'cjs', 14 | sourcemap: true, 15 | }, 16 | { 17 | file: packageJson.module, 18 | format: 'esm', 19 | sourcemap: true, 20 | }, 21 | ], 22 | plugins: [ 23 | peerDepsExternal(), 24 | resolve(), 25 | commonjs(), 26 | typescript({ 27 | useTsconfigDeclarationDir: true, 28 | }), 29 | ], 30 | } 31 | -------------------------------------------------------------------------------- /packages/chakra-ui-font-sizes/src/ThemeEditorFontSizes.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useCallback } from 'react' 2 | import { Box, Flex, Stack, Divider } from '@chakra-ui/react' 3 | import ThemeEditorFontSizesItem from './ThemeEditorFontSizesItem' 4 | import { ThemeEditorRootPanelProps, useThemeEditor } from '@hypertheme-editor/chakra-ui-core' 5 | 6 | type Props = { 7 | scale?: string[] 8 | } & Partial 9 | 10 | export const ThemeEditorFontSizes: FC = ({ 11 | scale = ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl', '4xl', '5xl', '6xl', '7xl', '8xl', '9xl'], 12 | ...props 13 | }) => { 14 | const { theme, setTheme } = useThemeEditor() 15 | 16 | const handleChangeFontSize = useCallback( 17 | ({ size, value }) => { 18 | if (theme && theme.fontSizes && theme.fontSizes[size] !== value) { 19 | try { 20 | const newTheme = { 21 | ...theme, 22 | fontSizes: { 23 | ...theme.fontSizes, 24 | [size]: value, 25 | }, 26 | } as any 27 | setTheme(newTheme) 28 | } catch (error) { 29 | // 30 | } 31 | } 32 | }, 33 | [setTheme, theme] 34 | ) 35 | 36 | return ( 37 | 38 | 39 | } 46 | > 47 | {scale.map((size) => ( 48 | 54 | ))} 55 | 56 | 57 | 58 | ) 59 | } 60 | -------------------------------------------------------------------------------- /packages/chakra-ui-font-sizes/src/ThemeEditorFontSizesItem.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useCallback, useMemo, useState, useEffect } from 'react' 2 | import { 3 | Box, 4 | Flex, 5 | Tag, 6 | Text, 7 | Slider, 8 | SliderTrack, 9 | SliderThumb, 10 | SliderFilledTrack, 11 | Select, 12 | Input, 13 | } from '@chakra-ui/react' 14 | import { useDebouncyEffect } from 'use-debouncy' 15 | import { ElementsHighlighter } from '@hypertheme-editor/chakra-ui-core' 16 | 17 | export type ThemeEditorFontSizesItemProps = { 18 | size: string 19 | sampleTitle?: string 20 | value: string 21 | onChange: (value: { size: string; value: string }) => void 22 | } 23 | 24 | const ThemeEditorFontSizesItem: FC = (props) => { 25 | const { sampleTitle = 'Lorem ipsum dolor sit', size, value, onChange } = props 26 | 27 | const [currentValue, setCurrentValue] = useState(value) 28 | 29 | const cssValue = useMemo(() => { 30 | return window && currentValue 31 | ? (window as any).CSSStyleValue.parse('font-size', currentValue) 32 | : undefined 33 | }, [currentValue]) 34 | 35 | const handleSliderChange = useCallback( 36 | (newValue: number) => { 37 | setCurrentValue(`${newValue}${cssValue.unit}`) 38 | }, 39 | [cssValue.unit] 40 | ) 41 | 42 | const handleInputChange = useCallback>( 43 | (event) => { 44 | setCurrentValue(`${event.target.value}${cssValue.unit}`) 45 | }, 46 | [cssValue.unit] 47 | ) 48 | 49 | const handleUnitChange = useCallback>( 50 | (event) => { 51 | if (cssValue.unit === 'px' && (event.target.value === 'em' || event.target.value === 'rem')) { 52 | setCurrentValue(`${cssValue.value / 16}${event.target.value}`) 53 | } else if ( 54 | (cssValue.unit === 'em' || cssValue.unit === 'rem') && 55 | event.target.value === 'px' 56 | ) { 57 | setCurrentValue(`${(cssValue.value * 16).toFixed(0)}${event.target.value}`) 58 | } else { 59 | setCurrentValue(`${cssValue.value}${event.target.value}`) 60 | } 61 | }, 62 | [cssValue.unit, cssValue.value] 63 | ) 64 | 65 | useDebouncyEffect(() => onChange({ size, value: currentValue }), 500, [currentValue]) 66 | 67 | useEffect(() => { 68 | setCurrentValue(value) 69 | }, [value]) 70 | 71 | return ( 72 | 73 | 74 | 75 | {size} 76 | 77 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 102 | 115 | 116 | 117 | 124 | {sampleTitle} 125 | 126 | 127 | ) 128 | } 129 | 130 | export default ThemeEditorFontSizesItem 131 | -------------------------------------------------------------------------------- /packages/chakra-ui-font-sizes/src/index.ts: -------------------------------------------------------------------------------- 1 | import { ThemeEditorFontSizes } from "./ThemeEditorFontSizes" 2 | import ThemeEditorFontSizesItem from "./ThemeEditorFontSizesItem" 3 | 4 | export { 5 | ThemeEditorFontSizes, 6 | ThemeEditorFontSizesItem, 7 | } 8 | -------------------------------------------------------------------------------- /packages/chakra-ui-font-sizes/stories/ThemeEditorFontSizes.stories.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies */ 2 | import React from 'react' 3 | import { ComponentMeta } from '@storybook/react' 4 | import { Button } from '@chakra-ui/react' 5 | import { CgColorPicker } from 'react-icons/cg' 6 | import { 7 | ThemeEditorButton, 8 | ThemeEditorProvider, 9 | ThemeEditorRootPanel, 10 | ThemeEditorDrawer, 11 | ThemeEditor, 12 | } from '@hypertheme-editor/chakra-ui-core' 13 | import { ThemeEditorFontSizes } from '../src' 14 | 15 | export default { 16 | title: 'HyperThemeEditor/chakra-ui-font-sizes/Font Sizes', 17 | component: ThemeEditor, 18 | argTypes: {}, 19 | } as ComponentMeta 20 | 21 | export const ThemeEditorColorsWithRootPanel = (args) => ( 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | ) 34 | 35 | export const ThemeEditorColorsWithoutRootPanel = (args) => ( 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | ) 46 | -------------------------------------------------------------------------------- /packages/chakra-ui-font-sizes/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "outDir": "lib", 5 | "lib": [ 6 | "dom", 7 | "dom.iterable", 8 | "esnext" 9 | ], 10 | "declaration": true, 11 | "declarationDir": "lib", 12 | "allowJs": true, 13 | "skipLibCheck": true, 14 | "esModuleInterop": true, 15 | "allowSyntheticDefaultImports": true, 16 | "strict": true, 17 | "forceConsistentCasingInFileNames": true, 18 | "module": "esnext", 19 | "moduleResolution": "node", 20 | "resolveJsonModule": true, 21 | "isolatedModules": true, 22 | "noImplicitAny": false, 23 | "noUnusedParameters": false, 24 | "noUnusedLocals": false, 25 | "noEmit": true, 26 | "jsx": "react" 27 | }, 28 | "include": [ 29 | "src" 30 | ], 31 | "exclude": [ 32 | "node_modules", 33 | "lib", 34 | "stories" 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /packages/chakra-ui-theme/.gitignore: -------------------------------------------------------------------------------- 1 | .env -------------------------------------------------------------------------------- /packages/chakra-ui-theme/README.md: -------------------------------------------------------------------------------- 1 | # @hypertheme-editor/chakra-ui-theme 2 | 3 | [Chakra UI Theme](https://chakra-ui.com/) used by [HyperTheme Editor](https://hyperthe.me). 4 | 5 | This module does contains only the theme files and it's used by the [@hypertheme-editor/chakra-ui-core](https://www.npmjs.com/package/@hypertheme-editor/chakra-ui-core) package. 6 | 7 | 8 | ## HyperTheme Editor 9 | 10 | ![HyperTheme Editor screen shot](https://www.hyperthe.me/images/social-banner.jpg) 11 | 12 | Powerful visual theme editor for your next Chakra UI project. 13 | 14 | Learn more about it on the website: [`hyperthe.me`](https://hyperthe.me). 15 | 16 | ### Documentation 17 | 18 | Read the HyperTheme Editor [documentation here](https://hyperthe.me/documentation). 19 | 20 | ## License 21 | 22 | HyperTheme Editor is licensed under the [MIT License](https://github.com/Hyperting/hypertheme-editor/blob/main/LICENSE) by [Hyperting S.r.l.](https://hyperting.com). 23 | -------------------------------------------------------------------------------- /packages/chakra-ui-theme/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hypertheme-editor/chakra-ui-theme", 3 | "version": "0.2.2", 4 | "description": "", 5 | "main": "lib/index.js", 6 | "module": "lib/index.esm.js", 7 | "types": "lib/index.d.ts", 8 | "files": [ 9 | "lib" 10 | ], 11 | "scripts": { 12 | "test": "echo \"Error: no test specified\" && exit 1", 13 | "build": "rollup -c", 14 | "start": "rollup -c -w" 15 | }, 16 | "author": "marco", 17 | "license": "ISC", 18 | "peerDependencies": { 19 | "@chakra-ui/react": ">= 1.0.0", 20 | "framer-motion": ">= 4", 21 | "react": ">=16.8.6", 22 | "react-dom": ">=16.8.6" 23 | }, 24 | "dependencies": {}, 25 | "publishConfig": { 26 | "access": "public", 27 | "registry": "https://registry.npmjs.org/" 28 | } 29 | } -------------------------------------------------------------------------------- /packages/chakra-ui-theme/rollup.config.js: -------------------------------------------------------------------------------- 1 | import peerDepsExternal from 'rollup-plugin-peer-deps-external' 2 | import resolve from '@rollup/plugin-node-resolve' 3 | import commonjs from '@rollup/plugin-commonjs' 4 | import typescript from 'rollup-plugin-typescript2' 5 | 6 | const packageJson = require('./package.json') 7 | 8 | export default { 9 | input: 'src/index.ts', 10 | output: [ 11 | { 12 | file: packageJson.main, 13 | format: 'cjs', 14 | sourcemap: true, 15 | }, 16 | { 17 | file: packageJson.module, 18 | format: 'esm', 19 | sourcemap: true, 20 | }, 21 | ], 22 | plugins: [ 23 | peerDepsExternal(), 24 | resolve(), 25 | commonjs(), 26 | typescript({ 27 | useTsconfigDeclarationDir: true, 28 | }), 29 | ], 30 | } 31 | -------------------------------------------------------------------------------- /packages/chakra-ui-theme/src/ThemeProvider.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | ChakraProvider, 3 | ColorModeScript, 4 | extendTheme, 5 | Theme, 6 | } from '@chakra-ui/react' 7 | import React, { FC } from 'react' 8 | 9 | type ThemeProviderProps = { 10 | theme: Theme 11 | } 12 | 13 | const ThemeProvider: FC = (props) => { 14 | const { theme, children } = props 15 | 16 | const customTheme = extendTheme({ 17 | ...theme, 18 | config: { ...theme.config, cssVarPrefix: 'hyper-theme' }, 19 | }) 20 | 21 | return ( 22 |
23 | 24 | {children} 25 |
26 | ) 27 | } 28 | 29 | export default ThemeProvider 30 | -------------------------------------------------------------------------------- /packages/chakra-ui-theme/src/foundations/colors.ts: -------------------------------------------------------------------------------- 1 | import { Colors } from '@chakra-ui/react' 2 | 3 | const colors: Colors = { 4 | primary: { 5 | 50: '#ffffff', 6 | 100: '#d1d2fa', 7 | 200: '#babbf8', 8 | 300: '#a3a4f6', 9 | 400: '#8b8df4', 10 | 500: '#5D5FEF', 11 | 600: '#2f31ea', 12 | 700: '#171ae8', 13 | 800: '#1517d1', 14 | 900: '#1012a3', 15 | }, 16 | secondary: { 17 | 50: '#EAFDED', 18 | 100: '#DCFBE7', 19 | 200: '#BBF8D6', 20 | 300: '#94ECC4', 21 | 400: '#75D9B5', 22 | 500: '#4AC1A2', 23 | 600: '#36A593', 24 | 700: '#258A84', 25 | 800: '#176D6F', 26 | 900: '#0E525C', 27 | }, 28 | purple: { 29 | 50: '#FBE5FF', 30 | 100: '#F3D4FF', 31 | 200: '#D6BEFB', 32 | 300: '#BB9CF5', 33 | 400: '#A280EB', 34 | 500: '#7F58DE', 35 | 600: '#6140BE', 36 | 700: '#472C9F', 37 | 800: '#301C80', 38 | 900: '#20106A', 39 | }, 40 | green: { 41 | 50: '#EAFDED', 42 | 100: '#DCFBE7', 43 | 200: '#BBF8D6', 44 | 300: '#94ECC4', 45 | 400: '#75D9B5', 46 | 500: '#4AC1A2', 47 | 600: '#36A593', 48 | 700: '#258A84', 49 | 800: '#176D6F', 50 | 900: '#0E525C', 51 | }, 52 | gray: { 53 | 50: '#F7FAFC', 54 | 100: '#EDF2F7', 55 | 200: '#E2E8F0', 56 | 300: '#CBD5E0', 57 | 400: '#A0AEC0', 58 | 500: '#718096', 59 | 600: '#4A5568', 60 | 700: '#252b35', 61 | 800: '#1A202C', 62 | 900: '#171923', 63 | }, 64 | } 65 | 66 | export default colors 67 | -------------------------------------------------------------------------------- /packages/chakra-ui-theme/src/foundations/config.ts: -------------------------------------------------------------------------------- 1 | const config = { 2 | useSystemColorMode: false, 3 | } 4 | 5 | export default config 6 | -------------------------------------------------------------------------------- /packages/chakra-ui-theme/src/foundations/fonts.ts: -------------------------------------------------------------------------------- 1 | const fonts = { 2 | body: 'Sora, -apple-system, system-ui, sans-serif', 3 | // body: 'Zen Tokyo Zoo', 4 | heading: 5 | 'Sora, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"', 6 | } 7 | 8 | export default fonts 9 | -------------------------------------------------------------------------------- /packages/chakra-ui-theme/src/foundations/radii.ts: -------------------------------------------------------------------------------- 1 | const radii = { 2 | none: '0', 3 | sm: '0.125rem', 4 | base: '0.75rem', 5 | md: '0.5rem', 6 | lg: '0.75rem', 7 | xl: '1.125rem', 8 | '2xl': '2.5rem', 9 | '3xl': '3.5rem', 10 | full: '9999px', 11 | } 12 | 13 | export default radii 14 | -------------------------------------------------------------------------------- /packages/chakra-ui-theme/src/foundations/shadows.ts: -------------------------------------------------------------------------------- 1 | const shadows = { 2 | surface: '0 0 0 1px rgba(63,63,68,0.05), 0 1px 35px 0 rgba(63,63,68,0.05)', 3 | surfaceDark: '0 0 0 1px rgba(195,195,195,0.045), 0 1px 35px 0 rgba(0,0,0,0.1)', 4 | sm: '0 1px 2px 0 rgba(0, 0, 0, 0.05)', 5 | outline: '0 0 0 3px rgba(125, 125, 125, 0.3)', 6 | xs: '0 0 0 1px rgba(0, 0, 0, 0.05)', 7 | base: '0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)', 8 | md: '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)', 9 | lg: '0 6px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)', 10 | xl: '0 8px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)', 11 | '2xl': '0 15px 50px -12px rgba(0, 0, 0, 0.25)', 12 | inner: 'inset 0 2px 4px 0 rgba(0,0,0,0.06)', 13 | none: 'none', 14 | } 15 | 16 | export default shadows 17 | -------------------------------------------------------------------------------- /packages/chakra-ui-theme/src/foundations/sizes.ts: -------------------------------------------------------------------------------- 1 | const sizes = { 2 | '3xs': '14rem', 3 | '2xs': '16rem', 4 | xs: '20rem', 5 | sm: '24rem', 6 | md: '28rem', 7 | lg: '32rem', 8 | xl: '36rem', 9 | '2xl': '42rem', 10 | '3xl': '48rem', 11 | '4xl': '56rem', 12 | '5xl': '64rem', 13 | '6xl': '72rem', 14 | '7xl': '80rem', 15 | '8xl': '90rem', 16 | container: { 17 | sm: '640px', 18 | md: '768px', 19 | lg: '1024px', 20 | xl: '1280px', 21 | }, 22 | } 23 | 24 | export default sizes 25 | -------------------------------------------------------------------------------- /packages/chakra-ui-theme/src/index.ts: -------------------------------------------------------------------------------- 1 | import { extendTheme, ThemeOverride } from '@chakra-ui/react' 2 | import components from './components' 3 | import config from './foundations/config' 4 | import fonts from './foundations/fonts' 5 | import colors from './foundations/colors' 6 | import styles from './styles' 7 | import sizes from './foundations/sizes' 8 | import shadows from './foundations/shadows' 9 | import radii from './foundations/radii' 10 | 11 | const customTheme: ThemeOverride = { 12 | config, 13 | styles, 14 | fonts, 15 | colors, 16 | sizes, 17 | shadows, 18 | radii, 19 | components, 20 | lineHeights: { 21 | 3: '.75rem', 22 | 4: '1rem', 23 | 5: '1.25rem', 24 | 6: '1.5rem', 25 | 7: '1.75rem', 26 | 8: '2rem', 27 | 9: '2.25rem', 28 | 10: '2.5rem', 29 | normal: 'normal', 30 | none: '1', 31 | shorter: '1.25', 32 | short: '1.375', 33 | base: '1.5', 34 | tall: '1.625', 35 | taller: '2', 36 | }, 37 | } 38 | 39 | export const theme = extendTheme({ 40 | ...customTheme, 41 | config: { 42 | ...customTheme.config, 43 | cssVarPrefix: 'hypertheme', 44 | }, 45 | }) 46 | -------------------------------------------------------------------------------- /packages/chakra-ui-theme/src/styles.ts: -------------------------------------------------------------------------------- 1 | import { mode, Styles } from '@chakra-ui/theme-tools' 2 | 3 | const styles: Styles = { 4 | global: (props) => ({ 5 | body: { 6 | // fontFamily: 'Sora', 7 | color: mode('gray.700', 'whiteAlpha.900')(props), 8 | overflowX: 'hidden', 9 | }, 10 | }), 11 | } 12 | export default styles 13 | -------------------------------------------------------------------------------- /packages/chakra-ui-theme/src/utils/colors.ts: -------------------------------------------------------------------------------- 1 | import { mode, transparentize } from '@chakra-ui/theme-tools' 2 | 3 | type Dict = Record 4 | 5 | export const variantSolid = (props: Dict) => { 6 | const { colorScheme: c } = props 7 | 8 | if (c === 'gray') { 9 | const bg = mode('gray.100', 'whiteAlpha.200')(props) 10 | 11 | return { 12 | bg, 13 | _hover: { 14 | bg: mode('gray.200', 'whiteAlpha.300')(props), 15 | _disabled: { 16 | bg, 17 | }, 18 | }, 19 | _active: { bg: mode('gray.300', 'whiteAlpha.400')(props) }, 20 | } 21 | } 22 | 23 | const { bg = `${c}.500`, color = 'white', hoverBg = `${c}.600`, activeBg = `${c}.700` } = {} 24 | 25 | const background = mode(bg, `${c}.400`)(props) 26 | 27 | return { 28 | bg: background, 29 | color: mode(color, 'gray.800')(props), 30 | _hover: { 31 | bg: mode(hoverBg, `${c}.300`)(props), 32 | _disabled: { 33 | bg: background, 34 | }, 35 | }, 36 | _active: { bg: mode(activeBg, `${c}.400`)(props) }, 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/chakra-ui-theme/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "outDir": "lib", 5 | "lib": [ 6 | "dom", 7 | "dom.iterable", 8 | "esnext" 9 | ], 10 | "declaration": true, 11 | "declarationDir": "lib", 12 | "allowJs": true, 13 | "skipLibCheck": true, 14 | "esModuleInterop": true, 15 | "allowSyntheticDefaultImports": true, 16 | "strict": true, 17 | "forceConsistentCasingInFileNames": true, 18 | "module": "esnext", 19 | "moduleResolution": "node", 20 | "resolveJsonModule": true, 21 | "isolatedModules": true, 22 | "noImplicitAny": false, 23 | "noUnusedParameters": false, 24 | "noUnusedLocals": false, 25 | "noEmit": true, 26 | "jsx": "react" 27 | }, 28 | "include": [ 29 | "src" 30 | ], 31 | "exclude": [ 32 | "node_modules", 33 | "lib", 34 | "stories" 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /packages/hypertheme-chakra-ui/.gitignore: -------------------------------------------------------------------------------- 1 | .env -------------------------------------------------------------------------------- /packages/hypertheme-chakra-ui/README.md: -------------------------------------------------------------------------------- 1 | # HyperTheme Editor 2 | 3 | ![HyperTheme Editor screen shot](https://www.hyperthe.me/images/social-banner.jpg) 4 | 5 | Powerful visual theme editor for your next Chakra UI project. 6 | 7 | ## Features 8 | 9 | - Chakra-UI Theme Foundation Color Editor 10 | - Chakra-UI Theme Foundation Font Sizes Editor 11 | - Undo/Redo 12 | - Customizable Editor Drawer 13 | - Custom Panel Editors 14 | - Unlimited exports 15 | 16 | 17 | ## PRO Version 18 | 19 | HyperTheme Editor has also a PRO version with more features: 20 | - Font Family Editor from Google Fonts 21 | - Line Heights Editor 22 | - Letter Spacing Editor 23 | - Shadows Editor 24 | - Radii Editor 25 | - Spacing Editor 26 | 27 | Visit [`hyperthe.me`](https://hyperthe.me) for more info. 28 | 29 | ## Documentation 30 | 31 | Documentation and guides can be found [here](https://hyperthe.me/documentation). 32 | 33 | ## Getting Started 34 | ### 1. Installation 35 | 36 | Install with NPM: 37 | 38 | ```bash 39 | npm i @hypertheme-editor/chakra-ui 40 | ``` 41 | 42 | or with Yarn: 43 | 44 | ```bash 45 | yarn add @hypertheme-editor/chakra-ui 46 | ``` 47 | 48 | ### 2. Setup 49 | 50 | Installation is super easy and fast: 51 | 52 | - Add the component `` just below the `` component. 53 | - Add the component ``. 54 | 55 | Here's an example: 56 | 57 | ```jsx 58 | import * as React from 'react' 59 | import { ChakraProvider } from '@chakra-ui/react' 60 | import { ThemeEditorProvider, HyperThemeEditor } from '@hypertheme-editor/chakra-ui' 61 | import theme from './my-theme' 62 | 63 | function App() { 64 | return ( 65 | 66 | 67 | 68 | 69 | 70 | ) 71 | } 72 | ``` 73 | 74 | ### 3. Next Steps 75 | 76 | Congratulations! You have a working **theme editor** on your application. 77 | 78 | HyperTheme Editor comes also with all the building blocks necessary to create custom theme editor that works with Chakra UI. 79 | 80 | To learn more read the [documentation](https://hyperthe.me/documentation). 81 | 82 | ## Contributing 83 | 84 | If you want to contribute to HyperTheme Editor, make sure to read the [contribution guide](CONTRIBUTING.md) first. 85 | 86 | ## License 87 | 88 | HyperTheme Editor is licensed under the [MIT License](https://github.com/Hyperting/hypertheme-editor/blob/main/LICENSE) by [Hyperting S.r.l.](https://hyperting.com). 89 | -------------------------------------------------------------------------------- /packages/hypertheme-chakra-ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hypertheme-editor/chakra-ui", 3 | "version": "0.2.2", 4 | "description": "", 5 | "main": "lib/index.js", 6 | "module": "lib/index.esm.js", 7 | "types": "lib/index.d.ts", 8 | "files": [ 9 | "lib" 10 | ], 11 | "scripts": { 12 | "test": "echo \"Error: no test specified\" && exit 1", 13 | "build": "rollup -c", 14 | "start": "rollup -c -w" 15 | }, 16 | "author": "marco", 17 | "license": "ISC", 18 | "peerDependencies": { 19 | "@chakra-ui/react": ">= 1.0.0", 20 | "framer-motion": ">= 4", 21 | "react": ">=16.8.6", 22 | "react-dom": ">=16.8.6" 23 | }, 24 | "devDependencies": { 25 | "@babel/core": "^7.14.6", 26 | "@chakra-ui/react": "^1.6.7", 27 | "@emotion/react": "^11.4.0", 28 | "@emotion/styled": "^11.3.0", 29 | "@rollup/plugin-commonjs": "^19.0.0", 30 | "@rollup/plugin-node-resolve": "^13.0.0", 31 | "@types/react": "^17.0.14", 32 | "babel-eslint": "10.1.0", 33 | "babel-loader": "^8.2.2", 34 | "framer-motion": "^4.1.17", 35 | "react": "^17.0.2", 36 | "react-dom": "^17.0.2", 37 | "rollup": "^2.53.0", 38 | "rollup-plugin-peer-deps-external": "^2.2.4", 39 | "rollup-plugin-typescript2": "^0.31.0", 40 | "typescript": "^4.3.5" 41 | }, 42 | "dependencies": { 43 | "@hypertheme-editor/chakra-ui-colors": "^0.2.2", 44 | "@hypertheme-editor/chakra-ui-core": "^0.2.2", 45 | "@hypertheme-editor/chakra-ui-font-sizes": "^0.2.2", 46 | "immer": "^9.0.6", 47 | "react-icons": "^4.2.0", 48 | "use-debouncy": "^4.2.0" 49 | }, 50 | "publishConfig": { 51 | "access": "public", 52 | "registry": "https://registry.npmjs.org/" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /packages/hypertheme-chakra-ui/rollup.config.js: -------------------------------------------------------------------------------- 1 | import peerDepsExternal from 'rollup-plugin-peer-deps-external' 2 | import resolve from '@rollup/plugin-node-resolve' 3 | import commonjs from '@rollup/plugin-commonjs' 4 | import typescript from 'rollup-plugin-typescript2' 5 | 6 | const packageJson = require('./package.json') 7 | 8 | export default { 9 | input: 'src/index.ts', 10 | output: [ 11 | { 12 | file: packageJson.main, 13 | format: 'cjs', 14 | sourcemap: true, 15 | }, 16 | { 17 | file: packageJson.module, 18 | format: 'esm', 19 | sourcemap: true, 20 | }, 21 | ], 22 | onwarn: function (warning) { 23 | // Skip certain warnings 24 | 25 | // should intercept ... but doesn't in some rollup versions 26 | if (warning.code === 'THIS_IS_UNDEFINED') { 27 | return 28 | } 29 | 30 | // console.warn everything else 31 | console.warn(warning.message) 32 | }, 33 | plugins: [ 34 | peerDepsExternal(), 35 | resolve(), 36 | commonjs(), 37 | typescript({ 38 | useTsconfigDeclarationDir: true, 39 | }), 40 | ], 41 | } 42 | -------------------------------------------------------------------------------- /packages/hypertheme-chakra-ui/src/HyperThemeEditor.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react' 2 | import { 3 | ThemeEditor, 4 | ThemeEditorButton, 5 | ThemeEditorButtonProps, 6 | ThemeEditorDrawer, 7 | } from '@hypertheme-editor/chakra-ui-core' 8 | import { ThemeEditorColors } from '@hypertheme-editor/chakra-ui-colors' 9 | import { ThemeEditorFontSizes } from '@hypertheme-editor/chakra-ui-font-sizes' 10 | import { CgColorPicker } from 'react-icons/cg' 11 | import { ImFontSize } from 'react-icons/im' 12 | 13 | export type HyperThemeEditorProps = ThemeEditorButtonProps 14 | 15 | export const HyperThemeEditor: FC = (props) => { 16 | return ( 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | ) 25 | } 26 | -------------------------------------------------------------------------------- /packages/hypertheme-chakra-ui/src/index.ts: -------------------------------------------------------------------------------- 1 | import { HyperThemeEditor, HyperThemeEditorProps } from './HyperThemeEditor' 2 | 3 | export * from '@hypertheme-editor/chakra-ui-core' 4 | export * from '@hypertheme-editor/chakra-ui-colors' 5 | export * from '@hypertheme-editor/chakra-ui-font-sizes' 6 | 7 | export { HyperThemeEditor } 8 | 9 | export type { HyperThemeEditorProps as HyperThemeEditorProps } 10 | -------------------------------------------------------------------------------- /packages/hypertheme-chakra-ui/stories/Test.stories.tsx: -------------------------------------------------------------------------------- 1 | // import React from 'react' 2 | // import { ComponentStory, ComponentMeta } from '@storybook/react' 3 | // import { 4 | // defaultTheme, 5 | // ThemeEditorDrawerButton, 6 | // ThemeEditorProvider, 7 | // ThemeEditorRootPanel, 8 | // ThemeEditorColors, 9 | // ThemeEditorFontSizes, 10 | // ThemeEditorTypography, 11 | // ThemeEditorRadii, 12 | // ThemeEditorSpacing, 13 | // ThemeEditorShadows, 14 | // ThemeEditorConfig, 15 | // } from '../src' 16 | 17 | // import { Button, useColorMode } from '@chakra-ui/react' 18 | // import ColorModeToggle from '../src/components/base/ColorModeToggle' 19 | // import { CgColorPicker, CgEditShadows, CgSpaceBetween } from 'react-icons/cg' 20 | // import { ImFontSize } from 'react-icons/im' 21 | // import { BiText } from 'react-icons/bi' 22 | // import { MdRoundedCorner } from 'react-icons/md' 23 | // import { FaCog, FaSlidersH } from 'react-icons/fa' 24 | 25 | // export default { 26 | // title: 'HyperThemeEditor/chakra-ui-core/ThemeEditorDrawerButtonTest', 27 | // component: ThemeEditorDrawerButton, 28 | // argTypes: {}, 29 | // } as ComponentMeta 30 | 31 | // // const Template: ComponentStory = (args) => { 32 | // // const { colorMode } = useColorMode() 33 | 34 | // // // const customTheme = extendTheme({ 35 | // // // ...defaultTheme, 36 | // // // config: { 37 | // // // ...defaultTheme.config, 38 | // // // cssVarPrefix: 'hypertheme', 39 | // // // // initialColorMode: colorMode, 40 | // // // }, 41 | // // // }) 42 | // // return ( 43 | // // 44 | // // 45 | // // 46 | // // 47 | // // 48 | // // 49 | 50 | // // 51 | // // 52 | // // 53 | // // 54 | // // 55 | // // 56 | // // ) 57 | // // } 58 | 59 | // export const FreeVersion = (args) => ( 60 | // 61 | // 62 | // 63 | // 64 | // 65 | // 66 | // 67 | // 68 | // 69 | // 70 | // 71 | // ) 72 | 73 | // export const ProVersion = (args) => ( 74 | // 75 | // 78 | // 79 | // 80 | // 81 | // 82 | 83 | // 84 | // 85 | // 86 | 87 | // 88 | // 89 | // 90 | 91 | // 92 | // 93 | // 94 | 95 | // 96 | // 97 | // 98 | // 99 | // 100 | // 101 | // 102 | // 103 | 104 | // 105 | // 106 | // 107 | 108 | // 109 | // 110 | // 111 | 112 | // 113 | // 114 | // 115 | 116 | // 117 | // 118 | // 119 | 120 | // 121 | // 122 | // 123 | // 124 | // 125 | // 126 | // ) 127 | -------------------------------------------------------------------------------- /packages/hypertheme-chakra-ui/stories/ThemeEditor.stories.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies */ 2 | import React from 'react' 3 | import { ComponentMeta } from '@storybook/react' 4 | import { Button } from '@chakra-ui/react' 5 | import { CgColorPicker, CgEditShadows, CgSpaceBetween } from 'react-icons/cg' 6 | import { ImFontSize } from 'react-icons/im' 7 | import { BiText } from 'react-icons/bi' 8 | import { MdRoundedCorner } from 'react-icons/md' 9 | import // ThemeEditorButton, 10 | // ThemeEditorProvider, 11 | // ThemeEditorRootPanel, 12 | // ThemeEditorDrawer, 13 | // ThemeEditor, 14 | '@hypertheme-editor/chakra-ui-core' 15 | import { ThemeEditorColors } from '@hypertheme-editor/chakra-ui-colors' 16 | import { ThemeEditorFontSizes } from '@hypertheme-editor/chakra-ui-font-sizes' 17 | import { 18 | HyperThemeEditor, 19 | ThemeEditorButton, 20 | ThemeEditorProvider, 21 | ThemeEditorRootPanel, 22 | ThemeEditorDrawer, 23 | ThemeEditor, 24 | } from '../src' 25 | 26 | export default { 27 | title: 'HyperThemeEditor/hypertheme-chakra-ui/ThemeEditor Community', 28 | component: ThemeEditor, 29 | argTypes: {}, 30 | } as ComponentMeta 31 | 32 | export const DefaultEditor = (args) => ( 33 | 34 | 35 | 36 | 37 | ) 38 | 39 | export const DefaultEditorWithHiddenCredits = (args) => ( 40 | 41 | 42 | 43 | 44 | ) 45 | 46 | export const WithRootPanel = (args) => ( 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | ) 62 | 63 | export const WithoutRootPanel = (args) => ( 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | ) 75 | -------------------------------------------------------------------------------- /packages/hypertheme-chakra-ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "outDir": "lib", 5 | "lib": [ 6 | "dom", 7 | "dom.iterable", 8 | "esnext" 9 | ], 10 | "declaration": true, 11 | "declarationDir": "lib", 12 | "allowJs": true, 13 | "skipLibCheck": true, 14 | "esModuleInterop": true, 15 | "allowSyntheticDefaultImports": true, 16 | "strict": true, 17 | "forceConsistentCasingInFileNames": true, 18 | "module": "esnext", 19 | "moduleResolution": "node", 20 | "resolveJsonModule": true, 21 | "isolatedModules": true, 22 | "noImplicitAny": false, 23 | "noUnusedParameters": false, 24 | "noUnusedLocals": false, 25 | "noEmit": true, 26 | "jsx": "react" 27 | }, 28 | "include": [ 29 | "src" 30 | ], 31 | "exclude": [ 32 | "node_modules", 33 | "lib", 34 | "stories" 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /setupTests.ts: -------------------------------------------------------------------------------- 1 | import { configure } from 'enzyme' 2 | import Adapter from '@wojtekmaj/enzyme-adapter-react-17' 3 | 4 | configure({ adapter: new Adapter() }) 5 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "outDir": "lib", 5 | "lib": [ 6 | "dom", 7 | "dom.iterable", 8 | "esnext" 9 | ], 10 | "declaration": true, 11 | "declarationDir": "lib", 12 | "allowJs": true, 13 | "skipLibCheck": true, 14 | "esModuleInterop": true, 15 | "allowSyntheticDefaultImports": true, 16 | "strict": true, 17 | "forceConsistentCasingInFileNames": true, 18 | "module": "esnext", 19 | "moduleResolution": "node", 20 | "resolveJsonModule": true, 21 | "isolatedModules": true, 22 | "noImplicitAny": false, 23 | "noUnusedParameters": false, 24 | "noUnusedLocals": false, 25 | "noEmit": true, 26 | "jsx": "react" 27 | }, 28 | "include": [ 29 | "packages", 30 | "website" 31 | ], 32 | "exclude": [ 33 | "node_modules" 34 | ] 35 | } 36 | --------------------------------------------------------------------------------