Compare commits
313 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9a2cf47a60 | |||
| 59fcfa0c3d | |||
| 578cf5aed3 | |||
| 384cf117f4 | |||
| 5194a02d82 | |||
| a3bf209171 | |||
| 31bfaebe2c | |||
| fa26ce02fd | |||
| 39bc460353 | |||
| 6bbd234aa2 | |||
| 20ba81948d | |||
| 07d722de59 | |||
| f24a97311a | |||
| bf68c91b9a | |||
| 8048dcc0c3 | |||
| 3b14c21e02 | |||
| 540aa2124b | |||
| bc759838cd | |||
| 06ecd213cf | |||
| 0108d06d3b | |||
| a2133009f7 | |||
| c8f792ccd7 | |||
| 7df2c71ecc | |||
| 1109baea6a | |||
| ca8554ccff | |||
| 77e85e665c | |||
| 5329de42d2 | |||
| 9d63259eea | |||
| f5bf12aedf | |||
| 8c5ae9fe0b | |||
| b51cd52cd5 | |||
| cf912f5f4b | |||
| 3312d4e261 | |||
| 6a9ef07388 | |||
| 8988981c53 | |||
| ad08010fe5 | |||
| 81eeea5093 | |||
| 3b0916d431 | |||
| 0f071ec1f1 | |||
| 7af584a4ba | |||
| 67553d270a | |||
| 422770cc6b | |||
| e34c5e307d | |||
| 5f735a9892 | |||
| 10497f1a97 | |||
| 633f9d208b | |||
| 76f4dd8e76 | |||
| dc245874ce | |||
| 73d9695994 | |||
| ec6fd12d7c | |||
| cc1d62a0a9 | |||
| f28b1e9db6 | |||
| c7bdd0665d | |||
| 668d21ac5d | |||
| 935dff50d1 | |||
| 9acf3eada0 | |||
| 3748abe830 | |||
| 9b21f22a9a | |||
| 72a950ab1b | |||
| 480a241005 | |||
| 91127f88a2 | |||
| e3afa4789a | |||
| f8b917a208 | |||
| 648edad77e | |||
| 5f5ad7a1dd | |||
| 2a2a0692ca | |||
| c83ff382b9 | |||
| 6aacde2594 | |||
| add893c33f | |||
| aba1716edc | |||
| f07de03ad1 | |||
| 1a033fe6f8 | |||
| b8e6b2fe25 | |||
| 765c04e2a5 | |||
| e9fef9bd92 | |||
| 1ba2d029a2 | |||
| dd39db2b5c | |||
| 308b32c0f1 | |||
| 3552353203 | |||
| 1cc5215ce6 | |||
| 10b0e476bb | |||
| 94f85e29c6 | |||
| 1dacf52f30 | |||
| 25484f8663 | |||
| 6b96ea9c9c | |||
| f00ab630bf | |||
| d532a9da7a | |||
| 31934017a5 | |||
| 10f2b8f42b | |||
| f0831ad12c | |||
| 4cad622460 | |||
| ebaa1de876 | |||
| 51095b1628 | |||
| a2b8421441 | |||
| eccc2f2f3d | |||
| 2bc1d1a58b | |||
| 183a4a6cfd | |||
| a644544dfb | |||
| 9e5dd8ce37 | |||
| 69f713cb05 | |||
| d820d9397a | |||
| f89356691c | |||
| 6b56e43a35 | |||
| 4ca6e2c1f6 | |||
| 2126ea1863 | |||
| cadbb30cfb | |||
| f728c0542c | |||
| 74eda31288 | |||
| 158c2aa004 | |||
| ccd8f730e1 | |||
| 098d7884cf | |||
| 60c61aaf0c | |||
| fdbfa1f299 | |||
| ed1dbbc172 | |||
| 9cdc262619 | |||
| c0d0a57338 | |||
| 651c8b55af | |||
| 3a22505185 | |||
| dc0bcf13a5 | |||
| aab1f19a96 | |||
| 9873c6fb83 | |||
| af877f7ddb | |||
| 25640e42ff | |||
| abf532b548 | |||
| e458432550 | |||
| c2ef4c0935 | |||
| a1c112431f | |||
| 4dd2605ac2 | |||
| dab0534095 | |||
| 12bf410807 | |||
| 09504cd702 | |||
| 86a78c9b91 | |||
| feab3e6c1d | |||
| eb11a7731a | |||
| 2829d4afae | |||
| ebab10804c | |||
| 6da144f459 | |||
| 6811ce52c3 | |||
| 2e49e08315 | |||
| a60c2d7b22 | |||
| c5049edfde | |||
| 410e30c720 | |||
| 82a9fd9bc4 | |||
| ef6ea6c1ff | |||
| c188e4d628 | |||
| a597c82a9c | |||
| 0359bf3084 | |||
| eb51bf2261 | |||
| b14067bb94 | |||
| 8761168d34 | |||
| cf0d4e471e | |||
| 2aa476455f | |||
| ee7a819147 | |||
| fb0625470f | |||
| 2b9f89023a | |||
| 26a3a7d2c6 | |||
| 888d861841 | |||
| 0973cd9bff | |||
| e67b3af223 | |||
| 52dafd08f1 | |||
| ceeb52363d | |||
| 0d9ea4925b | |||
| ea48ac2d08 | |||
| b0ee8e3fc2 | |||
| 5d84634c0a | |||
| 9496ab57c5 | |||
| 5ea479f6c4 | |||
| cda881799c | |||
| 707a61de4c | |||
| 5a8768a4b2 | |||
| a25292215a | |||
| a05aef0acb | |||
| 2894aecca1 | |||
| a702bb869d | |||
| 0443ec1c87 | |||
| 7263daca08 | |||
| e4b3c7d1a1 | |||
| a686262009 | |||
| f373400102 | |||
| 2c3be59dfb | |||
| 798e84e729 | |||
| 139b28437b | |||
| f2205642f4 | |||
| 0a10de8d08 | |||
| bae544d198 | |||
| feafb36fcc | |||
| 65f48135d6 | |||
| 1d32bf8f1e | |||
| 0a9982d3ef | |||
| f864dc3974 | |||
| 3a0f2c5610 | |||
| a030680e6a | |||
| e12f3faa81 | |||
| 2dc5adb6e0 | |||
| f63b20c59d | |||
| 0df5099a62 | |||
| cdc4981508 | |||
| acee3c2fda | |||
| fa72bb45cc | |||
| 265c121d65 | |||
| d6b42f757a | |||
| d8b0ef528b | |||
| e83595e06f | |||
| 141887d38c | |||
| a235e324b9 | |||
| 9b27da544e | |||
| 5528ccd042 | |||
| 4957f56e86 | |||
| fa27bccb37 | |||
| 0cc8c79c53 | |||
| 5be104f72d | |||
| dc289fb59b | |||
| 0ce10269c2 | |||
| cd3b80974c | |||
| dc91ab8bc9 | |||
| dec7d411b5 | |||
| 37bed2368e | |||
| 755a835fa7 | |||
| 6f27b048d0 | |||
| dc9362f959 | |||
| e6f36281cb | |||
| f75174d07e | |||
| ac0e564b34 | |||
| 60e453d265 | |||
| 6e5ba692db | |||
| e4ba43b4e3 | |||
| 995b37e2eb | |||
| 3be8c621a1 | |||
| 0b5cde7bc4 | |||
| a4cab30df3 | |||
| 9ff61b5d54 | |||
| 7a3015c497 | |||
| 2106d987f2 | |||
| 2934a90d41 | |||
| 39714775c7 | |||
| 07372eaa65 | |||
| dedd42c288 | |||
| f28e93d940 | |||
| 0ad6e033f8 | |||
| fb0707087c | |||
| d953a22b99 | |||
| 077c19506f | |||
| 6ca97c0bd6 | |||
| 5c8f21a106 | |||
| decf11d270 | |||
| fff248a30b | |||
| 4996d9fb36 | |||
| a5d7fcff0e | |||
| e8f5699fe2 | |||
| a343e8e2ce | |||
| a95e06acf2 | |||
| 22f2665b00 | |||
| 921746f284 | |||
| 132efbcb6d | |||
| bea1e1fde8 | |||
| 95ffa64d41 | |||
| e551226ff2 | |||
| f3d0d6dded | |||
| 1c77b33e82 | |||
| 0d7a19693b | |||
| 01aedcf09b | |||
| 436ba1cd84 | |||
| 0ba165f0fd | |||
| 088af8b6df | |||
| 6951e262e5 | |||
| 51c54f118f | |||
| 11fc733b36 | |||
| a8dc5b429a | |||
| 12cebcfdac | |||
| 0fb2160899 | |||
| 09380c5b06 | |||
| 0f5cce5d62 | |||
| 64c89bc57d | |||
| 467383b8a2 | |||
| aab1dbf77c | |||
| 36775dceee | |||
| 8628ba04a7 | |||
| a978687b07 | |||
| 57cafa06d0 | |||
| d2bd008ba3 | |||
| 2f87b8d644 | |||
| 6ecdc278ab | |||
| 9189b3fbf2 | |||
| 1149345cae | |||
| 135cc21e6f | |||
| 7f6218b978 | |||
| 0eb9f1e3fe | |||
| 11ef4365da | |||
| 85a820eeff | |||
| 6a6d426b10 | |||
| a3cbb5997b | |||
| a5cc42bf80 | |||
| e728a8967b | |||
| acba2cf8f5 | |||
| 049db57bb2 | |||
| 5296d9b84d | |||
| 1619a35cf9 | |||
| 8106629dab | |||
| d55f52cc35 | |||
| 78198c32be | |||
| 71807e8831 | |||
| 9af6174dcf | |||
| 1a129db6fe | |||
| 44ed1056e7 | |||
| db0e26a003 | |||
| 38af48faf5 | |||
| 5b60840751 | |||
| 4c56839db0 | |||
| ef61a3fe09 | |||
| bc4927c901 | |||
| 4191feab2a | |||
| fe8647bd1d | |||
| 23992572ea |
@@ -8,8 +8,11 @@
|
||||
"fixed": [],
|
||||
"linked": [],
|
||||
"access": "public",
|
||||
"baseBranch": "main",
|
||||
"baseBranch": "canary",
|
||||
"updateInternalDependencies": "patch",
|
||||
"___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": {
|
||||
"onlyUpdatePeerDependentsWhenOutOfRange": true
|
||||
},
|
||||
"ignore": [
|
||||
"@nextui-org/docs",
|
||||
"@nextui-org/storybook"
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
language: "en"
|
||||
early_access: false
|
||||
reviews:
|
||||
high_level_summary: true
|
||||
poem: false
|
||||
review_status: true
|
||||
collapse_walkthrough: false
|
||||
auto_review:
|
||||
enabled: true
|
||||
ignore_title_keywords:
|
||||
- "WIP"
|
||||
- "DO NOT MERGE"
|
||||
- 'ci(changesets)'
|
||||
drafts: false
|
||||
base_branches:
|
||||
- "main"
|
||||
- "canary"
|
||||
- "fix/.*"
|
||||
- "chore/.*"
|
||||
- "feat/.*"
|
||||
chat:
|
||||
auto_reply: true
|
||||
+1
-1
@@ -3,7 +3,7 @@ const conventional = require("@commitlint/config-conventional");
|
||||
module.exports = {
|
||||
extends: ["@commitlint/config-conventional"],
|
||||
plugins: ["commitlint-plugin-function-rules"],
|
||||
helpUrl: "https://github.com/nextui-org/nextui/blob/main/CONTRIBUTING.MD#commit-convention",
|
||||
helpUrl: "https://github.com/nextui-org/nextui/blob/main/CONTRIBUTING.md#commit-convention",
|
||||
rules: {
|
||||
...conventional.rules,
|
||||
"type-enum": [
|
||||
|
||||
@@ -3,7 +3,7 @@ name: QA
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- canary
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
@@ -6,7 +6,7 @@ on:
|
||||
- ".changeset/**"
|
||||
- "packages/**"
|
||||
branches:
|
||||
- main
|
||||
- canary
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
@@ -41,12 +41,21 @@ jobs:
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Create @dev release
|
||||
- name: Sync files from canary to main branch if a publish happens
|
||||
if: steps.changesets.outputs.published == 'true'
|
||||
run: |
|
||||
curl -X POST \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
|
||||
https://api.github.com/repos/owner/repo/dispatches \
|
||||
-d '{"event_type":"sync-canary-to-main"}'
|
||||
|
||||
- name: Create canary release
|
||||
if: steps.changesets.outputs.published != 'true'
|
||||
run: |
|
||||
git checkout main
|
||||
pnpm version:dev
|
||||
pnpm release:dev
|
||||
git checkout canary
|
||||
pnpm version:canary
|
||||
pnpm release:canary
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
name: Sync Canary to Main
|
||||
|
||||
on:
|
||||
# triggered manually in Github
|
||||
workflow_dispatch:
|
||||
# triggered by the type "sync-canary-to-main" (e.g. from release action after publishing)
|
||||
repository_dispatch:
|
||||
types: [sync-canary-to-main]
|
||||
|
||||
jobs:
|
||||
create_pull_request:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0 # Ensure all history is fetched
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: repo-sync/pull-request@v2
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
source_branch: "canary"
|
||||
destination_branch: "main"
|
||||
pr_title: "Auto-sync Canary to Main"
|
||||
pr_body: |
|
||||
## Automated: Sync from Canary to Main
|
||||
|
||||
This Pull Request is automatically generated to sync the changes from the Canary branch to the Main branch. Below are the included updates:
|
||||
|
||||
### Triggered by a Direct Push to Canary:
|
||||
- Please check the recent commits on the Canary branch directly as this sync may include multiple changes.
|
||||
|
||||
### Triggered by a Pull Request Merge:
|
||||
- Merged Pull Request: [PR#${{ github.event.pull_request.number }}](${{ github.event.pull_request.html_url }}) - ${{ github.event.pull_request.title }}
|
||||
- PR Description: ${{ github.event.pull_request.body }}
|
||||
- Merged by: ${{ github.event.pull_request.merged_by.login }}
|
||||
|
||||
### Action Required:
|
||||
- Please review the changes carefully.
|
||||
- Approve and merge the Pull Request if everything is in order.
|
||||
|
||||
Thank you for maintaining the Main branch updated and clean.
|
||||
Vendored
+1
-1
@@ -2,6 +2,6 @@
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"editor.formatOnSave": false,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true
|
||||
"source.fixAll.eslint": "explicit"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ https://www.conventionalcommits.org/ or check out the
|
||||
|
||||
## Pull Request Guidelines
|
||||
|
||||
- The `main` branch is basically a snapshot of the latest stable version. All development must be done in dedicated branches.
|
||||
- The `main` branch is basically a snapshot of the latest production version. All development must be done in dedicated branches and will be merged to `canary` branch.
|
||||
- Make sure that Github Actions are green
|
||||
- It is good to have multiple small commits while working on the PR. We'll let GitHub squash it automatically before the merge.
|
||||
- If you add a new feature:
|
||||
@@ -73,14 +73,14 @@ https://www.conventionalcommits.org/ or check out the
|
||||
|
||||
1. Fork of the nextui repository and clone your fork
|
||||
|
||||
2. Create a new branch out of the `main` branch. We follow the convention
|
||||
2. Create a new branch out of the `canary` branch. We follow the convention
|
||||
`[type/scope]`. For example `fix/dropdown-hook` or `docs/menu-typo`. `type`
|
||||
can be either `docs`, `fix`, `feat`, `build`, or any other conventional
|
||||
commit type. `scope` is just a short id that describes the scope of work.
|
||||
|
||||
3. Make and commit your changes following the
|
||||
[commit convention](https://github.com/nextui-org/nextui/blob/main/CONTRIBUTING.md#commit-convention).
|
||||
As you develop, you can run `pnpm build --filter=<module>` and
|
||||
As you canary, you can run `pnpm build --filter=<module>` and
|
||||
`pnpm test packages/<module>/<pkg>` e.g. `pnpm build --filter=avatar & pnpm test packages/components/avatar` to make sure everything works as expected.
|
||||
|
||||
> To know more about the `--filter` option, please check the turborepo [docs](https://turborepo.org/docs/core-concepts/filtering).
|
||||
@@ -118,12 +118,13 @@ We use [Turbo Repo](https://turborepo.org/) for the project management.
|
||||
|
||||
```bash
|
||||
## Start the dev babel server of NextUI core components
|
||||
pnpm dev:nextui
|
||||
pnpm dev
|
||||
|
||||
## optional
|
||||
pnpm dev:docs ## this will start the documentation next.js server and it will automatically detect the changes in the components.
|
||||
|
||||
pnpm start:sb ## this will start the storybook server for a faster development and testing.
|
||||
pnpm sb ## this will start the storybook server for a faster development and testing.
|
||||
|
||||
pnpm dev:docs ## this will start the documentation next.js server and it will automatically detect the changes in the components.
|
||||
```
|
||||
|
||||
- If you will be working just on the documentation source code / mdx, you can use the following commands to build
|
||||
@@ -131,7 +132,7 @@ pnpm start:sb ## this will start the storybook server for a faster development a
|
||||
|
||||
```bash
|
||||
## Build NextUI source components
|
||||
pnpm build:nextui
|
||||
pnpm build
|
||||
|
||||
## Start the next.js documentation dev server
|
||||
pnpm dev:docs
|
||||
@@ -141,10 +142,6 @@ pnpm dev:docs
|
||||
|
||||
```bash
|
||||
pnpm sb
|
||||
|
||||
#or
|
||||
|
||||
pnpm start:sb
|
||||
```
|
||||
|
||||
Remember that these commands must be executed in the root folder of the project.
|
||||
@@ -166,29 +163,46 @@ git checkout -b fix/something
|
||||
All commits that fix bugs or add features need a test.
|
||||
You can run the nest command for component specific tests.
|
||||
|
||||
```
|
||||
```bash
|
||||
# Test current code
|
||||
pnpm test:update # or npm run test:update
|
||||
|
||||
pnpm test
|
||||
|
||||
# or
|
||||
|
||||
npm run test
|
||||
```
|
||||
|
||||
```bash
|
||||
# Test isolated component code
|
||||
pnpm test:update src/button # or npm run test:update src/button
|
||||
|
||||
pnpm test button
|
||||
|
||||
# or
|
||||
|
||||
npm run test button
|
||||
|
||||
```
|
||||
|
||||
5. Be sure the package builds.
|
||||
|
||||
```
|
||||
```bash
|
||||
# Build current code
|
||||
pnpm build # or npm run build
|
||||
|
||||
pnpm build
|
||||
|
||||
# or
|
||||
|
||||
npm run build
|
||||
```
|
||||
|
||||
> Note: ensure your version of Node is 16 or higher to run scripts
|
||||
|
||||
6. Send your pull request:
|
||||
|
||||
- Send your pull request to the `main` branch
|
||||
- Send your pull request to the `canary` branch
|
||||
- Your pull request will be reviewed by the maintainers and the maintainers will decide if it is accepted or not
|
||||
- Once the pull request is accepted, the maintainers will merge it to the `main` branch
|
||||
- Once the pull request is accepted, the maintainers will merge it to the `canary` branch
|
||||
|
||||
## Visual Changes
|
||||
|
||||
@@ -12,4 +12,8 @@ IS_PREVIEW=true/false
|
||||
ANALYZE_BUNDLE=true/false
|
||||
|
||||
# Vercel preview env (is used for taking the docs directly from the project files)
|
||||
NEXT_PUBLIC_PREVIEW=true/false
|
||||
NEXT_PUBLIC_PREVIEW=true/false
|
||||
|
||||
## Featurebase
|
||||
NEXT_PUBLIC_FB_FEEDBACK_ORG=
|
||||
NEXT_PUBLIC_FB_FEEDBACK_URL=
|
||||
@@ -202,7 +202,7 @@
|
||||
"email": null,
|
||||
"twitter": null,
|
||||
"github": "https://github.com/HighError",
|
||||
"website": "https://higherror.github.io/"
|
||||
"website": null
|
||||
},
|
||||
{
|
||||
"MemberId": 409558,
|
||||
|
||||
@@ -7,6 +7,7 @@ import {format, parseISO} from "date-fns";
|
||||
import NextLink from "next/link";
|
||||
import {Balancer} from "react-wrap-balancer";
|
||||
|
||||
import {__DEV__, __PREVIEW__} from "@/utils";
|
||||
import {MDXContent} from "@/components/mdx-content";
|
||||
import {siteConfig} from "@/config/site";
|
||||
import {Route} from "@/libs/docs/page";
|
||||
@@ -18,6 +19,8 @@ interface BlogPostProps {
|
||||
};
|
||||
}
|
||||
|
||||
const isDraftVisible = __DEV__ || __PREVIEW__;
|
||||
|
||||
async function getBlogPostFromParams({params}: BlogPostProps) {
|
||||
const slug = params.slug || "";
|
||||
const post = allBlogPosts.find((post) => post.slugAsParams === slug);
|
||||
@@ -78,7 +81,7 @@ export async function generateStaticParams(): Promise<BlogPostProps["params"][]>
|
||||
export default async function DocPage({params}: BlogPostProps) {
|
||||
const {post} = await getBlogPostFromParams({params});
|
||||
|
||||
if (!post) {
|
||||
if (!post || (post.draft && !isDraftVisible)) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
@@ -96,6 +99,7 @@ export default async function DocPage({params}: BlogPostProps) {
|
||||
<ChevronRightLinearIcon className="rotate-180 inline-block mr-1" size={15} />
|
||||
Back to blog
|
||||
</Link>
|
||||
|
||||
<time className="block text-small mb-2 text-default-500" dateTime={post.date}>
|
||||
{format(parseISO(post.date), "LLLL d, yyyy")}
|
||||
</time>
|
||||
@@ -119,6 +123,7 @@ export default async function DocPage({params}: BlogPostProps) {
|
||||
</div>
|
||||
<h1 className="mb-2 font-bold text-4xl">
|
||||
<Balancer>{post.title}</Balancer>
|
||||
<strong className="text-default-300">{post?.draft && " (Draft)"}</strong>
|
||||
</h1>
|
||||
<MDXContent code={post.body.code} />
|
||||
</div>
|
||||
|
||||
@@ -2,9 +2,20 @@ import {allBlogPosts} from "contentlayer/generated";
|
||||
import {compareDesc} from "date-fns";
|
||||
|
||||
import {BlogPostList} from "@/components/blog-post";
|
||||
import {__DEV__, __PREVIEW__} from "@/utils";
|
||||
|
||||
const isDraftVisible = __DEV__ || __PREVIEW__;
|
||||
|
||||
export default function Blog() {
|
||||
const posts = allBlogPosts.sort((a, b) => compareDesc(new Date(a.date), new Date(b.date)));
|
||||
const posts = allBlogPosts
|
||||
.sort((a, b) => compareDesc(new Date(a.date), new Date(b.date)))
|
||||
?.filter((post) => {
|
||||
if (post.draft && !isDraftVisible) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="w-full lg:px-16 mt-12">
|
||||
|
||||
@@ -83,7 +83,7 @@ export default async function DocPage({params}: DocPageProps) {
|
||||
if (!doc) {
|
||||
notFound();
|
||||
}
|
||||
const editUrl = `${GITHUB_URL}/${REPO_NAME}/edit/${TAG}/${CONTENT_PATH}${currentRoute?.path}`;
|
||||
const editUrl = `${GITHUB_URL}/${REPO_NAME}/edit/${TAG}${CONTENT_PATH}${currentRoute?.path}`;
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -44,12 +44,12 @@ export default function Page() {
|
||||
];
|
||||
|
||||
return (
|
||||
<Navbar isBordered onMenuOpenChange={setIsMenuOpen}>
|
||||
<NavbarContent className="sm:hidden" justify="start">
|
||||
<NavbarMenuToggle aria-label={isMenuOpen ? "Close menu" : "Open menu"} />
|
||||
</NavbarContent>
|
||||
|
||||
<NavbarContent className="sm:hidden pr-3" justify="center">
|
||||
<Navbar onMenuOpenChange={setIsMenuOpen}>
|
||||
<NavbarContent>
|
||||
<NavbarMenuToggle
|
||||
aria-label={isMenuOpen ? "Close menu" : "Open menu"}
|
||||
className="sm:hidden"
|
||||
/>
|
||||
<NavbarBrand>
|
||||
<AcmeLogo />
|
||||
<p className="font-bold text-inherit">ACME</p>
|
||||
@@ -57,17 +57,13 @@ export default function Page() {
|
||||
</NavbarContent>
|
||||
|
||||
<NavbarContent className="hidden sm:flex gap-4" justify="center">
|
||||
<NavbarBrand>
|
||||
<AcmeLogo />
|
||||
<p className="font-bold text-inherit">ACME</p>
|
||||
</NavbarBrand>
|
||||
<NavbarItem>
|
||||
<Link color="foreground" href="#">
|
||||
Features
|
||||
</Link>
|
||||
</NavbarItem>
|
||||
<NavbarItem isActive>
|
||||
<Link aria-current="page" color="warning" href="#">
|
||||
<Link aria-current="page" href="#">
|
||||
Customers
|
||||
</Link>
|
||||
</NavbarItem>
|
||||
@@ -77,25 +73,23 @@ export default function Page() {
|
||||
</Link>
|
||||
</NavbarItem>
|
||||
</NavbarContent>
|
||||
|
||||
<NavbarContent justify="end">
|
||||
<NavbarItem className="hidden lg:flex">
|
||||
<Link href="#">Login</Link>
|
||||
</NavbarItem>
|
||||
<NavbarItem>
|
||||
<Button as={Link} color="warning" href="#" variant="flat">
|
||||
<Button as={Link} color="primary" href="#" variant="flat">
|
||||
Sign Up
|
||||
</Button>
|
||||
</NavbarItem>
|
||||
</NavbarContent>
|
||||
|
||||
<NavbarMenu>
|
||||
{menuItems.map((item, index) => (
|
||||
<NavbarMenuItem key={`${item}-${index}`}>
|
||||
<Link
|
||||
className="w-full"
|
||||
color={
|
||||
index === 2 ? "warning" : index === menuItems.length - 1 ? "danger" : "foreground"
|
||||
index === 2 ? "primary" : index === menuItems.length - 1 ? "danger" : "foreground"
|
||||
}
|
||||
href="#"
|
||||
size="lg"
|
||||
|
||||
@@ -28,6 +28,8 @@ const AcmeLogo = () => (
|
||||
);
|
||||
|
||||
export default function Page() {
|
||||
const [isMenuOpen, setIsMenuOpen] = React.useState<boolean | undefined>(false);
|
||||
|
||||
const menuItems = [
|
||||
"Profile",
|
||||
"Dashboard",
|
||||
@@ -42,12 +44,12 @@ export default function Page() {
|
||||
];
|
||||
|
||||
return (
|
||||
<Navbar disableAnimation isBordered>
|
||||
<NavbarContent className="sm:hidden" justify="start">
|
||||
<NavbarMenuToggle />
|
||||
</NavbarContent>
|
||||
|
||||
<NavbarContent className="sm:hidden pr-3" justify="center">
|
||||
<Navbar disableAnimation onMenuOpenChange={setIsMenuOpen}>
|
||||
<NavbarContent>
|
||||
<NavbarMenuToggle
|
||||
aria-label={isMenuOpen ? "Close menu" : "Open menu"}
|
||||
className="sm:hidden"
|
||||
/>
|
||||
<NavbarBrand>
|
||||
<AcmeLogo />
|
||||
<p className="font-bold text-inherit">ACME</p>
|
||||
@@ -55,17 +57,13 @@ export default function Page() {
|
||||
</NavbarContent>
|
||||
|
||||
<NavbarContent className="hidden sm:flex gap-4" justify="center">
|
||||
<NavbarBrand>
|
||||
<AcmeLogo />
|
||||
<p className="font-bold text-inherit">ACME</p>
|
||||
</NavbarBrand>
|
||||
<NavbarItem>
|
||||
<Link color="foreground" href="#">
|
||||
Features
|
||||
</Link>
|
||||
</NavbarItem>
|
||||
<NavbarItem isActive>
|
||||
<Link aria-current="page" color="warning" href="#">
|
||||
<Link aria-current="page" href="#">
|
||||
Customers
|
||||
</Link>
|
||||
</NavbarItem>
|
||||
@@ -75,25 +73,23 @@ export default function Page() {
|
||||
</Link>
|
||||
</NavbarItem>
|
||||
</NavbarContent>
|
||||
|
||||
<NavbarContent justify="end">
|
||||
<NavbarItem className="hidden lg:flex">
|
||||
<Link href="#">Login</Link>
|
||||
</NavbarItem>
|
||||
<NavbarItem>
|
||||
<Button as={Link} color="warning" href="#" variant="flat">
|
||||
<Button as={Link} color="primary" href="#" variant="flat">
|
||||
Sign Up
|
||||
</Button>
|
||||
</NavbarItem>
|
||||
</NavbarContent>
|
||||
|
||||
<NavbarMenu>
|
||||
{menuItems.map((item, index) => (
|
||||
<NavbarMenuItem key={`${item}-${index}`}>
|
||||
<Link
|
||||
className="w-full"
|
||||
color={
|
||||
index === 2 ? "warning" : index === menuItems.length - 1 ? "danger" : "foreground"
|
||||
index === 2 ? "primary" : index === menuItems.length - 1 ? "danger" : "foreground"
|
||||
}
|
||||
href="#"
|
||||
size="lg"
|
||||
|
||||
@@ -124,15 +124,15 @@ const MyInput = extendVariants(Input, {
|
||||
},
|
||||
size: {
|
||||
xs: {
|
||||
inputWrapper: "h-unit-6 min-h-unit-6 px-1",
|
||||
inputWrapper: "h-6 min-h-6 px-1",
|
||||
input: "text-tiny",
|
||||
},
|
||||
md: {
|
||||
inputWrapper: "h-unit-10 min-h-unit-10",
|
||||
inputWrapper: "h-10 min-h-10",
|
||||
input: "text-small",
|
||||
},
|
||||
xl: {
|
||||
inputWrapper: "h-unit-14 min-h-unit-14",
|
||||
inputWrapper: "h-14 min-h-14",
|
||||
input: "text-medium",
|
||||
},
|
||||
},
|
||||
@@ -182,7 +182,9 @@ const MyButton2 = extendVariants(Button, {
|
||||
xl: "px-12 py-6 text-xl",
|
||||
},
|
||||
},
|
||||
defaultVariants: {},
|
||||
defaultVariants: {
|
||||
color: "foreground",
|
||||
},
|
||||
});
|
||||
|
||||
const usersData = [
|
||||
@@ -544,7 +546,7 @@ export default function NextUIPerf() {
|
||||
|
||||
<Button>Click Me!</Button>
|
||||
|
||||
<MyButton2 color="foreground">Press Me!</MyButton2>
|
||||
<MyButton2 color="primary">Press Me!</MyButton2>
|
||||
|
||||
<Pagination
|
||||
showControls
|
||||
|
||||
@@ -2,7 +2,6 @@ import "@/styles/globals.css";
|
||||
import "@/styles/sandpack.css";
|
||||
import {Metadata} from "next";
|
||||
import {clsx} from "@nextui-org/shared-utils";
|
||||
import {Analytics} from "@vercel/analytics/react";
|
||||
|
||||
import {Providers} from "./providers";
|
||||
|
||||
@@ -12,7 +11,8 @@ import {siteConfig} from "@/config/site";
|
||||
import {fontSans} from "@/config/fonts";
|
||||
import {Navbar} from "@/components/navbar";
|
||||
import {Footer} from "@/components/footer";
|
||||
import {__PROD__} from "@/utils";
|
||||
import {ProBanner} from "@/components/pro-banner";
|
||||
import {ScriptProviders} from "@/components/scripts/script-providers";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: {
|
||||
@@ -70,13 +70,14 @@ export default function RootLayout({children}: {children: React.ReactNode}) {
|
||||
<body className={clsx("min-h-screen bg-background font-sans antialiased", fontSans.variable)}>
|
||||
<Providers themeProps={{attribute: "class", defaultTheme: "dark"}}>
|
||||
<div className="relative flex flex-col" id="app-container">
|
||||
<ProBanner />
|
||||
<Navbar mobileRoutes={manifest.mobileRoutes} routes={manifest.routes} />
|
||||
{children}
|
||||
<Footer />
|
||||
</div>
|
||||
<Cmdk />
|
||||
</Providers>
|
||||
{__PROD__ && <Analytics />}
|
||||
<ScriptProviders />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
||||
@@ -12,6 +12,7 @@ import {Community} from "@/components/marketing/community";
|
||||
import {Support} from "@/components/marketing/support";
|
||||
import landingContent from "@/content/landing";
|
||||
import {getAllSponsors} from "@/utils/get-all-sponsors";
|
||||
import {Sponsors} from "@/components/marketing/sponsors";
|
||||
|
||||
async function getData() {
|
||||
try {
|
||||
@@ -33,6 +34,7 @@ export default async function Home() {
|
||||
<section className="flex flex-col items-center justify-center">
|
||||
<Hero />
|
||||
<FeaturesGrid features={landingContent.topFeatures} />
|
||||
<Sponsors />
|
||||
<CustomThemes />
|
||||
<A11yOtb />
|
||||
<DarkMode />
|
||||
|
||||
@@ -57,7 +57,7 @@ const BlogPostCard = (post: BlogPost) => {
|
||||
</CardBody>
|
||||
<CardFooter className="flex justify-between items-center">
|
||||
<time className="block text-small text-default-500" dateTime={post.date}>
|
||||
{format(parseISO(post.date), "LLLL d, yyyy")}
|
||||
{format(parseISO(post.date), "LLLL d, yyyy")} {post?.draft && " (Draft)"}
|
||||
</time>
|
||||
<Avatar size="sm" src={post.author?.avatar} />
|
||||
</CardFooter>
|
||||
|
||||
@@ -154,6 +154,16 @@ export const Cmdk: FC<{}> = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const prioritizeFirstLevelItems = (a: SearchResultItem, b: SearchResultItem) => {
|
||||
if (a.type === "lvl1") {
|
||||
return -1;
|
||||
} else if (b.type === "lvl1") {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
const results = useMemo<SearchResultItem[]>(
|
||||
function getResults() {
|
||||
if (query.length < 2) return [];
|
||||
@@ -165,12 +175,22 @@ export const Cmdk: FC<{}> = () => {
|
||||
if (words.length === 1) {
|
||||
return matchSorter(data, query, {
|
||||
keys: MATCH_KEYS,
|
||||
sorter: (matches) => {
|
||||
matches.sort((a, b) => prioritizeFirstLevelItems(a.item, b.item));
|
||||
|
||||
return matches;
|
||||
},
|
||||
}).slice(0, MAX_RESULTS);
|
||||
}
|
||||
|
||||
const matchesForEachWord = words.map((word) =>
|
||||
matchSorter(data, word, {
|
||||
keys: MATCH_KEYS,
|
||||
sorter: (matches) => {
|
||||
matches.sort((a, b) => prioritizeFirstLevelItems(a.item, b.item));
|
||||
|
||||
return matches;
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -410,8 +430,8 @@ export const Cmdk: FC<{}> = () => {
|
||||
</Kbd>
|
||||
</div>
|
||||
<Command.List ref={listRef} className={slots.list()} role="listbox">
|
||||
{query.length > 0 && (
|
||||
<Command.Empty>
|
||||
<Command.Empty>
|
||||
{query.length > 0 && (
|
||||
<div className={slots.emptyWrapper()}>
|
||||
<div>
|
||||
<p>No results for "{query}"</p>
|
||||
@@ -424,8 +444,8 @@ export const Cmdk: FC<{}> = () => {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Command.Empty>
|
||||
)}
|
||||
)}
|
||||
</Command.Empty>
|
||||
|
||||
{isEmpty(query) &&
|
||||
(isEmpty(recentSearches) ? (
|
||||
|
||||
@@ -57,7 +57,7 @@ export const DemoCodeModal: FC<DemoCodeModalProps> = ({isOpen, code, title, subt
|
||||
<>
|
||||
This is an example of how to use the {lowerTitle} component, for more information
|
||||
please visit the
|
||||
<NextUILink as={Link} href={`/docs/${lowerTitle}`}>
|
||||
<NextUILink as={Link} href={`/docs/components/${lowerTitle}`}>
|
||||
{lowerTitle}
|
||||
</NextUILink>
|
||||
docs.
|
||||
|
||||
@@ -9,7 +9,6 @@ import {useCodeDemo, UseCodeDemoProps} from "./use-code-demo";
|
||||
import WindowResizer, {WindowResizerProps} from "./window-resizer";
|
||||
|
||||
import {GradientBoxProps} from "@/components/gradient-box";
|
||||
import {trackEvent} from "@/utils/va";
|
||||
|
||||
const DynamicReactLiveDemo = dynamic(
|
||||
() => import("./react-live-demo").then((m) => m.ReactLiveDemo),
|
||||
@@ -180,14 +179,6 @@ export const CodeDemo: React.FC<CodeDemoProps> = ({
|
||||
panel: "pt-0",
|
||||
}}
|
||||
variant="underlined"
|
||||
onSelectionChange={(tabKey) => {
|
||||
trackEvent("CodeDemo - Selection", {
|
||||
name: tabKey as string,
|
||||
action: "tabChange",
|
||||
category: "docs",
|
||||
data: tabKey ?? "",
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Tab key="preview" title="Preview">
|
||||
{previewContent}
|
||||
|
||||
@@ -2,6 +2,8 @@ import React from "react";
|
||||
import {LivePreview, LiveProvider, LiveError} from "react-live";
|
||||
import {clsx} from "@nextui-org/shared-utils";
|
||||
import * as Components from "@nextui-org/react";
|
||||
import * as intlDateUtils from "@internationalized/date";
|
||||
import * as reactAriaI18n from "@react-aria/i18n";
|
||||
|
||||
import {BgGridContainer} from "@/components/bg-grid-container";
|
||||
import {GradientBox, GradientBoxProps} from "@/components/gradient-box";
|
||||
@@ -19,6 +21,8 @@ export interface ReactLiveDemoProps {
|
||||
|
||||
export const scope = {
|
||||
...Components,
|
||||
...intlDateUtils,
|
||||
...reactAriaI18n,
|
||||
} as Record<string, unknown>;
|
||||
|
||||
export const ReactLiveDemo: React.FC<ReactLiveDemoProps> = ({
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import React, {forwardRef, useEffect} from "react";
|
||||
import {clsx, dataAttr, getUniqueID} from "@nextui-org/shared-utils";
|
||||
import BaseHighlight, {Language, PrismTheme, defaultProps} from "prism-react-renderer";
|
||||
import {debounce} from "lodash";
|
||||
import {debounce, omit} from "lodash";
|
||||
|
||||
import defaultTheme from "@/libs/prism-theme";
|
||||
import {trackEvent} from "@/utils/va";
|
||||
|
||||
interface CodeblockProps {
|
||||
language: Language;
|
||||
@@ -20,6 +19,17 @@ interface CodeblockProps {
|
||||
|
||||
type HighlightStyle = "inserted" | "deleted" | undefined;
|
||||
|
||||
const nextuiCliCommand = [
|
||||
/^init$/,
|
||||
/^add$/,
|
||||
/^upgrade$/,
|
||||
/^remove$/,
|
||||
/^list$/,
|
||||
/^env$/,
|
||||
/^doctor$/,
|
||||
];
|
||||
|
||||
const highlightStyleToken = ["bun", /nextui\s\w+(?=\s?)/, /^nextui$/, "Usage", ...nextuiCliCommand];
|
||||
const RE = /{([\d,-]+)}/;
|
||||
|
||||
const calculateLinesToHighlight = (meta?: string) => {
|
||||
@@ -112,12 +122,6 @@ const Codeblock = forwardRef<HTMLPreElement, CodeblockProps>(
|
||||
return;
|
||||
|
||||
lastSelectionText.current = selectionText;
|
||||
|
||||
trackEvent("Codeblock - Selection", {
|
||||
action: "selectText",
|
||||
category: "docs",
|
||||
data: selectionText,
|
||||
});
|
||||
};
|
||||
|
||||
const debouncedHandleSelectionChange = debounce(handleSelectionChange, 1000);
|
||||
@@ -152,8 +156,8 @@ const Codeblock = forwardRef<HTMLPreElement, CodeblockProps>(
|
||||
|
||||
return (
|
||||
<div
|
||||
{...omit(lineProps, ["key"])}
|
||||
key={`${i}-${getUniqueID("line-wrapper")}`}
|
||||
{...lineProps}
|
||||
className={clsx(
|
||||
lineProps.className,
|
||||
removeIndent ? "pr-4" : "px-4",
|
||||
@@ -172,13 +176,30 @@ const Codeblock = forwardRef<HTMLPreElement, CodeblockProps>(
|
||||
{showLines && (
|
||||
<span className="select-none text-xs mr-6 opacity-30">{i + 1}</span>
|
||||
)}
|
||||
{line.map((token, key) => (
|
||||
<span
|
||||
key={`${key}-${getUniqueID("line")}`}
|
||||
{...getTokenProps({token, key})}
|
||||
className={className}
|
||||
/>
|
||||
))}
|
||||
{line.map((token, key) => {
|
||||
// Bun has no color style by default in the code block, so hack add in here
|
||||
const props = getTokenProps({token, key}) || {};
|
||||
|
||||
return (
|
||||
<span
|
||||
{...omit(props, ["key"])}
|
||||
key={`${key}-${getUniqueID("line")}`}
|
||||
className={className}
|
||||
style={{
|
||||
...props.style,
|
||||
...(highlightStyleToken.some((t) => {
|
||||
const content = token.content.trim();
|
||||
|
||||
const regex = t instanceof RegExp ? t : new RegExp(t);
|
||||
|
||||
return regex.test(content);
|
||||
})
|
||||
? {color: "rgb(var(--code-function))"}
|
||||
: {}),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -8,6 +8,8 @@ import {trackEvent} from "@/utils/va";
|
||||
|
||||
export interface ComponentLinksProps {
|
||||
component: string;
|
||||
npm?: string;
|
||||
source?: string;
|
||||
styles?: string;
|
||||
storybook?: string;
|
||||
rscCompatible?: boolean;
|
||||
@@ -60,6 +62,8 @@ const ButtonLink = ({
|
||||
|
||||
export const ComponentLinks = ({
|
||||
component,
|
||||
npm,
|
||||
source,
|
||||
storybook,
|
||||
styles,
|
||||
rscCompatible,
|
||||
@@ -80,10 +84,10 @@ export const ComponentLinks = ({
|
||||
Storybook
|
||||
</ButtonLink>
|
||||
<ButtonLink
|
||||
href={`https://www.npmjs.com/package/@nextui-org/${component}`}
|
||||
href={`https://www.npmjs.com/package/@nextui-org/${npm || component}`}
|
||||
startContent={<NpmIcon className="text-2xl text-[#E53E3E]" />}
|
||||
>
|
||||
{`@nextui-org/${component}`}
|
||||
{`@nextui-org/${npm || component}`}
|
||||
</ButtonLink>
|
||||
{reactAriaHook && (
|
||||
<ButtonLink
|
||||
@@ -113,7 +117,10 @@ export const ComponentLinks = ({
|
||||
</ButtonLink>
|
||||
)}
|
||||
|
||||
<ButtonLink href={`${COMPONENT_PATH}/${component}`} startContent={<GithubIcon size={20} />}>
|
||||
<ButtonLink
|
||||
href={`${COMPONENT_PATH}/${source || component}`}
|
||||
startContent={<GithubIcon size={20} />}
|
||||
>
|
||||
Source
|
||||
</ButtonLink>
|
||||
<ButtonLink
|
||||
|
||||
@@ -2,22 +2,20 @@ import {Tabs, Tab, Snippet} from "@nextui-org/react";
|
||||
|
||||
import Codeblock from "./codeblock";
|
||||
|
||||
import {trackEvent} from "@/utils/va";
|
||||
|
||||
type PackageManager = {
|
||||
key: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
const importTabs: PackageManager[] = [
|
||||
{
|
||||
key: "main",
|
||||
name: "Main",
|
||||
},
|
||||
{
|
||||
key: "individual",
|
||||
name: "Individual",
|
||||
},
|
||||
{
|
||||
key: "main",
|
||||
name: "Global",
|
||||
},
|
||||
];
|
||||
|
||||
export interface ImportTabsProps {
|
||||
@@ -34,14 +32,6 @@ export const ImportTabs = ({commands}: ImportTabsProps) => {
|
||||
tabList: "relative h-10",
|
||||
}}
|
||||
variant="underlined"
|
||||
onSelectionChange={(tabKey) => {
|
||||
trackEvent("ImportTabs - Selection", {
|
||||
name: tabKey as string,
|
||||
action: "tabChange",
|
||||
category: "docs",
|
||||
data: commands[tabKey] ?? "",
|
||||
});
|
||||
}}
|
||||
>
|
||||
{importTabs.map(({key, name}) => {
|
||||
if (!commands[key]) return null;
|
||||
@@ -57,14 +47,6 @@ export const ImportTabs = ({commands}: ImportTabsProps) => {
|
||||
pre: "font-light text-base",
|
||||
copyButton: "text-lg text-default-400",
|
||||
}}
|
||||
onCopy={() => {
|
||||
trackEvent("ImportTabs - Copy", {
|
||||
name,
|
||||
action: "copyInstallScript",
|
||||
category: "docs",
|
||||
data: commands[name] ?? "",
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Codeblock
|
||||
hideScrollBar
|
||||
|
||||
@@ -1,20 +1,25 @@
|
||||
import {Tabs, Tab, Snippet} from "@nextui-org/react";
|
||||
import {Key} from "react";
|
||||
import {useLocalStorage} from "usehooks-ts";
|
||||
import {Tabs, Tab, Snippet, Code} from "@nextui-org/react";
|
||||
import {Key, useState} from "react";
|
||||
|
||||
import Codeblock from "./codeblock";
|
||||
import {Blockquote} from "./blockquote";
|
||||
|
||||
import {YarnIcon, NpmSmallIcon, PnpmIcon} from "@/components/icons";
|
||||
import {trackEvent} from "@/utils/va";
|
||||
import {YarnIcon, NpmSmallIcon, PnpmIcon, BunIcon, CLIBoldIcon} from "@/components/icons";
|
||||
|
||||
type PackageManagerName = "npm" | "yarn" | "pnpm";
|
||||
type PackageManagerName = "cli" | "npm" | "yarn" | "pnpm" | "bun";
|
||||
|
||||
type PackageManager = {
|
||||
icon: JSX.Element;
|
||||
name: PackageManagerName;
|
||||
label?: string;
|
||||
};
|
||||
|
||||
const packageManagers: PackageManager[] = [
|
||||
{
|
||||
name: "cli",
|
||||
label: "CLI",
|
||||
icon: <CLIBoldIcon className="text-lg text-default-600 dark:text-default-400" />,
|
||||
},
|
||||
{
|
||||
name: "npm",
|
||||
icon: <NpmSmallIcon className="text-[#E53E3E]" />,
|
||||
@@ -27,76 +32,76 @@ const packageManagers: PackageManager[] = [
|
||||
name: "pnpm",
|
||||
icon: <PnpmIcon className="text-[#F69220]" />,
|
||||
},
|
||||
{
|
||||
name: "bun",
|
||||
icon: <BunIcon className="text-lg text-[#FBF0DF]" />,
|
||||
},
|
||||
];
|
||||
|
||||
export interface PackageManagersProps {
|
||||
commands: Partial<Record<PackageManagerName, React.Key>>;
|
||||
showGlobalInstallWarning?: boolean;
|
||||
}
|
||||
|
||||
export const PackageManagers = ({commands}: PackageManagersProps) => {
|
||||
const [selectedManager, setSelectedManager] = useLocalStorage<PackageManagerName>(
|
||||
"selectedPackageManager",
|
||||
"npm",
|
||||
export const PackageManagers = ({
|
||||
commands,
|
||||
showGlobalInstallWarning = false,
|
||||
}: PackageManagersProps) => {
|
||||
const [selectedManager, setSelectedManager] = useState<PackageManagerName>(
|
||||
commands.cli ? "cli" : "npm",
|
||||
);
|
||||
|
||||
const handleSelectionChange = (tabKey: Key) => {
|
||||
trackEvent("PackageManagers - Selection", {
|
||||
name: `${tabKey}`,
|
||||
action: "tabChange",
|
||||
category: "docs",
|
||||
data: commands[tabKey as unknown as PackageManagerName] ?? "",
|
||||
});
|
||||
|
||||
setSelectedManager(tabKey as PackageManagerName);
|
||||
};
|
||||
|
||||
return (
|
||||
<Tabs
|
||||
aria-label="NextUI installation commands"
|
||||
classNames={{
|
||||
base: "group mt-4",
|
||||
tabList: "h-10",
|
||||
}}
|
||||
selectedKey={selectedManager}
|
||||
variant="underlined"
|
||||
onSelectionChange={handleSelectionChange}
|
||||
>
|
||||
{packageManagers.map(({name, icon}) => {
|
||||
if (!commands[name]) return null;
|
||||
<>
|
||||
<Tabs
|
||||
aria-label="NextUI installation commands"
|
||||
classNames={{
|
||||
base: "group mt-4 min-w-[300px] w-full overflow-x-auto",
|
||||
tabList: "h-10",
|
||||
}}
|
||||
selectedKey={selectedManager}
|
||||
variant="underlined"
|
||||
onSelectionChange={handleSelectionChange}
|
||||
>
|
||||
{packageManagers.map(({name, label, icon}) => {
|
||||
if (!commands[name]) return null;
|
||||
|
||||
return (
|
||||
<Tab
|
||||
key={name}
|
||||
title={
|
||||
<div className="flex items-center space-x-2">
|
||||
{icon}
|
||||
<span>{name}</span>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Snippet
|
||||
disableTooltip
|
||||
fullWidth
|
||||
hideSymbol
|
||||
classNames={{
|
||||
base: "bg-code-background text-code-foreground",
|
||||
pre: "font-light text-base",
|
||||
copyButton: "text-lg text-zinc-500 mr-2",
|
||||
}}
|
||||
onCopy={() => {
|
||||
trackEvent("PackageManagers - Copy", {
|
||||
name,
|
||||
action: "copyScript",
|
||||
category: "docs",
|
||||
data: commands[name] ?? "",
|
||||
});
|
||||
}}
|
||||
return (
|
||||
<Tab
|
||||
key={name}
|
||||
title={
|
||||
<div className="flex items-center space-x-2">
|
||||
{icon}
|
||||
<span>{label || name}</span>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Codeblock removeIndent codeString={commands[name] as string} language="bash" />
|
||||
</Snippet>
|
||||
</Tab>
|
||||
);
|
||||
})}
|
||||
</Tabs>
|
||||
<Snippet
|
||||
disableTooltip
|
||||
fullWidth
|
||||
hideSymbol
|
||||
classNames={{
|
||||
base: "bg-code-background text-code-foreground",
|
||||
pre: "font-light text-base",
|
||||
copyButton: "text-lg text-zinc-500 mr-2",
|
||||
}}
|
||||
>
|
||||
<Codeblock removeIndent codeString={commands[name] as string} language="bash" />
|
||||
</Snippet>
|
||||
</Tab>
|
||||
);
|
||||
})}
|
||||
</Tabs>
|
||||
{showGlobalInstallWarning && (
|
||||
<Blockquote className="my-2">
|
||||
No need to install this package if <Code>@nextui-org/react</Code> is already installed
|
||||
globally.
|
||||
</Blockquote>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -20,7 +20,7 @@ type SwatchColors = {
|
||||
|
||||
type SwatchSetProps = {
|
||||
colors: SwatchColors[];
|
||||
isSematic?: boolean;
|
||||
isSemantic?: boolean;
|
||||
};
|
||||
|
||||
const scaleIndexMap: Record<number, string> = {
|
||||
@@ -90,7 +90,7 @@ const Swatch = ({color, scale}: {color: string; scale?: string}) => {
|
||||
);
|
||||
};
|
||||
|
||||
const SematicSwatch = ({
|
||||
const SemanticSwatch = ({
|
||||
color,
|
||||
className,
|
||||
textClassName,
|
||||
@@ -139,15 +139,15 @@ const SematicSwatch = ({
|
||||
);
|
||||
};
|
||||
|
||||
const SwatchSet = ({colors, isSematic = false}: SwatchSetProps) => (
|
||||
const SwatchSet = ({colors, isSemantic = false}: SwatchSetProps) => (
|
||||
<div className="flex flex-row flex-wrap items-center justify-center">
|
||||
{colors.map(({title, items}) => (
|
||||
<div key={title} className="flex flex-col items-start w-full h-full">
|
||||
<h2 className="text-xl font-bold text-foreground">{title}</h2>
|
||||
<div className="flex flex-row flex-wrap items-center justify-start w-full h-full px-4 py-1">
|
||||
{items.map((c, index) =>
|
||||
isSematic ? (
|
||||
<SematicSwatch
|
||||
isSemantic ? (
|
||||
<SemanticSwatch
|
||||
key={`${c.color}-${index}`}
|
||||
className={c.className}
|
||||
color={c.color}
|
||||
@@ -218,7 +218,7 @@ export const CommonColors = () => {
|
||||
export const SemanticColors = () => {
|
||||
return (
|
||||
<SwatchSet
|
||||
isSematic
|
||||
isSemantic
|
||||
colors={[
|
||||
{
|
||||
title: "Layout",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import {FC} from "react";
|
||||
import {FC, useEffect, useState} from "react";
|
||||
import {ChevronIcon} from "@nextui-org/shared-icons";
|
||||
import {CollectionBase, Expandable, MultipleSelection, Node, ItemProps} from "@react-types/shared";
|
||||
import {BaseItem} from "@nextui-org/aria-utils";
|
||||
@@ -28,6 +28,11 @@ import {getRoutePaths} from "./utils";
|
||||
import {Route} from "@/libs/docs/page";
|
||||
import {TreeKeyboardDelegate} from "@/utils/tree-keyboard-delegate";
|
||||
import {trackEvent} from "@/utils/va";
|
||||
import {FbFeedbackButton} from "@/components/featurebase/fb-feedback-button";
|
||||
import {FbChangelogButton} from "@/components/featurebase/fb-changelog-button";
|
||||
import {FbRoadmapLink} from "@/components/featurebase/fb-roadmap-link";
|
||||
import {openFeedbackWidget} from "@/utils/featurebase";
|
||||
import emitter from "@/libs/emitter";
|
||||
|
||||
export interface Props<T> extends Omit<ItemProps<T>, "title">, Route {
|
||||
slug?: string;
|
||||
@@ -85,6 +90,18 @@ function TreeItem<T>(props: TreeItemProps<T>) {
|
||||
|
||||
const Component = hasChildNodes ? "ul" : "li";
|
||||
|
||||
const cn = clsx(
|
||||
"w-full",
|
||||
"font-normal",
|
||||
"before:mr-4",
|
||||
"before:content-['']",
|
||||
"before:block",
|
||||
"before:bg-default-300",
|
||||
"before:w-1",
|
||||
"before:h-1",
|
||||
"before:rounded-full",
|
||||
);
|
||||
|
||||
const {pressProps} = usePress({
|
||||
onPress: () => {
|
||||
if (hasChildNodes) {
|
||||
@@ -103,6 +120,79 @@ function TreeItem<T>(props: TreeItemProps<T>) {
|
||||
|
||||
const {focusProps, isFocused, isFocusVisible} = useFocusRing();
|
||||
|
||||
const renderFeaturebaseComponent = (key: string) => {
|
||||
if (key === "roadmap")
|
||||
return <FbRoadmapLink className={cn} innerClassName="opacity-80 dark:opacity-60" />;
|
||||
if (key === "changelog")
|
||||
return (
|
||||
<NextUILink as={Link} className={cn} color="foreground" href="#">
|
||||
<FbChangelogButton />
|
||||
</NextUILink>
|
||||
);
|
||||
|
||||
return (
|
||||
<NextUILink as={Link} className={cn} color="foreground" href="#" onClick={openFeedbackWidget}>
|
||||
<FbFeedbackButton />
|
||||
</NextUILink>
|
||||
);
|
||||
};
|
||||
|
||||
const renderComponent = () => {
|
||||
if (hasChildNodes) {
|
||||
return (
|
||||
<span className="flex items-center gap-3">
|
||||
<span>{rendered}</span>
|
||||
<ChevronIcon
|
||||
className={clsx("transition-transform", {
|
||||
"-rotate-90": isExpanded,
|
||||
})}
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
if (typeof key === "string" && ["changelog", "feedback", "roadmap"].includes(key)) {
|
||||
return renderFeaturebaseComponent(key);
|
||||
}
|
||||
|
||||
return (
|
||||
<NextUILink as={Link} className={clsx(cn)} color="foreground" href={paths.pathname}>
|
||||
<span
|
||||
className={clsx(
|
||||
isSelected
|
||||
? "text-primary font-medium dark:text-foreground"
|
||||
: "opacity-80 dark:opacity-60",
|
||||
{
|
||||
"pointer-events-none": item.props?.comingSoon,
|
||||
},
|
||||
)}
|
||||
>
|
||||
{rendered}
|
||||
</span>
|
||||
{isUpdated && (
|
||||
<Chip
|
||||
className="ml-1 py-1 text-tiny text-default-400 bg-default-100/50"
|
||||
color="default"
|
||||
size="sm"
|
||||
variant="flat"
|
||||
>
|
||||
Updated
|
||||
</Chip>
|
||||
)}
|
||||
{isNew && (
|
||||
<Chip className="ml-1 py-1 text-tiny" color="primary" size="sm" variant="flat">
|
||||
New
|
||||
</Chip>
|
||||
)}
|
||||
{item.props?.comingSoon && (
|
||||
<Chip className="ml-1 py-1 text-tiny" color="default" size="sm" variant="flat">
|
||||
Coming soon
|
||||
</Chip>
|
||||
)}
|
||||
</NextUILink>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Component
|
||||
{...focusProps}
|
||||
@@ -122,66 +212,7 @@ function TreeItem<T>(props: TreeItemProps<T>) {
|
||||
>
|
||||
<div className="flex items-center gap-3 cursor-pointer" {...pressProps}>
|
||||
<Spacer x={spaceLeft} />
|
||||
{hasChildNodes ? (
|
||||
<span className="flex items-center gap-3">
|
||||
<span>{rendered}</span>
|
||||
<ChevronIcon
|
||||
className={clsx("transition-transform", {
|
||||
"-rotate-90": isExpanded,
|
||||
})}
|
||||
/>
|
||||
</span>
|
||||
) : (
|
||||
<NextUILink
|
||||
as={Link}
|
||||
className={clsx(
|
||||
"w-full",
|
||||
"font-normal",
|
||||
"before:mr-4",
|
||||
"before:content-['']",
|
||||
"before:block",
|
||||
"before:bg-default-300",
|
||||
"before:w-1",
|
||||
"before:h-1",
|
||||
"before:rounded-full",
|
||||
)}
|
||||
color="foreground"
|
||||
href={paths.pathname}
|
||||
>
|
||||
<span
|
||||
className={clsx(
|
||||
isSelected
|
||||
? "text-primary font-medium dark:text-foreground"
|
||||
: "opacity-80 dark:opacity-60",
|
||||
{
|
||||
"pointer-events-none": item.props?.comingSoon,
|
||||
},
|
||||
)}
|
||||
>
|
||||
{rendered}
|
||||
</span>
|
||||
{isUpdated && (
|
||||
<Chip
|
||||
className="ml-1 py-1 text-tiny text-default-400 bg-default-100/50"
|
||||
color="default"
|
||||
size="sm"
|
||||
variant="flat"
|
||||
>
|
||||
Updated
|
||||
</Chip>
|
||||
)}
|
||||
{isNew && (
|
||||
<Chip className="ml-1 py-1 text-tiny" color="primary" size="sm" variant="flat">
|
||||
New
|
||||
</Chip>
|
||||
)}
|
||||
{item.props?.comingSoon && (
|
||||
<Chip className="ml-1 py-1 text-tiny" color="default" size="sm" variant="flat">
|
||||
Coming soon
|
||||
</Chip>
|
||||
)}
|
||||
</NextUILink>
|
||||
)}
|
||||
{renderComponent()}
|
||||
{/* Workaround to avoid scrollbar overlapping */}
|
||||
<Spacer x={4} />
|
||||
</div>
|
||||
@@ -252,6 +283,8 @@ export interface DocsSidebarProps {
|
||||
}
|
||||
|
||||
export const DocsSidebar: FC<DocsSidebarProps> = ({routes, slug, tag, className}) => {
|
||||
const [isProBannerVisible, setIsProBannerVisible] = useState(true);
|
||||
|
||||
const expandedKeys = routes?.reduce((keys, route) => {
|
||||
if (route.defaultOpen) {
|
||||
keys.push(route.key as string);
|
||||
@@ -260,8 +293,18 @@ export const DocsSidebar: FC<DocsSidebarProps> = ({routes, slug, tag, className}
|
||||
return keys;
|
||||
}, [] as string[]);
|
||||
|
||||
return (
|
||||
<div className={clsx("lg:fixed lg:top-20 mt-2 z-0 lg:h-[calc(100vh-121px)]", className)}>
|
||||
useEffect(() => {
|
||||
emitter.on("proBannerVisibilityChange", (value) => {
|
||||
setIsProBannerVisible(value === "visible");
|
||||
});
|
||||
|
||||
return () => {
|
||||
emitter.off("proBannerVisibilityChange");
|
||||
};
|
||||
}, []);
|
||||
|
||||
const treeContent = useMemo(() => {
|
||||
return (
|
||||
<Tree defaultExpandedKeys={expandedKeys} items={routes || []}>
|
||||
{(route) => (
|
||||
<Item
|
||||
@@ -275,6 +318,18 @@ export const DocsSidebar: FC<DocsSidebarProps> = ({routes, slug, tag, className}
|
||||
</Item>
|
||||
)}
|
||||
</Tree>
|
||||
);
|
||||
}, [routes]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
"lg:fixed mt-2 z-0 lg:h-[calc(100vh-121px)]",
|
||||
isProBannerVisible ? "lg:top-32" : "lg:top-20",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{treeContent}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import {FC, useRef, useEffect} from "react";
|
||||
import {FC, useRef, useEffect, useState} from "react";
|
||||
import {clsx} from "@nextui-org/shared-utils";
|
||||
import {Divider, Spacer} from "@nextui-org/react";
|
||||
import {ChevronCircleTopLinearIcon} from "@nextui-org/shared-icons";
|
||||
@@ -9,6 +9,7 @@ import scrollIntoView from "scroll-into-view-if-needed";
|
||||
import {Heading} from "@/libs/docs/utils";
|
||||
import {useScrollSpy} from "@/hooks/use-scroll-spy";
|
||||
import {useScrollPosition} from "@/hooks/use-scroll-position";
|
||||
import emitter from "@/libs/emitter";
|
||||
|
||||
export interface DocsTocProps {
|
||||
headings: Heading[];
|
||||
@@ -22,6 +23,8 @@ const paddingLeftByLevel: Record<number, string> = {
|
||||
};
|
||||
|
||||
export const DocsToc: FC<DocsTocProps> = ({headings}) => {
|
||||
const [isProBannerVisible, setIsProBannerVisible] = useState(true);
|
||||
|
||||
const tocRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const scrollPosition = useScrollPosition(tocRef);
|
||||
@@ -51,10 +54,23 @@ export const DocsToc: FC<DocsTocProps> = ({headings}) => {
|
||||
}
|
||||
}, [activeId, activeIndex]);
|
||||
|
||||
useEffect(() => {
|
||||
emitter.on("proBannerVisibilityChange", (value) => {
|
||||
setIsProBannerVisible(value === "visible");
|
||||
});
|
||||
|
||||
return () => {
|
||||
emitter.off("proBannerVisibilityChange");
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={tocRef}
|
||||
className="fixed w-full max-w-[12rem] flex flex-col gap-4 text-left top-20 pb-20 h-[calc(100vh-121px)] scrollbar-hide overflow-y-scroll"
|
||||
className={clsx(
|
||||
"fixed w-full max-w-[12rem] flex flex-col gap-4 text-left pb-28 h-[calc(100vh-121px)] scrollbar-hide overflow-y-scroll",
|
||||
isProBannerVisible ? "top-32" : "top-20",
|
||||
)}
|
||||
style={{
|
||||
WebkitMaskImage: `linear-gradient(to top, transparent 0%, #000 100px, #000 ${
|
||||
scrollPosition > 30 ? "90%" : "100%"
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
"use client";
|
||||
|
||||
import {useEffect} from "react";
|
||||
|
||||
import {trackEvent} from "@/utils/va";
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
// ref: https://developers.featurebase.app/install/changelog-widget/install
|
||||
export const FbChangelogButton = ({className}: Props) => {
|
||||
useEffect(() => {
|
||||
const win = window as any;
|
||||
|
||||
if (typeof win.Featurebase !== "function") {
|
||||
win.Featurebase = function () {
|
||||
// eslint-disable-next-line prefer-rest-params
|
||||
(win.Featurebase.q = win.Featurebase.q || []).push(arguments);
|
||||
};
|
||||
}
|
||||
win.Featurebase("initialize_changelog_widget", {
|
||||
organization: process.env.NEXT_PUBLIC_FB_FEEDBACK_ORG,
|
||||
theme: "dark",
|
||||
usersName: "",
|
||||
fullscreenPopup: true,
|
||||
alwaysShow: true,
|
||||
});
|
||||
}, []);
|
||||
|
||||
const fbButtonOnClick = () => {
|
||||
(window as any).Featurebase("manually_open_changelog_popup");
|
||||
|
||||
trackEvent("Featurebase - Changelog", {
|
||||
name: "featurebase-changelog",
|
||||
action: "press",
|
||||
category: "featurebase",
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<button className={className} onClick={fbButtonOnClick}>
|
||||
Changelog <span id="fb-update-badge" />
|
||||
</button>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
"use client";
|
||||
|
||||
import {useEffect} from "react";
|
||||
|
||||
import {trackEvent} from "@/utils/va";
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
// ref: https://developers.featurebase.app/install/feedback-widget/setup
|
||||
export const FbFeedbackButton = ({className}: Props) => {
|
||||
useEffect(() => {
|
||||
const win = window as any;
|
||||
|
||||
if (typeof win.Featurebase !== "function") {
|
||||
win.Featurebase = function () {
|
||||
// eslint-disable-next-line prefer-rest-params
|
||||
(win.Featurebase.q = win.Featurebase.q || []).push(arguments);
|
||||
};
|
||||
}
|
||||
win.Featurebase("initialize_feedback_widget", {
|
||||
organization: process.env.NEXT_PUBLIC_FB_FEEDBACK_ORG,
|
||||
theme: "dark",
|
||||
email: "",
|
||||
});
|
||||
}, []);
|
||||
|
||||
const fbButtonOnClick = () => {
|
||||
trackEvent("Featurebase - Feedback", {
|
||||
name: "featurebase-feedback",
|
||||
action: "press",
|
||||
category: "featurebase",
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<button data-featurebase-feedback className={className} onClick={fbButtonOnClick}>
|
||||
Feedback
|
||||
</button>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
"use client";
|
||||
|
||||
import NextLink from "next/link";
|
||||
import arrowRightUpIcon from "@iconify/icons-solar/arrow-right-up-linear";
|
||||
import {Icon} from "@iconify/react/dist/offline";
|
||||
import {clsx} from "@nextui-org/shared-utils";
|
||||
|
||||
import {trackEvent} from "@/utils/va";
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
innerClassName?: string;
|
||||
};
|
||||
|
||||
export const FbRoadmapLink = ({className, innerClassName}: Props) => {
|
||||
const fbLinkOnClick = () => {
|
||||
trackEvent("Featurebase - Roadmap", {
|
||||
name: "featurebase-roadmap",
|
||||
action: "press",
|
||||
category: "featurebase",
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<NextLink
|
||||
className={clsx("inline-flex items-center", className)}
|
||||
color="foreground"
|
||||
href={`${process.env.NEXT_PUBLIC_FB_FEEDBACK_URL}/roadmap`}
|
||||
target="_blank"
|
||||
onClick={fbLinkOnClick}
|
||||
>
|
||||
<div className={clsx("relative", innerClassName)}>
|
||||
Roadmap
|
||||
<Icon
|
||||
className="absolute right-[-10px] top-0 outline-none transition-transform group-data-[hover=true]:translate-y-0.5 [&>path]:stroke-[2.5px]"
|
||||
icon={arrowRightUpIcon}
|
||||
width={10}
|
||||
/>
|
||||
</div>
|
||||
</NextLink>
|
||||
);
|
||||
};
|
||||
@@ -3,9 +3,8 @@
|
||||
import dynamic from "next/dynamic";
|
||||
import {usePathname} from "next/navigation";
|
||||
|
||||
import {siteConfig} from "@/config/site";
|
||||
import {getCurrentYear} from "@/utils/time";
|
||||
|
||||
const Link = dynamic(() => import("@nextui-org/react").then((mod) => mod.Link));
|
||||
const VercelCallout = dynamic(() => import("./vercel-callout").then((mod) => mod.VercelCallout));
|
||||
|
||||
export const Footer = () => {
|
||||
@@ -18,12 +17,7 @@ export const Footer = () => {
|
||||
return (
|
||||
<footer className="container mx-auto max-w-7xl pb-12 px-12">
|
||||
<div className="flex flex-col justify-center items-center gap-1">
|
||||
<p className="text-sm text-default-400">
|
||||
Created by
|
||||
<Link isExternal className="text-sm" href={siteConfig.links.portfolio}>
|
||||
Junior Garcia
|
||||
</Link>
|
||||
</p>
|
||||
<p className="text-sm text-default-400">© {getCurrentYear()} NextUI Inc.</p>
|
||||
<VercelCallout />
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import {IconSvgProps} from "@/types";
|
||||
|
||||
export const CLIBoldIcon = ({...props}: IconSvgProps) => (
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="none"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
role="presentation"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
clipRule="evenodd"
|
||||
d="M3.464 3.464C2 4.93 2 7.286 2 12c0 4.714 0 7.071 1.464 8.535C4.93 22 7.286 22 12 22c4.714 0 7.071 0 8.535-1.465C22 19.072 22 16.714 22 12s0-7.071-1.465-8.536C19.072 2 16.714 2 12 2S4.929 2 3.464 3.464m2.96 6.056a.75.75 0 0 1 1.056-.096l.277.23c.605.504 1.12.933 1.476 1.328c.379.42.674.901.674 1.518s-.295 1.099-.674 1.518c-.356.395-.871.824-1.476 1.328l-.277.23a.75.75 0 1 1-.96-1.152l.234-.195c.659-.55 1.09-.91 1.366-1.216c.262-.29.287-.427.287-.513c0-.086-.025-.222-.287-.513c-.277-.306-.707-.667-1.366-1.216l-.234-.195a.75.75 0 0 1-.096-1.056M17.75 15a.75.75 0 0 1-.75.75h-5a.75.75 0 0 1 0-1.5h5a.75.75 0 0 1 .75.75"
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
@@ -16,3 +16,4 @@ export * from "./hash";
|
||||
export * from "./more-square";
|
||||
export * from "./play";
|
||||
export * from "./pause";
|
||||
export * from "./cli";
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import {IconSvgProps} from "@/types";
|
||||
|
||||
export const CLILinearIcon = ({...props}: IconSvgProps) => (
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="none"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
role="presentation"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<g fill="none" stroke="currentColor" strokeWidth="1.5">
|
||||
<path d="M2 12c0-4.714 0-7.071 1.464-8.536C4.93 2 7.286 2 12 2c4.714 0 7.071 0 8.535 1.464C22 4.93 22 7.286 22 12c0 4.714 0 7.071-1.465 8.535C19.072 22 16.714 22 12 22s-7.071 0-8.536-1.465C2 19.072 2 16.714 2 12Z" />
|
||||
<path
|
||||
d="M17 15h-5m-5-5l.234.195c1.282 1.068 1.923 1.602 1.923 2.305c0 .703-.64 1.237-1.923 2.305L7 15"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
@@ -20,3 +20,4 @@ export * from "./chevron-right";
|
||||
export * from "./search";
|
||||
export * from "./simple-grid";
|
||||
export * from "./rotate-left";
|
||||
export * from "./cli";
|
||||
|
||||
@@ -223,6 +223,44 @@ const AstroIcon: React.FC<IconSvgProps> = ({width = "1em", height = "1em", ...pr
|
||||
);
|
||||
};
|
||||
|
||||
const BunIcon: React.FC<IconSvgProps> = ({width = "1em", height = "1em", ...props}) => {
|
||||
return (
|
||||
<svg aria-hidden="true" height={height} viewBox="0 0 32 32" width={width} {...props}>
|
||||
<path
|
||||
d="M29 17c0 5.65-5.82 10.23-13 10.23S3 22.61 3 17c0-3.5 2.24-6.6 5.66-8.44S14.21 4.81 16 4.81s3.32 1.54 7.34 3.71C26.76 10.36 29 13.46 29 17"
|
||||
fill="#fbf0df"
|
||||
/>
|
||||
<path
|
||||
d="M16 27.65c7.32 0 13.46-4.65 13.46-10.65c0-3.72-2.37-7-5.89-8.85c-1.39-.75-2.46-1.41-3.37-2l-1.13-.69A6.14 6.14 0 0 0 16 4.35a6.88 6.88 0 0 0-3.3 1.23c-.42.24-.86.51-1.32.8c-.87.54-1.83 1.13-3 1.73C4.91 10 2.54 13.24 2.54 17c0 6 6.14 10.65 13.46 10.65Z"
|
||||
fill="none"
|
||||
stroke="#000"
|
||||
/>
|
||||
<ellipse cx="21.65" cy="18.62" fill="#febbd0" rx="2.17" ry="1.28" />
|
||||
<ellipse cx="10.41" cy="18.62" fill="#febbd0" rx="2.17" ry="1.28" />
|
||||
<path
|
||||
d="M11.43 18.11a2 2 0 1 0-2-2.05a2.05 2.05 0 0 0 2 2.05m9.2 0a2 2 0 1 0-2-2.05a2 2 0 0 0 2 2.05"
|
||||
fillRule="evenodd"
|
||||
/>
|
||||
<path
|
||||
d="M10.79 16.19a.77.77 0 1 0-.76-.77a.76.76 0 0 0 .76.77m9.2 0a.77.77 0 1 0 0-1.53a.77.77 0 0 0 0 1.53"
|
||||
fill="#fff"
|
||||
fillRule="evenodd"
|
||||
/>
|
||||
<path
|
||||
d="M18.62 19.67a3.3 3.3 0 0 1-1.09 1.75a2.48 2.48 0 0 1-1.5.69a2.53 2.53 0 0 1-1.5-.69a3.28 3.28 0 0 1-1.08-1.75a.26.26 0 0 1 .29-.3h4.58a.27.27 0 0 1 .3.3Z"
|
||||
fill="#b71422"
|
||||
stroke="#000"
|
||||
strokeWidth=".75"
|
||||
/>
|
||||
<path
|
||||
d="M14.93 5.75a6.09 6.09 0 0 1-2.09 4.62c-.1.09 0 .27.11.22c1.25-.49 2.94-1.94 2.23-4.88c-.03-.15-.25-.11-.25.04m.85 0a6 6 0 0 1 .57 5c0 .13.12.24.21.13c.83-1 1.54-3.11-.59-5.31c-.1-.11-.27.04-.19.17Zm1-.06a6.09 6.09 0 0 1 2.53 4.38c0 .14.21.17.24 0c.34-1.3.15-3.51-2.66-4.66c-.12-.02-.21.18-.09.27ZM9.94 9.55a6.27 6.27 0 0 0 3.89-3.33c.07-.13.28-.08.25.07c-.64 3-2.79 3.59-4.13 3.51c-.14-.01-.14-.21-.01-.25"
|
||||
fill="#ccbea7"
|
||||
fillRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
const NewNextJSIcon: React.FC<IconSvgProps> = ({width = "1em", height = "1em", ...props}) => {
|
||||
return (
|
||||
<svg aria-hidden="true" fill="none" focusable="false" height={height} width={width} {...props}>
|
||||
@@ -452,4 +490,5 @@ export {
|
||||
CodeSandboxIcon,
|
||||
JavascriptIcon,
|
||||
TypescriptIcon,
|
||||
BunIcon,
|
||||
};
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -14,7 +14,6 @@ import {PaletteIcon, MagicIcon, GamingConsoleIcon, StarIcon} from "@/components/
|
||||
import {NextUILogo, CodeWindow} from "@/components";
|
||||
import landingContent from "@/content/landing";
|
||||
import {useIsMobile} from "@/hooks/use-media-query";
|
||||
import {trackEvent} from "@/utils/va";
|
||||
|
||||
const themesTabs = (isMobile: boolean) => [
|
||||
{
|
||||
@@ -88,12 +87,6 @@ const CustomThemesExample = ({
|
||||
|
||||
const onSelectionChange = (value: React.Key) => {
|
||||
onChangeTheme(value as Theme);
|
||||
|
||||
trackEvent("CustomThemes - Selection", {
|
||||
action: "change_theme",
|
||||
category: "landing-page",
|
||||
data: value,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
"use client";
|
||||
|
||||
import NextLink from "next/link";
|
||||
import {Button, Link, Chip} from "@nextui-org/react";
|
||||
import {Button, Link, Chip, Snippet} from "@nextui-org/react";
|
||||
import {ArrowRightIcon} from "@nextui-org/shared-icons";
|
||||
import dynamic from "next/dynamic";
|
||||
|
||||
import {FloatingComponents} from "./floating-components";
|
||||
|
||||
import {title, subtitle} from "@/components/primitives";
|
||||
import {GithubIcon} from "@/components/icons";
|
||||
import {title, subtitle} from "@/components/primitives";
|
||||
import {trackEvent} from "@/utils/va";
|
||||
|
||||
const BgLooper = dynamic(() => import("./bg-looper").then((mod) => mod.BgLooper), {
|
||||
@@ -31,14 +31,14 @@ export const Hero = () => {
|
||||
<div className="w-full flex justify-center md:hidden">
|
||||
<Chip
|
||||
as={NextLink}
|
||||
className="w-full hover:bg-default-100 border-default-200/80 dark:border-default-100/80 transition-colors cursor-pointer"
|
||||
color="secondary"
|
||||
href="/blog/v2.2.0"
|
||||
className="bg-default-100/50 hover:bg-default-100 border-default-200/80 dark:border-default-100/80 transition-colors cursor-pointer"
|
||||
color="default"
|
||||
href="/blog/v2.3.0"
|
||||
variant="dot"
|
||||
onClick={() => handlePressAnnouncement("Introducing v2.2.0", "/blog/v2.2.0")}
|
||||
onClick={() => handlePressAnnouncement("New version v2.4.0", "/blog/v2.4.0")}
|
||||
>
|
||||
Introducing v2.2.0
|
||||
<span aria-label="rocket emoji" role="img">
|
||||
New version v2.4.0
|
||||
<span aria-label="emoji" role="img">
|
||||
🚀
|
||||
</span>
|
||||
</Chip>
|
||||
@@ -56,7 +56,7 @@ export const Hero = () => {
|
||||
<div className="flex flex-col md:flex-row items-center gap-4">
|
||||
<Button
|
||||
as={NextLink}
|
||||
className="w-full md:w-auto"
|
||||
className="w-full md:h-11 md:w-auto"
|
||||
color="primary"
|
||||
endContent={
|
||||
<ArrowRightIcon
|
||||
@@ -78,12 +78,27 @@ export const Hero = () => {
|
||||
>
|
||||
Get Started
|
||||
</Button>
|
||||
|
||||
<Snippet
|
||||
className="w-full rounded-full hidden md:flex sm:w-auto"
|
||||
copyButtonProps={{
|
||||
radius: "full",
|
||||
}}
|
||||
onCopy={() => {
|
||||
trackEvent("Hero - Copy Install Command", {
|
||||
name: "Copy",
|
||||
action: "click",
|
||||
category: "landing-page",
|
||||
data: "npx nextui-cli@latest init",
|
||||
});
|
||||
}}
|
||||
>
|
||||
npx nextui-cli@latest init
|
||||
</Snippet>
|
||||
<Button
|
||||
fullWidth
|
||||
isExternal
|
||||
as={Link}
|
||||
className="w-full md:w-auto"
|
||||
className="w-full md:hidden"
|
||||
href="https://github.com/nextui-org/nextui"
|
||||
radius="full"
|
||||
size="lg"
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
"use client";
|
||||
|
||||
import {Link} from "@nextui-org/react";
|
||||
|
||||
import {trackEvent} from "@/utils/va";
|
||||
|
||||
export type Sponsor = {
|
||||
name: string;
|
||||
href: string;
|
||||
logo: React.ReactNode;
|
||||
};
|
||||
|
||||
export const SponsorItem = ({name, href, logo}: Sponsor) => {
|
||||
return (
|
||||
<Link
|
||||
isExternal
|
||||
className="flex flex-col items-center justify-center"
|
||||
href={href}
|
||||
onClick={() => {
|
||||
trackEvent("Hero - Sponsors", {
|
||||
name,
|
||||
action: "click",
|
||||
category: "hero",
|
||||
});
|
||||
}}
|
||||
>
|
||||
{logo}
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,46 @@
|
||||
import {Button, Link} from "@nextui-org/react";
|
||||
|
||||
import {sectionWrapper} from "@/components/primitives";
|
||||
import {Story2DesignLogo} from "@/components/icons/sponsors";
|
||||
import {HeartFilledIcon} from "@/components/icons";
|
||||
import {VercelIcon} from "@/components/icons/social";
|
||||
import {siteConfig} from "@/config/site";
|
||||
import {Sponsor, SponsorItem} from "@/components/marketing/sponsor-item";
|
||||
|
||||
const sponsors: Sponsor[] = [
|
||||
{
|
||||
name: "Vercel",
|
||||
href: "https://www.vercel.com?utm_source=nextui&utm_marketing=oss",
|
||||
logo: <VercelIcon className="text-black dark:text-white" height={24} />,
|
||||
},
|
||||
{
|
||||
name: "story.to.design",
|
||||
href: "https://story.to.design?utm_source=nextui&utm_marketing=partnership",
|
||||
logo: <Story2DesignLogo className="pt-1" />,
|
||||
},
|
||||
];
|
||||
|
||||
export const Sponsors = () => {
|
||||
return (
|
||||
<section className={sectionWrapper({class: "text-center mt-24 lg:mt-32"})}>
|
||||
<h3 className="text-large text-default-500">Supported and backed by</h3>
|
||||
<div className="w-full flex flex-wrap gap-x-5 gap-y-3 justify-center items-center">
|
||||
{sponsors.map((sponsor) => (
|
||||
<SponsorItem key={sponsor.name} {...sponsor} />
|
||||
))}
|
||||
<Button
|
||||
isExternal
|
||||
as={Link}
|
||||
className="group text-sm border-dashed font-normal text-default-600 data-[hover=true]:bg-default-100/50"
|
||||
href={siteConfig.links.sponsor}
|
||||
startContent={
|
||||
<HeartFilledIcon className="text-danger group-data-[hover=true]:animate-heartbeat" />
|
||||
}
|
||||
variant="bordered"
|
||||
>
|
||||
Your Company
|
||||
</Button>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
@@ -11,7 +11,7 @@ import {CarbonAd} from "@/components/ads/carbon-ad";
|
||||
import * as DocsComponents from "@/components/docs/components";
|
||||
import * as BlogComponents from "@/components/blog/components";
|
||||
import {Codeblock} from "@/components/docs/components";
|
||||
import {VirtualAnchor, virtualAnchorEncode} from "@/components";
|
||||
import {VirtualAnchor, virtualAnchorEncode} from "@/components/virtual-anchor";
|
||||
import {trackEvent} from "@/utils/va";
|
||||
|
||||
const Table: React.FC<{children?: React.ReactNode}> = ({children}) => {
|
||||
@@ -105,7 +105,7 @@ const List: React.FC<{children?: React.ReactNode}> = ({children}) => {
|
||||
|
||||
const InlineCode = ({children}: {children?: React.ReactNode}) => {
|
||||
return (
|
||||
<Components.Code className="font-normal bg-transparent px-0 py-0 text-code-mdx">
|
||||
<Components.Code className="font-normal text-default-700 bg-default-200/50 dark:bg-default-100/60 px-2 py-0.5">
|
||||
{children}
|
||||
</Components.Code>
|
||||
);
|
||||
@@ -149,7 +149,6 @@ const Code = ({
|
||||
trackEvent("MDXComponents - Copy", {
|
||||
category: "docs",
|
||||
action: "copyCode",
|
||||
data: codeString,
|
||||
});
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -34,16 +34,11 @@ import {currentVersion} from "@/utils/version";
|
||||
import {siteConfig} from "@/config/site";
|
||||
import {Route} from "@/libs/docs/page";
|
||||
import {LargeLogo, SmallLogo, ThemeSwitch} from "@/components";
|
||||
import {
|
||||
TwitterIcon,
|
||||
GithubIcon,
|
||||
DiscordIcon,
|
||||
HeartFilledIcon,
|
||||
SearchLinearIcon,
|
||||
} from "@/components/icons";
|
||||
import {TwitterIcon, GithubIcon, DiscordIcon, SearchLinearIcon} from "@/components/icons";
|
||||
import {useIsMounted} from "@/hooks/use-is-mounted";
|
||||
import {DocsSidebar} from "@/components/docs/sidebar";
|
||||
import {useCmdkStore} from "@/components/cmdk";
|
||||
import {FbRoadmapLink} from "@/components/featurebase/fb-roadmap-link";
|
||||
import {trackEvent} from "@/utils/va";
|
||||
|
||||
export interface NavbarProps {
|
||||
@@ -242,7 +237,21 @@ export const Navbar: FC<NavbarProps> = ({children, routes, mobileRoutes = [], sl
|
||||
Figma
|
||||
</NextLink>
|
||||
</NavbarItem>
|
||||
{/* hide feedback and changelog at this moment */}
|
||||
{/* <NavbarItem>
|
||||
<NextLink className={navLinkClasses} color="foreground" href="#">
|
||||
<FbChangelogButton key="changelog" userName="" />
|
||||
</NextLink>
|
||||
</NavbarItem>
|
||||
<NavbarItem>
|
||||
<NextLink className={navLinkClasses} color="foreground" href="#">
|
||||
<FbFeedbackButton key="feedback" userEmail="" />
|
||||
</NextLink>
|
||||
</NavbarItem> */}
|
||||
<NavbarItem>
|
||||
<FbRoadmapLink className={navLinkClasses} />
|
||||
</NavbarItem>
|
||||
{/* <NavbarItem>
|
||||
<Chip
|
||||
as={NextLink}
|
||||
className="hover:bg-default-100 border-default-200/80 dark:border-default-100/80 transition-colors cursor-pointer"
|
||||
@@ -256,7 +265,7 @@ export const Navbar: FC<NavbarProps> = ({children, routes, mobileRoutes = [], sl
|
||||
🚀
|
||||
</span>
|
||||
</Chip>
|
||||
</NavbarItem>
|
||||
</NavbarItem> */}
|
||||
</ul>
|
||||
</NavbarContent>
|
||||
|
||||
@@ -298,6 +307,21 @@ export const Navbar: FC<NavbarProps> = ({children, routes, mobileRoutes = [], sl
|
||||
</NavbarContent>
|
||||
|
||||
<NavbarContent className="hidden sm:flex basis-1/5 sm:basis-full" justify="end">
|
||||
<NavbarItem className="hidden sm:flex">
|
||||
<Chip
|
||||
as={NextLink}
|
||||
className="bg-default-100/50 hover:bg-default-100 border-default-200/80 dark:border-default-100/80 transition-colors cursor-pointer"
|
||||
color="default"
|
||||
href="/blog/v2.4.0"
|
||||
variant="dot"
|
||||
onClick={() => handlePressNavbarItem("New version v2.4.0", "/blog/v2.4.0")}
|
||||
>
|
||||
New version v2.4.0
|
||||
<span aria-label="emoji" role="img">
|
||||
🚀
|
||||
</span>
|
||||
</Chip>
|
||||
</NavbarItem>
|
||||
<NavbarItem className="hidden sm:flex">
|
||||
<Link
|
||||
isExternal
|
||||
@@ -329,7 +353,7 @@ export const Navbar: FC<NavbarProps> = ({children, routes, mobileRoutes = [], sl
|
||||
<ThemeSwitch />
|
||||
</NavbarItem>
|
||||
<NavbarItem className="hidden lg:flex">{searchButton}</NavbarItem>
|
||||
<NavbarItem className="hidden md:flex">
|
||||
{/* <NavbarItem className="hidden md:flex">
|
||||
<Button
|
||||
isExternal
|
||||
as={Link}
|
||||
@@ -343,7 +367,7 @@ export const Navbar: FC<NavbarProps> = ({children, routes, mobileRoutes = [], sl
|
||||
>
|
||||
Sponsor
|
||||
</Button>
|
||||
</NavbarItem>
|
||||
</NavbarItem> */}
|
||||
<NavbarMenuToggle
|
||||
aria-label={isMenuOpen ? "Close menu" : "Open menu"}
|
||||
className="hidden sm:flex lg:hidden ml-4"
|
||||
@@ -351,7 +375,12 @@ export const Navbar: FC<NavbarProps> = ({children, routes, mobileRoutes = [], sl
|
||||
</NavbarContent>
|
||||
|
||||
<NavbarMenu>
|
||||
<DocsSidebar className="mt-4" routes={[...mobileRoutes, ...routes]} slug={slug} tag={tag} />
|
||||
<DocsSidebar
|
||||
className="mt-4 pt-8"
|
||||
routes={[...mobileRoutes, ...routes]}
|
||||
slug={slug}
|
||||
tag={tag}
|
||||
/>
|
||||
{children}
|
||||
</NavbarMenu>
|
||||
</NextUINavbar>
|
||||
|
||||
@@ -17,9 +17,10 @@ export const title = tv({
|
||||
foreground: "dark:from-[#FFFFFF] dark:to-[#4B4B4B]",
|
||||
},
|
||||
size: {
|
||||
sm: "text-2xl lg:text-4xl",
|
||||
md: "text-[2.1rem] lg:text-5xl",
|
||||
lg: "text-3xl lg:text-6xl",
|
||||
sm: "text-3xl lg:text-4xl",
|
||||
md: "text-[2.5rem] lg:text-5xl",
|
||||
lg: "text-4xl lg:text-6xl",
|
||||
xl: "text-5xl md:text-6xl lg:text-7xl",
|
||||
},
|
||||
fullWidth: {
|
||||
true: "w-full block",
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
"use client";
|
||||
|
||||
import {Icon} from "@iconify/react/dist/offline";
|
||||
import arrowRightIcon from "@iconify/icons-solar/arrow-right-linear";
|
||||
import {usePathname} from "next/navigation";
|
||||
import {useEffect} from "react";
|
||||
|
||||
import {trackEvent} from "@/utils/va";
|
||||
import emitter from "@/libs/emitter";
|
||||
|
||||
const hideOnPaths = ["examples"];
|
||||
|
||||
export const ProBanner = () => {
|
||||
const handleClick = () => {
|
||||
trackEvent("NextUI Pro Banner", {
|
||||
action: "click",
|
||||
category: "landing-page",
|
||||
});
|
||||
};
|
||||
|
||||
const pathname = usePathname();
|
||||
const shouldBeVisible = !hideOnPaths.some((path) => pathname.includes(path));
|
||||
|
||||
useEffect(() => {
|
||||
if (!shouldBeVisible) return;
|
||||
|
||||
// listen to scroll event, dispatch an event when scroll is at the top < 48 px
|
||||
const handleScroll = () => {
|
||||
if (window.scrollY < 48) {
|
||||
emitter.emit("proBannerVisibilityChange", "visible");
|
||||
} else {
|
||||
emitter.emit("proBannerVisibilityChange", "hidden");
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("scroll", handleScroll);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("scroll", handleScroll);
|
||||
};
|
||||
}, [shouldBeVisible]);
|
||||
|
||||
if (!shouldBeVisible) return null;
|
||||
|
||||
return (
|
||||
<div className="relative z-50 isolate flex items-center gap-x-6 overflow-hidden bg-background border-b-1 border-divider px-6 py-2 sm:px-3.5 sm:before:flex-1">
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="absolute left-[max(-7rem,calc(50%-52rem))] top-1/2 -z-10 -translate-y-1/2 transform-gpu blur-2xl"
|
||||
>
|
||||
<div
|
||||
className="aspect-[577/310] w-[36.0625rem] bg-gradient-to-r from-[#ff80b5] to-[#9089fc] dark:from-[#F54180] dark:to-[#338EF7] opacity-20 dark:opacity-10"
|
||||
style={{
|
||||
clipPath:
|
||||
"polygon(74.8% 41.9%, 97.2% 73.2%, 100% 34.9%, 92.5% 0.4%, 87.5% 0%, 75% 28.6%, 58.5% 54.6%, 50.1% 56.8%, 46.9% 44%, 48.3% 17.4%, 24.7% 53.9%, 0% 27.9%, 11.9% 74.2%, 24.9% 54.1%, 68.6% 100%, 74.8% 41.9%)",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="absolute left-[max(45rem,calc(50%+8rem))] top-1/2 -z-10 -translate-y-1/2 transform-gpu blur-2xl"
|
||||
>
|
||||
<div
|
||||
className="aspect-[577/310] w-[36.0625rem] bg-gradient-to-r from-[#ff80b5] to-[#9089fc] dark:from-[#F54180] dark:to-[#338EF7] opacity-30 dark:opacity-20"
|
||||
style={{
|
||||
clipPath:
|
||||
"polygon(74.8% 41.9%, 97.2% 73.2%, 100% 34.9%, 92.5% 0.4%, 87.5% 0%, 75% 28.6%, 58.5% 54.6%, 50.1% 56.8%, 46.9% 44%, 48.3% 17.4%, 24.7% 53.9%, 0% 27.9%, 11.9% 74.2%, 24.9% 54.1%, 68.6% 100%, 74.8% 41.9%)",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex w-full items-center justify-between md:justify-center gap-x-3">
|
||||
<a
|
||||
className="text-small flex items-end sm:text-[0.93rem] text-foreground hover:opacity-80 transition-opacity"
|
||||
href="https://nextui.pro?utm_source=nextui.org&utm_medium=top-banner"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
onClick={handleClick}
|
||||
>
|
||||
<span aria-label="rocket" className="hidden md:block" role="img">
|
||||
🚀
|
||||
</span>
|
||||
<span
|
||||
className="inline-flex md:ml-1 animate-text-gradient font-medium bg-clip-text text-transparent bg-[linear-gradient(90deg,#D6009A_0%,#8a56cc_50%,#D6009A_100%)] dark:bg-[linear-gradient(90deg,#FFEBF9_0%,#8a56cc_50%,#FFEBF9_100%)]"
|
||||
style={{
|
||||
fontSize: "inherit",
|
||||
backgroundSize: "200%",
|
||||
backgroundClip: "text",
|
||||
WebkitBackgroundClip: "text",
|
||||
color: "transparent",
|
||||
}}
|
||||
>
|
||||
Ship faster with beautiful components
|
||||
</span>
|
||||
</a>
|
||||
<a
|
||||
className="flex group min-w-[120px] items-center font-semibold text-foreground shadow-sm gap-1.5 relative overflow-hidden rounded-full p-[1px] focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary"
|
||||
href="https://nextui.pro?utm_source=nextui.org&utm_medium=top-banner"
|
||||
rel="noopener noreferrer"
|
||||
onClick={handleClick}
|
||||
>
|
||||
<span className="absolute inset-[-1000%] animate-[spin_3s_linear_infinite] bg-[conic-gradient(from_90deg_at_50%_50%,#F54180_0%,#338EF7_50%,#F54180_100%)]" />
|
||||
<div className="inline-flex h-full w-full cursor-pointer items-center justify-center rounded-full bg-background group-hover:bg-background/70 transition-background px-3 py-1 text-sm font-medium text-foreground backdrop-blur-3xl">
|
||||
NextUI Pro
|
||||
<Icon
|
||||
aria-hidden="true"
|
||||
className="outline-none transition-transform group-hover:translate-x-0.5 [&>path]:stroke-[2px]"
|
||||
icon={arrowRightIcon}
|
||||
width={16}
|
||||
/>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -5,7 +5,6 @@ import {capitalize, last} from "lodash";
|
||||
|
||||
import {BugIcon} from "@/components/icons";
|
||||
import {ISSUE_REPORT_URL} from "@/libs/github/constants";
|
||||
import {trackEvent} from "@/utils/va";
|
||||
|
||||
export const BugReportButton = () => {
|
||||
const pathname = usePathname();
|
||||
@@ -13,13 +12,6 @@ export const BugReportButton = () => {
|
||||
const componentTitle = capitalize(last(pathname?.split("/")));
|
||||
|
||||
const handlePress = () => {
|
||||
trackEvent("BugReportButton - Sandpack", {
|
||||
name: "sandpack - bug report",
|
||||
action: "press",
|
||||
category: "docs",
|
||||
data: `${ISSUE_REPORT_URL}${componentTitle}`,
|
||||
});
|
||||
|
||||
window.open(`${ISSUE_REPORT_URL}${componentTitle}`, "_blank");
|
||||
};
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ import {Language} from "prism-react-renderer";
|
||||
import {HighlightedLines} from "./types";
|
||||
import {Decorators} from "./types";
|
||||
|
||||
import {trackEvent} from "@/utils/va";
|
||||
import {Codeblock} from "@/components/docs/components";
|
||||
|
||||
export interface CodeViewerProps {
|
||||
@@ -84,13 +83,6 @@ export const SandpackCodeViewer = React.forwardRef<any, CodeViewerProps>(
|
||||
const handleExpand = () => {
|
||||
const nextIsExpanded = !isExpanded;
|
||||
|
||||
trackEvent("CodeViewer - Expand", {
|
||||
name: activeFile,
|
||||
action: "expand",
|
||||
category: "docs",
|
||||
data: nextIsExpanded ? "expanded" : "collapsed",
|
||||
});
|
||||
|
||||
setIsExpanded(nextIsExpanded);
|
||||
if (containerRef && containerRef?.current !== null) {
|
||||
const container = containerRef?.current;
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
import React from "react";
|
||||
import {UnstyledOpenInCodeSandboxButton} from "@codesandbox/sandpack-react";
|
||||
import {Tooltip, Button} from "@nextui-org/react";
|
||||
import {useSandpack} from "@codesandbox/sandpack-react";
|
||||
|
||||
import {CodeSandboxIcon} from "@/components/icons";
|
||||
import {trackEvent} from "@/utils/va";
|
||||
|
||||
export const CodeSandboxButton = () => {
|
||||
const {sandpack} = useSandpack();
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
className="text-xs px-2"
|
||||
@@ -17,20 +13,7 @@ export const CodeSandboxButton = () => {
|
||||
placement="top"
|
||||
radius="md"
|
||||
>
|
||||
<Button
|
||||
isIconOnly
|
||||
as="span"
|
||||
size="sm"
|
||||
title="Open in CodeSandbox"
|
||||
variant="light"
|
||||
onPress={() => {
|
||||
trackEvent("CodeSandboxButton - Sandpack", {
|
||||
action: "press",
|
||||
category: "docs",
|
||||
data: sandpack.files[sandpack.activeFile],
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Button isIconOnly as="span" size="sm" title="Open in CodeSandbox" variant="light">
|
||||
<UnstyledOpenInCodeSandboxButton
|
||||
style={{
|
||||
width: "100%",
|
||||
|
||||
@@ -3,7 +3,6 @@ import {useSandpack} from "@codesandbox/sandpack-react";
|
||||
import {Tooltip, Button} from "@nextui-org/react";
|
||||
import {useClipboard} from "@nextui-org/use-clipboard";
|
||||
|
||||
import {trackEvent} from "@/utils/va";
|
||||
import {CopyLinearIcon} from "@/components/icons";
|
||||
|
||||
export const CopyButton = () => {
|
||||
@@ -15,13 +14,6 @@ export const CopyButton = () => {
|
||||
const code = sandpack.files[sandpack.activeFile].code;
|
||||
|
||||
copy(code);
|
||||
|
||||
trackEvent("CopyButton - Sandpack", {
|
||||
name: "sandpack - copy code",
|
||||
action: "press",
|
||||
category: "docs",
|
||||
data: sandpack.activeFile,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -3,7 +3,6 @@ import {Tabs, Tab} from "@nextui-org/react";
|
||||
import {SandpackPredefinedTemplate} from "@codesandbox/sandpack-react";
|
||||
|
||||
import {TypescriptIcon, JavascriptIcon} from "@/components/icons";
|
||||
import {trackEvent} from "@/utils/va";
|
||||
|
||||
interface Props {
|
||||
template: SandpackPredefinedTemplate;
|
||||
@@ -21,12 +20,6 @@ export const LanguageSelector: React.FC<LanguageSelectorProps> = ({template, onC
|
||||
setSelectedTemplate(newTemplate);
|
||||
|
||||
setTimeout(() => {
|
||||
trackEvent("LanguageSelector - Selection", {
|
||||
name: "template",
|
||||
action: "tabChange",
|
||||
category: "docs",
|
||||
data: newTemplate ?? "",
|
||||
});
|
||||
onChange?.(newTemplate);
|
||||
}, 250);
|
||||
}, [template, onChange]);
|
||||
|
||||
@@ -70,7 +70,7 @@ export const useSandpack = ({
|
||||
}, {});
|
||||
|
||||
let dependencies = {
|
||||
"framer-motion": "10.12.16",
|
||||
"framer-motion": "11.0.22",
|
||||
"@nextui-org/react": "latest",
|
||||
};
|
||||
|
||||
@@ -139,7 +139,7 @@ export const useSandpack = ({
|
||||
|
||||
// const dependencies = useMemo(() => {
|
||||
// let deps = {
|
||||
// "framer-motion": "10.12.16",
|
||||
// "framer-motion": "11.0.22",
|
||||
// };
|
||||
|
||||
// if (hasComponents) {
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import * as React from "react";
|
||||
import {Analytics} from "@vercel/analytics/react";
|
||||
import Script from "next/script";
|
||||
|
||||
import {__PROD__} from "@/utils";
|
||||
|
||||
export function ScriptProviders() {
|
||||
return (
|
||||
<>
|
||||
<Script
|
||||
id="featurebase-sdk"
|
||||
src="https://do.featurebase.app/js/sdk.js"
|
||||
strategy={"beforeInteractive"}
|
||||
/>
|
||||
<Script
|
||||
defer
|
||||
data-modal-disclaimer="This is a custom LLM for NextUI with access to all developer docs (nextui.org/docs) and GitHub Issues and PRs (github.com/nextui-org/nextui)."
|
||||
data-modal-example-questions="How do I install for Next.js?,How do I customize primary color?"
|
||||
data-project-color="#000000"
|
||||
data-project-logo="https://avatars.githubusercontent.com/u/86160567?s=280&v=4"
|
||||
data-project-name="NextUI"
|
||||
data-website-id="e733a73f-980e-4f7d-9e8b-91867453f899"
|
||||
src="https://widget.kapa.ai/kapa-widget.bundle.js"
|
||||
strategy="afterInteractive"
|
||||
/>
|
||||
{__PROD__ && <Analytics />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
+101
-29
@@ -18,6 +18,13 @@
|
||||
"keywords": "getting started, installation, start, nextui",
|
||||
"path": "/docs/guide/installation.mdx"
|
||||
},
|
||||
{
|
||||
"key": "cli",
|
||||
"title": "CLI",
|
||||
"keywords": "cli, command line interface",
|
||||
"path": "/docs/guide/cli.mdx",
|
||||
"newPost": true
|
||||
},
|
||||
{
|
||||
"key": "design-principles",
|
||||
"title": "Design Principles",
|
||||
@@ -28,8 +35,7 @@
|
||||
"key": "routing",
|
||||
"title": "Routing",
|
||||
"keywords": "client side routing, routing, browser routing, nextui, next.js router, react router, remix router",
|
||||
"path": "/docs/guide/routing.mdx",
|
||||
"newPost": true
|
||||
"path": "/docs/guide/routing.mdx"
|
||||
},
|
||||
{
|
||||
"key": "upgrade-to-v2",
|
||||
@@ -85,7 +91,8 @@
|
||||
{
|
||||
"key": "layout",
|
||||
"title": "Layout",
|
||||
"path": "/docs/customization/layout.mdx"
|
||||
"path": "/docs/customization/layout.mdx",
|
||||
"updated": true
|
||||
},
|
||||
{
|
||||
"key": "colors",
|
||||
@@ -141,14 +148,14 @@
|
||||
"key": "autocomplete",
|
||||
"title": "Autocomplete",
|
||||
"keywords": "autocomplete, auto suggest, search, typeahead",
|
||||
"path": "/docs/components/autocomplete.mdx",
|
||||
"newPost": true
|
||||
"path": "/docs/components/autocomplete.mdx"
|
||||
},
|
||||
{
|
||||
"key": "badge",
|
||||
"title": "Badge",
|
||||
"keywords": "badge, markers, status indication, count display",
|
||||
"path": "/docs/components/badge.mdx"
|
||||
"path": "/docs/components/badge.mdx",
|
||||
"updated": true
|
||||
},
|
||||
{
|
||||
"key": "button",
|
||||
@@ -160,8 +167,7 @@
|
||||
"key": "breadcrumbs",
|
||||
"title": "Breadcrumbs",
|
||||
"keywords": "breadcrumbs, navigation, path, trail, location",
|
||||
"path": "/docs/components/breadcrumbs.mdx",
|
||||
"newPost": true
|
||||
"path": "/docs/components/breadcrumbs.mdx"
|
||||
},
|
||||
{
|
||||
"key": "card",
|
||||
@@ -187,6 +193,32 @@
|
||||
"keywords": "chip, tag, label, small actionable entity",
|
||||
"path": "/docs/components/chip.mdx"
|
||||
},
|
||||
{
|
||||
"key": "code",
|
||||
"title": "Code",
|
||||
"keywords": "code, code snippet, inline code, coding",
|
||||
"path": "/docs/components/code.mdx"
|
||||
},
|
||||
{
|
||||
"key": "input",
|
||||
"title": "Input",
|
||||
"keywords": "input, text box, form field, data entry",
|
||||
"path": "/docs/components/input.mdx"
|
||||
},
|
||||
{
|
||||
"key": "date-input",
|
||||
"title": "Date Input",
|
||||
"keywords": "date-input, time, input, timezone",
|
||||
"path": "/docs/components/date-input.mdx",
|
||||
"newPost": true
|
||||
},
|
||||
{
|
||||
"key": "time-input",
|
||||
"title": "Time Input",
|
||||
"keywords": "timeinput, time, input, timezone",
|
||||
"path": "/docs/components/time-input.mdx",
|
||||
"newPost": true
|
||||
},
|
||||
{
|
||||
"key": "circular-progress",
|
||||
"title": "Circular Progress",
|
||||
@@ -194,10 +226,32 @@
|
||||
"path": "/docs/components/circular-progress.mdx"
|
||||
},
|
||||
{
|
||||
"key": "code",
|
||||
"title": "Code",
|
||||
"keywords": "code, code snippet, inline code, coding",
|
||||
"path": "/docs/components/code.mdx"
|
||||
"key": "calendar",
|
||||
"title": "Calendar",
|
||||
"keywords": "calendar, date picker, month picker, year picker",
|
||||
"path": "/docs/components/calendar.mdx",
|
||||
"newPost": true
|
||||
},
|
||||
{
|
||||
"key": "range-calendar",
|
||||
"title": "Range Calendar",
|
||||
"keywords": "range calendar, date picker, month picker, year picker",
|
||||
"path": "/docs/components/range-calendar.mdx",
|
||||
"newPost": true
|
||||
},
|
||||
{
|
||||
"key": "date-picker",
|
||||
"title": "Date Picker",
|
||||
"keywords": "date-picker, time, input, timezone",
|
||||
"path": "/docs/components/date-picker.mdx",
|
||||
"newPost": true
|
||||
},
|
||||
{
|
||||
"key": "date-range-picker",
|
||||
"title": "Date Range Picker",
|
||||
"keywords": "date-range-picker, date-picker, time, input, timezone",
|
||||
"path": "/docs/components/date-range-picker.mdx",
|
||||
"newPost": true
|
||||
},
|
||||
{
|
||||
"key": "divider",
|
||||
@@ -209,8 +263,7 @@
|
||||
"key": "dropdown",
|
||||
"title": "Dropdown",
|
||||
"keywords": "dropdown, menu, selection, option list",
|
||||
"path": "/docs/components/dropdown.mdx",
|
||||
"updated": true
|
||||
"path": "/docs/components/dropdown.mdx"
|
||||
},
|
||||
{
|
||||
"key": "image",
|
||||
@@ -218,12 +271,6 @@
|
||||
"keywords": "image, media, picture, photo, graphic display",
|
||||
"path": "/docs/components/image.mdx"
|
||||
},
|
||||
{
|
||||
"key": "input",
|
||||
"title": "Input",
|
||||
"keywords": "input, text box, form field, data entry",
|
||||
"path": "/docs/components/input.mdx"
|
||||
},
|
||||
{
|
||||
"key": "kbd",
|
||||
"title": "Kbd",
|
||||
@@ -234,15 +281,13 @@
|
||||
"key": "link",
|
||||
"title": "Link",
|
||||
"keywords": "link, navigation, href, web page connection",
|
||||
"path": "/docs/components/link.mdx",
|
||||
"updated": true
|
||||
"path": "/docs/components/link.mdx"
|
||||
},
|
||||
{
|
||||
"key": "listbox",
|
||||
"title": "Listbox",
|
||||
"keywords": "listbox, selection, option list, multiple choice",
|
||||
"path": "/docs/components/listbox.mdx",
|
||||
"updated": true
|
||||
"path": "/docs/components/listbox.mdx"
|
||||
},
|
||||
{
|
||||
"key": "modal",
|
||||
@@ -290,7 +335,8 @@
|
||||
"key": "skeleton",
|
||||
"title": "Skeleton",
|
||||
"keywords": "skeleton, loading state, placeholder, content preview",
|
||||
"path": "/docs/components/skeleton.mdx"
|
||||
"path": "/docs/components/skeleton.mdx",
|
||||
"updated": true
|
||||
},
|
||||
{
|
||||
"key": "snippet",
|
||||
@@ -326,8 +372,7 @@
|
||||
"key": "slider",
|
||||
"title": "Slider",
|
||||
"keywords": "slider, range input, value selector, sliding control",
|
||||
"path": "/docs/components/slider.mdx",
|
||||
"newPost": true
|
||||
"path": "/docs/components/slider.mdx"
|
||||
},
|
||||
{
|
||||
"key": "table",
|
||||
@@ -346,8 +391,7 @@
|
||||
"key": "textarea",
|
||||
"title": "Textarea",
|
||||
"keywords": "textarea, multi-line text input, large text field, form control",
|
||||
"path": "/docs/components/textarea.mdx",
|
||||
"updated": true
|
||||
"path": "/docs/components/textarea.mdx"
|
||||
},
|
||||
{
|
||||
"key": "tooltip",
|
||||
@@ -362,6 +406,28 @@
|
||||
"path": "/docs/components/user.mdx"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "api-references",
|
||||
"title": "API References",
|
||||
"defaultOpen": true,
|
||||
"keywords": "api references, nextui, api",
|
||||
"routes": [
|
||||
{
|
||||
"key": "cli-api",
|
||||
"title": "NextUI CLI",
|
||||
"keywords": "api references, nextui, api, cli",
|
||||
"path": "/docs/api-references/cli-api.mdx",
|
||||
"updated": true
|
||||
},
|
||||
{
|
||||
"key": "nextui-provider",
|
||||
"title": "NextUI Provider",
|
||||
"keywords": "api references, nextui, api, nextui provider",
|
||||
"path": "/docs/api-references/nextui-provider.mdx",
|
||||
"updated": true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"mobileRoutes": [
|
||||
@@ -394,6 +460,12 @@
|
||||
"title": "Figma",
|
||||
"keywords": "figma, nextui, design, ui kit",
|
||||
"path": "/figma"
|
||||
},
|
||||
{
|
||||
"key": "roadmap",
|
||||
"title": "Roadmap",
|
||||
"keywords": "roadmap, nextui",
|
||||
"path": "#"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
+3140
-963
File diff suppressed because it is too large
Load Diff
@@ -322,4 +322,4 @@ Whether it's a feature request, bug report, or a project to showcase, please get
|
||||
|
||||
## Contributing
|
||||
|
||||
PR's on NextUI are always welcome, please see our [contribution guidelines](https://github.com/nextui-org/nextui/blob/main/CONTRIBUTING.MD) to learn how you can contribute to this project.
|
||||
PR's on NextUI are always welcome, please see our [contribution guidelines](https://github.com/nextui-org/nextui/blob/main/CONTRIBUTING.md) to learn how you can contribute to this project.
|
||||
|
||||
@@ -164,4 +164,4 @@ Whether it's a feature request, bug report, or a project to showcase, please get
|
||||
|
||||
## Contributing
|
||||
|
||||
PR's on NextUI are always welcome, please see our [contribution guidelines](https://github.com/nextui-org/nextui/blob/main/CONTRIBUTING.MD) to learn how you can contribute to this project.
|
||||
PR's on NextUI are always welcome, please see our [contribution guidelines](https://github.com/nextui-org/nextui/blob/main/CONTRIBUTING.md) to learn how you can contribute to this project.
|
||||
|
||||
@@ -269,7 +269,7 @@ collapse them into a dropdown menu.
|
||||
|
||||
<CodeDemo title="Customizing the Ellipsis Item" files={breadcrumbsContent.customizingEllipsis} />
|
||||
|
||||
### Apllying custom styles
|
||||
### Applying custom styles
|
||||
|
||||
You can customize the `Breadcrumbs` component by passing custom Tailwind CSS classes to the component slots.
|
||||
|
||||
@@ -440,4 +440,4 @@ Whether it's a feature request, bug report, or a project to showcase, please get
|
||||
|
||||
## Contributing
|
||||
|
||||
PR's on NextUI are always welcome, please see our [contribution guidelines](https://github.com/nextui-org/nextui/blob/main/CONTRIBUTING.MD) to learn how you can contribute to this project.
|
||||
PR's on NextUI are always welcome, please see our [contribution guidelines](https://github.com/nextui-org/nextui/blob/main/CONTRIBUTING.md) to learn how you can contribute to this project.
|
||||
|
||||
@@ -0,0 +1,428 @@
|
||||
---
|
||||
title: "Introducing v2.3.0 🎉"
|
||||
description: "NextUI v2.3.0 is here! includes six new components, NextUI CLI, bug fixes, React Aria, and a TailwindCSS upgrade, among other enhancements."
|
||||
date: "2024-04-16"
|
||||
image: "/blog/v2.3.0.jpg"
|
||||
tags: ["nextui", "cli", "date picker", "time input", "date input", "calendar"]
|
||||
author:
|
||||
name: "Junior Garcia"
|
||||
username: "@jrgarciadev"
|
||||
link: "https://twitter.com/jrgarciadev"
|
||||
avatar: "/avatars/junior-garcia.jpeg"
|
||||
---
|
||||
|
||||
import {dateInputContent} from "@/content/components/date-input";
|
||||
import {timeInputContent} from "@/content/components/time-input";
|
||||
import {calendarContent} from "@/content/components/calendar";
|
||||
import {rangeCalendarContent} from "@/content/components/range-calendar";
|
||||
import {datePickerContent} from "@/content/components/date-picker";
|
||||
import {dateRangePickerContent} from "@/content/components/date-range-picker";
|
||||
|
||||
<img
|
||||
src="/blog/v2.3.0_2x.jpg"
|
||||
width={700}
|
||||
height={350}
|
||||
alt="NextUI v2.3.0"
|
||||
className="w-full border border-transparent dark:border-default-200/50 object-fit rounded-xl shadow-lg"
|
||||
/>
|
||||
|
||||
We are excited to announce the latest update to NextUI, version **2.3.0**! This release introduces 6 new components,
|
||||
our new CLI, and several enhancements and bug fixes.
|
||||
|
||||
## What's New in v2.3.0?
|
||||
|
||||
- [NextUI CLI](/docs/guide/cli) - A command-line interface for creating and managing NextUI projects.
|
||||
- [DateInput](/docs/components/date-input) - Allows users to enter and edit date and time values using a keyboard.
|
||||
- [TimeInput](/docs/components/time-input) - Allows users to enter and edit time values using a keyboard.
|
||||
- [Calendar](/docs/components/calendar) - Displays a calendar for selecting dates and times.
|
||||
- [RangeCalendar](/docs/components/range-calendar) - Displays a calendar for selecting date ranges.
|
||||
- [DatePicker](/docs/components/date-picker) - Allows users to select a date from a calendar.
|
||||
- [DateRangePicker](/docs/components/date-range-picker) - Allows users to select a date range from a calendar.
|
||||
- [Other Changes](#other-changes) - Includes styling improvements, accessibility and usability enhancements.
|
||||
|
||||
<Spacer y={4} />
|
||||
|
||||
Requirements:
|
||||
|
||||
- [Tailwind CSS 3.4](https://tailwindcss.com/) or later
|
||||
|
||||
Upgrade today by running one of the following commands:
|
||||
|
||||
<Spacer y={4} />
|
||||
|
||||
<PackageManagers
|
||||
commands={{
|
||||
cli: "nextui upgrade",
|
||||
npm: "npx nextui-cli@latest upgrade",
|
||||
}}
|
||||
/>
|
||||
|
||||
<Spacer y={4} />
|
||||
|
||||
## NextUI CLI
|
||||
|
||||
We are thrilled to introduce the [NextUI CLI](https://github.com/nextui-org/nextui-cli), a command-line interface, It offers a comprehensive suite
|
||||
of commands to initialize, manage, and improve your NextUI projects. It enables you to `add`, `remove`, or
|
||||
`upgrade` individual components, assess the health of your project, and more.
|
||||
|
||||
### Installation
|
||||
|
||||
To install the CLI globally, execute one of the following commands in your terminal:
|
||||
|
||||
<PackageManagers
|
||||
commands={{
|
||||
npm: "npm install nextui-cli -g",
|
||||
yarn: "yarn add nextui-cli -g",
|
||||
pnpm: "pnpm add nextui-cli -g",
|
||||
bun: "bun add nextui-cli -g",
|
||||
}}
|
||||
/>
|
||||
|
||||
Alternatively, you can use the CLI without a global installation by employing `npx`:
|
||||
|
||||
```bash
|
||||
npx nextui-cli@latest
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
Once the CLI is installed, run the following command to display available commands:
|
||||
|
||||
```bash
|
||||
nextui
|
||||
```
|
||||
|
||||
NextUI CLI can help you create new projects, add components, upgrade components, remove components, detect issues in
|
||||
you setup, know your environment, and more.
|
||||
|
||||
<Spacer y={4} />
|
||||
|
||||
To initialize a new project, you can simply run:
|
||||
|
||||
```bash
|
||||
nextui init my-nextui-app
|
||||
```
|
||||
|
||||
<Spacer y={4} />
|
||||
|
||||
You will be prompted to configure your project:
|
||||
|
||||
```codeBlock bash
|
||||
? Select a template › - Use arrow-keys. Return to submit.
|
||||
❯ App
|
||||
A Next.js 13 with app directory template pre-configured with NextUI (v2) and Tailwind CSS.
|
||||
Pages
|
||||
A Next.js 13 with pages directory template pre-configured with NextUI (v2) and Tailwind CSS.
|
||||
```
|
||||
|
||||
Select the template you want to use and the CLI will create a new project for you.
|
||||
|
||||
> We're working on adding more templates to the CLI, so stay tuned for updates!
|
||||
|
||||
<Spacer y={4} />
|
||||
|
||||
If you already have a NextUI project, you can add components to it using the `add` command:
|
||||
|
||||
```bash
|
||||
nextui add date-input
|
||||
```
|
||||
|
||||
It will automatically detect the required dependencies, modify your `tailwind.config.(js|ts)` file,
|
||||
detect whether using `pnpm` if so, add the required configuration to your `.npmrc` file and add the component to your project.
|
||||
|
||||
If instead of installing a single component you want to install multiple components, you can do so by separating them with a space:
|
||||
|
||||
```bash
|
||||
nextui add date-input time-input calendar
|
||||
```
|
||||
|
||||
You can alternatively install the `main` package which includes all the components by passing the `--all` flag:
|
||||
|
||||
```bash
|
||||
nextui add --all
|
||||
```
|
||||
|
||||
<Spacer y={4} />
|
||||
|
||||
> The CLI is currentl in `Alpha` stage, we're working on adding more features and improvements. If you find any issues or have any suggestions, please let us know by [opening an issue](https://github.com/nextui-org/nextui-cli/issues/new).
|
||||
|
||||
To learn more about the CLI and its commands, please refer to the [CLI documentation](/docs/guide/cli) and the [CLI API reference](/docs/api-references/cli-api).
|
||||
|
||||
## New Components
|
||||
|
||||
Since the beginning of NextUI, devs have been asking for date and time input components. After
|
||||
months of iteration and development, we are excited to introduce the following new components:
|
||||
|
||||
### DateInput
|
||||
|
||||
DateInput is a component that allows users to enter and edit date and time values using a keyboard.
|
||||
Each part of a date value is displayed in an individually editable segment.
|
||||
|
||||
<CodeDemo title="Usage" files={dateInputContent.usage} />
|
||||
|
||||
Go to the [DateInput documentation](/docs/components/date-input) to learn more about the component.
|
||||
|
||||
### TimeInput
|
||||
|
||||
The `TimeInput` component consists of a label, and a group of segments representing each unit of a time (e.g. hours, minutes, and seconds). Each segment is individually focusable and editable by the user, by typing or using the arrow keys to increment and decrement the value. This approach allows values to be formatted and parsed correctly regardless of the locale or time format, and offers an easy and error-free way to edit times using the keyboard.
|
||||
|
||||
<CodeDemo title="Usage" files={timeInputContent.usage} />
|
||||
|
||||
Go to the [TimeInput documentation](/docs/components/time-input) to learn more about the component.
|
||||
|
||||
### Calendar
|
||||
|
||||
A Calendar consists of a grouping element containing one or more date grids (e.g. months), and a previous and next button for navigating between date ranges. Each calendar grid consists of cells containing button elements that can be pressed and navigated to using the arrow keys to select a date.
|
||||
|
||||
<CodeDemo title="Usage" files={calendarContent.usage} />
|
||||
|
||||
The calendar also supports selecting years and months for rapid selection.
|
||||
|
||||
<CodeDemo title="With Month And Year Picker" files={calendarContent.withMonthAndYearPicker} />
|
||||
|
||||
Go to the [Calendar documentation](/docs/components/calendar) to learn more about the component.
|
||||
|
||||
### RangeCalendar
|
||||
|
||||
A Range calendar consists of a grouping element containing one or more date grids (e.g. months), and a previous and next button for navigating through time. Each calendar grid consists of cells containing button elements that can be pressed and navigated to using the arrow keys to select a date range. Once a start date is selected, the user can navigate to another date using the keyboard or by hovering over it, and clicking it or pressing the Enter key commits the selected date range.
|
||||
|
||||
<CodeDemo title="Usage" files={rangeCalendarContent.usage} />
|
||||
|
||||
Go to the [RangeCalendar documentation](/docs/components/range-calendar) to learn more about the component.
|
||||
|
||||
### DatePicker
|
||||
|
||||
A Date Picker combines a DateInput and a Calendar popover to allow users to enter or select a date and time value.
|
||||
|
||||
<CodeDemo title="Usage" files={datePickerContent.usage} />
|
||||
|
||||
Go to the [DatePicker documentation](/docs/components/date-picker) to learn more about the component.
|
||||
|
||||
### DateRangePicker
|
||||
|
||||
Date Range Picker combines two DateInputs and a RangeCalendar popover to allow users to enter or select a date and time range.
|
||||
|
||||
<CodeDemo title="Usage" files={dateRangePickerContent.usage} />
|
||||
|
||||
Go to the [DateRangePicker documentation](/docs/components/date-range-picker) to learn more about the component.
|
||||
|
||||
|
||||
### Calendar Presets
|
||||
|
||||
`Calendar` and `RangeCalendar` components support adding custom content at the top and bottom of the calendar, this is useful for adding presets or
|
||||
custom actions to the calendar.
|
||||
|
||||
Here's an example of how to add presets to the `Calendar` component:
|
||||
|
||||
<CodeDemo title="Calendar Presets" files={calendarContent.presets} />
|
||||
|
||||
### Internationalization
|
||||
|
||||
These new components have built-in internationalization, time zones and granularity support, they supports selecting dates
|
||||
in many calendar systems used around the world, including `Gregorian`, `Hebrew`, `Indian`, `Islamic`, `Buddhist`, and more.
|
||||
|
||||
Dates are automatically displayed in the appropriate calendar system for the user's locale this is possible thanks to [@internationalized/date](https://react-spectrum.adobe.com/internationalized/date/) package, which
|
||||
includes functions for parsing strings in multiple formats into `ZonedDateTime` objects.
|
||||
|
||||
Here's and example using the `Indian` calendar system:
|
||||
|
||||
<CodeDemo title="International Calendar" files={datePickerContent.internationalCalendar} />
|
||||
|
||||
Alternatively you can set the `locale` globally by using the `NextUIProvider` component:
|
||||
|
||||
```jsx
|
||||
// Next.js App Router example
|
||||
"use client";
|
||||
|
||||
import {NextUIProvider} from "@nextui-org/react";
|
||||
|
||||
export interface ProvidersProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export function Providers({children}: ProvidersProps) {
|
||||
const router = useRouter();
|
||||
|
||||
return <NextUIProvider locale="hi-IN-u-ca-indian">{children}</NextUIProvider>;
|
||||
}
|
||||
```
|
||||
|
||||
If no `locale` is provided, it will extract the locale from the browser.
|
||||
|
||||
### NextUI Provider
|
||||
|
||||
The `NextUIProvider` component was updated to include the `createCalendar` function, which allows you to create a calendar instance with the specified locale and time zone,
|
||||
and the `defaultDates` object which allows you to set global minimum and maximum dates for the components.
|
||||
|
||||
```jsx
|
||||
// Next.js App Router example
|
||||
"use client";
|
||||
|
||||
import {NextUIProvider, SupportedCalendars} from "@nextui-org/react";
|
||||
import {CalendarDate, GregorianCalendar} from "@internationalized/date";
|
||||
|
||||
export interface ProvidersProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
function createCalendar(identifier: SupportedCalendars) {
|
||||
switch (identifier) {
|
||||
case "gregory":
|
||||
return new GregorianCalendar();
|
||||
default:
|
||||
throw new Error(`Unsupported calendar ${identifier}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function Providers({children}: ProvidersProps) {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<NextUIProvider
|
||||
locale="hi-IN-u-ca-indian"
|
||||
defaultDates={{
|
||||
minDate: new CalendarDate(1900, 1, 1),
|
||||
maxDate: new CalendarDate(2099, 12, 31),
|
||||
}}
|
||||
createCalendar={createCalendar}
|
||||
>
|
||||
{children}
|
||||
</NextUIProvider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
<Spacer y={4} />
|
||||
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
### Removal of the `units` creation
|
||||
|
||||
To improve performance and reduce bundle size, we have removed the `units` creation from the
|
||||
`nextui` plugin. [TailwindCSS v3.4](https://tailwindcss.com/blog/tailwindcss-v3-4) added support for `min-h-*` and `min-w-*` classes, so it is no longer needed.
|
||||
|
||||
How to upgrade:
|
||||
|
||||
1. Upgrade TailwindCSS to version 3.4 or later (if you haven't already). You can do this by running:
|
||||
|
||||
```bash
|
||||
npm install tailwindcss@latest
|
||||
```
|
||||
|
||||
2. Remove the `spacingUnit` configuration from your `tailwind.config.(js|ts)` file (if you have it):
|
||||
|
||||
```diff-js
|
||||
plugins: [
|
||||
nextui({
|
||||
layout: {
|
||||
- spacingUnit: 4,
|
||||
},
|
||||
}),
|
||||
],
|
||||
```
|
||||
|
||||
3. Find all `-unit` classes in your project and replace them with a `-` separator. For example, replace `p-unit-4` with `p-4`.
|
||||
|
||||
```diff-jsx
|
||||
import {Button} from "@nextui-org/react";
|
||||
|
||||
export const MyButton = () => {
|
||||
return (
|
||||
- <Button className="px-unit-2 py-unit-1 min-w-unit-48">
|
||||
+ <Button className="px-2 py-1 min-w-48">
|
||||
My Button
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Separation for `errorMessage` and `isInvalid`
|
||||
|
||||
We are currently working on supporting multiple types of validation, including native HTML constraint validation, custom validation, and real-time validation.
|
||||
Due to this reason, the requirements for displaying error messages have become more varied, and it is necessary to handle validation conditions separately from the `errorMessage`.
|
||||
|
||||
How to upgrade:
|
||||
|
||||
1. To display `errorMessage`, `isInvalid` must be set to `true`.
|
||||
|
||||
```diff-jsx
|
||||
<Input
|
||||
type="email"
|
||||
label="Email"
|
||||
variant="bordered"
|
||||
defaultValue="junior2nextui.org"
|
||||
+ isInvalid={true}
|
||||
errorMessage="Please enter a valid email"
|
||||
className="max-w-xs"
|
||||
/>
|
||||
```
|
||||
|
||||
That's it! Your project should now be using the latest version of TailwindCSS and NextUI.
|
||||
|
||||
<Spacer y={4} />
|
||||
|
||||
|
||||
## Other Changes
|
||||
|
||||
**Bug Fixes**:
|
||||
|
||||
- Fixed an HSL color rounding issue in the theme settings to ensure accurate color representation. [PR](https://github.com/nextui-org/nextui/pull/2702) - [@wingkwong](https://github.com/wingkwong)
|
||||
- Removed conflicting transition definitions affecting CSS classes. [PR](https://github.com/nextui-org/nextui/pull/2677) - [@u3u](https://github.com/u3u)
|
||||
- Patched the "@nextui-org/autocomplete" package to fix an issue where empty items with `allowCustomValue` would not render properly due to a null node problem. [PR](https://github.com/nextui-org/nextui/pull/2674) - [@wingkwong](https://github.com/wingkwong)
|
||||
- Implemented a fix in modal components to prevent carryover of IME (Input Method Editor) input when switching fields using the Tab key. [PR](https://github.com/nextui-org/nextui/pull/2709) - [@ryo-manba](https://github.com/ryo-manba)
|
||||
- Enhanced accessibility by handling Tab key press event in the `ModalContent` component. [PR](https://github.com/nextui-org/nextui/pull/2709) - [@ryo-manba](https://github.com/ryo-manba)
|
||||
- Fixed an issue where disabled select components could still be changed using blur and keyboard shortcuts. [PR](https://github.com/nextui-org/nextui/pull/2649) - [@wingkwong](https://github.com/wingkwong)
|
||||
- Patched issues in "@nextui-org/use-aria-multiselect" and "@nextui-org/stories-utils" packages to fix a warning about `SELECT defaultSelectedKeys`. [PR](https://github.com/nextui-org/nextui/pull/2648) - [@wingkwong](https://github.com/wingkwong)
|
||||
- Fixed an issue with incorrect `onChange` typing in Checkbox Group, ensuring it now correctly handles an array of strings as values. [PR](https://github.com/nextui-org/nextui/pull/2595) - [@wingkwong](https://github.com/wingkwong)
|
||||
- Fixed the `label` placement issue in `Select` component if a `description` prop is used [PR](https://github.com/nextui-org/nextui/pull/2553) - [@novsource](https://github.com/novsource)
|
||||
- Fixed the `originalProps` spread issue in the `Dropdown` component. [PR](https://github.com/nextui-org/nextui/pull/2450) - [@wingkwong](https://github.com/wingkwong)
|
||||
|
||||
**Improvements**
|
||||
- Framer Motion was updated to the latest version, improving performance and reducing bundle size. [Docs](https://www.framer.com/motion/guide-reduce-bundle-size/) [PR](https://github.com/nextui-org/nextui/pull/2464) - [@mezotv](https://github.com/mezotv)
|
||||
- `LazyMotion` was added to all components that use Framer Motion, improving performance by only loading the required motion components.
|
||||
- We removed the custom `units` creation from the `nextui` plugin, it is no longer needed with TailwindCSS v3.4 and above. [PR](https://github.com/nextui-org/nextui/pull/2713) - [@jrgarciadev](https://github.com/jrgarciadev)
|
||||
- Updated `framer-motion` package across various components and utilities to version `11.0.22` for enhanced performance and consistency. [PR](https://github.com/nextui-org/nextui/pull/2596) - [@wingkwong](https://github.com/wingkwong)
|
||||
- Ensured compatibility with `react@18.2.0` and `react-dom@18.2.0` across the board. [PR](https://github.com/nextui-org/nextui/pull/2596) - [@wingkwong](https://github.com/wingkwong)
|
||||
- Introduced patches for NextUI components to improve animations, including support for keyframes with spring and inertia animations. [PR](https://github.com/nextui-org/nextui/pull/2596) - [@wingkwong](https://github.com/wingkwong)
|
||||
- Improved handling of numeric keys in the multi-select component to ensure consistent behavior. [PR](https://github.com/nextui-org/nextui/pull/2589) - [@wingkwong](https://github.com/wingkwong)
|
||||
- Updated the version of react-aria to include the latest changes as detailed in the [2024-02-13 release](https://react-spectrum.adobe.com/releases/2024-02-13.html). [PR](https://github.com/nextui-org/nextui/pull/2561) - [@ryo-manba](https://github.com/ryo-manba)
|
||||
- Added support for custom class names in the `AvatarGroup` component, enhancing flexibility in styling. [PR](https://github.com/nextui-org/nextui/pull/2669) - [@wingkwong](https://github.com/wingkwong)
|
||||
- Introduced a `count` slot to the `AvatarGroup` for more customized rendering. [PR](https://github.com/nextui-org/nextui/pull/2669) - [@wingkwong](https://github.com/wingkwong)
|
||||
- Improved the `AvatarGroup` component's count rendering logic for better performance and flexibility. [PR](https://github.com/nextui-org/nextui/pull/2669) - [@wingkwong](https://github.com/wingkwong)
|
||||
- Add RTL support to the kbd component. [PR](https://github.com/nextui-org/nextui/pull/2482) - [@mrbadri](https://github.com/mrbadri)
|
||||
- Add RTL support to the Select component. [PR](https://github.com/nextui-org/nextui/pull/2485) - [@mrbadri](https://github.com/mrbadri)
|
||||
- Add RTL support to the avatar group componen. [PR](https://github.com/nextui-org/nextui/pull/2498) - [@mrbadri](https://github.com/mrbadri)
|
||||
- Add RTL support to the Table component. [PR](https://github.com/nextui-org/nextui/pull/2472) - [@mrbadri](https://github.com/mrbadri)
|
||||
|
||||
**Documentation**:
|
||||
- Updated documentation to reflect the new features and changes in the `AvatarGroup` component.
|
||||
- Added support for the "bun" package manager across documentation and components. [PR](https://github.com/nextui-org/nextui/pull/2625) - [@sudongyuer](https://github.com/sudongyuer)
|
||||
- [Kapa.ai](https://www.kapa.ai/) widget was added to the documentation to provide AI support for users. [PR](https://github.com/nextui-org/nextui/pull/2428) - [@wingkwong](https://github.com/sudongyuer)
|
||||
- Layout docs updated to remove the `units` configuration from the `tailwind.config.(js|ts)` file.
|
||||
|
||||
|
||||
Special thanks to NextUI Team members [@kuri-sun](https://github.com/kuri-sun), [@ryo-manba](https://github.com/ryo-manba),
|
||||
[@sudongyuer](https://github.com/sudongyuer), [@winchesHe](https://github.com/winchesHe), [@wingkwong](https://github.com/wingkwong),
|
||||
[@tianenpang](https://github.com/tianenpang), [@smultar](https://github.com/smultar) and contributors for their contributions to this release.
|
||||
|
||||
For a full list of changes, please refer to the [release notes](https://github.com/nextui-org/nextui/releases/tag/%40nextui-org%2Freact%402.3.0).
|
||||
|
||||
<Spacer y={6} />
|
||||
|
||||
We hope you enjoy these new components and the new features. We're excited to see what you build with them!
|
||||
|
||||
Thanks for reading and happy coding! 🚀
|
||||
|
||||
---
|
||||
|
||||
## Community
|
||||
|
||||
We're excited to see the community adopt NextUI, raise issues, and provide feedback.
|
||||
Whether it's a feature request, bug report, or a project to showcase, please get involved!
|
||||
|
||||
<Community />
|
||||
|
||||
## Contributing
|
||||
|
||||
PR's on NextUI are always welcome, please see our [contribution guidelines](https://github.com/nextui-org/nextui/blob/main/CONTRIBUTING.md) to learn how you can contribute to this project.
|
||||
@@ -0,0 +1,333 @@
|
||||
---
|
||||
title: "Introducing v2.4.0"
|
||||
description: "NextUI v2.4.0 is here! includes tons of bug fixes, the possibility to globally disable the animations and improvements."
|
||||
date: "2024-05-27"
|
||||
image: "/blog/v2.4.0.jpg"
|
||||
tags: ["nextui", "v2.4.0", "release", "bug fixes", "improvements", "animations"]
|
||||
author:
|
||||
name: "WK Wong"
|
||||
username: "@wingkwong"
|
||||
link: "https://github.com/wingkwong"
|
||||
avatar: "https://avatars.githubusercontent.com/u/35857179?v=4"
|
||||
---
|
||||
|
||||
<img
|
||||
src="/blog/v2.4.0_2x.jpg"
|
||||
width={700}
|
||||
height={350}
|
||||
alt="NextUI v2.4.0"
|
||||
className="w-full border border-transparent dark:border-default-200/50 object-fit rounded-xl shadow-lg"
|
||||
/>
|
||||
|
||||
We are excited to announce the latest update to NextUI, version **2.4.0**! This release introduces 60+ bug fixes, dependencies upgrades, improvements in API and CLI and more.
|
||||
|
||||
## What's New in v2.4.0?
|
||||
|
||||
- [Disable Animations Globally](#disable-animations-globally) - Allows users to disable animation globally via NextUIProvider
|
||||
- [API Improvements](#api-improvements) - Includes several props like `disableRipple`, `skipFramerMotionAnimations` and `validationBehavior`
|
||||
- [CLI Improvements](#cli-improvements) - Includes new command options and optimizes performance
|
||||
- [React Aria Packages Upgrades](#react-aria-packages-upgrades) - Upgrades and fixes the exact versions of React Aria
|
||||
- [Other Changes](#other-changes) - Includes styling improvements, accessibility, usability enhancements and over 60 bug fixes across different component packages
|
||||
|
||||
<Spacer y={4} />
|
||||
|
||||
Upgrade today by running one of the following commands:
|
||||
|
||||
<Spacer y={4} />
|
||||
|
||||
<PackageManagers
|
||||
commands={{
|
||||
cli: "nextui upgrade",
|
||||
npm: "npx nextui-cli@latest upgrade",
|
||||
}}
|
||||
/>
|
||||
|
||||
|
||||
<Spacer y={4} />
|
||||
|
||||
## Disable Animations Globally
|
||||
|
||||
NextUI components are animated by default and can be disabled by setting the prop `disableAnimation` individually. Since v2.4.0, you can globally disable the animation by setting `disableAnimation` to `NextUIProvider`.
|
||||
|
||||
```tsx
|
||||
"use client";
|
||||
|
||||
import {type ReactNode} from "react";
|
||||
import {NextUIProvider} from "@nextui-org/react";
|
||||
|
||||
export function AppProvider(props: AppProviderProps) {
|
||||
const {children, className} = props;
|
||||
|
||||
return (
|
||||
<NextUIProvider className={className} disableAnimation>
|
||||
{children}
|
||||
</NextUIProvider>
|
||||
);
|
||||
}
|
||||
|
||||
interface AppProviderProps {
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
```
|
||||
|
||||
<Spacer y={4} />
|
||||
|
||||
## API Improvements
|
||||
|
||||
### disableRipple
|
||||
|
||||
By default, there is a ripple effect on press in Button and Card component and can be disabled by setting the prop `disableRipple` individually. Since v2.4.0, it can be disabled globally by setting `disableRipple` to `NextUIProvider`.
|
||||
|
||||
```tsx
|
||||
"use client";
|
||||
|
||||
import {type ReactNode} from "react";
|
||||
import {NextUIProvider} from "@nextui-org/react";
|
||||
|
||||
export function AppProvider(props: AppProviderProps) {
|
||||
const {children, className} = props;
|
||||
|
||||
return (
|
||||
<NextUIProvider className={className} disableRipple>
|
||||
{children}
|
||||
</NextUIProvider>
|
||||
);
|
||||
}
|
||||
|
||||
interface AppProviderProps {
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
```
|
||||
|
||||
### skipFramerMotionAnimations
|
||||
|
||||
We can control whether `framer-motion` animations are skipped within the application. This property is automatically enabled (`true`) when the `disableAnimation` prop is set to `true`, effectively skipping all `framer-motion` animations. To retain `framer-motion` animations while using the `disableAnimation` prop for other purposes, set this to `false`. However, note that animations in NextUI Components are still omitted if the `disableAnimation` prop is `true`.
|
||||
|
||||
```tsx
|
||||
"use client";
|
||||
|
||||
import {type ReactNode} from "react";
|
||||
import {NextUIProvider} from "@nextui-org/react";
|
||||
|
||||
export function AppProvider(props: AppProviderProps) {
|
||||
const {children, className} = props;
|
||||
|
||||
return (
|
||||
<NextUIProvider className={className} skipFramerMotionAnimations>
|
||||
{children}
|
||||
</NextUIProvider>
|
||||
);
|
||||
}
|
||||
|
||||
interface AppProviderProps {
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
```
|
||||
|
||||
### validationBehavior
|
||||
|
||||
We can set `validationBehavior` to either `native` or `aria` to control whether to use native HTML form validation to prevent form submission when the value is missing or invalid, or mark the field as required or invalid via ARIA. If it is not specified, `aria` will be used by default.
|
||||
|
||||
```tsx
|
||||
"use client";
|
||||
|
||||
import {type ReactNode} from "react";
|
||||
import {NextUIProvider} from "@nextui-org/react";
|
||||
|
||||
export function AppProvider(props: AppProviderProps) {
|
||||
const {children, className} = props;
|
||||
|
||||
return (
|
||||
<NextUIProvider className={className} validationBehavior="native">
|
||||
{children}
|
||||
</NextUIProvider>
|
||||
);
|
||||
}
|
||||
|
||||
interface AppProviderProps {
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
```
|
||||
|
||||
<Spacer y={4} />
|
||||
|
||||
## CLI Improvements
|
||||
|
||||
### Refactor Init Flow View
|
||||
|
||||
We've refactored the init flow view to provide a better user experience.
|
||||
|
||||
The latest flow view output:
|
||||
|
||||
```bash
|
||||
┌ Create a new project
|
||||
│
|
||||
◇ Select a template (Enter to select)
|
||||
│ ● App (A Next.js 14 with app directory template pre-configured with NextUI (v2) and Tailwind CSS.)
|
||||
│ ○ Pages (A Next.js 14 with pages directory template pre-configured with NextUI (v2) and Tailwind CSS.)
|
||||
│ ○ Vite (A Vite template pre-configured with NextUI (v2) and Tailwind CSS.)
|
||||
│
|
||||
◇ New project name (Enter to skip with default name)
|
||||
│ my-nextui-app
|
||||
│
|
||||
◇ Select a package manager (Enter to select)
|
||||
│ ● npm
|
||||
│ ○ yarn
|
||||
│ ○ pnpm
|
||||
│ ○ bun
|
||||
│
|
||||
◇ Template created successfully!
|
||||
│
|
||||
◇ Next steps ───────╮
|
||||
│ │
|
||||
│ cd my-nextui-app │
|
||||
│ npm install │
|
||||
│ │
|
||||
├────────────────────╯
|
||||
│
|
||||
└ 🚀 Get started with npm run dev
|
||||
```
|
||||
|
||||
### Add Vite Template
|
||||
|
||||
We've introduced a new Vite template pre-configured with NextUI v2 and TailwindCSS. The following command is to initialize a new Vite project named `my-vite-app`.
|
||||
|
||||
```bash
|
||||
nextui init my-vite-app -t vite
|
||||
```
|
||||
|
||||
### Package Manager Flag
|
||||
|
||||
We've introduced a new flag `-p` (or `--package`) to init command to allow users to choose the package manager to use for the new project. By default, `npm` is used. For example, the following command will initialize a new NextUI project with the app template named my-nextui-app using pnpm package manager.
|
||||
|
||||
```bash
|
||||
nextui init my-nextui-app -t app -p pnpm
|
||||
```
|
||||
|
||||
### no-cache Flag
|
||||
|
||||
We've introduced a new flag `--no-cache` to allow users to disable the cache. By default, the data will be cached for 30 mins after the first request. It is useful when the data is cached, and you wish to upgrade to the new version just released after the first request. In this way, you can run the following command
|
||||
|
||||
```bash
|
||||
nextui --no-cache upgrade
|
||||
```
|
||||
|
||||
### Upgrade Version Output
|
||||
|
||||
You can now run the upgrade command and see the summary version of the package you are upgrading to.
|
||||
|
||||

|
||||
|
||||
### Upgrade And Remove Select View Optimization
|
||||
|
||||
The disabled option(s) will be displayed in the bottom of the list.
|
||||
|
||||

|
||||
|
||||
### Doctor Command add peerDependencies check
|
||||
|
||||
The `doctor` command now checks for peerDependencies and displays the incorrect peerDependencies, relation: [nextui-org/nextui#2954](https://github.com/nextui-org/nextui/issues/2954).
|
||||
|
||||
<Spacer y={4} />
|
||||
|
||||
## React Aria Packages Upgrades
|
||||
|
||||
We've upgraded and fixed React Aria packages dependencies across our components. This update focuses on enhancing accessibility, ensuring better compatibility with the latest versions of React Aria, and resolving previously reported bugs.
|
||||
|
||||
<Spacer y={4} />
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
### Export improved `cn` utility
|
||||
|
||||
> If you are using it from `@nextui-org/react`, no changes are required.
|
||||
|
||||
The new `cn` utility exported from the `@nextui-org/theme` package includes `tailwind-merge` to avoid conflicts between tailwindcss classes overrides and includes the config for NextUI custom classes.
|
||||
|
||||
If you are using the `cn` utility from the `@nextui-org/system`,
|
||||
|
||||
```diff-tsx
|
||||
-import {cn} from "@nextui-org/system"
|
||||
```
|
||||
|
||||
or `@nextui-org/system-rsc` package,
|
||||
|
||||
```diff-tsx
|
||||
-import {cn} from "@nextui-org/system-rsc"
|
||||
```
|
||||
|
||||
you need to update the import as follows:
|
||||
|
||||
```diff-tsx
|
||||
+ import {cn} from "@nextui-org/theme"
|
||||
```
|
||||
|
||||
<Spacer y={4} />
|
||||
|
||||
|
||||
## Other Changes
|
||||
|
||||
**Bug Fixes**:
|
||||
|
||||
- Fixed popover-based focus behaviours. [PR](https://github.com/nextui-org/nextui/pull/2889) - [@wingkwong](https://github.com/wingkwong)
|
||||
- Fixed missing ref to input wrapper. [PR](https://github.com/nextui-org/nextui/pull/3008) - [@wingkwong](https://github.com/wingkwong)
|
||||
- Fixed react-hook-form uncontrolled switch component. [PR](https://github.com/nextui-org/nextui/pull/3008) - [@wingkwong](https://github.com/wingkwong)
|
||||
- Fixed focus on the first item when pressing Space / Enter key on dropdown menu open. [PR](https://github.com/nextui-org/nextui/pull/2970) - [@wingkwong](https://github.com/wingkwong)
|
||||
- Fixed inputting spaces in textarea inside a Table row. [PR](https://github.com/nextui-org/nextui/pull/3020) - [@wingkwong](https://github.com/wingkwong)
|
||||
- Fixed incorrect tailwind classnames. [PR](https://github.com/nextui-org/nextui/pull/3018) - [@wingkwong](https://github.com/wingkwong)
|
||||
- Fixed onSelectionChange can handle number [PR](https://github.com/nextui-org/nextui/pull/2937) - [@ryo-manba](https://github.com/ryo-manba)
|
||||
- Fixed update type definition to prevent primitive values as items [PR](https://github.com/nextui-org/nextui/pull/2938) - [@ryo-manba](https://github.com/ryo-manba)
|
||||
- Fixed display placeholder text when unselected for controlled. [PR](https://github.com/nextui-org/nextui/pull/3081) - [@ryo-manba](https://github.com/ryo-manba)
|
||||
- Fixed the inert attribute in `CalendarMonth` and `CalendarPicker`. [PR](https://github.com/nextui-org/nextui/pull/3054) - [@ShrinidhiUpadhyaya](https://github.com/ShrinidhiUpadhyaya)
|
||||
- Fixed hiding of unavailable dates in RangeCalendar. [PR](https://github.com/nextui-org/nextui/pull/3054) - [@ShrinidhiUpadhyaya](https://github.com/ShrinidhiUpadhyaya)
|
||||
- Fixed calendar header controlled state on DatePicker. [PR](https://github.com/nextui-org/nextui/pull/2845) - [@chirokas](https://github.com/chirokas)
|
||||
- Fixed 'Tap to click' behavior on macOS for Accordion and Tab. [PR](https://github.com/nextui-org/nextui/pull/2725) - [@ericfabreu](https://github.com/ericfabreu)
|
||||
- Fixed incorrect margin on labels for RTL required inputs. [PR](https://github.com/nextui-org/nextui/pull/2781) - [@mrbadri](https://github.com/mrbadri)
|
||||
- Fixed a type error in the onKeyDown event handler for the menu component. [PR](https://github.com/nextui-org/nextui/pull/3064) - [@Gaic4o](https://github.com/Gaic4o)
|
||||
|
||||
**Improvements**
|
||||
|
||||
- Added `destroyInactiveTabPanel` prop for Tabs component. [PR](https://github.com/nextui-org/nextui/pull/2973) - [@wingkwong](https://github.com/wingkwong)
|
||||
- Added pointer-events-none to skeleton base. [PR](https://github.com/nextui-org/nextui/pull/2972) - [@wingkwong](https://github.com/wingkwong)
|
||||
- Revised isInvalid input styles. [PR](https://github.com/nextui-org/nextui/pull/3010) - [@wingkwong](https://github.com/wingkwong)
|
||||
- Revised slider styles. [PR](https://github.com/nextui-org/nextui/pull/2883) - [@wingkwong](https://github.com/wingkwong)
|
||||
- Removed unnecessary origin-bottom in button. [PR](https://github.com/nextui-org/nextui/pull/2990) - [@wingkwong](https://github.com/wingkwong)
|
||||
- Set overflow visible after skeleton loaded. [PR](https://github.com/nextui-org/nextui/pull/2986) - [@wingkwong](https://github.com/wingkwong)
|
||||
- Kept date picker style consistent for different variants. [PR](https://github.com/nextui-org/nextui/pull/2901) - [@wingkwong](https://github.com/wingkwong)
|
||||
- Calculated the correct value on mark click. [PR](https://github.com/nextui-org/nextui/pull/3017) - [@wingkwong](https://github.com/wingkwong)
|
||||
- Removed scrolling display during month change animation. [PR](https://github.com/nextui-org/nextui/pull/2949) - [@novsource](https://github.com/novsource)
|
||||
- Added the correct peerDep version [PR](https://github.com/nextui-org/nextui/pull/3014) - [@winchesHe](https://github.com/winchesHe)
|
||||
- Added missing export of TableRowProps type. [PR](https://github.com/nextui-org/nextui/pull/2866) - [@sapkra](https://github.com/sapkra)
|
||||
- Changed validationBehavior from native to aria by default, with the option to change via props. [PR](https://github.com/nextui-org/nextui/pull/2987) - [@ryo-manba](https://github.com/ryo-manba)
|
||||
- Made the VisuallyHidden elementType as span when the default parent element accepts only phrasing elements. [PR](https://github.com/nextui-org/nextui/pull/3013) - [@kosmotema](https://github.com/kosmotema)
|
||||
- Refactored the useScrollPosition hook to improve performance and stability by using useCallback for the handler function and useRef for throttleTimeout. [PR](https://github.com/nextui-org/nextui/pull/3049) - [@Gaic4o](https://github.com/Gaic4o)
|
||||
|
||||
**Documentation**:
|
||||
- Updated documentation to reflect the new features and changes in the components, API references, and CLI.
|
||||
|
||||
Special thanks to NextUI Team members [@kuri-sun](https://github.com/kuri-sun), [@ryo-manba](https://github.com/ryo-manba),
|
||||
[@sudongyuer](https://github.com/sudongyuer), [@winchesHe](https://github.com/winchesHe), [@wingkwong](https://github.com/wingkwong),
|
||||
[@tianenpang](https://github.com/tianenpang), [@smultar](https://github.com/smultar) and contributors for their contributions to this release.
|
||||
|
||||
For a full list of changes, please refer to the [release notes](https://github.com/nextui-org/nextui/releases/tag/%40nextui-org%2Freact%402.4.0).
|
||||
|
||||
<Spacer y={6} />
|
||||
|
||||
Thanks for reading and happy coding! 🚀
|
||||
|
||||
---
|
||||
|
||||
## Community
|
||||
|
||||
We're excited to see the community adopt NextUI, raise issues, and provide feedback.
|
||||
Whether it's a feature request, bug report, or a project to showcase, please get involved!
|
||||
|
||||
<Community />
|
||||
|
||||
## Contributing
|
||||
|
||||
PR's on NextUI are always welcome, please see our [contribution guidelines](https://github.com/nextui-org/nextui/blob/main/CONTRIBUTING.md) to learn how you can contribute to this project.
|
||||
@@ -24,10 +24,41 @@ export default function App() {
|
||||
);
|
||||
}`;
|
||||
|
||||
const AppTs = `import type {Selection} from "@nextui-org/react";
|
||||
|
||||
import {Accordion, AccordionItem} from "@nextui-org/react";
|
||||
|
||||
export default function App() {
|
||||
const [selectedKeys, setSelectedKeys] = React.useState<Selection>(new Set(["1"]));
|
||||
const defaultContent =
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.";
|
||||
return (
|
||||
<Accordion
|
||||
selectedKeys={selectedKeys}
|
||||
onSelectionChange={setSelectedKeys}
|
||||
>
|
||||
<AccordionItem key="1" aria-label="Accordion 1" title="Accordion 1">
|
||||
{defaultContent}
|
||||
</AccordionItem>
|
||||
<AccordionItem key="2" aria-label="Accordion 2" title="Accordion 2">
|
||||
{defaultContent}
|
||||
</AccordionItem>
|
||||
<AccordionItem key="3" aria-label="Accordion 3" title="Accordion 3">
|
||||
{defaultContent}
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
};
|
||||
|
||||
const reactTs = {
|
||||
"/App.tsx": AppTs,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
...reactTs,
|
||||
};
|
||||
|
||||
@@ -214,7 +214,7 @@ export default function App() {
|
||||
startContent={<MonitorMobileIcon className="text-primary" />}
|
||||
subtitle={
|
||||
<p className="flex">
|
||||
2 issues to <p className="text-primary ml-1">fix now</p>
|
||||
2 issues to <span className="text-primary ml-1">fix now</span>
|
||||
</p>
|
||||
}
|
||||
title="Connected devices"
|
||||
@@ -249,7 +249,7 @@ export default function App() {
|
||||
title={
|
||||
<p className="flex gap-1 items-center">
|
||||
Card expired
|
||||
<p className="text-default-400 text-small">*4812</p>
|
||||
<span className="text-default-400 text-small">*4812</span>
|
||||
</p>
|
||||
}
|
||||
>
|
||||
|
||||
@@ -51,7 +51,7 @@ export default function App() {
|
||||
}
|
||||
subtitle={
|
||||
<p className="flex">
|
||||
2 issues to<p className="text-primary ml-1">fix now</p>
|
||||
2 issues to<span className="text-primary ml-1">fix now</span>
|
||||
</p>
|
||||
}
|
||||
title="Zoey Lang"
|
||||
|
||||
@@ -37,6 +37,7 @@ export default function App() {
|
||||
<Autocomplete
|
||||
label="Favorite Animal"
|
||||
variant="bordered"
|
||||
defaultItems={animals}
|
||||
placeholder="Search an animal"
|
||||
className="max-w-xs"
|
||||
selectedKey={value}
|
||||
|
||||
@@ -24,6 +24,7 @@ import asyncLoadingItems from "./async-loading-items";
|
||||
import sections from "./sections";
|
||||
import customSectionsStyle from "./custom-sections-style";
|
||||
import customStyles from "./custom-styles";
|
||||
import readOnly from "./read-only";
|
||||
|
||||
export const autocompleteContent = {
|
||||
usage,
|
||||
@@ -52,4 +53,5 @@ export const autocompleteContent = {
|
||||
sections,
|
||||
customSectionsStyle,
|
||||
customStyles,
|
||||
readOnly,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
const data = `export const animals = [
|
||||
{label: "Cat", value: "cat", description: "The second most popular pet in the world"},
|
||||
{label: "Dog", value: "dog", description: "The most popular pet in the world"},
|
||||
{label: "Elephant", value: "elephant", description: "The largest land animal"},
|
||||
{label: "Lion", value: "lion", description: "The king of the jungle"},
|
||||
{label: "Tiger", value: "tiger", description: "The largest cat species"},
|
||||
{label: "Giraffe", value: "giraffe", description: "The tallest land animal"},
|
||||
{
|
||||
label: "Dolphin",
|
||||
value: "dolphin",
|
||||
description: "A widely distributed and diverse group of aquatic mammals",
|
||||
},
|
||||
{label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"},
|
||||
{label: "Zebra", value: "zebra", description: "A several species of African equids"},
|
||||
{
|
||||
label: "Shark",
|
||||
value: "shark",
|
||||
description: "A group of elasmobranch fish characterized by a cartilaginous skeleton",
|
||||
},
|
||||
{
|
||||
label: "Whale",
|
||||
value: "whale",
|
||||
description: "Diverse group of fully aquatic placental marine mammals",
|
||||
},
|
||||
{label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"},
|
||||
{label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"},
|
||||
];`;
|
||||
|
||||
const App = `import {Autocomplete, AutocompleteItem} from "@nextui-org/react";
|
||||
import {animals} from "./data";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<Autocomplete
|
||||
isReadOnly
|
||||
label="Favorite Animal"
|
||||
defaultItems={animals}
|
||||
placeholder="Search an animal"
|
||||
defaultSelectedKey="cat"
|
||||
className="max-w-xs"
|
||||
>
|
||||
{(item) => <AutocompleteItem key={item.value}>{item.label}</AutocompleteItem>}
|
||||
</Autocomplete>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/data.js": data,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
@@ -12,7 +12,7 @@ export default function App() {
|
||||
<DropdownTrigger>
|
||||
<Button
|
||||
isIconOnly
|
||||
className="min-w-unit-6 w-unit-6 h-unit-6"
|
||||
className="min-w-6 w-6 h-6"
|
||||
size="sm"
|
||||
variant="flat"
|
||||
>
|
||||
|
||||
@@ -20,7 +20,7 @@ const MyButton = forwardRef((props, ref) => {
|
||||
...props,
|
||||
});
|
||||
|
||||
const {ripples} = getRippleProps();
|
||||
const {ripples, onClear} = getRippleProps();
|
||||
|
||||
return (
|
||||
<button ref={domRef} {...getButtonProps()}>
|
||||
@@ -29,7 +29,7 @@ const MyButton = forwardRef((props, ref) => {
|
||||
{children}
|
||||
{isLoading && spinnerPlacement === "end" && spinner}
|
||||
{endContent}
|
||||
{!disableRipple && <Ripple ripples={ripples} />}
|
||||
{!disableRipple && <Ripple ripples={ripples} onClear={onClear} />}
|
||||
</button>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
const App = `import {Calendar} from "@nextui-org/react";
|
||||
import {today, getLocalTimeZone} from "@internationalized/date";
|
||||
|
||||
export default function App() {
|
||||
let defaultDate = today(getLocalTimeZone());
|
||||
let [focusedDate, setFocusedDate] = React.useState(defaultDate);
|
||||
|
||||
return (
|
||||
<Calendar
|
||||
aria-label="Date (Controlled Focused Value)"
|
||||
focusedValue={focusedDate}
|
||||
value={defaultDate}
|
||||
onFocusChange={setFocusedDate}
|
||||
/>
|
||||
);
|
||||
}`;
|
||||
|
||||
const AppTs = `import {Calendar} from "@nextui-org/react";
|
||||
import type {DateValue} from "@react-types/calendar";
|
||||
import {today, getLocalTimeZone} from "@internationalized/date";
|
||||
|
||||
export default function App() {
|
||||
let defaultDate = today(getLocalTimeZone());
|
||||
let [focusedDate, setFocusedDate] = React.useState<DateValue>(defaultDate);
|
||||
|
||||
return (
|
||||
<Calendar
|
||||
aria-label="Date (Controlled Focused Value)"
|
||||
focusedValue={focusedDate}
|
||||
value={defaultDate}
|
||||
onFocusChange={setFocusedDate}
|
||||
/>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/App.tsx": AppTs,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
const App = `import {Calendar} from "@nextui-org/react";
|
||||
import {parseDate} from "@internationalized/date";
|
||||
|
||||
export default function App() {
|
||||
let [value, setValue] = React.useState(parseDate("2024-03-07"));
|
||||
|
||||
return (
|
||||
<Calendar
|
||||
aria-label="Date (Controlled)"
|
||||
value={value}
|
||||
onChange={setValue}
|
||||
/>
|
||||
);
|
||||
}`;
|
||||
|
||||
const AppTs = `import {Calendar} from "@nextui-org/react";
|
||||
import type {DateValue} from "@react-types/calendar";
|
||||
import {parseDate} from "@internationalized/date";
|
||||
|
||||
export default function App() {
|
||||
let [value, setValue] = React.useState<DateValue>(parseDate("2024-03-07"));
|
||||
|
||||
return (
|
||||
<Calendar
|
||||
aria-label="Date (Controlled)"
|
||||
value={value}
|
||||
onChange={setValue}
|
||||
/>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/App.tsx": AppTs,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
@@ -0,0 +1,15 @@
|
||||
const App = `import {Calendar} from "@nextui-org/react";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<Calendar aria-label="Date (Disabled)" isDisabled />
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
import usage from "./usage";
|
||||
import disabled from "./disabled";
|
||||
import readonly from "./read-only";
|
||||
import controlled from "./controlled";
|
||||
import minDateValue from "./min-date-value";
|
||||
import maxDateValue from "./max-date-value";
|
||||
import unavailableDates from "./unavailable-dates";
|
||||
import controlledFocusedValue from "./controlled-focused-value";
|
||||
import invalidDate from "./invalid-date";
|
||||
import withMonthAndYearPicker from "./with-month-and-year-picker";
|
||||
import internationalCalendars from "./international-calendars";
|
||||
import visibleMonths from "./visible-months";
|
||||
import pageBehaviour from "./page-behaviour";
|
||||
import presets from "./presets";
|
||||
|
||||
export const calendarContent = {
|
||||
usage,
|
||||
disabled,
|
||||
readonly,
|
||||
controlled,
|
||||
minDateValue,
|
||||
maxDateValue,
|
||||
unavailableDates,
|
||||
controlledFocusedValue,
|
||||
invalidDate,
|
||||
withMonthAndYearPicker,
|
||||
internationalCalendars,
|
||||
visibleMonths,
|
||||
pageBehaviour,
|
||||
presets,
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
const App = `import {Calendar} from "@nextui-org/react";
|
||||
import {I18nProvider} from "@react-aria/i18n";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<I18nProvider locale="zh-CN-u-ca-chinese">
|
||||
<Calendar aria-label="Date (International Calendar)" />
|
||||
</I18nProvider>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
@@ -0,0 +1,51 @@
|
||||
const App = `import {Calendar} from "@nextui-org/react";
|
||||
import {today, getLocalTimeZone, isWeekend} from "@internationalized/date";
|
||||
import {useLocale} from "@react-aria/i18n";
|
||||
|
||||
|
||||
export default function App() {
|
||||
let [date, setDate] = React.useState(today(getLocalTimeZone()));
|
||||
let {locale} = useLocale();
|
||||
let isInvalid = isWeekend(date, locale);
|
||||
|
||||
return (
|
||||
<Calendar
|
||||
aria-label="Date (Invalid on weekends)"
|
||||
errorMessage={isInvalid ? "We are closed on weekends" : undefined}
|
||||
isInvalid={isInvalid}
|
||||
value={date}
|
||||
onChange={setDate}
|
||||
/>
|
||||
);
|
||||
}`;
|
||||
|
||||
const AppTs = `import {Calendar} from "@nextui-org/react";
|
||||
import type {DateValue} from "@react-types/calendar";
|
||||
import {today, getLocalTimeZone, isWeekend} from "@internationalized/date";
|
||||
import {useLocale} from "@react-aria/i18n";
|
||||
|
||||
|
||||
export default function App() {
|
||||
let [date, setDate] = React.useState<DateValue>(today(getLocalTimeZone()));
|
||||
let {locale} = useLocale();
|
||||
let isInvalid = isWeekend(date, locale);
|
||||
|
||||
return (
|
||||
<Calendar
|
||||
aria-label="Date (Invalid on weekends)"
|
||||
errorMessage={isInvalid ? "We are closed on weekends" : undefined}
|
||||
isInvalid={isInvalid}
|
||||
value={date}
|
||||
onChange={setDate}
|
||||
/>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/App.tsx": AppTs,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
const App = `import {Calendar} from "@nextui-org/react";
|
||||
import {today, getLocalTimeZone} from "@internationalized/date";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<Calendar
|
||||
aria-label="Date (Max Date Value)"
|
||||
defaultValue={today(getLocalTimeZone())}
|
||||
maxValue={today(getLocalTimeZone())}
|
||||
/>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
const App = `import {Calendar} from "@nextui-org/react";
|
||||
import {today, getLocalTimeZone} from "@internationalized/date";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<Calendar
|
||||
aria-label="Date (Min Date Value)"
|
||||
defaultValue={today(getLocalTimeZone())}
|
||||
minValue={today(getLocalTimeZone())}
|
||||
/>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
const App = `import {Calendar} from "@nextui-org/react";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<Calendar
|
||||
aria-label="Date (Page Behaviour)"
|
||||
pageBehavior="single"
|
||||
/>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
@@ -0,0 +1,185 @@
|
||||
const App = `import {Calendar, Radio, RadioGroup, Button, ButtonGroup, cn} from "@nextui-org/react";
|
||||
import {today, getLocalTimeZone, startOfWeek, startOfMonth} from "@internationalized/date";
|
||||
import {useLocale} from "@react-aria/i18n";
|
||||
|
||||
export default function App() {
|
||||
let defaultDate = today(getLocalTimeZone());
|
||||
let [value, setValue] = React.useState(defaultDate);
|
||||
let {locale} = useLocale();
|
||||
|
||||
let now = today(getLocalTimeZone());
|
||||
let nextWeek = startOfWeek(now.add({weeks: 1}), locale);
|
||||
let nextMonth = startOfMonth(now.add({months: 1}));
|
||||
|
||||
const CustomRadio = (props) => {
|
||||
const {children, ...otherProps} = props;
|
||||
|
||||
return (
|
||||
<Radio
|
||||
{...otherProps}
|
||||
classNames={{
|
||||
base: cn(
|
||||
"flex-none m-0 h-8 bg-content1 hover:bg-content2 items-center justify-between",
|
||||
"cursor-pointer rounded-full border-2 border-default-200/60",
|
||||
"data-[selected=true]:border-primary",
|
||||
),
|
||||
label: "text-tiny text-default-500",
|
||||
labelWrapper: "px-1 m-0",
|
||||
wrapper: "hidden",
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Radio>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<Calendar
|
||||
aria-label="Date (Presets)"
|
||||
bottomContent={
|
||||
<RadioGroup
|
||||
aria-label="Date precision"
|
||||
classNames={{
|
||||
base: "w-full pb-2",
|
||||
wrapper: "-my-2.5 py-2.5 px-3 gap-1 flex-nowrap max-w-[280px] overflow-x-scroll",
|
||||
}}
|
||||
defaultValue="exact_dates"
|
||||
orientation="horizontal"
|
||||
>
|
||||
<CustomRadio value="exact_dates">Exact dates</CustomRadio>
|
||||
<CustomRadio value="1_day">1 day</CustomRadio>
|
||||
<CustomRadio value="2_days">2 days</CustomRadio>
|
||||
<CustomRadio value="3_days">3 days</CustomRadio>
|
||||
<CustomRadio value="7_days">7 days</CustomRadio>
|
||||
<CustomRadio value="14_days">14 days</CustomRadio>
|
||||
</RadioGroup>
|
||||
}
|
||||
classNames={{
|
||||
content: "w-full",
|
||||
}}
|
||||
focusedValue={value}
|
||||
nextButtonProps={{
|
||||
variant: "bordered",
|
||||
}}
|
||||
prevButtonProps={{
|
||||
variant: "bordered",
|
||||
}}
|
||||
topContent={
|
||||
<ButtonGroup
|
||||
fullWidth
|
||||
className="px-3 pb-2 pt-3 bg-content1 [&>button]:text-default-500 [&>button]:border-default-200/60"
|
||||
radius="full"
|
||||
size="sm"
|
||||
variant="bordered"
|
||||
>
|
||||
<Button onPress={() => setValue(now)}>Today</Button>
|
||||
<Button onPress={() => setValue(nextWeek)}>Next week</Button>
|
||||
<Button onPress={() => setValue(nextMonth)}>Next month</Button>
|
||||
</ButtonGroup>
|
||||
}
|
||||
value={value}
|
||||
onChange={setValue}
|
||||
onFocusChange={setValue}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}`;
|
||||
|
||||
const AppTs = `import {Calendar, Radio, RadioGroup, Button, ButtonGroup, cn} from "@nextui-org/react";
|
||||
import type {DateValue} from "@react-types/calendar";
|
||||
import {today, getLocalTimeZone, startOfWeek, startOfMonth} from "@internationalized/date";
|
||||
import {useLocale} from "@react-aria/i18n";
|
||||
|
||||
export default function App() {
|
||||
let defaultDate = today(getLocalTimeZone());
|
||||
let [value, setValue] = React.useState<DateValue>(defaultDate);
|
||||
let {locale} = useLocale();
|
||||
|
||||
let now = today(getLocalTimeZone());
|
||||
let nextWeek = startOfWeek(now.add({weeks: 1}), locale);
|
||||
let nextMonth = startOfMonth(now.add({months: 1}));
|
||||
|
||||
const CustomRadio = (props) => {
|
||||
const {children, ...otherProps} = props;
|
||||
|
||||
return (
|
||||
<Radio
|
||||
{...otherProps}
|
||||
classNames={{
|
||||
base: cn(
|
||||
"flex-none m-0 h-8 bg-content1 hover:bg-content2 items-center justify-between",
|
||||
"cursor-pointer rounded-full border-2 border-default-200/60",
|
||||
"data-[selected=true]:border-primary",
|
||||
),
|
||||
label: "text-tiny text-default-500",
|
||||
labelWrapper: "px-1 m-0",
|
||||
wrapper: "hidden",
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Radio>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<Calendar
|
||||
aria-label="Date (Presets)"
|
||||
bottomContent={
|
||||
<RadioGroup
|
||||
aria-label="Date precision"
|
||||
classNames={{
|
||||
base: "w-full pb-2",
|
||||
wrapper: "-my-2.5 py-2.5 px-3 gap-1 flex-nowrap max-w-[280px] overflow-scroll",
|
||||
}}
|
||||
defaultValue="exact_dates"
|
||||
orientation="horizontal"
|
||||
>
|
||||
<CustomRadio value="exact_dates">Exact dates</CustomRadio>
|
||||
<CustomRadio value="1_day">1 day</CustomRadio>
|
||||
<CustomRadio value="2_days">2 days</CustomRadio>
|
||||
<CustomRadio value="3_days">3 days</CustomRadio>
|
||||
<CustomRadio value="7_days">7 days</CustomRadio>
|
||||
<CustomRadio value="14_days">14 days</CustomRadio>
|
||||
</RadioGroup>
|
||||
}
|
||||
classNames={{
|
||||
content: "w-full",
|
||||
}}
|
||||
focusedValue={value}
|
||||
nextButtonProps={{
|
||||
variant: "bordered",
|
||||
}}
|
||||
prevButtonProps={{
|
||||
variant: "bordered",
|
||||
}}
|
||||
topContent={
|
||||
<ButtonGroup
|
||||
fullWidth
|
||||
className="px-3 pb-2 pt-3 bg-content1 [&>button]:text-default-500 [&>button]:border-default-200/60"
|
||||
radius="full"
|
||||
size="sm"
|
||||
variant="bordered"
|
||||
>
|
||||
<Button onPress={() => setValue(now)}>Today</Button>
|
||||
<Button onPress={() => setValue(nextWeek)}>Next week</Button>
|
||||
<Button onPress={() => setValue(nextMonth)}>Next month</Button>
|
||||
</ButtonGroup>
|
||||
}
|
||||
value={value}
|
||||
onChange={setValue}
|
||||
onFocusChange={setValue}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/App.tsx": AppTs,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
const App = `import {Calendar} from "@nextui-org/react";
|
||||
import {today, getLocalTimeZone} from "@internationalized/date";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<Calendar
|
||||
aria-label="Date (Read Only)"
|
||||
value={today(getLocalTimeZone())}
|
||||
isReadOnly
|
||||
/>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
const App = `import {Calendar} from "@nextui-org/react";
|
||||
import {today, getLocalTimeZone, isWeekend} from "@internationalized/date";
|
||||
import {useLocale} from "@react-aria/i18n";
|
||||
|
||||
|
||||
export default function App() {
|
||||
let now = today(getLocalTimeZone());
|
||||
|
||||
let disabledRanges = [
|
||||
[now, now.add({days: 5})],
|
||||
[now.add({days: 14}), now.add({days: 16})],
|
||||
[now.add({days: 23}), now.add({days: 24})],
|
||||
];
|
||||
|
||||
let {locale} = useLocale();
|
||||
|
||||
let isDateUnavailable = (date) =>
|
||||
isWeekend(date, locale) ||
|
||||
disabledRanges.some(
|
||||
(interval) => date.compare(interval[0]) >= 0 && date.compare(interval[1]) <= 0,
|
||||
);
|
||||
|
||||
return (
|
||||
<Calendar
|
||||
aria-label="Date (Unavailable)"
|
||||
isDateUnavailable={isDateUnavailable}
|
||||
/>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
const App = `import {Calendar} from "@nextui-org/react";
|
||||
import {parseDate} from '@internationalized/date';
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<div className="flex gap-x-4">
|
||||
<Calendar aria-label="Date (No Selection)" />
|
||||
<Calendar aria-label="Date (Uncontrolled)" defaultValue={parseDate("2020-02-03")} />
|
||||
</div>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
const App = `import {Calendar} from "@nextui-org/react";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<Calendar
|
||||
aria-label="Date (Visible Month)"
|
||||
visibleMonths={3}
|
||||
/>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
const App = `import {Calendar} from "@nextui-org/react";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<Calendar
|
||||
aria-label="Date (Show Month and Year Picker)"
|
||||
showMonthAndYearPickers
|
||||
/>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
@@ -11,7 +11,7 @@ export default function App() {
|
||||
alt="Woman listing to music"
|
||||
className="object-cover"
|
||||
height={200}
|
||||
src="/images/hero-card.jpeg"
|
||||
src="https://nextui.org/images/hero-card.jpeg"
|
||||
width={200}
|
||||
/>
|
||||
<CardFooter className="justify-between before:bg-white/10 border-white/20 border-1 overflow-hidden py-1 absolute before:rounded-xl rounded-large bottom-1 w-[calc(100%_-_8px)] shadow-small ml-1 z-10">
|
||||
|
||||
@@ -165,7 +165,7 @@ export default function App() {
|
||||
className="object-cover"
|
||||
height={200}
|
||||
shadow="md"
|
||||
src="/images/album-cover.png"
|
||||
src="https://nextui.org/images/album-cover.png"
|
||||
width="100%"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -7,7 +7,7 @@ export default function App() {
|
||||
<Card className="max-w-[340px]">
|
||||
<CardHeader className="justify-between">
|
||||
<div className="flex gap-5">
|
||||
<Avatar isBordered radius="full" size="md" src="/avatars/avatar-1.png" />
|
||||
<Avatar isBordered radius="full" size="md" src="https://nextui.org/avatars/avatar-1.png" />
|
||||
<div className="flex flex-col gap-1 items-start justify-center">
|
||||
<h4 className="text-small font-semibold leading-none text-default-600">Zoey Lang</h4>
|
||||
<h5 className="text-small tracking-tight text-default-400">@zoeylang</h5>
|
||||
|
||||
@@ -12,7 +12,7 @@ export default function App() {
|
||||
removeWrapper
|
||||
alt="Card background"
|
||||
className="z-0 w-full h-full object-cover"
|
||||
src="/images/card-example-4.jpeg"
|
||||
src="https://nextui.org/images/card-example-4.jpeg"
|
||||
/>
|
||||
</Card>
|
||||
<Card className="col-span-12 sm:col-span-4 h-[300px]">
|
||||
@@ -24,7 +24,7 @@ export default function App() {
|
||||
removeWrapper
|
||||
alt="Card background"
|
||||
className="z-0 w-full h-full object-cover"
|
||||
src="/images/card-example-3.jpeg"
|
||||
src="https://nextui.org/images/card-example-3.jpeg"
|
||||
/>
|
||||
</Card>
|
||||
<Card className="col-span-12 sm:col-span-4 h-[300px]">
|
||||
@@ -36,7 +36,7 @@ export default function App() {
|
||||
removeWrapper
|
||||
alt="Card background"
|
||||
className="z-0 w-full h-full object-cover"
|
||||
src="/images/card-example-2.jpeg"
|
||||
src="https://nextui.org/images/card-example-2.jpeg"
|
||||
/>
|
||||
</Card>
|
||||
<Card isFooterBlurred className="w-full h-[300px] col-span-12 sm:col-span-5">
|
||||
@@ -48,7 +48,7 @@ export default function App() {
|
||||
removeWrapper
|
||||
alt="Card example background"
|
||||
className="z-0 w-full h-full scale-125 -translate-y-6 object-cover"
|
||||
src="/images/card-example-6.jpeg"
|
||||
src="https://nextui.org/images/card-example-6.jpeg"
|
||||
/>
|
||||
<CardFooter className="absolute bg-white/30 bottom-0 border-t-1 border-zinc-100/50 z-10 justify-between">
|
||||
<div>
|
||||
@@ -69,14 +69,14 @@ export default function App() {
|
||||
removeWrapper
|
||||
alt="Relaxing app background"
|
||||
className="z-0 w-full h-full object-cover"
|
||||
src="/images/card-example-5.jpeg"
|
||||
src="https://nextui.org/images/card-example-5.jpeg"
|
||||
/>
|
||||
<CardFooter className="absolute bg-black/40 bottom-0 z-10 border-t-1 border-default-600 dark:border-default-100">
|
||||
<div className="flex flex-grow gap-2 items-center">
|
||||
<Image
|
||||
alt="Breathing app icon"
|
||||
className="rounded-full w-10 h-11 bg-black"
|
||||
src="/images/breathing-app-icon.jpeg"
|
||||
src="https://nextui.org/images/breathing-app-icon.jpeg"
|
||||
/>
|
||||
<div className="flex flex-col">
|
||||
<p className="text-tiny text-white/60">Breathing App</p>
|
||||
|
||||
@@ -12,7 +12,7 @@ export default function App() {
|
||||
<Image
|
||||
alt="Card background"
|
||||
className="object-cover rounded-xl"
|
||||
src="/images/hero-card-complete.jpeg"
|
||||
src="https://nextui.org/images/hero-card-complete.jpeg"
|
||||
width={270}
|
||||
/>
|
||||
</CardBody>
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
const App = `import {DateInput} from "@nextui-org/react";
|
||||
import {parseDate, getLocalTimeZone} from "@internationalized/date";
|
||||
import {useDateFormatter} from "@react-aria/i18n";
|
||||
|
||||
export default function App() {
|
||||
const [value, setValue] = React.useState(parseDate("2024-04-04"));
|
||||
|
||||
let formatter = useDateFormatter({dateStyle: "full"});
|
||||
|
||||
return (
|
||||
<div className="w-full flex flex-row gap-2">
|
||||
<div className="w-full flex flex-col gap-y-2">
|
||||
<DateInput label="Date (controlled)" value={value} onChange={setValue} />
|
||||
<p className="text-default-500 text-sm">
|
||||
Selected date: {value ? formatter.format(value.toDate(getLocalTimeZone())) : "--"}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}`;
|
||||
|
||||
const AppTs = `import {DateInput} from "@nextui-org/react";
|
||||
import {DateValue, parseDate, getLocalTimeZone} from "@internationalized/date";
|
||||
import {useDateFormatter} from "@react-aria/i18n";
|
||||
|
||||
export default function App() {
|
||||
const [value, setValue] = React.useState<DateValue>(parseDate("2024-04-04"));
|
||||
|
||||
let formatter = useDateFormatter({dateStyle: "full"});
|
||||
|
||||
return (
|
||||
<div className="w-full flex flex-row gap-2">
|
||||
<div className="w-full flex flex-col gap-y-2">
|
||||
<DateInput label="Date (controlled)" value={value} onChange={setValue} />
|
||||
<p className="text-default-500 text-sm">
|
||||
Selected date: {value ? formatter.format(value.toDate(getLocalTimeZone())) : "--"}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/App.tsx": AppTs,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
const App = `import {DateInput} from "@nextui-org/react";
|
||||
import {CalendarDate} from "@internationalized/date";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<div className="flex w-full flex-wrap md:flex-nowrap gap-4">
|
||||
<DateInput
|
||||
label={"Birth date"}
|
||||
placeholderValue={new CalendarDate(1995, 11, 6)}
|
||||
description={"This is my birth date."}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
const App = `import {DateInput} from "@nextui-org/react";
|
||||
import {CalendarDate, parseDate} from "@internationalized/date";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<div className="flex w-full flex-wrap md:flex-nowrap gap-4">
|
||||
<DateInput
|
||||
label={"Birth date"}
|
||||
isDisabled
|
||||
defaultValue={parseDate("2024-04-04")}
|
||||
placeholderValue={new CalendarDate(1995, 11, 6)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
const App = `import {DateInput} from "@nextui-org/react";
|
||||
import {CalendarDate, parseDate} from "@internationalized/date";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<div className="flex w-full flex-wrap md:flex-nowrap gap-4">
|
||||
<DateInput
|
||||
label={"Birth date"}
|
||||
defaultValue={parseDate("2024-04-04")}
|
||||
placeholderValue={new CalendarDate(1995, 11, 6)}
|
||||
description={"Thiis is my birth date."}
|
||||
isInvalid
|
||||
errorMessage="Please enter a valid date."
|
||||
className="max-w-xs"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
@@ -0,0 +1,60 @@
|
||||
const App = `import {DateInput} from "@nextui-org/react";
|
||||
import {now, parseAbsoluteToLocal} from "@internationalized/date";
|
||||
import {useDateFormatter} from "@react-aria/i18n";
|
||||
|
||||
export default function App() {
|
||||
let [date, setDate] = React.useState(parseAbsoluteToLocal("2021-04-07T18:45:22Z"));
|
||||
|
||||
return (
|
||||
<div className="w-full max-w-xl flex flex-col items-start gap-4">
|
||||
<DateInput
|
||||
granularity="second"
|
||||
label="Date and time"
|
||||
value={date}
|
||||
onChange={setDate}
|
||||
/>
|
||||
<DateInput granularity="day" label="Date" value={date} onChange={setDate} />
|
||||
<DateInput granularity="second" label="Event date" />
|
||||
<DateInput
|
||||
granularity="second"
|
||||
label="Event date"
|
||||
placeholderValue={now("America/New_York")}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}`;
|
||||
|
||||
const AppTs = `import {DateInput} from "@nextui-org/react";
|
||||
import {DateValue, now, parseAbsoluteToLocal} from "@internationalized/date";
|
||||
import {useDateFormatter} from "@react-aria/i18n";
|
||||
|
||||
export default function App() {
|
||||
let [date, setDate] = React.useState<DateValue>(parseAbsoluteToLocal("2021-04-07T18:45:22Z"));
|
||||
|
||||
return (
|
||||
<div className="w-full max-w-xl flex flex-col items-start gap-4">
|
||||
<DateInput
|
||||
granularity="second"
|
||||
label="Date and time"
|
||||
value={date}
|
||||
onChange={setDate}
|
||||
/>
|
||||
<DateInput granularity="day" label="Date" value={date} onChange={setDate} />
|
||||
<DateInput granularity="second" label="Event date" />
|
||||
<DateInput
|
||||
granularity="second"
|
||||
label="Event date"
|
||||
placeholderValue={now("America/New_York")}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/App.tsx": AppTs,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
const App = `import {DateInput} from "@nextui-org/react";
|
||||
import {parseZonedDateTime} from "@internationalized/date";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<div className="w-full max-w-xl flex flex-row gap-4">
|
||||
<DateInput
|
||||
label="Appointment time"
|
||||
hideTimeZone
|
||||
defaultValue={parseZonedDateTime("2022-11-07T00:45[America/Los_Angeles]")}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
const App = `import {DateInput} from "@nextui-org/react";
|
||||
import {parseZonedDateTime} from "@internationalized/date";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<div className="w-full max-w-xl flex flex-row gap-4">
|
||||
<DateInput
|
||||
label="Appointment time"
|
||||
hourCycle={24}
|
||||
defaultValue={parseZonedDateTime("2022-11-07T00:45[America/Los_Angeles]")}
|
||||
granularity={"minute"}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
import usage from "./usage";
|
||||
import disabled from "./disabled";
|
||||
import readOnly from "./readonly";
|
||||
import required from "./required";
|
||||
import variants from "./variants";
|
||||
import labelPlacements from "./label-placements";
|
||||
import description from "./description";
|
||||
import startEndContent from "./start-end-content";
|
||||
import errorMessage from "./error-message";
|
||||
import controlled from "./controlled";
|
||||
import timeZones from "./time-zones";
|
||||
import granularity from "./granularity";
|
||||
import minAndMaxDate from "./min-and-max-date";
|
||||
import internationalCalendar from "./international-calendar";
|
||||
import hideTimeZone from "./hide-timezone";
|
||||
import hourlyCycle from "./hourly-cycle";
|
||||
|
||||
export const dateInputContent = {
|
||||
usage,
|
||||
disabled,
|
||||
readOnly,
|
||||
required,
|
||||
variants,
|
||||
labelPlacements,
|
||||
description,
|
||||
startEndContent,
|
||||
errorMessage,
|
||||
controlled,
|
||||
timeZones,
|
||||
granularity,
|
||||
minAndMaxDate,
|
||||
internationalCalendar,
|
||||
hideTimeZone,
|
||||
hourlyCycle,
|
||||
};
|
||||
@@ -0,0 +1,40 @@
|
||||
const App = `import {DateInput} from "@nextui-org/react";
|
||||
import {now, parseAbsoluteToLocal} from "@internationalized/date";
|
||||
import {I18nProvider} from "@react-aria/i18n";
|
||||
|
||||
export default function App() {
|
||||
const [date, setDate] = React.useState(parseAbsoluteToLocal("2021-04-07T18:45:22Z"));
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<I18nProvider locale="hi-IN-u-ca-indian">
|
||||
<DateInput label="Appointment date" value={date} onChange={setDate} />
|
||||
</I18nProvider>
|
||||
</div>
|
||||
);
|
||||
}`;
|
||||
|
||||
const AppTs = `import {DateInput} from "@nextui-org/react";
|
||||
import {DateValue, now, parseAbsoluteToLocal} from "@internationalized/date";
|
||||
import {I18nProvider} from "@react-aria/i18n";
|
||||
|
||||
export default function App() {
|
||||
const [date, setDate] = React.useState<DateValue>(parseAbsoluteToLocal("2021-04-07T18:45:22Z"));
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<I18nProvider locale="hi-IN-u-ca-indian">
|
||||
<DateInput label="Appointment date" value={date} onChange={setDate} />
|
||||
</I18nProvider>
|
||||
</div>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/App.tsx": AppTs,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
const App = `import {DateInput} from "@nextui-org/react";
|
||||
import {CalendarDate} from "@internationalized/date";
|
||||
|
||||
export default function App() {
|
||||
const placements = [
|
||||
"inside",
|
||||
"outside",
|
||||
"outside-left",
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="w-full flex flex-col max-w-sm gap-4">
|
||||
{placements.map((placement) => (
|
||||
<DateInput
|
||||
label="Birth date"
|
||||
placeholderValue={new CalendarDate(1995, 11, 6)}
|
||||
description={placement}
|
||||
labelPlacement={placement}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user