@sekiui/elements 0.1.6 → 0.1.7

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 (213) hide show
  1. package/dist/cjs/accordion-ids-Cjq6aIVd.js +42 -0
  2. package/dist/cjs/direction-Dt-r6_ec.js +14 -0
  3. package/dist/cjs/host-id-C3XieWsk.js +32 -0
  4. package/dist/cjs/layout-DR0fTNAb.js +109 -0
  5. package/dist/cjs/loader.cjs.js +1 -1
  6. package/dist/cjs/seki-accordion-content.cjs.entry.js +27 -25
  7. package/dist/cjs/seki-accordion-item.cjs.entry.js +13 -29
  8. package/dist/cjs/seki-accordion-trigger.cjs.entry.js +36 -28
  9. package/dist/cjs/seki-accordion.cjs.entry.js +18 -12
  10. package/dist/cjs/seki-carousel-dots.cjs.entry.js +15 -7
  11. package/dist/cjs/seki-carousel-next.cjs.entry.js +1 -1
  12. package/dist/cjs/seki-carousel-previous.cjs.entry.js +1 -1
  13. package/dist/cjs/seki-carousel.cjs.entry.js +51 -6
  14. package/dist/cjs/seki-combobox.cjs.entry.js +80 -1
  15. package/dist/cjs/seki-context-menu-content.cjs.entry.js +13 -16
  16. package/dist/cjs/seki-context-menu.cjs.entry.js +14 -9
  17. package/dist/cjs/seki-dropdown-menu-content.cjs.entry.js +13 -16
  18. package/dist/cjs/seki-dropdown-menu-trigger.cjs.entry.js +7 -15
  19. package/dist/cjs/seki-dropdown-menu.cjs.entry.js +14 -9
  20. package/dist/cjs/seki-hover-card-content.cjs.entry.js +27 -33
  21. package/dist/cjs/seki-hover-card-trigger.cjs.entry.js +10 -25
  22. package/dist/cjs/seki-hover-card.cjs.entry.js +7 -6
  23. package/dist/cjs/seki-radio-group-item.cjs.entry.js +18 -1
  24. package/dist/cjs/seki-radio-group.cjs.entry.js +8 -1
  25. package/dist/cjs/seki-resizable-handle.cjs.entry.js +72 -15
  26. package/dist/cjs/seki-resizable-panel-group.cjs.entry.js +11 -107
  27. package/dist/cjs/seki-resizable-panel.cjs.entry.js +5 -3
  28. package/dist/cjs/seki-tabs-content.cjs.entry.js +21 -19
  29. package/dist/cjs/seki-tabs-list.cjs.entry.js +8 -24
  30. package/dist/cjs/seki-tabs-trigger.cjs.entry.js +23 -31
  31. package/dist/cjs/seki-tabs.cjs.entry.js +15 -18
  32. package/dist/cjs/sekiui.cjs.js +1 -1
  33. package/dist/cjs/{tab-ids-BzpIZnUS.js → tab-ids-BOdn1Fu2.js} +14 -0
  34. package/dist/collection/collection-manifest.json +2 -2
  35. package/dist/collection/components/accordion/accordion-ids.js +36 -0
  36. package/dist/collection/components/accordion/seki-accordion-content.js +27 -25
  37. package/dist/collection/components/accordion/seki-accordion-item.js +12 -29
  38. package/dist/collection/components/accordion/seki-accordion-trigger.css +6 -2
  39. package/dist/collection/components/accordion/seki-accordion-trigger.js +37 -28
  40. package/dist/collection/components/accordion/seki-accordion.js +40 -11
  41. package/dist/collection/components/carousel/seki-carousel-dots.js +15 -7
  42. package/dist/collection/components/carousel/seki-carousel-navigation.css +9 -4
  43. package/dist/collection/components/carousel/seki-carousel.js +51 -6
  44. package/dist/collection/components/combobox/seki-combobox.js +103 -1
  45. package/dist/collection/components/context-menu/seki-context-menu-content.js +13 -16
  46. package/dist/collection/components/context-menu/seki-context-menu.js +31 -9
  47. package/dist/collection/components/dropdown-menu/seki-dropdown-menu-content.js +13 -16
  48. package/dist/collection/components/dropdown-menu/seki-dropdown-menu-trigger.js +7 -15
  49. package/dist/collection/components/dropdown-menu/seki-dropdown-menu.js +31 -9
  50. package/dist/collection/components/hover-card/seki-hover-card-content.js +27 -33
  51. package/dist/collection/components/hover-card/seki-hover-card-trigger.js +10 -25
  52. package/dist/collection/components/hover-card/seki-hover-card.js +24 -6
  53. package/dist/collection/components/radio-group/seki-radio-group-item.js +18 -1
  54. package/dist/collection/components/radio-group/seki-radio-group.js +25 -1
  55. package/dist/collection/components/resizable/layout.js +2 -0
  56. package/dist/collection/components/resizable/seki-resizable-handle.css +6 -2
  57. package/dist/collection/components/resizable/seki-resizable-handle.js +71 -14
  58. package/dist/collection/components/resizable/seki-resizable-panel-group.js +5 -2
  59. package/dist/collection/components/resizable/seki-resizable-panel.js +5 -3
  60. package/dist/collection/components/tabs/seki-tabs-content.js +24 -19
  61. package/dist/collection/components/tabs/seki-tabs-list.js +8 -24
  62. package/dist/collection/components/tabs/seki-tabs-trigger.js +24 -19
  63. package/dist/collection/components/tabs/seki-tabs.js +34 -18
  64. package/dist/collection/components/tabs/tab-ids.js +13 -0
  65. package/dist/collection/utils/host-id.js +27 -0
  66. package/dist/components/p-BD28aG2U.js +12 -0
  67. package/dist/components/p-BuvZJ6_r.js +39 -0
  68. package/dist/components/p-DmN32Mrm.js +103 -0
  69. package/dist/components/{p-CmuSriLz.js → p-bxPJJ6k4.js} +14 -1
  70. package/dist/components/p-ppXRAghy.js +29 -0
  71. package/dist/components/seki-accordion-content.js +27 -25
  72. package/dist/components/seki-accordion-item.js +12 -29
  73. package/dist/components/seki-accordion-trigger.js +38 -29
  74. package/dist/components/seki-accordion.js +22 -13
  75. package/dist/components/seki-carousel-dots.js +15 -7
  76. package/dist/components/seki-carousel-next.js +1 -1
  77. package/dist/components/seki-carousel-previous.js +1 -1
  78. package/dist/components/seki-carousel.js +51 -6
  79. package/dist/components/seki-combobox.js +81 -2
  80. package/dist/components/seki-context-menu-content.js +13 -16
  81. package/dist/components/seki-context-menu.js +14 -9
  82. package/dist/components/seki-dropdown-menu-content.js +13 -16
  83. package/dist/components/seki-dropdown-menu-trigger.js +7 -15
  84. package/dist/components/seki-dropdown-menu.js +14 -9
  85. package/dist/components/seki-hover-card-content.js +27 -33
  86. package/dist/components/seki-hover-card-trigger.js +10 -25
  87. package/dist/components/seki-hover-card.js +7 -6
  88. package/dist/components/seki-radio-group-item.js +18 -1
  89. package/dist/components/seki-radio-group.js +8 -1
  90. package/dist/components/seki-resizable-handle.js +72 -15
  91. package/dist/components/seki-resizable-panel-group.js +6 -102
  92. package/dist/components/seki-resizable-panel.js +5 -3
  93. package/dist/components/seki-tabs-content.js +23 -19
  94. package/dist/components/seki-tabs-list.js +8 -24
  95. package/dist/components/seki-tabs-trigger.js +25 -31
  96. package/dist/components/seki-tabs.js +16 -18
  97. package/dist/esm/accordion-ids-D58tRFZv.js +39 -0
  98. package/dist/esm/direction-BD28aG2U.js +12 -0
  99. package/dist/esm/host-id-ppXRAghy.js +29 -0
  100. package/dist/esm/layout-DmN32Mrm.js +103 -0
  101. package/dist/esm/loader.js +1 -1
  102. package/dist/esm/seki-accordion-content.entry.js +27 -25
  103. package/dist/esm/seki-accordion-item.entry.js +13 -29
  104. package/dist/esm/seki-accordion-trigger.entry.js +36 -28
  105. package/dist/esm/seki-accordion.entry.js +18 -12
  106. package/dist/esm/seki-carousel-dots.entry.js +15 -7
  107. package/dist/esm/seki-carousel-next.entry.js +1 -1
  108. package/dist/esm/seki-carousel-previous.entry.js +1 -1
  109. package/dist/esm/seki-carousel.entry.js +51 -6
  110. package/dist/esm/seki-combobox.entry.js +80 -1
  111. package/dist/esm/seki-context-menu-content.entry.js +13 -16
  112. package/dist/esm/seki-context-menu.entry.js +14 -9
  113. package/dist/esm/seki-dropdown-menu-content.entry.js +13 -16
  114. package/dist/esm/seki-dropdown-menu-trigger.entry.js +7 -15
  115. package/dist/esm/seki-dropdown-menu.entry.js +14 -9
  116. package/dist/esm/seki-hover-card-content.entry.js +27 -33
  117. package/dist/esm/seki-hover-card-trigger.entry.js +10 -25
  118. package/dist/esm/seki-hover-card.entry.js +7 -6
  119. package/dist/esm/seki-radio-group-item.entry.js +18 -1
  120. package/dist/esm/seki-radio-group.entry.js +8 -1
  121. package/dist/esm/seki-resizable-handle.entry.js +72 -15
  122. package/dist/esm/seki-resizable-panel-group.entry.js +6 -102
  123. package/dist/esm/seki-resizable-panel.entry.js +5 -3
  124. package/dist/esm/seki-tabs-content.entry.js +21 -19
  125. package/dist/esm/seki-tabs-list.entry.js +8 -24
  126. package/dist/esm/seki-tabs-trigger.entry.js +22 -30
  127. package/dist/esm/seki-tabs.entry.js +15 -18
  128. package/dist/esm/sekiui.js +1 -1
  129. package/dist/esm/{tab-ids-CmuSriLz.js → tab-ids-bxPJJ6k4.js} +14 -1
  130. package/dist/sekiui/p-05646b3d.entry.js +1 -0
  131. package/dist/sekiui/p-06307251.entry.js +1 -0
  132. package/dist/sekiui/p-13206751.entry.js +1 -0
  133. package/dist/sekiui/p-2681547f.entry.js +1 -0
  134. package/dist/sekiui/p-34880473.entry.js +1 -0
  135. package/dist/sekiui/{p-3a950a93.entry.js → p-44c4e0ca.entry.js} +1 -1
  136. package/dist/sekiui/p-45d3e21b.entry.js +1 -0
  137. package/dist/sekiui/p-5a222bfa.entry.js +1 -0
  138. package/dist/sekiui/p-68d2ef55.entry.js +1 -0
  139. package/dist/sekiui/p-78df4dc1.entry.js +1 -0
  140. package/dist/sekiui/p-81e180e1.entry.js +1 -0
  141. package/dist/sekiui/{p-5cb2a9fc.entry.js → p-99aba093.entry.js} +1 -1
  142. package/dist/sekiui/p-BD28aG2U.js +1 -0
  143. package/dist/sekiui/p-BuvZJ6_r.js +1 -0
  144. package/dist/sekiui/p-DmN32Mrm.js +1 -0
  145. package/dist/sekiui/p-ad26ccd9.entry.js +1 -0
  146. package/dist/sekiui/p-b3260d62.entry.js +1 -0
  147. package/dist/sekiui/p-bxPJJ6k4.js +1 -0
  148. package/dist/sekiui/{p-d26b63d0.entry.js → p-c906ad1e.entry.js} +1 -1
  149. package/dist/sekiui/p-cb621f86.entry.js +1 -0
  150. package/dist/sekiui/p-d4863b02.entry.js +1 -0
  151. package/dist/sekiui/p-d81d442f.entry.js +1 -0
  152. package/dist/sekiui/p-d9a625f4.entry.js +1 -0
  153. package/dist/sekiui/p-db922f6f.entry.js +1 -0
  154. package/dist/sekiui/p-e1fbadc1.entry.js +1 -0
  155. package/dist/sekiui/p-e3dc7f49.entry.js +1 -0
  156. package/dist/sekiui/{p-8a96f54c.entry.js → p-ea8654dc.entry.js} +1 -1
  157. package/dist/sekiui/p-eb76021c.entry.js +1 -0
  158. package/dist/sekiui/p-f049f47a.entry.js +1 -0
  159. package/dist/sekiui/p-f0650660.entry.js +1 -0
  160. package/dist/sekiui/p-ppXRAghy.js +1 -0
  161. package/dist/sekiui/sekiui.esm.js +1 -1
  162. package/dist/types/components/accordion/accordion-ids.d.ts +17 -0
  163. package/dist/types/components/accordion/seki-accordion-content.d.ts +3 -3
  164. package/dist/types/components/accordion/seki-accordion-item.d.ts +2 -9
  165. package/dist/types/components/accordion/seki-accordion-trigger.d.ts +4 -4
  166. package/dist/types/components/accordion/seki-accordion.d.ts +7 -2
  167. package/dist/types/components/carousel/seki-carousel.d.ts +10 -0
  168. package/dist/types/components/combobox/seki-combobox.d.ts +13 -0
  169. package/dist/types/components/context-menu/seki-context-menu-content.d.ts +3 -3
  170. package/dist/types/components/context-menu/seki-context-menu.d.ts +6 -0
  171. package/dist/types/components/dropdown-menu/seki-dropdown-menu-content.d.ts +3 -3
  172. package/dist/types/components/dropdown-menu/seki-dropdown-menu-trigger.d.ts +2 -4
  173. package/dist/types/components/dropdown-menu/seki-dropdown-menu.d.ts +6 -0
  174. package/dist/types/components/hover-card/seki-hover-card-content.d.ts +3 -1
  175. package/dist/types/components/hover-card/seki-hover-card-trigger.d.ts +2 -2
  176. package/dist/types/components/hover-card/seki-hover-card.d.ts +5 -1
  177. package/dist/types/components/radio-group/seki-radio-group-item.d.ts +3 -0
  178. package/dist/types/components/radio-group/seki-radio-group.d.ts +14 -0
  179. package/dist/types/components/resizable/layout.d.ts +2 -0
  180. package/dist/types/components/resizable/seki-resizable-handle.d.ts +13 -0
  181. package/dist/types/components/resizable/seki-resizable-panel-group.d.ts +1 -0
  182. package/dist/types/components/resizable/types.d.ts +2 -0
  183. package/dist/types/components/tabs/seki-tabs-content.d.ts +3 -4
  184. package/dist/types/components/tabs/seki-tabs-list.d.ts +2 -5
  185. package/dist/types/components/tabs/seki-tabs-trigger.d.ts +3 -4
  186. package/dist/types/components/tabs/seki-tabs.d.ts +8 -2
  187. package/dist/types/components/tabs/tab-ids.d.ts +5 -0
  188. package/dist/types/components.d.ts +14 -0
  189. package/dist/types/utils/host-id.d.ts +7 -0
  190. package/package.json +1 -1
  191. package/dist/sekiui/p-00ed0a52.entry.js +0 -1
  192. package/dist/sekiui/p-02fe58f7.entry.js +0 -1
  193. package/dist/sekiui/p-044e875d.entry.js +0 -1
  194. package/dist/sekiui/p-1971d77d.entry.js +0 -1
  195. package/dist/sekiui/p-233a4393.entry.js +0 -1
  196. package/dist/sekiui/p-2622ca4c.entry.js +0 -1
  197. package/dist/sekiui/p-28c64e07.entry.js +0 -1
  198. package/dist/sekiui/p-2bd22398.entry.js +0 -1
  199. package/dist/sekiui/p-368d2d15.entry.js +0 -1
  200. package/dist/sekiui/p-49742de9.entry.js +0 -1
  201. package/dist/sekiui/p-5bfb96fd.entry.js +0 -1
  202. package/dist/sekiui/p-5e596590.entry.js +0 -1
  203. package/dist/sekiui/p-78c8c4bf.entry.js +0 -1
  204. package/dist/sekiui/p-88ba7051.entry.js +0 -1
  205. package/dist/sekiui/p-90387022.entry.js +0 -1
  206. package/dist/sekiui/p-CmuSriLz.js +0 -1
  207. package/dist/sekiui/p-af71afe5.entry.js +0 -1
  208. package/dist/sekiui/p-b26b3102.entry.js +0 -1
  209. package/dist/sekiui/p-b43f6733.entry.js +0 -1
  210. package/dist/sekiui/p-bf687983.entry.js +0 -1
  211. package/dist/sekiui/p-eafe8a84.entry.js +0 -1
  212. package/dist/sekiui/p-ec0d8978.entry.js +0 -1
  213. package/dist/sekiui/p-f418ba3a.entry.js +0 -1
@@ -1,4 +1,7 @@
1
1
  import { h, Host } from "@stencil/core";
2
+ import { getAccordionIds, resolveAccordionIds } from "./accordion-ids";
3
+ import { claimHostId } from "../../utils/host-id";
4
+ import { ContextConsumer } from "../../utils/context-bridge";
2
5
  /**
3
6
  * Trigger button for accordion item - click to toggle
4
7
  *
@@ -10,21 +13,32 @@ import { h, Host } from "@stencil/core";
10
13
  export class SekiAccordionTrigger {
11
14
  constructor() {
12
15
  this._isOpen = false;
13
- this.onAccordionValueChange = () => {
14
- var _a;
15
- const context = this.getItemContext();
16
- this._isOpen = (_a = context === null || context === void 0 ? void 0 : context.isOpen) !== null && _a !== void 0 ? _a : false;
17
- };
16
+ this._disabled = false;
17
+ this.accordion = new ContextConsumer('seki-accordion', '_accordionContext', (ctx) => {
18
+ var _a, _b;
19
+ this._isOpen = this.readIsOpen();
20
+ // Derived from the accordion and the item's attribute (what handleClick
21
+ // checks), not from the item's cached context, which may not exist yet when
22
+ // the item upgrades after this trigger.
23
+ const item = this.el.closest('seki-accordion-item');
24
+ this._disabled = ((_a = item === null || item === void 0 ? void 0 : item.hasAttribute('disabled')) !== null && _a !== void 0 ? _a : false) || ((_b = ctx === null || ctx === void 0 ? void 0 : ctx.disabled) !== null && _b !== void 0 ? _b : false);
25
+ });
26
+ }
27
+ // Read open state straight from the accordion: the item's cached context may
28
+ // not have been refreshed yet when this listener runs.
29
+ readIsOpen() {
30
+ var _a, _b, _c;
31
+ const value = (_a = this.el.closest('seki-accordion-item')) === null || _a === void 0 ? void 0 : _a.getAttribute('value');
32
+ const ctx = this.accordion.context;
33
+ return value && ctx ? ctx.isItemOpen(value) : ((_c = (_b = this.getItemContext()) === null || _b === void 0 ? void 0 : _b.isOpen) !== null && _c !== void 0 ? _c : false);
18
34
  }
19
35
  getItemContext() {
20
36
  const item = this.el.closest('seki-accordion-item');
21
37
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
22
38
  return item ? item._accordionItemContext : null;
23
39
  }
24
- componentWillRender() {
25
- var _a;
26
- const context = this.getItemContext();
27
- this._isOpen = (_a = context === null || context === void 0 ? void 0 : context.isOpen) !== null && _a !== void 0 ? _a : false;
40
+ connectedCallback() {
41
+ this.accordion.connect(this.el);
28
42
  }
29
43
  handleClick() {
30
44
  // Direct method call - no closures, no timing issues
@@ -105,29 +119,23 @@ export class SekiAccordionTrigger {
105
119
  const triggers = this.getAllTriggers();
106
120
  (_a = triggers[triggers.length - 1]) === null || _a === void 0 ? void 0 : _a.focus();
107
121
  }
108
- componentDidLoad() {
109
- var _a, _b;
110
- // Listen for value changes from parent accordion
111
- this.accordionEl = (_a = this.el.closest('seki-accordion')) !== null && _a !== void 0 ? _a : undefined;
112
- (_b = this.accordionEl) === null || _b === void 0 ? void 0 : _b.addEventListener('sekiValueChange', this.onAccordionValueChange);
113
- }
114
122
  disconnectedCallback() {
115
- var _a;
116
- (_a = this.accordionEl) === null || _a === void 0 ? void 0 : _a.removeEventListener('sekiValueChange', this.onAccordionValueChange);
123
+ this.accordion.disconnect();
117
124
  }
118
125
  render() {
119
- var _a, _b, _c, _d;
120
- const context = this.getItemContext();
121
- const isOpen = (_a = context === null || context === void 0 ? void 0 : context.isOpen) !== null && _a !== void 0 ? _a : false;
122
- const disabled = (_b = context === null || context === void 0 ? void 0 : context.disabled) !== null && _b !== void 0 ? _b : false;
123
- const contentId = (_c = context === null || context === void 0 ? void 0 : context.contentId) !== null && _c !== void 0 ? _c : '';
124
- const triggerId = (_d = context === null || context === void 0 ? void 0 : context.triggerId) !== null && _d !== void 0 ? _d : '';
125
- return (h(Host, { key: 'ecc2cf4c4c44872c22755937e4056f2c5e6dbc8e', "data-state": isOpen ? 'open' : 'closed', "data-disabled": disabled ? '' : undefined, tabindex: disabled ? -1 : 0, role: "button" }, h("button", { key: '03e96da86d67bd9ff1d0630c312658d81e20cee0', part: "trigger", type: "button", id: triggerId, class: "accordion-trigger", "aria-expanded": isOpen ? 'true' : 'false', "aria-controls": contentId, disabled: disabled,
126
- // The host is the single tab stop and handles the keyboard.
127
- tabindex: -1 }, h("slot", { key: 'b2040f79657b46c4b081f2c1a72d6f81521766d4' }), h("svg", { key: 'bda6e302ee1d4e723b17a7807c0d2c8c879241a7', part: "icon", class: {
126
+ const isOpen = this._isOpen;
127
+ const disabled = this._disabled;
128
+ // IDREFs cannot cross shadow roots, so the id / aria-controls / aria-expanded
129
+ // live on this light-DOM host and point at the content host. An author id on
130
+ // either host is kept.
131
+ const item = this.el.closest('seki-accordion-item');
132
+ const ids = getAccordionIds(item);
133
+ const hostId = claimHostId(this.el, ids.triggerId);
134
+ const { contentId } = resolveAccordionIds(item);
135
+ return (h(Host, { key: 'e8460b73584f77661d9ef18d0b84042ccce84545', id: hostId, "data-state": isOpen ? 'open' : 'closed', "data-disabled": disabled ? '' : undefined, tabindex: disabled ? -1 : 0, role: "button", "aria-expanded": isOpen ? 'true' : 'false', "aria-controls": contentId, "aria-disabled": disabled ? 'true' : undefined }, h("div", { key: '025c1edaf275243cc2364db8d8e9cf2d19b23acd', part: "trigger", class: "accordion-trigger" }, h("slot", { key: '859a324fb14c061aea66533206a155c5080cb172' }), h("svg", { key: '87a6a132ee575ef3a7702d2a4873c9d8f6d5885e', part: "icon", class: {
128
136
  'accordion-chevron': true,
129
137
  'accordion-chevron--open': isOpen,
130
- }, xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, h("path", { key: '2507810e95127aee8bf85af8f4930e2644975625', d: "m6 9 6 6 6-6" })))));
138
+ }, xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, h("path", { key: '849c07b8fa675659f8bb7cb035dde02820796cfd', d: "m6 9 6 6 6-6" })))));
131
139
  }
132
140
  static get is() { return "seki-accordion-trigger"; }
133
141
  static get encapsulation() { return "shadow"; }
@@ -143,7 +151,8 @@ export class SekiAccordionTrigger {
143
151
  }
144
152
  static get states() {
145
153
  return {
146
- "_isOpen": {}
154
+ "_isOpen": {},
155
+ "_disabled": {}
147
156
  };
148
157
  }
149
158
  static get elementRef() { return "el"; }
@@ -1,4 +1,5 @@
1
1
  import { h, Host } from "@stencil/core";
2
+ import { publishContext } from "../../utils/context-bridge";
2
3
  /**
3
4
  * Accordion root component that manages expanded state of items
4
5
  *
@@ -32,10 +33,14 @@ export class SekiAccordion {
32
33
  if (initialValue) {
33
34
  this._openItems = Array.isArray(initialValue) ? [...initialValue] : [initialValue];
34
35
  }
36
+ // Parts already upgraded hear this; parts that upgrade later read it when
37
+ // they connect (#384).
38
+ this.publish();
35
39
  }
36
40
  valueChanged(newValue) {
37
41
  if (newValue !== undefined) {
38
42
  this._openItems = Array.isArray(newValue) ? [...newValue] : [newValue];
43
+ this.publish();
39
44
  // Programmatic `accordionEl.value = x` must notify already-mounted
40
45
  // children the same way toggleItem() does (found during #261 audit).
41
46
  this.sekiValueChange.emit(newValue);
@@ -53,6 +58,7 @@ export class SekiAccordion {
53
58
  // Only close if collapsible
54
59
  if (this.collapsible) {
55
60
  this._openItems = [];
61
+ this.publish();
56
62
  // Unlike the open/multiple-mode branches below, this direct emit
57
63
  // must stay: @Watch('value')'s handler skips emitting when
58
64
  // newValue is undefined (a pre-existing guard), so this is the
@@ -82,9 +88,10 @@ export class SekiAccordion {
82
88
  // separate emit needed (found during #268).
83
89
  this.value = [...this._openItems];
84
90
  }
85
- // Update context for child components
86
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
87
- this.el._accordionContext = this.getContext();
91
+ this.publish();
92
+ }
93
+ publish() {
94
+ publishContext(this.el, '_accordionContext', this.getContext(), this.contextChange);
88
95
  }
89
96
  isItemOpen(itemValue) {
90
97
  return this._openItems.includes(itemValue);
@@ -99,16 +106,11 @@ export class SekiAccordion {
99
106
  isItemOpen: (itemValue) => this.isItemOpen(itemValue),
100
107
  };
101
108
  }
102
- componentDidLoad() {
103
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
104
- this.el._accordionContext = this.getContext();
105
- }
106
- componentDidUpdate() {
107
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
108
- this.el._accordionContext = this.getContext();
109
+ settingsChanged() {
110
+ this.publish();
109
111
  }
110
112
  render() {
111
- return (h(Host, { key: '1b5ed02b2fc9c58659f70b4ef98b7624623a9477', "data-disabled": this.disabled ? '' : undefined }, h("div", { key: 'e9f375c32d554de4024996920b4cf58bff77b954', part: "accordion", class: "accordion" }, h("slot", { key: '635d3cb635711653bc0951f1c4088746ecdcaab8' }))));
113
+ return (h(Host, { key: '604f9f0845c8c3facd643ccf8eab22139443ed97', "data-disabled": this.disabled ? '' : undefined }, h("div", { key: 'fee00af3c001cd0553d17021df330e62096d3413', part: "accordion", class: "accordion" }, h("slot", { key: 'b1c5b80cb3fda3015b2bfebef43d174897cd085f' }))));
112
114
  }
113
115
  static get is() { return "seki-accordion"; }
114
116
  static get encapsulation() { return "shadow"; }
@@ -245,6 +247,24 @@ export class SekiAccordion {
245
247
  "resolved": "string | string[]",
246
248
  "references": {}
247
249
  }
250
+ }, {
251
+ "method": "contextChange",
252
+ "name": "sekiContextChange",
253
+ "bubbles": true,
254
+ "cancelable": true,
255
+ "composed": true,
256
+ "docs": {
257
+ "tags": [{
258
+ "name": "internal",
259
+ "text": undefined
260
+ }],
261
+ "text": "Internal: notifies accordion parts that the published context changed."
262
+ },
263
+ "complexType": {
264
+ "original": "void",
265
+ "resolved": "void",
266
+ "references": {}
267
+ }
248
268
  }];
249
269
  }
250
270
  static get methods() {
@@ -277,6 +297,15 @@ export class SekiAccordion {
277
297
  return [{
278
298
  "propName": "value",
279
299
  "methodName": "valueChanged"
300
+ }, {
301
+ "propName": "disabled",
302
+ "methodName": "settingsChanged"
303
+ }, {
304
+ "propName": "collapsible",
305
+ "methodName": "settingsChanged"
306
+ }, {
307
+ "propName": "type",
308
+ "methodName": "settingsChanged"
280
309
  }];
281
310
  }
282
311
  }
@@ -1,6 +1,7 @@
1
1
  import { h, Host } from "@stencil/core";
2
2
  import { subscribeToContextChange } from "../../utils/context-bridge";
3
3
  import { whenDefined } from "../../utils/when-defined";
4
+ import { isRtl } from "../../utils/direction";
4
5
  /**
5
6
  * Dot indicators for carousel navigation.
6
7
  * Automatically syncs with parent seki-carousel.
@@ -25,26 +26,33 @@ export class SekiCarouselDots {
25
26
  if (!dots)
26
27
  return;
27
28
  let targetIndex = null;
29
+ // Dots are laid out in the inline direction, so Left/Right are visual:
30
+ // in RTL the next dot is to the left.
31
+ const rtl = isRtl(this.el);
32
+ const prevKey = rtl ? 'ArrowRight' : 'ArrowLeft';
33
+ const nextKey = rtl ? 'ArrowLeft' : 'ArrowRight';
28
34
  switch (event.key) {
29
- case 'ArrowLeft':
35
+ case prevKey:
30
36
  case 'ArrowUp':
31
- event.preventDefault();
32
37
  targetIndex = index > 0 ? index - 1 : this.scrollSnapCount - 1;
33
38
  break;
34
- case 'ArrowRight':
39
+ case nextKey:
35
40
  case 'ArrowDown':
36
- event.preventDefault();
37
41
  targetIndex = index < this.scrollSnapCount - 1 ? index + 1 : 0;
38
42
  break;
39
43
  case 'Home':
40
- event.preventDefault();
41
44
  targetIndex = 0;
42
45
  break;
43
46
  case 'End':
44
- event.preventDefault();
45
47
  targetIndex = this.scrollSnapCount - 1;
46
48
  break;
47
49
  }
50
+ if (targetIndex !== null) {
51
+ event.preventDefault();
52
+ // The dots sit inside <seki-carousel>, whose own arrow-key handler would
53
+ // otherwise advance a second time.
54
+ event.stopPropagation();
55
+ }
48
56
  if (targetIndex !== null) {
49
57
  (_b = dots[targetIndex]) === null || _b === void 0 ? void 0 : _b.focus();
50
58
  this.handleDotClick(targetIndex);
@@ -91,7 +99,7 @@ export class SekiCarouselDots {
91
99
  }
92
100
  render() {
93
101
  const dots = Array.from({ length: this.scrollSnapCount }, (_, i) => i);
94
- return (h(Host, { key: '56d50497d6c68d6533d9d42544b3093a58d96a93', class: "carousel-dots" }, h("div", { key: 'eb9da4064571b863d0a214a9189725606aa6367f', class: "carousel-dots__container", role: "tablist", "aria-label": "Carousel pagination" }, dots.map((index) => (h("button", { key: index, type: "button", class: {
102
+ return (h(Host, { key: '54e11889da62fbd901a297bbc733af13c3920efc', class: "carousel-dots" }, h("div", { key: 'e14079fbfcef9b48cac10deaae16ebefbb949b38', class: "carousel-dots__container", role: "tablist", "aria-label": "Carousel pagination" }, dots.map((index) => (h("button", { key: index, type: "button", class: {
95
103
  'carousel-dots__dot': true,
96
104
  'carousel-dots__dot--active': index === this.selectedIndex,
97
105
  }, role: "tab", "aria-selected": index === this.selectedIndex ? 'true' : 'false', "aria-label": `Go to slide ${index + 1}`, tabindex: index === this.selectedIndex ? '0' : '-1', onClick: () => this.handleDotClick(index), onKeyDown: (e) => this.handleKeyDown(e, index) }, h("span", { class: "carousel-dots__dot-inner" })))))));
@@ -9,11 +9,11 @@
9
9
  }
10
10
 
11
11
  :host(.carousel-nav--prev) {
12
- left: 0.5rem;
12
+ inset-inline-start: 0.5rem;
13
13
  }
14
14
 
15
15
  :host(.carousel-nav--next) {
16
- right: 0.5rem;
16
+ inset-inline-end: 0.5rem;
17
17
  }
18
18
 
19
19
  .carousel-nav__button {
@@ -56,14 +56,19 @@
56
56
  flex-shrink: 0;
57
57
  }
58
58
 
59
+ /* Chevrons point along the inline direction, so mirror them in RTL. */
60
+ .carousel-nav__icon:dir(rtl) {
61
+ transform: scaleX(-1);
62
+ }
63
+
59
64
  /* Responsive: hide on very small screens if needed */
60
65
  @media (max-width: 480px) {
61
66
  :host(.carousel-nav--prev) {
62
- left: 0.25rem;
67
+ inset-inline-start: 0.25rem;
63
68
  }
64
69
 
65
70
  :host(.carousel-nav--next) {
66
- right: 0.25rem;
71
+ inset-inline-end: 0.25rem;
67
72
  }
68
73
 
69
74
  .carousel-nav__button {
@@ -1,5 +1,6 @@
1
1
  import { h, Host } from "@stencil/core";
2
2
  import { isDefined, whenDefined } from "../../utils/when-defined";
3
+ import { isRtl } from "../../utils/direction";
3
4
  // Lazily loaded so embla-carousel (an optional peer dependency, see #263)
4
5
  // isn't pulled into any bundle unless a <seki-carousel> actually renders.
5
6
  // Module-scope cache shared across every seki-carousel instance on the page.
@@ -66,6 +67,16 @@ export class SekiCarousel {
66
67
  /** Slide to come back to when Embla is re-created after a move or option change. */
67
68
  this.resumeIndex = null;
68
69
  this.waitedForContentDefinition = false;
70
+ /** Direction Embla was created with; compared on `dir` changes (#381). */
71
+ this.appliedRtl = null;
72
+ this.dirObserver = null;
73
+ this.handleDirectionChange = () => {
74
+ if (!this.emblaApi || this.appliedRtl === null || isRtl(this.el) === this.appliedRtl)
75
+ return;
76
+ // teardownEmbla() records the selected slide in resumeIndex.
77
+ this.teardownEmbla();
78
+ this.initEmbla();
79
+ };
69
80
  this.handleSelect = () => {
70
81
  this.updateState();
71
82
  this.slideChange.emit({
@@ -108,23 +119,31 @@ export class SekiCarousel {
108
119
  }
109
120
  };
110
121
  this.handleKeyDown = (event) => {
111
- var _a, _b, _c, _d;
122
+ var _a, _b, _c, _d, _e, _f;
123
+ // Arrow keys are visual: in RTL the next slide is to the left.
124
+ const rtl = isRtl(this.el);
112
125
  switch (event.key) {
113
126
  case 'ArrowLeft':
114
127
  event.preventDefault();
115
- (_a = this.emblaApi) === null || _a === void 0 ? void 0 : _a.scrollPrev();
128
+ if (rtl)
129
+ (_a = this.emblaApi) === null || _a === void 0 ? void 0 : _a.scrollNext();
130
+ else
131
+ (_b = this.emblaApi) === null || _b === void 0 ? void 0 : _b.scrollPrev();
116
132
  break;
117
133
  case 'ArrowRight':
118
134
  event.preventDefault();
119
- (_b = this.emblaApi) === null || _b === void 0 ? void 0 : _b.scrollNext();
135
+ if (rtl)
136
+ (_c = this.emblaApi) === null || _c === void 0 ? void 0 : _c.scrollPrev();
137
+ else
138
+ (_d = this.emblaApi) === null || _d === void 0 ? void 0 : _d.scrollNext();
120
139
  break;
121
140
  case 'Home':
122
141
  event.preventDefault();
123
- (_c = this.emblaApi) === null || _c === void 0 ? void 0 : _c.scrollTo(0);
142
+ (_e = this.emblaApi) === null || _e === void 0 ? void 0 : _e.scrollTo(0);
124
143
  break;
125
144
  case 'End':
126
145
  event.preventDefault();
127
- (_d = this.emblaApi) === null || _d === void 0 ? void 0 : _d.scrollTo(this.scrollSnapCount - 1);
146
+ (_f = this.emblaApi) === null || _f === void 0 ? void 0 : _f.scrollTo(this.scrollSnapCount - 1);
128
147
  break;
129
148
  }
130
149
  };
@@ -137,6 +156,7 @@ export class SekiCarousel {
137
156
  // once, so a carousel that is moved in the DOM (keyed lists, portals,
138
157
  // drag-and-drop) is set up again from connectedCallback (#333).
139
158
  connectedCallback() {
159
+ this.observeDirection();
140
160
  if (this.hasLoaded)
141
161
  this.initEmbla();
142
162
  }
@@ -145,6 +165,9 @@ export class SekiCarousel {
145
165
  this.initEmbla();
146
166
  }
147
167
  disconnectedCallback() {
168
+ var _a;
169
+ (_a = this.dirObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
170
+ this.dirObserver = null;
148
171
  this.teardownEmbla();
149
172
  }
150
173
  // =========================================================================
@@ -204,6 +227,24 @@ export class SekiCarousel {
204
227
  // =========================================================================
205
228
  // Private Methods
206
229
  // =========================================================================
230
+ /**
231
+ * Embla's `direction` is fixed at creation, so watch the `dir` attribute of
232
+ * every ancestor (crossing shadow roots) and re-create Embla when the computed
233
+ * direction actually flips. Attribute-filtered and non-subtree, so it is cheap.
234
+ */
235
+ observeDirection() {
236
+ var _a, _b, _c;
237
+ (_a = this.dirObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
238
+ if (typeof MutationObserver === 'undefined')
239
+ return;
240
+ this.dirObserver = new MutationObserver(this.handleDirectionChange);
241
+ let node = this.el;
242
+ while (node) {
243
+ if (node.nodeType === Node.ELEMENT_NODE)
244
+ this.dirObserver.observe(node, { attributes: true, attributeFilter: ['dir'] });
245
+ node = (_c = (_b = node.parentNode) !== null && _b !== void 0 ? _b : node.host) !== null && _c !== void 0 ? _c : null;
246
+ }
247
+ }
207
248
  /** Stop autoplay, cancel a pending init and destroy Embla. Idempotent. */
208
249
  teardownEmbla() {
209
250
  this.initGeneration++;
@@ -262,6 +303,7 @@ export class SekiCarousel {
262
303
  // Container found — reset retry bookkeeping.
263
304
  this.initRetries = 0;
264
305
  this.initRafId = null;
306
+ const rtl = isRtl(this.el);
265
307
  const options = {
266
308
  loop: this.loop,
267
309
  // Keep the current slide when re-created (DOM move, option change).
@@ -269,6 +311,8 @@ export class SekiCarousel {
269
311
  slidesToScroll: this.slidesToScroll,
270
312
  dragFree: this.dragFree,
271
313
  containScroll: 'trimSnaps',
314
+ // Slides flow right-to-left under dir="rtl"; embla must scroll to match.
315
+ direction: rtl ? 'rtl' : 'ltr',
272
316
  };
273
317
  let EmblaCarousel;
274
318
  try {
@@ -283,6 +327,7 @@ export class SekiCarousel {
283
327
  if (!this.el.isConnected || !this.viewportEl || generation !== this.initGeneration || this.emblaApi)
284
328
  return;
285
329
  this.emblaApi = EmblaCarousel(this.viewportEl, options);
330
+ this.appliedRtl = rtl;
286
331
  // Set up event listeners
287
332
  this.emblaApi.on('select', this.handleSelect);
288
333
  this.emblaApi.on('reInit', this.handleReInit);
@@ -342,7 +387,7 @@ export class SekiCarousel {
342
387
  // Render
343
388
  // =========================================================================
344
389
  render() {
345
- return (h(Host, { key: '2f0a761a9c673b84aecb4733676d0baf60c9b7a6', class: "carousel", role: "region", "aria-roledescription": "carousel", "aria-label": this.ariaLabel, tabindex: "0", onMouseEnter: this.handleMouseEnter, onMouseLeave: this.handleMouseLeave, onFocusIn: this.handleFocusIn, onFocusOut: this.handleFocusOut, onKeyDown: this.handleKeyDown }, h("div", { key: 'd2c07222335e50b5ff119989444d5389e3e4f6d3', class: "carousel__container" }, h("slot", { key: '1581ad7b8a049b9efaa0941f284e0e9d04a53a5d' })), h("div", { key: '56edee3b1ed6f6729eed4cb7ebf9c024db351be6', class: "carousel__live-region", "aria-live": "polite", "aria-atomic": "true" }, `Slide ${this.selectedIndex + 1} of ${this.scrollSnapCount}`)));
390
+ return (h(Host, { key: 'b55deaf2dd34be409d82b9edc6171bd17e479fa5', class: "carousel", role: "region", "aria-roledescription": "carousel", "aria-label": this.ariaLabel, tabindex: "0", onMouseEnter: this.handleMouseEnter, onMouseLeave: this.handleMouseLeave, onFocusIn: this.handleFocusIn, onFocusOut: this.handleFocusOut, onKeyDown: this.handleKeyDown }, h("div", { key: '4e24a73b01d4f839d8f1f7feb2957664016cb6ad', class: "carousel__container" }, h("slot", { key: '0ba983eb5c01d434a5fa25073dcccdc58c8c35d3' })), h("div", { key: 'f1a62d565f8f6cda368ac6d15c95c53e1598bed6', class: "carousel__live-region", "aria-live": "polite", "aria-atomic": "true" }, `Slide ${this.selectedIndex + 1} of ${this.scrollSnapCount}`)));
346
391
  }
347
392
  static get is() { return "seki-carousel"; }
348
393
  static get encapsulation() { return "shadow"; }
@@ -1,6 +1,7 @@
1
1
  import { Host, h } from "@stencil/core";
2
2
  import { publishContext } from "../../utils/context-bridge";
3
3
  import { OverlayLayer } from "../../utils/overlay";
4
+ import { FormControl } from "../../utils/form-control";
4
5
  /**
5
6
  * @slot - Content containing seki-combobox-input and seki-combobox-content
6
7
  */
@@ -193,6 +194,32 @@ export class SekiCombobox {
193
194
  }
194
195
  // Expose context
195
196
  this.updateContextOnElement();
197
+ this.formControl = new FormControl(this.el, this.internals, {
198
+ getFormValue: () => this.getFormValue(),
199
+ isEmpty: () => this.isValueEmpty(),
200
+ isRequired: () => this.required,
201
+ isDisabled: () => this.disabled,
202
+ requiredMessage: 'Please select an item in the list.',
203
+ getAnchor: () => {
204
+ var _a, _b;
205
+ const input = this.el.querySelector('seki-combobox-input');
206
+ return (_b = (_a = input === null || input === void 0 ? void 0 : input.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('input')) !== null && _b !== void 0 ? _b : input;
207
+ },
208
+ restore: initial => this.setValueFromForm(initial),
209
+ restoreState: state => {
210
+ if (this.multiple) {
211
+ // A multiple combobox submits FormData; recover its entries for our name.
212
+ this.setValueFromForm(state instanceof FormData && this.name ? state.getAll(this.name).map(String) : []);
213
+ }
214
+ else {
215
+ this.setValueFromForm(typeof state === 'string' ? state : '');
216
+ }
217
+ },
218
+ });
219
+ // form.reset() goes back to the selection the combobox was authored with.
220
+ this.formControl.captureInitial(Array.isArray(this.internalValue) ? [...this.internalValue] : this.internalValue);
221
+ // Seed the form value so an unchanged combobox still submits its initial value.
222
+ this.formControl.sync();
196
223
  }
197
224
  componentDidLoad() {
198
225
  this.updateContextOnElement();
@@ -219,6 +246,45 @@ export class SekiCombobox {
219
246
  }
220
247
  this.updateContextOnElement();
221
248
  }
249
+ handleFormStateChange() {
250
+ var _a;
251
+ (_a = this.formControl) === null || _a === void 0 ? void 0 : _a.sync();
252
+ }
253
+ // Form-associated lifecycle callbacks (all behaviour lives in FormControl)
254
+ formResetCallback() {
255
+ var _a;
256
+ (_a = this.formControl) === null || _a === void 0 ? void 0 : _a.reset();
257
+ }
258
+ formDisabledCallback(disabled) {
259
+ var _a;
260
+ (_a = this.formControl) === null || _a === void 0 ? void 0 : _a.setFieldsetDisabled(disabled);
261
+ }
262
+ formStateRestoreCallback(state) {
263
+ var _a;
264
+ (_a = this.formControl) === null || _a === void 0 ? void 0 : _a.restoreState(state);
265
+ }
266
+ /** Put the selection back to `next` without emitting `sekiChange` (reset is not a user edit). */
267
+ setValueFromForm(next) {
268
+ const value = Array.isArray(next) ? [...next] : next;
269
+ this.internalValue = value;
270
+ this.value = value;
271
+ }
272
+ isValueEmpty() {
273
+ const v = this.internalValue;
274
+ return Array.isArray(v) ? v.length === 0 : v === '' || v === undefined || v === null;
275
+ }
276
+ getFormValue() {
277
+ if (this.multiple) {
278
+ const values = Array.isArray(this.internalValue) ? this.internalValue : [];
279
+ if (!this.name || values.length === 0)
280
+ return null;
281
+ const formData = new FormData();
282
+ for (const val of values)
283
+ formData.append(this.name, String(val));
284
+ return formData;
285
+ }
286
+ return this.isValueEmpty() ? null : String(this.internalValue);
287
+ }
222
288
  handleOpenPropChange(newOpen) {
223
289
  if (newOpen !== undefined) {
224
290
  this.isOpen = newOpen;
@@ -361,10 +427,11 @@ export class SekiCombobox {
361
427
  // Render
362
428
  // ===========================
363
429
  render() {
364
- return (h(Host, { key: 'dcd00e406ef681ef98ace6eb4f6c5fa556283ddb', role: "combobox", "aria-expanded": this.isOpen ? 'true' : 'false', "aria-haspopup": "listbox", "aria-owns": this.listboxId, "aria-label": this.ariaLabel, "aria-labelledby": this.ariaLabelledby, "aria-describedby": this.ariaDescribedby }, h("div", { key: '941163234f2d941d697214ff58c1c6d14264cf1c', class: "combobox-container" }, h("slot", { key: 'b9993d891965c09ede6286908380a3334124979a' }))));
430
+ return (h(Host, { key: 'b25ca5b559cb21768c5b9a6393bcee483dd40b1d', role: "combobox", "aria-expanded": this.isOpen ? 'true' : 'false', "aria-haspopup": "listbox", "aria-owns": this.listboxId, "aria-label": this.ariaLabel, "aria-labelledby": this.ariaLabelledby, "aria-describedby": this.ariaDescribedby }, h("div", { key: '2866fe0ec4d3d84b8568e4bcedc2a38aa2cd0047', class: "combobox-container" }, h("slot", { key: '630ea691c0b83e8b7aa7cefa8ae5704fa8a91017' }))));
365
431
  }
366
432
  static get is() { return "seki-combobox"; }
367
433
  static get encapsulation() { return "shadow"; }
434
+ static get formAssociated() { return true; }
368
435
  static get originalStyleUrls() {
369
436
  return {
370
437
  "$": ["seki-combobox.css"]
@@ -596,6 +663,25 @@ export class SekiCombobox {
596
663
  "attribute": "required",
597
664
  "defaultValue": "false"
598
665
  },
666
+ "name": {
667
+ "type": "string",
668
+ "mutable": false,
669
+ "complexType": {
670
+ "original": "string",
671
+ "resolved": "string | undefined",
672
+ "references": {}
673
+ },
674
+ "required": false,
675
+ "optional": true,
676
+ "docs": {
677
+ "tags": [],
678
+ "text": "Form field name for form submission"
679
+ },
680
+ "getter": false,
681
+ "setter": false,
682
+ "reflect": true,
683
+ "attribute": "name"
684
+ },
599
685
  "open": {
600
686
  "type": "boolean",
601
687
  "mutable": true,
@@ -886,6 +972,21 @@ export class SekiCombobox {
886
972
  return [{
887
973
  "propName": "value",
888
974
  "methodName": "handleValuePropChange"
975
+ }, {
976
+ "propName": "internalValue",
977
+ "methodName": "handleFormStateChange"
978
+ }, {
979
+ "propName": "disabled",
980
+ "methodName": "handleFormStateChange"
981
+ }, {
982
+ "propName": "required",
983
+ "methodName": "handleFormStateChange"
984
+ }, {
985
+ "propName": "multiple",
986
+ "methodName": "handleFormStateChange"
987
+ }, {
988
+ "propName": "name",
989
+ "methodName": "handleFormStateChange"
889
990
  }, {
890
991
  "propName": "open",
891
992
  "methodName": "handleOpenPropChange"
@@ -915,4 +1016,5 @@ export class SekiCombobox {
915
1016
  "passive": false
916
1017
  }];
917
1018
  }
1019
+ static get attachInternalsMemberName() { return "internals"; }
918
1020
  }
@@ -1,5 +1,6 @@
1
1
  import { h, Host } from "@stencil/core";
2
2
  import { clearTypeahead, focusFirstMenuItem, handleMenuKeyDown } from "../../utils/menu-navigation";
3
+ import { ContextConsumer } from "../../utils/context-bridge";
3
4
  const NAV_CONFIG = {
4
5
  itemSelector: 'seki-dropdown-menu-item, seki-dropdown-menu-checkbox, seki-dropdown-menu-radio, seki-dropdown-menu-sub',
5
6
  subContentSelector: 'seki-dropdown-menu-sub [slot="content"]',
@@ -14,17 +15,20 @@ const NAV_CONFIG = {
14
15
  export class SekiContextMenuContent {
15
16
  constructor() {
16
17
  this.typeahead = { buffer: '' };
17
- this.onMenuOpenChange = () => {
18
+ this.hasLoaded = false;
19
+ this.menu = new ContextConsumer('seki-context-menu', '_contextMenuContext', (context) => {
18
20
  var _a;
19
- const context = this.getContext();
21
+ const wasOpen = this._isOpen;
20
22
  this._isOpen = (_a = context === null || context === void 0 ? void 0 : context.isOpen) !== null && _a !== void 0 ? _a : false;
21
- if (this._isOpen) {
23
+ // Focus the first item when the menu opens after mount; an initially open
24
+ // menu keeps focus where it is, as before.
25
+ if (this.hasLoaded && this._isOpen && !wasOpen) {
22
26
  requestAnimationFrame(() => {
23
27
  this.updatePosition();
24
28
  void focusFirstMenuItem(this.el, NAV_CONFIG);
25
29
  });
26
30
  }
27
- };
31
+ });
28
32
  /**
29
33
  * Internal open state
30
34
  */
@@ -35,19 +39,13 @@ export class SekiContextMenuContent {
35
39
  this._position = { top: '0', left: '0' };
36
40
  }
37
41
  getContext() {
38
- const menu = this.el.closest('seki-context-menu');
39
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
40
- return menu ? menu._contextMenuContext : null;
42
+ return this.menu.context;
41
43
  }
42
- componentWillRender() {
43
- var _a;
44
- const context = this.getContext();
45
- this._isOpen = (_a = context === null || context === void 0 ? void 0 : context.isOpen) !== null && _a !== void 0 ? _a : false;
44
+ connectedCallback() {
45
+ this.menu.connect(this.el);
46
46
  }
47
47
  componentDidLoad() {
48
- var _a, _b;
49
- this.menuEl = (_a = this.el.closest('seki-context-menu')) !== null && _a !== void 0 ? _a : undefined;
50
- (_b = this.menuEl) === null || _b === void 0 ? void 0 : _b.addEventListener('sekiOpenChange', this.onMenuOpenChange);
48
+ this.hasLoaded = true;
51
49
  }
52
50
  componentDidRender() {
53
51
  if (this._isOpen) {
@@ -99,9 +97,8 @@ export class SekiContextMenuContent {
99
97
  this._position = next;
100
98
  }
101
99
  disconnectedCallback() {
102
- var _a;
103
100
  clearTypeahead(this.typeahead);
104
- (_a = this.menuEl) === null || _a === void 0 ? void 0 : _a.removeEventListener('sekiOpenChange', this.onMenuOpenChange);
101
+ this.menu.disconnect();
105
102
  }
106
103
  render() {
107
104
  if (!this._isOpen) {