@object-ui/core 3.1.5 → 3.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +20 -1
  3. package/dist/actions/ActionRunner.d.ts +9 -0
  4. package/dist/actions/ActionRunner.js +41 -4
  5. package/dist/adapters/ValueDataSource.d.ts +5 -1
  6. package/dist/adapters/ValueDataSource.js +30 -1
  7. package/dist/errors/index.js +2 -3
  8. package/dist/evaluator/ExpressionCache.d.ts +9 -10
  9. package/dist/evaluator/ExpressionCache.js +29 -8
  10. package/dist/evaluator/SafeExpressionParser.d.ts +131 -0
  11. package/dist/evaluator/SafeExpressionParser.js +851 -0
  12. package/dist/evaluator/index.d.ts +1 -0
  13. package/dist/evaluator/index.js +1 -0
  14. package/dist/protocols/DndProtocol.js +2 -14
  15. package/dist/protocols/KeyboardProtocol.js +1 -4
  16. package/dist/protocols/NotificationProtocol.js +3 -13
  17. package/dist/utils/debug.js +2 -1
  18. package/dist/utils/filter-converter.js +25 -5
  19. package/package.json +33 -9
  20. package/.turbo/turbo-build.log +0 -4
  21. package/src/__benchmarks__/core.bench.ts +0 -64
  22. package/src/__tests__/protocols/DndProtocol.test.ts +0 -186
  23. package/src/__tests__/protocols/KeyboardProtocol.test.ts +0 -177
  24. package/src/__tests__/protocols/NotificationProtocol.test.ts +0 -142
  25. package/src/__tests__/protocols/ResponsiveProtocol.test.ts +0 -176
  26. package/src/__tests__/protocols/SharingProtocol.test.ts +0 -188
  27. package/src/actions/ActionEngine.ts +0 -268
  28. package/src/actions/ActionRunner.ts +0 -717
  29. package/src/actions/TransactionManager.ts +0 -521
  30. package/src/actions/UndoManager.ts +0 -215
  31. package/src/actions/__tests__/ActionEngine.test.ts +0 -206
  32. package/src/actions/__tests__/ActionRunner.params.test.ts +0 -134
  33. package/src/actions/__tests__/ActionRunner.test.ts +0 -711
  34. package/src/actions/__tests__/TransactionManager.test.ts +0 -447
  35. package/src/actions/__tests__/UndoManager.test.ts +0 -320
  36. package/src/actions/index.ts +0 -12
  37. package/src/adapters/ApiDataSource.ts +0 -376
  38. package/src/adapters/README.md +0 -180
  39. package/src/adapters/ValueDataSource.ts +0 -438
  40. package/src/adapters/__tests__/ApiDataSource.test.ts +0 -418
  41. package/src/adapters/__tests__/ValueDataSource.test.ts +0 -472
  42. package/src/adapters/__tests__/resolveDataSource.test.ts +0 -144
  43. package/src/adapters/index.ts +0 -15
  44. package/src/adapters/resolveDataSource.ts +0 -79
  45. package/src/builder/__tests__/schema-builder.test.ts +0 -235
  46. package/src/builder/schema-builder.ts +0 -584
  47. package/src/data-scope/DataScopeManager.ts +0 -269
  48. package/src/data-scope/ViewDataProvider.ts +0 -282
  49. package/src/data-scope/__tests__/DataScopeManager.test.ts +0 -211
  50. package/src/data-scope/__tests__/ViewDataProvider.test.ts +0 -270
  51. package/src/data-scope/index.ts +0 -24
  52. package/src/errors/__tests__/errors.test.ts +0 -292
  53. package/src/errors/index.ts +0 -270
  54. package/src/evaluator/ExpressionCache.ts +0 -192
  55. package/src/evaluator/ExpressionContext.ts +0 -118
  56. package/src/evaluator/ExpressionEvaluator.ts +0 -315
  57. package/src/evaluator/FormulaFunctions.ts +0 -398
  58. package/src/evaluator/__tests__/ExpressionCache.test.ts +0 -135
  59. package/src/evaluator/__tests__/ExpressionContext.test.ts +0 -110
  60. package/src/evaluator/__tests__/ExpressionEvaluator.test.ts +0 -131
  61. package/src/evaluator/__tests__/FormulaFunctions.test.ts +0 -447
  62. package/src/evaluator/index.ts +0 -12
  63. package/src/index.ts +0 -38
  64. package/src/protocols/DndProtocol.ts +0 -184
  65. package/src/protocols/KeyboardProtocol.ts +0 -185
  66. package/src/protocols/NotificationProtocol.ts +0 -159
  67. package/src/protocols/ResponsiveProtocol.ts +0 -210
  68. package/src/protocols/SharingProtocol.ts +0 -185
  69. package/src/protocols/index.ts +0 -13
  70. package/src/query/__tests__/query-ast.test.ts +0 -211
  71. package/src/query/__tests__/window-functions.test.ts +0 -275
  72. package/src/query/index.ts +0 -7
  73. package/src/query/query-ast.ts +0 -341
  74. package/src/registry/PluginScopeImpl.ts +0 -259
  75. package/src/registry/PluginSystem.ts +0 -206
  76. package/src/registry/Registry.ts +0 -219
  77. package/src/registry/WidgetRegistry.ts +0 -316
  78. package/src/registry/__tests__/PluginSystem.test.ts +0 -309
  79. package/src/registry/__tests__/Registry.test.ts +0 -293
  80. package/src/registry/__tests__/WidgetRegistry.test.ts +0 -321
  81. package/src/registry/__tests__/plugin-scope-integration.test.ts +0 -283
  82. package/src/theme/ThemeEngine.ts +0 -530
  83. package/src/theme/__tests__/ThemeEngine.test.ts +0 -668
  84. package/src/theme/index.ts +0 -24
  85. package/src/types/index.ts +0 -21
  86. package/src/utils/__tests__/debug-collector.test.ts +0 -102
  87. package/src/utils/__tests__/debug.test.ts +0 -134
  88. package/src/utils/__tests__/expand-fields.test.ts +0 -120
  89. package/src/utils/__tests__/extract-records.test.ts +0 -50
  90. package/src/utils/__tests__/filter-converter.test.ts +0 -118
  91. package/src/utils/__tests__/merge-views-into-objects.test.ts +0 -110
  92. package/src/utils/__tests__/normalize-quick-filter.test.ts +0 -123
  93. package/src/utils/debug-collector.ts +0 -100
  94. package/src/utils/debug.ts +0 -147
  95. package/src/utils/expand-fields.ts +0 -76
  96. package/src/utils/extract-records.ts +0 -33
  97. package/src/utils/filter-converter.ts +0 -133
  98. package/src/utils/merge-views-into-objects.ts +0 -36
  99. package/src/utils/normalize-quick-filter.ts +0 -78
  100. package/src/validation/__tests__/object-validation-engine.test.ts +0 -567
  101. package/src/validation/__tests__/schema-validator.test.ts +0 -118
  102. package/src/validation/__tests__/validation-engine.test.ts +0 -102
  103. package/src/validation/index.ts +0 -10
  104. package/src/validation/schema-validator.ts +0 -344
  105. package/src/validation/validation-engine.ts +0 -528
  106. package/src/validation/validators/index.ts +0 -25
  107. package/src/validation/validators/object-validation-engine.ts +0 -722
  108. package/tsconfig.json +0 -15
  109. package/tsconfig.tsbuildinfo +0 -1
  110. package/vitest.config.ts +0 -2
@@ -1,184 +0,0 @@
1
- /**
2
- * ObjectUI
3
- * Copyright (c) 2024-present ObjectStack Inc.
4
- *
5
- * This source code is licensed under the MIT license found in the
6
- * LICENSE file in the root directory of this source tree.
7
- */
8
-
9
- /**
10
- * @object-ui/core - DnD Protocol Bridge
11
- *
12
- * Converts spec-aligned DnD configuration schemas into runtime-usable
13
- * component props and CSS constraint styles for drag-and-drop interactions.
14
- *
15
- * @module protocols/DndProtocol
16
- * @packageDocumentation
17
- */
18
-
19
- import type { DndConfig, DragItem, DropZone, DragConstraint } from '@object-ui/types';
20
-
21
- // ============================================================================
22
- // Resolved Types
23
- // ============================================================================
24
-
25
- /** Fully resolved DnD configuration with all defaults applied. */
26
- export interface ResolvedDndConfig {
27
- enabled: boolean;
28
- sortable: boolean;
29
- autoScroll: boolean;
30
- touchDelay: number;
31
- dragItem?: DragItem;
32
- dropZone?: DropZone;
33
- }
34
-
35
- /** Component props for a draggable element. */
36
- export interface DragItemProps {
37
- draggable: boolean;
38
- 'aria-roledescription': string;
39
- 'aria-label'?: string;
40
- 'aria-describedby'?: string;
41
- role: string;
42
- 'data-drag-type': string;
43
- 'data-drag-handle': string;
44
- 'data-drag-disabled': string;
45
- }
46
-
47
- /** Component props for a droppable area. */
48
- export interface DropZoneProps {
49
- 'aria-dropeffect': string;
50
- 'aria-label'?: string;
51
- 'aria-describedby'?: string;
52
- role: string;
53
- 'data-drop-accept': string;
54
- 'data-drop-max-items'?: number;
55
- 'data-drop-highlight': string;
56
- }
57
-
58
- /** CSS constraint styles for drag movement. */
59
- export interface DragConstraintStyles {
60
- position: string;
61
- touchAction: string;
62
- [key: string]: string | number | undefined;
63
- }
64
-
65
- // ============================================================================
66
- // DnD Config Resolution
67
- // ============================================================================
68
-
69
- /**
70
- * Resolve a DnD configuration by applying spec defaults to missing fields.
71
- *
72
- * @param config - Partial DnD configuration from the spec
73
- * @returns Fully resolved DnD configuration
74
- */
75
- export function resolveDndConfig(config: DndConfig): ResolvedDndConfig {
76
- return {
77
- enabled: config.enabled ?? false,
78
- sortable: config.sortable ?? false,
79
- autoScroll: config.autoScroll ?? true,
80
- touchDelay: config.touchDelay ?? 200,
81
- dragItem: config.dragItem,
82
- dropZone: config.dropZone,
83
- };
84
- }
85
-
86
- // ============================================================================
87
- // Drag Item Props
88
- // ============================================================================
89
-
90
- /**
91
- * Convert a spec DragItem to component props suitable for a draggable element.
92
- * Produces `draggable`, ARIA attributes, and data attributes for DnD libraries.
93
- *
94
- * @param item - DragItem configuration from the spec
95
- * @returns Component props object for a draggable element
96
- */
97
- export function createDragItemProps(item: DragItem): DragItemProps {
98
- const ariaLabel = typeof item.ariaLabel === 'string'
99
- ? item.ariaLabel
100
- : item.ariaLabel?.defaultValue;
101
-
102
- const label = typeof item.label === 'string'
103
- ? item.label
104
- : item.label?.defaultValue;
105
-
106
- return {
107
- draggable: !(item.disabled ?? false),
108
- 'aria-roledescription': 'draggable',
109
- 'aria-label': ariaLabel ?? label,
110
- 'aria-describedby': item.ariaDescribedBy,
111
- role: item.role ?? 'listitem',
112
- 'data-drag-type': item.type,
113
- 'data-drag-handle': item.handle ?? 'element',
114
- 'data-drag-disabled': String(item.disabled ?? false),
115
- };
116
- }
117
-
118
- // ============================================================================
119
- // Drop Zone Props
120
- // ============================================================================
121
-
122
- /**
123
- * Convert a spec DropZone to component props suitable for a droppable area.
124
- * Produces ARIA attributes and data attributes for DnD libraries.
125
- *
126
- * @param zone - DropZone configuration from the spec
127
- * @returns Component props object for a droppable area
128
- */
129
- export function createDropZoneProps(zone: DropZone): DropZoneProps {
130
- const ariaLabel = typeof zone.ariaLabel === 'string'
131
- ? zone.ariaLabel
132
- : zone.ariaLabel?.defaultValue;
133
-
134
- const label = typeof zone.label === 'string'
135
- ? zone.label
136
- : zone.label?.defaultValue;
137
-
138
- return {
139
- 'aria-dropeffect': zone.dropEffect ?? 'move',
140
- 'aria-label': ariaLabel ?? label,
141
- 'aria-describedby': zone.ariaDescribedBy,
142
- role: zone.role ?? 'list',
143
- 'data-drop-accept': zone.accept.join(','),
144
- 'data-drop-max-items': zone.maxItems,
145
- 'data-drop-highlight': String(zone.highlightOnDragOver ?? true),
146
- };
147
- }
148
-
149
- // ============================================================================
150
- // Drag Constraint Styles
151
- // ============================================================================
152
-
153
- /**
154
- * Resolve a DragConstraint into CSS style properties that restrict
155
- * drag movement along an axis or within bounds.
156
- *
157
- * @param constraint - DragConstraint configuration from the spec
158
- * @returns CSS styles object for constraining drag movement
159
- */
160
- export function resolveDragConstraints(constraint: DragConstraint): DragConstraintStyles {
161
- const styles: DragConstraintStyles = {
162
- position: 'relative',
163
- touchAction: 'none',
164
- };
165
-
166
- const axis = constraint.axis ?? 'both';
167
- if (axis === 'x') {
168
- styles.touchAction = 'pan-y';
169
- } else if (axis === 'y') {
170
- styles.touchAction = 'pan-x';
171
- }
172
-
173
- const bounds = constraint.bounds ?? 'none';
174
- if (bounds === 'parent') {
175
- styles.overflow = 'hidden';
176
- }
177
-
178
- if (constraint.grid) {
179
- styles['--drag-grid-x'] = `${constraint.grid[0]}px`;
180
- styles['--drag-grid-y'] = `${constraint.grid[1]}px`;
181
- }
182
-
183
- return styles;
184
- }
@@ -1,185 +0,0 @@
1
- /**
2
- * ObjectUI
3
- * Copyright (c) 2024-present ObjectStack Inc.
4
- *
5
- * This source code is licensed under the MIT license found in the
6
- * LICENSE file in the root directory of this source tree.
7
- */
8
-
9
- /**
10
- * @object-ui/core - Keyboard Protocol Bridge
11
- *
12
- * Converts spec-aligned keyboard navigation and focus management
13
- * schemas into runtime-usable configurations, shortcut parsers,
14
- * and focus trap settings.
15
- *
16
- * @module protocols/KeyboardProtocol
17
- * @packageDocumentation
18
- */
19
-
20
- import type {
21
- KeyboardNavigationConfig,
22
- KeyboardShortcut,
23
- FocusManagement,
24
- FocusTrapConfig,
25
- } from '@object-ui/types';
26
-
27
- // ============================================================================
28
- // Resolved Types
29
- // ============================================================================
30
-
31
- /** Fully resolved keyboard navigation configuration. */
32
- export interface ResolvedKeyboardConfig {
33
- shortcuts: KeyboardShortcut[];
34
- focusManagement: ResolvedFocusManagement;
35
- rovingTabindex: boolean;
36
- ariaLabel?: string;
37
- ariaDescribedBy?: string;
38
- role?: string;
39
- }
40
-
41
- /** Fully resolved focus management configuration. */
42
- export interface ResolvedFocusManagement {
43
- tabOrder: 'auto' | 'manual';
44
- skipLinks: boolean;
45
- focusVisible: boolean;
46
- focusTrap?: ResolvedFocusTrapConfig;
47
- arrowNavigation: boolean;
48
- }
49
-
50
- /** Fully resolved focus trap configuration. */
51
- export interface ResolvedFocusTrapConfig {
52
- enabled: boolean;
53
- initialFocus?: string;
54
- returnFocus: boolean;
55
- escapeDeactivates: boolean;
56
- }
57
-
58
- /** Parsed keyboard shortcut descriptor. */
59
- export interface ParsedShortcut {
60
- key: string;
61
- ctrlOrMeta: boolean;
62
- shift: boolean;
63
- alt: boolean;
64
- }
65
-
66
- /** Minimal keyboard event shape for matching (no React dependency). */
67
- export interface KeyboardEventLike {
68
- key: string;
69
- ctrlKey?: boolean;
70
- metaKey?: boolean;
71
- shiftKey?: boolean;
72
- altKey?: boolean;
73
- }
74
-
75
- // ============================================================================
76
- // Keyboard Config Resolution
77
- // ============================================================================
78
-
79
- /**
80
- * Resolve a keyboard navigation configuration by applying spec defaults.
81
- *
82
- * @param config - KeyboardNavigationConfig from the spec
83
- * @returns Fully resolved keyboard navigation configuration
84
- */
85
- export function resolveKeyboardConfig(config: KeyboardNavigationConfig): ResolvedKeyboardConfig {
86
- const ariaLabel = typeof config.ariaLabel === 'string'
87
- ? config.ariaLabel
88
- : config.ariaLabel?.defaultValue;
89
-
90
- return {
91
- shortcuts: config.shortcuts ?? [],
92
- focusManagement: resolveFocusManagement(config.focusManagement),
93
- rovingTabindex: config.rovingTabindex ?? false,
94
- ariaLabel,
95
- ariaDescribedBy: config.ariaDescribedBy,
96
- role: config.role,
97
- };
98
- }
99
-
100
- /**
101
- * Resolve focus management configuration with defaults.
102
- */
103
- function resolveFocusManagement(fm?: FocusManagement): ResolvedFocusManagement {
104
- return {
105
- tabOrder: fm?.tabOrder ?? 'auto',
106
- skipLinks: fm?.skipLinks ?? false,
107
- focusVisible: fm?.focusVisible ?? true,
108
- focusTrap: fm?.focusTrap ? createFocusTrapConfig(fm.focusTrap) : undefined,
109
- arrowNavigation: fm?.arrowNavigation ?? false,
110
- };
111
- }
112
-
113
- // ============================================================================
114
- // Shortcut Parsing
115
- // ============================================================================
116
-
117
- /**
118
- * Parse a shortcut string like "Ctrl+Shift+S" into a structured descriptor.
119
- * Recognises Ctrl, Meta, Cmd (mapped to ctrlOrMeta), Shift, and Alt modifiers.
120
- * The last segment is treated as the key.
121
- *
122
- * Keys are normalised to lowercase for case-insensitive matching. The Shift
123
- * modifier is tracked separately, so "Shift+A" and "Shift+a" are equivalent
124
- * (both represent the same physical key combination).
125
- *
126
- * @param shortcut - Shortcut string (e.g. "Ctrl+S", "Alt+Shift+N", "Escape")
127
- * @returns Parsed shortcut descriptor
128
- */
129
- export function parseShortcutKey(shortcut: string): ParsedShortcut {
130
- const parts = shortcut.split('+').map(p => p.trim());
131
- const modifiers = new Set(parts.slice(0, -1).map(m => m.toLowerCase()));
132
- const key = (parts[parts.length - 1] ?? '').toLowerCase();
133
-
134
- return {
135
- key,
136
- ctrlOrMeta: modifiers.has('ctrl') || modifiers.has('meta') || modifiers.has('cmd'),
137
- shift: modifiers.has('shift'),
138
- alt: modifiers.has('alt'),
139
- };
140
- }
141
-
142
- // ============================================================================
143
- // Shortcut Matching
144
- // ============================================================================
145
-
146
- /**
147
- * Test whether a keyboard event matches a shortcut string.
148
- * Comparison is case-insensitive; the Shift modifier is checked separately.
149
- *
150
- * @param event - The keyboard event (or a plain object with key + modifier flags)
151
- * @param shortcut - Shortcut string (e.g. "Ctrl+S")
152
- * @returns `true` if the event matches the shortcut
153
- */
154
- export function matchesShortcut(event: KeyboardEventLike, shortcut: string): boolean {
155
- const parsed = parseShortcutKey(shortcut);
156
-
157
- if (event.key.toLowerCase() !== parsed.key) return false;
158
-
159
- const eventCtrlOrMeta = !!(event.ctrlKey || event.metaKey);
160
- if (parsed.ctrlOrMeta !== eventCtrlOrMeta) return false;
161
-
162
- if (parsed.shift !== !!(event.shiftKey)) return false;
163
- if (parsed.alt !== !!(event.altKey)) return false;
164
-
165
- return true;
166
- }
167
-
168
- // ============================================================================
169
- // Focus Trap Configuration
170
- // ============================================================================
171
-
172
- /**
173
- * Create a resolved focus trap configuration from a spec FocusTrapConfig.
174
- *
175
- * @param config - FocusTrapConfig from the spec
176
- * @returns Resolved focus trap configuration with defaults applied
177
- */
178
- export function createFocusTrapConfig(config: FocusTrapConfig): ResolvedFocusTrapConfig {
179
- return {
180
- enabled: config.enabled ?? false,
181
- initialFocus: config.initialFocus,
182
- returnFocus: config.returnFocus ?? true,
183
- escapeDeactivates: config.escapeDeactivates ?? true,
184
- };
185
- }
@@ -1,159 +0,0 @@
1
- /**
2
- * ObjectUI
3
- * Copyright (c) 2024-present ObjectStack Inc.
4
- *
5
- * This source code is licensed under the MIT license found in the
6
- * LICENSE file in the root directory of this source tree.
7
- */
8
-
9
- /**
10
- * @object-ui/core - Notification Protocol Bridge
11
- *
12
- * Converts spec-aligned Notification schemas into toast-compatible
13
- * objects that UI layers can render. Maps severity to variant,
14
- * position names, and resolves default notification configs.
15
- *
16
- * @module protocols/NotificationProtocol
17
- * @packageDocumentation
18
- */
19
-
20
- import type {
21
- Notification as SpecNotification,
22
- NotificationAction,
23
- NotificationConfig,
24
- NotificationPosition,
25
- NotificationSeverity,
26
- } from '@object-ui/types';
27
-
28
- // ============================================================================
29
- // Resolved Types
30
- // ============================================================================
31
-
32
- /** Fully resolved notification configuration. */
33
- export interface ResolvedNotificationConfig {
34
- defaultPosition: NotificationPosition;
35
- defaultDuration: number;
36
- maxVisible: number;
37
- stackDirection: 'up' | 'down';
38
- pauseOnHover: boolean;
39
- }
40
-
41
- /** Toast-compatible representation of a spec Notification. */
42
- export interface ToastNotification {
43
- title?: string;
44
- description: string;
45
- variant: string;
46
- position: string;
47
- duration: number;
48
- dismissible: boolean;
49
- actions: ToastAction[];
50
- }
51
-
52
- /** Toast-compatible action button. */
53
- export interface ToastAction {
54
- label: string;
55
- action: string;
56
- variant: 'primary' | 'secondary' | 'link';
57
- }
58
-
59
- // ============================================================================
60
- // Severity → Variant Mapping
61
- // ============================================================================
62
-
63
- const SEVERITY_TO_VARIANT: Record<string, string> = {
64
- info: 'default',
65
- success: 'success',
66
- warning: 'warning',
67
- error: 'destructive',
68
- };
69
-
70
- /**
71
- * Map a spec notification severity to a toast variant string.
72
- *
73
- * @param severity - Spec severity (info, success, warning, error)
74
- * @returns Toast variant (default, success, warning, destructive)
75
- */
76
- export function mapSeverityToVariant(severity: string): string {
77
- return SEVERITY_TO_VARIANT[severity] ?? 'default';
78
- }
79
-
80
- // ============================================================================
81
- // Position Mapping
82
- // ============================================================================
83
-
84
- const POSITION_MAP: Record<string, string> = {
85
- top_left: 'top-left',
86
- top_center: 'top-center',
87
- top_right: 'top-right',
88
- bottom_left: 'bottom-left',
89
- bottom_center: 'bottom-center',
90
- bottom_right: 'bottom-right',
91
- };
92
-
93
- /**
94
- * Map a spec notification position (underscore-separated) to a
95
- * toast position string (hyphen-separated).
96
- *
97
- * @param position - Spec position (e.g. "top_right")
98
- * @returns Toast position (e.g. "top-right")
99
- */
100
- export function mapPosition(position: string): string {
101
- return POSITION_MAP[position] ?? 'top-right';
102
- }
103
-
104
- // ============================================================================
105
- // Notification Config Resolution
106
- // ============================================================================
107
-
108
- /**
109
- * Resolve a notification configuration by applying spec defaults.
110
- *
111
- * @param config - NotificationConfig from the spec
112
- * @returns Fully resolved notification configuration
113
- */
114
- export function resolveNotificationConfig(config: NotificationConfig): ResolvedNotificationConfig {
115
- return {
116
- defaultPosition: config.defaultPosition ?? 'top_right',
117
- defaultDuration: config.defaultDuration ?? 5000,
118
- maxVisible: config.maxVisible ?? 5,
119
- stackDirection: config.stackDirection ?? 'down',
120
- pauseOnHover: config.pauseOnHover ?? true,
121
- };
122
- }
123
-
124
- // ============================================================================
125
- // Spec Notification → Toast
126
- // ============================================================================
127
-
128
- /**
129
- * Extract the display string from a translatable value (string or Translation object).
130
- */
131
- function resolveTranslatableString(value: string | { key: string; defaultValue?: string } | undefined): string | undefined {
132
- if (value === undefined) return undefined;
133
- if (typeof value === 'string') return value;
134
- return value.defaultValue;
135
- }
136
-
137
- /**
138
- * Convert a spec Notification to a toast-compatible object.
139
- *
140
- * @param notification - Spec Notification
141
- * @returns Toast-compatible notification with title, description, variant, etc.
142
- */
143
- export function specNotificationToToast(notification: SpecNotification): ToastNotification {
144
- const actions: ToastAction[] = (notification.actions ?? []).map((a: NotificationAction) => ({
145
- label: typeof a.label === 'string' ? a.label : a.label?.defaultValue ?? '',
146
- action: a.action,
147
- variant: a.variant ?? 'primary',
148
- }));
149
-
150
- return {
151
- title: resolveTranslatableString(notification.title),
152
- description: resolveTranslatableString(notification.message) ?? '',
153
- variant: mapSeverityToVariant(notification.severity ?? 'info'),
154
- position: mapPosition(notification.position ?? 'top_right'),
155
- duration: notification.duration ?? 5000,
156
- dismissible: notification.dismissible ?? true,
157
- actions,
158
- };
159
- }