astralyx-ui 0.13.0 → 0.14.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/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  [![Docs](https://img.shields.io/badge/docs-ui.astralyx.dev-0a7cff)](https://ui.astralyx.dev)
5
5
  [![License](https://img.shields.io/npm/l/astralyx-ui)](./LICENSE)
6
6
 
7
- **343 accessible React components you copy into your repo** — with a CLI and a
7
+ **344 accessible React components you copy into your repo** — with a CLI and a
8
8
  registry that work out what each one needs and bring that too.
9
9
 
10
10
  **[Browse every component, running, at ui.astralyx.dev →](https://ui.astralyx.dev)**
@@ -20,7 +20,7 @@ npx astralyx-ui add button
20
20
 
21
21
  ## What you get
22
22
 
23
- - **343 components** across 36 categories — forms, data, overlays, commerce,
23
+ - **344 components** across 36 categories — forms, data, overlays, commerce,
24
24
  auth, AI, crypto, observability, developer tooling.
25
25
  - **12 primitives** underneath them. Slot, Popper, FocusTrap, Dismissable and
26
26
  friends — no headless-UI dependency.
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "astralyx-ui",
3
- "version": "0.13.0",
3
+ "version": "0.14.0",
4
4
  "type": "module",
5
- "description": "343 accessible React components you copy into your repo, with a CLI and registry that resolve what each one needs.",
5
+ "description": "344 accessible React components you copy into your repo, with a CLI and registry that resolve what each one needs.",
6
6
  "license": "MIT",
7
7
  "author": "Astralyx",
8
8
  "homepage": "https://ui.astralyx.dev",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astralyx-ui",
3
- "version": "0.13.0",
3
+ "version": "0.14.0",
4
4
  "homepage": "https://ui.astralyx.dev",
5
5
  "items": [
6
6
  {
@@ -2420,6 +2420,17 @@
2420
2420
  "separator"
2421
2421
  ]
2422
2422
  },
2423
+ {
2424
+ "name": "logo",
2425
+ "type": "registry:ui",
2426
+ "title": "Logo",
2427
+ "description": "A brand mark that follows the room it has: the full artwork while the rail is open, the square mark once it collapses. Falls back to the product name, and to its initials when only 52px are left.",
2428
+ "category": "Utility",
2429
+ "dependencies": [],
2430
+ "registryDependencies": [
2431
+ "lib-utils"
2432
+ ]
2433
+ },
2423
2434
  {
2424
2435
  "name": "map-embed",
2425
2436
  "type": "registry:ui",
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "logo",
3
+ "type": "registry:ui",
4
+ "title": "Logo",
5
+ "description": "A brand mark that follows the room it has: the full artwork while the rail is open, the square mark once it collapses. Falls back to the product name, and to its initials when only 52px are left.",
6
+ "category": "Utility",
7
+ "dependencies": [],
8
+ "registryDependencies": [
9
+ "lib-utils"
10
+ ],
11
+ "files": [
12
+ {
13
+ "path": "components/ui/logo.tsx",
14
+ "type": "registry:ui",
15
+ "content": "'use client'\n\nimport { useState, type ComponentProps, type ReactNode } from 'react'\nimport { cn } from '@/lib/utils'\n\n/**\n * A brand mark that answers to how much room it has.\n *\n * A rail shows a wordmark when it is open and a square mark when it collapses to\n * 52px, and the usual way to get that is two copies of the artwork with opposing\n * visibility classes on them. That works, and it is the thing everyone rewrites\n * per project — including this repo, where the same trick was inlined in the\n * examples' shell and again in the sidebar's own documentation, once with a\n * cropped `viewBox` to fake the square version.\n *\n * The switch is CSS by default, keyed off the sidebar's `data-state`. That is\n * deliberate: reading the sidebar's context would make every project that runs\n * `add logo` install a sidebar it may not have, and the mark belongs in headers\n * and login screens too. Outside a rail nothing matches the selector, so the\n * full artwork shows and stays shown.\n *\n * `collapsed` overrides it. Pass a boolean when something other than a sidebar\n * decides — a header that shrinks on scroll, a canvas that zooms — and only that\n * one mark renders, which is also the way to avoid fetching both files.\n *\n * The accessible name lives on the container and everything inside it is hidden\n * from assistive technology. Two copies of a logo are one logo, and a reader\n * should not hear the company's name twice for a decision made in CSS.\n */\n\ntype LogoSource = string | ReactNode\n\n/** First letters of the first and last word — \"Astralyx UI\" becomes \"AU\". */\nfunction initialsFrom(text: string) {\n const words = text.trim().split(/\\s+/).filter(Boolean)\n if (words.length === 0) return ''\n if (words.length === 1) return words[0].slice(0, 2).toUpperCase()\n return (words[0][0] + words[words.length - 1][0]).toUpperCase()\n}\n\n/**\n * One rendition: a URL, whatever node you handed over, or the text that stands\n * in for artwork that is missing or refused to load.\n */\nfunction Mark({\n source,\n text,\n className,\n}: {\n source: LogoSource | undefined\n text: string\n className?: string\n}) {\n const [failed, setFailed] = useState(false)\n\n if (typeof source === 'string' && source && !failed) {\n return (\n <img\n src={source}\n alt=\"\"\n // A broken URL falls through to the text rather than leaving the\n // browser's torn-page icon where a brand mark should be.\n onError={() => setFailed(true)}\n className={cn('h-full w-auto object-contain', className)}\n />\n )\n }\n\n if (source != null && typeof source !== 'string') {\n return <span className={cn('flex h-full items-center [&_svg]:h-full [&_svg]:w-auto', className)}>{source}</span>\n }\n\n return (\n <span className={cn('truncate text-sm font-semibold tracking-tight', className)}>{text}</span>\n )\n}\n\nfunction Logo({\n icon,\n full,\n alt,\n fallbackText = '',\n collapsed,\n className,\n ...props\n}: Omit<ComponentProps<'span'>, 'children'> & {\n /** The square mark, for when the rail is collapsed. */\n icon?: LogoSource\n /** The full artwork — mark and wordmark together. */\n full?: LogoSource\n /**\n * The accessible name for whichever rendition is showing. Pass an empty\n * string when the product name is already written beside it, which makes the\n * mark decorative rather than announcing it twice.\n */\n alt: string\n /** Drawn when there is no artwork, or when the image fails. Collapsed, it is\n * reduced to initials — a rail is 52px and a name does not fit. */\n fallbackText?: string\n /** Overrides the sidebar's own state. Renders one mark instead of both. */\n collapsed?: boolean\n}) {\n const short = initialsFrom(fallbackText)\n\n const shell = cn('flex h-6 shrink-0 items-center', className)\n const label = alt ? { role: 'img' as const, 'aria-label': alt } : { 'aria-hidden': true }\n\n if (collapsed !== undefined) {\n return (\n <span data-slot=\"logo\" data-state={collapsed ? 'collapsed' : 'expanded'} className={shell} {...label} {...props}>\n <Mark\n source={collapsed ? (icon ?? full) : (full ?? icon)}\n text={collapsed ? short : fallbackText}\n />\n </span>\n )\n }\n\n return (\n <span data-slot=\"logo\" className={shell} {...label} {...props}>\n <Mark\n source={full ?? icon}\n text={fallbackText}\n className=\"group-data-[state=collapsed]/sidebar:hidden\"\n />\n <Mark\n source={icon ?? full}\n text={short}\n className=\"hidden group-data-[state=collapsed]/sidebar:flex group-data-[state=collapsed]/sidebar:items-center\"\n />\n </span>\n )\n}\n\nexport { Logo }\nexport type { LogoSource }\n"
16
+ }
17
+ ]
18
+ }