@sekiui/elements 0.0.56 → 0.0.59

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 (269) hide show
  1. package/dist/cdn/index.js +1053 -91
  2. package/dist/cdn/p-BJCq8m2o.js +138 -0
  3. package/dist/cdn/p-Cpa2leXN.js +111 -0
  4. package/dist/cdn/{p-bMBhrs0a.js → p-DxUZSKfL.js} +467 -9
  5. package/dist/cdn/seki-badge.d.ts +11 -0
  6. package/dist/cdn/seki-badge.js +109 -0
  7. package/dist/cdn/seki-button.js +1 -1
  8. package/dist/cdn/seki-card-action.js +1 -1
  9. package/dist/cdn/seki-card-content.js +1 -1
  10. package/dist/cdn/seki-card-description.js +1 -1
  11. package/dist/cdn/seki-card-footer.js +1 -1
  12. package/dist/cdn/seki-card-header.js +1 -1
  13. package/dist/cdn/seki-card-title.js +1 -1
  14. package/dist/cdn/seki-card.js +1 -1
  15. package/dist/cdn/seki-field-description.js +1 -1
  16. package/dist/cdn/seki-field-error.js +1 -1
  17. package/dist/cdn/seki-field-group.js +1 -1
  18. package/dist/cdn/seki-field-label.js +1 -1
  19. package/dist/cdn/seki-field-legend.js +1 -1
  20. package/dist/cdn/seki-field.js +1 -1
  21. package/dist/cdn/seki-fieldset.js +1 -1
  22. package/dist/cdn/seki-input.js +1 -1
  23. package/dist/cdn/seki-select-content.js +1 -1
  24. package/dist/cdn/seki-select-group.js +1 -1
  25. package/dist/cdn/seki-select-option.js +1 -1
  26. package/dist/cdn/seki-select-trigger.js +1 -1
  27. package/dist/cdn/seki-select.js +1 -1
  28. package/dist/cdn/seki-sidebar-content.d.ts +11 -0
  29. package/dist/cdn/seki-sidebar-content.js +38 -0
  30. package/dist/cdn/seki-sidebar-footer.d.ts +11 -0
  31. package/dist/cdn/seki-sidebar-footer.js +38 -0
  32. package/dist/cdn/seki-sidebar-group.d.ts +11 -0
  33. package/dist/cdn/seki-sidebar-group.js +131 -0
  34. package/dist/cdn/seki-sidebar-header.d.ts +11 -0
  35. package/dist/cdn/seki-sidebar-header.js +38 -0
  36. package/dist/cdn/seki-sidebar-menu-item.d.ts +11 -0
  37. package/dist/cdn/seki-sidebar-menu-item.js +200 -0
  38. package/dist/cdn/seki-sidebar-menu-sub.d.ts +11 -0
  39. package/dist/cdn/seki-sidebar-menu-sub.js +123 -0
  40. package/dist/cdn/seki-sidebar-menu.d.ts +11 -0
  41. package/dist/cdn/seki-sidebar-menu.js +38 -0
  42. package/dist/cdn/seki-sidebar-trigger.d.ts +11 -0
  43. package/dist/cdn/seki-sidebar-trigger.js +109 -0
  44. package/dist/cdn/seki-sidebar.d.ts +11 -0
  45. package/dist/cdn/seki-sidebar.js +380 -0
  46. package/dist/cdn/seki-skeleton.js +2 -2
  47. package/dist/cdn/seki-switch.js +1 -1
  48. package/dist/cdn/seki-tooltip.js +1 -1
  49. package/dist/cjs/{index-Dd6_-KaR.js → index-D4RM3EID.js} +466 -8
  50. package/dist/cjs/index.cjs.js +1115 -63
  51. package/dist/cjs/keyboard-Cjl5HYES.js +142 -0
  52. package/dist/cjs/loader.cjs.js +2 -2
  53. package/dist/cjs/seki-badge.cjs.entry.js +85 -0
  54. package/dist/cjs/seki-button.cjs.entry.js +1 -1
  55. package/dist/cjs/seki-card-action.cjs.entry.js +1 -1
  56. package/dist/cjs/seki-card-content.cjs.entry.js +1 -1
  57. package/dist/cjs/seki-card-description.cjs.entry.js +1 -1
  58. package/dist/cjs/seki-card-footer.cjs.entry.js +1 -1
  59. package/dist/cjs/seki-card-header.cjs.entry.js +1 -1
  60. package/dist/cjs/seki-card-title.cjs.entry.js +1 -1
  61. package/dist/cjs/seki-card.cjs.entry.js +1 -1
  62. package/dist/cjs/seki-field-description.cjs.entry.js +1 -1
  63. package/dist/cjs/seki-field-error.cjs.entry.js +1 -1
  64. package/dist/cjs/seki-field-group.cjs.entry.js +1 -1
  65. package/dist/cjs/seki-field-label.cjs.entry.js +1 -1
  66. package/dist/cjs/seki-field-legend.cjs.entry.js +1 -1
  67. package/dist/cjs/seki-field.cjs.entry.js +1 -1
  68. package/dist/cjs/seki-fieldset.cjs.entry.js +1 -1
  69. package/dist/cjs/seki-input.cjs.entry.js +1 -1
  70. package/dist/cjs/seki-select-content.cjs.entry.js +1 -1
  71. package/dist/cjs/seki-select-group.cjs.entry.js +1 -1
  72. package/dist/cjs/seki-select-option.cjs.entry.js +1 -1
  73. package/dist/cjs/seki-select-trigger.cjs.entry.js +1 -1
  74. package/dist/cjs/seki-select.cjs.entry.js +1 -1
  75. package/dist/cjs/seki-sidebar-content.cjs.entry.js +20 -0
  76. package/dist/cjs/seki-sidebar-footer.cjs.entry.js +20 -0
  77. package/dist/cjs/seki-sidebar-group.cjs.entry.js +105 -0
  78. package/dist/cjs/seki-sidebar-header.cjs.entry.js +20 -0
  79. package/dist/cjs/seki-sidebar-menu-item.cjs.entry.js +174 -0
  80. package/dist/cjs/seki-sidebar-menu-sub.cjs.entry.js +99 -0
  81. package/dist/cjs/seki-sidebar-menu.cjs.entry.js +20 -0
  82. package/dist/cjs/seki-sidebar-trigger.cjs.entry.js +86 -0
  83. package/dist/cjs/seki-sidebar.cjs.entry.js +342 -0
  84. package/dist/cjs/seki-skeleton.cjs.entry.js +2 -2
  85. package/dist/cjs/seki-switch.cjs.entry.js +81 -3
  86. package/dist/cjs/seki-tooltip.cjs.entry.js +1 -1
  87. package/dist/cjs/sekiui.cjs.js +3 -3
  88. package/dist/collection/collection-manifest.json +12 -2
  89. package/dist/collection/components/badge/seki-badge.css +140 -0
  90. package/dist/collection/components/badge/seki-badge.interface.js +1 -0
  91. package/dist/collection/components/badge/seki-badge.js +199 -0
  92. package/dist/collection/components/sidebar/seki-sidebar-content.css +82 -0
  93. package/dist/collection/components/sidebar/seki-sidebar-content.js +33 -0
  94. package/dist/collection/components/sidebar/seki-sidebar-footer.css +44 -0
  95. package/dist/collection/components/sidebar/seki-sidebar-footer.js +31 -0
  96. package/dist/collection/components/sidebar/seki-sidebar-group.css +158 -0
  97. package/dist/collection/components/sidebar/seki-sidebar-group.js +300 -0
  98. package/dist/collection/components/sidebar/seki-sidebar-header.css +44 -0
  99. package/dist/collection/components/sidebar/seki-sidebar-header.js +32 -0
  100. package/dist/collection/components/sidebar/seki-sidebar-menu-item.css +196 -0
  101. package/dist/collection/components/sidebar/seki-sidebar-menu-item.js +403 -0
  102. package/dist/collection/components/sidebar/seki-sidebar-menu-sub.css +357 -0
  103. package/dist/collection/components/sidebar/seki-sidebar-menu-sub.js +256 -0
  104. package/dist/collection/components/sidebar/seki-sidebar-menu.css +25 -0
  105. package/dist/collection/components/sidebar/seki-sidebar-menu.js +32 -0
  106. package/dist/collection/components/sidebar/seki-sidebar-trigger.css +68 -0
  107. package/dist/collection/components/sidebar/seki-sidebar-trigger.js +175 -0
  108. package/dist/collection/components/sidebar/seki-sidebar.css +352 -0
  109. package/dist/collection/components/sidebar/seki-sidebar.js +812 -0
  110. package/dist/collection/components/sidebar/types.js +18 -0
  111. package/dist/collection/components/skeleton/seki-skeleton.js +1 -1
  112. package/dist/collection/index.js +7 -0
  113. package/dist/collection/services/focus.js +192 -0
  114. package/dist/collection/services/index.js +7 -0
  115. package/dist/collection/services/keyboard.js +136 -0
  116. package/dist/collection/services/media-query.js +254 -0
  117. package/dist/collection/types.js +41 -0
  118. package/dist/collection/utils/a11y.js +291 -0
  119. package/dist/collection/utils/common.js +286 -0
  120. package/dist/components/index.js +1053 -91
  121. package/dist/components/p-BJCq8m2o.js +138 -0
  122. package/dist/components/{p-QhPshhKB.js → p-BU1kuAZS.js} +467 -9
  123. package/dist/components/p-wQy1sEm6.js +111 -0
  124. package/dist/components/seki-badge.d.ts +11 -0
  125. package/dist/components/seki-badge.js +109 -0
  126. package/dist/components/seki-button.js +1 -1
  127. package/dist/components/seki-card-action.js +1 -1
  128. package/dist/components/seki-card-content.js +1 -1
  129. package/dist/components/seki-card-description.js +1 -1
  130. package/dist/components/seki-card-footer.js +1 -1
  131. package/dist/components/seki-card-header.js +1 -1
  132. package/dist/components/seki-card-title.js +1 -1
  133. package/dist/components/seki-card.js +1 -1
  134. package/dist/components/seki-field-description.js +1 -1
  135. package/dist/components/seki-field-error.js +1 -1
  136. package/dist/components/seki-field-group.js +1 -1
  137. package/dist/components/seki-field-label.js +1 -1
  138. package/dist/components/seki-field-legend.js +1 -1
  139. package/dist/components/seki-field.js +1 -1
  140. package/dist/components/seki-fieldset.js +1 -1
  141. package/dist/components/seki-input.js +1 -1
  142. package/dist/components/seki-select-content.js +1 -1
  143. package/dist/components/seki-select-group.js +1 -1
  144. package/dist/components/seki-select-option.js +1 -1
  145. package/dist/components/seki-select-trigger.js +1 -1
  146. package/dist/components/seki-select.js +1 -1
  147. package/dist/components/seki-sidebar-content.d.ts +11 -0
  148. package/dist/components/seki-sidebar-content.js +38 -0
  149. package/dist/components/seki-sidebar-footer.d.ts +11 -0
  150. package/dist/components/seki-sidebar-footer.js +38 -0
  151. package/dist/components/seki-sidebar-group.d.ts +11 -0
  152. package/dist/components/seki-sidebar-group.js +131 -0
  153. package/dist/components/seki-sidebar-header.d.ts +11 -0
  154. package/dist/components/seki-sidebar-header.js +38 -0
  155. package/dist/components/seki-sidebar-menu-item.d.ts +11 -0
  156. package/dist/components/seki-sidebar-menu-item.js +200 -0
  157. package/dist/components/seki-sidebar-menu-sub.d.ts +11 -0
  158. package/dist/components/seki-sidebar-menu-sub.js +123 -0
  159. package/dist/components/seki-sidebar-menu.d.ts +11 -0
  160. package/dist/components/seki-sidebar-menu.js +38 -0
  161. package/dist/components/seki-sidebar-trigger.d.ts +11 -0
  162. package/dist/components/seki-sidebar-trigger.js +109 -0
  163. package/dist/components/seki-sidebar.d.ts +11 -0
  164. package/dist/components/seki-sidebar.js +380 -0
  165. package/dist/components/seki-skeleton.js +2 -2
  166. package/dist/components/seki-switch.js +1 -1
  167. package/dist/components/seki-tooltip.js +1 -1
  168. package/dist/esm/{index-CuXbV_yz.js → index-DI_YjzRi.js} +466 -8
  169. package/dist/esm/index.js +1053 -63
  170. package/dist/esm/keyboard-BJCq8m2o.js +138 -0
  171. package/dist/esm/loader.js +3 -3
  172. package/dist/esm/seki-badge.entry.js +83 -0
  173. package/dist/esm/seki-button.entry.js +1 -1
  174. package/dist/esm/seki-card-action.entry.js +1 -1
  175. package/dist/esm/seki-card-content.entry.js +1 -1
  176. package/dist/esm/seki-card-description.entry.js +1 -1
  177. package/dist/esm/seki-card-footer.entry.js +1 -1
  178. package/dist/esm/seki-card-header.entry.js +1 -1
  179. package/dist/esm/seki-card-title.entry.js +1 -1
  180. package/dist/esm/seki-card.entry.js +1 -1
  181. package/dist/esm/seki-field-description.entry.js +1 -1
  182. package/dist/esm/seki-field-error.entry.js +1 -1
  183. package/dist/esm/seki-field-group.entry.js +1 -1
  184. package/dist/esm/seki-field-label.entry.js +1 -1
  185. package/dist/esm/seki-field-legend.entry.js +1 -1
  186. package/dist/esm/seki-field.entry.js +1 -1
  187. package/dist/esm/seki-fieldset.entry.js +1 -1
  188. package/dist/esm/seki-input.entry.js +1 -1
  189. package/dist/esm/seki-select-content.entry.js +1 -1
  190. package/dist/esm/seki-select-group.entry.js +1 -1
  191. package/dist/esm/seki-select-option.entry.js +1 -1
  192. package/dist/esm/seki-select-trigger.entry.js +1 -1
  193. package/dist/esm/seki-select.entry.js +1 -1
  194. package/dist/esm/seki-sidebar-content.entry.js +18 -0
  195. package/dist/esm/seki-sidebar-footer.entry.js +18 -0
  196. package/dist/esm/seki-sidebar-group.entry.js +103 -0
  197. package/dist/esm/seki-sidebar-header.entry.js +18 -0
  198. package/dist/esm/seki-sidebar-menu-item.entry.js +172 -0
  199. package/dist/esm/seki-sidebar-menu-sub.entry.js +97 -0
  200. package/dist/esm/seki-sidebar-menu.entry.js +18 -0
  201. package/dist/esm/seki-sidebar-trigger.entry.js +84 -0
  202. package/dist/esm/seki-sidebar.entry.js +340 -0
  203. package/dist/esm/seki-skeleton.entry.js +2 -2
  204. package/dist/esm/seki-switch.entry.js +84 -2
  205. package/dist/esm/seki-tooltip.entry.js +1 -1
  206. package/dist/esm/sekiui.js +4 -4
  207. package/dist/sekiui/index.esm.js +1 -1
  208. package/dist/sekiui/p-0af1b81a.entry.js +1 -0
  209. package/dist/sekiui/p-0ca6b9f0.entry.js +1 -0
  210. package/dist/sekiui/{p-cf11115c.entry.js → p-1480b41a.entry.js} +1 -1
  211. package/dist/sekiui/{p-dd1e3e87.entry.js → p-161be4d4.entry.js} +1 -1
  212. package/dist/sekiui/{p-e71ad432.entry.js → p-1685e673.entry.js} +1 -1
  213. package/dist/sekiui/p-26b629bc.entry.js +1 -0
  214. package/dist/sekiui/{p-0544d787.entry.js → p-27deb555.entry.js} +1 -1
  215. package/dist/sekiui/p-37fa684c.entry.js +1 -0
  216. package/dist/sekiui/{p-1607dc4d.entry.js → p-402a5db6.entry.js} +1 -1
  217. package/dist/sekiui/{p-6bde807e.entry.js → p-40ba3ad6.entry.js} +1 -1
  218. package/dist/sekiui/{p-d4c92041.entry.js → p-4867d02d.entry.js} +1 -1
  219. package/dist/sekiui/p-4b29dbda.entry.js +1 -0
  220. package/dist/sekiui/{p-b10d81a6.entry.js → p-587fd313.entry.js} +1 -1
  221. package/dist/sekiui/{p-3e088b5a.entry.js → p-58ab95eb.entry.js} +1 -1
  222. package/dist/sekiui/p-60ff3543.entry.js +1 -0
  223. package/dist/sekiui/{p-9af5286b.entry.js → p-68b1fa1a.entry.js} +1 -1
  224. package/dist/sekiui/{p-43f7c542.entry.js → p-6a922121.entry.js} +1 -1
  225. package/dist/sekiui/{p-4d57c6ea.entry.js → p-6f5bf5af.entry.js} +1 -1
  226. package/dist/sekiui/{p-88f91658.entry.js → p-84d47cab.entry.js} +1 -1
  227. package/dist/sekiui/p-9dcd07b2.entry.js +1 -0
  228. package/dist/sekiui/p-BJCq8m2o.js +1 -0
  229. package/dist/sekiui/p-DI_YjzRi.js +2 -0
  230. package/dist/sekiui/p-ab9d1ba5.entry.js +1 -0
  231. package/dist/sekiui/p-b525d85a.entry.js +1 -0
  232. package/dist/sekiui/{p-ed440425.entry.js → p-b64e7007.entry.js} +1 -1
  233. package/dist/sekiui/{p-6e238adf.entry.js → p-b7f2b568.entry.js} +1 -1
  234. package/dist/sekiui/{p-009183ab.entry.js → p-c642ab55.entry.js} +1 -1
  235. package/dist/sekiui/{p-eefbc037.entry.js → p-c74bd925.entry.js} +1 -1
  236. package/dist/sekiui/p-c83d94c4.entry.js +1 -0
  237. package/dist/sekiui/p-ce1bbe04.entry.js +1 -0
  238. package/dist/sekiui/{p-b22df79e.entry.js → p-cf552ff9.entry.js} +1 -1
  239. package/dist/sekiui/{p-81709fc2.entry.js → p-d194caf1.entry.js} +1 -1
  240. package/dist/sekiui/{p-b479935d.entry.js → p-dfa2f8cd.entry.js} +1 -1
  241. package/dist/sekiui/{p-eedf44b5.entry.js → p-e6d5f56e.entry.js} +1 -1
  242. package/dist/sekiui/{p-97e6e5ce.entry.js → p-f1ffc3fa.entry.js} +1 -1
  243. package/dist/sekiui/{p-35f8f9c4.entry.js → p-f863f36b.entry.js} +1 -1
  244. package/dist/sekiui/sekiui.esm.js +1 -1
  245. package/dist/types/components/badge/seki-badge.d.ts +43 -0
  246. package/dist/types/components/badge/seki-badge.interface.d.ts +88 -0
  247. package/dist/types/components/sidebar/seki-sidebar-content.d.ts +18 -0
  248. package/dist/types/components/sidebar/seki-sidebar-footer.d.ts +16 -0
  249. package/dist/types/components/sidebar/seki-sidebar-group.d.ts +81 -0
  250. package/dist/types/components/sidebar/seki-sidebar-header.d.ts +17 -0
  251. package/dist/types/components/sidebar/seki-sidebar-menu-item.d.ts +104 -0
  252. package/dist/types/components/sidebar/seki-sidebar-menu-sub.d.ts +81 -0
  253. package/dist/types/components/sidebar/seki-sidebar-menu.d.ts +17 -0
  254. package/dist/types/components/sidebar/seki-sidebar-trigger.d.ts +53 -0
  255. package/dist/types/components/sidebar/seki-sidebar.d.ts +185 -0
  256. package/dist/types/components/sidebar/types.d.ts +245 -0
  257. package/dist/types/components.d.ts +599 -0
  258. package/dist/types/index.d.ts +4 -0
  259. package/dist/types/services/focus.d.ts +74 -0
  260. package/dist/types/services/index.d.ts +7 -0
  261. package/dist/types/services/keyboard.d.ts +74 -0
  262. package/dist/types/services/media-query.d.ts +121 -0
  263. package/dist/types/stencil-public-runtime.d.ts +19 -9
  264. package/dist/types/types.d.ts +105 -0
  265. package/dist/types/utils/a11y.d.ts +130 -0
  266. package/dist/types/utils/common.d.ts +142 -0
  267. package/package.json +10 -2
  268. package/dist/sekiui/p-9fe07f6e.entry.js +0 -1
  269. package/dist/sekiui/p-CuXbV_yz.js +0 -2
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Sidebar Component Type Definitions
3
+ * @module sidebar/types
4
+ *
5
+ * Complete TypeScript interfaces and types for the sidebar component system.
6
+ * These types are used throughout the component implementation, tests, and exported
7
+ * to the npm package for consumer IDE autocomplete support.
8
+ */
9
+ /**
10
+ * Default sidebar state values
11
+ */
12
+ export const DEFAULT_SIDEBAR_STATE = {
13
+ isOpen: true,
14
+ collapseMode: 'offcanvas',
15
+ variant: 'sidebar',
16
+ isMobile: typeof window !== 'undefined' ? window.innerWidth < 768 : false,
17
+ persistenceKey: 'seki-sidebar-state',
18
+ };
@@ -15,7 +15,7 @@ export class SekiSkeleton {
15
15
  }
16
16
  }
17
17
  render() {
18
- return (h("div", { key: 'ccee706a38b0783b89dbdda8f1844caef95ea62b', class: "skeleton", part: "skeleton", role: "status", "aria-busy": "true", "aria-label": this.ariaLabel || undefined }));
18
+ return (h("div", { key: '4f60930ca73fef0cf4b2c940cbb108e05380caeb', class: "skeleton", part: "skeleton", role: "status", "aria-busy": "true", "aria-label": this.ariaLabel || undefined }));
19
19
  }
20
20
  static get is() { return "seki-skeleton"; }
21
21
  static get encapsulation() { return "shadow"; }
@@ -1 +1,8 @@
1
1
  export { SekiSwitch } from './components/switch/seki-switch';
2
+ // Services
3
+ export * from './services';
4
+ // Utilities
5
+ export * from './utils/common';
6
+ export * from './utils/a11y';
7
+ // Types
8
+ export * from './types';
@@ -0,0 +1,192 @@
1
+ /**
2
+ * Focus Management Service
3
+ * Provides utilities for focus management, focus traps, and focus restoration
4
+ */
5
+ /**
6
+ * FocusService - Centralized focus management
7
+ * Provides:
8
+ * - Focus trap implementation
9
+ * - Focus restoration
10
+ * - Focusable element detection
11
+ * - Focus utilities for accessibility
12
+ */
13
+ export class FocusService {
14
+ constructor() {
15
+ this.focusTrapStack = new Map();
16
+ this.previouslyFocusedElement = null;
17
+ }
18
+ /**
19
+ * Get all focusable elements within a container
20
+ * Includes buttons, links, inputs, textareas, selects, and elements with tabindex
21
+ */
22
+ getFocusableElements(container = document) {
23
+ const focusableSelectors = [
24
+ 'button:not([disabled])',
25
+ 'a[href]',
26
+ 'input:not([disabled])',
27
+ 'textarea:not([disabled])',
28
+ 'select:not([disabled])',
29
+ '[tabindex]:not([tabindex="-1"])',
30
+ ].join(',');
31
+ const elements = Array.from(container.querySelectorAll(focusableSelectors));
32
+ return elements.filter((el) => {
33
+ // Exclude hidden elements
34
+ const style = window.getComputedStyle(el);
35
+ return style.display !== 'none' && style.visibility !== 'hidden';
36
+ });
37
+ }
38
+ /**
39
+ * Check if an element is focusable
40
+ */
41
+ isFocusable(element) {
42
+ const focusableSelectors = [
43
+ 'button',
44
+ 'a[href]',
45
+ 'input',
46
+ 'textarea',
47
+ 'select',
48
+ '[tabindex]',
49
+ ];
50
+ return focusableSelectors.some((selector) => element.matches(selector)) && !element.hasAttribute('disabled');
51
+ }
52
+ /**
53
+ * Set focus to an element with optional callbacks
54
+ */
55
+ setFocus(element, options) {
56
+ var _a;
57
+ if (!element)
58
+ return false;
59
+ try {
60
+ element.focus({ preventScroll: (_a = options === null || options === void 0 ? void 0 : options.preventScroll) !== null && _a !== void 0 ? _a : false });
61
+ return document.activeElement === element;
62
+ }
63
+ catch (error) {
64
+ console.warn('Failed to set focus:', error);
65
+ return false;
66
+ }
67
+ }
68
+ /**
69
+ * Trap focus within a container (keyboard navigation stays within bounds)
70
+ * @param container - The element to trap focus within
71
+ * @param options - Focus trap options
72
+ */
73
+ createFocusTrap(container, options = {}) {
74
+ if (typeof window === 'undefined')
75
+ return;
76
+ const { initialFocus, restoreFocus = true } = options;
77
+ // Store the previously focused element for restoration
78
+ if (restoreFocus) {
79
+ this.previouslyFocusedElement = document.activeElement;
80
+ }
81
+ // Set initial focus
82
+ if (initialFocus) {
83
+ this.setFocus(initialFocus);
84
+ }
85
+ else {
86
+ const focusableElements = this.getFocusableElements(container);
87
+ if (focusableElements.length > 0) {
88
+ this.setFocus(focusableElements[0]);
89
+ }
90
+ }
91
+ // Handle Tab key for focus trap
92
+ const handleKeyDown = (event) => {
93
+ if (event.key !== 'Tab')
94
+ return;
95
+ const focusableElements = this.getFocusableElements(container);
96
+ if (focusableElements.length === 0)
97
+ return;
98
+ const currentFocusIndex = focusableElements.indexOf(document.activeElement);
99
+ if (event.shiftKey) {
100
+ // Shift+Tab - move backwards
101
+ const previousIndex = currentFocusIndex - 1;
102
+ const targetElement = previousIndex >= 0 ? focusableElements[previousIndex] : focusableElements[focusableElements.length - 1];
103
+ event.preventDefault();
104
+ this.setFocus(targetElement);
105
+ }
106
+ else {
107
+ // Tab - move forwards
108
+ const nextIndex = currentFocusIndex + 1;
109
+ const targetElement = nextIndex < focusableElements.length ? focusableElements[nextIndex] : focusableElements[0];
110
+ event.preventDefault();
111
+ this.setFocus(targetElement);
112
+ }
113
+ };
114
+ container.addEventListener('keydown', handleKeyDown);
115
+ this.focusTrapStack.set(container, this.previouslyFocusedElement);
116
+ // Store the handler for potential later cleanup
117
+ // Note: Manual cleanup should be done via releaseFocusTrap()
118
+ }
119
+ /**
120
+ * Release focus trap from a container
121
+ */
122
+ releaseFocusTrap(container, restoreFocus = true) {
123
+ const previousElement = this.focusTrapStack.get(container);
124
+ this.focusTrapStack.delete(container);
125
+ if (restoreFocus && previousElement) {
126
+ this.setFocus(previousElement);
127
+ }
128
+ }
129
+ /**
130
+ * Get the first focusable element in a container
131
+ */
132
+ getFirstFocusable(container = document) {
133
+ const focusable = this.getFocusableElements(container);
134
+ return focusable.length > 0 ? focusable[0] : null;
135
+ }
136
+ /**
137
+ * Get the last focusable element in a container
138
+ */
139
+ getLastFocusable(container = document) {
140
+ const focusable = this.getFocusableElements(container);
141
+ return focusable.length > 0 ? focusable[focusable.length - 1] : null;
142
+ }
143
+ /**
144
+ * Move focus to the next focusable element
145
+ */
146
+ focusNext(container = document) {
147
+ const focusable = this.getFocusableElements(container);
148
+ const currentIndex = focusable.indexOf(document.activeElement);
149
+ const nextIndex = (currentIndex + 1) % focusable.length;
150
+ if (focusable.length > 0) {
151
+ this.setFocus(focusable[nextIndex]);
152
+ return true;
153
+ }
154
+ return false;
155
+ }
156
+ /**
157
+ * Move focus to the previous focusable element
158
+ */
159
+ focusPrevious(container = document) {
160
+ const focusable = this.getFocusableElements(container);
161
+ const currentIndex = focusable.indexOf(document.activeElement);
162
+ const previousIndex = (currentIndex - 1 + focusable.length) % focusable.length;
163
+ if (focusable.length > 0) {
164
+ this.setFocus(focusable[previousIndex]);
165
+ return true;
166
+ }
167
+ return false;
168
+ }
169
+ /**
170
+ * Clear all focus traps
171
+ */
172
+ clearFocusTraps() {
173
+ this.focusTrapStack.clear();
174
+ }
175
+ }
176
+ // Singleton instance
177
+ let focusServiceInstance = null;
178
+ /**
179
+ * Get the singleton FocusService instance
180
+ */
181
+ export function getFocusService() {
182
+ if (!focusServiceInstance) {
183
+ focusServiceInstance = new FocusService();
184
+ }
185
+ return focusServiceInstance;
186
+ }
187
+ /**
188
+ * Create a new isolated FocusService instance
189
+ */
190
+ export function createFocusService() {
191
+ return new FocusService();
192
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Services Index
3
+ * Central export point for all application services
4
+ */
5
+ export * from './keyboard';
6
+ export * from './focus';
7
+ export * from './media-query';
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Keyboard Service
3
+ * Provides normalized keyboard event handling and shortcut management
4
+ * Handles cross-platform shortcuts (Ctrl/Cmd combinations)
5
+ */
6
+ /**
7
+ * KeyboardService - Centralized keyboard event management
8
+ * Provides:
9
+ * - Normalized keyboard shortcuts (handles Ctrl vs Cmd on Mac)
10
+ * - Cross-platform shortcut registration
11
+ * - Event cleanup and teardown
12
+ */
13
+ export class KeyboardService {
14
+ constructor() {
15
+ this.shortcuts = new Map();
16
+ this.listeners = new Map();
17
+ }
18
+ /**
19
+ * Register a keyboard shortcut
20
+ * @param id - Unique identifier for this shortcut
21
+ * @param options - Shortcut configuration
22
+ */
23
+ registerShortcut(id, options) {
24
+ if (typeof window === 'undefined')
25
+ return;
26
+ const { key, ctrl = false, shift = false, alt = false, meta = false, handler, preventDefault = true } = options;
27
+ const handleKeyDown = (event) => {
28
+ const keyboardEvent = event;
29
+ const keyMatches = keyboardEvent.key.toLowerCase() === key.toLowerCase();
30
+ const ctrlMatches = ctrl ? keyboardEvent.ctrlKey : true;
31
+ const shiftMatches = shift ? keyboardEvent.shiftKey : !keyboardEvent.shiftKey;
32
+ const altMatches = alt ? keyboardEvent.altKey : !keyboardEvent.altKey;
33
+ // metaKey matches both metaKey (Mac Cmd) and ctrlKey (Windows/Linux) for cross-platform support
34
+ const metaMatches = meta ? keyboardEvent.metaKey || keyboardEvent.ctrlKey : !keyboardEvent.metaKey && !keyboardEvent.ctrlKey;
35
+ if (keyMatches && ctrlMatches && shiftMatches && altMatches && metaMatches) {
36
+ if (preventDefault) {
37
+ keyboardEvent.preventDefault();
38
+ }
39
+ handler(keyboardEvent);
40
+ }
41
+ };
42
+ this.shortcuts.set(id, {
43
+ key,
44
+ ctrlKey: ctrl,
45
+ shiftKey: shift,
46
+ altKey: alt,
47
+ metaKey: meta,
48
+ handler: handleKeyDown,
49
+ });
50
+ this.listeners.set(id, handleKeyDown);
51
+ window.addEventListener('keydown', handleKeyDown);
52
+ }
53
+ /**
54
+ * Register a shortcut that works with Ctrl on Windows/Linux and Cmd on Mac
55
+ * @param id - Unique identifier for this shortcut
56
+ * @param key - The key to listen for
57
+ * @param handler - Callback when shortcut is triggered
58
+ * @param preventDefault - Whether to prevent default browser behavior
59
+ */
60
+ registerCtrlOrCmdShortcut(id, key, handler, preventDefault = true) {
61
+ if (typeof window === 'undefined')
62
+ return;
63
+ const handleKeyDown = (event) => {
64
+ const keyboardEvent = event;
65
+ const keyMatches = keyboardEvent.key.toLowerCase() === key.toLowerCase();
66
+ const modifierMatches = keyboardEvent.ctrlKey || keyboardEvent.metaKey;
67
+ if (keyMatches && modifierMatches && !keyboardEvent.shiftKey && !keyboardEvent.altKey) {
68
+ if (preventDefault) {
69
+ keyboardEvent.preventDefault();
70
+ }
71
+ handler(keyboardEvent);
72
+ }
73
+ };
74
+ this.listeners.set(id, handleKeyDown);
75
+ window.addEventListener('keydown', handleKeyDown);
76
+ }
77
+ /**
78
+ * Unregister a keyboard shortcut
79
+ * @param id - The identifier of the shortcut to remove
80
+ */
81
+ unregisterShortcut(id) {
82
+ if (typeof window === 'undefined')
83
+ return;
84
+ const listener = this.listeners.get(id);
85
+ if (listener) {
86
+ window.removeEventListener('keydown', listener);
87
+ this.listeners.delete(id);
88
+ }
89
+ this.shortcuts.delete(id);
90
+ }
91
+ /**
92
+ * Unregister all shortcuts and clean up
93
+ */
94
+ cleanup() {
95
+ if (typeof window === 'undefined')
96
+ return;
97
+ this.listeners.forEach((listener) => {
98
+ window.removeEventListener('keydown', listener);
99
+ });
100
+ this.listeners.clear();
101
+ this.shortcuts.clear();
102
+ }
103
+ /**
104
+ * Check if a keyboard event matches a specific key combination
105
+ * @param event - The keyboard event to check
106
+ * @param key - The key to match
107
+ * @param ctrl - Whether Ctrl/Cmd should be pressed
108
+ * @param shift - Whether Shift should be pressed
109
+ * @param alt - Whether Alt should be pressed
110
+ */
111
+ matchesShortcut(event, key, ctrl = false, shift = false, alt = false) {
112
+ const keyMatches = event.key.toLowerCase() === key.toLowerCase();
113
+ const ctrlMatches = ctrl ? event.ctrlKey || event.metaKey : !event.ctrlKey && !event.metaKey;
114
+ const shiftMatches = shift ? event.shiftKey : !event.shiftKey;
115
+ const altMatches = alt ? event.altKey : !event.altKey;
116
+ return keyMatches && ctrlMatches && shiftMatches && altMatches;
117
+ }
118
+ }
119
+ // Singleton instance
120
+ let keyboardServiceInstance = null;
121
+ /**
122
+ * Get the singleton KeyboardService instance
123
+ */
124
+ export function getKeyboardService() {
125
+ if (!keyboardServiceInstance) {
126
+ keyboardServiceInstance = new KeyboardService();
127
+ }
128
+ return keyboardServiceInstance;
129
+ }
130
+ /**
131
+ * Create a new isolated KeyboardService instance
132
+ * Useful for testing or when you need independent event handling
133
+ */
134
+ export function createKeyboardService() {
135
+ return new KeyboardService();
136
+ }
@@ -0,0 +1,254 @@
1
+ /**
2
+ * Media Query Service
3
+ * Provides utilities for responsive design and media query management
4
+ */
5
+ /**
6
+ * Default breakpoints (Tailwind-inspired)
7
+ */
8
+ export const DEFAULT_BREAKPOINTS = {
9
+ sm: '640px',
10
+ md: '768px',
11
+ lg: '1024px',
12
+ xl: '1280px',
13
+ '2xl': '1536px',
14
+ };
15
+ /**
16
+ * MediaQueryService - Centralized media query management
17
+ * Provides:
18
+ * - Media query listener registration
19
+ * - Breakpoint detection
20
+ * - Window resize handling
21
+ * - Current viewport information
22
+ */
23
+ export class MediaQueryService {
24
+ constructor(breakpoints = DEFAULT_BREAKPOINTS) {
25
+ this.listeners = new Map();
26
+ this.resizeObserver = null;
27
+ this.resizeListeners = new Map();
28
+ this.breakpoints = breakpoints;
29
+ }
30
+ /**
31
+ * Register a media query listener
32
+ * @param id - Unique identifier for this listener
33
+ * @param query - CSS media query string
34
+ * @param handler - Callback when media query matches/unmatches
35
+ */
36
+ registerMediaQuery(id, query, handler) {
37
+ if (typeof window === 'undefined')
38
+ return;
39
+ try {
40
+ const matcher = window.matchMedia(query);
41
+ const listener = { query, handler, matcher };
42
+ // Call handler with initial state
43
+ handler(matcher.matches);
44
+ // Listen for changes
45
+ matcher.addEventListener('change', (e) => handler(e.matches));
46
+ this.listeners.set(id, listener);
47
+ }
48
+ catch (error) {
49
+ console.warn(`Failed to register media query "${query}":`, error);
50
+ }
51
+ }
52
+ /**
53
+ * Register a breakpoint listener (easier than raw media queries)
54
+ * @param id - Unique identifier
55
+ * @param breakpoint - Breakpoint name (sm, md, lg, xl, 2xl)
56
+ * @param type - Type of match: 'min' (mobile-first) or 'max' (desktop-first)
57
+ * @param handler - Callback when breakpoint matches
58
+ */
59
+ registerBreakpoint(id, breakpoint, type = 'min', handler) {
60
+ const size = this.breakpoints[breakpoint];
61
+ if (!size) {
62
+ console.warn(`Unknown breakpoint: ${breakpoint}`);
63
+ return;
64
+ }
65
+ const query = type === 'min' ? `(min-width: ${size})` : `(max-width: ${size})`;
66
+ this.registerMediaQuery(id, query, handler);
67
+ }
68
+ /**
69
+ * Unregister a media query listener
70
+ */
71
+ unregisterMediaQuery(id) {
72
+ const listener = this.listeners.get(id);
73
+ if (listener && listener.matcher) {
74
+ listener.matcher.removeEventListener('change', () => { });
75
+ }
76
+ this.listeners.delete(id);
77
+ }
78
+ /**
79
+ * Check if a media query currently matches
80
+ */
81
+ isMatching(query) {
82
+ if (typeof window === 'undefined')
83
+ return false;
84
+ try {
85
+ return window.matchMedia(query).matches;
86
+ }
87
+ catch (error) {
88
+ console.warn(`Failed to check media query "${query}":`, error);
89
+ return false;
90
+ }
91
+ }
92
+ /**
93
+ * Check if a breakpoint is currently active
94
+ */
95
+ isBreakpointActive(breakpoint, type = 'min') {
96
+ const size = this.breakpoints[breakpoint];
97
+ if (!size)
98
+ return false;
99
+ const query = type === 'min' ? `(min-width: ${size})` : `(max-width: ${size})`;
100
+ return this.isMatching(query);
101
+ }
102
+ /**
103
+ * Get the current viewport width
104
+ */
105
+ getViewportWidth() {
106
+ if (typeof window === 'undefined')
107
+ return 0;
108
+ return window.innerWidth;
109
+ }
110
+ /**
111
+ * Get the current viewport height
112
+ */
113
+ getViewportHeight() {
114
+ if (typeof window === 'undefined')
115
+ return 0;
116
+ return window.innerHeight;
117
+ }
118
+ /**
119
+ * Check if viewport is mobile-sized (< 768px)
120
+ */
121
+ isMobile() {
122
+ return this.getViewportWidth() < 768;
123
+ }
124
+ /**
125
+ * Check if viewport is tablet-sized (768px - 1024px)
126
+ */
127
+ isTablet() {
128
+ const width = this.getViewportWidth();
129
+ return width >= 768 && width < 1024;
130
+ }
131
+ /**
132
+ * Check if viewport is desktop-sized (>= 1024px)
133
+ */
134
+ isDesktop() {
135
+ return this.getViewportWidth() >= 1024;
136
+ }
137
+ /**
138
+ * Check if prefers-reduced-motion is enabled
139
+ */
140
+ prefersReducedMotion() {
141
+ if (typeof window === 'undefined')
142
+ return false;
143
+ try {
144
+ return window.matchMedia('(prefers-reduced-motion: reduce)').matches;
145
+ }
146
+ catch (_a) {
147
+ return false;
148
+ }
149
+ }
150
+ /**
151
+ * Check if dark mode is preferred
152
+ */
153
+ prefersDarkMode() {
154
+ if (typeof window === 'undefined')
155
+ return false;
156
+ try {
157
+ return window.matchMedia('(prefers-color-scheme: dark)').matches;
158
+ }
159
+ catch (_a) {
160
+ return false;
161
+ }
162
+ }
163
+ /**
164
+ * Get the current device orientation
165
+ */
166
+ getOrientation() {
167
+ if (typeof window === 'undefined')
168
+ return 'portrait';
169
+ return window.innerHeight > window.innerWidth ? 'portrait' : 'landscape';
170
+ }
171
+ /**
172
+ * Register a window resize listener
173
+ * @param id - Unique identifier
174
+ * @param handler - Callback with (width, height)
175
+ * @param debounceMs - Debounce delay in milliseconds
176
+ */
177
+ registerResizeListener(id, handler, debounceMs = 0) {
178
+ if (typeof window === 'undefined')
179
+ return;
180
+ let timeoutId = null;
181
+ const eventListener = () => {
182
+ if (timeoutId)
183
+ clearTimeout(timeoutId);
184
+ if (debounceMs > 0) {
185
+ timeoutId = setTimeout(() => {
186
+ handler(this.getViewportWidth(), this.getViewportHeight());
187
+ }, debounceMs);
188
+ }
189
+ else {
190
+ handler(this.getViewportWidth(), this.getViewportHeight());
191
+ }
192
+ };
193
+ this.resizeListeners.set(id, { handler, eventListener });
194
+ window.addEventListener('resize', eventListener);
195
+ // Call handler with initial dimensions
196
+ handler(this.getViewportWidth(), this.getViewportHeight());
197
+ }
198
+ /**
199
+ * Unregister a resize listener
200
+ */
201
+ unregisterResizeListener(id) {
202
+ if (typeof window === 'undefined')
203
+ return;
204
+ const entry = this.resizeListeners.get(id);
205
+ if (entry) {
206
+ window.removeEventListener('resize', entry.eventListener);
207
+ this.resizeListeners.delete(id);
208
+ }
209
+ }
210
+ /**
211
+ * Clean up all listeners
212
+ */
213
+ cleanup() {
214
+ this.listeners.forEach((listener) => {
215
+ if (listener.matcher) {
216
+ listener.matcher.removeEventListener('change', () => { });
217
+ }
218
+ });
219
+ this.listeners.clear();
220
+ if (typeof window !== 'undefined') {
221
+ this.resizeListeners.forEach((entry) => {
222
+ window.removeEventListener('resize', entry.eventListener);
223
+ });
224
+ }
225
+ this.resizeListeners.clear();
226
+ if (this.resizeObserver) {
227
+ this.resizeObserver.disconnect();
228
+ this.resizeObserver = null;
229
+ }
230
+ }
231
+ /**
232
+ * Update breakpoint configuration
233
+ */
234
+ setBreakpoints(breakpoints) {
235
+ this.breakpoints = Object.assign(Object.assign({}, DEFAULT_BREAKPOINTS), breakpoints);
236
+ }
237
+ }
238
+ // Singleton instance
239
+ let mediaQueryServiceInstance = null;
240
+ /**
241
+ * Get the singleton MediaQueryService instance
242
+ */
243
+ export function getMediaQueryService(breakpoints) {
244
+ if (!mediaQueryServiceInstance) {
245
+ mediaQueryServiceInstance = new MediaQueryService(breakpoints);
246
+ }
247
+ return mediaQueryServiceInstance;
248
+ }
249
+ /**
250
+ * Create a new isolated MediaQueryService instance
251
+ */
252
+ export function createMediaQueryService(breakpoints) {
253
+ return new MediaQueryService(breakpoints || DEFAULT_BREAKPOINTS);
254
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * SekiUI Type Definitions
3
+ * Central export point for all type definitions across components and services
4
+ */
5
+ // ============================================================================
6
+ // NOTE: Types from ./components/sidebar/types and ./utils/select/types
7
+ // are already re-exported at the top of this file via the main index.ts
8
+ // ============================================================================
9
+ // ============================================================================
10
+ // TYPE GUARDS AND UTILITY TYPES
11
+ // ============================================================================
12
+ /**
13
+ * Type guard to check if a value is a valid sidebar variant
14
+ */
15
+ export function isSidebarVariant(value) {
16
+ return ['sidebar', 'floating', 'inset'].includes(value);
17
+ }
18
+ /**
19
+ * Type guard to check if a value is a valid collapse mode
20
+ */
21
+ export function isCollapseMode(value) {
22
+ return ['offcanvas', 'icon', 'none'].includes(value);
23
+ }
24
+ /**
25
+ * Type guard to check if a value is a valid button size
26
+ */
27
+ export function isButtonSize(value) {
28
+ return ['sm', 'md', 'lg', 'icon-sm', 'icon', 'icon-lg'].includes(value);
29
+ }
30
+ /**
31
+ * Type guard to check if a value is a valid button variant
32
+ */
33
+ export function isButtonVariant(value) {
34
+ return ['primary', 'secondary', 'outline', 'ghost', 'destructive', 'link'].includes(value);
35
+ }
36
+ /**
37
+ * Type guard to check if a value is a valid tooltip side
38
+ */
39
+ export function isTooltipSide(value) {
40
+ return ['top', 'right', 'bottom', 'left'].includes(value);
41
+ }