/** * Icon — the shared icon primitive and controlled vocabulary (arch 09 §5). * * Astrolabe is label-first: an icon is added only when it does real work, usually * alongside text (arch 09 §5.1). This module is the single source of truth for the * icon set — one glyph per meaning, app-wide (§5.2). Adding an icon means adding a * `IconName` + a registry entry here, never inlining an SVG in a component. * * The glyphs are traced from IBM Carbon's icon set (32×32 grid, fill-based — they * read as outlines but are filled shapes). Carbon is inspiration, not a dependency. * Every glyph draws with `fill: currentColor`, so it inherits its text colour and * themes for free. * * Icons are decorative by default (`aria-hidden`): the control around them carries * the accessible name (text label, or an `aria-label` on an icon-only button — APG * button pattern). Never rely on an icon alone to name a control. */ import type { ReactNode } from 'react'; import styles from './Icon.module.css'; /** The controlled icon vocabulary (arch 09 §5.2). One entry = one meaning. */ export type IconName = | 'close' // close / dismiss (universal, icon-only) | 'moon' // theme: switch to dark (universal, icon-only) | 'sun' // theme: switch to light (universal, icon-only) | 'dataset' // "references a dataset" — Carbon DataTable | 'delete' // delete — Carbon TrashCan | 'add' // add / create-new — Carbon Add | 'chart' // build / open a chart — rising columns on a baseline (pane-icon style) | 'search' // live library search — Carbon Search | 'settings' // per-pane settings disclosure (gear) — Carbon Settings | 'import' // import a workspace file — Carbon Upload (a file goes in) | 'export' // export the workspace to a file — Carbon Download (a file comes out) | 'info' // about / information — Carbon Information (outline) | 'revert' // revert draft to last published — Carbon Reset | 'structure' // composition-structure wireframe disclosure (preview toolbar) — nested view blocks | 'layers' // layered-composition badge (wireframe) — two stacked planes (one shared space) // Mark sub-family (composition wireframe leaves) — a simplified glyph of a unit // view's mark type, so which-is-which reads at a glance. Vega-Lite mark synonyms // collapse onto these via `markIconName` (CompositionWireframe); unknown → generic. | 'mark-bar' | 'mark-line' | 'mark-area' | 'mark-point' | 'mark-arc' | 'mark-rect' | 'mark-tick' | 'mark-rule' | 'mark-text' | 'mark-generic' // Pane-toggle sub-family (spec §01A): a panel frame with one region filled, so the // glyph shows *which* pane it controls by position (left / centre / right). | 'pane-library' // toggle the library pane (left) | 'pane-editor' // toggle the editor pane (centre) | 'pane-preview' // toggle the preview pane (right) // Status sub-family (arch 09 §5.2) — Carbon's FILLED notification glyphs, coloured // by status (not text). A redundant non-colour severity channel (WCAG 1.4.1): the // triangle shape-codes warning apart from the round error/success/info. | 'status-error' // Carbon ErrorFilled | 'status-warning' // Carbon WarningAltFilled (triangle) | 'status-success' // Carbon CheckmarkFilled | 'status-info'; // Carbon InformationFilled /** Carbon icon scale (arch 09 §5.3). 16px (sm) is the default, paired to 14px body. */ type IconSize = 'sm' | 'md' | 'lg' | 'xl'; /** Inner SVG geometry per glyph, on Carbon's 0 0 32 32 grid. fill comes from CSS. */ const GLYPHS: Record = { close: ( ), add: , // Rising columns on a baseline — drawn in the pane-icon rect style (not a Carbon // trace) so it reads at 16px next to the pane glyphs. chart: ( <> ), // Carbon Search — magnifying glass, the active-search affordance (council SEARCH). search: ( ), settings: ( <> ), delete: ( <> ), // Carbon Upload — open tray with an up arrow (a file is brought into the app). import: ( <> ), // Carbon Download — open tray with a down arrow (a file is written out). export: ( <> ), // Carbon Information (outline) — ring + "i". info: ( <> ), // Carbon Reset — a circular arrow, "return to the prior state". revert: ( ), dataset: ( <> ), // Panel frame (x4–28 / y6–26, 2px border) with one third filled. The filled bar's // position maps to the pane: left = library, centre = editor, right = preview. 'pane-library': ( <> ), 'pane-editor': ( <> ), 'pane-preview': ( <> ), // Composition structure: a panel frame (matching the pane family) holding nested // view blocks — two side by side over one wide — the wireframe in miniature. structure: ( <> ), // Two offset planes — a layer is one space with several marks stacked in z-order. layers: ( <> ), // Mark glyphs: simplified renderings of each Vega-Lite mark, drawn on the same // 32-grid. Stroke-based where a line reads truer than a fill (line/rule/generic). 'mark-bar': ( <> ), 'mark-line': ( ), 'mark-area': , 'mark-point': ( <> ), // A three-quarter pie wedge (one quadrant empty) reads as arc/pie at a glance. 'mark-arc': , 'mark-rect': ( <> ), 'mark-tick': ( <> ), 'mark-rule': ( ), 'mark-text': ( <> ), 'mark-generic': ( ), moon: ( ), sun: ( <> ), // Filled status glyphs: the symbol is a winding-rule knockout in the solid shape, // so it shows the surface colour through (Carbon's filled notification family). 'status-error': ( ), 'status-warning': ( ), 'status-success': ( ), 'status-info': ( ), }; export interface IconProps { name: IconName; /** Carbon scale; defaults to `sm` (16px) — the body-text default. */ size?: IconSize; /** Extra class for layout-level overrides (margins, colour). */ className?: string; } export function Icon({ name, size = 'sm', className }: IconProps) { return ( ); }