Compare commits

...

36 Commits

Author SHA1 Message Date
shadcn
cbf7b3708a chore: minor refactor and error message 2026-01-06 13:23:13 +04:00
yeasin2002
9da12c13f9 fix(cli): #9160 updated CLI name validation 2025-12-20 17:36:15 +06:00
Devon Govett
ccafdaf7c6 Add React Aria registry (#9121) 2025-12-19 02:22:27 +04:00
github-actions[bot]
f0d147d581 chore(release): version packages (#9125)
* chore(release): version packages

* chore: deps

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: shadcn <m@shadcn.com>
2025-12-17 22:07:42 +04:00
shadcn
df67e49aac chore: add create command to readme (#9126) 2025-12-17 22:02:28 +04:00
shadcn
c0de90e1a1 ci: update permissions 2025-12-17 21:57:07 +04:00
shadcn
0447708efa Merge branch 'main' of github.com:shadcn-ui/ui 2025-12-17 21:52:50 +04:00
shadcn
4a470fc617 ci: update release 2025-12-17 21:52:29 +04:00
Dominik K.
137b1c12b7 feat(ui): add support for phosphor icons (#9044)
* feat: add phosphor icons to base ui

* feat_ add phosphor to blocks

* feat: add phosphor to radix blocks

* feat: add phosphor to radix ui

* feat: add phosphor to radix example

* feat: add missing phosphor icons

* fix: rename broken icons

* chore: format files

* fix: add missing phosphor icons

* chore: build registry

---------

Co-authored-by: shadcn <m@shadcn.com>
2025-12-17 21:36:46 +04:00
shadcn
73296e79c0 ci: switch to oidc 2025-12-17 21:31:02 +04:00
Hamed.dev
78e5fa2a39 fix(mira): combobox popup background in dark mode (#9039)
* fix(mira): combobox popup background in dark mode

* fix

---------

Co-authored-by: shadcn <m@shadcn.com>
2025-12-17 21:10:40 +04:00
François Best
9cf47dd4a3 fix: preview iframe URL state sync (#9095)
* fix: preview iframe URL state sync

* fix: reset iframe on base change
2025-12-17 21:09:14 +04:00
shadcn
f53400f934 chore: add livekit to directory (#9109) 2025-12-16 19:47:47 +04:00
Brendan Dash
b3d6f872db fix: remove duplicate @ui-layouts entry from registries.json (#9106) 2025-12-16 19:44:40 +04:00
Zaid Mukaddam
2aa5e11f6f Add "Open in Scira" in Copy menu item (#8013)
* Add "Open in Scira" in Copy menu item

* Fix link
2025-12-16 19:43:37 +04:00
Luka Klacar
058ebc5acd Remove duplicate @einui entry from directory.json (#9091)
Removed entry for @einui from the directory.
2025-12-16 03:24:24 +04:00
shadcn
a60683dea5 fix 2025-12-15 15:49:35 +04:00
shadcn
1dc1b8dbfb chore: remove console 2025-12-15 15:49:02 +04:00
shadcn
c6273cca03 fix: hover bug for pickers (#9084)
* fix

* fix
2025-12-15 15:46:16 +04:00
Gravityy
b15d7e8221 feat : add animbits to trusted registries (#8910)
* Add @animbits registry URL to registries.json and directory.json

* Add @animbits registry URL to registries.json and directory.json

* fix

---------

Co-authored-by: shadcn <m@shadcn.com>
2025-12-15 14:38:24 +04:00
shadcn
46e3c26a6e chore: registry build 2025-12-15 14:33:37 +04:00
Bundui.io
f36e25f703 feat(registry): Add BundUI Components to the registry index (#8473)
* feat(registry): Add BundUI Components to the registry index

* fix

---------

Co-authored-by: shadcn <m@shadcn.com>
2025-12-15 14:19:50 +04:00
MUDAVATH KUMAR
55f5d1c7cc feat: add Ein UI to registry directory (#9068)
Add Ein UI registry entry with:
- Registry URL: https://ui.eindev.ir/r/{name}.json
- Homepage: https://ui.eindev.ir
- Description: Beautiful, responsive Shadcn components with frosted glass morphism
- Custom gradient logo with glass theme

Resolves: #9048

Co-authored-by: Clacky <develop@clacky.ai>
2025-12-15 13:44:50 +04:00
Tham Kei Lok
db19605996 feat: add @8starlabs-ui to directory.json and registries.json (#8976) 2025-12-15 13:44:24 +04:00
Agustín Mayol
40012adb14 feat: add @optics to directory.json and registries.json (#8971) 2025-12-15 13:44:10 +04:00
Ali Hussein
ad8104e473 feat: add @cardcn registry #8902 (#8919) 2025-12-15 13:43:52 +04:00
Gildas Garcia
5fb0c4d19a Add shadcn-admin-kit to the registry index (#9018)
This PR adds https://marmelab.com/shadcn-admin-kit to the trusted registries.
2025-12-15 13:43:01 +04:00
Bruno Pérez
31c86f9fd5 feat: add @manifest registry (#9010) (#9011) 2025-12-15 13:42:11 +04:00
Gxuri
aad175ff87 feat: add @skiper-ui to directory.json (#9007)
* feat: add @skiper-ui to directory.json

* fix: update URL format for @skiper-ui in directory.json
2025-12-15 13:41:48 +04:00
Muskri
081c91c461 feat: add new registry entry for @pureui with logo and description (#8911) 2025-12-15 13:40:01 +04:00
Dominik K.
7dbf3688fb fix: rename hugeicons dot icon (#9033)
Renames the hugeicons icon from MoreHorizontalIcon to the actual component name MoreHorizontalCircle01Icon
2025-12-15 13:35:56 +04:00
Manuel
99ad18b389 fix: remove duplicate 'text-left' class from SelectValue component (#9045)
* fix: remove duplicate 'text-left' class from SelectValue component

* fix: select for base

---------

Co-authored-by: shadcn <m@shadcn.com>
2025-12-15 13:33:27 +04:00
Junaid Anjum
fabb886de9 minor typo in the recent changelog (#9024) 2025-12-15 13:29:04 +04:00
ateeb a.
4b561cf050 fix:select color-format component and color copy-to-clipboard (#9056)
* fix:Nuqs adapter scope, select color-format component and color copy-to-clipboard

* chore: remove changeset

---------

Co-authored-by: shadcn <m@shadcn.com>
2025-12-15 13:27:10 +04:00
Mert
0c2373f592 fix(lint): resolve @typescript-eslint/no-unused-vars warns in combobox and scroll-area (#9060) 2025-12-15 13:24:30 +04:00
François Best
ff42c27d41 refactor: nuqs APIs (#9055)
* ref: replace cache with loader + inferParserType

* ref: expose useDesignSystemSearchParams hook

* ref: use loader & exported hook to simplify iframe sync

* ref: remove unused code

* fix: params.size is non-nullable

* ref: move items shallow option to the parser level

---------

Co-authored-by: shadcn <m@shadcn.com>
2025-12-15 13:19:32 +04:00
804 changed files with 3526 additions and 3587 deletions

View File

@@ -0,0 +1,5 @@
---
"shadcn": patch
---
validate app name on create

View File

@@ -7,6 +7,11 @@ on:
branches:
- main
permissions:
id-token: write
contents: write
pull-requests: write
jobs:
release:
if: ${{ github.repository_owner == 'shadcn-ui' }}
@@ -24,12 +29,15 @@ jobs:
version: 9.0.6
- name: Use Node.js 20
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
version: 9.0.6
node-version: 20
registry-url: "https://registry.npmjs.org"
cache: "pnpm"
- name: Update npm for OIDC support
run: npm install -g npm@latest
- name: Install NPM Dependencies
run: pnpm install
@@ -49,5 +57,4 @@ jobs:
publish: npx changeset publish
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }}
NODE_ENV: "production"

View File

@@ -1,7 +1,5 @@
"use client"
import { useQueryStates } from "nuqs"
import { MENU_ACCENTS, type MenuAccentValue } from "@/registry/config"
import { LockButton } from "@/app/(create)/components/lock-button"
import {
@@ -12,7 +10,7 @@ import {
PickerRadioItem,
PickerTrigger,
} from "@/app/(create)/components/picker"
import { designSystemSearchParams } from "@/app/(create)/lib/search-params"
import { useDesignSystemSearchParams } from "@/app/(create)/lib/search-params"
export function MenuAccentPicker({
isMobile,
@@ -21,18 +19,15 @@ export function MenuAccentPicker({
isMobile: boolean
anchorRef: React.RefObject<HTMLDivElement | null>
}) {
const [params, setParams] = useQueryStates(designSystemSearchParams, {
shallow: false,
history: "push",
})
const [params, setParams] = useDesignSystemSearchParams()
const currentAccent = MENU_ACCENTS.find(
(accent) => accent.value === params.menuAccent
)
return (
<Picker>
<div className="group/picker relative">
<div className="group/picker relative">
<Picker>
<PickerTrigger>
<div className="flex flex-col justify-start text-left">
<div className="text-muted-foreground text-xs">Menu Accent</div>
@@ -40,7 +35,7 @@ export function MenuAccentPicker({
{currentAccent?.label}
</div>
</div>
<div className="text-foreground absolute top-1/2 right-4 flex size-4 -translate-y-1/2 items-center justify-center text-base">
<div className="text-foreground pointer-events-none absolute top-1/2 right-4 flex size-4 -translate-y-1/2 items-center justify-center text-base select-none">
<svg
xmlns="http://www.w3.org/2000/svg"
width="128"
@@ -70,31 +65,31 @@ export function MenuAccentPicker({
</svg>
</div>
</PickerTrigger>
<LockButton
param="menuAccent"
className="absolute top-1/2 right-10 -translate-y-1/2"
/>
</div>
<PickerContent
anchor={isMobile ? anchorRef : undefined}
side={isMobile ? "top" : "right"}
align={isMobile ? "center" : "start"}
>
<PickerRadioGroup
value={currentAccent?.value}
onValueChange={(value) => {
setParams({ menuAccent: value as MenuAccentValue })
}}
<PickerContent
anchor={isMobile ? anchorRef : undefined}
side={isMobile ? "top" : "right"}
align={isMobile ? "center" : "start"}
>
<PickerGroup>
{MENU_ACCENTS.map((accent) => (
<PickerRadioItem key={accent.value} value={accent.value}>
{accent.label}
</PickerRadioItem>
))}
</PickerGroup>
</PickerRadioGroup>
</PickerContent>
</Picker>
<PickerRadioGroup
value={currentAccent?.value}
onValueChange={(value) => {
setParams({ menuAccent: value as MenuAccentValue })
}}
>
<PickerGroup>
{MENU_ACCENTS.map((accent) => (
<PickerRadioItem key={accent.value} value={accent.value}>
{accent.label}
</PickerRadioItem>
))}
</PickerGroup>
</PickerRadioGroup>
</PickerContent>
</Picker>
<LockButton
param="menuAccent"
className="absolute top-1/2 right-10 -translate-y-1/2"
/>
</div>
)
}

View File

@@ -2,7 +2,6 @@
import * as React from "react"
import { useTheme } from "next-themes"
import { useQueryStates } from "nuqs"
import { useMounted } from "@/hooks/use-mounted"
import { BASE_COLORS, type BaseColorName } from "@/registry/config"
@@ -17,7 +16,7 @@ import {
PickerSeparator,
PickerTrigger,
} from "@/app/(create)/components/picker"
import { designSystemSearchParams } from "@/app/(create)/lib/search-params"
import { useDesignSystemSearchParams } from "@/app/(create)/lib/search-params"
export function BaseColorPicker({
isMobile,
@@ -28,10 +27,7 @@ export function BaseColorPicker({
}) {
const { resolvedTheme, setTheme } = useTheme()
const mounted = useMounted()
const [params, setParams] = useQueryStates(designSystemSearchParams, {
shallow: false,
history: "push",
})
const [params, setParams] = useDesignSystemSearchParams()
const currentBaseColor = React.useMemo(
() => BASE_COLORS.find((baseColor) => baseColor.name === params.baseColor),
@@ -39,8 +35,8 @@ export function BaseColorPicker({
)
return (
<Picker>
<div className="group/picker relative">
<div className="group/picker relative">
<Picker>
<PickerTrigger>
<div className="flex flex-col justify-start text-left">
<div className="text-muted-foreground text-xs">Base Color</div>
@@ -58,72 +54,72 @@ export function BaseColorPicker({
]?.["muted-foreground"],
} as React.CSSProperties
}
className="absolute top-1/2 right-4 size-4 -translate-y-1/2 rounded-full bg-(--color)"
className="pointer-events-none absolute top-1/2 right-4 size-4 -translate-y-1/2 rounded-full bg-(--color) select-none"
/>
)}
</PickerTrigger>
<LockButton
param="baseColor"
className="absolute top-1/2 right-10 -translate-y-1/2"
/>
</div>
<PickerContent
anchor={isMobile ? anchorRef : undefined}
side={isMobile ? "top" : "right"}
align={isMobile ? "center" : "start"}
>
<PickerRadioGroup
value={currentBaseColor?.name}
onValueChange={(value) => {
if (value === "dark") {
setTheme(resolvedTheme === "dark" ? "light" : "dark")
return
}
setParams({ baseColor: value as BaseColorName })
}}
<PickerContent
anchor={isMobile ? anchorRef : undefined}
side={isMobile ? "top" : "right"}
align={isMobile ? "center" : "start"}
>
<PickerGroup>
{BASE_COLORS.map((baseColor) => (
<PickerRadioItem key={baseColor.name} value={baseColor.name}>
<div className="flex items-center gap-2">
{mounted && resolvedTheme && (
<div
style={
{
"--color":
baseColor.cssVars?.[
resolvedTheme as "light" | "dark"
]?.["muted-foreground"],
} as React.CSSProperties
}
className="size-4 rounded-full bg-(--color)"
/>
)}
{baseColor.title}
</div>
</PickerRadioItem>
))}
</PickerGroup>
<PickerSeparator />
<PickerGroup>
<PickerItem
onClick={() => {
<PickerRadioGroup
value={currentBaseColor?.name}
onValueChange={(value) => {
if (value === "dark") {
setTheme(resolvedTheme === "dark" ? "light" : "dark")
}}
>
<div className="flex flex-col justify-start pointer-coarse:gap-1">
<div>
Switch to {resolvedTheme === "dark" ? "Light" : "Dark"} Mode
return
}
setParams({ baseColor: value as BaseColorName })
}}
>
<PickerGroup>
{BASE_COLORS.map((baseColor) => (
<PickerRadioItem key={baseColor.name} value={baseColor.name}>
<div className="flex items-center gap-2">
{mounted && resolvedTheme && (
<div
style={
{
"--color":
baseColor.cssVars?.[
resolvedTheme as "light" | "dark"
]?.["muted-foreground"],
} as React.CSSProperties
}
className="size-4 rounded-full bg-(--color)"
/>
)}
{baseColor.title}
</div>
</PickerRadioItem>
))}
</PickerGroup>
<PickerSeparator />
<PickerGroup>
<PickerItem
onClick={() => {
setTheme(resolvedTheme === "dark" ? "light" : "dark")
}}
>
<div className="flex flex-col justify-start pointer-coarse:gap-1">
<div>
Switch to {resolvedTheme === "dark" ? "Light" : "Dark"} Mode
</div>
<div className="text-muted-foreground text-xs pointer-coarse:text-sm">
Base colors are easier to see in dark mode.
</div>
</div>
<div className="text-muted-foreground text-xs pointer-coarse:text-sm">
Base colors are easier to see in dark mode.
</div>
</div>
</PickerItem>
</PickerGroup>
</PickerRadioGroup>
</PickerContent>
</Picker>
</PickerItem>
</PickerGroup>
</PickerRadioGroup>
</PickerContent>
</Picker>
<LockButton
param="baseColor"
className="absolute top-1/2 right-10 -translate-y-1/2"
/>
</div>
)
}

View File

@@ -1,7 +1,6 @@
"use client"
import * as React from "react"
import { useQueryStates } from "nuqs"
import { BASES } from "@/registry/config"
import {
@@ -12,7 +11,7 @@ import {
PickerRadioItem,
PickerTrigger,
} from "@/app/(create)/components/picker"
import { designSystemSearchParams } from "@/app/(create)/lib/search-params"
import { useDesignSystemSearchParams } from "@/app/(create)/lib/search-params"
export function BasePicker({
isMobile,
@@ -21,10 +20,7 @@ export function BasePicker({
isMobile: boolean
anchorRef: React.RefObject<HTMLDivElement | null>
}) {
const [params, setParams] = useQueryStates(designSystemSearchParams, {
shallow: false,
history: "push",
})
const [params, setParams] = useDesignSystemSearchParams()
const currentBase = React.useMemo(
() => BASES.find((base) => base.name === params.base),
@@ -54,7 +50,7 @@ export function BasePicker({
</div>
{currentBase?.meta?.logo && (
<div
className="text-foreground *:[svg]:text-foreground! absolute top-1/2 right-4 size-4 -translate-y-1/2 *:[svg]:size-4"
className="text-foreground *:[svg]:text-foreground! pointer-events-none absolute top-1/2 right-4 size-4 -translate-y-1/2 select-none *:[svg]:size-4"
dangerouslySetInnerHTML={{
__html: currentBase.meta.logo,
}}

View File

@@ -1,11 +1,9 @@
"use client"
import * as React from "react"
import { useRouter } from "next/navigation"
import Script from "next/script"
import { DiceFaces05Icon, Undo02Icon } from "@hugeicons/core-free-icons"
import { HugeiconsIcon } from "@hugeicons/react"
import { useQueryStates } from "nuqs"
import { cn } from "@/lib/utils"
import {
@@ -20,11 +18,6 @@ import {
} from "@/registry/config"
import { Button } from "@/registry/new-york-v4/ui/button"
import { Kbd } from "@/registry/new-york-v4/ui/kbd"
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/registry/new-york-v4/ui/tooltip"
import { useLocks } from "@/app/(create)/hooks/use-locks"
import { FONTS } from "@/app/(create)/lib/fonts"
import {
@@ -32,7 +25,7 @@ import {
RANDOMIZE_BIASES,
type RandomizeContext,
} from "@/app/(create)/lib/randomize-biases"
import { designSystemSearchParams } from "@/app/(create)/lib/search-params"
import { useDesignSystemSearchParams } from "@/app/(create)/lib/search-params"
export const RANDOMIZE_FORWARD_TYPE = "randomize-forward"
@@ -41,12 +34,8 @@ function randomItem<T>(array: readonly T[]): T {
}
export function CustomizerControls({ className }: { className?: string }) {
const router = useRouter()
const { locks } = useLocks()
const [params, setParams] = useQueryStates(designSystemSearchParams, {
shallow: false,
history: "push",
})
const [params, setParams] = useDesignSystemSearchParams()
const handleReset = React.useCallback(() => {
setParams({

View File

@@ -3,7 +3,6 @@
import * as React from "react"
import { Settings05Icon } from "@hugeicons/core-free-icons"
import { HugeiconsIcon } from "@hugeicons/react"
import { useQueryStates } from "nuqs"
import { useIsMobile } from "@/hooks/use-mobile"
import { getThemesForBaseColor, PRESETS, STYLES } from "@/registry/config"
@@ -20,10 +19,10 @@ import { RadiusPicker } from "@/app/(create)/components/radius-picker"
import { StylePicker } from "@/app/(create)/components/style-picker"
import { ThemePicker } from "@/app/(create)/components/theme-picker"
import { FONTS } from "@/app/(create)/lib/fonts"
import { designSystemSearchParams } from "@/app/(create)/lib/search-params"
import { useDesignSystemSearchParams } from "@/app/(create)/lib/search-params"
export function Customizer() {
const [params] = useQueryStates(designSystemSearchParams)
const [params] = useDesignSystemSearchParams()
const isMobile = useIsMobile()
const anchorRef = React.useRef<HTMLDivElement | null>(null)

View File

@@ -7,21 +7,23 @@ import {
DEFAULT_CONFIG,
type DesignSystemConfig,
} from "@/registry/config"
import { useDesignSystemParam } from "@/app/(create)/hooks/use-design-system"
import { useIframeMessageListener } from "@/app/(create)/hooks/use-iframe-sync"
import { FONTS } from "@/app/(create)/lib/fonts"
import { useDesignSystemSearchParams } from "@/app/(create)/lib/search-params"
export function DesignSystemProvider({
children,
}: {
children: React.ReactNode
}) {
const style = useDesignSystemParam("style")
const theme = useDesignSystemParam("theme")
const font = useDesignSystemParam("font")
const baseColor = useDesignSystemParam("baseColor")
const menuAccent = useDesignSystemParam("menuAccent")
const menuColor = useDesignSystemParam("menuColor")
const radius = useDesignSystemParam("radius")
const [
{ style, theme, font, baseColor, menuAccent, menuColor, radius },
setSearchParams,
] = useDesignSystemSearchParams({
shallow: true, // No need to go through the server…
history: "replace", // …or push updates into the iframe history.
})
useIframeMessageListener("design-system-params", setSearchParams)
const [isReady, setIsReady] = React.useState(false)
// Use useLayoutEffect for synchronous style updates to prevent flash.

View File

@@ -1,7 +1,6 @@
"use client"
import * as React from "react"
import { useQueryStates } from "nuqs"
import {
Item,
@@ -21,7 +20,7 @@ import {
PickerTrigger,
} from "@/app/(create)/components/picker"
import { type Font } from "@/app/(create)/lib/fonts"
import { designSystemSearchParams } from "@/app/(create)/lib/search-params"
import { useDesignSystemSearchParams } from "@/app/(create)/lib/search-params"
export function FontPicker({
fonts,
@@ -32,10 +31,7 @@ export function FontPicker({
isMobile: boolean
anchorRef: React.RefObject<HTMLDivElement | null>
}) {
const [params, setParams] = useQueryStates(designSystemSearchParams, {
shallow: false,
history: "push",
})
const [params, setParams] = useDesignSystemSearchParams()
const currentFont = React.useMemo(
() => fonts.find((font) => font.value === params.font),
@@ -43,8 +39,8 @@ export function FontPicker({
)
return (
<Picker>
<div className="group/picker relative">
<div className="group/picker relative">
<Picker>
<PickerTrigger>
<div className="flex flex-col justify-start text-left">
<div className="text-muted-foreground text-xs">Font</div>
@@ -53,54 +49,55 @@ export function FontPicker({
</div>
</div>
<div
className="text-foreground absolute top-1/2 right-4 flex size-4 -translate-y-1/2 items-center justify-center text-base"
className="text-foreground pointer-events-none absolute top-1/2 right-4 flex size-4 -translate-y-1/2 items-center justify-center text-base select-none"
style={{ fontFamily: currentFont?.font.style.fontFamily }}
>
Aa
</div>
</PickerTrigger>
<LockButton
param="font"
className="absolute top-1/2 right-10 -translate-y-1/2"
/>
</div>
<PickerContent
anchor={isMobile ? anchorRef : undefined}
side={isMobile ? "top" : "right"}
align={isMobile ? "center" : "start"}
className="max-h-80 md:w-72"
>
<PickerRadioGroup
value={currentFont?.value}
onValueChange={(value) => {
setParams({ font: value as FontValue })
}}
<PickerContent
anchor={isMobile ? anchorRef : undefined}
side={isMobile ? "top" : "right"}
align={isMobile ? "center" : "start"}
className="max-h-80 md:w-72"
>
<PickerGroup>
{fonts.map((font, index) => (
<React.Fragment key={font.value}>
<PickerRadioItem value={font.value}>
<Item size="xs">
<ItemContent className="gap-1">
<ItemTitle className="text-muted-foreground text-xs font-medium">
{font.name}
</ItemTitle>
<ItemDescription
style={{ fontFamily: font.font.style.fontFamily }}
>
Designers love packing quirky glyphs into test phrases.
</ItemDescription>
</ItemContent>
</Item>
</PickerRadioItem>
{index < fonts.length - 1 && (
<PickerSeparator className="opacity-50" />
)}
</React.Fragment>
))}
</PickerGroup>
</PickerRadioGroup>
</PickerContent>
</Picker>
<PickerRadioGroup
value={currentFont?.value}
onValueChange={(value) => {
setParams({ font: value as FontValue })
}}
>
<PickerGroup>
{fonts.map((font, index) => (
<React.Fragment key={font.value}>
<PickerRadioItem value={font.value}>
<Item size="xs">
<ItemContent className="gap-1">
<ItemTitle className="text-muted-foreground text-xs font-medium">
{font.name}
</ItemTitle>
<ItemDescription
style={{ fontFamily: font.font.style.fontFamily }}
>
Designers love packing quirky glyphs into test
phrases.
</ItemDescription>
</ItemContent>
</Item>
</PickerRadioItem>
{index < fonts.length - 1 && (
<PickerSeparator className="opacity-50" />
)}
</React.Fragment>
))}
</PickerGroup>
</PickerRadioGroup>
</PickerContent>
</Picker>
<LockButton
param="font"
className="absolute top-1/2 right-10 -translate-y-1/2"
/>
</div>
)
}

View File

@@ -2,7 +2,6 @@
import * as React from "react"
import { lazy, memo, Suspense } from "react"
import { useQueryStates } from "nuqs"
import { Item, ItemContent, ItemTitle } from "@/registry/bases/radix/ui/item"
import {
@@ -20,7 +19,7 @@ import {
PickerSeparator,
PickerTrigger,
} from "@/app/(create)/components/picker"
import { designSystemSearchParams } from "@/app/(create)/lib/search-params"
import { useDesignSystemSearchParams } from "@/app/(create)/lib/search-params"
const IconLucide = lazy(() =>
import("@/registry/icons/icon-lucide").then((mod) => ({
@@ -40,6 +39,12 @@ const IconHugeicons = lazy(() =>
}))
)
const IconPhosphor = lazy(() =>
import("@/registry/icons/icon-phosphor").then((mod) => ({
default: mod.IconPhosphor,
}))
)
const PREVIEW_ICONS = {
lucide: [
"CopyIcon",
@@ -79,7 +84,7 @@ const PREVIEW_ICONS = {
"Delete02Icon",
"Share03Icon",
"ShoppingBag01Icon",
"MoreHorizontalIcon",
"MoreHorizontalCircle01Icon",
"Loading03Icon",
"PlusSignIcon",
"MinusSignIcon",
@@ -89,6 +94,22 @@ const PREVIEW_ICONS = {
"ArrowDown01Icon",
"ArrowRight01Icon",
],
phosphor: [
"CopyIcon",
"WarningCircleIcon",
"TrashIcon",
"ShareIcon",
"BagIcon",
"DotsThreeIcon",
"SpinnerIcon",
"PlusIcon",
"MinusIcon",
"ArrowLeftIcon",
"ArrowRightIcon",
"CheckIcon",
"CaretDownIcon",
"CaretRightIcon",
],
}
const logos = {
@@ -155,6 +176,24 @@ const logos = {
></path>
</svg>
),
phosphor: (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 32 32"
width="32"
height="32"
>
<path fill="none" d="M0 0h32v32H0z" />
<path
fill="none"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M9 5h9v16H9zm9 16v9a9 9 0 0 1-9-9M9 5l9 16m0 0h1a8 8 0 0 0 0-16h-1"
/>
</svg>
),
}
export function IconLibraryPicker({
@@ -164,10 +203,7 @@ export function IconLibraryPicker({
isMobile: boolean
anchorRef: React.RefObject<HTMLDivElement | null>
}) {
const [params, setParams] = useQueryStates(designSystemSearchParams, {
shallow: false,
history: "push",
})
const [params, setParams] = useDesignSystemSearchParams()
const currentIconLibrary = React.useMemo(
() => iconLibraries[params.iconLibrary as keyof typeof iconLibraries],
@@ -175,8 +211,8 @@ export function IconLibraryPicker({
)
return (
<Picker>
<div className="group/picker relative">
<div className="group/picker relative">
<Picker>
<PickerTrigger>
<div className="flex flex-col justify-start text-left">
<div className="text-muted-foreground text-xs">Icon Library</div>
@@ -184,42 +220,42 @@ export function IconLibraryPicker({
{currentIconLibrary?.title}
</div>
</div>
<div className="text-foreground *:[svg]:text-foreground! absolute top-1/2 right-4 flex size-4 -translate-y-1/2 items-center justify-center text-base">
<div className="text-foreground *:[svg]:text-foreground! pointer-events-none absolute top-1/2 right-4 flex size-4 -translate-y-1/2 items-center justify-center text-base select-none">
{logos[currentIconLibrary?.name as keyof typeof logos]}
</div>
</PickerTrigger>
<LockButton
param="iconLibrary"
className="absolute top-1/2 right-10 -translate-y-1/2"
/>
</div>
<PickerContent
anchor={isMobile ? anchorRef : undefined}
side={isMobile ? "top" : "right"}
align={isMobile ? "center" : "start"}
>
<PickerRadioGroup
value={currentIconLibrary?.name}
onValueChange={(value) => {
setParams({ iconLibrary: value as IconLibraryName })
}}
<PickerContent
anchor={isMobile ? anchorRef : undefined}
side={isMobile ? "top" : "right"}
align={isMobile ? "center" : "start"}
>
<PickerGroup>
{Object.values(iconLibraries).map((iconLibrary, index) => (
<React.Fragment key={iconLibrary.name}>
<IconLibraryPickerItem
iconLibrary={iconLibrary}
value={iconLibrary.name}
/>
{index < Object.values(iconLibraries).length - 1 && (
<PickerSeparator className="opacity-50" />
)}
</React.Fragment>
))}
</PickerGroup>
</PickerRadioGroup>
</PickerContent>
</Picker>
<PickerRadioGroup
value={currentIconLibrary?.name}
onValueChange={(value) => {
setParams({ iconLibrary: value as IconLibraryName })
}}
>
<PickerGroup>
{Object.values(iconLibraries).map((iconLibrary, index) => (
<React.Fragment key={iconLibrary.name}>
<IconLibraryPickerItem
iconLibrary={iconLibrary}
value={iconLibrary.name}
/>
{index < Object.values(iconLibraries).length - 1 && (
<PickerSeparator className="opacity-50" />
)}
</React.Fragment>
))}
</PickerGroup>
</PickerRadioGroup>
</PickerContent>
</Picker>
<LockButton
param="iconLibrary"
className="absolute top-1/2 right-10 -translate-y-1/2"
/>
</div>
)
}
@@ -263,7 +299,9 @@ const IconLibraryPreview = memo(function IconLibraryPreview({
? IconLucide
: iconLibrary === "tabler"
? IconTabler
: IconHugeicons
: iconLibrary === "hugeicons"
? IconHugeicons
: IconPhosphor
return (
<Suspense

View File

@@ -4,7 +4,7 @@ import { lazy, Suspense } from "react"
import { SquareIcon } from "lucide-react"
import type { IconLibraryName } from "shadcn/icons"
import { useDesignSystemParam } from "@/app/(create)/hooks/use-design-system"
import { useDesignSystemSearchParams } from "@/app/(create)/lib/search-params"
const IconLucide = lazy(() =>
import("@/registry/icons/icon-lucide").then((mod) => ({
@@ -24,12 +24,18 @@ const IconHugeicons = lazy(() =>
}))
)
const IconPhosphor = lazy(() =>
import("@/registry/icons/icon-phosphor").then((mod) => ({
default: mod.IconPhosphor,
}))
)
export function IconPlaceholder({
...props
}: {
[K in IconLibraryName]: string
} & React.ComponentProps<"svg">) {
const iconLibrary = useDesignSystemParam("iconLibrary")
const [{ iconLibrary }] = useDesignSystemSearchParams()
const iconName = props[iconLibrary]
if (!iconName) {
@@ -43,6 +49,9 @@ export function IconPlaceholder({
{iconLibrary === "hugeicons" && (
<IconHugeicons name={iconName} {...props} />
)}
{iconLibrary === "phosphor" && (
<IconPhosphor name={iconName} {...props} />
)}
</Suspense>
)
}

View File

@@ -3,7 +3,6 @@
import * as React from "react"
import Link from "next/link"
import { ChevronRightIcon } from "lucide-react"
import { useQueryStates } from "nuqs"
import { type RegistryItem } from "shadcn/schema"
import { cn } from "@/lib/utils"
@@ -22,7 +21,7 @@ import {
SidebarMenuButton,
SidebarMenuItem,
} from "@/registry/new-york-v4/ui/sidebar"
import { designSystemSearchParams } from "@/app/(create)/lib/search-params"
import { useDesignSystemSearchParams } from "@/app/(create)/lib/search-params"
import { groupItemsByType } from "@/app/(create)/lib/utils"
const cachedGroupedItems = React.cache(
@@ -38,10 +37,7 @@ export function ItemExplorer({
base: Base["name"]
items: Pick<RegistryItem, "name" | "title" | "type">[]
}) {
const [params, setParams] = useQueryStates(designSystemSearchParams, {
history: "push",
shallow: true,
})
const [params, setParams] = useDesignSystemSearchParams()
const groupedItems = React.useMemo(() => cachedGroupedItems(items), [items])

View File

@@ -4,7 +4,6 @@ import * as React from "react"
import Script from "next/script"
import { Search01Icon } from "@hugeicons/core-free-icons"
import { HugeiconsIcon } from "@hugeicons/react"
import { useQueryStates } from "nuqs"
import { type RegistryItem } from "shadcn/schema"
import { Button } from "@/registry/new-york-v4/ui/button"
@@ -21,7 +20,7 @@ import {
ComboboxTrigger,
ComboboxValue,
} from "@/registry/new-york-v4/ui/combobox"
import { designSystemSearchParams } from "@/app/(create)/lib/search-params"
import { useDesignSystemSearchParams } from "@/app/(create)/lib/search-params"
import { groupItemsByType } from "@/app/(create)/lib/utils"
export const CMD_K_FORWARD_TYPE = "cmd-k-forward"
@@ -38,10 +37,7 @@ export function ItemPicker({
items: Pick<RegistryItem, "name" | "title" | "type">[]
}) {
const [open, setOpen] = React.useState(false)
const [params, setParams] = useQueryStates(designSystemSearchParams, {
history: "push",
shallow: true,
})
const [params, setParams] = useDesignSystemSearchParams()
const groupedItems = React.useMemo(() => cachedGroupedItems(items), [items])

View File

@@ -2,7 +2,6 @@
import * as React from "react"
import { useTheme } from "next-themes"
import { useQueryStates } from "nuqs"
import { useMounted } from "@/hooks/use-mounted"
import { type MenuColorValue } from "@/registry/config"
@@ -15,7 +14,7 @@ import {
PickerRadioItem,
PickerTrigger,
} from "@/app/(create)/components/picker"
import { designSystemSearchParams } from "@/app/(create)/lib/search-params"
import { useDesignSystemSearchParams } from "@/app/(create)/lib/search-params"
const MENU_OPTIONS = [
{
@@ -114,17 +113,14 @@ export function MenuColorPicker({
}) {
const { resolvedTheme } = useTheme()
const mounted = useMounted()
const [params, setParams] = useQueryStates(designSystemSearchParams, {
shallow: false,
history: "push",
})
const [params, setParams] = useDesignSystemSearchParams()
const currentMenu = MENU_OPTIONS.find(
(menu) => menu.value === params.menuColor
)
return (
<Picker>
<div className="group/picker relative">
<div className="group/picker relative">
<Picker>
<PickerTrigger disabled={mounted && resolvedTheme === "dark"}>
<div className="flex flex-col justify-start text-left">
<div className="text-muted-foreground text-xs">Menu Color</div>
@@ -132,36 +128,36 @@ export function MenuColorPicker({
{currentMenu?.label}
</div>
</div>
<div className="text-foreground absolute top-1/2 right-4 flex size-4 -translate-y-1/2 items-center justify-center text-base">
<div className="text-foreground pointer-events-none absolute top-1/2 right-4 flex size-4 -translate-y-1/2 items-center justify-center text-base select-none">
{currentMenu?.icon}
</div>
</PickerTrigger>
<LockButton
param="menuColor"
className="absolute top-1/2 right-10 -translate-y-1/2"
/>
</div>
<PickerContent
anchor={isMobile ? anchorRef : undefined}
side={isMobile ? "top" : "right"}
align={isMobile ? "center" : "start"}
>
<PickerRadioGroup
value={currentMenu?.value}
onValueChange={(value) => {
setParams({ menuColor: value as MenuColorValue })
}}
<PickerContent
anchor={isMobile ? anchorRef : undefined}
side={isMobile ? "top" : "right"}
align={isMobile ? "center" : "start"}
>
<PickerGroup>
{MENU_OPTIONS.map((menu) => (
<PickerRadioItem key={menu.value} value={menu.value}>
{menu.icon}
{menu.label}
</PickerRadioItem>
))}
</PickerGroup>
</PickerRadioGroup>
</PickerContent>
</Picker>
<PickerRadioGroup
value={currentMenu?.value}
onValueChange={(value) => {
setParams({ menuColor: value as MenuColorValue })
}}
>
<PickerGroup>
{MENU_OPTIONS.map((menu) => (
<PickerRadioItem key={menu.value} value={menu.value}>
{menu.icon}
{menu.label}
</PickerRadioItem>
))}
</PickerGroup>
</PickerRadioGroup>
</PickerContent>
</Picker>
<LockButton
param="menuColor"
className="absolute top-1/2 right-10 -translate-y-1/2"
/>
</div>
)
}

View File

@@ -138,6 +138,7 @@ function PickerSubTrigger({
lucide="ChevronRightIcon"
tabler="IconChevronRight"
hugeicons="ArrowRight01Icon"
phosphor="CaretRightIcon"
className="ml-auto"
/>
</MenuPrimitive.SubmenuTrigger>
@@ -190,6 +191,7 @@ function PickerCheckboxItem({
lucide="CheckIcon"
tabler="IconCheck"
hugeicons="Tick02Icon"
phosphor="CheckIcon"
/>
</MenuPrimitive.CheckboxItemIndicator>
</span>
@@ -230,6 +232,7 @@ function PickerRadioItem({
lucide="CheckIcon"
tabler="IconCheck"
hugeicons="Tick02Icon"
phosphor="CheckIcon"
className="size-4 pointer-coarse:size-5"
/>
</MenuPrimitive.RadioItemIndicator>

View File

@@ -1,9 +1,8 @@
"use client"
import * as React from "react"
import { useQueryStates } from "nuqs"
import { BASES, STYLES, type Preset } from "@/registry/config"
import { STYLES, type Preset } from "@/registry/config"
import {
Picker,
PickerContent,
@@ -12,7 +11,7 @@ import {
PickerRadioItem,
PickerTrigger,
} from "@/app/(create)/components/picker"
import { designSystemSearchParams } from "@/app/(create)/lib/search-params"
import { useDesignSystemSearchParams } from "@/app/(create)/lib/search-params"
export function PresetPicker({
presets,
@@ -23,10 +22,7 @@ export function PresetPicker({
isMobile: boolean
anchorRef: React.RefObject<HTMLDivElement | null>
}) {
const [params, setParams] = useQueryStates(designSystemSearchParams, {
shallow: false,
history: "push",
})
const [params, setParams] = useDesignSystemSearchParams()
const currentPreset = React.useMemo(() => {
return presets.find(

View File

@@ -1,27 +1,26 @@
"use client"
import { Monitor, Smartphone, Tablet } from "lucide-react"
import { useQueryStates } from "nuqs"
import {
ToggleGroup,
ToggleGroupItem,
} from "@/registry/new-york-v4/ui/toggle-group"
import { designSystemSearchParams } from "@/app/(create)/lib/search-params"
import { useDesignSystemSearchParams } from "@/app/(create)/lib/search-params"
export function PreviewControls() {
const [urlParams, setUrlParams] = useQueryStates(designSystemSearchParams, {
shallow: false,
const [params, setParams] = useDesignSystemSearchParams({
history: "replace",
})
return (
<div className="flex h-8 items-center gap-1.5 rounded-md border p-1">
<ToggleGroup
type="single"
value={(urlParams.size ?? 100).toString()}
value={params.size.toString()}
onValueChange={(newValue) => {
if (newValue) {
setUrlParams({ size: parseInt(newValue) })
setParams({ size: parseInt(newValue) })
}
}}
className="gap-1 *:data-[slot=toggle-group-item]:!size-6 *:data-[slot=toggle-group-item]:!rounded-sm"

View File

@@ -7,16 +7,16 @@ import { DARK_MODE_FORWARD_TYPE } from "@/components/mode-switcher"
import { Badge } from "@/registry/new-york-v4/ui/badge"
import { RANDOMIZE_FORWARD_TYPE } from "@/app/(create)/components/customizer-controls"
import { CMD_K_FORWARD_TYPE } from "@/app/(create)/components/item-picker"
import { useDesignSystemSync } from "@/app/(create)/hooks/use-design-system"
const MESSAGE_TYPE = "design-system-params"
import { sendToIframe } from "@/app/(create)/hooks/use-iframe-sync"
import {
serializeDesignSystemSearchParams,
useDesignSystemSearchParams,
} from "@/app/(create)/lib/search-params"
export function Preview() {
const params = useDesignSystemSync()
const [params] = useDesignSystemSearchParams()
const iframeRef = React.useRef<HTMLIFrameElement>(null)
const resizablePanelRef = React.useRef<ImperativePanelHandle>(null)
const [initialParams] = React.useState(params)
const [iframeKey, setIframeKey] = React.useState(0)
// Sync resizable panel with URL param changes.
React.useEffect(() => {
@@ -32,13 +32,7 @@ export function Preview() {
}
const sendParams = () => {
iframe.contentWindow?.postMessage(
{
type: MESSAGE_TYPE,
params,
},
"*"
)
sendToIframe(iframe, "design-system-params", params)
}
if (iframe.contentWindow) {
@@ -96,18 +90,25 @@ export function Preview() {
}
}, [])
if (!params.item || !params.base) {
return null
}
const iframeSrc = `/preview/${params.base}/${params.item}?theme=${initialParams.theme ?? "neutral"}&iconLibrary=${initialParams.iconLibrary ?? "lucide"}&style=${initialParams.style ?? "vega"}&font=${initialParams.font ?? "inter"}&baseColor=${initialParams.baseColor ?? "neutral"}`
const iframeSrc = React.useMemo(() => {
// The iframe src needs to include the serialized design system params
// for the initial load, but not be reactive to them as it would cause
// full-iframe reloads on every param change (flashes & loss of state).
// Further updates of the search params will be sent to the iframe
// via a postMessage channel, for it to sync its own history onto the host's.
return serializeDesignSystemSearchParams(
`/preview/${params.base}/${params.item}`,
params
)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [params.base, params.item])
return (
<div className="relative -mx-1 flex flex-1 flex-col justify-center sm:mx-0">
<div className="ring-foreground/15 3xl:max-h-[1200px] 3xl:max-w-[1800px] relative -z-0 mx-auto flex w-full flex-1 flex-col overflow-hidden rounded-2xl ring-1">
<div className="bg-muted dark:bg-muted/30 absolute inset-0 rounded-2xl" />
<iframe
key={`${params.item}-${iframeKey}`}
key={params.base + params.item}
ref={iframeRef}
src={iframeSrc}
className="z-10 size-full flex-1"

View File

@@ -1,7 +1,5 @@
"use client"
import { useQueryStates } from "nuqs"
import { RADII, type RadiusValue } from "@/registry/config"
import { LockButton } from "@/app/(create)/components/lock-button"
import {
@@ -13,7 +11,7 @@ import {
PickerSeparator,
PickerTrigger,
} from "@/app/(create)/components/picker"
import { designSystemSearchParams } from "@/app/(create)/lib/search-params"
import { useDesignSystemSearchParams } from "@/app/(create)/lib/search-params"
export function RadiusPicker({
isMobile,
@@ -22,18 +20,15 @@ export function RadiusPicker({
isMobile: boolean
anchorRef: React.RefObject<HTMLDivElement | null>
}) {
const [params, setParams] = useQueryStates(designSystemSearchParams, {
shallow: false,
history: "push",
})
const [params, setParams] = useDesignSystemSearchParams()
const currentRadius = RADII.find((radius) => radius.name === params.radius)
const defaultRadius = RADII.find((radius) => radius.name === "default")
const otherRadii = RADII.filter((radius) => radius.name !== "default")
return (
<Picker>
<div className="group/picker relative">
<div className="group/picker relative">
<Picker>
<PickerTrigger>
<div className="flex flex-col justify-start text-left">
<div className="text-muted-foreground text-xs">Radius</div>
@@ -41,7 +36,7 @@ export function RadiusPicker({
{currentRadius?.label}
</div>
</div>
<div className="text-foreground absolute top-1/2 right-4 flex size-4 -translate-y-1/2 rotate-90 items-center justify-center text-base">
<div className="text-foreground pointer-events-none absolute top-1/2 right-4 flex size-4 -translate-y-1/2 rotate-90 items-center justify-center text-base select-none">
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
@@ -60,47 +55,47 @@ export function RadiusPicker({
</svg>
</div>
</PickerTrigger>
<LockButton
param="radius"
className="absolute top-1/2 right-10 -translate-y-1/2"
/>
</div>
<PickerContent
anchor={isMobile ? anchorRef : undefined}
side={isMobile ? "top" : "right"}
align={isMobile ? "center" : "start"}
>
<PickerRadioGroup
value={currentRadius?.name}
onValueChange={(value) => {
setParams({ radius: value as RadiusValue })
}}
<PickerContent
anchor={isMobile ? anchorRef : undefined}
side={isMobile ? "top" : "right"}
align={isMobile ? "center" : "start"}
>
<PickerGroup>
{defaultRadius && (
<PickerRadioItem
key={defaultRadius.name}
value={defaultRadius.name}
>
<div className="flex flex-col justify-start pointer-coarse:gap-1">
<div>{defaultRadius.label}</div>
<div className="text-muted-foreground text-xs pointer-coarse:text-sm">
Use radius from style
<PickerRadioGroup
value={currentRadius?.name}
onValueChange={(value) => {
setParams({ radius: value as RadiusValue })
}}
>
<PickerGroup>
{defaultRadius && (
<PickerRadioItem
key={defaultRadius.name}
value={defaultRadius.name}
>
<div className="flex flex-col justify-start pointer-coarse:gap-1">
<div>{defaultRadius.label}</div>
<div className="text-muted-foreground text-xs pointer-coarse:text-sm">
Use radius from style
</div>
</div>
</div>
</PickerRadioItem>
)}
</PickerGroup>
<PickerSeparator />
<PickerGroup>
{otherRadii.map((radius) => (
<PickerRadioItem key={radius.name} value={radius.name}>
{radius.label}
</PickerRadioItem>
))}
</PickerGroup>
</PickerRadioGroup>
</PickerContent>
</Picker>
</PickerRadioItem>
)}
</PickerGroup>
<PickerSeparator />
<PickerGroup>
{otherRadii.map((radius) => (
<PickerRadioItem key={radius.name} value={radius.name}>
{radius.label}
</PickerRadioItem>
))}
</PickerGroup>
</PickerRadioGroup>
</PickerContent>
</Picker>
<LockButton
param="radius"
className="absolute top-1/2 right-10 -translate-y-1/2"
/>
</div>
)
}

View File

@@ -3,7 +3,6 @@
import * as React from "react"
import { Share03Icon, Tick02Icon } from "@hugeicons/core-free-icons"
import { HugeiconsIcon } from "@hugeicons/react"
import { useQueryStates } from "nuqs"
import { copyToClipboardWithMeta } from "@/components/copy-button"
import { Button } from "@/registry/new-york-v4/ui/button"
@@ -12,12 +11,10 @@ import {
TooltipContent,
TooltipTrigger,
} from "@/registry/new-york-v4/ui/tooltip"
import { designSystemSearchParams } from "@/app/(create)/lib/search-params"
import { useDesignSystemSearchParams } from "@/app/(create)/lib/search-params"
export function ShareButton() {
const [params] = useQueryStates(designSystemSearchParams, {
shallow: false,
})
const [params] = useDesignSystemSearchParams()
const [hasCopied, setHasCopied] = React.useState(false)
const shareUrl = React.useMemo(() => {

View File

@@ -1,7 +1,6 @@
"use client"
import * as React from "react"
import { useQueryStates } from "nuqs"
import { type Style, type StyleName } from "@/registry/config"
import { LockButton } from "@/app/(create)/components/lock-button"
@@ -14,7 +13,7 @@ import {
PickerSeparator,
PickerTrigger,
} from "@/app/(create)/components/picker"
import { designSystemSearchParams } from "@/app/(create)/lib/search-params"
import { useDesignSystemSearchParams } from "@/app/(create)/lib/search-params"
export function StylePicker({
styles,
@@ -25,16 +24,13 @@ export function StylePicker({
isMobile: boolean
anchorRef: React.RefObject<HTMLDivElement | null>
}) {
const [params, setParams] = useQueryStates(designSystemSearchParams, {
shallow: false,
history: "push",
})
const [params, setParams] = useDesignSystemSearchParams()
const currentStyle = styles.find((style) => style.name === params.style)
return (
<Picker>
<div className="group/picker relative">
<div className="group/picker relative">
<Picker>
<PickerTrigger>
<div className="flex flex-col justify-start text-left">
<div className="text-muted-foreground text-xs">Style</div>
@@ -43,58 +39,58 @@ export function StylePicker({
</div>
</div>
{currentStyle?.icon && (
<div className="absolute top-1/2 right-4 flex size-4 -translate-y-1/2 items-center justify-center">
<div className="pointer-events-none absolute top-1/2 right-4 flex size-4 -translate-y-1/2 items-center justify-center select-none">
{React.cloneElement(currentStyle.icon, {
className: "size-4",
})}
</div>
)}
</PickerTrigger>
<LockButton
param="style"
className="absolute top-1/2 right-10 -translate-y-1/2"
/>
</div>
<PickerContent
anchor={isMobile ? anchorRef : undefined}
side={isMobile ? "top" : "right"}
align={isMobile ? "center" : "start"}
className="md:w-64"
>
<PickerRadioGroup
value={currentStyle?.name}
onValueChange={(value) => {
setParams({ style: value as StyleName })
}}
<PickerContent
anchor={isMobile ? anchorRef : undefined}
side={isMobile ? "top" : "right"}
align={isMobile ? "center" : "start"}
className="md:w-64"
>
<PickerGroup>
{styles.map((style, index) => (
<React.Fragment key={style.name}>
<PickerRadioItem value={style.name}>
<div className="flex items-start gap-2">
{style.icon && (
<div className="flex size-4 translate-y-0.5 items-center justify-center">
{React.cloneElement(style.icon, {
className: "size-4",
})}
</div>
)}
<div className="flex flex-col justify-start pointer-coarse:gap-1">
<div>{style.title}</div>
<div className="text-muted-foreground text-xs pointer-coarse:text-sm">
{style.description}
<PickerRadioGroup
value={currentStyle?.name}
onValueChange={(value) => {
setParams({ style: value as StyleName })
}}
>
<PickerGroup>
{styles.map((style, index) => (
<React.Fragment key={style.name}>
<PickerRadioItem value={style.name}>
<div className="flex items-start gap-2">
{style.icon && (
<div className="flex size-4 translate-y-0.5 items-center justify-center">
{React.cloneElement(style.icon, {
className: "size-4",
})}
</div>
)}
<div className="flex flex-col justify-start pointer-coarse:gap-1">
<div>{style.title}</div>
<div className="text-muted-foreground text-xs pointer-coarse:text-sm">
{style.description}
</div>
</div>
</div>
</div>
</PickerRadioItem>
{index < styles.length - 1 && (
<PickerSeparator className="opacity-50" />
)}
</React.Fragment>
))}
</PickerGroup>
</PickerRadioGroup>
</PickerContent>
</Picker>
</PickerRadioItem>
{index < styles.length - 1 && (
<PickerSeparator className="opacity-50" />
)}
</React.Fragment>
))}
</PickerGroup>
</PickerRadioGroup>
</PickerContent>
</Picker>
<LockButton
param="style"
className="absolute top-1/2 right-10 -translate-y-1/2"
/>
</div>
)
}

View File

@@ -1,7 +1,5 @@
"use client"
import { useQueryStates } from "nuqs"
import {
Picker,
PickerContent,
@@ -10,7 +8,7 @@ import {
PickerRadioItem,
PickerTrigger,
} from "@/app/(create)/components/picker"
import { designSystemSearchParams } from "@/app/(create)/lib/search-params"
import { useDesignSystemSearchParams } from "@/app/(create)/lib/search-params"
const TEMPLATES = [
{
@@ -37,10 +35,7 @@ export function TemplatePicker({
isMobile: boolean
anchorRef: React.RefObject<HTMLDivElement | null>
}) {
const [params, setParams] = useQueryStates(designSystemSearchParams, {
shallow: false,
history: "push",
})
const [params, setParams] = useDesignSystemSearchParams()
const currentTemplate = TEMPLATES.find(
(template) => template.value === params.template
@@ -57,7 +52,7 @@ export function TemplatePicker({
</div>
{currentTemplate?.logo && (
<div
className="text-foreground *:[svg]:text-foreground! absolute top-1/2 right-4 size-4 -translate-y-1/2 *:[svg]:size-4"
className="text-foreground *:[svg]:text-foreground! pointer-events-none absolute top-1/2 right-4 size-4 -translate-y-1/2 select-none *:[svg]:size-4"
dangerouslySetInnerHTML={{
__html: currentTemplate.logo,
}}

View File

@@ -2,7 +2,6 @@
import * as React from "react"
import { useTheme } from "next-themes"
import { useQueryStates } from "nuqs"
import { useMounted } from "@/hooks/use-mounted"
import { BASE_COLORS, type Theme, type ThemeName } from "@/registry/config"
@@ -11,13 +10,12 @@ import {
Picker,
PickerContent,
PickerGroup,
PickerLabel,
PickerRadioGroup,
PickerRadioItem,
PickerSeparator,
PickerTrigger,
} from "@/app/(create)/components/picker"
import { designSystemSearchParams } from "@/app/(create)/lib/search-params"
import { useDesignSystemSearchParams } from "@/app/(create)/lib/search-params"
export function ThemePicker({
themes,
@@ -30,10 +28,7 @@ export function ThemePicker({
}) {
const { resolvedTheme } = useTheme()
const mounted = useMounted()
const [params, setParams] = useQueryStates(designSystemSearchParams, {
shallow: false,
history: "push",
})
const [params, setParams] = useDesignSystemSearchParams()
const currentTheme = React.useMemo(
() => themes.find((theme) => theme.name === params.theme),
@@ -52,8 +47,8 @@ export function ThemePicker({
}, [currentTheme, themes, setParams])
return (
<Picker>
<div className="group/picker relative">
<div className="group/picker relative">
<Picker>
<PickerTrigger>
<div className="flex flex-col justify-start text-left">
<div className="text-muted-foreground text-xs">Theme</div>
@@ -73,99 +68,99 @@ export function ThemePicker({
],
} as React.CSSProperties
}
className="absolute top-1/2 right-4 size-4 -translate-y-1/2 rounded-full bg-(--color)"
className="pointer-events-none absolute top-1/2 right-4 size-4 -translate-y-1/2 rounded-full bg-(--color) select-none"
/>
)}
</PickerTrigger>
<LockButton
param="theme"
className="absolute top-1/2 right-10 -translate-y-1/2"
/>
</div>
<PickerContent
anchor={isMobile ? anchorRef : undefined}
side={isMobile ? "top" : "right"}
align={isMobile ? "center" : "start"}
className="max-h-96"
>
<PickerRadioGroup
value={currentTheme?.name}
onValueChange={(value) => {
setParams({ theme: value as ThemeName })
}}
<PickerContent
anchor={isMobile ? anchorRef : undefined}
side={isMobile ? "top" : "right"}
align={isMobile ? "center" : "start"}
className="max-h-96"
>
<PickerGroup>
{themes
.filter((theme) =>
BASE_COLORS.find((baseColor) => baseColor.name === theme.name)
)
.map((theme) => {
const isBaseColor = BASE_COLORS.find(
(baseColor) => baseColor.name === theme.name
<PickerRadioGroup
value={currentTheme?.name}
onValueChange={(value) => {
setParams({ theme: value as ThemeName })
}}
>
<PickerGroup>
{themes
.filter((theme) =>
BASE_COLORS.find((baseColor) => baseColor.name === theme.name)
)
return (
<PickerRadioItem key={theme.name} value={theme.name}>
<div className="flex items-start gap-2">
{mounted && resolvedTheme && (
<div
style={
{
"--color":
theme.cssVars?.[
resolvedTheme as "light" | "dark"
]?.[
isBaseColor ? "muted-foreground" : "primary"
],
} as React.CSSProperties
}
className="size-4 translate-y-1 rounded-full bg-(--color)"
/>
)}
<div className="flex flex-col justify-start pointer-coarse:gap-1">
<div>{theme.title}</div>
<div className="text-muted-foreground text-xs pointer-coarse:text-sm">
Match base color
</div>
</div>
</div>
</PickerRadioItem>
)
})}
</PickerGroup>
<PickerSeparator />
<PickerGroup>
{themes
.filter(
(theme) =>
!BASE_COLORS.find(
.map((theme) => {
const isBaseColor = BASE_COLORS.find(
(baseColor) => baseColor.name === theme.name
)
)
.map((theme) => {
return (
<PickerRadioItem key={theme.name} value={theme.name}>
<div className="flex items-center gap-2">
{mounted && resolvedTheme && (
<div
style={
{
"--color":
theme.cssVars?.[
resolvedTheme as "light" | "dark"
]?.["primary"],
} as React.CSSProperties
}
className="size-4 rounded-full bg-(--color)"
/>
)}
{theme.title}
</div>
</PickerRadioItem>
return (
<PickerRadioItem key={theme.name} value={theme.name}>
<div className="flex items-start gap-2">
{mounted && resolvedTheme && (
<div
style={
{
"--color":
theme.cssVars?.[
resolvedTheme as "light" | "dark"
]?.[
isBaseColor ? "muted-foreground" : "primary"
],
} as React.CSSProperties
}
className="size-4 translate-y-1 rounded-full bg-(--color)"
/>
)}
<div className="flex flex-col justify-start pointer-coarse:gap-1">
<div>{theme.title}</div>
<div className="text-muted-foreground text-xs pointer-coarse:text-sm">
Match base color
</div>
</div>
</div>
</PickerRadioItem>
)
})}
</PickerGroup>
<PickerSeparator />
<PickerGroup>
{themes
.filter(
(theme) =>
!BASE_COLORS.find(
(baseColor) => baseColor.name === theme.name
)
)
})}
</PickerGroup>
</PickerRadioGroup>
</PickerContent>
</Picker>
.map((theme) => {
return (
<PickerRadioItem key={theme.name} value={theme.name}>
<div className="flex items-center gap-2">
{mounted && resolvedTheme && (
<div
style={
{
"--color":
theme.cssVars?.[
resolvedTheme as "light" | "dark"
]?.["primary"],
} as React.CSSProperties
}
className="size-4 rounded-full bg-(--color)"
/>
)}
{theme.title}
</div>
</PickerRadioItem>
)
})}
</PickerGroup>
</PickerRadioGroup>
</PickerContent>
</Picker>
<LockButton
param="theme"
className="absolute top-1/2 right-10 -translate-y-1/2"
/>
</div>
)
}

View File

@@ -7,7 +7,6 @@ import {
Tick02Icon,
} from "@hugeicons/core-free-icons"
import { HugeiconsIcon } from "@hugeicons/react"
import { useQueryStates } from "nuqs"
import { toast } from "sonner"
import { useConfig } from "@/hooks/use-config"
@@ -43,7 +42,7 @@ import {
TooltipContent,
TooltipTrigger,
} from "@/registry/new-york-v4/ui/tooltip"
import { designSystemSearchParams } from "@/app/(create)/lib/search-params"
import { useDesignSystemSearchParams } from "@/app/(create)/lib/search-params"
const TEMPLATES = [
{
@@ -65,10 +64,7 @@ const TEMPLATES = [
export function ToolbarControls() {
const [open, setOpen] = React.useState(false)
const [params, setParams] = useQueryStates(designSystemSearchParams, {
shallow: false,
history: "push",
})
const [params, setParams] = useDesignSystemSearchParams()
const [config, setConfig] = useConfig()
const [hasCopied, setHasCopied] = React.useState(false)

View File

@@ -1,7 +1,5 @@
"use client"
import { useQueryStates } from "nuqs"
import { cn } from "@/lib/utils"
import { useIsMobile } from "@/hooks/use-mobile"
import { useMounted } from "@/hooks/use-mounted"
@@ -13,20 +11,15 @@ import {
TooltipContent,
TooltipTrigger,
} from "@/registry/new-york-v4/ui/tooltip"
import { designSystemSearchParams } from "@/app/(create)/lib/search-params"
import { useDesignSystemSearchParams } from "@/app/(create)/lib/search-params"
export function V0Button({ className }: { className?: string }) {
const [params] = useQueryStates(designSystemSearchParams, {
shallow: false,
history: "push",
})
const [params, setParams] = useDesignSystemSearchParams()
const isMobile = useIsMobile()
const isMounted = useMounted()
const url = `${process.env.NEXT_PUBLIC_APP_URL}/create/v0?base=${params.base}&style=${params.style}&baseColor=${params.baseColor}&theme=${params.theme}&iconLibrary=${params.iconLibrary}&font=${params.font}&menuAccent=${params.menuAccent}&menuColor=${params.menuColor}&radius=${params.radius}&item=${params.item}`
console.log(url)
if (!isMounted) {
return <Skeleton className="h-8 w-24 rounded-lg" />
}

View File

@@ -21,7 +21,7 @@ import { ToolbarControls } from "@/app/(create)/components/toolbar-controls"
import { V0Button } from "@/app/(create)/components/v0-button"
import { WelcomeDialog } from "@/app/(create)/components/welcome-dialog"
import { getItemsForBase } from "@/app/(create)/lib/api"
import { designSystemSearchParamsCache } from "@/app/(create)/lib/search-params"
import { loadDesignSystemSearchParams } from "@/app/(create)/lib/search-params"
export const revalidate = false
export const dynamic = "force-static"
@@ -60,7 +60,7 @@ export default async function CreatePage({
}: {
searchParams: Promise<SearchParams>
}) {
const params = await designSystemSearchParamsCache.parse(searchParams)
const params = await loadDesignSystemSearchParams(searchParams)
const base = BASES.find((b) => b.name === params.base) ?? BASES[0]
const items = await getItemsForBase(base.name)

View File

@@ -1,41 +0,0 @@
"use client"
import * as React from "react"
import {
sendToIframe,
sendToParent,
useParentMessageListener,
} from "@/app/(create)/hooks/use-iframe-sync"
const MESSAGE_TYPE = "canva-zoom"
export type ZoomCommand =
| { type: "ZOOM_IN" }
| { type: "ZOOM_OUT" }
| { type: "ZOOM_SET"; value: number }
| { type: "ZOOM_FIT" }
| { type: "RESET" }
export function sendCanvaZoomCommand(
iframe: HTMLIFrameElement | null,
command: ZoomCommand
) {
sendToIframe(iframe, MESSAGE_TYPE, { command })
}
export function sendCanvaZoomUpdate(zoom: number) {
sendToParent(MESSAGE_TYPE, { zoom })
}
export function useCanvaZoomSync() {
const [zoom, setZoom] = React.useState(1)
useParentMessageListener<{ zoom: number }>(MESSAGE_TYPE, (data) => {
if (typeof data.zoom === "number") {
setZoom(data.zoom)
}
})
return zoom
}

View File

@@ -1,89 +0,0 @@
"use client"
import { useQueryStates } from "nuqs"
import {
createIframeSyncStore,
useIframeSyncAll,
useIframeSyncValue,
} from "@/app/(create)/hooks/use-iframe-sync"
import {
designSystemSearchParams,
type DesignSystemSearchParams,
} from "@/app/(create)/lib/search-params"
const MESSAGE_TYPE = "design-system-params"
const getInitialValues = (): DesignSystemSearchParams => {
if (typeof window === "undefined") {
return {
base: "radix",
iconLibrary: "lucide",
theme: "neutral",
style: "vega",
font: "inter",
item: "cover-example",
baseColor: "neutral",
menuAccent: "subtle",
menuColor: "default",
radius: "default",
size: 100,
custom: false,
template: "next",
}
}
const searchParams = new URLSearchParams(window.location.search)
return {
base: (searchParams.get("base") ||
"radix") as DesignSystemSearchParams["base"],
iconLibrary: (searchParams.get("iconLibrary") ||
"lucide") as DesignSystemSearchParams["iconLibrary"],
theme: (searchParams.get("theme") ||
"neutral") as DesignSystemSearchParams["theme"],
style: (searchParams.get("style") ||
"vega") as DesignSystemSearchParams["style"],
font: (searchParams.get("font") ||
"inter") as DesignSystemSearchParams["font"],
item: searchParams.get("item") || "cover-example",
baseColor: (searchParams.get("baseColor") ||
"neutral") as DesignSystemSearchParams["baseColor"],
menuAccent: (searchParams.get("menuAccent") ||
"subtle") as DesignSystemSearchParams["menuAccent"],
menuColor: (searchParams.get("menuColor") ||
"default") as DesignSystemSearchParams["menuColor"],
radius: (searchParams.get("radius") ||
"default") as DesignSystemSearchParams["radius"],
size: parseInt(searchParams.get("size") || "100"),
custom: (searchParams.get("custom") || "false") === "true",
template: (searchParams.get("template") ||
"next") as DesignSystemSearchParams["template"],
}
}
const designSystemStore = createIframeSyncStore(
MESSAGE_TYPE,
getInitialValues()
)
export function useDesignSystemSync() {
const [urlParams] = useQueryStates(designSystemSearchParams, {
shallow: false,
})
const keys = Object.keys(
designSystemSearchParams
) as (keyof DesignSystemSearchParams)[]
return useIframeSyncAll(designSystemStore, keys, urlParams)
}
export function useDesignSystemParam<K extends keyof DesignSystemSearchParams>(
key: K
) {
const [urlParams] = useQueryStates(designSystemSearchParams, {
shallow: false,
})
return useIframeSyncValue(designSystemStore, key, urlParams[key])
}

View File

@@ -1,8 +1,14 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
"use client"
import * as React from "react"
import type { DesignSystemSearchParams } from "@/app/(create)/lib/search-params"
type ParentToIframeMessage = {
type: "design-system-params"
data: DesignSystemSearchParams
}
export const isInIframe = () => {
if (typeof window === "undefined") {
return false
@@ -10,157 +16,21 @@ export const isInIframe = () => {
return window.self !== window.top
}
export function createIframeSyncStore<T extends Record<string, any>>(
messageType: string,
defaultValues: T
) {
const store = new Map<keyof T, any>()
const listeners = new Map<keyof T, Set<() => void>>()
if (typeof window !== "undefined") {
Object.entries(defaultValues).forEach(([key, value]) => {
store.set(key as keyof T, value)
})
}
if (typeof window !== "undefined" && isInIframe()) {
window.addEventListener("message", (event: MessageEvent) => {
if (event.data.type === messageType && event.data.params) {
Object.keys(event.data.params).forEach((key) => {
const newValue = event.data.params[key]
const oldValue = store.get(key as keyof T)
if (newValue !== oldValue) {
store.set(key as keyof T, newValue)
const keyListeners = listeners.get(key as keyof T)
if (keyListeners) {
keyListeners.forEach((listener) => listener())
}
}
})
}
})
}
return {
store,
listeners,
subscribe(key: keyof T, callback: () => void) {
if (!isInIframe()) return () => {}
if (!listeners.has(key)) {
listeners.set(key, new Set())
}
const keyListeners = listeners.get(key)!
keyListeners.add(callback)
return () => {
keyListeners.delete(callback)
if (keyListeners.size === 0) {
listeners.delete(key)
}
}
},
subscribeAll(keys: (keyof T)[], callback: () => void) {
if (!isInIframe()) return () => {}
keys.forEach((key) => {
if (!listeners.has(key)) {
listeners.set(key, new Set())
}
listeners.get(key)!.add(callback)
})
return () => {
keys.forEach((key) => {
const keyListeners = listeners.get(key)
if (keyListeners) {
keyListeners.delete(callback)
if (keyListeners.size === 0) {
listeners.delete(key)
}
}
})
}
},
get(key: keyof T) {
return store.get(key) as T[keyof T]
},
getAll() {
const result = {} as T
store.forEach((value, key) => {
result[key as keyof T] = value
})
return result
},
}
}
export function useIframeSyncValue<T>(
store: ReturnType<typeof createIframeSyncStore<any>>,
key: string,
urlValue: T
) {
const subscribe = React.useCallback(
(callback: () => void) => {
return store.subscribe(key, callback)
},
[store, key]
)
const getSnapshot = React.useCallback(() => {
if (!isInIframe()) {
return urlValue
}
return store.get(key) as T
}, [store, key, urlValue])
const getServerSnapshot = React.useCallback(() => {
return urlValue
}, [urlValue])
return React.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot)
}
export function useIframeSyncAll<T extends Record<string, any>>(
store: ReturnType<typeof createIframeSyncStore<T>>,
keys: (keyof T)[],
urlValues: T
) {
const subscribe = React.useCallback(
(callback: () => void) => {
return store.subscribeAll(keys, callback)
},
[store, keys]
)
const getSnapshot = React.useCallback(() => {
if (!isInIframe()) {
return urlValues
}
return store.getAll()
}, [store, urlValues])
const getServerSnapshot = React.useCallback(() => {
return urlValues
}, [urlValues])
return React.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot)
}
export function useParentMessageListener<T>(
messageType: string,
onMessage: (data: T) => void
export function useIframeMessageListener<
Message extends ParentToIframeMessage,
MessageType extends Message["type"],
>(
messageType: MessageType,
onMessage: (data: Extract<Message, { type: MessageType }>["data"]) => void
) {
React.useEffect(() => {
if (isInIframe()) {
if (!isInIframe()) {
return
}
const handleMessage = (event: MessageEvent) => {
if (event.data.type === messageType) {
onMessage(event.data)
onMessage(event.data.data)
}
}
@@ -171,33 +41,22 @@ export function useParentMessageListener<T>(
}, [messageType, onMessage])
}
export function sendToIframe(
export function sendToIframe<
Message extends ParentToIframeMessage,
MessageType extends Message["type"],
>(
iframe: HTMLIFrameElement | null,
messageType: string,
data: any
messageType: MessageType,
data: Extract<Message, { type: MessageType }>["data"]
) {
if (!iframe || !iframe.contentWindow) {
if (!iframe?.contentWindow) {
return
}
iframe.contentWindow.postMessage(
{
type: messageType,
...data,
},
"*"
)
}
export function sendToParent(messageType: string, data: any) {
if (!isInIframe()) {
return
}
window.parent.postMessage(
{
type: messageType,
...data,
data,
},
"*"
)

View File

@@ -1,9 +1,13 @@
import { useQueryStates } from "nuqs"
import {
createSearchParamsCache,
createLoader,
createSerializer,
parseAsBoolean,
parseAsInteger,
parseAsString,
parseAsStringLiteral,
type inferParserType,
type Options,
} from "nuqs/server"
import {
@@ -28,11 +32,11 @@ import {
} from "@/registry/config"
import { FONTS } from "@/app/(create)/lib/fonts"
export const designSystemSearchParams = {
const designSystemSearchParams = {
base: parseAsStringLiteral<BaseName>(BASES.map((b) => b.name)).withDefault(
DEFAULT_CONFIG.base
),
item: parseAsString.withDefault("preview"),
item: parseAsString.withDefault("preview").withOptions({ shallow: true }),
iconLibrary: parseAsStringLiteral<IconLibraryName>(
Object.values(iconLibraries).map((i) => i.name)
).withDefault(DEFAULT_CONFIG.iconLibrary),
@@ -57,19 +61,30 @@ export const designSystemSearchParams = {
radius: parseAsStringLiteral<RadiusValue>(
RADII.map((r) => r.name)
).withDefault("default"),
template: parseAsStringLiteral<"next" | "start" | "vite">([
template: parseAsStringLiteral([
"next",
"start",
"vite",
]).withDefault("next"),
] as const).withDefault("next"),
size: parseAsInteger.withDefault(100),
custom: parseAsBoolean.withDefault(false),
}
export const designSystemSearchParamsCache = createSearchParamsCache(
export const loadDesignSystemSearchParams = createLoader(
designSystemSearchParams
)
export type DesignSystemSearchParams = Awaited<
ReturnType<typeof designSystemSearchParamsCache.parse>
export const serializeDesignSystemSearchParams = createSerializer(
designSystemSearchParams
)
export const useDesignSystemSearchParams = (options: Options = {}) =>
useQueryStates(designSystemSearchParams, {
shallow: false,
history: "push",
...options,
})
export type DesignSystemSearchParams = inferParserType<
typeof designSystemSearchParams
>

View File

@@ -117,6 +117,7 @@ function DropdownMenuCheckboxes() {
lucide="UserIcon"
tabler="IconUser"
hugeicons="UserIcon"
phosphor="UserIcon"
/>
Profile
</DropdownMenuItem>
@@ -125,6 +126,7 @@ function DropdownMenuCheckboxes() {
lucide="CreditCardIcon"
tabler="IconCreditCard"
hugeicons="CreditCardIcon"
phosphor="CreditCardIcon"
/>
Billing
</DropdownMenuItem>
@@ -133,6 +135,7 @@ function DropdownMenuCheckboxes() {
lucide="SettingsIcon"
tabler="IconSettings"
hugeicons="SettingsIcon"
phosphor="GearIcon"
/>
Settings
</DropdownMenuItem>
@@ -167,6 +170,7 @@ function DropdownMenuCheckboxes() {
lucide="LogOutIcon"
tabler="IconLogout"
hugeicons="LogoutIcon"
phosphor="SignOutIcon"
/>
Sign Out
</DropdownMenuItem>
@@ -222,6 +226,7 @@ function DropdownMenuWithAvatar() {
lucide="ChevronsUpDownIcon"
tabler="IconChevronsUpDown"
hugeicons="ChevronUpDownIcon"
phosphor="CaretUpDownIcon"
className="text-muted-foreground ml-auto"
/>
</Button>
@@ -251,6 +256,7 @@ function DropdownMenuWithAvatar() {
lucide="SparklesIcon"
tabler="IconSparkles"
hugeicons="SparklesIcon"
phosphor="SparklesIcon"
/>
Upgrade to Pro
</DropdownMenuItem>
@@ -262,6 +268,7 @@ function DropdownMenuWithAvatar() {
lucide="BadgeCheckIcon"
tabler="IconBadgeCheck"
hugeicons="BadgeCheckIcon"
phosphor="CheckCircleIcon"
/>
Account
</DropdownMenuItem>
@@ -270,6 +277,7 @@ function DropdownMenuWithAvatar() {
lucide="CreditCardIcon"
tabler="IconCreditCard"
hugeicons="CreditCardIcon"
phosphor="CreditCardIcon"
/>
Billing
</DropdownMenuItem>
@@ -278,6 +286,7 @@ function DropdownMenuWithAvatar() {
lucide="BellIcon"
tabler="IconBell"
hugeicons="BellIcon"
phosphor="BellIcon"
/>
Notifications
</DropdownMenuItem>
@@ -288,6 +297,7 @@ function DropdownMenuWithAvatar() {
lucide="LogOutIcon"
tabler="IconLogout"
hugeicons="LogoutIcon"
phosphor="SignOutIcon"
/>
Sign Out
</DropdownMenuItem>
@@ -341,6 +351,7 @@ function DropdownMenuAvatarOnly() {
lucide="SparklesIcon"
tabler="IconSparkles"
hugeicons="SparklesIcon"
phosphor="SparklesIcon"
/>
Upgrade to Pro
</DropdownMenuItem>
@@ -352,6 +363,7 @@ function DropdownMenuAvatarOnly() {
lucide="BadgeCheckIcon"
tabler="IconBadgeCheck"
hugeicons="BadgeCheckIcon"
phosphor="CheckCircleIcon"
/>
Account
</DropdownMenuItem>
@@ -360,6 +372,7 @@ function DropdownMenuAvatarOnly() {
lucide="CreditCardIcon"
tabler="IconCreditCard"
hugeicons="CreditCardIcon"
phosphor="CreditCardIcon"
/>
Billing
</DropdownMenuItem>
@@ -368,6 +381,7 @@ function DropdownMenuAvatarOnly() {
lucide="BellIcon"
tabler="IconBell"
hugeicons="BellIcon"
phosphor="BellIcon"
/>
Notifications
</DropdownMenuItem>
@@ -378,6 +392,7 @@ function DropdownMenuAvatarOnly() {
lucide="LogOutIcon"
tabler="IconLogout"
hugeicons="LogoutIcon"
phosphor="SignOutIcon"
/>
Sign Out
</DropdownMenuItem>
@@ -395,6 +410,7 @@ function DropdownMenuIconColor() {
lucide="MoreHorizontalIcon"
tabler="IconDots"
hugeicons="MoreHorizontalCircle01Icon"
phosphor="DotsThreeOutlineIcon"
/>
<span className="sr-only">Toggle menu</span>
</Button>
@@ -406,6 +422,7 @@ function DropdownMenuIconColor() {
lucide="PencilIcon"
tabler="IconPencil"
hugeicons="EditIcon"
phosphor="PencilIcon"
/>
Edit
</DropdownMenuItem>
@@ -414,6 +431,7 @@ function DropdownMenuIconColor() {
lucide="ShareIcon"
tabler="IconShare"
hugeicons="ShareIcon"
phosphor="ShareIcon"
/>
Share
</DropdownMenuItem>
@@ -423,6 +441,7 @@ function DropdownMenuIconColor() {
lucide="TrashIcon"
tabler="IconTrash"
hugeicons="DeleteIcon"
phosphor="TrashIcon"
/>
Delete
</DropdownMenuItem>

View File

@@ -91,9 +91,11 @@ export default function RootLayout({
<ThemeProvider>
<LayoutProvider>
<ActiveThemeProvider>
<NuqsAdapter>{children}</NuqsAdapter>
<NuqsAdapter>
{children}
<Toaster position="top-center" />
</NuqsAdapter>
<TailwindIndicator />
<Toaster position="top-center" />
<Analytics />
</ActiveThemeProvider>
</LayoutProvider>

View File

@@ -40,7 +40,7 @@ export function ColorFormatSelector({
<span className="font-medium">Format: </span>
<span className="text-muted-foreground font-mono">{format}</span>
</SelectTrigger>
<SelectContent align="end" className="rounded-xl">
<SelectContent align="end" position="popper" className="rounded-xl">
{Object.entries(formats).map(([format, value]) => (
<SelectItem
key={format}

View File

@@ -87,6 +87,70 @@ const menuItems = {
Open in Claude
</a>
),
scira: (url: string) => (
<a
href={getPromptUrl("https://scira.ai/", url)}
target="_blank"
className="m-0 p-0"
>
<svg
width="910"
height="934"
viewBox="0 0 910 934"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M647.664 197.775C569.13 189.049 525.5 145.419 516.774 66.8849C508.048 145.419 464.418 189.049 385.884 197.775C464.418 206.501 508.048 250.131 516.774 328.665C525.5 250.131 569.13 206.501 647.664 197.775Z"
fill="currentColor"
stroke="currentColor"
strokeWidth="8"
strokeLinejoin="round"
/>
<path
d="M516.774 304.217C510.299 275.491 498.208 252.087 480.335 234.214C462.462 216.341 439.058 204.251 410.333 197.775C439.059 191.3 462.462 179.209 480.335 161.336C498.208 143.463 510.299 120.06 516.774 91.334C523.25 120.059 535.34 143.463 553.213 161.336C571.086 179.209 594.49 191.3 623.216 197.775C594.49 204.251 571.086 216.341 553.213 234.214C535.34 252.087 523.25 275.491 516.774 304.217Z"
fill="currentColor"
stroke="currentColor"
strokeWidth="8"
strokeLinejoin="round"
/>
<path
d="M857.5 508.116C763.259 497.644 710.903 445.288 700.432 351.047C689.961 445.288 637.605 497.644 543.364 508.116C637.605 518.587 689.961 570.943 700.432 665.184C710.903 570.943 763.259 518.587 857.5 508.116Z"
stroke="currentColor"
strokeWidth="20"
strokeLinejoin="round"
/>
<path
d="M700.432 615.957C691.848 589.05 678.575 566.357 660.383 548.165C642.191 529.973 619.499 516.7 592.593 508.116C619.499 499.533 642.191 486.258 660.383 468.066C678.575 449.874 691.848 427.181 700.432 400.274C709.015 427.181 722.289 449.874 740.481 468.066C758.673 486.258 781.365 499.533 808.271 508.116C781.365 516.7 758.673 529.973 740.481 548.165C722.289 566.357 709.015 589.05 700.432 615.957Z"
stroke="currentColor"
strokeWidth="20"
strokeLinejoin="round"
/>
<path
d="M889.949 121.237C831.049 114.692 798.326 81.9698 791.782 23.0692C785.237 81.9698 752.515 114.692 693.614 121.237C752.515 127.781 785.237 160.504 791.782 219.404C798.326 160.504 831.049 127.781 889.949 121.237Z"
fill="currentColor"
stroke="currentColor"
strokeWidth="8"
strokeLinejoin="round"
/>
<path
d="M791.782 196.795C786.697 176.937 777.869 160.567 765.16 147.858C752.452 135.15 736.082 126.322 716.226 121.237C736.082 116.152 752.452 107.324 765.16 94.6152C777.869 81.9065 786.697 65.5368 791.782 45.6797C796.867 65.5367 805.695 81.9066 818.403 94.6152C831.112 107.324 847.481 116.152 867.338 121.237C847.481 126.322 831.112 135.15 818.403 147.858C805.694 160.567 796.867 176.937 791.782 196.795Z"
fill="currentColor"
stroke="currentColor"
strokeWidth="8"
strokeLinejoin="round"
/>
<path
d="M760.632 764.337C720.719 814.616 669.835 855.1 611.872 882.692C553.91 910.285 490.404 924.255 426.213 923.533C362.022 922.812 298.846 907.419 241.518 878.531C184.19 849.643 134.228 808.026 95.4548 756.863C56.6815 705.7 30.1238 646.346 17.8129 583.343C5.50207 520.339 7.76433 455.354 24.4266 393.359C41.089 331.364 71.7099 274.001 113.947 225.658C156.184 177.315 208.919 139.273 268.117 114.442"
stroke="currentColor"
strokeWidth="30"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
Open in Scira
</a>
),
}
export function DocsCopyPage({ page, url }: { page: string; url: string }) {

View File

@@ -30,6 +30,7 @@
"@hookform/resolvers": "^3.10.0",
"@hugeicons/core-free-icons": "^1.2.1",
"@hugeicons/react": "^1.1.1",
"@phosphor-icons/react": "^2.1.10",
"@radix-ui/react-accessible-icon": "^1.1.1",
"@radix-ui/react-accordion": "^1.2.12",
"@radix-ui/react-alert-dialog": "^1.1.5",
@@ -100,7 +101,7 @@
"rehype-pretty-code": "^0.14.1",
"rimraf": "^6.0.1",
"server-only": "^0.0.1",
"shadcn": "3.6.1",
"shadcn": "3.6.2",
"shiki": "^1.10.1",
"sonner": "^2.0.0",
"swr": "^2.3.6",

View File

@@ -1,5 +1,6 @@
{
"@8bitcn": "https://8bitcn.com/r/{name}.json",
"@8starlabs-ui": "https://ui.8starlabs.com/r/{name}.json",
"@97cn": "https://97cn.itzik.co/r/{name}.json",
"@abstract": "https://build.abs.xyz/r/{name}/json",
"@abui": "https://abui.io/r/{name}.json",
@@ -19,6 +20,8 @@
"@billingsdk": "https://billingsdk.com/r/{name}.json",
"@blocks": "https://blocks.so/r/{name}.json",
"@bucharitesh": "https://bucharitesh.in/r/{name}.json",
"@bundui": "https://bundui.io/r/{name}.json",
"@cardcn": "https://cardcn.dev/r/{name}.json",
"@clerk": "https://clerk.com/r/{name}.json",
"@coss": "https://coss.com/ui/r/{name}.json",
"@commercn": "https://commercn.com/r/{name}.json",
@@ -28,6 +31,7 @@
"@diceui": "https://diceui.com/r/{name}.json",
"@doras-ui": "https://ui.doras.to/r/{name}.json",
"@efferd": "https://efferd.com/r/{name}.json",
"@einui": "https://ui.eindev.ir/r/{name}.json",
"@eldoraui": "https://eldoraui.site/r/{name}.json",
"@elements": "https://tryelements.dev/r/{name}.json",
"@elevenlabs-ui": "https://ui.elevenlabs.io/r/{name}.json",
@@ -45,20 +49,24 @@
"@limeplay": "https://limeplay.winoffrg.dev/r/{name}.json",
"@lucide-animated": "https://lucide-animated.com/r/{name}.json",
"@lytenyte": "https://www.1771technologies.com/r/{name}.json",
"@marmelab": "https://marmelab.com/shadcn-admin-kit/r/{name}.json",
"@magicui": "https://magicui.design/r/{name}.json",
"@magicui-pro": "https://pro.magicui.design/registry/{name}",
"@motion-primitives": "https://motion-primitives.com/c/{name}.json",
"@manifest": "https://ui.manifest.build/r/{name}.json",
"@mui-treasury": "https://mui-treasury.com/r/{name}.json",
"@nativeui": "https://nativeui.io/registry/{name}.json",
"@nexus-elements": "https://elements.nexus.availproject.org/r/{name}.json",
"@ncdai": "https://chanhdai.com/r/{name}.json",
"@nuqs": "https://nuqs.dev/r/{name}.json",
"@optics": "https://optics.agusmayol.com.ar/r/{name}.json",
"@oui": "https://oui.mw10013.workers.dev/r/{name}.json",
"@paceui": "https://ui.paceui.com/r/{name}.json",
"@plate": "https://platejs.org/r/{name}.json",
"@prompt-kit": "https://prompt-kit.com/c/{name}.json",
"@prosekit": "https://prosekit.dev/r/{name}.json",
"@phucbm": "https://ui.phucbm.com/r/{name}.json",
"@react-aria": "https://react-aria.adobe.com/registry/{name}.json",
"@react-bits": "https://reactbits.dev/r/{name}.json",
"@react-market": "https://www.react-market.com/get/{name}.json",
"@retroui": "https://retroui.dev/r/{name}.json",
@@ -86,6 +94,7 @@
"@tailwind-builder": "https://tailwindbuilder.ai/r/{name}.json",
"@tweakcn": "https://tweakcn.com/r/themes/{name}.json",
"@wds": "https://wds-shadcn-registry.netlify.app/r/{name}.json",
"@animbits": "https://animbits.dev/r/{name}.json",
"@wandry-ui": "https://ui.wandry.com.ua/r/{name}.json",
"@wigggle-ui": "https://wigggle-ui.vercel.app/r/{name}.json",
"@paykit-sdk": "https://www.usepaykit.dev/r/{name}.json",
@@ -102,7 +111,7 @@
"@uicapsule": "https://uicapsule.com/r/{name}.json",
"@uitripled": "https://ui.tripled.work/r/{name}.json",
"@ui-layouts": "https://ui-layouts.com/r/{name}.json",
"@pureui": "https://pure.kam-ui.com/r/{name}.json",
"@tour": "https://onboarding-tour.vercel.app/r/{name}.json",
"@tb-blocks": "https://tailwindbuilder.ai/r/blocks/{name}.json",
"@ui-layouts": "https://ui-layouts.com/r/{name}.json"
"@tb-blocks": "https://tailwindbuilder.ai/r/blocks/{name}.json"
}

File diff suppressed because one or more lines are too long

View File

@@ -4,7 +4,7 @@
"files": [
{
"path": "registry/base-lyra/ui/accordion.tsx",
"content": "\"use client\"\n\nimport { Accordion as AccordionPrimitive } from \"@base-ui/react/accordion\"\n\nimport { cn } from \"@/registry/bases/base/lib/utils\"\nimport { IconPlaceholder } from \"@/app/(create)/components/icon-placeholder\"\n\nfunction Accordion({ className, ...props }: AccordionPrimitive.Root.Props) {\n return (\n <AccordionPrimitive.Root\n data-slot=\"accordion\"\n className={cn(\"flex w-full flex-col\", className)}\n {...props}\n />\n )\n}\n\nfunction AccordionItem({ className, ...props }: AccordionPrimitive.Item.Props) {\n return (\n <AccordionPrimitive.Item\n data-slot=\"accordion-item\"\n className={cn(\"not-last:border-b\", className)}\n {...props}\n />\n )\n}\n\nfunction AccordionTrigger({\n className,\n children,\n ...props\n}: AccordionPrimitive.Trigger.Props) {\n return (\n <AccordionPrimitive.Header className=\"flex\">\n <AccordionPrimitive.Trigger\n data-slot=\"accordion-trigger\"\n className={cn(\n \"focus-visible:ring-ring/50 focus-visible:border-ring focus-visible:after:border-ring **:data-[slot=accordion-trigger-icon]:text-muted-foreground rounded-none py-2.5 text-left text-xs font-medium hover:underline focus-visible:ring-1 **:data-[slot=accordion-trigger-icon]:ml-auto **:data-[slot=accordion-trigger-icon]:size-4 group/accordion-trigger relative flex flex-1 items-start justify-between border border-transparent transition-all outline-none disabled:pointer-events-none disabled:opacity-50\",\n className\n )}\n {...props}\n >\n {children}\n <IconPlaceholder\n lucide=\"ChevronDownIcon\"\n tabler=\"IconChevronDown\"\n data-slot=\"accordion-trigger-icon\"\n hugeicons=\"ArrowDown01Icon\"\n className=\"pointer-events-none shrink-0 group-aria-expanded/accordion-trigger:hidden\"\n />\n <IconPlaceholder\n lucide=\"ChevronUpIcon\"\n tabler=\"IconChevronUp\"\n data-slot=\"accordion-trigger-icon\"\n hugeicons=\"ArrowUp01Icon\"\n className=\"pointer-events-none hidden shrink-0 group-aria-expanded/accordion-trigger:inline\"\n />\n </AccordionPrimitive.Trigger>\n </AccordionPrimitive.Header>\n )\n}\n\nfunction AccordionContent({\n className,\n children,\n ...props\n}: AccordionPrimitive.Panel.Props) {\n return (\n <AccordionPrimitive.Panel\n data-slot=\"accordion-content\"\n className=\"data-open:animate-accordion-down data-closed:animate-accordion-up text-xs overflow-hidden\"\n {...props}\n >\n <div\n className={cn(\n \"pt-0 pb-2.5 [&_a]:hover:text-foreground h-(--accordion-panel-height) data-ending-style:h-0 data-starting-style:h-0 [&_a]:underline [&_a]:underline-offset-3 [&_p:not(:last-child)]:mb-4\",\n className\n )}\n >\n {children}\n </div>\n </AccordionPrimitive.Panel>\n )\n}\n\nexport { Accordion, AccordionItem, AccordionTrigger, AccordionContent }\n",
"content": "\"use client\"\n\nimport { Accordion as AccordionPrimitive } from \"@base-ui/react/accordion\"\n\nimport { cn } from \"@/registry/bases/base/lib/utils\"\nimport { IconPlaceholder } from \"@/app/(create)/components/icon-placeholder\"\n\nfunction Accordion({ className, ...props }: AccordionPrimitive.Root.Props) {\n return (\n <AccordionPrimitive.Root\n data-slot=\"accordion\"\n className={cn(\"flex w-full flex-col\", className)}\n {...props}\n />\n )\n}\n\nfunction AccordionItem({ className, ...props }: AccordionPrimitive.Item.Props) {\n return (\n <AccordionPrimitive.Item\n data-slot=\"accordion-item\"\n className={cn(\"not-last:border-b\", className)}\n {...props}\n />\n )\n}\n\nfunction AccordionTrigger({\n className,\n children,\n ...props\n}: AccordionPrimitive.Trigger.Props) {\n return (\n <AccordionPrimitive.Header className=\"flex\">\n <AccordionPrimitive.Trigger\n data-slot=\"accordion-trigger\"\n className={cn(\n \"focus-visible:ring-ring/50 focus-visible:border-ring focus-visible:after:border-ring **:data-[slot=accordion-trigger-icon]:text-muted-foreground rounded-none py-2.5 text-left text-xs font-medium hover:underline focus-visible:ring-1 **:data-[slot=accordion-trigger-icon]:ml-auto **:data-[slot=accordion-trigger-icon]:size-4 group/accordion-trigger relative flex flex-1 items-start justify-between border border-transparent transition-all outline-none disabled:pointer-events-none disabled:opacity-50\",\n className\n )}\n {...props}\n >\n {children}\n <IconPlaceholder\n lucide=\"ChevronDownIcon\"\n tabler=\"IconChevronDown\"\n data-slot=\"accordion-trigger-icon\"\n hugeicons=\"ArrowDown01Icon\"\n phosphor=\"CaretDownIcon\"\n className=\"pointer-events-none shrink-0 group-aria-expanded/accordion-trigger:hidden\"\n />\n <IconPlaceholder\n lucide=\"ChevronUpIcon\"\n tabler=\"IconChevronUp\"\n data-slot=\"accordion-trigger-icon\"\n hugeicons=\"ArrowUp01Icon\"\n phosphor=\"CaretUpIcon\"\n className=\"pointer-events-none hidden shrink-0 group-aria-expanded/accordion-trigger:inline\"\n />\n </AccordionPrimitive.Trigger>\n </AccordionPrimitive.Header>\n )\n}\n\nfunction AccordionContent({\n className,\n children,\n ...props\n}: AccordionPrimitive.Panel.Props) {\n return (\n <AccordionPrimitive.Panel\n data-slot=\"accordion-content\"\n className=\"data-open:animate-accordion-down data-closed:animate-accordion-up text-xs overflow-hidden\"\n {...props}\n >\n <div\n className={cn(\n \"pt-0 pb-2.5 [&_a]:hover:text-foreground h-(--accordion-panel-height) data-ending-style:h-0 data-starting-style:h-0 [&_a]:underline [&_a]:underline-offset-3 [&_p:not(:last-child)]:mb-4\",\n className\n )}\n >\n {children}\n </div>\n </AccordionPrimitive.Panel>\n )\n}\n\nexport { Accordion, AccordionItem, AccordionTrigger, AccordionContent }\n",
"type": "registry:ui"
}
],

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -4,7 +4,7 @@
"files": [
{
"path": "registry/base-lyra/ui/breadcrumb.tsx",
"content": "import * as React from \"react\"\nimport { mergeProps } from \"@base-ui/react/merge-props\"\nimport { useRender } from \"@base-ui/react/use-render\"\n\nimport { cn } from \"@/registry/bases/base/lib/utils\"\nimport { IconPlaceholder } from \"@/app/(create)/components/icon-placeholder\"\n\nfunction Breadcrumb({ className, ...props }: React.ComponentProps<\"nav\">) {\n return (\n <nav\n aria-label=\"breadcrumb\"\n data-slot=\"breadcrumb\"\n className={cn(className)}\n {...props}\n />\n )\n}\n\nfunction BreadcrumbList({ className, ...props }: React.ComponentProps<\"ol\">) {\n return (\n <ol\n data-slot=\"breadcrumb-list\"\n className={cn(\n \"text-muted-foreground gap-1.5 text-xs flex flex-wrap items-center break-words\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction BreadcrumbItem({ className, ...props }: React.ComponentProps<\"li\">) {\n return (\n <li\n data-slot=\"breadcrumb-item\"\n className={cn(\"gap-1 inline-flex items-center\", className)}\n {...props}\n />\n )\n}\n\nfunction BreadcrumbLink({\n className,\n render,\n ...props\n}: useRender.ComponentProps<\"a\">) {\n return useRender({\n defaultTagName: \"a\",\n props: mergeProps<\"a\">(\n {\n className: cn(\"hover:text-foreground transition-colors\", className),\n },\n props\n ),\n render,\n state: {\n slot: \"breadcrumb-link\",\n },\n })\n}\n\nfunction BreadcrumbPage({ className, ...props }: React.ComponentProps<\"span\">) {\n return (\n <span\n data-slot=\"breadcrumb-page\"\n role=\"link\"\n aria-disabled=\"true\"\n aria-current=\"page\"\n className={cn(\"text-foreground font-normal\", className)}\n {...props}\n />\n )\n}\n\nfunction BreadcrumbSeparator({\n children,\n className,\n ...props\n}: React.ComponentProps<\"li\">) {\n return (\n <li\n data-slot=\"breadcrumb-separator\"\n role=\"presentation\"\n aria-hidden=\"true\"\n className={cn(\"[&>svg]:size-3.5\", className)}\n {...props}\n >\n {children ?? (\n <IconPlaceholder\n lucide=\"ChevronRightIcon\"\n tabler=\"IconChevronRight\"\n hugeicons=\"ArrowRight01Icon\"\n />\n )}\n </li>\n )\n}\n\nfunction BreadcrumbEllipsis({\n className,\n ...props\n}: React.ComponentProps<\"span\">) {\n return (\n <span\n data-slot=\"breadcrumb-ellipsis\"\n role=\"presentation\"\n aria-hidden=\"true\"\n className={cn(\n \"size-5 [&>svg]:size-4 flex items-center justify-center\",\n className\n )}\n {...props}\n >\n <IconPlaceholder\n lucide=\"MoreHorizontalIcon\"\n tabler=\"IconDots\"\n hugeicons=\"MoreHorizontalCircle01Icon\"\n />\n <span className=\"sr-only\">More</span>\n </span>\n )\n}\n\nexport {\n Breadcrumb,\n BreadcrumbList,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbPage,\n BreadcrumbSeparator,\n BreadcrumbEllipsis,\n}\n",
"content": "import * as React from \"react\"\nimport { mergeProps } from \"@base-ui/react/merge-props\"\nimport { useRender } from \"@base-ui/react/use-render\"\n\nimport { cn } from \"@/registry/bases/base/lib/utils\"\nimport { IconPlaceholder } from \"@/app/(create)/components/icon-placeholder\"\n\nfunction Breadcrumb({ className, ...props }: React.ComponentProps<\"nav\">) {\n return (\n <nav\n aria-label=\"breadcrumb\"\n data-slot=\"breadcrumb\"\n className={cn(className)}\n {...props}\n />\n )\n}\n\nfunction BreadcrumbList({ className, ...props }: React.ComponentProps<\"ol\">) {\n return (\n <ol\n data-slot=\"breadcrumb-list\"\n className={cn(\n \"text-muted-foreground gap-1.5 text-xs flex flex-wrap items-center break-words\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction BreadcrumbItem({ className, ...props }: React.ComponentProps<\"li\">) {\n return (\n <li\n data-slot=\"breadcrumb-item\"\n className={cn(\"gap-1 inline-flex items-center\", className)}\n {...props}\n />\n )\n}\n\nfunction BreadcrumbLink({\n className,\n render,\n ...props\n}: useRender.ComponentProps<\"a\">) {\n return useRender({\n defaultTagName: \"a\",\n props: mergeProps<\"a\">(\n {\n className: cn(\"hover:text-foreground transition-colors\", className),\n },\n props\n ),\n render,\n state: {\n slot: \"breadcrumb-link\",\n },\n })\n}\n\nfunction BreadcrumbPage({ className, ...props }: React.ComponentProps<\"span\">) {\n return (\n <span\n data-slot=\"breadcrumb-page\"\n role=\"link\"\n aria-disabled=\"true\"\n aria-current=\"page\"\n className={cn(\"text-foreground font-normal\", className)}\n {...props}\n />\n )\n}\n\nfunction BreadcrumbSeparator({\n children,\n className,\n ...props\n}: React.ComponentProps<\"li\">) {\n return (\n <li\n data-slot=\"breadcrumb-separator\"\n role=\"presentation\"\n aria-hidden=\"true\"\n className={cn(\"[&>svg]:size-3.5\", className)}\n {...props}\n >\n {children ?? (\n <IconPlaceholder\n lucide=\"ChevronRightIcon\"\n tabler=\"IconChevronRight\"\n hugeicons=\"ArrowRight01Icon\"\n phosphor=\"CaretRightIcon\"\n />\n )}\n </li>\n )\n}\n\nfunction BreadcrumbEllipsis({\n className,\n ...props\n}: React.ComponentProps<\"span\">) {\n return (\n <span\n data-slot=\"breadcrumb-ellipsis\"\n role=\"presentation\"\n aria-hidden=\"true\"\n className={cn(\n \"size-5 [&>svg]:size-4 flex items-center justify-center\",\n className\n )}\n {...props}\n >\n <IconPlaceholder\n lucide=\"MoreHorizontalIcon\"\n tabler=\"IconDots\"\n hugeicons=\"MoreHorizontalCircle01Icon\"\n phosphor=\"DotsThreeIcon\"\n />\n <span className=\"sr-only\">More</span>\n </span>\n )\n}\n\nexport {\n Breadcrumb,\n BreadcrumbList,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbPage,\n BreadcrumbSeparator,\n BreadcrumbEllipsis,\n}\n",
"type": "registry:ui"
}
],

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -4,7 +4,7 @@
"files": [
{
"path": "registry/base-lyra/ui/checkbox.tsx",
"content": "\"use client\"\n\nimport { Checkbox as CheckboxPrimitive } from \"@base-ui/react/checkbox\"\n\nimport { cn } from \"@/registry/bases/base/lib/utils\"\nimport { IconPlaceholder } from \"@/app/(create)/components/icon-placeholder\"\n\nfunction Checkbox({ className, ...props }: CheckboxPrimitive.Root.Props) {\n return (\n <CheckboxPrimitive.Root\n data-slot=\"checkbox\"\n className={cn(\n \"border-input dark:bg-input/30 data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary data-checked:border-primary aria-invalid:aria-checked:border-primary aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 flex size-4 items-center justify-center rounded-none border transition-colors group-has-disabled/field:opacity-50 focus-visible:ring-1 aria-invalid:ring-1 peer relative shrink-0 outline-none after:absolute after:-inset-x-3 after:-inset-y-2 disabled:cursor-not-allowed disabled:opacity-50\",\n className\n )}\n {...props}\n >\n <CheckboxPrimitive.Indicator\n data-slot=\"checkbox-indicator\"\n className=\"[&>svg]:size-3.5 grid place-content-center text-current transition-none\"\n >\n <IconPlaceholder\n lucide=\"CheckIcon\"\n tabler=\"IconCheck\"\n hugeicons=\"Tick02Icon\"\n />\n </CheckboxPrimitive.Indicator>\n </CheckboxPrimitive.Root>\n )\n}\n\nexport { Checkbox }\n",
"content": "\"use client\"\n\nimport { Checkbox as CheckboxPrimitive } from \"@base-ui/react/checkbox\"\n\nimport { cn } from \"@/registry/bases/base/lib/utils\"\nimport { IconPlaceholder } from \"@/app/(create)/components/icon-placeholder\"\n\nfunction Checkbox({ className, ...props }: CheckboxPrimitive.Root.Props) {\n return (\n <CheckboxPrimitive.Root\n data-slot=\"checkbox\"\n className={cn(\n \"border-input dark:bg-input/30 data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary data-checked:border-primary aria-invalid:aria-checked:border-primary aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 flex size-4 items-center justify-center rounded-none border transition-colors group-has-disabled/field:opacity-50 focus-visible:ring-1 aria-invalid:ring-1 peer relative shrink-0 outline-none after:absolute after:-inset-x-3 after:-inset-y-2 disabled:cursor-not-allowed disabled:opacity-50\",\n className\n )}\n {...props}\n >\n <CheckboxPrimitive.Indicator\n data-slot=\"checkbox-indicator\"\n className=\"[&>svg]:size-3.5 grid place-content-center text-current transition-none\"\n >\n <IconPlaceholder\n lucide=\"CheckIcon\"\n tabler=\"IconCheck\"\n hugeicons=\"Tick02Icon\"\n phosphor=\"CheckIcon\"\n />\n </CheckboxPrimitive.Indicator>\n </CheckboxPrimitive.Root>\n )\n}\n\nexport { Checkbox }\n",
"type": "registry:ui"
}
],

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -7,7 +7,7 @@
"files": [
{
"path": "registry/base-lyra/ui/dialog.tsx",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Dialog as DialogPrimitive } from \"@base-ui/react/dialog\"\n\nimport { cn } from \"@/registry/bases/base/lib/utils\"\nimport { Button } from \"@/registry/bases/base/ui/button\"\nimport { IconPlaceholder } from \"@/app/(create)/components/icon-placeholder\"\n\nfunction Dialog({ ...props }: DialogPrimitive.Root.Props) {\n return <DialogPrimitive.Root data-slot=\"dialog\" {...props} />\n}\n\nfunction DialogTrigger({ ...props }: DialogPrimitive.Trigger.Props) {\n return <DialogPrimitive.Trigger data-slot=\"dialog-trigger\" {...props} />\n}\n\nfunction DialogPortal({ ...props }: DialogPrimitive.Portal.Props) {\n return <DialogPrimitive.Portal data-slot=\"dialog-portal\" {...props} />\n}\n\nfunction DialogClose({ ...props }: DialogPrimitive.Close.Props) {\n return <DialogPrimitive.Close data-slot=\"dialog-close\" {...props} />\n}\n\nfunction DialogOverlay({\n className,\n ...props\n}: DialogPrimitive.Backdrop.Props) {\n return (\n <DialogPrimitive.Backdrop\n data-slot=\"dialog-overlay\"\n className={cn(\"data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs fixed inset-0 isolate z-50\", className)}\n {...props}\n />\n )\n}\n\nfunction DialogContent({\n className,\n children,\n showCloseButton = true,\n ...props\n}: DialogPrimitive.Popup.Props & {\n showCloseButton?: boolean\n}) {\n return (\n <DialogPortal>\n <DialogOverlay />\n <DialogPrimitive.Popup\n data-slot=\"dialog-content\"\n className={cn(\n \"bg-background data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 ring-foreground/10 grid max-w-[calc(100%-2rem)] gap-4 rounded-none p-4 text-xs/relaxed ring-1 duration-100 sm:max-w-sm fixed top-1/2 left-1/2 z-50 w-full -translate-x-1/2 -translate-y-1/2 outline-none\",\n className\n )}\n {...props}\n >\n {children}\n {showCloseButton && (\n <DialogPrimitive.Close\n data-slot=\"dialog-close\"\n render={\n <Button\n variant=\"ghost\"\n className=\"absolute top-2 right-2\"\n size=\"icon-sm\"\n />\n }\n >\n <IconPlaceholder\n lucide=\"XIcon\"\n tabler=\"IconX\"\n hugeicons=\"Cancel01Icon\"\n />\n <span className=\"sr-only\">Close</span>\n </DialogPrimitive.Close>\n )}\n </DialogPrimitive.Popup>\n </DialogPortal>\n )\n}\n\nfunction DialogHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"dialog-header\"\n className={cn(\"gap-1 text-left flex flex-col\", className)}\n {...props}\n />\n )\n}\n\nfunction DialogFooter({\n className,\n showCloseButton = false,\n children,\n ...props\n}: React.ComponentProps<\"div\"> & {\n showCloseButton?: boolean\n}) {\n return (\n <div\n data-slot=\"dialog-footer\"\n className={cn(\n \"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end\",\n className\n )}\n {...props}\n >\n {children}\n {showCloseButton && (\n <DialogPrimitive.Close render={<Button variant=\"outline\" />}>\n Close\n </DialogPrimitive.Close>\n )}\n </div>\n )\n}\n\nfunction DialogTitle({ className, ...props }: DialogPrimitive.Title.Props) {\n return (\n <DialogPrimitive.Title\n data-slot=\"dialog-title\"\n className={cn(\"text-sm font-medium\", className)}\n {...props}\n />\n )\n}\n\nfunction DialogDescription({\n className,\n ...props\n}: DialogPrimitive.Description.Props) {\n return (\n <DialogPrimitive.Description\n data-slot=\"dialog-description\"\n className={cn(\"text-muted-foreground *:[a]:hover:text-foreground text-xs/relaxed *:[a]:underline *:[a]:underline-offset-3\", className)}\n {...props}\n />\n )\n}\n\nexport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogOverlay,\n DialogPortal,\n DialogTitle,\n DialogTrigger,\n}\n",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Dialog as DialogPrimitive } from \"@base-ui/react/dialog\"\n\nimport { cn } from \"@/registry/bases/base/lib/utils\"\nimport { Button } from \"@/registry/bases/base/ui/button\"\nimport { IconPlaceholder } from \"@/app/(create)/components/icon-placeholder\"\n\nfunction Dialog({ ...props }: DialogPrimitive.Root.Props) {\n return <DialogPrimitive.Root data-slot=\"dialog\" {...props} />\n}\n\nfunction DialogTrigger({ ...props }: DialogPrimitive.Trigger.Props) {\n return <DialogPrimitive.Trigger data-slot=\"dialog-trigger\" {...props} />\n}\n\nfunction DialogPortal({ ...props }: DialogPrimitive.Portal.Props) {\n return <DialogPrimitive.Portal data-slot=\"dialog-portal\" {...props} />\n}\n\nfunction DialogClose({ ...props }: DialogPrimitive.Close.Props) {\n return <DialogPrimitive.Close data-slot=\"dialog-close\" {...props} />\n}\n\nfunction DialogOverlay({\n className,\n ...props\n}: DialogPrimitive.Backdrop.Props) {\n return (\n <DialogPrimitive.Backdrop\n data-slot=\"dialog-overlay\"\n className={cn(\"data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs fixed inset-0 isolate z-50\", className)}\n {...props}\n />\n )\n}\n\nfunction DialogContent({\n className,\n children,\n showCloseButton = true,\n ...props\n}: DialogPrimitive.Popup.Props & {\n showCloseButton?: boolean\n}) {\n return (\n <DialogPortal>\n <DialogOverlay />\n <DialogPrimitive.Popup\n data-slot=\"dialog-content\"\n className={cn(\n \"bg-background data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 ring-foreground/10 grid max-w-[calc(100%-2rem)] gap-4 rounded-none p-4 text-xs/relaxed ring-1 duration-100 sm:max-w-sm fixed top-1/2 left-1/2 z-50 w-full -translate-x-1/2 -translate-y-1/2 outline-none\",\n className\n )}\n {...props}\n >\n {children}\n {showCloseButton && (\n <DialogPrimitive.Close\n data-slot=\"dialog-close\"\n render={\n <Button\n variant=\"ghost\"\n className=\"absolute top-2 right-2\"\n size=\"icon-sm\"\n />\n }\n >\n <IconPlaceholder\n lucide=\"XIcon\"\n tabler=\"IconX\"\n hugeicons=\"Cancel01Icon\"\n phosphor=\"XIcon\"\n />\n <span className=\"sr-only\">Close</span>\n </DialogPrimitive.Close>\n )}\n </DialogPrimitive.Popup>\n </DialogPortal>\n )\n}\n\nfunction DialogHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"dialog-header\"\n className={cn(\"gap-1 text-left flex flex-col\", className)}\n {...props}\n />\n )\n}\n\nfunction DialogFooter({\n className,\n showCloseButton = false,\n children,\n ...props\n}: React.ComponentProps<\"div\"> & {\n showCloseButton?: boolean\n}) {\n return (\n <div\n data-slot=\"dialog-footer\"\n className={cn(\n \"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end\",\n className\n )}\n {...props}\n >\n {children}\n {showCloseButton && (\n <DialogPrimitive.Close render={<Button variant=\"outline\" />}>\n Close\n </DialogPrimitive.Close>\n )}\n </div>\n )\n}\n\nfunction DialogTitle({ className, ...props }: DialogPrimitive.Title.Props) {\n return (\n <DialogPrimitive.Title\n data-slot=\"dialog-title\"\n className={cn(\"text-sm font-medium\", className)}\n {...props}\n />\n )\n}\n\nfunction DialogDescription({\n className,\n ...props\n}: DialogPrimitive.Description.Props) {\n return (\n <DialogPrimitive.Description\n data-slot=\"dialog-description\"\n className={cn(\"text-muted-foreground *:[a]:hover:text-foreground text-xs/relaxed *:[a]:underline *:[a]:underline-offset-3\", className)}\n {...props}\n />\n )\n}\n\nexport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogOverlay,\n DialogPortal,\n DialogTitle,\n DialogTrigger,\n}\n",
"type": "registry:ui"
}
],

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -7,7 +7,7 @@
"files": [
{
"path": "registry/base-lyra/ui/input-otp.tsx",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { OTPInput, OTPInputContext } from \"input-otp\"\n\nimport { cn } from \"@/registry/bases/base/lib/utils\"\nimport { IconPlaceholder } from \"@/app/(create)/components/icon-placeholder\"\n\nfunction InputOTP({\n className,\n containerClassName,\n ...props\n}: React.ComponentProps<typeof OTPInput> & {\n containerClassName?: string\n}) {\n return (\n <OTPInput\n data-slot=\"input-otp\"\n containerClassName={cn(\n \"cn-input-otp flex items-center has-disabled:opacity-50\",\n containerClassName\n )}\n spellCheck={false}\n className={cn(\n \"disabled:cursor-not-allowed\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction InputOTPGroup({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"input-otp-group\"\n className={cn(\"has-aria-invalid:ring-destructive/20 dark:has-aria-invalid:ring-destructive/40 has-aria-invalid:border-destructive rounded-none has-aria-invalid:ring-1 flex items-center\", className)}\n {...props}\n />\n )\n}\n\nfunction InputOTPSlot({\n index,\n className,\n ...props\n}: React.ComponentProps<\"div\"> & {\n index: number\n}) {\n const inputOTPContext = React.useContext(OTPInputContext)\n const { char, hasFakeCaret, isActive } = inputOTPContext?.slots[index] ?? {}\n\n return (\n <div\n data-slot=\"input-otp-slot\"\n data-active={isActive}\n className={cn(\n \"dark:bg-input/30 border-input data-[active=true]:border-ring data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:ring-destructive/20 dark:data-[active=true]:aria-invalid:ring-destructive/40 aria-invalid:border-destructive data-[active=true]:aria-invalid:border-destructive size-8 border-y border-r text-xs transition-all outline-none first:rounded-none first:border-l last:rounded-none data-[active=true]:ring-1 relative flex items-center justify-center data-[active=true]:z-10\",\n className\n )}\n {...props}\n >\n {char}\n {hasFakeCaret && (\n <div className=\"pointer-events-none absolute inset-0 flex items-center justify-center\">\n <div className=\"animate-caret-blink bg-foreground h-4 w-px duration-1000 bg-foreground h-4 w-px\" />\n </div>\n )}\n </div>\n )\n}\n\nfunction InputOTPSeparator({ ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"input-otp-separator\"\n className=\"[&_svg:not([class*='size-'])]:size-4 flex items-center\"\n role=\"separator\"\n {...props}\n >\n <IconPlaceholder\n lucide=\"MinusIcon\"\n tabler=\"IconMinus\"\n hugeicons=\"MinusSignIcon\"\n />\n </div>\n )\n}\n\nexport { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator }\n",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { OTPInput, OTPInputContext } from \"input-otp\"\n\nimport { cn } from \"@/registry/bases/base/lib/utils\"\nimport { IconPlaceholder } from \"@/app/(create)/components/icon-placeholder\"\n\nfunction InputOTP({\n className,\n containerClassName,\n ...props\n}: React.ComponentProps<typeof OTPInput> & {\n containerClassName?: string\n}) {\n return (\n <OTPInput\n data-slot=\"input-otp\"\n containerClassName={cn(\n \"cn-input-otp flex items-center has-disabled:opacity-50\",\n containerClassName\n )}\n spellCheck={false}\n className={cn(\n \"disabled:cursor-not-allowed\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction InputOTPGroup({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"input-otp-group\"\n className={cn(\"has-aria-invalid:ring-destructive/20 dark:has-aria-invalid:ring-destructive/40 has-aria-invalid:border-destructive rounded-none has-aria-invalid:ring-1 flex items-center\", className)}\n {...props}\n />\n )\n}\n\nfunction InputOTPSlot({\n index,\n className,\n ...props\n}: React.ComponentProps<\"div\"> & {\n index: number\n}) {\n const inputOTPContext = React.useContext(OTPInputContext)\n const { char, hasFakeCaret, isActive } = inputOTPContext?.slots[index] ?? {}\n\n return (\n <div\n data-slot=\"input-otp-slot\"\n data-active={isActive}\n className={cn(\n \"dark:bg-input/30 border-input data-[active=true]:border-ring data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:ring-destructive/20 dark:data-[active=true]:aria-invalid:ring-destructive/40 aria-invalid:border-destructive data-[active=true]:aria-invalid:border-destructive size-8 border-y border-r text-xs transition-all outline-none first:rounded-none first:border-l last:rounded-none data-[active=true]:ring-1 relative flex items-center justify-center data-[active=true]:z-10\",\n className\n )}\n {...props}\n >\n {char}\n {hasFakeCaret && (\n <div className=\"pointer-events-none absolute inset-0 flex items-center justify-center\">\n <div className=\"animate-caret-blink bg-foreground h-4 w-px duration-1000 bg-foreground h-4 w-px\" />\n </div>\n )}\n </div>\n )\n}\n\nfunction InputOTPSeparator({ ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"input-otp-separator\"\n className=\"[&_svg:not([class*='size-'])]:size-4 flex items-center\"\n role=\"separator\"\n {...props}\n >\n <IconPlaceholder\n lucide=\"MinusIcon\"\n tabler=\"IconMinus\"\n hugeicons=\"MinusSignIcon\"\n phosphor=\"MinusIcon\"\n />\n </div>\n )\n}\n\nexport { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator }\n",
"type": "registry:ui"
}
],

File diff suppressed because one or more lines are too long

View File

@@ -12,7 +12,7 @@
"files": [
{
"path": "registry/base-lyra/examples/kbd-example.tsx",
"content": "import {\n Example,\n ExampleWrapper,\n} from \"@/registry/bases/base/components/example\"\nimport { Button } from \"@/registry/bases/base/ui/button\"\nimport {\n InputGroup,\n InputGroupAddon,\n InputGroupInput,\n} from \"@/registry/bases/base/ui/input-group\"\nimport { Kbd, KbdGroup } from \"@/registry/bases/base/ui/kbd\"\nimport {\n Tooltip,\n TooltipContent,\n TooltipTrigger,\n} from \"@/registry/bases/base/ui/tooltip\"\nimport { IconPlaceholder } from \"@/app/(create)/components/icon-placeholder\"\n\nexport default function KbdExample() {\n return (\n <ExampleWrapper>\n <KbdBasic />\n <KbdModifierKeys />\n <KbdGroupExample />\n <KbdArrowKeys />\n <KbdWithIcons />\n <KbdWithIconsAndText />\n <KbdInInputGroup />\n <KbdInTooltip />\n <KbdWithSamp />\n </ExampleWrapper>\n )\n}\n\nfunction KbdBasic() {\n return (\n <Example title=\"Basic\">\n <div className=\"flex items-center gap-2\">\n <Kbd>Ctrl</Kbd>\n <Kbd>⌘K</Kbd>\n <Kbd>Ctrl + B</Kbd>\n </div>\n </Example>\n )\n}\n\nfunction KbdModifierKeys() {\n return (\n <Example title=\"Modifier Keys\">\n <div className=\"flex items-center gap-2\">\n <Kbd>⌘</Kbd>\n <Kbd>C</Kbd>\n </div>\n </Example>\n )\n}\n\nfunction KbdGroupExample() {\n return (\n <Example title=\"KbdGroup\">\n <KbdGroup>\n <Kbd>Ctrl</Kbd>\n <Kbd>Shift</Kbd>\n <Kbd>P</Kbd>\n </KbdGroup>\n </Example>\n )\n}\n\nfunction KbdArrowKeys() {\n return (\n <Example title=\"Arrow Keys\">\n <div className=\"flex items-center gap-2\">\n <Kbd>↑</Kbd>\n <Kbd>↓</Kbd>\n <Kbd>←</Kbd>\n <Kbd>→</Kbd>\n </div>\n </Example>\n )\n}\n\nfunction KbdWithIcons() {\n return (\n <Example title=\"With Icons\">\n <KbdGroup>\n <Kbd>\n <IconPlaceholder\n lucide=\"CircleDashedIcon\"\n tabler=\"IconCircleDashed\"\n hugeicons=\"DashedLineCircleIcon\"\n />\n </Kbd>\n <Kbd>\n <IconPlaceholder\n lucide=\"ArrowLeftIcon\"\n tabler=\"IconArrowLeft\"\n hugeicons=\"ArrowLeft01Icon\"\n />\n </Kbd>\n <Kbd>\n <IconPlaceholder\n lucide=\"ArrowRightIcon\"\n tabler=\"IconArrowRight\"\n hugeicons=\"ArrowRight01Icon\"\n />\n </Kbd>\n </KbdGroup>\n </Example>\n )\n}\n\nfunction KbdWithIconsAndText() {\n return (\n <Example title=\"With Icons and Text\">\n <KbdGroup>\n <Kbd>\n <IconPlaceholder\n lucide=\"ArrowLeftIcon\"\n tabler=\"IconArrowLeft\"\n hugeicons=\"ArrowLeft01Icon\"\n />\n Left\n </Kbd>\n <Kbd>\n <IconPlaceholder\n lucide=\"CircleDashedIcon\"\n tabler=\"IconCircleDashed\"\n hugeicons=\"DashedLineCircleIcon\"\n />\n Voice Enabled\n </Kbd>\n </KbdGroup>\n </Example>\n )\n}\n\nfunction KbdInInputGroup() {\n return (\n <Example title=\"InputGroup\">\n <InputGroup>\n <InputGroupInput />\n <InputGroupAddon>\n <Kbd>Space</Kbd>\n </InputGroupAddon>\n </InputGroup>\n </Example>\n )\n}\n\nfunction KbdInTooltip() {\n return (\n <Example title=\"Tooltip\">\n <Tooltip>\n <TooltipTrigger render={<Button size=\"icon-sm\" variant=\"outline\" />}>\n <IconPlaceholder\n lucide=\"SaveIcon\"\n tabler=\"IconDeviceFloppy\"\n hugeicons=\"FloppyDiskIcon\"\n />\n </TooltipTrigger>\n <TooltipContent className=\"pr-1.5\">\n <div className=\"flex items-center gap-2\">\n Save Changes <Kbd>S</Kbd>\n </div>\n </TooltipContent>\n </Tooltip>\n </Example>\n )\n}\n\nfunction KbdWithSamp() {\n return (\n <Example title=\"With samp\">\n <Kbd>\n <samp>File</samp>\n </Kbd>\n </Example>\n )\n}\n",
"content": "import {\n Example,\n ExampleWrapper,\n} from \"@/registry/bases/base/components/example\"\nimport { Button } from \"@/registry/bases/base/ui/button\"\nimport {\n InputGroup,\n InputGroupAddon,\n InputGroupInput,\n} from \"@/registry/bases/base/ui/input-group\"\nimport { Kbd, KbdGroup } from \"@/registry/bases/base/ui/kbd\"\nimport {\n Tooltip,\n TooltipContent,\n TooltipTrigger,\n} from \"@/registry/bases/base/ui/tooltip\"\nimport { IconPlaceholder } from \"@/app/(create)/components/icon-placeholder\"\n\nexport default function KbdExample() {\n return (\n <ExampleWrapper>\n <KbdBasic />\n <KbdModifierKeys />\n <KbdGroupExample />\n <KbdArrowKeys />\n <KbdWithIcons />\n <KbdWithIconsAndText />\n <KbdInInputGroup />\n <KbdInTooltip />\n <KbdWithSamp />\n </ExampleWrapper>\n )\n}\n\nfunction KbdBasic() {\n return (\n <Example title=\"Basic\">\n <div className=\"flex items-center gap-2\">\n <Kbd>Ctrl</Kbd>\n <Kbd>⌘K</Kbd>\n <Kbd>Ctrl + B</Kbd>\n </div>\n </Example>\n )\n}\n\nfunction KbdModifierKeys() {\n return (\n <Example title=\"Modifier Keys\">\n <div className=\"flex items-center gap-2\">\n <Kbd>⌘</Kbd>\n <Kbd>C</Kbd>\n </div>\n </Example>\n )\n}\n\nfunction KbdGroupExample() {\n return (\n <Example title=\"KbdGroup\">\n <KbdGroup>\n <Kbd>Ctrl</Kbd>\n <Kbd>Shift</Kbd>\n <Kbd>P</Kbd>\n </KbdGroup>\n </Example>\n )\n}\n\nfunction KbdArrowKeys() {\n return (\n <Example title=\"Arrow Keys\">\n <div className=\"flex items-center gap-2\">\n <Kbd>↑</Kbd>\n <Kbd>↓</Kbd>\n <Kbd>←</Kbd>\n <Kbd>→</Kbd>\n </div>\n </Example>\n )\n}\n\nfunction KbdWithIcons() {\n return (\n <Example title=\"With Icons\">\n <KbdGroup>\n <Kbd>\n <IconPlaceholder\n lucide=\"CircleDashedIcon\"\n tabler=\"IconCircleDashed\"\n hugeicons=\"DashedLineCircleIcon\"\n phosphor=\"CircleDashedIcon\"\n />\n </Kbd>\n <Kbd>\n <IconPlaceholder\n lucide=\"ArrowLeftIcon\"\n tabler=\"IconArrowLeft\"\n hugeicons=\"ArrowLeft01Icon\"\n phosphor=\"ArrowLeftIcon\"\n />\n </Kbd>\n <Kbd>\n <IconPlaceholder\n lucide=\"ArrowRightIcon\"\n tabler=\"IconArrowRight\"\n hugeicons=\"ArrowRight01Icon\"\n phosphor=\"ArrowRightIcon\"\n />\n </Kbd>\n </KbdGroup>\n </Example>\n )\n}\n\nfunction KbdWithIconsAndText() {\n return (\n <Example title=\"With Icons and Text\">\n <KbdGroup>\n <Kbd>\n <IconPlaceholder\n lucide=\"ArrowLeftIcon\"\n tabler=\"IconArrowLeft\"\n hugeicons=\"ArrowLeft01Icon\"\n phosphor=\"ArrowLeftIcon\"\n />\n Left\n </Kbd>\n <Kbd>\n <IconPlaceholder\n lucide=\"CircleDashedIcon\"\n tabler=\"IconCircleDashed\"\n hugeicons=\"DashedLineCircleIcon\"\n phosphor=\"CircleDashedIcon\"\n />\n Voice Enabled\n </Kbd>\n </KbdGroup>\n </Example>\n )\n}\n\nfunction KbdInInputGroup() {\n return (\n <Example title=\"InputGroup\">\n <InputGroup>\n <InputGroupInput />\n <InputGroupAddon>\n <Kbd>Space</Kbd>\n </InputGroupAddon>\n </InputGroup>\n </Example>\n )\n}\n\nfunction KbdInTooltip() {\n return (\n <Example title=\"Tooltip\">\n <Tooltip>\n <TooltipTrigger render={<Button size=\"icon-sm\" variant=\"outline\" />}>\n <IconPlaceholder\n lucide=\"SaveIcon\"\n tabler=\"IconDeviceFloppy\"\n hugeicons=\"FloppyDiskIcon\"\n phosphor=\"FloppyDiskIcon\"\n />\n </TooltipTrigger>\n <TooltipContent className=\"pr-1.5\">\n <div className=\"flex items-center gap-2\">\n Save Changes <Kbd>S</Kbd>\n </div>\n </TooltipContent>\n </Tooltip>\n </Example>\n )\n}\n\nfunction KbdWithSamp() {\n return (\n <Example title=\"With samp\">\n <Kbd>\n <samp>File</samp>\n </Kbd>\n </Example>\n )\n}\n",
"type": "registry:example"
}
],

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -4,7 +4,7 @@
"files": [
{
"path": "registry/base-lyra/ui/native-select.tsx",
"content": "import * as React from \"react\"\n\nimport { cn } from \"@/registry/bases/base/lib/utils\"\nimport { IconPlaceholder } from \"@/app/(create)/components/icon-placeholder\"\n\ntype NativeSelectProps = Omit<React.ComponentProps<\"select\">, \"size\"> & {\n size?: \"sm\" | \"default\"\n}\n\nfunction NativeSelect({\n className,\n size = \"default\",\n ...props\n}: NativeSelectProps) {\n return (\n <div\n className={cn(\n \"group/native-select relative w-fit has-[select:disabled]:opacity-50\",\n className\n )}\n data-slot=\"native-select-wrapper\"\n data-size={size}\n >\n <select\n data-slot=\"native-select\"\n data-size={size}\n className=\"border-input placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 dark:hover:bg-input/50 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 h-8 w-full min-w-0 appearance-none rounded-none border bg-transparent py-1 pr-8 pl-2.5 text-xs transition-colors select-none focus-visible:ring-1 aria-invalid:ring-1 data-[size=sm]:h-7 data-[size=sm]:rounded-none data-[size=sm]:py-0.5 outline-none disabled:pointer-events-none disabled:cursor-not-allowed\"\n {...props}\n />\n <IconPlaceholder\n lucide=\"ChevronDownIcon\"\n tabler=\"IconSelector\"\n hugeicons=\"UnfoldMoreIcon\"\n className=\"text-muted-foreground top-1/2 right-2.5 size-4 -translate-y-1/2 pointer-events-none absolute select-none\"\n aria-hidden=\"true\"\n data-slot=\"native-select-icon\"\n />\n </div>\n )\n}\n\nfunction NativeSelectOption({ ...props }: React.ComponentProps<\"option\">) {\n return <option data-slot=\"native-select-option\" {...props} />\n}\n\nfunction NativeSelectOptGroup({\n className,\n ...props\n}: React.ComponentProps<\"optgroup\">) {\n return (\n <optgroup\n data-slot=\"native-select-optgroup\"\n className={cn(className)}\n {...props}\n />\n )\n}\n\nexport { NativeSelect, NativeSelectOptGroup, NativeSelectOption }\n",
"content": "import * as React from \"react\"\n\nimport { cn } from \"@/registry/bases/base/lib/utils\"\nimport { IconPlaceholder } from \"@/app/(create)/components/icon-placeholder\"\n\ntype NativeSelectProps = Omit<React.ComponentProps<\"select\">, \"size\"> & {\n size?: \"sm\" | \"default\"\n}\n\nfunction NativeSelect({\n className,\n size = \"default\",\n ...props\n}: NativeSelectProps) {\n return (\n <div\n className={cn(\n \"group/native-select relative w-fit has-[select:disabled]:opacity-50\",\n className\n )}\n data-slot=\"native-select-wrapper\"\n data-size={size}\n >\n <select\n data-slot=\"native-select\"\n data-size={size}\n className=\"border-input placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 dark:hover:bg-input/50 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 h-8 w-full min-w-0 appearance-none rounded-none border bg-transparent py-1 pr-8 pl-2.5 text-xs transition-colors select-none focus-visible:ring-1 aria-invalid:ring-1 data-[size=sm]:h-7 data-[size=sm]:rounded-none data-[size=sm]:py-0.5 outline-none disabled:pointer-events-none disabled:cursor-not-allowed\"\n {...props}\n />\n <IconPlaceholder\n lucide=\"ChevronDownIcon\"\n tabler=\"IconSelector\"\n hugeicons=\"UnfoldMoreIcon\"\n phosphor=\"CaretDownIcon\"\n className=\"text-muted-foreground top-1/2 right-2.5 size-4 -translate-y-1/2 pointer-events-none absolute select-none\"\n aria-hidden=\"true\"\n data-slot=\"native-select-icon\"\n />\n </div>\n )\n}\n\nfunction NativeSelectOption({ ...props }: React.ComponentProps<\"option\">) {\n return <option data-slot=\"native-select-option\" {...props} />\n}\n\nfunction NativeSelectOptGroup({\n className,\n ...props\n}: React.ComponentProps<\"optgroup\">) {\n return (\n <optgroup\n data-slot=\"native-select-optgroup\"\n className={cn(className)}\n {...props}\n />\n )\n}\n\nexport { NativeSelect, NativeSelectOptGroup, NativeSelectOption }\n",
"type": "registry:ui"
}
],

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -7,7 +7,7 @@
"files": [
{
"path": "registry/base-lyra/ui/pagination.tsx",
"content": "import * as React from \"react\"\n\nimport { cn } from \"@/registry/bases/base/lib/utils\"\nimport { Button } from \"@/registry/bases/base/ui/button\"\nimport { IconPlaceholder } from \"@/app/(create)/components/icon-placeholder\"\n\nfunction Pagination({ className, ...props }: React.ComponentProps<\"nav\">) {\n return (\n <nav\n role=\"navigation\"\n aria-label=\"pagination\"\n data-slot=\"pagination\"\n className={cn(\n \"mx-auto flex w-full justify-center\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction PaginationContent({\n className,\n ...props\n}: React.ComponentProps<\"ul\">) {\n return (\n <ul\n data-slot=\"pagination-content\"\n className={cn(\"gap-0.5 flex items-center\", className)}\n {...props}\n />\n )\n}\n\nfunction PaginationItem({ ...props }: React.ComponentProps<\"li\">) {\n return <li data-slot=\"pagination-item\" {...props} />\n}\n\ntype PaginationLinkProps = {\n isActive?: boolean\n} & Pick<React.ComponentProps<typeof Button>, \"size\"> &\n React.ComponentProps<\"a\">\n\nfunction PaginationLink({\n className,\n isActive,\n size = \"icon\",\n ...props\n}: PaginationLinkProps) {\n return (\n <Button\n variant={isActive ? \"outline\" : \"ghost\"}\n size={size}\n className={cn(className)}\n nativeButton={false}\n render={\n <a\n aria-current={isActive ? \"page\" : undefined}\n data-slot=\"pagination-link\"\n data-active={isActive}\n {...props}\n />\n }\n />\n )\n}\n\nfunction PaginationPrevious({\n className,\n ...props\n}: React.ComponentProps<typeof PaginationLink>) {\n return (\n <PaginationLink\n aria-label=\"Go to previous page\"\n size=\"default\"\n className={cn(\"pl-1.5!\", className)}\n {...props}\n >\n <IconPlaceholder\n lucide=\"ChevronLeftIcon\"\n tabler=\"IconChevronLeft\"\n hugeicons=\"ArrowLeft01Icon\"\n data-icon=\"inline-start\"\n />\n <span className=\"hidden sm:block\">\n Previous\n </span>\n </PaginationLink>\n )\n}\n\nfunction PaginationNext({\n className,\n ...props\n}: React.ComponentProps<typeof PaginationLink>) {\n return (\n <PaginationLink\n aria-label=\"Go to next page\"\n size=\"default\"\n className={cn(\"pr-1.5!\", className)}\n {...props}\n >\n <span className=\"hidden sm:block\">Next</span>\n <IconPlaceholder\n lucide=\"ChevronRightIcon\"\n tabler=\"IconChevronRight\"\n hugeicons=\"ArrowRight01Icon\"\n data-icon=\"inline-end\"\n />\n </PaginationLink>\n )\n}\n\nfunction PaginationEllipsis({\n className,\n ...props\n}: React.ComponentProps<\"span\">) {\n return (\n <span\n aria-hidden\n data-slot=\"pagination-ellipsis\"\n className={cn(\n \"size-8 items-center justify-center [&_svg:not([class*='size-'])]:size-4 flex items-center justify-center\",\n className\n )}\n {...props}\n >\n <IconPlaceholder\n lucide=\"MoreHorizontalIcon\"\n tabler=\"IconDots\"\n hugeicons=\"MoreHorizontalCircle01Icon\"\n />\n <span className=\"sr-only\">More pages</span>\n </span>\n )\n}\n\nexport {\n Pagination,\n PaginationContent,\n PaginationEllipsis,\n PaginationItem,\n PaginationLink,\n PaginationNext,\n PaginationPrevious,\n}\n",
"content": "import * as React from \"react\"\n\nimport { cn } from \"@/registry/bases/base/lib/utils\"\nimport { Button } from \"@/registry/bases/base/ui/button\"\nimport { IconPlaceholder } from \"@/app/(create)/components/icon-placeholder\"\n\nfunction Pagination({ className, ...props }: React.ComponentProps<\"nav\">) {\n return (\n <nav\n role=\"navigation\"\n aria-label=\"pagination\"\n data-slot=\"pagination\"\n className={cn(\n \"mx-auto flex w-full justify-center\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction PaginationContent({\n className,\n ...props\n}: React.ComponentProps<\"ul\">) {\n return (\n <ul\n data-slot=\"pagination-content\"\n className={cn(\"gap-0.5 flex items-center\", className)}\n {...props}\n />\n )\n}\n\nfunction PaginationItem({ ...props }: React.ComponentProps<\"li\">) {\n return <li data-slot=\"pagination-item\" {...props} />\n}\n\ntype PaginationLinkProps = {\n isActive?: boolean\n} & Pick<React.ComponentProps<typeof Button>, \"size\"> &\n React.ComponentProps<\"a\">\n\nfunction PaginationLink({\n className,\n isActive,\n size = \"icon\",\n ...props\n}: PaginationLinkProps) {\n return (\n <Button\n variant={isActive ? \"outline\" : \"ghost\"}\n size={size}\n className={cn(className)}\n nativeButton={false}\n render={\n <a\n aria-current={isActive ? \"page\" : undefined}\n data-slot=\"pagination-link\"\n data-active={isActive}\n {...props}\n />\n }\n />\n )\n}\n\nfunction PaginationPrevious({\n className,\n ...props\n}: React.ComponentProps<typeof PaginationLink>) {\n return (\n <PaginationLink\n aria-label=\"Go to previous page\"\n size=\"default\"\n className={cn(\"pl-1.5!\", className)}\n {...props}\n >\n <IconPlaceholder\n lucide=\"ChevronLeftIcon\"\n tabler=\"IconChevronLeft\"\n hugeicons=\"ArrowLeft01Icon\"\n phosphor=\"CaretLeftIcon\"\n data-icon=\"inline-start\"\n />\n <span className=\"hidden sm:block\">\n Previous\n </span>\n </PaginationLink>\n )\n}\n\nfunction PaginationNext({\n className,\n ...props\n}: React.ComponentProps<typeof PaginationLink>) {\n return (\n <PaginationLink\n aria-label=\"Go to next page\"\n size=\"default\"\n className={cn(\"pr-1.5!\", className)}\n {...props}\n >\n <span className=\"hidden sm:block\">Next</span>\n <IconPlaceholder\n lucide=\"ChevronRightIcon\"\n tabler=\"IconChevronRight\"\n hugeicons=\"ArrowRight01Icon\"\n phosphor=\"CaretRightIcon\"\n data-icon=\"inline-end\"\n />\n </PaginationLink>\n )\n}\n\nfunction PaginationEllipsis({\n className,\n ...props\n}: React.ComponentProps<\"span\">) {\n return (\n <span\n aria-hidden\n data-slot=\"pagination-ellipsis\"\n className={cn(\n \"size-8 items-center justify-center [&_svg:not([class*='size-'])]:size-4 flex items-center justify-center\",\n className\n )}\n {...props}\n >\n <IconPlaceholder\n lucide=\"MoreHorizontalIcon\"\n tabler=\"IconDots\"\n hugeicons=\"MoreHorizontalCircle01Icon\"\n phosphor=\"DotsThreeIcon\"\n />\n <span className=\"sr-only\">More pages</span>\n </span>\n )\n}\n\nexport {\n Pagination,\n PaginationContent,\n PaginationEllipsis,\n PaginationItem,\n PaginationLink,\n PaginationNext,\n PaginationPrevious,\n}\n",
"type": "registry:ui"
}
],

File diff suppressed because one or more lines are too long

View File

@@ -12,7 +12,7 @@
"files": [
{
"path": "registry/base-lyra/examples/progress-example.tsx",
"content": "\"use client\"\n\nimport * as React from \"react\"\n\nimport {\n Example,\n ExampleWrapper,\n} from \"@/registry/bases/base/components/example\"\nimport {\n Item,\n ItemActions,\n ItemContent,\n ItemGroup,\n ItemMedia,\n ItemTitle,\n} from \"@/registry/bases/base/ui/item\"\nimport {\n Progress,\n ProgressLabel,\n ProgressValue,\n} from \"@/registry/bases/base/ui/progress\"\nimport { Slider } from \"@/registry/bases/base/ui/slider\"\nimport { IconPlaceholder } from \"@/app/(create)/components/icon-placeholder\"\n\nexport default function ProgressExample() {\n return (\n <ExampleWrapper>\n <ProgressValues />\n <ProgressWithLabel />\n <ProgressControlled />\n <FileUploadList />\n </ExampleWrapper>\n )\n}\n\nfunction ProgressValues() {\n return (\n <Example title=\"Progress Bar\">\n <div className=\"flex w-full flex-col gap-4\">\n <Progress value={0} />\n <Progress value={25} className=\"w-full\" />\n <Progress value={50} />\n <Progress value={75} />\n <Progress value={100} />\n </div>\n </Example>\n )\n}\n\nfunction ProgressWithLabel() {\n return (\n <Example title=\"With Label\">\n <Progress value={56}>\n <ProgressLabel>Upload progress</ProgressLabel>\n <ProgressValue />\n </Progress>\n </Example>\n )\n}\n\nfunction ProgressControlled() {\n const [value, setValue] = React.useState(50)\n\n return (\n <Example title=\"Controlled\">\n <div className=\"flex w-full flex-col gap-4\">\n <Progress value={value} className=\"w-full\" />\n <Slider\n value={value}\n onValueChange={(value) => setValue(value as number)}\n min={0}\n max={100}\n step={1}\n />\n </div>\n </Example>\n )\n}\n\nfunction FileUploadList() {\n const files = React.useMemo(\n () => [\n {\n id: \"1\",\n name: \"document.pdf\",\n progress: 45,\n timeRemaining: \"2m 30s\",\n },\n {\n id: \"2\",\n name: \"presentation.pptx\",\n progress: 78,\n timeRemaining: \"45s\",\n },\n {\n id: \"3\",\n name: \"spreadsheet.xlsx\",\n progress: 12,\n timeRemaining: \"5m 12s\",\n },\n {\n id: \"4\",\n name: \"image.jpg\",\n progress: 100,\n timeRemaining: \"Complete\",\n },\n ],\n []\n )\n\n return (\n <Example title=\"File Upload List\">\n <ItemGroup>\n {files.map((file) => (\n <Item key={file.id} size=\"xs\" className=\"px-0\">\n <ItemMedia variant=\"icon\">\n <IconPlaceholder\n lucide=\"FileIcon\"\n tabler=\"IconFile\"\n hugeicons=\"FileIcon\"\n className=\"size-5\"\n />\n </ItemMedia>\n <ItemContent className=\"inline-block truncate\">\n <ItemTitle className=\"inline\">{file.name}</ItemTitle>\n </ItemContent>\n <ItemContent>\n <Progress value={file.progress} className=\"w-32\" />\n </ItemContent>\n <ItemActions className=\"w-16 justify-end\">\n <span className=\"text-muted-foreground text-sm\">\n {file.timeRemaining}\n </span>\n </ItemActions>\n </Item>\n ))}\n </ItemGroup>\n </Example>\n )\n}\n",
"content": "\"use client\"\n\nimport * as React from \"react\"\n\nimport {\n Example,\n ExampleWrapper,\n} from \"@/registry/bases/base/components/example\"\nimport {\n Item,\n ItemActions,\n ItemContent,\n ItemGroup,\n ItemMedia,\n ItemTitle,\n} from \"@/registry/bases/base/ui/item\"\nimport {\n Progress,\n ProgressLabel,\n ProgressValue,\n} from \"@/registry/bases/base/ui/progress\"\nimport { Slider } from \"@/registry/bases/base/ui/slider\"\nimport { IconPlaceholder } from \"@/app/(create)/components/icon-placeholder\"\n\nexport default function ProgressExample() {\n return (\n <ExampleWrapper>\n <ProgressValues />\n <ProgressWithLabel />\n <ProgressControlled />\n <FileUploadList />\n </ExampleWrapper>\n )\n}\n\nfunction ProgressValues() {\n return (\n <Example title=\"Progress Bar\">\n <div className=\"flex w-full flex-col gap-4\">\n <Progress value={0} />\n <Progress value={25} className=\"w-full\" />\n <Progress value={50} />\n <Progress value={75} />\n <Progress value={100} />\n </div>\n </Example>\n )\n}\n\nfunction ProgressWithLabel() {\n return (\n <Example title=\"With Label\">\n <Progress value={56}>\n <ProgressLabel>Upload progress</ProgressLabel>\n <ProgressValue />\n </Progress>\n </Example>\n )\n}\n\nfunction ProgressControlled() {\n const [value, setValue] = React.useState(50)\n\n return (\n <Example title=\"Controlled\">\n <div className=\"flex w-full flex-col gap-4\">\n <Progress value={value} className=\"w-full\" />\n <Slider\n value={value}\n onValueChange={(value) => setValue(value as number)}\n min={0}\n max={100}\n step={1}\n />\n </div>\n </Example>\n )\n}\n\nfunction FileUploadList() {\n const files = React.useMemo(\n () => [\n {\n id: \"1\",\n name: \"document.pdf\",\n progress: 45,\n timeRemaining: \"2m 30s\",\n },\n {\n id: \"2\",\n name: \"presentation.pptx\",\n progress: 78,\n timeRemaining: \"45s\",\n },\n {\n id: \"3\",\n name: \"spreadsheet.xlsx\",\n progress: 12,\n timeRemaining: \"5m 12s\",\n },\n {\n id: \"4\",\n name: \"image.jpg\",\n progress: 100,\n timeRemaining: \"Complete\",\n },\n ],\n []\n )\n\n return (\n <Example title=\"File Upload List\">\n <ItemGroup>\n {files.map((file) => (\n <Item key={file.id} size=\"xs\" className=\"px-0\">\n <ItemMedia variant=\"icon\">\n <IconPlaceholder\n lucide=\"FileIcon\"\n tabler=\"IconFile\"\n hugeicons=\"FileIcon\"\n phosphor=\"FileIcon\"\n className=\"size-5\"\n />\n </ItemMedia>\n <ItemContent className=\"inline-block truncate\">\n <ItemTitle className=\"inline\">{file.name}</ItemTitle>\n </ItemContent>\n <ItemContent>\n <Progress value={file.progress} className=\"w-32\" />\n </ItemContent>\n <ItemActions className=\"w-16 justify-end\">\n <span className=\"text-muted-foreground text-sm\">\n {file.timeRemaining}\n </span>\n </ItemActions>\n </Item>\n ))}\n </ItemGroup>\n </Example>\n )\n}\n",
"type": "registry:example"
}
],

View File

@@ -4,7 +4,7 @@
"files": [
{
"path": "registry/base-lyra/ui/radio-group.tsx",
"content": "\"use client\"\n\nimport { Radio as RadioPrimitive } from \"@base-ui/react/radio\"\nimport { RadioGroup as RadioGroupPrimitive } from \"@base-ui/react/radio-group\"\n\nimport { cn } from \"@/registry/bases/base/lib/utils\"\nimport { IconPlaceholder } from \"@/app/(create)/components/icon-placeholder\"\n\nfunction RadioGroup({ className, ...props }: RadioGroupPrimitive.Props) {\n return (\n <RadioGroupPrimitive\n data-slot=\"radio-group\"\n className={cn(\"grid gap-2 w-full\", className)}\n {...props}\n />\n )\n}\n\nfunction RadioGroupItem({ className, ...props }: RadioPrimitive.Root.Props) {\n return (\n <RadioPrimitive.Root\n data-slot=\"radio-group-item\"\n className={cn(\n \"border-input text-primary dark:bg-input/30 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 data-checked:bg-primary data-checked:border-primary flex size-4 rounded-full focus-visible:ring-1 aria-invalid:ring-1 group/radio-group-item peer relative aspect-square shrink-0 border outline-none after:absolute after:-inset-x-3 after:-inset-y-2 disabled:cursor-not-allowed disabled:opacity-50\",\n className\n )}\n {...props}\n >\n <RadioPrimitive.Indicator\n data-slot=\"radio-group-indicator\"\n className=\"group-aria-invalid/radio-group-item:text-destructive flex size-4 items-center justify-center text-white\"\n >\n <IconPlaceholder\n lucide=\"CircleIcon\"\n tabler=\"IconCircle\"\n hugeicons=\"CircleIcon\"\n className=\"absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2 fill-current\"\n />\n </RadioPrimitive.Indicator>\n </RadioPrimitive.Root>\n )\n}\n\nexport { RadioGroup, RadioGroupItem }\n",
"content": "\"use client\"\n\nimport { Radio as RadioPrimitive } from \"@base-ui/react/radio\"\nimport { RadioGroup as RadioGroupPrimitive } from \"@base-ui/react/radio-group\"\n\nimport { cn } from \"@/registry/bases/base/lib/utils\"\nimport { IconPlaceholder } from \"@/app/(create)/components/icon-placeholder\"\n\nfunction RadioGroup({ className, ...props }: RadioGroupPrimitive.Props) {\n return (\n <RadioGroupPrimitive\n data-slot=\"radio-group\"\n className={cn(\"grid gap-2 w-full\", className)}\n {...props}\n />\n )\n}\n\nfunction RadioGroupItem({ className, ...props }: RadioPrimitive.Root.Props) {\n return (\n <RadioPrimitive.Root\n data-slot=\"radio-group-item\"\n className={cn(\n \"border-input text-primary dark:bg-input/30 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 data-checked:bg-primary data-checked:border-primary flex size-4 rounded-full focus-visible:ring-1 aria-invalid:ring-1 group/radio-group-item peer relative aspect-square shrink-0 border outline-none after:absolute after:-inset-x-3 after:-inset-y-2 disabled:cursor-not-allowed disabled:opacity-50\",\n className\n )}\n {...props}\n >\n <RadioPrimitive.Indicator\n data-slot=\"radio-group-indicator\"\n className=\"group-aria-invalid/radio-group-item:text-destructive flex size-4 items-center justify-center text-white\"\n >\n <IconPlaceholder\n lucide=\"CircleIcon\"\n tabler=\"IconCircle\"\n hugeicons=\"CircleIcon\"\n phosphor=\"CircleIcon\"\n className=\"absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2 fill-current\"\n />\n </RadioPrimitive.Indicator>\n </RadioPrimitive.Root>\n )\n}\n\nexport { RadioGroup, RadioGroupItem }\n",
"type": "registry:ui"
}
],

View File

@@ -4,7 +4,7 @@
"files": [
{
"path": "registry/base-lyra/ui/scroll-area.tsx",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { ScrollArea as ScrollAreaPrimitive } from \"@base-ui/react/scroll-area\"\n\nimport { cn } from \"@/registry/bases/base/lib/utils\"\n\nfunction ScrollArea({\n className,\n children,\n ...props\n}: ScrollAreaPrimitive.Root.Props) {\n return (\n <ScrollAreaPrimitive.Root\n data-slot=\"scroll-area\"\n className={cn(\"relative\", className)}\n {...props}\n >\n <ScrollAreaPrimitive.Viewport\n data-slot=\"scroll-area-viewport\"\n className=\"focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1\"\n >\n {children}\n </ScrollAreaPrimitive.Viewport>\n <ScrollBar />\n <ScrollAreaPrimitive.Corner />\n </ScrollAreaPrimitive.Root>\n )\n}\n\nfunction ScrollBar({\n className,\n orientation = \"vertical\",\n ...props\n}: ScrollAreaPrimitive.Scrollbar.Props) {\n return (\n <ScrollAreaPrimitive.Scrollbar\n data-slot=\"scroll-area-scrollbar\"\n data-orientation={orientation}\n orientation={orientation}\n className=\"data-horizontal:h-2.5 data-horizontal:flex-col data-horizontal:border-t data-horizontal:border-t-transparent data-vertical:h-full data-vertical:w-2.5 data-vertical:border-l data-vertical:border-l-transparent flex touch-none p-px transition-colors select-none\"\n {...props}\n >\n <ScrollAreaPrimitive.Thumb\n data-slot=\"scroll-area-thumb\"\n className=\"rounded-none bg-border relative flex-1\"\n />\n </ScrollAreaPrimitive.Scrollbar>\n )\n}\n\nexport { ScrollArea, ScrollBar }\n",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { ScrollArea as ScrollAreaPrimitive } from \"@base-ui/react/scroll-area\"\n\nimport { cn } from \"@/registry/bases/base/lib/utils\"\n\nfunction ScrollArea({\n className,\n children,\n ...props\n}: ScrollAreaPrimitive.Root.Props) {\n return (\n <ScrollAreaPrimitive.Root\n data-slot=\"scroll-area\"\n className={cn(\"relative\", className)}\n {...props}\n >\n <ScrollAreaPrimitive.Viewport\n data-slot=\"scroll-area-viewport\"\n className=\"focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1\"\n >\n {children}\n </ScrollAreaPrimitive.Viewport>\n <ScrollBar />\n <ScrollAreaPrimitive.Corner />\n </ScrollAreaPrimitive.Root>\n )\n}\n\nfunction ScrollBar({\n className,\n orientation = \"vertical\",\n ...props\n}: ScrollAreaPrimitive.Scrollbar.Props) {\n return (\n <ScrollAreaPrimitive.Scrollbar\n data-slot=\"scroll-area-scrollbar\"\n data-orientation={orientation}\n orientation={orientation}\n className={cn(\n \"data-horizontal:h-2.5 data-horizontal:flex-col data-horizontal:border-t data-horizontal:border-t-transparent data-vertical:h-full data-vertical:w-2.5 data-vertical:border-l data-vertical:border-l-transparent flex touch-none p-px transition-colors select-none\",\n className\n )}\n {...props}\n >\n <ScrollAreaPrimitive.Thumb\n data-slot=\"scroll-area-thumb\"\n className=\"rounded-none bg-border relative flex-1\"\n />\n </ScrollAreaPrimitive.Scrollbar>\n )\n}\n\nexport { ScrollArea, ScrollBar }\n",
"type": "registry:ui"
}
],

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -7,7 +7,7 @@
"files": [
{
"path": "registry/base-lyra/ui/sheet.tsx",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Dialog as SheetPrimitive } from \"@base-ui/react/dialog\"\n\nimport { cn } from \"@/registry/bases/base/lib/utils\"\nimport { Button } from \"@/registry/bases/base/ui/button\"\nimport { IconPlaceholder } from \"@/app/(create)/components/icon-placeholder\"\n\nfunction Sheet({ ...props }: SheetPrimitive.Root.Props) {\n return <SheetPrimitive.Root data-slot=\"sheet\" {...props} />\n}\n\nfunction SheetTrigger({ ...props }: SheetPrimitive.Trigger.Props) {\n return <SheetPrimitive.Trigger data-slot=\"sheet-trigger\" {...props} />\n}\n\nfunction SheetClose({ ...props }: SheetPrimitive.Close.Props) {\n return <SheetPrimitive.Close data-slot=\"sheet-close\" {...props} />\n}\n\nfunction SheetPortal({ ...props }: SheetPrimitive.Portal.Props) {\n return <SheetPrimitive.Portal data-slot=\"sheet-portal\" {...props} />\n}\n\nfunction SheetOverlay({ className, ...props }: SheetPrimitive.Backdrop.Props) {\n return (\n <SheetPrimitive.Backdrop\n data-slot=\"sheet-overlay\"\n className={cn(\"data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 bg-black/10 text-xs/relaxed duration-100 data-ending-style:opacity-0 data-starting-style:opacity-0 supports-backdrop-filter:backdrop-blur-xs fixed inset-0 z-50\", className)}\n {...props}\n />\n )\n}\n\nfunction SheetContent({\n className,\n children,\n side = \"right\",\n showCloseButton = true,\n ...props\n}: SheetPrimitive.Popup.Props & {\n side?: \"top\" | \"right\" | \"bottom\" | \"left\"\n showCloseButton?: boolean\n}) {\n return (\n <SheetPortal>\n <SheetOverlay />\n <SheetPrimitive.Popup\n data-slot=\"sheet-content\"\n data-side={side}\n className={cn(\"bg-background data-open:animate-in data-closed:animate-out data-[side=right]:data-closed:slide-out-to-right-10 data-[side=right]:data-open:slide-in-from-right-10 data-[side=left]:data-closed:slide-out-to-left-10 data-[side=left]:data-open:slide-in-from-left-10 data-[side=top]:data-closed:slide-out-to-top-10 data-[side=top]:data-open:slide-in-from-top-10 data-closed:fade-out-0 data-open:fade-in-0 data-[side=bottom]:data-closed:slide-out-to-bottom-10 data-[side=bottom]:data-open:slide-in-from-bottom-10 fixed z-50 flex flex-col bg-clip-padding text-xs/relaxed shadow-lg transition duration-200 ease-in-out data-[side=bottom]:inset-x-0 data-[side=bottom]:bottom-0 data-[side=bottom]:h-auto data-[side=bottom]:border-t data-[side=left]:inset-y-0 data-[side=left]:left-0 data-[side=left]:h-full data-[side=left]:w-3/4 data-[side=left]:border-r data-[side=right]:inset-y-0 data-[side=right]:right-0 data-[side=right]:h-full data-[side=right]:w-3/4 data-[side=right]:border-l data-[side=top]:inset-x-0 data-[side=top]:top-0 data-[side=top]:h-auto data-[side=top]:border-b data-[side=left]:sm:max-w-sm data-[side=right]:sm:max-w-sm\", className)}\n {...props}\n >\n {children}\n {showCloseButton && (\n <SheetPrimitive.Close\n data-slot=\"sheet-close\"\n render={\n <Button\n variant=\"ghost\"\n className=\"absolute top-3 right-3\"\n size=\"icon-sm\"\n />\n }\n >\n <IconPlaceholder\n lucide=\"XIcon\"\n tabler=\"IconX\"\n hugeicons=\"Cancel01Icon\"\n />\n <span className=\"sr-only\">Close</span>\n </SheetPrimitive.Close>\n )}\n </SheetPrimitive.Popup>\n </SheetPortal>\n )\n}\n\nfunction SheetHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"sheet-header\"\n className={cn(\"gap-0.5 p-4 flex flex-col\", className)}\n {...props}\n />\n )\n}\n\nfunction SheetFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"sheet-footer\"\n className={cn(\"gap-2 p-4 mt-auto flex flex-col\", className)}\n {...props}\n />\n )\n}\n\nfunction SheetTitle({ className, ...props }: SheetPrimitive.Title.Props) {\n return (\n <SheetPrimitive.Title\n data-slot=\"sheet-title\"\n className={cn(\"text-foreground text-sm font-medium\", className)}\n {...props}\n />\n )\n}\n\nfunction SheetDescription({\n className,\n ...props\n}: SheetPrimitive.Description.Props) {\n return (\n <SheetPrimitive.Description\n data-slot=\"sheet-description\"\n className={cn(\"text-muted-foreground text-xs/relaxed\", className)}\n {...props}\n />\n )\n}\n\nexport {\n Sheet,\n SheetTrigger,\n SheetClose,\n SheetContent,\n SheetHeader,\n SheetFooter,\n SheetTitle,\n SheetDescription,\n}\n",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Dialog as SheetPrimitive } from \"@base-ui/react/dialog\"\n\nimport { cn } from \"@/registry/bases/base/lib/utils\"\nimport { Button } from \"@/registry/bases/base/ui/button\"\nimport { IconPlaceholder } from \"@/app/(create)/components/icon-placeholder\"\n\nfunction Sheet({ ...props }: SheetPrimitive.Root.Props) {\n return <SheetPrimitive.Root data-slot=\"sheet\" {...props} />\n}\n\nfunction SheetTrigger({ ...props }: SheetPrimitive.Trigger.Props) {\n return <SheetPrimitive.Trigger data-slot=\"sheet-trigger\" {...props} />\n}\n\nfunction SheetClose({ ...props }: SheetPrimitive.Close.Props) {\n return <SheetPrimitive.Close data-slot=\"sheet-close\" {...props} />\n}\n\nfunction SheetPortal({ ...props }: SheetPrimitive.Portal.Props) {\n return <SheetPrimitive.Portal data-slot=\"sheet-portal\" {...props} />\n}\n\nfunction SheetOverlay({ className, ...props }: SheetPrimitive.Backdrop.Props) {\n return (\n <SheetPrimitive.Backdrop\n data-slot=\"sheet-overlay\"\n className={cn(\"data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 bg-black/10 text-xs/relaxed duration-100 data-ending-style:opacity-0 data-starting-style:opacity-0 supports-backdrop-filter:backdrop-blur-xs fixed inset-0 z-50\", className)}\n {...props}\n />\n )\n}\n\nfunction SheetContent({\n className,\n children,\n side = \"right\",\n showCloseButton = true,\n ...props\n}: SheetPrimitive.Popup.Props & {\n side?: \"top\" | \"right\" | \"bottom\" | \"left\"\n showCloseButton?: boolean\n}) {\n return (\n <SheetPortal>\n <SheetOverlay />\n <SheetPrimitive.Popup\n data-slot=\"sheet-content\"\n data-side={side}\n className={cn(\"bg-background data-open:animate-in data-closed:animate-out data-[side=right]:data-closed:slide-out-to-right-10 data-[side=right]:data-open:slide-in-from-right-10 data-[side=left]:data-closed:slide-out-to-left-10 data-[side=left]:data-open:slide-in-from-left-10 data-[side=top]:data-closed:slide-out-to-top-10 data-[side=top]:data-open:slide-in-from-top-10 data-closed:fade-out-0 data-open:fade-in-0 data-[side=bottom]:data-closed:slide-out-to-bottom-10 data-[side=bottom]:data-open:slide-in-from-bottom-10 fixed z-50 flex flex-col bg-clip-padding text-xs/relaxed shadow-lg transition duration-200 ease-in-out data-[side=bottom]:inset-x-0 data-[side=bottom]:bottom-0 data-[side=bottom]:h-auto data-[side=bottom]:border-t data-[side=left]:inset-y-0 data-[side=left]:left-0 data-[side=left]:h-full data-[side=left]:w-3/4 data-[side=left]:border-r data-[side=right]:inset-y-0 data-[side=right]:right-0 data-[side=right]:h-full data-[side=right]:w-3/4 data-[side=right]:border-l data-[side=top]:inset-x-0 data-[side=top]:top-0 data-[side=top]:h-auto data-[side=top]:border-b data-[side=left]:sm:max-w-sm data-[side=right]:sm:max-w-sm\", className)}\n {...props}\n >\n {children}\n {showCloseButton && (\n <SheetPrimitive.Close\n data-slot=\"sheet-close\"\n render={\n <Button\n variant=\"ghost\"\n className=\"absolute top-3 right-3\"\n size=\"icon-sm\"\n />\n }\n >\n <IconPlaceholder\n lucide=\"XIcon\"\n tabler=\"IconX\"\n hugeicons=\"Cancel01Icon\"\n phosphor=\"XIcon\"\n />\n <span className=\"sr-only\">Close</span>\n </SheetPrimitive.Close>\n )}\n </SheetPrimitive.Popup>\n </SheetPortal>\n )\n}\n\nfunction SheetHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"sheet-header\"\n className={cn(\"gap-0.5 p-4 flex flex-col\", className)}\n {...props}\n />\n )\n}\n\nfunction SheetFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"sheet-footer\"\n className={cn(\"gap-2 p-4 mt-auto flex flex-col\", className)}\n {...props}\n />\n )\n}\n\nfunction SheetTitle({ className, ...props }: SheetPrimitive.Title.Props) {\n return (\n <SheetPrimitive.Title\n data-slot=\"sheet-title\"\n className={cn(\"text-foreground text-sm font-medium\", className)}\n {...props}\n />\n )\n}\n\nfunction SheetDescription({\n className,\n ...props\n}: SheetPrimitive.Description.Props) {\n return (\n <SheetPrimitive.Description\n data-slot=\"sheet-description\"\n className={cn(\"text-muted-foreground text-xs/relaxed\", className)}\n {...props}\n />\n )\n}\n\nexport {\n Sheet,\n SheetTrigger,\n SheetClose,\n SheetContent,\n SheetHeader,\n SheetFooter,\n SheetTitle,\n SheetDescription,\n}\n",
"type": "registry:ui"
}
],

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -8,7 +8,7 @@
"files": [
{
"path": "registry/base-lyra/ui/sonner.tsx",
"content": "\"use client\"\n\nimport { useTheme } from \"next-themes\"\nimport { Toaster as Sonner, type ToasterProps } from \"sonner\"\n\nimport { IconPlaceholder } from \"@/app/(create)/components/icon-placeholder\"\n\nconst Toaster = ({ ...props }: ToasterProps) => {\n const { theme = \"system\" } = useTheme()\n\n return (\n <Sonner\n theme={theme as ToasterProps[\"theme\"]}\n className=\"toaster group\"\n icons={{\n success: (\n <IconPlaceholder\n lucide=\"CircleCheckIcon\"\n tabler=\"IconCircleCheck\"\n hugeicons=\"CheckmarkCircle02Icon\"\n className=\"size-4\"\n />\n ),\n info: (\n <IconPlaceholder\n lucide=\"InfoIcon\"\n tabler=\"IconInfoCircle\"\n hugeicons=\"InformationCircleIcon\"\n className=\"size-4\"\n />\n ),\n warning: (\n <IconPlaceholder\n lucide=\"TriangleAlertIcon\"\n tabler=\"IconAlertTriangle\"\n hugeicons=\"Alert02Icon\"\n className=\"size-4\"\n />\n ),\n error: (\n <IconPlaceholder\n lucide=\"OctagonXIcon\"\n tabler=\"IconAlertOctagon\"\n hugeicons=\"MultiplicationSignCircleIcon\"\n className=\"size-4\"\n />\n ),\n loading: (\n <IconPlaceholder\n lucide=\"Loader2Icon\"\n tabler=\"IconLoader\"\n hugeicons=\"Loading03Icon\"\n className=\"size-4 animate-spin\"\n />\n ),\n }}\n style={\n {\n \"--normal-bg\": \"var(--popover)\",\n \"--normal-text\": \"var(--popover-foreground)\",\n \"--normal-border\": \"var(--border)\",\n \"--border-radius\": \"var(--radius)\",\n } as React.CSSProperties\n }\n toastOptions={{\n classNames: {\n toast: \"cn-toast\",\n },\n }}\n {...props}\n />\n )\n}\n\nexport { Toaster }\n",
"content": "\"use client\"\n\nimport { useTheme } from \"next-themes\"\nimport { Toaster as Sonner, type ToasterProps } from \"sonner\"\n\nimport { IconPlaceholder } from \"@/app/(create)/components/icon-placeholder\"\n\nconst Toaster = ({ ...props }: ToasterProps) => {\n const { theme = \"system\" } = useTheme()\n\n return (\n <Sonner\n theme={theme as ToasterProps[\"theme\"]}\n className=\"toaster group\"\n icons={{\n success: (\n <IconPlaceholder\n lucide=\"CircleCheckIcon\"\n tabler=\"IconCircleCheck\"\n hugeicons=\"CheckmarkCircle02Icon\"\n phosphor=\"CheckCircleIcon\"\n className=\"size-4\"\n />\n ),\n info: (\n <IconPlaceholder\n lucide=\"InfoIcon\"\n tabler=\"IconInfoCircle\"\n hugeicons=\"InformationCircleIcon\"\n phosphor=\"InfoIcon\"\n className=\"size-4\"\n />\n ),\n warning: (\n <IconPlaceholder\n lucide=\"TriangleAlertIcon\"\n tabler=\"IconAlertTriangle\"\n hugeicons=\"Alert02Icon\"\n phosphor=\"WarningIcon\"\n className=\"size-4\"\n />\n ),\n error: (\n <IconPlaceholder\n lucide=\"OctagonXIcon\"\n tabler=\"IconAlertOctagon\"\n hugeicons=\"MultiplicationSignCircleIcon\"\n phosphor=\"XCircleIcon\"\n className=\"size-4\"\n />\n ),\n loading: (\n <IconPlaceholder\n lucide=\"Loader2Icon\"\n tabler=\"IconLoader\"\n hugeicons=\"Loading03Icon\"\n phosphor=\"SpinnerIcon\"\n className=\"size-4 animate-spin\"\n />\n ),\n }}\n style={\n {\n \"--normal-bg\": \"var(--popover)\",\n \"--normal-text\": \"var(--popover-foreground)\",\n \"--normal-border\": \"var(--border)\",\n \"--border-radius\": \"var(--radius)\",\n } as React.CSSProperties\n }\n toastOptions={{\n classNames: {\n toast: \"cn-toast\",\n },\n }}\n {...props}\n />\n )\n}\n\nexport { Toaster }\n",
"type": "registry:ui"
}
],

View File

@@ -14,7 +14,7 @@
"files": [
{
"path": "registry/base-lyra/examples/spinner-example.tsx",
"content": "import {\n Example,\n ExampleWrapper,\n} from \"@/registry/bases/base/components/example\"\nimport { Badge } from \"@/registry/bases/base/ui/badge\"\nimport { Button } from \"@/registry/bases/base/ui/button\"\nimport {\n Empty,\n EmptyContent,\n EmptyDescription,\n EmptyHeader,\n EmptyMedia,\n EmptyTitle,\n} from \"@/registry/bases/base/ui/empty\"\nimport { Field, FieldLabel } from \"@/registry/bases/base/ui/field\"\nimport {\n InputGroup,\n InputGroupAddon,\n InputGroupInput,\n} from \"@/registry/bases/base/ui/input-group\"\nimport { Spinner } from \"@/registry/bases/base/ui/spinner\"\nimport { IconPlaceholder } from \"@/app/(create)/components/icon-placeholder\"\n\nexport default function SpinnerExample() {\n return (\n <ExampleWrapper>\n <SpinnerBasic />\n <SpinnerInButtons />\n <SpinnerInBadges />\n <SpinnerInInputGroup />\n <SpinnerInEmpty />\n </ExampleWrapper>\n )\n}\n\nfunction SpinnerBasic() {\n return (\n <Example title=\"Basic\">\n <div className=\"flex items-center gap-6\">\n <Spinner />\n <Spinner className=\"size-6\" />\n </div>\n </Example>\n )\n}\n\nfunction SpinnerInButtons() {\n return (\n <Example title=\"In Buttons\">\n <div className=\"flex flex-wrap items-center gap-4\">\n <Button>\n <Spinner data-icon=\"inline-start\" /> Submit\n </Button>\n <Button disabled>\n <Spinner data-icon=\"inline-start\" /> Disabled\n </Button>\n <Button variant=\"outline\" disabled>\n <Spinner data-icon=\"inline-start\" /> Outline\n </Button>\n <Button variant=\"outline\" size=\"icon\" disabled>\n <Spinner data-icon=\"inline-start\" />\n <span className=\"sr-only\">Loading...</span>\n </Button>\n </div>\n </Example>\n )\n}\n\nfunction SpinnerInBadges() {\n return (\n <Example title=\"In Badges\" className=\"items-center justify-center\">\n <div className=\"flex flex-wrap items-center justify-center gap-4\">\n <Badge>\n <Spinner data-icon=\"inline-start\" />\n Badge\n </Badge>\n <Badge variant=\"secondary\">\n <Spinner data-icon=\"inline-start\" />\n Badge\n </Badge>\n <Badge variant=\"destructive\">\n <Spinner data-icon=\"inline-start\" />\n Badge\n </Badge>\n <Badge variant=\"outline\">\n <Spinner data-icon=\"inline-start\" />\n Badge\n </Badge>\n </div>\n </Example>\n )\n}\n\nfunction SpinnerInInputGroup() {\n return (\n <Example title=\"In Input Group\">\n <Field>\n <FieldLabel htmlFor=\"input-group-spinner\">Input Group</FieldLabel>\n <InputGroup>\n <InputGroupInput id=\"input-group-spinner\" />\n <InputGroupAddon>\n <Spinner />\n </InputGroupAddon>\n </InputGroup>\n </Field>\n </Example>\n )\n}\n\nfunction SpinnerInEmpty() {\n return (\n <Example title=\"In Empty State\" containerClassName=\"lg:col-span-full\">\n <Empty className=\"min-h-[300px]\">\n <EmptyHeader>\n <EmptyMedia variant=\"icon\">\n <Spinner />\n </EmptyMedia>\n <EmptyTitle>No projects yet</EmptyTitle>\n <EmptyDescription>\n You haven&apos;t created any projects yet. Get started by creating\n your first project.\n </EmptyDescription>\n </EmptyHeader>\n <EmptyContent>\n <div className=\"flex gap-2\">\n <Button render={<a href=\"#\" />} nativeButton={false}>\n Create project\n </Button>\n <Button variant=\"outline\">Import project</Button>\n </div>\n <Button\n variant=\"link\"\n render={<a href=\"#\" />}\n nativeButton={false}\n className=\"text-muted-foreground\"\n >\n Learn more{\" \"}\n <IconPlaceholder\n lucide=\"ArrowRightIcon\"\n tabler=\"IconArrowRight\"\n hugeicons=\"ArrowRight02Icon\"\n />\n </Button>\n </EmptyContent>\n </Empty>\n </Example>\n )\n}\n",
"content": "import {\n Example,\n ExampleWrapper,\n} from \"@/registry/bases/base/components/example\"\nimport { Badge } from \"@/registry/bases/base/ui/badge\"\nimport { Button } from \"@/registry/bases/base/ui/button\"\nimport {\n Empty,\n EmptyContent,\n EmptyDescription,\n EmptyHeader,\n EmptyMedia,\n EmptyTitle,\n} from \"@/registry/bases/base/ui/empty\"\nimport { Field, FieldLabel } from \"@/registry/bases/base/ui/field\"\nimport {\n InputGroup,\n InputGroupAddon,\n InputGroupInput,\n} from \"@/registry/bases/base/ui/input-group\"\nimport { Spinner } from \"@/registry/bases/base/ui/spinner\"\nimport { IconPlaceholder } from \"@/app/(create)/components/icon-placeholder\"\n\nexport default function SpinnerExample() {\n return (\n <ExampleWrapper>\n <SpinnerBasic />\n <SpinnerInButtons />\n <SpinnerInBadges />\n <SpinnerInInputGroup />\n <SpinnerInEmpty />\n </ExampleWrapper>\n )\n}\n\nfunction SpinnerBasic() {\n return (\n <Example title=\"Basic\">\n <div className=\"flex items-center gap-6\">\n <Spinner />\n <Spinner className=\"size-6\" />\n </div>\n </Example>\n )\n}\n\nfunction SpinnerInButtons() {\n return (\n <Example title=\"In Buttons\">\n <div className=\"flex flex-wrap items-center gap-4\">\n <Button>\n <Spinner data-icon=\"inline-start\" /> Submit\n </Button>\n <Button disabled>\n <Spinner data-icon=\"inline-start\" /> Disabled\n </Button>\n <Button variant=\"outline\" disabled>\n <Spinner data-icon=\"inline-start\" /> Outline\n </Button>\n <Button variant=\"outline\" size=\"icon\" disabled>\n <Spinner data-icon=\"inline-start\" />\n <span className=\"sr-only\">Loading...</span>\n </Button>\n </div>\n </Example>\n )\n}\n\nfunction SpinnerInBadges() {\n return (\n <Example title=\"In Badges\" className=\"items-center justify-center\">\n <div className=\"flex flex-wrap items-center justify-center gap-4\">\n <Badge>\n <Spinner data-icon=\"inline-start\" />\n Badge\n </Badge>\n <Badge variant=\"secondary\">\n <Spinner data-icon=\"inline-start\" />\n Badge\n </Badge>\n <Badge variant=\"destructive\">\n <Spinner data-icon=\"inline-start\" />\n Badge\n </Badge>\n <Badge variant=\"outline\">\n <Spinner data-icon=\"inline-start\" />\n Badge\n </Badge>\n </div>\n </Example>\n )\n}\n\nfunction SpinnerInInputGroup() {\n return (\n <Example title=\"In Input Group\">\n <Field>\n <FieldLabel htmlFor=\"input-group-spinner\">Input Group</FieldLabel>\n <InputGroup>\n <InputGroupInput id=\"input-group-spinner\" />\n <InputGroupAddon>\n <Spinner />\n </InputGroupAddon>\n </InputGroup>\n </Field>\n </Example>\n )\n}\n\nfunction SpinnerInEmpty() {\n return (\n <Example title=\"In Empty State\" containerClassName=\"lg:col-span-full\">\n <Empty className=\"min-h-[300px]\">\n <EmptyHeader>\n <EmptyMedia variant=\"icon\">\n <Spinner />\n </EmptyMedia>\n <EmptyTitle>No projects yet</EmptyTitle>\n <EmptyDescription>\n You haven&apos;t created any projects yet. Get started by creating\n your first project.\n </EmptyDescription>\n </EmptyHeader>\n <EmptyContent>\n <div className=\"flex gap-2\">\n <Button render={<a href=\"#\" />} nativeButton={false}>\n Create project\n </Button>\n <Button variant=\"outline\">Import project</Button>\n </div>\n <Button\n variant=\"link\"\n render={<a href=\"#\" />}\n nativeButton={false}\n className=\"text-muted-foreground\"\n >\n Learn more{\" \"}\n <IconPlaceholder\n lucide=\"ArrowRightIcon\"\n tabler=\"IconArrowRight\"\n hugeicons=\"ArrowRight02Icon\"\n phosphor=\"ArrowRightIcon\"\n />\n </Button>\n </EmptyContent>\n </Empty>\n </Example>\n )\n}\n",
"type": "registry:example"
}
],

View File

@@ -4,7 +4,7 @@
"files": [
{
"path": "registry/base-lyra/ui/spinner.tsx",
"content": "import { cn } from \"@/registry/bases/base/lib/utils\"\nimport { IconPlaceholder } from \"@/app/(create)/components/icon-placeholder\"\n\nfunction Spinner({ className, ...props }: React.ComponentProps<\"svg\">) {\n return (\n <IconPlaceholder\n lucide=\"Loader2Icon\"\n tabler=\"IconLoader\"\n hugeicons=\"Loading03Icon\"\n role=\"status\"\n aria-label=\"Loading\"\n className={cn(\"size-4 animate-spin\", className)}\n {...props}\n />\n )\n}\n\nexport { Spinner }\n",
"content": "import { cn } from \"@/registry/bases/base/lib/utils\"\nimport { IconPlaceholder } from \"@/app/(create)/components/icon-placeholder\"\n\nfunction Spinner({ className, ...props }: React.ComponentProps<\"svg\">) {\n return (\n <IconPlaceholder\n lucide=\"Loader2Icon\"\n tabler=\"IconLoader\"\n hugeicons=\"Loading03Icon\"\n phosphor=\"SpinnerIcon\"\n role=\"status\"\n aria-label=\"Loading\"\n className={cn(\"size-4 animate-spin\", className)}\n {...props}\n />\n )\n}\n\nexport { Spinner }\n",
"type": "registry:ui"
}
],

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -11,7 +11,7 @@
"files": [
{
"path": "registry/base-lyra/examples/tooltip-example.tsx",
"content": "\"use client\"\n\nimport {\n Example,\n ExampleWrapper,\n} from \"@/registry/bases/base/components/example\"\nimport { Button } from \"@/registry/bases/base/ui/button\"\nimport { Kbd } from \"@/registry/bases/base/ui/kbd\"\nimport {\n Tooltip,\n TooltipContent,\n TooltipTrigger,\n} from \"@/registry/bases/base/ui/tooltip\"\nimport { IconPlaceholder } from \"@/app/(create)/components/icon-placeholder\"\n\nexport default function TooltipExample() {\n return (\n <ExampleWrapper>\n <TooltipBasic />\n <TooltipSides />\n <TooltipWithIcon />\n <TooltipLongContent />\n <TooltipDisabled />\n <TooltipWithKeyboard />\n <TooltipOnLink />\n <TooltipFormatted />\n </ExampleWrapper>\n )\n}\n\nfunction TooltipBasic() {\n return (\n <Example title=\"Basic\">\n <Tooltip>\n <TooltipTrigger render={<Button variant=\"outline\" className=\"w-fit\" />}>\n Show Tooltip\n </TooltipTrigger>\n <TooltipContent>\n <p>Add to library</p>\n </TooltipContent>\n </Tooltip>\n </Example>\n )\n}\n\nfunction TooltipSides() {\n return (\n <Example title=\"Sides\">\n <div className=\"flex flex-wrap gap-2\">\n {([\"top\", \"right\", \"bottom\", \"left\"] as const).map((side) => (\n <Tooltip key={side}>\n <TooltipTrigger\n render={<Button variant=\"outline\" className=\"w-fit capitalize\" />}\n >\n {side}\n </TooltipTrigger>\n <TooltipContent side={side}>\n <p>Add to library</p>\n </TooltipContent>\n </Tooltip>\n ))}\n </div>\n </Example>\n )\n}\n\nfunction TooltipWithIcon() {\n return (\n <Example title=\"With Icon\">\n <Tooltip>\n <TooltipTrigger render={<Button variant=\"ghost\" size=\"icon\" />}>\n <IconPlaceholder\n lucide=\"InfoIcon\"\n tabler=\"IconInfoCircle\"\n hugeicons=\"AlertCircleIcon\"\n />\n <span className=\"sr-only\">Info</span>\n </TooltipTrigger>\n <TooltipContent>\n <p>Additional information</p>\n </TooltipContent>\n </Tooltip>\n </Example>\n )\n}\n\nfunction TooltipLongContent() {\n return (\n <Example title=\"Long Content\">\n <Tooltip>\n <TooltipTrigger render={<Button variant=\"outline\" className=\"w-fit\" />}>\n Show Tooltip\n </TooltipTrigger>\n <TooltipContent>\n To learn more about how this works, check out the docs. If you have\n any questions, please reach out to us.\n </TooltipContent>\n </Tooltip>\n </Example>\n )\n}\n\nfunction TooltipDisabled() {\n return (\n <Example title=\"Disabled\">\n <Tooltip>\n <TooltipTrigger render={<span className=\"inline-block w-fit\" />}>\n <Button variant=\"outline\" disabled>\n Disabled\n </Button>\n </TooltipTrigger>\n <TooltipContent>\n <p>This feature is currently unavailable</p>\n </TooltipContent>\n </Tooltip>\n </Example>\n )\n}\n\nfunction TooltipWithKeyboard() {\n return (\n <Example title=\"With Keyboard Shortcut\">\n <Tooltip>\n <TooltipTrigger render={<Button variant=\"outline\" size=\"icon-sm\" />}>\n <IconPlaceholder\n lucide=\"SaveIcon\"\n tabler=\"IconDeviceFloppy\"\n hugeicons=\"FloppyDiskIcon\"\n />\n </TooltipTrigger>\n <TooltipContent className=\"pr-1.5\">\n <div className=\"flex items-center gap-2\">\n Save Changes <Kbd>S</Kbd>\n </div>\n </TooltipContent>\n </Tooltip>\n </Example>\n )\n}\n\nfunction TooltipOnLink() {\n return (\n <Example title=\"On Link\">\n <Tooltip>\n <TooltipTrigger\n render={\n <a\n href=\"#\"\n className=\"text-primary w-fit text-sm underline-offset-4 hover:underline\"\n onClick={(e) => e.preventDefault()}\n />\n }\n >\n Learn more\n </TooltipTrigger>\n <TooltipContent>\n <p>Click to read the documentation</p>\n </TooltipContent>\n </Tooltip>\n </Example>\n )\n}\n\nfunction TooltipFormatted() {\n return (\n <Example title=\"Formatted Content\">\n <Tooltip>\n <TooltipTrigger render={<Button variant=\"outline\" className=\"w-fit\" />}>\n Status\n </TooltipTrigger>\n <TooltipContent>\n <div className=\"flex flex-col gap-1\">\n <p className=\"font-semibold\">Active</p>\n <p className=\"text-xs opacity-80\">Last updated 2 hours ago</p>\n </div>\n </TooltipContent>\n </Tooltip>\n </Example>\n )\n}\n",
"content": "\"use client\"\n\nimport {\n Example,\n ExampleWrapper,\n} from \"@/registry/bases/base/components/example\"\nimport { Button } from \"@/registry/bases/base/ui/button\"\nimport { Kbd } from \"@/registry/bases/base/ui/kbd\"\nimport {\n Tooltip,\n TooltipContent,\n TooltipTrigger,\n} from \"@/registry/bases/base/ui/tooltip\"\nimport { IconPlaceholder } from \"@/app/(create)/components/icon-placeholder\"\n\nexport default function TooltipExample() {\n return (\n <ExampleWrapper>\n <TooltipBasic />\n <TooltipSides />\n <TooltipWithIcon />\n <TooltipLongContent />\n <TooltipDisabled />\n <TooltipWithKeyboard />\n <TooltipOnLink />\n <TooltipFormatted />\n </ExampleWrapper>\n )\n}\n\nfunction TooltipBasic() {\n return (\n <Example title=\"Basic\">\n <Tooltip>\n <TooltipTrigger render={<Button variant=\"outline\" className=\"w-fit\" />}>\n Show Tooltip\n </TooltipTrigger>\n <TooltipContent>\n <p>Add to library</p>\n </TooltipContent>\n </Tooltip>\n </Example>\n )\n}\n\nfunction TooltipSides() {\n return (\n <Example title=\"Sides\">\n <div className=\"flex flex-wrap gap-2\">\n {([\"top\", \"right\", \"bottom\", \"left\"] as const).map((side) => (\n <Tooltip key={side}>\n <TooltipTrigger\n render={<Button variant=\"outline\" className=\"w-fit capitalize\" />}\n >\n {side}\n </TooltipTrigger>\n <TooltipContent side={side}>\n <p>Add to library</p>\n </TooltipContent>\n </Tooltip>\n ))}\n </div>\n </Example>\n )\n}\n\nfunction TooltipWithIcon() {\n return (\n <Example title=\"With Icon\">\n <Tooltip>\n <TooltipTrigger render={<Button variant=\"ghost\" size=\"icon\" />}>\n <IconPlaceholder\n lucide=\"InfoIcon\"\n tabler=\"IconInfoCircle\"\n hugeicons=\"AlertCircleIcon\"\n phosphor=\"InfoIcon\"\n />\n <span className=\"sr-only\">Info</span>\n </TooltipTrigger>\n <TooltipContent>\n <p>Additional information</p>\n </TooltipContent>\n </Tooltip>\n </Example>\n )\n}\n\nfunction TooltipLongContent() {\n return (\n <Example title=\"Long Content\">\n <Tooltip>\n <TooltipTrigger render={<Button variant=\"outline\" className=\"w-fit\" />}>\n Show Tooltip\n </TooltipTrigger>\n <TooltipContent>\n To learn more about how this works, check out the docs. If you have\n any questions, please reach out to us.\n </TooltipContent>\n </Tooltip>\n </Example>\n )\n}\n\nfunction TooltipDisabled() {\n return (\n <Example title=\"Disabled\">\n <Tooltip>\n <TooltipTrigger render={<span className=\"inline-block w-fit\" />}>\n <Button variant=\"outline\" disabled>\n Disabled\n </Button>\n </TooltipTrigger>\n <TooltipContent>\n <p>This feature is currently unavailable</p>\n </TooltipContent>\n </Tooltip>\n </Example>\n )\n}\n\nfunction TooltipWithKeyboard() {\n return (\n <Example title=\"With Keyboard Shortcut\">\n <Tooltip>\n <TooltipTrigger render={<Button variant=\"outline\" size=\"icon-sm\" />}>\n <IconPlaceholder\n lucide=\"SaveIcon\"\n tabler=\"IconDeviceFloppy\"\n hugeicons=\"FloppyDiskIcon\"\n phosphor=\"FloppyDiskIcon\"\n />\n </TooltipTrigger>\n <TooltipContent className=\"pr-1.5\">\n <div className=\"flex items-center gap-2\">\n Save Changes <Kbd>S</Kbd>\n </div>\n </TooltipContent>\n </Tooltip>\n </Example>\n )\n}\n\nfunction TooltipOnLink() {\n return (\n <Example title=\"On Link\">\n <Tooltip>\n <TooltipTrigger\n render={\n <a\n href=\"#\"\n className=\"text-primary w-fit text-sm underline-offset-4 hover:underline\"\n onClick={(e) => e.preventDefault()}\n />\n }\n >\n Learn more\n </TooltipTrigger>\n <TooltipContent>\n <p>Click to read the documentation</p>\n </TooltipContent>\n </Tooltip>\n </Example>\n )\n}\n\nfunction TooltipFormatted() {\n return (\n <Example title=\"Formatted Content\">\n <Tooltip>\n <TooltipTrigger render={<Button variant=\"outline\" className=\"w-fit\" />}>\n Status\n </TooltipTrigger>\n <TooltipContent>\n <div className=\"flex flex-col gap-1\">\n <p className=\"font-semibold\">Active</p>\n <p className=\"text-xs opacity-80\">Last updated 2 hours ago</p>\n </div>\n </TooltipContent>\n </Tooltip>\n </Example>\n )\n}\n",
"type": "registry:example"
}
],

View File

@@ -4,7 +4,7 @@
"files": [
{
"path": "registry/base-lyra/hooks/use-mobile.ts",
"content": "import * as React from \"react\"\n\nconst MOBILE_BREAKPOINT = 768\n\nexport function useIsMobile() {\n const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)\n\n React.useEffect(() => {\n const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)\n const onChange = () => {\n setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)\n }\n mql.addEventListener(\"change\", onChange)\n setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)\n return () => mql.removeEventListener(\"change\", onChange)\n }, [])\n\n return !!isMobile\n}\n\n",
"content": "import * as React from \"react\"\n\nconst MOBILE_BREAKPOINT = 768\n\nexport function useIsMobile() {\n const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)\n\n React.useEffect(() => {\n const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)\n const onChange = () => {\n setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)\n }\n mql.addEventListener(\"change\", onChange)\n setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)\n return () => mql.removeEventListener(\"change\", onChange)\n }, [])\n\n return !!isMobile\n}\n",
"type": "registry:hook"
}
],

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More