From 3d3776f6d19cefd2409c0a739a0dc5dc13c209d1 Mon Sep 17 00:00:00 2001 From: woksin Date: Sun, 23 Aug 2026 02:25:30 +0200 Subject: [PATCH 001/267] Document the renderer-independent UI foundation --- Documentation/toc.yml | 2 + Documentation/ui-foundation.md | 97 ++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 Documentation/ui-foundation.md diff --git a/Documentation/toc.yml b/Documentation/toc.yml index ec093714..91f1190f 100644 --- a/Documentation/toc.yml +++ b/Documentation/toc.yml @@ -2,6 +2,8 @@ href: index.mdx - name: Why Components href: why-components.md +- name: UI foundation + href: ui-foundation.md - name: PrimeReact and Components href: coming-from-primereact.md - name: Getting started diff --git a/Documentation/ui-foundation.md b/Documentation/ui-foundation.md new file mode 100644 index 00000000..d5cee961 --- /dev/null +++ b/Documentation/ui-foundation.md @@ -0,0 +1,97 @@ +--- +title: UI foundation +description: Why Components owns its public design system and uses React Aria for accessible behavior. +sidebar: + order: 2 +--- + +Components exists to give a Cratis application a stable, productive UI API—not to expose whichever rendering library happens to implement it. The default package therefore owns its markup, styling contract, and TypeScript types. It uses React Aria internally for accessible interaction behavior and uses TanStack Table only when a table needs advanced state that Arc paging and React Aria do not already provide. + +This is an accepted direction for the next major release. Components 3 remains PrimeReact-backed so compatible fixes can ship without hiding a renderer migration inside a minor release. + +## Decision + +The next major release follows these boundaries: + +```mermaid +graph TD + App[Consumer application] --> Components[@cratis/components] + Components --> Contracts[Cratis-owned APIs, parts, tokens, and state attributes] + Components --> Aria[React Aria interaction behavior] + Components --> Table[TanStack Table where advanced table state is needed] + Components --> Arc[@cratis/arc.react bindings] + Legacy[@cratis/components.primereact compatibility package] --> Prime[PrimeReact / PrimeUI] +``` + +Consumers continue importing `@cratis/components/*`. React Aria and TanStack are implementation dependencies and do not appear in public prop types. + +Arc command, query, and dialog bindings remain owned by `@cratis/arc.react`. Components builds visual behavior around those bindings rather than re-exporting them through a package with optional rendering peers. + +## Consumer contract + +The public contract must make both the default Cratis look and a deeply customized product design straightforward: + +- Cratis-owned CSS variables for semantic colors, spacing, typography, motion, elevation, and control dimensions. +- Stable Cratis part names for every meaningful component element. +- State data attributes such as open, selected, disabled, invalid, pending, and orientation. +- `className`, `style`, and per-part configuration without renderer-specific types. +- No required theme preset or proprietary provider. +- Light, dark, forced-colors, reduced-motion, responsive, and right-to-left behavior. +- Public component behavior that remains stable when the internal foundation changes. + +A product may map its own tokens directly onto the Cratis variables or style the stable parts itself. It must not need selectors tied to React Aria's DOM or an internal package's class names. + +## Why React Aria + +[React Aria Components](https://react-aria.adobe.com/getting-started) is style-free and Apache-2.0 licensed. It supplies the interaction behavior that is difficult to implement and maintain correctly: keyboard navigation, focus management, screen-reader semantics, collection behavior, overlays, internationalized dates, and cross-device input. + +Its official component set covers the high-risk primitives Components needs, including Dialog, Select, ComboBox, Table, DatePicker, NumberField, Slider, ColorPicker, Tooltip, and form controls. Its table supports selection, sorting, hierarchical rows, column resizing, drag-and-drop, infinite loading, and virtualization. Its date stack supports locale-specific formats, time zones, right-to-left layouts, and multiple calendar systems. + +React Aria does not remove application responsibility. Components still owns labels, error associations, focus appearance, contrast, hit targets, responsive layout, and behavior specs. + +The React Aria Components Toast API is still exported as unstable. Components keeps its own toast dispatch and presentation contract and may use the lower-level React Aria/Stately toast packages or its own accessible region until the component API is stable. + +## Why TanStack Table is selective + +[TanStack Table](https://tanstack.com/table/latest/docs/overview) is an MIT-licensed headless table engine. It provides advanced filtering, sorting, grouping, sizing, visibility, pinning, selection, and manual server-side state without prescribing markup. + +Query-backed Cratis tables already receive paging from Arc, and React Aria supplies accessible table semantics. TanStack is added only where it removes real state-management complexity. Server filtering, sorting, and paging remain one coherent operation: the server filters before paging and returns the filtered total. + +## Why PrimeReact is not the default + +PrimeReact 11 has a capable layered architecture, and its headless hooks are explicitly intended for custom component libraries. The constraint is the consumer contract around that architecture. + +The [PrimeUI Community License](https://primeui.dev/licenses/community) states that developers building on an internal wrapper or design system still need seats. Eligibility excludes many organizations by revenue, team size, funding, or public-sector status. The [OEM guidance](https://primeui.dev/licenses/oem) also identifies frameworks and SDKs used for third-party development as potential OEM uses, while not clarifying peer-only open-source wrappers. + +A PrimeReact-backed compatibility package is therefore explicit rather than invisible. It requires the Prime peers, documents that PrimeUI licensing still applies, and is not published until PrimeTek confirms the applicable OEM and redistribution terms in writing. + +## Why Components does not implement every primitive itself + +Owning the public API does not mean reimplementing accessibility infrastructure. Dialog focus traps, composite keyboard navigation, international calendars, collection selection, and screen-reader behavior carry high maintenance and regression risk. + +Components owns product-facing composition, Arc integration, styling, and semantics. It delegates low-level interaction behavior to an open, specialized foundation and verifies the result with browser and accessibility tests. + +## Release sequence + +The transition is deliberately split: + +1. A Components 3 minor release delivers source-compatible accessibility, localization, filtering, notification, and paging fixes. PrimeReact and PrimeUI licensing remain unchanged and explicit. +2. The next major release changes the default foundation, removes Prime runtime and declaration references, and introduces the Cratis-owned styling and provider contracts. +3. A PrimeReact compatibility package may preserve the existing implementation for consumers that need a staged migration, subject to licensing confirmation. + +The stabilization specs are the parity contract for the major release. The new implementation is not complete until it preserves the observable behavior those specs protect. + +## Validation gates + +The major release does not ship until: + +- The default package contains no Prime runtime imports or Prime references in emitted declarations. +- npm, pnpm, and Yarn PnP packed-consumer fixtures pass. +- Supported Arc versions compile and load with the packed artifact. +- Representative consumers compile and exercise their critical screens. +- Keyboard, focus, browser accessibility, Storybook, SSR, responsive, dark-mode, forced-colors, and reduced-motion scenarios pass. +- Custom token systems and per-part styling work without internal DOM selectors. +- Bundle size and rendering performance are measured against the stabilization release. +- The migration guide has been followed successfully without repository-specific knowledge. + +Track implementation and acceptance evidence in [the UI foundation issue](https://github.com/Cratis/Components/issues/170). From ab6c08db65746eb590f820ab72d60cd7ada7b5f0 Mon Sep 17 00:00:00 2001 From: woksin Date: Sun, 23 Aug 2026 02:29:14 +0200 Subject: [PATCH 002/267] Add the open UI foundation dependencies --- Source/package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/package.json b/Source/package.json index 479792d6..c677c62a 100644 --- a/Source/package.json +++ b/Source/package.json @@ -139,9 +139,11 @@ "build-storybook": "npx storybook build" }, "dependencies": { + "@internationalized/date": "3.12.3", "allotment": "1.20.5", "framer-motion": "13.1.1", "pixi.js": "^8.20.0", + "react-aria-components": "1.20.0", "react-icons": "5.7.0", "ts-deepmerge": "8.0.0" }, From 06de8dfe5ecdbe7c848d4da7a57ae08eee43dd7d Mon Sep 17 00:00:00 2001 From: woksin Date: Sun, 23 Aug 2026 02:29:14 +0200 Subject: [PATCH 003/267] Own display component markup and styling --- Source/Display/Avatar.tsx | 30 ++++--- Source/Display/Badge.tsx | 24 +++--- Source/Display/Chip.tsx | 30 +++---- Source/Display/Display.css | 130 +++++++++++++++++++++++++++++ Source/Display/Message.tsx | 61 ++++++-------- Source/Display/ProgressBar.tsx | 45 ++++++---- Source/Display/ProgressSpinner.tsx | 28 ++++--- Source/Display/Skeleton.tsx | 25 +++--- Source/Display/Tag.tsx | 30 +++---- Source/styles.css | 1 + 10 files changed, 273 insertions(+), 131 deletions(-) create mode 100644 Source/Display/Display.css diff --git a/Source/Display/Avatar.tsx b/Source/Display/Avatar.tsx index 624a75f2..b0d30157 100644 --- a/Source/Display/Avatar.tsx +++ b/Source/Display/Avatar.tsx @@ -1,8 +1,7 @@ // Copyright (c) Cratis. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -import React from 'react'; -import { Avatar as PrimeAvatar } from 'primereact/avatar'; +import type { ReactNode } from 'react'; /** Props for {@link Avatar}. */ export interface AvatarProps { @@ -11,7 +10,7 @@ export interface AvatarProps { /** Text fallback (e.g. initials) shown when no image is available. */ label?: string; /** Icon fallback shown when no image/label is available. */ - icon?: React.ReactNode; + icon?: ReactNode; /** Alt text for the image. */ alt?: string; /** Avatar size. */ @@ -20,14 +19,19 @@ export interface AvatarProps { className?: string; } -/** - * A user/entity avatar built on PrimeReact 11's compositional `Avatar`. Shows - * an image when available and falls back to initials or an icon otherwise. - */ -export const Avatar = ({ image, label, icon, alt, size, className }: AvatarProps) => ( - - {image - ? - : {icon ?? label}} - +/** A user or entity avatar with an image and text/icon fallback. */ +export const Avatar = ({ image, label, icon, alt = '', size = 'normal', className }: AvatarProps) => ( + + {image ? ( + {alt} + ) : ( + + {icon ?? label} + + )} + ); diff --git a/Source/Display/Badge.tsx b/Source/Display/Badge.tsx index 3d4d1063..da97472e 100644 --- a/Source/Display/Badge.tsx +++ b/Source/Display/Badge.tsx @@ -1,8 +1,7 @@ // Copyright (c) Cratis. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -import React from 'react'; -import { Badge as PrimeBadge } from 'primereact/badge'; +import type { ReactNode } from 'react'; /** Severity tone of a {@link Badge}. */ export type BadgeSeverity = 'secondary' | 'info' | 'success' | 'warn' | 'danger' | 'contrast'; @@ -10,7 +9,7 @@ export type BadgeSeverity = 'secondary' | 'info' | 'success' | 'warn' | 'danger' /** Props for {@link Badge}. */ export interface BadgeProps { /** The value shown inside the badge (e.g. a count). */ - value?: React.ReactNode; + value?: ReactNode; /** Severity tone (drives the color). */ severity?: BadgeSeverity; /** Badge size. */ @@ -20,15 +19,18 @@ export interface BadgeProps { /** Extra class name. */ className?: string; /** Badge content (alternative to {@link value}). */ - children?: React.ReactNode; + children?: ReactNode; } -/** - * A compact count/status badge built on PrimeReact 11's `Badge`. Use for - * unread counts, notification indicators, and small numeric overlays. - */ -export const Badge = ({ value, severity, size, shape, className, children }: BadgeProps) => ( - +/** A compact count or status badge. */ +export const Badge = ({ value, severity = 'secondary', size = 'small', shape, className, children }: BadgeProps) => ( + {value ?? children} - + ); diff --git a/Source/Display/Chip.tsx b/Source/Display/Chip.tsx index 4c56885d..5253b205 100644 --- a/Source/Display/Chip.tsx +++ b/Source/Display/Chip.tsx @@ -1,15 +1,14 @@ // Copyright (c) Cratis. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -import React from 'react'; -import { Chip as PrimeChip } from 'primereact/chip'; +import type { ReactNode } from 'react'; /** Props for {@link Chip}. */ export interface ChipProps { /** The chip label. */ label?: string; /** An icon rendered before the label. */ - icon?: React.ReactNode; + icon?: ReactNode; /** When true, shows a remove control. */ removable?: boolean; /** Invoked when the remove control is activated. */ @@ -20,18 +19,21 @@ export interface ChipProps { className?: string; } -/** - * A labeled, optionally-removable chip built on PrimeReact 11's compositional - * `Chip`. Use for filter pills, selected tokens, and tag-like affordances. - */ +/** A labeled, optionally removable chip. */ export const Chip = ({ label, icon, removable, onRemove, removeAriaLabel = 'Remove', className }: ChipProps) => ( - - {icon && {icon}} - {label} + + {icon && {icon}} + {label} {removable && ( - - - + )} - + ); diff --git a/Source/Display/Display.css b/Source/Display/Display.css new file mode 100644 index 00000000..19b7b407 --- /dev/null +++ b/Source/Display/Display.css @@ -0,0 +1,130 @@ +/* Copyright (c) Cratis. All rights reserved. */ +/* Licensed under the MIT license. See LICENSE file in the project root for full license information. */ + +.cratis-avatar { + display: inline-flex; + width: 2rem; + height: 2rem; + overflow: hidden; + align-items: center; + justify-content: center; + flex: 0 0 auto; + border-radius: 50%; + background: var(--cratis-surface-100); + color: var(--cratis-text-color); +} +.cratis-avatar[data-size='large'] { width: 3rem; height: 3rem; } +.cratis-avatar[data-size='xlarge'] { width: 4rem; height: 4rem; } +.cratis-avatar__image { width: 100%; height: 100%; object-fit: cover; } +.cratis-avatar__fallback { display: inline-flex; align-items: center; justify-content: center; font-weight: 600; } + +.cratis-badge, +.cratis-tag, +.cratis-chip { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 0.35rem; + border-radius: var(--cratis-border-radius); + background: var(--cratis-surface-100); + color: var(--cratis-text-color); + line-height: 1; +} +.cratis-badge { min-width: 1.25rem; min-height: 1.25rem; padding: 0.2rem 0.35rem; font-size: 0.75rem; font-weight: 700; } +.cratis-badge[data-size='large'] { min-width: 1.75rem; min-height: 1.75rem; font-size: 0.875rem; } +.cratis-badge[data-size='xlarge'] { min-width: 2.25rem; min-height: 2.25rem; font-size: 1rem; } +.cratis-badge[data-shape='circle'] { padding: 0; border-radius: 50%; } +.cratis-tag { padding: 0.35rem 0.55rem; font-size: 0.75rem; font-weight: 600; } +.cratis-tag[data-rounded='true'] { border-radius: 999px; } +.cratis-chip { padding: 0.35rem 0.5rem; font-size: 0.875rem; } + +.cratis-badge[data-severity='info'], .cratis-tag[data-severity='info'] { background: color-mix(in srgb, var(--cratis-primary-color) 16%, transparent); color: var(--cratis-primary-color); } +.cratis-badge[data-severity='success'], .cratis-tag[data-severity='success'] { background: color-mix(in srgb, var(--cratis-green-500) 16%, transparent); color: var(--cratis-green-500); } +.cratis-badge[data-severity='warn'], .cratis-tag[data-severity='warn'] { background: color-mix(in srgb, var(--cratis-orange-500) 18%, transparent); color: var(--cratis-orange-500); } +.cratis-badge[data-severity='danger'], .cratis-tag[data-severity='danger'] { background: color-mix(in srgb, var(--cratis-red-500) 16%, transparent); color: var(--cratis-red-500); } +.cratis-badge[data-severity='contrast'], .cratis-tag[data-severity='contrast'] { background: var(--cratis-text-color); color: var(--cratis-surface-0); } + +.cratis-chip__remove { + display: inline-flex; + width: 1.25rem; + height: 1.25rem; + padding: 0; + align-items: center; + justify-content: center; + border: 0; + border-radius: 50%; + background: transparent; + color: inherit; + cursor: pointer; +} +.cratis-chip__remove:hover { background: var(--cratis-surface-hover); } +.cratis-chip__remove:focus-visible { outline: none; box-shadow: var(--cratis-focus-ring); } + +.cratis-message { + display: flex; + align-items: flex-start; + gap: 0.6rem; + padding: 0.75rem 0.9rem; + border: 1px solid var(--cratis-surface-border); + border-radius: var(--cratis-border-radius); + background: var(--cratis-surface-section); + color: var(--cratis-text-color); +} +.cratis-message[data-severity='info'] { border-color: var(--cratis-primary-color); } +.cratis-message[data-severity='success'] { border-color: var(--cratis-green-500); } +.cratis-message[data-severity='warn'] { border-color: var(--cratis-orange-500); } +.cratis-message[data-severity='error'] { border-color: var(--cratis-red-500); } +.cratis-message__icon { flex: 0 0 auto; } +.cratis-message__text { min-width: 0; } + +.cratis-progress-bar { + position: relative; + height: 1rem; + overflow: hidden; + border-radius: 999px; + background: var(--cratis-surface-100); +} +.cratis-progress-bar__indicator { + display: flex; + height: 100%; + min-width: 0; + align-items: center; + justify-content: center; + background: var(--cratis-primary-color); + color: var(--cratis-primary-color-text); + transition: width 160ms ease; +} +.cratis-progress-bar[data-mode='indeterminate'] .cratis-progress-bar__indicator { + width: 40%; + animation: cratis-progress-indeterminate 1.2s ease-in-out infinite; +} +.cratis-progress-bar__label { padding-inline: 0.35rem; font-size: 0.7rem; } + +.cratis-progress-spinner { display: inline-flex; width: 2rem; height: 2rem; } +.cratis-progress-spinner__svg { width: 100%; height: 100%; animation: cratis-spinner-rotate 1s linear infinite; } +.cratis-progress-spinner__track, +.cratis-progress-spinner__range { fill: none; stroke-width: 4; } +.cratis-progress-spinner__track { stroke: var(--cratis-surface-100); } +.cratis-progress-spinner__range { stroke: var(--cratis-primary-color); stroke-linecap: round; stroke-dasharray: 90 150; } + +.cratis-skeleton { + display: block; + background: linear-gradient(90deg, var(--cratis-surface-100), var(--cratis-surface-hover), var(--cratis-surface-100)); + background-size: 200% 100%; + animation: cratis-skeleton-pulse 1.4s ease-in-out infinite; +} + +@keyframes cratis-progress-indeterminate { from { transform: translateX(-120%); } to { transform: translateX(350%); } } +@keyframes cratis-spinner-rotate { to { transform: rotate(360deg); } } +@keyframes cratis-skeleton-pulse { from { background-position: 200% 0; } to { background-position: -200% 0; } } + +@media (prefers-reduced-motion: reduce) { + .cratis-progress-bar__indicator, + .cratis-progress-spinner__svg, + .cratis-skeleton { animation: none; transition: none; } +} + +@media (forced-colors: active) { + .cratis-message, .cratis-chip, .cratis-tag, .cratis-badge { border: 1px solid CanvasText; } + .cratis-progress-bar__indicator, .cratis-progress-spinner__range { background: Highlight; stroke: Highlight; } +} diff --git a/Source/Display/Message.tsx b/Source/Display/Message.tsx index 4359a2f6..3242cc44 100644 --- a/Source/Display/Message.tsx +++ b/Source/Display/Message.tsx @@ -1,8 +1,7 @@ // Copyright (c) Cratis. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -import React from 'react'; -import { Message as PrimeMessage } from 'primereact/message'; +import type { ReactNode } from 'react'; /** Severity tone of a {@link Message}. */ export type MessageSeverity = 'info' | 'success' | 'warn' | 'error' | 'secondary' | 'contrast'; @@ -12,45 +11,37 @@ export interface MessageProps { /** Severity tone (drives the color). */ severity?: MessageSeverity; /** The message text. */ - text?: React.ReactNode; + text?: ReactNode; /** Message content (alternative to {@link text}). */ - children?: React.ReactNode; + children?: ReactNode; /** Extra class name applied to the message root. */ className?: string; - /** - * The icon shown ahead of the text. Defaults to the glyph PrimeReact 10 showed for the - * severity; pass `false` for no icon, or a node of your own. - */ - icon?: React.ReactNode | false; + /** The icon shown ahead of the text. Pass `false` for no icon. */ + icon?: ReactNode | false; } -/** - * The severity glyphs of PrimeReact 10's `Message`, which composed its own icon; v11's - * `Message.Icon` part renders whatever it is given and nothing on its own. - */ -const severityIcons: Record = { - info: 'pi pi-info-circle', - success: 'pi pi-check', - warn: 'pi pi-exclamation-triangle', - error: 'pi pi-times-circle', - secondary: 'pi pi-info-circle', - contrast: 'pi pi-info-circle', +const severitySymbols: Record = { + info: 'ⓘ', + success: '✓', + warn: '⚠', + error: '⨯', + secondary: 'ⓘ', + contrast: 'ⓘ', }; -/** - * A declarative inline message built on PrimeReact 11's compositional `Message` parts. - * - * PrimeReact 10's `` became `Message.Root` + `Message.Content` + - * `Message.Text` in 11; this preserves the flat call shape so an inline notice stays a - * single element at the call site. - */ +/** A declarative inline status message. */ export const Message = ({ severity = 'info', text, children, className, icon }: MessageProps) => ( - - - {icon !== false && ( - {icon ?? } - )} - {children ?? text} - - +
+ {icon !== false && ( + + )} + {children ?? text} +
); diff --git a/Source/Display/ProgressBar.tsx b/Source/Display/ProgressBar.tsx index 3ac8cbd4..fd825cfd 100644 --- a/Source/Display/ProgressBar.tsx +++ b/Source/Display/ProgressBar.tsx @@ -1,8 +1,6 @@ // Copyright (c) Cratis. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -import { ProgressBar as PrimeProgressBar } from 'primereact/progressbar'; - /** Props for {@link ProgressBar}. */ export interface ProgressBarProps { /** Completion value, 0–100. Ignored in `indeterminate` mode. */ @@ -15,17 +13,32 @@ export interface ProgressBarProps { className?: string; } -/** - * A horizontal progress indicator built on PrimeReact 11's compositional - * `ProgressBar`. Use `indeterminate` for unknown-duration work and - * `determinate` with a `value` for measurable progress (e.g. an upload). - */ -export const ProgressBar = ({ value, mode = 'determinate', showValue = true, className }: ProgressBarProps) => ( - - - {showValue && mode === 'determinate' && ( - {value ?? 0}% - )} - - -); +/** A horizontal determinate or indeterminate progress indicator. */ +export const ProgressBar = ({ value = 0, mode = 'determinate', showValue = true, className }: ProgressBarProps) => { + const boundedValue = Math.min(100, Math.max(0, value)); + const ariaValue = mode === 'determinate' ? boundedValue : undefined; + + return ( +
+ + {showValue && mode === 'determinate' && ( + + {boundedValue}% + + )} + +
+ ); +}; diff --git a/Source/Display/ProgressSpinner.tsx b/Source/Display/ProgressSpinner.tsx index 011c28dc..da4b92e7 100644 --- a/Source/Display/ProgressSpinner.tsx +++ b/Source/Display/ProgressSpinner.tsx @@ -1,28 +1,30 @@ // Copyright (c) Cratis. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -import React from 'react'; -import { ProgressSpinner as PrimeProgressSpinner } from 'primereact/progressspinner'; +import type { CSSProperties } from 'react'; /** Props for {@link ProgressSpinner}. */ export interface ProgressSpinnerProps { /** Applied to the spinner root — use it to size the spinner. */ - style?: React.CSSProperties; + style?: CSSProperties; /** Extra class name applied to the spinner root. */ className?: string; /** Announced to assistive technology while the spinner is visible. */ 'aria-label'?: string; } -/** - * An indeterminate spinner built on PrimeReact 11's compositional `ProgressSpinner` parts. - * - * PrimeReact 10's single `` became `ProgressSpinner.Root` + `Track` + - * `Range` in 11; this preserves the flat call shape for the common loading indicator. - */ +/** An indeterminate loading spinner. */ export const ProgressSpinner = ({ style, className, 'aria-label': ariaLabel = 'Loading' }: ProgressSpinnerProps) => ( - - - - + + + ); diff --git a/Source/Display/Skeleton.tsx b/Source/Display/Skeleton.tsx index bffd0c13..48e98593 100644 --- a/Source/Display/Skeleton.tsx +++ b/Source/Display/Skeleton.tsx @@ -1,8 +1,6 @@ // Copyright (c) Cratis. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -import { Skeleton as PrimeSkeleton } from 'primereact/skeleton'; - /** Props for {@link Skeleton}. */ export interface SkeletonProps { /** Width, any CSS length. Defaults to `'100%'`. */ @@ -17,18 +15,17 @@ export interface SkeletonProps { className?: string; } -/** - * A loading placeholder built on PrimeReact 11's `Skeleton`. Use to reserve - * layout while a query is loading (e.g. `result.isPerforming`) instead of a - * blank flash or a spinner. - */ +/** A loading placeholder that reserves content layout. */ export const Skeleton = ({ width = '100%', height = '1rem', borderRadius, circle, className }: SkeletonProps) => ( -