@seekora-ai/ui-sdk-core 0.1.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.
@@ -0,0 +1,449 @@
1
+ import { ThemeConfig, Theme } from '@seekora-ai/ui-sdk-types';
2
+ import { SearchResponse, SeekoraClient, SearchOptions } from '@seekora-ai/search-sdk';
3
+
4
+ /**
5
+ * Field Mapping Utilities
6
+ *
7
+ * Shared utilities for extracting and mapping fields from API responses
8
+ */
9
+ /**
10
+ * Get nested value from object using dot notation path
11
+ */
12
+ declare const getNestedValue: (obj: any, path: string) => any;
13
+ /**
14
+ * Format price value with currency
15
+ */
16
+ declare const formatPrice: (value: any, currency?: string) => string | undefined;
17
+ /**
18
+ * Extract field value using dot notation path
19
+ */
20
+ declare const extractField: (item: any, path: string | undefined) => any;
21
+
22
+ /**
23
+ * Theme Management Utilities
24
+ *
25
+ * Shared theme utilities for creating and merging themes
26
+ */
27
+
28
+ /**
29
+ * Create a complete theme from a partial theme configuration
30
+ */
31
+ declare const createTheme: (config: ThemeConfig, baseTheme: Theme) => Theme;
32
+ /**
33
+ * Merge multiple themes together
34
+ */
35
+ declare const mergeThemes: (baseTheme: Theme, ...themes: Partial<Theme>[]) => Theme;
36
+
37
+ /**
38
+ * CSS Variables Theme System
39
+ *
40
+ * Generates CSS variables from theme configuration
41
+ * Provides utilities for injecting and managing CSS themes
42
+ */
43
+
44
+ interface CSSVariablesConfig {
45
+ /** CSS selector for the theme scope (default: ':root') */
46
+ scope?: string;
47
+ /** Prefix for CSS variable names (default: '--seekora') */
48
+ prefix?: string;
49
+ /** Whether to include component classes (default: true) */
50
+ includeClasses?: boolean;
51
+ }
52
+ /**
53
+ * Convert a theme object to CSS variables
54
+ */
55
+ declare function themeToCSSVariables(theme: Theme, config?: CSSVariablesConfig): string;
56
+ /**
57
+ * Generate complete CSS stylesheet with variables and optional component classes
58
+ */
59
+ declare function generateThemeStylesheet(theme: Theme, config?: CSSVariablesConfig): string;
60
+ /**
61
+ * Theme presets
62
+ */
63
+ declare const lightThemeVariables = "\n:root, [data-seekora-theme=\"light\"] {\n --seekora-color-primary: #0066cc;\n --seekora-color-secondary: #6c757d;\n --seekora-color-background: #ffffff;\n --seekora-color-surface: #f8f9fa;\n --seekora-color-text: #212529;\n --seekora-color-textSecondary: #6c757d;\n --seekora-color-border: #dee2e6;\n --seekora-color-hover: #f1f3f5;\n --seekora-color-focus: rgba(0, 102, 204, 0.25);\n --seekora-color-error: #dc3545;\n --seekora-color-success: #28a745;\n --seekora-color-warning: #ffc107;\n \n --seekora-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;\n --seekora-font-size-small: 0.875rem;\n --seekora-font-size-medium: 1rem;\n --seekora-font-size-large: 1.25rem;\n --seekora-font-weight-normal: 400;\n --seekora-font-weight-medium: 500;\n --seekora-font-weight-semibold: 600;\n --seekora-font-weight-bold: 700;\n --seekora-line-height-tight: 1.25;\n --seekora-line-height-normal: 1.5;\n --seekora-line-height-relaxed: 1.75;\n \n --seekora-spacing-small: 0.5rem;\n --seekora-spacing-medium: 1rem;\n --seekora-spacing-large: 1.5rem;\n \n --seekora-border-radius: 4px;\n --seekora-border-radius-none: 0;\n --seekora-border-radius-small: 2px;\n --seekora-border-radius-medium: 4px;\n --seekora-border-radius-large: 8px;\n --seekora-border-radius-full: 9999px;\n \n --seekora-shadow-small: 0 1px 2px rgba(0, 0, 0, 0.05);\n --seekora-shadow-medium: 0 4px 6px rgba(0, 0, 0, 0.1);\n --seekora-shadow-large: 0 10px 15px rgba(0, 0, 0, 0.1);\n \n --seekora-transition-fast: 150ms ease-in-out;\n --seekora-transition-normal: 300ms ease-in-out;\n --seekora-transition-slow: 500ms ease-in-out;\n \n --seekora-z-index-dropdown: 1000;\n --seekora-z-index-modal: 1050;\n --seekora-z-index-tooltip: 1100;\n}\n";
64
+ declare const darkThemeVariables = "\n[data-seekora-theme=\"dark\"] {\n --seekora-color-primary: #4da6ff;\n --seekora-color-secondary: #adb5bd;\n --seekora-color-background: #1a1a1a;\n --seekora-color-surface: #2d2d2d;\n --seekora-color-text: #f8f9fa;\n --seekora-color-textSecondary: #adb5bd;\n --seekora-color-border: #495057;\n --seekora-color-hover: #343a40;\n --seekora-color-focus: rgba(77, 166, 255, 0.25);\n --seekora-color-error: #f87171;\n --seekora-color-success: #4ade80;\n --seekora-color-warning: #fbbf24;\n \n --seekora-shadow-small: 0 1px 2px rgba(0, 0, 0, 0.3);\n --seekora-shadow-medium: 0 4px 6px rgba(0, 0, 0, 0.4);\n --seekora-shadow-large: 0 10px 15px rgba(0, 0, 0, 0.5);\n}\n";
65
+ declare const minimalThemeVariables = "\n[data-seekora-theme=\"minimal\"] {\n --seekora-color-primary: #000000;\n --seekora-color-secondary: #666666;\n --seekora-color-background: #ffffff;\n --seekora-color-surface: #fafafa;\n --seekora-color-text: #000000;\n --seekora-color-textSecondary: #666666;\n --seekora-color-border: #e5e5e5;\n --seekora-color-hover: #f5f5f5;\n --seekora-color-focus: rgba(0, 0, 0, 0.1);\n \n --seekora-border-radius: 0;\n --seekora-border-radius-none: 0;\n --seekora-border-radius-small: 0;\n --seekora-border-radius-medium: 0;\n --seekora-border-radius-large: 0;\n --seekora-border-radius-full: 0;\n \n --seekora-shadow-small: none;\n --seekora-shadow-medium: none;\n --seekora-shadow-large: none;\n}\n";
66
+ declare const highContrastThemeVariables = "\n[data-seekora-theme=\"high-contrast\"] {\n --seekora-color-primary: #0000ff;\n --seekora-color-secondary: #000000;\n --seekora-color-background: #ffffff;\n --seekora-color-surface: #ffffff;\n --seekora-color-text: #000000;\n --seekora-color-textSecondary: #000000;\n --seekora-color-border: #000000;\n --seekora-color-hover: #ffff00;\n --seekora-color-focus: #0000ff;\n --seekora-color-error: #ff0000;\n --seekora-color-success: #008000;\n --seekora-color-warning: #ffff00;\n \n --seekora-font-weight-normal: 500;\n --seekora-font-weight-medium: 600;\n --seekora-font-weight-semibold: 700;\n --seekora-font-weight-bold: 900;\n}\n";
67
+ /**
68
+ * Complete stylesheet with all presets and component classes
69
+ */
70
+ declare function generateCompleteStylesheet(): string;
71
+ /**
72
+ * Inject styles into the document head
73
+ */
74
+ declare function injectStyles(css: string, id?: string): void;
75
+ /**
76
+ * Set the active theme
77
+ */
78
+ declare function setTheme(themeName: 'light' | 'dark' | 'minimal' | 'high-contrast'): void;
79
+ /**
80
+ * Get the current theme
81
+ */
82
+ declare function getTheme(): string;
83
+
84
+ /**
85
+ * Logger utility for Seekora UI SDK
86
+ *
87
+ * Provides structured logging with different log levels:
88
+ * - verbose: Detailed debugging information
89
+ * - info: General informational messages
90
+ * - warn: Warning messages for potential issues
91
+ * - error: Error messages for failures
92
+ */
93
+ declare enum LogLevel {
94
+ VERBOSE = 0,
95
+ INFO = 1,
96
+ WARN = 2,
97
+ ERROR = 3,
98
+ SILENT = 4
99
+ }
100
+ interface LoggerConfig {
101
+ level?: LogLevel;
102
+ prefix?: string;
103
+ enableTimestamp?: boolean;
104
+ }
105
+ declare class Logger {
106
+ private level;
107
+ private prefix;
108
+ private enableTimestamp;
109
+ constructor(config?: LoggerConfig);
110
+ private getDefaultLevel;
111
+ private formatMessage;
112
+ verbose(...args: any[]): void;
113
+ info(...args: any[]): void;
114
+ warn(...args: any[]): void;
115
+ error(...args: any[]): void;
116
+ setLevel(level: LogLevel): void;
117
+ getLevel(): LogLevel;
118
+ setPrefix(prefix: string): void;
119
+ setTimestamp(enabled: boolean): void;
120
+ }
121
+ declare function createLogger(config?: LoggerConfig): Logger;
122
+ declare function getLogger(): Logger;
123
+ declare function setDefaultLogger(logger: Logger): void;
124
+ declare function setLogLevel(level: LogLevel): void;
125
+ declare function setLogPrefix(prefix: string): void;
126
+ declare function setLogTimestamp(enabled: boolean): void;
127
+ declare const log: {
128
+ verbose: (...args: any[]) => void;
129
+ info: (...args: any[]) => void;
130
+ warn: (...args: any[]) => void;
131
+ error: (...args: any[]) => void;
132
+ };
133
+
134
+ /**
135
+ * Highlight Utilities
136
+ *
137
+ * Core utilities for highlighting matching terms in search results
138
+ * Framework-agnostic functions shared across all packages
139
+ */
140
+ interface HighlightOptions {
141
+ /** The attribute to highlight */
142
+ attribute: string;
143
+ /** The search hit containing highlight data */
144
+ hit: Record<string, any>;
145
+ /** Pre-tag for highlighted text (default: '<mark>') */
146
+ preTag?: string;
147
+ /** Post-tag for highlighted text (default: '</mark>') */
148
+ postTag?: string;
149
+ /** Fallback value if no highlight exists */
150
+ fallback?: string;
151
+ }
152
+ interface SnippetOptions extends HighlightOptions {
153
+ /** Maximum length of snippet (default: 100) */
154
+ maxLength?: number;
155
+ /** Ellipsis to use when truncating (default: '...') */
156
+ ellipsis?: string;
157
+ }
158
+ interface HighlightPart {
159
+ value: string;
160
+ isHighlighted: boolean;
161
+ }
162
+ /**
163
+ * Get highlighted value from a hit
164
+ * Looks for _highlightResult or highlight_result field
165
+ */
166
+ declare function getHighlightedValue(options: HighlightOptions): string;
167
+ /**
168
+ * Get snippet (truncated highlighted value) from a hit
169
+ */
170
+ declare function getSnippetedValue(options: SnippetOptions): string;
171
+ /**
172
+ * Parse highlighted value into parts for custom rendering
173
+ */
174
+ declare function parseHighlightedParts(highlightedValue: string, preTag?: string, postTag?: string): HighlightPart[];
175
+ /**
176
+ * Highlight a query in text (client-side highlighting)
177
+ * Useful when server doesn't provide highlight data
178
+ */
179
+ declare function highlightQuery(text: string, query: string, options?: {
180
+ preTag?: string;
181
+ postTag?: string;
182
+ caseSensitive?: boolean;
183
+ }): string;
184
+ /**
185
+ * Parse query-highlighted text into parts
186
+ */
187
+ declare function parseQueryHighlightParts(text: string, query: string, caseSensitive?: boolean): HighlightPart[];
188
+ /**
189
+ * Strip HTML tags from highlighted text
190
+ */
191
+ declare function stripHighlightTags(text: string): string;
192
+
193
+ /**
194
+ * Accessibility Utilities
195
+ *
196
+ * Shared accessibility helpers for keyboard navigation, focus management,
197
+ * screen reader announcements, and ARIA attributes.
198
+ */
199
+ /**
200
+ * Announce a message to screen readers
201
+ */
202
+ declare function announce(message: string, priority?: 'polite' | 'assertive'): void;
203
+ /**
204
+ * Announce search results count
205
+ */
206
+ declare function announceResults(count: number, query?: string): void;
207
+ /**
208
+ * Announce filter changes
209
+ */
210
+ declare function announceFilterChange(action: 'added' | 'removed' | 'cleared', filterName?: string, filterValue?: string): void;
211
+ interface KeyboardNavigationOptions {
212
+ /** List of navigable items */
213
+ items: HTMLElement[];
214
+ /** Current active index (-1 for none) */
215
+ activeIndex: number;
216
+ /** Callback when active index changes */
217
+ onActiveChange: (index: number) => void;
218
+ /** Callback when item is selected */
219
+ onSelect?: (index: number) => void;
220
+ /** Callback when navigation is cancelled (Escape) */
221
+ onCancel?: () => void;
222
+ /** Whether navigation wraps around */
223
+ wrap?: boolean;
224
+ /** Orientation */
225
+ orientation?: 'vertical' | 'horizontal' | 'both';
226
+ }
227
+ /**
228
+ * Handle keyboard navigation for a list of items
229
+ */
230
+ declare function handleKeyboardNavigation(event: KeyboardEvent, options: KeyboardNavigationOptions): boolean;
231
+ /**
232
+ * Create a roving tabindex manager for accessible list navigation
233
+ */
234
+ declare function createRovingTabIndex(container: HTMLElement, selector: string): {
235
+ update: (activeIndex: number) => void;
236
+ destroy: () => void;
237
+ };
238
+ /**
239
+ * Get all focusable elements within a container
240
+ */
241
+ declare function getFocusableElements(container: HTMLElement): HTMLElement[];
242
+ /**
243
+ * Create a focus trap within a container (for modals/dialogs)
244
+ */
245
+ declare function createFocusTrap(container: HTMLElement): {
246
+ activate: () => void;
247
+ deactivate: () => void;
248
+ };
249
+ /**
250
+ * Focus first error or first input in a form
251
+ */
252
+ declare function focusFirstError(container: HTMLElement): boolean;
253
+ declare function generateId(prefix?: string): string;
254
+ /**
255
+ * Set up ARIA describedby relationship
256
+ */
257
+ declare function setAriaDescribedBy(element: HTMLElement, describer: HTMLElement): () => void;
258
+ /**
259
+ * Check if user prefers reduced motion
260
+ */
261
+ declare function prefersReducedMotion(): boolean;
262
+ /**
263
+ * Check if user prefers high contrast
264
+ */
265
+ declare function prefersHighContrast(): boolean;
266
+ interface KeyboardShortcut {
267
+ key: string;
268
+ ctrl?: boolean;
269
+ alt?: boolean;
270
+ shift?: boolean;
271
+ meta?: boolean;
272
+ handler: (e: KeyboardEvent) => void;
273
+ description?: string;
274
+ }
275
+ /**
276
+ * Create a keyboard shortcuts manager
277
+ */
278
+ declare function createKeyboardShortcuts(shortcuts: KeyboardShortcut[]): {
279
+ attach: () => void;
280
+ detach: () => void;
281
+ getShortcuts: () => KeyboardShortcut[];
282
+ };
283
+
284
+ /**
285
+ * SearchStateManager
286
+ *
287
+ * Centralized state management for search operations
288
+ * Framework-agnostic core logic shared across all packages
289
+ * Handles query, refinements, pagination, sorting, and automatic search triggering
290
+ * Similar to InstantSearch.js architecture
291
+ */
292
+
293
+ interface Refinement {
294
+ field: string;
295
+ value: string;
296
+ }
297
+ interface SearchState {
298
+ query: string;
299
+ refinements: Refinement[];
300
+ currentPage: number;
301
+ itemsPerPage: number;
302
+ sortBy?: string;
303
+ results: SearchResponse | null;
304
+ loading: boolean;
305
+ error: Error | null;
306
+ }
307
+ interface SearchStateManagerConfig {
308
+ client: SeekoraClient;
309
+ initialQuery?: string;
310
+ itemsPerPage?: number;
311
+ autoSearch?: boolean;
312
+ debounceMs?: number;
313
+ defaultSearchOptions?: Partial<SearchOptions>;
314
+ }
315
+ declare class SearchStateManager {
316
+ private client;
317
+ private state;
318
+ private listeners;
319
+ private debounceTimer;
320
+ private autoSearch;
321
+ private debounceMs;
322
+ private defaultSearchOptions;
323
+ constructor(config: SearchStateManagerConfig);
324
+ getState(): SearchState;
325
+ getQuery(): string;
326
+ getRefinements(): Refinement[];
327
+ getCurrentPage(): number;
328
+ getResults(): SearchResponse | null;
329
+ getLoading(): boolean;
330
+ getError(): Error | null;
331
+ setQuery(query: string, triggerSearch?: boolean): void;
332
+ addRefinement(field: string, value: string, triggerSearch?: boolean): void;
333
+ removeRefinement(field: string, value: string, triggerSearch?: boolean): void;
334
+ clearRefinements(triggerSearch?: boolean): void;
335
+ setPage(page: number, triggerSearch?: boolean): void;
336
+ setSortBy(sortBy: string, triggerSearch?: boolean): void;
337
+ setItemsPerPage(itemsPerPage: number, triggerSearch?: boolean): void;
338
+ search(additionalOptions?: Partial<SearchOptions>): Promise<SearchResponse | null>;
339
+ private buildSearchOptions;
340
+ private debouncedSearch;
341
+ subscribe(listener: (state: SearchState) => void): () => void;
342
+ private setState;
343
+ private notifyListeners;
344
+ clear(): void;
345
+ }
346
+
347
+ /**
348
+ * URL Router
349
+ *
350
+ * Syncs search state with browser URL for shareable links and back/forward navigation
351
+ * Framework-agnostic core logic shared across all packages
352
+ */
353
+
354
+ interface URLStateMapping {
355
+ /** Query parameter name for search query (default: 'q') */
356
+ query?: string;
357
+ /** Query parameter name for page (default: 'page') */
358
+ page?: string;
359
+ /** Query parameter name for items per page (default: 'hitsPerPage') */
360
+ hitsPerPage?: string;
361
+ /** Query parameter name for sort (default: 'sortBy') */
362
+ sortBy?: string;
363
+ /** Prefix for refinement parameters (default: '') */
364
+ refinementPrefix?: string;
365
+ /** Custom serialization for refinements */
366
+ refinementSerializer?: (refinements: Refinement[]) => Record<string, string>;
367
+ /** Custom deserialization for refinements */
368
+ refinementDeserializer?: (params: URLSearchParams) => Refinement[];
369
+ }
370
+ interface URLRouterConfig {
371
+ /** SearchStateManager instance to sync with */
372
+ stateManager: SearchStateManager;
373
+ /** State mapping configuration */
374
+ mapping?: URLStateMapping;
375
+ /** Whether to write state to URL (default: true) */
376
+ writeToUrl?: boolean;
377
+ /** Whether to read initial state from URL (default: true) */
378
+ readFromUrl?: boolean;
379
+ /** Whether to use history.pushState or replaceState (default: 'push') */
380
+ historyMode?: 'push' | 'replace';
381
+ /** Debounce delay for URL updates in ms (default: 400) */
382
+ debounceMs?: number;
383
+ /** Base URL path (default: current path) */
384
+ basePath?: string;
385
+ /** Whether to trigger search on URL change (default: true) */
386
+ triggerSearchOnChange?: boolean;
387
+ }
388
+ declare class URLRouter {
389
+ private stateManager;
390
+ private mapping;
391
+ private writeToUrl;
392
+ private readFromUrl;
393
+ private historyMode;
394
+ private debounceMs;
395
+ private basePath;
396
+ private triggerSearchOnChange;
397
+ private unsubscribe;
398
+ private debounceTimer;
399
+ private isUpdatingFromUrl;
400
+ constructor(config: URLRouterConfig);
401
+ private init;
402
+ /**
403
+ * Read state from current URL and update StateManager
404
+ */
405
+ private readStateFromUrl;
406
+ /**
407
+ * Write state to URL
408
+ */
409
+ private writeStateToUrl;
410
+ /**
411
+ * Debounced write to URL
412
+ */
413
+ private debouncedWriteToUrl;
414
+ /**
415
+ * Handle browser back/forward navigation
416
+ */
417
+ private handlePopState;
418
+ /**
419
+ * Serialize refinements to URL params
420
+ */
421
+ private serializeRefinements;
422
+ /**
423
+ * Deserialize refinements from URL params
424
+ */
425
+ private deserializeRefinements;
426
+ /**
427
+ * Manually update URL from current state
428
+ */
429
+ refresh(): void;
430
+ /**
431
+ * Get current URL
432
+ */
433
+ getUrl(): string;
434
+ /**
435
+ * Generate shareable URL from state
436
+ */
437
+ createUrl(state?: Partial<SearchState>): string;
438
+ /**
439
+ * Destroy the router and cleanup
440
+ */
441
+ destroy(): void;
442
+ }
443
+ /**
444
+ * Create a URL router instance
445
+ */
446
+ declare function createURLRouter(config: URLRouterConfig): URLRouter;
447
+
448
+ export { LogLevel, SearchStateManager, URLRouter, announce, announceFilterChange, announceResults, createFocusTrap, createKeyboardShortcuts, createLogger, createRovingTabIndex, createTheme, createURLRouter, darkThemeVariables, extractField, focusFirstError, formatPrice, generateCompleteStylesheet, generateId, generateThemeStylesheet, getFocusableElements, getHighlightedValue, getLogger, getNestedValue, getSnippetedValue, getTheme, handleKeyboardNavigation, highContrastThemeVariables, highlightQuery, injectStyles, lightThemeVariables, log, mergeThemes, minimalThemeVariables, parseHighlightedParts, parseQueryHighlightParts, prefersHighContrast, prefersReducedMotion, setAriaDescribedBy, setDefaultLogger, setLogLevel, setLogPrefix, setLogTimestamp, setTheme, stripHighlightTags, themeToCSSVariables };
449
+ export type { CSSVariablesConfig, HighlightOptions, HighlightPart, KeyboardNavigationOptions, KeyboardShortcut, LoggerConfig, Refinement, SearchState, SearchStateManagerConfig, SnippetOptions, URLRouterConfig, URLStateMapping };