├── .gitattributes ├── .github ├── CODEOWNERS ├── labeler.yml └── workflows │ ├── housekeeping.yml │ ├── labeler.yml │ ├── quality.yml │ ├── release.yml │ └── storybook.yml ├── .gitignore ├── .husky └── pre-commit ├── .lintstagedrc.js ├── .npmrc ├── .prettierignore ├── .prettierrc.js ├── .vscode ├── extensions.json └── settings.json ├── .yarn ├── plugins │ └── @yarnpkg │ │ └── plugin-after-install.cjs └── releases │ └── yarn-4.6.0.cjs ├── .yarnrc.yml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── docs ├── CONVENTIONS.md ├── TOOLING.md └── TROUBLESHOOTING.md ├── eslint.config.mjs ├── mise.toml ├── multi-release.config.js ├── package.json ├── packages ├── design-tokens │ ├── .gitignore │ ├── CHANGELOG.md │ ├── README.md │ ├── config.js │ ├── eslint.config.mjs │ ├── nodemon.json │ ├── package.json │ ├── src │ │ ├── actions.js │ │ ├── build.js │ │ ├── constants.js │ │ ├── formats.js │ │ ├── matchers.js │ │ ├── tailwindcss │ │ │ ├── tailwind.config.ts │ │ │ └── tailwind.theme.ts │ │ └── transforms.js │ ├── tokens │ │ ├── border.js │ │ ├── color │ │ │ ├── background.js │ │ │ ├── base.js │ │ │ ├── border.js │ │ │ ├── brand.js │ │ │ ├── decorative.js │ │ │ ├── feedback.js │ │ │ ├── icon.js │ │ │ ├── stroke.js │ │ │ └── text.js │ │ ├── shadow.js │ │ ├── size │ │ │ ├── border.js │ │ │ ├── breakpoint.js │ │ │ ├── radius.js │ │ │ └── spacing.js │ │ ├── style-and-decoration.js │ │ └── typography.js │ ├── tsconfig.build.json │ └── tsconfig.json └── fractal │ ├── .gitignore │ ├── .prettierrc.cjs │ ├── .storybook │ ├── DocumentationTemplate.mdx │ ├── main.ts │ ├── manager-head.html │ ├── manager.ts │ ├── preview-head.html │ ├── preview.tsx │ └── theme.ts │ ├── CHANGELOG.md │ ├── README.md │ ├── eslint.config.mjs │ ├── global.d.ts │ ├── index.ts │ ├── package.json │ ├── postcss.config.cjs │ ├── public │ ├── android-chrome-192x192.png │ ├── android-chrome-384x384.png │ ├── apple-touch-icon-120x120.png │ ├── apple-touch-icon-152x152.png │ ├── apple-touch-icon-180x180.png │ ├── apple-touch-icon-60x60.png │ ├── apple-touch-icon-76x76.png │ ├── apple-touch-icon.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon.ico │ ├── images │ │ ├── cover_fractal.png │ │ ├── cover_fractal_old.png │ │ ├── logo_fractal.png │ │ ├── logo_pink_black.png │ │ └── logo_pink_white.png │ ├── safari-pinned-tab.svg │ └── site.webmanifest │ ├── scripts │ ├── build-stats.sh │ └── transform-icons-imports.js │ ├── src │ ├── ThemeProvider.tsx │ ├── components │ │ ├── Autocomplete │ │ │ ├── Autocomplete.constants.ts │ │ │ ├── Autocomplete.mdx │ │ │ ├── Autocomplete.stories.tsx │ │ │ ├── Autocomplete.tsx │ │ │ ├── Autocomplete.types.ts │ │ │ ├── AutocompleteEmpty.mdx │ │ │ ├── AutocompleteEmpty.stories.tsx │ │ │ ├── AutocompleteEmpty.tsx │ │ │ ├── AutocompleteItem.mdx │ │ │ ├── AutocompleteItem.stories.tsx │ │ │ ├── AutocompleteItemGroup.mdx │ │ │ ├── AutocompleteItemGroup.stories.tsx │ │ │ ├── AutocompleteItemSeparator.mdx │ │ │ ├── AutocompleteItemSeparator.stories.tsx │ │ │ ├── AutocompleteLoading.mdx │ │ │ ├── AutocompleteLoading.stories.tsx │ │ │ ├── AutocompleteLoading.tsx │ │ │ └── index.ts │ │ ├── Avatar │ │ │ ├── Avatar.constants.ts │ │ │ ├── Avatar.mdx │ │ │ ├── Avatar.stories.tsx │ │ │ ├── Avatar.tsx │ │ │ ├── Avatar.types.ts │ │ │ └── index.ts │ │ ├── Badge │ │ │ ├── Badge.constants.ts │ │ │ ├── Badge.mdx │ │ │ ├── Badge.stories.tsx │ │ │ ├── Badge.tsx │ │ │ ├── Badge.types.ts │ │ │ └── index.ts │ │ ├── Button │ │ │ ├── Button.constants.ts │ │ │ ├── Button.mdx │ │ │ ├── Button.stories.tsx │ │ │ ├── Button.tsx │ │ │ ├── Button.types.ts │ │ │ └── index.ts │ │ ├── Card │ │ │ ├── Card.constants.ts │ │ │ ├── Card.mdx │ │ │ ├── Card.stories.tsx │ │ │ ├── Card.tsx │ │ │ ├── Card.types.ts │ │ │ └── index.ts │ │ ├── Confirm │ │ │ ├── Confirm.constants.ts │ │ │ ├── Confirm.mdx │ │ │ ├── Confirm.stories.tsx │ │ │ ├── Confirm.tsx │ │ │ ├── Confirm.types.ts │ │ │ └── index.ts │ │ ├── CuteIcon │ │ │ ├── CuteIcon.constants.ts │ │ │ ├── CuteIcon.mdx │ │ │ ├── CuteIcon.stories.tsx │ │ │ ├── CuteIcon.tsx │ │ │ ├── CuteIcon.types.ts │ │ │ └── index.ts │ │ ├── DateTimePicker │ │ │ ├── DateTimePicker.constants.ts │ │ │ ├── DateTimePicker.mdx │ │ │ ├── DateTimePicker.stories.tsx │ │ │ ├── DateTimePicker.tsx │ │ │ ├── DateTimePicker.types.ts │ │ │ └── index.ts │ │ ├── Dialog │ │ │ ├── Dialog.constants.ts │ │ │ ├── Dialog.mdx │ │ │ ├── Dialog.stories.tsx │ │ │ ├── Dialog.tsx │ │ │ ├── Dialog.types.ts │ │ │ └── index.ts │ │ ├── Dropdown │ │ │ ├── Dropdown.constants.ts │ │ │ ├── Dropdown.mdx │ │ │ ├── Dropdown.stories.tsx │ │ │ ├── Dropdown.tsx │ │ │ ├── Dropdown.types.ts │ │ │ ├── DropdownContext.ts │ │ │ ├── DropdownGroupContext.ts │ │ │ ├── DropdownItem.mdx │ │ │ ├── DropdownItem.stories.tsx │ │ │ ├── DropdownItem.tsx │ │ │ ├── DropdownItemGroup.mdx │ │ │ ├── DropdownItemGroup.stories.tsx │ │ │ ├── DropdownItemGroup.tsx │ │ │ ├── DropdownItemSeparator.mdx │ │ │ ├── DropdownItemSeparator.stories.tsx │ │ │ ├── DropdownItemSeparator.tsx │ │ │ ├── DropdownRadioGroup.mdx │ │ │ ├── DropdownRadioGroup.stories.tsx │ │ │ ├── DropdownRadioGroup.tsx │ │ │ ├── DropdownRadioItem.mdx │ │ │ ├── DropdownRadioItem.stories.tsx │ │ │ ├── DropdownRadioItem.tsx │ │ │ ├── SubDropdown.mdx │ │ │ ├── SubDropdown.stories.tsx │ │ │ ├── SubDropdown.tsx │ │ │ └── index.ts │ │ ├── EmojiPicker │ │ │ ├── Emoji.mdx │ │ │ ├── Emoji.stories.tsx │ │ │ ├── Emoji.tsx │ │ │ ├── EmojiPicker.constants.ts │ │ │ ├── EmojiPicker.mdx │ │ │ ├── EmojiPicker.stories.tsx │ │ │ ├── EmojiPicker.tsx │ │ │ ├── EmojiPicker.types.ts │ │ │ └── index.ts │ │ ├── Header │ │ │ ├── Header.constants.ts │ │ │ ├── Header.mdx │ │ │ ├── Header.stories.tsx │ │ │ ├── Header.tsx │ │ │ ├── Header.types.ts │ │ │ └── index.ts │ │ ├── InputCheckbox │ │ │ ├── InputCheckbox.constants.ts │ │ │ ├── InputCheckbox.mdx │ │ │ ├── InputCheckbox.stories.tsx │ │ │ ├── InputCheckbox.tsx │ │ │ ├── InputCheckbox.types.ts │ │ │ └── index.ts │ │ ├── InputDate │ │ │ ├── InputDate.constants.ts │ │ │ ├── InputDate.mdx │ │ │ ├── InputDate.stories.tsx │ │ │ ├── InputDate.tsx │ │ │ ├── InputDate.types.ts │ │ │ └── index.ts │ │ ├── InputFile │ │ │ ├── InputFile.constants.ts │ │ │ ├── InputFile.mdx │ │ │ ├── InputFile.stories.tsx │ │ │ ├── InputFile.tsx │ │ │ ├── InputFile.types.ts │ │ │ └── index.ts │ │ ├── InputPhone │ │ │ ├── InputPhone.constants.ts │ │ │ ├── InputPhone.mdx │ │ │ ├── InputPhone.stories.tsx │ │ │ ├── InputPhone.tsx │ │ │ ├── InputPhone.types.ts │ │ │ └── index.ts │ │ ├── InputPinCode │ │ │ ├── InputPinCode.constants.ts │ │ │ ├── InputPinCode.mdx │ │ │ ├── InputPinCode.stories.tsx │ │ │ ├── InputPinCode.tsx │ │ │ ├── InputPinCode.types.ts │ │ │ └── index.ts │ │ ├── InputRadio │ │ │ ├── InputRadio.constants.ts │ │ │ ├── InputRadio.mdx │ │ │ ├── InputRadio.stories.tsx │ │ │ ├── InputRadio.tsx │ │ │ ├── InputRadio.types.ts │ │ │ ├── InputRadioContext.ts │ │ │ ├── InputRadioGroup.mdx │ │ │ ├── InputRadioGroup.stories.tsx │ │ │ ├── InputRadioGroup.tsx │ │ │ └── index.ts │ │ ├── InputText │ │ │ ├── InputText.constants.ts │ │ │ ├── InputText.mdx │ │ │ ├── InputText.stories.tsx │ │ │ ├── InputText.tsx │ │ │ ├── InputText.types.ts │ │ │ └── index.ts │ │ ├── Loader │ │ │ ├── Loader.constants.ts │ │ │ ├── Loader.mdx │ │ │ ├── Loader.stories.tsx │ │ │ ├── Loader.tsx │ │ │ ├── Loader.types.ts │ │ │ └── index.ts │ │ ├── Logo │ │ │ ├── Logo.constants.ts │ │ │ ├── Logo.mdx │ │ │ ├── Logo.stories.tsx │ │ │ ├── Logo.tsx │ │ │ ├── Logo.types.ts │ │ │ └── index.ts │ │ ├── Menu │ │ │ ├── Menu.constants.ts │ │ │ ├── Menu.mdx │ │ │ ├── Menu.stories.tsx │ │ │ ├── Menu.tsx │ │ │ ├── Menu.types.ts │ │ │ ├── MenuContext.ts │ │ │ ├── MenuGroupContext.ts │ │ │ ├── MenuItem.mdx │ │ │ ├── MenuItem.stories.tsx │ │ │ ├── MenuItem.tsx │ │ │ ├── MenuItemGroup.mdx │ │ │ ├── MenuItemGroup.stories.tsx │ │ │ ├── MenuItemGroup.tsx │ │ │ ├── MenuItemSeparator.mdx │ │ │ ├── MenuItemSeparator.stories.tsx │ │ │ ├── MenuItemSeparator.tsx │ │ │ ├── SubMenu.mdx │ │ │ ├── SubMenu.stories.tsx │ │ │ ├── SubMenu.tsx │ │ │ └── index.ts │ │ ├── Paper │ │ │ ├── Paper.constants.ts │ │ │ ├── Paper.mdx │ │ │ ├── Paper.stories.tsx │ │ │ ├── Paper.tsx │ │ │ ├── Paper.types.ts │ │ │ └── index.ts │ │ ├── Popover │ │ │ ├── Popover.constants.ts │ │ │ ├── Popover.mdx │ │ │ ├── Popover.stories.tsx │ │ │ ├── Popover.tsx │ │ │ ├── Popover.types.ts │ │ │ └── index.ts │ │ ├── Progress │ │ │ ├── Progress.constants.ts │ │ │ ├── Progress.mdx │ │ │ ├── Progress.stories.tsx │ │ │ ├── Progress.tsx │ │ │ ├── Progress.types.ts │ │ │ └── index.ts │ │ ├── ScrollArea │ │ │ ├── ScrollArea.constants.ts │ │ │ ├── ScrollArea.mdx │ │ │ ├── ScrollArea.stories.tsx │ │ │ ├── ScrollArea.tsx │ │ │ ├── ScrollArea.types.ts │ │ │ └── index.ts │ │ ├── Select │ │ │ ├── Select.constants.ts │ │ │ ├── Select.mdx │ │ │ ├── Select.stories.tsx │ │ │ ├── Select.tsx │ │ │ ├── Select.types.ts │ │ │ ├── SelectEmpty.mdx │ │ │ ├── SelectEmpty.stories.tsx │ │ │ ├── SelectEmpty.tsx │ │ │ ├── SelectGroupContext.ts │ │ │ ├── SelectItem.mdx │ │ │ ├── SelectItem.stories.tsx │ │ │ ├── SelectItem.tsx │ │ │ ├── SelectItemGroup.mdx │ │ │ ├── SelectItemGroup.stories.tsx │ │ │ ├── SelectItemGroup.tsx │ │ │ ├── SelectItemSeparator.mdx │ │ │ ├── SelectItemSeparator.stories.tsx │ │ │ └── index.ts │ │ ├── Skeleton │ │ │ ├── Skeleton.constants.ts │ │ │ ├── Skeleton.mdx │ │ │ ├── Skeleton.stories.tsx │ │ │ ├── Skeleton.tsx │ │ │ ├── Skeleton.types.ts │ │ │ └── index.ts │ │ ├── Slider │ │ │ ├── Slider.constants.ts │ │ │ ├── Slider.mdx │ │ │ ├── Slider.stories.tsx │ │ │ ├── Slider.tsx │ │ │ ├── Slider.types.ts │ │ │ └── index.ts │ │ ├── Stepper │ │ │ ├── Stepper.constants.ts │ │ │ ├── Stepper.mdx │ │ │ ├── Stepper.stories.tsx │ │ │ ├── Stepper.tsx │ │ │ ├── Stepper.types.ts │ │ │ └── index.ts │ │ ├── Switch │ │ │ ├── Switch.constants.ts │ │ │ ├── Switch.mdx │ │ │ ├── Switch.stories.tsx │ │ │ ├── Switch.tsx │ │ │ ├── Switch.types.ts │ │ │ └── index.ts │ │ ├── Tabs │ │ │ ├── Tab.mdx │ │ │ ├── Tab.stories.tsx │ │ │ ├── Tab.tsx │ │ │ ├── TabContent.mdx │ │ │ ├── TabContent.stories.tsx │ │ │ ├── TabContent.tsx │ │ │ ├── Tabs.constants.ts │ │ │ ├── Tabs.mdx │ │ │ ├── Tabs.stories.tsx │ │ │ ├── Tabs.tsx │ │ │ ├── Tabs.types.ts │ │ │ └── index.ts │ │ ├── Tag │ │ │ ├── Tag.constants.ts │ │ │ ├── Tag.mdx │ │ │ ├── Tag.stories.tsx │ │ │ ├── Tag.tsx │ │ │ ├── Tag.types.ts │ │ │ └── index.ts │ │ ├── Textarea │ │ │ ├── Textarea.constants.ts │ │ │ ├── Textarea.mdx │ │ │ ├── Textarea.stories.tsx │ │ │ ├── Textarea.tsx │ │ │ ├── Textarea.types.ts │ │ │ └── index.ts │ │ ├── Toggle │ │ │ ├── Toggle.constants.ts │ │ │ ├── Toggle.mdx │ │ │ ├── Toggle.stories.tsx │ │ │ ├── Toggle.tsx │ │ │ ├── Toggle.types.ts │ │ │ └── index.ts │ │ ├── ToggleGroup │ │ │ ├── ToggleGroup.constants.ts │ │ │ ├── ToggleGroup.mdx │ │ │ ├── ToggleGroup.stories.tsx │ │ │ ├── ToggleGroup.tsx │ │ │ ├── ToggleGroup.types.ts │ │ │ ├── ToggleGroupContext.ts │ │ │ ├── ToggleGroupItem.mdx │ │ │ ├── ToggleGroupItem.stories.tsx │ │ │ ├── ToggleGroupItem.tsx │ │ │ └── index.ts │ │ ├── Toolbar │ │ │ ├── Toolbar.constants.ts │ │ │ ├── Toolbar.mdx │ │ │ ├── Toolbar.stories.tsx │ │ │ ├── Toolbar.tsx │ │ │ ├── Toolbar.types.ts │ │ │ ├── ToolbarButton.mdx │ │ │ ├── ToolbarButton.stories.tsx │ │ │ ├── ToolbarButton.tsx │ │ │ ├── ToolbarContext.ts │ │ │ ├── ToolbarDropdown.mdx │ │ │ ├── ToolbarDropdown.stories.tsx │ │ │ ├── ToolbarDropdown.tsx │ │ │ ├── ToolbarDropdownItem.mdx │ │ │ ├── ToolbarDropdownItem.stories.tsx │ │ │ ├── ToolbarDropdownItemGroup.mdx │ │ │ ├── ToolbarDropdownItemGroup.stories.tsx │ │ │ ├── ToolbarDropdownItemSeparator.mdx │ │ │ ├── ToolbarDropdownItemSeparator.stories.tsx │ │ │ ├── ToolbarSeparator.mdx │ │ │ ├── ToolbarSeparator.stories.tsx │ │ │ ├── ToolbarSeparator.tsx │ │ │ └── index.ts │ │ ├── Tooltip │ │ │ ├── Tooltip.constants.ts │ │ │ ├── Tooltip.mdx │ │ │ ├── Tooltip.stories.tsx │ │ │ ├── Tooltip.tsx │ │ │ ├── Tooltip.types.ts │ │ │ └── index.ts │ │ ├── Typography │ │ │ ├── Typography.constants.ts │ │ │ ├── Typography.mdx │ │ │ ├── Typography.stories.tsx │ │ │ ├── Typography.tsx │ │ │ ├── Typography.types.ts │ │ │ └── index.ts │ │ └── index.ts │ ├── constants.ts │ ├── hooks │ │ ├── index.ts │ │ └── useTheme.ts │ ├── index.ts │ ├── mocks.tsx │ ├── styles │ │ ├── global.css │ │ ├── helpers.ts │ │ ├── polyfills.css │ │ └── smartphones.css │ ├── tests_helpers.ts │ ├── types.ts │ └── utils.ts │ ├── stories │ ├── Breakpoints.mdx │ ├── Colors.mdx │ ├── Iconography.mdx │ ├── Introduction.mdx │ ├── Shadows.mdx │ ├── Spacing.mdx │ ├── Tokens.mdx │ └── Typography.mdx │ ├── tailwind.config.ts │ ├── tsconfig.build.json │ ├── tsconfig.json │ ├── tsup.config.ts │ └── vite.config.ts ├── renovate.json5 ├── scripts ├── check-tokens.sh ├── colors.sh ├── copy-storybook.sh ├── filter-vercel-deployments.sh ├── setup-freezer.sh └── update-fonts.sh ├── tsconfig.eslint.json ├── tsconfig.json ├── yarn.config.cjs └── yarn.lock /.gitattributes: -------------------------------------------------------------------------------- 1 | /.yarn/** linguist-vendored 2 | /.yarn/releases/* binary 3 | /.yarn/plugins/**/* binary 4 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Code owners 2 | # Read more: https://help.github.com/en/articles/about-code-owners 3 | 4 | * @snowball-tech/engineering 5 | -------------------------------------------------------------------------------- /.github/labeler.yml: -------------------------------------------------------------------------------- 1 | Design Tokens: 2 | - package/design-tokens/** 3 | 4 | Fractal: 5 | - package/fractal/** 6 | -------------------------------------------------------------------------------- /.github/workflows/housekeeping.yml: -------------------------------------------------------------------------------- 1 | name: Issues & PRs housekeep 2 | 3 | on: 4 | schedule: 5 | - cron: '30 1 * * *' 6 | 7 | jobs: 8 | stale: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/stale@v9 12 | with: 13 | stale-issue-message: | 14 | This issue didn't had any activity in the last 30 days. 15 | 16 | Without any further action or update, it will be closed in 7 days. 17 | 18 | You can remove the "Stale" label or add a comment to keep it open. 19 | close-issue-message: | 20 | This issue was closed because it has been stalled for 7 days. 21 | 22 | If you think this is still relevant, you can reopen it. 23 | stale-pr-message: | 24 | This pull request didn't had any activity in the last 30 days. 25 | 26 | Without any further action or update, it will be closed in 7 days. 27 | 28 | You can remove the "Stale" label or add a comment to keep it open. 29 | close-pr-message: | 30 | This pull request was closed because it has been stalled for 7 days. 31 | 32 | If you think this is still relevant, you can reopen it. 33 | days-before-stale: 30 34 | exempt-all-milestones: true 35 | exempt-pr-labels: Do no stale 36 | -------------------------------------------------------------------------------- /.github/workflows/labeler.yml: -------------------------------------------------------------------------------- 1 | # Automatically add a label to a pull request based on the modified files 2 | 3 | name: Pull Request Labeler 4 | 5 | on: 6 | - pull_request_target 7 | 8 | jobs: 9 | triage: 10 | permissions: 11 | contents: read 12 | pull-requests: write 13 | 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/labeler@v5 18 | with: 19 | sync-labels: true 20 | -------------------------------------------------------------------------------- /.github/workflows/quality.yml: -------------------------------------------------------------------------------- 1 | # This is a Github Workflow that runs lint, formatter and unit tests on every 2 | # push and pull request. 3 | 4 | name: Quality 5 | 6 | on: 7 | push: 8 | branches: 9 | - main 10 | 11 | pull_request: 12 | branches: 13 | - main 14 | 15 | concurrency: 16 | group: ${{ github.workflow }}-${{ github.ref }} 17 | cancel-in-progress: true 18 | 19 | env: 20 | FREEZER_TOKEN: ${{ secrets.FREEZER_TOKEN }} 21 | 22 | jobs: 23 | quality: 24 | name: Checking code quality 25 | runs-on: ubuntu-latest 26 | steps: 27 | - uses: actions/checkout@v4 28 | 29 | - uses: actions/setup-node@v4 30 | with: 31 | node-version: 22.x 32 | cache: yarn 33 | 34 | - name: Setting up dependencies & workspaces 35 | run: yarn install --immutable && yarn run -T setup 36 | 37 | - name: Checking linter 38 | run: yarn run -T lint 39 | 40 | - name: Checking format 41 | run: yarn run -T format 42 | 43 | - name: Checking typings 44 | run: yarn run -T types-check 45 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # This is a Github Workflow that waits for quality and security to be executed 2 | # on the `main` branch. 3 | 4 | # If everything pass it also release all package using Semantic Release. 5 | 6 | name: Release 7 | 8 | on: 9 | push: 10 | branches: 11 | - main 12 | 13 | env: 14 | FREEZER_TOKEN: ${{ secrets.FREEZER_TOKEN }} 15 | NODE_ENV: production 16 | 17 | jobs: 18 | release: 19 | name: Releasing updated packages 20 | runs-on: ubuntu-latest 21 | timeout-minutes: 10 22 | steps: 23 | - uses: actions/checkout@v4 24 | with: 25 | token: ${{ secrets.MACHINE_GITHUB_TOKEN }} 26 | 27 | - uses: actions/setup-node@v4 28 | with: 29 | node-version: 22.x 30 | cache: yarn 31 | 32 | - name: Setting up dependencies & workspaces 33 | run: yarn install --immutable && yarn run -T setup 34 | 35 | - name: Setting up Git to access Freezer 36 | run: yarn run setup-freezer 37 | 38 | - name: Building all packages 39 | run: yarn run -T build 40 | 41 | - name: Waiting for quality to pass 42 | uses: lewagon/wait-on-check-action@v1.3.4 43 | with: 44 | ref: ${{ github.ref }} 45 | check-name: Checking code quality 46 | repo-token: ${{ secrets.GITHUB_TOKEN }} 47 | wait-interval: 10 48 | 49 | - name: Publishing updated public packages to NPM, Git & GitHub 50 | env: 51 | GH_TOKEN: ${{ secrets.MACHINE_GITHUB_TOKEN }} 52 | NPM_TOKEN: ${{ secrets.MACHINE_NPM_TOKEN }} 53 | GIT_AUTHOR_NAME: '@snowball-tech-bot' 54 | GIT_AUTHOR_EMAIL: snowball-bot@snowball.xyz 55 | GIT_COMMITTER_NAME: '@snowball-tech-bot' 56 | GIT_COMMITTER_EMAIL: snowball-bot@snowball.xyz 57 | run: yarn run -T publish-all 58 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Misc. MacOS 2 | **/*.DS_Store 3 | 4 | # Logs 5 | **/logs 6 | **/*.log 7 | **/npm-*.log* 8 | **/yarn-*.log* 9 | **/*.tsbuildinfo 10 | **/vite.config.ts.timestamp* 11 | 12 | # Local testing 13 | *.tgz 14 | 15 | # Cache 16 | **/.eslintcache 17 | 18 | # Coverage 19 | **/coverage 20 | 21 | # Dependencies 22 | **/node_modules 23 | .yarn/* 24 | !.yarn/patches 25 | !.yarn/plugins 26 | !.yarn/releases 27 | !.yarn/sdks 28 | !.yarn/versions 29 | 30 | # Next.JS 31 | **/.next 32 | next-env.d.ts 33 | 34 | # Production 35 | **/build 36 | **/out 37 | **/dist 38 | 39 | # Secret files 40 | **/.env*.local 41 | *.pem 42 | 43 | # IDE 44 | **/.idea 45 | **/*.ntvs* 46 | **/*.njsproj 47 | **/*.sln 48 | **/*.sw? 49 | **/.vscode/* 50 | # Let's keep the extensions.json file because it references all the recommended 51 | # extensions for this workspace. That way, newcomers will receive suggestions. 52 | !.vscode/extensions.json 53 | # Also keeps the settings file because it's critical for VSCode to properly work 54 | # in the mono-repository. 55 | !.vscode/settings.json 56 | # Files from the VSCode "SQLTools" extensions. 57 | **/*.session.sql 58 | 59 | # Pythagora project (https://pythagora.ai/). 60 | .pythagora 61 | 62 | # Storybook. 63 | **/storybook-static* 64 | 65 | # Vercel. 66 | **/.vercel 67 | 68 | # Supabase. 69 | **/supabase/.branches 70 | **/supabase/.temp 71 | 72 | # Misc. 73 | *.bak 74 | scripts/tmp 75 | # This file is only used for the Tailwind VSCode extension. 76 | tailwind.config.cjs 77 | 78 | # PandaCSS. 79 | styled-system* 80 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | yarn lint-staged 2 | -------------------------------------------------------------------------------- /.lintstagedrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | '!*.{js,cjs,mjs,jsx,ts,tsx,json,json5,jsonc,yml,yaml,md,mdx,css,html}': [ 3 | 'prettier --ignore-unknown --cache --write', 4 | ], 5 | '*.{js,cjs,mjs,ts,json,json5,jsonc,mdx,yml,yaml,md}': [ 6 | 'eslint --cache --fix', 7 | 'prettier --ignore-unknown --cache --write', 8 | ], 9 | '*.{jsx,tsx,css,html}': [ 10 | 'eslint --cache --fix', 11 | 'prettier --ignore-unknown --cache --write', 12 | ], 13 | } 14 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | workspaces-update = false 2 | registry=https://registry.npmjs.org/ 3 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | *.log 2 | .* 3 | !.*.cjs 4 | !.*.js 5 | !.*.json 6 | !.*.json5 7 | !.*.jsx 8 | !.*.md 9 | !.*.mdx 10 | !.*.mjs 11 | !.*.ts 12 | !.*.tsx 13 | !.*.yaml 14 | !.*.yml 15 | !.github 16 | !.storybook 17 | !.vscode 18 | LICENSE 19 | 20 | mise.toml 21 | 22 | .pnp.* 23 | *.lock 24 | *.tsbuildinfo 25 | vite.config.ts.timestamp* 26 | 27 | **/dist 28 | **/out 29 | **/build 30 | 31 | **/coverage 32 | 33 | **/storybook-static 34 | **/styled-system 35 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = require('@snowball-tech/prettier-config/.prettierrc-tailwind') 2 | -------------------------------------------------------------------------------- /.yarn/plugins/@yarnpkg/plugin-after-install.cjs: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | //prettier-ignore 3 | module.exports = { 4 | name: "@yarnpkg/plugin-after-install", 5 | factory: function (require) { 6 | "use strict";var plugin=(()=>{var s=Object.defineProperty;var g=Object.getOwnPropertyDescriptor;var x=Object.getOwnPropertyNames;var C=Object.prototype.hasOwnProperty;var r=(t=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(t,{get:(o,e)=>(typeof require<"u"?require:o)[e]}):t)(function(t){if(typeof require<"u")return require.apply(this,arguments);throw new Error('Dynamic require of "'+t+'" is not supported')});var I=(t,o)=>{for(var e in o)s(t,e,{get:o[e],enumerable:!0})},h=(t,o,e,a)=>{if(o&&typeof o=="object"||typeof o=="function")for(let n of x(o))!C.call(t,n)&&n!==e&&s(t,n,{get:()=>o[n],enumerable:!(a=g(o,n))||a.enumerable});return t};var k=t=>h(s({},"__esModule",{value:!0}),t);var P={};I(P,{default:()=>y});var d=r("@yarnpkg/core");var f=r("@yarnpkg/core"),c={afterInstall:{description:"Hook that will always run after install",type:f.SettingsType.STRING,default:""}};var p=r("clipanion"),u=r("@yarnpkg/core");var m=r("@yarnpkg/shell"),l=async(t,o)=>{let e=t.get("afterInstall"),a=!!t.projectCwd?.endsWith(`dlx-${process.pid}`);return e&&!a?(o&&console.log("Running `afterInstall` hook..."),(0,m.execute)(e,[],{cwd:t.projectCwd||void 0})):0};var i=class extends p.Command{async execute(){let o=await u.Configuration.find(this.context.cwd,this.context.plugins);return l(o,!1)}};i.paths=[["after-install"]];var w={configuration:c,commands:[i],hooks:{afterAllInstalled:async(t,o)=>{if(o?.mode===d.InstallMode.UpdateLockfile)return;if(await l(t.configuration,!0))throw new Error("The `afterInstall` hook failed, see output above.")}}},y=w;return k(P);})(); 7 | return plugin; 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | compressionLevel: mixed 2 | 3 | enableConstraintsChecks: true 4 | 5 | enableTelemetry: false 6 | 7 | injectEnvironmentFiles: 8 | - .env.yarn? 9 | - .env.local? 10 | 11 | nodeLinker: node-modules 12 | 13 | npmPublishRegistry: 'https://registry.npmjs.org' 14 | 15 | plugins: 16 | - checksum: 0a2a35fbed2f33f0df1ceb1db51bf72554201f994eaecb86cbc62a295c3d05f7cc44fa8be8e64fc5e1c0bee4f529a17a0cc429ea9e3486ad467443291d5a8e3b 17 | path: .yarn/plugins/@yarnpkg/plugin-after-install.cjs 18 | spec: 'https://raw.githubusercontent.com/mhassan1/yarn-plugin-after-install/v0.6.0/bundles/@yarnpkg/plugin-after-install.js' 19 | 20 | yarnPath: .yarn/releases/yarn-4.6.0.cjs 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Snowball 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 | -------------------------------------------------------------------------------- /docs/TROUBLESHOOTING.md: -------------------------------------------------------------------------------- 1 | # FAQ & Troubleshooting 2 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import base from '@snowball-tech/eslint-snowball-config/configs/base.js' 2 | import html from '@snowball-tech/eslint-snowball-config/configs/html.js' 3 | import importConfig from '@snowball-tech/eslint-snowball-config/configs/import.js' 4 | import json from '@snowball-tech/eslint-snowball-config/configs/json.js' 5 | import lodash from '@snowball-tech/eslint-snowball-config/configs/lodash.js' 6 | import markdown from '@snowball-tech/eslint-snowball-config/configs/markdown.js' 7 | import perfectionist from '@snowball-tech/eslint-snowball-config/configs/perfectionist.js' 8 | import prettier from '@snowball-tech/eslint-snowball-config/configs/prettier.js' 9 | import react from '@snowball-tech/eslint-snowball-config/configs/react.js' 10 | import secrets from '@snowball-tech/eslint-snowball-config/configs/secrets.js' 11 | import tailwind from '@snowball-tech/eslint-snowball-config/configs/tailwind.js' 12 | import typescript from '@snowball-tech/eslint-snowball-config/configs/typescript.js' 13 | import yml from '@snowball-tech/eslint-snowball-config/configs/yml.js' 14 | 15 | export default [ 16 | ...base, 17 | ...html, 18 | ...json, 19 | ...markdown, 20 | ...yml, 21 | ...secrets, 22 | ...importConfig, 23 | ...typescript, 24 | ...react, 25 | ...lodash, 26 | ...perfectionist, 27 | ...tailwind, 28 | ...prettier, 29 | 30 | { 31 | files: ['packages/design-tokens/src/tailwind.config.ts'], 32 | 33 | rules: { 34 | 'import/no-unresolved': 'off', 35 | }, 36 | }, 37 | 38 | { 39 | files: ['**/*.md/*.{js,ts,tsx}'], 40 | 41 | rules: { 42 | '@typescript-eslint/no-unused-vars': 'off', 43 | }, 44 | }, 45 | 46 | { 47 | files: ['**/stories/*.mdx'], 48 | 49 | rules: { 50 | 'markdown/heading-increment': 'off', 51 | 'markdown/no-missing-label-refs': 'off', 52 | }, 53 | }, 54 | ] 55 | -------------------------------------------------------------------------------- /mise.toml: -------------------------------------------------------------------------------- 1 | [tools] 2 | node = '22' 3 | -------------------------------------------------------------------------------- /multi-release.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: '@snowball-tech/semantic-release-config/multi-release.config.js', 3 | } 4 | -------------------------------------------------------------------------------- /packages/design-tokens/.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | -------------------------------------------------------------------------------- /packages/design-tokens/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line import/no-relative-packages 2 | import defaultConfig from '../../eslint.config.mjs' 3 | 4 | export default [ 5 | ...defaultConfig, 6 | 7 | { 8 | files: ['src/tailwind.config.ts'], 9 | 10 | rules: { 11 | 'import/no-unresolved': 'off', 12 | }, 13 | }, 14 | ] 15 | -------------------------------------------------------------------------------- /packages/design-tokens/nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["tokens/", "src/", "assets/", "config.js"], 3 | "ext": "js,json,json5" 4 | } 5 | -------------------------------------------------------------------------------- /packages/design-tokens/src/actions.js: -------------------------------------------------------------------------------- 1 | const { execSync } = require('node:child_process') 2 | const path = require('node:path') 3 | 4 | const StyleDictionary = require('style-dictionary') 5 | 6 | const noop = require('lodash/fp/noop') 7 | 8 | StyleDictionary.registerAction({ 9 | do: () => { 10 | const constantsPath = path.resolve(__dirname, './constants.js') 11 | 12 | const destinationDirectory = 'dist/web/typescript/' 13 | const destinationPath = path.resolve( 14 | __dirname, 15 | `../${destinationDirectory}`, 16 | ) 17 | 18 | execSync( 19 | `yarn to-esm ${constantsPath} --output ${destinationPath} --extension .js --minify --no-comments --noHeader`, 20 | ) 21 | console.log(`✔︎ ${destinationDirectory}constants.js (ESM)`) 22 | }, 23 | name: 'typescript/copy-constants', 24 | undo: noop, 25 | }) 26 | 27 | StyleDictionary.registerAction({ 28 | do: () => { 29 | const destinationDirectory = 'dist/web/' 30 | 31 | execSync(`yarn run tsc --outDir ${destinationDirectory}`) 32 | console.log( 33 | `✔︎ ${destinationDirectory}tailwindcss/tailwind.config.js (ESM)`, 34 | ) 35 | console.log( 36 | `✔︎ ${destinationDirectory}tailwindcss/tailwind.theme.js (ESM)`, 37 | ) 38 | }, 39 | name: 'tailwindcss/copy-config', 40 | undo: noop, 41 | }) 42 | -------------------------------------------------------------------------------- /packages/design-tokens/src/build.js: -------------------------------------------------------------------------------- 1 | const StyleDictionary = require('style-dictionary') 2 | 3 | require('./actions') 4 | require('./formats') 5 | require('./transforms') 6 | 7 | const config = require('../config') 8 | 9 | const platforms = Object.keys(config.platforms) 10 | 11 | let message = `Building design tokens on ${platforms.length} platform${ 12 | platforms.length > 1 ? 's' : '' 13 | }:` 14 | message = 15 | platforms.length > 1 16 | ? `${message} ${platforms.slice(0, -1).join(', ')} and ${platforms.at(-1)}` 17 | : `${message} ${platforms[0]}` 18 | console.info(`${message}...`) 19 | 20 | const sdInstance = StyleDictionary.extend(config) 21 | 22 | platforms.forEach((platform) => { 23 | sdInstance.buildPlatform(platform) 24 | }) 25 | 26 | console.log('') 27 | console.info( 28 | `${platforms.length} platform${platforms.length > 1 ? 's' : ''} built!`, 29 | ) 30 | -------------------------------------------------------------------------------- /packages/design-tokens/src/constants.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | breakpoints: { 3 | xxs: 'xxs', 4 | 5 | xs: 'xs', 6 | 7 | sm: 'sm', 8 | 9 | md: 'md', 10 | 11 | lg: 'lg', 12 | 13 | xl: 'xl', 14 | 15 | xxl: 'xxl', 16 | }, 17 | } 18 | -------------------------------------------------------------------------------- /packages/design-tokens/src/matchers.js: -------------------------------------------------------------------------------- 1 | // Those matcher are defined to customize transformation of tokens. 2 | 3 | function isIgnored(token) { 4 | return token.original.ignore === true 5 | } 6 | 7 | function isSize(token) { 8 | return token.attributes.category === 'size' || token.original.group === 'size' 9 | } 10 | 11 | function isBreakpointOrRadiusSize(token) { 12 | return ( 13 | !isIgnored(token) && 14 | isSize(token) && 15 | ['breakpoint', 'radius'].includes(token.attributes.type) 16 | ) 17 | } 18 | 19 | function isNotBreakpointNorRadiusSize(token) { 20 | return ( 21 | !isIgnored(token) && 22 | isSize(token) && 23 | !['breakpoint', 'radius'].includes(token.attributes.type) 24 | ) 25 | } 26 | 27 | function isMediaQuery(token) { 28 | return ( 29 | (!isIgnored(token) && token.attributes.category === 'mediaQuery') || 30 | token.original.group === 'mediaQuery' 31 | ) 32 | } 33 | 34 | module.exports = { 35 | isBreakpointOrRadiusSize, 36 | isIgnored, 37 | isMediaQuery, 38 | isNotBreakpointNorRadiusSize, 39 | isSize, 40 | } 41 | -------------------------------------------------------------------------------- /packages/design-tokens/src/tailwindcss/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import path from 'node:path' 2 | 3 | import type { Config } from 'tailwindcss' 4 | 5 | import tailwindTheme from './tailwind.theme' 6 | 7 | export default { 8 | content: [ 9 | './src/**/*.{html,js,ts,jsx,tsx}', 10 | path.join( 11 | path.dirname(require.resolve('@snowball-tech/fractal')), 12 | '**/*.{html,js,ts,jsx,tsx}', 13 | ), 14 | ], 15 | 16 | corePlugins: { 17 | columns: false, 18 | }, 19 | 20 | plugins: [ 21 | // eslint-disable-next-line @typescript-eslint/no-require-imports 22 | require('@tailwindcss/aspect-ratio'), 23 | // eslint-disable-next-line @typescript-eslint/no-require-imports 24 | require('@tailwindcss/container-queries'), 25 | ], 26 | 27 | theme: tailwindTheme, 28 | } satisfies Config 29 | -------------------------------------------------------------------------------- /packages/design-tokens/src/transforms.js: -------------------------------------------------------------------------------- 1 | const StyleDictionary = require('style-dictionary') 2 | 3 | const isFinite = require('lodash/fp/isFinite') 4 | const isString = require('lodash/fp/isString') 5 | 6 | const { 7 | isBreakpointOrRadiusSize, 8 | isMediaQuery, 9 | isNotBreakpointNorRadiusSize, 10 | } = require('./matchers') 11 | 12 | StyleDictionary.registerTransform({ 13 | matcher: isNotBreakpointNorRadiusSize, 14 | name: 'size/other/pxToRem', 15 | transformer: (token, options) => { 16 | if (isString(token.value) && token.value.endsWith('%')) { 17 | return token.value 18 | } 19 | 20 | const floatValue = Number.parseFloat(token.value) 21 | if (!isFinite(floatValue)) { 22 | return token.value 23 | } 24 | 25 | if (floatValue === 0) { 26 | return '0' 27 | } 28 | 29 | const baseFontSize = (options && options.basePxFontSize) || 16 30 | 31 | return `${floatValue / baseFontSize}rem` 32 | }, 33 | type: 'value', 34 | }) 35 | 36 | StyleDictionary.registerTransform({ 37 | matcher: isBreakpointOrRadiusSize, 38 | name: 'size/breakpoint-radius/px', 39 | transformer: (token) => { 40 | if (isString(token.value) && token.value.endsWith('%')) { 41 | return token.value 42 | } 43 | 44 | const floatValue = Number.parseFloat(token.value) 45 | if (!isFinite(floatValue)) { 46 | return token.value 47 | } 48 | 49 | if (floatValue === 0) { 50 | return '0' 51 | } 52 | 53 | return `${floatValue}px` 54 | }, 55 | type: 'value', 56 | }) 57 | 58 | StyleDictionary.registerTransform({ 59 | matcher: isMediaQuery, 60 | name: 'media-query/quote', 61 | transformer: (token) => `'${token.value}'`, 62 | transitive: true, 63 | type: 'value', 64 | }) 65 | 66 | module.exports = {} 67 | -------------------------------------------------------------------------------- /packages/design-tokens/tokens/border.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | border: { 3 | none: { 4 | comment: 'Disable borders.', 5 | 6 | value: 'none', 7 | }, 8 | 9 | 1: { 10 | comment: 'A subtle border.', 11 | 12 | value: '{size.border.1.value} solid {color.border.default.value}', 13 | }, 14 | 2: { 15 | comment: 'A more obvious border.', 16 | 17 | value: '{size.border.2.value} solid {color.border.default.value}', 18 | }, 19 | 20 | disabled: { 21 | comment: 22 | 'A border to use on disabled elements (e.g. buttons, input, ...).', 23 | 24 | value: '{size.border.1.value} solid {color.border.disabled.value}', 25 | }, 26 | 27 | transparent: { 28 | 1: { 29 | comment: 'A transparent 1px border.', 30 | 31 | value: '{size.border.1.value} solid {color.base.transparent.value}', 32 | }, 33 | 2: { 34 | comment: 'A transparent 2px border.', 35 | 36 | value: '{size.border.2.value} solid {color.base.transparent.value}', 37 | }, 38 | }, 39 | }, 40 | } 41 | -------------------------------------------------------------------------------- /packages/design-tokens/tokens/color/background.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | color: { 3 | background: { 4 | body: { 5 | black: { 6 | comment: 'Black Tone 0', 7 | value: '{color.base.black.value}', 8 | }, 9 | dark: { 10 | comment: 'Black', 11 | value: '{color.brand.body.dark.value}', 12 | }, 13 | default: { 14 | comment: 'Default background color', 15 | value: '{color.brand.body.light.value}', 16 | }, 17 | light: { 18 | comment: 'Yellowish White Tone 90', 19 | value: '{color.brand.body.light.value}', 20 | }, 21 | primary: { 22 | comment: 'Primary color for background', 23 | value: '{color.brand.primary.value}', 24 | }, 25 | secondary: { 26 | comment: 'Secondary color for background', 27 | value: '{color.brand.secondary.value}', 28 | }, 29 | white: { 30 | comment: 'White Tone 100', 31 | value: '{color.base.white.value}', 32 | }, 33 | }, 34 | 35 | disabled: { 36 | comment: 37 | 'Background color to use on most disabled elements (e.g. buttons).', 38 | 39 | value: '{color.base.grey.70.value}', 40 | }, 41 | }, 42 | }, 43 | } 44 | -------------------------------------------------------------------------------- /packages/design-tokens/tokens/color/base.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | color: { 3 | base: { 4 | black: { comment: 'Black Tone 0', value: '#000000' }, 5 | grey: { 6 | 30: { comment: 'Grey Tone 30', value: '#7E7C78' }, 7 | 50: { comment: 'Grey Tone 50', value: '#B7B1A6' }, 8 | 70: { comment: 'Grey Tone 70', value: '#DDD9D4' }, 9 | 90: { comment: 'Grey Tone 90', value: '#F5F1EC' }, 10 | }, 11 | white: { comment: 'White Tone 100', value: '#FFFFFF' }, 12 | 13 | transparent: { comment: 'Transparent', value: 'transparent' }, 14 | }, 15 | }, 16 | } 17 | -------------------------------------------------------------------------------- /packages/design-tokens/tokens/color/border.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | color: { 3 | border: { 4 | dark: { value: '{color.brand.primary.value}' }, 5 | default: { value: '{color.base.black.value}' }, 6 | disabled: { value: '{color.base.grey.50.value}' }, 7 | light: { value: '{color.base.white.value}' }, 8 | primary: { value: '{color.brand.primary.value}' }, 9 | 'primary-dark': { value: '{color.brand.primary-light.value}' }, 10 | 'primary-light': { value: '{color.brand.primary-dark.value}' }, 11 | secondary: { value: '{color.brand.secondary.value}' }, 12 | 'secondary-dark': { value: '{color.brand.secondary-dark.value}' }, 13 | 'secondary-light': { value: '{color.brand.secondary-light.value}' }, 14 | }, 15 | }, 16 | } 17 | -------------------------------------------------------------------------------- /packages/design-tokens/tokens/color/brand.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | color: { 3 | brand: { 4 | comment: "Main Snowball's brand colors.", 5 | 6 | body: { 7 | dark: { comment: 'Black', value: '{color.base.black}' }, 8 | light: { comment: 'Yellowish White Tone 90', value: '#FFFBF4' }, 9 | }, 10 | 11 | highlight: { 12 | comment: 'Tone 90', 13 | value: '{color.decorative.pink.90.value}', 14 | }, 15 | 16 | primary: { comment: 'Tone 90', value: '#FF8ACD' }, 17 | 'primary-dark': { comment: 'Tone 90', value: '#FF8ACD' }, 18 | 'primary-light': { comment: 'Tone 90', value: '#FF8ACD' }, 19 | secondary: { 20 | comment: 'Black Tone 0', 21 | value: '{color.base.black.value}', 22 | }, 23 | 'secondary-dark': { 24 | comment: 'White Tone 100', 25 | value: '{color.base.white.value}', 26 | }, 27 | 'secondary-light': { 28 | comment: 'Black Tone 0', 29 | value: '{color.base.black.value}', 30 | }, 31 | 32 | separator: { 33 | comment: 'Used for line separators', 34 | value: '{color.base.grey.70.value}', 35 | }, 36 | }, 37 | }, 38 | } 39 | -------------------------------------------------------------------------------- /packages/design-tokens/tokens/color/decorative.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | color: { 3 | decorative: { 4 | comment: 'Decorative colors used for branding and marketing.', 5 | 6 | blue: { 7 | 50: { comment: 'Blue Tone 50', value: '#8AF1FF' }, 8 | 70: { comment: 'Blue Tone 70', value: '#B3F6FF' }, 9 | 90: { comment: 'Blue Tone 90', value: '#D6FAFF' }, 10 | }, 11 | green: { 12 | 50: { comment: 'Green Tone 50', value: '#76F7AE' }, 13 | 70: { comment: 'Green Tone 70', value: '#ADFACE' }, 14 | 90: { comment: 'Green Tone 90', value: '#CFFCE3' }, 15 | }, 16 | pink: { 17 | 70: { comment: 'Pink Tone 70', value: '#FFC6E7' }, 18 | 90: { comment: 'Pink Tone 90', value: '#FFE2F3' }, 19 | }, 20 | purple: { 21 | 50: { comment: 'Purple Tone 50', value: '#B37DFF' }, 22 | 70: { comment: 'Purple Tone 70', value: '#D9BEFF' }, 23 | 90: { comment: 'Purple Tone 90', value: '#F0E5FF' }, 24 | }, 25 | yellow: { 26 | 50: { comment: 'Yellow Tone 50', value: '#FFE959' }, 27 | 70: { comment: 'Yellow Tone 70', value: '#FFF29B' }, 28 | 90: { comment: 'Yellow Tone 90', value: '#FFF8C5' }, 29 | }, 30 | }, 31 | }, 32 | } 33 | -------------------------------------------------------------------------------- /packages/design-tokens/tokens/color/feedback.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | color: { 3 | feedback: { 4 | comment: 5 | 'Feedback colors used for danger/error, warning and success states.', 6 | 7 | danger: { 8 | 50: { comment: 'Tone 50', value: '#FF5454' }, 9 | 90: { comment: 'Tone 90', value: '#FFD6D6' }, 10 | }, 11 | error: { 12 | 50: { comment: 'Tone 50', value: '{color.feedback.danger.50.value}' }, 13 | 90: { comment: 'Tone 90', value: '{color.feedback.danger.90.value}' }, 14 | }, 15 | success: { 16 | 50: { comment: 'Tone 50', value: '#3CD39D' }, 17 | 90: { comment: 'Tone 90', value: '#CFFCE3' }, 18 | }, 19 | warning: { 20 | 50: { comment: 'Tone 50', value: '#FF9F69' }, 21 | 90: { comment: 'Tone 90', value: '#FFEAD1' }, 22 | }, 23 | }, 24 | }, 25 | } 26 | -------------------------------------------------------------------------------- /packages/design-tokens/tokens/color/stroke.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | color: { 3 | stroke: { 4 | comment: 'Colors for various strokes.', 5 | 6 | separator: { 7 | comment: 'Used for separators', 8 | 9 | value: '{color.base.grey.70.value}', 10 | }, 11 | }, 12 | }, 13 | } 14 | -------------------------------------------------------------------------------- /packages/design-tokens/tokens/size/border.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | size: { 3 | border: { 4 | 1: { value: 1 }, 5 | 2: { value: 2 }, 6 | }, 7 | }, 8 | } 9 | -------------------------------------------------------------------------------- /packages/design-tokens/tokens/size/radius.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | size: { 3 | radius: { 4 | 0: { 5 | value: 0, 6 | }, 7 | 8 | xs: { 9 | value: 4, 10 | }, 11 | 12 | s: { 13 | value: 8, 14 | }, 15 | 16 | m: { 17 | value: 16, 18 | }, 19 | 20 | rounded: { 21 | value: 9999, 22 | }, 23 | }, 24 | }, 25 | } 26 | -------------------------------------------------------------------------------- /packages/design-tokens/tokens/size/spacing.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | size: { 3 | spacing: { 4 | 0: { 5 | value: 0, 6 | }, 7 | 8 | px: { 9 | value: 1, 10 | }, 11 | 12 | quarter: { 13 | value: 2, 14 | }, 15 | 16 | half: { 17 | value: 4, 18 | }, 19 | 20 | 1: { 21 | value: 8, 22 | }, 23 | 24 | 2: { 25 | value: 16, 26 | }, 27 | 28 | 3: { 29 | value: 24, 30 | }, 31 | 32 | 4: { 33 | value: 32, 34 | }, 35 | 36 | 5: { 37 | value: 40, 38 | }, 39 | 40 | 6: { 41 | value: 48, 42 | }, 43 | 44 | 7: { 45 | value: 56, 46 | }, 47 | 48 | 8: { 49 | value: 64, 50 | }, 51 | 52 | 9: { 53 | value: 72, 54 | }, 55 | 56 | 10: { 57 | value: 80, 58 | }, 59 | 60 | 11: { 61 | value: 88, 62 | }, 63 | 64 | 12: { 65 | value: 96, 66 | }, 67 | 68 | 13: { 69 | value: 104, 70 | }, 71 | 72 | 14: { 73 | value: 112, 74 | }, 75 | 76 | 15: { 77 | value: 120, 78 | }, 79 | 80 | 16: { 81 | value: 128, 82 | }, 83 | 84 | 17: { 85 | value: 136, 86 | }, 87 | 88 | 18: { 89 | value: 144, 90 | }, 91 | 92 | 19: { 93 | value: 152, 94 | }, 95 | 96 | 20: { 97 | value: 160, 98 | }, 99 | }, 100 | }, 101 | } 102 | -------------------------------------------------------------------------------- /packages/design-tokens/tokens/style-and-decoration.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | decoration: { 3 | text: { 4 | none: { 5 | comment: 'Display a text without any decoration.', 6 | 7 | value: 'none', 8 | }, 9 | 10 | striked: { 11 | comment: 'Display a striked text.', 12 | 13 | value: 'line-through', 14 | }, 15 | 16 | underline: { 17 | comment: 'Display an underlined text.', 18 | 19 | value: 'underline', 20 | }, 21 | 22 | wavy: { 23 | comment: 'Display a text with a wavy underline.', 24 | 25 | value: 'wavy', 26 | }, 27 | }, 28 | }, 29 | 30 | style: { 31 | text: { 32 | default: { 33 | comment: 'Display a text normally.', 34 | 35 | value: 'normal', 36 | }, 37 | 38 | italic: { 39 | comment: 'Display a text in italic.', 40 | 41 | value: 'italic', 42 | }, 43 | 44 | placeholder: { 45 | comment: 'Text style for placeholders.', 46 | 47 | value: '{style.text.default.value}', 48 | }, 49 | }, 50 | }, 51 | } 52 | -------------------------------------------------------------------------------- /packages/design-tokens/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "composite": false, 5 | "noEmit": false 6 | }, 7 | "include": ["src/**/*.ts"], 8 | "files": [], 9 | "references": [] 10 | } 11 | -------------------------------------------------------------------------------- /packages/design-tokens/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "isolatedModules": true, 6 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 7 | "module": "ESNext", 8 | "moduleResolution": "bundler", 9 | "outDir": "./dist", 10 | "paths": { 11 | "@/*": ["./src/*"] 12 | }, 13 | "rootDir": "./src", 14 | "target": "ESNext", 15 | "useDefineForClassFields": true 16 | }, 17 | "include": ["src/**/*.ts"], 18 | "files": [], 19 | "references": [] 20 | } 21 | -------------------------------------------------------------------------------- /packages/fractal/.gitignore: -------------------------------------------------------------------------------- 1 | .vercel 2 | .env*.local 3 | dist 4 | storybook-static 5 | vite.config.ts.timestamp* 6 | 7 | # Fonts are not open-source 8 | public/fonts/PolySans* 9 | -------------------------------------------------------------------------------- /packages/fractal/.prettierrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = require('@snowball-tech/prettier-config/.prettierrc-tailwind') 2 | -------------------------------------------------------------------------------- /packages/fractal/.storybook/DocumentationTemplate.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | Controls, 3 | Description, 4 | Meta, 5 | Primary, 6 | Subtitle, 7 | Title, 8 | } from '@storybook/blocks' 9 | 10 | 11 | 12 | 13 | 14 | <Subtitle /> 15 | 16 | <Description /> 17 | 18 | --- 19 | 20 | <Primary /> 21 | 22 | ## Props 23 | 24 | The component accepts the following props: 25 | 26 | <Controls /> 27 | -------------------------------------------------------------------------------- /packages/fractal/.storybook/manager.ts: -------------------------------------------------------------------------------- 1 | import { addons } from '@storybook/manager-api' 2 | 3 | import fractalTheme from './theme' 4 | 5 | addons.setConfig({ 6 | sidebar: { 7 | filters: { 8 | patterns: (item) => !item.name.startsWith('Interactive'), 9 | }, 10 | }, 11 | theme: fractalTheme, 12 | }) 13 | -------------------------------------------------------------------------------- /packages/fractal/.storybook/preview.tsx: -------------------------------------------------------------------------------- 1 | import type { Preview } from '@storybook/react' 2 | 3 | import { 4 | ColorBaseBlack, 5 | ColorBaseWhite, 6 | } from '@snowball-tech/design-tokens/dist/web/typescript/design-tokens' 7 | import isChromatic from 'chromatic/isChromatic' 8 | 9 | import DocumentationTemplate from './DocumentationTemplate.mdx' 10 | import fractalTheme from './theme' 11 | 12 | import '../src/styles/global.css' 13 | 14 | const preview: Preview = { 15 | decorators: isChromatic() 16 | ? [(storyFunction) => <div className="p-2">{storyFunction()}</div>] 17 | : [], 18 | 19 | parameters: { 20 | actions: { argTypesRegex: '^on[A-Z].*' }, 21 | 22 | controls: { 23 | expanded: true, 24 | hideNoControlsWarning: true, 25 | matchers: { 26 | color: /(background|color)$/i, 27 | date: /Date$/, 28 | }, 29 | }, 30 | 31 | docs: { 32 | argTypes: { 33 | sort: 'requiredFirst', 34 | }, 35 | controls: { 36 | sort: 'requiredFirst', 37 | }, 38 | page: DocumentationTemplate, 39 | theme: { 40 | ...fractalTheme, 41 | 42 | textColor: ColorBaseBlack, 43 | textInverseColor: ColorBaseWhite, 44 | }, 45 | }, 46 | 47 | options: { 48 | storySort: { 49 | method: 'alphabetical', 50 | order: [ 51 | 'Fractal', 52 | 'Atoms', 53 | 'Molecules', 54 | 'Organisms', 55 | 'Templates', 56 | 'Pages', 57 | '[Work In Progress]', 58 | ], 59 | }, 60 | }, 61 | 62 | pseudo: { 63 | rootSelector: 'body', 64 | }, 65 | 66 | tags: ['autodocs'], 67 | }, 68 | } 69 | 70 | export default preview 71 | -------------------------------------------------------------------------------- /packages/fractal/.storybook/theme.ts: -------------------------------------------------------------------------------- 1 | import type { ThemeVars } from '@storybook/theming' 2 | 3 | import { 4 | ColorBaseBlack as Black, 5 | ColorBackgroundBodyLight as ContentBackgroundColor, 6 | FontFamilyNormal as FontFamily, 7 | ColorBrandPrimary as PrimaryColor, 8 | SizeRadiusM as Radius, 9 | ColorBrandSecondary as SecondaryColor, 10 | ColorBaseWhite as White, 11 | } from '@snowball-tech/design-tokens/dist/web/typescript/design-tokens' 12 | import { create } from '@storybook/theming/create' 13 | 14 | const SidebarBackgroundColor = Black 15 | const TopbarBackgroundColor = PrimaryColor 16 | 17 | const theme: ThemeVars = create({ 18 | base: 'light', 19 | 20 | brandImage: '/images/logo_pink_white.png', 21 | brandTarget: '_blank', 22 | brandTitle: 'Fractal', 23 | brandUrl: 'https://snowball.xyz', 24 | 25 | appBg: SidebarBackgroundColor, 26 | appContentBg: ContentBackgroundColor, 27 | barBg: TopbarBackgroundColor, 28 | fontBase: FontFamily, 29 | 30 | barTextColor: Black, 31 | textColor: Black, 32 | textInverseColor: White, 33 | 34 | appBorderColor: Black, 35 | appBorderRadius: Number.parseInt(Radius, 10), 36 | 37 | colorPrimary: PrimaryColor, 38 | colorSecondary: SecondaryColor, 39 | }) 40 | 41 | export default theme 42 | -------------------------------------------------------------------------------- /packages/fractal/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line no-restricted-exports, import/no-relative-packages 2 | export { default } from '../../eslint.config.mjs' 3 | -------------------------------------------------------------------------------- /packages/fractal/global.d.ts: -------------------------------------------------------------------------------- 1 | declare module '@snowball-tech/design-tokens/dist/web/typescript/constants' { 2 | export const breakpoints: { 3 | xxs: 'xxs' 4 | 5 | xs: 'xs' 6 | 7 | sm: 'sm' 8 | 9 | md: 'md' 10 | 11 | lg: 'lg' 12 | 13 | xl: 'xl' 14 | 15 | xxl: 'xxl' 16 | } 17 | } 18 | 19 | declare namespace JSX { 20 | interface IntrinsicElements { 21 | 'em-emoji': { 22 | fallback?: string 23 | id?: string 24 | native?: string 25 | set?: 'apple' | 'facebook' | 'google' | 'native' | 'twitter' 26 | shortcodes?: string 27 | size?: number | string 28 | skin?: number | string 29 | } & React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement> 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/fractal/index.ts: -------------------------------------------------------------------------------- 1 | export * from './src/components/index.js' 2 | -------------------------------------------------------------------------------- /packages/fractal/postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | 'postcss-import': {}, 4 | 5 | tailwindcss: {}, 6 | 7 | ...(process.env.NODE_ENV === 'development' 8 | ? {} 9 | : { '@csstools/postcss-cascade-layers': { onImportLayerRule: 'warn' } }), 10 | 11 | 'postcss-preset-env': { 12 | autoprefixer: { 13 | flexbox: 'no-2009', 14 | }, 15 | features: { 16 | 'custom-properties': false, 17 | }, 18 | stage: 3, 19 | }, 20 | 21 | 'postcss-flexbugs-fixes': {}, 22 | 23 | 'postcss-logical': {}, 24 | }, 25 | } 26 | -------------------------------------------------------------------------------- /packages/fractal/public/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snowball-tech/fractal/937cb391854d855c58a5421ad81bb989814f5c2a/packages/fractal/public/android-chrome-192x192.png -------------------------------------------------------------------------------- /packages/fractal/public/android-chrome-384x384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snowball-tech/fractal/937cb391854d855c58a5421ad81bb989814f5c2a/packages/fractal/public/android-chrome-384x384.png -------------------------------------------------------------------------------- /packages/fractal/public/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snowball-tech/fractal/937cb391854d855c58a5421ad81bb989814f5c2a/packages/fractal/public/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /packages/fractal/public/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snowball-tech/fractal/937cb391854d855c58a5421ad81bb989814f5c2a/packages/fractal/public/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /packages/fractal/public/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snowball-tech/fractal/937cb391854d855c58a5421ad81bb989814f5c2a/packages/fractal/public/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /packages/fractal/public/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snowball-tech/fractal/937cb391854d855c58a5421ad81bb989814f5c2a/packages/fractal/public/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /packages/fractal/public/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snowball-tech/fractal/937cb391854d855c58a5421ad81bb989814f5c2a/packages/fractal/public/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /packages/fractal/public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snowball-tech/fractal/937cb391854d855c58a5421ad81bb989814f5c2a/packages/fractal/public/apple-touch-icon.png -------------------------------------------------------------------------------- /packages/fractal/public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snowball-tech/fractal/937cb391854d855c58a5421ad81bb989814f5c2a/packages/fractal/public/favicon-16x16.png -------------------------------------------------------------------------------- /packages/fractal/public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snowball-tech/fractal/937cb391854d855c58a5421ad81bb989814f5c2a/packages/fractal/public/favicon-32x32.png -------------------------------------------------------------------------------- /packages/fractal/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snowball-tech/fractal/937cb391854d855c58a5421ad81bb989814f5c2a/packages/fractal/public/favicon.ico -------------------------------------------------------------------------------- /packages/fractal/public/images/cover_fractal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snowball-tech/fractal/937cb391854d855c58a5421ad81bb989814f5c2a/packages/fractal/public/images/cover_fractal.png -------------------------------------------------------------------------------- /packages/fractal/public/images/cover_fractal_old.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snowball-tech/fractal/937cb391854d855c58a5421ad81bb989814f5c2a/packages/fractal/public/images/cover_fractal_old.png -------------------------------------------------------------------------------- /packages/fractal/public/images/logo_fractal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snowball-tech/fractal/937cb391854d855c58a5421ad81bb989814f5c2a/packages/fractal/public/images/logo_fractal.png -------------------------------------------------------------------------------- /packages/fractal/public/images/logo_pink_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snowball-tech/fractal/937cb391854d855c58a5421ad81bb989814f5c2a/packages/fractal/public/images/logo_pink_black.png -------------------------------------------------------------------------------- /packages/fractal/public/images/logo_pink_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snowball-tech/fractal/937cb391854d855c58a5421ad81bb989814f5c2a/packages/fractal/public/images/logo_pink_white.png -------------------------------------------------------------------------------- /packages/fractal/public/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Fractal", 3 | "short_name": "Fractal", 4 | "icons": [ 5 | { 6 | "src": "/android-chrome-192x192.png?v=1", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/android-chrome-384x384.png?v=1", 12 | "sizes": "384x384", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#ffffff", 17 | "background_color": "#ffffff", 18 | "start_url": "https://fractal.snowball.xyz", 19 | "display": "standalone" 20 | } 21 | -------------------------------------------------------------------------------- /packages/fractal/scripts/build-stats.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Enable globstar expansion to make glob path below work. 4 | shopt -s globstar 5 | # And also enable extended glob syntax to exclude some files below. 6 | shopt -s extglob 7 | 8 | # shellcheck disable=SC2181 9 | 10 | # Compute and display stats about the built Fractal package 11 | 12 | # shellcheck disable=SC1090 13 | source "$(dirname "$0")/../../../scripts/colors.sh" 14 | 15 | if [ ! -d "./dist" ]; then 16 | bold_error "The 'dist' directory does not exist. Please build the Fractal package first:" 17 | 18 | echo "" 19 | echo -e "\t$(reverse "yarn build")" 20 | echo "" 21 | 22 | exit 1 23 | fi 24 | 25 | # Total size 26 | totalSize=$(du -sh ./dist | awk '{print $1}') 27 | jsSize=$(du -ch ./dist/**/!(chunk).js | grep total$ | awk '{print $1}') 28 | cssSize=$(du -ch ./dist/**/*.css | grep total$ | awk '{print $1}') 29 | mapsSize=$(du -ch ./dist/**/*.map | grep total$ | awk '{print $1}') 30 | typesSize=$(du -ch ./dist/**/*.d.ts | grep total$ | awk '{print $1}') 31 | chunksSize=$(du -ch ./dist/*.js | grep total$ | awk '{print $1}') 32 | bundleSize=$(du -ch ./dist/*.js ./dist/*.css | grep total$ | awk '{print $1}') 33 | 34 | echo "" 35 | echo -e "$(bold "Total size"): $(display_number "$totalSize")" 36 | echo -e "\t$(underline "Package JS files"): $(display_number "$jsSize")" 37 | echo -e "\t$(underline "TypeScript typings"): $(display_number "$typesSize")" 38 | echo -e "\t$(underline "Maps"): $(display_number "$mapsSize")" 39 | echo "" 40 | 41 | echo -e "$(bold "Bundle size"): $(display_number "$bundleSize")" 42 | echo -e "\t$(underline "Chunks"): $(display_number "$chunksSize")" 43 | echo -e "\t$(underline "CSS"): $(display_number "$cssSize")" 44 | echo "" 45 | -------------------------------------------------------------------------------- /packages/fractal/scripts/transform-icons-imports.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import fs from 'fs-extra' 4 | import { globSync } from 'glob' 5 | 6 | // eslint-disable-next-line import/extensions 7 | import _ from 'lodash/fp.js' 8 | 9 | function kebabToPascalCase(string_) { 10 | return string_ 11 | .split('-') 12 | .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) 13 | .join('') 14 | } 15 | 16 | // Fonction pour transformer les imports 17 | function transformImports(filePath) { 18 | const content = fs.readFileSync(filePath, 'utf8') 19 | const newContent = content.replaceAll( 20 | /import\s+(\w+)\s+from\s+'@iconscout\/react-un(?:icons\/){2}uil-([\w-]+)'/g, 21 | (match, p1, p2) => { 22 | const pascalCaseName = kebabToPascalCase(p2) 23 | 24 | return `import { Uil${pascalCaseName} as ${p1} } from '@tooni/iconscout-unicons-react'` 25 | }, 26 | ) 27 | 28 | if (newContent !== content) { 29 | fs.writeFileSync(filePath, newContent, 'utf8') 30 | console.log(`Updated imports in: ${filePath}`) 31 | } 32 | } 33 | 34 | try { 35 | const files = globSync(`src/**/*.tsx`) 36 | 37 | if (_.isEmpty(files)) { 38 | console.log('No file found.') 39 | } else { 40 | files.forEach((filePath) => transformImports(filePath)) 41 | console.log('All imports have been updated.') 42 | } 43 | } catch (error) { 44 | console.error('Unable to find TSX file using icons:', error) 45 | } 46 | -------------------------------------------------------------------------------- /packages/fractal/src/ThemeProvider.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { type ReactNode, createContext } from 'react' 4 | 5 | import { DEFAULT_THEME, Themes } from './constants' 6 | 7 | export const ThemeContext = createContext<{ 8 | theme: Themes 9 | }>({ theme: DEFAULT_THEME }) 10 | 11 | type Props = { 12 | children: ReactNode 13 | theme: Themes 14 | } 15 | export default function ThemeProvider({ 16 | children, 17 | theme = DEFAULT_THEME, 18 | }: Props) { 19 | return ( 20 | <ThemeContext.Provider value={{ theme }}>{children}</ThemeContext.Provider> 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Autocomplete/Autocomplete.constants.ts: -------------------------------------------------------------------------------- 1 | export const GROUP_NAME = 'autocomplete' 2 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Autocomplete/Autocomplete.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Subtitle, 8 | Title, 9 | } from '@storybook/blocks' 10 | 11 | import * as AutocompleteStories from './Autocomplete.stories' 12 | 13 | <Meta of={AutocompleteStories} /> 14 | 15 | <Title /> 16 | 17 | <Subtitle /> 18 | 19 | <Description /> 20 | 21 | --- 22 | 23 | ## Props 24 | 25 | On top of all the props, attributes and event handler you can set on a 26 | `<Dropdown />` component (and thus a `<div />` element), the component accepts 27 | the following props: 28 | 29 | <ArgTypes /> 30 | 31 | ## Playground 32 | 33 | <Primary /> 34 | 35 | <Controls /> 36 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Autocomplete/AutocompleteEmpty.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Subtitle, 8 | Title, 9 | } from '@storybook/blocks' 10 | 11 | import * as AutocompleteEmptyStories from './AutocompleteEmpty.stories' 12 | 13 | <Meta of={AutocompleteEmptyStories} /> 14 | 15 | <Title /> 16 | 17 | <Subtitle /> 18 | 19 | <Description /> 20 | 21 | --- 22 | 23 | ## Props 24 | 25 | On top of all the props, attributes and event handler you can set on a 26 | `<div />` element, the component accepts the following props: 27 | 28 | <ArgTypes /> 29 | 30 | ## Playground 31 | 32 | <Primary /> 33 | 34 | <Controls /> 35 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Autocomplete/AutocompleteEmpty.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react' 2 | 3 | import type { ComponentProps } from 'react' 4 | 5 | import Autocomplete from './Autocomplete' 6 | import AutocompleteEmpty from './AutocompleteEmpty' 7 | 8 | type AutocompleteEmptyProps = ComponentProps<typeof AutocompleteEmpty> 9 | 10 | const meta: Meta<AutocompleteEmptyProps> = { 11 | args: { 12 | children: 'No results! Sorry about that!', 13 | }, 14 | argTypes: { 15 | children: { 16 | control: 'text', 17 | }, 18 | }, 19 | component: AutocompleteEmpty, 20 | parameters: { 21 | chromatic: { delay: 2000 }, 22 | }, 23 | 24 | title: 'Molecules/Autocomplete/AutocompleteEmpty', 25 | } satisfies Meta<AutocompleteEmptyProps> 26 | 27 | export default meta 28 | type Story = StoryObj<typeof meta> 29 | 30 | export const Playground: Story = { 31 | render: ({ children }) => ( 32 | <div className="h-[200px]"> 33 | <Autocomplete placeholder="Start typing to autocomplete"> 34 | <AutocompleteEmpty>{children}</AutocompleteEmpty> 35 | </Autocomplete> 36 | </div> 37 | ), 38 | } 39 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Autocomplete/AutocompleteEmpty.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import * as RxDropdownMenu from '@radix-ui/react-dropdown-menu' 4 | 5 | import isEmpty from 'lodash/fp/isEmpty' 6 | import omit from 'lodash/fp/omit' 7 | 8 | import { Typography } from '@/components/Typography/Typography' 9 | import { PREFIX } from '@/constants' 10 | import { cn } from '@/styles/helpers' 11 | 12 | import type { AutocompleteEmptyProps } from './Autocomplete.types' 13 | 14 | import { GROUP_NAME } from './Autocomplete.constants' 15 | 16 | /** 17 | * `AutocompleteEmpty` component is used to display an empty state inside of the 18 | * dropdown of an `Autocomplete` component. 19 | * 20 | * See https://www.radix-ui.com/primitives/docs/components/dropdown-menu#item 21 | * for more information. 22 | */ 23 | export const AutocompleteEmpty = ({ 24 | children, 25 | label, 26 | ...props 27 | }: AutocompleteEmptyProps) => { 28 | const hasChildren = Boolean(children) 29 | if (!hasChildren && isEmpty(label)) { 30 | console.warn( 31 | 'You must provide a `label` or `children` to the `AutocompleteEmpty` component', 32 | ) 33 | } 34 | 35 | return ( 36 | <RxDropdownMenu.Item 37 | aria-label={label} 38 | className={cn( 39 | `${PREFIX}-${GROUP_NAME}__empty`, 40 | 'cursor-default rounded-sm p-2 outline-none', 41 | props.className, 42 | )} 43 | title={label} 44 | onSelect={(event) => event.preventDefault()} 45 | {...omit(['className', 'disabled', 'onSelect'], props)} 46 | > 47 | <Typography element="div">{hasChildren ? children : label}</Typography> 48 | </RxDropdownMenu.Item> 49 | ) 50 | } 51 | AutocompleteEmpty.displayName = 'AutocompleteEmpty' 52 | 53 | export default AutocompleteEmpty 54 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Autocomplete/AutocompleteItem.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Subtitle, 8 | Title, 9 | } from '@storybook/blocks' 10 | 11 | import * as AutocompleteItemStories from './AutocompleteItem.stories' 12 | 13 | <Meta of={AutocompleteItemStories} /> 14 | 15 | <Title /> 16 | 17 | <Subtitle /> 18 | 19 | <Description /> 20 | 21 | --- 22 | 23 | ## Props 24 | 25 | On top of all the props, attributes and event handler you can set on a 26 | `<DropdownItem />` component (and thus a `<div />` element), the component 27 | accepts the following props: 28 | 29 | <ArgTypes /> 30 | 31 | ## Playground 32 | 33 | <Primary /> 34 | 35 | <Controls /> 36 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Autocomplete/AutocompleteItem.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react' 2 | 3 | import { action } from '@storybook/addon-actions' 4 | 5 | import type { ComponentProps } from 'react' 6 | 7 | import AutocompleteItem from '@/components/Dropdown/DropdownItem' 8 | 9 | import Autocomplete from './Autocomplete' 10 | 11 | type AutocompleteItemProps = ComponentProps<typeof AutocompleteItem> 12 | 13 | const meta: Meta<AutocompleteItemProps> = { 14 | args: { 15 | disabled: false, 16 | label: 'Jar Jar Binks', 17 | value: 'jar-jar-binks', 18 | }, 19 | component: AutocompleteItem, 20 | parameters: { 21 | chromatic: { delay: 2000 }, 22 | }, 23 | 24 | title: 'Molecules/Autocomplete/AutocompleteItem', 25 | } satisfies Meta<AutocompleteItemProps> 26 | 27 | export default meta 28 | type Story = StoryObj<typeof meta> 29 | 30 | export const Playground: Story = { 31 | render: ({ disabled = false, label = '', value = '' }) => ( 32 | <div className="h-[1200px]"> 33 | <Autocomplete 34 | placeholder="Start typing to autocomplete" 35 | onChange={action('onChange')} 36 | > 37 | <AutocompleteItem disabled={disabled} label={label} value={value} /> 38 | </Autocomplete> 39 | </div> 40 | ), 41 | } 42 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Autocomplete/AutocompleteItemGroup.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Subtitle, 8 | Title, 9 | } from '@storybook/blocks' 10 | 11 | import * as AutocompleteItemGroupStories from './AutocompleteItemGroup.stories' 12 | 13 | <Meta of={AutocompleteItemGroupStories} /> 14 | 15 | <Title /> 16 | 17 | <Subtitle /> 18 | 19 | <Description /> 20 | 21 | --- 22 | 23 | ## Props 24 | 25 | On top of all the props, attributes and event handler you can set on a 26 | `<DropdownItemGroup />` component (and thus a `<div />` element), the component 27 | accepts the following props: 28 | 29 | <ArgTypes /> 30 | 31 | ## Playground 32 | 33 | <Primary /> 34 | 35 | <Controls /> 36 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Autocomplete/AutocompleteItemSeparator.mdx: -------------------------------------------------------------------------------- 1 | import { Description, Meta, Primary, Subtitle, Title } from '@storybook/blocks' 2 | 3 | import * as AutocompleteItemSeparatortories from './AutocompleteItemSeparator.stories' 4 | 5 | <Meta of={AutocompleteItemSeparatortories} /> 6 | 7 | <Title /> 8 | 9 | <Subtitle /> 10 | 11 | <Description /> 12 | 13 | --- 14 | 15 | ## Props 16 | 17 | You can set for this component all the props, attributes and event handler you 18 | can set on a `<DropdownItemSeparator />` component (and thus a `<div />` 19 | element). 20 | 21 | ## Playground 22 | 23 | <Primary /> 24 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Autocomplete/AutocompleteItemSeparator.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react' 2 | 3 | import { action } from '@storybook/addon-actions' 4 | 5 | import type { ComponentProps } from 'react' 6 | 7 | import AutocompleteItem from '@/components/Dropdown/DropdownItem' 8 | import AutocompleteItemSeparator from '@/components/Dropdown/DropdownItemSeparator' 9 | 10 | import Autocomplete from './Autocomplete' 11 | 12 | type AutocompleteItemSeparatorProps = ComponentProps< 13 | typeof AutocompleteItemSeparator 14 | > 15 | 16 | const meta: Meta<AutocompleteItemSeparatorProps> = { 17 | component: AutocompleteItemSeparator, 18 | parameters: { 19 | chromatic: { delay: 2000 }, 20 | }, 21 | 22 | title: 'Molecules/Autocomplete/AutocompleteItemSeparator', 23 | } satisfies Meta<AutocompleteItemSeparatorProps> 24 | 25 | export default meta 26 | type Story = StoryObj<typeof meta> 27 | 28 | export const Playground: Story = { 29 | render: () => ( 30 | <div className="h-[1200px]"> 31 | <Autocomplete 32 | placeholder="Start typing to autocomplete" 33 | onChange={action('onChange')} 34 | > 35 | <AutocompleteItem label="Yoda" value="yoda" /> 36 | <AutocompleteItemSeparator /> 37 | <AutocompleteItem label="Darth Sidious" value="darth-sidious" /> 38 | </Autocomplete> 39 | </div> 40 | ), 41 | } 42 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Autocomplete/AutocompleteLoading.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Subtitle, 8 | Title, 9 | } from '@storybook/blocks' 10 | 11 | import * as AutocompleteLoadingStories from './AutocompleteLoading.stories' 12 | 13 | <Meta of={AutocompleteLoadingStories} /> 14 | 15 | <Title /> 16 | 17 | <Subtitle /> 18 | 19 | <Description /> 20 | 21 | --- 22 | 23 | ## Props 24 | 25 | On top of all the props, attributes and event handler you can set on a 26 | `<div />` element, the component accepts the following props: 27 | 28 | <ArgTypes /> 29 | 30 | ## Playground 31 | 32 | <Primary /> 33 | 34 | <Controls /> 35 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Autocomplete/AutocompleteLoading.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react' 2 | 3 | import { 4 | UilMessage as SendIcon, 5 | UilEnvelopeStar as StarIcon, 6 | } from '@tooni/iconscout-unicons-react' 7 | 8 | import type { ComponentProps } from 'react' 9 | 10 | import Autocomplete from './Autocomplete' 11 | import AutocompleteLoading from './AutocompleteLoading' 12 | 13 | type AutocompleteLoadingProps = ComponentProps<typeof AutocompleteLoading> 14 | 15 | const meta: Meta<AutocompleteLoadingProps> = { 16 | args: { 17 | children: 'Loading... please wait!', 18 | icon: 'Default', 19 | spin: true, 20 | }, 21 | argTypes: { 22 | children: { 23 | control: 'text', 24 | }, 25 | icon: { 26 | mapping: { 27 | Default: true, 28 | None: false, 29 | Send: <SendIcon />, 30 | Star: <StarIcon />, 31 | }, 32 | options: ['Default', 'None', 'Send', 'Star'], 33 | table: { type: { summary: 'ReactNode | boolean' } }, 34 | }, 35 | }, 36 | component: AutocompleteLoading, 37 | parameters: { 38 | chromatic: { delay: 2000 }, 39 | }, 40 | 41 | title: 'Molecules/Autocomplete/AutocompleteLoading', 42 | } satisfies Meta<AutocompleteLoadingProps> 43 | 44 | export default meta 45 | type Story = StoryObj<typeof meta> 46 | 47 | export const Playground: Story = { 48 | render: ({ children, icon, spin = false }) => ( 49 | <div className="h-[800px]"> 50 | <Autocomplete placeholder="Start typing to autocomplete"> 51 | <AutocompleteLoading icon={icon} spin={spin}> 52 | {children} 53 | </AutocompleteLoading> 54 | </Autocomplete> 55 | </div> 56 | ), 57 | } 58 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Autocomplete/index.ts: -------------------------------------------------------------------------------- 1 | export { default as AutocompleteItem } from '../Dropdown/DropdownItem.js' 2 | export { default as AutocompleteItemGroup } from '../Dropdown/DropdownItemGroup.js' 3 | export { default as AutocompleteItemSeparator } from '../Dropdown/DropdownItemSeparator.js' 4 | export { default as Autocomplete } from './Autocomplete.js' 5 | export type { 6 | AutocompleteEmptyProps, 7 | AutocompleteItemGroupProps, 8 | AutocompleteLoadingProps, 9 | AutocompleteProps, 10 | CombinedRefs as AutocompleteRefs, 11 | } from './Autocomplete.types.js' 12 | export { default as AutocompleteEmpty } from './AutocompleteEmpty.js' 13 | export { default as AutocompleteLoading } from './AutocompleteLoading.js' 14 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Avatar/Avatar.constants.ts: -------------------------------------------------------------------------------- 1 | import { Variants as TypographyVariants } from '@/components/Typography/Typography.constants' 2 | 3 | export const GROUP_NAME = 'avatar' 4 | 5 | export enum Sizes { 6 | // Small. 7 | S = 's', 8 | 9 | // Medium. 10 | M = 'm', 11 | 12 | // Large. 13 | L = 'l', 14 | 15 | // Extra large. 16 | XL = 'xl', 17 | 18 | // Fluid. 19 | Fluid = 'fluid', 20 | } 21 | 22 | export const DEFAULT_SIZE = Sizes.M 23 | 24 | export const sizeToTypographyVariant: Record<Sizes, `${TypographyVariants}`> = { 25 | [Sizes.S]: TypographyVariants.CaptionBold, 26 | 27 | [Sizes.M]: TypographyVariants.CaptionBold, 28 | 29 | [Sizes.L]: TypographyVariants.Body1Bold, 30 | 31 | [Sizes.XL]: TypographyVariants.Heading4, 32 | 33 | [Sizes.Fluid]: TypographyVariants.CaptionBold, 34 | } 35 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Avatar/Avatar.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Stories, 8 | Subtitle, 9 | Title, 10 | } from '@storybook/blocks' 11 | 12 | import * as AvatarStories from './Avatar.stories' 13 | 14 | <Meta of={AvatarStories} /> 15 | 16 | <Title /> 17 | 18 | <Subtitle /> 19 | 20 | <Description /> 21 | 22 | --- 23 | 24 | ## Props 25 | 26 | On top of all the props, attributes and event handler you can set on a 27 | `<div />` element, the component accepts the following props: 28 | 29 | <ArgTypes /> 30 | 31 | <Stories includePrimary={false} title="Avatars" /> 32 | 33 | ## Playground 34 | 35 | <Primary /> 36 | 37 | <Controls /> 38 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Avatar/Avatar.types.ts: -------------------------------------------------------------------------------- 1 | import type { AllHTMLAttributes, ReactNode } from 'react' 2 | 3 | import { Sizes } from './Avatar.constants' 4 | 5 | export interface AvatarProps 6 | extends Omit<AllHTMLAttributes<HTMLImageElement>, 'size'> { 7 | /** 8 | * The content of the dropdown menu of the avatar (if you want one). 9 | * 10 | * For the best result, please use the `DropdownItem`, `DropdownItemGroup`, 11 | * `DropdownItemSeparator`, `SubDropdown` or `DropdownRadioGroup` components. 12 | */ 13 | children?: ReactNode 14 | /** Indicates if the avatar menu dropdown is disabled. */ 15 | disabled?: boolean 16 | /** The URL of the image to display as the avatar. */ 17 | imageUrl?: string 18 | /** The name of the person to display as the avatar. */ 19 | name?: string 20 | /** The wanted size of the loader. */ 21 | size?: `${Sizes}` 22 | } 23 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Avatar/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | Sizes as AvatarSizes, 3 | sizeToTypographyVariant as avatarSizeToTypographyVariant, 4 | DEFAULT_SIZE as DEFAULT_AVATAR_SIZE, 5 | } from './Avatar.constants.js' 6 | export { default as Avatar } from './Avatar.js' 7 | export type { AvatarProps } from './Avatar.types.js' 8 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Badge/Badge.constants.ts: -------------------------------------------------------------------------------- 1 | export const GROUP_NAME = 'badge' 2 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Badge/Badge.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Stories, 8 | Subtitle, 9 | Title, 10 | } from '@storybook/blocks' 11 | 12 | import * as BadgeStories from './Badge.stories' 13 | 14 | <Meta of={BadgeStories} /> 15 | 16 | <Title /> 17 | 18 | <Subtitle /> 19 | 20 | <Description /> 21 | 22 | --- 23 | 24 | ## Props 25 | 26 | On top of all the props, attributes and event handler you can set on a 27 | `<div />` element, the component accepts the following props: 28 | 29 | <ArgTypes /> 30 | 31 | <Stories includePrimary={false} title="Badges" /> 32 | 33 | ## Playground 34 | 35 | <Primary /> 36 | 37 | <Controls /> 38 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Badge/Badge.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react' 2 | 3 | import type { ComponentProps } from 'react' 4 | 5 | import { Badge } from '.' 6 | 7 | type BadgeProps = ComponentProps<typeof Badge> 8 | 9 | const perVariantStoriesParameters = { 10 | controls: { 11 | include: ['count, limit = 99'], 12 | }, 13 | } 14 | 15 | const meta = { 16 | args: { 17 | count: undefined, 18 | limit: 99, 19 | }, 20 | argTypes: { 21 | count: { control: 'number' }, 22 | limit: { control: 'number' }, 23 | }, 24 | component: Badge, 25 | parameters: { 26 | docs: { 27 | subtitle: 28 | "🎈 You may notice one is missing. It's my Assisting the Elderly badge. - Russel - Up", 29 | }, 30 | }, 31 | 32 | title: 'Molecules/Badge', 33 | } satisfies Meta<BadgeProps> 34 | 35 | export default meta 36 | type Story = StoryObj<typeof meta> 37 | 38 | export const Playground: Story = {} 39 | 40 | export const Badges: Story = { 41 | parameters: { ...perVariantStoriesParameters }, 42 | render: () => ( 43 | <div className="flex flex-col gap-2"> 44 | <Badge /> 45 | <Badge count={-42} /> 46 | <Badge count={-1} /> 47 | <Badge count={0} /> 48 | <Badge count={1} /> 49 | <Badge count={42} /> 50 | <Badge count={99} /> 51 | <Badge count={100} /> 52 | <Badge count={100} limit={1000} /> 53 | <Badge count={999} limit={1000} /> 54 | <Badge count={9999} limit={1000} /> 55 | </div> 56 | ), 57 | } 58 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Badge/Badge.tsx: -------------------------------------------------------------------------------- 1 | import isEmpty from 'lodash/fp/isEmpty' 2 | import isInteger from 'lodash/fp/isInteger' 3 | import isNil from 'lodash/fp/isNil' 4 | import isNumber from 'lodash/fp/isNumber' 5 | import omit from 'lodash/fp/omit' 6 | 7 | import { Typography } from '@/components/Typography/Typography' 8 | import { PREFIX } from '@/constants' 9 | import { cn } from '@/styles/helpers' 10 | 11 | import type { BadgeProps } from './Badge.types' 12 | 13 | import { GROUP_NAME } from './Badge.constants' 14 | 15 | /** 16 | * `Badge` component displays a number in a small colored bubble. 17 | */ 18 | export const Badge = ({ count, label, limit = 99, ...props }: BadgeProps) => { 19 | let actualCount = isNumber(count) && isInteger(count) ? `${count}` : '' 20 | if ( 21 | isNumber(limit) && 22 | limit > 0 && 23 | !isEmpty(actualCount) && 24 | (count ?? 0) > limit 25 | ) { 26 | actualCount = `+${limit}` 27 | } 28 | 29 | return ( 30 | <Typography 31 | aria-label={label} 32 | className={cn( 33 | `${PREFIX}-${GROUP_NAME}`, 34 | 'inline-flex shrink-0 items-center justify-center rounded-full bg-primary', 35 | isNil(count) || !isNumber(count) || !isInteger(count) 36 | ? `${PREFIX}-${GROUP_NAME}--empty h-2 max-h-2 min-h-2 w-2 min-w-2 max-w-2 p-0` 37 | : `${PREFIX}-${GROUP_NAME}--with-count h-3 max-h-3 w-fit min-w-3 px-half py-0`, 38 | props.className, 39 | )} 40 | element="div" 41 | title={label} 42 | variant="caption-bold" 43 | {...omit(['className'], props)} 44 | > 45 | {actualCount} 46 | </Typography> 47 | ) 48 | } 49 | Badge.displayName = 'Badge' 50 | 51 | export default Badge 52 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Badge/Badge.types.ts: -------------------------------------------------------------------------------- 1 | import type { AllHTMLAttributes } from 'react' 2 | 3 | export interface BadgeProps extends AllHTMLAttributes<HTMLDivElement> { 4 | /** The number to display in the badge. */ 5 | count?: number | undefined 6 | /** 7 | * The accessible label of the badge. 8 | * 9 | * If provided, this will be used as the `aria-label` and the `title` of the 10 | * badge. 11 | */ 12 | label?: string 13 | /** The number above which we will display "+xx" */ 14 | limit?: number | undefined 15 | } 16 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Badge/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Badge } from './Badge.js' 2 | export type { BadgeProps } from './Badge.types.js' 3 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Button/Button.constants.ts: -------------------------------------------------------------------------------- 1 | export const GROUP_NAME = 'button' 2 | 3 | export enum Variants { 4 | Display = 'display', 5 | Primary = 'primary', 6 | Secondary = 'secondary', 7 | Text = 'text', 8 | } 9 | 10 | export const DEFAULT_VARIANT = Variants.Primary 11 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Button/Button.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Stories, 8 | Subtitle, 9 | Title, 10 | } from '@storybook/blocks' 11 | 12 | import * as ButtonStories from './Button.stories' 13 | 14 | <Meta of={ButtonStories} /> 15 | 16 | <Title /> 17 | 18 | <Subtitle /> 19 | 20 | <Description /> 21 | 22 | --- 23 | 24 | ## Props 25 | 26 | On top of all the props, attributes and event handler you can set on a 27 | `<button />` (or a `<a />` if you give an `href`) element, the component accepts 28 | the following props: 29 | 30 | <ArgTypes /> 31 | 32 | <Stories includePrimary={false} title="Buttons" /> 33 | 34 | ## Playground 35 | 36 | <Primary /> 37 | 38 | <Controls /> 39 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Button/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | Variants as ButtonVariants, 3 | DEFAULT_VARIANT as DEFAULT_BUTTON_VARIANT, 4 | } from './Button.constants.js' 5 | export { 6 | default as Button, 7 | variantDisabledClassNames as buttonDisabledClassNames, 8 | variantDisabledStyles as buttonDisabledStyles, 9 | variantClassNames as buttonVariantClassNames, 10 | variantStyles as buttonVariantStyles, 11 | } from './Button.js' 12 | export type { ButtonProps } from './Button.types.js' 13 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Card/Card.constants.ts: -------------------------------------------------------------------------------- 1 | export const GROUP_NAME = 'card' 2 | 3 | export enum Colors { 4 | Blue = 'blue', 5 | Error = 'error', 6 | Green = 'green', 7 | Pink = 'pink', 8 | Purple = 'purple', 9 | Success = 'success', 10 | Warning = 'warning', 11 | Yellow = 'yellow', 12 | } 13 | 14 | export const DEFAULT_COLOR = Colors.Pink 15 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Card/Card.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Stories, 8 | Subtitle, 9 | Title, 10 | } from '@storybook/blocks' 11 | 12 | import * as CardStories from './Card.stories' 13 | 14 | <Meta of={CardStories} /> 15 | 16 | <Title /> 17 | 18 | <Subtitle /> 19 | 20 | <Description /> 21 | 22 | --- 23 | 24 | ## Props 25 | 26 | On top of all the props, attributes and event handler you can set on a 27 | `<div />` element, component accepts the following props: 28 | 29 | <ArgTypes /> 30 | 31 | <Stories includePrimary={false} title="Cards" /> 32 | 33 | ## Playground 34 | 35 | <Primary /> 36 | 37 | <Controls /> 38 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Card/Card.types.ts: -------------------------------------------------------------------------------- 1 | import type { AllHTMLAttributes, ReactNode } from 'react' 2 | 3 | import { Colors } from './Card.constants' 4 | 5 | export interface CardProps extends AllHTMLAttributes<HTMLDivElement> { 6 | /** The content of the card. */ 7 | children?: ReactNode 8 | /** The background color of the card. */ 9 | color?: `${Colors}` 10 | /** Indicates if we can dismiss the card. */ 11 | dismissable?: boolean 12 | /** 13 | * The label of the small "x" dimiss button in the top right corner of the 14 | * card (if it is dismissable). 15 | */ 16 | dismissButtonLabel?: string 17 | /** 18 | * Indicate the font size of the title and the body of the card. 19 | * 1 is `body-1` and 2 is `body-2`. 20 | */ 21 | fontSize?: 1 | 2 22 | /** 23 | * An icon to display at the top of the card (to the left of the title if 24 | * there is one). 25 | */ 26 | icon?: ReactNode 27 | /** A title to display at the top of the card. */ 28 | title?: string 29 | /** Event handler called when the card is dismissed. */ 30 | onDismiss?: () => void 31 | } 32 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Card/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | Colors as CardColors, 3 | DEFAULT_COLOR as DEFAULT_CARD_COLOR, 4 | } from './Card.constants.js' 5 | export { default as Card } from './Card.js' 6 | export type { CardProps } from './Card.types.js' 7 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Confirm/Confirm.constants.ts: -------------------------------------------------------------------------------- 1 | export const GROUP_NAME = 'confirm' 2 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Confirm/Confirm.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Stories, 8 | Subtitle, 9 | Title, 10 | } from '@storybook/blocks' 11 | 12 | import * as ConfirmStories from './Confirm.stories' 13 | 14 | <Meta of={ConfirmStories} /> 15 | 16 | <Title /> 17 | 18 | <Subtitle /> 19 | 20 | <Description /> 21 | 22 | --- 23 | 24 | ## Props 25 | 26 | On top of all the props, attributes and event handler you can set on a 27 | `<Dialog />` component (and this a `<div />` element), the component accepts the 28 | following props: 29 | 30 | <ArgTypes /> 31 | 32 | <Stories includePrimary={false} title="Confirms" /> 33 | 34 | ## Playground 35 | 36 | <Primary /> 37 | 38 | <Controls /> 39 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Confirm/Confirm.types.ts: -------------------------------------------------------------------------------- 1 | import { ButtonProps } from '@/components/Button/Button.types' 2 | import { DialogProps } from '@/components/Dialog/Dialog.types' 3 | 4 | export interface ConfirmProps 5 | extends Omit<DialogProps, 'disabled' | 'dismissable' | 'modal'> { 6 | /** 7 | * The configuration of the cancel button. 8 | * 9 | * You must provide at least a `label` or a `children` prop. 10 | */ 11 | cancel: 12 | | string 13 | | Omit< 14 | ButtonProps, 15 | 'fullWidth' | 'href' | 'onClick' | 'target' | 'type' | 'variant' 16 | > 17 | /** 18 | * The configuration of the confirmation button. 19 | * 20 | * You must provide at least a `label` or a `children` prop. 21 | */ 22 | confirm: 23 | | string 24 | | Omit< 25 | ButtonProps, 26 | 'fullWidth' | 'href' | 'onClick' | 'target' | 'type' | 'variant' 27 | > 28 | /** 29 | * The event handler called when the confirm dialog is dismissed or the 30 | * "Cancel" button is pressed. 31 | */ 32 | onCancel?: () => void 33 | /** The event handler called when the "Confirm" button is pressed. */ 34 | onConfirm?: () => void 35 | } 36 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Confirm/index.ts: -------------------------------------------------------------------------------- 1 | export { Positions as ConfirmPositions } from '../Dialog/Dialog.constants.js' 2 | export { default as Confirm } from './Confirm.js' 3 | export type { ConfirmProps } from './Confirm.types.js' 4 | -------------------------------------------------------------------------------- /packages/fractal/src/components/CuteIcon/CuteIcon.constants.ts: -------------------------------------------------------------------------------- 1 | export const GROUP_NAME = 'cute-icon' 2 | 3 | export enum Colors { 4 | Blue = 'blue', 5 | Green = 'green', 6 | Pink = 'pink', 7 | Purple = 'purple', 8 | Yellow = 'yellow', 9 | } 10 | 11 | export const DEFAULT_COLOR = Colors.Pink 12 | -------------------------------------------------------------------------------- /packages/fractal/src/components/CuteIcon/CuteIcon.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Stories, 8 | Subtitle, 9 | Title, 10 | } from '@storybook/blocks' 11 | 12 | import * as CuteIconStories from './CuteIcon.stories' 13 | 14 | <Meta of={CuteIconStories} /> 15 | 16 | <Title /> 17 | 18 | <Subtitle /> 19 | 20 | <Description /> 21 | 22 | --- 23 | 24 | ## Props 25 | 26 | On top of all the props, attributes and event handler you can set on a 27 | `<div />` element, the component accepts the following props: 28 | 29 | <ArgTypes /> 30 | 31 | <Stories includePrimary={false} title="CuteIcons" /> 32 | 33 | ## Playground 34 | 35 | <Primary /> 36 | 37 | <Controls /> 38 | -------------------------------------------------------------------------------- /packages/fractal/src/components/CuteIcon/CuteIcon.tsx: -------------------------------------------------------------------------------- 1 | import omit from 'lodash/fp/omit' 2 | 3 | import { LIGHT_BG_COLORS_CLASSNAMES, PREFIX } from '@/constants' 4 | import { cn } from '@/styles/helpers' 5 | 6 | import type { CuteIconProps } from './CuteIcon.types' 7 | 8 | import { DEFAULT_COLOR, GROUP_NAME } from './CuteIcon.constants' 9 | 10 | /** 11 | * `CuteIcon` component displays an icon in a small cute colored bubble. 12 | */ 13 | export const CuteIcon = ({ 14 | color = DEFAULT_COLOR, 15 | icon, 16 | ...props 17 | }: CuteIconProps) => ( 18 | <div 19 | className={cn( 20 | `${PREFIX}-${GROUP_NAME}`, 21 | `${PREFIX}-${GROUP_NAME}--${color}`, 22 | 'inline-flex h-5 max-h-5 min-h-5 w-5 min-w-5 max-w-5 items-center justify-center rounded-full p-1', 23 | LIGHT_BG_COLORS_CLASSNAMES[color], 24 | props.className, 25 | )} 26 | {...omit(['className'], props)} 27 | > 28 | {icon} 29 | </div> 30 | ) 31 | CuteIcon.displayName = 'CuteIcon' 32 | 33 | export default CuteIcon 34 | -------------------------------------------------------------------------------- /packages/fractal/src/components/CuteIcon/CuteIcon.types.ts: -------------------------------------------------------------------------------- 1 | import type { AllHTMLAttributes, ReactNode } from 'react' 2 | 3 | import { Colors } from './CuteIcon.constants' 4 | 5 | export interface CuteIconProps extends AllHTMLAttributes<HTMLDivElement> { 6 | /** The icon to display. */ 7 | icon: ReactNode 8 | /** The color of the bubble. */ 9 | color?: `${Colors}` 10 | } 11 | -------------------------------------------------------------------------------- /packages/fractal/src/components/CuteIcon/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | Colors as CuteIconColors, 3 | DEFAULT_COLOR as DEFAULT_CUTE_ICON_COLOR, 4 | } from './CuteIcon.constants.js' 5 | export { default as CuteIcon } from './CuteIcon.js' 6 | export type { CuteIconProps } from './CuteIcon.types.js' 7 | -------------------------------------------------------------------------------- /packages/fractal/src/components/DateTimePicker/DateTimePicker.constants.ts: -------------------------------------------------------------------------------- 1 | export const GROUP_NAME = 'datetime-picker' 2 | 3 | export enum PickerVariants { 4 | SideBySide = 'side-by-side', 5 | Tabs = 'tabs', 6 | } 7 | 8 | export const DEFAULT_PICKER_VARIANT = PickerVariants.Tabs 9 | 10 | export enum TimeVariants { 11 | Clock = 'clock', 12 | } 13 | 14 | export const DEFAULT_TIME_VARIANT = TimeVariants.Clock 15 | 16 | export enum Orientations { 17 | Horizontal = 'horizontal', 18 | Responsive = 'responsive', 19 | Vertical = 'vertical', 20 | } 21 | 22 | export const DEFAULT_ORIENTATION = Orientations.Responsive 23 | export const DEFAULT_DESKTOP_ORIENTATION = Orientations.Horizontal 24 | export const DEFAULT_MOBILE_ORIENTATION = Orientations.Vertical 25 | -------------------------------------------------------------------------------- /packages/fractal/src/components/DateTimePicker/DateTimePicker.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Subtitle, 8 | Title, 9 | } from '@storybook/blocks' 10 | 11 | import * as DateTimePickerStories from './DateTimePicker.stories' 12 | 13 | <Meta of={DateTimePickerStories} /> 14 | 15 | <Title /> 16 | 17 | <Subtitle /> 18 | 19 | <Description /> 20 | 21 | --- 22 | 23 | ## Props 24 | 25 | On top of all the props, attributes and event handler you can set on a `<div />` 26 | element, the component accepts the following props: 27 | 28 | <ArgTypes /> 29 | 30 | ## Playground 31 | 32 | <Primary /> 33 | 34 | <Controls /> 35 | -------------------------------------------------------------------------------- /packages/fractal/src/components/DateTimePicker/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | Orientations as DateTimeOrientations, 3 | PickerVariants as DateTimePickerVariants, 4 | TimeVariants as DateTimeTimeVariants, 5 | DEFAULT_DESKTOP_ORIENTATION as DEFAULT_DATETIME_DESKTOP_ORIENTATION, 6 | DEFAULT_MOBILE_ORIENTATION as DEFAULT_DATETIME_MOBILE_ORIENTATION, 7 | DEFAULT_ORIENTATION as DEFAULT_DATETIME_ORIENTATION, 8 | DEFAULT_PICKER_VARIANT as DEFAULT_DATETIME_PICKER_VARIANT, 9 | DEFAULT_TIME_VARIANT as DEFAULT_DATETIME_TIME_VARIANT, 10 | } from './DateTimePicker.constants.js' 11 | export { default as DateTimePicker } from './DateTimePicker.js' 12 | export type { 13 | DateTimePickerProps, 14 | CombinedRefs as DateTimePickerRefs, 15 | } from './DateTimePicker.types.js' 16 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Dialog/Dialog.constants.ts: -------------------------------------------------------------------------------- 1 | export const GROUP_NAME = 'dialog' 2 | 3 | export enum Positions { 4 | Absolute = 'absolute', 5 | Fixed = 'fixed', 6 | } 7 | 8 | export const DEFAULT_POSITION = Positions.Fixed 9 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Dialog/Dialog.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Stories, 8 | Subtitle, 9 | Title, 10 | } from '@storybook/blocks' 11 | 12 | import * as DialogStories from './Dialog.stories' 13 | 14 | <Meta of={DialogStories} /> 15 | 16 | <Title /> 17 | 18 | <Subtitle /> 19 | 20 | <Description /> 21 | 22 | --- 23 | 24 | ## Props 25 | 26 | On top of all the props, attributes and event handler you can set on a 27 | `<div />` element, the component accepts the following props: 28 | 29 | <ArgTypes /> 30 | 31 | <Stories includePrimary={false} title="Dialogs" /> 32 | 33 | ## Playground 34 | 35 | <Primary /> 36 | 37 | <Controls /> 38 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Dialog/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | DEFAULT_POSITION as DEFAULT_DIALOG_POSITION, 3 | Positions as DialogPositions, 4 | } from './Dialog.constants.js' 5 | export { default as Dialog } from './Dialog.js' 6 | export type { DialogProps } from './Dialog.types.js' 7 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Dropdown/Dropdown.constants.ts: -------------------------------------------------------------------------------- 1 | import { PaperElevations } from '@/components/Paper' 2 | 3 | export const GROUP_NAME = 'dropdown' 4 | 5 | export const DEFAULT_ELEVATION = PaperElevations.Bordered 6 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Dropdown/Dropdown.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Stories, 8 | Subtitle, 9 | Title, 10 | } from '@storybook/blocks' 11 | 12 | import * as DropdownStories from './Dropdown.stories' 13 | 14 | <Meta of={DropdownStories} /> 15 | 16 | <Title /> 17 | 18 | <Subtitle /> 19 | 20 | <Description /> 21 | 22 | --- 23 | 24 | ## Props 25 | 26 | On top of all the props, attributes and event handler you can set on a 27 | `<div />` element, the component accepts the following props: 28 | 29 | <ArgTypes /> 30 | 31 | <Stories includePrimary={false} title="Dropdowns" /> 32 | 33 | ## Playground 34 | 35 | <Primary /> 36 | 37 | <Controls /> 38 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Dropdown/DropdownContext.ts: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { createContext } from 'react' 4 | 5 | export const DropdownContext = createContext<{ 6 | condensed: boolean 7 | disabled: boolean 8 | }>({ condensed: false, disabled: false }) 9 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Dropdown/DropdownGroupContext.ts: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { createContext } from 'react' 4 | 5 | export const DropdownGroupContext = createContext<{ 6 | condensed: boolean 7 | disabled: boolean 8 | }>({ condensed: false, disabled: false }) 9 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Dropdown/DropdownItem.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Subtitle, 8 | Title, 9 | } from '@storybook/blocks' 10 | 11 | import * as DropdownItemStories from './DropdownItem.stories' 12 | 13 | <Meta of={DropdownItemStories} /> 14 | 15 | <Title /> 16 | 17 | <Subtitle /> 18 | 19 | <Description /> 20 | 21 | --- 22 | 23 | ## Props 24 | 25 | On top of all the props, attributes and event handler you can set on a 26 | `<div />` element, the component accepts the following props: 27 | 28 | <ArgTypes /> 29 | 30 | ## Playground 31 | 32 | <Primary /> 33 | 34 | <Controls /> 35 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Dropdown/DropdownItemGroup.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Subtitle, 8 | Title, 9 | } from '@storybook/blocks' 10 | 11 | import * as DropdownItemGroupStories from './DropdownItemGroup.stories' 12 | 13 | <Meta of={DropdownItemGroupStories} /> 14 | 15 | <Title /> 16 | 17 | <Subtitle /> 18 | 19 | <Description /> 20 | 21 | --- 22 | 23 | ## Props 24 | 25 | On top of all the props, attributes and event handler you can set on a 26 | `<div />` element, the component accepts the following props: 27 | 28 | <ArgTypes /> 29 | 30 | ## Playground 31 | 32 | <Primary /> 33 | 34 | <Controls /> 35 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Dropdown/DropdownItemGroup.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react' 2 | 3 | import type { ComponentProps } from 'react' 4 | 5 | import { Dropdown, DropdownItem, DropdownItemGroup } from '.' 6 | 7 | type DropdownItemGroupProps = ComponentProps<typeof DropdownItemGroup> 8 | 9 | const meta: Meta<DropdownItemGroupProps> = { 10 | args: { 11 | condensed: false, 12 | disabled: false, 13 | label: 'Jedis', 14 | }, 15 | argTypes: { 16 | children: { 17 | control: false, 18 | table: { 19 | type: { 20 | summary: 21 | 'DropdownItem | DropdownItemSeparator | SubDropdown | DropdownRadioGroup | Array<DropdownItem | DropdownItemSeparator | SubDropdown | DropdownRadioGroup>', 22 | }, 23 | }, 24 | }, 25 | }, 26 | component: DropdownItemGroup, 27 | parameters: { 28 | chromatic: { delay: 2000 }, 29 | }, 30 | 31 | title: 'Molecules/Dropdown/DropdownItemGroup', 32 | } satisfies Meta<DropdownItemGroupProps> 33 | 34 | export default meta 35 | type Story = StoryObj<typeof meta> 36 | 37 | export const Playground: Story = { 38 | render: ({ condensed = false, disabled = false, label }) => ( 39 | <div className="h-[300px]"> 40 | <Dropdown trigger="Jedis"> 41 | <DropdownItemGroup 42 | condensed={condensed} 43 | disabled={disabled} 44 | label={label} 45 | > 46 | <DropdownItem label="Luke Skywalker" /> 47 | <DropdownItem label="Obi-Wan Kenobi" /> 48 | <DropdownItem label="Yoda" /> 49 | </DropdownItemGroup> 50 | </Dropdown> 51 | </div> 52 | ), 53 | } 54 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Dropdown/DropdownItemSeparator.mdx: -------------------------------------------------------------------------------- 1 | import { Description, Meta, Primary, Subtitle, Title } from '@storybook/blocks' 2 | 3 | import * as DropdownItemSeparatortories from './DropdownItemSeparator.stories' 4 | 5 | <Meta of={DropdownItemSeparatortories} /> 6 | 7 | <Title /> 8 | 9 | <Subtitle /> 10 | 11 | <Description /> 12 | 13 | --- 14 | 15 | ## Props 16 | 17 | You can pass any props, attributes and event handler you can set on a 18 | `<div />` element. 19 | 20 | ## Playground 21 | 22 | <Primary /> 23 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Dropdown/DropdownItemSeparator.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react' 2 | 3 | import type { ComponentProps } from 'react' 4 | 5 | import { Typography } from '@/components/Typography' 6 | 7 | import { Dropdown, DropdownItem, DropdownItemSeparator } from '.' 8 | 9 | type DropdownItemSeparatorProps = ComponentProps<typeof DropdownItemSeparator> 10 | 11 | const meta: Meta<DropdownItemSeparatorProps> = { 12 | component: DropdownItemSeparator, 13 | parameters: { 14 | chromatic: { delay: 2000 }, 15 | }, 16 | 17 | title: 'Molecules/Dropdown/DropdownItemSeparator', 18 | } satisfies Meta<DropdownItemSeparatorProps> 19 | 20 | export default meta 21 | type Story = StoryObj<typeof meta> 22 | 23 | export const Playground: Story = { 24 | render: () => ( 25 | <div className="flex h-[200px] flex-row gap-4"> 26 | <div className="space-y-2"> 27 | <Typography variant="heading-4">Normal dropdown</Typography> 28 | 29 | <Dropdown trigger="Fighter"> 30 | <DropdownItem label="Yoda" /> 31 | <DropdownItemSeparator /> 32 | <DropdownItem label="Darth Sidious" /> 33 | </Dropdown> 34 | </div> 35 | 36 | <div className="space-y-2"> 37 | <Typography variant="heading-4">Condensed dropdown</Typography> 38 | 39 | <Dropdown condensed trigger="Fighter"> 40 | <DropdownItem label="Yoda" /> 41 | <DropdownItemSeparator /> 42 | <DropdownItem label="Darth Sidious" /> 43 | </Dropdown> 44 | </div> 45 | </div> 46 | ), 47 | } 48 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Dropdown/DropdownItemSeparator.tsx: -------------------------------------------------------------------------------- 1 | import * as RxSelect from '@radix-ui/react-select' 2 | 3 | import omit from 'lodash/fp/omit' 4 | 5 | import { PREFIX } from '@/constants' 6 | import { cn } from '@/styles/helpers' 7 | 8 | import { GROUP_NAME } from './Dropdown.constants' 9 | import { DropdownItemSeparatorProps } from './Dropdown.types' 10 | 11 | /** 12 | * `ItemSeparator` component is used to display a separator between groups or 13 | * items in a dropdown. 14 | * 15 | * See https://www.radix-ui.com/primitives/docs/components/select#separator for 16 | * more information. 17 | */ 18 | export default function DropdownItemSeparator({ 19 | ...props 20 | }: DropdownItemSeparatorProps) { 21 | return ( 22 | <RxSelect.Separator 23 | className={cn( 24 | `${PREFIX}-${GROUP_NAME}__separator`, 25 | 'mx-2 my-1 h-px bg-separator', 26 | props.className, 27 | )} 28 | {...omit(['className'], props)} 29 | /> 30 | ) 31 | } 32 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Dropdown/DropdownRadioGroup.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Stories, 8 | Subtitle, 9 | Title, 10 | } from '@storybook/blocks' 11 | 12 | import * as DropdownRadioGroupStories from './DropdownRadioGroup.stories' 13 | 14 | <Meta of={DropdownRadioGroupStories} /> 15 | 16 | <Title /> 17 | 18 | <Subtitle /> 19 | 20 | <Description /> 21 | 22 | --- 23 | 24 | ## Props 25 | 26 | On top of all the props, attributes and event handler you can set on a 27 | `<InputRadioGroup />` component (and thus a `<div />` element), the component 28 | accepts the following props: 29 | 30 | <ArgTypes /> 31 | 32 | <Stories includePrimary={false} title="Radios" /> 33 | 34 | ## Playground 35 | 36 | <Primary /> 37 | 38 | <Controls /> 39 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Dropdown/DropdownRadioItem.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Subtitle, 8 | Title, 9 | } from '@storybook/blocks' 10 | 11 | import * as DropdownRadioItemStories from './DropdownRadioItem.stories' 12 | 13 | <Meta of={DropdownRadioItemStories} /> 14 | 15 | <Title /> 16 | 17 | <Subtitle /> 18 | 19 | <Description /> 20 | 21 | --- 22 | 23 | ## Props 24 | 25 | On top of all the props, attributes and event handler you can set on a 26 | `<InputRadio />` component (and thus a `<button />` element), the component 27 | accepts the following props: 28 | 29 | <ArgTypes /> 30 | 31 | ## Playground 32 | 33 | <Primary /> 34 | 35 | <Controls /> 36 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Dropdown/DropdownRadioItem.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react' 2 | 3 | import { action } from '@storybook/addon-actions' 4 | 5 | import type { ComponentProps } from 'react' 6 | 7 | import { Dropdown, DropdownRadioGroup, DropdownRadioItem } from '.' 8 | 9 | type DropdownRadioItemProps = ComponentProps<typeof DropdownRadioItem> 10 | 11 | const meta: Meta<DropdownRadioItemProps> = { 12 | args: { 13 | condensed: false, 14 | disabled: false, 15 | label: 'Jar Jar Binks', 16 | value: 'jar-jar-binks', 17 | }, 18 | argTypes: { 19 | asChild: { table: { disable: true } }, 20 | }, 21 | component: DropdownRadioItem, 22 | 23 | title: 'Molecules/Dropdown/DropdownRadio/DropdownRadioItem', 24 | } satisfies Meta<DropdownRadioItemProps> 25 | 26 | export default meta 27 | type Story = StoryObj<typeof meta> 28 | 29 | export const Playground: Story = { 30 | render: ({ condensed = false, disabled = false, label, value }) => ( 31 | <div className="h-[1200px]"> 32 | <Dropdown condensed={condensed} trigger="Click me"> 33 | <DropdownRadioGroup onValueChange={action('onValueChange')}> 34 | <DropdownRadioItem disabled={disabled} label={label} value={value} /> 35 | </DropdownRadioGroup> 36 | </Dropdown> 37 | </div> 38 | ), 39 | } 40 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Dropdown/SubDropdown.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Subtitle, 8 | Title, 9 | } from '@storybook/blocks' 10 | 11 | import * as SubDropdownStories from './SubDropdown.stories' 12 | 13 | <Meta of={SubDropdownStories} /> 14 | 15 | <Title /> 16 | 17 | <Subtitle /> 18 | 19 | <Description /> 20 | 21 | --- 22 | 23 | ## Props 24 | 25 | On top of all the props, attributes and event handler you can set on a 26 | `<div />` element, the component accepts the following props: 27 | 28 | <ArgTypes /> 29 | 30 | ## Playground 31 | 32 | <Primary /> 33 | 34 | <Controls /> 35 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Dropdown/index.ts: -------------------------------------------------------------------------------- 1 | export { Elevations as DropdownElevations } from '../Paper/Paper.constants.js' 2 | export { DEFAULT_ELEVATION as DEFAULT_DROPDOWN_ELEVATION } from './Dropdown.constants.js' 3 | export { default as Dropdown } from './Dropdown.js' 4 | export type { 5 | DropdownItemGroupProps, 6 | DropdownItemProps, 7 | DropdownItemSeparatorProps, 8 | DropdownProps, 9 | DropdownRadioGroupProps, 10 | DropdownRadioItemProps, 11 | } from './Dropdown.types.js' 12 | export { default as DropdownItem } from './DropdownItem.js' 13 | export { default as DropdownItemGroup } from './DropdownItemGroup.js' 14 | export { default as DropdownItemSeparator } from './DropdownItemSeparator.js' 15 | export { default as DropdownRadioGroup } from './DropdownRadioGroup.js' 16 | export { default as DropdownRadioItem } from './DropdownRadioItem.js' 17 | export { default as SubDropdown } from './SubDropdown.js' 18 | -------------------------------------------------------------------------------- /packages/fractal/src/components/EmojiPicker/Emoji.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Subtitle, 8 | Title, 9 | } from '@storybook/blocks' 10 | 11 | import * as EmojiStories from './Emoji.stories' 12 | 13 | <Meta of={EmojiStories} /> 14 | 15 | <Title /> 16 | 17 | <Subtitle /> 18 | 19 | <Description /> 20 | 21 | --- 22 | 23 | > **Its is important to note that to be able to use this component, you must 24 | > have loaded the `emoji-mart` data first:** 25 | > 26 | > ```js 27 | > import data from '@emoji-mart/data' 28 | > import { init } from 'emoji-mart' 29 | > 30 | > init({ data }) 31 | > ``` 32 | > 33 | > **See https://github.com/missive/emoji-mart#-emoji-component for more 34 | > information**. 35 | 36 | --- 37 | 38 | ## Props 39 | 40 | On top of all the props, attributes and event handler you can set on a 41 | `<div />` element, the component accepts the following props: 42 | 43 | <ArgTypes /> 44 | 45 | ## Playground 46 | 47 | <Primary /> 48 | 49 | <Controls /> 50 | -------------------------------------------------------------------------------- /packages/fractal/src/components/EmojiPicker/Emoji.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react' 2 | 3 | import data from '@emoji-mart/data' 4 | import { init } from 'emoji-mart' 5 | 6 | import type { ComponentProps } from 'react' 7 | 8 | import { Typography } from '@/components/Typography' 9 | 10 | import { Emoji } from '.' 11 | import { 12 | DEFAULT_EMOJIS_SET, 13 | DEFAULT_SKIN_TONE, 14 | EmojisSets, 15 | SKIN_TONES, 16 | } from './EmojiPicker.constants' 17 | 18 | init({ data }) 19 | 20 | type EmojiProps = ComponentProps<typeof Emoji> 21 | 22 | const meta = { 23 | args: { 24 | id: '+1', 25 | native: '', 26 | set: DEFAULT_EMOJIS_SET, 27 | shortCode: '', 28 | skinTone: DEFAULT_SKIN_TONE, 29 | }, 30 | argTypes: { 31 | set: { 32 | options: Object.values(EmojisSets), 33 | table: { 34 | defaultValue: { summary: DEFAULT_EMOJIS_SET }, 35 | type: { summary: Object.values(EmojisSets).join('|') }, 36 | }, 37 | }, 38 | skinTone: { 39 | options: SKIN_TONES as unknown as Array<number>, 40 | table: { 41 | defaultValue: { summary: `${DEFAULT_SKIN_TONE}` }, 42 | type: { summary: SKIN_TONES.join('|') }, 43 | }, 44 | }, 45 | }, 46 | component: Emoji, 47 | parameters: { 48 | docs: { subtitle: '💃 Everybody, do the Emoji Pop! - The Emoji Movie' }, 49 | }, 50 | 51 | title: 'Molecules/Emojis/Emoji', 52 | } satisfies Meta<EmojiProps> 53 | 54 | export default meta 55 | type Story = StoryObj<typeof meta> 56 | 57 | export const Playground: Story = { 58 | render: (arguments_) => ( 59 | <Typography variant="display-1"> 60 | <Emoji {...arguments_} /> 61 | </Typography> 62 | ), 63 | } 64 | -------------------------------------------------------------------------------- /packages/fractal/src/components/EmojiPicker/EmojiPicker.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Subtitle, 8 | Title, 9 | } from '@storybook/blocks' 10 | 11 | import * as EmojiPickerStories from './EmojiPicker.stories' 12 | 13 | <Meta of={EmojiPickerStories} /> 14 | 15 | <Title /> 16 | 17 | <Subtitle /> 18 | 19 | <Description /> 20 | 21 | --- 22 | 23 | ## Props 24 | 25 | On top of all the props, attributes and event handler you can set on a 26 | `<div />` element, the component accepts the following props: 27 | 28 | <ArgTypes /> 29 | 30 | ## Playground 31 | 32 | <Primary /> 33 | 34 | <Controls /> 35 | -------------------------------------------------------------------------------- /packages/fractal/src/components/EmojiPicker/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Emoji } from './Emoji.js' 2 | export { 3 | DEFAULT_CATEGORIES as DEFAULT_EMOJI_PICKER_CATEGORIES, 4 | DEFAULT_EMOJIS_SET as DEFAULT_EMOJI_PICKER_EMOJIS_SET, 5 | DEFAULT_VERSION as DEFAULT_EMOJI_PICKER_EMOJIS_VERSION, 6 | DEFAULT_LOCALE as DEFAULT_EMOJI_PICKER_LOCALE, 7 | DEFAULT_NAV_POSITION as DEFAULT_EMOJI_PICKER_NAV_POSITION, 8 | DEFAULT_PREVIEW_POSITION as DEFAULT_EMOJI_PICKER_PREVIEW_POSITION, 9 | DEFAULT_SEARCH_POSITION as DEFAULT_EMOJI_PICKER_SEARCH_POSITION, 10 | DEFAULT_SKIN_TONE as DEFAULT_EMOJI_PICKER_SKIN_TONE, 11 | DEFAULT_SKIN_TONE_POSITION as DEFAULT_EMOJI_PICKER_SKIN_TONE_POSITION, 12 | DEFAULT_SKIN_TONE as DEFAULT_EMOJI_SKIN_TONE, 13 | Categories as EmojiPickerCategories, 14 | EmojisSets as EmojiPickerEmojisSets, 15 | VERSIONS as EmojiPickerEmojisVersions, 16 | Locales as EmojiPickerLocales, 17 | Positions as EmojiPickerNavPositions, 18 | Positions as EmojiPickerPreviewPositions, 19 | SearchPositions as EmojiPickerSearchPositions, 20 | SkinTonePositions as EmojiPickerSkinTonePositions, 21 | SKIN_TONES as EmojiPickerSkinTones, 22 | SKIN_TONES as EmojiSkinTones, 23 | EmojisSets, 24 | SKIN_TONES as SkinTones, 25 | } from './EmojiPicker.constants.js' 26 | export { default as EmojiPicker } from './EmojiPicker.js' 27 | export type { 28 | EmojiPickerProps, 29 | EmojiProps, 30 | EmojisCategory, 31 | Emoji as EmojiType, 32 | } from './EmojiPicker.types.js' 33 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Header/Header.constants.ts: -------------------------------------------------------------------------------- 1 | export const GROUP_NAME = 'header' 2 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Header/Header.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Subtitle, 8 | Title, 9 | } from '@storybook/blocks' 10 | 11 | import * as HeaderStories from './Header.stories' 12 | 13 | <Meta of={HeaderStories} /> 14 | 15 | <Title /> 16 | 17 | <Subtitle /> 18 | 19 | <Description /> 20 | 21 | --- 22 | 23 | ## Props 24 | 25 | On top of all the props, attributes and event handler you can set on a 26 | `<div />` element, the component accepts the following props: 27 | 28 | <ArgTypes /> 29 | 30 | ## Playground 31 | 32 | <Primary /> 33 | 34 | <Controls /> 35 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Header/Header.types.ts: -------------------------------------------------------------------------------- 1 | import type { AllHTMLAttributes, MouseEvent, ReactNode } from 'react' 2 | 3 | export interface HeaderProps extends AllHTMLAttributes<HTMLDivElement> { 4 | /** The content to display in the middle of the header. */ 5 | children?: ReactNode 6 | /** The content to display on the left of the header. */ 7 | left?: ReactNode 8 | /** The content to display on the right of the header. */ 9 | right?: ReactNode 10 | /** The event handler called when the header is clicked. */ 11 | onClick?: (event: MouseEvent<HTMLDivElement>) => void 12 | } 13 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Header/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Header } from './Header.js' 2 | export type { HeaderProps } from './Header.types.js' 3 | -------------------------------------------------------------------------------- /packages/fractal/src/components/InputCheckbox/InputCheckbox.constants.ts: -------------------------------------------------------------------------------- 1 | export const GROUP_NAME = 'input-checkbox' 2 | 3 | export enum Variants { 4 | Primary = 'primary', 5 | Secondary = 'secondary', 6 | Tertiary = 'tertiary', 7 | } 8 | 9 | export const DEFAULT_VARIANT = Variants.Primary 10 | 11 | export enum Colors { 12 | Blue = 'blue', 13 | Green = 'green', 14 | Pink = 'pink', 15 | Purple = 'purple', 16 | Yellow = 'yellow', 17 | } 18 | 19 | export const DEFAULT_COLOR = Colors.Pink 20 | -------------------------------------------------------------------------------- /packages/fractal/src/components/InputCheckbox/InputCheckbox.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Stories, 8 | Subtitle, 9 | Title, 10 | } from '@storybook/blocks' 11 | 12 | import * as InputCheckboxStories from './InputCheckbox.stories' 13 | 14 | <Meta of={InputCheckboxStories} /> 15 | 16 | <Title /> 17 | 18 | <Subtitle /> 19 | 20 | <Description /> 21 | 22 | --- 23 | 24 | ## Props 25 | 26 | On top of all the props, attributes and event handler you can set on a 27 | `<button />` element, the component accepts the following props: 28 | 29 | <ArgTypes /> 30 | 31 | <Stories includePrimary={false} title="Checkboxes" /> 32 | 33 | ## Playground 34 | 35 | <Primary /> 36 | 37 | <Controls /> 38 | -------------------------------------------------------------------------------- /packages/fractal/src/components/InputCheckbox/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | DEFAULT_COLOR as DEFAULT_INPUT_CHECKBOX_COLOR, 3 | DEFAULT_VARIANT as DEFAULT_INPUT_CHECKBOX_VARIANT, 4 | Variants as InputCheckboxVariants, 5 | } from './InputCheckbox.constants.js' 6 | export { default as InputCheckbox } from './InputCheckbox.js' 7 | export type { InputCheckboxProps } from './InputCheckbox.types.js' 8 | -------------------------------------------------------------------------------- /packages/fractal/src/components/InputDate/InputDate.constants.ts: -------------------------------------------------------------------------------- 1 | export const GROUP_NAME = 'input-date' 2 | -------------------------------------------------------------------------------- /packages/fractal/src/components/InputDate/InputDate.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Subtitle, 8 | Title, 9 | } from '@storybook/blocks' 10 | 11 | import * as InputDateStories from './InputDate.stories' 12 | 13 | <Meta of={InputDateStories} /> 14 | 15 | <Title /> 16 | 17 | <Subtitle /> 18 | 19 | <Description /> 20 | 21 | --- 22 | 23 | ## Props 24 | 25 | On top of all the props, attributes and event handler you can set on a 26 | `<InputText />` component (and thus a `<input />` element), the component 27 | accepts the following props: 28 | 29 | <ArgTypes /> 30 | 31 | ## Playground 32 | 33 | <Primary /> 34 | 35 | <Controls /> 36 | -------------------------------------------------------------------------------- /packages/fractal/src/components/InputDate/index.ts: -------------------------------------------------------------------------------- 1 | export { default as InputDate } from './InputDate.js' 2 | export type { 3 | DateFormat, 4 | Descriptions, 5 | InputDateProps, 6 | CombinedRefs as InputDateRefs, 7 | Placeholders, 8 | } from './InputDate.types.js' 9 | -------------------------------------------------------------------------------- /packages/fractal/src/components/InputFile/InputFile.constants.ts: -------------------------------------------------------------------------------- 1 | export const GROUP_NAME = 'inputFile' 2 | 3 | export enum Variants { 4 | Display = 'display', 5 | Primary = 'primary', 6 | Secondary = 'secondary', 7 | Text = 'text', 8 | } 9 | 10 | export const DEFAULT_VARIANT = Variants.Primary 11 | -------------------------------------------------------------------------------- /packages/fractal/src/components/InputFile/InputFile.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Stories, 8 | Subtitle, 9 | Title, 10 | } from '@storybook/blocks' 11 | 12 | import * as InputFileStories from './InputFile.stories' 13 | 14 | <Meta of={InputFileStories} /> 15 | 16 | <Title /> 17 | 18 | <Subtitle /> 19 | 20 | <Description /> 21 | 22 | --- 23 | 24 | ## Props 25 | 26 | On top of all the props, attributes and event handler you can set on a 27 | `<input />` element, the component accepts the following props: 28 | 29 | <ArgTypes /> 30 | 31 | <Stories includePrimary={false} title="InputFiles" /> 32 | 33 | ## Playground 34 | 35 | <Primary /> 36 | 37 | <Controls /> 38 | -------------------------------------------------------------------------------- /packages/fractal/src/components/InputFile/InputFile.types.ts: -------------------------------------------------------------------------------- 1 | import type { AllHTMLAttributes } from 'react' 2 | 3 | import { ButtonProps } from '@/components/Button/Button.types' 4 | 5 | import { Variants } from './InputFile.constants' 6 | 7 | export type CombinedRefs = { 8 | fileInput: HTMLInputElement | null 9 | trigger: HTMLButtonElement | null 10 | } 11 | 12 | export interface InputFileProps 13 | extends Omit<AllHTMLAttributes<HTMLInputElement>, 'onChange'> { 14 | /** The label of the trigger of the input file. */ 15 | label: string 16 | /** Prevents the user from interacting with the input file and the trigger. */ 17 | disabled?: boolean 18 | /** The props to pass to the trigger of the input file. */ 19 | triggerProps?: Partial< 20 | Omit< 21 | ButtonProps, 22 | 'disabled' | 'href' | 'label' | 'target' | 'type' | 'variant' 23 | > 24 | > 25 | /** 26 | * The variant of the input file. 27 | * 28 | * Currently, only trigger button variants are available (and the variants 29 | * name follow the variants name of the `Button` component). 30 | */ 31 | variant?: `${Variants}` 32 | /** Event handler called when one or multiple files are selected. */ 33 | onChange?: (files: FileList | null) => void 34 | } 35 | -------------------------------------------------------------------------------- /packages/fractal/src/components/InputFile/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | DEFAULT_VARIANT as DEFAULT_INPUT_FILE_VARIANT, 3 | Variants as InputFileVariants, 4 | } from './InputFile.constants.js' 5 | export { default as InputFile } from './InputFile.js' 6 | export type { InputFileProps } from './InputFile.types.js' 7 | -------------------------------------------------------------------------------- /packages/fractal/src/components/InputPhone/InputPhone.constants.ts: -------------------------------------------------------------------------------- 1 | import getUnicodeFlagIcon from 'country-flag-icons/unicode' 2 | import { getCountries, getCountryCallingCode } from 'libphonenumber-js/max' 3 | 4 | import { CountryDetails } from './InputPhone.types' 5 | 6 | export const GROUP_NAME = 'input-phone' 7 | 8 | export const DEFAULT_COUNTRY_CODE = 'FR' 9 | 10 | export const countryByCountryCode: Record<string, CountryDetails> = {} 11 | export const supportedCountries = getCountries().map((countryCode) => { 12 | const countryDetails: CountryDetails = { 13 | countryCode, 14 | countryName: 15 | new Intl.DisplayNames(undefined, { type: 'region' }).of(countryCode) || 16 | 'Unknown', 17 | flag: getUnicodeFlagIcon(countryCode), 18 | prefix: getCountryCallingCode(countryCode), 19 | } 20 | 21 | countryByCountryCode[countryDetails.countryCode] = countryDetails 22 | 23 | return countryDetails 24 | }) 25 | -------------------------------------------------------------------------------- /packages/fractal/src/components/InputPhone/InputPhone.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Subtitle, 8 | Title, 9 | } from '@storybook/blocks' 10 | 11 | import * as InputPhoneStories from './InputPhone.stories' 12 | 13 | <Meta of={InputPhoneStories} /> 14 | 15 | <Title /> 16 | 17 | <Subtitle /> 18 | 19 | <Description /> 20 | 21 | --- 22 | 23 | ## Props 24 | 25 | On top of all the props, attributes and event handler you can set on a 26 | `<InputText />` component (and thus a `<input />` element), the component 27 | accepts the following props: 28 | 29 | <ArgTypes /> 30 | 31 | ## Playground 32 | 33 | <Primary /> 34 | 35 | <Controls /> 36 | -------------------------------------------------------------------------------- /packages/fractal/src/components/InputPhone/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | DEFAULT_COUNTRY_CODE as DEFAULT_INPUT_PHONE_COUNTRY_CODE, 3 | countryByCountryCode as inputPhoneCountryByCountryCode, 4 | supportedCountries as inputPhoneSupportedCountries, 5 | } from './InputPhone.constants.js' 6 | export { default as InputPhone } from './InputPhone.js' 7 | export type { 8 | InputPhoneProps, 9 | CombinedRefs as InputPhoneRefs, 10 | PhoneNumber, 11 | } from './InputPhone.types.js' 12 | -------------------------------------------------------------------------------- /packages/fractal/src/components/InputPinCode/InputPinCode.constants.ts: -------------------------------------------------------------------------------- 1 | export const GROUP_NAME = 'input-pincode' 2 | -------------------------------------------------------------------------------- /packages/fractal/src/components/InputPinCode/InputPinCode.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Subtitle, 8 | Title, 9 | } from '@storybook/blocks' 10 | 11 | import * as InputPinCodeStories from './InputPinCode.stories' 12 | 13 | <Meta of={InputPinCodeStories} /> 14 | 15 | <Title /> 16 | 17 | <Subtitle /> 18 | 19 | <Description /> 20 | 21 | --- 22 | 23 | ## Props 24 | 25 | On top of all the props, attributes and event handler you can set on a 26 | `<InputText />` component (and thus a `<input />` element), the component 27 | accepts the following props: 28 | 29 | <ArgTypes /> 30 | 31 | ## Playground 32 | 33 | <Primary /> 34 | 35 | <Controls /> 36 | -------------------------------------------------------------------------------- /packages/fractal/src/components/InputPinCode/index.ts: -------------------------------------------------------------------------------- 1 | export { default as InputPinCode } from './InputPinCode.js' 2 | export type { InputPinCodeProps } from './InputPinCode.types.js' 3 | -------------------------------------------------------------------------------- /packages/fractal/src/components/InputRadio/InputRadio.constants.ts: -------------------------------------------------------------------------------- 1 | export const GROUP_NAME = 'input-radio' 2 | 3 | export enum Variants { 4 | Primary = 'primary', 5 | Secondary = 'secondary', 6 | Tertiary = 'tertiary', 7 | } 8 | 9 | export const DEFAULT_VARIANT = Variants.Primary 10 | -------------------------------------------------------------------------------- /packages/fractal/src/components/InputRadio/InputRadio.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Subtitle, 8 | Title, 9 | } from '@storybook/blocks' 10 | 11 | import * as InputRadioStories from './InputRadio.stories' 12 | 13 | <Meta of={InputRadioStories} /> 14 | 15 | <Title /> 16 | 17 | <Subtitle /> 18 | 19 | <Description /> 20 | 21 | --- 22 | 23 | ## Props 24 | 25 | On top of all the props, attributes and event handler you can set on a 26 | `<button />` element, the component accepts the following props: 27 | 28 | <ArgTypes /> 29 | 30 | ## Playground 31 | 32 | <Primary /> 33 | 34 | <Controls /> 35 | -------------------------------------------------------------------------------- /packages/fractal/src/components/InputRadio/InputRadio.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react' 2 | 3 | import { action } from '@storybook/addon-actions' 4 | 5 | import type { ComponentProps } from 'react' 6 | 7 | import { InputRadio, InputRadioGroup } from '.' 8 | 9 | type InputRadioProps = ComponentProps<typeof InputRadio> 10 | 11 | const meta: Meta<{ required?: boolean } & InputRadioProps> = { 12 | args: { 13 | condensed: false, 14 | disabled: false, 15 | fullWidth: false, 16 | label: 'Jar Jar Binks', 17 | required: false, 18 | value: 'jar-jar-binks', 19 | }, 20 | argTypes: { 21 | asChild: { table: { disable: true } }, 22 | }, 23 | component: InputRadio, 24 | parameters: { 25 | docs: { subtitle: '🎶 Video killed the radio star - The Buggles' }, 26 | }, 27 | 28 | title: 'Molecules/Input/InputRadio', 29 | } satisfies Meta<{ required?: boolean } & InputRadioProps> 30 | 31 | export default meta 32 | type Story = StoryObj<typeof meta> 33 | 34 | export const Playground: Story = { 35 | render: ({ 36 | condensed = false, 37 | disabled = false, 38 | fullWidth = false, 39 | label, 40 | required = false, 41 | value, 42 | }) => ( 43 | <InputRadioGroup 44 | fullWidth={fullWidth} 45 | required={required} 46 | onValueChange={action('onValueChange')} 47 | > 48 | <InputRadio 49 | condensed={condensed} 50 | disabled={disabled} 51 | fullWidth={fullWidth} 52 | label={label} 53 | value={value} 54 | /> 55 | </InputRadioGroup> 56 | ), 57 | } 58 | -------------------------------------------------------------------------------- /packages/fractal/src/components/InputRadio/InputRadioContext.ts: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { createContext } from 'react' 4 | 5 | import { DEFAULT_VARIANT, Variants } from './InputRadio.constants' 6 | 7 | export const InputRadioContext = createContext<{ 8 | condensed: boolean 9 | disabled: boolean 10 | required: boolean 11 | variant: `${Variants}` 12 | }>({ 13 | condensed: false, 14 | disabled: false, 15 | required: false, 16 | variant: DEFAULT_VARIANT, 17 | }) 18 | -------------------------------------------------------------------------------- /packages/fractal/src/components/InputRadio/InputRadioGroup.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Stories, 8 | Subtitle, 9 | Title, 10 | } from '@storybook/blocks' 11 | 12 | import * as InputRadioGroupStories from './InputRadioGroup.stories' 13 | 14 | <Meta of={InputRadioGroupStories} /> 15 | 16 | <Title /> 17 | 18 | <Subtitle /> 19 | 20 | <Description /> 21 | 22 | --- 23 | 24 | ## Props 25 | 26 | On top of all the props, attributes and event handler you can set on a 27 | `<div />` element, the component accepts the following props: 28 | 29 | <ArgTypes /> 30 | 31 | <Stories includePrimary={false} title="Radios" /> 32 | 33 | ## Playground 34 | 35 | <Primary /> 36 | 37 | <Controls /> 38 | -------------------------------------------------------------------------------- /packages/fractal/src/components/InputRadio/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | DEFAULT_VARIANT as DEFAULT_INPUT_RADIO_VARIANT, 3 | Variants as InputRadioVariants, 4 | } from './InputRadio.constants.js' 5 | export { default as InputRadio } from './InputRadio.js' 6 | export type { 7 | InputRadioGroupProps, 8 | InputRadioProps, 9 | } from './InputRadio.types.js' 10 | export { default as InputRadioGroup } from './InputRadioGroup.js' 11 | -------------------------------------------------------------------------------- /packages/fractal/src/components/InputText/InputText.constants.ts: -------------------------------------------------------------------------------- 1 | export const GROUP_NAME = 'input-text' 2 | -------------------------------------------------------------------------------- /packages/fractal/src/components/InputText/InputText.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Stories, 8 | Subtitle, 9 | Title, 10 | } from '@storybook/blocks' 11 | 12 | import * as InputTextStories from './InputText.stories' 13 | 14 | <Meta of={InputTextStories} /> 15 | 16 | <Title /> 17 | 18 | <Subtitle /> 19 | 20 | <Description /> 21 | 22 | --- 23 | 24 | ## Props 25 | 26 | On top of all the props, attributes and event handler you can set on a 27 | `<input />` element, the component accepts the following props: 28 | 29 | <ArgTypes /> 30 | 31 | <Stories includePrimary={false} title="Text inputs" /> 32 | 33 | ## Playground 34 | 35 | <Primary /> 36 | 37 | <Controls /> 38 | -------------------------------------------------------------------------------- /packages/fractal/src/components/InputText/index.ts: -------------------------------------------------------------------------------- 1 | export { default as InputText } from './InputText.js' 2 | export type { InputTextProps } from './InputText.types.js' 3 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Loader/Loader.constants.ts: -------------------------------------------------------------------------------- 1 | export const GROUP_NAME = 'loader' 2 | 3 | export enum Sizes { 4 | // Extra Extra Small. 5 | XXS = 'xxs', 6 | 7 | // Extra Small. 8 | XS = 'xs', 9 | 10 | // Small. 11 | S = 's', 12 | 13 | // Medium. 14 | M = 'm', 15 | 16 | // Large. 17 | L = 'l', 18 | 19 | // Extra Large. 20 | XL = 'xl', 21 | } 22 | 23 | export const DEFAULT_SIZE = Sizes.M 24 | 25 | export const DURATION = '3.5s' 26 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Loader/Loader.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Stories, 8 | Subtitle, 9 | Title, 10 | } from '@storybook/blocks' 11 | 12 | import * as LoaderStories from './Loader.stories' 13 | 14 | <Meta of={LoaderStories} /> 15 | 16 | <Title /> 17 | 18 | <Subtitle /> 19 | 20 | <Description /> 21 | 22 | --- 23 | 24 | ## Props 25 | 26 | On top of all the props, attributes and event handler you can set on a 27 | `<svg />` element, the component accepts the following props: 28 | 29 | <ArgTypes /> 30 | 31 | <Stories includePrimary={false} title="Loaders" /> 32 | 33 | ## Playground 34 | 35 | <Primary /> 36 | 37 | <Controls /> 38 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Loader/Loader.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react' 2 | 3 | import type { ComponentProps } from 'react' 4 | 5 | import { Loader, LoaderSizes } from '.' 6 | import { DEFAULT_SIZE } from './Loader.constants' 7 | 8 | type LoaderProps = ComponentProps<typeof Loader> 9 | 10 | const meta = { 11 | argTypes: { 12 | size: { 13 | options: Object.values(LoaderSizes), 14 | table: { 15 | defaultValue: { summary: DEFAULT_SIZE }, 16 | type: { summary: Object.values(LoaderSizes).join('|') }, 17 | }, 18 | }, 19 | }, 20 | component: Loader, 21 | parameters: { 22 | docs: { subtitle: `👷‍♀️ Well, I can drive that loader - Ripley - Alien` }, 23 | }, 24 | 25 | title: 'Molecules/Loader', 26 | } satisfies Meta<LoaderProps> 27 | 28 | export default meta 29 | type Story = StoryObj<typeof meta> 30 | 31 | export const Playground: Story = { 32 | args: { 33 | size: DEFAULT_SIZE, 34 | }, 35 | } 36 | 37 | export const Loaders: Story = { 38 | render: () => ( 39 | <div className="flex flex-col gap-2"> 40 | <Loader size="xxs" /> 41 | <Loader size="xs" /> 42 | <Loader size="s" /> 43 | <Loader size="m" /> 44 | <Loader size="l" /> 45 | <Loader size="xl" /> 46 | </div> 47 | ), 48 | } 49 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Loader/Loader.types.ts: -------------------------------------------------------------------------------- 1 | import type { AllHTMLAttributes } from 'react' 2 | 3 | import { Sizes } from './Loader.constants' 4 | 5 | export interface LoaderProps 6 | extends Omit<AllHTMLAttributes<HTMLOrSVGElement>, 'size'> { 7 | /** 8 | * The accessible label of the loader. 9 | * 10 | * If provided, this will be used as the `aria-label` and the `title` of the 11 | * loader. 12 | */ 13 | label?: string 14 | /** The wanted size of the loader. */ 15 | size?: `${Sizes}` 16 | } 17 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Loader/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | DURATION as DEFAULT_LOADER_DURATION, 3 | DEFAULT_SIZE as DEFAULT_LOADER_SIZE, 4 | Sizes as LoaderSizes, 5 | } from './Loader.constants.js' 6 | export { default as Loader } from './Loader.js' 7 | export type { LoaderProps } from './Loader.types.js' 8 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Logo/Logo.constants.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ColorBrandPrimary, 3 | ColorTextDark, 4 | ColorTextLight, 5 | } from '@snowball-tech/design-tokens' 6 | 7 | export const GROUP_NAME = 'logo' 8 | 9 | export enum Sizes { 10 | // Small. 11 | S = 's', 12 | 13 | // Medium 14 | M = 'm', 15 | 16 | // Large. 17 | L = 'l', 18 | 19 | // Extra Large. 20 | XL = 'xl', 21 | 22 | // Fluid. 23 | Fluid = 'fluid', 24 | } 25 | 26 | export const DEFAULT_SIZE = Sizes.Fluid 27 | 28 | export const PictoColors = { 29 | dark: ColorTextDark, 30 | light: ColorTextLight, 31 | none: 'transparent', 32 | primary: ColorBrandPrimary, 33 | } 34 | 35 | export const DEFAULT_PICTO_COLOR = 'dark' 36 | 37 | export const BrandColors = { 38 | dark: ColorTextDark, 39 | light: ColorTextLight, 40 | none: 'transparent', 41 | primary: ColorBrandPrimary, 42 | } 43 | 44 | export const DEFAULT_BRAND_COLOR = 'dark' 45 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Logo/Logo.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Stories, 8 | Subtitle, 9 | Title, 10 | } from '@storybook/blocks' 11 | 12 | import * as LogoStories from './Logo.stories' 13 | 14 | <Meta of={LogoStories} /> 15 | 16 | <Title /> 17 | 18 | <Subtitle /> 19 | 20 | <Description /> 21 | 22 | --- 23 | 24 | ## Props 25 | 26 | On top of all the props, attributes and event handler you can set on a 27 | `<svg />` element, the component accepts the following props: 28 | 29 | <ArgTypes /> 30 | 31 | <Stories includePrimary={false} title="Logos" /> 32 | 33 | ## Playground 34 | 35 | <Primary /> 36 | 37 | <Controls /> 38 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Logo/Logo.types.ts: -------------------------------------------------------------------------------- 1 | import type { AllHTMLAttributes } from 'react' 2 | 3 | import { BrandColors, PictoColors, Sizes } from './Logo.constants' 4 | 5 | export interface LogoProps 6 | extends Omit<AllHTMLAttributes<HTMLOrSVGElement>, 'size'> { 7 | /** The color of the brand name text. */ 8 | brandVariant?: keyof typeof BrandColors 9 | /** The color of the picto. */ 10 | pictoVariant?: keyof typeof PictoColors 11 | /** The wanted size of the loader. */ 12 | size?: `${Sizes}` 13 | } 14 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Logo/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | DEFAULT_BRAND_COLOR as DEFAULT_LOGO_BRAND_COLOR, 3 | DEFAULT_PICTO_COLOR as DEFAULT_LOGO_PICTO_COLOR, 4 | DEFAULT_SIZE as DEFAULT_LOGO_SIZE, 5 | BrandColors as LogoBrandColors, 6 | PictoColors as LogoPictoColors, 7 | Sizes as LogoSizes, 8 | } from './Logo.constants.js' 9 | export { default as Logo } from './Logo.js' 10 | export type { LogoProps } from './Logo.types.js' 11 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Menu/Menu.constants.ts: -------------------------------------------------------------------------------- 1 | import { Elevations } from '@/components/Paper/Paper.constants' 2 | 3 | export const GROUP_NAME = 'menu' 4 | 5 | export enum Orientations { 6 | Horizontal = 'horizontal', 7 | Vertical = 'vertical', 8 | } 9 | 10 | export const DEFAULT_ORIENTATION = Orientations.Vertical 11 | 12 | export const DEFAULT_ELEVATION = Elevations.Elevated 13 | export const DEFAULT_SUB_MENU_ELEVATION = Elevations.Bordered 14 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Menu/Menu.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Stories, 8 | Subtitle, 9 | Title, 10 | } from '@storybook/blocks' 11 | 12 | import * as MenuStories from './Menu.stories' 13 | 14 | <Meta of={MenuStories} /> 15 | 16 | <Title /> 17 | 18 | <Subtitle /> 19 | 20 | <Description /> 21 | 22 | --- 23 | 24 | ## Props 25 | 26 | On top of all the props, attributes and event handler you can set on a 27 | `<div />` element, the component accepts the following props: 28 | 29 | <ArgTypes /> 30 | 31 | <Stories includePrimary={false} title="Menus" /> 32 | 33 | ## Playground 34 | 35 | <Primary /> 36 | 37 | <Controls /> 38 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Menu/MenuContext.ts: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { createContext } from 'react' 4 | 5 | import { DEFAULT_ORIENTATION, Orientations } from './Menu.constants' 6 | 7 | export const MenuContext = createContext<{ 8 | condensed: boolean 9 | disabled: boolean 10 | orientation: `${Orientations}` 11 | }>({ condensed: false, disabled: false, orientation: DEFAULT_ORIENTATION }) 12 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Menu/MenuGroupContext.ts: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { createContext } from 'react' 4 | 5 | export const MenuGroupContext = createContext<{ 6 | condensed: boolean 7 | disabled: boolean 8 | }>({ condensed: false, disabled: false }) 9 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Menu/MenuItem.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Subtitle, 8 | Title, 9 | } from '@storybook/blocks' 10 | 11 | import * as MenuItemStories from './MenuItem.stories' 12 | 13 | <Meta of={MenuItemStories} /> 14 | 15 | <Title /> 16 | 17 | <Subtitle /> 18 | 19 | <Description /> 20 | 21 | --- 22 | 23 | ## Props 24 | 25 | On top of all the props, attributes and event handler you can set on a 26 | `<div />` element, the component accepts the following props: 27 | 28 | <ArgTypes /> 29 | 30 | ## Playground 31 | 32 | <Primary /> 33 | 34 | <Controls /> 35 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Menu/MenuItemGroup.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Subtitle, 8 | Title, 9 | } from '@storybook/blocks' 10 | 11 | import * as MenuItemGroupStories from './MenuItemGroup.stories' 12 | 13 | <Meta of={MenuItemGroupStories} /> 14 | 15 | <Title /> 16 | 17 | <Subtitle /> 18 | 19 | <Description /> 20 | 21 | --- 22 | 23 | ## Props 24 | 25 | On top of all the props, attributes and event handler you can set on a 26 | `<div />` element, the component accepts the following props: 27 | 28 | <ArgTypes /> 29 | 30 | ## Playground 31 | 32 | <Primary /> 33 | 34 | <Controls /> 35 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Menu/MenuItemSeparator.mdx: -------------------------------------------------------------------------------- 1 | import { Description, Meta, Primary, Subtitle, Title } from '@storybook/blocks' 2 | 3 | import * as MenuItemSeparatortories from './MenuItemSeparator.stories' 4 | 5 | <Meta of={MenuItemSeparatortories} /> 6 | 7 | <Title /> 8 | 9 | <Subtitle /> 10 | 11 | <Description /> 12 | 13 | --- 14 | 15 | ## Props 16 | 17 | You can pass any props, attributes and event handler you can set on a 18 | `<div />` element. 19 | 20 | ## Playground 21 | 22 | <Primary /> 23 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Menu/MenuItemSeparator.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react' 2 | 3 | import type { ComponentProps } from 'react' 4 | 5 | import { Typography } from '@/components/Typography' 6 | 7 | import { Menu, MenuItem, MenuItemSeparator } from '.' 8 | 9 | type MenuItemSeparatorProps = ComponentProps<typeof MenuItemSeparator> 10 | 11 | const meta: Meta<MenuItemSeparatorProps> = { 12 | component: MenuItemSeparator, 13 | parameters: { 14 | chromatic: { delay: 2000 }, 15 | }, 16 | 17 | title: 'Molecules/Menu/MenuItemSeparator', 18 | } satisfies Meta<MenuItemSeparatorProps> 19 | 20 | export default meta 21 | type Story = StoryObj<typeof meta> 22 | 23 | export const Playground: Story = { 24 | render: () => ( 25 | <div className="flex h-[200px] flex-row gap-4"> 26 | <div className="space-y-2"> 27 | <Typography variant="heading-4">Normal menu</Typography> 28 | 29 | <Menu> 30 | <MenuItem label="Yoda" /> 31 | <MenuItemSeparator /> 32 | <MenuItem label="Darth Sidious" /> 33 | </Menu> 34 | </div> 35 | 36 | <div className="space-y-2"> 37 | <Typography variant="heading-4">Condensed menu</Typography> 38 | 39 | <Menu condensed> 40 | <MenuItem label="Yoda" /> 41 | <MenuItemSeparator /> 42 | <MenuItem label="Darth Sidious" /> 43 | </Menu> 44 | </div> 45 | </div> 46 | ), 47 | } 48 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Menu/MenuItemSeparator.tsx: -------------------------------------------------------------------------------- 1 | import * as RxSelect from '@radix-ui/react-select' 2 | 3 | import omit from 'lodash/fp/omit' 4 | 5 | import { PREFIX } from '@/constants' 6 | import { cn } from '@/styles/helpers' 7 | 8 | import { GROUP_NAME } from './Menu.constants' 9 | import { MenuItemSeparatorProps } from './Menu.types' 10 | 11 | /** 12 | * `ItemSeparator` component is used to display a separator between groups or 13 | * items in a menu. 14 | */ 15 | export default function MenuItemSeparator({ 16 | ...props 17 | }: MenuItemSeparatorProps) { 18 | return ( 19 | <RxSelect.Separator 20 | className={cn( 21 | `${PREFIX}-${GROUP_NAME}__separator`, 22 | 'mx-2 my-1 h-px bg-separator', 23 | props.className, 24 | )} 25 | {...omit(['className'], props)} 26 | /> 27 | ) 28 | } 29 | MenuItemSeparator.displayName = 'MenuItemSeparator' 30 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Menu/SubMenu.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Subtitle, 8 | Title, 9 | } from '@storybook/blocks' 10 | 11 | import * as SubMenuStories from './SubMenu.stories' 12 | 13 | <Meta of={SubMenuStories} /> 14 | 15 | <Title /> 16 | 17 | <Subtitle /> 18 | 19 | <Description /> 20 | 21 | --- 22 | 23 | ## Props 24 | 25 | On top of all the props, attributes and event handler you can set on a 26 | `<div />` element, the component accepts the following props: 27 | 28 | <ArgTypes /> 29 | 30 | ## Playground 31 | 32 | <Primary /> 33 | 34 | <Controls /> 35 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Menu/index.ts: -------------------------------------------------------------------------------- 1 | export { Elevations as MenuElevations } from '../Paper/Paper.constants.js' 2 | export { 3 | DEFAULT_ELEVATION as DEFAULT_MENU_ELEVATION, 4 | DEFAULT_ORIENTATION as DEFAULT_MENU_ORIENTATION, 5 | DEFAULT_SUB_MENU_ELEVATION as DEFAULT_MENU_SUB_MENU_ELEVATION, 6 | Orientations as MenuOrientations, 7 | } from './Menu.constants.js' 8 | export { default as Menu } from './Menu.js' 9 | export type { 10 | MenuItemGroupProps, 11 | MenuItemProps, 12 | MenuItemSeparatorProps, 13 | MenuProps, 14 | } from './Menu.types.js' 15 | export { default as MenuItem } from './MenuItem.js' 16 | export { default as MenuItemGroup } from './MenuItemGroup.js' 17 | export { default as MenuItemSeparator } from './MenuItemSeparator.js' 18 | export { default as SubMenu } from './SubMenu.js' 19 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Paper/Paper.constants.ts: -------------------------------------------------------------------------------- 1 | import type { ElementType } from 'react' 2 | 3 | export const GROUP_NAME = 'paper' 4 | 5 | export enum Elevations { 6 | Bordered = '1', 7 | Elevated = '2', 8 | Higher = '3', 9 | } 10 | 11 | export const DEFAULT_ELEVATION = Elevations.Bordered 12 | 13 | export const DEFAULT_ELEMENT: ElementType = 'div' 14 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Paper/Paper.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Stories, 8 | Subtitle, 9 | Title, 10 | } from '@storybook/blocks' 11 | 12 | import * as PaperStories from './Paper.stories' 13 | 14 | <Meta of={PaperStories} /> 15 | 16 | <Title /> 17 | 18 | <Subtitle /> 19 | 20 | <Description /> 21 | 22 | --- 23 | 24 | ## Props 25 | 26 | On top of all the props, attributes and event handler you can set on a 27 | `<div />` element, component accepts the following props: 28 | 29 | <ArgTypes /> 30 | 31 | <Stories includePrimary={false} title="Papers" /> 32 | 33 | ## Playground 34 | 35 | <Primary /> 36 | 37 | <Controls /> 38 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Paper/Paper.types.ts: -------------------------------------------------------------------------------- 1 | import type { AllHTMLAttributes, ElementType, ReactNode } from 'react' 2 | 3 | import { Themes } from '@/constants' 4 | 5 | import { Elevations } from './Paper.constants' 6 | 7 | export interface PaperProps extends AllHTMLAttributes<HTMLDivElement> { 8 | /** The content of the paper. */ 9 | children: ReactNode 10 | /** The HTML element to use to display your paper. */ 11 | element?: ElementType 12 | /** 13 | * The elevation level of the paper. 14 | * 15 | * 1 (bordered) is a non elevated bordered block 16 | * 2 (elevated) is a lightly raised (small shadow) bordered block 17 | * 3 (higher) is a raised bordered block 18 | */ 19 | elevation?: `${Elevations}` 20 | /** 21 | * Indicates to inline all styles (including resets, font, ...) or only the 22 | * needed ones. 23 | * 24 | * Only used with `inlineStyle` enabled. 25 | */ 26 | fullStyle?: boolean 27 | /** 28 | * Indicates to inline the styles instead of using Tailwind CSS classes. 29 | * 30 | * The typical usage for this is when creating HTML for an email. 31 | */ 32 | inlineStyle?: boolean 33 | /** 34 | * Force the theme of the paper. 35 | * 36 | * If none is given, it will use the one provided by the Context/Provider. 37 | */ 38 | theme?: Themes 39 | } 40 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Paper/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | DEFAULT_ELEVATION as DEFAULT_PAPER_ELEVATION, 3 | Elevations as PaperElevations, 4 | } from './Paper.constants.js' 5 | export { 6 | default as Paper, 7 | elevationClassNames as paperElevationClassNames, 8 | elevationStyles as paperElevationStyles, 9 | } from './Paper.js' 10 | export type { PaperProps } from './Paper.types.js' 11 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Popover/Popover.constants.ts: -------------------------------------------------------------------------------- 1 | import { Elevations } from '@/components/Paper/Paper.constants' 2 | 3 | export const GROUP_NAME = 'popover' 4 | 5 | export const DEFAULT_ELEVATION = Elevations.Elevated 6 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Popover/Popover.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Subtitle, 8 | Title, 9 | } from '@storybook/blocks' 10 | 11 | import * as PopoverStories from './Popover.stories' 12 | 13 | <Meta of={PopoverStories} /> 14 | 15 | <Title /> 16 | 17 | <Subtitle /> 18 | 19 | <Description /> 20 | 21 | --- 22 | 23 | ## Props 24 | 25 | On top of all the props, attributes and event handler you can set on a 26 | `<div />` element, the component accepts the following props: 27 | 28 | <ArgTypes /> 29 | 30 | ## Playground 31 | 32 | <Primary /> 33 | 34 | <Controls /> 35 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Popover/index.ts: -------------------------------------------------------------------------------- 1 | export { Elevations as PopoverElevations } from '../Paper/Paper.constants.js' 2 | export { DEFAULT_ELEVATION as DEFAULT_POPOVER_ELEVATION } from './Popover.constants.js' 3 | export { default as Popover } from './Popover.js' 4 | export type { 5 | CombinedRefs as PopoverCombinedRefs, 6 | PopoverProps, 7 | } from './Popover.types.js' 8 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Progress/Progress.constants.ts: -------------------------------------------------------------------------------- 1 | export const GROUP_NAME = 'progress' 2 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Progress/Progress.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Subtitle, 8 | Title, 9 | } from '@storybook/blocks' 10 | 11 | import * as ProgressStories from './Progress.stories' 12 | 13 | <Meta of={ProgressStories} /> 14 | 15 | <Title /> 16 | 17 | <Subtitle /> 18 | 19 | <Description /> 20 | 21 | --- 22 | 23 | ## Props 24 | 25 | On top of all the props, attributes and event handler you can set on a 26 | `<div />` element, the component accepts the following props: 27 | 28 | <ArgTypes /> 29 | 30 | ## Playground 31 | 32 | <Primary /> 33 | 34 | <Controls /> 35 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Progress/Progress.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react' 2 | 3 | import type { ComponentProps } from 'react' 4 | 5 | import { Progress } from '.' 6 | 7 | type ProgressProps = ComponentProps<typeof Progress> 8 | 9 | const meta: Meta<ProgressProps> = { 10 | args: { 11 | max: 100, 12 | value: 33, 13 | }, 14 | component: Progress, 15 | parameters: { 16 | docs: { 17 | subtitle: 18 | '🧑‍🔧 Move, move, move! This is a 54-23 in progress - 04114 - Monsters University', 19 | }, 20 | }, 21 | 22 | title: 'Molecules/Progress', 23 | } satisfies Meta<ProgressProps> 24 | 25 | export default meta 26 | type Story = StoryObj<typeof meta> 27 | 28 | export const Playground: Story = {} 29 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Progress/Progress.tsx: -------------------------------------------------------------------------------- 1 | import * as RxProgress from '@radix-ui/react-progress' 2 | 3 | import isFunction from 'lodash/fp/isFunction' 4 | import omit from 'lodash/fp/omit' 5 | 6 | import { PREFIX } from '@/constants' 7 | import { cj, cn } from '@/styles/helpers' 8 | 9 | import type { ProgressProps } from './Progress.types' 10 | 11 | import { GROUP_NAME } from './Progress.constants' 12 | 13 | /** 14 | * `Progress` component is used to display a progression to the user. 15 | * 16 | * See https://www.radix-ui.com/primitives/docs/components/progress for more 17 | * information. 18 | */ 19 | export const Progress = ({ 20 | getValueLabel, 21 | max = 100, 22 | value = 0, 23 | ...props 24 | }: ProgressProps) => ( 25 | <RxProgress.Root 26 | className={cn( 27 | `${PREFIX}-${GROUP_NAME}`, 28 | 'relative h-1 w-full max-w-full overflow-hidden rounded-full border-1 border-normal bg-highlight', 29 | props.className, 30 | )} 31 | max={max} 32 | value={value} 33 | {...(isFunction(getValueLabel) ? { getValueLabel } : {})} 34 | {...omit(['className'], props)} 35 | > 36 | <RxProgress.Indicator 37 | className={cj( 38 | `${PREFIX}-${GROUP_NAME}__indicator`, 39 | 'h-full w-full rounded-full bg-secondary transition-transform delay-100 duration-600 [transition-property:cubic-bezier(0.65,0,0.35,1)]', 40 | props.className, 41 | )} 42 | style={{ 43 | transform: `translateX(-${100 - (value / max) * 100}%)`, 44 | }} 45 | /> 46 | </RxProgress.Root> 47 | ) 48 | Progress.displayName = 'Progress' 49 | 50 | export default Progress 51 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Progress/Progress.types.ts: -------------------------------------------------------------------------------- 1 | import { AllHTMLAttributes } from 'react' 2 | 3 | export interface ProgressProps extends AllHTMLAttributes<HTMLDivElement> { 4 | /** 5 | * A function to get the accessible label text representing the current value 6 | * in a human-readable format. 7 | * 8 | * If not provided, the value label will be read as the numeric value as a 9 | * percentage of the max value. 10 | */ 11 | getValueLabel?: (value: number, max: number) => string 12 | /** The maximum progress value. */ 13 | max?: number 14 | /** The current progress value. */ 15 | value?: number 16 | } 17 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Progress/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Progress } from './Progress.js' 2 | export type { ProgressProps } from './Progress.types.js' 3 | -------------------------------------------------------------------------------- /packages/fractal/src/components/ScrollArea/ScrollArea.constants.ts: -------------------------------------------------------------------------------- 1 | export const GROUP_NAME = 'scroll-area' 2 | -------------------------------------------------------------------------------- /packages/fractal/src/components/ScrollArea/ScrollArea.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Stories, 8 | Subtitle, 9 | Title, 10 | } from '@storybook/blocks' 11 | 12 | import * as ScrollAreaStories from './ScrollArea.stories' 13 | 14 | <Meta of={ScrollAreaStories} /> 15 | 16 | <Title /> 17 | 18 | <Subtitle /> 19 | 20 | <Description /> 21 | 22 | --- 23 | 24 | ## Props 25 | 26 | On top of all the props, attributes and event handler you can set on a 27 | `<div />` element, component accepts the following props: 28 | 29 | <ArgTypes /> 30 | 31 | <Stories includePrimary={false} title="ScrollAreas" /> 32 | 33 | ## Playground 34 | 35 | <Primary /> 36 | 37 | <Controls /> 38 | -------------------------------------------------------------------------------- /packages/fractal/src/components/ScrollArea/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ScrollArea } from './ScrollArea.js' 2 | export type { ScrollAreaProps } from './ScrollArea.types.js' 3 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Select/Select.constants.ts: -------------------------------------------------------------------------------- 1 | export const GROUP_NAME = 'select' 2 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Select/Select.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Subtitle, 8 | Title, 9 | } from '@storybook/blocks' 10 | 11 | import * as SelectStories from './Select.stories' 12 | 13 | <Meta of={SelectStories} /> 14 | 15 | <Title /> 16 | 17 | <Subtitle /> 18 | 19 | <Description /> 20 | 21 | --- 22 | 23 | ## Props 24 | 25 | On top of all the props, attributes and event handler you can set on a 26 | `<button />` element, the component accepts the following props: 27 | 28 | <ArgTypes /> 29 | 30 | ## Playground 31 | 32 | <Primary /> 33 | 34 | <Controls /> 35 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Select/SelectEmpty.mdx: -------------------------------------------------------------------------------- 1 | import { Description, Meta, Primary, Subtitle, Title } from '@storybook/blocks' 2 | 3 | import * as SelectEmptyStories from './SelectEmpty.stories' 4 | 5 | <Meta of={SelectEmptyStories} /> 6 | 7 | <Title /> 8 | 9 | <Subtitle /> 10 | 11 | <Description /> 12 | 13 | --- 14 | 15 | ## Props 16 | 17 | You can pass any props, attributes and event handler you can set on a 18 | `<div />` element. 19 | 20 | ## Playground 21 | 22 | <Primary /> 23 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Select/SelectEmpty.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react' 2 | 3 | import type { ComponentProps } from 'react' 4 | 5 | import { Select, SelectEmpty } from '.' 6 | 7 | type SelectEmptyProps = ComponentProps<typeof SelectEmpty> 8 | 9 | const meta: Meta<SelectEmptyProps> = { 10 | args: { 11 | children: 'No results! Sorry about that!', 12 | }, 13 | argTypes: { 14 | children: { 15 | control: 'text', 16 | }, 17 | }, 18 | component: SelectEmpty, 19 | parameters: { 20 | chromatic: { delay: 2000 }, 21 | }, 22 | 23 | title: 'Molecules/Select/SelectEmpty', 24 | } satisfies Meta<SelectEmptyProps> 25 | 26 | export default meta 27 | type Story = StoryObj<typeof meta> 28 | 29 | export const Playground: Story = { 30 | render: ({ children }) => ( 31 | <div className="h-[200px]"> 32 | <Select placeholder="Select a value"> 33 | <SelectEmpty>{children}</SelectEmpty> 34 | </Select> 35 | </div> 36 | ), 37 | } 38 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Select/SelectGroupContext.ts: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { createContext } from 'react' 4 | 5 | export const SelectGroupContext = createContext<{ 6 | disabled: boolean 7 | }>({ disabled: false }) 8 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Select/SelectItem.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Subtitle, 8 | Title, 9 | } from '@storybook/blocks' 10 | 11 | import * as SelectItemStories from './SelectItem.stories' 12 | 13 | <Meta of={SelectItemStories} /> 14 | 15 | <Title /> 16 | 17 | <Subtitle /> 18 | 19 | <Description /> 20 | 21 | --- 22 | 23 | ## Props 24 | 25 | On top of all the props, attributes and event handler you can set on a 26 | `<div />` element, the component accepts the following props: 27 | 28 | <ArgTypes /> 29 | 30 | ## Playground 31 | 32 | <Primary /> 33 | 34 | <Controls /> 35 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Select/SelectItem.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react' 2 | 3 | import { action } from '@storybook/addon-actions' 4 | 5 | import type { ComponentProps } from 'react' 6 | 7 | import { Select, SelectItem } from '.' 8 | 9 | type SelectItemProps = ComponentProps<typeof SelectItem> 10 | 11 | const meta: Meta<SelectItemProps> = { 12 | args: { 13 | children: 'Jar Jar Binks', 14 | disabled: false, 15 | value: 'jar-jar-binks', 16 | }, 17 | argTypes: { 18 | children: { control: 'text' }, 19 | }, 20 | component: SelectItem, 21 | 22 | title: 'Molecules/Select/SelectItem', 23 | } satisfies Meta<SelectItemProps> 24 | 25 | export default meta 26 | type Story = StoryObj<typeof meta> 27 | 28 | export const Playground: Story = { 29 | render: ({ children, disabled = false, value }) => ( 30 | <div className="h-[1200px]"> 31 | <Select placeholder="Click to open" onSelect={action('onSelect')}> 32 | <SelectItem disabled={disabled} value={value}> 33 | {children} 34 | </SelectItem> 35 | </Select> 36 | </div> 37 | ), 38 | } 39 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Select/SelectItemGroup.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Subtitle, 8 | Title, 9 | } from '@storybook/blocks' 10 | 11 | import * as SelectItemGroupStories from './SelectItemGroup.stories' 12 | 13 | <Meta of={SelectItemGroupStories} /> 14 | 15 | <Title /> 16 | 17 | <Subtitle /> 18 | 19 | <Description /> 20 | 21 | --- 22 | 23 | ## Props 24 | 25 | On top of all the props, attributes and event handler you can set on a 26 | `<div />` element, the component accepts the following props: 27 | 28 | <ArgTypes /> 29 | 30 | ## Playground 31 | 32 | <Primary /> 33 | 34 | <Controls /> 35 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Select/SelectItemGroup.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react' 2 | 3 | import { action } from '@storybook/addon-actions' 4 | 5 | import type { ComponentProps } from 'react' 6 | 7 | import { Select, SelectItem, SelectItemGroup } from '.' 8 | 9 | type SelectItemGroupProps = ComponentProps<typeof SelectItemGroup> 10 | 11 | const meta: Meta<SelectItemGroupProps> = { 12 | args: { 13 | disabled: false, 14 | label: 'Jedis', 15 | }, 16 | argTypes: { 17 | children: { 18 | control: false, 19 | table: { 20 | type: { 21 | summary: 22 | 'SelectItem | SelectItemSeparator | Array<SelectItem | SelectItemSeparator>', 23 | }, 24 | }, 25 | }, 26 | }, 27 | component: SelectItemGroup, 28 | 29 | title: 'Molecules/Select/SelectItemGroup', 30 | } satisfies Meta<SelectItemGroupProps> 31 | 32 | export default meta 33 | type Story = StoryObj<typeof meta> 34 | 35 | export const Playground: Story = { 36 | render: ({ disabled = false, label }) => ( 37 | <div className="h-[1200px]"> 38 | <Select placeholder="Click to open" onSelect={action('onSelect')}> 39 | <SelectItemGroup disabled={disabled} label={label}> 40 | <SelectItem value="luke-skywalker">Luke Skywalker</SelectItem> 41 | <SelectItem value="obi-wan-kenobi">Obi-Wan Kenobi</SelectItem> 42 | <SelectItem value="yoda">Yoda</SelectItem> 43 | </SelectItemGroup> 44 | </Select> 45 | </div> 46 | ), 47 | } 48 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Select/SelectItemSeparator.mdx: -------------------------------------------------------------------------------- 1 | import { Description, Meta, Primary, Subtitle, Title } from '@storybook/blocks' 2 | 3 | import * as SelectItemSeparatorStories from './SelectItemSeparator.stories' 4 | 5 | <Meta of={SelectItemSeparatorStories} /> 6 | 7 | <Title /> 8 | 9 | <Subtitle /> 10 | 11 | <Description /> 12 | 13 | --- 14 | 15 | ## Props 16 | 17 | You can pass any props, attributes and event handler you can set on a 18 | `<div />` element. 19 | 20 | ## Playground 21 | 22 | <Primary /> 23 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Select/SelectItemSeparator.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react' 2 | 3 | import { action } from '@storybook/addon-actions' 4 | 5 | import type { ComponentProps } from 'react' 6 | 7 | import { Select, SelectItem, SelectItemSeparator } from '.' 8 | 9 | type SelectItemSeparatorProps = ComponentProps<typeof SelectItemSeparator> 10 | 11 | const meta: Meta<SelectItemSeparatorProps> = { 12 | component: SelectItemSeparator, 13 | 14 | title: 'Molecules/Select/SelectItemSeparator', 15 | } satisfies Meta<SelectItemSeparatorProps> 16 | 17 | export default meta 18 | type Story = StoryObj<typeof meta> 19 | 20 | export const Playground: Story = { 21 | render: () => ( 22 | <div className="h-[800px]"> 23 | <Select placeholder="Click to open" onSelect={action('onSelect')}> 24 | <SelectItem value="yoda">Yoda</SelectItem> 25 | <SelectItemSeparator /> 26 | <SelectItem value="darth-sidious">Darth Sidious</SelectItem> 27 | </Select> 28 | </div> 29 | ), 30 | } 31 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Select/index.ts: -------------------------------------------------------------------------------- 1 | export { default as SelectItemSeparator } from '../Dropdown/DropdownItemSeparator.js' 2 | export { default as Select } from './Select.js' 3 | export type { 4 | SelectItemGroupProps, 5 | SelectItemProps, 6 | SelectItemSeparatorProps, 7 | SelectProps, 8 | CombinedRefs as SelectRefs, 9 | } from './Select.types.js' 10 | export { default as SelectEmpty } from './SelectEmpty.js' 11 | export { default as SelectItem } from './SelectItem.js' 12 | export { default as SelectItemGroup } from './SelectItemGroup.js' 13 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Skeleton/Skeleton.constants.ts: -------------------------------------------------------------------------------- 1 | export const GROUP_NAME = 'skeleton' 2 | 3 | export enum Colors { 4 | Blue = 'blue', 5 | Green = 'green', 6 | Pink = 'pink', 7 | Purple = 'purple', 8 | Yellow = 'yellow', 9 | 10 | // Greyscale. 11 | Black = 'black', 12 | DarkGrey = 'dark-grey', 13 | Grey = 'grey', 14 | LightGrey = 'light-grey', 15 | White = 'white', 16 | } 17 | 18 | export const DEFAULT_COLOR = Colors.Grey 19 | 20 | export enum Shapes { 21 | Circle = 'circle', 22 | Rectangle = 'rectangle', 23 | RoundedRectangle = 'roundedRectangle', 24 | Square = 'square', 25 | } 26 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Skeleton/Skeleton.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Stories, 8 | Subtitle, 9 | Title, 10 | } from '@storybook/blocks' 11 | 12 | import * as SkeletonStories from './Skeleton.stories' 13 | 14 | <Meta of={SkeletonStories} /> 15 | 16 | <Title /> 17 | 18 | <Subtitle /> 19 | 20 | <Description /> 21 | 22 | --- 23 | 24 | ## Props 25 | 26 | On top of all the props, attributes and event handler you can set on a 27 | `<div />` element, the component accepts the following props: 28 | 29 | <ArgTypes /> 30 | 31 | <Stories includePrimary={false} title="Skeletons" /> 32 | 33 | ## Playground 34 | 35 | <Primary /> 36 | 37 | <Controls /> 38 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Skeleton/Skeleton.types.ts: -------------------------------------------------------------------------------- 1 | import type { AllHTMLAttributes, ReactNode } from 'react' 2 | 3 | import { Colors, Shapes } from './Skeleton.constants' 4 | 5 | export interface SkeletonProps extends AllHTMLAttributes<HTMLDivElement> { 6 | /** The shape of the skeleton. */ 7 | shape: `${Shapes}` 8 | /** The optional content you may want to display inside of the skeleton. */ 9 | children?: ReactNode 10 | /** The color of the skeleton. */ 11 | color?: `${Colors}` 12 | } 13 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Skeleton/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | DEFAULT_COLOR as DEFAULT_SKELETON_COLOR, 3 | Colors as SkeletonColors, 4 | Shapes as SkeletonShapes, 5 | } from './Skeleton.constants.js' 6 | export { default as Skeleton } from './Skeleton.js' 7 | export type { SkeletonProps } from './Skeleton.types.js' 8 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Slider/Slider.constants.ts: -------------------------------------------------------------------------------- 1 | export const GROUP_NAME = 'slider' 2 | 3 | export enum Orientations { 4 | Horizontal = 'horizontal', 5 | Vertical = 'vertical', 6 | } 7 | 8 | export const DEFAULT_ORIENTATION = Orientations.Horizontal 9 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Slider/Slider.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Subtitle, 8 | Title, 9 | } from '@storybook/blocks' 10 | 11 | import * as SliderStories from './Slider.stories' 12 | 13 | <Meta of={SliderStories} /> 14 | 15 | <Title /> 16 | 17 | <Subtitle /> 18 | 19 | <Description /> 20 | 21 | --- 22 | 23 | ## Props 24 | 25 | On top of all the props, attributes and event handler you can set on a 26 | `<div />` element, the component accepts the following props: 27 | 28 | <ArgTypes /> 29 | 30 | ## Playground 31 | 32 | <Primary /> 33 | 34 | <Controls /> 35 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Slider/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | DEFAULT_ORIENTATION as DEFAULT_SLIDER_ORIENTATION, 3 | Orientations as SliderOrientations, 4 | } from './Slider.constants.js' 5 | export { default as Slider } from './Slider.js' 6 | 7 | export type { SliderProps } from './Slider.types.js' 8 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Stepper/Stepper.constants.ts: -------------------------------------------------------------------------------- 1 | export const GROUP_NAME = 'stepper' 2 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Stepper/Stepper.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Stories, 8 | Subtitle, 9 | Title, 10 | } from '@storybook/blocks' 11 | 12 | import * as StepperStories from './Stepper.stories' 13 | 14 | <Meta of={StepperStories} /> 15 | 16 | <Title /> 17 | 18 | <Subtitle /> 19 | 20 | <Description /> 21 | 22 | --- 23 | 24 | ## Props 25 | 26 | On top of all the props, attributes and event handler you can set on a 27 | `<div />` element, the component accepts the following props: 28 | 29 | <ArgTypes /> 30 | 31 | <Stories includePrimary={false} title="Steppers" /> 32 | 33 | ## Playground 34 | 35 | <Primary /> 36 | 37 | <Controls /> 38 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Stepper/Stepper.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react' 2 | 3 | import type { ComponentProps } from 'react' 4 | 5 | import { Stepper } from '.' 6 | 7 | type StepperProps = ComponentProps<typeof Stepper> 8 | 9 | const meta: Meta<StepperProps> = { 10 | args: { 11 | current: 1, 12 | currentAs: 'step', 13 | length: 3, 14 | max: 100, 15 | value: 33, 16 | }, 17 | component: Stepper, 18 | parameters: { 19 | docs: { subtitle: '🎈 Is this step three or step five? - Russel - Up' }, 20 | }, 21 | 22 | title: 'Molecules/Stepper', 23 | } satisfies Meta<StepperProps> 24 | 25 | export default meta 26 | type Story = StoryObj<typeof meta> 27 | 28 | export const Playground: Story = {} 29 | 30 | export const Simple: Story = { 31 | render: () => <Stepper current={1} length={3} />, 32 | } 33 | 34 | export const Progress: Story = { 35 | render: () => ( 36 | <Stepper current={1} currentAs="progress" length={3} value={33} /> 37 | ), 38 | } 39 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Stepper/Stepper.types.ts: -------------------------------------------------------------------------------- 1 | import { AllHTMLAttributes } from 'react' 2 | 3 | export interface StepperProps extends AllHTMLAttributes<HTMLDivElement> { 4 | /** The number of steps. */ 5 | length: number 6 | /** The current step. */ 7 | current?: number 8 | /** 9 | * Indicates how you want to display the current step. 10 | * 11 | * - `step`: The current step will be displayed as a fixed step. 12 | * - `progress`: The current step will be displayed as a progress bar. 13 | */ 14 | currentAs?: 'progress' | 'step' 15 | /** 16 | * A function to get the accessible label text representing the current value 17 | * in a human-readable format. 18 | * 19 | * If not provided, the value label will be read as "<current>/<length>". 20 | */ 21 | getValueLabel?: (current: number, length: number) => string 22 | /** 23 | * When displaying the current step as a progress bar, the maximum value of 24 | * the progress bar. 25 | */ 26 | max?: number 27 | /** 28 | * When displaying the current step as a progress bar, the current value of 29 | * the progress bar. 30 | */ 31 | value?: number 32 | } 33 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Stepper/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Stepper } from './Stepper.js' 2 | export type { StepperProps } from './Stepper.types.js' 3 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Switch/Switch.constants.ts: -------------------------------------------------------------------------------- 1 | export const GROUP_NAME = 'switch' 2 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Switch/Switch.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Stories, 8 | Subtitle, 9 | Title, 10 | } from '@storybook/blocks' 11 | 12 | import * as SwitchStories from './Switch.stories' 13 | 14 | <Meta of={SwitchStories} /> 15 | 16 | <Title /> 17 | 18 | <Subtitle /> 19 | 20 | <Description /> 21 | 22 | --- 23 | 24 | ## Props 25 | 26 | On top of all the props, attributes and event handler you can set on a 27 | `<button />` element, the component accepts the following props: 28 | 29 | <ArgTypes /> 30 | 31 | <Stories includePrimary={false} title="Switches" /> 32 | 33 | ## Playground 34 | 35 | <Primary /> 36 | 37 | <Controls /> 38 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Switch/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Switch } from './Switch.js' 2 | export type { SwitchProps } from './Switch.types.js' 3 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Tabs/Tab.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Stories, 8 | Subtitle, 9 | Title, 10 | } from '@storybook/blocks' 11 | 12 | import * as TabStories from './Tab.stories' 13 | 14 | <Meta of={TabStories} /> 15 | 16 | <Title /> 17 | 18 | <Subtitle /> 19 | 20 | <Description /> 21 | 22 | --- 23 | 24 | ## Props 25 | 26 | On top of all the props, attributes and event handler you can set on a 27 | `<button />` element, the component accepts the following props: 28 | 29 | <ArgTypes /> 30 | 31 | <Stories includePrimary={false} title="Tabs" /> 32 | 33 | ## Playground 34 | 35 | <Primary /> 36 | 37 | <Controls /> 38 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Tabs/TabContent.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Stories, 8 | Subtitle, 9 | Title, 10 | } from '@storybook/blocks' 11 | 12 | import * as TabContentStories from './TabContent.stories' 13 | 14 | <Meta of={TabContentStories} /> 15 | 16 | <Title /> 17 | 18 | <Subtitle /> 19 | 20 | <Description /> 21 | 22 | --- 23 | 24 | ## Props 25 | 26 | On top of all the props, attributes and event handler you can set on a 27 | `<div />` element, the component accepts the following props: 28 | 29 | <ArgTypes /> 30 | 31 | <Stories includePrimary={false} title="Content" /> 32 | 33 | ## Playground 34 | 35 | <Primary /> 36 | 37 | <Controls /> 38 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Tabs/TabContent.tsx: -------------------------------------------------------------------------------- 1 | import * as RxTabs from '@radix-ui/react-tabs' 2 | 3 | import { type ForwardedRef, forwardRef } from 'react' 4 | 5 | import omit from 'lodash/fp/omit' 6 | 7 | import { PREFIX } from '@/constants' 8 | import { cn } from '@/styles/helpers' 9 | 10 | import type { TabContentProps } from './Tabs.types' 11 | 12 | import { GROUP_NAME } from './Tabs.constants' 13 | 14 | /** 15 | * `TabContent` component is used to display the content of a tab inside of a 16 | * `Tabs` component. 17 | * 18 | * See https://www.radix-ui.com/primitives/docs/components/tabs#content for more 19 | * information. 20 | */ 21 | export const TabContent = forwardRef<HTMLDivElement, TabContentProps>( 22 | ( 23 | { children, forceMount = false, name, ...props }: TabContentProps, 24 | ref: ForwardedRef<HTMLDivElement>, 25 | ) => ( 26 | <RxTabs.Content 27 | ref={ref} 28 | className={cn(`${PREFIX}-${GROUP_NAME}__content`, '', props.className)} 29 | {...(forceMount ? { forceMount: true } : {})} 30 | value={name} 31 | {...omit(['className'], props)} 32 | > 33 | {children} 34 | </RxTabs.Content> 35 | ), 36 | ) 37 | TabContent.displayName = 'TabContent' 38 | 39 | export default TabContent 40 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Tabs/Tabs.constants.ts: -------------------------------------------------------------------------------- 1 | export const GROUP_NAME = 'tabs' 2 | 3 | export enum Orientations { 4 | Horizontal = 'horizontal', 5 | Vertical = 'vertical', 6 | } 7 | 8 | export const DEFAULT_ORIENTATION = Orientations.Horizontal 9 | 10 | export enum Positions { 11 | End = 'end', 12 | Start = 'start', 13 | } 14 | 15 | export const DEFAULT_POSITION = Positions.Start 16 | 17 | export enum Sizes { 18 | Large = 'large', 19 | Medium = 'medium', 20 | Small = 'small', 21 | } 22 | 23 | export const DEFAULT_SIZE = Sizes.Small 24 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Tabs/Tabs.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Stories, 8 | Subtitle, 9 | Title, 10 | } from '@storybook/blocks' 11 | 12 | import * as TabsStories from './Tabs.stories' 13 | 14 | <Meta of={TabsStories} /> 15 | 16 | <Title /> 17 | 18 | <Subtitle /> 19 | 20 | <Description /> 21 | 22 | --- 23 | 24 | ## Props 25 | 26 | On top of all the props, attributes and event handler you can set on a 27 | `<div />` element, the component accepts the following props: 28 | 29 | <ArgTypes /> 30 | 31 | <Stories includePrimary={false} title="Tabs Bar" /> 32 | 33 | ## Playground 34 | 35 | <Primary /> 36 | 37 | <Controls /> 38 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Tabs/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Tab } from './Tab.js' 2 | export { default as TabContent } from './TabContent.js' 3 | export { 4 | DEFAULT_SIZE as DEFAULT_TAB_SIZE, 5 | DEFAULT_ORIENTATION as DEFAULT_TABS_ORIENTATION, 6 | DEFAULT_POSITION as DEFAULT_TABS_POSITION, 7 | Sizes as TabSizes, 8 | Orientations as TabsOrientations, 9 | Positions as TabsPositions, 10 | } from './Tabs.constants.js' 11 | export { default as Tabs } from './Tabs.js' 12 | export type { TabContentProps, TabProps, TabsProps } from './Tabs.types.js' 13 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Tag/Tag.constants.ts: -------------------------------------------------------------------------------- 1 | import { Variants as TypographyVariants } from '@/components/Typography/Typography.constants' 2 | 3 | export const GROUP_NAME = 'tag' 4 | 5 | export enum Sizes { 6 | // Small. 7 | S = 's', 8 | 9 | // Medium. 10 | M = 'm', 11 | } 12 | 13 | export const DEFAULT_SIZE = Sizes.S 14 | 15 | export const sizeToTypographyVariant: Record<Sizes, `${TypographyVariants}`> = { 16 | [Sizes.S]: TypographyVariants.CaptionMedian, 17 | 18 | [Sizes.M]: TypographyVariants.Body1, 19 | } 20 | 21 | export enum Colors { 22 | Blue = 'blue', 23 | Green = 'green', 24 | Pink = 'pink', 25 | Purple = 'purple', 26 | White = 'white', 27 | Yellow = 'yellow', 28 | } 29 | 30 | export const DEFAULT_COLOR = Colors.Pink 31 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Tag/Tag.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Stories, 8 | Subtitle, 9 | Title, 10 | } from '@storybook/blocks' 11 | 12 | import * as TagStories from './Tag.stories' 13 | 14 | <Meta of={TagStories} /> 15 | 16 | <Title /> 17 | 18 | <Subtitle /> 19 | 20 | <Description /> 21 | 22 | --- 23 | 24 | ## Props 25 | 26 | On top of all the props, attributes and event handler you can set on a 27 | `<div />` element, the component accepts the following props: 28 | 29 | <ArgTypes /> 30 | 31 | <Stories includePrimary={false} title="Tags" /> 32 | 33 | ## Playground 34 | 35 | <Primary /> 36 | 37 | <Controls /> 38 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Tag/Tag.types.ts: -------------------------------------------------------------------------------- 1 | import type { AllHTMLAttributes, ReactNode } from 'react' 2 | 3 | import { Colors, Sizes } from './Tag.constants' 4 | 5 | export interface TagProps 6 | extends Omit<AllHTMLAttributes<HTMLDivElement>, 'size'> { 7 | /** 8 | * The content of the tag. 9 | * 10 | * Use this for complex content where a string (passed to the `label` prop) is 11 | * not enough. 12 | */ 13 | children?: ReactNode 14 | /** The color of the tag to use. */ 15 | color?: `${Colors}` 16 | /** Indicates if the tag is disabled. */ 17 | disabled?: boolean 18 | /** Indicates if the tag should take all the available width. */ 19 | fullWidth?: boolean 20 | /** 21 | * The content of the tag. 22 | * 23 | * Use this when you only need to display text in a tag. 24 | * If you need more complex content, use the `children` prop. 25 | * 26 | * When using the `children` prop, you can use this prop to set a simple 27 | * textual representation of the item that will be used as the `aria-label` 28 | * and `title` for the tag. 29 | */ 30 | label?: string 31 | /** The size of the tag. */ 32 | size?: `${Sizes}` 33 | } 34 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Tag/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | DEFAULT_COLOR as DEFAULT_TAG_COLOR, 3 | DEFAULT_SIZE as DEFAULT_TAG_SIZE, 4 | Colors as TagColors, 5 | Sizes as TagSizes, 6 | sizeToTypographyVariant as tagSizeToTypographyVariant, 7 | } from './Tag.constants.js' 8 | export { default as Tag } from './Tag.js' 9 | export type { TagProps } from './Tag.types.js' 10 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Textarea/Textarea.constants.ts: -------------------------------------------------------------------------------- 1 | export const GROUP_NAME = 'input-textarea' 2 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Textarea/Textarea.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Stories, 8 | Subtitle, 9 | Title, 10 | } from '@storybook/blocks' 11 | 12 | import * as TextareaStories from './Textarea.stories' 13 | 14 | <Meta of={TextareaStories} /> 15 | 16 | <Title /> 17 | 18 | <Subtitle /> 19 | 20 | <Description /> 21 | 22 | --- 23 | 24 | ## Props 25 | 26 | On top of all the props, attributes and event handler you can set on a 27 | `<TextareaAutosize />` component (and thus a `<textarea />` element), the 28 | component accepts the following props: 29 | 30 | <ArgTypes /> 31 | 32 | <Stories includePrimary={false} title="Textareas" /> 33 | 34 | ## Playground 35 | 36 | <Primary /> 37 | 38 | <Controls /> 39 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Textarea/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Textarea } from './Textarea.js' 2 | export type { TextareaProps } from './Textarea.types.js' 3 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Toggle/Toggle.constants.ts: -------------------------------------------------------------------------------- 1 | export const GROUP_NAME = 'toggle' 2 | 3 | export enum Variants { 4 | Primary = 'primary', 5 | } 6 | 7 | export const DEFAULT_VARIANT = Variants.Primary 8 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Toggle/Toggle.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Stories, 8 | Subtitle, 9 | Title, 10 | } from '@storybook/blocks' 11 | 12 | import * as ToggleStories from './Toggle.stories' 13 | 14 | <Meta of={ToggleStories} /> 15 | 16 | <Title /> 17 | 18 | <Subtitle /> 19 | 20 | <Description /> 21 | 22 | --- 23 | 24 | ## Props 25 | 26 | On top of all the props, attributes and event handler you can set on a 27 | `<button />` element, the component accepts the following props: 28 | 29 | <ArgTypes /> 30 | 31 | <Stories includePrimary={false} title="Toggles" /> 32 | 33 | ## Playground 34 | 35 | <Primary /> 36 | 37 | <Controls /> 38 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Toggle/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | DEFAULT_VARIANT as DEFAULT_TOGGLE_VARIANT, 3 | Variants as ToggleVariants, 4 | } from './Toggle.constants.js' 5 | export { default as Toggle } from './Toggle.js' 6 | export type { ToggleProps } from './Toggle.types.js' 7 | -------------------------------------------------------------------------------- /packages/fractal/src/components/ToggleGroup/ToggleGroup.constants.ts: -------------------------------------------------------------------------------- 1 | export const GROUP_NAME = 'toggle-group' 2 | -------------------------------------------------------------------------------- /packages/fractal/src/components/ToggleGroup/ToggleGroup.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Stories, 8 | Subtitle, 9 | Title, 10 | } from '@storybook/blocks' 11 | 12 | import * as ToggleGroupStories from './ToggleGroup.stories' 13 | 14 | <Meta of={ToggleGroupStories} /> 15 | 16 | <Title /> 17 | 18 | <Subtitle /> 19 | 20 | <Description /> 21 | 22 | --- 23 | 24 | ## Props 25 | 26 | On top of all the props, attributes and event handler you can set on a 27 | `<div />` element, the component accepts the following props: 28 | 29 | <ArgTypes /> 30 | 31 | <Stories includePrimary={false} title="Toggle Groups" /> 32 | 33 | ## Playground 34 | 35 | <Primary /> 36 | 37 | <Controls /> 38 | -------------------------------------------------------------------------------- /packages/fractal/src/components/ToggleGroup/ToggleGroupContext.ts: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { createContext } from 'react' 4 | 5 | import { DEFAULT_VARIANT, Variants } from '@/components/Toggle/Toggle.constants' 6 | 7 | export const ToggleGroupContext = createContext<{ 8 | disabled: boolean 9 | variant: `${Variants}` 10 | }>({ disabled: false, variant: DEFAULT_VARIANT }) 11 | -------------------------------------------------------------------------------- /packages/fractal/src/components/ToggleGroup/ToggleGroupItem.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Subtitle, 8 | Title, 9 | } from '@storybook/blocks' 10 | 11 | import * as ToggleGroupItem from './ToggleGroupItem.stories' 12 | 13 | <Meta of={ToggleGroupItem} /> 14 | 15 | <Title /> 16 | 17 | <Subtitle /> 18 | 19 | <Description /> 20 | 21 | --- 22 | 23 | ## Props 24 | 25 | On top of all the props, attributes and event handler you can set on a 26 | `<button />` element, the component accepts the following props: 27 | 28 | <ArgTypes /> 29 | 30 | ## Playground 31 | 32 | <Primary /> 33 | 34 | <Controls /> 35 | -------------------------------------------------------------------------------- /packages/fractal/src/components/ToggleGroup/index.ts: -------------------------------------------------------------------------------- 1 | export { Variants as ToggleGroupVariants } from '../Toggle/Toggle.constants.js' 2 | export { default as ToggleGroup } from './ToggleGroup.js' 3 | export type { 4 | ToggleGroupItemProps, 5 | ToggleGroupProps, 6 | } from './ToggleGroup.types.js' 7 | export { default as ToggleGroupItem } from './ToggleGroupItem.js' 8 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Toolbar/Toolbar.constants.ts: -------------------------------------------------------------------------------- 1 | import { Elevations } from '@/components/Paper/Paper.constants' 2 | 3 | export const GROUP_NAME = 'toolbar' 4 | 5 | export enum Orientations { 6 | Horizontal = 'horizontal', 7 | Vertical = 'vertical', 8 | } 9 | 10 | export const DEFAULT_ORIENTATION = Orientations.Horizontal 11 | 12 | export const DEFAULT_ELEVATION = Elevations.Elevated 13 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Toolbar/Toolbar.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Stories, 8 | Subtitle, 9 | Title, 10 | } from '@storybook/blocks' 11 | 12 | import * as ToolbarStories from './Toolbar.stories' 13 | 14 | <Meta of={ToolbarStories} /> 15 | 16 | <Title /> 17 | 18 | <Subtitle /> 19 | 20 | <Description /> 21 | 22 | --- 23 | 24 | ## Props 25 | 26 | On top of all the props, attributes and event handler you can set on a 27 | `<div />` element, the component accepts the following props: 28 | 29 | <ArgTypes /> 30 | 31 | <Stories includePrimary={false} title="Toolbars" /> 32 | 33 | ## Playground 34 | 35 | <Primary /> 36 | 37 | <Controls /> 38 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Toolbar/ToolbarButton.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Subtitle, 8 | Title, 9 | } from '@storybook/blocks' 10 | 11 | import * as ToolbarButtonStories from './ToolbarButton.stories' 12 | 13 | <Meta of={ToolbarButtonStories} /> 14 | 15 | <Title /> 16 | 17 | <Subtitle /> 18 | 19 | <Description /> 20 | 21 | --- 22 | 23 | ## Props 24 | 25 | On top of all the props, attributes and event handler you can set on a 26 | `<button />` element, the component accepts the following props: 27 | 28 | <ArgTypes /> 29 | 30 | ## Playground 31 | 32 | <Primary /> 33 | 34 | <Controls /> 35 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Toolbar/ToolbarContext.ts: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { createContext } from 'react' 4 | 5 | import { DEFAULT_ORIENTATION, Orientations } from './Toolbar.constants' 6 | 7 | export const ToolbarContext = createContext<{ 8 | disabled: boolean 9 | orientation: `${Orientations}` 10 | }>({ 11 | disabled: false, 12 | orientation: DEFAULT_ORIENTATION, 13 | }) 14 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Toolbar/ToolbarDropdown.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Stories, 8 | Subtitle, 9 | Title, 10 | } from '@storybook/blocks' 11 | 12 | import * as ToolbarDropdownStories from './ToolbarDropdown.stories' 13 | 14 | <Meta of={ToolbarDropdownStories} /> 15 | 16 | <Title /> 17 | 18 | <Subtitle /> 19 | 20 | <Description /> 21 | 22 | --- 23 | 24 | ## Props 25 | 26 | On top of all the props, attributes and event handler you can set on a 27 | `<Dropdown />` component (and thus on a`<div />` element) (except for 28 | `condensed` and the `trigger`), you can also pass the 'active', 'icon', 29 | 'iconOnly', 'iconPosition' and 'label' props of a `<ToolbarButton />` component 30 | that will be used as the trigger of the dropdown. 31 | Also, you can pass the following props: 32 | 33 | <ArgTypes /> 34 | 35 | <Stories includePrimary={false} title="ToolbarDropdowns" /> 36 | 37 | ## Playground 38 | 39 | <Primary /> 40 | 41 | <Controls /> 42 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Toolbar/ToolbarDropdownItem.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Subtitle, 8 | Title, 9 | } from '@storybook/blocks' 10 | 11 | import * as ToolbarDropdownItemStories from './ToolbarDropdownItem.stories' 12 | 13 | <Meta of={ToolbarDropdownItemStories} /> 14 | 15 | <Title /> 16 | 17 | <Subtitle /> 18 | 19 | <Description /> 20 | 21 | --- 22 | 23 | ## Props 24 | 25 | On top of all the props, attributes and event handler you can set on a 26 | `<DropdownItem />` component (and thus a `<div />` element), the component 27 | accepts the following props: 28 | 29 | <ArgTypes /> 30 | 31 | ## Playground 32 | 33 | <Primary /> 34 | 35 | <Controls /> 36 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Toolbar/ToolbarDropdownItemGroup.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Subtitle, 8 | Title, 9 | } from '@storybook/blocks' 10 | 11 | import * as ToolbarDropdownItemGroupStories from './ToolbarDropdownItemGroup.stories' 12 | 13 | <Meta of={ToolbarDropdownItemGroupStories} /> 14 | 15 | <Title /> 16 | 17 | <Subtitle /> 18 | 19 | <Description /> 20 | 21 | --- 22 | 23 | ## Props 24 | 25 | On top of all the props, attributes and event handler you can set on a 26 | `<DropdownItemGroup />` component (and thus a `<div />` element), the component 27 | accepts the following props: 28 | 29 | <ArgTypes /> 30 | 31 | ## Playground 32 | 33 | <Primary /> 34 | 35 | <Controls /> 36 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Toolbar/ToolbarDropdownItemSeparator.mdx: -------------------------------------------------------------------------------- 1 | import { Description, Meta, Primary, Subtitle, Title } from '@storybook/blocks' 2 | 3 | import * as ToolbarDropdownItemSeparatortories from './ToolbarDropdownItemSeparator.stories' 4 | 5 | <Meta of={ToolbarDropdownItemSeparatortories} /> 6 | 7 | <Title /> 8 | 9 | <Subtitle /> 10 | 11 | <Description /> 12 | 13 | --- 14 | 15 | ## Props 16 | 17 | You can set for this component all the props, attributes and event handler you 18 | can set on a `<DropdownItemSeparator />` component (and thus a `<div />` 19 | element). 20 | 21 | ## Playground 22 | 23 | <Primary /> 24 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Toolbar/ToolbarDropdownItemSeparator.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react' 2 | 3 | import { UilUserCircle as UserIcon } from '@tooni/iconscout-unicons-react' 4 | 5 | import type { ComponentProps } from 'react' 6 | 7 | import ToolbarDropdownItem from '@/components/Dropdown/DropdownItem' 8 | import ToolbarDropdownItemSeparator from '@/components/Dropdown/DropdownItemSeparator' 9 | 10 | import Toolbar from './Toolbar' 11 | import ToolbarDropdown from './ToolbarDropdown' 12 | 13 | type ToolbarDropdownItemSeparatorProps = ComponentProps< 14 | typeof ToolbarDropdownItemSeparator 15 | > 16 | 17 | const meta: Meta<ToolbarDropdownItemSeparatorProps> = { 18 | component: ToolbarDropdownItemSeparator, 19 | parameters: { 20 | chromatic: { delay: 2000 }, 21 | }, 22 | 23 | title: 'Molecules/Toolbar/ToolbarDropdown/ToolbarDropdownItemSeparator', 24 | } satisfies Meta<ToolbarDropdownItemSeparatorProps> 25 | 26 | export default meta 27 | type Story = StoryObj<typeof meta> 28 | 29 | export const Playground: Story = { 30 | render: () => ( 31 | <div className="h-[1200px]"> 32 | <Toolbar> 33 | <ToolbarDropdown 34 | icon={<UserIcon />} 35 | iconOnly 36 | label="Star wars characters" 37 | > 38 | <ToolbarDropdownItem label="Yoda" value="yoda" /> 39 | <ToolbarDropdownItemSeparator /> 40 | <ToolbarDropdownItem label="Darth Sidious" value="darth-sidious" /> 41 | </ToolbarDropdown> 42 | </Toolbar> 43 | </div> 44 | ), 45 | } 46 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Toolbar/ToolbarSeparator.mdx: -------------------------------------------------------------------------------- 1 | import { Description, Meta, Primary, Subtitle, Title } from '@storybook/blocks' 2 | 3 | import * as ToolbarSeparatortories from './ToolbarSeparator.stories' 4 | 5 | <Meta of={ToolbarSeparatortories} /> 6 | 7 | <Title /> 8 | 9 | <Subtitle /> 10 | 11 | <Description /> 12 | 13 | --- 14 | 15 | ## Props 16 | 17 | You can pass any props, attributes and event handler you can set on a 18 | `<div />` element. 19 | 20 | ## Playground 21 | 22 | <Primary /> 23 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Toolbar/ToolbarSeparator.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react' 2 | 3 | import type { ComponentProps } from 'react' 4 | 5 | import { Toolbar, ToolbarButton, ToolbarSeparator } from '.' 6 | 7 | type ToolbarSeparatorProps = ComponentProps<typeof ToolbarSeparator> 8 | 9 | const meta: Meta<ToolbarSeparatorProps> = { 10 | component: ToolbarSeparator, 11 | parameters: { 12 | chromatic: { delay: 2000 }, 13 | }, 14 | 15 | title: 'Molecules/Toolbar/ToolbarSeparator', 16 | } satisfies Meta<ToolbarSeparatorProps> 17 | 18 | export default meta 19 | type Story = StoryObj<typeof meta> 20 | 21 | export const Playground: Story = { 22 | render: () => ( 23 | <div className="h-[200px]"> 24 | <Toolbar> 25 | <ToolbarButton label="Luke Skywalker" value="luke-skywalker" /> 26 | <ToolbarButton label="Obi-Wan Kenobi" value="obi-wan-kenobi" /> 27 | <ToolbarButton label="Yoda" value="yoda" /> 28 | 29 | <ToolbarSeparator /> 30 | 31 | <ToolbarButton label="Darth Sidious" value="darth-sidious" /> 32 | <ToolbarButton label="Darth Maul" value="darth-maul" /> 33 | <ToolbarButton label="Darth Vader" value="darth-vader" /> 34 | </Toolbar> 35 | </div> 36 | ), 37 | } 38 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Toolbar/ToolbarSeparator.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import * as RxToolbar from '@radix-ui/react-toolbar' 4 | 5 | import { useContext } from 'react' 6 | 7 | import omit from 'lodash/fp/omit' 8 | 9 | import { PREFIX } from '@/constants' 10 | import { cn } from '@/styles/helpers' 11 | 12 | import { GROUP_NAME, Orientations } from './Toolbar.constants' 13 | import { ToolbarSeparatorProps } from './Toolbar.types' 14 | import { ToolbarContext } from './ToolbarContext' 15 | 16 | /** 17 | * `ToolbarSeparator` component is used to display a separator between groups or 18 | * items in a toolbar. 19 | */ 20 | export default function ToolbarSeparator({ ...props }: ToolbarSeparatorProps) { 21 | const { orientation } = useContext(ToolbarContext) 22 | 23 | return ( 24 | <RxToolbar.Separator 25 | className={cn( 26 | `${PREFIX}-${GROUP_NAME}__separator`, 27 | `${PREFIX}-${GROUP_NAME}__separator--${orientation}`, 28 | 'bg-separator', 29 | orientation === Orientations.Horizontal ? 'h-full w-px' : 'h-px w-full', 30 | props.className, 31 | )} 32 | {...omit(['className'], props)} 33 | /> 34 | ) 35 | } 36 | ToolbarSeparator.displayName = 'ToolbarSeparator' 37 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Toolbar/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ToolbarDropdownItem } from '../Dropdown/DropdownItem.js' 2 | export { default as ToolbarDropdownItemGroup } from '../Dropdown/DropdownItemGroup.js' 3 | export { default as ToolbarDropdownItemSeparator } from '../Dropdown/DropdownItemSeparator.js' 4 | export { Elevations as ToolbarElevations } from '../Paper/Paper.constants.js' 5 | export { 6 | DEFAULT_ELEVATION as DEFAULT_TOOLBAR_ELEVATION, 7 | DEFAULT_ORIENTATION as DEFAULT_TOOLBAR_ORIENTATION, 8 | Orientations as ToolbarOrientations, 9 | } from './Toolbar.constants.js' 10 | export { default as Toolbar } from './Toolbar.js' 11 | export type { ToolbarButtonProps, ToolbarProps } from './Toolbar.types.js' 12 | export { default as ToolbarButton } from './ToolbarButton.js' 13 | export { default as ToolbarDropdown } from './ToolbarDropdown.js' 14 | export { default as ToolbarSeparator } from './ToolbarSeparator.js' 15 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Tooltip/Tooltip.constants.ts: -------------------------------------------------------------------------------- 1 | export const GROUP_NAME = 'tooltip' 2 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Tooltip/Tooltip.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Stories, 8 | Subtitle, 9 | Title, 10 | } from '@storybook/blocks' 11 | 12 | import * as TooltipStories from './Tooltip.stories' 13 | 14 | <Meta of={TooltipStories} /> 15 | 16 | <Title /> 17 | 18 | <Subtitle /> 19 | 20 | <Description /> 21 | 22 | --- 23 | 24 | ## Props 25 | 26 | On top of all the props, attributes and event handler you can set on a 27 | `<div />` element, component accepts the following props: 28 | 29 | <ArgTypes /> 30 | 31 | <Stories includePrimary={false} title="Tooltips" /> 32 | 33 | ## Playground 34 | 35 | <Primary /> 36 | 37 | <Controls /> 38 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Tooltip/Tooltip.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react' 2 | 3 | import { fn } from '@storybook/test' 4 | import { UilEllipsisV as MoreMenuIcon } from '@tooni/iconscout-unicons-react' 5 | 6 | import type { ComponentProps } from 'react' 7 | 8 | import { Button } from '@/components/Button' 9 | 10 | import { Tooltip } from '.' 11 | 12 | type TooltipProps = ComponentProps<typeof Tooltip> 13 | 14 | const meta = { 15 | args: { 16 | align: undefined, 17 | children: 'Hover me', 18 | content: 'This is a tooltip', 19 | disabled: false, 20 | side: undefined, 21 | withArrow: true, 22 | wrapInButton: false, 23 | }, 24 | argTypes: { 25 | content: { control: 'text' }, 26 | trigger: { 27 | control: 'radio', 28 | mapping: { 29 | Button: <Button label="Hover me" />, 30 | 'Icon Button': ( 31 | <Button 32 | icon={<MoreMenuIcon />} 33 | iconOnly 34 | label="Hover me" 35 | variant="text" 36 | /> 37 | ), 38 | Text: 'Hover me', 39 | }, 40 | options: ['Text', 'Button', 'Icon Button'], 41 | }, 42 | }, 43 | component: Tooltip, 44 | parameters: { 45 | docs: { 46 | subtitle: `🎩 A tip of the hat from Dr. Facilier. - Dr. Facilier - The Princess and the Frog`, 47 | }, 48 | }, 49 | 50 | title: 'Molecules/Tooltip', 51 | } satisfies Meta<TooltipProps> 52 | 53 | export default meta 54 | type Story = StoryObj<typeof meta> 55 | 56 | export const Playground: Story = { 57 | args: { 58 | onDisplayChange: fn(), 59 | onHide: fn(), 60 | onInteractOutside: fn(), 61 | onShow: fn(), 62 | }, 63 | } 64 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Tooltip/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Tooltip } from './Tooltip.js' 2 | export type { TooltipProps } from './Tooltip.types.js' 3 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Typography/Typography.constants.ts: -------------------------------------------------------------------------------- 1 | import type { ElementType } from 'react' 2 | 3 | export const GROUP_NAME = 'typography' 4 | 5 | export enum Variants { 6 | Body1 = 'body-1', 7 | Body1Bold = 'body-1-bold', 8 | Body1Link = 'body-1-link', 9 | Body1Median = 'body-1-median', 10 | 11 | Body2 = 'body-2', 12 | Body2Bold = 'body-2-bold', 13 | Body2Link = 'body-2-link', 14 | Body2Median = 'body-2-median', 15 | 16 | CaptionBold = 'caption-bold', 17 | CaptionLink = 'caption-link', 18 | CaptionMedian = 'caption-median', 19 | 20 | Display1 = 'display-1', 21 | Display2 = 'display-2', 22 | DisplayWide = 'display-wide', 23 | 24 | Heading1 = 'heading-1', 25 | Heading2 = 'heading-2', 26 | Heading3 = 'heading-3', 27 | Heading3Link = 'heading-3-link', 28 | Heading4 = 'heading-4', 29 | Heading4Link = 'heading-4-link', 30 | } 31 | 32 | export const DEFAULT_VARIANT = Variants.Body1 33 | 34 | export const VARIANTS_MAPPING: Record<Variants, ElementType> = { 35 | [Variants.Body1]: 'p', 36 | [Variants.Body1Bold]: 'p', 37 | [Variants.Body1Link]: 'a', 38 | [Variants.Body1Median]: 'p', 39 | 40 | [Variants.Body2]: 'p', 41 | [Variants.Body2Bold]: 'p', 42 | [Variants.Body2Link]: 'a', 43 | [Variants.Body2Median]: 'p', 44 | 45 | [Variants.CaptionBold]: 'p', 46 | [Variants.CaptionLink]: 'a', 47 | [Variants.CaptionMedian]: 'p', 48 | 49 | [Variants.Display1]: 'h1', 50 | [Variants.Display2]: 'h2', 51 | [Variants.DisplayWide]: 'h3', 52 | 53 | [Variants.Heading1]: 'h1', 54 | [Variants.Heading2]: 'h2', 55 | [Variants.Heading3]: 'h3', 56 | [Variants.Heading3Link]: 'a', 57 | [Variants.Heading4]: 'h4', 58 | [Variants.Heading4Link]: 'a', 59 | } 60 | 61 | export const DEFAULT_ELEMENT: ElementType = 'p' 62 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Typography/Typography.mdx: -------------------------------------------------------------------------------- 1 | import { 2 | ArgTypes, 3 | Controls, 4 | Description, 5 | Meta, 6 | Primary, 7 | Stories, 8 | Subtitle, 9 | Title, 10 | } from '@storybook/blocks' 11 | 12 | import * as TypographyStories from './Typography.stories' 13 | 14 | <Meta of={TypographyStories} /> 15 | 16 | <Title /> 17 | 18 | <Subtitle /> 19 | 20 | <Description /> 21 | 22 | --- 23 | 24 | ## Props 25 | 26 | On top of all the props, attributes and event handler you can set on a 27 | `<p />` element (by default, or any other `element` you may choose for your 28 | typography), the component accepts the following props: 29 | 30 | <ArgTypes /> 31 | 32 | <Stories includePrimary={false} title="Typography types" /> 33 | 34 | ## Playground 35 | 36 | <Primary /> 37 | 38 | <Controls /> 39 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Typography/Typography.types.ts: -------------------------------------------------------------------------------- 1 | import type { AllHTMLAttributes, ElementType, ReactNode } from 'react' 2 | 3 | import { Variants } from './Typography.constants' 4 | 5 | export interface TypographyProps extends AllHTMLAttributes<HTMLElement> { 6 | /** The text to display. */ 7 | children?: ReactNode 8 | /** The HTML element to use to display your text. */ 9 | element?: ElementType 10 | /** 11 | * Indicates to inline all styles (including resets, font, ...) or only the 12 | * needed ones. 13 | * 14 | * Only used with `inlineStyle` enabled. 15 | */ 16 | fullStyle?: boolean 17 | /** 18 | * Indicates to inline the styles instead of using any CSS classes. 19 | * 20 | * The typical usage for this is when creating HTML for an email. 21 | */ 22 | inlineStyle?: boolean 23 | /** The variant of typography (style and size) to use. */ 24 | variant?: `${Variants}` 25 | /** 26 | * Force the XS variation of typography. 27 | * In particular of headings.s 28 | * 29 | * This is useful when inlining styles. 30 | */ 31 | xs?: boolean 32 | 33 | /** 34 | * Disable Sendgrid click tracking. 35 | * This will output a `clicktracking="off"` attribute to the `a` element. 36 | * 37 | * Of course this is only useful for a Typography component that outputs an 38 | * `a` element with an `href` attribute (i.e. a link). 39 | */ 40 | disableClickTracking?: boolean 41 | } 42 | -------------------------------------------------------------------------------- /packages/fractal/src/components/Typography/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | DEFAULT_ELEMENT as DEFAULT_TYPOGRAPHY_ELEMENT, 3 | DEFAULT_VARIANT as DEFAULT_TYPOGRAPHY_VARIANT, 4 | Variants as TypographyVariants, 5 | VARIANTS_MAPPING as typographyVariantToElement, 6 | } from './Typography.constants.js' 7 | export { default as Typography } from './Typography.js' 8 | export type { TypographyProps } from './Typography.types.js' 9 | -------------------------------------------------------------------------------- /packages/fractal/src/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Autocomplete/index.js' 2 | export * from './Avatar/index.js' 3 | export * from './Badge/index.js' 4 | export * from './Button/index.js' 5 | export * from './Card/index.js' 6 | export * from './Confirm/index.js' 7 | export * from './CuteIcon/index.js' 8 | export * from './DateTimePicker/index.js' 9 | export * from './Dialog/index.js' 10 | export * from './Dropdown/index.js' 11 | export * from './EmojiPicker/index.js' 12 | export * from './Header/index.js' 13 | export * from './InputCheckbox/index.js' 14 | export * from './InputDate/index.js' 15 | export * from './InputFile/index.js' 16 | export * from './InputPhone/index.js' 17 | export * from './InputPinCode/index.js' 18 | export * from './InputRadio/index.js' 19 | export * from './InputText/index.js' 20 | export * from './Loader/index.js' 21 | export * from './Logo/index.js' 22 | export * from './Menu/index.js' 23 | export * from './Paper/index.js' 24 | export * from './Popover/index.js' 25 | export * from './Progress/index.js' 26 | export * from './ScrollArea/index.js' 27 | export * from './Select/index.js' 28 | export * from './Skeleton/index.js' 29 | export * from './Slider/index.js' 30 | export * from './Stepper/index.js' 31 | export * from './Switch/index.js' 32 | export * from './Tabs/index.js' 33 | export * from './Tag/index.js' 34 | export * from './Textarea/index.js' 35 | export * from './Toggle/index.js' 36 | export * from './ToggleGroup/index.js' 37 | export * from './Toolbar/index.js' 38 | export * from './Tooltip/index.js' 39 | export * from './Typography/index.js' 40 | -------------------------------------------------------------------------------- /packages/fractal/src/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export { default as useTheme } from './useTheme.js' 2 | -------------------------------------------------------------------------------- /packages/fractal/src/hooks/useTheme.ts: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { useContext } from 'react' 4 | 5 | import isEmpty from 'lodash/fp/isEmpty' 6 | 7 | import { DEFAULT_THEME, Themes } from '@/constants' 8 | import { ThemeContext } from '@/ThemeProvider' 9 | 10 | export default function useTheme( 11 | themeOverride?: Themes, 12 | { fail = false, log = false } = {}, 13 | ) { 14 | if (!isEmpty(themeOverride)) { 15 | return themeOverride 16 | } 17 | 18 | // Try to load the context. 19 | try { 20 | // eslint-disable-next-line react-hooks/rules-of-hooks 21 | const themeContext = useContext(ThemeContext) 22 | 23 | return themeContext.theme 24 | } catch (error) { 25 | if (log) { 26 | console.warn( 27 | '[FRACTAL][THEME] Unable to load the global theme as no ThemeProvider has been found!', 28 | ) 29 | } 30 | 31 | if (fail) { 32 | throw error 33 | } 34 | } 35 | 36 | return DEFAULT_THEME 37 | } 38 | -------------------------------------------------------------------------------- /packages/fractal/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './components/index.js' 2 | export * from './constants.js' 3 | export * from './hooks/index.js' 4 | export * from './styles/helpers.js' 5 | export * from './ThemeProvider.js' 6 | export * from './types.js' 7 | -------------------------------------------------------------------------------- /packages/fractal/src/styles/global.css: -------------------------------------------------------------------------------- 1 | @import url('normalize.css') layer(reset); 2 | @import url('tailwindcss/base') layer(base); 3 | @import url('./polyfills.css') layer(base); 4 | @import url('@snowball-tech/design-tokens/dist/web/css/fonts.css') 5 | layer(fractal); 6 | @import url('@snowball-tech/design-tokens/dist/web/css/variables.css') 7 | layer(fractal); 8 | @import url('tailwindcss/components'); 9 | @import url('tailwindcss/utilities'); 10 | 11 | @layer reset, base, fractal, tokens, recipes, utilities; 12 | 13 | :root { 14 | --tw-translate-x: 0; 15 | --tw-translate-y: 0; 16 | --tw-rotate: 0; 17 | --tw-skew-x: 0; 18 | --tw-skew-y: 0; 19 | --tw-scale-x: 1; 20 | --tw-scale-y: 1; 21 | } 22 | 23 | @layer reset { 24 | *, 25 | ::before, 26 | ::after { 27 | border-width: 0; 28 | border-style: solid; 29 | border-color: var(--color-border-default); 30 | } 31 | 32 | html, 33 | body { 34 | margin: 0; 35 | padding: 0; 36 | } 37 | 38 | h1, 39 | h2, 40 | h3, 41 | h4, 42 | h5, 43 | h6, 44 | p, 45 | ol, 46 | ul { 47 | margin: 0; 48 | } 49 | 50 | div { 51 | box-sizing: border-box; 52 | } 53 | 54 | a { 55 | color: var(--color-brand-secondary); 56 | } 57 | 58 | * { 59 | -webkit-tap-highlight-color: transparent; 60 | } 61 | 62 | *::selection { 63 | background-color: var(--color-brand-primary); 64 | } 65 | 66 | *:focus-visible { 67 | outline: none; 68 | } 69 | } 70 | 71 | @layer utilities { 72 | @supports (height: 100dvh) { 73 | .h-screen { 74 | height: 100dvh; 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /packages/fractal/src/tests_helpers.ts: -------------------------------------------------------------------------------- 1 | import { userEvent } from '@storybook/test' 2 | 3 | import { sleep } from './utils' 4 | 5 | export async function slowType( 6 | text: string, 7 | target: HTMLElement, 8 | { strokeDelay = 50 } = {}, 9 | ) { 10 | await userEvent.click(target) 11 | await sleep(100) 12 | 13 | for (let index = 0; index < text.length; index += 1) { 14 | // eslint-disable-next-line no-await-in-loop 15 | await userEvent.type(target, text[index]!, { skipClick: true }) 16 | // eslint-disable-next-line no-await-in-loop 17 | await sleep(strokeDelay) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/fractal/src/types.ts: -------------------------------------------------------------------------------- 1 | import { breakpoints } from '@snowball-tech/design-tokens/dist/web/typescript/constants' 2 | 3 | export type Breakpoint = keyof typeof breakpoints 4 | -------------------------------------------------------------------------------- /packages/fractal/stories/Tokens.mdx: -------------------------------------------------------------------------------- 1 | import { Meta } from '@storybook/blocks' 2 | 3 | <Meta title="Atoms/Tokens" /> 4 | 5 | # Tokens 6 | 7 | --- 8 | 9 | All design values _(colors, breakpoints, shadows, spacings, ...)_ are defined as 10 | design tokens. 11 | 12 | You can find those tokens in the 13 | [`design-tokens` directory of the Fractal repository](https://github.com/snowball-tech/fractal/tree/main/packages/design-tokens) 14 | 15 | > ##### ℹ️ Info 16 | > 17 | > --- 18 | > 19 | > For more information about design tokens, read the 20 | > [README](https://github.com/snowball-tech/fractal/blob/main/packages/design-tokens/README.md) 21 | 22 | For convenience and ease of use, those tokens are translated into usable 23 | resources when developing your application. 24 | 25 | ## CSS variables 26 | 27 | All tokens are exported as CSS variables in the 28 | `@snowball-tech/design-tokens/dist/web/css/variables.css` file 29 | _(exported by the 30 | [`@snowball-tech/design-tokens`](https://www.npmjs.com/package/@snowball-tech/design-tokens) 31 | package)_. 32 | 33 | The variables are named exactly like the corresponding tokens, except that the 34 | "." is replaced by a "-" _(and of course, variables are prefixed with "--")_. 35 | 36 | ## JavaScript/TypeScript constants 37 | 38 | If you need to use a token in your JavaScript/TypeScript code, you can import 39 | any values from the 40 | `@snowball-tech/design-tokens/dist/web/typescript/design-tokens.js` file 41 | _(exported by the 42 | [`@snowball-tech/design-tokens`](https://www.npmjs.com/package/@snowball-tech/design-tokens) 43 | package)_. 44 | 45 | The constants are named exactly like the corresponding tokens but in 46 | [PascalCase](https://techterms.com/definition/pascalcase) 47 | -------------------------------------------------------------------------------- /packages/fractal/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line no-restricted-exports 2 | export { default } from '@snowball-tech/design-tokens/dist/web/tailwindcss/tailwind.config' 3 | -------------------------------------------------------------------------------- /packages/fractal/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "noEmit": false 5 | }, 6 | "include": ["src/**/*.ts", "src/**/*.tsx"], 7 | "files": ["global.d.ts", "index.ts"], 8 | "exclude": ["src/**/*.stories.*", "src/**/*.mdx"], 9 | "references": [] 10 | } 11 | -------------------------------------------------------------------------------- /packages/fractal/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "isolatedModules": true, 6 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 7 | "module": "ESNext", 8 | "moduleResolution": "bundler", 9 | "outDir": "./dist", 10 | "paths": { 11 | "@/*": ["./src/*"] 12 | }, 13 | "rootDir": "./", 14 | "target": "ESNext", 15 | "useDefineForClassFields": true 16 | }, 17 | "include": [ 18 | "./*.ts", 19 | "src/**/*.ts", 20 | "src/**/*.tsx", 21 | "./.storybook/*.ts", 22 | "./.storybook/*.tsx", 23 | "./.storybook/*.mdx", 24 | "./stories" 25 | ], 26 | "files": ["global.d.ts"], 27 | "references": [] 28 | } 29 | -------------------------------------------------------------------------------- /renovate.json5: -------------------------------------------------------------------------------- 1 | { 2 | $schema: 'https://docs.renovatebot.com/renovate-schema.json', 3 | 4 | // https://docs.renovatebot.com/configuration-options/#extends 5 | extends: [ 6 | 'github>snowball-tech/glacier//packages/renovate-config/default.json5', 7 | ], 8 | 9 | enabled: false, 10 | } 11 | -------------------------------------------------------------------------------- /scripts/check-tokens.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | cd "$(dirname "$0")/../packages/fractal" >/dev/null 2>&1 || exit 1 4 | 5 | tokens=$(grep -RPoh "var\(--[^ ,\"=$]+\)" --exclude-dir={node_modules,dist,storybook-static} .storybook src stories | sort | sed -e "s/var(//g" | sed -e "s/)//g") 6 | 7 | for token in $tokens; do 8 | count=$(grep -c -- "$token" "../../node_modules/@snowball-tech/design-tokens/dist/web/css/variables.css") 9 | if [ "$count" -eq 0 ]; then 10 | echo "Token '$token' is not defined in the design tokens." 11 | fi 12 | done 13 | 14 | cd - >/dev/null 2>&1 || exit 2 15 | -------------------------------------------------------------------------------- /scripts/copy-storybook.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # shellcheck disable=SC2181 4 | 5 | # shellcheck disable=SC1090 6 | source "$(dirname "$0")/colors.sh" 7 | 8 | from="$1" 9 | to="$2" 10 | 11 | if [ -z "$from" ] || [ -z "$to" ]; then 12 | echo "Usage: $0 <from> <to>" 13 | exit 1 14 | fi 15 | 16 | echo -n "$(bold_info "Copying '$from' to '$to'... ")" 17 | 18 | copy=$(cp -r "$from" "$to" 2>&1) 19 | if [ $? -gt 0 ]; then 20 | bold_error "FAILED" 21 | error "$copy" 22 | 23 | warning "Something wrong happened during the copy of '$from' to '$to'." 24 | 25 | exit 0 26 | fi 27 | 28 | bold_success "DONE" 29 | 30 | bold_success "Storybook successfully copied!" 31 | echo "" 32 | 33 | info "$to" 34 | 35 | echo -n "$BLUE" 36 | ls "$to" 37 | echo -n "$NORMAL" 38 | -------------------------------------------------------------------------------- /scripts/filter-vercel-deployments.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | echo "Checking if Vercel deployment should be skipped..." 4 | 5 | # If VERCEL_GIT_COMMIT_REF env var starts with "renovate/", then exit with 0 to 6 | # block the Vercel deployment. 7 | 8 | if [[ "${VERCEL_GIT_COMMIT_REF}" == "renovate"/* ]]; then 9 | echo "Renovate branch detected! Skipping deployment!" 10 | exit 0 11 | fi 12 | 13 | # If the commit message contains "[skip ci]" (e.g. release commit generated by 14 | # semantic release), then exit with 0 to block the Vercel deployment. 15 | 16 | if [[ "${VERCEL_GIT_COMMIT_MESSAGE}" == *"[skip ci]"* ]]; then 17 | echo "Commit message contains \"[skip ci]\"! Skipping deployment!" 18 | exit 0 19 | fi 20 | 21 | # Otherwise exit with 1 to carry on with the deployment. 22 | 23 | echo "Continuing with deployment!" 24 | 25 | exit 1 26 | -------------------------------------------------------------------------------- /scripts/setup-freezer.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # shellcheck disable=SC2181 4 | 5 | # Setup SSH keys to access GitHub repositories. 6 | 7 | # shellcheck disable=SC1090 8 | source "$(dirname "$0")/colors.sh" 9 | 10 | if [ -z "$FREEZER_TOKEN" ]; then 11 | bold_error "You must defined the 'FREEZER_TOKEN' environment variable to use this script!" 12 | exit 1 13 | fi 14 | 15 | if [ -n "$CI" ]; then 16 | echo -n "$(info "Updating Git config to enforce HTTPS to connect to Freezer... ")" 17 | git config --global url."https://${FREEZER_TOKEN}@github.com/snowball-tech/freezer.git".insteadOf "git@github.com:snowball-tech/freezer.git" 18 | bold_success "DONE" 19 | else 20 | bold_info "Nothing to do, not in CI environment..." 21 | fi 22 | -------------------------------------------------------------------------------- /tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "noEmit": true, 5 | "allowJs": true 6 | }, 7 | "include": ["**/*.ts", "**/*.d.ts", "**/*.tsx", "**/*.js", "**/*.jsx"], 8 | "files": [] 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "checkJs": false, 5 | "composite": false, 6 | "declaration": true, 7 | "declarationMap": true, 8 | "esModuleInterop": true, 9 | "exactOptionalPropertyTypes": false, 10 | "forceConsistentCasingInFileNames": true, 11 | "incremental": false, 12 | "jsx": "react-jsx", 13 | "module": "NodeNext", 14 | "moduleResolution": "nodenext", 15 | "noEmit": false, 16 | "noErrorTruncation": true, 17 | "noImplicitOverride": true, 18 | "noImplicitReturns": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "noUncheckedIndexedAccess": true, 22 | "preserveSymlinks": true, 23 | "preserveWatchOutput": true, 24 | "resolveJsonModule": true, 25 | "removeComments": true, 26 | "skipLibCheck": true, 27 | "sourceMap": true, 28 | "strict": true, 29 | "target": "ES6", 30 | "useDefineForClassFields": true 31 | }, 32 | "include": [], 33 | "exclude": [ 34 | "**/.yarn/plugins/**/*", 35 | "**/.yarn/releases/**/*", 36 | "**/build", 37 | "**/dist", 38 | "**/out", 39 | "**/node_modules" 40 | ], 41 | "files": [], 42 | "references": [ 43 | { 44 | "path": "packages/design-tokens" 45 | }, 46 | { 47 | "path": "packages/fractal" 48 | } 49 | ] 50 | } 51 | -------------------------------------------------------------------------------- /yarn.config.cjs: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | /** @type {import('@yarnpkg/types')} */ 4 | const { defineConfig } = require(`@yarnpkg/types`) 5 | 6 | /** 7 | * This rule will enforce that a workspace MUST depend on the same version of 8 | * a dependency as the one used by the other workspaces. 9 | * 10 | * @param {Context} context 11 | */ 12 | function enforceConsistentDependenciesAcrossTheProject({ Yarn }) { 13 | for (const dependency of Yarn.dependencies()) { 14 | if (dependency.type === `peerDependencies`) continue 15 | 16 | for (const otherDependency of Yarn.dependencies({ 17 | ident: dependency.ident, 18 | })) { 19 | if (otherDependency.type === `peerDependencies`) continue 20 | 21 | dependency.update(otherDependency.range) 22 | } 23 | } 24 | } 25 | 26 | module.exports = defineConfig({ 27 | constraints: async (context) => { 28 | enforceConsistentDependenciesAcrossTheProject(context) 29 | }, 30 | }) 31 | --------------------------------------------------------------------------------