astralyx-ui 0.14.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/registry/index.json +1 -1
- package/registry/items/tabs.json +1 -1
package/package.json
CHANGED
package/registry/index.json
CHANGED
package/registry/items/tabs.json
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
{
|
|
18
18
|
"path": "components/ui/tabs.tsx",
|
|
19
19
|
"type": "registry:ui",
|
|
20
|
-
"content": "import type { ComponentProps } from 'react'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { TabsProvider, tabIds, useTabs } from '@/components/primitives/tabs'\nimport { enterFade } from '@/lib/motion'\nimport { focusRing, radius } from '@/lib/styles'\nimport { cn } from '@/lib/utils'\n\nconst listVariants = cva('flex', {\n variants: {\n variant: {\n /** Segmented control: a filled track with the active tab lifted out. */\n solid: 'bg-muted gap-0.5 p-0.5',\n /** A rule with the active tab underlined. */\n underline: 'border-border gap-4 border-b',\n },\n orientation: {\n horizontal: 'flex-row items-center',\n vertical: 'flex-col items-stretch',\n },\n },\n compoundVariants: [\n { variant: 'underline', orientation: 'vertical', class: 'border-b-0 border-r' },\n ],\n defaultVariants: { variant: 'solid', orientation: 'horizontal' },\n})\n\nconst triggerVariants = cva(\n [\n 'inline-flex cursor-pointer items-center justify-center gap-2 whitespace-nowrap',\n 'text-sm font-medium',\n 'transition-colors duration-150 ease-out motion-reduce:transition-none',\n 'disabled:pointer-events-none disabled:opacity-50',\n focusRing,\n ].join(' '),\n {\n variants: {\n variant: {\n solid: cn(\n radius.control,\n 'px-3 py-1.5',\n 'text-muted-foreground hover:text-foreground',\n 'data-[state=active]:bg-background data-[state=active]:text-foreground',\n ),\n underline: cn(\n 'border-b-2 border-transparent px-1 py-2',\n 'text-muted-foreground hover:text-foreground',\n 'data-[state=active]:border-foreground data-[state=active]:text-foreground',\n ),\n },\n },\n defaultVariants: { variant: 'solid' },\n },\n)\n\ntype TabsProps = Omit<ComponentProps<'div'>, 'onChange'> & {\n value?: string\n defaultValue?: string\n onValueChange?: (value: string) => void\n orientation?: 'horizontal' | 'vertical'\n activationMode?: 'automatic' | 'manual'\n}\n\nfunction Tabs({\n className,\n value,\n defaultValue,\n onValueChange,\n orientation = 'horizontal',\n activationMode = 'automatic',\n ...props\n}: TabsProps) {\n return (\n <TabsProvider\n value={value}\n defaultValue={defaultValue}\n onValueChange={onValueChange}\n orientation={orientation}\n activationMode={activationMode}\n >\n <div\n data-slot=\"tabs\"\n className={cn(\n 'flex gap-3',\n orientation === 'vertical' ? 'flex-row' : 'flex-col',\n className,\n )}\n {...props}\n />\n </TabsProvider>\n )\n}\n\nfunction TabsList({\n className,\n variant,\n ...props\n}: ComponentProps<'div'> & VariantProps<typeof listVariants>) {\n const { orientation, onListKeyDown } = useTabs()\n\n return (\n <div\n role=\"tablist\"\n data-slot=\"tabs-list\"\n aria-orientation={orientation}\n onKeyDown={onListKeyDown}\n className={cn(\n listVariants({ variant, orientation }),\n variant === 'underline' ? '' : radius.control,\n 'w-fit',\n className,\n )}\n {...props}\n />\n )\n}\n\nfunction TabsTrigger({\n className,\n variant,\n value,\n ...props\n}: Omit<ComponentProps<'button'>, 'value'> &\n VariantProps<typeof triggerVariants> & { value: string }) {\n const { value: selected, select, baseId } = useTabs()\n const active = selected === value\n const ids = tabIds(baseId, value)\n\n return (\n <button\n type=\"button\"\n role=\"tab\"\n id={ids.trigger}\n data-slot=\"tabs-trigger\"\n data-value={value}\n data-state={active ? 'active' : 'inactive'}\n aria-selected={active}\n // Only the active panel is mounted, so only the active trigger can point\n // at one. Setting it unconditionally leaves every inactive tab with a\n // dangling reference to an element that is not in the document.\n aria-controls={active ? ids.panel : undefined}\n // Only the active tab is in the tab order; arrows move between the rest.\n tabIndex={active ? 0 : -1}\n onClick={() => select(value)}\n className={cn(triggerVariants({ variant }), className)}\n {...props}\n />\n )\n}\n\n/**\n * A panel.\n *\n * Unmounted when it is not selected, which is usually what you want: a panel\n * that keeps a video playing or a subscription open behind another tab is a\n * bug, and a fresh mount is the cheapest way to guarantee it cannot happen.\n *\n * `keepMounted` is for the case where the content has to exist whether or not\n * anyone is looking at it — the one that prompted this was documentation, where\n * the source behind a Code tab is the page's actual content and unmounting it\n * meant it never reached the HTML a crawler reads. Hidden, not removed: the\n * panel keeps its markup, and `hidden` takes it out of the view and out of the\n * accessibility tree just as unmounting did.\n */\nfunction TabsContent({\n className,\n value,\n keepMounted = false,\n ...props\n}: ComponentProps<'div'> & { value: string; keepMounted?: boolean }) {\n const { value: selected, baseId } = useTabs()\n const active = selected === value\n if (!active && !keepMounted) return null\n\n const ids = tabIds(baseId, value)\n\n return (\n <div\n role=\"tabpanel\"\n id={ids.panel}\n data-slot=\"tabs-content\"\n aria-labelledby={ids.trigger}\n // A panel nobody can see is not a tab stop.\n tabIndex={active ? 0 : -1}\n hidden={!active}\n className={cn(active && enterFade, 'outline-none', className)}\n {...props}\n />\n )\n}\n\nexport { Tabs, TabsContent, TabsList, TabsTrigger, listVariants, triggerVariants }\nexport type { TabsProps }\n"
|
|
20
|
+
"content": "import { createContext, use, type ComponentProps } from 'react'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { TabsProvider, tabIds, useTabs } from '@/components/primitives/tabs'\nimport { enterFade } from '@/lib/motion'\nimport { focusRing, radius } from '@/lib/styles'\nimport { cn } from '@/lib/utils'\n\nconst listVariants = cva('flex', {\n variants: {\n variant: {\n /** Segmented control: a filled track with the active tab lifted out. */\n solid: 'bg-muted gap-0.5 p-0.5',\n /** A rule with the active tab underlined. */\n underline: 'border-border gap-4 border-b',\n /**\n * A browser's tab strip: the active tab is the panel, pulled upwards.\n *\n * The strip is the recessed ground and the tabs sit flush on its bottom\n * edge, so the active one runs straight into the content below it with\n * nothing between them.\n */\n browser: 'bg-muted gap-1 px-1.5 pt-1.5',\n },\n orientation: {\n horizontal: 'flex-row items-center',\n vertical: 'flex-col items-stretch',\n },\n },\n compoundVariants: [\n { variant: 'underline', orientation: 'vertical', class: 'border-b-0 border-r' },\n ],\n defaultVariants: { variant: 'solid', orientation: 'horizontal' },\n})\n\nconst triggerVariants = cva(\n [\n 'inline-flex cursor-pointer items-center justify-center gap-2 whitespace-nowrap',\n 'text-sm font-medium',\n 'transition-colors duration-150 ease-out motion-reduce:transition-none',\n 'disabled:pointer-events-none disabled:opacity-50',\n focusRing,\n ].join(' '),\n {\n variants: {\n variant: {\n solid: cn(\n radius.control,\n 'px-3 py-1.5',\n 'text-muted-foreground hover:text-foreground',\n 'data-[state=active]:bg-background data-[state=active]:text-foreground',\n ),\n underline: cn(\n 'border-b-2 border-transparent px-1 py-2',\n 'text-muted-foreground hover:text-foreground',\n 'data-[state=active]:border-foreground data-[state=active]:text-foreground',\n ),\n /*\n * The connected tab.\n *\n * No borders anywhere, which is the whole trick: the active tab and the\n * panel are the same fill, so where they meet there is nothing to draw\n * and nothing to line up. A bordered version has to hide one border\n * with another and comes apart at every zoom level.\n *\n * The two pseudo-elements are the outward flare at the bottom corners.\n * Each is a square of panel fill sitting just outside the tab, with a\n * quarter circle masked out of the corner that faces away from it —\n * which leaves the concave curve a browser tab has and a folder tab\n * does not.\n */\n browser: cn(\n // Tall enough to be the header's own height class. A browser's tab\n // fills its titlebar; at the default control padding it came out\n // shorter than the collapse control beside it and read as something\n // dropped into the bar rather than part of it.\n 'relative h-9 rounded-t-lg px-4',\n 'text-muted-foreground hover:text-foreground',\n 'data-[state=active]:bg-card data-[state=active]:text-foreground',\n 'data-[state=active]:before:bg-card data-[state=active]:after:bg-card',\n // The flares carry the same transition as the tab. Without it the\n // fill eases across while the two corners snap, and the shape looks\n // like it arrives in two pieces.\n 'before:transition-colors before:duration-150 before:ease-out',\n 'after:transition-colors after:duration-150 after:ease-out',\n 'motion-reduce:before:transition-none motion-reduce:after:transition-none',\n \"before:pointer-events-none before:absolute before:-left-2 before:bottom-0 before:size-2 before:content-['']\",\n \"after:pointer-events-none after:absolute after:-right-2 after:bottom-0 after:size-2 after:content-['']\",\n 'before:[mask-image:radial-gradient(circle_at_top_left,transparent_8px,black_8.5px)]',\n 'after:[mask-image:radial-gradient(circle_at_top_right,transparent_8px,black_8.5px)]',\n ),\n },\n },\n defaultVariants: { variant: 'solid' },\n },\n)\n\ntype TabsVariant = NonNullable<VariantProps<typeof listVariants>['variant']>\n\n/**\n * The look, shared down the tree.\n *\n * It lives here rather than in the primitive because the primitive is\n * behaviour: roving focus, activation mode, ids. Nothing about how a tab is\n * painted belongs in it.\n *\n * A variant set on the child still wins, so the older way of writing it —\n * `variant` on the list and on each trigger — keeps working. What the context\n * adds is a variant the *panel* can see, which `browser` needs: an active tab\n * that runs into its content has to know what the content looks like.\n */\nconst VariantContext = createContext<TabsVariant>('solid')\n\nfunction useTabsVariant(override: TabsVariant | null | undefined) {\n const inherited = use(VariantContext)\n return override ?? inherited\n}\n\ntype TabsProps = Omit<ComponentProps<'div'>, 'onChange'> & {\n /** Sets the look for the list, the triggers and the panel at once. */\n variant?: TabsVariant\n value?: string\n defaultValue?: string\n onValueChange?: (value: string) => void\n orientation?: 'horizontal' | 'vertical'\n activationMode?: 'automatic' | 'manual'\n}\n\nfunction Tabs({\n className,\n value,\n defaultValue,\n onValueChange,\n orientation = 'horizontal',\n activationMode = 'automatic',\n variant = 'solid',\n ...props\n}: TabsProps) {\n return (\n <TabsProvider\n value={value}\n defaultValue={defaultValue}\n onValueChange={onValueChange}\n orientation={orientation}\n activationMode={activationMode}\n >\n <VariantContext value={variant}>\n <div\n data-slot=\"tabs\"\n data-variant={variant}\n className={cn(\n 'flex',\n // The strip and the panel are one surface, so there is nothing to\n // space apart. Every other variant reads as two things.\n variant === 'browser' ? 'gap-0' : 'gap-3',\n orientation === 'vertical' ? 'flex-row' : 'flex-col',\n className,\n )}\n {...props}\n />\n </VariantContext>\n </TabsProvider>\n )\n}\n\nfunction TabsList({\n className,\n variant,\n ...props\n}: ComponentProps<'div'> & VariantProps<typeof listVariants>) {\n const { orientation, onListKeyDown } = useTabs()\n const look = useTabsVariant(variant)\n\n return (\n <div\n role=\"tablist\"\n data-slot=\"tabs-list\"\n aria-orientation={orientation}\n onKeyDown={onListKeyDown}\n className={cn(\n listVariants({ variant: look, orientation }),\n look === 'underline' ? '' : radius.control,\n // A browser strip spans its container and is only round on top; the\n // panel below finishes the shape.\n look === 'browser' ? 'w-full rounded-b-none' : 'w-fit',\n className,\n )}\n {...props}\n />\n )\n}\n\nfunction TabsTrigger({\n className,\n variant,\n value,\n ...props\n}: Omit<ComponentProps<'button'>, 'value'> &\n VariantProps<typeof triggerVariants> & { value: string }) {\n const { value: selected, select, baseId } = useTabs()\n const look = useTabsVariant(variant)\n const active = selected === value\n const ids = tabIds(baseId, value)\n\n return (\n <button\n type=\"button\"\n role=\"tab\"\n id={ids.trigger}\n data-slot=\"tabs-trigger\"\n data-value={value}\n data-state={active ? 'active' : 'inactive'}\n aria-selected={active}\n // Only the active panel is mounted, so only the active trigger can point\n // at one. Setting it unconditionally leaves every inactive tab with a\n // dangling reference to an element that is not in the document.\n aria-controls={active ? ids.panel : undefined}\n // Only the active tab is in the tab order; arrows move between the rest.\n tabIndex={active ? 0 : -1}\n onClick={() => select(value)}\n className={cn(triggerVariants({ variant: look }), className)}\n {...props}\n />\n )\n}\n\n/**\n * A panel.\n *\n * Unmounted when it is not selected, which is usually what you want: a panel\n * that keeps a video playing or a subscription open behind another tab is a\n * bug, and a fresh mount is the cheapest way to guarantee it cannot happen.\n *\n * `keepMounted` is for the case where the content has to exist whether or not\n * anyone is looking at it — the one that prompted this was documentation, where\n * the source behind a Code tab is the page's actual content and unmounting it\n * meant it never reached the HTML a crawler reads. Hidden, not removed: the\n * panel keeps its markup, and `hidden` takes it out of the view and out of the\n * accessibility tree just as unmounting did.\n */\nfunction TabsContent({\n className,\n value,\n keepMounted = false,\n variant,\n ...props\n}: ComponentProps<'div'> & {\n value: string\n keepMounted?: boolean\n variant?: TabsVariant\n}) {\n const { value: selected, baseId } = useTabs()\n const look = useTabsVariant(variant)\n const active = selected === value\n if (!active && !keepMounted) return null\n\n const ids = tabIds(baseId, value)\n\n return (\n <div\n role=\"tabpanel\"\n id={ids.panel}\n data-slot=\"tabs-content\"\n aria-labelledby={ids.trigger}\n // A panel nobody can see is not a tab stop.\n tabIndex={active ? 0 : -1}\n hidden={!active}\n className={cn(\n active && enterFade,\n 'outline-none',\n // The panel *is* the active tab, continued. Same fill, no border\n // between them, and the corners it does round are the two at the\n // bottom — the strip above rounds the other two.\n look === 'browser' && cn('bg-card p-4', radius.control, 'rounded-t-none'),\n className,\n )}\n {...props}\n />\n )\n}\n\nexport { Tabs, TabsContent, TabsList, TabsTrigger, listVariants, triggerVariants }\nexport type { TabsProps }\n"
|
|
21
21
|
}
|
|
22
22
|
]
|
|
23
23
|
}
|