@salmexio/ui 1.2.1 → 1.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 (88) hide show
  1. package/dist/dialogs/Modal/Modal.svelte +1 -1
  2. package/dist/feedback/Alert/Alert.svelte +4 -1
  3. package/dist/feedback/Alert/Alert.svelte.d.ts +1 -0
  4. package/dist/feedback/Alert/Alert.svelte.d.ts.map +1 -1
  5. package/dist/feedback/Skeleton/Skeleton.svelte +3 -13
  6. package/dist/feedback/Skeleton/Skeleton.svelte.d.ts.map +1 -1
  7. package/dist/feedback/Spinner/Spinner.svelte +4 -1
  8. package/dist/feedback/Spinner/Spinner.svelte.d.ts +1 -0
  9. package/dist/feedback/Spinner/Spinner.svelte.d.ts.map +1 -1
  10. package/dist/feedback/Toast/Toaster.svelte +9 -8
  11. package/dist/feedback/Toast/Toaster.svelte.d.ts.map +1 -1
  12. package/dist/forms/DatePicker/DatePicker.svelte +755 -0
  13. package/dist/forms/DatePicker/DatePicker.svelte.d.ts +48 -0
  14. package/dist/forms/DatePicker/DatePicker.svelte.d.ts.map +1 -0
  15. package/dist/forms/DatePicker/index.d.ts +2 -0
  16. package/dist/forms/DatePicker/index.d.ts.map +1 -0
  17. package/dist/forms/DatePicker/index.js +1 -0
  18. package/dist/forms/FormField/FormField.svelte +173 -0
  19. package/dist/forms/FormField/FormField.svelte.d.ts +46 -0
  20. package/dist/forms/FormField/FormField.svelte.d.ts.map +1 -0
  21. package/dist/forms/FormField/index.d.ts +2 -0
  22. package/dist/forms/FormField/index.d.ts.map +1 -0
  23. package/dist/forms/FormField/index.js +1 -0
  24. package/dist/forms/MultiSelect/MultiSelect.svelte +820 -0
  25. package/dist/forms/MultiSelect/MultiSelect.svelte.d.ts +69 -0
  26. package/dist/forms/MultiSelect/MultiSelect.svelte.d.ts.map +1 -0
  27. package/dist/forms/MultiSelect/index.d.ts +3 -0
  28. package/dist/forms/MultiSelect/index.d.ts.map +1 -0
  29. package/dist/forms/MultiSelect/index.js +1 -0
  30. package/dist/forms/PhoneInput/PhoneInput.svelte +591 -0
  31. package/dist/forms/PhoneInput/PhoneInput.svelte.d.ts +57 -0
  32. package/dist/forms/PhoneInput/PhoneInput.svelte.d.ts.map +1 -0
  33. package/dist/forms/PhoneInput/index.d.ts +4 -0
  34. package/dist/forms/PhoneInput/index.d.ts.map +1 -0
  35. package/dist/forms/PhoneInput/index.js +2 -0
  36. package/dist/forms/RadioGroup/RadioGroup.svelte +417 -0
  37. package/dist/forms/RadioGroup/RadioGroup.svelte.d.ts +62 -0
  38. package/dist/forms/RadioGroup/RadioGroup.svelte.d.ts.map +1 -0
  39. package/dist/forms/RadioGroup/index.d.ts +3 -0
  40. package/dist/forms/RadioGroup/index.d.ts.map +1 -0
  41. package/dist/forms/RadioGroup/index.js +1 -0
  42. package/dist/forms/SearchInput/SearchInput.svelte +788 -0
  43. package/dist/forms/SearchInput/SearchInput.svelte.d.ts +79 -0
  44. package/dist/forms/SearchInput/SearchInput.svelte.d.ts.map +1 -0
  45. package/dist/forms/SearchInput/index.d.ts +3 -0
  46. package/dist/forms/SearchInput/index.d.ts.map +1 -0
  47. package/dist/forms/SearchInput/index.js +1 -0
  48. package/dist/forms/Select/Select.svelte +14 -8
  49. package/dist/forms/Select/Select.svelte.d.ts +2 -0
  50. package/dist/forms/Select/Select.svelte.d.ts.map +1 -1
  51. package/dist/forms/TextInput/TextInput.svelte +38 -16
  52. package/dist/forms/TextInput/TextInput.svelte.d.ts +6 -0
  53. package/dist/forms/TextInput/TextInput.svelte.d.ts.map +1 -1
  54. package/dist/forms/Textarea/Textarea.svelte +7 -1
  55. package/dist/forms/Textarea/Textarea.svelte.d.ts +2 -0
  56. package/dist/forms/Textarea/Textarea.svelte.d.ts.map +1 -1
  57. package/dist/forms/TimePicker/TimePicker.svelte +417 -0
  58. package/dist/forms/TimePicker/TimePicker.svelte.d.ts +53 -0
  59. package/dist/forms/TimePicker/TimePicker.svelte.d.ts.map +1 -0
  60. package/dist/forms/TimePicker/index.d.ts +2 -0
  61. package/dist/forms/TimePicker/index.d.ts.map +1 -0
  62. package/dist/forms/TimePicker/index.js +1 -0
  63. package/dist/forms/Toggle/Toggle.svelte +0 -2
  64. package/dist/forms/Toggle/Toggle.svelte.d.ts.map +1 -1
  65. package/dist/forms/index.d.ts +12 -0
  66. package/dist/forms/index.d.ts.map +1 -1
  67. package/dist/forms/index.js +8 -0
  68. package/dist/layout/Container/Container.svelte +3 -0
  69. package/dist/layout/Container/Container.svelte.d.ts +1 -0
  70. package/dist/layout/Container/Container.svelte.d.ts.map +1 -1
  71. package/dist/layout/ThermalBackground/ThermalBackground.svelte +17 -17
  72. package/dist/layout/ThermalBackground/ThermalBackground.svelte.d.ts.map +1 -1
  73. package/dist/primitives/Badge/Badge.svelte +5 -1
  74. package/dist/primitives/Badge/Badge.svelte.d.ts +1 -0
  75. package/dist/primitives/Badge/Badge.svelte.d.ts.map +1 -1
  76. package/dist/primitives/Tooltip/Tooltip.svelte +7 -1
  77. package/dist/primitives/Tooltip/Tooltip.svelte.d.ts.map +1 -1
  78. package/dist/styles/tokens.css +78 -46
  79. package/dist/utils/accessibility.d.ts +16 -0
  80. package/dist/utils/accessibility.d.ts.map +1 -0
  81. package/dist/utils/accessibility.js +80 -0
  82. package/dist/utils/index.d.ts +2 -1
  83. package/dist/utils/index.d.ts.map +1 -1
  84. package/dist/utils/index.js +2 -1
  85. package/dist/utils/keyboard.d.ts +6 -0
  86. package/dist/utils/keyboard.d.ts.map +1 -1
  87. package/dist/utils/keyboard.js +15 -9
  88. package/package.json +21 -1
@@ -147,7 +147,13 @@ function positionTooltip() {
147
147
  $effect(() => {
148
148
  if (tooltipEl && visible) {
149
149
  document.body.appendChild(tooltipEl);
150
- tick().then(() => positionTooltip());
150
+ // Position only after the node is in body and laid out (showcase-style behavior)
151
+ const run = () => {
152
+ tick().then(() => {
153
+ positionTooltip();
154
+ });
155
+ };
156
+ requestAnimationFrame(run);
151
157
  return () => {
152
158
  if (tooltipEl?.parentNode === document.body) {
153
159
  document.body.removeChild(tooltipEl);
@@ -1 +1 @@
1
- {"version":3,"file":"Tooltip.svelte.d.ts","sourceRoot":"","sources":["../../../src/primitives/Tooltip/Tooltip.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAMtC,KAAK,SAAS,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;AAErD,UAAU,KAAK;IACd,4BAA4B;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,8CAA8C;IAC9C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,8BAA8B;IAC9B,QAAQ,EAAE,OAAO,CAAC;IAClB,mDAAmD;IACnD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAgKD;;;;;;;;;;;;;;GAcG;AACH,QAAA,MAAM,OAAO,2CAAwC,CAAC;AACtD,KAAK,OAAO,GAAG,UAAU,CAAC,OAAO,OAAO,CAAC,CAAC;AAC1C,eAAe,OAAO,CAAC"}
1
+ {"version":3,"file":"Tooltip.svelte.d.ts","sourceRoot":"","sources":["../../../src/primitives/Tooltip/Tooltip.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAMtC,KAAK,SAAS,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;AAErD,UAAU,KAAK;IACd,4BAA4B;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,8CAA8C;IAC9C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,8BAA8B;IAC9B,QAAQ,EAAE,OAAO,CAAC;IAClB,mDAAmD;IACnD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAsKD;;;;;;;;;;;;;;GAcG;AACH,QAAA,MAAM,OAAO,2CAAwC,CAAC;AACtD,KAAK,OAAO,GAAG,UAAU,CAAC,OAAO,OAAO,CAAC,CAAC;AAC1C,eAAe,OAAO,CAAC"}
@@ -17,42 +17,42 @@
17
17
  Raw palette — never use directly in components
18
18
  ======================================== */
19
19
 
20
- --sx-gray-950: #08080C;
21
- --sx-gray-900: #0F0F14;
20
+ --sx-gray-950: #08080c;
21
+ --sx-gray-900: #0f0f14;
22
22
  --sx-gray-800: #171720;
23
- --sx-gray-700: #1F1F2A;
24
- --sx-gray-600: #2D2D3A;
23
+ --sx-gray-700: #1f1f2a;
24
+ --sx-gray-600: #2d2d3a;
25
25
  --sx-gray-500: #505060;
26
- --sx-gray-400: #7A7A88;
27
- --sx-gray-300: #A0A0AB;
28
- --sx-gray-200: #C5C5CC;
29
- --sx-gray-100: #E8E6E1;
30
- --sx-white: #F0EEE8;
26
+ --sx-gray-400: #7a7a88;
27
+ --sx-gray-300: #a0a0ab;
28
+ --sx-gray-200: #c5c5cc;
29
+ --sx-gray-100: #e8e6e1;
30
+ --sx-white: #f0eee8;
31
31
 
32
32
  /* Accents — Vermilion (forge heat) */
33
- --sx-vermilion-500: #FF6B35;
34
- --sx-vermilion-400: #FF8C5A;
35
- --sx-vermilion-600: #E05520;
33
+ --sx-vermilion-500: #ff6b35;
34
+ --sx-vermilion-400: #ff8c5a;
35
+ --sx-vermilion-600: #e05520;
36
36
 
37
37
  /* Accents — Brass (earned authority) */
38
- --sx-brass-500: #C8A84E;
39
- --sx-brass-400: #D4B86A;
40
- --sx-brass-600: #A68B3A;
38
+ --sx-brass-500: #c8a84e;
39
+ --sx-brass-400: #d4b86a;
40
+ --sx-brass-600: #a68b3a;
41
41
 
42
42
  /* Accents — Danger */
43
- --sx-red-500: #DC2626;
44
- --sx-red-400: #EF4444;
45
- --sx-red-600: #B91C1C;
43
+ --sx-red-500: #dc2626;
44
+ --sx-red-400: #ef4444;
45
+ --sx-red-600: #b91c1c;
46
46
 
47
47
  /* Accents — Phosphor (operational green) */
48
- --sx-green-500: #4ADE80;
49
- --sx-green-400: #6EE7A0;
50
- --sx-green-600: #22C55E;
48
+ --sx-green-500: #4ade80;
49
+ --sx-green-400: #6ee7a0;
50
+ --sx-green-600: #22c55e;
51
51
 
52
52
  /* Accents — Teal (encrypted channel) */
53
- --sx-teal-500: #3D8B8B;
54
- --sx-teal-400: #5AABAB;
55
- --sx-teal-600: #2D6B6B;
53
+ --sx-teal-500: #3d8b8b;
54
+ --sx-teal-400: #5aabab;
55
+ --sx-teal-600: #2d6b6b;
56
56
 
57
57
  /* ========================================
58
58
  SEMANTIC COLORS
@@ -112,7 +112,7 @@
112
112
  --sx-shadow-lg: 0 8px 32px rgba(0, 0, 0, 0.5);
113
113
  --sx-shadow-xl: 0 16px 48px rgba(0, 0, 0, 0.6);
114
114
  --sx-shadow-glow-primary: 0 0 20px rgba(255, 107, 53, 0.18);
115
- --sx-shadow-glow-primary-strong: 0 0 30px rgba(255, 107, 53, 0.30);
115
+ --sx-shadow-glow-primary-strong: 0 0 30px rgba(255, 107, 53, 0.3);
116
116
  --sx-shadow-glow-brass: 0 0 20px rgba(200, 168, 78, 0.15);
117
117
  --sx-shadow-glow-red: 0 0 20px rgba(220, 38, 38, 0.15);
118
118
  --sx-shadow-glow-green: 0 0 20px rgba(74, 222, 128, 0.15);
@@ -164,9 +164,9 @@
164
164
  TYPOGRAPHY
165
165
  ======================================== */
166
166
 
167
- --sx-font-body: 'TT Fors', system-ui, -apple-system, sans-serif;
168
- --sx-font-display: 'TT Quaris', Georgia, 'Times New Roman', serif;
169
- --sx-font-mono: 'JetBrains Mono', 'Consolas', 'Courier New', monospace;
167
+ --sx-font-body: "TT Fors", system-ui, -apple-system, sans-serif;
168
+ --sx-font-display: "TT Quaris", Georgia, "Times New Roman", serif;
169
+ --sx-font-mono: "JetBrains Mono", "Consolas", "Courier New", monospace;
170
170
 
171
171
  --sx-text-xs: 0.75rem;
172
172
  --sx-text-sm: 0.875rem;
@@ -251,7 +251,7 @@
251
251
  ======================================== */
252
252
 
253
253
  --sx-z-base: 1;
254
- --sx-z-dropdown: 1000;
254
+ --sx-z-dropdown: 1055;
255
255
  --sx-z-sticky: 1020;
256
256
  --sx-z-fixed: 1030;
257
257
  --sx-z-modal-backdrop: 1040;
@@ -316,9 +316,11 @@
316
316
 
317
317
  body {
318
318
  background-color: var(--sx-color-base);
319
- background-image:
320
- radial-gradient(ellipse at 15% 50%, rgba(255, 107, 53, 0.02) 0%, transparent 50%),
321
- radial-gradient(ellipse at 85% 20%, rgba(200, 168, 78, 0.015) 0%, transparent 50%),
319
+ background-image: radial-gradient(
320
+ ellipse at 15% 50%,
321
+ rgba(255, 107, 53, 0.02) 0%,
322
+ transparent 50%
323
+ ), radial-gradient(ellipse at 85% 20%, rgba(200, 168, 78, 0.015) 0%, transparent 50%),
322
324
  radial-gradient(ellipse at 50% 85%, rgba(61, 139, 139, 0.015) 0%, transparent 50%);
323
325
  background-size: 200% 200%;
324
326
  animation: sx-ambient-drift 25s ease-in-out infinite;
@@ -359,23 +361,47 @@ body {
359
361
  }
360
362
 
361
363
  @keyframes sx-ambient-drift {
362
- 0% { background-position: 0% 50%; }
363
- 50% { background-position: 100% 50%; }
364
- 100% { background-position: 0% 50%; }
364
+ 0% {
365
+ background-position: 0% 50%;
366
+ }
367
+ 50% {
368
+ background-position: 100% 50%;
369
+ }
370
+ 100% {
371
+ background-position: 0% 50%;
372
+ }
365
373
  }
366
374
 
367
375
  @keyframes sx-focus-breathe {
368
- 0%, 100% { box-shadow: 0 0 0 3px rgba(255, 107, 53, 0.12); }
369
- 50% { box-shadow: 0 0 0 3px rgba(255, 107, 53, 0.28); }
376
+ 0%,
377
+ 100% {
378
+ box-shadow: 0 0 0 3px rgba(255, 107, 53, 0.12);
379
+ }
380
+ 50% {
381
+ box-shadow: 0 0 0 3px rgba(255, 107, 53, 0.28);
382
+ }
370
383
  }
371
384
 
372
385
  @keyframes sx-error-shake {
373
- 0%, 100% { transform: translateX(0); }
374
- 15% { transform: translateX(-4px); }
375
- 30% { transform: translateX(4px); }
376
- 45% { transform: translateX(-3px); }
377
- 60% { transform: translateX(2px); }
378
- 75% { transform: translateX(-1px); }
386
+ 0%,
387
+ 100% {
388
+ transform: translateX(0);
389
+ }
390
+ 15% {
391
+ transform: translateX(-4px);
392
+ }
393
+ 30% {
394
+ transform: translateX(4px);
395
+ }
396
+ 45% {
397
+ transform: translateX(-3px);
398
+ }
399
+ 60% {
400
+ transform: translateX(2px);
401
+ }
402
+ 75% {
403
+ transform: translateX(-1px);
404
+ }
379
405
  }
380
406
 
381
407
  @keyframes sx-check-in {
@@ -390,9 +416,15 @@ body {
390
416
  }
391
417
 
392
418
  @keyframes sx-badge-pop {
393
- 0% { transform: scale(1); }
394
- 40% { transform: scale(1.15); }
395
- 100% { transform: scale(1); }
419
+ 0% {
420
+ transform: scale(1);
421
+ }
422
+ 40% {
423
+ transform: scale(1.15);
424
+ }
425
+ 100% {
426
+ transform: scale(1);
427
+ }
396
428
  }
397
429
 
398
430
  @keyframes sx-alert-in {
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Accessibility utilities for WCAG 2.1 AA compliance.
3
+ * Focus management, screen reader announcements, media query helpers.
4
+ */
5
+ /** Check whether an element can receive focus. */
6
+ export declare function isElementFocusable(el: HTMLElement): boolean;
7
+ /** Get all focusable elements within a container, in DOM order. */
8
+ export declare function getFocusableElements(container: HTMLElement): HTMLElement[];
9
+ /**
10
+ * Announce a message to screen readers via an ARIA live region.
11
+ * Creates the region lazily and reuses it across calls.
12
+ */
13
+ export declare function announce(message: string, priority?: 'polite' | 'assertive'): void;
14
+ /** Check if the user prefers reduced motion. */
15
+ export declare function prefersReducedMotion(): boolean;
16
+ //# sourceMappingURL=accessibility.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accessibility.d.ts","sourceRoot":"","sources":["../../src/utils/accessibility.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAcH,kDAAkD;AAClD,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,WAAW,GAAG,OAAO,CAe3D;AAED,mEAAmE;AACnE,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,WAAW,GAAG,WAAW,EAAE,CAI1E;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,GAAE,QAAQ,GAAG,WAAsB,GAAG,IAAI,CAgC3F;AAED,gDAAgD;AAChD,wBAAgB,oBAAoB,IAAI,OAAO,CAG9C"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Accessibility utilities for WCAG 2.1 AA compliance.
3
+ * Focus management, screen reader announcements, media query helpers.
4
+ */
5
+ const FOCUSABLE_SELECTORS = [
6
+ 'a[href]:not([tabindex="-1"])',
7
+ 'button:not([disabled]):not([tabindex="-1"])',
8
+ 'input:not([disabled]):not([tabindex="-1"]):not([type="hidden"])',
9
+ 'select:not([disabled]):not([tabindex="-1"])',
10
+ 'textarea:not([disabled]):not([tabindex="-1"])',
11
+ '[tabindex]:not([tabindex="-1"])',
12
+ '[contenteditable]:not([tabindex="-1"])'
13
+ ];
14
+ const FOCUSABLE_SELECTOR = FOCUSABLE_SELECTORS.join(', ');
15
+ /** Check whether an element can receive focus. */
16
+ export function isElementFocusable(el) {
17
+ if (el.hasAttribute('disabled') || el.disabled)
18
+ return false;
19
+ if (el.getAttribute('tabindex') === '-1')
20
+ return false;
21
+ if (el.hasAttribute('hidden'))
22
+ return false;
23
+ const style = getComputedStyle(el);
24
+ if (style.display === 'none' || style.visibility === 'hidden')
25
+ return false;
26
+ const tag = el.tagName.toLowerCase();
27
+ if (['input', 'select', 'textarea', 'button'].includes(tag)) {
28
+ return !el.disabled;
29
+ }
30
+ if (tag === 'a')
31
+ return el.hasAttribute('href');
32
+ if (el.hasAttribute('tabindex') || el.hasAttribute('contenteditable'))
33
+ return true;
34
+ return false;
35
+ }
36
+ /** Get all focusable elements within a container, in DOM order. */
37
+ export function getFocusableElements(container) {
38
+ return Array.from(container.querySelectorAll(FOCUSABLE_SELECTOR)).filter(isElementFocusable);
39
+ }
40
+ /**
41
+ * Announce a message to screen readers via an ARIA live region.
42
+ * Creates the region lazily and reuses it across calls.
43
+ */
44
+ export function announce(message, priority = 'polite') {
45
+ if (typeof document === 'undefined')
46
+ return;
47
+ const id = `sx-announce-${priority}`;
48
+ let region = document.getElementById(id);
49
+ if (!region) {
50
+ region = document.createElement('div');
51
+ region.id = id;
52
+ region.setAttribute('role', 'log');
53
+ region.setAttribute('aria-live', priority);
54
+ region.setAttribute('aria-relevant', 'additions');
55
+ region.setAttribute('aria-atomic', 'true');
56
+ Object.assign(region.style, {
57
+ position: 'absolute',
58
+ width: '1px',
59
+ height: '1px',
60
+ padding: '0',
61
+ margin: '-1px',
62
+ overflow: 'hidden',
63
+ clip: 'rect(0, 0, 0, 0)',
64
+ whiteSpace: 'nowrap',
65
+ border: '0'
66
+ });
67
+ document.body.appendChild(region);
68
+ }
69
+ // Clear then re-set to trigger announcement
70
+ region.textContent = '';
71
+ requestAnimationFrame(() => {
72
+ region.textContent = message;
73
+ });
74
+ }
75
+ /** Check if the user prefers reduced motion. */
76
+ export function prefersReducedMotion() {
77
+ if (typeof window === 'undefined')
78
+ return false;
79
+ return window.matchMedia('(prefers-reduced-motion: reduce)').matches;
80
+ }
@@ -1,4 +1,5 @@
1
1
  export { cn } from './cn.js';
2
- export { createFocusTrap, generateId, Keys } from './keyboard.js';
2
+ export { createFocusTrap, generateId, Keys, isKey, isActivationKey, isNavigationKey } from './keyboard.js';
3
3
  export type { FocusTrap, Key } from './keyboard.js';
4
+ export { isElementFocusable, getFocusableElements, announce, prefersReducedMotion } from './accessibility.js';
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAC7B,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAClE,YAAY,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAC7B,OAAO,EACN,eAAe,EACf,UAAU,EACV,IAAI,EACJ,KAAK,EACL,eAAe,EACf,eAAe,EACf,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EACN,kBAAkB,EAClB,oBAAoB,EACpB,QAAQ,EACR,oBAAoB,EACpB,MAAM,oBAAoB,CAAC"}
@@ -1,2 +1,3 @@
1
1
  export { cn } from './cn.js';
2
- export { createFocusTrap, generateId, Keys } from './keyboard.js';
2
+ export { createFocusTrap, generateId, Keys, isKey, isActivationKey, isNavigationKey } from './keyboard.js';
3
+ export { isElementFocusable, getFocusableElements, announce, prefersReducedMotion } from './accessibility.js';
@@ -23,6 +23,12 @@ export interface FocusTrap {
23
23
  * Trap focus within a container (e.g. modal). Tab cycles inside; restores focus on deactivate.
24
24
  */
25
25
  export declare function createFocusTrap(container: HTMLElement): FocusTrap;
26
+ /** Check if the key matches one of the provided keys. */
27
+ export declare function isKey(event: KeyboardEvent, ...keys: Key[]): boolean;
28
+ /** Check if the key is an activation key (Enter or Space). */
29
+ export declare function isActivationKey(event: KeyboardEvent): boolean;
30
+ /** Check if the key is a navigation key (arrows, Home, End). */
31
+ export declare function isNavigationKey(event: KeyboardEvent): boolean;
26
32
  /** Unique ID for aria attributes (per-instance safe). */
27
33
  export declare function generateId(prefix?: string): string;
28
34
  //# sourceMappingURL=keyboard.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"keyboard.d.ts","sourceRoot":"","sources":["../../src/utils/keyboard.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,eAAO,MAAM,IAAI;;;;;;;;;;;CAWP,CAAC;AAEX,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,IAAI,CAAC,CAAC,MAAM,OAAO,IAAI,CAAC,CAAC;AAEnD,MAAM,WAAW,SAAS;IACzB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,UAAU,EAAE,MAAM,IAAI,CAAC;CACvB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,WAAW,GAAG,SAAS,CA4CjE;AAED,yDAAyD;AACzD,wBAAgB,UAAU,CAAC,MAAM,SAAW,GAAG,MAAM,CAEpD"}
1
+ {"version":3,"file":"keyboard.d.ts","sourceRoot":"","sources":["../../src/utils/keyboard.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,eAAO,MAAM,IAAI;;;;;;;;;;;CAWP,CAAC;AAEX,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,IAAI,CAAC,CAAC,MAAM,OAAO,IAAI,CAAC,CAAC;AAEnD,MAAM,WAAW,SAAS;IACzB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,UAAU,EAAE,MAAM,IAAI,CAAC;CACvB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,WAAW,GAAG,SAAS,CAoCjE;AAED,yDAAyD;AACzD,wBAAgB,KAAK,CAAC,KAAK,EAAE,aAAa,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,CAEnE;AAED,8DAA8D;AAC9D,wBAAgB,eAAe,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAE7D;AAED,gEAAgE;AAChE,wBAAgB,eAAe,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAU7D;AAED,yDAAyD;AACzD,wBAAgB,UAAU,CAAC,MAAM,SAAW,GAAG,MAAM,CAEpD"}
@@ -2,6 +2,7 @@
2
2
  * Keyboard and focus utilities for accessible components.
3
3
  * WCAG 2.1 / WAI-ARIA aligned; used by Modal and other dialogs.
4
4
  */
5
+ import { getFocusableElements } from './accessibility.js';
5
6
  export const Keys = {
6
7
  Enter: 'Enter',
7
8
  Space: ' ',
@@ -19,16 +20,9 @@ export const Keys = {
19
20
  */
20
21
  export function createFocusTrap(container) {
21
22
  let previouslyFocused = null;
22
- const focusableSelector = [
23
- 'button:not([disabled]):not([tabindex="-1"])',
24
- '[href]:not([tabindex="-1"])',
25
- 'input:not([disabled]):not([tabindex="-1"])',
26
- 'select:not([disabled]):not([tabindex="-1"])',
27
- 'textarea:not([disabled]):not([tabindex="-1"])',
28
- '[tabindex]:not([tabindex="-1"])'
29
- ].join(', ');
30
23
  function getFocusable() {
31
- return Array.from(container.querySelectorAll(focusableSelector));
24
+ // Use shared implementation that filters invisible/disabled elements
25
+ return getFocusableElements(container);
32
26
  }
33
27
  function onKeyDown(e) {
34
28
  if (e.key !== Keys.Tab)
@@ -61,6 +55,18 @@ export function createFocusTrap(container) {
61
55
  }
62
56
  };
63
57
  }
58
+ /** Check if the key matches one of the provided keys. */
59
+ export function isKey(event, ...keys) {
60
+ return keys.includes(event.key);
61
+ }
62
+ /** Check if the key is an activation key (Enter or Space). */
63
+ export function isActivationKey(event) {
64
+ return isKey(event, Keys.Enter, Keys.Space);
65
+ }
66
+ /** Check if the key is a navigation key (arrows, Home, End). */
67
+ export function isNavigationKey(event) {
68
+ return isKey(event, Keys.ArrowUp, Keys.ArrowDown, Keys.ArrowLeft, Keys.ArrowRight, Keys.Home, Keys.End);
69
+ }
64
70
  /** Unique ID for aria attributes (per-instance safe). */
65
71
  export function generateId(prefix = 'salmex') {
66
72
  return `${prefix}-${Math.random().toString(36).slice(2, 11)}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salmexio/ui",
3
- "version": "1.2.1",
3
+ "version": "1.3.1",
4
4
  "description": "Salmex I/O Design System — INFRARED component library for Salmex products",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -39,6 +39,26 @@
39
39
  "svelte": "./dist/navigation/index.js",
40
40
  "default": "./dist/navigation/index.js"
41
41
  },
42
+ "./forms": {
43
+ "types": "./dist/forms/index.d.ts",
44
+ "svelte": "./dist/forms/index.js",
45
+ "default": "./dist/forms/index.js"
46
+ },
47
+ "./layout": {
48
+ "types": "./dist/layout/index.d.ts",
49
+ "svelte": "./dist/layout/index.js",
50
+ "default": "./dist/layout/index.js"
51
+ },
52
+ "./feedback": {
53
+ "types": "./dist/feedback/index.d.ts",
54
+ "svelte": "./dist/feedback/index.js",
55
+ "default": "./dist/feedback/index.js"
56
+ },
57
+ "./dialogs": {
58
+ "types": "./dist/dialogs/index.d.ts",
59
+ "svelte": "./dist/dialogs/index.js",
60
+ "default": "./dist/dialogs/index.js"
61
+ },
42
62
  "./utils": {
43
63
  "types": "./dist/utils/index.d.ts",
44
64
  "default": "./dist/utils/index.js"