# React UI Library β€” Complete Documentation Every documentation page concatenated in full: component and chart pages with their props tables and every demo, hook pages with their type definitions, the guides, and the FAQ. Nothing here is truncated. For an index of the same content as individually fetchable pages, use https://react-ui-library.com/llms.txt All code examples use the published package imports (@platform-blocks/react-ui-library, @platform-blocks/charts). ================================================================================ # GUIDES # Getting started Install react-ui-library, wire up the provider, and render your first component. Docs: https://react-ui-library.com/getting-started **Prerequisites:** - [Node.js 20.19.4 or newer](https://nodejs.org/en/download) β€” An active LTS release (22.x or 24.x) is the safest choice - [npm 10 or newer](https://www.npmjs.com) β€” Bundled with Node.js ## Install with npm Add [@platform-blocks/react-ui-library](https://www.npmjs.com/package/@platform-blocks/react-ui-library) to your React Native or Expo project: ```tsx npm install @platform-blocks/react-ui-library ``` ## Install the peer dependencies React UI Library builds on a handful of packages your app provides. On Expo, install them with expo install so the versions match your SDK: ```tsx npx expo install \ react-native-reanimated \ react-native-safe-area-context \ react-native-svg \ @tabler/icons-react-native ``` ## Set up the provider Wrap your root component with PlatformBlocksProvider to enable theming: `App.tsx` ```tsx import React from 'react'; import { PlatformBlocksProvider } from '@platform-blocks/react-ui-library'; import { YourApp } from './YourApp'; export default function App() { return ( ); } ``` ## Verify the install Render a component to confirm everything is wired up: `TestComponent.tsx` ```tsx import React from 'react'; import { Text, Button, Card } from '@platform-blocks/react-ui-library'; export function TestComponent() { return ( Welcome to React UI Library! πŸŽ‰ ); } ``` Have an accessibility request or need help auditing a flow? Open an issue on GitHub or ask in Discord so we can collaborate on an inclusive solution. -------------------------------------------------------------------------------- # Localization React UI Library ships a lightweight i18n layer. Provide locale resource objects, wrap your app with I18nProvider, then translate via the tx prop or the useI18n hook. Docs: https://react-ui-library.com/localization ## Create resource files Create JSON resource files per locale. `resources.ts` ```tsx import en from './locales/en/common.json'; import fr from './locales/fr/common.json'; import es from './locales/es/common.json'; export const resources = { en: { translation: en }, fr: { translation: fr }, es: { translation: es } }; ``` ## Wrap the app in I18nProvider Wrap your app in the I18nProvider component. `App.tsx` ```tsx import { I18nProvider } from '@platform-blocks/react-ui-library'; import { resources } from './resources'; export function App() { return ( ); } ``` ## Translate and switch locales Switch locales by calling setLocale('fr') etc. Components re-render automatically. `Greeting.tsx` ```tsx import { Alert, Text, ToggleButton, ToggleGroup, useI18n } from '@platform-blocks/react-ui-library'; const LOCALES = ['en', 'fr', 'es']; function Greeting() { const { t, setLocale, locale } = useI18n(); return ( <> { if (typeof next === 'string') setLocale(next); }} > {LOCALES.map((l) => ( {l.toUpperCase()} ))} {t('localization.current', { locale })} ); } ``` ## Notes - Use to render translated copy. - Or call const { t, setLocale, locale } = useI18n(); then t('localization.exampleGreeting', { name: 'Ada' }). - Use formatDate / formatNumber / formatRelativeTime helpers for localized formatting. - Missing keys fall back to fallbackLocale then return the key name (configurable via onMissingKey). -------------------------------------------------------------------------------- # Contributing to React UI Library How the repo is laid out, how to run it locally, and what it takes to land a component, a demo, or a docs page. Docs: https://react-ui-library.com/contribute ## Repo layout - `packages/ui` β€” The component library published as `@platform-blocks/react-ui-library` β€” 100+ components, hooks, and the theming system - `packages/charts` β€” The charting package published as `@platform-blocks/charts` β€” 25 chart types on that same theming - `apps/react-ui-library.com` β€” This documentation site (Expo Router, statically rendered web) - `scripts/` β€” Generators: demos, docs metadata, llms.txt, sitemap, exports map, release ## Set up the repo React UI Library is one npm workspace. Install from the root β€” the workspaces are linked, so the docs site runs against your local packages rather than the published ones. ```bash git clone https://github.com/platform-blocks/react-ui-library.git cd react-ui-library npm install ``` Start the docs site. It is the main development surface: every component demo renders there, against the source you are editing. ```bash npm run dev ``` Press `w` for web, or open the project in Expo Go, an iOS simulator, or an Android emulator. ## Working on the UI package Each component lives in `packages/ui/src/components//` with a conventional shape β€” tests, docs metadata, and demos beside the source: `packages/ui/src/components/Button` ```text Button/ Button.tsx types.ts index.ts __tests__/ # jest tests meta/component.md # frontmatter: title, category, tags + prose demos// # index.tsx (default-export Demo) + description.md ``` Build, test, and lint the package from the repo root: ```bash npm run ui:build # rollup + type declarations npm run ui:test # jest (70% coverage threshold) npm run ui:lint # eslint ``` ## Adding a component A new component is five steps, and the last one writes most of the docs for you: 1. Create the directory following the shape above. 2. Export it from `packages/ui/src/index.ts`. 3. Run `npm run ui:exports` to regenerate the per-component `exports` map in `package.json` β€” CI fails if it is stale. 4. Add a row to `apps/react-ui-library.com/config/coreComponents.ts`. 5. Run `npm run docs:all` β€” the docs route, nav entry, props table, demo code blocks, and llms.txt page are all generated. ## Adding a demo Demos are the examples on a component page, and each one is a real component the site renders. Create `demos//index.tsx` with a default-exported `Demo`, plus a `description.md` carrying `title`, `order`, and `tags` frontmatter, then regenerate: ```bash npm run demos:all ``` The validator that runs afterwards fails on a missing description, a duplicate slug, or a demo that does not compile. ## Working on the docs site Guide pages keep their copy in JSX-free modules under `apps/react-ui-library.com/config/` β€” `gettingStarted.ts`, `templates.ts`, `faq.ts`, and this page's `contribute.ts` β€” so `scripts/generate-llms.ts` can render the same source into `llms.txt` for language models. A new page touches four files: - the route file under `app/`, - a config module holding its copy, - `config/navigationConfig.ts` for the sidebar entry, - `config/routeSeo.ts` for the title and description β€” the prerender check fails without them. Before opening a pull request that changes docs content, regenerate the derived files: ```bash npm run docs:all ``` ## Verifying a change One command covers both packages β€” the exports map, the skills check, lint, and the test suites: ```bash npm run verify:packages ``` ## Starter templates & community The starter templates are separate repositories, listed on the site from one config module. - Templates live under the [platform-blocks org](https://github.com/platform-blocks) and are listed via `apps/react-ui-library.com/config/templates.ts`. - Built a starter with your own stack? [Share it with us](https://github.com/platform-blocks/react-ui-library/issues/new?template=community_template.yml) β€” accepted templates get listed on the [Getting Started](https://react-ui-library.com/getting-started) page. ## Releases Maintainers run `npm run release`, which verifies both packages (exports, lint, tests, build) and publishes `@platform-blocks/react-ui-library` and `@platform-blocks/charts` to npm. Contributors never need to bump a version β€” say what the change is in the pull request and it lands in the next release. Stuck on any of this? Open a [discussion](https://github.com/orgs/platform-blocks/discussions) or [issue](https://github.com/platform-blocks/react-ui-library/issues) β€” a question that needed asking is usually a docs bug worth fixing. -------------------------------------------------------------------------------- # COMPONENTS # Accordion The Accordion component groups related content into expandable sections. ## Metadata - Canonical name: `Accordion` - Package: `@platform-blocks/react-ui-library` - Import: `import { Accordion } from '@platform-blocks/react-ui-library';` - Status: stable - Since: 0.4.0 - Category: display - Tags: collapse, expand, panel, ui, content-grouping - Docs: https://react-ui-library.com/components/Accordion - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Accordion ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `items` | AccordionItem[] | Yes | | Ordered list of items to render. The `key` for each item must be unique. | | `type` | AccordionType | No | 'single' | Expansion behavior. 'single' ensures only one item can be expanded at a time; 'multiple' allows independent expansion. | | `defaultExpanded` | string[] | No | [] as string[] | Initial expanded item keys (uncontrolled). Ignored when `expanded` is provided. For `type="single"` only the first key is used at initialization. | | `expanded` | string[] | No | | Controlled set of expanded item keys. Provide alongside `onExpandedChange`. | | `onExpandedChange` | (expanded: string[]) => void | No | | Called when the expanded keys change (both controlled & uncontrolled flows). | | `onItemToggle` | OnAccordionToggle | No | | Per-item toggle event with rich metadata. Fires after state resolution. | | `variant` | AccordionVariant | No | 'default' | Visual variant style preset. | | `size` | SizeValue | No | 'md' | Size scale controlling paddings, font sizes, and icon dimensions. | | `color` | ThemeColor | No | undefined | Brand accent applied to the expanded item (title, chevron, and a subtle surface tint). Opt-in β€” when unset, the open state stays neutral and reads from the bolded title and rotated chevron alone. | | `showChevron` | boolean | No | true | Whether to render the chevron affordance. | | `chevronPosition` | 'start' \| 'end' | No | 'end' | Chevron placement relative to the header text. | | `density` | 'comfortable' \| 'compact' \| 'spacious' | No | 'comfortable' | Space efficiency / vertical density preset. | | `style` | StyleProp | No | | Root container style override. | | `headerStyle` | StyleProp | No | | Header row style override applied to each item. | | `contentStyle` | StyleProp | No | | Collapsible content container style override. | | `headerTextStyle` | StyleProp | No | | Text style applied to the header label. | | `titleProps` | Omit | No | | Override props applied to each item's header `` (style, weight, ff, size, color). Applies to every item in the accordion. | | `persistKey` | string | No | | Explicit persistence key. If omitted, an automatic hash key will be generated when uncontrolled. | | `autoPersist` | boolean | No | true | Enables persistence of expanded state (uncontrolled only) across remounts in-process. | | `animated` | AccordionAnimationProp | No | true | Enables animation or accepts a config object for custom durations & easing. | | `transitionDuration` | number | No | 220 | Length of the expand/collapse transition (chevron spin + panel height) in ms. Takes precedence over `animated`; `0` renders state changes instantly. Always 0 when the user prefers reduced motion. | | `m` | number | No | | Margin applied to all sides | | `mt` | number | No | | Margin applied to the top side | | `mr` | number | No | | Margin applied to the right side | | `mb` | number | No | | Margin applied to the bottom side | | `ml` | number | No | | Margin applied to the left side | | `mx` | number | No | | Horizontal margin applied to left and right sides | | `my` | number | No | | Vertical margin applied to top and bottom sides | | `p` | number | No | | Padding applied to all sides | | `pt` | number | No | | Padding applied to the top side | | `pr` | number | No | | Padding applied to the right side | | `pb` | number | No | | Padding applied to the bottom side | | `pl` | number | No | | Padding applied to the left side | | `px` | number | No | | Horizontal padding applied to left and right sides | | `py` | number | No | | Vertical padding applied to top and bottom sides | | `radius` | RadiusValue | No | | Border radius value - supports size tokens, numbers, and special values | ## Examples ### Single Expansion ID: `Accordion.basic` β€’ Tags: accordion β€’ Category: usage β€’ Status: stable β€’ Since: 1.0.0 Allow only one item to open at a time by setting `type="single"` and passing an items array. ```tsx return ; } ``` ### Multiple Expansion ID: `Accordion.multiple` β€’ Tags: accordion, controlled β€’ Category: behavior β€’ Status: stable β€’ Since: 1.0.0 Control the `expanded` keys to keep several accordion items open at the same time. ```tsx const [expandedKeys, setExpandedKeys] = useState(['collaboration']); return ( ); } ``` ### Visual Variants ID: `Accordion.variants` β€’ Tags: accordion, appearance β€’ Category: appearance β€’ Status: stable β€’ Since: 1.0.0 Switch between `default`, `separated`, and `bordered` variants to adjust emphasis. ```tsx const variants = ['default', 'separated', 'bordered'] as const; return ( {variants.map((variant) => ( {variant} ))} ); } ``` ### Accent Colors ID: `Accordion.colors` β€’ Tags: accordion, appearance, color β€’ Category: appearance β€’ Status: stable β€’ Since: 0.10.1 Accent each expanded panel with a theme palette β€” `primary`, `secondary`, `tertiary`, `success`, `warning`, `error`, or `gray`. Set `color` on the accordion for a uniform accent, or per item to mix accents in a single accordion. Collapsed items stay neutral so only the open panel is highlighted. ```tsx return ( ); } ``` ### Title customization ID: `Accordion.title-customization` β€’ Tags: titleProps, customization, slot-props β€’ Category: general β€’ Status: stable β€’ Since: 1.0.0 `titleProps` accepts any `` props (`ff`, `weight`, `tracking`, `uppercase`, `size`, `color`, `style`) and applies them to every item header in the accordion. The existing `headerTextStyle` escape hatch still works and can be combined. ```tsx return ( ); } ``` -------------------------------------------------------------------------------- # Alert The Alert component displays important messages to users with different severity levels, variants, and optional actions like dismissal. Title and body each accept full `` props via `titleProps` / `bodyProps`. ## Metadata - Canonical name: `Alert` - Package: `@platform-blocks/react-ui-library` - Import: `import { Alert } from '@platform-blocks/react-ui-library';` - Category: feedback - Tags: alert, notice, notification, message, status, feedback, callout - Docs: https://react-ui-library.com/components/Alert - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Alert ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `variant` | AlertVariant | No | 'light' | | | `color` | ThemeColor | No | 'primary' | | | `severity` | AlertSeverity | No | | Severity helper β€” sets both the color and the default icon (`info \| success \| warning \| error`). More than a color: prefer it over `color` when the alert carries a status, so the icon comes with it. | | `title` | string | No | | | | `children` | React.ReactNode | No | | | | `icon` | React.ReactNode \| string \| null \| false | No | | | | `fullWidth` | boolean | No | false | | | `withCloseButton` | boolean | No | false | | | `closeButtonLabel` | string | No | | | | `onClose` | () => void | No | | | | `style` | StyleProp | No | | | | `testID` | string | No | | | | `titleProps` | Omit | No | | Override props applied to the title `` (style, weight, ff, size, color). | | `bodyProps` | Omit | No | | Override props applied to the body `` (the `children` content). | | `m` | number | No | | Margin applied to all sides | | `mt` | number | No | | Margin applied to the top side | | `mr` | number | No | | Margin applied to the right side | | `mb` | number | No | | Margin applied to the bottom side | | `ml` | number | No | | Margin applied to the left side | | `mx` | number | No | | Horizontal margin applied to left and right sides | | `my` | number | No | | Vertical margin applied to top and bottom sides | | `p` | number | No | | Padding applied to all sides | | `pt` | number | No | | Padding applied to the top side | | `pr` | number | No | | Padding applied to the right side | | `pb` | number | No | | Padding applied to the bottom side | | `pl` | number | No | | Padding applied to the left side | | `px` | number | No | | Horizontal padding applied to left and right sides | | `py` | number | No | | Vertical padding applied to top and bottom sides | | `radius` | RadiusValue | No | 'md' | Border radius value - supports size tokens, numbers, and special values | ## Examples ### Basics ID: `Alert.basic` β€’ Tags: alerts β€’ Category: basics β€’ Status: stable β€’ Since: 1.0.0 Use `severity` to set it β€” it picks the matching color and icon automatically. ```tsx return ( Use alerts to highlight contextual information inline with page content. Your changes were stored successfully. Retry the action or check the status page for outages. ); } ``` ### Variants ID: `Alert.variants` β€’ Tags: alerts, variants β€’ Category: styling β€’ Status: stable β€’ Since: 1.0.0 Compare light, outline, filled, and subtle variants to match alert prominence to the message. ```tsx return ( Balanced background and border treatment for inline notes. Subtle emphasis without increasing background contrast. High-contrast option for urgent messaging. No background color, but tinted icon and text. ); } ``` ### Dismissible ID: `Alert.interactive` β€’ Tags: alerts, dismissible β€’ Category: interaction β€’ Status: stable β€’ Since: 1.0.0 Add `withCloseButton` and handle `onClose` to let users dismiss an alert. ```tsx const [visible, setVisible] = useState(true); if (!visible) { return ( ); } return ( setVisible(false)} > Your draft is missing a title. Resolve before publishing. ); } ``` -------------------------------------------------------------------------------- # AppShell High-level layout container orchestrating header, navigation rail/drawer, aside panel, footer, and optional mobile bottom navigation. Provides consistent responsive behavior and safe-area handling across platforms. ## Responsibilities - Manage responsive breakpoints & derive layout measurements - Provide context for child sections (header height, navbar width, etc.) - Support desktop inline collapsing (rail) and mobile drawer presentation - Coordinate safe-area padding via `SafeAreaProvider` - Offer configurable animation duration for structural transitions ## Public Sub-Components - `AppShell.Header` – fixed header region at top - `AppShell.Navbar` – left navigation (rail / drawer) - `AppShell.Aside` – right supplemental panel - `AppShell.Footer` – bottom footer (desktop) - `AppShell.BottomNav` – mobile-only bottom navigation bar - `AppShell.Main` – primary scroll/content surface - `AppShell.Section` – helper container for vertical stacking inside panels ## Key Hooks - `useAppShell()` – consume computed layout context - `useBreakpoint()` – current breakpoint token - `useNavbarHover()` – desktop rail hover expansion state - `resolveResponsiveValue(value, breakpoint)` – utility to normalize `ResponsiveSize` ## Default Config Reference See `defaults.ts` for baseline dimension & behavior values and `meta.schema.ts` for a lightweight machine-readable spec. ## Notes - Hover expansion is intentionally local to navbar to avoid global re-renders - Rail width defined via `navbar.collapsedWidth` (default 72) - Future: integrate design token pipeline for breakpoint map & spacing scales. - `AppShell.Main` supports configurable `maxWidth`, centering, and responsive table-of-contents rail. When `autoLayout` is enabled you can pass `maxContentWidth`, `centerContent`, and table of contents props directly to `AppShell` for convenience. ## Metadata - Canonical name: `AppShell` - Package: `@platform-blocks/react-ui-library` - Import: `import { AppShell } from '@platform-blocks/react-ui-library';` - Category: layout - Docs: https://react-ui-library.com/components/AppShell - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/AppShell ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `layout` | 'default' \| 'alt' | No | | | | `header` | HeaderConfig | No | | | | `navbar` | NavbarConfig | No | | | | `aside` | AsideConfig | No | | | | `footer` | FooterConfig | No | | | | `bottomNav` | BottomNavConfig | No | | | | `showHeader` | boolean | No | true | | | `layoutSections` | LayoutVisibilityConfig | No | | Toggle rendering of individual autoLayout sections | | `autoLayout` | boolean | No | | Enable AppShell auto-composition. When true, AppShell will render its own Header/Navbar/Main/Footer/BottomBar using the provided content props instead of relying on children. | | `headerContent` | React.ReactNode \| (() => React.ReactNode) | No | | Content to render inside AppShell.Header when autoLayout is enabled | | `navbarContent` | React.ReactNode \| (() => React.ReactNode) | No | | Content to render inside AppShell.Navbar when autoLayout is enabled | | `asideContent` | React.ReactNode \| (() => React.ReactNode) | No | | Content to render inside AppShell.Aside when autoLayout is enabled | | `footerContent` | React.ReactNode \| (() => React.ReactNode) | No | | Content to render inside AppShell.Footer when autoLayout is enabled | | `bottomNavItems` | BottomAppBarItem[] | No | | Items for a mobile bottom navigation bar when autoLayout is enabled | | `bottomNavProps` | Partial | No | | Additional props forwarded to BottomAppBar in autoLayout mode (items overridden by bottomNavItems) | | `mobileMenu` | MobileMenuConfig | No | | | | `cssGeometry` | boolean | No | false | Take the shell's geometry from CSS custom properties rather than from the breakpoint the JavaScript resolved. Web only, and a contract: the app must inline the stylesheet `createAppShellCss` builds from the same config. It exists for statically rendered apps, where the prerender has no viewport to measure and every guess it makes lands as a layout shift and a hydration mismatch on first paint. See `shellCssVars.ts`. | | `statusBar` | StatusBarConfig | No | | | | `padding` | ResponsiveSize | No | 'md' | | | `withBorder` | boolean | No | true | | | `zIndex` | number | No | 100 | | | `transitionDuration` | number | No | 200 | | | `transitionTimingFunction` | string | No | 'ease' | | | `disabled` | boolean | No | false | | | `children` | React.ReactNode | Yes | | | | `backgroundColor` | string | No | | | | `withSafeArea` | boolean | No | true | | | `style` | any | No | | | | `testID` | string | No | | | | `maxContentWidth` | number \| string | No | | Maximum width for main content area to prevent stretching on wide screens | | `centerContent` | boolean | No | true | Center content when maxContentWidth is set | | `tableOfContents` | React.ReactNode | No | | Optional table of contents rendered to the right of the main content | | `hideTableOfContentsOnMobile` | boolean | No | true | Hide the table of contents automatically on mobile breakpoints | | `tableOfContentsWidth` | number \| string | No | 280 | Custom width for the table of contents column | | `tableOfContentsWithBorder` | boolean | No | true | Toggle border between content and table of contents | | `m` | number | No | | Margin applied to all sides | | `mt` | number | No | | Margin applied to the top side | | `mr` | number | No | | Margin applied to the right side | | `mb` | number | No | | Margin applied to the bottom side | | `ml` | number | No | | Margin applied to the left side | | `mx` | number | No | | Horizontal margin applied to left and right sides | | `my` | number | No | | Vertical margin applied to top and bottom sides | | `p` | number | No | | Padding applied to all sides | | `pt` | number | No | | Padding applied to the top side | | `pr` | number | No | | Padding applied to the right side | | `pb` | number | No | | Padding applied to the bottom side | | `pl` | number | No | | Padding applied to the left side | | `px` | number | No | | Horizontal padding applied to left and right sides | | `py` | number | No | | Vertical padding applied to top and bottom sides | ## Examples ### Enhanced ID: `AppShell.enhanced` β€’ Category: general ```tsx const sampleTOC = ( Contents Introduction Getting Started Installation NPM Package Yarn Setup Configuration Components AppShell Layout System Examples ); return ( ( Documentation )} navbarContent={() => ( Navigation Getting Started Components Examples API Reference )} maxContentWidth={960} tableOfContents={sampleTOC} tableOfContentsWidth={280} hideTableOfContentsOnMobile centerContent > Main Content with TOC This demonstrates the enhanced AppShell with max width constraints and a table of contents sidebar. The main content area has a maximum width and is centered, while the table of contents appears on the right on desktop screens. The layout is fully responsive - on mobile devices, the table of contents is hidden by default to preserve screen space. Features β€’ Max width constraint for better readability on wide screens β€’ Table of contents sidebar with responsive behavior β€’ Configurable through AppShell or AppShellMain props β€’ Seamless integration with existing AppShell layout system ); } ``` -------------------------------------------------------------------------------- # AudioPlayer AudioPlayer wraps `expo-audio` with a seekable [Waveform](/components/Waveform), transport controls and progress callbacks. Times in `PlaybackState`, `ProgressData` and the ref methods are milliseconds. Playback needs the optional `expo-audio` peer dependency (`npx expo install expo-audio`); without it the component renders and reports a missing-module error rather than throwing. ## Metadata - Canonical name: `AudioPlayer` - Package: `@platform-blocks/react-ui-library` - Import: `import { AudioPlayer } from '@platform-blocks/react-ui-library';` - Status: beta - Since: 0.11.0 - Category: media - Tags: audio, player, waveform, media, playback - Docs: https://react-ui-library.com/components/AudioPlayer - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/AudioPlayer ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `source` | string \| number \| { uri: string } | No | | Audio source - can be URL, local file, or asset | | `peaks` | number[] | No | | Pre-computed waveform peaks (optional - will generate if not provided) | | `autoPlay` | boolean | No | | Whether to auto-play when loaded | | `loop` | boolean | No | | Whether to loop the audio | | `volume` | number | No | | Initial volume (0-1) | | `rate` | number | No | | Playback rate (0.5-2.0) | | `showControls` | boolean | No | | Whether to show player controls | | `controls` | { playPause?: boolean; skip?: boolean; volume?: boolean; speed?: boolean; download?: boolean; share?: boolean; waveform?: boolean; } | No | | Which controls to display | | `controlsPosition` | 'top' \| 'bottom' \| 'overlay' \| 'none' | No | | Custom control layout | | `variant` | 'minimal' \| 'compact' \| 'full' \| 'soundcloud' \| 'spotify' | No | | Player theme variant | | `colorScheme` | 'light' \| 'dark' \| 'auto' | No | | Color scheme | | `onLoad` | (data: AudioLoadData) => void | No | | Called when audio is loaded and ready | | `onPlaybackStateChange` | (state: PlaybackState) => void | No | | Called when playback state changes | | `onProgress` | (data: ProgressData) => void | No | | Called during playback with current time | | `onEnd` | () => void | No | | Called when playback finishes | | `onError` | (error: AudioError) => void | No | | Called on playback error | | `onBuffer` | (data: BufferData) => void | No | | Called when audio buffer updates | | `generateWaveform` | boolean | No | | Whether to generate waveform from audio | | `waveformOptions` | { samples?: number; precision?: number; channel?: 'left' \| 'right' \| 'mix'; } | No | | Waveform generation options | | `showTime` | boolean | No | | Show time labels | | `timeFormat` | 'mm:ss' \| 'hh:mm:ss' \| 'relative' | No | | Time format | | `showMetadata` | boolean | No | | Show audio metadata | | `metadata` | AudioMetadata | No | | Audio metadata | | `showSpectrum` | boolean | No | | Show spectrum analyzer | | `spectrumOptions` | SpectrumOptions | No | | Spectrum analyzer options | | `enableKeyboardShortcuts` | boolean | No | | Enable keyboard shortcuts | | `keyboardShortcuts` | KeyboardShortcuts | No | | Custom keyboard shortcuts | | `enableGestures` | boolean | No | | Enable gesture controls | | `gestureConfig` | GestureConfig | No | | Gesture configuration | | `enableEffects` | boolean | No | | Enable audio effects | | `effects` | AudioEffects | No | | Audio effects configuration | | `playlist` | PlaylistItem[] | No | | Enable playlist support | | `currentTrack` | number | No | | Current playlist index | | `onTrackChange` | (index: number, track: PlaylistItem) => void | No | | Playlist callbacks | | `enableExport` | boolean | No | | Enable audio export | | `exportOptions` | { formats?: ('mp3' \| 'wav' \| 'aac')[]; quality?: 'low' \| 'medium' \| 'high'; } | No | | Export options | | `shareOptions` | { platforms?: ('copy' \| 'email' \| 'social')[]; includeTimestamp?: boolean; } | No | | Custom share options | | `w` | number | No | | Width of the waveform | | `h` | number | No | | Height of the waveform | | `color` | string | No | | Color of the waveform | | `size` | ComponentSizeValue | No | 'md' | Size token controlling height, bar width/gap, stroke width, and label type. Accepts any of the seven component tokens (`xs`–`3xl`) or a number, which is read as the waveform height and scales the bar metrics proportionally. Individual props (`h`, `barWidth`, `barGap`, `strokeWidth`, `minBarHeight`) override the token they derive from. | | `barWidth` | number | No | | Width of individual bars (for bar variants) | | `barGap` | number | No | | Gap between bars (for bar variants) | | `strokeWidth` | number | No | | Stroke width for line variant | | `gradientColors` | string[] | No | | Colors for gradient variant | | `progressColor` | string | No | | Color for the progress indicator | | `interactive` | boolean | No | | Whether the waveform is interactive (clickable for seeking) | | `onSeek` | (position: number) => void | No | | Callback fired when user clicks/seeks to a position | | `onDragStart` | (position: number) => void | No | | Callback fired when user starts dragging | | `onDrag` | (position: number) => void | No | | Callback fired when user is dragging | | `onDragEnd` | (position: number) => void | No | | Callback fired when user ends dragging | | `accessibilityLabel` | string | No | | Accessibility label for the waveform | | `accessibilityHint` | string | No | | Accessibility hint for interactive waveforms | | `minBarHeight` | number | No | | Minimum height for bars (prevents invisible bars) | | `normalize` | boolean | No | | Whether to normalize waveform heights so the tallest bar uses full height | | `fullWidth` | boolean | No | | Whether the waveform should take the full width of its container | | `maxVisibleBars` | number | No | | Maximum number of bars to render (for performance with large datasets) | | `showProgressLine` | boolean | No | | Whether to show a vertical progress line indicator | | `progressLineStyle` | { color?: string; width?: number; opacity?: number; } | No | | Style configuration for the progress line | | `showTimeStamps` | boolean | No | | Whether to show time stamps along the waveform | | `duration` | number | No | | Duration in seconds for time stamp calculation | | `timeStampInterval` | number | No | | Time stamp interval in seconds | | `loading` | boolean | No | | Whether the waveform is in a loading state | | `error` | string | No | | Error message to display | | `loadingProgress` | number | No | | Loading progress (0-1) for progressive loading | | `selection` | [number, number] | No | | Selected time range [start, end] in normalized coordinates (0-1) | | `onSelectionChange` | (selection: [number, number]) => void | No | | Callback when selection changes | | `zoomLevel` | number | No | | Zoom level (1 = normal, 2 = 2x zoom, etc.) | | `zoomCenter` | number | No | | Zoom center position (0-1) | | `onZoomChange` | (zoomLevel: number, center: number) => void | No | | Callback when zoom changes | | `enableAnimations` | boolean | No | | Whether to enable smooth animations | | `showRMS` | boolean | No | | Whether to show RMS (average) levels alongside peaks | | `rmsData` | number[] | No | | RMS data array (should match peaks length) | | `markers` | WaveformMarker[] | No | | Custom markers to display on the waveform | | `enablePerformanceMonitoring` | boolean | No | | Enable performance monitoring | | `onPerformanceMetrics` | (metrics: PerformanceMetrics) => void | No | | Callback for performance metrics | ## Examples ### Basic Usage ID: `AudioPlayer.basic` β€’ Tags: audio, playback, peaks, expo-audio β€’ Category: basics β€’ Status: stable β€’ Since: 1.0.0 Point `source` at a URL or a bundled clip and the player handles loading, play/pause, seeking and progress. Playback requires `expo-audio`; without it the controls render but report a missing-module error. ```tsx // Peaks measured from the same bundled clip the player loads. return ( Playback runs through `expo-audio`. Pass `peaks` to draw the real waveform, then tap it to seek β€” the progress line follows playback either way. ); } ``` -------------------------------------------------------------------------------- # AutoComplete The AutoComplete component provides search functionality with suggestions, supporting single/multi-select, async data loading, and rich content display ## Metadata - Canonical name: `AutoComplete` - Package: `@platform-blocks/react-ui-library` - Import: `import { AutoComplete } from '@platform-blocks/react-ui-library';` - Category: input - Tags: input, search, typeahead, autocomplete, suggestions - Docs: https://react-ui-library.com/components/AutoComplete - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/AutoComplete ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `label` | string | No | | Input label | | `description` | string | No | | Description text below the input | | `helperText` | string | No | | Helper text displayed below the field when no error is present | | `required` | boolean | No | | Whether the field is required | | `error` | string | No | | Error message | | `value` | string | No | | Input value | | `onChangeText` | (text: string) => void | No | | Change handler | | `placeholder` | string | No | | Placeholder text | | `disabled` | boolean | No | | Whether the input is disabled | | `size` | SizeValue | No | | Controls input size (affects padding and height) | | `radius` | RadiusValue | No | | Controls border radius; accepts size tokens or numeric value | | `clearable` | boolean | No | | Show built-in clear button when there is text | | `clearButtonLabel` | string | No | | Accessible label for the clear button | | `onClear` | () => void | No | | Callback when the clear button is pressed | | `style` | StyleProp | No | | Custom style | | `data` | AutoCompleteOption[] | No | | Data source for suggestions | | `onSearch` | (query: string) => Promise | No | | Async data fetcher | | `minSearchLength` | number | No | | Minimum characters to trigger search | | `searchDelay` | number | No | | Search debounce delay | | `renderItem` | ( item: AutoCompleteOption, index: number, options: { query: string; onSelect: (item: AutoCompleteOption) => void; isHighlighted?: boolean; isSelected?: boolean; } ) => React.ReactNode | No | | Custom item renderer | | `onSelect` | (item: AutoCompleteOption) => void | No | | Selection handler | | `renderValue` | ( item: AutoCompleteOption, context: { focused: boolean; clear: () => void; } ) => React.ReactNode | No | | Custom renderer for the selected option shown inside the single-select input. When provided and an option is selected, the returned node is overlaid on the text field while it is not focused (focusing the field reveals the editable text so the query can be changed). Ignored in multiSelect mode β€” use `renderSelectedValue` for chips there. | | `allowCustomValue` | boolean | No | | Whether to allow custom values | | `maxSuggestions` | number | No | | Maximum number of suggestions to display | | `showSuggestionsOnFocus` | boolean | No | | Whether to show suggestions on focus (default: true) | | `renderEmptyState` | () => React.ReactNode | No | | Custom empty state component | | `renderLoadingState` | () => React.ReactNode | No | | Custom loading state component | | `filter` | (item: AutoCompleteOption, query: string) => boolean | No | | Filter function for local data | | `highlightMatches` | boolean | No | | Whether to highlight matching text | | `highlightColor` | string | No | | Text color for the matched substring when `highlightMatches` is on. Defaults to a primary-ramp shade chosen for the active color scheme. | | `highlightBackgroundColor` | string | No | | Background color painted behind the matched substring (default: transparent β€” the match is distinguished by color and weight). | | `suggestionsStyle` | any | No | | Custom styles for suggestions container | | `suggestionItemStyle` | any | No | | Custom styles for suggestion items | | `multiSelect` | boolean | No | | Enable multi-select mode | | `selectedValues` | AutoCompleteOption[] | No | | Selected values for multi-select mode | | `renderSelectedValue` | ( item: AutoCompleteOption, index: number, context: { onRemove: () => void; disabled: boolean; isFocused: boolean; inputValue: string; source: 'input' \| 'modal'; } ) => React.ReactNode | No | | Custom renderer for each selected value chip in multi-select mode | | `selectedValuesContainerStyle` | StyleProp | No | | Optional style override for the selected values container | | `selectedValueChipProps` | Partial | No | | Additional props applied to the default Chip renderer for selected values | | `refocusAfterSelect` | boolean | No | | Controls whether the input regains focus after selecting an option | | `freeSolo` | boolean | No | | Whether to allow free-form input (can add custom values) | | `displayProperty` | 'label' \| 'value' | No | | What to display in input after selection - 'label' or 'value' | | `useModal` | boolean | No | | Whether to render suggestions in a modal for guaranteed top layering | | `usePortal` | boolean | No | | Whether to render suggestions in a portal for proper z-index handling (default: true) | | `inputWidth` | number \| string | No | | Explicit width for the input container (overrides layout width) | | `minWidth` | number | No | | Minimum width (particularly helpful on Android where intrinsic shrink can occur) | | `textInputProps` | Omit | No | | Additional TextInput props | | `autoCapitalize` | RNTextInputProps['autoCapitalize'] | No | | Text auto-capitalization behavior | | `autoCorrect` | boolean | No | | Whether to enable auto-correct | | `autoFocus` | boolean | No | | Whether to auto-focus on mount | | `returnKeyType` | RNTextInputProps['returnKeyType'] | No | | Return key type for soft keyboard | | `blurOnSubmit` | boolean | No | | Whether to blur on submit | | `selectTextOnFocus` | boolean | No | | Select all text on focus | | `textContentType` | RNTextInputProps['textContentType'] | No | | iOS text content type for autofill | | `textAlign` | RNTextInputProps['textAlign'] | No | | Text alignment | | `spellCheck` | boolean | No | | Whether spell check is enabled | | `inputMode` | RNTextInputProps['inputMode'] | No | | Input mode (modern alternative to keyboardType) | | `enterKeyHint` | RNTextInputProps['enterKeyHint'] | No | | Enter key hint | | `selectionColor` | string | No | | Color of the text selection handles and highlight | | `showSoftInputOnFocus` | boolean | No | | Whether to show the soft keyboard on focus | | `editable` | boolean | No | | Whether the field is editable | | `caretHidden` | boolean | No | | Hide the blinking text caret (useful for select-like, non-editable fields) | | `placement` | 'auto' \| 'top' \| 'bottom' \| 'left' \| 'right' \| 'top-start' \| 'top-end' \| 'bottom-start' \| 'bottom-end' \| 'left-start' \| 'left-end' \| 'right-start' \| 'right-end' | No | | Placement preference for the suggestions dropdown (default: 'bottom-start') | | `flip` | boolean | No | | Enable flipping to opposite side when dropdown would go off-screen (default: true) | | `shift` | boolean | No | | Enable shifting within bounds when dropdown would go off-screen (default: true) | | `boundary` | number | No | | Distance from viewport edges in pixels (default: 12) | | `autoReposition` | boolean | No | | Enable automatic repositioning on scroll/resize (default: true) | | `labelProps` | Omit | No | | Override props applied to the field label ``. | | `descriptionProps` | Omit | No | | Override props applied to the field description ``. | | `placeholderTextColor` | string | No | | Color of the placeholder text (defaults to `theme.text.muted`). | | `startSectionProps` | Omit | No | | View props applied to the wrapper around startSection (chip area, etc.). | | `endSectionProps` | Omit | No | | View props applied to the wrapper around endSection (clear button area). | | `m` | number | No | | Margin applied to all sides | | `mt` | number | No | | Margin applied to the top side | | `mr` | number | No | | Margin applied to the right side | | `mb` | number | No | | Margin applied to the bottom side | | `ml` | number | No | | Margin applied to the left side | | `mx` | number | No | | Horizontal margin applied to left and right sides | | `my` | number | No | | Vertical margin applied to top and bottom sides | | `p` | number | No | | Padding applied to all sides | | `pt` | number | No | | Padding applied to the top side | | `pr` | number | No | | Padding applied to the right side | | `pb` | number | No | | Padding applied to the bottom side | | `pl` | number | No | | Padding applied to the left side | | `px` | number | No | | Horizontal padding applied to left and right sides | | `py` | number | No | | Vertical padding applied to top and bottom sides | | `fullWidth` | boolean | No | | Makes the component fill the full width of its parent | | `w` | DimensionValue | No | | Sets a specific width | | `h` | DimensionValue | No | | Sets a specific height | | `maxW` | DimensionValue | No | | Sets the maximum width | | `minW` | DimensionValue | No | | Sets the minimum width | | `maxH` | DimensionValue | No | | Sets the maximum height | | `minH` | DimensionValue | No | | Sets the minimum height | ## Examples ### Basic ID: `AutoComplete.basic` β€’ Tags: basic, getting-started, search β€’ Category: usage β€’ Status: stable β€’ Since: 1.0.0 Simple auto-complete. Start typing to filter the list. Selecting an option fills the input. ```tsx const [inputValue, setInputValue] = useState(''); const [selectedSport, setSelectedSport] = useState(null); const displayValue = useMemo(() => selectedSport?.label ?? inputValue, [selectedSport, inputValue]); return ( { setInputValue(value); if (!value) setSelectedSport(null); }} onSelect={(item) => { setSelectedSport(item); setInputValue(item.label); }} displayProperty="label" minSearchLength={1} /> ); } ``` ### Multi-select tags ID: `AutoComplete.multi` β€’ Tags: multi-select, multiple, selection β€’ Category: features β€’ Status: stable β€’ Since: 1.0.0 Tap an item to add or remove it. Selected genres render as removable chips. ```tsx const [inputValue, setInputValue] = useState('') const [selectedGenres, setSelectedGenres] = useState([]) const handleToggle = (option: AutoCompleteOption) => { const isSelected = selectedGenres.some((genre) => genre.value === option.value) setSelectedGenres((current) => isSelected ? current.filter((genre) => genre.value !== option.value) : [...current, option], ) } return ( { setSelectedGenres([]) setInputValue('') }} selectedValuesContainerStyle={{ flexWrap: 'wrap', gap: 6 }} renderSelectedValue={(item, _index, helpers) => ( } onRemove={helpers.onRemove} > {item.label} )} inputWidth={400} /> ) } ``` ### Select-Like Behavior ID: `AutoComplete.select-like` β€’ Tags: select, dropdown, focus, options β€’ Category: usage β€’ Status: stable β€’ Since: 1.0.0 Behaves like a Select: the field is non-editable (`editable={false}`), so it can't be typed into or filtered. Tapping opens the full option list (`filter={() => true}`) and the value is chosen from it. ```tsx const [inputValue, setInputValue] = useState('') const [selectedCountry, setSelectedCountry] = useState(null) return ( { setInputValue(value) if (!value) setSelectedCountry(null) }} onSelect={(item) => { setSelectedCountry(item) setInputValue(item.label) }} minSearchLength={0} maxSuggestions={countries.length} editable={false} caretHidden filter={() => true} highlightMatches={false} fullWidth /> ) } ``` ### Async auto-complete ID: `AutoComplete.async` β€’ Tags: async, loading, api, remote β€’ Category: features β€’ Status: stable β€’ Since: 1.0.0 Performs a debounced search against a simulated API before returning matches. ```tsx const searchLanguages = async (query: string): Promise => { await new Promise((resolve) => setTimeout(resolve, 400)) const normalized = query.toLowerCase() return programmingLanguages.filter((language) => language.label.toLowerCase().includes(normalized), ) } const [inputValue, setInputValue] = useState('') const [selectedLanguage, setSelectedLanguage] = useState(null) return ( { setInputValue(value) if (!value) setSelectedLanguage(null) }} onSelect={(item) => { setSelectedLanguage(item) setInputValue(item.label) }} minSearchLength={2} searchDelay={300} highlightMatches fullWidth /> ) } ``` ### Free Solo ID: `AutoComplete.freesolo` β€’ Tags: freesolo, custom, text-input β€’ Category: features β€’ Status: stable β€’ Since: 1.0.0 Suggests fruits while still accepting custom values. ```tsx const [value, setValue] = useState('') return ( setValue(item.label)} freeSolo minSearchLength={0} fullWidth /> Current value: {value || '(empty)'} ) } ``` ### Free Solo (multi-select) ID: `AutoComplete.freesolo-multi` β€’ Tags: freesolo, multi-select, custom, tags β€’ Category: features β€’ Status: stable β€’ Since: 1.0.0 Suggests fruits but lets you add any custom value as a tag β€” press Enter to add what you typed. ```tsx const [inputValue, setInputValue] = useState('') const [selected, setSelected] = useState([]) const handleToggle = (option: AutoCompleteOption) => { const isSelected = selected.some((item) => item.value === option.value) setSelected((current) => isSelected ? current.filter((item) => item.value !== option.value) : [...current, option], ) } return ( { setSelected([]) setInputValue('') }} selectedValuesContainerStyle={{ flexWrap: 'wrap', gap: 6 }} renderSelectedValue={(item, _index, helpers) => ( } onRemove={helpers.onRemove} > {item.label} )} /> ) } ``` ### Grouped suggestions ID: `AutoComplete.grouped` β€’ Tags: grouped, categories, sections β€’ Category: features β€’ Status: stable β€’ Since: 1.0.0 Countries are organized by region to make large lists easier to scan. ```tsx const [value, setValue] = useState('') const [selectedCountry, setSelectedCountry] = useState(null) return ( { setValue(next) if (!next) setSelectedCountry(null) }} onSelect={(item) => { setSelectedCountry(item) setValue(item.label) }} minSearchLength={1} highlightMatches fullWidth /> ) } ``` ### Rich Content ID: `AutoComplete.rich` β€’ Tags: rich, custom-render, complex β€’ Category: features β€’ Status: stable β€’ Since: 1.0.0 AutoComplete with custom rendering and complex data structures. ```tsx interface RichSportOption { label: string; value: string; emoji: string; color: string; price: number; // Avg ticket price duration: string; // Typical game length / format } // The option colors are plain 6-digit hex, so an 8-digit suffix gives a tint // that works on both web and native without a color library. const tint = (hex: string, alpha: string) => `${hex}${alpha}` const [value, setValue] = useState('') const [selectedSport, setSelectedSport] = useState(null) const richSportData: RichSportOption[] = [ { label: 'Soccer', value: 'soccer', emoji: '⚽', color: '#22c55e', price: 75.5, duration: '90 min' }, { label: 'Basketball', value: 'basketball', emoji: 'πŸ€', color: '#f97316', price: 120.0, duration: '48 min' }, { label: 'Football', value: 'football', emoji: '🏈', color: '#92400e', price: 180.0, duration: '60 min' }, { label: 'Volleyball', value: 'volleyball', emoji: '🏐', color: '#fbbf24', price: 60.0, duration: 'Best of 5' }, { label: 'Baseball', value: 'baseball', emoji: '⚾', color: '#ef4444', price: 85.0, duration: '9 innings' }, { label: 'Golf', value: 'golf', emoji: 'β›³', color: '#15803d', price: 110.0, duration: '4 hrs' }, ]; // Colored emoji tile β€” carries the option color instead of a loose dot, and // doubles as the leading media for both the dropdown row and the input value. const renderTile = (sport: RichSportOption, size: number) => ( = 40 ? 'xl' : 'md'}>{sport.emoji} ) return ( { setValue(next) if (!next) setSelectedSport(null) }} onSelect={(item) => { const sport = item as RichSportOption setSelectedSport(sport) setValue(sport.label) }} // Media / title+meta / trailing price β€” the standard three-slot list row, // so the eye scans names down the left and prices down the right. renderItem={(item, index, helpers) => { const sport = item as RichSportOption const isChosen = selectedSport?.value === sport.value return ( helpers?.onSelect?.(sport)} style={{ alignItems: 'stretch', gap: 0 }} > {renderTile(sport, 40)} {sport.label} {sport.duration} ${sport.price.toFixed(2)} avg ticket {isChosen ? ( ) : ( )} ) }} // Chosen sport inside the input box β€” same tile, single line so it fits // the field height. renderValue={(item) => { const sport = item as RichSportOption return ( {renderTile(sport, 24)} {sport.label} {sport.duration} ${sport.price.toFixed(2)} ) }} minSearchLength={1} fullWidth /> ) } ``` ### Highlight colours ID: `AutoComplete.highlight-colors` β€’ Tags: highlight, palette, theme β€’ Category: styling β€’ Status: stable β€’ Since: 1.0.0 `highlightMatches` bolds and tints the part of each suggestion that matches what you typed. That tint is derived from `theme.colors.primary` by default; pass `highlightColor` (and optionally `highlightBackgroundColor`) to override it β€” here with shades from `theme.colors.highlight`. Pick a swatch while the menu is open to see the match repaint. ```tsx const sampleData = [ { label: 'Apple', value: 'apple', description: 'A red or green fruit' }, { label: 'Banana', value: 'banana', description: 'A yellow curved fruit' }, { label: 'Cherry', value: 'cherry', description: 'A small red fruit' }, { label: 'Date', value: 'date', description: 'A sweet brown fruit' }, { label: 'Elderberry', value: 'elderberry', description: 'A dark purple berry' }, { label: 'Fig', value: 'fig', description: 'A purple or green fruit' }, { label: 'Grape', value: 'grape', description: 'Clusters of small berries' }, { label: 'Honeydew', value: 'honeydew', description: 'A sweet green melon' }, ] const theme = useTheme() const isDark = theme.colorScheme === 'dark' // The matched substring sits on the menu surface, so only the shades with // enough contrast against it are offered: dark end of the ramp on light // surfaces, light end on dark ones. const ramp = theme.colors.highlight ?? [] const shadeIndices = isDark ? [3, 4, 5, 6] : [6, 7, 8, 9] const swatches = shadeIndices.map(index => ramp[index]).filter(Boolean) // `undefined` = no override, i.e. the primary-derived default AutoComplete // uses when `highlightColor` is omitted. const [highlightColor, setHighlightColor] = useState(undefined) const tintIndex = isDark ? 8 : 1 return ( Match colour setHighlightColor(undefined)} accessibilityLabel="Theme default highlight colour" /> {swatches.map(color => ( setHighlightColor(color)} accessibilityLabel={`Highlight colour ${color}`} /> ))} Type a letter or two, then pick a swatch. The first is the default (primary ramp); the rest come from `theme.colors.highlight`, paired with a soft tint from the same ramp as the match background. ) } ``` -------------------------------------------------------------------------------- # Avatar The Avatar component displays user profile images, initials, or icons. Supports different sizes, colors, and can be grouped together in an AvatarGroup. Each text slot β€” initials, label, description β€” accepts the full Text-prop API via `fallbackProps` / `labelProps` / `descriptionProps`. ## Metadata - Canonical name: `Avatar` - Package: `@platform-blocks/react-ui-library` - Import: `import { Avatar } from '@platform-blocks/react-ui-library';` - Category: display - Tags: avatar, profile, user, image, initials - Docs: https://react-ui-library.com/components/Avatar - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Avatar ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `size` | ComponentSizeValue | No | | Size of the avatar | | `src` | string \| ImageSourcePropType | No | | Image for the avatar: a remote URL string or a bundled asset (`require('./avatar.png')`) | | `fallback` | React.ReactNode | No | | Fallback shown when no image is provided: initials string or a custom React node (e.g. an icon). | | `backgroundColor` | string | No | | Background color for the fallback initials | | `textColor` | string | No | | Text color for the fallback initials | | `online` | boolean | No | | Whether to show online status indicator | | `indicatorColor` | string | No | | Color override for the status indicator | | `style` | StyleProp | No | | Style override for the avatar container | | `accessibilityLabel` | string | No | | Accessibility label for the avatar image | | `label` | React.ReactNode | No | | Primary label displayed to the right of the avatar (string or custom React node) | | `description` | React.ReactNode | No | | Secondary description/subtext under the label | | `gap` | number | No | | Spacing between avatar and text block | | `showText` | boolean | No | | Force horizontal layout off (set false to hide label/description wrapper) | | `fallbackProps` | Omit | No | | Override props applied to the fallback initials `` (style, weight, ff, size, color). | | `labelProps` | Omit | No | | Override props applied to the adjacent label `` (only when `label` is a string). | | `descriptionProps` | Omit | No | | Override props applied to the secondary description `` (only when `description` is a string). | ## Examples ### Basics ID: `Avatar.basic` β€’ Tags: avatars β€’ Category: basics β€’ Status: stable β€’ Since: 1.0.0 Illustrates loading an avatar image with a reliable initials fallback for offline scenarios. ```tsx return ( ) } ``` ### Sizes ID: `Avatar.sizes` β€’ Tags: avatars, sizes β€’ Category: styling β€’ Status: stable β€’ Since: 1.0.0 Display every avatar size token with guidance on when to use each scale. ```tsx const SIZES = ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl'] as const; return ( {SIZES.map((size) => ( ))} ); } ``` ### Icon ID: `Avatar.icon` β€’ Tags: avatars, icon β€’ Category: content β€’ Status: stable β€’ Since: 1.0.0 Render any `` inside an avatar by passing it to the `fallback` prop. The icon scales with the avatar size and works alongside labels, descriptions, and status indicators. ```tsx return ( Render an icon inside the avatar via the `fallback` prop } backgroundColor="#6366f1" /> } backgroundColor="#10b981" /> } backgroundColor="#f59e0b" /> } backgroundColor="#ef4444" /> Scales with the avatar size } backgroundColor="#6366f1" /> } backgroundColor="#6366f1" /> } backgroundColor="#6366f1" /> } backgroundColor="#6366f1" /> Icon avatar with label and online status } backgroundColor="#6366f1" label="Jane Doe" description="Product Designer" online /> ); } ``` ### Colors ID: `Avatar.colors` β€’ Tags: avatars, colors β€’ Category: styling β€’ Status: stable β€’ Since: 1.0.0 Preview semantic color tokens and custom hex backgrounds applied to avatar fallbacks. ```tsx return ( ) } ``` ### Groups ID: `Avatar.group` β€’ Tags: avatars, groups β€’ Category: features β€’ Status: stable β€’ Since: 1.0.0 Showcases how `AvatarGroup` overlaps avatars by default to conserve space. ```tsx const TEAM = [ { id: 1, initials: 'SJ', color: '#FF6B6B' }, { id: 2, initials: 'MC', color: '#4ECDC4' }, { id: 3, initials: 'ER', color: '#45B7D1' }, { id: 4, initials: 'DL', color: '#96CEB4' }, { id: 5, initials: 'KP', color: '#FFEAA7' }, { id: 6, initials: 'TW', color: '#DDA0DD' }, { id: 7, initials: 'AB', color: '#FFB6C1' } ]; return ( Simple group {TEAM.map(({ id, initials, color }) => ( ))} Groups overlap avatars automatically to conserve space. ); } ``` ### Overflow ID: `Avatar.overflow` β€’ Tags: avatars, groups, overflow, limit, tooltip β€’ Category: features β€’ Status: stable β€’ Since: 1.0.0 Set `limit` to cap visible avatars and show the remaining count. Pass `surplusTooltip` to reveal who's hidden on hover. ```tsx const TEAM = [ { id: 1, name: 'Sarah Johnson', initials: 'SJ', color: '#FF6B6B' }, { id: 2, name: 'Marcus Chen', initials: 'MC', color: '#4ECDC4' }, { id: 3, name: 'Elena Ruiz', initials: 'ER', color: '#45B7D1' }, { id: 4, name: 'David Lee', initials: 'DL', color: '#96CEB4' }, { id: 5, name: 'Kira Patel', initials: 'KP', color: '#FFEAA7' }, { id: 6, name: 'Tom Ward', initials: 'TW', color: '#DDA0DD' }, { id: 7, name: 'Aisha Bello', initials: 'AB', color: '#FFB6C1' } ]; const LIMIT = 3; const hidden = TEAM.slice(LIMIT).map((member) => member.name); return ( {TEAM.map(({ id, initials, color }) => ( ))} ); } ``` ### Status indicator ID: `Avatar.status` β€’ Tags: avatars, status β€’ Category: behavior β€’ Status: stable β€’ Since: 1.0.0 Demonstrates the `online` presence indicator, including custom `indicatorColor` overrides for alternate states. ```tsx type StatusAvatar = Pick & { key: string; label: string; description: string; }; const STATUS_AVATARS: StatusAvatar[] = [ { key: 'online', label: 'Josh', description: 'Online', src: require('../../../../assets/avatars/avatar-1.png') }, { key: 'available', label: 'Alice', description: 'Available', src: require('../../../../assets/avatars/avatar-2.png') }, { key: 'focus', label: 'Mike', description: 'Focus time', src: require('../../../../assets/avatars/avatar-3.png'), indicatorColor: '#f59e0b' }, { key: 'offline', label: 'Tori', description: 'Last active 5m ago', src: require('../../../../assets/avatars/avatar-4.png'), online: false } ]; return ( {STATUS_AVATARS.map(({ key, indicatorColor, online = true, ...avatar }) => ( ))} ); } ``` -------------------------------------------------------------------------------- # Badge The Badge component displays compact elements that represent an input, attribute, or action. Supports different colors, sizes, and interactive features like removal. Inner label accepts the full Text-prop API via `labelProps`. ## Metadata - Canonical name: `Badge` - Package: `@platform-blocks/react-ui-library` - Import: `import { Badge } from '@platform-blocks/react-ui-library';` - Category: data - Tags: chip, tag, badge, label, removable - Docs: https://react-ui-library.com/components/Badge - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Badge ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `children` | React.ReactNode | Yes | | | | `size` | ComponentSizeValue | No | | | | `variant` | 'filled' \| 'outline' \| 'light' \| 'subtle' \| 'gradient' | No | | | | `v` | 'filled' \| 'outline' \| 'light' \| 'subtle' \| 'gradient' | No | | Shorthand alias for `variant`. `variant` wins when both are set. | | `color` | ThemeColor | No | | Badge color. A palette token, `'primary.6'` shade syntax, or any CSS color. | | `c` | ThemeColor | No | | Shorthand alias for `color`, resolved identically. `color` wins when both are set. | | `onPress` | () => void | No | | | | `startIcon` | React.ReactNode | No | | | | `endIcon` | React.ReactNode | No | | | | `onRemove` | () => void | No | | | | `removePosition` | 'left' \| 'right' | No | | | | `disabled` | boolean | No | | | | `style` | StyleProp | No | | | | `textStyle` | StyleProp | No | | | | `labelProps` | Omit | No | | Override props applied to the inner label `` (style, weight, ff, size, color). | | `radius` | any | No | | | | `shadow` | any | No | | | | `m` | number | No | | Margin applied to all sides | | `mt` | number | No | | Margin applied to the top side | | `mr` | number | No | | Margin applied to the right side | | `mb` | number | No | | Margin applied to the bottom side | | `ml` | number | No | | Margin applied to the left side | | `mx` | number | No | | Horizontal margin applied to left and right sides | | `my` | number | No | | Vertical margin applied to top and bottom sides | | `p` | number | No | | Padding applied to all sides | | `pt` | number | No | | Padding applied to the top side | | `pr` | number | No | | Padding applied to the right side | | `pb` | number | No | | Padding applied to the bottom side | | `pl` | number | No | | Padding applied to the left side | | `px` | number | No | | Horizontal padding applied to left and right sides | | `py` | number | No | | Vertical padding applied to top and bottom sides | ## Examples ### Basics ID: `Badge.basic` β€’ Tags: badge, getting-started β€’ Category: basics β€’ Status: stable β€’ Since: 1.0.0 Wrap any label in a Badge to render a compact tag β€” the default `filled` variant and `primary` color apply automatically. ```tsx return ( New Beta v1.0 ) } ``` ### Semantic colors ID: `Badge.colors` β€’ Tags: colors, theming β€’ Category: theming β€’ Status: stable β€’ Since: 1.0.0 Set the `color` prop to tokens such as `primary`, `success`, `warning`, `error`, or `gray` to align Badges with semantic meaning instead of hard-coded hex values. ```tsx return ( Primary Success Warning Error Gray ) } ``` ### Size scale ID: `Badge.sizes` β€’ Tags: sizes, density β€’ Category: layout β€’ Status: stable β€’ Since: 1.0.0 Adjust the `size` prop (`xs` through `3xl`, or a number) to match the density of the surrounding UI. ```tsx const SIZES = ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl'] as const; return ( {SIZES.map((size) => ( Badge {size} ))} ); } ``` ### Variant styles ID: `Badge.variants` β€’ Tags: variants, styling β€’ Category: appearance β€’ Status: stable β€’ Since: 1.0.0 Pick a `variant` like `filled`, `outline`, `light`, `subtle`, or `gradient` when you need to shift emphasis without changing the Badge content. ```tsx return ( Filled Outline Light Subtle Gradient ) } ``` ### Shadow depth ID: `Badge.shadow` β€’ Tags: shadow, emphasis β€’ Category: appearance β€’ Status: stable β€’ Since: 1.0.0 Use the `shadow` prop (`none` through `xl`) to raise a Badge when it needs extra emphasis over surrounding UI. ```tsx return ( No Shadow XS Shadow SM Shadow MD Shadow LG Shadow XL Shadow ) } ``` ### Prop aliases ID: `Badge.aliases` β€’ Tags: aliases, shorthand β€’ Category: props β€’ Status: stable β€’ Since: 1.0.0 Shorthand props `v` and `c` mirror `variant` and `color`, so you can write more compact JSX without losing any functionality. ```tsx const badges = [ { label: 'Primary Filled', variant: 'filled', color: 'primary' }, { label: 'Secondary Outline', variant: 'outline', color: 'secondary' }, { label: 'Success Light', variant: 'light', color: 'success' }, { label: 'Warning Subtle', variant: 'subtle', color: 'warning' }, ] as const return ( {badges.map((badge) => ( {badge.label} ))} {badges.map((badge) => ( {badge.label} ))} ) } ``` -------------------------------------------------------------------------------- # Block A polymorphic building block component that serves as a foundational element to replace View components throughout the application. Similar to a `
` in web development. The `bg` prop resolves through the theme β€” same lookup rules as ``. ## Metadata - Canonical name: `Block` - Package: `@platform-blocks/react-ui-library` - Import: `import { Block } from '@platform-blocks/react-ui-library';` - Status: stable - Since: 1.0.0 - Category: layout - Tags: layout, building-block, polymorphic, foundational - Docs: https://react-ui-library.com/components/Block - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Block ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `children` | React.ReactNode | No | | Child elements to render inside the block | | `component` | React.ElementType | No | | The component to render as | | `style` | StyleProp | No | | Custom style object | | `testID` | string | No | | Test ID for testing purposes | | `accessibilityLabel` | string | No | | Accessibility label | | `accessible` | boolean | No | | Whether the element is accessible | | `accessibilityRole` | string | No | | Accessibility role | | `className` | string | No | | Custom className (for web) | | `m` | number | No | | Margin applied to all sides | | `mt` | number | No | | Margin applied to the top side | | `mr` | number | No | | Margin applied to the right side | | `mb` | number | No | | Margin applied to the bottom side | | `ml` | number | No | | Margin applied to the left side | | `mx` | number | No | | Horizontal margin applied to left and right sides | | `my` | number | No | | Vertical margin applied to top and bottom sides | | `p` | number | No | | Padding applied to all sides | | `pt` | number | No | | Padding applied to the top side | | `pr` | number | No | | Padding applied to the right side | | `pb` | number | No | | Padding applied to the bottom side | | `pl` | number | No | | Padding applied to the left side | | `px` | number | No | | Horizontal padding applied to left and right sides | | `py` | number | No | | Vertical padding applied to top and bottom sides | | `bg` | string | No | | Background color for the block | | `radius` | number \| 'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| 'full' | No | | Border radius for rounded corners | | `borderWidth` | number | No | | Border width | | `borderColor` | string | No | | Border color | | `shadow` | number \| 'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl' | No | | Shadow depth (0-5) | | `opacity` | number | No | | Opacity (0-1) | | `w` | number \| string \| 'auto' \| 'full' | No | | Width of the block | | `h` | number \| string \| 'auto' \| 'full' | No | | Height of the block | | `fullWidth` | boolean | No | | Whether to take full width (100%) - shorthand for w="full" | | `fluid` | boolean | No | | Makes block take full available height (flex: 1) - useful for scrollable containers | | `minW` | number \| string | No | | Minimum width | | `minH` | number \| string | No | | Minimum height | | `maxW` | number \| string | No | | Maximum width | | `maxH` | number \| string | No | | Maximum height | | `grow` | boolean \| number | No | | Flex grow | | `shrink` | boolean \| number | No | | Flex shrink | | `basis` | number \| string | No | | Flex basis | | `direction` | 'row' \| 'column' \| 'row-reverse' \| 'column-reverse' | No | | Flex direction | | `align` | 'stretch' \| 'flex-start' \| 'flex-end' \| 'center' \| 'baseline' | No | | Align items | | `justify` | 'flex-start' \| 'flex-end' \| 'center' \| 'space-between' \| 'space-around' \| 'space-evenly' | No | | Justify content | | `wrap` | boolean \| 'nowrap' \| 'wrap' \| 'wrap-reverse' | No | | Flex wrap | | `gap` | number \| 'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl' | No | | Gap between children. Defaults to `'sm'`; pass `0` to remove it. | | `position` | 'relative' \| 'absolute' | No | | Position type | | `top` | number \| string | No | | Top position | | `right` | number \| string | No | | Right position | | `bottom` | number \| string | No | | Bottom position | | `left` | number \| string | No | | Left position | | `start` | number \| string | No | | Start position (logical property - becomes left in LTR, right in RTL) | | `end` | number \| string | No | | End position (logical property - becomes right in LTR, left in RTL) | | `zIndex` | number | No | | Z-index | | `flex` | boolean | No | | Whether to render as a flex container | ## Examples ### Basic usage ID: `Block.basic` β€’ Tags: layout, polymorphic β€’ Category: basics β€’ Status: stable β€’ Since: 0.3.0 Combine spacing, layout, and polymorphic props on `Block` to build cards, responsive rows, and button-style actions without custom wrappers. ```tsx return ( Release summary Apply `bg`, `p`, and `radius` props on `Block` to build a card without custom stylesheets. Velocity Use `grow` so sibling Blocks share remaining space. Backlog Combine fixed widths with flexible layouts via the `w` prop. Create project View roadmap ); } ``` ### bg shorthand ID: `Block.bg-shorthand` β€’ Tags: bg, theme, shorthand, customization β€’ Category: general β€’ Status: stable β€’ Since: 1.0.0 `bg` resolves through the theme. Pass a palette name (`'primary'`, `'success'`) for a subtle tint (shade-1), a `'palette.shade'` like `'primary.6'` for a specific shade, a theme-background key (`'surface'`, `'subtle'`, `'elevated'`), or any CSS color string. The same resolver powers ``. ```tsx return ( Palette names β†’ subtle tint (shade-1) primary success warning error Specific shade with `palette.shade` syntax primary.6 gray.2 Theme background keys surface subtle Plain CSS color string still works Custom hex ); } ``` -------------------------------------------------------------------------------- # Blockquote The Blockquote component is used to highlight and stylize quotations or important text within your content. It supports various styles and can be customized to fit the design of your application. ## Metadata - Canonical name: `Blockquote` - Package: `@platform-blocks/react-ui-library` - Import: `import { Blockquote } from '@platform-blocks/react-ui-library';` - Category: typography - Tags: blockquote, text, typography - Docs: https://react-ui-library.com/components/Blockquote - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Blockquote ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `children` | React.ReactNode | Yes | | Core content | | `variant` | 'default' \| 'testimonial' \| 'featured' \| 'minimal' | No | | Styling | | `size` | SizeValue | No | | | | `color` | string | No | | | | `quoteIcon` | string \| React.ReactNode | No | | Quote icon | | `quoteIconPosition` | 'top-left' \| 'top-center' \| 'bottom-right' \| 'none' | No | | | | `quoteIconSize` | SizeValue | No | | | | `author` | BlockquoteAuthor | No | | Author attribution | | `links` | BlockquoteLinks | No | | Social/profile links | | `date` | Date \| string | No | | Metadata | | `rating` | BlockquoteRating | No | | | | `source` | BlockquoteSource | No | | Brand/source | | `verified` | boolean | No | | Verification | | `verifiedTooltip` | string | No | | | | `alignment` | 'left' \| 'center' \| 'right' | No | | Layout | | `attributionAlignment` | 'left' \| 'center' \| 'right' | No | | Which side the attribution block (avatar, name, source, meta) sits on. Defaults to `'right'`, or `'center'` when `alignment` is `'center'`. | | `border` | boolean | No | | | | `shadow` | boolean | No | | | | `style` | StyleProp | No | | Standard props | | `onPress` | () => void | No | | | ## Examples ### Pull quote ID: `Blockquote.basic` β€’ Tags: blockquote, testimonial β€’ Category: content β€’ Status: stable β€’ Since: 1.0.0 Frames a simple pull quote with author details. ```tsx const AUTHOR = { name: 'Jamie Ortega', title: 'Principal Product Designer', }; return (
The Blockquote component keeps editorial typography consistent so our brand voice always feels elevated.
); } ``` ### Testimonial card ID: `Blockquote.testimonial` β€’ Tags: blockquote, testimonial β€’ Category: content β€’ Status: stable β€’ Since: 1.0.0 Full-fidelity testimonial with avatar, organization, rating, verified badge, and shadow. ```tsx return (
React UI Library helped us ship an entirely new settings experience in a single sprint. The components feel native on every platform.
); } ``` ### Social proof ID: `Blockquote.social` β€’ Tags: blockquote, social β€’ Category: content β€’ Status: stable β€’ Since: 1.0.0 Maps social-style quotes into `Blockquote` with avatars, verification, and network metadata. ```tsx return (
The future is going to be wild πŸš€
Just finished testing the new React UI Library UI library. The component quality and developer experience is outstanding!
This library has saved us countless hours of development time. Clean API, great documentation, and excellent TypeScript support.
); } ``` ### Attribution side ID: `Blockquote.attribution` β€’ Tags: blockquote, attribution, layout β€’ Category: content β€’ Status: stable β€’ Since: 1.0.0 Attribution sits on the right by default. Use `attributionAlignment` to move the avatar, name, and metadata to the left or center it under the quote. ```tsx return ( Right (default)
{QUOTE}
Left
{QUOTE}
); } ``` ### Variants overview ID: `Blockquote.variants` β€’ Tags: blockquote, variants β€’ Category: content β€’ Status: stable β€’ Since: 1.0.0 Renders each preset to compare layout, alignment, and metadata options. ```tsx return ( Default
The best way to predict the future is to create it.
Testimonial
Great experience with this service. The team was professional and delivered quality results.
Featured
Imagination is more important than knowledge.
Minimal
Just discovered this amazing new feature! πŸš€
); } ``` -------------------------------------------------------------------------------- # BrandButton The BrandButton component renders a branded pressable for any platform in the brand icon registry. By default it's a single-line button supporting variants, sizes, icons, loading state, and full-width layout. Pass `primaryText` and `secondaryText` instead of `title` and it renders the two-line store-badge layout ("Download on the / App Store"). ## Metadata - Canonical name: `BrandButton` - Package: `@platform-blocks/react-ui-library` - Import: `import { BrandButton } from '@platform-blocks/react-ui-library';` - Category: input - Tags: action, pressable, interactive, badge, app-store - Docs: https://react-ui-library.com/components/BrandButton - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/BrandButton ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `brand` | BrandPlatform | Yes | | The brand/platform to style the button for | | `iconPosition` | 'left' \| 'right' | No | | Position of the brand icon | | `iconVariant` | 'full' \| 'mono' | No | | Icon variant: 'full' for multi-color, 'mono' for single-color outline | | `icon` | React.ReactNode | No | | Override the default brand icon | | `title` | string | No | | Button text. Omit when rendering a store badge. | | `color` | string | No | | Override icon color (overrides brand default colors) | | `primaryText` | string | No | | Badge lead-in line, e.g. "Download on the" / "Listen on". Supplying this or `secondaryText` switches the component to the two-line store-badge layout, where `variant`, `loading`, `fullWidth` and the spacing props do not apply. | | `secondaryText` | string | No | | Badge headline, e.g. "App Store" / "Spotify" | | `backgroundColor` | string | No | | Badge shell background (badge layout only) | | `borderColor` | string | No | | Badge shell border color (badge layout only) | | `darkMode` | boolean | No | | Force the badge's dark-mode styling instead of following the theme | | `key` | React.Key | No | | allow React key without complaint in TS where JSX key is forwarded in type checking | | `children` | React.ReactNode | No | | Button text content - alternative to title prop | | `onPress` | () => void | No | | Called when the button is pressed | | `onPressIn` | () => void | No | | Called when the button press starts (for immediate feedback) | | `onPressOut` | () => void | No | | Called when the button press ends | | `onHoverIn` | () => void | No | | Called when the button is hovered (web/desktop only) | | `onHoverOut` | () => void | No | | Called when the button is no longer hovered (web/desktop only) | | `onLongPress` | () => void | No | | Called when the button is long-pressed | | `onLayout` | (event: any) => void | No | | Called when the button layout is calculated | | `variant` | 'default' \| 'filled' \| 'light' \| 'subtle' \| 'secondary' \| 'outline' \| 'ghost' \| 'gradient' \| 'link' \| 'none' | No | 'default' | Button visual variant. `default` is a neutral button β€” the card surface with a hairline border and body text β€” so an unstyled ` ); } ``` ### Loading state ID: `Button.loading` β€’ Tags: buttons, loading β€’ Category: feedback β€’ Status: stable β€’ Since: 1.0.0 Demonstrates consistent width preservation, custom `loadingTitle`, and disabling actions while background work completes. ```tsx const LOADING_DURATION_MS = 2000; const timeoutRef = useRef | null>(null); const [activeKey, setActiveKey] = useState(null); useEffect(() => () => { if (timeoutRef.current) { clearTimeout(timeoutRef.current); } }, []); const triggerLoading = (key: string) => { if (timeoutRef.current) { clearTimeout(timeoutRef.current); } setActiveKey(key); timeoutRef.current = setTimeout(() => { setActiveKey(null); timeoutRef.current = null; }, LOADING_DURATION_MS); }; return ( ); } ``` ### Variants ID: `Button.variants` β€’ Tags: buttons, variants β€’ Category: styling β€’ Status: stable β€’ Since: 1.0.0 Preview the available button variants to match the desired emphasis level. ```tsx return ( ); } ``` ### Sizes ID: `Button.sizes` β€’ Tags: buttons, sizes β€’ Category: styling β€’ Status: stable β€’ Since: 1.0.0 Preview the available button size tokens for different density requirements. ```tsx return ( ); } ``` ### Localized labels ID: `Button.localized-basic` β€’ Tags: buttons, i18n β€’ Category: accessibility β€’ Status: stable β€’ Since: 1.0.0 Switch locales at runtime and render translated button copy with `useI18n` helpers. ```tsx const LOCALES = [ { label: 'English', value: 'en' }, { label: 'EspaΓ±ol', value: 'es' }, { label: 'FranΓ§ais', value: 'fr' }, ]; const { t, locale, setLocale } = useI18n(); return ( ) }); }; return ( ); } ``` ### Confirmation ID: `Dialog.confirmation` β€’ Tags: dialog, confirmation, destructive β€’ Category: patterns β€’ Status: stable β€’ Since: 1.0.0 Pair `variant: 'modal'` with a destructive button (`color="error"`) to confirm irreversible actions before calling your business logic. ```tsx const { openDialog, closeDialog } = useDialog(); const showConfirmationDialog = () => { const dialogId = openDialog({ variant: 'modal', title: 'Confirm Action', content: ( Are you sure you want to delete this item? This action cannot be undone. ) }); }; return ( ); } ``` ### Form Dialog ID: `Dialog.form` β€’ Tags: dialog, forms, validation β€’ Category: patterns β€’ Status: stable β€’ Since: 1.0.0 Embed inputs in the dialog `content`, collect values via controlled callbacks, and validate before resolving the promise or calling `closeDialog`. ```tsx const { openDialog, closeDialog } = useDialog(); const nameRef = useRef(null); const showFormDialog = () => { let formData = { name: '', email: '' }; const dialogId = openDialog({ variant: 'modal', title: 'Create Account', // Focus the name field once the open transition settles. `autoFocus: true` // picks the first focusable field automatically, but only on web β€” a ref // works on every platform. autoFocus: nameRef, content: ( Fill in your details to create an account. { formData.name = text; }} /> { formData.email = text; }} /> ) }); }; return ( ); } ``` ### Title customization ID: `Dialog.title-customization` β€’ Tags: titleProps, customization, slot-props β€’ Category: general β€’ Status: stable β€’ Since: 1.0.0 `titleProps` accepts any `` props (`ff`, `weight`, `tracking`, `uppercase`, `size`, `color`, `style`) and applies them to the dialog header without changing the rest of the chrome. The same prop is also accepted by `openDialog({ titleProps })` for imperative dialogs. ```tsx const { openDialog, closeDialog } = useDialog(); const open = (titleProps: any) => { const id = openDialog({ variant: 'modal', title: 'Welcome aboard', titleProps, content: ( Dialog title styled via `titleProps`. ), }); }; return ( ); } ``` -------------------------------------------------------------------------------- # Divider The Divider component provides a visual separator between content sections. Supports horizontal and vertical orientations, four visual variants (`solid`, `dashed`, `dotted`, `gradient`), an aligned `color` vocabulary with a soft default tuned for separators, an `opacity` shorthand, and optional labels. ## Metadata - Canonical name: `Divider` - Package: `@platform-blocks/react-ui-library` - Import: `import { Divider } from '@platform-blocks/react-ui-library';` - Category: layout - Tags: divider, separator, line, section - Docs: https://react-ui-library.com/components/Divider - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Divider ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `orientation` | DividerOrientation | No | | Layout direction of the line. `'horizontal'` spans width; `'vertical'` spans height. Defaults to `'horizontal'`. | | `variant` | DividerVariant | No | | Visual style of the line. `'gradient'` fades transparent β†’ color β†’ transparent. Defaults to `'solid'`. | | `color` | ThemeColor | No | | Line color. Accepts the named tokens `'border'` / `'subtle'` / `'muted'`, a palette name (`'success'` β†’ a shade well below the accent, so a tinted rule still reads as chrome), `'primary.6'` shade syntax, or any CSS color. Defaults to `'border'`. | | `size` | SizeValue \| number | No | | Thickness of the divider (default 1). Accepts a size token or pixel value. | | `opacity` | number | No | | Multiplied with the divider's overall opacity. Convenience prop equivalent to `style={{ opacity }}`. | | `label` | React.ReactNode | No | | Optional content rendered in the middle of the line. | | `labelPosition` | 'left' \| 'center' \| 'right' | No | | Where the `label` sits along the line. Defaults to `'center'`. | | `labelProps` | Omit | No | | Override props applied to the label `` (only when `label` is a string). | | `style` | StyleProp | No | | Style override applied to the outer wrapping `View`. | | `testID` | string | No | | Test identifier forwarded to the wrapping `View`. | | `m` | number | No | | Margin applied to all sides | | `mt` | number | No | | Margin applied to the top side | | `mr` | number | No | | Margin applied to the right side | | `mb` | number | No | | Margin applied to the bottom side | | `ml` | number | No | | Margin applied to the left side | | `mx` | number | No | | Horizontal margin applied to left and right sides | | `my` | number | No | | Vertical margin applied to top and bottom sides | | `p` | number | No | | Padding applied to all sides | | `pt` | number | No | | Padding applied to the top side | | `pr` | number | No | | Padding applied to the right side | | `pb` | number | No | | Padding applied to the bottom side | | `pl` | number | No | | Padding applied to the left side | | `px` | number | No | | Horizontal padding applied to left and right sides | | `py` | number | No | | Vertical padding applied to top and bottom sides | ## Examples ### Basic Usage ID: `Divider.basic` β€’ Tags: solid, dashed β€’ Category: basics β€’ Status: stable β€’ Since: 1.0.0 Insert horizontal dividers between sections to separate content; switch the `variant` prop to toggle between solid and dashed lines. ```tsx return ( Q1 Highlights Revenue grew 12% year over year. Customer retention improved across every region. Product roadmap updates will ship next quarter. ); } ``` ### Color Variants ID: `Divider.colors` β€’ Tags: color, label, variant β€’ Category: theming β€’ Status: stable β€’ Since: 1.0.0 Select a `color` to match semantic palettes and combine it with `label` plus `variant` to fit your divider into content sections. ```tsx const COLORS: Array<{ label: string; tone?: DividerProps['color'] }> = [ { label: 'Border (default)' }, { label: 'Subtle', tone: 'subtle' }, { label: 'Muted', tone: 'muted' }, { label: 'Gray', tone: 'gray' }, { label: 'Primary', tone: 'primary' }, { label: 'Secondary', tone: 'secondary' }, { label: 'Success', tone: 'success' }, { label: 'Warning', tone: 'warning' }, { label: 'Error', tone: 'error' }, ]; return ( Semantic color variants {COLORS.map(({ label, tone }) => ( {label} ))} Labeled dividers Variant styles ); } ``` ### Gradient & opacity ID: `Divider.gradient-opacity` β€’ Tags: gradient, opacity, customization, variants β€’ Category: general β€’ Status: stable β€’ Since: 1.0.0 The `gradient` variant fades transparent β†’ color β†’ transparent, perfect for breaking up sections without a hard edge. The `opacity` prop is a shorthand for `style={{ opacity }}` β€” combine it with `color` to dial in subtle separators. ```tsx return ( Gradient variant Opacity prop β€” same color, different emphasis Subtle separator (border default + low opacity) Custom color + opacity ); } ``` ### Labeled Dividers ID: `Divider.labeled` β€’ Tags: label, labelPosition β€’ Category: layout β€’ Status: stable β€’ Since: 1.0.0 Provide a `label` node and adjust `labelPosition` plus `color` to separate form sections with contextual dividers. ```tsx return ( Sign in with email Continue with social accounts Settings} labelPosition="left" color="secondary" /> Manage notification preferences Invite admins or export account data ); } ``` ### Vertical Dividers ID: `Divider.vertical` β€’ Tags: vertical, navigation, label β€’ Category: layout β€’ Status: stable β€’ Since: 1.0.0 Switch `orientation="vertical"` to separate horizontal layouts like navigation and add `label` or `color` when you need emphasis. ```tsx return ( Profile View details Settings Preferences Support Help center Home Fixtures Standings Highlights ); } ``` ### Sizes ID: `Divider.sizes` β€’ Category: general Demonstrates how the `size` prop accepts both numeric values and spacing tokens so you can dial in subtle, comfortable, or bold divider weights in horizontal and vertical layouts. ```tsx const SIZES = ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl'] as const; return ( {SIZES.map((size) => ( {size} ))} 1 (numeric) ); } ``` ### Variants ID: `Divider.variants` β€’ Category: general Showcase solid, dashed, and dotted dividers in both horizontal and vertical layouts to highlight how each variant can communicate different section breaks. ```tsx return ( Release Notes Default solid divider keeps sections crisp. The winter update introduces a revamped queue and faster syncing. Sprint Checklist Dashed lines work nicely for in-progress flows. QA sign-off, regression pass, and rollout comms are scheduled for Friday. Creator Status Dotted borders add a softer visual break. Enable payouts once verification documents finish processing. Section break Gradient variant fades the line in and out β€” softer than a hard rule. The fade keeps long-form content breathable without dropping a horizontal stripe. Published Drafts Scheduled Archived ); } ``` -------------------------------------------------------------------------------- # FileInput The FileInput component provides a user-friendly interface for file uploads with drag-and-drop functionality, file validation, and preview capabilities. ## Metadata - Canonical name: `FileInput` - Package: `@platform-blocks/react-ui-library` - Import: `import { FileInput } from '@platform-blocks/react-ui-library';` - Status: stable - Category: input - Docs: https://react-ui-library.com/components/FileInput - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/FileInput ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `variant` | 'standard' \| 'dropzone' \| 'compact' | No | | File input variant | | `accept` | string[] | No | | Accepted file types (MIME types or extensions) | | `multiple` | boolean | No | | Multiple file selection | | `maxSize` | number | No | | Maximum file size in bytes | | `maxFiles` | number | No | | Maximum number of files | | `onUpload` | (files: FileInputFile[]) => Promise | No | | Upload handler | | `onProgress` | (fileId: string, progress: number) => void | No | | Upload progress callback | | `onFilesChange` | (files: FileInputFile[]) => void | No | | File change handler | | `onFileRemove` | (fileId: string) => void | No | | File remove handler | | `PreviewComponent` | React.ComponentType<{ file: FileInputFile; onRemove: () => void }> | No | | File preview component | | `children` | React.ReactNode | No | | Custom drop zone content | | `showFileList` | boolean | No | | Whether to show file list | | `enableDragDrop` | boolean | No | | Whether to enable drag and drop | | `validateFile` | (file: File \| DocumentPickerAssetLike) => string \| null | No | | Custom validation function | | `imagePreview` | { enabled?: boolean; maxWidth?: number; maxHeight?: number; quality?: number; } | No | | Image preview settings | | `uploadSettings` | { url?: string; method?: 'POST' \| 'PUT'; headers?: Record; fieldName?: string; formData?: Record; } | No | | Upload settings | | `value` | string | No | | Input value | | `onChangeText` | (text: string) => void | No | | Change handler | | `label` | React.ReactNode | No | | Input label (string or component) | | `disabled` | boolean | No | | Whether input is disabled | | `required` | boolean | No | | Whether input is required | | `placeholder` | string | No | | Input placeholder | | `error` | string | No | | Error message | | `helperText` | string | No | | Helper text | | `description` | string | No | | Optional short description displayed directly under the label (above the field) | | `size` | SizeValue | No | | Input size | | `withAsterisk` | boolean | No | | Whether to show required indicator | | `name` | string | No | | Input name for form integration | | `startSection` | React.ReactNode | No | | Left section content | | `endSection` | React.ReactNode | No | | Right section content | | `style` | any | No | | Additional styling | | `accessibilityLabel` | string | No | | Accessibility label | | `accessibilityHint` | string | No | | Accessibility hint | | `testID` | string | No | | Test ID for testing | | `debounceMs` | number | No | | Debounce delay for validation in milliseconds | | `onFocus` | () => void | No | | Focus handler | | `onBlur` | () => void | No | | Blur handler | | `onEnter` | () => void | No | | Enter key press handler | | `clearable` | boolean | No | | Show built-in clear button when input has value | | `clearButtonLabel` | string | No | | Accessible label for the clear button | | `onClear` | () => void | No | | Callback when the clear button is pressed | | `keyboardFocusId` | string | No | | Identifier used with KeyboardManagerProvider to request refocus | | `labelProps` | Omit | No | | Override props applied to the field label `` (style, weight, ff, etc.) | | `descriptionProps` | Omit | No | | Override props applied to the field description `` | | `placeholderTextColor` | string | No | | Color of the placeholder text. Falls back to `theme.text.muted`. | | `startSectionProps` | Omit | No | | Props applied to the wrapping `` around `startSection` (style, accessibility, etc.). | | `endSectionProps` | Omit | No | | Props applied to the wrapping `` around `endSection`. | | `m` | number | No | | Margin applied to all sides | | `mt` | number | No | | Margin applied to the top side | | `mr` | number | No | | Margin applied to the right side | | `mb` | number | No | | Margin applied to the bottom side | | `ml` | number | No | | Margin applied to the left side | | `mx` | number | No | | Horizontal margin applied to left and right sides | | `my` | number | No | | Vertical margin applied to top and bottom sides | | `p` | number | No | | Padding applied to all sides | | `pt` | number | No | | Padding applied to the top side | | `pr` | number | No | | Padding applied to the right side | | `pb` | number | No | | Padding applied to the bottom side | | `pl` | number | No | | Padding applied to the left side | | `px` | number | No | | Horizontal padding applied to left and right sides | | `py` | number | No | | Vertical padding applied to top and bottom sides | | `fullWidth` | boolean | No | | Makes the component fill the full width of its parent | | `w` | DimensionValue | No | | Sets a specific width | | `h` | DimensionValue | No | | Sets a specific height | | `maxW` | DimensionValue | No | | Sets the maximum width | | `minW` | DimensionValue | No | | Sets the minimum width | | `maxH` | DimensionValue | No | | Sets the maximum height | | `minH` | DimensionValue | No | | Sets the minimum height | | `radius` | RadiusValue | No | | Border radius value - supports size tokens, numbers, and special values | ## Examples ### Basic ID: `FileInput.basic` β€’ Tags: basic, upload, files β€’ Category: basics β€’ Status: stable β€’ Since: 1.0.0 Simple file input with helper text and multiple file selection. ```tsx const [files, setFiles] = useState([]); return ( {files.length > 0 && ( Selected: {files.map((file) => file.name).join(', ')} )} ); } ``` ### Dropzone ID: `FileInput.dropzone` β€’ Tags: dropzone, drag-and-drop, upload β€’ Category: variants β€’ Status: stable β€’ Since: 1.0.0 Drag-and-drop dropzone variant with native fallback instructions and selected file list. ```tsx const [files, setFiles] = useState([]); const instructions = Platform.OS === 'web' ? 'Drag files into the dropzone or click Browse files to pick from your desktop.' : 'Tap the dropzone to open the native file picker on touch devices.'; return ( {instructions} {files.length > 0 && ( Selected files ({files.length}) {files.map((file) => ( {file.name} ))} )} ); } ``` ### File Type Restrictions ID: `FileInput.fileTypes` β€’ Tags: accept, validation, upload β€’ Category: features β€’ Status: stable β€’ Since: 1.0.0 Configure different file inputs with MIME filters, extension lists, and size limits. ```tsx const [imageFiles, setImageFiles] = useState([]); const [documentFiles, setDocumentFiles] = useState([]); const [videoFiles, setVideoFiles] = useState([]); return ( File type restrictions Limit accepted file types per uploader using MIME types, extensions, and size caps. Images only {imageFiles.length > 0 && ( Selected: {imageFiles.map((file) => file.name).join(', ')} )} Documents only {documentFiles.length > 0 && ( Selected: {documentFiles.map((file) => file.name).join(', ')} )} Videos (max 50MB) {videoFiles.length > 0 && ( Selected: {videoFiles.map((file) => file.name).join(', ')} )} ); } ``` ### Image Preview ID: `FileInput.imagePreview` β€’ Category: general Image upload with preview thumbnails and remove functionality. ```tsx const [images, setImages] = useState([]); const handleRemoveFile = (index: number) => { setImages((prev) => prev.filter((_, itemIndex) => itemIndex !== index)); }; return ( {images.length > 0 && ( Selected images ({images.length}) {images.map((file, index) => ( {file.previewUrl && ( {file.name} )} {file.name} ))} )} ); } ``` ### Size Variants ID: `FileInput.variants` β€’ Category: general Different size variants and customization options. ```tsx const sizes = [ { label: 'Small', size: 'sm' as const }, { label: 'Medium (default)', size: 'md' as const }, { label: 'Large', size: 'lg' as const }, ]; const [files, setFiles] = useState>({}); const handleChange = (key: string) => (next: FileInputFile[]) => { setFiles((prev) => ({ ...prev, [key]: next })); }; return ( {sizes.map(({ label, size }) => ( {label} {files[label]?.length ? ( Selected: {files[label].length} ) : null} ))} Custom placeholder ); } ``` ### Upload Progress ID: `FileInput.upload` β€’ Category: general File upload with progress simulation and bulk actions. ```tsx const [files, setFiles] = useState([]); const [isUploading, setIsUploading] = useState(false); const [uploadProgress, setUploadProgress] = useState<{[key: string]: number}>({}); const handleUpload = async () => { if (files.length === 0) return; setIsUploading(true); // Simulate upload process for each file for (const file of files) { // Simulate progress updates for (let progress = 0; progress <= 100; progress += 25) { setUploadProgress(prev => ({ ...prev, [file.name]: progress })); await new Promise(resolve => setTimeout(resolve, 300)); } } setIsUploading(false); alert('Files uploaded successfully!'); setFiles([]); setUploadProgress({}); }; const handleRemove = (index: number) => { setFiles(prev => prev.filter((_, i) => i !== index)); }; return ( {files.length > 0 && ( Selected files {files.map((file, index) => ( {file.name} {(file.size / 1024).toFixed(1)} KB {uploadProgress[file.name] !== undefined && ( Progress: {uploadProgress[file.name]}% )} ))} )} ); } ``` ### Validation & States ID: `FileInput.validation` β€’ Category: general File input with various validation rules and states. ```tsx const [validatedFiles, setValidatedFiles] = useState([]); const [singleFile, setSingleFile] = useState([]); const [limitedFiles, setLimitedFiles] = useState([]); return ( Size validation (max 2MB) {validatedFiles.length > 0 && ( Selected: {validatedFiles.length} )} Single file only {singleFile[0] && ( Selected: {singleFile[0].name} )} Limited file count {limitedFiles.length > 0 && ( Selected: {limitedFiles.length} )} With error state {}} error="Please select at least one file" required fullWidth /> Disabled state {}} disabled fullWidth /> ); } ``` -------------------------------------------------------------------------------- # Flex Flex provides a powerful and intuitive way to create flexible layouts using CSS Flexbox principles. It handles spacing, alignment, and direction with a clean API that works consistently across platforms. ## Metadata - Canonical name: `Flex` - Package: `@platform-blocks/react-ui-library` - Import: `import { Flex } from '@platform-blocks/react-ui-library';` - Status: stable - Category: layout - Docs: https://react-ui-library.com/components/Flex - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Flex ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `direction` | 'row' \| 'column' \| 'row-reverse' \| 'column-reverse' | No | | Flex direction | | `align` | 'flex-start' \| 'flex-end' \| 'center' \| 'stretch' \| 'baseline' | No | | Align items on the cross axis | | `justify` | 'flex-start' \| 'flex-end' \| 'center' \| 'space-between' \| 'space-around' \| 'space-evenly' | No | | Justify content on the main axis | | `wrap` | 'nowrap' \| 'wrap' \| 'wrap-reverse' | No | | Flex wrap | | `gap` | SizeValue | No | | Gap between children (applies to both row and column gap) | | `rowGap` | SizeValue | No | | Row gap between children | | `columnGap` | SizeValue | No | | Column gap between children | | `grow` | number | No | | Flex grow | | `shrink` | number | No | | Flex shrink | | `basis` | DimensionValue | No | | Flex basis | | `children` | React.ReactNode | No | | Children elements | | `style` | StyleProp | No | | Custom styles | | `testID` | string | No | | Test ID for testing | | `disableRTLMirroring` | boolean | No | | Disable automatic RTL mirroring for row direction | | `m` | number | No | | Margin applied to all sides | | `mt` | number | No | | Margin applied to the top side | | `mr` | number | No | | Margin applied to the right side | | `mb` | number | No | | Margin applied to the bottom side | | `ml` | number | No | | Margin applied to the left side | | `mx` | number | No | | Horizontal margin applied to left and right sides | | `my` | number | No | | Vertical margin applied to top and bottom sides | | `p` | number | No | | Padding applied to all sides | | `pt` | number | No | | Padding applied to the top side | | `pr` | number | No | | Padding applied to the right side | | `pb` | number | No | | Padding applied to the bottom side | | `pl` | number | No | | Padding applied to the left side | | `px` | number | No | | Horizontal padding applied to left and right sides | | `py` | number | No | | Vertical padding applied to top and bottom sides | | `fullWidth` | boolean | No | | Makes the component fill the full width of its parent | | `w` | DimensionValue | No | | Sets a specific width | | `h` | DimensionValue | No | | Sets a specific height | | `maxW` | DimensionValue | No | | Sets the maximum width | | `minW` | DimensionValue | No | | Sets the minimum width | | `maxH` | DimensionValue | No | | Sets the maximum height | | `minH` | DimensionValue | No | | Sets the minimum height | ## Examples ### Align Items ID: `Flex.align` β€’ Tags: align, items, cross-axis, alignment, stretch β€’ Category: general Item alignment options along the cross axis (flex-start, center, stretch, etc.). ```tsx const ALIGNMENTS = ['flex-start', 'center', 'flex-end', 'stretch', 'baseline'] as const; const theme = useTheme(); // Baseline is only legible if each Text has a visible box; pull the fill from // the theme so it reads in both light and dark. const chip = { backgroundColor: theme.backgrounds.elevated, paddingHorizontal: 8 }; return ( // wrap="wrap" β€” five fixed-width examples in a row would overflow on narrow // viewports, since flex children don't shrink by default here. {ALIGNMENTS.map((value) => ( align="{value}" {value === 'baseline' ? ( {/* Text of varying sizes β€” their baselines line up, not their boxes */} Aa Bb Cc ) : value === 'stretch' ? ( {/* No fixed heights so children stretch to the container's cross-size */} 1 2 3 ) : ( {/* Different heights to showcase flex-start/center/flex-end */} A B C )} ))} ); } ``` ### Basic Flex Layout ID: `Flex.basic` β€’ Tags: basic, gap, layout, container β€’ Category: general Simple flex container with three items and gap spacing. ```tsx return ( Item 1 Item 2 Item 3 ); } ``` ### Flex Direction ID: `Flex.direction` β€’ Tags: direction, row, column, arrangement, axis β€’ Category: general Row and column direction layouts for different item arrangements. ```tsx return ( Row Direction Item 1 Item 2 Item 3 Column Direction Item 1 Item 2 Item 3 ); } ``` ### Justify Content ID: `Flex.justify` β€’ Tags: justify, content, spacing, distribution, main-axis β€’ Category: general Content justification options along the main axis (flex-start, center, space-between, etc.). ```tsx const theme = useTheme(); return ( {[ { label: 'Start', value: 'flex-start' }, { label: 'Center', value: 'center' }, { label: 'End', value: 'flex-end' }, { label: 'Between', value: 'space-between' }, { label: 'Around', value: 'space-around' }, { label: 'Evenly', value: 'space-evenly' } ].map(({ value }) => ( justify="{value}" {/* Small fixed squares with no shrink so free space is obvious */} A B C ))} ); } ``` -------------------------------------------------------------------------------- # Form Form manages values, validation, and submission state for a group of inputs. Wrap each control in a `Form.Field` (which injects value and change handlers via context) and submit with `Form.Submit`. ## Metadata - Canonical name: `Form` - Package: `@platform-blocks/react-ui-library` - Import: `import { Form } from '@platform-blocks/react-ui-library';` - Status: stable - Category: input - Tags: form, fields, validation, submit - Docs: https://react-ui-library.com/components/Form - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Form ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `initialValues` | Record | No | | Initial form values | | `validationSchema` | ValidationSchema | No | | Form validation schema | | `onSubmit` | (values: Record) => void \| Promise | No | | Submit handler | | `validate` | (values: Record) => Record \| Promise> | No | | Validation handler | | `disabled` | boolean | No | | Whether form is disabled | | `validateOnChange` | boolean | No | | Whether to validate on change | | `validateOnBlur` | boolean | No | | Whether to validate on blur | | `children` | React.ReactNode | Yes | | Children components | ## Examples ### Basic Usage ID: `Form.basic` β€’ Tags: form, fields, validation β€’ Category: basics β€’ Status: stable β€’ Since: 1.0.0 `Form` manages values, validation, and submission state. Wrap each input in a `Form.Field` (which injects value/change handlers via context) and trigger submission with `Form.Submit`. ```tsx return (
console.log('submit', values)} > Create account
); } ``` -------------------------------------------------------------------------------- # Gallery The Gallery component displays a collection of images or media with thumbnail navigation, keyboard controls, and optional fullscreen/modal viewing. ## Metadata - Canonical name: `Gallery` - Package: `@platform-blocks/react-ui-library` - Import: `import { Gallery } from '@platform-blocks/react-ui-library';` - Status: experimental - Category: media - Tags: gallery, images, thumbnails, media - Docs: https://react-ui-library.com/components/Gallery - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Gallery ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `images` | GalleryItem[] | Yes | | Array of images to display in the gallery. | | `initialIndex` | number | No | 0 | Index of the image shown when the gallery first opens. | | `onClose` | () => void | No | | Called when the gallery is closed. | | `onImageChange` | (index: number, image: GalleryItem) => void | No | | Called when the active image changes, receiving the new index and image. | | `onDownload` | (image: GalleryItem) => void | No | | Called when the download action is triggered for the current image. | | `showMetadata` | boolean | No | false | Whether to display the metadata panel for the current image. | | `showThumbnails` | boolean | No | true | Whether to display the thumbnail strip for navigating between images. | | `showDownloadButton` | boolean | No | true | Whether to display the download button in the gallery controls. | | `allowKeyboardNavigation` | boolean | No | true | Whether arrow keys and Escape can be used to navigate and close the gallery. | | `allowSwipeNavigation` | boolean | No | true | Whether swipe gestures can be used to move between images. | | `overlayOpacity` | number | No | 0.9 | Opacity of the backdrop overlay behind the gallery, from 0 to 1. | | `animationDuration` | number | No | 250 | Duration of open/close and transition animations, in milliseconds. | ## Examples ### Basic ID: `Gallery.basic` β€’ Tags: gallery, images, navigation, metadata β€’ Category: basics β€’ Status: stable β€’ Since: 1.0.0 Basic image gallery with navigation and metadata. ```tsx // `null` closes the gallery; any index opens it on that image. const [openIndex, setOpenIndex] = useState(null); return ( {SAMPLE_IMAGES.map((image, index) => ( setOpenIndex(index)}> ))} setOpenIndex(null)} showMetadata /> ); } ``` ### Advanced ID: `Gallery.advanced` β€’ Tags: advanced, customization, handlers, minimal β€’ Category: features β€’ Status: stable β€’ Since: 1.0.0 Advanced gallery configurations with custom handlers. ```tsx const [active, setActive] = useState<'minimal' | 'custom' | null>(null); const [downloaded, setDownloaded] = useState(null); return ( {/* Chrome stripped back to the image itself β€” swipe and arrow keys still navigate. */} setActive(null)} showThumbnails={false} showDownloadButton={false} /> {/* `onDownload` replaces the built-in behaviour, so the host app decides what saving means. */} setActive(null)} onDownload={(image: GalleryItem) => setDownloaded(image.title ?? image.id)} showMetadata /> {downloaded ? ( Downloaded {downloaded} ) : null} ); } ``` -------------------------------------------------------------------------------- # GradientText A text component that displays text with gradient colors. Supports customizable gradients with multiple colors, different angles, and animated transitions (web only). ## Metadata - Canonical name: `GradientText` - Package: `@platform-blocks/react-ui-library` - Import: `import { GradientText } from '@platform-blocks/react-ui-library';` - Category: typography - Tags: text, gradient, animation, color - Docs: https://react-ui-library.com/components/GradientText - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/GradientText ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `colors` | string[] | Yes | | Array of colors for the gradient (at least 2 required) | | `locations` | number[] | No | | Color stops (0-1) for each color. If not provided, colors are evenly distributed | | `angle` | number | No | | Gradient direction angle in degrees (0 = left to right, 90 = top to bottom, etc.) | | `start` | [number, number] | No | | Start point [x, y] (0-1). Overrides angle if provided | | `end` | [number, number] | No | | End point [x, y] (0-1). Overrides angle if provided | | `position` | number | No | | Gradient position offset (0-1). Moves the gradient along the line | | `animation` | GradientTextAnimation | No | | Sweep the gradient position continuously (web only). Runs as a CSS animation, so no JavaScript executes per frame. Overrides `position` while it is running; on native the gradient stays static. | | `testID` | string | No | | Custom testID for testing | ## Examples ### Basic ID: `GradientText.basic` β€’ Category: typography β€’ Status: stable β€’ Since: 1.0.0 Pass two or more `colors` to fill text with a gradient. All other `Text` props still apply. ```tsx return ( Hello World ); } ``` ### Angles ID: `GradientText.angles` β€’ Category: typography β€’ Status: stable β€’ Since: 1.0.0 Different gradient directions using the `angle` prop. ```tsx const angles = [0, 45, 90, 135]; return ( {angles.map((angle) => ( {angle}Β° gradient ))} ); } ``` ### Controlled ID: `GradientText.controlled` β€’ Category: typography β€’ Status: stable β€’ Since: 1.0.0 Control the gradient position manually using the `position` prop (0.0 to 1.0). ```tsx const [position, setPosition] = useState(0); return ( ); } ``` -------------------------------------------------------------------------------- # Grid Responsive 12‑column layout primitive with span-based children. Each `GridItem` declares how many columns it consumes; container controls total columns and gaps. Supports responsive values for `columns` and `span` using breakpoint-aware props. ## Metadata - Canonical name: `Grid` - Package: `@platform-blocks/react-ui-library` - Import: `import { Grid } from '@platform-blocks/react-ui-library';` - Status: beta - Since: 0.1.0 - Category: layout - Docs: https://react-ui-library.com/components/Grid - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Grid ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `columns` | ResponsiveProp | No | | Number of columns (can be responsive) | | `gap` | SizeValue | No | 0 | Gap between items | | `rowGap` | SizeValue | No | | Row gap between items | | `columnGap` | SizeValue | No | | Column gap between items | | `fullWidth` | boolean | No | false | Make the grid take full width (100%) | | `children` | React.ReactNode | No | | Children elements | | `style` | StyleProp | No | | Custom styles | | `testID` | string | No | | Test ID for testing | | `m` | number | No | | Margin applied to all sides | | `mt` | number | No | | Margin applied to the top side | | `mr` | number | No | | Margin applied to the right side | | `mb` | number | No | | Margin applied to the bottom side | | `ml` | number | No | | Margin applied to the left side | | `mx` | number | No | | Horizontal margin applied to left and right sides | | `my` | number | No | | Vertical margin applied to top and bottom sides | | `p` | number | No | | Padding applied to all sides | | `pt` | number | No | | Padding applied to the top side | | `pr` | number | No | | Padding applied to the right side | | `pb` | number | No | | Padding applied to the bottom side | | `pl` | number | No | | Padding applied to the left side | | `px` | number | No | | Horizontal padding applied to left and right sides | | `py` | number | No | | Vertical padding applied to top and bottom sides | ## Examples ### Basic ID: `Grid.basic` β€’ Category: general Basic 12-column grid with equal width items. ```tsx return ( {Array.from({ length: 12 }).map((_, index) => ( {index + 1} ))} Twelve even columns, each spanning a single track ); } ``` ### Gaps ID: `Grid.gaps` β€’ Category: general Row and column gutters come from the container: `gap` sets both, `rowGap` and `columnGap` override one each. Items never carry their own padding or margin. ```tsx const sections = [ { label: 'Compact gap (xs)', props: { gap: 'xs' as const }, }, { label: 'Roomy gap (2xl)', props: { gap: '2xl' as const }, }, { label: 'Wide rows, tight columns', props: { rowGap: '2xl' as const, columnGap: 'xs' as const }, }, ]; return ( {sections.map(({ label, props }) => ( {label} {Array.from({ length: 12 }).map((_, index) => ( Item {index + 1} ))} ))} ); } ``` ### Nesting ID: `Grid.nesting` β€’ Category: general Nested grids demonstrating composition inside a grid item. ```tsx return ( {/* Block's own gap separates the label from the nested grid β€” no margin on either one. */} Parent span=8 {Array.from({ length: 6 }).map((_, index) => ( Nested {index + 1} ))} Sidebar span=4 GridItem components can render another Grid to illustrate nested layouts ); } ``` ### Responsive ID: `Grid.responsive` β€’ Category: general Responsive columns and item spans using breakpoint-aware props. ```tsx // Responsive props match the breakpoint configuration used in Grid return ( Hero (4/8/6) Hero (4/8/6) Side (2/4/3) Side (2/4/3) Footer (4/8/12) Column and span props adapt at base, md, and lg breakpoints ); } ``` ### Spans ID: `Grid.spans` β€’ Category: general Demonstrates varying column spans within a 12-column grid. ```tsx const spans = [6, 6, 4, 4, 4, 3, 3, 3, 3]; return ( {spans.map((span, index) => ( {`span=${span}`} ))} Mix spans within a 12-column grid to create varied layouts ); } ``` -------------------------------------------------------------------------------- # Highlight Highlight emphasizes matching fragments inside longer strings, reusing the Text component so typography settings stay consistent across platforms. ## Metadata - Canonical name: `Highlight` - Package: `@platform-blocks/react-ui-library` - Import: `import { Highlight } from '@platform-blocks/react-ui-library';` - Category: typography - Tags: text, emphasis, highlight, mark - Docs: https://react-ui-library.com/components/Highlight - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Highlight ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `highlight` | HighlightValue \| HighlightValue[] | No | | Substring or substrings to emphasize within the provided children | | `highlightStyles` | any \| ((theme: PlatformBlocksTheme) => any) | No | | Optional override for the highlighted segment styles. Accepts either a style object/array or a callback that receives the current theme and returns styles. | | `highlightColor` | string | No | | When provided, overrides the default highlight background/text palette. If the value matches a key from the theme color palettes it will use the related swatch, otherwise the value is treated as a raw color string. | | `caseSensitive` | boolean | No | | Toggle case-sensitive matching (defaults to case-insensitive). | | `trim` | boolean | No | | Trim highlight values before matching to ignore accidental whitespace. Defaults to true. | | `highlightProps` | Partial | No | | Additional props applied to the highlighted Text nodes. | ## Examples ### Basic ID: `Highlight.basic` β€’ Category: general Default highlight behavior with a single search term. Matching fragments are wrapped with the theme-aware highlight styles. ```tsx const PARAGRAPH = 'Highlight This, definitely THIS and also this!'; return ( Case-insensitive match {PARAGRAPH} ); } ``` ### Multiple ID: `Highlight.multiple` β€’ Category: general Pass an array to highlight several distinct substrings. Every match shares the same styles by default. ```tsx const SENTENCE = 'React UI Library brings patterns, blocks, and building tools together.'; return ( Multiple values {SENTENCE} ); } ``` ### Styles ID: `Highlight.styles` β€’ Category: general Swap the marker color with the `highlightColor` prop, passing any theme palette name. The default marker style (yellow background, unchanged text) is preserved. ```tsx const copy = 'You can switch the highlight color while keeping the default marker style.'; return ( Highlight color {copy} {copy} {copy} ); } ``` -------------------------------------------------------------------------------- # Icon The `Icon` component displays icons with optional captions and overlays, providing a flexible way to present visual content in your application. ## Metadata - Canonical name: `Icon` - Package: `@platform-blocks/react-ui-library` - Import: `import { Icon } from '@platform-blocks/react-ui-library';` - Status: beta - Category: typography - Docs: https://react-ui-library.com/components/Icon - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Icon ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `name` | string | No | | Icon name from the registry | | `icon` | ExternalIconComponent \| React.ReactElement | No | | An external icon library component or element, rendered instead of `name`. Enables using any icon library (e.g. Tabler) without registry registration. | | `size` | IconSize | No | 'md' | Size of the icon | | `color` | string | No | | Color of the icon | | `stroke` | number | No | 1.5 | Stroke thickness for outlined icons. Defaults to 1.5. | | `variant` | IconVariant | No | 'outlined' | Icon variant - overrides the default variant from icon definition | | `style` | StyleProp | No | | Additional styles | | `label` | string | No | | Accessibility label | | `decorative` | boolean | No | false | Whether the icon is purely decorative (skip a11y) | | `mirrorInRTL` | boolean | No | | Whether to mirror this icon in RTL mode. If not specified, uses auto-detection based on icon name | | `m` | number | No | | Margin applied to all sides | | `mt` | number | No | | Margin applied to the top side | | `mr` | number | No | | Margin applied to the right side | | `mb` | number | No | | Margin applied to the bottom side | | `ml` | number | No | | Margin applied to the left side | | `mx` | number | No | | Horizontal margin applied to left and right sides | | `my` | number | No | | Vertical margin applied to top and bottom sides | | `p` | number | No | | Padding applied to all sides | | `pt` | number | No | | Padding applied to the top side | | `pr` | number | No | | Padding applied to the right side | | `pb` | number | No | | Padding applied to the bottom side | | `pl` | number | No | | Padding applied to the left side | | `px` | number | No | | Horizontal padding applied to left and right sides | | `py` | number | No | | Vertical padding applied to top and bottom sides | ## Examples ### Basic ID: `Icon.basic` β€’ Category: general ```tsx return ( {/* Header */} Icon Scalable vector icons with consistent sizing and theming integration. {/* Different Sizes */} Sizes Available in multiple sizes using the UI theme system. sm md lg xl {/* Navigation Icons */} Navigation Icons Common navigation and directional icons. {['home', 'arrow-left', 'arrow-right', 'arrow-up', 'arrow-down', 'chevron-left', 'chevron-right', 'chevron-up', 'chevron-down', 'menu'].map(iconName => ( {iconName} ))} {/* Action Icons */} Action Icons Icons for common user actions and operations. {['plus', 'minus', 'x', 'check', 'search', 'edit', 'delete', 'save', 'copy', 'funnel', 'phone', 'toggle', 'qrcode', 'pin', 'spotlight'].map(iconName => ( {iconName} ))} {/* UI Icons */} UI Icons Interface and user experience icons. {['eye', 'eyeOff', 'settings', 'user', 'heart', 'star'].map(iconName => ( {iconName} ))} {/* Variants */} Icon Variants Icons can be displayed in outlined or filled variants. Outlined (Default): Filled: {/* Custom Colors */} Custom Colors Override icon colors to match your design. Pink Amber Green Blue {/* New Icons */} New Icons Recently added icons for common use cases. link exclamation funnel camera mic bell calendar phone email folder file timeline loader switch carousel avatar toggle bone toast radio qrcode progress map list gallery pin tree keycap breadcrumbs pagination table of contents stepper context menu grid dialog card tooltip slider input emoji button select textarea autocomplete rating datatable chip markdown accordion text title waveform ); } ``` ### Stroke ID: `Icon.stroke` β€’ Category: general ```tsx const strokeVariants = [ { label: 'Thin (0.75)', value: 0.75 }, { label: 'Default (1.5)', value: 1.5 }, { label: 'Bold (3)', value: 3 }, ]; return ( Stroke thickness Adjust the stroke thickness to match different visual weights. Filled icons that opt in to preserving stroke (like{' '} contrast) keep their outline while the fill still applies. {strokeVariants.map(({ label, value }) => ( {label} ))} ); } ``` -------------------------------------------------------------------------------- # IconButton An IconButton is a clickable button that contains an icon and is used to perform actions or trigger events. It is typically used in toolbars, action bars, or as standalone buttons in user interfaces. ## Metadata - Canonical name: `IconButton` - Package: `@platform-blocks/react-ui-library` - Import: `import { IconButton } from '@platform-blocks/react-ui-library';` - Status: stable - Since: 1.0.0 - Category: input - Tags: button, icon, clickable, action - Docs: https://react-ui-library.com/components/IconButton - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/IconButton ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `icon` | string \| ExternalIconComponent \| React.ReactElement | Yes | | Icon to render. Accepts a registry name, or an external icon library component/element (e.g. a Tabler icon) for use without registration. | | `onPress` | () => void | No | | Called when the button is pressed | | `onLayout` | (event: any) => void | No | | Called when the button layout is calculated | | `variant` | 'default' \| 'filled' \| 'secondary' \| 'outline' \| 'ghost' \| 'gradient' \| 'none' | No | 'default' | Button visual variant. `default` is the neutral surface-plus-hairline button, matching `Button`; a solid primary fill is opt-in via `filled`. | | `size` | SizeValue | No | | Button size | | `disabled` | boolean | No | | Whether the button is disabled | | `loading` | boolean | No | | Whether button is in loading state (shows loader) | | `color` | string | No | | Tint for the button. Accepts raw CSS color OR theme token syntax: - 'primary' (palette key -> uses middle shade 5) - 'primary.6' (palette key + shade index) - '#ff0000' / 'rgb(...)' direct colors `filled`, `secondary` and `outline` tint the container; `ghost` and the neutral `default`/`none` keep their chrome and tint only the icon. `gradient` draws its own overlay and ignores this. | | `iconColor` | string | No | | Explicit icon color override (else derived automatically from variant & color) | | `iconVariant` | IconProps['variant'] | No | | Icon variant override | | `iconSize` | IconProps['size'] | No | | Icon size override (defaults to appropriate size for button size) | | `tooltip` | TooltipPropValue | No | | Tooltip shown on hover/focus β€” wraps the button in a `Tooltip`. Pass a string, or a config object (`{ label, maxWidth, withArrow, … }`) for long labels that need a wider bubble. | | `tooltipPosition` | TooltipProps['position'] | No | | Tooltip position when the string form of `tooltip` is used | | `accessibilityLabel` | string | No | | Accessibility label - highly recommended for icon-only buttons | | `transitionDuration` | number | No | 100 | Length of the press scale transition in ms. `0` applies the pressed state instantly. Always 0 under reduced motion. | | `style` | any | No | | Style overrides for the button container | | `testID` | string | No | | Test ID for testing | | `m` | number | No | | Margin applied to all sides | | `mt` | number | No | | Margin applied to the top side | | `mr` | number | No | | Margin applied to the right side | | `mb` | number | No | | Margin applied to the bottom side | | `ml` | number | No | | Margin applied to the left side | | `mx` | number | No | | Horizontal margin applied to left and right sides | | `my` | number | No | | Vertical margin applied to top and bottom sides | | `p` | number | No | | Padding applied to all sides | | `pt` | number | No | | Padding applied to the top side | | `pr` | number | No | | Padding applied to the right side | | `pb` | number | No | | Padding applied to the bottom side | | `pl` | number | No | | Padding applied to the left side | | `px` | number | No | | Horizontal padding applied to left and right sides | | `py` | number | No | | Vertical padding applied to top and bottom sides | | `fullWidth` | boolean | No | | Makes the component fill the full width of its parent | | `w` | DimensionValue | No | | Sets a specific width | | `h` | DimensionValue | No | | Sets a specific height | | `maxW` | DimensionValue | No | | Sets the maximum width | | `minW` | DimensionValue | No | | Sets the minimum width | | `maxH` | DimensionValue | No | | Sets the maximum height | | `minH` | DimensionValue | No | | Sets the minimum height | | `radius` | RadiusValue | No | | Border radius value - supports size tokens, numbers, and special values | | `shadow` | ShadowValue | No | | Shadow value - supports size tokens and 'none' | ## Examples ### Basic ID: `IconButton.basic` β€’ Category: general ```tsx const [loading, setLoading] = useState(false); const [disabled, setDisabled] = useState(false); const handlePress = (action: string) => { console.log(`IconButton pressed: ${action}`); }; return ( IconButton Component Demo IconButton is designed specifically for displaying icons in square or circular shapes. Use radius="xl" for circular buttons. {/* Controls */} Controls {/* Variants */} Variants handlePress('default')} loading={loading} disabled={disabled} tooltip="Home (Default)" /> handlePress('filled')} loading={loading} disabled={disabled} tooltip="Home (Filled)" /> handlePress('secondary')} loading={loading} disabled={disabled} tooltip="Favorite (Secondary)" /> handlePress('outline')} loading={loading} disabled={disabled} tooltip="Settings (Outline)" /> handlePress('ghost')} loading={loading} disabled={disabled} tooltip="Search (Ghost)" /> handlePress('gradient')} loading={loading} disabled={disabled} tooltip="Star (Gradient)" /> {/* Sizes */} Sizes handlePress('xs')} tooltip="Extra Small" /> handlePress('sm')} tooltip="Small" /> handlePress('md')} tooltip="Medium" /> handlePress('lg')} tooltip="Large" /> handlePress('xl')} tooltip="Extra Large" /> {/* Shape: Square vs Circular */} Shape: Square vs Circular handlePress('square-sm')} tooltip="Small Radius (Square-ish)" /> radius="sm" handlePress('square-md')} tooltip="Medium Radius" /> radius="md" handlePress('square-lg')} tooltip="Large Radius" /> radius="lg" handlePress('circular')} tooltip="Circular (XL Radius)" /> radius="xl" (circular) {/* Custom Colors */} Custom Colors handlePress('red')} tooltip="Red Heart" /> handlePress('green')} tooltip="Green Check" /> handlePress('blue')} tooltip="Blue Info" /> handlePress('orange')} tooltip="Orange Warning" /> handlePress('purple')} tooltip="Purple Star (Circular)" /> {/* Common Use Cases */} Common Use Cases {/* Toolbar */} Toolbar Actions {/* Social Actions */} Social Actions (Circular) {/* Navigation */} Navigation ); } ``` -------------------------------------------------------------------------------- # Image The `Image` component displays images with optional captions and overlays, providing a flexible way to present visual content in your application. ## Metadata - Canonical name: `Image` - Package: `@platform-blocks/react-ui-library` - Import: `import { Image } from '@platform-blocks/react-ui-library';` - Status: beta - Category: media - Docs: https://react-ui-library.com/components/Image - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Image ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `src` | string \| ImageSourcePropType | No | | Remote image URI, or a bundled asset from `require('./photo.png')` | | `source` | ImageSourcePropType | No | | Image source object (alternative to src) | | `alt` | string | No | | Alternative text for accessibility | | `accessibilityLabel` | string | No | | Accessibility label | | `resizeMode` | 'cover' \| 'contain' \| 'stretch' \| 'repeat' \| 'center' | No | | Image resize mode | | `size` | SizeValue \| number | No | | Image size preset | | `w` | number \| string | No | | Custom width | | `h` | number \| string | No | | Custom height | | `aspectRatio` | number | No | | Aspect ratio | | `borderWidth` | number | No | | Border width | | `borderColor` | ColorValue | No | | Border color | | `rounded` | boolean | No | | Whether image should be rounded | | `circle` | boolean | No | | Whether image should be circular | | `fallback` | React.ReactNode | No | | Fallback element to show on error | | `loading` | React.ReactNode | No | | Loading state element | | `onLoad` | () => void | No | | Called when image loads successfully | | `onError` | (error: any) => void | No | | Called when image fails to load | | `onLoadStart` | () => void | No | | Called when image starts loading | | `onLoadEnd` | () => void | No | | Called when image finishes loading (success or error) | | `containerStyle` | StyleProp | No | | Container style | | `imageStyle` | StyleProp | No | | Image style overrides | | `testID` | string | No | | Component test ID for testing | | `style` | any | No | | Additional CSS styles | | `m` | number | No | | Margin applied to all sides | | `mt` | number | No | | Margin applied to the top side | | `mr` | number | No | | Margin applied to the right side | | `mb` | number | No | | Margin applied to the bottom side | | `ml` | number | No | | Margin applied to the left side | | `mx` | number | No | | Horizontal margin applied to left and right sides | | `my` | number | No | | Vertical margin applied to top and bottom sides | | `p` | number | No | | Padding applied to all sides | | `pt` | number | No | | Padding applied to the top side | | `pr` | number | No | | Padding applied to the right side | | `pb` | number | No | | Padding applied to the bottom side | | `pl` | number | No | | Padding applied to the left side | | `px` | number | No | | Horizontal padding applied to left and right sides | | `py` | number | No | | Vertical padding applied to top and bottom sides | | `fullWidth` | boolean | No | | Makes the component fill the full width of its parent | | `maxW` | DimensionValue | No | | Sets the maximum width | | `minW` | DimensionValue | No | | Sets the minimum width | | `maxH` | DimensionValue | No | | Sets the maximum height | | `minH` | DimensionValue | No | | Sets the minimum height | | `radius` | RadiusValue | No | | Border radius value - supports size tokens, numbers, and special values | ## Examples ### Sizes ID: `Image.sizes` β€’ Tags: size, layout β€’ Category: layout β€’ Status: stable β€’ Since: 0.3.0 Set the `size` prop to any token (`xs`–`3xl`) to scale the image box, or pass `w`/`h` when you need exact dimensions. ```tsx const SIZES = ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl'] as const; /** Inline 8x8 PNG β€” keeps the demo offline and identical on web and native. */ const SAMPLE_SRC = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAIAAABLbSncAAAALklEQVR42mNITvsIR409P+CIAasokMuAVRQqgSkKksAqiiKB5goGrKJQCawuBgC2Wnfh+zNA9wAAAABJRU5ErkJggg=='; return ( {SIZES.map((size) => ( {`Sample {size} ))} ); } ``` ### Basic ID: `Image.basic` β€’ Category: general ```tsx return ( Basic Image Usage Mountain landscape A simple image with specified dimensions ); } ``` ### Fallback ID: `Image.fallback` β€’ Category: general ```tsx return ( Image Fallback & Error Handling } alt="Failed to load" /> With Icon Fallback Image not found
} alt="Failed to load" /> With Text Fallback When images fail to load, fallback content is displayed
); } ``` ### Shapes ID: `Image.shapes` β€’ Category: general ```tsx return ( Image Shapes Default Default Rounded Rounded Circle Circle Shape variations: default, rounded corners, and circular ); } ``` ### Spacing ID: `Image.spacing` β€’ Category: general ```tsx return ( Universal Spacing Props {/* Auto margin example */} Auto Margin Example Centered image Image with m="auto" should be centered {/* Theme spacing example */} Theme Spacing Values Image with theme spacing Image with m="lg" using theme spacing {/* Numeric spacing example */} Numeric Spacing Values Image with numeric spacing Image with m={`{20}`} using numeric spacing {/* Zero margin example */} Zero Margin Example Image with zero margin Image with m="0" should have no margin {/* Mixed spacing props example */} Mixed Spacing Props Image with mixed spacing Image with mx="auto", my="md", p="sm" ); } ``` -------------------------------------------------------------------------------- # Indicator The Indicator component renders a small dot or pill-shaped badge in the corner of a parent container β€” perfect for online status, unread counts, or "new" markers. Pass `label` for text content (the dot auto-expands to fit multi-digit counts); use `children` for arbitrary custom content like icons. ## Metadata - Canonical name: `Indicator` - Package: `@platform-blocks/react-ui-library` - Import: `import { Indicator } from '@platform-blocks/react-ui-library';` - Category: data - Tags: indicator, badge, status, count, dot - Docs: https://react-ui-library.com/components/Indicator - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Indicator ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `size` | SizeValue \| number | No | | | | `color` | string | No | | | | `borderColor` | string | No | | | | `borderWidth` | number | No | | | | `placement` | 'top-left' \| 'top-right' \| 'bottom-left' \| 'bottom-right' | No | | | | `offset` | number | No | | | | `style` | StyleProp | No | | | | `children` | React.ReactNode | No | | Free-form content rendered inside the indicator dot. Useful when a custom icon is needed; for plain text counts prefer `label`, which auto-resizes the dot and applies a contrast-aware text color. | | `label` | React.ReactNode | No | | Convenience text content (typically a count). When set, the dot expands to fit the label and the text uses a contrast-aware color. | | `labelProps` | Omit | No | | Override props applied to the label `` (style, weight, ff, size, color). | | `invisible` | boolean | No | | | ## Examples ### Basic usage ID: `Indicator.basic` β€’ Tags: status, notification β€’ Category: basics β€’ Status: stable β€’ Since: 0.4.0 Use `Indicator` to layer small notices on any container: position it at a corner, pair it with avatars for presence, or wrap children to show counters without building custom badges. ```tsx return ( Corner indicator Panel Avatar status Numeric counter Inbox 5 ); } ``` ### Placements ID: `Indicator.placements` β€’ Tags: placement, offset β€’ Category: basics β€’ Status: stable β€’ Since: 0.4.0 Control positioning by combining the `placement` prop (top/bottom + left/right) with `offset` to nudge the badge; add children inside `Indicator` when you need numeric or icon content. ```tsx const cornerPlacements = [ { label: 'Top left', placement: 'top-left', color: '#F59E0B' }, { label: 'Top right', placement: 'top-right', color: '#10B981' }, { label: 'Bottom left', placement: 'bottom-left', color: '#6366F1' }, { label: 'Bottom right', placement: 'bottom-right', color: '#EF4444' }, ] as const; const offsetPlacements = [ { label: '9 unread', placement: 'top-right', color: '#6366F1', value: '9', offset: 6 }, { label: '2 new', placement: 'bottom-right', color: '#10B981', value: '2', offset: 4 }, ] as const; const Tile = ({ children }: { children: ReactNode }) => ( {children} ); return ( Corner placements {cornerPlacements.map((placement) => ( {placement.label} ))} Offset and content {offsetPlacements.map((placement) => ( {placement.label} {placement.value} ))} ); } ``` ### Sizes ID: `Indicator.sizes` β€’ Tags: sizes, tokens β€’ Category: basics β€’ Status: stable β€’ Since: 0.4.0 Set the `size` prop to any token (`xs`–`3xl`) for theme-aligned dots, or provide a raw number when you need a bespoke diameter for your badge. ```tsx const SIZES = ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl', 24] as const; return ( {SIZES.map((size) => ( {typeof size === 'number' ? `${size} (numeric)` : size} ))} ); } ``` ### Statuses ID: `Indicator.statuses` β€’ Tags: presence, status β€’ Category: basics β€’ Status: stable β€’ Since: 0.4.0 Map semantic states (online, idle, busy) to palette colors and cap large notification counts by rendering text inside `Indicator`β€”perfect for presence chips or inbox badges. ```tsx const presenceStatuses = [ { label: 'Online', palette: 'success', avatar: require('../../../../assets/avatars/avatar-1.png') }, { label: 'Idle', palette: 'warning', avatar: require('../../../../assets/avatars/avatar-2.png') }, { label: 'Busy', palette: 'error', avatar: require('../../../../assets/avatars/avatar-3.png') }, { label: 'Offline', palette: 'gray', avatar: require('../../../../assets/avatars/avatar-4.png') }, ] as const; const notificationCounts = [3, 47, 99, 134, 1005]; const theme = useTheme(); const resolveColor = (palette: (typeof presenceStatuses)[number]['palette']) => { const swatch = (theme.colors as any)[palette]; return Array.isArray(swatch) ? swatch[5] : swatch; }; return ( Presence indicators {presenceStatuses.map((status) => ( {status.label} ))} Max count handling {notificationCounts.map((count) => { const display = count > 99 ? '99+' : `${count}`; return ( {display} {count} ); })} ); } ``` ### Labels & counts ID: `Indicator.labels` β€’ Tags: label, count, labelProps, customization β€’ Category: general β€’ Status: stable β€’ Since: 1.0.0 Pass `label` to render a count or short text inside the indicator β€” the dot expands to a pill so multi-digit values fit. `labelProps` accepts any `` props for fonts, weights, etc. For arbitrary custom content (icons, status markers), use `children` instead. ```tsx const Anchor = ({ children }: { children?: React.ReactNode }) => ( {children} ); return ( Numeric counts Monospace badge with custom label styling Custom child content (children, not label) {/* anything you want β€” icon, custom shape, etc. */} ); } ``` -------------------------------------------------------------------------------- # Input A versatile text input component that provides a consistent interface for text entry across different platforms. The Input component supports various types, validation states, and accessibility features. ## Metadata - Canonical name: `Input` - Package: `@platform-blocks/react-ui-library` - Import: `import { Input } from '@platform-blocks/react-ui-library';` - Status: stable - Since: 1.0.0 - Category: input - Tags: input, form, text, validation - Docs: https://react-ui-library.com/components/Input - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Input ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `type` | 'text' \| 'password' \| 'email' \| 'tel' \| 'number' \| 'search' | No | 'text' | Input type - determines styling and behavior | | `validation` | ValidationRule[] | No | | Input validation rules | | `autoComplete` | 'off' \| 'password' \| 'email' \| 'tel' \| 'url' \| 'name' \| 'additional-name' \| 'address-line1' \| 'address-line2' \| 'birthdate-day' \| 'birthdate-full' \| 'birthdate-month' \| 'birthdate-year' \| 'cc-csc' \| 'cc-exp' \| 'cc-exp-month' \| 'cc-exp-year' \| 'cc-number' \| 'country' \| 'current-password' \| 'family-name' \| 'given-name' \| 'honorific-prefix' \| 'honorific-suffix' \| 'new-password' \| 'one-time-code' \| 'organization' \| 'organization-title' \| 'postal-code' \| 'street-address' \| 'username' | No | | Auto-complete type | | `keyboardType` | KeyboardTypeOptions | No | | Keyboard type for mobile | | `multiline` | boolean | No | | Whether input is multiline | | `numberOfLines` | number | No | | Number of lines for multiline input | | `minLines` | number | No | 1 | Minimum number of lines for multiline input (default: 1) | | `maxLines` | number | No | | Maximum number of lines for multiline input | | `maxLength` | number | No | | Maximum length | | `secureTextEntry` | boolean | No | | Whether to secure text entry | | `textInputProps` | ExtendedTextInputProps | No | | Additional TextInput props | | `inputRef` | React.Ref | No | | Ref to underlying TextInput (focus control) | | `autoCapitalize` | RNTextInputProps['autoCapitalize'] | No | | Text auto-capitalization behavior | | `autoCorrect` | boolean | No | | Whether to enable auto-correct | | `autoFocus` | boolean | No | | Whether to auto-focus on mount | | `returnKeyType` | RNTextInputProps['returnKeyType'] | No | | Return key type for soft keyboard | | `blurOnSubmit` | boolean | No | | Whether to blur on submit | | `selectTextOnFocus` | boolean | No | | Select all text on focus | | `textContentType` | RNTextInputProps['textContentType'] | No | | iOS text content type for autofill | | `textAlign` | RNTextInputProps['textAlign'] | No | | Text alignment | | `spellCheck` | boolean | No | | Whether spell check is enabled | | `inputMode` | RNTextInputProps['inputMode'] | No | | Input mode (modern alternative to keyboardType) | | `enterKeyHint` | RNTextInputProps['enterKeyHint'] | No | | Hint for the enter key | | `selectionColor` | string | No | | Color of the text selection handles and highlight | | `showSoftInputOnFocus` | boolean | No | | Whether to show the soft keyboard on focus | | `editable` | boolean | No | | Whether the field is read-only (alias for !editable) | | `variant` | InputVariant | No | | Visual variant of the input. `default` (light surface + border), `filled` (gray fill, no border), `outline` (transparent fill, border only), `unstyled` (no border, no fill). | | `value` | string | No | | Input value | | `onChangeText` | (text: string) => void | No | | Change handler | | `label` | React.ReactNode | No | | Input label (string or component) | | `disabled` | boolean | No | | Whether input is disabled | | `required` | boolean | No | | Whether input is required | | `placeholder` | string | No | | Input placeholder | | `error` | string | No | | Error message | | `helperText` | string | No | | Helper text | | `description` | string | No | | Optional short description displayed directly under the label (above the field) | | `size` | SizeValue | No | 'md' | Input size | | `withAsterisk` | boolean | No | | Whether to show required indicator | | `name` | string | No | | Input name for form integration | | `startSection` | React.ReactNode | No | | Left section content | | `endSection` | React.ReactNode | No | | Right section content | | `style` | any | No | | Additional styling | | `accessibilityLabel` | string | No | | Accessibility label | | `accessibilityHint` | string | No | | Accessibility hint | | `testID` | string | No | | Test ID for testing | | `debounceMs` | number | No | | Debounce delay for validation in milliseconds | | `onFocus` | () => void | No | | Focus handler | | `onBlur` | () => void | No | | Blur handler | | `onEnter` | () => void | No | | Enter key press handler | | `clearable` | boolean | No | | Show built-in clear button when input has value | | `clearButtonLabel` | string | No | | Accessible label for the clear button | | `onClear` | () => void | No | | Callback when the clear button is pressed | | `keyboardFocusId` | string | No | | Identifier used with KeyboardManagerProvider to request refocus | | `labelProps` | Omit | No | | Override props applied to the field label `` (style, weight, ff, etc.) | | `descriptionProps` | Omit | No | | Override props applied to the field description `` | | `placeholderTextColor` | string | No | | Color of the placeholder text. Falls back to `theme.text.muted`. | | `startSectionProps` | Omit | No | | Props applied to the wrapping `` around `startSection` (style, accessibility, etc.). | | `endSectionProps` | Omit | No | | Props applied to the wrapping `` around `endSection`. | | `m` | number | No | | Margin applied to all sides | | `mt` | number | No | | Margin applied to the top side | | `mr` | number | No | | Margin applied to the right side | | `mb` | number | No | | Margin applied to the bottom side | | `ml` | number | No | | Margin applied to the left side | | `mx` | number | No | | Horizontal margin applied to left and right sides | | `my` | number | No | | Vertical margin applied to top and bottom sides | | `p` | number | No | | Padding applied to all sides | | `pt` | number | No | | Padding applied to the top side | | `pr` | number | No | | Padding applied to the right side | | `pb` | number | No | | Padding applied to the bottom side | | `pl` | number | No | | Padding applied to the left side | | `px` | number | No | | Horizontal padding applied to left and right sides | | `py` | number | No | | Vertical padding applied to top and bottom sides | | `fullWidth` | boolean | No | | Makes the component fill the full width of its parent | | `w` | DimensionValue | No | | Sets a specific width | | `h` | DimensionValue | No | | Sets a specific height | | `maxW` | DimensionValue | No | | Sets the maximum width | | `minW` | DimensionValue | No | | Sets the minimum width | | `maxH` | DimensionValue | No | | Sets the maximum height | | `minH` | DimensionValue | No | | Sets the minimum height | | `radius` | RadiusValue | No | | Border radius value - supports size tokens, numbers, and special values | ## Examples ### Basic ID: `Input.basic` β€’ Tags: basic, input, text β€’ Category: basics β€’ Status: stable β€’ Since: 1.0.0 Basic text input with label, placeholder, and value handling. ```tsx const [value, setValue] = useState(''); return ( ); } ``` ### Variants ID: `Input.variants` β€’ Tags: variants, filled, outline, unstyled β€’ Category: basics β€’ Status: stable β€’ Since: 1.0.0 Four visual variants for the input shell: `default`, `filled`, `outline`, and `unstyled`. The variant only changes the container fill and border β€” label, sections, and disclaimer stay consistent. ```tsx return ( ); } ``` ### Types ID: `Input.types` β€’ Tags: types, email, password, number, tel β€’ Category: usage β€’ Status: stable β€’ Since: 1.0.0 Set `type` to switch the keyboard and browser behaviour β€” email, password, number, and tel are all supported. ```tsx return ( ); } ``` ### Validation ID: `Input.validation` β€’ Tags: validation, error, required, helper β€’ Category: usage β€’ Status: stable β€’ Since: 1.0.0 Pass `error` to show a validation message, `required` to mark the field, and `helperText` for guidance. `disabled` blocks editing. ```tsx const isValidEmail = (email: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); const [email, setEmail] = useState(''); return ( 0 && !isValidEmail(email) ? 'Please enter a valid email address' : undefined} helperText="We'll never share your email" /> ); } ``` ### Multiline Modes ID: `Input.multiline` β€’ Tags: input, multiline β€’ Category: behavior β€’ Status: stable β€’ Since: 1.0.0 Pair `multiline` with `minLines`/`maxLines` to auto-expand, or with `numberOfLines` for a fixed height. ```tsx const [autoText, setAutoText] = useState(''); const [fixedText, setFixedText] = useState(''); return ( ); } ``` ### Sections and slot styling ID: `Input.slot-styling` β€’ Tags: startSection, endSection, clearable, placeholderTextColor, slot-props, customization β€’ Category: usage β€’ Status: stable β€’ Since: 1.0.0 Render content inside the field with `startSection` / `endSection`, and add `clearable` for a dismiss button. `startSectionProps` and `endSectionProps` accept any `` props (including `style`) and apply them to the slot wrapper; `placeholderTextColor` overrides the muted default. ```tsx const [workspace, setWorkspace] = useState(''); const [search, setSearch] = useState(''); return ( https://} startSectionProps={{ style: { paddingRight: 8 } }} /> } startSectionProps={{ style: { paddingRight: 8 } }} /> ); } ``` -------------------------------------------------------------------------------- # Joystick Joystick is a two-axis positional input. In its default `circle` shape it behaves like a physical stick β€” the handle rides the rim at full deflection and springs back to centre when released. As a `square` it becomes an XY pad: each axis clamps on its own so the corners are reachable, and the handle stays where it is left. Both axes are normalized to βˆ’1…1. `y` is up-positive by default, matching how a gamepad axis reads; pass `invertY={false}` to follow screen space instead. `deadZone` zeroes small deflections and rescales what is left, so the value still spans the full range past the threshold rather than jumping to the dead-zone size. `step` snaps each axis, `lockAxis` restricts travel to one direction, and `showCrosshair` adds accent rules that track the handle β€” the usual XY-pad readout. The gesture runs on the shared `useDragGesture` hook, which means a drag that leaves the pad keeps tracking the finger instead of handing the touch back to the page. Arrow keys nudge by `keyboardStep` on web, `Home` and `Escape` recentre, and VoiceOver/TalkBack get increment and decrement actions. ## Metadata - Canonical name: `Joystick` - Package: `@platform-blocks/react-ui-library` - Import: `import { Joystick } from '@platform-blocks/react-ui-library';` - Category: input - Tags: joystick, xy, pad, gesture, two-axis, input - Docs: https://react-ui-library.com/components/Joystick - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Joystick ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `value` | JoystickValue | No | | Controlled value. Both axes are normalized to βˆ’1…1. | | `defaultValue` | JoystickValue | No | | Initial value while uncontrolled. Default `{ x: 0, y: 0 }`. | | `onChange` | (value: JoystickValue) => void | No | | Fired for every position change, including each frame of a drag. | | `onChangeEnd` | (value: JoystickValue) => void | No | | Fired once when the gesture ends, with the value it settled on. | | `onChangeStart` | (value: JoystickValue) => void | No | | Fired when a drag or keyboard interaction begins. | | `shape` | JoystickShape | No | 'circle' | `circle` clamps the handle to a disc β€” a stick. `square` clamps each axis on its own so the corners are reachable β€” an XY pad. Default `circle`. | | `returnToCenter` | boolean | No | | Spring the handle back to the centre when released, the way a physical stick does. Defaults to `true` for `circle` and `false` for `square`. | | `lockAxis` | 'x' \| 'y' | No | | Restrict travel to a single axis. | | `deadZone` | number | No | 0 | Report `0` until the handle travels this far from centre (0–1). Default `0`. | | `step` | number | No | 0 | Snap each axis to this increment. Default `0` (continuous). | | `keyboardStep` | number | No | | Increment applied by a single arrow key press. Defaults to `step` or `0.1`. | | `invertY` | boolean | No | true | Report a positive `y` when the handle is pushed up. Default `true`. | | `size` | ComponentSizeValue | No | 'md' | Outer size in px, or a size token. Default `'md'`. | | `handleSize` | number | No | | Handle diameter in px. Defaults to ~32% of `size`. | | `variant` | JoystickVariant | No | 'default' | Visual preset. Default `'default'`. | | `color` | string | No | | Accent color: a palette token (`'primary'`), `'primary.6'` shade syntax, or any CSS color. | | `baseColor` | string | No | | Base surface color override. | | `handleColor` | string | No | | Handle color override. | | `showGuides` | boolean | No | true | Draw the static centre guides. Default `true`. | | `showCrosshair` | boolean | No | false | Draw accent rules that track the handle on each axis β€” the XY-pad readout. Default `false`. | | `valueLabel` | boolean \| ((value: JoystickValue) => string) | No | false | Render the current value under the pad. Pass a function to format it. | | `label` | React.ReactNode | No | | Field label rendered above the pad. | | `disabled` | boolean | No | false | Ignore all input and dim the control. | | `readOnly` | boolean | No | false | Ignore all input while keeping full contrast. | | `transitionDuration` | number | No | | Spring-back / keyboard transition duration in ms. Default `220`. | | `style` | StyleProp | No | | Root style. | | `baseStyle` | StyleProp | No | | Style for the pad surface. | | `handleStyle` | StyleProp | No | | Style for the handle. | | `valueLabelStyle` | StyleProp | No | | Style for the value label text. | | `accessibilityLabel` | string | No | | | | `testID` | string | No | | | | `m` | number | No | | Margin applied to all sides | | `mt` | number | No | | Margin applied to the top side | | `mr` | number | No | | Margin applied to the right side | | `mb` | number | No | | Margin applied to the bottom side | | `ml` | number | No | | Margin applied to the left side | | `mx` | number | No | | Horizontal margin applied to left and right sides | | `my` | number | No | | Vertical margin applied to top and bottom sides | | `p` | number | No | | Padding applied to all sides | | `pt` | number | No | | Padding applied to the top side | | `pr` | number | No | | Padding applied to the right side | | `pb` | number | No | | Padding applied to the bottom side | | `pl` | number | No | | Padding applied to the left side | | `px` | number | No | | Horizontal padding applied to left and right sides | | `py` | number | No | | Vertical padding applied to top and bottom sides | ## Examples ### Basic ID: `Joystick.basic` β€’ Tags: basic, joystick, stick β€’ Category: basics β€’ Status: stable β€’ Since: 1.1.0 A stick that springs back to centre on release. Drag anywhere on the pad β€” the gesture keeps tracking even when the finger leaves it. ```tsx const [value, setValue] = useState({ x: 0, y: 0 }); return ( ); } ``` ### XY pad ID: `Joystick.xy-pad` β€’ Tags: xy, pad, square, filter β€’ Category: variants β€’ Status: stable β€’ Since: 1.1.0 `shape="square"` clamps each axis independently so the corners are reachable, and the handle holds its position on release β€” the shape a filter or effect pad wants. ```tsx const [value, setValue] = useState({ x: -0.4, y: 0.6 }); // Map the pad onto a pair of parameters the way an effect unit would. const cutoff = Math.round(((value.x + 1) / 2) * 18000 + 200); const resonance = ((value.y + 1) / 2).toFixed(2); return ( Cutoff {cutoff} Hz Β· Resonance {resonance} ); } ``` ### Dead zone and steps ID: `Joystick.dead-zone` β€’ Tags: deadZone, step, snapping β€’ Category: features β€’ Status: stable β€’ Since: 1.1.0 `deadZone` ignores small deflections around centre and rescales the rest, so full travel still reports 1. `step` snaps each axis onto a grid. ```tsx const [free, setFree] = useState({ x: 0, y: 0 }); const [stepped, setStepped] = useState({ x: 0, y: 0 }); return ( ); } ``` ### Axis lock ID: `Joystick.axis-lock` β€’ Tags: lockAxis, single-axis, pan β€’ Category: features β€’ Status: stable β€’ Since: 1.1.0 `lockAxis` restricts travel to one direction. A single-axis pad also leaves the perpendicular direction to the page, so vertical scrolling still works over a horizontal control. ```tsx const [pan, setPan] = useState({ x: 0, y: 0 }); const position = pan.x === 0 ? 'Center' : `${pan.x < 0 ? 'L' : 'R'} ${Math.round(Math.abs(pan.x) * 100)}`; return ( {position} ); } ``` -------------------------------------------------------------------------------- # KeyCap A visual component for displaying keyboard keys, shortcuts, and key combinations with proper styling. ## Metadata - Canonical name: `KeyCap` - Package: `@platform-blocks/react-ui-library` - Import: `import { KeyCap } from '@platform-blocks/react-ui-library';` - Category: typography - Tags: keycap, keyboard, shortcut, key, hotkey - Docs: https://react-ui-library.com/components/KeyCap - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/KeyCap ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `children` | ReactNode | Yes | | The key or text to display | | `size` | ComponentSizeValue | No | 'md' | Size variant of the key cap | | `variant` | 'default' \| 'minimal' \| 'outline' \| 'filled' | No | 'default' | Visual variant of the key cap | | `color` | 'primary' \| 'secondary' \| 'gray' \| 'success' \| 'warning' \| 'error' | No | 'gray' | Color scheme for the key cap | | `animateOnPress` | boolean | No | true | Whether the key should animate when the actual key is pressed Only works on web platforms | | `transitionDuration` | number | No | 250 | Length of the press-down/up animation in ms; both legs scale against a 250ms baseline. `0` leaves the cap at rest. Always 0 under reduced motion. | | `keyCode` | string | No | | The actual key code to listen for (e.g., 'Enter', 'Space', 'Escape') If provided, the component will animate when this key is pressed | | `modifiers` | Array<'ctrl' \| 'cmd' \| 'alt' \| 'shift' \| 'meta'> | No | | Modifier keys that must be pressed along with the main key | | `pressed` | boolean | No | | Whether the key cap should appear pressed | | `onKeyPress` | () => void | No | | Callback when the key combination is pressed | | `testID` | string | No | | Custom test ID for testing | | `fontFamily` | string | No | | Custom font family (overrides the default monospace stack) | | `ff` | string | No | | Shorthand alias for `fontFamily` | | `m` | number | No | | Margin applied to all sides | | `mt` | number | No | | Margin applied to the top side | | `mr` | number | No | | Margin applied to the right side | | `mb` | number | No | | Margin applied to the bottom side | | `ml` | number | No | | Margin applied to the left side | | `mx` | number | No | | Horizontal margin applied to left and right sides | | `my` | number | No | | Vertical margin applied to top and bottom sides | | `p` | number | No | | Padding applied to all sides | | `pt` | number | No | | Padding applied to the top side | | `pr` | number | No | | Padding applied to the right side | | `pb` | number | No | | Padding applied to the bottom side | | `pl` | number | No | | Padding applied to the left side | | `px` | number | No | | Horizontal padding applied to left and right sides | | `py` | number | No | | Vertical padding applied to top and bottom sides | | `fullWidth` | boolean | No | | Makes the component fill the full width of its parent | | `w` | DimensionValue | No | | Sets a specific width | | `h` | DimensionValue | No | | Sets a specific height | | `maxW` | DimensionValue | No | | Sets the maximum width | | `minW` | DimensionValue | No | | Sets the minimum width | | `maxH` | DimensionValue | No | | Sets the maximum height | | `minH` | DimensionValue | No | | Sets the minimum height | | `radius` | RadiusValue | No | | Border radius value - supports size tokens, numbers, and special values | ## Examples ### Basic Usage ID: `KeyCap.basic` β€’ Tags: basic, keyboard β€’ Category: usage β€’ Status: stable β€’ Since: 1.0.0 Basic keyboard key display for showing shortcuts and key combinations. ```tsx return ( A Enter Space ⌘ Ctrl ⇧ ); } ``` ### Sizes ID: `KeyCap.sizes` β€’ Tags: sizes β€’ Category: usage β€’ Status: stable β€’ Since: 1.0.0 Different sizes for KeyCap components from extra small to extra large. ```tsx return ( XS SM MD LG XL ); } ``` ### Variants ID: `KeyCap.variants` β€’ Tags: variants, styles β€’ Category: usage β€’ Status: stable β€’ Since: 1.0.0 Different visual variants for KeyCap components including default, filled, minimal, and outline styles. ```tsx return ( Default Filled Minimal Outline ); } ``` ### Modifiers ID: `KeyCap.modifiers` β€’ Tags: modifiers, shortcuts, combinations β€’ Category: usage β€’ Status: stable β€’ Since: 1.0.0 KeyCap components with modifier keys for displaying keyboard shortcuts and key combinations. ```tsx return ( Copy ⌘ + C Save ⌘ + S Undo ⌘ + Z ); } ``` -------------------------------------------------------------------------------- # Knob The Knob component provides a rotary control for adjusting values with touch, mouse, and keyboard input. It supports snapping to marks, internal value labels, and accessible field headers for external labels. ## Metadata - Canonical name: `Knob` - Package: `@platform-blocks/react-ui-library` - Import: `import { Knob } from '@platform-blocks/react-ui-library';` - Category: input - Tags: knob, dial, rotary, gesture, input - Docs: https://react-ui-library.com/components/Knob - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Knob ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `behavior` | KnobBehavior | No | 'level' | What kind of control this is: how it behaves and what it reads out. @default 'level' | | `variant` | KnobVariant | No | 'default' | Visual style preset. Merged under `appearance`, so single properties stay overridable. Behavior values (`level`, `stepped`, …) still work here at runtime but are deprecated β€” pass them to `behavior` instead. @default 'default' | | `mode` | 'bounded' \| 'endless' | No | | Interaction mode for bounded or endless rotary behavior | | `value` | number | No | | Controlled value | | `defaultValue` | number | No | | Uncontrolled initial value | | `min` | number | No | | Minimum selectable value | | `max` | number | No | | Maximum selectable value | | `step` | number | No | | Step increment applied when interacting | | `onChange` | (value: number) => void | No | | Called on every value change | | `onChangeEnd` | (value: number) => void | No | | Called after interaction completes | | `onScrubStart` | () => void | No | | Fired when the user begins dragging | | `onScrubEnd` | () => void | No | | Fired when the user ends dragging | | `size` | ComponentSizeValue | No | | Size token (`xs`–`3xl`) or an explicit diameter in pixels | | `thumbSize` | number | No | | Diameter of the thumb indicator, in pixels. Defaults to a ratio of the resolved size. | | `disabled` | boolean | No | | Disable all user interaction | | `readOnly` | boolean | No | | Prevent interaction but keep visual state | | `formatLabel` | (value: number) => ReactNode | No | | Custom formatter for the value label | | `withLabel` | boolean | No | | Render the value label inside the knob | | `valueLabel` | KnobValueLabelConfig \| false | No | | Structured configuration for the value label block | | `marks` | KnobMark[] | No | | Optional marks rendered around the control | | `restrictToMarks` | boolean | No | | Restrict interaction to the supplied marks | | `label` | ReactNode | No | | Optional visual label rendered outside the knob | | `description` | ReactNode | No | | Optional helper text rendered with the label | | `labelPosition` | 'left' \| 'right' \| 'top' \| 'bottom' | No | | Placement for the external label | | `style` | StyleProp | No | | Style overrides for the outer container | | `trackStyle` | StyleProp | No | | Style overrides for the circular track | | `thumbStyle` | StyleProp | No | | Style overrides for the thumb | | `markLabelStyle` | StyleProp | No | | Style overrides for mark labels | | `testID` | string | No | | Accessibility identifier | | `accessibilityLabel` | string | No | | Screen reader label | | `appearance` | KnobAppearance | No | | Unified surface styling and interaction overrides | | `m` | number | No | | Margin applied to all sides | | `mt` | number | No | | Margin applied to the top side | | `mr` | number | No | | Margin applied to the right side | | `mb` | number | No | | Margin applied to the bottom side | | `ml` | number | No | | Margin applied to the left side | | `mx` | number | No | | Horizontal margin applied to left and right sides | | `my` | number | No | | Vertical margin applied to top and bottom sides | | `p` | number | No | | Padding applied to all sides | | `pt` | number | No | | Padding applied to the top side | | `pr` | number | No | | Padding applied to the right side | | `pb` | number | No | | Padding applied to the bottom side | | `pl` | number | No | | Padding applied to the left side | | `px` | number | No | | Horizontal padding applied to left and right sides | | `py` | number | No | | Vertical padding applied to top and bottom sides | | `fullWidth` | boolean | No | | Makes the component fill the full width of its parent | | `w` | DimensionValue | No | | Sets a specific width | | `h` | DimensionValue | No | | Sets a specific height | | `maxW` | DimensionValue | No | | Sets the maximum width | | `minW` | DimensionValue | No | | Sets the minimum width | | `maxH` | DimensionValue | No | | Sets the maximum height | | `minH` | DimensionValue | No | | Sets the minimum height | ## Examples ### Basic ID: `Knob.basic` β€’ Tags: basic, knob, control β€’ Category: basics β€’ Status: experimental β€’ Since: 1.0.0 Controlled knob with percentage formatting and a mirrored readout below the dial. ```tsx const [value, setValue] = useState(90); return ( Math.round(current), suffix: 'Β°', }} /> ); } ``` ### Events ID: `Knob.events` β€’ Tags: events, scrubbing, callbacks β€’ Category: behavior β€’ Status: experimental β€’ Since: 1.0.0 Demonstrates live value, committed value, and scrubbing lifecycle callbacks. ```tsx const [value, setValue] = useState(32); const [isScrubbing, setIsScrubbing] = useState(false); const [committed, setCommitted] = useState(value); return ( setIsScrubbing(true)} onScrubEnd={() => setIsScrubbing(false)} /> ); } ``` ### Visual Variants ID: `Knob.variants` β€’ Tags: variant, style, preset β€’ Category: variants β€’ Status: experimental β€’ Since: 1.0.0 `variant` picks the dial's look, independent of `behavior`. Each preset sets stroke weights, caps, body fill, and which indicator carries the value, taking its colors from the theme so the same knob reads correctly in light and dark. Presets are merged *under* `appearance`, so any single property stays overridable. ```tsx const VARIANTS: { variant: KnobVariant; blurb: string }[] = [ { variant: 'default', blurb: 'Stock dial' }, { variant: 'minimal', blurb: 'Hairline, dense UIs' }, { variant: 'digital', blurb: 'Hard edges, lit marker' }, { variant: 'retro', blurb: 'Solid body, indicator arm' }, { variant: 'studio', blurb: 'Plugin rack' }, ]; const [value, setValue] = useState(62); return ( {VARIANTS.map(({ variant, blurb }) => ( {variant} ))} ); } ``` ### Endless ID: `Knob.endless` β€’ Tags: endless, encoder, rotation β€’ Category: variants β€’ Status: experimental β€’ Since: 1.0.0 Endless mode resets the dial each turn while tracking the cumulative rotation value. ```tsx const [value, setValue] = useState(0); const normalizedAngle = useMemo(() => ((value % 360) + 360) % 360, [value]); const rotations = useMemo(() => value / 360, [value]); return ( `${Math.round(normalizedAngle)}Β°`, secondary: { formatter: () => `${rotations.toFixed(2)} turns`, }, }} /> ); } ``` ### Dual Readout ID: `Knob.dual-readout` β€’ Tags: dual, valueLabel β€’ Category: variants β€’ Status: experimental β€’ Since: 1.1.0 Demonstrates the `dual` behavior with a center frequency label and a derived percentage below the knob. ```tsx const [cutoff, setCutoff] = useState(3200); const percent = useMemo(() => Math.round(((cutoff - 200) / (8000 - 200)) * 100), [cutoff]); return ( `${Math.round(val)} Hz`, secondary: { position: 'bottom', formatter: () => `${percent}% span`, }, }} marks={[ { value: 400, label: 'Warm' }, { value: 1200, label: 'Neutral' }, { value: 6400, label: 'Bright' }, ]} /> ); } ``` ### Status Selector ID: `Knob.status-selector` β€’ Tags: status, behavior β€’ Category: variants β€’ Status: experimental β€’ Since: 1.1.0 Highlights the `status` behavior with icon-enhanced marks, accent colors, and the active scene named directly beneath the icon in the center slot. ```tsx const [value, setValue] = useState(0); const statusMarks = useMemo( () => STATUS_SCENES.map(scene => ({ ...scene, icon: , })), [] ); const activeStatus = useMemo( () => statusMarks.reduce((closest, mark) => ( Math.abs(mark.value - value) < Math.abs(closest.value - value) ? mark : closest ), statusMarks[0]), [statusMarks, value] ); return ( activeStatus.label, }} /> ); } ``` ### Segmented Progress ID: `Knob.segment-progress` β€’ Tags: segments, progress, ring, gauge β€’ Category: features β€’ Status: experimental β€’ Since: 1.0.0 The same bands with `ring.segmentMode: 'progress'`, which makes them the progress arc itself: they stop at the current value and nothing is drawn beyond it, so the fill runs through each color in turn. ```tsx const ZONES = [ { value: 60, color: '#22c55e' }, { value: 25, color: '#f59e0b' }, { value: 15, color: '#ef4444' }, ]; const [load, setLoad] = useState(72); return ( `${Math.round(val)}%` }} /> ); } ``` ### Tick Selector ID: `Knob.tick-selector` β€’ Tags: ticks, selector, detent, active β€’ Category: features β€’ Status: experimental β€’ Since: 1.0.0 A twelve-position rotary switch using `activeMode: 'nearest'`, which lights only the tick the pointer is aimed at. The default `'fill'` instead lights every tick up to the value, the way a meter fills. ```tsx // Twelve detents on a full circle. `max` is 12 rather than 11 so position 11 sits one step // short of the top instead of overlapping position 0. Each detent carries its own const POSITIONS = POSITION_COLORS.map((accentColor, index) => ({ value: index, accentColor })); const [position, setPosition] = useState(3); return ( (mark?.accentColor ? `${mark.accentColor}44` : '#475569'), }, ], }} valueLabel={{ formatter: (val) => `${Math.round(val) + 1}` }} /> ); } ``` ### Compound Panning ID: `Knob.compound-panning` β€’ Category: general Stereo-style panning knob composed with `Knob.Root`, split progress, and custom tick labels to highlight the compound sub-component API. ```tsx // The split arc reads the same on both sides of center: direction is carried by which way // the arc grows and by the L/R label, not by a color change. const PAN_COLOR = '#4ade80'; const [pan, setPan] = useState(-18); const readout = useMemo(() => { if (pan === 0) return 'Center'; return pan > 0 ? `Right ${Math.abs(pan)}` : `Left ${Math.abs(pan)}`; }, [pan]); return ( `${value > 0 ? 'R' : value < 0 ? 'L' : ''}${Math.abs(Math.round(value))}`} textStyle={{ fontSize: 30, fontWeight: '700', color: '#f8fafc' }} /> Stereo balance Β· {readout} ); } ``` ### Pointer Clock ID: `Knob.pointer-clock` β€’ Category: general Read-only analog clock face built with the compound `Knob.Root` API: a bezel ring, 60 minute marks with bolder hour marks, hour numerals, and `Knob.Pointer` as the hour hand driven by the value (minutes past 12). The minute and second hands are plain rotated views composed over the same center, synced to the system clock each second. ```tsx const SIZE = 240; const CENTER = SIZE / 2; const MINUTES_PER_TURN = 12 * 60; const HOUR_VALUES = Array.from({ length: 12 }, (_, index) => index * 60); const HOUR_LABELS = ['12', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11']; // 60 minute marks around the dial; hour positions are drawn by the layer above. const MINUTE_VALUES = Array.from({ length: 60 }, (_, index) => index * 12).filter( (value) => value % 60 !== 0 ); const formatTime = (minutes: number) => { const hour = Math.floor(minutes / 60); return `${hour === 0 ? 12 : hour}:${(minutes % 60).toString().padStart(2, '0')}`; }; /** Hand pivoting on the dial center: the wrapper is twice the hand length, so it rotates around it. */ const Hand = ({ angle, length, width, color, tail = 0, }: { angle: number; length: number; width: number; color: string; tail?: number; }) => ( ); const theme = useTheme(); const face = theme.backgrounds.surface; const ink = theme.text.primary; const accent = theme.colors.primary[6]; // Start on a fixed time so server-rendered and client markup match, then sync on mount. const [time, setTime] = useState({ minutes: 10 * 60 + 10, seconds: 0 }); useEffect(() => { const tick = () => { const now = new Date(); setTime({ minutes: (now.getHours() % 12) * 60 + now.getMinutes(), seconds: now.getSeconds(), }); }; tick(); const id = setInterval(tick, 1000); return () => clearInterval(id); }, []); return ( {/* Chapter ring just inside the minute marks */} HOUR_LABELS[index], position: 'inner', offset: -26, style: { color: ink, fontSize: 15, fontWeight: '600' }, }} /> {/* Hour hand β€” the knob value is minutes past 12, so it advances gradually. */} {formatTime(time.minutes)} ); } ``` ### Semicircle Gauge ID: `Knob.semicircle-gauge` β€’ Category: general Semicircle gauge layout that uses `Knob.Root` with custom ring thickness, contiguous progress, and a pointer hand for instrumentation-style readouts. ```tsx const TEMPERATURE_STOPS = [0, 25, 50, 75, 100]; const [level, setLevel] = useState(62); const status = useMemo(() => { if (level >= 85) return 'Critical'; if (level >= 60) return 'Elevated'; if (level >= 35) return 'Nominal'; return 'Idle'; }, [level]); return ( `${TEMPERATURE_STOPS[index]}%`, offset: 24, style: { color: '#cbd5f5', fontSize: 12, fontWeight: '600' }, }} /> `${Math.round(value)}% capacity`} textStyle={{ fontSize: 18, fontWeight: '600', color: '#f8fafc' }} secondary={{ formatter: () => status, position: 'bottom', textStyle: { fontSize: 14, color: '#94a3b8', marginTop: 4 }, }} /> Thermal headroom Β· {status} ); } ``` ### Tick Layers ID: `Knob.tick-layers` β€’ Category: general Stacks two tick layers on one dial: labelled lines driven by `marks`, over a finer dot scale from an explicit step list. ```tsx const LEVEL_MARKS = [ { value: 0, label: 'Mute' }, { value: 25, label: 'Low' }, { value: 50, label: 'Mid' }, { value: 75, label: 'High' }, { value: 100, label: 'Max' }, ]; const [level, setLevel] = useState(48); return ( `${Math.round(val)}%` }} /> ); } ``` ### Interaction modes ID: `Knob.interaction-modes` β€’ Tags: interaction, gestures, scroll β€’ Category: behavior β€’ Status: experimental β€’ Since: 1.0.0 Showcases spin, vertical-slide, horizontal-slide, and scroll gestures enabled through `appearance.interaction`, updating the label as each mode locks in. ```tsx const MODES = [ { key: 'spin', name: 'Spin', detail: 'Drag in a circular path. Move away from the thumb for finer adjustments.', }, { key: 'vertical-slide', name: 'Vertical slide', detail: 'Grab either side of the knob and drag up or down for mixer-style throws.', }, { key: 'horizontal-slide', name: 'Horizontal slide', detail: 'Start above or below the center, then drag left or right for sideways sweeps.', }, { key: 'scroll', name: 'Scroll', detail: 'Hover with a mouse or trackpad and use the wheel/two-finger scroll.', }, ] as const; type ModeName = (typeof MODES)[number]['key']; const MODE_LABELS: Record = MODES.reduce((acc, mode) => { acc[mode.key] = mode.name; return acc; }, {} as Record); const theme = useTheme(); const [value, setValue] = useState(12); const [activeMode, setActiveMode] = useState(null); return ( Multimodal control `${current > 0 ? '+' : ''}${Math.round(current)}`, secondary: { position: 'bottom', formatter: () => (activeMode ? `${MODE_LABELS[activeMode]} mode` : 'Try a gesture'), }, }} appearance={{ arc: { startAngle: -135, sweepAngle: 270, clampInput: true }, ring: { thickness: 16, color: '#0f172a', trailColor: '#1e293b' }, fill: { color: '#020617', radiusOffset: -14 }, progress: { mode: 'split', roundedCaps: true, thickness: 10, color: '#38bdf8', trailColor: '#475569', }, interaction: { modes: MODES.map((mode) => mode.key), lockThresholdPx: 32, slideRatio: 1.5, variancePx: 6, spinPrecisionRadius: 80, respectStartSide: true, scroll: { enabled: true, ratio: 0.8, preventPageScroll: true }, onModeChange: setActiveMode, }, }} /> {MODES.map((mode) => ( {/* The mode currently driving the knob is pulled up to full-contrast text. */} {mode.name} {mode.detail} ))} ); } ``` -------------------------------------------------------------------------------- # Link A versatile component for creating styled hyperlinks and navigation elements with hover states and accessibility features. ## Metadata - Canonical name: `Link` - Package: `@platform-blocks/react-ui-library` - Import: `import { Link } from '@platform-blocks/react-ui-library';` - Category: navigation - Tags: link, anchor, navigation, url, href - Docs: https://react-ui-library.com/components/Link - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Link ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `children` | React.ReactNode | Yes | | Link text content | | `href` | string | No | | URL or handler for the link | | `onPress` | () => void | No | | Custom onPress handler (overrides href) | | `size` | SizeValue | No | 'lg' | Size of the link text (default: 'lg' = 16px to match Text component) | | `color` | 'primary' \| 'secondary' \| 'success' \| 'warning' \| 'error' \| 'gray' \| 'inherit' \| string | No | | Color variant or custom color string | | `variant` | 'default' \| 'subtle' \| 'hover-underline' | No | 'default' | Link variant | | `disabled` | boolean | No | false | Whether the link is disabled | | `external` | boolean | No | false | Whether to show external link indicator | | `style` | ViewStyle | No | | Custom style for container | | `textStyle` | TextStyle | No | | Custom style for text | | `accessibilityLabel` | string | No | | Accessibility label | | `target` | '_blank' \| '_self' | No | '_self' | Whether this link opens in a new tab/window (web only) | | `fontFamily` | string | No | | Custom font family (overrides theme font) | | `ff` | string | No | | Shorthand alias for `fontFamily` | | `m` | number | No | | Margin applied to all sides | | `mt` | number | No | | Margin applied to the top side | | `mr` | number | No | | Margin applied to the right side | | `mb` | number | No | | Margin applied to the bottom side | | `ml` | number | No | | Margin applied to the left side | | `mx` | number | No | | Horizontal margin applied to left and right sides | | `my` | number | No | | Vertical margin applied to top and bottom sides | | `p` | number | No | | Padding applied to all sides | | `pt` | number | No | | Padding applied to the top side | | `pr` | number | No | | Padding applied to the right side | | `pb` | number | No | | Padding applied to the bottom side | | `pl` | number | No | | Padding applied to the left side | | `px` | number | No | | Horizontal padding applied to left and right sides | | `py` | number | No | | Vertical padding applied to top and bottom sides | ## Examples ### Inline Links ID: `Link.basic` β€’ Tags: link β€’ Category: usage β€’ Status: stable β€’ Since: 1.0.0 Embed links directly inside supporting copy to guide readers toward related resources. ```tsx const resources = [ { href: '#brand', label: 'brand guidelines' }, { href: '#voice', label: 'voice and tone guide' }, { href: '#releases', label: 'release checklist' }, ]; const [brandGuide, voiceGuide, releaseChecklist] = resources; return ( Use `Link` inline with body copy to direct readers to additional guidance without breaking the flow of text. Before publishing, review the{' '} {brandGuide.label}, consult our{' '} {voiceGuide.label}, and confirm each launch in the{' '} {releaseChecklist.label}. ); } ``` ### External Destinations ID: `Link.external` β€’ Tags: link, external β€’ Category: usage β€’ Status: stable β€’ Since: 1.0.0 Use the `external` prop when pointing to destinations outside the current shell. ```tsx const references = [ { href: 'https://reactnative.dev', label: 'React Native documentation', color: 'primary' }, { href: 'https://expo.dev', label: 'Expo documentation', color: 'secondary' }, { href: 'mailto:support@example.com', label: 'Email support', color: 'gray' }, ]; return ( Set `external` to ensure the link opens outside the app shell and receives the proper accessibility attributes. {references.map((resource) => ( {resource.label} ))} ); } ``` ### Size Options ID: `Link.sizes` β€’ Tags: link, sizing β€’ Category: appearance β€’ Status: stable β€’ Since: 1.0.0 Demonstrate how the `size` token scales link typography and spacing. ```tsx const SIZES = ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl'] as const; return ( {SIZES.map((size) => ( Link {size} ))} ); } ``` ### Visual Variants ID: `Link.variants` β€’ Tags: link, appearance β€’ Category: appearance β€’ Status: stable β€’ Since: 1.0.0 Compare persistent and hover-only underlines alongside subtle variants. ```tsx const linkVariants = [ { label: 'Default underline', variant: 'default' as const, description: 'Underline is always visible for maximum affordance.' }, { label: 'Hover underline', variant: 'hover-underline' as const, description: 'Underline appears on hover for denser layouts.' }, { label: 'Subtle primary', variant: 'subtle' as const, color: 'primary', description: 'Muted style that still matches the brand palette.' }, { label: 'Subtle gray', variant: 'subtle' as const, color: 'gray', description: 'Pair with neutral layouts or footers.' }, ]; return ( Choose a `variant` that matches the surrounding density while keeping the link discoverable. {linkVariants.map((entry) => ( {entry.label} {entry.description} ))} ); } ``` -------------------------------------------------------------------------------- # ListGroup The ListGroup component provides an organized list structure with items, dividers, and sections for displaying grouped content. ## Metadata - Canonical name: `ListGroup` - Package: `@platform-blocks/react-ui-library` - Import: `import { ListGroup } from '@platform-blocks/react-ui-library';` - Category: display - Tags: list, group, items, divider, sections - Docs: https://react-ui-library.com/components/ListGroup - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/ListGroup ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `children` | React.ReactNode | Yes | | | | `variant` | 'default' \| 'bordered' \| 'flush' | No | 'default' | | | `size` | ComponentSizeValue | No | 'md' | | | `radius` | 'sm' \| 'md' \| 'lg' \| number | No | 'md' | | | `dividers` | boolean | No | true | | | `insetDividers` | boolean | No | false | | | `style` | StyleProp | No | | | ## Examples ### Basic Usage ID: `ListGroup.basic` β€’ Tags: list, items β€’ Category: basics β€’ Status: stable β€’ Since: 1.0.0 Compose a vertical list by nesting `ListGroupItem` elements inside a `ListGroup`. Use the `variant` prop to switch between `default`, `bordered`, and `flush` styles. ```tsx return ( Overview Analytics Reports Settings ); } ``` ### Two-line rows ID: `ListGroup.two-line` β€’ Tags: list, label, description, settings β€’ Category: basics β€’ Status: stable β€’ Since: 1.0.0 Pass `label` and `description` for a stacked row. These take precedence over `children`, which renders as a single line of text and so cannot hold a layout block. `description` is optional β€” a `label` on its own reads the same as `children`, and mixing both row shapes in one group stays aligned. ```tsx return ( ); } ``` ### Trailing value ID: `ListGroup.trailing-value` β€’ Tags: list, value, alignment, sections β€’ Category: composition β€’ Status: stable β€’ Since: 1.0.0 `value` renders muted text at the end of the row, before `endSection`. A two-line row already claims the free space, so its value sits flush right on its own; a single-line row only takes its natural width, so the value is what gets pushed to the edge and `endSection` follows it. ```tsx return ( New}> Inbox ); } ``` -------------------------------------------------------------------------------- # Loader A animated loading component for indicating ongoing processes and loading states with various sizes and styles. ## Metadata - Canonical name: `Loader` - Package: `@platform-blocks/react-ui-library` - Import: `import { Loader } from '@platform-blocks/react-ui-library';` - Category: feedback - Tags: loader, loading, progress, indicator, animation - Docs: https://react-ui-library.com/components/Loader - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Loader ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `size` | SizeValue | No | 'md' | Size of the loader - can be a size token or number | | `color` | string | No | | Color of the loader | | `variant` | LoaderVariant | No | 'oval' | Variant of the loader | | `speed` | number | No | 1000 | Animation speed in milliseconds | | `style` | StyleProp | No | | Container style | | `testID` | string | No | | Test ID for testing | | `m` | number | No | | Margin applied to all sides | | `mt` | number | No | | Margin applied to the top side | | `mr` | number | No | | Margin applied to the right side | | `mb` | number | No | | Margin applied to the bottom side | | `ml` | number | No | | Margin applied to the left side | | `mx` | number | No | | Horizontal margin applied to left and right sides | | `my` | number | No | | Vertical margin applied to top and bottom sides | | `p` | number | No | | Padding applied to all sides | | `pt` | number | No | | Padding applied to the top side | | `pr` | number | No | | Padding applied to the right side | | `pb` | number | No | | Padding applied to the bottom side | | `pl` | number | No | | Padding applied to the left side | | `px` | number | No | | Horizontal padding applied to left and right sides | | `py` | number | No | | Vertical padding applied to top and bottom sides | ## Examples ### Basic Usage ID: `Loader.basic` β€’ Tags: variant β€’ Category: basics β€’ Status: stable β€’ Since: 1.0.0 Pick a loader `variant` to match the type of busy indicator you need for a loading state. ```tsx return ( ); } ``` ### Sizes ID: `Loader.sizes` β€’ Tags: size β€’ Category: layout β€’ Status: stable β€’ Since: 1.0.0 Set the `size` token to align loaders with other controls, from `xs` indicators up to `3xl` spinners. ```tsx const SIZES = ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl'] as const; return ( {SIZES.map((size) => ( {size} ))} ); } ``` ### Colors ID: `Loader.colors` β€’ Tags: color, theme β€’ Category: theming β€’ Status: stable β€’ Since: 1.0.0 Pull palette values from `useTheme()` and pass them to the `color` prop to align loaders with your semantic colors. ```tsx interface LoaderSwatch { label: string; color: string; } const theme = useTheme(); const swatches: LoaderSwatch[] = [ { label: 'Primary', color: theme.colors.primary[5] }, { label: 'Success', color: theme.colors.success[5] }, { label: 'Warning', color: theme.colors.warning[5] }, { label: 'Error', color: theme.colors.error[5] } ]; return ( {swatches.map(({ label, color }) => ( {label} ))} ); } ``` ### Speed ID: `Loader.speed` β€’ Category: general ```tsx // `speed` is the duration of one full animation cycle in milliseconds β€” // lower is faster. Default is 1000ms. const SPEEDS = [ { label: 'Fast', value: 400 }, { label: 'Default', value: 1000 }, { label: 'Slow', value: 2000 }, ]; return ( {SPEEDS.map(({ label, value }) => ( {label} {value}ms ))} ); } ``` -------------------------------------------------------------------------------- # LoadingOverlay `LoadingOverlay` composits the core `Overlay` and `Loader` primitives to create a convenient helper for blocking interactions with a visual indicator during asynchronous operations. Render it inside a relatively positioned container, toggle `visible` during asynchronous work, and customize appearance by passing `overlayProps` or `loaderProps`. ## Metadata - Canonical name: `LoadingOverlay` - Package: `@platform-blocks/react-ui-library` - Import: `import { LoadingOverlay } from '@platform-blocks/react-ui-library';` - Status: beta - Category: feedback - Docs: https://react-ui-library.com/components/LoadingOverlay - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/LoadingOverlay ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `visible` | boolean | No | false | Controls visibility of the loading overlay. | | `zIndex` | number | No | | z-index applied to the overlay container. Overrides value defined in overlayProps when provided. | | `overlayProps` | OverlayProps | No | | Props forwarded to the underlying Overlay component. | | `loaderProps` | LoaderProps | No | | Props forwarded to the Loader component. | | `loader` | ReactNode | No | | Custom loader content. When provided, Loader component is not rendered. | ## Examples ### Form blocking ID: `LoadingOverlay.basic` β€’ Tags: overlays, loading β€’ Category: feedback β€’ Status: stable β€’ Since: 1.0.0 Locks a simple form while background work finishes and keeps the loader aligned with the card container. ```tsx type TextFieldConfig = { key: string; } & Pick, 'label' | 'placeholder' | 'keyboardType' | 'secureTextEntry'>; const TEXT_FIELDS: TextFieldConfig[] = [ { key: 'first-name', label: 'First name', placeholder: 'Jane' }, { key: 'last-name', label: 'Last name', placeholder: 'Doe' }, { key: 'email', label: 'Email', placeholder: 'jane@react-ui-library.com', keyboardType: 'email-address', }, { key: 'password', label: 'Password', placeholder: 'β€’β€’β€’β€’β€’β€’β€’β€’', secureTextEntry: true, }, ]; const [visible, setVisible] = useState(false); return ( Account details Pause form interaction while requests finish and keep the layout intact. {TEXT_FIELDS.map(({ key, ...field }) => ( ))} LoadingOverlay anchors to a relative container and dims the content while the loader animates. ); } const styles = StyleSheet.create({ wrapper: { width: '100%', }, section: { width: '100%', maxWidth: 480, alignSelf: 'center', }, card: { width: '100%', }, }); ``` -------------------------------------------------------------------------------- # Markdown Markdown component provides a way to render Markdown content with custom styling and component mapping. It supports standard Markdown syntax including headers, lists, code blocks, and more. ## Metadata - Canonical name: `Markdown` - Package: `@platform-blocks/react-ui-library` - Import: `import { Markdown } from '@platform-blocks/react-ui-library';` - Status: stable - Category: data - Docs: https://react-ui-library.com/components/Markdown - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Markdown ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `children` | string | Yes | | | | `defaultCodeLanguage` | string | No | | Override default code block language guess | | `maxHeadingLevel` | number | No | | Max heading level to render (others downgraded) | | `allowHtml` | boolean | No | | Whether to render inline HTML literally (ignored for now) | | `components` | Partial | No | | Custom renderer overrides | | `onLinkPress` | (href: string) => void | No | | Optional handler invoked when a markdown link is pressed | | `fontFamily` | string | No | | Custom font family applied to all rendered text (overrides the theme font) | | `ff` | string | No | | Shorthand alias for `fontFamily` | | `m` | number | No | | Margin applied to all sides | | `mt` | number | No | | Margin applied to the top side | | `mr` | number | No | | Margin applied to the right side | | `mb` | number | No | | Margin applied to the bottom side | | `ml` | number | No | | Margin applied to the left side | | `mx` | number | No | | Horizontal margin applied to left and right sides | | `my` | number | No | | Vertical margin applied to top and bottom sides | | `p` | number | No | | Padding applied to all sides | | `pt` | number | No | | Padding applied to the top side | | `pr` | number | No | | Padding applied to the right side | | `pb` | number | No | | Padding applied to the bottom side | | `pl` | number | No | | Padding applied to the left side | | `px` | number | No | | Horizontal padding applied to left and right sides | | `py` | number | No | | Vertical padding applied to top and bottom sides | ## Examples ### Basic Usage ID: `Markdown.basic` β€’ Category: general Simple markdown rendering with headers, lists, and text formatting. ```tsx const CONTENT = `# Hello Markdown This is a **bold** statement and this is _italic_. - Item one - Item two - Item three > Blockquote with *inline emphasis* and **strong** text. Inline code: \`const x = 42;\``; return ( {CONTENT} Rendered using the default Markdown renderer ); } ``` ### Code Blocks ID: `Markdown.code` β€’ Category: general Markdown rendering with syntax-highlighted code blocks. ```tsx const CONTENT = `# Code examples Here's some JavaScript: \`\`\`javascript function fibonacci(n) { if (n <= 1) return n; return fibonacci(n - 1) + fibonacci(n - 2); } \`\`\` And some TypeScript: \`\`\`typescript interface User { id: number; name: string; email: string; } const user: User = { id: 1, name: "John Doe", email: "john@example.com" }; \`\`\` Inline code: \`const result = fibonacci(10);\``; return ( {CONTENT} Showcases fenced code blocks with syntax highlighting ); } ``` ### Custom Components ID: `Markdown.custom` β€’ Category: general Custom component mapping for markdown elements. ```tsx const CUSTOM_COMPONENTS = { h1: ({ children, ...props }: any) => ( {children} ), h2: ({ children, ...props }: any) => ( {children} ), p: ({ children, ...props }: any) => ( {children} ), blockquote: ({ children, ...props }: any) => ( {children} ), }; const CONTENT = `# Custom styled Markdown ## This is a subtitle This paragraph uses custom styling and components. > This blockquote is rendered with a custom Card component and muted background. Regular paragraph text with default styling. `; return ( {CONTENT} Headings, paragraphs, and quotes use custom renderers ); } ``` ### Inline Usage ID: `Markdown.inline` β€’ Category: general Using markdown inline within other text content. ```tsx const inlineContent = 'This is **bold text** and this is *italic text* with `inline code`.'; return ( Inline markdown: {inlineContent} Mix with regular text: Here's some regular text, then **markdown formatting** and back to regular. Code in context: Use `const x = 42;` to declare a variable. ); } ``` ### Media & Tables ID: `Markdown.media` β€’ Category: general Markdown with images, links, tables, and horizontal rules. ```tsx const CONTENT = `# Media in Markdown ## Images ![PlatformBlocks Logo](https://raw.githubusercontent.com/platform-blocks/react-ui-library/main/apps/react-ui-library.com/assets/favicon.png) ## Links Visit the [PlatformBlocks Documentation](https://react-ui-library.com) for more examples. ## Tables | Feature | Status | Notes | |---------|--------|-------| | **Text Formatting** | βœ… | Bold, _italic_, \`code\` | | Code Blocks | βœ… | Syntax highlighting | | Tables | βœ… | Responsive layout | | Images | βœ… | Auto-sizing | | [Links](https://react-ui-library.com) | βœ… | External navigation | ## Horizontal Rule Content above the line. --- Content below the line.`; return ( {CONTENT} Images, links, tables, and horizontal rules render inline ); } ``` ### Table Support ID: `Markdown.table` β€’ Category: general Markdown tables with proper formatting and styling. ```tsx const CONTENT = `# Table examples ## Basic table | Name | Age | City | |------|-----|------| | John Doe | 30 | New York | | Jane Smith | 25 | Los Angeles | | Bob Johnson | 35 | Chicago | ## Table with formatting | Feature | Status | **Priority** | Notes | |---------|--------|-------------|--------| | Authentication | βœ… | **High** | _Complete_ | | User Management | πŸ”„ | **Medium** | In progress | | Analytics | ❌ | **Low** | \`Not started\` | | API Integration | βœ… | **High** | [Documentation](https://example.com) | ## Table with code | Language | Extension | Sample code | |----------|-----------|-------------| | TypeScript | \`.tsx\` | \`const x: string = "hello";\` | | JavaScript | \`.js\` | \`function hello() { return "world"; }\` | | Python | \`.py\` | \`def hello(): return "world"\` | ## Complex table | Component | **Props** | _Description_ | Example | |-----------|----------|-------------|---------| | Button | \`variant\`, \`size\`, \`disabled\` | Interactive button element | \`\` | | Input | \`placeholder\`, \`value\`, \`onChange\` | Text input field | \`\` | | Card | \`variant\`, \`padding\` | Container component | \`Content\` |`; return ( {CONTENT} Multiple table layouts rendered with Markdown ); } ``` -------------------------------------------------------------------------------- # Masonry Masonry provides an efficient way to create Pinterest-style layouts where items are arranged in columns with varying heights. Built on FlashList for optimal performance with large datasets, it automatically handles item positioning and provides smooth scrolling even with hundreds of items. The component supports dynamic heights through the heightRatio property on items, custom renderers, and responsive column counts. Perfect for image galleries, card layouts, or any scenario where you need an organic, space-efficient arrangement of content. ## Metadata - Canonical name: `Masonry` - Package: `@platform-blocks/react-ui-library` - Import: `import { Masonry } from '@platform-blocks/react-ui-library';` - Status: stable - Category: layout - Docs: https://react-ui-library.com/components/Masonry - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Masonry ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `data` | MasonryItem[] | Yes | | Array of items to display in masonry layout | | `numColumns` | number | No | | Number of columns (default: 2) | | `gap` | SizeValue | No | | Spacing between items | | `optimizeItemArrangement` | boolean | No | | Whether to optimize for staggered grid layout | | `renderItem` | (item: MasonryItem, index: number) => ReactNode | No | | Custom item renderer - receives item and index | | `contentContainerStyle` | StyleProp | No | | Content container style | | `style` | StyleProp | No | | Custom styles | | `testID` | string | No | | Test ID for testing | | `loading` | boolean | No | | Loading state | | `emptyContent` | ReactNode | No | | Empty state content | | `flashListProps` | MasonryFlashListProps | No | | Flash list props to pass through | | `onEndReached` | ((info: { distanceFromEnd: number }) => void) \| null | No | | Callback when the end of the list is reached (for pagination / infinite scroll) | | `onEndReachedThreshold` | number | No | | Distance from end (in pixels) to trigger onEndReached (default: FlashList default) | | `onViewableItemsChanged` | MasonryViewabilityCallback | No | | Callback when viewable items change | | `scrollEnabled` | boolean | No | | Whether scrolling is enabled | | `ListEmptyComponent` | React.ComponentType \| React.ReactElement \| null | No | | Component rendered when the list is empty | | `ListFooterComponent` | React.ComponentType \| React.ReactElement \| null | No | | Component rendered at the bottom of the list | | `ListHeaderComponent` | React.ComponentType \| React.ReactElement \| null | No | | Component rendered at the top of the list | | `estimatedItemSize` | number | No | | Estimated size of each item (performance hint) | | `refreshControl` | React.ReactElement | No | | Pull-to-refresh control | | `onScroll` | ScrollViewProps['onScroll'] | No | | Scroll event callback | | `scrollEventThrottle` | number | No | | Throttle interval for scroll events in ms | | `m` | number | No | | Margin applied to all sides | | `mt` | number | No | | Margin applied to the top side | | `mr` | number | No | | Margin applied to the right side | | `mb` | number | No | | Margin applied to the bottom side | | `ml` | number | No | | Margin applied to the left side | | `mx` | number | No | | Horizontal margin applied to left and right sides | | `my` | number | No | | Vertical margin applied to top and bottom sides | | `p` | number | No | | Padding applied to all sides | | `pt` | number | No | | Padding applied to the top side | | `pr` | number | No | | Padding applied to the right side | | `pb` | number | No | | Padding applied to the bottom side | | `pl` | number | No | | Padding applied to the left side | | `px` | number | No | | Horizontal padding applied to left and right sides | | `py` | number | No | | Vertical padding applied to top and bottom sides | ## Examples ### Basic Masonry ID: `Masonry.basic` β€’ Tags: basic, layout, grid, columns, simple β€’ Category: general Simple masonry layout with uniform item heights arranged in a two-column grid. ```tsx const theme = useTheme(); const masonryItems: MasonryItem[] = [ { id: '1', content: ( Card 1 This is a basic card item in the masonry layout. ), }, { id: '2', content: ( Card 2 Short content. ), }, { id: '3', content: ( Card 3 A third card showing how items are arranged in the masonry grid with longer content that will make this card taller than the others. ), }, { id: '4', content: ( Card 4 Medium length content here. ), }, { id: '5', content: ( Card 5 Fifth card in the masonry layout grid. ), }, { id: '6', content: ( Card 6 Sixth card showing the two-column arrangement. ), }, ]; return ( ); } ``` ### Custom Columns ID: `Masonry.custom-columns` β€’ Tags: columns, responsive, grid, arrangement, configurable β€’ Category: general Masonry layout with configurable number of columns demonstrating different grid arrangements. ```tsx const theme = useTheme(); const [numColumns, setNumColumns] = useState(3); const masonryItems: MasonryItem[] = [ { id: '1', heightRatio: 1.1, content: ( Item 1 Content for first item with some extra text. ), }, { id: '2', heightRatio: 0.8, content: ( Item 2 Short content. ), }, { id: '3', heightRatio: 1.3, content: ( Item 3 Longer content to demonstrate height variation in different column layouts. ), }, { id: '4', heightRatio: 0.9, content: ( Item 4 Medium length content. ), }, { id: '5', heightRatio: 1.5, content: ( Item 5 Extended content that takes up more space to show how columns adapt. ), }, { id: '6', heightRatio: 0.7, content: ( Item 6 Compact. ), }, { id: '7', heightRatio: 1.2, content: ( Item 7 Another item with moderate content length for testing. ), }, { id: '8', heightRatio: 0.9, content: ( Item 8 Standard content item. ), }, { id: '9', heightRatio: 1.4, content: ( Item 9 Taller content to fill out the grid and show column distribution effects. ), }, ]; return ( <> }> Profile }> Settings }> Help & Support }> Logout ); } ``` ### Context Trigger ID: `Menu.context` β€’ Tags: menu, contextmenu β€’ Category: behavior β€’ Status: stable β€’ Since: 1.0.0 Enable `trigger="contextmenu"` to surface a menu when users right-click or long-press a target. ```tsx Block, Card, Icon, Menu, MenuDivider, MenuDropdown, MenuItem, Text, } from '@platform-blocks/react-ui-library'; return ( Right-click or long-press this area }> Copy link }> Rename }> Share }> Delete ); } ``` ### Placement Presets ID: `Menu.positioning` β€’ Tags: menu, position β€’ Category: layout β€’ Status: stable β€’ Since: 1.0.0 Adjust the `position` prop to pin dropdown content to any edge of the trigger. ```tsx const POSITIONS = [ { label: 'Bottom start', position: 'bottom-start' }, { label: 'Bottom', position: 'bottom' }, { label: 'Bottom end', position: 'bottom-end' }, { label: 'Top start', position: 'top-start' }, { label: 'Top', position: 'top' }, { label: 'Top end', position: 'top-end' }, ] as const; return ( {POSITIONS.map(({ label, position }) => ( Duplicate Archive ))} ); } ``` ### Submenu ID: `Menu.submenu` β€’ Category: general ```tsx Button, Icon, Menu, MenuDivider, MenuDropdown, MenuItem, MenuLabel, MenuSub, } from '@platform-blocks/react-ui-library'; return ( Document }>Rename {/* Flyout submenu β€” opens to the side on hover (web) or tap */} }> }>Copy link }>Email {/* Submenus nest arbitrarily deep */} Twitter / X LinkedIn Reddit }> Projects Archive Trash }> Delete ); } ``` -------------------------------------------------------------------------------- # MenuItemButton A row button used inside menus and command palettes. The inner label `` accepts the full Text-prop API via `labelProps` (`ff`, `weight`, `tracking`, `uppercase`, `color`, `style`). ## Metadata - Canonical name: `MenuItemButton` - Package: `@platform-blocks/react-ui-library` - Import: `import { MenuItemButton } from '@platform-blocks/react-ui-library';` - Category: navigation - Tags: menu, dropdown, command, item, button - Docs: https://react-ui-library.com/components/MenuItemButton - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/MenuItemButton ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `title` | string | No | | Text label (alternative to children) | | `children` | React.ReactNode | No | | Custom content | | `startIcon` | React.ReactNode | No | | Leading icon | | `endIcon` | React.ReactNode | No | | Trailing icon / shortcut hint | | `onPress` | () => void | No | | Click handler | | `disabled` | boolean | No | | Whether the button is disabled | | `active` | boolean | No | | Whether the button is active (selected) | | `danger` | boolean | No | | Whether the button has destructive styling | | `fullWidth` | boolean | No | | Whether the button should take up full width | | `size` | ComponentSizeValue | No | | Size of the button | | `compact` | boolean | No | | Whether to use compact styling | | `rounded` | boolean | No | | Whether to use fully rounded corners | | `style` | any | No | | Custom styles override | | `onPressIn` | (event: GestureResponderEvent) => void | No | | Callback fired when press starts | | `onPressOut` | (event: GestureResponderEvent) => void | No | | Callback fired when press ends | | `onMouseDown` | (event: any) => void | No | | Web-only mouse down handler | | `onMouseEnter` | (event: any) => void | No | | Web-only mouse enter handler | | `onMouseLeave` | (event: any) => void | No | | Web-only mouse leave handler | | `onHoverIn` | PressableProps['onHoverIn'] | No | | Pointer hover start handler (web) | | `onHoverOut` | PressableProps['onHoverOut'] | No | | Pointer hover end handler (web) | | `onFocus` | PressableProps['onFocus'] | No | | Focus handler | | `onBlur` | PressableProps['onBlur'] | No | | Blur handler | | `color` | MenuItemColor | No | | Semantic color for menu styling | | `hoverColor` | MenuItemColor | No | | Color to apply when hovered | | `activeColor` | MenuItemColor | No | | Color to apply when active/pressed | | `textColor` | string | No | | Override text color for base state | | `hoverTextColor` | string | No | | Override text color when hovered | | `activeTextColor` | string | No | | Override text color when active | | `testID` | string | No | | Test identifier forwarded to Pressable | | `labelProps` | Omit | No | | Override props applied to the inner label `` (style, weight, ff, size, color). | | `m` | number | No | | Margin applied to all sides | | `mt` | number | No | | Margin applied to the top side | | `mr` | number | No | | Margin applied to the right side | | `mb` | number | No | | Margin applied to the bottom side | | `ml` | number | No | | Margin applied to the left side | | `mx` | number | No | | Horizontal margin applied to left and right sides | | `my` | number | No | | Vertical margin applied to top and bottom sides | | `p` | number | No | | Padding applied to all sides | | `pt` | number | No | | Padding applied to the top side | | `pr` | number | No | | Padding applied to the right side | | `pb` | number | No | | Padding applied to the bottom side | | `pl` | number | No | | Padding applied to the left side | | `px` | number | No | | Horizontal padding applied to left and right sides | | `py` | number | No | | Vertical padding applied to top and bottom sides | ## Examples ### Basic Menu ID: `MenuItemButton.basic` β€’ Category: general Simple dropdown menu with icons and dividers. ```tsx return ( }> Profile }> Settings }> Help & Support }> Logout ) } ``` -------------------------------------------------------------------------------- # MiniCalendar A compact calendar component for displaying a month view with selectable dates. ## Metadata - Canonical name: `MiniCalendar` - Package: `@platform-blocks/react-ui-library` - Import: `import { MiniCalendar } from '@platform-blocks/react-ui-library';` - Category: dates - Docs: https://react-ui-library.com/components/MiniCalendar - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/MiniCalendar ## Props _No documented props yet._ ## Examples ### Basic MiniCalendar ID: `MiniCalendar.basic` β€’ Category: general Compact calendar showing a week view with date selection. ```tsx const [selectedDate, setSelectedDate] = useState(new Date()); return ( setSelectedDate(date)} numberOfDays={7} /> {selectedDate ? `Selected: ${selectedDate.toLocaleDateString()}` : 'No date selected'} ); } ``` ### Custom Day Count ID: `MiniCalendar.customDays` β€’ Category: general MiniCalendar with configurable number of days displayed. ```tsx const DAY_OPTIONS = [3, 5, 7]; const [selectedDate, setSelectedDate] = useState(new Date()); const [numberOfDays, setNumberOfDays] = useState(5); return ( {DAY_OPTIONS.map((days) => ( ))} setSelectedDate(date)} numberOfDays={numberOfDays} /> {selectedDate ? `Selected: ${selectedDate.toLocaleDateString()}` : 'No date selected'} ); } ``` ### Date Constraints ID: `MiniCalendar.constrained` β€’ Category: general MiniCalendar with minimum and maximum date restrictions. ```tsx const [selectedDate, setSelectedDate] = useState(new Date()); const { minDate, maxDate } = useMemo(() => { const today = new Date(); const nextWeek = new Date(); nextWeek.setDate(today.getDate() + 7); return { minDate: today, maxDate: nextWeek }; }, []); return ( setSelectedDate(date)} numberOfDays={7} minDate={minDate} maxDate={maxDate} /> {selectedDate ? `Selected: ${selectedDate.toLocaleDateString()}` : 'No date selected'} Only the next seven days are enabled ); } ``` -------------------------------------------------------------------------------- # MonthPicker Interactive grid for selecting a month within a given year. Renders a responsive layout that adapts to screen width and respects locale formatting as well as min/max date constraints. ## Metadata - Canonical name: `MonthPicker` - Package: `@platform-blocks/react-ui-library` - Import: `import { MonthPicker } from '@platform-blocks/react-ui-library';` - Category: dates - Tags: date, month, picker, calendar - Docs: https://react-ui-library.com/components/MonthPicker - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/MonthPicker ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `value` | Date \| null | No | | Currently selected date (uses the first day of the month) | | `onChange` | (date: Date \| null) => void | No | | Called when user picks a new month | | `year` | number | No | | Force a specific year to render | | `onYearChange` | (year: number) => void | No | | Called when the visible year changes | | `minDate` | Date | No | | Minimum selectable date (inclusive) | | `maxDate` | Date | No | | Maximum selectable date (inclusive) | | `locale` | string | No | | Locale used for month labels | | `size` | ComponentSizeValue | No | | Size token that influences typography weight | | `monthLabelFormat` | 'short' \| 'long' | No | | Format of month labels | | `hideHeader` | boolean | No | | Hide navigation header (used when embedded in Calendar) | | `monthsPerRow` | ResponsiveProp | No | | Responsive override for the number of months rendered per row | | `fullWidth` | boolean | No | | Stretch to fill the container instead of sizing to the natural grid width. Default `false`. | ## Examples ### Basic ID: `MonthPicker.basic` β€’ Category: general ```tsx const [value, setValue] = useState(new Date()); return ( {value ? value.toLocaleDateString(undefined, { month: 'long', year: 'numeric' }) : 'No month selected'} ); } ``` -------------------------------------------------------------------------------- # MonthPickerInput Form-friendly wrapper around `MonthPicker` that renders an input field and opens the picker in a modal dialog. Mirrors the `DatePickerInput` API for consistency while focusing on month-level selection workflows. ## Metadata - Canonical name: `MonthPickerInput` - Package: `@platform-blocks/react-ui-library` - Import: `import { MonthPickerInput } from '@platform-blocks/react-ui-library';` - Status: stable - Since: 0.1.0 - Category: dates - Docs: https://react-ui-library.com/components/MonthPickerInput - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/MonthPickerInput ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `value` | Date \| null | No | | Controlled value for the selected month | | `defaultValue` | Date \| null | No | | Default month when uncontrolled | | `onChange` | (value: Date \| null) => void | No | | Called when the month selection changes | | `locale` | string | No | | Locale used for formatting the input value | | `formatOptions` | Intl.DateTimeFormatOptions | No | | Intl format options for rendering the selected month | | `formatValue` | (value: Date) => string | No | | Custom formatter for the input value; overrides locale/formatOptions | | `placeholder` | string | No | | Placeholder text when no month is selected | | `clearable` | boolean | No | | Show a clear button when a month is selected | | `closeOnSelect` | boolean | No | | Close the picker after selecting a month | | `monthPickerProps` | Partial> | No | | Additional props forwarded to MonthPicker (except value) | | `modalTitle` | string | No | | Dialog title text | | `onOpen` | () => void | No | | Called when the picker dialog opens | | `onClose` | () => void | No | | Called when the picker dialog closes | | `variant` | InputVariant | No | | Visual variant of the input. `default` (light surface + border), `filled` (gray fill, no border), `outline` (transparent fill, border only), `unstyled` (no border, no fill). | | `label` | React.ReactNode | No | | Input label (string or component) | | `disabled` | boolean | No | | Whether input is disabled | | `required` | boolean | No | | Whether input is required | | `error` | string | No | | Error message | | `helperText` | string | No | | Helper text | | `description` | string | No | | Optional short description displayed directly under the label (above the field) | | `size` | SizeValue | No | | Input size | | `withAsterisk` | boolean | No | | Whether to show required indicator | | `name` | string | No | | Input name for form integration | | `startSection` | React.ReactNode | No | | Left section content | | `endSection` | React.ReactNode | No | | Right section content | | `style` | any | No | | Additional styling | | `accessibilityLabel` | string | No | | Accessibility label | | `accessibilityHint` | string | No | | Accessibility hint | | `testID` | string | No | | Test ID for testing | | `debounceMs` | number | No | | Debounce delay for validation in milliseconds | | `onFocus` | () => void | No | | Focus handler | | `onBlur` | () => void | No | | Blur handler | | `onEnter` | () => void | No | | Enter key press handler | | `clearButtonLabel` | string | No | | Accessible label for the clear button | | `onClear` | () => void | No | | Callback when the clear button is pressed | | `keyboardFocusId` | string | No | | Identifier used with KeyboardManagerProvider to request refocus | | `labelProps` | Omit | No | | Override props applied to the field label `` (style, weight, ff, etc.) | | `descriptionProps` | Omit | No | | Override props applied to the field description `` | | `placeholderTextColor` | string | No | | Color of the placeholder text. Falls back to `theme.text.muted`. | | `startSectionProps` | Omit | No | | Props applied to the wrapping `` around `startSection` (style, accessibility, etc.). | | `endSectionProps` | Omit | No | | Props applied to the wrapping `` around `endSection`. | | `m` | number | No | | Margin applied to all sides | | `mt` | number | No | | Margin applied to the top side | | `mr` | number | No | | Margin applied to the right side | | `mb` | number | No | | Margin applied to the bottom side | | `ml` | number | No | | Margin applied to the left side | | `mx` | number | No | | Horizontal margin applied to left and right sides | | `my` | number | No | | Vertical margin applied to top and bottom sides | | `p` | number | No | | Padding applied to all sides | | `pt` | number | No | | Padding applied to the top side | | `pr` | number | No | | Padding applied to the right side | | `pb` | number | No | | Padding applied to the bottom side | | `pl` | number | No | | Padding applied to the left side | | `px` | number | No | | Horizontal padding applied to left and right sides | | `py` | number | No | | Vertical padding applied to top and bottom sides | | `fullWidth` | boolean | No | | Makes the component fill the full width of its parent | | `w` | DimensionValue | No | | Sets a specific width | | `h` | DimensionValue | No | | Sets a specific height | | `maxW` | DimensionValue | No | | Sets the maximum width | | `minW` | DimensionValue | No | | Sets the minimum width | | `maxH` | DimensionValue | No | | Sets the maximum height | | `minH` | DimensionValue | No | | Sets the minimum height | | `radius` | RadiusValue | No | | Border radius value - supports size tokens, numbers, and special values | ## Examples ### Basic ID: `MonthPickerInput.basic` β€’ Category: general ```tsx const [value, setValue] = useState(null); return ( {value ? value.toLocaleDateString(undefined, { month: 'long', year: 'numeric' }) : 'No month selected'} ); } ``` -------------------------------------------------------------------------------- # NavTree A sidebar that nests itself. Hand it the flat list of routes an app already has β€” with a category on each β€” and it groups, orders and renders them as a tree. The branches above the current page open on their own, the row for that page is marked and scrolled to, and which branches are open survives a reload. Rows carrying an `href` render as real `` elements on web, so cmd-click, middle-click, "copy link address" and crawlers all work; a plain left-click goes to `onNavigate` for client-side routing. Omit `onNavigate` and the rows stay ordinary links the browser follows. Built on [Tree](/components/Tree), so keyboard navigation, guide lines, filtering and the ARIA `tree`/`treeitem` roles come along with it. ## Metadata - Canonical name: `NavTree` - Package: `@platform-blocks/react-ui-library` - Import: `import { NavTree } from '@platform-blocks/react-ui-library';` - Since: 1.1.0 - Category: navigation - Tags: navigation, sidebar, tree, menu, routes - Docs: https://react-ui-library.com/components/NavTree - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/NavTree ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `items` | NavTreeItem[] | Yes | | The destinations, flat. Grouped and nested by `buildNavTree`. | | `activeHref` | string | No | | Current route. Marks its row and opens the groups above it. | | `onNavigate` | (item: NavTreeItem, node: TreeNode) => void | No | | Where a row press goes. Supply it to route client-side; without it the rows stay plain links and the browser navigates. | | `size` | ComponentSizeValue | No | 'sm' | Row density. @default 'sm' | | `collapsed` | boolean | No | | Rail mode: only the top level renders, as icons. For a sidebar that collapses to a strip β€” the full tree is one hover away, and a column of every leaf's icon is not navigation, it is noise. | | `searchable` | boolean | No | | Show a filter field above the tree, wired to `filterQuery`. Past a certain length no amount of nesting beats typing three letters, and every sidebar that needs one would otherwise wire the same input and the same state. Pass `filterQuery` as well to drive it from outside; on its own the field keeps its own query. Hidden in `collapsed` mode, where there is no room. | | `searchPlaceholder` | string | No | 'Filter…' | Placeholder for the filter field. @default 'Filter…' | | `highlightMatches` | boolean | No | true | Matched substrings are marked in the row labels. @default true | | `style` | StyleProp | No | | | | `groupOrder` | string[] | No | | Curated order for group labels, checked at every level. Groups not listed follow, alphabetically β€” so a partial order is enough, and a new group appears in a sensible place without touching this. Entries are bare labels (`'Input'`) or full paths (`'Hooks/Navigation'`). A path wins over a bare label, which is how the same name can rank differently in two branches. | | `groupIcons` | Record | No | | Leading icon per group label. | | `sortLeaves` | 'alpha' \| 'none' | No | 'alpha' | How leaves inside a group are ordered. - `'alpha'` β€” by `order` then label. The default: a long list is easier to scan alphabetically than in whatever order the array happened to be in. - `'none'` β€” keep the order given. | | `openDepth` | number | No | 1 | Groups shallower than this start open. `1` opens the top level and leaves everything below it closed, which is the shape a docs sidebar wants: the sections are visible, the long category lists are not. | | `openGroups` | string[] | No | | Group labels (or full `A/B` paths) to open regardless of `openDepth`. | | `getGroupNode` | (context: { label: string; path: string[]; depth: number; items: NavTreeItem[]; }) => Partial | No | | Decorates each group row β€” a count, a badge, an icon. Receives the group's path from the root and the items beneath it, at every level. | ## Examples ### Grouped routes ID: `NavTree.basic` β€’ Tags: navtree, sidebar β€’ Category: navigation β€’ Status: stable β€’ Since: 1.1.0 A flat list of routes becomes a nested sidebar. The group above the active route is already open, because `activeHref` opened it. ```tsx // The whole input: a flat list, with a category on each row. Nothing here // describes the tree β€” `NavTree` derives it. const ROUTES: NavTreeItem[] = [ { label: 'Getting Started', href: '/getting-started' }, { label: 'Button', href: '/components/Button', group: ['Components', 'Input'] }, { label: 'Select', href: '/components/Select', group: ['Components', 'Input'] }, { label: 'Checkbox', href: '/components/Checkbox', group: ['Components', 'Input'] }, { label: 'Card', href: '/components/Card', group: ['Components', 'Display'] }, { label: 'Badge', href: '/components/Badge', group: ['Components', 'Display'] }, { label: 'Tabs', href: '/components/Tabs', group: ['Components', 'Navigation'] }, ]; const [route, setRoute] = useState('/components/Select'); return ( setRoute(item.href)} showGuides /> ); } ``` ### Counts and order ID: `NavTree.counts` β€’ Tags: navtree, sidebar β€’ Category: navigation β€’ Status: stable β€’ Since: 1.1.0 `groupOrder` curates the sections that matter and leaves the rest alphabetical. `renderEndSection` hangs a count off each branch, and `openDepth={0}` starts everything closed. ```tsx const ROUTES: NavTreeItem[] = [ { label: 'Button', href: '/components/Button', group: 'Input' }, { label: 'Select', href: '/components/Select', group: 'Input' }, { label: 'Checkbox', href: '/components/Checkbox', group: 'Input' }, { label: 'Card', href: '/components/Card', group: 'Display' }, { label: 'Badge', href: '/components/Badge', group: 'Display' }, { label: 'Tabs', href: '/components/Tabs', group: 'Navigation' }, ]; const [route, setRoute] = useState('/components/Card'); return ( setRoute(item.href)} // Curate the order that matters and let the rest sort themselves. groupOrder={['Input', 'Display']} openDepth={0} renderEndSection={node => node.children ? {node.children.length} : null } /> ); } ``` ### Filtering ID: `NavTree.search` β€’ Tags: navtree, sidebar, search, filter β€’ Category: navigation β€’ Status: stable β€’ Since: 1.1.0 `searchable` adds a filter field wired to the tree. Typing hides the rows that do not match, opens the branches above the ones that do, and marks the matched substring β€” past a certain length, three letters beat any amount of nesting. ```tsx const ROUTES: NavTreeItem[] = [ { label: 'Button', href: '/components/Button', group: 'Input' }, { label: 'Checkbox', href: '/components/Checkbox', group: 'Input' }, { label: 'Select', href: '/components/Select', group: 'Input' }, { label: 'TextArea', href: '/components/TextArea', group: 'Input' }, { label: 'Badge', href: '/components/Badge', group: 'Display' }, { label: 'Card', href: '/components/Card', group: 'Display' }, { label: 'Breadcrumbs', href: '/components/Breadcrumbs', group: 'Navigation' }, { label: 'Tabs', href: '/components/Tabs', group: 'Navigation' }, ]; const [route, setRoute] = useState('/components/Card'); return ( setRoute(item.href)} searchable searchPlaceholder="Filter components…" /> ); } ``` ### Collapsed rail ID: `NavTree.collapsed` β€’ Tags: navtree, sidebar, rail β€’ Category: navigation β€’ Status: stable β€’ Since: 1.1.0 `collapsed` drops the sidebar to a strip of top-level icons β€” the group holding the current route stays marked, and pressing one lands on the first page inside it. A sidebar with a hundred routes shows a handful of icons here, not a hundred. ```tsx const ROUTES: NavTreeItem[] = [ { label: 'Button', href: '/components/Button', group: 'Components' }, { label: 'Card', href: '/components/Card', group: 'Components' }, { label: 'LineChart', href: '/components/LineChart', group: 'Charts' }, { label: 'BarChart', href: '/components/BarChart', group: 'Charts' }, { label: 'useHover', href: '/hooks/useHover', group: 'Hooks' }, ]; const GROUP_ICONS = { Components: , Charts: , Hooks: , }; const [collapsed, setCollapsed] = useState(true); const [route, setRoute] = useState('/components/Card'); return ( setRoute(item.href)} groupIcons={GROUP_ICONS} collapsed={collapsed} /> ); } ``` -------------------------------------------------------------------------------- # NumberInput The `NumberInput` component is a numeric text input field that provides built-in step controls for incrementing and decrementing the value. It supports custom formatting and parsing functions, allowing you to display numbers in various formats (e.g., currency, percentages) while maintaining a numeric value internally. ## Metadata - Canonical name: `NumberInput` - Package: `@platform-blocks/react-ui-library` - Import: `import { NumberInput } from '@platform-blocks/react-ui-library';` - Category: input - Tags: input, numeric, stepper, formatter - Docs: https://react-ui-library.com/components/NumberInput - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/NumberInput ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `value` | number | No | | Number value | | `onChange` | (value: number \| undefined) => void | No | | Change handler | | `allowDecimal` | boolean | No | | Allow decimal values | | `allowNegative` | boolean | No | | Allow negative values | | `allowLeadingZeros` | boolean | No | | Allow leading zeros while editing | | `allowedDecimalSeparators` | string[] | No | | Additional characters that should be treated as decimal separators | | `decimalSeparator` | string | No | | Decimal separator character | | `decimalScale` | number | No | | Maximum number of digits after the decimal point | | `fixedDecimalScale` | boolean | No | | When true, pads the decimal part with trailing zeros to match decimalScale | | `min` | number | No | | Minimum value | | `max` | number | No | | Maximum value | | `step` | number | No | | Step increment | | `shiftMultiplier` | number | No | | Multiplier applied to the step when using modifier keys | | `precision` | number | No | | Number of decimal places | | `thousandSeparator` | string \| boolean | No | | Thousand separator character or boolean to enable default separator | | `thousandsGroupStyle` | 'none' \| 'thousand' \| 'lakh' \| 'wan' | No | | Thousand grouping strategy | | `prefix` | string | No | | Prefix string appended before the value when displayed | | `suffix` | string | No | | Suffix string appended after the value when displayed | | `format` | 'integer' \| 'decimal' \| 'currency' \| 'percentage' | No | | Number format | | `currency` | string | No | | Currency code for currency format | | `isAllowed` | (values: { floatValue?: number; formattedValue: string; value: string }) => boolean | No | | Optional guard executed before value is committed | | `startValue` | number | No | | Value applied when stepping from an empty state | | `stepHoldDelay` | number | No | | Delay before step-hold behaviour kicks in (ms) | | `stepHoldInterval` | number \| ((stepCount: number) => number) | No | | Interval or function controlling step-hold cadence | | `withKeyboardEvents` | boolean | No | | Enable keyboard arrow interactions | | `withControls` | boolean | No | | Show increment/decrement buttons | | `withSideButtons` | boolean | No | | Render horizontal decrement/increment buttons flanking the input | | `hideControlsOnMobile` | boolean | No | | Whether to hide step controls on mobile | | `withDragGesture` | boolean | No | | Enable press-drag gesture to adjust value | | `dragAxis` | 'horizontal' \| 'vertical' | No | | Axis that determines how drag gestures adjust the value | | `dragStepDistance` | number | No | | Pixel distance required to trigger a single step while dragging | | `dragStepMultiplier` | number | No | | Multiplier applied to the configured step while dragging | | `onDragStateChange` | (isDragging: boolean) => void | No | | Callback fired when the drag gesture activation state changes | | `formatter` | (value: number) => string | No | | Custom formatter function | | `parser` | (value: string) => number | No | | Custom parser function | | `clampBehavior` | 'strict' \| 'blur' \| 'none' | No | | Clamp value to min/max bounds | | `allowEmpty` | boolean | No | | Allow empty value | | `textInputProps` | ExtendedTextInputProps | No | | Additional TextInput props | | `autoCapitalize` | RNTextInputProps['autoCapitalize'] | No | | Text auto-capitalization behavior | | `autoCorrect` | boolean | No | | Whether to enable auto-correct | | `autoFocus` | boolean | No | | Whether to auto-focus on mount | | `returnKeyType` | RNTextInputProps['returnKeyType'] | No | | Return key type for soft keyboard | | `blurOnSubmit` | boolean | No | | Whether to blur on submit | | `selectTextOnFocus` | boolean | No | | Select all text on focus | | `textContentType` | RNTextInputProps['textContentType'] | No | | iOS text content type for autofill | | `textAlign` | RNTextInputProps['textAlign'] | No | | Text alignment | | `spellCheck` | boolean | No | | Whether spell check is enabled | | `inputMode` | RNTextInputProps['inputMode'] | No | | Input mode (modern alternative to keyboardType) | | `enterKeyHint` | RNTextInputProps['enterKeyHint'] | No | | Enter key hint | | `selectionColor` | string | No | | Color of the text selection handles and highlight | | `showSoftInputOnFocus` | boolean | No | | Whether to show the soft keyboard on focus | | `editable` | boolean | No | | Whether the field is editable | | `variant` | InputVariant | No | | Visual variant of the input. `default` (light surface + border), `filled` (gray fill, no border), `outline` (transparent fill, border only), `unstyled` (no border, no fill). | | `label` | React.ReactNode | No | | Input label (string or component) | | `disabled` | boolean | No | | Whether input is disabled | | `required` | boolean | No | | Whether input is required | | `placeholder` | string | No | | Input placeholder | | `error` | string | No | | Error message | | `helperText` | string | No | | Helper text | | `description` | string | No | | Optional short description displayed directly under the label (above the field) | | `size` | SizeValue | No | | Input size | | `withAsterisk` | boolean | No | | Whether to show required indicator | | `name` | string | No | | Input name for form integration | | `startSection` | React.ReactNode | No | | Left section content | | `endSection` | React.ReactNode | No | | Right section content | | `style` | any | No | | Additional styling | | `accessibilityLabel` | string | No | | Accessibility label | | `accessibilityHint` | string | No | | Accessibility hint | | `testID` | string | No | | Test ID for testing | | `debounceMs` | number | No | | Debounce delay for validation in milliseconds | | `onFocus` | () => void | No | | Focus handler | | `onBlur` | () => void | No | | Blur handler | | `onEnter` | () => void | No | | Enter key press handler | | `clearable` | boolean | No | | Show built-in clear button when input has value | | `clearButtonLabel` | string | No | | Accessible label for the clear button | | `onClear` | () => void | No | | Callback when the clear button is pressed | | `keyboardFocusId` | string | No | | Identifier used with KeyboardManagerProvider to request refocus | | `labelProps` | Omit | No | | Override props applied to the field label `` (style, weight, ff, etc.) | | `descriptionProps` | Omit | No | | Override props applied to the field description `` | | `placeholderTextColor` | string | No | | Color of the placeholder text. Falls back to `theme.text.muted`. | | `startSectionProps` | Omit | No | | Props applied to the wrapping `` around `startSection` (style, accessibility, etc.). | | `endSectionProps` | Omit | No | | Props applied to the wrapping `` around `endSection`. | | `m` | number | No | | Margin applied to all sides | | `mt` | number | No | | Margin applied to the top side | | `mr` | number | No | | Margin applied to the right side | | `mb` | number | No | | Margin applied to the bottom side | | `ml` | number | No | | Margin applied to the left side | | `mx` | number | No | | Horizontal margin applied to left and right sides | | `my` | number | No | | Vertical margin applied to top and bottom sides | | `p` | number | No | | Padding applied to all sides | | `pt` | number | No | | Padding applied to the top side | | `pr` | number | No | | Padding applied to the right side | | `pb` | number | No | | Padding applied to the bottom side | | `pl` | number | No | | Padding applied to the left side | | `px` | number | No | | Horizontal padding applied to left and right sides | | `py` | number | No | | Vertical padding applied to top and bottom sides | | `fullWidth` | boolean | No | | Makes the component fill the full width of its parent | | `w` | DimensionValue | No | | Sets a specific width | | `h` | DimensionValue | No | | Sets a specific height | | `maxW` | DimensionValue | No | | Sets the maximum width | | `minW` | DimensionValue | No | | Sets the minimum width | | `maxH` | DimensionValue | No | | Sets the maximum height | | `minH` | DimensionValue | No | | Sets the minimum height | | `radius` | RadiusValue | No | | Border radius value - supports size tokens, numbers, and special values | ## Examples ### Basic ID: `NumberInput.basic` β€’ Tags: basic, numeric, step β€’ Category: usage β€’ Status: stable β€’ Since: 1.0.0 Controlled number input with simple step controls and live value preview. ```tsx const [quantity, setQuantity] = useState(2); return ( ); } ``` ### Formats ID: `NumberInput.formats` β€’ Tags: currency, percent, formatting β€’ Category: usage β€’ Status: stable β€’ Since: 1.0.0 Showcases currency formatting, percentage suffixes, and a derived total. ```tsx const [price, setPrice] = useState(249.99); const [discount, setDiscount] = useState(10); const finalPrice = price != null && discount != null ? price * (1 - discount / 100) : undefined; return ( Final price: {finalPrice != null ? `$${finalPrice.toFixed(2)}` : 'β€”'} ); } ``` ### Side buttons ID: `NumberInput.side-buttons` β€’ Tags: controls, step, buttons β€’ Category: interaction β€’ Status: stable β€’ Since: 1.0.0 Side button controls with shift multipliers for both fine and coarse adjustments. ```tsx const EnhancedNumberInput = NumberInput as any; const [value, setValue] = useState(32); const [step, setStep] = useState(1); const effectiveStep = useMemo(() => step || 1, [step]); return ( Side buttons and shift multiplier Combine side buttons with the default controls to support coarse and fine adjustments. { if (typeof next === 'number') { setValue(next); } }} /> Current speed: {value}% Shift-click = Β±{effectiveStep * 10} Adjust the base step Update the increment to see how the multiplier scales. { if (typeof next === 'number') { setStep(next); } }} /> ); } ``` ### Drag gesture ID: `NumberInput.drag-gesture` β€’ Tags: drag, gesture, adjustment β€’ Category: interaction β€’ Status: stable β€’ Since: 1.0.0 Press-and-drag interactions for horizontal and vertical number adjustments. ```tsx const [horizontalValue, setHorizontalValue] = useState(32); const [verticalValue, setVerticalValue] = useState(120); const [dragging, setDragging] = useState(false); const handleDragStateChange = (state: boolean) => { setDragging(state); }; return ( Press-and-drag adjustment Drag across the input to nudge values without lifting your pointer. The status below reflects the current drag state. Dragging: {dragging ? 'active' : 'idle'} Horizontal drag Step every 14px drag movement with a multiplier for faster adjustments. Vertical drag Drag up or down to adjust between 0 and 200 with built-in controls. ); } ``` -------------------------------------------------------------------------------- # Overlay The Overlay component provides a utility for dimming background content or drawing focus to foreground elements. It supports theme-aware colors, configurable opacity, gradients, and blur to achieve anything from subtle scrims to dramatic glassmorphism. Because Overlay is non-interactive by default, pair it with focus traps or dismiss controls when building dialogs, sheets, or other blocking surfaces. ## Metadata - Canonical name: `Overlay` - Package: `@platform-blocks/react-ui-library` - Import: `import { Overlay } from '@platform-blocks/react-ui-library';` - Status: beta - Category: overlay - Docs: https://react-ui-library.com/components/Overlay - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Overlay ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `color` | string | No | | Background color for the overlay. Accepts raw colors or theme tokens like `primary.6`. | | `opacity` | number | No | | Opacity applied to the background color. Defaults to 0.6. | | `backgroundOpacity` | number | No | | Opacity applied to the entire overlay, including gradients and blur effects. Defaults to 1. | | `gradient` | string | No | | Web-only CSS gradient string. Falls back to `color` on native platforms. | | `blur` | number \| string | No | | Amount of backdrop blur (in pixels). Supported on web. | | `radius` | SizeValue \| number | No | | Corner radius for the overlay surface. | | `zIndex` | number | No | | z-index applied to the overlay container. | | `fixed` | boolean | No | false | Use viewport-fixed positioning instead of absolute positioning (web only). | | `center` | boolean | No | false | Center children horizontally and vertically. | | `style` | StyleProp | No | | Optional style overrides applied after computed styles. | | `children` | ReactNode | No | | Overlay content rendered on top of the dimmed background. | ## Examples ### Overlay patterns ID: `Overlay.basic` β€’ Tags: overlays, effects β€’ Category: surfaces β€’ Status: stable β€’ Since: 1.0.0 Showcases dimming, gradient, and blurred overlays that inherit their parent size for spotlights and modal scrims. ```tsx const HERO_IMAGE = require('../../../../assets/images/scene-city.png'); const GRADIENT_IMAGE = require('../../../../assets/images/scene-aurora.png'); const BLUR_IMAGE = require('../../../../assets/images/scene-desert.png'); type OverlayExample = { key: string; image: ImageSourcePropType; title: string; description: string; align?: 'flex-start' | 'center'; overlayProps: Omit, 'children'>; }; const STATIC_EXAMPLES: OverlayExample[] = [ { key: 'gradient', image: GRADIENT_IMAGE, title: 'Gradient spotlight', description: 'When `gradient` is provided, the overlay renders a vivid fade instead of a solid tint.', overlayProps: { gradient: 'linear-gradient(145deg, rgba(0, 0, 0, 0.95) 0%, rgba(0, 0, 0, 0) 75%)', radius: 'xl', }, }, { key: 'blurred', image: BLUR_IMAGE, title: 'Glass overlay', description: 'Blend blur with partial opacity to achieve a glassmorphism effect (blur is web-only).', align: 'center', overlayProps: { color: '#000', backgroundOpacity: 0.35, blur: 18, radius: 'xl', center: true, }, }, ]; const [visible, setVisible] = useState(true); return ( {visible ? : null} Toggle overlay Overlay fills its parent. Use `backgroundOpacity` to dim the background without affecting children. {STATIC_EXAMPLES.map(({ key, image, overlayProps, align = 'flex-start', title, description }) => ( {title} {description} ))} Overlay inherits the size of its container, making it ideal for dimming media, spotlights, and modal scrims. ); } const styles = StyleSheet.create({ wrapper: { width: '100%', }, section: { width: '100%', maxWidth: 520, alignSelf: 'center', }, image: { width: '100%', aspectRatio: 16 / 9, borderRadius: 24, overflow: 'hidden', justifyContent: 'flex-end', }, imageInner: { borderRadius: 24, }, overlayContent: { padding: 24, }, }); ``` -------------------------------------------------------------------------------- # Pagination A comprehensive pagination component that provides intuitive navigation through large datasets. The component offers flexible configuration options and consistent styling across different use cases. ## Metadata - Canonical name: `Pagination` - Package: `@platform-blocks/react-ui-library` - Import: `import { Pagination } from '@platform-blocks/react-ui-library';` - Status: stable - Since: 1.0.0 - Category: navigation - Tags: pagination, navigation, pages, data - Docs: https://react-ui-library.com/components/Pagination - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Pagination ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `current` | number | Yes | | Current page number (1-indexed) | | `total` | number | Yes | | Total number of pages | | `siblings` | number | No | 1 | Number of page items to show on each side of current page | | `boundaries` | number | No | 1 | Number of page items to show at the boundaries | | `onChange` | (page: number) => void | Yes | | Page change handler | | `size` | ComponentSizeValue | No | 'md' | Size of pagination controls | | `variant` | 'default' \| 'outline' \| 'subtle' | No | 'default' | Variant style | | `color` | 'primary' \| 'secondary' \| 'gray' | No | 'primary' | Color scheme | | `showFirst` | boolean | No | true | Show first/last page buttons | | `showPrevNext` | boolean | No | true | Show previous/next buttons | | `labels` | { first?: ReactNode; previous?: ReactNode; next?: ReactNode; last?: ReactNode; } | No | {} | Custom labels for navigation buttons | | `disabled` | boolean | No | false | Whether pagination is disabled | | `style` | StyleProp | No | | Custom styles | | `buttonStyle` | StyleProp | No | | Custom button styles | | `activeButtonStyle` | StyleProp | No | | Custom active button styles | | `textStyle` | StyleProp | No | | Custom text styles | | `activeTextStyle` | StyleProp | No | | Custom active text styles | | `hideOnSinglePage` | boolean | No | false | Hide pagination when there's only one page | | `showSizeChanger` | boolean | No | false | Show page size selector | | `pageSizeOptions` | number[] | No | [10, 20, 50, 100] | Available page sizes | | `pageSize` | number | No | 10 | Current page size | | `onPageSizeChange` | (size: number) => void | No | | Page size change handler | | `showTotal` | boolean \| ((total: number, range: [number, number]) => ReactNode) | No | false | Show total count | | `totalItems` | number | No | | Total number of items | | `labelProps` | Omit | No | | Override props applied to every page-button label `` (style, weight, ff, size, color). | | `activeLabelProps` | Omit | No | | Override props applied to the active page-button label `` (merged on top of `labelProps`). | | `m` | number | No | | Margin applied to all sides | | `mt` | number | No | | Margin applied to the top side | | `mr` | number | No | | Margin applied to the right side | | `mb` | number | No | | Margin applied to the bottom side | | `ml` | number | No | | Margin applied to the left side | | `mx` | number | No | | Horizontal margin applied to left and right sides | | `my` | number | No | | Vertical margin applied to top and bottom sides | | `p` | number | No | | Padding applied to all sides | | `pt` | number | No | | Padding applied to the top side | | `pr` | number | No | | Padding applied to the right side | | `pb` | number | No | | Padding applied to the bottom side | | `pl` | number | No | | Padding applied to the left side | | `px` | number | No | | Horizontal padding applied to left and right sides | | `py` | number | No | | Vertical padding applied to top and bottom sides | ## Examples ### Basic ID: `Pagination.basic` β€’ Tags: basic, pagination, navigation β€’ Category: usage β€’ Status: stable β€’ Since: 1.0.0 Provide `current`, `total`, and an `onChange` handler to keep numbered pagination in sync with surrounding state. ```tsx const [currentPage, setCurrentPage] = useState(1); const totalPages = 10; return ( Page {currentPage} of {totalPages} ); } ``` ### Variants ID: `Pagination.variants` β€’ Tags: variants, style, appearance β€’ Category: styling β€’ Status: stable β€’ Since: 1.0.0 Switch the `variant` prop between `default`, `outline`, and `subtle` to align pagination with the surrounding surface treatment. ```tsx const [defaultPage, setDefaultPage] = useState(5); const [outlinePage, setOutlinePage] = useState(5); const [subtlePage, setSubtlePage] = useState(5); return ( Default variant keeps the control fully filled. Page {defaultPage} of 15. Outline keeps the surface quiet while the active page gets a stroke. Page {outlinePage} of 15. Subtle removes backgrounds for tinted surfaces. Page {subtlePage} of 15. ); } ``` ### Sizes ID: `Pagination.sizes` β€’ Tags: sizes, scale, responsive β€’ Category: styling β€’ Status: stable β€’ Since: 1.0.0 Use the `size` prop (`xs` through `3xl`) to match pagination density to its container without changing behavior. ```tsx const SIZES = ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl'] as const; const [page, setPage] = useState(3); return ( {SIZES.map((size) => ( {size} ))} ); } ``` ### Advanced ID: `Pagination.advanced` β€’ Tags: advanced, controls, boundaries, siblings β€’ Category: usage β€’ Status: stable β€’ Since: 1.0.0 Combine `showFirst`, `showPrevNext`, `siblings`, and `boundaries` to reveal the right amount of context for long result sets. ```tsx const [page1, setPage1] = useState(10); const [page2, setPage2] = useState(15); const [page3, setPage3] = useState(25); return ( Includes first and last buttons. Page {page1} of 30. Minimal navigation with prev/next only. Page {page2} of 40. Compact layout with tight siblings. Page {page3} of 50. ); } ``` ### Total & size changer ID: `Pagination.size-changer` β€’ Tags: pagination, page-size, total β€’ Category: usage β€’ Status: stable β€’ Since: 1.0.0 Set `showTotal` with `totalItems` to render an "X-Y of N" summary, and `showSizeChanger` with `pageSizeOptions` / `onPageSizeChange` to let users change the rows-per-page. This is the same footer the `DataTable` renders internally. ```tsx const totalItems = 248; const [pageSize, setPageSize] = useState(10); const [current, setCurrent] = useState(1); const total = Math.max(1, Math.ceil(totalItems / pageSize)); return ( { setPageSize(size); setCurrent(1); }} /> Page {current} of {total} Β· {pageSize} rows per page ); } ``` -------------------------------------------------------------------------------- # PhoneInput The `PhoneInput` component provides a flexible way to capture telephone numbers with built-in masking and formatting. ## Metadata - Canonical name: `PhoneInput` - Package: `@platform-blocks/react-ui-library` - Import: `import { PhoneInput } from '@platform-blocks/react-ui-library';` - Category: input - Tags: phone, input, mask, formatting, international - Docs: https://react-ui-library.com/components/PhoneInput - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/PhoneInput ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `value` | string | No | | Phone number value (digits only). Omit for an uncontrolled field. | | `defaultValue` | string | No | | Initial value while uncontrolled. | | `onChange` | (raw: string, formatted: string, meta: PhoneChangeMeta) => void | No | | Change handler receiving (nationalDigits, formattedDisplay, meta) | | `country` | PhoneCountryCode | No | | Country preset to format against. Controlled when provided. | | `defaultCountry` | PhoneCountryCode | No | 'US' | Initial country while uncontrolled. Defaults to 'US'. | | `onCountryChange` | (country: PhoneCountryCode) => void | No | | Called when the country changes (via the picker, or `autoDetect`). | | `selectableCountry` | boolean | No | false | Render the dial code as a dropdown so the user can change country. | | `autoDetect` | boolean | No | false | Switch country when the user types or pastes an explicit `+` prefix. Off by default: it changes the mask out from under the caller's `country` prop. The active country's own dial code is stripped either way, so pasting a full local number never truncates it. A *foreign* dial code is only stripped when `autoDetect` lets us switch to that country β€” otherwise the digits would be re-filed under the active country, turning `+447911123456` into `+17911123456`. | | `showCountryCode` | boolean | No | true | Show the dial code prefix ahead of the field | | `mask` | string | No | | Custom mask pattern (overrides the country mask). Use '0' for digits, any other character as a literal. Avoid literal digits β€” see `PhoneFormat.mask`. | | `textInputProps` | ExtendedTextInputProps | No | | Additional props forwarded to the underlying TextInput. | | `variant` | InputVariant | No | | Visual variant of the input. `default` (light surface + border), `filled` (gray fill, no border), `outline` (transparent fill, border only), `unstyled` (no border, no fill). | | `label` | React.ReactNode | No | | Input label (string or component) | | `disabled` | boolean | No | | Whether input is disabled | | `required` | boolean | No | | Whether input is required | | `placeholder` | string | No | | Input placeholder | | `error` | string | No | | Error message | | `helperText` | string | No | | Helper text | | `description` | string | No | | Optional short description displayed directly under the label (above the field) | | `size` | SizeValue | No | 'md' | Input size | | `withAsterisk` | boolean | No | | Whether to show required indicator | | `name` | string | No | | Input name for form integration | | `startSection` | React.ReactNode | No | | Left section content | | `endSection` | React.ReactNode | No | | Right section content | | `style` | any | No | | Additional styling | | `accessibilityLabel` | string | No | | Accessibility label | | `accessibilityHint` | string | No | | Accessibility hint | | `testID` | string | No | | Test ID for testing | | `debounceMs` | number | No | | Debounce delay for validation in milliseconds | | `onFocus` | () => void | No | | Focus handler | | `onBlur` | () => void | No | | Blur handler | | `onEnter` | () => void | No | | Enter key press handler | | `clearable` | boolean | No | | Show built-in clear button when input has value | | `clearButtonLabel` | string | No | | Accessible label for the clear button | | `onClear` | () => void | No | | Callback when the clear button is pressed | | `keyboardFocusId` | string | No | | Identifier used with KeyboardManagerProvider to request refocus | | `labelProps` | Omit | No | | Override props applied to the field label `` (style, weight, ff, etc.) | | `descriptionProps` | Omit | No | | Override props applied to the field description `` | | `placeholderTextColor` | string | No | | Color of the placeholder text. Falls back to `theme.text.muted`. | | `startSectionProps` | Omit | No | | Props applied to the wrapping `` around `startSection` (style, accessibility, etc.). | | `endSectionProps` | Omit | No | | Props applied to the wrapping `` around `endSection`. | | `m` | number | No | | Margin applied to all sides | | `mt` | number | No | | Margin applied to the top side | | `mr` | number | No | | Margin applied to the right side | | `mb` | number | No | | Margin applied to the bottom side | | `ml` | number | No | | Margin applied to the left side | | `mx` | number | No | | Horizontal margin applied to left and right sides | | `my` | number | No | | Vertical margin applied to top and bottom sides | | `p` | number | No | | Padding applied to all sides | | `pt` | number | No | | Padding applied to the top side | | `pr` | number | No | | Padding applied to the right side | | `pb` | number | No | | Padding applied to the bottom side | | `pl` | number | No | | Padding applied to the left side | | `px` | number | No | | Horizontal padding applied to left and right sides | | `py` | number | No | | Vertical padding applied to top and bottom sides | | `fullWidth` | boolean | No | | Makes the component fill the full width of its parent | | `w` | DimensionValue | No | | Sets a specific width | | `h` | DimensionValue | No | | Sets a specific height | | `maxW` | DimensionValue | No | | Sets the maximum width | | `minW` | DimensionValue | No | | Sets the minimum width | | `maxH` | DimensionValue | No | | Sets the maximum height | | `minH` | DimensionValue | No | | Sets the minimum height | | `radius` | RadiusValue | No | | Border radius value - supports size tokens, numbers, and special values | ## Examples ### Basic ID: `PhoneInput.basic` β€’ Tags: basic, phone, input β€’ Category: basics β€’ Status: experimental β€’ Since: 1.0.0 Controlled PhoneInput example that surfaces both raw digits and the formatted display. ```tsx const [raw, setRaw] = useState(''); const [formatted, setFormatted] = useState(''); const [e164, setE164] = useState(''); const [complete, setComplete] = useState(false); return ( Basic phone input Controlled phone field showing the raw national digits, the formatted display value, and the submittable E.164 form. { setRaw(rawDigits); setFormatted(formattedDisplay); setE164(meta.e164); setComplete(meta.isComplete); }} country="US" showCountryCode /> Current values {JSON.stringify({ raw, formatted, e164, complete }, null, 2)} ); } ``` ### International ID: `PhoneInput.international` β€’ Tags: international, auto-detect, phone β€’ Category: features β€’ Status: experimental β€’ Since: 1.0.0 Compare auto-detected formats with a manual international configuration. ```tsx const [autoDetectValue, setAutoDetectValue] = useState(''); const [autoDetectE164, setAutoDetectE164] = useState(''); const [autoDetectCountry, setAutoDetectCountry] = useState('US'); const [intlValue, setIntlValue] = useState(''); const [intlFormatted, setIntlFormatted] = useState(''); return ( International detection With autoDetect, an explicit + prefix picks the country: type or paste +447911123456 and the mask, dial code and E.164 output follow along. A recognized dial code is stripped on paste either way, so a full international number never overflows the national mask. { setAutoDetectValue(raw); setAutoDetectE164(meta.e164); }} defaultCountry="US" onCountryChange={setAutoDetectCountry} autoDetect showCountryCode placeholder="Try +447911123456 or +33123456789" /> { setIntlValue(raw); setIntlFormatted(formatted); }} showCountryCode={false} placeholder="Enter any international number" /> Values {JSON.stringify( { autoDetect: { country: autoDetectCountry, raw: autoDetectValue, e164: autoDetectE164 }, international: { raw: intlValue, formatted: intlFormatted } }, null, 2 )} ); } ``` ### Country Picker ID: `PhoneInput.country-select` β€’ Tags: country, picker, dial-code, phone β€’ Category: features β€’ Status: experimental β€’ Since: 1.0.0 Let the user pick the country from the dial-code prefix, remasking the number in place. ```tsx const [country, setCountry] = useState('US'); const [raw, setRaw] = useState(''); const [e164, setE164] = useState(''); return ( Country picker With selectableCountry the dial-code prefix becomes a dropdown. Changing the country remasks the digits already entered instead of clearing them, and the E.164 value is rebuilt against the new dial code. { setRaw(rawDigits); setE164(meta.e164); }} /> Current values {JSON.stringify({ country, raw, e164 }, null, 2)} ); } ``` ### Country Formats ID: `PhoneInput.formats` β€’ Tags: formatting, country, phone β€’ Category: features β€’ Status: experimental β€’ Since: 1.0.0 Showcase of built-in country presets with their localized masks and raw digit output. ```tsx const [us, setUs] = useState(''); const [uk, setUk] = useState(''); const [fr, setFr] = useState(''); const [br, setBr] = useState(''); return ( Country formatting Compare built-in masks for several countries. Each input stores digits only while rendering a localized format. setUs(raw)} showCountryCode /> setUk(raw)} showCountryCode /> setFr(raw)} showCountryCode /> setBr(raw)} showCountryCode /> Raw digit values {JSON.stringify({ us, uk, fr, br }, null, 2)} ); } ``` ### Mask Visibility ID: `PhoneInput.mask-visibility` β€’ Tags: country-code, placeholder β€’ Category: features β€’ Status: experimental β€’ Since: 1.0.0 Demonstrates showing or hiding the country code prefix while preserving raw digits. ```tsx const [withCountryCode, setWithCountryCode] = useState(''); const [withoutCountryCode, setWithoutCountryCode] = useState(''); return ( Country code visibility Toggle the country prefix while keeping the same underlying digits. setWithCountryCode(raw)} country="US" showCountryCode /> Raw digits: {withCountryCode || 'β€”'} setWithoutCountryCode(raw)} country="US" showCountryCode={false} /> Raw digits: {withoutCountryCode || 'β€”'} ); } ``` ### Validation ID: `PhoneInput.validation` β€’ Tags: validation, feedback, phone β€’ Category: features β€’ Status: experimental β€’ Since: 1.0.0 Length-based validation for US and international formats with inline messaging. ```tsx const [usRaw, setUsRaw] = useState(''); const [usFormatted, setUsFormatted] = useState(''); const [internationalRaw, setInternationalRaw] = useState(''); const [internationalFormatted, setInternationalFormatted] = useState(''); const isValidUs = useMemo(() => usRaw.length === 10, [usRaw]); const isValidInternational = useMemo( () => internationalRaw.length >= 7 && internationalRaw.length <= 15, [internationalRaw] ); return ( Validation states Surface validation messages based on raw digit counts for domestic and international numbers. { setUsRaw(raw); setUsFormatted(formatted); }} country="US" showCountryCode error={usRaw.length > 0 && !isValidUs ? 'Enter a 10-digit US phone number' : undefined} /> {usRaw.length === 0 ? 'Enter a phone number' : isValidUs ? `βœ“ ${usFormatted}` : `${usRaw.length}/10 digits entered`} { setInternationalRaw(raw); setInternationalFormatted(formatted); }} defaultCountry="INTL" autoDetect showCountryCode error={ internationalRaw.length > 0 && !isValidInternational ? 'International numbers should be 7-15 digits' : undefined } /> {internationalRaw.length === 0 ? 'Enter an international phone number' : isValidInternational ? `βœ“ ${internationalFormatted}` : 'Adjust to 7-15 digits'} ); } ``` ### Advanced Masking ID: `PhoneInput.advanced-masking` β€’ Tags: mask, formatting, advanced β€’ Category: features β€’ Status: experimental β€’ Since: 1.0.0 Custom mask patterns for international formats and extension fields. ```tsx const [intlRaw, setIntlRaw] = useState(''); const [intlFormatted, setIntlFormatted] = useState(''); const [extensionRaw, setExtensionRaw] = useState(''); const [extensionFormatted, setExtensionFormatted] = useState(''); return ( Advanced masking Apply custom mask patterns to control formatting for international numbers and extension fields. { setIntlRaw(raw); setIntlFormatted(formatted); }} autoDetect={false} showCountryCode={false} mask="+00 (000) 000-0000" placeholder="+44 (7911) 123-456" /> Raw digits: {intlRaw || 'β€”'} Formatted: {intlFormatted || 'β€”'} { setExtensionRaw(raw); setExtensionFormatted(formatted); }} autoDetect={false} showCountryCode={false} mask="000-000-0000 x0000" placeholder="555-123-4567 x1234" /> Raw digits: {extensionRaw || 'β€”'} Formatted: {extensionFormatted || 'β€”'} ); } ``` -------------------------------------------------------------------------------- # PinInput A specialized input component designed for entering PIN codes, one-time passwords (OTP), verification codes, and other sequential character inputs. The component provides an intuitive interface with automatic focus management. ## Metadata - Canonical name: `PinInput` - Package: `@platform-blocks/react-ui-library` - Import: `import { PinInput } from '@platform-blocks/react-ui-library';` - Status: stable - Since: 1.0.0 - Category: input - Tags: pin, otp, security, input, verification - Docs: https://react-ui-library.com/components/PinInput - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/PinInput ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `length` | number | No | 4 | Number of PIN digits | | `keyboardFocusId` | string | No | | Stable id used by KeyboardManager to restore focus | | `value` | string | No | | PIN value (controlled) | | `defaultValue` | string | No | '' | Uncontrolled initial value (used when `value` is not provided) | | `onChange` | (pin: string) => void | No | | Change handler | | `mask` | boolean | No | false | Whether to mask PIN | | `maskChar` | string | No | 'β€’' | Character to use for masking | | `manageFocus` | boolean | No | true | Whether to focus next input automatically | | `enforceOrderInitialOnly` | boolean | No | | Enforce sequential entry (forces focus to first empty). If false, user can edit any position after complete | | `type` | 'alphanumeric' \| 'numeric' | No | 'numeric' | Type of input | | `placeholder` | string | No | '' | Placeholder for each input | | `allowPaste` | boolean | No | true | Whether to allow paste | | `oneTimeCode` | boolean | No | false | One-time code auto-complete | | `spacing` | number | No | 8 | Input spacing | | `borderRadius` | number | No | | Input border radius | | `onComplete` | (pin: string) => void | No | | Complete handler - called when all digits are filled | | `textInputProps` | Omit | No | | Additional TextInput props for each input | | `autoCapitalize` | RNTextInputProps['autoCapitalize'] | No | | Text auto-capitalization behavior | | `autoCorrect` | boolean | No | | Whether to enable auto-correct | | `autoFocus` | boolean | No | | Whether to auto-focus on first input on mount | | `selectTextOnFocus` | boolean | No | | Select all text on focus | | `textContentType` | RNTextInputProps['textContentType'] | No | | iOS text content type for autofill | | `textAlign` | RNTextInputProps['textAlign'] | No | | Text alignment | | `spellCheck` | boolean | No | | Whether spell check is enabled | | `selectionColor` | string | No | | Color of the text selection handles and highlight | | `showSoftInputOnFocus` | boolean | No | | Whether to show the soft keyboard on focus | | `variant` | InputVariant | No | | Visual variant of the input. `default` (light surface + border), `filled` (gray fill, no border), `outline` (transparent fill, border only), `unstyled` (no border, no fill). | | `label` | React.ReactNode | No | | Input label (string or component) | | `disabled` | boolean | No | false | Whether input is disabled | | `required` | boolean | No | | Whether input is required | | `error` | string | No | | Error message | | `helperText` | string | No | | Helper text | | `description` | string | No | | Optional short description displayed directly under the label (above the field) | | `size` | SizeValue | No | 'md' | Input size | | `withAsterisk` | boolean | No | | Whether to show required indicator | | `name` | string | No | | Input name for form integration | | `startSection` | React.ReactNode | No | | Left section content | | `endSection` | React.ReactNode | No | | Right section content | | `style` | any | No | | Additional styling | | `accessibilityLabel` | string | No | | Accessibility label | | `accessibilityHint` | string | No | | Accessibility hint | | `testID` | string | No | | Test ID for testing | | `debounceMs` | number | No | | Debounce delay for validation in milliseconds | | `onFocus` | () => void | No | | Focus handler | | `onBlur` | () => void | No | | Blur handler | | `onEnter` | () => void | No | | Enter key press handler | | `clearable` | boolean | No | | Show built-in clear button when input has value | | `clearButtonLabel` | string | No | | Accessible label for the clear button | | `onClear` | () => void | No | | Callback when the clear button is pressed | | `labelProps` | Omit | No | | Override props applied to the field label `` (style, weight, ff, etc.) | | `descriptionProps` | Omit | No | | Override props applied to the field description `` | | `placeholderTextColor` | string | No | | Color of the placeholder text. Falls back to `theme.text.muted`. | | `startSectionProps` | Omit | No | | Props applied to the wrapping `` around `startSection` (style, accessibility, etc.). | | `endSectionProps` | Omit | No | | Props applied to the wrapping `` around `endSection`. | | `m` | number | No | | Margin applied to all sides | | `mt` | number | No | | Margin applied to the top side | | `mr` | number | No | | Margin applied to the right side | | `mb` | number | No | | Margin applied to the bottom side | | `ml` | number | No | | Margin applied to the left side | | `mx` | number | No | | Horizontal margin applied to left and right sides | | `my` | number | No | | Vertical margin applied to top and bottom sides | | `p` | number | No | | Padding applied to all sides | | `pt` | number | No | | Padding applied to the top side | | `pr` | number | No | | Padding applied to the right side | | `pb` | number | No | | Padding applied to the bottom side | | `pl` | number | No | | Padding applied to the left side | | `px` | number | No | | Horizontal padding applied to left and right sides | | `py` | number | No | | Vertical padding applied to top and bottom sides | | `fullWidth` | boolean | No | | Makes the component fill the full width of its parent | | `w` | DimensionValue | No | | Sets a specific width | | `h` | DimensionValue | No | | Sets a specific height | | `maxW` | DimensionValue | No | | Sets the maximum width | | `minW` | DimensionValue | No | | Sets the minimum width | | `maxH` | DimensionValue | No | | Sets the maximum height | | `minH` | DimensionValue | No | | Sets the minimum height | | `radius` | RadiusValue | No | | Border radius value - supports size tokens, numbers, and special values | ## Examples ### Basic ID: `PinInput.basic` β€’ Tags: basic, pin, code β€’ Category: usage β€’ Status: stable β€’ Since: 1.0.0 Controlled 4-digit PIN input with automatic focus handoff and live preview. ```tsx const [value, setValue] = useState(''); return ( ); } ``` ### Types ID: `PinInput.types` β€’ Tags: types, numeric, alphanumeric β€’ Category: usage β€’ Status: stable β€’ Since: 1.0.0 Contrast numeric-only PIN entry with an alphanumeric option for recovery codes. ```tsx const [numericValue, setNumericValue] = useState(''); const [alphanumericValue, setAlphanumericValue] = useState(''); return ( PIN input types Numeric (default) Restricts entry to digits 0-9 for PIN and OTP flows. Alphanumeric Allow letters and numbers for recovery or backup codes. ); } ``` ### Sizes ID: `PinInput.sizes` β€’ Tags: sizes, scale, responsive β€’ Category: styling β€’ Status: stable β€’ Since: 1.0.0 Demonstrates xs through lg PIN inputs and when each token fits best. ```tsx type SizeToken = 'xs' | 'sm' | 'md' | 'lg'; const [xsValue, setXsValue] = useState(''); const [smValue, setSmValue] = useState(''); const [mdValue, setMdValue] = useState(''); const [lgValue, setLgValue] = useState(''); const sizeExamples: Array<{ id: SizeToken; label: string; helper: string; size: SizeToken; value: string; setValue: (value: string) => void; }> = [ { id: 'xs', label: 'Extra small (xs)', helper: 'Use for dense layouts or compact verification prompts.', size: 'xs', value: xsValue, setValue: setXsValue, }, { id: 'sm', label: 'Small (sm)', helper: 'Pairs well with mobile forms and inline flows.', size: 'sm', value: smValue, setValue: setSmValue, }, { id: 'md', label: 'Medium (md)', helper: 'Default size for most experiences.', size: 'md', value: mdValue, setValue: setMdValue, }, { id: 'lg', label: 'Large (lg)', helper: 'Highlight critical actions with spacious fields.', size: 'lg', value: lgValue, setValue: setLgValue, }, ]; return ( PIN input sizes {sizeExamples.map((example) => ( {example.label} {example.helper} ))} ); } ``` ### Lengths ID: `PinInput.lengths` β€’ Tags: length, digits, fields β€’ Category: usage β€’ Status: stable β€’ Since: 1.0.0 Compare 4, 6, and 8-digit PIN inputs tailored for common authentication flows. ```tsx const [fourDigit, setFourDigit] = useState(''); const [sixDigit, setSixDigit] = useState(''); const [eightDigit, setEightDigit] = useState(''); const lengthExamples = [ { length: 4, title: '4-digit PIN (default)', helper: 'Common for ATM and device security codes.', label: '4-digit PIN', value: fourDigit, setValue: setFourDigit, }, { length: 6, title: '6-digit verification', helper: 'Typical for SMS-based one-time codes.', label: 'Verification code', value: sixDigit, setValue: setSixDigit, }, { length: 8, title: '8-digit code', helper: 'Use for longer recovery or backup codes.', label: 'Security code', value: eightDigit, setValue: setEightDigit, }, ]; return ( PIN input lengths {lengthExamples.map((example) => ( {example.title} {example.helper} ))} ); } ``` ### Security ID: `PinInput.security` β€’ Tags: security, mask, validation, otp β€’ Category: usage β€’ Status: stable β€’ Since: 1.0.0 Highlights masking, automatic OTP completion, and validation flows with inline messaging. ```tsx const [maskedValue, setMaskedValue] = useState(''); const [otpValue, setOtpValue] = useState(''); const [otpStatus, setOtpStatus] = useState(''); const [validationValue, setValidationValue] = useState(''); const [validationMessage, setValidationMessage] = useState(''); const [error, setError] = useState(''); const [disabled, setDisabled] = useState(false); const correctPin = '1234'; const handleValidate = () => { if (validationValue !== correctPin) { setError('Incorrect PIN. Try again.'); setValidationMessage(''); return; } setError(''); setValidationMessage('PIN verified successfully.'); }; const handleOtpComplete = (value: string) => { setOtpStatus(`OTP entered: ${value}`); }; const handleToggleDisabled = () => { setDisabled((prev) => !prev); setError(''); setValidationMessage(''); }; const handleClear = () => { setValidationValue(''); setError(''); setValidationMessage(''); }; return ( Security-focused PIN inputs Masked PIN input Conceal characters as they are typed. OTP with auto-complete Automatically completes once all digits are entered. { setOtpValue(value); if (otpStatus) setOtpStatus(''); }} onComplete={handleOtpComplete} oneTimeCode length={6} label="One-time password" /> {otpStatus ? ( {otpStatus} ) : null} PIN validation state Enter the correct PIN: 1234 { setValidationValue(newValue); if (error) setError(''); if (validationMessage) setValidationMessage(''); }} label="Enter PIN" error={error} disabled={disabled} helperText={!error ? 'Enter the correct 4-digit PIN' : undefined} /> {validationMessage ? ( {validationMessage} ) : null} ); } ``` -------------------------------------------------------------------------------- # Popover Popover sits on the same overlay primitives as Menu and Tooltip, making it suitable for interactive content like forms, lists, and quick action menus while keeping focus management predictable. ## Metadata - Canonical name: `Popover` - Package: `@platform-blocks/react-ui-library` - Import: `import { Popover } from '@platform-blocks/react-ui-library';` - Status: beta - Category: overlay - Docs: https://react-ui-library.com/components/Popover - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Popover ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `children` | ReactNode | Yes | | | | `opened` | boolean | No | | Controlled open state | | `defaultOpened` | boolean | No | false | Initial open state in uncontrolled mode | | `onChange` | (opened: boolean) => void | No | | Called when open state changes | | `onOpen` | () => void | No | | Called when popover opens | | `onClose` | () => void | No | | Called when popover closes | | `onDismiss` | () => void | No | | Called when popover is dismissed via outside click or escape | | `trigger` | 'click' \| 'hover' | No | 'click' | How the popover is triggered: 'click' (default) or 'hover' (mostly useful for devices with a mouse) | | `disabled` | boolean | No | false | Disable popover entirely | | `closeOnClickOutside` | boolean | No | true | Close when clicking outside | | `closeOnEscape` | boolean | No | true | Close when pressing Escape | | `clickOutsideEvents` | string[] | No | | Events considered for outside click detection (web only) | | `trapFocus` | boolean | No | | Trap focus within dropdown (web only) | | `keepMounted` | boolean | No | false | Keep dropdown mounted when hidden | | `returnFocus` | boolean | No | false | Return focus to target after close | | `withinPortal` | boolean | No | true | Render dropdown within portal | | `withOverlay` | boolean | No | false | Render overlay/backdrop | | `overlayProps` | Record | No | | Overlay component props | | `w` | number \| 'target' | No | | Dropdown width, number or 'target' to match target width | | `maxW` | number | No | | Dropdown max-width | | `maxH` | number | No | | Dropdown max-height | | `minW` | number | No | | Dropdown min-width | | `minH` | number | No | | Dropdown min-height | | `radius` | RadiusValue \| number | No | | Border radius | | `shadow` | ShadowValue | No | | Box shadow | | `zIndex` | number | No | 300 | Dropdown z-index | | `position` | PlacementType | No | 'bottom' | Popover position relative to target | | `offset` | number \| { mainAxis?: number; crossAxis?: number } | No | 8 | Offset from target | | `floatingStrategy` | FloatingStrategy | No | 'fixed' | Floating strategy for positioning | | `middlewares` | PopoverMiddlewares | No | | Custom positioning options | | `preventPositionChangeWhenVisible` | boolean | No | false | Prevent flipping/shifting when visible | | `hideDetached` | boolean | No | true | Hide dropdown when target becomes detached | | `viewport` | PositioningOptions['viewport'] | No | | Override viewport padding | | `keyboardAvoidance` | boolean | No | true | Whether positioning should avoid the on-screen keyboard | | `fallbackPlacements` | PlacementType[] | No | | Override fallback placements | | `boundary` | number | No | | Override boundary padding | | `withRoles` | boolean | No | true | Render ARIA roles | | `id` | string | No | | Unique id base for accessibility | | `withArrow` | boolean | No | false | Render arrow | | `arrowSize` | number | No | DEFAULT_ARROW_SIZE | Arrow size | | `arrowRadius` | number | No | 0 | Arrow border radius | | `arrowOffset` | number | No | 5 | Arrow offset | | `arrowPosition` | ArrowPosition | No | 'center' | Arrow position for start/end placements | | `onPositionChange` | (placement: PlacementType) => void | No | | Called when dropdown position changes | | `testID` | string | No | | Test identifier | | `m` | number | No | | Margin applied to all sides | | `mt` | number | No | | Margin applied to the top side | | `mr` | number | No | | Margin applied to the right side | | `mb` | number | No | | Margin applied to the bottom side | | `ml` | number | No | | Margin applied to the left side | | `mx` | number | No | | Horizontal margin applied to left and right sides | | `my` | number | No | | Vertical margin applied to top and bottom sides | | `p` | number | No | | Padding applied to all sides | | `pt` | number | No | | Padding applied to the top side | | `pr` | number | No | | Padding applied to the right side | | `pb` | number | No | | Padding applied to the bottom side | | `pl` | number | No | | Padding applied to the left side | | `px` | number | No | | Horizontal padding applied to left and right sides | | `py` | number | No | | Vertical padding applied to top and bottom sides | ## Examples ### Basic Usage ID: `Popover.basic` β€’ Tags: popover β€’ Category: usage β€’ Status: stable β€’ Since: 1.0.0 Popover targets wrap an interactive element and render dropdown content within `Popover.Dropdown`. ```tsx return ( Quick actions Popovers expose more content than tooltips without leaving the page. ); } ``` ### Hover Trigger ID: `Popover.hover` β€’ Tags: popover, hover, trigger β€’ Category: usage β€’ Status: stable β€’ Since: 1.0.0 Set `trigger="hover"` to open the popover when the user hovers over the target element. This is useful for mouse users who want quick access to additional content without clicking. ```tsx return ( Hover popover This popover opens on hover, ideal for mouse users who want quick access to additional content. ); } ``` ### Controlled State ID: `Popover.controlled` β€’ Tags: popover, state β€’ Category: behavior β€’ Status: stable β€’ Since: 1.0.0 Control the `opened` prop and respond to `onChange` when the popover needs to sync with surrounding form state. ```tsx const [opened, setOpened] = useState(false); const [email, setEmail] = useState('team@example.com'); return ( Invite team member ); } ``` ### Placement Options ID: `Popover.placements` β€’ Tags: popover, position β€’ Category: layout β€’ Status: stable β€’ Since: 1.0.0 Set the `position` prop to control where the dropdown renders relative to its trigger. ```tsx const OPTIONS = [ { label: 'Top', position: 'top', description: 'Appears above the trigger.' }, { label: 'Right', position: 'right', description: 'Anchors to the right edge.' }, { label: 'Bottom', position: 'bottom', description: 'Drops below the trigger.' }, { label: 'Left', position: 'left', description: 'Anchors to the left edge.' }, ] as const; return ( {OPTIONS.map(({ label, position, description }) => ( {label} placement {description} ))} ); } ``` -------------------------------------------------------------------------------- # Progress The Progress component displays the completion progress of a task or process. Supports different variants, colors, and animations. ## Label and description Progress accepts the same field props as the input components, rendered outside the track: ```tsx ``` `description` is the sublabel beneath the label, `error` replaces it and renders below the bar, `required` adds an asterisk (suppress it with `withAsterisk={false}`), and `labelPosition` accepts `top` (default), `bottom`, `left`, or `right`. `labelGap` tunes the space between the block and the bar, and `labelProps` / `descriptionProps` pass through to the underlying `` elements. `Progress.Root` takes the same props, so a segmented bar can be labelled the same way. Don't confuse this with `Progress.Label`, which renders text *inside* a filled section. ## Compound components For multi-part bars, compose `Progress.Root` with one `Progress.Section` per segment, and optionally a `Progress.Label` inside each section: ```tsx 35% 28% ``` Each section takes its `value` as a percentage of the whole track, so sections may sum to less than 100 and leave the remainder unfilled. Sections support `color`, `striped`, `animate`, `radius`, and `transitionDuration` (inherited from `Progress.Root` when omitted). ## Tooltips Use the section's own `tooltip` prop β€” a string, or a config object for full `Tooltip` props: ```tsx ``` Do not wrap a section in `Tooltip` yourself. `Tooltip` renders a wrapper view, which then becomes the flex item inside `Progress.Root` and sizes itself to its content β€” collapsing the section's percentage width. The `tooltip` prop renders the tooltip *inside* the already-sized section instead. If you do need a manual wrapper, give it the width explicitly: ``. Sections also forward `onPress` and hover/focus handlers, so they can be made interactive directly. ## Vertical orientation Pass `orientation="vertical"` to `Progress` or `Progress.Root` to fill from the bottom up. Vertical bars have no intrinsic length, so they default to 160 β€” set `length` (or the `h` layout prop) to size them, and `size` controls the thickness. ```tsx ``` ## Metadata - Canonical name: `Progress` - Package: `@platform-blocks/react-ui-library` - Import: `import { Progress } from '@platform-blocks/react-ui-library';` - Category: feedback - Tags: progress, loading, status, indicator, completion, segments, vertical - Docs: https://react-ui-library.com/components/Progress - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Progress ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `value` | number | Yes | | 0-100 | | `size` | SizeValue | No | 'md' | | | `color` | ThemeColor | No | 'primary' | | | `radius` | SizeValue | No | 'md' | | | `striped` | boolean | No | false | | | `animate` | boolean | No | false | | | `transitionDuration` | number | No | 0 | ms | | `orientation` | ProgressOrientation | No | 'horizontal' | Axis the bar fills along. Vertical bars fill bottom-up. @default 'horizontal' | | `length` | number \| `${number}%` | No | | Length along the main axis. Vertical bars default to 160. | | `trackColor` | string | No | | Track (unfilled) color. Defaults to the theme's `gray[1]`. | | `style` | StyleProp | No | | Styles applied to the track. Spacing/layout props stay on the outermost element. | | `testID` | string | No | | | | `m` | number | No | | Margin applied to all sides | | `mt` | number | No | | Margin applied to the top side | | `mr` | number | No | | Margin applied to the right side | | `mb` | number | No | | Margin applied to the bottom side | | `ml` | number | No | | Margin applied to the left side | | `mx` | number | No | | Horizontal margin applied to left and right sides | | `my` | number | No | | Vertical margin applied to top and bottom sides | | `p` | number | No | | Padding applied to all sides | | `pt` | number | No | | Padding applied to the top side | | `pr` | number | No | | Padding applied to the right side | | `pb` | number | No | | Padding applied to the bottom side | | `pl` | number | No | | Padding applied to the left side | | `px` | number | No | | Horizontal padding applied to left and right sides | | `py` | number | No | | Vertical padding applied to top and bottom sides | | `fullWidth` | boolean | No | | Makes the component fill the full width of its parent | | `w` | DimensionValue | No | | Sets a specific width | | `h` | DimensionValue | No | | Sets a specific height | | `maxW` | DimensionValue | No | | Sets the maximum width | | `minW` | DimensionValue | No | | Sets the minimum width | | `maxH` | DimensionValue | No | | Sets the maximum height | | `minH` | DimensionValue | No | | Sets the minimum height | | `label` | React.ReactNode | No | | Label rendered outside the track. Strings are styled; nodes render as-is. | | `description` | React.ReactNode | No | | Helper text ("sublabel") rendered directly beneath the label. Hidden while `error` is set. | | `error` | React.ReactNode | No | | Error message rendered below the bar. Replaces `description` when present. | | `required` | boolean | No | false | Marks the field as required, rendering an asterisk beside the label. @default false | | `withAsterisk` | boolean | No | true | Whether the required marker is drawn. @default true | | `labelPosition` | ProgressLabelPosition | No | 'top' | Placement of the label block relative to the bar. @default 'top' | | `labelGap` | SizeValue \| number | No | 'xs' | Gap between the label block and the bar β€” a theme size token or pixel value. @default 'xs' | | `labelProps` | Omit | No | | Override props applied to the label `` | | `descriptionProps` | Omit | No | | Override props applied to the description `` | ## Examples ### Basics ID: `Progress.basic` β€’ Tags: progress β€’ Category: basics β€’ Status: stable β€’ Since: 1.0.0 Track a single completion percentage. Set `transitionDuration` so the bar animates its width whenever `value` changes instead of snapping to it. ```tsx const TRANSITION_MS = 400; const [completion, setCompletion] = useState(50); return ( ); } ``` ### Label and description ID: `Progress.label` β€’ Tags: label, description, error, field β€’ Category: basics β€’ Status: stable β€’ Since: 0.11.0 Progress takes the same field props as the input components: `label`, `description` (the sublabel beneath it), `error`, `required`, and `labelPosition`. The block renders outside the track β€” use `Progress.Label` for text drawn *inside* a filled section. ```tsx return ( ); } ``` ### Advanced ID: `Progress.advanced` β€’ Tags: animation, striped β€’ Category: behavior β€’ Status: stable β€’ Since: 1.0.0 Combine `striped` and `animate` to represent indeterminate work. ```tsx const [value, setValue] = useState(0); useEffect(() => { // Hold on the completed state for a beat, then start the run over. if (value >= 100) { const restart = setTimeout(() => setValue(0), 1800); return () => clearTimeout(restart); } const tick = setTimeout(() => { setValue((current) => Math.min(100, current + stageFor(current).speed)); }, TICK_MS); return () => clearTimeout(tick); }, [value]); const done = value >= 100; const stage = stageFor(value); return ( ); } ``` ### Compound sections ID: `Progress.compound` β€’ Tags: compound, sections, label β€’ Category: composition β€’ Status: stable β€’ Since: 0.10.2 Compose a multi-part bar from `Progress.Root`, `Progress.Section`, and `Progress.Label`. Each section is sized as a percentage of the track, so sections may sum to less than 100% and leave the remainder unfilled. ```tsx return ( Sections with inline labels Docs Media Other Sections take a share of the track, so the remaining 22% stays unfilled. Striped and animated sections `striped` and `animate` work per section, marking in-flight work. ); } ``` ### With tooltips ID: `Progress.tooltips` β€’ Tags: tooltip, sections, hover β€’ Category: composition β€’ Status: stable β€’ Since: 0.10.2 ```tsx const SECTIONS = [ { label: 'Documents', value: 34, color: 'primary' as const }, { label: 'Photos', value: 26, color: 'success' as const }, { label: 'Backups', value: 18, color: 'warning' as const } ]; return ( {SECTIONS.map((section) => ( {section.value}% ))} ); } ``` ### Example β€” segments with legend ID: `Progress.segments` β€’ Tags: segments, legend, storage β€’ Category: composition β€’ Status: stable β€’ Since: 0.10.2 Custom-colored segments with tooltips and legend. ```tsx const USAGE = [ { label: 'Documents', value: 32, color: '#4c6ef5' }, { label: 'Music', value: 24, color: '#12b886' }, { label: 'Code', value: 14, color: '#fab005' }, { label: 'Video Games', value: 9, color: '#fa5252' } ]; const TOTAL_GB = 500; const used = USAGE.reduce((sum, segment) => sum + segment.value, 0); const formatSize = (percent: number) => { const gb = (percent / 100) * TOTAL_GB; return gb < 1 ? `${Math.round(gb * 1024)} MB` : `${Math.round(gb)} GB`; }; return ( Project storage {formatSize(used)} of {TOTAL_GB} GB used {USAGE.map((segment) => ( {formatSize(segment.value)} ))} {USAGE.map((segment) => ( {segment.label} {segment.value}% ))} ); } ``` ### Vertical orientation ID: `Progress.vertical` β€’ Tags: orientation, vertical β€’ Category: composition β€’ Status: stable β€’ Since: 0.10.2 Set `orientation="vertical"` to fill from the bottom up. Vertical bars have no intrinsic length, so they default to 160 β€” use `length` (or `h`) to size them. ```tsx const CHANNELS = [ { label: 'Kick', value: 82, color: 'primary' as const }, { label: 'Snare', value: 64, color: 'success' as const }, { label: 'Bass', value: 91, color: 'warning' as const }, { label: 'Vox', value: 47, color: 'error' as const } ]; return ( Vertical bars fill from the bottom up {CHANNELS.map((channel) => ( {channel.label} ))} ); } ``` -------------------------------------------------------------------------------- # QRCode The QRCode component generates QR codes for encoding text, URLs, or other data. Supports customization of size, colors, quiet zones, error correction, and various rendering options. ## Metadata - Canonical name: `QRCode` - Package: `@platform-blocks/react-ui-library` - Import: `import { QRCode } from '@platform-blocks/react-ui-library';` - Category: data - Tags: qrcode, barcode, scan, data, encoding - Docs: https://react-ui-library.com/components/QRCode - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/QRCode ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `value` | string | Yes | | The data/text to encode in the QR code | | `label` | React.ReactNode | No | | Caption rendered with the code β€” what the user is being asked to scan. Also supplies the accessibility label when `accessibilityLabel` is unset. | | `description` | React.ReactNode | No | | Secondary line rendered under the label, for the longer explanation. | | `labelPosition` | 'top' \| 'bottom' | No | 'bottom' | Which side of the code the caption sits on. @default 'bottom' | | `labelProps` | Omit | No | | Override props applied to the label `` | | `descriptionProps` | Omit | No | | Override props applied to the description `` | | `size` | ComponentSizeValue | No | | Size of the QR code (both width and height). Accepts a size token (`xs`–`3xl`) or an explicit pixel value. | | `backgroundColor` | string | No | | Background color of the QR code | | `color` | string | No | | Foreground color (the QR code pattern color) | | `moduleShape` | 'square' \| 'rounded' \| 'diamond' | No | | Module shape variant for data modules. Note: Finder patterns (corner anchors) always remain square for optimal scanner compatibility. | | `finderShape` | 'square' \| 'rounded' | No | | Corner (finder) shape variant - DEPRECATED: Finder patterns always remain square | | `cornerRadius` | number | No | | Rounded corner radius factor (0-1) applied when moduleShape='rounded' | | `gradient` | { type?: 'linear' \| 'radial'; from: string; to: string; rotation?: number; } | No | | Gradient fill (overrides color) | | `errorCorrectionLevel` | 'L' \| 'M' \| 'Q' \| 'H' | No | | Error correction level | | `quietZone` | number | No | | Quiet zone size (border modules around the QR code). Defaults to 1 for compact layouts. Set to 4 for strict QR code standard compliance. Set to 0 to remove all padding around the code. | | `logo` | { uri: string \| ImageSourcePropType; element?: React.ReactNode; size?: number; backgroundColor?: string; borderRadius?: number; } | No | | Logo to display in the center of the QR code | | `style` | StyleProp | No | | Custom container style | | `testID` | string | No | | Test ID for testing | | `accessibilityLabel` | string | No | | Accessibility label | | `onError` | (error: Error) => void | No | | Callback when QR code generation fails | | `onLoadStart` | () => void | No | | Callback when QR code starts loading | | `onLoadEnd` | () => void | No | | Callback when QR code finishes loading | | `copyOnPress` | boolean \| { value?: string } | No | | If true (or object), tapping the QR copies the value (or provided value). | | `showCopyButton` | boolean | No | | Show a floating copy button overlay | | `copyToastTitle` | string | No | | Custom toast title when copied | | `copyToastMessage` | string | No | | Custom toast message when copied | | `m` | number | No | | Margin applied to all sides | | `mt` | number | No | | Margin applied to the top side | | `mr` | number | No | | Margin applied to the right side | | `mb` | number | No | | Margin applied to the bottom side | | `ml` | number | No | | Margin applied to the left side | | `mx` | number | No | | Horizontal margin applied to left and right sides | | `my` | number | No | | Vertical margin applied to top and bottom sides | | `p` | number | No | | Padding applied to all sides | | `pt` | number | No | | Padding applied to the top side | | `pr` | number | No | | Padding applied to the right side | | `pb` | number | No | | Padding applied to the bottom side | | `pl` | number | No | | Padding applied to the left side | | `px` | number | No | | Horizontal padding applied to left and right sides | | `py` | number | No | | Vertical padding applied to top and bottom sides | | `fullWidth` | boolean | No | | Makes the component fill the full width of its parent | | `w` | DimensionValue | No | | Sets a specific width | | `h` | DimensionValue | No | | Sets a specific height | | `maxW` | DimensionValue | No | | Sets the maximum width | | `minW` | DimensionValue | No | | Sets the minimum width | | `maxH` | DimensionValue | No | | Sets the maximum height | | `minH` | DimensionValue | No | | Sets the minimum height | ## Examples ### Basics ID: `QRCode.basic` β€’ Tags: qr-code β€’ Category: basics β€’ Status: stable β€’ Since: 1.0.0 Render a single QR code for a link or payload and provide helper text for scanning context. ```tsx return ( ); } ``` ### Sizes ID: `QRCode.sizes` β€’ Tags: size β€’ Category: layout β€’ Status: stable β€’ Since: 1.0.0 Size accepts a token (`xs`–`3xl`) or an explicit pixel value, so QR codes line up with the rest of the size system while still allowing a bespoke footprint. ```tsx return ( {SIZES.map((size) => ( ))} ); } ``` ### Spacing ID: `QRCode.spacing` β€’ Tags: quiet-zone β€’ Category: layout β€’ Status: stable β€’ Since: 1.0.0 Compare quiet zone values and pair them with outer spacing props when embedding codes in dense layouts. ```tsx const theme = useTheme(); return ( {QUIET_ZONES.map(({ label, quietZone }) => ( ))} Use spacing props and container styling to pad the QR code externally. ); } ``` ### Colors ID: `QRCode.colors` β€’ Tags: palette β€’ Category: theming β€’ Status: stable β€’ Since: 1.0.0 Derive QR foreground and background colors from theme palettes to keep scans on brand. ```tsx const theme = useTheme(); return ( Theme-aligned palettes {SCHEMES.map(({ key, label }) => { const palette = theme.colors[key]; const foreground = palette?.[6] ?? theme.colors.primary[6]; const background = palette?.[0] ?? theme.backgrounds.surface; return ( ); })} ); } ``` ### Shapes ID: `QRCode.shapes` β€’ Tags: modules β€’ Category: features β€’ Status: stable β€’ Since: 1.0.0 Switch between square, rounded, and diamond module shapes while keeping finder patterns scanner-safe. ```tsx return ( Module geometry {SHAPES.map(({ label, value, moduleShape, cornerRadius }) => ( ))} ); } ``` ### Gradients ID: `QRCode.gradient` β€’ Tags: gradient β€’ Category: theming β€’ Status: stable β€’ Since: 1.0.0 Blend theme colors with linear or radial gradients to add polish without hurting scan reliability. ```tsx const theme = useTheme(); const gradients = createGradientExamples(theme); return ( Gradient fills {gradients.map(({ label, value, gradient, moduleShape, cornerRadius }) => ( ))} ); } ``` ### Interactive ID: `QRCode.interactive` β€’ Tags: controls β€’ Category: interaction β€’ Status: stable β€’ Since: 1.0.0 Let editors tweak the payload, size, error correction, and module shape while previewing the QR code live. ```tsx const [value, setValue] = useState(PRESETS[0].value); const [size, setSize] = useState<(typeof SIZES)[number]>(SIZES[1]); const [errorLevel, setErrorLevel] = useState<(typeof ERROR_LEVELS)[number]>('M'); const [moduleShape, setModuleShape] = useState<(typeof MODULE_SHAPES)[number]>('square'); return ( Source content {PRESETS.map(({ label, value: preset }) => ( ))} {value.length} characters Size {SIZES.map((option) => ( ))} Error correction {ERROR_LEVELS.map((level) => ( ))} Lβ‰ˆ7% β€’ Mβ‰ˆ15% β€’ Qβ‰ˆ25% β€’ Hβ‰ˆ30% recovery Module shape {MODULE_SHAPES.map((shape) => ( ))} ); } ``` ### Logos ID: `QRCode.logo` β€’ Tags: logo β€’ Category: branding β€’ Status: stable β€’ Since: 1.0.0 Embed brand marks inside the QR code while preserving quiet zones and scanner-friendly contrast. ```tsx const theme = useTheme(); return ( {LOGO_EXAMPLES.map(({ label, value, moduleShape, cornerRadius, logo }) => ( ))} ); } ``` ### QR Code Variants ID: `QRCode.variants` β€’ Tags: variants, error-correction, quiet-zone β€’ Category: usage β€’ Status: stable β€’ Since: 1.0.0 Compare how error correction levels and quiet zone widths influence scannability. ```tsx return ( Error correction levels {ERROR_LEVELS.map(({ label, value }) => ( ))} Quiet zone widths {QUIET_ZONES.map((quietZone) => ( ))} ); } ``` -------------------------------------------------------------------------------- # Radio Radio buttons allow users to select a single option from a group of mutually exclusive choices. ## Metadata - Canonical name: `Radio` - Package: `@platform-blocks/react-ui-library` - Import: `import { Radio } from '@platform-blocks/react-ui-library';` - Status: stable - Since: 1.0.0 - Category: input - Tags: input, form, selection, choice - Docs: https://react-ui-library.com/components/Radio - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Radio ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `value` | string | Yes | | Radio value | | `checked` | boolean | No | | Whether radio is selected | | `onChange` | (value: string) => void | No | | Change handler | | `name` | string | No | | Radio group name | | `size` | SizeValue | No | | Radio size | | `color` | ColorValue | No | | Radio color theme | | `label` | React.ReactNode | No | | Radio label | | `disabled` | boolean | No | | Whether radio is disabled | | `required` | boolean | No | | Whether radio is required | | `error` | string | No | | Error message | | `description` | string | No | | Helper text | | `labelPosition` | 'left' \| 'right' | No | | Label position relative to radio | | `children` | React.ReactNode | No | | Radio content/children (alternative to label) | | `icon` | React.ReactNode \| string | No | | Optional icon displayed alongside the label | | `onKeyDown` | (event: any) => void | No | | Key handler for accessibility/keyboard support | | `labelProps` | Omit | No | | Override props applied to the label `` | | `descriptionProps` | Omit | No | | Override props applied to the description `` | | `transitionDuration` | number | No | 160 | Length of the select/deselect animation in ms; the center dot grows in and shrinks out against it. `0` applies the state instantly. Always 0 under reduced motion. | | `testID` | string | No | | Component test ID for testing | | `style` | any | No | | Additional CSS styles | | `m` | number | No | | Margin applied to all sides | | `mt` | number | No | | Margin applied to the top side | | `mr` | number | No | | Margin applied to the right side | | `mb` | number | No | | Margin applied to the bottom side | | `ml` | number | No | | Margin applied to the left side | | `mx` | number | No | | Horizontal margin applied to left and right sides | | `my` | number | No | | Vertical margin applied to top and bottom sides | | `p` | number | No | | Padding applied to all sides | | `pt` | number | No | | Padding applied to the top side | | `pr` | number | No | | Padding applied to the right side | | `pb` | number | No | | Padding applied to the bottom side | | `pl` | number | No | | Padding applied to the left side | | `px` | number | No | | Horizontal padding applied to left and right sides | | `py` | number | No | | Vertical padding applied to top and bottom sides | ## Examples ### Basic Usage ID: `Radio.basic` β€’ Tags: Radio, RadioGroup β€’ Category: basics β€’ Status: stable β€’ Since: 1.0.0 Use standalone `Radio` components for custom layouts or pass an `options` array to `RadioGroup` for quick single-selection forms. ```tsx const TEAMS = ['Falcons', 'Tigers', 'Sharks'] as const; const [favoriteTeam, setFavoriteTeam] = useState('Tigers'); const [ticketType, setTicketType] = useState('reserved'); return ( Standalone radios {TEAMS.map((team) => ( ))} Grouped selection ); } ``` ### Variants ID: `Radio.variants` β€’ Tags: variants, radio, radiogroup, segmented, chip, card β€’ Category: usage β€’ Status: stable β€’ Since: 1.0.0 The `variant` prop on `RadioGroup` selects how the group is laid out and how the selected option is communicated. `default` keeps the classic dot indicators; `card` renders each option as a bordered surface (useful when options have descriptions); `segmented` joins the options into a single iOS-style control; `chip` lays them out as wrap-friendly pills (good for filter UIs). ```tsx const PLAN_OPTIONS = [ { label: 'Starter', value: 'starter', description: 'Up to 3 projects, community support' }, { label: 'Growth', value: 'growth', description: 'Unlimited projects, priority email support' }, { label: 'Scale', value: 'scale', description: 'Dedicated success manager + SSO' }, ]; const FREQUENCY_OPTIONS = [ { label: 'Daily', value: 'daily' }, { label: 'Weekly', value: 'weekly' }, { label: 'Monthly', value: 'monthly' }, ]; const FILTER_OPTIONS = [ { label: 'All', value: 'all' }, { label: 'Active', value: 'active' }, { label: 'Archived', value: 'archived' }, { label: 'Trashed', value: 'trashed' }, ]; const [defaultValue, setDefaultValue] = useState('weekly'); const [planValue, setPlanValue] = useState('growth'); const [frequencyValue, setFrequencyValue] = useState('weekly'); const [filterValue, setFilterValue] = useState('active'); return ( default card segmented chip ); } ``` ### Theming ID: `Radio.theming` β€’ Tags: size, color, state β€’ Category: theming β€’ Status: stable β€’ Since: 1.0.0 Combine the `size`, `color`, and validation props to align radios with your UI tokens and state requirements. ```tsx const COLOR_OPTIONS = ['primary', 'secondary', 'success', 'error'] as const; const [sizeValue, setSizeValue] = useState('club'); const [colorValue, setColorValue] = useState('primary'); return ( Size tokens Semantic colors {COLOR_OPTIONS.map((tone) => ( setColorValue(value as typeof COLOR_OPTIONS[number])} label={`${tone.charAt(0).toUpperCase()}${tone.slice(1)} tickets`} color={tone} /> ))} Common states ); } ``` ### Orientations ID: `Radio.orientations` β€’ Tags: horizontal, vertical β€’ Category: layout β€’ Status: stable β€’ Since: 1.0.0 Toggle `orientation` between `horizontal` and `vertical` to adapt radio groups to the available space. ```tsx const [favoriteSport, setFavoriteSport] = useState('soccer'); const [skillLevel, setSkillLevel] = useState('intermediate'); return ( Horizontal layout Vertical layout ); } ``` ### Forms ID: `Radio.forms` β€’ Tags: form, validation β€’ Category: advanced β€’ Status: stable β€’ Since: 1.0.0 Pair `RadioGroup` with `required` and `error` messaging to validate selections before submitting a form workflow. ```tsx const PLANS = [ { label: 'Starter β€” $9/mo', value: 'starter', description: 'Streamline a single project' }, { label: 'Team β€” $19/mo', value: 'team', description: 'Collaborate with up to 10 teammates' }, { label: 'Club β€” $39/mo', value: 'club', description: 'Unlock advanced analytics' } ]; const BILLING = [ { label: 'Monthly', value: 'monthly' }, { label: 'Annual (save 20%)', value: 'annual' } ]; const [plan, setPlan] = useState(''); const [billingCycle, setBillingCycle] = useState('monthly'); const [planError, setPlanError] = useState(); const [confirmation, setConfirmation] = useState(null); const handleSubmit = () => { if (!plan) { setPlanError('Select a plan to continue'); setConfirmation(null); return; } setPlanError(undefined); setConfirmation(`Subscribed to the ${plan} plan with ${billingCycle} billing.`); }; return ( { setPlan(next); setPlanError(undefined); }} error={planError} required /> {confirmation && ( {confirmation} )} ); } ``` -------------------------------------------------------------------------------- # Rating An interactive component for displaying star ratings and allowing users to provide ratings with customizable appearance. ## Metadata - Canonical name: `Rating` - Package: `@platform-blocks/react-ui-library` - Import: `import { Rating } from '@platform-blocks/react-ui-library';` - Category: input - Tags: rating, stars, review, score, feedback - Docs: https://react-ui-library.com/components/Rating - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Rating ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `value` | number | No | | Current rating value | | `defaultValue` | number | No | 0 | Initial rating value for uncontrolled component | | `count` | number | No | 5 | Number of rating items (stars) to render | | `readOnly` | boolean | No | false | Disables input β€” the rating only displays its value | | `disabled` | boolean | No | false | Disables the rating. Like `readOnly` it blocks input, but it also dims the control and reports a disabled state to assistive technology. | | `allowFraction` | boolean | No | false | Allows partial values so a star can be filled fractionally | | `precision` | number | No | 0.1 when `allowFraction`, otherwise 1 | Smallest increment a value is rounded to when `allowFraction` is enabled. Clamped to the `0.01`–`1` range. | | `size` | SizeValue \| number | No | 'md' | Size of each rating item β€” a theme size token or an explicit pixel size | | `color` | string | No | | Color of filled items. Defaults to the theme warning color. | | `emptyColor` | string | No | | Color of empty items. Defaults to the theme gray color. | | `hoverColor` | string | No | | Color of items while hovering/dragging. Defaults to a darker theme warning color. | | `onChange` | (value: number) => void | No | | Called with the new value when the rating changes | | `onHover` | (value: number) => void | No | | Called with the previewed value while hovering (web only) | | `clearable` | boolean | No | false | Allows clearing the rating by selecting the value that is already set | | `required` | boolean | No | false | Marks the field as required. Renders an asterisk beside the label and reports the requirement to assistive technology on web. | | `error` | React.ReactNode | No | | Error message rendered below the rating | | `description` | React.ReactNode | No | | Helper text rendered below the rating | | `showTooltip` | boolean | No | false | Shows a tooltip with the current value out of `count` while hovering | | `getTooltipLabel` | (value: number, count: number) => string | No | | Formats the tooltip text. Receives the previewed value and `count`; defaults to `4.5 / 5`. | | `icon` | RatingIcon | No | | Icon rendered for each item instead of the default star. Accepts an icon registry name (`'heart'`), an icon library component, or an element. Takes precedence over `character`. | | `emptyIcon` | RatingIcon | No | | Icon rendered for empty items. Defaults to `icon`, so the same glyph is drawn in `emptyColor` unless a different empty icon is supplied. | | `character` | string \| React.ReactNode | No | 'β˜…' | Character or node rendered for filled items. Custom strings render as text glyphs, a React element is cloned with `size` and `color`, and the default star character renders the built-in star icon. Ignored when `icon` is set. | | `emptyCharacter` | string \| React.ReactNode | No | 'β˜†' | Character or node rendered for empty items. Ignored when `icon` or `emptyIcon` is set. | | `gap` | SizeValue \| number | No | 'xs' | Spacing between rating items β€” a theme size token or an explicit pixel value | | `style` | StyleProp | No | | Additional styles applied to the root element | | `testID` | string | No | | Test ID for testing | | `accessibilityLabel` | string | No | | Custom accessibility label. Defaults to `Rating: {value} out of {count} stars`. | | `accessibilityHint` | string | No | | Custom accessibility hint. Defaults to an adjust hint unless `readOnly`. | | `label` | React.ReactNode | No | | Label rendered next to the rating. Strings are wrapped in a secondary `Text`. | | `labelPosition` | 'left' \| 'right' \| 'above' \| 'below' | No | 'above' | Placement of the label relative to the rating | | `labelGap` | SizeValue \| number | No | 'xs' | Spacing between the label and the rating β€” a theme size token or pixel value | | `m` | number | No | | Margin applied to all sides | | `mt` | number | No | | Margin applied to the top side | | `mr` | number | No | | Margin applied to the right side | | `mb` | number | No | | Margin applied to the bottom side | | `ml` | number | No | | Margin applied to the left side | | `mx` | number | No | | Horizontal margin applied to left and right sides | | `my` | number | No | | Vertical margin applied to top and bottom sides | | `p` | number | No | | Padding applied to all sides | | `pt` | number | No | | Padding applied to the top side | | `pr` | number | No | | Padding applied to the right side | | `pb` | number | No | | Padding applied to the bottom side | | `pl` | number | No | | Padding applied to the left side | | `px` | number | No | | Horizontal padding applied to left and right sides | | `py` | number | No | | Vertical padding applied to top and bottom sides | ## Examples ### Basics ID: `Rating.basic` β€’ Tags: interactive β€’ Category: basics β€’ Status: stable β€’ Since: 1.0.0 Capture a single rating value with an interactive control and mirror the current score in helper text. ```tsx const [score, setScore] = useState(3); return ( Current score: {score} out of 5. ); } ``` ### Sizes ID: `Rating.sizes` β€’ Tags: size β€’ Category: layout β€’ Status: stable β€’ Since: 1.0.0 Compare the available `size` tokens side by side to pick the right scale for your scene. ```tsx const SIZES = ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl'] as const; return ( {SIZES.map((size) => ( ))} ); } ``` ### Colors ID: `Rating.colors` β€’ Tags: palette β€’ Category: theming β€’ Status: stable β€’ Since: 1.0.0 Derive filled, hover, and empty colors from the theme palette to align ratings with product semantics. ```tsx const COLOR_CONFIG = [ { key: 'primary', label: 'Primary accent', getColors: (palette: string[]) => ({ color: palette[5], emptyColor: palette[1], hoverColor: palette[6] }) }, { key: 'success', label: 'Success feedback', getColors: (palette: string[]) => ({ color: palette[5], emptyColor: palette[1], hoverColor: palette[6] }) }, { key: 'warning', label: 'Warning feedback', getColors: (palette: string[]) => ({ color: palette[5], emptyColor: palette[1], hoverColor: palette[6] }) } ] as const; type PaletteKey = (typeof COLOR_CONFIG)[number]['key']; const theme = useTheme(); const [values, setValues] = useState>({ primary: 4, success: 3.5, warning: 2.5 }); return ( {COLOR_CONFIG.map(({ key, label, getColors }) => { const palette = theme.colors[key as keyof typeof theme.colors] ?? theme.colors.gray; const { color, emptyColor, hoverColor } = getColors(palette); return ( setValues((prev) => ({ ...prev, [key]: next })) } color={color} emptyColor={emptyColor} hoverColor={hoverColor} size="lg" labelPosition="right" label={ {label} } /> ); })} ); } ``` ### Fractions ID: `Rating.fractions` β€’ Tags: precision β€’ Category: features β€’ Status: stable β€’ Since: 1.0.0 Enable fractional ratings with configurable `precision` values to capture nuanced feedback. ```tsx const FRACTION_SETTINGS = [ { key: 'match', label: 'Match excitement', precision: 0.1, helper: 'Set scores in 0.1 increments to capture precise fan sentiment.' }, { key: 'broadcast', label: 'Broadcast quality', precision: 0.5, helper: 'Use half-star increments when quick feedback is enough.' } ] as const; type FractionKey = (typeof FRACTION_SETTINGS)[number]['key']; const theme = useTheme(); const [values, setValues] = useState>({ match: 4.2, broadcast: 3.5 }); return ( {FRACTION_SETTINGS.map(({ key, label, precision, helper }) => ( {label} setValues((prev) => ({ ...prev, [key]: next }))} allowFraction precision={precision} size="lg" color={theme.colors.highlight[5]} emptyColor={theme.colors.highlight[1]} hoverColor={theme.colors.highlight[6]} showTooltip /> {helper} ))} ); } ``` ### Custom Icons ID: `Rating.icons` β€’ Tags: icon, character β€’ Category: theming β€’ Status: stable β€’ Since: 0.11.0 Swap the default star for any registry icon with `icon`, pair it with a different `emptyIcon` for the unfilled state, or fall back to plain text glyphs through `character` and `emptyCharacter`. ```tsx const theme = useTheme(); const [hearts, setHearts] = useState(4); const [bolts, setBolts] = useState(3); return ( ); } ``` ### Variants ID: `Rating.variants` β€’ Tags: interactive, read-only β€’ Category: behavior β€’ Status: stable β€’ Since: 1.0.0 Contrast interactive, read-only, and tooltip-enabled ratings to decide which fits your feedback flow. ```tsx const [interactiveValue, setInteractiveValue] = useState(4); return ( `${value} out of ${count} stars`} size="lg" label="Custom tooltip text" disclaimer="Pass `getTooltipLabel` to format the tooltip." /> ); } ``` ### Form Field ID: `Rating.form-field` β€’ Tags: validation, required, clearable β€’ Category: features β€’ Status: stable β€’ Since: 0.11.0 Use `required`, `description`, and `error` to drop a rating into a form like any other field, and `clearable` to let people undo a score by selecting it again. ```tsx const [score, setScore] = useState(0); const [submitted, setSubmitted] = useState(false); const error = submitted && score === 0 ? 'Please choose a rating' : undefined; return ( {score === 0 ? 'No rating selected.' : `You rated ${score} out of 5.`} ); } ``` -------------------------------------------------------------------------------- # Ring The Ring component displays progress or status using a radial indicator. It supports custom labels, color stops, neutral states, and fully customized center content. ## Metadata - Canonical name: `Ring` - Package: `@platform-blocks/react-ui-library` - Import: `import { Ring } from '@platform-blocks/react-ui-library';` - Category: feedback - Tags: ring, progress, indicator, radial - Docs: https://react-ui-library.com/components/Ring - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Ring ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `value` | number | Yes | | Current value represented by the ring | | `min` | number | No | 0 | Lower bound for normalization. Defaults to 0. | | `max` | number | No | 100 | Upper bound for normalization. Defaults to 100. | | `size` | number | No | 100 | Diameter of the ring in pixels. Defaults to 100. | | `thickness` | number | No | 12 | Stroke thickness in pixels. Defaults to 12. | | `caption` | React.ReactNode | No | | Optional caption rendered beneath the ring | | `label` | React.ReactNode | No | | Main label rendered in the ring center | | `subLabel` | React.ReactNode | No | | Secondary label rendered below the main label | | `showValue` | boolean | No | true | Displays the computed percentage when no label/subLabel is provided. Defaults to true. | | `valueFormatter` | (value: number, percent: number) => React.ReactNode | No | | Formats the displayed value or percentage | | `trackColor` | string | No | | Track color behind the progress stroke | | `progressColor` | string \| ((value: number, percent: number) => string) | No | | Progress stroke color or resolver | | `colorStops` | RingColorStop[] | No | | Optional color stops evaluated against the computed percent | | `neutral` | boolean | No | false | Forces the ring into a neutral state, disabling the progress stroke | | `roundedCaps` | boolean | No | true | Controls whether the progress stroke has rounded caps. Defaults to true. | | `style` | StyleProp | No | | Container style for the outer wrapper | | `ringStyle` | StyleProp | No | | Style applied to the ring wrapper | | `contentStyle` | StyleProp | No | | Style applied to the center content container | | `labelStyle` | StyleProp | No | | Style overrides for the main label | | `subLabelStyle` | StyleProp | No | | Style overrides for the secondary label | | `captionStyle` | StyleProp | No | | Style overrides for the caption | | `labelColor` | string | No | | Color override for the main label | | `subLabelColor` | string | No | | Color override for the secondary label | | `captionColor` | string | No | | Color override for the caption | | `children` | React.ReactNode \| ((context: RingRenderContext) => React.ReactNode) | No | | Custom center content. Receives value info when passed as a function | | `testID` | string | No | | Test identifier for end-to-end tests | | `accessibilityLabel` | string | No | | Accessibility label describing the ring | | `m` | number | No | | Margin applied to all sides | | `mt` | number | No | | Margin applied to the top side | | `mr` | number | No | | Margin applied to the right side | | `mb` | number | No | | Margin applied to the bottom side | | `ml` | number | No | | Margin applied to the left side | | `mx` | number | No | | Horizontal margin applied to left and right sides | | `my` | number | No | | Vertical margin applied to top and bottom sides | | `p` | number | No | | Padding applied to all sides | | `pt` | number | No | | Padding applied to the top side | | `pr` | number | No | | Padding applied to the right side | | `pb` | number | No | | Padding applied to the bottom side | | `pl` | number | No | | Padding applied to the left side | | `px` | number | No | | Horizontal padding applied to left and right sides | | `py` | number | No | | Vertical padding applied to top and bottom sides | ## Examples ### Interactive Presets ID: `Ring.basic` β€’ Tags: ring β€’ Category: usage β€’ Status: stable β€’ Since: 1.0.0 Drive multiple ring presentations from a single stateful value and expose how sizing and labels adapt. ```tsx const [value, setValue] = useState(72); return ( ); } ``` ### Dynamic Color Stops ID: `Ring.color-stops` β€’ Tags: ring β€’ Category: styling β€’ Status: stable β€’ Since: 1.0.0 Display how `colorStops` shift the progress color as values cross threshold ranges. ```tsx const colorStops = [ { value: 0, color: '#f87171' }, { value: 60, color: '#f59e0b' }, { value: 90, color: '#14b8a6' }, ]; return ( {[48, 72, 97].map((value) => ( ))} ); } ``` ### Custom Center Content ID: `Ring.custom-content` β€’ Tags: ring β€’ Category: customization β€’ Status: stable β€’ Since: 1.0.0 Showcase the render-prop API for injecting icons, text, or status badges inside the ring. ```tsx return ( {({ percent }) => ( {Math.round(percent)}% )} On hold ); } ``` -------------------------------------------------------------------------------- # RollingNumber RollingNumber displays a number and animates every digit that changes, rolling it to its new position. Use it for counters, live totals, prices and metric readouts where the change itself is part of the information. ## Metadata - Canonical name: `RollingNumber` - Package: `@platform-blocks/react-ui-library` - Import: `import { RollingNumber } from '@platform-blocks/react-ui-library';` - Category: display - Tags: number, counter, animation, odometer, metric - Docs: https://react-ui-library.com/components/RollingNumber - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/RollingNumber ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `value` | number | Yes | | Value to display. Each digit that changes rolls to its new position. | | `prefix` | string | No | | Static text rendered before the number (e.g. `"$ "`). | | `suffix` | string | No | | Static text rendered after the number (e.g. `" USD"`). | | `thousandSeparator` | boolean \| string | No | false | `true` for `,`, or an explicit separator string. | | `decimalSeparator` | string | No | '.' | Character between the integer and decimal parts. Default `.`. | | `decimalScale` | number | No | | Number of decimal places to render. | | `fixedDecimalScale` | boolean | No | false | Pad the decimal part with zeros up to `decimalScale`. | | `transitionDuration` | number | No | | Roll duration in ms. Default `600`. `0` β€” and an active reduced-motion preference β€” snap straight to the new digits. | | `animationDuration` | number | No | | alias for `transitionDuration`. | | `timingFunction` | RollingNumberTimingFunction | No | 'ease' | Easing curve for the roll. Default `ease`. | | `stagger` | number | No | 0 | Per-column delay in ms, applied right-to-left so the least significant digit leads. Default `0` (all columns move together). | | `animateOnMount` | boolean | No | false | Animate from zero on first render instead of appearing settled. Default `false`. | | `size` | SizeValue | No | 'md' | Font size token or explicit number. Default `'md'`. | | `color` | string | No | | Text color. Accepts theme palette syntax (`'primary.6'`, `'dimmed'`) or any CSS color. | | `c` | string | No | | Shorthand alias for `color`, resolved identically. `color` wins when both are set. | | `weight` | TextStyle['fontWeight'] \| 'normal' \| 'medium' \| 'semibold' \| 'bold' | No | | Font weight. | | `fontFamily` | string | No | | Custom font family. | | `ff` | string | No | | Shorthand alias for `fontFamily`. | | `tabularNums` | boolean | No | true | Use tabular (fixed-width) figures so columns do not shift width as digits change. Default `true`. | | `style` | StyleProp | No | | Style for the row that wraps prefix, digits and suffix. | | `textStyle` | StyleProp | No | | Style applied to every glyph β€” digits, separators, prefix and suffix. | | `digitStyle` | StyleProp | No | | Style applied to digit glyphs only. | | `accessibilityLabel` | string | No | | Screen-reader label. Defaults to the formatted value including prefix and suffix, so the rolling columns never have to be read digit by digit. | | `testID` | string | No | | | | `m` | number | No | | Margin applied to all sides | | `mt` | number | No | | Margin applied to the top side | | `mr` | number | No | | Margin applied to the right side | | `mb` | number | No | | Margin applied to the bottom side | | `ml` | number | No | | Margin applied to the left side | | `mx` | number | No | | Horizontal margin applied to left and right sides | | `my` | number | No | | Vertical margin applied to top and bottom sides | | `p` | number | No | | Padding applied to all sides | | `pt` | number | No | | Padding applied to the top side | | `pr` | number | No | | Padding applied to the right side | | `pb` | number | No | | Padding applied to the bottom side | | `pl` | number | No | | Padding applied to the left side | | `px` | number | No | | Horizontal padding applied to left and right sides | | `py` | number | No | | Vertical padding applied to top and bottom sides | ## Examples ### Basic ID: `RollingNumber.basic` β€’ Tags: basic, counter, number β€’ Category: basics β€’ Status: stable β€’ Since: 1.1.0 A counter whose digits roll to their new positions. Only the columns that changed move. ```tsx const [value, setValue] = useState(1234); return ( ); } ``` ### Currency ID: `RollingNumber.currency` β€’ Tags: currency, prefix, suffix, decimals β€’ Category: features β€’ Status: stable β€’ Since: 1.1.0 `prefix`, `suffix` and the decimal options cover currency formatting without an external formatter. Copying the value on web yields the formatted string, not the digit strips. ```tsx const [total, setTotal] = useState(1299.99); return ( ); } ``` ### Timing ID: `RollingNumber.timing` β€’ Tags: animation, duration, easing, stagger β€’ Category: features β€’ Status: stable β€’ Since: 1.1.0 `transitionDuration`, `timingFunction` and `stagger` shape the roll. Stagger delays each column right-to-left, so the carries trail the ones place the way an odometer does. ```tsx const [value, setValue] = useState(407219); return ( Snappy β€” 200ms, no stagger Odometer β€” 900ms, 60ms stagger ); } ``` ### Live metric ID: `RollingNumber.live-metric` β€’ Tags: metric, dashboard, live β€’ Category: examples β€’ Status: stable β€’ Since: 1.1.0 A ticking metric tile. Values that change faster than the roll retarget mid-flight rather than snapping. ```tsx const [requests, setRequests] = useState(84213); useEffect(() => { const timer = setInterval(() => { setRequests((current) => current + Math.floor(Math.random() * 40)); }, 1200); return () => clearInterval(timer); }, []); return ( Requests today ); } ``` -------------------------------------------------------------------------------- # Search The Search component provides a search input with debouncing, loading states, and customizable clear functionality. ## Metadata - Canonical name: `Search` - Package: `@platform-blocks/react-ui-library` - Import: `import { Search } from '@platform-blocks/react-ui-library';` - Category: input - Tags: search, input, filter, debounce - Docs: https://react-ui-library.com/components/Search - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Search ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `value` | string | No | | | | `defaultValue` | string | No | | | | `onChange` | (value: string) => void | No | | | | `onSubmit` | (value: string) => void | No | | | | `placeholder` | string | No | | | | `size` | SizeValue | No | | | | `radius` | any | No | | | | `autoFocus` | boolean | No | | | | `debounce` | number | No | | | | `clearButton` | boolean | No | | | | `loading` | boolean | No | | | | `endSection` | React.ReactNode | No | | | | `accessibilityLabel` | string | No | | | | `style` | any | No | | | | `buttonMode` | boolean | No | | When true, renders as a button that opens the spotlight instead of a typeable input | | `onPress` | () => void | No | | Callback when search button is pressed (only used in buttonMode) | | `rightComponent` | React.ReactNode | No | | Component to render on the right side (useful for button mode to show shortcuts like CMD+K) | | `m` | number | No | | Margin applied to all sides | | `mt` | number | No | | Margin applied to the top side | | `mr` | number | No | | Margin applied to the right side | | `mb` | number | No | | Margin applied to the bottom side | | `ml` | number | No | | Margin applied to the left side | | `mx` | number | No | | Horizontal margin applied to left and right sides | | `my` | number | No | | Vertical margin applied to top and bottom sides | | `p` | number | No | | Padding applied to all sides | | `pt` | number | No | | Padding applied to the top side | | `pr` | number | No | | Padding applied to the right side | | `pb` | number | No | | Padding applied to the bottom side | | `pl` | number | No | | Padding applied to the left side | | `px` | number | No | | Horizontal padding applied to left and right sides | | `py` | number | No | | Vertical padding applied to top and bottom sides | ## Examples ### Basic search ID: `Search.basic` β€’ Tags: controlled, input β€’ Category: basics β€’ Status: stable β€’ Since: 0.3.0 Control the `Search` value with local state so you can react to user input and mirror the query elsewhere in your UI. ```tsx const [query, setQuery] = useState(''); return ( Current query: {query || 'β€”'} ); } ``` ### Button mode ID: `Search.button-mode` β€’ Tags: spotlight, shortcuts β€’ Category: behavior β€’ Status: stable β€’ Since: 0.3.0 Set `buttonMode` to turn `Search` into a pressable launcher and pass a `rightComponent` with `KeyCap` shortcuts so users discover keyboard access. ```tsx const toast = useToast(); const handleCustomPress = () => { toast.show({ message: 'Launching saved search…' }); }; return ( Default Spotlight launcher ⌘ K )} /> Custom handler with shortcut hint Ctrl F )} /> ); } ``` -------------------------------------------------------------------------------- # SegmentedControl Segmented controls present a small set of exclusive options. The indicator animates between segments with support for horizontal and vertical layouts, optional auto contrast for filled variants, and reduced motion awareness for accessibility. ## Metadata - Canonical name: `SegmentedControl` - Package: `@platform-blocks/react-ui-library` - Import: `import { SegmentedControl } from '@platform-blocks/react-ui-library';` - Status: beta - Since: 1.0.0 - Category: input - Tags: input, segmentation, toggle, selection - Docs: https://react-ui-library.com/components/SegmentedControl - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/SegmentedControl ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `data` | SegmentedControlData[] | Yes | | Data that defines the segments | | `value` | string | No | | Controlled value | | `defaultValue` | string | No | | Uncontrolled initial value | | `onChange` | (value: string) => void | No | | Called when value changes | | `size` | SizeValue | No | | Control size, maps to height and font size | | `color` | string | No | | Indicator color token or hex | | `orientation` | 'horizontal' \| 'vertical' | No | | Layout orientation | | `fullWidth` | boolean | No | | Stretch across available width | | `disabled` | boolean | No | | Disable entire control | | `readOnly` | boolean | No | | Prevent user interaction but keep visual state | | `autoContrast` | boolean | No | | Adjust text color automatically for filled/outline variants | | `withItemsBorders` | boolean | No | | Render dividers between items | | `transitionDuration` | number | No | | Indicator transition duration (ms) | | `transitionTimingFunction` | string | No | | Indicator transition easing | | `name` | string | No | | Optional radio group name hint | | `variant` | 'default' \| 'filled' \| 'outline' \| 'ghost' | No | | Visual style variant | | `indicatorStyle` | StyleProp | No | | Custom style for indicator | | `itemStyle` | StyleProp | No | | Custom style applied to every item | | `style` | StyleProp | No | | Style applied to the container | | `testID` | string | No | | Test identifier applied to container | | `accessibilityLabel` | string | No | | Accessibility label for the entire control | | `label` | ReactNode | No | | Optional label rendered alongside the control | | `description` | ReactNode | No | | Supplementary description text rendered with the label | | `labelPosition` | 'left' \| 'right' \| 'top' \| 'bottom' | No | | Placement of the label relative to the control | | `m` | number | No | | Margin applied to all sides | | `mt` | number | No | | Margin applied to the top side | | `mr` | number | No | | Margin applied to the right side | | `mb` | number | No | | Margin applied to the bottom side | | `ml` | number | No | | Margin applied to the left side | | `mx` | number | No | | Horizontal margin applied to left and right sides | | `my` | number | No | | Vertical margin applied to top and bottom sides | | `p` | number | No | | Padding applied to all sides | | `pt` | number | No | | Padding applied to the top side | | `pr` | number | No | | Padding applied to the right side | | `pb` | number | No | | Padding applied to the bottom side | | `pl` | number | No | | Padding applied to the left side | | `px` | number | No | | Horizontal padding applied to left and right sides | | `py` | number | No | | Vertical padding applied to top and bottom sides | | `w` | DimensionValue | No | | Sets a specific width | | `h` | DimensionValue | No | | Sets a specific height | | `maxW` | DimensionValue | No | | Sets the maximum width | | `minW` | DimensionValue | No | | Sets the minimum width | | `maxH` | DimensionValue | No | | Sets the maximum height | | `minH` | DimensionValue | No | | Sets the minimum height | | `radius` | RadiusValue | No | | Border radius value - supports size tokens, numbers, and special values | ## Examples ### Basic Usage ID: `SegmentedControl.basic` β€’ Tags: segmented-control, selection, uncontrolled β€’ Category: basics β€’ Status: stable β€’ Since: 1.0.0 Set `defaultValue` to preselect a segment and let the control manage focus and selection state internally. ```tsx return ( ); } ``` ### Controlled Value ID: `SegmentedControl.controlled` β€’ Tags: segmented-control, controlled, state β€’ Category: usage β€’ Status: stable β€’ Since: 1.0.0 Provide `value` and `onChange` to synchronize the selected segment with external state or companion controls. ```tsx const [value, setValue] = useState('react'); return ( Drive the segmented control from external state to synchronize its value with other inputs. Selected value: {value} ); } ``` ### Sizes ID: `SegmentedControl.sizes` β€’ Tags: segmented-control, sizes, density β€’ Category: layout β€’ Status: stable β€’ Since: 1.0.0 Use the `size` prop to match dense toolbars or spacious layouts without changing the underlying data. ```tsx const SIZES = ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl'] as const; return ( {SIZES.map((size) => ( {size} ))} ); } ``` ### Full Width ID: `SegmentedControl.full-width` β€’ Tags: segmented-control, layout, full-width β€’ Category: layout β€’ Status: stable β€’ Since: 1.0.0 Apply `fullWidth` to let segments expand and distribute evenly across the available horizontal space. ```tsx return ( ); } ``` ### Orientation ID: `SegmentedControl.orientation` β€’ Tags: segmented-control, layout, orientation β€’ Category: layout β€’ Status: stable β€’ Since: 1.0.0 Toggle the `orientation` prop to rotate the control vertically for sidebars or keep it horizontal for toolbars. ```tsx return ( ); } ``` ### Custom Colors ID: `SegmentedControl.colors` β€’ Tags: segmented-control, colors, theming β€’ Category: theming β€’ Status: stable β€’ Since: 1.0.0 Set the `color` prop to pull semantic tokens or pass custom values, and enable `autoContrast` when you need readable labels on vivid fills. ```tsx const palettes = [ { key: 'primary', color: 'primary', defaultValue: 'react', data: frameworks }, { key: 'success', color: 'success', defaultValue: 'code', data: panes }, { key: 'purple', color: 'purple', defaultValue: 'settings', data: accountSections }, { key: 'custom', color: '#FF6B6B', defaultValue: 'medium', data: priorities }, ]; return ( {palettes.map((palette) => ( ))} ); } ``` ### Interaction States ID: `SegmentedControl.states` β€’ Tags: segmented-control, states, disabled, readonly β€’ Category: behavior β€’ Status: stable β€’ Since: 1.0.0 Combine `disabled`, `readOnly`, or per-item `disabled` flags to signal availability without changing layout or selection rules. ```tsx const scenarios = [ { key: 'default', label: 'Interactive', props: {}, defaultValue: 'react', data: frameworks }, { key: 'disabled', label: 'Disabled', props: { disabled: true }, defaultValue: 'code', data: panes }, { key: 'readOnly', label: 'Read only', props: { readOnly: true }, defaultValue: 'medium', data: priorities }, // `languages` carries the disabled flag on its last item. { key: 'itemDisabled', label: 'Single option disabled', props: {}, defaultValue: 'typescript', data: languages }, ]; return ( {scenarios.map((scenario) => ( ))} ); } ``` ### Visual Variants ID: `SegmentedControl.variants` β€’ Tags: segmented-control, variants, styling β€’ Category: theming β€’ Status: stable β€’ Since: 1.0.0 Choose between `default`, `filled`, `outline`, or `ghost` variants and pair them with semantic `color` tokens to match the surrounding surface. ```tsx const variants = [ { key: 'default', label: 'Default', props: { variant: 'default' as const }, defaultValue: 'react', description: 'Baseline segmented control with tonal contrast.', data: frameworks, }, { key: 'filledPrimary', label: 'Filled', props: { variant: 'filled' as const, color: 'primary' as const }, defaultValue: 'code', description: 'Solid background that matches the selected color token.', data: panes, }, { key: 'filledContrast', label: 'Filled with auto-contrast', props: { variant: 'filled' as const, color: 'warning' as const, autoContrast: true, }, defaultValue: 'medium', description: 'Enable autoContrast when using vivid palettes to keep labels legible.', data: priorities, }, { key: 'outline', label: 'Outline', props: { variant: 'outline' as const, color: 'secondary' as const }, defaultValue: 'weekly', description: 'Focus on outlining the chosen tab while keeping the surface quiet.', data: cadences, }, { key: 'ghost', label: 'Ghost', props: { variant: 'ghost' as const, color: 'success' as const }, defaultValue: 'published', description: 'Ghost removes the segment background until selection, ideal on tinted surfaces.', data: publishStates, }, ]; return ( Change the variant to match the surface and emphasis level of the surrounding layout. {variants.map((variant) => ( {variant.label} {variant.description} ))} ); } ``` -------------------------------------------------------------------------------- # Select Select provides a dropdown interface for choosing from predefined options. It supports single and multi-selection modes, disabled states, validation, and customizable styling. ## Metadata - Canonical name: `Select` - Package: `@platform-blocks/react-ui-library` - Import: `import { Select } from '@platform-blocks/react-ui-library';` - Status: stable - Category: input - Docs: https://react-ui-library.com/components/Select - Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Select ## Props | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `value` | T \| null | No | | Current value when the component is controlled. | | `defaultValue` | T \| null | No | | Initial value when the component manages its own state. | | `onChange` | (value: T \| null, option?: SelectOption \| null) => void | No | | Callback fired whenever the selection changes. | | `options` | SelectOption[] | Yes | | Collection of options available to choose from. | | `placeholder` | string | No | | Placeholder text shown when no value is selected. | | `size` | SizeValue | No | | Size token controlling trigger height and typography. | | `radius` | any | No | | Corner radius token applied to the trigger and dropdown. | | `disabled` | boolean | No | | Disables the control when set to true. | | `label` | string | No | | Optional label rendered above the trigger. | | `description` | string | No | | Optional short descriptive text shown directly under the label (above the field). | | `helperText` | string | No | | Helper copy displayed beneath the control. | | `error` | string | No | | Error message shown beneath the control in error state. | | `searchable` | boolean | No | | Enables client-side filtering of options. | | `renderOption` | (opt: SelectOption, active: boolean, selected: boolean) => React.ReactNode | No | | Custom renderer for an individual option row. | | `fullWidth` | boolean | No | | Stretches the trigger to occupy the full width of its container. | | `maxH` | number | No | | Maximum height the dropdown may reach before it scrolls. | | `closeOnSelect` | boolean | No | | Whether the dropdown should close immediately after selection. | | `clearable` | boolean | No | | Allows the user to clear the current selection. | | `clearButtonLabel` | string | No | | Accessible label announced for the clear button when present. | | `onClear` | () => void | No | | Handler invoked after the selection is cleared. | | `refocusAfterSelect` | boolean | No | | Controls whether the trigger regains focus after selecting an option. | | `keyboardAvoidance` | boolean | No | | Whether dropdown positioning should avoid the on-screen keyboard. | | `labelProps` | Omit | No | | Override props applied to the label `` | | `descriptionProps` | Omit | No | | Override props applied to the description `` | | `variant` | InputVariant | No | | Visual variant of the trigger shell β€” `'default' \| 'filled' \| 'outline' \| 'unstyled'`. Mirrors ``. | | `m` | number | No | | Margin applied to all sides | | `mt` | number | No | | Margin applied to the top side | | `mr` | number | No | | Margin applied to the right side | | `mb` | number | No | | Margin applied to the bottom side | | `ml` | number | No | | Margin applied to the left side | | `mx` | number | No | | Horizontal margin applied to left and right sides | | `my` | number | No | | Vertical margin applied to top and bottom sides | | `p` | number | No | | Padding applied to all sides | | `pt` | number | No | | Padding applied to the top side | | `pr` | number | No | | Padding applied to the right side | | `pb` | number | No | | Padding applied to the bottom side | | `pl` | number | No | | Padding applied to the left side | | `px` | number | No | | Horizontal padding applied to left and right sides | | `py` | number | No | | Vertical padding applied to top and bottom sides | | `w` | DimensionValue | No | | Sets a specific width | | `h` | DimensionValue | No | | Sets a specific height | | `maxW` | DimensionValue | No | | Sets the maximum width | | `minW` | DimensionValue | No | | Sets the minimum width | | `minH` | DimensionValue | No | | Sets the minimum height | ## Examples ### Basic ID: `Select.basic` β€’ Tags: basic, label, placeholder, single β€’ Category: usage β€’ Status: stable β€’ Since: 1.0.0 Simple single-value select with helper copy and live selection feedback. ```tsx return ( ` β€” `default`, `filled`, `outline`, `unstyled` β€” and shares the underlying input styles, so the trigger reads consistently with text inputs in the same form. ```tsx const variants = [ { variant: 'default', label: 'Default' }, { variant: 'filled', label: 'Filled' }, { variant: 'outline', label: 'Outline' }, { variant: 'unstyled', label: 'Unstyled' }, ] as const const [value, setValue] = useState(null) return ( {variants.map(({ variant, label }) => ( setValue(selected as string)} renderOption={(option, active, selected) => { const { emoji, name, description } = option as DetailedSport return ( {emoji} {name} {description} {selected ? : null} ) }} /> ) } ``` ### Disabled states ID: `Select.disabled` β€’ Tags: disabled, options, state β€’ Category: states β€’ Status: stable β€’ Since: 1.0.0 Disable individual options or the full control to reflect availability. ```tsx // One option is taken out of play to show the per-option disabled state next to // the whole-field one. const options = sports.map((option) => option.value === 'basketball' ? { ...option, label: 'Basketball (disabled)', disabled: true } : option, ) const [value, setValue] = useState(sports[0].value) return ( ) } ``` ### Persistent menu ID: `Select.noCloseOnSelect` β€’ Tags: persistent, close-on-select, comparison β€’ Category: interaction β€’ Status: stable β€’ Since: 1.0.0 Keep the dropdown open after each choice for quick comparisons. ```tsx const [value, setValue] = useState(null) return (