From 8f075f179247328b71d33e6ef8c21907b761b113 Mon Sep 17 00:00:00 2001 From: Mats Bosson Date: Sun, 29 Mar 2026 19:34:13 +0700 Subject: [PATCH] =?UTF-8?q?feat:=20add=2012=20components=20=E2=80=94=20Too?= =?UTF-8?q?ltip,=20Popover,=20HoverCard,=20Alert,=20Badge,=20Skeleton,=20B?= =?UTF-8?q?readcrumbs,=20Link,=20Button,=20Image,=20Meter,=20NumberField?= =?UTF-8?q?=20Floating=20components:=20Tooltip=20(hover/focus),=20Popover?= =?UTF-8?q?=20(click,=20with=20focus=20trap=20and=20dismiss),=20HoverCard?= =?UTF-8?q?=20(hover=20with=20safe=20area).=20Simple=20components:=20Alert?= =?UTF-8?q?=20(role=3Dalert),=20Badge=20(role=3Dstatus),=20Skeleton=20(loa?= =?UTF-8?q?ding=20placeholder=20with=20data=20attributes).=20Navigation:?= =?UTF-8?q?=20Breadcrumbs=20(nav>ol>li=20with=20separators),=20Link=20(acc?= =?UTF-8?q?essible=20anchor=20with=20disabled),=20Button=20(with=20disable?= =?UTF-8?q?d=20click=20suppression).=20Data/Form:=20Image=20(Img+Fallback?= =?UTF-8?q?=20with=20loading=20status),=20Meter=20(like=20Progress=20for?= =?UTF-8?q?=20known=20ranges),=20NumberField=20(spinbutton=20with=20inc/de?= =?UTF-8?q?c).=20302=20tests=20across=2046=20files,=20typecheck=20clean,?= =?UTF-8?q?=20build=20produces=20176=20files.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/core/package.json | 14 +- packages/core/src/components/alert/alert.tsx | 16 ++ packages/core/src/components/alert/index.ts | 2 + packages/core/src/components/badge/badge.tsx | 16 ++ packages/core/src/components/badge/index.ts | 2 + .../breadcrumbs/breadcrumbs-item.tsx | 20 +++ .../breadcrumbs/breadcrumbs-link.tsx | 20 +++ .../breadcrumbs/breadcrumbs-root.tsx | 23 +++ .../breadcrumbs/breadcrumbs-separator.tsx | 18 +++ .../core/src/components/breadcrumbs/index.ts | 16 ++ .../core/src/components/button/button.tsx | 38 +++++ packages/core/src/components/button/index.ts | 2 + .../hover-card/hover-card-content.tsx | 59 +++++++ .../hover-card/hover-card-context.ts | 68 ++++++++ .../components/hover-card/hover-card-root.tsx | 140 ++++++++++++++++ .../hover-card/hover-card-trigger.tsx | 57 +++++++ .../core/src/components/hover-card/index.ts | 9 ++ .../src/components/image/image-context.ts | 30 ++++ .../src/components/image/image-fallback.tsx | 23 +++ .../core/src/components/image/image-img.tsx | 44 +++++ .../core/src/components/image/image-root.tsx | 33 ++++ packages/core/src/components/image/index.ts | 16 ++ packages/core/src/components/link/index.ts | 2 + packages/core/src/components/link/link.tsx | 31 ++++ packages/core/src/components/meter/index.ts | 2 + packages/core/src/components/meter/meter.tsx | 45 ++++++ .../core/src/components/number-field/index.ts | 28 ++++ .../number-field/number-field-context.ts | 38 +++++ .../number-field-decrement-trigger.tsx | 48 ++++++ .../number-field/number-field-description.tsx | 23 +++ .../number-field-error-message.tsx | 27 ++++ .../number-field-increment-trigger.tsx | 48 ++++++ .../number-field/number-field-input.tsx | 48 ++++++ .../number-field/number-field-label.tsx | 24 +++ .../number-field/number-field-root.tsx | 99 ++++++++++++ packages/core/src/components/popover/index.ts | 12 ++ .../src/components/popover/popover-close.tsx | 30 ++++ .../components/popover/popover-content.tsx | 84 ++++++++++ .../src/components/popover/popover-context.ts | 72 +++++++++ .../src/components/popover/popover-portal.tsx | 18 +++ .../src/components/popover/popover-root.tsx | 87 ++++++++++ .../components/popover/popover-trigger.tsx | 57 +++++++ .../core/src/components/skeleton/index.ts | 2 + .../core/src/components/skeleton/skeleton.tsx | 33 ++++ packages/core/src/components/tooltip/index.ts | 4 + .../components/tooltip/tooltip-content.tsx | 50 ++++++ .../src/components/tooltip/tooltip-context.ts | 72 +++++++++ .../src/components/tooltip/tooltip-root.tsx | 151 ++++++++++++++++++ .../components/tooltip/tooltip-trigger.tsx | 58 +++++++ .../tests/components/alert/alert.test.tsx | 22 +++ .../tests/components/badge/badge.test.tsx | 22 +++ .../breadcrumbs/breadcrumbs.test.tsx | 59 +++++++ .../tests/components/button/button.test.tsx | 34 ++++ .../components/hover-card/hover-card.test.tsx | 61 +++++++ .../tests/components/image/image.test.tsx | 40 +++++ .../core/tests/components/link/link.test.tsx | 30 ++++ .../tests/components/meter/meter.test.tsx | 28 ++++ .../number-field/number-field.test.tsx | 79 +++++++++ .../tests/components/popover/popover.test.tsx | 77 +++++++++ .../components/skeleton/skeleton.test.tsx | 28 ++++ .../tests/components/tooltip/tooltip.test.tsx | 64 ++++++++ packages/core/tsdown.config.ts | 13 +- 62 files changed, 2408 insertions(+), 8 deletions(-) create mode 100644 packages/core/src/components/alert/alert.tsx create mode 100644 packages/core/src/components/alert/index.ts create mode 100644 packages/core/src/components/badge/badge.tsx create mode 100644 packages/core/src/components/badge/index.ts create mode 100644 packages/core/src/components/breadcrumbs/breadcrumbs-item.tsx create mode 100644 packages/core/src/components/breadcrumbs/breadcrumbs-link.tsx create mode 100644 packages/core/src/components/breadcrumbs/breadcrumbs-root.tsx create mode 100644 packages/core/src/components/breadcrumbs/breadcrumbs-separator.tsx create mode 100644 packages/core/src/components/breadcrumbs/index.ts create mode 100644 packages/core/src/components/button/button.tsx create mode 100644 packages/core/src/components/button/index.ts create mode 100644 packages/core/src/components/hover-card/hover-card-content.tsx create mode 100644 packages/core/src/components/hover-card/hover-card-context.ts create mode 100644 packages/core/src/components/hover-card/hover-card-root.tsx create mode 100644 packages/core/src/components/hover-card/hover-card-trigger.tsx create mode 100644 packages/core/src/components/hover-card/index.ts create mode 100644 packages/core/src/components/image/image-context.ts create mode 100644 packages/core/src/components/image/image-fallback.tsx create mode 100644 packages/core/src/components/image/image-img.tsx create mode 100644 packages/core/src/components/image/image-root.tsx create mode 100644 packages/core/src/components/image/index.ts create mode 100644 packages/core/src/components/link/index.ts create mode 100644 packages/core/src/components/link/link.tsx create mode 100644 packages/core/src/components/meter/index.ts create mode 100644 packages/core/src/components/meter/meter.tsx create mode 100644 packages/core/src/components/number-field/index.ts create mode 100644 packages/core/src/components/number-field/number-field-context.ts create mode 100644 packages/core/src/components/number-field/number-field-decrement-trigger.tsx create mode 100644 packages/core/src/components/number-field/number-field-description.tsx create mode 100644 packages/core/src/components/number-field/number-field-error-message.tsx create mode 100644 packages/core/src/components/number-field/number-field-increment-trigger.tsx create mode 100644 packages/core/src/components/number-field/number-field-input.tsx create mode 100644 packages/core/src/components/number-field/number-field-label.tsx create mode 100644 packages/core/src/components/number-field/number-field-root.tsx create mode 100644 packages/core/src/components/popover/index.ts create mode 100644 packages/core/src/components/popover/popover-close.tsx create mode 100644 packages/core/src/components/popover/popover-content.tsx create mode 100644 packages/core/src/components/popover/popover-context.ts create mode 100644 packages/core/src/components/popover/popover-portal.tsx create mode 100644 packages/core/src/components/popover/popover-root.tsx create mode 100644 packages/core/src/components/popover/popover-trigger.tsx create mode 100644 packages/core/src/components/skeleton/index.ts create mode 100644 packages/core/src/components/skeleton/skeleton.tsx create mode 100644 packages/core/src/components/tooltip/index.ts create mode 100644 packages/core/src/components/tooltip/tooltip-content.tsx create mode 100644 packages/core/src/components/tooltip/tooltip-context.ts create mode 100644 packages/core/src/components/tooltip/tooltip-root.tsx create mode 100644 packages/core/src/components/tooltip/tooltip-trigger.tsx create mode 100644 packages/core/tests/components/alert/alert.test.tsx create mode 100644 packages/core/tests/components/badge/badge.test.tsx create mode 100644 packages/core/tests/components/breadcrumbs/breadcrumbs.test.tsx create mode 100644 packages/core/tests/components/button/button.test.tsx create mode 100644 packages/core/tests/components/hover-card/hover-card.test.tsx create mode 100644 packages/core/tests/components/image/image.test.tsx create mode 100644 packages/core/tests/components/link/link.test.tsx create mode 100644 packages/core/tests/components/meter/meter.test.tsx create mode 100644 packages/core/tests/components/number-field/number-field.test.tsx create mode 100644 packages/core/tests/components/popover/popover.test.tsx create mode 100644 packages/core/tests/components/skeleton/skeleton.test.tsx create mode 100644 packages/core/tests/components/tooltip/tooltip.test.tsx diff --git a/packages/core/package.json b/packages/core/package.json index a703da8..3e2a1c0 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -59,7 +59,19 @@ "./listbox": { "solid": "./src/components/listbox/index.ts", "import": "./dist/components/listbox/index.js", "require": "./dist/components/listbox/index.cjs" }, "./dropdown-menu": { "solid": "./src/components/dropdown-menu/index.ts", "import": "./dist/components/dropdown-menu/index.js", "require": "./dist/components/dropdown-menu/index.cjs" }, "./context-menu": { "solid": "./src/components/context-menu/index.ts", "import": "./dist/components/context-menu/index.js", "require": "./dist/components/context-menu/index.cjs" }, - "./toast": { "solid": "./src/components/toast/index.ts", "import": "./dist/components/toast/index.js", "require": "./dist/components/toast/index.cjs" } + "./toast": { "solid": "./src/components/toast/index.ts", "import": "./dist/components/toast/index.js", "require": "./dist/components/toast/index.cjs" }, + "./tooltip": { "solid": "./src/components/tooltip/index.ts", "import": "./dist/components/tooltip/index.js", "require": "./dist/components/tooltip/index.cjs" }, + "./alert": { "solid": "./src/components/alert/index.ts", "import": "./dist/components/alert/index.js", "require": "./dist/components/alert/index.cjs" }, + "./badge": { "solid": "./src/components/badge/index.ts", "import": "./dist/components/badge/index.js", "require": "./dist/components/badge/index.cjs" }, + "./skeleton": { "solid": "./src/components/skeleton/index.ts", "import": "./dist/components/skeleton/index.js", "require": "./dist/components/skeleton/index.cjs" }, + "./popover": { "solid": "./src/components/popover/index.ts", "import": "./dist/components/popover/index.js", "require": "./dist/components/popover/index.cjs" }, + "./breadcrumbs": { "solid": "./src/components/breadcrumbs/index.ts", "import": "./dist/components/breadcrumbs/index.js", "require": "./dist/components/breadcrumbs/index.cjs" }, + "./link": { "solid": "./src/components/link/index.ts", "import": "./dist/components/link/index.js", "require": "./dist/components/link/index.cjs" }, + "./button": { "solid": "./src/components/button/index.ts", "import": "./dist/components/button/index.js", "require": "./dist/components/button/index.cjs" }, + "./hover-card": { "solid": "./src/components/hover-card/index.ts", "import": "./dist/components/hover-card/index.js", "require": "./dist/components/hover-card/index.cjs" }, + "./image": { "solid": "./src/components/image/index.ts", "import": "./dist/components/image/index.js", "require": "./dist/components/image/index.cjs" }, + "./meter": { "solid": "./src/components/meter/index.ts", "import": "./dist/components/meter/index.js", "require": "./dist/components/meter/index.cjs" }, + "./number-field": { "solid": "./src/components/number-field/index.ts", "import": "./dist/components/number-field/index.js", "require": "./dist/components/number-field/index.cjs" } }, "scripts": { "build": "tsdown", diff --git a/packages/core/src/components/alert/alert.tsx b/packages/core/src/components/alert/alert.tsx new file mode 100644 index 0000000..da9e6a4 --- /dev/null +++ b/packages/core/src/components/alert/alert.tsx @@ -0,0 +1,16 @@ +import type { JSX } from "solid-js"; +import { splitProps } from "solid-js"; + +export interface AlertProps extends JSX.HTMLAttributes { + children?: JSX.Element; +} + +/** An alert element that announces important messages to screen readers via role="alert". */ +export function Alert(props: AlertProps): JSX.Element { + const [local, rest] = splitProps(props, ["children"]); + return ( +
+ {local.children} +
+ ); +} diff --git a/packages/core/src/components/alert/index.ts b/packages/core/src/components/alert/index.ts new file mode 100644 index 0000000..74a66f0 --- /dev/null +++ b/packages/core/src/components/alert/index.ts @@ -0,0 +1,2 @@ +export { Alert } from "./alert"; +export type { AlertProps } from "./alert"; diff --git a/packages/core/src/components/badge/badge.tsx b/packages/core/src/components/badge/badge.tsx new file mode 100644 index 0000000..04015d9 --- /dev/null +++ b/packages/core/src/components/badge/badge.tsx @@ -0,0 +1,16 @@ +import type { JSX } from "solid-js"; +import { splitProps } from "solid-js"; + +export interface BadgeProps extends JSX.HTMLAttributes { + children?: JSX.Element; +} + +/** A status badge that announces its content to screen readers via role="status". */ +export function Badge(props: BadgeProps): JSX.Element { + const [local, rest] = splitProps(props, ["children"]); + return ( + + {local.children} + + ); +} diff --git a/packages/core/src/components/badge/index.ts b/packages/core/src/components/badge/index.ts new file mode 100644 index 0000000..5389c10 --- /dev/null +++ b/packages/core/src/components/badge/index.ts @@ -0,0 +1,2 @@ +export { Badge } from "./badge"; +export type { BadgeProps } from "./badge"; diff --git a/packages/core/src/components/breadcrumbs/breadcrumbs-item.tsx b/packages/core/src/components/breadcrumbs/breadcrumbs-item.tsx new file mode 100644 index 0000000..cd8ca83 --- /dev/null +++ b/packages/core/src/components/breadcrumbs/breadcrumbs-item.tsx @@ -0,0 +1,20 @@ +import type { JSX } from "solid-js"; +import { splitProps } from "solid-js"; + +/** Props for a breadcrumbs list item. */ +export interface BreadcrumbsItemProps extends JSX.LiHTMLAttributes { + /** When true, marks this item as the current page via aria-current="page". */ + current?: boolean | undefined; + children?: JSX.Element | undefined; +} + +/** A single item in the breadcrumbs trail. Renders as `
  • `. */ +export function BreadcrumbsItem(props: BreadcrumbsItemProps): JSX.Element { + const [local, rest] = splitProps(props, ["current", "children"]); + + return ( +
  • + {local.children} +
  • + ); +} diff --git a/packages/core/src/components/breadcrumbs/breadcrumbs-link.tsx b/packages/core/src/components/breadcrumbs/breadcrumbs-link.tsx new file mode 100644 index 0000000..d5e3b75 --- /dev/null +++ b/packages/core/src/components/breadcrumbs/breadcrumbs-link.tsx @@ -0,0 +1,20 @@ +import type { JSX } from "solid-js"; +import { splitProps } from "solid-js"; + +/** Props for a breadcrumbs link anchor element. */ +export interface BreadcrumbsLinkProps extends JSX.AnchorHTMLAttributes { + /** The URL the breadcrumb link navigates to. */ + href: string; + children?: JSX.Element | undefined; +} + +/** An anchor link inside a breadcrumbs item. */ +export function BreadcrumbsLink(props: BreadcrumbsLinkProps): JSX.Element { + const [local, rest] = splitProps(props, ["href", "children"]); + + return ( + + {local.children} + + ); +} diff --git a/packages/core/src/components/breadcrumbs/breadcrumbs-root.tsx b/packages/core/src/components/breadcrumbs/breadcrumbs-root.tsx new file mode 100644 index 0000000..983deca --- /dev/null +++ b/packages/core/src/components/breadcrumbs/breadcrumbs-root.tsx @@ -0,0 +1,23 @@ +import type { JSX } from "solid-js"; +import { splitProps } from "solid-js"; + +/** Props for the root breadcrumbs navigation container. */ +export interface BreadcrumbsRootProps extends JSX.HTMLAttributes { + /** Accessible label for the navigation landmark. @default "Breadcrumbs" */ + "aria-label"?: string | undefined; + children: JSX.Element; +} + +/** + * Root navigation container for breadcrumbs. + * Renders a `