# OGE UI — full reference > Signal-based Angular UI component suite for data-heavy applications: a virtualized Data Grid, Tree List, Pivot Grid, BPMN editor, form editors, buttons, overlay surfaces (modal and toast), tabs and layout containers. Angular 22+, standalone components only, zoneless, zero runtime dependencies. MIT licensed, except @oge-ui/pivot and @oge-ui/bpmn which are commercial. Version 0.12.0. Generated from the OGE UI source tree: the API tables and demo sources below are the same ones https://ogeui.com renders. Index: https://ogeui.com/llms.txt ## Contents - `@oge-ui/grid` — Data Grid - `@oge-ui/tree-list` — Tree List - `@oge-ui/inputs` — Inputs - `@oge-ui/buttons` — Buttons - `@oge-ui/overlay` — Overlay - `@oge-ui/tabs` — Tabs - `@oge-ui/layout` — Layout - `@oge-ui/forms` — Forms - `@oge-ui/upload` — Upload - `@oge-ui/navigation` — Navigation - `@oge-ui/pivot` — Pivot Grid - `@oge-ui/bpmn` — BPMN Editor - `@oge-ui/charts` — Charts - `@oge-ui/gantt` — Gantt - `@oge-ui/kanban` — Kanban - `@oge-ui/scheduler` — Scheduler - `@oge-ui/core` — Core - `@oge-ui/react-buttons` — Buttons (React) - `@oge-ui/react-inputs` — Inputs (React) - `@oge-ui/react-tabs` — Tabs (React) - `@oge-ui/react-layout` — Layout (React) - `@oge-ui/react-navigation` — Navigation (React) - `@oge-ui/react-overlay` — Overlay (React) - `@oge-ui/behavior` — Behavior - `@oge-ui/react` — Umbrella package (React) - `oge-ui` — Umbrella package ## Install ```sh npm i oge-ui # every MIT family behind one import path npm i @oge-ui/grid # …or one family at a time ``` Requires Angular >= 22 and Node >= 22.22. Standalone and zoneless applications are fully supported; nothing depends on `zone.js` or NgModules. Component styles ship inside the components, so **no global stylesheet is required** and the light theme is built in. Shared engines (`@oge-ui/core`, `@oge-ui/overlay`) install automatically as dependencies. Export features live in secondary entry points so their libraries stay out of the bundle until used: `@oge-ui/grid/export-excel` and `@oge-ui/tree-list/export-excel` need `exceljs`, `@oge-ui/grid/export-pdf` needs `jspdf`. CSV export is built in. ## Writing OGE code Follow these rules and generated code compiles on the first try. 1. **Standalone only.** There are no NgModules and no `Oge*Module` symbols. Import the component class and list it in the host component's `imports` array: `imports: [OgeGrid, OgeColumn]`. 2. **Signal APIs, never decorators.** Public members are `input()`, `input.required()`, `model()` and `output()` — not `@Input()`/`@Output()`. Read them as signals in TypeScript (`grid.selectedKeys()`). 3. **Two-way state is `model()`.** Bind with the banana box against a signal: `[(selectedKeys)]="keys"`. `ngModel` is supported by the editors in `@oge-ui/inputs` for reactive/template forms, but signal binding is the idiomatic form. 4. **Modes are string unions, never enums.** Write the literal: `selectionMode="multiple"`, `editMode="batch"`, `stylingMode="outlined"`, `severity="danger"`. There is no `OgeSelectionMode.Multiple`. 5. **Outputs are past tense with no `on` prefix** — `(rowClick)`, `(selectionChanged)`, `(savedChanges)`, `(clicked)`. Never `(onRowClick)`. 6. **`-ing` outputs are cancelable.** Events like `rowInserting`, `rowUpdating`, `rowRemoving`, `savingChanges`, `exporting`, the modal's `opening`/`closing` and the tab strip's `selectionChanging` carry a mutable `cancel: boolean` — set `event.cancel = true` to veto, and the matching past-tense event never fires. 7. **App-wide defaults and every user-facing string** come from a provider: `provideOgeGridConfig()`, `provideOgeInputsConfig()`, `provideOgeButtonsConfig()`, `provideOgeOverlayConfig()`, `provideOgeTabsConfig()`, `provideOgeAccordionConfig()`. Each takes a `messages` block — that is how localization works; there is no i18n dependency. 8. **Styling is CSS custom properties**, all prefixed `--oge-`. Never target internal class names to change colors or sizing; override the token. 9. **Templates and slots are structural directives on the markup you want**, not `` wrappers: ``, `
`. ### Theming ```css :root { --oge-accent: #4f46e5; /* selection, focus, primary actions */ --oge-radius-lg: 10px; --oge-row-height: 32px; /* grid & tree-list density */ } ``` Optional stylesheets, imported once: `@oge-ui/grid/themes/dark.css` (then put `class="oge-theme-dark"` on `` or any subtree), `@oge-ui/grid/themes/tailwind.css` and `@oge-ui/grid/themes/bootstrap.css` (bridge `--oge-*` onto an existing design system). ## Common mistakes Predictable wrong guesses, and what to write instead. | Wrong | Right | | --- | --- | | `` | `` | | `[dataSource]="rows"` | `[data]="rows"` | | `import { OgeGridModule }` | no modules — `imports: [OgeGrid, OgeColumn]` | | `@Input() foo` on an OGE component | `readonly foo = input()` | | `(onRowClick)` / `(onSelectionChanged)` | `(rowClick)` / `(selectionChanged)` | | `selectionMode="[SelectionMode.Multiple]"` | `selectionMode="multiple"` | | `` | `` | | `MatDialog` / `DialogService` | `` or `OgeModalService.open()` | | `MessageService` / `ToastrService` / `MatSnackBar` | `OgeToastService.show()` (plus `success/info/warning/error`) | | `` / `` | `` (`` when multiple) | | `::ng-deep .oge-grid-row { … }` | override a `--oge-*` token | | importing a theme to get default styles | not needed — styles ship with the components | ## Guides ### Overview — Agents block ```ts ## UI components — OGE UI This project uses **OGE UI** for its UI. Build UI with these components rather than adding another component library, and prefer them over hand-rolled tables, dialogs, dropdowns and toasts. | Need | Use | | --- | --- | | data table (sort, filter, group, edit, export) | `` with `` children | **Conventions** … **Full API reference** — read this before guessing at an API: - `node_modules/@oge-ui/grid/llms.txt` - ``` ### Overview — Fetch ```ts # the index: packages, every documentation page, one line each curl https://ogeui.com/llms.txt # everything inlined — conventions, every API member, every demo source curl https://ogeui.com/llms-full.txt # one package only curl https://ogeui.com/llms/grid.txt # already on disk after npm install cat node_modules/@oge-ui/grid/llms.txt ``` ### Overview — Ng add ```ts # installs the package, optionally registers a theme, and writes # an OGE usage block into your AGENTS.md ng add @oge-ui/grid # opt out of the AGENTS.md block ng add @oge-ui/grid --skip-agents-file ``` ### Overview — Rules ```ts // 1. Standalone only — no NgModules @Component({ imports: [OgeGrid, OgeColumn], /* … */ }) // 2. Signal APIs, never decorators readonly rows = input([]); // not @Input() // 3. Two-way state binds to a signal // 4. Modes are string unions, never enums // 5. Outputs are past tense with no "on" prefix // 6. "-ing" outputs are cancelable onRowUpdating(e: OgeRowUpdatingEvent) { e.cancel = true; } // 7. Defaults and every user-facing string come from a provider provideOgeGridConfig({ rowHeight: 32, messages: { noData: 'Veri yok' } }) ``` ### Getting started — Install ```ts # everything at once — one install, one import path npm install oge-ui # …or install only what you use — every package is standalone npm install @oge-ui/grid # data grid (+ @oge-ui/core) npm install @oge-ui/tree-list # hierarchical grid npm install @oge-ui/pivot # pivot table npm install @oge-ui/buttons # buttons, groups, drop-downs (+ @oge-ui/overlay) npm install @oge-ui/inputs # text, textarea, number and select editors ``` ### Getting started — Install react intro ```ts # everything at once — one install, one import path npm install @oge-ui/react # …or install only what you use — every package is standalone npm install @oge-ui/react-buttons # buttons, groups, drop-downs npm install @oge-ui/react-inputs # text, number, select, date, color editors npm install @oge-ui/react-tabs # tab strip and tab panel npm install @oge-ui/react-layout # card, accordion, splitter, toolbar, loaders npm install @oge-ui/react-navigation # tree view, drawer, stepper, menubar, breadcrumb npm install @oge-ui/react-overlay # anchored popups and menus ``` ### Getting started — Quick start ```ts import { Component, signal } from '@angular/core'; import { OgeButton } from '@oge-ui/buttons'; import { OgeTextBox } from '@oge-ui/inputs'; @Component({ selector: 'app-search-bar', imports: [OgeTextBox, OgeButton], template: ` `, }) export class SearchBar { readonly query = signal(''); // async action: the button manages its own loading spinner readonly load = () => fetch('/api/search?q=' + this.query()); } ``` ### Getting started — Quick start react ```ts 'use client'; import { useState } from 'react'; import { OgeButton, OgeTextBox } from '@oge-ui/react'; export function SearchBar() { const [query, setQuery] = useState(''); // async action: the button manages its own loading spinner const load = () => fetch('/api/search?q=' + query); return ( <> ); } ``` ### Localization — Behavior ```ts // The same providers also carry non-text defaults: provideOgeButtonsConfig({ clickGuardMs: 300, // default clickGuard window holdToConfirmMs: 1000, // default hold duration }), provideOgeInputsConfig({ spinRepeatDelayMs: 300, // number-box spin: delay before repeating spinRepeatIntervalMs: 60, copiedResetMs: 1500, // "copied" indicator duration }) ``` ### Localization — Global ```ts import { provideOgeGridConfig } from '@oge-ui/grid'; import { provideOgeInputsConfig } from '@oge-ui/inputs'; import { provideOgeButtonsConfig } from '@oge-ui/buttons'; // app.config.ts — a Turkish application providers: [ provideOgeGridConfig({ messages: { noData: 'Kayıt bulunamadı', search: 'Ara…', rowsSuffix: 'satır', summaryLabels: { sum: 'Toplam', avg: 'Ort', min: 'Min', max: 'Maks', count: 'Adet' }, }, }), provideOgeInputsConfig({ messages: { requiredError: 'Bu alan zorunludur', clearButton: 'Temizle', counterAria: '{max} karakterden {count} tanesi kullanıldı', }, }), provideOgeButtonsConfig({ messages: { loading: 'Yükleniyor', holdToConfirm: 'Onaylamak için basılı tutun' }, }), ] ``` ### Localization — Number locale ```ts ``` ### Localization — Per component ```ts ``` ### Localization — Validation ```ts // Message patterns interpolate the constraint that failed: provideOgeInputsConfig({ messages: { minError: 'Value must be at least {min}', maxLengthError: 'Enter no more than {requiredLength} characters', }, }) // Priority when an editor resolves its error text: // 1. errorText input (always wins when set) // 2. parse errors (e.g. invalid number) // 3. form errors (Signal Forms / reactive), mapped through the catalog ``` ### Setup — Install ```ts # everything at once — one install, one import path npm install oge-ui # …or install only what you use — every package is standalone npm install @oge-ui/grid # data grid (+ @oge-ui/core) npm install @oge-ui/tree-list # hierarchical grid npm install @oge-ui/pivot # pivot table npm install @oge-ui/buttons # buttons, groups, drop-downs (+ @oge-ui/overlay) npm install @oge-ui/inputs # text, textarea, number and select editors ``` ### Setup — Install react ```ts # the React render layer — one package per family npm install @oge-ui/react-buttons # react is a peer you already have # @oge-ui/behavior and @oge-ui/core come along as dependencies ``` ### Setup — Ng add ```ts # installs the package and wires the optional extras ng add @oge-ui/grid # with the dark theme registered in angular.json ng add @oge-ui/grid --theme=dark # without touching AGENTS.md ng add @oge-ui/inputs --skip-agents-file ``` ### Setup — Optional ```ts # Excel export (grid + tree list secondary entries) npm install exceljs # PDF export (grid secondary entry) npm install jspdf ``` ### Setup — Providers ```ts import { ApplicationConfig } from '@angular/core'; import { provideOgeGridConfig } from '@oge-ui/grid'; import { provideOgeInputsConfig } from '@oge-ui/inputs'; export const appConfig: ApplicationConfig = { providers: [ // optional — components work with sensible defaults out of the box provideOgeGridConfig({ rowHeight: 32, allowUnsorting: false }), provideOgeInputsConfig({ spinRepeatDelayMs: 300 }), ], }; ``` ### Setup — Providers react ```ts 'use client'; import type { ReactNode } from 'react'; import { OgeButtonsConfigProvider } from '@oge-ui/react-buttons'; // optional — components work with sensible defaults out of the box. // The React counterpart of Angular's provideOgeButtonsConfig(). export function Providers({ children }: { children: ReactNode }) { return ( {children} ); } ``` ### Setup — Styles react ```ts // once, at your app entry — the components ship class names, not inline styles import '@oge-ui/react-buttons/styles.css'; // optional themes, shared with the Angular packages import '@oge-ui/grid/themes/dark.css'; ``` ### Setup — Verify ```ts import { Component } from '@angular/core'; import { OgeButton } from '@oge-ui/buttons'; @Component({ selector: 'app-root', imports: [OgeButton], template: ``, }) export class App {} ``` ### Setup — Verify react ```ts 'use client'; import { OgeButton } from '@oge-ui/react-buttons'; export function App() { return ; } ``` ### Styling — Bridge ```ts /* Bridge themes map --oge-* tokens onto your framework's variables, so components automatically follow your existing design system. */ @import '@oge-ui/grid/themes/tailwind.css'; /* Tailwind v4 */ @import '@oge-ui/grid/themes/bootstrap.css'; /* Bootstrap 5 */ ``` ### Styling — Colors ```ts ``` ### Styling — Dark ```ts /* Import once, then toggle a class — on for the whole app or on any subtree for a mixed page. */ @import '@oge-ui/grid/themes/dark.css'; ``` ### Styling — Dark html ```ts
``` ### Styling — Scoped ```ts /* Tokens cascade — scope them to re-skin a single area. */ .compact-dashboard { --oge-row-height: 26px; --oge-radius-lg: 6px; } /* Or a single component instance */ .danger-zone oge-button { --oge-accent: var(--oge-danger); } ``` ### Styling — Tokens ```ts /* One override restyles every component consistently. */ :root { --oge-accent: #4f46e5; /* selection, focus, primary actions */ --oge-radius-lg: 10px; /* cards, popups, buttons */ --oge-row-height: 32px; /* grid & tree-list row density */ --oge-input-width: 240px; /* default editor width */ --oge-header-bg: #eef2f8; /* grid header surface */ } ``` ## @oge-ui/grid Virtualized data grid: 100k+ rows, sorting, filtering, grouping with summaries, inline/batch/form editing, selection, master-detail, state persistence and CSV/Excel/PDF export. Docs: https://ogeui.com/components/data-grid ### Entry points `@oge-ui/grid` - values: `ColumnsSlice`, `ExpansionSlice`, `FilterSlice`, `GridDataAdapter`, `GridStateStore`, `GroupingSlice`, `OGE_DEFAULT_GRID_CONFIG`, `OGE_DEFAULT_MESSAGES`, `OGE_GRID_CONFIG`, `OGE_STATE_STORAGE`, `OgeCellEditor`, `OgeCellTemplate`, `OgeColumn`, `OgeColumnGroup`, `OgeDetailTemplate`, `OgeEditTemplate`, `OgeEditingSlice`, `OgeFilterBuilderGroup`, `OgeGrid`, `OgeGridToolbarItem`, `OgeHeaderTemplate`, `OgeNoDataTemplate`, `OgePager`, `OgeRowTemplate`, `PagingSlice`, `SelectionSlice`, `SortSlice`, `builderToExpr`, `describeExpr`, `exprToBuilder`, `formatCellValue`, `operatorsFor`, `provideOgeGridConfig` - types: `OgeBuilderCondition`, `OgeBuilderGroup`, `OgeCellClickEvent`, `OgeCellEditorSurface`, `OgeCellTemplateContext`, `OgeColumnDef`, `OgeColumnLookup`, `OgeCommandButton`, `OgeContextMenuEvent`, `OgeDataChange`, `OgeDataErrorEvent`, `OgeDataType`, `OgeDetailTemplateContext`, `OgeEditFormItem`, `OgeEditMode`, `OgeEditTemplateContext`, `OgeEditingOptions`, `OgeEditingStartEvent`, `OgeExportCellArgs`, `OgeExportColumn`, `OgeExportData`, `OgeExportOptions`, `OgeExportingEvent`, `OgeFilterBuilderField`, `OgeFilterRowOptions`, `OgeFocusedRowChangedEvent`, `OgeGridConfig`, `OgeGridConfigInput`, `OgeGridMessages`, `OgeGroupingOptions`, `OgeHeaderContextMenuEvent`, `OgeHeaderFilterOptions`, `OgeHeaderTemplateContext`, `OgeInitNewRowEvent`, `OgeMenuItem`, `OgePagingOptions`, `OgeRowClickEvent`, `OgeRowInsertedEvent`, `OgeRowInsertingEvent`, `OgeRowRemovedEvent`, `OgeRowRemovingEvent`, `OgeRowReorderedEvent`, `OgeRowTemplateContext`, `OgeRowUpdatedEvent`, `OgeRowUpdatingEvent`, `OgeSavedChangesEvent`, `OgeSavingChangesEvent`, `OgeScrollingOptions`, `OgeSearchPanelOptions`, `OgeSelectionChangedEvent`, `OgeSelectionMode`, `OgeSortingOptions`, `OgeStateStorage` `@oge-ui/grid/export-pdf` - values: `buildPdfDocument`, `exportGridToPdf` - types: `OgePdfExportOptions` `@oge-ui/grid/foundation` - values: `CHECKBOX_WIDTH`, `COMMAND_WIDTH`, `ColumnLayoutModel`, `ColumnModel`, `DRAG_WIDTH`, `DeferredChildrenLoader`, `EXPANDER_WIDTH`, `EditingModel`, `KeyboardNavModel`, `OGE_STATE_STORAGE`, `OgeEditingSlice`, `RowVirtualizerModel`, `buildRowFilterExpr`, `createStatePersistence`, `dateFilterExpr`, `defaultOperatorFor`, `humanize`, `isDataSource`, `lookupTextOf`, `mapLookupItems`, `resolveLookupItems` - types: `ColumnDefLike`, `ColumnLayoutModelDeps`, `ColumnModelDeps`, `ColumnSource`, `DeferredBaseOptions`, `DeferredChildrenLoaderDeps`, `EditingModelDeps`, `KeyboardNavModelDeps`, `KeyboardNavTreeHooks`, `LookupItem`, `OgeColumnLookup`, `OgeDataChange`, `OgeDataType`, `OgeEditFormItem`, `OgeEditMode`, `OgeEditingOptions`, `OgeEditingStartEvent`, `OgeRowInsertedEvent`, `OgeRowInsertingEvent`, `OgeRowRemovedEvent`, `OgeRowRemovingEvent`, `OgeRowUpdatedEvent`, `OgeRowUpdatingEvent`, `OgeSavedChangesEvent`, `OgeSavingChangesEvent`, `OgeStateStorage`, `PendingChildRequest`, `ResolvedColumn`, `RowVirtualizerModelDeps`, `RowVirtualizerWindowAdapter`, `StatePersistenceOptions` `@oge-ui/grid/export-excel` - values: `buildExcelWorkbook`, `exportGridToExcel` - types: `OgeExcelExportOptions` ### OgeGrid — `` #### Properties _Data & columns_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `data` | `readonly T[] \| DataSource` | `[]` | Rows to render: a static array or any DataSource implementation (remote, OData…). | | `columns` | `readonly (string \| OgeColumnDef)[] \| undefined` | `—` | Programmatic columns; used only when no declarative `` children exist. When both are absent, columns derive from the first row. | | `keyField` | `keyof T \| ((row: T) => RowKey) \| undefined` | `—` | Field (or selector) producing a stable row key; falls back to the row index. | _Sorting, filtering & search_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `sortable` | `boolean \| 'single' \| 'multi'` | `'multi'` | `false` disables sorting; `'single'` restricts to one column. | | `sorting` | `OgeSortingOptions \| undefined` | `—` | Sorting options; overrides the `sortable` shorthand. | | `filterRow` | `boolean \| OgeFilterRowOptions` | `false` | Per-column filter editors below the header. | | `headerFilter` | `boolean \| OgeHeaderFilterOptions` | `false` | Excel-style distinct-value filter button in headers. | | `searchPanel` | `boolean \| OgeSearchPanelOptions` | `false` | Global search box above the grid. | | `filterPanel` | `boolean` | `false` | Filter panel bar with the filter-builder entry point. | | `filterValue` | `model` | `null` | Two-way binding of the builder/programmatic filter expression. | | `filterDebounce` | `number \| undefined` | `—` | Debounce for text filter inputs, in ms. Set to `0` in tests. | _Paging & scrolling_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `paging` | `false \| OgePagingOptions` | `false` | Client/server paging with the built-in pager. | | `virtualScroll` | `boolean` | `false` | Renders only the rows inside the scroll viewport (plus overscan). Needs a bounded height. | | `scrolling` | `OgeScrollingOptions \| undefined` | `—` | Scrolling options (`standard/virtual/infinite`, remote windowing, column virtualization); overrides the shorthand. | | `rowHeight` | `number \| undefined` | `—` | Fixed row height in px used by the virtualizer. Defaults from global config. | | `autoRowHeight` | `boolean` | `false` | Measures real row heights with scroll anchoring. Virtual mode only. | | `detailRowHeight` | `number \| undefined` | `—` | Height assumed for expanded master-detail rows in virtual mode. | | `overscan` | `number \| undefined` | `—` | Extra rows rendered above/below the virtual window. | _Grouping_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `groupPanel` | `boolean` | `false` | Drop area for drag-and-drop row grouping. | | `groupBy` | `readonly string[] \| undefined` | `—` | Initial/programmatic grouping by field names. | | `grouping` | `OgeGroupingOptions \| undefined` | `—` | `autoExpandAll: false` starts collapsed and enables deferred child loading. | _Selection & focus_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `selectionMode` | `OgeSelectionMode` | `'none'` | Row selection: none \| single \| multiple (ctrl/shift) \| checkbox column. | | `selectedKeys` | `model` | `[]` | Two-way binding of the selected row keys. | | `selectAllMode` | `'allPages' \| 'page'` | `'allPages'` | Header select-all scope. | | `selectionDeferred` | `boolean` | `false` | Selection tracked as a serializable `selectionFilter` expression — no key set. Requires a string `keyField`. | | `selectionFilter` | `model` | `null` | Two-way selection expression (deferred mode). | | `focusedRowEnabled` | `boolean` | `false` | Highlights and tracks a single focused row. | | `focusedRowKey` | `model` | `null` | Two-way binding of the focused row's key. | _Editing & rows_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `editing` | `false \| OgeEditingOptions` | `false` | Enables editing: `{ mode: 'cell' \| 'row' \| 'batch' \| 'popup' \| 'form', allow… }`. | | `commandButtons` | `readonly OgeCommandButton[] \| undefined` | `—` | Customizes the trailing command column: built-in 'edit'/'delete' plus custom buttons with per-row `visible`. | | `rowDragging` | `boolean` | `false` | Drag-handle column for reordering rows. Arrays mutate in place; DataSources handle `rowReordered`. | _Columns UX_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `columnChooser` | `boolean` | `false` | Column visibility chooser button. | | `columnResize` | `boolean` | `true` | Drag-resize handles on header edges. | | `columnReorder` | `boolean` | `true` | Drag-and-drop column reordering. | | `columnMinWidth` | `number \| undefined` | `—` | Track minimum for columns without an explicit width. | _Appearance & misc_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `stateKey` | `string \| undefined` | `—` | Persists user state (sort, filters, grouping, layout) via `OGE_STATE_STORAGE`. | | `messages` | `Partial \| undefined` | `—` | Per-grid overrides of the UI strings. | | `loadPanel` | `boolean` | `false` | Spinner overlay while a load is in flight. | | `wordWrap` | `boolean` | `false` | Cells wrap instead of truncating. | | `rowAlternation` | `boolean` | `false` | Zebra striping, stable under virtualization. | | `highlightChanges` | `boolean` | `false` | Briefly flashes cells patched by push updates. | | `rtlEnabled` | `boolean \| undefined` | `—` | `undefined` auto-detects the inherited CSS `direction`. | #### Methods _Data & view_ | Name | Type | Description | | --- | --- | --- | | `refresh(): void` | `void` | Re-runs the current load against the DataSource. | | `getVisibleRows(): readonly T[]` | `readonly T[]` | Data rows of the currently rendered page, in display order. | | `getRowByKey(key: RowKey): T \| undefined` | `T \| undefined` | The loaded row carrying `key`, if currently rendered. | | `totalCount(): number` | `Signal` | Data row count of the current filtered set, across all pages. | _Navigation & expansion_ | Name | Type | Description | | --- | --- | --- | | `scrollToRow(target: number \| RowKey): void` | `void` | Scrolls a row into the viewport by flat index or key. | | `navigateToRow(key: RowKey): void` | `void` | Scrolls to the row and focuses it when `focusedRowEnabled`. | | `expandRow(key) / collapseRow(key)` | `void` | Expands/collapses a group row (group node key) or master-detail row. | | `isRowExpanded(key): boolean` | `boolean` | Expansion state of a group or master-detail row. | | `expandAllGroups() / collapseAllGroups()` | `void` | Expands/collapses every group row (all levels). | _Selection_ | Name | Type | Description | | --- | --- | --- | | `selectAll(): void` | `void` | Selects the current filtered set; honors `selectAllMode` and deferred mode. | | `deselectAll() / clearSelection()` | `void` | Clears the selection (deferred mode: resets `selectionFilter`). | | `isRowSelected(key): boolean` | `boolean` | Whether the row is currently selected. | | `getSelectedRowsData(): T[]` | `T[]` | Data of the selected rows among the loaded rows. | | `copyToClipboard(): Promise` | `Promise` | Copies the selected rows (or the focused cell) as tab-separated values. | _Editing_ | Name | Type | Description | | --- | --- | --- | | `addRow(): void` | `void` | Adds a new (unsaved) row and opens its editors; `initNewRow` can prefill. Requires `allowAdding`. | | `editRow(key: RowKey): void` | `void` | Opens the row editor (row/form/popup modes). Requires `allowUpdating`. | | `deleteRow(key: RowKey): void` | `void` | Deletes the row: staged in batch mode (toggle = undelete), saved immediately otherwise. Requires `allowDeleting`. | | `saveChanges(): void` | `void` | Commits the open editor and (batch) saves the staged change set. `savingChanges` can cancel. | | `discardChanges(): void` | `void` | Discards pending changes and closes any open editor; emits `editCanceled`. | | `hasChanges(): boolean` | `boolean` | Whether unsaved edits exist. | _Paging_ | Name | Type | Description | | --- | --- | --- | | `pageIndex(): number / setPageIndex(index)` | `number / void` | Zero-based page getter / clamped setter. | | `pageSize(): number / setPageSize(size)` | `number / void` | Page size getter / setter; `0` turns paging off. | | `pageCount(): number` | `Signal` | Number of pages; `1` when paging is off. | _Loading, state & export_ | Name | Type | Description | | --- | --- | --- | | `beginCustomLoading(message?) / endCustomLoading()` | `void` | Shows/hides the load panel with an optional message — independent of data activity. | | `state(): GridStateSnapshot / applyState(snapshot)` | `GridStateSnapshot / void` | Captures / applies the persistable UI state. | | `clearFilters() / clearSorting()` | `void` | Clears every filter (row, header, builder, search) / the sort order. | | `getExportData(options?): Promise>` | `Promise` | Rows + column metadata of the current view; `scope: 'all' \| 'page' \| 'selection'`. | | `getCsv(options?): Promise` | `Promise` | CSV of the current view. | | `exportCsv(filename = 'grid.csv'): Promise` | `Promise` | Downloads the current view as CSV; fires the cancelable `exporting` event first. | #### Events _Interaction_ | Name | Type | Description | | --- | --- | --- | | `rowClick / rowDblClick` | `OgeRowClickEvent` | `{ row, key, event }`. | | `cellClick / cellDblClick` | `OgeCellClickEvent` | `{ row, key, field, value, event }`. | | `rowContextMenu` | `OgeContextMenuEvent` | Row right-click; push into `items` to open the built-in menu. | | `headerContextMenu` | `OgeHeaderContextMenuEvent` | Prebuilt, mutable header menu items (sort/group/pin/hide). | | `rowReordered` | `OgeRowReorderedEvent` | A row was dropped in a new position (`rowDragging`). | _Selection & focus_ | Name | Type | Description | | --- | --- | --- | | `selectionChanged` | `OgeSelectionChangedEvent` | `{ selectedKeys, addedKeys, removedKeys }` after every selection change. | | `focusedRowChanged` | `OgeFocusedRowChangedEvent` | `{ key, row }` after the focused row changed. | | `selectedKeysChange / focusedRowKeyChange / filterValueChange / selectionFilterChange` | `model outputs` | Implicit outputs of the two-way models. | _Editing lifecycle_ | Name | Type | Description | | --- | --- | --- | | `editingStart` | `OgeEditingStartEvent` | Cancelable — before a cell or row editor opens. | | `initNewRow` | `OgeInitNewRowEvent` | Write into `values` to prefill rows created by `addRow()`. | | `rowInserting / rowInserted` | `OgeRowInserting/-edEvent` | Around each DataSource insert; `rowInserting` cancelable. | | `rowUpdating / rowUpdated` | `OgeRowUpdating/-edEvent` | Around each DataSource update; `rowUpdating` cancelable (carries `row` + `values`). | | `rowRemoving / rowRemoved` | `OgeRowRemoving/-edEvent` | Around each DataSource remove; `rowRemoving` cancelable. | | `savingChanges / savedChanges` | `OgeSaving/-edChangesEvent` | Whole batch before (cancelable) / after the save. | | `editCanceled` | `void` | An edit session ended without saving. | _Lifecycle & errors_ | Name | Type | Description | | --- | --- | --- | | `contentReady` | `void` | A new result set finished rendering (post-render notification). | | `stateChange` | `GridStateSnapshot` | Debounced — the persistable UI state changed. | | `exporting` | `OgeExportingEvent` | Cancelable, mutable `fileName` — before a CSV export. | | `dataErrorOccurred` | `OgeDataErrorEvent` | `{ error }` — a DataSource load or save failed. | ### OgeColumn — `` #### Properties _Companion directives_ | Name | Type | Description | | --- | --- | --- | | `OgeColumnGroup` | `oge-column-group — input: caption (required)` | Banded header: wraps sibling `` elements under one shared caption. Re-exported by `@oge-ui/tree-list`. | | `OgeGridToolbarItem` | `directive — [ogeToolbar]` | Marks projected content as a toolbar item. The toolbar appears as soon as one item exists, alongside the built-in controls. Named `OgeGridToolbarItem` so it cannot collide with `@oge-ui/layout`’s `OgeToolbarItem`; the selector is unchanged. | _Basics_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `field` | `string \| undefined` | `—` | Data field (dot paths supported via accessors). | | `caption` | `string \| undefined` | `—` | Header text; humanized from `field` when omitted. | | `dataType` | `OgeDataType` | `'string'` | 'string' \| 'number' \| 'date' \| 'boolean' — drives editors, filters and alignment. | | `width / minWidth` | `number \| string / number` | `—` | Track size; `minWidth` guards resizing. | | `visible` | `model` | `true` | Two-way visibility (column chooser writes it). | | `format` | `(value: unknown) => string \| undefined` | `—` | Display formatter for cells, group rows, export. | | `pinned` | `false \| 'left' \| 'right'` | `false` | Pins the column to an edge. | | `hidingPriority` | `number \| undefined` | `—` | Adaptive hiding order when width runs out (higher survives longer). | | `lookup` | `OgeColumnLookup \| undefined` | `—` | Display + dropdown editor from a value list; cascading via function dataSource. | _Sort, filter & group_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `sortable / filterable` | `boolean` | `true` | Per-column opt-outs. | | `sortOrder / sortIndex` | `'asc' \| 'desc' / number` | `—` | Initial sort (stateKey/user wins). | | `groupIndex` | `number \| undefined` | `—` | Initial grouping position. | | `filterOperator` | `FilterOperator \| undefined` | `—` | Initial operator of the filter-row cell. | | `calculateCellValue` | `(row: T) => unknown` | `—` | Calculated column value. | | `calculateSortValue` | `(row: T) => unknown` | `—` | Custom sort key. | | `calculateFilterExpression` | `(value, operator) => FilterExpr \| null` | `—` | Custom filter expression builder. | _Summaries & editing_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `groupSummary / totalSummary` | `SummaryType \| readonly SummaryType[]` | `—` | sum/avg/min/max/count/custom aggregates per column. | | `groupSummaryPosition` | `'row' \| 'footer'` | `'row'` | Group aggregates inline or in a footer row. | | `calculateCustomSummary` | `(rows: readonly T[]) => unknown` | `—` | Reducer for `type: 'custom'`. | | `editable` | `boolean` | `true` | Per-column editing opt-out. | | `required / validators` | `boolean / readonly ValidatorFn[]` | `—` | Editor validation. | #### Types | Name | Type | Description | | --- | --- | --- | | `` | `component` | Banded (multi-row) headers — wraps child ``s. | | `*ogeCellTemplate` | `OgeCellTemplateContext` | `{ $implicit: value, row, rowIndex, column }`. | | `*ogeHeaderTemplate` | `OgeHeaderTemplateContext` | `{ $implicit: column }`. | | `*ogeEditTemplate` | `OgeEditTemplateContext` | `{ $implicit: FormControl, row, column }`. | | `*ogeDetailTemplate` | `OgeDetailTemplateContext` | Master-detail content; `{ $implicit: row }`. | | `*ogeRowTemplate` | `OgeRowTemplateContext` | Full-row replacement; `{ $implicit: row, index, key }`. | | `*ogeNoDataTemplate` | `TemplateRef` | Custom empty state. | | `[ogeToolbar]` | `marker directive` | Projects custom controls into the grid toolbar. | ### Grid types & configuration #### Types _Standalone building blocks_ | Name | Type | Description | | --- | --- | --- | | `OgeCellEditor` | `oge-cell-editor — inputs: control (required), dataType, lookupItems, label, surface, invalid, errorTitle; outputs: enterKey, escapeKey, tabKey` | The editor the grid renders in a cell: picks the `dataType`-matched `oge-*-box` from `@oge-ui/inputs` and binds it to a reactive `FormControl`. Usable on its own to get grid-identical editing in a form. | | `OgePager` | `oge-pager — inputs: pageIndex, pageCount, totalCount (required), pageSize, pageSizes, showInfo, displayMode, messages; outputs: pageChange, pageSizeChange` | The grid's pager as a standalone component — reuse it under a list or a card grid so paging looks identical everywhere. | | `OgeFilterBuilderGroup` | `oge-filter-builder-group` | Recursive node of the filter builder (a group of conditions plus nested groups). Exported so a custom filter UI can reuse the same tree editor. | | `formatCellValue(value, dataType, format?)` | `(value: unknown, dataType: OgeDataType, format?: (value: unknown) => string) => string` | The exact formatting the grid applies to a cell. Use it to keep exports, tooltips or custom templates byte-identical with the rendered grid. | _Internals — not a supported API_ | Name | Type | Description | | --- | --- | --- | | `GridStateStore` | `component-scoped service` | Composes the state slices; `loadOptions` is the single choke point through which every data-affecting change triggers exactly one load. Injected by the grid, not by applications — use `state()` / `applyState()` instead. | | `GridDataAdapter` | `component-scoped service` | Bridges the reactive state to the pull-based `DataSource` contract with switchMap semantics, so a stale response can never win over a newer one. | | `SortSlice / FilterSlice / GroupingSlice / PagingSlice / ColumnsSlice / SelectionSlice / ExpansionSlice / OgeEditingSlice` | `state slices` | Read-only signals plus intent methods behind `GridStateStore`. Exported for the suite's own packages (tree-list, pivot) — treat them as internal: they may change in any release. | _Option objects (boolean shorthands stay valid)_ | Name | Type | Description | | --- | --- | --- | | `OgePagingOptions` | `{ pageSize: number; pageSizes?: readonly (number \| 'all')[]; showInfo?; displayMode?: 'full' \| 'compact' \| 'adaptive' }` | Pager configuration. | | `OgeSortingOptions` | `{ mode?: 'none' \| 'single' \| 'multi'; allowUnsorting?: boolean }` | Sorting behavior. | | `OgeFilterRowOptions` | `{ visible?: boolean; debounce?: number }` | Filter row. | | `OgeHeaderFilterOptions` | `{ visible?: boolean; valueLimit?: number }` | Header filter. | | `OgeSearchPanelOptions` | `{ visible?: boolean; placeholder?: string; width?: number }` | Search panel. | | `OgeScrollingOptions` | `{ mode?: 'standard' \| 'virtual' \| 'infinite'; remote?: boolean; columnRenderingMode?: 'standard' \| 'virtual' }` | Scrolling engine. | | `OgeGroupingOptions` | `{ autoExpandAll?: boolean }` | `false` starts collapsed and defers child loading. | | `OgeEditingOptions` | `{ mode: OgeEditMode; allowUpdating?; allowAdding?; allowDeleting?; confirmDelete?; formItems?; formColCount? }` | Editing configuration; `OgeEditMode` = cell \| row \| batch \| popup \| form. | | `OgeCommandButton` | `{ name?: 'edit' \| 'delete'; text?; onClick?(row, key); visible?(row) }` | Command column entries. | | `OgeColumnLookup` | `{ dataSource: readonly unknown[] \| ((row) => readonly unknown[]); valueExpr?; displayExpr? }` | Lookup source. | _Export_ | Name | Type | Description | | --- | --- | --- | | `OgeExportOptions` | `{ scope?: 'all' \| 'page' \| 'selection'; customizeCell?(args) }` | Shared by CSV/Excel/PDF. | | `OgeExportData / OgeExportColumn / OgeExportCellArgs` | `interfaces` | Rows + resolved column metadata handed to exporters. | | `exportGridToExcel(grid, options?)` | `@oge-ui/grid/export-excel` | Lazy Excel export (exceljs peer); `buildExcelWorkbook(data)` for custom pipelines. | | `exportGridToPdf(grid, options?)` | `@oge-ui/grid/export-pdf` | Lazy PDF export (jspdf peer); `buildPdfDocument(data)` for custom pipelines. | _Configuration_ | Name | Type | Description | | --- | --- | --- | | `provideOgeGridConfig(config)` | `Provider` | App/component-scoped defaults; deep-merges `messages`. | | `OgeGridConfig` | `{ rowHeight: 36; detailRowHeight: 200; filterDebounce: 300; overscan: 6; columnMinWidth: 120; pinnedDefaultWidth: 150; headerFilterValueLimit: 200; allowUnsorting: true; messages }` | Defaults shown inline. | | `OgeGridMessages` | `60+ string keys` | Every user-facing string, incl. aria labels, filter operators, summary patterns — see `OGE_DEFAULT_MESSAGES` in the source. | | `OGE_STATE_STORAGE / OgeStateStorage` | `InjectionToken` | Pluggable sync/async persistence backend for `stateKey`. | _Filter builder_ | Name | Type | Description | | --- | --- | --- | | `builderToExpr / exprToBuilder / describeExpr / operatorsFor` | `functions` | Convert between builder trees and `FilterExpr`; humanize expressions. | | `OgeBuilderGroup / OgeBuilderCondition / OgeFilterBuilderField` | `interfaces` | Filter-builder data model. | ### Demos Each demo below is a complete standalone component — copy one whole and it compiles. #### Columns ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeColumn, OgeColumnGroup, OgeGrid } from '@oge-ui/grid'; @Component({ selector: 'demo-root', imports: [OgeColumn, OgeColumnGroup, OgeGrid], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { readonly employees = [ { id: 1, firstName: 'Ali', lastName: 'Yılmaz', department: 'Engineering', city: 'İstanbul', salary: 8400, hireDate: '2019-03-11' }, { id: 2, firstName: 'Ayşe', lastName: 'Kaya', department: 'Sales', city: 'Ankara', salary: 7200, hireDate: '2020-07-02' }, { id: 3, firstName: 'Mehmet', lastName: 'Demir', department: 'Engineering', city: 'İzmir', salary: 9100, hireDate: '2018-01-23' }, { id: 4, firstName: 'Zeynep', lastName: 'Şahin', department: 'Finance', city: 'İstanbul', salary: 6800, hireDate: '2021-11-15' }, { id: 5, firstName: 'Emre', lastName: 'Çelik', department: 'Support', city: 'Bursa', salary: 5400, hireDate: '2022-05-09' }, ]; protected readonly departments = [ { code: 'Engineering', label: 'Engineering' }, { code: 'Sales', label: 'Sales' }, { code: 'Finance', label: 'Finance' }, { code: 'Support', label: 'Support' }, ]; protected readonly yearly = (row: { salary: number }): number => row.salary * 12; } ``` #### Header menu ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeColumn, OgeGrid } from '@oge-ui/grid'; import type { OgeHeaderContextMenuEvent } from '@oge-ui/grid'; @Component({ selector: 'demo-root', imports: [OgeColumn, OgeGrid], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { readonly employees = [ { id: 1, firstName: 'Ali', lastName: 'Yılmaz', department: 'Engineering', city: 'İstanbul', salary: 8400, hireDate: '2019-03-11' }, { id: 2, firstName: 'Ayşe', lastName: 'Kaya', department: 'Sales', city: 'Ankara', salary: 7200, hireDate: '2020-07-02' }, { id: 3, firstName: 'Mehmet', lastName: 'Demir', department: 'Engineering', city: 'İzmir', salary: 9100, hireDate: '2018-01-23' }, { id: 4, firstName: 'Zeynep', lastName: 'Şahin', department: 'Finance', city: 'İstanbul', salary: 6800, hireDate: '2021-11-15' }, { id: 5, firstName: 'Emre', lastName: 'Çelik', department: 'Support', city: 'Bursa', salary: 5400, hireDate: '2022-05-09' }, ]; protected onHeaderMenu(event: OgeHeaderContextMenuEvent): void { // the built-in items (sort / group / pin / hide) arrive prebuilt — // extend, filter or replace them before the menu opens event.items.push({ text: `Reset ${event.caption} filter`, action: () => this.clearFilterFor(event.field), }); if (event.field === 'salary') { event.items = event.items.filter((item) => !item.text.startsWith('Pin')); } } private clearFilterFor(field: string | undefined): void { console.log('clear filter for', field); } } ``` #### Row menu ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeColumn, OgeGrid } from '@oge-ui/grid'; import type { OgeContextMenuEvent } from '@oge-ui/grid'; interface Employee { id: number; firstName: string; lastName: string; department: string; city: string; salary: number; hireDate: string; } @Component({ selector: 'demo-root', imports: [OgeColumn, OgeGrid], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { readonly employees = [ { id: 1, firstName: 'Ali', lastName: 'Yılmaz', department: 'Engineering', city: 'İstanbul', salary: 8400, hireDate: '2019-03-11' }, { id: 2, firstName: 'Ayşe', lastName: 'Kaya', department: 'Sales', city: 'Ankara', salary: 7200, hireDate: '2020-07-02' }, { id: 3, firstName: 'Mehmet', lastName: 'Demir', department: 'Engineering', city: 'İzmir', salary: 9100, hireDate: '2018-01-23' }, { id: 4, firstName: 'Zeynep', lastName: 'Şahin', department: 'Finance', city: 'İstanbul', salary: 6800, hireDate: '2021-11-15' }, { id: 5, firstName: 'Emre', lastName: 'Çelik', department: 'Support', city: 'Bursa', salary: 5400, hireDate: '2022-05-09' }, ]; protected onRowMenu(event: OgeContextMenuEvent): void { // push items to open the built-in menu at the cursor; // leave the array empty to fall back to the native browser menu event.items.push( { text: `Open ${event.row.firstName}`, action: () => this.open(event.row) }, { text: 'Duplicate', action: () => this.duplicate(event.key) }, { text: 'Delete (no permission)', disabled: true }, ); } private open(row: Employee): void { console.log('open', row.id); } private duplicate(key: unknown): void { console.log('duplicate', key); } } ``` #### Lookup ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeColumn, OgeGrid } from '@oge-ui/grid'; import type { OgeCommandButton } from '@oge-ui/grid'; interface Assignment { id: number; countryId: number; cityId: number; done: boolean; } const CITIES = [ { id: 1, countryId: 1, name: 'Hamburg' }, { id: 2, countryId: 1, name: 'Berlin' }, { id: 3, countryId: 2, name: 'İzmir' }, ]; @Component({ selector: 'demo-root', imports: [OgeColumn, OgeGrid], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly assignments: Assignment[] = [ { id: 1, countryId: 1, cityId: 1, done: false }, { id: 2, countryId: 2, cityId: 3, done: true }, ]; protected readonly countries = [ { id: 1, name: 'Germany' }, { id: 2, name: 'Türkiye' }, ]; protected readonly citiesOf = (row: Assignment) => CITIES.filter((c) => c.countryId === row.countryId); protected readonly commandButtons: OgeCommandButton[] = [ { name: 'edit' }, { name: 'delete' }, { text: 'Archive', visible: (row) => !row.done, onClick: (row) => this.archive(row), }, ]; private archive(row: Assignment): void { console.log('archive', row.id); } } ``` #### Editing ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; import { OgeColumn, OgeEditTemplate, OgeGrid } from '@oge-ui/grid'; import type { OgeSavingChangesEvent } from '@oge-ui/grid'; interface Employee { id: number; firstName: string; department: string; } @Component({ selector: 'demo-root', imports: [ReactiveFormsModule, OgeColumn, OgeEditTemplate, OgeGrid], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { readonly employees = [ { id: 1, firstName: 'Ali', lastName: 'Yılmaz', department: 'Engineering', city: 'İstanbul', salary: 8400, hireDate: '2019-03-11' }, { id: 2, firstName: 'Ayşe', lastName: 'Kaya', department: 'Sales', city: 'Ankara', salary: 7200, hireDate: '2020-07-02' }, { id: 3, firstName: 'Mehmet', lastName: 'Demir', department: 'Engineering', city: 'İzmir', salary: 9100, hireDate: '2018-01-23' }, { id: 4, firstName: 'Zeynep', lastName: 'Şahin', department: 'Finance', city: 'İstanbul', salary: 6800, hireDate: '2021-11-15' }, { id: 5, firstName: 'Emre', lastName: 'Çelik', department: 'Support', city: 'Bursa', salary: 5400, hireDate: '2022-05-09' }, ]; protected onSaving(event: OgeSavingChangesEvent): void { // event.changes = [{ type: 'update' | 'insert' | 'remove', key, data }] // set event.cancel = true to abort; otherwise the DataSource // (insert/update/remove) is called and the grid reloads. console.log(event.changes); } } ``` #### Filtering ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeColumn, OgeGrid } from '@oge-ui/grid'; import type { FilterExpr } from '@oge-ui/core'; @Component({ selector: 'demo-root', imports: [OgeColumn, OgeGrid], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { readonly employees = [ { id: 1, firstName: 'Ali', lastName: 'Yılmaz', department: 'Engineering', city: 'İstanbul', salary: 8400, hireDate: '2019-03-11' }, { id: 2, firstName: 'Ayşe', lastName: 'Kaya', department: 'Sales', city: 'Ankara', salary: 7200, hireDate: '2020-07-02' }, { id: 3, firstName: 'Mehmet', lastName: 'Demir', department: 'Engineering', city: 'İzmir', salary: 9100, hireDate: '2018-01-23' }, { id: 4, firstName: 'Zeynep', lastName: 'Şahin', department: 'Finance', city: 'İstanbul', salary: 6800, hireDate: '2021-11-15' }, { id: 5, firstName: 'Emre', lastName: 'Çelik', department: 'Support', city: 'Bursa', salary: 5400, hireDate: '2022-05-09' }, ]; // filterValue is a serializable and/or expression tree protected readonly filter = signal({ type: 'and', operands: [ { type: 'binary', field: 'department', op: 'eq', value: 'Engineering' }, { type: 'binary', field: 'salary', op: 'ge', value: 60000 }, ], }); } ``` #### Deferred ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeColumn, OgeGrid } from '@oge-ui/grid'; import { CustomDataSource } from '@oge-ui/core'; interface Employee { id: number; firstName: string; lastName: string; city: string; department: string; salary: number; } @Component({ selector: 'demo-root', imports: [OgeColumn, OgeGrid], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { // grouped request → headers only (items: null, count); // expanding a group fetches its rows with an eq-filter protected readonly source = new CustomDataSource({ key: 'id', load: async (options) => { if (options.group?.length) { const groups = await fetch('/api/employees/groups?by=department'); return { data: await groups.json() }; // [{ key, items: null, count }] } const rows = await fetch( '/api/employees?' + JSON.stringify(options.filter ?? null), ); return { data: await rows.json() }; }, }); } ``` #### Grouping ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeColumn, OgeGrid } from '@oge-ui/grid'; @Component({ selector: 'demo-root', imports: [OgeColumn, OgeGrid], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { readonly employees = [ { id: 1, firstName: 'Ali', lastName: 'Yılmaz', department: 'Engineering', city: 'İstanbul', salary: 8400, hireDate: '2019-03-11' }, { id: 2, firstName: 'Ayşe', lastName: 'Kaya', department: 'Sales', city: 'Ankara', salary: 7200, hireDate: '2020-07-02' }, { id: 3, firstName: 'Mehmet', lastName: 'Demir', department: 'Engineering', city: 'İzmir', salary: 9100, hireDate: '2018-01-23' }, { id: 4, firstName: 'Zeynep', lastName: 'Şahin', department: 'Finance', city: 'İstanbul', salary: 6800, hireDate: '2021-11-15' }, { id: 5, firstName: 'Emre', lastName: 'Çelik', department: 'Support', city: 'Bursa', salary: 5400, hireDate: '2022-05-09' }, ]; protected readonly money = (value: unknown): string => Number(value).toLocaleString('de-DE', { style: 'currency', currency: 'EUR' }); } ``` #### Summary ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeColumn, OgeGrid } from '@oge-ui/grid'; interface Employee { city: string; salary: number; } @Component({ selector: 'demo-root', imports: [OgeColumn, OgeGrid], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { readonly employees = [ { id: 1, firstName: 'Ali', lastName: 'Yılmaz', department: 'Engineering', city: 'İstanbul', salary: 8400, hireDate: '2019-03-11' }, { id: 2, firstName: 'Ayşe', lastName: 'Kaya', department: 'Sales', city: 'Ankara', salary: 7200, hireDate: '2020-07-02' }, { id: 3, firstName: 'Mehmet', lastName: 'Demir', department: 'Engineering', city: 'İzmir', salary: 9100, hireDate: '2018-01-23' }, { id: 4, firstName: 'Zeynep', lastName: 'Şahin', department: 'Finance', city: 'İstanbul', salary: 6800, hireDate: '2021-11-15' }, { id: 5, firstName: 'Emre', lastName: 'Çelik', department: 'Support', city: 'Bursa', salary: 5400, hireDate: '2022-05-09' }, ]; protected readonly distinctCities = (rows: readonly Employee[]): string => `${new Set(rows.map((r) => r.city)).size} cities`; } ``` #### Master detail ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeColumn, OgeDetailTemplate, OgeGrid } from '@oge-ui/grid'; @Component({ selector: 'demo-root', imports: [OgeColumn, OgeDetailTemplate, OgeGrid], changeDetection: ChangeDetectionStrategy.OnPush, template: `
{{ employee.firstName }} {{ employee.lastName }}
{{ employee.department }}
{{ employee.city }}
`, }) export class Demo { readonly employees = [ { id: 1, firstName: 'Ali', lastName: 'Yılmaz', department: 'Engineering', city: 'İstanbul', salary: 8400, hireDate: '2019-03-11' }, { id: 2, firstName: 'Ayşe', lastName: 'Kaya', department: 'Sales', city: 'Ankara', salary: 7200, hireDate: '2020-07-02' }, { id: 3, firstName: 'Mehmet', lastName: 'Demir', department: 'Engineering', city: 'İzmir', salary: 9100, hireDate: '2018-01-23' }, { id: 4, firstName: 'Zeynep', lastName: 'Şahin', department: 'Finance', city: 'İstanbul', salary: 6800, hireDate: '2021-11-15' }, { id: 5, firstName: 'Emre', lastName: 'Çelik', department: 'Support', city: 'Bursa', salary: 5400, hireDate: '2022-05-09' }, ]; } ``` #### Quick start ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeCellTemplate, OgeColumn, OgeGrid } from '@oge-ui/grid'; @Component({ selector: 'app-employees', imports: [OgeCellTemplate, OgeColumn, OgeGrid], changeDetection: ChangeDetectionStrategy.OnPush, template: ` {{ value }} `, }) export class EmployeesPage { readonly employees = [ { id: 1, firstName: 'Ali', lastName: 'Yılmaz', department: 'Engineering', city: 'İstanbul', salary: 8400, hireDate: '2019-03-11' }, { id: 2, firstName: 'Ayşe', lastName: 'Kaya', department: 'Sales', city: 'Ankara', salary: 7200, hireDate: '2020-07-02' }, { id: 3, firstName: 'Mehmet', lastName: 'Demir', department: 'Engineering', city: 'İzmir', salary: 9100, hireDate: '2018-01-23' }, { id: 4, firstName: 'Zeynep', lastName: 'Şahin', department: 'Finance', city: 'İstanbul', salary: 6800, hireDate: '2021-11-15' }, { id: 5, firstName: 'Emre', lastName: 'Çelik', department: 'Support', city: 'Bursa', salary: 5400, hireDate: '2022-05-09' }, ]; protected readonly money = (value: unknown): string => `₺${(value as number).toLocaleString('tr-TR')}`; } ``` #### Custom storage ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeColumn, OgeGrid, OGE_STATE_STORAGE } from '@oge-ui/grid'; import { HttpClient } from '@angular/common/http'; import { firstValueFrom } from 'rxjs'; import type { ApplicationConfig } from '@angular/core'; import type { OgeStateStorage } from '@oge-ui/grid'; @Component({ selector: 'demo-root', imports: [OgeColumn, OgeGrid], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { readonly employees = [ { id: 1, firstName: 'Ali', lastName: 'Yılmaz', department: 'Engineering', city: 'İstanbul', salary: 8400, hireDate: '2019-03-11' }, { id: 2, firstName: 'Ayşe', lastName: 'Kaya', department: 'Sales', city: 'Ankara', salary: 7200, hireDate: '2020-07-02' }, { id: 3, firstName: 'Mehmet', lastName: 'Demir', department: 'Engineering', city: 'İzmir', salary: 9100, hireDate: '2018-01-23' }, { id: 4, firstName: 'Zeynep', lastName: 'Şahin', department: 'Finance', city: 'İstanbul', salary: 6800, hireDate: '2021-11-15' }, { id: 5, firstName: 'Emre', lastName: 'Çelik', department: 'Support', city: 'Bursa', salary: 5400, hireDate: '2022-05-09' }, ]; } // Persist wherever you want — the backend may be fully async. export function provideRemoteGridState(http: HttpClient): ApplicationConfig { return { providers: [ { provide: OGE_STATE_STORAGE, useValue: { get: (key: string) => firstValueFrom( http.get(`/api/grid-state/${key}`, { responseType: 'text' }), ), set: (key: string, value: string) => firstValueFrom(http.put(`/api/grid-state/${key}`, value)).then( () => undefined, ), } satisfies OgeStateStorage, }, ], }; } ``` #### Imperative ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeColumn, OgeGrid } from '@oge-ui/grid'; import type { GridStateSnapshot } from '@oge-ui/core'; @Component({ selector: 'demo-root', imports: [OgeColumn, OgeGrid], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { readonly employees = [ { id: 1, firstName: 'Ali', lastName: 'Yılmaz', department: 'Engineering', city: 'İstanbul', salary: 8400, hireDate: '2019-03-11' }, { id: 2, firstName: 'Ayşe', lastName: 'Kaya', department: 'Sales', city: 'Ankara', salary: 7200, hireDate: '2020-07-02' }, { id: 3, firstName: 'Mehmet', lastName: 'Demir', department: 'Engineering', city: 'İzmir', salary: 9100, hireDate: '2018-01-23' }, { id: 4, firstName: 'Zeynep', lastName: 'Şahin', department: 'Finance', city: 'İstanbul', salary: 6800, hireDate: '2021-11-15' }, { id: 5, firstName: 'Emre', lastName: 'Çelik', department: 'Support', city: 'Bursa', salary: 5400, hireDate: '2022-05-09' }, ]; private snapshot: GridStateSnapshot | null = null; // capture / restore programmatically — the snapshot is serializable protected capture(snapshot: GridStateSnapshot): void { this.snapshot = snapshot; } // stateChange fires debounced on every user-driven change protected saveToBackend(snapshot: GridStateSnapshot): void { void fetch('/api/me/grid-state', { method: 'PUT', body: JSON.stringify(snapshot), }); } } ``` #### State key ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeColumn, OgeGrid } from '@oge-ui/grid'; @Component({ selector: 'demo-root', imports: [OgeColumn, OgeGrid], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { readonly employees = [ { id: 1, firstName: 'Ali', lastName: 'Yılmaz', department: 'Engineering', city: 'İstanbul', salary: 8400, hireDate: '2019-03-11' }, { id: 2, firstName: 'Ayşe', lastName: 'Kaya', department: 'Sales', city: 'Ankara', salary: 7200, hireDate: '2020-07-02' }, { id: 3, firstName: 'Mehmet', lastName: 'Demir', department: 'Engineering', city: 'İzmir', salary: 9100, hireDate: '2018-01-23' }, { id: 4, firstName: 'Zeynep', lastName: 'Şahin', department: 'Finance', city: 'İstanbul', salary: 6800, hireDate: '2021-11-15' }, { id: 5, firstName: 'Emre', lastName: 'Çelik', department: 'Support', city: 'Bursa', salary: 5400, hireDate: '2022-05-09' }, ]; } ``` #### Drag ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeColumn, OgeGrid } from '@oge-ui/grid'; import type { RowKey } from '@oge-ui/core'; import type { OgeRowReorderedEvent } from '@oge-ui/grid'; @Component({ selector: 'demo-root', imports: [OgeColumn, OgeGrid], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { readonly employees = [ { id: 1, firstName: 'Ali', lastName: 'Yılmaz', department: 'Engineering', city: 'İstanbul', salary: 8400, hireDate: '2019-03-11' }, { id: 2, firstName: 'Ayşe', lastName: 'Kaya', department: 'Sales', city: 'Ankara', salary: 7200, hireDate: '2020-07-02' }, { id: 3, firstName: 'Mehmet', lastName: 'Demir', department: 'Engineering', city: 'İzmir', salary: 9100, hireDate: '2018-01-23' }, { id: 4, firstName: 'Zeynep', lastName: 'Şahin', department: 'Finance', city: 'İstanbul', salary: 6800, hireDate: '2021-11-15' }, { id: 5, firstName: 'Emre', lastName: 'Çelik', department: 'Support', city: 'Bursa', salary: 5400, hireDate: '2022-05-09' }, ]; protected readonly focusedKey = signal(null); protected onReordered(event: OgeRowReorderedEvent): void { console.log(event.fromIndex, '→', event.toIndex); } } ``` #### Nodata ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeColumn, OgeGrid, OgeNoDataTemplate } from '@oge-ui/grid'; @Component({ selector: 'demo-root', imports: [OgeColumn, OgeGrid, OgeNoDataTemplate], changeDetection: ChangeDetectionStrategy.OnPush, template: `
No employees match — adjust the filter or add a new record.
`, }) export class Demo { readonly employees = [ { id: 1, firstName: 'Ali', lastName: 'Yılmaz', department: 'Engineering', city: 'İstanbul', salary: 8400, hireDate: '2019-03-11' }, { id: 2, firstName: 'Ayşe', lastName: 'Kaya', department: 'Sales', city: 'Ankara', salary: 7200, hireDate: '2020-07-02' }, { id: 3, firstName: 'Mehmet', lastName: 'Demir', department: 'Engineering', city: 'İzmir', salary: 9100, hireDate: '2018-01-23' }, { id: 4, firstName: 'Zeynep', lastName: 'Şahin', department: 'Finance', city: 'İstanbul', salary: 6800, hireDate: '2021-11-15' }, { id: 5, firstName: 'Emre', lastName: 'Çelik', department: 'Support', city: 'Bursa', salary: 5400, hireDate: '2022-05-09' }, ]; } ``` #### Row template ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeColumn, OgeGrid, OgeRowTemplate } from '@oge-ui/grid'; interface Employee { firstName: string; lastName: string; department: string; city: string; salary: number; } @Component({ selector: 'demo-root', imports: [OgeColumn, OgeGrid, OgeRowTemplate], changeDetection: ChangeDetectionStrategy.OnPush, template: `
{{ initials(employee) }}
{{ employee.firstName }} {{ employee.lastName }}
{{ employee.department }} · {{ employee.city }}
{{ money(employee.salary) }}
`, }) export class Demo { readonly employees = [ { id: 1, firstName: 'Ali', lastName: 'Yılmaz', department: 'Engineering', city: 'İstanbul', salary: 8400, hireDate: '2019-03-11' }, { id: 2, firstName: 'Ayşe', lastName: 'Kaya', department: 'Sales', city: 'Ankara', salary: 7200, hireDate: '2020-07-02' }, { id: 3, firstName: 'Mehmet', lastName: 'Demir', department: 'Engineering', city: 'İzmir', salary: 9100, hireDate: '2018-01-23' }, { id: 4, firstName: 'Zeynep', lastName: 'Şahin', department: 'Finance', city: 'İstanbul', salary: 6800, hireDate: '2021-11-15' }, { id: 5, firstName: 'Emre', lastName: 'Çelik', department: 'Support', city: 'Bursa', salary: 5400, hireDate: '2022-05-09' }, ]; protected initials(employee: Employee): string { return `${employee.firstName[0]}${employee.lastName[0]}`; } protected money(value: number): string { return value.toLocaleString('de-DE', { style: 'currency', currency: 'EUR' }); } } ``` #### Deferred ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeColumn, OgeGrid } from '@oge-ui/grid'; import type { FilterExpr } from '@oge-ui/core'; @Component({ selector: 'demo-root', imports: [OgeColumn, OgeGrid], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { readonly employees = [ { id: 1, firstName: 'Ali', lastName: 'Yılmaz', department: 'Engineering', city: 'İstanbul', salary: 8400, hireDate: '2019-03-11' }, { id: 2, firstName: 'Ayşe', lastName: 'Kaya', department: 'Sales', city: 'Ankara', salary: 7200, hireDate: '2020-07-02' }, { id: 3, firstName: 'Mehmet', lastName: 'Demir', department: 'Engineering', city: 'İzmir', salary: 9100, hireDate: '2018-01-23' }, { id: 4, firstName: 'Zeynep', lastName: 'Şahin', department: 'Finance', city: 'İstanbul', salary: 6800, hireDate: '2021-11-15' }, { id: 5, firstName: 'Emre', lastName: 'Çelik', department: 'Support', city: 'Bursa', salary: 5400, hireDate: '2022-05-09' }, ]; protected readonly selectionFilter = signal(null); } ``` #### Selection ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeColumn, OgeGrid } from '@oge-ui/grid'; import type { RowKey } from '@oge-ui/core'; import type { OgeContextMenuEvent } from '@oge-ui/grid'; interface Employee { id: number; firstName: string; lastName: string; } @Component({ selector: 'demo-root', imports: [OgeColumn, OgeGrid], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { readonly employees = [ { id: 1, firstName: 'Ali', lastName: 'Yılmaz', department: 'Engineering', city: 'İstanbul', salary: 8400, hireDate: '2019-03-11' }, { id: 2, firstName: 'Ayşe', lastName: 'Kaya', department: 'Sales', city: 'Ankara', salary: 7200, hireDate: '2020-07-02' }, { id: 3, firstName: 'Mehmet', lastName: 'Demir', department: 'Engineering', city: 'İzmir', salary: 9100, hireDate: '2018-01-23' }, { id: 4, firstName: 'Zeynep', lastName: 'Şahin', department: 'Finance', city: 'İstanbul', salary: 6800, hireDate: '2021-11-15' }, { id: 5, firstName: 'Emre', lastName: 'Çelik', department: 'Support', city: 'Bursa', salary: 5400, hireDate: '2022-05-09' }, ]; protected readonly selected = signal([]); protected onContextMenu(event: OgeContextMenuEvent): void { // push items to open the built-in menu; leave empty for the browser menu event.items.push({ text: 'Copy name', action: () => this.copy(event.row), }); } private copy(row: Employee): void { void navigator.clipboard.writeText(`${row.firstName} ${row.lastName}`); } } ``` #### Infinite scroll ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeColumn, OgeGrid } from '@oge-ui/grid'; import { CustomDataSource } from '@oge-ui/core'; interface Employee { id: number; firstName: string; lastName: string; department: string; city: string; salary: number; } const TOTAL = 1_000_000; @Component({ selector: 'app-employees', imports: [OgeColumn, OgeGrid], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class EmployeesComponent { // The grid asks for 100-row blocks as you scroll; nothing else is fetched. readonly employees = new CustomDataSource({ key: 'id', load: async ({ skip = 0, take = 100 }) => { const response = await fetch(`/api/employees?skip=${skip}&take=${take}`); const { data } = await response.json(); return { data, totalCount: TOTAL }; }, }); } ``` #### Live updates ```ts import { ChangeDetectionStrategy, Component, DestroyRef, inject } from '@angular/core'; import { OgeCellTemplate, OgeColumn, OgeGrid } from '@oge-ui/grid'; import { ArrayDataSource } from '@oge-ui/core'; interface Stock { id: number; symbol: string; price: number; change: number; changePercent: number; } const SEED_STOCKS: Stock[] = [ { id: 1, symbol: 'AAPL', price: 227.4, change: 0, changePercent: 0 }, { id: 2, symbol: 'MSFT', price: 415.2, change: 0, changePercent: 0 }, ]; @Component({ selector: 'app-ticker', imports: [OgeCellTemplate, OgeColumn, OgeGrid], changeDetection: ChangeDetectionStrategy.OnPush, template: ` {{ value }} ({{ asStock(row).changePercent }}%) `, }) export class TickerComponent { readonly stocks = new ArrayDataSource(SEED_STOCKS, { key: 'id' }); // the cell template's row context is untyped — narrow it once protected asStock(row: unknown): Stock { return row as Stock; } protected readonly money = (value: unknown): string => Number(value).toFixed(2); constructor() { // any push source works: WebSocket, SSE, SignalR… const timer = setInterval(() => { const stock = SEED_STOCKS[Math.floor(Math.random() * SEED_STOCKS.length)]; const change = Number((Math.random() * 2 - 1).toFixed(2)); this.stocks.push([ { type: 'update', key: stock.id, patch: { price: stock.price + change, change, changePercent: Number(((change / stock.price) * 100).toFixed(2)), }, }, ]); }, 600); inject(DestroyRef).onDestroy(() => clearInterval(timer)); } } ``` #### Remote data ```ts import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; import { OgeColumn, OgeGrid } from '@oge-ui/grid'; import { HttpClient } from '@angular/common/http'; import { CustomDataSource } from '@oge-ui/core'; import { firstValueFrom } from 'rxjs'; import type { LoadResult } from '@oge-ui/core'; interface Employee { id: number; firstName: string; department: string; } @Component({ selector: 'demo-root', imports: [OgeColumn, OgeGrid], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { private readonly http = inject(HttpClient); protected readonly source = new CustomDataSource({ key: 'id', load: (options) => // options = { skip, take, sort, filter, searchText, … } — serialize as-is firstValueFrom( this.http.post>('/api/employees/query', options), ), }); } ``` #### Sorting ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeColumn, OgeGrid } from '@oge-ui/grid'; @Component({ selector: 'demo-root', imports: [OgeColumn, OgeGrid], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { readonly employees = [ { id: 1, firstName: 'Ali', lastName: 'Yılmaz', department: 'Engineering', city: 'İstanbul', salary: 8400, hireDate: '2019-03-11' }, { id: 2, firstName: 'Ayşe', lastName: 'Kaya', department: 'Sales', city: 'Ankara', salary: 7200, hireDate: '2020-07-02' }, { id: 3, firstName: 'Mehmet', lastName: 'Demir', department: 'Engineering', city: 'İzmir', salary: 9100, hireDate: '2018-01-23' }, { id: 4, firstName: 'Zeynep', lastName: 'Şahin', department: 'Finance', city: 'İstanbul', salary: 6800, hireDate: '2021-11-15' }, { id: 5, firstName: 'Emre', lastName: 'Çelik', department: 'Support', city: 'Bursa', salary: 5400, hireDate: '2022-05-09' }, ]; } ``` #### Auto height ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeColumn, OgeGrid } from '@oge-ui/grid'; @Component({ selector: 'demo-root', imports: [OgeColumn, OgeGrid], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly notes = [ { id: 1, title: 'Kickoff', body: 'Short note.' }, { id: 2, title: 'Retro', body: 'A much longer note that wraps over several lines and makes the row taller than its neighbours.' }, ]; } ``` #### Column ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeGrid } from '@oge-ui/grid'; import type { OgeColumnDef } from '@oge-ui/grid'; @Component({ selector: 'demo-root', imports: [OgeGrid], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly wideColumns: OgeColumnDef[] = Array.from( { length: 200 }, (_, i) => ({ field: `c${i}`, caption: `Column ${i}`, width: 120 }), ); protected readonly wideRows = Array.from({ length: 500 }, (_, row) => Object.fromEntries( this.wideColumns.map((column, i) => [column.field, row * 200 + i]), ), ); } ``` #### Virtual scroll ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeColumn, OgeGrid } from '@oge-ui/grid'; @Component({ selector: 'demo-root', imports: [OgeColumn, OgeGrid], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { readonly employees = [ { id: 1, firstName: 'Ali', lastName: 'Yılmaz', department: 'Engineering', city: 'İstanbul', salary: 8400, hireDate: '2019-03-11' }, { id: 2, firstName: 'Ayşe', lastName: 'Kaya', department: 'Sales', city: 'Ankara', salary: 7200, hireDate: '2020-07-02' }, { id: 3, firstName: 'Mehmet', lastName: 'Demir', department: 'Engineering', city: 'İzmir', salary: 9100, hireDate: '2018-01-23' }, { id: 4, firstName: 'Zeynep', lastName: 'Şahin', department: 'Finance', city: 'İstanbul', salary: 6800, hireDate: '2021-11-15' }, { id: 5, firstName: 'Emre', lastName: 'Çelik', department: 'Support', city: 'Bursa', salary: 5400, hireDate: '2022-05-09' }, ]; } ``` ## @oge-ui/tree-list The data-grid feature set on hierarchical data: lazy loading, ancestor-preserving filtering, tri-state selection and drag & drop. Docs: https://ogeui.com/components/tree-list ### Entry points `@oge-ui/tree-list` - values: `OGE_GRID_CONFIG`, `OgeCellTemplate`, `OgeColumn`, `OgeColumnGroup`, `OgeHeaderTemplate`, `OgeNoDataTemplate`, `OgeTreeList`, `provideOgeGridConfig` - types: `OgeCellClickEvent`, `OgeCellTemplateContext`, `OgeColumnLookup`, `OgeDataErrorEvent`, `OgeDataType`, `OgeEditingStartEvent`, `OgeExportingEvent`, `OgeFocusedRowChangedEvent`, `OgeGridConfig`, `OgeGridConfigInput`, `OgeGridMessages`, `OgeHeaderTemplateContext`, `OgeRowClickEvent`, `OgeRowInsertedEvent`, `OgeRowInsertingEvent`, `OgeRowRemovedEvent`, `OgeRowRemovingEvent`, `OgeRowUpdatedEvent`, `OgeRowUpdatingEvent`, `OgeSavedChangesEvent`, `OgeSavingChangesEvent`, `OgeSelectionChangedEvent`, `OgeSelectionMode`, `OgeSortingOptions`, `OgeTreeDropPosition`, `OgeTreeExportData`, `OgeTreeInitNewRowEvent`, `OgeTreeRowReparentEvent`, `OgeTreeRowToggleEvent`, `OgeTreeRowTogglingEvent` `@oge-ui/tree-list/export-excel` - values: `buildTreeExcelWorkbook`, `exportOgeTreeListToExcel` - types: `OgeTreeExcelExportOptions` ### OgeTreeList — `` #### Properties _Tree data_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `data` | `readonly T[] \| DataSource` | `[]` | Flat self-referencing rows: a static array or any DataSource. | | `keyExpr` | `string \| ((row: T) => RowKey)` | `'id'` | Row key: field path or selector (grid uses `keyField`). | | `parentIdExpr` | `string \| ((row: T) => unknown)` | `'parentId'` | Parent reference: field path or selector. | | `rootValue` | `unknown` | `null` | Parent value marking root rows. | | `orphanPolicy` | `'discard' \| 'promoteToRoot'` | `'discard'` | Rows whose parent key is missing: drop or render as roots. | | `itemsExpr` | `string \| ((row: T) => readonly T[] \| undefined) \| undefined` | `—` | Nested payloads: rows carry children inline (plain arrays only; `parentIdExpr` ignored). | | `hasItemsExpr` | `string \| ((row: T) => boolean) \| undefined` | `—` | Expandability hint for lazily loaded children. | | `loadMode` | `'full' \| 'lazy' \| undefined` | `—` | `'lazy'` fetches children per expansion (`filter: [parentIdExpr,'=',key]`); defaults to lazy with DataSource + `hasItemsExpr`. | _Expansion & focus_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `autoExpandAll` | `boolean` | `false` | Expands every row initially; the toggled-set polarity follows. | | `expandedRowKeys` | `model` | `[]` | Two-way binding of the expanded row keys. | | `expandNodesOnFiltering` | `boolean` | `true` | Auto-expands ancestor chains of matches while a filter is active. | | `focusedRowEnabled` | `boolean` | `false` | Highlights and tracks a single focused row. | | `focusedRowKey` | `model` | `null` | Two-way binding of the focused row's key. | | `autoNavigateToFocusedRow` | `boolean` | `false` | A `focusedRowKey` change expands its ancestor path and scrolls (tree-only). | _Selection_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `selectionMode` | `OgeSelectionMode` | `'none'` | none \| single \| multiple \| checkbox. | | `selectedKeys` | `model` | `[]` | Two-way binding of the selected row keys. | | `selectionRecursive` | `boolean` | `false` | Tri-state cascade to descendants and ancestors. | | `allowSelectAll` | `boolean` | `true` | Hides the header select-all checkbox when false. | _Filtering & paging_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `filterRow / headerFilter / searchPanel / filterPanel` | `boolean \| options` | `false` | Same options objects as the grid — all client-side over the loaded rows. | | `filterMode` | `TreeFilterMode` | `'withAncestors'` | Matches keep their ancestors; `'fullBranch'` also keeps all descendants. | | `filterValue` | `model` | `null` | Two-way filter expression (builder). | | `filterDebounce` | `number \| undefined` | `—` | Debounce for text filter inputs. | | `paging` | `false \| OgePagingOptions` | `false` | Pages the visible (flattened) rows client-side; paging wins over `virtualScroll`. | | `sortable / sorting` | `boolean \| 'single' \| 'multi' / OgeSortingOptions` | `true` | Sibling-scoped, multi-column by default. | _Layout, editing & misc (grid-shared)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `columns` | `readonly (string \| ColumnDefLike)[] \| undefined` | `—` | Programmatic columns (or declarative ``). | | `virtualScroll / columnRenderingMode / rowHeight / overscan / columnMinWidth` | `various` | `—` | Virtualization knobs; `columnRenderingMode` is a top-level input here. | | `columnResize / columnReorder / columnChooser` | `boolean` | `—` | Column UX (defaults: true/true/false). | | `editing` | `false \| OgeEditingOptions` | `false` | cell/row/batch/form/popup via the shared EditingModel. | | `commandButtons / rowDragging / rowAlternation / wordWrap / loadPanel / rtlEnabled / messages / stateKey` | `various` | `—` | Same semantics as the grid. | #### Methods _Tree navigation & data_ | Name | Type | Description | | --- | --- | --- | | `expandAll() / collapseAll()` | `void` | Polarity-aware over the expandable keys. | | `expandRow(key) / collapseRow(key) / isRowExpanded(key)` | `void / boolean` | Per-row expansion (imperative API bypasses the cancelable events). | | `focusRow(key) / navigateToRow(key)` | `void` | Expands the ancestor path, scrolls to the row and focuses its first cell. | | `scrollToRow(target: number \| RowKey)` | `void` | Scrolls a visible row into the viewport. | | `getNodeByKey(key): T \| undefined` | `T \| undefined` | The loaded row carrying `key`. | | `forEachNode(callback)` | `void` | Runs the callback for every loaded row with `(row, key, parentKey)`. | | `getVisibleRows(): readonly T[]` | `readonly T[]` | Data rows of the rendered page, in display order. | | `refresh(): void` | `void` | Re-runs the load and drops lazily fetched rows. | _Selection_ | Name | Type | Description | | --- | --- | --- | | `getSelectedRowKeys(mode = 'all')` | `RowKey[]` | `'all' \| 'leavesOnly' \| 'excludeRecursive'` narrows recursive selections. | | `getSelectedRowsData(mode = 'all')` | `T[]` | Row data per the same modes. | | `selectAll() / deselectAll() / clearSelection() / isRowSelected(key)` | `void / boolean` | Recursive mode cascades select-all to descendants. | | `copyToClipboard(): Promise` | `Promise` | Selected rows as tab-separated values (with header). | _Editing, paging, state & export_ | Name | Type | Description | | --- | --- | --- | | `addRow(parentKey?)` | `void` | New unsaved row; parent pre-staged with a string `parentIdExpr`; `initNewRow` can prefill. | | `editRow(key) / deleteRow(key) / saveChanges() / discardChanges() / hasChanges()` | `void / boolean` | Same semantics as the grid. | | `pageIndex / setPageIndex(i) / pageSize() / setPageSize(n) / pageCount() / totalCount()` | `signal / methods` | `pageIndex` is a writable signal; `totalCount()` spans all pages. | | `beginCustomLoading(message?) / endCustomLoading()` | `void` | Load panel independent of data activity. | | `state() / applyState(snapshot)` | `TreeListStateSnapshot / void` | Sort, filters, column layout and expansion. | | `clearFilters() / clearSorting()` | `void` | Reset the view. | | `getExportData() / getCsv() / exportCsv()` | `sync` | **Synchronous** (grid: async); CSV indents the first column 2 spaces per level; Excel entry sets real outline levels. | #### Events _Tree-specific_ | Name | Type | Description | | --- | --- | --- | | `rowExpanding / rowCollapsing` | `OgeTreeRowTogglingEvent` | Cancelable — UI-driven toggles only (the imperative API stays silent). | | `rowExpanded / rowCollapsed` | `OgeTreeRowToggleEvent` | `{ key, row }` after a toggle. | | `rowReparented` | `OgeTreeRowReparentEvent` | Drag & drop: `{ key, row, fromParentKey, toParentKey, position: 'inside' \| 'before' \| 'after' }`. | | `initNewRow` | `OgeTreeInitNewRowEvent` | `{ key, parentKey, values }` prefill hook. | _Shared with the grid_ | Name | Type | Description | | --- | --- | --- | | `rowClick / rowDblClick / cellClick / cellDblClick` | `OgeRowClickEvent / OgeCellClickEvent` | Flat payloads with the originating DOM event. | | `rowContextMenu / headerContextMenu` | `context-menu events` | Mutable `items`. | | `selectionChanged / focusedRowChanged` | `diff / focus events` | Same payloads as the grid. | | `editingStart / rowInserting‑ed / rowUpdating‑ed / rowRemoving‑ed / savingChanges / savedChanges / editCanceled` | `editing lifecycle` | Same shared EditingModel pipeline as the grid; `-ing` events cancelable. | | `exporting / dataErrorOccurred / contentReady / stateChange` | `misc` | Same semantics as the grid (`stateChange` carries a `TreeListStateSnapshot`). | #### Types | Name | Type | Description | | --- | --- | --- | | `OgeTreeDropPosition` | `'inside' \| 'before' \| 'after'` | Reparent vs sibling ordering. | | `OgeTreeExportData` | `OgeExportData & { levels: readonly number[] }` | Zero-based depth per exported row (drives spreadsheet outline levels). | | `TreeFilterMode` | `'withAncestors' \| 'fullBranch'` | Visible set under a filter. | | `exportOgeTreeListToExcel(treeList, options?)` | `@oge-ui/tree-list/export-excel` | Lazy Excel export with native outline grouping. | | `Re-exports` | `from @oge-ui/grid` | `OgeColumn`, templates, config/messages and the shared event payload types are re-exported so tree-only consumers have a single import source. | ### Demos Each demo below is a complete standalone component — copy one whole and it compiles. #### Drag drop ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeTreeList } from '@oge-ui/tree-list'; import type { OgeTreeRowReparentEvent } from '@oge-ui/tree-list'; @Component({ selector: 'demo-root', imports: [OgeTreeList], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { readonly org = [ { id: 1, parentId: null, name: 'Aylin Koç', title: 'CEO', office: 'İstanbul', headcount: 4 }, { id: 2, parentId: 1, name: 'Baran Ateş', title: 'VP Engineering', office: 'İstanbul', headcount: 2 }, { id: 3, parentId: 2, name: 'Ceren Aksu', title: 'Team Lead', office: 'İzmir', headcount: 1 }, { id: 4, parentId: 3, name: 'Deniz Ünal', title: 'Engineer', office: 'İzmir', headcount: 0 }, { id: 5, parentId: 1, name: 'Elif Barış', title: 'VP Sales', office: 'Ankara', headcount: 0 }, ]; protected onReparent(event: OgeTreeRowReparentEvent): void { console.log(event.key, 'moved under', event.toParentKey); } } ``` #### Editing ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeTreeList } from '@oge-ui/tree-list'; @Component({ selector: 'demo-root', imports: [OgeTreeList], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { readonly org = [ { id: 1, parentId: null, name: 'Aylin Koç', title: 'CEO', office: 'İstanbul', headcount: 4 }, { id: 2, parentId: 1, name: 'Baran Ateş', title: 'VP Engineering', office: 'İstanbul', headcount: 2 }, { id: 3, parentId: 2, name: 'Ceren Aksu', title: 'Team Lead', office: 'İzmir', headcount: 1 }, { id: 4, parentId: 3, name: 'Deniz Ünal', title: 'Engineer', office: 'İzmir', headcount: 0 }, { id: 5, parentId: 1, name: 'Elif Barış', title: 'VP Sales', office: 'Ankara', headcount: 0 }, ]; } ``` #### Filtering ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeColumn, OgeTreeList } from '@oge-ui/tree-list'; @Component({ selector: 'demo-root', imports: [OgeColumn, OgeTreeList], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { readonly org = [ { id: 1, parentId: null, name: 'Aylin Koç', title: 'CEO', office: 'İstanbul', headcount: 4 }, { id: 2, parentId: 1, name: 'Baran Ateş', title: 'VP Engineering', office: 'İstanbul', headcount: 2 }, { id: 3, parentId: 2, name: 'Ceren Aksu', title: 'Team Lead', office: 'İzmir', headcount: 1 }, { id: 4, parentId: 3, name: 'Deniz Ünal', title: 'Engineer', office: 'İzmir', headcount: 0 }, { id: 5, parentId: 1, name: 'Elif Barış', title: 'VP Sales', office: 'Ankara', headcount: 0 }, ]; } ``` #### Lazy loading ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeTreeList } from '@oge-ui/tree-list'; import { CustomDataSource } from '@oge-ui/core'; interface Node { id: number; parentId: number | null; name: string; hasReports: boolean; } @Component({ selector: 'demo-root', imports: [OgeTreeList], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly source = new CustomDataSource({ key: 'id', load: (options) => fetch(`/api/org?parent=${parentKeyOf(options)}`) .then((response) => response.json()) .then((data: Node[]) => ({ data, totalCount: data.length })), }); } /** The tree list asks for one level at a time: ['parentId', '=', key]. */ function parentKeyOf(options: { filter?: unknown }): string { const filter = options.filter as [string, string, unknown] | undefined; return String(filter?.[2] ?? ''); } ``` #### Overview ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeColumn, OgeTreeList } from '@oge-ui/tree-list'; @Component({ selector: 'demo-root', imports: [OgeColumn, OgeTreeList], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { readonly org = [ { id: 1, parentId: null, name: 'Aylin Koç', title: 'CEO', office: 'İstanbul', headcount: 4 }, { id: 2, parentId: 1, name: 'Baran Ateş', title: 'VP Engineering', office: 'İstanbul', headcount: 2 }, { id: 3, parentId: 2, name: 'Ceren Aksu', title: 'Team Lead', office: 'İzmir', headcount: 1 }, { id: 4, parentId: 3, name: 'Deniz Ünal', title: 'Engineer', office: 'İzmir', headcount: 0 }, { id: 5, parentId: 1, name: 'Elif Barış', title: 'VP Sales', office: 'Ankara', headcount: 0 }, ]; } ``` #### Selection ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeTreeList } from '@oge-ui/tree-list'; import type { RowKey } from '@oge-ui/core'; @Component({ selector: 'demo-root', imports: [OgeTreeList], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { readonly org = [ { id: 1, parentId: null, name: 'Aylin Koç', title: 'CEO', office: 'İstanbul', headcount: 4 }, { id: 2, parentId: 1, name: 'Baran Ateş', title: 'VP Engineering', office: 'İstanbul', headcount: 2 }, { id: 3, parentId: 2, name: 'Ceren Aksu', title: 'Team Lead', office: 'İzmir', headcount: 1 }, { id: 4, parentId: 3, name: 'Deniz Ünal', title: 'Engineer', office: 'İzmir', headcount: 0 }, { id: 5, parentId: 1, name: 'Elif Barış', title: 'VP Sales', office: 'Ankara', headcount: 0 }, ]; protected readonly selectedKeys = signal([]); } ``` #### Virtual scroll ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeTreeList } from '@oge-ui/tree-list'; @Component({ selector: 'demo-root', imports: [OgeTreeList], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly rows = Array.from({ length: 100 }, (_, branch) => [ { id: branch * 1000, parentId: null, name: `Branch ${branch + 1}` }, ...Array.from({ length: 999 }, (_, leaf) => ({ id: branch * 1000 + leaf + 1, parentId: branch * 1000, name: `Node ${leaf + 1}`, })), ]).flat(); } ``` ## @oge-ui/inputs Form editors on one field chrome: TextBox, TextArea, NumberBox, SelectBox, TagBox, Autocomplete, DateBox, ColorBox, CheckBox, Switch, and the APG Slider/RangeSlider with live drag commits and Escape-to-cancel — floating labels, validation, Signal Forms and reactive forms. Docs: https://ogeui.com/components/inputs ### Entry points `@oge-ui/inputs` - values: `OGE_DEFAULT_COLOR_PALETTE`, `OGE_DEFAULT_INPUTS_CONFIG`, `OGE_DEFAULT_INPUTS_MESSAGES`, `OGE_INPUTS_CONFIG`, `OGE_SELECT_OPTION_HEIGHT`, `OgeAutocomplete`, `OgeCalendar`, `OgeCalendarCellTemplate`, `OgeCheckBox`, `OgeColorBox`, `OgeDateBox`, `OgeDateRangeBox`, `OgeInputPrefix`, `OgeInputSuffix`, `OgeNumberBox`, `OgeRadioGroup`, `OgeRangeSlider`, `OgeSelectBox`, `OgeSlider`, `OgeSwitch`, `OgeTagBox`, `OgeTextArea`, `OgeTextBox`, `OgeTreeSelect`, `datePartOrder`, `formatPattern`, `measureTextAreaHeight`, `parseDateText`, `provideOgeInputsConfig`, `resolveErrorMessage` - types: `OgeAutocompleteItemClickEvent`, `OgeAutocompleteSelectionChangedEvent`, `OgeCalendarCellClickEvent`, `OgeCalendarCellTemplateContext`, `OgeCalendarDisabledDates`, `OgeCalendarRange`, `OgeCalendarSelectionMode`, `OgeCalendarWeekNumberOptions`, `OgeCalendarZoomLevel`, `OgeColorBoxApplyValueMode`, `OgeColorBoxView`, `OgeDateBoxApplyValueMode`, `OgeDateBoxDisplayFormat`, `OgeDateBoxTimeView`, `OgeDateBoxType`, `OgeFieldError`, `OgeInputCopyApi`, `OgeInputCounterMode`, `OgeInputCounterState`, `OgeInputDropDownApi`, `OgeInputErrorDisplay`, `OgeInputFocusEvent`, `OgeInputKeyEvent`, `OgeInputLabelMode`, `OgeInputRawEvent`, `OgeInputRevealApi`, `OgeInputShowSuccessIcon`, `OgeInputSize`, `OgeInputSpinApi`, `OgeInputStylingMode`, `OgeInputSubscriptSizing`, `OgeInputValueCommittedEvent`, `OgeInputsConfig`, `OgeInputsConfigInput`, `OgeInputsMessages`, `OgeNumberBoxMode`, `OgeRadioGroupItemClickEvent`, `OgeRadioGroupLayout`, `OgeSelectBoxCustomItemEvent`, `OgeSelectBoxDisabledExpr`, `OgeSelectBoxDisplayExpr`, `OgeSelectBoxGroupExpr`, `OgeSelectBoxImageExpr`, `OgeSelectBoxItemClickEvent`, `OgeSelectBoxItemsFn`, `OgeSelectBoxSearchChangedEvent`, `OgeSelectBoxSearchExpr`, `OgeSelectBoxSearchMode`, `OgeSelectBoxSelectionChangedEvent`, `OgeSelectBoxValueExpr`, `OgeSelectItemTemplateContext`, `OgeSliderDragStartedEvent`, `OgeSliderOrientation`, `OgeSliderSlideEndedEvent`, `OgeSliderValueIndicator`, `OgeTagBoxItemClickEvent`, `OgeTagBoxSelectionChangedEvent`, `OgeTextBoxMode`, `OgeTreeSelectDisplayMode`, `OgeTreeSelectSelectionChangedEvent`, `OgeTreeSelectSelectionMode`, `OgeVirtualScrollOptions` ### OgeTextBox — `` #### Properties _OgeTextBox_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `value` | `model` | `''` | Editor value — two-way. | | `mode` | `OgeTextBoxMode` | `'text'` | Native input type. `password` auto-enables the reveal toggle. | | `maxLength` | `number \| undefined` | `—` | Counter denominator; enforced natively while `counterMode` is `'limit'`. | | `minLength` | `number \| undefined` | `—` | Native `minlength` attribute. | | `showCounter` | `boolean` | `false` | Renders the grapheme-accurate character counter in the subscript end slot. | | `counterMode` | `OgeInputCounterMode` | `'limit'` | Enforce `maxLength` natively, or allow typing past it and color the counter. | | `revealable` | `boolean` | `true` | Password reveal toggle; on by default for `mode="password"`. Preserves caret/selection when toggling. | | `showCopyButton` | `boolean` | `false` | Copy-to-clipboard rail button (API keys, tokens…); copies the live text. | | `autocomplete` | `string \| undefined` | `—` | Native `autocomplete` attribute. | | `inputMode` | `string \| undefined` | `—` | Native `inputmode` attribute. | | `enterKeyHint` | `string \| undefined` | `—` | Native `enterkeyhint` attribute. | | `autocapitalize` | `string \| undefined` | `—` | Native `autocapitalize` attribute. | | `spellcheck` | `boolean \| undefined` | `—` | `undefined` omits the attribute (browser default). | _Common — field chrome (all editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `label` | `string` | `''` | Field label; placement follows `labelMode`. | | `labelMode` | `OgeInputLabelMode` | `'static'` | Label placement: static / floating / hidden (aria-only) / outside. | | `stylingMode` | `OgeInputStylingMode` | `'outlined'` | Container fill style. | | `size` | `OgeInputSize` | `'md'` | Container height preset — 28/34/42px, the button scale. | | `placeholder` | `string` | `''` | Native placeholder text. | | `hint` | `string \| undefined` | `—` | Helper text in the subscript region (hidden while an error shows). | | `tooltip` | `string \| undefined` | `—` | Native `title` attribute of the input element. | | `subscriptSizing` | `OgeInputSubscriptSizing` | `'fixed'` | Whether the hint/error line reserves height, collapses, or is removed. | | `fluid` | `boolean` | `false` | Stretches the field to 100% width (default 240px via `--oge-input-width`). | | `showClearButton` | `boolean` | `false` | Renders the clear (✕) button while the field has a value. | | `showSuccessIcon` | `OgeInputShowSuccessIcon` | `false` | Success icon when valid: `false` / on touch / always. | | `id` | `string \| undefined` | `—` | Base for the generated element ids (input/label/hint/error/counter). | | `tabIndex` | `number` | `0` | Tab order of the native input. | | `autofocus` | `boolean` | `false` | Focuses the editor after its first render. | | `selectOnFocus` | `boolean` | `false` | Selects the whole text when the input receives focus. | | `inputAttr` | `Record` | `{}` | Escape hatch: extra attributes rendered onto the native input (template-owned attributes are ignored). | | `messages` | `Partial \| undefined` | `—` | Per-instance overrides of user-facing strings. | _Common — state & forms (all editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `disabled` | `boolean` | `false` | Disables the editor. | | `readonly` | `boolean` | `false` | Focusable but not editable. (Contract name — not `readOnly`.) | | `required` | `boolean` | `false` | Marks the field required (label asterisk + validation). | | `name` | `string` | `''` | Native `name` attribute. | | `invalid` | `boolean` | `false` | External invalid override — combined with forms state. | | `pending` | `boolean` | `false` | Async-validation indicator; a spinner shows in the rail while `true`. | | `touched` | `boolean` | `false` | External touched override (Signal Forms contract). | | `dirty` | `boolean` | `false` | External dirty override (Signal Forms contract). | | `errors` | `readonly OgeFieldError[]` | `[]` | Signal Forms validation errors (auto-bound by `[formField]`). | | `errorText` | `string \| undefined` | `—` | Explicit error message — always wins over resolved messages. | | `errorDisplay` | `OgeInputErrorDisplay` | `'touched'` | When resolved errors become visible. | | `debounce` | `number \| undefined` | `—` | Commit delay in ms for `value`/forms updates; blur and Enter flush immediately. | #### Methods _Common (all editors)_ | Name | Type | Description | | --- | --- | --- | | `focus(): void` | `void` | Moves keyboard focus to the native input. | | `blur(): void` | `void` | Blurs the native input. | | `clear(): void` | `void` | Clears the value (commits immediately), keeps focus in the field; no-op when disabled/readonly. | | `reset(value?: T): void` | `void` | Returns the field to pristine: sets `value` (default: empty), clears touched/dirty/parse errors, cancels pending commits. On a reactive-forms-bound editor resets the control itself. | #### Events _Common (all editors)_ | Name | Type | Description | | --- | --- | --- | | `valueCommitted` | `OgeInputValueCommittedEvent` | Every committed change with `previousValue` + originating DOM event (`undefined` for programmatic writes) — the reference `onValueChanged` shape. | | `inputChange` | `OgeInputRawEvent` | Raw text on every keystroke, regardless of commit policy. | | `cleared` | `void` | Value cleared via the clear button / `clear()`. | | `enterKey` | `OgeInputKeyEvent` | Enter pressed inside the editor (pending debounce is flushed first). | | `focused` | `OgeInputFocusEvent` | The editor received focus. | | `blurred` | `OgeInputFocusEvent` | The editor lost focus. | | `touch` | `void` | Signal Forms `FormValueControl` contract — emitted once per blur. | | `valueChange` | `T` | Implicit output of the `value` model. | ### OgeTextArea — `` #### Properties _OgeTextArea_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `value` | `model` | `''` | Editor value — two-way. | | `rows` | `number` | `3` | Visible rows when `autoResize` is off; the floor when it is on. | | `autoResize` | `boolean` | `false` | Grow/shrink with content between `minRows` and `maxRows`. | | `minRows` | `number \| undefined` | `—` | Defaults to `rows`. | | `maxRows` | `number \| undefined` | `—` | `undefined` = unbounded growth. | | `maxLength` | `number \| undefined` | `—` | Counter denominator / native cap. | | `minLength` | `number \| undefined` | `—` | Native `minlength` attribute. | | `showCounter` | `boolean` | `false` | Grapheme-accurate character counter. | | `counterMode` | `OgeInputCounterMode` | `'limit'` | Enforce `maxLength` natively, or soft-cap. | | `spellcheck` | `boolean` | `true` | Non-optional here, unlike the text box. | | `autocapitalize` | `string \| undefined` | `—` | Native `autocapitalize` attribute. | _Common — field chrome (all editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `label` | `string` | `''` | Field label; placement follows `labelMode`. | | `labelMode` | `OgeInputLabelMode` | `'static'` | Label placement: static / floating / hidden (aria-only) / outside. | | `stylingMode` | `OgeInputStylingMode` | `'outlined'` | Container fill style. | | `size` | `OgeInputSize` | `'md'` | Container height preset — 28/34/42px, the button scale. | | `placeholder` | `string` | `''` | Native placeholder text. | | `hint` | `string \| undefined` | `—` | Helper text in the subscript region (hidden while an error shows). | | `tooltip` | `string \| undefined` | `—` | Native `title` attribute of the input element. | | `subscriptSizing` | `OgeInputSubscriptSizing` | `'fixed'` | Whether the hint/error line reserves height, collapses, or is removed. | | `fluid` | `boolean` | `false` | Stretches the field to 100% width (default 240px via `--oge-input-width`). | | `showClearButton` | `boolean` | `false` | Renders the clear (✕) button while the field has a value. | | `showSuccessIcon` | `OgeInputShowSuccessIcon` | `false` | Success icon when valid: `false` / on touch / always. | | `id` | `string \| undefined` | `—` | Base for the generated element ids (input/label/hint/error/counter). | | `tabIndex` | `number` | `0` | Tab order of the native input. | | `autofocus` | `boolean` | `false` | Focuses the editor after its first render. | | `selectOnFocus` | `boolean` | `false` | Selects the whole text when the input receives focus. | | `inputAttr` | `Record` | `{}` | Escape hatch: extra attributes rendered onto the native input (template-owned attributes are ignored). | | `messages` | `Partial \| undefined` | `—` | Per-instance overrides of user-facing strings. | _Common — state & forms (all editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `disabled` | `boolean` | `false` | Disables the editor. | | `readonly` | `boolean` | `false` | Focusable but not editable. (Contract name — not `readOnly`.) | | `required` | `boolean` | `false` | Marks the field required (label asterisk + validation). | | `name` | `string` | `''` | Native `name` attribute. | | `invalid` | `boolean` | `false` | External invalid override — combined with forms state. | | `pending` | `boolean` | `false` | Async-validation indicator; a spinner shows in the rail while `true`. | | `touched` | `boolean` | `false` | External touched override (Signal Forms contract). | | `dirty` | `boolean` | `false` | External dirty override (Signal Forms contract). | | `errors` | `readonly OgeFieldError[]` | `[]` | Signal Forms validation errors (auto-bound by `[formField]`). | | `errorText` | `string \| undefined` | `—` | Explicit error message — always wins over resolved messages. | | `errorDisplay` | `OgeInputErrorDisplay` | `'touched'` | When resolved errors become visible. | | `debounce` | `number \| undefined` | `—` | Commit delay in ms for `value`/forms updates; blur and Enter flush immediately. | #### Methods _Common (all editors)_ | Name | Type | Description | | --- | --- | --- | | `focus(): void` | `void` | Moves keyboard focus to the native input. | | `blur(): void` | `void` | Blurs the native input. | | `clear(): void` | `void` | Clears the value (commits immediately), keeps focus in the field; no-op when disabled/readonly. | | `reset(value?: T): void` | `void` | Returns the field to pristine: sets `value` (default: empty), clears touched/dirty/parse errors, cancels pending commits. On a reactive-forms-bound editor resets the control itself. | #### Events _Common (all editors)_ | Name | Type | Description | | --- | --- | --- | | `valueCommitted` | `OgeInputValueCommittedEvent` | Every committed change with `previousValue` + originating DOM event (`undefined` for programmatic writes) — the reference `onValueChanged` shape. | | `inputChange` | `OgeInputRawEvent` | Raw text on every keystroke, regardless of commit policy. | | `cleared` | `void` | Value cleared via the clear button / `clear()`. | | `enterKey` | `OgeInputKeyEvent` | Enter pressed inside the editor (pending debounce is flushed first). | | `focused` | `OgeInputFocusEvent` | The editor received focus. | | `blurred` | `OgeInputFocusEvent` | The editor lost focus. | | `touch` | `void` | Signal Forms `FormValueControl` contract — emitted once per blur. | | `valueChange` | `T` | Implicit output of the `value` model. | ### OgeNumberBox — `` #### Properties _OgeNumberBox_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `value` | `model` | `null` | `null` is the empty state — never `0`. | | `min` | `number \| undefined` | `—` | Lower bound — values clamp on commit (typing is never blocked). | | `max` | `number \| undefined` | `—` | Upper bound — clamped on commit. | | `step` | `number` | `1` | Spin/arrow-key increment. Spinning commits immediately. | | `showSpinButtons` | `boolean` | `false` | Up/down spin buttons with hold-to-repeat. | | `format` | `Intl.NumberFormatOptions \| undefined` | `—` | Display formatting applied while unfocused; focus shows the raw number. `style: 'percent'` formats display only — the model value is not rescaled. | | `locale` | `string \| undefined` | `—` | Overrides the application locale (`LOCALE_ID`). | | `mode` | `OgeNumberBoxMode` | `'text'` | Native `type` attribute; `inputmode` is always `decimal`. | _Common — field chrome (all editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `label` | `string` | `''` | Field label; placement follows `labelMode`. | | `labelMode` | `OgeInputLabelMode` | `'static'` | Label placement: static / floating / hidden (aria-only) / outside. | | `stylingMode` | `OgeInputStylingMode` | `'outlined'` | Container fill style. | | `size` | `OgeInputSize` | `'md'` | Container height preset — 28/34/42px, the button scale. | | `placeholder` | `string` | `''` | Native placeholder text. | | `hint` | `string \| undefined` | `—` | Helper text in the subscript region (hidden while an error shows). | | `tooltip` | `string \| undefined` | `—` | Native `title` attribute of the input element. | | `subscriptSizing` | `OgeInputSubscriptSizing` | `'fixed'` | Whether the hint/error line reserves height, collapses, or is removed. | | `fluid` | `boolean` | `false` | Stretches the field to 100% width (default 240px via `--oge-input-width`). | | `showClearButton` | `boolean` | `false` | Renders the clear (✕) button while the field has a value. | | `showSuccessIcon` | `OgeInputShowSuccessIcon` | `false` | Success icon when valid: `false` / on touch / always. | | `id` | `string \| undefined` | `—` | Base for the generated element ids (input/label/hint/error/counter). | | `tabIndex` | `number` | `0` | Tab order of the native input. | | `autofocus` | `boolean` | `false` | Focuses the editor after its first render. | | `selectOnFocus` | `boolean` | `false` | Selects the whole text when the input receives focus. | | `inputAttr` | `Record` | `{}` | Escape hatch: extra attributes rendered onto the native input (template-owned attributes are ignored). | | `messages` | `Partial \| undefined` | `—` | Per-instance overrides of user-facing strings. | _Common — state & forms (all editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `disabled` | `boolean` | `false` | Disables the editor. | | `readonly` | `boolean` | `false` | Focusable but not editable. (Contract name — not `readOnly`.) | | `required` | `boolean` | `false` | Marks the field required (label asterisk + validation). | | `name` | `string` | `''` | Native `name` attribute. | | `invalid` | `boolean` | `false` | External invalid override — combined with forms state. | | `pending` | `boolean` | `false` | Async-validation indicator; a spinner shows in the rail while `true`. | | `touched` | `boolean` | `false` | External touched override (Signal Forms contract). | | `dirty` | `boolean` | `false` | External dirty override (Signal Forms contract). | | `errors` | `readonly OgeFieldError[]` | `[]` | Signal Forms validation errors (auto-bound by `[formField]`). | | `errorText` | `string \| undefined` | `—` | Explicit error message — always wins over resolved messages. | | `errorDisplay` | `OgeInputErrorDisplay` | `'touched'` | When resolved errors become visible. | | `debounce` | `number \| undefined` | `—` | Commit delay in ms for `value`/forms updates; blur and Enter flush immediately. | #### Methods _Common (all editors)_ | Name | Type | Description | | --- | --- | --- | | `focus(): void` | `void` | Moves keyboard focus to the native input. | | `blur(): void` | `void` | Blurs the native input. | | `clear(): void` | `void` | Clears the value (commits immediately), keeps focus in the field; no-op when disabled/readonly. | | `reset(value?: T): void` | `void` | Returns the field to pristine: sets `value` (default: empty), clears touched/dirty/parse errors, cancels pending commits. On a reactive-forms-bound editor resets the control itself. | #### Events _Common (all editors)_ | Name | Type | Description | | --- | --- | --- | | `valueCommitted` | `OgeInputValueCommittedEvent` | Every committed change with `previousValue` + originating DOM event (`undefined` for programmatic writes) — the reference `onValueChanged` shape. | | `inputChange` | `OgeInputRawEvent` | Raw text on every keystroke, regardless of commit policy. | | `cleared` | `void` | Value cleared via the clear button / `clear()`. | | `enterKey` | `OgeInputKeyEvent` | Enter pressed inside the editor (pending debounce is flushed first). | | `focused` | `OgeInputFocusEvent` | The editor received focus. | | `blurred` | `OgeInputFocusEvent` | The editor lost focus. | | `touch` | `void` | Signal Forms `FormValueControl` contract — emitted once per blur. | | `valueChange` | `T` | Implicit output of the `value` model. | ### OgeSelectBox — `` #### Properties _OgeSelectBox_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `value` | `model` | `null` | Committed value (the `valueExpr` of the selected item); two-way. | | `items` | `readonly TItem[] \| OgeSelectBoxItemsFn` | `[]` | The selectable items: an array, or a function invoked lazily on first open (sync or promise; loading/error rows render while pending). The selected item is resolved from this full set, never the filtered one. | | `displayExpr` | `string \| ((item) => string)` | `—` | Item → display text. Omitted, the item itself is stringified. | | `valueExpr` | `string \| ((item) => unknown)` | `—` | Item → committed value. Omitted, the whole item is the value. | | `disabledExpr` | `string \| ((item) => boolean)` | `—` | Marks individual items as non-selectable. | | `searchEnabled` | `boolean` | `false` | Enables typing into the field to filter the list. | | `searchMode` | `'contains' \| 'startswith'` | `'contains'` | How typed search text matches an item. | | `searchExpr` | `string \| string[] \| ((item) => string)` | `—` | Which text the filter matches; defaults to the display text. | | `minSearchLength` | `number` | `0` | Characters required before the filter narrows the list. | | `showDataBeforeSearch` | `boolean` | `false` | Below `minSearchLength`: show the full list (`true`) or nothing (`false`). | | `searchTimeout` | `number \| undefined` | `—` | Debounce before typed text filters the list; `undefined` = config default (250ms). The displayed text is never debounced. | | `acceptCustomValue` | `boolean` | `false` | Lets typed text that matches no item become the value (committed on Enter/blur) — see `customItemCreating`. | | `groupBy` | `string \| ((item) => string)` | `—` | Groups flat items under headers; items are re-ordered by first-seen group. | | `imageExpr` | `string \| ((item) => string)` | `—` | Item → image URL rendered before the option text (avatars, flags…). For inline SVG icons use `itemTemplate`. | | `showDropDownButton` | `boolean` | `true` | Renders the chevron toggle in the field rail. | | `openOnFieldClick` | `boolean` | `true` | Clicking the field opens the popup (select-only mode toggles it). | | `loading` | `boolean` | `false` | Shows a loading row instead of items — server-side filtering escape hatch. | | `dropdownPlacement` | `OgePopupPlacement` | `'bottom-start'` | Preferred popup side/alignment (flips when cramped). | | `dropdownWidth` | `number \| 'anchor'` | `'anchor'` | Popup width: fixed pixels or `'anchor'` to match the field box. | | `dropdownMaxHeight` | `number \| undefined` | `—` | Scrollable list height cap; `undefined` = the CSS default (320px). | | `wrapItemText` | `boolean` | `false` | Wraps long option text instead of ellipsizing it. | | `useItemTextAsTitle` | `boolean` | `false` | Mirrors each option's display text into its `title` attribute. | | `itemTemplate` | `TemplateRef` | `—` | Custom option row rendering; context: `$implicit`, `index`, `selected`, `active`. | | `virtualScroll` | `boolean \| OgeVirtualScrollOptions` | `false` | Windowed rendering for large lists (`{ itemHeight, overscan }`). Rows get a fixed size-matched height; `groupBy` and `wrapItemText` are ignored while active. | | `opened` | `model` | `false` | Popup visibility — two-way. | | `selectedItem` | `Signal` | `—` | Read-only: the item whose `valueExpr` matches `value`. | | `displayText` | `Signal` | `—` | Read-only: display text of the selected item. | _Common — field chrome (all editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `label` | `string` | `''` | Field label; placement follows `labelMode`. | | `labelMode` | `OgeInputLabelMode` | `'static'` | Label placement: static / floating / hidden (aria-only) / outside. | | `stylingMode` | `OgeInputStylingMode` | `'outlined'` | Container fill style. | | `size` | `OgeInputSize` | `'md'` | Container height preset — 28/34/42px, the button scale. | | `placeholder` | `string` | `''` | Native placeholder text. | | `hint` | `string \| undefined` | `—` | Helper text in the subscript region (hidden while an error shows). | | `tooltip` | `string \| undefined` | `—` | Native `title` attribute of the input element. | | `subscriptSizing` | `OgeInputSubscriptSizing` | `'fixed'` | Whether the hint/error line reserves height, collapses, or is removed. | | `fluid` | `boolean` | `false` | Stretches the field to 100% width (default 240px via `--oge-input-width`). | | `showClearButton` | `boolean` | `false` | Renders the clear (✕) button while the field has a value. | | `showSuccessIcon` | `OgeInputShowSuccessIcon` | `false` | Success icon when valid: `false` / on touch / always. | | `id` | `string \| undefined` | `—` | Base for the generated element ids (input/label/hint/error/counter). | | `tabIndex` | `number` | `0` | Tab order of the native input. | | `autofocus` | `boolean` | `false` | Focuses the editor after its first render. | | `selectOnFocus` | `boolean` | `false` | Selects the whole text when the input receives focus. | | `inputAttr` | `Record` | `{}` | Escape hatch: extra attributes rendered onto the native input (template-owned attributes are ignored). | | `messages` | `Partial \| undefined` | `—` | Per-instance overrides of user-facing strings. | _Common — state & forms (all editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `disabled` | `boolean` | `false` | Disables the editor. | | `readonly` | `boolean` | `false` | Focusable but not editable. (Contract name — not `readOnly`.) | | `required` | `boolean` | `false` | Marks the field required (label asterisk + validation). | | `name` | `string` | `''` | Native `name` attribute. | | `invalid` | `boolean` | `false` | External invalid override — combined with forms state. | | `pending` | `boolean` | `false` | Async-validation indicator; a spinner shows in the rail while `true`. | | `touched` | `boolean` | `false` | External touched override (Signal Forms contract). | | `dirty` | `boolean` | `false` | External dirty override (Signal Forms contract). | | `errors` | `readonly OgeFieldError[]` | `[]` | Signal Forms validation errors (auto-bound by `[formField]`). | | `errorText` | `string \| undefined` | `—` | Explicit error message — always wins over resolved messages. | | `errorDisplay` | `OgeInputErrorDisplay` | `'touched'` | When resolved errors become visible. | | `debounce` | `number \| undefined` | `—` | Commit delay in ms for `value`/forms updates; blur and Enter flush immediately. | #### Methods _OgeSelectBox methods_ | Name | Type | Description | | --- | --- | --- | | `open()` | `void` | Opens the popup (no-op while disabled/readonly). | | `close()` | `void` | Closes the popup. | | `toggle()` | `void` | Toggles the popup. | _Common (all editors)_ | Name | Type | Description | | --- | --- | --- | | `focus(): void` | `void` | Moves keyboard focus to the native input. | | `blur(): void` | `void` | Blurs the native input. | | `clear(): void` | `void` | Clears the value (commits immediately), keeps focus in the field; no-op when disabled/readonly. | | `reset(value?: T): void` | `void` | Returns the field to pristine: sets `value` (default: empty), clears touched/dirty/parse errors, cancels pending commits. On a reactive-forms-bound editor resets the control itself. | #### Events _OgeSelectBox events_ | Name | Type | Description | | --- | --- | --- | | `selectionChanged` | `OgeSelectBoxSelectionChangedEvent` | The resolved selected item changed (user or programmatic) — `{ item, previousItem }`. | | `itemClick` | `OgeSelectBoxItemClickEvent` | An option row was activated — `{ item, index, event }`; `index` is within the visible (filtered) list. | | `dropDownOpened / dropDownClosed` | `void` | Popup visibility changes, from any trigger. | | `searchChanged` | `OgeSelectBoxSearchChangedEvent` | Raw search text on every keystroke — drive server-side filtering from here. | | `customItemCreating` | `OgeSelectBoxCustomItemEvent` | Mutable payload (as in the references): assign `customItem` — an item, a promise of one, or `null` to reject the text. Left unset, the raw text becomes the item. | _Common (all editors)_ | Name | Type | Description | | --- | --- | --- | | `valueCommitted` | `OgeInputValueCommittedEvent` | Every committed change with `previousValue` + originating DOM event (`undefined` for programmatic writes) — the reference `onValueChanged` shape. | | `inputChange` | `OgeInputRawEvent` | Raw text on every keystroke, regardless of commit policy. | | `cleared` | `void` | Value cleared via the clear button / `clear()`. | | `enterKey` | `OgeInputKeyEvent` | Enter pressed inside the editor (pending debounce is flushed first). | | `focused` | `OgeInputFocusEvent` | The editor received focus. | | `blurred` | `OgeInputFocusEvent` | The editor lost focus. | | `touch` | `void` | Signal Forms `FormValueControl` contract — emitted once per blur. | | `valueChange` | `T` | Implicit output of the `value` model. | #### Types _Select box types_ | Name | Type | Description | | --- | --- | --- | | `OgeSelectBoxDisplayExpr / ValueExpr / DisabledExpr` | `string \| fn` | Field-name string or function expressions for display text, committed value and per-item disabling. | | `OgeSelectBoxSearchMode` | `'contains' \| 'startswith'` | Filter match mode. | | `OgeSelectItemTemplateContext` | `interface` | `{ $implicit: TItem; index: number; selected: boolean; active: boolean }`. | ### OgeTreeSelect — `` #### Properties _Value & data_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `value` | `RowKey \| readonly RowKey[] \| null` | `null` | Committed value — two-way. The selected node's key in `single` mode, an array of keys in `multiple`. | | `items` | `readonly TItem[] \| undefined` | `—` | Nodes to display — a flat parent-referencing list or nested children. | | `keyExpr / parentIdExpr / itemsExpr` | `string \| ((row: TItem) => …)` | `—` | Identity and structure accessors, forwarded to the popup tree. `itemsExpr` switches to hierarchical data. | | `displayExpr` | `string \| ((row: TItem) => unknown)` | `'text'` | Node label, used both in the tree and for the text shown in the closed field. | | `disabledExpr / hasItemsExpr / iconExpr / rootValue / dataStructure` | `see OgeTreeView` | `—` | Forwarded verbatim to the popup tree. | _Selection_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `selectionMode` | `'single' \| 'multiple'` | `'single'` | `multiple` makes `value` an array and keeps the popup open while picking. | | `showCheckBoxes` | `'none' \| 'normal' \| 'selectAll'` | `'none'` | Checkbox column inside the popup. | | `selectNodesRecursive` | `boolean` | `true` | Cascades selection down to descendants and up to fully-selected parents. | | `selectedKeysMode` | `'all' \| 'leavesOnly' \| 'excludeRecursive'` | `'all'` | Projection applied to the committed keys — `leavesOnly` is usually what you want to store from a cascade. | | `displayMode` | `'text' \| 'count'` | `'text'` | Closed-field rendering for a multiple selection: the joined labels, or just how many are picked. | _Popup_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `opened` | `boolean` | `false` | Whether the popup is open — two-way. | | `expandedKeys` | `readonly RowKey[]` | `[]` | Expanded nodes — two-way, so the shape survives close and reopen. | | `expandEvent` | `'click' \| 'dblclick'` | `'dblclick'` | Which gesture expands inside the popup. Unlike the bare tree this defaults to `dblclick` — in a picker a single click should choose, and the chevron expands either way. | | `searchEnabled / searchMode / filterMode` | `see OgeTreeView` | `—` | Puts the tree's own search box inside the popup. | | `loadChildren` | `(parent: TItem, key: RowKey) => Promise` | `—` | Lazy children, fetched on first expand. | | `virtualScroll` | `boolean \| { itemHeight: number }` | `false` | Windowed rendering inside the popup for very large trees. | | `dropdownPlacement / dropdownWidth / dropdownMaxHeight` | `OgePopupPlacement \| number \| 'anchor'` | `—` | Popup geometry. Width defaults to `'anchor'` (matches the field), max height to 320px. | | `openOnFieldClick` | `boolean` | `true` | Opens on a click anywhere in the field, not only on the chevron. | #### Methods | Name | Type | Description | | --- | --- | --- | | `open() / close() / toggle()` | `() => void` | Imperative popup control. | | `focus() / blur() / reset() / clear()` | `() => void` | Inherited field-chrome control methods. `clear()` empties the value, commits immediately and keeps focus in the field. | #### Events | Name | Type | Description | | --- | --- | --- | | `selectionChanged` | `OgeTreeSelectSelectionChangedEvent` | Emitted after the committed selection changed, with `keys` and `previousKeys` (always arrays, even in single mode). | | `dropDownOpened / dropDownClosed` | `void` | Popup lifecycle. | | `valueCommitted` | `OgeInputValueCommittedEvent` | Inherited commit event carrying the previous value. | #### Types | Name | Type | Description | | --- | --- | --- | | `OgeTreeSelectSelectionMode` | `'single' \| 'multiple'` | How many nodes may be committed. | | `OgeTreeSelectDisplayMode` | `'text' \| 'count'` | Closed-field rendering of a multiple selection. | | `OgeTreeSelectSelectionChangedEvent` | `{ keys, previousKeys }` | Payload of `selectionChanged`. | ### OgeTagBox — `` #### Properties _OgeTagBox_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `value` | `model` | `[]` | Committed values — the `valueExpr` of every selected item; two-way. | | `items / displayExpr / valueExpr / disabledExpr / imageExpr` | `shared with OgeSelectBox` | `—` | The tag box reuses the select box expression vocabulary verbatim. | | `searchEnabled / searchMode / searchExpr` | `shared with OgeSelectBox` | `—` | Client-side filtering of the option list. | | `showSelectionControls` | `boolean` | `true` | Renders checkboxes in front of the options. | | `hideSelectedItems` | `boolean` | `false` | Hides already-selected items from the popup list. | | `maxDisplayedTags` | `number \| undefined` | `—` | Caps the rendered chips; the rest collapse into a `+N` chip. | | `opened / dropdownPlacement / dropdownWidth / dropdownMaxHeight / showDropDownButton / openOnFieldClick` | `shared with OgeSelectBox` | `—` | Popup configuration and two-way visibility. | | `virtualScroll` | `boolean \| OgeVirtualScrollOptions` | `false` | Windowed rendering for large lists — same contract as the select box. | _Common — field chrome (all editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `label` | `string` | `''` | Field label; placement follows `labelMode`. | | `labelMode` | `OgeInputLabelMode` | `'static'` | Label placement: static / floating / hidden (aria-only) / outside. | | `stylingMode` | `OgeInputStylingMode` | `'outlined'` | Container fill style. | | `size` | `OgeInputSize` | `'md'` | Container height preset — 28/34/42px, the button scale. | | `placeholder` | `string` | `''` | Native placeholder text. | | `hint` | `string \| undefined` | `—` | Helper text in the subscript region (hidden while an error shows). | | `tooltip` | `string \| undefined` | `—` | Native `title` attribute of the input element. | | `subscriptSizing` | `OgeInputSubscriptSizing` | `'fixed'` | Whether the hint/error line reserves height, collapses, or is removed. | | `fluid` | `boolean` | `false` | Stretches the field to 100% width (default 240px via `--oge-input-width`). | | `showClearButton` | `boolean` | `false` | Renders the clear (✕) button while the field has a value. | | `showSuccessIcon` | `OgeInputShowSuccessIcon` | `false` | Success icon when valid: `false` / on touch / always. | | `id` | `string \| undefined` | `—` | Base for the generated element ids (input/label/hint/error/counter). | | `tabIndex` | `number` | `0` | Tab order of the native input. | | `autofocus` | `boolean` | `false` | Focuses the editor after its first render. | | `selectOnFocus` | `boolean` | `false` | Selects the whole text when the input receives focus. | | `inputAttr` | `Record` | `{}` | Escape hatch: extra attributes rendered onto the native input (template-owned attributes are ignored). | | `messages` | `Partial \| undefined` | `—` | Per-instance overrides of user-facing strings. | _Common — state & forms (all editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `disabled` | `boolean` | `false` | Disables the editor. | | `readonly` | `boolean` | `false` | Focusable but not editable. (Contract name — not `readOnly`.) | | `required` | `boolean` | `false` | Marks the field required (label asterisk + validation). | | `name` | `string` | `''` | Native `name` attribute. | | `invalid` | `boolean` | `false` | External invalid override — combined with forms state. | | `pending` | `boolean` | `false` | Async-validation indicator; a spinner shows in the rail while `true`. | | `touched` | `boolean` | `false` | External touched override (Signal Forms contract). | | `dirty` | `boolean` | `false` | External dirty override (Signal Forms contract). | | `errors` | `readonly OgeFieldError[]` | `[]` | Signal Forms validation errors (auto-bound by `[formField]`). | | `errorText` | `string \| undefined` | `—` | Explicit error message — always wins over resolved messages. | | `errorDisplay` | `OgeInputErrorDisplay` | `'touched'` | When resolved errors become visible. | | `debounce` | `number \| undefined` | `—` | Commit delay in ms for `value`/forms updates; blur and Enter flush immediately. | #### Methods _OgeTagBox methods_ | Name | Type | Description | | --- | --- | --- | | `open() / close() / toggle()` | `void` | Popup control (no-ops while disabled/readonly). | _Common (all editors)_ | Name | Type | Description | | --- | --- | --- | | `focus(): void` | `void` | Moves keyboard focus to the native input. | | `blur(): void` | `void` | Blurs the native input. | | `clear(): void` | `void` | Clears the value (commits immediately), keeps focus in the field; no-op when disabled/readonly. | | `reset(value?: T): void` | `void` | Returns the field to pristine: sets `value` (default: empty), clears touched/dirty/parse errors, cancels pending commits. On a reactive-forms-bound editor resets the control itself. | #### Events _OgeTagBox events_ | Name | Type | Description | | --- | --- | --- | | `selectionChanged` | `OgeTagBoxSelectionChangedEvent` | Per-commit delta — `{ addedItems, removedItems }`. | | `itemClick` | `OgeTagBoxItemClickEvent` | An option row was toggled — `{ item, index, event }`. | | `dropDownOpened / dropDownClosed` | `void` | Popup visibility changes, from any trigger. | _Common (all editors)_ | Name | Type | Description | | --- | --- | --- | | `valueCommitted` | `OgeInputValueCommittedEvent` | Every committed change with `previousValue` + originating DOM event (`undefined` for programmatic writes) — the reference `onValueChanged` shape. | | `inputChange` | `OgeInputRawEvent` | Raw text on every keystroke, regardless of commit policy. | | `cleared` | `void` | Value cleared via the clear button / `clear()`. | | `enterKey` | `OgeInputKeyEvent` | Enter pressed inside the editor (pending debounce is flushed first). | | `focused` | `OgeInputFocusEvent` | The editor received focus. | | `blurred` | `OgeInputFocusEvent` | The editor lost focus. | | `touch` | `void` | Signal Forms `FormValueControl` contract — emitted once per blur. | | `valueChange` | `T` | Implicit output of the `value` model. | ### OgeAutocomplete — `` #### Properties _OgeAutocomplete_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `value` | `model` | `''` | The typed text — the committed value is the string itself, not an item value; two-way. | | `items` | `readonly TItem[] \| OgeSelectBoxItemsFn` | `[]` | The suggestion items: an array, or a function invoked lazily on first open (sync or promise; loading/error rows render while pending). | | `displayExpr / disabledExpr / imageExpr / searchExpr / searchMode / groupBy / itemTemplate` | `shared with OgeSelectBox` | `—` | The autocomplete reuses the select box expression vocabulary and list rendering verbatim (no `valueExpr` — the value is text). | | `minSearchLength` | `number` | `1` | Characters required before suggestions open while typing; deleting below the threshold closes the list. | | `maxItemCount` | `number` | `10` | Caps the rendered suggestion list. | | `searchTimeout` | `number \| undefined` | `—` | Debounce before typed text filters the list; `undefined` = config default (250ms). The displayed text is never debounced. | | `forceSelection` | `boolean` | `false` | Reverts non-matching text to the last committed value on blur; an exact display match resolves to the item with its canonical casing. | | `searchHighlight` | `boolean` | `true` | Marks the matched part of each suggestion (``). | | `showDropDownButton` | `boolean` | `false` | Renders the chevron toggle in the field rail (off by default — reference parity). | | `openOnFieldClick` | `boolean` | `false` | Clicking the field opens the suggestion list. | | `loading / dropdownPlacement / dropdownWidth / dropdownMaxHeight / wrapItemText / useItemTextAsTitle` | `shared with OgeSelectBox` | `—` | Popup configuration and list rendering. | | `virtualScroll` | `boolean \| OgeVirtualScrollOptions` | `false` | Windowed rendering for large lists — same contract as the select box. | | `opened` | `model` | `false` | Popup visibility — two-way. | | `selectedItem` | `Signal` | `—` | Read-only: the last picked suggestion; `null` once the text diverges from it. | _Common — field chrome (all editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `label` | `string` | `''` | Field label; placement follows `labelMode`. | | `labelMode` | `OgeInputLabelMode` | `'static'` | Label placement: static / floating / hidden (aria-only) / outside. | | `stylingMode` | `OgeInputStylingMode` | `'outlined'` | Container fill style. | | `size` | `OgeInputSize` | `'md'` | Container height preset — 28/34/42px, the button scale. | | `placeholder` | `string` | `''` | Native placeholder text. | | `hint` | `string \| undefined` | `—` | Helper text in the subscript region (hidden while an error shows). | | `tooltip` | `string \| undefined` | `—` | Native `title` attribute of the input element. | | `subscriptSizing` | `OgeInputSubscriptSizing` | `'fixed'` | Whether the hint/error line reserves height, collapses, or is removed. | | `fluid` | `boolean` | `false` | Stretches the field to 100% width (default 240px via `--oge-input-width`). | | `showClearButton` | `boolean` | `false` | Renders the clear (✕) button while the field has a value. | | `showSuccessIcon` | `OgeInputShowSuccessIcon` | `false` | Success icon when valid: `false` / on touch / always. | | `id` | `string \| undefined` | `—` | Base for the generated element ids (input/label/hint/error/counter). | | `tabIndex` | `number` | `0` | Tab order of the native input. | | `autofocus` | `boolean` | `false` | Focuses the editor after its first render. | | `selectOnFocus` | `boolean` | `false` | Selects the whole text when the input receives focus. | | `inputAttr` | `Record` | `{}` | Escape hatch: extra attributes rendered onto the native input (template-owned attributes are ignored). | | `messages` | `Partial \| undefined` | `—` | Per-instance overrides of user-facing strings. | _Common — state & forms (all editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `disabled` | `boolean` | `false` | Disables the editor. | | `readonly` | `boolean` | `false` | Focusable but not editable. (Contract name — not `readOnly`.) | | `required` | `boolean` | `false` | Marks the field required (label asterisk + validation). | | `name` | `string` | `''` | Native `name` attribute. | | `invalid` | `boolean` | `false` | External invalid override — combined with forms state. | | `pending` | `boolean` | `false` | Async-validation indicator; a spinner shows in the rail while `true`. | | `touched` | `boolean` | `false` | External touched override (Signal Forms contract). | | `dirty` | `boolean` | `false` | External dirty override (Signal Forms contract). | | `errors` | `readonly OgeFieldError[]` | `[]` | Signal Forms validation errors (auto-bound by `[formField]`). | | `errorText` | `string \| undefined` | `—` | Explicit error message — always wins over resolved messages. | | `errorDisplay` | `OgeInputErrorDisplay` | `'touched'` | When resolved errors become visible. | | `debounce` | `number \| undefined` | `—` | Commit delay in ms for `value`/forms updates; blur and Enter flush immediately. | #### Methods _OgeAutocomplete methods_ | Name | Type | Description | | --- | --- | --- | | `open() / close() / toggle()` | `void` | Popup control (no-ops while disabled/readonly). | _Common (all editors)_ | Name | Type | Description | | --- | --- | --- | | `focus(): void` | `void` | Moves keyboard focus to the native input. | | `blur(): void` | `void` | Blurs the native input. | | `clear(): void` | `void` | Clears the value (commits immediately), keeps focus in the field; no-op when disabled/readonly. | | `reset(value?: T): void` | `void` | Returns the field to pristine: sets `value` (default: empty), clears touched/dirty/parse errors, cancels pending commits. On a reactive-forms-bound editor resets the control itself. | #### Events _OgeAutocomplete events_ | Name | Type | Description | | --- | --- | --- | | `selectionChanged` | `OgeAutocompleteSelectionChangedEvent` | A suggestion was picked or the selection was canceled — `{ item: TItem \| null, event? }`. | | `itemClick` | `OgeAutocompleteItemClickEvent` | A suggestion row was activated — `{ item, index, event }`. | | `dropDownOpened / dropDownClosed` | `void` | Popup visibility changes, from any trigger. | | `searchChanged` | `OgeSelectBoxSearchChangedEvent` | Raw search text on every keystroke — drive server-side filtering from here. | _Common (all editors)_ | Name | Type | Description | | --- | --- | --- | | `valueCommitted` | `OgeInputValueCommittedEvent` | Every committed change with `previousValue` + originating DOM event (`undefined` for programmatic writes) — the reference `onValueChanged` shape. | | `inputChange` | `OgeInputRawEvent` | Raw text on every keystroke, regardless of commit policy. | | `cleared` | `void` | Value cleared via the clear button / `clear()`. | | `enterKey` | `OgeInputKeyEvent` | Enter pressed inside the editor (pending debounce is flushed first). | | `focused` | `OgeInputFocusEvent` | The editor received focus. | | `blurred` | `OgeInputFocusEvent` | The editor lost focus. | | `touch` | `void` | Signal Forms `FormValueControl` contract — emitted once per blur. | | `valueChange` | `T` | Implicit output of the `value` model. | #### Types _Autocomplete types_ | Name | Type | Description | | --- | --- | --- | | `OgeAutocompleteSelectionChangedEvent` | `interface` | `{ item: TItem \| null; event?: Event }` — `null` means the selection was canceled. | | `OgeVirtualScrollOptions` | `interface` | `{ itemHeight?: number; overscan?: number }`; default heights come from `OGE_SELECT_OPTION_HEIGHT` (28/34/40px for sm/md/lg). | ### OgeCheckBox — `` #### Properties _OgeCheckBox_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `label` | `string` | `''` | Text rendered beside the control. | | `value` | `model` | `false` | `true`/`false`, or `null` for the indeterminate (dash) state — two-way. `null` renders regardless of `threeState`. | | `threeState` | `boolean` | `false` | Lets users cycle into the indeterminate state: `null → true → false → null` (the reference cycle). | | `text` | `string` | `''` | Label text; the default `` slot renders when unset. | | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Glyph/font size preset. | | `tooltip` | `string \| undefined` | `—` | Native `title` on the label element. | _Common — state & forms (all editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `disabled` | `boolean` | `false` | Disables the editor. | | `readonly` | `boolean` | `false` | Focusable but not editable. (Contract name — not `readOnly`.) | | `required` | `boolean` | `false` | Marks the field required (label asterisk + validation). | | `name` | `string` | `''` | Native `name` attribute. | | `invalid` | `boolean` | `false` | External invalid override — combined with forms state. | | `pending` | `boolean` | `false` | Async-validation indicator; a spinner shows in the rail while `true`. | | `touched` | `boolean` | `false` | External touched override (Signal Forms contract). | | `dirty` | `boolean` | `false` | External dirty override (Signal Forms contract). | | `errors` | `readonly OgeFieldError[]` | `[]` | Signal Forms validation errors (auto-bound by `[formField]`). | | `errorText` | `string \| undefined` | `—` | Explicit error message — always wins over resolved messages. | | `errorDisplay` | `OgeInputErrorDisplay` | `'touched'` | When resolved errors become visible. | | `debounce` | `number \| undefined` | `—` | Commit delay in ms for `value`/forms updates; blur and Enter flush immediately. | #### Methods _OgeCheckBox methods_ | Name | Type | Description | | --- | --- | --- | | `toggle(): void` | `void` | Advances the state exactly like a user click (respects `threeState`, no-op while disabled/readonly). | _Common (all editors)_ | Name | Type | Description | | --- | --- | --- | | `focus(): void` | `void` | Moves keyboard focus to the native input. | | `blur(): void` | `void` | Blurs the native input. | | `clear(): void` | `void` | Clears the value (commits immediately), keeps focus in the field; no-op when disabled/readonly. | | `reset(value?: T): void` | `void` | Returns the field to pristine: sets `value` (default: empty), clears touched/dirty/parse errors, cancels pending commits. On a reactive-forms-bound editor resets the control itself. | #### Events _Common (all editors)_ | Name | Type | Description | | --- | --- | --- | | `valueCommitted` | `OgeInputValueCommittedEvent` | Every committed change with `previousValue` + originating DOM event (`undefined` for programmatic writes) — the reference `onValueChanged` shape. | | `inputChange` | `OgeInputRawEvent` | Raw text on every keystroke, regardless of commit policy. | | `cleared` | `void` | Value cleared via the clear button / `clear()`. | | `enterKey` | `OgeInputKeyEvent` | Enter pressed inside the editor (pending debounce is flushed first). | | `focused` | `OgeInputFocusEvent` | The editor received focus. | | `blurred` | `OgeInputFocusEvent` | The editor lost focus. | | `touch` | `void` | Signal Forms `FormValueControl` contract — emitted once per blur. | | `valueChange` | `T` | Implicit output of the `value` model. | ### OgeSlider — `` #### Properties _OgeSlider_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `value` | `model` | `0` | The slider value — two-way. Programmatic writes clamp and snap to the step grid. | | `min / max` | `number \| undefined` | `0 / 100` | Scale bounds. Typed `number \| undefined` because the Signal Forms contract reserves these member names — `undefined` falls back to 0/100. | | `step` | `number` | `1` | Arrow-key and drag increment; thumbs always sit on this grid, with float-error correction (0.1-style steps never drift). | | `largeStep` | `number \| undefined` | `—` | PageUp/PageDown increment; `undefined` means `step × 10`. | | `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | A vertical slider announces `aria-orientation="vertical"`; Up still increases (APG). | | `showRange` | `boolean` | `true` | Fills the selected portion of the track. | | `showTicks / tickStep` | `boolean / number \| undefined` | `false` | Tick marks on the `tickStep` grid — falling back to `largeStep`, then `step`; capped at 200 marks. | | `showTickLabels` | `boolean` | `false` | Formatted labels under each tick (Kendo's tick `title` callback, fed by `formatValue`). | | `showLabels` | `boolean` | `false` | Formatted `min`/`max` labels at the track ends. | | `valueIndicator` | `'none' \| 'active' \| 'always'` | `'none'` | The inline value bubble: `'active'` while focused, dragged **or hovered** (Material's discrete plus DevExtreme's `showMode: 'onHover'`), `'always'` permanent. | | `formatValue` | `(value: number) => string \| undefined` | `—` | Formats the bubble, the end labels **and** `aria-valuetext` — display and announcement never diverge. | | `showButtons` | `boolean` | `false` | Kendo-style increment/decrement buttons with press-and-hold repeat — the number box's spin timing config. | | `ariaLabel` | `string \| undefined` | `—` | Accessible name of the thumb; the localized `sliderHandle` message is the fallback. | _Common — state & forms (all editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `disabled` | `boolean` | `false` | Disables the editor. | | `readonly` | `boolean` | `false` | Focusable but not editable. (Contract name — not `readOnly`.) | | `required` | `boolean` | `false` | Marks the field required (label asterisk + validation). | | `name` | `string` | `''` | Native `name` attribute. | | `invalid` | `boolean` | `false` | External invalid override — combined with forms state. | | `pending` | `boolean` | `false` | Async-validation indicator; a spinner shows in the rail while `true`. | | `touched` | `boolean` | `false` | External touched override (Signal Forms contract). | | `dirty` | `boolean` | `false` | External dirty override (Signal Forms contract). | | `errors` | `readonly OgeFieldError[]` | `[]` | Signal Forms validation errors (auto-bound by `[formField]`). | | `errorText` | `string \| undefined` | `—` | Explicit error message — always wins over resolved messages. | | `errorDisplay` | `OgeInputErrorDisplay` | `'touched'` | When resolved errors become visible. | | `debounce` | `number \| undefined` | `—` | Commit delay in ms for `value`/forms updates; blur and Enter flush immediately. | #### Methods _Common (all editors)_ | Name | Type | Description | | --- | --- | --- | | `focus(): void` | `void` | Moves keyboard focus to the native input. | | `blur(): void` | `void` | Blurs the native input. | | `clear(): void` | `void` | Clears the value (commits immediately), keeps focus in the field; no-op when disabled/readonly. | | `reset(value?: T): void` | `void` | Returns the field to pristine: sets `value` (default: empty), clears touched/dirty/parse errors, cancels pending commits. On a reactive-forms-bound editor resets the control itself. | #### Events _OgeSlider events_ | Name | Type | Description | | --- | --- | --- | | `dragStarted` | `OgeSliderDragStartedEvent` | A drag gesture began on the thumb or the track. | | `slideEnded` | `OgeSliderSlideEndedEvent` | Fires once per gesture at release — DevExtreme's `onHandleRelease` timing without a mode switch (live changes stream through `valueCommitted`, throttled by `debounce`). Not emitted when Escape cancels the gesture. | _Common (all editors)_ | Name | Type | Description | | --- | --- | --- | | `valueCommitted` | `OgeInputValueCommittedEvent` | Every committed change with `previousValue` + originating DOM event (`undefined` for programmatic writes) — the reference `onValueChanged` shape. | | `inputChange` | `OgeInputRawEvent` | Raw text on every keystroke, regardless of commit policy. | | `cleared` | `void` | Value cleared via the clear button / `clear()`. | | `enterKey` | `OgeInputKeyEvent` | Enter pressed inside the editor (pending debounce is flushed first). | | `focused` | `OgeInputFocusEvent` | The editor received focus. | | `blurred` | `OgeInputFocusEvent` | The editor lost focus. | | `touch` | `void` | Signal Forms `FormValueControl` contract — emitted once per blur. | | `valueChange` | `T` | Implicit output of the `value` model. | #### Types | Name | Type | Description | | --- | --- | --- | | `OgeSliderOrientation` | `'horizontal' \| 'vertical'` | Axis the track lays along. | | `OgeSliderValueIndicator` | `'none' \| 'active' \| 'always'` | When the inline value bubble shows. | | `OgeSliderDragStartedEvent / OgeSliderSlideEndedEvent` | `{ event } / { value; event }` | The drag gesture pair. | ### OgeRangeSlider — `` #### Properties _OgeRangeSlider_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `value` | `model` | `[0, 0]` | The `[start, end]` pair — two-way. Programmatic writes clamp, snap and sort. | | `minRange` | `number` | `0` | Minimum distance kept between the thumbs — reflected in each thumb’s dynamic `aria-valuemin`/`aria-valuemax` (the APG multi-thumb constraint). | | `startAriaLabel / endAriaLabel` | `string \| undefined` | `—` | Accessible names of the thumbs; the localized `sliderStartHandle`/`sliderEndHandle` messages are the fallbacks. | | `startName / endName` | `string` | `''` | Hidden-input names for plain HTML form posts — DevExtreme's `startName`/`endName` contract (the single slider uses the inherited `name`). | _Shared with OgeSlider_ | Name | Type | Description | | --- | --- | --- | | `min / max / step / largeStep / orientation / showRange / showTicks / tickStep / showLabels / valueIndicator / formatValue` | `—` | The full scale/appearance surface of `OgeSlider`, identical semantics. `showButtons` is single-slider only (the Kendo split). Clicking the track moves the **nearest** thumb. | _Common — state & forms (all editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `disabled` | `boolean` | `false` | Disables the editor. | | `readonly` | `boolean` | `false` | Focusable but not editable. (Contract name — not `readOnly`.) | | `required` | `boolean` | `false` | Marks the field required (label asterisk + validation). | | `name` | `string` | `''` | Native `name` attribute. | | `invalid` | `boolean` | `false` | External invalid override — combined with forms state. | | `pending` | `boolean` | `false` | Async-validation indicator; a spinner shows in the rail while `true`. | | `touched` | `boolean` | `false` | External touched override (Signal Forms contract). | | `dirty` | `boolean` | `false` | External dirty override (Signal Forms contract). | | `errors` | `readonly OgeFieldError[]` | `[]` | Signal Forms validation errors (auto-bound by `[formField]`). | | `errorText` | `string \| undefined` | `—` | Explicit error message — always wins over resolved messages. | | `errorDisplay` | `OgeInputErrorDisplay` | `'touched'` | When resolved errors become visible. | | `debounce` | `number \| undefined` | `—` | Commit delay in ms for `value`/forms updates; blur and Enter flush immediately. | #### Methods _Common (all editors)_ | Name | Type | Description | | --- | --- | --- | | `focus(): void` | `void` | Moves keyboard focus to the native input. | | `blur(): void` | `void` | Blurs the native input. | | `clear(): void` | `void` | Clears the value (commits immediately), keeps focus in the field; no-op when disabled/readonly. | | `reset(value?: T): void` | `void` | Returns the field to pristine: sets `value` (default: empty), clears touched/dirty/parse errors, cancels pending commits. On a reactive-forms-bound editor resets the control itself. | #### Events _OgeRangeSlider events_ | Name | Type | Description | | --- | --- | --- | | `dragStarted / slideEnded` | `OgeSliderSlideEndedEvent` | The drag gesture pair; an unchanged pair never re-emits `valueCommitted`. | _Common (all editors)_ | Name | Type | Description | | --- | --- | --- | | `valueCommitted` | `OgeInputValueCommittedEvent` | Every committed change with `previousValue` + originating DOM event (`undefined` for programmatic writes) — the reference `onValueChanged` shape. | | `inputChange` | `OgeInputRawEvent` | Raw text on every keystroke, regardless of commit policy. | | `cleared` | `void` | Value cleared via the clear button / `clear()`. | | `enterKey` | `OgeInputKeyEvent` | Enter pressed inside the editor (pending debounce is flushed first). | | `focused` | `OgeInputFocusEvent` | The editor received focus. | | `blurred` | `OgeInputFocusEvent` | The editor lost focus. | | `touch` | `void` | Signal Forms `FormValueControl` contract — emitted once per blur. | | `valueChange` | `T` | Implicit output of the `value` model. | ### OgeSwitch — `` #### Properties _OgeSwitch_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `value` | `model` | `false` | The on/off state — two-way. | | `label` | `string` | `''` | Accessible name (`aria-label`). | | `onText / offText` | `string \| undefined` | `—` | Track texts; `undefined` falls back to the localized `switchOn`/`switchOff` messages ('ON'/'OFF'), empty strings hide the text. | | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Track size preset. | _Common — state & forms (all editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `disabled` | `boolean` | `false` | Disables the editor. | | `readonly` | `boolean` | `false` | Focusable but not editable. (Contract name — not `readOnly`.) | | `required` | `boolean` | `false` | Marks the field required (label asterisk + validation). | | `name` | `string` | `''` | Native `name` attribute. | | `invalid` | `boolean` | `false` | External invalid override — combined with forms state. | | `pending` | `boolean` | `false` | Async-validation indicator; a spinner shows in the rail while `true`. | | `touched` | `boolean` | `false` | External touched override (Signal Forms contract). | | `dirty` | `boolean` | `false` | External dirty override (Signal Forms contract). | | `errors` | `readonly OgeFieldError[]` | `[]` | Signal Forms validation errors (auto-bound by `[formField]`). | | `errorText` | `string \| undefined` | `—` | Explicit error message — always wins over resolved messages. | | `errorDisplay` | `OgeInputErrorDisplay` | `'touched'` | When resolved errors become visible. | | `debounce` | `number \| undefined` | `—` | Commit delay in ms for `value`/forms updates; blur and Enter flush immediately. | #### Methods _OgeSwitch methods_ | Name | Type | Description | | --- | --- | --- | | `toggle(): void` | `void` | Flips the state (no-op while disabled/readonly). | _Common (all editors)_ | Name | Type | Description | | --- | --- | --- | | `focus(): void` | `void` | Moves keyboard focus to the native input. | | `blur(): void` | `void` | Blurs the native input. | | `clear(): void` | `void` | Clears the value (commits immediately), keeps focus in the field; no-op when disabled/readonly. | | `reset(value?: T): void` | `void` | Returns the field to pristine: sets `value` (default: empty), clears touched/dirty/parse errors, cancels pending commits. On a reactive-forms-bound editor resets the control itself. | #### Events _Common (all editors)_ | Name | Type | Description | | --- | --- | --- | | `valueCommitted` | `OgeInputValueCommittedEvent` | Every committed change with `previousValue` + originating DOM event (`undefined` for programmatic writes) — the reference `onValueChanged` shape. | | `inputChange` | `OgeInputRawEvent` | Raw text on every keystroke, regardless of commit policy. | | `cleared` | `void` | Value cleared via the clear button / `clear()`. | | `enterKey` | `OgeInputKeyEvent` | Enter pressed inside the editor (pending debounce is flushed first). | | `focused` | `OgeInputFocusEvent` | The editor received focus. | | `blurred` | `OgeInputFocusEvent` | The editor lost focus. | | `touch` | `void` | Signal Forms `FormValueControl` contract — emitted once per blur. | | `valueChange` | `T` | Implicit output of the `value` model. | ### OgeRadioGroup — `` #### Properties _OgeRadioGroup_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `value` | `model` | `null` | The selected item's `valueExpr` result; two-way. | | `items` | `readonly TItem[]` | `[]` | The selectable items. | | `displayExpr / valueExpr / disabledExpr` | `shared with OgeSelectBox` | `—` | Field-name string or function expressions — the select box vocabulary. | | `layout` | `'vertical' \| 'horizontal'` | `'vertical'` | Column or row arrangement. | | `label` | `string` | `''` | Accessible name of the group (`aria-label`). | | `itemTemplate` | `TemplateRef` | `—` | Custom item rendering next to the radio dot; context: `$implicit`, `index`, `selected`, `active`. | | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Dot/font size preset. | _Common — state & forms (all editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `disabled` | `boolean` | `false` | Disables the editor. | | `readonly` | `boolean` | `false` | Focusable but not editable. (Contract name — not `readOnly`.) | | `required` | `boolean` | `false` | Marks the field required (label asterisk + validation). | | `name` | `string` | `''` | Native `name` attribute. | | `invalid` | `boolean` | `false` | External invalid override — combined with forms state. | | `pending` | `boolean` | `false` | Async-validation indicator; a spinner shows in the rail while `true`. | | `touched` | `boolean` | `false` | External touched override (Signal Forms contract). | | `dirty` | `boolean` | `false` | External dirty override (Signal Forms contract). | | `errors` | `readonly OgeFieldError[]` | `[]` | Signal Forms validation errors (auto-bound by `[formField]`). | | `errorText` | `string \| undefined` | `—` | Explicit error message — always wins over resolved messages. | | `errorDisplay` | `OgeInputErrorDisplay` | `'touched'` | When resolved errors become visible. | | `debounce` | `number \| undefined` | `—` | Commit delay in ms for `value`/forms updates; blur and Enter flush immediately. | #### Methods _Common (all editors)_ | Name | Type | Description | | --- | --- | --- | | `focus(): void` | `void` | Moves keyboard focus to the native input. | | `blur(): void` | `void` | Blurs the native input. | | `clear(): void` | `void` | Clears the value (commits immediately), keeps focus in the field; no-op when disabled/readonly. | | `reset(value?: T): void` | `void` | Returns the field to pristine: sets `value` (default: empty), clears touched/dirty/parse errors, cancels pending commits. On a reactive-forms-bound editor resets the control itself. | #### Events _OgeRadioGroup events_ | Name | Type | Description | | --- | --- | --- | | `itemClick` | `OgeRadioGroupItemClickEvent` | A radio item was activated by click or keyboard — `{ item, index, event }`. | _Common (all editors)_ | Name | Type | Description | | --- | --- | --- | | `valueCommitted` | `OgeInputValueCommittedEvent` | Every committed change with `previousValue` + originating DOM event (`undefined` for programmatic writes) — the reference `onValueChanged` shape. | | `inputChange` | `OgeInputRawEvent` | Raw text on every keystroke, regardless of commit policy. | | `cleared` | `void` | Value cleared via the clear button / `clear()`. | | `enterKey` | `OgeInputKeyEvent` | Enter pressed inside the editor (pending debounce is flushed first). | | `focused` | `OgeInputFocusEvent` | The editor received focus. | | `blurred` | `OgeInputFocusEvent` | The editor lost focus. | | `touch` | `void` | Signal Forms `FormValueControl` contract — emitted once per blur. | | `valueChange` | `T` | Implicit output of the `value` model. | ### OgeCalendar — `` #### Properties _Slot & locale helper_ | Name | Type | Description | | --- | --- | --- | | `*ogeCalendarCellTemplate` | `OgeCalendarCellTemplate` | Replaces the default day-cell rendering — badges, prices, availability dots. Also usable on `oge-date-box` and `oge-date-range-box`, which project into the same calendar. | | `datePartOrder(locale, kind)` | `(locale: string \| undefined, kind: 'date' \| 'datetime' \| 'time') => string[]` | The order a locale writes date parts in, derived from `Intl`. Drives locale-aware typed parsing; exported so consumers can build their own date editors on the same rules. | _OgeCalendar_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `label` | `string` | `''` | Accessible name of the grid (`aria-label`); the messages supply a default. | | `value` | `model` | `null` | The selected day (single mode) — two-way, local Date. | | `values` | `model` | `[]` | Selected days for `selectionMode: 'multiple'` — two-way. | | `selectionMode` | `'single' \| 'multiple' \| 'range'` | `'single'` | Range mode picks a start–end pair with a live hover preview. | | `range` | `model<[Date \| null, Date \| null]>` | `[null, null]` | The selected tuple for `selectionMode: 'range'` — two-way; either end may stay open. | | `viewsCount` | `1 \| 2` | `1` | Side-by-side month views (2 is the range layout). | | `zoomLevel / minZoomLevel / maxZoomLevel` | `'month' \| 'year' \| 'decade'` | `'month' / 'decade' / 'month'` | Drill level (two-way) and its reachable bounds; dx's 'century' is deliberately dropped. | | `min / max` | `Date \| undefined` | `—` | Day bounds; `undefined` = unbounded (no dx 1000–3000 defaults). | | `disabledDates` | `Date[] \| ((d: Date) => boolean)` | `—` | Individual unselectable days. | | `firstDayOfWeek` | `number \| undefined` | `—` | 0–6 (Sunday-first); `undefined` resolves from the locale's Intl week info. | | `showWeekNumbers` | `boolean \| { rule: 'firstDay' \| 'firstFourDays' \| 'fullWeek' }` | `false` | Week-number column; `true` = the ISO rule. | | `showTodayButton` | `boolean` | `false` | Renders the localized today shortcut. | | `focusedDate` | `model` | `—` | The keyboard-focused day — two-way (controlled navigation). | | `locale` | `string \| undefined` | `—` | BCP 47 locale for all texts (Intl). | | `cellTemplate` | `TemplateRef` | `—` | Custom cell rendering — also available as the projected `[ogeCalendarCellTemplate]` slot. | _Common — state & forms (all editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `disabled` | `boolean` | `false` | Disables the editor. | | `readonly` | `boolean` | `false` | Focusable but not editable. (Contract name — not `readOnly`.) | | `required` | `boolean` | `false` | Marks the field required (label asterisk + validation). | | `name` | `string` | `''` | Native `name` attribute. | | `invalid` | `boolean` | `false` | External invalid override — combined with forms state. | | `pending` | `boolean` | `false` | Async-validation indicator; a spinner shows in the rail while `true`. | | `touched` | `boolean` | `false` | External touched override (Signal Forms contract). | | `dirty` | `boolean` | `false` | External dirty override (Signal Forms contract). | | `errors` | `readonly OgeFieldError[]` | `[]` | Signal Forms validation errors (auto-bound by `[formField]`). | | `errorText` | `string \| undefined` | `—` | Explicit error message — always wins over resolved messages. | | `errorDisplay` | `OgeInputErrorDisplay` | `'touched'` | When resolved errors become visible. | | `debounce` | `number \| undefined` | `—` | Commit delay in ms for `value`/forms updates; blur and Enter flush immediately. | #### Methods _Common (all editors)_ | Name | Type | Description | | --- | --- | --- | | `focus(): void` | `void` | Moves keyboard focus to the native input. | | `blur(): void` | `void` | Blurs the native input. | | `clear(): void` | `void` | Clears the value (commits immediately), keeps focus in the field; no-op when disabled/readonly. | | `reset(value?: T): void` | `void` | Returns the field to pristine: sets `value` (default: empty), clears touched/dirty/parse errors, cancels pending commits. On a reactive-forms-bound editor resets the control itself. | #### Events _OgeCalendar events_ | Name | Type | Description | | --- | --- | --- | | `cellClick` | `OgeCalendarCellClickEvent` | A day/month/year cell was activated — `{ date, view, event }`. | _Common (all editors)_ | Name | Type | Description | | --- | --- | --- | | `valueCommitted` | `OgeInputValueCommittedEvent` | Every committed change with `previousValue` + originating DOM event (`undefined` for programmatic writes) — the reference `onValueChanged` shape. | | `inputChange` | `OgeInputRawEvent` | Raw text on every keystroke, regardless of commit policy. | | `cleared` | `void` | Value cleared via the clear button / `clear()`. | | `enterKey` | `OgeInputKeyEvent` | Enter pressed inside the editor (pending debounce is flushed first). | | `focused` | `OgeInputFocusEvent` | The editor received focus. | | `blurred` | `OgeInputFocusEvent` | The editor lost focus. | | `touch` | `void` | Signal Forms `FormValueControl` contract — emitted once per blur. | | `valueChange` | `T` | Implicit output of the `value` model. | ### OgeDateBox — `` #### Properties _OgeDateBox_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `dropdownPlacement` | `OgePopupPlacement` | `'bottom-start'` | Preferred popup side/alignment; flips when it would clip. | | `showDropDownButton` | `boolean` | `true` | Renders the rail button that toggles the picker; the field click and the keyboard still open it when hidden. | | `value` | `model` | `null` | Always a local `Date` — serialization is the app's concern (no `dateSerializationFormat`). CVA writes accept ISO-like strings and epoch numbers leniently. | | `type` | `'date' \| 'time' \| 'datetime'` | `'date'` | Picker: calendar, interval time list, or both (no dx `pickerType`). The rail icon follows the type. | | `displayFormat` | `Intl.DateTimeFormatOptions \| ((d: Date) => string)` | `—` | Display text; `undefined` = per-type Intl defaults. No format strings, no date library. | | `min / max / disabledDates` | `as OgeCalendar` | `—` | Out-of-range typed text marks the field invalid — it is never clamped (unlike the number box). | | `interval` | `number` | `30` | Time list step in minutes. | | `timeView` | `'list' \| 'columns'` | `'list'` | Time picker layout: one interval list, or hour + minute columns. | | `applyValueMode` | `'instantly' \| 'useButtons'` | `'instantly'` | OK/Cancel footer collects picker changes in a draft when `useButtons`. | | `acceptCustomValue` | `boolean` | `true` | `false` makes the text read-only (picker input only). | | `openOnFieldClick` | `boolean` | `true` | Clicking the field opens the picker. | | `firstDayOfWeek / showWeekNumbers / zoomLevel / calendarCellTemplate / locale` | `calendar passthroughs` | `—` | Exposed individually — no `calendarOptions` kitchen-sink object. | | `opened` | `model` | `false` | Picker visibility — two-way. | _OgeDateRangeBox_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `value` | `model<[Date \| null, Date \| null]>` | `[null, null]` | Start–end tuple on one field: two parsed inputs + a two-view range calendar popup. A reversed pair reorders on commit; either end may stay open. | | `type` | `'date' \| 'datetime'` | `'date'` | `'datetime'` adds start/end time lists to the picker: day and time picks collect in a draft and commit together on OK; both sides parse and render times. | | `interval` | `number` | `30` | Time list step in minutes (`type: 'datetime'`). | | `min / max / disabledDates / firstDayOfWeek / showWeekNumbers / locale / displayFormat / openOnFieldClick / acceptCustomValue` | `as OgeDateBox` | `—` | Shared configuration surface. | _Common — field chrome (all editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `label` | `string` | `''` | Field label; placement follows `labelMode`. | | `labelMode` | `OgeInputLabelMode` | `'static'` | Label placement: static / floating / hidden (aria-only) / outside. | | `stylingMode` | `OgeInputStylingMode` | `'outlined'` | Container fill style. | | `size` | `OgeInputSize` | `'md'` | Container height preset — 28/34/42px, the button scale. | | `placeholder` | `string` | `''` | Native placeholder text. | | `hint` | `string \| undefined` | `—` | Helper text in the subscript region (hidden while an error shows). | | `tooltip` | `string \| undefined` | `—` | Native `title` attribute of the input element. | | `subscriptSizing` | `OgeInputSubscriptSizing` | `'fixed'` | Whether the hint/error line reserves height, collapses, or is removed. | | `fluid` | `boolean` | `false` | Stretches the field to 100% width (default 240px via `--oge-input-width`). | | `showClearButton` | `boolean` | `false` | Renders the clear (✕) button while the field has a value. | | `showSuccessIcon` | `OgeInputShowSuccessIcon` | `false` | Success icon when valid: `false` / on touch / always. | | `id` | `string \| undefined` | `—` | Base for the generated element ids (input/label/hint/error/counter). | | `tabIndex` | `number` | `0` | Tab order of the native input. | | `autofocus` | `boolean` | `false` | Focuses the editor after its first render. | | `selectOnFocus` | `boolean` | `false` | Selects the whole text when the input receives focus. | | `inputAttr` | `Record` | `{}` | Escape hatch: extra attributes rendered onto the native input (template-owned attributes are ignored). | | `messages` | `Partial \| undefined` | `—` | Per-instance overrides of user-facing strings. | _Common — state & forms (all editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `disabled` | `boolean` | `false` | Disables the editor. | | `readonly` | `boolean` | `false` | Focusable but not editable. (Contract name — not `readOnly`.) | | `required` | `boolean` | `false` | Marks the field required (label asterisk + validation). | | `name` | `string` | `''` | Native `name` attribute. | | `invalid` | `boolean` | `false` | External invalid override — combined with forms state. | | `pending` | `boolean` | `false` | Async-validation indicator; a spinner shows in the rail while `true`. | | `touched` | `boolean` | `false` | External touched override (Signal Forms contract). | | `dirty` | `boolean` | `false` | External dirty override (Signal Forms contract). | | `errors` | `readonly OgeFieldError[]` | `[]` | Signal Forms validation errors (auto-bound by `[formField]`). | | `errorText` | `string \| undefined` | `—` | Explicit error message — always wins over resolved messages. | | `errorDisplay` | `OgeInputErrorDisplay` | `'touched'` | When resolved errors become visible. | | `debounce` | `number \| undefined` | `—` | Commit delay in ms for `value`/forms updates; blur and Enter flush immediately. | #### Methods _OgeDateBox methods_ | Name | Type | Description | | --- | --- | --- | | `open() / close() / toggle()` | `void` | Picker control (no-ops while disabled/readonly). | _Common (all editors)_ | Name | Type | Description | | --- | --- | --- | | `focus(): void` | `void` | Moves keyboard focus to the native input. | | `blur(): void` | `void` | Blurs the native input. | | `clear(): void` | `void` | Clears the value (commits immediately), keeps focus in the field; no-op when disabled/readonly. | | `reset(value?: T): void` | `void` | Returns the field to pristine: sets `value` (default: empty), clears touched/dirty/parse errors, cancels pending commits. On a reactive-forms-bound editor resets the control itself. | #### Events _OgeDateBox events_ | Name | Type | Description | | --- | --- | --- | | `dropDownOpened / dropDownClosed` | `void` | Picker visibility changes, from any trigger. | _Common (all editors)_ | Name | Type | Description | | --- | --- | --- | | `valueCommitted` | `OgeInputValueCommittedEvent` | Every committed change with `previousValue` + originating DOM event (`undefined` for programmatic writes) — the reference `onValueChanged` shape. | | `inputChange` | `OgeInputRawEvent` | Raw text on every keystroke, regardless of commit policy. | | `cleared` | `void` | Value cleared via the clear button / `clear()`. | | `enterKey` | `OgeInputKeyEvent` | Enter pressed inside the editor (pending debounce is flushed first). | | `focused` | `OgeInputFocusEvent` | The editor received focus. | | `blurred` | `OgeInputFocusEvent` | The editor lost focus. | | `touch` | `void` | Signal Forms `FormValueControl` contract — emitted once per blur. | | `valueChange` | `T` | Implicit output of the `value` model. | #### Types _Date types_ | Name | Type | Description | | --- | --- | --- | | `parseDateText(text, locale, kind, reference?)` | `function` | Exported: locale-aware text → local `Date \| null` via Intl part order — never `Date.parse`. | | `OgeDateBoxType / OgeDateBoxApplyValueMode / OgeDateBoxDisplayFormat / OgeDateBoxTimeView` | `types` | The string unions and the display-format shape. | ### OgeColorBox — `` #### Properties _OgeColorBox_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `dropdownPlacement` | `OgePopupPlacement` | `'bottom-start'` | Preferred popup side/alignment; flips when it would clip. | | `showDropDownButton` | `boolean` | `true` | Renders the rail button that toggles the picker; the field click and the keyboard still open it when hidden. | | `value` | `model` | `null` | The committed color as a CSS string, normalized to `format` on user commits. Programmatic writes keep any parseable CSS color verbatim (never reformatted); unparseable writes land as `null`. | | `format` | `'hex' \| 'rgb' \| 'rgba' \| 'hsl'` | `'hex'` | Committed string shape (hex — the DevExtreme default; Kendo defaults to rgba). Translucent colors widen to carry alpha: `#rrggbbaa` / `rgba()` / `hsla()`. | | `view` | `'gradient' \| 'palette' \| 'both'` | `'gradient'` | Popup surfaces: the saturation/brightness gradient with sliders and inputs, the swatch palette, or both stacked — no view switcher (Kendo's `activeView` is deliberately skipped). | | `editAlphaChannel` | `boolean` | `false` | Adds the alpha slider + percent input and lets the output carry alpha. Without it, alpha is coerced to 1 on commit — `rgba()` text still parses. | | `applyValueMode` | `'instantly' \| 'useButtons'` | `'instantly'` | OK/Cancel footer collects panel interactions in a draft when `useButtons`; the default commits live (dragging streams through `valueCommitted`, throttled by `debounce`). | | `acceptCustomValue` | `boolean` | `true` | `false` makes the text read-only (picker input only). Typed text parses any CSS color incl. the 148 named colors; unparseable text reverts on blur. | | `keyStep` | `number` | `5` | Arrow-key increment of the panel parts in value units — hue degrees, alpha percent, surface saturation/brightness percent. PageUp/PageDown move by 5× (value-space, not Kendo’s pixel steps — zoom-independent). | | `palette` | `readonly string[] \| undefined` | `—` | Palette swatches as CSS color strings; `undefined` renders the exported `OGE_DEFAULT_COLOR_PALETTE`. Unparseable entries are dropped. | | `paletteColumns` | `number` | `10` | Swatch columns of the palette grid. | | `openOnFieldClick` | `boolean` | `true` | Clicking the field opens the picker. | | `showDropDownButton` | `boolean` | `true` | `false` hides the rail chevron — field click and ArrowDown still open. | | `showEyedropper` | `boolean` | `true` | The eyedropper button (pick a color from anywhere on screen) — rendered only in browsers shipping the `EyeDropper` API; progressive enhancement, no polyfill. The picked color keeps the working alpha. | | `opened` | `model` | `false` | Picker visibility — two-way. | _Common — field chrome (all editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `label` | `string` | `''` | Field label; placement follows `labelMode`. | | `labelMode` | `OgeInputLabelMode` | `'static'` | Label placement: static / floating / hidden (aria-only) / outside. | | `stylingMode` | `OgeInputStylingMode` | `'outlined'` | Container fill style. | | `size` | `OgeInputSize` | `'md'` | Container height preset — 28/34/42px, the button scale. | | `placeholder` | `string` | `''` | Native placeholder text. | | `hint` | `string \| undefined` | `—` | Helper text in the subscript region (hidden while an error shows). | | `tooltip` | `string \| undefined` | `—` | Native `title` attribute of the input element. | | `subscriptSizing` | `OgeInputSubscriptSizing` | `'fixed'` | Whether the hint/error line reserves height, collapses, or is removed. | | `fluid` | `boolean` | `false` | Stretches the field to 100% width (default 240px via `--oge-input-width`). | | `showClearButton` | `boolean` | `false` | Renders the clear (✕) button while the field has a value. | | `showSuccessIcon` | `OgeInputShowSuccessIcon` | `false` | Success icon when valid: `false` / on touch / always. | | `id` | `string \| undefined` | `—` | Base for the generated element ids (input/label/hint/error/counter). | | `tabIndex` | `number` | `0` | Tab order of the native input. | | `autofocus` | `boolean` | `false` | Focuses the editor after its first render. | | `selectOnFocus` | `boolean` | `false` | Selects the whole text when the input receives focus. | | `inputAttr` | `Record` | `{}` | Escape hatch: extra attributes rendered onto the native input (template-owned attributes are ignored). | | `messages` | `Partial \| undefined` | `—` | Per-instance overrides of user-facing strings. | _Common — state & forms (all editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `disabled` | `boolean` | `false` | Disables the editor. | | `readonly` | `boolean` | `false` | Focusable but not editable. (Contract name — not `readOnly`.) | | `required` | `boolean` | `false` | Marks the field required (label asterisk + validation). | | `name` | `string` | `''` | Native `name` attribute. | | `invalid` | `boolean` | `false` | External invalid override — combined with forms state. | | `pending` | `boolean` | `false` | Async-validation indicator; a spinner shows in the rail while `true`. | | `touched` | `boolean` | `false` | External touched override (Signal Forms contract). | | `dirty` | `boolean` | `false` | External dirty override (Signal Forms contract). | | `errors` | `readonly OgeFieldError[]` | `[]` | Signal Forms validation errors (auto-bound by `[formField]`). | | `errorText` | `string \| undefined` | `—` | Explicit error message — always wins over resolved messages. | | `errorDisplay` | `OgeInputErrorDisplay` | `'touched'` | When resolved errors become visible. | | `debounce` | `number \| undefined` | `—` | Commit delay in ms for `value`/forms updates; blur and Enter flush immediately. | #### Methods _OgeColorBox methods_ | Name | Type | Description | | --- | --- | --- | | `open() / close() / toggle()` | `void` | Picker control (no-ops while disabled/readonly). | _Common (all editors)_ | Name | Type | Description | | --- | --- | --- | | `focus(): void` | `void` | Moves keyboard focus to the native input. | | `blur(): void` | `void` | Blurs the native input. | | `clear(): void` | `void` | Clears the value (commits immediately), keeps focus in the field; no-op when disabled/readonly. | | `reset(value?: T): void` | `void` | Returns the field to pristine: sets `value` (default: empty), clears touched/dirty/parse errors, cancels pending commits. On a reactive-forms-bound editor resets the control itself. | #### Events _OgeColorBox events_ | Name | Type | Description | | --- | --- | --- | | `dropDownOpened / dropDownClosed` | `void` | Picker visibility changes, from any trigger. | _Common (all editors)_ | Name | Type | Description | | --- | --- | --- | | `valueCommitted` | `OgeInputValueCommittedEvent` | Every committed change with `previousValue` + originating DOM event (`undefined` for programmatic writes) — the reference `onValueChanged` shape. | | `inputChange` | `OgeInputRawEvent` | Raw text on every keystroke, regardless of commit policy. | | `cleared` | `void` | Value cleared via the clear button / `clear()`. | | `enterKey` | `OgeInputKeyEvent` | Enter pressed inside the editor (pending debounce is flushed first). | | `focused` | `OgeInputFocusEvent` | The editor received focus. | | `blurred` | `OgeInputFocusEvent` | The editor lost focus. | | `touch` | `void` | Signal Forms `FormValueControl` contract — emitted once per blur. | | `valueChange` | `T` | Implicit output of the `value` model. | #### Types _Color types_ | Name | Type | Description | | --- | --- | --- | | `OgeColorBoxView / OgeColorBoxApplyValueMode` | `types` | The string unions of `view` and `applyValueMode`. | | `OGE_DEFAULT_COLOR_PALETTE` | `readonly string[]` | The built-in 50-swatch palette used when `palette` is not set. | | `Color messages` | `OgeInputsMessages keys` | All popup strings localize through the family config: `colorPickerLabel`, `hueSliderLabel`/`hueValueText`, `alphaSliderLabel`/`alphaValueText`, `colorSurfaceLabel`/`colorSurfaceRoleDescription`/`surfaceValueText`, `paletteLabel`, the hex/R/G/B/A input labels, `eyedropperButton` and `invalidColorError`. | | `parseColor / formatColor / normalizeColor / rgbaToHsva / hsvaToRgba / relativeLuminance / contrastForeground / colorsEqual` | `@oge-ui/core` | The DOM-free color kernel (`OgeRgba` / `OgeHsva` / `OgeColorFormat`): CSS color-text parsing, HSV↔RGB conversion, canonical formatting and the WCAG swatch-contrast decision — unit-tested without a DOM, the slider-math precedent. | ### Shared input types #### Types _Unions & contracts_ | Name | Type | Description | | --- | --- | --- | | `OgeInputLabelMode` | `'static' \| 'floating' \| 'hidden' \| 'outside'` | `hidden` renders the label as `aria-label` only. | | `OgeInputStylingMode` | `'outlined' \| 'filled' \| 'underlined'` | Container fill style. | | `OgeInputSize` | `'sm' \| 'md' \| 'lg'` | 28 / 34 / 42px heights. | | `OgeInputSubscriptSizing` | `'fixed' \| 'dynamic' \| 'none'` | `fixed` reserves one line so errors never shift layout. | | `OgeInputErrorDisplay` | `'touched' \| 'dirty' \| 'always'` | When resolved errors become visible. | | `OgeInputCounterMode` | `'limit' \| 'soft'` | `soft` allows typing past `maxLength` and colors the counter danger. | | `OgeInputShowSuccessIcon` | `false \| 'touched' \| 'always'` | Success-icon visibility policy. | | `OgeTextBoxMode` | `'text' \| 'email' \| 'password' \| 'search' \| 'tel' \| 'url'` | Native input type of the text box. | | `OgeNumberBoxMode` | `'text' \| 'tel'` | Native input type of the number box. | | `OgeFieldError` | `{ kind: string; message?: string }` | Structural mirror of Signal Forms' `ValidationError`. | _Event payloads_ | Name | Type | Description | | --- | --- | --- | | `OgeInputValueCommittedEvent` | `{ value: T; previousValue: T; event: Event \| undefined }` | `event === undefined` means a programmatic change. | | `OgeInputRawEvent` | `{ text: string; event: Event }` | Raw keystroke payload. | | `OgeInputKeyEvent` | `{ event: KeyboardEvent }` | Enter-key payload. | | `OgeInputFocusEvent` | `{ event: FocusEvent }` | Focus/blur payload. | _Slots & helpers_ | Name | Type | Description | | --- | --- | --- | | `OgeInputPrefix` | `directive — [ogeInputPrefix]` | Leading adornment inside the field. | | `OgeInputSuffix` | `directive — [ogeInputSuffix]` | Trailing adornment; renders after the built-in rail buttons. | | `resolveErrorMessage(sfErrors, cvaErrors, messages)` | `string \| null` | The single message a field displays. Signal Forms errors win over reactive-forms errors, and an explicit `message` wins over the kind→message map. Exported so a form-level error summary reads exactly like the inline text. | | `formatPattern(pattern, values)` | `string` | Interpolates `{token}` placeholders in a message — the same contract the grid uses for its message patterns. | | `OgeInputCounterState` | `{ count: number; max: number \| undefined; over: boolean }` | Counter state rendered in the subscript end slot. | | `OgeInputRevealApi` | `{ visible; active; toggle() }` | Password-reveal API (text box only). | | `OgeInputCopyApi` | `{ visible; copied; trigger() }` | Copy-to-clipboard API (text box only). | | `OgeInputSpinApi` | `{ visible; canUp; canDown; press(dir, event); release() }` | Spin-button API (number box only). | | `measureTextAreaHeight(el, minRows, maxRows?)` | `number` | Fallback auto-resize measurement for browsers without CSS `field-sizing: content`. | ### Inputs configuration #### Methods | Name | Type | Description | | --- | --- | --- | | `provideOgeInputsConfig(config: OgeInputsConfigInput): Provider` | `Provider` | Application- or component-scoped defaults; deep-merges `messages` over the defaults. | #### Types _OgeInputsConfig_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `spinRepeatDelayMs` | `number` | `400` | Delay before spin buttons start repeating. | | `spinRepeatIntervalMs` | `number` | `80` | Interval between spin repeats. | | `copiedResetMs` | `number` | `2000` | How long the copy button shows "copied". | | `messages` | `OgeInputsMessages` | `—` | User-facing strings (see below). | _OgeInputsMessages_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `clearButton` | `string` | `'Clear'` | Aria label of the clear (✕) button. | | `showPassword / hidePassword` | `string` | `'Show password' / 'Hide password'` | Reveal toggle aria labels. | | `copyButton / copied` | `string` | `'Copy to clipboard' / 'Copied'` | Copy button aria label and transient confirmation. | | `spinIncrement / spinDecrement` | `string` | `'Increase value' / 'Decrease value'` | Aria labels of the spin buttons. | | `pending / valid` | `string` | `'Validating' / 'Valid'` | Screen-reader text next to the pending spinner / success icon. | | `counter / counterNoMax` | `string` | `'{count}/{max}' / '{count}'` | Visual counter patterns. | | `counterAria / counterAriaNoMax` | `string` | `'{count} of {max} characters used' / '{count} characters entered'` | Counter aria labels. | | `requiredError` | `string` | `'This field is required'` | Resolved message for the `required` error kind. | | `emailError` | `string` | `'Enter a valid email address'` | Resolved message for the `email` error kind. | | `minError / maxError` | `string` | `'Value must be at least {min}' / '…at most {max}'` | Numeric bound errors. | | `minLengthError / maxLengthError` | `string` | `'Enter at least {requiredLength} characters' / 'Enter no more than…'` | Length errors. | | `patternError` | `string` | `'The value has an invalid format'` | Pattern mismatch. | | `invalidNumberError` | `string` | `'Enter a valid number'` | Number box parse failure (reverts on blur). | | `invalidError` | `string` | `'Invalid value'` | Fallback for unknown validation error kinds. | ### Demos Each demo below is a complete standalone component — copy one whole and it compiles. #### Basic ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeAutocomplete } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeAutocomplete], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly cities = ['Ankara', 'Berlin', 'Lisbon', 'Oslo', 'Tokyo']; // the committed value is the TEXT itself, not an item value protected readonly cityName = signal(''); } ``` #### Chrome ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeAutocomplete } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeAutocomplete], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly tags = ['angular', 'signals', 'zoneless']; protected readonly tag = signal(''); } ``` #### Force ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeAutocomplete } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeAutocomplete], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly users = [ { id: 1, name: 'Elif Kaya' }, { id: 2, name: 'Mert Demir' }, ]; protected readonly assigneeName = signal(''); protected assignee: unknown = null; } ``` #### Lazy ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeAutocomplete } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeAutocomplete], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly repo = signal(''); // invoked once, on first open protected readonly loadRepos = (): Promise => fetch('/api/repos').then((response) => response.json()); protected readonly serverItems = signal([]); protected readonly serverLoading = signal(false); protected queryServer(text: string): void { this.serverLoading.set(true); fetch(`/api/repos?q=${encodeURIComponent(text)}`) .then((response) => response.json()) .then((items: string[]) => this.serverItems.set(items)) .finally(() => this.serverLoading.set(false)); } } ``` #### Tuning ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeAutocomplete } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeAutocomplete], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly products = [ { id: 1, name: 'Aurora Display' }, { id: 2, name: 'Aurora Keyboard' }, { id: 3, name: 'Nimbus Router' }, ]; protected readonly productName = signal(''); } ``` #### Virtual ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeAutocomplete, OgeSelectBox } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeAutocomplete, OgeSelectBox], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly accounts = Array.from( { length: 10000 }, (_, index) => `Account ${index + 1}`, ); protected readonly accountName = signal(''); protected readonly accountId = signal(null); } ``` #### Basic ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeColorBox } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeColorBox], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly brand = signal('#3aa0ff'); } ``` #### Buttons ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeColorBox } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeColorBox], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly theme = signal('#7c3aed'); } ``` #### Formats ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeColorBox } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeColorBox], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly overlay = signal('rgba(58, 160, 255, 0.5)'); protected readonly accent = signal('hsl(210, 100%, 61%)'); } ``` #### Forms ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeForm } from '@oge-ui/forms'; import type { OgeFormItemData } from '@oge-ui/forms'; @Component({ selector: 'demo-root', imports: [OgeForm], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly branding = signal({ primary: '#3aa0ff' }); protected readonly items: OgeFormItemData[] = [ { field: 'primary', label: 'Primary color', editorType: 'colorBox', editorOptions: { colorFormat: 'hex', showClearButton: true }, }, ]; } ``` #### Palette ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeColorBox } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeColorBox], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly tag = signal('#16a34a'); protected readonly swatches: readonly string[] = [ '#dc2626', '#ea580c', '#d97706', '#16a34a', '#0d9488', '#2563eb', '#7c3aed', '#c026d3', '#475569', '#111827', ]; } ``` #### Typed ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeColorBox } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeColorBox], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly typed = signal('rebeccapurple'); } ``` #### Calendar ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeCalendar } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeCalendar], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly date = signal(null); protected readonly min = new Date(2026, 0, 1); protected readonly isWeekend = (day: Date): boolean => day.getDay() === 0 || day.getDay() === 6; } ``` #### Datebox ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeDateBox } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeDateBox], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly start = signal(null); protected readonly delivery = signal(null); protected readonly today = new Date(); } ``` #### Grid ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeColumn, OgeGrid } from '@oge-ui/grid'; @Component({ selector: 'demo-root', imports: [OgeColumn, OgeGrid], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly rows = [ { id: 1, shipped: new Date(2026, 2, 11) }, { id: 2, shipped: new Date(2026, 5, 2) }, ]; } ``` #### Range ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeCalendar, OgeDateRangeBox } from '@oge-ui/inputs'; import type { OgeCalendarRange } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeCalendar, OgeDateRangeBox], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly range = signal([null, null]); protected readonly period = signal([null, null]); protected readonly window = signal([null, null]); } ``` #### Timeview ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeDateBox } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeDateBox], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly t1 = signal(null); protected readonly t2 = signal(null); } ``` #### Types ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeDateBox } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeDateBox], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly at = signal(null); protected readonly alarm = signal(null); protected readonly due = signal(null); } ``` #### Basic ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeNumberBox, OgeTextArea, OgeTextBox } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeNumberBox, OgeTextArea, OgeTextBox], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly name = signal(''); protected readonly amount = signal(null); protected readonly notes = signal(''); } ``` #### Label ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeTextBox } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeTextBox], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo {} ``` #### Prefix ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeInputPrefix, OgeTextBox } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeInputPrefix, OgeTextBox], changeDetection: ChangeDetectionStrategy.OnPush, template: ` https:// `, }) export class Demo {} ``` #### Styling ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeTextBox } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeTextBox], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo {} ``` #### Basic ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeSelectBox } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeSelectBox], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly cities = ['Ankara', 'Berlin', 'Lisbon', 'Oslo', 'Tokyo']; protected readonly city = signal(null); } ``` #### Chrome ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeSelectBox } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeSelectBox], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly countries = ['Germany', 'Netherlands', 'Türkiye']; protected readonly country = signal(null); } ``` #### Group ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeSelectBox } from '@oge-ui/inputs'; import type { OgeSelectBoxCustomItemEvent } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeSelectBox], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly users = [ { id: 1, name: 'Elif Kaya', role: 'Engineering' }, { id: 2, name: 'Mert Demir', role: 'Design' }, ]; protected readonly memberId = signal(null); protected readonly tags = signal(['angular', 'signals']); protected readonly tag = signal(null); protected createTag(event: OgeSelectBoxCustomItemEvent): void { event.customItem = event.text; // or a promise, or null to reject this.tags.update((current) => [...current, event.text]); } } ``` #### Lazy ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeSelectBox } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeSelectBox], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly warehouse = signal(null); // invoked once, on first open — loading/error rows render while pending protected readonly loadWarehouses = () => new Promise((resolve) => setTimeout(() => resolve(['Hamburg', 'İzmir', 'Rotterdam']), 900), ); } ``` #### Mapping ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeSelectBox } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeSelectBox], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly users = [ { id: 1, name: 'Elif Kaya', role: 'Engineering' }, { id: 2, name: 'Mert Demir', role: 'Design' }, { id: 3, name: 'Deniz Ünal', role: 'Engineering' }, ]; protected readonly assigneeId = signal(null); protected onSearch(text: string): void { console.log('searching for', text); } } ``` #### States ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeSelectBox } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeSelectBox], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly plans = [ { id: 'free', name: 'Free', soldOut: false }, { id: 'pro', name: 'Pro', soldOut: false }, { id: 'enterprise', name: 'Enterprise', soldOut: true }, ]; protected readonly planId = signal('free'); } ``` #### Tagbox ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeTagBox } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeTagBox], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly skills = ['Angular', 'TypeScript', 'CSS', 'Testing']; protected readonly selectedSkills = signal(['Angular']); protected readonly users = [ { id: 1, name: 'Elif Kaya', avatar: '/avatars/1.png' }, { id: 2, name: 'Mert Demir', avatar: '/avatars/2.png' }, ]; protected readonly teamIds = signal([]); protected onDelta(added: readonly unknown[], removed: readonly unknown[]): void { console.log({ added, removed }); } } ``` #### Counter ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeTextArea, OgeTextBox } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeTextArea, OgeTextBox], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly bio = signal(''); } ``` #### Debounce ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeTextBox } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeTextBox], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly query = signal(''); protected keystrokes = 0; } ``` #### Number ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeNumberBox } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeNumberBox], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly price = signal(1249.9); } ``` #### Password ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeTextBox } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeTextBox], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly password = signal(''); protected readonly token = signal('oge_live_9f0b01cf'); } ``` #### Basic ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeSlider } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeSlider], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly volume = signal(40); } ``` #### Buttons ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeSlider } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeSlider], changeDetection: ChangeDetectionStrategy.OnPush, template: `
`, }) export class Demo { protected readonly volume = signal(40); } ``` #### Forms ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeForm } from '@oge-ui/forms'; import type { OgeFormItemData } from '@oge-ui/forms'; @Component({ selector: 'demo-root', imports: [OgeForm], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly settings = signal({ brightness: 70 }); protected readonly items: OgeFormItemData[] = [ { field: 'brightness', label: 'Brightness', editorType: 'slider', editorOptions: { min: 0, max: 100, step: 5 }, }, ]; } ``` #### Indicator ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeSlider } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeSlider], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly volume = signal(40); protected readonly asDecibels = (value: number): string => `${value} dB`; } ``` #### Range ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeRangeSlider } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeRangeSlider], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly price = signal([200, 600]); } ``` #### Ticks ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeSlider } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeSlider], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly rating = signal(6); } ``` #### Checkbox ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeCheckBox } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeCheckBox], changeDetection: ChangeDetectionStrategy.OnPush, template: ` I agree to the terms `, }) export class Demo { protected readonly agreed = signal(false); protected readonly all = signal(null); } ``` #### Forms ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { ReactiveFormsModule, FormControl } from '@angular/forms'; import { FormField, form } from '@angular/forms/signals'; import { OgeCheckBox, OgeRadioGroup, OgeSwitch } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [ReactiveFormsModule, FormField, OgeCheckBox, OgeRadioGroup, OgeSwitch], changeDetection: ChangeDetectionStrategy.OnPush, template: ` Accept terms `, }) export class Demo { protected readonly plans = [ { id: 'free', name: 'Free' }, { id: 'pro', name: 'Pro' }, ]; protected readonly model = signal({ terms: false, marketing: false, plan: 'free', }); protected readonly f = form(this.model); protected readonly termsCtrl = new FormControl(false, { nonNullable: true }); } ``` #### Radio ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeRadioGroup } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeRadioGroup], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly plans = [ { id: 'free', name: 'Free', soldOut: false }, { id: 'pro', name: 'Pro', soldOut: false }, { id: 'enterprise', name: 'Enterprise', soldOut: true }, ]; protected readonly planId = signal('free'); protected readonly priority = signal('Normal'); } ``` #### Switch ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeSwitch } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeSwitch], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly notify = signal(true); protected readonly enabled = signal(false); protected readonly plain = signal(false); } ``` #### Basic ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeTreeSelect } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeTreeSelect], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly folders = [ { id: 1, parentId: null, name: 'Documents' }, { id: 2, parentId: 1, name: 'Reports' }, { id: 3, parentId: 2, name: 'Q1.pdf' }, ]; protected readonly folderId = signal(null); } ``` #### Lazy ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeTreeSelect } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeTreeSelect], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly roots = [ { id: 1, parentId: null, name: 'Server root', hasItems: true }, { id: 2, parentId: null, name: 'readme.txt', hasItems: false }, ]; protected readonly remoteId = signal(null); // called once per node, on first expand — a placeholder row shows meanwhile protected readonly loadChildren = (parent: { id: number; name: string }) => new Promise<{ id: number; parentId: number; name: string }[]>((resolve) => setTimeout( () => resolve([{ id: parent.id * 100, parentId: parent.id, name: 'logs' }]), 700, ), ); } ``` #### Multiple ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeTreeSelect } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeTreeSelect], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly folders = [ { id: 1, parentId: null, name: 'Documents' }, { id: 2, parentId: 1, name: 'Reports' }, { id: 3, parentId: 2, name: 'Q1.pdf' }, ]; // checking a node cascades to its descendants; 'leavesOnly' reports // just the childless keys, so the value stays the concrete grants protected readonly permissions = signal([]); } ``` #### Nested ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeTreeSelect } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeTreeSelect], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly tree = [ { id: 1, name: 'src', children: [{ id: 2, name: 'main.ts' }] }, ]; protected readonly fileId = signal(null); } ``` #### Linked ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeButton, OgeButtonGroup } from '@oge-ui/buttons'; import { OgeNumberBox, OgeTextBox } from '@oge-ui/inputs'; import type { OgeInputValueCommittedEvent } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeButton, OgeButtonGroup, OgeNumberBox, OgeTextBox], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly invoiceType = signal(['person']); protected readonly minValue = signal(null); // rich change payload: { value, previousValue, event } protected onMaxChanged(e: OgeInputValueCommittedEvent): void { console.log(e.previousValue, '→', e.value, e.event ? 'user' : 'programmatic'); } } ``` #### Pending ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeTextBox } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeTextBox], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly apiKey = signal(''); protected readonly checking = signal(false); private checkTimer: ReturnType | undefined; // `pending` shows a rail spinner; pair it with async validation protected simulateCheck(): void { this.checking.set(true); clearTimeout(this.checkTimer); this.checkTimer = setTimeout(() => this.checking.set(false), 900); } } ``` #### Reactive ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { ReactiveFormsModule, FormControl, Validators } from '@angular/forms'; import { OgeTextBox } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [ReactiveFormsModule, OgeTextBox], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { // classic reactive forms — the CVA bridge renders control errors protected readonly email = new FormControl('', { nonNullable: true, validators: [Validators.required, Validators.email], }); } ``` #### Signal ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { FormField, form, minLength, required } from '@angular/forms/signals'; import { OgeNumberBox, OgeTextBox } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [FormField, OgeNumberBox, OgeTextBox], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { // Angular Signal Forms — schema constraints auto-bind protected readonly model = signal({ username: '', age: null as number | null }); protected readonly f = form(this.model, (p) => { required(p.username); minLength(p.username, 3); }); } ``` #### Standalone ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeTextBox } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeTextBox], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly username = signal(''); } ``` ## @oge-ui/buttons Buttons with async actions and automatic loading, click guards, hold-to-confirm, auto-repeat, badges, button groups and drop-down/split buttons. Docs: https://ogeui.com/components/buttons ### Entry points `@oge-ui/buttons` - values: `OGE_BUTTONS_CONFIG`, `OGE_BUTTON_GROUP`, `OGE_DEFAULT_BUTTONS_CONFIG`, `OGE_DEFAULT_BUTTONS_MESSAGES`, `OgeButton`, `OgeButtonGroup`, `OgeButtonIcon`, `OgeDropDownButton`, `OgeDropDownContent`, `provideOgeButtonsConfig` - types: `OgeAutoRepeatOptions`, `OgeButtonActionDoneEvent`, `OgeButtonActionFailedEvent`, `OgeButtonClickEvent`, `OgeButtonGroupContext`, `OgeButtonGroupItem`, `OgeButtonGroupItemClickEvent`, `OgeButtonGroupSelectionChangedEvent`, `OgeButtonGroupSelectionMode`, `OgeButtonIconPosition`, `OgeButtonSeverity`, `OgeButtonSize`, `OgeButtonStylingMode`, `OgeButtonsConfig`, `OgeButtonsConfigInput`, `OgeButtonsMessages`, `OgeClickGuardOptions`, `OgeDropDownButtonItemClickEvent`, `OgeDropDownContentContext`, `OgeDropDownItemsFn`, `OgeDropDownSelectionChangedEvent`, `OgeHoldToConfirmOptions` ### OgeButton — `` #### Properties _Appearance_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `text` | `string` | `''` | Label text; alternative (or addition) to projecting content. | | `hint` | `string \| undefined` | `—` | Tooltip — rendered as the native `title` attribute. | | `stylingMode` | `OgeButtonStylingMode \| undefined` | `—` | Fill style; falls back to the enclosing group, then `contained`. | | `severity` | `OgeButtonSeverity \| undefined` | `—` | Semantic color; falls back to the enclosing group, then `normal`. | | `size` | `OgeButtonSize \| undefined` | `—` | Size preset; falls back to the enclosing group, then `md`. | | `color` | `string \| undefined` | `—` | Custom main color (any CSS color) — overrides the severity palette; the soft tint is derived via `color-mix`. | | `iconPosition` | `OgeButtonIconPosition` | `'before'` | Where `[ogeButtonIcon]` content renders relative to the label. | | `badge` | `string \| number \| boolean \| undefined` | `—` | String/number renders a pill (numbers cap at `99+` and join the accessible name); `true` renders a plain dot. | _Behavior_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `disabled` | `boolean` | `false` | Disables the button. | | `loading` | `model` | `false` | Busy state — two-way; managed automatically while `action` is pending. | | `action` | `(() => unknown) \| undefined` | `—` | Async click handler: sets `loading` while pending, single-flight; sync returns emit `actionDone` immediately. | | `clickGuard` | `boolean \| OgeClickGuardOptions` | `false` | Rate-limits the `clicked` output; `true` throttles with `config.clickGuardMs`. | | `holdToConfirm` | `boolean \| OgeHoldToConfirmOptions` | `false` | Fires `clicked` only after an uninterrupted press; wins over `autoRepeat`. `true` uses `config.holdToConfirmMs`. | | `autoRepeat` | `boolean \| OgeAutoRepeatOptions` | `false` | Repeats `clicked` while held; ignored when `holdToConfirm` is set. | | `useSubmitBehavior` | `boolean` | `false` | Renders `type="submit"` so the button submits the enclosing form. | | `buttonType` | `'button' \| 'submit' \| 'reset'` | `'button'` | Native button type; `useSubmitBehavior` is sugar for `submit`. | | `value` | `string \| undefined` | `—` | Selection key inside an ``; unused standalone. | | `tabIndex` | `number` | `0` | Tab order of the native button. | | `accessKey` | `string \| undefined` | `—` | Native `accesskey` of the inner button. | | `messages` | `Partial \| undefined` | `—` | Per-instance overrides of user-facing strings. | _Accessibility_ | Name | Type | Description | | --- | --- | --- | | `ariaLabel` | `string \| undefined` | Accessible name of the native button — required for icon-only buttons. | | `ariaHasPopup` | `string \| undefined` | `aria-haspopup` of the native button — for popup triggers. | | `ariaExpanded` | `boolean \| undefined` | `aria-expanded`; `undefined` omits the attribute. | | `ariaControls` | `string \| undefined` | `aria-controls` — id of the controlled popup. | #### Methods | Name | Type | Description | | --- | --- | --- | | `focus(): void` | `void` | Moves keyboard focus to the inner native button (`preventScroll: true`). | | `isDisabled` | `Signal` | Read-only computed: disabled, busy, or inside a disabled group. | #### Events | Name | Type | Description | | --- | --- | --- | | `clicked` | `OgeButtonClickEvent` | Fires after the gesture/guard pipeline accepts a click. Bind this instead of native `(click)`, which bypasses every guard. | | `actionDone` | `OgeButtonActionDoneEvent` | The `action` callback settled successfully. | | `actionFailed` | `OgeButtonActionFailedEvent` | The `action` callback threw or rejected. | | `loadingChange` | `boolean` | Implicit output of the `loading` model. | #### Types | Name | Type | Description | | --- | --- | --- | | `OgeButtonStylingMode` | `'contained' \| 'outlined' \| 'text'` | Fill style. | | `OgeButtonSeverity` | `'normal' \| 'accent' \| 'success' \| 'warning' \| 'danger'` | Semantic color mapped to the severity tokens. | | `OgeButtonSize` | `'sm' \| 'md' \| 'lg'` | Height/padding preset. | | `OgeButtonIconPosition` | `'before' \| 'after'` | Icon slot placement. | | `OgeButtonClickEvent` | `{ event: MouseEvent \| KeyboardEvent }` | `KeyboardEvent` when produced by Space/Enter during hold/repeat. | | `OgeButtonActionDoneEvent` | `{ result: unknown }` | Resolved value (or sync return) of `action`. | | `OgeButtonActionFailedEvent` | `{ error: unknown }` | Rejection reason or thrown error of `action`. | | `OgeClickGuardOptions` | `{ mode: 'debounce' \| 'throttle'; ms?: number }` | `ms` defaults to `config.clickGuardMs`; `true` shorthand ≡ throttle. | | `OgeHoldToConfirmOptions` | `{ ms?: number }` | Hold duration; defaults to `config.holdToConfirmMs`. | | `OgeAutoRepeatOptions` | `{ delayMs?: number; intervalMs?: number }` | Defaults from `config.autoRepeatDelayMs` / `autoRepeatIntervalMs`. | | `OgeButtonIcon` | `directive — [ogeButtonIcon]` | Marks projected content as the icon slot; placement follows `iconPosition`. | ### OgeButtonGroup — `` #### Properties | Name | Type | Default | Description | | --- | --- | --- | --- | | `items` | `readonly OgeButtonGroupItem[] \| undefined` | `—` | Data-driven items rendered after projected `` children. | | `selectionMode` | `OgeButtonGroupSelectionMode` | `'none'` | Selection behavior; also drives the ARIA role (toolbar / radiogroup / group). | | `selectedKeys` | `model` | `[]` | Selected `value`s — two-way; `single` keeps at most one entry. | | `stylingMode` | `OgeButtonStylingMode` | `'contained'` | Cascaded to children without their own. | | `severity` | `OgeButtonSeverity` | `'normal'` | Cascaded to children without their own. | | `size` | `OgeButtonSize` | `'md'` | Cascaded to children without their own. | | `disabled` | `boolean` | `false` | Disables every button in the group. | | `ariaLabel` | `string \| undefined` | `—` | Accessible name of the toolbar/radiogroup/group element. | #### Methods | Name | Type | Description | | --- | --- | --- | | `focus(): void` | `void` | Moves keyboard focus to the roving-tabindex target button. | | `isSelected(value: string \| undefined): boolean` | `boolean` | Whether the given button `value` is currently selected (reactive). | #### Events | Name | Type | Description | | --- | --- | --- | | `itemClick` | `OgeButtonGroupItemClickEvent` | Every accepted child click, before any selection change. | | `selectionChanged` | `OgeButtonGroupSelectionChangedEvent` | `selectedKeys` changed through user interaction. | | `selectedKeysChange` | `readonly string[]` | Implicit output of the `selectedKeys` model. | #### Types | Name | Type | Description | | --- | --- | --- | | `OgeButtonGroupSelectionMode` | `'none' \| 'single' \| 'multiple'` | Selection behavior of the group. | | `OgeButtonGroupItem` | `{ value: string; text?; hint?; disabled?; severity?; badge? }` | Data-driven item; icons require declarative children. | | `OgeButtonGroupItemClickEvent` | `{ value: string \| undefined; event: MouseEvent \| KeyboardEvent; item?: OgeButtonGroupItem; index: number }` | `index` is DOM-order; `-1` when unresolvable. | | `OgeButtonGroupSelectionChangedEvent` | `{ selectedKeys: readonly string[]; addedKeys: readonly string[]; removedKeys: readonly string[] }` | Full state plus diffs. | ### OgeDropDownButton — `` #### Properties _Trigger button_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `text` | `string` | `''` | Label of the (main) trigger button. | | `hint` | `string \| undefined` | `—` | Tooltip of the trigger. | | `disabled` | `boolean` | `false` | Disables the whole control. | | `stylingMode` | `OgeButtonStylingMode \| undefined` | `—` | Same fallback chain as `OgeButton`. | | `severity` | `OgeButtonSeverity \| undefined` | `—` | Same fallback chain as `OgeButton`. | | `size` | `OgeButtonSize \| undefined` | `—` | Same fallback chain as `OgeButton`. | | `color` | `string \| undefined` | `—` | Custom main color — overrides the severity palette. | | `iconPosition` | `OgeButtonIconPosition` | `'before'` | Icon slot placement on the trigger. | | `badge` | `string \| number \| boolean \| undefined` | `—` | Badge on the trigger, as on `OgeButton`. | | `splitButton` | `boolean` | `false` | `true` renders a separate chevron toggle next to an action main button. | | `action` | `(() => unknown) \| undefined` | `—` | Async click handler of the split main button (single-flight, drives `loading`). | | `clickGuard` | `boolean \| OgeClickGuardOptions` | `false` | Click guard of the split main button. | | `loading` | `model` | `false` | Busy state of the (main) button — two-way. | | `messages` | `Partial \| undefined` | `—` | Per-instance overrides of user-facing strings (status rows, toggle label). | _Drop-down panel_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `items` | `readonly OgeMenuItem[] \| OgeDropDownItemsFn \| undefined` | `—` | Menu items — an array, or a function invoked lazily on first open (result cached until the function reference changes). | | `opened` | `model` | `false` | Panel visibility — two-way. | | `dropdownPlacement` | `OgePopupPlacement` | `'bottom-start'` | Preferred panel placement (flips/clamps automatically). | | `dropdownWidth` | `number \| 'anchor' \| undefined` | `—` | Fixed pixels or `'anchor'` to match the button width. | | `rememberLastAction` | `boolean` | `false` | Split mode: the last clicked item becomes the main button's label + action. | | `itemTemplate` | `TemplateRef \| undefined` | `—` | Custom rendering for menu items — see `OgeMenuList`. | #### Methods | Name | Type | Description | | --- | --- | --- | | `open(): void` | `void` | Opens the panel programmatically. | | `close(): void` | `void` | Closes the panel programmatically. | | `toggle(): void` | `void` | Toggles `opened`. | | `focus(): void` | `void` | Focuses the trigger (split mode: the chevron toggle). | | `panel` | `OgeAnchoredPanel` | The anchored-panel model — public so templates/tests can read `panelId`. | #### Events | Name | Type | Description | | --- | --- | --- | | `itemClick` | `OgeDropDownButtonItemClickEvent` | Menu item activated; the panel closes afterwards. | | `selectionChanged` | `OgeDropDownSelectionChangedEvent` | `rememberLastAction` mode: the remembered item changed. | | `clicked` | `OgeButtonClickEvent` | Split mode only: the main action button was clicked. In non-split mode the trigger only toggles the panel. | | `actionDone` | `OgeButtonActionDoneEvent` | The split-button `action` settled successfully. | | `actionFailed` | `OgeButtonActionFailedEvent` | The split-button `action` threw or rejected. | | `openedChange` | `boolean` | Implicit output of the `opened` model — fires on both open and close. | | `loadingChange` | `boolean` | Implicit output of the `loading` model. | #### Types | Name | Type | Description | | --- | --- | --- | | `OgeDropDownItemsFn` | `() => readonly OgeMenuItem[] \| Promise` | Lazy items factory — invoked on first open. | | `OgeDropDownButtonItemClickEvent` | `{ item: OgeMenuItem; index: number; event: MouseEvent \| KeyboardEvent }` | Index within the resolved items list (separators included). | | `OgeDropDownSelectionChangedEvent` | `{ item: OgeMenuItem; previousItem: OgeMenuItem \| null }` | Remembered-item change in `rememberLastAction` mode. | | `OgeDropDownContent` | `directive — [ogeDropDownContent]` | Replaces the item menu with arbitrary panel content; context `{ $implicit: () => void }` closes the panel and restores focus. | ### Buttons configuration #### Methods | Name | Type | Description | | --- | --- | --- | | `provideOgeButtonsConfig(config: OgeButtonsConfigInput): Provider` | `Provider` | Application- or component-scoped defaults; deep-merges `messages` over the defaults. | #### Types _OgeButtonsConfig_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `clickGuardMs` | `number` | `500` | Default window for `clickGuard: true` and guard options without `ms`. | | `holdToConfirmMs` | `number` | `800` | Default hold duration for `holdToConfirm: true`. | | `autoRepeatDelayMs` | `number` | `400` | Delay before `autoRepeat` starts repeating. | | `autoRepeatIntervalMs` | `number` | `80` | Interval between repeated clicks. | | `messages` | `OgeButtonsMessages` | `—` | User-facing strings (see below). | _OgeButtonsMessages_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `loading` | `string` | `'Loading'` | Screen-reader text announced while a button is busy. | | `holdToConfirm` | `string` | `'Hold to confirm'` | Tooltip fragment when `holdToConfirm` is enabled. | | `dropDownLoading` | `string` | `'Loading…'` | Status row while a drop-down loads async items. | | `dropDownNoItems` | `string` | `'No items'` | Status row when a drop-down has no items. | | `dropDownLoadError` | `string` | `'Could not load items'` | Status row when async items failed to load. | | `dropDownToggle` | `string` | `'Open menu'` | Aria label of the split drop-down's chevron toggle. | ### Demos Each demo below is a complete standalone component — copy one whole and it compiles. #### Items ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeButtonGroup } from '@oge-ui/buttons'; import type { OgeButtonGroupItem } from '@oge-ui/buttons'; @Component({ selector: 'demo-root', imports: [OgeButtonGroup], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly periods: OgeButtonGroupItem[] = [ { value: 'day', text: 'Day' }, { value: 'week', text: 'Week' }, { value: 'month', text: 'Month' }, { value: 'year', text: 'Year', disabled: true }, ]; protected readonly period = signal(['week']); } ``` #### Multi ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeButton, OgeButtonGroup } from '@oge-ui/buttons'; @Component({ selector: 'demo-root', imports: [OgeButton, OgeButtonGroup], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly styles = signal(['bold']); } ``` #### Single ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeButton, OgeButtonGroup } from '@oge-ui/buttons'; @Component({ selector: 'demo-root', imports: [OgeButton, OgeButtonGroup], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly align = signal(['center']); } ``` #### Async ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeDropDownButton } from '@oge-ui/buttons'; import type { OgeMenuItem } from '@oge-ui/overlay'; @Component({ selector: 'demo-root', imports: [OgeDropDownButton], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { // invoked on first open, cached until the reference changes protected readonly loadBranches = (): Promise => fetch('/api/branches').then((response) => response.json()); } ``` #### Basic ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeDropDownButton } from '@oge-ui/buttons'; import type { OgeMenuItem } from '@oge-ui/overlay'; @Component({ selector: 'demo-root', imports: [OgeDropDownButton], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly exportItems: OgeMenuItem[] = [ { text: 'Excel (.xlsx)', value: 'xlsx' }, { text: 'CSV', value: 'csv' }, { separator: true, text: '' }, { text: 'PDF', value: 'pdf' }, ]; protected exportAs(format: unknown): void { console.log('export as', format); } } ``` #### Content ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeButton, OgeDropDownButton, OgeDropDownContent } from '@oge-ui/buttons'; @Component({ selector: 'demo-root', imports: [OgeButton, OgeDropDownButton, OgeDropDownContent], changeDetection: ChangeDetectionStrategy.OnPush, template: `
…any content…
`, }) export class Demo { protected apply(): void { console.log('apply the filters'); } } ``` #### Split ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeDropDownButton } from '@oge-ui/buttons'; import type { OgeMenuItem } from '@oge-ui/overlay'; @Component({ selector: 'demo-root', imports: [OgeDropDownButton], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly runTargets: OgeMenuItem[] = [ { text: 'Run tests', value: 'test' }, { text: 'Run build', value: 'build' }, { text: 'Run lint', value: 'lint' }, ]; protected runCurrent(): void { console.log('run the remembered target'); } protected run(target: unknown): void { console.log('run', target); } } ``` #### Action ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeButton } from '@oge-ui/buttons'; @Component({ selector: 'demo-root', imports: [OgeButton], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly save = () => new Promise((resolve) => setTimeout(resolve, 1500)); protected log(message: string): void { console.log(message); } } ``` #### Guard ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeButton } from '@oge-ui/buttons'; @Component({ selector: 'demo-root', imports: [OgeButton], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly clicks = signal(0); protected count(): void { this.clicks.update((value) => value + 1); } } ``` #### Hold ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeButton } from '@oge-ui/buttons'; @Component({ selector: 'demo-root', imports: [OgeButton], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected destroyEverything(): void { console.warn('boom'); } } ``` #### Repeat ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeButton } from '@oge-ui/buttons'; @Component({ selector: 'demo-root', imports: [OgeButton], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected value = 0; } ``` #### Badge ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeButton } from '@oge-ui/buttons'; @Component({ selector: 'demo-root', imports: [OgeButton], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo {} ``` #### Color ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeButton } from '@oge-ui/buttons'; @Component({ selector: 'demo-root', imports: [OgeButton], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo {} ``` #### Icon ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeButton, OgeButtonIcon } from '@oge-ui/buttons'; @Component({ selector: 'demo-root', imports: [OgeButton, OgeButtonIcon], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo {} ``` #### Sizes ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeButton } from '@oge-ui/buttons'; @Component({ selector: 'demo-root', imports: [OgeButton], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo {} ``` #### Variants ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeButton } from '@oge-ui/buttons'; @Component({ selector: 'demo-root', imports: [OgeButton], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo {} ``` ## @oge-ui/overlay Popup foundation and surfaces: flip-aware anchored placement, WAI-ARIA menus, tooltips, context menus, the `oge-modal` dialog with `OgeModalService`, and `OgeToastService` notifications. Docs: https://ogeui.com/components/overlay ### Entry points `@oge-ui/overlay` - values: `OGE_DEFAULT_OVERLAY_CONFIG`, `OGE_DEFAULT_OVERLAY_MESSAGES`, `OGE_MODAL_DATA`, `OGE_OVERLAY_CONFIG`, `OgeAnchoredPanel`, `OgeContextMenu`, `OgeMenuList`, `OgeModal`, `OgeModalFooter`, `OgeModalHeaderActions`, `OgeModalRef`, `OgeModalService`, `OgeModalTitle`, `OgePopup`, `OgeToastRef`, `OgeToastService`, `OgeTooltip`, `getTabbableElements`, `isTopOverlay`, `lockBodyScroll`, `provideOgeOverlayConfig`, `pushOverlay`, `removeOverlay`, `resolvePopupPosition`, `trapTabKey`, `unlockBodyScroll` - types: `OgeAnchoredPanelOptions`, `OgeMenuCloseRequestEvent`, `OgeMenuItem`, `OgeMenuItemSeverity`, `OgeMenuItemTemplateContext`, `OgeMenuListItemClickEvent`, `OgeModalAutoFocus`, `OgeModalCloseReason`, `OgeModalClosedEvent`, `OgeModalClosingEvent`, `OgeModalOpenConfig`, `OgeModalOpeningEvent`, `OgeModalPlacement`, `OgeModalResizeEvent`, `OgeModalSlotContext`, `OgeOverlayConfig`, `OgeOverlayConfigInput`, `OgeOverlayMessages`, `OgePopupAlign`, `OgePopupCloseReason`, `OgePopupPlacement`, `OgePopupPositionRequest`, `OgePopupSide`, `OgeRect`, `OgeResolvedPopupPosition`, `OgeToastAction`, `OgeToastActionEvent`, `OgeToastAnnounce`, `OgeToastCloseReason`, `OgeToastClosedEvent`, `OgeToastOptions`, `OgeToastPosition`, `OgeToastPromiseOptions`, `OgeToastSeverity`, `OgeToastSlotContext`, `OgeToastUpdate` ### OgeModal — `` #### Properties _Content slots (structural directives)_ | Name | Type | Description | | --- | --- | --- | | `*ogeModalTitle` | `OgeModalTitle` | Rich title slot, replacing the plain `title` text. | | `*ogeModalHeaderActions` | `OgeModalHeaderActions` | Extra buttons rendered next to ✕. Presses that start here never begin a header drag. | | `*ogeModalFooter` | `OgeModalFooter` | Footer slot; `$implicit` is a close function whose optional argument becomes `closed.result` — ``. | | Name | Type | Default | Description | | --- | --- | --- | --- | | `opened` | `model` | `false` | Two-way open state. Setting it `false` directly closes without the guard pipeline. | | `fullScreen` | `model` | `false` | Two-way full-screen state; size inputs are ignored while `true`. Driven by the maximize button when shown. | | `title` | `string \| undefined` | `—` | Header text; also the aria-label fallback when the header is hidden. | | `ariaLabel` | `string \| undefined` | `—` | Accessible name override for headerless modals. | | `width / height / minWidth / minHeight / maxWidth / maxHeight` | `number \| string \| undefined` | `—` | Panel size — numbers are px, strings pass through. Default width `min(560px, 100%)`. | | `placement` | `OgeModalPlacement` | `'center'` | Viewport position: centered or pinned near the top edge (`'top'`, command-palette style). | | `shading` | `boolean` | `true` | Dims the page behind the modal. `false` keeps the backdrop transparent while staying fully modal. | | `showCloseButton` | `boolean` | `true` | Shows the header ✕ button. | | `showMaximizeButton` | `boolean` | `false` | Shows a maximize/restore toggle in the header, driving `fullScreen`. | | `dragEnabled` | `boolean` | `false` | Lets the user drag the panel by its header (viewport-clamped unless `dragOutsideBoundary`). | | `dragOutsideBoundary` | `boolean` | `false` | Allows dragging the panel beyond the viewport edges. | | `restorePosition` | `boolean` | `true` | Resets drag offset and resized size on every reopen. | | `resizeEnabled` | `boolean` | `false` | Shows a bottom-end resize handle (min 160×120, viewport-capped). | | `inertBackground` | `boolean` | `false` | Marks everything outside the modal `inert` while open — opt-in; content appended to `body` after opening is not covered. | | `closeOnEscape` | `boolean` | `true` | Escape closes the modal when it is the topmost overlay (popups inside close first). | | `closeOnBackdropClick` | `boolean` | `true` | A click that starts _and_ ends on the backdrop closes the modal — a text-selection drag released outside never does. | | `scrollLock` | `boolean` | `true` | Locks body scroll while open (scrollbar-width compensated, ref-counted across stacked modals). | | `autoFocus` | `OgeModalAutoFocus` | `'first-tabbable'` | Initial focus target; an `[autofocus]` element inside the panel always wins. | | `restoreFocus` | `boolean` | `true` | Restores focus to the opener on close — only when focus would otherwise be lost. | | `padding` | `boolean` | `true` | `false` makes the body flush for grids and custom layouts. | | `busy` | `boolean` | `false` | Spinner veil + `aria-busy`; user-initiated closes are blocked, programmatic `close()` still works. | | `closeGuard` | `() => boolean \| Promise \| undefined` | `—` | Veto hook run before every pipeline close; may be async (single-flight — see `closePending`). A rejected promise vetoes with a dev warning. | | `messages` | `Partial \| undefined` | `—` | Per-instance message overrides. | | `closePending` | `Signal` | `—` | `true` while an async `closeGuard` is pending — disable footer actions with it. | #### Methods | Name | Type | Description | | --- | --- | --- | | `open(): void` | `void` | Opens the modal. | | `close(result?: R): void` | `void` | Closes through the full pipeline (`closing` → `closeGuard`); reason `'api'`, the argument becomes `closed.result`. | | `toggle(): void` | `void` | Open ⇄ close. | | `focus(): void` | `void` | Re-applies the initial-focus resolution. No-op while closed. | | `toggleFullScreen(): void` | `void` | Switches between windowed and full-screen (the maximize button’s action). | #### Events | Name | Type | Description | | --- | --- | --- | | `opening` | `OgeModalOpeningEvent` | Cancelable: fires before the modal opens (any open path). Set `cancel = true` to keep it closed. | | `closing` | `OgeModalClosingEvent` | Cancelable: fires before any pipeline close (Escape, backdrop, ✕, `close()`). Set `cancel = true` to keep the modal open. | | `closed` | `OgeModalClosedEvent` | Fires after the modal closed, with the reason and the optional result. | | `resizeStarted / resized` | `OgeModalResizeEvent` | Fire when a resize gesture starts (starting size) and ends (final size). | #### Types | Name | Type | Description | | --- | --- | --- | | `OgeModalCloseReason` | `'api' \| 'escape' \| 'backdrop' \| 'closeButton'` | Why the modal closed. | | `OgeModalClosingEvent` | `{ reason: OgeModalCloseReason; cancel: boolean }` | Cancelable pre-close event. | | `OgeModalClosedEvent` | `{ reason: OgeModalCloseReason; result?: R }` | Post-close event; `result` comes from `close(result)` or the slot close function. | | `OgeModalAutoFocus` | `'first-tabbable' \| 'panel' \| string` | Initial-focus strategy — a plain string is treated as a CSS selector inside the panel. | | `OgeModalPlacement` | `'center' \| 'top'` | Where the panel sits in the viewport. | | `OgeModalOpeningEvent` | `{ cancel: boolean }` | Cancelable pre-open event. | | `OgeModalResizeEvent` | `{ width: number; height: number; event: PointerEvent }` | Payload of the resize outputs. | | `OgeModalSlotContext` | `{ $implicit: (result?: unknown) => void }` | Context of `*ogeModalTitle` / `*ogeModalHeaderActions` / `*ogeModalFooter`; the function closes the modal. | | `*ogeModalHeaderActions` | `structural slot` | Custom title-bar buttons, rendered between the title and the maximize/✕ buttons; presses here never start a header drag. | ### OgeModalService #### Methods | Name | Type | Description | | --- | --- | --- | | `open(content: Type \| TemplateRef, config?: OgeModalOpenConfig): OgeModalRef` | `OgeModalRef` | Opens a body-appended modal hosting the component or template — the escape hatch for `transform`ed ancestors and for prompt/confirm flows without a declared ``. | #### Types | Name | Type | Description | | --- | --- | --- | | `OgeModalOpenConfig` | `object` | The declarative inputs minus slots (`title`, sizing, `placement`, `closeGuard`, …) plus `data?: D`, made available to the content via `OGE_MODAL_DATA`. | | `OgeModalRef` | `{ close(result?: R): void; closed: Promise> }` | Handle of a service-opened modal; content components can inject it to close themselves with a result. | | `OGE_MODAL_DATA` | `InjectionToken` | The `config.data` payload, injectable in the content component. | ### OgeToastService #### Properties _OgeToastOptions_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `message` | `string (required)` | `—` | Body text; also the screen-reader announcement. | | `title` | `string \| undefined` | `—` | Optional bold first line above the message. | | `severity` | `OgeToastSeverity` | `'info'` | Drives the accent bar, icon and announcement mode. | | `displayTime` | `number` | `config toastDisplayTime (4000)` | Auto-dismiss time in ms. | | `sticky` | `boolean` | `false` | Never auto-dismisses (`loading` toasts are implicitly sticky). | | `closable` | `boolean` | `true` | Shows the ✕ button; aria label from `messages.toastClose`. | | `closeOnClick` | `boolean` | `false` | A click anywhere on the toast closes it (reason `'click'`); button presses excluded. | | `progressBar` | `boolean` | `config toastProgressBar (false)` | Remaining-time bar — freezes exactly in sync with the paused timer. | | `action` | `OgeToastAction \| undefined` | `—` | Inline action button; pressing it runs `handler` and closes with reason `'action'`. | | `position` | `OgeToastPosition` | `config toastPosition ('bottom-end')` | Region override for this toast. | | `announce` | `OgeToastAnnounce` | `severity-derived` | `'assertive'` for errors, `'polite'` otherwise; `'off'` silences. | | `announceText` | `string \| undefined` | `—` | Screen-reader text override — announced instead of `title` + `message`, so the visual text can stay short. | | `icon` | `TemplateRef \| undefined` | `—` | Replaces the severity icon (the `loading` spinner still wins). | | `loading` | `boolean` | `false` | Spinner instead of the severity icon; implicitly sticky while `true`. | | `coalesce` | `boolean` | `config toastCoalesceDuplicates (false)` | Merge with an identical visible toast into one with a live ×N badge (timer restarts, same ref returned). | | `id` | `string \| undefined` | `—` | Coalesce key override; defaults to severity+title+message. | | `cssClass` | `string \| undefined` | `—` | Extra class(es) on the toast element. | | `template` | `TemplateRef \| undefined` | `—` | Replaces the title/message body; `$implicit` closes the toast, `data` is in context. | | `data` | `D \| undefined` | `—` | Arbitrary payload surfaced in the template context and action event. | #### Methods _OgeToastService_ | Name | Type | Description | | --- | --- | --- | | `show(toast: string \| OgeToastOptions): OgeToastRef` | `OgeToastRef` | Shows a toast; a bare string becomes an info toast. SSR-safe no-op. | | `success / info / warning / error(message, options?): OgeToastRef` | `OgeToastRef` | Severity sugar for `show()`. | | `promise(promise, options): OgeToastRef` | `OgeToastRef` | Sticky spinner toast that morphs in place when the promise settles; the timer starts then. `success`/`error` accept a message or a function returning a message or an update patch. | | `clear(position?: OgeToastPosition): void` | `void` | Closes every toast (or one region) with reason `'clear'`. | _OgeToastRef_ | Name | Type | Description | | --- | --- | --- | | `close(): void` | `void` | Closes the toast (reason `'api'`). | | `update(patch: OgeToastUpdate): void` | `void` | Patches the toast in place; timing changes restart the timer, a changed message re-announces. | | `closed` | `Promise` | Resolves after the toast closed (exit transition included), with the typed reason. | #### Types | Name | Type | Description | | --- | --- | --- | | `OgeToastSeverity` | `'info' \| 'success' \| 'warning' \| 'error'` | Toast severity. | | `OgeToastPosition` | `'top-start' \| 'top-center' \| 'top-end' \| 'bottom-start' \| 'bottom-center' \| 'bottom-end'` | Logical, RTL-aware region positions. | | `OgeToastCloseReason` | `'timeout' \| 'closeButton' \| 'click' \| 'action' \| 'api' \| 'clear'` | Why a toast closed. | | `OgeToastAction` | `{ text: string; handler?: (event: OgeToastActionEvent) => void }` | Inline action button. | | `OgeToastSlotContext` | `{ $implicit: () => void; data?: D }` | Context of a `template` toast body. | | `Config keys` | `toastPosition · toastDisplayTime · toastMaxVisible · toastProgressBar · toastCoalesceDuplicates` | Defaults via `provideOgeOverlayConfig()`; strings via `messages.toastClose/toastRegionLabel/toastCountBadge`. | ### OgeTooltip — `<[ogeTooltip]>` #### Properties | Name | Type | Default | Description | | --- | --- | --- | --- | | `ogeTooltip` | `string (required)` | `—` | Tooltip text. Applied to any element — the host gets `aria-describedby` pointing at the panel while it is shown. | | `tooltipPlacement` | `OgePopupPlacement` | `'top'` | Preferred side; flips and clamps against the viewport like every anchored panel. | | `tooltipShowDelay` | `number \| undefined` | `—` | Hover dwell before showing, in ms. Falls back to `tooltipShowDelayMs` from the overlay config. Keyboard focus always shows immediately. | | `tooltipHideDelay` | `number \| undefined` | `—` | Grace period after the pointer leaves, in ms; falls back to `tooltipHideDelayMs`. | | `tooltipDisabled` | `boolean` | `false` | Suppresses the tooltip without removing the directive — hides an already open panel. | ### OgeContextMenu — `<[ogeContextMenu]>` #### Properties | Name | Type | Default | Description | | --- | --- | --- | --- | | `ogeContextMenu` | `readonly OgeMenuItem[] (required)` | `—` | Items of the menu opened on right-click or Shift+F10. An empty array falls back to the native browser menu. | | `contextMenuAriaLabel` | `string \| undefined` | `—` | Accessible name of the menu. | | `contextMenuDisabled` | `boolean` | `false` | Leaves the browser menu in charge without removing the directive. | #### Events | Name | Type | Description | | --- | --- | --- | | `contextMenuItemClick` | `OgeMenuListItemClickEvent` | An item was activated — same payload as `OgeMenuList`. The menu closes afterwards and focus returns to the host. | | `contextMenuOpened` | `void` | The menu opened at the pointer (or at the host for Shift+F10). | | `contextMenuClosed` | `void` | The menu closed — by selection, Escape, an outside click or a scroll. | ### OgeMenuList — `` #### Properties | Name | Type | Description | | --- | --- | --- | | `items` | `readonly OgeMenuItem[] (required)` | Menu items, separators included. | | `menuId` | `string \| undefined` | Id of the `role="menu"` element; generated (`oge-menu-N`) when omitted. | | `ariaLabel` | `string \| undefined` | Accessible name of the menu. | | `itemTemplate` | `TemplateRef \| undefined` | Replaces the default check+text item rendering (icons, badges…). | #### Methods | Name | Type | Description | | --- | --- | --- | | `focus(position: 'first' \| 'last' = 'first'): void` | `void` | Focuses the menu container and activates the first/last enabled item. | #### Events | Name | Type | Description | | --- | --- | --- | | `itemClick` | `OgeMenuListItemClickEvent` | An enabled item was activated (click, Enter or Space). Order: `itemClick` → `item.action?.()` → `closeRequest`. | | `closeRequest` | `OgeMenuCloseRequestEvent` | The menu asks its owner to close it; the owner handles focus. Tab does not `preventDefault`, so the browser keeps tabbing from the owner. | #### Types | Name | Type | Description | | --- | --- | --- | | `OgeMenuItem` | `{ text: string; value?: T; hint?; disabled?; checked?; icon?; iconClass?; severity?; separator?; action?: () => void; url?; badge?; shortcut?; items?: readonly OgeMenuItem[] }` | Canonical menu item of the suite. A defined `checked` renders `menuitemcheckbox`; `separator: true` ignores every other field. `icon` takes SVG path data and `iconClass` hooks an icon font — one row with either gives every row an icon column, so labels stay aligned. `url` renders the row as a real `` with `role="menuitem"` — keyboard activation clicks the link, so `preventDefault()` in `itemClick` hands navigation to a router exactly like a pointer click. `badge` renders a trailing counter pill; `shortcut` renders a right-aligned accelerator hint and is announced via `aria-keyshortcuts` (display only — the application owns the binding). `items` makes the row a **submenu parent** (trailing chevron, `aria-haspopup="menu"`, `aria-expanded`): activation or ArrowRight opens a nested `oge-menu-list`, hover opens after a dwell, and `checked`/`action`/`url` are ignored on it. | | `OgeMenuItemSeverity` | `'normal' \| 'danger'` | Destructive items render with the danger token. | | `OgeMenuListItemClickEvent` | `{ item: OgeMenuItem; index: number; event: MouseEvent \| KeyboardEvent }` | Index within the `items` input (separators included). | | `OgeMenuCloseRequestEvent` | `{ reason: 'escape' \| 'tab' \| 'select' \| 'back'; event: KeyboardEvent \| MouseEvent }` | Why the menu wants to close. `'back'` is a nested submenu returning to its parent item — absorbed by the parent menu, it never reaches the root owner; `'select'` and `'tab'` chain up so the owner still receives exactly one request. | | `OgeMenuItemTemplateContext` | `{ $implicit: OgeMenuItem; index: number }` | Context of `itemTemplate`. | ### OgeAnchoredPanel #### Properties _Members_ | Name | Type | Description | | --- | --- | --- | | `panelId` | `string` | Unique id applied to the panel element (`oge-popup-N`) — wire to `aria-controls`. | | `isOpen` | `Signal` | Open state. | | `position` | `Signal` | `null` until the first measure after open; hide the panel while `null`. | _OgeAnchoredPanelOptions (constructor)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `anchor` | `() => HTMLElement \| null` | `—` | Anchor element getter (`null` while not rendered). Required. | | `panel` | `() => HTMLElement \| null` | `—` | Panel element getter (`null` while closed). Required. | | `placement` | `() => OgePopupPlacement` | `'bottom-start'` | Reactive getter — read signals inside so the next update sees changes. | | `width` | `() => number \| 'anchor' \| undefined` | `—` | Fixed pixel value or `'anchor'` to match the anchor width. | | `offset` | `() => number \| undefined` | `4` | Main-axis gap between anchor and panel. | | `viewportPadding` | `() => number \| undefined` | `8` | Minimum distance kept from viewport edges when clamping. | | `closeOnOutsidePointerDown` | `boolean` | `true` | Close on document pointerdown outside anchor+panel (capture phase, composedPath-aware). | | `closeOnEscape` | `boolean` | `true` | Close on Escape — stacked overlays only close the topmost. | | `restoreFocus` | `() => void` | `—` | Restores focus after closes caused by `escape`/`select` (only when focus would otherwise be orphaned). | | `onClosed` | `(reason: OgePopupCloseReason) => void` | `—` | Notified after every close with its reason. | #### Methods | Name | Type | Description | | --- | --- | --- | | `open(): void` | `void` | Opens (SSR-safe no-op without `window`); pushes onto the open-panel stack, adds listeners, measures. | | `close(reason: OgePopupCloseReason = 'api'): void` | `void` | Closes, removes listeners, restores focus for `escape`/`select`, then calls `onClosed(reason)`. | | `toggle(): void` | `void` | Open ⇄ close. | | `updatePosition(): void` | `void` | Re-measures anchor/panel and recomputes the position (rAF-coalesced). Also runs automatically on scroll/resize/panel growth. | | `destroy(): void` | `void` | Removes every listener and pending frame; call from the owner's `DestroyRef.onDestroy`. | #### Types | Name | Type | Description | | --- | --- | --- | | `OgePopupCloseReason` | `'api' \| 'outside' \| 'escape' \| 'select' \| 'tab'` | Why a panel closed. | ### OgePopup — `` #### Properties | Name | Type | Description | | --- | --- | --- | | `panel` | `OgeAnchoredPanel (required)` | The anchored-panel model driving id, position and visibility. | #### Types | Name | Type | Description | | --- | --- | --- | | `` | `component` | Presentational chrome: fixed positioning, popup surface tokens, `--oge-z-popup` stacking, transparent (via `opacity`, so the subtree stays focusable) until the first measure. Projects arbitrary content. | ### resolvePopupPosition #### Methods | Name | Type | Description | | --- | --- | --- | | `resolvePopupPosition(req: OgePopupPositionRequest): OgeResolvedPopupPosition` | `OgeResolvedPopupPosition` | Pure anchored-popup placement: preferred side with flip when the opposite side has more room, cross-axis alignment fallback, and a final clamp into the viewport. Coordinates are viewport-relative (`position: fixed`). | #### Types _OgePopupPositionRequest_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `anchor` | `OgeRect` | `—` | Anchor rectangle (viewport-relative). Required. | | `panel` | `{ width: number; height: number }` | `—` | Measured panel size. Required. | | `viewport` | `{ width: number; height: number }` | `—` | Viewport size. Required. | | `placement` | `OgePopupPlacement` | `—` | Preferred placement. Required. | | `offset` | `number` | `4` | Gap between anchor and panel on the main axis. | | `viewportPadding` | `number` | `8` | Minimum distance kept from viewport edges when clamping. | | `rtl` | `boolean` | `false` | Resolves logical `start`/`end` (and left/right sides) against RTL. | _OgeResolvedPopupPosition_ | Name | Type | Description | | --- | --- | --- | | `top / left` | `number` | Viewport-relative — apply with `position: fixed`. | | `placement` | `OgePopupPlacement` | Logical placement actually used after flipping. | | `width?` | `number` | Panel width when anchor-width matching or a fixed width was requested (set by `OgeAnchoredPanel`, not by the pure function). | _Supporting types_ | Name | Type | Description | | --- | --- | --- | | `OgePopupPlacement` | `'bottom-start' \| 'bottom-end' \| 'top-start' \| 'top-end' \| 'left-start' \| 'left-end' \| 'right-start' \| 'right-end'` | Side + cross-axis alignment. | | `OgePopupSide` | `'top' \| 'bottom' \| 'left' \| 'right'` | Main-axis side. | | `OgePopupAlign` | `'start' \| 'end'` | Cross-axis alignment. | | `OgeRect` | `{ top: number; left: number; width: number; height: number }` | Structurally compatible with `DOMRect`. | ### Overlay configuration #### Methods | Name | Type | Description | | --- | --- | --- | | `provideOgeOverlayConfig(config: OgeOverlayConfigInput): Provider` | `Provider` | Application- or component-scoped defaults. | #### Types _OgeOverlayConfig_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `offset` | `number` | `4` | Gap between anchor and panel on the main axis. | | `viewportPadding` | `number` | `8` | Minimum distance kept from viewport edges when clamping. | | `typeAheadMs` | `number` | `500` | Idle time after which the menu type-ahead buffer resets. | | `menuShowDelayMs` | `number` | `50` | Hover dwell time before a submenu parent row opens its submenu. | | `menuHideDelayMs` | `number` | `300` | Grace period before an open submenu closes after hovering a sibling row — the diagonal-pointer allowance. | | `messages` | `OgeOverlayMessages` | `—` | User-facing strings of the modal header buttons: `modalClose`, `modalMaximize`, `modalRestore`. | ### Overlay primitives #### Methods _Escape stack_ | Name | Type | Description | | --- | --- | --- | | `pushOverlay(surface: object): void` | `void` | Registers a surface as the new topmost overlay. No-op if it is already in the stack. | | `removeOverlay(surface: object): void` | `void` | Removes a surface from the stack; tolerates surfaces that were never pushed. | | `isTopOverlay(surface: object): boolean` | `boolean` | True only for the topmost surface. Gate your Escape handler on this and a popup opened inside a modal or a drawer closes before its host does. | _Focus trap_ | Name | Type | Description | | --- | --- | --- | | `getTabbableElements(root: HTMLElement): HTMLElement[]` | `HTMLElement[]` | Tabbable descendants in DOM order. Recomputed per call rather than cached behind sentinel elements, so content added or removed after open is always accounted for. | | `trapTabKey(event, root, fallback): void` | `void` | Wraps Tab and Shift+Tab inside `root`. With no tabbable descendants it focuses `fallback`, so focus can never escape a modal surface. | _Scroll lock_ | Name | Type | Description | | --- | --- | --- | | `lockBodyScroll(): void` | `void` | Locks body scroll and compensates for the scrollbar width. Ref-counted, so nested surfaces cannot unlock each other. | | `unlockBodyScroll(): void` | `void` | Releases one reference; the last release restores the inline styles exactly as they were. | ### Demos Each demo below is a complete standalone component — copy one whole and it compiles. #### Basic ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeButton } from '@oge-ui/buttons'; import { OgeModal, OgeModalFooter } from '@oge-ui/overlay'; @Component({ selector: 'demo-root', imports: [OgeButton, OgeModal, OgeModalFooter], changeDetection: ChangeDetectionStrategy.OnPush, template: `

Centered dialog with backdrop, focus trap and scroll lock.

`, }) export class Demo { protected readonly opened = signal(false); } ``` #### Busy ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeModal } from '@oge-ui/overlay'; @Component({ selector: 'demo-root', imports: [OgeModal], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly opened = signal(false); protected readonly saving = signal(true); } ``` #### Form ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeSelectBox } from '@oge-ui/inputs'; import { OgeModal } from '@oge-ui/overlay'; @Component({ selector: 'demo-root', imports: [OgeSelectBox, OgeModal], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly opened = signal(false); protected readonly statuses = ['Draft', 'In review', 'Published']; protected readonly status = signal('Draft'); } ``` #### Guard ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeModal } from '@oge-ui/overlay'; @Component({ selector: 'demo-root', imports: [OgeModal], changeDetection: ChangeDetectionStrategy.OnPush, template: `

Unsaved work lives here.

`, }) export class Demo { protected readonly opened = signal(false); protected readonly dirty = signal(true); // runs for every close reason (Escape, backdrop, ✕, close()); // may be async: the modal stays open until the promise resolves protected readonly confirmDiscard = (): boolean => !this.dirty() || confirm('Discard unsaved changes?'); } ``` #### Result ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeButton } from '@oge-ui/buttons'; import { OgeModal, OgeModalFooter } from '@oge-ui/overlay'; import type { OgeModalClosedEvent } from '@oge-ui/overlay'; @Component({ selector: 'demo-root', imports: [OgeButton, OgeModal, OgeModalFooter], changeDetection: ChangeDetectionStrategy.OnPush, template: `

This cannot be undone.

`, }) export class Demo { protected readonly opened = signal(false); // $event: { reason: 'api' | 'escape' | 'backdrop' | 'closeButton', result? } protected onClosed(event: OgeModalClosedEvent): void { if (event.result === 'delete') this.remove(); } private remove(): void { console.log('deleted'); } } ``` #### Sizing ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeModal } from '@oge-ui/overlay'; @Component({ selector: 'demo-root', imports: [OgeModal], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly opened = signal(false); protected readonly search = signal(false); protected readonly quiet = signal(false); protected readonly max = signal(false); } ``` #### Window ```ts import { ChangeDetectionStrategy, Component, inject, signal } from '@angular/core'; import { OgeModal, OGE_MODAL_DATA, OgeModalRef, OgeModalService } from '@oge-ui/overlay'; import { Injectable } from '@angular/core'; import type { OgeModalResizeEvent } from '@oge-ui/overlay'; @Component({ selector: 'demo-root', imports: [OgeModal], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly opened = signal(false); protected onResized(event: OgeModalResizeEvent): void { console.log(event.width, event.height); } // imperative, body-appended — for transformed ancestors & prompt flows private readonly modals = inject(OgeModalService); protected async openPrompt(): Promise { const ref = this.modals.open(RenameDialog, { title: 'Rename file', width: 380, data: { name: 'report.xlsx' }, }); const { result } = await ref.closed; if (result) this.rename(result); } private rename(name: string): void { console.log('renamed to', name); } } // content component: inject its data + the ref to close with a result @Component({ selector: 'demo-rename-dialog', template: `

Renaming {{ data.name }}

`, }) export class RenameDialog { readonly data = inject<{ name: string }>(OGE_MODAL_DATA); readonly ref = inject>(OgeModalRef); } ``` #### Menu ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeMenuList } from '@oge-ui/overlay'; import type { OgeMenuItem } from '@oge-ui/overlay'; @Component({ selector: 'demo-root', imports: [OgeMenuList], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly last = signal(''); protected readonly items: OgeMenuItem[] = [ { text: 'Duplicate', checked: false }, { text: 'Move to…' }, { text: '', separator: true }, { text: 'Delete', severity: 'danger' }, ]; } ``` #### Panel ```ts import { ChangeDetectionStrategy, Component, ElementRef, signal, viewChild } from '@angular/core'; import { OgeButton } from '@oge-ui/buttons'; import { OgePopup, OgeAnchoredPanel } from '@oge-ui/overlay'; import type { OgePopupPlacement } from '@oge-ui/overlay'; @Component({ selector: 'demo-root', imports: [OgeButton, OgePopup], changeDetection: ChangeDetectionStrategy.OnPush, template: ` @if (open()) {
Anchored content…
} `, }) export class Demo { readonly open = signal(false); readonly placement = signal('bottom-start'); private readonly anchorRef = viewChild.required>('anchor'); private readonly popupRef = viewChild(OgePopup, { read: ElementRef }); readonly panel = new OgeAnchoredPanel({ anchor: () => this.anchorRef().nativeElement, panel: () => this.popupRef()?.nativeElement ?? null, placement: () => this.placement(), onClosed: () => this.open.set(false), }); } ``` #### Action ```ts import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; import { OgeButton } from '@oge-ui/buttons'; import { OgeToastService } from '@oge-ui/overlay'; @Component({ selector: 'demo-root', imports: [OgeButton], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { private readonly toasts = inject(OgeToastService); protected async deleteRow(): Promise { const ref = this.toasts.show({ message: 'Row deleted', sticky: true, // action toasts should stick action: { text: 'Undo', handler: () => this.restore() }, }); const { reason } = await ref.closed; // 'action' | 'closeButton' | … console.log('closed because of', reason); } private restore(): void { console.log('restored'); } } ``` #### Basic ```ts import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; import { OgeButton } from '@oge-ui/buttons'; import { OgeToastService } from '@oge-ui/overlay'; @Component({ selector: 'demo-root', imports: [OgeButton], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { private readonly toasts = inject(OgeToastService); protected save(): void { this.toasts.success('Saved'); this.toasts.warning('Quota at 90%', { title: 'Heads up' }); this.toasts.error('Save failed'); // announces assertively this.toasts.show({ message: 'Plain info toast' }); } } ``` #### Coalesce ```ts import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; import { OgeButton } from '@oge-ui/buttons'; import { OgeToastService } from '@oge-ui/overlay'; @Component({ selector: 'demo-root', imports: [OgeButton], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { private readonly toasts = inject(OgeToastService); private readonly failedRows = [3, 17, 42]; protected report(): void { // identical toasts merge into one with a live ×N badge for (const _row of this.failedRows) { this.toasts.error('Import row failed', { coalesce: true }); } // remaining-time progress bar; pauses with the timer on hover/focus this.toasts.info('With progress', { progressBar: true, displayTime: 6000 }); } } ``` #### Position ```ts import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; import { OgeButton } from '@oge-ui/buttons'; import { OgeToastService, provideOgeOverlayConfig } from '@oge-ui/overlay'; import type { ApplicationConfig } from '@angular/core'; @Component({ selector: 'demo-root', imports: [OgeButton], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { private readonly toasts = inject(OgeToastService); // 6 logical positions (RTL-aware); the default comes from config protected notify(): void { this.toasts.info('Top center', { position: 'top-center' }); } } // extras beyond toastMaxVisible queue FIFO and promote as slots free up export const appConfig: ApplicationConfig = { providers: [ provideOgeOverlayConfig({ toastPosition: 'bottom-end', toastMaxVisible: 5 }), ], }; ``` #### Promise ```ts import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; import { OgeButton } from '@oge-ui/buttons'; import { OgeToastService } from '@oge-ui/overlay'; @Component({ selector: 'demo-root', imports: [OgeButton], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { private readonly toasts = inject(OgeToastService); // spinner → severity morph in place; the timer starts on settle protected publish(): void { this.toasts.promise(this.publishPages(), { loading: 'Publishing…', success: (result) => `Published ${result.count} pages`, error: (error) => ({ title: 'Publish failed', message: String(error) }), }); } private publishPages(): Promise<{ count: number }> { return fetch('/api/publish').then((response) => response.json()); } } ``` #### Context events ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeContextMenu } from '@oge-ui/overlay'; import type { OgeMenuItem } from '@oge-ui/overlay'; @Component({ selector: 'demo-root', imports: [OgeContextMenu], changeDetection: ChangeDetectionStrategy.OnPush, template: `
`, }) export class Demo { protected readonly menu: OgeMenuItem[] = [ { text: 'Open', value: 'open' }, { text: 'Delete', value: 'delete', severity: 'danger' }, ]; // $event: { item, index, event } — the same payload as OgeMenuList protected run(command: unknown): void { console.log('run', command); } protected log(phase: string): void { console.log(phase); } } ``` #### Context ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeContextMenu } from '@oge-ui/overlay'; import type { OgeMenuItem } from '@oge-ui/overlay'; @Component({ selector: 'demo-root', imports: [OgeContextMenu], changeDetection: ChangeDetectionStrategy.OnPush, template: `
Right-click me (or press Shift+F10)
`, }) export class Demo { // the canonical OgeMenuItem model: separators, checked state, // danger severity, per-item actions protected readonly rowMenu: OgeMenuItem[] = [ { text: 'Open', value: 'open' }, { text: 'Duplicate', value: 'duplicate' }, { separator: true, text: '' }, { text: 'Delete', value: 'delete', severity: 'danger' }, ]; } ``` #### Tooltip options ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeButton } from '@oge-ui/buttons'; import { OgeTooltip, provideOgeOverlayConfig } from '@oge-ui/overlay'; import type { ApplicationConfig } from '@angular/core'; @Component({ selector: 'demo-root', imports: [OgeButton, OgeTooltip], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo {} // application-wide defaults export const appConfig: ApplicationConfig = { providers: [ provideOgeOverlayConfig({ tooltipShowDelayMs: 300, tooltipHideDelayMs: 150 }), ], }; ``` #### Tooltip ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeButton } from '@oge-ui/buttons'; import { OgeTooltip } from '@oge-ui/overlay'; @Component({ selector: 'demo-root', imports: [OgeButton, OgeTooltip], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo {} ``` ## @oge-ui/tabs Tab strip and tab panel: declarative or data-driven tabs, deferred rendering with keep-alive, closable and reorderable tabs, router integration. Docs: https://ogeui.com/components/tabs ### Entry points `@oge-ui/tabs` - values: `OGE_DEFAULT_TABS_CONFIG`, `OGE_DEFAULT_TABS_MESSAGES`, `OGE_TABS_CONFIG`, `OgeTab`, `OgeTabContentTemplate`, `OgeTabHeaderTemplate`, `OgeTabPanel`, `OgeTabs`, `provideOgeTabsConfig` - types: `OgeTabClickEvent`, `OgeTabCloseGuard`, `OgeTabClosedEvent`, `OgeTabClosingEvent`, `OgeTabContentTemplateContext`, `OgeTabHeaderTemplateContext`, `OgeTabItem`, `OgeTabPanelAnimation`, `OgeTabReorderedEvent`, `OgeTabReorderingEvent`, `OgeTabSelectionChangedEvent`, `OgeTabSelectionChangingEvent`, `OgeTabsActivation`, `OgeTabsAlignment`, `OgeTabsConfig`, `OgeTabsConfigInput`, `OgeTabsIndicatorFit`, `OgeTabsMessages`, `OgeTabsNavButtonsMode`, `OgeTabsOrientation`, `OgeTabsPosition`, `OgeTabsSize`, `OgeTabsStylingMode` ### OgeTabPanel — `` #### Properties _Tabs & selection_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `items` | `readonly OgeTabItem[] \| undefined` | `—` | Data-driven tabs rendered after the projected `` children. | | `selectedIndex` | `number` | `0` | Index of the selected tab — two-way. `-1` selects none; clamped when tabs are removed. | | `selectedKey` | `string \| undefined` | `undefined` | Key of the selected tab — two-way, reconciled with `selectedIndex` both ways. | | `activation` | `'automatic' \| 'manual'` | `'automatic'` | APG activation: arrows select immediately, or move focus only until Enter/Space commits. | | `disabled` | `boolean` | `false` | Disables the whole component. | | `ariaLabel` | `string \| undefined` | `—` | Aria label of the tablist. | | `messages` | `Partial` | `{}` | Per-instance overrides of the config `messages`. | _Closing, overflow & order_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `closable` | `boolean` | `false` | Default closability; overridable per tab / per item. Closed tabs are removed by the app in `tabClosed`. The ✕ is presentational — the keyboard path is Delete/Backspace on the focused tab. | | `showNavButtons` | `'auto' \| 'always' \| 'never'` | `'auto'` | Overflow nav arrows; `auto` shows them only while the strip overflows. | | `showTabListButton` | `boolean` | `false` | Shows the all-tabs overflow menu (an `oge-menu-list` with the active tab checked). | | `allowTabReordering` | `boolean` | `false` | Enables drag & drop reordering of tab headers; Escape cancels an in-flight drag. | _Appearance_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `stylingMode` | `'primary' \| 'secondary'` | `'primary'` | Visual variant: underline ink (`primary`) or soft pills (`secondary`). | | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Density of the tab strip. | | `tabAlignment` | `'start' \| 'center' \| 'end' \| 'justify' \| 'stretch'` | `'start'` | Distribution of the tabs while they fit: `justify` spreads them to the edges, `stretch` gives every tab an equal share. | | `indicatorFit` | `'tab' \| 'content'` | `'tab'` | Whether the selected-tab indicator spans the whole tab or only its label area. | _Panel rendering (oge-tab-panel only)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `tabsPosition` | `'top' \| 'bottom' \| 'start' \| 'end'` | `'top'` | Side the strip sits on — logical values, so RTL flips `start`/`end`. Vertical positions switch the arrow keys to Up/Down. | | `deferRendering` | `boolean` | `true` | Instantiate a panel's content only when its tab first activates (lazy templates via `ogeTabContentTemplate`). | | `keepAlive` | `boolean` | `true` | Keep once-rendered panels mounted (hidden) so state survives switches; `false` destroys lazy content on deactivation. | | `panelAnimation` | `'none' \| 'fade' \| 'slide'` | `'none'` | Transition played by the incoming panel; `slide` enters from the direction of travel (mirrored in RTL). Duration comes from `--oge-tab-panel-transition` (180ms) and is suppressed under `prefers-reduced-motion`. | | `dynamicHeight` | `boolean` | `false` | Animates the content box between the outgoing and incoming panel heights instead of jumping; async content is tracked with a `ResizeObserver`. | #### Methods | Name | Type | Description | | --- | --- | --- | | `focus(): void` | `void` | Focuses the active tab header (roving-tabindex target). | | `closeTab(target: number \| string): void` | `void` | Runs the close pipeline (`tabClosing` → `closeGuard` → `tabClosed`) for an index or key. | | `scrollToTab(target: number \| string): void` | `void` | Scrolls the tab at an index or with a key into view. | #### Events | Name | Type | Description | | --- | --- | --- | | `selectionChanging` | `OgeTabSelectionChangingEvent` | Cancelable pre-event of a user-gesture selection change (`cancel = true` keeps the current tab). Programmatic model writes bypass it. | | `selectionChanged` | `OgeTabSelectionChangedEvent` | After the selection committed — `index/key/previousIndex/previousKey/item/event`. | | `tabClick` | `OgeTabClickEvent` | A tab header was activated by pointer or keyboard (Enter/Space). | | `tabClosing` | `OgeTabClosingEvent` | Cancelable pre-event of a close, before the async `closeGuard` runs. | | `tabClosed` | `OgeTabClosedEvent` | The close passed all guards — remove the `items` entry or `` here. | | `tabReordering` | `OgeTabReorderingEvent` | Cancelable pre-event of a drag-reorder drop (`fromIndex/toIndex/key`). | | `tabReordered` | `OgeTabReorderedEvent` | A drag reorder committed to the display order. | ### OgeTabs — `` #### Properties _Strip (oge-tabs only)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Strip direction; `vertical` renders a column, maps arrows to Up/Down and sets `aria-orientation`. | _Tabs & selection_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `items` | `readonly OgeTabItem[] \| undefined` | `—` | Data-driven tabs rendered after the projected `` children. | | `selectedIndex` | `number` | `0` | Index of the selected tab — two-way. `-1` selects none; clamped when tabs are removed. | | `selectedKey` | `string \| undefined` | `undefined` | Key of the selected tab — two-way, reconciled with `selectedIndex` both ways. | | `activation` | `'automatic' \| 'manual'` | `'automatic'` | APG activation: arrows select immediately, or move focus only until Enter/Space commits. | | `disabled` | `boolean` | `false` | Disables the whole component. | | `ariaLabel` | `string \| undefined` | `—` | Aria label of the tablist. | | `messages` | `Partial` | `{}` | Per-instance overrides of the config `messages`. | _Closing, overflow & order_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `closable` | `boolean` | `false` | Default closability; overridable per tab / per item. Closed tabs are removed by the app in `tabClosed`. The ✕ is presentational — the keyboard path is Delete/Backspace on the focused tab. | | `showNavButtons` | `'auto' \| 'always' \| 'never'` | `'auto'` | Overflow nav arrows; `auto` shows them only while the strip overflows. | | `showTabListButton` | `boolean` | `false` | Shows the all-tabs overflow menu (an `oge-menu-list` with the active tab checked). | | `allowTabReordering` | `boolean` | `false` | Enables drag & drop reordering of tab headers; Escape cancels an in-flight drag. | _Appearance_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `stylingMode` | `'primary' \| 'secondary'` | `'primary'` | Visual variant: underline ink (`primary`) or soft pills (`secondary`). | | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Density of the tab strip. | | `tabAlignment` | `'start' \| 'center' \| 'end' \| 'justify' \| 'stretch'` | `'start'` | Distribution of the tabs while they fit: `justify` spreads them to the edges, `stretch` gives every tab an equal share. | | `indicatorFit` | `'tab' \| 'content'` | `'tab'` | Whether the selected-tab indicator spans the whole tab or only its label area. | #### Methods | Name | Type | Description | | --- | --- | --- | | `focus(): void` | `void` | Focuses the active tab header (roving-tabindex target). | | `closeTab(target: number \| string): void` | `void` | Runs the close pipeline (`tabClosing` → `closeGuard` → `tabClosed`) for an index or key. | | `scrollToTab(target: number \| string): void` | `void` | Scrolls the tab at an index or with a key into view. | #### Events | Name | Type | Description | | --- | --- | --- | | `selectionChanging` | `OgeTabSelectionChangingEvent` | Cancelable pre-event of a user-gesture selection change (`cancel = true` keeps the current tab). Programmatic model writes bypass it. | | `selectionChanged` | `OgeTabSelectionChangedEvent` | After the selection committed — `index/key/previousIndex/previousKey/item/event`. | | `tabClick` | `OgeTabClickEvent` | A tab header was activated by pointer or keyboard (Enter/Space). | | `tabClosing` | `OgeTabClosingEvent` | Cancelable pre-event of a close, before the async `closeGuard` runs. | | `tabClosed` | `OgeTabClosedEvent` | The close passed all guards — remove the `items` entry or `` here. | | `tabReordering` | `OgeTabReorderingEvent` | Cancelable pre-event of a drag-reorder drop (`fromIndex/toIndex/key`). | | `tabReordered` | `OgeTabReorderedEvent` | A drag reorder committed to the display order. | ### OgeTab — `` #### Properties | Name | Type | Default | Description | | --- | --- | --- | --- | | `text` | `string` | `''` | Tab label; alternative to an inline `[ogeTabHeaderTemplate]`. | | `key` | `string \| undefined` | `—` | Stable identity used by `selectedKey`, reorder tracking and DOM ids. | | `disabled` | `boolean` | `false` | Disabled tabs are skipped by keyboard navigation and selection. | | `visible` | `boolean` | `true` | `false` removes the tab (and its panel) entirely. | | `closable` | `boolean \| undefined` | `undefined` | Shows a close button; `undefined` falls back to the component-level `closable`. | | `badge` | `string \| number \| undefined` | `—` | Badge rendered after the label. | | `dirty` | `boolean` | `false` | Renders the unsaved-changes dot and announces it to screen readers (`messages.dirty`). | | `hint` | `string \| undefined` | `—` | Tooltip — rendered as the native `title` attribute. | | `closeGuard` | `() => boolean \| Promise` | `—` | Veto hook run before this tab closes; may be async (single-flight, rejection = veto, pending spinner on the ✕). | #### Types _Template slots_ | Name | Type | Description | | --- | --- | --- | | `[ogeTabHeaderTemplate]` | `OgeTabHeaderTemplateContext` | Custom tab header (icons, rich markup). Inside an ``: that tab only; directly inside the component: every `items` tab. Context: `{ $implicit: item, index, selected, text }`. | | `[ogeTabContentTemplate]` | `OgeTabContentTemplateContext` | Lazily instantiated panel content. Inside an `` it replaces the projected content; directly inside `oge-tab-panel` it renders every `items` tab. Context: `{ $implicit: item, index }`. | ### Tabs configuration #### Properties | Name | Type | Description | | --- | --- | --- | | `provideOgeTabsConfig(config)` | `(config: OgeTabsConfigInput) => Provider` | Application- or component-scoped defaults; shallow-merges `messages` over the built-ins. | | `messages` | `OgeTabsMessages` | Every user-facing string: `closeTab`, `scrollBackward`, `scrollForward`, `tabListMenu`, `dirty`, `noData`. | #### Types | Name | Type | Description | | --- | --- | --- | | `OgeTabItem` | `{ key?, text?, badge?, hint?, disabled?, visible?, closable?, dirty?, closeGuard? }` | One data-driven tab of the `items` input. | | `OgeTabCloseGuard` | `() => boolean \| Promise` | Per-tab veto hook; resolving `false` (or rejecting) keeps the tab open. | | `OgeTabsActivation` | `'automatic' \| 'manual'` | How keyboard focus interacts with selection (APG). | | `OgeTabsPosition` | `'top' \| 'bottom' \| 'start' \| 'end'` | Logical strip placement of `oge-tab-panel`. | | `OgeTabsOrientation` | `'horizontal' \| 'vertical'` | Direction of a stand-alone `oge-tabs` strip. | | `OgeTabsNavButtonsMode` | `'auto' \| 'always' \| 'never'` | When the overflow nav arrows are shown. | | `OgeTabsAlignment` | `'start' \| 'center' \| 'end' \| 'justify' \| 'stretch'` | How tabs are distributed along the strip. | | `OgeTabsIndicatorFit` | `'tab' \| 'content'` | Width of the selected-tab indicator. | | `OgeTabPanelAnimation` | `'none' \| 'fade' \| 'slide'` | Transition played by the newly displayed panel. | | `OgeTabsStylingMode / OgeTabsSize` | `'primary' \| 'secondary' / 'sm' \| 'md' \| 'lg'` | Visual variant and density unions. | ### Demos Each demo below is a complete standalone component — copy one whole and it compiles. #### Alignment ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeTabs } from '@oge-ui/tabs'; import type { OgeTabItem } from '@oge-ui/tabs'; @Component({ selector: 'demo-root', imports: [OgeTabs], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly tabs: OgeTabItem[] = [ { key: 'one', text: 'One' }, { key: 'two', text: 'Two' }, ]; protected readonly index = signal(0); } ``` #### Animation ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeTab, OgeTabPanel } from '@oge-ui/tabs'; @Component({ selector: 'demo-root', imports: [OgeTab, OgeTabPanel], changeDetection: ChangeDetectionStrategy.OnPush, template: ` One line. Several lines of taller content… `, }) export class Demo { protected readonly index = signal(0); } /* duration is a CSS variable, not an input: .oge-tab-panel-content { --oge-tab-panel-transition: 240ms; } */ ``` #### Basic ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeTab, OgeTabPanel } from '@oge-ui/tabs'; import type { OgeTabSelectionChangedEvent } from '@oge-ui/tabs'; @Component({ selector: 'demo-root', imports: [OgeTab, OgeTabPanel], changeDetection: ChangeDetectionStrategy.OnPush, template: ` Project overview… Latest activity… Settings… `, }) export class Demo { protected readonly index = signal(0); protected onChanged(event: OgeTabSelectionChangedEvent): void { console.log('now on', event.index); } } ``` #### Close ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeTabPanel } from '@oge-ui/tabs'; import type { OgeTabClosedEvent, OgeTabItem } from '@oge-ui/tabs'; @Component({ selector: 'demo-root', imports: [OgeTabPanel], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { // the tab is removed by the app, after the guard allowed it protected readonly files = signal([ { key: 'a.ts', text: 'a.ts' }, // async closeGuard: resolve(false) keeps the tab, rejection = veto { key: 'b.ts', text: 'b.ts (guarded)', dirty: true, closeGuard: () => this.confirmDiscard(), }, ]); protected remove(e: OgeTabClosedEvent): void { this.files.set(this.files().filter((f) => f.key !== e.key)); } private confirmDiscard(): boolean { return confirm('Discard unsaved changes?'); } } ``` #### Items ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeTabContentTemplate, OgeTabPanel } from '@oge-ui/tabs'; import type { OgeTabItem } from '@oge-ui/tabs'; @Component({ selector: 'demo-root', imports: [OgeTabContentTemplate, OgeTabPanel], changeDetection: ChangeDetectionStrategy.OnPush, template: ` Editing {{ item?.text }} `, }) export class Demo { protected readonly docs: OgeTabItem[] = [ { key: 'readme', text: 'README.md' }, { key: 'spec', text: 'spec.ts', badge: 3 }, { key: 'draft', text: 'draft.md', dirty: true }, ]; protected readonly active = signal('readme'); } ``` #### Lazy ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeTab, OgeTabContentTemplate, OgeTabPanel } from '@oge-ui/tabs'; @Component({ selector: 'demo-root', imports: [OgeTab, OgeTabContentTemplate, OgeTabPanel], changeDetection: ChangeDetectionStrategy.OnPush, template: ` Created at {{ stamp() }} Created at {{ stamp() }} `, }) export class Demo { protected readonly keepAlive = signal(true); protected stamp(): string { return new Date().toLocaleTimeString(); } } ``` #### Overflow ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeTabs } from '@oge-ui/tabs'; import type { OgeTabItem } from '@oge-ui/tabs'; @Component({ selector: 'demo-root', imports: [OgeTabs], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly manyTabs: OgeTabItem[] = Array.from( { length: 20 }, (_, i) => ({ key: `t${i}`, text: `Section ${i + 1}` }), ); protected readonly index = signal(0); } ``` #### Position ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeTab, OgeTabPanel } from '@oge-ui/tabs'; @Component({ selector: 'demo-root', imports: [OgeTab, OgeTabPanel], changeDetection: ChangeDetectionStrategy.OnPush, template: ` General settings… Team members… `, }) export class Demo {} ``` #### Reorder ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeTabPanel } from '@oge-ui/tabs'; import type { OgeTabItem, OgeTabReorderedEvent } from '@oge-ui/tabs'; @Component({ selector: 'demo-root', imports: [OgeTabPanel], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly stages: OgeTabItem[] = [ { key: 'plan', text: 'Plan' }, { key: 'build', text: 'Build' }, { key: 'ship', text: 'Ship' }, ]; protected log(event: OgeTabReorderedEvent): void { console.log(event.fromIndex, '→', event.toIndex); } } ``` #### Routed ```ts import { ChangeDetectionStrategy, Component, computed, inject } from '@angular/core'; import { RouterOutlet, NavigationEnd, Router } from '@angular/router'; import { OgeTabs } from '@oge-ui/tabs'; import { toSignal } from '@angular/core/rxjs-interop'; import { filter, map } from 'rxjs'; import type { OgeTabItem, OgeTabSelectionChangedEvent } from '@oge-ui/tabs'; @Component({ selector: 'demo-root', imports: [RouterOutlet, OgeTabs], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly tabs: OgeTabItem[] = [ { key: 'overview', text: 'Overview' }, { key: 'activity', text: 'Activity' }, { key: 'settings', text: 'Settings' }, ]; // the URL is the single source of truth private readonly router = inject(Router); private readonly url = toSignal( this.router.events.pipe( filter((e): e is NavigationEnd => e instanceof NavigationEnd), map((e) => e.urlAfterRedirects), ), { initialValue: this.router.url }, ); protected readonly activeKey = computed(() => { const segment = this.url().split(/[?#]/)[0].split('/').pop() ?? ''; return this.tabs.some((t) => t.key === segment) ? segment : 'overview'; }); protected go(event: OgeTabSelectionChangedEvent): void { if (event.key) { void this.router.navigate(['/components/tabs/routed', event.key]); } } } ``` ## @oge-ui/layout Layout containers and loading visuals — accordion panels with single or multiple expansion, a splitter with resizable, collapsible and nestable panes, a toolbar with an overflow menu, a card content surface with attribute-slot sections, and the loading trio: progress bar (buffer/chunked/severity), load-indicator ring and shimmer skeleton with the aria progressbar contract done right. Docs: https://ogeui.com/components/accordion ### Entry points `@oge-ui/layout` - values: `OGE_ACCORDION_CONFIG`, `OGE_CARD_CONFIG`, `OGE_DEFAULT_ACCORDION_CONFIG`, `OGE_DEFAULT_ACCORDION_MESSAGES`, `OGE_DEFAULT_CARD_CONFIG`, `OGE_DEFAULT_LOAD_INDICATOR_CONFIG`, `OGE_DEFAULT_LOAD_INDICATOR_MESSAGES`, `OGE_DEFAULT_PROGRESS_BAR_CONFIG`, `OGE_DEFAULT_PROGRESS_BAR_MESSAGES`, `OGE_DEFAULT_SKELETON_CONFIG`, `OGE_DEFAULT_SPLITTER_CONFIG`, `OGE_DEFAULT_SPLITTER_MESSAGES`, `OGE_DEFAULT_TOOLBAR_CONFIG`, `OGE_DEFAULT_TOOLBAR_MESSAGES`, `OGE_LOAD_INDICATOR_CONFIG`, `OGE_PROGRESS_BAR_CONFIG`, `OGE_SKELETON_CONFIG`, `OGE_SPLITTER_CONFIG`, `OGE_TOOLBAR_CONFIG`, `OgeAccordion`, `OgeAccordionActionRow`, `OgeAccordionContentTemplate`, `OgeAccordionHeaderActionsTemplate`, `OgeAccordionHeaderTemplate`, `OgeAccordionItem`, `OgeAccordionToggleIconTemplate`, `OgeCard`, `OgeCardActions`, `OgeCardAvatar`, `OgeCardFooter`, `OgeCardHeaderActions`, `OgeCardMedia`, `OgeCardSeparator`, `OgeLoadIndicator`, `OgeProgressBar`, `OgeSkeleton`, `OgeSplitter`, `OgeSplitterPane`, `OgeSplitterPaneTemplate`, `OgeToolbar`, `OgeToolbarItem`, `OgeToolbarItemTemplate`, `OgeToolbarMenuItemTemplate`, `provideOgeAccordionConfig`, `provideOgeCardConfig`, `provideOgeLoadIndicatorConfig`, `provideOgeProgressBarConfig`, `provideOgeSkeletonConfig`, `provideOgeSplitterConfig`, `provideOgeToolbarConfig` - types: `OgeAccordionCollapsedEvent`, `OgeAccordionCollapsingEvent`, `OgeAccordionConfig`, `OgeAccordionConfigInput`, `OgeAccordionContentFailedEvent`, `OgeAccordionContentLoadedEvent`, `OgeAccordionContentLoader`, `OgeAccordionContentTemplateContext`, `OgeAccordionDisplayMode`, `OgeAccordionExpandGuard`, `OgeAccordionExpandedEvent`, `OgeAccordionExpandingEvent`, `OgeAccordionHeaderActionsTemplateContext`, `OgeAccordionHeaderTemplateContext`, `OgeAccordionItemClickEvent`, `OgeAccordionItemData`, `OgeAccordionMessages`, `OgeAccordionSize`, `OgeAccordionStylingMode`, `OgeAccordionToggleIconTemplateContext`, `OgeAccordionTogglePosition`, `OgeCardActionsAlign`, `OgeCardConfig`, `OgeCardConfigInput`, `OgeCardOrientation`, `OgeCardSeverity`, `OgeCardSize`, `OgeCardStylingMode`, `OgeLoadIndicatorConfig`, `OgeLoadIndicatorConfigInput`, `OgeLoadIndicatorMessages`, `OgeLoadIndicatorSeverity`, `OgeProgressBarCompletedEvent`, `OgeProgressBarConfig`, `OgeProgressBarConfigInput`, `OgeProgressBarMessages`, `OgeProgressBarSeverity`, `OgeSkeletonAnimation`, `OgeSkeletonConfig`, `OgeSkeletonConfigInput`, `OgeSkeletonShape`, `OgeSplitterConfig`, `OgeSplitterConfigInput`, `OgeSplitterGripSide`, `OgeSplitterMessages`, `OgeSplitterOrientation`, `OgeSplitterPaneClickEvent`, `OgeSplitterPaneCollapsedEvent`, `OgeSplitterPaneCollapsingEvent`, `OgeSplitterPaneData`, `OgeSplitterPaneHoldEvent`, `OgeSplitterPaneTemplateContext`, `OgeSplitterResizeEvent`, `OgeSplitterResizeStartEvent`, `OgeSplitterSize`, `OgeToolbarConfig`, `OgeToolbarConfigInput`, `OgeToolbarDisplayMode`, `OgeToolbarItemActiveChangedEvent`, `OgeToolbarItemClickEvent`, `OgeToolbarItemData`, `OgeToolbarItemHoldEvent`, `OgeToolbarItemLocation`, `OgeToolbarItemSeverity`, `OgeToolbarItemTemplateContext`, `OgeToolbarItemType`, `OgeToolbarLocateInMenu`, `OgeToolbarMenuCloseReason`, `OgeToolbarMenuClosedEvent`, `OgeToolbarMenuClosingEvent`, `OgeToolbarMenuOpeningEvent`, `OgeToolbarMessages`, `OgeToolbarOrientation`, `OgeToolbarOverflow`, `OgeToolbarOverflowChangedEvent`, `OgeToolbarSize`, `OgeToolbarStylingMode` ### OgeAccordion — `` #### Properties _Panels & expansion_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `items` | `readonly OgeAccordionItemData[] \| undefined` | `—` | Data-driven panels rendered after the projected `` children. | | `expandedKeys` | `readonly string[]` | `[]` | Keys of the expanded panels — two-way. The multi-expand counterpart of `selectedIndex`; only panels that declare a `key` can appear here. | | `selectedIndex` | `number` | `-1` | Index of the expanded panel in single-expand mode — two-way. `-1` means none; in `multiple` mode it reports the first expanded panel. | | `multiple` | `boolean` | `false` | Allows more than one panel to stay expanded. | | `collapsible` | `boolean` | `false` | Allows collapsing the last expanded panel, leaving none open. While `false`, that header is `aria-disabled` per the APG — it stays focusable. | | `disabled` | `boolean` | `false` | Disables the whole component. | _Rendering & animation_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `deferRendering` | `boolean` | `true` | Instantiate a panel's content only when it first expands. | | `keepAlive` | `boolean` | `true` | Keep once-rendered panels mounted (hidden) so their state survives a collapse. Ignored while `deferRendering` is `false`. | | `animation` | `boolean \| number` | `true` | Height animation: `true` uses the default duration, a number overrides it in milliseconds, `false` disables it. Always suppressed under `prefers-reduced-motion`. | _Appearance_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `togglePosition` | `'start' \| 'end'` | `'end'` | Side of the header the chevron sits on — logical, so RTL mirrors it. | | `hideToggle` | `boolean` | `false` | Hides the chevron entirely. Overridable per panel via ``. | | `collapsedHeaderHeight` | `string \| undefined` | `—` | Minimum height of a collapsed header (any CSS length). `undefined` lets `size` and the padding tokens decide. Material’s `collapsedHeight`. | | `expandedHeaderHeight` | `string \| undefined` | `—` | Minimum height of an expanded header; falls back to `collapsedHeaderHeight`. Material’s `expandedHeight`. | | `displayMode` | `'default' \| 'flat'` | `'default'` | `flat` removes the gutters between panels and joins them into one stack. | | `stylingMode` | `'outlined' \| 'filled' \| 'flat'` | `'outlined'` | Visual variant of the panels. | | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Density of the header rows. | _Keyboard & accessibility_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `keyboardNavigation` | `boolean` | `true` | Enables Up/Down/Home/End and Ctrl+PageUp/PageDown header navigation. The APG pattern itself requires only Enter/Space and Tab — this is the optional enhancement. | | `typeAhead` | `boolean` | `true` | Enables printable-character type-ahead over the panel titles. Matching is accent- and locale-insensitive. | | `selectOnFocus` | `boolean` | `false` | Expands a panel as soon as keyboard navigation moves focus onto it. | | `headingLevel` | `number` | `3` | `aria-level` of the heading wrapping each header button. | | `useRegionRole` | `boolean` | `true` | Gives each panel `role="region"` (APG-optional; adds one landmark per panel). | | `ariaLabel` | `string \| undefined` | `—` | Aria label of the accordion container. | | `messages` | `Partial` | `{}` | Per-instance overrides of the config `messages`. | #### Methods | Name | Type | Description | | --- | --- | --- | | `expand(target)` | `(target: number \| string) => Promise` | Runs the expand pipeline for the panel at an index or with a key. Resolves `true` once it expanded, `false` if an unknown target, `itemExpanding` or the `expandGuard` vetoed it. | | `collapse(target)` | `(target: number \| string) => Promise` | Runs the collapse pipeline; resolves whether the panel actually collapsed. | | `toggle(target)` | `(target: number \| string) => Promise` | Expands the panel if collapsed, collapses it otherwise. | | `expandAll()` | `() => void` | Expands every enabled panel. Requires `multiple` — otherwise it warns in dev mode and does nothing. | | `collapseAll()` | `() => void` | Collapses every panel. In single-expand mode the last panel stays open unless `collapsible` is set. | | `expandInvalid()` | `() => void` | Expands every panel flagged `invalid` — call it after a failed form submit so the user sees each section needing attention. | | `isExpanded(target)` | `(target: number \| string) => boolean` | Whether the panel at an index or with a key is currently expanded. | | `focus(target?)` | `(target?: number \| string) => void` | Focuses a panel's header button, or the first enabled one. | #### Events | Name | Type | Description | | --- | --- | --- | | `itemExpanding` | `OgeAccordionExpandingEvent` | Cancelable pre-event of a panel expanding — set `cancel = true` to block it. Runs before the panel’s `expandGuard`. | | `itemExpanded` | `OgeAccordionExpandedEvent` | Emitted after a panel expanded. | | `itemCollapsing` | `OgeAccordionCollapsingEvent` | Cancelable pre-event of a panel collapsing — set `cancel = true` to block it. | | `itemCollapsed` | `OgeAccordionCollapsedEvent` | Emitted after a panel collapsed. | | `afterExpand` | `OgeAccordionExpandedEvent` | Emitted once the expand animation finished — the point at which the panel has its final height. Fires immediately when the animation is off or suppressed by `prefers-reduced-motion`. | | `afterCollapse` | `OgeAccordionCollapsedEvent` | Emitted once the collapse animation finished. | | `itemClick` | `OgeAccordionItemClickEvent` | Emitted when a header button is activated, before the expand pipeline runs. Fires for disabled panels too. | | `itemContentLoaded` | `OgeAccordionContentLoadedEvent` | Emitted after a panel's `contentLoader` resolved. | | `itemContentFailed` | `OgeAccordionContentFailedEvent` | Emitted after a panel's `contentLoader` rejected. | | `expandedKeysChange` | `readonly string[]` | Two-way model output of `expandedKeys`. | | `selectedIndexChange` | `number` | Two-way model output of `selectedIndex`. | #### Types _Types_ | Name | Type | Description | | --- | --- | --- | | `OgeAccordionItemData` | `interface` | Data-driven counterpart of a declarative panel: `key`, `title`, `description`, `icon`, `badge`, `hint`, `disabled`, `visible`, `expanded`, `invalid`, `expandGuard`, `contentLoader`. | | `OgeAccordionExpandGuard` | `() => boolean \| Promise` | Veto for a pending expand or collapse. `false` blocks it; throwing or rejecting is also a veto. While a promise is pending the panel shows a spinner and ignores further toggles (single-flight). | | `OgeAccordionContentLoader` | `() => Promise` | Loads a panel's content the first time it expands. The resolved value reaches the content template as `data`. | | `OgeAccordionTogglePosition` | `'start' \| 'end'` | Chevron side inside the header button. | | `OgeAccordionDisplayMode` | `'default' \| 'flat'` | Gutters between panels, or one joined stack. | | `OgeAccordionStylingMode` | `'outlined' \| 'filled' \| 'flat'` | Visual variant of the panels. | | `OgeAccordionSize` | `'sm' \| 'md' \| 'lg'` | Density of the header rows. | ### OgeAccordionItem — `` #### Properties | Name | Type | Default | Description | | --- | --- | --- | --- | | `title` | `string` | `''` | Header title; alternative to an inline `[ogeAccordionHeaderTemplate]`. | | `text` | `string \| undefined` | `—` | Plain-text panel body, rendered when there is no projected content or content template. The reference `html` item field has no counterpart on purpose. | | `description` | `string \| undefined` | `—` | Secondary line rendered under the title. | | `key` | `string \| undefined` | `—` | Stable identity used by `expandedKeys` and DOM ids. | | `icon` | `string \| undefined` | `—` | SVG path data (`d`) rendered as a 24×24 aria-hidden icon before the title. | | `badge` | `string \| number \| undefined` | `—` | Badge rendered after the title. | | `hint` | `string \| undefined` | `—` | Tooltip — rendered as the native `title` attribute. | | `disabled` | `boolean` | `false` | Disabled panels cannot expand and are skipped by keyboard navigation. | | `visible` | `boolean` | `true` | `false` removes the panel entirely. | | `expanded` | `boolean` | `false` | Expanded state of this panel — **two-way**. Set it to expand on first render, bind it to follow the state, or write to it to drive the panel from outside. Writes still run the pipeline, so a veto reverts the binding. | | `hideToggle` | `boolean \| undefined` | `—` | Overrides the accordion's `hideToggle` for this panel. | | `togglePosition` | `'start' \| 'end' \| undefined` | `—` | Overrides the accordion's `togglePosition` for this panel. | | `invalid` | `boolean` | `false` | Flags the section as failing validation — renders the danger rail and feeds `expandInvalid()`. | | `expandGuard` | `OgeAccordionExpandGuard \| undefined` | `—` | Veto hook run before this panel expands or collapses; may be async (single-flight). | | `contentLoader` | `OgeAccordionContentLoader \| undefined` | `—` | Loads this panel's content on first expand, with a skeleton while pending and a retry button on failure. | #### Methods | Name | Type | Description | | --- | --- | --- | | `open()` | `() => void` | Expands this panel. Like a user gesture it runs the accordion’s pipeline, so `itemExpanding` and `expandGuard` can still veto it. | | `close()` | `() => void` | Collapses this panel, subject to `collapsible` and the guards. | | `toggle()` | `() => void` | Expands the panel if collapsed, collapses it otherwise. | #### Types _Template slots_ | Name | Type | Description | | --- | --- | --- | | `[ogeAccordionHeaderTemplate]` | `{ $implicit, index, expanded, title, description }` | Replaces the built-in title/description/icon layout inside the header button. Component-level instances apply to `items` panels only (queried with `descendants: false`). Must not contain focusable controls. | | `[ogeAccordionContentTemplate]` | `{ $implicit, index, data }` | Panel body; marks the content lazy. `data` carries the panel's `contentLoader` result. | | `[ogeAccordionToggleIconTemplate]` | `{ $implicit: boolean, index }` | Replaces the chevron. Accordion-level chrome — a component-level instance applies to declarative children too. | | `[ogeAccordionHeaderActionsTemplate]` | `{ $implicit, index, expanded }` | Per-panel actions rendered _beside_ the toggle button, never inside it — real focusable controls without a `nested-interactive` violation. | | `[ogeAccordionActionRow]` | `directive` | Marks a row of buttons at the end of a panel body as its action bar (divider above, actions at the inline end) — the references' action-row slot. Inside the panel, so only reachable while expanded. | ### Accordion configuration #### Properties _OgeAccordionMessages_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `invalidSection` | `string` | `'section has errors'` | Announced after the title of a panel flagged `invalid`. | | `pending` | `string` | `'working'` | Announced while an `expandGuard` promise is in flight. | | `loadingContent` | `string` | `'Loading…'` | Shown while a panel's `contentLoader` is running. | | `contentLoadFailed` | `string` | `'Could not load this section.'` | Shown when a panel's `contentLoader` rejected. | | `retry` | `string` | `'Retry'` | Label of the retry button on a failed content load. | | `noData` | `string` | `'No sections to display'` | Shown in place of the panels when there are no visible items. | #### Types _Behavioural defaults_ | Name | Type | Description | | --- | --- | --- | | `hideToggle` | `boolean \| undefined` | Default for the `hideToggle` input. | | `collapsedHeaderHeight` | `string \| undefined` | Default for the `collapsedHeaderHeight` input. | | `expandedHeaderHeight` | `string \| undefined` | Default for the `expandedHeaderHeight` input. Together with the two above this is the `MAT_EXPANSION_PANEL_DEFAULT_OPTIONS` equivalent. | | Name | Type | Description | | --- | --- | --- | | `provideOgeAccordionConfig(config)` | `(config: OgeAccordionConfigInput) => Provider` | Application- or component-scoped defaults; shallow-merges `messages` over the built-ins. | | `OGE_ACCORDION_CONFIG` | `InjectionToken` | The token itself, with a factory default — inject it to read the effective config. | ### OgeCard — `` #### Properties | Name | Type | Default | Description | | --- | --- | --- | --- | | `header` | `string \| undefined` | `—` | Header title. Named after the PrimeNG input rather than `title` — a static `title` attribute would double as a native tooltip. | | `subheader` | `string \| undefined` | `—` | Line rendered under `header` in the muted color. | | `stylingMode` | `'outlined' \| 'raised' \| 'filled' \| 'flat'` | `'outlined'` | Chrome preset: `outlined` (border), `raised` (rests on the `--oge-shadow-card` token), `filled` (tinted surface) or `flat` (no chrome — for a card nested in another surface). Falls back to `provideOgeCardConfig({ stylingMode })`. | | `orientation` | `'vertical' \| 'horizontal'` | `'vertical'` | `horizontal` turns the card into a two-column grid with the `[ogeCardMedia]` element spanning the inline-start column, sized by `--oge-card-media-size`. Falls back to `provideOgeCardConfig({ orientation })`. | | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Density preset — scales the section padding and type ramp together (`--oge-card-pad` is the per-card escape hatch). Falls back to `provideOgeCardConfig({ size })`. | | `severity` | `'accent' \| 'success' \| 'warning' \| 'danger' \| undefined` | `undefined` | Colored status rail on the inline-start edge — the toast’s rail idiom on a static surface. `undefined` renders no rail. | | `interactive` | `boolean` | `false` | Purely visual affordance for the documented clickable-card pattern: a hover/focus-within lift and a keyboard focus ring on the surface. Adds **no** role, tabindex or wrapper — pair it with one primary `` in the content. | | `loading` | `boolean` | `false` | Replaces the content and action row with a shimmer skeleton and marks the card `aria-busy`. Header, media and footer stay, so the card keeps its footprint while the data arrives. | _Accessibility contract_ | Name | Type | Description | | --- | --- | --- | | `(no role, no clickable input)` | `—` | There is no WAI-ARIA card pattern, so the card renders no role and no `tabindex`, and ships no clickable-card API — wrapping the card in a link or button is the `nested-interactive` trap. Add `role="article"` / `role="region"` on the host yourself, and make a card clickable with one primary `` in the content plus a CSS-stretched hit area. | #### Types | Name | Type | Description | | --- | --- | --- | | `OgeCardStylingMode` | `'outlined' \| 'raised' \| 'filled' \| 'flat'` | Chrome preset union — the layout family’s `stylingMode` vocabulary plus Material’s `raised`. | | `OgeCardOrientation` | `'vertical' \| 'horizontal'` | Section flow union. | | `OgeCardSize` | `'sm' \| 'md' \| 'lg'` | Density preset union. | | `OgeCardSeverity` | `'accent' \| 'success' \| 'warning' \| 'danger'` | Status rail union for the `severity` input. | | `OgeCardActionsAlign` | `'start' \| 'center' \| 'end' \| 'stretched'` | Justification of the `[ogeCardActions]` row. | | `OgeCardConfig / OgeCardConfigInput` | `{ stylingMode?; orientation?; size? }` | The config shape and its partial input for `provideOgeCardConfig()`. | ### Slot directives #### Properties | Name | Type | Description | | --- | --- | --- | | `[ogeCardMedia]` | `OgeCardMedia` | Marks the full-bleed media element — an ``, `` or a wrapper. Sized by consumer CSS (`aspect-ratio`, `block-size`); there is deliberately no size input. | | `[ogeCardAvatar]` | `OgeCardAvatar` | The round image before the header titles — the counterpart of Material’s `mat-card-avatar`. | | `[ogeCardHeaderActions]` | `OgeCardHeaderActions` | Controls at the inline end of the header row. Real controls in the Tab sequence — the card never wraps them in anything interactive. | | `[ogeCardActions]` | `OgeCardActions` | The action row under the content. Its `align` input takes `'start' \| 'center' \| 'end' \| 'stretched'` (default `'start'`) — the Kendo superset of Material’s two values. | | `[ogeCardFooter]` | `OgeCardFooter` | A divided strip on the header surface after the actions — metadata rather than commands. | | `[ogeCardSeparator]` | `OgeCardSeparator` | A full-bleed hairline between content sections — put it on an `` inside the default projection. | ### Card configuration #### Properties | Name | Type | Description | | --- | --- | --- | | `provideOgeCardConfig(config)` | `(config: OgeCardConfigInput) => Provider` | Application- or component-scoped defaults for `stylingMode`, `orientation` and `size`. There is deliberately no `messages` block: the card renders no user-facing strings and no interactive chrome of its own. | | `OGE_CARD_CONFIG` | `InjectionToken` | The token behind `provideOgeCardConfig()`, with `OGE_DEFAULT_CARD_CONFIG` as its factory default. | ### OgeProgressBar — `` #### Properties | Name | Type | Default | Description | | --- | --- | --- | --- | | `value` | `number \| null` | `null` | Current value; `null` renders the **indeterminate** sliding bar — and `aria-valuenow` is then omitted entirely (the ARIA rule), never pinned to a sentinel. | | `min / max` | `number` | `0 / 100` | Scale bounds; the fill ratio clamps into them. | | `bufferValue` | `number \| undefined` | `—` | Material's buffer layer — a soft second fill behind the primary one (media pre-loading behind the play position). | | `chunkCount` | `number \| undefined` | `—` | Renders the bar as N discrete segments (Kendo's chunk progress bar); the filled count is the rounded ratio. | | `severity` | `'accent' \| 'success' \| 'warning' \| 'danger'` | `'accent'` | Fill color — the card/toast severity vocabulary; recolors the fill only. | | `showLabel` | `boolean` | `false` | Renders the formatted value next to the bar (rounded percent by default). | | `formatLabel` | `(value: number, ratio: number) => string \| undefined` | `—` | Formats the visible label **and** `aria-valuetext` — DevExtreme's `statusFormat` in the house argument order; display and announcement never diverge. | | `ariaLabel` | `string \| undefined` | `—` | Accessible name; the localized `progress` message is the fallback. A progressbar must always be named. | #### Events | Name | Type | Description | | --- | --- | --- | | `completed` | `OgeProgressBarCompletedEvent` | The value reached `max` — DevExtreme's `onComplete`. Fired once per arrival: staying at max is silent, re-crossing after a reset fires again. | #### Types | Name | Type | Description | | --- | --- | --- | | `OgeProgressBarSeverity` | `'accent' \| 'success' \| 'warning' \| 'danger'` | Fill color vocabulary. | | `OgeProgressBarCompletedEvent` | `{ value: number }` | Payload of `completed`. | ### OgeLoadIndicator — `` #### Properties | Name | Type | Default | Description | | --- | --- | --- | --- | | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Ring diameter preset — 16/24/32px. | | `inheritSize` | `boolean` | `false` | A `1em` ring that scales with the surrounding font — the inside-a-button case. | | `severity` | `'accent' \| 'success' \| 'warning' \| 'danger'` | `'accent'` | Ring color — the card/toast severity vocabulary. | | `ariaLabel` | `string \| undefined` | `—` | Accessible name; the localized `loading` message is the fallback. | _Accessibility contract_ | Name | Type | Description | | --- | --- | --- | | `role="progressbar", no aria-valuenow` | `—` | Deliberately indeterminate-only (dx, Kendo and PrimeNG all are — a circle filling toward completion is the progress bar’s job), announced without `aria-valuenow` per the ARIA rule. Under `prefers-reduced-motion` the spin **slows rather than stops**: a frozen ring reads as finished. | ### OgeSkeleton — `` #### Properties | Name | Type | Default | Description | | --- | --- | --- | --- | | `shape` | `'text' \| 'circle' \| 'rectangle'` | `'text'` | What the placeholder stands in for; a `text` skeleton with no height derives it from the font. | | `animation` | `'shimmer' \| 'pulse' \| 'none'` | `'shimmer'` | `shimmer` is the card/accordion moving-gradient recipe, `pulse` the data grid filler rows' opacity beat, `none` a static block. | | `width / height` | `string \| number \| undefined` | `—` | Numbers mean pixels; strings pass through as CSS. | | `lines` | `number` | `1` | `text` shape only: renders N stacked lines with the last one tapered — the card/accordion placeholder pattern as one input. Capped at 20. | _Accessibility contract_ | Name | Type | Description | | --- | --- | --- | | `aria-hidden, always` | `—` | A skeleton is decoration — the loading **region** owns the announcement. Put `aria-busy` (and, where the change should be announced, a visually-hidden status text) on the container the skeleton stands in for. | ### Configuration #### Properties _provideOgeProgressBarConfig()_ | Name | Type | Description | | --- | --- | --- | | `messages` | `OgeProgressBarMessages` | Every user-facing string: `progress` — the accessible name fallback (default `Progress`). | | `severity / showLabel` | `—` | Defaults for the matching inputs. | _provideOgeLoadIndicatorConfig()_ | Name | Type | Description | | --- | --- | --- | | `messages` | `OgeLoadIndicatorMessages` | Every user-facing string: `loading` — the accessible name fallback (default `Loading`). | _provideOgeSkeletonConfig()_ | Name | Type | Description | | --- | --- | --- | | `shape / animation` | `—` | Defaults for the matching inputs. Deliberately no messages block: a skeleton renders no user-facing strings — the loading region owns the announcement. | ### OgeSplitter — `` #### Properties _Panes & sizing_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `panes` | `readonly OgeSplitterPaneData[] \| undefined` | `—` | Data-driven panes rendered after the projected `` children. | | `dataSource` | `DataSource \| undefined` | `—` | Remote pane list, loaded through `@oge-ui/core`’s `DataSource` contract and merged after `panes`. A source that publishes `changes` triggers a reload. | | `itemHoldTimeout` | `number` | `750` | Milliseconds a pointer must rest on a pane before `paneHold` fires. | | `sizes` | `readonly OgeSplitterSize[] \| undefined` | `—` | Current pane sizes — two-way, and the whole persistable state. Setting it overrides the per-pane `size` inputs. Numbers are ratios; `'240px'` pins a pane. | | `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Axis the panes are laid out along. Also drives which arrow keys move a separator. | | `separatorSize` | `number` | `6` | Thickness of each separator in pixels — a real grid track, so it never eats into a pane. | _Interaction_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `resizable` | `boolean` | `true` | Pins every separator when `false`. | | `step` | `number` | `5` | Share points one arrow-key press moves a separator. | | `keyboardNavigation` | `boolean` | `true` | Enables Arrow / Home / End / Enter / Ctrl+Arrow on the separators. While off they also leave the Tab sequence. | | `showCollapseGrips` | `boolean` | `true` | Renders an `aria-hidden` chevron on the separator for each collapsible neighbour — one for the pane before it, one for the pane after. The keyboard paths (Enter, Ctrl+Arrow) stay available either way. | | `disabled` | `boolean` | `false` | Disables the whole splitter — no dragging, no keyboard, no collapsing. | _Accessibility & text_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `ariaLabel` | `string \| undefined` | `—` | Accessible name of the splitter container. | | `messages` | `Partial` | `{}` | Per-instance overrides of the config strings, including the separators’ accessible names. | #### Methods | Name | Type | Description | | --- | --- | --- | | `collapse(target)` | `(target: number \| string) => boolean` | Collapses a pane by index or key. Returns `false` when the pane is not collapsible or `paneCollapsing` vetoed it. | | `expand(target)` | `(target: number \| string) => boolean` | Expands a collapsed pane, restoring the size it had when it collapsed and scaling its siblings back down to fit. | | `toggle(target)` | `(target: number \| string) => boolean` | Collapses the pane if expanded, expands it otherwise. | | `isCollapsed(target)` | `(target: number \| string) => boolean` | Whether a pane is currently collapsed. | | `resize(separatorIndex, delta)` | `(separatorIndex: number, delta: number) => boolean` | Moves a separator by `delta` share points — the programmatic equivalent of an arrow key. `false` when that separator cannot move. | | `focus(separatorIndex?)` | `(separatorIndex?: number) => void` | Focuses a separator, the first one by default. | #### Events | Name | Type | Description | | --- | --- | --- | | `resizeStarted` | `OgeSplitterResizeStartEvent` | Emitted once when a drag or keyboard resize begins. The reference `onResizeStart`. | | `resized` | `OgeSplitterResizeEvent` | Emitted every time the sizes change during a resize — once per pointer move. The reference `onResize`. | | `resizeEnded` | `OgeSplitterResizeEvent` | Emitted once when the gesture finishes, after the sizes model has been published. The reference `onResizeEnd`. | | `paneCollapsing` | `OgeSplitterPaneCollapsingEvent` | Cancelable pre-event of a pane collapsing — set `cancel = true` to block it. | | `paneExpanding` | `OgeSplitterPaneCollapsingEvent` | Cancelable pre-event of a pane expanding. | | `paneCollapsed` | `OgeSplitterPaneCollapsedEvent` | Emitted after a pane collapsed. | | `paneExpanded` | `OgeSplitterPaneCollapsedEvent` | Emitted after a pane expanded. | | `paneClick` | `OgeSplitterPaneClickEvent` | Emitted when a pane is clicked. A nested splitter reports its own panes — the event does not surface on the parent. | | `paneHold` | `OgeSplitterPaneHoldEvent` | A pane was held for `itemHoldTimeout` — a touch long-press or a mouse hold. | | `paneContextMenu` | `OgeSplitterPaneHoldEvent` | A pane was right-clicked or long-pressed for a menu. | | `sizesChange` | `readonly OgeSplitterSize[] \| undefined` | Two-way model output of `sizes`. | #### Types _Types_ | Name | Type | Description | | --- | --- | --- | | `OgeSplitterOrientation` | `'horizontal' \| 'vertical'` | Axis the panes are laid out along. | | `OgeSplitterGripSide` | `'start' \| 'end'` | Which neighbour a separator's collapse grip acts on: `'start'` is the pane before it (the APG primary pane), `'end'` the one after. | | `OgeSplitterSize` | `number \| string` | A number (or `'%'`) is a **ratio** of the space the flexible panes share, so `[30, 30]` lays out like `[50, 50]`. `'px'` pins the pane to a fixed track. Any other string is ignored with a dev-mode warning. | | `OgeSplitterPaneData` | `interface` | Data-driven counterpart of a declarative pane: `key`, `size`, `minSize`, `maxSize`, `collapsible`, `collapsed`, `collapsedSize`, `resizable`, `scrollable`, `disabled`, `visible`, `text`, `cssClass`, `htmlAttributes`, `panes`, `orientation`. | | `OgeSplitterPaneTemplateContext` | `interface` | Context of `[ogeSplitterPaneTemplate]`: `$implicit` (the pane entry), `index`, `collapsed`. | | `OgeSplitterResizeStartEvent` | `interface` | `separatorIndex`, `sizes` at the start of the gesture, and the originating `event` (absent for a keyboard resize). | | `OgeSplitterResizeEvent` | `interface` | `separatorIndex`, current `sizes`, `previousSizes` from the start of the gesture, and the originating `event`. | | `OgeSplitterPaneCollapsingEvent` | `interface` | `index`, `key`, `item`, `event`, and a mutable `cancel` flag. | | `OgeSplitterPaneCollapsedEvent` | `interface` | `index`, `key`, `item`, `event`. | | `OgeSplitterPaneHoldEvent` | `interface` | Payload of `paneHold` and `paneContextMenu`: `index`, `key?`, `item?`, `event`. | | `OgeSplitterPaneClickEvent` | `interface` | `index`, `key`, `item` and the originating `event`. | ### OgeSplitterPane — `` #### Properties | Name | Type | Default | Description | | --- | --- | --- | --- | | `key` | `string \| undefined` | `—` | Stable identity used by DOM ids and by the collapse API’s string targets. | | `size` | `OgeSplitterSize \| undefined` | `—` | Initial size — a ratio number, `'40%'` or `'240px'`. Panes without one split whatever the sized ones leave. | | `minSize` | `OgeSplitterSize \| undefined` | `—` | Smallest size a resize may leave this pane at. A pixel value also becomes the grid track’s floor. | | `maxSize` | `OgeSplitterSize \| undefined` | `—` | Largest size a resize may grow this pane to. | | `collapsible` | `boolean` | `false` | Allows the pane to be collapsed from its separator — Enter, the grip, or a double click. | | `collapsed` | `boolean` | `false` | Collapsed state — two-way. Writes run the splitter’s pipeline, so a vetoed change reverts the binding. | | `collapsedSize` | `OgeSplitterSize \| undefined` | `0` | Size the pane keeps while collapsed. | | `resizable` | `boolean` | `true` | Pins the pane — both of its separators refuse to drag and report `aria-disabled`. | | `scrollable` | `boolean` | `true` | Clips overflowing content instead of scrolling it when `false`. | | `disabled` | `boolean` | `false` | Disabled panes cannot be collapsed and their separators are inert. | | `visible` | `boolean` | `true` | Removes the pane entirely when `false`. | | `text` | `string \| undefined` | `—` | Plain-text body, rendered when the pane has no projected content. | | `htmlAttributes` | `Readonly> \| undefined` | `—` | Extra attributes on the pane element. Keys removed from the bag are removed from the DOM, so clearing it clears the element. | | `cssClass` | `string \| undefined` | `—` | Extra class on the pane element. | #### Methods | Name | Type | Description | | --- | --- | --- | | `collapse()` | `() => void` | Collapses this pane, subject to the splitter’s pipeline. | | `expand()` | `() => void` | Expands this pane, subject to the splitter’s pipeline. | | `toggle()` | `() => void` | Collapses the pane if expanded, expands it otherwise. | #### Events | Name | Type | Description | | --- | --- | --- | | `collapsedChange` | `boolean` | Two-way model output of `collapsed`. | #### Types _Directives_ | Name | Type | Description | | --- | --- | --- | | `OgeSplitterPaneTemplate` | `[ogeSplitterPaneTemplate]` | Structural directive rendering the body of every data-driven `panes` entry. Declarative children use their projected content instead. | ### Splitter configuration #### Properties | Name | Type | Default | Description | | --- | --- | --- | --- | | `provideOgeSplitterConfig(config)` | `(config: OgeSplitterConfigInput) => Provider` | `—` | Application- or component-scoped defaults. `messages` is shallow-merged over the built-in strings. | | `separatorSize` | `number \| undefined` | `6` | Default for the `separatorSize` input. | | `step` | `number \| undefined` | `5` | Default for the `step` input. | | `showCollapseGrips` | `boolean \| undefined` | `true` | Default for the `showCollapseGrips` input. | | `messages` | `OgeSplitterMessages` | `—` | Every user-facing string: `separator` (with `{{first}}` / `{{second}}` placeholders), `collapsed`, `collapsePane`, `expandPane`, `noData`. | #### Types _Types_ | Name | Type | Description | | --- | --- | --- | | `OgeSplitterConfig` | `interface` | Shape held by `OGE_SPLITTER_CONFIG`: `messages` plus the optional input defaults. | | `OgeSplitterConfigInput` | `interface` | Argument of `provideOgeSplitterConfig()` — every field optional, `messages` partial. | | `OgeSplitterMessages` | `interface` | Every user-facing string in the splitter, including the separators’ accessible names. | | `OGE_SPLITTER_CONFIG` | `InjectionToken` | The token itself — inject it to read the effective defaults. | | `OGE_DEFAULT_SPLITTER_CONFIG` | `OgeSplitterConfig` | The built-in defaults, exported for composition. | | `OGE_DEFAULT_SPLITTER_MESSAGES` | `OgeSplitterMessages` | The built-in English strings. | ### OgeToolbar — `` #### Properties _Items_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `items` | `readonly OgeToolbarItemData[] \| undefined` | `—` | Data-driven entries, rendered after the declarative `` children. | | `dataSource` | `DataSource \| undefined` | `—` | Remote command list, loaded through `@oge-ui/core`’s `DataSource` contract and merged after `items`. A source that publishes `changes` triggers a reload. | | `showText` | `'always' \| 'onBar' \| 'inMenu' \| 'never'` | `'always'` | Default for every item’s `showText`: both places, the bar only, the menu only, or neither. An item that renders icon-only keeps its `text` as the accessible name. | | `showIcon` | `'always' \| 'onBar' \| 'inMenu' \| 'never'` | `'always'` | Default for every item’s `showIcon`. It resolves separately for the bar and the overflow menu, so a collapsed command keeps its icon on its menu row unless you say `'onBar'`. | _Layout & overflow_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `overflow` | `'menu' \| 'scroll' \| 'wrap' \| 'extended' \| 'none'` | `'menu'` | `menu` collapses what does not fit into an overflow menu, `scroll` keeps one line and adds scroll buttons, `wrap` flows onto more lines (the reference `multiline` mode), `extended` hides the remainder in a second row behind a toggle, `none` lets the row overflow. | | `scrollStep` | `number` | `120` | Pixels a scroll button moves the row in `overflow: 'scroll'`. | | `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Main axis. Drives the arrow keys and `aria-orientation` (written only when vertical, since horizontal is the ARIA default). | | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Density preset. Falls back to `provideOgeToolbarConfig({ size })`. | | `stylingMode` | `'outlined' \| 'filled' \| 'flat'` | `'outlined'` | Container chrome. Falls back to `provideOgeToolbarConfig({ stylingMode })`. | _State & accessibility_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `disabled` | `boolean` | `false` | Disables every item and takes the whole toolbar out of the Tab sequence. | | `wrap` | `boolean` | `true` | Whether arrow navigation wraps around the ends — optional in the APG toolbar pattern, on by default here. | | `keyboardNavigation` | `boolean` | `true` | Turns arrow/Home/End handling off entirely. The controls then keep their natural Tab order instead of a roving tabindex. | | `itemHoldTimeout` | `number` | `750` | Milliseconds a pointer must rest on an item before `itemHold` fires. | | `ariaLabel` | `string \| undefined` | `—` | Accessible name of the toolbar; falls back to `messages.toolbar`. | | `ariaLabelledBy` | `string \| undefined` | `—` | Id of a visible label. Wins over `ariaLabel`, which is then omitted. | | `messages` | `Partial \| undefined` | `—` | Per-instance overrides of the config strings (`toolbar`, `overflowMenu`, `noData`). | _Template slots_ | Name | Type | Description | | --- | --- | --- | | `ogeToolbarBefore` | `attribute slot` | Projects any control into the leading group. Slot content always stays on the bar — the toolbar cannot re-stamp DOM it does not own. | | `ogeToolbarCenter` | `attribute slot` | Projects any control into the centre group. | | `ogeToolbarAfter` | `attribute slot` | Projects any control into the trailing group. | | `ogeToolbarItemTemplate` | `TemplateRef` | Replaces the default rendering of every `items` entry — the curated stand-in for the reference libraries’ string-keyed `widget` + `options` bag. Declared inside an `` it renders that one item instead, and stays re-stampable into the menu. | | `ogeToolbarMenuItemTemplate` | `TemplateRef` | Replaces the default rendering of an item inside the overflow menu (the reference `menuItemTemplate`). | #### Methods _Methods_ | Name | Type | Description | | --- | --- | --- | | `focus()` | `(): void` | Focuses the toolbar’s current roving-tabindex stop. | | `openMenu()` | `(event?: Event): void` | Opens the overflow menu. Runs the `menuOpening` pipeline, so it can be vetoed. | | `closeMenu()` | `(reason?: OgeToolbarMenuCloseReason): void` | Closes the overflow menu, subject to `menuClosing`. Defaults to reason `'api'`. | | `toggleMenu()` | `(event?: Event): void` | Opens the menu when closed, closes it otherwise — the reference `toggle()` method. | | `toggleExtendedRow()` | `(): void` | Shows or hides the second row of `overflow: 'extended'`. | | `refreshOverflow()` | `(): void` | Drops the measurement cache and re-measures. Signal changes and container resizes already do this — call it after something the toolbar cannot observe changed a control’s size (a late web font, a stylesheet swap). | | `addItem()` | `(item: OgeToolbarItemData): void` | Appends a runtime entry, merged after `items`. `items` stays the declared source of truth, so a re-supplied array does not drop it. | | `removeItem()` | `(key: string): void` | Drops an entry added by `addItem()`, or hides an `items` entry. | | `hideItem()` | `(key: string, hidden?: boolean): void` | Hides (or re-shows) an entry without touching the `items` array. | | `enableItem()` | `(key: string, enabled?: boolean): void` | Enables (or disables) an entry without touching the `items` array. | | `clearItemOverrides()` | `(): void` | Drops every `hideItem()` / `enableItem()` override. | #### Events _Events_ | Name | Type | Description | | --- | --- | --- | | `itemClick` | `OgeToolbarItemClickEvent` | An item was activated on the bar or from the menu. Payload: `index`, `key`, `item`, `inMenu`, `event`. | | `menuOpening` | `OgeToolbarMenuOpeningEvent` | Cancelable — set `cancel` to keep the overflow menu closed. | | `menuOpened` | `void` | The overflow menu opened. | | `menuClosing` | `OgeToolbarMenuClosingEvent` | Cancelable — set `cancel` to keep the overflow menu open. Carries the close `reason`. | | `menuClosed` | `OgeToolbarMenuClosedEvent` | The overflow menu closed, with its reason. | | `overflowChanged` | `OgeToolbarOverflowChangedEvent` | The set of items living in the overflow menu changed. Payload: `keys`, `count`. | | `activeChanged` | `OgeToolbarItemActiveChangedEvent` | A toggle item’s pressed state changed. `items` entries are data the toolbar must not mutate, so this is how the new value reaches the app; a declarative child also writes its two-way `active` model. | | `itemHold` | `OgeToolbarItemHoldEvent` | An item was held for `itemHoldTimeout` — touch long-press or mouse hold. | | `itemContextMenu` | `OgeToolbarItemHoldEvent` | An item was right-clicked or long-pressed. | #### Types _Types_ | Name | Type | Description | | --- | --- | --- | | `OgeToolbarItemData` | `interface` | One data-driven entry: `key`, `type`, `text`, `icon`, `suffixIcon`, `iconClass`, `suffixIconClass`, `hint`, `width`, `htmlAttributes`, `location`, `locateInMenu`, `overflowPriority`, `showText`, `showIcon`, `disabled`, `visible`, `cssClass`, `severity`, `active`, `data`. | | `OgeToolbarItemType` | `'button' \| 'separator' \| 'spacer' \| 'label'` | What the toolbar renders for an item it owns. | | `OgeToolbarItemLocation` | `'before' \| 'center' \| 'after'` | Which of the three groups an item belongs to. | | `OgeToolbarLocateInMenu` | `'auto' \| 'always' \| 'never'` | Whether an item may move into the overflow menu. Structurally core’s `OgeToolbarOverflowPolicy`, which `fitToolbarItems()` consumes. | | `OgeToolbarDisplayMode` | `'always' \| 'onBar' \| 'inMenu' \| 'never'` | Where an item’s text or icon is rendered: both places, the bar only, the menu only, or neither. | | `OgeToolbarItemSeverity` | `'default' \| 'accent' \| 'danger'` | Emphasis of an item the toolbar renders itself. | | `OgeToolbarOverflow` | `'menu' \| 'scroll' \| 'wrap' \| 'extended' \| 'none'` | How the toolbar reacts to more items than room. | | `OgeToolbarOrientation` | `'horizontal' \| 'vertical'` | Main axis of the toolbar. | | `OgeToolbarSize` | `'sm' \| 'md' \| 'lg'` | Density preset. | | `OgeToolbarStylingMode` | `'outlined' \| 'filled' \| 'flat'` | Container chrome preset. | | `OgeToolbarMenuCloseReason` | `'api' \| 'outside' \| 'escape' \| 'select' \| 'tab'` | Why the overflow menu closed — the overlay package’s canonical reason set. | | `OgeToolbarItemClickEvent` | `interface` | `index`, `key?`, `item?`, `inMenu`, `event`. | | `OgeToolbarOverflowChangedEvent` | `interface` | `keys`, `count`. | | `OgeToolbarItemActiveChangedEvent` | `interface` | `index`, `key?`, `item?`, `active`, `event`. | | `OgeToolbarItemHoldEvent` | `interface` | Payload of `itemHold` and `itemContextMenu`: `index`, `key?`, `item?`, `event`. | | `OgeToolbarMenuOpeningEvent` | `interface` | `cancel`, `event?`. | | `OgeToolbarMenuClosingEvent` | `interface` | `cancel`, `reason`. | | `OgeToolbarMenuClosedEvent` | `interface` | `reason`. | | `OgeToolbarItemTemplateContext` | `interface` | `$implicit` (the `items` entry, `undefined` for declarative children), `index`, `inMenu`. | | `fitToolbarItems` | `(options: OgeToolbarFitOptions) => OgeToolbarFitResult` | The framework-free fitting math in `@oge-ui/core`: which items fit and which collapse. Pure arithmetic, so it is unit-testable without a DOM — the component only feeds it measurements. | ### OgeToolbarItem — `` #### Properties _Inputs_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `key` | `string \| undefined` | `—` | Stable identity echoed on `itemClick` and used for DOM ids. | | `type` | `'button' \| 'separator' \| 'spacer' \| 'label'` | `'button'` | What the toolbar renders when the item has no inline template. | | `text` | `string \| undefined` | `—` | Label; also the accessible name when the item renders icon-only. | | `icon` | `string \| undefined` | `—` | SVG path data (`d`) for a leading aria-hidden 16×16 icon. | | `suffixIcon` | `string \| undefined` | `—` | SVG path data (`d`) for a trailing icon, rendered after the text. | | `iconClass` | `string \| undefined` | `—` | Class(es) for a leading icon rendered as an empty `_` — the hook for an icon font the application already ships. `icon` stays the dependency-free default. | | `suffixIconClass` | `string \| undefined` | `—` | Class(es) for a trailing icon element. | | `width` | `number \| string \| undefined` | `—` | Fixed main-axis size of the item — a bare number is pixels. | | `htmlAttributes` | `Readonly> \| undefined` | `—` | Extra attributes on the item element. Keys removed from the bag are removed from the DOM, so clearing it clears the element. | | `hint` | `string \| undefined` | `—` | Tooltip — the native `title` attribute. | | `location` | `'before' \| 'center' \| 'after'` | `'before'` | Which of the toolbar’s three groups the item joins. | | `locateInMenu` | `'auto' \| 'always' \| 'never'` | `'auto'` | Whether the item may move into the overflow menu. The default diverges from the reference `never` on purpose — collapsing is the point. | | `overflowPriority` | `number \| undefined` | `0` | How hard the item holds its place on the bar; higher survives longer. The default makes the trailing item yield first, as in every reference toolbar. Raise it to keep a primary command visible without moving it to the front of the bar. | | `showText` | `'always' \| 'onBar' \| 'inMenu' \| 'never' \| undefined` | `—` | Overrides the toolbar’s `showText`. | | `showIcon` | `'always' \| 'onBar' \| 'inMenu' \| 'never' \| undefined` | `—` | Overrides the toolbar’s `showIcon`. | | `disabled` | `boolean` | `false` | Not clickable, and skipped by the toolbar’s arrow navigation. | | `visible` | `boolean` | `true` | `false` removes the item entirely. | | `cssClass` | `string \| undefined` | `—` | Extra class on the item element. | | `severity` | `'default' \| 'accent' \| 'danger'` | `'default'` | Emphasis of a toolbar-rendered button. | | `data` | `unknown` | `—` | Arbitrary payload echoed back on `itemClick` — the declarative counterpart of the same field on an `[items]` entry. | | `active` | `boolean \| undefined (two-way)` | `—` | Toggle-button state. Defining it is what makes the item a toggle: it renders `aria-pressed` on the bar and a checkmark in the menu, and every activation flips the value. | #### Events _Events_ | Name | Type | Description | | --- | --- | --- | | `itemClick` | `OgeToolbarItemClickEvent` | This item was activated, on the bar or from the overflow menu. Saves the `index` lookup the toolbar-level event needs. | | `activeChanged` | `OgeToolbarItemActiveChangedEvent` | This toggle item’s pressed state changed. | ### Toolbar configuration #### Properties _provideOgeToolbarConfig()_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `size` | `'sm' \| 'md' \| 'lg' \| undefined` | `—` | Default for every toolbar’s `size`. | | `stylingMode` | `'outlined' \| 'filled' \| 'flat' \| undefined` | `—` | Default for every toolbar’s `stylingMode`. | | `messages.toolbar` | `string` | `'Toolbar'` | Accessible name used when neither `ariaLabel` nor `ariaLabelledBy` is set. | | `messages.overflowMenu` | `string` | `'More commands'` | Accessible name and tooltip of the overflow button, and the menu’s label. | | `messages.moreCommands` | `string` | `'Show more commands'` | Accessible name of the `overflow: 'extended'` second-row toggle. | | `messages.scrollBackward` | `string` | `'Scroll backward'` | Accessible name of the back scroll button in `overflow: 'scroll'`. | | `messages.scrollForward` | `string` | `'Scroll forward'` | Accessible name of the forward scroll button. | | `messages.noData` | `string` | `'No commands to display'` | Shown when the toolbar has no items of its own and nothing is projected into a slot (the reference `noDataText`). | ### Demos Each demo below is a complete standalone component — copy one whole and it compiles. #### Actions ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeCard, OgeCardActions } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeCard, OgeCardActions], changeDetection: ChangeDetectionStrategy.OnPush, template: `

Unsaved changes.

`, }) export class Demo { protected discard(): void {} protected save(): void {} } ``` #### Basic ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeCard, OgeCardActions } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeCard, OgeCardActions], changeDetection: ChangeDetectionStrategy.OnPush, template: `

Four days above the tree line, one pass a day.

`, }) export class Demo { protected share(): void {} } ``` #### Clickable ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeCard } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeCard], changeDetection: ChangeDetectionStrategy.OnPush, template: `

Four days above the tree line.

Read the full report
`, }) export class Demo {} ``` #### Config ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeCard, provideOgeCardConfig } from '@oge-ui/layout'; // Application- or component-scoped defaults. There is no messages // block: the card renders no user-facing strings of its own. export const cardProviders = [ provideOgeCardConfig({ stylingMode: 'raised' }), ]; @Component({ selector: 'demo-root', imports: [OgeCard], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo {} ``` #### Footer ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeCard, OgeCardFooter, OgeCardSeparator } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeCard, OgeCardFooter, OgeCardSeparator], changeDetection: ChangeDetectionStrategy.OnPush, template: `

Generated from last week's data.


12 pages, 4 charts.

Updated 2 hours ago
`, }) export class Demo {} ``` #### Header ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeCard, OgeCardAvatar, OgeCardHeaderActions } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeCard, OgeCardAvatar, OgeCardHeaderActions], changeDetection: ChangeDetectionStrategy.OnPush, template: `

Reached the ridge before the weather turned.

`, }) export class Demo { protected menu(): void {} } ``` #### Horizontal ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeCard, OgeCardMedia } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeCard, OgeCardMedia], changeDetection: ChangeDetectionStrategy.OnPush, template: `

Vertical is the default; Kendo is the only reference with an orientation input at all.

`, }) export class Demo {} ``` #### Media ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeCard, OgeCardMedia } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeCard, OgeCardMedia], changeDetection: ChangeDetectionStrategy.OnPush, template: `

The heading stays before the media in DOM order, so a screen reader announces the card by its title first.

`, }) export class Demo {} ``` #### Modes ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeCard } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeCard], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo {} ``` #### Size ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeCard } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeCard], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo {} ``` #### States ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeCard, OgeCardActions } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeCard, OgeCardActions], changeDetection: ChangeDetectionStrategy.OnPush, template: `

The e2e stage timed out after 20 minutes.

`, }) export class Demo { protected readonly pending = signal(true); protected retry(): void {} } ``` #### Actions ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeAccordion, OgeAccordionHeaderActionsTemplate, OgeAccordionItem } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeAccordion, OgeAccordionHeaderActionsTemplate, OgeAccordionItem], changeDetection: ChangeDetectionStrategy.OnPush, template: ` Members… `, }) export class Demo { protected remove(index: number): void { console.log('remove section', index); } } ``` #### Basic ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeAccordion, OgeAccordionItem } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeAccordion, OgeAccordionItem], changeDetection: ChangeDetectionStrategy.OnPush, template: ` Account settings… Notification settings… Never reachable… `, }) export class Demo { protected readonly index = signal(0); } ``` #### Guard ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeAccordion } from '@oge-ui/layout'; import type { OgeAccordionItemData } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeAccordion], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { // resolve(false) vetoes, rejection vetoes too; while the promise is // pending the header shows a spinner and ignores further clicks protected readonly guarded: OgeAccordionItemData[] = [ { key: 'plain', title: 'Opens right away' }, { key: 'slow', title: 'Confirms first', expandGuard: () => this.confirm() }, ]; private confirm(): boolean { return confirm('Open this section?'); } } ``` #### Invalid ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeAccordion } from '@oge-ui/layout'; import type { OgeAccordionItemData } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeAccordion], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { // flag the sections your form group reports as invalid protected readonly formSections: OgeAccordionItemData[] = [ { key: 'contact', title: 'Contact' }, { key: 'billing', title: 'Billing', invalid: true }, { key: 'shipping', title: 'Shipping', invalid: true }, ]; } ``` #### Items ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeAccordion, OgeAccordionContentTemplate } from '@oge-ui/layout'; import type { OgeAccordionItemData } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeAccordion, OgeAccordionContentTemplate], changeDetection: ChangeDetectionStrategy.OnPush, template: ` Body of {{ item.title }} `, }) export class Demo { protected readonly sections: OgeAccordionItemData[] = [ { key: 'general', title: 'General', description: 'Language and time zone' }, { key: 'security', title: 'Security', badge: 2 }, { key: 'danger', title: 'Danger zone' }, ]; protected readonly open = signal(['general']); } ``` #### Lazy ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeAccordion, OgeAccordionContentTemplate, OgeAccordionItem } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeAccordion, OgeAccordionContentTemplate, OgeAccordionItem], changeDetection: ChangeDetectionStrategy.OnPush, template: ` Created at {{ stamp() }} `, }) export class Demo { protected readonly keepAlive = signal(true); protected stamp(): string { return new Date().toLocaleTimeString(); } } ``` #### Loader ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeAccordion, OgeAccordionContentTemplate, OgeAccordionItem } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeAccordion, OgeAccordionContentTemplate, OgeAccordionItem], changeDetection: ChangeDetectionStrategy.OnPush, template: ` {{ data }} `, }) export class Demo { // a skeleton shows while pending; a rejection renders a retry button protected readonly loadInvoices = () => new Promise((resolve) => setTimeout(() => resolve('42 invoices'), 900), ); } ``` #### Mode ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeAccordion } from '@oge-ui/layout'; import type { OgeAccordionItemData } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeAccordion], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly multiple = signal(true); protected readonly collapsible = signal(true); protected readonly items: OgeAccordionItemData[] = [ { key: 'a', title: 'First' }, { key: 'b', title: 'Second' }, ]; } ``` #### Panel ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeAccordion, OgeAccordionActionRow, OgeAccordionItem } from '@oge-ui/layout'; import type { OgeAccordionExpandedEvent } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeAccordion, OgeAccordionActionRow, OgeAccordionItem], changeDetection: ChangeDetectionStrategy.OnPush, template: `

…fields…

`, }) export class Demo { protected readonly profileOpen = signal(true); protected onSettled(event: OgeAccordionExpandedEvent): void { console.log('settled', event.key); } protected save(): void { console.log('saved'); } } ``` #### Styling ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeAccordion } from '@oge-ui/layout'; import type { OgeAccordionItemData } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeAccordion], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly items: OgeAccordionItemData[] = [ { key: 'a', title: 'First' }, { key: 'b', title: 'Second' }, ]; } ``` #### Async ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeProgressBar, OgeSkeleton } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeProgressBar, OgeSkeleton], changeDetection: ChangeDetectionStrategy.OnPush, template: ` @if (total() === null) { } @else { } `, }) export class Demo { protected readonly total = signal(null); protected readonly received = signal(0); protected readonly done = signal(false); } ``` #### Chunk ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeProgressBar } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeProgressBar], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly step = signal(3); } ``` #### Determinate ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeProgressBar } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeProgressBar], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly uploaded = signal(80); protected readonly asMegabytes = (value: number): string => `${value} MB`; } ``` #### Indeterminate ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeProgressBar } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeProgressBar], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly played = signal(35); protected readonly buffered = signal(70); } ``` #### Load indicator ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeLoadIndicator } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeLoadIndicator], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo {} ``` #### Skeleton ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeSkeleton } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeSkeleton], changeDetection: ChangeDetectionStrategy.OnPush, template: `
`, }) export class Demo { protected readonly loading = signal(true); } ``` #### Basic ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeSplitter, OgeSplitterPane } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeSplitter, OgeSplitterPane], changeDetection: ChangeDetectionStrategy.OnPush, template: ` Result list… Detail view… `, }) export class Demo { // Sizes are ratios, not percentages — [30, 30] lays out like [50, 50], // so a configuration that does not add up to 100 is never an error. protected readonly sizes = signal([35, 65]); } ``` #### Collapse ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeSplitter, OgeSplitterPane } from '@oge-ui/layout'; import type { OgeSplitterPaneCollapsedEvent } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeSplitter, OgeSplitterPane], changeDetection: ChangeDetectionStrategy.OnPush, template: ` Navigation… Editor… `, }) export class Demo { protected readonly sideCollapsed = signal(false); protected onCollapsed(event: OgeSplitterPaneCollapsedEvent): void { console.log('collapsed', event.key); } } ``` #### Config ```ts import { provideOgeSplitterConfig } from '@oge-ui/layout'; export const appConfig: ApplicationConfig = { providers: [ provideOgeSplitterConfig({ separatorSize: 8, step: 10, messages: { separator: '{{first}} ile {{second}} arasını yeniden boyutlandır', collapsePane: 'Paneli daralt', expandPane: 'Paneli aç', }, }), ], }; ``` #### Events ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeSplitter, OgeSplitterPane } from '@oge-ui/layout'; import type { OgeSplitterPaneCollapsingEvent, OgeSplitterResizeEvent } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeSplitter, OgeSplitterPane], changeDetection: ChangeDetectionStrategy.OnPush, template: ` A B `, }) export class Demo { protected readonly locked = signal(true); protected onResized(event: OgeSplitterResizeEvent): void { console.log(event.sizes, event.previousSizes); } // paneCollapsing / paneExpanding are cancelable — set cancel to veto. protected onCollapsing(event: OgeSplitterPaneCollapsingEvent): void { if (this.locked()) event.cancel = true; } protected log(phase: string): void { console.log(phase); } } ``` #### Fixed ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeSplitter, OgeSplitterPane } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeSplitter, OgeSplitterPane], changeDetection: ChangeDetectionStrategy.OnPush, template: ` Fixed sidebar — dragged in pixels Fluid content `, }) export class Demo {} ``` #### Form ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeForm } from '@oge-ui/forms'; import { OgeSplitter, OgeSplitterPane } from '@oge-ui/layout'; import type { OgeFormItemData } from '@oge-ui/forms'; @Component({ selector: 'demo-root', imports: [OgeForm, OgeSplitter, OgeSplitterPane], changeDetection: ChangeDetectionStrategy.OnPush, template: ` Preview… `, }) export class Demo { protected readonly server = signal({ host: '', port: 5432, user: '' }); protected readonly fields: OgeFormItemData[] = [ { field: 'host', label: 'Host' }, { field: 'port', label: 'Port' }, { field: 'user', label: 'User' }, ]; } ``` #### Keyboard ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeSplitter, OgeSplitterPane } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeSplitter, OgeSplitterPane], changeDetection: ChangeDetectionStrategy.OnPush, template: ` Primary Secondary `, }) export class Demo {} ``` #### Nested ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeSplitter, OgeSplitterPane } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeSplitter, OgeSplitterPane], changeDetection: ChangeDetectionStrategy.OnPush, template: ` Sidebar Editor Terminal `, }) export class Demo {} ``` #### Orientation ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeSplitter, OgeSplitterPane } from '@oge-ui/layout'; import type { OgeSplitterOrientation } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeSplitter, OgeSplitterPane], changeDetection: ChangeDetectionStrategy.OnPush, template: ` Top / left Bottom / right `, }) export class Demo { protected readonly orientation = signal('vertical'); } ``` #### Panes ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeSplitter, OgeSplitterPaneTemplate } from '@oge-ui/layout'; import type { OgeSplitterPaneData } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeSplitter, OgeSplitterPaneTemplate], changeDetection: ChangeDetectionStrategy.OnPush, template: `

{{ index }} — {{ pane.key }}

`, }) export class Demo { protected readonly areas: OgeSplitterPaneData[] = [ { key: 'explorer', size: 25, minSize: 15, collapsible: true }, { key: 'editor', size: 50 }, { key: 'inspector', size: 25, minSize: 15 }, ]; } ``` #### Persist ```ts import { ChangeDetectionStrategy, Component, effect, signal } from '@angular/core'; import { OgeSplitter, OgeSplitterPane } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeSplitter, OgeSplitterPane], changeDetection: ChangeDetectionStrategy.OnPush, template: ` Left Right `, }) export class Demo { // [(sizes)] is the whole persistable state, so there is no stateKey to // learn and no storage token to provide — save it wherever you like. protected readonly sizes = signal<(number | string)[]>( JSON.parse(localStorage.getItem('editor-layout') ?? 'null') ?? [30, 70], ); constructor() { effect(() => localStorage.setItem('editor-layout', JSON.stringify(this.sizes())), ); } } ``` #### Basic ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeToolbar, OgeToolbarItem } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeToolbar, OgeToolbarItem], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected create(): void {} protected open(): void {} protected save(): void {} } ``` #### Config ```ts import { provideOgeToolbarConfig } from '@oge-ui/layout'; export const appConfig: ApplicationConfig = { providers: [ provideOgeToolbarConfig({ size: 'sm', stylingMode: 'flat', messages: { toolbar: 'Araç çubuğu', overflowMenu: 'Daha fazla komut', noData: 'Gösterilecek komut yok', }, }), ], }; ``` #### Icon ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeToolbar, OgeToolbarItem } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeToolbar, OgeToolbarItem], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { // Icons are SVG path data — there is no icon font or icon package. protected readonly boldPath = 'M5 3h4a3 3 0 0 1 0 6H5zM5 9h5a3 3 0 0 1 0 6H5z'; protected readonly italicPath = 'M10 3H6m4 0-3 10m0 0H3m4 0h3'; protected readonly underlinePath = 'M4 3v5a4 4 0 0 0 8 0V3M3 14h10'; } ``` #### Items ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeToolbar } from '@oge-ui/layout'; import type { OgeToolbarItemClickEvent, OgeToolbarItemData } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeToolbar], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { // Declarative children and [items] can be used together: children render // first, exactly like the tabs and accordion families. protected readonly tools: readonly OgeToolbarItemData[] = [ { key: 'undo', text: 'Undo' }, { key: 'redo', text: 'Redo' }, { key: 'sep', type: 'separator' }, { key: 'bold', text: 'Bold', active: true }, { key: 'note', type: 'label', text: 'Draft' }, { key: 'publish', text: 'Publish', location: 'after', severity: 'accent' }, ]; protected onTool(event: OgeToolbarItemClickEvent): void { console.log(event.key, 'clicked from the menu?', event.inMenu); } } ``` #### Keyboard ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeToolbar, OgeToolbarItem } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeToolbar, OgeToolbarItem], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo {} ``` #### Location ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeToolbar, OgeToolbarItem } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeToolbar, OgeToolbarItem], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo {} ``` #### Modes ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeToolbar, OgeToolbarItem } from '@oge-ui/layout'; import type { OgeToolbarOverflow } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeToolbar, OgeToolbarItem], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly mode = signal('extended'); } ``` #### Overflow ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeToolbar, OgeToolbarItem } from '@oge-ui/layout'; import type { OgeToolbarOverflowChangedEvent } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeToolbar, OgeToolbarItem], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected onOverflow(event: OgeToolbarOverflowChangedEvent): void { console.log(event.count, 'commands are in the menu'); } } ``` #### Priority ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeToolbar, OgeToolbarItem } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeToolbar, OgeToolbarItem], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo {} ``` #### Runtime ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeToolbar } from '@oge-ui/layout'; import type { OgeToolbarItemData } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeToolbar], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly tools: readonly OgeToolbarItemData[] = [ { key: 'cut', text: 'Cut' }, { key: 'copy', text: 'Copy' }, { key: 'paste', text: 'Paste' }, ]; } ``` #### Slots ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeToolbar, OgeToolbarItem, OgeToolbarItemTemplate } from '@oge-ui/layout'; import { OgeSelectBox } from '@oge-ui/inputs'; @Component({ selector: 'demo-root', imports: [OgeToolbar, OgeToolbarItem, OgeToolbarItemTemplate, OgeSelectBox], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly views = ['All', 'Mine', 'Archived']; protected readonly view = signal('All'); } ``` #### Toggle ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeToolbar, OgeToolbarItem } from '@oge-ui/layout'; import type { OgeToolbarItemActiveChangedEvent } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeToolbar, OgeToolbarItem], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly bold = signal(true); protected readonly italic = signal(false); protected onToggle(event: OgeToolbarItemActiveChangedEvent): void { console.log(event.key, 'is now', event.active); } } ``` ## @oge-ui/forms Form layout over the editors — responsive columns, fieldset groups, declarative validation rules and a validation summary. Docs: https://ogeui.com/components/forms ### Entry points `@oge-ui/forms` - values: `OGE_DEFAULT_FORMS_CONFIG`, `OGE_DEFAULT_FORMS_MESSAGES`, `OGE_FORMS_CONFIG`, `OGE_FORM_COL_SPAN`, `OGE_FORM_DATA_TYPE`, `OGE_FORM_EDITOR`, `OGE_FORM_EDITOR_OPTIONS`, `OGE_FORM_GROUP`, `OGE_FORM_HINT`, `OGE_FORM_LABEL`, `OGE_FORM_ORDER`, `OGE_FORM_PLACEHOLDER`, `OgeForm`, `OgeFormAccordion`, `OgeFormActions`, `OgeFormEditorTemplate`, `OgeFormGroup`, `OgeFormGroupCaptionTemplate`, `OgeFormItem`, `OgeFormItemTemplate`, `OgeFormLabelTemplate`, `OgeFormSteps`, `OgeFormTabs`, `OgeValidationSummary`, `itemFromMetadata`, `provideOgeFormsConfig` - types: `OgeFormColCount`, `OgeFormDataType`, `OgeFormEditorAppearance`, `OgeFormEditorOptions`, `OgeFormEditorType`, `OgeFormErrorEntry`, `OgeFormFieldChangedEvent`, `OgeFormFieldNode`, `OgeFormGroupCaptionTemplateContext`, `OgeFormGroupData`, `OgeFormItemData`, `OgeFormItemTemplateContext`, `OgeFormKeyEvent`, `OgeFormLabelLocation`, `OgeFormLabelTemplateContext`, `OgeFormMode`, `OgeFormScreenSize`, `OgeFormSubmittedEvent`, `OgeFormSubmittingEvent`, `OgeFormValidatedEvent`, `OgeFormsConfig`, `OgeFormsConfigInput`, `OgeFormsMessages`, `OgeResolvedFormItem`, `OgeValidationContext`, `OgeValidationRule` ### OgeForm — `` #### Properties _Binding_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `fieldTree` | `FieldTree \| undefined` | `undefined` | An Angular Signal Forms tree, as returned by `form()`. The caller owns validation, required marks, disabled and readonly state. | | `formGroup` | `FormGroup \| undefined` | `undefined` | A reactive `FormGroup`. Each item binds its matching control through the editors' control-value-accessor path. | | `formData` | `T \| undefined` | `undefined` | A plain model object — two-way. The form builds its own Signal Forms tree over it, compiling each item's `validationRules` into the schema. | | `items` | `readonly OgeFormItemData[] \| undefined` | `undefined` | Data-driven items, rendered after the projected `` children. | | `groups` | `readonly OgeFormGroupData[] \| undefined` | `undefined` | Data-driven groups, matched to items by `key` (or `caption`) through an item's `group`. | | `mode` | `Signal` | `—` | Read-only: which binding the form resolved to. Derived from the bound inputs, never configured. | _Layout_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `colCount` | `number \| 'auto'` | `'auto'` | Layout columns. `'auto'` fits as many `minColWidth` tracks as the form is wide. | | `colCountByScreen` | `Partial> \| undefined` | `undefined` | Column count per breakpoint. Implemented as container queries on the form itself, so a form in a dialog or a grid cell sizes from its own width — not the window's. | | `minColWidth` | `number` | `220` | Narrowest column `colCount: 'auto'` will produce, in pixels. | | `labelLocation` | `'top' \| 'start' \| 'end'` | `'top'` | `'top'` keeps each editor's own label chrome; the side values hand the label to the form, which draws a real `` in its own column. | | `labelMode` | `'static' \| 'floating' \| 'hidden' \| 'outside'` | `'static'` | Forwarded to every editor. Forced to `'hidden'` when `labelLocation` is a side value, so no label renders twice. | | `alignItemLabels` | `boolean` | `true` | Gives side labels one shared column width so the editors line up. | | `showColonAfterLabel` | `boolean` | `false` | Appends `messages.labelColon` to every label. | | `showRequiredMark` | `boolean` | `true` | Renders `messages.requiredMark` after a required label, `aria-hidden`, with a screen-reader-only word beside it. | | `showOptionalMark` | `boolean` | `false` | Renders `messages.optionalMark` after every non-required label. | | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Forwarded to every editor. | | `stylingMode` | `'outlined' \| 'filled' \| 'underlined'` | `'outlined'` | Forwarded to every editor. | | `subscriptSizing` | `'fixed' \| 'dynamic' \| 'none'` | `'fixed'` | Forwarded to every editor. `'fixed'` reserves the hint/error line so an appearing error never shifts the layout. | _State_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `readOnly` | `boolean` | `false` | Makes every editor read-only, overridable per group and per item. In `[fieldTree]` mode use the schema's `readonly()` instead. | | `disabled` | `boolean` | `false` | Wraps the fields in a ``. In `[fieldTree]` mode use the schema's `disabled()` — the `FormField` directive writes the editor's `disabled` input itself. | | `showValidationSummary` | `boolean` | `false` | Renders an `` above the fields once a submit has failed. | | `scrollToFirstInvalid` | `boolean` | `true` | Scrolls the first invalid field into view when a submit fails. Focus moves there either way. | | `errors` | `Signal` | `—` | Read-only: one entry per invalid field, in layout order, regardless of whether the field is showing its error yet. | | `valid` | `Signal` | `—` | Read-only: whether every bound field currently validates. | | `dirty` | `Signal` | `—` | Read-only: whether any bound field has been edited since the last reset. Works in all three binding modes. | | `messages` | `Partial \| undefined` | `undefined` | Per-instance string overrides, merged over `provideOgeFormsConfig()`. | | `renderFormElement` | `boolean` | `true` | Whether the fields are wrapped in a real ``. Set `false` inside another form — nested forms are invalid HTML; the grid's row editor does exactly this. With `false` there is no native submit, so drive it with `submit()`. | #### Methods | Name | Type | Description | | --- | --- | --- | | `submit(event?: Event)` | `Promise` | Marks every field touched, validates, emits `submitting` and then `submitted`. Resolves `false` when the form was invalid or the submit was canceled, and focuses the first invalid field. | | `validate()` | `boolean` | Re-reads validity and emits `validated`. Does not move focus. | | `reset(values?: Partial)` | `void` | Resets every field to `values`, or to the bound form's initial data, and hides the validation summary. | | `clear()` | `void` | Empties every editor using the per-`dataType` empty value (`''`, `null`, `false`, `[]`). | | `focus(field?: string)` | `void` | Focuses a named field, or the first one when called with no argument. | | `focusFirstInvalid()` | `boolean` | Focuses — and, with `scrollToFirstInvalid`, scrolls to — the first invalid field. Returns `false` when the form is valid. | | `itemOption(field: string)` | `OgeResolvedFormItem \| undefined` | The resolved configuration of one item, as the form actually renders it. Replaces the reference libraries' `getEditor()`, which hands out a component instance. | | `updateData(field: string, value: unknown)` | `void` | Writes one field. The overload `updateData(partial)` merges an object into the bound data. | #### Events | Name | Type | Description | | --- | --- | --- | | `submitting` | `OgeFormSubmittingEvent` | Cancelable pre-submit — `{ data, valid, cancel, event }`. Set `cancel` to stop the submit. | | `submitted` | `OgeFormSubmittedEvent` | Emitted after a submit passed validation and was not canceled. | | `fieldChanged` | `OgeFormFieldChangedEvent` | One field's value changed — `{ field, value, previousValue }`. | | `validated` | `OgeFormValidatedEvent` | Emitted after `validate()` or a submit attempt — `{ valid, errors }`. | | `editorEnterKey` | `OgeFormKeyEvent` | Enter pressed inside an editor — `{ field, event }`. | #### Types | Name | Type | Description | | --- | --- | --- | | `OgeFormMode` | `'fieldTree' \| 'formGroup' \| 'formData'` | Which binding the form resolved to. | | `OgeFormDataType` | `'string' \| 'number' \| 'boolean' \| 'date' \| 'datetime' \| 'dateRange' \| 'array' \| 'object'` | Value shape of an item. Inferred from the model value when not set. | | `OgeFormEditorType` | `'textBox' \| 'textArea' \| 'numberBox' \| 'selectBox' \| 'tagBox' \| 'autocomplete' \| 'treeSelect' \| 'dateBox' \| 'dateRangeBox' \| 'calendar' \| 'checkBox' \| 'switch' \| 'radioGroup'` | Which `@oge-ui/inputs` editor renders an item. House camelCase names, not the reference libraries' class names. | | `OgeFormLabelLocation` | `'top' \| 'start' \| 'end'` | Where an item's label sits relative to its editor. | | `OgeFormScreenSize` | `'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl'` | Container-query breakpoints: under 480, then 480 / 720 / 960 / 1200 pixels of form width. | | `OgeFormColCount` | `number \| 'auto'` | A fixed track count, or auto-fit by `minColWidth`. | | `OgeValidationRule` | `{ type: 'required' \| 'email' \| 'numeric' \| 'stringLength' \| 'pattern' \| 'range' \| 'custom' \| 'async'; … }` | A declarative rule. Compiled into an Angular Signal Forms schema — there is no second validation engine. | | `OgeValidationContext` | `{ value: unknown; data: Record }` | What a `custom` rule sees: its own value and the whole model, which is what makes cross-field rules possible. | | `OgeFormErrorEntry` | `{ field: string; label: string; message: string }` | One row of the validation summary. | | `OgeResolvedFormItem` | `interface` | An item after label defaulting, dataType inference, editor selection and state inheritance — what `itemOption()` returns. | ### OgeFormItem — `` #### Properties | Name | Type | Default | Description | | --- | --- | --- | --- | | `field` | `string` | `—` | Required. Model property this item edits; dot-notation reaches nested objects. | | `key` | `string \| undefined` | `undefined` | Stable identity; defaults to `field`. | | `label` | `string \| undefined` | `undefined` | Label text. Defaults to a title-cased `field` — `postalCode` becomes “Postal code”. | | `labelVisible` | `boolean` | `true` | Set `false` to render the editor with no label. | | `hint` | `string \| undefined` | `undefined` | Help text under the editor. Chrome editors render it in their own subscript; bare controls get it from the form. | | `placeholder` | `string \| undefined` | `undefined` | Placeholder forwarded to the editor. | | `dataType` | `OgeFormDataType \| undefined` | `undefined` | Value shape. Inferred from the current model value when omitted. | | `editorType` | `OgeFormEditorType \| undefined` | `undefined` | Explicit editor; beats both `editorOptions.items` and `dataType`. | | `editorOptions` | `OgeFormEditorOptions \| undefined` | `undefined` | A curated, typed subset of editor inputs (`items`, `displayExpr`, `min`, `max`, `rows`, …). Supplying `items` selects a select box or tag box. | | `colSpan` | `number` | `1` | Layout columns the item spans, clamped to the column count in force. | | `visible` | `boolean` | `true` | A hidden item is dropped from the layout entirely — no hidden input, no stale DOM value. | | `visibleIndex` | `number \| undefined` | `undefined` | Items with an index come first, in index order; everything else keeps its declaration order behind them. | | `isRequired` | `boolean` | `false` | Adds a `required` rule and shows the required mark. | | `validationRules` | `readonly OgeValidationRule[] \| undefined` | `undefined` | Declarative rules, compiled into the form's Signal Forms schema. Ignored — with a dev-mode warning — when the form is bound with `[fieldTree]` or `[formGroup]`. | | `readOnly` | `boolean \| undefined` | `undefined` | `undefined` falls back to the enclosing group, then the form. | | `disabled` | `boolean \| undefined` | `undefined` | `undefined` falls back to the enclosing group, then the form. | | `cssClass` | `string \| undefined` | `undefined` | Extra class on the item wrapper. | ### OgeFormGroup — `` #### Properties | Name | Type | Default | Description | | --- | --- | --- | --- | | `caption` | `string` | `''` | Legend text. An empty caption renders an unlabelled section. | | `key` | `string \| undefined` | `undefined` | Stable identity; defaults to the caption. | | `colCount` | `OgeFormColCount \| undefined` | `undefined` | Columns inside this group; `undefined` inherits the form's count. | | `colSpan` | `number` | `1` | Columns the group itself spans in its parent layout. | | `visible` | `boolean` | `true` | Drops the whole section, and its items, from the layout. | | `disabled` | `boolean \| undefined` | `undefined` | Disables every item in the section, unless the item overrides it. | | `readOnly` | `boolean \| undefined` | `undefined` | Makes every item in the section read-only, unless the item overrides it. | | `visibleIndex` | `number \| undefined` | `undefined` | Explicit ordering among this group's siblings. Ordering is scoped per level, the way the reference libraries scope it. | | `cssClass` | `string \| undefined` | `undefined` | Extra class on the fieldset. | ### OgeValidationSummary — `` #### Properties | Name | Type | Default | Description | | --- | --- | --- | --- | | `errors` | `readonly OgeFormErrorEntry[]` | `[]` | One entry per invalid field, in layout order. Bind `form.errors()`. | | `messages` | `Partial \| undefined` | `undefined` | Per-instance string overrides. | #### Events | Name | Type | Description | | --- | --- | --- | | `errorClick` | `OgeFormErrorEntry` | A summary row was activated. Bind it to `form.focus($event.field)`. | ### Sections #### Properties _OgeFormTabs — _ | Name | Type | Default | Description | | --- | --- | --- | --- | | `selectedIndex` | `number` | `0` | Open tab — two-way. A failed submit sets it to the tab holding the first invalid field. | | `deferRendering` | `boolean` | `false` | Deliberately the opposite of the tab panel's own default: a form usually wants every field in the DOM. Validation runs on the model either way. | | `keepAlive` | `boolean` | `true` | Keeps a rendered tab's fields mounted while it is hidden. | | `showErrorBadges` | `boolean` | `true` | Shows each tab's invalid-field count as a badge on the tab. | | `key / visible / visibleIndex / colSpan / cssClass` | `string \| boolean \| number \| undefined` | `—` | The same section-level knobs a group has. | _OgeFormSteps — _ | Name | Type | Default | Description | | --- | --- | --- | --- | | `activeIndex` | `number` | `0` | Active step — two-way. A failed submit moves to the step holding the first invalid field. | | `linear` | `boolean` | `false` | Blocks moving past a step that still has invalid fields. Completion comes from the form's own per-step error rollup, so it behaves identically in all three binding modes. | | `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Passed through to the stepper. | | `showNavigation` | `boolean` | `true` | Renders the stepper's built-in Back / Next bar. On by default here, because a wizard inside a form almost always wants one. | | `touchOnLeave` | `boolean` | `true` | Touches only the leaving step's fields on each advance, so the steps ahead stay quiet instead of turning red — which a plain `markAllAsTouched()` would cause. | | `showInvalidSections` | `boolean` | `true` | Flags a step whose fields are invalid, driving the stepper's error indicator. | | `deferRendering / keepAlive` | `boolean` | `—` | See `OgeFormTabs` — same defaults, same reasoning. | | `key / visible / visibleIndex / colSpan / cssClass` | `string \| boolean \| number \| undefined` | `—` | The same section-level knobs a group has. | _OgeFormAccordion — _ | Name | Type | Default | Description | | --- | --- | --- | --- | | `expandedKeys` | `readonly string[]` | `[]` | Expanded panels — two-way. A failed submit adds the panel holding the first invalid field. | | `multiple` | `boolean` | `true` | Whether more than one panel may be open at a time. | | `collapsible` | `boolean` | `true` | Whether the open panel may be closed again. | | `showInvalidSections` | `boolean` | `true` | Drives the accordion's own invalid indicator — danger rail, dot and screen-reader label — from the panel's field errors. | | `deferRendering / keepAlive` | `boolean` | `—` | As on ``. | ### Template slots #### Properties _Slots_ | Name | Type | Description | | --- | --- | --- | | `ogeFormItemTemplate` | `directive — [ogeFormItemTemplate]` | Replaces a field entirely: label, editor and error text. Legal at form level (every item) or inside one ``, where it wins. | | `ogeFormEditorTemplate` | `directive — [ogeFormEditorTemplate]` | Replaces only the control, keeping the form's label, required mark and error chrome. The escape hatch for anything `editorOptions` cannot express. | | `ogeFormLabelTemplate` | `directive — [ogeFormLabelTemplate]` | Replaces the label content. The surrounding `` stays, so the control association and the required mark survive. | | `ogeFormGroupCaptionTemplate` | `directive — [ogeFormGroupCaptionTemplate]` | Replaces the content of a group's ``. The fieldset/legend pair itself stays — that is what makes the section a labelled group. | | `ogeFormActions` | `directive — [ogeFormActions]` | Marks the projected action bar (submit, reset, …). A typed marker rather than a bare attribute. | #### Types | Name | Type | Description | | --- | --- | --- | | `OgeFormItemTemplateContext` | `{ $implicit: OgeResolvedFormItem; item; field; control; error; editorId }` | Context of the item and editor slots. `field` is the Signal Forms node (bind with `[formField]`), `control` the reactive one, and `editorId` the id the form's `` points at. | | `OgeFormLabelTemplateContext` | `{ $implicit: string; item; required; editorId }` | Context of the label slot; `$implicit` is the resolved label text. | | `OgeFormGroupCaptionTemplateContext` | `{ $implicit: string }` | Context of the group caption slot. | ### Schema metadata #### Properties _Schema-carried layout_ | Name | Type | Description | | --- | --- | --- | | `OGE_FORM_LABEL` | `MetadataKey` | Label text, set with `metadata(path, OGE_FORM_LABEL, () => '…')` in a Signal Forms schema. | | `OGE_FORM_HINT` | `MetadataKey` | Help text under the editor. | | `OGE_FORM_PLACEHOLDER` | `MetadataKey` | Editor placeholder. | | `OGE_FORM_COL_SPAN` | `MetadataKey` | Layout columns the field spans. | | `OGE_FORM_EDITOR` | `MetadataKey` | Explicit editor for the field. | | `OGE_FORM_EDITOR_OPTIONS` | `MetadataKey` | Curated editor inputs. | | `OGE_FORM_DATA_TYPE` | `MetadataKey` | Value shape, when the live value is not descriptive enough. | | `OGE_FORM_GROUP` | `MetadataKey` | Caption of the group the field belongs to; the group is created on demand. | | `OGE_FORM_ORDER` | `MetadataKey` | Ordering hint, equivalent to an item's `visibleIndex`. | | `itemFromMetadata(field, node)` | `OgeFormItemData \| undefined` | Builds one item description from a field's metadata; `undefined` for a field the schema hid with `hidden()`. | ### Forms configuration #### Properties _provideOgeFormsConfig()_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `labelLocation` | `OgeFormLabelLocation \| undefined` | `'top'` | Application-wide default for the input of the same name. | | `minColWidth` | `number \| undefined` | `220` | Application-wide default for the input of the same name. | | `showRequiredMark` | `boolean \| undefined` | `true` | Application-wide default for the input of the same name. | | `showOptionalMark` | `boolean \| undefined` | `false` | Application-wide default for the input of the same name. | | `showColonAfterLabel` | `boolean \| undefined` | `false` | Application-wide default for the input of the same name. | _OgeFormsMessages_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `requiredMark` | `string` | `'*'` | Marker after a required label; rendered aria-hidden. | | `optionalMark` | `string` | `'optional'` | Marker after an optional label. | | `requiredLabel` | `string` | `'required'` | Screen-reader text beside the required mark. | | `optionalLabel` | `string` | `'optional'` | Screen-reader text beside the optional mark. | | `labelColon` | `string` | `':'` | Separator drawn when `showColonAfterLabel`. | | `validationSummaryTitle` | `string` | `'{count} fields need your attention'` | Summary heading; `{count}` is interpolated. | | `validationSummaryTitleOne` | `string` | `'1 field needs your attention'` | Summary heading when exactly one field is invalid. | | `validationSummaryLabel` | `string` | `'Validation summary'` | Accessible label of the summary region. | | `invalidError` | `string` | `'This value is invalid'` | Fallback text for an error with no resolvable message. | | `submitButton` | `string` | `'Submit'` | Label of the built-in submit button. | | `resetButton` | `string` | `'Reset'` | Label of the built-in reset button. | | `submitting` | `string` | `'Submitting…'` | Announced while an async submit handler is in flight. | | `noItems` | `string` | `'No fields to display'` | Shown when no visible item resolves. | ### Demos Each demo below is a complete standalone component — copy one whole and it compiles. #### Accordion section ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeForm, OgeFormAccordion, OgeFormGroup, OgeFormItem } from '@oge-ui/forms'; @Component({ selector: 'demo-root', imports: [OgeForm, OgeFormAccordion, OgeFormGroup, OgeFormItem], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly employee = signal({ firstName: 'Ada', lastName: 'Lovelace', title: '', salary: 120000, }); } ``` #### Auto ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeForm } from '@oge-ui/forms'; import type { OgeFormItemData } from '@oge-ui/forms'; @Component({ selector: 'demo-root', imports: [OgeForm], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly server = signal({ host: 'db.internal', port: 5432, user: 'postgres', database: 'oge', }); protected readonly fields: OgeFormItemData[] = [ { field: 'host', label: 'Host' }, { field: 'port', label: 'Port' }, { field: 'user', label: 'User' }, { field: 'database', label: 'Database' }, ]; } ``` #### Breakpoint ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeForm } from '@oge-ui/forms'; import type { OgeFormItemData } from '@oge-ui/forms'; @Component({ selector: 'demo-root', imports: [OgeForm], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly server = signal({ host: 'db.internal', port: 5432, user: 'postgres', database: 'oge', }); protected readonly fields: OgeFormItemData[] = [ { field: 'host', label: 'Host' }, { field: 'port', label: 'Port' }, { field: 'user', label: 'User' }, { field: 'database', label: 'Database' }, ]; } ``` #### Colcount ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeForm, OgeFormItem } from '@oge-ui/forms'; @Component({ selector: 'demo-root', imports: [OgeForm, OgeFormItem], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly columns = signal(3); protected readonly record = signal({ code: 'OGE-1', name: 'Form layout', owner: 'Ada', summary: '', }); } ``` #### Nested ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeForm, OgeFormGroup, OgeFormItem } from '@oge-ui/forms'; @Component({ selector: 'demo-root', imports: [OgeForm, OgeFormGroup, OgeFormItem], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly company = signal({ name: 'OGE UI', taxId: '', employees: 12, street: '', city: '', postalCode: '', }); } ``` #### Readonly ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeForm, OgeFormItem } from '@oge-ui/forms'; @Component({ selector: 'demo-root', imports: [OgeForm, OgeFormItem], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly locked = signal(true); protected readonly archived = signal(false); protected readonly invoice = signal({ number: 'INV-204', total: 1290, comment: '', }); } ``` #### Sections ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeForm, OgeFormGroup, OgeFormItem, OgeFormTabs } from '@oge-ui/forms'; @Component({ selector: 'demo-root', imports: [OgeForm, OgeFormGroup, OgeFormItem, OgeFormTabs], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly employee = signal({ firstName: 'Ada', lastName: 'Lovelace', title: '', salary: 120000, }); } ``` #### Visibility ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeForm, OgeFormItem } from '@oge-ui/forms'; @Component({ selector: 'demo-root', imports: [OgeForm, OgeFormItem], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly shipment = signal({ carrier: 'DHL', reference: 'REF-9', trackingNumber: '', }); } ``` #### Actions ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeForm, OgeFormItem } from '@oge-ui/forms'; import { OgeButton } from '@oge-ui/buttons'; @Component({ selector: 'demo-root', imports: [OgeForm, OgeFormItem, OgeButton], changeDetection: ChangeDetectionStrategy.OnPush, template: `
`, }) export class Demo { protected readonly signup = signal({ email: '', password: '' }); protected readonly saved = signal(false); } ``` #### Basic ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeForm, OgeFormItem } from '@oge-ui/forms'; @Component({ selector: 'demo-root', imports: [OgeForm, OgeFormItem], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly employee = signal({ firstName: 'Ada', lastName: 'Lovelace', email: '', notes: '', }); } ``` #### Editor ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeForm, OgeFormItem } from '@oge-ui/forms'; @Component({ selector: 'demo-root', imports: [OgeForm, OgeFormItem], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly teams = ['Platform', 'Design', 'Support']; protected readonly profile = signal({ name: 'Grace', age: 45, birthday: new Date(1980, 4, 12), active: true, team: 'Platform', bio: '', }); } ``` #### Group ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeForm, OgeFormGroup, OgeFormItem } from '@oge-ui/forms'; @Component({ selector: 'demo-root', imports: [OgeForm, OgeFormGroup, OgeFormItem], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly account = signal({ firstName: 'Ada', lastName: 'Lovelace', email: 'ada@example.com', phone: '', address: '', }); } ``` #### Items ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeForm } from '@oge-ui/forms'; import type { OgeFormItemData } from '@oge-ui/forms'; @Component({ selector: 'demo-root', imports: [OgeForm], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly order = signal({ reference: '', quantity: 1, priority: 'normal', shipped: false, }); protected readonly fields: OgeFormItemData[] = [ { field: 'reference', label: 'Reference', isRequired: true }, { field: 'quantity', label: 'Quantity', editorOptions: { min: 1, max: 99 } }, { field: 'priority', label: 'Priority', editorOptions: { items: ['low', 'normal', 'high'] }, }, { field: 'shipped', label: 'Shipped', dataType: 'boolean' }, ]; } ``` #### Label ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeForm, OgeFormItem } from '@oge-ui/forms'; @Component({ selector: 'demo-root', imports: [OgeForm, OgeFormItem], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly settings = signal({ host: 'localhost', port: 5432, secure: true, }); } ``` #### Template ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeForm, OgeFormItem, OgeFormEditorTemplate, OgeFormLabelTemplate } from '@oge-ui/forms'; @Component({ selector: 'demo-root', imports: [OgeForm, OgeFormItem, OgeFormEditorTemplate, OgeFormLabelTemplate], changeDetection: ChangeDetectionStrategy.OnPush, template: ` {{ text }} {{ ticket().rating }} / 5 `, }) export class Demo { protected readonly ticket = signal({ title: '', rating: 3 }); protected setRating(value: string): void { this.ticket.update((t) => ({ ...t, rating: Number(value) })); } } ``` #### Config ```ts import { provideOgeFormsConfig } from '@oge-ui/forms'; export const appConfig: ApplicationConfig = { providers: [ provideOgeFormsConfig({ labelLocation: 'start', showOptionalMark: true, messages: { requiredMark: '•', optionalMark: 'isteğe bağlı', validationSummaryTitle: '{count} alan düzeltilmeli', validationSummaryTitleOne: '1 alan düzeltilmeli', }, }), ], }; ``` #### Custom ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeForm, OgeFormItem } from '@oge-ui/forms'; import type { OgeValidationRule } from '@oge-ui/forms'; @Component({ selector: 'demo-root', imports: [OgeForm, OgeFormItem], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly account = signal({ password: '', confirm: '' }); // a custom rule sees its own value and the whole model, so cross-field // checks need no second engine protected readonly matchRule: OgeValidationRule[] = [ { type: 'custom', validate: ({ value, data }) => value === data['password'] ? null : 'Passwords do not match', }, ]; } ``` #### Metadata ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeForm, OGE_FORM_LABEL, OGE_FORM_HINT, OGE_FORM_GROUP, OGE_FORM_COL_SPAN, OGE_FORM_EDITOR } from '@oge-ui/forms'; import { form, required, email, metadata } from '@angular/forms/signals'; @Component({ selector: 'demo-root', imports: [OgeForm], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly model = signal({ name: '', email: '', bio: '' }); protected readonly profile = form(this.model, (p) => { required(p.name); required(p.email); email(p.email); metadata(p.name, OGE_FORM_LABEL, () => 'Full name'); metadata(p.email, OGE_FORM_LABEL, () => 'E-mail address'); metadata(p.email, OGE_FORM_HINT, () => 'Work address, please'); metadata(p.email, OGE_FORM_GROUP, () => 'Contact'); metadata(p.bio, OGE_FORM_EDITOR, () => 'textArea' as const); metadata(p.bio, OGE_FORM_COL_SPAN, () => 2); }); } ``` #### Reactive ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeForm, OgeFormItem } from '@oge-ui/forms'; import { FormControl, FormGroup, Validators } from '@angular/forms'; @Component({ selector: 'demo-root', imports: [OgeForm, OgeFormItem], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly group = new FormGroup({ name: new FormControl('', Validators.required), email: new FormControl('', [Validators.required, Validators.email]), }); } ``` #### Rules ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeForm, OgeFormItem } from '@oge-ui/forms'; @Component({ selector: 'demo-root', imports: [OgeForm, OgeFormItem], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly signup = signal({ username: '', email: '', age: 0 }); } ``` #### Signal forms ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeForm, OgeFormItem } from '@oge-ui/forms'; import { form, required, email, minLength, disabled } from '@angular/forms/signals'; @Component({ selector: 'demo-root', imports: [OgeForm, OgeFormItem], changeDetection: ChangeDetectionStrategy.OnPush, template: `

valid: {{ profile().valid() }}

`, }) export class Demo { protected readonly model = signal({ name: '', email: '', tenant: 'acme' }); protected readonly profile = form(this.model, (p) => { required(p.name); minLength(p.name, 2); required(p.email); email(p.email); disabled(p.tenant, () => true); }); } ``` #### Summary ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeForm, OgeFormItem, OgeValidationSummary } from '@oge-ui/forms'; import { OgeButton } from '@oge-ui/buttons'; @Component({ selector: 'demo-root', imports: [OgeForm, OgeFormItem, OgeValidationSummary, OgeButton], changeDetection: ChangeDetectionStrategy.OnPush, template: `
`, }) export class Demo { protected readonly ticket = signal({ title: '', reporter: '' }); } ``` ## @oge-ui/upload File uploader — drag & drop with directory and paste support, client-side restrictions, image previews, per-file progress, and chunked resumable transfer with pause, resume and retry. Docs: https://ogeui.com/components/upload ### Entry points `@oge-ui/upload` - values: `OGE_DEFAULT_UPLOAD_CONFIG`, `OGE_DEFAULT_UPLOAD_MESSAGES`, `OGE_UPLOAD_CONFIG`, `OGE_UPLOAD_TRANSPORT`, `OgeFileUploader`, `OgeUploadDropZone`, `OgeUploadDropZoneTemplate`, `OgeUploadEmptyTemplate`, `OgeUploadFileTemplate`, `OgeUploadHeaderTemplate`, `OgeUploadIconTemplate`, `OgeUploadToolbarTemplate`, `OgeUploadTrigger`, `createHttpClientUploadAdapter`, `createXhrUploadAdapter`, `formatFileSize`, `provideOgeUploadConfig` - types: `OgeFormatFileSizeOptions`, `OgeUploadAbortReason`, `OgeUploadAbortedEvent`, `OgeUploadActionsLayout`, `OgeUploadAdapter`, `OgeUploadAllUploadedEvent`, `OgeUploadAnnouncementMessages`, `OgeUploadButtonMessages`, `OgeUploadCallbacks`, `OgeUploadCancelableEvent`, `OgeUploadCandidate`, `OgeUploadChunkFailedEvent`, `OgeUploadChunkMetadata`, `OgeUploadChunkOptions`, `OgeUploadChunkUploadedEvent`, `OgeUploadChunkUploadingEvent`, `OgeUploadClearedEvent`, `OgeUploadClearingEvent`, `OgeUploadConfig`, `OgeUploadConfigInput`, `OgeUploadDisplayMode`, `OgeUploadDropEffect`, `OgeUploadDropZoneEvent`, `OgeUploadDropZoneMessages`, `OgeUploadDropZoneTemplateContext`, `OgeUploadErrorKind`, `OgeUploadFailedEvent`, `OgeUploadFieldError`, `OgeUploadFile`, `OgeUploadFileDownloadingEvent`, `OgeUploadFileError`, `OgeUploadFileListOptions`, `OgeUploadFileRejectedEvent`, `OgeUploadFileRemovedEvent`, `OgeUploadFileRemovingEvent`, `OgeUploadFileStatus`, `OgeUploadFileTemplateContext`, `OgeUploadFilesDroppedEvent`, `OgeUploadFilesSelectedEvent`, `OgeUploadFilesSelectingEvent`, `OgeUploadHandle`, `OgeUploadHeaderTemplateContext`, `OgeUploadIconSlot`, `OgeUploadIconTemplateContext`, `OgeUploadListType`, `OgeUploadMessages`, `OgeUploadMode`, `OgeUploadPart`, `OgeUploadPausedEvent`, `OgeUploadPausingEvent`, `OgeUploadPreloadedFile`, `OgeUploadPreviewHiddenEvent`, `OgeUploadPreviewShowingEvent`, `OgeUploadProgressEvent`, `OgeUploadRequest`, `OgeUploadRestrictions`, `OgeUploadResumedEvent`, `OgeUploadResumingEvent`, `OgeUploadRetryOptions`, `OgeUploadSelectionSource`, `OgeUploadStartedEvent`, `OgeUploadStatusMessages`, `OgeUploadThumbnailFailedEvent`, `OgeUploadToolbarTemplateContext`, `OgeUploadUploadedEvent`, `OgeUploadUploadingEvent`, `OgeUploadValidationMessages` ### OgeFileUploader — `` #### Properties _Selection_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `accept` | `string` | `''` | The `accept` attribute of the file input. Also filters drops and pastes, which the browser does not do for you. | | `multiple` | `boolean` | `true` | Allows several files. With `false` a new selection _replaces_ the list, which is what every reference does. | | `directory` | `boolean` | `false` | Lets the dialog pick a folder, and descends into dropped folders. Falls back to the flat file list where the entry API is unavailable. | | `pastable` | `boolean` | `false` | Adds files from a paste while the uploader has focus — a pasted screenshot included. | | `allowDrop` | `boolean` | `true` | Turns drag & drop off without hiding the browse affordance. | | `dropZone` | `string \| undefined` | `undefined` | Name this uploader answers to, so `[ogeUploadDropZone]` and `[ogeUploadTrigger]` elsewhere can reach it. dx `dropZone`, Kendo `zoneId`, Syncfusion `dropArea`. | | `dropEffect` | `'copy' \| 'move' \| 'link' \| 'none' \| 'default'` | `'copy'` | Pointer feedback while files hover the zone. | | `fieldName` | `string` | `'files[]'` | Multipart field name, and the `name` attribute of the file input. Called `fieldName` because `name` belongs to the Angular forms contract — Kendo splits it the same way with `saveField`. | | `openFileDialogOnClick` | `boolean` | `true` | Ant’s option. `false` makes the zone drop-only — and stops it being a button, because a button that does nothing on Enter is worse than none; the separate browse button appears instead so the keyboard path survives. | | `capture` | `boolean \| 'user' \| 'environment'` | `undefined` | The native `capture` attribute: opens the camera or microphone directly on mobile instead of the file browser. | | `transformFile` | `((file: File) => File \| Promise) \| undefined` | `undefined` | Rewrites each file — compression, watermarking, EXIF stripping. Ant folds this into `beforeUpload`; keeping it separate from `validateFile` means a transform cannot accidentally reject. Applied _before_ validation, so the restrictions judge the bytes that will be sent. | | `thumbnailFor` | `((file: OgeUploadFile) => string \| null \| Promise) \| undefined` | `undefined` | Supplies a preview the browser cannot make itself — a server-rendered PDF thumbnail, a downscaled canvas image. Ant’s `previewFile` plus `isImageUrl`: returning `null` is the "not an image" half. | | `inputAttributes` | `Record` | `{}` | Extra attributes for the internal file input, which no Angular binding can reach. | _Restrictions_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `allowedFileExtensions` | `readonly string[]` | `[]` | `.png`-style or bare `png`-style; empty allows everything. | | `maxFileSize / minFileSize` | `number \| undefined` | `undefined` | Inclusive bounds in bytes. `undefined` means no limit — an explicit `0` is a real limit, unlike dx, where it is the sentinel. | | `maxFileCount` | `number \| undefined` | `undefined` | Only the files past the limit are rejected, and a rejected file never spends a count slot. | | `maxTotalFileSize` | `number \| undefined` | `undefined` | Budget across the whole list. **OGE extra**. | | `validateFile` | `((file: File) => string \| null) \| undefined` | `undefined` | Returns a message to reject, or `null` to accept — the validation half of Ant’s `beforeUpload`. | _Transport_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `uploadUrl` | `string \| ((files: readonly File[]) => string)` | `''` | Empty means there is nowhere to send to, and nothing is sent — the uploader stays a picker. | | `uploadMode` | `'instantly' \| 'useButtons' \| 'useForm' \| 'select'` | `'instantly'` | `select` never uploads: Kendo’s FileSelect as a mode rather than a second component. | | `uploadMethod / uploadHeaders / uploadCustomData` | `'post' \| 'put' \| 'patch' / Record / Record \| fn` | `'post' / {} / {}` | `uploadCustomData` also takes a per-file function — Ant’s `data` in both shapes. | | `withCredentials / responseType / timeout` | `boolean / 'json' \| 'text' \| 'blob' / number` | `false / json / undefined` | Standard request knobs. | | `batch` | `boolean` | `false` | Every file in one request. | | `concurrency` | `number \| undefined` | `3 (config)` | One number replaces two booleans: Kendo’s `concurrent: false` and Syncfusion’s `sequentialUpload: true` are both `1`. | | `chunk` | `boolean \| OgeUploadChunkOptions` | `false` | Kendo’s `ChunkSettings` defaults: `size: 1 MiB`, `autoRetryAfter: 100`, `maxAutoRetries: 1`, `resumable: true`. | | `autoRetry` | `boolean \| OgeUploadRetryOptions` | `false` | Whole-file retry; defaults `{ count: 3, delayMs: 500 }`. A run serving its backoff frees its concurrency slot. | | `uploadAdapter` | `OgeUploadAdapter \| undefined` | `undefined` | Replaces the transport wholesale. See `createHttpClientUploadAdapter` for interceptor support. | | `abortable` | `boolean` | `true` | dx’s `allowCanceling`. | | `removeUrl / removeMethod / removeHeaders / removeField` | `string / 'post' \| 'delete' / Record / string` | `undefined / 'post' / {} / 'fileNames'` | Server-side delete. Only files that actually reached the server are deleted there. | _Display_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `displayMode` | `'full' \| 'compact' \| 'button'` | `'full'` | PrimeNG’s advanced/basic pair, plus a slim `compact` bar (**OGE extra**). | | `showFileList` | `boolean \| OgeUploadFileListOptions` | `true` | The boolean, or Ant’s options object (`showRemove`, `showRetry`, `showCancel`, `showPause`, …). | | `listType / previewWidth` | `'text' \| 'picture' \| 'pictureCard' / number` | `'text' / 50` | Preview rendering. | | `actionsLayout` | `'start' \| 'center' \| 'end' \| 'stretch'` | `'end'` | Where the action row sits. | | `showUploadButton / showClearButton / showCancelButton` | `boolean \| undefined` | `undefined` | `undefined` derives visibility from `uploadMode` — no button that has nothing to do. | | `initialFiles` | `readonly OgeUploadPreloadedFile[]` | `[]` | Files that already live on the server — Syncfusion’s `files`, Ant’s `defaultFileList`. | | `messages` | `Partial` | `undefined` | Per-instance override, layered over `provideOgeUploadConfig()`. | _State and forms_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `value` | `model` | `[]` | Every row that carries a real `File`, **including invalid ones** — hiding them would let `required` pass while a file is plainly on screen. The validator is what blocks submission. | | `disabled / readonly / required / invalid / touched / dirty / name / errors` | `FormValueControl contract` | `—` | The Signal Forms member names. Never bind these alongside `[formField]` — express them in the schema. | | `files` | `Signal` | `—` | Read-only. Every row, invalid and preloaded included. | | `progress / busy / uploadedCount / fileCount / limitExceeded / valid / validationErrors` | `Signal` | `—` | Read-only. Replaces dx’s `progress`/`isValid`/`validationErrors` and PrimeNG’s boolean methods. | #### Methods _OgeFileUploader_ | Name | Type | Description | | --- | --- | --- | | `upload(uids?)` | `void` | Starts the queued transfers. Files that failed a restriction are never sent. | | `abort(uid?, reason?)` | `void` | dx `abortUpload`, Kendo `cancelUploadByUid`. | | `pause(uid) / resume(uid)` | `boolean` | Chunked and resumable transfers only; returns `false` otherwise rather than aborting and calling it a pause. | | `retry(uid?)` | `void` | With no argument, everything that failed or was aborted. | | `addFiles(files)` | `void` | Adds files through the same pipeline as a drop. | | `removeFile(uid) / clear()` | `void` | Both run their cancelable pre-event first. | | `openFileDialog()` | `void` | PrimeNG `choose`. | | `getFiles(index?) / sortFiles(compare?)` | `readonly OgeUploadFile[] / void` | Syncfusion `getFilesData` / `sortFileList`; sorts by name unless told otherwise. | | `preview(uid) / download(uid)` | `void` | Opens the built-in lightbox / downloads the file. Both fire a cancelable event first, so an app can substitute its own viewer or signed-URL flow — Ant’s `onPreview` and `onDownload`. | | `reset(value?)` | `void` | dx’s `reset`: back to a pristine state, clearing touched and dirty. Fires no `clearing` pipeline — a reset is the app rewinding its form, not the user removing files. | | `focus() / blur()` | `void` | Moves focus to the browse affordance. | | `formatFileSize(bytes, options?)` | `string` | Exported free function — PrimeNG `formatSize`, Syncfusion `bytesToSize`. | #### Events _Selection_ | Name | Type | Description | | --- | --- | --- | | `filesSelecting` | `OgeUploadFilesSelectingEvent` | **Cancelable.** Before anything is validated or added. Carries the source: dialog, drop, paste or api. | | `filesSelected / fileRejected / filesDropped` | `event` | `fileRejected` fires once per file that failed a restriction — **OGE extra**; the references only render a message. | | `dropZoneEntered / dropZoneLeft` | `OgeUploadDropZoneEvent` | dx `onDropZoneEnter` / `onDropZoneLeave`. | _Transfers_ | Name | Type | Description | | --- | --- | --- | | `uploading` | `OgeUploadUploadingEvent` | **Cancelable**, and the one place the outgoing `request` is writable — dx’s `onBeforeSend`. | | `uploadStarted / uploadProgress / uploaded / uploadFailed / uploadAborted / allUploaded` | `event` | `uploadStarted` fires when a request actually goes out — not when the file is queued, and again on a retry. | | `chunkUploading / chunkUploaded / chunkFailed` | `event` | `chunkUploading` is cancelable, per slice. | | `uploadPausing / uploadPaused / uploadResuming / uploadResumed` | `event` | The `-ing` halves are cancelable. | _List_ | Name | Type | Description | | --- | --- | --- | | `fileRemoving / fileRemoved` | `event` | `fileRemoving` is cancelable and reports whether a server delete will follow. | | `clearing / cleared` | `event` | `clearing` is cancelable. | | `previewShowing / previewHidden` | `event` | `previewShowing` is cancelable — veto it to open your own viewer instead of the built-in lightbox. | | `fileDownloading` | `OgeUploadFileDownloadingEvent` | Cancelable. The default is an anchor click against the server `url`, or a temporary object URL for a file that only exists locally. | | `thumbnailFailed / valueChange / touch` | `event` | Preview decode failure, the two-way model, and the forms contract. | #### Types _Types_ | Name | Type | Description | | --- | --- | --- | | `OgeUploadFile` | `interface` | `uid`, `name`, `size`, `type`, `file`, `status`, `loaded`, `progress`, `errors`, `response`, `httpStatus`, `chunk`, `attempts`, plus `bytesPerSecond` and `secondsRemaining` (**OGE extra**). | | `OgeUploadFileStatus` | `union` | `'pending' \| 'uploading' \| 'paused' \| 'uploaded' \| 'failed' \| 'aborted' \| 'invalid' \| 'removed'`. | | `OgeUploadAdapter` | `interface` | `send(parts, request, callbacks)` and optional `remove(...)`. Batch versus per-file is the shape of the argument, not a flag. | | `OgeUploadChunkMetadata` | `interface` | Kendo’s `ChunkMetadata` field for field, so a Kendo-shaped server needs no changes. | | `OGE_UPLOAD_TRANSPORT` | `InjectionToken` | The default adapter. Override it in tests and demos; jsdom’s XHR performs real network I/O. | | `provideOgeUploadConfig(config)` | `Provider` | App-wide defaults and messages — five nested message blocks: buttons, dropZone, status, validation, announcements. | | `OgeUploadDropZone` | `directive — [ogeUploadDropZone]` | Turns any element into a drop target for the uploader whose `dropZone` matches the given name. Exposes an `over` signal for your own hover styling. | | `OgeUploadTrigger` | `directive — [ogeUploadTrigger]` | Opens an uploader’s file dialog from a button elsewhere on the page — dx’s `dialogTrigger`. Disables itself while no uploader answers to that name. | | `createXhrUploadAdapter()` | `() => OgeUploadAdapter` | The default transport, and the value behind `OGE_UPLOAD_TRANSPORT`. Exported so a custom adapter can delegate to it. | | `createHttpClientUploadAdapter(http)` | `(http: HttpClient) => OgeUploadAdapter` | Runs transfers through Angular’s `HttpClient`, so interceptors (auth, tracing) apply. `@angular/common/http` is passed in, never imported by the package. | _Template directives_ | Name | Type | Description | | --- | --- | --- | | `OgeUploadFileTemplate` | `directive — *ogeUploadFileTemplate` | Replaces one file row. Context: `$implicit` (the file), `index`, and the pre-formatted `size` and `status`. Covers PrimeNG’s `file`/`filelabel`, Syncfusion’s `template` and Ant’s `itemRender`. | | `OgeUploadHeaderTemplate` | `directive — *ogeUploadHeaderTemplate` | Replaces the strip above the list. Context: the files, `count`, `uploadedCount` and a pre-formatted `totalSize`. | | `OgeUploadDropZoneTemplate` | `directive — *ogeUploadDropZoneTemplate` | Replaces the drop zone’s contents. Context: `$implicit` is `true` while files hover, plus `disabled`. | | `OgeUploadEmptyTemplate` | `directive — *ogeUploadEmptyTemplate` | Rendered in place of the list while nothing is selected. | | `OgeUploadToolbarTemplate` | `directive — *ogeUploadToolbarTemplate` | Replaces the Upload/Clear action row. Context: the files and `uploading`. | | `OgeUploadIconTemplate` | `directive — *ogeUploadIconTemplate` | Replaces one glyph. `$implicit` is an `OgeUploadIconSlot` — 14 values covering PrimeNG’s four icon slots and Ant’s three, in one directive instead of seven inputs. | ### Demos Each demo below is a complete standalone component — copy one whole and it compiles. #### Chunk ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeFileUploader } from '@oge-ui/upload'; @Component({ selector: 'demo-root', imports: [OgeFileUploader], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected onChunk(index: number, total: number): void { console.log(`chunk ${index + 1} of ${total}`); } } ``` #### External zone ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeFileUploader, OgeUploadDropZone, OgeUploadTrigger } from '@oge-ui/upload'; @Component({ selector: 'demo-root', imports: [OgeFileUploader, OgeUploadDropZone, OgeUploadTrigger], changeDetection: ChangeDetectionStrategy.OnPush, template: `
Drop files anywhere in this panel
`, }) export class Demo {} ``` #### Forms ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeFileUploader } from '@oge-ui/upload'; import { ReactiveFormsModule, FormControl } from '@angular/forms'; @Component({ selector: 'demo-root', imports: [OgeFileUploader, ReactiveFormsModule], changeDetection: ChangeDetectionStrategy.OnPush, template: `

valid: {{ attachments.valid }}

`, }) export class Demo { protected readonly attachments = new FormControl([], { nonNullable: true, }); } ``` #### Getting started ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeFileUploader } from '@oge-ui/upload'; @Component({ selector: 'demo-root', imports: [OgeFileUploader], changeDetection: ChangeDetectionStrategy.OnPush, template: `

{{ attachments().length }} file(s) ready

`, }) export class Demo { protected readonly attachments = signal([]); } ``` #### Picture ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeFileUploader } from '@oge-ui/upload'; @Component({ selector: 'demo-root', imports: [OgeFileUploader], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo {} ``` #### Restrictions ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeFileUploader } from '@oge-ui/upload'; @Component({ selector: 'demo-root', imports: [OgeFileUploader], changeDetection: ChangeDetectionStrategy.OnPush, template: ` @if (lastRejection(); as reason) {

{{ reason }}

} `, }) export class Demo { protected readonly lastRejection = signal(null); /** Returns a message to reject, or null to accept. */ protected readonly noSpaces = (file: File): string | null => file.name.includes(' ') ? 'File names must not contain spaces.' : null; } ``` #### Template ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeFileUploader, OgeUploadFileTemplate } from '@oge-ui/upload'; @Component({ selector: 'demo-root', imports: [OgeFileUploader, OgeUploadFileTemplate], changeDetection: ChangeDetectionStrategy.OnPush, template: ` {{ file.name }} {{ size }} · {{ status }} `, }) export class Demo {} ``` #### Upload ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeFileUploader } from '@oge-ui/upload'; @Component({ selector: 'demo-root', imports: [OgeFileUploader], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected onUploaded(name: string, response: unknown): void { console.log('uploaded', name, response); } protected onFailed(name: string, message: string): void { console.warn('failed', name, message); } } ``` ## @oge-ui/navigation Navigation controls — a tree view over flat or nested data with tri-state checkboxes, search, lazy load on demand, virtual scrolling and drag & drop reparenting, a drawer whose modality follows its layout mode (dialog when it covers the content, landmark when it shares the row), a WAI-ARIA APG menubar with nested submenus and a container-width hamburger collapse, an APG breadcrumb whose oldest middle crumbs collapse into an ellipsis menu against the container width while staying reachable as links, and a standalone pagination bar with a constant-width ellipsis page window, page-size selector, info range and adaptive compact mode. Docs: https://ogeui.com/components/tree-view ### Entry points `@oge-ui/navigation` - values: `OGE_BREADCRUMB_CONFIG`, `OGE_DEFAULT_BREADCRUMB_CONFIG`, `OGE_DEFAULT_BREADCRUMB_MESSAGES`, `OGE_DEFAULT_DRAWER_CONFIG`, `OGE_DEFAULT_DRAWER_MESSAGES`, `OGE_DEFAULT_MENUBAR_CONFIG`, `OGE_DEFAULT_MENUBAR_MESSAGES`, `OGE_DEFAULT_PAGINATION_CONFIG`, `OGE_DEFAULT_PAGINATION_MESSAGES`, `OGE_DEFAULT_STEPPER_CONFIG`, `OGE_DEFAULT_STEPPER_MESSAGES`, `OGE_DEFAULT_TREE_VIEW_CONFIG`, `OGE_DEFAULT_TREE_VIEW_MESSAGES`, `OGE_DRAWER_CONFIG`, `OGE_MENUBAR_CONFIG`, `OGE_PAGINATION_CONFIG`, `OGE_STEPPER_CONFIG`, `OGE_TREE_VIEW_CONFIG`, `OgeBreadcrumb`, `OgeBreadcrumbItem`, `OgeBreadcrumbItemTemplate`, `OgeBreadcrumbSeparatorTemplate`, `OgeDrawer`, `OgeMenubar`, `OgeMenubarItem`, `OgeMenubarItemTemplate`, `OgePagination`, `OgeStep`, `OgeStepContentTemplate`, `OgeStepHeaderTemplate`, `OgeStepIndicatorTemplate`, `OgeStepper`, `OgeStepperNext`, `OgeStepperPrevious`, `OgeTreeExpandIconTemplate`, `OgeTreeItemTemplate`, `OgeTreeNoDataTemplate`, `OgeTreeView`, `provideOgeBreadcrumbConfig`, `provideOgeDrawerConfig`, `provideOgeMenubarConfig`, `provideOgePaginationConfig`, `provideOgeStepperConfig`, `provideOgeTreeViewConfig` - types: `CheckState`, `OgeBreadcrumbCollapseMode`, `OgeBreadcrumbConfig`, `OgeBreadcrumbConfigInput`, `OgeBreadcrumbItemClickEvent`, `OgeBreadcrumbItemData`, `OgeBreadcrumbItemTemplateContext`, `OgeBreadcrumbMessages`, `OgeBreadcrumbSeparatorTemplateContext`, `OgeDrawerAutoFocus`, `OgeDrawerCloseReason`, `OgeDrawerClosedEvent`, `OgeDrawerClosingEvent`, `OgeDrawerConfig`, `OgeDrawerConfigInput`, `OgeDrawerLandmark`, `OgeDrawerMessages`, `OgeDrawerMode`, `OgeDrawerModeChangedEvent`, `OgeDrawerOpeningEvent`, `OgeDrawerPosition`, `OgeMenubarCloseReason`, `OgeMenubarCompactChangedEvent`, `OgeMenubarConfig`, `OgeMenubarConfigInput`, `OgeMenubarItemClickEvent`, `OgeMenubarItemData`, `OgeMenubarItemTemplateContext`, `OgeMenubarMessages`, `OgeMenubarOpenMode`, `OgeMenubarOrientation`, `OgeMenubarSubmenuClosedEvent`, `OgeMenubarSubmenuClosingEvent`, `OgeMenubarSubmenuOpenedEvent`, `OgeMenubarSubmenuOpeningEvent`, `OgePaginationConfig`, `OgePaginationConfigInput`, `OgePaginationDisplayMode`, `OgePaginationMessages`, `OgePaginationPageChangedEvent`, `OgePaginationPageSizeChangedEvent`, `OgePaginationSize`, `OgeStepBlockedEvent`, `OgeStepChangedEvent`, `OgeStepChangingEvent`, `OgeStepData`, `OgeStepGuard`, `OgeStepState`, `OgeStepTemplateContext`, `OgeStepperConfig`, `OgeStepperConfigInput`, `OgeStepperDisplay`, `OgeStepperFinishEvent`, `OgeStepperMessages`, `OgeStepperOrientation`, `OgeTreeCheckBoxesMode`, `OgeTreeChildrenFailedEvent`, `OgeTreeChildrenLoadedEvent`, `OgeTreeCollapsedEvent`, `OgeTreeCollapsingEvent`, `OgeTreeDataStructure`, `OgeTreeDropPosition`, `OgeTreeExpandEvent`, `OgeTreeExpandIconTemplateContext`, `OgeTreeExpandedEvent`, `OgeTreeExpandingEvent`, `OgeTreeExpr`, `OgeTreeItemClickEvent`, `OgeTreeItemSelectionChangedEvent`, `OgeTreeItemTemplateContext`, `OgeTreeLoadChildren`, `OgeTreeReorderedEvent`, `OgeTreeReorderingEvent`, `OgeTreeSearchMode`, `OgeTreeSelectAllChangedEvent`, `OgeTreeSelectedKeysMode`, `OgeTreeSelectionChangedEvent`, `OgeTreeSelectionChangingEvent`, `OgeTreeSelectionMode`, `OgeTreeSize`, `OgeTreeViewConfig`, `OgeTreeViewConfigInput`, `OgeTreeViewMessages`, `OgeTreeVirtualScrollOptions`, `RowKey`, `TreeFilterMode` ### OgeTreeView — `` #### Properties _Data & accessors_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `items` | `readonly T[] \| undefined` | `—` | Nodes to display — a flat parent-referencing list or nested children. | | `keyExpr` | `string \| ((row: T) => RowKey)` | `'id'` | Field holding a node's stable key. | | `parentIdExpr` | `string \| ((row: T) => unknown)` | `'parentId'` | Field holding a node's parent key (flat data). | | `itemsExpr` | `string \| ((row: T) => readonly T[]) \| undefined` | `—` | Field holding nested children. Setting it switches the tree to hierarchical data. | | `displayExpr` | `string \| ((row: T) => unknown)` | `'text'` | Field holding the display text. | | `disabledExpr` | `string \| ((row: T) => unknown)` | `'disabled'` | Field marking a node disabled. | | `hasItemsExpr` | `string \| ((row: T) => unknown)` | `'hasItems'` | Field hinting that a node has children that are not loaded yet — only consulted with a `loadChildren`. | | `iconExpr` | `string \| ((row: T) => unknown) \| undefined` | `—` | Field holding SVG path data (`d`) for a per-node icon. | | `rootValue` | `unknown` | `—` | Parent value that marks root nodes in flat data. `undefined`/`null` treats both as root. | | `dataStructure` | `'plain' \| 'tree' \| undefined` | `—` | Explicit data shape; inferred from `itemsExpr` when unset. | _State (two-way)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `expandedKeys` | `readonly RowKey[]` | `[]` | Keys of the expanded nodes. | | `selectedKeys` | `readonly RowKey[]` | `[]` | Keys of the selected nodes, projected by `selectedKeysMode` on the way out. | | `focusedKey` | `RowKey \| undefined` | `—` | Key of the node holding the roving tabindex. | | `searchValue` | `string` | `''` | Current search text. | _Selection_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `selectionMode` | `'none' \| 'single' \| 'multiple'` | `'none'` | How nodes may be selected. | | `selectByClick` | `boolean \| undefined` | `—` | Selects a node when its row is clicked. `undefined` resolves to `true` without checkboxes and `false` with them, so clicking a label never silently ticks the box beside it. | | `selectNodesRecursive` | `boolean` | `true` | Cascades selection down to descendants and up to fully-selected parents (the tri-state model). | | `showCheckBoxes` | `'none' \| 'normal' \| 'selectAll'` | `'none'` | Checkbox column: hidden, per node, or per node plus a "select all" row. | | `selectedKeysMode` | `'all' \| 'leavesOnly' \| 'excludeRecursive'` | `'all'` | Projection applied to `selectedKeys`: everything, only childless nodes, or the top-most roots of fully-selected subtrees. | _Expansion_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `expandEvent` | `'click' \| 'dblclick'` | `'click'` | Which gesture expands a node. The chevron always expands regardless. | | `expandNodesRecursive` | `boolean` | `true` | Expanding a node also expands its ancestors. | | `allowExpandAll` | `boolean` | `true` | Enables the APG `*` shortcut, which expands every sibling at the focused level. | _Search_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `searchEnabled` | `boolean` | `false` | Renders the built-in search box above the tree. | | `searchMode` | `'contains' \| 'startsWith' \| 'equals'` | `'contains'` | How the text is compared. Matching is accent- and locale-insensitive. | | `searchExpr` | `string \| ((row: T) => unknown) \| array \| undefined` | `—` | Fields searched instead of `displayExpr`; an array searches several. | | `searchTimeout` | `number` | `0` | Debounce applied to the search box, in milliseconds. | | `filterMode` | `'matchOnly' \| 'withAncestors' \| 'fullBranch'` | `'withAncestors'` | Which relatives of a match stay visible. `fullBranch` also keeps a match's descendants. | | `expandNodesOnFiltering` | `boolean` | `true` | Auto-expands the ancestors of matches. | | `highlightSearchResults` | `boolean` | `true` | Wraps matches in ``. | _Lazy loading, virtualization & drag_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `loadChildren` | `(parent: T, key: RowKey) => Promise` | `—` | Loads a node's children the first time it expands; a placeholder row shows meanwhile. Single-flight per node, and fetched children join the index so cascades reach them. | | `virtualScroll` | `boolean \| { itemHeight: number }` | `false` | Windowed rendering for large trees. Every row must actually be `itemHeight` tall (30px by default). | | `height` | `string \| undefined` | `—` | Height of the scroll container (any CSS length) — required for virtual scrolling to have a viewport. | | `allowDragging` | `boolean` | `false` | Enables pointer drag reordering. | | `allowDropInside` | `boolean` | `true` | Allows dropping *into* a node (reparenting), not just between siblings. | _Presentation_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `disabled` | `boolean` | `false` | Disables the whole component. | | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Density of the node rows. | | `ariaLabel` | `string \| undefined` | `—` | Aria label of the tree. | | `treeId` | `string \| undefined` | `—` | DOM id put on the inner `role="tree"` element — set it when an outside control (a combobox owning this tree as its popup) must point `aria-controls` at the list rather than the host. | | `messages` | `Partial` | `{}` | Per-instance overrides of the config strings. | #### Methods | Name | Type | Description | | --- | --- | --- | | `expand(key)` | `(key: RowKey) => Promise` | Expands a node, awaiting the lazy child fetch when there is one. Resolves `false` if the node is unknown or `itemExpanding` vetoed it. | | `collapse(key)` | `(key: RowKey) => Promise` | Collapses a node; resolves whether it actually collapsed. | | `toggle(key)` | `(key: RowKey) => Promise` | Expands the node if collapsed, collapses it otherwise. | | `expandAll()` | `() => void` | Expands every node that has loaded children. | | `collapseAll()` | `() => void` | Collapses every node. | | `selectAll()` | `() => void` | Selects every node. | | `unselectAll()` | `() => void` | Clears the selection. | | `select(key) / unselect(key)` | `(key: RowKey) => void` | Selects or deselects one node, cascading when `selectNodesRecursive` is on. | | `isExpanded(key) / isSelected(key)` | `(key: RowKey) => boolean` | Current state of one node. | | `getSelectedKeys(mode?)` | `(mode?: OgeTreeSelectedKeysMode) => RowKey[]` | Selected keys under a projection, defaulting to `selectedKeysMode`. | | `focus(key?)` | `(key?: RowKey) => void` | Focuses a node's row, or the first enabled one. | | `scrollToItem(key)` | `(key: RowKey) => void` | Scrolls a node into view, using offset math when virtualized. | #### Events | Name | Type | Description | | --- | --- | --- | | `itemExpanding / itemCollapsing` | `OgeTreeExpandingEvent / OgeTreeCollapsingEvent` | Cancelable pre-events — set `cancel = true` to block the change. | | `itemExpanded / itemCollapsed` | `OgeTreeExpandedEvent / OgeTreeCollapsedEvent` | Emitted after the change committed. | | `selectionChanging` | `OgeTreeSelectionChangingEvent` | Cancelable pre-event carrying the keys the selection would become. | | `selectionChanged` | `OgeTreeSelectionChangedEvent` | Emitted after the selection committed, with `previousKeys`. | | `itemSelectionChanged` | `OgeTreeItemSelectionChangedEvent` | Emitted for the single node whose own state flipped. | | `itemClick / itemDblClick` | `OgeTreeItemClickEvent` | Emitted when a node row is clicked or double-clicked. | | `childrenLoaded / childrenLoadFailed` | `OgeTreeChildrenLoadedEvent / OgeTreeChildrenFailedEvent` | Emitted after a lazy `loadChildren` settled; the failure carries the original error. | | `selectAllChanged` | `OgeTreeSelectAllChangedEvent` | Emitted when the "select all" row is toggled. | | `itemReordering / itemReordered` | `OgeTreeReorderingEvent / OgeTreeReorderedEvent` | Cancelable pre-event and result of a drag & drop reparent, carrying `position: 'inside' \| 'before' \| 'after'`. The tree does not mutate your data. | #### Types _Template slots_ | Name | Type | Description | | --- | --- | --- | | `[ogeTreeItemTemplate]` | `{ $implicit, key, level, expanded, selected, checkState, hasChildren, highlightedHtml }` | Replaces a node's built-in label. Renders inside `role="treeitem"`, so it must not contain focusable controls. | | `[ogeTreeExpandIconTemplate]` | `{ $implicit: boolean, item, key, loading }` | Replaces the expand/collapse chevron. | | `[ogeTreeNoDataTemplate]` | `—` | Replaces the empty state shown when the tree has no nodes or a search matched nothing. | _Keyboard (WAI-ARIA APG treeview)_ | Name | Type | Description | | --- | --- | --- | | `Down / Up Arrow` | `navigation` | Moves focus over the visible nodes, skipping disabled ones. Trees do not wrap at the ends. | | `Right Arrow` | `navigation` | Opens a collapsed parent; on an open parent moves to its first child; no-op on a leaf. | | `Left Arrow` | `navigation` | Closes an open parent; otherwise moves focus to the parent node. | | `Home / End` | `navigation` | Moves to the first / last visible node. | | `Enter` | `activation` | Toggles a parent, or selects a leaf when a selection mode is set. | | `Space` | `selection` | Toggles selection on the focused node. `Shift+Space` selects the contiguous range. | | `Printable characters` | `type-ahead` | Moves focus to the next node whose label starts with the typed prefix, accent-insensitively. | | `*` | `expansion` | Expands every sibling at the focused level. | | `Ctrl+A, Shift+Arrow, Ctrl+Shift+Home/End` | `multi-select` | Select all, extend by one, and range-select to the start/end — the APG "recommended" model, so plain navigation needs no modifier. | ### Tree view configuration #### Properties _OgeTreeViewMessages_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `selectAll` | `string` | `'Select all'` | Label of the "select all" row. | | `searchPlaceholder` | `string` | `'Search…'` | Placeholder of the built-in search box. | | `searchLabel` | `string` | `'Search the tree'` | Accessible name of the built-in search box. | | `clearSearch` | `string` | `'Clear search'` | Accessible name of the search box's clear button. | | `loadingChildren` | `string` | `'Loading…'` | Shown while a node's lazy children are loading. | | `childrenLoadFailed` | `string` | `'Could not load these items.'` | Shown when `loadChildren` rejected. | | `noData` | `string` | `'No items to display'` | Shown when the tree has no nodes at all. | | `noSearchResults` | `string` | `'No matching items'` | Shown when a search matched nothing. | #### Types _Behavioural defaults_ | Name | Type | Description | | --- | --- | --- | | `itemHeight` | `number \| undefined` | Default row height used by `virtualScroll`. | | `expandEvent` | `'click' \| 'dblclick' \| undefined` | Default for the `expandEvent` input. | | Name | Type | Description | | --- | --- | --- | | `provideOgeTreeViewConfig(config)` | `(config: OgeTreeViewConfigInput) => Provider` | Application- or component-scoped defaults; shallow-merges `messages` over the built-ins. | | `OGE_TREE_VIEW_CONFIG` | `InjectionToken` | The token itself, with a factory default — inject it to read the effective config. | ### OgeDrawer — `` #### Properties _Layout_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `opened` | `boolean (two-way)` | `false` | Whether the drawer is open. Two-way, so `[(opened)]` is the whole state. | | `mode` | `'overlay' \| 'push' \| 'side'` | `'overlay'` | `overlay` floats over the content, `push` shifts it aside without resizing it, `side` shrinks it so both share the row. **This also decides modality** — see the accessibility group. | | `position` | `'start' \| 'end' \| 'top' \| 'bottom'` | `'start'` | Edge the panel is attached to. Logical, so `start`/`end` mirror in RTL with no flag to set. | | `size` | `number \| string` | `260` | Size of the open panel along its cross axis. A number means pixels. | | `minSize` | `number \| string \| undefined` | `—` | Size of the _closed_ panel — the compact rail that keeps icons visible. Only meaningful for `mode: 'side'`: a rail belongs to the layout, and a modal drawer still partly on screen is not closed. | | `compactBelow` | `number \| undefined` | `—` | Below this **container** inline size the drawer downgrades to `'overlay'` and closes. Measured against the drawer's own box, never the window, so a drawer nested in a dialog or a split pane adapts to the room it actually has. | | `disabled` | `boolean` | `false` | Blocks every open and close gesture, `open()` and `close()` included. A drawer already open stays open and stays usable; only a `compact` close still goes through, because a drawer with no room left must stop covering the content. | | `animationEnabled` | `boolean` | `true` | Enables the open/close transition. | | `animationDuration` | `number` | `240` | Duration of that transition in milliseconds. Suppressed entirely under `prefers-reduced-motion`. | _Dismissal_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `showCloseButton` | `boolean` | `false` | Renders the built-in close button in the panel, labelled by the `close` message. It closes through the full pipeline, so `closeGuard` still applies. | | `shading` | `boolean` | `true` | Renders the backdrop of a modal drawer. A persistent drawer never shades the content it shares the row with. | | `closeOnEscape` | `boolean` | `true` | Escape closes a modal drawer, and only when it is the topmost overlay — a popup opened inside it closes first. A persistent drawer never takes Escape from the page. | | `closeOnBackdropClick` | `boolean` | `true` | A click on the backdrop closes the drawer. Only a press that _started_ on the backdrop counts, so a drag ending there does not close it. | | `closeGuard` | `(() => boolean \| Promise) \| undefined` | `—` | Vetoes a close. Return `false`, throw, or reject to keep the drawer open; a promise reports pending through `closePending` and a second gesture meanwhile is dropped. | | `scrollLock` | `boolean` | `false` | Locks body scroll while a modal drawer is open, through the same ref-counted lock every other OGE overlay uses. Off by default, because a drawer is usually an in-page region rather than a page-level dialog. | _Accessibility_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `landmark` | `'navigation' \| 'complementary' \| 'region'` | `'navigation'` | Landmark role while persistent. Ignored while modal, which is always `role="dialog"`. | | `ariaLabel` | `string \| undefined` | `—` | Accessible name of the panel. Falls back to the `drawer` message. | | `ariaLabelledBy` | `string \| undefined` | `—` | id of an element naming the panel. Wins over `ariaLabel`, which is then cleared so there is only one name. | | `messages` | `Partial \| undefined` | `—` | Per-instance overrides of the config strings — the panel’s fallback accessible name (`drawer`) and the close button’s label (`close`). | | `autoFocus` | `'first-tabbable' \| 'panel' \| 'none' \| string` | `'first-tabbable'` | Where focus lands when a _modal_ drawer opens; any other string is a CSS selector, and an `[autofocus]` element always wins. A persistent drawer never moves focus. | | `restoreFocus` | `boolean` | `true` | Returns focus to the opener on close, but only when focus would otherwise be orphaned — it never steals a target the user has moved to. | | `inertBackground` | `boolean` | `true` | Marks the content behind a modal drawer `inert`, so neither Tab nor assistive tech can reach it. Scoped to the drawer's own content region rather than the document, because a drawer wraps the very content it covers. None of the four reference drawers does this. | | `drawerId` | `string (readonly)` | `—` | id of the panel element. The panel stays in the DOM while closed, so a trigger's `aria-controls` always resolves to a real element. | #### Methods _Methods_ | Name | Type | Description | | --- | --- | --- | | `open()` | `void` | Opens the drawer through the cancelable pre-event. | | `close()` | `void` | Closes through the full pipeline (`closing` → `closeGuard`) with reason `'api'`. | | `toggle()` | `void` | Opens when closed, closes when open. | | `focus()` | `void` | Re-applies the initial-focus resolution. No-op unless the drawer is open and modal. | | `closePending` | `Signal` | True while an async `closeGuard` is in flight. | #### Events _Events_ | Name | Type | Description | | --- | --- | --- | | `opening` | `OgeDrawerOpeningEvent` | Cancelable — set `cancel` to keep the drawer closed; the two-way model is reset for you. | | `afterOpened` | `void` | The drawer finished opening. Fires on a render hook rather than `transitionend`, because the transition is CSS-only and `prefers-reduced-motion` zeroes it. | | `closing` | `OgeDrawerClosingEvent` | Cancelable, carries the `reason`. Runs before `closeGuard`. | | `closed` | `OgeDrawerClosedEvent` | The drawer finished closing. | | `modeChanged` | `OgeDrawerModeChangedEvent` | The resolved layout mode changed, carrying the requested mode and whether `compactBelow` forced it. | #### Types _Content slots_ | Name | Type | Description | | --- | --- | --- | | `[ogeDrawerPanel]` | `attribute` | Marks the element that becomes the drawer panel. Everything else projected into `` is the content. | _Types_ | Name | Type | Description | | --- | --- | --- | | `OgeDrawerMode` | `'overlay' \| 'push' \| 'side'` | Layout mode, and therefore modality. | | `OgeDrawerPosition` | `'start' \| 'end' \| 'top' \| 'bottom'` | Edge the panel attaches to; logical for RTL. | | `OgeDrawerLandmark` | `'navigation' \| 'complementary' \| 'region'` | Landmark role of a persistent drawer. | | `OgeDrawerAutoFocus` | `'first-tabbable' \| 'panel' \| 'none' \| string` | Initial-focus strategy of a modal drawer. | | `OgeDrawerCloseReason` | `'api' \| 'escape' \| 'backdrop' \| 'outside' \| 'compact'` | Why the drawer closed. | | `OgeDrawerOpeningEvent` | `{ cancel: boolean }` | Cancelable pre-event for opening. | | `OgeDrawerClosingEvent` | `{ cancel: boolean; reason: OgeDrawerCloseReason }` | Cancelable pre-event for closing. | | `OgeDrawerClosedEvent` | `{ reason: OgeDrawerCloseReason }` | Payload of `closed`. | | `OgeDrawerModeChangedEvent` | `{ mode; requestedMode; compact: boolean }` | Payload of `modeChanged`. | | `resolveDrawerMode()` | `(request: OgeDrawerModeRequest) => OgeDrawerModeResult` | The pure function in `@oge-ui/core` that decides whether a drawer keeps its mode or goes compact. DOM-free, so the rule is unit-tested on its own. A non-positive `containerSize` means “not measured yet” and the requested mode is returned unchanged. | ### Drawer configuration #### Properties _provideOgeDrawerConfig()_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `mode` | `OgeDrawerMode` | `—` | Default for the `mode` input. | | `position` | `OgeDrawerPosition` | `—` | Default for the `position` input. | | `size` | `number \| string` | `—` | Default for the `size` input. | | `messages.drawer` | `string` | `'Drawer'` | Accessible name of the panel when the application supplies none. | | `messages.close` | `string` | `'Close drawer'` | Label of a close affordance rendered in the panel. | ### OgeStepper — `` #### Properties _Steps & selection_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `activeIndex` | `number (two-way)` | `0` | Index of the active step. | | `activeKey` | `string \| undefined (two-way)` | `—` | Key of the active step. Reconciled before the index, so an initial key binding wins over the index default on first run. | | `steps` | `readonly OgeStepData[] \| undefined` | `—` | Data-driven steps, merged _after_ any declarative `` children. | | `linear` | `boolean` | `false` | Blocks moving past a step that is neither `completed` nor `optional`. The default matches Material and PrimeNG; Kendo is the outlier at `true`. | | `disabled` | `boolean` | `false` | Blocks every step change. | _Layout & chrome_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Main axis of the step list. **The ARIA semantics do not change with it** — see the accessibility group. | | `display` | `'full' \| 'label' \| 'indicator'` | `'full'` | How much of each header renders: label plus description, label only, or just the round indicator. | | `showNavigation` | `boolean` | `false` | Renders the built-in Back / Next bar, which becomes Finish on the last step. None of the three reference steppers ships one. | | `deferRendering` | `boolean` | `false` | Creates a step's body on first activation. | | `keepAlive` | `boolean` | `true` | Keeps a body mounted after the user leaves it. | _Accessibility_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `keyboardNavigation` | `boolean` | `false` | Adds arrow / Home / End over the headers. Off by default because the headers are buttons in a list, not tabs, so they are already Tab-reachable. It moves focus only, and deliberately does **not** wrap. | | `ariaLabel` | `string \| undefined` | `—` | Accessible name of the step list. Falls back to the `stepper` message. | | `messages` | `Partial \| undefined` | `—` | Per-instance overrides of the config strings — the list’s accessible name, the optional/completed/invalid announcements and the navigation bar’s labels. | | `stepperId` | `string (readonly)` | `—` | id prefix of the generated header / panel pairs. | | `changePending` | `Signal` | `—` | True while an async `stepGuard` is in flight. | #### Methods | Name | Type | Description | | --- | --- | --- | | `next(event?: Event)` | `void` | Advances one step, or confirms the finish when already on the last one. The guard runs either way, so a final step can still veto. | | `previous(event?: Event)` | `void` | Goes back one step, through the same pipeline. | | `goTo(target: number \| string, event?: Event)` | `void` | Moves to a step by index or key. | | `reset()` | `void` | Clears the rendered-body cache and returns to the first step. | | `focus()` | `void` | Focuses the active step's header. | #### Events | Name | Type | Description | | --- | --- | --- | | `stepChanging` | `OgeStepChangingEvent` | Cancelable, emitted before the leaving step’s `stepGuard` runs. | | `stepChanged` | `OgeStepChangedEvent` | The active step changed. | | `stepBlocked` | `OgeStepBlockedEvent` | A move was refused, carrying `reason: 'linear' \| 'editable' \| 'guard' \| 'disabled'`. Angular Material refuses silently. | | `finished` | `OgeStepperFinishEvent` | `next()` was confirmed on the last step. | #### Types _OgeStep (declarative child)_ | Name | Type | Description | | --- | --- | --- | | `key / label / description` | `string` | Identity and header text. | | `icon / iconClass` | `string \| undefined` | SVG path data, or class(es) for an icon font — replacing the step number. | | `completed / optional / editable` | `boolean` | The linear gate: `completed` lets a linear stepper past, `optional` lets it past regardless, and `editable: false` blocks coming _back_. | | `errorMessage` | `string \| undefined` | Shown under the label while `invalid`, replacing `description` so two sub-lines never compete. Angular Material has this; Kendo and PrimeNG do not. | | `invalid / disabled / visible` | `boolean` | Error state, non-activatable, and removal from the list. | | `stepGuard` | `() => boolean \| Promise` | Veto hook run when leaving this step. A throw and a rejection both veto. | _Types & directives_ | Name | Type | Description | | --- | --- | --- | | `OgeStepState` | `'number' \| 'active' \| 'done' \| 'error'` | Derived indicator state; error outranks done, so a completed step that later fails still reads as needing attention. | | `OgeStepperOrientation` | `'horizontal' \| 'vertical'` | Main axis; the ARIA model is the same for both. | | `OgeStepperDisplay` | `'full' \| 'label' \| 'indicator'` | How much of a header renders. | | `OgeStepData` | `{ key?; label?; description?; icon?; iconClass?; disabled?; visible?; completed?; optional?; editable?; invalid?; cssClass?; stepGuard? }` | One data-driven step. | | `OgeStepGuard` | `() => boolean \| Promise` | Core's `OgeAsyncGuard`, the same veto contract the tabs' close guard and the accordion's expand guard use. | | `[ogeStepperNext] / [ogeStepperPrevious]` | `directive` | Turn any button into a navigation control. They find the stepper by DI when written inside it, or take one explicitly (`ogeStepperNext [ogeStepperTarget]="wizard"`) from outside — which Material’s equivalents cannot do. | | `[ogeStepHeaderTemplate] / [ogeStepIndicatorTemplate] / [ogeStepContentTemplate]` | `directive` | Replace the label block, the round indicator, or supply lazy body content. | | `` | `directive (@oge-ui/forms)` | Wraps this component inside ``. Step completion comes from the form's own per-step error rollup, so it behaves identically in all three binding modes, and leaving a step touches only that step's fields. | ### Stepper configuration #### Properties _provideOgeStepperConfig()_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `orientation / display / linear` | `defaults` | `—` | Defaults for the matching inputs. | | `messages.stepper` | `string` | `'Steps'` | Accessible name of the step list. | | `messages.optional` | `string` | `'Optional'` | Sub-label of an optional step, wired through `aria-describedby`. | | `messages.completed / messages.invalid` | `string` | `'Completed' / 'Has errors'` | Announced in visually hidden text, because the indicator glyph is `aria-hidden`. | | `messages.previous / next / finish` | `string` | `'Back' / 'Next' / 'Finish'` | Labels of the built-in navigation bar. | ### OgeMenubar — `` #### Properties _Data_ | Name | Type | Description | | --- | --- | --- | | `items` | `readonly OgeMenubarItemData[] \| undefined` | Data-driven item tree; children at any depth open as nested submenus. Rendered **after** any declarative `` children — the house merge order. | | `activeKey` | `string \| undefined` | The item `key` rendered with `aria-current="page"` and the active style. Consumer-driven — bind it from the router; the menubar itself takes no router dependency. | | `messages` | `Partial \| undefined` | Per-instance overrides of the user-facing strings, merged over `provideOgeMenubarConfig()`. | _Behavior_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | A vertical bar announces `aria-orientation="vertical"` and swaps the arrow axes: Up/Down traverse, ArrowRight opens the submenu beside the bar. | | `openMode` | `'click' \| 'hover'` | `'click'` | How **top-level** submenus open. Nested levels always open on hover and on ArrowRight/Enter — the reference libraries’ first-vs-nested split baked in as behavior. With a menu open, hovering siblings switches it in either mode. | | `hoverDelay` | `number` | `100` | Hover dwell before a top-level submenu opens in `'hover'` mode, in ms. Nested levels use the overlay config's `menuShowDelayMs`/`menuHideDelayMs` (50/300). | | `compactBelow` | `number \| undefined` | `—` | Below this **container** inline size the whole bar collapses into a hamburger button opening the full tree as one nested menu. Measured against the menubar's own box, never the window. | | `disabled` | `boolean` | `false` | Disables the whole bar: every item goes inert and the bar leaves the Tab sequence. | | `submenuItemTemplate` | `TemplateRef \| undefined` | `—` | Custom rendering for submenu rows at **every depth** — the shared `oge-menu-list` context. Top-level bar items use `[ogeMenubarItemTemplate]` instead. | #### Methods | Name | Type | Description | | --- | --- | --- | | `open(target: number \| string)` | `void` | Opens the submenu of a top-level item, by index or `key`. Runs through the cancelable `submenuOpening` pipeline. | | `close()` | `void` | Closes any open submenu through the cancelable `submenuClosing` pipeline with `reason: 'api'`. | | `focus()` | `void` | Focuses the bar's roving tab target — or the hamburger button when compact. | #### Events | Name | Type | Description | | --- | --- | --- | | `itemClick` | `OgeMenubarItemClickEvent` | A leaf item was activated, at any depth. Carries the item, its `key` and the hierarchical index `path` from the bar down. | | `submenuOpening` | `OgeMenubarSubmenuOpeningEvent` | **Cancelable** — set `cancel` to keep the submenu closed. `item` is `undefined` for the compact hamburger menu (empty `path`). | | `submenuOpened` | `OgeMenubarSubmenuOpenedEvent` | A top-level submenu (or the hamburger menu) opened. | | `submenuClosing` | `OgeMenubarSubmenuClosingEvent` | **Cancelable** — set `cancel` to keep the submenu open. Fires for closes the menubar itself initiates (`escape`, `select`, `navigation`, `api`); overlay-owned closes (`outside`) and Tab only report `submenuClosed`. | | `submenuClosed` | `OgeMenubarSubmenuClosedEvent` | A submenu closed, with its `reason`. | | `compactChanged` | `OgeMenubarCompactChangedEvent` | The bar collapsed into (or recovered from) the compact hamburger. | #### Types | Name | Type | Description | | --- | --- | --- | | `OgeMenubarItemData` | `interface` | The canonical overlay `OgeMenuItem` narrowed recursively — `badge` and `shortcut` included — plus `key` (identity for `activeKey`/`open()`/events), `url` (renders the item as a real `` at the bar **and** at any submenu depth; `itemClick` fires first so `preventDefault()` hands navigation to a router) and `visible`. Submenus come from `items`. | | `OgeMenubarCloseReason` | `'escape' \| 'outside' \| 'select' \| 'tab' \| 'navigation' \| 'api'` | Why a submenu closed. `'navigation'` is a Left/Right or hover switch to a sibling top-level item. | | `OgeMenubarItemClickEvent` | `{ item; key?; index; path; event }` | `path` is the hierarchical index chain from the bar down to the item; `index` is its last entry. | | `OgeMenubarItemTemplate` | `directive — ng-template[ogeMenubarItemTemplate]` | Replaces the rendering of **top-level** bar items; context is `OgeMenubarItemTemplateContext`. Submenu rows keep the shared `oge-menu-list` rendering. | | `OgeMenubarItemTemplateContext` | `{ $implicit: OgeMenubarItemData; index: number }` | Context of `[ogeMenubarItemTemplate]`: the item and its top-level index. | ### OgeMenubarItem — `` #### Properties | Name | Type | Default | Description | | --- | --- | --- | --- | | `text` | `string` | `''` | Label of the item. | | `key` | `string \| undefined` | `—` | Stable identity used by `activeKey`, `open()` and event payloads. | | `value` | `unknown` | `—` | Consumer-defined value carried through click events. | | `url` | `string \| undefined` | `—` | Top-level items only: renders the item as a real link (``). | | `hint` | `string \| undefined` | `—` | Tooltip (native `title`) — e.g. why an item is disabled. | | `icon` | `string \| undefined` | `—` | SVG path data (`d`) for a leading `aria-hidden` icon. | | `iconClass` | `string \| undefined` | `—` | Class(es) for a leading icon element — the icon-font hook. | | `disabled` | `boolean` | `false` | Disabled items are exposed (`aria-disabled`) but inert and skipped by the arrow keys. | | `visible` | `boolean` | `true` | `false` removes the item and its subtree. | | `separator` | `boolean` | `false` | Renders a divider (`role="separator"`); every other input is ignored. | ### Menubar configuration #### Properties _provideOgeMenubarConfig()_ | Name | Type | Description | | --- | --- | --- | | `messages` | `OgeMenubarMessages` | Every user-facing string: `menubar` (accessible name of the bar, default `Menu bar`) and `hamburger` (aria label of the compact button, default `Menu`). | | `openMode` | `'click' \| 'hover' \| undefined` | Default for the `openMode` input. | | `hoverDelay` | `number \| undefined` | Default for the `hoverDelay` input, in ms. | | `orientation` | `'horizontal' \| 'vertical' \| undefined` | Default for the `orientation` input. | | `compactBelow` | `number \| undefined` | Default for the `compactBelow` input. | ### OgeBreadcrumb — `` #### Properties | Name | Type | Default | Description | | --- | --- | --- | --- | | `items` | `readonly OgeBreadcrumbItemData[] \| undefined` | `—` | Data-driven trail — a flat list, never nested. Rendered **after** any declarative `` children — the house merge order. | | `collapseMode` | `'auto' \| 'wrap' \| 'none'` | `'auto'` | `'auto'` collapses the **oldest middle** crumbs into an ellipsis menu against the breadcrumb's own **container** width (never the window) — the first and last crumb always stay visible, and the collapsed crumbs remain reachable as real links. `'wrap'` breaks onto multiple rows; `'none'` keeps one scrollable row. The fitting arithmetic is core's pure `fitToolbarItems`. | | `messages` | `Partial \| undefined` | `—` | Per-instance overrides of the user-facing strings, merged over `provideOgeBreadcrumbConfig()`. | #### Methods | Name | Type | Description | | --- | --- | --- | | `focus()` | `void` | Focuses the first interactive crumb — or the ellipsis button when the trail is collapsed. | #### Events | Name | Type | Description | | --- | --- | --- | | `itemClick` | `OgeBreadcrumbItemClickEvent` | A crumb (inline or inside the ellipsis menu) was activated. **Not fired** by disabled crumbs or by the last crumb — that is the current page. On `url` crumbs, `event.preventDefault()` hands navigation to a router. | #### Types | Name | Type | Description | | --- | --- | --- | | `OgeBreadcrumbItemData` | `{ text; key?; value?; url?; hint?; icon?; iconClass?; disabled?; visible? }` | A deliberately narrow interface — no submenu, checked or shortcut fields, because none of them mean anything on a trail. `url` renders the crumb as a real `` (ignored on the last crumb); `disabled` crumbs are exposed via `aria-disabled` but inert; `visible: false` removes the crumb. | | `OgeBreadcrumbCollapseMode` | `'auto' \| 'wrap' \| 'none'` | How the breadcrumb behaves when room runs out. | | `OgeBreadcrumbItemClickEvent` | `{ item; key?; index; event }` | `index` is the position within the full trail, collapsed crumbs included. | | `OgeBreadcrumbItemTemplate` | `directive — ng-template[ogeBreadcrumbItemTemplate]` | Replaces the crumb's interior only — the link/current/disabled element semantics stay with the component. Context: `OgeBreadcrumbItemTemplateContext` (`$implicit`, `index`, `last`). | | `OgeBreadcrumbSeparatorTemplate` | `directive — ng-template[ogeBreadcrumbSeparatorTemplate]` | Replaces the default chevron separator. Rendered `aria-hidden` — a separator is decoration, never content (APG). Context: `OgeBreadcrumbSeparatorTemplateContext`. | ### OgeBreadcrumbItem — `` #### Properties | Name | Type | Default | Description | | --- | --- | --- | --- | | `text` | `string` | `''` | Label of the crumb. | | `key` | `string \| undefined` | `—` | Stable identity used in event payloads and DOM ids. | | `value` | `unknown` | `—` | Consumer-defined value carried through click events. | | `url` | `string \| undefined` | `—` | Renders the crumb as a real link (``). | | `hint` | `string \| undefined` | `—` | Tooltip (native `title`). | | `icon` | `string \| undefined` | `—` | SVG path data (`d`) for a leading `aria-hidden` icon. | | `iconClass` | `string \| undefined` | `—` | Class(es) for a leading icon element — the icon-font hook. | | `disabled` | `boolean` | `false` | Disabled crumbs are exposed (`aria-disabled`) but inert. | | `visible` | `boolean` | `true` | `false` removes the crumb entirely. | ### Breadcrumb configuration #### Properties _provideOgeBreadcrumbConfig()_ | Name | Type | Description | | --- | --- | --- | | `messages` | `OgeBreadcrumbMessages` | Every user-facing string: `breadcrumb` (accessible name of the `` landmark, default `Breadcrumb`) and `collapsed` (aria label of the ellipsis button, default `Show hidden items`). | | `collapseMode` | `'auto' \| 'wrap' \| 'none' \| undefined` | Default for the `collapseMode` input. | ### OgePagination — `` #### Properties _OgePagination_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `pageIndex` | `model` | `0` | The current page — **0-based**, two-way. Auto-clamped when the page count shrinks (an implicit `pageIndexChange`, no rich event). DevExtreme migrators: check your origin — dx documentation is ambiguous about its base. | | `pageSize` | `model` | `20` | Items per page — two-way. `0` means "all items on one page" (the grid pager contract, kept aligned for eventual delegation). | | `itemCount` | `number \| undefined` | `—` | Total items. `undefined` = unknown total: only prev/next and a "Page N" indicator render, and **next never disables** — clamp `pageIndex` yourself when the server reports the end. | | `pageSizes` | `readonly (number \| 'all')[] \| undefined` | `—` | Page-size choices; `'all'` adds the unpaged option. **Presence shows the selector** — no separate boolean (DevExtreme's `showPageSizeSelector` is deliberately skipped). | | `showInfo` | `boolean` | `false` | Renders the `{from}–{to} of {itemCount}` range (the `info` message template) in an `aria-live="polite"` region. | | `showFirstLastButtons` | `boolean` | `false` | First/last jump buttons (Material name and default) — the numeric window already renders both rail pages, so they are opt-in chrome. | | `showNavigationButtons` | `boolean` | `true` | Prev/next buttons; forced on in compact and unknown-total modes (they are the backbone there). | | `showJumpToPageInput` | `boolean` | `false` | Jump-to-page input (PrimeNG name; Kendo's `type: 'input'`): 1-based display, Enter/change commit, clamped into range, display re-synced after a clamp. Hidden while the total is unknown. | | `maxButtons` | `number \| undefined` | `7 (config)` | Total rendered slots **including ellipsis slots** — the window width never changes while paging, so the bar never jitters. An ellipsis hiding a single page renders the page instead. | | `displayMode` | `'full' \| 'compact' \| 'adaptive'` | `'full' (config)` | `'compact'` renders the `N / M` indicator; `'adaptive'` switches below `compactBelow` (config, default 480px), measured against the bar's own container via ResizeObserver — never the window. | | `disabled` | `boolean` | `false` | Disables every control (native `disabled` — they are all real buttons/selects/inputs). | | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Density preset (26/32/40px hit targets). | | `messages` | `Partial` | `—` | Per-instance string overrides, merged over the config messages. Several bars on one page need distinct `paginationLabel` values — landmarks must be unique (axe `landmark-unique`). | | `pageCount` | `Signal` | `—` | Readonly derived page count; `undefined` while the total is unknown. DevExtreme's `getPageCount()` as a signal — the house read API. | #### Methods | Name | Type | Description | | --- | --- | --- | | `firstPage() / lastPage() / nextPage() / previousPage()` | `void` | Programmatic paging (Material names). `lastPage()` no-ops while the total is unknown. Model updates only — no rich event (no user event). | | `hasPreviousPage() / hasNextPage()` | `boolean` | `hasNextPage()` returns `true` while the total is unknown — the component cannot know the end. | | `focus()` | `void` | Moves keyboard focus to the first enabled control. | #### Events | Name | Type | Description | | --- | --- | --- | | `pageChanged` | `OgePaginationPageChangedEvent` | `{ pageIndex, previousPageIndex, pageSize, event }` — user interactions only; programmatic writes and auto-clamps update the model without it. | | `pageSizeChanged` | `OgePaginationPageSizeChangedEvent` | `{ pageSize, previousPageSize, pageIndex, event }` — `pageIndex` reports the **post-clamp** page (changing the size can move the current page). | | `pageIndexChange / pageSizeChange` | `number` | The implicit model outputs — fire on every change including programmatic writes and auto-clamps. | #### Types | Name | Type | Description | | --- | --- | --- | | `OgePaginationDisplayMode / OgePaginationSize` | `types` | The string unions of the mode and density inputs. | | `resolvePageWindow / resolvePageRange / resolvePageCount / OGE_PAGE_ELLIPSIS` | `@oge-ui/core` | The DOM-free paging kernel (`pagination-math.ts`): the constant-width page window with real ellipsis markers, the from/to info arithmetic and the never-below-1 page-count division — unit-tested without a DOM. | ### Pagination configuration #### Properties _OgePaginationConfig_ | Name | Type | Description | | --- | --- | --- | | `messages` | `OgePaginationMessages` | All strings: `paginationLabel` (the `` name), `firstPage`/`lastPage`/`previousPage`/`nextPage`, `pageLabel` (`{page}`, 1-based), `info` (`{from}` `{to}` `{itemCount}`), `pageInfoUnknown`, `pageIndicator` (`{page}` `{pageCount}`), `pageSizeLabel`, `allRows`, `jumpLabel`. | | `displayMode / compactBelow / maxButtons` | `OgePaginationDisplayMode / number / number` | Application-wide input defaults; the component resolves `input ?? config ?? literal` (480px / 7). | #### Methods | Name | Type | Description | | --- | --- | --- | | `provideOgePaginationConfig(config: OgePaginationConfigInput)` | `Provider` | Application- or component-scoped defaults; shallow-merges `messages` over the built-ins. | ### Demos Each demo below is a complete standalone component — copy one whole and it compiles. #### Breadcrumb routed ```ts import { ChangeDetectionStrategy, Component, computed, inject, signal } from '@angular/core'; import { OgeBreadcrumb } from '@oge-ui/navigation'; import { RouterOutlet, NavigationEnd, Router } from '@angular/router'; import type { OgeBreadcrumbItemData, OgeBreadcrumbItemClickEvent } from '@oge-ui/navigation'; @Component({ selector: 'demo-root', imports: [OgeBreadcrumb, RouterOutlet], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { private readonly router = inject(Router); private readonly url = signal(this.router.url); /** One crumb per URL segment — the last one is the current page. */ protected readonly trail = computed(() => { const segments = this.url().split(/[?#]/)[0].split('/').filter(Boolean); return segments.map((segment, index) => ({ text: segment, key: segment, url: '/' + segments.slice(0, index + 1).join('/'), })); }); constructor() { this.router.events.subscribe((event) => { if (event instanceof NavigationEnd) this.url.set(event.urlAfterRedirects); }); } protected go(event: OgeBreadcrumbItemClickEvent): void { event.event.preventDefault(); if (event.item.url) void this.router.navigateByUrl(event.item.url); } } ``` #### Basic ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeBreadcrumb } from '@oge-ui/navigation'; import type { OgeBreadcrumbItemData, OgeBreadcrumbItemClickEvent } from '@oge-ui/navigation'; @Component({ selector: 'demo-root', imports: [OgeBreadcrumb], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly trail: OgeBreadcrumbItemData[] = [ { text: 'Home', key: 'home', url: '/', icon: 'M2 8 8 2l6 6M4 7v7h8V7' }, { text: 'Products', key: 'products', url: '/products' }, { text: 'Keyboards', key: 'keyboards', url: '/products/keyboards' }, { text: 'Mechanical' }, ]; protected go(event: OgeBreadcrumbItemClickEvent): void { console.log(event.key, event.index); } } ``` #### Collapse ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeBreadcrumb } from '@oge-ui/navigation'; import type { OgeBreadcrumbItemData } from '@oge-ui/navigation'; @Component({ selector: 'demo-root', imports: [OgeBreadcrumb], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly trail: OgeBreadcrumbItemData[] = [ { text: 'Home', url: '/' }, { text: 'Products', url: '/products' }, { text: 'Peripherals', url: '/products/peripherals' }, { text: 'Keyboards', url: '/products/keyboards' }, { text: 'Mechanical' }, ]; } ``` #### Config ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeBreadcrumb, provideOgeBreadcrumbConfig } from '@oge-ui/navigation'; import type { OgeBreadcrumbItemData } from '@oge-ui/navigation'; // Every user-facing string lives in the messages block — the nav // landmark's label and the ellipsis button's label included. export const BREADCRUMB_PROVIDERS = [ provideOgeBreadcrumbConfig({ collapseMode: 'auto', messages: { breadcrumb: 'İçerik haritası', collapsed: 'Gizli öğeleri göster' }, }), ]; @Component({ selector: 'demo-root', imports: [OgeBreadcrumb], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly trail: OgeBreadcrumbItemData[] = [ { text: 'Giriş', url: '/' }, { text: 'Raporlar' }, ]; } ``` #### Declarative ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeBreadcrumb, OgeBreadcrumbItem } from '@oge-ui/navigation'; @Component({ selector: 'demo-root', imports: [OgeBreadcrumb, OgeBreadcrumbItem], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo {} ``` #### Templates ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeBreadcrumb, OgeBreadcrumbItemTemplate, OgeBreadcrumbSeparatorTemplate } from '@oge-ui/navigation'; import type { OgeBreadcrumbItemData } from '@oge-ui/navigation'; @Component({ selector: 'demo-root', imports: [OgeBreadcrumb, OgeBreadcrumbItemTemplate, OgeBreadcrumbSeparatorTemplate], changeDetection: ChangeDetectionStrategy.OnPush, template: ` {{ item.text }} · `, }) export class Demo { protected readonly trail: OgeBreadcrumbItemData[] = [ { text: 'Home', url: '/' }, { text: 'Library', url: '/library' }, { text: 'Data' }, ]; } ``` #### App shell ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeDrawer, OgeTreeView } from '@oge-ui/navigation'; import { OgeToolbar, OgeToolbarItem, OgeSplitter, OgeSplitterPane } from '@oge-ui/layout'; @Component({ selector: 'demo-root', imports: [OgeDrawer, OgeTreeView, OgeToolbar, OgeToolbarItem, OgeSplitter, OgeSplitterPane], changeDetection: ChangeDetectionStrategy.OnPush, template: ` Rows… Details… `, }) export class Demo { protected readonly menuOpen = signal(true); protected readonly sizes = signal([60, 40]); protected readonly nav = [ { id: 1, parentId: null, text: 'Reports' }, { id: 2, parentId: 1, text: 'Monthly' }, { id: 3, parentId: null, text: 'Settings' }, ]; } ``` #### Compact ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeDrawer } from '@oge-ui/navigation'; import type { OgeDrawerModeChangedEvent } from '@oge-ui/navigation'; @Component({ selector: 'demo-root', imports: [OgeDrawer], changeDetection: ChangeDetectionStrategy.OnPush, template: `
Navigation
Content
`, }) export class Demo { protected readonly opened = signal(true); protected onMode(event: OgeDrawerModeChangedEvent): void { console.log(event.mode, 'compact:', event.compact); } } ``` #### Config ```ts import { provideOgeDrawerConfig } from '@oge-ui/navigation'; bootstrapApplication(App, { providers: [ provideOgeDrawerConfig({ mode: 'side', size: 280, messages: { drawer: 'Gezinme', close: 'Kapat' }, }), ], }); ``` #### Guard ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeDrawer } from '@oge-ui/navigation'; @Component({ selector: 'demo-root', imports: [OgeDrawer], changeDetection: ChangeDetectionStrategy.OnPush, template: `
Unsaved edits…
Content
`, }) export class Demo { protected readonly opened = signal(true); protected readonly dirty = signal(true); protected readonly confirmDiscard = (): boolean => !this.dirty() || confirm('Discard your changes?'); } ``` #### Modal ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeDrawer } from '@oge-ui/navigation'; @Component({ selector: 'demo-root', imports: [OgeDrawer], changeDetection: ChangeDetectionStrategy.OnPush, template: `
Content
`, }) export class Demo { protected readonly opened = signal(false); } ``` #### Modes ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeDrawer } from '@oge-ui/navigation'; import type { OgeDrawerMode } from '@oge-ui/navigation'; @Component({ selector: 'demo-root', imports: [OgeDrawer], changeDetection: ChangeDetectionStrategy.OnPush, template: `
Navigation…
Content that overlay covers, push shifts and side shrinks.
`, }) export class Demo { protected readonly opened = signal(true); protected readonly mode = signal('side'); } ``` #### Position ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeDrawer } from '@oge-ui/navigation'; import type { OgeDrawerPosition } from '@oge-ui/navigation'; @Component({ selector: 'demo-root', imports: [OgeDrawer], changeDetection: ChangeDetectionStrategy.OnPush, template: `
Panel
Content
`, }) export class Demo { protected readonly opened = signal(false); protected readonly position = signal('start'); } ``` #### Rail ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeDrawer } from '@oge-ui/navigation'; @Component({ selector: 'demo-root', imports: [OgeDrawer], changeDetection: ChangeDetectionStrategy.OnPush, template: `
Icons, then labels once open
Content
`, }) export class Demo { protected readonly opened = signal(false); } ``` #### Menubar routed ```ts import { ChangeDetectionStrategy, Component, computed, inject, signal } from '@angular/core'; import { OgeMenubar } from '@oge-ui/navigation'; import { RouterOutlet, NavigationEnd, Router } from '@angular/router'; import type { OgeMenubarItemData, OgeMenubarItemClickEvent } from '@oge-ui/navigation'; @Component({ selector: 'demo-root', imports: [OgeMenubar, RouterOutlet], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { private readonly router = inject(Router); protected readonly menu: OgeMenubarItemData[] = [ { text: 'Overview', key: 'overview', url: '/app/overview' }, { text: 'Members', key: 'members', url: '/app/members' }, { text: 'Reports', items: [ { text: 'Monthly', key: 'monthly' }, { text: 'Annual', key: 'annual' }, ], }, ]; private readonly url = signal(this.router.url); protected readonly activeKey = computed( () => this.url().split('/').pop() ?? 'overview', ); constructor() { this.router.events.subscribe((event) => { if (event instanceof NavigationEnd) this.url.set(event.urlAfterRedirects); }); } protected go(event: OgeMenubarItemClickEvent): void { event.event.preventDefault(); if (event.key) void this.router.navigate(['/app', event.key]); } } ``` #### Basic ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeMenubar } from '@oge-ui/navigation'; import type { OgeMenubarItemData, OgeMenubarItemClickEvent } from '@oge-ui/navigation'; @Component({ selector: 'demo-root', imports: [OgeMenubar], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly menu: OgeMenubarItemData[] = [ { text: 'File', items: [ // shortcut renders right-aligned and announces aria-keyshortcuts; // the actual key binding stays the application's job. { text: 'New', key: 'new', shortcut: 'Ctrl+N' }, { text: 'Open…', key: 'open', shortcut: 'Ctrl+O' }, { separator: true, text: '' }, { text: 'Share', badge: 2, items: [{ text: 'Email', key: 'email' }] }, ], }, { text: 'Edit', items: [{ text: 'Undo', key: 'undo', shortcut: 'Ctrl+Z' }] }, { text: 'Help', key: 'help' }, ]; protected run(event: OgeMenubarItemClickEvent): void { console.log(event.key, event.path); } } ``` #### Compact ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeMenubar } from '@oge-ui/navigation'; import type { OgeMenubarItemData, OgeMenubarCompactChangedEvent } from '@oge-ui/navigation'; @Component({ selector: 'demo-root', imports: [OgeMenubar], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly menu: OgeMenubarItemData[] = [ { text: 'File', items: [{ text: 'New' }] }, { text: 'Edit', items: [{ text: 'Undo' }] }, { text: 'Help', key: 'help' }, ]; protected onCompact(event: OgeMenubarCompactChangedEvent): void { console.log('compact:', event.compact); } } ``` #### Config ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeMenubar, provideOgeMenubarConfig } from '@oge-ui/navigation'; import type { OgeMenubarItemData } from '@oge-ui/navigation'; // Every user-facing string lives in the messages block — the bar's // accessible name and the compact hamburger's label included. export const MENUBAR_PROVIDERS = [ provideOgeMenubarConfig({ openMode: 'hover', compactBelow: 480, messages: { menubar: 'Ana menü', hamburger: 'Menü' }, }), ]; @Component({ selector: 'demo-root', imports: [OgeMenubar], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly menu: OgeMenubarItemData[] = [ { text: 'Dosya', items: [{ text: 'Yeni' }] }, ]; } ``` #### Declarative ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeMenubar, OgeMenubarItem } from '@oge-ui/navigation'; @Component({ selector: 'demo-root', imports: [OgeMenubar, OgeMenubarItem], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo {} ``` #### Events ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeMenubar } from '@oge-ui/navigation'; import type { OgeMenubarItemData, OgeMenubarSubmenuOpeningEvent, OgeMenubarSubmenuClosingEvent } from '@oge-ui/navigation'; @Component({ selector: 'demo-root', imports: [OgeMenubar], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly locked = signal(false); protected readonly menu: OgeMenubarItemData[] = [ { text: 'File', key: 'file', items: [{ text: 'New' }] }, ]; protected onOpening(event: OgeMenubarSubmenuOpeningEvent): void { if (this.locked()) event.cancel = true; } protected onClosing(event: OgeMenubarSubmenuClosingEvent): void { if (this.locked() && event.reason !== 'tab') event.cancel = true; } } ``` #### Open mode ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeMenubar } from '@oge-ui/navigation'; import type { OgeMenubarItemData } from '@oge-ui/navigation'; @Component({ selector: 'demo-root', imports: [OgeMenubar], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly menu: OgeMenubarItemData[] = [ { text: 'File', items: [{ text: 'New' }] }, { text: 'Edit', items: [{ text: 'Undo' }] }, ]; } ``` #### Vertical ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeMenubar } from '@oge-ui/navigation'; import type { OgeMenubarItemData } from '@oge-ui/navigation'; @Component({ selector: 'demo-root', imports: [OgeMenubar], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly menu: OgeMenubarItemData[] = [ { text: 'Dashboard', key: 'dashboard' }, { text: 'Reports', items: [{ text: 'Monthly' }, { text: 'Annual' }] }, { text: 'Settings', items: [{ text: 'Profile' }] }, ]; } ``` #### Check ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeTreeView } from '@oge-ui/navigation'; import type { RowKey } from '@oge-ui/core'; interface Folder { id: number; parentId: number | null; name: string; hasItems?: boolean; } @Component({ selector: 'demo-root', imports: [OgeTreeView], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly folders: Folder[] = [ { id: 1, parentId: null, name: 'Documents' }, { id: 2, parentId: 1, name: 'Reports' }, { id: 3, parentId: 2, name: 'Q1.pdf' }, ]; protected readonly picked = signal([]); } ``` #### Dnd ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeTreeView } from '@oge-ui/navigation'; import type { OgeTreeReorderedEvent } from '@oge-ui/navigation'; interface Folder { id: number; parentId: number | null; name: string; hasItems?: boolean; } @Component({ selector: 'demo-root', imports: [OgeTreeView], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly folders = signal([ { id: 1, parentId: null, name: 'Documents' }, { id: 2, parentId: 1, name: 'Reports' }, ]); // the tree never mutates your data; apply the move yourself protected reparent(e: OgeTreeReorderedEvent): void { this.folders.update((rows) => rows.map((row) => row.id === e.dragKey ? { ...row, parentId: e.position === 'inside' ? (e.dropKey as number) : e.dropItem.parentId, } : row, ), ); } } ``` #### Flat ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeTreeView } from '@oge-ui/navigation'; import type { RowKey } from '@oge-ui/core'; interface Folder { id: number; parentId: number | null; name: string; hasItems?: boolean; } @Component({ selector: 'demo-root', imports: [OgeTreeView], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly folders: Folder[] = [ { id: 1, parentId: null, name: 'Documents' }, { id: 2, parentId: 1, name: 'Reports' }, { id: 3, parentId: 2, name: 'Q1.pdf' }, ]; protected readonly open = signal([1]); protected readonly picked = signal([]); } ``` #### Lazy ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeTreeView } from '@oge-ui/navigation'; interface Folder { id: number; parentId: number | null; name: string; hasItems?: boolean; } @Component({ selector: 'demo-root', imports: [OgeTreeView], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly roots: Folder[] = [ { id: 1, parentId: null, name: 'Documents', hasItems: true }, ]; // a skeleton row shows while the promise is pending protected readonly loadChildren = (parent: Folder): Promise => fetch(`/api/folders?parent=${parent.id}`).then((r) => r.json()); } ``` #### Nested ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeTreeView } from '@oge-ui/navigation'; interface NestedFolder { id: number; name: string; children?: NestedFolder[]; } @Component({ selector: 'demo-root', imports: [OgeTreeView], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly tree: NestedFolder[] = [ { id: 1, name: 'src', children: [{ id: 2, name: 'app', children: [{ id: 3, name: 'main.ts' }] }], }, ]; } ``` #### Search ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeTreeView } from '@oge-ui/navigation'; interface Folder { id: number; parentId: number | null; name: string; hasItems?: boolean; } @Component({ selector: 'demo-root', imports: [OgeTreeView], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly folders: Folder[] = [ { id: 1, parentId: null, name: 'Documents' }, { id: 2, parentId: 1, name: 'Reports' }, { id: 3, parentId: 2, name: 'Q1.pdf' }, ]; } ``` #### Template ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeTreeItemTemplate, OgeTreeView } from '@oge-ui/navigation'; interface Folder { id: number; parentId: number | null; name: string; hasItems?: boolean; } @Component({ selector: 'demo-root', imports: [OgeTreeItemTemplate, OgeTreeView], changeDetection: ChangeDetectionStrategy.OnPush, template: ` {{ item.name }} level {{ level }} `, }) export class Demo { protected readonly folders: Folder[] = [ { id: 1, parentId: null, name: 'Documents' }, { id: 2, parentId: 1, name: 'Reports' }, { id: 3, parentId: 2, name: 'Q1.pdf' }, ]; } ``` #### Virtual ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeTreeView } from '@oge-ui/navigation'; interface Folder { id: number; parentId: number | null; name: string; hasItems?: boolean; } @Component({ selector: 'demo-root', imports: [OgeTreeView], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly tenThousand: Folder[] = Array.from( { length: 10000 }, (_, i) => ({ id: i + 1, parentId: null, name: `Item ${i + 1}` }), ); } ``` #### Adaptive ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgePagination } from '@oge-ui/navigation'; @Component({ selector: 'demo-root', imports: [OgePagination], changeDetection: ChangeDetectionStrategy.OnPush, template: `
`, }) export class Demo { protected readonly page = signal(4); protected readonly total = 400; } ``` #### Basic ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgePagination } from '@oge-ui/navigation'; @Component({ selector: 'demo-root', imports: [OgePagination], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly page = signal(0); protected readonly total = 400; } ``` #### Config ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgePagination, provideOgePaginationConfig } from '@oge-ui/navigation'; // Application- or component-scoped defaults; per-instance [messages] wins. // providers: [ // provideOgePaginationConfig({ // maxButtons: 9, // messages: { pageSizeLabel: 'Sayfa başına', info: '{itemCount} kayıttan {from}–{to}' }, // }), // ] @Component({ selector: 'demo-root', imports: [OgePagination], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly page = signal(0); protected readonly total = 250; } ``` #### Jump ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgePagination } from '@oge-ui/navigation'; @Component({ selector: 'demo-root', imports: [OgePagination], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly page = signal(41); protected readonly total = 1000; } ``` #### Sizes ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgePagination } from '@oge-ui/navigation'; @Component({ selector: 'demo-root', imports: [OgePagination], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly page = signal(0); protected readonly size = signal(20); protected readonly total = 97; } ``` #### Unknown ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgePagination } from '@oge-ui/navigation'; @Component({ selector: 'demo-root', imports: [OgePagination], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly page = signal(3); } ``` #### Basic ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeStepper, OgeStep } from '@oge-ui/navigation'; @Component({ selector: 'demo-root', imports: [OgeStepper, OgeStep], changeDetection: ChangeDetectionStrategy.OnPush, template: ` Account fields… Shipping fields… Confirm and submit… `, }) export class Demo { protected readonly step = signal(0); protected readonly userIcon = 'M8 7.5A2.75 2.75 0 1 0 8 2a2.75 2.75 0 0 0 0 5.5ZM2.5 14c0-3 2.5-4.5 5.5-4.5s5.5 1.5 5.5 4.5Z'; } ``` #### Config ```ts import { provideOgeStepperConfig } from '@oge-ui/navigation'; bootstrapApplication(App, { providers: [ provideOgeStepperConfig({ linear: true, messages: { next: 'İleri', previous: 'Geri', finish: 'Bitir' }, }), ], }); ``` #### Form ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeForm, OgeFormItem, OgeFormGroup, OgeFormSteps } from '@oge-ui/forms'; @Component({ selector: 'demo-root', imports: [OgeForm, OgeFormItem, OgeFormGroup, OgeFormSteps], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly order = signal({ email: '', card: '' }); } ``` #### Guard ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeStepper, OgeStep } from '@oge-ui/navigation'; @Component({ selector: 'demo-root', imports: [OgeStepper, OgeStep], changeDetection: ChangeDetectionStrategy.OnPush, template: ` Details… Done… `, }) export class Demo { protected readonly step = signal(0); protected readonly dirty = signal(true); protected readonly confirmLeave = (): boolean => !this.dirty() || confirm('Discard your changes?'); } ``` #### Linear ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeStepper, OgeStep } from '@oge-ui/navigation'; import type { OgeStepBlockedEvent } from '@oge-ui/navigation'; @Component({ selector: 'demo-root', imports: [OgeStepper, OgeStep], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly step = signal(0); protected readonly accountDone = signal(false); protected readonly paymentDone = signal(false); protected onBlocked(event: OgeStepBlockedEvent): void { console.log('refused because', event.reason); } } ``` #### Nav ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeStepper, OgeStep, OgeStepperNext, OgeStepperPrevious } from '@oge-ui/navigation'; @Component({ selector: 'demo-root', imports: [OgeStepper, OgeStep, OgeStepperNext, OgeStepperPrevious], changeDetection: ChangeDetectionStrategy.OnPush, template: ` First body… Second body… `, }) export class Demo { protected readonly step = signal(0); } ``` #### State ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeStepper, OgeStep } from '@oge-ui/navigation'; @Component({ selector: 'demo-root', imports: [OgeStepper, OgeStep], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo {} ``` #### Vertical ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeStepper, OgeStep } from '@oge-ui/navigation'; import type { OgeStepperOrientation } from '@oge-ui/navigation'; @Component({ selector: 'demo-root', imports: [OgeStepper, OgeStep], changeDetection: ChangeDetectionStrategy.OnPush, template: ` First body… Second body… `, }) export class Demo { protected readonly step = signal(0); protected readonly orientation = signal('vertical'); } ``` ## @oge-ui/pivot Cross-tab analytics: rows × columns × measures, grand totals, field chooser, sorting and Excel export. **This package is commercially licensed — unlike the rest of the suite, it is not MIT. See https://ogeui.com/license before shipping it.** Docs: https://ogeui.com/components/pivot-grid ### Entry points `@oge-ui/pivot` - values: `OGE_DEFAULT_PIVOT_MESSAGES`, `OGE_PIVOT_FIELD_DRAG_TYPE`, `OGE_PIVOT_MESSAGES`, `OgePivotField`, `OgePivotGrid`, `OgePivotStateStore`, `provideOgePivotMessages` - types: `OgePivotAxisLine`, `OgePivotCellClickEvent`, `OgePivotCellPrepared`, `OgePivotHeaderCell`, `OgePivotMenuItem`, `OgePivotMessages` `@oge-ui/pivot/export-excel` - values: `buildPivotWorkbook`, `exportPivotToExcel` - types: `OgePivotExcelExportOptions` ### OgePivotGrid — `` #### Properties | Name | Type | Default | Description | | --- | --- | --- | --- | | `data` | `readonly T[] \| OgePivotStore` | `[]` | Local rows, or any `OgePivotStore` for remote (pre-aggregated) data. | | `virtualScrolling` | `boolean` | `false` | Two-axis fixed-track windowing. | | `showRowTotals / showColumnTotals` | `boolean` | `true` | Sub-total lines per axis. | | `showRowGrandTotals / showColumnGrandTotals` | `boolean` | `true` | Grand-total lines per axis. | | `fieldPanel` | `boolean` | `true` | Collapsible drag & drop field panel. | | `fieldChooser` | `{ applyChangesMode?: 'instantly' \| 'onDemand' }` | `{}` | Field-chooser dialog behavior. | | `customizeCell` | `(cell: OgePivotCellPrepared) => void` | `—` | Appearance hook: mutate `text` / `cssClass` per cell (the reference `cellPrepared` equivalent). | | `stateKey` | `string \| undefined` | `—` | Persists the field layout + expansion via `OGE_STATE_STORAGE`. | | `messages` | `Partial` | `{}` | Per-instance overrides of the UI strings. | #### Methods | Name | Type | Description | | --- | --- | --- | | `getResult(): PivotResult` | `PivotResult` | The materialized pivot exactly as rendered — for custom export integrations. | | `drillDown(args: PivotDrillDownArgs): T[]` | `T[]` | Raw rows behind a cell (local data only). | | `expandAll(area: 'row' \| 'column') / collapseAll(area)` | `void` | Axis-wide expansion; remote mode expands only what is loaded. | | `getFieldLayout(): readonly PivotFieldConfig[]` | `readonly PivotFieldConfig[]` | Declared fields merged with user overrides. | | `showFieldChooser(): void` | `void` | Opens the field-chooser dialog. | | `state() / applyState(snapshot)` | `PivotGridStateSnapshot / void` | Field layout + expansion snapshot. | | `getCsv(options?) / exportCsv(filename?)` | `string / void` | CSV of exactly what is on screen (multi-level headers flattened). | #### Events | Name | Type | Description | | --- | --- | --- | | `cellClick / cellDblClick` | `OgePivotCellClickEvent` | `{ rowPath, columnPath, measureIndex, value, event }`. | | `fieldLayoutChange` | `readonly PivotFieldConfig[]` | The field layout changed (drag, chooser, menus). | | `stateChange` | `PivotGridStateSnapshot` | Debounced — the persistable state changed. | #### Types _Cell & axis types_ | Name | Type | Description | | --- | --- | --- | | `OgePivotCellPrepared` | `{ rowPath, columnPath, measureId, isTotal, isGrandTotal, value; mutable text, cssClass? }` | Args of the `customizeCell` hook. | | `OgePivotAxisLine` | `{ text, path, level, expanded, hasChildren, isTotal, isGrandTotal }` | One visible axis line, in matrix order. | | `OgePivotHeaderCell` | `OgePivotAxisLine & { rowStart, rowEnd, columnStart, span }` | Header cell with 1-based matrix coordinates. | | `OGE_PIVOT_FIELD_DRAG_TYPE` | `'application/x-oge-pivot-field'` | DataTransfer type of field chips. | _Configuration & engine_ | Name | Type | Description | | --- | --- | --- | | `provideOgePivotMessages(messages)` | `Provider` | App-scoped overrides of `OgePivotMessages` (39 keys — areas, menus, chooser, export…). | | `PivotFieldConfig / PivotResult / PivotLoadOptions / OgePivotStore…` | `from @oge-ui/core` | The serializable engine contract lives in `@oge-ui/core`, not in this package. | | `exportPivotToExcel(grid, options?)` | `@oge-ui/pivot/export-excel` | Lazy Excel export with merged multi-level headers; `buildPivotWorkbook(result)` for custom pipelines. | ### OgePivotField — `` #### Properties _Internals — not a supported API_ | Name | Type | Description | | --- | --- | --- | | `OgePivotStateStore` | `component-scoped service` | UI state of a pivot grid: field-layout overrides on top of the declared `` configuration, the expansion of both axes (kept as key → path so remote contracts get real paths back) and the field-panel collapse flag. Injected by the grid — applications should use `stateKey` or `state()`/`applyState()`. | _Placement_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `dataField` | `string (required)` | `—` | Source field; dotted paths supported. | | `id` | `string \| undefined` | `—` | Stable field id (defaults to `dataField`). | | `caption` | `string \| undefined` | `—` | Chip/header label. | | `area` | `PivotArea \| null` | `null` | row \| column \| data \| filter; `null` keeps the field available in the chooser only. | | `areaIndex` | `number \| undefined` | `—` | Order within the area. | | `dataType` | `'string' \| 'number' \| 'date' \| 'boolean' \| undefined` | `—` | Drives group intervals and formatting. | | `groupInterval` | `PivotGroupInterval \| undefined` | `—` | year/quarter/month/day/dayOfWeek or a numeric bucket size. | _Measures (area="data")_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `summaryType` | `SummaryType` | `'sum'` | sum/avg/min/max/count/custom. | | `summaryName` | `string \| undefined` | `—` | Registered custom-summary name. | | `summaryDisplayMode` | `PivotSummaryDisplayMode` | `'none'` | percent-of/running-total/variation post-processing. | | `runningTotal` | `PivotRunningTotal \| undefined` | `—` | Running totals with per-group reset. | | `calculateCustomSummary` | `CustomSummaryFn \| undefined` | `—` | Out-of-band custom reducer. | _Row/column fields_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `sortOrder` | `SortDirection \| undefined` | `—` | Label sort. | | `sortBySummaryField / sortBySummaryPath` | `string / PivotPath` | `—` | Sort by a summary value at an opposite-axis path. | | `filterValues / filterType` | `readonly unknown[] / 'include' \| 'exclude'` | `—` | Field filter. | | `showTotals` | `boolean` | `true` | Sub-totals for this field. | | `selector / format / customizeText` | `functions` | `—` | Out-of-band value selector, display formatter and text hook. | ### Demos Each demo below is a complete standalone component — copy one whole and it compiles. #### Analytics ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgePivotField, OgePivotGrid } from '@oge-ui/pivot'; @Component({ selector: 'demo-root', imports: [OgePivotField, OgePivotGrid], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly sales = [ { region: 'EMEA', date: '2026-02-11', amount: 1249 }, { region: 'EMEA', date: '2026-05-02', amount: 890 }, { region: 'APAC', date: '2025-11-19', amount: 2140 }, ]; } ``` #### Export ```ts import { ChangeDetectionStrategy, Component, viewChild } from '@angular/core'; import { OgePivotField, OgePivotGrid } from '@oge-ui/pivot'; @Component({ selector: 'demo-root', imports: [OgePivotField, OgePivotGrid], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly sales = [ { region: 'EMEA', date: '2026-02-11', amount: 1249 }, { region: 'EMEA', date: '2026-05-02', amount: 890 }, { region: 'APAC', date: '2025-11-19', amount: 2140 }, ]; private readonly pivot = viewChild.required(OgePivotGrid); // CSV ships in the package… protected exportCsv(): void { this.pivot().exportCsv('sales.csv'); } // …Excel lives in a lazy secondary entry, so exceljs stays out of the bundle protected async exportExcel(): Promise { const { exportPivotToExcel } = await import('@oge-ui/pivot/export-excel'); await exportPivotToExcel(this.pivot(), { filename: 'sales.xlsx' }); } } ``` #### Overview ```ts import { ChangeDetectionStrategy, Component, viewChild } from '@angular/core'; import { OgePivotField, OgePivotGrid } from '@oge-ui/pivot'; import type { OgePivotCellClickEvent } from '@oge-ui/pivot'; @Component({ selector: 'demo-root', imports: [OgePivotField, OgePivotGrid], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly sales = [ { region: 'EMEA', country: 'Germany', city: 'Berlin', date: '2026-02-11', amount: 1249 }, { region: 'EMEA', country: 'Türkiye', city: 'İzmir', date: '2026-05-02', amount: 890 }, { region: 'APAC', country: 'Japan', city: 'Tokyo', date: '2025-11-19', amount: 2140 }, ]; private readonly pivot = viewChild.required(OgePivotGrid); protected readonly money = (value: unknown): string => Number(value).toLocaleString('de-DE', { style: 'currency', currency: 'EUR' }); // every cell knows its coordinates — perfect for drill-down protected onDrillDown(event: OgePivotCellClickEvent): void { const rows = this.pivot().drillDown({ rowPath: event.rowPath, columnPath: event.columnPath, }); console.log(rows); } } ``` ## @oge-ui/bpmn From-scratch BPMN 2.0 modeler: its own dependency-free XML + diagram-interchange engine, orthogonal routing, snapping, undo/redo, a keyboard-accessible SVG canvas and no watermark. **This package is commercially licensed — unlike the rest of the suite, it is not MIT. See https://ogeui.com/license before shipping it.** Docs: https://ogeui.com/components/bpmn ### Entry points `@oge-ui/bpmn` - values: `OGE_BPMN_CONFIG`, `OGE_DEFAULT_BPMN_COLOR_PRESETS`, `OGE_DEFAULT_BPMN_CONFIG`, `OGE_DEFAULT_BPMN_MESSAGES`, `OgeBpmnEditor`, `VALID_EVENT_DEFINITIONS`, `alignElements`, `createEmptyDiagram`, `distributeElements`, `fromBpmnJson`, `provideOgeBpmnConfig`, `readBpmnXml`, `renderDiagramSvg`, `toBpmnJson`, `writeBpmnXml` - types: `BpmnActivityMarker`, `BpmnAlignMode`, `BpmnClipboard`, `BpmnDataNodeType`, `BpmnDiagram`, `BpmnDiagramJson`, `BpmnDistributeAxis`, `BpmnEdge`, `BpmnEdgeType`, `BpmnElementNameKey`, `BpmnEventDefinitionKind`, `BpmnImportResult`, `BpmnImportWarning`, `BpmnImportWarningCode`, `BpmnJsonParseResult`, `BpmnLane`, `BpmnMessageFlow`, `BpmnNode`, `BpmnNodeType`, `BpmnPaletteItemType`, `BpmnPool`, `BpmnSubProcessType`, `BpmnSvgExportOptions`, `OgeBpmnAnnouncementMessages`, `OgeBpmnChangeSource`, `OgeBpmnConfig`, `OgeBpmnConfigInput`, `OgeBpmnContextPadMessages`, `OgeBpmnDiagramChangedEvent`, `OgeBpmnElementInfo`, `OgeBpmnElementsChangedEvent`, `OgeBpmnHeaderMessages`, `OgeBpmnImportEvent`, `OgeBpmnMessages`, `OgeBpmnOverlay`, `OgeBpmnPaletteItem`, `OgeBpmnPropertiesMessages`, `OgeBpmnSelectionEvent`, `Point`, `Rect` ### OgeBpmnEditor — `` #### Properties | Name | Type | Default | Description | | --- | --- | --- | --- | | `readOnly` | `boolean` | `false` | Disables every mutation: palette, context pad, keyboard editing and drags. Selection, pan, zoom and element search keep working. | | `gridVisible` | `boolean` | `true` | Shows the dotted background grid. | | `snapEnabled` | `boolean` | `true` | Enables grid and neighbor-alignment snapping (with guide lines) while moving and placing. | | `paletteItems` | `readonly BpmnPaletteItemType[]` | `all 18 entries` | Palette entries offered, in render order: every placeable node type plus the `'pool'` pseudo-entry, which creates a collaboration participant. Event sub-processes and transactions are reached by morphing a sub-process via the panel type select. | | `showPropertiesPanel` | `boolean` | `true` | Shows the right-side properties panel (always hidden in `readOnly`). | | `showMinimap` | `boolean` | `true` | Shows the bottom-right minimap overlay (hidden while the diagram is empty). Click or drag the minimap to pan the main viewport. | | `showHeader` | `boolean` | `true` | Shows the header toolbar: the inline-editable diagram name, undo/redo, zoom out / percentage (fit) / zoom in, the properties-panel collapse toggle, the optional edit/view mode toggle and the fullscreen button (native Fullscreen API with a fixed-position maximized fallback). | | `mode` | `model<'edit' \| 'view'>` | `'edit'` | The UI mode — two-way. `'view'` locks every mutating surface exactly like `readOnly`; zoom, pan, search and fullscreen stay available. | | `allowModeToggle` | `boolean` | `false` | Shows the edit/view toggle in the header (hidden while `readOnly` — the app-level lock always wins). | | `showBranding` | `boolean` | `true` | The badge in the canvas corner — a bare logo with no chrome, removable exclusively from code via `false`. Branding is a courtesy, never a license term (unlike bpmn-js's mandatory watermark). | | `brandLogoUrl` | `string \| undefined` | `—` | Badge image URL (per instance, or app-wide via `provideOgeBpmnConfig({ brandLogoUrl })`); unset renders the built-in drawn mark — no bundled bitmap, no network dependency by default. | | `Panel resizing` | `built-in` | `—` | The palette rail and the properties panel carry `role="separator"` drag handles — pointer drag (Escape cancels) or Tab + Arrow keys / Home / End, the APG window-splitter keys. The header panel-toggle collapses the properties panel entirely. | | `messages` | `Partial` | `{}` | Per-instance message overrides, merged over the `provideOgeBpmnConfig()` defaults. | | `zoom` | `model` | `1` | Two-way zoom factor (`[(zoom)]`); wheel zooming writes it back. Clamped to the configured `zoomMin`/`zoomMax`. | _Canvas keyboard_ | Name | Type | Description | | --- | --- | --- | | `role="application" canvas` | `Tab / Shift+Tab · arrows (Shift = 1px) · C · A · H / L / S · F2 / Enter · Delete · Ctrl+Z / Ctrl+Y · Ctrl+C / Ctrl+X / Ctrl+V · Ctrl+A · Ctrl+F · + / − / F · Escape` | Tab cycles elements (never trapped on an empty selection — leave with Escape then Tab, announced in the canvas hint), arrows move the selection by one grid step, `C` arms the connect tool (Tab/arrows walk candidate targets, Enter commits), `A` appends a connected task, `H`/`L`/`S` switch to the hand / lasso / space tool (edit mode only), `F` zooms to fit, `F2`/Enter edit the label, Ctrl+C/X/V copy/cut/paste via the internal clipboard, Ctrl+A selects all, Ctrl+F opens the element search overlay, Escape cancels the active tool or drag. The selected element is exposed via `aria-activedescendant` and every action is narrated in a polite live region. | #### Methods _Import & export_ | Name | Type | Description | | --- | --- | --- | | `importXml(xml: string)` | `Promise` | Parses BPMN XML and loads it into the editor, resetting undo history and fitting the viewport. Resolves with the import result (model + warnings); on a fatal parse error the current diagram is left untouched. | | `exportXml()` | `string` | Serializes the current diagram to deterministic BPMN 2.0 XML — same model, same bytes. | | `exportJson()` | `BpmnDiagramJson` | Wraps the current diagram in the versioned JSON persistence envelope — the shape to store in an application database and the payload of the `diagramChanged` autosave stream. | | `importJson(value: unknown)` | `{ error?: string }` | Validates a JSON persistence envelope (see `fromBpmnJson`) and loads it, resetting undo history and fitting the viewport exactly like `importXml`. On a validation error the current diagram is left untouched and the error message is returned. | | `exportSvg()` | `string` | Renders the current diagram as a self-contained static SVG string (neutral hardcoded colors, no grid or selection, `viewBox` fitted to the content) via `renderDiagramSvg` — writable to a file or embeddable as-is. | | `newDiagram()` | `void` | Replaces the diagram with an empty one and resets history and viewport. | _Selection, history & navigation_ | Name | Type | Description | | --- | --- | --- | | `zoomToFit()` | `void` | Fits and centers the whole diagram in the canvas. | | `centerOn(id: string)` | `void` | Pans the viewport (keeping the current zoom) so the given element is centered in the canvas. Unknown ids are ignored. Used by the element search overlay; public for app-driven navigation. | | `select(ids: readonly string[])` | `void` | Selects the given element ids, pools included (unknown ids are ignored). | | `getSelection()` | `readonly string[]` | The currently selected element ids. | | `deleteSelection()` | `void` | Deletes the selected elements, cascading to their attached edges and clearing orphaned default-flow markers. | | `undo() / redo()` | `void` | Undoes / re-applies the most recent command. Snapshot-based: each command — including every arrow-key step — is exactly one entry. | | `canUndo() / canRedo()` | `boolean` | Whether at least one command can be undone / redone. | | `isDirty()` | `boolean` | True when the model differs from the last save point. | | `markSaved()` | `void` | Marks the current model as saved; `isDirty()` reports false until the model changes again. | | `focus()` | `void` | Moves keyboard focus onto the diagram canvas. | _Overlays_ | Name | Type | Description | | --- | --- | --- | | `addOverlay(overlay: OgeBpmnOverlay)` | `string` | Attaches an HTML badge to a diagram element and returns a handle for `removeOverlay`. The badge tracks the element through pan/zoom and model changes; a dangling `elementId` hides it without removing the registration. The `html` renders through Angular's sanitizing `[innerHTML]` binding. | | `removeOverlay(id: string)` | `void` | Removes the overlay registered under the given handle. Unknown handles are ignored. | | `clearOverlays(elementId?: string)` | `void` | Removes every registered overlay, or — when `elementId` is given — only the overlays attached to that element. | #### Events | Name | Type | Description | | --- | --- | --- | | `selectionChanged` | `OgeBpmnSelectionEvent` | The selection changed (user interaction or `select()`): `{ ids, elements }` with per-element `{ id, type, name? }` summaries. | | `elementsChanged` | `OgeBpmnElementsChangedEvent` | The diagram model changed: `{ source, label }`, where `source` is `execute \| undo \| redo \| import \| new` and `label` is the command label. | | `diagramChanged` | `OgeBpmnDiagramChangedEvent` | Debounced autosave stream: after model changes settle for `autoSaveDebounceMs` (default 500ms; `0` emits synchronously) the diagram is serialized once to both JSON and XML and emitted together with the change source. Emitted for every source including `import` and `new` — filter on `source` to persist only user edits. No serialization happens mid-drag (gestures commit one command on release); a pending emission is cancelled on destroy. | | `importCompleted` | `OgeBpmnImportEvent` | An `importXml()` call finished parsing; carries the fidelity warnings (`{ warnings }`) — emitted on fatal errors too. | | `dirtyChanged` | `boolean` | The dirty state flipped — the model diverged from, or returned to, the save point. | #### Types _Event payloads & overlays_ | Name | Type | Description | | --- | --- | --- | | `OgeBpmnSelectionEvent` | `{ ids: readonly string[]; elements: readonly OgeBpmnElementInfo[] }` | Payload of `selectionChanged`. | | `OgeBpmnElementInfo` | `{ id: string; type: BpmnNodeType \| BpmnEdgeType \| 'pool'; name?: string }` | Summary of one diagram element carried in editor event payloads. | | `OgeBpmnElementsChangedEvent` | `{ source: OgeBpmnChangeSource; label: string }` | Payload of `elementsChanged`: what changed the model and the command label. | | `OgeBpmnChangeSource` | `'execute' \| 'undo' \| 'redo' \| 'import' \| 'new'` | Origin of a model change reported by `elementsChanged` and `diagramChanged`. | | `OgeBpmnDiagramChangedEvent` | `{ json: BpmnDiagramJson; xml: string; source: OgeBpmnChangeSource }` | Payload of the debounced `diagramChanged` autosave stream: the diagram in both persistence formats plus what caused the change. | | `OgeBpmnImportEvent` | `{ warnings: readonly BpmnImportWarning[] }` | Payload of `importCompleted`: the fidelity warnings collected during import. | | `OgeBpmnOverlay` | `{ elementId: string; html: string; position: 'top-left' \| 'top-right' \| 'bottom-left' \| 'bottom-right' \| 'center'; offset?: Point }` | A programmatic HTML badge attached to a diagram element (process-monitoring overlays), registered via `addOverlay()`. `position` picks which corner (or the center) of the element's bounds the badge anchors to; `offset` is extra diagram-unit displacement applied before the screen transform. `html` is bound through Angular's sanitizing `[innerHTML]` — script tags and inline event handlers are stripped. | | `OgeBpmnPaletteItem` | `{ type: BpmnPaletteItemType }` | One entry of the elements palette. | | `BpmnPaletteItemType` | `BpmnNodeType \| 'pool'` | Everything the palette can place: node types plus the `'pool'` pseudo-entry, which creates a collaboration participant. | _Engine — import & export_ | Name | Type | Description | | --- | --- | --- | | `readBpmnXml(xml: string)` | `BpmnImportResult` | Standalone, prefix-agnostic BPMN 2.0 reader (`bpmn:`, `bpmn2:` or no prefix) — pure TypeScript, usable outside the component. Missing DI is auto-laid-out with a warning. | | `writeBpmnXml(model: BpmnDiagram)` | `string` | Standalone byte-deterministic BPMN 2.0 writer with fixed, normalized prefixes; preserved `extensionElements`/`documentation`/unknown attributes are written back verbatim. | | `toBpmnJson(model: BpmnDiagram)` | `BpmnDiagramJson` | Wraps the diagram model in the versioned JSON persistence envelope — the standalone twin of `OgeBpmnEditor.exportJson()`. | | `fromBpmnJson(value: unknown)` | `BpmnJsonParseResult` | Structurally validates a value produced by `toBpmnJson` (typically after a `JSON.parse` round trip through a database) and returns the diagram model, or an error describing the first problem found. Unknown extra keys are tolerated for forward compatibility; version mismatches, missing required maps and broken id cross-references are not. | | `renderDiagramSvg(model, options?)` | `(model: BpmnDiagram, options?: BpmnSvgExportOptions) => string` | Renders the diagram as a self-contained static `` string: shapes, edges (with arrowhead markers) and labels re-rendered with inline fill/stroke attributes, `viewBox` fitted to the content bounds plus padding. No grid, no selection state, no external CSS. | | `BpmnSvgExportOptions` | `{ padding?: number }` | Options of `renderDiagramSvg`: padding in diagram units added around the content bounds (default 20). | | `createEmptyDiagram(processId?)` | `BpmnDiagram` | A fresh empty diagram model with default `` attributes. | | `BpmnDiagramJson` | `{ version: 1; diagram: BpmnDiagram }` | Versioned JSON envelope for persisting a diagram to an application database; `version` guards forward compatibility of the envelope shape. | | `BpmnJsonParseResult` | `{ model: BpmnDiagram \| null; error?: string }` | Result of `fromBpmnJson`: the model, or null plus an error message. | | `BpmnImportResult` | `{ model: BpmnDiagram \| null; warnings: readonly BpmnImportWarning[]; error?: string }` | Result of importing BPMN XML: the model (`null` on fatal errors) plus fidelity warnings. | | `BpmnImportWarning / BpmnImportWarningCode` | `{ code, message, elementId?, localName? } · 'unsupported-element' \| 'missing-di' \| 'multiple-processes' \| 'dangling-ref' \| 'event-definition-stripped' \| 'invalid-event-definition' \| 'nested-lanes-flattened'` | A non-fatal fidelity loss reported while importing — dropped flow elements, stripped or position-invalid event definitions, dangling references, missing DI, flattened nested lanes. | _Engine — alignment_ | Name | Type | Description | | --- | --- | --- | | `alignElements(rects, mode)` | `(rects: Readonly>, mode: BpmnAlignMode) => Record` | Computes the per-element move deltas that align the given rectangles (bpmn-js align-elements semantics): edge modes move to the outermost matching edge, center modes to the center of the joint bounding box. Every input id appears in the result (zero delta when already aligned); fewer than 2 rectangles produce an empty result. Pure — no model involved. | | `distributeElements(rects, axis)` | `(rects: Readonly>, axis: BpmnDistributeAxis) => Record` | Computes the deltas that spread the elements at equal center gaps along one axis (3+ elements). Pure — no model involved. | | `BpmnAlignMode` | `'left' \| 'centerX' \| 'right' \| 'top' \| 'centerY' \| 'bottom'` | Alignment edge/axis of `alignElements`: edge values align the matching edges, `centerX`/`centerY` align the centers on the horizontal / vertical axis of the selection's bounding box. | | `BpmnDistributeAxis` | `'x' \| 'y'` | Distribution axis of `distributeElements`: `x` spreads horizontally. | _Engine — model_ | Name | Type | Description | | --- | --- | --- | | `BpmnDiagram` | `readonly plain-object graph` | The complete immutable diagram model: the default process, optional collaboration `pools`, `nodes`/`edges` records, a deterministic `order` list, per-element DI bounds/waypoints and the preserved foreign XML fragments. All nodes and edges of every pool's process live in the single flat maps; a node's `poolId` decides which `` it is serialized into. | | `BpmnNode / BpmnNodeType` | `'startEvent' \| 'endEvent' \| 'intermediateThrowEvent' \| 'intermediateCatchEvent' \| 'boundaryEvent' \| 'task' \| 'userTask' \| 'serviceTask' \| 'scriptTask' \| 'callActivity' \| 'subProcess' \| 'eventSubProcess' \| 'transaction' \| 'exclusiveGateway' \| 'parallelGateway' \| 'dataObject' \| 'dataStore' \| 'group' \| 'textAnnotation'` | A diagram node and every node kind that can appear on the canvas. Events carry `eventDefinition` (and boundary events `attachedToRef`/`cancelActivity`), gateways carry `defaultFlowId`, activities carry `marker`/`isForCompensation`, call activities `calledElement`, sub-process children `parentId`, annotations `text`. | | `BpmnEdge / BpmnEdgeType` | `'sequenceFlow' \| 'association' \| 'messageFlow' \| 'dataAssociation'` | A connection with `sourceRef`/`targetRef`: sequence flows (may carry `name` and `conditionExpression`), annotation associations, cross-pool message flows and data associations (v0.4 — one endpoint must be an activity). | | `BpmnEventDefinitionKind` | `'message' \| 'timer' \| 'error' \| 'signal' \| 'escalation' \| 'conditional' \| 'link' \| 'compensate' \| 'terminate'` | The nine standard BPMN event definition kinds (single definition per event). | | `VALID_EVENT_DEFINITIONS` | `Readonly>` | Which event definition kinds each event position accepts (BPMN 2.0 table 10.87 subset), enforced by the reader and the panel definition select. v0.3 simplification: `error` on a start event is allowed unconditionally although the spec restricts it to event sub-processes. | | `BpmnActivityMarker` | `'loop' \| 'multiInstanceParallel' \| 'multiInstanceSequential' \| 'compensation'` | Loop/multi-instance/compensation markers rendered at an activity's bottom center. | | `BpmnSubProcessType` | `'subProcess' \| 'eventSubProcess' \| 'transaction'` | The three sub-process container kinds (children carry `parentId`). | | `BpmnDataNodeType` | `'dataObject' \| 'dataStore'` | Data element kinds (v0.4): the page-with-fold object and the cylinder store. | | `BpmnPool / BpmnLane` | `interfaces` | A collaboration participant and its swimlanes. A pool without a `processRef` is a black-box pool: it renders as an empty band and is a valid message-flow endpoint, but has no process contents. Lane membership is the ordered `flowNodeRefs` id list, auto-maintained from geometry on every editing command. | | `BpmnMessageFlow` | `interface` | A message flow between elements of different pools. Either endpoint may be a participant (pool) id or a flow-node id; serialized inside the `` element. | | `BpmnClipboard` | `interface` | A deep-cloned diagram subgraph held by the editor's internal clipboard: the copied nodes plus every edge whose both endpoints were copied, with their DI. Ids still refer to the source diagram; pasting remaps them. | | `Point / Rect` | `{ x, y } · { x, y, width, height }` | Geometry primitives used by DI bounds and waypoints. | ### Configuration #### Properties _OgeBpmnConfig_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `gridSize` | `number` | `10` | Grid step in diagram units used for placement and arrow-key movement. | | `snapThreshold` | `number` | `5` | Neighbor-alignment snapping threshold in diagram units — center/edge alignment beats the grid inside it. | | `zoomMin / zoomMax` | `number` | `0.2 / 4` | Bounds of the zoom factor. | | `autoSaveDebounceMs` | `number` | `500` | Debounce in milliseconds for the editor's `diagramChanged` autosave stream: rapid model changes collapse into one emission carrying the final state; `0` emits synchronously after every change. Serialization happens only on emit and never while dragging (move/bend gestures commit a single command on release). | | `colorPresets` | `readonly string[]` | `OGE_DEFAULT_BPMN_COLOR_PRESETS` | Fill color presets (any CSS color strings) offered as swatch buttons in the properties panel's appearance section. Presets set the fill only; the stroke has its own picker. | | `messages` | `OgeBpmnMessages` | `—` | Every user-facing string the editor renders, including aria labels. | _OgeBpmnMessages_ | Name | Type | Description | | --- | --- | --- | | `canvasLabel / canvasHint` | `string` | Accessible name of the `role="application"` canvas, and the focus hint explaining how to leave the diagram (appended to the label). | | `emptyText` | `string` | Centered hint shown while the diagram has no elements. | | `paletteLabel` | `string` | Accessible name of the elements palette toolbar. | | `paletteLabels` | `Readonly>` | Label (tooltip + aria label) of each palette entry, per palette item type (including `'pool'`) — a full record, so an override supplies every key. | | `tools` | `OgeBpmnToolsMessages` | Labels of the tool strip below the palette: `label` (the toolbar's accessible name), `hand`, `lasso`, `space`, `globalConnect` and `search`. | | `align` | `OgeBpmnAlignMessages` | Labels of the align/distribute flyout on multi-element selections: `menuLabel`, the six `align*` entries and `distributeHorizontal`/`distributeVertical` (equal gaps, 3+ elements). | | `search` | `OgeBpmnSearchMessages` | Labels of the element search overlay (Ctrl+F): `label`, `placeholder` and `noResults`. | | `minimapLabel` | `string` | Accessible name of the minimap navigation overlay. | | `contextPad` | `OgeBpmnContextPadMessages` | Aria labels and titles of the context-pad actions: `connect`, `appendTask`, `appendGateway`, `appendEndEvent`, `editLabel`, `toggleDefault`, `deleteElement`. | | `announcements` | `OgeBpmnAnnouncementMessages` | Live-region announcement templates; `{token}` placeholders are substituted. | | `elementNames` | `Readonly>` | Fallback display name per element type — node/edge types plus pools and lanes — used when an element has no name. | | `properties` | `OgeBpmnPropertiesMessages` | Labels of the properties panel: headings, field labels and templates. | _OgeBpmnPropertiesMessages_ | Name | Type | Description | | --- | --- | --- | | `panelLabel / processHeading / name / id / executable` | `string` | The panel region's accessible name, the no-selection (process) heading, the name field label, the read-only id row label and the process "is executable" checkbox label. | | `condition / defaultFlow / annotationText / selectionCount` | `string` | The sequence-flow condition textarea, the "default flow" checkbox on an exclusive gateway's flow, the text-annotation textarea and the multi-selection summary (`{count}`). | | `appearanceHeading / fillLabel / strokeLabel / clearColors / presetLabel` | `string` | The appearance (colors) section: heading, fill/stroke picker labels, the "clear colors" button and the aria label of a preset swatch (`{color}`). | | `typeLabel / eventDefinition / noneOption / eventDefinitionNames` | `string · Readonly>` | The element type (morph) select, the event definition select on events, the shared "None" option and the display name per event definition kind. | | `interrupting / collapsed / marker / markerNames / forCompensation / calledElement` | `string · Readonly>` | The boundary event "Interrupting" checkbox, the sub-process "Collapsed" checkbox, the activity marker select with its per-marker display names, the "For compensation" checkbox and the call activity "Called element" field. | | `lanesHeading / addLane / removeLane / laneName` | `string` | The lanes section of the pool panel: heading, "Add lane" button, per-lane "Remove" button (`{name}`) and lane name input aria label (`{name}`). | _OgeBpmnAnnouncementMessages_ | Name | Type | Description | | --- | --- | --- | | `created / moved / connected / deleted` | `string templates` | After a palette placement (`{type}`), a move (`{name}`), a connection (`{source}`/`{target}`) and a deletion (`{count}`). | | `undone / redone` | `string templates` | After undo/redo; `{label}` is the affected command label. | | `selected / selectionCleared` | `string templates` | When an element becomes selected (`{name}`) and when the selection is cleared. | | `imported / importedWithWarnings` | `string templates` | After a clean import, and after an import that produced warnings (`{count}`). | | `connectDenied / labelEdited` | `string templates` | When a requested connection is not allowed by the rules, and after an inline label edit is committed. | | `copied / cut / pasted` | `string templates` | After a clipboard copy, cut and paste; `{count}` is the number of affected elements. | | `recolored / resized / typeChanged` | `string templates` | After a recolor (`{count}`), a resize (`{name}`) and a properties-panel type morph (`{name}`/`{type}`). | | `attached / attachDenied / collapsedToggled` | `string templates` | After a boundary event attaches (`{name}`/`{host}`), when a boundary-event placement finds no activity border, and after a sub-process collapse/expand (`{name}`). | | `poolCreated / laneAdded / laneRemoved` | `string templates` | After a pool is placed from the palette and after a lane is added to / removed from a pool (`{name}` is the pool's display name). | | `aligned / distributed / spaceAdjusted` | `string templates` | After an align, distribute and space-tool commit; `{count}` is the number of moved/shifted elements. | | `searchResults / labelMoved / waypointRemoved` | `string templates` | When the search result set changes (`{count}`), after an external label drag (`{name}`) and after a bend-point handle was removed by double click. | #### Types | Name | Type | Description | | --- | --- | --- | | `provideOgeBpmnConfig(config: OgeBpmnConfigInput)` | `Provider` | Application- or component-scoped editor defaults; `messages` is a partial merged over the built-in English strings. | | `OgeBpmnConfigInput` | `Partial with Partial` | Argument shape of `provideOgeBpmnConfig()`. | | `OGE_BPMN_CONFIG` | `InjectionToken` | The DI token the editor reads; defaults to `OGE_DEFAULT_BPMN_CONFIG`. | | `OGE_DEFAULT_BPMN_CONFIG / OGE_DEFAULT_BPMN_MESSAGES` | `OgeBpmnConfig / OgeBpmnMessages` | The built-in defaults — handy as a base for wholesale message replacement. | | `OGE_DEFAULT_BPMN_COLOR_PRESETS` | `readonly string[]` | The default fill presets of the properties panel's appearance section: eight soft pastel tones that keep dark strokes and labels readable. Override per app via `colorPresets`. | | `BpmnElementNameKey` | `BpmnNodeType \| BpmnEdgeType \| 'pool' \| 'lane'` | Every key of `elementNames`: node/edge types plus pools and lanes. | | `OgeBpmnContextPadMessages / OgeBpmnToolsMessages / OgeBpmnAlignMessages / OgeBpmnSearchMessages / OgeBpmnPropertiesMessages / OgeBpmnHeaderMessages` | `interfaces` | The message blocks referenced above — context-pad actions, tool strip, align/distribute flyout, search overlay and properties panel. | ### Demos Each demo below is a complete standalone component — copy one whole and it compiles. #### Autosave ```ts import { ChangeDetectionStrategy, Component, viewChild } from '@angular/core'; import { OgeBpmnEditor } from '@oge-ui/bpmn'; import type { OgeBpmnDiagramChangedEvent } from '@oge-ui/bpmn'; @Component({ selector: 'demo-root', imports: [OgeBpmnEditor], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { private readonly editor = viewChild.required(OgeBpmnEditor); // fires once per settled change (autoSaveDebounceMs, default 500ms), // with the diagram already serialized to both JSON and XML — no // exportJson() call needed and never mid-drag protected onDiagramChanged(event: OgeBpmnDiagramChangedEvent): void { if (event.source === 'import' || event.source === 'new') return; localStorage.setItem('diagram', JSON.stringify(event.json)); this.editor().markSaved(); } protected restore(): void { const raw = localStorage.getItem('diagram'); if (raw === null) return; // structural validation — a broken envelope never clobbers the canvas const { error } = this.editor().importJson(JSON.parse(raw)); if (error !== undefined) console.warn(error); } } ``` #### Config ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeBpmnEditor, provideOgeBpmnConfig } from '@oge-ui/bpmn'; import type { OgeBpmnMessages } from '@oge-ui/bpmn'; @Component({ selector: 'demo-root', imports: [OgeBpmnEditor], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { // paletteLabels is a full record — every palette entry gets its label protected readonly turkish: Partial = { canvasLabel: 'BPMN diyagram editörü', canvasHint: 'Diyagramdan çıkmak için Escape sonra Tab', emptyText: 'Boş diyagram — paletten bir öğe seçin', paletteLabel: 'Öğe paleti', paletteLabels: { startEvent: 'Başlangıç olayı', endEvent: 'Bitiş olayı', intermediateThrowEvent: 'Ara fırlatma olayı', intermediateCatchEvent: 'Ara yakalama olayı', boundaryEvent: 'Sınır olayı', task: 'Görev', userTask: 'Kullanıcı görevi', serviceTask: 'Servis görevi', scriptTask: 'Betik görevi', callActivity: 'Çağrı aktivitesi', subProcess: 'Alt süreç', eventSubProcess: 'Olay alt süreci', transaction: 'İşlem', exclusiveGateway: 'Dışlayıcı geçit', parallelGateway: 'Paralel geçit', dataObject: 'Veri nesnesi', dataStore: 'Veri deposu', group: 'Grup', pool: 'Havuz', textAnnotation: 'Metin notu', }, }; } // or app-scoped defaults (merged over the built-ins): export const appConfig = { providers: [ provideOgeBpmnConfig({ gridSize: 20, // placement + arrow-key step snapThreshold: 8, // neighbor-alignment snapping zoomMin: 0.5, zoomMax: 2, autoSaveDebounceMs: 1000, // diagramChanged settle time (0 = sync) colorPresets: ['#fee2e2', '#dcfce7', '#dbeafe'], // panel fill swatches messages: { emptyText: 'Boş diyagram — paletten bir öğe seçin' }, }), ], }; ``` #### Getting started ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeBpmnEditor } from '@oge-ui/bpmn'; import type { OgeBpmnElementsChangedEvent } from '@oge-ui/bpmn'; @Component({ selector: 'demo-root', imports: [OgeBpmnEditor], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { // two-way zoom model: wheel zooming writes it back protected readonly zoom = signal(1); // fires on every command, undo/redo, import and newDiagram() protected onChanged(event: OgeBpmnElementsChangedEvent): void { console.log(event.source, event.label); } } ``` #### Import export ```ts import { ChangeDetectionStrategy, Component, signal, viewChild } from '@angular/core'; import { OgeBpmnEditor } from '@oge-ui/bpmn'; import type { BpmnImportWarning, OgeBpmnImportEvent } from '@oge-ui/bpmn'; const SAMPLE_BPMN_XML = ` `; @Component({ selector: 'demo-root', imports: [OgeBpmnEditor], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { private readonly editor = viewChild.required(OgeBpmnEditor); protected readonly xml = signal(SAMPLE_BPMN_XML); protected readonly warnings = signal([]); protected importXml(): void { // resolves with { model, warnings, error? }; a fatal parse // error leaves the current diagram untouched void this.editor().importXml(this.xml()); } protected exportXml(): void { // deterministic BPMN 2.0 XML — same model, same bytes this.xml.set(this.editor().exportXml()); } protected onImport(event: OgeBpmnImportEvent): void { this.warnings.set(event.warnings); } } ``` #### Overlays ```ts import { ChangeDetectionStrategy, Component, viewChild } from '@angular/core'; import { OgeBpmnEditor } from '@oge-ui/bpmn'; @Component({ selector: 'demo-root', imports: [OgeBpmnEditor], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { private readonly editor = viewChild.required(OgeBpmnEditor); private count = 0; // attach a count bubble to the selected element; the badge tracks it // through pan, zoom and model changes, hides while the element is // gone and returns the handle for removeOverlay() protected addBadge(): void { const [id] = this.editor().getSelection(); if (id === undefined) return; this.editor().addOverlay({ elementId: id, // rendered through Angular's sanitizing [innerHTML] html: '' + ++this.count + '', position: 'top-right', offset: { x: 4, y: -4 }, }); } protected clearBadges(): void { this.editor().clearOverlays(); // or clearOverlays(elementId) } } ``` #### Readonly ```ts import { ChangeDetectionStrategy, Component, afterNextRender, viewChild } from '@angular/core'; import { OgeBpmnEditor } from '@oge-ui/bpmn'; declare const PROCESS_XML: string; // e.g. fetched from your API @Component({ selector: 'demo-root', imports: [OgeBpmnEditor], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { private readonly viewer = viewChild.required(OgeBpmnEditor); constructor() { // load the diagram once the view exists; readOnly blocks every // mutation (palette, context pad, keyboard editing, drags) but // keeps selection, pan, zoom and the accessible reading order afterNextRender(() => { void this.viewer().importXml(PROCESS_XML); }); } } ``` #### Sample bpmn xml ```ts ``` ## @oge-ui/charts Charts: cartesian line/spline/area/bar/stacked/scatter/range/candlestick series and pie/doughnut on a dependency-free SVG kernel — time/log axes, zoom & pan, crosshair, shared tooltips, interactive legend and keyboard point inspection. **This package is commercially licensed — unlike the rest of the suite, it is not MIT. See https://ogeui.com/license before shipping it.** Docs: https://ogeui.com/components/charts ### Entry points `@oge-ui/charts` - values: `OGE_CHARTS_CONFIG`, `OGE_CHART_PALETTE`, `OGE_DEFAULT_CHARTS_CONFIG`, `OGE_DEFAULT_CHARTS_MESSAGES`, `OgeChart`, `OgeChartAnnotationTemplate`, `OgeChartLegendTemplate`, `OgeChartTooltipTemplate`, `OgePieChart`, `OgePolarChart`, `OgeRangeSelector`, `provideOgeChartsConfig` - types: `OgeChartAnnotation`, `OgeChartAnnotationTemplateContext`, `OgeChartAxisOptions`, `OgeChartAxisType`, `OgeChartCrosshairOptions`, `OgeChartExportData`, `OgeChartLegendClickEvent`, `OgeChartLegendOptions`, `OgeChartLegendTemplateContext`, `OgeChartPieSliceEvent`, `OgeChartPoint`, `OgeChartPointEvent`, `OgeChartPointRef`, `OgeChartRange`, `OgeChartSeriesEvent`, `OgeChartSeriesInput`, `OgeChartSeriesType`, `OgeChartSmallValuesGrouping`, `OgeChartStripLine`, `OgeChartTooltipOptions`, `OgeChartTooltipShowingEvent`, `OgeChartTooltipTemplateContext`, `OgeChartsAnnouncementMessages`, `OgeChartsAriaMessages`, `OgeChartsConfig`, `OgeChartsConfigInput`, `OgeChartsMessages` `@oge-ui/charts/export-image` - values: `exportChartToPng`, `exportChartToSvg`, `serializeChartSvg` - types: `OgeChartImageExportOptions`, `OgeChartSvgSource` ### OgeChart — `` #### Properties _Data & series_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `dataSource` | `readonly T[]` | `[]` | Data items; never mutated. | | `series` | `readonly OgeChartSeriesInput[]` | `[]` | Series definitions: `type` (16 kinds), field mapping (`valueField`/`argumentField` — names, dotted paths or getters), `name`, `color`, `axis` (value-axis index), `stack` group, `dashStyle`/`width`/`opacity`, `showInLegend`, rangeArea bounds (`value1Field`/`value2Field`) and candlestick OHLC (`openField`/`highField`/`lowField`/`closeField`), `sizeField` (bubble area), `visible` (start hidden; the legend re-shows) and `showLabels` (SI-formatted value labels on small series). Null/NaN values render as gaps. | | `commonSeries` | `Partial` | `{}` | Defaults merged under every series (dx `commonSeriesSettings` parity). | | `palette` | `readonly string[] \| undefined` | `—` | Series colors; defaults to the 10-color `OGE_CHART_PALETTE` (concrete hex values so exported images keep their colors). | _Axes_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `argumentAxis` | `OgeChartAxisOptions` | `{}` | Argument axis: `type` auto-detects (numbers / dates / categories) when unset; `min`/`max`, `inverted`, `grid`, `title`, `labelFormat`, `labelOverlap` (`rotate`/`skip`/`none`). | | `valueAxis` | `OgeChartAxisOptions \| readonly OgeChartAxisOptions[]` | `{}` | One or more value axes; series pick theirs via `axis`. `position: 'end'` renders on the right, `type: 'logarithmic'` spaces decades evenly, `abbreviate: false` disables SI labels (`1.2K`). | | `stripLines` | `readonly OgeChartStripLine[]` | `[]` | Argument-axis markers: `{ start, end?, label?, color? }` — a line without `end`, a shaded band with it. | | `annotations` | `readonly OgeChartAnnotation[]` | `[]` | Plot annotations: `type: 'point'` draws a marker dot with a connector into a label box at (`argument`, `value`); `'text'` places the label alone (top of the plot without a value). `axis`, `color` and `offsetX/Y` refine placement. | _Interaction_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `zoomEnabled / panEnabled` | `'none' \| 'wheel' \| 'drag' \| 'both' / boolean` | `'none' / false` | Cursor-centered wheel zoom, drag-select zoom (Escape cancels mid-drag, 8px threshold), Shift+drag pan. Escape on the focused plot resets the zoom. | | `visualRange` | `OgeChartRange \| null` | `null` | The zoom window in argument-axis units (`null` = full extent). Two-way (`[(visualRange)]`); writes clamp into the data bounds. | | `tooltip` | `OgeChartTooltipOptions` | `{}` | `{ enabled?, shared? }` — shared lists every series at the hovered argument; otherwise the value-nearest series wins. | | `crosshair` | `OgeChartCrosshairOptions` | `{}` | `{ enabled?, horizontal? }` — the vertical tracker snaps to the nearest argument (binary search). | | `legend` | `OgeChartLegendOptions` | `{}` | `{ visible?, position? (top/bottom/start/end), interactive? }` — real buttons with `aria-pressed`; clicking toggles the series and the axes rescale. | | `selectionMode / selectedPoints` | `'point' \| 'series' \| 'none' / readonly OgeChartPointRef[]` | `'none' / []` | Click (or Enter) selects; Ctrl adds points to the set; series mode selects the whole series. Two-way (`[(selectedPoints)]`). | _Appearance & i18n_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `title / subtitle` | `string` | `''` | Headings above the plot. | | `animation` | `boolean` | `true` | Hover/selection transitions; honors `prefers-reduced-motion`. | | `locale` | `string \| undefined` | `—` | BCP 47 locale for every `Intl` format; defaults to the config locale, then the browser locale. | | `messages` | `Partial` | `{}` | Per-instance message overrides, merged over the DI config per top-level block. | #### Methods | Name | Type | Description | | --- | --- | --- | | `zoomToRange(range) / resetZoom()` | `void` | Programmatic zoom (clamped into the data bounds) / back to the full extent, announced. | | `hideTooltip()` | `void` | Clears the hover state (tooltip + crosshair). | | `refresh()` | `void` | Re-measures the container (ResizeObserver normally covers it). | | `focus()` | `void` | Focuses the keyboard-inspectable plot region. | | `getExportData()` | `OgeChartExportData` | Snapshot for custom pipelines: per-series names/types/colors/visibility/points plus the plotted range. | | `getSvgElement()` | `SVGSVGElement` | The live SVG root — what the image exporters serialize. | _Export entry point (lazy, dependency-free)_ | Name | Type | Description | | --- | --- | --- | | `exportChartToPng(chart, options?) / exportChartToSvg(chart, options?) / serializeChartSvg(svg, options?)` | `@oge-ui/charts/export-image` | No third-party libraries: the live SVG is cloned with computed styles inlined, then downloaded as a standalone `.svg` or rasterized onto a canvas for `.png` (`pixelRatio`, `background`). Import the entry point dynamically. | #### Events | Name | Type | Description | | --- | --- | --- | | `pointClick / seriesClick` | `OgeChartPointEvent / OgeChartSeriesEvent` | Pointer (and keyboard Enter) activation with the normalized point payload. | | `legendClick` | `OgeChartLegendClickEvent` | Cancelable — set `cancel = true` to veto the visibility toggle; carries `willHide`. | | `tooltipShowing` | `OgeChartTooltipShowingEvent` | Cancelable, before the tooltip shows for a new argument. | | `visualRangeChange / selectedPointsChange` | `OgeChartRange \| null / readonly OgeChartPointRef[]` | The two-way model outputs. | | `drawn` | `void` | After every render pass. | #### Types | Name | Type | Description | | --- | --- | --- | | `OgeChartSeriesType` | `string union` | 'line' \| 'spline' \| 'stepLine' \| 'area' \| 'splineArea' \| 'stepArea' \| 'stackedArea' \| 'fullStackedArea' \| 'bar' \| 'stackedBar' \| 'fullStackedBar' \| 'rangeBar' \| 'scatter' \| 'bubble' \| 'rangeArea' \| 'candlestick'. | | `OgeChartPoint` | `interface` | The normalized point: `argument`, `argNumeric`, `value`(s incl. OHLC), `source`, `index`. | | `OgeChartAxisType / OgeChartRange` | `'linear' \| 'logarithmic' \| 'category' \| 'time' / { min, max }` | Axis kinds and the numeric window type (time axes: epoch ms; category: index space). | | `[ogeChartTooltipTemplate]` | `structural directive (OgeChartTooltipTemplate)` | Replaces the tooltip's content; context `OgeChartTooltipTemplateContext`: `{ $implicit: OgeChartPointEvent[] }`. | | `[ogeChartAnnotationTemplate]` | `structural directive (OgeChartAnnotationTemplate)` | Replaces an annotation's label (rendered in a `foreignObject`, so any HTML works); context `OgeChartAnnotationTemplateContext`: `{ $implicit: { text } }`. | | `[ogeChartLegendTemplate]` | `structural directive (OgeChartLegendTemplate)` | Replaces a legend item's content; context `OgeChartLegendTemplateContext`: `{ $implicit: { name, color, hidden } }`. | ### OgePieChart — `` #### Properties | Name | Type | Default | Description | | --- | --- | --- | --- | | `dataSource / argumentField / valueField` | `readonly T[] / string \| getter` | `[] / 'argument' / 'value'` | One slice per item; negative values clamp to zero. | | `type / innerRadius / startAngle` | `'pie' \| 'doughnut' / number / number` | `'pie' / 0.5 / 0` | Doughnut hole as an outer-radius fraction; start angle in radians (0 = 12 o'clock, clockwise). | | `smallValuesGrouping` | `OgeChartSmallValuesGrouping \| null` | `null` | `{ mode: 'topN' \| 'smallValueThreshold', topCount?, threshold? }` — the tail folds into an "Others" slice (`othersLabel`). | | `showLabels` | `boolean` | `true` | Outside labels in two anti-overlap columns with connector lines. | | `selectedSlices` | `readonly number[]` | `[]` | Selected slice indexes — selected slices explode. Two-way (`[(selectedSlices)]`). | | `legend / tooltipEnabled / palette / title / locale / messages` | `see OgeChart` | `—` | Shared options with the cartesian chart. | #### Events | Name | Type | Description | | --- | --- | --- | | `sliceClick` | `OgeChartPieSliceEvent` | Slice activation: `argument`, `value`, `fraction`, merged `sources` and the `grouped` flag for the "Others" slice. | | `legendClick / selectedSlicesChange` | `OgeChartLegendClickEvent / readonly number[]` | Cancelable legend toggle; the two-way selection output. | ### OgePolarChart — `` #### Properties | Name | Type | Default | Description | | --- | --- | --- | --- | | `dataSource / series / commonSeries` | `readonly T[] / readonly OgeChartSeriesInput[] / Partial` | `[] / [] / {}` | Same field mapping as the cartesian chart; supported polar types: `'line'` and `'area'` (closed radar loops — a null value breaks the loop into a gap), `'scatter'` (markers) and `'bar'` (sectors from the center). | | `spider` | `boolean` | `false` | Straight-segment (polygon) grid rings instead of circles. | | `startAngle` | `number` | `0` | First category's angle in radians (0 = 12 o'clock, clockwise). | | `valueAxis` | `OgeChartAxisOptions` | `{}` | The radial axis: `max` override and `labelFormat` of the nice-tick rings. | | `selectionMode / selectedPoints` | `'point' \| 'none' / readonly OgeChartPointRef[]` | `'none' / []` | Keyboard Enter (and clicks on markers/sectors) select; two-way (`[(selectedPoints)]`). | | `legend / tooltipEnabled / palette / title / locale / messages` | `see OgeChart` | `—` | Shared options — the legend, tooltip, sr data table and keyboard inspection (arrows walk categories and series) work exactly like the cartesian chart. | #### Methods | Name | Type | Description | | --- | --- | --- | | `focus() / getSvgElement()` | `void / SVGSVGElement` | Focuses the keyboard-inspectable plot / the live SVG root for the image exporters. | #### Events | Name | Type | Description | | --- | --- | --- | | `pointClick / legendClick / selectedPointsChange` | `OgeChartPointEvent / OgeChartLegendClickEvent / readonly OgeChartPointRef[]` | Point activation, the cancelable legend toggle and the two-way selection output. | ### OgeRangeSelector — `` #### Properties | Name | Type | Default | Description | | --- | --- | --- | --- | | `dataSource / series` | `readonly T[] / readonly OgeChartSeriesInput[]` | `[] / []` | The mini background chart (line/area recommended) drawn behind the selection window. | | `value` | `OgeChartRange \| null` | `null` | The selected window in argument units (`null` = full range). Two-way (`[(value)]`) — bind the same signal to a chart's `[(visualRange)]` and the two stay in lockstep. | | `scaleType` | `'time' \| 'linear' \| undefined` | `—` | Auto-detects from the first argument (dates → time) when unset. | | `palette / locale / messages` | `see OgeChart` | `—` | Shared options; handle labels come from `messages.aria.rangeStart/rangeEnd/rangeWindow`. | #### Methods | Name | Type | Description | | --- | --- | --- | | `reset()` | `void` | Back to the full range (`value = null`). | #### Events | Name | Type | Description | | --- | --- | --- | | `valueChange` | `OgeChartRange \| null` | The two-way model output. Interaction: drag the window (grab cursor), drag either handle, click the track to center the window there — Escape mid-drag restores; the handles are WAI-ARIA sliders (arrow keys adjust by 2%, Home/End jump to the bounds, changes announced). | ### Configuration #### Properties | Name | Type | Default | Description | | --- | --- | --- | --- | | `provideOgeChartsConfig(config)` | `Provider` | `—` | Configures every chart below the provider (`OgeChartsConfigInput`); shallow merge over `OGE_DEFAULT_CHARTS_CONFIG` per top-level key — a partial `messages` replaces whole nested blocks. The token is `OGE_CHARTS_CONFIG` (`OgeChartsConfig`). | | `messages` | `OgeChartsMessages` | `—` | Every user-facing string, aria labels included: `aria` (`OgeChartsAriaMessages` — chart/pie labels with `{title}`/`{count}`, table caption, plot hint, legend label), `announcements` (`OgeChartsAnnouncementMessages` — live-region templates with `{series}`/`{argument}`/`{value}`) and `noData`. Defaults: `OGE_DEFAULT_CHARTS_MESSAGES`. | | `locale` | `string \| undefined` | `—` | BCP 47 locale for every `Intl` format in scope; a per-instance `[locale]` input wins. | | `a11yTableLimit` | `number` | `50` | Rows of the screen-reader data table. | | `markerThreshold` | `number` | `200` | Marker circles render only up to this many points per series — beyond it the single path carries the series alone. Line-family paths additionally auto-downsample with LTTB to about one point per pixel once a series outgrows the plot width (hit-testing and tooltips keep the full data). | ### Demos Each demo below is a complete standalone component — copy one whole and it compiles. #### Annotations ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeChart } from '@oge-ui/charts'; import type { OgeChartAnnotation, OgeChartSeriesInput } from '@oge-ui/charts'; @Component({ selector: 'demo-root', imports: [OgeChart], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly data = Array.from({ length: 40 }, (_, i) => ({ day: i + 1, price: 80 + Math.sin(i / 5) * 20 + i / 2, })); protected readonly series: OgeChartSeriesInput[] = [ { type: 'spline', argumentField: 'day', valueField: 'price', name: 'Price' }, ]; protected readonly annotations: OgeChartAnnotation[] = [ { type: 'point', text: 'All-time high', argument: 34, value: 116.9 }, { type: 'point', text: 'Correction', argument: 22, value: 76.1, offsetY: 24 }, { type: 'text', text: 'Q1 guidance', argument: 8 }, ]; } ``` #### Events export ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeChart, provideOgeChartsConfig } from '@oge-ui/charts'; import type { OgeChartPointEvent, OgeChartPointRef, OgeChartSeriesInput } from '@oge-ui/charts'; @Component({ selector: 'demo-root', imports: [OgeChart], changeDetection: ChangeDetectionStrategy.OnPush, template: `
`, }) export class Demo { // App-wide (main.ts / route providers): // provideOgeChartsConfig({ locale: 'de', a11yTableLimit: 100 }) protected readonly selected = signal([]); protected lastPoint: OgeChartPointEvent> | null = null; protected readonly data = [ { month: 'Jan', value: 12 }, { month: 'Feb', value: 31 }, { month: 'Mar', value: 24 }, { month: 'Apr', value: 42 }, ]; protected readonly series: OgeChartSeriesInput[] = [ { type: 'bar', argumentField: 'month', valueField: 'value', name: 'Value' }, ]; /** The exporter loads lazily and needs no third-party library at all. */ protected async exportPng( chart: OgeChart, ): Promise { const { exportChartToPng } = await import('@oge-ui/charts/export-image'); await exportChartToPng(chart, { filename: 'chart.png' }); } protected async exportSvg( chart: OgeChart, ): Promise { const { exportChartToSvg } = await import('@oge-ui/charts/export-image'); exportChartToSvg(chart, { filename: 'chart.svg' }); } } ``` #### Financial ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeChart } from '@oge-ui/charts'; import type { OgeChartSeriesInput } from '@oge-ui/charts'; @Component({ selector: 'demo-root', imports: [OgeChart], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly data = Array.from({ length: 30 }, (_, i) => { const open = 100 + Math.sin(i / 4) * 12 + (i % 5); const close = open + Math.sin(i / 2) * 6 - 2; return { day: new Date(2026, 6, 1 + i), o: open, h: Math.max(open, close) + 4, l: Math.min(open, close) - 4, c: close, vol: 800 + (i % 9) * 120, }; }); protected readonly series: OgeChartSeriesInput[] = [ { type: 'candlestick', argumentField: 'day', openField: 'o', highField: 'h', lowField: 'l', closeField: 'c', name: 'OGE', }, { type: 'bar', argumentField: 'day', valueField: 'vol', name: 'Volume', axis: 1, opacity: 0.4, }, ]; } ``` #### Getting started ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeChart } from '@oge-ui/charts'; import type { OgeChartSeriesInput } from '@oge-ui/charts'; @Component({ selector: 'demo-root', imports: [OgeChart], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly data = [ { quarter: 'Q1', product: 120, services: 60 }, { quarter: 'Q2', product: 150, services: 74 }, { quarter: 'Q3', product: 138, services: 90 }, { quarter: 'Q4', product: 190, services: 105 }, ]; protected readonly series: OgeChartSeriesInput[] = [ { type: 'bar', argumentField: 'quarter', valueField: 'product', name: 'Product' }, { type: 'line', argumentField: 'quarter', valueField: 'services', name: 'Services' }, ]; } ``` #### Pie ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgePieChart } from '@oge-ui/charts'; @Component({ selector: 'demo-root', imports: [OgePieChart], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly data = [ { browser: 'Chrome', share: 62 }, { browser: 'Safari', share: 20 }, { browser: 'Edge', share: 6 }, { browser: 'Firefox', share: 5 }, { browser: 'Samsung', share: 3 }, { browser: 'Opera', share: 2 }, { browser: 'Other', share: 2 }, ]; } ``` #### Polar ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgePolarChart } from '@oge-ui/charts'; import type { OgeChartSeriesInput } from '@oge-ui/charts'; @Component({ selector: 'demo-root', imports: [OgePolarChart], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly data = [ { skill: 'TypeScript', ada: 9, grace: 7 }, { skill: 'CSS', ada: 6, grace: 8 }, { skill: 'SQL', ada: 7, grace: 5 }, { skill: 'Rust', ada: 4, grace: 6 }, { skill: 'Go', ada: 5, grace: 9 }, { skill: 'Testing', ada: 8, grace: 7 }, ]; protected readonly series: OgeChartSeriesInput[] = [ { type: 'area', valueField: 'ada', name: 'Ada' }, { type: 'line', valueField: 'grace', name: 'Grace', width: 2.5 }, ]; } ``` #### Range selector ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeChart, OgeRangeSelector } from '@oge-ui/charts'; import type { OgeChartRange, OgeChartSeriesInput } from '@oge-ui/charts'; @Component({ selector: 'demo-root', imports: [OgeChart, OgeRangeSelector], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly range = signal(null); protected readonly data = Array.from({ length: 365 }, (_, i) => ({ date: new Date(2026, 0, 1 + i), sales: 200 + Math.sin(i / 20) * 80 + (i % 11) * 6, })); protected readonly series: OgeChartSeriesInput[] = [ { type: 'line', argumentField: 'date', valueField: 'sales', name: 'Sales' }, ]; protected readonly miniSeries: OgeChartSeriesInput[] = [ { type: 'area', argumentField: 'date', valueField: 'sales', name: 'Sales' }, ]; } ``` #### Series types ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeChart } from '@oge-ui/charts'; import type { OgeChartSeriesInput } from '@oge-ui/charts'; @Component({ selector: 'demo-root', imports: [OgeChart], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly data = Array.from({ length: 14 }, (_, i) => ({ day: i + 1, smooth: Math.sin(i / 2) * 30 + 60, lo: Math.sin(i / 2) * 12 + 22, hi: Math.sin(i / 2) * 12 + 42, dots: Math.cos(i / 1.5) * 25 + 55, weight: (i % 5) + 1, })); protected readonly series: OgeChartSeriesInput[] = [ { type: 'rangeBar', value1Field: 'lo', value2Field: 'hi', name: 'Band' }, { type: 'stepLine', valueField: 'smooth', name: 'Steps', width: 2.5 }, { type: 'bubble', valueField: 'dots', sizeField: 'weight', name: 'Bubbles', opacity: 0.75, }, ]; } ``` #### Stacks ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeChart } from '@oge-ui/charts'; import type { OgeChartSeriesInput } from '@oge-ui/charts'; @Component({ selector: 'demo-root', imports: [OgeChart], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly data = [ { month: 'Jan', on: 40, off: 24, refunds: -6 }, { month: 'Feb', on: 52, off: 28, refunds: -4 }, { month: 'Mar', on: 47, off: 35, refunds: -9 }, { month: 'Apr', on: 61, off: 31, refunds: -5 }, ]; protected readonly series: OgeChartSeriesInput[] = [ { type: 'stackedBar', valueField: 'on', name: 'Online' }, { type: 'stackedBar', valueField: 'off', name: 'Retail' }, { type: 'stackedBar', valueField: 'refunds', name: 'Refunds' }, ]; } ``` #### Time axis ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeChart } from '@oge-ui/charts'; import type { OgeChartSeriesInput, OgeChartStripLine } from '@oge-ui/charts'; @Component({ selector: 'demo-root', imports: [OgeChart], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly data = Array.from({ length: 120 }, (_, i) => ({ date: new Date(2026, 0, 1 + i), visitors: 400 + Math.sin(i / 9) * 150 + (i % 17) * 8, })); protected readonly series: OgeChartSeriesInput[] = [ { type: 'area', argumentField: 'date', valueField: 'visitors', name: 'Visitors' }, ]; protected readonly stripLines: OgeChartStripLine[] = [ { start: new Date(2026, 2, 1), end: new Date(2026, 2, 15), label: 'Campaign' }, { start: new Date(2026, 3, 10), label: 'Release', color: '#dc2626' }, ]; } ``` #### Zoom ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeChart } from '@oge-ui/charts'; import type { OgeChartRange, OgeChartSeriesInput } from '@oge-ui/charts'; @Component({ selector: 'demo-root', imports: [OgeChart], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly range = signal(null); protected readonly data = Array.from({ length: 50_000 }, (_, i) => ({ t: new Date(2026, 0, 1, 0, i * 15), cpu: 40 + Math.sin(i / 60) * 25 + (i % 13), memory: 55 + Math.cos(i / 90) * 18 + (i % 7), })); protected readonly series: OgeChartSeriesInput[] = [ { type: 'line', argumentField: 't', valueField: 'cpu', name: 'CPU' }, { type: 'line', argumentField: 't', valueField: 'memory', name: 'Memory' }, ]; } ``` ## @oge-ui/gantt Gantt chart: virtualized task tree pane + timeline chart, summary/milestone/baseline bars, FS/SS/FF/SF dependency arrows, critical path, drag editing with Escape-cancel and snapshot undo/redo. **This package is commercially licensed — unlike the rest of the suite, it is not MIT. See https://ogeui.com/license before shipping it.** Docs: https://ogeui.com/components/gantt ### Entry points `@oge-ui/gantt` - values: `OGE_DEFAULT_GANTT_CONFIG`, `OGE_DEFAULT_GANTT_MESSAGES`, `OGE_GANTT_CONFIG`, `OgeGantt`, `OgeGanttTaskTemplate`, `OgeGanttTooltipTemplate`, `provideOgeGanttConfig` - types: `OgeGanttAnnouncementMessages`, `OgeGanttColumn`, `OgeGanttColumnMessages`, `OgeGanttConfig`, `OgeGanttConfigInput`, `OgeGanttDependency`, `OgeGanttDependencyDeletedEvent`, `OgeGanttDependencyDeletingEvent`, `OgeGanttDependencyInsertedEvent`, `OgeGanttDependencyInsertingEvent`, `OgeGanttDependencyType`, `OgeGanttDialogMessages`, `OgeGanttDialogShowingEvent`, `OgeGanttExportColumn`, `OgeGanttExportData`, `OgeGanttGridMessages`, `OgeGanttMenuMessages`, `OgeGanttMessages`, `OgeGanttScaleType`, `OgeGanttSelectionChangedEvent`, `OgeGanttStripLine`, `OgeGanttTask`, `OgeGanttTaskClickEvent`, `OgeGanttTaskDeletedEvent`, `OgeGanttTaskDeletingEvent`, `OgeGanttTaskInsertedEvent`, `OgeGanttTaskInsertingEvent`, `OgeGanttTaskTemplateContext`, `OgeGanttTaskTitlePosition`, `OgeGanttTaskUpdatedEvent`, `OgeGanttTaskUpdatingEvent`, `OgeGanttToolbarMessages`, `OgeGanttTooltipTemplateContext`, `OgeGanttWorkCalendar` `@oge-ui/gantt/export-pdf` - values: `buildGanttPdfDocument`, `exportGanttToPdf` - types: `OgeGanttPdfExportOptions` `@oge-ui/gantt/export-excel` - values: `buildGanttExcelWorkbook`, `exportGanttToExcel` - types: `OgeGanttExcelExportOptions` `@oge-ui/gantt/export-image` - values: `buildGanttCanvas`, `exportGanttToPng`, `ganttImageSize` - types: `OgeGanttImageExportOptions` ### OgeGantt — `` #### Properties _Data_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `tasks` | `readonly T[]` | `[]` | Task items — a plain array, copied into an internal working set; the input is never mutated. Edits surface through the past-tense events. | | `dependencies` | `readonly D[]` | `[]` | Dependency links between tasks; same working-set semantics as `tasks`. | | `keyExpr / parentKeyExpr / titleExpr / startExpr / endExpr / progressExpr / colorExpr` | `string \| ((item: T) => unknown)` | `'id' / 'parentId' / 'title' / 'start' / 'end' / 'progress' / 'color'` | Task field mapping: names (dotted paths reach nested objects) or getter functions. String dates parse as _local_ wall time and write back in the same storage shape. | | `baselineStartExpr / baselineEndExpr` | `string \| ((item: T) => unknown)` | `'baselineStart' / 'baselineEnd'` | Baseline plan fields — tasks with both render the original plan as a slim bar under the live bar. | | `dependencyKeyExpr / predecessorKeyExpr / successorKeyExpr / dependencyTypeExpr` | `string \| ((item: D) => unknown)` | `'id' / 'predecessorId' / 'successorId' / 'type'` | Dependency field mapping. Types are `'FS' \| 'SS' \| 'FF' \| 'SF'` (dx numeric codes 0–3 also parse); missing type means FS. | | `resources` | `readonly { id, text, color?, calendar? }[]` | `[]` | Resource choices: labels next to the bars, the multi-assignment tag editor in the task dialog, the workload band rows — and a resource's own `calendar` overrides `workCalendar` for its tasks (first assigned resource with a calendar wins). | | `resourceIdExpr` | `string \| ((item: T) => unknown)` | `'resourceId'` | The task's assigned resource field — a single id or an array of ids (multi-assignment). Write-back preserves the storage shape: array stores stay arrays, scalar stores stay scalar while at most one id is assigned. | _Appearance & behavior_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `scaleType` | `'hours' \| 'days' \| 'weeks' \| 'months'` | `'days'` | Timeline scale — calendar-true ticks (real month lengths, DST-safe). Two-way (`[(scaleType)]`); the toolbar zoom and Ctrl+wheel write it. | | `firstDayOfWeek` | `number \| undefined` | `—` | First day of week (0 = Sunday) for the weeks scale; `undefined` resolves from the locale. | | `columns` | `readonly OgeGanttColumn[]` | `title / start / end / duration` | Task-list columns: built-in fields (`title`, `start`, `end`, `duration`, `progress`) or any data field, with optional `header`, `widthPx` and `format`. | | `taskListWidth` | `number` | `360` | Initial width (px) of the task pane; the splitter between the panes drags. | | `taskTitlePosition` | `'inside' \| 'outside' \| 'none'` | `'inside'` | Where the task title renders relative to its bar. | | `showDependencies / showRowLines` | `boolean` | `true` | Dependency arrows / horizontal row guides. | | `showCriticalPath` | `boolean` | `false` | Outlines the zero-slack chain (backward-pass latest-finish relaxation over all four link types). | | `weekendsHighlighted / holidays` | `boolean / readonly Date[]` | `true / []` | Off-day shading on the days scale. | | `workCalendar` | `OgeGanttWorkCalendar \| null` | `null` | Work-time calendar (`{ workingDays?, holidays? }`, 0 = Sunday): shades every off day and makes auto-scheduling roll pushed starts onto working days, preserving durations in _working_ days. The `holidays` input merges in; per-resource `calendar`s override it per task. | | `showResourceWorkload` | `boolean` | `false` | Renders the per-resource workload band under the chart: merged assignment segments per resource, overallocated stretches (concurrent assignments) in the danger color. | | `stripLines` | `readonly OgeGanttStripLine[]` | `[]` | Vertical markers: `{ start, end?, label?, color? }` — a line without `end`, a shaded range with it (dx parity). | | `autoScheduling` | `boolean` | `false` | Forward-pass scheduling: moving a predecessor pushes its successors to satisfy FS/SS/FF/SF constraints (never pulls them earlier). | | `locale` | `string \| undefined` | `—` | BCP 47 locale for every `Intl` format; defaults to the config locale, then the browser locale. | | `messages` | `Partial` | `{}` | Per-instance message overrides, merged over the DI config per top-level block. | | `selectedTaskKey` | `RowKey \| null` | `null` | The selected task. Two-way (`[(selectedTaskKey)]`). | _Editing gates_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `editingEnabled` | `boolean` | `true` | Master editing switch (dx `editing.enabled`). | | `allowTaskAdding / allowTaskUpdating / allowTaskDeleting / allowDependencyAdding / allowDependencyDeleting` | `boolean` | `true` | Per-capability editing gates. | | `readOnly` | `boolean` | `false` | **Display-only shorthand**: equivalent to `editingEnabled=false`, hides every editing affordance. | #### Methods | Name | Type | Description | | --- | --- | --- | | `insertTask(taskData) / updateTask(taskData, patch) / deleteTask(taskData)` | `void` | Programmatic CRUD through the same cancelable pipelines as interactive editing — one undo step each. | | `insertDependency(predecessorData, successorData, type?) / deleteDependency(dependencyData)` | `void` | Guarded link CRUD; inserting runs the same cycle check as interactive drawing. | | `undo() / redo()` | `void` | Snapshot history — every applied edit, drags included, is exactly one step (depth: config `undoLimit`). | | `zoomIn() / zoomOut() / zoomToFit()` | `void` | Steps the scale (hours ⇄ days ⇄ weeks ⇄ months) / picks the scale that fits the whole plan and scrolls to it. | | `scrollToDate(date)` | `void` | Scrolls the chart so `date` is in view. | | `expandAll() / collapseAll() / expandAllToLevel(level) / expandToTask(key)` | `void` | Tree expansion control; `expandToTask` also selects and reveals the row. | | `showTaskDetailsDialog(taskData?)` | `void` | Opens the task dialog — edit form for the given task, prefilled create form without one. | | `indentTask(task) / outdentTask(task)` | `void` | Reparents through the guarded update pipeline: indent makes the task a child of its previous sibling (MS Project parity), outdent lifts it to the grandparent. Also on the built-in context menu and **Alt+Shift+Left/Right** on the focused row. | | `focus()` | `void` | Focuses the task tree (roving row). | | `getExportData()` | `OgeGanttExportData` | Snapshot for the exporters: every task in tree order (collapse ignored), the resolved columns with pane-identical formatting, the chart range and the critical-path keys. | _Export entry points (lazy, optional peers)_ | Name | Type | Description | | --- | --- | --- | | `exportGanttToExcel(gantt, options?) / buildGanttExcelWorkbook(data, options?)` | `@oge-ui/gantt/export-excel` | Lazy Excel export (`exceljs` peer): the task tree as a typed worksheet — indented titles, bold summary rows, real Date cells, an appended resource column. Import the entry point dynamically so exceljs stays out of the initial bundle. | | `exportGanttToPdf(gantt, options?) / buildGanttPdfDocument(data, options?)` | `@oge-ui/gantt/export-pdf` | Lazy PDF export (`jspdf` peer): the chart drawn as vector graphics — scale header, bars with progress fill, summary brackets, milestone diamonds, optional critical-path outlining, multi-page pagination. | | `exportGanttToPng(gantt, options?) / buildGanttCanvas(data, options?)` | `@oge-ui/gantt/export-image` | Lazy PNG export with **no dependencies** — plain canvas drawing of the same chart (configurable width, pixel ratio, background and critical-path outlining). | #### Events _Editing (cancelable pipeline)_ | Name | Type | Description | | --- | --- | --- | | `taskInserting / taskUpdating / taskDeleting` | `OgeGanttTask*ingEvent` | Cancelable pre-events — set `cancel = true` to veto before the store changes. | | `taskInserted / taskUpdated / taskDeleted` | `OgeGanttTask*edEvent` | Fired only for applied changes — persist from these. | | `dependencyInserting / dependencyDeleting` | `OgeGanttDependency*ingEvent` | Cancelable link pre-events; inserting carries `predecessorKey`, `successorKey` and `type`. | | `dependencyInserted / dependencyDeleted` | `OgeGanttDependency*edEvent` | Applied link changes. | | `taskEditDialogShowing` | `OgeGanttDialogShowingEvent` | Cancelable, before the task dialog opens; replace `formItems` to customize the form (dx `onTaskEditDialogShowing` parity). | _Interaction_ | Name | Type | Description | | --- | --- | --- | | `taskClick / taskDblClick / taskContextMenu` | `OgeGanttTaskClickEvent` | Bar/row pointer events with the normalized task and the raw `MouseEvent`. Right-click also opens the **built-in context menu** (edit, new task/subtask, indent/outdent, delete — labels in `messages.menu`); listen to `taskContextMenu` to add your own entries alongside it. | | `selectionChanged` | `OgeGanttSelectionChangedEvent` | Single-row selection changed (task or `null`). | | `scaleTypeChange / selectedTaskKeyChange` | `OgeGanttScaleType / RowKey \| null` | The two-way model outputs. | #### Types | Name | Type | Description | | --- | --- | --- | | `OgeGanttTask` | `interface` | The normalized task — the payload of events and templates: `key`, `parentKey`, `source` (the original item), `title`, `start`/`end`, `progress`, `color`, baseline dates, `isSummary`/`isMilestone` and `level`. | | `OgeGanttDependency` | `interface` | The normalized link: `key`, `source`, `predecessorKey`, `successorKey`, `type`. | | `OgeGanttDependencyType` | `'FS' \| 'SS' \| 'FF' \| 'SF'` | Finish-to-start, start-to-start, finish-to-finish, start-to-finish. | | `OgeGanttScaleType` | `'hours' \| 'days' \| 'weeks' \| 'months'` | The timeline scale units. | | `OgeGanttColumn` | `interface` | A task-list column: `{ field, header?, widthPx?, format? }`. | | `OgeGanttStripLine` | `interface` | `{ start, end?, label?, color? }` — a chart marker line or range. | | `OgeGanttWorkCalendar` | `interface` | `{ workingDays?, holidays? }` — the work-time calendar (0 = Sunday; default working week Monday-Friday). | | `OgeGanttExportData / OgeGanttExportColumn` | `interface` | The exporter snapshot: `tasks`, `columns` (header + pane-identical `text()`), `rangeStart`/`rangeEnd`, `critical` keys and `resourceText()`. | | `OgeGanttTaskTitlePosition` | `'inside' \| 'outside' \| 'none'` | Task title placement relative to the bar. | | `[ogeGanttTaskTemplate]` | `structural directive (OgeGanttTaskTemplate)` | Replaces the bar's title content; context `OgeGanttTaskTemplateContext`: `{ $implicit: OgeGanttTask }`. | | `[ogeGanttTooltipTemplate]` | `structural directive (OgeGanttTooltipTemplate)` | Replaces the hover tooltip's content (default: title, dates + duration, progress, resources); context `OgeGanttTooltipTemplateContext`: `{ $implicit: OgeGanttTask }`. | ### Configuration #### Properties | Name | Type | Default | Description | | --- | --- | --- | --- | | `provideOgeGanttConfig(config)` | `Provider` | `—` | Configures every Gantt below the provider (`OgeGanttConfigInput`); shallow merge over `OGE_DEFAULT_GANTT_CONFIG` per top-level key — a partial `messages` replaces whole nested blocks. The token is `OGE_GANTT_CONFIG` (`OgeGanttConfig`). | | `messages` | `OgeGanttMessages` | `—` | Every user-facing string, aria labels included: `toolbar` (`OgeGanttToolbarMessages`), `columns` (`OgeGanttColumnMessages`), `dialog` (`OgeGanttDialogMessages`), `grid` (`OgeGanttGridMessages`, aria templates with `{token}` placeholders) and `announcements` (`OgeGanttAnnouncementMessages`, live-region templates). Defaults: `OGE_DEFAULT_GANTT_MESSAGES`. | | `locale` | `string \| undefined` | `—` | BCP 47 locale for every `Intl` format in scope; a per-instance `[locale]` input wins. | | `rowHeight` | `number` | `36` | Fixed row height in px — the invariant behind the row virtualization of both panes. | | `undoLimit` | `number` | `50` | Undo history depth. | ### Demos Each demo below is a complete standalone component — copy one whole and it compiles. #### Baselines ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeGantt } from '@oge-ui/gantt'; import type { OgeGanttStripLine } from '@oge-ui/gantt'; @Component({ selector: 'demo-root', imports: [OgeGantt], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly tasks = [ { id: 1, title: 'Data migration', start: new Date(2026, 7, 4), end: new Date(2026, 7, 11), baselineStart: new Date(2026, 7, 3), baselineEnd: new Date(2026, 7, 7), progress: 70, resourceId: 'ada', }, { id: 2, title: 'Cutover rehearsal', start: new Date(2026, 7, 11), end: new Date(2026, 7, 14), baselineStart: new Date(2026, 7, 10), baselineEnd: new Date(2026, 7, 12), resourceId: 'grace', }, ]; protected readonly stripLines: OgeGanttStripLine[] = [ { start: new Date(2026, 7, 18), label: 'Go-live', color: '#dc2626' }, { start: new Date(2026, 7, 14), end: new Date(2026, 7, 17), label: 'Freeze', }, ]; protected readonly people = [ { id: 'ada', text: 'Ada', color: '#7c3aed' }, { id: 'grace', text: 'Grace', color: '#0891b2' }, ]; protected readonly holidays = [new Date(2026, 7, 10)]; } ``` #### Config ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeGantt, provideOgeGanttConfig } from '@oge-ui/gantt'; @Component({ selector: 'demo-root', imports: [OgeGantt], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { // App-wide (main.ts / route providers): // provideOgeGanttConfig({ // locale: 'de', // rowHeight: 32, // messages: { // toolbar: { ...germanToolbar }, // }, // }) protected readonly tasks = [ { id: 1, title: 'Planung', start: new Date(2026, 7, 3), end: new Date(2026, 7, 10), progress: 25 }, ]; } ``` #### Critical path ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeGantt } from '@oge-ui/gantt'; @Component({ selector: 'demo-root', imports: [OgeGantt], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly tasks = [ { id: 1, title: 'Foundation', start: new Date(2026, 7, 3), end: new Date(2026, 7, 6), progress: 100 }, { id: 2, title: 'Framing', start: new Date(2026, 7, 6), end: new Date(2026, 7, 12), progress: 60 }, { id: 3, title: 'Electrical', start: new Date(2026, 7, 12), end: new Date(2026, 7, 15) }, { id: 4, title: 'Landscaping', start: new Date(2026, 7, 6), end: new Date(2026, 7, 10) }, { id: 5, title: 'Inspection', start: new Date(2026, 7, 17), end: new Date(2026, 7, 18) }, ]; protected readonly links = [ { id: 1, predecessorId: 1, successorId: 2 }, { id: 2, predecessorId: 2, successorId: 3 }, { id: 3, predecessorId: 3, successorId: 5 }, { id: 4, predecessorId: 4, successorId: 5 }, ]; } ``` #### Editing ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeGantt } from '@oge-ui/gantt'; import type { OgeGanttTaskDeletingEvent, OgeGanttTaskUpdatingEvent } from '@oge-ui/gantt'; @Component({ selector: 'demo-root', imports: [OgeGantt], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly tasks = [ { id: 1, title: 'Audit (done — locked)', start: new Date(2026, 7, 3), end: new Date(2026, 7, 6), progress: 100 }, { id: 2, title: 'Remediation', start: new Date(2026, 7, 6), end: new Date(2026, 7, 13), progress: 30 }, ]; protected protectDone( event: OgeGanttTaskUpdatingEvent>, ): void { if ((event.oldData['progress'] as number) === 100) event.cancel = true; } protected confirmDelete( event: OgeGanttTaskDeletingEvent>, ): void { event.cancel = !confirm('Delete this task?'); } } ``` #### Field mapping ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeGantt } from '@oge-ui/gantt'; @Component({ selector: 'demo-root', imports: [OgeGantt], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly workItems = [ { code: 'EPIC-1', subject: 'Checkout revamp', plan: { begin: '2026-08-03', finish: '2026-08-14' }, }, { code: 'T-1', parentCode: 'EPIC-1', subject: 'Payment API', plan: { begin: '2026-08-03', finish: '2026-08-07' }, done: 80, }, { code: 'T-2', parentCode: 'EPIC-1', subject: 'Wallet UI', plan: { begin: '2026-08-07', finish: '2026-08-14' }, done: 20, }, ]; protected readonly relations = [{ relId: 1, fromCode: 'T-1', toCode: 'T-2' }]; } ``` #### Getting started ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeGantt } from '@oge-ui/gantt'; @Component({ selector: 'demo-root', imports: [OgeGantt], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly tasks = [ { id: 1, title: 'Release 1.0', start: new Date(2026, 7, 3), end: new Date(2026, 7, 21) }, { id: 2, parentId: 1, title: 'Design', start: new Date(2026, 7, 3), end: new Date(2026, 7, 7), progress: 100, }, { id: 3, parentId: 1, title: 'Implementation', start: new Date(2026, 7, 7), end: new Date(2026, 7, 17), progress: 45, }, { id: 4, parentId: 1, title: 'Ship', start: new Date(2026, 7, 21), end: new Date(2026, 7, 21), // zero-length => milestone diamond }, ]; protected readonly links = [ { id: 'a', predecessorId: 2, successorId: 3 }, // FS is the default type { id: 'b', predecessorId: 3, successorId: 4 }, ]; } ``` #### Template ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeGantt, OgeGanttTaskTemplate, OgeGanttTooltipTemplate } from '@oge-ui/gantt'; @Component({ selector: 'demo-root', imports: [OgeGantt, OgeGanttTaskTemplate, OgeGanttTooltipTemplate], changeDetection: ChangeDetectionStrategy.OnPush, template: ` {{ task.title }} · {{ task.progress }}% {{ task.title }} {{ task.progress }}% complete `, }) export class Demo { protected readonly tasks = [ { id: 1, title: 'Usability study', start: new Date(2026, 7, 3), end: new Date(2026, 7, 12), progress: 55, color: '#0f766e' }, { id: 2, title: 'Findings report', start: new Date(2026, 7, 12), end: new Date(2026, 7, 17), progress: 10 }, ]; } ``` #### Toolbar ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeGantt } from '@oge-ui/gantt'; import type { OgeGanttScaleType } from '@oge-ui/gantt'; @Component({ selector: 'demo-root', imports: [OgeGantt], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly scale = signal('weeks'); protected readonly columns = [ { field: 'title' }, { field: 'progress' }, { field: 'owner', header: 'Owner' }, ] as const; protected readonly tasks = [ { id: 1, title: 'Discovery', start: new Date(2026, 6, 6), end: new Date(2026, 6, 24), progress: 100, owner: 'Ada' }, { id: 2, title: 'Build', start: new Date(2026, 6, 27), end: new Date(2026, 8, 4), progress: 40, owner: 'Grace' }, { id: 3, title: 'Rollout', start: new Date(2026, 8, 7), end: new Date(2026, 8, 25), owner: 'Ada' }, ]; } ``` #### Work export ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeGantt } from '@oge-ui/gantt'; import type { OgeGanttWorkCalendar } from '@oge-ui/gantt'; @Component({ selector: 'demo-root', imports: [OgeGantt], changeDetection: ChangeDetectionStrategy.OnPush, template: `
`, }) export class Demo { protected readonly calendar: OgeGanttWorkCalendar = { workingDays: [1, 2, 3, 4], // four-day week holidays: [new Date(2026, 7, 12)], }; protected readonly people = [ { id: 'ada', text: 'Ada', color: '#7c3aed' }, { id: 'grace', text: 'Grace', color: '#0891b2', // per-resource calendar: overrides workCalendar for Grace's tasks calendar: { workingDays: [1, 2, 3, 4, 5] }, }, ]; protected readonly tasks = [ { id: 1, title: 'Prototype', start: new Date(2026, 7, 3), end: new Date(2026, 7, 6), progress: 80, resourceId: ['ada', 'grace'], // multi-assignment }, { id: 2, title: 'Field test', start: new Date(2026, 7, 6), end: new Date(2026, 7, 11), resourceId: 'grace', }, ]; protected readonly links = [{ id: 1, predecessorId: 1, successorId: 2 }]; /** exceljs stays out of the initial bundle — loaded on first click. */ protected async exportExcel( gantt: OgeGantt, ): Promise { const { exportGanttToExcel } = await import('@oge-ui/gantt/export-excel'); await exportGanttToExcel(gantt, { filename: 'plan.xlsx' }); } /** jspdf loads lazily the same way. */ protected async exportPdf( gantt: OgeGantt, ): Promise { const { exportGanttToPdf } = await import('@oge-ui/gantt/export-pdf'); await exportGanttToPdf(gantt, { filename: 'plan.pdf', title: 'Plan' }); } /** PNG needs no third-party library at all — plain canvas drawing. */ protected async exportPng( gantt: OgeGantt, ): Promise { const { exportGanttToPng } = await import('@oge-ui/gantt/export-image'); await exportGanttToPng(gantt, { filename: 'plan.png' }); } } ``` ## @oge-ui/kanban Kanban board: columns + swimlanes over a plain card array with field mapping, WIP limits with drag previews, per-column virtualization, drag & drop with Escape-cancel, Ctrl+Arrow keyboard card moving with live announcements, built-in edit dialog, context menu and toolbar. **This package is commercially licensed — unlike the rest of the suite, it is not MIT. See https://ogeui.com/license before shipping it.** Docs: https://ogeui.com/components/kanban ### Entry points `@oge-ui/kanban` - values: `OGE_DEFAULT_KANBAN_CONFIG`, `OGE_DEFAULT_KANBAN_MESSAGES`, `OGE_KANBAN_CONFIG`, `OgeKanban`, `OgeKanbanCardTemplate`, `OgeKanbanColumnHeaderTemplate`, `provideOgeKanbanConfig` - types: `OgeKanbanAnnouncementMessages`, `OgeKanbanBoardMessages`, `OgeKanbanCard`, `OgeKanbanCardAddedEvent`, `OgeKanbanCardAddingEvent`, `OgeKanbanCardDeletedEvent`, `OgeKanbanCardDeletingEvent`, `OgeKanbanCardEvent`, `OgeKanbanCardMovedEvent`, `OgeKanbanCardMovingEvent`, `OgeKanbanCardTemplateContext`, `OgeKanbanCardUpdatedEvent`, `OgeKanbanCardUpdatingEvent`, `OgeKanbanColumn`, `OgeKanbanColumnAddedEvent`, `OgeKanbanColumnAddingEvent`, `OgeKanbanColumnHeaderTemplateContext`, `OgeKanbanColumnReorderedEvent`, `OgeKanbanConfig`, `OgeKanbanConfigInput`, `OgeKanbanDialogMessages`, `OgeKanbanEditDialogShowingEvent`, `OgeKanbanEditorModel`, `OgeKanbanFieldExpr`, `OgeKanbanMenuMessages`, `OgeKanbanMessages`, `OgeKanbanToolbarMessages` ### OgeKanban — `` #### Properties _Data_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `dataSource` | `readonly T[]` | `[]` | Card items — a plain array, copied into an internal working set; the input is never mutated. Edits surface through the past-tense events. | | `keyExpr / columnExpr / titleExpr / descriptionExpr / colorExpr` | `string \| ((item: T) => unknown)` | `'id' / 'status' / 'title' / 'description' / 'color'` | Card field mapping: names (dotted paths reach nested objects) or getter functions. `columnExpr` holds the card's column key; a card with no resolvable column lands in the untitled column instead of being dropped. | | `orderExpr` | `string \| ((item: T) => unknown) \| undefined` | `undefined` | Numeric in-column sort order. Unset, the array order is the board order and moves reorder the working set; set, moves write a midpoint order value back onto the item (sequential renumber of the cell when the midpoint has no room). | | `swimlaneExpr` | `string \| ((item: T) => unknown) \| undefined` | `undefined` | Set = the board renders collapsible swimlane rows (first-seen data order); each lane holds every column. | | `tagsExpr / assigneeExpr` | `string \| ((item: T) => unknown) \| undefined` | `undefined` | Tag chips and assignee avatars (initials). Both accept a single value _or_ an array — write-back preserves the storage shape. | | `dueDateExpr / priorityExpr` | `string \| ((item: T) => unknown) \| undefined` | `undefined` | Due-date badge (danger when overdue; formatted through `locale`) and the priority indicator (colored by value: `'low'` green, `'medium'`/`'normal'` amber, `'high'`/`'urgent'`/`'critical'` red). | | `searchExprs` | `readonly (string \| ((item: T) => unknown))[] \| undefined` | `undefined` | Extra fields the toolbar search matches, beyond the built-in title + description + tags + assignees haystack. Matching is fold-insensitive (accents, Turkish İ/i). | | `columns` | `readonly OgeKanbanColumn[] \| undefined` | `undefined` | Declared columns (`{ key, title?, color?, wipLimit?, minCount?, collapsed?, allowAdding?, allowDrag?, allowDrop?, transitionColumns? }`); unset = derived from the data's distinct column keys in first-seen order. Cards in undeclared columns stay in the data but leave the view. | _State (two-way)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `collapsedColumns / collapsedSwimlanes` | `model` | `[]` | Collapsed column keys (slim vertical pills) and collapsed swimlane keys. | | `columnOrder` | `model` | `[]` | Persisted column key order (empty = declared order); written by header drags when `allowColumnReordering` is on. | | `selectedCardKey` | `model` | `null` | The selected card's key — single selection. | _Behavior_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `virtualScrolling` | `boolean` | `true` | Per-column card windowing over a fixed `cardHeight` — 10k cards stay smooth. Rich variable-height templates may opt out (`false`), the documented exception. | | `cardHeight` | `number \| undefined` | `undefined (config: 112)` | Fixed card height in px; also drives the drag hit-testing and the keyboard scroll-into-view math. | | `showToolbar` | `boolean` | `true` | The built-in toolbar: primary add button, collapse/expand-all pill and the search box. | | `allowAdding / allowUpdating / allowDeleting / allowDragging / allowColumnReordering / allowColumnAdding` | `boolean` | `true / true / true / true / false / false` | Capability gates for the toolbar, dialog, menu, hover quick actions, keyboard shortcuts and drags. `allowColumnAdding` renders the "+ Add column" ghost column (inline composer → cancelable `columnAdding` → `columnAdded`). Per-column `allowAdding: false` overrides the board. | | `columnWidth` | `number` | `300` | Fixed column track width in px — headers stay legible and the board scrolls horizontally, Trello-style. | | `cardColorMode` | `'stripe' \| 'surface'` | `'stripe'` | How `colorExpr` renders: an accent bar on the card's edge, or the whole card surface tinted with the color. | | `dialogItems` | `readonly OgeFormItemData[] \| undefined` | `undefined` | Replaces the edit dialog's default form wholesale (generic `OgeForm` items); `cardEditDialogShowing` can still adjust per open. The default form only renders editors for fields the board actually maps. | | `readOnly` | `boolean` | `false` | One switch over every `allow*` capability; the context menu falls back to the browser's native menu. | | `messages / locale` | `Partial / string \| undefined` | `{} / undefined` | Per-instance overrides of the DI config (`provideOgeKanbanConfig`). Every user-facing string, aria labels and live-region templates included, lives in the messages interface; `locale` drives every Intl format. | #### Methods _Methods_ | Name | Type | Description | | --- | --- | --- | | `addCard(item)` | `(item: T) => void` | Programmatic insert through the cancelable `cardAdding` pipeline; the column and swimlane resolve from the item's own fields. | | `updateCard(original, updated)` | `(original: T, updated: T) => void` | Programmatic update through the cancelable `cardUpdating` pipeline. | | `deleteCard(item)` | `(item: T) => void` | Programmatic delete through the cancelable `cardDeleting` pipeline. | | `moveCard(key, toColumn, toIndex?, toSwimlane?)` | `(key: unknown, toColumn: string, toIndex?: number, toSwimlane?: string \| null) => void` | Moves a card (append when `toIndex` is omitted) through the cancelable `cardMoving` pipeline — the same path the drag, the Ctrl+Arrow twin and the context menu commit through. | | `editCard(card) / openNewCard(column, swimlane)` | `(…) => void` | Opens the built-in dialog for an existing card / prefilled for a new card, through the `cardEditDialogShowing` hook. | | `closeDialog()` | `() => void` | Closes the edit dialog without saving; fires `cardEditDialogHidden`. | | `collapseAllColumns() / expandAllColumns()` | `() => void` | The toolbar buttons, callable from code. | #### Events _Events_ | Name | Type | Description | | --- | --- | --- | | `cardClick / cardDblClick / cardContextMenu` | `OgeKanbanCardEvent` | Pointer interactions with a card (`{ card, event }`). Double-click also opens the editor; right-click fires _before_ the built-in menu opens, so app handlers can coexist with it. | | `cardAdding / cardUpdating / cardDeleting / cardMoving` | `Oge…Event (mutable cancel)` | Cancelable pre-events — set `cancel = true` to veto. `cardMoving` carries `{ card, fromColumn, toColumn, fromIndex, toIndex, fromSwimlane, toSwimlane }` and guards drags, keyboard moves and programmatic moves alike. | | `cardAdded / cardUpdated / cardDeleted / cardMoved` | `Oge…Event` | Past-tense events fire only for applied changes and carry the data to persist (`cardMoved.card` is the _updated_ item, orderExpr write-back included). | | `cardEditDialogShowing` | `OgeKanbanEditDialogShowingEvent` | Cancelable + customization point before the dialog opens: `formItems` arrives pre-populated with the default `OgeForm` items and may be mutated or replaced (dx `onAppointmentFormOpening` parity). | | `cardEditDialogHidden` | `void` | The edit dialog closed — saved, cancelled, deleted or `closeDialog()` (Syncfusion `dialogClose` parity). | | `columnReordered` | `OgeKanbanColumnReorderedEvent` | A header drag committed a new order: `{ column, fromIndex, toIndex, columnOrder }`. | | `columnAdding / columnAdded` | `OgeKanbanColumnAddingEvent / OgeKanbanColumnAddedEvent` | The "+ Add column" composer's cancelable pre-event and its past-tense commit. | #### Types _Templates_ | Name | Type | Description | | --- | --- | --- | | `*ogeKanbanCardTemplate` | `OgeKanbanCardTemplateContext` | Replaces the card body (`$implicit` card with its `source`, plus `column` and `swimlane`). Drag, keyboard and ARIA stay on the component. | | `*ogeKanbanColumnHeaderTemplate` | `OgeKanbanColumnHeaderTemplateContext` | Replaces the column header's title row (`$implicit` column, `count`, `wip`); the collapse affordance stays. An OGE extra — no reference library templates its headers. | _Configuration_ | Name | Type | Description | | --- | --- | --- | | `provideOgeKanbanConfig(config)` | `(config: OgeKanbanConfigInput) => Provider` | DI-level configuration: `messages` (shallow-merged per top-level block), `locale`, `cardHeight`. | | `OgeKanbanMessages` | `interface` | Every user-facing string: `toolbar`, `menu`, `dialog`, `board` (aria label templates with `{title}`/`{count}`/`{limit}` tokens) and `announcements` (live-region templates). | | `OgeKanbanColumn` | `interface` | `{ key, title?, color?, wipLimit?, minCount?, collapsed?, allowAdding?, allowDrag?, allowDrop?, transitionColumns? }` — the declared column shape. `wipLimit`/`minCount` drive the danger/warning badges; `allowDrag`/`allowDrop`/`transitionColumns` gate interactive moves (programmatic `moveCard` is deliberately not gated). | | `OgeKanbanCard` | `interface` | The normalized card handed to templates and events: `key`, `source` (your item, unchanged), `column`, `title`, `description`, `color`, `order`, `swimlane`, `tags`, `assignees`, `dueDate`, `priority`. | ### Demos Each demo below is a complete standalone component — copy one whole and it compiles. #### Config ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeKanban, provideOgeKanbanConfig } from '@oge-ui/kanban'; @Component({ selector: 'demo-root', imports: [OgeKanban], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { // App-wide (main.ts / route providers): // provideOgeKanbanConfig({ // locale: 'de-DE', // cardHeight: 104, // messages: { // toolbar: { // label: 'Kanban-Werkzeugleiste', // addCard: 'Neue Karte', // collapseAll: 'Alle einklappen', // expandAll: 'Alle ausklappen', // searchLabel: 'Karten durchsuchen', // searchPlaceholder: 'Suchen…', // clearSearch: 'Suche löschen', // }, // }, // }) protected readonly tasks = [ { id: 1, status: 'Offen', title: 'Angebot schreiben', due: new Date(2026, 7, 14) }, { id: 2, status: 'In Arbeit', title: 'Rechnung prüfen' }, { id: 3, status: 'Fertig', title: 'Kickoff-Termin' }, ]; } ``` #### Dialog events ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeKanban } from '@oge-ui/kanban'; import type { OgeKanbanEditDialogShowingEvent } from '@oge-ui/kanban'; @Component({ selector: 'demo-root', imports: [OgeKanban], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected onDialogShowing(event: OgeKanbanEditDialogShowingEvent): void { // drop the color field and add a sprint picker event.formItems = [ ...event.formItems.filter((item) => item.field !== 'color'), { field: 'sprint', label: 'Sprint', editorType: 'selectBox', editorOptions: { items: ['Sprint 41', 'Sprint 42', 'Sprint 43'] }, }, ]; } protected readonly tasks = [ { id: 1, status: 'todo', title: 'Double-click me', notes: 'Custom form field below' }, { id: 2, status: 'doing', title: 'Right-click me for the menu' }, ]; } ``` #### Drag drop ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeKanban } from '@oge-ui/kanban'; import type { OgeKanbanCardMovingEvent } from '@oge-ui/kanban'; @Component({ selector: 'demo-root', imports: [OgeKanban], changeDetection: ChangeDetectionStrategy.OnPush, template: `

{{ log() }}

`, }) export class Demo { protected readonly log = signal('drag a card'); protected onMoving(event: OgeKanbanCardMovingEvent): void { // veto example: nothing may leave "done" if (event.fromColumn === 'done') event.cancel = true; } protected readonly tasks = [ { id: 1, status: 'todo', title: 'Refactor auth', rank: 0 }, { id: 2, status: 'todo', title: 'Ship dark mode', rank: 1 }, { id: 3, status: 'doing', title: 'Bundle size audit', rank: 0 }, { id: 4, status: 'done', title: 'This card is locked in', rank: 0 }, ]; } ``` #### Getting started ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeKanban } from '@oge-ui/kanban'; @Component({ selector: 'demo-root', imports: [OgeKanban], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly columns = [ { key: 'todo', title: 'To do', color: '#64748b' }, { key: 'doing', title: 'In progress', color: '#2563eb', wipLimit: 3 }, { key: 'review', title: 'Review', color: '#d97706' }, { key: 'done', title: 'Done', color: '#16a34a' }, ]; protected readonly tasks = [ { id: 1, status: 'doing', title: 'Checkout revamp', notes: 'New payment flow behind the feature flag', owner: 'Ada Lovelace', due: new Date(2026, 7, 21), priority: 'high', labels: ['feature'], }, { id: 2, status: 'todo', title: 'Wallet UI polish', owner: ['Grace Hopper', 'Alan Turing'], priority: 'medium', labels: ['design'], }, { id: 3, status: 'todo', title: 'Upgrade CI runners', priority: 'low' }, { id: 4, status: 'review', title: 'Fix login crash', notes: 'Repro: expired refresh token', owner: 'Ada Lovelace', due: new Date(2026, 7, 5), priority: 'high', labels: ['bug'], }, { id: 5, status: 'done', title: 'Q3 roadmap draft' }, ]; } ``` #### Keyboard ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeKanban } from '@oge-ui/kanban'; @Component({ selector: 'demo-root', imports: [OgeKanban], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly tasks = [ { id: 1, status: 'todo', title: 'Tab to the board, then arrow around' }, { id: 2, status: 'todo', title: 'Ctrl+ArrowRight moves me' }, { id: 3, status: 'doing', title: 'Enter opens my dialog' }, ]; } ``` #### Swimlanes ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeKanban } from '@oge-ui/kanban'; @Component({ selector: 'demo-root', imports: [OgeKanban], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly collapsedLanes = signal([]); protected readonly tasks = [ { id: 1, team: 'Platform', status: 'doing', title: 'Sharding rollout' }, { id: 2, team: 'Platform', status: 'todo', title: 'Postgres 18 upgrade' }, { id: 3, team: 'Mobile', status: 'todo', title: 'Push notification opt-in' }, { id: 4, team: 'Mobile', status: 'done', title: 'Biometric login' }, { id: 5, team: 'Web', status: 'doing', title: 'Design token migration' }, ]; } ``` #### Template ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeKanban, OgeKanbanCardTemplate } from '@oge-ui/kanban'; import type { OgeKanbanCard } from '@oge-ui/kanban'; @Component({ selector: 'demo-root', imports: [OgeKanban, OgeKanbanCardTemplate], changeDetection: ChangeDetectionStrategy.OnPush, template: `
{{ card.title }} {{ field(card, 'version') }}
`, }) export class Demo { protected field(card: OgeKanbanCard, name: string): string { return String((card.source as Record)[name] ?? ''); } protected readonly deployments = [ { id: 1, stage: 'staging', service: 'api-gateway', version: 'v2.14.0', health: 98 }, { id: 2, stage: 'staging', service: 'search', version: 'v1.9.2', health: 74 }, { id: 3, stage: 'production', service: 'billing', version: 'v3.1.1', health: 100 }, ]; } ``` #### Wip ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeKanban } from '@oge-ui/kanban'; @Component({ selector: 'demo-root', imports: [OgeKanban], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly tasks = [ { id: 1, status: 'doing', title: 'Payments API' }, { id: 2, status: 'doing', title: 'Search relevance' }, { id: 3, status: 'doing', title: 'One too many — WIP exceeded' }, { id: 4, status: 'todo', title: 'Docs sweep' }, { id: 5, status: 'done', title: 'Login rate limits' }, ]; } ``` ## @oge-ui/scheduler Scheduler / event calendar: day, week and month views, all-day strip, pure-kernel overlap layout, drag & resize with Escape-cancel, appointment popup and form editing. **This package is commercially licensed — unlike the rest of the suite, it is not MIT. See https://ogeui.com/license before shipping it.** Docs: https://ogeui.com/components/scheduler ### Entry points `@oge-ui/scheduler` - values: `OGE_DEFAULT_SCHEDULER_CONFIG`, `OGE_DEFAULT_SCHEDULER_MESSAGES`, `OGE_SCHEDULER_CONFIG`, `OgeAppointmentTemplate`, `OgeDateHeaderTemplate`, `OgeScheduler`, `OgeSchedulerCellTemplate`, `provideOgeSchedulerConfig` - types: `OgeAppointmentTemplateContext`, `OgeDateHeaderTemplateContext`, `OgeSchedulerAnnouncementMessages`, `OgeSchedulerAppointment`, `OgeSchedulerAppointmentAddedEvent`, `OgeSchedulerAppointmentAddingEvent`, `OgeSchedulerAppointmentClickEvent`, `OgeSchedulerAppointmentDeletedEvent`, `OgeSchedulerAppointmentDeletingEvent`, `OgeSchedulerAppointmentUpdatedEvent`, `OgeSchedulerAppointmentUpdatingEvent`, `OgeSchedulerCellClickEvent`, `OgeSchedulerCellTemplateContext`, `OgeSchedulerConfig`, `OgeSchedulerConfigInput`, `OgeSchedulerEditorMessages`, `OgeSchedulerEditorShowingEvent`, `OgeSchedulerGridMessages`, `OgeSchedulerMenuMessages`, `OgeSchedulerMessages`, `OgeSchedulerPopupMessages`, `OgeSchedulerRangeSelectedEvent`, `OgeSchedulerRecurrenceScopeMessages`, `OgeSchedulerReminderEvent`, `OgeSchedulerResource`, `OgeSchedulerResourceItem`, `OgeSchedulerToolbarMessages`, `OgeSchedulerView`, `OgeSchedulerViewOptions`, `OgeSchedulerWorkHours` ### OgeScheduler — `` #### Properties _Data_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `dataSource` | `readonly T[] \| DataSource \| null` | `null` | Appointment items: a plain array (copied into an internal working set — the input is never mutated) or any `@oge-ui/core` `DataSource`, whose `insert`/`update`/`remove` are used for CRUD when present. | | `keyExpr` | `string \| ((item: T) => unknown)` | `'id'` | Key field or selector; items without a resolvable key fall back to their index. | | `textExpr / startDateExpr / endDateExpr / allDayExpr / colorExpr / locationExpr / descriptionExpr / disabledExpr` | `string \| ((item: T) => unknown)` | `'text' / 'startDate' / …` | Field mapping: names (dotted paths reach nested objects) or getter functions. String dates parse as _local_ wall time and write back in the same storage shape. | | `recurrenceRuleExpr / recurrenceExceptionExpr` | `string \| ((item: T) => unknown)` | `'recurrenceRule' / 'recurrenceException'` | Recurrence fields: rules in the documented RFC 5545 subset expand into occurrence instances in every view; exceptions are comma-separated EXDATE stamps. | | `reminderExpr` | `string \| ((item: T) => unknown)` | `'reminder'` | Minutes before the start a reminder fires (see `reminderTriggered`) — **OGE extra** (Outlook parity). | | `resources` | `readonly OgeSchedulerResource[]` | `[]` | Resource kinds (`{ fieldExpr, items, label?, useColorAsDefault? }`): editor select fields, default appointment colors and timeline rows. | | `groups` | `readonly string[]` | `[]` | Resource field grouping the views (first entry): timeline rows, and day/week columns split per resource — grouped cells prefill the resource on create, and drags across subcolumns/rows reassign it. | | `recurrenceEditMode` | `'dialog' \| 'occurrence' \| 'series'` | `'dialog'` | How edits to a recurring occurrence apply: ask per action, always detach the occurrence (EXDATE + standalone copy), or always change the series. | _Date & views_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `currentDate` | `Date` | `new Date()` | Anchor date of the visible period. Two-way (`[(currentDate)]`); writes clamp into `[min, max]`. | | `currentView` | `'day' \| 'week' \| 'workWeek' \| 'month' \| 'agenda' \| 'timelineDay' \| 'timelineWeek' \| 'year'` | `'week'` | The active view. Two-way (`[(currentView)]`). | | `views` | `readonly (OgeSchedulerView \| OgeSchedulerViewOptions)[]` | `['day', 'week', 'month']` | View-switcher entries; option objects override `name`, `dayStartHour`, `dayEndHour` and `cellDuration` per view. | | `min / max` | `Date \| undefined` | `—` | Navigable date bounds: navigation buttons disable at the edges and every date write clamps. | | `firstDayOfWeek` | `number \| undefined` | `—` | First day of week (0 = Sunday); `undefined` resolves from the locale via `Intl.Locale.weekInfo`. | | `hiddenWeekDays` | `readonly number[] \| undefined` | `—` | Weekdays removed from the week views; the `workWeek` view always drops the weekend on top. | | `dayStartHour / dayEndHour / cellDuration` | `number` | `0 / 24 / 30` | Visible hour window and slot raster (minutes) of the time grids. | | `agendaDuration` | `number` | `7` | Days the agenda view lists from the anchor date. | | `scrollTime` | `number \| undefined` | `—` | Initial scroll position of the day/week body in hours (fractions allowed, e.g. `8.5`); re-applied on view/period changes. | _Behavior_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `allowAdding / allowUpdating / allowDeleting / allowDragging / allowResizing` | `boolean` | `true` | Per-capability editing gates. | | `readOnly` | `boolean` | `false` | **Display-only shorthand**: overrides every `allow*` flag at once and hides the editing affordances. | | `snapDuration` | `number \| undefined` | `—` | Drag/resize snap raster in minutes; defaults to `cellDuration`. | | `workHours` | `OgeSchedulerWorkHours \| null` | `null` | Working-hours emphasis: cells outside `{ start, end, days? }` get the off-hours shading. | | `showAddButton` | `boolean` | `true` | Shows the toolbar "new appointment" button (Outlook parity) — creation without double-click; hidden while `readOnly` or `allowAdding=false`. | | `showAllDayPanel` | `boolean` | `true` | Shows the all-day strip in the day/week views. | | `showCurrentTimeIndicator` | `boolean` | `true` | The accent now-line in today's column. | | `shadeUntilCurrentTime` | `boolean` | `false` | Dims today's column above the now-line. | | `maxAppointmentsPerCell` | `number \| 'auto'` | `'auto'` | Month-view lane budget per cell; the overflow folds into a "+N more" button that drills into the day view. | | `locale` | `string \| undefined` | `—` | BCP 47 locale for every `Intl` format; defaults to the browser locale. | | `messages` | `Partial` | `{}` | Per-instance message overrides, merged over the DI config per top-level block. | | `dateNavigatorText` | `(start: Date, end: Date, view: OgeSchedulerView) => string` | `—` | Custom period-title formatter for the toolbar. | #### Methods | Name | Type | Description | | --- | --- | --- | | `addAppointment(appointmentData)` | `void` | Inserts programmatically through the same cancelable `appointmentAdding` pipeline as interactive creation. | | `updateAppointment(appointmentData, patch)` | `void` | Applies a patch through the guarded update pipeline. | | `deleteAppointment(appointmentData)` | `void` | Deletes through the guarded delete pipeline. | | `showAppointmentPopup(appointmentData?, createNew?)` | `void` | Opens the editing form — prefilled create form with `createNew`/no data, edit form otherwise (dx parity: the method opens the _form_). | | `hideAppointmentPopup()` | `void` | Closes the editor dialog and the summary popup. | | `scrollToTime(hours, minutes?)` | `void` | Scrolls the day/week body to the given time of day. | | `scrollTo(date)` | `void` | Navigates to `date` and scrolls to its time of day. | | `getStartViewDate() / getEndViewDate()` | `Date` | First moment / exclusive end of the visible period. | | `getDataSource()` | `readonly T[] \| DataSource \| null` | The bound data source, as given. | | `focus()` | `void` | Focuses the active view's grid (roving cell). | | `goToday() / navigate(direction)` | `void` | Toolbar equivalents: jump to today / step one period (respects `min`/`max`). | #### Events _Editing (cancelable pipeline)_ | Name | Type | Description | | --- | --- | --- | | `appointmentAdding / appointmentUpdating / appointmentDeleting` | `OgeSchedulerAppointment*ingEvent` | Cancelable pre-events — set `cancel = true` to veto before the store changes. | | `appointmentAdded / appointmentUpdated / appointmentDeleted` | `OgeSchedulerAppointment*edEvent` | Fired only for applied changes — persist from these when binding plain arrays. | | `editorShowing` | `OgeSchedulerEditorShowingEvent` | Cancelable, before the editor opens; replace `formItems` to customize the form (dx `onAppointmentFormOpening` parity). | _Reminders_ | Name | Type | Description | | --- | --- | --- | | `reminderTriggered` | `OgeSchedulerReminderEvent` | Fires once per occurrence when `start − reminder` minutes is reached (checked about every 30 s while mounted) — **OGE extra**. | _Interaction_ | Name | Type | Description | | --- | --- | --- | | `appointmentClick / appointmentDblClick` | `OgeSchedulerAppointmentClickEvent` | Chip clicks; single click also opens the popup, double click the editor. | | `cellClick / cellDblClick` | `OgeSchedulerCellClickEvent` | Empty-cell clicks; double click also opens the prefilled create editor. | | `appointmentContextMenu / cellContextMenu` | `OgeSchedulerAppointmentClickEvent / OgeSchedulerCellClickEvent` | Right-clicks with full payloads. A **built-in context menu** also opens (chip: edit/delete through the guarded pipelines incl. recurrence scope; cell: new appointment prefilled at that slot — labels in `messages.menu`); listen to these events to add your own entries alongside it. | | `rangeSelected` | `OgeSchedulerRangeSelectedEvent` | A drag-to-create cell-range selection landed; the prefilled create editor opens next. | | `currentDateChange / currentViewChange` | `Date / OgeSchedulerView` | The two-way model outputs. | #### Types | Name | Type | Description | | --- | --- | --- | | `OgeSchedulerAppointment` | `interface` | The normalized appointment: `key`, `source` (the original item), `text`, `startDate`/`endDate`, `allDay`, `color`, `description`, recurrence fields and `disabled`. | | `OgeSchedulerViewOptions` | `interface` | Per-view overrides: `type`, `name`, `dayStartHour`, `dayEndHour`, `cellDuration`. | | `OgeSchedulerWorkHours` | `interface` | `{ start, end, days? }` — the emphasized working hours. | | `[ogeAppointmentTemplate]` | `structural directive` | Replaces the chip content; context `{ $implicit: OgeSchedulerAppointment, view }`. | | `OgeSchedulerCellTemplate` | `structural directive [ogeCellTemplate]` | **OGE extra** — custom empty-cell content; context `{ $implicit: Date, view, allDay }`. | | `OgeDateHeaderTemplate` | `structural directive [ogeDateHeaderTemplate]` | **OGE extra** — custom date-header content; context `{ $implicit: Date, view }`. | ### Configuration #### Properties | Name | Type | Default | Description | | --- | --- | --- | --- | | `provideOgeSchedulerConfig(config)` | `Provider` | `—` | Configures every scheduler below the provider; shallow merge per top-level key (a partial `messages` replaces whole nested blocks). | | `messages` | `OgeSchedulerMessages` | `—` | Every user-facing string, aria labels included: `toolbar` (labels, view names, date-navigator), `popup`, `editor` (titles, field labels, validation), `grid` (aria templates with `{token}` placeholders, "+{count} more") and `announcements` (live-region templates). | | `minAppointmentMinutes` | `number` | `15` | Minimum rendered chip height in minutes — zero-length reminders stay clickable. | ### Demos Each demo below is a complete standalone component — copy one whole and it compiles. #### Config ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeScheduler, provideOgeSchedulerConfig } from '@oge-ui/scheduler'; // Typically in app.config.ts — shown per-component here. The merge is // shallow per top-level key: replace whole nested blocks, not single strings. // A [messages] input on one instance overrides the DI value the same way. @Component({ selector: 'demo-root', imports: [OgeScheduler], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly date = new Date(2026, 7, 6); } export const appConfig = { providers: [ provideOgeSchedulerConfig({ messages: { toolbar: { label: 'Terminplaner', today: 'Heute', previous: 'Zurück', next: 'Weiter', viewSwitcherLabel: 'Ansichten', dateNavigatorLabel: 'Datum wählen', newAppointment: 'Neu', viewNames: { day: 'Tag', week: 'Woche', workWeek: 'Arbeitswoche', month: 'Monat', agenda: 'Agenda', timelineDay: 'Zeitachse Tag', timelineWeek: 'Zeitachse Woche', year: 'Jahr', }, }, }, }), ], }; ``` #### Editing ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeScheduler } from '@oge-ui/scheduler'; import type { OgeSchedulerAppointmentAddingEvent, OgeSchedulerAppointmentDeletingEvent } from '@oge-ui/scheduler'; @Component({ selector: 'demo-root', imports: [OgeScheduler], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly date = new Date(2026, 7, 6); protected readonly appointments = [ { id: 1, text: 'Release', startDate: new Date(2026, 7, 7, 14, 0), endDate: new Date(2026, 7, 7, 15, 0), }, ]; protected blockWeekends( event: OgeSchedulerAppointmentAddingEvent>, ): void { const day = (event.appointmentData['startDate'] as Date).getDay(); if (day === 0 || day === 6) event.cancel = true; } protected confirmDelete( event: OgeSchedulerAppointmentDeletingEvent>, ): void { event.cancel = !confirm('Delete this appointment?'); } } ``` #### Field mapping ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeScheduler } from '@oge-ui/scheduler'; @Component({ selector: 'demo-root', imports: [OgeScheduler], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly date = new Date(2026, 7, 6); protected readonly meetings = [ { meetingId: 'a', subject: 'Standup', slot: { begin: '2026-08-06T09:00', finish: '2026-08-06T09:15' }, badge: '#7c3aed', }, { meetingId: 'b', subject: '1:1', slot: { begin: '2026-08-06T09:00', finish: '2026-08-06T10:00' }, badge: '#0891b2', }, ]; } ``` #### Getting started ```ts import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { OgeScheduler } from '@oge-ui/scheduler'; import type { OgeSchedulerView } from '@oge-ui/scheduler'; @Component({ selector: 'demo-root', imports: [OgeScheduler], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly date = signal(new Date(2026, 7, 6)); protected readonly view = signal('week'); protected readonly appointments = [ { id: 1, text: 'Design review', startDate: new Date(2026, 7, 4, 9, 30), endDate: new Date(2026, 7, 4, 11, 0), color: '#2563eb', }, { id: 2, text: 'Sprint planning', startDate: new Date(2026, 7, 6, 10, 0), endDate: new Date(2026, 7, 6, 12, 0), color: '#16a34a', }, { id: 3, text: 'Customer workshop', startDate: new Date(2026, 7, 5), endDate: new Date(2026, 7, 7), allDay: true, color: '#d97706', }, ]; } ``` #### Planning ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeScheduler } from '@oge-ui/scheduler'; @Component({ selector: 'demo-root', imports: [OgeScheduler], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly date = new Date(2026, 7, 6); protected readonly min = new Date(2026, 6, 1); protected readonly max = new Date(2026, 8, 30); protected readonly appointments = [ { id: 1, text: 'Architecture sync', startDate: new Date(2026, 7, 6, 9, 30), endDate: new Date(2026, 7, 6, 10, 30), }, { id: 2, text: 'Late incident review', startDate: new Date(2026, 7, 6, 18, 0), endDate: new Date(2026, 7, 6, 19, 0), color: '#dc2626', }, ]; } ``` #### Teams ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeScheduler } from '@oge-ui/scheduler'; import type { OgeSchedulerResource } from '@oge-ui/scheduler'; @Component({ selector: 'demo-root', imports: [OgeScheduler], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly date = new Date(2026, 7, 6); protected readonly resources: OgeSchedulerResource[] = [ { fieldExpr: 'ownerId', label: 'Owner', useColorAsDefault: true, items: [ { id: 'ada', text: 'Ada', color: '#7c3aed' }, { id: 'grace', text: 'Grace', color: '#0891b2' }, ], }, ]; protected readonly appointments = [ { id: 1, text: 'Daily standup', startDate: new Date(2026, 7, 3, 9, 0), endDate: new Date(2026, 7, 3, 9, 15), recurrenceRule: 'FREQ=DAILY;BYDAY=MO,TU,WE,TH,FR', ownerId: 'ada', reminder: 5, }, { id: 2, text: 'Design pairing', startDate: new Date(2026, 7, 5, 14, 0), endDate: new Date(2026, 7, 5, 16, 0), ownerId: 'grace', }, { id: 3, text: 'Ops review', startDate: new Date(2026, 7, 6, 11, 0), endDate: new Date(2026, 7, 6, 12, 0), }, ]; } ``` #### Template ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeScheduler, OgeAppointmentTemplate } from '@oge-ui/scheduler'; @Component({ selector: 'demo-root', imports: [OgeScheduler, OgeAppointmentTemplate], changeDetection: ChangeDetectionStrategy.OnPush, template: ` {{ appointment.text }} @if (appointment.description) { {{ appointment.description }} } `, }) export class Demo { protected readonly date = new Date(2026, 7, 6); protected readonly appointments = [ { id: 1, text: 'Usability session', description: 'Recording — join muted', startDate: new Date(2026, 7, 6, 9, 0), endDate: new Date(2026, 7, 6, 11, 0), color: '#0f766e', }, ]; } ``` #### Views ```ts import { ChangeDetectionStrategy, Component } from '@angular/core'; import { OgeScheduler } from '@oge-ui/scheduler'; @Component({ selector: 'demo-root', imports: [OgeScheduler], changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, }) export class Demo { protected readonly date = new Date(2026, 7, 6); protected readonly appointments = [ { id: 1, text: 'Board meeting', startDate: new Date(2026, 7, 3, 9, 0), endDate: new Date(2026, 7, 3, 10, 0), }, { id: 2, text: 'Audit', startDate: new Date(2026, 7, 3, 10, 0), endDate: new Date(2026, 7, 3, 11, 0), color: '#dc2626', }, { id: 3, text: 'Retro', startDate: new Date(2026, 7, 3, 15, 0), endDate: new Date(2026, 7, 3, 16, 0), color: '#16a34a', }, { id: 4, text: 'Conference', startDate: new Date(2026, 7, 12), endDate: new Date(2026, 7, 15), allDay: true, color: '#7c3aed', }, ]; } ``` ## @oge-ui/core Framework-free data engine shared by every package: sort/filter/group/aggregate pipelines, selection and virtualization math. Installed automatically — you rarely import it directly. ### Entry points `@oge-ui/core` - values: `ArrayDataSource`, `CustomDataSource`, `LocalPivotStore`, `ODataDataSource`, `OGE_PAGE_ELLIPSIS`, `OffsetTree`, `PivotEngine`, `accValue`, `accumulate`, `addDays`, `addMinutes`, `addMonths`, `addYears`, `ancestorsOf`, `applyDisplayModes`, `applyFilter`, `applyPaging`, `applySort`, `buildCsv`, `buildODataQuery`, `buildPivotCsv`, `buildSearchFilter`, `buildSearchHighlightHtml`, `buildTreeIndex`, `clampDate`, `clampValue`, `colorsEqual`, `compareValues`, `computePivot`, `computeSummaries`, `computeTreeCheckStates`, `computeWindow`, `constrainRangeValue`, `contrastForeground`, `createAcc`, `createFieldAccessor`, `createFilterPredicate`, `createTypeAheadBuffer`, `drillDownFilter`, `drillDownRows`, `edgeEnabledIndex`, `escapeCsvCell`, `filterTreeKeys`, `fitToolbarItems`, `flattenGroupedData`, `flattenNestedTree`, `flattenTreeData`, `foldText`, `foldTextWithMap`, `formatColor`, `groupNodeKey`, `groupRows`, `hsvaToRgba`, `intervalKey`, `intervalRange`, `matchByPrefix`, `mergeAcc`, `monthMatrix`, `nextDay`, `normalizeColor`, `normalizeSplitTracks`, `parseColor`, `pathKey`, `pathToFilterExpr`, `rangesOverlap`, `ratioToValue`, `relativeLuminance`, `resizeSplitAt`, `resolveDrawerMode`, `resolveFirstDayOfWeek`, `resolveKeySelector`, `resolveMenubarCompact`, `resolvePageCount`, `resolvePageRange`, `resolvePageWindow`, `resolveSelectedKeys`, `rgbaToHsva`, `runAsyncGuard`, `runLoadOptions`, `sameDay`, `sameMonth`, `serializeLikeOriginal`, `snapToStep`, `sortAxisChildren`, `splitSeparatorRange`, `splitTrackPx`, `startOfDay`, `startOfMonth`, `startOfWeek`, `stepEnabledIndex`, `toLocalDate`, `toggleTreeSelection`, `valueToRatio`, `weekNumber` - types: `ArrayDataSourceOptions`, `CheckState`, `CsvColumn`, `CsvOptions`, `CustomDataSourceOptions`, `CustomSummaryFn`, `CustomSummaryMap`, `DataChange`, `DataRowNode`, `DataSource`, `DataSourceCapabilities`, `DetailRowNode`, `FillerRowNode`, `FilterExpr`, `FilterOperator`, `FlattenConfig`, `FlattenTreeConfig`, `FoldedText`, `GridStateSnapshot`, `GroupDescriptor`, `GroupRowNode`, `GroupedItem`, `LoadOptions`, `LoadResult`, `LocalPivotStoreOptions`, `NestedTreeConfig`, `NestedTreeResult`, `ODataDataSourceOptions`, `ODataQueryOptions`, `OgeAsyncGuard`, `OgeColorFormat`, `OgeDrawerLayoutMode`, `OgeDrawerModeRequest`, `OgeDrawerModeResult`, `OgeGuardHandlers`, `OgeHsva`, `OgeMenubarCompactRequest`, `OgeMenubarCompactResult`, `OgeNavDirection`, `OgePageCountRequest`, `OgePageRangeRequest`, `OgePageRangeResult`, `OgePageWindowEntry`, `OgePageWindowRequest`, `OgePivotStore`, `OgeRangeThumb`, `OgeRgba`, `OgeSplitBounds`, `OgeSplitSeparatorRange`, `OgeSplitTrack`, `OgeToolbarFitItem`, `OgeToolbarFitOptions`, `OgeToolbarFitResult`, `OgeToolbarOverflowPolicy`, `OgeTypeAheadBuffer`, `PivotAcc`, `PivotArea`, `PivotAxisNode`, `PivotAxisPayloadNode`, `PivotComputeOptions`, `PivotComputeSettings`, `PivotCsvOptions`, `PivotDrillDownArgs`, `PivotFieldConfig`, `PivotFieldDescriptor`, `PivotFieldFns`, `PivotFieldStateEntry`, `PivotGridStateSnapshot`, `PivotGroupInterval`, `PivotLoadOptions`, `PivotLoadResult`, `PivotPath`, `PivotResult`, `PivotRunningTotal`, `PivotSlot`, `PivotSummaryDisplayMode`, `RowKey`, `RowNode`, `RunLoadOptionsConfig`, `SortDescriptor`, `SortDirection`, `SubscribableLike`, `SummaryDescriptor`, `SummaryRowNode`, `SummaryType`, `SummaryValue`, `TreeFilterMode`, `TreeIndex`, `TreeIndexConfig`, `TreeListStateSnapshot`, `ValueAccessor`, `ViewportWindow`, `WeekNumberRule` ## @oge-ui/react-buttons React buttons and button groups: severity/styling variants, async single-flight actions, click guarding, badges, hold-to-confirm and auto-repeat — running the same press machine and the same stylesheet as the Angular package. Docs: https://ogeui.com/components/buttons ### #### Properties _Appearance_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `text` | `string` | `''` | Label text; alternative (or addition) to children. | | `hint` | `string` | `—` | Tooltip — rendered as the native `title` attribute. | | `stylingMode` | `OgeButtonStylingMode` | `—` | Fill style; falls back to the enclosing group, then `contained`. | | `severity` | `OgeButtonSeverity` | `—` | Semantic color; falls back to the enclosing group, then `normal`. | | `size` | `OgeButtonSize` | `—` | Size preset; falls back to the enclosing group, then `md`. | | `color` | `string` | `—` | Custom main color (any CSS color); the soft tint is derived automatically. | | `icon` | `ReactNode` | `—` | Icon node rendered before or after the label. React slots take nodes, not directive markup. | | `iconPosition` | `OgeButtonIconPosition` | `'before'` | Where `icon` renders relative to the label. | | `badge` | `string \| number \| boolean` | `—` | Notification badge: a string/number renders a pill (numbers cap at `99+` and join the accessible name), `true` renders a plain dot. | | `className` | `string` | `—` | Extra class names appended to the host element. | _Behavior_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `disabled` | `boolean` | `false` | Disables the button. | | `value` | `string` | `—` | Selection key within an enclosing ``; also stamped as `data-oge-value` so the group resolves clicks and arrow-selection off the DOM. | | `loading` | `boolean` | `—` | Busy state. Omit it to let the component own the flag while an `action` is pending; pass it to control the flag yourself. | | `action` | `() => unknown` | `—` | Async click handler. Turns `loading` on while the returned promise is pending and ignores further clicks until it settles (single-flight). | | `clickGuard` | `boolean \| OgeClickGuardOptions` | `false` | Rate-limits `onClick`. `true` throttles with `config.clickGuardMs`. | | `holdToConfirm` | `boolean \| OgeHoldToConfirmOptions` | `false` | Fires `onClick` only after an uninterrupted press. Mutually exclusive with `autoRepeat`, which it wins. | | `autoRepeat` | `boolean \| OgeAutoRepeatOptions` | `false` | Repeats `onClick` while the button is held. Ignored when `holdToConfirm` is also set. | | `useSubmitBehavior` | `boolean` | `false` | Renders `type="submit"` so the button submits the enclosing form. | | `buttonType` | `'button' \| 'submit' \| 'reset'` | `'button'` | Native button type. | | `messages` | `Partial` | `—` | Per-instance overrides of user-facing strings. | _Accessibility_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `ariaLabel` | `string` | `—` | Accessible name of the native button — required for icon-only buttons. | | `ariaHasPopup` | `AriaAttributes['aria-haspopup']` | `—` | For popup triggers. | | `ariaExpanded` | `boolean` | `—` | Omitted from the DOM when undefined. | | `ariaControls` | `string` | `—` | Id of the controlled popup. | | `tabIndex` | `number` | `0` | Ignored inside a group, which owns the roving tabindex itself. | | `accessKey` | `string` | `—` | Native `accesskey` of the inner button. | #### Methods | Name | Type | Description | | --- | --- | --- | | `focus()` | `() => void` | Moves keyboard focus to the inner native button. Reached through a `ref` typed `OgeButtonHandle`. | #### Events | Name | Type | Description | | --- | --- | --- | | `onClick` | `(event: MouseEvent \| KeyboardEvent) => void` | Fires after the gesture/guard pipeline accepts a click. Use this rather than a native click handler — the native event bypasses `clickGuard`, `holdToConfirm`, `autoRepeat` and the single-flight protection. | | `onActionDone` | `(result: unknown) => void` | The `action` settled successfully. | | `onActionFailed` | `(error: unknown) => void` | The `action` threw or rejected. | | `onLoadingChange` | `(loading: boolean) => void` | The busy state changed — the controlled half of `loading`. | #### Types | Name | Type | Description | | --- | --- | --- | | `OgeButtonStylingMode` | `'contained' \| 'outlined' \| 'text'` | Shared with the Angular package via @oge-ui/behavior. | | `OgeButtonSeverity` | `'normal' \| 'accent' \| 'success' \| 'warning' \| 'danger'` | Shared with the Angular package via @oge-ui/behavior. | | `OgeButtonSize` | `'sm' \| 'md' \| 'lg'` | Shared with the Angular package via @oge-ui/behavior. | | `OgeButtonHandle` | `{ focus(): void }` | Imperative handle exposed through `ref`. | ### #### Properties | Name | Type | Default | Description | | --- | --- | --- | --- | | `items` | `readonly OgeButtonGroupItem[]` | `—` | Data-driven entries rendered after the projected children. | | `selectionMode` | `'none' \| 'single' \| 'multiple'` | `'none'` | Drives the container role: `toolbar`, `radiogroup` or `group`. | | `selectedKeys` | `readonly string[]` | `—` | Controlled selection. Pair with onSelectionChange. | | `defaultSelectedKeys` | `readonly string[]` | `—` | Uncontrolled initial selection — the component owns it from there. | | `stylingMode` | `OgeButtonStylingMode` | `'contained'` | Cascaded to children without their own. | | `severity` | `OgeButtonSeverity` | `'normal'` | Cascaded to children without their own. | | `size` | `OgeButtonSize` | `'md'` | Cascaded to children without their own. | | `disabled` | `boolean` | `false` | Disables every button in the group. | | `ariaLabel` | `string` | `—` | Accessible name of the toolbar/radiogroup/group element. | #### Methods | Name | Type | Description | | --- | --- | --- | | `focus()` | `() => void` | Moves keyboard focus to the button currently holding the roving tabindex. Reached through a `ref` typed `OgeButtonGroupHandle`. | #### Events | Name | Type | Description | | --- | --- | --- | | `onSelectionChange` | `(change: OgeButtonGroupSelectionChange) => void` | The selection changed through user interaction. The payload carries `selectedKeys`, `addedKeys` and `removedKeys`. | | `onItemClick` | `(event: OgeButtonGroupItemClickEvent) => void` | Every accepted child click, before any selection change. The payload carries `value`, the raw `event`, the matching `item` and the DOM-order `index`. | #### Types | Name | Type | Description | | --- | --- | --- | | `OgeButtonGroupItem` | `{ value: string; text?: string; hint?: string; disabled?: boolean; severity?: OgeButtonSeverity; badge?: string \| number \| boolean }` | A data-driven group entry. | | `OgeButtonGroupHandle` | `{ focus(): void }` | Imperative handle exposed through `ref`. | | `OgeButtonGroupSelectionChange` | `{ selectedKeys: readonly string[]; addedKeys: readonly string[]; removedKeys: readonly string[] }` | Computed by @oge-ui/behavior, so the delta rules match the Angular group exactly. | ### #### Properties _Trigger appearance_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `text` | `string` | `''` | Label of the (main) trigger button. rememberLastAction replaces it with the remembered item’s text. | | `hint` | `string` | `—` | Tooltip of the trigger (native `title`). | | `disabled` | `boolean` | `false` | Disables the trigger (and the split toggle). | | `stylingMode` | `OgeButtonStylingMode` | `—` | Fill style, forwarded to the buttons. | | `severity` | `OgeButtonSeverity` | `—` | Semantic color, forwarded to the buttons. | | `size` | `OgeButtonSize` | `—` | Size preset, forwarded to the buttons. | | `color` | `string` | `—` | Custom main color (any CSS color) — overrides the severity palette. | | `icon` | `ReactNode` | `—` | Leading icon of the (main) trigger — any inline SVG. | | `iconPosition` | `OgeButtonIconPosition` | `'before'` | Places the icon before or after the label. | | `badge` | `string \| number \| boolean` | `—` | Corner pill on the trigger; numbers cap at 99+, `true` renders a dot. | _Menu & panel_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `items` | `readonly OgeMenuItem[] \| OgeDropDownItemsFn` | `—` | Menu items — an array, or a function invoked lazily on first open (cached until its reference changes; loading/empty/error rows render while it settles). | | `dropdownPlacement` | `OgePopupPlacement` | `'bottom-start'` | Preferred panel placement; flips near viewport edges. | | `dropdownWidth` | `number \| 'anchor'` | `—` | Panel width: fixed pixels or `anchor` to match the button width. | | `renderItem` | `(item: OgeMenuItem, index: number) => ReactNode` | `—` | Custom rendering for menu items (icons, badges…) — the React counterpart of the Angular item template. | | `renderContent` | `(close: () => void) => ReactNode` | `—` | Replaces the menu entirely with arbitrary panel content — the counterpart of `*ogeDropDownContent`. `close()` shuts the panel and restores focus. | | `opened / defaultOpened` | `boolean` | `—` | Panel visibility — controlled with `opened` + `onOpenedChange`, or uncontrolled starting from `defaultOpened`. | | `messages` | `Partial` | `—` | Per-instance overrides of the loading/empty/error/toggle strings. | _Split mode_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `splitButton` | `boolean` | `false` | Renders a separate chevron toggle next to an action main button. | | `action` | `() => unknown` | `—` | Async click handler of the split main button (single-flight, automatic loading). | | `clickGuard` | `boolean \| OgeClickGuardOptions` | `false` | Click guard of the split main button. | | `rememberLastAction` | `boolean` | `false` | Split mode: the last clicked menu item becomes the main button’s label and action for the session (the IDE Run-button pattern). | | `loading / onLoadingChange` | `boolean / (loading: boolean) => void` | `—` | Busy state of the (main) button — controlled when `loading` is provided. | #### Methods _Imperative handle (via ref)_ | Name | Type | Description | | --- | --- | --- | | `focus()` | `() => void` | Moves keyboard focus to the trigger (split mode: the chevron toggle). | | `open() / close() / toggle()` | `() => void` | Programmatic panel control. | #### Events | Name | Type | Description | | --- | --- | --- | | `onItemClick` | `(event: OgeDropDownButtonItemClickEvent) => void` | A menu item was activated; the panel closes afterwards. In non-split mode this is the selection callback — the trigger click only toggles the panel. | | `onSelectionChange` | `(event: OgeDropDownSelectionChangedEvent) => void` | `rememberLastAction` mode: the remembered item changed. | | `onClick` | `(event: MouseEvent \| KeyboardEvent) => void` | Split mode only: the main action button was clicked. | | `onActionDone / onActionFailed` | `(value: unknown) => void` | The split main button’s `action` settled. | | `onOpenedChange` | `(opened: boolean) => void` | Panel visibility changed (open or close, any reason). | #### Types | Name | Type | Description | | --- | --- | --- | | `OgeMenuItem` | `interface` | The canonical menu item (text, value, checked, severity, icon, url, badge, shortcut, separator, action, nested items) — shared with the Angular overlay via `@oge-ui/behavior`. | | `OgeDropDownItemsFn` | `() => readonly OgeMenuItem[] \| Promise` | Lazy items source — see `items`. | | `OgeDropDownButtonItemClickEvent` | `{ item; index; event }` | Payload of `onItemClick`. | | `OgeDropDownSelectionChangedEvent` | `{ item; previousItem }` | Payload of `onSelectionChange`. | ### Demos Each demo below is a complete standalone component — copy one whole and it compiles. #### Severities & styling modes ```ts 'use client'; import { OgeButton } from '@oge-ui/react-buttons'; export function VariantsDemo() { return (
); } ``` #### Sizes ```ts 'use client'; import { OgeButton } from '@oge-ui/react-buttons'; export function SizesDemo() { return (
); } ``` #### Icons ```ts 'use client'; import { OgeButton } from '@oge-ui/react-buttons'; const downloadIcon = ( ); const nextIcon = ( ); export function IconsDemo() { return (
); } ``` #### Custom colors ```ts 'use client'; import { type CSSProperties } from 'react'; import { OgeButton } from '@oge-ui/react-buttons'; export function ColorsDemo() { return (
); } ``` #### Badges ```ts 'use client'; import { OgeButton } from '@oge-ui/react-buttons'; export function BadgesDemo() { return (
); } ``` #### Single selection (radio pattern) ```ts 'use client'; import { useState } from 'react'; import { OgeButton, OgeButtonGroup } from '@oge-ui/react-buttons'; export function SingleSelectionDemo() { const [align, setAlign] = useState(['left']); return (
setAlign(selectedKeys)} ariaLabel="Text alignment" > selected: {align.join(', ') || '—'}
); } ``` #### Multiple selection (toggle buttons) ```ts 'use client'; import { useState } from 'react'; import { OgeButton, OgeButtonGroup } from '@oge-ui/react-buttons'; export function MultipleSelectionDemo() { const [styles, setStyles] = useState(['bold']); return (
setStyles(selectedKeys)} stylingMode="outlined" ariaLabel="Text styles" > active: {styles.join(', ') || '—'}
); } ``` #### Data-driven items ```ts 'use client'; import { useState } from 'react'; import { OgeButtonGroup } from '@oge-ui/react-buttons'; import type { OgeButtonGroupItem } from '@oge-ui/react-buttons'; const periods: readonly OgeButtonGroupItem[] = [ { value: 'day', text: 'Day' }, { value: 'week', text: 'Week' }, { value: 'month', text: 'Month' }, { value: 'year', text: 'Year', disabled: true }, ]; export function DataDrivenDemo() { const [period, setPeriod] = useState(['week']); return (
setPeriod(selectedKeys)} size="sm" ariaLabel="Period" /> period: {period.join(', ') || '—'}
); } ``` #### Menu button ```ts 'use client'; import { OgeDropDownButton } from '@oge-ui/react-buttons'; import type { OgeMenuItem } from '@oge-ui/react-buttons'; const exportItems: readonly OgeMenuItem[] = [ { text: 'CSV', value: 'csv' }, { text: 'Excel', value: 'xlsx' }, { separator: true, text: '' }, { text: 'PDF', value: 'pdf', disabled: true }, ]; export function MenuButtonDemo() { return ( console.log(item.value)} /> ); } ``` #### Lazy items ```ts 'use client'; import { useCallback } from 'react'; import { OgeDropDownButton } from '@oge-ui/react-buttons'; export function LazyItemsDemo() { const loadTargets = useCallback(async () => { const response = await fetch('/api/run-targets'); return await response.json(); }, []); return ( ); } ``` #### Split button with a remembered action ```ts 'use client'; import { OgeDropDownButton } from '@oge-ui/react-buttons'; import type { OgeMenuItem } from '@oge-ui/react-buttons'; const runTargets: readonly OgeMenuItem[] = [ { text: 'Run tests', action: () => fetch('/api/run?suite=tests', { method: 'POST' }) }, { text: 'Run lint', action: () => fetch('/api/run?suite=lint', { method: 'POST' }) }, ]; export function SplitButtonDemo() { return ( ); } ``` #### Async actions & loading ```ts 'use client'; import { useState } from 'react'; import { OgeButton } from '@oge-ui/react-buttons'; const save = () => new Promise((resolve) => setTimeout(resolve, 1500)); const fail = () => new Promise((_, reject) => setTimeout(() => reject(new Error('nope')), 1000)); export function AsyncActionsDemo() { const [saved, setSaved] = useState(0); const [failed, setFailed] = useState(0); return (
setSaved(saved + 1)} /> setFailed(failed + 1)} /> saved ×{saved} · failed ×{failed}
); } ``` #### Click guard ```ts 'use client'; import { useState } from 'react'; import { OgeButton } from '@oge-ui/react-buttons'; export function ClickGuardDemo() { const [throttled, setThrottled] = useState(0); const [debounced, setDebounced] = useState(0); return (
setThrottled(throttled + 1)} /> setDebounced(debounced + 1)} /> throttled ×{throttled} · debounced ×{debounced}
); } ``` #### Hold to confirm ```ts 'use client'; import { useState } from 'react'; import { OgeButton } from '@oge-ui/react-buttons'; export function HoldToConfirmDemo() { const [deletions, setDeletions] = useState(0); return (
setDeletions(deletions + 1)} /> confirmed ×{deletions}
); } ``` #### Auto-repeat ```ts 'use client'; import { useState } from 'react'; import { OgeButton } from '@oge-ui/react-buttons'; export function AutoRepeatDemo() { const [value, setValue] = useState(0); return (
setValue((n) => n - 1)} /> {value} setValue((n) => n + 1)} />
); } ``` ## @oge-ui/react-inputs React form editors on the same field chrome: TextBox, TextArea, NumberBox, SelectBox, TagBox, Autocomplete (virtual scrolling, custom values), CheckBox, Switch, RadioGroup, Slider/RangeSlider, ColorBox, Calendar and DateBox/DateRangeBox — running the same commit pipeline, list/selection machines and stylesheet as the Angular package. Docs: https://ogeui.com/components/inputs ### #### Properties _OgeTextBox_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `value / defaultValue` | `string` | `''` | The editor value. Controlled with `value` + `onValueChange`, or uncontrolled starting from `defaultValue` — the React half of Angular’s `[(value)]`. | | `mode` | `OgeTextBoxMode` | `'text'` | Native input type. `password` auto-enables the reveal toggle. | | `maxLength` | `number` | `—` | Counter denominator; enforced natively while `counterMode` is `'limit'`. | | `minLength` | `number` | `—` | Native `minlength` attribute. | | `showCounter` | `boolean` | `false` | Renders the grapheme-accurate character counter in the subscript end slot. | | `counterMode` | `OgeInputCounterMode` | `'limit'` | Enforce `maxLength` natively, or allow typing past it and color the counter. | | `revealable` | `boolean` | `true` | Password reveal toggle; on by default for `mode="password"`. Preserves caret/selection when toggling. | | `showCopyButton` | `boolean` | `false` | Copy-to-clipboard rail button (API keys, tokens…); copies the live text. | | `autocomplete` | `string` | `—` | Native `autocomplete` attribute. | | `inputMode` | `string` | `—` | Native `inputmode` attribute. | | `enterKeyHint` | `string` | `—` | Native `enterkeyhint` attribute. | | `autocapitalize` | `string` | `—` | Native `autocapitalize` attribute. | | `spellcheck` | `boolean` | `—` | `undefined` omits the attribute (browser default). | _Common — field chrome (all field editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `label` | `string` | `''` | Field label; placement follows `labelMode`. | | `labelMode` | `OgeInputLabelMode` | `'static'` | Label placement: static / floating / hidden (aria-only) / outside. | | `stylingMode` | `OgeInputStylingMode` | `'outlined'` | Container fill style. | | `size` | `OgeInputSize` | `'md'` | Container height preset — 28/34/42px, the button scale. | | `placeholder` | `string` | `''` | Native placeholder text. | | `hint` | `string` | `—` | Helper text in the subscript region (hidden while an error shows). | | `tooltip` | `string` | `—` | Native `title` attribute of the input element. | | `subscriptSizing` | `OgeInputSubscriptSizing` | `'fixed'` | Whether the hint/error line reserves height, collapses, or is removed. | | `fluid` | `boolean` | `false` | Stretches the field to 100% width (default 240px via `--oge-input-width`). | | `showClearButton` | `boolean` | `false` | Renders the clear (✕) button while the field has a value. | | `id` | `string` | `—` | Base for the generated element ids (input/label/hint/error/counter). Omitted, a stable id comes from `useId()`. | | `tabIndex` | `number` | `0` | Tab order of the native input. | | `autofocus` | `boolean` | `false` | Focuses the editor after its first render. | | `messages` | `Partial` | `—` | Per-instance overrides of user-facing strings; merged over the `` values. | | `prefix` | `ReactNode` | `—` | Leading adornment inside the field — the React face of the `[ogeInputPrefix]` slot. React slots take nodes, not directive markup. | | `suffix` | `ReactNode` | `—` | Trailing adornment, rendered after the built-in rail buttons — the React face of `[ogeInputSuffix]`. | | `showSuccessIcon` | `OgeInputShowSuccessIcon` | `false` | Success icon when valid: `false` / on touch / always. | | `selectOnFocus` | `boolean` | `false` | Selects the whole text when the input receives focus. | | `inputAttr` | `Record` | `—` | Escape hatch: extra attributes rendered onto the native input (component-owned attributes are ignored). | | `className` | `string` | `—` | Extra class names appended to the host element. | | `style` | `CSSProperties` | `—` | Inline styles on the host element. | _Common — state & validation (all editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `disabled` | `boolean` | `false` | Disables the editor. | | `readonly` | `boolean` | `false` | Focusable but not editable. (Contract name — not `readOnly`.) | | `required` | `boolean` | `false` | Marks the field required (label asterisk + validation). | | `name` | `string` | `''` | Native `name` attribute. | | `invalid` | `boolean` | `false` | External invalid override — combined with the `errors` props. | | `pending` | `boolean` | `false` | Async-validation indicator; a spinner shows in the rail while `true`. | | `touched` | `boolean` | `false` | External touched override — hand it your form library’s touched flag. | | `dirty` | `boolean` | `false` | External dirty override. | | `errors` | `readonly OgeFieldError[]` | `[]` | Validation errors in the shared `OgeFieldError` shape — the bridge from React Hook Form, Formik or your own resolver. | | `errorText` | `string` | `—` | Explicit error message — always wins over resolved messages. | | `errorDisplay` | `OgeInputErrorDisplay` | `'touched'` | When resolved errors become visible. | | `debounce` | `number` | `—` | Commit delay in ms for `onValueChange`; blur and Enter flush immediately. | #### Methods _Common — imperative handle (via ref)_ | Name | Type | Description | | --- | --- | --- | | `focus()` | `() => void` | Moves keyboard focus to the native input. | | `blur()` | `() => void` | Blurs the native input. | | `clear()` | `() => void` | Clears the value (commits immediately), keeps focus in the field; no-op when disabled/readonly. | #### Events _Common (all field editors)_ | Name | Type | Description | | --- | --- | --- | | `onValueChange` | `(value: T) => void` | Every committed change — the controlled half of `value`. Pass `defaultValue` instead to let the editor own its state. | | `onValueCommitted` | `(event: { value: T; previousValue: T; event: Event \| undefined }) => void` | The same commits with `previousValue` and the originating DOM event (`undefined` for programmatic writes) — the rich payload for cross-field rules. | | `onCleared` | `() => void` | Value cleared via the clear button or the handle’s `clear()`. | | `onEnterKey` | `(event: KeyboardEvent) => void` | Enter pressed inside the editor (pending debounce is flushed first). | | `onFocus` | `(event: FocusEvent) => void` | The editor received focus. | | `onBlur` | `(event: FocusEvent) => void` | The editor lost focus. | | `onInputChange` | `(event: { text: string; event: Event }) => void` | Raw text on every keystroke, regardless of commit policy. | #### Types | Name | Type | Description | | --- | --- | --- | | `OgeTextBoxProps` | `interface` | Extends `OgeControlProps` with everything above. | | `OgeTextBoxHandle` | `{ focus(); blur(); clear() }` | Imperative handle exposed through `ref`. | ### #### Properties _OgeTextArea_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `value / defaultValue` | `string` | `''` | The editor value — controlled with `onValueChange`, or uncontrolled from `defaultValue`. | | `rows` | `number` | `3` | Visible rows when `autoResize` is off; the floor when it is on. | | `autoResize` | `boolean` | `false` | Grow/shrink with content between `minRows` and `maxRows`. | | `minRows` | `number` | `—` | Defaults to `rows`. | | `maxRows` | `number` | `—` | `undefined` = unbounded growth. | | `maxLength` | `number` | `—` | Counter denominator / native cap. | | `minLength` | `number` | `—` | Native `minlength` attribute. | | `showCounter` | `boolean` | `false` | Grapheme-accurate character counter. | | `counterMode` | `OgeInputCounterMode` | `'limit'` | Enforce `maxLength` natively, or soft-cap. | | `spellcheck` | `boolean` | `true` | Non-optional here, unlike the text box. | | `autocapitalize` | `string` | `—` | Native `autocapitalize` attribute. | _Common — field chrome (all field editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `label` | `string` | `''` | Field label; placement follows `labelMode`. | | `labelMode` | `OgeInputLabelMode` | `'static'` | Label placement: static / floating / hidden (aria-only) / outside. | | `stylingMode` | `OgeInputStylingMode` | `'outlined'` | Container fill style. | | `size` | `OgeInputSize` | `'md'` | Container height preset — 28/34/42px, the button scale. | | `placeholder` | `string` | `''` | Native placeholder text. | | `hint` | `string` | `—` | Helper text in the subscript region (hidden while an error shows). | | `tooltip` | `string` | `—` | Native `title` attribute of the input element. | | `subscriptSizing` | `OgeInputSubscriptSizing` | `'fixed'` | Whether the hint/error line reserves height, collapses, or is removed. | | `fluid` | `boolean` | `false` | Stretches the field to 100% width (default 240px via `--oge-input-width`). | | `showClearButton` | `boolean` | `false` | Renders the clear (✕) button while the field has a value. | | `id` | `string` | `—` | Base for the generated element ids (input/label/hint/error/counter). Omitted, a stable id comes from `useId()`. | | `tabIndex` | `number` | `0` | Tab order of the native input. | | `autofocus` | `boolean` | `false` | Focuses the editor after its first render. | | `messages` | `Partial` | `—` | Per-instance overrides of user-facing strings; merged over the `` values. | | `prefix` | `ReactNode` | `—` | Leading adornment inside the field — the React face of the `[ogeInputPrefix]` slot. React slots take nodes, not directive markup. | | `suffix` | `ReactNode` | `—` | Trailing adornment, rendered after the built-in rail buttons — the React face of `[ogeInputSuffix]`. | | `showSuccessIcon` | `OgeInputShowSuccessIcon` | `false` | Success icon when valid: `false` / on touch / always. | | `selectOnFocus` | `boolean` | `false` | Selects the whole text when the input receives focus. | | `inputAttr` | `Record` | `—` | Escape hatch: extra attributes rendered onto the native input (component-owned attributes are ignored). | | `className` | `string` | `—` | Extra class names appended to the host element. | | `style` | `CSSProperties` | `—` | Inline styles on the host element. | _Common — state & validation (all editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `disabled` | `boolean` | `false` | Disables the editor. | | `readonly` | `boolean` | `false` | Focusable but not editable. (Contract name — not `readOnly`.) | | `required` | `boolean` | `false` | Marks the field required (label asterisk + validation). | | `name` | `string` | `''` | Native `name` attribute. | | `invalid` | `boolean` | `false` | External invalid override — combined with the `errors` props. | | `pending` | `boolean` | `false` | Async-validation indicator; a spinner shows in the rail while `true`. | | `touched` | `boolean` | `false` | External touched override — hand it your form library’s touched flag. | | `dirty` | `boolean` | `false` | External dirty override. | | `errors` | `readonly OgeFieldError[]` | `[]` | Validation errors in the shared `OgeFieldError` shape — the bridge from React Hook Form, Formik or your own resolver. | | `errorText` | `string` | `—` | Explicit error message — always wins over resolved messages. | | `errorDisplay` | `OgeInputErrorDisplay` | `'touched'` | When resolved errors become visible. | | `debounce` | `number` | `—` | Commit delay in ms for `onValueChange`; blur and Enter flush immediately. | #### Methods _Common — imperative handle (via ref)_ | Name | Type | Description | | --- | --- | --- | | `focus()` | `() => void` | Moves keyboard focus to the native input. | | `blur()` | `() => void` | Blurs the native input. | | `clear()` | `() => void` | Clears the value (commits immediately), keeps focus in the field; no-op when disabled/readonly. | #### Events _Common (all field editors)_ | Name | Type | Description | | --- | --- | --- | | `onValueChange` | `(value: T) => void` | Every committed change — the controlled half of `value`. Pass `defaultValue` instead to let the editor own its state. | | `onValueCommitted` | `(event: { value: T; previousValue: T; event: Event \| undefined }) => void` | The same commits with `previousValue` and the originating DOM event (`undefined` for programmatic writes) — the rich payload for cross-field rules. | | `onCleared` | `() => void` | Value cleared via the clear button or the handle’s `clear()`. | | `onEnterKey` | `(event: KeyboardEvent) => void` | Enter pressed inside the editor (pending debounce is flushed first). | | `onFocus` | `(event: FocusEvent) => void` | The editor received focus. | | `onBlur` | `(event: FocusEvent) => void` | The editor lost focus. | | `onInputChange` | `(event: { text: string; event: Event }) => void` | Raw text on every keystroke, regardless of commit policy. | #### Types | Name | Type | Description | | --- | --- | --- | | `OgeTextAreaHandle` | `{ focus(); blur(); clear() }` | Imperative handle exposed through `ref`. | | `measureTextAreaHeight(el, minRows, maxRows?)` | `number` | Fallback auto-resize measurement for browsers without CSS `field-sizing: content` — the same helper the Angular package exports. | ### #### Properties _OgeNumberBox_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `value / defaultValue` | `number \| null` | `null` | `null` is the empty state — never `0`. Controlled with `onValueChange`, or uncontrolled from `defaultValue`. | | `min` | `number` | `—` | Lower bound — values clamp on commit (typing is never blocked). | | `max` | `number` | `—` | Upper bound — clamped on commit. | | `step` | `number` | `1` | Spin/arrow-key increment. Spinning commits immediately. | | `showSpinButtons` | `boolean` | `false` | Up/down spin buttons with hold-to-repeat. | | `format` | `Intl.NumberFormatOptions` | `—` | Display formatting applied while unfocused; focus shows the raw number. `style: 'percent'` formats display only — the value is not rescaled. | | `locale` | `string` | `—` | Overrides the runtime locale (React has no `LOCALE_ID`; the config provider carries the default). | | `mode` | `OgeNumberBoxMode` | `'text'` | Native `type` attribute; `inputmode` is always `decimal`. | _Common — field chrome (all field editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `label` | `string` | `''` | Field label; placement follows `labelMode`. | | `labelMode` | `OgeInputLabelMode` | `'static'` | Label placement: static / floating / hidden (aria-only) / outside. | | `stylingMode` | `OgeInputStylingMode` | `'outlined'` | Container fill style. | | `size` | `OgeInputSize` | `'md'` | Container height preset — 28/34/42px, the button scale. | | `placeholder` | `string` | `''` | Native placeholder text. | | `hint` | `string` | `—` | Helper text in the subscript region (hidden while an error shows). | | `tooltip` | `string` | `—` | Native `title` attribute of the input element. | | `subscriptSizing` | `OgeInputSubscriptSizing` | `'fixed'` | Whether the hint/error line reserves height, collapses, or is removed. | | `fluid` | `boolean` | `false` | Stretches the field to 100% width (default 240px via `--oge-input-width`). | | `showClearButton` | `boolean` | `false` | Renders the clear (✕) button while the field has a value. | | `id` | `string` | `—` | Base for the generated element ids (input/label/hint/error/counter). Omitted, a stable id comes from `useId()`. | | `tabIndex` | `number` | `0` | Tab order of the native input. | | `autofocus` | `boolean` | `false` | Focuses the editor after its first render. | | `messages` | `Partial` | `—` | Per-instance overrides of user-facing strings; merged over the `` values. | | `prefix` | `ReactNode` | `—` | Leading adornment inside the field — the React face of the `[ogeInputPrefix]` slot. React slots take nodes, not directive markup. | | `suffix` | `ReactNode` | `—` | Trailing adornment, rendered after the built-in rail buttons — the React face of `[ogeInputSuffix]`. | | `showSuccessIcon` | `OgeInputShowSuccessIcon` | `false` | Success icon when valid: `false` / on touch / always. | | `selectOnFocus` | `boolean` | `false` | Selects the whole text when the input receives focus. | | `inputAttr` | `Record` | `—` | Escape hatch: extra attributes rendered onto the native input (component-owned attributes are ignored). | | `className` | `string` | `—` | Extra class names appended to the host element. | | `style` | `CSSProperties` | `—` | Inline styles on the host element. | _Common — state & validation (all editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `disabled` | `boolean` | `false` | Disables the editor. | | `readonly` | `boolean` | `false` | Focusable but not editable. (Contract name — not `readOnly`.) | | `required` | `boolean` | `false` | Marks the field required (label asterisk + validation). | | `name` | `string` | `''` | Native `name` attribute. | | `invalid` | `boolean` | `false` | External invalid override — combined with the `errors` props. | | `pending` | `boolean` | `false` | Async-validation indicator; a spinner shows in the rail while `true`. | | `touched` | `boolean` | `false` | External touched override — hand it your form library’s touched flag. | | `dirty` | `boolean` | `false` | External dirty override. | | `errors` | `readonly OgeFieldError[]` | `[]` | Validation errors in the shared `OgeFieldError` shape — the bridge from React Hook Form, Formik or your own resolver. | | `errorText` | `string` | `—` | Explicit error message — always wins over resolved messages. | | `errorDisplay` | `OgeInputErrorDisplay` | `'touched'` | When resolved errors become visible. | | `debounce` | `number` | `—` | Commit delay in ms for `onValueChange`; blur and Enter flush immediately. | #### Methods _Common — imperative handle (via ref)_ | Name | Type | Description | | --- | --- | --- | | `focus()` | `() => void` | Moves keyboard focus to the native input. | | `blur()` | `() => void` | Blurs the native input. | | `clear()` | `() => void` | Clears the value (commits immediately), keeps focus in the field; no-op when disabled/readonly. | #### Events _Common (all field editors)_ | Name | Type | Description | | --- | --- | --- | | `onValueChange` | `(value: T) => void` | Every committed change — the controlled half of `value`. Pass `defaultValue` instead to let the editor own its state. | | `onValueCommitted` | `(event: { value: T; previousValue: T; event: Event \| undefined }) => void` | The same commits with `previousValue` and the originating DOM event (`undefined` for programmatic writes) — the rich payload for cross-field rules. | | `onCleared` | `() => void` | Value cleared via the clear button or the handle’s `clear()`. | | `onEnterKey` | `(event: KeyboardEvent) => void` | Enter pressed inside the editor (pending debounce is flushed first). | | `onFocus` | `(event: FocusEvent) => void` | The editor received focus. | | `onBlur` | `(event: FocusEvent) => void` | The editor lost focus. | | `onInputChange` | `(event: { text: string; event: Event }) => void` | Raw text on every keystroke, regardless of commit policy. | #### Types | Name | Type | Description | | --- | --- | --- | | `OgeNumberBoxHandle` | `{ focus(); blur(); clear() }` | Imperative handle exposed through `ref`. | ### #### Properties _OgeSelectBox_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `value / defaultValue` | `unknown` | `null` | Committed value (the `valueExpr` of the selected item) — controlled with `onValueChange`, or uncontrolled from `defaultValue`. | | `items` | `readonly TItem[] \| OgeSelectItemsFn` | `[]` | The selectable items: an array, or a function invoked lazily on first open (sync or promise; loading/error rows render while pending). The selected item is resolved from this full set, never the filtered one. | | `displayExpr` | `string \| ((item) => string)` | `—` | Item → display text. Omitted, the item itself is stringified. | | `valueExpr` | `string \| ((item) => unknown)` | `—` | Item → committed value. Omitted, the whole item is the value. | | `disabledExpr` | `string \| ((item) => boolean)` | `—` | Marks individual items as non-selectable. | | `searchEnabled` | `boolean` | `false` | Enables typing into the field to filter the list. | | `searchMode` | `'contains' \| 'startswith'` | `'contains'` | How typed search text matches an item. | | `searchExpr` | `string \| string[] \| ((item) => string)` | `—` | Which text the filter matches; defaults to the display text. | | `minSearchLength` | `number` | `0` | Characters required before the filter narrows the list. | | `showDataBeforeSearch` | `boolean` | `false` | Below `minSearchLength`: show the full list (`true`) or nothing (`false`). | | `searchTimeout` | `number` | `—` | Debounce before typed text filters the list; `undefined` = config default (250ms). The displayed text is never debounced. | | `acceptCustomValue` | `boolean` | `false` | Lets typed text that matches no item become the value (committed on Enter/blur) — see `onCustomItemCreating`. | | `groupBy` | `string \| ((item) => string)` | `—` | Groups flat items under headers; items are re-ordered by first-seen group. | | `imageExpr` | `string \| ((item) => string)` | `—` | Item → image URL rendered before the option text (avatars, flags…). For inline SVG icons use `renderItem`. | | `showDropDownButton` | `boolean` | `true` | Renders the chevron toggle in the field rail. | | `openOnFieldClick` | `boolean` | `true` | Clicking the field opens the popup (select-only mode toggles it). | | `loading` | `boolean` | `false` | Shows a loading row instead of items — server-side filtering escape hatch. | | `dropdownPlacement` | `OgePopupPlacement` | `'bottom-start'` | Preferred popup side/alignment (flips when cramped). | | `dropdownWidth` | `number \| 'anchor'` | `'anchor'` | Popup width: fixed pixels or `'anchor'` to match the field box. | | `dropdownMaxHeight` | `number` | `—` | Scrollable list height cap; `undefined` = the CSS default (320px). | | `wrapItemText` | `boolean` | `false` | Wraps long option text instead of ellipsizing it. | | `useItemTextAsTitle` | `boolean` | `false` | Mirrors each option's display text into its `title` attribute. | | `renderItem` | `(item: TItem, context: { index; selected; active }) => ReactNode` | `—` | Custom option row rendering — the render prop replacing Angular’s `itemTemplate`; the context carries the same fields the template context does. | | `virtualScroll` | `boolean \| OgeVirtualScrollOptions` | `false` | Windowed rendering for large lists (`{ itemHeight, overscan }`). Rows get a fixed size-matched height; `groupBy` and `wrapItemText` are ignored while active. | | `opened / defaultOpened` | `boolean` | `false` | Popup visibility — controlled with `opened` + `onOpenedChange`, or uncontrolled from `defaultOpened`. | _Common — field chrome (all field editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `label` | `string` | `''` | Field label; placement follows `labelMode`. | | `labelMode` | `OgeInputLabelMode` | `'static'` | Label placement: static / floating / hidden (aria-only) / outside. | | `stylingMode` | `OgeInputStylingMode` | `'outlined'` | Container fill style. | | `size` | `OgeInputSize` | `'md'` | Container height preset — 28/34/42px, the button scale. | | `placeholder` | `string` | `''` | Native placeholder text. | | `hint` | `string` | `—` | Helper text in the subscript region (hidden while an error shows). | | `tooltip` | `string` | `—` | Native `title` attribute of the input element. | | `subscriptSizing` | `OgeInputSubscriptSizing` | `'fixed'` | Whether the hint/error line reserves height, collapses, or is removed. | | `fluid` | `boolean` | `false` | Stretches the field to 100% width (default 240px via `--oge-input-width`). | | `showClearButton` | `boolean` | `false` | Renders the clear (✕) button while the field has a value. | | `id` | `string` | `—` | Base for the generated element ids (input/label/hint/error/counter). Omitted, a stable id comes from `useId()`. | | `tabIndex` | `number` | `0` | Tab order of the native input. | | `autofocus` | `boolean` | `false` | Focuses the editor after its first render. | | `messages` | `Partial` | `—` | Per-instance overrides of user-facing strings; merged over the `` values. | | `prefix` | `ReactNode` | `—` | Leading adornment inside the field — the React face of the `[ogeInputPrefix]` slot. React slots take nodes, not directive markup. | | `suffix` | `ReactNode` | `—` | Trailing adornment, rendered after the built-in rail buttons — the React face of `[ogeInputSuffix]`. | | `showSuccessIcon` | `OgeInputShowSuccessIcon` | `false` | Success icon when valid: `false` / on touch / always. | | `selectOnFocus` | `boolean` | `false` | Selects the whole text when the input receives focus. | | `inputAttr` | `Record` | `—` | Escape hatch: extra attributes rendered onto the native input (component-owned attributes are ignored). | | `className` | `string` | `—` | Extra class names appended to the host element. | | `style` | `CSSProperties` | `—` | Inline styles on the host element. | _Common — state & validation (all editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `disabled` | `boolean` | `false` | Disables the editor. | | `readonly` | `boolean` | `false` | Focusable but not editable. (Contract name — not `readOnly`.) | | `required` | `boolean` | `false` | Marks the field required (label asterisk + validation). | | `name` | `string` | `''` | Native `name` attribute. | | `invalid` | `boolean` | `false` | External invalid override — combined with the `errors` props. | | `pending` | `boolean` | `false` | Async-validation indicator; a spinner shows in the rail while `true`. | | `touched` | `boolean` | `false` | External touched override — hand it your form library’s touched flag. | | `dirty` | `boolean` | `false` | External dirty override. | | `errors` | `readonly OgeFieldError[]` | `[]` | Validation errors in the shared `OgeFieldError` shape — the bridge from React Hook Form, Formik or your own resolver. | | `errorText` | `string` | `—` | Explicit error message — always wins over resolved messages. | | `errorDisplay` | `OgeInputErrorDisplay` | `'touched'` | When resolved errors become visible. | | `debounce` | `number` | `—` | Commit delay in ms for `onValueChange`; blur and Enter flush immediately. | #### Methods _OgeSelectBox handle (via ref)_ | Name | Type | Description | | --- | --- | --- | | `open()` | `() => void` | Opens the popup (no-op while disabled/readonly). | | `close()` | `() => void` | Closes the popup. | | `toggle()` | `() => void` | Toggles the popup. | _Common — imperative handle (via ref)_ | Name | Type | Description | | --- | --- | --- | | `focus()` | `() => void` | Moves keyboard focus to the native input. | | `blur()` | `() => void` | Blurs the native input. | | `clear()` | `() => void` | Clears the value (commits immediately), keeps focus in the field; no-op when disabled/readonly. | #### Events _OgeSelectBox callbacks_ | Name | Type | Description | | --- | --- | --- | | `onSelectionChange` | `(event: OgeSelectBoxSelectionChangedEvent) => void` | The resolved selected item changed (user or programmatic) — `{ item, previousItem }`. | | `onItemClick` | `(event: OgeSelectBoxItemClickEvent) => void` | An option row was activated — `{ item, index, event }`; `index` is within the visible (filtered) list. | | `onDropDownOpened / onDropDownClosed` | `() => void` | Popup visibility changes, from any trigger. | | `onOpenedChange` | `(opened: boolean) => void` | The controlled half of `opened` — fires for every open and close. | | `onSearchChange` | `(event: { text: string }) => void` | Raw search text on every keystroke — drive server-side filtering from here. | | `onCustomItemCreating` | `(payload: OgeSelectBoxCustomItemEvent) => void` | Mutable payload (as in the references): assign `customItem` — an item, a promise of one, or `null` to reject the text. Left unset, the raw text becomes the item. | _Common (all field editors)_ | Name | Type | Description | | --- | --- | --- | | `onValueChange` | `(value: T) => void` | Every committed change — the controlled half of `value`. Pass `defaultValue` instead to let the editor own its state. | | `onValueCommitted` | `(event: { value: T; previousValue: T; event: Event \| undefined }) => void` | The same commits with `previousValue` and the originating DOM event (`undefined` for programmatic writes) — the rich payload for cross-field rules. | | `onCleared` | `() => void` | Value cleared via the clear button or the handle’s `clear()`. | | `onEnterKey` | `(event: KeyboardEvent) => void` | Enter pressed inside the editor (pending debounce is flushed first). | | `onFocus` | `(event: FocusEvent) => void` | The editor received focus. | | `onBlur` | `(event: FocusEvent) => void` | The editor lost focus. | | `onInputChange` | `(event: { text: string; event: Event }) => void` | Raw text on every keystroke, regardless of commit policy. | #### Types _Select box types_ | Name | Type | Description | | --- | --- | --- | | `OgeSelectBoxHandle` | `{ focus(); blur(); clear(); open(); close(); toggle() }` | Imperative handle exposed through `ref`. | | `OgeSelectBoxSelectionChangedEvent` | `{ item: TItem \| null; previousItem: TItem \| null }` | Payload of `onSelectionChange`. | | `OgeSelectBoxItemClickEvent` | `{ item; index; event }` | Payload of `onItemClick`. | | `OgeSelectBoxCustomItemEvent` | `{ text: string; customItem?: TItem \| null \| PromiseLike }` | The mutable payload of `onCustomItemCreating`. | ### #### Properties _Value & data_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `value / defaultValue` | `RowKey \| readonly RowKey[] \| null` | `null` | Committed value — the selected node's key in `single` mode, an array of keys in `multiple`. Controlled with `onValueChange`, or uncontrolled from `defaultValue`. | | `items` | `readonly TItem[] \| undefined` | `—` | Nodes to display — a flat parent-referencing list or nested children. | | `keyExpr / parentIdExpr / itemsExpr` | `string \| ((row: TItem) => …)` | `—` | Identity and structure accessors, forwarded to the popup tree. `itemsExpr` switches to hierarchical data. | | `displayExpr` | `string \| ((row: TItem) => unknown)` | `'text'` | Node label, used both in the tree and for the text shown in the closed field. | | `disabledExpr / hasItemsExpr / iconExpr / rootValue / dataStructure` | `see OgeTreeView` | `—` | Forwarded verbatim to the popup tree. | _Selection_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `selectionMode` | `'single' \| 'multiple'` | `'single'` | `multiple` makes `value` an array and keeps the popup open while picking. | | `showCheckBoxes` | `'none' \| 'normal' \| 'selectAll'` | `'none'` | Checkbox column inside the popup. | | `selectNodesRecursive` | `boolean` | `true` | Cascades selection down to descendants and up to fully-selected parents. | | `selectedKeysMode` | `'all' \| 'leavesOnly' \| 'excludeRecursive'` | `'all'` | Projection applied to the committed keys — `leavesOnly` is usually what you want to store from a cascade. | | `displayMode` | `'text' \| 'count'` | `'text'` | Closed-field rendering for a multiple selection: the joined labels, or just how many are picked. | _Popup_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `opened / defaultOpened` | `boolean` | `false` | Popup visibility — controlled with `opened` + `onOpenedChange`, or uncontrolled from `defaultOpened`. | | `expandedKeys / defaultExpandedKeys` | `readonly RowKey[]` | `[]` | Expanded nodes — controlled with `onExpandedKeysChange`, or uncontrolled from `defaultExpandedKeys`, so the shape survives close and reopen. | | `expandEvent` | `'click' \| 'dblclick'` | `'dblclick'` | Which gesture expands inside the popup. Unlike the bare tree this defaults to `dblclick` — in a picker a single click should choose, and the chevron expands either way. | | `searchEnabled / searchMode / filterMode` | `see OgeTreeView` | `—` | Puts the tree's own search box inside the popup. | | `loadChildren` | `(parent: TItem, key: RowKey) => Promise` | `—` | Lazy children, fetched on first expand. | | `virtualScroll` | `boolean \| { itemHeight: number }` | `false` | Windowed rendering inside the popup for very large trees. | | `dropdownPlacement / dropdownWidth / dropdownMaxHeight` | `OgePopupPlacement \| number \| 'anchor'` | `—` | Popup geometry. Width defaults to `'anchor'` (matches the field), max height to 320px. | | `openOnFieldClick` | `boolean` | `true` | Opens on a click anywhere in the field, not only on the chevron. | #### Methods _OgeTreeSelectHandle (via ref)_ | Name | Type | Description | | --- | --- | --- | | `open() / close() / toggle()` | `() => void` | Imperative popup control (no-op while disabled/readonly). | | `focus() / blur() / clear()` | `() => void` | Field-chrome control methods. `clear()` commits the empty value and keeps focus in the field. | #### Events _OgeTreeSelect callbacks_ | Name | Type | Description | | --- | --- | --- | | `onSelectionChanged` | `(event: OgeTreeSelectSelectionChangedEvent) => void` | Fires after the committed selection changed, with `keys` and `previousKeys` (always arrays, even in single mode). | | `onDropDownOpened / onDropDownClosed` | `() => void` | Popup lifecycle, from any trigger. | | `onValueChange` | `(value: unknown) => void` | Every committed change — the controlled half of `value`. Pass `defaultValue` instead to let the editor own its state. | | `onValueCommitted` | `(event: { value; previousValue; event }) => void` | The same commits with `previousValue` and the originating DOM event (`undefined` for programmatic writes). | | `onOpenedChange` | `(opened: boolean) => void` | The controlled half of `opened` — fires for every open and close. | | `onExpandedKeysChange` | `(keys: readonly RowKey[]) => void` | The controlled half of `expandedKeys`. | #### Types _Tree select types_ | Name | Type | Description | | --- | --- | --- | | `OgeTreeSelectHandle` | `{ focus(); blur(); clear(); open(); close(); toggle() }` | Imperative handle exposed through `ref`. | | `OgeTreeSelectSelectionMode` | `'single' \| 'multiple'` | How many nodes may be committed. | | `OgeTreeSelectDisplayMode` | `'text' \| 'count'` | Closed-field rendering of a multiple selection. | | `OgeTreeSelectSelectionChangedEvent` | `{ keys: readonly RowKey[]; previousKeys: readonly RowKey[] }` | Payload of `onSelectionChanged`. | ### #### Properties _OgeTagBox_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `value / defaultValue` | `readonly unknown[]` | `[]` | Committed values — the `valueExpr` of every selected item; controlled with `onValueChange`, or uncontrolled from `defaultValue`. | | `items / displayExpr / valueExpr / disabledExpr / imageExpr` | `shared with OgeSelectBox` | `—` | The tag box reuses the select box expression vocabulary verbatim. | | `searchEnabled / searchMode / searchExpr` | `shared with OgeSelectBox` | `—` | Client-side filtering of the option list. | | `showSelectionControls` | `boolean` | `true` | Renders checkboxes in front of the options. | | `hideSelectedItems` | `boolean` | `false` | Hides already-selected items from the popup list. | | `maxDisplayedTags` | `number` | `—` | Caps the rendered chips; the rest collapse into a `+N` chip. | | `opened / defaultOpened / dropdownPlacement / dropdownWidth / dropdownMaxHeight / showDropDownButton / openOnFieldClick` | `shared with OgeSelectBox` | `—` | Popup configuration and the controlled/uncontrolled visibility pair. | | `virtualScroll` | `boolean \| OgeVirtualScrollOptions` | `false` | Windowed rendering for large lists — same contract as the select box. | _Common — field chrome (all field editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `label` | `string` | `''` | Field label; placement follows `labelMode`. | | `labelMode` | `OgeInputLabelMode` | `'static'` | Label placement: static / floating / hidden (aria-only) / outside. | | `stylingMode` | `OgeInputStylingMode` | `'outlined'` | Container fill style. | | `size` | `OgeInputSize` | `'md'` | Container height preset — 28/34/42px, the button scale. | | `placeholder` | `string` | `''` | Native placeholder text. | | `hint` | `string` | `—` | Helper text in the subscript region (hidden while an error shows). | | `tooltip` | `string` | `—` | Native `title` attribute of the input element. | | `subscriptSizing` | `OgeInputSubscriptSizing` | `'fixed'` | Whether the hint/error line reserves height, collapses, or is removed. | | `fluid` | `boolean` | `false` | Stretches the field to 100% width (default 240px via `--oge-input-width`). | | `showClearButton` | `boolean` | `false` | Renders the clear (✕) button while the field has a value. | | `id` | `string` | `—` | Base for the generated element ids (input/label/hint/error/counter). Omitted, a stable id comes from `useId()`. | | `tabIndex` | `number` | `0` | Tab order of the native input. | | `autofocus` | `boolean` | `false` | Focuses the editor after its first render. | | `messages` | `Partial` | `—` | Per-instance overrides of user-facing strings; merged over the `` values. | | `prefix` | `ReactNode` | `—` | Leading adornment inside the field — the React face of the `[ogeInputPrefix]` slot. React slots take nodes, not directive markup. | | `suffix` | `ReactNode` | `—` | Trailing adornment, rendered after the built-in rail buttons — the React face of `[ogeInputSuffix]`. | | `showSuccessIcon` | `OgeInputShowSuccessIcon` | `false` | Success icon when valid: `false` / on touch / always. | | `selectOnFocus` | `boolean` | `false` | Selects the whole text when the input receives focus. | | `inputAttr` | `Record` | `—` | Escape hatch: extra attributes rendered onto the native input (component-owned attributes are ignored). | | `className` | `string` | `—` | Extra class names appended to the host element. | | `style` | `CSSProperties` | `—` | Inline styles on the host element. | _Common — state & validation (all editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `disabled` | `boolean` | `false` | Disables the editor. | | `readonly` | `boolean` | `false` | Focusable but not editable. (Contract name — not `readOnly`.) | | `required` | `boolean` | `false` | Marks the field required (label asterisk + validation). | | `name` | `string` | `''` | Native `name` attribute. | | `invalid` | `boolean` | `false` | External invalid override — combined with the `errors` props. | | `pending` | `boolean` | `false` | Async-validation indicator; a spinner shows in the rail while `true`. | | `touched` | `boolean` | `false` | External touched override — hand it your form library’s touched flag. | | `dirty` | `boolean` | `false` | External dirty override. | | `errors` | `readonly OgeFieldError[]` | `[]` | Validation errors in the shared `OgeFieldError` shape — the bridge from React Hook Form, Formik or your own resolver. | | `errorText` | `string` | `—` | Explicit error message — always wins over resolved messages. | | `errorDisplay` | `OgeInputErrorDisplay` | `'touched'` | When resolved errors become visible. | | `debounce` | `number` | `—` | Commit delay in ms for `onValueChange`; blur and Enter flush immediately. | #### Methods _OgeTagBox handle (via ref)_ | Name | Type | Description | | --- | --- | --- | | `open() / close() / toggle()` | `() => void` | Popup control (no-ops while disabled/readonly). | _Common — imperative handle (via ref)_ | Name | Type | Description | | --- | --- | --- | | `focus()` | `() => void` | Moves keyboard focus to the native input. | | `blur()` | `() => void` | Blurs the native input. | | `clear()` | `() => void` | Clears the value (commits immediately), keeps focus in the field; no-op when disabled/readonly. | #### Events _OgeTagBox callbacks_ | Name | Type | Description | | --- | --- | --- | | `onSelectionChange` | `(event: OgeTagBoxSelectionChangedEvent) => void` | Per-commit delta — `{ addedItems, removedItems }`. | | `onItemClick` | `(event: OgeTagBoxItemClickEvent) => void` | An option row was toggled — `{ item, index, event }`. | | `onDropDownOpened / onDropDownClosed` | `() => void` | Popup visibility changes, from any trigger. | | `onOpenedChange` | `(opened: boolean) => void` | The controlled half of `opened`. | _Common (all field editors)_ | Name | Type | Description | | --- | --- | --- | | `onValueChange` | `(value: T) => void` | Every committed change — the controlled half of `value`. Pass `defaultValue` instead to let the editor own its state. | | `onValueCommitted` | `(event: { value: T; previousValue: T; event: Event \| undefined }) => void` | The same commits with `previousValue` and the originating DOM event (`undefined` for programmatic writes) — the rich payload for cross-field rules. | | `onCleared` | `() => void` | Value cleared via the clear button or the handle’s `clear()`. | | `onEnterKey` | `(event: KeyboardEvent) => void` | Enter pressed inside the editor (pending debounce is flushed first). | | `onFocus` | `(event: FocusEvent) => void` | The editor received focus. | | `onBlur` | `(event: FocusEvent) => void` | The editor lost focus. | | `onInputChange` | `(event: { text: string; event: Event }) => void` | Raw text on every keystroke, regardless of commit policy. | #### Types | Name | Type | Description | | --- | --- | --- | | `OgeTagBoxHandle` | `{ focus(); blur(); clear(); open(); close(); toggle() }` | Imperative handle exposed through `ref`. | | `OgeTagBoxSelectionChangedEvent` | `{ addedItems; removedItems }` | Payload of `onSelectionChange`. | ### #### Properties _OgeAutocomplete_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `value / defaultValue` | `string` | `''` | The typed text — the committed value is the string itself, not an item value; controlled with `onValueChange`, or uncontrolled from `defaultValue`. | | `items` | `readonly TItem[] \| OgeSelectItemsFn` | `[]` | The suggestion items: an array, or a function invoked lazily on first open (sync or promise; loading/error rows render while pending). | | `displayExpr / disabledExpr / imageExpr / searchExpr / searchMode / groupBy / renderItem` | `shared with OgeSelectBox` | `—` | The autocomplete reuses the select box expression vocabulary and list rendering verbatim (no `valueExpr` — the value is text). | | `minSearchLength` | `number` | `1` | Characters required before suggestions open while typing; deleting below the threshold closes the list. | | `maxItemCount` | `number` | `10` | Caps the rendered suggestion list. | | `searchTimeout` | `number` | `—` | Debounce before typed text filters the list; `undefined` = config default (250ms). The displayed text is never debounced. | | `forceSelection` | `boolean` | `false` | Reverts non-matching text to the last committed value on blur; an exact display match resolves to the item with its canonical casing. | | `searchHighlight` | `boolean` | `true` | Marks the matched part of each suggestion (``). | | `showDropDownButton` | `boolean` | `false` | Renders the chevron toggle in the field rail (off by default — reference parity). | | `openOnFieldClick` | `boolean` | `false` | Clicking the field opens the suggestion list. | | `loading / dropdownPlacement / dropdownWidth / dropdownMaxHeight / wrapItemText / useItemTextAsTitle` | `shared with OgeSelectBox` | `—` | Popup configuration and list rendering. | | `virtualScroll` | `boolean \| OgeVirtualScrollOptions` | `false` | Windowed rendering for large lists — same contract as the select box. | | `opened / defaultOpened` | `boolean` | `false` | Popup visibility — controlled with `onOpenedChange`, or uncontrolled from `defaultOpened`. | _Common — field chrome (all field editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `label` | `string` | `''` | Field label; placement follows `labelMode`. | | `labelMode` | `OgeInputLabelMode` | `'static'` | Label placement: static / floating / hidden (aria-only) / outside. | | `stylingMode` | `OgeInputStylingMode` | `'outlined'` | Container fill style. | | `size` | `OgeInputSize` | `'md'` | Container height preset — 28/34/42px, the button scale. | | `placeholder` | `string` | `''` | Native placeholder text. | | `hint` | `string` | `—` | Helper text in the subscript region (hidden while an error shows). | | `tooltip` | `string` | `—` | Native `title` attribute of the input element. | | `subscriptSizing` | `OgeInputSubscriptSizing` | `'fixed'` | Whether the hint/error line reserves height, collapses, or is removed. | | `fluid` | `boolean` | `false` | Stretches the field to 100% width (default 240px via `--oge-input-width`). | | `showClearButton` | `boolean` | `false` | Renders the clear (✕) button while the field has a value. | | `id` | `string` | `—` | Base for the generated element ids (input/label/hint/error/counter). Omitted, a stable id comes from `useId()`. | | `tabIndex` | `number` | `0` | Tab order of the native input. | | `autofocus` | `boolean` | `false` | Focuses the editor after its first render. | | `messages` | `Partial` | `—` | Per-instance overrides of user-facing strings; merged over the `` values. | | `prefix` | `ReactNode` | `—` | Leading adornment inside the field — the React face of the `[ogeInputPrefix]` slot. React slots take nodes, not directive markup. | | `suffix` | `ReactNode` | `—` | Trailing adornment, rendered after the built-in rail buttons — the React face of `[ogeInputSuffix]`. | | `showSuccessIcon` | `OgeInputShowSuccessIcon` | `false` | Success icon when valid: `false` / on touch / always. | | `selectOnFocus` | `boolean` | `false` | Selects the whole text when the input receives focus. | | `inputAttr` | `Record` | `—` | Escape hatch: extra attributes rendered onto the native input (component-owned attributes are ignored). | | `className` | `string` | `—` | Extra class names appended to the host element. | | `style` | `CSSProperties` | `—` | Inline styles on the host element. | _Common — state & validation (all editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `disabled` | `boolean` | `false` | Disables the editor. | | `readonly` | `boolean` | `false` | Focusable but not editable. (Contract name — not `readOnly`.) | | `required` | `boolean` | `false` | Marks the field required (label asterisk + validation). | | `name` | `string` | `''` | Native `name` attribute. | | `invalid` | `boolean` | `false` | External invalid override — combined with the `errors` props. | | `pending` | `boolean` | `false` | Async-validation indicator; a spinner shows in the rail while `true`. | | `touched` | `boolean` | `false` | External touched override — hand it your form library’s touched flag. | | `dirty` | `boolean` | `false` | External dirty override. | | `errors` | `readonly OgeFieldError[]` | `[]` | Validation errors in the shared `OgeFieldError` shape — the bridge from React Hook Form, Formik or your own resolver. | | `errorText` | `string` | `—` | Explicit error message — always wins over resolved messages. | | `errorDisplay` | `OgeInputErrorDisplay` | `'touched'` | When resolved errors become visible. | | `debounce` | `number` | `—` | Commit delay in ms for `onValueChange`; blur and Enter flush immediately. | #### Methods _OgeAutocomplete handle (via ref)_ | Name | Type | Description | | --- | --- | --- | | `open() / close() / toggle()` | `() => void` | Popup control (no-ops while disabled/readonly). | _Common — imperative handle (via ref)_ | Name | Type | Description | | --- | --- | --- | | `focus()` | `() => void` | Moves keyboard focus to the native input. | | `blur()` | `() => void` | Blurs the native input. | | `clear()` | `() => void` | Clears the value (commits immediately), keeps focus in the field; no-op when disabled/readonly. | #### Events _OgeAutocomplete callbacks_ | Name | Type | Description | | --- | --- | --- | | `onSelectionChange` | `(event: OgeAutocompleteSelectionChangedEvent) => void` | A suggestion was picked or the selection was canceled — `{ item: TItem \| null, event? }`. | | `onItemClick` | `(event: OgeAutocompleteItemClickEvent) => void` | A suggestion row was activated — `{ item, index, event }`. | | `onDropDownOpened / onDropDownClosed` | `() => void` | Popup visibility changes, from any trigger. | | `onOpenedChange` | `(opened: boolean) => void` | The controlled half of `opened`. | | `onSearchChange` | `(event: { text: string }) => void` | Raw search text on every keystroke — drive server-side filtering from here. | _Common (all field editors)_ | Name | Type | Description | | --- | --- | --- | | `onValueChange` | `(value: T) => void` | Every committed change — the controlled half of `value`. Pass `defaultValue` instead to let the editor own its state. | | `onValueCommitted` | `(event: { value: T; previousValue: T; event: Event \| undefined }) => void` | The same commits with `previousValue` and the originating DOM event (`undefined` for programmatic writes) — the rich payload for cross-field rules. | | `onCleared` | `() => void` | Value cleared via the clear button or the handle’s `clear()`. | | `onEnterKey` | `(event: KeyboardEvent) => void` | Enter pressed inside the editor (pending debounce is flushed first). | | `onFocus` | `(event: FocusEvent) => void` | The editor received focus. | | `onBlur` | `(event: FocusEvent) => void` | The editor lost focus. | | `onInputChange` | `(event: { text: string; event: Event }) => void` | Raw text on every keystroke, regardless of commit policy. | #### Types _Autocomplete types_ | Name | Type | Description | | --- | --- | --- | | `OgeAutocompleteHandle` | `{ focus(); blur(); clear(); open(); close(); toggle() }` | Imperative handle exposed through `ref`. | | `OgeAutocompleteSelectionChangedEvent` | `{ item: TItem \| null; event?: Event }` | `null` means the selection was canceled — the same shape as the Angular output. | | `OgeVirtualScrollOptions` | `interface` | `{ itemHeight?: number; overscan?: number }`; default heights come from the shared `@oge-ui/behavior` option-height table (28/34/40px for sm/md/lg). | ### #### Properties _OgeCheckBox_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `label` | `string` | `''` | Text rendered beside the control. | | `value / defaultValue` | `boolean \| null` | `false` | `true`/`false`, or `null` for the indeterminate (dash) state. Controlled with `onValueChange`, or uncontrolled from `defaultValue`; `null` renders regardless of `threeState`. | | `threeState` | `boolean` | `false` | Lets users cycle into the indeterminate state: `null → true → false → null` (the reference cycle). | | `text` | `string` | `''` | Label text; `children` renders when unset — the React face of the default `` slot. | | `children` | `ReactNode` | `—` | Rich label content when `text` is not enough (JSX projection). | | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Glyph/font size preset. | | `tooltip` | `string` | `—` | Native `title` on the label element. | _Host styling_ | Name | Type | Description | | --- | --- | --- | | `className` | `string` | Extra class names appended to the host element. | | `style` | `CSSProperties` | Inline styles on the host element. | _Common — state & validation (all editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `disabled` | `boolean` | `false` | Disables the editor. | | `readonly` | `boolean` | `false` | Focusable but not editable. (Contract name — not `readOnly`.) | | `required` | `boolean` | `false` | Marks the field required (label asterisk + validation). | | `name` | `string` | `''` | Native `name` attribute. | | `invalid` | `boolean` | `false` | External invalid override — combined with the `errors` props. | | `pending` | `boolean` | `false` | Async-validation indicator; a spinner shows in the rail while `true`. | | `touched` | `boolean` | `false` | External touched override — hand it your form library’s touched flag. | | `dirty` | `boolean` | `false` | External dirty override. | | `errors` | `readonly OgeFieldError[]` | `[]` | Validation errors in the shared `OgeFieldError` shape — the bridge from React Hook Form, Formik or your own resolver. | | `errorText` | `string` | `—` | Explicit error message — always wins over resolved messages. | | `errorDisplay` | `OgeInputErrorDisplay` | `'touched'` | When resolved errors become visible. | | `debounce` | `number` | `—` | Commit delay in ms for `onValueChange`; blur and Enter flush immediately. | #### Methods _OgeCheckBox handle (via ref)_ | Name | Type | Description | | --- | --- | --- | | `toggle()` | `() => void` | Advances the state exactly like a user click (respects `threeState`, no-op while disabled/readonly). | _Common — imperative handle (via ref)_ | Name | Type | Description | | --- | --- | --- | | `focus()` | `() => void` | Moves keyboard focus to the control. | | `blur()` | `() => void` | Blurs the control. | #### Events _Common (all editors)_ | Name | Type | Description | | --- | --- | --- | | `onValueChange` | `(value: T) => void` | Every committed change — the controlled half of `value`. Pass `defaultValue` instead to let the editor own its state. | | `onValueCommitted` | `(event: { value: T; previousValue: T; event: Event \| undefined }) => void` | The same commits with `previousValue` and the originating DOM event (`undefined` for programmatic writes) — the rich payload for cross-field rules. | | `onCleared` | `() => void` | Value cleared via the clear button or the handle’s `clear()`. | | `onEnterKey` | `(event: KeyboardEvent) => void` | Enter pressed inside the editor (pending debounce is flushed first). | | `onFocus` | `(event: FocusEvent) => void` | The editor received focus. | | `onBlur` | `(event: FocusEvent) => void` | The editor lost focus. | #### Types | Name | Type | Description | | --- | --- | --- | | `OgeCheckBoxHandle` | `{ focus(); blur(); toggle() }` | Imperative handle exposed through `ref`. | ### #### Properties _OgeSlider_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `value / defaultValue` | `number` | `0` | The slider value — controlled with `onValueChange`, or uncontrolled from `defaultValue`. Programmatic writes clamp and snap to the step grid. | | `min / max` | `number` | `0 / 100` | Scale bounds. | | `step` | `number` | `1` | Arrow-key and drag increment; thumbs always sit on this grid, with float-error correction (0.1-style steps never drift). | | `largeStep` | `number` | `—` | PageUp/PageDown increment; `undefined` means `step × 10`. | | `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | A vertical slider announces `aria-orientation="vertical"`; Up still increases (APG). | | `showRange` | `boolean` | `true` | Fills the selected portion of the track. | | `showTicks / tickStep` | `boolean / number` | `false` | Tick marks on the `tickStep` grid — falling back to `largeStep`, then `step`; capped at 200 marks. | | `showTickLabels` | `boolean` | `false` | Formatted labels under each tick, fed by `formatValue`. | | `showLabels` | `boolean` | `false` | Formatted `min`/`max` labels at the track ends. | | `valueIndicator` | `'none' \| 'active' \| 'always'` | `'none'` | The inline value bubble: `'active'` while focused, dragged **or hovered**, `'always'` permanent. | | `formatValue` | `(value: number) => string` | `—` | Formats the bubble, the end labels **and** `aria-valuetext` — display and announcement never diverge. | | `showButtons` | `boolean` | `false` | Increment/decrement buttons with press-and-hold repeat — the number box’s spin timing config. | | `ariaLabel` | `string` | `—` | Accessible name of the thumb; the localized `sliderHandle` message is the fallback. | _Host styling_ | Name | Type | Description | | --- | --- | --- | | `className` | `string` | Extra class names appended to the host element. | | `style` | `CSSProperties` | Inline styles on the host element. | _Common — state & validation (all editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `disabled` | `boolean` | `false` | Disables the editor. | | `readonly` | `boolean` | `false` | Focusable but not editable. (Contract name — not `readOnly`.) | | `required` | `boolean` | `false` | Marks the field required (label asterisk + validation). | | `name` | `string` | `''` | Native `name` attribute. | | `invalid` | `boolean` | `false` | External invalid override — combined with the `errors` props. | | `pending` | `boolean` | `false` | Async-validation indicator; a spinner shows in the rail while `true`. | | `touched` | `boolean` | `false` | External touched override — hand it your form library’s touched flag. | | `dirty` | `boolean` | `false` | External dirty override. | | `errors` | `readonly OgeFieldError[]` | `[]` | Validation errors in the shared `OgeFieldError` shape — the bridge from React Hook Form, Formik or your own resolver. | | `errorText` | `string` | `—` | Explicit error message — always wins over resolved messages. | | `errorDisplay` | `OgeInputErrorDisplay` | `'touched'` | When resolved errors become visible. | | `debounce` | `number` | `—` | Commit delay in ms for `onValueChange`; blur and Enter flush immediately. | #### Methods _Common — imperative handle (via ref)_ | Name | Type | Description | | --- | --- | --- | | `focus()` | `() => void` | Moves keyboard focus to the control. | | `blur()` | `() => void` | Blurs the control. | #### Events _OgeSlider callbacks_ | Name | Type | Description | | --- | --- | --- | | `onDragStarted` | `(event: OgeSliderDragStartedEvent) => void` | A drag gesture began on the thumb or the track. | | `onSlideEnded` | `(event: OgeSliderSlideEndedEvent) => void` | Fires once per gesture at release (live changes stream through `onValueCommitted`, throttled by `debounce`). Not emitted when Escape cancels the gesture. | _Common (all editors)_ | Name | Type | Description | | --- | --- | --- | | `onValueChange` | `(value: T) => void` | Every committed change — the controlled half of `value`. Pass `defaultValue` instead to let the editor own its state. | | `onValueCommitted` | `(event: { value: T; previousValue: T; event: Event \| undefined }) => void` | The same commits with `previousValue` and the originating DOM event (`undefined` for programmatic writes) — the rich payload for cross-field rules. | | `onCleared` | `() => void` | Value cleared via the clear button or the handle’s `clear()`. | | `onEnterKey` | `(event: KeyboardEvent) => void` | Enter pressed inside the editor (pending debounce is flushed first). | | `onFocus` | `(event: FocusEvent) => void` | The editor received focus. | | `onBlur` | `(event: FocusEvent) => void` | The editor lost focus. | #### Types | Name | Type | Description | | --- | --- | --- | | `OgeSliderOrientation` | `'horizontal' \| 'vertical'` | Axis the track lays along. | | `OgeSliderValueIndicator` | `'none' \| 'active' \| 'always'` | When the inline value bubble shows. | | `OgeSliderDragStartedEvent / OgeSliderSlideEndedEvent` | `{ event } / { value; event }` | The drag gesture pair. | | `OgeSliderBaseProps` | `interface` | The scale/appearance surface both sliders extend — exported so wrappers can reuse it. | ### #### Properties _OgeRangeSlider_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `value / defaultValue` | `readonly [number, number]` | `[0, 0]` | The `[start, end]` pair — controlled with `onValueChange`, or uncontrolled from `defaultValue`. Programmatic writes clamp, snap and sort. | | `minRange` | `number` | `0` | Minimum distance kept between the thumbs — reflected in each thumb’s dynamic `aria-valuemin`/`aria-valuemax` (the APG multi-thumb constraint). | | `startAriaLabel / endAriaLabel` | `string` | `—` | Accessible names of the thumbs; the localized `sliderStartHandle`/`sliderEndHandle` messages are the fallbacks. | | `startName / endName` | `string` | `''` | Hidden-input names for plain HTML form posts (the single slider uses the inherited `name`). | _Shared with OgeSlider_ | Name | Type | Description | | --- | --- | --- | | `min / max / step / largeStep / orientation / showRange / showTicks / tickStep / showLabels / valueIndicator / formatValue` | `—` | The full scale/appearance surface of ``, identical semantics. `showButtons` is single-slider only. Clicking the track moves the **nearest** thumb. | _Host styling_ | Name | Type | Description | | --- | --- | --- | | `className` | `string` | Extra class names appended to the host element. | | `style` | `CSSProperties` | Inline styles on the host element. | _Common — state & validation (all editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `disabled` | `boolean` | `false` | Disables the editor. | | `readonly` | `boolean` | `false` | Focusable but not editable. (Contract name — not `readOnly`.) | | `required` | `boolean` | `false` | Marks the field required (label asterisk + validation). | | `name` | `string` | `''` | Native `name` attribute. | | `invalid` | `boolean` | `false` | External invalid override — combined with the `errors` props. | | `pending` | `boolean` | `false` | Async-validation indicator; a spinner shows in the rail while `true`. | | `touched` | `boolean` | `false` | External touched override — hand it your form library’s touched flag. | | `dirty` | `boolean` | `false` | External dirty override. | | `errors` | `readonly OgeFieldError[]` | `[]` | Validation errors in the shared `OgeFieldError` shape — the bridge from React Hook Form, Formik or your own resolver. | | `errorText` | `string` | `—` | Explicit error message — always wins over resolved messages. | | `errorDisplay` | `OgeInputErrorDisplay` | `'touched'` | When resolved errors become visible. | | `debounce` | `number` | `—` | Commit delay in ms for `onValueChange`; blur and Enter flush immediately. | #### Methods _Common — imperative handle (via ref)_ | Name | Type | Description | | --- | --- | --- | | `focus()` | `() => void` | Moves keyboard focus to the control. | | `blur()` | `() => void` | Blurs the control. | #### Events _OgeRangeSlider callbacks_ | Name | Type | Description | | --- | --- | --- | | `onDragStarted / onSlideEnded` | `(event: OgeSliderSlideEndedEvent) => void` | The drag gesture pair; an unchanged pair never re-emits `onValueCommitted`. | _Common (all editors)_ | Name | Type | Description | | --- | --- | --- | | `onValueChange` | `(value: T) => void` | Every committed change — the controlled half of `value`. Pass `defaultValue` instead to let the editor own its state. | | `onValueCommitted` | `(event: { value: T; previousValue: T; event: Event \| undefined }) => void` | The same commits with `previousValue` and the originating DOM event (`undefined` for programmatic writes) — the rich payload for cross-field rules. | | `onCleared` | `() => void` | Value cleared via the clear button or the handle’s `clear()`. | | `onEnterKey` | `(event: KeyboardEvent) => void` | Enter pressed inside the editor (pending debounce is flushed first). | | `onFocus` | `(event: FocusEvent) => void` | The editor received focus. | | `onBlur` | `(event: FocusEvent) => void` | The editor lost focus. | #### Types | Name | Type | Description | | --- | --- | --- | | `OgeRangeSliderHandle` | `{ focus(); blur() }` | Imperative handle exposed through `ref`; `focus()` targets the start thumb. | ### #### Properties _OgeSwitch_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `value / defaultValue` | `boolean` | `false` | The on/off state — controlled with `onValueChange`, or uncontrolled from `defaultValue`. | | `label` | `string` | `''` | Accessible name (`aria-label`). | | `onText / offText` | `string` | `—` | Track texts; `undefined` falls back to the localized `switchOn`/`switchOff` messages ('ON'/'OFF'), empty strings hide the text. | | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Track size preset. | _Host styling_ | Name | Type | Description | | --- | --- | --- | | `className` | `string` | Extra class names appended to the host element. | | `style` | `CSSProperties` | Inline styles on the host element. | _Common — state & validation (all editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `disabled` | `boolean` | `false` | Disables the editor. | | `readonly` | `boolean` | `false` | Focusable but not editable. (Contract name — not `readOnly`.) | | `required` | `boolean` | `false` | Marks the field required (label asterisk + validation). | | `name` | `string` | `''` | Native `name` attribute. | | `invalid` | `boolean` | `false` | External invalid override — combined with the `errors` props. | | `pending` | `boolean` | `false` | Async-validation indicator; a spinner shows in the rail while `true`. | | `touched` | `boolean` | `false` | External touched override — hand it your form library’s touched flag. | | `dirty` | `boolean` | `false` | External dirty override. | | `errors` | `readonly OgeFieldError[]` | `[]` | Validation errors in the shared `OgeFieldError` shape — the bridge from React Hook Form, Formik or your own resolver. | | `errorText` | `string` | `—` | Explicit error message — always wins over resolved messages. | | `errorDisplay` | `OgeInputErrorDisplay` | `'touched'` | When resolved errors become visible. | | `debounce` | `number` | `—` | Commit delay in ms for `onValueChange`; blur and Enter flush immediately. | #### Methods _OgeSwitch handle (via ref)_ | Name | Type | Description | | --- | --- | --- | | `toggle()` | `() => void` | Flips the state (no-op while disabled/readonly). | _Common — imperative handle (via ref)_ | Name | Type | Description | | --- | --- | --- | | `focus()` | `() => void` | Moves keyboard focus to the control. | | `blur()` | `() => void` | Blurs the control. | #### Events _Common (all editors)_ | Name | Type | Description | | --- | --- | --- | | `onValueChange` | `(value: T) => void` | Every committed change — the controlled half of `value`. Pass `defaultValue` instead to let the editor own its state. | | `onValueCommitted` | `(event: { value: T; previousValue: T; event: Event \| undefined }) => void` | The same commits with `previousValue` and the originating DOM event (`undefined` for programmatic writes) — the rich payload for cross-field rules. | | `onCleared` | `() => void` | Value cleared via the clear button or the handle’s `clear()`. | | `onEnterKey` | `(event: KeyboardEvent) => void` | Enter pressed inside the editor (pending debounce is flushed first). | | `onFocus` | `(event: FocusEvent) => void` | The editor received focus. | | `onBlur` | `(event: FocusEvent) => void` | The editor lost focus. | #### Types | Name | Type | Description | | --- | --- | --- | | `OgeSwitchHandle` | `{ focus(); blur(); toggle() }` | Imperative handle exposed through `ref`. | ### #### Properties _OgeRadioGroup_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `value / defaultValue` | `unknown` | `null` | The selected item's `valueExpr` result — controlled with `onValueChange`, or uncontrolled from `defaultValue`. | | `items` | `readonly TItem[]` | `[]` | The selectable items. | | `displayExpr / valueExpr / disabledExpr` | `shared with OgeSelectBox` | `—` | Field-name string or function expressions — the select box vocabulary. | | `layout` | `'vertical' \| 'horizontal'` | `'vertical'` | Column or row arrangement. | | `label` | `string` | `''` | Accessible name of the group (`aria-label`). | | `renderItem` | `(item: TItem, context: { index; selected; active }) => ReactNode` | `—` | Custom item rendering next to the radio dot — the render prop replacing Angular’s `itemTemplate`. | | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Dot/font size preset. | _Host styling_ | Name | Type | Description | | --- | --- | --- | | `className` | `string` | Extra class names appended to the host element. | | `style` | `CSSProperties` | Inline styles on the host element. | _Common — state & validation (all editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `disabled` | `boolean` | `false` | Disables the editor. | | `readonly` | `boolean` | `false` | Focusable but not editable. (Contract name — not `readOnly`.) | | `required` | `boolean` | `false` | Marks the field required (label asterisk + validation). | | `name` | `string` | `''` | Native `name` attribute. | | `invalid` | `boolean` | `false` | External invalid override — combined with the `errors` props. | | `pending` | `boolean` | `false` | Async-validation indicator; a spinner shows in the rail while `true`. | | `touched` | `boolean` | `false` | External touched override — hand it your form library’s touched flag. | | `dirty` | `boolean` | `false` | External dirty override. | | `errors` | `readonly OgeFieldError[]` | `[]` | Validation errors in the shared `OgeFieldError` shape — the bridge from React Hook Form, Formik or your own resolver. | | `errorText` | `string` | `—` | Explicit error message — always wins over resolved messages. | | `errorDisplay` | `OgeInputErrorDisplay` | `'touched'` | When resolved errors become visible. | | `debounce` | `number` | `—` | Commit delay in ms for `onValueChange`; blur and Enter flush immediately. | #### Methods _Common — imperative handle (via ref)_ | Name | Type | Description | | --- | --- | --- | | `focus()` | `() => void` | Moves keyboard focus to the control. | | `blur()` | `() => void` | Blurs the control. | #### Events _OgeRadioGroup callbacks_ | Name | Type | Description | | --- | --- | --- | | `onItemClick` | `(event: OgeRadioGroupItemClickEvent) => void` | A radio item was activated by click or keyboard — `{ item, index, event }`. | _Common (all editors)_ | Name | Type | Description | | --- | --- | --- | | `onValueChange` | `(value: T) => void` | Every committed change — the controlled half of `value`. Pass `defaultValue` instead to let the editor own its state. | | `onValueCommitted` | `(event: { value: T; previousValue: T; event: Event \| undefined }) => void` | The same commits with `previousValue` and the originating DOM event (`undefined` for programmatic writes) — the rich payload for cross-field rules. | | `onCleared` | `() => void` | Value cleared via the clear button or the handle’s `clear()`. | | `onEnterKey` | `(event: KeyboardEvent) => void` | Enter pressed inside the editor (pending debounce is flushed first). | | `onFocus` | `(event: FocusEvent) => void` | The editor received focus. | | `onBlur` | `(event: FocusEvent) => void` | The editor lost focus. | #### Types | Name | Type | Description | | --- | --- | --- | | `OgeRadioGroupLayout` | `'vertical' \| 'horizontal'` | Arrangement of the radios. | | `OgeRadioGroupHandle` | `{ focus(); blur() }` | `focus()` moves to the radio holding the roving tabindex. | ### #### Properties _OgeCalendar_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `label` | `string` | `''` | Accessible name of the grid (`aria-label`); the messages supply a default. | | `value / defaultValue` | `Date \| null` | `null` | The selected day (single mode) — a local `Date`; controlled with `onValueChange`, or uncontrolled from `defaultValue`. | | `values / defaultValues` | `readonly Date[]` | `[]` | Selected days for `selectionMode: 'multiple'` — controlled with `onValuesChange`. | | `selectionMode` | `'single' \| 'multiple' \| 'range'` | `'single'` | Range mode picks a start–end pair with a live hover preview. | | `range / defaultRange` | `[Date \| null, Date \| null]` | `[null, null]` | The selected tuple for `selectionMode: 'range'` — controlled with `onRangeChange`; either end may stay open. | | `viewsCount` | `1 \| 2` | `1` | Side-by-side month views (2 is the range layout). | | `zoomLevel / defaultZoomLevel / minZoomLevel / maxZoomLevel` | `'month' \| 'year' \| 'decade'` | `'month' / 'decade' / 'month'` | Drill level (controlled with `onZoomLevelChange`) and its reachable bounds; dx's 'century' is deliberately dropped. | | `min / max` | `Date` | `—` | Day bounds; `undefined` = unbounded (no dx 1000–3000 defaults). | | `disabledDates` | `Date[] \| ((d: Date) => boolean)` | `—` | Individual unselectable days. | | `firstDayOfWeek` | `number` | `—` | 0–6 (Sunday-first); `undefined` resolves from the locale's Intl week info. | | `showWeekNumbers` | `boolean \| { rule: 'firstDay' \| 'firstFourDays' \| 'fullWeek' }` | `false` | Week-number column; `true` = the ISO rule. | | `showTodayButton` | `boolean` | `false` | Renders the localized today shortcut. | | `focusedDate / defaultFocusedDate` | `Date \| null` | `—` | The keyboard-focused day — controlled navigation via `onFocusedDateChange`. | | `locale` | `string` | `—` | BCP 47 locale for all texts (Intl). | | `renderCell` | `(context: OgeCalendarCellContext) => ReactNode` | `—` | Custom day/month/year cell rendering — badges, prices, availability dots. The React face of the Angular `[ogeCalendarCellTemplate]` slot; the context carries `date`, `view`, `text`, `disabled`, `selected`, `today` and `otherPeriod`. | _Host styling_ | Name | Type | Description | | --- | --- | --- | | `className` | `string` | Extra class names appended to the host element. | | `style` | `CSSProperties` | Inline styles on the host element. | _Common — state & validation (all editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `disabled` | `boolean` | `false` | Disables the editor. | | `readonly` | `boolean` | `false` | Focusable but not editable. (Contract name — not `readOnly`.) | | `required` | `boolean` | `false` | Marks the field required (label asterisk + validation). | | `name` | `string` | `''` | Native `name` attribute. | | `invalid` | `boolean` | `false` | External invalid override — combined with the `errors` props. | | `pending` | `boolean` | `false` | Async-validation indicator; a spinner shows in the rail while `true`. | | `touched` | `boolean` | `false` | External touched override — hand it your form library’s touched flag. | | `dirty` | `boolean` | `false` | External dirty override. | | `errors` | `readonly OgeFieldError[]` | `[]` | Validation errors in the shared `OgeFieldError` shape — the bridge from React Hook Form, Formik or your own resolver. | | `errorText` | `string` | `—` | Explicit error message — always wins over resolved messages. | | `errorDisplay` | `OgeInputErrorDisplay` | `'touched'` | When resolved errors become visible. | | `debounce` | `number` | `—` | Commit delay in ms for `onValueChange`; blur and Enter flush immediately. | #### Methods _OgeCalendar handle (via ref)_ | Name | Type | Description | | --- | --- | --- | | `focus()` | `() => void` | Moves keyboard focus to the focused day cell. | #### Events _OgeCalendar callbacks_ | Name | Type | Description | | --- | --- | --- | | `onCellClick` | `(event: OgeCalendarCellClickEvent) => void` | A day/month/year cell was activated — `{ date, view, event }`. | | `onValuesChange / onRangeChange` | `(value) => void` | The controlled halves of `values` and `range` — the multiple/range selections have their own pairs so one calendar never guesses which model you drive. | | `onZoomLevelChange / onFocusedDateChange` | `(value) => void` | The controlled halves of `zoomLevel` and `focusedDate`. | _Common (all editors)_ | Name | Type | Description | | --- | --- | --- | | `onValueChange` | `(value: T) => void` | Every committed change — the controlled half of `value`. Pass `defaultValue` instead to let the editor own its state. | | `onValueCommitted` | `(event: { value: T; previousValue: T; event: Event \| undefined }) => void` | The same commits with `previousValue` and the originating DOM event (`undefined` for programmatic writes) — the rich payload for cross-field rules. | | `onCleared` | `() => void` | Value cleared via the clear button or the handle’s `clear()`. | | `onEnterKey` | `(event: KeyboardEvent) => void` | Enter pressed inside the editor (pending debounce is flushed first). | | `onFocus` | `(event: FocusEvent) => void` | The editor received focus. | | `onBlur` | `(event: FocusEvent) => void` | The editor lost focus. | #### Types | Name | Type | Description | | --- | --- | --- | | `OgeCalendarCellContext` | `{ date; view; text; disabled; selected; today; otherPeriod }` | Argument of `renderCell`. | | `OgeCalendarZoomLevel / OgeCalendarSelectionMode / OgeCalendarRange / OgeCalendarWeekNumberOptions / OgeCalendarDisabledDates` | `@oge-ui/behavior` | The shared calendar vocabulary — the same types the Angular package uses. | ### #### Properties _OgeDateBox_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `showDropDownButton` | `boolean` | `true` | Renders the rail button that toggles the picker; the field click and the keyboard still open it when hidden. | | `dropdownPlacement` | `OgePopupPlacement` | `'bottom-start'` | Preferred popup side/alignment; flips when it would clip. | | `value / defaultValue` | `Date \| null` | `null` | Always a local `Date` — serialization is the app's concern (no `dateSerializationFormat`). Controlled with `onValueChange`, or uncontrolled from `defaultValue`. | | `type` | `'date' \| 'time' \| 'datetime'` | `'date'` | Picker: calendar, interval time list, or both (no dx `pickerType`). The rail icon follows the type. | | `displayFormat` | `Intl.DateTimeFormatOptions \| ((d: Date) => string)` | `—` | Display text; `undefined` = per-type Intl defaults. No format strings, no date library. | | `min / max / disabledDates` | `as OgeCalendar` | `—` | Out-of-range typed text marks the field invalid — it is never clamped (unlike the number box). | | `interval` | `number` | `30` | Time list step in minutes. | | `timeView` | `'list' \| 'columns'` | `'list'` | Time picker layout: one interval list, or hour + minute columns. | | `applyValueMode` | `'instantly' \| 'useButtons'` | `'instantly'` | OK/Cancel footer collects picker changes in a draft when `useButtons`. | | `acceptCustomValue` | `boolean` | `true` | `false` makes the text read-only (picker input only). | | `openOnFieldClick` | `boolean` | `true` | Clicking the field opens the picker. | | `firstDayOfWeek / showWeekNumbers / zoomLevel / renderCalendarCell / locale` | `calendar passthroughs` | `—` | Exposed individually — no `calendarOptions` kitchen-sink object. `renderCalendarCell` is the render prop replacing the projected cell template. | | `opened / defaultOpened` | `boolean` | `false` | Picker visibility — controlled with `onOpenedChange`, or uncontrolled from `defaultOpened`. | _OgeDateRangeBox_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `value / defaultValue` | `[Date \| null, Date \| null]` | `[null, null]` | Start–end tuple on one field: two parsed inputs + a two-view range calendar popup. A reversed pair reorders on commit; either end may stay open. | | `type` | `'date' \| 'datetime'` | `'date'` | `'datetime'` adds start/end time lists to the picker: day and time picks collect in a draft and commit together on OK; both sides parse and render times. | | `interval` | `number` | `30` | Time list step in minutes (`type: 'datetime'`). | | `min / max / disabledDates / firstDayOfWeek / showWeekNumbers / locale / displayFormat / openOnFieldClick / acceptCustomValue` | `as OgeDateBox` | `—` | Shared configuration surface. | _Common — field chrome (all field editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `label` | `string` | `''` | Field label; placement follows `labelMode`. | | `labelMode` | `OgeInputLabelMode` | `'static'` | Label placement: static / floating / hidden (aria-only) / outside. | | `stylingMode` | `OgeInputStylingMode` | `'outlined'` | Container fill style. | | `size` | `OgeInputSize` | `'md'` | Container height preset — 28/34/42px, the button scale. | | `placeholder` | `string` | `''` | Native placeholder text. | | `hint` | `string` | `—` | Helper text in the subscript region (hidden while an error shows). | | `tooltip` | `string` | `—` | Native `title` attribute of the input element. | | `subscriptSizing` | `OgeInputSubscriptSizing` | `'fixed'` | Whether the hint/error line reserves height, collapses, or is removed. | | `fluid` | `boolean` | `false` | Stretches the field to 100% width (default 240px via `--oge-input-width`). | | `showClearButton` | `boolean` | `false` | Renders the clear (✕) button while the field has a value. | | `id` | `string` | `—` | Base for the generated element ids (input/label/hint/error/counter). Omitted, a stable id comes from `useId()`. | | `tabIndex` | `number` | `0` | Tab order of the native input. | | `autofocus` | `boolean` | `false` | Focuses the editor after its first render. | | `messages` | `Partial` | `—` | Per-instance overrides of user-facing strings; merged over the `` values. | | `prefix` | `ReactNode` | `—` | Leading adornment inside the field — the React face of the `[ogeInputPrefix]` slot. React slots take nodes, not directive markup. | | `suffix` | `ReactNode` | `—` | Trailing adornment, rendered after the built-in rail buttons — the React face of `[ogeInputSuffix]`. | | `showSuccessIcon` | `OgeInputShowSuccessIcon` | `false` | Success icon when valid: `false` / on touch / always. | | `selectOnFocus` | `boolean` | `false` | Selects the whole text when the input receives focus. | | `inputAttr` | `Record` | `—` | Escape hatch: extra attributes rendered onto the native input (component-owned attributes are ignored). | | `className` | `string` | `—` | Extra class names appended to the host element. | | `style` | `CSSProperties` | `—` | Inline styles on the host element. | _Common — state & validation (all editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `disabled` | `boolean` | `false` | Disables the editor. | | `readonly` | `boolean` | `false` | Focusable but not editable. (Contract name — not `readOnly`.) | | `required` | `boolean` | `false` | Marks the field required (label asterisk + validation). | | `name` | `string` | `''` | Native `name` attribute. | | `invalid` | `boolean` | `false` | External invalid override — combined with the `errors` props. | | `pending` | `boolean` | `false` | Async-validation indicator; a spinner shows in the rail while `true`. | | `touched` | `boolean` | `false` | External touched override — hand it your form library’s touched flag. | | `dirty` | `boolean` | `false` | External dirty override. | | `errors` | `readonly OgeFieldError[]` | `[]` | Validation errors in the shared `OgeFieldError` shape — the bridge from React Hook Form, Formik or your own resolver. | | `errorText` | `string` | `—` | Explicit error message — always wins over resolved messages. | | `errorDisplay` | `OgeInputErrorDisplay` | `'touched'` | When resolved errors become visible. | | `debounce` | `number` | `—` | Commit delay in ms for `onValueChange`; blur and Enter flush immediately. | #### Methods _OgeDateBox / OgeDateRangeBox handle (via ref)_ | Name | Type | Description | | --- | --- | --- | | `open() / close() / toggle()` | `() => void` | Picker control (no-ops while disabled/readonly). | _Common — imperative handle (via ref)_ | Name | Type | Description | | --- | --- | --- | | `focus()` | `() => void` | Moves keyboard focus to the native input. | | `blur()` | `() => void` | Blurs the native input. | | `clear()` | `() => void` | Clears the value (commits immediately), keeps focus in the field; no-op when disabled/readonly. | #### Events _OgeDateBox callbacks_ | Name | Type | Description | | --- | --- | --- | | `onDropDownOpened / onDropDownClosed` | `() => void` | Picker visibility changes, from any trigger. | | `onOpenedChange` | `(opened: boolean) => void` | The controlled half of `opened`. | _Common (all field editors)_ | Name | Type | Description | | --- | --- | --- | | `onValueChange` | `(value: T) => void` | Every committed change — the controlled half of `value`. Pass `defaultValue` instead to let the editor own its state. | | `onValueCommitted` | `(event: { value: T; previousValue: T; event: Event \| undefined }) => void` | The same commits with `previousValue` and the originating DOM event (`undefined` for programmatic writes) — the rich payload for cross-field rules. | | `onCleared` | `() => void` | Value cleared via the clear button or the handle’s `clear()`. | | `onEnterKey` | `(event: KeyboardEvent) => void` | Enter pressed inside the editor (pending debounce is flushed first). | | `onFocus` | `(event: FocusEvent) => void` | The editor received focus. | | `onBlur` | `(event: FocusEvent) => void` | The editor lost focus. | | `onInputChange` | `(event: { text: string; event: Event }) => void` | Raw text on every keystroke, regardless of commit policy. | #### Types _Date types_ | Name | Type | Description | | --- | --- | --- | | `OgeDateBoxHandle / OgeDateRangeBoxHandle` | `{ focus(); blur(); clear(); open(); close(); toggle() }` | Imperative handles exposed through `ref`. | | `OgeDateBoxType / OgeDateBoxApplyValueMode / OgeDateBoxDisplayFormat / OgeDateBoxTimeView` | `@oge-ui/behavior` | The string unions and the display-format shape — shared with the Angular package, so locale-aware typed parsing behaves identically. | ### #### Properties _OgeColorBox_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `dropdownPlacement` | `OgePopupPlacement` | `'bottom-start'` | Preferred popup side/alignment; flips when it would clip. | | `value / defaultValue` | `string \| null` | `null` | The committed color as a CSS string, normalized to `format` on user commits. Programmatic writes keep any parseable CSS color verbatim; unparseable writes land as `null`. | | `format` | `'hex' \| 'rgb' \| 'rgba' \| 'hsl'` | `'hex'` | Committed string shape. Translucent colors widen to carry alpha: `#rrggbbaa` / `rgba()` / `hsla()`. | | `view` | `'gradient' \| 'palette' \| 'both'` | `'gradient'` | Popup surfaces: the saturation/brightness gradient with sliders and inputs, the swatch palette, or both stacked — no view switcher. | | `editAlphaChannel` | `boolean` | `false` | Adds the alpha slider + percent input and lets the output carry alpha. Without it, alpha is coerced to 1 on commit — `rgba()` text still parses. | | `applyValueMode` | `'instantly' \| 'useButtons'` | `'instantly'` | OK/Cancel footer collects panel interactions in a draft when `useButtons`; the default commits live (dragging streams through `onValueCommitted`, throttled by `debounce`). | | `acceptCustomValue` | `boolean` | `true` | `false` makes the text read-only (picker input only). Typed text parses any CSS color incl. the 148 named colors; unparseable text reverts on blur. | | `keyStep` | `number` | `5` | Arrow-key increment of the panel parts in value units — hue degrees, alpha percent, surface saturation/brightness percent. PageUp/PageDown move by 5× (value-space, zoom-independent). | | `palette` | `readonly string[]` | `—` | Palette swatches as CSS color strings; `undefined` renders the exported `OGE_DEFAULT_COLOR_PALETTE`. Unparseable entries are dropped. | | `paletteColumns` | `number` | `10` | Swatch columns of the palette grid. | | `openOnFieldClick` | `boolean` | `true` | Clicking the field opens the picker. | | `showDropDownButton` | `boolean` | `true` | `false` hides the rail chevron — field click and ArrowDown still open. | | `showEyedropper` | `boolean` | `true` | The eyedropper button (pick a color from anywhere on screen) — rendered only in browsers shipping the `EyeDropper` API; progressive enhancement, no polyfill. The picked color keeps the working alpha. | | `opened / defaultOpened` | `boolean` | `false` | Picker visibility — controlled with `onOpenedChange`, or uncontrolled from `defaultOpened`. | _Common — field chrome (all field editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `label` | `string` | `''` | Field label; placement follows `labelMode`. | | `labelMode` | `OgeInputLabelMode` | `'static'` | Label placement: static / floating / hidden (aria-only) / outside. | | `stylingMode` | `OgeInputStylingMode` | `'outlined'` | Container fill style. | | `size` | `OgeInputSize` | `'md'` | Container height preset — 28/34/42px, the button scale. | | `placeholder` | `string` | `''` | Native placeholder text. | | `hint` | `string` | `—` | Helper text in the subscript region (hidden while an error shows). | | `tooltip` | `string` | `—` | Native `title` attribute of the input element. | | `subscriptSizing` | `OgeInputSubscriptSizing` | `'fixed'` | Whether the hint/error line reserves height, collapses, or is removed. | | `fluid` | `boolean` | `false` | Stretches the field to 100% width (default 240px via `--oge-input-width`). | | `showClearButton` | `boolean` | `false` | Renders the clear (✕) button while the field has a value. | | `id` | `string` | `—` | Base for the generated element ids (input/label/hint/error/counter). Omitted, a stable id comes from `useId()`. | | `tabIndex` | `number` | `0` | Tab order of the native input. | | `autofocus` | `boolean` | `false` | Focuses the editor after its first render. | | `messages` | `Partial` | `—` | Per-instance overrides of user-facing strings; merged over the `` values. | | `prefix` | `ReactNode` | `—` | Leading adornment inside the field — the React face of the `[ogeInputPrefix]` slot. React slots take nodes, not directive markup. | | `suffix` | `ReactNode` | `—` | Trailing adornment, rendered after the built-in rail buttons — the React face of `[ogeInputSuffix]`. | | `showSuccessIcon` | `OgeInputShowSuccessIcon` | `false` | Success icon when valid: `false` / on touch / always. | | `selectOnFocus` | `boolean` | `false` | Selects the whole text when the input receives focus. | | `inputAttr` | `Record` | `—` | Escape hatch: extra attributes rendered onto the native input (component-owned attributes are ignored). | | `className` | `string` | `—` | Extra class names appended to the host element. | | `style` | `CSSProperties` | `—` | Inline styles on the host element. | _Common — state & validation (all editors)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `disabled` | `boolean` | `false` | Disables the editor. | | `readonly` | `boolean` | `false` | Focusable but not editable. (Contract name — not `readOnly`.) | | `required` | `boolean` | `false` | Marks the field required (label asterisk + validation). | | `name` | `string` | `''` | Native `name` attribute. | | `invalid` | `boolean` | `false` | External invalid override — combined with the `errors` props. | | `pending` | `boolean` | `false` | Async-validation indicator; a spinner shows in the rail while `true`. | | `touched` | `boolean` | `false` | External touched override — hand it your form library’s touched flag. | | `dirty` | `boolean` | `false` | External dirty override. | | `errors` | `readonly OgeFieldError[]` | `[]` | Validation errors in the shared `OgeFieldError` shape — the bridge from React Hook Form, Formik or your own resolver. | | `errorText` | `string` | `—` | Explicit error message — always wins over resolved messages. | | `errorDisplay` | `OgeInputErrorDisplay` | `'touched'` | When resolved errors become visible. | | `debounce` | `number` | `—` | Commit delay in ms for `onValueChange`; blur and Enter flush immediately. | #### Methods _OgeColorBox handle (via ref)_ | Name | Type | Description | | --- | --- | --- | | `open() / close() / toggle()` | `() => void` | Picker control (no-ops while disabled/readonly). | _Common — imperative handle (via ref)_ | Name | Type | Description | | --- | --- | --- | | `focus()` | `() => void` | Moves keyboard focus to the native input. | | `blur()` | `() => void` | Blurs the native input. | | `clear()` | `() => void` | Clears the value (commits immediately), keeps focus in the field; no-op when disabled/readonly. | #### Events _OgeColorBox callbacks_ | Name | Type | Description | | --- | --- | --- | | `onDropDownOpened / onDropDownClosed` | `() => void` | Picker visibility changes, from any trigger. | | `onOpenedChange` | `(opened: boolean) => void` | The controlled half of `opened`. | _Common (all field editors)_ | Name | Type | Description | | --- | --- | --- | | `onValueChange` | `(value: T) => void` | Every committed change — the controlled half of `value`. Pass `defaultValue` instead to let the editor own its state. | | `onValueCommitted` | `(event: { value: T; previousValue: T; event: Event \| undefined }) => void` | The same commits with `previousValue` and the originating DOM event (`undefined` for programmatic writes) — the rich payload for cross-field rules. | | `onCleared` | `() => void` | Value cleared via the clear button or the handle’s `clear()`. | | `onEnterKey` | `(event: KeyboardEvent) => void` | Enter pressed inside the editor (pending debounce is flushed first). | | `onFocus` | `(event: FocusEvent) => void` | The editor received focus. | | `onBlur` | `(event: FocusEvent) => void` | The editor lost focus. | | `onInputChange` | `(event: { text: string; event: Event }) => void` | Raw text on every keystroke, regardless of commit policy. | #### Types _Color types_ | Name | Type | Description | | --- | --- | --- | | `OgeColorBoxView / OgeColorBoxApplyValueMode / OgeColorFormat` | `@oge-ui/behavior` | The string unions of `view`, `applyValueMode` and `format`. | | `OGE_DEFAULT_COLOR_PALETTE` | `readonly string[]` | The built-in 50-swatch palette used when `palette` is not set. | | `Color messages` | `OgeInputsMessages keys` | All popup strings localize through ``: `colorPickerLabel`, `hueSliderLabel`/`hueValueText`, `alphaSliderLabel`/`alphaValueText`, `colorSurfaceLabel`/`colorSurfaceRoleDescription`/`surfaceValueText`, `paletteLabel`, the hex/R/G/B/A input labels, `eyedropperButton` and `invalidColorError`. | ### Shared input types #### Types _Unions & contracts_ | Name | Type | Description | | --- | --- | --- | | `OgeControlProps` | `interface` | The base every editor extends: the `value`/`defaultValue`/`onValueChange` trio, `onValueCommitted`, the state and validation props, and the focus/blur/enter/cleared callbacks. The React counterpart of the Angular `OgeInputBase` class. | | `OgeInputLabelMode` | `'static' \| 'floating' \| 'hidden' \| 'outside'` | `hidden` renders the label as `aria-label` only. | | `OgeInputStylingMode` | `'outlined' \| 'filled' \| 'underlined'` | Container fill style. | | `OgeInputSize` | `'sm' \| 'md' \| 'lg'` | 28 / 34 / 42px heights. | | `OgeInputSubscriptSizing` | `'fixed' \| 'dynamic' \| 'none'` | `fixed` reserves one line so errors never shift layout. | | `OgeInputErrorDisplay` | `'touched' \| 'dirty' \| 'always'` | When resolved errors become visible. | | `OgeInputCounterMode` | `'limit' \| 'soft'` | `soft` allows typing past `maxLength` and colors the counter danger. | | `OgeInputShowSuccessIcon` | `false \| 'touched' \| 'always'` | Success-icon visibility policy. | | `OgeTextBoxMode` | `'text' \| 'email' \| 'password' \| 'search' \| 'tel' \| 'url'` | Native input type of the text box. | | `OgeNumberBoxMode` | `'text' \| 'tel'` | Native input type of the number box. | | `OgeFieldError` | `{ kind: string; message?: string }` | The validation-error shape the `errors` prop takes — map your form library’s errors into it once. | _Callback payloads_ | Name | Type | Description | | --- | --- | --- | | `onValueCommitted payload` | `{ value: T; previousValue: T; event: Event \| undefined }` | `event === undefined` means a programmatic change. | | `onInputChange payload` | `{ text: string; event: Event }` | Raw keystroke payload. | | `onEnterKey / onFocus / onBlur payloads` | `KeyboardEvent / FocusEvent` | The native DOM events, not React synthetic wrappers — the editors listen natively so debounce flushing stays ordered. | _Slots & helpers_ | Name | Type | Description | | --- | --- | --- | | `prefix / suffix` | `ReactNode props` | Leading and trailing adornments inside the field — the React face of the `[ogeInputPrefix]` / `[ogeInputSuffix]` directives. The trailing slot renders after the built-in rail buttons. | | `OgeInputCounterState` | `{ count: number; max: number \| undefined; over: boolean }` | Counter state rendered in the subscript end slot. | | `OgeInputRevealState` | `{ visible; active; toggle() }` | Password-reveal state (text box only). | | `OgeInputCopyState` | `{ visible; copied; trigger() }` | Copy-to-clipboard state (text box only). | | `measureTextAreaHeight(el, minRows, maxRows?)` | `number` | Fallback auto-resize measurement for browsers without CSS `field-sizing: content`. | ### Inputs configuration #### Methods | Name | Type | Description | | --- | --- | --- | | `OgeInputsConfigProvider` | `(props: { config?: OgeInputsConfigInput; children?: ReactNode }) => JSX.Element` | Wrap a subtree to change the editors’ defaults and user-facing strings beneath it — the React counterpart of Angular’s `provideOgeInputsConfig()`. Both merge over the same `@oge-ui/behavior` defaults, so a message override reads identically in either layer. | | `useOgeInputsConfig()` | `() => OgeInputsConfig` | Reads the resolved config of the current subtree — how a custom editor of your own picks up the same messages and timings. | #### Types _OgeInputsConfig_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `spinRepeatDelayMs` | `number` | `400` | Delay before spin buttons start repeating. | | `spinRepeatIntervalMs` | `number` | `80` | Interval between spin repeats. | | `copiedResetMs` | `number` | `2000` | How long the copy button shows "copied". | | `messages` | `OgeInputsMessages` | `—` | User-facing strings — the same key set the Angular package documents (clear/reveal/copy/spin labels, counter patterns and the validation messages). | ### Demos Each demo below is a complete standalone component — copy one whole and it compiles. #### Basic usage ```ts 'use client'; import { useState } from 'react'; import { OgeAutocomplete } from '@oge-ui/react-inputs'; const cities = ['Ankara', 'Berlin', 'Lisbon', 'Oslo', 'Tokyo']; export function AutocompleteBasicDemo() { // the committed value is the TEXT itself, not an item value const [cityName, setCityName] = useState(''); return ( ); } ``` #### Suggestion tuning ```ts 'use client'; import { useState } from 'react'; import { OgeAutocomplete } from '@oge-ui/react-inputs'; const products = [ { id: 1, name: 'Aurora Display' }, { id: 2, name: 'Aurora Keyboard' }, { id: 3, name: 'Nimbus Router' }, ]; export function AutocompleteTuningDemo() { const [productName, setProductName] = useState(''); return ( ); } ``` #### Force selection ```ts 'use client'; import { useState } from 'react'; import { OgeAutocomplete } from '@oge-ui/react-inputs'; const users = [ { id: 1, name: 'Elif Kaya', role: 'Engineering' }, { id: 2, name: 'Mert Demir', role: 'Design' }, ]; export function AutocompleteForceDemo() { const [assigneeName, setAssigneeName] = useState(''); // non-matching text reverts on blur; an exact match resolves to the item // (canonical casing) and fires onSelectionChange const [assigneeRole, setAssigneeRole] = useState(null); return ( setAssigneeRole(event.item?.role ?? null)} /> ); } ``` #### Virtual scrolling ```ts 'use client'; import { useState } from 'react'; import { OgeAutocomplete, OgeSelectBox } from '@oge-ui/react-inputs'; const accounts = Array.from( { length: 10000 }, (_, index) => `Account ${index + 1}`, ); export function AutocompleteVirtualDemo() { const [accountName, setAccountName] = useState(''); const [accountId, setAccountId] = useState(null); return (
{/* 10 000 rows, ~15 in the DOM */}
); } ``` #### Lazy & server-side data ```ts 'use client'; import { useState } from 'react'; import { OgeAutocomplete } from '@oge-ui/react-inputs'; // invoked once, on first open const loadRepos = (): Promise => fetch('/api/repos').then((response) => response.json()); export function AutocompleteLazyDemo() { const [repo, setRepo] = useState(''); const [serverItems, setServerItems] = useState([]); const [serverLoading, setServerLoading] = useState(false); const queryServer = (text: string): void => { setServerLoading(true); fetch(`/api/repos?q=${encodeURIComponent(text)}`) .then((response) => response.json()) .then((items: string[]) => setServerItems(items)) .finally(() => setServerLoading(false)); }; return (
{/* or fully server-side: keep items in sync yourself */} queryServer(event.text)} />
); } ``` #### Field chrome ```ts 'use client'; import { useState } from 'react'; import { OgeAutocomplete } from '@oge-ui/react-inputs'; const tags = ['angular', 'signals', 'zoneless']; export function AutocompleteChromeDemo() { const [tag, setTag] = useState(''); return ( ); } ``` #### Getting started ```ts 'use client'; import { useState } from 'react'; import { OgeColorBox } from '@oge-ui/react-inputs'; export function ColorBoxDemo() { const [brand, setBrand] = useState('#3aa0ff'); return (
{/* A dropdown color editor: the field shows a swatch + the committed string; the popup is a role="dialog" that takes real DOM focus (no APG color-picker pattern exists — it is composed from dialog, sliders and grid primitives). ArrowDown opens; Escape restores focus to the input. */}

Value: {brand ?? 'null'}

); } ``` #### Formats and alpha ```ts 'use client'; import { useState } from 'react'; import { OgeColorBox } from '@oge-ui/react-inputs'; export function FormatsDemo() { const [overlay, setOverlay] = useState( 'rgba(58, 160, 255, 0.5)', ); const [accent, setAccent] = useState('hsl(210, 100%, 61%)'); return (
{/* format controls the committed string shape ('hex' default — the DevExtreme choice; Kendo defaults to rgba). editAlphaChannel adds the alpha slider + input; a translucent color then WIDENS the output (#rrggbbaa / rgba() / hsla()), while opaque colors stay compact. */}

Overlay: {overlay} — Accent: {accent}

); } ``` #### Palette view ```ts 'use client'; import { useState } from 'react'; import { OgeColorBox } from '@oge-ui/react-inputs'; const swatches: readonly string[] = [ '#dc2626', '#ea580c', '#d97706', '#16a34a', '#0d9488', '#2563eb', '#7c3aed', '#c026d3', '#475569', '#111827', ]; export function PaletteDemo() { const [tag, setTag] = useState('#16a34a'); return ( <> {/* The palette is an APG grid: roving tabindex, arrows move by cell/row, Home/End row edges, Ctrl+Home/End grid corners, Enter/Space picks (and closes — a swatch is a final choice). Cells announce their color string; the selected checkmark picks black or white by WCAG contrast. */} ); } ``` #### Apply with buttons ```ts 'use client'; import { useState } from 'react'; import { OgeColorBox } from '@oge-ui/react-inputs'; export function ButtonsDemo() { const [theme, setTheme] = useState('#7c3aed'); return ( <> {/* applyValueMode: 'instantly' (default) commits every panel interaction live — dragging streams through onValueCommitted. 'useButtons' collects interactions in a draft, shows a committed | draft preview pair in the footer and commits only on OK; Cancel (or Escape / outside click) discards. The date box's exact contract, applied to color. */} ); } ``` #### Typed colors ```ts 'use client'; import { useState } from 'react'; import { OgeColorBox } from '@oge-ui/react-inputs'; export function TypedDemo() { const [typed, setTyped] = useState('rebeccapurple'); return (
{/* Typed text parses ANY CSS color — #rgb/#rrggbb/#rrggbbaa, rgb()/rgba() (comma and space/slash syntax), hsl(), the 148 named colors, 'transparent'. Commits normalize to format; unparseable text shows the invalid state while typing and REVERTS on blur — a wrong color is never committed. acceptCustomValue makes the text read-only. */}
); } ``` #### Inside a form ```ts 'use client'; import { useState } from 'react'; import { OgeColorBox } from '@oge-ui/react-inputs'; export function FormDemo() { const [branding, setBranding] = useState({ primary: '#3aa0ff' }); return (
setBranding((current) => ({ ...current, primary: primary ?? '' })) } />

Model: {branding.primary}

); } ``` #### Calendar ```ts 'use client'; import { useState } from 'react'; import { OgeCalendar } from '@oge-ui/react-inputs'; const min = new Date(2026, 7, 10); const isWeekend = (day: Date): boolean => day.getDay() === 0 || day.getDay() === 6; export function CalendarDemo() { const [date, setDate] = useState(new Date(2026, 7, 15)); return (
value: {date?.toDateString() ?? 'null'}
); } ``` #### Date Box ```ts 'use client'; import { useState } from 'react'; import { OgeDateBox } from '@oge-ui/react-inputs'; const min = new Date(2026, 7, 10); export function DateBoxDemo() { const [start, setStart] = useState(null); const [delivery, setDelivery] = useState(null); return (
{/* typed text parses locale-aware through Intl — never Date.parse */}
); } ``` #### Range selection ```ts 'use client'; import { useState } from 'react'; import { OgeCalendar, OgeDateRangeBox } from '@oge-ui/react-inputs'; import type { OgeCalendarRange } from '@oge-ui/react-inputs'; export function RangeDemo() { const [range, setRange] = useState([null, null]); const [period, setPeriod] = useState([ new Date(2026, 7, 10), new Date(2026, 7, 20), ]); const [maintenance, setMaintenance] = useState([ new Date(2026, 7, 14, 22, 0), new Date(2026, 7, 15, 6, 30), ]); return (
{/* two-view range calendar with hover preview */}
{/* start–end on one field; typed or picked, reversed pairs reorder */}
period:{' '} {period[0]?.toDateString() ?? '—'} → {period[1]?.toDateString() ?? '—'}
{/* datetime range: start/end time lists + OK, commits as a draft */}
window:{' '} {maintenance[0]?.toLocaleString() ?? '—'} →{' '} {maintenance[1]?.toLocaleString() ?? '—'}
); } ``` #### Time & datetime ```ts 'use client'; import { useState } from 'react'; import { OgeDateBox } from '@oge-ui/react-inputs'; export function TimeDemo() { const [meeting, setMeeting] = useState( new Date(2026, 7, 15, 9, 30), ); const [alarm, setAlarm] = useState(new Date(2026, 7, 15, 7, 0)); const [due, setDue] = useState(null); return (
{/* one interval list (default) … */} {/* … or hour + minute columns */} {/* OK/Cancel commit policy */}
); } ``` #### The three editors ```ts 'use client'; import { useState } from 'react'; import { OgeNumberBox, OgeTextArea, OgeTextBox } from '@oge-ui/react-inputs'; export function EditorsDemo() { const [name, setName] = useState(''); const [amount, setAmount] = useState(null); const [notes, setNotes] = useState(''); return (
); } ``` #### Styling modes & sizes ```ts 'use client'; import { OgeTextBox } from '@oge-ui/react-inputs'; export function StylingDemo() { return (
); } ``` #### Label modes ```ts 'use client'; import { OgeTextBox } from '@oge-ui/react-inputs'; export function LabelModesDemo() { return (
); } ``` #### Prefix & suffix slots ```ts 'use client'; import { OgeTextBox } from '@oge-ui/react-inputs'; export function AdornmentsDemo() { return (
€} /> https://} />
); } ``` #### Basic usage ```ts 'use client'; import { useState } from 'react'; import { OgeSelectBox } from '@oge-ui/react-inputs'; const cities = ['Ankara', 'Berlin', 'Lisbon', 'Oslo', 'Tokyo']; export function SelectBoxBasicDemo() { const [city, setCity] = useState(null); return ( ); } ``` #### Data mapping & search ```ts 'use client'; import { useState } from 'react'; import { OgeSelectBox } from '@oge-ui/react-inputs'; const users = [ { id: 1, name: 'Elif Kaya', role: 'Engineering' }, { id: 2, name: 'Mert Demir', role: 'Design' }, { id: 3, name: 'Deniz Ünal', role: 'Engineering' }, ]; export function SelectBoxMappingDemo() { const [assigneeId, setAssigneeId] = useState(null); return ( console.log('searching for', event.text)} /> ); } ``` #### Grouping & custom values ```ts 'use client'; import { useState } from 'react'; import { OgeSelectBox } from '@oge-ui/react-inputs'; const users = [ { id: 1, name: 'Elif Kaya', role: 'Engineering' }, { id: 2, name: 'Mert Demir', role: 'Design' }, ]; export function SelectBoxGroupingDemo() { const [memberId, setMemberId] = useState(null); const [tags, setTags] = useState(['angular', 'signals']); const [tag, setTag] = useState(null); return (
{/* flat data, grouped on the fly */} {/* typed text becomes a new item */} { // or a promise, or null to reject payload.customItem = payload.text; setTags((current) => [...current, payload.text]); }} />
); } ``` #### Lazy data ```ts 'use client'; import { useState } from 'react'; import { OgeSelectBox } from '@oge-ui/react-inputs'; // invoked once, on first open — loading/error rows render while pending const loadWarehouses = (): Promise => new Promise((resolve) => setTimeout(() => resolve(['Hamburg', 'İzmir', 'Rotterdam']), 900), ); export function SelectBoxLazyDemo() { const [warehouse, setWarehouse] = useState(null); return ( ); } ``` #### Tag Box — multi-select ```ts 'use client'; import { useState } from 'react'; import { OgeTagBox } from '@oge-ui/react-inputs'; const skills = ['Angular', 'TypeScript', 'CSS', 'Testing']; const users = [ { id: 1, name: 'Elif Kaya', avatar: '/avatars/1.png' }, { id: 2, name: 'Mert Demir', avatar: '/avatars/2.png' }, ]; export function TagBoxDemo() { const [selectedSkills, setSelectedSkills] = useState([ 'Angular', ]); const [teamIds, setTeamIds] = useState([]); return (
console.log(event.addedItems, event.removedItems) } />
); } ``` #### Item states & templates ```ts 'use client'; import { useState } from 'react'; import { OgeSelectBox } from '@oge-ui/react-inputs'; const plans = [ { id: 'free', name: 'Free', soldOut: false }, { id: 'pro', name: 'Pro', soldOut: false }, { id: 'enterprise', name: 'Enterprise', soldOut: true }, ]; export function SelectBoxStatesDemo() { const [planId, setPlanId] = useState('free'); return ( ); } ``` #### Field chrome ```ts 'use client'; import { useState } from 'react'; import { OgeSelectBox } from '@oge-ui/react-inputs'; const countries = ['Germany', 'Netherlands', 'Türkiye']; export function SelectBoxChromeDemo() { const [country, setCountry] = useState(null); return (
); } ``` #### Character counter ```ts 'use client'; import { useState } from 'react'; import { OgeTextArea, OgeTextBox } from '@oge-ui/react-inputs'; export function CounterDemo() { const [bio, setBio] = useState(''); return (
{/* grapheme-accurate: a family emoji counts as 1 character, not 8 code units */} {/* soft mode: typing past the limit is allowed, the counter turns red */}
); } ``` #### Password reveal & copy ```ts 'use client'; import { useState } from 'react'; import { OgeTextBox } from '@oge-ui/react-inputs'; export function PasswordDemo() { const [password, setPassword] = useState('top-secret-42'); const [token] = useState('oge_live_4f8a2b91c3d7'); return (
); } ``` #### Locale-aware numbers ```ts 'use client'; import { useState } from 'react'; import { OgeNumberBox } from '@oge-ui/react-inputs'; export function NumbersDemo() { const [price, setPrice] = useState(1234.5); const [quantity, setQuantity] = useState(10); return (
{/* Intl.NumberFormat display on blur, raw editing on focus */}
); } ``` #### Debounced commits ```ts 'use client'; import { useState } from 'react'; import { OgeTextBox } from '@oge-ui/react-inputs'; export function DebounceDemo() { const [query, setQuery] = useState(''); const [keystrokes, setKeystrokes] = useState(0); return (
{/* value commits 400ms after the last keystroke; blur/Enter flush instantly */} setKeystrokes((n) => n + 1)} /> keystrokes: {keystrokes} · committed value: "{query}"
); } ``` #### Getting started ```ts 'use client'; import { useState } from 'react'; import { OgeSlider } from '@oge-ui/react-inputs'; export function SliderDemo() { // The WAI-ARIA APG slider: a focusable role="slider" thumb — // arrows ±step (RTL-aware), PageUp/PageDown ±largeStep, Home/End to the // ends. Dragging commits live; debounce throttles it; Escape cancels the // gesture and restores the start value. const [volume, setVolume] = useState(40); return ( <>

Value: {volume}

); } ``` #### Range slider ```ts 'use client'; import { useState } from 'react'; import { OgeRangeSlider } from '@oge-ui/react-inputs'; export function RangeSliderDemo() { // APG multi-thumb: two focusable thumbs, each one's aria-valuemin/max // dynamically constrained by the other (plus minRange). Clicking the // track moves the NEAREST thumb. const [price, setPrice] = useState([200, 600]); return ( <>

Range:{' '} {price[0]} – {price[1]}

); } ``` #### Ticks and labels ```ts 'use client'; import { useState } from 'react'; import { OgeSlider } from '@oge-ui/react-inputs'; export function SliderTicksDemo() { const [rating, setRating] = useState(6); return ( ); } ``` #### Value indicator ```ts 'use client'; import { useState } from 'react'; import { OgeSlider } from '@oge-ui/react-inputs'; const asDecibels = (value: number): string => `${value} dB`; export function SliderIndicatorDemo() { const [decibels, setDecibels] = useState(40); return ( ); } ``` #### Buttons and vertical ```ts 'use client'; import { useState } from 'react'; import { OgeSlider } from '@oge-ui/react-inputs'; export function SliderButtonsDemo() { const [level, setLevel] = useState(30); return ( <>
); } ``` #### Inside a form ```ts 'use client'; import { useState } from 'react'; import { OgeSlider } from '@oge-ui/react-inputs'; export function SliderFormDemo() { // A bare editor: the slider renders no label/hint/error chrome of its own. // Any form library binds it the same way — read the value from your form // state, write it back from onValueChange. const [settings, setSettings] = useState({ brightness: 70 }); return (
event.preventDefault()}> Brightness setSettings({ brightness })} min={0} max={100} step={5} ariaLabel="Brightness" />

Model: {settings.brightness}

); } ``` #### Check Box ```ts 'use client'; import { useState } from 'react'; import { OgeCheckBox } from '@oge-ui/react-inputs'; export function CheckBoxDemo() { const [agreed, setAgreed] = useState(false); const [all, setAll] = useState(null); return (
I agree to the terms {/* tri-state: null renders the indeterminate dash; threeState lets USERS cycle null → true → false → null */}
select all: {all === null ? 'null (indeterminate)' : String(all)}
); } ``` #### Switch ```ts 'use client'; import { useState } from 'react'; import { OgeSwitch } from '@oge-ui/react-inputs'; export function SwitchDemo() { const [notify, setNotify] = useState(true); const [enabled, setEnabled] = useState(false); const [plain, setPlain] = useState(false); const [small, setSmall] = useState(true); return (
{/* label feeds aria-label — always name your switch */} {/* track texts come from the localized messages (ON/OFF); override per instance, empty string hides them */}
); } ``` #### Radio Group ```ts 'use client'; import { useState } from 'react'; import { OgeRadioGroup } from '@oge-ui/react-inputs'; const plans = [ { id: 'starter', name: 'Starter' }, { id: 'team', name: 'Team' }, { id: 'scale', name: 'Scale (sold out)', soldOut: true }, { id: 'enterprise', name: 'Enterprise' }, ]; export function RadioGroupDemo() { const [planId, setPlanId] = useState('team'); const [priority, setPriority] = useState('Normal'); return (
plan: {String(planId ?? 'null')}
); } ``` #### Forms integration ```ts 'use client'; import { useState } from 'react'; import { OgeCheckBox, OgeRadioGroup, OgeSwitch } from '@oge-ui/react-inputs'; const plans = [ { id: 'free', name: 'Free' }, { id: 'pro', name: 'Pro' }, ]; export function ToggleFormDemo() { // The form state lives here — with a form library it would live in the // field object instead. Either way the binding is the same controlled pair. const [model, setModel] = useState<{ terms: boolean; marketing: boolean; plan: unknown; }>({ terms: false, marketing: false, plan: 'free' }); return (
event.preventDefault()}> setModel({ ...model, terms: value === true })} > Accept terms setModel({ ...model, marketing })} /> setModel({ ...model, plan })} />
model: {JSON.stringify(model)}
); } ``` #### Basic usage ```ts 'use client'; import { useState } from 'react'; import { OgeTreeSelect } from '@oge-ui/react-inputs'; const folders = [ { id: 1, parentId: null, name: 'Documents' }, { id: 2, parentId: 1, name: 'Reports' }, { id: 3, parentId: 2, name: 'Q1.pdf' }, ]; export function TreeSelectBasicDemo() { const [folderId, setFolderId] = useState(null); return ( ); } ``` #### Nested data & search ```ts 'use client'; import { useState } from 'react'; import { OgeTreeSelect } from '@oge-ui/react-inputs'; // nested payloads need only itemsExpr const tree = [ { id: 1, name: 'src', children: [{ id: 2, name: 'main.ts' }] }, ]; export function TreeSelectNestedDemo() { const [fileId, setFileId] = useState(null); return ( ); } ``` #### Multiple selection ```ts 'use client'; import { useState } from 'react'; import { OgeTreeSelect } from '@oge-ui/react-inputs'; const folders = [ { id: 1, parentId: null, name: 'Documents' }, { id: 2, parentId: 1, name: 'Reports' }, { id: 3, parentId: 2, name: 'Q1.pdf' }, ]; export function TreeSelectMultipleDemo() { // checking a node cascades to its descendants; 'leavesOnly' reports // just the childless keys, so the value stays the concrete grants const [permissions, setPermissions] = useState([]); return ( console.log(event.keys, event.previousKeys)} /> ); } ``` #### Lazy load on demand ```ts 'use client'; import { useState } from 'react'; import { OgeTreeSelect } from '@oge-ui/react-inputs'; interface Folder { id: number; parentId: number | null; name: string; hasItems?: boolean; } const roots: Folder[] = [ { id: 1, parentId: null, name: 'Server root', hasItems: true }, { id: 2, parentId: null, name: 'readme.txt', hasItems: false }, ]; // called once per node, on first expand — a placeholder row shows meanwhile const loadChildren = (parent: Folder): Promise => new Promise((resolve) => setTimeout( () => resolve([{ id: parent.id * 100, parentId: parent.id, name: 'logs' }]), 700, ), ); export function TreeSelectLazyDemo() { const [remoteId, setRemoteId] = useState(null); return ( ); } ``` #### Standalone validation ```ts 'use client'; import { useState } from 'react'; import { OgeTextBox } from '@oge-ui/react-inputs'; export function StandaloneDemo() { const [username, setUsername] = useState(''); return (
{/* no forms library: drive state via props */} 0 && username.length < 3} errorText="At least 3 characters" errorDisplay="always" />
); } ``` #### Form library integration ```ts 'use client'; import { useState } from 'react'; import { OgeNumberBox, OgeTextBox } from '@oge-ui/react-inputs'; import type { OgeFieldError } from '@oge-ui/react-inputs'; // Whatever produces them — a resolver, a schema, a reducer — the editor // only needs the shared `OgeFieldError` shape. function emailErrors(value: string): OgeFieldError[] { if (value === '') return [{ kind: 'required' }]; return /^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(value) ? [] : [{ kind: 'email' }]; } export function FormLibraryDemo() { const [email, setEmail] = useState(''); const [quantity, setQuantity] = useState(null); const [touched, setTouched] = useState(false); const errors = emailErrors(email); return (
setTouched(true)} hint="required + email" /> status: {errors.length ? 'INVALID' : 'VALID'} · touched: {String(touched)}
); } ``` #### Schema-driven errors ```ts 'use client'; import { useState } from 'react'; import { OgeNumberBox, OgeTextBox } from '@oge-ui/react-inputs'; import type { OgeFieldError } from '@oge-ui/react-inputs'; // One rule set, read by both the messages and the constraint props. const schema = { username: { required: true, minLength: 3 } }; function usernameErrors(value: string): OgeFieldError[] { if (value === '') return [{ kind: 'required' }]; if (value.length < schema.username.minLength) { return [{ kind: 'minLength', message: 'At least 3 characters' }]; } return []; } export function SchemaDemo() { const [username, setUsername] = useState(''); const [age, setAge] = useState(null); const errors = usernameErrors(username); return (
value: {username} · valid: {String(errors.length === 0)}
); } ``` #### Linked fields ```ts 'use client'; import { useState } from 'react'; import { OgeButton, OgeButtonGroup } from '@oge-ui/react-buttons'; import { OgeNumberBox, OgeTextBox } from '@oge-ui/react-inputs'; export function LinkedDemo() { const [invoiceType, setInvoiceType] = useState(['personal']); const [taxId, setTaxId] = useState(''); const [minValue, setMinValue] = useState(0); const [maxValue, setMaxValue] = useState(10); const [lastChange, setLastChange] = useState('Change Max…'); return (
{/* cross-field rules: bind state to state — no callbacks needed */} setInvoiceType(change.selectedKeys)} ariaLabel="Invoice type" > {/* Max takes its lower bound from Min */} // rich change payload: { value, previousValue, event } setLastChange( `last change: ${e.previousValue ?? 'empty'} → ${e.value ?? 'empty'} ` + `(${e.event ? 'user' : 'programmatic'})`, ) } /> {lastChange}
); } ``` #### Async validation indicator ```ts 'use client'; import { useRef, useState } from 'react'; import { OgeTextBox } from '@oge-ui/react-inputs'; export function PendingDemo() { const [apiKey, setApiKey] = useState(''); const [checking, setChecking] = useState(false); const timer = useRef | undefined>(undefined); // `pending` shows a rail spinner; pair it with your async validation const simulateCheck = () => { setChecking(true); clearTimeout(timer.current); timer.current = setTimeout(() => setChecking(false), 900); }; return (
); } ``` ## @oge-ui/react-tabs React tab strip and tab panel: the WAI-ARIA APG tabs pattern with declarative or data-driven tabs, automatic/manual activation, overflow scrolling with an all-tabs menu, closable tabs with async close guards, drag reordering and lazy panel rendering — running the same selection/close/reorder pipelines and the same stylesheet as the Angular tabs package. Docs: https://ogeui.com/components/tabs ### #### Properties _Common — tabs & selection_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `tabs` | `readonly OgeTabDefinition[]` | `—` | Declarative tabs — the React counterpart of the projected `` children: an item plus its `content` and optional `renderHeader`. | | `items` | `readonly OgeTabItem[]` | `—` | Data-driven tabs, rendered after the `tabs` entries. | | `selectedIndex` | `number` | `—` | Index of the selected tab — controlled when provided, so pass `onSelectedIndexChange` with it. `-1` selects none; clamped when tabs are removed. | | `defaultSelectedIndex` | `number` | `0` | Uncontrolled initial selection — the component owns the index from there. Never combine with `selectedIndex`. | | `onSelectedIndexChange` | `(index: number) => void` | `—` | The controlled half of `selectedIndex`; Angular’s `[(selectedIndex)]` model is both halves at once. | | `selectedKey` | `string` | `—` | Key of the selected tab — controlled when provided, reconciled with the index the same way the Angular model is. | | `onSelectedKeyChange` | `(key: string \| undefined) => void` | `—` | The controlled half of `selectedKey`; fires with the key of the newly selected tab. | | `renderTabHeader` | `(context: OgeTabHeaderContext) => ReactNode` | `—` | Shared header renderer for the `items` tabs (icons, rich markup) — the React face of `[ogeTabHeaderTemplate]`. Context: `{ item, index, selected, text }`. A `tabs` entry overrides it with its own `renderHeader`. | | `activation` | `'automatic' \| 'manual'` | `'automatic'` | APG activation: arrows select immediately, or move focus only until Enter/Space commits. | | `disabled` | `boolean` | `false` | Disables the whole component. | | `ariaLabel` | `string` | `—` | Aria label of the tablist. | | `messages` | `Partial` | `—` | Per-instance overrides of the `` messages. | _Common — closing, overflow & order_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `closable` | `boolean` | `false` | Default closability; overridable per tab / per item. Closed tabs are removed by the app in `onTabClosed`. The ✕ is presentational — the keyboard path is Delete/Backspace on the focused tab. | | `showNavButtons` | `'auto' \| 'always' \| 'never'` | `'auto'` | Overflow nav arrows; `auto` shows them only while the strip overflows. | | `showTabListButton` | `boolean` | `false` | Shows the all-tabs overflow menu (the same menu list, with the active tab checked). | | `allowTabReordering` | `boolean` | `false` | Enables drag & drop reordering of tab headers; Escape cancels an in-flight drag. | _Common — appearance_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `stylingMode` | `'primary' \| 'secondary'` | `'primary'` | Visual variant: underline ink (`primary`) or soft pills (`secondary`). | | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Density of the tab strip. | | `tabAlignment` | `'start' \| 'center' \| 'end' \| 'justify' \| 'stretch'` | `'start'` | Distribution of the tabs while they fit: `justify` spreads them to the edges, `stretch` gives every tab an equal share. | | `indicatorFit` | `'tab' \| 'content'` | `'tab'` | Whether the selected-tab indicator spans the whole tab or only its label area. | | `className` | `string` | `—` | Extra class names appended to the host element. | | `style` | `CSSProperties` | `—` | Inline styles applied to the host element. | _Panel rendering (<OgeTabPanel> only)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `tabsPosition` | `'top' \| 'bottom' \| 'start' \| 'end'` | `'top'` | Side the strip sits on — logical values, so RTL flips `start`/`end`. Vertical positions switch the arrow keys to Up/Down. | | `deferRendering` | `boolean` | `true` | Mount a panel's content only when its tab first activates. | | `keepAlive` | `boolean` | `true` | Keep once-rendered panels mounted (hidden) so React state survives switches; `false` unmounts lazy content on deactivation. | | `panelAnimation` | `'none' \| 'fade' \| 'slide'` | `'none'` | Transition played by the incoming panel; `slide` enters from the direction of travel (mirrored in RTL). Duration comes from `--oge-tab-panel-transition` (180ms) and is suppressed under `prefers-reduced-motion`. | | `dynamicHeight` | `boolean` | `false` | Animates the content box between the outgoing and incoming panel heights instead of jumping; async content is tracked with a `ResizeObserver`. | | `renderTabContent` | `(context: { item; index: number }) => ReactNode` | `—` | Panel content for the `items` tabs — the React face of `[ogeTabContentTemplate]`. A `tabs` entry carries its own `content` instead. | #### Methods _Common — imperative handle (via ref)_ | Name | Type | Description | | --- | --- | --- | | `focus()` | `() => void` | Focuses the active tab header (roving-tabindex target). | | `closeTab(target: number \| string)` | `() => void` | Runs the close pipeline (`onTabClosing` → `closeGuard` → `onTabClosed`) for an index or key. | | `scrollToTab(target: number \| string)` | `() => void` | Scrolls the tab at an index or with a key into view. | #### Events _Common — callbacks_ | Name | Type | Description | | --- | --- | --- | | `onSelectionChanging` | `(event: OgeTabSelectionChangingEvent) => void` | Cancelable pre-event of a user-gesture selection change (set `event.cancel = true` to keep the current tab). Programmatic prop writes bypass it. | | `onSelectionChanged` | `(event: OgeTabSelectionChangedEvent) => void` | After the selection committed — `index/key/previousIndex/previousKey/item/event`. | | `onTabClick` | `(event: OgeTabClickEvent) => void` | A tab header was activated by pointer or keyboard (Enter/Space). | | `onTabClosing` | `(event: OgeTabClosingEvent) => void` | Cancelable pre-event of a close, before the async `closeGuard` runs. | | `onTabClosed` | `(event: OgeTabClosedEvent) => void` | The close passed all guards — drop the `items` / `tabs` entry from your state here. | | `onTabReordering` | `(event: OgeTabReorderingEvent) => void` | Cancelable pre-event of a drag-reorder drop (`fromIndex/toIndex/key`). | | `onTabReordered` | `(event: OgeTabReorderedEvent) => void` | A drag reorder committed to the display order. | #### Types | Name | Type | Description | | --- | --- | --- | | `OgeTabPanelProps` | `interface` | Extends `OgeTabsSharedProps` with the panel-rendering props above. | | `OgeTabsHandle` | `{ focus(); closeTab(target); scrollToTab(target) }` | Imperative handle exposed through `ref`. | ### #### Properties _Strip (<OgeTabs> only)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Strip direction; `vertical` renders a column, maps arrows to Up/Down and sets `aria-orientation`. | _Common — tabs & selection_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `tabs` | `readonly OgeTabDefinition[]` | `—` | Declarative tabs — the React counterpart of the projected `` children: an item plus its `content` and optional `renderHeader`. | | `items` | `readonly OgeTabItem[]` | `—` | Data-driven tabs, rendered after the `tabs` entries. | | `selectedIndex` | `number` | `—` | Index of the selected tab — controlled when provided, so pass `onSelectedIndexChange` with it. `-1` selects none; clamped when tabs are removed. | | `defaultSelectedIndex` | `number` | `0` | Uncontrolled initial selection — the component owns the index from there. Never combine with `selectedIndex`. | | `onSelectedIndexChange` | `(index: number) => void` | `—` | The controlled half of `selectedIndex`; Angular’s `[(selectedIndex)]` model is both halves at once. | | `selectedKey` | `string` | `—` | Key of the selected tab — controlled when provided, reconciled with the index the same way the Angular model is. | | `onSelectedKeyChange` | `(key: string \| undefined) => void` | `—` | The controlled half of `selectedKey`; fires with the key of the newly selected tab. | | `renderTabHeader` | `(context: OgeTabHeaderContext) => ReactNode` | `—` | Shared header renderer for the `items` tabs (icons, rich markup) — the React face of `[ogeTabHeaderTemplate]`. Context: `{ item, index, selected, text }`. A `tabs` entry overrides it with its own `renderHeader`. | | `activation` | `'automatic' \| 'manual'` | `'automatic'` | APG activation: arrows select immediately, or move focus only until Enter/Space commits. | | `disabled` | `boolean` | `false` | Disables the whole component. | | `ariaLabel` | `string` | `—` | Aria label of the tablist. | | `messages` | `Partial` | `—` | Per-instance overrides of the `` messages. | _Common — closing, overflow & order_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `closable` | `boolean` | `false` | Default closability; overridable per tab / per item. Closed tabs are removed by the app in `onTabClosed`. The ✕ is presentational — the keyboard path is Delete/Backspace on the focused tab. | | `showNavButtons` | `'auto' \| 'always' \| 'never'` | `'auto'` | Overflow nav arrows; `auto` shows them only while the strip overflows. | | `showTabListButton` | `boolean` | `false` | Shows the all-tabs overflow menu (the same menu list, with the active tab checked). | | `allowTabReordering` | `boolean` | `false` | Enables drag & drop reordering of tab headers; Escape cancels an in-flight drag. | _Common — appearance_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `stylingMode` | `'primary' \| 'secondary'` | `'primary'` | Visual variant: underline ink (`primary`) or soft pills (`secondary`). | | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Density of the tab strip. | | `tabAlignment` | `'start' \| 'center' \| 'end' \| 'justify' \| 'stretch'` | `'start'` | Distribution of the tabs while they fit: `justify` spreads them to the edges, `stretch` gives every tab an equal share. | | `indicatorFit` | `'tab' \| 'content'` | `'tab'` | Whether the selected-tab indicator spans the whole tab or only its label area. | | `className` | `string` | `—` | Extra class names appended to the host element. | | `style` | `CSSProperties` | `—` | Inline styles applied to the host element. | #### Methods _Common — imperative handle (via ref)_ | Name | Type | Description | | --- | --- | --- | | `focus()` | `() => void` | Focuses the active tab header (roving-tabindex target). | | `closeTab(target: number \| string)` | `() => void` | Runs the close pipeline (`onTabClosing` → `closeGuard` → `onTabClosed`) for an index or key. | | `scrollToTab(target: number \| string)` | `() => void` | Scrolls the tab at an index or with a key into view. | #### Events _Common — callbacks_ | Name | Type | Description | | --- | --- | --- | | `onSelectionChanging` | `(event: OgeTabSelectionChangingEvent) => void` | Cancelable pre-event of a user-gesture selection change (set `event.cancel = true` to keep the current tab). Programmatic prop writes bypass it. | | `onSelectionChanged` | `(event: OgeTabSelectionChangedEvent) => void` | After the selection committed — `index/key/previousIndex/previousKey/item/event`. | | `onTabClick` | `(event: OgeTabClickEvent) => void` | A tab header was activated by pointer or keyboard (Enter/Space). | | `onTabClosing` | `(event: OgeTabClosingEvent) => void` | Cancelable pre-event of a close, before the async `closeGuard` runs. | | `onTabClosed` | `(event: OgeTabClosedEvent) => void` | The close passed all guards — drop the `items` / `tabs` entry from your state here. | | `onTabReordering` | `(event: OgeTabReorderingEvent) => void` | Cancelable pre-event of a drag-reorder drop (`fromIndex/toIndex/key`). | | `onTabReordered` | `(event: OgeTabReorderedEvent) => void` | A drag reorder committed to the display order. | #### Types | Name | Type | Description | | --- | --- | --- | | `OgeTabsProps` | `interface` | Extends `OgeTabsSharedProps` with `orientation`. | | `OgeTabsHandle` | `{ focus(); closeTab(target); scrollToTab(target) }` | Imperative handle exposed through `ref`. | ### OgeTab (OgeTabDefinition) #### Properties | Name | Type | Default | Description | | --- | --- | --- | --- | | `text` | `string` | `—` | Tab label; alternative to a per-tab `renderHeader`. | | `key` | `string` | `—` | Stable identity used by `selectedKey`, reorder tracking and DOM ids. | | `disabled` | `boolean` | `false` | Disabled tabs are skipped by keyboard navigation and selection. | | `visible` | `boolean` | `true` | `false` removes the tab (and its panel) entirely. | | `closable` | `boolean` | `—` | Shows a close button; omitted, it falls back to the component-level `closable`. | | `badge` | `string \| number` | `—` | Badge rendered after the label. | | `dirty` | `boolean` | `false` | Renders the unsaved-changes dot and announces it to screen readers (`messages.dirty`). | | `hint` | `string` | `—` | Tooltip — rendered as the native `title` attribute. | | `closeGuard` | `() => boolean \| Promise` | `—` | Veto hook run before this tab closes; may be async (single-flight, rejection = veto, pending spinner on the ✕). | | `content` | `ReactNode` | `—` | Panel content rendered while the tab is displayed — the React face of the content projected into an ``. | | `renderHeader` | `(context: OgeTabHeaderContext) => ReactNode` | `—` | Custom header for this tab alone — the React face of an `[ogeTabHeaderTemplate]` inside an ``. | #### Types _Render props_ | Name | Type | Description | | --- | --- | --- | | `OgeTabHeaderContext` | `{ item: OgeTabItem \| undefined; index: number; selected: boolean; text: string }` | Context handed to `renderHeader` / `renderTabHeader` — the React face of `OgeTabHeaderTemplateContext`. `item` is `undefined` for `tabs`-declared entries. | | `renderTabContent context` | `{ item: OgeTabItem; index: number }` | Context handed to ``’s `renderTabContent` — the React face of `OgeTabContentTemplateContext`. Lazy by default: it runs on first activation, honoring `deferRendering` / `keepAlive`. | ### Tabs configuration #### Properties _OgeTabsConfig_ | Name | Type | Description | | --- | --- | --- | | `messages` | `OgeTabsMessages` | Every user-facing string: `closeTab`, `scrollBackward`, `scrollForward`, `tabListMenu`, `dirty`, `noData`. | #### Methods | Name | Type | Description | | --- | --- | --- | | `OgeTabsConfigProvider` | `(props: { config?: OgeTabsConfigInput; children?: ReactNode }) => JSX.Element` | Wrap a subtree to change the tabs’ user-facing strings beneath it — the React counterpart of Angular’s `provideOgeTabsConfig()`. Both shallow-merge `messages` over the same `@oge-ui/behavior` defaults, so an override reads identically in either layer. | | `useOgeTabsConfig()` | `() => OgeTabsConfig` | Reads the resolved config of the current subtree — how a strip of your own picks up the same messages. | #### Types | Name | Type | Description | | --- | --- | --- | | `OgeTabItem` | `{ key?, text?, badge?, hint?, disabled?, visible?, closable?, dirty?, closeGuard? }` | One data-driven tab of the `items` prop. | | `OgeTabDefinition` | `OgeTabItem & { content?, renderHeader? }` | One declarative tab of the `tabs` prop — an item plus its panel content. | | `OgeTabCloseGuard` | `() => boolean \| Promise` | Per-tab veto hook; resolving `false` (or rejecting) keeps the tab open. | | `OgeTabsActivation` | `'automatic' \| 'manual'` | How keyboard focus interacts with selection (APG). | | `OgeTabsPosition` | `'top' \| 'bottom' \| 'start' \| 'end'` | Logical strip placement of ``. | | `OgeTabsOrientation` | `'horizontal' \| 'vertical'` | Direction of a stand-alone `` strip. | | `OgeTabsNavButtonsMode` | `'auto' \| 'always' \| 'never'` | When the overflow nav arrows are shown. | | `OgeTabsAlignment` | `'start' \| 'center' \| 'end' \| 'justify' \| 'stretch'` | How tabs are distributed along the strip. | | `OgeTabsIndicatorFit` | `'tab' \| 'content'` | Width of the selected-tab indicator. | | `OgeTabPanelAnimation` | `'none' \| 'fade' \| 'slide'` | Transition played by the newly displayed panel. | | `OgeTabsStylingMode / OgeTabsSize` | `'primary' \| 'secondary' / 'sm' \| 'md' \| 'lg'` | Visual variant and density unions. | ### Demos Each demo below is a complete standalone component — copy one whole and it compiles. #### Declarative tabs ```ts 'use client'; import { useState } from 'react'; import { OgeTabPanel } from '@oge-ui/react-tabs'; import type { OgeTabSelectionChangedEvent } from '@oge-ui/react-tabs'; export function DeclarativeTabsDemo() { const [index, setIndex] = useState(0); const [lastChange, setLastChange] = useState(null); return ( <> Project overview — selected index: {index}

}, { text: 'Activity', content:

Latest activity feed…

}, { text: 'Settings', disabled: true, content:

Settings…

}, ]} /> {lastChange && (

selectionChanged → index {lastChange.index} (from {lastChange.previousIndex})

)} ); } ``` #### Data-driven items ```ts 'use client'; import { useState } from 'react'; import { OgeTabPanel } from '@oge-ui/react-tabs'; import type { OgeTabItem } from '@oge-ui/react-tabs'; const docs: OgeTabItem[] = [ { key: 'readme', text: 'README.md' }, { key: 'spec', text: 'spec.ts', badge: 3 }, { key: 'draft', text: 'draft.md', dirty: true }, ]; export function ItemsDemo() { const [activeDoc, setActiveDoc] = useState('readme'); return ( (

Editing {item.text} — selectedKey: {activeDoc}

)} /> ); } ``` #### Lazy rendering & keep-alive ```ts 'use client'; import { useState } from 'react'; import { OgeTabPanel } from '@oge-ui/react-tabs'; /** Stamps its creation time — makes lazy/keep-alive behavior visible. */ function CreatedAt() { const [createdAt] = useState(() => new Date().toLocaleTimeString()); return created at {createdAt}; } export function LazyDemo() { const [keepAlive, setKeepAlive] = useState(true); return ( <> }, { text: 'Second', content: }, ]} /> ); } ``` #### Closable tabs & async close guard ```ts 'use client'; import { useRef, useState } from 'react'; import { OgeTabPanel } from '@oge-ui/react-tabs'; import type { OgeTabItem } from '@oge-ui/react-tabs'; export function ClosableDemo() { const [notice, setNotice] = useState(''); const armedUntil = useRef(0); /** First attempt arms a 3s window; a second attempt inside it allows. */ const confirmDiscard = (): Promise => { const now = Date.now(); if (now < armedUntil.current) { armedUntil.current = 0; return Promise.resolve(true); } armedUntil.current = now + 3000; return new Promise((resolve) => setTimeout(() => { setNotice('closeGuard vetoed — close again within 3s to discard changes'); resolve(false); }, 600), ); }; const buildFiles = (): OgeTabItem[] => [ { key: 'a.ts', text: 'a.ts' }, { key: 'b.ts', text: 'b.ts (guarded)', dirty: true, closeGuard: confirmDiscard }, { key: 'c.ts', text: 'c.ts' }, ]; const [files, setFiles] = useState(buildFiles); return ( <> { setFiles((current) => current.filter((file) => file.key !== event.key)); setNotice(''); }} renderTabContent={({ item }) =>

{item.text} content…

} />
{notice && {notice}}
); } ``` #### Overflow: arrows & all-tabs menu ```ts 'use client'; import { useState } from 'react'; import { OgeTabs } from '@oge-ui/react-tabs'; import type { OgeTabItem } from '@oge-ui/react-tabs'; const manyTabs: OgeTabItem[] = Array.from({ length: 14 }, (_, i) => ({ key: `ch${i + 1}`, text: `Chapter ${i + 1}`, disabled: i === 5, })); export function OverflowDemo() { const [index, setIndex] = useState(0); return ( <>

selected: {manyTabs[index].text}

); } ``` #### Drag reorder ```ts 'use client'; import { OgeTabPanel } from '@oge-ui/react-tabs'; import type { OgeTabItem } from '@oge-ui/react-tabs'; const stages: OgeTabItem[] = [ { key: 'todo', text: 'To do' }, { key: 'doing', text: 'In progress' }, { key: 'review', text: 'Review' }, { key: 'done', text: 'Done' }, ]; export function ReorderDemo() { return ( console.log(event.fromIndex, '→', event.toIndex)} renderTabContent={({ item }) =>

{item.text} stage…

} /> ); } ``` #### Positions & styling ```ts 'use client'; import { OgeTabPanel } from '@oge-ui/react-tabs'; export function PositionDemo() { return ( General project settings…

}, { text: 'Members', content:

Member management…

}, { text: 'Danger zone', content:

Careful now…

}, ]} /> ); } ``` #### Alignment, indicator & empty state ```ts 'use client'; import { useState } from 'react'; import { OgeTabs } from '@oge-ui/react-tabs'; import type { OgeTabItem, OgeTabsAlignment } from '@oge-ui/react-tabs'; const stages: OgeTabItem[] = [ { key: 'todo', text: 'To do' }, { key: 'doing', text: 'In progress' }, { key: 'review', text: 'Review' }, { key: 'done', text: 'Done' }, ]; const alignments: OgeTabsAlignment[] = [ 'start', 'center', 'end', 'justify', 'stretch', ]; export function AlignmentDemo() { const [alignment, setAlignment] = useState('start'); const [fitIndicator, setFitIndicator] = useState(false); const [index, setIndex] = useState(0); return ( <>

Empty strip:

); } ``` #### Panel transitions ```ts 'use client'; import { useState } from 'react'; import { OgeTabPanel } from '@oge-ui/react-tabs'; import type { OgeTabPanelAnimation } from '@oge-ui/react-tabs'; const animations: OgeTabPanelAnimation[] = ['none', 'fade', 'slide']; const tallLines = [ 'Tab panels can differ a lot in height.', 'Without dynamicHeight the page jumps as you switch.', 'With it, the content box animates between the two heights.', 'The transition honours prefers-reduced-motion.', 'And async content is picked up by a ResizeObserver.', ]; export function AnimationDemo() { const [panelAnimation, setPanelAnimation] = useState('slide'); const [dynamicHeight, setDynamicHeight] = useState(true); const [index, setIndex] = useState(0); return ( <>
One line of content.

}, { text: 'Medium', content: ( <>

A few more lines.

So the panel is noticeably taller than the first one.

), }, { text: 'Tall', content: ( <> {tallLines.map((line) => (

{line}

))} ), }, ]} /> ); } ``` ## @oge-ui/react-layout React layout containers and loading visuals: card, accordion with async expand guards and lazy content, splitter with the APG window-splitter keyboard, toolbar with an overflow menu, plus the progress bar, load indicator and shimmer skeleton — running the same config defaults, decision functions and stylesheet as the Angular layout package. Docs: https://ogeui.com/components/accordion ### #### Properties _Panels & expansion_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `items` | `readonly OgeAccordionItemDefinition[]` | `—` | The panels, in render order — the React counterpart of both the projected `` children and the Angular `items` array: the shared data fields plus `content` and the per-panel render props. | | `expandedKeys` | `readonly string[]` | `—` | Keys of the expanded panels — controlled when provided, so pass `onExpandedKeysChange` with it. The multi-expand counterpart of `selectedIndex`; only panels that declare a `key` can appear here. | | `defaultExpandedKeys` | `readonly string[]` | `—` | Uncontrolled initial expansion by key — the component owns the set from there. Never combine with `expandedKeys`. | | `onExpandedKeysChange` | `(keys: readonly string[]) => void` | `—` | The controlled half of `expandedKeys`; Angular’s `[(expandedKeys)]` model is both halves at once. | | `selectedIndex` | `number` | `—` | Index of the expanded panel in single-expand mode — controlled when provided. `-1` means none; in `multiple` mode it reports the first expanded panel. | | `defaultSelectedIndex` | `number` | `-1` | Uncontrolled initial expansion by index. Never combine with `selectedIndex`. | | `onSelectedIndexChange` | `(index: number) => void` | `—` | The controlled half of `selectedIndex`; Angular’s `[(selectedIndex)]` model is both halves at once. | | `multiple` | `boolean` | `false` | Allows more than one panel to stay expanded. | | `collapsible` | `boolean` | `false` | Allows collapsing the last expanded panel, leaving none open. While `false`, that header is `aria-disabled` per the APG — it stays focusable. | | `disabled` | `boolean` | `false` | Disables the whole component. | _Rendering & animation_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `deferRendering` | `boolean` | `true` | Mount a panel's `renderContent` body only when it first expands. | | `keepAlive` | `boolean` | `true` | Keep once-rendered panels mounted (hidden) so their React state survives a collapse. Ignored while `deferRendering` is `false`. | | `animation` | `boolean \| number` | `true` | Height animation: `true` uses the default duration, a number overrides it in milliseconds, `false` disables it. Always suppressed under `prefers-reduced-motion`. | | `renderHeader` | `(context: OgeAccordionHeaderContext) => ReactNode` | `—` | Shared header renderer — the React face of the component-level `[ogeAccordionHeaderTemplate]`. Overridden by a panel’s own `renderHeader`. Must not contain focusable controls. | | `renderContent` | `(context: OgeAccordionContentContext) => ReactNode` | `—` | Shared lazy body for panels that carry no `content` — the React face of `[ogeAccordionContentTemplate]`. `context.data` carries the panel’s `contentLoader` result. | | `renderToggleIcon` | `(context: OgeAccordionToggleIconContext) => ReactNode` | `—` | Replaces the chevron — the React face of `[ogeAccordionToggleIconTemplate]`. | | `renderHeaderActions` | `(context: OgeAccordionHeaderActionsContext) => ReactNode` | `—` | Actions rendered _beside_ the toggle button, never inside it — the React face of `[ogeAccordionHeaderActionsTemplate]`, and the reason there is no `nested-interactive` violation. | _Appearance_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `togglePosition` | `'start' \| 'end'` | `'end'` | Side of the header the chevron sits on — logical, so RTL mirrors it. | | `hideToggle` | `boolean` | `false` | Hides the chevron entirely. Overridable per panel via an item’s `hideToggle`. | | `collapsedHeaderHeight` | `string` | `—` | Minimum height of a collapsed header (any CSS length). Unset lets `size` and the padding tokens decide. | | `expandedHeaderHeight` | `string` | `—` | Minimum height of an expanded header; falls back to `collapsedHeaderHeight`. | | `displayMode` | `'default' \| 'flat'` | `'default'` | `flat` removes the gutters between panels and joins them into one stack. | | `stylingMode` | `'outlined' \| 'filled' \| 'flat'` | `'outlined'` | Visual variant of the panels. | | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Density of the header rows. | | `className` | `string` | `—` | Extra class names appended to the host element. | | `style` | `CSSProperties` | `—` | Inline styles applied to the host element. | _Keyboard & accessibility_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `keyboardNavigation` | `boolean` | `true` | Enables Up/Down/Home/End and Ctrl+PageUp/PageDown header navigation. The APG pattern itself requires only Enter/Space and Tab — this is the optional enhancement. | | `typeAhead` | `boolean` | `true` | Enables printable-character type-ahead over the panel titles. Matching is accent- and locale-insensitive. | | `selectOnFocus` | `boolean` | `false` | Expands a panel as soon as keyboard navigation moves focus onto it. | | `headingLevel` | `number` | `3` | `aria-level` of the heading wrapping each header button; 1–6 render a real `h1`–`h6`. | | `useRegionRole` | `boolean` | `true` | Gives each panel `role="region"` (APG-optional; adds one landmark per panel). | | `ariaLabel` | `string` | `—` | Aria label of the accordion container. | | `messages` | `Partial` | `—` | Per-instance overrides of the `` messages. | #### Methods _Imperative handle (via ref)_ | Name | Type | Description | | --- | --- | --- | | `expand(target)` | `(target: number \| string) => Promise` | Runs the expand pipeline for the panel at an index or with a key. Resolves `true` once it expanded, `false` if an unknown target, `onItemExpanding` or the `expandGuard` vetoed it. | | `collapse(target)` | `(target: number \| string) => Promise` | Runs the collapse pipeline; resolves whether the panel actually collapsed. | | `toggle(target)` | `(target: number \| string) => Promise` | Expands the panel if collapsed, collapses it otherwise. | | `expandAll()` | `() => void` | Expands every enabled panel. Requires `multiple` — otherwise it warns in dev mode and does nothing. | | `collapseAll()` | `() => void` | Collapses every panel. In single-expand mode the last panel stays open unless `collapsible` is set. | | `expandInvalid()` | `() => void` | Expands every panel flagged `invalid` — call it after a failed form submit so the user sees each section needing attention. | | `isExpanded(target)` | `(target: number \| string) => boolean` | Whether the panel at an index or with a key is currently expanded. | | `focus(target?)` | `(target?: number \| string) => void` | Focuses a panel's header button, or the first enabled one. | #### Events _Callbacks_ | Name | Type | Description | | --- | --- | --- | | `onItemExpanding` | `(event: OgeAccordionExpandingEvent) => void` | Cancelable pre-event of a panel expanding — set `event.cancel = true` to block it. Runs before the panel’s `expandGuard`. | | `onItemExpanded` | `(event: OgeAccordionExpandedEvent) => void` | Fires after a panel expanded. | | `onItemCollapsing` | `(event: OgeAccordionCollapsingEvent) => void` | Cancelable pre-event of a panel collapsing — set `event.cancel = true` to block it. | | `onItemCollapsed` | `(event: OgeAccordionCollapsedEvent) => void` | Fires after a panel collapsed. | | `onAfterExpand` | `(event: OgeAccordionExpandedEvent) => void` | Fires once the expand animation finished — the point at which the panel has its final height. Fires immediately when the animation is off or suppressed by `prefers-reduced-motion`. | | `onAfterCollapse` | `(event: OgeAccordionCollapsedEvent) => void` | Fires once the collapse animation finished. | | `onItemClick` | `(event: OgeAccordionItemClickEvent) => void` | Fires when a header button is activated, before the expand pipeline runs. Fires for disabled panels too. | | `onItemContentLoaded` | `(event: OgeAccordionContentLoadedEvent) => void` | Fires after a panel's `contentLoader` resolved. | | `onItemContentFailed` | `(event: OgeAccordionContentFailedEvent) => void` | Fires after a panel's `contentLoader` rejected. | #### Types _Types_ | Name | Type | Description | | --- | --- | --- | | `OgeAccordionProps` | `interface` | Extends `OgeAccordionBehaviorProps` with `className` and `style`. | | `OgeAccordionHandle` | `{ expand(); collapse(); toggle(); expandAll(); collapseAll(); expandInvalid(); isExpanded(); focus() }` | Imperative handle exposed through `ref`. | | `OgeAccordionItemData` | `interface` | The framework-free panel fields shared with Angular: `key`, `title`, `text`, `description`, `icon`, `badge`, `hint`, `disabled`, `visible`, `expanded`, `invalid`, `hideToggle`, `togglePosition`, `expandGuard`, `contentLoader`. | | `OgeAccordionExpandGuard` | `() => boolean \| Promise` | Veto for a pending expand or collapse. `false` blocks it; throwing or rejecting is also a veto. While a promise is pending the panel shows a spinner and ignores further toggles (single-flight). | | `OgeAccordionContentLoader` | `() => Promise` | Loads a panel's content the first time it expands. The resolved value reaches `renderContent` as `data`. | | `OgeAccordionTogglePosition` | `'start' \| 'end'` | Chevron side inside the header button. | | `OgeAccordionDisplayMode` | `'default' \| 'flat'` | Gutters between panels, or one joined stack. | | `OgeAccordionStylingMode` | `'outlined' \| 'filled' \| 'flat'` | Visual variant of the panels. | | `OgeAccordionSize` | `'sm' \| 'md' \| 'lg'` | Density of the header rows. | | `useOgeAccordion(props)` | `(props: OgeAccordionBehaviorProps) => accordion state` | The headless hook behind the component — descriptors, the expanded/pending/rendered sets and the expand/collapse pipelines, for a stack you render yourself. | ### OgeAccordionItem (OgeAccordionItemDefinition) #### Properties | Name | Type | Default | Description | | --- | --- | --- | --- | | `title` | `string` | `—` | Header title; alternative to a per-panel `renderHeader`. | | `text` | `string` | `—` | Plain-text panel body, rendered when there is neither `content` nor a `renderContent`. The reference `html` item field has no counterpart on purpose. | | `description` | `string` | `—` | Secondary line rendered under the title. | | `key` | `string` | `—` | Stable identity used by `expandedKeys`, the handle’s targets and DOM ids. | | `icon` | `string` | `—` | SVG path data (`d`) rendered as a 24×24 aria-hidden icon before the title. | | `badge` | `string \| number` | `—` | Badge rendered after the title. | | `hint` | `string` | `—` | Tooltip — rendered as the native `title` attribute. | | `disabled` | `boolean` | `false` | Disabled panels cannot expand and are skipped by keyboard navigation. | | `visible` | `boolean` | `true` | `false` removes the panel entirely. | | `expanded` | `boolean` | `false` | Expands this panel on first render — the seed only. React drives further state through the controlled `expandedKeys` / `selectedIndex` pairs or the `ref` handle, where Angular offers a per-panel **two-way** `[(expanded)]`. | | `hideToggle` | `boolean` | `—` | Overrides the accordion's `hideToggle` for this panel. | | `togglePosition` | `'start' \| 'end'` | `—` | Overrides the accordion's `togglePosition` for this panel. | | `invalid` | `boolean` | `false` | Flags the section as failing validation — renders the danger rail and feeds `expandInvalid()`. | | `expandGuard` | `OgeAccordionExpandGuard` | `—` | Veto hook run before this panel expands or collapses; may be async (single-flight). | | `contentLoader` | `OgeAccordionContentLoader` | `—` | Loads this panel's content on first expand, with a skeleton while pending and a retry button on failure. | | `content` | `ReactNode` | `—` | Panel body — the React face of the content projected into an ``. Eager: a panel that should be lazy uses `renderContent` instead. | | `renderHeader` | `(context: OgeAccordionHeaderContext) => ReactNode` | `—` | Custom header for this panel alone — the React face of an `[ogeAccordionHeaderTemplate]` inside an ``. | | `renderContent` | `(context: OgeAccordionContentContext) => ReactNode` | `—` | Lazy body for this panel alone — the React face of an inline `[ogeAccordionContentTemplate]`. Ignored when `content` is set. | | `renderToggleIcon` | `(context: OgeAccordionToggleIconContext) => ReactNode` | `—` | Custom chevron for this panel alone. | | `renderHeaderActions` | `(context: OgeAccordionHeaderActionsContext) => ReactNode` | `—` | Actions for this panel, rendered beside the toggle button. | #### Types _Render-prop contexts & slots_ | Name | Type | Description | | --- | --- | --- | | `OgeAccordionHeaderContext` | `{ item, index, expanded, title, description }` | Context handed to `renderHeader` — the React face of the `[ogeAccordionHeaderTemplate]` context. | | `OgeAccordionContentContext` | `{ item, index, data }` | Context handed to `renderContent`. `data` carries the panel's `contentLoader` result. | | `OgeAccordionToggleIconContext` | `{ expanded, index }` | Context handed to `renderToggleIcon`. | | `OgeAccordionHeaderActionsContext` | `{ item, index, expanded }` | Context handed to `renderHeaderActions`. | | `.oge-accordion-action-row` | `class name` | A row of buttons at the end of a panel body marked with this class becomes its action bar (divider above, actions at the inline end) — the React face of the `[ogeAccordionActionRow]` directive, which does nothing else but apply it. Inside the panel, so only reachable while expanded. | | `OgeAccordionItemDefinition` | `OgeAccordionItemData & { content?, renderHeader?, renderContent?, renderToggleIcon?, renderHeaderActions? }` | One entry of the `items` prop — the shared panel data plus the React content slots. | ### Accordion configuration #### Properties _OgeAccordionMessages_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `invalidSection` | `string` | `'section has errors'` | Announced after the title of a panel flagged `invalid`. | | `pending` | `string` | `'working'` | Announced while an `expandGuard` promise is in flight. | | `loadingContent` | `string` | `'Loading…'` | Shown while a panel's `contentLoader` is running. | | `contentLoadFailed` | `string` | `'Could not load this section.'` | Shown when a panel's `contentLoader` rejected. | | `retry` | `string` | `'Retry'` | Label of the retry button on a failed content load. | | `noData` | `string` | `'No sections to display'` | Shown in place of the panels when there are no visible items. | #### Types _Behavioural defaults_ | Name | Type | Description | | --- | --- | --- | | `hideToggle` | `boolean \| undefined` | Default for the `hideToggle` prop. | | `collapsedHeaderHeight` | `string \| undefined` | Default for the `collapsedHeaderHeight` prop. | | `expandedHeaderHeight` | `string \| undefined` | Default for the `expandedHeaderHeight` prop. | | Name | Type | Description | | --- | --- | --- | | `OgeAccordionConfigProvider` | `(props: { config?: OgeAccordionConfigInput; children?: ReactNode }) => JSX.Element` | Wrap a subtree to change the accordion’s defaults and user-facing strings beneath it — the React counterpart of Angular’s `provideOgeAccordionConfig()`. Both shallow-merge `messages` over the same `@oge-ui/behavior` defaults, so an override reads identically in either layer. | | `useOgeAccordionConfig()` | `() => OgeAccordionConfig` | Reads the resolved config of the current subtree — the counterpart of injecting `OGE_ACCORDION_CONFIG`. | | `OGE_DEFAULT_ACCORDION_CONFIG` | `OgeAccordionConfig` | The built-in defaults themselves, re-exported from `@oge-ui/behavior` so both layers start from one object. | ### #### Properties | Name | Type | Default | Description | | --- | --- | --- | --- | | `header` | `string \| undefined` | `—` | Header title. Named after the PrimeNG input rather than `title` — a static `title` attribute would double as a native tooltip. | | `subheader` | `string \| undefined` | `—` | Line rendered under `header` in the muted color. | | `stylingMode` | `'outlined' \| 'raised' \| 'filled' \| 'flat'` | `'outlined'` | Chrome preset: `outlined` (border), `raised` (rests on the `--oge-shadow-card` token), `filled` (tinted surface) or `flat` (no chrome — for a card nested in another surface). Falls back to ``. | | `orientation` | `'vertical' \| 'horizontal'` | `'vertical'` | `horizontal` turns the card into a two-column grid with the `media` node spanning the inline-start column, sized by `--oge-card-media-size`. Falls back to the config provider. | | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Density preset — scales the section padding and type ramp together (`--oge-card-pad` is the per-card escape hatch). Falls back to the config provider. | | `severity` | `'accent' \| 'success' \| 'warning' \| 'danger' \| undefined` | `undefined` | Colored status rail on the inline-start edge — the toast’s rail idiom on a static surface. `undefined` renders no rail. | | `interactive` | `boolean` | `false` | Purely visual affordance for the documented clickable-card pattern: a hover/focus-within lift and a keyboard focus ring on the surface. Adds **no** role, tabindex or wrapper — pair it with one primary `` in the content. | | `loading` | `boolean` | `false` | Replaces the content and action row with a shimmer skeleton and marks the card `aria-busy`. Header, media and footer stay, so the card keeps its footprint while the data arrives. | | `children` | `ReactNode` | `—` | The card content — the React counterpart of the default `` projection. | | `className / style` | `string \| CSSProperties` | `—` | Merged onto the card host. `className` is appended to the generated `oge-card*` classes; the Angular host takes `class`/`style` natively. | _Accessibility contract_ | Name | Type | Description | | --- | --- | --- | | `(no role, no clickable prop)` | `—` | There is no WAI-ARIA card pattern, so the card renders no role and no `tabIndex`, and ships no clickable-card API — wrapping the card in a link or button is the `nested-interactive` trap. Add `role="article"` / `role="region"` yourself, and make a card clickable with one primary `` in the content plus a CSS-stretched hit area. | #### Types | Name | Type | Description | | --- | --- | --- | | `OgeCardStylingMode` | `'outlined' \| 'raised' \| 'filled' \| 'flat'` | Chrome preset union — the layout family’s `stylingMode` vocabulary plus Material’s `raised`. | | `OgeCardOrientation` | `'vertical' \| 'horizontal'` | Section flow union. | | `OgeCardSize` | `'sm' \| 'md' \| 'lg'` | Density preset union. | | `OgeCardSeverity` | `'accent' \| 'success' \| 'warning' \| 'danger'` | Status rail union for the `severity` prop. | | `OgeCardActionsAlign` | `'start' \| 'center' \| 'end' \| 'stretched'` | Justification vocabulary of the action row. The React `actions` node is plain markup, so this types your own state and maps to the `oge-card-actions-*` class. | | `OgeCardProps` | `interface` | Props of ``, including the slot nodes. | | `OgeCardConfig / OgeCardConfigInput` | `{ stylingMode?; orientation?; size? }` | The config shape and its partial input for ``; `OGE_DEFAULT_CARD_CONFIG` is the resolved default. | ### Slot props #### Properties | Name | Type | Description | | --- | --- | --- | | `media` | `ReactNode` | The full-bleed media element (`[ogeCardMedia]` in Angular) — an ``, `` or a wrapper carrying `className="oge-card-media"`. Sized by consumer CSS (`aspect-ratio`, `block-size`); there is deliberately no size prop. | | `avatar` | `ReactNode` | The round image before the header titles (`[ogeCardAvatar]`), rendered with `className="oge-card-avatar"`. | | `headerActions` | `ReactNode` | Controls at the inline end of the header row (`[ogeCardHeaderActions]`), in a node with `className="oge-card-header-actions"`. Real controls in the Tab sequence — the card never wraps them in anything interactive. | | `actions` | `ReactNode` | The action row under the content (`[ogeCardActions]`). Give it `className="oge-card-actions"` plus one of `oge-card-actions-center` / `-end` / `-stretched` — the alignment the Angular directive takes as its `align` input. | | `footer` | `ReactNode` | A divided strip on the header surface after the actions (`[ogeCardFooter]`, `className="oge-card-footer"`) — metadata rather than commands. | | `oge-card-separator` | `class` | A full-bleed hairline between content sections — put the class on an `` inside `children`. | ### Card configuration #### Properties | Name | Type | Description | | --- | --- | --- | | `OgeCardConfigProvider` | `(props: { config?: OgeCardConfigInput; children?: ReactNode }) => JSX.Element` | Subtree defaults for `stylingMode`, `orientation` and `size` — the React counterpart of `provideOgeCardConfig()`. There is deliberately no `messages` block: the card renders no user-facing strings and no interactive chrome of its own. | | `useOgeCardConfig()` | `() => OgeCardConfig` | Reads the resolved config of the nearest provider, merged over `OGE_DEFAULT_CARD_CONFIG` — the hook behind the component, exported for cards you compose yourself. | ### #### Properties | Name | Type | Default | Description | | --- | --- | --- | --- | | `value` | `number \| null` | `null` | Current value; `null` renders the **indeterminate** sliding bar — and `aria-valuenow` is then omitted entirely (the ARIA rule), never pinned to a sentinel. | | `min / max` | `number` | `0 / 100` | Scale bounds; the fill ratio clamps into them. | | `bufferValue` | `number \| undefined` | `—` | Material's buffer layer — a soft second fill behind the primary one (media pre-loading behind the play position). | | `chunkCount` | `number \| undefined` | `—` | Renders the bar as N discrete segments (Kendo's chunk progress bar); the filled count is the rounded ratio. | | `severity` | `'accent' \| 'success' \| 'warning' \| 'danger'` | `'accent'` | Fill color — the card/toast severity vocabulary; recolors the fill only. Falls back to ``. | | `showLabel` | `boolean` | `false` | Renders the formatted value next to the bar (rounded percent by default). Falls back to the config provider. | | `formatLabel` | `(value: number, ratio: number) => string \| undefined` | `—` | Formats the visible label **and** `aria-valuetext` — DevExtreme's `statusFormat` in the house argument order; display and announcement never diverge. | | `ariaLabel` | `string \| undefined` | `—` | Accessible name; the localized `progress` message is the fallback. A progressbar must always be named. | | `className / style` | `string \| CSSProperties` | `—` | Merged onto the bar host; the Angular host takes `class`/`style` natively. | #### Events | Name | Type | Description | | --- | --- | --- | | `onCompleted` | `(event: OgeProgressBarCompletedEvent) => void` | The value reached `max` — DevExtreme's `onComplete`, the callback half of Angular's `(completed)`. Fired once per arrival: staying at max is silent, re-crossing after a reset fires again. | #### Types | Name | Type | Description | | --- | --- | --- | | `OgeProgressBarSeverity` | `'accent' \| 'success' \| 'warning' \| 'danger'` | Fill color vocabulary. | | `OgeProgressBarCompletedEvent` | `{ value: number }` | Payload of `onCompleted`. | | `OgeProgressBarProps` | `interface` | Props of ``. | ### #### Properties | Name | Type | Default | Description | | --- | --- | --- | --- | | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Ring diameter preset — 16/24/32px. | | `inheritSize` | `boolean` | `false` | A `1em` ring that scales with the surrounding font — the inside-a-button case. | | `severity` | `'accent' \| 'success' \| 'warning' \| 'danger'` | `'accent'` | Ring color — the card/toast severity vocabulary. | | `ariaLabel` | `string \| undefined` | `—` | Accessible name; the localized `loading` message is the fallback. | | `className / style` | `string \| CSSProperties` | `—` | Merged onto the ring host; the Angular host takes `class`/`style` natively. | _Accessibility contract_ | Name | Type | Description | | --- | --- | --- | | `role="progressbar", no aria-valuenow` | `—` | Deliberately indeterminate-only (dx, Kendo and PrimeNG all are — a circle filling toward completion is the progress bar’s job), announced without `aria-valuenow` per the ARIA rule. Under `prefers-reduced-motion` the spin **slows rather than stops**: a frozen ring reads as finished. | #### Types | Name | Type | Description | | --- | --- | --- | | `OgeLoadIndicatorSeverity` | `'accent' \| 'success' \| 'warning' \| 'danger'` | Ring color vocabulary. | | `OgeLoadIndicatorProps` | `interface` | Props of ``. | ### #### Properties | Name | Type | Default | Description | | --- | --- | --- | --- | | `shape` | `'text' \| 'circle' \| 'rectangle'` | `'text'` | What the placeholder stands in for; a `text` skeleton with no height derives it from the font. Falls back to ``. | | `animation` | `'shimmer' \| 'pulse' \| 'none'` | `'shimmer'` | `shimmer` is the card/accordion moving-gradient recipe, `pulse` the data grid filler rows' opacity beat, `none` a static block. Falls back to the config provider. | | `width / height` | `string \| number \| undefined` | `—` | Numbers mean pixels; strings pass through as CSS. | | `lines` | `number` | `1` | `text` shape only: renders N stacked lines with the last one tapered — the card/accordion placeholder pattern as one prop. Capped at 20. | | `className / style` | `string \| CSSProperties` | `—` | Merged onto the placeholder host; the Angular host takes `class`/`style` natively. | _Accessibility contract_ | Name | Type | Description | | --- | --- | --- | | `aria-hidden, always` | `—` | A skeleton is decoration — the loading **region** owns the announcement. Put `aria-busy` (and, where the change should be announced, a visually-hidden status text) on the container the skeleton stands in for. | #### Types | Name | Type | Description | | --- | --- | --- | | `OgeSkeletonShape / OgeSkeletonAnimation` | `'text' \| 'circle' \| 'rectangle' / 'shimmer' \| 'pulse' \| 'none'` | The two vocabularies, shared with the Angular package. | | `OgeSkeletonProps` | `interface` | Props of ``. | ### Configuration #### Properties _OgeProgressBarConfigProvider_ | Name | Type | Description | | --- | --- | --- | | `messages` | `OgeProgressBarMessages` | Every user-facing string: `progress` — the accessible name fallback (default `Progress`). | | `severity / showLabel` | `—` | Defaults for the matching props. `useOgeProgressBarConfig()` reads the resolved value. | _OgeLoadIndicatorConfigProvider_ | Name | Type | Description | | --- | --- | --- | | `messages` | `OgeLoadIndicatorMessages` | Every user-facing string: `loading` — the accessible name fallback (default `Loading`). `useOgeLoadIndicatorConfig()` reads the resolved value. | _OgeSkeletonConfigProvider_ | Name | Type | Description | | --- | --- | --- | | `shape / animation` | `—` | Defaults for the matching props. Deliberately no messages block: a skeleton renders no user-facing strings — the loading region owns the announcement. `useOgeSkeletonConfig()` reads the resolved value. | ### #### Properties _Panes & sizing_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `panes` | `readonly OgeSplitterPaneItem[] \| undefined` | `—` | The panes, in layout order — the React counterpart of the projected `` children: every field of a pane plus its `content`. | | `dataSource` | `OgeSplitterDataSourceLike \| undefined` | `—` | Remote pane list, loaded through `@oge-ui/core`’s `DataSource` contract and merged after `panes`. A source that publishes `changes` triggers a reload. | | `itemHoldTimeout` | `number` | `750` | Milliseconds a pointer must rest on a pane before `onPaneHold` fires. | | `sizes` | `readonly OgeSplitterSize[] \| undefined` | `—` | Current pane sizes — the controlled half of the pair, and the whole persistable state. Setting it overrides the per-pane `size` fields. Numbers are ratios; `'240px'` pins a pane. | | `defaultSizes` | `readonly OgeSplitterSize[] \| undefined` | `—` | Initial sizes of the uncontrolled pair — the component owns them from there. Never combine with `sizes`. | | `renderPane` | `(pane: OgeSplitterPaneItem, index: number, collapsed: boolean) => ReactNode` | `—` | Body of every pane that carries no `content` — the React face of `[ogeSplitterPaneTemplate]`. | | `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Axis the panes are laid out along. Also drives which arrow keys move a separator. | | `separatorSize` | `number` | `6` | Thickness of each separator in pixels — a real grid track, so it never eats into a pane. | _Interaction_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `resizable` | `boolean` | `true` | Pins every separator when `false`. | | `step` | `number` | `5` | Share points one arrow-key press moves a separator. | | `keyboardNavigation` | `boolean` | `true` | Enables Arrow / Home / End / Enter / Ctrl+Arrow on the separators. While off they also leave the Tab sequence. | | `showCollapseGrips` | `boolean` | `true` | Renders an `aria-hidden` chevron on the separator for each collapsible neighbour — one for the pane before it, one for the pane after. The keyboard paths (Enter, Ctrl+Arrow) stay available either way. | | `disabled` | `boolean` | `false` | Disables the whole splitter — no dragging, no keyboard, no collapsing. | _Accessibility & text_ | Name | Type | Description | | --- | --- | --- | | `ariaLabel` | `string \| undefined` | Accessible name of the splitter container. | | `messages` | `Partial` | Per-instance overrides of the `` strings, including the separators’ accessible names. | _Host_ | Name | Type | Description | | --- | --- | --- | | `className` | `string \| undefined` | Extra classes on the splitter element — the React host styling idiom; an Angular host takes `class` natively. | | `style` | `CSSProperties \| undefined` | Inline styles on the splitter element. The splitter fills its container and has no intrinsic height, so this is usually where the height comes from. | | `id` | `string \| undefined` | Id of the splitter element; the separators and panes derive their own ids independently. | #### Methods _OgeSplitterHandle (ref)_ | Name | Type | Description | | --- | --- | --- | | `collapse(target)` | `(target: number \| string) => boolean` | Collapses a pane by index or key. Returns `false` when the pane is not collapsible or `onPaneCollapsing` vetoed it. | | `expand(target)` | `(target: number \| string) => boolean` | Expands a collapsed pane, restoring the size it had when it collapsed and scaling its siblings back down to fit. | | `toggle(target)` | `(target: number \| string) => boolean` | Collapses the pane if expanded, expands it otherwise. | | `isCollapsed(target)` | `(target: number \| string) => boolean` | Whether a pane is currently collapsed. | | `resize(separatorIndex, delta)` | `(separatorIndex: number, delta: number) => boolean` | Moves a separator by `delta` share points — the programmatic equivalent of an arrow key. `false` when that separator cannot move. | | `focus(separatorIndex?)` | `(separatorIndex?: number) => void` | Focuses a separator, the first one by default. | #### Events | Name | Type | Description | | --- | --- | --- | | `onResizeStarted` | `(event: OgeSplitterResizeStartEvent) => void` | Fires once when a drag or keyboard resize begins. The reference `onResizeStart`. | | `onResized` | `(event: OgeSplitterResizeEvent) => void` | Fires every time the sizes change during a resize — once per pointer move. The reference `onResize`. | | `onResizeEnded` | `(event: OgeSplitterResizeEvent) => void` | Fires once when the gesture finishes, after the new sizes have been published. The reference `onResizeEnd`. | | `onPaneCollapsing` | `(event: OgeSplitterPaneCollapsingEvent) => void` | Cancelable pre-event of a pane collapsing — set `cancel = true` to block it. | | `onPaneExpanding` | `(event: OgeSplitterPaneCollapsingEvent) => void` | Cancelable pre-event of a pane expanding. | | `onPaneCollapsed` | `(event: OgeSplitterPaneCollapsedEvent) => void` | Fires after a pane collapsed. | | `onPaneExpanded` | `(event: OgeSplitterPaneCollapsedEvent) => void` | Fires after a pane expanded. | | `onPaneClick` | `(event: OgeSplitterPaneClickEvent) => void` | Fires when a pane is clicked. A nested splitter reports its own panes — the event does not surface on the parent. | | `onPaneHold` | `(event: OgeSplitterPaneHoldEvent) => void` | A pane was held for `itemHoldTimeout` — a touch long-press or a mouse hold. | | `onPaneContextMenu` | `(event: OgeSplitterPaneHoldEvent) => void` | A pane was right-clicked or long-pressed for a menu. | | `onSizesChange` | `(sizes: readonly OgeSplitterSize[]) => void` | The controlled half of `sizes`; Angular’s `[(sizes)]` model is both halves at once. Persist the array here. | #### Types _Types_ | Name | Type | Description | | --- | --- | --- | | `OgeSplitterOrientation` | `'horizontal' \| 'vertical'` | Axis the panes are laid out along. | | `OgeSplitterGripSide` | `'start' \| 'end'` | Which neighbour a separator's collapse grip acts on: `'start'` is the pane before it (the APG primary pane), `'end'` the one after. | | `OgeSplitterSize` | `number \| string` | A number (or `'%'`) is a **ratio** of the space the flexible panes share, so `[30, 30]` lays out like `[50, 50]`. `'px'` pins the pane to a fixed track. Any other string is ignored with a dev-mode warning. | | `OgeSplitterPaneItem` | `interface` | One pane of the `panes` prop — every field of `OgeSplitterPaneData` (`key`, `size`, `minSize`, `maxSize`, `collapsible`, `collapsed`, `collapsedSize`, `resizable`, `scrollable`, `disabled`, `visible`, `text`, `cssClass`, `htmlAttributes`, `orientation`) plus `content` and a nested `panes` array of the same shape. | | `OgeSplitterHandle` | `interface` | The `ref` handle: `collapse`, `expand`, `toggle`, `isCollapsed`, `resize`, `focus` — the React face of the Angular public methods. | | `OgeSplitterResizeStartEvent` | `interface` | `separatorIndex`, `sizes` at the start of the gesture, and the originating `event` (absent for a keyboard resize). | | `OgeSplitterResizeEvent` | `interface` | `separatorIndex`, current `sizes`, `previousSizes` from the start of the gesture, and the originating `event`. | | `OgeSplitterPaneCollapsingEvent` | `interface` | `index`, `key`, `item`, `event`, and a mutable `cancel` flag. | | `OgeSplitterPaneCollapsedEvent` | `interface` | `index`, `key`, `item`, `event`. | | `OgeSplitterPaneHoldEvent` | `interface` | Payload of `onPaneHold` and `onPaneContextMenu`: `index`, `key?`, `item?`, `event`. | | `OgeSplitterPaneClickEvent` | `interface` | `index`, `key`, `item` and the originating `event`. | ### OgeSplitterPane (OgeSplitterPaneItem) #### Properties | Name | Type | Default | Description | | --- | --- | --- | --- | | `key` | `string \| undefined` | `—` | Stable identity used by DOM ids and by the handle’s string targets. | | `size` | `OgeSplitterSize \| undefined` | `—` | Initial size — a ratio number, `'40%'` or `'240px'`. Panes without one split whatever the sized ones leave. | | `minSize` | `OgeSplitterSize \| undefined` | `—` | Smallest size a resize may leave this pane at. A pixel value also becomes the grid track’s floor. | | `maxSize` | `OgeSplitterSize \| undefined` | `—` | Largest size a resize may grow this pane to. | | `collapsible` | `boolean` | `false` | Allows the pane to be collapsed from its separator — Enter, the grip, or a double click. | | `collapsed` | `boolean` | `false` | Collapsed state. Writing it runs the splitter’s pipeline, so a vetoed change is reverted — the React face of Angular’s `[(collapsed)]` model, whose other half is `onPaneCollapsed` / `onPaneExpanded`. | | `collapsedSize` | `OgeSplitterSize \| undefined` | `0` | Size the pane keeps while collapsed. | | `resizable` | `boolean` | `true` | Pins the pane — both of its separators refuse to drag and report `aria-disabled`. | | `scrollable` | `boolean` | `true` | Clips overflowing content instead of scrolling it when `false`. | | `disabled` | `boolean` | `false` | Disabled panes cannot be collapsed and their separators are inert. | | `visible` | `boolean` | `true` | Removes the pane entirely when `false`. | | `text` | `string \| undefined` | `—` | Plain-text body, rendered when the pane has neither `content` nor a `renderPane`. | | `htmlAttributes` | `Readonly> \| undefined` | `—` | Extra attributes on the pane element. Keys removed from the bag are removed from the DOM, so clearing it clears the element. | | `cssClass` | `string \| undefined` | `—` | Extra class on the pane element. | | `content` | `ReactNode` | `—` | Pane body — the React counterpart of the content projected into an ``. Takes precedence over `renderPane` and `text`. | | `panes` | `readonly OgeSplitterPaneItem[] \| undefined` | `—` | Nested splitter inside this pane, rendered on the opposite axis by default — the data form of an `` placed in `content`. | | `orientation` | `'horizontal' \| 'vertical'` | `—` | Axis of the nested splitter, when this pane has one. | #### Types _Render props_ | Name | Type | Description | | --- | --- | --- | | `renderPane` | `(pane, index, collapsed) => ReactNode` | The splitter-level renderer for panes with no `content` — the React replacement for the `[ogeSplitterPaneTemplate]` structural directive. Documented as a prop of ``. | ### Splitter configuration #### Properties _OgeSplitterConfig_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `separatorSize` | `number \| undefined` | `6` | Default for the `separatorSize` prop. | | `step` | `number \| undefined` | `5` | Default for the `step` prop. | | `showCollapseGrips` | `boolean \| undefined` | `true` | Default for the `showCollapseGrips` prop. | | `messages` | `OgeSplitterMessages` | `—` | Every user-facing string: `separator` (with `{{first}}` / `{{second}}` placeholders), `collapsed`, `collapsePane`, `expandPane`, `noData`. | #### Methods | Name | Type | Description | | --- | --- | --- | | `OgeSplitterConfigProvider` | `(props: { config?: OgeSplitterConfigInput; children?: ReactNode }) => JSX.Element` | Wrap a subtree to change the splitter defaults and user-facing strings beneath it — the React counterpart of Angular’s `provideOgeSplitterConfig()`. Both shallow-merge `messages` over the same `@oge-ui/behavior` defaults, so an override reads identically in either layer. | | `useOgeSplitterConfig()` | `() => OgeSplitterConfig` | Reads the resolved config of the current subtree — how a splitter of your own picks up the same defaults. | #### Types _Types_ | Name | Type | Description | | --- | --- | --- | | `OgeSplitterConfig` | `interface` | Shape the provider publishes: `messages` plus the optional prop defaults. | | `OgeSplitterConfigInput` | `interface` | The provider’s `config` prop — every field optional, `messages` partial. | | `OgeSplitterMessages` | `interface` | Every user-facing string in the splitter, including the separators’ accessible names. | | `OGE_DEFAULT_SPLITTER_CONFIG` | `OgeSplitterConfig` | The built-in defaults, exported for composition. | | `OGE_DEFAULT_SPLITTER_MESSAGES` | `OgeSplitterMessages` | The built-in English strings. | ### #### Properties _Items_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `items` | `readonly OgeToolbarItemData[] \| undefined` | `—` | Data-driven entries — the only item source in React, since there is no child component to project. | | `dataSource` | `OgeToolbarDataSourceLike \| undefined` | `—` | Remote command list, accepted structurally through the `@oge-ui/core` `DataSource` contract and merged after `items`. A source that publishes `changes` triggers a reload. | | `showText` | `'always' \| 'onBar' \| 'inMenu' \| 'never'` | `'always'` | Default for every item’s `showText`: both places, the bar only, the menu only, or neither. An item that renders icon-only keeps its `text` as the accessible name. | | `showIcon` | `'always' \| 'onBar' \| 'inMenu' \| 'never'` | `'always'` | Default for every item’s `showIcon`. It resolves separately for the bar and the overflow menu, so a collapsed command keeps its icon on its menu row unless you say `'onBar'`. | _Layout & overflow_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `overflow` | `'menu' \| 'scroll' \| 'wrap' \| 'extended' \| 'none'` | `'menu'` | `menu` collapses what does not fit into an overflow menu, `scroll` keeps one line and adds scroll buttons, `wrap` flows onto more lines (the reference `multiline` mode), `extended` hides the remainder in a second row behind a toggle, `none` lets the row overflow. | | `scrollStep` | `number` | `120` | Pixels a scroll button moves the row in `overflow: 'scroll'`. | | `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Main axis. Drives the arrow keys and `aria-orientation` (written only when vertical, since horizontal is the ARIA default). | | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Density preset. Falls back to ``. | | `stylingMode` | `'outlined' \| 'filled' \| 'flat'` | `'outlined'` | Container chrome. Falls back to ``. | _State & accessibility_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `disabled` | `boolean` | `false` | Disables every item and takes the whole toolbar out of the Tab sequence. | | `wrap` | `boolean` | `true` | Whether arrow navigation wraps around the ends — optional in the APG toolbar pattern, on by default here. | | `keyboardNavigation` | `boolean` | `true` | Turns arrow/Home/End handling off entirely. The controls then keep their natural Tab order instead of a roving tabindex. | | `itemHoldTimeout` | `number` | `750` | Milliseconds a pointer must rest on an item before `onItemHold` fires. | | `ariaLabel` | `string \| undefined` | `—` | Accessible name of the toolbar; falls back to `messages.toolbar`. | | `ariaLabelledBy` | `string \| undefined` | `—` | Id of a visible label. Wins over `ariaLabel`, which is then omitted. | | `messages` | `Partial \| undefined` | `—` | Per-instance overrides of the context strings (`toolbar`, `overflowMenu`, `noData`). | | `className` | `string \| undefined` | `—` | Extra class(es) on the `role="toolbar"` host — the React styling idiom; the Angular host takes `class` natively. | | `style` | `CSSProperties \| undefined` | `—` | Inline styles on the host — the React styling idiom; the Angular host takes `style` natively. | _Node slots & render props_ | Name | Type | Description | | --- | --- | --- | | `before` | `ReactNode` | Any control, rendered into the leading group — the React face of `[ogeToolbarBefore]`. Slot content always stays on the bar: only items the toolbar owns can be re-rendered inside the menu. | | `center` | `ReactNode` | Any control, rendered into the centre group (`[ogeToolbarCenter]`). | | `after` | `ReactNode` | Any control, rendered into the trailing group (`[ogeToolbarAfter]`). | | `renderItem` | `(context: OgeToolbarItemRenderContext) => ReactNode` | Replaces the default rendering of every `items` entry on the bar — the render-prop face of `ogeToolbarItemTemplate`. Context: `{ item, index, inMenu }`. A rendered entry stays re-stampable, so it can still collapse into the overflow menu. | | `renderMenuItem` | `(context: OgeToolbarItemRenderContext) => ReactNode` | Replaces the default rendering of an item inside the overflow menu (`ogeToolbarMenuItemTemplate`). | #### Methods _Handle (ref: OgeToolbarHandle)_ | Name | Type | Description | | --- | --- | --- | | `focus()` | `(): void` | Focuses the toolbar’s current roving-tabindex stop. | | `openMenu()` | `(event?: Event): void` | Opens the overflow menu. Runs the `onMenuOpening` pipeline, so it can be vetoed. | | `closeMenu()` | `(reason?: OgeToolbarMenuCloseReason): void` | Closes the overflow menu, subject to `onMenuClosing`. Defaults to reason `'api'`. | | `toggleMenu()` | `(event?: Event): void` | Opens the menu when closed, closes it otherwise — the reference `toggle()` method. | | `toggleExtendedRow()` | `(): void` | Shows or hides the second row of `overflow: 'extended'`. | | `refreshOverflow()` | `(): void` | Drops the measurement cache and re-measures. Prop changes and container resizes already do this — call it after something the toolbar cannot observe changed a control’s size (a late web font, a stylesheet swap). | | `addItem()` | `(item: OgeToolbarItemData): void` | Appends a runtime entry, merged after `items`. `items` stays the declared source of truth, so a re-supplied array does not drop it. | | `removeItem()` | `(key: string): void` | Drops an entry added by `addItem()`, or hides an `items` entry. | | `hideItem()` | `(key: string, hidden?: boolean): void` | Hides (or re-shows) an entry without touching the `items` array. | | `enableItem()` | `(key: string, enabled?: boolean): void` | Enables (or disables) an entry without touching the `items` array. | | `clearItemOverrides()` | `(): void` | Drops every `hideItem()` / `enableItem()` override. | #### Events _Callbacks_ | Name | Type | Description | | --- | --- | --- | | `onItemClick` | `(event: OgeToolbarItemClickEvent) => void` | An item was activated on the bar or from the menu. Payload: `index`, `key`, `item`, `inMenu`, `event`. | | `onMenuOpening` | `(event: OgeToolbarMenuOpeningEvent) => void` | Cancelable — set `cancel` to keep the overflow menu closed. | | `onMenuOpened` | `() => void` | The overflow menu opened. | | `onMenuClosing` | `(event: OgeToolbarMenuClosingEvent) => void` | Cancelable — set `cancel` to keep the overflow menu open. Carries the close `reason`. | | `onMenuClosed` | `(event: OgeToolbarMenuClosedEvent) => void` | The overflow menu closed, with its reason. | | `onOverflowChanged` | `(event: OgeToolbarOverflowChangedEvent) => void` | The set of items living in the overflow menu changed. Payload: `keys`, `count`. | | `onActiveChanged` | `(event: OgeToolbarItemActiveChangedEvent) => void` | A toggle item’s pressed state changed. `items` entries are data the toolbar must not mutate, so a React toggle is controlled: this reports the new value and the application writes it back into `items`. | | `onItemHold` | `(event: OgeToolbarItemHoldEvent) => void` | An item was held for `itemHoldTimeout` — touch long-press or mouse hold. | | `onItemContextMenu` | `(event: OgeToolbarItemHoldEvent) => void` | An item was right-clicked or long-pressed. | #### Types _Types_ | Name | Type | Description | | --- | --- | --- | | `OgeToolbarProps` | `interface` | Every prop of ``. | | `OgeToolbarHandle` | `interface` | The imperative handle reached through `ref` — the React counterpart of the Angular component’s public methods. | | `OgeToolbarItemRenderContext` | `interface` | Argument of `renderItem` / `renderMenuItem`: `item` (the `items` entry), `index`, `inMenu`. | | `OgeToolbarItemData` | `interface` | One data-driven entry: `key`, `type`, `text`, `icon`, `suffixIcon`, `iconClass`, `suffixIconClass`, `hint`, `width`, `htmlAttributes`, `location`, `locateInMenu`, `overflowPriority`, `showText`, `showIcon`, `disabled`, `visible`, `cssClass`, `severity`, `active`, `data`. | | `OgeToolbarItemType` | `'button' \| 'separator' \| 'spacer' \| 'label'` | What the toolbar renders for an item it owns. | | `OgeToolbarItemLocation` | `'before' \| 'center' \| 'after'` | Which of the three groups an item belongs to. | | `OgeToolbarLocateInMenu` | `'auto' \| 'always' \| 'never'` | Whether an item may move into the overflow menu. Structurally core’s `OgeToolbarOverflowPolicy`, which the shared fitting math consumes. | | `OgeToolbarDisplayMode` | `'always' \| 'onBar' \| 'inMenu' \| 'never'` | Where an item’s text or icon is rendered: both places, the bar only, the menu only, or neither. | | `OgeToolbarItemSeverity` | `'default' \| 'accent' \| 'danger'` | Emphasis of an item the toolbar renders itself. | | `OgeToolbarOverflow` | `'menu' \| 'scroll' \| 'wrap' \| 'extended' \| 'none'` | How the toolbar reacts to more items than room. | | `OgeToolbarOrientation` | `'horizontal' \| 'vertical'` | Main axis of the toolbar. | | `OgeToolbarSize` | `'sm' \| 'md' \| 'lg'` | Density preset. | | `OgeToolbarStylingMode` | `'outlined' \| 'filled' \| 'flat'` | Container chrome preset. | | `OgeToolbarMenuCloseReason` | `'api' \| 'outside' \| 'escape' \| 'select' \| 'tab'` | Why the overflow menu closed — the overlay package’s canonical reason set. | | `OgeToolbarItemClickEvent` | `interface` | `index`, `key?`, `item?`, `inMenu`, `event`. | | `OgeToolbarOverflowChangedEvent` | `interface` | `keys`, `count`. | | `OgeToolbarItemActiveChangedEvent` | `interface` | `index`, `key?`, `item?`, `active`, `event`. | | `OgeToolbarItemHoldEvent` | `interface` | Payload of `onItemHold` and `onItemContextMenu`: `index`, `key?`, `item?`, `event`. | | `OgeToolbarMenuOpeningEvent` | `interface` | `cancel`, `event?`. | | `OgeToolbarMenuClosingEvent` | `interface` | `cancel`, `reason`. | | `OgeToolbarMenuClosedEvent` | `interface` | `reason`. | | `OgeToolbarDataSourceLike` | `interface` | The structural shape `dataSource` accepts — `load()` plus an optional `changes` subscription — so the React package never depends on the Angular `DataSource` class. | ### OgeToolbarItem (OgeToolbarItemData) #### Properties _Fields_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `key` | `string \| undefined` | `—` | Stable identity echoed on `onItemClick` and used for DOM ids. | | `type` | `'button' \| 'separator' \| 'spacer' \| 'label'` | `'button'` | What the toolbar renders when no `renderItem` is supplied. | | `text` | `string \| undefined` | `—` | Label; also the accessible name when the item renders icon-only. | | `icon` | `string \| undefined` | `—` | SVG path data (`d`) for a leading aria-hidden 16×16 icon. | | `suffixIcon` | `string \| undefined` | `—` | SVG path data (`d`) for a trailing icon, rendered after the text. | | `iconClass` | `string \| undefined` | `—` | Class(es) for a leading icon rendered as an empty `_` — the hook for an icon font the application already ships. `icon` stays the dependency-free default. | | `suffixIconClass` | `string \| undefined` | `—` | Class(es) for a trailing icon element. | | `width` | `number \| string \| undefined` | `—` | Fixed main-axis size of the item — a bare number is pixels. | | `htmlAttributes` | `Readonly> \| undefined` | `—` | Extra attributes spread onto the item element. | | `hint` | `string \| undefined` | `—` | Tooltip — the native `title` attribute. | | `location` | `'before' \| 'center' \| 'after'` | `'before'` | Which of the toolbar’s three groups the item joins. | | `locateInMenu` | `'auto' \| 'always' \| 'never'` | `'auto'` | Whether the item may move into the overflow menu. The default diverges from the reference `never` on purpose — collapsing is the point. | | `overflowPriority` | `number \| undefined` | `0` | How hard the item holds its place on the bar; higher survives longer. The default makes the trailing item yield first, as in every reference toolbar. Raise it to keep a primary command visible without moving it to the front of the bar. | | `showText` | `'always' \| 'onBar' \| 'inMenu' \| 'never' \| undefined` | `—` | Overrides the toolbar’s `showText`. | | `showIcon` | `'always' \| 'onBar' \| 'inMenu' \| 'never' \| undefined` | `—` | Overrides the toolbar’s `showIcon`. | | `disabled` | `boolean` | `false` | Not clickable, and skipped by the toolbar’s arrow navigation. | | `visible` | `boolean` | `true` | `false` removes the item entirely. | | `cssClass` | `string \| undefined` | `—` | Extra class on the item element. | | `severity` | `'default' \| 'accent' \| 'danger'` | `'default'` | Emphasis of a toolbar-rendered button. | | `active` | `boolean \| undefined` | `—` | Toggle-button state. Defining it is what makes the item a toggle: it renders `aria-pressed` on the bar and a checkmark in the menu. React toggles are controlled — the component reports through `onActiveChanged` and the app writes the new value back. | | `data` | `unknown` | `—` | Arbitrary payload echoed back on `onItemClick` through `event.item`. | ### Toolbar configuration #### Properties _OgeToolbarConfigProvider (config prop)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `size` | `'sm' \| 'md' \| 'lg' \| undefined` | `—` | Default for every toolbar’s `size`. | | `stylingMode` | `'outlined' \| 'filled' \| 'flat' \| undefined` | `—` | Default for every toolbar’s `stylingMode`. | | `messages.toolbar` | `string` | `'Toolbar'` | Accessible name used when neither `ariaLabel` nor `ariaLabelledBy` is set. | | `messages.overflowMenu` | `string` | `'More commands'` | Accessible name and tooltip of the overflow button, and the menu’s label. | | `messages.moreCommands` | `string` | `'Show more commands'` | Accessible name of the `overflow: 'extended'` second-row toggle. | | `messages.scrollBackward` | `string` | `'Scroll backward'` | Accessible name of the back scroll button in `overflow: 'scroll'`. | | `messages.scrollForward` | `string` | `'Scroll forward'` | Accessible name of the forward scroll button. | | `messages.noData` | `string` | `'No commands to display'` | Shown when the toolbar has no items of its own and nothing is passed to a node slot (the reference `noDataText`). | #### Methods | Name | Type | Description | | --- | --- | --- | | `useOgeToolbarConfig()` | `() => OgeToolbarConfig` | Reads the resolved config of the current subtree — how a bar of your own picks up the same defaults and messages. The Angular counterpart is `inject(OGE_TOOLBAR_CONFIG)`. | ### Demos Each demo below is a complete standalone component — copy one whole and it compiles. #### Basics ```ts 'use client'; import { useState } from 'react'; import { OgeCard } from '@oge-ui/react-layout'; export function CardBasicsDemo() { const [last, setLast] = useState('—'); return (
} >

Four days above the tree line, one pass a day.

last action → {last}

); } ``` #### Chrome presets ```ts 'use client'; import { OgeCard } from '@oge-ui/react-layout'; // outlined is the default; raised rests on the --oge-shadow-card token, // filled sits on the header surface, flat is for a card nested inside // another surface. const modes = ['outlined', 'raised', 'filled', 'flat'] as const; export function CardModesDemo() { return (
{modes.map((mode) => (

The {mode} chrome preset.

))}
); } ``` #### Configuration ```ts 'use client'; import { OgeCard, OgeCardConfigProvider } from '@oge-ui/react-layout'; export function CardConfigDemo() { return (

What every card in the subtree looks like. Instance props still win: add stylingMode="outlined" to opt one card out.

); } ``` #### Density ```ts 'use client'; import { OgeCard } from '@oge-ui/react-layout'; const sizes = ['sm', 'md', 'lg'] as const; export function CardSizeDemo() { return (
{sizes.map((size) => (

The {size} density.

))}
); } ``` #### Media ```ts 'use client'; import { OgeCard } from '@oge-ui/react-layout'; export function CardMediaDemo() { return (
} >

Media renders edge to edge, clipped by the card radius.

); } ``` #### Horizontal ```ts 'use client'; import { OgeCard } from '@oge-ui/react-layout'; import type { CSSProperties } from 'react'; export function CardHorizontalDemo() { return (
} >

The media column follows the writing mode, so it mirrors in RTL with no flag to set.

); } ``` #### Header slots ```ts 'use client'; import { useState } from 'react'; import { OgeCard } from '@oge-ui/react-layout'; export function CardHeaderDemo() { const [last, setLast] = useState('—'); return (
} headerActions={
} >

Reached the ridge before the weather turned.

last action → {last}

); } ``` #### Actions alignment ```ts 'use client'; import { useState } from 'react'; import { OgeCard } from '@oge-ui/react-layout'; import type { OgeCardActionsAlign } from '@oge-ui/react-layout'; const aligns: readonly OgeCardActionsAlign[] = [ 'start', 'center', 'end', 'stretched', ]; export function CardActionsDemo() { const [align, setAlign] = useState('start'); return (
{aligns.map((value) => ( ))}
} >

Unsaved changes.

); } ``` #### Footer & separator ```ts 'use client'; import { OgeCard } from '@oge-ui/react-layout'; export function CardFooterDemo() { return (
Updated 2 hours ago
} >

Generated from last week's data.


12 pages, 4 charts.

); } ``` #### Status & loading ```ts 'use client'; import { useState } from 'react'; import { OgeCard } from '@oge-ui/react-layout'; export function CardStatesDemo() { const [pending, setPending] = useState(true); return ( <>
} >

The e2e stage timed out after 20 minutes.

Generated from last week's data.

); } ``` #### Clickable cards, accessibly ```ts 'use client'; import { OgeCard } from '@oge-ui/react-layout'; // The stretched hit area, in your own stylesheet: // // .oge-card { position: relative; } // .card-link::after { content: ''; position: absolute; inset: 0; } // // Other controls stay usable by raising them above the overlay // (position: relative + z-index). export function CardClickableDemo() { return (

Four days above the tree line.

Read the full report
); } ``` #### Declarative panels ```ts 'use client'; import { useState } from 'react'; import { OgeAccordion } from '@oge-ui/react-layout'; export function BasicAccordionDemo() { const [index, setIndex] = useState(-1); return ( console.log('itemExpanded', event.index)} items={[ { key: 'account', title: 'Account', description: 'Name and e-mail', content:

Account settings — selected index: {index}

, }, { key: 'notifications', title: 'Notifications', badge: 3, content:

Notification settings…

}, { key: 'archived', title: 'Archived', disabled: true, content:

Never reachable…

}, ]} /> ); } ``` #### Data-driven items ```ts 'use client'; import { useState } from 'react'; import { OgeAccordion } from '@oge-ui/react-layout'; import type { OgeAccordionItemDefinition } from '@oge-ui/react-layout'; const sections: OgeAccordionItemDefinition[] = [ { key: 'general', title: 'General', description: 'Language, time zone and formats', icon: 'M12 3v2m0 14v2m9-9h-2M5 12H3m14.5-6.5l-1.4 1.4M7.9 16.1l-1.4 1.4m11.2 0l-1.4-1.4M7.9 7.9L6.5 6.5', }, { key: 'security', title: 'Security', description: 'Password and two-factor auth', badge: 2, icon: 'M12 2l8 4v6c0 5-3.4 8.5-8 10-4.6-1.5-8-5-8-10V6z', }, { key: 'danger', title: 'Danger zone', description: 'Irreversible actions' }, ]; export function ItemsAccordionDemo() { const [openKeys, setOpenKeys] = useState(['general']); return ( (

Body of {item?.title} — expandedKeys: {openKeys.join(', ') || '(none)'}

)} /> ); } ``` #### Single, multiple & collapsible ```ts 'use client'; import { useState } from 'react'; import { OgeAccordion } from '@oge-ui/react-layout'; import type { OgeAccordionItemDefinition } from '@oge-ui/react-layout'; const sections: OgeAccordionItemDefinition[] = [ { key: 'general', title: 'General', description: 'Language, time zone and formats' }, { key: 'security', title: 'Security', description: 'Password and two-factor auth', badge: 2 }, { key: 'danger', title: 'Danger zone', description: 'Irreversible actions' }, ]; export function ModeAccordionDemo() { const [multiple, setMultiple] = useState(false); const [collapsible, setCollapsible] = useState(false); return ( <>

{item?.title} body…

} /> ); } ``` #### Lazy rendering & keep-alive ```ts 'use client'; import { useState } from 'react'; import { OgeAccordion } from '@oge-ui/react-layout'; function CreatedAt() { const [createdAt] = useState(() => new Date().toLocaleTimeString()); return

Created at {createdAt}

; } export function LazyAccordionDemo() { const [keepAlive, setKeepAlive] = useState(true); return ( <> }, { key: 'second', title: 'Second', renderContent: () => }, ]} /> ); } ``` #### Async expand guard ```ts 'use client'; import { OgeAccordion } from '@oge-ui/react-layout'; import type { OgeAccordionItemDefinition } from '@oge-ui/react-layout'; const guarded: OgeAccordionItemDefinition[] = [ { key: 'plain', title: 'Opens right away' }, { key: 'slow', title: 'Confirms first (1s)', expandGuard: () => new Promise((resolve) => setTimeout(() => resolve(true), 1000)), }, { key: 'locked', title: 'Always vetoes', expandGuard: () => false }, ]; export function GuardAccordionDemo() { return (

{item?.title} body…

} /> ); } ``` #### Invalid sections ```ts 'use client'; import { useRef, useState } from 'react'; import { OgeAccordion } from '@oge-ui/react-layout'; import type { OgeAccordionHandle, OgeAccordionItemDefinition } from '@oge-ui/react-layout'; export function InvalidAccordionDemo() { const accordion = useRef(null); const [sections, setSections] = useState([ { key: 'contact', title: 'Contact' }, { key: 'billing', title: 'Billing', invalid: true }, { key: 'shipping', title: 'Shipping', invalid: true }, ]); return ( <>

{item?.title} fields…

} />
); } ``` #### Async content loader ```ts 'use client'; import { OgeAccordion } from '@oge-ui/react-layout'; const loadInvoices = () => new Promise((resolve) => setTimeout(() => resolve('42 invoices loaded.'), 900)); let flakyAttempts = 0; const loadFlaky = () => new Promise((resolve, reject) => setTimeout(() => { flakyAttempts++; if (flakyAttempts === 1) reject(new Error('network')); else resolve(`Report ready on attempt ${flakyAttempts}.`); }, 700), ); export function LoaderAccordionDemo() { return (

{data as string}

} items={[ { key: 'invoices', title: 'Invoices', contentLoader: loadInvoices }, { key: 'flaky', title: 'Flaky report', contentLoader: loadFlaky }, ]} /> ); } ``` #### Header actions ```ts 'use client'; import { useState } from 'react'; import { OgeAccordion } from '@oge-ui/react-layout'; const ALL_TEAMS = ['Platform', 'Design', 'Support']; export function ActionsAccordionDemo() { const [teams, setTeams] = useState(ALL_TEAMS); return ( <> ({ key: team, title: team, content:

{team} members…

, renderHeaderActions: () => ( ), }))} /> ); } ``` #### Panel-level control ```ts 'use client'; import { useRef, useState } from 'react'; import { OgeAccordion } from '@oge-ui/react-layout'; import type { OgeAccordionHandle } from '@oge-ui/react-layout'; export function PanelControlAccordionDemo() { const accordion = useRef(null); const [settled, setSettled] = useState(null); return ( <> setSettled(`afterExpand → ${event.index}`)} onAfterCollapse={(event) => setSettled(`afterCollapse → ${event.index}`)} items={[ { key: 'profile', title: 'Profile', expanded: true, content: ( <>

Name, e-mail and avatar…

), }, { key: 'preferences', title: 'Preferences', hideToggle: true, content:

This panel overrides hideToggle on its own.

, }, ]} />
{settled && {settled}}
); } ``` #### Toggle position & styling ```ts 'use client'; import { useState } from 'react'; import { OgeAccordion } from '@oge-ui/react-layout'; import type { OgeAccordionItemDefinition, OgeAccordionTogglePosition } from '@oge-ui/react-layout'; const sections: OgeAccordionItemDefinition[] = [ { key: 'general', title: 'General', description: 'Language, time zone and formats' }, { key: 'security', title: 'Security', description: 'Password and two-factor auth', badge: 2 }, { key: 'danger', title: 'Danger zone', description: 'Irreversible actions' }, ]; export function StylingAccordionDemo() { const [togglePosition, setTogglePosition] = useState('end'); const [flat, setFlat] = useState(false); return ( <>

{item?.title} body…

} /> ); } ``` #### Determinate bar ```ts 'use client'; import { useState } from 'react'; import { OgeProgressBar } from '@oge-ui/react-layout'; const asMegabytes = (value: number): string => `${value} MB`; export function ProgressDeterminateDemo() { const [uploaded, setUploaded] = useState(80); return ( <> {/* role="progressbar" with the full aria triple. showLabel renders the rounded percent; formatLabel replaces it AND feeds aria-valuetext. */} ); } ``` #### Indeterminate & buffer ```ts 'use client'; import { OgeProgressBar } from '@oge-ui/react-layout'; export function ProgressIndeterminateDemo() { return ( <> {/* Per the ARIA guidance aria-valuenow is OMITTED entirely for the unknown state — never pinned to a sentinel. */} ); } ``` #### Chunks & severity ```ts 'use client'; import { OgeProgressBar } from '@oge-ui/react-layout'; export function ProgressChunkDemo() { return ( <> {/* The filled segment count is the rounded ratio. */} ); } ``` #### Load indicator ```ts 'use client'; import { OgeLoadIndicator } from '@oge-ui/react-layout'; export function LoadIndicatorDemo() { return (
{/* inheritSize makes a 1em ring that scales with the button's font. */}
); } ``` #### Skeleton ```ts 'use client'; import { OgeSkeleton } from '@oge-ui/react-layout'; export function SkeletonDemo() { return ( <>
{/* lines: the tapered multi-line text stack in one prop. */} ); } ``` #### A real async flow ```ts 'use client'; import { useEffect, useRef, useState } from 'react'; import { OgeProgressBar } from '@oge-ui/react-layout'; export function ProgressAsyncDemo() { const [total, setTotal] = useState(null); const [received, setReceived] = useState(0); const [done, setDone] = useState(false); const discovery = useRef | null>(null); const ticker = useRef | null>(null); const stop = () => { if (discovery.current !== null) clearTimeout(discovery.current); if (ticker.current !== null) clearInterval(ticker.current); discovery.current = null; ticker.current = null; }; // Timers never outlive the component — the React counterpart of the // Angular page's DestroyRef cleanup. useEffect(() => stop, []); useEffect(() => { if (received >= 100) stop(); }, [received]); const start = () => { stop(); setTotal(null); setReceived(0); setDone(false); discovery.current = setTimeout(() => { setTotal(100); // "size discovered" ticker.current = setInterval( () => setReceived((current) => Math.min(current + 9, 100)), 250, ); }, 900); }; return ( <> {total === null ? ( ) : ( setDone(true)} /> )}

{done && completed ✓}

); } ``` #### Resizable panes ```ts 'use client'; import { useState } from 'react'; import { OgeSplitter } from '@oge-ui/react-layout'; import type { OgeSplitterSize } from '@oge-ui/react-layout'; export function SplitterDemo() { // Sizes are ratios, not percentages — [30, 30] lays out like [50, 50], // so a configuration that does not add up to 100 is never an error. const [sizes, setSizes] = useState([35, 65]); return ( // The splitter fills its container, so give it (or a wrapper) a height. Result list… }, { key: 'detail', minSize: 25, content:
Detail view…
}, ]} /> ); } ``` #### Orientation ```ts 'use client'; import { useState } from 'react'; import { OgeSplitter } from '@oge-ui/react-layout'; import type { OgeSplitterOrientation } from '@oge-ui/react-layout'; export function SplitterOrientationDemo() { const [orientation, setOrientation] = useState('vertical'); return ( <> {(['horizontal', 'vertical'] as const).map((option) => ( ))} Top / left }, { content:
Bottom / right
}, ]} /> ); } ``` #### Configuration ```ts 'use client'; import { OgeSplitter, OgeSplitterConfigProvider } from '@oge-ui/react-layout'; export function SplitterConfigDemo() { return ( Sol }, { content:
Sağ
}, ]} />
); } ``` #### Fixed and fluid panes ```ts 'use client'; import { OgeSplitter } from '@oge-ui/react-layout'; export function SplitterFixedDemo() { return ( // A 'px' size pins the pane: it becomes a fixed grid track and // drops out of the share pool, while '%' and plain numbers stay ratios. // min and max accept either unit, so a px floor on a ratio pane is fine. Fixed sidebar — dragged in pixels, }, { minSize: 20, content:
Fluid content
}, ]} /> ); } ``` #### Collapsible panes ```ts 'use client'; import { useState } from 'react'; import { OgeSplitter } from '@oge-ui/react-layout'; import type { OgeSplitterPaneCollapsedEvent } from '@oge-ui/react-layout'; export function SplitterCollapseDemo() { // A pane's `collapsed` field is the React counterpart of the Angular // [(collapsed)] model: write it to drive the pane, and follow the pane's // own collapses through onPaneCollapsed / onPaneExpanded. const [sideCollapsed, setSideCollapsed] = useState(false); const onCollapsed = (event: OgeSplitterPaneCollapsedEvent) => { console.log('collapsed', event.key); setSideCollapsed(true); }; return ( setSideCollapsed(false)} panes={[ { key: 'side', size: 30, collapsible: true, collapsedSize: '28px', collapsed: sideCollapsed, content:
Navigation…
, }, { key: 'main', content:
Editor…
}, ]} /> ); } ``` #### Data-driven panes ```ts 'use client'; import { OgeSplitter } from '@oge-ui/react-layout'; import type { OgeSplitterPaneItem } from '@oge-ui/react-layout'; const areas: OgeSplitterPaneItem[] = [ { key: 'explorer', size: 25, minSize: 15, collapsible: true }, { key: 'editor', size: 50 }, { key: 'inspector', size: 25, minSize: 15 }, ]; export function SplitterPanesDemo() { return ( (

{index} — {pane.key}

)} /> ); } ``` #### Nested splitters ```ts 'use client'; import { OgeSplitter } from '@oge-ui/react-layout'; export function SplitterNestedDemo() { return ( // Nesting needs no second component: a splitter inside a pane just // works, and a pane also nests by carrying its own panes array // (which defaults to the opposite axis). Sidebar }, { content: ( Editor }, { size: 30, collapsible: true, content:
Terminal
}, ]} /> ), }, ]} /> ); } ``` #### Forms inside a pane ```ts 'use client'; import { useState } from 'react'; import { OgeNumberBox, OgeTextBox } from '@oge-ui/react-inputs'; import { OgeSplitter } from '@oge-ui/react-layout'; export function SplitterFormDemo() { // @oge-ui/forms has no React layer yet (docs/REACT-PARITY.md), so the // editors hold their value in your own state — useState here, but React // Hook Form, Formik or TanStack Form bind exactly the same way. const [server, setServer] = useState({ host: 'db.internal', port: 5432 as number | null, user: 'app', }); return ( setServer((s) => ({ ...s, host }))} /> setServer((s) => ({ ...s, port }))} /> setServer((s) => ({ ...s, user }))} /> ), }, { size: 22, content:
Preview…
}, ]} /> ); } ``` #### Keyboard & accessibility ```ts 'use client'; import { OgeSplitter } from '@oge-ui/react-layout'; export function SplitterKeyboardDemo() { return ( // WAI-ARIA APG window splitter: every separator is a focusable // role="separator" with aria-controls on the pane before it and // aria-valuenow/min/max on one 0-100 scale. // Arrow keys move it by `step` share points (RTL mirrored) // Home / End jump to the primary pane's smallest / largest size // Enter collapse the primary pane, or restore it // Ctrl + Arrow collapse the pane the arrow points at, or restore // the collapsed one it points away from Primary, }, { minSize: 20, collapsible: true, content:
Secondary
}, ]} /> ); } ``` #### Events ```ts 'use client'; import { useState } from 'react'; import { OgeSplitter } from '@oge-ui/react-layout'; import type { OgeSplitterPaneCollapsingEvent, OgeSplitterResizeEvent } from '@oge-ui/react-layout'; export function SplitterEventsDemo() { const [locked, setLocked] = useState(true); const onResized = (event: OgeSplitterResizeEvent) => { console.log(event.sizes, event.previousSizes); }; // onPaneCollapsing / onPaneExpanding are cancelable — set cancel to veto. const onCollapsing = (event: OgeSplitterPaneCollapsingEvent) => { if (locked) event.cancel = true; }; return ( <> console.log('start')} onResized={onResized} onResizeEnded={() => console.log('end')} onPaneCollapsing={onCollapsing} panes={[ { key: 'a', collapsible: true, content:
A
}, { key: 'b', content:
B
}, ]} /> ); } ``` #### Persisting sizes ```ts 'use client'; import { useEffect, useState } from 'react'; import { OgeSplitter } from '@oge-ui/react-layout'; import type { OgeSplitterSize } from '@oge-ui/react-layout'; export function SplitterPersistDemo() { // sizes + onSizesChange is the whole persistable state, so there is no // stateKey to learn and no storage context to provide — save it anywhere. const [sizes, setSizes] = useState( () => JSON.parse(localStorage.getItem('editor-layout') ?? 'null') ?? [30, 70], ); useEffect(() => { localStorage.setItem('editor-layout', JSON.stringify(sizes)); }, [sizes]); return ( Left }, { content:
Right
}, ]} /> ); } ``` #### Commands ```ts 'use client'; import { useState } from 'react'; import { OgeToolbar } from '@oge-ui/react-layout'; import type { OgeToolbarItemData } from '@oge-ui/react-layout'; const commands: readonly OgeToolbarItemData[] = [ { key: 'new', text: 'New' }, { key: 'open', text: 'Open' }, { key: 'sep', type: 'separator' }, { key: 'save', text: 'Save', severity: 'accent' }, { key: 'delete', text: 'Delete', severity: 'danger', location: 'after' }, ]; export function ToolbarCommandsDemo() { const [last, setLast] = useState('—'); return ( <> setLast(event.item?.text ?? '')} />

last command → {last}

); } ``` #### Data-driven items ```ts 'use client'; import { useState } from 'react'; import { OgeToolbar } from '@oge-ui/react-layout'; import type { OgeToolbarItemData } from '@oge-ui/react-layout'; const tools: readonly OgeToolbarItemData[] = [ { key: 'undo', text: 'Undo' }, { key: 'redo', text: 'Redo' }, { key: 'sep', type: 'separator' }, { key: 'bold', text: 'Bold', active: true }, { key: 'note', type: 'label', text: 'Draft' }, { key: 'publish', text: 'Publish', location: 'after', severity: 'accent' }, ]; export function ToolbarItemsDemo() { const [last, setLast] = useState('—'); return ( <> setLast(`${event.key} (${event.inMenu ? 'menu' : 'bar'})`) } />

last command → {last}

); } ``` #### Keyboard & accessibility ```ts 'use client'; import { OgeToolbar } from '@oge-ui/react-layout'; import type { OgeToolbarItemData } from '@oge-ui/react-layout'; const items: readonly OgeToolbarItemData[] = [ { key: 'select', text: 'Select' }, { key: 'move', text: 'Move', disabled: true }, { key: 'zoom', text: 'Zoom' }, ]; export function ToolbarKeyboardDemo() { return ( ); } ``` #### Configuration ```ts 'use client'; import { OgeToolbar, OgeToolbarConfigProvider } from '@oge-ui/react-layout'; import type { OgeToolbarItemData } from '@oge-ui/react-layout'; const items: readonly OgeToolbarItemData[] = [ { key: 'yeni', text: 'Yeni' }, { key: 'kaydet', text: 'Kaydet', severity: 'accent' }, { key: 'ayarlar', text: 'Ayarlar', locateInMenu: 'always' }, ]; export function ToolbarConfigDemo() { return ( {/* A single instance can still override the strings with the messages prop. */} ); } ``` #### Location groups ```ts 'use client'; import { OgeToolbar } from '@oge-ui/react-layout'; import type { OgeToolbarItemData } from '@oge-ui/react-layout'; const items: readonly OgeToolbarItemData[] = [ { key: 'back', text: 'Back', location: 'before' }, { key: 'file', type: 'label', text: 'report.xlsx', location: 'center' }, { key: 'share', text: 'Share', location: 'after' }, ]; export function ToolbarLocationDemo() { return ( ); } ``` #### Overflow menu ```ts 'use client'; import { useState } from 'react'; import { OgeToolbar } from '@oge-ui/react-layout'; import type { OgeToolbarItemData } from '@oge-ui/react-layout'; const items: readonly OgeToolbarItemData[] = [ { key: 'cut', text: 'Cut', locateInMenu: 'never' }, { key: 'copy', text: 'Copy' }, { key: 'paste', text: 'Paste' }, { key: 'paste-special', text: 'Paste special' }, { key: 'print', text: 'Print preview' }, { key: 'settings', text: 'Document settings', locateInMenu: 'always' }, ]; export function ToolbarOverflowDemo() { const [width, setWidth] = useState(520); const [inMenu, setInMenu] = useState(0); return ( <> setWidth(Number(event.target.value))} />
setInMenu(event.count)} />

in the menu → {inMenu} command(s)

); } ``` #### Overflow priority ```ts 'use client'; import { useState } from 'react'; import { OgeToolbar } from '@oge-ui/react-layout'; import type { OgeToolbarItemData } from '@oge-ui/react-layout'; const items: readonly OgeToolbarItemData[] = [ { key: 'open', text: 'Open' }, { key: 'print', text: 'Print preview', overflowPriority: -1 }, { key: 'settings', text: 'Document settings', overflowPriority: -1 }, // Save sits last on the bar and is still the last to collapse. { key: 'save', text: 'Save', severity: 'accent', overflowPriority: 10 }, ]; export function ToolbarPriorityDemo() { const [width, setWidth] = useState(520); return ( <> setWidth(Number(event.target.value))} />
); } ``` #### Overflow modes ```ts 'use client'; import { useState } from 'react'; import { OgeToolbar } from '@oge-ui/react-layout'; import type { OgeToolbarItemData, OgeToolbarOverflow } from '@oge-ui/react-layout'; const modes: readonly OgeToolbarOverflow[] = [ 'menu', 'scroll', 'wrap', 'extended', 'none', ]; const items: readonly OgeToolbarItemData[] = [ { key: 'cut', text: 'Cut' }, { key: 'copy', text: 'Copy' }, { key: 'paste', text: 'Paste' }, { key: 'paste-special', text: 'Paste special' }, { key: 'print', text: 'Print preview' }, ]; export function ToolbarModesDemo() { const [mode, setMode] = useState('extended'); return ( <>
{modes.map((option) => ( ))}
); } ``` #### Toggle commands ```ts 'use client'; import { useState } from 'react'; import { OgeToolbar } from '@oge-ui/react-layout'; import type { OgeToolbarItemData } from '@oge-ui/react-layout'; export function ToolbarToggleDemo() { const [bold, setBold] = useState(true); const [italic, setItalic] = useState(false); const [last, setLast] = useState('—'); const items: readonly OgeToolbarItemData[] = [ { key: 'bold', text: 'Bold', active: bold }, { key: 'italic', text: 'Italic', active: italic }, ]; return ( <> { if (event.key === 'bold') setBold(event.active); if (event.key === 'italic') setItalic(event.active); setLast(`${event.key} → ${event.active}`); }} />

bold {String(bold)} · italic {String(italic)} · last {last}

); } ``` #### Runtime changes ```ts 'use client'; import { useRef } from 'react'; import { OgeToolbar } from '@oge-ui/react-layout'; import type { OgeToolbarHandle, OgeToolbarItemData } from '@oge-ui/react-layout'; const tools: readonly OgeToolbarItemData[] = [ { key: 'cut', text: 'Cut' }, { key: 'copy', text: 'Copy' }, { key: 'paste', text: 'Paste' }, ]; export function ToolbarRuntimeDemo() { const bar = useRef(null); return ( <> ); } ``` #### Icon-only commands ```ts 'use client'; import { OgeToolbar } from '@oge-ui/react-layout'; import type { OgeToolbarItemData } from '@oge-ui/react-layout'; // Icons are SVG path data — there is no icon font or icon package. const items: readonly OgeToolbarItemData[] = [ { key: 'bold', text: 'Bold', icon: 'M5 3h4a3 3 0 0 1 0 6H5zM5 9h5a3 3 0 0 1 0 6H5z' }, { key: 'italic', text: 'Italic', icon: 'M10 3H6m4 0-3 10m0 0H3m4 0h3' }, { key: 'underline', text: 'Underline', icon: 'M4 3v5a4 4 0 0 0 8 0V3M3 14h10', showText: 'always', }, ]; export function ToolbarIconDemo() { return ( ); } ``` #### Custom content ```ts 'use client'; import { useState } from 'react'; import { OgeToolbar } from '@oge-ui/react-layout'; import { OgeSelectBox } from '@oge-ui/react-inputs'; import type { OgeToolbarItemData } from '@oge-ui/react-layout'; const views = ['All', 'Mine', 'Archived']; const items: readonly OgeToolbarItemData[] = [{ key: 'view', text: 'View' }]; export function ToolbarSlotsDemo() { const [view, setView] = useState('All'); return ( ( )} after={} /> ); } ``` ## @oge-ui/react-navigation React navigation and wayfinding: a virtualized tree view with lazy children, checkbox tri-state and drag reparenting, a drawer with overlay/push/side modes and derived modality, a linear or free stepper with async step guards, a full WAI-ARIA menubar with submenus and type-ahead, a collapsing breadcrumb and a pagination bar — running the same config defaults, decision functions and stylesheet as the Angular navigation package. Docs: https://ogeui.com/components/tree-view ### #### Properties _Data & accessors_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `items` | `readonly T[] \| undefined` | `—` | Nodes to display — a flat parent-referencing list or nested children. | | `keyExpr` | `string \| ((row: T) => RowKey)` | `'id'` | Field holding a node's stable key. | | `parentIdExpr` | `string \| ((row: T) => unknown)` | `'parentId'` | Field holding a node's parent key (flat data). | | `itemsExpr` | `string \| ((row: T) => readonly T[]) \| undefined` | `—` | Field holding nested children. Setting it switches the tree to hierarchical data. | | `displayExpr` | `string \| ((row: T) => unknown)` | `'text'` | Field holding the display text. | | `disabledExpr` | `string \| ((row: T) => unknown)` | `'disabled'` | Field marking a node disabled. | | `hasItemsExpr` | `string \| ((row: T) => unknown)` | `'hasItems'` | Field hinting that a node has children that are not loaded yet — only consulted with a `loadChildren`. | | `iconExpr` | `string \| ((row: T) => unknown) \| undefined` | `—` | Field holding SVG path data (`d`) for a per-node icon. | | `rootValue` | `unknown` | `—` | Parent value that marks root nodes in flat data. `undefined`/`null` treats both as root. | | `dataStructure` | `'plain' \| 'tree' \| undefined` | `—` | Explicit data shape; inferred from `itemsExpr` when unset. | _State (controlled pairs)_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `expandedKeys / defaultExpandedKeys / onExpandedKeysChange` | `readonly RowKey[]` | `[]` | Keys of the expanded nodes. Pass `expandedKeys` to control the tree, `defaultExpandedKeys` to seed it and let the tree own the state — the React face of Angular’s `[(expandedKeys)]` model. | | `selectedKeys / defaultSelectedKeys / onSelectedKeysChange` | `readonly RowKey[]` | `[]` | Keys of the selected nodes, projected by `selectedKeysMode` on the way out. | | `focusedKey / defaultFocusedKey / onFocusedKeyChange` | `RowKey \| undefined` | `—` | Key of the node holding the roving tabindex. | | `searchValue / defaultSearchValue / onSearchValueChange` | `string` | `''` | Current search text. | _Selection_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `selectionMode` | `'none' \| 'single' \| 'multiple'` | `'none'` | How nodes may be selected. | | `selectByClick` | `boolean \| undefined` | `—` | Selects a node when its row is clicked. `undefined` resolves to `true` without checkboxes and `false` with them, so clicking a label never silently ticks the box beside it. | | `selectNodesRecursive` | `boolean` | `true` | Cascades selection down to descendants and up to fully-selected parents (the tri-state model). | | `showCheckBoxes` | `'none' \| 'normal' \| 'selectAll'` | `'none'` | Checkbox column: hidden, per node, or per node plus a "select all" row. | | `selectedKeysMode` | `'all' \| 'leavesOnly' \| 'excludeRecursive'` | `'all'` | Projection applied to `selectedKeys`: everything, only childless nodes, or the top-most roots of fully-selected subtrees. | _Expansion_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `expandEvent` | `'click' \| 'dblclick'` | `'click'` | Which gesture expands a node. The chevron always expands regardless. | | `expandNodesRecursive` | `boolean` | `true` | Expanding a node also expands its ancestors. | | `allowExpandAll` | `boolean` | `true` | Enables the APG `*` shortcut, which expands every sibling at the focused level. | _Search_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `searchEnabled` | `boolean` | `false` | Renders the built-in search box above the tree. | | `searchMode` | `'contains' \| 'startsWith' \| 'equals'` | `'contains'` | How the text is compared. Matching is accent- and locale-insensitive. | | `searchExpr` | `string \| ((row: T) => unknown) \| array \| undefined` | `—` | Fields searched instead of `displayExpr`; an array searches several. | | `searchTimeout` | `number` | `0` | Debounce applied to the search box, in milliseconds. | | `filterMode` | `'matchOnly' \| 'withAncestors' \| 'fullBranch'` | `'withAncestors'` | Which relatives of a match stay visible. `fullBranch` also keeps a match's descendants. | | `expandNodesOnFiltering` | `boolean` | `true` | Auto-expands the ancestors of matches. | | `highlightSearchResults` | `boolean` | `true` | Wraps matches in ``. | _Lazy loading, virtualization & drag_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `loadChildren` | `(parent: T, key: RowKey) => Promise` | `—` | Loads a node's children the first time it expands; a placeholder row shows meanwhile. Single-flight per node, and fetched children join the index so cascades reach them. | | `virtualScroll` | `boolean \| { itemHeight: number }` | `false` | Windowed rendering for large trees. Every row must actually be `itemHeight` tall (30px by default). | | `height` | `string \| undefined` | `—` | Height of the scroll container (any CSS length) — required for virtual scrolling to have a viewport. | | `allowDragging` | `boolean` | `false` | Enables pointer drag reordering. | | `allowDropInside` | `boolean` | `true` | Allows dropping *into* a node (reparenting), not just between siblings. | _Presentation_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `disabled` | `boolean` | `false` | Disables the whole component. | | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Density of the node rows. | | `ariaLabel` | `string \| undefined` | `—` | Aria label of the tree. | | `treeId` | `string \| undefined` | `—` | DOM id put on the inner `role="tree"` element — set it when an outside control (a combobox owning this tree as its popup) must point `aria-controls` at the list rather than the host. | | `messages` | `Partial` | `{}` | Per-instance overrides of the config strings. | | `className / style` | `string \| CSSProperties` | `—` | Merged onto the tree host. `className` is appended to the generated `oge-tree-view*` classes; the Angular host takes `class`/`style` natively. | #### Methods _OgeTreeViewHandle (via ref)_ | Name | Type | Description | | --- | --- | --- | | `expand(key)` | `(key: RowKey) => Promise` | Expands a node, awaiting the lazy child fetch when there is one. Resolves `false` if the node is unknown or `onItemExpanding` vetoed it. | | `collapse(key)` | `(key: RowKey) => Promise` | Collapses a node; resolves whether it actually collapsed. | | `toggle(key)` | `(key: RowKey) => Promise` | Expands the node if collapsed, collapses it otherwise. | | `expandAll()` | `() => void` | Expands every node that has loaded children. | | `collapseAll()` | `() => void` | Collapses every node. | | `selectAll()` | `() => void` | Selects every node. | | `unselectAll()` | `() => void` | Clears the selection. | | `select(key) / unselect(key)` | `(key: RowKey) => void` | Selects or deselects one node, cascading when `selectNodesRecursive` is on. | | `isExpanded(key) / isSelected(key)` | `(key: RowKey) => boolean` | Current state of one node. | | `getSelectedKeys(mode?)` | `(mode?: OgeTreeSelectedKeysMode) => RowKey[]` | Selected keys under a projection, defaulting to `selectedKeysMode`. | | `focus(key?)` | `(key?: RowKey) => void` | Focuses a node's row, or the first enabled one. | | `scrollToItem(key)` | `(key: RowKey) => void` | Scrolls a node into view, using offset math when virtualized. | #### Events | Name | Type | Description | | --- | --- | --- | | `onItemExpanding / onItemCollapsing` | `(event: OgeTreeExpandingEvent) => void / (event: OgeTreeCollapsingEvent) => void` | Cancelable pre-events — set `event.cancel = true` to block the change. | | `onItemExpanded / onItemCollapsed` | `(event: OgeTreeExpandedEvent) => void / (event: OgeTreeCollapsedEvent) => void` | Called after the change committed. | | `onSelectionChanging` | `(event: OgeTreeSelectionChangingEvent) => void` | Cancelable pre-event carrying the keys the selection would become. | | `onSelectionChanged` | `(event: OgeTreeSelectionChangedEvent) => void` | Called after the selection committed, with `previousKeys`. | | `onItemSelectionChanged` | `(event: OgeTreeItemSelectionChangedEvent) => void` | Called for the single node whose own state flipped. | | `onItemClick / onItemDblClick` | `(event: OgeTreeItemClickEvent) => void` | Called when a node row is clicked or double-clicked. | | `onChildrenLoaded / onChildrenLoadFailed` | `(event: OgeTreeChildrenLoadedEvent) => void / (event: OgeTreeChildrenFailedEvent) => void` | Called after a lazy `loadChildren` settled; the failure carries the original error. | | `onSelectAllChanged` | `(event: OgeTreeSelectAllChangedEvent) => void` | Called when the "select all" row is toggled. | | `onItemReordering / onItemReordered` | `(event: OgeTreeReorderingEvent) => void / (event: OgeTreeReorderedEvent) => void` | Cancelable pre-event and result of a drag & drop reparent, carrying `position: 'inside' \| 'before' \| 'after'`. The tree does not mutate your data. | #### Types _Render props_ | Name | Type | Description | | --- | --- | --- | | `renderItem` | `(context: { item, key, level, expanded, selected, checkState, hasChildren, highlightedHtml }) => ReactNode` | Replaces a node's built-in label — the React face of `[ogeTreeItemTemplate]`. Renders inside `role="treeitem"`, so it must not contain focusable controls. | | `renderExpandIcon` | `(context: { expanded: boolean, item, key, loading }) => ReactNode` | Replaces the expand/collapse chevron. | | `renderNoData` | `() => ReactNode` | Replaces the empty state shown when the tree has no nodes or a search matched nothing. | _Keyboard (WAI-ARIA APG treeview)_ | Name | Type | Description | | --- | --- | --- | | `Down / Up Arrow` | `navigation` | Moves focus over the visible nodes, skipping disabled ones. Trees do not wrap at the ends. | | `Right Arrow` | `navigation` | Opens a collapsed parent; on an open parent moves to its first child; no-op on a leaf. | | `Left Arrow` | `navigation` | Closes an open parent; otherwise moves focus to the parent node. | | `Home / End` | `navigation` | Moves to the first / last visible node. | | `Enter` | `activation` | Toggles a parent, or selects a leaf when a selection mode is set. | | `Space` | `selection` | Toggles selection on the focused node. `Shift+Space` selects the contiguous range. | | `Printable characters` | `type-ahead` | Moves focus to the next node whose label starts with the typed prefix, accent-insensitively. | | `*` | `expansion` | Expands every sibling at the focused level. | | `Ctrl+A, Shift+Arrow, Ctrl+Shift+Home/End` | `multi-select` | Select all, extend by one, and range-select to the start/end — the APG "recommended" model, so plain navigation needs no modifier. | ### Tree view configuration #### Properties _OgeTreeViewMessages_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `selectAll` | `string` | `'Select all'` | Label of the "select all" row. | | `searchPlaceholder` | `string` | `'Search…'` | Placeholder of the built-in search box. | | `searchLabel` | `string` | `'Search the tree'` | Accessible name of the built-in search box. | | `clearSearch` | `string` | `'Clear search'` | Accessible name of the search box's clear button. | | `loadingChildren` | `string` | `'Loading…'` | Shown while a node's lazy children are loading. | | `childrenLoadFailed` | `string` | `'Could not load these items.'` | Shown when `loadChildren` rejected. | | `noData` | `string` | `'No items to display'` | Shown when the tree has no nodes at all. | | `noSearchResults` | `string` | `'No matching items'` | Shown when a search matched nothing. | #### Types _Behavioural defaults_ | Name | Type | Description | | --- | --- | --- | | `itemHeight` | `number \| undefined` | Default row height used by `virtualScroll`. | | `expandEvent` | `'click' \| 'dblclick' \| undefined` | Default for the `expandEvent` prop. | | Name | Type | Description | | --- | --- | --- | | `OgeTreeViewConfigProvider` | `(props: { config?: OgeTreeViewConfigInput; children?: ReactNode }) => JSX.Element` | Subtree defaults — the React counterpart of `provideOgeTreeViewConfig()`; shallow-merges `messages` over the built-ins. | | `useOgeTreeViewConfig()` | `() => OgeTreeViewConfig` | Reads the resolved config of the nearest provider — the hook behind the component, and the React counterpart of injecting `OGE_TREE_VIEW_CONFIG`. | ### #### Properties _Layout_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `opened / defaultOpened / onOpenedChange` | `boolean \| (opened: boolean) => void` | `false` | Whether the drawer is open. Controlled through `opened` + `onOpenedChange`, uncontrolled through `defaultOpened` — the React halves of Angular’s `[(opened)]` model. | | `mode` | `'overlay' \| 'push' \| 'side'` | `'overlay'` | `overlay` floats over the content, `push` shifts it aside without resizing it, `side` shrinks it so both share the row. **This also decides modality** — see the accessibility group. | | `position` | `'start' \| 'end' \| 'top' \| 'bottom'` | `'start'` | Edge the panel is attached to. Logical, so `start`/`end` mirror in RTL with no flag to set. | | `size` | `number \| string` | `260` | Size of the open panel along its cross axis. A number means pixels. | | `minSize` | `number \| string \| undefined` | `—` | Size of the _closed_ panel — the compact rail that keeps icons visible. Only meaningful for `mode: 'side'`: a rail belongs to the layout, and a modal drawer still partly on screen is not closed. | | `compactBelow` | `number \| undefined` | `—` | Below this **container** inline size the drawer downgrades to `'overlay'` and closes. Measured against the drawer's own box, never the window, so a drawer nested in a dialog or a split pane adapts to the room it actually has. | | `disabled` | `boolean` | `false` | Blocks every open and close gesture, the handle’s `open()` and `close()` included. A drawer already open stays open and stays usable; only a `compact` close still goes through, because a drawer with no room left must stop covering the content. | | `animationEnabled` | `boolean` | `true` | Enables the open/close transition. | | `animationDuration` | `number` | `240` | Duration of that transition in milliseconds. Suppressed entirely under `prefers-reduced-motion`. | | `className / style` | `string \| CSSProperties` | `—` | Merged onto the drawer host. `className` is appended to the generated `oge-drawer*` classes; the Angular host takes `class`/`style` natively. | _Dismissal_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `showCloseButton` | `boolean` | `false` | Renders the built-in close button in the panel, labelled by the `close` message. It closes through the full pipeline, so `closeGuard` still applies. | | `shading` | `boolean` | `true` | Renders the backdrop of a modal drawer. A persistent drawer never shades the content it shares the row with. | | `closeOnEscape` | `boolean` | `true` | Escape closes a modal drawer, and only when it is the topmost overlay — a popup opened inside it closes first. A persistent drawer never takes Escape from the page. | | `closeOnBackdropClick` | `boolean` | `true` | A click on the backdrop closes the drawer. Only a press that _started_ on the backdrop counts, so a drag ending there does not close it. | | `closeGuard` | `(() => boolean \| Promise) \| undefined` | `—` | Vetoes a close. Return `false`, throw, or reject to keep the drawer open; a promise reports pending through the handle’s `closePending` and `onClosePendingChange`, and a second gesture meanwhile is dropped. | | `scrollLock` | `boolean` | `false` | Locks body scroll while a modal drawer is open, through the same ref-counted lock every other OGE overlay uses. Off by default, because a drawer is usually an in-page region rather than a page-level dialog. | _Accessibility_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `landmark` | `'navigation' \| 'complementary' \| 'region'` | `'navigation'` | Landmark role while persistent. Ignored while modal, which is always `role="dialog"`. | | `ariaLabel` | `string \| undefined` | `—` | Accessible name of the panel. Falls back to the `drawer` message. | | `ariaLabelledBy` | `string \| undefined` | `—` | id of an element naming the panel. Wins over `ariaLabel`, which is then cleared so there is only one name. | | `messages` | `Partial \| undefined` | `—` | Per-instance overrides of the config strings — the panel’s fallback accessible name (`drawer`) and the close button’s label (`close`). | | `autoFocus` | `'first-tabbable' \| 'panel' \| 'none' \| string` | `'first-tabbable'` | Where focus lands when a _modal_ drawer opens; any other string is a CSS selector, and an `[autofocus]` element always wins. A persistent drawer never moves focus. | | `restoreFocus` | `boolean` | `true` | Returns focus to the opener on close, but only when focus would otherwise be orphaned — it never steals a target the user has moved to. | | `inertBackground` | `boolean` | `true` | Marks the content behind a modal drawer `inert`, so neither Tab nor assistive tech can reach it. None of the four reference drawers does this. | | `drawerId` | `string (handle)` | `—` | id of the panel element, read from the ref. The panel stays in the DOM while closed, so a trigger's `aria-controls` always resolves to a real element. | #### Methods _Handle (ref)_ | Name | Type | Description | | --- | --- | --- | | `open()` | `void` | Opens the drawer through the cancelable pre-event. | | `close()` | `void` | Closes through the full pipeline (`onClosing` → `closeGuard`) with reason `'api'`. | | `toggle(force?: boolean)` | `void` | Opens when closed, closes when open; `force` drives it to a known state. | | `focus()` | `void` | Re-applies the initial-focus resolution. No-op unless the drawer is open and modal. | | `closePending` | `boolean` | True while an async `closeGuard` is in flight. `onClosePendingChange` reports the same transitions as a callback. | #### Events _Callbacks_ | Name | Type | Description | | --- | --- | --- | | `onOpening` | `(event: OgeDrawerOpeningEvent) => void` | Cancelable — set `cancel` to keep the drawer closed; the controlled value is reset for you. | | `onAfterOpened` | `() => void` | The drawer finished opening. Fires on the render pass rather than `transitionend`, because the transition is CSS-only and `prefers-reduced-motion` zeroes it. | | `onClosing` | `(event: OgeDrawerClosingEvent) => void` | Cancelable, carries the `reason`. Runs before `closeGuard`. | | `onClosed` | `(event: OgeDrawerClosedEvent) => void` | The drawer finished closing. | | `onModeChanged` | `(event: OgeDrawerModeChangedEvent) => void` | The resolved layout mode changed, carrying the requested mode and whether `compactBelow` forced it. | | `onClosePendingChange` | `(pending: boolean) => void` | Fires whenever the async `closeGuard` starts or settles — the callback half of the handle’s `closePending`. | #### Types _Content slots_ | Name | Type | Description | | --- | --- | --- | | `panel` | `ReactNode` | The drawer panel itself — the React counterpart of the `[ogeDrawerPanel]` attribute slot. | | `children` | `ReactNode` | Everything the drawer sits next to: the content `overlay` covers, `push` shifts and `side` shrinks. | _Types_ | Name | Type | Description | | --- | --- | --- | | `OgeDrawerMode` | `'overlay' \| 'push' \| 'side'` | Layout mode, and therefore modality. | | `OgeDrawerPosition` | `'start' \| 'end' \| 'top' \| 'bottom'` | Edge the panel attaches to; logical for RTL. | | `OgeDrawerLandmark` | `'navigation' \| 'complementary' \| 'region'` | Landmark role of a persistent drawer. | | `OgeDrawerAutoFocus` | `'first-tabbable' \| 'panel' \| 'none' \| string` | Initial-focus strategy of a modal drawer. | | `OgeDrawerCloseReason` | `'api' \| 'escape' \| 'backdrop' \| 'outside' \| 'compact'` | Why the drawer closed. | | `OgeDrawerOpeningEvent` | `{ cancel: boolean }` | Cancelable pre-event for opening. | | `OgeDrawerClosingEvent` | `{ cancel: boolean; reason: OgeDrawerCloseReason }` | Cancelable pre-event for closing. | | `OgeDrawerClosedEvent` | `{ reason: OgeDrawerCloseReason }` | Payload of `onClosed`. | | `OgeDrawerModeChangedEvent` | `{ mode; requestedMode; compact: boolean }` | Payload of `onModeChanged`. | | `OgeDrawerProps / OgeDrawerHandle` | `interface` | Props of `` and the shape of its `ref`. | | `resolveDrawerMode()` | `(request: OgeDrawerModeRequest) => OgeDrawerModeResult` | The pure function in `@oge-ui/behavior` that decides whether a drawer keeps its mode or goes compact. DOM-free, so the rule is unit-tested on its own — and shared verbatim with the Angular drawer. A non-positive `containerSize` means “not measured yet” and the requested mode is returned unchanged. | ### Drawer configuration #### Properties _<OgeDrawerConfigProvider>_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `mode` | `OgeDrawerMode` | `—` | Default for the `mode` prop. | | `position` | `OgeDrawerPosition` | `—` | Default for the `position` prop. | | `size` | `number \| string` | `—` | Default for the `size` prop. | | `messages.drawer` | `string` | `'Drawer'` | Accessible name of the panel when the application supplies none. | | `messages.close` | `string` | `'Close drawer'` | Label of the built-in close button. | | `useOgeDrawerConfig()` | `() => OgeDrawerConfig` | `—` | Reads the resolved config of the nearest provider, merged over `OGE_DEFAULT_DRAWER_CONFIG` — the hook behind the component, exported for drawers you compose yourself. | ### #### Properties _Steps & selection_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `activeIndex / defaultActiveIndex / onActiveIndexChange` | `number \| (index: number) => void` | `0` | Index of the active step — controlled through `activeIndex` + `onActiveIndexChange`, uncontrolled through `defaultActiveIndex`. | | `activeKey / defaultActiveKey / onActiveKeyChange` | `string \| undefined \| (key: string \| undefined) => void` | `—` | Key of the active step. Resolved before the index, so an initial key binding wins over the index default on first run. | | `steps` | `readonly OgeStepDefinition[] \| undefined` | `—` | The steps, in render order — the shared `OgeStepData` fields plus the React content slots. React has no declarative `` child to project, so this array is the only step source. | | `linear` | `boolean` | `false` | Blocks moving past a step that is neither `completed` nor `optional`. The default matches Material and PrimeNG; Kendo is the outlier at `true`. | | `disabled` | `boolean` | `false` | Blocks every step change. | _Layout & chrome_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Main axis of the step list. **The ARIA semantics do not change with it** — see the accessibility group. | | `display` | `'full' \| 'label' \| 'indicator'` | `'full'` | How much of each header renders: label plus description, label only, or just the round indicator. | | `showNavigation` | `boolean` | `false` | Renders the built-in Back / Next bar, which becomes Finish on the last step. None of the three reference steppers ships one. | | `deferRendering` | `boolean` | `false` | Creates a step's body on first activation. | | `keepAlive` | `boolean` | `true` | Keeps a body mounted after the user leaves it. | | `className / style` | `string \| CSSProperties` | `—` | Merged onto the stepper host. `className` is appended to the generated `oge-stepper*` classes; the Angular host takes `class`/`style` natively. | _Accessibility_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `keyboardNavigation` | `boolean` | `false` | Adds arrow / Home / End over the headers. Off by default because the headers are buttons in a list, not tabs, so they are already Tab-reachable. It moves focus only, and deliberately does **not** wrap. | | `ariaLabel` | `string \| undefined` | `—` | Accessible name of the step list. Falls back to the `stepper` message. | | `messages` | `Partial \| undefined` | `—` | Per-instance overrides of the config strings — the list’s accessible name, the optional/completed/invalid announcements and the navigation bar’s labels. | | `stepperId` | `string (handle)` | `—` | id prefix of the generated header / panel pairs, read from the ref. | | `changePending` | `boolean (handle)` | `—` | True while an async `stepGuard` is in flight. `onChangePendingChange` reports the same transitions as a callback. | #### Methods _Handle (ref)_ | Name | Type | Description | | --- | --- | --- | | `next(event?: Event)` | `void` | Advances one step, or confirms the finish when already on the last one. The guard runs either way, so a final step can still veto. | | `previous(event?: Event)` | `void` | Goes back one step, through the same pipeline. | | `goTo(target: number \| string, event?: Event)` | `void` | Moves to a step by index or key. | | `reset()` | `void` | Clears the rendered-body cache and returns to the first step. | | `focus()` | `void` | Focuses the active step's header. | #### Events _Callbacks_ | Name | Type | Description | | --- | --- | --- | | `onStepChanging` | `(event: OgeStepChangingEvent) => void` | Cancelable, called before the leaving step’s `stepGuard` runs. | | `onStepChanged` | `(event: OgeStepChangedEvent) => void` | The active step changed. | | `onStepBlocked` | `(event: OgeStepBlockedEvent) => void` | A move was refused, carrying `reason: 'linear' \| 'editable' \| 'guard' \| 'disabled'`. Angular Material refuses silently. | | `onFinished` | `(event: OgeStepperFinishEvent) => void` | `next()` was confirmed on the last step. | | `onChangePendingChange` | `(pending: boolean) => void` | Fires whenever an async `stepGuard` starts or settles — the callback half of the handle’s `changePending`. | #### Types _OgeStepDefinition (a steps entry)_ | Name | Type | Description | | --- | --- | --- | | `key / label / description` | `string` | Identity and header text. | | `icon / iconClass` | `string \| undefined` | SVG path data, or class(es) for an icon font — replacing the step number. | | `completed / optional / editable` | `boolean` | The linear gate: `completed` lets a linear stepper past, `optional` lets it past regardless, and `editable: false` blocks coming _back_. | | `errorMessage` | `string \| undefined` | Shown under the label while `invalid`, replacing `description` so two sub-lines never compete. Angular Material has this; Kendo and PrimeNG do not. | | `invalid / disabled / visible` | `boolean` | Error state, non-activatable, and removal from the list. | | `stepGuard` | `() => boolean \| Promise` | Veto hook run when leaving this step. A throw and a rejection both veto. | | `content` | `ReactNode` | The step body — the React counterpart of what an `` projects. | _Types & render props_ | Name | Type | Description | | --- | --- | --- | | `OgeStepState` | `'number' \| 'active' \| 'done' \| 'error'` | Derived indicator state; error outranks done, so a completed step that later fails still reads as needing attention. | | `OgeStepperOrientation` | `'horizontal' \| 'vertical'` | Main axis; the ARIA model is the same for both. | | `OgeStepperDisplay` | `'full' \| 'label' \| 'indicator'` | How much of a header renders. | | `OgeStepData` | `{ key?; label?; description?; icon?; iconClass?; disabled?; visible?; completed?; optional?; editable?; invalid?; cssClass?; stepGuard? }` | One step’s shared data — the same shape the Angular layer uses, from `@oge-ui/behavior`. `OgeStepDefinition` adds the React slots on top. | | `OgeStepGuard` | `() => boolean \| Promise` | Behavior's `OgeAsyncGuard`, the same veto contract the tabs' close guard and the accordion's expand guard use. | | `next() / previous() on the handle` | `OgeStepperHandle` | The React counterpart of the `[ogeStepperNext]` / `[ogeStepperPrevious]` directives: any button drives the stepper through the same pipeline by calling the ref, from inside a step body or from outside — which Material’s equivalents cannot do. | | `renderHeader / renderIndicator / renderContent` | `(context) => ReactNode` | Render props replacing `[ogeStepHeaderTemplate]`, `[ogeStepIndicatorTemplate]` and `[ogeStepContentTemplate]`: the label block, the round indicator, or a lazily built body. Set on the stepper for all steps, or on one `steps` entry. | | `Stepper inside a form` | `@oge-ui/react-inputs` | Angular’s `` wrapper has no React counterpart yet — `@oge-ui/forms` ships no React layer. Bind the editors to your own state and compute each step's `completed` / `invalid` from it; the linear gate and the per-step error display are the stepper's own. | ### Stepper configuration #### Properties _<OgeStepperConfigProvider>_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `orientation / display / linear` | `defaults` | `—` | Defaults for the matching props. | | `messages.stepper` | `string` | `'Steps'` | Accessible name of the step list. | | `messages.optional` | `string` | `'Optional'` | Sub-label of an optional step, wired through `aria-describedby`. | | `messages.completed / messages.invalid` | `string` | `'Completed' / 'Has errors'` | Announced in visually hidden text, because the indicator glyph is `aria-hidden`. | | `messages.previous / next / finish` | `string` | `'Back' / 'Next' / 'Finish'` | Labels of the built-in navigation bar. | | `useOgeStepperConfig()` | `() => OgeStepperConfig` | `—` | Reads the resolved config of the nearest provider, merged over `OGE_DEFAULT_STEPPER_CONFIG` — the hook behind the component, exported for steppers you compose yourself. | ### #### Properties _Data_ | Name | Type | Description | | --- | --- | --- | | `items` | `readonly OgeMenubarItemData[] \| undefined` | Data-driven item tree; children at any depth open as nested submenus. The **only** item API in React — the Angular layer also accepts declarative `` children, which React cannot mirror because `key` is reserved by React itself. | | `activeKey` | `string \| undefined` | The item `key` rendered with `aria-current="page"` and the active style. Consumer-driven — bind it from your router; the menubar itself takes no router dependency. | | `messages` | `Partial \| undefined` | Per-instance overrides of the user-facing strings, merged over ``. | _Behavior_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | A vertical bar announces `aria-orientation="vertical"` and swaps the arrow axes: Up/Down traverse, ArrowRight opens the submenu beside the bar. | | `openMode` | `'click' \| 'hover'` | `'click'` | How **top-level** submenus open. Nested levels always open on hover and on ArrowRight/Enter — the reference libraries’ first-vs-nested split baked in as behavior. With a menu open, hovering siblings switches it in either mode. | | `hoverDelay` | `number` | `100` | Hover dwell before a top-level submenu opens in `'hover'` mode, in ms. Nested levels use the overlay config's `menuShowDelayMs`/`menuHideDelayMs` (50/300). | | `compactBelow` | `number \| undefined` | `—` | Below this **container** inline size the whole bar collapses into a hamburger button opening the full tree as one nested menu. Measured against the menubar's own box, never the window. | | `disabled` | `boolean` | `false` | Disables the whole bar: every item goes inert and the bar leaves the Tab sequence. | | `renderSubmenuItem` | `(item: OgeMenuItem, index: number) => ReactNode` | `—` | Custom rendering for submenu rows at **every depth** — the shared menu-list renderer, the React face of `[submenuItemTemplate]`. Top-level bar items use `renderItem` instead. | | `renderItem` | `(item: OgeMenubarItemData, index: number) => ReactNode` | `—` | Replaces the interior of the **top-level** bar items — the React face of `[ogeMenubarItemTemplate]` (documented in the Angular block’s types table). The caret, roles and roving tabindex stay with the component. | _Host_ | Name | Type | Description | | --- | --- | --- | | `className` | `string \| undefined` | Extra classes on the menubar element — the React host styling idiom; an Angular host takes `class` natively. | | `style` | `CSSProperties \| undefined` | Inline styles on the menubar element. | #### Methods _OgeMenubarHandle (ref)_ | Name | Type | Description | | --- | --- | --- | | `open(target: number \| string)` | `void` | Opens the submenu of a top-level item, by index or `key`. Runs through the cancelable `onSubmenuOpening` pipeline. | | `close()` | `void` | Closes any open submenu through the cancelable `onSubmenuClosing` pipeline with `reason: 'api'`. | | `focus()` | `void` | Focuses the bar's roving tab target — or the hamburger button when compact. | #### Events | Name | Type | Description | | --- | --- | --- | | `onItemClick` | `(event: OgeMenubarItemClickEvent) => void` | A leaf item was activated, at any depth. Carries the item, its `key` and the hierarchical index `path` from the bar down. | | `onSubmenuOpening` | `(event: OgeMenubarSubmenuOpeningEvent) => void` | **Cancelable** — set `cancel` to keep the submenu closed. `item` is `undefined` for the compact hamburger menu (empty `path`). | | `onSubmenuOpened` | `(event: OgeMenubarSubmenuOpenedEvent) => void` | A top-level submenu (or the hamburger menu) opened. | | `onSubmenuClosing` | `(event: OgeMenubarSubmenuClosingEvent) => void` | **Cancelable** — set `cancel` to keep the submenu open. Fires for closes the menubar itself initiates (`escape`, `select`, `navigation`, `api`); overlay-owned closes (`outside`) and Tab only report `onSubmenuClosed`. | | `onSubmenuClosed` | `(event: OgeMenubarSubmenuClosedEvent) => void` | A submenu closed, with its `reason`. | | `onCompactChanged` | `(event: OgeMenubarCompactChangedEvent) => void` | The bar collapsed into (or recovered from) the compact hamburger. | #### Types | Name | Type | Description | | --- | --- | --- | | `OgeMenubarItemData` | `interface` | The canonical overlay `OgeMenuItem` narrowed recursively — `badge` and `shortcut` included — plus `key` (identity for `activeKey`/`open()`/events), `url` (renders the item as a real `` at the bar **and** at any submenu depth; `onItemClick` fires first so `preventDefault()` hands navigation to a router) and `visible`. Submenus come from `items`. | | `OgeMenubarCloseReason` | `'escape' \| 'outside' \| 'select' \| 'tab' \| 'navigation' \| 'api'` | Why a submenu closed. `'navigation'` is a Left/Right or hover switch to a sibling top-level item. | | `OgeMenubarItemClickEvent` | `{ item; key?; index; path; event }` | `path` is the hierarchical index chain from the bar down to the item; `index` is its last entry. | | `OgeMenubarHandle` | `interface` | The `ref` handle: `open`, `close`, `focus` — the React face of the Angular public methods. | | `OgeMenubarProps` | `interface` | Props of ``, render props included. | ### OgeMenubarItem (OgeMenubarItemData) #### Properties | Name | Type | Default | Description | | --- | --- | --- | --- | | `text` | `string` | `''` | Label of the item. | | `key` | `string \| undefined` | `—` | Stable identity used by `activeKey`, the handle’s `open()` and event payloads. Not React’s `key` — an item is data, so nothing collides. | | `value` | `unknown` | `—` | Consumer-defined value carried through click events. | | `url` | `string \| undefined` | `—` | Renders the item as a real link (``). | | `hint` | `string \| undefined` | `—` | Tooltip (native `title`) — e.g. why an item is disabled. | | `icon` | `string \| undefined` | `—` | SVG path data (`d`) for a leading `aria-hidden` icon. | | `iconClass` | `string \| undefined` | `—` | Class(es) for a leading icon element — the icon-font hook. | | `disabled` | `boolean` | `false` | Disabled items are exposed (`aria-disabled`) but inert and skipped by the arrow keys. | | `visible` | `boolean` | `true` | `false` removes the item and its subtree. | | `separator` | `boolean` | `false` | Renders a divider (`role="separator"`); every other field is ignored. | ### Menubar configuration #### Properties _OgeMenubarConfigProvider_ | Name | Type | Description | | --- | --- | --- | | `messages` | `OgeMenubarMessages` | Every user-facing string: `menubar` (accessible name of the bar, default `Menu bar`) and `hamburger` (aria label of the compact button, default `Menu`). | | `openMode` | `'click' \| 'hover' \| undefined` | Default for the `openMode` prop. | | `hoverDelay` | `number \| undefined` | Default for the `hoverDelay` prop, in ms. | | `orientation` | `'horizontal' \| 'vertical' \| undefined` | Default for the `orientation` prop. | | `compactBelow` | `number \| undefined` | Default for the `compactBelow` prop. | ### #### Properties | Name | Type | Default | Description | | --- | --- | --- | --- | | `items` | `readonly OgeBreadcrumbItemData[] \| undefined` | `—` | Data-driven trail — a flat list, never nested. The **only** crumb API in React: the Angular layer also accepts declarative `` children, which React cannot mirror because `key` is reserved by React itself. | | `collapseMode` | `'auto' \| 'wrap' \| 'none'` | `'auto'` | `'auto'` collapses the **oldest middle** crumbs into an ellipsis menu against the breadcrumb's own **container** width (never the window) — the first and last crumb always stay visible, and the collapsed crumbs remain reachable as real links. `'wrap'` breaks onto multiple rows; `'none'` keeps one scrollable row. The fitting arithmetic is core's pure `fitToolbarItems`. | | `messages` | `Partial \| undefined` | `—` | Per-instance overrides of the user-facing strings, merged over ``. | | `renderItem` | `(context: OgeBreadcrumbItemRenderContext) => ReactNode` | `—` | Replaces the crumb's interior only — the link/current/disabled element semantics stay with the component. The React face of `[ogeBreadcrumbItemTemplate]` (documented in the Angular block’s types table); the context carries `item`, `index` and `last`. | | `renderSeparator` | `(context: OgeBreadcrumbSeparatorRenderContext) => ReactNode` | `—` | Replaces the default chevron separator — the React face of `[ogeBreadcrumbSeparatorTemplate]`. Rendered `aria-hidden`: a separator is decoration, never content (APG). | _Host_ | Name | Type | Description | | --- | --- | --- | | `className` | `string \| undefined` | Extra classes on the breadcrumb element — the React host styling idiom; an Angular host takes `class` natively. | | `style` | `CSSProperties \| undefined` | Inline styles on the breadcrumb element. | | `id` | `string \| undefined` | Id of the breadcrumb element; the crumbs derive their own ids independently. | #### Methods _OgeBreadcrumbHandle (ref)_ | Name | Type | Description | | --- | --- | --- | | `focus()` | `void` | Focuses the first interactive crumb — or the ellipsis button when the trail is collapsed. | #### Events | Name | Type | Description | | --- | --- | --- | | `onItemClick` | `(event: OgeBreadcrumbItemClickEvent) => void` | A crumb (inline or inside the ellipsis menu) was activated. **Not fired** by disabled crumbs or by the last crumb — that is the current page. On `url` crumbs, `event.event.preventDefault()` hands navigation to a router. | #### Types | Name | Type | Description | | --- | --- | --- | | `OgeBreadcrumbItemData` | `{ text; key?; value?; url?; hint?; icon?; iconClass?; disabled?; visible? }` | A deliberately narrow interface — no submenu, checked or shortcut fields, because none of them mean anything on a trail. `url` renders the crumb as a real `` (ignored on the last crumb); `disabled` crumbs are exposed via `aria-disabled` but inert; `visible: false` removes the crumb. | | `OgeBreadcrumbCollapseMode` | `'auto' \| 'wrap' \| 'none'` | How the breadcrumb behaves when room runs out. | | `OgeBreadcrumbItemClickEvent` | `{ item; key?; index; event }` | `index` is the position within the full trail, collapsed crumbs included. | | `OgeBreadcrumbItemRenderContext` | `{ item; index; last }` | Context of `renderItem` — the React face of `OgeBreadcrumbItemTemplateContext`. `last` is `true` on the current page’s crumb. | | `OgeBreadcrumbSeparatorRenderContext` | `{ index }` | Context of `renderSeparator` — the index of the crumb the separator precedes. | | `OgeBreadcrumbHandle` | `interface` | The `ref` handle: `focus` — the React face of the Angular public method. | | `OgeBreadcrumbProps` | `interface` | Props of ``, render props included. | ### OgeBreadcrumbItem (OgeBreadcrumbItemData) #### Properties | Name | Type | Default | Description | | --- | --- | --- | --- | | `text` | `string` | `''` | Label of the crumb. | | `key` | `string \| undefined` | `—` | Stable identity used in event payloads and DOM ids. Not React’s `key` — a crumb is data, so nothing collides. | | `value` | `unknown` | `—` | Consumer-defined value carried through click events. | | `url` | `string \| undefined` | `—` | Renders the crumb as a real link (``). | | `hint` | `string \| undefined` | `—` | Tooltip (native `title`). | | `icon` | `string \| undefined` | `—` | SVG path data (`d`) for a leading `aria-hidden` icon. | | `iconClass` | `string \| undefined` | `—` | Class(es) for a leading icon element — the icon-font hook. | | `disabled` | `boolean` | `false` | Disabled crumbs are exposed (`aria-disabled`) but inert. | | `visible` | `boolean` | `true` | `false` removes the crumb entirely. | ### Breadcrumb configuration #### Properties _OgeBreadcrumbConfigProvider_ | Name | Type | Description | | --- | --- | --- | | `messages` | `OgeBreadcrumbMessages` | Every user-facing string: `breadcrumb` (accessible name of the `` landmark, default `Breadcrumb`) and `collapsed` (aria label of the ellipsis button, default `Show hidden items`). | | `collapseMode` | `'auto' \| 'wrap' \| 'none' \| undefined` | Default for the `collapseMode` prop. | ### #### Properties _OgePagination_ | Name | Type | Default | Description | | --- | --- | --- | --- | | `pageIndex` | `number \| undefined` | `0` | The current page — **0-based**, controlled; pair it with `onPageIndexChange`. Auto-clamped when the page count shrinks (an implicit `onPageIndexChange`, no rich event). DevExtreme migrators: check your origin — dx documentation is ambiguous about its base. | | `defaultPageIndex` | `number \| undefined` | `0` | Initial page of the **uncontrolled** mode — the half of Angular’s `[(pageIndex)]` model React splits out. Ignored once `pageIndex` is passed. | | `pageSize` | `number \| undefined` | `20` | Items per page — controlled; pair it with `onPageSizeChange`. `0` means "all items on one page" (the grid pager contract, kept aligned for eventual delegation). | | `defaultPageSize` | `number \| undefined` | `20` | Initial size of the **uncontrolled** mode. Ignored once `pageSize` is passed. | | `itemCount` | `number \| undefined` | `—` | Total items. `undefined` = unknown total: only prev/next and a "Page N" indicator render, and **next never disables** — clamp `pageIndex` yourself when the server reports the end. | | `pageSizes` | `readonly (number \| 'all')[] \| undefined` | `—` | Page-size choices; `'all'` adds the unpaged option. **Presence shows the selector** — no separate boolean (DevExtreme's `showPageSizeSelector` is deliberately skipped). | | `showInfo` | `boolean` | `false` | Renders the `{from}–{to} of {itemCount}` range (the `info` message template) in an `aria-live="polite"` region. | | `showFirstLastButtons` | `boolean` | `false` | First/last jump buttons (Material name and default) — the numeric window already renders both rail pages, so they are opt-in chrome. | | `showNavigationButtons` | `boolean` | `true` | Prev/next buttons; forced on in compact and unknown-total modes (they are the backbone there). | | `showJumpToPageInput` | `boolean` | `false` | Jump-to-page input (PrimeNG name; Kendo's `type: 'input'`): 1-based display, clamped into range, display re-synced after a clamp. Hidden while the total is unknown. **React idiom:** the input is uncontrolled and commits on **blur or Enter** — React has no `onChange` bound to the native `change` event, which is what the Angular version listens to alongside `(keydown.enter)`. | | `maxButtons` | `number \| undefined` | `7 (config)` | Total rendered slots **including ellipsis slots** — the window width never changes while paging, so the bar never jitters. An ellipsis hiding a single page renders the page instead. | | `displayMode` | `'full' \| 'compact' \| 'adaptive'` | `'full' (config)` | `'compact'` renders the `N / M` indicator; `'adaptive'` switches below `compactBelow` (config, default 480px), measured against the bar's own container via ResizeObserver — never the window. | | `disabled` | `boolean` | `false` | Disables every control (native `disabled` — they are all real buttons/selects/inputs). | | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Density preset (26/32/40px hit targets). | | `messages` | `Partial` | `—` | Per-instance string overrides, merged over the config messages. Several bars on one page need distinct `paginationLabel` values — landmarks must be unique (axe `landmark-unique`). | | `className / style / id` | `string \| CSSProperties \| string` | `—` | Merged onto the pagination host. `className` is appended to the generated `oge-pagination*` classes; the Angular host takes `class`/`style`/`id` natively. | #### Methods _OgePaginationHandle (ref)_ | Name | Type | Description | | --- | --- | --- | | `firstPage() / lastPage() / nextPage() / previousPage()` | `void` | Programmatic paging (Material names). `lastPage()` no-ops while the total is unknown. State updates only — no rich event (no user event). | | `hasPreviousPage() / hasNextPage()` | `boolean` | `hasNextPage()` returns `true` while the total is unknown — the component cannot know the end. | | `pageCount()` | `number \| undefined` | Total pages; `undefined` while the total is unknown. DevExtreme's `getPageCount()` — a handle method rather than the Angular signal, because React has no signal to read off the instance. | | `focus()` | `void` | Moves keyboard focus to the first enabled control. | #### Events | Name | Type | Description | | --- | --- | --- | | `onPageChanged` | `(event: OgePaginationPageChangedEvent) => void` | `{ pageIndex, previousPageIndex, pageSize, event }` — user interactions only; programmatic writes and auto-clamps update the state without it. | | `onPageSizeChanged` | `(event: OgePaginationPageSizeChangedEvent) => void` | `{ pageSize, previousPageSize, pageIndex, event }` — `pageIndex` reports the **post-clamp** page (changing the size can move the current page). | | `onPageIndexChange / onPageSizeChange` | `(value: number) => void` | The controlled halves of Angular’s `[(pageIndex)]` / `[(pageSize)]` models — fire on every change including programmatic writes and auto-clamps. | #### Types | Name | Type | Description | | --- | --- | --- | | `OgePaginationDisplayMode / OgePaginationSize` | `types` | The string unions of the mode and density props, re-exported from `@oge-ui/behavior`. | | `OgePaginationProps / OgePaginationHandle` | `interface` | The props of `` and the shape of its `ref` handle. | | `resolvePageWindow / resolvePageRange / resolvePageCount / OGE_PAGE_ELLIPSIS` | `@oge-ui/behavior` | The DOM-free paging kernel (`pagination-math.ts`): the constant-width page window with real ellipsis markers, the from/to info arithmetic and the never-below-1 page-count division — the **same module** the Angular component runs on, unit-tested without a DOM. | ### Pagination configuration #### Properties _OgePaginationConfig_ | Name | Type | Description | | --- | --- | --- | | `messages` | `OgePaginationMessages` | All strings: `paginationLabel` (the `` name), `firstPage`/`lastPage`/`previousPage`/`nextPage`, `pageLabel` (`{page}`, 1-based), `info` (`{from}` `{to}` `{itemCount}`), `pageInfoUnknown`, `pageIndicator` (`{page}` `{pageCount}`), `pageSizeLabel`, `allRows`, `jumpLabel`. | | `displayMode / compactBelow / maxButtons` | `OgePaginationDisplayMode / number / number` | Subtree-wide prop defaults; the component resolves `prop ?? config ?? literal` (480px / 7). | #### Methods | Name | Type | Description | | --- | --- | --- | | `OgePaginationConfigProvider` | `(props: { config?: OgePaginationConfigInput; children?: ReactNode }) => JSX.Element` | Subtree defaults — the React counterpart of `provideOgePaginationConfig()`; shallow-merges `messages` over the built-ins. | | `useOgePaginationConfig()` | `() => OgePaginationConfig` | Reads the resolved config of the nearest provider, merged over `OGE_DEFAULT_PAGINATION_CONFIG` — the hook behind the component, exported for pagination chrome you compose yourself. | ### Demos Each demo below is a complete standalone component — copy one whole and it compiles. #### Getting started ```ts 'use client'; import { useState } from 'react'; import { OgeBreadcrumb } from '@oge-ui/react-navigation'; import type { OgeBreadcrumbItemData, OgeBreadcrumbItemClickEvent } from '@oge-ui/react-navigation'; // The APG breadcrumb: a nav landmark holding an ordered list of // links, the current page carrying aria-current="page". The last crumb is // never interactive — you are already there — and disabled crumbs stay // visible but inert. No roving tabindex: the APG defines no keyboard // behavior for a breadcrumb, so none is invented. const trail: OgeBreadcrumbItemData[] = [ { text: 'Home', key: 'home', url: '/', icon: 'M2 8 8 2l6 6M4 7v7h8V7' }, { text: 'Products', key: 'products', url: '/products' }, { text: 'Keyboards', key: 'keyboards', url: '/products/keyboards' }, { text: 'Mechanical' }, ]; export function BreadcrumbBasicsDemo() { const [last, setLast] = useState('—'); const go = (event: OgeBreadcrumbItemClickEvent) => { setLast(`${event.key ?? event.item.text} [${event.index}]`); }; return ( <>

Last click: {last}

); } ``` #### Declarative items ```ts 'use client'; import { OgeBreadcrumb } from '@oge-ui/react-navigation'; import type { OgeBreadcrumbItemData } from '@oge-ui/react-navigation'; // The Angular counterpart of this section lists // elements. React has no such child: `key` is // reserved by React itself, so a crumb component could not carry the // identity `key` means here. A trail is the items array — flat, never // nested. const trail: OgeBreadcrumbItemData[] = [ { text: 'Home', key: 'home', url: '/' }, { text: 'Reports', key: 'reports', url: '/reports' }, { text: 'Q3 summary' }, ]; export function BreadcrumbItemsDemo() { return ( ); } ``` #### Collapse modes ```ts 'use client'; import { useState } from 'react'; import { OgeBreadcrumb } from '@oge-ui/react-navigation'; import type { OgeBreadcrumbItemData } from '@oge-ui/react-navigation'; // collapseMode 'auto' (default) measures the breadcrumb's OWN // container, never the window. When room runs out the OLDEST middle crumbs // collapse first — first and last always stay visible — and unlike the // references the collapsed crumbs remain reachable: the ellipsis opens // them as real links. 'wrap' breaks onto rows, 'none' keeps one // scrollable row. const trail: OgeBreadcrumbItemData[] = [ { text: 'Home', url: '/' }, { text: 'Products', url: '/products' }, { text: 'Peripherals', url: '/products/peripherals' }, { text: 'Keyboards', url: '/products/keyboards' }, { text: 'Mechanical' }, ]; export function BreadcrumbCollapseDemo() { const [width, setWidth] = useState(640); return ( <>
); } ``` #### Templates ```ts 'use client'; import { OgeBreadcrumb } from '@oge-ui/react-navigation'; import type { OgeBreadcrumbItemData } from '@oge-ui/react-navigation'; // The item render prop replaces the crumb's interior only — the // link/current/disabled element semantics stay with the component. The // separator render prop is rendered aria-hidden: a separator is // decoration. They are the React face of Angular's two ng-template slots. const trail: OgeBreadcrumbItemData[] = [ { text: 'Home', url: '/' }, { text: 'Library', url: '/library' }, { text: 'Data' }, ]; export function BreadcrumbTemplatesDemo() { return ( ( {item.text} )} renderSeparator={() => '·'} /> ); } ``` #### Configuration ```ts 'use client'; import { OgeBreadcrumb, OgeBreadcrumbConfigProvider } from '@oge-ui/react-navigation'; import type { OgeBreadcrumbItemData } from '@oge-ui/react-navigation'; // Every user-facing string lives in the messages block — the nav // landmark's label and the ellipsis button's label included. The context // provider is the React shape of provideOgeBreadcrumbConfig(). const trail: OgeBreadcrumbItemData[] = [ { text: 'Giriş', url: '/' }, { text: 'Raporlar' }, ]; export function BreadcrumbConfigDemo() { return ( ); } ``` #### Layout modes ```ts 'use client'; import { useState } from 'react'; import { OgeDrawer } from '@oge-ui/react-navigation'; import type { OgeDrawerMode } from '@oge-ui/react-navigation'; // One component, not the container/drawer/content trio the reference // libraries need. The panel is the `panel` node prop; `children` is // everything the drawer sits next to. export function DrawerModesDemo() { const [opened, setOpened] = useState(true); const [mode, setMode] = useState('side'); return ( Navigation…} >
Content that overlay covers, push shifts and side shrinks.
); } ``` #### Position ```ts 'use client'; import { useState } from 'react'; import { OgeDrawer } from '@oge-ui/react-navigation'; import type { OgeDrawerPosition } from '@oge-ui/react-navigation'; // start/end are logical: they mirror in RTL on their own, because there // is no rtlEnabled flag anywhere in this suite. export function DrawerPositionDemo() { const [opened, setOpened] = useState(false); const [position, setPosition] = useState('start'); return ( Panel} >
Content
); } ``` #### Modal drawer ```ts 'use client'; import { useState } from 'react'; import { OgeDrawer } from '@oge-ui/react-navigation'; // Modality is DERIVED from mode, never configured. overlay and push cover // or displace the content, so they are dialogs: role="dialog", aria-modal, // a focus trap, Escape and inert on the background. side is part of the // layout, so it is a landmark with none of those. // // An independent "modal" flag is exactly what lets a panel claim // role="complementary" and aria-modal="true" at the same time. export function DrawerModalDemo() { const [opened, setOpened] = useState(false); return ( Reports} >
Content
); } ``` #### Compact rail ```ts 'use client'; import { useState } from 'react'; import { OgeDrawer } from '@oge-ui/react-navigation'; // minSize is the closed size: the compact rail that keeps icons visible. // It only applies to mode="side", because a rail belongs to the layout and // a modal drawer still partly on screen is not closed. export function DrawerRailDemo() { const [opened, setOpened] = useState(false); return ( Icons, then labels once open} >
Content
); } ``` #### Responsive downgrade ```ts 'use client'; import { useState } from 'react'; import { OgeDrawer } from '@oge-ui/react-navigation'; import type { OgeDrawerModeChangedEvent } from '@oge-ui/react-navigation'; // compactBelow measures the drawer's OWN container, never the window, so a // drawer nested in a dialog or a split pane adapts to the room it actually // has. Below the threshold it downgrades to an overlay and closes, rather // than leaving a backdrop the user never asked for. export function DrawerCompactDemo() { const [opened, setOpened] = useState(true); const onModeChanged = (event: OgeDrawerModeChangedEvent): void => { console.log(event.mode, 'compact:', event.compact); }; return ( Navigation} >
Content
); } ``` #### Close guard ```ts 'use client'; import { useState } from 'react'; import { OgeDrawer } from '@oge-ui/react-navigation'; // closeGuard follows the overlay package's veto semantics: false, a throw // and a rejection all mean "stay open", a promise reports pending through // the handle's closePending (and onClosePendingChange), and a second // gesture meanwhile is dropped. export function DrawerGuardDemo() { const [opened, setOpened] = useState(true); const [dirty, setDirty] = useState(true); const confirmDiscard = (): boolean => !dirty || confirm('Discard your changes?'); return ( Unsaved edits…} >
Content
); } ``` #### App shell ```ts 'use client'; import { useState } from 'react'; import { OgeDrawer, OgeTreeView } from '@oge-ui/react-navigation'; import { OgeToolbar, OgeSplitter } from '@oge-ui/react-layout'; import type { OgeSplitterSize, OgeToolbarItemData } from '@oge-ui/react-layout'; // The whole app shell out of three OGE containers: a toolbar on top, a // drawer down the side holding the tree view that ships in the same // package, and a splitter dividing the workspace. // // compactBelow makes the shell responsive to its own width, so the same // markup works full-page and inside a preview card. const nav = [ { id: 1, parentId: null, text: 'Reports' }, { id: 2, parentId: 1, text: 'Monthly' }, { id: 3, parentId: null, text: 'Settings' }, ]; export function DrawerAppShellDemo() { const [menuOpen, setMenuOpen] = useState(true); const [sizes, setSizes] = useState([60, 40]); const commands: readonly OgeToolbarItemData[] = [ { key: 'menu', text: 'Menu' }, { key: 'save', text: 'Save', severity: 'accent', overflowPriority: 10 }, { key: 'help', text: 'Help', location: 'after', overflowPriority: -1 }, ]; return ( <> { if (event.key === 'menu') setMenuOpen((open) => !open); }} /> } > Rows… }, { key: 'detail', content:
Details…
}, ]} />
); } ``` #### Configuration ```ts 'use client'; import { useState } from 'react'; import { OgeDrawer, OgeDrawerConfigProvider } from '@oge-ui/react-navigation'; export function DrawerConfigDemo() { const [opened, setOpened] = useState(true); return ( {/* A single instance can still override the strings with the messages prop. */} Gezinme…} >
İçerik
); } ``` #### Getting started ```ts 'use client'; import { useState } from 'react'; import { OgeMenubar } from '@oge-ui/react-navigation'; import type { OgeMenubarItemData, OgeMenubarItemClickEvent } from '@oge-ui/react-navigation'; // role="menubar" with the full APG keyboard contract: roving // tabindex, Left/Right between items, Down/Enter opens, Escape unwinds, // type-ahead. Submenus run on the same every other menu in // the suite uses. const menu: OgeMenubarItemData[] = [ { text: 'File', items: [ // shortcut renders right-aligned and announces aria-keyshortcuts; // the actual key binding stays the application's job. { text: 'New', key: 'new', shortcut: 'Ctrl+N' }, { text: 'Open…', key: 'open', shortcut: 'Ctrl+O' }, { separator: true, text: '' }, { text: 'Share', badge: 2, items: [{ text: 'Email', key: 'email' }] }, ], }, { text: 'Edit', items: [{ text: 'Undo', key: 'undo', shortcut: 'Ctrl+Z' }] }, { text: 'Help', key: 'help' }, ]; export function MenubarBasicsDemo() { const [last, setLast] = useState('—'); const run = (event: OgeMenubarItemClickEvent) => { setLast(`${event.key ?? event.item.text} [${event.path.join(', ')}]`); }; return ( <>

Last click: {last}

); } ``` #### Declarative items ```ts 'use client'; import { OgeMenubar } from '@oge-ui/react-navigation'; import type { OgeMenubarItemData } from '@oge-ui/react-navigation'; // The Angular counterpart of this section nests // elements. React has no such child: `key` is // reserved by React itself, so an item component could not carry the // identity `key` means here. Nesting is the items array, all the way // down — the same tree the section above builds. const menu: OgeMenubarItemData[] = [ { text: 'File', items: [ { text: 'New', key: 'new' }, { separator: true, text: '' }, { text: 'Exit', key: 'exit' }, ], }, { text: 'Help', key: 'help' }, ]; export function MenubarItemsDemo() { return ( ); } ``` #### Open mode ```ts 'use client'; import { OgeMenubar } from '@oge-ui/react-navigation'; import type { OgeMenubarItemData } from '@oge-ui/react-navigation'; // openMode applies to the TOP level only: 'click' is the // desktop-menubar default, 'hover' opens after hoverDelay. Nested levels // always open on hover and on ArrowRight/Enter — the reference libraries' // first-vs-nested split baked in as behavior, not a second prop. Once a // menu is open, hovering siblings switches it in either mode. const menu: OgeMenubarItemData[] = [ { text: 'File', items: [{ text: 'New' }] }, { text: 'Edit', items: [{ text: 'Undo' }] }, ]; export function MenubarOpenModeDemo() { return ( ); } ``` #### Vertical menubar ```ts 'use client'; import { OgeMenubar } from '@oge-ui/react-navigation'; import type { OgeMenubarItemData } from '@oge-ui/react-navigation'; // A vertical menubar keeps role="menubar" and announces // aria-orientation="vertical"; Up/Down traverse the bar and ArrowRight // opens the submenu beside it, exactly as the APG allows. const menu: OgeMenubarItemData[] = [ { text: 'Dashboard', key: 'dashboard' }, { text: 'Reports', items: [{ text: 'Monthly' }, { text: 'Annual' }] }, { text: 'Settings', items: [{ text: 'Profile' }] }, ]; export function MenubarVerticalDemo() { return ( ); } ``` #### Adaptive hamburger ```ts 'use client'; import { useState } from 'react'; import { OgeMenubar } from '@oge-ui/react-navigation'; import type { OgeMenubarItemData, OgeMenubarCompactChangedEvent } from '@oge-ui/react-navigation'; // compactBelow measures the menubar's OWN container, never the // window (DevExtreme collapses on widget overflow, PrimeNG on a media // query). Below the threshold the whole bar becomes a hamburger button // opening the full tree as one nested menu — no second interaction model. const menu: OgeMenubarItemData[] = [ { text: 'File', items: [{ text: 'New' }] }, { text: 'Edit', items: [{ text: 'Undo' }] }, { text: 'Help', key: 'help' }, ]; export function MenubarCompactDemo() { const [width, setWidth] = useState(640); const onCompact = (event: OgeMenubarCompactChangedEvent) => { console.log('compact:', event.compact); }; return ( <>
); } ``` #### Cancelable events ```ts 'use client'; import { useRef, useState } from 'react'; import { OgeMenubar } from '@oge-ui/react-navigation'; import type { OgeMenubarItemData, OgeMenubarSubmenuOpeningEvent, OgeMenubarSubmenuClosingEvent } from '@oge-ui/react-navigation'; // The -ing pair is cancelable with the house mutable cancel // flag. Closes the menubar initiates (escape, select, navigation, api) run // through onSubmenuClosing; pointer closes owned by the overlay (outside) // and Tab only report onSubmenuClosed. const menu: OgeMenubarItemData[] = [ { text: 'File', key: 'file', items: [{ text: 'New' }] }, ]; export function MenubarEventsDemo() { const [locked, setLocked] = useState(false); // The callbacks run inside the component's event pipeline, so they read the // latest value from a ref rather than the render they were created in. const lockedRef = useRef(locked); lockedRef.current = locked; const onOpening = (event: OgeMenubarSubmenuOpeningEvent) => { if (lockedRef.current) event.cancel = true; }; const onClosing = (event: OgeMenubarSubmenuClosingEvent) => { if (lockedRef.current && event.reason !== 'tab') event.cancel = true; }; return ( <> ); } ``` #### Configuration ```ts 'use client'; import { OgeMenubar, OgeMenubarConfigProvider } from '@oge-ui/react-navigation'; import type { OgeMenubarItemData } from '@oge-ui/react-navigation'; // Every user-facing string lives in the messages block — the bar's // accessible name and the compact hamburger's label included. The context // provider is the React shape of provideOgeMenubarConfig(). const menu: OgeMenubarItemData[] = [ { text: 'Dosya', items: [{ text: 'Yeni' }] }, ]; export function MenubarConfigDemo() { return ( ); } ``` #### Flat data ```ts 'use client'; import { useState } from 'react'; import { OgeTreeView } from '@oge-ui/react-navigation'; import type { RowKey } from '@oge-ui/react-navigation'; interface Folder { id: number; parentId: number | null; name: string; hasItems?: boolean; } const folders: Folder[] = [ { id: 1, parentId: null, name: 'Documents' }, { id: 2, parentId: 1, name: 'Reports' }, { id: 3, parentId: 2, name: 'Q1.pdf' }, ]; export function TreeViewFlatDemo() { const [open, setOpen] = useState([1]); const [picked, setPicked] = useState([]); return ( ); } ``` #### Nested data ```ts 'use client'; import { OgeTreeView } from '@oge-ui/react-navigation'; interface NestedFolder { id: number; name: string; children?: NestedFolder[]; } const tree: NestedFolder[] = [ { id: 1, name: 'src', children: [{ id: 2, name: 'app', children: [{ id: 3, name: 'main.ts' }] }], }, ]; export function TreeViewNestedDemo() { return ( <> {/* nested payloads need only itemsExpr; the parent links are derived */} ); } ``` #### Checkboxes & cascade ```ts 'use client'; import { useState } from 'react'; import { OgeTreeView } from '@oge-ui/react-navigation'; import type { RowKey } from '@oge-ui/react-navigation'; interface Folder { id: number; parentId: number | null; name: string; hasItems?: boolean; } const folders: Folder[] = [ { id: 1, parentId: null, name: 'Documents' }, { id: 2, parentId: 1, name: 'Reports' }, { id: 3, parentId: 2, name: 'Q1.pdf' }, ]; export function TreeViewCheckBoxesDemo() { const [picked, setPicked] = useState([]); return ( <> {/* selectedKeysMode projects the stored set on the way out: 'all' (default) · 'leavesOnly' · 'excludeRecursive' */} ); } ``` #### Search ```ts 'use client'; import { OgeTreeView } from '@oge-ui/react-navigation'; interface Folder { id: number; parentId: number | null; name: string; hasItems?: boolean; } const folders: Folder[] = [ { id: 1, parentId: null, name: 'Documents' }, { id: 2, parentId: 1, name: 'Reports' }, { id: 3, parentId: 2, name: 'Q1.pdf' }, ]; export function TreeViewSearchDemo() { return ( ); } ``` #### Lazy load on demand ```ts 'use client'; import { OgeTreeView } from '@oge-ui/react-navigation'; interface Folder { id: number; parentId: number | null; name: string; hasItems?: boolean; } const roots: Folder[] = [ { id: 1, parentId: null, name: 'Documents', hasItems: true }, ]; // a skeleton row shows while the promise is pending const loadChildren = (parent: Folder): Promise => fetch(`/api/folders?parent=${parent.id}`).then((r) => r.json()); export function TreeViewLazyDemo() { return ( ); } ``` #### Virtual scrolling ```ts 'use client'; import { OgeTreeView } from '@oge-ui/react-navigation'; interface Folder { id: number; parentId: number | null; name: string; hasItems?: boolean; } const tenThousand: Folder[] = Array.from({ length: 10000 }, (_, i) => ({ id: i + 1, parentId: null, name: `Item ${i + 1}`, })); export function TreeViewVirtualDemo() { return ( <> {/* every row must actually be itemHeight tall */} ); } ``` #### Drag & drop reparenting ```ts 'use client'; import { useState } from 'react'; import { OgeTreeView } from '@oge-ui/react-navigation'; import type { OgeTreeReorderedEvent } from '@oge-ui/react-navigation'; interface Folder { id: number; parentId: number | null; name: string; hasItems?: boolean; } export function TreeViewDragDemo() { const [folders, setFolders] = useState([ { id: 1, parentId: null, name: 'Documents' }, { id: 2, parentId: 1, name: 'Reports' }, ]); // the tree never mutates your data; apply the move yourself const reparent = (e: OgeTreeReorderedEvent): void => { setFolders((rows) => rows.map((row) => row.id === e.dragKey ? { ...row, parentId: e.position === 'inside' ? (e.dropKey as number) : e.dropItem.parentId, } : row, ), ); }; return ( ); } ``` #### Custom node template ```ts 'use client'; import { OgeTreeView } from '@oge-ui/react-navigation'; interface Folder { id: number; parentId: number | null; name: string; hasItems?: boolean; } const folders: Folder[] = [ { id: 1, parentId: null, name: 'Documents' }, { id: 2, parentId: 1, name: 'Reports' }, { id: 3, parentId: 2, name: 'Q1.pdf' }, ]; export function TreeViewRenderItemDemo() { return ( <> ( <> {item.name} level {level} )} /> {/* the render prop renders inside role="treeitem", so it must not contain focusable controls — that would be a nested-interactive violation */} ); } ``` #### Getting started ```ts 'use client'; import { useState } from 'react'; import { OgePagination } from '@oge-ui/react-navigation'; // pageIndex is 0-BASED and controlled (pass defaultPageIndex instead for // the uncontrolled mode); the numeric window keeps a CONSTANT width — // ellipsis slots count toward maxButtons (default 7), so paging from the // first page to the middle never jitters the layout. An ellipsis never // hides a single page (it would render the page instead). export function PaginationBasicDemo() { const [page, setPage] = useState(0); const total = 400; return ( ); } ``` #### Page sizes and info ```ts 'use client'; import { useState } from 'react'; import { OgePagination } from '@oge-ui/react-navigation'; // Presence of pageSizes shows the selector (no separate boolean); // 'all' commits pageSize 0 — "all items on one page", the grid pager's // exact contract. Changing the size re-clamps pageIndex before the rich // onPageSizeChanged callback reports it. export function PaginationSizesDemo() { const [page, setPage] = useState(0); const [size, setSize] = useState(20); const total = 97; return ( ); } ``` #### Jump controls ```ts 'use client'; import { useState } from 'react'; import { OgePagination } from '@oge-ui/react-navigation'; // First/last jump buttons (Material's showFirstLastButtons) and the // jump-to-page input (PrimeNG's showJumpToPageInput; Kendo's // type: 'input'): 1-based display, clamped into range, display re-synced // after a clamp. The input is UNCONTROLLED and commits on BLUR or ENTER — // React has no onChange bound to the native `change` event, so there is // no commit-on-commit-of-the-native-change moment the Angular version // gets from (change). export function PaginationJumpDemo() { const [page, setPage] = useState(41); const total = 1000; return ( ); } ``` #### Unknown total ```ts 'use client'; import { useState } from 'react'; import { OgePagination } from '@oge-ui/react-navigation'; // itemCount undefined = the server cannot count. Only prev/next and a // "Page N" indicator render, and NEXT NEVER DISABLES — the component // cannot know the end. Clamp pageIndex yourself when the server reports // the last page. The ref handle's pageCount() returns undefined here. export function PaginationUnknownDemo() { const [page, setPage] = useState(3); return ( ); } ``` #### Adaptive display ```ts 'use client'; import { useState } from 'react'; import { OgePagination } from '@oge-ui/react-navigation'; // displayMode 'adaptive' switches to the compact "N / M" indicator below // the container threshold (config compactBelow, default 480px) — measured // against the pagination's OWN box via ResizeObserver, never the window // (behavior's paginationIsCompact). 'compact' forces the indicator // unconditionally. export function PaginationAdaptiveDemo() { const [page, setPage] = useState(4); const total = 400; return (
); } ``` #### Configuration ```ts 'use client'; import { useState } from 'react'; import { OgePagination, OgePaginationConfigProvider } from '@oge-ui/react-navigation'; // Application- or subtree-scoped defaults; the per-instance messages prop // wins over them, and both shallow-merge over the built-in strings. export function PaginationConfigDemo() { const [page, setPage] = useState(0); const total = 250; return ( ); } ``` #### Commands ```ts 'use client'; import { useState } from 'react'; import { OgeStepper } from '@oge-ui/react-navigation'; import type { OgeStepDefinition } from '@oge-ui/react-navigation'; // A React step is a `steps` entry: the shared step data plus the content // node the Angular would have projected. The built-in Back / // Next bar is opt-in — none of the reference steppers ships one at all. // icon takes SVG path data and replaces the step number in the indicator. const userIcon = 'M8 7.5A2.75 2.75 0 1 0 8 2a2.75 2.75 0 0 0 0 5.5ZM2.5 14c0-3 2.5-4.5 5.5-4.5s5.5 1.5 5.5 4.5Z'; const steps: readonly OgeStepDefinition[] = [ { key: 'account', label: 'Account', description: 'Who you are', icon: userIcon, content:

Account fields…

, }, { key: 'shipping', label: 'Shipping', description: 'Where it goes', optional: true, content:

Shipping fields…

, }, { key: 'review', label: 'Review', description: 'One last look', content:

Confirm and submit…

, }, ]; export function StepperBasicsDemo() { const [step, setStep] = useState(0); return ( ); } ``` #### Linear flow ```ts 'use client'; import { useState } from 'react'; import { OgeStepper } from '@oge-ui/react-navigation'; import type { OgeStepBlockedEvent } from '@oge-ui/react-navigation'; // linear blocks moving past a step that is neither completed nor optional, // and editable:false blocks coming back. Every refusal says WHY — Angular // Material refuses silently and tells you to add your own live region. export function StepperLinearDemo() { const [step, setStep] = useState(0); const [accountDone, setAccountDone] = useState(false); const [paymentDone, setPaymentDone] = useState(false); const onStepBlocked = (event: OgeStepBlockedEvent): void => { console.log('refused because', event.reason); }; return ( ); } ``` #### Step states ```ts 'use client'; import { OgeStepper } from '@oge-ui/react-navigation'; // The indicator state is derived: error outranks done, so a completed step // that later fails still reads as needing attention. The glyph is // aria-hidden, so the state is also announced in text. export function StepperStateDemo() { return ( ); } ``` #### Leave guard ```ts 'use client'; import { useState } from 'react'; import { OgeStepper } from '@oge-ui/react-navigation'; // stepGuard runs when the user LEAVES a step, inside the same pipeline the // headers use. false, a throw and a rejection all veto; a promise reports // changePending (and onChangePendingChange) and a second gesture meanwhile // is dropped. It gates the finish on the last step too. export function StepperGuardDemo() { const [step, setStep] = useState(0); const [dirty, setDirty] = useState(true); const confirmLeave = (): boolean => !dirty || confirm('Discard your changes?'); return ( Details…

}, { label: 'Done', content:

Done…

}, ]} /> ); } ``` #### Orientation ```ts 'use client'; import { useState } from 'react'; import { OgeStepper } from '@oge-ui/react-navigation'; import type { OgeStepperOrientation } from '@oge-ui/react-navigation'; // The ARIA semantics do NOT change with the orientation: it is an ordered // list of buttons with aria-current="step" either way. Angular Material // swaps to tab semantics when horizontal, so the same widget reads as two // different things to a screen reader. export function StepperVerticalDemo() { const [step, setStep] = useState(0); const [orientation, setOrientation] = useState('vertical'); return ( First body…

}, { label: 'Two', content:

Second body…

}, ]} /> ); } ``` #### Navigation buttons ```ts 'use client'; import { useRef, useState } from 'react'; import { OgeStepper } from '@oge-ui/react-navigation'; import type { OgeStepperHandle } from '@oge-ui/react-navigation'; // The handle routes through the same pipeline the headers use, so linear // and stepGuard still apply. A button inside a step body and a button // outside the stepper drive it identically — which Material's directives // cannot do from outside. export function StepperNavDemo() { const wizard = useRef(null); const [step, setStep] = useState(0); return ( <>

First body…

), }, { label: 'Two', content:

Second body…

}, ]} /> ); } ``` #### Inside a form ```ts 'use client'; import { useState } from 'react'; import { OgeTextBox } from '@oge-ui/react-inputs'; import { OgeStepper } from '@oge-ui/react-navigation'; // @oge-ui/forms has no React layer yet (docs/REACT-PARITY.md), so the // editors hold their value in your own state — useState here, but React // Hook Form, Formik or TanStack Form bind exactly the same way. Step // completion is a plain derivation of that state, which is what the Angular // wrapper computes from the form's per-step error rollup. export function StepperFormDemo() { const [order, setOrder] = useState({ email: '', card: '' }); return ( setOrder((o) => ({ ...o, email }))} /> ), }, { key: 'payment', label: 'Payment', completed: order.card !== '', content: ( setOrder((o) => ({ ...o, card }))} /> ), }, ]} /> ); } ``` #### Configuration ```ts 'use client'; import { OgeStepper, OgeStepperConfigProvider } from '@oge-ui/react-navigation'; export function StepperConfigDemo() { return ( {/* A single instance can still override the strings with the messages prop. */} ); } ``` ## @oge-ui/react-overlay React overlay primitives: viewport-aware anchored popups (flip + clamp, RTL-aware, shared Escape stack) and a full WAI-ARIA menu with submenus and type-ahead — running the same positioning and menu machines as the Angular overlay package. Docs: https://ogeui.com/components/buttons ## @oge-ui/behavior Framework-free interaction and accessibility layer shared by every package: popup positioning, focus trapping, the single overlay Escape stack and ref-counted body scroll locking. Installed automatically — you rarely import it directly. ### Entry points `@oge-ui/behavior` - values: `OGE_BREADCRUMB_ELLIPSIS_FALLBACK`, `OGE_DEFAULT_ACCORDION_CONFIG`, `OGE_DEFAULT_ACCORDION_MESSAGES`, `OGE_DEFAULT_BREADCRUMB_CONFIG`, `OGE_DEFAULT_BREADCRUMB_MESSAGES`, `OGE_DEFAULT_BUTTONS_CONFIG`, `OGE_DEFAULT_BUTTONS_MESSAGES`, `OGE_DEFAULT_CARD_CONFIG`, `OGE_DEFAULT_COLOR_PALETTE`, `OGE_DEFAULT_DRAWER_CONFIG`, `OGE_DEFAULT_DRAWER_MESSAGES`, `OGE_DEFAULT_INPUTS_CONFIG`, `OGE_DEFAULT_INPUTS_MESSAGES`, `OGE_DEFAULT_LOAD_INDICATOR_CONFIG`, `OGE_DEFAULT_LOAD_INDICATOR_MESSAGES`, `OGE_DEFAULT_MENUBAR_CONFIG`, `OGE_DEFAULT_MENUBAR_MESSAGES`, `OGE_DEFAULT_OVERLAY_TIMINGS`, `OGE_DEFAULT_PAGINATION_CONFIG`, `OGE_DEFAULT_PAGINATION_MESSAGES`, `OGE_DEFAULT_PROGRESS_BAR_CONFIG`, `OGE_DEFAULT_PROGRESS_BAR_MESSAGES`, `OGE_DEFAULT_SKELETON_CONFIG`, `OGE_DEFAULT_SPLITTER_CONFIG`, `OGE_DEFAULT_SPLITTER_MESSAGES`, `OGE_DEFAULT_STEPPER_CONFIG`, `OGE_DEFAULT_STEPPER_MESSAGES`, `OGE_DEFAULT_TABS_CONFIG`, `OGE_DEFAULT_TABS_MESSAGES`, `OGE_DEFAULT_TOOLBAR_CONFIG`, `OGE_DEFAULT_TOOLBAR_MESSAGES`, `OGE_DEFAULT_TREE_VIEW_CONFIG`, `OGE_DEFAULT_TREE_VIEW_MESSAGES`, `OGE_MENUBAR_HOVER_DELAY`, `OGE_PAGE_ELLIPSIS`, `OGE_PAGINATION_DEFAULT_COMPACT_BELOW`, `OGE_PAGINATION_DEFAULT_MAX_BUTTONS`, `OGE_SELECT_OPTION_HEIGHT`, `OGE_SLIDER_MAX_TICKS`, `OGE_SPLITTER_FULL_TRAVEL`, `OGE_SPLITTER_GRIP_SIDES`, `OGE_TAB_DRAG_THRESHOLD`, `OGE_TOOLBAR_STOP_SELECTOR`, `OGE_TREE_DEFAULT_ITEM_HEIGHT`, `OGE_TREE_DRAG_HOVER_EXPAND_MS`, `OGE_TREE_DRAG_THRESHOLD`, `OgeAnchoredPanelCore`, `OgeButtonPress`, `OgeInputCommit`, `OgeListVirtualizerCore`, `OgeMenuTypeAhead`, `OgeSelectListCore`, `accordionAriaDisabled`, `accordionItemDescriptor`, `accordionNavIntent`, `accordionPageDirection`, `accordionTypeAheadStart`, `addDays`, `addMonths`, `addYears`, `ancestorsOf`, `applyButtonGroupSelection`, `applyTabOrder`, `applyToolbarOverride`, `breadcrumbDataDescriptors`, `breadcrumbMenuItems`, `buildTreeViewIndex`, `buildTreeViewModel`, `buildTreeViewNodes`, `buttonGroupNavIndex`, `buttonGroupRole`, `canCollapseAccordionPanel`, `canResizeSplitterAt`, `canSelectTab`, `clampNumber`, `clampPaginationIndex`, `clampValue`, `colorsEqual`, `constrainRangeValue`, `contrastForeground`, `createNumberFormatter`, `createTreeSearchPredicate`, `createTypeAheadBuffer`, `datePartOrder`, `decadeCells`, `edgeEnabledIndex`, `exceedsTreeDragThreshold`, `expandedIdsAfterCollapse`, `expandedIdsAfterExpand`, `findMenubarItemPath`, `fitBreadcrumbDescriptors`, `fitToolbarDescriptors`, `fitToolbarItems`, `formatColor`, `formatPaginationMessage`, `formatPattern`, `getTabbableElements`, `graphemeCount`, `hsvaToRgba`, `isAccordionTypeAheadKey`, `isDayDisabled`, `isMenubarCompact`, `isSplitterPaneCollapsed`, `isSplitterPaneCollapsible`, `isStepReachable`, `isToolbarStopDisabled`, `isToolbarTextEntry`, `isTopOverlay`, `loadSplitterPanes`, `loadToolbarItems`, `lockBodyScroll`, `matchAccordionTitle`, `matchByPrefix`, `menuEdgeIndex`, `menuEnabledIndexes`, `menuMoveIndex`, `menubarBarKeys`, `menubarClosedReason`, `menubarDataDescriptors`, `menubarEventBase`, `menubarItemDomId`, `menubarPanelItems`, `menubarPanelLabel`, `menubarPanelPlacement`, `menubarPopupCloseReason`, `menubarStopDisabled`, `messageForFieldError`, `monthCells`, `monthMatrix`, `navigate`, `nextTreeExpansion`, `nextTreeSelection`, `normalizeSplitTracks`, `offsetByStep`, `orderToolbarDescriptors`, `paginationHasNextPage`, `paginationInfoText`, `paginationIsCompact`, `paginationPageCount`, `parseColor`, `parseDateText`, `parseSplitterSize`, `planTreeViewKey`, `pruneHiddenMenubarItems`, `pushOverlay`, `ratioToValue`, `readToolbarStyleMetrics`, `removeOverlay`, `reorderTabIds`, `resetGraphemeSegmenter`, `resetScrollLockForTests`, `resizeSplitAt`, `resolveAccordionIndex`, `resolveAutoRepeat`, `resolveClickGuard`, `resolveDisabled`, `resolveDisplay`, `resolveDrawerMode`, `resolveFirstDayOfWeek`, `resolveHoldToConfirm`, `resolveMenubarCompact`, `resolveOgeAccordionConfig`, `resolveOgeBreadcrumbConfig`, `resolveOgeButtonsConfig`, `resolveOgeCardConfig`, `resolveOgeDrawerConfig`, `resolveOgeInputsConfig`, `resolveOgeLoadIndicatorConfig`, `resolveOgeMenubarConfig`, `resolveOgePaginationConfig`, `resolveOgeProgressBarConfig`, `resolveOgeSkeletonConfig`, `resolveOgeSplitterConfig`, `resolveOgeStepperConfig`, `resolveOgeTabsConfig`, `resolveOgeToolbarConfig`, `resolveOgeTreeViewConfig`, `resolvePageCount`, `resolvePageRange`, `resolvePageWindow`, `resolvePopupPosition`, `resolveSelectedKeys`, `resolveSplitterIndex`, `resolveStepIndex`, `resolveTabIndex`, `resolveTreeDropPosition`, `resolveTreeItemHeight`, `resolveTreeSelectByClick`, `resolveValue`, `rgbaToHsva`, `roundSplitterValue`, `runAsyncGuard`, `sameAccordionIds`, `sameAccordionKeys`, `sameDay`, `sameMonth`, `sameSplitterSizes`, `shouldRenderAccordionPanel`, `sliderKeyboardTarget`, `sliderPercent`, `sliderTicks`, `sliderValueFromPointer`, `snapToStep`, `splitSeparatorRange`, `splitTrackPx`, `splitterBoundInTrackUnit`, `splitterBounds`, `splitterCollapseSideInDirection`, `splitterDragDelta`, `splitterFlexiblePx`, `splitterGridTemplate`, `splitterGripPath`, `splitterGripTitle`, `splitterKeyAction`, `splitterKeyShortcuts`, `splitterPaneDescriptor`, `splitterPaneOf`, `splitterSeparatorLabel`, `splitterSeparatorRanges`, `splitterSizesWithRestored`, `splitterTrackCss`, `splitterTracks`, `splitterTracksToSizes`, `startOfDay`, `startSliderDrag`, `startSplitterDrag`, `stepBlockReason`, `stepEnabledIndex`, `stepItemDescriptor`, `stepState`, `stepperArrowKeys`, `stepperKeyTarget`, `stepsCompleteBefore`, `tabItemDescriptor`, `toLocalDate`, `toolbarCollapses`, `toolbarDataDescriptors`, `toolbarIconVisible`, `toolbarItemId`, `toolbarItemWidth`, `toolbarMenuItems`, `toolbarOverflowEvent`, `toolbarScrollState`, `toolbarTextVisible`, `trapTabKey`, `treeAccessor`, `treeAnchorIndex`, `treeAriaChecked`, `treeAriaSelected`, `treeCanDrop`, `treeCheckStates`, `treeChildrenLoadNeeded`, `treeEdgeIndex`, `treeEffectiveExpanded`, `treeExpandableKeys`, `treeFilterExpandedKeys`, `treeHasChildrenHint`, `treeLoadingAny`, `treeNodeDisabled`, `treeNodeIndent`, `treeParentIndex`, `treeRangeSelection`, `treeSearchAccessors`, `treeSelectAllState`, `treeSiblingExpansion`, `treeStepIndex`, `treeTypeAheadIndex`, `treeVisibleKeys`, `unlockBodyScroll`, `valueToRatio`, `viewLabel`, `weekNumber`, `weekdayNames`, `withToolbarIndexes`, `yearCells` - types: `CalendarCell`, `CheckState`, `DateParseKind`, `OgeAccordionCollapsedEvent`, `OgeAccordionCollapsingEvent`, `OgeAccordionConfig`, `OgeAccordionConfigInput`, `OgeAccordionContentFailedEvent`, `OgeAccordionContentLoadedEvent`, `OgeAccordionContentLoader`, `OgeAccordionDescriptorCore`, `OgeAccordionDisplayMode`, `OgeAccordionExpandGuard`, `OgeAccordionExpandedEvent`, `OgeAccordionExpandingEvent`, `OgeAccordionItemClickEvent`, `OgeAccordionItemData`, `OgeAccordionLoadState`, `OgeAccordionMessages`, `OgeAccordionNavIntent`, `OgeAccordionSize`, `OgeAccordionStylingMode`, `OgeAccordionTogglePosition`, `OgeAnchoredPanelCoreOptions`, `OgeAsyncGuard`, `OgeAutoRepeatOptions`, `OgeBreadcrumbCollapseMode`, `OgeBreadcrumbConfig`, `OgeBreadcrumbConfigInput`, `OgeBreadcrumbDescriptorCore`, `OgeBreadcrumbFitRequest`, `OgeBreadcrumbFitResult`, `OgeBreadcrumbItemClickEvent`, `OgeBreadcrumbItemData`, `OgeBreadcrumbMessages`, `OgeButtonGroupSelectionChange`, `OgeButtonGroupSelectionMode`, `OgeButtonGuardTiming`, `OgeButtonHoldState`, `OgeButtonHoldTiming`, `OgeButtonIconPosition`, `OgeButtonPressOptions`, `OgeButtonRepeatTiming`, `OgeButtonSeverity`, `OgeButtonSize`, `OgeButtonStylingMode`, `OgeButtonsConfig`, `OgeButtonsConfigInput`, `OgeButtonsMessages`, `OgeCalendarCellClickEvent`, `OgeCalendarDisabledDates`, `OgeCalendarRange`, `OgeCalendarSelectionMode`, `OgeCalendarWeekNumberOptions`, `OgeCalendarZoomLevel`, `OgeCardActionsAlign`, `OgeCardConfig`, `OgeCardConfigInput`, `OgeCardOrientation`, `OgeCardSeverity`, `OgeCardSize`, `OgeCardStylingMode`, `OgeClickGuardOptions`, `OgeColorBoxApplyValueMode`, `OgeColorBoxView`, `OgeColorFormat`, `OgeDateBoxApplyValueMode`, `OgeDateBoxDisplayFormat`, `OgeDateBoxTimeView`, `OgeDateBoxType`, `OgeDrawerAutoFocus`, `OgeDrawerCloseReason`, `OgeDrawerClosedEvent`, `OgeDrawerClosingEvent`, `OgeDrawerConfig`, `OgeDrawerConfigInput`, `OgeDrawerLandmark`, `OgeDrawerMessages`, `OgeDrawerMode`, `OgeDrawerModeChangedEvent`, `OgeDrawerModeRequest`, `OgeDrawerModeResult`, `OgeDrawerOpeningEvent`, `OgeDrawerPosition`, `OgeFieldError`, `OgeGuardHandlers`, `OgeHoldToConfirmOptions`, `OgeHsva`, `OgeInputCommitOptions`, `OgeInputCounterMode`, `OgeInputErrorDisplay`, `OgeInputLabelMode`, `OgeInputShowSuccessIcon`, `OgeInputSize`, `OgeInputStylingMode`, `OgeInputSubscriptSizing`, `OgeInputsConfig`, `OgeInputsConfigInput`, `OgeInputsMessages`, `OgeListVirtualizerDeps`, `OgeLoadIndicatorConfig`, `OgeLoadIndicatorConfigInput`, `OgeLoadIndicatorMessages`, `OgeMenuCloseReason`, `OgeMenuItem`, `OgeMenuItemSeverity`, `OgeMenubarBarKeys`, `OgeMenubarCloseReason`, `OgeMenubarCompactChangedEvent`, `OgeMenubarCompactRequest`, `OgeMenubarCompactResult`, `OgeMenubarConfig`, `OgeMenubarConfigInput`, `OgeMenubarDescriptorCore`, `OgeMenubarItemClickEvent`, `OgeMenubarItemData`, `OgeMenubarMessages`, `OgeMenubarOpenMode`, `OgeMenubarOrientation`, `OgeMenubarPanelSource`, `OgeMenubarSubmenuClosedEvent`, `OgeMenubarSubmenuClosingEvent`, `OgeMenubarSubmenuOpenedEvent`, `OgeMenubarSubmenuOpeningEvent`, `OgeNumberFormatter`, `OgeOverlayTimings`, `OgePageCountRequest`, `OgePageRangeRequest`, `OgePageRangeResult`, `OgePageWindowEntry`, `OgePageWindowRequest`, `OgePaginationCompactRequest`, `OgePaginationConfig`, `OgePaginationConfigInput`, `OgePaginationDisplayMode`, `OgePaginationMessages`, `OgePaginationPageChangedEvent`, `OgePaginationPageSizeChangedEvent`, `OgePaginationSize`, `OgePopupAlign`, `OgePopupCloseReason`, `OgePopupPlacement`, `OgePopupPositionRequest`, `OgePopupSide`, `OgeProgressBarCompletedEvent`, `OgeProgressBarConfig`, `OgeProgressBarConfigInput`, `OgeProgressBarMessages`, `OgeProgressBarSeverity`, `OgeRangeThumb`, `OgeReactiveCell`, `OgeReactivityAdapter`, `OgeRect`, `OgeResolvedPopupPosition`, `OgeRgba`, `OgeSelectDisabledExpr`, `OgeSelectDisplayExpr`, `OgeSelectGroupExpr`, `OgeSelectImageExpr`, `OgeSelectItemsFn`, `OgeSelectListCoreDeps`, `OgeSelectListRow`, `OgeSelectSearchExpr`, `OgeSelectSearchMode`, `OgeSelectValueExpr`, `OgeSkeletonAnimation`, `OgeSkeletonConfig`, `OgeSkeletonConfigInput`, `OgeSkeletonShape`, `OgeSliderAxis`, `OgeSliderDragHandlers`, `OgeSliderScale`, `OgeSliderTrackRect`, `OgeSplitBounds`, `OgeSplitSeparatorRange`, `OgeSplitTrack`, `OgeSplitterConfig`, `OgeSplitterConfigInput`, `OgeSplitterDataSourceLike`, `OgeSplitterDescriptorCore`, `OgeSplitterDragHandlers`, `OgeSplitterGripSide`, `OgeSplitterKeyAction`, `OgeSplitterKeyInput`, `OgeSplitterMessages`, `OgeSplitterOrientation`, `OgeSplitterPaneClickEvent`, `OgeSplitterPaneCollapsedEvent`, `OgeSplitterPaneCollapsingEvent`, `OgeSplitterPaneData`, `OgeSplitterPaneHoldEvent`, `OgeSplitterResizeEvent`, `OgeSplitterResizeStartEvent`, `OgeSplitterSize`, `OgeSplitterView`, `OgeStepBlockReason`, `OgeStepBlockedEvent`, `OgeStepChangedEvent`, `OgeStepChangingEvent`, `OgeStepData`, `OgeStepDescriptorCore`, `OgeStepGuard`, `OgeStepReachRequest`, `OgeStepState`, `OgeStepperArrowKeys`, `OgeStepperConfig`, `OgeStepperConfigInput`, `OgeStepperDisplay`, `OgeStepperFinishEvent`, `OgeStepperKeyRequest`, `OgeStepperMessages`, `OgeStepperOrientation`, `OgeTabClickEvent`, `OgeTabCloseGuard`, `OgeTabClosedEvent`, `OgeTabClosingEvent`, `OgeTabDescriptorCore`, `OgeTabItem`, `OgeTabPanelAnimation`, `OgeTabReorderedEvent`, `OgeTabReorderingEvent`, `OgeTabSelectionChangedEvent`, `OgeTabSelectionChangingEvent`, `OgeTabsActivation`, `OgeTabsAlignment`, `OgeTabsConfig`, `OgeTabsConfigInput`, `OgeTabsIndicatorFit`, `OgeTabsMessages`, `OgeTabsNavButtonsMode`, `OgeTabsOrientation`, `OgeTabsPosition`, `OgeTabsSize`, `OgeTabsStylingMode`, `OgeToolbarConfig`, `OgeToolbarConfigInput`, `OgeToolbarDataSourceLike`, `OgeToolbarDescriptorCore`, `OgeToolbarDisplayDefaults`, `OgeToolbarDisplayMode`, `OgeToolbarFitItem`, `OgeToolbarFitOptions`, `OgeToolbarFitRequest`, `OgeToolbarFitResult`, `OgeToolbarItemActiveChangedEvent`, `OgeToolbarItemClickEvent`, `OgeToolbarItemData`, `OgeToolbarItemHoldEvent`, `OgeToolbarItemLocation`, `OgeToolbarItemOverride`, `OgeToolbarItemSeverity`, `OgeToolbarItemTemplateContextCore`, `OgeToolbarItemType`, `OgeToolbarLocateInMenu`, `OgeToolbarMenuCloseReason`, `OgeToolbarMenuClosedEvent`, `OgeToolbarMenuClosingEvent`, `OgeToolbarMenuOpeningEvent`, `OgeToolbarMessages`, `OgeToolbarOrientation`, `OgeToolbarOverflow`, `OgeToolbarOverflowChangedEvent`, `OgeToolbarOverflowPolicy`, `OgeToolbarScrollState`, `OgeToolbarSize`, `OgeToolbarStyleMetrics`, `OgeToolbarStylingMode`, `OgeTreeCheckBoxesMode`, `OgeTreeChildrenFailedEvent`, `OgeTreeChildrenLoadedEvent`, `OgeTreeCollapsedEvent`, `OgeTreeCollapsingEvent`, `OgeTreeDataStructure`, `OgeTreeDropPosition`, `OgeTreeExpandEvent`, `OgeTreeExpandedEvent`, `OgeTreeExpandingEvent`, `OgeTreeExpr`, `OgeTreeIndexInput`, `OgeTreeItemClickEvent`, `OgeTreeItemSelectionChangedEvent`, `OgeTreeKeyAction`, `OgeTreeKeyInput`, `OgeTreeKeyPlan`, `OgeTreeLoadChildren`, `OgeTreeLoadState`, `OgeTreeNodesInput`, `OgeTreeReorderedEvent`, `OgeTreeReorderingEvent`, `OgeTreeSearchMode`, `OgeTreeSelectAllChangedEvent`, `OgeTreeSelectedKeysMode`, `OgeTreeSelectionChangedEvent`, `OgeTreeSelectionChangingEvent`, `OgeTreeSelectionMode`, `OgeTreeSize`, `OgeTreeViewConfig`, `OgeTreeViewConfigInput`, `OgeTreeViewMessages`, `OgeTreeViewModel`, `OgeTreeViewModelInput`, `OgeTreeViewNode`, `OgeTreeVirtualScrollOptions`, `OgeTypeAheadBuffer`, `OgeVirtualScrollOptions`, `RowKey`, `TreeFilterMode`, `TreeIndex`, `WeekNumberRule` ## @oge-ui/react Re-exports every React family from a single import path, with one stylesheet for all of them. `npm i @oge-ui/react` then `import { OgeButton, OgeTextBox } from '@oge-ui/react'`. ## oge-ui Re-exports every MIT family from a single import path. `npm i oge-ui` then `import { OgeGrid, OgeButton } from 'oge-ui'`. ### Entry points `oge-ui` - values: `OgeToolbarItem`, `OgeTreeList` - types: `OgeMenuItem`, `OgeTreeDropPosition`, `OgeTreeExportData`, `OgeTreeInitNewRowEvent`, `OgeTreeRowReparentEvent`, `OgeTreeRowToggleEvent`, `OgeTreeRowTogglingEvent`