├── .all-contributorsrc
├── .eslintignore
├── .eslintrc.json
├── .github
├── CODEOWNERS
├── PULL_REQUEST_TEMPLATE.md
└── workflows
│ └── main.yml
├── .gitignore
├── .husky
├── .gitignore
├── commit-msg
└── pre-commit
├── .kodiak.toml
├── .nvmrc
├── .prettierignore
├── .prettierrc.js
├── .release-it.json
├── .storybook
├── main.ts
├── manager.ts
├── preview-body.html
├── preview-head.html
├── preview.tsx
├── storybookTheme.ts
└── utils.ts
├── .vscode
├── extensions.json
└── settings.json
├── CHANGELOG.md
├── LICENSE
├── README.md
├── assets
└── logo.png
├── babel.config.js
├── docs-templates
├── accordion.md
├── breadcrumb.md
├── calendar.md
├── datefield.md
├── datepicker.md
├── daterange-picker.md
├── disclosure.md
├── drawer.md
├── link.md
├── meter.md
├── numberfield.md
├── pagination.md
├── progress.md
├── range-calendar.md
├── slider.md
├── timefield.md
└── toast.md
├── docs
├── accordion.md
├── breadcrumb.md
├── calendar.md
├── code-base-overview.md
├── core-principles.md
├── datefield.md
├── datepicker.md
├── daterange-picker.md
├── disclosure.md
├── drawer.md
├── getting-started.md
├── link.md
├── meter.md
├── numberfield.md
├── pagination.md
├── progress.md
├── range-calendar.md
├── select.md
├── slider.md
├── timefield.md
└── toast.md
├── index.d.ts
├── jest.config.ts
├── jest.setup.js
├── package.json
├── postcss.config.js
├── renovate.json
├── scripts
├── builds
│ └── create-previews.js
├── docs
│ ├── add-composition.js
│ ├── add-csb-links.js
│ ├── add-examples.js
│ ├── add-props.js
│ ├── add-toc.js
│ ├── index.js
│ ├── typeFootPrint.js
│ └── utils
│ │ ├── add-md-content.js
│ │ ├── index.js
│ │ └── md-prettify.js
└── utils
│ ├── common-utils.js
│ ├── index.js
│ └── transpile-ts.js
├── src
├── accordion
│ ├── __utils.ts
│ ├── accordion-base.ts
│ ├── accordion-disclosure.ts
│ ├── accordion-panel.ts
│ ├── accordion-state.ts
│ ├── index.ts
│ └── stories
│ │ ├── AccordionBasic.component.tsx
│ │ ├── AccordionBasic.stories.tsx
│ │ ├── AccordionMultiple.component.tsx
│ │ ├── AccordionMultiple.stories.tsx
│ │ ├── AccordionStyled.component.tsx
│ │ ├── AccordionStyled.css
│ │ └── AccordionStyled.stories.tsx
├── breadcrumbs
│ ├── breadcrumb-link.ts
│ ├── breadcrumbs-base.ts
│ ├── index.ts
│ └── stories
│ │ ├── BreadcrumbsBasic.component.tsx
│ │ ├── BreadcrumbsBasic.css
│ │ └── BreadcrumbsBasic.stories.tsx
├── calendar
│ ├── calendar-base-state.ts
│ ├── calendar-base.ts
│ ├── calendar-cell-button.ts
│ ├── calendar-cell-state.ts
│ ├── calendar-cell.ts
│ ├── calendar-grid-state.ts
│ ├── calendar-grid.ts
│ ├── calendar-next-button.ts
│ ├── calendar-prev-button.ts
│ ├── calendar-state.ts
│ ├── calendar-title.ts
│ ├── index.ts
│ └── stories
│ │ ├── CalendarBasic.component.tsx
│ │ ├── CalendarBasic.css
│ │ ├── CalendarBasic.stories.tsx
│ │ ├── CalendarStyled.component.tsx
│ │ ├── CalendarStyled.stories.tsx
│ │ ├── Utils.component.tsx
│ │ └── tailwind.css
├── datefield
│ ├── date-segment.ts
│ ├── datefield-base-state.ts
│ ├── datefield-base.ts
│ ├── datefield-description.ts
│ ├── datefield-errormessage.ts
│ ├── datefield-label.ts
│ ├── datefield-state.ts
│ ├── index.ts
│ └── stories
│ │ ├── DateFieldBasic.component.tsx
│ │ ├── DateFieldBasic.css
│ │ ├── DateFieldBasic.stories.tsx
│ │ ├── DateFieldStyled.component.tsx
│ │ ├── DateFieldStyled.stories.tsx
│ │ └── tailwind.css
├── datepicker
│ ├── datepicker-base-state.ts
│ ├── datepicker-disclosure.ts
│ ├── datepicker-group.ts
│ ├── datepicker-label.ts
│ ├── datepicker-popover.ts
│ ├── datepicker-state.ts
│ ├── index.ts
│ └── stories
│ │ ├── DatePickerBasic.component.tsx
│ │ ├── DatePickerBasic.css
│ │ ├── DatePickerBasic.stories.tsx
│ │ ├── DatePickerStyled.component.tsx
│ │ ├── DatePickerStyled.stories.tsx
│ │ ├── Utils.component.tsx
│ │ └── tailwind.css
├── daterange-picker
│ ├── daterangepicker-base-state.ts
│ ├── daterangepicker-state.ts
│ ├── index.ts
│ └── stories
│ │ ├── DateRangePickerBasic.component.tsx
│ │ ├── DateRangePickerBasic.css
│ │ ├── DateRangePickerBasic.stories.tsx
│ │ ├── DateRangePickerStyled.component.tsx
│ │ ├── DateRangePickerStyled.stories.tsx
│ │ ├── Utils.component.tsx
│ │ └── tailwind.css
├── disclosure
│ ├── __utils.ts
│ ├── disclosure-collapsible-content.ts
│ ├── index.ts
│ └── stories
│ │ ├── DisclosureHorizontalCollapseBasic.component.tsx
│ │ ├── DisclosureHorizontalCollapseBasic.stories.tsx
│ │ ├── DisclosureVerticalCollapseBasic.component.tsx
│ │ └── DisclosureVerticalCollapseBasic.stories.tsx
├── drawer
│ ├── drawer.ts
│ ├── index.ts
│ └── stories
│ │ ├── DrawerBasic.component.tsx
│ │ ├── DrawerBasic.css
│ │ └── DrawerBasic.stories.tsx
├── index.ts
├── link
│ ├── index.ts
│ ├── link-base.ts
│ └── stories
│ │ ├── LinkBasic.component.tsx
│ │ ├── LinkBasic.stories.tsx
│ │ ├── LinkSpan.component.tsx
│ │ └── LinkSpan.stories.tsx
├── meter
│ ├── __utils.ts
│ ├── index.ts
│ ├── meter-base.tsx
│ ├── meter-state.ts
│ └── stories
│ │ ├── MeterBasic.component.tsx
│ │ ├── MeterBasic.css
│ │ ├── MeterBasic.stories.tsx
│ │ ├── MeterStyled.component.tsx
│ │ └── MeterStyled.stories.tsx
├── numberfield
│ ├── index.ts
│ ├── numberfield-base-state.ts
│ ├── numberfield-decrement-button.ts
│ ├── numberfield-group.ts
│ ├── numberfield-increment-button.ts
│ ├── numberfield-input.ts
│ ├── numberfield-label.ts
│ ├── numberfield-state.ts
│ └── stories
│ │ ├── NumberFieldBasic.component.tsx
│ │ └── NumberFieldBasic.stories.tsx
├── pagination
│ ├── __utils.ts
│ ├── index.ts
│ ├── pagination-base.ts
│ ├── pagination-button.ts
│ ├── pagination-state.ts
│ └── stories
│ │ ├── PaginationBasic.component.tsx
│ │ └── PaginationBasic.stories.tsx
├── progress
│ ├── __utils.ts
│ ├── index.ts
│ ├── progress-base.tsx
│ ├── progress-state.ts
│ └── stories
│ │ ├── CircularProgress.component.tsx
│ │ ├── CircularProgress.stories.tsx
│ │ ├── LinearProgress.component.tsx
│ │ ├── LinearProgress.stories.tsx
│ │ ├── ProgressBasic.component.tsx
│ │ ├── ProgressBasic.css
│ │ └── ProgressBasic.stories.tsx
├── range-calendar
│ ├── index.ts
│ ├── range-calendar-base-state.ts
│ ├── range-calendar-state.ts
│ ├── range-calendar.ts
│ └── stories
│ │ ├── RangeCalendarBasic.component.tsx
│ │ ├── RangeCalendarBasic.css
│ │ ├── RangeCalendarBasic.stories.tsx
│ │ ├── RangeCalendarStyled.component.tsx
│ │ ├── RangeCalendarStyled.stories.tsx
│ │ └── tailwind.css
├── slider
│ ├── index.ts
│ ├── slider-base-state.ts
│ ├── slider-base.ts
│ ├── slider-input.tsx
│ ├── slider-label.ts
│ ├── slider-output.ts
│ ├── slider-state.ts
│ ├── slider-thumb-state.ts
│ ├── slider-thumb.tsx
│ ├── slider-track.ts
│ └── stories
│ │ ├── SliderAllInOne.component.tsx
│ │ ├── SliderAllInOne.stories.tsx
│ │ ├── SliderBasic.component.tsx
│ │ ├── SliderBasic.css
│ │ ├── SliderBasic.stories.tsx
│ │ ├── SliderMulti.component.tsx
│ │ ├── SliderMulti.stories.tsx
│ │ ├── SliderRange.component.tsx
│ │ ├── SliderRange.stories.tsx
│ │ ├── SliderSingle.component.tsx
│ │ ├── SliderSingle.stories.tsx
│ │ ├── SliderSingleOrigin.component.tsx
│ │ ├── SliderSingleOrigin.stories.tsx
│ │ ├── SliderSingleReversed.component.tsx
│ │ ├── SliderSingleReversed.stories.tsx
│ │ ├── SliderSingleVertical.component.tsx
│ │ └── SliderSingleVertical.stories.tsx
├── timefield
│ ├── index.ts
│ ├── stories
│ │ ├── TimeFieldBasic.component.tsx
│ │ ├── TimeFieldBasic.css
│ │ ├── TimeFieldBasic.stories.tsx
│ │ ├── TimeFieldStyled.component.tsx
│ │ ├── TimeFieldStyled.stories.tsx
│ │ └── tailwind.css
│ ├── time-segment.ts
│ ├── timefield-base-state.ts
│ ├── timefield-base.ts
│ ├── timefield-description.ts
│ ├── timefield-errormessage.ts
│ ├── timefield-label.ts
│ └── timefield-state.ts
└── toast
│ ├── CreateToastContext.tsx
│ ├── CreateToastContext.types.ts
│ ├── ToastState.ts
│ ├── ToastTypes.ts
│ ├── __keys.ts
│ ├── __tests__
│ └── ToastState.test.tsx
│ ├── helpers
│ └── index.ts
│ ├── index.ts
│ ├── stories
│ ├── ToastBasic.component.tsx
│ ├── ToastBasic.css
│ ├── ToastBasic.stories.tsx
│ ├── ToastCSSAnimated.component.tsx
│ ├── ToastCSSAnimated.stories.tsx
│ ├── ToastCSSTransition.component.tsx
│ ├── ToastCSSTransition.stories.tsx
│ ├── ToastReactSpring.component.tsx
│ ├── ToastReactSpring.stories.tsx
│ └── Utils.component.tsx
│ └── useToastTimer.tsx
├── tailwind.config.js
├── tsconfig.json
├── tsconfig.prod.json
└── yarn.lock
/.all-contributorsrc:
--------------------------------------------------------------------------------
1 | {
2 | "projectName": "react",
3 | "projectOwner": "adaptui",
4 | "repoType": "github",
5 | "repoHost": "https://github.com",
6 | "files": [
7 | "README.md"
8 | ],
9 | "imageSize": 100,
10 | "commit": true,
11 | "commitConvention": "angular",
12 | "contributors": [
13 | {
14 | "login": "navin-moorthy",
15 | "name": "Navin Moorthy",
16 | "avatar_url": "https://avatars0.githubusercontent.com/u/39694575?v=4",
17 | "profile": "https://navinmoorthy.me/",
18 | "contributions": [
19 | "code"
20 | ]
21 | },
22 | {
23 | "login": "anuraghazra",
24 | "name": "Anurag Hazra",
25 | "avatar_url": "https://avatars3.githubusercontent.com/u/35374649?v=4",
26 | "profile": "http://anuraghazra.github.io/",
27 | "contributions": [
28 | "code"
29 | ]
30 | },
31 | {
32 | "login": "sandeepprabhakaran",
33 | "name": "Sandeep Prabhakaran",
34 | "avatar_url": "https://avatars2.githubusercontent.com/u/6380293?v=4",
35 | "profile": "http://timeless.co/",
36 | "contributions": [
37 | "ideas"
38 | ]
39 | },
40 | {
41 | "login": "mcnaveen",
42 | "name": "MC Naveen",
43 | "avatar_url": "https://avatars.githubusercontent.com/u/8493007?v=4",
44 | "profile": "https://github.com/mcnaveen",
45 | "contributions": [
46 | "code"
47 | ]
48 | }
49 | ],
50 | "contributorsPerLine": 7
51 | }
52 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | # dotenv environment variable files
2 | .env*
3 |
4 | # testing
5 | /coverage
6 |
7 | # next.js
8 | /.next/
9 | /out/
10 |
11 | # Storybook build
12 | storybook-static
13 |
14 | # Local Netlify
15 | .netlify
16 | .idea
17 | *.env
18 |
19 | # Vercel folder
20 | .vercel
21 |
22 | # Optional eslint cache
23 | .eslintcache
24 |
25 | # Logs
26 | *.log*
27 |
28 | # Dependency directories
29 | node_modules
30 | jspm_packages
31 |
32 | # Mac files
33 | .DS_Store
34 |
35 | # Optional npm cache directory
36 | .npm
37 |
38 | # Yarn 2
39 | .pnp/
40 | .pnp.*
41 | .yarn/*
42 | !.yarn/cache
43 | !.yarn/patches
44 | !.yarn/plugins
45 | !.yarn/releases
46 | !.yarn/sdks
47 | !.yarn/versions
48 | .yarn-integrity # Yarn Integrity file
49 |
50 | # Lock files
51 | package-lock.json
52 | yarn.lock
53 |
54 | ## library folder
55 | dist
56 | __js
57 | templates
58 | docs
59 | CHANGELOG.md
60 | .yalc
61 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "react-app",
4 | "react-app/jest",
5 | "plugin:prettier/recommended",
6 | "plugin:storybook/recommended"
7 | ],
8 | "plugins": ["simple-import-sort"],
9 | "rules": {
10 | "no-console": "off",
11 | "simple-import-sort/imports": [
12 | "error",
13 | {
14 | // https://github.com/lydell/eslint-plugin-simple-import-sort#custom-grouping
15 | "groups": [
16 | // Packages. `react` related packages come first.
17 | [
18 | "^react",
19 | "^ariakit-utils/system",
20 | "^ariakit",
21 | "^ariakit-utils",
22 | "^@?\\w"
23 | ],
24 | // Parent imports. Put `..` last.
25 | ["^\\.\\.(?!/?$)", "^\\.\\./?$"],
26 | // Other relative imports. Put same-folder imports and `.` last.
27 | ["^\\./(?=.*/)(?!/?$)", "^\\.(?!/?$)", "^\\./?$"],
28 | // Style imports.
29 | ["^.+\\.s?css$"]
30 | ]
31 | }
32 | ],
33 | "simple-import-sort/exports": "error",
34 | "import/first": "error",
35 | "import/newline-after-import": "error",
36 | "import/no-duplicates": "error",
37 | "testing-library/prefer-explicit-assert": ["error"],
38 | "testing-library/no-node-access": "off",
39 | "testing-library/consistent-data-testid": [
40 | 2,
41 | {
42 | "testIdPattern": "^testid(-.*\\w)?$"
43 | }
44 | ]
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # Learn how to add code owners here:
2 | # https://help.github.com/en/articles/about-code-owners
3 |
4 | * @navin-moorthy
5 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | # Description
2 |
3 |
5 |
6 |
7 |
8 | Fixes # (issue)
9 |
10 | ## Type of change
11 |
12 | Please delete options that are not relevant.
13 |
14 | - [ ] Bug fix (non-breaking change which fixes an issue)
15 | - [ ] New feature (non-breaking change which adds functionality)
16 | - [ ] Breaking change (fix or feature that would cause existing functionality to
17 | not work as expected)
18 | - [ ] This change requires a documentation update
19 |
20 | ## How has this been tested?
21 |
22 | Please describe the tests that you ran to verify your changes. Provide
23 | instructions so we can reproduce.
24 |
25 | - [ ] Linting Passed
26 | - [ ] Others
27 |
28 | ## Checklist:
29 |
30 | - [ ] My code follows the style guidelines of this project
31 | - [ ] I have performed a self-review of my code
32 | - [ ] I have commented my code, particularly in hard-to-understand areas
33 | - [ ] I have made corresponding changes to the documentation
34 | - [ ] My changes generate no new warnings
35 | - [ ] I have added tests that prove my fix is effective or that my feature works
36 | - [ ] New and existing unit tests pass locally with my changes
37 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # dotenv environment variable files
2 | .env*
3 |
4 | # testing
5 | /coverage
6 |
7 | # next.js
8 | /.next/
9 | /out/
10 |
11 | # Storybook build
12 | storybook-static
13 |
14 | # Local Netlify
15 | .netlify
16 | .idea
17 | *.env
18 |
19 | # Vercel folder
20 | .vercel
21 |
22 | # Optional eslint cache
23 | .eslintcache
24 |
25 | # Logs
26 | *.log*
27 |
28 | # Dependency directories
29 | node_modules
30 | jspm_packages
31 |
32 | # Mac files
33 | .DS_Store
34 |
35 | # Optional npm cache directory
36 | .npm
37 |
38 | # Yarn 2
39 | .pnp/
40 | .pnp.*
41 | .yarn/*
42 | !.yarn/cache
43 | !.yarn/patches
44 | !.yarn/plugins
45 | !.yarn/releases
46 | !.yarn/sdks
47 | !.yarn/versions
48 | .yarn-integrity # Yarn Integrity file
49 |
50 | # Lock files
51 | package-lock.json
52 | yarn.lock
53 |
54 | ## library folder
55 | dist
56 | __js
57 | templates
58 |
--------------------------------------------------------------------------------
/.husky/.gitignore:
--------------------------------------------------------------------------------
1 | _
2 |
--------------------------------------------------------------------------------
/.husky/commit-msg:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | npx commitlint --edit $1
5 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | npx lint-staged
5 |
--------------------------------------------------------------------------------
/.kodiak.toml:
--------------------------------------------------------------------------------
1 | # .kodiak.toml
2 | version = 1
3 |
4 | [merge]
5 | automerge_label = "ready 🎉"
6 | require_automerge_label = false
7 | delete_branch_on_merge = true
8 | optimistic_updates = true
9 | prioritize_ready_to_merge = true
10 | notify_on_conflict = false
11 |
12 | [merge.message]
13 | title = "pull_request_title"
14 | body = "github_default"
15 | include_pr_number = true
16 | body_type = "markdown"
17 | strip_html_comments = true
18 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 16
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | # dotenv environment variable files
2 | .env*
3 |
4 | # testing
5 | /coverage
6 |
7 | # next.js
8 | /.next/
9 | /out/
10 |
11 | # Storybook build
12 | storybook-static
13 |
14 | # Local Netlify
15 | .netlify
16 | .idea
17 | *.env
18 |
19 | # Vercel folder
20 | .vercel
21 |
22 | # Optional eslint cache
23 | .eslintcache
24 |
25 | # Logs
26 | *.log*
27 |
28 | # Dependency directories
29 | node_modules
30 | jspm_packages
31 |
32 | # Mac files
33 | .DS_Store
34 |
35 | # Optional npm cache directory
36 | .npm
37 |
38 | # Yarn 2
39 | .pnp/
40 | .pnp.*
41 | .yarn/*
42 | !.yarn/cache
43 | !.yarn/patches
44 | !.yarn/plugins
45 | !.yarn/releases
46 | !.yarn/sdks
47 | !.yarn/versions
48 | .yarn-integrity # Yarn Integrity file
49 |
50 | # Lock files
51 | package-lock.json
52 | yarn.lock
53 |
54 | ## library folder
55 | dist
56 | __js
57 | templates
58 | /docs
59 | CHANGELOG.md
60 | .yalc
61 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | // max 120 characters per line
3 | printWidth: 80,
4 | // use 2 spaces for indentation
5 | tabWidth: 2,
6 | // use spaces instead of indentations
7 | useTabs: false,
8 | // semicolon at the end of the line
9 | semi: true,
10 | // use single quotes
11 | singleQuote: false,
12 | // object's key is quoted only when necessary
13 | quoteProps: "as-needed",
14 | // use double quotes instead of single quotes in jsx
15 | jsxSingleQuote: false,
16 | // no comma at the end
17 | trailingComma: "all",
18 | // spaces are required at the beginning and end of the braces
19 | bracketSpacing: true,
20 | // brackets are not required for arrow function parameter, when there is only one parameter
21 | arrowParens: "avoid",
22 | // format the entire contents of the file
23 | rangeStart: 0,
24 | rangeEnd: Infinity,
25 | // no need to write the beginning @prettier of the file
26 | requirePragma: false,
27 | // No need to automatically insert @prettier at the beginning of the file
28 | insertPragma: false,
29 | // use default break criteria
30 | proseWrap: "always",
31 | // decide whether to break the html according to the display style
32 | htmlWhitespaceSensitivity: "css",
33 | // vue files script and style tags indentation
34 | vueIndentScriptAndStyle: false,
35 | // lf for newline
36 | endOfLine: "lf",
37 | // formats quoted code embedded
38 | embeddedLanguageFormatting: "auto",
39 | };
40 |
--------------------------------------------------------------------------------
/.release-it.json:
--------------------------------------------------------------------------------
1 | {
2 | "hooks": {
3 | "before:init": [
4 | "if [ \"$(git log $(git describe --tags --abbrev=0)..HEAD)\" = \"\" ]; then exit 1; fi;",
5 | "yarn test",
6 | "yarn build"
7 | ]
8 | },
9 | "git": {
10 | "requireBranch": "main",
11 | "commitMessage": "🚀 Release v${version}",
12 | "commitArgs": ["--no-verify", "-S"],
13 | "tagArgs": ["-s"]
14 | },
15 | "github": {
16 | "release": true,
17 | "releaseName": "Release v${version}"
18 | },
19 | "plugins": {
20 | "@release-it/conventional-changelog": {
21 | "ignoreRecommendedBump": true,
22 | "infile": "CHANGELOG.md",
23 | "preset": {
24 | "name": "conventionalcommits",
25 | "types": [
26 | { "type": "feat", "section": "Feature Updates", "hidden": false },
27 | { "type": "fix", "section": "Bug Fixes", "hidden": false },
28 | {
29 | "type": "refactor",
30 | "section": "Code Refactors",
31 | "hidden": false
32 | },
33 | {
34 | "type": "docs",
35 | "section": "Documentation Changes",
36 | "hidden": false
37 | },
38 | {
39 | "type": "chore",
40 | "section": "Maintanance Updates",
41 | "hidden": false
42 | },
43 | { "type": "build", "section": "Build Updates", "hidden": false },
44 | { "type": "test", "section": "Test Updates", "hidden": false },
45 | { "type": "style", "section": "Other Changes", "hidden": false },
46 | {
47 | "type": "perf",
48 | "section": "Performance Improvements",
49 | "hidden": false
50 | },
51 | { "type": "ci", "section": "CI Changes", "hidden": false },
52 | { "type": "revert", "section": "Updates Reverted", "hidden": false }
53 | ]
54 | }
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/.storybook/main.ts:
--------------------------------------------------------------------------------
1 | const config = {
2 | framework: "@storybook/react",
3 | core: { builder: "webpack5" },
4 | // storyStoreV7 removes the circular dependency issue with Webpack 5
5 | // So, we added ThemeProvider in preview.jsx and so src/theme should work for HMR
6 | features: { storyStoreV7: true, babelModeV7: true },
7 | stories: ["../src/*/stories/*.stories.@(ts|tsx)"],
8 | addons: [
9 | "storybook-addon-preview",
10 | "@storybook/addon-essentials",
11 | "@storybook/addon-a11y",
12 | {
13 | name: "@storybook/addon-postcss",
14 | options: {
15 | postcssLoaderOptions: {
16 | implementation: require("postcss"),
17 | },
18 | },
19 | },
20 | ],
21 | staticDirs: ["../assets"],
22 | };
23 |
24 | module.exports = config;
25 |
--------------------------------------------------------------------------------
/.storybook/manager.ts:
--------------------------------------------------------------------------------
1 | import { addons } from "@storybook/addons";
2 | import storybookTheme from "./storybookTheme";
3 |
4 | addons.setConfig({ enableShortcuts: false, theme: storybookTheme });
5 |
--------------------------------------------------------------------------------
/.storybook/preview-body.html:
--------------------------------------------------------------------------------
1 |
16 |
--------------------------------------------------------------------------------
/.storybook/preview-head.html:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/.storybook/preview.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { kebabCase } from "lodash";
3 |
4 | export const parameters = {
5 | actions: { argTypesRegex: "^on[A-Z].*" },
6 | controls: {
7 | passArgsFirst: true,
8 | expanded: true,
9 | hideNoControlsWarning: true,
10 | },
11 | };
12 |
13 | export const decorators = [
14 | (Story: any, context: any) => {
15 | document.body.id = kebabCase(context.kind);
16 | document.body.classList.add("font-sans");
17 | document.body.classList.add("antialiased");
18 |
19 | return ;
20 | },
21 | ];
22 |
--------------------------------------------------------------------------------
/.storybook/storybookTheme.ts:
--------------------------------------------------------------------------------
1 | import { create } from "@storybook/theming";
2 |
3 | export default create({
4 | base: "light",
5 | brandTitle: "AdaptUI React",
6 | brandUrl: "https://github.com/adaptui/react",
7 | brandImage: "/logo.png",
8 | brandTarget: "_self",
9 | });
10 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "dbaeumer.vscode-eslint",
4 | "esbenp.prettier-vscode",
5 | "vivaxy.vscode-conventional-commits",
6 | "christian-kohler.path-intellisense",
7 | "bradlc.vscode-tailwindcss",
8 | "orta.vscode-jest",
9 | "tlent.jest-snapshot-language-support",
10 | "vespa-dev-works.jestrunit"
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "typescript.tsdk": "node_modules/typescript/lib",
3 | "git.branchProtection": ["main"],
4 | "editor.tabSize": 2,
5 | "editor.guides.bracketPairs": true,
6 | "editor.rulers": [80, 100, 120],
7 | "editor.wordWrap": "bounded",
8 | "editor.wordWrapColumn": 120,
9 | "files.insertFinalNewline": true,
10 | "files.trimTrailingWhitespace": true,
11 | "files.eol": "\n",
12 | "editor.defaultFormatter": "esbenp.prettier-vscode",
13 | "prettier.enable": true,
14 | "editor.formatOnSave": true,
15 | "editor.codeActionsOnSave": {
16 | "source.addMissingImports": true,
17 | // "source.organizeImports": true,
18 | // "source.sortImports": true,
19 | // "source.fixAll": true
20 | "source.fixAll.eslint": true
21 | // "source.fixAll.stylelint": true
22 | },
23 | "eslint.enable": true,
24 | "eslint.useESLintClass": true,
25 | "eslint.validate": [
26 | "javascript",
27 | "javascriptreact",
28 | "vue",
29 | "typescript",
30 | "typescriptreact",
31 | "html"
32 | ]
33 | }
34 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Timeless
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 |
--------------------------------------------------------------------------------
/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adaptui/react/0be2be926f268ce9474566018d92071e37e8ff1c/assets/logo.png
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | const BABEL_ENV = process.env.BABEL_ENV;
2 | const isCommonJS = BABEL_ENV !== undefined && BABEL_ENV === "cjs";
3 | const isESM = BABEL_ENV !== undefined && BABEL_ENV === "esm";
4 | const isBuild = !!BABEL_ENV;
5 |
6 | module.exports = function (api) {
7 | api.cache(true);
8 |
9 | const presets = [
10 | [
11 | "@babel/env",
12 | {
13 | modules: isCommonJS ? "commonjs" : false,
14 | targets: {
15 | esmodules: isESM ? true : undefined,
16 | },
17 | },
18 | ],
19 | ["@babel/preset-react", { runtime: "automatic" }],
20 | "@babel/preset-typescript",
21 | ];
22 |
23 | const plugins = [
24 | ["@babel/plugin-proposal-class-properties", { loose: true }],
25 | ["@babel/plugin-proposal-logical-assignment-operators", { loose: true }],
26 | ["@babel/plugin-proposal-private-property-in-object", { loose: true }],
27 | ["@babel/plugin-proposal-private-methods", { loose: true }],
28 | isBuild
29 | ? [
30 | "babel-plugin-jsx-remove-data-test-id",
31 | { attributes: ["data-testid"] },
32 | ]
33 | : {},
34 | ];
35 |
36 | return {
37 | presets,
38 | plugins,
39 | env: {
40 | test: {
41 | presets: [["@babel/env", { targets: { node: "current" } }]],
42 | },
43 | },
44 | ignore: isBuild ? ["**/*/__tests__", "**/*/stories"] : [],
45 | };
46 | };
47 |
--------------------------------------------------------------------------------
/docs-templates/accordion.md:
--------------------------------------------------------------------------------
1 | # Accordion
2 |
3 | `Accordion` component expands/collapses to show more information on clicking the
4 | trigger button. It follows The
5 | [WAI-ARIA Accordion Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/accordion/)
6 | for
7 | [keyboard interaction](https://www.w3.org/WAI/ARIA/apg/patterns/accordion/#:~:text=at%20a%20time.-,Keyboard%20Interaction,-Enter)
8 | &
9 | [accessibiltiy properties](https://www.w3.org/WAI/ARIA/apg/patterns/accordion/#:~:text=last%20accordion%20header.-,WAI%2DARIA%20Roles%2C%20States%2C%20and%20Properties%3A,-The%20title%20of).
10 |
11 |
12 |
13 | ## Usage
14 |
15 |
16 |
17 |
21 |
25 |
26 | ## Other Examples
27 |
28 |
32 |
36 |
37 |
42 |
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/docs-templates/breadcrumb.md:
--------------------------------------------------------------------------------
1 | # Breadcrumb
2 |
3 | `Breadcrumb` component is used for the page navigation and it provides the
4 | required aria attributes for it's links and the current link. It follows the
5 | [WAI-ARIA Breadcrumb Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/breadcrumb/)
6 | for its
7 | [accessibility properties](https://www.w3.org/WAI/ARIA/apg/patterns/breadcrumb/#:~:text=Not%20applicable.-,WAI%2DARIA%20Roles%2C%20States%2C%20and%20Properties,-Breadcrumb%20trail%20is).
8 |
9 |
10 |
11 | ## Usage
12 |
13 |
14 |
15 |
20 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/docs-templates/calendar.md:
--------------------------------------------------------------------------------
1 | # Calendar
2 |
3 | `Calendar` component provides a way to select a date while allowing you to style
4 | them however. All the date, month & year calculations are done internally using
5 | [@internationalized/date](https://react-spectrum.adobe.com/internationalized/date/index.html)
6 | to provide the ease of use. It follows the
7 | [Grid Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/grid/) for the keyboard
8 | navigaiton & focus management. Supports all the features as React Aria's
9 | [useCalendar](https://react-spectrum.adobe.com/react-aria/useCalendar.html#features).
10 |
11 |
12 |
13 | ## Usage
14 |
15 |
16 |
17 |
23 |
29 |
30 | ## Other Examples
31 |
32 |
38 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/docs-templates/datefield.md:
--------------------------------------------------------------------------------
1 | # DateField
2 |
3 | `DateField` component is an input that provides a way to select a date and time
4 | using keyboard. It follows the
5 | [Native Input Date](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/date)
6 | for the keyboard navigation & accessibility features. Supports all the features
7 | of React Aria's
8 | [useDateField](https://react-spectrum.adobe.com/react-aria/useDateField.html#features).
9 |
10 |
11 |
12 | ## Usage
13 |
14 |
15 |
16 |
21 |
26 |
27 | ## Other Examples
28 |
29 |
34 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/docs-templates/disclosure.md:
--------------------------------------------------------------------------------
1 | # Disclosure
2 |
3 | `Disclosure` component that controls visibility of a section of content. It
4 | follows the
5 | [WAI-ARIA Disclosure Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/)
6 | for it's
7 | [keyboard interaction](https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/#:~:text=Top%2DLevel%20Links-,Keyboard%20Interaction,-When%20the%20disclosure)
8 | &
9 | [accessibility properties](https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/#:~:text=the%20disclosure%20content.-,WAI%2DARIA%20Roles%2C%20States%2C%20and%20Properties,-The%20element%20that).
10 |
11 |
12 |
13 | ## Usage
14 |
15 | ### Horizontal Disclosure
16 |
17 |
18 |
19 |
23 |
27 |
28 | ### Vertical Disclosure
29 |
30 |
31 |
32 |
36 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/docs-templates/drawer.md:
--------------------------------------------------------------------------------
1 | # Drawer
2 |
3 | `Drawer` component that controls visibility of a section of content by following
4 | the
5 | [WAI-ARIA Dialog(Modal) Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/dialogmodal/)
6 | for it's
7 | [keyboard interaction](https://www.w3.org/WAI/ARIA/apg/patterns/dialogmodal/#:~:text=choosing%20a%20date.-,Keyboard%20Interaction,-In%20the%20following)
8 | &
9 | [accessibility properties](https://www.w3.org/WAI/ARIA/apg/patterns/dialogmodal/#:~:text=or%20cancel%20button.-,WAI%2DARIA%20Roles%2C%20States%2C%20and%20Properties,-The%20element%20that).
10 |
11 |
12 |
13 | ## Usage
14 |
15 |
16 |
17 |
22 |
23 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/docs-templates/link.md:
--------------------------------------------------------------------------------
1 | # Link
2 |
3 | `Link` component that provides the required aria role when used under different
4 | compositions. It follows the
5 | [WAI-ARIA Link Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/link/) for
6 | [keyboard interactions](https://www.w3.org/WAI/ARIA/apg/patterns/link/#:~:text=and%20img%20elements.-,Keyboard%20Interaction,-Enter)
7 | and
8 | [accessibilty properties](https://www.w3.org/WAI/ARIA/apg/patterns/link/#:~:text=for%20the%20link.-,WAI%2DARIA%20Roles%2C%20States%2C%20and%20Properties,-The%20element%20containing)
9 |
10 |
11 |
12 | ## Usage
13 |
14 |
15 |
16 |
20 |
24 |
25 | ## Other Examples
26 |
27 |
31 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/docs-templates/meter.md:
--------------------------------------------------------------------------------
1 | # Meter
2 |
3 | `Meter` component can be used to provide a graphical display of a numeric value
4 | that varies within a defined range. It follows the
5 | [WAI-ARIA Meter Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/meter/) for
6 | it's
7 | [accessibility properties](https://www.w3.org/WAI/ARIA/apg/patterns/meter/#:~:text=Not%20applicable.-,WAI%2DARIA%20Roles%2C%20States%2C%20and%20Properties,-The%20element%20serving).
8 |
9 |
10 |
11 | ## Usage
12 |
13 |
14 |
15 |
20 |
25 |
26 | ## Other Examples
27 |
28 |
33 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/docs-templates/numberfield.md:
--------------------------------------------------------------------------------
1 | # NumberField
2 |
3 | `NumberField` component is a form element used to select a number while
4 | following the keyboard interactions & accessibility properties like the
5 | [Native Number Input](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/number).
6 | It follows
7 | [WAI-ARIA Spin Button Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/spinbutton/)
8 | for the
9 | [keyboard interactions](https://www.w3.org/WAI/ARIA/apg/patterns/spinbutton/#:~:text=month%2C%20and%20year.-,Keyboard%20Interaction,-Up%20Arrow)
10 | and
11 | [accessibility features](https://www.w3.org/WAI/ARIA/apg/patterns/spinbutton/#:~:text=to%20perform%20them.-,WAI%2DARIA%20Roles%2C%20States%2C%20and%20Properties,-The%20focusable%20element).
12 | Supports all the features as React Aria's
13 | [useNumberField](https://react-spectrum.adobe.com/react-aria/useNumberField.html#features).
14 |
15 |
16 |
17 | ## Usage
18 |
19 |
20 |
21 |
25 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/docs-templates/pagination.md:
--------------------------------------------------------------------------------
1 | # Pagination
2 |
3 | `Pagination` component provides all the accessibility features for the page
4 | navigation.
5 |
6 |
7 |
8 | ## Usage
9 |
10 |
11 |
12 |
16 |
20 |
21 | ## Accessibility Requirement
22 |
23 | - `Pagination` should have `aria-label` or `aria-labelledby` attribute.
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/docs-templates/progress.md:
--------------------------------------------------------------------------------
1 | # Progress
2 |
3 | `Progress` component provides a graphical status for tasks that take some amount
4 | of time to load. It follows
5 | [WAI-ARIA Progressbar Pattern](https://www.w3.org/TR/wai-aria-1.2/#progressbar).
6 |
7 |
8 |
9 | ## Usage
10 |
11 |
12 |
13 |
18 |
23 |
24 | ## Other Examples
25 |
26 |
31 |
36 |
37 |
42 |
47 |
48 | ## Accessibility Requirement
49 |
50 | - If the `Progress` is describing the loading progress of a particular region of
51 | a page, you should use `aria-describedby` to point to the status, and set the
52 | `aria-busy `attribute to `true` on the region until it is finished loading.
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/docs-templates/timefield.md:
--------------------------------------------------------------------------------
1 | # TimeField
2 |
3 | `TimeField` component provides a way to select a time while giving the freedom
4 | to style. It follows the
5 | [Native Input Time](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/time)
6 | for the keyboard navigation & accessibility features. Supports all the features
7 | of React Aria's
8 | [useTimeField](https://react-spectrum.adobe.com/react-aria/useTimeField.html#features).
9 |
10 |
11 |
12 | ## Usage
13 |
14 |
15 |
16 |
21 |
26 |
27 | ## Other Examples
28 |
29 |
34 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/docs/core-principles.md:
--------------------------------------------------------------------------------
1 | # Core Principles
2 |
3 | AdaptUI React components follows all the
4 | [basic concepts from ariakit](https://reakit.io/docs/basic-concepts/) to make it
5 | more consistent overall and also composable at the same time, understanding these
6 | concepts are essential to work with components.
7 |
8 | ### Accessibility
9 |
10 | AdaptUI React components are built with a11y in mind from the ground up by strictly
11 | following the [ARIA Authoring Practices Guide](https://www.w3.org/WAI/ARIA/apg/)
12 | and ensuring each and every component properly supports keyboard and
13 | screen-readers.
14 |
15 | ## Composable
16 |
17 | All of our components are built in a way which provides fully composability
18 | across the board thanks to Ariakit's approach to use hooks in order to built
19 | larger components.
20 |
21 | ## Stylable
22 |
23 | Components are crafted in a way that are 100% stylable with any styling
24 | solutions available which makes them a great foundation for any Design Systems.
25 |
26 | ## Extensible
27 |
28 | With composability comes extensibility, our main goal was to make the components
29 | extensible so that even everyone can built their own Component APIs on top of
30 | our components which enable components to be adoptable in Design Systems
31 |
32 | ---
33 |
34 | Ariakit concepts which we also follow :-
35 |
36 | - Composition: [reakit.io/docs/composition](https://reakit.io/docs/composition)
37 | - Accessibility:
38 | [reakit.io/docs/accessibility](https://reakit.io/docs/accessibility)
39 | - Styling: [reakit.io/docs/styling](https://reakit.io/docs/styling)
40 |
41 |
42 | Next | Codebase Overview →
43 |
44 |
--------------------------------------------------------------------------------
/index.d.ts:
--------------------------------------------------------------------------------
1 | import "@testing-library/jest-dom/extend-expect";
2 |
--------------------------------------------------------------------------------
/jest.config.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * For a detailed explanation regarding each configuration property and type check, visit:
3 | * https://jestjs.io/docs/en/configuration.html
4 | */
5 | const { join } = require("path");
6 | const pkg = require("./package.json");
7 |
8 | // eslint-disable-next-line import/no-anonymous-default-export
9 | export default {
10 | rootDir: __dirname,
11 | testEnvironment: "jsdom",
12 | displayName: pkg.name,
13 | testMatch: [join(__dirname, "src/**/*.test.{js,ts,tsx}")],
14 | setupFilesAfterEnv: ["/jest.setup.js"],
15 | moduleNameMapper: {
16 | "\\.(css|less|sass|scss)$": "/src/__mocks__/styleMock.js",
17 | "^@shared(.*)$": "/shared$1",
18 | },
19 | coveragePathIgnorePatterns: [
20 | "node_modules",
21 | "__mocks__",
22 | "stories",
23 | "/src/meter/__examples__/index.ts",
24 | "/src/meter/__examples__/__tests__/statehook-test-data.ts",
25 | ],
26 | clearMocks: true,
27 | };
28 |
--------------------------------------------------------------------------------
/jest.setup.js:
--------------------------------------------------------------------------------
1 | const {
2 | matcherHint,
3 | printReceived,
4 | printExpected,
5 | } = require("jest-matcher-utils");
6 | const { toHaveNoViolations: axeMatchers } = require("jest-axe");
7 | const matchers = require("@testing-library/jest-dom/matchers");
8 |
9 | // Consider [aria-activedescendant="${id}"] #${id} as the focused element.
10 | function toHaveFocus(element) {
11 | const result = matchers.toHaveFocus.call(this, element);
12 | const { activeElement } = element.ownerDocument;
13 | const activeId =
14 | activeElement && activeElement.getAttribute("aria-activedescendant");
15 | return {
16 | ...result,
17 | pass: result.pass || activeId === element.id,
18 | message: () => {
19 | if (activeId) {
20 | return [
21 | matcherHint(`${this.isNot ? ".not" : ""}.toHaveFocus`, "element", ""),
22 | "",
23 | "Expected:",
24 | ` ${printExpected(element)}`,
25 | "Received:",
26 | ` ${printReceived(element.ownerDocument.getElementById(activeId))}`,
27 | ].join("\n");
28 | }
29 | return result.message();
30 | },
31 | };
32 | }
33 |
34 | expect.extend({ ...matchers, ...axeMatchers, toHaveFocus });
35 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const scopify = require("postcss-scopify");
3 | const { kebabCase } = require("lodash");
4 |
5 | function rewriteRootRule() {
6 | return root => {
7 | root.walkRules(rule => {
8 | rule.selectors = rule.selectors.map(selector => {
9 | if (selector === ":root") {
10 | return "&";
11 | }
12 | return selector;
13 | });
14 | });
15 | };
16 | }
17 |
18 | function addIdScope() {
19 | return root => {
20 | const filename = root.source.input.file;
21 | const isTailwind = path.basename(path.dirname(filename)) === "tailwind";
22 |
23 | if (isTailwind) return scopify("#tailwind")(root);
24 |
25 | const basename = path.basename(filename, ".css");
26 | const id = kebabCase(basename);
27 |
28 | return scopify(`#${id}`)(root);
29 | };
30 | }
31 |
32 | module.exports = {
33 | plugins: [
34 | require("postcss-import"),
35 | require("tailwindcss"),
36 | require("postcss-flexbugs-fixes"),
37 | require("autoprefixer")({ flexbox: "no-2009" }),
38 | require("postcss-merge-selectors")({
39 | matchers: {
40 | active: {
41 | selectorFilter: /(:active|\[data-active\])/,
42 | promote: true,
43 | },
44 | focusVisible: {
45 | selectorFilter: /(:focus-visible|\[data-focus-visible\])/,
46 | promote: true,
47 | },
48 | },
49 | }),
50 | rewriteRootRule(),
51 | addIdScope(),
52 | ],
53 | };
54 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "enabled": false,
3 | "extends": ["config:base", ":semanticCommitTypeAll(chore)"],
4 | "commitMessageAction": "⬆️ update",
5 | "ignoreDeps": ["node-fetch", "chalk"],
6 | "packageRules": [
7 | {
8 | "matchDepTypes": ["devDependencies"],
9 | "matchUpdateTypes": ["patch", "minor"],
10 | "groupName": "dev dependencies (minor)"
11 | },
12 | {
13 | "matchDepTypes": ["devDependencies"],
14 | "matchUpdateTypes": ["major"],
15 | "groupName": "dev dependencies (major)"
16 | },
17 | {
18 | "matchDepTypes": ["dependencies"],
19 | "matchUpdateTypes": ["patch", "minor"],
20 | "groupName": "prod dependencies (minor)"
21 | }
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/scripts/docs/add-examples.js:
--------------------------------------------------------------------------------
1 | const strip = require("strip-comments");
2 | const prettier = require("prettier/standalone");
3 | const parserBabel = require("prettier/parser-babel");
4 |
5 | const prettierConfig = require("../../.prettierrc.js");
6 | const { joinCwd, extractCode } = require("../utils/common-utils");
7 | const { addMdContent } = require("./utils");
8 |
9 | // eslint-disable-next-line no-useless-escape
10 | const CODE_EXAMPLE_FLAG = /\<\!\-\- ADD_EXAMPLE (.*) \-\-\>/m;
11 |
12 | const addExamples = docsTemplate => {
13 | return addMdContent(docsTemplate, CODE_EXAMPLE_FLAG, (line, regexMatched) => {
14 | const importString = regexMatched[1];
15 | const importPath = joinCwd(importString);
16 | const prettifiedCode = prettier.format(strip(extractCode(importPath)), {
17 | parser: "babel",
18 | plugins: [parserBabel],
19 | ...prettierConfig,
20 | });
21 | return ["```js", "\n", prettifiedCode, "```"].join("");
22 | });
23 | };
24 |
25 | module.exports = { addExamples };
26 |
--------------------------------------------------------------------------------
/scripts/docs/add-toc.js:
--------------------------------------------------------------------------------
1 | const toc = require("markdown-toc");
2 | const { outdent } = require("outdent");
3 |
4 | // eslint-disable-next-line no-useless-escape
5 | const TOC_REPLACE_FLAG = /\<\!\-\- ADD_TOC \-\-\>/m;
6 |
7 | const addToc = docsTemplate => {
8 | const tocContents = outdent`
9 | ## Table of Contents
10 |
11 | ${toc(docsTemplate, { firsth1: false }).content}
12 | `;
13 |
14 | return docsTemplate.replace(TOC_REPLACE_FLAG, tocContents);
15 | };
16 |
17 | module.exports = { addToc };
18 |
--------------------------------------------------------------------------------
/scripts/docs/index.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const chalk = require("chalk");
3 |
4 | const {
5 | walkSync,
6 | createFile,
7 | joinCwd,
8 | readFile,
9 | } = require("../utils/common-utils");
10 | const { addToc } = require("./add-toc");
11 | const { addProps } = require("./add-props");
12 | const { addExamples } = require("./add-examples");
13 | const { addCsbLinks } = require("./add-csb-links");
14 | const { addComposition } = require("./add-composition");
15 | const { mdPrettify } = require("./utils");
16 | const { typeFootprint } = require("./typeFootPrint");
17 |
18 | const docsFolder = joinCwd("docs");
19 |
20 | const logProgress = (msg, fileName) => {
21 | console.log(chalk.red.yellow(`${msg}:`, chalk.red.greenBright(fileName)));
22 | };
23 |
24 | const generateDocs = async templateFilePath => {
25 | const fileName = path.basename(templateFilePath);
26 | const template = readFile(templateFilePath, "utf-8");
27 |
28 | const addedExamples = addExamples(template);
29 | logProgress(`Added examples`, fileName);
30 |
31 | const addedCsbLinks = await addCsbLinks(addedExamples);
32 | logProgress(`Added csb links`, fileName);
33 |
34 | const addedComposition = addComposition(addedCsbLinks);
35 | logProgress(`Added composition`, fileName);
36 |
37 | const addedProps = addProps(addedComposition);
38 | logProgress(`Added props`, fileName);
39 |
40 | const addedToc = addToc(addedProps);
41 | logProgress(`Added table of contents`, fileName);
42 |
43 | createFile(path.join(docsFolder, fileName), mdPrettify(addedToc));
44 | logProgress(`Docs generated`, fileName);
45 | };
46 |
47 | if (process.argv[2]) {
48 | const templateFile = path.join("docs-templates", `${process.argv[2]}.md`);
49 | const templateFilePath = joinCwd(templateFile);
50 |
51 | generateDocs(templateFilePath);
52 | } else {
53 | const docsTemplatesFolder = joinCwd("docs-templates");
54 |
55 | const docsTemplateFiles = walkSync(docsTemplatesFolder);
56 | docsTemplateFiles.forEach(generateDocs);
57 | }
58 |
--------------------------------------------------------------------------------
/scripts/docs/utils/add-md-content.js:
--------------------------------------------------------------------------------
1 | const addMdContent = (md, regex, callback) => {
2 | return md
3 | .split("\n")
4 | .map(line => {
5 | const flagMatch = line.match(regex);
6 | if (flagMatch) {
7 | return callback(line, flagMatch);
8 | }
9 | return line;
10 | })
11 | .join("\n");
12 | };
13 |
14 | module.exports = { addMdContent };
15 |
--------------------------------------------------------------------------------
/scripts/docs/utils/index.js:
--------------------------------------------------------------------------------
1 | const { mdPrettify } = require("./md-prettify");
2 | const { addMdContent } = require("./add-md-content");
3 |
4 | module.exports = { mdPrettify, addMdContent };
5 |
--------------------------------------------------------------------------------
/scripts/docs/utils/md-prettify.js:
--------------------------------------------------------------------------------
1 | const prettier = require("prettier/standalone");
2 | const markdownParser = require("prettier/parser-markdown");
3 |
4 | const prettierConfig = require("../../../.prettierrc.js");
5 |
6 | const mdPrettify = docsTemplate => {
7 | return prettier.format(docsTemplate, {
8 | parser: "markdown",
9 | plugins: [markdownParser],
10 | ...prettierConfig,
11 | });
12 | };
13 |
14 | module.exports = { mdPrettify };
15 |
--------------------------------------------------------------------------------
/scripts/utils/transpile-ts.js:
--------------------------------------------------------------------------------
1 | const ts = require("typescript");
2 | const prettier = require("prettier/standalone");
3 | const parserBabel = require("prettier/parser-babel");
4 |
5 | const prettierConfig = require("../../.prettierrc.js");
6 |
7 | module.exports = function transformTs(file) {
8 | const emptyLinesPreserved = file.replace(/\n$/gm, "\n/** NEWLINE **/");
9 |
10 | const { outputText } = ts.transpileModule(emptyLinesPreserved, {
11 | compilerOptions: {
12 | target: ts.ScriptTarget.ESNext,
13 | module: ts.ModuleKind.ESNext,
14 | jsx: ts.JsxEmit.Preserve,
15 | },
16 | });
17 | const emptyLinesRestored = outputText.replace(/\/\*\* NEWLINE \*\*\//g, "\n");
18 |
19 | return prettier.format(emptyLinesRestored, {
20 | parser: "babel",
21 | plugins: [parserBabel],
22 | ...prettierConfig,
23 | });
24 | };
25 |
--------------------------------------------------------------------------------
/src/accordion/__utils.ts:
--------------------------------------------------------------------------------
1 | import { createStoreContext } from "ariakit-utils/store";
2 |
3 | import { AccordionState, Item } from "./accordion-state";
4 |
5 | export const AccordionContext = createStoreContext();
6 |
7 | export const getSelectedId = (state?: AccordionState, id?: string) => {
8 | if (!id) return;
9 |
10 | if (state?.allowMultiple) return state?.selectedId?.includes(id);
11 |
12 | return state?.selectedId === id;
13 | };
14 |
15 | export const findEnabledAccordionById = (items: Item[], id?: string | null) => {
16 | return items.find(item => item.id === id && !item.disabled && !item.dimmed);
17 | };
18 |
19 | export const findFirstEnabledAccordion = (items: Item[]) => {
20 | return items.find(item => !item.disabled && !item.dimmed);
21 | };
22 |
23 | export const getAccordionId = (
24 | panels?: AccordionState["panels"],
25 | id?: string,
26 | ) => {
27 | if (!id) return;
28 |
29 | return panels?.items.find(panel => panel.id === id)?.accordionId;
30 | };
31 |
32 | export const getPanelId = (panels?: AccordionState["panels"], id?: string) => {
33 | if (!id) return;
34 | return panels?.items.find(panel => panel.accordionId === id)?.id;
35 | };
36 |
--------------------------------------------------------------------------------
/src/accordion/accordion-base.ts:
--------------------------------------------------------------------------------
1 | import {
2 | createComponent,
3 | createElement,
4 | createHook,
5 | } from "ariakit-utils/system";
6 | import { CompositeOptions, useComposite } from "ariakit";
7 | import { useStoreProvider } from "ariakit-utils/store";
8 | import { As, Props } from "ariakit-utils/types";
9 |
10 | import { AccordionContext } from "./__utils";
11 | import { AccordionState } from "./accordion-state";
12 |
13 | export const useAccordion = createHook(
14 | ({ state, ...props }) => {
15 | props = useStoreProvider({ state, ...props }, AccordionContext);
16 | props = useComposite({ state, ...props });
17 |
18 | return props;
19 | },
20 | );
21 |
22 | export const Accordion = createComponent(props => {
23 | const htmlProps = useAccordion(props);
24 |
25 | return createElement("div", htmlProps);
26 | });
27 |
28 | export type AccordionOptions = Omit<
29 | CompositeOptions,
30 | "state"
31 | > & {
32 | /**
33 | * Object returned by the `useAccordionState` hook.
34 | */
35 | state: AccordionState;
36 | };
37 |
38 | export type AccordionProps = Props>;
39 |
--------------------------------------------------------------------------------
/src/accordion/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./accordion-base";
2 | export * from "./accordion-disclosure";
3 | export * from "./accordion-panel";
4 | export * from "./accordion-state";
5 |
--------------------------------------------------------------------------------
/src/accordion/stories/AccordionBasic.component.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 |
3 | import {
4 | Accordion,
5 | AccordionDisclosure,
6 | AccordionPanel,
7 | AccordionStateProps,
8 | useAccordionState,
9 | } from "../../index";
10 |
11 | export type AccordionBasicProps = AccordionStateProps;
12 |
13 | export const AccordionBasic: React.FC = props => {
14 | const state = useAccordionState(props);
15 |
16 | return (
17 |
18 |
19 | Trigger 1
20 |
21 | Panel 1
22 |
23 | Trigger 2
24 |
25 | Panel 2
26 |
27 | Trigger 3
28 |
29 | Panel 3
30 |
31 | Trigger 4
32 |
33 | Panel 4
34 |
35 | Trigger 5
36 |
37 | Panel 5
38 |
39 | Trigger 6
40 |
41 | Panel 6
42 |
43 | );
44 | };
45 |
46 | export default AccordionBasic;
47 |
--------------------------------------------------------------------------------
/src/accordion/stories/AccordionMultiple.component.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 |
3 | import {
4 | Accordion,
5 | AccordionDisclosure,
6 | AccordionPanel,
7 | AccordionStateProps,
8 | useAccordionState,
9 | } from "../../index";
10 |
11 | export type AccordionMultipleProps = AccordionStateProps;
12 |
13 | export const AccordionMultiple: React.FC = props => {
14 | const state = useAccordionState({ allowMultiple: true, ...props });
15 |
16 | return (
17 |
18 |
19 | Trigger 1
20 |
21 | Panel 1
22 |
23 | Trigger 2
24 |
25 | Panel 2
26 |
27 | Trigger 3
28 |
29 | Panel 3
30 |
31 | Trigger 4
32 |
33 | Panel 4
34 |
35 | Trigger 5
36 |
37 | Panel 5
38 |
39 | Trigger 6
40 |
41 | Panel 6
42 |
43 | );
44 | };
45 |
46 | export default AccordionMultiple;
47 |
--------------------------------------------------------------------------------
/src/accordion/stories/AccordionMultiple.stories.tsx:
--------------------------------------------------------------------------------
1 | import { ComponentMeta, ComponentStoryObj } from "@storybook/react";
2 |
3 | import { createControls, createPreviewTabs } from "../../../.storybook/utils";
4 |
5 | import js from "./templates/AccordionMultipleJsx";
6 | import ts from "./templates/AccordionMultipleTsx";
7 | import { AccordionMultiple } from "./AccordionMultiple.component";
8 |
9 | type Meta = ComponentMeta;
10 | type Story = ComponentStoryObj;
11 |
12 | export default {
13 | title: "Accordion/Multiple",
14 | component: AccordionMultiple,
15 | parameters: {
16 | layout: "centered",
17 | preview: createPreviewTabs({ js, ts }),
18 | },
19 | argTypes: createControls({
20 | ignore: [
21 | "items",
22 | "setItems",
23 | "orientation",
24 | "virtualFocus",
25 | "rtl",
26 | "focusLoop",
27 | "focusWrap",
28 | "focusShift",
29 | "moves",
30 | "includesBaseElement",
31 | "activeId",
32 | "defaultActiveId",
33 | "setMoves",
34 | "setActiveId",
35 | "selectedId",
36 | "defaultSelectedId",
37 | "setSelectedId",
38 | "allowToggle",
39 | ],
40 | }),
41 | } as Meta;
42 |
43 | export const Default: Story = { args: {} };
44 |
45 | export const DefaultFirstIdSelected: Story = {
46 | ...Default,
47 | args: { ...Default.args, shouldSelectFirstId: true },
48 | };
49 |
50 | export const DefaultSelected: Story = {
51 | ...Default,
52 | args: { ...Default.args, defaultSelectedId: ["Trigger 3", "Trigger 4"] },
53 | };
54 |
55 | export const NoLoop: Story = {
56 | ...Default,
57 | args: { ...Default.args, focusLoop: false },
58 | };
59 |
--------------------------------------------------------------------------------
/src/breadcrumbs/breadcrumb-link.ts:
--------------------------------------------------------------------------------
1 | import {
2 | createComponent,
3 | createElement,
4 | createHook,
5 | } from "ariakit-utils/system";
6 | import { CommandOptions } from "ariakit";
7 | import { As, Props } from "ariakit-utils/types";
8 |
9 | import { useLink } from "../link";
10 |
11 | export const useBreadcrumbLink = createHook(
12 | ({ isCurrentPage, ...props }) => {
13 | props = {
14 | "aria-current": isCurrentPage && "page",
15 | ...props,
16 | };
17 |
18 | props = useLink(props);
19 |
20 | return props;
21 | },
22 | );
23 |
24 | export const BreadcrumbLink = createComponent(props => {
25 | const htmlProps = useBreadcrumbLink(props);
26 |
27 | return createElement("a", htmlProps);
28 | });
29 |
30 | export type BreadcrumbLinkOptions = CommandOptions & {
31 | /**
32 | * If true, sets `aria-current: "page"`
33 | */
34 | isCurrentPage?: boolean;
35 | };
36 |
37 | export type BreadcrumbLinkProps = Props<
38 | BreadcrumbLinkOptions
39 | >;
40 |
--------------------------------------------------------------------------------
/src/breadcrumbs/breadcrumbs-base.ts:
--------------------------------------------------------------------------------
1 | import {
2 | createComponent,
3 | createElement,
4 | createHook,
5 | } from "ariakit-utils/system";
6 | import { As, Options, Props } from "ariakit-utils/types";
7 |
8 | export const useBreadcrumbs = createHook(({ ...props }) => {
9 | props = {
10 | "aria-label": "breadcrumbs",
11 | ...props,
12 | };
13 |
14 | return props;
15 | });
16 |
17 | export const Breadcrumbs = createComponent(props => {
18 | const htmlProps = useBreadcrumbs(props);
19 |
20 | return createElement("nav", htmlProps);
21 | });
22 |
23 | export type BreadcrumbsOptions = Options & {};
24 |
25 | export type BreadcrumbsProps = Props<
26 | BreadcrumbsOptions
27 | >;
28 |
--------------------------------------------------------------------------------
/src/breadcrumbs/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./breadcrumb-link";
2 | export * from "./breadcrumbs-base";
3 |
--------------------------------------------------------------------------------
/src/breadcrumbs/stories/BreadcrumbsBasic.component.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 |
3 | import { BreadcrumbLink, Breadcrumbs, BreadcrumbsProps } from "../../index";
4 |
5 | export type BreadcrumbsBasicProps = BreadcrumbsProps & {};
6 |
7 | export const BreadcrumbsBasic: React.FC = props => {
8 | return (
9 |
10 |
11 | -
12 |
13 | ARIA Authoring Practices Guide
14 |
15 |
16 | -
17 |
18 | APG Patterns
19 |
20 |
21 | -
22 |
26 | Breadcrumb Pattern
27 |
28 |
29 | -
30 |
31 | Breadcrumb Example
32 |
33 |
34 |
35 |
36 | );
37 | };
38 |
39 | export default BreadcrumbsBasic;
40 |
--------------------------------------------------------------------------------
/src/breadcrumbs/stories/BreadcrumbsBasic.css:
--------------------------------------------------------------------------------
1 | .breadcrumb {
2 | padding: 0.8em 1em;
3 | border: 1px solid hsl(0, 0%, 90%);
4 | border-radius: 4px;
5 | background: hsl(300, 14%, 97%);
6 | }
7 |
8 | .breadcrumb ol {
9 | margin: 0;
10 | padding-left: 0;
11 | list-style: none;
12 | }
13 |
14 | .breadcrumb li {
15 | display: inline;
16 | }
17 |
18 | .breadcrumb li + li::before {
19 | display: inline-block;
20 | margin: 0 0.25em;
21 | transform: rotate(15deg);
22 | border-right: 0.1em solid currentColor;
23 | height: 0.8em;
24 | content: "";
25 | }
26 |
27 | .breadcrumb [aria-current="page"] {
28 | color: #000;
29 | font-weight: 700;
30 | text-decoration: none;
31 | }
32 |
--------------------------------------------------------------------------------
/src/breadcrumbs/stories/BreadcrumbsBasic.stories.tsx:
--------------------------------------------------------------------------------
1 | import { ComponentMeta, ComponentStoryObj } from "@storybook/react";
2 |
3 | import { createPreviewTabs } from "../../../.storybook/utils";
4 |
5 | import js from "./templates/BreadcrumbsBasicJsx";
6 | import ts from "./templates/BreadcrumbsBasicTsx";
7 | import { BreadcrumbsBasic } from "./BreadcrumbsBasic.component";
8 |
9 | import "./BreadcrumbsBasic.css";
10 |
11 | type Meta = ComponentMeta;
12 | type Story = ComponentStoryObj;
13 |
14 | export default {
15 | title: "Breadcrumbs/Basic",
16 | component: BreadcrumbsBasic,
17 | parameters: {
18 | layout: "centered",
19 | preview: createPreviewTabs({ js, ts }),
20 | },
21 | } as Meta;
22 |
23 | export const Default: Story = {};
24 |
--------------------------------------------------------------------------------
/src/calendar/calendar-base-state.ts:
--------------------------------------------------------------------------------
1 | import {
2 | CalendarState,
3 | CalendarStateOptions as CalendarStateProps,
4 | useCalendarState,
5 | } from "@react-stately/calendar";
6 |
7 | export function useCalendarBaseState(
8 | props: CalendarBaseStateProps,
9 | ): CalendarBaseState {
10 | const state = useCalendarState(props);
11 |
12 | return state;
13 | }
14 |
15 | export type CalendarBaseState = CalendarState & {};
16 |
17 | export type CalendarBaseStateProps = CalendarStateProps & {};
18 |
--------------------------------------------------------------------------------
/src/calendar/calendar-base.ts:
--------------------------------------------------------------------------------
1 | import {
2 | createComponent,
3 | createElement,
4 | createHook,
5 | } from "ariakit-utils/system";
6 | import { As, Options, Props } from "ariakit-utils/types";
7 | import { mergeProps } from "@react-aria/utils";
8 |
9 | import { CalendarState } from "./calendar-state";
10 |
11 | export const useCalendar = createHook(
12 | ({ state, ...props }) => {
13 | props = mergeProps(state.calendarProps, props);
14 |
15 | return props;
16 | },
17 | );
18 |
19 | export const Calendar = createComponent(props => {
20 | const htmlProps = useCalendar(props);
21 |
22 | return createElement("div", htmlProps);
23 | });
24 |
25 | export type CalendarOptions = Options & {
26 | /**
27 | * Object returned by the `useCalendarState` hook.
28 | */
29 | state: CalendarState;
30 | };
31 |
32 | export type CalendarProps = Props>;
33 |
--------------------------------------------------------------------------------
/src/calendar/calendar-cell-button.ts:
--------------------------------------------------------------------------------
1 | import {
2 | createComponent,
3 | createElement,
4 | createHook,
5 | } from "ariakit-utils/system";
6 | import { useForkRef } from "ariakit-utils";
7 | import { As, Options, Props } from "ariakit-utils/types";
8 | import { mergeProps } from "@react-aria/utils";
9 |
10 | import { CalendarCellState } from "./calendar-cell-state";
11 |
12 | export const useCalendarCellButton = createHook(
13 | ({ state, ...props }) => {
14 | props = { ...props, ref: useForkRef(state.ref, props.ref) };
15 | props = mergeProps(state.buttonProps, props);
16 |
17 | return props;
18 | },
19 | );
20 |
21 | export const CalendarCellButton = createComponent(
22 | props => {
23 | const htmlProps = useCalendarCellButton(props);
24 |
25 | return createElement("span", htmlProps);
26 | },
27 | );
28 |
29 | export type CalendarCellButtonOptions = Options & {
30 | /**
31 | * Object returned by the `useCalendarCellState` hook.
32 | */
33 | state: CalendarCellState;
34 | };
35 |
36 | export type CalendarCellButtonProps = Props<
37 | CalendarCellButtonOptions
38 | >;
39 |
--------------------------------------------------------------------------------
/src/calendar/calendar-cell-state.ts:
--------------------------------------------------------------------------------
1 | import { RefObject, useRef } from "react";
2 | import { CalendarDate } from "@internationalized/date";
3 | import {
4 | AriaCalendarCellProps,
5 | CalendarCellAria,
6 | useCalendarCell,
7 | } from "@react-aria/calendar";
8 |
9 | import { RangeCalendarBaseState } from "../range-calendar";
10 |
11 | import { CalendarBaseState } from "./calendar-base-state";
12 |
13 | export function useCalendarCellState({
14 | state,
15 | ...props
16 | }: CalendarCellStateProps): CalendarCellState {
17 | const ref = useRef(null);
18 | const calendarCellProps = useCalendarCell(props, state, ref);
19 |
20 | return { ...calendarCellProps, ref, baseState: state, date: props.date };
21 | }
22 |
23 | export type CalendarCellState = CalendarCellAria & {
24 | /**
25 | * Reference for the button element within the cell inside the table
26 | */
27 | ref: RefObject;
28 | /**
29 | * Object returned by the `useSliderState` hook.
30 | */
31 | baseState: CalendarBaseState | RangeCalendarBaseState;
32 | /** The date that this cell represents. */
33 | date: CalendarDate;
34 | };
35 |
36 | export type CalendarCellStateProps = AriaCalendarCellProps & {
37 | /**
38 | * Object returned by the `useCalendarBaseState` & `RangeCalendarBaseState` hook.
39 | */
40 | state: CalendarBaseState | RangeCalendarBaseState;
41 | };
42 |
--------------------------------------------------------------------------------
/src/calendar/calendar-grid-state.ts:
--------------------------------------------------------------------------------
1 | import {
2 | AriaCalendarGridProps,
3 | CalendarGridAria,
4 | useCalendarGrid,
5 | } from "@react-aria/calendar";
6 |
7 | import { RangeCalendarBaseState } from "../range-calendar";
8 |
9 | import { CalendarBaseState } from "./calendar-base-state";
10 |
11 | export function useCalendarGridState({
12 | state,
13 | ...props
14 | }: CalendarGridStateProps): CalendarGridState {
15 | const calendarGridProps = useCalendarGrid(props, state);
16 |
17 | return calendarGridProps;
18 | }
19 |
20 | export type CalendarGridState = CalendarGridAria;
21 |
22 | export type CalendarGridStateProps = AriaCalendarGridProps & {
23 | /**
24 | * Object returned by the `useCalendarBaseState` & `RangeCalendarBaseState` hook.
25 | */
26 | state: CalendarBaseState | RangeCalendarBaseState;
27 | };
28 |
--------------------------------------------------------------------------------
/src/calendar/calendar-grid.ts:
--------------------------------------------------------------------------------
1 | import {
2 | createComponent,
3 | createElement,
4 | createHook,
5 | } from "ariakit-utils/system";
6 | import { As, Options, Props } from "ariakit-utils/types";
7 | import { mergeProps } from "@react-aria/utils";
8 |
9 | import { CalendarGridState } from "./calendar-grid-state";
10 |
11 | export const useCalendarGrid = createHook(
12 | ({ state, ...props }) => {
13 | props = mergeProps(state.gridProps, props);
14 |
15 | return props;
16 | },
17 | );
18 |
19 | export const CalendarGrid = createComponent(props => {
20 | const htmlProps = useCalendarGrid(props);
21 |
22 | return createElement("table", htmlProps);
23 | });
24 |
25 | export type CalendarGridOptions = Options & {
26 | /**
27 | * Object returned by the `useCalendarGridState` hook.
28 | */
29 | state: CalendarGridState;
30 | };
31 |
32 | export type CalendarGridProps = Props<
33 | CalendarGridOptions
34 | >;
35 |
--------------------------------------------------------------------------------
/src/calendar/calendar-next-button.ts:
--------------------------------------------------------------------------------
1 | import { useRef } from "react";
2 | import {
3 | createComponent,
4 | createElement,
5 | createHook,
6 | } from "ariakit-utils/system";
7 | import { useForkRef } from "ariakit-utils";
8 | import { As, Options, Props } from "ariakit-utils/types";
9 | import { useButton } from "@react-aria/button";
10 | import { mergeProps } from "@react-aria/utils";
11 |
12 | import { RangeCalendarState } from "../range-calendar";
13 |
14 | import { CalendarState } from "./calendar-state";
15 |
16 | export const useCalendarNextButton = createHook(
17 | ({ state, ...props }) => {
18 | const ref = useRef(null);
19 | props = { ...props, ref: useForkRef(ref, props.ref) };
20 | const { buttonProps } = useButton(state.nextButtonProps, ref);
21 | props = mergeProps(buttonProps, props);
22 |
23 | return props;
24 | },
25 | );
26 |
27 | export const CalendarNextButton = createComponent(
28 | props => {
29 | const htmlProps = useCalendarNextButton(props);
30 |
31 | return createElement("button", htmlProps);
32 | },
33 | );
34 |
35 | export type CalendarNextButtonOptions = Options & {
36 | /**
37 | * Object returned by the `useCalendarState` & `RangeCalendarState` hook.
38 | */
39 | state: CalendarState | RangeCalendarState;
40 | };
41 |
42 | export type CalendarNextButtonProps = Props<
43 | CalendarNextButtonOptions
44 | >;
45 |
--------------------------------------------------------------------------------
/src/calendar/calendar-prev-button.ts:
--------------------------------------------------------------------------------
1 | import { useRef } from "react";
2 | import {
3 | createComponent,
4 | createElement,
5 | createHook,
6 | } from "ariakit-utils/system";
7 | import { useForkRef } from "ariakit-utils";
8 | import { As, Options, Props } from "ariakit-utils/types";
9 | import { useButton } from "@react-aria/button";
10 | import { mergeProps } from "@react-aria/utils";
11 |
12 | import { RangeCalendarState } from "../range-calendar";
13 |
14 | import { CalendarState } from "./calendar-state";
15 |
16 | export const useCalendarPreviousButton =
17 | createHook(({ state, ...props }) => {
18 | const ref = useRef(null);
19 | props = { ...props, ref: useForkRef(ref, props.ref) };
20 | const { buttonProps } = useButton(state.prevButtonProps, ref);
21 | props = mergeProps(buttonProps, props);
22 |
23 | return props;
24 | });
25 |
26 | export const CalendarPreviousButton =
27 | createComponent(props => {
28 | const htmlProps = useCalendarPreviousButton(props);
29 |
30 | return createElement("button", htmlProps);
31 | });
32 |
33 | export type CalendarPreviousButtonOptions =
34 | Options & {
35 | /**
36 | * Object returned by the `useCalendarState` & `RangeCalendarState` hook.
37 | */
38 | state: CalendarState | RangeCalendarState;
39 | };
40 |
41 | export type CalendarPreviousButtonProps = Props<
42 | CalendarPreviousButtonOptions
43 | >;
44 |
--------------------------------------------------------------------------------
/src/calendar/calendar-state.ts:
--------------------------------------------------------------------------------
1 | import { CalendarAria, useCalendar } from "@react-aria/calendar";
2 | import { CalendarProps, DateValue } from "@react-types/calendar";
3 |
4 | import { CalendarBaseState } from "./calendar-base-state";
5 |
6 | export function useCalendarState({
7 | state,
8 | ...props
9 | }: CalendarStateProps): CalendarState {
10 | const calendarProps = useCalendar(props, state);
11 |
12 | return calendarProps;
13 | }
14 |
15 | export type CalendarState = CalendarAria & {};
16 |
17 | export type CalendarStateProps = CalendarProps & {
18 | /**
19 | * Object returned by the `useCalendarBaseState` hook.
20 | */
21 | state: CalendarBaseState;
22 | };
23 |
--------------------------------------------------------------------------------
/src/calendar/calendar-title.ts:
--------------------------------------------------------------------------------
1 | import {
2 | createComponent,
3 | createElement,
4 | createHook,
5 | } from "ariakit-utils/system";
6 | import { As, Options, Props } from "ariakit-utils/types";
7 |
8 | import { CalendarState } from "./calendar-state";
9 |
10 | export const useCalendarTitle = createHook(
11 | ({ state, ...props }) => {
12 | props = { children: state.title, ...props };
13 |
14 | return props;
15 | },
16 | );
17 |
18 | export const CalendarTitle = createComponent(props => {
19 | const htmlProps = useCalendarTitle(props);
20 |
21 | return createElement("h2", htmlProps);
22 | });
23 |
24 | export type CalendarTitleOptions = Options & {
25 | /**
26 | * Object returned by the `useCalendarState` hook.
27 | */
28 | state: CalendarState;
29 | };
30 |
31 | export type CalendarTitleProps = Props<
32 | CalendarTitleOptions
33 | >;
34 |
--------------------------------------------------------------------------------
/src/calendar/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./calendar-base";
2 | export * from "./calendar-base-state";
3 | export * from "./calendar-cell";
4 | export * from "./calendar-cell-button";
5 | export * from "./calendar-cell-state";
6 | export * from "./calendar-grid";
7 | export * from "./calendar-grid-state";
8 | export * from "./calendar-next-button";
9 | export * from "./calendar-prev-button";
10 | export * from "./calendar-state";
11 | export * from "./calendar-title";
12 |
--------------------------------------------------------------------------------
/src/calendar/stories/CalendarBasic.stories.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { ComponentMeta } from "@storybook/react";
3 |
4 | import { createPreviewTabs } from "../../../.storybook/utils";
5 |
6 | import css from "./templates/CalendarBasicCss";
7 | import js from "./templates/CalendarBasicJsx";
8 | import ts from "./templates/CalendarBasicTsx";
9 | import jsUtils from "./templates/UtilsJsx";
10 | import tsUtils from "./templates/UtilsTsx";
11 | import { CalendarBasic } from "./CalendarBasic.component";
12 |
13 | import "./CalendarBasic.css";
14 |
15 | type Meta = ComponentMeta;
16 | // type Story = ComponentStoryObj;
17 |
18 | export default {
19 | title: "Calendar/Basic",
20 | component: CalendarBasic,
21 | parameters: {
22 | layout: "centered",
23 | preview: createPreviewTabs({ js, ts, css, jsUtils, tsUtils }),
24 | },
25 | } as Meta;
26 |
27 | export const Default = () => {
28 | return ;
29 | };
30 |
--------------------------------------------------------------------------------
/src/calendar/stories/CalendarStyled.stories.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { ComponentMeta } from "@storybook/react";
3 |
4 | import { createPreviewTabs } from "../../../.storybook/utils";
5 |
6 | import js from "./templates/CalendarStyledJsx";
7 | import ts from "./templates/CalendarStyledTsx";
8 | import jsUtils from "./templates/UtilsJsx";
9 | import tsUtils from "./templates/UtilsTsx";
10 | import { CalendarStyled } from "./CalendarStyled.component";
11 |
12 | import "./tailwind.css";
13 |
14 | type Meta = ComponentMeta;
15 | // type Story = ComponentStoryObj;
16 |
17 | export default {
18 | title: "Calendar/Styled",
19 | component: CalendarStyled,
20 | parameters: {
21 | layout: "centered",
22 | preview: createPreviewTabs({ js, ts, jsUtils, tsUtils }),
23 | },
24 | decorators: [
25 | Story => {
26 | document.body.id = "tailwind";
27 | return ;
28 | },
29 | ],
30 | } as Meta;
31 |
32 | export const Default = () => {
33 | return ;
34 | };
35 |
--------------------------------------------------------------------------------
/src/calendar/stories/Utils.component.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 |
3 | export const ChevronLeft = (props: React.SVGProps) => {
4 | return (
5 |
19 | );
20 | };
21 |
22 | export const ChevronRight = (props: React.SVGProps) => (
23 |
24 | );
25 |
--------------------------------------------------------------------------------
/src/calendar/stories/tailwind.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | @layer components {
6 | .styled-datepicker .calendar__cell {
7 | height: 32px;
8 | width: 32px;
9 | max-height: 32px;
10 | max-width: 32px;
11 | @apply text-sm text-center rounded-lg;
12 | }
13 | .styled-datepicker .calendar__cell[data-is-range-selection] {
14 | @apply bg-blue-100 rounded-none text-gray-800 !important;
15 | }
16 | .styled-datepicker .calendar__cell[data-is-selection-start] {
17 | @apply bg-blue-500 rounded-l-lg text-white !important;
18 | }
19 | .styled-datepicker .calendar__cell[data-is-selection-end] {
20 | @apply bg-blue-500 rounded-r-lg text-white !important;
21 | }
22 |
23 | .styled-datepicker .calendar__cell[data-is-range-selection]:focus-within {
24 | @apply bg-blue-400 text-white !important;
25 | }
26 | .styled-datepicker .calendar__cell:focus-within {
27 | @apply bg-gray-100;
28 | }
29 |
30 | .styled-datepicker.calendar [data-weekend] {
31 | @apply text-red-600;
32 | }
33 |
34 | .styled-datepicker.calendar [aria-selected="true"] {
35 | @apply text-white bg-blue-500;
36 | }
37 |
38 | .styled-datepicker.calendar [aria-selected]:focus-within {
39 | @apply bg-gray-100;
40 | }
41 |
42 | .styled-datepicker.calendar [aria-selected="true"]:focus-within {
43 | @apply text-white bg-blue-400;
44 | }
45 |
46 | .styled-datepicker.calendar [aria-disabled="true"] {
47 | @apply text-gray-500;
48 | }
49 |
50 | .styled-datepicker.calendar span {
51 | outline: none;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/datefield/date-segment.ts:
--------------------------------------------------------------------------------
1 | import { useRef } from "react";
2 | import {
3 | createComponent,
4 | createElement,
5 | createHook,
6 | } from "ariakit-utils/system";
7 | import { useForkRef } from "ariakit-utils";
8 | import { As, Options, Props } from "ariakit-utils/types";
9 | import { useDateSegment as useAriaDateSegment } from "@react-aria/datepicker";
10 | import { mergeProps } from "@react-aria/utils";
11 | import { DateSegment as DateSegmentState } from "@react-stately/datepicker";
12 |
13 | import { DateFieldBaseState } from "./datefield-base-state";
14 |
15 | export const useDateSegment = createHook(
16 | ({ state, segment, ...props }) => {
17 | const ref = useRef(null);
18 |
19 | props = { ...props, ref: useForkRef(ref, props.ref) };
20 | const { segmentProps } = useAriaDateSegment(segment, state, ref);
21 | props = mergeProps(segmentProps, props);
22 |
23 | return props;
24 | },
25 | );
26 |
27 | export const DateSegment = createComponent(props => {
28 | const htmlProps = useDateSegment(props);
29 |
30 | return createElement("div", htmlProps);
31 | });
32 |
33 | export type DateSegmentOptions = Options & {
34 | /**
35 | * Current segment state return from `state.segments`.
36 | */
37 | segment: DateSegmentState;
38 | /**
39 | * Object returned by the `useDateFieldBaseState` hook.
40 | */
41 | state: DateFieldBaseState;
42 | };
43 |
44 | export type DateSegmentProps = Props<
45 | DateSegmentOptions
46 | >;
47 |
--------------------------------------------------------------------------------
/src/datefield/datefield-base-state.ts:
--------------------------------------------------------------------------------
1 | import {
2 | DateFieldState,
3 | DateFieldStateOptions as DateFieldStateProps,
4 | useDateFieldState,
5 | } from "@react-stately/datepicker";
6 |
7 | export function useDateFieldBaseState(
8 | props: DateFieldBaseStateProps,
9 | ): DateFieldBaseState {
10 | const state = useDateFieldState(props);
11 |
12 | return state;
13 | }
14 |
15 | export type DateFieldBaseState = DateFieldState & {};
16 |
17 | export type DateFieldBaseStateProps = DateFieldStateProps & {};
18 |
--------------------------------------------------------------------------------
/src/datefield/datefield-base.ts:
--------------------------------------------------------------------------------
1 | import {
2 | createComponent,
3 | createElement,
4 | createHook,
5 | } from "ariakit-utils/system";
6 | import { useForkRef } from "ariakit-utils";
7 | import { As, Options, Props } from "ariakit-utils/types";
8 | import { mergeProps } from "@react-aria/utils";
9 |
10 | import { DateFieldState } from "./datefield-state";
11 |
12 | export const useDateField = createHook(
13 | ({ state, ...props }) => {
14 | props = { ...props, ref: useForkRef(state.ref, props.ref) };
15 | props = mergeProps(state.fieldProps, props);
16 |
17 | return props;
18 | },
19 | );
20 |
21 | export const DateField = createComponent(props => {
22 | const htmlProps = useDateField(props);
23 |
24 | return createElement("div", htmlProps);
25 | });
26 |
27 | export type DateFieldOptions = Options & {
28 | /**
29 | * Object returned by the `useDateFieldState` hook.
30 | */
31 | state: DateFieldState;
32 | };
33 |
34 | export type DateFieldProps = Props>;
35 |
--------------------------------------------------------------------------------
/src/datefield/datefield-description.ts:
--------------------------------------------------------------------------------
1 | import {
2 | createComponent,
3 | createElement,
4 | createHook,
5 | } from "ariakit-utils/system";
6 | import { As, Options, Props } from "ariakit-utils/types";
7 | import { mergeProps } from "@react-aria/utils";
8 |
9 | import { DateFieldState } from "./datefield-state";
10 |
11 | export const useDateFieldDescription = createHook(
12 | ({ state, ...props }) => {
13 | props = mergeProps(state.descriptionProps, props);
14 |
15 | return props;
16 | },
17 | );
18 |
19 | export const DateFieldDescription =
20 | createComponent(props => {
21 | const htmlProps = useDateFieldDescription(props);
22 |
23 | return createElement("label", htmlProps);
24 | });
25 |
26 | export type DateFieldDescriptionOptions = Options & {
27 | /**
28 | * Object returned by the `useDateFieldState` hook.
29 | */
30 | state: DateFieldState;
31 | };
32 |
33 | export type DateFieldDescriptionProps = Props<
34 | DateFieldDescriptionOptions
35 | >;
36 |
--------------------------------------------------------------------------------
/src/datefield/datefield-errormessage.ts:
--------------------------------------------------------------------------------
1 | import {
2 | createComponent,
3 | createElement,
4 | createHook,
5 | } from "ariakit-utils/system";
6 | import { As, Options, Props } from "ariakit-utils/types";
7 | import { mergeProps } from "@react-aria/utils";
8 |
9 | import { DateFieldState } from "./datefield-state";
10 |
11 | export const useDateFieldErrorMessage =
12 | createHook(({ state, ...props }) => {
13 | props = mergeProps(state.errorMessageProps, props);
14 |
15 | return props;
16 | });
17 |
18 | export const DateFieldErrorMessage =
19 | createComponent(props => {
20 | const htmlProps = useDateFieldErrorMessage(props);
21 |
22 | return createElement("label", htmlProps);
23 | });
24 |
25 | export type DateFieldErrorMessageOptions =
26 | Options & {
27 | /**
28 | * Object returned by the `useDateFieldState` hook.
29 | */
30 | state: DateFieldState;
31 | };
32 |
33 | export type DateFieldErrorMessageProps = Props<
34 | DateFieldErrorMessageOptions
35 | >;
36 |
--------------------------------------------------------------------------------
/src/datefield/datefield-label.ts:
--------------------------------------------------------------------------------
1 | import {
2 | createComponent,
3 | createElement,
4 | createHook,
5 | } from "ariakit-utils/system";
6 | import { As, Options, Props } from "ariakit-utils/types";
7 | import { mergeProps } from "@react-aria/utils";
8 |
9 | import { DateFieldState } from "./datefield-state";
10 |
11 | export const useDateFieldLabel = createHook(
12 | ({ state, ...props }) => {
13 | props = mergeProps(state.labelProps, props);
14 |
15 | return props;
16 | },
17 | );
18 |
19 | export const DateFieldLabel = createComponent(props => {
20 | const htmlProps = useDateFieldLabel(props);
21 |
22 | return createElement("span", htmlProps);
23 | });
24 |
25 | export type DateFieldLabelOptions = Options & {
26 | /**
27 | * Object returned by the `useDateFieldState` hook.
28 | */
29 | state: DateFieldState;
30 | };
31 |
32 | export type DateFieldLabelProps = Props<
33 | DateFieldLabelOptions
34 | >;
35 |
--------------------------------------------------------------------------------
/src/datefield/datefield-state.ts:
--------------------------------------------------------------------------------
1 | import { RefObject, useRef } from "react";
2 | import { DateValue } from "@internationalized/date";
3 | import {
4 | AriaDateFieldProps,
5 | DateFieldAria,
6 | useDateField,
7 | } from "@react-aria/datepicker";
8 |
9 | import { DateFieldBaseState } from "./datefield-base-state";
10 |
11 | export function useDateFieldState({
12 | state,
13 | ...props
14 | }: DateFieldStateProps): DateFieldState {
15 | const ref = useRef(null);
16 | const datefield = useDateField(props, state, ref);
17 |
18 | return { ...datefield, ref };
19 | }
20 |
21 | export type DateFieldState = DateFieldAria & {
22 | /**
23 | * Reference for the date picker's visible label element, if any.
24 | */
25 | ref: RefObject;
26 | };
27 |
28 | export type DateFieldStateProps = AriaDateFieldProps & {
29 | /**
30 | * Object returned by the `useDateFieldBaseState` hook.
31 | */
32 | state: DateFieldBaseState;
33 | };
34 |
--------------------------------------------------------------------------------
/src/datefield/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./date-segment";
2 | export * from "./datefield-base";
3 | export * from "./datefield-base-state";
4 | export * from "./datefield-description";
5 | export * from "./datefield-errormessage";
6 | export * from "./datefield-label";
7 | export * from "./datefield-state";
8 |
--------------------------------------------------------------------------------
/src/datefield/stories/DateFieldBasic.component.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { createCalendar } from "@internationalized/date";
3 | import { useLocale } from "@react-aria/i18n";
4 |
5 | import {
6 | DateField,
7 | DateFieldBaseStateProps,
8 | DateFieldLabel,
9 | DateSegment,
10 | useDateFieldBaseState,
11 | useDateFieldState,
12 | } from "../../index";
13 |
14 | export type DateFieldBasicProps = Omit<
15 | DateFieldBaseStateProps,
16 | "locale" | "createCalendar"
17 | > & {};
18 |
19 | // Example from https://react-spectrum.adobe.com/react-aria/useDateField.html
20 | export const DateFieldBasic: React.FC = props => {
21 | let { locale } = useLocale();
22 |
23 | const state = useDateFieldBaseState({ locale, createCalendar, ...props });
24 | const datefield = useDateFieldState({ ...props, state });
25 |
26 | return (
27 |
28 |
29 | {props.label}
30 |
31 |
32 | {state.segments.map((segment, i) => (
33 |
41 | {segment.text}
42 |
43 | ))}
44 | {state.validationState === "invalid" && (
45 | 🚫
46 | )}
47 |
48 |
49 | );
50 | };
51 |
52 | export default DateFieldBasic;
53 |
--------------------------------------------------------------------------------
/src/datefield/stories/DateFieldBasic.css:
--------------------------------------------------------------------------------
1 | * {
2 | box-sizing: border-box;
3 | }
4 |
5 | .datefield {
6 | display: flex;
7 | flex-direction: column;
8 | align-items: flex-start;
9 | }
10 |
11 | .datefield__label {
12 | display: inline-flex;
13 | margin-bottom: 0.5rem;
14 | }
15 |
16 | .datefield__field {
17 | display: inline-flex;
18 | padding: 2px 4px;
19 | border-radius: 2px;
20 | border: 1px solid #6f6f6f;
21 | background: #fff;
22 | }
23 |
24 | .datefield__field--item {
25 | padding: 0 2px;
26 | border-radius: 4px;
27 | font-variant-numeric: tabular-nums;
28 | text-align: end;
29 | }
30 |
31 | .datefield__field--item.placeholder {
32 | color: #767676;
33 | }
34 |
35 | .datefield__field--item:focus {
36 | background-color: #1e65fd;
37 | outline: none;
38 | color: white;
39 | }
40 |
--------------------------------------------------------------------------------
/src/datefield/stories/DateFieldBasic.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { ComponentMeta } from "@storybook/react";
3 |
4 | import { createPreviewTabs } from "../../../.storybook/utils";
5 |
6 | import css from "./templates/DateFieldBasicCss";
7 | import js from "./templates/DateFieldBasicJsx";
8 | import ts from "./templates/DateFieldBasicTsx";
9 | import { DateFieldBasic } from "./DateFieldBasic.component";
10 |
11 | import "./DateFieldBasic.css";
12 |
13 | type Meta = ComponentMeta;
14 | // type Story = ComponentStoryObj;
15 |
16 | export default {
17 | title: "DateField/Basic",
18 | component: DateFieldBasic,
19 | parameters: {
20 | layout: "centered",
21 | preview: createPreviewTabs({ js, ts, css }),
22 | },
23 | } as Meta;
24 |
25 | export const Default = () => {
26 | return ;
27 | };
28 |
--------------------------------------------------------------------------------
/src/datefield/stories/DateFieldStyled.component.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { createCalendar } from "@internationalized/date";
3 | import { useLocale } from "@react-aria/i18n";
4 |
5 | import {
6 | DateField,
7 | DateFieldBaseStateProps,
8 | DateFieldLabel,
9 | DateSegment,
10 | useDateFieldBaseState,
11 | useDateFieldState,
12 | } from "../../index";
13 |
14 | export type DateFieldStyledProps = Omit<
15 | DateFieldBaseStateProps,
16 | "locale" | "createCalendar"
17 | > & {};
18 |
19 | export const DateFieldStyled: React.FC = props => {
20 | let { locale } = useLocale();
21 |
22 | const state = useDateFieldBaseState({ locale, createCalendar, ...props });
23 | const datefield = useDateFieldState({ ...props, state });
24 |
25 | return (
26 |
27 |
28 | {props.label}
29 |
30 |
34 | {state.segments.map((segment, i) => (
35 |
43 | {segment.text}
44 |
45 | ))}
46 |
47 |
48 | );
49 | };
50 |
51 | export default DateFieldStyled;
52 |
--------------------------------------------------------------------------------
/src/datefield/stories/DateFieldStyled.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { ComponentMeta } from "@storybook/react";
3 |
4 | import { createPreviewTabs } from "../../../.storybook/utils";
5 |
6 | import js from "./templates/DateFieldStyledJsx";
7 | import ts from "./templates/DateFieldStyledTsx";
8 | import { DateFieldStyled } from "./DateFieldStyled.component";
9 |
10 | import "./tailwind.css";
11 |
12 | type Meta = ComponentMeta;
13 | // type Story = ComponentStoryObj;
14 |
15 | export default {
16 | title: "DateField/Styled",
17 | component: DateFieldStyled,
18 | parameters: {
19 | layout: "centered",
20 | preview: createPreviewTabs({ js, ts }),
21 | },
22 | decorators: [
23 | Story => {
24 | document.body.id = "tailwind";
25 | return ;
26 | },
27 | ],
28 | } as Meta;
29 |
30 | export const Default = () => {
31 | return ;
32 | };
33 |
--------------------------------------------------------------------------------
/src/datefield/stories/tailwind.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/src/datepicker/datepicker-base-state.ts:
--------------------------------------------------------------------------------
1 | import { PopoverState, PopoverStateProps, usePopoverState } from "ariakit";
2 | import {
3 | DatePickerState,
4 | DatePickerStateOptions as DatePickerStateProps,
5 | useDatePickerState,
6 | } from "@react-stately/datepicker";
7 |
8 | export function useDatePickerBaseState(
9 | props: DatePickerBaseStateProps,
10 | ): DatePickerBaseState {
11 | const datepicker = useDatePickerState(props);
12 | const { isOpen, setOpen } = datepicker;
13 | console.log("%cisOpen", "color: #007300", isOpen);
14 |
15 | const popover = usePopoverState({
16 | open: isOpen,
17 | setOpen: setOpen,
18 | ...props,
19 | });
20 |
21 | return { datepicker, popover };
22 | }
23 |
24 | export type DatePickerBaseState = {
25 | /**
26 | * Object returned by the `useDatePickerState` hook.
27 | */
28 | datepicker: DatePickerState;
29 | /**
30 | * Object returned by the `usePopoverState` hook.
31 | */
32 | popover: PopoverState;
33 | };
34 |
35 | export type DatePickerBaseStateProps = DatePickerStateProps &
36 | PopoverStateProps & {};
37 |
--------------------------------------------------------------------------------
/src/datepicker/datepicker-disclosure.ts:
--------------------------------------------------------------------------------
1 | import { useRef } from "react";
2 | import {
3 | createComponent,
4 | createElement,
5 | createHook,
6 | } from "ariakit-utils/system";
7 | import { PopoverDisclosureOptions, usePopoverDisclosure } from "ariakit";
8 | import { useForkRef } from "ariakit-utils";
9 | import { As, Props } from "ariakit-utils/types";
10 | import { useButton } from "@react-aria/button";
11 |
12 | import { DateRangePickerState } from "../daterange-picker";
13 |
14 | import { DatePickerState } from "./datepicker-state";
15 |
16 | export const useDatePickerDisclosure = createHook(
17 | ({ state, ...props }) => {
18 | const ref = useRef(null);
19 | props = { ...props, ref: useForkRef(ref, props.ref, state.ref) };
20 |
21 | props = usePopoverDisclosure({ ...props, state: state.baseState.popover });
22 |
23 | const { buttonProps } = useButton(state.buttonProps, ref);
24 | props = { ...props, ...buttonProps };
25 |
26 | return props;
27 | },
28 | );
29 |
30 | export const DatePickerDisclosure =
31 | createComponent(props => {
32 | const htmlProps = useDatePickerDisclosure(props);
33 |
34 | return createElement("button", htmlProps);
35 | });
36 |
37 | export type DatePickerDisclosureOptions = Omit<
38 | PopoverDisclosureOptions,
39 | "state"
40 | > & {
41 | /**
42 | * Object returned by the `useDatePickerState` & `useDateRangePickerState` hook.
43 | */
44 | state: DatePickerState | DateRangePickerState;
45 | };
46 |
47 | export type DatePickerDisclosureProps = Props<
48 | DatePickerDisclosureOptions
49 | >;
50 |
--------------------------------------------------------------------------------
/src/datepicker/datepicker-group.ts:
--------------------------------------------------------------------------------
1 | import {
2 | createComponent,
3 | createElement,
4 | createHook,
5 | } from "ariakit-utils/system";
6 | import { useForkRef } from "ariakit-utils";
7 | import { As, Options, Props } from "ariakit-utils/types";
8 | import { mergeProps } from "@react-aria/utils";
9 |
10 | import { DateRangePickerState } from "../daterange-picker";
11 |
12 | import { DatePickerState } from "./datepicker-state";
13 |
14 | export const useDatePickerGroup = createHook(
15 | ({ state, ...props }) => {
16 | props = { ...props, ref: useForkRef(state.ref, props.ref) };
17 | props = mergeProps(state.groupProps, props);
18 |
19 | return props;
20 | },
21 | );
22 |
23 | export const DatePickerGroup = createComponent(
24 | props => {
25 | const htmlProps = useDatePickerGroup(props);
26 |
27 | return createElement("div", htmlProps);
28 | },
29 | );
30 |
31 | export type DatePickerGroupOptions = Options & {
32 | /**
33 | * Object returned by the `useDatePickerState` & `useDateRangePickerState` hook.
34 | */
35 | state: DatePickerState | DateRangePickerState;
36 | };
37 |
38 | export type DatePickerGroupProps = Props<
39 | DatePickerGroupOptions
40 | >;
41 |
--------------------------------------------------------------------------------
/src/datepicker/datepicker-label.ts:
--------------------------------------------------------------------------------
1 | import {
2 | createComponent,
3 | createElement,
4 | createHook,
5 | } from "ariakit-utils/system";
6 | import { As, Options, Props } from "ariakit-utils/types";
7 | import { mergeProps } from "@react-aria/utils";
8 |
9 | import { DateRangePickerState } from "../daterange-picker";
10 |
11 | import { DatePickerState } from "./datepicker-state";
12 |
13 | export const useDatePickerLabel = createHook(
14 | ({ state, ...props }) => {
15 | props = mergeProps(state.labelProps, props);
16 |
17 | return props;
18 | },
19 | );
20 |
21 | export const DatePickerLabel = createComponent(
22 | props => {
23 | const htmlProps = useDatePickerLabel(props);
24 |
25 | return createElement("span", htmlProps);
26 | },
27 | );
28 |
29 | export type DatePickerLabelOptions = Options & {
30 | /**
31 | * Object returned by the `useDatePickerState` & `useDateRangePickerState` hook.
32 | */
33 | state: DatePickerState | DateRangePickerState;
34 | };
35 |
36 | export type DatePickerLabelProps = Props<
37 | DatePickerLabelOptions
38 | >;
39 |
--------------------------------------------------------------------------------
/src/datepicker/datepicker-popover.ts:
--------------------------------------------------------------------------------
1 | import {
2 | createComponent,
3 | createElement,
4 | createHook,
5 | } from "ariakit-utils/system";
6 | import { PopoverOptions, usePopover } from "ariakit";
7 | import { useEvent } from "ariakit-utils";
8 | import { As, Props } from "ariakit-utils/types";
9 | import { mergeProps } from "@react-aria/utils";
10 |
11 | import { DateRangePickerState } from "../daterange-picker";
12 |
13 | import { DatePickerState } from "./datepicker-state";
14 |
15 | export const useDatePickerPopover = createHook(
16 | ({ state, ...props }) => {
17 | const onKeyDownProp = props.onKeyDown;
18 |
19 | const onKeyDown = useEvent((event: React.KeyboardEvent) => {
20 | onKeyDownProp?.(event);
21 |
22 | if (event.key !== "Escape") return;
23 | state.baseState.popover.hide();
24 |
25 | if (event.defaultPrevented) return;
26 | });
27 |
28 | props = usePopover({
29 | ...props,
30 | state: state.baseState.popover,
31 | modal: true,
32 | autoFocusOnShow: false,
33 | backdropProps: { onKeyDown },
34 | });
35 | props = mergeProps(state.dialogProps, props);
36 |
37 | return props;
38 | },
39 | );
40 |
41 | export const DatePickerPopover = createComponent(
42 | props => {
43 | const htmlProps = useDatePickerPopover(props);
44 |
45 | return createElement("span", htmlProps);
46 | },
47 | );
48 |
49 | export type DatePickerPopoverOptions = Omit<
50 | PopoverOptions,
51 | "state"
52 | > & {
53 | /**
54 | * Object returned by the `useDatePickerState` & `useDateRangePickerState` hook.
55 | */
56 | state: DatePickerState | DateRangePickerState;
57 | };
58 |
59 | export type DatePickerPopoverProps = Props<
60 | DatePickerPopoverOptions
61 | >;
62 |
--------------------------------------------------------------------------------
/src/datepicker/datepicker-state.ts:
--------------------------------------------------------------------------------
1 | import { RefObject, useRef } from "react";
2 | import { DateValue } from "@internationalized/date";
3 | import { DatePickerAria, useDatePicker } from "@react-aria/datepicker";
4 | import { AriaDatePickerProps } from "@react-types/datepicker";
5 |
6 | import { DatePickerBaseState } from "./datepicker-base-state";
7 |
8 | export function useDatePickerState({
9 | state,
10 | ...props
11 | }: DatePickerStateProps): DatePickerState {
12 | const ref = useRef(null);
13 | const datepicker = useDatePicker(props, state.datepicker, ref);
14 |
15 | return { ...datepicker, ref, baseState: state };
16 | }
17 |
18 | export type DatePickerState = DatePickerAria & {
19 | /**
20 | * Reference for the date picker's visible label element, if any.
21 | */
22 | ref: RefObject;
23 | /**
24 | * Object returned by the `useDatePickerBaseState` hook.
25 | */
26 | baseState: DatePickerBaseState;
27 | };
28 |
29 | export type DatePickerStateProps =
30 | AriaDatePickerProps & {
31 | /**
32 | * Object returned by the `useDatePickerBaseState` hook.
33 | */
34 | state: DatePickerBaseState;
35 | };
36 |
--------------------------------------------------------------------------------
/src/datepicker/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./datepicker-base-state";
2 | export * from "./datepicker-disclosure";
3 | export * from "./datepicker-group";
4 | export * from "./datepicker-label";
5 | export * from "./datepicker-popover";
6 | export * from "./datepicker-state";
7 |
--------------------------------------------------------------------------------
/src/datepicker/stories/DatePickerBasic.component.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import CalendarBasic from "../../calendar/stories/CalendarBasic.component";
4 | import DateFieldBasic from "../../datefield/stories/DateFieldBasic.component";
5 | import {
6 | DatePickerBaseStateProps,
7 | DatePickerDisclosure,
8 | DatePickerGroup,
9 | DatePickerLabel,
10 | DatePickerPopover,
11 | useDatePickerBaseState,
12 | useDatePickerState,
13 | } from "../../index";
14 |
15 | import { CalendarIcon } from "./Utils.component";
16 |
17 | export type DatePickerBasicProps = DatePickerBaseStateProps & {};
18 |
19 | export const DatePickerBasic: React.FC = props => {
20 | const state = useDatePickerBaseState({ ...props, gutter: 10 });
21 | const datepicker = useDatePickerState({ ...props, state });
22 |
23 | return (
24 |
25 |
26 | {props.label}
27 |
28 |
29 |
30 |
34 |
35 |
36 |
37 | {state.popover.open && (
38 |
39 |
40 |
41 | )}
42 |
43 | );
44 | };
45 |
46 | export default DatePickerBasic;
47 |
--------------------------------------------------------------------------------
/src/datepicker/stories/DatePickerBasic.stories.tsx:
--------------------------------------------------------------------------------
1 | import { ComponentMeta, ComponentStoryObj } from "@storybook/react";
2 |
3 | import { createPreviewTabs } from "../../../.storybook/utils";
4 |
5 | import css from "./templates/DatePickerBasicCss";
6 | import js from "./templates/DatePickerBasicJsx";
7 | import ts from "./templates/DatePickerBasicTsx";
8 | import { DatePickerBasic } from "./DatePickerBasic.component";
9 |
10 | import "./DatePickerBasic.css";
11 |
12 | type Meta = ComponentMeta;
13 | type Story = ComponentStoryObj;
14 |
15 | export default {
16 | title: "DatePicker/Basic",
17 | component: DatePickerBasic,
18 | parameters: {
19 | layout: "centered",
20 | preview: createPreviewTabs({ js, ts, css }),
21 | },
22 | } as Meta;
23 |
24 | export const Default: Story = {
25 | args: { label: "DatePicker" },
26 | };
27 |
--------------------------------------------------------------------------------
/src/datepicker/stories/DatePickerStyled.component.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import CalendarStyled from "../../calendar/stories/CalendarStyled.component";
4 | import DateFieldStyled from "../../datefield/stories/DateFieldStyled.component";
5 | import {
6 | DatePickerBaseStateProps,
7 | DatePickerDisclosure,
8 | DatePickerGroup,
9 | DatePickerLabel,
10 | DatePickerPopover,
11 | useDatePickerBaseState,
12 | useDatePickerState,
13 | } from "../../index";
14 |
15 | import { CalendarStyledIcon } from "./Utils.component";
16 |
17 | export type DatePickerStyledProps = DatePickerBaseStateProps & {};
18 |
19 | export const DatePickerStyled: React.FC = props => {
20 | const state = useDatePickerBaseState({ ...props, gutter: 10 });
21 | const datepicker = useDatePickerState({ ...props, state });
22 |
23 | return (
24 |
25 |
26 | {props.label}
27 |
28 |
32 |
33 |
37 |
38 |
39 | {state.popover.open && (
40 |
41 |
42 |
43 | )}
44 |
45 |
46 | );
47 | };
48 |
49 | export default DatePickerStyled;
50 |
--------------------------------------------------------------------------------
/src/datepicker/stories/DatePickerStyled.stories.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { ComponentMeta, ComponentStoryObj } from "@storybook/react";
3 |
4 | import { createPreviewTabs } from "../../../.storybook/utils";
5 |
6 | import js from "./templates/DatePickerStyledJsx";
7 | import ts from "./templates/DatePickerStyledTsx";
8 | import { DatePickerStyled } from "./DatePickerStyled.component";
9 |
10 | import "./tailwind.css";
11 |
12 | type Meta = ComponentMeta;
13 | type Story = ComponentStoryObj;
14 |
15 | export default {
16 | title: "DatePicker/Styled",
17 | component: DatePickerStyled,
18 | parameters: {
19 | layout: "centered",
20 | preview: createPreviewTabs({ js, ts }),
21 | },
22 | decorators: [
23 | Story => {
24 | document.body.id = "tailwind";
25 | return ;
26 | },
27 | ],
28 | } as Meta;
29 |
30 | export const Default: Story = {
31 | args: { label: "DatePicker" },
32 | };
33 |
--------------------------------------------------------------------------------
/src/datepicker/stories/Utils.component.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 |
3 | export const CalendarIcon = (props: React.SVGProps) => (
4 |
14 | );
15 |
16 | export const CalendarStyledIcon = (props: React.SVGProps) => (
17 |
34 | );
35 |
36 | export const ChevronLeft = (props: React.SVGProps) => {
37 | return (
38 |
52 | );
53 | };
54 |
55 | export const ChevronRight = (props: React.SVGProps) => (
56 |
57 | );
58 |
--------------------------------------------------------------------------------
/src/datepicker/stories/tailwind.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | @layer components {
6 | .styled-datepicker .calendar__cell {
7 | height: 32px;
8 | width: 32px;
9 | max-height: 32px;
10 | max-width: 32px;
11 | @apply text-sm text-center rounded-lg;
12 | }
13 | .styled-datepicker .calendar__cell[data-is-range-selection] {
14 | @apply bg-blue-100 rounded-none text-gray-800 !important;
15 | }
16 | .styled-datepicker .calendar__cell[data-is-selection-start] {
17 | @apply bg-blue-500 rounded-l-lg text-white !important;
18 | }
19 | .styled-datepicker .calendar__cell[data-is-selection-end] {
20 | @apply bg-blue-500 rounded-r-lg text-white !important;
21 | }
22 |
23 | .styled-datepicker .calendar__cell[data-is-range-selection]:focus-within {
24 | @apply bg-blue-400 text-white !important;
25 | }
26 | .styled-datepicker .calendar__cell:focus-within {
27 | @apply bg-gray-100;
28 | }
29 |
30 | .styled-datepicker.calendar [data-weekend] {
31 | @apply text-red-600;
32 | }
33 |
34 | .styled-datepicker.calendar [aria-selected="true"] {
35 | @apply text-white bg-blue-500;
36 | }
37 |
38 | .styled-datepicker.calendar [aria-selected]:focus-within {
39 | @apply bg-gray-100;
40 | }
41 |
42 | .styled-datepicker.calendar [aria-selected="true"]:focus-within {
43 | @apply text-white bg-blue-400;
44 | }
45 |
46 | .styled-datepicker.calendar [aria-disabled="true"] {
47 | @apply text-gray-500;
48 | }
49 |
50 | .styled-datepicker.calendar span {
51 | outline: none;
52 | }
53 |
54 | .datepicker__dash:before {
55 | content: "-";
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/daterange-picker/daterangepicker-base-state.ts:
--------------------------------------------------------------------------------
1 | import { PopoverState, PopoverStateProps, usePopoverState } from "ariakit";
2 | import {
3 | DateRangePickerState,
4 | DateRangePickerStateOptions as DateRangePickerStateProps,
5 | useDateRangePickerState,
6 | } from "@react-stately/datepicker";
7 |
8 | export function useDateRangePickerBaseState(
9 | props: DateRangePickerBaseStateProps,
10 | ): DateRangePickerBaseState {
11 | const datepicker = useDateRangePickerState(props);
12 | const { isOpen, setOpen } = datepicker;
13 |
14 | const popover = usePopoverState({ open: isOpen, setOpen, ...props });
15 |
16 | return { datepicker, popover };
17 | }
18 |
19 | export type DateRangePickerBaseState = {
20 | /**
21 | * Object returned by the `useDatePickerState` hook.
22 | */
23 | datepicker: DateRangePickerState;
24 | /**
25 | * Object returned by the `usePopoverState` hook.
26 | */
27 | popover: PopoverState;
28 | };
29 |
30 | export type DateRangePickerBaseStateProps = DateRangePickerStateProps &
31 | PopoverStateProps & {};
32 |
--------------------------------------------------------------------------------
/src/daterange-picker/daterangepicker-state.ts:
--------------------------------------------------------------------------------
1 | import { RefObject, useRef } from "react";
2 | import { DateValue } from "@internationalized/date";
3 | import {
4 | DateRangePickerAria,
5 | useDateRangePicker,
6 | } from "@react-aria/datepicker";
7 | import { AriaDateRangePickerProps } from "@react-types/datepicker";
8 |
9 | import { DateRangePickerBaseState } from "./daterangepicker-base-state";
10 |
11 | export function useDateRangePickerState({
12 | state,
13 | ...props
14 | }: DateRangePickerStateProps): DateRangePickerState {
15 | const ref = useRef(null);
16 | const datepicker = useDateRangePicker(props, state.datepicker, ref);
17 |
18 | return { ...datepicker, ref, baseState: state };
19 | }
20 |
21 | export type DateRangePickerState = DateRangePickerAria & {
22 | /**
23 | * Reference for the date picker's visible label element, if any.
24 | */
25 | ref: RefObject;
26 | /**
27 | * Object returned by the `useDateRangePickerBaseState` hook.
28 | */
29 | baseState: DateRangePickerBaseState;
30 | };
31 |
32 | export type DateRangePickerStateProps =
33 | AriaDateRangePickerProps & {
34 | /**
35 | * Object returned by the `useDateRangePickerBaseState` hook.
36 | */
37 | state: DateRangePickerBaseState;
38 | };
39 |
--------------------------------------------------------------------------------
/src/daterange-picker/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./daterangepicker-base-state";
2 | export * from "./daterangepicker-state";
3 |
--------------------------------------------------------------------------------
/src/daterange-picker/stories/DateRangePickerBasic.component.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import DateFieldBasic from "../../datefield/stories/DateFieldBasic.component";
4 | import {
5 | DatePickerDisclosure,
6 | DatePickerGroup,
7 | DatePickerLabel,
8 | DatePickerPopover,
9 | DateRangePickerBaseStateProps,
10 | useDateRangePickerBaseState,
11 | useDateRangePickerState,
12 | } from "../../index";
13 | import CalendarRange from "../../range-calendar/stories/RangeCalendarBasic.component";
14 |
15 | import { CalendarIcon } from "./Utils.component";
16 |
17 | export type DateRangePickerBasicProps = DateRangePickerBaseStateProps & {};
18 |
19 | export const DateRangePickerBasic: React.FC<
20 | DateRangePickerBasicProps
21 | > = props => {
22 | const state = useDateRangePickerBaseState({ ...props, gutter: 10 });
23 | const daterangepicker = useDateRangePickerState({ ...props, state });
24 |
25 | return (
26 |
27 |
28 | {props.label}
29 |
30 |
31 |
32 |
33 |
37 |
38 |
39 |
40 | {state.popover.open && (
41 |
42 |
43 |
44 | )}
45 |
46 | );
47 | };
48 |
49 | export default DateRangePickerBasic;
50 |
--------------------------------------------------------------------------------
/src/daterange-picker/stories/DateRangePickerBasic.stories.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { ComponentMeta, ComponentStoryObj } from "@storybook/react";
3 |
4 | import { createPreviewTabs } from "../../../.storybook/utils";
5 |
6 | import css from "./templates/DateRangePickerBasicCss";
7 | import js from "./templates/DateRangePickerBasicJsx";
8 | import ts from "./templates/DateRangePickerBasicTsx";
9 | import { DateRangePickerBasic } from "./DateRangePickerBasic.component";
10 |
11 | import "./DateRangePickerBasic.css";
12 |
13 | type Meta = ComponentMeta;
14 | type Story = ComponentStoryObj;
15 |
16 | export default {
17 | title: "DateRangePicker/Basic",
18 | component: DateRangePickerBasic,
19 | parameters: {
20 | layout: "centered",
21 | preview: createPreviewTabs({ js, ts, css }),
22 | },
23 | decorators: [
24 | Story => {
25 | document.body.id = "date-range-picker-basic";
26 | return ;
27 | },
28 | ],
29 | } as Meta;
30 |
31 | export const Default: Story = {
32 | args: { label: "DateRangePicker" },
33 | };
34 |
--------------------------------------------------------------------------------
/src/daterange-picker/stories/DateRangePickerStyled.stories.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { ComponentMeta, ComponentStoryObj } from "@storybook/react";
3 |
4 | import { createPreviewTabs } from "../../../.storybook/utils";
5 |
6 | import js from "./templates/DateRangePickerStyledJsx";
7 | import ts from "./templates/DateRangePickerStyledTsx";
8 | import { DateRangePickerStyled } from "./DateRangePickerStyled.component";
9 |
10 | import "./tailwind.css";
11 |
12 | type Meta = ComponentMeta;
13 | type Story = ComponentStoryObj;
14 |
15 | export default {
16 | title: "DateRangePicker/Styled",
17 | component: DateRangePickerStyled,
18 | parameters: {
19 | layout: "centered",
20 | preview: createPreviewTabs({ js, ts }),
21 | },
22 | decorators: [
23 | Story => {
24 | document.body.id = "tailwind";
25 | return ;
26 | },
27 | ],
28 | } as Meta;
29 |
30 | export const Default: Story = {
31 | args: { label: "DateRangePicker" },
32 | };
33 |
--------------------------------------------------------------------------------
/src/daterange-picker/stories/Utils.component.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 |
3 | export const CalendarIcon = (props: React.SVGProps) => (
4 |
14 | );
15 |
16 | export const CalendarStyledIcon = (props: React.SVGProps) => (
17 |
34 | );
35 |
36 | export const ChevronLeft = (props: React.SVGProps) => {
37 | return (
38 |
52 | );
53 | };
54 |
55 | export const ChevronRight = (props: React.SVGProps) => (
56 |
57 | );
58 |
--------------------------------------------------------------------------------
/src/daterange-picker/stories/tailwind.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | @layer components {
6 | .styled-datepicker .calendar__cell {
7 | height: 32px;
8 | width: 32px;
9 | max-height: 32px;
10 | max-width: 32px;
11 | @apply text-sm text-center rounded-lg;
12 | }
13 | .styled-datepicker .calendar__cell[data-is-range-selection] {
14 | @apply bg-blue-100 rounded-none text-gray-800 !important;
15 | }
16 | .styled-datepicker .calendar__cell[data-is-selection-start] {
17 | @apply bg-blue-500 rounded-l-lg text-white !important;
18 | }
19 | .styled-datepicker .calendar__cell[data-is-selection-end] {
20 | @apply bg-blue-500 rounded-r-lg text-white !important;
21 | }
22 |
23 | .styled-datepicker .calendar__cell[data-is-range-selection]:focus-within {
24 | @apply bg-blue-400 text-white !important;
25 | }
26 | .styled-datepicker .calendar__cell:focus-within {
27 | @apply bg-gray-100;
28 | }
29 |
30 | .styled-datepicker.calendar [data-weekend] {
31 | @apply text-red-600;
32 | }
33 |
34 | .styled-datepicker.calendar [aria-selected="true"] {
35 | @apply text-white bg-blue-500;
36 | }
37 |
38 | .styled-datepicker.calendar [aria-selected]:focus-within {
39 | @apply bg-gray-100;
40 | }
41 |
42 | .styled-datepicker.calendar [aria-selected="true"]:focus-within {
43 | @apply text-white bg-blue-400;
44 | }
45 |
46 | .styled-datepicker.calendar [aria-disabled="true"] {
47 | @apply text-gray-500;
48 | }
49 |
50 | .styled-datepicker.calendar span {
51 | outline: none;
52 | }
53 |
54 | .datepicker__dash:before {
55 | content: "-";
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/disclosure/__utils.ts:
--------------------------------------------------------------------------------
1 | // https://github.com/mui-org/material-ui/blob/da362266f7c137bf671d7e8c44c84ad5cfc0e9e2/packages/material-ui/src/styles/transitions.js#L89-L98
2 | export function getAutoSizeDuration(size: number | string): number {
3 | if (!size || typeof size === "string") {
4 | return 0;
5 | }
6 |
7 | const constant = size / 36;
8 |
9 | // https://www.wolframalpha.com/input/?i=(4+%2B+15+*+(x+%2F+36+)+**+0.25+%2B+(x+%2F+36)+%2F+5)+*+10
10 | return Math.round((4 + 15 * constant ** 0.25 + constant / 5) * 10);
11 | }
12 |
13 | export function getElementHeight(
14 | el: React.RefObject | { current?: { scrollHeight: number } },
15 | ): string | number {
16 | if (!el?.current) {
17 | return "auto";
18 | }
19 |
20 | return el.current.scrollHeight;
21 | }
22 |
23 | export function getElementWidth(
24 | el: React.RefObject | { current?: { scrollWidth: number } },
25 | ): string | number {
26 | if (!el?.current) {
27 | return "auto";
28 | }
29 |
30 | return el.current.scrollWidth;
31 | }
32 |
--------------------------------------------------------------------------------
/src/disclosure/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./disclosure-collapsible-content";
2 |
--------------------------------------------------------------------------------
/src/disclosure/stories/DisclosureHorizontalCollapseBasic.component.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { Disclosure, DisclosureStateProps, useDisclosureState } from "ariakit";
3 |
4 | import { DisclosureCollapsibleContent } from "../../index";
5 |
6 | export type DisclosureHorizontalCollapseBasicProps = DisclosureStateProps & {};
7 |
8 | export const DisclosureHorizontalCollapseBasic: React.FC<
9 | DisclosureHorizontalCollapseBasicProps
10 | > = props => {
11 | const state = useDisclosureState(props);
12 |
13 | return (
14 |
15 | Show More
16 |
24 | Item 1
25 | Item 2
26 | Item 3
27 | Item 4
28 | Item 5
29 | Item 6
30 |
31 |
32 | );
33 | };
34 |
35 | export default DisclosureHorizontalCollapseBasic;
36 |
--------------------------------------------------------------------------------
/src/disclosure/stories/DisclosureHorizontalCollapseBasic.stories.tsx:
--------------------------------------------------------------------------------
1 | import { ComponentMeta, ComponentStoryObj } from "@storybook/react";
2 |
3 | import { createPreviewTabs } from "../../../.storybook/utils";
4 |
5 | import js from "./templates/DisclosureHorizontalCollapseBasicJsx";
6 | import ts from "./templates/DisclosureHorizontalCollapseBasicTsx";
7 | import { DisclosureHorizontalCollapseBasic } from "./DisclosureHorizontalCollapseBasic.component";
8 |
9 | type Meta = ComponentMeta;
10 | type Story = ComponentStoryObj;
11 |
12 | export default {
13 | title: "Disclosure/Horizontal",
14 | component: DisclosureHorizontalCollapseBasic,
15 | parameters: {
16 | layout: "centered",
17 | preview: createPreviewTabs({ js, ts }),
18 | },
19 | } as Meta;
20 |
21 | export const Default: Story = { args: {} };
22 |
--------------------------------------------------------------------------------
/src/disclosure/stories/DisclosureVerticalCollapseBasic.component.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { Disclosure, DisclosureStateProps, useDisclosureState } from "ariakit";
3 |
4 | import { DisclosureCollapsibleContent } from "../../index";
5 |
6 | export type DisclosureVerticalCollapseBasicProps = DisclosureStateProps & {};
7 |
8 | export const DisclosureVerticalCollapseBasic: React.FC<
9 | DisclosureVerticalCollapseBasicProps
10 | > = props => {
11 | const state = useDisclosureState(props);
12 |
13 | return (
14 |
15 | Show More
16 |
24 | Item 1
25 | Item 2
26 | Item 3
27 | Item 4
28 | Item 5
29 | Item 6
30 |
31 |
32 | );
33 | };
34 |
35 | export default DisclosureVerticalCollapseBasic;
36 |
--------------------------------------------------------------------------------
/src/disclosure/stories/DisclosureVerticalCollapseBasic.stories.tsx:
--------------------------------------------------------------------------------
1 | import { ComponentMeta, ComponentStoryObj } from "@storybook/react";
2 |
3 | import { createPreviewTabs } from "../../../.storybook/utils";
4 |
5 | import js from "./templates/DisclosureVerticalCollapseBasicJsx";
6 | import ts from "./templates/DisclosureVerticalCollapseBasicTsx";
7 | import { DisclosureVerticalCollapseBasic } from "./DisclosureVerticalCollapseBasic.component";
8 |
9 | type Meta = ComponentMeta;
10 | type Story = ComponentStoryObj;
11 |
12 | export default {
13 | title: "Disclosure/Vertical",
14 | component: DisclosureVerticalCollapseBasic,
15 | parameters: {
16 | layout: "centered",
17 | preview: createPreviewTabs({ js, ts }),
18 | },
19 | } as Meta;
20 |
21 | export const Default: Story = { args: {} };
22 |
--------------------------------------------------------------------------------
/src/drawer/drawer.ts:
--------------------------------------------------------------------------------
1 | import { CSSProperties } from "react";
2 | import {
3 | createComponent,
4 | createElement,
5 | createHook,
6 | } from "ariakit-utils/system";
7 | import { DialogOptions, useDialog } from "ariakit/dialog";
8 | import { As, Props } from "ariakit-utils/types";
9 |
10 | export const useDrawer = createHook(
11 | ({ placement = "left", state, ...props }) => {
12 | const style: CSSProperties = {
13 | ...PLACEMENTS[placement],
14 | position: "fixed",
15 | ...props.style,
16 | };
17 |
18 | props = { ...props, style };
19 | props = useDialog({ state, ...props });
20 |
21 | return props;
22 | },
23 | );
24 |
25 | export const Drawer = createComponent(props => {
26 | const htmlProps = useDrawer(props);
27 |
28 | return createElement("div", htmlProps);
29 | });
30 |
31 | export type DrawerOptions = DialogOptions & {
32 | /**
33 | * Direction to place the drawer.
34 | *
35 | * @default left
36 | */
37 | placement?: Placement;
38 | };
39 |
40 | export type DrawerProps = Props>;
41 |
42 | export type Placement = keyof typeof PLACEMENTS;
43 |
44 | export const PLACEMENTS = {
45 | left: {
46 | left: 0,
47 | top: 0,
48 | bottom: 0,
49 | height: "100vh",
50 | },
51 | right: {
52 | right: 0,
53 | top: 0,
54 | bottom: 0,
55 | height: "100vh",
56 | },
57 | top: {
58 | right: 0,
59 | left: 0,
60 | top: 0,
61 | width: "100vw",
62 | },
63 | bottom: {
64 | right: 0,
65 | left: 0,
66 | bottom: 0,
67 | width: "100vw",
68 | },
69 | };
70 |
--------------------------------------------------------------------------------
/src/drawer/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./drawer";
2 |
--------------------------------------------------------------------------------
/src/drawer/stories/DrawerBasic.component.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import {
3 | Button,
4 | DialogDismiss,
5 | DialogHeading,
6 | DisclosureStateProps,
7 | useDialogState,
8 | } from "ariakit";
9 |
10 | import { Drawer } from "../../index";
11 |
12 | export type DrawerBasicProps = DisclosureStateProps & {};
13 |
14 | export const DrawerBasic: React.FC = props => {
15 | const dialog = useDialogState({ animated: true, ...props });
16 |
17 | return (
18 | <>
19 |
22 |
27 |
31 |
32 | -
33 | Calories: 95
34 |
35 | -
36 | Carbs: 25 grams
37 |
38 | -
39 | Fibers: 4 grams
40 |
41 | -
42 | Vitamin C: 14% of the Reference Daily Intake (RDI)
43 |
44 | -
45 | Potassium: 6% of the RDI
46 |
47 | -
48 | Vitamin K: 5% of the RDI
49 |
50 |
51 |
52 | >
53 | );
54 | };
55 |
56 | export default DrawerBasic;
57 |
--------------------------------------------------------------------------------
/src/drawer/stories/DrawerBasic.css:
--------------------------------------------------------------------------------
1 | .dialog {
2 | opacity: 0;
3 | padding: 10px;
4 | background-color: white;
5 | transition: 250ms ease-in-out;
6 | transform: translate(-200px, 0);
7 | }
8 |
9 | .dialog[data-enter] {
10 | opacity: 1;
11 | transform: translate(0, 0);
12 | }
13 |
14 | .dialog[data-leave] {
15 | opacity: 0;
16 | transform: translate(-200px, 0);
17 | }
18 |
19 | .backdrop {
20 | opacity: 0;
21 | position: fixed;
22 | top: 0;
23 | left: 0;
24 | bottom: 0;
25 | right: 0;
26 | transition: opacity 250ms ease-in-out;
27 | background-color: rgba(0, 0, 0, 0.2);
28 | }
29 |
30 | .backdrop[data-enter] {
31 | opacity: 1;
32 | }
33 |
34 | .header {
35 | display: flex;
36 | justify-content: space-between;
37 | }
38 |
39 | .heading {
40 | margin: 0px;
41 | }
42 |
--------------------------------------------------------------------------------
/src/drawer/stories/DrawerBasic.stories.tsx:
--------------------------------------------------------------------------------
1 | import { ComponentMeta, ComponentStoryObj } from "@storybook/react";
2 |
3 | import { createPreviewTabs } from "../../../.storybook/utils";
4 |
5 | import js from "./templates/DrawerBasicJsx";
6 | import ts from "./templates/DrawerBasicTsx";
7 | import { DrawerBasic } from "./DrawerBasic.component";
8 |
9 | import "./DrawerBasic.css";
10 |
11 | type Meta = ComponentMeta;
12 | type Story = ComponentStoryObj;
13 |
14 | export default {
15 | title: "Drawer/Basic",
16 | component: DrawerBasic,
17 | parameters: {
18 | layout: "centered",
19 | preview: createPreviewTabs({ js, ts }),
20 | },
21 | } as Meta;
22 |
23 | export const Default: Story = { args: {} };
24 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./accordion";
2 | export * from "./breadcrumbs";
3 | export * from "./calendar";
4 | export * from "./datefield";
5 | export * from "./datepicker";
6 | export * from "./daterange-picker";
7 | export * from "./disclosure";
8 | export * from "./drawer";
9 | export * from "./link";
10 | export * from "./meter";
11 | export * from "./numberfield";
12 | export * from "./pagination";
13 | export * from "./progress";
14 | export * from "./range-calendar";
15 | export * from "./slider";
16 | export * from "./timefield";
17 | export * from "./toast";
18 |
--------------------------------------------------------------------------------
/src/link/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./link-base";
2 |
--------------------------------------------------------------------------------
/src/link/link-base.ts:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import {
3 | createComponent,
4 | createElement,
5 | createHook,
6 | } from "ariakit-utils/system";
7 | import { CommandOptions, useCommand } from "ariakit";
8 | import { useForkRef, useTagName } from "ariakit-utils";
9 | import { As, Props } from "ariakit-utils/types";
10 |
11 | export const useLink = createHook(
12 | ({ isExternal = false, ...props }) => {
13 | const ref = React.useRef(null);
14 | const tagName = useTagName(ref, props.as || "a");
15 | const [isNativeLink, setIsNativeLink] = React.useState(
16 | () => !!tagName && isLink({ tagName }),
17 | );
18 |
19 | React.useEffect(() => {
20 | if (!ref.current) return;
21 |
22 | setIsNativeLink(isLink(ref.current));
23 | }, []);
24 |
25 | props = {
26 | role: !isNativeLink && tagName !== "a" ? "link" : undefined,
27 | ...(isExternal && { target: "_blank", rel: "noopener noreferrer" }),
28 | ...props,
29 | ref: useForkRef(ref, props.ref),
30 | };
31 |
32 | props = useCommand({ clickOnSpace: false, ...props });
33 |
34 | return props;
35 | },
36 | );
37 |
38 | export const Link = createComponent(props => {
39 | const htmlProps = useLink(props);
40 |
41 | return createElement("a", htmlProps);
42 | });
43 |
44 | export type LinkOptions = CommandOptions & {
45 | /**
46 | * Opens the link in a new tab
47 | * @default false
48 | */
49 | isExternal?: boolean;
50 | };
51 |
52 | export type LinkProps = Props>;
53 |
54 | export function isLink(element: { tagName: string }) {
55 | const tagName = element.tagName.toLowerCase();
56 | if (tagName === "a") return true;
57 |
58 | return false;
59 | }
60 |
--------------------------------------------------------------------------------
/src/link/stories/LinkBasic.component.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 |
3 | import { Link, LinkProps } from "../../index";
4 |
5 | export type LinkBasicProps = LinkProps & {};
6 |
7 | export const LinkBasic: React.FC = props => {
8 | return (
9 |
10 | Timeless
11 |
12 | );
13 | };
14 |
15 | export default LinkBasic;
16 |
--------------------------------------------------------------------------------
/src/link/stories/LinkBasic.stories.tsx:
--------------------------------------------------------------------------------
1 | import { ComponentMeta, ComponentStoryObj } from "@storybook/react";
2 |
3 | import { createPreviewTabs } from "../../../.storybook/utils";
4 |
5 | import js from "./templates/LinkBasicJsx";
6 | import ts from "./templates/LinkBasicTsx";
7 | import { LinkBasic } from "./LinkBasic.component";
8 |
9 | type Meta = ComponentMeta;
10 | type Story = ComponentStoryObj;
11 |
12 | export default {
13 | title: "Link/Basic",
14 | component: LinkBasic,
15 | parameters: {
16 | layout: "centered",
17 | preview: createPreviewTabs({ js, ts }),
18 | },
19 | } as Meta;
20 |
21 | export const Default: Story = { args: {} };
22 |
23 | export const DisabledLink: Story = {
24 | args: { disabled: true },
25 | };
26 |
--------------------------------------------------------------------------------
/src/link/stories/LinkSpan.component.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 |
3 | import { Link, LinkProps } from "../../index";
4 |
5 | export type LinkSpanProps = LinkProps & {};
6 |
7 | export const LinkSpan: React.FC = props => {
8 | return (
9 |
12 | goToLink(event, "https://timeless.co/")
13 | }
14 | onKeyDown={(event: React.KeyboardEvent) =>
15 | goToLink(event, "https://timeless.co/")
16 | }
17 | {...props}
18 | >
19 | Timeless
20 |
21 | );
22 | };
23 |
24 | export default LinkSpan;
25 |
26 | function goToLink(event: React.MouseEvent | React.KeyboardEvent, url: string) {
27 | var type = event.type;
28 |
29 | // @ts-ignore
30 | if (type === "click" || (type === "keydown" && event.key === "Enter")) {
31 | window.location.href = url;
32 |
33 | event.preventDefault();
34 | event.stopPropagation();
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/link/stories/LinkSpan.stories.tsx:
--------------------------------------------------------------------------------
1 | import { ComponentMeta, ComponentStoryObj } from "@storybook/react";
2 |
3 | import { createPreviewTabs } from "../../../.storybook/utils";
4 |
5 | import js from "./templates/LinkSpanJsx";
6 | import ts from "./templates/LinkSpanTsx";
7 | import { LinkSpan } from "./LinkSpan.component";
8 |
9 | type Meta = ComponentMeta;
10 | type Story = ComponentStoryObj;
11 |
12 | export default {
13 | title: "Link/Span",
14 | component: LinkSpan,
15 | parameters: {
16 | layout: "centered",
17 | preview: createPreviewTabs({ js, ts }),
18 | },
19 | } as Meta;
20 |
21 | export const Default: Story = { args: {} };
22 |
23 | export const DisabledLink: Story = { args: { disabled: true } };
24 |
--------------------------------------------------------------------------------
/src/meter/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./__utils";
2 | export * from "./meter-base";
3 | export * from "./meter-state";
4 |
--------------------------------------------------------------------------------
/src/meter/meter-base.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | createComponent,
3 | createElement,
4 | createHook,
5 | } from "ariakit-utils/system";
6 | import { As, Options, Props } from "ariakit-utils/types";
7 |
8 | import { MeterState } from "./meter-state";
9 |
10 | export const useMeter = createHook(({ state, ...props }) => {
11 | const { value, max, min, percent } = state;
12 |
13 | props = {
14 | role: "meter progressbar",
15 | "aria-valuemax": max,
16 | "aria-valuemin": min,
17 | "aria-valuenow": value,
18 | "aria-valuetext": !percent.toString() ? undefined : `${percent}%`,
19 | ...props,
20 | };
21 |
22 | return props;
23 | });
24 |
25 | export const Meter = createComponent(props => {
26 | const htmlProps = useMeter(props);
27 |
28 | return createElement("div", htmlProps);
29 | });
30 |
31 | export type MeterOptions = Options & {
32 | /**
33 | * Object returned by the `useMeterState` hook.
34 | */
35 | state: MeterState;
36 | };
37 |
38 | export type MeterProps = Props>;
39 |
--------------------------------------------------------------------------------
/src/meter/stories/MeterBasic.component.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 |
3 | import { Meter, MeterStateProps, useMeterState } from "../../index";
4 |
5 | export const MeterBasic: React.FC = props => {
6 | const state = useMeterState({
7 | value: 5,
8 | min: 0,
9 | max: 10,
10 | low: 0,
11 | high: 10,
12 | optimum: 5,
13 | ...props,
14 | });
15 | const { percent, status } = state;
16 |
17 | return (
18 |
19 |
28 |
29 | );
30 | };
31 |
32 | export default MeterBasic;
33 |
34 | const background = {
35 | safe: "#8bcf69",
36 | caution: "#e6d450",
37 | danger: "#f28f68",
38 | };
39 |
--------------------------------------------------------------------------------
/src/meter/stories/MeterBasic.css:
--------------------------------------------------------------------------------
1 | /* CSS Styles from https://css-tricks.com/html5-meter-element/ */
2 |
3 | .meter {
4 | position: relative;
5 | width: 500px;
6 | height: 1rem;
7 | background: whiteSmoke;
8 | border-radius: 3px;
9 | border: 1px solid #ccc;
10 | box-shadow: 0 5px 5px -5px #333 inset;
11 | overflow: hidden;
12 | }
13 |
14 | .meterbar {
15 | height: 100%;
16 | }
17 |
--------------------------------------------------------------------------------
/src/meter/stories/MeterStyled.stories.tsx:
--------------------------------------------------------------------------------
1 | import { ComponentMeta, ComponentStoryObj } from "@storybook/react";
2 |
3 | import { createPreviewTabs } from "../../../.storybook/utils";
4 |
5 | import js from "./templates/MeterStyledJsx";
6 | import ts from "./templates/MeterStyledTsx";
7 | import { MeterStyled } from "./MeterStyled.component";
8 |
9 | type Meta = ComponentMeta;
10 | type Story = ComponentStoryObj;
11 |
12 | export default {
13 | component: MeterStyled,
14 | title: "Meter/Styled",
15 | parameters: {
16 | layout: "centered",
17 | preview: createPreviewTabs({ js, ts, deps: ["@emotion/css@latest"] }),
18 | },
19 | } as Meta;
20 |
21 | const Default: Story = {
22 | args: { value: 5, min: 0, max: 10, low: 0, high: 10, optimum: 5 },
23 | };
24 |
25 | export const WithLabel = { args: { ...Default.args, withLabel: true } };
26 |
27 | export const WithStripe = { args: { ...Default.args, withStripe: true } };
28 |
29 | export const WithStripeAnimation = {
30 | args: { ...Default.args, withStripe: true, withStripeAnimation: true },
31 | };
32 |
--------------------------------------------------------------------------------
/src/numberfield/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./numberfield-base-state";
2 | export * from "./numberfield-decrement-button";
3 | export * from "./numberfield-group";
4 | export * from "./numberfield-increment-button";
5 | export * from "./numberfield-input";
6 | export * from "./numberfield-label";
7 | export * from "./numberfield-state";
8 |
--------------------------------------------------------------------------------
/src/numberfield/numberfield-base-state.ts:
--------------------------------------------------------------------------------
1 | import {
2 | NumberFieldState,
3 | NumberFieldStateProps,
4 | useNumberFieldState,
5 | } from "@react-stately/numberfield";
6 |
7 | export function useNumberFieldBaseState(
8 | props: NumberFieldBaseStateProps,
9 | ): NumberFieldBaseState {
10 | const state = useNumberFieldState(props);
11 |
12 | return state;
13 | }
14 |
15 | export type NumberFieldBaseState = NumberFieldState & {};
16 |
17 | export type NumberFieldBaseStateProps = NumberFieldStateProps & {};
18 |
--------------------------------------------------------------------------------
/src/numberfield/numberfield-decrement-button.ts:
--------------------------------------------------------------------------------
1 | import { useRef } from "react";
2 | import {
3 | createComponent,
4 | createElement,
5 | createHook,
6 | } from "ariakit-utils/system";
7 | import { useForkRef } from "ariakit-utils";
8 | import { As, Options, Props } from "ariakit-utils/types";
9 | import { useButton } from "@react-aria/button";
10 | import { mergeProps } from "@react-aria/utils";
11 |
12 | import { NumberFieldState } from "./numberfield-state";
13 |
14 | export const useNumberFieldDecrementButton =
15 | createHook(({ state, ...props }) => {
16 | const ref = useRef(null);
17 | const { buttonProps } = useButton(state.decrementButtonProps, ref);
18 |
19 | props = { ...props, ref: useForkRef(ref, props.ref) };
20 | props = mergeProps(buttonProps, props);
21 |
22 | return props;
23 | });
24 |
25 | export const NumberFieldDecrementButton =
26 | createComponent(props => {
27 | const htmlProps = useNumberFieldDecrementButton(props);
28 |
29 | return createElement("button", htmlProps);
30 | });
31 |
32 | export type NumberFieldDecrementButtonOptions =
33 | Options & {
34 | /**
35 | * Object returned by the `useNumberFieldState` hook.
36 | */
37 | state: NumberFieldState;
38 | };
39 |
40 | export type NumberFieldDecrementButtonProps = Props<
41 | NumberFieldDecrementButtonOptions
42 | >;
43 |
--------------------------------------------------------------------------------
/src/numberfield/numberfield-group.ts:
--------------------------------------------------------------------------------
1 | import {
2 | createComponent,
3 | createElement,
4 | createHook,
5 | } from "ariakit-utils/system";
6 | import { As, Options, Props } from "ariakit-utils/types";
7 | import { mergeProps } from "@react-aria/utils";
8 |
9 | import { NumberFieldState } from "./numberfield-state";
10 |
11 | export const useNumberFieldGroup = createHook(
12 | ({ state, ...props }) => {
13 | props = mergeProps(state.groupProps, props);
14 |
15 | return props;
16 | },
17 | );
18 |
19 | export const NumberFieldGroup = createComponent(
20 | props => {
21 | const htmlProps = useNumberFieldGroup(props);
22 |
23 | return createElement("div", htmlProps);
24 | },
25 | );
26 |
27 | export type NumberFieldGroupOptions = Options & {
28 | /**
29 | * Object returned by the `useNumberFieldState` hook.
30 | */
31 | state: NumberFieldState;
32 | };
33 |
34 | export type NumberFieldGroupProps = Props<
35 | NumberFieldGroupOptions
36 | >;
37 |
--------------------------------------------------------------------------------
/src/numberfield/numberfield-increment-button.ts:
--------------------------------------------------------------------------------
1 | import { useRef } from "react";
2 | import {
3 | createComponent,
4 | createElement,
5 | createHook,
6 | } from "ariakit-utils/system";
7 | import { useForkRef } from "ariakit-utils";
8 | import { As, Options, Props } from "ariakit-utils/types";
9 | import { useButton } from "@react-aria/button";
10 | import { mergeProps } from "@react-aria/utils";
11 |
12 | import { NumberFieldState } from "./numberfield-state";
13 |
14 | export const useNumberFieldIncrementButton =
15 | createHook(({ state, ...props }) => {
16 | const ref = useRef(null);
17 | const { buttonProps } = useButton(state.incrementButtonProps, ref);
18 |
19 | props = { ...props, ref: useForkRef(ref, props.ref) };
20 | props = mergeProps(buttonProps, props);
21 |
22 | return props;
23 | });
24 |
25 | export const NumberFieldIncrementButton =
26 | createComponent(props => {
27 | const htmlProps = useNumberFieldIncrementButton(props);
28 |
29 | return createElement("button", htmlProps);
30 | });
31 |
32 | export type NumberFieldIncrementButtonOptions =
33 | Options & {
34 | /**
35 | * Object returned by the `useNumberFieldState` hook.
36 | */
37 | state: NumberFieldState;
38 | };
39 |
40 | export type NumberFieldIncrementButtonProps = Props<
41 | NumberFieldIncrementButtonOptions
42 | >;
43 |
--------------------------------------------------------------------------------
/src/numberfield/numberfield-input.ts:
--------------------------------------------------------------------------------
1 | import {
2 | createComponent,
3 | createElement,
4 | createHook,
5 | } from "ariakit-utils/system";
6 | import { useForkRef } from "ariakit-utils";
7 | import { As, Options, Props } from "ariakit-utils/types";
8 | import { mergeProps } from "@react-aria/utils";
9 |
10 | import { NumberFieldState } from "./numberfield-state";
11 |
12 | export const useNumberFieldInput = createHook(
13 | ({ state, ...props }) => {
14 | props = { ...props, ref: useForkRef(state.inputRef, props.ref) };
15 | props = mergeProps(state.inputProps, props);
16 |
17 | return props;
18 | },
19 | );
20 |
21 | export const NumberFieldInput = createComponent(
22 | props => {
23 | const htmlProps = useNumberFieldInput(props);
24 |
25 | return createElement("input", htmlProps);
26 | },
27 | );
28 |
29 | export type NumberFieldInputOptions = Options & {
30 | /**
31 | * Object returned by the `useNumberFieldState` hook.
32 | */
33 | state: NumberFieldState;
34 | };
35 |
36 | export type NumberFieldInputProps = Props<
37 | NumberFieldInputOptions
38 | >;
39 |
--------------------------------------------------------------------------------
/src/numberfield/numberfield-label.ts:
--------------------------------------------------------------------------------
1 | import {
2 | createComponent,
3 | createElement,
4 | createHook,
5 | } from "ariakit-utils/system";
6 | import { As, Options, Props } from "ariakit-utils/types";
7 | import { mergeProps } from "@react-aria/utils";
8 |
9 | import { NumberFieldState } from "./numberfield-state";
10 |
11 | export const useNumberFieldLabel = createHook(
12 | ({ state, ...props }) => {
13 | props = mergeProps(state.labelProps, props);
14 |
15 | return props;
16 | },
17 | );
18 |
19 | export const NumberFieldLabel = createComponent(
20 | props => {
21 | const htmlProps = useNumberFieldLabel(props);
22 |
23 | return createElement("label", htmlProps);
24 | },
25 | );
26 |
27 | export type NumberFieldLabelOptions = Options & {
28 | /**
29 | * Object returned by the `useNumberFieldState` hook.
30 | */
31 | state: NumberFieldState;
32 | };
33 |
34 | export type NumberFieldLabelProps = Props<
35 | NumberFieldLabelOptions
36 | >;
37 |
--------------------------------------------------------------------------------
/src/numberfield/numberfield-state.ts:
--------------------------------------------------------------------------------
1 | import { RefObject, useRef } from "react";
2 | import { NumberFieldAria, useNumberField } from "@react-aria/numberfield";
3 | import { AriaNumberFieldProps } from "@react-types/numberfield";
4 |
5 | import { NumberFieldBaseState } from "./numberfield-base-state";
6 |
7 | export function useNumberFieldState(
8 | props: NumberFieldStateProps,
9 | ): NumberFieldState {
10 | const { state: baseState, ...rest } = props;
11 | const inputRef = useRef(null);
12 | const state = useNumberField(rest, baseState, inputRef);
13 |
14 | return { ...state, baseState, inputRef };
15 | }
16 |
17 | export type NumberFieldState = NumberFieldAria & {
18 | /**
19 | * Reference for the input element in number field element, if any.
20 | */
21 | inputRef: RefObject;
22 | /**
23 | * Object returned by the `useNumberFieldBaseState` hook.
24 | */
25 | baseState: NumberFieldBaseState;
26 | };
27 |
28 | export type NumberFieldStateProps = AriaNumberFieldProps & {
29 | /**
30 | * Object returned by the `useNumberFieldBaseState` hook.
31 | */
32 | state: NumberFieldBaseState;
33 | };
34 |
--------------------------------------------------------------------------------
/src/numberfield/stories/NumberFieldBasic.component.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { useLocale } from "@react-aria/i18n";
3 |
4 | import {
5 | NumberFieldBaseStateProps,
6 | NumberFieldDecrementButton,
7 | NumberFieldGroup,
8 | NumberFieldIncrementButton,
9 | NumberFieldInput,
10 | NumberFieldLabel,
11 | useNumberFieldBaseState,
12 | useNumberFieldState,
13 | } from "../../index";
14 |
15 | export type NumberFieldBasicProps = NumberFieldBaseStateProps & {};
16 |
17 | export const NumberFieldBasic: React.FC = props => {
18 | let { locale } = useLocale();
19 | const baseState = useNumberFieldBaseState({ ...props, locale });
20 | const state = useNumberFieldState({ ...props, state: baseState });
21 |
22 | return (
23 |
24 | NumberField
25 |
26 | -
27 |
28 | +
29 |
30 |
31 | );
32 | };
33 |
34 | export default NumberFieldBasic;
35 |
--------------------------------------------------------------------------------
/src/numberfield/stories/NumberFieldBasic.stories.tsx:
--------------------------------------------------------------------------------
1 | import { ComponentMeta, ComponentStoryObj } from "@storybook/react";
2 |
3 | import { createPreviewTabs } from "../../../.storybook/utils";
4 |
5 | import js from "./templates/NumberFieldBasicJsx";
6 | import ts from "./templates/NumberFieldBasicTsx";
7 | import { NumberFieldBasic } from "./NumberFieldBasic.component";
8 |
9 | type Meta = ComponentMeta;
10 | type Story = ComponentStoryObj;
11 |
12 | export default {
13 | title: "NumberField/Basic",
14 | component: NumberFieldBasic,
15 | parameters: {
16 | layout: "centered",
17 | preview: createPreviewTabs({ js, ts }),
18 | },
19 | } as Meta;
20 |
21 | export const Default: Story = { args: { label: "NumberField" } };
22 |
--------------------------------------------------------------------------------
/src/pagination/__utils.ts:
--------------------------------------------------------------------------------
1 | import { createStoreContext } from "ariakit-utils/store";
2 |
3 | import { PaginationState } from "./pagination-state";
4 |
5 | export const PaginationContextState = createStoreContext();
6 |
--------------------------------------------------------------------------------
/src/pagination/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./pagination-base";
2 | export * from "./pagination-button";
3 | export * from "./pagination-state";
4 |
--------------------------------------------------------------------------------
/src/pagination/pagination-base.ts:
--------------------------------------------------------------------------------
1 | import {
2 | createComponent,
3 | createElement,
4 | createHook,
5 | } from "ariakit-utils/system";
6 | import { useStoreProvider } from "ariakit-utils";
7 | import { As, Options, Props } from "ariakit-utils/types";
8 |
9 | import { PaginationContextState } from "./__utils";
10 | import { PaginationState } from "./pagination-state";
11 |
12 | export const usePagination = createHook(
13 | ({ state, ...props }) => {
14 | props = useStoreProvider({ state, ...props }, PaginationContextState);
15 | props = { "aria-label": "pagination", ...props };
16 |
17 | return props;
18 | },
19 | );
20 |
21 | export const Pagination = createComponent