# Platform Blocks UI Knowledge Base Generated: 2026-04-02T22:56:16.105Z ## Package Overview - Name: @platform-blocks/ui - Version: 0.8.2 - Description: A comprehensive React Native UI component library — 80+ accessible, themeable, cross-platform components for iOS, Android, and Web - Main: lib/cjs/index.js - Module: lib/esm/index.js - Types: lib/index.d.ts - React Native Entry: ./lib/esm/index.js ### Scripts (top commands) - build: npm run clean && rollup -c && npm run build:types - build:cjs: tsc -p tsconfig.cjs.json - build:esm: tsc -p tsconfig.esm.json - build:types: tsc -p tsconfig.json --emitDeclarationOnly - clean: rm -rf lib || true - format: eslint src --fix && echo 'Code formatted successfully!' - generate:llms: tsx ../../scripts/generate-llms.ts - lint: eslint src - lint:check: eslint src --max-warnings 0 - lint:fix: eslint src --fix - precommit: npm run lint:check && npm run test - prepare: sh -c 'if [ "$BUILD_ON_PREPARE" = "1" ]; then npm run build; else echo "ui: skipping build on prepare (set BUILD_ON_PREPARE=1 to enable)"; fi' - … 6 more ### Peer Dependencies - @react-native-masked-view/masked-view: >=0.3.0 - @shopify/flash-list: >=1.6.0 - expo-audio: >=1.0.0 - expo-document-picker: >=13.0.0 - expo-haptics: >=14.0.0 - expo-linear-gradient: >=12.0.0 - expo-status-bar: >=1.12.0 - react: >=18.0.0 - react-native: >=0.73.0 - react-native-reanimated: >=3.4.0 - react-native-reanimated-carousel: >=3.4.0 - react-native-safe-area-context: >=4.5.0 - … 3 more ### Dev Dependencies (not exhaustive) - @babel/core: ^7.28.0 - @babel/preset-env: ^7.28.0 - @babel/preset-flow: ^7.27.0 - @babel/preset-react: ^7.28.0 - @babel/preset-typescript: ^7.28.0 - @react-native-masked-view/masked-view: ^0.3.2 - @react-native/babel-preset: ^0.83.0 - @rollup/plugin-commonjs: ^29.0.0 - @rollup/plugin-json: ^6.1.0 - @rollup/plugin-node-resolve: ^16.0.0 - @rollup/plugin-typescript: ^12.3.0 - @shopify/flash-list: ^2.2.0 - … 32 more ## UI README

Platform Blocks logo

@platform-blocks/ui

[![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/joshstovall/platform-blocks/blob/HEAD/LICENSE) [![npm](https://img.shields.io/npm/v/@platform-blocks/ui)](https://www.npmjs.com/package/@platform-blocks/ui) [![Discord](https://img.shields.io/badge/Chat%20on-Discord-%235865f2)](https://discord.gg/kbHjwzgXbc)
A comprehensive React Native UI component library for building accessible, themeable, and cross-platform mobile and web applications. Part of the [Platform Blocks](https://platform-blocks.com/) ecosystem. ## Features - **80+ components** — Inputs, navigation, data display, overlays, media, and more - **Cross-platform** — iOS, Android, and Web from a single codebase - **Themeable** — Built-in light and dark themes with full customization via `createTheme` - **Accessible** — Screen reader, keyboard navigation, and RTL support out of the box - **Animated** — Smooth interactions powered by `react-native-reanimated` - **Haptics & sound** — Optional feedback via `expo-haptics` and `expo-audio` - **i18n ready** — Built-in internationalization with `I18nProvider` - **Tree-shakeable** — ESM and CJS builds with no side effects ## Installation ```bash npm install @platform-blocks/ui ``` ### Peer dependencies | Package | Version | | --- | --- | | `react` | `>=18.0.0 <20.0.0` | | `react-native` | `>=0.73.0` | | `react-native-reanimated` | `>=3.4.0` | | `react-native-safe-area-context` | `>=4.5.0` | | `react-native-svg` | `>=13.0.0` | ### Optional integrations These are lazily required and only needed when you use features that depend on them: `expo-audio` · `expo-document-picker` · `expo-haptics` · `expo-linear-gradient` · `expo-status-bar` · `react-native-worklets` · `react-syntax-highlighter` · `@react-native-masked-view/masked-view` · `@shopify/flash-list` · `react-native-reanimated-carousel` ## Quick start ```tsx import { PlatformBlocksProvider, Button } from '@platform-blocks/ui'; export function App() { return ( ); } ``` - Colors | (id: Button.colors) | tags: buttons, colors Cycle through semantic `colorVariant` values to align actions with brand intent. ```tsx import { Button, Column, Row } from '@platform-blocks/ui'; return ( ); } ``` ## Component: A versatile calendar component for selecting dates, months, and years with customizable styles and behaviors. ### Props `level` (CalendarLevel) [optional] View control `defaultLevel` (CalendarLevel) [optional, default: 'month'] `onLevelChange` ((level: CalendarLevel) => void) [optional] `date` (Date) [optional] Date management `defaultDate` (Date) [optional] `onDateChange` ((date: Date) => void) [optional] `value` (CalendarValue) [optional] Value handling (for selection) `onChange` ((value: CalendarValue) => void) [optional] `type` (CalendarType) [optional, default: 'single'] `minDate` (Date) [optional] Constraints `maxDate` (Date) [optional] `excludeDate` ((date: Date) => boolean) [optional] - … 12 more props documented ### Demos - Basic selection | (id: Calendar.basic) | tags: single, selection Bind `value` and `onChange` to local state to capture the selected day while `highlightToday` keeps the current date visually distinct. ```tsx import { useState } from 'react'; import { Calendar, Column, Text } from '@platform-blocks/ui'; const formatter = new Intl.DateTimeFormat('en-US', { dateStyle: 'medium' }); const [selectedDate, setSelectedDate] = useState(new Date()); return ( setSelectedDate(date as Date | null)} highlightToday /> Selected date: {selectedDate ? formatter.format(selectedDate) : 'none'} ); } ``` - Date constraints | (id: Calendar.constrained) | tags: min-date, max-date Set `minDate` and `maxDate` to keep navigation inside the current month while still allowing the calendar to show surrounding weeks. ```tsx import { useMemo, useState } from 'react'; import { Calendar, Column, Text } from '@platform-blocks/ui'; const monthFormatter = new Intl.DateTimeFormat('en-US', { month: 'long', year: 'numeric' }); const dateFormatter = new Intl.DateTimeFormat('en-US', { dateStyle: 'medium' }); const [selectedDate, setSelectedDate] = useState(new Date()); const { minDate, maxDate, monthLabel } = useMemo(() => { const today = new Date(); const start = new Date(today.getFullYear(), today.getMonth(), 1); const end = new Date(today.getFullYear(), today.getMonth() + 1, 0); return { minDate: start, maxDate: end, monthLabel: monthFormatter.format(start) }; }, []); return ( setSelectedDate(date as Date | null)} minDate={minDate} maxDate={maxDate} highlightToday /> Selected date: {selectedDate ? dateFormatter.format(selectedDate) : 'none'} Only dates in {monthLabel} are enabled. ); } ``` ## Component: Visualizes financial OHLC price movements over time. ### Props `series` (CandlestickSeries[]) [required] One or more candlestick series to render `barWidth` (number) [optional] Width of each candle body in pixels `gap` (number) [optional] Fractional gap between candles (0-1) `movingAveragePeriods` (number[]) [optional, default: []] Periods for moving average overlay lines (e.g. [20,50]) `movingAverageColors` (string[]) [optional, default: []] Colors for moving average overlays (falls back to series palette) `showMovingAverages` (boolean) [optional, default: true] Show moving average overlays (defaults true if periods provided) `showVolume` (boolean) [optional, default: false] Show volume bars underneath (reserved) `volumeHeightRatio` (number) [optional, default: 0.22] Relative height ratio for volume sub-chart (0-0.5) `xAxis` (ChartAxis) [optional] Configuration for the horizontal axis `yAxis` (ChartAxis) [optional] Configuration for the vertical axis `grid` (ChartGrid) [optional] Background grid configuration `legend` (ChartLegend) [optional] Legend display options - … 17 more props documented ### Demos - Basic | (id: CandlestickChart.basic) ```tsx import { CandlestickChart } from '../../'; const PRICE_SERIES = [ { x: new Date('2023-10-02'), open: 154, high: 158, low: 153, close: 157 }, { x: new Date('2023-10-03'), open: 157, high: 161, low: 156, close: 160 }, { x: new Date('2023-10-04'), open: 160, high: 164, low: 159, close: 162 }, { x: new Date('2023-10-05'), open: 162, high: 166, low: 161, close: 165 }, { x: new Date('2023-10-06'), open: 165, high: 168, low: 163, close: 164 }, { x: new Date('2023-10-09'), open: 164, high: 167, low: 162, close: 166 }, { x: new Date('2023-10-10'), open: 166, high: 170, low: 165, close: 169 }, { x: new Date('2023-10-11'), open: 169, high: 172, low: 167, close: 168 }, ]; return ( new Date(value).toLocaleDateString('en-US', { month: 'short', day: 'numeric', }), title: 'Trading day', }} yAxis={{ show: true, title: 'Price (USD)', labelFormatter: (value) => `$${value.toFixed(0)}`, }} grid={{ show: true, color: '#E5EAF7' }} legend={{ show: true }} tooltip={{ show: true, formatter: (candle) => `O: $${candle.open.toFixed(2)} • H: $${candle.high.toFixed(2)} • L: $${candle.low.toFixed(2)} • C: $${candle.close.toFixed(2)}`, }} enableCrosshair liveTooltip animation={{ duration: 400 }} enablePanZoom zoomMode="both" minZoom={0.2} resetOnDoubleTap clampToInitialDomain xScaleType="time" /> ); } ``` - Battery Material Pricing | (id: CandlestickChart.battery-material-pricing) ```tsx import { CandlestickChart } from '../../'; const LITHIUM_SERIES = [ { x: new Date('2024-09-02'), open: 36200, high: 37180, low: 35420, close: 36840 }, { x: new Date('2024-09-09'), open: 36840, high: 37510, low: 36080, close: 37200 }, { x: new Date('2024-09-16'), open: 37200, high: 37840, low: 36520, close: 36610 }, { x: new Date('2024-09-23'), open: 36610, high: 36950, low: 35840, close: 36020 }, { x: new Date('2024-09-30'), open: 36020, high: 36480, low: 35260, close: 35510 }, { x: new Date('2024-10-07'), open: 35510, high: 36040, low: 34800, close: 35160 }, { x: new Date('2024-10-14'), open: 35160, high: 35590, low: 34420, close: 34780 }, { x: new Date('2024-10-21'), open: 34780, high: 35200, low: 34060, close: 34920 }, ]; const NICKEL_SERIES = [ { x: new Date('2024-09-02'), open: 18940, high: 19480, low: 18620, close: 19260 }, { x: new Date('2024-09-09'), open: 19260, high: 19850, low: 19010, close: 19530 }, { x: new Date('2024-09-16'), open: 19530, high: 20040, low: 19300, close: 19780 }, { x: new Date('2024-09-23'), open: 19780, high: 20360, low: 19420, close: 19890 }, { x: new Date('2024-09-30'), open: 19890, high: 20540, low: 19610, close: 20330 }, { x: new Date('2024-10-07'), open: 20330, high: 20810, low: 20060, close: 20210 }, { x: new Date('2024-10-14'), open: 20210, high: 20640, low: 19880, close: 20140 }, { x: new Date('2024-10-21'), open: 20140, high: 20520, low: 19710, close: 19940 }, ]; const negotiationMarkers = [ { id: 'kickoff', shape: 'vertical-line' as const, x: new Date('2024-09-16').getTime(), color: '#f97316', opacity: 0.55, }, { id: 'conclusion', shape: 'vertical-line' as const, x: new Date('2024-10-14').getTime(), color: '#0ea5e9', opacity: 0.55, }, ]; const formatWeek = (value: number) => new Date(value).toLocaleDateString('en-US', { month: 'short', day: 'numeric', }); return ( `Open $${candle.open.toLocaleString('en-US')} • Close $${candle.close.toLocaleString('en-US')} \nRange $${candle.low.toLocaleString('en-US')} – $${candle.high.toLocaleString('en-US')}`, }} xAxis={{ show: true, title: 'Week of shipment', labelFormatter: formatWeek, }} yAxis={{ show: true, title: 'Spot price (USD per metric ton)', labelFormatter: (value) => `$${value.toLocaleString('en-US')}`, }} enableCrosshair liveTooltip multiTooltip xScaleType="time" /> ); } ``` ## Component: The Card component provides a flexible container for displaying content with consistent styling, padding, and visual hierarchy. ### Props `children` (React.ReactNode) [optional] children optional to reduce noisy TS errors during composition `variant` ('outline' | 'filled' | 'elevated' | 'subtle' | 'ghost' | 'gradient') [optional] `padding` (number) [optional] `style` (any) [optional] `onPress` (() => void) [optional] Interactive props `disabled` (boolean) [optional] `onContextMenu` ((e: any) => void) [optional] Web-only events passthrough ### Demos - Basics | (id: Card.basic) | tags: content Compose card content with spacing primitives and a primary action for quick scenarios. ```tsx import { Card, Column, Text, Button } from '@platform-blocks/ui'; return ( Upcoming match Falcons at Bears Kickoff is set for 7:30 PM with rain in the forecast. Review the lineup and travel logistics before departure. ); } ``` - Variants | (id: Card.variants) | tags: surface Tour the available card `variant` treatments to pick the right surface style for your layout. ```tsx import { Card, Column, Text } from '@platform-blocks/ui'; const VARIANTS = ['filled', 'outline', 'elevated', 'subtle', 'ghost', 'gradient'] as const; return ( {VARIANTS.map((variant) => ( {String(variant).toUpperCase()} variant Apply the {variant} treatment to match surface contrast needs. ))} ); } ``` ## Component: The Carousel component displays a series of content in a horizontal scrollable view with optional navigation dots and controls. ### Props `children` (React.ReactNode[]) [required] Array of carousel slide elements `orientation` ('horizontal' | 'vertical') [optional] Orientation of the carousel `showArrows` (boolean) [optional] Show navigation arrow buttons `showDots` (boolean) [optional] Show navigation dots `autoPlay` (boolean) [optional] Enable autoplay `autoPlayInterval` (number) [optional] Autoplay interval in ms `autoPlayPauseOnTouch` (boolean) [optional] Pause autoplay on user interaction `loop` (boolean) [optional] Enable looping `itemsPerPage` (number) [optional] Number of visible items per page `slidesToScroll` (number) [optional] Number of slides to advance per snap (defaults to itemsPerPage for backwards compatibility) `align` ('start' | 'center' | 'end') [optional] Align the visible slides within the viewport when there is extra space `containScroll` (false | 'trimSnaps' | 'keepSnaps') [optional] Contain leading/trailing space by trimming or keeping snap points - … 20 more props documented ### Demos - Basic usage | (id: Carousel.basic) | tags: auto-play, loop Enable `autoPlay` and `loop` on `Carousel` to rotate a small set of slides without custom pagination controls. ```tsx import { Block, Carousel, Text } from '@platform-blocks/ui'; return ( Weekly highlights Rotate through featured stories without building pagination controls. Product updates Enable `autoPlay` and `loop` to keep the carousel moving hands-free. Team spotlights Add a few slides to share wins, announcements, or campaign promos. ); } ``` - Vertical orientation | (id: Carousel.vertical) | tags: vertical, navigation Set `orientation="vertical"` to rotate content along the Y-axis while keeping arrow and dot controls aligned for keyboard and touch users. ```tsx import { Block, Carousel, Text } from '@platform-blocks/ui'; return ( Breaking news `orientation="vertical"` stacks stories where horizontal space is tight. Weather alerts Pair arrows and dots so keyboard and touch users find the controls. Schedule changes Slides rotate every few seconds but still allow manual scrolling. ); } ``` ## Component: The Checkbox component allows users to select one or more options from a set. Supports different states, colors, and group functionality. ### Props `checked` (boolean) [optional] Whether checkbox is checked `defaultChecked` (boolean) [optional, default: false] Initial checked value for uncontrolled usage `onChange` ((checked: boolean) => void) [optional] Change handler `indeterminate` (boolean) [optional, default: false] Indeterminate state for partial selections `color` (string) [optional] Custom color override `colorVariant` ('primary' | 'secondary' | 'success' | 'error' | 'warning') [optional, default: 'primary'] Named color variant from theme `size` (SizeValue) [optional, default: 'md'] Checkbox size `label` (React.ReactNode) [optional] Checkbox label `disabled` (boolean) [optional, default: false] Whether checkbox is disabled `required` (boolean) [optional, default: false] Whether checkbox is required `error` (string) [optional] Error message `description` (string) [optional] Helper text - … 4 more props documented ### Demos - Basics | (id: Checkbox.basic) | tags: checkboxes Controlled checkbox example with a helper message that reacts to user selection. ```tsx import { useState } from 'react'; import { Checkbox, Column, Text } from '@platform-blocks/ui'; const [checked, setChecked] = useState(false); return ( {checked ? 'Thanks! You can proceed to the next step.' : 'Check the box to continue.'} ); } ``` - Sizes | (id: Checkbox.sizes) | tags: checkboxes, sizes Explore available checkbox sizes with guidance on where each fits best. ```tsx import { useState } from 'react'; import { Checkbox, Column, Text } from '@platform-blocks/ui'; const SIZES = [ { key: 'xs', label: 'Extra small', description: 'Dense tables and compact lists.' }, { key: 'sm', label: 'Small', description: 'Standard data tables.' }, { key: 'md', label: 'Medium', description: 'Default for forms and settings.' }, { key: 'lg', label: 'Large', description: 'Touch-heavy or marketing layouts.' } ] as const; const [values, setValues] = useState>({ xs: false, sm: true, md: true, lg: false }); const toggle = (size: string) => { setValues((current) => ({ ...current, [size]: !current[size] })); }; return ( Size tokens {SIZES.map(({ key, label, description }) => ( toggle(key)} /> {description} ))} ); } ``` ## Component: The Chip component displays compact elements that represent an input, attribute, or action. Supports different colors, sizes, and interactive features like removal. ### Props `children` (React.ReactNode) [required] `size` (SizeValue) [optional, default: 'md'] `variant` ('filled' | 'outline' | 'light' | 'subtle' | 'gradient') [optional, default: 'filled'] `color` ('primary' | 'secondary' | 'success' | 'warning' | 'error' | 'gray' | string) [optional, default: 'primary'] `onPress` (() => void) [optional] `startIcon` (React.ReactNode) [optional] `endIcon` (React.ReactNode) [optional] `onRemove` (() => void) [optional] `removePosition` ('left' | 'right') [optional, default: 'right'] `disabled` (boolean) [optional, default: false] `style` (StyleProp) [optional] `textStyle` (StyleProp) [optional] - … 2 more props documented ### Demos - Core styles | (id: Chip.basic) | tags: variants, getting-started Toggle the `variant` prop between the default, `filled`, `outline`, `light`, and `subtle` styles when you need different emphasis levels for the same Chip content. ```tsx import { Chip, Row } from '@platform-blocks/ui' return ( Default Filled Outline Light Subtle ) } ``` - Semantic colors | (id: Chip.colors) | tags: colors, theming Map the `color` prop to semantic tokens like `primary`, `success`, `warning`, `error`, or `gray` so Chips inherit your design system palette without inline styles. ```tsx import { Chip, Row } from '@platform-blocks/ui' return ( Primary Success Warning Error Gray ) } ``` ## Component: The CodeBlock component renders source code with optional syntax highlighting, copy-to-clipboard, GitHub integration, line wrapping, and width controls (content-fit by default, with an opt-in full width mode). Set `wrap={false}` to disable… ### Props `language` (string) [optional, default: 'tsx'] Optional language for syntax highlighting `children` (string) [required] `title` (string) [optional] Optional title displayed above the code block `fileName` (string) [optional] Optional filename displayed next to the title `fileIcon` (React.ReactNode) [optional] Optional icon displayed next to the filename `showLineNumbers` (boolean) [optional, default: false] Show line numbers in the code block `highlight` (boolean) [optional, default: true] Enable syntax highlighting `fullWidth` (boolean) [optional, default: true] Make the code block take the full width of its container `showCopyButton` (boolean) [optional, default: true] Show a copy button to copy the code to clipboard `onCopy` ((code: string) => void) [optional] Callback when code is copied `style` (StyleProp) [optional] Custom styles for the code block container and text `textStyle` (StyleProp) [optional] Custom styles for the code text - … 10 more props documented ### Demos - Basic | (id: CodeBlock.basic) | tags: basic, code, snippet Default CodeBlock showing a single snippet with automatic language detection and copy controls. ```tsx import { CodeBlock, Column, Text } from '@platform-blocks/ui'; const sample = `import { View, Text } from 'react-native'; return ( Hello, World! ); }`; return ( Basic code block The default CodeBlock renders formatted code with copy support and automatic language detection. {sample} ); } ``` - Interactive | (id: CodeBlock.interactive) | tags: interactive, copy, events Custom onCopy handling with inline feedback and a manual trigger button. ```tsx import { useEffect, useRef, useState } from 'react'; import { Button, CodeBlock, Column, Text } from '@platform-blocks/ui'; const sampleCode = `const greeting = "Hello, World!"; console.log(greeting); // A simple function function add(a, b) { return a + b; } const result = add(5, 3); console.log(\`5 + 3 = \${result}\`);`; const [copiedLength, setCopiedLength] = useState(null); const timeoutRef = useRef | null>(null); useEffect(() => { return () => { if (timeoutRef.current) { clearTimeout(timeoutRef.current); } }; }, []); const handleCopy = (code: string) => { if (timeoutRef.current) { clearTimeout(timeoutRef.current); } setCopiedLength(code.length); timeoutRef.current = setTimeout(() => { setCopiedLength(null); timeoutRef.current = null; }, 2000); }; return ( Interactive copy Attach an onCopy handler to trigger custom feedback and reuse it outside the CodeBlock controls. {copiedLength !== null && ( Copied {copiedLength} characters to the clipboard. )} {sampleCode} This is some content inside the Collapse component. It will be shown or hidden based on the isCollapsed prop. ); } ``` ## Component: The ColorPicker component provides an intuitive interface for selecting colors through various input methods including color wheel, hex input, and preset swatches. ### Props `value` (string) [optional] Current color value in hex format (e.g., "#ff0000") `defaultValue` (string) [optional, default: ''] Default color value for uncontrolled usage `onChange` ((color: string) => void) [optional] Callback when color changes `label` (string) [optional] Label for the color picker `placeholder` (string) [optional, default: 'Select color'] Placeholder text when no color is selected `disabled` (boolean) [optional, default: false] Whether the picker is disabled `required` (boolean) [optional, default: false] Whether the picker is required `error` (string) [optional] Error message to display `description` (string) [optional] Help text to display below the picker `size` (ComponentSizeValue) [optional, default: 'md'] Size of the picker `variant` ('default' | 'filled' | 'unstyled') [optional, default: 'default'] Variant of the picker `radius` ('none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl') [optional, default: 'md'] Radius of the picker - … 20 more props documented ### Demos - Basic Usage | (id: ColorPicker.basic) Simple color picker with default settings. ```tsx import { useState } from 'react'; import { ColorPicker, Column, Text } from '@platform-blocks/ui'; const [color, setColor] = useState('#FF6B6B'); return ( Selected: {color || 'none'} ); } ``` - Custom Swatches | (id: ColorPicker.swatches) Color picker with custom swatch palettes. ```tsx import { useState } from 'react'; import { ColorPicker, Column, Text } from '@platform-blocks/ui'; const [color1, setColor1] = useState('#2196F3'); const [color2, setColor2] = useState('#4CAF50'); const [color3, setColor3] = useState('#FF9800'); const blueSwatches = [ '#E3F2FD', '#BBDEFB', '#90CAF9', '#64B5F6', '#42A5F5', '#2196F3', '#1E88E5', '#1976D2', '#1565C0', '#0D47A1', ]; const greenSwatches = [ '#E8F5E8', '#C8E6C9', '#A5D6A7', '#81C784', '#66BB6A', '#4CAF50', '#43A047', '#388E3C', '#2E7D32', '#1B5E20', ]; return ( Custom blue palette Custom green palette Without swatches ); } ``` ## Component: A simple square component for displaying colors, designed as a building block for color pickers and palette interfaces. ### Props `color` (string) [required] The color value to display (hex, rgb, hsl, etc.) `size` (number) [optional, default: 32] Size of the swatch in pixels `selected` (boolean) [optional, default: false] Whether the swatch is selected/active `disabled` (boolean) [optional, default: false] Whether the swatch is disabled `onPress` (() => void) [optional] Callback when swatch is pressed `showBorder` (boolean) [optional, default: true] Show a border around the swatch `borderColor` (string) [optional] Custom border color (defaults to theme color) `borderWidth` (number) [optional, default: 1] Border width in pixels `borderRadius` (number) [optional, default: 4] Border radius in pixels `showCheckmark` (boolean) [optional, default: true] Show a checkmark when selected `checkmarkColor` (string) [optional] Custom checkmark color `accessibilityLabel` (string) [optional] Accessibility label - … 1 more props documented ### Demos - Advanced | (id: ColorSwatch.advanced) ```tsx import React, { useState } from 'react'; import { Flex, Text, ColorSwatch } from '@platform-blocks/ui'; const [selectedColor, setSelectedColor] = useState('#E74C3C'); const colors = [ '#E74C3C', '#3498DB', '#2ECC71', '#F39C12', '#9B59B6', '#1ABC9C', '#34495E', '#95A5A6', '#E67E22', '#16A085' ]; return ( Advanced Color Swatches Custom styling options: {/* Rounded swatches */} setSelectedColor('#E74C3C')} /> Rounded {/* Square with thick border */} setSelectedColor('#3498DB')} /> Square {/* Custom border color */} setSelectedColor('#2ECC71')} /> Gold border {/* No checkmark */} setSelectedColor('#F39C12')} /> No checkmark {/* Custom checkmark color */} setSelectedColor('#9B59B6')} /> Gold check Color palette grid: {colors.map(color => ( setSelectedColor(color)} /> ))} Selected: {selectedColor} Grayscale palette: {[ '#000000', '#1a1a1a', '#333333', '#4d4d4d', '#666666', '#808080', '#999999', '#b3b3b3', '#cccccc', '#e6e6e6', '#ffffff' ].map(color => ( setSelectedColor(color)} /> ))} Large display swatch: {selectedColor} Click any swatch above to change ); } ``` - Basic | (id: ColorSwatch.basic) ```tsx import React, { useState } from 'react'; import { Flex, Text, ColorSwatch } from '@platform-blocks/ui'; const colors = [ '#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7', '#DDA0DD', '#98D8C8', '#F7DC6F', '#BB8FCE', '#85C1E9' ]; const [selectedColor, setSelectedColor] = useState('#FF6B6B'); return ( Basic Color Swatches Click to select a color: {colors.map(color => ( setSelectedColor(color)} /> ))} Selected: {selectedColor} Different sizes: Without borders: {colors.slice(0, 5).map(color => ( ))} Disabled state: {colors.slice(0, 3).map(color => ( ))} ); } ``` ## Component: Combines multiple chart types (e.g., bars and line) for richer comparison. ### Props `layers` (ComboChartLayer[]) [required] Ordered set of layers to render in the combo chart `enableCrosshair` (boolean) [optional, default: true] Enable crosshair indicator across layers `multiTooltip` (boolean) [optional, default: true] Enable multi-series tooltip aggregation `liveTooltip` (boolean) [optional, default: true] Follow pointer live with tooltip `xDomain` ([number, number]) [optional] Explicit override for the shared x-domain `yDomain` ([number, number]) [optional] Explicit override for the primary y-domain `yDomainRight` ([number, number]) [optional] Explicit override for the secondary y-domain `xAxis` (ChartAxis) [optional] Axis configuration for the shared x-axis `yAxis` (ChartAxis) [optional] Axis configuration for the primary y-axis `yAxisRight` (ChartAxis) [optional] Axis configuration for the secondary y-axis `grid` (ChartGrid) [optional] Grid line configuration `legend` (ChartLegend) [optional] Legend display options ### Demos - Basic | (id: ComboChart.basic) ```tsx import { ComboChart } from '../../'; const LAYERS = [ { type: 'bar' as const, id: 'revenue', name: 'Monthly revenue', data: [ { x: 1, y: 420 }, { x: 2, y: 455 }, { x: 3, y: 508 }, { x: 4, y: 480 }, { x: 5, y: 532 }, { x: 6, y: 575 }, ], color: '#4C6EF5', opacity: 0.85, }, { type: 'line' as const, id: 'active-users', name: 'Active users', targetAxis: 'right' as const, data: [ { x: 1, y: 110 }, { x: 2, y: 134 }, { x: 3, y: 149 }, { x: 4, y: 158 }, { x: 5, y: 166 }, { x: 6, y: 172 }, ], color: '#12B880', thickness: 3, showPoints: true, pointSize: 6, }, ]; return ( `M${value}`, }} yAxis={{ show: true, title: 'Revenue (USD thousands)', labelFormatter: (value) => `$${value}`, }} yAxisRight={{ show: true, title: 'Active users (thousands)', labelFormatter: (value) => `${value}k`, }} yDomain={[0, 650]} yDomainRight={[80, 200]} grid={{ show: true, color: '#E3E7F4', style: 'dashed' }} legend={{ show: true, position: 'bottom' }} /> ); } ``` ## Component: # CopyButton ### Props `value` (string) [required] The text to copy to clipboard `onCopy` ((value: string) => void) [optional] Callback fired after copy action `iconOnly` (boolean) [optional] If true, only the icon is shown (no button chrome or label) `label` (string) [optional] Accessible label for the button `toastTitle` (string) [optional] Title for the toast `toastMessage` (string) [optional] Detailed message for the toast `size` (ComponentSizeValue) [optional] Visual size token `style` (StyleProp) [optional] Style overrides for the button container `disableToast` (boolean) [optional] Disable the "copied to clipboard" toast `tooltip` (string) [optional] Tooltip text `tooltipPosition` ('top' | 'bottom' | 'left' | 'right') [optional] Tooltip position `mode` ('button' | 'icon') [optional] Presentation mode: default button (legacy) or bare icon - … 5 more props documented ### Demos - Basic | (id: CopyButton.basic) Icon-only copy button with default toast feedback. ```tsx import { CopyButton, Card, Flex, Text } from '@platform-blocks/ui'; return ( Invite Code: ABCD-1234 ); } ``` - Labeled | (id: CopyButton.labeled) Copy control showing label text instead of icon-only presentation. ```tsx import { CopyButton, Card, Flex, Text } from '@platform-blocks/ui'; return ( API Key sk_live_1a2b3c4d5e6f7g8h9i10 ); } ``` ## Component: The DataTable component provides a feature-rich interface for displaying tabular data with sorting, pagination, row selection, and customizable columns. ### Props `id` (string) [optional] Stable id for user preference persistence `data` (T[]) [required] Data rows `columns` (DataTableColumn[]) [required] Column definitions `loading` (boolean) [optional] Loading state `error` (string | null) [optional] Error message (when defined overrides table body) `emptyMessage` (string) [optional] Message to display when there is no data `searchable` (boolean) [optional] Enable global search input `searchPlaceholder` (string) [optional] Placeholder text for global search `searchValue` (string) [optional] Controlled global search value `onSearchChange` ((value: string) => void) [optional] Global search change handler `sortBy` (DataTableSort[]) [optional] Current sorting state `onSortChange` ((sort: DataTableSort[]) => void) [optional] Sorting change callback - … 66 more props documented ### Demos - Getting Started | (id: DataTable.basic) | tags: datatable Define columns, feed the dataset, and let `DataTable` handle search, sorting, and pagination out of the box. ```tsx import { useState } from 'react'; import { Column, DataTable, Text } from '@platform-blocks/ui'; import type { DataTableColumn, DataTablePagination, DataTableSort } from '@platform-blocks/ui'; type Employee = { id: number; name: string; email: string; role: string; status: 'active' | 'inactive' | 'pending'; department: string; }; const rows: Employee[] = [ { id: 1, name: 'Aria Chen', email: 'aria@example.com', role: 'Product Manager', status: 'active', department: 'Product' }, { id: 2, name: 'Ben Ortiz', email: 'ben@example.com', role: 'Engineer', status: 'active', department: 'Engineering' }, { id: 3, name: 'Carla Singh', email: 'carla@example.com', role: 'Designer', status: 'inactive', department: 'Design' }, { id: 4, name: 'Diego Price', email: 'diego@example.com', role: 'Support', status: 'pending', department: 'Operations' }, { id: 5, name: 'Elena Ruiz', email: 'elena@example.com', role: 'Engineer', status: 'active', department: 'Engineering' }, ]; const columns: DataTableColumn[] = [ { key: 'name', header: 'Name', accessor: 'name', sortable: true, cell: (value) => {value}, }, { key: 'email', header: 'Email', accessor: 'email', sortable: true, }, { key: 'role', header: 'Role', accessor: 'role', sortable: true, }, { key: 'status', header: 'Status', accessor: 'status', sortable: true, cell: (value: Employee['status']) => ( {value.toUpperCase()} ), }, { key: 'department', header: 'Department', accessor: 'department', sortable: true, }, ]; const [sortBy, setSortBy] = useState([]); const [pagination, setPagination] = useState({ page: 1, pageSize: 4, total: rows.length, }); return ( Sortable columns with built-in pagination and search ); } ``` - Column Filters | (id: DataTable.advanced-filtering) | tags: datatable, filters Toggle `filterable` on each column to surface context-aware controls and provide quick clearing for active filters. ```tsx import { useMemo, useState } from 'react'; import { Button, Column, DataTable, Row, Text } from '@platform-blocks/ui'; import type { DataTableColumn, DataTableFilter, DataTableSort } from '@platform-blocks/ui'; type Employee = { id: number; name: string; department: 'Engineering' | 'Design' | 'Marketing' | 'Sales'; status: 'active' | 'inactive'; salary: number; }; const rows: Employee[] = [ { id: 1, name: 'Avery Knight', department: 'Engineering', status: 'active', salary: 125_000 }, { id: 2, name: 'Bianca Hall', department: 'Design', status: 'active', salary: 98_500 }, { id: 3, name: 'Caleb Fox', department: 'Marketing', status: 'inactive', salary: 72_400 }, { id: 4, name: 'Dana Moss', department: 'Sales', status: 'active', salary: 88_100 }, { id: 5, name: 'Enzo Reed', department: 'Engineering', status: 'inactive', salary: 112_900 }, { id: 6, name: 'Farah Li', department: 'Sales', status: 'active', salary: 94_300 }, ]; const columns: DataTableColumn[] = [ { key: 'name', header: 'Name', accessor: 'name', sortable: true, filterable: true, filterType: 'text' }, { key: 'department', header: 'Department', accessor: 'department', sortable: true, filterable: true, filterType: 'select', filterOptions: [ { label: 'Engineering', value: 'Engineering' }, { label: 'Design', value: 'Design' }, { label: 'Marketing', value: 'Marketing' }, { label: 'Sales', value: 'Sales' }, ], }, { key: 'status', header: 'Status', accessor: 'status', sortable: true, filterable: true, filterType: 'select', filterOptions: [ { label: 'Active', value: 'active' }, { label: 'Inactive', value: 'inactive' }, ], }, { key: 'salary', header: 'Salary', accessor: 'salary', sortable: true, filterable: true, filterType: 'number', dataType: 'currency', align: 'right', }, ]; const [sortBy, setSortBy] = useState([]); const [filters, setFilters] = useState([]); const activeFilters = useMemo(() => filters.length, [filters]); return ( Enable column-level filters for text, select, or numeric operators {activeFilters ? `${activeFilters} filter${activeFilters > 1 ? 's' : ''} applied` : 'No active filters'} ); } ``` ## Component: --- ### Props `value` (CalendarValue) [optional] Selected value; type depends on `type` prop `defaultValue` (CalendarValue) [optional] Initial value for uncontrolled usage `onChange` ((value: CalendarValue) => void) [optional] Called when value changes `type` (CalendarType) [optional] Selection behavior `calendarProps` (Partial) [optional] Pass-through customization for underlying Calendar `style` (StyleProp) [optional] Optional container style for the inline calendar `testID` (string) [optional] Test identifier `accessibilityLabel` (string) [optional] Accessibility label for the inline calendar region `accessibilityHint` (string) [optional] Accessibility hint for the inline calendar region ### Demos - Basic Date Picker | (id: DatePicker.basic) | tags: basic, single-date, label, placeholder Standard single date selection with label and placeholder text. ```tsx import React, { useState } from 'react'; import { Column, DatePicker, Text } from '@platform-blocks/ui'; const [value, setValue] = useState(null); return ( setValue(next as Date | null)} calendarProps={{ numberOfMonths: 1, highlightToday: true }} /> {value ? `Selected: ${value.toLocaleDateString()}` : 'No date selected'} ); } ``` - Date Range Picker | (id: DatePicker.range) | tags: range, start-date, end-date, date-range Select a range of dates with start and end date selection. ```tsx import React, { useState } from 'react'; import { Column, DatePicker, Text } from '@platform-blocks/ui'; const [value, setValue] = useState<[Date | null, Date | null] | null>(null); const start = value?.[0]; const end = value?.[1]; return ( setValue(next as [Date | null, Date | null] | null)} calendarProps={{ numberOfMonths: 2, withCellSpacing: true }} /> {start && end ? `${start.toLocaleDateString()} – ${end.toLocaleDateString()}` : 'Select a start and end date'} ); } ``` ## Component: DatePickerInput wraps the inline `DatePicker` in an accessible input experience. It handles focus management, modal presentation, and formatting so people can choose dates without leaving the form flow. ### Props `value` (CalendarValue) [optional] Selected value; type depends on `type` prop `defaultValue` (CalendarValue) [optional] Initial value for uncontrolled usage `onChange` ((value: CalendarValue) => void) [optional] Called when value changes `type` (CalendarType) [optional] Selection behavior `calendarProps` (Partial) [optional] Pass-through customization for underlying Calendar `placeholder` (string) [optional] Input placeholder text `displayFormat` (string) [optional] Format string for displaying value in the input `valueFormat` (string) [optional] Serialization/parsing format (reserved for future) `clearable` (boolean) [optional] Show a clear button `size` (SizeValue) [optional] Visual size `disabled` (boolean) [optional] Disable interaction `withAsterisk` (boolean) [optional] Show required indicator - … 7 more props documented ### Demos - Basic | (id: DatePickerInput.basic) ```tsx import React, { useState } from 'react'; import { Column, DatePickerInput, Text } from '@platform-blocks/ui'; const [value, setValue] = useState(null); return ( setValue(next as Date | null)} placeholder="Select a date" label="Date" clearable fullWidth /> {value ? `Selected: ${value.toLocaleDateString()}` : 'No date selected'} ); } ``` - Multiple | (id: DatePickerInput.multiple) ```tsx import React, { useState } from 'react'; import { Column, DatePickerInput, Text } from '@platform-blocks/ui'; const [value, setValue] = useState([]); return ( setValue((next as Date[]) || [])} label="Multiple dates" placeholder="Select dates" fullWidth /> {value.length > 0 ? `Selected: ${value.map((date) => date.toLocaleDateString()).join(', ')}` : 'Select one or more dates'} ); } ``` ## Component: The Dialog component presents content above the app, supporting focus trapping, scroll locking, and multiple presentation styles (modal, confirmation, bottom sheet). ### Props `visible` (boolean) [required] Controls whether the dialog is visible. `variant` ('modal' | 'bottomsheet' | 'fullscreen') [optional] Presentation style of the dialog. `title` (string | null) [optional] Optional title text shown in the header area. `children` (ReactNode) [required] Dialog body content. `closable` (boolean) [optional] Allows the user to close the dialog via UI controls or escape/back. `backdrop` (boolean) [optional] Whether to render the dimming backdrop behind the dialog. `backdropClosable` (boolean) [optional] Whether tapping the backdrop should close the dialog. `shouldClose` (boolean) [optional] Triggers close animation when set to true. `onClose` (() => void) [optional] Called when the dialog requests to close. `w` (number) [optional] Optional explicit width for the dialog content (modal/bottomsheet). `h` (number) [optional] Optional explicit height for the dialog content. `radius` (number) [optional] Corner radius for the dialog container (bottom sheet rounds top corners only). - … 3 more props documented ### Demos - Basic Modal | (id: Dialog.basic) | tags: dialog, modal, actions Call `openDialog` with `variant: 'modal'` to show a titled dialog and wire action buttons to `closeDialog` when the user makes a choice. ```tsx import { Alert } from 'react-native'; import { Button, Column, Row, Text, useDialog } from '@platform-blocks/ui'; const { openDialog, closeDialog } = useDialog(); const showBasicDialog = () => { const dialogId = openDialog({ variant: 'modal', title: 'Basic Dialog', content: ( This is a basic modal dialog with theme-aware styling. Works in both light and dark mode. ) }); }; return ( ); } ``` - Bottom Sheet | (id: Dialog.bottomsheet) | tags: dialog, bottomsheet, gestures Switch the dialog `variant` to `'bottomsheet'` to get swipe-to-dismiss behavior and retain full control with `closeDialog` handlers. ```tsx import { Button, Column, Input, Text, useDialog } from '@platform-blocks/ui'; const { openDialog, closeDialog } = useDialog(); const showBottomSheetDialog = () => { const dialogId = openDialog({ variant: 'bottomsheet', title: 'Bottom Sheet with Gestures', content: ( This dialog slides up from the bottom with theme-aware styling. Drag the handle or surface to move it. Swipe down to dismiss with velocity thresholds. Rubber-band resistance keeps the sheet anchored. ) }); }; return ( ); } ``` ## Component: The Divider component provides a visual separator between content sections. Supports horizontal and vertical orientations, with optional labels. ### Props `orientation` (DividerOrientation) [optional, default: 'horizontal'] `variant` (DividerVariant) [optional, default: 'solid'] `color` (string) [optional] `colorVariant` ('primary' | 'secondary' | 'tertiary' | 'surface' | 'success' | 'warning' | 'error' | 'gray' | 'muted' | 'subtle') [optional] Semantic color variant (overrides color prop). Supports theme color palettes `size` (SizeValue | number) [optional, default: 1] `label` (React.ReactNode) [optional] `labelPosition` ('left' | 'center' | 'right') [optional, default: 'center'] `style` (StyleProp) [optional] `testID` (string) [optional] ### Demos - Basic Usage | (id: Divider.basic) | tags: solid, dashed Insert horizontal dividers between sections to separate content; switch the `variant` prop to toggle between solid and dashed lines. ```tsx import { Column, Divider, Text } from '@platform-blocks/ui'; 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: colorVariant, label, variant Select a `colorVariant` to match semantic palettes and combine it with `label` plus `variant` to fit your divider into content sections. ```tsx import type { DividerProps } from '@platform-blocks/ui'; import { Column, Divider, Text } from '@platform-blocks/ui'; const COLOR_VARIANTS: Array<{ label: string; tone?: DividerProps['colorVariant'] }> = [ { label: 'Surface (default)' }, { label: 'Primary', tone: 'primary' }, { label: 'Success', tone: 'success' }, { label: 'Warning', tone: 'warning' }, { label: 'Error', tone: 'error' }, { label: 'Muted', tone: 'muted' }, { label: 'Subtle', tone: 'subtle' }, { label: 'Gray', tone: 'gray' } ]; return ( Semantic color variants {COLOR_VARIANTS.map(({ label, tone }) => ( {label} ))} Labeled dividers Variant styles ); } ``` ## Component: Circular proportional chart with a hollow center (variant of pie chart). ### Props `data` (DonutChartDataPoint[]) [optional] Data points rendered in the donut `rings` (DonutChartRing[]) [optional] Optional multi-ring configuration. When provided, the top-level data acts as a fallback `size` (number) [optional, default: 280] Convenience size that sets both width and height when explicit values are omitted `innerRadiusRatio` (number) [optional, default: 0.55] Ratio (0-1) of the inner radius relative to the outer radius when thickness is not provided `thickness` (number) [optional] Explicit ring thickness override (outerRadius - innerRadius) `ringGap` (number) [optional] Gap in chart units between concentric rings (defaults to 8 for multi-ring charts) `padAngle` (number) [optional, default: 1.5] Padding between slices in degrees `startAngle` (number) [optional, default: -90] Starting angle for the first slice (degrees) `endAngle` (number) [optional, default: 270] Ending angle for the last slice (degrees) `primaryRingIndex` (number) [optional, default: 0] Index of the ring used for center totals and value formatting (defaults to 0) `legendRingIndex` (number) [optional] Index of the ring whose slices feed the legend (defaults to primaryRingIndex) `inheritColorByLabel` (boolean) [optional, default: true] When true, slices across rings reuse colors based on their label/id - … 11 more props documented ### Demos - Annual Expense Allocation | (id: DonutChart.annual-expense-allocation) ```tsx import { DonutChart } from '../../'; import type { DonutChartDataPoint } from '../../'; const DEPARTMENT_ALLOCATIONS: DonutChartDataPoint[] = [ { label: 'Product & Engineering', value: 42, color: '#5C7CFA' }, { label: 'Go-to-Market', value: 24, color: '#FF922B' }, { label: 'Customer Success', value: 14, color: '#20C997' }, { label: 'G&A', value: 9, color: '#748FFC' }, { label: 'R&D Partnerships', value: 7, color: '#F76707' }, { label: 'Workplace & Ops', value: 4, color: '#15AABF' }, ]; const formatBudget = (value: number) => `$${Intl.NumberFormat('en-US', { maximumFractionDigits: 0 }).format(value)}M`; return ( 'Budget'} centerSubLabel={() => 'Allocation by function'} centerValueFormatter={(value) => formatBudget(value)} /> ); } ``` - Basic | (id: DonutChart.basic) ```tsx import { DonutChart } from '../../'; const SEGMENTS = [ { label: 'Design', value: 28, color: '#845EF7' }, { label: 'Engineering', value: 42, color: '#20C997' }, { label: 'Marketing', value: 18, color: '#FF922B' }, { label: 'Support', value: 12, color: '#4C6EF5' }, ]; return ( ); } ``` ## Component: The EmojiPicker component ### Props `variant` (EmojiPickerVariant) [optional, default: 'quick'] Picker variant `value` (string) [optional] Controlled value (selected emoji) `onSelect` ((emoji: string) => void) [optional] Callback when emoji selected `disabled` (boolean) [optional] Whether picker is disabled `searchPlaceholder` (string) [optional, default: 'Search emoji…'] Placeholder text for search `emojis` ({ label: string; emoji: string }[]) [optional, default: [{ label: 'Thumbs Up'] Number of frequently used / quick emojis to show in quick variant `defaultOpened` (boolean) [optional] Default opened (uncontrolled) `onOpenChange` ((opened: boolean) => void) [optional] Callback when open state changes `onSearchChange` ((query: string) => void) [optional] Callback whenever search query changes `showBackground` (boolean) [optional, default: false] Show background for the whole quick variant component `testID` (string) [optional] Test id ### Demos - Quick | (id: EmojiPicker.quick) ```tsx import React, { useState } from 'react'; import { Flex, Text, EmojiPicker } from '@platform-blocks/ui'; const [reaction, setReaction] = useState('❤️'); return ( Quick Reaction Emojis {/* Basic without background */} Without background: {/* With background */} With background: {reaction && ( Current reaction: {reaction} )} ); } ``` ## Component: The FileInput component provides a user-friendly interface for file uploads with drag-and-drop functionality, file validation, and preview capabilities. ### Props `variant` ('standard' | 'dropzone' | 'compact') [optional] File input variant `accept` (string[]) [optional] Accepted file types (MIME types or extensions) `multiple` (boolean) [optional] Multiple file selection `maxSize` (number) [optional] Maximum file size in bytes `maxFiles` (number) [optional] Maximum number of files `onUpload` ((files: FileInputFile[]) => Promise) [optional] Upload handler `onProgress` ((fileId: string, progress: number) => void) [optional] Upload progress callback `onFilesChange` ((files: FileInputFile[]) => void) [optional] File change handler `onFileRemove` ((fileId: string) => void) [optional] File remove handler `PreviewComponent` (React.ComponentType<{ file: FileInputFile; onRemove: () => void }>) [optional] File preview component `children` (React.ReactNode) [optional] Custom drop zone content `showFileList` (boolean) [optional] Whether to show file list - … 13 more props documented ### Demos - Basic | (id: FileInput.basic) | tags: basic, upload, files Simple file input with helper text and multiple file selection. ```tsx import { useState } from 'react'; import { Column, FileInput, Text } from '@platform-blocks/ui'; import type { FileInputFile } from '@platform-blocks/ui'; 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 Drag-and-drop dropzone variant with native fallback instructions and selected file list. ```tsx import { useState } from 'react'; import { Platform } from 'react-native'; import { Column, FileInput, Text } from '@platform-blocks/ui'; import type { FileInputFile } from '@platform-blocks/ui'; 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} ))} )} ); } ``` ## Component: 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. ### Props `direction` ('row' | 'column' | 'row-reverse' | 'column-reverse') [optional] Flex direction `align` ('flex-start' | 'flex-end' | 'center' | 'stretch' | 'baseline') [optional] Align items on the cross axis `justify` ('flex-start' | 'flex-end' | 'center' | 'space-between' | 'space-around' | 'space-evenly') [optional] Justify content on the main axis `wrap` ('nowrap' | 'wrap' | 'wrap-reverse') [optional] Flex wrap `gap` (SizeValue) [optional] Gap between children (applies to both row and column gap) `rowGap` (SizeValue) [optional] Row gap between children `columnGap` (SizeValue) [optional] Column gap between children `grow` (number) [optional] Flex grow `shrink` (number) [optional] Flex shrink `basis` (DimensionValue) [optional] Flex basis `children` (React.ReactNode) [optional] Children elements `style` (ViewStyle) [optional] Custom styles - … 2 more props documented ### Demos - Align Items | (id: Flex.align) | tags: align, items, cross-axis, alignment, stretch Item alignment options along the cross axis (flex-start, center, stretch, etc.). ```tsx import { Flex, Card, Text, Column } from '@platform-blocks/ui'; return ( {[ { label: 'Start', value: 'flex-start' }, { label: 'Center', value: 'center' }, { label: 'End', value: 'flex-end' }, { label: 'Stretch', value: 'stretch' }, { label: 'Baseline', value: 'baseline' } ].map(({ label, value }) => ( align="{value}" {value === 'baseline' ? ( {/* Use Text of varying sizes directly to demonstrate baseline alignment */} 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 Simple flex container with three items and gap spacing. ```tsx import { Flex, Card, Text } from '@platform-blocks/ui'; return ( Item 1 Item 2 Item 3 ); } ``` ## Component: Shows progressive reduction of data through stages (conversion pipeline). ### Props `series` (FunnelChartSeries | FunnelChartSeries[]) [required] Funnel data series to render `layout` (FunnelLayoutConfig) [optional] Layout customization options `valueFormatter` (FunnelValueFormatter) [optional] Formatter for step values `tooltip` (ChartTooltip) [optional] Tooltip configuration `legend` (ChartLegend) [optional] Legend configuration `enableCrosshair` (boolean) [optional, default: true] Enable crosshair indicator `multiTooltip` (boolean) [optional, default: true] Enable aggregated tooltip for multiple series `liveTooltip` (boolean) [optional, default: false] Keep tooltip following the pointer `annotations` (any[]) [optional] Additional decorations or notes to render `accessibilityTable` (FunnelAccessibilityTableOptions) [optional] Render hidden accessibility table `onDataTable` ((payloads: FunnelDataTablePayload[]) => void) [optional] Callback invoked with data table payload ### Demos - Basic | (id: FunnelChart.basic) ```tsx import { FunnelChart } from '../../'; const SALES_FUNNEL = { id: 'pipeline', name: 'Q2 pipeline', steps: [ { label: 'Website visits', value: 32_500 }, { label: 'Sign-ups', value: 9_600 }, { label: 'Qualified leads', value: 4_350 }, { label: 'Trials started', value: 2_150 }, { label: 'Customers', value: 1_120 }, ], }; return ( { const label = index === 0 ? `${value.toLocaleString()} visitors` : `${value.toLocaleString()} leads`; const lines = [label]; if (context?.conversion != null) { lines.push(`Retention ${(context.conversion * 100).toFixed(1)}%`); } if (context?.dropValue && context.previousValue) { lines.push( `Drop ${(context.dropRate * 100).toFixed(1)}% (${context.dropValue.toLocaleString()} lost)` ); } return lines; }} legend={{ show: false }} multiTooltip enableCrosshair tooltip={{ show: true, formatter: (step) => `${step.label}: ${step.value.toLocaleString()}` }} /> ); } ``` - Data Pipeline Quality | (id: FunnelChart.data-pipeline-quality) ```tsx import { FunnelChart } from '../../'; type PipelineMeta = { note?: string; }; const PIPELINE_QUALITY = { id: 'data-quality-pipeline', name: 'Data quality checkpoints', steps: [ { label: 'Ingested records', value: 12_400_000, color: '#0f766e' }, { label: 'Schema validated', value: 11_760_000, color: '#0d9488', meta: { note: 'Dropped malformed partner feeds and null timestamps' } as PipelineMeta }, { label: 'Deduplicated', value: 11_180_000, color: '#14b8a6', meta: { note: 'UserId + sessionId key resolves campaign duplicates' } as PipelineMeta }, { label: 'Quality checks passed', value: 10_260_000, color: '#2dd4bf', meta: { note: 'Primary blockers: stale reference data & threshold breaches' } as PipelineMeta }, { label: 'Certified for dashboards', value: 9_940_000, color: '#5eead4', meta: { note: 'Ready for downstream activation' } as PipelineMeta }, ], }; return ( { const step = PIPELINE_QUALITY.steps[index]; const meta = step.meta as PipelineMeta | undefined; const lines: string[] = [`${value.toLocaleString()} rows`]; if (context?.previousValue) { const dropRate = (context.dropRate * 100).toFixed(1); const drop = context.dropValue.toLocaleString(); lines.push(`Filtered ${drop} rows (${dropRate}%)`); } lines.push(`Retention ${((context?.conversion ?? 1) * 100).toFixed(1)}% of ingest`); if (meta?.note) { lines.push(meta.note); } return lines; }} legend={{ show: false }} tooltip={{ show: true, formatter: (step) => { const idx = PIPELINE_QUALITY.steps.findIndex((candidate) => candidate.label === step.label); const previous = idx > 0 ? PIPELINE_QUALITY.steps[idx - 1] : undefined; const dropValue = previous ? previous.value - step.value : 0; const dropRate = previous && previous.value > 0 ? (dropValue / previous.value) * 100 : 0; const meta = step.meta as PipelineMeta | undefined; return [ step.label, `${step.value.toLocaleString()} rows`, previous ? `Filtered: ${dropValue.toLocaleString()} (${dropRate.toFixed(1)}%)` : 'Ingestion baseline', meta?.note, ] .filter(Boolean) .join('\n'); }, }} /> ); } ``` ## Component: The Gallery component displays a collection of images or media with thumbnail navigation, keyboard controls, and optional fullscreen/modal viewing. ### Props `images` (GalleryItem[]) [required] `initialIndex` (number) [optional] `onClose` (() => void) [optional] `onImageChange` ((index: number, image: GalleryItem) => void) [optional] `onDownload` ((image: GalleryItem) => void) [optional] `showMetadata` (boolean) [optional] `showThumbnails` (boolean) [optional] `showDownloadButton` (boolean) [optional] `allowKeyboardNavigation` (boolean) [optional] `allowSwipeNavigation` (boolean) [optional] `overlayOpacity` (number) [optional] `animationDuration` (number) [optional] ### Demos - Basic | (id: Gallery.basic) | tags: gallery, images, navigation, metadata Basic image gallery with navigation and metadata. ```tsx import { useState } from 'react'; import { View, TouchableOpacity, Image } from 'react-native'; import { Gallery, Button, Card, Text, Flex } from '@platform-blocks/ui'; import type { GalleryItem } from '../../types'; // Sample images for the gallery const SAMPLE_IMAGES: GalleryItem[] = [ { id: '1', uri: 'https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=800&h=600&fit=crop', title: 'Mountain Landscape', description: 'Beautiful mountain view with snow-capped peaks', metadata: { size: '2.4 MB', dimensions: { width: 1920, height: 1080 }, dateCreated: 'March 15, 2024', camera: 'Canon EOS R5', location: 'Swiss Alps', }, }, { id: '2', uri: 'https://images.unsplash.com/photo-1469474968028-56623f02e42e?w=800&h=600&fit=crop', title: 'Forest Path', description: 'A serene path through the forest', metadata: { size: '1.8 MB', dimensions: { width: 1600, height: 1200 }, dateCreated: 'April 2, 2024', camera: 'Sony A7 III', location: 'Pacific Northwest', }, }, { id: '3', uri: 'https://images.unsplash.com/photo-1441974231531-c6227db76b6e?w=800&h=600&fit=crop', title: 'Dense Forest', description: 'Tall trees reaching toward the sky', metadata: { size: '3.1 MB', dimensions: { width: 2048, height: 1536 }, dateCreated: 'May 18, 2024', camera: 'Nikon D850', location: 'Redwood National Park', }, }, ]; const [galleryVisible, setGalleryVisible] = useState(false); const [currentIndex, setCurrentIndex] = useState(0); const openGallery = (index: number = 0) => { setCurrentIndex(index); setGalleryVisible(true); }; const handleDownload = (image: GalleryItem) => { alert(`Downloading: ${image.title}`); }; return ( Gallery Demo Tap any image below to open the fullscreen gallery viewer. {SAMPLE_IMAGES.map((image, index) => ( openGallery(index)} > {image.title} ))} 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%', }, }); ``` ## Component: Visualize categorical mix alongside overall weight with a mosaic-style variable-width column chart. ### Props `data` (MarimekkoCategory[]) [required] Categories rendered as variable-width columns. `legend` (ChartLegend) [optional] Optional legend configuration. `xAxis` (ChartAxis) [optional] Optional x-axis configuration. `yAxis` (ChartAxis) [optional] Optional y-axis configuration (defaults to percentage scale). `grid` (ChartGrid) [optional] Grid configuration applied to the background. `columnGap` (number) [optional, default: 12] Gap (in pixels) inserted between columns. `segmentBorderRadius` (number) [optional, default: 2] Corner radius applied to each segment rectangle. `padding` ({ top: number; right: number; bottom: number; left: number }) [optional] Override padding around the chart plot area. `categoryLabelFormatter` ((category: MarimekkoCategory, index: number) => string) [optional] Formatter applied to categorical labels along the x-axis. ### Demos - Basic | (id: MarimekkoChart.basic) Variable-width columns reveal how each pipeline channel and region contributes to total qualified pipeline. ```tsx import { MarimekkoChart } from '../../'; const PIPELINE_COMPOSITION = [ { label: 'Inbound', segments: [ { label: 'North America', value: 52 }, { label: 'EMEA', value: 34 }, { label: 'APAC', value: 24 }, ], }, { label: 'Outbound', segments: [ { label: 'North America', value: 44 }, { label: 'EMEA', value: 28 }, { label: 'APAC', value: 18 }, ], }, { label: 'Partnerships', segments: [ { label: 'North America', value: 29 }, { label: 'EMEA', value: 22 }, { label: 'APAC', value: 15 }, ], }, { label: 'Expansion', segments: [ { label: 'North America', value: 37 }, { label: 'EMEA', value: 18 }, { label: 'APAC', value: 12 }, ], }, ]; return ( category.label} /> ); } ``` - Budget Allocation | (id: MarimekkoChart.budget-allocation) Lay out the company plan to see both department budgets and how each team invests within their allocation. ```tsx import { MarimekkoChart } from '../../'; const BUDGET_PLAN = [ { label: 'Growth', segments: [ { label: 'Paid media', value: 28 }, { label: 'Field marketing', value: 24 }, { label: 'Events', value: 18 }, { label: 'Advocacy', value: 12 }, ], }, { label: 'Product', segments: [ { label: 'Roadmap', value: 26 }, { label: 'Design', value: 14 }, { label: 'Research', value: 10 }, { label: 'Maintenance', value: 22 }, ], }, { label: 'Customer', segments: [ { label: 'Success', value: 18 }, { label: 'Support', value: 21 }, { label: 'Education', value: 9 }, { label: 'Community', value: 7 }, ], }, { label: 'Operations', segments: [ { label: 'Security', value: 15 }, { label: 'IT', value: 11 }, { label: 'Finance', value: 14 }, { label: 'People', value: 13 }, ], }, ]; return ( `${category.label} (${category.segments.reduce((sum, seg) => sum + seg.value, 0)}%)`} /> ); } ``` ## Component: 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. ### Props `children` (string) [required] `defaultCodeLanguage` (string) [optional] Override default code block language guess `maxHeadingLevel` (number) [optional] Max heading level to render (others downgraded) `allowHtml` (boolean) [optional] Whether to render inline HTML literally (ignored for now) `components` (Partial) [optional] Custom renderer overrides `onLinkPress` ((href: string) => void) [optional] Optional handler invoked when a markdown link is pressed ### Demos - Basic Usage | (id: Markdown.basic) Simple markdown rendering with headers, lists, and text formatting. ```tsx import { Column, Markdown, Text } from '@platform-blocks/ui'; 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) Markdown rendering with syntax-highlighted code blocks. ```tsx import { Column, Markdown, Text } from '@platform-blocks/ui'; 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 ); } ``` ## Component: 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 pro… ### Props `data` (MasonryItem[]) [required] Array of items to display in masonry layout `numColumns` (number) [optional] Number of columns (default: 2) `gap` (SizeValue) [optional] Spacing between items `optimizeItemArrangement` (boolean) [optional] Whether to optimize for staggered grid layout `renderItem` ((item: MasonryItem, index: number) => ReactNode) [optional] Custom item renderer - receives item and index `contentContainerStyle` (StyleProp) [optional] Content container style `style` (StyleProp) [optional] Custom styles `testID` (string) [optional] Test ID for testing `loading` (boolean) [optional] Loading state `emptyContent` (ReactNode) [optional] Empty state content `flashListProps` (Partial, 'data' | 'renderItem' | 'numColumns'>>) [optional] Flash list props to pass through `onEndReached` (((info: { distanceFromEnd: number }) => void) | null) [optional] Callback when the end of the list is reached (for pagination / infinite scroll) - … 10 more props documented ### Demos - Basic Masonry | (id: Masonry.basic) | tags: basic, layout, grid, columns, simple Simple masonry layout with uniform item heights arranged in a two-column grid. ```tsx import { Masonry } from '../../Masonry'; import type { MasonryItem } from '../../types'; import { Card } from '../../../Card'; import { Text } from '../../../Text'; import { useTheme } from '../../../../core/theme'; 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 Masonry layout with configurable number of columns demonstrating different grid arrangements. ```tsx import { Masonry } from '../../Masonry'; import type { MasonryItem } from '../../types'; import { Card } from '../../../Card'; import { Text } from '../../../Text'; import { Button } from '../../../Button'; import { Row } from '../../../Layout'; import { useTheme } from '../../../../core/theme'; import { useState } from 'react'; 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 Enable `trigger="contextmenu"` to surface a menu when users right-click or long-press a target. ```tsx import { Card, Column, Icon, Menu, MenuDivider, MenuDropdown, MenuItem, Text, } from '@platform-blocks/ui'; return ( Right-click or long-press this area }> Copy link }> Rename }> Share }> Delete ); } ``` ## Component: A button component that triggers a dropdown menu ### Props `title` (string) [optional] Text label (alternative to children) `children` (React.ReactNode) [optional] Custom content `startIcon` (React.ReactNode) [optional] Leading icon `endIcon` (React.ReactNode) [optional] Trailing icon / shortcut hint `onPress` (() => void) [optional] Click handler `disabled` (boolean) [optional] Whether the button is disabled `active` (boolean) [optional] Whether the button is active (selected) `danger` (boolean) [optional] Whether the button has destructive styling `fullWidth` (boolean) [optional] Whether the button should take up full width `size` (ComponentSizeValue) [optional] Size of the button `compact` (boolean) [optional] Whether to use compact styling `rounded` (boolean) [optional] Whether to use fully rounded corners - … 17 more props documented ### Demos - Basic Menu | (id: MenuItemButton.basic) Simple dropdown menu with icons and dividers. ```tsx import { Menu, MenuItem, MenuDivider, MenuDropdown, Button, Icon } from '@platform-blocks/ui'; return ( }> Profile }> Settings }> Help & Support }> Logout ) } ``` ## Component: A compact calendar component for displaying a month view with selectable dates. ### Demos - Basic MiniCalendar | (id: MiniCalendar.basic) Compact calendar showing a week view with date selection. ```tsx import React, { useState } from 'react'; import { Column, MiniCalendar, Text } from '@platform-blocks/ui'; const [selectedDate, setSelectedDate] = useState(new Date()); return ( setSelectedDate(date)} numberOfDays={7} /> {selectedDate ? `Selected: ${selectedDate.toLocaleDateString()}` : 'No date selected'} ); } ``` - Custom Day Count | (id: MiniCalendar.customDays) MiniCalendar with configurable number of days displayed. ```tsx import React, { useState } from 'react'; import { Button, Column, MiniCalendar, Row, Text } from '@platform-blocks/ui'; 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'} ); } ``` ## Component: 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. ### Props `value` (Date | null) [optional] Currently selected date (uses the first day of the month) `onChange` ((date: Date | null) => void) [optional] Called when user picks a new month `year` (number) [optional] Force a specific year to render `onYearChange` ((year: number) => void) [optional] Called when the visible year changes `minDate` (Date) [optional] Minimum selectable date (inclusive) `maxDate` (Date) [optional] Maximum selectable date (inclusive) `locale` (string) [optional] Locale used for month labels `size` (ComponentSizeValue) [optional] Size token that influences typography weight `monthLabelFormat` ('short' | 'long') [optional] Format of month labels `hideHeader` (boolean) [optional] Hide navigation header (used when embedded in Calendar) `monthsPerRow` (ResponsiveProp) [optional] Responsive override for the number of months rendered per row ### Demos - Basic | (id: MonthPicker.basic) ```tsx import React, { useState } from 'react'; import { Column, MonthPicker, Text } from '@platform-blocks/ui'; const [value, setValue] = useState(new Date()); return ( {value ? value.toLocaleDateString(undefined, { month: 'long', year: 'numeric' }) : 'No month selected'} ); } ``` ## Component: 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. ### Props `value` (Date | null) [optional] Controlled value for the selected month `defaultValue` (Date | null) [optional] Default month when uncontrolled `onChange` ((value: Date | null) => void) [optional] Called when the month selection changes `locale` (string) [optional] Locale used for formatting the input value `formatOptions` (Intl.DateTimeFormatOptions) [optional] Intl format options for rendering the selected month `formatValue` ((value: Date) => string) [optional] Custom formatter for the input value; overrides locale/formatOptions `placeholder` (string) [optional] Placeholder text when no month is selected `clearable` (boolean) [optional] Show a clear button when a month is selected `closeOnSelect` (boolean) [optional] Close the picker after selecting a month `monthPickerProps` (Partial>) [optional] Additional props forwarded to MonthPicker (except value) `modalTitle` (string) [optional] Dialog title text `onOpen` (() => void) [optional] Called when the picker dialog opens - … 1 more props documented ### Demos - Basic | (id: MonthPickerInput.basic) ```tsx import React, { useState } from 'react'; import { Column, MonthPickerInput, Text } from '@platform-blocks/ui'; const [value, setValue] = useState(null); return ( {value ? value.toLocaleDateString(undefined, { month: 'long', year: 'numeric' }) : 'No month selected'} ); } ``` ## Component: Graph structure visualization (nodes & links) for relationships. ### Props `nodes` (NetworkNode[]) [required] Nodes to render in the network `links` (NetworkLink[]) [required] Links connecting the nodes `layout` (NetworkLayoutMode) [optional, default: 'force'] Layout engine mode `grid` (ChartGrid | boolean) [optional] Optional grid configuration for coordinate layouts `xAxis` (ChartAxis) [optional] X axis configuration when using coordinate layouts `yAxis` (ChartAxis) [optional] Y axis configuration when using coordinate layouts `padding` (Partial<{ top: number; right: number; bottom: number; left: number }>) [optional] Optional padding overrides `coordinateAccessor` ({) [optional] Accessor overrides for coordinate layouts `x` ((node: NetworkNode, index: number) => number) [optional] `y` ((node: NetworkNode, index: number) => number) [optional] `showLabels` (boolean) [optional, default: true] Show node labels `nodeRadius` (number) [optional, default: 12] Radius override for nodes - … 14 more props documented ### Demos - Basic | (id: NetworkChart.basic) ```tsx import { NetworkChart } from '../../'; const NODES = [ { id: 'product', name: 'Product', group: 'teams', value: 12, color: '#4C6EF5' }, { id: 'design', name: 'Design', group: 'teams', value: 8, color: '#845EF7' }, { id: 'engineering', name: 'Engineering', group: 'teams', value: 18, color: '#20C997' }, { id: 'marketing', name: 'Marketing', group: 'teams', value: 10, color: '#FF922B' }, { id: 'sales', name: 'Sales', group: 'teams', value: 9, color: '#F03E3E' }, { id: 'support', name: 'Support', group: 'teams', value: 7, color: '#15AABF' }, { id: 'platform', name: 'Platform', group: 'initiatives', value: 15, color: '#2F9E44' }, { id: 'ai', name: 'AI', group: 'initiatives', value: 11, color: '#D9480F' }, ]; const LINKS = [ { source: 'platform', target: 'engineering', value: 6 }, { source: 'platform', target: 'product', value: 5 }, { source: 'platform', target: 'support', value: 2 }, { source: 'ai', target: 'product', value: 4 }, { source: 'ai', target: 'marketing', value: 3 }, { source: 'ai', target: 'sales', value: 2 }, { source: 'product', target: 'design', value: 7 }, { source: 'product', target: 'engineering', value: 8 }, { source: 'marketing', target: 'sales', value: 5 }, { source: 'support', target: 'sales', value: 4 }, ]; return ( ); } ``` - Customer Referral Network | (id: NetworkChart.customer-referral-network) ```tsx import { NetworkChart } from '../../'; import type { NetworkLink, NetworkNode } from '../../types'; const COHORTS: NetworkNode[] = [ { id: 'seed-advocates', name: 'Seed Advocates', group: 'seed', value: 38, color: '#12B886' }, { id: 'growth-us', name: 'Growth - US', group: 'growth', value: 44, color: '#228BE6' }, { id: 'growth-eu', name: 'Growth - EU', group: 'growth', value: 36, color: '#15AABF' }, { id: 'enterprise-wave', name: 'Enterprise Wave', group: 'enterprise', value: 29, color: '#F76707' }, { id: 'partner-ecosystem', name: 'Partner Ecosystem', group: 'partners', value: 24, color: '#845EF7' }, { id: 'freemium-community', name: 'Freemium Community', group: 'seed', value: 50, color: '#5C7CFA' }, { id: 'latam-expansion', name: 'LATAM Expansion', group: 'growth', value: 31, color: '#FF922B' }, ]; const REFERRALS: NetworkLink[] = [ { source: 'seed-advocates', target: 'freemium-community', weight: 6.4, meta: { wave: 1 } }, { source: 'seed-advocates', target: 'growth-us', weight: 4.7, meta: { wave: 1 } }, { source: 'freemium-community', target: 'growth-eu', weight: 3.8, meta: { wave: 2 } }, { source: 'growth-us', target: 'enterprise-wave', weight: 3.3, meta: { wave: 2 } }, { source: 'growth-eu', target: 'enterprise-wave', weight: 2.6, meta: { wave: 3 } }, { source: 'partner-ecosystem', target: 'enterprise-wave', weight: 3.9, meta: { wave: 1 } }, { source: 'partner-ecosystem', target: 'growth-us', weight: 2.4, meta: { wave: 2 } }, { source: 'latam-expansion', target: 'growth-eu', weight: 3.2, meta: { wave: 2 } }, { source: 'latam-expansion', target: 'freemium-community', weight: 2.7, meta: { wave: 3 } }, ]; const waveToColor = (wave?: number) => { if (wave === 1) return '#34C759'; if (wave === 2) return '#4DABF7'; if (wave === 3) return '#FF922B'; return '#ADB5BD'; }; const waveToOpacity = (wave?: number) => { if (wave === 1) return 0.7; if (wave === 2) return 0.6; if (wave === 3) return 0.55; return 0.45; }; return ( waveToColor(typeof link.meta?.wave === 'number' ? link.meta.wave : Number(link.meta?.wave))} linkOpacityAccessor={(link) => waveToOpacity(typeof link.meta?.wave === 'number' ? link.meta.wave : Number(link.meta?.wave))} /> ); } ``` ## Component: The Notice component displays important messages to users with different severity levels, variants, and optional actions like dismissal. ### Props `variant` (NoticeVariant) [optional, default: 'light'] `color` (NoticeColor | string) [optional, default: 'primary'] `sev` (NoticeSeverity) [optional] `title` (string) [optional] `children` (React.ReactNode) [optional] `icon` (React.ReactNode | string | null | false) [optional] `fullWidth` (boolean) [optional, default: false] `withCloseButton` (boolean) [optional, default: false] `closeButtonLabel` (string) [optional] `onClose` (() => void) [optional] `style` (StyleProp) [optional] `testID` (string) [optional] ### Demos - Basics | (id: Notice.basic) | tags: alerts Show the default alert alongside common status colors to communicate outcomes and guidance. ```tsx import type { ComponentProps } from 'react'; import { Notice, Column, Text } from '@platform-blocks/ui'; type NoticeExample = { key: string; title?: string; color?: ComponentProps['color']; message: string; }; const ALERTS: NoticeExample[] = [ { key: 'default', color: 'secondary', message: 'Use alerts to highlight contextual information inline with page content.' }, { key: 'info', title: 'Information', color: 'primary', message: 'Share helpful tips or next steps without interrupting the flow.' }, { key: 'success', title: 'Success', color: 'success', message: 'Confirm when actions complete successfully so users can continue.' }, { key: 'warning', title: 'Warning', color: 'warning', message: 'Draw attention to inputs that need review before progressing.' }, { key: 'error', title: 'Error', color: 'error', message: 'Describe blocking errors with guidance on how to resolve them.' } ]; return ( {ALERTS.map(({ key, title, color, message }) => ( {message} ))} Notices adapt their color palette to match the tone of the underlying message. ); } ``` - Variants | (id: Notice.variants) | tags: alerts, variants Compare light, outline, filled, and subtle variants to match alert prominence to the message. ```tsx import type { ComponentProps } from 'react'; import { Notice, Column, Text } from '@platform-blocks/ui'; type Variant = NonNullable['variant']>; const VARIANT_EXAMPLES: Array<{ variant: Variant; color: NonNullable['color']>; title: string; body: string; }> = [ { variant: 'light', color: 'primary', title: 'Light', body: 'Balanced background and border treatment best suited for inline notes.' }, { variant: 'outline', color: 'success', title: 'Outline', body: 'Use outline styling for subtle emphasis without increasing background contrast.' }, { variant: 'filled', color: 'warning', title: 'Filled', body: 'High-contrast option for urgent or high-visibility messaging.' }, { variant: 'subtle', color: 'error', title: 'Subtle', body: 'Removes background color but keeps iconography and text tinted.' } ]; return ( {VARIANT_EXAMPLES.map(({ variant, color, title, body }) => ( {body} ))} Choose a variant that matches the prominence you want without straying from the semantic color. ); } ``` ## Component: 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 for… ### Props `value` (number) [optional] Number value `onChange` ((value: number | undefined) => void) [optional] Change handler `allowDecimal` (boolean) [optional] Allow decimal values `allowNegative` (boolean) [optional, default: true] Allow negative values `allowLeadingZeros` (boolean) [optional, default: true] Allow leading zeros while editing `allowedDecimalSeparators` (string[]) [optional] Additional characters that should be treated as decimal separators `decimalSeparator` (string) [optional, default: DEFAULT_DECIMAL_SEPARATOR] Decimal separator character `decimalScale` (number) [optional] Maximum number of digits after the decimal point `fixedDecimalScale` (boolean) [optional, default: false] When true, pads the decimal part with trailing zeros to match decimalScale `min` (number) [optional] Minimum value `max` (number) [optional] Maximum value `step` (number) [optional, default: 1] Step increment - … 40 more props documented ### Demos - Basic | (id: NumberInput.basic) | tags: basic, numeric, step Controlled number input with simple step controls and live value preview. ```tsx import { useState } from 'react'; import { Column, NumberInput, Text } from '@platform-blocks/ui'; const [quantity, setQuantity] = useState(2); return ( Basic number input Controlled numeric field with increment buttons and a minimum of zero. Current value: {quantity ?? '—'} ); } ``` - Formats | (id: NumberInput.formats) | tags: currency, percent, formatting Showcases currency formatting, percentage suffixes, and a derived total. ```tsx import { useState } from 'react'; import { Column, NumberInput, Text } from '@platform-blocks/ui'; const [price, setPrice] = useState(249.99); const [discount, setDiscount] = useState(10); const finalPrice = price != null && discount != null ? price * (1 - discount / 100) : undefined; return ( Formatted values Pair currency formatting with percentage discounts to display a calculated total. Final price: {finalPrice != null ? `$${finalPrice.toFixed(2)}` : '—'} ); } ``` ## Component: 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 g… ### Props `color` (string) [optional] Background color for the overlay. Accepts raw colors or theme tokens like `primary.6`. `opacity` (number) [optional] Opacity applied to the background color. Defaults to 0.6. `backgroundOpacity` (number) [optional] Opacity applied to the entire overlay, including gradients and blur effects. Defaults to 1. `gradient` (string) [optional] Web-only CSS gradient string. Falls back to `color` on native platforms. `blur` (number | string) [optional] Amount of backdrop blur (in pixels). Supported on web. `radius` (SizeValue | number) [optional] Corner radius for the overlay surface. `zIndex` (number) [optional] z-index applied to the overlay container. `fixed` (boolean) [optional, default: false] Use viewport-fixed positioning instead of absolute positioning (web only). `center` (boolean) [optional, default: false] Center children horizontally and vertically. `style` (StyleProp) [optional] Optional style overrides applied after computed styles. `children` (ReactNode) [optional] Overlay content rendered on top of the dimmed background. ### Demos - Overlay patterns | (id: Overlay.basic) | tags: overlays, effects Showcases dimming, gradient, and blurred overlays that inherit their parent size for spotlights and modal scrims. ```tsx import { useState, type ComponentProps } from 'react'; import { ImageBackground, StyleSheet } from 'react-native'; import { Button, Column, Overlay, Text } from '@platform-blocks/ui'; const HERO_IMAGE = 'https://picsum.photos/800/450?random=8'; const GRADIENT_IMAGE = 'https://picsum.photos/800/450?random=2'; const BLUR_IMAGE = 'https://picsum.photos/800/450?random=10'; type OverlayExample = { key: string; image: string; 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, }, }); ``` ## Component: A comprehensive pagination component that provides intuitive navigation through large datasets. The component offers flexible configuration options and consistent styling across different use cases. ### Props `current` (number) [required] Current page number (1-indexed) `total` (number) [required] Total number of pages `siblings` (number) [optional, default: 1] Number of page items to show on each side of current page `boundaries` (number) [optional, default: 1] Number of page items to show at the boundaries `onChange` ((page: number) => void) [required] Page change handler `size` (ComponentSizeValue) [optional, default: 'md'] Size of pagination controls `variant` ('default' | 'outline' | 'subtle') [optional, default: 'default'] Variant style `color` ('primary' | 'secondary' | 'gray') [optional, default: 'primary'] Color scheme `showFirst` (boolean) [optional, default: true] Show first/last page buttons `showPrevNext` (boolean) [optional, default: true] Show previous/next buttons `labels` ({) [optional, default: {}] Custom labels for navigation buttons `first` (ReactNode) [optional] - … 16 more props documented ### Demos - Basic | (id: Pagination.basic) | tags: basic, pagination, navigation Provide `current`, `total`, and an `onChange` handler to keep numbered pagination in sync with surrounding state. ```tsx import { useState } from 'react'; import { Column, Pagination, Text } from '@platform-blocks/ui'; const [currentPage, setCurrentPage] = useState(1); const totalPages = 10; return ( Page {currentPage} of {totalPages} ); } ``` - Variants | (id: Pagination.variants) | tags: variants, style, appearance Switch the `variant` prop between `default`, `outline`, and `subtle` to align pagination with the surrounding surface treatment. ```tsx import { useState } from 'react'; import { Column, Pagination, Text } from '@platform-blocks/ui'; 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. ); } ``` ## Component: Highlight the small number of categories that drive the majority of impact with a bar plus cumulative line visualization. ### Props `data` (ParetoChartDatum[]) [required] Raw categories to render inside the Pareto analysis. `sortDirection` ('desc' | 'asc' | 'none') [optional, default: 'desc'] Sorting direction applied before calculating cumulative percentages. `valueSeriesLabel` (string) [optional, default: 'Frequency'] Display label for the bar series. `cumulativeSeriesLabel` (string) [optional, default: 'Cumulative %'] Display label for the cumulative line series. `barColor` (string) [optional] Base color used for the bar series when data points do not provide one. `lineColor` (string) [optional] Base color used for the cumulative line series. `categoryLabelFormatter` ((category: string, index: number) => string) [optional] Optional formatter applied to the categorical axis labels. ### Demos - Basic | (id: ParetoChart.basic) Simple Pareto chart showing how cumulative contribution highlights the dominant defect categories. ```tsx import { ParetoChart } from '../../'; const DEFECT_BREAKDOWN = [ { label: 'Authentication', value: 118 }, { label: 'Checkout', value: 96 }, { label: 'Notifications', value: 64 }, { label: 'Analytics', value: 42 }, { label: 'Billing', value: 31 }, { label: 'Integrations', value: 27 }, { label: 'Mobile', value: 19 }, { label: 'Reporting', value: 17 }, ]; return ( ); } ``` - Customer Support Hotspots | (id: ParetoChart.customer-support-hotspots) Highlight how a handful of ticket categories drive the majority of support backlog volume. ```tsx import { ParetoChart } from '../../'; const SUPPORT_CASES = [ { label: 'Login reset', value: 420 }, { label: 'Billing error', value: 318 }, { label: 'Delayed shipment', value: 247 }, { label: 'Missing items', value: 186 }, { label: 'Promo code', value: 132 }, { label: 'Damaged product', value: 108 }, { label: 'Account locked', value: 96 }, { label: 'Wrong item', value: 74 }, { label: 'Return label', value: 65 }, { label: 'Subscription cancel', value: 52 }, ]; return ( ); } ``` ## Component: The `PhoneInput` component provides a flexible way to capture telephone numbers with built-in masking and formatting. ### Props `value` (string) [optional, default: ''] Phone number value (digits only) `onChange` ((raw: string, formatted: string) => void) [optional] Change handler receiving (rawDigits, formattedDisplay) `country` (string) [optional, default: 'US'] Country code for format (US, CA, UK, FR, DE, AU, BR, IN, JP, INTL) `autoDetect` (boolean) [optional, default: true] Auto-detect format based on input `showCountryCode` (boolean) [optional, default: true] Show country code prefix in display `mask` (string) [optional] Custom mask pattern (overrides country-based mask). Use '0' for digits, other characters as literals ### Demos - Basic | (id: PhoneInput.basic) | tags: basic, phone, input Controlled PhoneInput example that surfaces both raw digits and the formatted display. ```tsx import { useState } from 'react'; import { Card, Code, Column, PhoneInput, Text } from '@platform-blocks/ui'; const [raw, setRaw] = useState(''); const [formatted, setFormatted] = useState(''); return ( Basic phone input Controlled phone field showing both the raw digits and the formatted display value. { setRaw(rawDigits); setFormatted(formattedDisplay); }} country="US" showCountryCode /> Current values {JSON.stringify({ raw, formatted }, null, 2)} ); } ``` - International | (id: PhoneInput.international) | tags: international, auto-detect, phone Compare auto-detected formats with a manual international configuration. ```tsx import { useState } from 'react'; import { Card, Code, Column, PhoneInput, Text } from '@platform-blocks/ui'; const [autoDetectValue, setAutoDetectValue] = useState(''); const [autoDetectFormatted, setAutoDetectFormatted] = useState(''); const [intlValue, setIntlValue] = useState(''); const [intlFormatted, setIntlFormatted] = useState(''); return ( International detection Compare auto-detected formats against a fully manual international mask. { setAutoDetectValue(raw); setAutoDetectFormatted(formatted); }} autoDetect showCountryCode placeholder="Try 5551234567, 447911123456, or 33123456789" /> { setIntlValue(raw); setIntlFormatted(formatted); }} autoDetect={false} showCountryCode={false} placeholder="Enter any international number" /> Values {JSON.stringify( { autoDetect: { raw: autoDetectValue, formatted: autoDetectFormatted }, international: { raw: intlValue, formatted: intlFormatted } }, null, 2 )} ); } ``` ## Component: Proportional slice chart for categorical part-to-whole representation. ### Props `data` (PieChartDataPoint[]) [required] Data points `innerRadius` (number) [optional] Inner radius (for donut chart) `outerRadius` (number) [optional] Outer radius `startAngle` (number) [optional] Start angle in degrees `endAngle` (number) [optional] End angle in degrees `padAngle` (number) [optional] Padding between slices `showLabels` (boolean) [optional] Show labels `labelPosition` ('inside' | 'outside' | 'center') [optional] Label position `labelStrategy` ('auto' | 'inside' | 'outside' | 'center') [optional] Automatically choose label placement `labelAutoSwitchAngle` (number) [optional] Minimum slice angle (deg) to keep label inside when strategy is auto `wrapLabels` (boolean) [optional] Automatically wrap labels that exceed width `labelMaxCharsPerLine` (number) [optional] Maximum characters per label line when wrapping - … 18 more props documented ### Demos - Basic | (id: PieChart.basic) ```tsx import { PieChart } from '../../'; const TRAFFIC_SOURCES = [ { id: 'direct', label: 'Direct', value: 55, color: '#4C6EF5' }, { id: 'organic', label: 'Organic', value: 25, color: '#51CF66' }, { id: 'referral', label: 'Referral', value: 15, color: '#FF922B' }, { id: 'social', label: 'Social', value: 5, color: '#845EF7' }, ]; return ( `${value}%`} legend={{ show: true, position: 'right' }} tooltip={{ show: true, formatter: (segment) => `${segment.label}: ${segment.value}%`, }} startAngle={-90} endAngle={270} /> ); } ``` - Browser Usage Share | (id: PieChart.browser-usage-share) ```tsx import { PieChart, type PieChartDataPoint } from '../../'; const BROWSER_USAGE = [ { id: 'chrome', label: 'Chrome', value: 52, color: '#5C7CFA' }, { id: 'safari', label: 'Safari', value: 28, color: '#38D9A9' }, { id: 'edge', label: 'Edge', value: 11, color: '#FF922B' }, { id: 'firefox', label: 'Firefox', value: 6, color: '#BE4BDB' }, { id: 'other', label: 'Other', value: 3, color: '#ADB5BD' }, ]; const formatLabel = (slice: PieChartDataPoint) => `${slice.label} ${slice.value}%`; const formatTooltip = (slice: PieChartDataPoint) => `${slice.label}: ${slice.value}% of sessions`; return ( ); } ``` ## Component: 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. ### Props `length` (number) [optional, default: 4] Number of PIN digits `keyboardFocusId` (string) [optional] Stable id used by KeyboardManager to restore focus `value` (string) [optional, default: ''] PIN value `onChange` ((pin: string) => void) [optional] Change handler `mask` (boolean) [optional, default: false] Whether to mask PIN `maskChar` (string) [optional, default: '•'] Character to use for masking `manageFocus` (boolean) [optional, default: true] Whether to focus next input automatically `enforceOrderInitialOnly` (boolean) [optional] Enforce sequential entry (forces focus to first empty). If false, user can edit any position after complete `type` ('alphanumeric' | 'numeric') [optional, default: 'numeric'] Type of input `placeholder` (string) [optional, default: ''] Placeholder for each input `allowPaste` (boolean) [optional, default: true] Whether to allow paste `oneTimeCode` (boolean) [optional, default: false] One-time code auto-complete - … 13 more props documented ### Demos - Basic | (id: PinInput.basic) | tags: basic, pin, code Controlled 4-digit PIN input with automatic focus handoff and live preview. ```tsx import { useState } from 'react'; import { Column, PinInput, Text } from '@platform-blocks/ui'; const [value, setValue] = useState(''); return ( Basic PIN input Controlled 4-digit PIN field with automatic focus management. {value ? ( Current value: {value} ) : null} ); } ``` - Types | (id: PinInput.types) | tags: types, numeric, alphanumeric Contrast numeric-only PIN entry with an alphanumeric option for recovery codes. ```tsx import { useState } from 'react'; import { Column, PinInput, Text } from '@platform-blocks/ui'; 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. ); } ``` ## Component: 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. ### Props `children` (ReactNode) [required] `opened` (boolean) [optional] Controlled open state `defaultOpened` (boolean) [optional, default: false] Initial open state in uncontrolled mode `onChange` ((opened: boolean) => void) [optional] Called when open state changes `onOpen` (() => void) [optional] Called when popover opens `onClose` (() => void) [optional] Called when popover closes `onDismiss` (() => void) [optional] Called when popover is dismissed via outside click or escape `trigger` ('click' | 'hover') [optional, default: 'click'] How the popover is triggered: 'click' (default) or 'hover' (mostly useful for devices with a mouse) `disabled` (boolean) [optional, default: false] Disable popover entirely `closeOnClickOutside` (boolean) [optional, default: true] Close when clicking outside `closeOnEscape` (boolean) [optional, default: true] Close when pressing Escape `clickOutsideEvents` (string[]) [optional] Events considered for outside click detection (web only) - … 33 more props documented ### Demos - Basic Usage | (id: Popover.basic) | tags: popover Popover targets wrap an interactive element and render dropdown content within `Popover.Dropdown`. ```tsx import { Button, Card, Column, Popover, Text } from '@platform-blocks/ui'; return ( Quick actions Popovers expose more content than tooltips without leaving the page. ); } ``` - Hover Trigger | (id: Popover.hover) | tags: popover, hover, trigger 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 import { Button, Card, Column, Popover, Text } from '@platform-blocks/ui'; return ( Hover popover This popover opens on hover, ideal for mouse users who want quick access to additional content. ); } ``` ## Component: The Progress component displays the completion progress of a task or process. Supports different variants, colors, and animations. ### Props `value` (number) [required] 0-100 `size` (SizeValue) [optional, default: 'md'] `color` (ProgressColor | string) [optional, default: 'primary'] `radius` (SizeValue) [optional, default: 'md'] `striped` (boolean) [optional, default: false] `animate` (boolean) [optional, default: false] `transitionDuration` (number) [optional, default: 0] ms `fullWidth` (boolean) [optional] `style` (StyleProp) [optional] `testID` (string) [optional] ### Demos - Basics | (id: Progress.basic) | tags: progress Track a single completion percentage, adjust it with controls, and embed the bar inside constrained layouts. ```tsx import { useState } from 'react'; import { Block, Button, Column, Progress, Row, Text } from '@platform-blocks/ui'; const STEP = 10; const [completion, setCompletion] = useState(60); const handleAdjust = (delta: number) => { setCompletion((value) => Math.min(100, Math.max(0, value + delta))); }; return ( Scouting report completion {completion}% of checklist items submitted. Embed inside constrained layouts ); } ``` - Variants | (id: Progress.variants) | tags: color, stripe Compare default, semantic, and striped progress states to match the tone of your status updates. ```tsx import { Column, Progress, Text } from '@platform-blocks/ui'; const VARIANTS = [ { label: 'Primary status', value: 60, props: {} }, { label: 'Success status', value: 82, props: { color: 'success' as const } }, { label: 'Warning status', value: 45, props: { color: 'warning' as const } }, { label: 'Striped + animated', value: 70, props: { striped: true, animate: true, transitionDuration: 600 } } ] as const; return ( {VARIANTS.map(({ label, value, props }) => ( {label} ))} ); } ``` ## Component: 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. ### Props `value` (string) [required] The data/text to encode in the QR code `size` (number) [optional] Size of the QR code (both width and height) `backgroundColor` (string) [optional] Background color of the QR code `color` (string) [optional] Foreground color (the QR code pattern color) `moduleShape` ('square' | 'rounded' | 'diamond') [optional] Module shape variant for data modules. Note: Finder patterns (corner anchors) always remain square for optimal scanner compatibility. `finderShape` ('square' | 'rounded') [optional] Corner (finder) shape variant - DEPRECATED: Finder patterns always remain square `cornerRadius` (number) [optional] Rounded corner radius factor (0-1) applied when moduleShape='rounded' `gradient` ({) [optional] Gradient fill (overrides color) `type` ('linear' | 'radial') [optional] `from` (string) [required] Start color `to` (string) [required] End color `rotation` (number) [optional] (linear) rotation deg (0=left->right) - … 16 more props documented ### Demos - Basics | (id: QRCode.basic) | tags: qr-code Render a single QR code for a link or payload and provide helper text for scanning context. ```tsx import { Column, QRCode, Text } from '@platform-blocks/ui'; return ( Scan to open the Platform Blocks docs. ); } ``` - Sizes | (id: QRCode.sizes) | tags: size Preview recommended QR code dimensions to pick the right footprint for your canvas. ```tsx import { Column, QRCode, Row, Text } from '@platform-blocks/ui'; const SIZES = [128, 152, 176, 200] as const; return ( Available size presets {SIZES.map((size) => ( {size}px ))} ); } ``` ## Component: Displays multivariate data across axes starting from the same origin. ### Props `series` (RadarChartSeries[]) [required] Radar series to display `maxValue` (number) [optional] Maximum value displayed across axes `radialGrid` (RadarGridConfig) [optional] Grid styling configuration `smooth` (boolean | number) [optional] Smooth the polygon edges; pass a number between 0 and 1 to control tension `fill` (boolean) [optional, default: true] Fill the radar area `colorScheme` (string) [optional] Color palette identifier `enableCrosshair` (boolean) [optional] Enable radial crosshair highlights `multiTooltip` (boolean) [optional] Enable multi-series tooltip aggregation `liveTooltip` (boolean) [optional] Follow pointer with tooltip `legend` (ChartLegend) [optional] Legend configuration `tooltip` (ChartTooltip) [optional] Tooltip configuration `annotations` (any[]) [optional] Additional annotations to render ### Demos - Skill Comparison | (id: RadarChart.skill-comparison) Comparison of three engineering guilds with polygon grid, crosshair, and aggregated tooltip. ```tsx import { RadarChart } from '../../'; const AXES = [ 'Code quality', 'Delivery speed', 'Testing coverage', 'Observability', 'Collaboration', 'Innovation', ]; const buildSeriesData = (values: number[]) => AXES.map((axis, index) => ({ axis, value: values[index] })); const SERIES = [ { id: 'frontend-guild', name: 'Frontend guild', color: 'rgba(59, 130, 246, 0.6)', showPoints: true, pointSize: 4, data: buildSeriesData([92, 84, 78, 86, 90, 74]), }, { id: 'platform-guild', name: 'Platform guild', color: 'rgba(16, 185, 129, 0.6)', showPoints: true, pointSize: 4, data: buildSeriesData([88, 79, 91, 93, 82, 70]), }, { id: 'qa-guild', name: 'QA guild', color: 'rgba(249, 115, 22, 0.6)', showPoints: true, pointSize: 4, data: buildSeriesData([80, 72, 95, 88, 76, 68]), }, ]; return ( `${point.axis}: ${Math.round(point.value)}%`, }} /> ); } ``` - Product Health | (id: RadarChart.product-health) Product health snapshot with circular grid, point markers, and custom tooltip messaging. ```tsx import { RadarChart } from '../../'; const AXES = [ 'Availability', 'Latency', 'NPS', 'Feature velocity', 'Security posture', 'Cost efficiency', ]; const makeSeriesData = (values: number[]) => AXES.map((axis, index) => ({ axis, value: values[index] })); const SERIES = [ { id: 'current-health', name: 'Current health', color: 'rgba(14, 165, 233, 0.55)', showPoints: true, pointSize: 3, data: makeSeriesData([8.4, 7.2, 6.8, 7.5, 8.8, 6.2]), }, { id: 'target-health', name: 'Target health', color: 'rgba(59, 130, 246, 0.45)', showPoints: true, pointSize: 3, data: makeSeriesData([9.2, 8.6, 8.1, 8.5, 9.0, 7.5]), }, ]; return ( `${point.axis}: ${point.value.toFixed(1)} / 10`, }} /> ); } ``` ## Component: Circular bar segments representing values radially. ### Props `data` (RadialBarDatum[]) [required] Radial bar data to render `radius` (number) [optional] Radius of outer ring (auto if not provided) `barThickness` (number) [optional, default: 14] Thickness of each arc `gap` (number) [optional, default: 8] Gap (px) between concentric bars `minAngle` (number) [optional, default: 0] Minimum angle span percentage (helps show tiny values) `startAngle` (number) [optional, default: -90] Start angle in degrees (default -90 = top) `endAngle` (number) [optional, default: 270] End angle in degrees (default 270 for full circle) `animate` (boolean) [optional, default: true] Animate value growth `showValueLabels` (boolean) [optional, default: true] Show value labels inside arcs `valueFormatter` ((value: number, datum: RadialBarDatum, index: number) => string) [optional] Format value for label `multiTooltip` (boolean) [optional, default: true] Enable aggregated tooltip across arcs `liveTooltip` (boolean) [optional, default: true] Keep tooltip following the pointer - … 3 more props documented ### Demos - Basic | (id: RadialBarChart.basic) ```tsx import { RadialBarChart } from '../../'; const METRICS = [ { id: 'uptime', label: 'Uptime', value: 99, max: 100, color: '#4C6EF5', trackColor: '#E3E9FF' }, { id: 'nps', label: 'NPS', value: 72, max: 100, color: '#20C997', trackColor: '#DEF7EE' }, { id: 'retention', label: 'Retention', value: 86, max: 100, color: '#FF922B', trackColor: '#FFE8D7' }, { id: 'sla', label: 'SLA', value: 94, max: 100, color: '#845EF7', trackColor: '#EFE6FF' }, ]; return ( `${value}% ${datum.label}`} multiTooltip liveTooltip legend={{ show: true, position: 'bottom' }} /> ); } ``` ## Component: Radio buttons allow users to select a single option from a group of mutually exclusive choices. ### Props `value` (string) [required] Radio value `checked` (boolean) [optional, default: false] Whether radio is selected `onChange` ((value: string) => void) [optional] Change handler `name` (string) [optional] Radio group name `size` (SizeValue) [optional, default: 'md'] Radio size `color` (ColorValue) [optional, default: 'primary'] Radio color theme `label` (React.ReactNode) [optional] Radio label `disabled` (boolean) [optional, default: false] Whether radio is disabled `required` (boolean) [optional, default: false] Whether radio is required `error` (string) [optional] Error message `description` (string) [optional] Helper text `labelPosition` ('left' | 'right') [optional, default: 'right'] Label position relative to radio - … 3 more props documented ### Demos - Basic Usage | (id: Radio.basic) | tags: Radio, RadioGroup Use standalone `Radio` components for custom layouts or pass an `options` array to `RadioGroup` for quick single-selection forms. ```tsx import { useState } from 'react'; import { Column, Radio, RadioGroup, Text } from '@platform-blocks/ui'; 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: size, color, state Combine the `size`, `color`, and validation props to align radios with your UI tokens and state requirements. ```tsx import { useState } from 'react'; import { Column, Radio, RadioGroup, Text } from '@platform-blocks/ui'; 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 ); } ``` ## Component: An interactive component for displaying star ratings and allowing users to provide ratings with customizable appearance. ### Props `value` (number) [optional] Current rating value `defaultValue` (number) [optional] Initial rating value for uncontrolled component `count` (number) [optional] `readOnly` (boolean) [optional] `allowFraction` (boolean) [optional] `allowHalf` (boolean) [optional] Deprecated `precision` (number) [optional] `size` (SizeValue | number) [optional] `color` (string) [optional] `emptyColor` (string) [optional] `hoverColor` (string) [optional] `onChange` ((value: number) => void) [optional] - … 12 more props documented ### Demos - Basics | (id: Rating.basic) | tags: interactive Capture a single rating value with an interactive control and mirror the current score in helper text. ```tsx import { useState } from 'react'; import { Column, Rating, Text } from '@platform-blocks/ui'; const [score, setScore] = useState(3); return ( Current score: {score} out of 5. ); } ``` - Sizes | (id: Rating.sizes) | tags: size Compare the available `size` tokens side by side to pick the right scale for your scene. ```tsx import { Rating, Block } from '@platform-blocks/ui'; return ( ) } ``` ## Component: Layered density plots (joyplot) comparing distributions across categories. ### Props `series` (DensitySeries[]) [required] Density series rendered in the ridge chart `samples` (number) [optional] Density sample resolution `bandwidth` (number) [optional] Kernel bandwidth override `bandPadding` (number) [optional] Fractional padding between ridge bands (0 - 0.8) `amplitudeScale` (number) [optional] Amplitude scaling applied to each ridge (0.1 - 1) `xAxis` (ChartAxis) [optional] X-axis configuration `yAxis` (ChartAxis) [optional] Y-axis configuration `grid` (ChartGrid) [optional] Grid line configuration `statsMarkers` (RidgeStatsMarkersConfig) [optional] Optional statistic marker configuration ### Demos - Api Latency Endpoints | (id: RidgeChart.api-latency-endpoints) ```tsx import { RidgeChart } from '../../'; const SAMPLE_POINTS = 170; const createLatencyProfile = (base: number, jitter: number, tail: number) => Array.from({ length: SAMPLE_POINTS }, (_, index) => { const diurnal = Math.sin(index / 7) * jitter; const deployWave = Math.max(0, Math.sin((index - 28) / 16)) * tail; const background = Math.cos(index / 4.5) * jitter * 0.3; const heavyTail = Math.pow(Math.sin((index + 12) / 40), 6) * tail * 1.8; const value = base + diurnal + deployWave + background + heavyTail; return Number(Math.max(48, value).toFixed(1)); }); const formatLatency = (value: number) => `${Math.round(value)} ms`; const latencyTooltip = ({ value, density, series }: any) => { const p90 = series?.stats?.p90; const p90Label = p90 != null ? ` • p90 ${formatLatency(p90)}` : ''; return `${formatLatency(value)} • density ${(density * 100).toFixed(1)}%${p90Label}`; }; const SERIES = [ { id: 'projects', name: 'GET /projects', color: '#6366f1', values: createLatencyProfile(210, 48, 160), fillOpacity: 0.64, strokeWidth: 1.2, valueFormatter: formatLatency, tooltipFormatter: latencyTooltip, }, { id: 'reports', name: 'GET /reports', color: '#f43f5e', values: createLatencyProfile(260, 52, 210), fillOpacity: 0.64, strokeWidth: 1.2, valueFormatter: formatLatency, tooltipFormatter: latencyTooltip, }, { id: 'ingest', name: 'POST /ingest', color: '#0ea5e9', values: createLatencyProfile(340, 60, 280), fillOpacity: 0.64, strokeWidth: 1.2, valueFormatter: formatLatency, tooltipFormatter: latencyTooltip, }, { id: 'search', name: 'GET /search', color: '#facc15', values: createLatencyProfile(180, 44, 150), fillOpacity: 0.64, strokeWidth: 1.2, valueFormatter: formatLatency, tooltipFormatter: latencyTooltip, }, { id: 'billing', name: 'POST /billing', color: '#22c55e', values: createLatencyProfile(400, 66, 320), fillOpacity: 0.64, strokeWidth: 1.2, valueFormatter: formatLatency, tooltipFormatter: latencyTooltip, }, ]; return ( formatLatency(value as number), }} yAxis={{ show: true, tickLength: 6, }} /> ); } ``` - Basic | (id: RidgeChart.basic) ```tsx import { RidgeChart } from '../../'; const SERIES = [ { id: '2019', name: '2019', color: '#4C6EF5', values: [ 42, 44, 45, 46, 47, 48, 49, 49, 50, 52, 53, 54, 55, 55, 55, 57, 58, 60, 62, ], }, { id: '2020', name: '2020', color: '#20C997', values: [ 38, 39, 40, 41, 42, 43, 44, 46, 48, 50, 51, 51, 52, 53, 53, 54, 55, 57, 59, ], }, { id: '2021', name: '2021', color: '#FF922B', values: [ 45, 47, 48, 49, 50, 51, 52, 53, 55, 56, 57, 58, 59, 59, 60, 61, 62, 63, 64, ], }, { id: '2022', name: '2022', color: '#845EF7', values: [ 50, 51, 52, 54, 55, 56, 57, 58, 60, 61, 62, 63, 64, 65, 65, 66, 67, 68, 69, ], }, ]; return ( ); } ``` ## Component: The Ring component displays progress or status using a radial indicator. It supports custom labels, color stops, neutral states, and fully customized center content. ### Props `value` (number) [required] Current value represented by the ring `min` (number) [optional, default: 0] Lower bound for normalization. Defaults to 0. `max` (number) [optional, default: 100] Upper bound for normalization. Defaults to 100. `size` (number) [optional, default: 100] Diameter of the ring in pixels. Defaults to 100. `thickness` (number) [optional, default: 12] Stroke thickness in pixels. Defaults to 12. `caption` (React.ReactNode) [optional] Optional caption rendered beneath the ring `label` (React.ReactNode) [optional] Main label rendered in the ring center `subLabel` (React.ReactNode) [optional] Secondary label rendered below the main label `showValue` (boolean) [optional, default: true] Displays the computed percentage when no label/subLabel is provided. Defaults to true. `valueFormatter` ((value: number, percent: number) => React.ReactNode) [optional] Formats the displayed value or percentage `trackColor` (string) [optional] Track color behind the progress stroke `progressColor` (string | ((value: number, percent: number) => string)) [optional] Progress stroke color or resolver - … 15 more props documented ### Demos - Interactive Presets | (id: Ring.basic) | tags: ring Drive multiple ring presentations from a single stateful value and expose how sizing and labels adapt. ```tsx import { useState } from 'react'; import { Button, Card, Column, Ring, Row, Text } from '@platform-blocks/ui'; const ringPresets = [ { key: 'default', label: 'Default presentation', props: { caption: 'Completion' }, }, { key: 'compact', label: 'Compact footprint', props: { size: 72, thickness: 8, caption: 'Compact' }, }, { key: 'labeled', label: 'Labeled preview', props: { size: 120, thickness: 14, label: 'Release' }, getSubLabel: (value: number) => `${value}%`, }, ]; const [value, setValue] = useState(72); const updateValue = (delta: number) => { setValue((current) => Math.min(100, Math.max(0, current + delta))); }; const displayValue = Math.round(value); return ( Use rings to surface progress at a glance; adjust `size`, `thickness`, and labeling to fit the surface area. {ringPresets.map((preset) => { const subLabel = preset.getSubLabel?.(displayValue); return ( {preset.label} ); })} Current value: {displayValue}% ); } ``` - Dynamic Color Stops | (id: Ring.color-stops) | tags: ring Display how `colorStops` shift the progress color as values cross threshold ranges. ```tsx import { Card, Column, Ring, Row, Text } from '@platform-blocks/ui'; const colorStops = [ { value: 0, color: '#f87171' }, { value: 60, color: '#fb923c' }, { value: 75, color: '#f59e0b' }, { value: 85, color: '#0ea5e9' }, { value: 92, color: '#14b8a6' }, ]; const completionSamples = [48, 72, 97]; return ( Provide ordered `colorStops` to create gradient-style progress rings that shift tone as completion grows. {completionSamples.map((value) => ( {value}% complete ))} ); } ``` ## Component: Flow diagram showing volume between nodes. ### Props `nodes` (SankeyNode[]) [required] Nodes included in the Sankey diagram `links` (SankeyLink[]) [required] Links connecting the nodes `animationDuration` (number) [optional, default: 1000] Animation duration in milliseconds `disabled` (boolean) [optional, default: false] Disable animations `nodeWidth` (number) [optional] Fixed node width in pixels (auto-calculated if omitted) `nodePadding` (number) [optional] Vertical gap between nodes (auto-calculated if omitted) `chartPadding` (Partial>) [optional] Override chart padding (defaults to 40px all around) `labelFormatter` ((node: SankeyNode) => string) [optional] Format display label for a node `valueFormatter` ((value: number, node: SankeyNode | undefined) => string) [optional] Format value label for a node `onNodeHover` ((node: SankeyNode | null) => void) [optional] Receive callbacks when a node is hovered/focused `onLinkHover` ((link: SankeyLink | null) => void) [optional] Receive callbacks when a link is hovered/focused `highlightOnHover` (boolean) [optional, default: true] Highlight hovered nodes/links (defaults to true) - … 1 more props documented ### Demos - Basic | (id: SankeyChart.basic) ```tsx import { SankeyChart } from '../../'; const NODES = [ { id: 'solar', name: 'Solar', color: '#FFC078' }, { id: 'wind', name: 'Wind', color: '#74C0FC' }, { id: 'hydro', name: 'Hydro', color: '#69DB7C' }, { id: 'grid', name: 'Grid', color: '#FFD43B' }, { id: 'battery', name: 'Battery Storage', color: '#748FFC' }, { id: 'residential', name: 'Residential', color: '#FF6B6B' }, { id: 'commercial', name: 'Commercial', color: '#12B886' }, { id: 'industrial', name: 'Industrial', color: '#5F3DC4' }, ]; const LINKS = [ { source: 'solar', target: 'grid', value: 32 }, { source: 'wind', target: 'grid', value: 28 }, { source: 'hydro', target: 'grid', value: 18 }, { source: 'solar', target: 'battery', value: 6 }, { source: 'wind', target: 'battery', value: 4 }, { source: 'battery', target: 'grid', value: 8 }, { source: 'grid', target: 'residential', value: 30 }, { source: 'grid', target: 'commercial', value: 24 }, { source: 'grid', target: 'industrial', value: 16 }, ]; return ( ); } ``` - Budget Allocation | (id: SankeyChart.budget-allocation) ```tsx import { SankeyChart } from '../../'; const NODES = [ { id: 'corporate-budget', name: 'Corporate Budget', color: '#2563EB' }, { id: 'gtm', name: 'Go-to-Market', color: '#34D399' }, { id: 'product', name: 'Product & Engineering', color: '#A855F7' }, { id: 'operations', name: 'Operations', color: '#F97316' }, { id: 'paid-media', name: 'Paid Media', color: '#60A5FA' }, { id: 'events', name: 'Events', color: '#F472B6' }, { id: 'product-investment', name: 'Product Investment', color: '#C084FC' }, { id: 'platform-modernization', name: 'Platform Modernization', color: '#8B5CF6' }, { id: 'customer-success', name: 'Customer Success', color: '#22C55E' }, { id: 'supply-chain', name: 'Supply Chain', color: '#FB923C' }, ]; const LINKS = [ { source: 'corporate-budget', target: 'gtm', value: 24 }, { source: 'corporate-budget', target: 'product', value: 32 }, { source: 'corporate-budget', target: 'operations', value: 18 }, { source: 'gtm', target: 'paid-media', value: 12 }, { source: 'gtm', target: 'events', value: 8 }, { source: 'gtm', target: 'customer-success', value: 4 }, { source: 'product', target: 'product-investment', value: 14 }, { source: 'product', target: 'platform-modernization', value: 12 }, { source: 'operations', target: 'customer-success', value: 6 }, { source: 'operations', target: 'supply-chain', value: 10 }, ]; return ( ); } ``` ## Component: Plots data points in two-dimensional space for correlation analysis. ### Props `data` (ChartDataPoint[]) [required] Data points `series` (ScatterSeries[]) [optional] Optional multi-series data (overrides top-level data if provided) `pointSize` (number) [optional, default: 6] Point size `pointColor` (string) [optional] Point color `pointOpacity` (number) [optional, default: 1] Point opacity `allowAddPoints` (boolean) [optional, default: false] Allow adding points by tapping `allowDragPoints` (boolean) [optional, default: false] Allow dragging points `showTrendline` (boolean | 'overall' | 'per-series') [optional, default: false] Show trend line. true|'overall' for single combined regression, 'per-series' for one per series `trendlineColor` (string) [optional] Trend line color `enablePanZoom` (boolean) [optional] Enable pan & zoom interactions `zoomMode` ('x' | 'y' | 'both') [optional] Zoom mode (axes constrained) `minZoom` (number) [optional] Minimum zoom scale (domain fraction) - … 18 more props documented ### Demos - Api Error Rate Volume | (id: ScatterChart.api-error-rate-volume) ```tsx import { ScatterChart } from '../../'; const SERIES = [ { id: 'core-apis', name: 'Core services', color: '#1E66F5', pointSize: 8, data: [ { id: 'core-auth', x: 520, y: 2.9, label: 'Auth' }, { id: 'core-catalog', x: 480, y: 2.6, label: 'Catalog' }, { id: 'core-inventory', x: 410, y: 2.4, label: 'Inventory' }, { id: 'core-profiles', x: 365, y: 2.1, label: 'Profiles' }, { id: 'core-checkout', x: 445, y: 2.7, label: 'Checkout' }, ], }, { id: 'payment-apis', name: 'Payment services', color: '#F76707', pointSize: 8, data: [ { id: 'pay-processing', x: 260, y: 4.1, label: 'Processor' }, { id: 'pay-ledger', x: 195, y: 3.1, label: 'Ledger' }, { id: 'pay-invoicing', x: 220, y: 3.6, label: 'Invoicing' }, { id: 'pay-fx', x: 180, y: 3.4, label: 'FX gateway' }, { id: 'pay-risk', x: 240, y: 3.9, label: 'Risk scoring' }, ], }, { id: 'edge-apis', name: 'Edge and experimental', color: '#15AABF', pointSize: 7, data: [ { id: 'edge-recos', x: 120, y: 4.2, label: 'Recommendations' }, { id: 'edge-search', x: 95, y: 4.5, label: 'Search beta' }, { id: 'edge-proto', x: 70, y: 3.9, label: 'Prototype API' }, { id: 'edge-labs', x: 55, y: 3.6, label: 'Labs checkout' }, { id: 'edge-content', x: 82, y: 4, label: 'Content sync' }, ], }, ]; const QUADRANTS = { x: 180, y: 3.5, fills: { topLeft: 'rgba(255, 193, 7, 0.1)', topRight: 'rgba(255, 107, 107, 0.12)', bottomLeft: 'rgba(63, 142, 252, 0.08)', bottomRight: 'rgba(34, 197, 247, 0.08)', }, fillOpacity: 1, lineColor: '#B0C4FE', lineWidth: 1, labels: { topLeft: 'High error - lower volume', topRight: 'Critical risk', bottomLeft: 'Monitor growth', bottomRight: 'Healthy scale', }, labelColor: '#1F2933', labelFontSize: 11, labelOffset: 12, }; const describeQuadrant = (x: number, y: number) => { const labels = QUADRANTS.labels; if (!labels) return null; const isRight = x >= QUADRANTS.x; const isTop = y >= QUADRANTS.y; if (isRight && isTop) return labels.topRight ?? null; if (!isRight && isTop) return labels.topLeft ?? null; if (isRight && !isTop) return labels.bottomRight ?? null; return labels.bottomLeft ?? null; }; return ( serie.data)} series={SERIES} quadrants={QUADRANTS} pointOpacity={0.9} enableCrosshair multiTooltip liveTooltip grid={{ show: true, color: '#E9ECEF' }} legend={{ show: true, position: 'bottom' }} xScaleType="log" xAxis={{ show: true, title: 'Requests per minute (thousands)', labelFormatter: (value: number) => `${Math.round(value)}k`, }} yAxis={{ show: true, title: 'Error rate (%)', labelFormatter: (value: number) => `${value.toFixed(1)}%`, }} tooltip={{ show: true, backgroundColor: '#12263A', textColor: '#F4F6FB', formatter: (point) => { const quadrantNote = describeQuadrant(point.x, point.y); const lines = [ point.label ?? 'Service', `Volume ${Math.round(point.x)}k rpm · Errors ${point.y.toFixed(1)}%`, ]; if (quadrantNote) { lines.push(quadrantNote); } return lines.join('\n'); }, }} /> ); } ``` - Basic | (id: ScatterChart.basic) ```tsx import { ScatterChart } from '../../'; const SERIES = [ { id: 'marketing', name: 'Marketing spend', color: '#4C6EF5', data: [ { x: 18, y: 42 }, { x: 22, y: 48 }, { x: 25, y: 54 }, { x: 27, y: 58 }, { x: 30, y: 61 }, { x: 34, y: 66 }, { x: 38, y: 70 }, ], pointSize: 8, }, { id: 'events', name: 'Event sponsorship', color: '#20C997', data: [ { x: 12, y: 36 }, { x: 16, y: 41 }, { x: 19, y: 44 }, { x: 23, y: 47 }, { x: 27, y: 55 }, { x: 32, y: 62 }, { x: 35, y: 65 }, ], pointSize: 7, }, ]; return ( serie.data)} series={SERIES} showTrendline="per-series" enableCrosshair enablePanZoom zoomMode="both" multiTooltip liveTooltip xAxis={{ show: true, title: 'Spend (USD thousands)', labelFormatter: (value) => `$${value}`, }} yAxis={{ show: true, title: 'Qualified leads', }} grid={{ show: true, color: '#E7ECFC' }} legend={{ show: true, position: 'bottom' }} tooltip={{ show: true, formatter: (point) => `${point.x}k spend → ${point.y} leads`, }} /> ); } ``` ## Component: The Search component provides a search input with debouncing, loading states, and customizable clear functionality. ### Props `value` (string) [optional] `defaultValue` (string) [optional] `onChange` ((value: string) => void) [optional] `onSubmit` ((value: string) => void) [optional] `placeholder` (string) [optional] `size` (SizeValue) [optional] `radius` (any) [optional] `autoFocus` (boolean) [optional] `debounce` (number) [optional] `clearButton` (boolean) [optional] `loading` (boolean) [optional] `endSection` (React.ReactNode) [optional] - … 5 more props documented ### Demos - Basic search | (id: Search.basic) | tags: controlled, input Control the `Search` value with local state so you can react to user input and mirror the query elsewhere in your UI. ```tsx import { useState } from 'react'; import { Column, Search, Text } from '@platform-blocks/ui'; const [query, setQuery] = useState(''); return ( Current query: {query || '—'} ); } ``` - Button mode | (id: Search.button-mode) | tags: spotlight, shortcuts Set `buttonMode` to turn `Search` into a pressable launcher and pass a `rightComponent` with `KeyCap` shortcuts so users discover keyboard access. ```tsx import { Column, KeyCap, Row, Search, Text, useToast } from '@platform-blocks/ui'; const toast = useToast(); const handleCustomPress = () => { toast.show({ message: 'Launching saved search…' }); }; return ( Default Spotlight launcher K )} /> Custom handler with shortcut hint Ctrl F )} /> ); } ``` ## Component: 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 accessibili… ### Props `data` (SegmentedControlData[]) [required] Data that defines the segments `value` (string) [optional] Controlled value `defaultValue` (string) [optional] Uncontrolled initial value `onChange` ((value: string) => void) [optional] Called when value changes `size` (SizeValue) [optional] Control size, maps to height and font size `color` (string) [optional] Indicator color token or hex `orientation` ('horizontal' | 'vertical') [optional] Layout orientation `fullWidth` (boolean) [optional] Stretch across available width `disabled` (boolean) [optional] Disable entire control `readOnly` (boolean) [optional] Prevent user interaction but keep visual state `autoContrast` (boolean) [optional] Adjust text color automatically for filled/outline variants `withItemsBorders` (boolean) [optional] Render dividers between items - … 12 more props documented ### Demos - Basic Usage | (id: SegmentedControl.basic) | tags: segmented-control, selection, uncontrolled Set `defaultValue` to preselect a segment and let the control manage focus and selection state internally. ```tsx import { Card, Column, SegmentedControl, Text } from '@platform-blocks/ui'; const frameworks = [ { label: 'React', value: 'react' }, { label: 'Angular', value: 'angular' }, { label: 'Vue', value: 'vue' }, ]; return ( Segmented controls expose a compact option switcher. This version relies on internal state via `defaultValue`. ); } ``` - Controlled Value | (id: SegmentedControl.controlled) | tags: segmented-control, controlled, state Provide `value` and `onChange` to synchronize the selected segment with external state or companion controls. ```tsx import { useState } from 'react'; import { Button, Card, Column, Row, SegmentedControl, Text } from '@platform-blocks/ui'; const frameworks = [ { label: 'React', value: 'react' }, { label: 'Angular', value: 'angular' }, { label: 'Vue', value: 'vue' }, ]; const [value, setValue] = useState('react'); return ( Drive the segmented control from external state to synchronize its value with other inputs. Selected value: {value} ); } ``` ## Component: setValue(selected as string)} /> {value && ( Selected: {value} )} ) } ``` - Full width | (id: Select.fullWidth) | tags: full-width, radius, responsive Stretch the control to the container width and tweak radius tokens for styling. ```tsx import { useState } from 'react' import { Column, Select, Text } from '@platform-blocks/ui' const options = [ { label: 'Small', value: 'sm' }, { label: 'Medium', value: 'md' }, { label: 'Large', value: 'lg' }, ] const [value, setValue] = useState(null) return ( Full-width variations Stretch the select to fill its container and adjust radius tokens as needed. setValue(val as string)} /> Apply radius tokens to align with your design system. ) } ``` ## Component: Animated text highlight that sweeps a configurable gradient across the content. Ideal for loading states, premium callouts, and attention-grabbing text accents. ### Props `children` (ReactNode) [optional] Text node children. Overrides `text` when provided `text` (string) [optional] Text content to render when not using children `color` (string) [optional] Base text color rendered underneath the shimmer `colors` (string[]) [optional] Optional gradient stops override `shimmerColor` (string) [optional] Highlight color used for the shimmer pass `spread` (number) [optional] Length multiplier for the shimmer sweep (higher = wider highlight) `duration` (number) [optional] Duration of a single shimmer cycle in seconds `delay` (number) [optional] Delay before the shimmer starts (seconds) `repeatDelay` (number) [optional] Delay between shimmer repetitions (seconds) `repeat` (boolean) [optional] Whether the shimmer should repeat indefinitely `once` (boolean) [optional] Animate only once after becoming visible `direction` (ShimmerDirection) [optional] Direction of shimmer movement - … 6 more props documented ### Demos - Basic shimmer | (id: ShimmerText.basic) | tags: animation, text Wrap text in `ShimmerText` to add the default looping highlight with no additional configuration. ```tsx import { Column, ShimmerText } from '@platform-blocks/ui'; return ( Weekly highlights go live New arrivals shimmer into view every Friday at noon. ); } ``` - Interactive controls | (id: ShimmerText.controls) | tags: animation, controls Expose `spread`, `repeat`, and `once` controls to let readers tune the shimmer loop at runtime. ```tsx import { useState } from 'react'; import { Column, Row, ShimmerText, Slider, Switch, Text } from '@platform-blocks/ui'; const MIN_SPREAD = 1; const MAX_SPREAD = 4; const SPREAD_STEP = 0.1; const [spread, setSpread] = useState(2); const [repeat, setRepeat] = useState(true); const [once, setOnce] = useState(false); const handleRepeatChange = (value: boolean) => { setRepeat(value); if (value) { setOnce(false); } }; const handleOnceChange = (value: boolean) => { setOnce(value); if (value) { setRepeat(false); } }; return ( Interactive shimmer headline Spread: {spread.toFixed(1)} Repeat animation Run once ); } ``` ## Component: Skeleton components provide visual placeholders for text, avatars, and blocks to reduce perceived loading time. ### Props `shape` (SkeletonShape) [optional, default: 'rectangle'] Shape of the skeleton placeholder `w` (DimensionValue) [optional] Width of the skeleton component `h` (DimensionValue) [optional] Height of the skeleton component `size` (SizeValue) [optional, default: 'md'] Size of the skeleton component (overrides width/height) `radius` (SizeValue | number) [optional] Border radius for rectangle/rounded shapes `animate` (boolean) [optional, default: true] Whether to show the loading animation `animationDuration` (number) [optional, default: 1500] Duration of the loading animation in milliseconds `colors` ([string, string]) [optional] Gradient colors for the shimmer effect `style` (StyleProp) [optional] Style overrides for the skeleton container `testID` (string) [optional] Optional test identifier ### Demos - Basics | (id: Skeleton.basic) | tags: loading, placeholder Stack text, avatar, and block placeholders to preview the structure of incoming content while data loads. ```tsx import { Skeleton, Column, Row } from '@platform-blocks/ui'; return ( ); } ``` - Shapes | (id: Skeleton.shapes) | tags: avatar, text, button Preview the available skeleton shapes for avatars, typography lines, actions, and media blocks. ```tsx import { Skeleton, Column, Row } from '@platform-blocks/ui'; return ( ); } ``` ## Component: The Slider component allows users to select a value or range of values by moving a handle along a track. Supports single values, ranges, vertical layouts, and rich customization hooks. ### Props `value` (number) [optional] Slider value `defaultValue` (number) [optional, default: 0] Uncontrolled initial value `onChange` ((value: number) => void) [optional] Change handler `min` (number) [optional, default: 0] Minimum value `max` (number) [optional, default: 100] Maximum value `step` (number) [optional, default: 1] Step increment `orientation` ('horizontal' | 'vertical') [optional, default: 'horizontal'] Slider orientation `trackColor` (ColorValue) [optional] Track color `activeTrackColor` (ColorValue) [optional] Active track color `thumbColor` (ColorValue) [optional] Thumb color `trackSize` (number) [optional] Track height/width `thumbSize` (number) [optional] Thumb size - … 19 more props documented ### Demos - Basic Usage | (id: Slider.basic) | tags: basic, slider Basic slider usage for selecting numeric values within a range. ```tsx import { useState } from 'react'; import { Slider, Text, Column, Card, Block } from '@platform-blocks/ui'; const [value, setValue] = useState(25); return ( Basic Slider Value: {value} ); } ``` - Range Slider | (id: Slider.range) | tags: range, multiple, dual-thumb Range slider for selecting a range of values with two handles on a single track. Perfect for filtering, price ranges, and any scenario where you need to select minimum and maximum values. ```tsx import { useState } from 'react'; import { RangeSlider, Text, Block, Card, Flex } from '@platform-blocks/ui'; const [priceRange, setPriceRange] = useState<[number, number]>([25, 75]); const [temperatureRange, setTemperatureRange] = useState<[number, number]>([18, 24]); const [scoreRange, setScoreRange] = useState<[number, number]>([60, 90]); return ( Price Range Min: ${priceRange[0]} Max: ${priceRange[1]} Budget range: ${priceRange[1] - priceRange[0]} Temperature Range `${value}°C`} valueLabelAlwaysOn /> Range: {temperatureRange[0]}°C - {temperatureRange[1]}°C Score Range Score range: {scoreRange[0]} - {scoreRange[1]} (span: {scoreRange[1] - scoreRange[0]}) ); } ``` ## Component: Use the Space component to insert fixed spacing between elements when margin props are not available or would make layouts harder to reason about. ### Props `h` (SizeValue) [optional] Height of the spacer. Accepts theme spacing tokens or raw numbers. `w` (SizeValue) [optional] Width of the spacer. Accepts theme spacing tokens or raw numbers. `size` (SizeValue) [optional, default: 'md'] Fallback size when neither `h` nor `w` is provided. Defaults to `md` so the component always occupies some space. `style` (StyleProp) [optional] Optional style overrides. `children` (never) [optional] Space is presentational only, so children are not supported. ### Demos - Vertical spacing | (id: Space.basic) | tags: spacing, layout Compare token-based and numeric vertical gaps between stacked content blocks. ```tsx import { Block, Column, Space, Text, useTheme } from '@platform-blocks/ui'; const EXAMPLES = [ { label: 'Token spacing (md)', gap: 'md' as const, helper: 'Use theme tokens for consistent rhythm between related content.' }, { label: 'Token spacing (xl)', gap: 'xl' as const, helper: 'Larger tokens create breathing room for grouped sections.' }, { label: 'Numeric spacing (24px)', gap: 24, helper: 'Fallback to numeric values when a token does not fit the layout.' } ] as const; const theme = useTheme(); return ( {EXAMPLES.map(({ label, gap, helper }) => ( {label} First line Second line {helper} ))} ); } ``` - Horizontal spacing | (id: Space.horizontal) | tags: spacing, layout Use `Space` to control gutters between inline buttons with tokens or fixed widths. ```tsx import { Block, Button, Column, Row, Space, Text, useTheme } from '@platform-blocks/ui'; const GROUPS = [ { label: 'Token spacing (lg)', gap: 'lg' as const, helper: 'Theme tokens keep button gutters aligned with the spacing scale.' }, { label: 'Numeric spacing (18px)', gap: 18, helper: 'Use a numeric width when exact measurements are required.' } ] as const; const theme = useTheme(); return ( {GROUPS.map(({ label, gap, helper }) => ( {label} {helper} ))} ); } ``` ## Component: Compact inline trend indicator for dense data summaries. ### Props `id` (string | number) [optional] Optional unique series ID `name` (string) [optional] Optional series name (for tooltip) `data` (number[] | SparklinePoint[]) [required] Data points (plain y values or explicit {x,y} pairs) `color` (string) [optional] Line color `fill` (boolean) [optional, default: true] Fill area under line `fillOpacity` (number) [optional, default: 0.3] Fill opacity `strokeWidth` (number) [optional, default: 2] Stroke width `smooth` (boolean) [optional, default: true] Curve smoothing `showPoints` (boolean) [optional, default: false] Show data points `pointSize` (number) [optional, default: 3] Point size (for individual data points) `domain` (SparklineDomain) [optional] Provide min/max to avoid re-scaling jitter across multiple sparklines `highlightLast` (boolean) [optional, default: true] Show last value bubble - … 7 more props documented ### Demos - Basic | (id: SparklineChart.basic) ```tsx import { SparklineChart } from '../../'; const DAILY_SIGNUPS = [ 32, 36, 31, 40, 44, 47, 46, 52, 58, 60, 64, 67, 70, 72, ]; return ( // TODO: cant see gradient fill on this one, investigate `${value} signups`} domain={{ y: [20, 80] }} /> ); } ``` - Dashboard Daily Active Users | (id: SparklineChart.dashboard-daily-active-users) ```tsx import { View, Text } from 'react-native'; import { SparklineChart } from '../../'; const SURFACE_SERIES = [ { id: 'web', title: 'Web', color: '#3B82F6', data: [1820, 1855, 1880, 1915, 1940, 1975, 2010, 2045, 2070, 2095, 2130, 2165, 2195, 2230], }, { id: 'ios', title: 'iOS', color: '#6366F1', data: [940, 955, 968, 984, 1005, 1018, 1042, 1058, 1075, 1098, 1110, 1126, 1148, 1168], }, { id: 'android', title: 'Android', color: '#10B981', data: [1280, 1295, 1310, 1335, 1342, 1360, 1385, 1410, 1432, 1455, 1470, 1488, 1512, 1536], }, ]; const formatUsers = (value: number) => `${Math.round(value).toLocaleString()} users`; const getDeltaLabel = (series: number[]) => { if (series.length < 2) return 'Stable vs yesterday'; const latest = series[series.length - 1]; const prior = series[series.length - 2]; const delta = latest - prior; if (delta === 0) return 'Stable vs yesterday'; const prefix = delta > 0 ? '+' : '-'; return `${prefix}${Math.abs(delta).toLocaleString()} vs yesterday`; }; return ( Daily Active Users Trailing two weeks, by platform {SURFACE_SERIES.map((series) => { const latest = series.data[series.data.length - 1]; return ( {series.title} {latest.toLocaleString()} · {getDeltaLabel(series.data)} ); })} ); } ``` ## Component: The Spoiler component automatically collapses content that exceeds a specified height, providing a show/hide toggle to reveal the full content. ### Props `children` (React.ReactNode) [required] Content to hide/show `maxHeight` (number) [optional] Max height (in px) when collapsed `initiallyOpen` (boolean) [optional] Whether component starts initially opened `showLabel` (string) [optional] Label for show more `hideLabel` (string) [optional] Label for hide `transitionDuration` (number) [optional] Transition duration ms `size` (SizeValue) [optional] Size token influences padding / font `opened` (boolean) [optional] Optional controlled open state `onToggle` ((opened: boolean) => void) [optional] Callback when toggle `disabled` (boolean) [optional] Disable toggle `style` (any) [optional] Optional style `renderControl` ((args: { opened: boolean; toggle: () => void; showLabel: string; hideLabel: string }) => React.ReactNode) [optional] Render custom control - … 3 more props documented ### Demos - Basic Usage | (id: Spoiler.basic) | tags: spoiler, collapse, content Set `maxHeight` to reveal a preview of long copy while the rest stays accessible behind the built-in toggle. ```tsx import { Card, Column, Spoiler, Text } from '@platform-blocks/ui'; const paragraphs = [ 'Spoilers collapse long sections of copy while keeping the content accessible to screen readers and keyboard users.', 'Use them for optional detail or secondary information that might distract from a primary task. They expand inline, so the surrounding layout stays stable.', ]; return ( Keep the initial height short to hint that more detail is available without overwhelming the layout. {paragraphs.map((paragraph) => ( {paragraph} ))} ); } ``` - Initial State | (id: Spoiler.initiallyOpen) | tags: spoiler, state, initially-open Flip the `initiallyOpen` prop to choose whether content renders expanded on mount or waits for user interaction. ```tsx import { Card, Column, Spoiler, Text } from '@platform-blocks/ui'; const examples = [ { key: 'open', label: 'Initially open', description: 'Starts expanded by default so the reader sees the full content on first render.', props: { initiallyOpen: true }, }, { key: 'closed', label: 'Initially closed', description: 'Keeps the section compact to emphasize surrounding UI until the user opts in.', props: {}, }, ]; const bodyCopy = 'Vivamus fermentum orci eget tortor facilisis, eu egestas eros maximus. Fusce vitae semper libero. Pellentesque habitant morbi tristique senectus et netus.'; return ( Control whether the content renders expanded on mount or waits for user input. Both states remain accessible to assistive tech. {examples.map((example) => ( {example.label} {bodyCopy} {example.description} ))} ); } ``` ## Component: The Spotlight component provides a fast, keyboard-driven interface for searching commands, routes, and entities. Supports arrow key navigation, Enter to select, and Escape to dismiss. ### Props `actions` (SpotlightItem[]) [required] `nothingFound` (string) [optional] `highlightQuery` (boolean | HighlightComponentProps['highlight']) [optional] `limit` (number) [optional] `scrollable` (boolean) [optional] `maxHeight` (number) [optional] `shortcut` (string | string[] | null) [optional] `searchProps` (any) [optional] `store` (any) [optional] `variant` ('modal' | 'bottomsheet' | 'fullscreen') [optional] `width` (number) [optional] `height` (number) [optional] ### Demos - Keyboard Palette | (id: Spotlight.basic) | tags: spotlight Open the Spotlight command palette through the shared store and a primary trigger button. ```tsx import { Button, Card, Column, Spotlight, Text, useSpotlightStoreInstance, type SpotlightProps } from '@platform-blocks/ui'; const actions: SpotlightProps['actions'] = [ { id: 'home', label: 'Go to home', description: 'Navigate to the home screen', icon: 'home', onPress: () => console.log('navigate: home'), }, { id: 'profile', label: 'Open profile', description: 'View your account details', icon: 'user', onPress: () => console.log('navigate: profile'), }, { id: 'settings', label: 'Adjust settings', description: 'Update application preferences', icon: 'settings', onPress: () => console.log('navigate: settings'), }, ]; const [store] = useSpotlightStoreInstance(); return ( Spotlight provides a keyboard-driven command palette. Open it with `⌘K` or `Ctrl+K`, or trigger it imperatively from a button. You can reuse the same store across multiple triggers. ); } ``` - Custom Icons | (id: Spotlight.custom-icons) | tags: spotlight Swap `icon` definitions with custom React nodes to render richer action affordances. ```tsx import { Button, Card, Column, Icon, Spotlight, Text, useSpotlightStoreInstance, type SpotlightProps } from '@platform-blocks/ui'; const actions: SpotlightProps['actions'] = [ { id: 'deploy', label: 'Deploy service', description: 'Trigger the CI/CD pipeline', icon: , onPress: () => console.log('deploy service'), }, { id: 'logs', label: 'Inspect logs', description: 'Open the latest runtime logs', icon: , onPress: () => console.log('view logs'), }, { id: 'alerts', label: 'Review alerts', description: 'Check active incidents', icon: , onPress: () => console.log('open alerts'), }, ]; const [store] = useSpotlightStoreInstance(); return ( Icons accept full React nodes, so you can swap in composable UI like `Icon`, avatars, or status badges for richer visuals. ); } ``` ## Component: Area chart with multiple series stacked to show cumulative contributions. ### Props `series` (LineChartSeries[]) [required] `stackOrder` ('normal' | 'reverse') [optional, default: 'normal'] `smooth` (boolean) [optional, default: true] `opacity` (number) [optional, default: 0.55] base opacity for layers `stackMode` ('absolute' | 'percentage') [optional, default: 'absolute'] ### Demos - Basic | (id: StackedAreaChart.basic) ```tsx import { StackedAreaChart } from '../../'; const SERIES = [ { id: 'mobile', name: 'Mobile', color: '#4C6EF5', data: [ { x: 1, y: 22 }, { x: 2, y: 26 }, { x: 3, y: 28 }, { x: 4, y: 32 }, { x: 5, y: 36 }, { x: 6, y: 38 }, { x: 7, y: 42 }, { x: 8, y: 44 }, { x: 9, y: 47 }, { x: 10, y: 50 }, { x: 11, y: 52 }, { x: 12, y: 55 }, ], }, { id: 'web', name: 'Web', color: '#20C997', data: [ { x: 1, y: 18 }, { x: 2, y: 20 }, { x: 3, y: 22 }, { x: 4, y: 25 }, { x: 5, y: 26 }, { x: 6, y: 27 }, { x: 7, y: 28 }, { x: 8, y: 30 }, { x: 9, y: 32 }, { x: 10, y: 33 }, { x: 11, y: 34 }, { x: 12, y: 35 }, ], }, { id: 'api', name: 'API', color: '#FF922B', data: [ { x: 1, y: 12 }, { x: 2, y: 14 }, { x: 3, y: 15 }, { x: 4, y: 16 }, { x: 5, y: 18 }, { x: 6, y: 19 }, { x: 7, y: 21 }, { x: 8, y: 22 }, { x: 9, y: 23 }, { x: 10, y: 24 }, { x: 11, y: 25 }, { x: 12, y: 27 }, ], }, ]; return ( `M${value}` }} yAxis={{ show: true, title: 'Active users (thousands)', labelFormatter: (value) => `${value}`, }} grid={{ show: true, color: '#E3E8FB' }} legend={{ show: true, position: 'bottom' }} enableCrosshair multiTooltip liveTooltip /> ); } ``` ## Component: Bar chart with segments stacked to show part-to-whole by category. ### Props `series` (StackedBarSeries[]) [required] Stacked bar series to render `barSpacing` (number) [optional, default: 0.25] Gap between stacked groups `legend` (ChartLegend) [optional, default: { show: true] Legend configuration `xAxis` (ChartAxis) [optional] X-axis configuration `yAxis` (ChartAxis) [optional] Y-axis configuration `grid` (ChartGrid) [optional] Grid line configuration `animation` (ChartAnimation) [optional] Animation configuration ### Demos - Basic | (id: StackedBarChart.basic) ```tsx import { StackedBarChart } from '../../'; const SERIES = [ { id: 'new-business', name: 'New business', color: '#4C6EF5', data: [ { id: 'q1-new', category: 'Q1', value: 220 }, { id: 'q2-new', category: 'Q2', value: 250 }, { id: 'q3-new', category: 'Q3', value: 240 }, { id: 'q4-new', category: 'Q4', value: 280 }, ], }, { id: 'expansion', name: 'Expansion', color: '#20C997', data: [ { id: 'q1-exp', category: 'Q1', value: 110 }, { id: 'q2-exp', category: 'Q2', value: 135 }, { id: 'q3-exp', category: 'Q3', value: 150 }, { id: 'q4-exp', category: 'Q4', value: 165 }, ], }, { id: 'renewal', name: 'Renewal', color: '#FF922B', data: [ { id: 'q1-renew', category: 'Q1', value: 180 }, { id: 'q2-renew', category: 'Q2', value: 172 }, { id: 'q3-renew', category: 'Q3', value: 188 }, { id: 'q4-renew', category: 'Q4', value: 194 }, ], }, ]; return ( `$${value}`, }} grid={{ show: true, color: '#E5EAF7' }} legend={{ show: true, position: 'bottom' }} animation={{ duration: 500 }} /> ); } ``` ## Component: The Stepper component provides a step-by-step navigation interface, perfect for multi-step forms, wizards, and progress tracking. ### Props `active` (number) [required] Active step index `onStepClick` ((stepIndex: number) => void) [optional] Called when step is clicked `orientation` ('horizontal' | 'vertical') [optional] Step orientation `iconPosition` ('left' | 'right') [optional] Icon position relative to step body `iconSize` (number) [optional] Icon size `size` (ComponentSizeValue) [optional] Component size `color` (string) [optional] Component color `completedIcon` (ReactNode) [optional] Icon to display when step is completed `allowNextStepsSelect` (boolean) [optional] Whether next steps (steps with higher index) can be selected `children` (ReactNode) [required] Step content `ref` (React.Ref) [optional] Accessibility label Component reference ### Demos - Controlled Flow | (id: Stepper.basic) | tags: stepper Control the active step with local state and show completion messaging once the flow finishes. ```tsx import { useState } from 'react'; import { Button, Card, Block, Row, Stepper, Text } from '@platform-blocks/ui'; const steps = [ { label: 'Account', description: 'Create your credentials', details: 'Set up your sign-in information and confirm your contact details.', }, { label: 'Verification', description: 'Confirm your email', details: 'Check your inbox for a verification link to secure the account.', }, { label: 'Profile', description: 'Complete setup', details: 'Add profile details so the team can find and collaborate with you.', }, ]; const totalSteps = steps.length; const [activeStep, setActiveStep] = useState(1); const handleStepChange = (nextIndex: number) => { if (nextIndex < 0 || nextIndex > totalSteps) { return; } setActiveStep(nextIndex); }; const goPrevious = () => handleStepChange(activeStep - 1); const goNext = () => handleStepChange(activeStep + 1); return ( Control the current step with the `active` prop and provide completion content with `Stepper.Completed`. {steps.map((step) => ( {step.details} ))} All onboarding tasks are complete. You can continue to the dashboard. ); } ``` - Visited Step Selection | (id: Stepper.allowSelect) | tags: stepper, selection Limit navigation to completed steps using `allowStepSelect`, while preserving forward progress through the flow. ```tsx import { useState } from 'react'; import { Button, Card, Block, Row, Stepper, Text } from '@platform-blocks/ui'; const steps = [ { label: 'Account', description: 'Create credentials', details: 'Provide your name, email, and a secure password to get started.', }, { label: 'Verification', description: 'Confirm ownership', details: 'We email a confirmation link that needs to be opened before continuing.', }, { label: 'Preferences', description: 'Adjust settings', details: 'Choose notification and privacy defaults for your workspace.', }, ]; const totalSteps = steps.length; const [activeStep, setActiveStep] = useState(0); const [highestVisitedStep, setHighestVisitedStep] = useState(0); const handleStepChange = (nextIndex: number) => { if (nextIndex < 0 || nextIndex > totalSteps) { return; } setActiveStep(nextIndex); setHighestVisitedStep((previous) => Math.max(previous, Math.min(nextIndex, totalSteps - 1))); }; const canSelectStep = (stepIndex: number) => highestVisitedStep >= stepIndex && activeStep !== stepIndex; const goPrevious = () => handleStepChange(activeStep - 1); const goNext = () => handleStepChange(activeStep + 1); return ( Gate step selection with `allowStepSelect` so people can revisit completed steps without skipping ahead. {steps.map((step, index) => ( {step.details} ))} Setup is complete. You can always return to earlier steps from the navigation. ); } ``` ## Component: Switch components provide a way to toggle between two states, typically representing on/off or enabled/disabled states. ### Props `checked` (boolean) [optional] Whether switch is on `defaultChecked` (boolean) [optional, default: false] Initial checked state for uncontrolled usage `onChange` ((checked: boolean) => void) [optional] Change handler `size` (SizeValue) [optional, default: 'md'] Switch size `color` (ColorValue) [optional, default: 'primary'] Switch color theme when on `label` (React.ReactNode) [optional] Switch label `disabled` (boolean) [optional, default: false] Whether switch is disabled `required` (boolean) [optional, default: false] Whether switch is required `error` (string) [optional] Error message `description` (string) [optional] Helper text `labelPosition` ('left' | 'right' | 'top' | 'bottom') [optional, default: 'right'] Label position relative to switch `children` (React.ReactNode) [optional] Switch content/children (alternative to label) - … 7 more props documented ### Demos - Basic Usage | (id: Switch.basic) | tags: controlled, label Control a `Switch` with local state and surface its status with supporting text and the `description` prop. ```tsx import { useState } from 'react'; import { Column, Switch, Text } from '@platform-blocks/ui'; const [enabled, setEnabled] = useState(true); return ( Notices are {enabled ? 'enabled' : 'disabled'}. ); } ``` - Sizes | (id: Switch.sizes) | tags: size Choose a `size` token to scale the switch track and thumb; `defaultChecked` seeds uncontrolled switches. ```tsx import type { SwitchProps } from '@platform-blocks/ui'; import { Column, Row, Switch, Text } from '@platform-blocks/ui'; const SIZES: NonNullable[] = ['sm', 'md', 'lg', 'xl']; return ( {SIZES.map((size) => ( ))} {SIZES.map((size) => ( ))} ); } ``` ## Component: The Table component offers a minimal semantic wrapper (thead, tbody, tr, th, td) useful for simple static tabular data when the full DataTable is unnecessary. ### Props `children` (React.ReactNode) [optional] `data` (TableData) [optional] Table data for automatic generation of rows `horizontalSpacing` ('xs' | 'sm' | 'md' | 'lg' | 'xl' | number) [optional] Horizontal spacing between cells `verticalSpacing` ('xs' | 'sm' | 'md' | 'lg' | 'xl' | number) [optional] Vertical spacing between cells `striped` (boolean) [optional] Add striped styling to rows `highlightOnHover` (boolean) [optional] Highlight rows on hover/press `withTableBorder` (boolean) [optional] Add borders around table `withColumnBorders` (boolean) [optional] Add borders between columns `withRowBorders` (boolean) [optional] Add borders between rows `captionSide` ('top' | 'bottom') [optional] Caption position `layout` ('auto' | 'fixed') [optional] Table layout mode `variant` ('default' | 'vertical') [optional] Variant of table layout - … 9 more props documented ### Demos - Data Prop | (id: Table.basic) | tags: table Pass a dataset to the `data` prop to render the caption, header, and body without composing subcomponents. ```tsx import { Card, Column, Table, Text } from '@platform-blocks/ui'; const data = { caption: 'User accounts overview', head: ['Name', 'Role', 'Status', 'Posts'], body: [ ['Alice', 'Admin', 'Active', 128], ['Bob', 'Editor', 'Invited', 42], ['Carol', 'Viewer', 'Active', 5], ['Dave', 'Editor', 'Suspended', 16], ], }; return ( Provide the `data` prop to render the caption, header, and body automatically.
); } ``` - Border Options | (id: Table.borders) | tags: table, borders Enable `withTableBorder`, `withColumnBorders`, and `withRowBorders` to emphasize cell boundaries. ```tsx import { Card, Column, Table, Text } from '@platform-blocks/ui'; const data = { head: ['ID', 'Region', 'Sales', 'Growth %'], body: [ ['#1001', 'NA', '$120,340', '+12.4%'], ['#1002', 'EU', '$98,210', '+4.1%'], ['#1003', 'APAC', '$76,003', '+8.9%'], ['#1004', 'LATAM', '$23,554', '+15.2%'], ], caption: 'Quarterly regional performance', }; return ( Combine table, column, and row borders to separate dense numeric data.
); } ``` ## Component: The `TableOfContents` component automatically discovers headings on the page (h1–h6) and renders a navigable outline with active section tracking. It supports scroll‑spy highlighting, deep-link copying, custom initial data (SSR / virtual d… ### Props `variant` ('filled' | 'outline' | 'ghost' | 'none') [optional, default: 'none'] Visual style variant for the table of contents `color` (string) [optional] Background color for filled variant. Falls back to theme primary color if not specified `size` (SizeValue) [optional, default: 'sm'] Text size for table of contents items `radius` (any) [optional] Border radius value for the container `scrollSpyOptions` (ScrollSpyOptions) [optional] Configuration options for scroll spy behavior `getControlProps` ((payload: { data: TocItem; active: boolean; index: number }) => any) [optional] Function to customize props for each table of contents item control `initialData` (TocItem[]) [optional] Initial data for table of contents items (useful for SSR or pre-rendering) `minDepthToOffset` (number) [optional, default: 1] Minimum depth level at which to start applying depth offset indentation `depthOffset` (number) [optional, default: 20] Pixel offset to apply for each depth level (indentation amount) `reinitializeRef` (React.RefObject<() => void>) [optional] Ref to expose the reinitialize function for manually triggering TOC refresh `autoContrast` (boolean) [optional, default: false] Automatically adjust text color for contrast when using filled variant `style` (any) [optional] Additional styles to apply to the container - … 2 more props documented ### Demos - Basics | (id: TableOfContents.basic) | tags: navigation Drop a table of contents beside your main article and it will register headings automatically through the shared title registry. ```tsx import { useRef } from 'react'; import { Block, Column, Row, TableOfContents, Text, Title, TitleRegistryProvider } from '@platform-blocks/ui'; const SECTIONS = [ { id: 'intro', title: 'Introduction', summary: 'Set the stage for the walkthrough and link to key resources.' }, { id: 'setup', title: 'Setup', summary: 'Install dependencies and initialize the provider before rendering content.' }, { id: 'usage', title: 'Usage', summary: 'Render headings inside your main content area so they register automatically.' }, { id: 'advanced', title: 'Advanced Features', summary: 'Expose the controller or customize the render function when needed.' }, { id: 'faq', title: 'FAQ', summary: 'Answer the questions you expect to receive most often.' }, ]; const contentRef = useRef(null); return ( {SECTIONS.map((section, index) => ( {section.title} {section.summary} ))} ); } ``` - Active callbacks | (id: TableOfContents.active-callback) | tags: navigation Subscribe to `onActiveChange` to surface the currently highlighted section, perfect for syncing status chips or analytics. ```tsx import { useRef, useState } from 'react'; import { Block, Chip, Column, Row, TableOfContents, Text, Title, TitleRegistryProvider } from '@platform-blocks/ui'; const SECTIONS = [ { id: 'overview', title: 'Overview', summary: 'Explain when the progress indicator should appear.' }, { id: 'loading', title: 'Loading States', summary: 'Describe how to expose feedback while content is fetching.' }, { id: 'completion', title: 'Completion', summary: 'Document the triggers that finalize navigation progress.' }, { id: 'error', title: 'Error Recovery', summary: 'Clarify what happens if the data fails to load.' }, ]; const [activeId, setActiveId] = useState(null); const contentRef = useRef(null); return ( setActiveId(id)} /> {SECTIONS.map((section, index) => ( {section.title} {section.summary} ))} Active section: {activeId ?? 'None'} ); } ``` ## Component: Tabs organize content into multiple sections that users can navigate between. The component supports various visual styles, orientations, and interactive behaviors while maintaining accessibility standards. ### Props `items` (TabItem[]) [required] Array of tab definitions to render. The first item becomes active by default when uncontrolled. `activeTab` (string) [optional] Controlled active tab key. When omitted the component manages internal state. `onTabChange` ((tabKey: string) => void) [optional] Called whenever the active tab changes. Fires for both controlled and uncontrolled usage. `onDisabledTabPress` ((tabKey: string, item: TabItem) => void) [optional] Invoked when a disabled tab is pressed, allowing custom messaging or recovery flows. `variant` ('line' | 'chip' | 'card' | 'folder') [optional, default: 'line'] Visual style of the tabs. `size` (SizeValue) [optional, default: 'sm'] Size token controlling text and padding. `color` ('primary' | 'secondary' | 'gray' | 'tertiary' | string) [optional, default: 'primary'] Theme color token or custom color used for indicators and active states. `orientation` ('horizontal' | 'vertical') [optional, default: 'horizontal'] Orientation of the tab list. `location` ('start' | 'end') [optional, default: 'start'] Placement of the tabs relative to their content. Influences indicator positioning. `scrollable` (boolean) [optional, default: false] Enables scrolling when tabs overflow the available axis. `animated` (boolean) [optional, default: true] Enables animated indicator transitions between tabs. `animationDuration` (number) [optional, default: 250] Duration (ms) for indicator animations when `animated` is true. - … 16 more props documented ### Demos - Basics | (id: Tabs.basic) | tags: tabs Render the default tabs experience with inline content and a short caption explaining the pattern. ```tsx import { Column, Tabs, Text } from '@platform-blocks/ui'; const ITEMS = [ { key: 'overview', label: 'Overview', content: High-level summary and entry point. }, { key: 'details', label: 'Details', content: Deeper dive into metrics and configuration. }, { key: 'activity', label: 'Activity', content: Recent events, tasks, and notifications. } ]; return ( Tabs render inline content directly below the active trigger by default. ); } ``` - Animated transitions | (id: Tabs.animated) | tags: animation, tabs Demonstrates motion-enabled tabs using the `animated` flag and custom duration to smooth the switch between panels. ```tsx import { Column, Tabs, Text } from '@platform-blocks/ui'; const ITEMS = [ { key: 'home', label: 'Home', content: ( Welcome back Animated transitions ease between dashboard sections and reinforce context shifts. ) }, { key: 'analytics', label: 'Analytics', content: ( Analytics overview Surface key charts and KPIs while the motion guides attention to new content. ) }, { key: 'settings', label: 'Settings', content: ( Account settings Manage notifications, billing, and other preferences without abrupt content swaps. ) } ]; return ( Enable `animated` to add motion and use `animationDuration` to moderate the easing speed. ); } ``` ## Component: Text component provides consistent typography with various variants, colors, and styling options for displaying content. ### Props `children` (React.ReactNode) [optional] Text node children. Optional if using translation via `tx`. `tx` (string) [optional] Translation key (if provided, overrides children when found) `txParams` (Record) [optional] Params for translation interpolation `variant` (HTMLTextVariant) [optional] Text variant (mirrors semantic HTML tags) `size` (SizeValue) [optional] Size can be a size token or number (overrides variant fontSize) `color` (string) [optional] Text color (overrides theme text color) `colorVariant` ('primary' | 'secondary' | 'muted' | 'disabled' | 'link' | 'success' | 'warning' | 'error' | 'info') [optional] Semantic color variant (overrides color prop). Supports text palette plus status colors `weight` ('normal' | 'medium' | 'semibold' | 'bold' | 'light' | 'black' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900' | number) [optional] Font weight (supports all CSS font-weight values) `align` ('left' | 'center' | 'right' | 'justify') [optional] Text alignment `lineHeight` (number) [optional] Line height as a multiplier (e.g., 1.5) or absolute value `tracking` (number) [optional] Letter spacing (tracking) in pixels or em units `uppercase` (boolean) [optional] Convert text to uppercase - … 9 more props documented ### Demos - Basic Usage | (id: Text.basic) | tags: basic, typography Basic text usage with different variants and semantic elements. ```tsx import { Card, Column, Text } from '@platform-blocks/ui'; return ( Heading 1 Heading 2 Heading 3 Heading 4 Heading 5 Heading 6 Body text is the default variant for paragraphs. Caption text keeps supporting details readable. Small text works well for fine print or metadata. Default text without a variant falls back to body styling. This paragraph shows natural wrapping behavior when the content spans multiple lines in a layout. ); } ``` - Colors | (id: Text.colors) | tags: colors, theming Text color variants and custom color options for different contexts. ```tsx import { Card, Column, Text } from '@platform-blocks/ui'; return ( Semantic colors Primary color text Secondary color text Muted color text Disabled color text Link color text Custom palette Custom red text Custom teal text Custom blue text Custom green text Custom yellow text ); } ``` ## Component: