mirror of
https://github.com/shadcn-ui/ui.git
synced 2026-06-12 10:21:39 +00:00
Compare commits
36 Commits
shadcn@3.6
...
fix/cli-va
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cbf7b3708a | ||
|
|
9da12c13f9 | ||
|
|
ccafdaf7c6 | ||
|
|
f0d147d581 | ||
|
|
df67e49aac | ||
|
|
c0de90e1a1 | ||
|
|
0447708efa | ||
|
|
4a470fc617 | ||
|
|
137b1c12b7 | ||
|
|
73296e79c0 | ||
|
|
78e5fa2a39 | ||
|
|
9cf47dd4a3 | ||
|
|
f53400f934 | ||
|
|
b3d6f872db | ||
|
|
2aa5e11f6f | ||
|
|
058ebc5acd | ||
|
|
a60683dea5 | ||
|
|
1dc1b8dbfb | ||
|
|
c6273cca03 | ||
|
|
b15d7e8221 | ||
|
|
46e3c26a6e | ||
|
|
f36e25f703 | ||
|
|
55f5d1c7cc | ||
|
|
db19605996 | ||
|
|
40012adb14 | ||
|
|
ad8104e473 | ||
|
|
5fb0c4d19a | ||
|
|
31c86f9fd5 | ||
|
|
aad175ff87 | ||
|
|
081c91c461 | ||
|
|
7dbf3688fb | ||
|
|
99ad18b389 | ||
|
|
fabb886de9 | ||
|
|
4b561cf050 | ||
|
|
0c2373f592 | ||
|
|
ff42c27d41 |
5
.changeset/rude-clowns-retire.md
Normal file
5
.changeset/rude-clowns-retire.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"shadcn": patch
|
||||
---
|
||||
|
||||
validate app name on create
|
||||
13
.github/workflows/release.yml
vendored
13
.github/workflows/release.yml
vendored
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}}
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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])
|
||||
|
||||
|
||||
@@ -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])
|
||||
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}}
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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" />
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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])
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
"*"
|
||||
)
|
||||
|
||||
@@ -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
|
||||
>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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 }) {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -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
@@ -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
@@ -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"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -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'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'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"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -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
@@ -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"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user