Repository: IHIutch/draft-ui
Branch: main
Commit: f63a2c8be76d
Files: 329
Total size: 312.1 KB
Directory structure:
gitextract_vesfxg_a/
├── .editorconfig
├── .eslintrc.js
├── .gitignore
├── .husky/
│ └── pre-commit
├── .npmrc
├── .vscode/
│ └── settings.json
├── LICENSE.md
├── apps/
│ ├── docs/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── __registry__/
│ │ │ └── index.tsx
│ │ ├── app/
│ │ │ ├── changelog/
│ │ │ │ ├── layout.tsx
│ │ │ │ └── page.tsx
│ │ │ ├── docs/
│ │ │ │ └── [[...slug]]/
│ │ │ │ ├── layout.tsx
│ │ │ │ └── page.tsx
│ │ │ ├── getting-started/
│ │ │ │ └── [[...slug]]/
│ │ │ │ ├── layout.tsx
│ │ │ │ └── page.tsx
│ │ │ ├── layout.tsx
│ │ │ ├── page.tsx
│ │ │ ├── provider.tsx
│ │ │ ├── robots.tsx
│ │ │ └── sitemap.tsx
│ │ ├── components/
│ │ │ ├── analytics.tsx
│ │ │ ├── copy-clipboard-button.tsx
│ │ │ ├── docs/
│ │ │ │ ├── callout.tsx
│ │ │ │ ├── component-example.tsx
│ │ │ │ ├── component-source.tsx
│ │ │ │ ├── heading.tsx
│ │ │ │ ├── jump-to-content-menu.tsx
│ │ │ │ ├── list-stepper.tsx
│ │ │ │ └── markdown.tsx
│ │ │ ├── edit-feedback.tsx
│ │ │ ├── fathom-analytics.tsx
│ │ │ ├── home-page/
│ │ │ │ ├── cta-section.tsx
│ │ │ │ └── example-section.tsx
│ │ │ ├── link-list.tsx
│ │ │ ├── mode-toggle.tsx
│ │ │ ├── navigation.tsx
│ │ │ ├── page-toc.tsx
│ │ │ ├── search-component.tsx
│ │ │ └── theme-provider.tsx
│ │ ├── content/
│ │ │ ├── CHANGELOG.mdx
│ │ │ ├── components/
│ │ │ │ ├── breadcrumbs.mdx
│ │ │ │ ├── button.mdx
│ │ │ │ ├── checkbox-group.mdx
│ │ │ │ ├── checkbox.mdx
│ │ │ │ ├── combo-box.mdx
│ │ │ │ ├── date-picker.mdx
│ │ │ │ ├── date-range-picker.mdx
│ │ │ │ ├── grid-list.mdx
│ │ │ │ ├── icon-button.mdx
│ │ │ │ ├── input.mdx
│ │ │ │ ├── label.mdx
│ │ │ │ ├── menu.mdx
│ │ │ │ ├── meter.mdx
│ │ │ │ ├── modal.mdx
│ │ │ │ ├── number-field.mdx
│ │ │ │ ├── progress-bar.mdx
│ │ │ │ ├── radio-group.mdx
│ │ │ │ ├── radio.mdx
│ │ │ │ ├── search-field.mdx
│ │ │ │ ├── select.mdx
│ │ │ │ ├── slider.mdx
│ │ │ │ ├── switch.mdx
│ │ │ │ ├── table.mdx
│ │ │ │ ├── tabs.mdx
│ │ │ │ ├── text-field.mdx
│ │ │ │ ├── toggle-button.mdx
│ │ │ │ └── tooltip.mdx
│ │ │ └── getting-started/
│ │ │ ├── about.mdx
│ │ │ ├── cli.mdx
│ │ │ ├── installation.mdx
│ │ │ └── introduction.mdx
│ │ ├── contentlayer.config.ts
│ │ ├── lib/
│ │ │ ├── cva.config.ts
│ │ │ ├── rehype-component.ts
│ │ │ └── remark/
│ │ │ └── with-table-of-contents.ts
│ │ ├── next.config.js
│ │ ├── package.json
│ │ ├── postcss.config.js
│ │ ├── public/
│ │ │ └── site.webmanifest
│ │ ├── registry/
│ │ │ ├── breadcrumbs/
│ │ │ │ └── default.tsx
│ │ │ ├── button/
│ │ │ │ ├── default.tsx
│ │ │ │ ├── disabled.tsx
│ │ │ │ ├── sizes.tsx
│ │ │ │ └── theme.tsx
│ │ │ ├── calendar/
│ │ │ │ └── default.tsx
│ │ │ ├── checkbox/
│ │ │ │ ├── default.tsx
│ │ │ │ ├── disabled.tsx
│ │ │ │ └── sizes.tsx
│ │ │ ├── checkbox-group/
│ │ │ │ ├── default.tsx
│ │ │ │ ├── disabled.tsx
│ │ │ │ └── horizontal.tsx
│ │ │ ├── combobox/
│ │ │ │ ├── default.tsx
│ │ │ │ ├── disabled-keys.tsx
│ │ │ │ ├── disabled.tsx
│ │ │ │ └── with-button.tsx
│ │ │ ├── date-field/
│ │ │ │ ├── default.tsx
│ │ │ │ └── disabled.tsx
│ │ │ ├── date-input/
│ │ │ │ ├── default.tsx
│ │ │ │ └── sizes.tsx
│ │ │ ├── date-picker/
│ │ │ │ ├── default.tsx
│ │ │ │ └── disabled.tsx
│ │ │ ├── date-range-picker/
│ │ │ │ ├── default.tsx
│ │ │ │ └── disabled.tsx
│ │ │ ├── grid-list/
│ │ │ │ └── default.tsx
│ │ │ ├── icon-button/
│ │ │ │ ├── default.tsx
│ │ │ │ ├── disabled.tsx
│ │ │ │ ├── sizes.tsx
│ │ │ │ └── theme.tsx
│ │ │ ├── input/
│ │ │ │ ├── default.tsx
│ │ │ │ ├── disabled.tsx
│ │ │ │ └── sizes.tsx
│ │ │ ├── label/
│ │ │ │ └── default.tsx
│ │ │ ├── menu/
│ │ │ │ ├── as-checkbox.tsx
│ │ │ │ ├── as-radio.tsx
│ │ │ │ ├── default.tsx
│ │ │ │ ├── disabled.tsx
│ │ │ │ └── with-sections.tsx
│ │ │ ├── meter/
│ │ │ │ └── default.tsx
│ │ │ ├── modal/
│ │ │ │ ├── default.tsx
│ │ │ │ ├── dismissable-false.tsx
│ │ │ │ ├── set-autofocus.tsx
│ │ │ │ └── sizes.tsx
│ │ │ ├── number-field/
│ │ │ │ ├── default.tsx
│ │ │ │ ├── disabled.tsx
│ │ │ │ ├── with-mobile-stepper.tsx
│ │ │ │ └── with-stepper.tsx
│ │ │ ├── progress-bar/
│ │ │ │ └── default.tsx
│ │ │ ├── radio/
│ │ │ │ ├── default.tsx
│ │ │ │ ├── disabled.tsx
│ │ │ │ └── sizes.tsx
│ │ │ ├── radio-group/
│ │ │ │ ├── default.tsx
│ │ │ │ ├── disabled.tsx
│ │ │ │ └── horizontal.tsx
│ │ │ ├── range-calendar/
│ │ │ │ └── default.tsx
│ │ │ ├── search-field/
│ │ │ │ ├── default.tsx
│ │ │ │ └── with-clear-button.tsx
│ │ │ ├── select/
│ │ │ │ ├── default.tsx
│ │ │ │ └── disabled.tsx
│ │ │ ├── slider/
│ │ │ │ ├── default.tsx
│ │ │ │ └── vertical.tsx
│ │ │ ├── switch/
│ │ │ │ ├── alignment.tsx
│ │ │ │ ├── default.tsx
│ │ │ │ ├── disabled.tsx
│ │ │ │ └── sizes.tsx
│ │ │ ├── tabs/
│ │ │ │ ├── default.tsx
│ │ │ │ ├── disabled-keys.tsx
│ │ │ │ ├── disabled.tsx
│ │ │ │ └── vertical.tsx
│ │ │ ├── text-field/
│ │ │ │ ├── default.tsx
│ │ │ │ ├── disabled.tsx
│ │ │ │ └── with-error.tsx
│ │ │ └── tooltip/
│ │ │ ├── default.tsx
│ │ │ └── placement.tsx
│ │ ├── styles/
│ │ │ └── globals.css
│ │ ├── tailwind.config.js
│ │ ├── tsconfig.json
│ │ └── types.ts
│ └── storybook/
│ ├── .eslintignore
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .storybook/
│ │ ├── main.ts
│ │ └── preview.ts
│ ├── package.json
│ ├── postcss.config.cjs
│ ├── src/
│ │ ├── components/
│ │ │ ├── breadcrumbs/
│ │ │ │ ├── breadcrumbs.stories.tsx
│ │ │ │ └── stories/
│ │ │ │ └── default.tsx
│ │ │ ├── button/
│ │ │ │ ├── button.stories.tsx
│ │ │ │ └── stories/
│ │ │ │ ├── default.tsx
│ │ │ │ ├── disabled.tsx
│ │ │ │ ├── sizes.tsx
│ │ │ │ └── theme.tsx
│ │ │ ├── calendar/
│ │ │ │ ├── calendar.stories.tsx
│ │ │ │ └── stories/
│ │ │ │ └── default.tsx
│ │ │ ├── checkbox/
│ │ │ │ ├── checkbox.stories.tsx
│ │ │ │ └── stories/
│ │ │ │ ├── default.tsx
│ │ │ │ ├── disabled.tsx
│ │ │ │ └── sizes.tsx
│ │ │ ├── checkbox-group/
│ │ │ │ ├── checkbox-group.stories.tsx
│ │ │ │ └── stories/
│ │ │ │ ├── default.tsx
│ │ │ │ ├── disabled.tsx
│ │ │ │ └── horizontal.tsx
│ │ │ ├── combobox/
│ │ │ │ ├── combo-box.stories.tsx
│ │ │ │ └── stories/
│ │ │ │ ├── default.tsx
│ │ │ │ ├── disabled-keys.tsx
│ │ │ │ ├── disabled.tsx
│ │ │ │ └── with-button.tsx
│ │ │ ├── date-field/
│ │ │ │ ├── date-field.stories.tsx
│ │ │ │ └── stories/
│ │ │ │ ├── default.tsx
│ │ │ │ └── disabled.tsx
│ │ │ ├── date-input/
│ │ │ │ ├── date-input.stories.tsx
│ │ │ │ └── stories/
│ │ │ │ ├── default.tsx
│ │ │ │ └── sizes.tsx
│ │ │ ├── date-picker/
│ │ │ │ ├── date-picker.stories.tsx
│ │ │ │ └── stories/
│ │ │ │ ├── default.tsx
│ │ │ │ └── disabled.tsx
│ │ │ ├── date-range-picker/
│ │ │ │ ├── date-range-picker.stories.tsx
│ │ │ │ └── stories/
│ │ │ │ ├── default.tsx
│ │ │ │ └── disabled.tsx
│ │ │ ├── grid-list/
│ │ │ │ ├── grid-list.stories.tsx
│ │ │ │ └── stories/
│ │ │ │ └── default.tsx
│ │ │ ├── icon-button/
│ │ │ │ ├── icon-button.stories.tsx
│ │ │ │ └── stories/
│ │ │ │ ├── default.tsx
│ │ │ │ ├── disabled.tsx
│ │ │ │ ├── sizes.tsx
│ │ │ │ └── theme.tsx
│ │ │ ├── input/
│ │ │ │ ├── input.stories.tsx
│ │ │ │ └── stories/
│ │ │ │ ├── default.tsx
│ │ │ │ ├── disabled.tsx
│ │ │ │ └── sizes.tsx
│ │ │ ├── label/
│ │ │ │ ├── label.stories.tsx
│ │ │ │ └── stories/
│ │ │ │ └── default.tsx
│ │ │ ├── menu/
│ │ │ │ ├── menu.stories.tsx
│ │ │ │ └── stories/
│ │ │ │ ├── as-checkbox.tsx
│ │ │ │ ├── as-radio.tsx
│ │ │ │ ├── default.tsx
│ │ │ │ ├── disabled.tsx
│ │ │ │ └── with-sections.tsx
│ │ │ ├── meter/
│ │ │ │ ├── meter.stories.tsx
│ │ │ │ └── stories/
│ │ │ │ └── default.tsx
│ │ │ ├── modal/
│ │ │ │ ├── modal.stories.tsx
│ │ │ │ └── stories/
│ │ │ │ ├── default.tsx
│ │ │ │ ├── dismissable-false.tsx
│ │ │ │ ├── set-autofocus.tsx
│ │ │ │ └── sizes.tsx
│ │ │ ├── number-field/
│ │ │ │ ├── number-field.stories.tsx
│ │ │ │ └── stories/
│ │ │ │ ├── default.tsx
│ │ │ │ ├── disabled.tsx
│ │ │ │ ├── with-mobile-stepper.tsx
│ │ │ │ └── with-stepper.tsx
│ │ │ ├── progress-bar/
│ │ │ │ ├── progress-bar.stories.tsx
│ │ │ │ └── stories/
│ │ │ │ └── default.tsx
│ │ │ ├── radio/
│ │ │ │ ├── radio.stories.tsx
│ │ │ │ └── stories/
│ │ │ │ ├── default.tsx
│ │ │ │ ├── disabled.tsx
│ │ │ │ └── sizes.tsx
│ │ │ ├── radio-group/
│ │ │ │ ├── radio-group.stories.tsx
│ │ │ │ └── stories/
│ │ │ │ ├── default.tsx
│ │ │ │ ├── disabled.tsx
│ │ │ │ └── horizontal.tsx
│ │ │ ├── range-calendar/
│ │ │ │ ├── range-calendar.stories.tsx
│ │ │ │ └── stories/
│ │ │ │ └── default.tsx
│ │ │ ├── search-field/
│ │ │ │ ├── search-field.stories.tsx
│ │ │ │ └── stories/
│ │ │ │ ├── default.tsx
│ │ │ │ └── with-clear-button.tsx
│ │ │ ├── select/
│ │ │ │ ├── select.stories.tsx
│ │ │ │ └── stories/
│ │ │ │ ├── default.tsx
│ │ │ │ └── disabled.tsx
│ │ │ ├── slider/
│ │ │ │ ├── slider.stories.tsx
│ │ │ │ └── stories/
│ │ │ │ ├── default.tsx
│ │ │ │ └── vertical.tsx
│ │ │ ├── switch/
│ │ │ │ ├── stories/
│ │ │ │ │ ├── alignment.tsx
│ │ │ │ │ ├── default.tsx
│ │ │ │ │ ├── disabled.tsx
│ │ │ │ │ └── sizes.tsx
│ │ │ │ └── switch.stories.tsx
│ │ │ ├── tabs/
│ │ │ │ ├── stories/
│ │ │ │ │ ├── default.tsx
│ │ │ │ │ ├── disabled-keys.tsx
│ │ │ │ │ ├── disabled.tsx
│ │ │ │ │ └── vertical.tsx
│ │ │ │ └── tabs.stories.tsx
│ │ │ ├── text-field/
│ │ │ │ ├── stories/
│ │ │ │ │ ├── default.tsx
│ │ │ │ │ ├── disabled.tsx
│ │ │ │ │ └── with-error.tsx
│ │ │ │ └── text-field.stories.tsx
│ │ │ └── tooltip/
│ │ │ ├── stories/
│ │ │ │ ├── default.tsx
│ │ │ │ └── placement.tsx
│ │ │ └── tooltip.stories.tsx
│ │ └── styles.css
│ ├── tailwind.config.cjs
│ └── tsconfig.json
├── package.json
├── packages/
│ ├── eslint-config-custom/
│ │ ├── index.js
│ │ └── package.json
│ ├── postcss-config/
│ │ ├── package.json
│ │ └── postcss.config.cjs
│ ├── tailwind-config/
│ │ ├── package.json
│ │ └── tailwind.config.cjs
│ ├── ts-config/
│ │ ├── base.json
│ │ ├── nextjs.json
│ │ ├── package.json
│ │ └── react-library.json
│ └── ui/
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── lib/
│ │ └── cva.config.ts
│ ├── package.json
│ ├── postcss.config.cjs
│ ├── src/
│ │ ├── breadcrumbs.tsx
│ │ ├── button.tsx
│ │ ├── calendar.tsx
│ │ ├── checkbox-group.tsx
│ │ ├── checkbox.tsx
│ │ ├── combobox.tsx
│ │ ├── date-input.tsx
│ │ ├── date-picker.tsx
│ │ ├── date-range-picker.tsx
│ │ ├── grid-list.tsx
│ │ ├── icon-button.tsx
│ │ ├── index.tsx
│ │ ├── input.tsx
│ │ ├── label.tsx
│ │ ├── menu.tsx
│ │ ├── meter.tsx
│ │ ├── modal.tsx
│ │ ├── number-field.tsx
│ │ ├── progress-bar.tsx
│ │ ├── radio-group.tsx
│ │ ├── radio.tsx
│ │ ├── search-field.tsx
│ │ ├── select.tsx
│ │ ├── slider.tsx
│ │ ├── styles.css
│ │ ├── switch.tsx
│ │ ├── table.tsx
│ │ ├── tabs.tsx
│ │ ├── text-field.tsx
│ │ ├── toggle-button.tsx
│ │ └── tooltip.tsx
│ ├── tailwind.config.cjs
│ └── tsconfig.json
├── pnpm-workspace.yaml
├── prettier.config.cjs
├── scripts/
│ ├── package.json
│ ├── src/
│ │ └── build-registry.ts
│ └── tsconfig.json
├── tsconfig.json
├── turbo.json
└── vercel.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
# editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
================================================
FILE: .eslintrc.js
================================================
module.exports = {
root: true,
extends: ['custom'],
settings: {
next: {
rootDir: ['apps/docs/*/'],
},
},
}
================================================
FILE: .gitignore
================================================
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
node_modules
.pnp
.pnp.js
# testing
coverage
# next.js
.next/
out/
# production
build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
# contentlayer
.contentlayer
storybook-static/
# turbo
.turbo
#ui
dist/
================================================
FILE: .husky/pre-commit
================================================
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx lint-staged
================================================
FILE: .npmrc
================================================
auto-install-peers=true
legacy-peer-deps=true
node-linker=hoisted
================================================
FILE: .vscode/settings.json
================================================
{
"prettier.enable": false,
"editor.formatOnSave": false,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit",
"source.organizeImports": "never"
},
// The following is optional.
// It's better to put under project setting `.vscode/settings.json`
// to avoid conflicts with working with different eslint configs
// that does not support all formats.
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
// "markdown",
"json",
"yaml"
],
"files.associations": {
"*.mdx": "mdx"
},
"tailwindCSS.experimental.classRegex": [
[
"cva\\(([^)]*)\\)",
"[\"'`]([^\"'`]*).*?[\"'`]"
],
[
"cx\\(([^)]*)\\)",
"(?:'|\"|`)([^']*)(?:'|\"|`)"
]
],
"typescript.tsdk": "node_modules/typescript/lib"
}
================================================
FILE: LICENSE.md
================================================
MIT License
Copyright (c) 2024 Jonathan Hutchison
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: apps/docs/.eslintrc.js
================================================
// eslint-disable-next-line @typescript-eslint/no-var-requires
const path = require('path')
module.exports = {
root: true,
extends: ['custom'],
rules: {
'@next/next/no-html-link-for-pages': 'off',
},
settings: {
tailwindcss: {
config: path.join(__dirname, './tailwind.config.js'),
},
},
overrides: [
// MDX
{
files: ['*.md', '*.mdx'],
extends: ['plugin:mdx/recommended'],
},
],
ignorePatterns: ['registry/**/*.tsx'],
}
================================================
FILE: apps/docs/.gitignore
================================================
.vscode
.env
# contentlayer
.contentlayer
================================================
FILE: apps/docs/README.md
================================================
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
## Getting Started
First, run the development server:
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`.
The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
================================================
FILE: apps/docs/__registry__/index.tsx
================================================
/* eslint-disable prettier/prettier */
// @ts-nocheck
// This file is autogenerated by scripts/build-registry.ts
// Do not edit this file directly.
import * as React from "react"
type ComponentRegistry = Record<
string,
Record<
string,
{
name: string
story: string
component: string
file: string
}
>
>
export const Index: ComponentRegistry = {
"breadcrumbs": {
"default": {
name: "breadcrumbs-default",
story: "default",
component: React.lazy(() => import("@/registry/breadcrumbs/default")),
file: "registry/breadcrumbs/default.tsx"
},
},
"button": {
"default": {
name: "button-default",
story: "default",
component: React.lazy(() => import("@/registry/button/default")),
file: "registry/button/default.tsx"
},
"disabled": {
name: "button-disabled",
story: "disabled",
component: React.lazy(() => import("@/registry/button/disabled")),
file: "registry/button/disabled.tsx"
},
"sizes": {
name: "button-sizes",
story: "sizes",
component: React.lazy(() => import("@/registry/button/sizes")),
file: "registry/button/sizes.tsx"
},
"theme": {
name: "button-theme",
story: "theme",
component: React.lazy(() => import("@/registry/button/theme")),
file: "registry/button/theme.tsx"
},
},
"calendar": {
"default": {
name: "calendar-default",
story: "default",
component: React.lazy(() => import("@/registry/calendar/default")),
file: "registry/calendar/default.tsx"
},
},
"checkbox": {
"default": {
name: "checkbox-default",
story: "default",
component: React.lazy(() => import("@/registry/checkbox/default")),
file: "registry/checkbox/default.tsx"
},
"disabled": {
name: "checkbox-disabled",
story: "disabled",
component: React.lazy(() => import("@/registry/checkbox/disabled")),
file: "registry/checkbox/disabled.tsx"
},
"sizes": {
name: "checkbox-sizes",
story: "sizes",
component: React.lazy(() => import("@/registry/checkbox/sizes")),
file: "registry/checkbox/sizes.tsx"
},
},
"checkbox-group": {
"default": {
name: "checkbox-group-default",
story: "default",
component: React.lazy(() => import("@/registry/checkbox-group/default")),
file: "registry/checkbox-group/default.tsx"
},
"disabled": {
name: "checkbox-group-disabled",
story: "disabled",
component: React.lazy(() => import("@/registry/checkbox-group/disabled")),
file: "registry/checkbox-group/disabled.tsx"
},
"horizontal": {
name: "checkbox-group-horizontal",
story: "horizontal",
component: React.lazy(() => import("@/registry/checkbox-group/horizontal")),
file: "registry/checkbox-group/horizontal.tsx"
},
},
"combobox": {
"default": {
name: "combobox-default",
story: "default",
component: React.lazy(() => import("@/registry/combobox/default")),
file: "registry/combobox/default.tsx"
},
"disabled-keys": {
name: "combobox-disabled-keys",
story: "disabled-keys",
component: React.lazy(() => import("@/registry/combobox/disabled-keys")),
file: "registry/combobox/disabled-keys.tsx"
},
"disabled": {
name: "combobox-disabled",
story: "disabled",
component: React.lazy(() => import("@/registry/combobox/disabled")),
file: "registry/combobox/disabled.tsx"
},
"with-button": {
name: "combobox-with-button",
story: "with-button",
component: React.lazy(() => import("@/registry/combobox/with-button")),
file: "registry/combobox/with-button.tsx"
},
},
"date-field": {
"default": {
name: "date-field-default",
story: "default",
component: React.lazy(() => import("@/registry/date-field/default")),
file: "registry/date-field/default.tsx"
},
"disabled": {
name: "date-field-disabled",
story: "disabled",
component: React.lazy(() => import("@/registry/date-field/disabled")),
file: "registry/date-field/disabled.tsx"
},
},
"date-input": {
"default": {
name: "date-input-default",
story: "default",
component: React.lazy(() => import("@/registry/date-input/default")),
file: "registry/date-input/default.tsx"
},
"sizes": {
name: "date-input-sizes",
story: "sizes",
component: React.lazy(() => import("@/registry/date-input/sizes")),
file: "registry/date-input/sizes.tsx"
},
},
"date-picker": {
"default": {
name: "date-picker-default",
story: "default",
component: React.lazy(() => import("@/registry/date-picker/default")),
file: "registry/date-picker/default.tsx"
},
"disabled": {
name: "date-picker-disabled",
story: "disabled",
component: React.lazy(() => import("@/registry/date-picker/disabled")),
file: "registry/date-picker/disabled.tsx"
},
},
"date-range-picker": {
"default": {
name: "date-range-picker-default",
story: "default",
component: React.lazy(() => import("@/registry/date-range-picker/default")),
file: "registry/date-range-picker/default.tsx"
},
"disabled": {
name: "date-range-picker-disabled",
story: "disabled",
component: React.lazy(() => import("@/registry/date-range-picker/disabled")),
file: "registry/date-range-picker/disabled.tsx"
},
},
"grid-list": {
"default": {
name: "grid-list-default",
story: "default",
component: React.lazy(() => import("@/registry/grid-list/default")),
file: "registry/grid-list/default.tsx"
},
},
"icon-button": {
"default": {
name: "icon-button-default",
story: "default",
component: React.lazy(() => import("@/registry/icon-button/default")),
file: "registry/icon-button/default.tsx"
},
"disabled": {
name: "icon-button-disabled",
story: "disabled",
component: React.lazy(() => import("@/registry/icon-button/disabled")),
file: "registry/icon-button/disabled.tsx"
},
"sizes": {
name: "icon-button-sizes",
story: "sizes",
component: React.lazy(() => import("@/registry/icon-button/sizes")),
file: "registry/icon-button/sizes.tsx"
},
"theme": {
name: "icon-button-theme",
story: "theme",
component: React.lazy(() => import("@/registry/icon-button/theme")),
file: "registry/icon-button/theme.tsx"
},
},
"input": {
"default": {
name: "input-default",
story: "default",
component: React.lazy(() => import("@/registry/input/default")),
file: "registry/input/default.tsx"
},
"disabled": {
name: "input-disabled",
story: "disabled",
component: React.lazy(() => import("@/registry/input/disabled")),
file: "registry/input/disabled.tsx"
},
"sizes": {
name: "input-sizes",
story: "sizes",
component: React.lazy(() => import("@/registry/input/sizes")),
file: "registry/input/sizes.tsx"
},
},
"label": {
"default": {
name: "label-default",
story: "default",
component: React.lazy(() => import("@/registry/label/default")),
file: "registry/label/default.tsx"
},
},
"menu": {
"as-checkbox": {
name: "menu-as-checkbox",
story: "as-checkbox",
component: React.lazy(() => import("@/registry/menu/as-checkbox")),
file: "registry/menu/as-checkbox.tsx"
},
"as-radio": {
name: "menu-as-radio",
story: "as-radio",
component: React.lazy(() => import("@/registry/menu/as-radio")),
file: "registry/menu/as-radio.tsx"
},
"default": {
name: "menu-default",
story: "default",
component: React.lazy(() => import("@/registry/menu/default")),
file: "registry/menu/default.tsx"
},
"disabled": {
name: "menu-disabled",
story: "disabled",
component: React.lazy(() => import("@/registry/menu/disabled")),
file: "registry/menu/disabled.tsx"
},
"with-sections": {
name: "menu-with-sections",
story: "with-sections",
component: React.lazy(() => import("@/registry/menu/with-sections")),
file: "registry/menu/with-sections.tsx"
},
},
"meter": {
"default": {
name: "meter-default",
story: "default",
component: React.lazy(() => import("@/registry/meter/default")),
file: "registry/meter/default.tsx"
},
},
"modal": {
"default": {
name: "modal-default",
story: "default",
component: React.lazy(() => import("@/registry/modal/default")),
file: "registry/modal/default.tsx"
},
"dismissable-false": {
name: "modal-dismissable-false",
story: "dismissable-false",
component: React.lazy(() => import("@/registry/modal/dismissable-false")),
file: "registry/modal/dismissable-false.tsx"
},
"set-autofocus": {
name: "modal-set-autofocus",
story: "set-autofocus",
component: React.lazy(() => import("@/registry/modal/set-autofocus")),
file: "registry/modal/set-autofocus.tsx"
},
"sizes": {
name: "modal-sizes",
story: "sizes",
component: React.lazy(() => import("@/registry/modal/sizes")),
file: "registry/modal/sizes.tsx"
},
},
"number-field": {
"default": {
name: "number-field-default",
story: "default",
component: React.lazy(() => import("@/registry/number-field/default")),
file: "registry/number-field/default.tsx"
},
"disabled": {
name: "number-field-disabled",
story: "disabled",
component: React.lazy(() => import("@/registry/number-field/disabled")),
file: "registry/number-field/disabled.tsx"
},
"with-mobile-stepper": {
name: "number-field-with-mobile-stepper",
story: "with-mobile-stepper",
component: React.lazy(() => import("@/registry/number-field/with-mobile-stepper")),
file: "registry/number-field/with-mobile-stepper.tsx"
},
"with-stepper": {
name: "number-field-with-stepper",
story: "with-stepper",
component: React.lazy(() => import("@/registry/number-field/with-stepper")),
file: "registry/number-field/with-stepper.tsx"
},
},
"progress-bar": {
"default": {
name: "progress-bar-default",
story: "default",
component: React.lazy(() => import("@/registry/progress-bar/default")),
file: "registry/progress-bar/default.tsx"
},
},
"radio": {
"default": {
name: "radio-default",
story: "default",
component: React.lazy(() => import("@/registry/radio/default")),
file: "registry/radio/default.tsx"
},
"disabled": {
name: "radio-disabled",
story: "disabled",
component: React.lazy(() => import("@/registry/radio/disabled")),
file: "registry/radio/disabled.tsx"
},
"sizes": {
name: "radio-sizes",
story: "sizes",
component: React.lazy(() => import("@/registry/radio/sizes")),
file: "registry/radio/sizes.tsx"
},
},
"radio-group": {
"default": {
name: "radio-group-default",
story: "default",
component: React.lazy(() => import("@/registry/radio-group/default")),
file: "registry/radio-group/default.tsx"
},
"disabled": {
name: "radio-group-disabled",
story: "disabled",
component: React.lazy(() => import("@/registry/radio-group/disabled")),
file: "registry/radio-group/disabled.tsx"
},
"horizontal": {
name: "radio-group-horizontal",
story: "horizontal",
component: React.lazy(() => import("@/registry/radio-group/horizontal")),
file: "registry/radio-group/horizontal.tsx"
},
},
"range-calendar": {
"default": {
name: "range-calendar-default",
story: "default",
component: React.lazy(() => import("@/registry/range-calendar/default")),
file: "registry/range-calendar/default.tsx"
},
},
"search-field": {
"default": {
name: "search-field-default",
story: "default",
component: React.lazy(() => import("@/registry/search-field/default")),
file: "registry/search-field/default.tsx"
},
"with-clear-button": {
name: "search-field-with-clear-button",
story: "with-clear-button",
component: React.lazy(() => import("@/registry/search-field/with-clear-button")),
file: "registry/search-field/with-clear-button.tsx"
},
},
"select": {
"default": {
name: "select-default",
story: "default",
component: React.lazy(() => import("@/registry/select/default")),
file: "registry/select/default.tsx"
},
"disabled": {
name: "select-disabled",
story: "disabled",
component: React.lazy(() => import("@/registry/select/disabled")),
file: "registry/select/disabled.tsx"
},
},
"slider": {
"default": {
name: "slider-default",
story: "default",
component: React.lazy(() => import("@/registry/slider/default")),
file: "registry/slider/default.tsx"
},
"vertical": {
name: "slider-vertical",
story: "vertical",
component: React.lazy(() => import("@/registry/slider/vertical")),
file: "registry/slider/vertical.tsx"
},
},
"switch": {
"alignment": {
name: "switch-alignment",
story: "alignment",
component: React.lazy(() => import("@/registry/switch/alignment")),
file: "registry/switch/alignment.tsx"
},
"default": {
name: "switch-default",
story: "default",
component: React.lazy(() => import("@/registry/switch/default")),
file: "registry/switch/default.tsx"
},
"disabled": {
name: "switch-disabled",
story: "disabled",
component: React.lazy(() => import("@/registry/switch/disabled")),
file: "registry/switch/disabled.tsx"
},
"sizes": {
name: "switch-sizes",
story: "sizes",
component: React.lazy(() => import("@/registry/switch/sizes")),
file: "registry/switch/sizes.tsx"
},
},
"tabs": {
"default": {
name: "tabs-default",
story: "default",
component: React.lazy(() => import("@/registry/tabs/default")),
file: "registry/tabs/default.tsx"
},
"disabled-keys": {
name: "tabs-disabled-keys",
story: "disabled-keys",
component: React.lazy(() => import("@/registry/tabs/disabled-keys")),
file: "registry/tabs/disabled-keys.tsx"
},
"disabled": {
name: "tabs-disabled",
story: "disabled",
component: React.lazy(() => import("@/registry/tabs/disabled")),
file: "registry/tabs/disabled.tsx"
},
"vertical": {
name: "tabs-vertical",
story: "vertical",
component: React.lazy(() => import("@/registry/tabs/vertical")),
file: "registry/tabs/vertical.tsx"
},
},
"text-field": {
"default": {
name: "text-field-default",
story: "default",
component: React.lazy(() => import("@/registry/text-field/default")),
file: "registry/text-field/default.tsx"
},
"disabled": {
name: "text-field-disabled",
story: "disabled",
component: React.lazy(() => import("@/registry/text-field/disabled")),
file: "registry/text-field/disabled.tsx"
},
"with-error": {
name: "text-field-with-error",
story: "with-error",
component: React.lazy(() => import("@/registry/text-field/with-error")),
file: "registry/text-field/with-error.tsx"
},
},
"tooltip": {
"default": {
name: "tooltip-default",
story: "default",
component: React.lazy(() => import("@/registry/tooltip/default")),
file: "registry/tooltip/default.tsx"
},
"placement": {
name: "tooltip-placement",
story: "placement",
component: React.lazy(() => import("@/registry/tooltip/placement")),
file: "registry/tooltip/placement.tsx"
},
},
};
================================================
FILE: apps/docs/app/changelog/layout.tsx
================================================
import {
allChangelogDocuments,
allComponentDocuments,
allGeneralDocuments,
} from 'contentlayer/generated'
import { notFound } from 'next/navigation'
import JumpToContentMenu from '@/components/docs/jump-to-content-menu'
import LinkList from '@/components/link-list'
import Navigation from '@/components/navigation'
const sortedComponents = allComponentDocuments
.filter((doc) => doc.isComponent === true)
.sort((a, b) => a.title.localeCompare(b.title))
.map((doc) => ({
isComing: doc?.isComing || false,
isWip: doc?.isWip || false,
slug: doc.slug,
title: doc.title,
}))
const sortedDocuments = [...allGeneralDocuments, ...allChangelogDocuments]
.sort((a, b) => a.order - b.order)
.map((doc) => ({
isNew: 'isNew' in doc ? doc.isNew : false,
slug: doc.slug,
title: doc.title,
}))
export default async function DocsLayout({
children,
}: {
children: React.ReactNode
}) {
const post = allChangelogDocuments[0]
if (!post) {
notFound()
}
return (
)
}
================================================
FILE: apps/docs/app/changelog/page.tsx
================================================
import * as React from 'react'
import { allChangelogDocuments } from 'contentlayer/generated'
import { type Metadata, type ResolvingMetadata } from 'next'
import { notFound } from 'next/navigation'
import Markdown from '@/components/docs/markdown'
import PageToc from '@/components/page-toc'
export function generateStaticParams() {
return allChangelogDocuments
}
export async function generateMetadata(
_,
parent: ResolvingMetadata,
): Promise {
const post = allChangelogDocuments[0]
if (!post) {
notFound()
}
const parentMeta = await parent
return {
title: post.title,
description: post.description || parentMeta?.openGraph?.description,
openGraph: {
siteName: parentMeta?.openGraph?.siteName,
title: post.title || parentMeta?.openGraph?.title,
description: post.description || parentMeta?.openGraph?.description,
images: parentMeta?.openGraph?.images || [],
url: post.slug,
locale: parentMeta?.openGraph?.locale,
},
twitter: {
title: post.title || parentMeta?.twitter?.title,
description: post.description || parentMeta?.twitter?.description || '',
images: parentMeta?.twitter?.images || [],
card: parentMeta?.twitter?.card,
},
alternates: {
canonical: '/changelog',
},
}
}
export default function ChangelogPage() {
const post = allChangelogDocuments[0]
if (!post) {
notFound()
}
return (
<>
{post.title}
{post.description}
>
)
}
================================================
FILE: apps/docs/app/docs/[[...slug]]/layout.tsx
================================================
import {
allChangelogDocuments,
allComponentDocuments,
allGeneralDocuments,
} from 'contentlayer/generated'
import { notFound } from 'next/navigation'
import JumpToContentMenu from '@/components/docs/jump-to-content-menu'
import LinkList from '@/components/link-list'
import Navigation from '@/components/navigation'
const sortedComponents = allComponentDocuments
.filter((doc) => doc.isComponent === true)
.sort((a, b) => a.title.localeCompare(b.title))
.map((doc) => ({
isComing: doc?.isComing || false,
isWip: doc?.isWip || false,
slug: doc.slug,
title: doc.title,
}))
const sortedDocuments = [...allGeneralDocuments, ...allChangelogDocuments]
.sort((a, b) => a.order - b.order)
.map((doc) => ({
isNew: 'isNew' in doc ? doc.isNew : false,
slug: doc.slug,
title: doc.title,
}))
export default function DocsLayout({
params,
children,
}: {
params: { slug: Array }
children: React.ReactNode
}) {
const post = allComponentDocuments.find((post) => {
return (
post._raw.flattenedPath.replace('docs/', '') === params.slug.join('/')
)
})
if (!post) {
notFound()
}
return (
)
}
================================================
FILE: apps/docs/app/docs/[[...slug]]/page.tsx
================================================
import { allComponentDocuments } from 'contentlayer/generated'
import { type Metadata, type ResolvingMetadata } from 'next'
import { notFound } from 'next/navigation'
import Markdown from '@/components/docs/markdown'
import EditFeedbackLinks from '@/components/edit-feedback'
import PageToc from '@/components/page-toc'
export function generateStaticParams() {
return allComponentDocuments.map((post) => {
return {
slug: post._raw.flattenedPath.split('/'),
}
})
}
export async function generateMetadata(
{ params }: { params: { slug: string[] } },
parent: ResolvingMetadata,
): Promise {
const post = allComponentDocuments.find((post) => {
return (
post._raw.flattenedPath.replace('docs/', '') === params.slug.join('/')
)
})
if (!post) {
notFound()
}
const parentMeta = await parent
return {
title: post.title,
description: post.description || parentMeta?.openGraph?.description,
openGraph: {
siteName: parentMeta?.openGraph?.siteName,
title: post.title || parentMeta?.openGraph?.title,
description: post.description || parentMeta?.openGraph?.description,
images: parentMeta?.openGraph?.images || [],
url: post.slug,
locale: parentMeta?.openGraph?.locale,
},
twitter: {
title: post.title || parentMeta?.twitter?.title,
description: post.description || parentMeta?.twitter?.description || '',
images: parentMeta?.twitter?.images || [],
card: parentMeta?.twitter?.card,
},
alternates: {
canonical: post.slug,
},
}
}
export default function DocPage({
params,
}: {
params: { slug: Array }
}) {
const post = allComponentDocuments.find((post) => {
return (
post._raw.flattenedPath.replace('docs/', '') === params.slug.join('/')
)
})
if (!post) {
notFound()
}
return (
<>
{post.title}
{post.description}
>
)
}
================================================
FILE: apps/docs/app/getting-started/[[...slug]]/layout.tsx
================================================
import {
allChangelogDocuments,
allComponentDocuments,
allGeneralDocuments,
} from 'contentlayer/generated'
import { notFound } from 'next/navigation'
import JumpToContentMenu from '@/components/docs/jump-to-content-menu'
import LinkList from '@/components/link-list'
import Navigation from '@/components/navigation'
const sortedComponents = allComponentDocuments
.filter((doc) => doc.isComponent === true)
.sort((a, b) => a.title.localeCompare(b.title))
.map((doc) => ({
isComing: doc?.isComing || false,
isWip: doc?.isWip || false,
slug: doc.slug,
title: doc.title,
}))
const sortedDocuments = [...allGeneralDocuments, ...allChangelogDocuments]
.sort((a, b) => a.order - b.order)
.map((doc) => ({
isNew: 'isNew' in doc ? doc.isNew : false,
slug: doc.slug,
title: doc.title,
}))
export default function DocsLayout({
params,
children,
}: {
params: { slug: Array }
children: React.ReactNode
}) {
const post = allGeneralDocuments.find((post) => {
return (
post._raw.flattenedPath.replace('getting-started/', '') ===
params.slug.join('/')
)
})
if (!post) {
notFound()
}
return (
)
}
================================================
FILE: apps/docs/app/getting-started/[[...slug]]/page.tsx
================================================
import * as React from 'react'
import { allGeneralDocuments } from 'contentlayer/generated'
import { type Metadata, type ResolvingMetadata } from 'next'
import { notFound } from 'next/navigation'
import Markdown from '@/components/docs/markdown'
import EditFeedbackLinks from '@/components/edit-feedback'
import PageToc from '@/components/page-toc'
export function generateStaticParams() {
return allGeneralDocuments.map((post) => {
return {
slug: post._raw.flattenedPath.split('/'),
}
})
}
export async function generateMetadata(
{ params }: { params: { slug: string[] } },
parent: ResolvingMetadata,
): Promise {
const post = allGeneralDocuments.find((post) => {
return (
post._raw.flattenedPath.replace('getting-started/', '') ===
params.slug.join('/')
)
})
if (!post) {
notFound()
}
const parentMeta = await parent
return {
title: post.title,
description: post.description || parentMeta?.openGraph?.description,
openGraph: {
siteName: parentMeta?.openGraph?.siteName,
title: post.title || parentMeta?.openGraph?.title,
description: post.description || parentMeta?.openGraph?.description,
images: parentMeta?.openGraph?.images || [],
url: post.slug,
locale: parentMeta?.openGraph?.locale,
},
twitter: {
title: post.title || parentMeta?.twitter?.title,
description: post.description || parentMeta?.twitter?.description || '',
images: parentMeta?.twitter?.images || [],
card: parentMeta?.twitter?.card,
},
alternates: {
canonical: post.slug,
},
}
}
export default function GettingStartedPage({
params,
}: {
params: { slug: string[] }
}) {
const post = allGeneralDocuments.find((post) => {
return (
post._raw.flattenedPath.replace('getting-started/', '') ===
params.slug.join('/')
)
})
if (!post) {
notFound()
}
return (
<>
{post.toc.length > 0 ?
: null}
{post.title}
{post.description}
>
)
}
================================================
FILE: apps/docs/app/layout.tsx
================================================
import '@/styles/globals.css'
import { type Metadata } from 'next'
import { Inter } from 'next/font/google'
import Analytics from '@/components/analytics'
import ThemeProvider from '@/components/theme-provider'
import { ClientProviders } from './provider'
interface DocsLayoutProps {
children: React.ReactNode
}
const inter = Inter({
subsets: ['latin'],
display: 'swap',
})
export function generateMetadata(): Metadata {
const siteName = 'Draft UI'
const siteUrl = 'https://draft-ui.com'
const description =
'A collection of simply designed React components focused on making web accessibility easier than ever. Built with React Aria and Tailwind CSS.'
const imageUrl = '/meta.jpg'
const imageAlt =
'The Draft UI name and logo overlaying a whisp of wofting smoke'
return {
metadataBase: new URL(siteUrl),
title: {
template: `%s · ${siteName}`,
default: siteName,
},
description,
openGraph: {
siteName,
title: {
template: `%s · ${siteName}`,
default: siteName,
},
description,
url: '/',
images: {
url: imageUrl,
alt: imageAlt,
},
type: 'website',
locale: 'US_en',
},
twitter: {
title: {
template: `%s · ${siteName}`,
default: siteName,
},
description,
images: {
url: imageUrl,
alt: imageAlt,
},
card: 'summary_large_image',
},
verification: {
google: 'hsAvKsU0gaaI6wN1wUtrHAEMVQORMU08rUqYQHMj1x0',
},
alternates: {
canonical: '/',
},
}
}
export default function DocsLayout({ children }: DocsLayoutProps) {
return (
{/*
will contain the components returned by the nearest parent
head.tsx. Find out more at https://beta.nextjs.org/docs/api-reference/file-conventions/head
*/}
{children}
)
}
================================================
FILE: apps/docs/app/page.tsx
================================================
import {
allChangelogDocuments,
allComponentDocuments,
allGeneralDocuments,
} from 'contentlayer/generated'
import CtaSection from '@/components/home-page/cta-section'
import ExampleSection from '@/components/home-page/example-section'
import Navigation from '@/components/navigation'
export default async function Home() {
const sortedComponents = allComponentDocuments
.filter((doc) => doc.isComponent === true)
.sort((a, b) => a.title.localeCompare(b.title))
const sortedDocuments = [
...allGeneralDocuments,
...allChangelogDocuments,
].sort((a, b) => a.order - b.order)
return (
Accessibility doesn't have
to be hard
Draft UI is a collection of simply designed React components
focused on making web accessibility as easy as copy & paste.
Built with{' '}
React Aria Components
{' '}
and{' '}
Tailwind CSS
25+ Accessible Components
)
}
================================================
FILE: apps/docs/app/provider.tsx
================================================
'use client'
import { useRouter } from 'next/navigation'
import { RouterProvider } from 'react-aria-components'
export function ClientProviders({ children }) {
const router = useRouter()
return {children}
}
================================================
FILE: apps/docs/app/robots.tsx
================================================
import { type MetadataRoute } from 'next'
export default function robots(): MetadataRoute.Robots {
return {
rules: {
userAgent: '*',
allow: '/',
disallow: '/private/',
},
sitemap: 'https://draft-ui.com/sitemap.xml',
}
}
================================================
FILE: apps/docs/app/sitemap.tsx
================================================
import { allDocuments } from 'contentlayer/generated'
import { type MetadataRoute } from 'next'
export default function sitemap(): MetadataRoute.Sitemap {
const siteUrl = 'https://draft-ui.com'
const urls = allDocuments.map((post) => {
return { url: siteUrl + post.slug }
})
return [
{
url: siteUrl,
},
...urls,
]
}
================================================
FILE: apps/docs/components/analytics.tsx
================================================
'use client'
import { Analytics as VercelAnalytics } from '@vercel/analytics/react'
import Fathom from './fathom-analytics'
export default function Analytics() {
return (
<>
>
)
}
================================================
FILE: apps/docs/components/copy-clipboard-button.tsx
================================================
'use client'
import { useState } from 'react'
import { Copy } from 'lucide-react'
import { IconButton, Tooltip, TooltipContent } from 'ui'
export default function CopyClipboardButton({ text }: { text: string }) {
const [isOpen, setOpen] = useState(false)
const handleCopyToClipboard = () => {
setOpen(true)
navigator.clipboard.writeText(text)
setTimeout(() => {
setOpen(false)
}, 800)
}
return (
Code copied!
)
}
================================================
FILE: apps/docs/components/docs/callout.tsx
================================================
import * as React from 'react'
import { AlertTriangle, Info, MessageSquare } from 'lucide-react'
import { match } from 'ts-pattern'
export default function Callout({
type,
children,
}: {
type: 'note' | 'warning' | 'important'
children: React.ReactNode
}) {
return match(type)
.with('note', () => (
))
.with('important', () => (
))
.with('warning', () => (
))
.exhaustive()
}
================================================
FILE: apps/docs/components/docs/component-example.tsx
================================================
'use client'
import * as React from 'react'
import { Tab, TabList, TabPanel, Tabs } from 'ui'
import { Index } from '@/__registry__'
import { cx } from '@/lib/cva.config'
import CopyClipboardButton from '../copy-clipboard-button'
interface ComponentExampleProps extends React.HTMLAttributes {
align?: 'center' | 'start' | 'end'
name: string
story: string
}
export default function ComponentExample({
align = 'center',
name,
story,
children,
}: ComponentExampleProps) {
const [Code] = React.Children.toArray(children) as React.ReactElement[]
const codeString = React.useMemo(() => {
if (
typeof Code?.props['data-rehype-pretty-code-fragment'] !== 'undefined'
) {
const [codeToCopy] = React.Children.toArray(
Code.props.children,
) as React.ReactElement[]
return codeToCopy?.props?.__rawString__ || null
}
}, [Code])
const Story = React.useMemo(() => {
const Component = Index[name]?.[story]?.component
if (!Component) {
return (
{name}{' '}
"{story}"
{' '}
not found in registry.
)
}
return
}, [name, story])
return (
)
}
================================================
FILE: apps/docs/components/docs/component-source.tsx
================================================
'use client'
import * as React from 'react'
import CopyClipboardButton from '../copy-clipboard-button'
export default function ComponentSource({ children }) {
const [Code] = React.Children.toArray(children) as React.ReactElement[]
const codeString = React.useMemo(() => {
if (
typeof Code?.props['data-rehype-pretty-code-fragment'] !== 'undefined'
) {
const [Button] = React.Children.toArray(
Code.props.children,
) as React.ReactElement[]
return Button?.props?.value || Button?.props?.__rawString__ || null
}
}, [Code])
return (
)
}
================================================
FILE: apps/docs/components/docs/heading.tsx
================================================
'use client'
import * as React from 'react'
import { type TocItemProps } from '@/types'
interface HeadingProps extends TocItemProps {
children: React.ReactNode
}
export default function Heading({ slug, lvl, children }: HeadingProps) {
const headingEl = React.createElement(
`h${lvl}`,
{
id: slug,
className:
'mt-0 scroll-mt-20 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-slate-400 focus-visible:ring-offset-2 dark:focus-visible:ring-slate-400 dark:focus-visible:ring-offset-slate-900 rounded-sm',
tabIndex: -1,
},
children,
)
return {headingEl}
}
================================================
FILE: apps/docs/components/docs/jump-to-content-menu.tsx
================================================
'use client'
import { ChevronDown } from 'lucide-react'
import { Button } from 'ui/src/button'
import {
Menu,
MenuContent,
MenuHeader,
MenuItem,
MenuSection,
MenuSeparator,
} from 'ui/src/menu'
import { type TocItemProps } from '@/types'
export default function JumpToContentMenu({
toc,
}: {
toc: Array
}) {
const focusAnchorElement = (anchor: string) => {
const el = document?.getElementById(anchor)
if (el) el.focus()
}
return (
Skip to Content
focusAnchorElement(slug)}
>
Landmark Regions
Page Content
Site Navigation
Page Navigation
{toc ? (
Headings
{toc.map((heading: TocItemProps, idx: number) => (
{heading.content}
))}
) : null}
)
}
================================================
FILE: apps/docs/components/docs/list-stepper.tsx
================================================
import * as React from 'react'
export function ListStepper({ children }: { children: React.ReactNode }) {
return (
{children}
)
}
export function ListStep({ children }: { children: React.ReactNode }) {
return (
{children}
)
}
================================================
FILE: apps/docs/components/docs/markdown.tsx
================================================
'use client'
import * as React from 'react'
import { type DocumentTypes } from 'contentlayer/generated'
import { useMDXComponent } from 'next-contentlayer/hooks'
import Link from 'next/link'
import { Tab, TabList, TabPanel, Tabs } from 'ui'
import Callout from '@/components/docs/callout'
import ComponentExample from '@/components/docs/component-example'
import ComponentSource from '@/components/docs/component-source'
import Heading from '@/components/docs/heading'
import { cx } from '@/lib/cva.config'
import { type NpmCommands } from '@/types'
import CopyClipboardButton from '../copy-clipboard-button'
import { ListStep, ListStepper } from './list-stepper'
type MarkdownProps = {
doc: DocumentTypes
}
export default function Markdown(props: MarkdownProps) {
const { doc } = props
const MDXComponent = useMDXComponent(doc.body.code)
return (
) => (
),
figure: ({
className,
__npmCommand__,
__yarnCommand__,
__pnpmCommand__,
__niCommand__,
__rawString__,
...props
}: React.HTMLAttributes & {
__rawString__?: string
} & NpmCommands) => (
<>
{/* TODO: Introduce buttons that let you choose package manager */}
>
),
}}
/>
)
}
================================================
FILE: apps/docs/components/edit-feedback.tsx
================================================
export default function EditFeedbackLinks({
title,
contentPath,
}: {
title: string
contentPath: string
}) {
const pageEditUrl = `https://github.com/IHIutch/draft-ui/tree/main/apps/docs/content/${contentPath}`
const pageFeedbackUrl = new URL(
`https://github.com/IHIutch/draft-ui/issues/new`,
)
pageFeedbackUrl.searchParams.set('title', `Feedback for “${title}”`)
// Is this the right label to set? Do we need to make a new label for this?
pageFeedbackUrl.searchParams.set('labels', 'documentation')
return (
Edit this page
Give us feedback
)
}
const LinkItem = ({
href,
children,
}: {
href: string
children?: React.ReactNode
}) => (
{children}
)
================================================
FILE: apps/docs/components/fathom-analytics.tsx
================================================
// Fathom.tsx
'use client'
import { Suspense, useEffect } from 'react'
import { load, trackPageview } from 'fathom-client'
import { usePathname, useSearchParams } from 'next/navigation'
function TrackPageView() {
const pathname = usePathname()
const searchParams = useSearchParams()
useEffect(() => {
load('ERIJKXRN', {
includedDomains: ['draft-ui.com'],
auto: false,
})
}, [])
// Record a pageview when route changes
useEffect(() => {
trackPageview()
}, [pathname, searchParams])
return null
}
export default function Fathom() {
return (
)
}
================================================
FILE: apps/docs/components/home-page/cta-section.tsx
================================================
'use client'
import * as React from 'react'
import Link from 'next/link'
import { buttonVariants } from '@/components/ui'
export default function CtaSection() {
return (
Get Started
Components
)
}
================================================
FILE: apps/docs/components/home-page/example-section.tsx
================================================
'use client'
import * as React from 'react'
import BreadcrumbsDefault from '@/registry/breadcrumbs/default'
import CheckboxGroupDefault from '@/registry/checkbox-group/default'
import ComboBoxWithButton from '@/registry/combobox/with-button'
import DatePickerDefault from '@/registry/date-picker/default'
import ModalDefault from '@/registry/modal/default'
import NumberInputMobile from '@/registry/number-field/with-mobile-stepper'
import SelectDefault from '@/registry/select/default'
import SliderDefault from '@/registry/slider/default'
import SwitchDefault from '@/registry/switch/default'
import TabsDefault from '@/registry/tabs/default'
export default function ExampleSection() {
return (
)
}
================================================
FILE: apps/docs/components/link-list.tsx
================================================
'use client'
import Link from 'next/link'
import { usePathname } from 'next/navigation'
import { cx } from '@/lib/cva.config'
type LinkListItemProps = {
isComing?: boolean
isWip?: boolean
isNew?: boolean
slug: string
title: string
}
export default function LinkList({ list }: { list: LinkListItemProps[] }) {
const pathname = usePathname()
return (
{list.map((link, idx) => (
{
if (!!link?.isComing) {
e.preventDefault()
}
}}
className={cx(
'flex items-center justify-between gap-2 rounded-sm py-1 text-sm transition',
// Focus
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-slate-400 focus-visible:ring-offset-2 dark:focus-visible:ring-slate-400 dark:focus-visible:ring-offset-slate-900',
pathname === link.slug
? 'font-medium text-slate-900 dark:text-white'
: 'text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-white',
link?.isComing &&
'cursor-not-allowed text-slate-400 hover:text-slate-400 dark:text-slate-600 dark:hover:text-slate-600',
)}
>
{link.title}
{link?.isComing ? (
Coming Soon
) : link?.isNew ? (
New!
) : link?.isWip ? (
WIP
) : null}
))}
)
}
================================================
FILE: apps/docs/components/mode-toggle.tsx
================================================
'use client'
import * as React from 'react'
import { LaptopIcon, MoonIcon, SunIcon } from 'lucide-react'
import { useTheme } from 'next-themes'
import { IconButton, Menu, MenuContent, MenuItem } from '@/components/ui'
export function ModeToggle() {
const { setTheme, resolvedTheme } = useTheme()
return (
{resolvedTheme === 'dark' ? (
) : (
)}
setTheme(value)}
>
Light
Dark
System
)
}
================================================
FILE: apps/docs/components/navigation.tsx
================================================
'use client'
import { useEffect, useState } from 'react'
import { GithubIcon, MenuIcon, TwitterIcon, XIcon } from 'lucide-react'
import Image from 'next/image'
import Link from 'next/link'
import { usePathname } from 'next/navigation'
import {
IconButton,
iconButtonVariants,
ModalBody,
ModalContent,
ModalHeader,
ModalOverlay,
} from 'ui'
import { cx } from '@/lib/cva.config'
import LinkList from './link-list'
import { ModeToggle } from './mode-toggle'
import SearchComponent from './search-component'
type LinkListItemProps = {
isComing?: boolean
isWip?: boolean
isNew?: boolean
slug: string
title: string
}
export default function Navigation({
gettingStartedList,
componentList,
}: {
gettingStartedList: LinkListItemProps[]
componentList: LinkListItemProps[]
}) {
const pathname = usePathname()
const [isModalOpen, setIsModalOpen] = useState(false)
useEffect(() => {
setIsModalOpen(false)
}, [pathname])
return (
Draft UI
{/* Desktop Nav */}
Getting Started
Components
{/* Mobile Nav */}
setIsModalOpen(true)}
>
Menu
setIsModalOpen(false)}
autoFocus
>
)
}
================================================
FILE: apps/docs/components/page-toc.tsx
================================================
'use client'
import { useEffect, useState } from 'react'
import { cx } from '@/lib/cva.config'
import { type TocItemProps } from '@/types'
export default function PageToc({
headings,
}: {
headings?: Array
}) {
const activeHeading = useActiveHeading((headings || []).map((h) => h.slug))
return (
On This Page
)
}
function useActiveHeading(itemIds: string[]) {
const [activeId, setActiveId] = useState(null)
useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
setActiveId(entry.target.id)
}
})
},
{ rootMargin: `0% 0% -80% 0%` },
)
itemIds?.forEach((id) => {
const element = document.getElementById(id)
if (element) {
observer.observe(element)
}
})
return () => {
itemIds?.forEach((id) => {
const element = document.getElementById(id)
if (element) {
observer.unobserve(element)
}
})
}
}, [itemIds])
return activeId
}
================================================
FILE: apps/docs/components/search-component.tsx
================================================
'use client'
import { useState } from 'react'
import { Search } from 'lucide-react'
import Link from 'next/link'
import { DocSearchModal } from '@docsearch/react'
import '@docsearch/css/dist/style.css'
import { Button, IconButton } from 'ui'
import { cx } from '@/lib/cva.config'
export default function SearchComponent() {
const [isOpen, setIsOpen] = useState(false)
return (
<>
setIsOpen(true)}
className="hidden items-center justify-start text-slate-600 md:flex md:w-72"
>
Search the docs...
setIsOpen(true)}
aria-label="Search"
className="md:hidden"
size="sm"
variant="ghost"
>
{isOpen ? (
setIsOpen(false)}
placeholder="Search the docs..."
hitComponent={Hit}
transformItems={(items) => {
return items.map((item, index) => {
const a = document.createElement('a')
a.href = item.url
if (item.hierarchy?.lvl0) {
item.hierarchy.lvl0 = item.hierarchy.lvl0.replace(/&/g, '&')
}
if (item._highlightResult?.hierarchy?.lvl0?.value) {
item._highlightResult.hierarchy.lvl0.value =
item._highlightResult.hierarchy.lvl0.value.replace(
/&/g,
'&',
)
}
return {
...item,
url: `${a.pathname}${a.hash}`,
__is_result: () => true,
// __is_parent: () =>
// item.type === 'lvl1' && items.length > 1 && index === 0,
// __is_child: () =>
// item.type !== 'lvl1' &&
// items.length > 1 &&
// items[0].type === 'lvl1' &&
// index !== 0,
__is_first: () => index === 1,
__is_last: () => index === items.length - 1 && index !== 0,
}
})
}}
/>
) : null}
>
)
}
const Hit = ({ hit, children }) => {
return (
{children}
)
}
================================================
FILE: apps/docs/components/theme-provider.tsx
================================================
'use client'
import * as React from 'react'
import { ThemeProvider as NextThemesProvider } from 'next-themes'
import { type ThemeProviderProps } from 'next-themes/dist/types'
export default function ThemeProvider({
children,
...props
}: ThemeProviderProps) {
return {children}
}
================================================
FILE: apps/docs/content/CHANGELOG.mdx
================================================
---
title: Changelog
description: Stay up to date on the latest changes and updates
order: 5
---
## January 2024
- We have a **CLI** tool!
- Thanks to [@jolbol1](https://github.com/jolbol1) and [@jacobparis](https://github.com/jacobparis) for getting this set up. And to [@kentcdodds](https://github.com/kentcdodds) for kicking this off and suggesting this approach.
- Docs and installation have been updated to show how to install components with [`@sly-cli/sly`](https://github.com/jacobparis-insiders/sly/blob/main/cli/README.md)
- Update **React Aria Components** to `1.0.0`
- Breadcrumb now exposes `BreadcrumbLink` to correctly handle React Aria **`Link`** within components [@Littletonconnor](https://github.com/Littletonconnor) - [#20](https://github.com/IHIutch/draft-ui/pull/20)
- Updates to installation to show how to integrate React Aria **`Link`** with various frameworks using [`RouterProvider`](https://react-spectrum.adobe.com/react-aria/routing.html).
- No more `'use client'` needed. React Aria handles this automatically, behind the scenes.
- Docs and code samples were tidied up significantly
- You can now copy directly from `bash` snippets
- Thanks to [@itsMapleLeaf's](https://github.com/itsMapleLeaf) [suggestion](https://twitter.com/heyImMapleLeaf/status/1723216226833346848), we're now using namespaced imports from React Aria Components. This has significantly increased readability and helps keep things simple.
- Update Tailwind to `3.4.1`
- While there were several exciting updates in this release, we've not yet integrated things like `:has()` and `size-*`. Since this change is still fairly fresh, we're going hold off on introducing those features because, well, not changing them won't break anything.
- Several spelling and text fixes:
- [@henrikvtcodes](https://github.com/henrikvtcodes) - [#3](https://github.com/IHIutch/draft-ui/pull/3),
- [@Littletonconnor](https://github.com/Littletonconnor) - [#6](https://github.com/IHIutch/draft-ui/pull/6),
- [@Littletonconnor](https://github.com/Littletonconnor) - [#8](https://github.com/IHIutch/draft-ui/pull/18)
## November 2023
- Update **React Aria Components** to RC (`1.0.0-rc.0`)!
- Breadcrumb **`Item`** becomes **`Breadcrumb`**
- ListBox **`Item`** becomes **`ListBoxItem`**
- Menu **`Item`** becomes **`MenuItem`**
- GridList **`Item`** becomes **`GridListItem`**
- Replace **`validationState`** with **`isInvalid`** for error handling
- Add React Aria Components Tailwind config and update various classNames
- Update Installation instructions with specific React Aria Components version
- Update `Slider` example to destructure `state` key
- Replaces `class-variance-authority` with [`cva@beta`](https://beta.cva.style/getting-started/whats-new/)
- Base component styles must now be located within the `base` property:
- Replace `cn(...)` helper function with `cx(...)` by using `CVA` config and including `twMerge`
- Removes `clsx` as a dependency because it is now included in CVA as `cx`
- Updated red/error colors to be WCAG compliant
- Added additional examples to the following components:
- `Checkbox`
- `CheckboxGroup`
- `ComboBox`
- `DatePicker`
- `DateRangePicker`
- `Input`
- `NumberField`
- `Radio`
- `RadioGroup`
- `Select`
- `Tabs`
- `TextField`
## September 2023
- Reordered components' classNames properly
## August 2023
- Doc updates
- Update React imports in components
- Add lots of new examples and variants:
- `Button` variants
- `Checkbox` variants
- `ComboBox` examples
- `DateInput` variants
- `Input` variants
- `Menu` examples
- `Meter` examples
- `NumberField` examples
- `Radio` variants
- `Switch` examples
- `Tooltip` examples
- Fixed a bunch of component layouts
- Add `flex-col` to `Modal`
- Add `w-full` to `NumberField`
- Remove verbose classes from `RadioGroup`
- Add `w-full` to `Select`
- Remove `h-full` from vertical `Slider` variant
## July 2023
Using `react-aria-components@1.0.0-alpha.5`
- We have a logo and a homepage!
- Docs updated; content pages are live including installation
instructions
- Components and the rest of the site support dark mode.
- Tabs now use `--border-width` variable to avoid issues
with nested tabs
================================================
FILE: apps/docs/content/components/breadcrumbs.mdx
================================================
---
title: Breadcrumbs
description: Breadcrumbs display a hierarchy of links to the current page or resource in an application.
isComponent: true
---
By default, **`BreadcrumbLink`** uses a React Aria Component **`Link`**. To ensure this navigation works as expected with your framework, be sure to follow the Configure Routing step of the Draft UI installation.
## Installation
CLI
Copy & Paste
```bash title="bash"
npx @sly-cli/sly add draft-ui breadcrumbs
```
## Examples
### Default
This is an example of the default Breadcrumb component
================================================
FILE: apps/docs/content/components/button.mdx
================================================
---
title: Button
description: A button allows a user to perform an action, with mouse, touch, and keyboard interactions.
isComponent: true
---
## Installation
CLI
Copy & Paste
```bash title="bash"
npx @sly-cli/sly add draft-ui button
```
## Examples
### Default
The default **`Button`** uses variant `solid` and size `md`.
### Themes
You can use the `variant` prop to alter the **`Button`** theme. Variants `solid`, `outline`, `subtle`, `ghost`, `destructive`, and `link` are included by default.
### Sizes
You can use the `size` prop to alter the **`Button`** size. Sizes `xs`, `sm`, `md`, and `lg` are included by default.
### Disabled
Use the `isDisabled` prop to make the **`Button`** disabled.
================================================
FILE: apps/docs/content/components/checkbox-group.mdx
================================================
---
title: Checkbox Group
description: A checkbox group allows a user to select multiple items from a list of options.
isComponent: true
---
## Installation
CLI
Copy & Paste
```bash title="bash"
npx @sly-cli/sly add draft-ui checkbox-group
```
## Examples
### Default
The default **`CheckboxGroup`** uses orientation `vertical`.
### Horizontal
Set the `orientation` prop to `horizontal` to use a horizontal **`CheckboxGroup`**.
### Disabled
Use the `isDisabled` prop to make the entire **`CheckboxGroup`** disabled.
================================================
FILE: apps/docs/content/components/checkbox.mdx
================================================
---
title: Checkbox
description: A checkbox allows a user to select multiple items from a list of individual items, or to mark one individual item as selected.
isComponent: true
---
## Installation
CLI
Copy & Paste
```bash title="bash"
npx @sly-cli/sly add draft-ui checkbox
```
## Examples
### Default
The default **`Checkbox`** uses size `md`.
### Sizes
You can use the `size` prop to alter the **`Checkbox`** size. Sizes `sm`, `md`, and `lg` are included by default.
### Disabled
Use the `isDisabled` prop to make an individual **`Checkbox`** disabled.
================================================
FILE: apps/docs/content/components/combo-box.mdx
================================================
---
title: Combo Box
description: A combo box combines a text input with a listbox, allowing users to filter a list of options to items matching a query.
isComponent: true
---
## Installation
CLI
Copy & Paste
```bash title="bash"
npx @sly-cli/sly add draft-ui combobox
```
## Examples
### Default
This is the default **`ComboBox`**
### With Button
Any React Aria Component **`Button`** placed inside **`ComboBox`**, will automatically toggle the appearance of **`ComboBoxContent`** when pressed.
### Disabled Keys
To disable specific **`ComboBoxItem`**'s, add an array of `disabledKey`'s to **`ComboBox`**. Ensure the **`ComboBoxItem`**'s have appropriate, corresponding `id`'s.
### Disabled
Use the `isDisabled` prop to disable the **`ComboxInput`** altogether.
================================================
FILE: apps/docs/content/components/date-picker.mdx
================================================
---
title: Date Picker
description: A date picker combines a DateField and a Calendar popover to allow users to enter or select a date and time value.
isComponent: true
---
## Installation
CLI
Copy & Paste
```bash title="bash"
npx @sly-cli/sly add draft-ui date-picker
```
## Examples
### Default
This is the default **`DatePicker`** example.
### Disabled
Use the `isDisabled` prop to disable the entire **`DatePicker`** including the **`DateInput`**.
================================================
FILE: apps/docs/content/components/date-range-picker.mdx
================================================
---
title: Date Range Picker
description: A date range picker combines two DateFields and a RangeCalendar popover to allow users to enter or select a date and time range.
isComponent: true
---
## Installation
CLI
Copy & Paste
```bash title="bash"
npx @sly-cli/sly add draft-ui date-range-picker
```
## Examples
### Default
This is the default **`DateRangePicker`**
### Disabled
Use the `isDisabled` prop to disable the entire **`DateRangePicker`** including the **`DateInput`**'s.
================================================
FILE: apps/docs/content/components/grid-list.mdx
================================================
---
title: Grid List
description: A grid list displays a list of interactive items, with support for keyboard navigation, single or multiple selection, and row actions.
isComponent: true
isWip: true
isComing: true
---
## Installation
CLI
Copy & Paste
```bash title="bash"
npx @sly-cli/sly add draft-ui grid-list
```
## Examples
### Default
This is the default example
---
### Disabled
This is the disabled example
---
### With Label
This is an example with a label
---
### With Text
This is an example with text
---
### With Input
This is an example with a input
================================================
FILE: apps/docs/content/components/icon-button.mdx
================================================
---
title: Icon Button
description: Displays a form input field or a component that looks like an input field.
isComponent: true
---
## Installation
CLI
Copy & Paste
```bash title="bash"
npx @sly-cli/sly add draft-ui icon-button
```
## Examples
### Default
The default **`IconButton`** uses variant `solid` and size `md`.
When using an **`IconButton`** you should almost always use an `aria-label`.
In some components, however, React Aria Components will automatically assign
an `aria-label` to buttons.
### Themes
You can use the `variant` prop to alter the button theme. Variants `solid`, `outline`, `subtle`, `ghost`, `destructive`, and `link` are included by default.
### Sizes
You can use the `size` prop to alter the button size. Sizes `xs`, `sm`, `md`, and `lg` are included by default.
### Disabled
Use the `isDisabled` prop to make the **`IconButton`** disabled.
================================================
FILE: apps/docs/content/components/input.mdx
================================================
---
title: Input
description: Displays a form input field or a component that looks like an input field.
isComponent: true
---
**`Input`** is a primitive component, it is not meant to be used on it's own.
Typically, it is used in any component that handles raw text, such as
**`TextField`**, **`NumberField`**, **`SearchField`**, and **`ComboxBox`**.
## Installation
CLI
Copy & Paste
```bash title="bash"
npx @sly-cli/sly add draft-ui input
```
## Examples
### Default
The default **`Input`** uses size `md`.
### Size
You can use the `size` prop to alter the input size. Sizes `xs`, `sm`, `md`, and `lg` are included by default.
### Disabled
Use the `disabled` prop to make the **`Input`** disabled.
Currently, the **`Input`** component uses the `disabled` prop rather than `isDisabled`.
================================================
FILE: apps/docs/content/components/label.mdx
================================================
---
title: Label
description: Displays a form input field or a component that looks like an input field.
isComponent: true
---
**`Label`** is a primitive component, it is not meant to be used on it's own.
Many components that behave as form inputs automatically handle linking labels
to their respective inputs, such as **`DatePicker`**, **`RadioGroup`**,
**`Select`**, **`TextField`**, **`Switch`**, etc.
## Installation
CLI
Copy & Paste
```bash title="bash"
npx @sly-cli/sly add draft-ui label
```
## Examples
### Default
This is the default **`Label`**
================================================
FILE: apps/docs/content/components/menu.mdx
================================================
---
title: Menu
description: A menu displays a list of actions or options that a user can choose.
isComponent: true
---
## Installation
CLI
Copy & Paste
```bash title="bash"
npx @sly-cli/sly add draft-ui menu
```
## Examples
### Default
This is the default **`Menu`** example.
### Disabled
Make specific **`MenuItem`**'s disabled by adding their `id` to the **`MenuContent`**'s `disabledKeys` prop.
### With Sections
Add sections to the menu by using **`MenuSection`** and **`MenuHeader`**.
### As Checkbox List
**`MenuItem`** can act checkboxes by setting the **`MenuContent`**'s `selectionMode` prop to `multiple`.
### As Radio List
**`MenuItem`** can act radios by setting the **`MenuContent`**'s `selectionMode` prop to `single`.
### Complex Menu Items
**Coming Soon**: An example showing how to properly create **`MenuItem`**s with complex content.
================================================
FILE: apps/docs/content/components/meter.mdx
================================================
---
title: Meter
description: A meter represents a quantity within a known range, or a fractional value.
isComponent: true
---
## Installation
CLI
Copy & Paste
```bash title="bash"
npx @sly-cli/sly add draft-ui meter
```
## Examples
Althought similar to **`ProgressBar`**, **`Meter`** is used to represent an
amount or quantity. For example: Storage space, download speed, or battery
life.
### Default
This is the default **`Meter`**
### With Custom Children
{/* TODO */}
**Coming soon:** An example of **`Meter`** with custom children
================================================
FILE: apps/docs/content/components/modal.mdx
================================================
---
title: Modal
description: A modal is an overlay element which blocks interaction with elements outside it.
isComponent: true
---
## Installation
CLI
Copy & Paste
```bash title="bash"
npx @sly-cli/sly add draft-ui modal
```
## Examples
### Default
The default **`Modal`** uses size `md`.
### Sizes
You can use the `size` prop to alter the button size. Sizes `xs`, `sm`, `md`, `lg`, `xl`, and `full` are included by default.
### Set Autofocus
You can manually control which element recieves focus when the **`Modal`** opens by setting `autoFocus` on a focusable element.
### Dismissable
Modals are dismissable by clicking outside by default or by pressing the Esc key. To prevent this, set `isDismissable` to `false` and/or `isKeyboardDismissDisabled` to `true`.
================================================
FILE: apps/docs/content/components/number-field.mdx
================================================
---
title: Number Field
description: A number field allows a user to enter a number, and increment or decrement the value using stepper buttons.
isComponent: true
---
## Installation
CLI
Copy & Paste
```bash title="bash"
npx @sly-cli/sly add draft-ui number-field
```
## Examples
### Default
This is the default **`NumberField`**
### With Stepper
This is an example using the built-in **`NumberInputStepper`**.
### With Mobile Stepper
This is an example of a custom **`NumberField`** that makes the stepper buttons easier to press on touch devices.
### Disabled
Use the `isDisabled` prop to make the **`NumberField`** disabled.
================================================
FILE: apps/docs/content/components/progress-bar.mdx
================================================
---
title: Progress Bar
description: Progress bars show either determinate or indeterminate progress of an operation over time.
isComponent: true
---
## Installation
CLI
Copy & Paste
```bash title="bash"
npx @sly-cli/sly add draft-ui progress-bar
```
## Examples
Althought similar to **`Meter`**, **`ProgressBar`** is used to represent
progress. In other words, it would typically be used in circumstances that
start at zero and tend toward completion. For example: Loading progress,
download progress, or battery charge progress.
### Default
This is the default **`ProgressBar`**
### With Custom Children
{/* TODO */}
**Coming soon:** This is an example with custom children
================================================
FILE: apps/docs/content/components/radio-group.mdx
================================================
---
title: Radio Group
description: A radio group allows a user to select a single item from a list of mutually exclusive options.
isComponent: true
---
## Installation
CLI
Copy & Paste
```bash title="bash"
npx @sly-cli/sly add draft-ui radio-group
```
## Examples
### Default
The default **`RadioGroup`** uses orientation `vertical`.
### Horizontal
Set the `orientation` prop to `horizontal` to use a horizontal **`RadioGroup`**.
### Disabled
Use the `isDisabled` prop to make the entire **`RadioGroup`** disabled.
================================================
FILE: apps/docs/content/components/radio.mdx
================================================
---
title: Radio
description: Displays a form input field or a component that looks like an input field.
isComponent: true
---
## Installation
CLI
Copy & Paste
```bash title="bash"
npx @sly-cli/sly add draft-ui radio
```
## Examples
**`Radio`** is a primitive component, it is not meant to be used on it's own. It must be wrapped in a **`RadioGroup`**.
### Default
The default **`Radio`** uses size `md`.
### Sizes
You can use the `size` prop to alter the **`Radio`** size. Sizes `sm`, `md`, and `lg` are included by default.
### Disabled
Use the `isDisabled` prop to make an individual **`Radio`** disabled.
================================================
FILE: apps/docs/content/components/search-field.mdx
================================================
---
title: Search Field
description: A search field allows a user to enter and clear a search query.
isComponent: true
---
## Installation
CLI
Copy & Paste
```bash title="bash"
npx @sly-cli/sly add draft-ui search-field
```
## Examples
### Default
This is the default **`SearchField`**
### With Clear Button
Use **`SearchFieldClearButton`** to display a button that clear the **`SearchField`** input. By default, it will only show up when the input is not empty.
================================================
FILE: apps/docs/content/components/select.mdx
================================================
---
title: Select
description: A select displays a collapsible list of options and allows a user to select one of them.
isComponent: true
---
## Installation
CLI
Copy & Paste
```bash title="bash"
npx @sly-cli/sly add draft-ui select
```
## Examples
### Default
This is the default **`Select`**.
### Disabled
Use the `isDisabled` prop to make the **`Select`** disabled.
================================================
FILE: apps/docs/content/components/slider.mdx
================================================
---
title: Slider
description: A slider allows a user to select one or more values within a range.
isComponent: true
---
## Installation
CLI
Copy & Paste
```bash title="bash"
npx @sly-cli/sly add draft-ui slider
```
## Examples
### Default
The default **`Slider`** uses orientation `horizontal`.
### Vertical
Set the `orientation` prop to `vertical` to use a vertical **`Slider`**.
`orientation` value passed to **`SliderFilledTrack`** must be set to `vertical` and you must give **`SliderTrack`** a specific height for this variation to be styled correctly.
================================================
FILE: apps/docs/content/components/switch.mdx
================================================
---
title: Switch
description: A text field allows a user to enter a plain text value with a keyboard.
isComponent: true
---
## Installation
CLI
Copy & Paste
```bash title="bash"
npx @sly-cli/sly add draft-ui switch
```
## Examples
### Default
The default **`Switch`** uses size `md`.
### Alignment
Switches can be either left or right aligned.
### Sizes
You can use the `size` prop to alter the **`Switch`** size. Sizes `sm`, `md`, and `lg` are included by default.
### Disabled
Use the `isDisabled` prop to make the **`Switch`** disabled.
================================================
FILE: apps/docs/content/components/table.mdx
================================================
---
title: Table
description: A table displays data in rows and columns and enables a user to navigate its contents via directional navigation keys, and optionally supports row selection and sorting.
isComponent: true
isWip: true
isComing: true
---
## Installation
CLI
Copy & Paste
```bash title="bash"
npx @sly-cli/sly add draft-ui table
```
## Examples
### Default
This is the default example
---
### Disabled
This is the disabled example
---
### With Label
This is an example with a label
---
### With Text
This is an example with text
---
### With Input
This is an example with a input
================================================
FILE: apps/docs/content/components/tabs.mdx
================================================
---
title: Tabs
description: Tabs organize content into multiple sections and allow users to navigate between them.
isComponent: true
---
## Installation
CLI
Copy & Paste
```bash title="bash"
npx @sly-cli/sly add draft-ui tabs
```
## Examples
### Default
The default **`Tabs`** uses orientation `horizontal`.
### Vertical
Set the `orientation` prop to `vertical` to use vertical **`Tabs`**.
### Disabled Keys
Use `disabledKeys` prop to disable specific **`Tab`**'s **`ComboBox`**.
### Disabled
Use the `isDisabled` prop to disable all **`Tab`**'s.
================================================
FILE: apps/docs/content/components/text-field.mdx
================================================
---
title: Text Field
description: Displays a form input field or a component that looks like an input field.
isComponent: true
---
## Installation
CLI
Copy & Paste
```bash title="bash"
npx @sly-cli/sly add draft-ui text-field
```
## Examples
### Default
This is the default **`TextField`**.
### With Error
Use the `isInvalid` prop to make an **`Input`** invalid. You can also include an error message to tell users why the `input` is invalid.
### Disabled
Use the `isDisabled` prop to make the **`Input`** disabled.
================================================
FILE: apps/docs/content/components/toggle-button.mdx
================================================
---
title: Toggle Button
description: A toggle button allows a user to toggle a selection on or off, for example switching between two states or modes.
isComponent: true
isWip: true
isComing: true
---
## Installation
CLI
Copy & Paste
```bash title="bash"
npx @sly-cli/sly add draft-ui toggle-button
```
## Examples
### Default
**Coming soon:** This is the default example
{/* TODO */}
---
### Disabled
**Coming soon:** This is the disabled example
{/* TODO */}
================================================
FILE: apps/docs/content/components/tooltip.mdx
================================================
---
title: Tooltip
description: A tooltip displays a description of an element on hover or focus.
isComponent: true
---
## Installation
CLI
Copy & Paste
```bash title="bash"
npx @sly-cli/sly add draft-ui tooltip
```
## Examples
### Default
The default **`Tooltip`** uses placement `top`.
### Placement
Set the `placement` prop to change the default **`Tooltip`** placement.
================================================
FILE: apps/docs/content/getting-started/about.mdx
================================================
---
title: About
description: The purpose of Draft UI is to make building accessible applications
faster and easier than ever
order: 4
---
## Make It Your Own
I've intentionally avoided using the Tailwind config and
arbitrary values as much as possible. While you are welcome to copy
and paste these components and use them in your projects as-is, the
goal of this project is to get your projects up and running as quickly
as possible without having to worry about accessibility and
interactivity.
## Special Thanks
There are many OSS projects who have inspired and power the backbone
of this project. This project is a combination of other folks really
hard work so I'd like to recognize and thank them:
- [shadcn/ui](https://ui.shadcn.com): Without the inspiration of shadcn/ui, this project would not
exist. The idea to create a beautiful, copy and paste library
opened my eyes on how to make a useful tool without having to
worry about bundling, distribution, and a bunch of other things I
don't know much about.
- [React Aria Components](https://react-spectrum.adobe.com/react-aria/react-aria-components.html): The backbone of this project. I consider React Aria to be the de
facto accessibility library on the web. Creating React Aria
Components has made it easier than ever to build UI that is
functional and accessible to all users.
I'd especially like to acknowledge [@devongovett](https://twitter.com/devongovett)
who has been representing React Aria on Twitter and has been very
communicative about the team's progress and quick to respond
to feedback.
- [Chakra UI](): Although not directly involved with this project, I leveraged
many of their styling decisions where I wanted additional
flexibility with components. I also heavily relied on their naming
to conventions to compose components.
I've also been heavily combing through their repo to better
understand how their Storybook and Docs are working together.
================================================
FILE: apps/docs/content/getting-started/cli.mdx
================================================
---
title: CLI
description: Get started using Draft UI faster than ever by installing components right from the command line
isNew: true
order: 3
---
## Install Sly CLI
You're able to install Draft UI components from the command line thanks to an awesome project by [Jacob Paris](https://twitter.com/jacobmparis), [`@sly-cli/sly`](https://sly-cli.fly.dev/).
Run the following code to add Draft UI to your project. **`sly`** will show you a list of components available to download.
```bash title="bash"
npx @sly-cli/sly add draft-ui
```
## Configuration
**`@sly-cli/sly`** let's you configure some specific settings about how you want to install components from the CLI. Assuming you've followed the initial Draft UI installation instructions, this is what your `sly.json` should look like.
```json title="sly.json"
{
"$schema": "https://sly-cli.fly.dev/registry/config.json",
"libraries": [
{
"name": "draft-ui",
"directory": "./components",
"transformers": ["transform-sly-install.ts"], // optional, otherwise leave empty
"postinstall": [],
}
]
}
```
Of course, if you've made your own customizations, you should update your `sly.json` to match those changes.
## Transformers
**`sly`** also allows you to manipulate how you install your components via transformers.
In order to use transformers, you'll need to install **`sly`** into your project:
```bash title="bash"
npm install -D @sly-cli/sly
```
For example, if you're using a different path alias for your directories, overwrite the default values automatically.
```ts title="transform-sly-install.ts"
const cvaConfig = '@/lib/cva.config'
/**
* @type {import('@sly-cli/sly/dist').Transformer}
*/
export default async function transformSlyInstall(input: string) {
input = input.replace('@/lib/cva.config', cvaConfig)
return input
}
```
For additional information on tranformers, go to the [`@sly-cli/sly` repo](https://github.com/jacobparis-insiders/sly/blob/main/cli/README.md#transformers).
================================================
FILE: apps/docs/content/getting-started/installation.mdx
================================================
---
title: Installation
description: Here's everything you need to get started
order: 2
---
## Install TailwindCSS
Depending on your project, steps to install Tailwind may vary.
Check out [their installation page](https://tailwindcss.com/docs/installation/framework-guides) for guidance.
## Install Required Dependencies
```bash title="bash"
npm install react-aria-components@1.0.0 cva@beta tailwind-merge tailwindcss-animate tailwindcss-react-aria-components@1.0.0
```
This project requires the following packages:
- [react-aria-components@1.0.0](https://github.com/adobe/react-spectrum/tree/main/packages/react-aria-components)
- [cva@beta](https://github.com/joe-bell/cva)
- [tailwind-merge](https://github.com/dcastil/tailwind-merge)
- [tailwindcss-animate](https://github.com/jamiebuilds/tailwindcss-animate)
- [tailwindcss-react-aria-components@1.0.0](https://github.com/adobe/react-spectrum/tree/main/packages/tailwindcss-react-aria-components)
## Install Icons
Draft UI uses [Lucide Icons](https://lucide.dev/guide/packages/lucide-react) by default. You can use whichever library you prefer, but
you'll need to replace the default icons manually.
```bash title="bash"
npm install lucide-react
```
## Configure Path Aliases
Draft UI uses the path alias @/\* by default. If you
want to use a different alias, you'll need to update your
components.
```json {3-6} title="tsconfig.json"
// ...
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./*"]
}
}
}
// ...
```
## Configure Tailwind
Make sure to include the `tailwindcss-animate` plugin.
```js title="tailwind.config.js"
/** @type {import('tailwindcss').Config} */
module.exports = {
darkMode: ['class'],
content: ['app/**/*.{ts,tsx}', 'components/**/*.{ts,tsx}'],
plugins: [
require('tailwindcss-animate'),
require('tailwindcss-react-aria-components'),
],
}
```
## Configure CVA
Add **`twMerge`** to your `cva.config.ts`. This way, anytime you use the `cx(...)` function any conflicting Tailwind classes will be gracefully resolved.
```ts title="cva.config.ts"
import { defineConfig } from 'cva'
import { twMerge } from 'tailwind-merge'
export const { cva, cx, compose } = defineConfig({
hooks: {
onComplete: (className) => twMerge(className),
},
})
```
## Configure Routing (recommended)
Many components use built-in **`Link`** components to handle navigation. By default, these links perform native browser navigation when they are interacted with.
In order for these links to work as expected with whichever framework you're using, wrap your application using React Aria's `RouterProvider` component.
Next.js App
Next.js Pages
Remix
React Router
```tsx title="app/provider.tsx"
'use client'
import { useRouter } from 'next/navigation'
import { RouterProvider } from 'react-aria-components'
export function ClientProviders({ children }) {
const router = useRouter()
return (
{children}
)
}
```
```tsx title="app/layout.tsx"
import { ClientProviders } from './provider';
export default function RootLayout({ children }) {
return (
{children}
);
}
```
```tsx title="pages/_app.tsx"
import type { AppProps } from 'next/app'
import { useRouter } from 'next/router'
import { RouterProvider } from 'react-aria-components'
export default function MyApp({ Component, pageProps }: AppProps) {
let router = useRouter()
return (
)
}
```
```tsx title="app/root.tsx"
import { useNavigate, Outlet } from '@remix-run/react'
import { RouterProvider } from 'react-aria-components'
export default function App() {
let navigate = useNavigate()
return (
{/* ... */}
{/* ... */}
)
}
```
```tsx
import { BrowserRouter, useNavigate } from 'react-router-dom'
import { RouterProvider } from 'react-aria-components'
function App() {
let navigate = useNavigate()
return (
{/* Your app here... */}
} />
{/* ... */}
)
}
```
For more information, go to the React Aria [Client Side Routing](https://react-spectrum.adobe.com/react-aria/routing.html).
## Configure @sly-cli/sly (optional)
If your installation varies from any of the configs described above (like if your components are in a different location) you'll want to update your `sly.json` to make sure your CLI installs work correctly.
Go to the CLI page to learn more.
## All Done!
Now you're ready to start building your application!
================================================
FILE: apps/docs/content/getting-started/introduction.mdx
================================================
---
title: Introduction
description: Copy and paste-able components built with React Aria Components and
Tailwind CSS
order: 1
---
Draft UI has been massively inspired by **`shadcn/ui`** so I encourage you to take a look at [their introduction page](https://ui.shadcn.com/docs).
---
Long story short, Draft UI follows all the same ideals:
- Copy and paste components give you full control over the component
code, it is yours to use.
- Modify and adapt to your hearts content, using these docs as a
starting point.
- These components should be able to be used in any framework that
supports React.
- Draft UI is free to use on any personal or commercial projects.
And if you do, [let me know!](https://twitter.com/draft__ui)
================================================
FILE: apps/docs/contentlayer.config.ts
================================================
import { defineDocumentType, makeSource } from 'contentlayer/source-files'
// @ts-ignore
import toc from 'markdown-toc'
import rehypePrettyCode from 'rehype-pretty-code'
import remarkGfm from 'remark-gfm'
import { visit } from 'unist-util-visit'
import { rehypeComponent } from './lib/rehype-component'
import { withTableOfContents } from './lib/remark/with-table-of-contents'
import { type TocItemProps } from './types'
export const ComponentDocument = defineDocumentType(() => ({
name: 'ComponentDocument',
filePathPattern: 'components/**/*.mdx',
contentType: 'mdx',
fields: {
title: {
type: 'string',
required: true,
},
description: {
type: 'string',
},
isComponent: {
type: 'boolean',
},
isWip: {
type: 'boolean',
},
isComing: {
type: 'boolean',
},
},
computedFields: {
slug: {
type: 'string',
resolve: (post) => `/docs/${post._raw.flattenedPath}`,
},
toc: {
type: 'json',
resolve: (doc): TocItemProps => toc(doc.body.raw, { maxdepth: 3 }).json,
},
},
}))
export const GeneralDocument = defineDocumentType(() => ({
name: 'GeneralDocument',
filePathPattern: 'getting-started/**/*.mdx',
contentType: 'mdx',
fields: {
title: {
type: 'string',
required: true,
},
description: {
type: 'string',
required: true,
},
isNew: {
type: 'boolean',
},
order: {
type: 'number',
required: true,
},
},
computedFields: {
slug: {
type: 'string',
resolve: (post) => `/${post._raw.flattenedPath}`,
},
toc: {
type: 'json',
resolve: (doc): TocItemProps => toc(doc.body.raw, { maxdepth: 3 }).json,
},
},
}))
export const ChangelogDocument = defineDocumentType(() => ({
name: 'ChangelogDocument',
filePathPattern: 'CHANGELOG.mdx',
contentType: 'mdx',
fields: {
title: {
type: 'string',
required: true,
},
description: {
type: 'string',
required: true,
},
order: {
type: 'number',
required: true,
},
},
computedFields: {
slug: {
type: 'string',
resolve: () => '/changelog',
},
toc: {
type: 'json',
resolve: (doc): TocItemProps => toc(doc.body.raw, { maxdepth: 2 }).json,
},
},
}))
export default makeSource({
contentDirPath: 'content',
documentTypes: [ComponentDocument, ChangelogDocument, GeneralDocument],
mdx: {
rehypePlugins: [
rehypeComponent,
() => (tree) => {
visit(tree, (node) => {
if (node?.type === 'element' && node?.tagName === 'pre') {
const [codeEl] = node.children
if (codeEl.tagName !== 'code') {
return
}
if (codeEl.data?.meta) {
// Extract event from meta and pass it down the tree.
const regex = /event="([^"]*)"/
const match = codeEl.data?.meta.match(regex)
if (match) {
node.__event__ = match ? match[1] : null
codeEl.data.meta = codeEl.data.meta.replace(regex, '')
}
}
node.__rawString__ = codeEl.children?.[0].value
node.__src__ = node.properties?.__src__
node.__style__ = node.properties?.__style__
}
})
},
[
// @ts-expect-error related: https://github.com/atomiks/rehype-pretty-code/issues/145
rehypePrettyCode,
{
theme: 'dracula',
onVisitLine(node) {
// Prevent lines from collapsing in `display: grid` mode, and allow empty
// lines to be copy/pasted
if (node.children.length === 0) {
node.children = [{ type: 'text', value: ' ' }]
}
},
onVisitHighlightedLine(node) {
node.properties?.className?.push('line--highlighted')
},
onVisitHighlightedWord(node) {
node.properties.className = ['word--highlighted']
},
},
],
() => (tree) => {
visit(tree, (node) => {
if (node?.type === 'element' && node?.tagName === 'figure') {
if (!('data-rehype-pretty-code-figure' in node.properties)) {
return
}
// Pass code snippet to figure wrapper
if (node.__rawString__) {
node.properties['__rawString__'] = node.__rawString__
}
// npm install
if (node.__rawString__?.startsWith('npm install')) {
const npmCommand = node.__rawString__
node.properties['__npmCommand__'] = npmCommand
node.properties['__yarnCommand__'] = npmCommand.replace(
'npm install',
'yarn add',
)
node.properties['__pnpmCommand__'] = npmCommand.replace(
'npm install',
'pnpm add',
)
node.properties['__niCommand__'] = npmCommand.replace(
'npm install',
'ni',
)
}
// npx
if (node.__rawString__?.startsWith('npx')) {
const npmCommand = node.__rawString__
node.properties['__npmCommand__'] = npmCommand
node.properties['__yarnCommand__'] = npmCommand
node.properties['__pnpmCommand__'] = npmCommand.replace(
'npx',
'pnpm dlx',
)
node.properties['__niCommand__'] = npmCommand.replace(
'npx',
'nlx',
)
}
}
})
},
],
remarkPlugins: [remarkGfm, withTableOfContents],
},
})
================================================
FILE: apps/docs/lib/cva.config.ts
================================================
import { defineConfig } from 'cva'
import { twMerge } from 'tailwind-merge'
export const { cva, cx, compose } = defineConfig({
hooks: {
onComplete: (className) => twMerge(className),
},
})
================================================
FILE: apps/docs/lib/rehype-component.ts
================================================
import fs from 'fs'
import path from 'path'
import { u } from 'unist-builder'
import { visit } from 'unist-util-visit'
import { type UnistNode, type UnistTree } from '@/types'
import { Index } from '../__registry__'
export function rehypeComponent() {
return async (tree: UnistTree) => {
visit(tree, (node: UnistNode) => {
if (node.name === 'ComponentSource') {
const src = getNodeAttributeByName(node, 'src')?.value as string
if (!src) {
return null
}
try {
// Read the source file.
const filePath = path.join(process.cwd(), src)
let source = fs.readFileSync(filePath, 'utf8')
source = source.replaceAll('export default', 'export')
node.children?.push(
u('element', {
tagName: 'pre',
properties: {
__src__: src,
},
children: [
u('element', {
tagName: 'code',
properties: {
className: ['language-tsx'],
},
children: [
{
type: 'text',
value: source,
},
],
}),
],
}),
)
} catch (error) {
console.error(error)
}
}
if (node.name === 'ComponentExample') {
const name = getNodeAttributeByName(node, 'name')?.value as string
const story = getNodeAttributeByName(node, 'story')?.value as string
if (!name || !story) {
console.log('❌ name or story prop not passed on ComponentExample')
return null
}
try {
const component = Index[name][story]
const src = component.file
// Read the source file.
const filePath = path.join(process.cwd(), src)
let source = fs.readFileSync(filePath, 'utf8')
source = source.replaceAll('export default', 'export')
node.children?.push(
u('element', {
tagName: 'pre',
properties: {
__src__: src,
},
children: [
u('element', {
tagName: 'code',
properties: {
className: ['language-tsx'],
},
children: [
{
type: 'text',
value: source,
},
],
}),
],
}),
)
} catch (error) {
console.error(error)
}
}
})
}
}
function getNodeAttributeByName(node: UnistNode, name: string) {
return node.attributes?.find((attribute) => attribute.name === name)
}
================================================
FILE: apps/docs/lib/remark/with-table-of-contents.ts
================================================
import Slugger from 'github-slugger'
import { toString } from 'hast-util-to-string'
import { visit } from 'unist-util-visit'
export function withTableOfContents() {
const slugs = new Slugger()
return (tree) => {
visit(tree, 'heading', (node) => {
node.data = node.data || {}
node.data = { hName: 'Heading' }
node.data.hProperties = node.data.hProperties || {}
node.data.hProperties.lvl = node.depth
node.data.hProperties.slug = slugs.slug(toString(node))
})
}
}
================================================
FILE: apps/docs/next.config.js
================================================
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { withContentlayer } = require('next-contentlayer')
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
typedRoutes: true,
},
reactStrictMode: true,
transpilePackages: ['ui'],
redirects() {
return [
{
source: '/docs/components',
destination: '/docs/components/breadcrumbs',
permanent: true,
},
// Old Links
{
source: '/getting-started',
destination: '/getting-started/introduction',
permanent: true,
},
{
source: '/docs/introduction',
destination: '/getting-started/introduction',
permanent: true,
},
{
source: '/docs/installation',
destination: '/getting-started/installation',
permanent: true,
},
{
source: '/docs/about',
destination: '/getting-started/about',
permanent: true,
},
{
source: '/docs/changelog',
destination: '/changelog',
permanent: true,
},
]
},
}
module.exports = withContentlayer(nextConfig)
================================================
FILE: apps/docs/package.json
================================================
{
"name": "docs",
"version": "0.1.0",
"private": true,
"main": "src/index.ts",
"types": "src/index.ts",
"scripts": {
"dev": "rm -rf .next & rm -rf .contentlayer && contentlayer build && next dev",
"build": "contentlayer build && next build",
"start": "next start",
"lint": "eslint --ignore-path .gitignore '**/*.{js,jsx,cjs,mjs,ts,tsx,json}'",
"typecheck": "tsc --noEmit --pretty"
},
"dependencies": {
"@docsearch/react": "^3.4.0",
"@vercel/analytics": "^1.0.1",
"contentlayer": "0.3.4",
"fathom-client": "^3.5.0",
"fs-extra": "11.1.1",
"gray-matter": "^4.0.3",
"hast-util-to-string": "^3.0.0",
"lucide-react": "^0.259.0",
"markdown-toc": "^1.2.0",
"next": "14.0.4",
"next-contentlayer": "0.3.4",
"next-themes": "^0.2.1",
"react": "18.2.0",
"react-aria-components": "1.0.0",
"react-dom": "18.2.0",
"react-stately": "^3.21.0",
"rehype-pretty-code": "^0.12.3",
"remark-gfm": "^3.0.1",
"shikiji": "^0.9.0",
"tailwind-merge": "^1.13.2",
"ui": "workspace:*",
"unist-util-visit": "^5.0.0"
},
"devDependencies": {
"@tailwindcss/typography": "^0.5.9",
"@types/node": "18.16.19",
"@types/react": "18.0.27",
"@types/react-dom": "18.0.10",
"@types/unist": "^3.0.2",
"autoprefixer": "^10.4.14",
"eslint": "^8.56.0",
"eslint-config-custom": "workspace:*",
"eslint-plugin-mdx": "^2.3.2",
"postcss": "^8.4.23",
"postcss-config": "workspace:*",
"tailwind-config": "workspace:*",
"tailwindcss": "^3.4.1",
"ts-config": "workspace:*",
"ts-pattern": "^5.0.6",
"typescript": "^5.1.6",
"unist-builder": "^4.0.0"
}
}
================================================
FILE: apps/docs/postcss.config.js
================================================
/** @type {import('postcss').Config} */
module.exports = require('postcss-config/postcss.config.cjs')
================================================
FILE: apps/docs/public/site.webmanifest
================================================
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
================================================
FILE: apps/docs/registry/breadcrumbs/default.tsx
================================================
import { ChevronRight } from 'lucide-react'
import { BreadcrumbItem, BreadcrumbLink, Breadcrumbs } from 'ui'
export default function Default() {
return (
}>
Home
}>
React Aria
useBreadcrumbs
)
}
================================================
FILE: apps/docs/registry/button/default.tsx
================================================
import { Button } from 'ui'
export default function Default() {
return Press Me
}
================================================
FILE: apps/docs/registry/button/disabled.tsx
================================================
import { Button } from 'ui'
export default function Disabled() {
return (
Solid
Outline
Subtle
Ghost
Destructive
Link
)
}
================================================
FILE: apps/docs/registry/button/sizes.tsx
================================================
import { Button } from 'ui'
export default function Sizes() {
return (
X Small
Small
Medium
Large
)
}
================================================
FILE: apps/docs/registry/button/theme.tsx
================================================
import { Button } from 'ui'
export default function Theme() {
return (
Solid
Outline
Subtle
Ghost
Destructive
Link
)
}
================================================
FILE: apps/docs/registry/calendar/default.tsx
================================================
import {
Calendar,
CalendarCell,
CalendarGrid,
CalendarGridBody,
CalendarGridHeader,
CalendarHeader,
CalendarHeaderCell,
CalendarHeading,
CalendarNextButton,
CalendarPrevButton,
} from 'ui'
export default function Default() {
return (
{(day) => {day} }
{(date) => }
)
}
================================================
FILE: apps/docs/registry/checkbox/default.tsx
================================================
import { Checkbox } from 'ui'
export default function Default() {
return (
Cat
Dog
Reptile
)
}
================================================
FILE: apps/docs/registry/checkbox/disabled.tsx
================================================
import { Checkbox } from 'ui'
export default function Sizes() {
return (
Medium Cat
Medium Dog
Medium Reptile
)
}
================================================
FILE: apps/docs/registry/checkbox/sizes.tsx
================================================
import { Checkbox } from 'ui'
export default function Sizes() {
return (
Small Cat
Small Dog
Small Reptile
Medium Cat
Medium Dog
Medium Reptile
Large Cat
Large Dog
Large Reptile
)
}
================================================
FILE: apps/docs/registry/checkbox-group/default.tsx
================================================
import { Checkbox, CheckboxGroup, CheckboxGroupContent, Label } from 'ui'
export default function Default() {
return (
Favorite sports
Soccer
Baseball
Basketball
)
}
================================================
FILE: apps/docs/registry/checkbox-group/disabled.tsx
================================================
import { Checkbox, CheckboxGroup, CheckboxGroupContent, Label } from 'ui'
export default function Default() {
return (
Favorite sports
Soccer
Baseball
Basketball
)
}
================================================
FILE: apps/docs/registry/checkbox-group/horizontal.tsx
================================================
import { Checkbox, CheckboxGroup, CheckboxGroupContent, Label } from 'ui'
export default function Horizontal() {
return (
Favorite sports
Soccer
Baseball
Basketball
)
}
================================================
FILE: apps/docs/registry/combobox/default.tsx
================================================
import {
ComboBox,
ComboBoxContent,
ComboBoxInput,
ComboBoxItem,
Label,
} from 'ui'
export default function Default() {
return (
Favorite Animal
Aardvark
Cat
Dog
Kangaroo
Panda
Snake
)
}
================================================
FILE: apps/docs/registry/combobox/disabled-keys.tsx
================================================
import { ChevronDown } from 'lucide-react'
import {
ComboBox,
ComboBoxButton,
ComboBoxContent,
ComboBoxInput,
ComboBoxItem,
iconButtonVariants,
Label,
} from 'ui'
export default function DisabledKeys() {
return (
Favorite Animal
Aardvark
Cat
Dog
Kangaroo
Panda
Snake
)
}
================================================
FILE: apps/docs/registry/combobox/disabled.tsx
================================================
import { ChevronDown } from 'lucide-react'
import {
ComboBox,
ComboBoxButton,
ComboBoxContent,
ComboBoxInput,
ComboBoxItem,
iconButtonVariants,
Label,
} from 'ui'
export default function Disabled() {
return (
Favorite Animal
Aardvark
Cat
Dog
Kangaroo
Panda
Snake
)
}
================================================
FILE: apps/docs/registry/combobox/with-button.tsx
================================================
import { ChevronDown } from 'lucide-react'
import {
ComboBox,
ComboBoxButton,
ComboBoxContent,
ComboBoxInput,
ComboBoxItem,
iconButtonVariants,
Label,
} from 'ui'
export default function WithButton() {
return (
Favorite Animal
Aardvark
Cat
Dog
Kangaroo
Panda
Snake
)
}
================================================
FILE: apps/docs/registry/date-field/default.tsx
================================================
import { DateField, DateInput, DateInputGroup, DateSegment, Label } from 'ui'
export default function Default() {
return (
Date Label
{(segment) => }
)
}
================================================
FILE: apps/docs/registry/date-field/disabled.tsx
================================================
import { DateField, DateInput, DateInputGroup, DateSegment, Label } from 'ui'
export default function Disabled() {
return (
Date Label
{(segment) => }
)
}
================================================
FILE: apps/docs/registry/date-input/default.tsx
================================================
import { DateField, DateInput, DateInputGroup, DateSegment } from 'ui'
export default function Default() {
return (
{(segment) => }
)
}
================================================
FILE: apps/docs/registry/date-input/sizes.tsx
================================================
import { DateField, DateInput, DateInputGroup, DateSegment } from 'ui'
export default function Sizes() {
return (
{(segment) => }
{(segment) => }
{(segment) => }
{(segment) => }
)
}
================================================
FILE: apps/docs/registry/date-picker/default.tsx
================================================
import { ChevronDown, ChevronLeft, ChevronRight } from 'lucide-react'
import {
Calendar,
CalendarCell,
CalendarGrid,
CalendarGridBody,
CalendarGridHeader,
CalendarHeader,
CalendarHeaderCell,
CalendarHeading,
CalendarNextButton,
CalendarPrevButton,
DateInput,
DateInputGroup,
DatePicker,
DatePickerButton,
DatePickerContent,
DateSegment,
iconButtonVariants,
Label,
} from 'ui'
export default function Default() {
return (
Date
{(segment) => }
{(day) => {day} }
{(date) => }
)
}
================================================
FILE: apps/docs/registry/date-picker/disabled.tsx
================================================
import { ChevronDown, ChevronLeft, ChevronRight } from 'lucide-react'
import {
Calendar,
CalendarCell,
CalendarGrid,
CalendarGridBody,
CalendarGridHeader,
CalendarHeader,
CalendarHeaderCell,
CalendarHeading,
CalendarNextButton,
CalendarPrevButton,
DateInput,
DateInputGroup,
DatePicker,
DatePickerButton,
DatePickerContent,
DateSegment,
iconButtonVariants,
Label,
} from 'ui'
export default function Default() {
return (
Date
{(segment) => }
{(day) => {day} }
{(date) => }
)
}
================================================
FILE: apps/docs/registry/date-range-picker/default.tsx
================================================
import { ChevronDown } from 'lucide-react'
import {
CalendarCell,
CalendarGrid,
CalendarGridBody,
CalendarGridHeader,
CalendarHeader,
CalendarHeaderCell,
CalendarHeading,
CalendarNextButton,
CalendarPrevButton,
DateInput,
DateInputGroup,
DatePickerButton,
DatePickerContent,
DateRangePicker,
DateSegment,
iconButtonVariants,
Label,
RangeCalendar,
} from 'ui'
export default function Default() {
return (
Date Range
{(segment) => }
–
{(segment) => }
{(day) => {day} }
{(date) => }
)
}
================================================
FILE: apps/docs/registry/date-range-picker/disabled.tsx
================================================
import { ChevronDown } from 'lucide-react'
import {
CalendarCell,
CalendarGrid,
CalendarGridBody,
CalendarGridHeader,
CalendarHeader,
CalendarHeaderCell,
CalendarHeading,
CalendarNextButton,
CalendarPrevButton,
DateInput,
DateInputGroup,
DatePickerButton,
DatePickerContent,
DateRangePicker,
DateSegment,
iconButtonVariants,
Label,
RangeCalendar,
} from 'ui'
export default function Default() {
return (
Date Range
{(segment) => }
–
{(segment) => }
{(day) => {day} }
{(date) => }
)
}
================================================
FILE: apps/docs/registry/grid-list/default.tsx
================================================
import { Info } from 'lucide-react'
import { Checkbox, GridList, GridListItem, IconButton } from 'ui'
export default function Default() {
return (
Charizard
Blastoise
Venusaur
Pikachu
)
}
================================================
FILE: apps/docs/registry/icon-button/default.tsx
================================================
import { AlertCircle } from 'lucide-react'
import { IconButton } from 'ui'
export default function Default() {
return (
)
}
================================================
FILE: apps/docs/registry/icon-button/disabled.tsx
================================================
import { AlertCircle } from 'lucide-react'
import { IconButton } from 'ui'
export default function Disabled() {
return (
)
}
================================================
FILE: apps/docs/registry/icon-button/sizes.tsx
================================================
import { AlertCircle } from 'lucide-react'
import { IconButton } from 'ui'
export default function Sizes() {
return (
)
}
================================================
FILE: apps/docs/registry/icon-button/theme.tsx
================================================
import { AlertCircle } from 'lucide-react'
import { IconButton } from 'ui'
export default function Theme() {
return (
)
}
================================================
FILE: apps/docs/registry/input/default.tsx
================================================
import { Input } from 'ui'
export default function Default() {
return
}
================================================
FILE: apps/docs/registry/input/disabled.tsx
================================================
import { Input } from 'ui'
export default function Disabled() {
return
}
================================================
FILE: apps/docs/registry/input/sizes.tsx
================================================
import { Input } from 'ui'
export default function Sizes() {
return (
)
}
================================================
FILE: apps/docs/registry/label/default.tsx
================================================
import { Label } from 'ui'
export default function Default() {
return This is a label
}
================================================
FILE: apps/docs/registry/menu/as-checkbox.tsx
================================================
import * as React from 'react'
import { ChevronDown } from 'lucide-react'
import { type Selection } from 'react-aria-components'
import { Button, Menu, MenuContent, MenuItem } from 'ui'
export default function AsCheckbox() {
const [selected, setSelected] = React.useState(new Set(['bar1']))
return (
Menu
Foo
Bar
Baz
)
}
================================================
FILE: apps/docs/registry/menu/as-radio.tsx
================================================
import * as React from 'react'
import { ChevronDown } from 'lucide-react'
import { type Selection } from 'react-aria-components'
import { Button, Menu, MenuContent, MenuItem } from 'ui'
export default function AsRadio() {
const [selected, setSelected] = React.useState(new Set(['bar1']))
return (
Menu
Foo
Bar
Baz
)
}
================================================
FILE: apps/docs/registry/menu/default.tsx
================================================
import { ChevronDown } from 'lucide-react'
import { Button, Menu, MenuContent, MenuItem } from 'ui'
export default function Default() {
return (
Menu
Foo
Bar
Baz
)
}
================================================
FILE: apps/docs/registry/menu/disabled.tsx
================================================
import { ChevronDown } from 'lucide-react'
import { Button, Menu, MenuContent, MenuItem } from 'ui'
export default function Disabled() {
return (
Menu
Foo
Bar
Baz
)
}
================================================
FILE: apps/docs/registry/menu/with-sections.tsx
================================================
import { ChevronDown } from 'lucide-react'
import {
Button,
Menu,
MenuContent,
MenuHeader,
MenuItem,
MenuSection,
MenuSeparator,
} from 'ui'
export default function WithSections() {
return (
Menu
Styles
Foo
Bar
Baz
Align
Foo
Bar
Baz
)
}
================================================
FILE: apps/docs/registry/meter/default.tsx
================================================
import { Label, Meter, MeterFilledTrack, MeterTrack } from 'ui'
export default function Default() {
return (
{({ percentage }) => (
<>
Meter Label
>
)}
)
}
================================================
FILE: apps/docs/registry/modal/default.tsx
================================================
import * as React from 'react'
import { X } from 'lucide-react'
import {
Button,
IconButton,
ModalBody,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
} from 'ui'
export default function Default() {
const [isOpen, setIsOpen] = React.useState(false)
return (
<>
setIsOpen(true)}>Open Modal
Modal Title
setIsOpen(false)}
>
Sit nulla est ex deserunt exercitation anim occaecat. Nostrud
ullamco deserunt aute id consequat veniam incididunt duis in sint
irure nisi. Mollit officia cillum Lorem ullamco minim nostrud elit
officia tempor esse quis.
setIsOpen(false)}>
Close
setIsOpen(false)}
>
Confirm
>
)
}
================================================
FILE: apps/docs/registry/modal/dismissable-false.tsx
================================================
import * as React from 'react'
import { X } from 'lucide-react'
import {
Button,
IconButton,
ModalBody,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
} from 'ui'
export default function DismissableFalse() {
const [isOpen, setIsOpen] = React.useState(false)
return (
<>
setIsOpen(true)}>Open Modal
Modal Title
setIsOpen(false)}
>
Sit nulla est ex deserunt exercitation anim occaecat. Nostrud
ullamco deserunt aute id consequat veniam incididunt duis in sint
irure nisi. Mollit officia cillum Lorem ullamco minim nostrud elit
officia tempor esse quis.
setIsOpen(false)}>
Close
setIsOpen(false)}
>
Confirm
>
)
}
================================================
FILE: apps/docs/registry/modal/set-autofocus.tsx
================================================
import * as React from 'react'
import { X } from 'lucide-react'
import {
Button,
IconButton,
Input,
Label,
ModalBody,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
TextField,
} from 'ui'
export default function SetAutofocus() {
const [isOpen, setIsOpen] = React.useState(false)
return (
<>
setIsOpen(true)}>Open Modal
Modal Title
setIsOpen(false)}
>
First Name
Last Name
setIsOpen(false)}>
Close
setIsOpen(false)}
>
Confirm
>
)
}
================================================
FILE: apps/docs/registry/modal/sizes.tsx
================================================
import * as React from 'react'
import { X } from 'lucide-react'
import {
Button,
IconButton,
ModalBody,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
type ModalContentProps,
} from 'ui'
type Size = Pick['size']
export default function Default() {
const [isOpen, setIsOpen] = React.useState(false)
const [modalSize, setModalSize] = React.useState('md')
const sizes: Size[] = ['xs', 'sm', 'md', 'lg', 'xl', 'full']
const handlePress = (size: Size, open: boolean) => {
setModalSize(size)
setIsOpen(open)
}
return (
<>
{sizes.map((size, idx) => (
handlePress(size, true)} key={idx}>
Open {size}
))}
Modal Title
setIsOpen(false)}
>
Sit nulla est ex deserunt exercitation anim occaecat. Nostrud
ullamco deserunt aute id consequat veniam incididunt duis in sint
irure nisi. Mollit officia cillum Lorem ullamco minim nostrud elit
officia tempor esse quis.
setIsOpen(false)}>
Close
setIsOpen(false)}
>
Confirm
>
)
}
================================================
FILE: apps/docs/registry/number-field/default.tsx
================================================
import { Input, Label, NumberField } from 'ui'
export default function Default() {
return (
Width
)
}
================================================
FILE: apps/docs/registry/number-field/disabled.tsx
================================================
import * as React from 'react'
import { ChevronDown, ChevronUp } from 'lucide-react'
import {
Label,
NumberDecrementStepper,
NumberField,
NumberIncrementStepper,
NumberInput,
NumberInputGroup,
NumberInputStepper,
} from 'ui'
export default function Default() {
return (
Width
)
}
================================================
FILE: apps/docs/registry/number-field/with-mobile-stepper.tsx
================================================
import { ChevronDown, ChevronUp } from 'lucide-react'
import { IconButton, Input, Label, NumberField, NumberInputGroup } from 'ui'
export default function Mobile() {
return (
Width
)
}
================================================
FILE: apps/docs/registry/number-field/with-stepper.tsx
================================================
import * as React from 'react'
import { ChevronDown, ChevronUp } from 'lucide-react'
import {
Label,
NumberDecrementStepper,
NumberField,
NumberIncrementStepper,
NumberInput,
NumberInputGroup,
NumberInputStepper,
} from 'ui'
export default function Default() {
return (
Width
)
}
================================================
FILE: apps/docs/registry/progress-bar/default.tsx
================================================
import {
Label,
ProgressBar,
ProgressBarFilledTrack,
ProgressBarTrack,
} from 'ui'
export default function Default() {
return (
{({ percentage }) => (
<>
Progress
>
)}
)
}
================================================
FILE: apps/docs/registry/radio/default.tsx
================================================
import { Radio, RadioGroup } from 'ui'
export default function Default() {
return (
Cat
Dog
)
}
================================================
FILE: apps/docs/registry/radio/disabled.tsx
================================================
import { Radio, RadioGroup } from 'ui'
export default function Default() {
return (
Cat
Dog
)
}
================================================
FILE: apps/docs/registry/radio/sizes.tsx
================================================
import { Radio, RadioGroup } from 'ui'
export default function Sizes() {
return (
Small Cat
Small Dog
Medium Cat
Medium Dog
Large Cat
Large Dog
)
}
================================================
FILE: apps/docs/registry/radio-group/default.tsx
================================================
import { Label, Radio, RadioGroup, RadioGroupContent } from 'ui'
export default function Default() {
return (
Favorite animal
Dog
Cat
Dragon
)
}
================================================
FILE: apps/docs/registry/radio-group/disabled.tsx
================================================
import { Label, Radio, RadioGroup, RadioGroupContent } from 'ui'
export default function Disabled() {
return (
Favorite animal
Dog
Cat
Dragon
)
}
================================================
FILE: apps/docs/registry/radio-group/horizontal.tsx
================================================
import { Label, Radio, RadioGroup, RadioGroupContent } from 'ui'
export default function Horizontal() {
return (
Favorite animal
Dog
Cat
Dragon
)
}
================================================
FILE: apps/docs/registry/range-calendar/default.tsx
================================================
import * as React from 'react'
import {
CalendarCell,
CalendarGrid,
CalendarGridBody,
CalendarGridHeader,
CalendarHeader,
CalendarHeaderCell,
CalendarHeading,
CalendarNextButton,
CalendarPrevButton,
RangeCalendar,
} from 'ui'
export default function Default() {
return (
{(day) => {day} }
{(date) => }
)
}
================================================
FILE: apps/docs/registry/search-field/default.tsx
================================================
import { Search } from 'lucide-react'
import { Label, SearchField, SearchFieldInput } from 'ui'
export default function Default() {
return (
Search
)
}
================================================
FILE: apps/docs/registry/search-field/with-clear-button.tsx
================================================
import { Search } from 'lucide-react'
import {
Label,
SearchField,
SearchFieldClearButton,
SearchFieldInput,
} from 'ui'
export default function WithClearButton() {
return (
Search
)
}
================================================
FILE: apps/docs/registry/select/default.tsx
================================================
import * as React from 'react'
import { ChevronDown } from 'lucide-react'
import {
Label,
Select,
SelectButton,
SelectContent,
SelectItem,
SelectValue,
} from 'ui'
export default function Default() {
return (
<>
Favorite Animal
{({ selectedText }) => (
{selectedText || 'Select an item'}
)}
Aardvark
Cat
Dog
Kangaroo
Panda
Snake
>
)
}
================================================
FILE: apps/docs/registry/select/disabled.tsx
================================================
import * as React from 'react'
import { ChevronDown } from 'lucide-react'
import {
Label,
Select,
SelectButton,
SelectContent,
SelectItem,
SelectValue,
} from 'ui'
export default function Disabled() {
return (
<>
Favorite Animal
{({ selectedText }) => (
{selectedText || 'Select an item'}
)}
Aardvark
Cat
Dog
Kangaroo
Panda
Snake
>
)
}
================================================
FILE: apps/docs/registry/slider/default.tsx
================================================
import { Label, Slider, SliderFilledTrack, SliderThumb, SliderTrack } from 'ui'
export default function Default() {
return (
Opacity
{({ state }) => (
<>
>
)}
)
}
================================================
FILE: apps/docs/registry/slider/vertical.tsx
================================================
import { Label, Slider, SliderFilledTrack, SliderThumb, SliderTrack } from 'ui'
export default function Vertical() {
return (
Vertical Slider
{({ state }) => (
<>
>
)}
)
}
================================================
FILE: apps/docs/registry/switch/alignment.tsx
================================================
import { Switch, SwitchIndicator } from 'ui'
export default function Alignment() {
return (
I'm on the right
I'm on the left
)
}
================================================
FILE: apps/docs/registry/switch/default.tsx
================================================
import { Switch, SwitchIndicator } from 'ui'
export default function Default() {
return (
Low power mode
)
}
================================================
FILE: apps/docs/registry/switch/disabled.tsx
================================================
import { Switch, SwitchIndicator } from 'ui'
export default function Default() {
return (
Low power mode
)
}
================================================
FILE: apps/docs/registry/switch/sizes.tsx
================================================
import { Switch, SwitchIndicator } from 'ui'
export default function Default() {
return (
)
}
================================================
FILE: apps/docs/registry/tabs/default.tsx
================================================
import { Tab, TabList, TabPanel, Tabs } from 'ui'
export default function Default() {
return (
Founding of Rome
Monarchy and Republic
Empire
Arma virumque cano, Troiae qui primus ab oris.
Senatus Populusque Romanus.
Alea jacta est.
)
}
================================================
FILE: apps/docs/registry/tabs/disabled-keys.tsx
================================================
import { Tab, TabList, TabPanel, Tabs } from 'ui'
export default function DisabledKeys() {
return (
Founding of Rome
Monarchy and Republic
Empire
Arma virumque cano, Troiae qui primus ab oris.
Senatus Populusque Romanus.
Alea jacta est.
)
}
================================================
FILE: apps/docs/registry/tabs/disabled.tsx
================================================
import { Tab, TabList, TabPanel, Tabs } from 'ui'
export default function Disabled() {
return (
Founding of Rome
Monarchy and Republic
Empire
Arma virumque cano, Troiae qui primus ab oris.
Senatus Populusque Romanus.
Alea jacta est.
)
}
================================================
FILE: apps/docs/registry/tabs/vertical.tsx
================================================
import { Tab, TabList, TabPanel, Tabs } from 'ui'
export default function Vertical() {
return (
Founding of Rome
Monarchy and
Empire
Arma virumque cano, Troiae qui primus ab oris.
Senatus Populusque Romanus.
Alea jacta est.
)
}
================================================
FILE: apps/docs/registry/text-field/default.tsx
================================================
import { Input, Label, TextField } from 'ui'
export default function Default() {
return (
Label
)
}
================================================
FILE: apps/docs/registry/text-field/disabled.tsx
================================================
import { Input, Label, TextField } from 'ui'
export default function Default() {
return (
Label
)
}
================================================
FILE: apps/docs/registry/text-field/with-error.tsx
================================================
import { Input, Label, TextField, TextFieldErrorMessage } from 'ui'
export default function withError() {
return (
Label
This is an error message
)
}
================================================
FILE: apps/docs/registry/tooltip/default.tsx
================================================
import { Button, Tooltip, TooltipContent } from 'ui'
export default function Default() {
return (
Hover Me
Hello!
)
}
================================================
FILE: apps/docs/registry/tooltip/placement.tsx
================================================
import { type TooltipProps } from 'react-aria-components'
import { Button, Tooltip, TooltipContent } from 'ui'
type Placement = Pick['placement']
export default function Placement() {
const placements: Placement[] = [
'bottom',
'bottom left',
'bottom right',
'bottom start',
'bottom end',
'top',
'top left',
'top right',
'top start',
'top end',
'left',
'left top',
'left bottom',
'start',
'start top',
'start bottom',
'right',
'right top',
'right bottom',
'end',
'end top',
'end bottom',
]
return (
{placements.map((placement, idx) => (
{placement}
Hello!
))}
)
}
================================================
FILE: apps/docs/styles/globals.css
================================================
@tailwind base;
@tailwind components;
@tailwind utilities;
================================================
FILE: apps/docs/tailwind.config.js
================================================
/** @type {import('tailwindcss').Config} */
// eslint-disable-next-line @typescript-eslint/no-var-requires
const sharedConfig = require('tailwind-config/tailwind.config.cjs')
module.exports = {
presets: [sharedConfig],
content: [
'./app/**/*.{js,ts,jsx,tsx}',
'./components/**/*.{js,ts,jsx,tsx}',
'./registry/**/*.{js,ts,jsx,tsx}',
'../../packages/**/*.{js,ts,jsx,tsx}',
],
theme: {
extend: {
typography: (theme) => ({
DEFAULT: {
css: {
'code::before': {
content: 'none',
},
'code::after': {
content: 'none',
},
'code:not([data-rehype-pretty-code-figure] code)': {
color: theme('colors.slate.800'),
fontWeight: 'normal',
backgroundColor: theme('colors.slate.100'),
paddingTop: '2px',
paddingRight: theme('spacing.1'),
paddingBottom: '2px',
paddingLeft: theme('spacing.1'),
borderRadius: theme('spacing.1'),
letterSpacing: theme('letterSpacing.wide'),
},
'strong code:not([data-rehype-pretty-code-figure] code)': {
fontWeight: 'bolder',
},
'[data-rehype-pretty-code-title]': {
backgroundColor: '#282a36',
borderTopLeftRadius: theme('borderRadius.md'),
borderTopRightRadius: theme('borderRadius.md'),
paddingLeft: theme('spacing.4'),
paddingRight: theme('spacing.4'),
paddingTop: theme('spacing.3'),
marginTop: theme('spacing.0'),
paddingBottom: theme('spacing.1'),
color: theme('colors.gray.400'),
userSelect: 'none',
'+ pre': {
borderTopLeftRadius: theme('borderRadius.none'),
borderTopRightRadius: theme('borderRadius.none'),
marginTop: theme('spacing.0'),
},
},
'[data-rehype-pretty-code-figure]': {
'> pre': {
paddingLeft: theme('spacing.0'),
paddingRight: theme('spacing.0'),
code: {
paddingLeft: theme('spacing.0'),
paddingRight: theme('spacing.0'),
'[data-line]': {
paddingLeft: theme('spacing.4'),
paddingRight: theme('spacing.4'),
},
},
},
},
},
},
invert: {
css: {
'code:not([data-rehype-pretty-code-figure] code)': {
color: theme('colors.white'),
backgroundColor: theme('colors.slate.800'),
},
},
},
}),
},
},
plugins: [require('@tailwindcss/typography')],
}
================================================
FILE: apps/docs/tsconfig.json
================================================
{
"extends": "ts-config/nextjs.json",
"compilerOptions": {
"plugins": [
{
"name": "next",
},
],
"baseUrl": ".",
"paths": {
"contentlayer/generated": ["./.contentlayer/generated"],
"@/components/ui": [
"./../../packages/ui/src", // Mock importing as local components
],
"@/*": ["./*"],
},
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"esModuleInterop": true,
"moduleResolution": "node",
"isolatedModules": true,
"strictNullChecks": true,
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
".contentlayer/generated",
],
"exclude": ["node_modules"],
}
================================================
FILE: apps/docs/types.ts
================================================
import { type Node } from 'unist'
export interface UnistNode extends Node {
type: string
name?: string
tagName?: string
value?: string
properties?: {
__rawString__?: string
__className__?: string
__event__?: string
[key: string]: unknown
} & NpmCommands
attributes?: {
name: string
value: unknown
type?: string
}[]
children?: UnistNode[]
}
export interface UnistTree extends Node {
children: UnistNode[]
}
export interface NpmCommands {
__npmCommand__?: string
__yarnCommand__?: string
__pnpmCommand__?: string
__niCommand__?: string
}
export interface ExamplesListItem {
name: string
variant: string
text: string
functionName: string
}
export interface TocItemProps {
slug: string
content: string
lvl: number
}
================================================
FILE: apps/storybook/.eslintignore
================================================
!.storybook
================================================
FILE: apps/storybook/.eslintrc.js
================================================
// eslint-disable-next-line @typescript-eslint/no-var-requires
const path = require('path')
module.exports = {
root: true,
extends: ['custom', 'plugin:storybook/recommended'],
settings: {
tailwindcss: {
config: path.join(__dirname, './tailwind.config.js'),
},
},
}
================================================
FILE: apps/storybook/.gitignore
================================================
.turbo
storybook-static/
================================================
FILE: apps/storybook/.storybook/main.ts
================================================
import { dirname, join } from "path";
/** @type { import('@storybook/react-vite').StorybookConfig } */
import { mergeConfig } from 'vite'
import tsconfigPaths from 'vite-tsconfig-paths'
import type { StorybookConfig } from '@storybook/react-vite'
const config: StorybookConfig = {
framework: getAbsolutePath("@storybook/react-vite"),
stories: ['../src/**/*.stories.@(js|jsx|ts|tsx)'],
addons: [
getAbsolutePath("@storybook/addon-a11y"),
getAbsolutePath("@storybook/addon-links"),
getAbsolutePath("@storybook/addon-essentials"),
{
name: '@storybook/addon-styling',
},
],
typescript: {
reactDocgen: false,
},
viteFinal(config, { configType }) {
return mergeConfig(config, {
plugins: [tsconfigPaths()],
})
},
}
export default config
function getAbsolutePath(value: string): any {
return dirname(require.resolve(join(value, "package.json")));
}
================================================
FILE: apps/storybook/.storybook/preview.ts
================================================
/** @type { import('@storybook/react').Preview } */
import { withThemeByClassName } from '@storybook/addon-styling'
import '../src/styles.css'
import { Preview } from '@storybook/react'
const preview: Preview = {
parameters: {
actions: { argTypesRegex: '^on[A-Z].*' },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
},
}
export const decorators = [
withThemeByClassName({
themes: {
light: '',
dark: 'dark bg-slate-800',
},
defaultTheme: 'light',
}),
]
export default preview
================================================
FILE: apps/storybook/package.json
================================================
{
"name": "storybook",
"version": "0.1.0",
"private": true,
"main": "src/index.ts",
"types": "src/index.ts",
"scripts": {
"lint": "eslint --ignore-path .gitignore '**/*.{js,jsx,cjs,mjs,ts,tsx,json}'",
"storybook": "storybook dev -p 6006",
"build": "storybook build",
"upgrade": "pnpm dlx sb@latest upgrade",
"typecheck": "tsc --noEmit --pretty"
},
"dependencies": {
"@babel/preset-env": "^7.23.7",
"@babel/preset-react": "^7.23.3",
"@babel/preset-typescript": "^7.23.3",
"@storybook/addon-a11y": "^7.6.7",
"@storybook/addon-essentials": "^7.6.7",
"@storybook/addon-interactions": "^7.6.7",
"@storybook/addon-links": "^7.6.7",
"@storybook/addon-styling": "^1.3.7",
"@storybook/blocks": "^7.6.7",
"@storybook/react": "^7.6.7",
"@storybook/react-vite": "^7.6.7",
"@storybook/testing-library": "^0.2.2",
"autoprefixer": "^10.4.16",
"lucide-react": "^0.259.0",
"postcss-loader": "^7.3.4",
"prop-types": "^15.8.1",
"react": "^18.2.0",
"react-aria-components": "1.0.0",
"react-dom": "^18.2.0",
"storybook": "^7.6.7",
"tailwindcss": "^3.4.1",
"tailwindcss-animate": "^1.0.7",
"typescript": "^5.1.6",
"ui": "workspace:*",
"vite": "^4.5.1",
"vite-tsconfig-paths": "^4.2.3"
},
"devDependencies": {
"@storybook/manager-api": "^7.6.7",
"@storybook/theming": "^7.6.7",
"@types/react": "18.0.27",
"@types/react-dom": "18.0.10",
"@typescript-eslint/eslint-plugin": "^5.62.0",
"eslint": "^8.56.0",
"eslint-config-custom": "workspace:*",
"eslint-plugin-storybook": "^0.6.15",
"postcss": "^8.4.33",
"postcss-config": "workspace:*",
"tailwind-config": "workspace:*",
"ts-config": "workspace:*"
}
}
================================================
FILE: apps/storybook/postcss.config.cjs
================================================
/** @type {import('postcss').Config} */
module.exports = require('postcss-config/postcss.config.cjs')
================================================
FILE: apps/storybook/src/components/breadcrumbs/breadcrumbs.stories.tsx
================================================
import { Breadcrumbs } from 'ui'
import { type Meta } from '@storybook/react'
import DefaultStory from './stories/default'
const meta: Meta = {
title: 'Breadcrumbs',
component: Breadcrumbs,
}
export default meta
export const Default = () =>
================================================
FILE: apps/storybook/src/components/breadcrumbs/stories/default.tsx
================================================
import { ChevronRight } from 'lucide-react'
import { BreadcrumbItem, BreadcrumbLink, Breadcrumbs } from 'ui'
export default function Default() {
return (
}>
Home
}>
React Aria
useBreadcrumbs
)
}
================================================
FILE: apps/storybook/src/components/button/button.stories.tsx
================================================
import { Button } from 'ui'
import { type Meta } from '@storybook/react'
import DefaultStory from './stories/default'
import DisabledStory from './stories/disabled'
import SizesStory from './stories/sizes'
import ThemeStory from './stories/theme'
const meta: Meta = {
title: 'Button',
component: Button,
}
export default meta
export const Default = () =>
export const Sizes = () =>
export const Theme = () =>
export const Disabled = () =>
================================================
FILE: apps/storybook/src/components/button/stories/default.tsx
================================================
import { Button } from 'ui'
export default function Default() {
return Press Me
}
================================================
FILE: apps/storybook/src/components/button/stories/disabled.tsx
================================================
import { Button } from 'ui'
export default function Disabled() {
return (
Solid
Outline
Subtle
Ghost
Destructive
Link
)
}
================================================
FILE: apps/storybook/src/components/button/stories/sizes.tsx
================================================
import { Button } from 'ui'
export default function Sizes() {
return (
X Small
Small
Medium
Large
)
}
================================================
FILE: apps/storybook/src/components/button/stories/theme.tsx
================================================
import { Button } from 'ui'
export default function Theme() {
return (
Solid
Outline
Subtle
Ghost
Destructive
Link
)
}
================================================
FILE: apps/storybook/src/components/calendar/calendar.stories.tsx
================================================
import { Calendar } from 'ui'
import { type Meta } from '@storybook/react'
import DefaultStory from './stories/default'
const meta: Meta = {
title: 'Calendar',
component: Calendar,
}
export default meta
export const Default = () =>
================================================
FILE: apps/storybook/src/components/calendar/stories/default.tsx
================================================
import {
Calendar,
CalendarCell,
CalendarGrid,
CalendarGridBody,
CalendarGridHeader,
CalendarHeader,
CalendarHeaderCell,
CalendarHeading,
CalendarNextButton,
CalendarPrevButton,
} from 'ui'
export default function Default() {
return (
{(day) => {day} }
{(date) => }
)
}
================================================
FILE: apps/storybook/src/components/checkbox/checkbox.stories.tsx
================================================
import { Checkbox } from 'ui'
import { type Meta } from '@storybook/react'
import DefaultStory from './stories/default'
import DisabledStory from './stories/disabled'
import SizesStory from './stories/sizes'
const meta: Meta = {
title: 'Checkbox',
component: Checkbox,
}
export default meta
export const Default = () =>
export const Sizes = () =>
export const Disabled = () =>
================================================
FILE: apps/storybook/src/components/checkbox/stories/default.tsx
================================================
import { Checkbox } from 'ui'
export default function Default() {
return (
Cat
Dog
Reptile
)
}
================================================
FILE: apps/storybook/src/components/checkbox/stories/disabled.tsx
================================================
import { Checkbox } from 'ui'
export default function Sizes() {
return (
Medium Cat
Medium Dog
Medium Reptile
)
}
================================================
FILE: apps/storybook/src/components/checkbox/stories/sizes.tsx
================================================
import { Checkbox } from 'ui'
export default function Sizes() {
return (
Small Cat
Small Dog
Small Reptile
Medium Cat
Medium Dog
Medium Reptile
Large Cat
Large Dog
Large Reptile
)
}
================================================
FILE: apps/storybook/src/components/checkbox-group/checkbox-group.stories.tsx
================================================
import { CheckboxGroup } from 'ui'
import { type Meta } from '@storybook/react'
import DefaultStory from './stories/default'
import DisabledStory from './stories/disabled'
import HorizontalStory from './stories/horizontal'
const meta: Meta = {
title: 'CheckboxGroup',
component: CheckboxGroup,
}
export default meta
export const Default = () =>
export const Horizontal = () =>
export const Disabled = () =>
================================================
FILE: apps/storybook/src/components/checkbox-group/stories/default.tsx
================================================
import { Checkbox, CheckboxGroup, CheckboxGroupContent, Label } from 'ui'
export default function Default() {
return (
Favorite sports
Soccer
Baseball
Basketball
)
}
================================================
FILE: apps/storybook/src/components/checkbox-group/stories/disabled.tsx
================================================
import { Checkbox, CheckboxGroup, CheckboxGroupContent, Label } from 'ui'
export default function Default() {
return (
Favorite sports
Soccer
Baseball
Basketball
)
}
================================================
FILE: apps/storybook/src/components/checkbox-group/stories/horizontal.tsx
================================================
import { Checkbox, CheckboxGroup, CheckboxGroupContent, Label } from 'ui'
export default function Horizontal() {
return (
Favorite sports
Soccer
Baseball
Basketball
)
}
================================================
FILE: apps/storybook/src/components/combobox/combo-box.stories.tsx
================================================
import { ComboBox } from 'ui'
import { type Meta } from '@storybook/react'
import DefaultStory from './stories/default'
import DisabledStory from './stories/disabled'
import DisabledKeysStory from './stories/disabled-keys'
import WithButtonStory from './stories/with-button'
const meta: Meta = {
title: 'ComboBox',
component: ComboBox,
}
export default meta
export const Default = () =>
export const WithButton = () =>
export const Disabled = () =>
export const DisabledKeys = () =>
================================================
FILE: apps/storybook/src/components/combobox/stories/default.tsx
================================================
import {
ComboBox,
ComboBoxContent,
ComboBoxInput,
ComboBoxItem,
Label,
} from 'ui'
export default function Default() {
return (
Favorite Animal
Aardvark
Cat
Dog
Kangaroo
Panda
Snake
)
}
================================================
FILE: apps/storybook/src/components/combobox/stories/disabled-keys.tsx
================================================
import { ChevronDown } from 'lucide-react'
import {
ComboBox,
ComboBoxButton,
ComboBoxContent,
ComboBoxInput,
ComboBoxItem,
iconButtonVariants,
Label,
} from 'ui'
export default function DisabledKeys() {
return (
Favorite Animal
Aardvark
Cat
Dog
Kangaroo
Panda
Snake
)
}
================================================
FILE: apps/storybook/src/components/combobox/stories/disabled.tsx
================================================
import { ChevronDown } from 'lucide-react'
import {
ComboBox,
ComboBoxButton,
ComboBoxContent,
ComboBoxInput,
ComboBoxItem,
iconButtonVariants,
Label,
} from 'ui'
export default function Disabled() {
return (
Favorite Animal
Aardvark
Cat
Dog
Kangaroo
Panda
Snake
)
}
================================================
FILE: apps/storybook/src/components/combobox/stories/with-button.tsx
================================================
import { ChevronDown } from 'lucide-react'
import {
ComboBox,
ComboBoxButton,
ComboBoxContent,
ComboBoxInput,
ComboBoxItem,
iconButtonVariants,
Label,
} from 'ui'
export default function WithButton() {
return (
Favorite Animal
Aardvark
Cat
Dog
Kangaroo
Panda
Snake
)
}
================================================
FILE: apps/storybook/src/components/date-field/date-field.stories.tsx
================================================
import { DateField } from 'ui'
import { type Meta } from '@storybook/react'
import DefaultStory from './stories/default'
import DisabledStory from './stories/disabled'
const meta: Meta = {
title: 'DateField',
component: DateField,
}
export default meta
export const Default = () =>
export const Disabled = () =>
================================================
FILE: apps/storybook/src/components/date-field/stories/default.tsx
================================================
import { DateField, DateInput, DateInputGroup, DateSegment, Label } from 'ui'
export default function Default() {
return (
Date Label
{(segment) => }
)
}
================================================
FILE: apps/storybook/src/components/date-field/stories/disabled.tsx
================================================
import { DateField, DateInput, DateInputGroup, DateSegment, Label } from 'ui'
export default function Disabled() {
return (
Date Label
{(segment) => }
)
}
================================================
FILE: apps/storybook/src/components/date-input/date-input.stories.tsx
================================================
import { DateInput } from 'ui'
import { type Meta } from '@storybook/react'
import DefaultStory from './stories/default'
import SizesStory from './stories/sizes'
const meta: Meta = {
title: 'DateInput',
component: DateInput,
}
export default meta
export const Default = () =>
export const Sizes = () =>
================================================
FILE: apps/storybook/src/components/date-input/stories/default.tsx
================================================
import { DateField, DateInput, DateInputGroup, DateSegment } from 'ui'
export default function Default() {
return (
{(segment) => }
)
}
================================================
FILE: apps/storybook/src/components/date-input/stories/sizes.tsx
================================================
import { DateField, DateInput, DateInputGroup, DateSegment } from 'ui'
export default function Sizes() {
return (
{(segment) => }
{(segment) => }
{(segment) => }
{(segment) => }
)
}
================================================
FILE: apps/storybook/src/components/date-picker/date-picker.stories.tsx
================================================
import { DatePicker } from 'ui'
import { type Meta } from '@storybook/react'
import DefaultStory from './stories/default'
import DisabledStory from './stories/disabled'
const meta: Meta = {
title: 'DatePicker',
component: DatePicker,
}
export default meta
export const Default = () =>
export const Disabled = () =>
================================================
FILE: apps/storybook/src/components/date-picker/stories/default.tsx
================================================
import { ChevronDown, ChevronLeft, ChevronRight } from 'lucide-react'
import {
Calendar,
CalendarCell,
CalendarGrid,
CalendarGridBody,
CalendarGridHeader,
CalendarHeader,
CalendarHeaderCell,
CalendarHeading,
CalendarNextButton,
CalendarPrevButton,
DateInput,
DateInputGroup,
DatePicker,
DatePickerButton,
DatePickerContent,
DateSegment,
iconButtonVariants,
Label,
} from 'ui'
export default function Default() {
return (
Date
{(segment) => }
{(day) => {day} }
{(date) => }
)
}
================================================
FILE: apps/storybook/src/components/date-picker/stories/disabled.tsx
================================================
import { ChevronDown, ChevronLeft, ChevronRight } from 'lucide-react'
import {
Calendar,
CalendarCell,
CalendarGrid,
CalendarGridBody,
CalendarGridHeader,
CalendarHeader,
CalendarHeaderCell,
CalendarHeading,
CalendarNextButton,
CalendarPrevButton,
DateInput,
DateInputGroup,
DatePicker,
DatePickerButton,
DatePickerContent,
DateSegment,
iconButtonVariants,
Label,
} from 'ui'
export default function Default() {
return (
Date
{(segment) => }
{(day) => {day} }
{(date) => }
)
}
================================================
FILE: apps/storybook/src/components/date-range-picker/date-range-picker.stories.tsx
================================================
import { DateRangePicker } from 'ui'
import { type Meta } from '@storybook/react'
import DefaultStory from './stories/default'
import DisabledStory from './stories/disabled'
const meta: Meta = {
title: 'DateRangePicker',
component: DateRangePicker,
}
export default meta
export const Default = () =>
export const Disabled = () =>
================================================
FILE: apps/storybook/src/components/date-range-picker/stories/default.tsx
================================================
import { ChevronDown } from 'lucide-react'
import {
CalendarCell,
CalendarGrid,
CalendarGridBody,
CalendarGridHeader,
CalendarHeader,
CalendarHeaderCell,
CalendarHeading,
CalendarNextButton,
CalendarPrevButton,
DateInput,
DateInputGroup,
DatePickerButton,
DatePickerContent,
DateRangePicker,
DateSegment,
iconButtonVariants,
Label,
RangeCalendar,
} from 'ui'
export default function Default() {
return (
Date Range
{(segment) => }
–
{(segment) => }
{(day) => {day} }
{(date) => }
)
}
================================================
FILE: apps/storybook/src/components/date-range-picker/stories/disabled.tsx
================================================
import { ChevronDown } from 'lucide-react'
import {
CalendarCell,
CalendarGrid,
CalendarGridBody,
CalendarGridHeader,
CalendarHeader,
CalendarHeaderCell,
CalendarHeading,
CalendarNextButton,
CalendarPrevButton,
DateInput,
DateInputGroup,
DatePickerButton,
DatePickerContent,
DateRangePicker,
DateSegment,
iconButtonVariants,
Label,
RangeCalendar,
} from 'ui'
export default function Default() {
return (
Date Range
{(segment) => }
–
{(segment) => }
{(day) => {day} }
{(date) => }
)
}
================================================
FILE: apps/storybook/src/components/grid-list/grid-list.stories.tsx
================================================
import { GridList } from 'ui'
import { type Meta } from '@storybook/react'
import DefaultStory from './stories/default'
const meta: Meta = {
title: 'GridList',
component: GridList,
}
export default meta
export const Default = () =>
================================================
FILE: apps/storybook/src/components/grid-list/stories/default.tsx
================================================
import { Info } from 'lucide-react'
import { Checkbox, GridList, GridListItem, IconButton } from 'ui'
export default function Default() {
return (
Charizard
Blastoise
Venusaur
Pikachu
)
}
================================================
FILE: apps/storybook/src/components/icon-button/icon-button.stories.tsx
================================================
import { IconButton } from 'ui'
import { type Meta } from '@storybook/react'
import DefaultStory from './stories/default'
import DisabledStory from './stories/disabled'
import SizesStory from './stories/sizes'
import ThemeStory from './stories/theme'
const meta: Meta = {
title: 'IconButton',
component: IconButton,
}
export default meta
export const Default = () =>
export const Sizes = () =>
export const Theme = () =>
export const Disabled = () =>
================================================
FILE: apps/storybook/src/components/icon-button/stories/default.tsx
================================================
import { AlertCircle } from 'lucide-react'
import { IconButton } from 'ui'
export default function Default() {
return (
)
}
================================================
FILE: apps/storybook/src/components/icon-button/stories/disabled.tsx
================================================
import { AlertCircle } from 'lucide-react'
import { IconButton } from 'ui'
export default function Disabled() {
return (
)
}
================================================
FILE: apps/storybook/src/components/icon-button/stories/sizes.tsx
================================================
import { AlertCircle } from 'lucide-react'
import { IconButton } from 'ui'
export default function Sizes() {
return (
)
}
================================================
FILE: apps/storybook/src/components/icon-button/stories/theme.tsx
================================================
import { AlertCircle } from 'lucide-react'
import { IconButton } from 'ui'
export default function Theme() {
return (
)
}
================================================
FILE: apps/storybook/src/components/input/input.stories.tsx
================================================
import { Input } from 'ui'
import { type Meta } from '@storybook/react'
import DefaultStory from './stories/default'
import DisabledStory from './stories/disabled'
import SizesStory from './stories/sizes'
const meta: Meta = {
title: 'Input',
component: Input,
}
export default meta
export const Default = () =>
export const Sizes = () =>
export const Disabled = () =>
================================================
FILE: apps/storybook/src/components/input/stories/default.tsx
================================================
import { Input } from 'ui'
export default function Default() {
return
}
================================================
FILE: apps/storybook/src/components/input/stories/disabled.tsx
================================================
import { Input } from 'ui'
export default function Disabled() {
return
}
================================================
FILE: apps/storybook/src/components/input/stories/sizes.tsx
================================================
import { Input } from 'ui'
export default function Sizes() {
return (
)
}
================================================
FILE: apps/storybook/src/components/label/label.stories.tsx
================================================
import { Label } from 'ui'
import { type Meta } from '@storybook/react'
import DefaultStory from './stories/default'
const meta: Meta = {
title: 'Label',
component: Label,
}
export default meta
export const Default = () =>
================================================
FILE: apps/storybook/src/components/label/stories/default.tsx
================================================
import { Label } from 'ui'
export default function Default() {
return This is a label
}
================================================
FILE: apps/storybook/src/components/menu/menu.stories.tsx
================================================
import { Menu } from 'ui'
import { type Meta } from '@storybook/react'
import AsCheckboxStory from './stories/as-checkbox'
import AsRadioStory from './stories/as-radio'
import DefaultStory from './stories/default'
import DisabledStory from './stories/disabled'
import WithSectionsStory from './stories/with-sections'
const meta: Meta = {
title: 'Menu',
component: Menu,
}
export default meta
export const Default = () =>
export const WithSections = () =>
export const AsCheckbox = () =>
export const AsRadio = () =>
export const Disabled = () =>
================================================
FILE: apps/storybook/src/components/menu/stories/as-checkbox.tsx
================================================
import * as React from 'react'
import { ChevronDown } from 'lucide-react'
import { type Selection } from 'react-aria-components'
import { Button, Menu, MenuContent, MenuItem } from 'ui'
export default function AsCheckbox() {
const [selected, setSelected] = React.useState(new Set(['bar1']))
return (
Menu
Foo
Bar
Baz
)
}
================================================
FILE: apps/storybook/src/components/menu/stories/as-radio.tsx
================================================
import * as React from 'react'
import { ChevronDown } from 'lucide-react'
import { type Selection } from 'react-aria-components'
import { Button, Menu, MenuContent, MenuItem } from 'ui'
export default function AsRadio() {
const [selected, setSelected] = React.useState(new Set(['bar1']))
return (
Menu
Foo
Bar
Baz
)
}
================================================
FILE: apps/storybook/src/components/menu/stories/default.tsx
================================================
import { ChevronDown } from 'lucide-react'
import { Button, Menu, MenuContent, MenuItem } from 'ui'
export default function Default() {
return (
Menu
Foo
Bar
Baz
)
}
================================================
FILE: apps/storybook/src/components/menu/stories/disabled.tsx
================================================
import { ChevronDown } from 'lucide-react'
import { Button, Menu, MenuContent, MenuItem } from 'ui'
export default function Disabled() {
return (
Menu
Foo
Bar
Baz
)
}
================================================
FILE: apps/storybook/src/components/menu/stories/with-sections.tsx
================================================
import { ChevronDown } from 'lucide-react'
import {
Button,
Menu,
MenuContent,
MenuHeader,
MenuItem,
MenuSection,
MenuSeparator,
} from 'ui'
export default function WithSections() {
return (
Menu
Styles
Foo
Bar
Baz
Align
Foo
Bar
Baz
)
}
================================================
FILE: apps/storybook/src/components/meter/meter.stories.tsx
================================================
import { Meter } from 'ui'
import { type Meta } from '@storybook/react'
import DefaultStory from './stories/default'
const meta: Meta = {
title: 'Meter',
component: Meter,
}
export default meta
export const Default = () =>
================================================
FILE: apps/storybook/src/components/meter/stories/default.tsx
================================================
import { Label, Meter, MeterFilledTrack, MeterTrack } from 'ui'
export default function Default() {
return (
{({ percentage }) => (
<>
Meter Label
>
)}
)
}
================================================
FILE: apps/storybook/src/components/modal/modal.stories.tsx
================================================
import { ModalContent } from 'ui'
import { type Meta } from '@storybook/react'
import DefaultStory from './stories/default'
import DismissableFalseStory from './stories/dismissable-false'
import SizesStory from './stories/sizes'
const meta: Meta = {
title: 'Modal',
component: ModalContent,
}
export default meta
export const Default = () =>
export const Sizes = () =>
export const DismissableFalse = () =>
================================================
FILE: apps/storybook/src/components/modal/stories/default.tsx
================================================
import * as React from 'react'
import { X } from 'lucide-react'
import {
Button,
IconButton,
ModalBody,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
} from 'ui'
export default function Default() {
const [isOpen, setIsOpen] = React.useState(false)
return (
<>
setIsOpen(true)}>Open Modal
Modal Title
setIsOpen(false)}
>
Sit nulla est ex deserunt exercitation anim occaecat. Nostrud
ullamco deserunt aute id consequat veniam incididunt duis in sint
irure nisi. Mollit officia cillum Lorem ullamco minim nostrud elit
officia tempor esse quis.
setIsOpen(false)}>
Close
setIsOpen(false)}
>
Confirm
>
)
}
================================================
FILE: apps/storybook/src/components/modal/stories/dismissable-false.tsx
================================================
import * as React from 'react'
import { X } from 'lucide-react'
import {
Button,
IconButton,
ModalBody,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
} from 'ui'
export default function DismissableFalse() {
const [isOpen, setIsOpen] = React.useState(false)
return (
<>
setIsOpen(true)}>Open Modal
Modal Title
setIsOpen(false)}
>
Sit nulla est ex deserunt exercitation anim occaecat. Nostrud
ullamco deserunt aute id consequat veniam incididunt duis in sint
irure nisi. Mollit officia cillum Lorem ullamco minim nostrud elit
officia tempor esse quis.
setIsOpen(false)}>
Close
setIsOpen(false)}
>
Confirm
>
)
}
================================================
FILE: apps/storybook/src/components/modal/stories/set-autofocus.tsx
================================================
import * as React from 'react'
import { X } from 'lucide-react'
import {
Button,
IconButton,
Input,
Label,
ModalBody,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
TextField,
} from 'ui'
export default function SetAutofocus() {
const [isOpen, setIsOpen] = React.useState(false)
return (
<>
setIsOpen(true)}>Open Modal
Modal Title
setIsOpen(false)}
>
First Name
Last Name
setIsOpen(false)}>
Close
setIsOpen(false)}
>
Confirm
>
)
}
================================================
FILE: apps/storybook/src/components/modal/stories/sizes.tsx
================================================
import * as React from 'react'
import { X } from 'lucide-react'
import {
Button,
IconButton,
ModalBody,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
type ModalContentProps,
} from 'ui'
type Size = Pick['size']
export default function Default() {
const [isOpen, setIsOpen] = React.useState(false)
const [modalSize, setModalSize] = React.useState('md')
const sizes: Size[] = ['xs', 'sm', 'md', 'lg', 'xl', 'full']
const handlePress = (size: Size, open: boolean) => {
setModalSize(size)
setIsOpen(open)
}
return (
<>
{sizes.map((size, idx) => (
handlePress(size, true)} key={idx}>
Open {size}
))}
Modal Title
setIsOpen(false)}
>
Sit nulla est ex deserunt exercitation anim occaecat. Nostrud
ullamco deserunt aute id consequat veniam incididunt duis in sint
irure nisi. Mollit officia cillum Lorem ullamco minim nostrud elit
officia tempor esse quis.
setIsOpen(false)}>
Close
setIsOpen(false)}
>
Confirm
>
)
}
================================================
FILE: apps/storybook/src/components/number-field/number-field.stories.tsx
================================================
import { NumberField } from 'ui'
import { type Meta } from '@storybook/react'
import DefaultStory from './stories/default'
import DisabledStory from './stories/disabled'
import MobileStepperStory from './stories/with-mobile-stepper'
import StepperStory from './stories/with-stepper'
const meta: Meta = {
title: 'NumberField',
component: NumberField,
}
export default meta
export const Default = () =>
export const Stepper = () =>
export const MobileStepper = () =>
export const Disabled = () =>
================================================
FILE: apps/storybook/src/components/number-field/stories/default.tsx
================================================
import { Input, Label, NumberField } from 'ui'
export default function Default() {
return (
Width
)
}
================================================
FILE: apps/storybook/src/components/number-field/stories/disabled.tsx
================================================
import * as React from 'react'
import { ChevronDown, ChevronUp } from 'lucide-react'
import {
Label,
NumberDecrementStepper,
NumberField,
NumberIncrementStepper,
NumberInput,
NumberInputGroup,
NumberInputStepper,
} from 'ui'
export default function Default() {
return (
Width
)
}
================================================
FILE: apps/storybook/src/components/number-field/stories/with-mobile-stepper.tsx
================================================
import { ChevronDown, ChevronUp } from 'lucide-react'
import { IconButton, Input, Label, NumberField, NumberInputGroup } from 'ui'
export default function Mobile() {
return (
Width
)
}
================================================
FILE: apps/storybook/src/components/number-field/stories/with-stepper.tsx
================================================
import * as React from 'react'
import { ChevronDown, ChevronUp } from 'lucide-react'
import {
Label,
NumberDecrementStepper,
NumberField,
NumberIncrementStepper,
NumberInput,
NumberInputGroup,
NumberInputStepper,
} from 'ui'
export default function Default() {
return (
Width
)
}
================================================
FILE: apps/storybook/src/components/progress-bar/progress-bar.stories.tsx
================================================
import { ProgressBar } from 'ui'
import { type Meta } from '@storybook/react'
import DefaultStory from './stories/default'
const meta: Meta = {
title: 'ProgressBar',
component: ProgressBar,
}
export default meta
export const Default = () =>
================================================
FILE: apps/storybook/src/components/progress-bar/stories/default.tsx
================================================
import {
Label,
ProgressBar,
ProgressBarFilledTrack,
ProgressBarTrack,
} from 'ui'
export default function Default() {
return (
{({ percentage }) => (
<>
Progress
>
)}
)
}
================================================
FILE: apps/storybook/src/components/radio/radio.stories.tsx
================================================
import { Radio } from 'ui'
import { type Meta } from '@storybook/react'
import DefaultStory from './stories/default'
import DisabledStory from './stories/disabled'
import SizesStory from './stories/sizes'
const meta: Meta = {
title: 'Radio',
component: Radio,
}
export default meta
export const Default = () =>
export const Sizes = () =>
export const Disabled = () =>
================================================
FILE: apps/storybook/src/components/radio/stories/default.tsx
================================================
import { Radio, RadioGroup } from 'ui'
export default function Default() {
return (
Cat
Dog
)
}
================================================
FILE: apps/storybook/src/components/radio/stories/disabled.tsx
================================================
import { Radio, RadioGroup } from 'ui'
export default function Default() {
return (
Cat
Dog
)
}
================================================
FILE: apps/storybook/src/components/radio/stories/sizes.tsx
================================================
import { Radio, RadioGroup } from 'ui'
export default function Sizes() {
return (
Small Cat
Small Dog
Medium Cat
Medium Dog
Large Cat
Large Dog
)
}
================================================
FILE: apps/storybook/src/components/radio-group/radio-group.stories.tsx
================================================
import { RadioGroup } from 'ui'
import { type Meta } from '@storybook/react'
import DefaultStory from './stories/default'
import HorizontalStory from './stories/horizontal'
const meta: Meta = {
title: 'RadioGroup',
component: RadioGroup,
}
export default meta
export const Default = () =>
export const Horizontal = () =>
================================================
FILE: apps/storybook/src/components/radio-group/stories/default.tsx
================================================
import { Label, Radio, RadioGroup, RadioGroupContent } from 'ui'
export default function Default() {
return (
Favorite animal
Dog
Cat
Dragon
)
}
================================================
FILE: apps/storybook/src/components/radio-group/stories/disabled.tsx
================================================
import { Label, Radio, RadioGroup, RadioGroupContent } from 'ui'
export default function Disabled() {
return (
Favorite animal
Dog
Cat
Dragon
)
}
================================================
FILE: apps/storybook/src/components/radio-group/stories/horizontal.tsx
================================================
import { Label, Radio, RadioGroup, RadioGroupContent } from 'ui'
export default function Horizontal() {
return (
Favorite animal
Dog
Cat
Dragon
)
}
================================================
FILE: apps/storybook/src/components/range-calendar/range-calendar.stories.tsx
================================================
import { RangeCalendar } from 'ui'
import { type Meta } from '@storybook/react'
import DefaultStory from './stories/default'
const meta: Meta = {
title: 'RangeCalendar',
component: RangeCalendar,
}
export default meta
export const Default = () =>
================================================
FILE: apps/storybook/src/components/range-calendar/stories/default.tsx
================================================
import * as React from 'react'
import {
CalendarCell,
CalendarGrid,
CalendarGridBody,
CalendarGridHeader,
CalendarHeader,
CalendarHeaderCell,
CalendarHeading,
CalendarNextButton,
CalendarPrevButton,
RangeCalendar,
} from 'ui'
export default function Default() {
return (
{(day) => {day} }
{(date) => }
)
}
================================================
FILE: apps/storybook/src/components/search-field/search-field.stories.tsx
================================================
import { SearchField } from 'ui'
import { type Meta } from '@storybook/react'
import DefaultStory from './stories/default'
import WithClearButtonStory from './stories/with-clear-button'
const meta: Meta = {
title: 'SearchField',
component: SearchField,
}
export default meta
export const Default = () =>
export const WithClearButton = () =>
================================================
FILE: apps/storybook/src/components/search-field/stories/default.tsx
================================================
import { Search } from 'lucide-react'
import { Label, SearchField, SearchFieldInput } from 'ui'
export default function Default() {
return (
Search
)
}
================================================
FILE: apps/storybook/src/components/search-field/stories/with-clear-button.tsx
================================================
import { Search } from 'lucide-react'
import {
Label,
SearchField,
SearchFieldClearButton,
SearchFieldInput,
} from 'ui'
export default function WithClearButton() {
return (
Search
)
}
================================================
FILE: apps/storybook/src/components/select/select.stories.tsx
================================================
import { Select } from 'ui'
import { type Meta } from '@storybook/react'
import DefaultStory from './stories/default'
import DisabledStory from './stories/disabled'
const meta: Meta = {
title: 'Select',
component: Select,
}
export default meta
export const Default = () =>
export const Disabled = () =>
================================================
FILE: apps/storybook/src/components/select/stories/default.tsx
================================================
import * as React from 'react'
import { ChevronDown } from 'lucide-react'
import {
Label,
Select,
SelectButton,
SelectContent,
SelectItem,
SelectValue,
} from 'ui'
export default function Default() {
return (
<>
Favorite Animal
{({ selectedText }) => (
{selectedText || 'Select an item'}
)}
Aardvark
Cat
Dog
Kangaroo
Panda
Snake
>
)
}
================================================
FILE: apps/storybook/src/components/select/stories/disabled.tsx
================================================
import * as React from 'react'
import { ChevronDown } from 'lucide-react'
import {
Label,
Select,
SelectButton,
SelectContent,
SelectItem,
SelectValue,
} from 'ui'
export default function Disabled() {
return (
<>
Favorite Animal
{({ selectedText }) => (
{selectedText || 'Select an item'}
)}
Aardvark
Cat
Dog
Kangaroo
Panda
Snake
>
)
}
================================================
FILE: apps/storybook/src/components/slider/slider.stories.tsx
================================================
import { Slider } from 'ui'
import { type Meta } from '@storybook/react'
import DefaultStory from './stories/default'
import VerticalStory from './stories/vertical'
const meta: Meta = {
title: 'Slider',
component: Slider,
}
export default meta
export const Default = () =>
export const Vertical = () =>
================================================
FILE: apps/storybook/src/components/slider/stories/default.tsx
================================================
import { Label, Slider, SliderFilledTrack, SliderThumb, SliderTrack } from 'ui'
export default function Default() {
return (
Opacity
{({ state }) => (
<>
>
)}
)
}
================================================
FILE: apps/storybook/src/components/slider/stories/vertical.tsx
================================================
import { Label, Slider, SliderFilledTrack, SliderThumb, SliderTrack } from 'ui'
export default function Vertical() {
return (
Vertical Slider
{({ state }) => (
<>
>
)}
)
}
================================================
FILE: apps/storybook/src/components/switch/stories/alignment.tsx
================================================
import { Switch, SwitchIndicator } from 'ui'
export default function Alignment() {
return (
I'm on the right
I'm on the left
)
}
================================================
FILE: apps/storybook/src/components/switch/stories/default.tsx
================================================
import { Switch, SwitchIndicator } from 'ui'
export default function Default() {
return (
Low power mode
)
}
================================================
FILE: apps/storybook/src/components/switch/stories/disabled.tsx
================================================
import { Switch, SwitchIndicator } from 'ui'
export default function Default() {
return (
Low power mode
)
}
================================================
FILE: apps/storybook/src/components/switch/stories/sizes.tsx
================================================
import { Switch, SwitchIndicator } from 'ui'
export default function Default() {
return (
)
}
================================================
FILE: apps/storybook/src/components/switch/switch.stories.tsx
================================================
import { Switch } from 'ui'
import { type Meta } from '@storybook/react'
import AlignmentStory from './stories/alignment'
import DefaultStory from './stories/default'
import DisabledStory from './stories/disabled'
import SizesStory from './stories/sizes'
const meta: Meta = {
title: 'Switch',
component: Switch,
}
export default meta
export const Default = () =>
export const Alignment = () =>
export const Sizes = () =>
export const Disabled = () =>
================================================
FILE: apps/storybook/src/components/tabs/stories/default.tsx
================================================
import { Tab, TabList, TabPanel, Tabs } from 'ui'
export default function Default() {
return (
Founding of Rome
Monarchy and Republic
Empire
Arma virumque cano, Troiae qui primus ab oris.
Senatus Populusque Romanus.
Alea jacta est.
)
}
================================================
FILE: apps/storybook/src/components/tabs/stories/disabled-keys.tsx
================================================
import { Tab, TabList, TabPanel, Tabs } from 'ui'
export default function DisabledKeys() {
return (
Founding of Rome
Monarchy and Republic
Empire
Arma virumque cano, Troiae qui primus ab oris.
Senatus Populusque Romanus.
Alea jacta est.
)
}
================================================
FILE: apps/storybook/src/components/tabs/stories/disabled.tsx
================================================
import { Tab, TabList, TabPanel, Tabs } from 'ui'
export default function Disabled() {
return (
Founding of Rome
Monarchy and Republic
Empire
Arma virumque cano, Troiae qui primus ab oris.
Senatus Populusque Romanus.
Alea jacta est.
)
}
================================================
FILE: apps/storybook/src/components/tabs/stories/vertical.tsx
================================================
import { Tab, TabList, TabPanel, Tabs } from 'ui'
export default function Vertical() {
return (
Founding of Rome
Monarchy and
Empire
Arma virumque cano, Troiae qui primus ab oris.
Senatus Populusque Romanus.
Alea jacta est.
)
}
================================================
FILE: apps/storybook/src/components/tabs/tabs.stories.tsx
================================================
import { Tabs } from 'ui'
import { type Meta } from '@storybook/react'
import DefaultStory from './stories/default'
import DisabledStory from './stories/disabled'
import DisabledKeysStory from './stories/disabled-keys'
import VerticalStory from './stories/vertical'
const meta: Meta = {
title: 'Tabs',
component: Tabs,
}
export default meta
export const Default = () =>
export const Vertical = () =>
export const Disabled = () =>
export const DisabledKeys = () =>
================================================
FILE: apps/storybook/src/components/text-field/stories/default.tsx
================================================
import { Input, Label, TextField } from 'ui'
export default function Default() {
return (
Label
)
}
================================================
FILE: apps/storybook/src/components/text-field/stories/disabled.tsx
================================================
import { Input, Label, TextField } from 'ui'
export default function Default() {
return (
Label
)
}
================================================
FILE: apps/storybook/src/components/text-field/stories/with-error.tsx
================================================
import { Input, Label, TextField, TextFieldErrorMessage } from 'ui'
export default function withError() {
return (
Label
This is an error message
)
}
================================================
FILE: apps/storybook/src/components/text-field/text-field.stories.tsx
================================================
import { TextField } from 'ui'
import { type Meta } from '@storybook/react'
import DefaultStory from './stories/default'
import DisabledStory from './stories/disabled'
import WithErrorStory from './stories/with-error'
const meta: Meta = {
title: 'TextField',
component: TextField,
}
export default meta
export const Default = () =>
export const Disabled = () =>
export const WithError = () =>
================================================
FILE: apps/storybook/src/components/tooltip/stories/default.tsx
================================================
import { Button, Tooltip, TooltipContent } from 'ui'
export default function Default() {
return (
Hover Me
Hello!
)
}
================================================
FILE: apps/storybook/src/components/tooltip/stories/placement.tsx
================================================
import { type TooltipProps } from 'react-aria-components'
import { Button, Tooltip, TooltipContent } from 'ui'
type Placement = Pick['placement']
export default function Placement() {
const placements: Placement[] = [
'bottom',
'bottom left',
'bottom right',
'bottom start',
'bottom end',
'top',
'top left',
'top right',
'top start',
'top end',
'left',
'left top',
'left bottom',
'start',
'start top',
'start bottom',
'right',
'right top',
'right bottom',
'end',
'end top',
'end bottom',
]
return (
{placements.map((placement, idx) => (
{placement}
Hello!
))}
)
}
================================================
FILE: apps/storybook/src/components/tooltip/tooltip.stories.tsx
================================================
import { Tooltip } from 'ui'
import { type Meta } from '@storybook/react'
import DefaultStory from './stories/default'
import PlacementStory from './stories/placement'
const meta: Meta = {
title: 'Tooltip',
component: Tooltip,
}
export default meta
export const Default = () =>
export const Placement = () =>
================================================
FILE: apps/storybook/src/styles.css
================================================
@tailwind base;
@tailwind components;
@tailwind utilities;
================================================
FILE: apps/storybook/tailwind.config.cjs
================================================
/** @type {import('tailwindcss').Config} */
// eslint-disable-next-line @typescript-eslint/no-var-requires
const sharedConfig = require('tailwind-config/tailwind.config.cjs')
module.exports = {
presets: [sharedConfig],
content: [
'./.storybook/**/*.{js,ts,jsx,tsx}',
'./src/**/*.{js,ts,jsx,tsx}',
'../../packages/**/*.{js,ts,jsx,tsx}',
],
}
================================================
FILE: apps/storybook/tsconfig.json
================================================
{
"extends": "ts-config/base.json",
"include": ["."],
"exclude": ["node_modules"],
"compilerOptions": {
"jsx": "react-jsx",
},
}
================================================
FILE: package.json
================================================
{
"name": "draft-ui",
"scripts": {
"build": "turbo run build",
"dev": "turbo run dev",
"lint": "turbo run lint",
"storybook": "turbo run storybook",
"prepare": "husky install",
"build-registry": "turbo run build-registry",
"typecheck": "turbo run typecheck"
},
"devDependencies": {
"@ianvs/prettier-plugin-sort-imports": "^3.7.2",
"eslint": "^8.56.0",
"eslint-config-custom": "workspace:*",
"husky": "^8.0.3",
"lint-staged": "^15.2.0",
"prettier": "^2.8.8",
"ts-config": "workspace:*",
"turbo": "^1.11.3"
},
"lint-staged": {
"**/*.{js,jsx,cjs,mjs,ts,tsx,json}": [
"eslint --fix"
]
}
}
================================================
FILE: packages/eslint-config-custom/index.js
================================================
module.exports = {
parser: '@typescript-eslint/parser',
extends: [
'next',
'turbo',
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
'plugin:tailwindcss/recommended',
],
parserOptions: {
ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features
sourceType: 'module', // Allows for the use of imports
},
plugins: ['@typescript-eslint/eslint-plugin'],
rules: {
'@next/next/no-html-link-for-pages': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
'react/react-in-jsx-scope': 'off',
'react/prop-types': 'off',
'prettier/prettier': 'error',
'@typescript-eslint/no-unused-vars': [
'warn',
{
varsIgnorePattern: '^_',
argsIgnorePattern: '^_',
destructuredArrayIgnorePattern: '^_',
},
],
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/consistent-type-imports': [
'error',
{ prefer: 'type-imports', fixStyle: 'inline-type-imports' },
],
'tailwindcss/no-custom-classname': 'off',
},
settings: {
react: {
version: 'detect',
},
tailwindcss: {
callees: ['cva', 'cn', 'clsx'],
},
},
}
================================================
FILE: packages/eslint-config-custom/package.json
================================================
{
"name": "eslint-config-custom",
"version": "0.0.0",
"main": "index.js",
"publishConfig": {
"access": "public"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^5.59.2",
"@typescript-eslint/parser": "^5.59.2",
"eslint": "^8.39.0",
"eslint-config-next": "^13.3.4",
"eslint-config-prettier": "^9.0.0",
"eslint-config-turbo": "^1.9.3",
"eslint-plugin-prettier": "^5.0.0",
"eslint-plugin-react": "7.32.2",
"eslint-plugin-tailwindcss": "^3.13.0",
"typescript": "^5.1.6"
}
}
================================================
FILE: packages/postcss-config/package.json
================================================
{
"name": "postcss-config",
"dependencies": {
"postcss": "^8.4.23"
}
}
================================================
FILE: packages/postcss-config/postcss.config.cjs
================================================
/** @type {import('postcss').Config} */
module.exports = {
plugins: {
'tailwindcss/nesting': {},
tailwindcss: {},
autoprefixer: {},
},
}
================================================
FILE: packages/tailwind-config/package.json
================================================
{
"name": "tailwind-config",
"dependencies": {
"tailwindcss": "^3.4.1",
"tailwindcss-animate": "^1.0.7",
"tailwindcss-react-aria-components": "1.0.0"
}
}
================================================
FILE: packages/tailwind-config/tailwind.config.cjs
================================================
/** @type {import('tailwindcss').Config} */
module.exports = {
darkMode: ['class'],
theme: {
extend: {},
},
plugins: [
require('tailwindcss-animate'),
require('tailwindcss-react-aria-components'),
],
// safelist: [
// {
// pattern: /react-aria-.+/,
// },
// ],
}
================================================
FILE: packages/ts-config/base.json
================================================
{
"$schema": "https://json.schemastore.org/tsconfig",
"display": "Default",
"compilerOptions": {
"composite": false,
"declaration": true,
"declarationMap": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"inlineSources": false,
"isolatedModules": true,
"moduleResolution": "node",
"noUnusedLocals": false,
"noUnusedParameters": false,
"preserveWatchOutput": true,
"skipLibCheck": true,
"strict": true
},
"include": ["**/*.ts", "**/*.tsx"],
"exclude": ["node_modules", "**/*.cjs"]
}
================================================
FILE: packages/ts-config/nextjs.json
================================================
{
"$schema": "https://json.schemastore.org/tsconfig",
"display": "Next.js",
"compilerOptions": {
"plugins": [
{
"name": "next"
}
],
"allowJs": true,
"declaration": false,
"declarationMap": false,
"incremental": true,
"jsx": "preserve",
"lib": ["dom", "dom.iterable", "esnext"],
"module": "esnext",
"noEmit": true,
"resolveJsonModule": true,
"strict": false,
"target": "es5"
},
"include": ["src", "next-env.d.ts"],
"exclude": ["node_modules"]
}
================================================
FILE: packages/ts-config/package.json
================================================
{
"name": "ts-config",
"version": "0.0.0",
"private": true,
"license": "MIT",
"publishConfig": {
"access": "public"
}
}
================================================
FILE: packages/ts-config/react-library.json
================================================
{
"$schema": "https://json.schemastore.org/tsconfig",
"display": "React Library",
"extends": "./base.json",
"compilerOptions": {
"jsx": "react-jsx",
"lib": ["ES2015", "DOM"],
"module": "ESNext",
"target": "es6"
}
}
================================================
FILE: packages/ui/.eslintrc.js
================================================
module.exports = {
root: true,
extends: ['custom'],
}
================================================
FILE: packages/ui/.gitignore
================================================
.turbo
dist/
================================================
FILE: packages/ui/lib/cva.config.ts
================================================
import { defineConfig } from 'cva'
import { twMerge } from 'tailwind-merge'
export const { cva, cx, compose } = defineConfig({
hooks: {
onComplete: (className) => twMerge(className),
},
})
================================================
FILE: packages/ui/package.json
================================================
{
"name": "ui",
"version": "0.1.0",
"main": "./src/index.tsx",
"types": "./src/index.tsx",
"license": "MIT",
"scripts": {
"lint": "eslint --ignore-path .gitignore '**/*.{js,jsx,cjs,mjs,ts,tsx,json}'",
"typecheck": "tsc --noEmit --pretty"
},
"peerDependencies": {
"react": "^18.2.0"
},
"dependencies": {
"cva": "1.0.0-beta.1",
"lucide-react": "^0.259.0",
"react-aria-components": "1.0.0",
"tailwind-merge": "^1.13.2"
},
"devDependencies": {
"@types/react": "18.0.27",
"eslint": "^8.37.0",
"eslint-config-custom": "workspace:*",
"postcss": "^8.4.21",
"postcss-config": "workspace:*",
"react": "^18.2.0",
"tailwind-config": "workspace:*",
"ts-config": "workspace:*",
"typescript": "^5.1.6"
}
}
================================================
FILE: packages/ui/postcss.config.cjs
================================================
/** @type {import('postcss').Config} */
module.exports = require('postcss-config/postcss.config.cjs')
================================================
FILE: packages/ui/src/breadcrumbs.tsx
================================================
import * as React from 'react'
import * as ReactAria from 'react-aria-components'
import { cx } from '../lib/cva.config'
export const Breadcrumbs = ({
className,
...props
}: ReactAria.BreadcrumbsProps) => {
return (
)
}
export interface BreadcrumbItemProps extends ReactAria.BreadcrumbProps {
separator?: React.ReactNode
}
export const BreadcrumbItem = ({
separator,
children,
...props
}: BreadcrumbItemProps) => {
return (
{children}
{separator ? (
{separator}
) : null}
)
}
export const BreadcrumbLink = ({
children,
className,
...props
}: ReactAria.LinkProps) => {
return (
{children}
)
}
================================================
FILE: packages/ui/src/button.tsx
================================================
import { type VariantProps } from 'cva'
import * as ReactAria from 'react-aria-components'
import { cva, cx } from '../lib/cva.config'
export const buttonVariants = cva({
base: [
'inline-flex items-center justify-center rounded-md font-semibold outline-none transition-colors',
// Focus
'focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 dark:focus:ring-slate-400 dark:focus:ring-offset-slate-900',
// Disabled
'disabled:pointer-events-none disabled:opacity-40',
],
variants: {
variant: {
solid:
'bg-slate-900 text-white open:bg-slate-100 hover:bg-slate-700 dark:bg-slate-50 dark:text-slate-900 dark:open:bg-slate-800 dark:hover:bg-slate-200',
destructive:
'bg-red-600 text-white hover:bg-red-700 dark:hover:bg-red-700',
outline:
'border border-slate-200 bg-transparent hover:bg-slate-100 focus:bg-slate-100 dark:border-slate-700 dark:text-slate-100 dark:hover:bg-slate-700 dark:focus:bg-slate-700',
subtle:
'bg-slate-100 text-slate-900 hover:bg-slate-200 focus:bg-slate-200 dark:bg-slate-700 dark:text-slate-100 dark:hover:bg-slate-700 dark:focus:bg-slate-700',
ghost:
'bg-transparent open:bg-transparent hover:bg-slate-100 focus:bg-slate-100 dark:text-slate-100 dark:open:bg-transparent dark:hover:bg-slate-800 dark:hover:text-slate-100 dark:focus:bg-slate-800 dark:focus:text-slate-100',
link: 'bg-transparent text-slate-900 underline-offset-4 hover:bg-transparent hover:underline focus:bg-transparent focus:underline dark:bg-transparent dark:text-slate-100 dark:hover:bg-transparent dark:focus:bg-transparent',
},
size: {
lg: 'h-12 px-6 text-lg',
md: 'h-10 px-4 text-base',
sm: 'h-8 px-3 text-sm',
xs: 'h-6 px-2 text-xs',
},
},
defaultVariants: {
variant: 'solid',
size: 'md',
},
})
export interface ButtonProps
extends ReactAria.ButtonProps,
VariantProps {
className?: string
}
export const Button = ({ className, variant, size, ...props }: ButtonProps) => {
return (
)
}
================================================
FILE: packages/ui/src/calendar.tsx
================================================
import * as React from 'react'
import * as ReactAria from 'react-aria-components'
import { cx } from '../lib/cva.config'
export const Calendar = ({
className,
...props
}: ReactAria.CalendarProps) => {
return (
)
}
export const RangeCalendar = ({
className,
...props
}: ReactAria.RangeCalendarProps) => {
return (
)
}
export const CalendarGrid = ({
className,
...props
}: ReactAria.CalendarGridProps) => {
return
}
export const CalendarGridHeader = (
props: ReactAria.CalendarGridHeaderProps,
) => {
return
}
export const CalendarHeaderCell = ({
className,
...props
}: ReactAria.CalendarHeaderCellProps) => {
return (
)
}
export const CalendarGridBody = ({
className,
...props
}: ReactAria.CalendarGridBodyProps) => {
return (
)
}
export const CalendarCell = ({
className,
...props
}: ReactAria.CalendarCellProps) => {
return (
)
}
export const CalendarHeading = ({
className,
...props
}: ReactAria.HeadingProps) => {
return (
)
}
export const CalendarHeader = ({
className,
...props
}: React.HTMLAttributes) => {
return (
)
}
export const CalendarFooter = ({
className,
...props
}: React.HTMLAttributes) => {
return
}
export const CalendarNextButton = ({
className,
...props
}: ReactAria.ButtonProps) => {
return
}
export const CalendarPrevButton = ({
className,
...props
}: ReactAria.ButtonProps) => {
return (
)
}
================================================
FILE: packages/ui/src/checkbox-group.tsx
================================================
import * as React from 'react'
import * as ReactAria from 'react-aria-components'
import { cx } from '../lib/cva.config'
interface CheckboxGroupProps extends ReactAria.CheckboxGroupProps {
orientation?: 'horizontal' | 'vertical'
}
export const CheckboxGroup = ({
orientation = 'vertical',
...props
}: CheckboxGroupProps) => {
return (
)
}
export const CheckboxGroupContent = (
props: React.HTMLAttributes,
) => {
return (
)
}
================================================
FILE: packages/ui/src/checkbox.tsx
================================================
import * as React from 'react'
import { type VariantProps } from 'cva'
import * as ReactAria from 'react-aria-components'
import { cva, cx } from '../lib/cva.config'
const checkboxVariants = cva({
base: [
'group flex cursor-pointer items-center gap-2',
// Disabled
'disabled:cursor-not-allowed',
],
variants: {
size: {
lg: 'text-lg',
md: 'text-base',
sm: 'text-sm',
},
},
defaultVariants: {
size: 'md',
},
})
const checkboxInnerVariants = cva({
base: [
'flex items-center justify-center rounded border-2 border-slate-300 text-white transition-colors dark:border-slate-600 dark:text-black',
// Focus
'group-focus:ring-2 group-focus:ring-slate-400 group-focus:ring-offset-2 dark:group-focus:ring-slate-400 dark:group-focus:ring-offset-slate-900',
// Selected
'group-selected:border-black group-selected:bg-black dark:group-selected:border-white dark:group-selected:bg-white',
// Disabled
'group-disabled:border-slate-100 group-disabled:bg-slate-100',
// Selected
'group-indeterminate:border-black group-indeterminate:bg-black dark:group-indeterminate:border-white dark:group-indeterminate:bg-white',
// Selected & Disabled
'group-[[data-selected][data-disabled]]:border-slate-100 group-[[data-selected][data-disabled]]:bg-slate-100 group-[[data-selected][data-disabled]]:text-slate-400',
// Indeterminate & Disabled
'group-[[data-indeterminate][data-disabled]]:border-slate-100 group-[[data-indeterminate][data-disabled]]:bg-slate-100 group-[[data-indeterminate][data-disabled]]:text-slate-400',
],
variants: {
size: {
lg: 'h-5 w-5',
md: 'h-4 w-4',
sm: 'h-3 w-3',
},
},
defaultVariants: {
size: 'md',
},
})
export interface CheckboxProps
extends ReactAria.CheckboxProps,
VariantProps,
VariantProps {
className?: string
children?: React.ReactNode
}
export const Checkbox = ({
className,
size,
children,
...props
}: CheckboxProps) => {
return (
<>
{children}
>
)
}
================================================
FILE: packages/ui/src/combobox.tsx
================================================
import { Check } from 'lucide-react'
import * as ReactAria from 'react-aria-components'
import { cx } from '../lib/cva.config'
import { type ButtonProps } from './button'
import { Input, type InputProps } from './input'
export const ComboBox = ({
className,
...props
}: ReactAria.ComboBoxProps) => {
return (
)
}
export const ComboBoxInput = (props: InputProps) => {
return
}
export interface ComboBoxContentProps
extends Omit,
Omit, 'style'> {
popoverClassName?: string
}
export const ComboBoxContent = ({
className,
popoverClassName,
...props
}: ComboBoxContentProps) => {
return (
)
}
export interface ListBoxItemProps extends ReactAria.ListBoxItemProps {
textValue: string
}
export const ComboBoxItem = ({
className,
children,
...props
}: ListBoxItemProps) => {
return (
<>
{children}
>
)
}
export const ComboBoxButton = ({ className, ...props }: ButtonProps) => {
return
}
================================================
FILE: packages/ui/src/date-input.tsx
================================================
import { type VariantProps } from 'cva'
import * as ReactAria from 'react-aria-components'
import { cva, cx } from '../lib/cva.config'
export const dateInputGroupVariants = cva({
base: [
'inline-flex w-full', // Using .inline-flex here, as opposed to .flex appears to fix this issue https://github.com/adobe/react-spectrum/issues/3164
'w-full items-center border border-slate-300 bg-transparent placeholder:text-slate-400 dark:border-slate-700 dark:text-slate-50',
// Focus
'focus:outline-none',
// Focus-visible
'focus-visible:ring-2 focus-visible:ring-slate-400 focus-visible:ring-offset-2 dark:focus-visible:ring-slate-400 dark:focus-visible:ring-offset-slate-900',
// Disabled
'disabled:cursor-not-allowed disabled:opacity-40',
// Invalid
'invalid:border-red-600 dark:invalid:border-red-400',
],
variants: {
size: {
lg: 'h-12 rounded-lg px-3 py-2 text-lg',
md: 'h-10 rounded-md px-3 py-1 text-base',
sm: 'h-8 rounded px-2 py-1 text-sm',
xs: 'h-6 rounded px-1 py-0.5 text-xs',
},
},
defaultVariants: {
size: 'md',
},
})
export interface DateInputGroupProps
extends ReactAria.GroupProps,
VariantProps {
className?: string
}
export const DateInputGroup = ({
className,
size,
...props
}: DateInputGroupProps) => {
return (
)
}
export const DateField = (
props: ReactAria.DateFieldProps,
) => {
return
}
export const DateInput = ({
className,
...props
}: ReactAria.DateInputProps) => {
return (
)
}
export const DateSegment = ({
className,
...props
}: ReactAria.DateSegmentProps) => {
return (
)
}
================================================
FILE: packages/ui/src/date-picker.tsx
================================================
import * as ReactAria from 'react-aria-components'
import { cx } from '../lib/cva.config'
export const DatePicker = ({
className,
...props
}: ReactAria.DatePickerProps) => {
return
}
export interface DatePickerContentProps
extends Omit,
ReactAria.DialogProps {
className?: string
popoverClassName?: string
}
export const DatePickerContent = ({
popoverClassName,
...props
}: DatePickerContentProps) => {
return (
)
}
export const DatePickerButton = ({
className,
...props
}: ReactAria.ButtonProps) => {
return
}
================================================
FILE: packages/ui/src/date-range-picker.tsx
================================================
import * as ReactAria from 'react-aria-components'
import { cx } from '../lib/cva.config'
export const DateRangePicker = ({
className,
...props
}: ReactAria.DateRangePickerProps) => {
return (
)
}
export interface DateRangePickerContentProps
extends Omit,
ReactAria.DialogProps {
className?: string
popoverClassName?: string
}
export const DateRangePickerContent = ({
popoverClassName,
...props
}: DateRangePickerContentProps) => {
return (
)
}
================================================
FILE: packages/ui/src/grid-list.tsx
================================================
import * as ReactAria from 'react-aria-components'
import { cx } from '../lib/cva.config'
export const GridList = ({
className,
...props
}: ReactAria.GridListProps) => {
return (
)
}
export const GridListItem = (props: ReactAria.GridListItemProps) => {
return
}
================================================
FILE: packages/ui/src/icon-button.tsx
================================================
import { type VariantProps } from 'cva'
import * as ReactAria from 'react-aria-components'
import { cva, cx } from '../lib/cva.config'
export const iconButtonVariants = cva({
base: [
'inline-flex items-center justify-center rounded-md font-semibold outline-none transition-colors',
// Focus
'focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 dark:focus:ring-offset-slate-900',
// Disabled
'disabled:pointer-events-none disabled:opacity-40',
],
variants: {
variant: {
solid: [
// Base
'bg-slate-900 text-white dark:bg-slate-50 dark:text-slate-900',
// Hover
'hover:bg-slate-700 dark:hover:bg-slate-200',
// Focus
'focus:bg-slate-700 dark:focus:bg-slate-200',
// Open
'open:bg-slate-100 dark:open:bg-slate-800',
],
destructive: [
// Base
'bg-red-600 text-white',
// Hover
'hover:bg-red-600 dark:hover:bg-red-600',
// Focus
'focus:bg-red-600 dark:focus:bg-red-600',
// Open
'',
],
outline: [
// Base
'border border-slate-200 bg-transparent dark:border-slate-700 dark:text-slate-100',
// Hover
'hover:bg-slate-100 dark:hover:bg-slate-700',
// Focus
'focus:bg-slate-100 dark:focus:bg-slate-700',
// Open
'',
],
subtle: [
// Base
'bg-slate-100 text-slate-900 dark:bg-slate-700 dark:text-slate-100',
// Hover
'hover:bg-slate-200 dark:hover:bg-slate-600',
// Focus
'focus:bg-slate-200 dark:focus:bg-slate-600',
// Open
'',
],
ghost: [
// Base
'bg-transparent dark:text-slate-100',
// Hover
'hover:bg-slate-100 dark:hover:bg-slate-800 dark:hover:text-slate-100',
// Focus
'focus:bg-slate-100 dark:focus:bg-slate-800 dark:focus:text-slate-100',
// Open
'open:bg-transparent dark:open:bg-transparent',
],
link: [
// Base
'bg-transparent text-slate-900 underline-offset-4 dark:bg-transparent dark:text-slate-100',
// Hover
'hover:bg-transparent hover:underline dark:hover:bg-transparent',
// Focus
'focus:bg-transparent focus:underline dark:focus:bg-transparent',
// Open
'',
],
},
size: {
lg: 'h-12 w-12 rounded-lg text-3xl',
md: 'h-10 w-10 rounded-md text-2xl',
sm: 'h-8 w-8 rounded px-1 text-xl',
xs: 'h-6 w-6 rounded px-1 text-lg',
},
},
defaultVariants: {
variant: 'solid',
size: 'md',
},
})
export interface IconButtonProps
extends ReactAria.ButtonProps,
VariantProps {
className?: string
'aria-label': string
}
export const IconButton = ({
className,
variant,
size,
...props
}: IconButtonProps) => {
return (
)
}
================================================
FILE: packages/ui/src/index.tsx
================================================
import './styles.css'
export * from './breadcrumbs'
export * from './button'
export * from './calendar'
export * from './checkbox'
export * from './checkbox-group'
export * from './combobox'
export * from './date-input'
export * from './date-picker'
export * from './date-range-picker'
export * from './grid-list'
export * from './input'
export * from './icon-button'
export * from './label'
export * from './menu'
export * from './meter'
export * from './modal'
export * from './number-field'
export * from './progress-bar'
export * from './radio'
export * from './radio-group'
export * from './search-field'
export * from './select'
export * from './slider'
export * from './switch'
export * from './tabs'
// export * from "./table";
export * from './text-field'
// export * from "./toggle-button";
export * from './tooltip'
================================================
FILE: packages/ui/src/input.tsx
================================================
import { type VariantProps } from 'cva'
import * as ReactAria from 'react-aria-components'
import { cva, cx } from '../lib/cva.config'
const inputVariants = cva({
base: [
'flex w-full border border-slate-300 bg-transparent placeholder:text-slate-400',
// Focus
'focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2',
// Dark
'dark:border-slate-700 dark:text-slate-50 dark:focus:ring-slate-400 dark:focus:ring-offset-slate-900',
// Disabled
'disabled:cursor-not-allowed disabled:opacity-40',
// Invalid
'invalid:border-red-600 dark:invalid:border-red-400',
],
variants: {
size: {
lg: 'h-12 rounded-lg px-4 text-lg',
md: 'h-10 rounded-md px-4 text-base',
sm: 'h-8 rounded px-3 text-sm',
xs: 'h-6 rounded px-2 text-xs',
},
},
defaultVariants: {
size: 'md',
},
})
export interface InputProps
extends Omit,
VariantProps {
className?: string
}
export const Input = ({ className, size, ...props }: InputProps) => {
return (
)
}
================================================
FILE: packages/ui/src/label.tsx
================================================
import * as ReactAria from 'react-aria-components'
import { cx } from '../lib/cva.config'
export const Label = ({ className, ...props }: ReactAria.LabelProps) => {
return (
)
}
================================================
FILE: packages/ui/src/menu.tsx
================================================
import * as React from 'react'
import { Check, Circle } from 'lucide-react'
import * as ReactAria from 'react-aria-components'
import { cx } from '../lib/cva.config'
export const Menu = (props: ReactAria.MenuTriggerProps) => {
return
}
export interface MenuContentProps
extends Omit,
ReactAria.MenuProps {
className?: string
popoverClassName?: string
}
export const MenuContent = ({
className,
popoverClassName,
...props
}: MenuContentProps) => {
return (
)
}
export const MenuItem = ({
className,
children,
...props
}: ReactAria.MenuItemProps) => {
return (
{({ selectionMode }) => (
<>
{selectionMode === 'single' ? (
) : selectionMode === 'multiple' ? (
) : null}
{children}
>
)}
)
}
export const MenuSection = (
props: ReactAria.SectionProps,
) => {
return
}
export const MenuHeader = ({
className,
...props
}: React.HTMLAttributes) => {
return (
)
}
export const MenuSeparator = ({
className,
...props
}: ReactAria.SeparatorProps) => {
return (
)
}
================================================
FILE: packages/ui/src/meter.tsx
================================================
import * as React from 'react'
import * as ReactAria from 'react-aria-components'
import { cx } from '../lib/cva.config'
export const Meter = ({ className, ...props }: ReactAria.MeterProps) => {
return
}
export const MeterTrack = ({
className,
children,
...props
}: React.HTMLAttributes) => {
return (
{children}
)
}
export interface MeterFilledTrackProps
extends React.HTMLAttributes {
percentage: number
}
export const MeterFilledTrack = ({
className,
percentage,
...props
}: MeterFilledTrackProps) => {
return (
)
}
================================================
FILE: packages/ui/src/modal.tsx
================================================
import * as React from 'react'
import { type VariantProps } from 'cva'
import * as ReactAria from 'react-aria-components'
import { cva, cx } from '../lib/cva.config'
export const modalVariants = cva({
base: [
'mx-auto w-full flex-col rounded bg-white outline-none dark:bg-slate-800',
// Entering
'entering:animate-in entering:zoom-in-95',
// Exiting
'exiting:animate-in exiting:zoom-in-95 exiting:direction-reverse',
],
variants: {
size: {
full: 'h-full',
xl: 'my-16 max-w-[36rem]',
lg: 'my-16 max-w-[32rem]',
md: 'my-16 max-w-[28rem]',
sm: 'my-16 max-w-[24rem]',
xs: 'my-16 max-w-[20rem]',
},
},
defaultVariants: {
size: 'md',
},
})
export const Modal = (props: ReactAria.DialogTriggerProps) => {
return
}
export interface ModalContentProps
extends ReactAria.ModalOverlayProps,
VariantProps {
className?: string
children?: React.ReactNode
}
export const ModalContent = ({
className,
size,
children,
...props
}: ModalContentProps) => {
return (
{children}
)
}
export const ModalOverlay = ({
isDismissable = true,
className,
...props
}: ReactAria.ModalOverlayProps) => {
return (
)
}
export const ModalHeader = ({
className,
...props
}: React.HTMLAttributes) => {
return (
)
}
export const ModalBody = ({
className,
...props
}: React.HTMLAttributes) => {
return
}
export const ModalFooter = ({
className,
...props
}: React.HTMLAttributes) => {
return
}
================================================
FILE: packages/ui/src/number-field.tsx
================================================
import * as React from 'react'
import * as ReactAria from 'react-aria-components'
import { cx } from '../lib/cva.config'
import { type ButtonProps } from './button'
import { Input, type InputProps } from './input'
export const NumberField = ({
className,
...props
}: ReactAria.NumberFieldProps) => {
return (
)
}
export const NumberInputGroup = (props: ReactAria.GroupProps) => {
return
}
export const NumberInputStepper = ({
className,
...props
}: React.HTMLAttributes) => {
return (
)
}
export const NumberInput = ({ className, ...props }: InputProps) => {
return
}
export const NumberIncrementStepper = ({
className,
...props
}: ButtonProps) => {
return (
)
}
export const NumberDecrementStepper = ({
className,
...props
}: ButtonProps) => {
return (
)
}
================================================
FILE: packages/ui/src/progress-bar.tsx
================================================
import * as React from 'react'
import * as ReactAria from 'react-aria-components'
import { cx } from '../lib/cva.config'
export const ProgressBar = ({
className,
...props
}: ReactAria.ProgressBarProps) => {
return (
)
}
export const ProgressBarTrack = ({
className,
children,
...props
}: React.HTMLAttributes) => {
return (
{children}
)
}
export interface ProgressBarFilledTrackProps
extends React.HTMLAttributes {
percentage?: number
}
export const ProgressBarFilledTrack = ({
className,
percentage = 0,
...props
}: ProgressBarFilledTrackProps) => {
return (
)
}
================================================
FILE: packages/ui/src/radio-group.tsx
================================================
import * as React from 'react'
import * as ReactAria from 'react-aria-components'
import { cx } from '../lib/cva.config'
export const RadioGroup = (props: ReactAria.RadioGroupProps) => {
return
}
export const RadioGroupContent = (
props: React.HTMLAttributes,
) => {
return (
)
}
================================================
FILE: packages/ui/src/radio.tsx
================================================
import * as React from 'react'
import { type VariantProps } from 'cva'
import * as ReactAria from 'react-aria-components'
import { cva, cx } from '../lib/cva.config'
const radioVariants = cva({
base: [
'group flex cursor-pointer flex-row items-center gap-2',
// Disabled
'disabled:cursor-not-allowed',
],
variants: {
size: {
lg: 'text-lg',
md: 'text-base',
sm: 'text-sm',
},
},
defaultVariants: {
size: 'md',
},
})
const radioInnerVariants = cva({
base: [
'flex items-center justify-center rounded-full border-2 border-slate-300 transition-colors dark:border-slate-600',
// ::before
'before:block before:h-1/2 before:w-1/2 before:scale-0 before:rounded-full before:bg-white before:transition-transform before:duration-300 before:content-[""] dark:before:bg-black',
// Selected
'group-selected:border-black group-selected:bg-black group-selected:text-white dark:group-selected:border-white dark:group-selected:bg-white dark:group-selected:text-black',
// ::before Selected
'group-selected:before:scale-100',
// Focus
'group-focus:ring-2 group-focus:ring-slate-400 group-focus:ring-offset-2 dark:group-focus:ring-slate-400 dark:group-focus:ring-offset-slate-900',
// Disabled
'group-disabled:border-slate-100',
// Selected & Disabled
'group-[[data-selected][data-disabled]]:border-slate-100 group-[[data-selected][data-disabled]]:bg-slate-100 group-[[data-selected][data-disabled]]:before:bg-slate-400',
],
variants: {
size: {
lg: 'h-5 w-5',
md: 'h-4 w-4',
sm: 'h-3 w-3',
},
},
defaultVariants: {
size: 'md',
},
})
export interface RadioProps
extends ReactAria.RadioProps,
VariantProps,
VariantProps {
className?: string
children?: React.ReactNode
}
export const Radio = ({ className, size, children, ...props }: RadioProps) => {
return (
<>
{children}
>
)
}
================================================
FILE: packages/ui/src/search-field.tsx
================================================
import { X } from 'lucide-react'
import * as ReactAria from 'react-aria-components'
import { cx } from '../lib/cva.config'
import { IconButton, type IconButtonProps } from './icon-button'
import { Input, type InputProps } from './input'
export const SearchField = ({
className,
...props
}: ReactAria.SearchFieldProps) => {
return (
)
}
export const SearchFieldInput = ({ className, ...props }: InputProps) => {
return (
)
}
export const SearchFieldClearButton = ({
className,
...props
}: IconButtonProps) => {
return (
)
}
================================================
FILE: packages/ui/src/select.tsx
================================================
import { Check } from 'lucide-react'
import * as ReactAria from 'react-aria-components'
import { cx } from '../lib/cva.config'
import { Button, type ButtonProps } from './button'
export const Select = ({
className,
...props
}: ReactAria.SelectProps) => {
return
}
export interface SelectContentProps
extends Omit,
Omit, 'style'> {
className?: string
popoverClassName?: string
}
export const SelectContent = ({
className,
popoverClassName,
...props
}: SelectContentProps) => {
return (
)
}
export const SelectItem = ({
className,
children,
...props
}: ReactAria.ListBoxItemProps) => {
return (
<>
{children}
>
)
}
export const SelectValue = (
props: ReactAria.SelectValueProps,
) => {
return
}
export const SelectButton = ({ className, ...props }: ButtonProps) => {
return (
)
}
================================================
FILE: packages/ui/src/slider.tsx
================================================
import * as React from 'react'
import * as ReactAria from 'react-aria-components'
import { cx } from '../lib/cva.config'
export const Slider = ({ className, ...props }: ReactAria.SliderProps) => {
return (
)
}
export const SliderOutput = (props: ReactAria.SliderOutputProps) => {
return
}
export const SliderTrack = ({
className,
...props
}: ReactAria.SliderTrackProps) => {
return (
)
}
export interface SliderFilledTrackProps
extends React.HTMLAttributes {
percentage?: number
orientation: 'horizontal' | 'vertical'
className?: string
}
export const SliderFilledTrack = ({
percentage = 0,
orientation,
className,
...props
}: SliderFilledTrackProps) => {
const dir = orientation === 'vertical' ? 'height' : 'width'
return (
)
}
export const SliderThumb = ({
className,
...props
}: ReactAria.SliderThumbProps) => {
return (
)
}
================================================
FILE: packages/ui/src/styles.css
================================================
@tailwind base;
@tailwind components;
@tailwind utilities;
================================================
FILE: packages/ui/src/switch.tsx
================================================
import * as React from 'react'
import { type VariantProps } from 'cva'
import * as ReactAria from 'react-aria-components'
import { cva, cx } from '../lib/cva.config'
const switchIndicatorVariants = cva({
base: [
'cursor-pointer rounded-full bg-gray-300 p-0.5 transition-colors dark:bg-slate-700',
// Focus
'group-focus:ring-2 group-focus:ring-slate-400 group-focus:ring-offset-2 dark:group-focus:ring-slate-400 dark:group-focus:ring-offset-slate-900',
// Selected
'group-selected:bg-black dark:group-selected:bg-slate-400',
// Disabled
'group-disabled:cursor-not-allowed group-disabled:opacity-40',
],
variants: {
size: {
lg: 'h-7 w-12',
md: 'h-5 w-8',
sm: 'h-4 w-6',
},
},
defaultVariants: {
size: 'md',
},
})
const switchIndicatorInnerVariants = cva({
base: 'rounded-full bg-white transition-transform',
variants: {
size: {
lg: 'h-6 w-6 group-selected:translate-x-5',
md: 'h-4 w-4 group-selected:translate-x-3',
sm: 'h-3 w-3 group-selected:translate-x-2',
},
},
defaultVariants: {
size: 'md',
},
})
export interface SwitchProps
extends ReactAria.SwitchProps,
VariantProps {
className?: string
}
export const Switch = ({ children, className, ...props }: SwitchProps) => {
return (
{children}
)
}
export interface SwitchIndicatorProps
extends React.HTMLAttributes,
VariantProps {
className?: string
}
export const SwitchIndicator = ({
size,
className,
...props
}: SwitchIndicatorProps) => {
return (
)
}
================================================
FILE: packages/ui/src/table.tsx
================================================
// TODO: Table
export const Table = () => {
return
}
================================================
FILE: packages/ui/src/tabs.tsx
================================================
import * as ReactAria from 'react-aria-components'
import { cx } from '../lib/cva.config'
export const Tabs = ({ className, ...props }: ReactAria.TabsProps) => {
return (
)
}
export const TabList = ({
className,
...props
}: ReactAria.TabListProps) => {
return (
)
}
export const Tab = ({ className, ...props }: ReactAria.TabProps) => {
return (
)
}
export const TabPanel = ({ className, ...props }: ReactAria.TabPanelProps) => {
return
}
================================================
FILE: packages/ui/src/text-field.tsx
================================================
import * as ReactAria from 'react-aria-components'
import { cx } from '../lib/cva.config'
export const TextField = ({
className,
...props
}: ReactAria.TextFieldProps) => {
return
}
export const TextFieldDescription = ({
className,
...props
}: ReactAria.TextProps) => {
return (
)
}
export const TextFieldErrorMessage = ({
className,
...props
}: ReactAria.TextProps) => {
return (
)
}
================================================
FILE: packages/ui/src/toggle-button.tsx
================================================
// TODO: ToggleButton
export const ToggleButton = () => {
return
}
================================================
FILE: packages/ui/src/tooltip.tsx
================================================
import * as ReactAria from 'react-aria-components'
import { cx } from '../lib/cva.config'
export const Tooltip = (props: ReactAria.TooltipTriggerComponentProps) => {
return
}
export const TooltipContent = ({
className,
children,
...props
}: ReactAria.TooltipProps) => {
return (
{children}
)
}
================================================
FILE: packages/ui/tailwind.config.cjs
================================================
/** @type {import('tailwindcss').Config} */
// eslint-disable-next-line @typescript-eslint/no-var-requires
const sharedConfig = require('tailwind-config/tailwind.config.cjs')
module.exports = {
presets: [sharedConfig],
content: ['./src/**/*.{js,ts,jsx,tsx,mdx}'],
}
================================================
FILE: packages/ui/tsconfig.json
================================================
{
"extends": "ts-config/react-library.json",
"include": ["."],
"exclude": ["dist", "build", "node_modules"],
}
================================================
FILE: pnpm-workspace.yaml
================================================
packages:
- "apps/*"
- "packages/*"
- "scripts"
================================================
FILE: prettier.config.cjs
================================================
/** @type {import('prettier').Config} */
module.exports = {
semi: false,
singleQuote: true,
//
importOrder: [
'^react$',
'',
'',
'^[@/]',
'^[./]',
'',
'^(?!.*[.]css$)[./].*$',
'.css$',
],
importOrderBuiltinModulesToTop: true,
importOrderCaseInsensitive: true,
importOrderParserPlugins: ['typescript', 'jsx', 'decorators-legacy'],
importOrderMergeDuplicateImports: true,
importOrderCombineTypeAndValueImports: true,
importOrderSeparation: true,
importOrderSortSpecifiers: true,
plugins: ['@ianvs/prettier-plugin-sort-imports'],
}
================================================
FILE: scripts/package.json
================================================
{
"name": "scripts",
"version": "1.0.0",
"type": "module",
"private": true,
"scripts": {
"migrate-stories": "ts-node src/migrate.ts",
"build-registry": "ts-node src/build-registry.ts"
},
"dependencies": {
"find-up": "6.3.0",
"fs-extra": "11.1.1",
"ts-morph": "19.0.0",
"ts-pattern": "5.0.3"
},
"devDependencies": {
"@types/fs-extra": "11.0.1",
"@types/node": "18.16.19",
"@types/prettier": "2.7.3",
"ts-node": "10.9.1",
"typescript": "5.1.6",
"ts-config": "workspace:*"
}
}
================================================
FILE: scripts/src/build-registry.ts
================================================
import { dirname, join } from 'path'
import { findUpSync } from 'find-up'
import { outputFile, remove } from 'fs-extra'
import { Project } from 'ts-morph'
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const root = dirname(findUpSync('pnpm-lock.yaml')!)
process.chdir(join(root, 'apps', 'storybook'))
const project = new Project({})
project.addSourceFilesAtPaths('src/**/stories/*.tsx')
const sourceFiles = project.getSourceFiles()
// ----------------------------------------------------------------------------
// Build __registry__/index.tsx.
// ----------------------------------------------------------------------------
type ComponentRegistry = Record<
string,
Record<
string,
{
name: string
story: string
component: string
file: string
}
>
>
function generateNested(registry: ComponentRegistry): string {
let nestedStory = '{\n'
for (const component in registry) {
nestedStory += ` "${component}": {\n`
for (const story in registry[component]) {
const entry = registry[component][story]
nestedStory += ` "${story}": {\n`
nestedStory += ` name: "${entry.name}",\n`
nestedStory += ` story: "${entry.story}",\n`
nestedStory += ` component: ${entry.component},\n`
nestedStory += ` file: "${entry.file}"\n`
nestedStory += ' },\n'
}
nestedStory += ' },\n'
}
nestedStory += '}'
return nestedStory
}
const buildRegistryIndex = async () => {
const registry: ComponentRegistry = {}
sourceFiles.forEach((file) => {
const component = file.getDirectory().getParent()?.getBaseName()
if (!component) {
console.log('Component not found')
return
}
const story = file.getBaseNameWithoutExtension()
if (!story) {
console.log(`Story not found for: ${component}`)
return
}
if (!registry[component]) {
registry[component] = {}
}
registry[component][story] = {
name: `${component}-${story}`,
story,
component: `React.lazy(() => import("@/registry/${component}/${story}"))`,
file: `registry/${component}/${story}.tsx`,
}
})
const index = `/* eslint-disable prettier/prettier */
// @ts-nocheck
// This file is autogenerated by scripts/build-registry.ts
// Do not edit this file directly.
import * as React from "react"
type ComponentRegistry = Record<
string,
Record<
string,
{
name: string
story: string
component: string
file: string
}
>
>
export const Index: ComponentRegistry = ${generateNested(registry)};
`
const outPath = join(root, 'apps', 'docs', '__registry__', 'index.tsx')
await remove(outPath)
await outputFile(outPath, index)
}
// ----------------------------------------------------------------------------
// Build registry/[component]/[variant].tsx.
// ----------------------------------------------------------------------------
const copyDemoComponents = async () => {
const registryDir = join(root, 'apps', 'docs', 'registry')
await remove(registryDir)
await Promise.all(
sourceFiles.map(async (file) => {
const component = file.getDirectory().getParent()?.getBaseName()
if (!component) {
console.log('Component baseName not found')
return
}
const variant = file.getBaseNameWithoutExtension()
if (!variant) {
console.log(`Variant not found for: ${component}`)
return
}
let fileContents = file.getText()
fileContents = fileContents.replace(/\n$/, '').replace('../lib', '@/lib')
const newPath = join(registryDir, `${component}`, `${variant}.tsx`)
await outputFile(newPath, fileContents)
}),
)
}
// ----------------------------------------------------------------------------
// Run
// ----------------------------------------------------------------------------
buildRegistryIndex().catch((err) => {
console.error(err.message)
process.exit(1)
})
copyDemoComponents().catch((err) => {
console.error(err.message)
process.exit(1)
})
console.log('✅ Done!')
================================================
FILE: scripts/tsconfig.json
================================================
{
"extends": "ts-config/base.json",
"compilerOptions": {
"target": "esnext",
"module": "esnext",
},
"ts-node": {
"esm": true
},
"include": [
"src"
]
}
================================================
FILE: tsconfig.json
================================================
{
"extends": "ts-config/base.json",
"include": ["."],
"exclude": ["node_modules"],
"compilerOptions": {
"jsx": "react-jsx",
},
}
================================================
FILE: turbo.json
================================================
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": ["**/.env.*local"],
"pipeline": {
"build": {
"outputs": ["storybook-static/**", ".next/**", "!.next/cache/**"]
},
"build-registry": {
"cache": false
},
"dev": {
"cache": false,
"persistent": true
},
"lint": {},
"storybook": {},
"topo": {
"dependsOn": ["^topo"]
},
"typecheck": {
"dependsOn": ["topo"]
}
}
}
================================================
FILE: vercel.json
================================================
{
"github": {
"silent": true
}
}