le-kit 0.5.2 → 0.5.4

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 (475) hide show
  1. package/LLM_CONTEXT.md +22 -33
  2. package/package.json +3 -1
  3. package/readme.md +2 -0
  4. package/dist/cjs/index-BzadfLTc.js +0 -1864
  5. package/dist/cjs/index-BzadfLTc.js.map +0 -1
  6. package/dist/cjs/index.cjs.js +0 -124
  7. package/dist/cjs/index.cjs.js.map +0 -1
  8. package/dist/cjs/le-bar_16.cjs.entry.js +0 -3709
  9. package/dist/cjs/le-box.cjs.entry.js +0 -184
  10. package/dist/cjs/le-card.cjs.entry.js +0 -29
  11. package/dist/cjs/le-code-input.cjs.entry.js +0 -181
  12. package/dist/cjs/le-combobox.cjs.entry.js +0 -237
  13. package/dist/cjs/le-header-placeholder.cjs.entry.js +0 -18
  14. package/dist/cjs/le-kit.cjs.js +0 -27
  15. package/dist/cjs/le-kit.cjs.js.map +0 -1
  16. package/dist/cjs/le-multiselect.cjs.entry.js +0 -306
  17. package/dist/cjs/le-number-input.cjs.entry.js +0 -202
  18. package/dist/cjs/le-round-progress.cjs.entry.js +0 -106
  19. package/dist/cjs/le-segmented-control.cjs.entry.js +0 -245
  20. package/dist/cjs/le-side-panel-toggle.cjs.entry.js +0 -229
  21. package/dist/cjs/le-side-panel.cjs.entry.js +0 -546
  22. package/dist/cjs/le-stack.cjs.entry.js +0 -135
  23. package/dist/cjs/le-tab-bar.cjs.entry.js +0 -242
  24. package/dist/cjs/le-tab-panel.cjs.entry.js +0 -100
  25. package/dist/cjs/le-tab.cjs.entry.js +0 -136
  26. package/dist/cjs/le-tabs.cjs.entry.js +0 -307
  27. package/dist/cjs/le-tag.cjs.entry.js +0 -68
  28. package/dist/cjs/le-text.cjs.entry.js +0 -335
  29. package/dist/cjs/le-turntable.cjs.entry.js +0 -139
  30. package/dist/cjs/loader.cjs.js +0 -15
  31. package/dist/cjs/loader.cjs.js.map +0 -1
  32. package/dist/cjs/utils-Dxx9WhWK.js +0 -152
  33. package/dist/cjs/utils-Dxx9WhWK.js.map +0 -1
  34. package/dist/collection/assets/icons/chevron-down.svg +0 -3
  35. package/dist/collection/collection-manifest.json +0 -47
  36. package/dist/collection/components/le-bar/le-bar.css +0 -255
  37. package/dist/collection/components/le-bar/le-bar.js +0 -680
  38. package/dist/collection/components/le-bar/le-bar.js.map +0 -1
  39. package/dist/collection/components/le-box/le-box.css +0 -37
  40. package/dist/collection/components/le-box/le-box.js +0 -614
  41. package/dist/collection/components/le-box/le-box.js.map +0 -1
  42. package/dist/collection/components/le-button/le-button.css +0 -290
  43. package/dist/collection/components/le-button/le-button.js +0 -460
  44. package/dist/collection/components/le-button/le-button.js.map +0 -1
  45. package/dist/collection/components/le-card/le-card.css +0 -74
  46. package/dist/collection/components/le-card/le-card.js +0 -102
  47. package/dist/collection/components/le-card/le-card.js.map +0 -1
  48. package/dist/collection/components/le-checkbox/le-checkbox.css +0 -93
  49. package/dist/collection/components/le-checkbox/le-checkbox.js +0 -192
  50. package/dist/collection/components/le-checkbox/le-checkbox.js.map +0 -1
  51. package/dist/collection/components/le-code-input/le-code-input.css +0 -106
  52. package/dist/collection/components/le-code-input/le-code-input.js +0 -466
  53. package/dist/collection/components/le-code-input/le-code-input.js.map +0 -1
  54. package/dist/collection/components/le-collapse/le-collapse.css +0 -31
  55. package/dist/collection/components/le-collapse/le-collapse.js +0 -184
  56. package/dist/collection/components/le-collapse/le-collapse.js.map +0 -1
  57. package/dist/collection/components/le-combobox/le-combobox.css +0 -144
  58. package/dist/collection/components/le-combobox/le-combobox.js +0 -659
  59. package/dist/collection/components/le-combobox/le-combobox.js.map +0 -1
  60. package/dist/collection/components/le-component/le-component.css +0 -189
  61. package/dist/collection/components/le-component/le-component.js +0 -367
  62. package/dist/collection/components/le-component/le-component.js.map +0 -1
  63. package/dist/collection/components/le-current-heading/le-current-heading.css +0 -13
  64. package/dist/collection/components/le-current-heading/le-current-heading.js +0 -130
  65. package/dist/collection/components/le-current-heading/le-current-heading.js.map +0 -1
  66. package/dist/collection/components/le-dropdown-base/le-dropdown-base.css +0 -167
  67. package/dist/collection/components/le-dropdown-base/le-dropdown-base.js +0 -761
  68. package/dist/collection/components/le-dropdown-base/le-dropdown-base.js.map +0 -1
  69. package/dist/collection/components/le-header/le-header.css +0 -120
  70. package/dist/collection/components/le-header/le-header.js +0 -508
  71. package/dist/collection/components/le-header/le-header.js.map +0 -1
  72. package/dist/collection/components/le-header-placeholder/le-header-placeholder.js +0 -21
  73. package/dist/collection/components/le-header-placeholder/le-header-placeholder.js.map +0 -1
  74. package/dist/collection/components/le-icon/le-icon.css +0 -13
  75. package/dist/collection/components/le-icon/le-icon.js +0 -185
  76. package/dist/collection/components/le-icon/le-icon.js.map +0 -1
  77. package/dist/collection/components/le-multiselect/le-multiselect.css +0 -163
  78. package/dist/collection/components/le-multiselect/le-multiselect.js +0 -734
  79. package/dist/collection/components/le-multiselect/le-multiselect.js.map +0 -1
  80. package/dist/collection/components/le-navigation/le-navigation.css +0 -330
  81. package/dist/collection/components/le-navigation/le-navigation.js +0 -700
  82. package/dist/collection/components/le-navigation/le-navigation.js.map +0 -1
  83. package/dist/collection/components/le-number-input/le-number-input.css +0 -135
  84. package/dist/collection/components/le-number-input/le-number-input.js +0 -515
  85. package/dist/collection/components/le-number-input/le-number-input.js.map +0 -1
  86. package/dist/collection/components/le-popover/le-popover.css +0 -163
  87. package/dist/collection/components/le-popover/le-popover.js +0 -853
  88. package/dist/collection/components/le-popover/le-popover.js.map +0 -1
  89. package/dist/collection/components/le-popup/le-popup.api.js +0 -102
  90. package/dist/collection/components/le-popup/le-popup.api.js.map +0 -1
  91. package/dist/collection/components/le-popup/le-popup.css +0 -222
  92. package/dist/collection/components/le-popup/le-popup.js +0 -626
  93. package/dist/collection/components/le-popup/le-popup.js.map +0 -1
  94. package/dist/collection/components/le-round-progress/le-round-progress.css +0 -34
  95. package/dist/collection/components/le-round-progress/le-round-progress.js +0 -184
  96. package/dist/collection/components/le-round-progress/le-round-progress.js.map +0 -1
  97. package/dist/collection/components/le-scroll-progress/le-scroll-progress.css +0 -29
  98. package/dist/collection/components/le-scroll-progress/le-scroll-progress.js +0 -186
  99. package/dist/collection/components/le-scroll-progress/le-scroll-progress.js.map +0 -1
  100. package/dist/collection/components/le-segmented-control/le-segmented-control.css +0 -78
  101. package/dist/collection/components/le-segmented-control/le-segmented-control.js +0 -445
  102. package/dist/collection/components/le-segmented-control/le-segmented-control.js.map +0 -1
  103. package/dist/collection/components/le-select/le-select.css +0 -121
  104. package/dist/collection/components/le-select/le-select.js +0 -578
  105. package/dist/collection/components/le-select/le-select.js.map +0 -1
  106. package/dist/collection/components/le-side-panel/le-side-panel.css +0 -193
  107. package/dist/collection/components/le-side-panel/le-side-panel.js +0 -953
  108. package/dist/collection/components/le-side-panel/le-side-panel.js.map +0 -1
  109. package/dist/collection/components/le-side-panel-toggle/le-side-panel-toggle.js +0 -610
  110. package/dist/collection/components/le-side-panel-toggle/le-side-panel-toggle.js.map +0 -1
  111. package/dist/collection/components/le-slot/le-slot.default.css +0 -222
  112. package/dist/collection/components/le-slot/le-slot.js +0 -638
  113. package/dist/collection/components/le-slot/le-slot.js.map +0 -1
  114. package/dist/collection/components/le-stack/le-stack.default.css +0 -37
  115. package/dist/collection/components/le-stack/le-stack.js +0 -389
  116. package/dist/collection/components/le-stack/le-stack.js.map +0 -1
  117. package/dist/collection/components/le-string-input/le-string-input.css +0 -83
  118. package/dist/collection/components/le-string-input/le-string-input.js +0 -413
  119. package/dist/collection/components/le-string-input/le-string-input.js.map +0 -1
  120. package/dist/collection/components/le-tab/le-tab.css +0 -289
  121. package/dist/collection/components/le-tab/le-tab.js +0 -565
  122. package/dist/collection/components/le-tab/le-tab.js.map +0 -1
  123. package/dist/collection/components/le-tab-bar/le-tab-bar.css +0 -89
  124. package/dist/collection/components/le-tab-bar/le-tab-bar.js +0 -467
  125. package/dist/collection/components/le-tab-bar/le-tab-bar.js.map +0 -1
  126. package/dist/collection/components/le-tab-panel/le-tab-panel.css +0 -30
  127. package/dist/collection/components/le-tab-panel/le-tab-panel.js +0 -302
  128. package/dist/collection/components/le-tab-panel/le-tab-panel.js.map +0 -1
  129. package/dist/collection/components/le-tabs/le-tabs.css +0 -146
  130. package/dist/collection/components/le-tabs/le-tabs.js +0 -588
  131. package/dist/collection/components/le-tabs/le-tabs.js.map +0 -1
  132. package/dist/collection/components/le-tag/le-tag.css +0 -139
  133. package/dist/collection/components/le-tag/le-tag.js +0 -266
  134. package/dist/collection/components/le-tag/le-tag.js.map +0 -1
  135. package/dist/collection/components/le-text/le-text.css +0 -169
  136. package/dist/collection/components/le-text/le-text.js +0 -475
  137. package/dist/collection/components/le-text/le-text.js.map +0 -1
  138. package/dist/collection/components/le-turntable/le-turntable.css +0 -10
  139. package/dist/collection/components/le-turntable/le-turntable.js +0 -210
  140. package/dist/collection/components/le-turntable/le-turntable.js.map +0 -1
  141. package/dist/collection/dist/components/assets/custom-elements.json +0 -11935
  142. package/dist/collection/dist/components/assets/icons/chevron-down.json +0 -13
  143. package/dist/collection/dist/components/assets/icons/chevron-down.svg +0 -3
  144. package/dist/collection/dist/components/assets/icons/ellipsis-horizontal.json +0 -14
  145. package/dist/collection/dist/components/assets/icons/ellipsis-vertical.json +0 -14
  146. package/dist/collection/dist/components/assets/icons/hamburger.json +0 -14
  147. package/dist/collection/dist/components/assets/icons/side-panel.json +0 -61
  148. package/dist/collection/dist/themes/base.css +0 -89
  149. package/dist/collection/dist/themes/dark.css +0 -104
  150. package/dist/collection/dist/themes/default.css +0 -112
  151. package/dist/collection/dist/themes/gradient.css +0 -104
  152. package/dist/collection/dist/themes/index.css +0 -76
  153. package/dist/collection/dist/themes/minimal.css +0 -104
  154. package/dist/collection/dist/themes/warm.css +0 -104
  155. package/dist/collection/global/app.js +0 -177
  156. package/dist/collection/global/app.js.map +0 -1
  157. package/dist/collection/index.js +0 -16
  158. package/dist/collection/index.js.map +0 -1
  159. package/dist/collection/types/blocks.js +0 -115
  160. package/dist/collection/types/blocks.js.map +0 -1
  161. package/dist/collection/types/options.js +0 -2
  162. package/dist/collection/types/options.js.map +0 -1
  163. package/dist/collection/utils/utils.js +0 -141
  164. package/dist/collection/utils/utils.js.map +0 -1
  165. package/dist/components/app.js +0 -180
  166. package/dist/components/app.js.map +0 -1
  167. package/dist/components/assets/custom-elements.json +0 -11935
  168. package/dist/components/assets/icons/chevron-down.json +0 -13
  169. package/dist/components/assets/icons/chevron-down.svg +0 -3
  170. package/dist/components/assets/icons/ellipsis-horizontal.json +0 -14
  171. package/dist/components/assets/icons/ellipsis-vertical.json +0 -14
  172. package/dist/components/assets/icons/hamburger.json +0 -14
  173. package/dist/components/assets/icons/side-panel.json +0 -61
  174. package/dist/components/index.d.ts +0 -33
  175. package/dist/components/index.js +0 -12
  176. package/dist/components/index.js.map +0 -1
  177. package/dist/components/le-bar.d.ts +0 -11
  178. package/dist/components/le-bar.js +0 -9
  179. package/dist/components/le-bar.js.map +0 -1
  180. package/dist/components/le-bar2.js +0 -506
  181. package/dist/components/le-bar2.js.map +0 -1
  182. package/dist/components/le-box.d.ts +0 -11
  183. package/dist/components/le-box.js +0 -273
  184. package/dist/components/le-box.js.map +0 -1
  185. package/dist/components/le-button.d.ts +0 -11
  186. package/dist/components/le-button.js +0 -9
  187. package/dist/components/le-button.js.map +0 -1
  188. package/dist/components/le-button2.js +0 -1791
  189. package/dist/components/le-button2.js.map +0 -1
  190. package/dist/components/le-card.d.ts +0 -11
  191. package/dist/components/le-card.js +0 -100
  192. package/dist/components/le-card.js.map +0 -1
  193. package/dist/components/le-checkbox.d.ts +0 -11
  194. package/dist/components/le-checkbox.js +0 -9
  195. package/dist/components/le-checkbox.js.map +0 -1
  196. package/dist/components/le-code-input.d.ts +0 -11
  197. package/dist/components/le-code-input.js +0 -265
  198. package/dist/components/le-code-input.js.map +0 -1
  199. package/dist/components/le-collapse.d.ts +0 -11
  200. package/dist/components/le-collapse.js +0 -9
  201. package/dist/components/le-collapse.js.map +0 -1
  202. package/dist/components/le-collapse2.js +0 -138
  203. package/dist/components/le-collapse2.js.map +0 -1
  204. package/dist/components/le-combobox.d.ts +0 -11
  205. package/dist/components/le-combobox.js +0 -326
  206. package/dist/components/le-combobox.js.map +0 -1
  207. package/dist/components/le-component.d.ts +0 -11
  208. package/dist/components/le-component.js +0 -9
  209. package/dist/components/le-component.js.map +0 -1
  210. package/dist/components/le-current-heading.d.ts +0 -11
  211. package/dist/components/le-current-heading.js +0 -93
  212. package/dist/components/le-current-heading.js.map +0 -1
  213. package/dist/components/le-dropdown-base.d.ts +0 -11
  214. package/dist/components/le-dropdown-base.js +0 -9
  215. package/dist/components/le-dropdown-base.js.map +0 -1
  216. package/dist/components/le-dropdown-base2.js +0 -393
  217. package/dist/components/le-dropdown-base2.js.map +0 -1
  218. package/dist/components/le-header-placeholder.d.ts +0 -11
  219. package/dist/components/le-header-placeholder.js +0 -37
  220. package/dist/components/le-header-placeholder.js.map +0 -1
  221. package/dist/components/le-header.d.ts +0 -11
  222. package/dist/components/le-header.js +0 -349
  223. package/dist/components/le-header.js.map +0 -1
  224. package/dist/components/le-icon.d.ts +0 -11
  225. package/dist/components/le-icon.js +0 -9
  226. package/dist/components/le-icon.js.map +0 -1
  227. package/dist/components/le-icon2.js +0 -150
  228. package/dist/components/le-icon2.js.map +0 -1
  229. package/dist/components/le-multiselect.d.ts +0 -11
  230. package/dist/components/le-multiselect.js +0 -405
  231. package/dist/components/le-multiselect.js.map +0 -1
  232. package/dist/components/le-navigation.d.ts +0 -11
  233. package/dist/components/le-navigation.js +0 -502
  234. package/dist/components/le-navigation.js.map +0 -1
  235. package/dist/components/le-number-input.d.ts +0 -11
  236. package/dist/components/le-number-input.js +0 -288
  237. package/dist/components/le-number-input.js.map +0 -1
  238. package/dist/components/le-popover.d.ts +0 -11
  239. package/dist/components/le-popover.js +0 -9
  240. package/dist/components/le-popover.js.map +0 -1
  241. package/dist/components/le-popover2.js +0 -667
  242. package/dist/components/le-popover2.js.map +0 -1
  243. package/dist/components/le-popup.api.js +0 -106
  244. package/dist/components/le-popup.api.js.map +0 -1
  245. package/dist/components/le-popup.d.ts +0 -11
  246. package/dist/components/le-popup.js +0 -9
  247. package/dist/components/le-popup.js.map +0 -1
  248. package/dist/components/le-round-progress.d.ts +0 -11
  249. package/dist/components/le-round-progress.js +0 -135
  250. package/dist/components/le-round-progress.js.map +0 -1
  251. package/dist/components/le-scroll-progress.d.ts +0 -11
  252. package/dist/components/le-scroll-progress.js +0 -142
  253. package/dist/components/le-scroll-progress.js.map +0 -1
  254. package/dist/components/le-segmented-control.d.ts +0 -11
  255. package/dist/components/le-segmented-control.js +0 -331
  256. package/dist/components/le-segmented-control.js.map +0 -1
  257. package/dist/components/le-select.d.ts +0 -11
  258. package/dist/components/le-select.js +0 -9
  259. package/dist/components/le-select.js.map +0 -1
  260. package/dist/components/le-side-panel-toggle.d.ts +0 -11
  261. package/dist/components/le-side-panel-toggle.js +0 -9
  262. package/dist/components/le-side-panel-toggle.js.map +0 -1
  263. package/dist/components/le-side-panel-toggle2.js +0 -311
  264. package/dist/components/le-side-panel-toggle2.js.map +0 -1
  265. package/dist/components/le-side-panel.d.ts +0 -11
  266. package/dist/components/le-side-panel.js +0 -660
  267. package/dist/components/le-side-panel.js.map +0 -1
  268. package/dist/components/le-slot.d.ts +0 -11
  269. package/dist/components/le-slot.js +0 -9
  270. package/dist/components/le-slot.js.map +0 -1
  271. package/dist/components/le-stack.d.ts +0 -11
  272. package/dist/components/le-stack.js +0 -215
  273. package/dist/components/le-stack.js.map +0 -1
  274. package/dist/components/le-string-input.d.ts +0 -11
  275. package/dist/components/le-string-input.js +0 -9
  276. package/dist/components/le-string-input.js.map +0 -1
  277. package/dist/components/le-tab-bar.d.ts +0 -11
  278. package/dist/components/le-tab-bar.js +0 -330
  279. package/dist/components/le-tab-bar.js.map +0 -1
  280. package/dist/components/le-tab-panel.d.ts +0 -11
  281. package/dist/components/le-tab-panel.js +0 -182
  282. package/dist/components/le-tab-panel.js.map +0 -1
  283. package/dist/components/le-tab.d.ts +0 -11
  284. package/dist/components/le-tab.js +0 -9
  285. package/dist/components/le-tab.js.map +0 -1
  286. package/dist/components/le-tab2.js +0 -220
  287. package/dist/components/le-tab2.js.map +0 -1
  288. package/dist/components/le-tabs.d.ts +0 -11
  289. package/dist/components/le-tabs.js +0 -397
  290. package/dist/components/le-tabs.js.map +0 -1
  291. package/dist/components/le-tag.d.ts +0 -11
  292. package/dist/components/le-tag.js +0 -9
  293. package/dist/components/le-tag.js.map +0 -1
  294. package/dist/components/le-tag2.js +0 -141
  295. package/dist/components/le-tag2.js.map +0 -1
  296. package/dist/components/le-text.d.ts +0 -11
  297. package/dist/components/le-text.js +0 -415
  298. package/dist/components/le-text.js.map +0 -1
  299. package/dist/components/le-turntable.d.ts +0 -11
  300. package/dist/components/le-turntable.js +0 -164
  301. package/dist/components/le-turntable.js.map +0 -1
  302. package/dist/components/utils.js +0 -146
  303. package/dist/components/utils.js.map +0 -1
  304. package/dist/docs.d.ts +0 -443
  305. package/dist/docs.json +0 -14289
  306. package/dist/esm/index-DFTm5BqT.js +0 -1844
  307. package/dist/esm/index-DFTm5BqT.js.map +0 -1
  308. package/dist/esm/index.js +0 -109
  309. package/dist/esm/index.js.map +0 -1
  310. package/dist/esm/le-bar_16.entry.js +0 -3692
  311. package/dist/esm/le-box.entry.js +0 -182
  312. package/dist/esm/le-box.entry.js.map +0 -1
  313. package/dist/esm/le-card.entry.js +0 -27
  314. package/dist/esm/le-card.entry.js.map +0 -1
  315. package/dist/esm/le-code-input.entry.js +0 -179
  316. package/dist/esm/le-code-input.entry.js.map +0 -1
  317. package/dist/esm/le-combobox.entry.js +0 -235
  318. package/dist/esm/le-combobox.entry.js.map +0 -1
  319. package/dist/esm/le-header-placeholder.entry.js +0 -16
  320. package/dist/esm/le-header-placeholder.entry.js.map +0 -1
  321. package/dist/esm/le-kit.js +0 -23
  322. package/dist/esm/le-kit.js.map +0 -1
  323. package/dist/esm/le-multiselect.entry.js +0 -304
  324. package/dist/esm/le-multiselect.entry.js.map +0 -1
  325. package/dist/esm/le-number-input.entry.js +0 -200
  326. package/dist/esm/le-number-input.entry.js.map +0 -1
  327. package/dist/esm/le-round-progress.entry.js +0 -104
  328. package/dist/esm/le-round-progress.entry.js.map +0 -1
  329. package/dist/esm/le-segmented-control.entry.js +0 -243
  330. package/dist/esm/le-segmented-control.entry.js.map +0 -1
  331. package/dist/esm/le-side-panel-toggle.entry.js +0 -227
  332. package/dist/esm/le-side-panel-toggle.entry.js.map +0 -1
  333. package/dist/esm/le-side-panel.entry.js +0 -544
  334. package/dist/esm/le-side-panel.entry.js.map +0 -1
  335. package/dist/esm/le-stack.entry.js +0 -133
  336. package/dist/esm/le-stack.entry.js.map +0 -1
  337. package/dist/esm/le-tab-bar.entry.js +0 -240
  338. package/dist/esm/le-tab-bar.entry.js.map +0 -1
  339. package/dist/esm/le-tab-panel.entry.js +0 -98
  340. package/dist/esm/le-tab-panel.entry.js.map +0 -1
  341. package/dist/esm/le-tab.entry.js +0 -134
  342. package/dist/esm/le-tab.entry.js.map +0 -1
  343. package/dist/esm/le-tabs.entry.js +0 -305
  344. package/dist/esm/le-tabs.entry.js.map +0 -1
  345. package/dist/esm/le-tag.entry.js +0 -66
  346. package/dist/esm/le-tag.entry.js.map +0 -1
  347. package/dist/esm/le-text.entry.js +0 -333
  348. package/dist/esm/le-text.entry.js.map +0 -1
  349. package/dist/esm/le-turntable.entry.js +0 -137
  350. package/dist/esm/le-turntable.entry.js.map +0 -1
  351. package/dist/esm/loader.js +0 -13
  352. package/dist/esm/loader.js.map +0 -1
  353. package/dist/esm/utils-DZYCZLrF.js +0 -146
  354. package/dist/esm/utils-DZYCZLrF.js.map +0 -1
  355. package/dist/index.cjs.js +0 -1
  356. package/dist/index.js +0 -1
  357. package/dist/le-kit/dist/components/assets/custom-elements.json +0 -11935
  358. package/dist/le-kit/dist/components/assets/icons/chevron-down.json +0 -13
  359. package/dist/le-kit/dist/components/assets/icons/chevron-down.svg +0 -3
  360. package/dist/le-kit/dist/components/assets/icons/ellipsis-horizontal.json +0 -14
  361. package/dist/le-kit/dist/components/assets/icons/ellipsis-vertical.json +0 -14
  362. package/dist/le-kit/dist/components/assets/icons/hamburger.json +0 -14
  363. package/dist/le-kit/dist/components/assets/icons/side-panel.json +0 -61
  364. package/dist/le-kit/dist/themes/base.css +0 -89
  365. package/dist/le-kit/dist/themes/dark.css +0 -104
  366. package/dist/le-kit/dist/themes/default.css +0 -112
  367. package/dist/le-kit/dist/themes/gradient.css +0 -104
  368. package/dist/le-kit/dist/themes/index.css +0 -76
  369. package/dist/le-kit/dist/themes/minimal.css +0 -104
  370. package/dist/le-kit/dist/themes/warm.css +0 -104
  371. package/dist/le-kit/index.esm.js +0 -2
  372. package/dist/le-kit/index.esm.js.map +0 -1
  373. package/dist/le-kit/le-kit.css +0 -1
  374. package/dist/le-kit/le-kit.esm.js +0 -2
  375. package/dist/le-kit/le-kit.esm.js.map +0 -1
  376. package/dist/le-kit/loader.esm.js.map +0 -1
  377. package/dist/le-kit/p-221d379a.entry.js +0 -2
  378. package/dist/le-kit/p-221d379a.entry.js.map +0 -1
  379. package/dist/le-kit/p-24112ca3.entry.js +0 -2
  380. package/dist/le-kit/p-24112ca3.entry.js.map +0 -1
  381. package/dist/le-kit/p-2c6d080d.entry.js +0 -2
  382. package/dist/le-kit/p-2c6d080d.entry.js.map +0 -1
  383. package/dist/le-kit/p-46276e77.entry.js +0 -2
  384. package/dist/le-kit/p-46276e77.entry.js.map +0 -1
  385. package/dist/le-kit/p-516c8531.entry.js +0 -2
  386. package/dist/le-kit/p-516c8531.entry.js.map +0 -1
  387. package/dist/le-kit/p-6ae60ba5.entry.js +0 -2
  388. package/dist/le-kit/p-6ae60ba5.entry.js.map +0 -1
  389. package/dist/le-kit/p-6b69f9a2.entry.js +0 -2
  390. package/dist/le-kit/p-6b69f9a2.entry.js.map +0 -1
  391. package/dist/le-kit/p-6d14306f.entry.js +0 -2
  392. package/dist/le-kit/p-6d14306f.entry.js.map +0 -1
  393. package/dist/le-kit/p-7bcdf2d4.entry.js +0 -2
  394. package/dist/le-kit/p-7bcdf2d4.entry.js.map +0 -1
  395. package/dist/le-kit/p-7cf1e23c.entry.js +0 -2
  396. package/dist/le-kit/p-7cf1e23c.entry.js.map +0 -1
  397. package/dist/le-kit/p-85f2fd4d.entry.js +0 -2
  398. package/dist/le-kit/p-85f2fd4d.entry.js.map +0 -1
  399. package/dist/le-kit/p-98242429.entry.js +0 -2
  400. package/dist/le-kit/p-98242429.entry.js.map +0 -1
  401. package/dist/le-kit/p-D8RYDS9p.js +0 -2
  402. package/dist/le-kit/p-D8RYDS9p.js.map +0 -1
  403. package/dist/le-kit/p-DFTm5BqT.js +0 -3
  404. package/dist/le-kit/p-DFTm5BqT.js.map +0 -1
  405. package/dist/le-kit/p-ab6c1def.entry.js +0 -2
  406. package/dist/le-kit/p-ab6c1def.entry.js.map +0 -1
  407. package/dist/le-kit/p-ae4ead64.entry.js +0 -2
  408. package/dist/le-kit/p-ae4ead64.entry.js.map +0 -1
  409. package/dist/le-kit/p-b05d4511.entry.js +0 -2
  410. package/dist/le-kit/p-b05d4511.entry.js.map +0 -1
  411. package/dist/le-kit/p-b6ac02ff.entry.js +0 -2
  412. package/dist/le-kit/p-b6ac02ff.entry.js.map +0 -1
  413. package/dist/le-kit/p-c24769e2.entry.js +0 -2
  414. package/dist/le-kit/p-c24769e2.entry.js.map +0 -1
  415. package/dist/le-kit/p-dc0445ad.entry.js +0 -2
  416. package/dist/le-kit/p-dc0445ad.entry.js.map +0 -1
  417. package/dist/le-kit/p-eb5286f2.entry.js +0 -2
  418. package/dist/le-kit/p-eb5286f2.entry.js.map +0 -1
  419. package/dist/le-kit/p-f78b1ee6.entry.js +0 -2
  420. package/dist/le-kit/p-f78b1ee6.entry.js.map +0 -1
  421. package/dist/themes/base.css +0 -89
  422. package/dist/themes/dark.css +0 -104
  423. package/dist/themes/default.css +0 -112
  424. package/dist/themes/gradient.css +0 -104
  425. package/dist/themes/index.css +0 -76
  426. package/dist/themes/minimal.css +0 -104
  427. package/dist/themes/warm.css +0 -104
  428. package/dist/types/components/le-bar/le-bar.d.ts +0 -131
  429. package/dist/types/components/le-box/le-box.d.ts +0 -111
  430. package/dist/types/components/le-button/le-button.d.ts +0 -93
  431. package/dist/types/components/le-card/le-card.d.ts +0 -37
  432. package/dist/types/components/le-checkbox/le-checkbox.d.ts +0 -46
  433. package/dist/types/components/le-code-input/le-code-input.d.ts +0 -102
  434. package/dist/types/components/le-collapse/le-collapse.d.ts +0 -43
  435. package/dist/types/components/le-combobox/le-combobox.d.ts +0 -128
  436. package/dist/types/components/le-component/le-component.d.ts +0 -115
  437. package/dist/types/components/le-current-heading/le-current-heading.d.ts +0 -25
  438. package/dist/types/components/le-dropdown-base/le-dropdown-base.d.ts +0 -118
  439. package/dist/types/components/le-header/le-header.d.ts +0 -115
  440. package/dist/types/components/le-header-placeholder/le-header-placeholder.d.ts +0 -13
  441. package/dist/types/components/le-icon/le-icon.d.ts +0 -28
  442. package/dist/types/components/le-multiselect/le-multiselect.d.ts +0 -143
  443. package/dist/types/components/le-navigation/le-navigation.d.ts +0 -125
  444. package/dist/types/components/le-number-input/le-number-input.d.ts +0 -106
  445. package/dist/types/components/le-popover/le-popover.d.ts +0 -129
  446. package/dist/types/components/le-popup/le-popup.api.d.ts +0 -73
  447. package/dist/types/components/le-popup/le-popup.d.ts +0 -127
  448. package/dist/types/components/le-round-progress/le-round-progress.d.ts +0 -37
  449. package/dist/types/components/le-scroll-progress/le-scroll-progress.d.ts +0 -40
  450. package/dist/types/components/le-segmented-control/le-segmented-control.d.ts +0 -82
  451. package/dist/types/components/le-select/le-select.d.ts +0 -125
  452. package/dist/types/components/le-side-panel/le-side-panel.d.ts +0 -100
  453. package/dist/types/components/le-side-panel-toggle/le-side-panel-toggle.d.ts +0 -48
  454. package/dist/types/components/le-slot/le-slot.d.ts +0 -149
  455. package/dist/types/components/le-stack/le-stack.d.ts +0 -73
  456. package/dist/types/components/le-string-input/le-string-input.d.ts +0 -91
  457. package/dist/types/components/le-tab/le-tab.d.ts +0 -116
  458. package/dist/types/components/le-tab-bar/le-tab-bar.d.ts +0 -88
  459. package/dist/types/components/le-tab-panel/le-tab-panel.d.ts +0 -75
  460. package/dist/types/components/le-tabs/le-tabs.d.ts +0 -108
  461. package/dist/types/components/le-tag/le-tag.d.ts +0 -78
  462. package/dist/types/components/le-text/le-text.d.ts +0 -141
  463. package/dist/types/components/le-turntable/le-turntable.d.ts +0 -55
  464. package/dist/types/components.d.ts +0 -5716
  465. package/dist/types/global/app.d.ts +0 -96
  466. package/dist/types/index.d.ts +0 -16
  467. package/dist/types/stencil-public-runtime.d.ts +0 -1799
  468. package/dist/types/types/blocks.d.ts +0 -136
  469. package/dist/types/types/options.d.ts +0 -143
  470. package/dist/types/utils/utils.d.ts +0 -54
  471. package/loader/cdn.js +0 -1
  472. package/loader/index.cjs.js +0 -1
  473. package/loader/index.d.ts +0 -24
  474. package/loader/index.es2017.js +0 -1
  475. package/loader/index.js +0 -2
@@ -1,3709 +0,0 @@
1
- 'use strict';
2
-
3
- var index = require('./index-BzadfLTc.js');
4
- var utils = require('./utils-Dxx9WhWK.js');
5
- var index$1 = require('./index.cjs.js');
6
-
7
- const leBarCss = () => `:host{display:block;--le-bar-gap:var(--le-spacing-2);--le-bar-padding:var(--le-spacing-2);--le-bar-min-height:2.5rem;--le-bar-background:transparent;--le-bar-border-color:var(--le-color-border);--le-bar-border-radius:var(--le-radius-md);--le-bar-button-size:2rem;--le-bar-button-color:var(--le-color-text-primary);--le-bar-button-hover-bg:var(--le-color-gray-100);--le-bar-button-border-radius:var(--le-radius-sm);--le-bar-arrow-size:1.5rem;--le-bar-popover-min-width:200px;--le-bar-popover-gap:var(--le-spacing-1)}.bar-container{display:flex;align-items:center;gap:var(--le-bar-gap);min-height:var(--le-bar-min-height);background:var(--le-bar-background);border-radius:var(--le-bar-border-radius);padding:var(--le-bar-padding)}.bar-container.align-start{justify-content:flex-start}.bar-container.align-end .bar-items{justify-content:flex-end}.bar-container.align-center .bar-items{justify-content:center}.bar-container.align-stretch .bar-items{justify-content:space-evenly}.bar-items{display:flex;flex-wrap:wrap;align-items:center;gap:var(--le-bar-gap);overflow:hidden;min-width:0;flex:1 1 0%;position:relative}.bar-items[style*="height"]{overflow:hidden}.bar-items.is-scrollable{flex-wrap:nowrap;overflow-x:auto;overflow-y:hidden;scroll-behavior:smooth;scroll-snap-type:x mandatory;-webkit-overflow-scrolling:touch;scrollbar-width:none;-ms-overflow-style:none}.bar-items.is-scrollable::-webkit-scrollbar{display:none}.bar-items.is-scrollable ::slotted(*){scroll-snap-align:start;flex-shrink:0}.bar-items.is-wrapping{flex-wrap:wrap;overflow:visible;height:auto !important}.bar-controls{display:flex;align-items:center;gap:var(--le-spacing-1);flex-shrink:0}.bar-controls-start{order:-1}.bar-controls-end{order:1}.bar-more-button,.bar-hamburger-button,.bar-all-menu-button{display:inline-flex;align-items:center;justify-content:center;width:var(--le-bar-button-size);height:var(--le-bar-button-size);padding:0;border:none;background:transparent;color:var(--le-bar-button-color);border-radius:var(--le-bar-button-border-radius);cursor:pointer;transition:background-color 0.15s ease}.bar-more-button:hover,.bar-hamburger-button:hover,.bar-all-menu-button:hover{background:var(--le-bar-button-hover-bg)}.bar-more-button:focus-visible,.bar-hamburger-button:focus-visible,.bar-all-menu-button:focus-visible{outline:2px solid var(--le-color-focus);outline-offset:2px}.bar-arrow{display:inline-flex;align-items:center;justify-content:center;width:var(--le-bar-arrow-size);height:var(--le-bar-arrow-size);padding:0;border:none;background:transparent;color:var(--le-bar-button-color);border-radius:var(--le-bar-button-border-radius);cursor:pointer;transition:background-color 0.15s ease, opacity 0.15s ease}.bar-arrow:hover:not(:disabled){background:var(--le-bar-button-hover-bg)}.bar-arrow:focus-visible{outline:2px solid var(--le-color-focus);outline-offset:2px}.bar-arrow:disabled,.bar-arrow.disabled{opacity:0.3;cursor:not-allowed}.arrow-icon-start{transform:rotate(90deg)}.arrow-icon-end{transform:rotate(-90deg)}.bar-overflow-trigger,.bar-all-menu-trigger{display:inline-flex}.bar-popover-content{display:flex;flex-direction:column;gap:var(--le-bar-popover-gap);min-width:var(--le-bar-popover-min-width);padding:var(--le-spacing-2)}.bar-popover-item{cursor:pointer}.bar-popover-item:hover{background:var(--le-bar-button-hover-bg);border-radius:var(--le-bar-button-border-radius)}:host(.hamburger-active) .bar-items{visibility:hidden;pointer-events:none}`;
8
-
9
- const LeBar = class {
10
- constructor(hostRef) {
11
- index.registerInstance(this, hostRef);
12
- this.leBarOverflowChange = index.createEvent(this, "leBarOverflowChange");
13
- }
14
- get el() { return index.getElement(this); }
15
- /**
16
- * Overflow behavior when items don't fit on one row.
17
- * - `more`: Overflow items appear in a "more" dropdown
18
- * - `scroll`: Items scroll horizontally with optional arrows
19
- * - `hamburger`: All items go into a hamburger menu if any overflow
20
- * - `wrap`: Items wrap to additional rows
21
- */
22
- overflow = 'more';
23
- /**
24
- * Alignment of items within the bar (maps to justify-content).
25
- */
26
- alignItems = 'start';
27
- /**
28
- * Show scroll arrows when overflow is "scroll".
29
- */
30
- arrows = false;
31
- /**
32
- * Disable the internal overflow popover.
33
- * When true, the bar still detects overflow and hides items,
34
- * but doesn't render its own popover. Use this when providing
35
- * custom overflow handling via the leBarOverflowChange event.
36
- */
37
- disablePopover = false;
38
- /**
39
- * Minimum number of visible items required when using "more" overflow mode.
40
- * If fewer items would be visible, the bar falls back to hamburger mode.
41
- * Only applies when overflow is "more".
42
- */
43
- minVisibleItems = 0;
44
- /**
45
- * Show an "all items" menu button.
46
- * - `false`: Don't show
47
- * - `true` or `'end'`: Show at end
48
- * - `'start'`: Show at start
49
- */
50
- showAllMenu = false;
51
- /**
52
- * Emitted when overflow state changes.
53
- */
54
- leBarOverflowChange;
55
- /** Whether the hamburger/more popover is open */
56
- popoverOpen = false;
57
- /** Whether hamburger mode is active (for hamburger overflow) */
58
- hamburgerActive = false;
59
- /** IDs of items that are overflowing */
60
- overflowingIds = new Set();
61
- /** Whether we can scroll left */
62
- canScrollStart = false;
63
- /** Whether we can scroll right */
64
- canScrollEnd = false;
65
- /** Whether the all-menu popover is open */
66
- allMenuOpen = false;
67
- /** Current height of the items container (for overflow handling) */
68
- containerHeight = null;
69
- itemsContainerEl;
70
- moreButtonEl;
71
- resizeObserver;
72
- mutationObserver;
73
- instanceId = utils.generateId('le-bar');
74
- // Map to track item elements and their IDs
75
- itemMap = new Map();
76
- // Prevent multiple recalculations in the same frame
77
- pendingRecalc = null;
78
- handleOverflowChange() {
79
- this.resetOverflowState();
80
- this.scheduleOverflowRecalc();
81
- }
82
- handleSlotChange() {
83
- this.scheduleOverflowRecalc();
84
- }
85
- connectedCallback() {
86
- this.setupObservers();
87
- }
88
- componentDidLoad() {
89
- this.scheduleOverflowRecalc();
90
- }
91
- componentDidRender() {
92
- // Recalculate after render in case children changed
93
- this.scheduleOverflowRecalc();
94
- }
95
- disconnectedCallback() {
96
- this.resizeObserver?.disconnect();
97
- this.mutationObserver?.disconnect();
98
- }
99
- /**
100
- * Force recalculation of overflow state.
101
- */
102
- async recalculate() {
103
- this.computeOverflow();
104
- }
105
- setupObservers() {
106
- // ResizeObserver for container size changes
107
- if (typeof ResizeObserver !== 'undefined') {
108
- this.resizeObserver = new ResizeObserver(() => {
109
- this.computeOverflow();
110
- this.updateScrollState();
111
- });
112
- }
113
- // MutationObserver for child changes
114
- this.mutationObserver = new MutationObserver(() => {
115
- this.scheduleOverflowRecalc();
116
- });
117
- this.mutationObserver.observe(this.el, {
118
- childList: true,
119
- subtree: false,
120
- });
121
- }
122
- observeContainer(el) {
123
- if (!this.resizeObserver)
124
- return;
125
- this.resizeObserver.disconnect();
126
- if (el)
127
- this.resizeObserver.observe(el);
128
- }
129
- scheduleOverflowRecalc() {
130
- // Debounce recalculations to prevent infinite loops
131
- if (this.pendingRecalc !== null) {
132
- cancelAnimationFrame(this.pendingRecalc);
133
- }
134
- this.pendingRecalc = requestAnimationFrame(() => {
135
- this.pendingRecalc = null;
136
- this.computeOverflow();
137
- });
138
- }
139
- resetOverflowState() {
140
- this.hamburgerActive = false;
141
- this.overflowingIds = new Set();
142
- this.containerHeight = null;
143
- this.popoverOpen = false;
144
- }
145
- getSlottedItems() {
146
- // Get direct children from the light DOM (excluding named slot elements)
147
- return Array.from(this.el.children).filter((el) => el instanceof HTMLElement && !el.hasAttribute('slot'));
148
- }
149
- getItemId(el, index) {
150
- return el.id || el.dataset.barId || `${this.instanceId}-item-${index}`;
151
- }
152
- computeOverflow() {
153
- if (this.overflow === 'wrap' || this.overflow === 'scroll') {
154
- // No overflow handling needed for wrap/scroll modes
155
- this.resetOverflowState();
156
- this.updateScrollState();
157
- return;
158
- }
159
- const container = this.itemsContainerEl;
160
- if (!container)
161
- return;
162
- const items = this.getSlottedItems();
163
- if (items.length === 0) {
164
- this.resetOverflowState();
165
- return;
166
- }
167
- // Build item map
168
- this.itemMap.clear();
169
- items.forEach((item, index) => {
170
- const id = this.getItemId(item, index);
171
- this.itemMap.set(id, item);
172
- });
173
- // Get the position of items to determine which are on the first row
174
- const itemRects = items.map((item, index) => ({
175
- item,
176
- id: this.getItemId(item, index),
177
- rect: item.getBoundingClientRect(),
178
- }));
179
- if (itemRects.length === 0) {
180
- return;
181
- }
182
- // Find the first row's top position (accounting for vertical alignment)
183
- const containerRect = container.getBoundingClientRect();
184
- const topValues = itemRects.map(i => i.rect.top - containerRect.top);
185
- const minTop = Math.min(...topValues);
186
- // Items are on the first row if their top is close to minTop
187
- // Allow some tolerance for alignment differences
188
- const tolerance = 15;
189
- const firstRowItems = itemRects.filter(i => i.rect.top - containerRect.top <= minTop + tolerance);
190
- const overflowItems = itemRects.filter(i => i.rect.top - containerRect.top > minTop + tolerance);
191
- // Calculate the height of the first row
192
- const firstRowBottom = firstRowItems.length > 0
193
- ? Math.max(...firstRowItems.map(i => i.rect.bottom - containerRect.top))
194
- : 0;
195
- if (this.overflow === 'hamburger') {
196
- // In hamburger mode, if ANY item overflows, all go into the menu
197
- const shouldHamburger = overflowItems.length > 0;
198
- if (shouldHamburger !== this.hamburgerActive) {
199
- this.hamburgerActive = shouldHamburger;
200
- this.emitOverflowChange();
201
- }
202
- // Set height to show only first row (or hide all if hamburger is active)
203
- if (shouldHamburger && firstRowBottom > 0) {
204
- this.containerHeight = firstRowBottom;
205
- }
206
- else {
207
- this.containerHeight = null;
208
- }
209
- }
210
- else {
211
- // 'more' mode
212
- let newOverflowingIds = new Set(overflowItems.map(i => i.id));
213
- // Check if we need to make room for the "more" button
214
- if (newOverflowingIds.size > 0 && this.moreButtonEl) {
215
- const moreRect = this.moreButtonEl.getBoundingClientRect();
216
- const moreTop = moreRect.top - containerRect.top;
217
- // If "more" button is not on the first row, we need to hide one more item
218
- if (moreTop > minTop + tolerance) {
219
- // Find the last visible item and move it to overflow
220
- const lastVisible = firstRowItems[firstRowItems.length - 1];
221
- if (lastVisible) {
222
- newOverflowingIds.add(lastVisible.id);
223
- }
224
- }
225
- }
226
- // Check if we should fallback to hamburger mode
227
- // This happens when minVisibleItems is set and fewer items would be visible
228
- const visibleCount = items.length - newOverflowingIds.size;
229
- const shouldFallbackToHamburger = this.minVisibleItems > 0 &&
230
- newOverflowingIds.size > 0 &&
231
- visibleCount < this.minVisibleItems;
232
- if (shouldFallbackToHamburger) {
233
- // Switch to hamburger mode - all items go into the menu
234
- if (!this.hamburgerActive) {
235
- this.hamburgerActive = true;
236
- this.overflowingIds = new Set();
237
- this.emitOverflowChange();
238
- }
239
- // Set height to show only first row
240
- if (firstRowBottom > 0) {
241
- this.containerHeight = firstRowBottom;
242
- }
243
- else {
244
- this.containerHeight = null;
245
- }
246
- return;
247
- }
248
- // Not falling back to hamburger - ensure hamburgerActive is false
249
- if (this.hamburgerActive) {
250
- this.hamburgerActive = false;
251
- }
252
- // Check if overflow state changed
253
- const hasChanged = (newOverflowingIds?.size ?? 0) !== (this.overflowingIds?.size ?? 0) ||
254
- ![...newOverflowingIds].every(id => this.overflowingIds?.has(id));
255
- if (hasChanged) {
256
- this.overflowingIds = newOverflowingIds;
257
- this.emitOverflowChange();
258
- }
259
- // Set container height to show only first row
260
- if ((newOverflowingIds?.size ?? 0) > 0 && firstRowBottom > 0) {
261
- this.containerHeight = firstRowBottom;
262
- }
263
- else {
264
- this.containerHeight = null;
265
- }
266
- }
267
- }
268
- emitOverflowChange() {
269
- this.leBarOverflowChange.emit({
270
- overflowingIds: [...(this.overflowingIds ?? [])],
271
- hamburgerActive: this.hamburgerActive,
272
- });
273
- }
274
- updateScrollState() {
275
- if (this.overflow !== 'scroll' || !this.itemsContainerEl) {
276
- this.canScrollStart = false;
277
- this.canScrollEnd = false;
278
- return;
279
- }
280
- const el = this.itemsContainerEl;
281
- this.canScrollStart = el.scrollLeft > 0;
282
- this.canScrollEnd = el.scrollLeft < el.scrollWidth - el.clientWidth - 1;
283
- }
284
- handleScroll = () => {
285
- this.updateScrollState();
286
- };
287
- scrollToStart = () => {
288
- if (!this.itemsContainerEl)
289
- return;
290
- const items = this.getSlottedItems();
291
- const container = this.itemsContainerEl;
292
- // Find the scroll position of the previous item
293
- const currentScroll = container.scrollLeft;
294
- let targetScroll = 0;
295
- for (let i = items.length - 1; i >= 0; i--) {
296
- const item = items[i];
297
- // Calculate item's left edge relative to container's scroll position
298
- const itemLeft = item.offsetLeft - container.offsetLeft;
299
- // If this item starts before current scroll position, scroll to it
300
- if (itemLeft < currentScroll - 1) {
301
- targetScroll = itemLeft;
302
- break;
303
- }
304
- }
305
- container.scrollTo({ left: targetScroll, behavior: 'smooth' });
306
- // Update scroll state after animation
307
- setTimeout(() => this.updateScrollState(), 300);
308
- };
309
- scrollToEnd = () => {
310
- if (!this.itemsContainerEl)
311
- return;
312
- const container = this.itemsContainerEl;
313
- const items = this.getSlottedItems();
314
- if (items.length === 0)
315
- return;
316
- const containerWidth = container.clientWidth;
317
- const currentScroll = container.scrollLeft;
318
- let targetScroll = container.scrollWidth - containerWidth;
319
- for (const item of items) {
320
- // Calculate item's right edge relative to container
321
- const itemLeft = item.offsetLeft - container.offsetLeft;
322
- // If this is the next item to scroll to from left to right then scroll to it
323
- if (itemLeft > currentScroll + 1) {
324
- targetScroll = itemLeft;
325
- break;
326
- }
327
- }
328
- container.scrollTo({ left: Math.max(0, targetScroll), behavior: 'smooth' });
329
- // Update scroll state after animation
330
- setTimeout(() => this.updateScrollState(), 300);
331
- };
332
- togglePopover = () => {
333
- this.popoverOpen = !this.popoverOpen;
334
- };
335
- closePopover = () => {
336
- this.popoverOpen = false;
337
- };
338
- toggleAllMenu = () => {
339
- this.allMenuOpen = !this.allMenuOpen;
340
- };
341
- closeAllMenu = () => {
342
- this.allMenuOpen = false;
343
- };
344
- handleItemClick = (_e, id) => {
345
- // Close popover when an item inside is clicked
346
- const originalItem = this.itemMap.get(id);
347
- if (originalItem) {
348
- // Clone the click to the original item
349
- const cloneEvent = new MouseEvent('click', {
350
- bubbles: true,
351
- cancelable: true,
352
- view: window,
353
- });
354
- originalItem.dispatchEvent(cloneEvent);
355
- }
356
- this.closePopover();
357
- this.closeAllMenu();
358
- };
359
- renderMoreButton() {
360
- const hasSlottedMore = this.el.querySelector('[slot="more"]');
361
- return (index.h("button", { class: "bar-more-button", part: "more-button", ref: el => (this.moreButtonEl = el), onClick: this.togglePopover, "aria-expanded": String(this.popoverOpen), "aria-haspopup": "true" }, hasSlottedMore ? index.h("slot", { name: "more" }) : index.h("le-icon", { name: "ellipsis-horizontal" })));
362
- }
363
- renderHamburgerButton() {
364
- const hasSlottedHamburger = this.el.querySelector('[slot="hamburger"]');
365
- return (index.h("button", { class: "bar-hamburger-button", part: "hamburger-button", onClick: this.togglePopover, "aria-expanded": String(this.popoverOpen), "aria-haspopup": "true" }, hasSlottedHamburger ? index.h("slot", { name: "hamburger" }) : index.h("le-icon", { name: "hamburger" })));
366
- }
367
- renderScrollArrows() {
368
- if (!this.arrows || this.overflow !== 'scroll')
369
- return null;
370
- const hasSlottedStartArrow = this.el.querySelector('[slot="start-arrow"]');
371
- const hasSlottedEndArrow = this.el.querySelector('[slot="end-arrow"]');
372
- return [
373
- index.h("button", { class: utils.classnames('bar-arrow', 'bar-arrow-start', {
374
- disabled: !this.canScrollStart,
375
- }), part: "arrow-start", onClick: this.scrollToStart, disabled: !this.canScrollStart, "aria-label": "Scroll to start" }, hasSlottedStartArrow ? (index.h("slot", { name: "start-arrow" })) : (index.h("le-icon", { name: "chevron-down", class: "arrow-icon-start" }))),
376
- index.h("button", { class: utils.classnames('bar-arrow', 'bar-arrow-end', {
377
- disabled: !this.canScrollEnd,
378
- }), part: "arrow-end", onClick: this.scrollToEnd, disabled: !this.canScrollEnd, "aria-label": "Scroll to end" }, hasSlottedEndArrow ? (index.h("slot", { name: "end-arrow" })) : (index.h("le-icon", { name: "chevron-down", class: "arrow-icon-end" }))),
379
- ];
380
- }
381
- renderAllMenuButton() {
382
- if (!this.showAllMenu)
383
- return null;
384
- const hasSlottedAllMenu = this.el.querySelector('[slot="all-menu"]');
385
- return (index.h("button", { class: "bar-all-menu-button", part: "all-menu-button", onClick: this.toggleAllMenu, "aria-expanded": String(this.allMenuOpen), "aria-haspopup": "true" }, hasSlottedAllMenu ? index.h("slot", { name: "all-menu" }) : index.h("le-icon", { name: "hamburger" })));
386
- }
387
- renderPopoverContent(itemsToShow) {
388
- return (index.h("div", { class: "bar-popover-content", part: "popover-content" }, itemsToShow.map(({ id, item }) => (index.h("div", { class: "bar-popover-item", key: id, onClick: (e) => this.handleItemClick(e, id), innerHTML: item.outerHTML })))));
389
- }
390
- renderOverflowPopover() {
391
- if (this.overflow !== 'more' && this.overflow !== 'hamburger')
392
- return null;
393
- const items = this.getSlottedItems();
394
- let itemsToShow = [];
395
- if (this.overflow === 'hamburger' && this.hamburgerActive) {
396
- // Show all items in hamburger mode
397
- itemsToShow = items.map((item, index) => ({
398
- id: this.getItemId(item, index),
399
- item,
400
- }));
401
- }
402
- else if (this.overflow === 'more' && (this.overflowingIds?.size ?? 0) > 0) {
403
- // Show only overflowing items
404
- itemsToShow = items
405
- .map((item, index) => ({
406
- id: this.getItemId(item, index),
407
- item,
408
- }))
409
- .filter(({ id }) => this.overflowingIds?.has(id));
410
- }
411
- if (itemsToShow.length === 0)
412
- return null;
413
- return (index.h("le-popover", { mode: "default", open: this.popoverOpen, position: "bottom", align: "end", showClose: false, closeOnClickOutside: true, closeOnEscape: true, onLePopoverClose: this.closePopover }, index.h("div", { slot: "trigger", class: "bar-overflow-trigger" }, this.overflow === 'hamburger' ? this.renderHamburgerButton() : this.renderMoreButton()), this.renderPopoverContent(itemsToShow)));
414
- }
415
- renderAllMenuPopover() {
416
- if (!this.showAllMenu)
417
- return null;
418
- const items = this.getSlottedItems();
419
- const itemsToShow = items.map((item, index) => ({
420
- id: this.getItemId(item, index),
421
- item,
422
- }));
423
- const position = this.showAllMenu === 'start' ? 'start' : 'end';
424
- return (index.h("le-popover", { mode: "default", open: this.allMenuOpen, position: "bottom", align: position, showClose: false, closeOnClickOutside: true, closeOnEscape: true, onLePopoverClose: this.closeAllMenu }, index.h("div", { slot: "trigger", class: "bar-all-menu-trigger" }, this.renderAllMenuButton()), this.renderPopoverContent(itemsToShow)));
425
- }
426
- render() {
427
- const showOverflowButton = !this.disablePopover &&
428
- ((this.overflow === 'more' && (this.overflowingIds?.size ?? 0) > 0) ||
429
- (this.overflow === 'hamburger' && this.hamburgerActive));
430
- const containerStyle = {};
431
- if (this.containerHeight !== null &&
432
- (this.overflow === 'more' || this.overflow === 'hamburger')) {
433
- containerStyle.height = `${this.containerHeight}px`;
434
- }
435
- const showAllMenuAtStart = this.showAllMenu === 'start';
436
- const showAllMenuAtEnd = this.showAllMenu === true || this.showAllMenu === 'end';
437
- return (index.h(index.Host, { key: '56d04e4504ed7ce1d8e78a825c71c71c074d0b62', class: utils.classnames({
438
- 'overflow-more': this.overflow === 'more',
439
- 'overflow-scroll': this.overflow === 'scroll',
440
- 'overflow-hamburger': this.overflow === 'hamburger',
441
- 'overflow-wrap': this.overflow === 'wrap',
442
- 'hamburger-active': this.hamburgerActive,
443
- 'has-overflow': (this.overflowingIds?.size ?? 0) > 0 || this.hamburgerActive,
444
- }) }, index.h("div", { key: '4e7aeb4c84a9964e19012c3d05827fa8406988b0', class: utils.classnames('bar-container', {
445
- [`align-${this.alignItems}`]: true,
446
- }), part: "container" }, this.overflow === 'scroll' && this.arrows && (index.h("div", { key: '2e3ae04c00e88e5055216dbc5b6d53574c450470', class: "bar-controls bar-controls-start" }, this.renderScrollArrows()?.[0])), showAllMenuAtStart && (index.h("div", { key: '9628c8dc2b15d9358baad63b19c75fd933eeb19a', class: "bar-controls bar-controls-start" }, this.renderAllMenuPopover())), index.h("div", { key: 'd1feb9603d0ff4247852aadb62cb920a5f5a1921', class: utils.classnames('bar-items', {
447
- 'is-scrollable': this.overflow === 'scroll',
448
- 'is-wrapping': this.overflow === 'wrap',
449
- }), style: containerStyle, ref: el => {
450
- this.itemsContainerEl = el;
451
- this.observeContainer(el);
452
- }, onScroll: this.overflow === 'scroll' ? this.handleScroll : undefined }, index.h("slot", { key: '338698e038b9df4c7b163017ceb0675b834160e4' })), showOverflowButton && (index.h("div", { key: 'd619fcda1b0c28247af60899f73cc076b0517787', class: "bar-controls bar-controls-end" }, this.renderOverflowPopover())), showAllMenuAtEnd && (index.h("div", { key: 'fd9fef90194aabb8d48e77c8f7615daa5d7297ac', class: "bar-controls bar-controls-end" }, this.renderAllMenuPopover())), this.overflow === 'scroll' && this.arrows && (index.h("div", { key: 'bcffb914e2788767c3765351a3eeae771f3e115f', class: "bar-controls bar-controls-end" }, this.renderScrollArrows()?.[1])))));
453
- }
454
- static get watchers() { return {
455
- "overflow": ["handleOverflowChange"]
456
- }; }
457
- };
458
- LeBar.style = leBarCss();
459
-
460
- const leButtonCss = () => `:host{display:inline-block;--le-button-border-radius:var(--le-radius-md);--le-button-padding-x:var(--le-spacing-3);--le-button-padding-y:var(--le-spacing-1);--le-button-padding:var(--le-button-padding-y) var(--le-button-padding-x);--le-button-small-padding:0.25rem;--le-button-font-size:var(--le-font-size-md);--le-button-font-weight:var(--le-font-weight-medium);--le-button-transition:var(--le-transition-fast);--le-transition-easing:ease-in-out;--le-button-icon-aspect-ratio:1;--le-button-color:var(--le-color-primary-contrast);--_btn-bg:var(--le-color-primary);--_btn-bg-hover:var(--le-color-primary-dark);--_btn-bg-system:var(--le-color-black);--_btn-color:var(--le-button-color);--_btn-border-color:var(--le-color-primary)}:host([full-width]){display:block;width:100%}.le-button-container{display:inline-flex;flex-direction:row;align-items:center;justify-content:center;gap:var(--le-spacing-3);width:100%;padding:var(--le-button-padding);border:1px solid var(--_btn-border-color);border-radius:var(--le-button-border-radius);background:var(--_btn-bg);color:var(--_btn-color);font-family:var(--le-font-family-base);font-size:var(--le-button-font-size);font-weight:var(--le-button-font-weight);line-height:var(--le-line-height-tight);text-decoration:none;cursor:pointer;transition:background-color var(--le-button-transition) var(--le-transition-easing),
461
- border-color var(--le-button-transition) var(--le-transition-easing),
462
- box-shadow var(--le-button-transition) var(--le-transition-easing),
463
- transform var(--le-button-transition) var(--le-transition-easing)}.le-button-container:hover:not(:disabled){background:var(--_btn-bg-hover);border-color:var(--_btn-bg-hover)}.le-button-container:active:not(:disabled){box-shadow:inset 0 0 5px color-mix(in srgb, var(--_btn-bg) 50%, transparent)}.le-button-container:focus-visible{outline:2px solid var(--le-color-focus);outline-offset:2px}.le-button-container:disabled{opacity:0.5;cursor:not-allowed}.le-button-label{display:inline-flex;flex-direction:row;align-items:center;justify-content:center;gap:var(--le-spacing-2)}:host(.color-primary){--_btn-bg:var(--le-color-primary);--_btn-bg-hover:var(--le-color-primary-dark);--_btn-color:var(--le-button-color);--_btn-border-color:var(--le-color-primary)}:host(.color-secondary){--_btn-bg:var(--le-color-secondary);--_btn-bg-hover:var(--le-color-secondary-dark);--_btn-color:var(--le-color-secondary-contrast);--_btn-border-color:var(--le-color-secondary)}:host(.color-success){--_btn-bg:var(--le-color-success);--_btn-bg-hover:var(--le-color-success-dark);--_btn-color:var(--le-color-success-contrast);--_btn-border-color:var(--le-color-success)}:host(.color-warning){--_btn-bg:var(--le-color-warning);--_btn-bg-hover:var(--le-color-warning-dark);--_btn-color:var(--le-color-warning-contrast);--_btn-border-color:var(--le-color-warning)}:host(.color-danger){--_btn-bg:var(--le-color-danger);--_btn-bg-hover:var(--le-color-danger-dark);--_btn-color:var(--le-color-danger-contrast);--_btn-border-color:var(--le-color-danger)}:host(.color-info){--_btn-bg:var(--le-color-info);--_btn-bg-hover:var(--le-color-info-dark);--_btn-color:var(--le-color-info-contrast);--_btn-border-color:var(--le-color-info)}:host(.variant-solid) .le-button-container{box-shadow:var(--le-shadow-sm)}:host(.variant-solid) .le-button-container:hover:not(:disabled){box-shadow:var(--le-shadow-md)}:host(.variant-outlined) .le-button-container{background:transparent;color:var(--_btn-bg);border-color:color-mix(in srgb, var(--_btn-border-color) 33%, transparent)}:host(.variant-outlined) .le-button-container:hover:not(:disabled){border-color:var(--_btn-border-color)}:host(.variant-clear) .le-button-container{background:transparent;color:var(--_btn-bg);border-color:transparent}:host(.variant-clear) .le-button-container:hover:not(:disabled){background:var(--le-color-gray-100);border-color:transparent}:host(.variant-system) .le-button-container{background:transparent;color:var(--_btn-bg-system);border-color:transparent}:host(.size-small){--le-button-padding-x:0.4rem;--le-button-padding-y:0.3rem;--le-button-padding-top:0.35rem;--le-button-font-size:var(--le-button-small-font-size, var(--le-font-size-xs))}:host(.size-large){--le-button-padding-x:0.9rem;--le-button-padding-y:0.6rem;--le-button-font-size:var(--le-font-size-xl)}:host(.full-width){display:block;width:100%}:host(.selected) .le-button-container{box-shadow:inset 0 0 4px var(--le-color-shadow)}:host(.variant-outlined.selected) .le-button-container,:host(.variant-clear.selected) .le-button-container{background:var(--_btn-bg);color:var(--_btn-color)}:host(.icon-only) .le-button-container{padding:var(--le-button-icon-only-padding, 0.5rem);padding-bottom:var(--le-button-icon-only-padding, 0.6rem);aspect-ratio:var(--le-button-icon-aspect-ratio, 1)}:host(.icon-only.size-small) .le-button-container{padding:var(--le-button-small-padding, 0.25rem)}:host(.icon-only.size-large) .le-button-container{padding:0.75rem}:host(.icon-only) .content{display:none}.content{display:inline}.content:empty{display:none}.icon-start,.icon-only,.icon-end{display:flex;align-items:center;justify-content:center}.icon-start:empty,.icon-only:empty,.icon-end:empty{display:none}::slotted([slot="icon-start"]),::slotted([slot="icon-only"]),::slotted([slot="icon-end"]){display:flex;align-items:center;justify-content:center;width:1.125em;height:1.125em}.le-button-align-start{justify-content:flex-start}.le-button-align-center{justify-content:center}.le-button-align-space-between{justify-content:space-between}.le-button-align-end{justify-content:flex-end}`;
464
-
465
- const LeButton = class {
466
- constructor(hostRef) {
467
- index.registerInstance(this, hostRef);
468
- this.leClick = index.createEvent(this, "click");
469
- }
470
- get el() { return index.getElement(this); }
471
- /**
472
- * Mode of the popover should be 'default' for internal use
473
- */
474
- mode;
475
- /**
476
- * Button variant style
477
- * @allowedValues solid | outlined | clear
478
- */
479
- variant = 'solid';
480
- /**
481
- * Button color theme (uses theme semantic colors)
482
- * @allowedValues primary | secondary | success | warning | danger | info
483
- */
484
- color = 'primary';
485
- /**
486
- * Button size
487
- * @allowedValues small | medium | large
488
- */
489
- size = 'medium';
490
- /**
491
- * Whether the button is in a selected/active state
492
- */
493
- selected = false;
494
- /**
495
- * Whether the button takes full width of its container
496
- */
497
- fullWidth = false;
498
- /**
499
- * Icon only button image or emoji
500
- * if this prop is set, the button will render only the icon slot
501
- */
502
- iconOnly;
503
- /**
504
- * Start icon image or emoji
505
- */
506
- iconStart;
507
- /**
508
- * End icon image or emoji
509
- */
510
- iconEnd;
511
- /**
512
- * Whether the button is disabled
513
- */
514
- disabled = false;
515
- /**
516
- * The button type attribute
517
- * @allowedValues button | submit | reset
518
- */
519
- type = 'button';
520
- /**
521
- * Optional href to make the button act as a link
522
- */
523
- href;
524
- /**
525
- * Link target when href is set
526
- */
527
- target;
528
- /**
529
- * Alignment of the button label without the end icon
530
- * @allowedValues start | center | space-between | end
531
- */
532
- align = 'center';
533
- /**
534
- * Emitted when the button is clicked.
535
- * This is a custom event that wraps the native click but ensures the target is the le-button.
536
- */
537
- leClick;
538
- handleClick = (event) => {
539
- // We stop the internal button click from bubbling up
540
- event.stopPropagation();
541
- if (this.disabled) {
542
- event.preventDefault();
543
- return;
544
- }
545
- // And emit our own click event from the host element
546
- this.leClick.emit(event);
547
- };
548
- render() {
549
- const classes = utils.classnames(`variant-${this.variant}`, `color-${this.color}`, `size-${this.size}`, {
550
- 'selected': this.selected,
551
- 'full-width': this.fullWidth,
552
- 'icon-only': this.iconOnly,
553
- 'disabled': this.disabled,
554
- });
555
- const TagType = this.href ? 'a' : 'button';
556
- const attrs = this.href
557
- ? { href: this.href, target: this.target, role: 'button' }
558
- : { type: this.type, disabled: this.disabled };
559
- return (index.h(index.Host, { key: 'a42aef03b5faf34f47b50dff799316a6815babd5', class: classes }, index.h("le-component", { key: '86ef8acf04d43e829b83d3e225f210acefc99515', component: "le-button" }, index.h(TagType, { key: '44145a5244577aab167c103501b788f0088799e3', class: utils.classnames('le-button-container', `le-button-align-${this.align}`), part: "button", ...attrs, onClick: this.handleClick }, this.iconOnly !== undefined ? (index.h("slot", { name: "icon-only" }, typeof this.iconOnly === 'string' ? this.iconOnly : null)) : (index.h(index.Fragment, null, index.h("span", { class: "le-button-label" }, this.iconStart && (index.h("span", { class: "icon-start", part: "icon-start" }, this.iconStart)), index.h("le-slot", { name: "", description: "Button text", type: "text", class: "content", part: "content" }, index.h("slot", null))), this.iconEnd && (index.h("span", { class: "icon-end", part: "icon-end" }, this.iconEnd))))))));
560
- }
561
- };
562
- LeButton.style = leButtonCss();
563
-
564
- const leCheckboxCss = () => `:host{display:block;--le-checkbox-size:18px;--le-checkbox-color:var(--le-color-primary, #007bff);--le-checkbox-label-color:var(--le-color-text-primary, #333);--le-checkbox-desc-color:var(--le-color-text-secondary, #666);--le-checkbox-border-radius:var(--le-radius-sm, 2px);--le-checkbox-marker-color:var(--le-color-surface, #fff)}.le-checkbox-wrapper{display:flex;flex-direction:column;gap:4px}.le-checkbox-label{display:inline-flex;align-items:flex-start;gap:8px;cursor:pointer;user-select:none}:host([disabled]) .le-checkbox-label{cursor:not-allowed;opacity:0.6}.le-checkbox-input{display:flex;align-items:center;justify-content:center;min-height:1.4em}input[type="checkbox"]{appearance:none;-webkit-appearance:none;width:var(--le-checkbox-size);height:var(--le-checkbox-size);border:var(--le-border-width, 2px) solid var(--le-checkbox-color);border-radius:var(--le-checkbox-border-radius);margin:0;margin-top:2px;position:relative;cursor:inherit;background-color:transparent;transition:background-color 0.2s, border-color 0.2s}input[type="checkbox"]:checked{background-color:var(--le-checkbox-color)}input[type="checkbox"]:checked::after{content:'';position:absolute;left:0;top:0;bottom:calc(var(--le-checkbox-size) / 5);right:0;margin:auto;width:calc(var(--le-checkbox-size) / 4);height:calc(var(--le-checkbox-size) / 2);border:solid var(--le-checkbox-marker-color, #fff);border-width:0 calc(var(--le-checkbox-size) / 10) calc(var(--le-checkbox-size) / 10) 0;transform:rotate(45deg)}input[type="checkbox"]:focus-visible{outline:2px solid var(--le-color-focus);outline-offset:2px}.le-checkbox-text{flex:1;flex-wrap:wrap;color:var(--le-checkbox-label-color);line-height:1.5;text-align:start}.le-checkbox-description{margin-left:calc(var(--le-checkbox-size) + 8px);font-size:0.875em;color:var(--le-checkbox-desc-color);line-height:1.4}:host [slot="description"]{margin:0}`;
565
-
566
- const LeCheckbox = class {
567
- constructor(hostRef) {
568
- index.registerInstance(this, hostRef);
569
- this.leChange = index.createEvent(this, "change");
570
- }
571
- get el() { return index.getElement(this); }
572
- /**
573
- * Whether the checkbox is checked
574
- */
575
- checked = false;
576
- /**
577
- * Whether the checkbox is disabled
578
- */
579
- disabled = false;
580
- /**
581
- * The name of the checkbox input
582
- */
583
- name;
584
- /**
585
- * The value of the checkbox input
586
- */
587
- value;
588
- /**
589
- * External ID for linking with external systems (e.g. database ID, PDF form field ID)
590
- */
591
- externalId;
592
- /**
593
- * Emitted when the checked state changes
594
- */
595
- leChange;
596
- handleChange = (event) => {
597
- // We stop the internal button click from bubbling up
598
- event.stopPropagation();
599
- if (this.disabled) {
600
- event.preventDefault();
601
- return;
602
- }
603
- const input = event.target;
604
- this.checked = input.checked;
605
- this.leChange.emit({
606
- checked: this.checked,
607
- value: this.value,
608
- name: this.name,
609
- externalId: this.externalId
610
- });
611
- };
612
- render() {
613
- return (index.h("le-component", { key: 'cb04cc6a990ee321dd7e8fca2f0bf56c76a865fa', component: "le-checkbox", hostClass: utils.classnames({ 'disabled': this.disabled }) }, index.h("div", { key: '1b8737eb0d572ba5fdf8c2cfc5ced4af5ef30997', class: "le-checkbox-wrapper" }, index.h("label", { key: '1d26d68565ade32489f2fade8120c7b8ad75b251', class: "le-checkbox-label" }, index.h("span", { key: '7e6b5e610c9f16c36b93c57284891366e6b3b756', class: "le-checkbox-input" }, index.h("input", { key: 'a12a374aa69526d802f36f00b0ae4bcd4ed27bc3', type: "checkbox", name: this.name, value: this.value, checked: this.checked, disabled: this.disabled, onChange: this.handleChange })), index.h("span", { key: 'e4ea1f85102c99751f9853b8e23fbd40ebd24780', class: "le-checkbox-text" }, index.h("le-slot", { key: 'cdda2111e4dfa6d7270280bac305255980842d48', name: "", type: "text", tag: "span" }, index.h("slot", { key: '32f57155d7a56f48e5780e37ea1ccac308db6848' })))), index.h("div", { key: '37d54d8c87b90987653f66a319958849893e7d91', class: "le-checkbox-description" }, index.h("le-slot", { key: 'fceedc24ef6a3561120456e9b387801a40c5919c', name: "description", type: "text", tag: "div", label: "Description" }, index.h("slot", { key: 'f1f87b7192565831a2f20428e8e2c0a974166dfd', name: "description" }))))));
614
- }
615
- };
616
- LeCheckbox.style = leCheckboxCss();
617
-
618
- const leCollapseCss = () => `:host{--le-collapse-duration:var(--le-transition-normal);display:grid;grid-template-rows:1fr;opacity:1;transition:grid-template-rows var(--le-collapse-duration),
619
- opacity var(--le-collapse-duration)}:host([data-open="false"]),:host([closed]){grid-template-rows:0fr;opacity:0}:host([no-fading][data-open="false"]),:host([no-fading][closed]){opacity:1}.region{display:flex;flex-direction:column;justify-content:flex-end;overflow:hidden}.region.scroll-down{justify-content:flex-start}`;
620
-
621
- const LeCollapse = class {
622
- constructor(hostRef) {
623
- index.registerInstance(this, hostRef);
624
- }
625
- get el() { return index.getElement(this); }
626
- /**
627
- * Since Stencil boolean props default to `false` when the attribute is missing.
628
- * instead of `open` defaulting to `true`, using a `closed` prop.
629
- */
630
- closed = false;
631
- /** Whether the content should scroll down from the top when open. */
632
- scrollDown = false;
633
- /** Stop fading the content when collapsing/expanding. */
634
- noFading = false;
635
- /** If true, collapse/expand based on the nearest header shrink event. */
636
- collapseOnHeaderShrink = false;
637
- /**
638
- * Handles `leHeaderShrinkChange` events from the `le-header`.
639
- * In case multiple headers are present, only the nearest one in the DOM tree is used.
640
- */
641
- handleHeaderShrink(ev) {
642
- const e = ev;
643
- this.headerShrunk = !!e.detail?.shrunk;
644
- }
645
- headerShrunk = false;
646
- componentDidLoad() {
647
- this.applyOpenState();
648
- }
649
- onOpenChange() {
650
- this.applyOpenState();
651
- }
652
- onDrivenStateChange() {
653
- this.applyOpenState();
654
- }
655
- shouldBeOpen() {
656
- if (this.closed)
657
- return false;
658
- if (this.collapseOnHeaderShrink && this.headerShrunk)
659
- return false;
660
- return true;
661
- }
662
- applyOpenState() {
663
- const nextOpen = this.shouldBeOpen();
664
- this.el.toggleAttribute('data-open', nextOpen);
665
- }
666
- render() {
667
- return (index.h(index.Host, { key: 'e0882ec40ed132dbd6eeaa43da4aff03b6e45352', "data-open": this.shouldBeOpen() ? 'true' : 'false' }, index.h("le-component", { key: '55c98e5382ddd6b5ef8a8f506f9a2901fc0b48ce', component: "le-collapse" }, index.h("div", { key: '0bcfa5aada8a7a6f9c86011e6a3dfa5bbb5e08c4', class: { 'region': true, 'scroll-down': this.scrollDown }, part: "region" }, index.h("slot", { key: '7b762e372802512a287ae98d4aeecdb3160bb54e' })))));
668
- }
669
- static get watchers() { return {
670
- "open": ["onOpenChange"],
671
- "headerShrunk": ["onDrivenStateChange"]
672
- }; }
673
- };
674
- LeCollapse.style = leCollapseCss();
675
-
676
- const leComponentCss = () => `:host{display:contents}:host(.admin-mode){display:block}.le-component-wrapper{position:relative;border:2px dashed var(--le-admin-border-color, #90caf9);border-radius:var(--le-radius-md, 8px);background:var(--le-admin-bg, rgba(144, 202, 249, 0.05));transition:border-color 0.2s ease, box-shadow 0.2s ease}.le-component-wrapper:hover{border-color:var(--le-admin-border-hover, #42a5f5);box-shadow:0 0 0 2px var(--le-admin-glow, rgba(66, 165, 245, 0.2))}.le-component-header{display:flex;align-items:center;justify-content:space-between;gap:var(--le-spacing-1, 4px);padding:0 0 0 var(--le-spacing-1, 4px);background:var(--le-admin-header-bg, rgba(144, 202, 249, 0.15));border-bottom:1px solid var(--le-admin-border-color, #90caf9);border-radius:var(--le-radius-md, 8px) var(--le-radius-md, 8px) 0 0;font-size:var(--le-font-size-xs, 11px)}.le-component-name{font-weight:var(--le-font-weight-medium, 500);color:var(--le-admin-text, #1976d2);text-transform:capitalize;text-align:start;overflow:hidden;width:0;flex:1 1 0%}.le-component-content{padding:var(--le-space-xs, 4px)}.le-component-trigger{font-size:24px;line-height:0px;width:12px;height:12px}.le-component-button{width:20px}.property-editor{display:flex;flex-direction:column;gap:var(--le-space-sm, 8px);max-width:380px}.property-field{display:flex;flex-direction:column;gap:var(--le-space-xs, 4px)}.property-field label{display:flex;flex-direction:column;gap:2px;font-size:var(--le-font-size-sm, 13px);font-weight:var(--le-font-weight-medium, 500);color:var(--le-color-text, #333)}.property-hint{font-size:var(--le-font-size-xs, 11px);font-weight:normal;color:var(--le-color-text-secondary, #666);line-height:1.3}.property-field input[type="text"],.property-field input[type="number"],.property-field select{padding:var(--le-space-xs, 4px) var(--le-space-sm, 8px);border:1px solid var(--le-color-border, #ddd);border-radius:var(--le-radius-md, 7px);font-size:var(--le-font-size-sm, 13px);font-family:inherit;background:var(--le-color-surface, #fff);color:var(--le-color-text, #333);transition:border-color 0.15s ease, box-shadow 0.15s ease}.property-field input:focus,.property-field select:focus{outline:none;border-color:var(--le-color-primary, #1976d2);box-shadow:0 0 0 2px var(--le-color-primary-light, rgba(25, 118, 210, 0.2))}.property-field--checkbox{flex-direction:column}.property-field--checkbox label{flex-direction:row;align-items:center;gap:var(--le-space-sm, 8px);cursor:pointer}.property-field--checkbox input[type="checkbox"]{width:16px;height:16px;margin:0;cursor:pointer;accent-color:var(--le-color-primary, #1976d2)}.property-field--checkbox .property-hint{margin-left:24px}.no-properties{margin:0;padding:var(--le-space-sm, 8px);font-size:var(--le-font-size-sm, 13px);color:var(--le-color-text-secondary, #666);text-align:center}.property-editor-container{display:flex;flex-direction:column;gap:var(--le-space-md, 12px)}.property-editor-actions{padding-top:var(--le-space-sm, 8px);border-top:1px solid var(--le-color-border, #e5e5e5)}.delete-component-btn{display:flex;align-items:center;justify-content:center;gap:var(--le-space-xs, 4px);width:100%;padding:var(--le-space-sm, 8px) var(--le-space-md, 12px);border:1px solid var(--le-color-danger, #e53935);border-radius:var(--le-radius-md, 6px);background:transparent;color:var(--le-color-danger, #e53935);font-size:var(--le-font-size-sm, 13px);font-weight:500;cursor:pointer;transition:background-color 0.15s, color 0.15s}.delete-component-btn:hover{background:var(--le-color-danger, #e53935);color:white}.delete-component-btn:active{opacity:0.9}`;
677
-
678
- const LeComponent = class {
679
- constructor(hostRef) {
680
- index.registerInstance(this, hostRef);
681
- }
682
- get el() { return index.getElement(this); }
683
- /**
684
- * The tag name of the component (e.g., 'le-card').
685
- * Used to look up property metadata and display the component name.
686
- */
687
- component;
688
- /**
689
- * Optional display name for the component.
690
- * If not provided, the tag name will be formatted as the display name.
691
- */
692
- displayName;
693
- /**
694
- * Classes to apply to the host element.
695
- * Allows parent components to pass their styling classes.
696
- */
697
- hostClass;
698
- /**
699
- * Inline styles to apply to the host element.
700
- * Allows parent components to pass dynamic styles (e.g., flex properties).
701
- */
702
- hostStyle;
703
- /**
704
- * Reference to the host element (found automatically from parent)
705
- */
706
- hostElement;
707
- /**
708
- * Internal state to track admin mode
709
- */
710
- adminMode = false;
711
- /**
712
- * Component metadata loaded from Custom Elements Manifest
713
- */
714
- componentMeta = null;
715
- /**
716
- * Current property values of the host component
717
- */
718
- propertyValues = {};
719
- disconnectModeObserver;
720
- connectedCallback() {
721
- // Find the host element - le-component is rendered inside the component's shadow DOM,
722
- // so we need to find the shadow root's host element
723
- this.findHostElement();
724
- this.disconnectModeObserver = utils.observeModeChanges(this.el, mode => {
725
- this.adminMode = mode === 'admin';
726
- // Load metadata and refresh property values only when entering admin mode
727
- if (this.adminMode) {
728
- if (!this.componentMeta) {
729
- this.loadComponentMetadata();
730
- }
731
- else {
732
- this.readPropertyValues();
733
- }
734
- }
735
- });
736
- }
737
- /**
738
- * Find the host element by traversing up through shadow DOM
739
- */
740
- findHostElement() {
741
- // Get the shadow root that contains this le-component
742
- const rootNode = this.el.getRootNode();
743
- if (rootNode instanceof ShadowRoot) {
744
- // The host of this shadow root is our target component (e.g., le-card)
745
- this.hostElement = rootNode.host;
746
- }
747
- }
748
- componentDidLoad() {
749
- // Read initial property values from the host element
750
- this.readPropertyValues();
751
- }
752
- disconnectedCallback() {
753
- this.disconnectModeObserver?.();
754
- }
755
- /**
756
- * Formats a tag name into a display name
757
- * e.g., 'le-card' -> 'Card'
758
- */
759
- formatDisplayName(tagName) {
760
- if (!tagName || typeof tagName !== 'string')
761
- return 'unknown';
762
- return tagName
763
- .replace(/^le-/, '') // Remove 'le-' prefix
764
- .split('-')
765
- .map(word => word.charAt(0).toUpperCase() + word.slice(1))
766
- .join(' ');
767
- }
768
- /**
769
- * Load component metadata from the Custom Elements Manifest
770
- */
771
- async loadComponentMetadata() {
772
- try {
773
- // Fetch the manifest from configured URL
774
- const { manifestFile } = index.getLeKitConfig();
775
- const manifestFileResolved = index.getAssetPath(`./assets/${manifestFile}`);
776
- const response = await fetch(manifestFileResolved);
777
- const manifest = await response.json();
778
- // Find the component definition
779
- for (const module of manifest.modules) {
780
- for (const declaration of module.declarations || []) {
781
- if (declaration.tagName === this.component) {
782
- const attributes = (declaration.attributes || []).filter((attr) => !this.isInternalAttribute(attr.name));
783
- this.componentMeta = {
784
- tagName: declaration.tagName,
785
- description: declaration.description,
786
- attributes,
787
- };
788
- // console.log(`[le-component] Loaded metadata for ${this.component}:`, this.componentMeta);
789
- // Read property values after metadata is loaded
790
- this.readPropertyValues();
791
- return;
792
- }
793
- }
794
- }
795
- // console.warn(`[le-component] No metadata found for component: ${this.component}`);
796
- }
797
- catch (error) {
798
- // console.warn(`[le-component] Failed to load metadata for component: ${this.component}`, error);
799
- }
800
- }
801
- /**
802
- * Check if an attribute is internal (should not be shown in editor)
803
- */
804
- isInternalAttribute(name) {
805
- const internalAttrs = ['mode', 'theme', 'class', 'style', 'id', 'slot'];
806
- return internalAttrs.includes(name);
807
- }
808
- /**
809
- * Read current property values from the host element
810
- */
811
- readPropertyValues() {
812
- if (!this.hostElement || !this.componentMeta)
813
- return;
814
- const values = {};
815
- for (const attr of this.componentMeta.attributes) {
816
- const value = this.hostElement.getAttribute(attr.name);
817
- values[attr.name] = this.parseAttributeValue(value, attr.type?.text);
818
- }
819
- this.propertyValues = values;
820
- }
821
- /**
822
- * Parse an attribute value based on its type
823
- */
824
- parseAttributeValue(value, type) {
825
- if (value === null)
826
- return undefined;
827
- if (type === 'boolean') {
828
- return value !== null && value !== 'false';
829
- }
830
- if (type === 'number') {
831
- return parseFloat(value);
832
- }
833
- return value;
834
- }
835
- /**
836
- * Handle property value changes from the editor
837
- */
838
- handlePropertyChange(attrName, value, type) {
839
- if (!this.hostElement)
840
- return;
841
- // Update the host element's attribute
842
- if (type === 'boolean') {
843
- if (value) {
844
- this.hostElement.setAttribute(attrName, '');
845
- }
846
- else {
847
- this.hostElement.removeAttribute(attrName);
848
- }
849
- }
850
- else if (value === undefined || value === '') {
851
- this.hostElement.removeAttribute(attrName);
852
- }
853
- else {
854
- this.hostElement.setAttribute(attrName, String(value));
855
- }
856
- // Update local state
857
- this.propertyValues = { ...this.propertyValues, [attrName]: value };
858
- // update the host element the way the parent element mutation observer would catch it?
859
- }
860
- /**
861
- * Delete this component from the DOM
862
- */
863
- deleteComponent() {
864
- if (!this.hostElement)
865
- return;
866
- // Confirm deletion
867
- const name = this.displayName || this.formatDisplayName(this.component);
868
- if (!index$1.leConfirm(`Delete this ${name}?`))
869
- return;
870
- // Remove the host element from its parent
871
- const parent = this.hostElement.parentElement;
872
- if (parent) {
873
- this.hostElement.remove();
874
- }
875
- }
876
- /**
877
- * Render the property editor form
878
- */
879
- renderPropertyEditor() {
880
- const hasProperties = this.componentMeta && this.componentMeta.attributes.length > 0;
881
- return (index.h("div", { class: "property-editor-container" }, hasProperties ? (index.h("form", { class: "property-editor", onSubmit: e => e.preventDefault() }, this.componentMeta.attributes.map(attr => this.renderPropertyField(attr)))) : (index.h("p", { class: "no-properties" }, "No editable properties")), index.h("div", { class: "property-editor-actions" }, index.h("le-button", { type: "button", variant: "outlined", color: "danger", "full-width": true, onClick: () => this.deleteComponent() }, index.h("span", { slot: "icon-start" }, "\uD83D\uDDD1\uFE0F"), index.h("span", null, "Delete Component")))));
882
- }
883
- /**
884
- * Render a single property field based on its type
885
- */
886
- renderPropertyField(attr) {
887
- const value = this.propertyValues[attr.name];
888
- const type = attr.type?.text || 'string';
889
- // Check if type is a union of string literals (e.g., "'default' | 'outlined' | 'elevated'")
890
- const enumMatch = type.match(/^'[^']+'/);
891
- if (enumMatch) {
892
- const options = type.split('|').map(opt => opt.trim().replace(/'/g, ''));
893
- return (index.h("div", { class: "property-field" }, index.h("label", { htmlFor: `prop-${attr.name}` }, attr.name, attr.description && index.h("span", { class: "property-hint" }, attr.description)), index.h("le-select", { options: [...options.map(opt => ({ label: opt, value: opt }))], "full-width": true, value: value ?? attr.default?.replace(/'/g, ''), placeholder: attr.default?.replace(/'/g, ''), onChange: (e) => this.handlePropertyChange(attr.name, e.detail.value, type) })));
894
- }
895
- // Boolean type
896
- if (type === 'boolean') {
897
- return (index.h("div", { class: "property-field property-field--checkbox" }, index.h("le-checkbox", { name: `prop-${attr.name}`, checked: value === true || value === '', onChange: e => this.handlePropertyChange(attr.name, e.target.checked, type) }, attr.name, attr.description && index.h("div", { slot: "description" }, attr.description))));
898
- }
899
- // Number type
900
- if (type === 'number') {
901
- return (index.h("div", { class: "property-field" }, index.h("label", { htmlFor: `prop-${attr.name}` }, attr.name, attr.description && index.h("span", { class: "property-hint" }, attr.description)), index.h("input", { type: "number", id: `prop-${attr.name}`, value: value ?? '', placeholder: attr.default, onChange: e => this.handlePropertyChange(attr.name, e.target.value, type) })));
902
- }
903
- // Default: string/text input
904
- return (index.h("div", { class: "property-field" }, index.h("le-string-input", { name: `prop-${attr.name}`, label: attr.name, value: value ?? '', placeholder: attr.default?.replace(/'/g, ''), onChange: (e) => this.handlePropertyChange(attr.name, e.detail.value, type) }, index.h("span", { slot: "description" }, attr.description))));
905
- }
906
- render() {
907
- const name = this.displayName || this.formatDisplayName(this.component);
908
- // In default mode, just pass through content with host classes
909
- if (!this.adminMode) {
910
- return (index.h(index.Host, { class: utils.classnames(this.component, this.hostClass), style: this.hostStyle }, index.h("slot", null)));
911
- }
912
- // In admin mode, show wrapper with header and settings
913
- return (index.h(index.Host, { class: utils.classnames(this.component, this.hostClass, 'admin-mode'), style: this.hostStyle }, index.h("div", { class: "le-component-wrapper" }, index.h("div", { class: "le-component-header" }, index.h("span", { class: "le-component-name" }, name), index.h("le-popover", { popoverTitle: `${name} Settings`, position: "right", align: "start", "min-width": "300px", mode: "default" }, index.h("le-button", { type: "button", class: "le-component-button", slot: "trigger", variant: "clear", size: "small", "aria-label": "Edit component properties", "icon-only": true }, index.h("span", { class: "le-component-trigger", slot: "icon-only" }, "\u2699")), this.renderPropertyEditor())), index.h("div", { class: "le-component-content" }, index.h("slot", null)))));
914
- }
915
- static get assetsDirs() { return ["assets"]; }
916
- };
917
- LeComponent.style = leComponentCss();
918
-
919
- const leCurrentHeadingCss = () => `:host{display:inline-flex;min-width:0}.title{font:inherit;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--le-current-heading-color, var(--le-color-text-primary))}`;
920
-
921
- const LeCurrentHeading = class {
922
- constructor(hostRef) {
923
- index.registerInstance(this, hostRef);
924
- }
925
- get el() { return index.getElement(this); }
926
- /** CSS selector for page title/headings to watch (e.g. `.page-title`, `main h2`). */
927
- selector = '';
928
- activeText = null;
929
- componentWillLoad() {
930
- this.updateActiveTitle();
931
- }
932
- onSelectorChange() {
933
- this.updateActiveTitle();
934
- }
935
- onScroll() {
936
- this.updateActiveTitle();
937
- }
938
- onResize() {
939
- this.updateActiveTitle();
940
- }
941
- updateActiveTitle() {
942
- if (typeof window === 'undefined')
943
- return;
944
- const selector = (this.selector ?? '').trim();
945
- if (!selector) {
946
- this.activeText = null;
947
- return;
948
- }
949
- let elements = [];
950
- try {
951
- elements = Array.from(document.querySelectorAll(selector));
952
- }
953
- catch {
954
- this.activeText = null;
955
- return;
956
- }
957
- // Pick the last element that is fully above the viewport.
958
- let nextText = null;
959
- for (const element of elements) {
960
- const rect = element.getBoundingClientRect();
961
- if (rect.height > 0 && rect.bottom <= 0) {
962
- const t = (element.textContent ?? '').trim();
963
- if (t)
964
- nextText = t;
965
- }
966
- }
967
- // Do not create oscillations: update only when the computed title changes.
968
- if (nextText !== this.activeText) {
969
- this.activeText = nextText;
970
- }
971
- }
972
- render() {
973
- return (index.h(index.Host, { key: 'c8d94a695a402039000210914b6780264a034a89' }, this.activeText ? (index.h("span", { class: "title", part: "title" }, this.activeText)) : (index.h("slot", null))));
974
- }
975
- static get watchers() { return {
976
- "selector": ["onSelectorChange"]
977
- }; }
978
- };
979
- LeCurrentHeading.style = leCurrentHeadingCss();
980
-
981
- const leDropdownBaseCss = () => `:host{display:block;--le-dropdown-list-padding:var(--le-spacing-1);--le-dropdown-empty-padding:var(--le-spacing-4);--le-dropdown-option-radius:var(--le-radius-md);--le-dropdown-font-size:var(--le-font-size-sm);--le-dropdown-option-padding:var(--le-spacing-1) var(--le-spacing-2);--le-dropdown-group-padding:var(--le-spacing-2) var(--le-spacing-2) var(--le-spacing-1);--le-dropdown-group-font-size:var(--le-font-size-xs)}:host([disabled]){pointer-events:none;opacity:0.5}le-popover::part(content){overflow-y:auto;overflow-x:hidden;padding:var(--le-dropdown-list-padding, 0.25rem)}.dropdown-empty{padding:var(--le-dropdown-empty-padding);text-align:center;color:var(--le-color-text-secondary, #9ca3af);font-size:var(--le-dropdown-font-size)}.dropdown-group-header{padding:var(--le-dropdown-group-padding);font-size:var(--le-dropdown-group-font-size);font-weight:700;color:var(--le-color-text-secondary, #9ca3af);letter-spacing:0.05em}.dropdown-list{text-align:initial}.dropdown-separator{height:1px;margin:var(--le-dropdown-separator-margin, 0.25rem 0);background:var(--le-color-border, #e5e7eb)}.dropdown-option{display:flex;align-items:center;gap:var(--le-dropdown-option-gap, 0.5rem);padding:var(--le-dropdown-option-padding);font-size:var(--le-dropdown-font-size, 0.875rem);line-height:1.4;color:var(--le-color-text, #1f2937);border:1px solid transparent;border-radius:var(--le-dropdown-option-radius, 0.25rem);cursor:pointer;user-select:none;transition:background-color 0.1s ease}.dropdown-option:hover,.dropdown-option.is-focused{border-color:var(--le-color-border-hover, #d1d5db)}.dropdown-option.is-disabled{opacity:0.5;cursor:not-allowed}.dropdown-option.is-disabled:hover{background:transparent}.option-checkbox{display:flex;align-items:center;justify-content:center;width:1rem;height:1rem;border:2px solid var(--le-color-border, #d1d5db);border-radius:0.25rem;background:var(--le-color-surface, #fff);flex-shrink:0}.is-selected .option-checkbox{background:var(--le-color-primary, #3b82f6);border-color:var(--le-color-primary, #3b82f6);color:white}.option-checkbox svg{width:0.75rem;height:0.75rem}.option-icon-start,.option-icon-end{display:flex;align-items:center;justify-content:center;flex-shrink:0;width:1.25rem;height:1.25rem}.option-icon-start img,.option-icon-end img{width:100%;height:100%;object-fit:contain}.option-content{flex:1;min-width:0;display:flex;flex-direction:column}.option-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.option-description{font-size:0.75rem;color:var(--le-color-text-muted, #6b7280);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.option-check{display:flex;align-items:center;justify-content:center;width:1rem;height:1rem;color:var(--le-color-primary, #3b82f6);flex-shrink:0}.option-check svg{width:1rem;height:1rem}`;
982
-
983
- const LeDropdownBase = class {
984
- constructor(hostRef) {
985
- index.registerInstance(this, hostRef);
986
- this.leOptionSelect = index.createEvent(this, "leOptionSelect");
987
- this.leDropdownOpen = index.createEvent(this, "leDropdownOpen");
988
- this.leDropdownClose = index.createEvent(this, "leDropdownClose");
989
- }
990
- get el() { return index.getElement(this); }
991
- /**
992
- * The options to display in the dropdown.
993
- */
994
- options = [];
995
- /**
996
- * Current value(s) - single value or array for multiselect.
997
- */
998
- value;
999
- /**
1000
- * Whether multiple selection is allowed.
1001
- */
1002
- multiple = false;
1003
- /**
1004
- * Whether the dropdown is open.
1005
- */
1006
- open = false;
1007
- /**
1008
- * Whether the dropdown is disabled.
1009
- */
1010
- disabled = false;
1011
- /**
1012
- * Filter function for options.
1013
- * Return true to include the option.
1014
- */
1015
- filterFn;
1016
- /**
1017
- * Current filter query string.
1018
- */
1019
- filterQuery = '';
1020
- /**
1021
- * Placeholder text when no options match filter.
1022
- */
1023
- emptyText = 'No options';
1024
- /**
1025
- * Whether to show checkboxes for multiselect mode.
1026
- */
1027
- showCheckboxes = true;
1028
- /**
1029
- * Maximum height of the dropdown list.
1030
- */
1031
- maxHeight = '300px';
1032
- /**
1033
- * Width of the dropdown. If not set, matches trigger width.
1034
- */
1035
- width;
1036
- /**
1037
- * Sets the dropdown to full width of the trigger.
1038
- */
1039
- fullWidth = false;
1040
- /**
1041
- * Whether to close the dropdown when clicking outside.
1042
- * (used to support combobox with input focus)
1043
- */
1044
- closeOnClickOutside = true;
1045
- /**
1046
- * Emitted when an option is selected.
1047
- */
1048
- leOptionSelect;
1049
- /**
1050
- * Emitted when the dropdown opens.
1051
- */
1052
- leDropdownOpen;
1053
- /**
1054
- * Emitted when the dropdown closes.
1055
- */
1056
- leDropdownClose;
1057
- focusedIndex = -1;
1058
- filteredOptions = [];
1059
- popoverEl;
1060
- listEl;
1061
- triggerWidth = 0;
1062
- handleOptionsChange() {
1063
- this.updateFilteredOptions();
1064
- }
1065
- componentWillLoad() {
1066
- this.updateFilteredOptions();
1067
- }
1068
- updateFilteredOptions() {
1069
- // Remember previously focused option
1070
- const focusedOption = this.filteredOptions[this.focusedIndex];
1071
- if (!this.filterQuery || !this.filterFn) {
1072
- this.filteredOptions = this.options;
1073
- }
1074
- else {
1075
- this.filteredOptions = this.options.filter(opt => this.filterFn(opt, this.filterQuery));
1076
- }
1077
- // try to maintain focus on same option if still present
1078
- if (focusedOption) {
1079
- const newIndex = this.filteredOptions.indexOf(focusedOption);
1080
- this.focusedIndex = newIndex >= 0 ? newIndex : this.getInitialFocusIndex();
1081
- }
1082
- else {
1083
- this.focusedIndex = -1;
1084
- }
1085
- }
1086
- getSelectableOptions() {
1087
- return this.filteredOptions.filter(opt => !opt.disabled);
1088
- }
1089
- isSelected(option) {
1090
- const optValue = option.value ?? option.label;
1091
- if (this.multiple && Array.isArray(this.value)) {
1092
- setTimeout(() => {
1093
- this.popoverEl?.updatePosition();
1094
- }, 50);
1095
- return this.value.includes(optValue);
1096
- }
1097
- return this.value === optValue;
1098
- }
1099
- handleOptionClick(option, e) {
1100
- e.preventDefault();
1101
- e.stopPropagation();
1102
- if (option.disabled)
1103
- return;
1104
- this.leOptionSelect.emit({
1105
- value: option.value ?? option.label,
1106
- option,
1107
- });
1108
- // Close dropdown for single select
1109
- if (!this.multiple) {
1110
- this.hide();
1111
- }
1112
- }
1113
- handleKeyDown = (e) => {
1114
- if (!this.open)
1115
- return;
1116
- const optionCount = this.filteredOptions.length;
1117
- switch (e.key) {
1118
- case 'ArrowDown':
1119
- e.preventDefault();
1120
- // check for the next non-disabled option and focus
1121
- let nextIndex = this.focusedIndex < optionCount - 1 ? this.focusedIndex + 1 : 0;
1122
- while (this.filteredOptions[nextIndex].disabled) {
1123
- nextIndex = ++nextIndex < optionCount ? nextIndex : 0;
1124
- }
1125
- this.focusedIndex = nextIndex;
1126
- this.scrollToFocused();
1127
- break;
1128
- case 'ArrowUp':
1129
- e.preventDefault();
1130
- // check for the previous non-disabled option and focus
1131
- let prevIndex = this.focusedIndex > 0 ? this.focusedIndex - 1 : optionCount - 1;
1132
- while (this.filteredOptions[prevIndex].disabled) {
1133
- prevIndex = --prevIndex >= 0 ? prevIndex : optionCount - 1;
1134
- }
1135
- this.focusedIndex = prevIndex;
1136
- this.scrollToFocused();
1137
- break;
1138
- case 'Home':
1139
- e.preventDefault();
1140
- // check for the first non-disabled option and focus
1141
- let firstIndex = 0;
1142
- while (this.filteredOptions[firstIndex].disabled) {
1143
- firstIndex++;
1144
- if (firstIndex >= optionCount) {
1145
- firstIndex = -1;
1146
- break;
1147
- }
1148
- }
1149
- this.focusedIndex = firstIndex;
1150
- this.scrollToFocused();
1151
- break;
1152
- case 'End':
1153
- e.preventDefault();
1154
- // check for the last non-disabled option and focus
1155
- let lastIndex = optionCount - 1;
1156
- while (this.filteredOptions[lastIndex].disabled) {
1157
- lastIndex--;
1158
- if (lastIndex < 0) {
1159
- lastIndex = -1;
1160
- break;
1161
- }
1162
- }
1163
- this.focusedIndex = lastIndex;
1164
- this.scrollToFocused();
1165
- break;
1166
- case 'Enter':
1167
- case ' ':
1168
- e.preventDefault();
1169
- if (this.focusedIndex >= 0 && this.focusedIndex < optionCount) {
1170
- const option = this.filteredOptions[this.focusedIndex];
1171
- if (!option || option.disabled)
1172
- return;
1173
- this.leOptionSelect.emit({
1174
- value: option.value ?? option.label,
1175
- option,
1176
- });
1177
- if (!this.multiple) {
1178
- this.hide();
1179
- }
1180
- }
1181
- break;
1182
- case 'Escape':
1183
- e.preventDefault();
1184
- this.hide();
1185
- break;
1186
- case 'Tab':
1187
- this.hide();
1188
- break;
1189
- }
1190
- };
1191
- scrollToFocused() {
1192
- if (!this.listEl || this.focusedIndex < 0)
1193
- return;
1194
- const focusedEl = this.listEl.querySelector(`[data-index="${this.focusedIndex}"]`);
1195
- if (focusedEl) {
1196
- focusedEl.scrollIntoView({ block: 'nearest' });
1197
- }
1198
- }
1199
- handlePopoverOpen = () => {
1200
- this.open = true;
1201
- this.focusedIndex = this.getInitialFocusIndex();
1202
- this.leDropdownOpen.emit();
1203
- // Add keyboard listener
1204
- document.addEventListener('keydown', this.handleKeyDown);
1205
- };
1206
- handlePopoverClose = () => {
1207
- this.open = false;
1208
- this.focusedIndex = -1;
1209
- this.leDropdownClose.emit();
1210
- // Remove keyboard listener
1211
- document.removeEventListener('keydown', this.handleKeyDown);
1212
- };
1213
- getInitialFocusIndex() {
1214
- // Focus on first selected option, or first option
1215
- const selectableOptions = this.getSelectableOptions();
1216
- const selectedIndex = selectableOptions.findIndex(opt => this.isSelected(opt));
1217
- return selectedIndex >= 0 ? selectedIndex : 0;
1218
- }
1219
- /**
1220
- * Opens the dropdown.
1221
- */
1222
- async show() {
1223
- if (this.disabled)
1224
- return;
1225
- // Capture trigger width for matching dropdown width
1226
- const trigger = this.el.querySelector('[slot="trigger"]');
1227
- if (trigger) {
1228
- this.triggerWidth = trigger.offsetWidth;
1229
- }
1230
- await this.popoverEl?.show();
1231
- }
1232
- /**
1233
- * Closes the dropdown.
1234
- */
1235
- async hide() {
1236
- await this.popoverEl?.hide();
1237
- }
1238
- /**
1239
- * Toggles the dropdown.
1240
- */
1241
- async toggle() {
1242
- if (this.open) {
1243
- await this.hide();
1244
- }
1245
- else {
1246
- await this.show();
1247
- }
1248
- }
1249
- renderIcon(icon, className) {
1250
- if (!icon)
1251
- return null;
1252
- if (icon.startsWith('http') || icon.startsWith('/')) {
1253
- return index.h("img", { class: className, src: icon, alt: "" });
1254
- }
1255
- return index.h("span", { class: className }, icon);
1256
- }
1257
- renderOption(option, index$1) {
1258
- const isSelected = this.isSelected(option);
1259
- const isFocused = index$1 === this.focusedIndex;
1260
- const optionId = option.id || utils.generateId();
1261
- return (index.h("div", { class: {
1262
- 'dropdown-option': true,
1263
- 'is-selected': isSelected,
1264
- 'is-focused': isFocused,
1265
- 'is-disabled': !!option.disabled,
1266
- }, role: "option", id: optionId, "aria-selected": isSelected ? 'true' : 'false', "aria-disabled": option.disabled ? 'true' : undefined, "data-index": index$1, onClick: e => this.handleOptionClick(option, e), onMouseEnter: () => {
1267
- if (!option.disabled) {
1268
- this.focusedIndex = index$1;
1269
- }
1270
- } }, this.renderIcon(option.iconStart, 'option-icon-start'), index.h("div", { class: "option-content" }, index.h("span", { class: "option-label" }, option.label), option.description && index.h("span", { class: "option-description" }, option.description)), this.renderIcon(option.iconEnd, 'option-icon-end'), (!this.multiple || this.showCheckboxes) && isSelected && (index.h("span", { class: "option-check" }, index.h("svg", { viewBox: "0 0 16 16", fill: "currentColor" }, index.h("path", { d: "M13.78 4.22a.75.75 0 010 1.06l-7.25 7.25a.75.75 0 01-1.06 0L2.22 9.28a.75.75 0 011.06-1.06L6 10.94l6.72-6.72a.75.75 0 011.06 0z" }))))));
1271
- }
1272
- renderOptions() {
1273
- if (this.filteredOptions.length === 0) {
1274
- return index.h("div", { class: "dropdown-empty" }, this.emptyText);
1275
- }
1276
- // Group options if they have group property
1277
- const grouped = new Map();
1278
- const ungrouped = [];
1279
- this.filteredOptions.forEach(opt => {
1280
- if (opt.group) {
1281
- const group = grouped.get(opt.group) || [];
1282
- group.push(opt);
1283
- grouped.set(opt.group, group);
1284
- }
1285
- else {
1286
- ungrouped.push(opt);
1287
- }
1288
- });
1289
- // Build flat list with group headers for index tracking
1290
- let globalIndex = 0;
1291
- const elements = [];
1292
- // Render ungrouped options first
1293
- ungrouped.forEach(opt => {
1294
- if (opt.separator === 'before') {
1295
- elements.push(index.h("div", { class: "dropdown-separator", role: "separator" }));
1296
- }
1297
- elements.push(this.renderOption(opt, globalIndex++));
1298
- if (opt.separator === 'after') {
1299
- elements.push(index.h("div", { class: "dropdown-separator", role: "separator" }));
1300
- }
1301
- });
1302
- // Render grouped options
1303
- grouped.forEach((options, groupLabel) => {
1304
- elements.push(index.h("div", { class: "dropdown-group-header", role: "presentation" }, groupLabel));
1305
- options.forEach(opt => {
1306
- elements.push(this.renderOption(opt, globalIndex++));
1307
- });
1308
- });
1309
- return elements;
1310
- }
1311
- render() {
1312
- const dropdownWidth = this.width || (this.triggerWidth ? `${this.triggerWidth}px` : undefined);
1313
- return (index.h(index.Host, { key: '90bca5763b9110f8e5cad70870110cab1efeeec6' }, index.h("le-popover", { key: 'ebded64a4f2b23c8b1a954dd995c4d6171ef9c88', ref: el => (this.popoverEl = el), position: "bottom", align: "start", showClose: false, closeOnClickOutside: this.closeOnClickOutside, closeOnEscape: true, offset: 4, width: dropdownWidth, minWidth: "150px", "trigger-full-width": this.fullWidth, onLePopoverOpen: this.handlePopoverOpen, onLePopoverClose: this.handlePopoverClose }, index.h("slot", { key: '57e540660ee269de594209186d6fbe066b9c8fc1', name: "trigger", slot: "trigger" }), index.h("slot", { key: 'e58a76a0097f938873f5462c844ee9f1a5eece72', name: "header" }), index.h("div", { key: 'c4fa35069438df82894e45e87f46aef67a189c43', class: "dropdown-list", role: "listbox", "aria-multiselectable": this.multiple ? 'true' : undefined, ref: el => (this.listEl = el), style: { maxHeight: this.maxHeight } }, this.renderOptions()))));
1314
- }
1315
- static get watchers() { return {
1316
- "options": ["handleOptionsChange"],
1317
- "filterQuery": ["handleOptionsChange"]
1318
- }; }
1319
- };
1320
- LeDropdownBase.style = leDropdownBaseCss();
1321
-
1322
- const leHeaderCss = () => `:host{display:block;width:100%;--le-header-top-offset:0;--le-header-bg:var(--le-color-surface);--le-header-max-width:var(--le-header-max-width, auto);--le-header-margin:0 auto;--le-header-border:1px solid var(--le-color-border);--le-header-border-radius:0px;--le-header-shadow:var(--le-header-shadow, none);--le-header-color:var(--le-color-text);--le-header-content-max-width:800px;--le-header-padding-x:var(--le-space-md);--le-header-padding-y:var(--le-space-sm);--le-header-gap:var(--le-space-sm);--le-header-transition:var(--le-transition-normal);--le-header-z:1000}.header{width:100%;max-width:var(--le-header-max-width);margin:var(--le-header-margin);background:var(--le-header-bg);color:var(--le-header-color);border-bottom:var(--le-header-border);border-radius:var(--le-header-border-radius);box-shadow:var(--le-header-shadow)}:host(.is-fixed){position:fixed;top:var(--le-header-top-offset);left:0;right:0;z-index:var(--le-header-z)}:host(.is-sticky){position:sticky;top:var(--le-header-top-offset);z-index:var(--le-header-z)}:host(.is-static){position:relative}.inner{max-width:var(--le-header-content-max-width);margin:0 auto;padding:var(--le-header-padding-y) var(--le-header-padding-x)}.row{display:grid;grid-template-columns:minmax(0, 1fr) minmax(0, 2fr) minmax(0, 1fr);align-items:center;gap:var(--le-header-gap);transition:height var(--le-header-transition),
1323
- padding var(--le-header-transition),
1324
- transform var(--le-header-transition)}:host(.is-shrunk) .row{height:var(--le-header-height-condensed)}:host(.is-sticky.is-hidden){transform:translateY(-150%);transition:transform var(--le-header-transition)}:host(.is-sticky.is-revealed){transform:translateY(0);transition:transform var(--le-header-transition)}.start,.title,.end{min-width:0;display:flex;align-items:center}.start{justify-content:flex-start}.end{justify-content:flex-end}.title{justify-content:center;text-align:center}.title-slot ::slotted(*){white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.compact-title{font:inherit;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}`;
1325
-
1326
- const LeHeader = class {
1327
- constructor(hostRef) {
1328
- index.registerInstance(this, hostRef);
1329
- this.leHeaderState = index.createEvent(this, "leHeaderState");
1330
- this.leHeaderShrinkChange = index.createEvent(this, "leHeaderShrinkChange");
1331
- this.leHeaderVisibilityChange = index.createEvent(this, "leHeaderVisibilityChange");
1332
- }
1333
- get el() { return index.getElement(this); }
1334
- /** Force static positioning (default). Ignored if `sticky` or `fixed` are true. */
1335
- isStatic = false;
1336
- /** Sticky positioning (in-flow). Ignored if `fixed` is true. */
1337
- sticky = false;
1338
- /** Fixed positioning (out-of-flow). Takes precedence over `sticky`/`static`. */
1339
- fixed = false;
1340
- /**
1341
- * Sticky-only reveal behavior (hide on scroll down, show on scroll up).
1342
- * - missing/false: disabled
1343
- * - true/empty attribute: enabled with default threshold (16)
1344
- * - number (as string): enabled and used as threshold
1345
- */
1346
- revealOnScroll;
1347
- /**
1348
- * Shrink trigger.
1349
- * - missing/0: disabled
1350
- * - number (px): shrink when scrollY >= that value (but never before header height)
1351
- * - css var name (e.g. --foo): shrink when scrollY >= resolved var value
1352
- * - selector (e.g. .page-title): shrink when that element scrolls out of view above the viewport
1353
- */
1354
- shrinkOffset;
1355
- /**
1356
- * If true, expand the header when hovered
1357
- */
1358
- expandOnHover = false;
1359
- /** Emits whenever scroll-driven state changes. */
1360
- leHeaderState;
1361
- /** Emits when the header shrinks/expands (only on change). */
1362
- leHeaderShrinkChange;
1363
- /** Emits when the header hides/shows (only on change). */
1364
- leHeaderVisibilityChange;
1365
- revealed = true;
1366
- shrunk = false;
1367
- placeholderHeight = null;
1368
- hoverActive = false;
1369
- disconnectModeObserver;
1370
- rafId = null;
1371
- measureRafId = null;
1372
- lastY = 0;
1373
- lastEmittedDirection = 'down';
1374
- headerEl;
1375
- shrinkSelectorEl;
1376
- setShrunk(next, y) {
1377
- if (next === this.shrunk)
1378
- return;
1379
- this.shrunk = next;
1380
- this.leHeaderShrinkChange.emit({ shrunk: this.shrunk, y });
1381
- }
1382
- componentDidLoad() {
1383
- if (typeof window === 'undefined')
1384
- return;
1385
- this.lastY = window.scrollY || 0;
1386
- this.scheduleMeasure(true);
1387
- this.scheduleUpdate(true);
1388
- }
1389
- disconnectedCallback() {
1390
- this.disconnectModeObserver?.();
1391
- if (this.rafId != null) {
1392
- cancelAnimationFrame(this.rafId);
1393
- this.rafId = null;
1394
- }
1395
- if (this.measureRafId != null) {
1396
- cancelAnimationFrame(this.measureRafId);
1397
- this.measureRafId = null;
1398
- }
1399
- }
1400
- onBehaviorPropsChange() {
1401
- this.scheduleUpdate(true);
1402
- this.scheduleMeasure(true);
1403
- }
1404
- onWindowScroll() {
1405
- this.scheduleUpdate();
1406
- }
1407
- onWindowResize() {
1408
- this.scheduleMeasure(true);
1409
- this.scheduleUpdate(true);
1410
- }
1411
- getPosition() {
1412
- if (this.fixed)
1413
- return 'fixed';
1414
- if (this.sticky)
1415
- return 'sticky';
1416
- return 'static';
1417
- }
1418
- parseRevealThreshold() {
1419
- // Only applies in sticky mode.
1420
- if (!this.sticky || this.fixed)
1421
- return null;
1422
- if (this.revealOnScroll == null)
1423
- return null;
1424
- const raw = String(this.revealOnScroll).trim();
1425
- if (raw === '' || raw === 'true')
1426
- return 16;
1427
- if (raw === 'false')
1428
- return null;
1429
- const n = Number(raw);
1430
- return Number.isFinite(n) ? Math.max(0, n) : 16;
1431
- }
1432
- resolveShrinkStartPx() {
1433
- const raw = (this.shrinkOffset ?? '').trim();
1434
- if (!raw || raw === '0')
1435
- return null;
1436
- // Numeric
1437
- const numeric = Number(raw);
1438
- if (Number.isFinite(numeric))
1439
- return Math.max(0, numeric);
1440
- // CSS variable name
1441
- if (raw.startsWith('--')) {
1442
- const value = getComputedStyle(document.documentElement).getPropertyValue(raw).trim();
1443
- const v = Number(value.replace('px', '').trim());
1444
- return Number.isFinite(v) ? Math.max(0, v) : null;
1445
- }
1446
- // Selector
1447
- try {
1448
- const el = document.querySelector(raw);
1449
- this.shrinkSelectorEl = el;
1450
- return null;
1451
- }
1452
- catch {
1453
- this.shrinkSelectorEl = null;
1454
- return null;
1455
- }
1456
- }
1457
- scheduleUpdate(force = false) {
1458
- if (this.rafId != null) {
1459
- if (!force)
1460
- return;
1461
- cancelAnimationFrame(this.rafId);
1462
- this.rafId = null;
1463
- }
1464
- this.rafId = requestAnimationFrame(() => {
1465
- this.rafId = null;
1466
- this.updateFromScroll();
1467
- });
1468
- }
1469
- scheduleMeasure(force = false) {
1470
- if (this.measureRafId != null && !force)
1471
- return;
1472
- this.measureRafId = requestAnimationFrame(() => {
1473
- this.measureRafId = null;
1474
- this.measurePlaceholderHeight();
1475
- });
1476
- }
1477
- measurePlaceholderHeight() {
1478
- // Measure the rendered header height once (and on resize/mode change).
1479
- // This intentionally ignores scroll/shrink behavior; it should reserve the full header height.
1480
- if (!this.headerEl)
1481
- return;
1482
- const next = Math.ceil(this.headerEl.getBoundingClientRect().height);
1483
- if (!Number.isFinite(next) || next <= 0)
1484
- return;
1485
- if (next !== this.placeholderHeight) {
1486
- this.placeholderHeight = next;
1487
- // Publish to global root so placeholders anywhere can read it.
1488
- if (typeof document !== 'undefined') {
1489
- document.documentElement.style.setProperty('--le-header-height', `${next}px`);
1490
- }
1491
- }
1492
- }
1493
- updateFromScroll() {
1494
- const y = typeof window !== 'undefined' ? window.scrollY || 0 : 0;
1495
- const delta = y - this.lastY;
1496
- const direction = delta < 0 ? 'up' : 'down';
1497
- // Shrink behavior
1498
- let computedShrunk = false;
1499
- const headerHeight = Math.max(0, this.placeholderHeight ?? 0);
1500
- const shrinkStartPx = typeof window !== 'undefined' ? this.resolveShrinkStartPx() : null;
1501
- if (this.shrinkSelectorEl) {
1502
- const rect = this.shrinkSelectorEl.getBoundingClientRect();
1503
- computedShrunk = rect.bottom <= 0;
1504
- }
1505
- else if (shrinkStartPx != null) {
1506
- const effectiveStart = Math.max(shrinkStartPx, headerHeight);
1507
- computedShrunk = y >= effectiveStart;
1508
- }
1509
- // Hover override: when enabled and hovered, force expanded.
1510
- const nextShrunk = this.expandOnHover && this.hoverActive ? false : computedShrunk;
1511
- this.setShrunk(nextShrunk, y);
1512
- // Reveal-on-scroll (sticky-only)
1513
- const revealThreshold = this.parseRevealThreshold();
1514
- if (revealThreshold != null) {
1515
- // Always show the header near the top of the page.
1516
- const topLock = Math.max(0, this.placeholderHeight ?? 0);
1517
- if (y <= topLock) {
1518
- if (!this.revealed) {
1519
- this.revealed = true;
1520
- this.leHeaderVisibilityChange.emit({ visible: true, y });
1521
- }
1522
- }
1523
- else if (Math.abs(delta) >= revealThreshold) {
1524
- const nextRevealed = direction === 'up' || y <= 0;
1525
- if (nextRevealed !== this.revealed) {
1526
- this.revealed = nextRevealed;
1527
- this.leHeaderVisibilityChange.emit({ visible: this.revealed, y });
1528
- }
1529
- this.lastEmittedDirection = direction;
1530
- }
1531
- }
1532
- else {
1533
- if (!this.revealed) {
1534
- this.revealed = true;
1535
- this.leHeaderVisibilityChange.emit({ visible: true, y });
1536
- }
1537
- }
1538
- this.lastY = y;
1539
- this.leHeaderState.emit({
1540
- y,
1541
- direction: this.lastEmittedDirection,
1542
- revealed: this.revealed,
1543
- shrunk: this.shrunk,
1544
- });
1545
- }
1546
- render() {
1547
- const position = this.getPosition();
1548
- const hostClass = utils.classnames('le-header', {
1549
- 'header-is-shrunk': this.shrunk,
1550
- 'is-fixed': position === 'fixed',
1551
- 'is-sticky': position === 'sticky',
1552
- 'is-static': position === 'static',
1553
- 'is-revealed': this.revealed,
1554
- 'is-hidden': !this.revealed,
1555
- 'is-shrunk': this.shrunk,
1556
- });
1557
- return (index.h(index.Host, { key: 'd1a4ae64dff81139b1c35681698539fdf072ae66', class: hostClass, onMouseEnter: () => {
1558
- if (!this.expandOnHover)
1559
- return;
1560
- this.hoverActive = true;
1561
- this.scheduleUpdate(true);
1562
- }, onMouseLeave: () => {
1563
- if (!this.expandOnHover)
1564
- return;
1565
- this.hoverActive = false;
1566
- this.scheduleUpdate(true);
1567
- } }, index.h("le-component", { key: '305bc8b4aefaf2ef1dd7287422167cfd68d3e8c2', component: "le-header" }, index.h("header", { key: '1cf3b2ab01140e14870c1282e49e6eb3dbae5bf8', class: "header", part: "header", role: "banner", ref: el => (this.headerEl = el) }, index.h("div", { key: 'b238e3762dd31228f3ab3627fbca2ff1a9c630fd', class: "inner", part: "inner" }, index.h("div", { key: 'f186fbecc9c9b6f6f08f45c272c292af9333450e', class: "row", part: "row" }, index.h("div", { key: 'e496055801a36f4cf7a045513df2bc917a573786', class: "start", part: "start" }, index.h("le-slot", { key: '697b90a8291836452c17d2c9d3759c23b7238e93', name: "start", label: "Start", description: "Logo / back button / nav", "allowed-components": "le-button,le-text,le-tag,le-box,le-stack" }, index.h("slot", { key: 'e7e2b7ad283ce089def47d1f11f78a8a1603face', name: "start" }))), index.h("div", { key: '145aaadb93972b9018f9303baee2d5b3c42db319', class: "title", part: "title" }, index.h("le-slot", { key: 'c488ef0d01da476a6351ae4b7dfb397950f9c3ce', name: "title", label: "Title", description: "Header title", type: "text", tag: "span" }, index.h("span", { key: '78c21f46cbc81dc720e2f803cb3edc81ba5f76cf', class: "title-slot", part: "title" }, index.h("slot", { key: 'a47d5c8d4d0ae42b1cb4d7fab98022cbb380a27e', name: "title" })))), index.h("div", { key: '5fb7c5febcfb9b837f313caa65ea9b5e08b6f9cb', class: "end", part: "end" }, index.h("le-slot", { key: 'cc2ed75ea779a29e5c616daafec3ef90bbec3cfc', name: "end", label: "End", description: "Actions", "allowed-components": "le-button,le-text,le-tag,le-box,le-stack" }, index.h("slot", { key: '135e8c2c0d7a7d9bef174f380d1f31d377cb043f', name: "end" })))), index.h("div", { key: 'b0d9e5fba045b7cfd5640e7ba700cb44d673f982', class: "secondary", part: "secondary" }, index.h("le-slot", { key: '75ed60e3ddef918db1534f8818cd0f337c71e8db', name: "", label: "Secondary", description: "Secondary row content", "allowed-components": "le-tabs,le-tab-bar,le-select,le-combobox,le-text,le-stack,le-box" }, index.h("slot", { key: '1266dc5bb54bf22055acbfc23befb398c1ee5a31' }))))))));
1568
- }
1569
- static get watchers() { return {
1570
- "revealOnScroll": ["onBehaviorPropsChange"],
1571
- "shrinkOffset": ["onBehaviorPropsChange"],
1572
- "fixed": ["onBehaviorPropsChange"],
1573
- "sticky": ["onBehaviorPropsChange"],
1574
- "isStatic": ["onBehaviorPropsChange"]
1575
- }; }
1576
- };
1577
- LeHeader.style = leHeaderCss();
1578
-
1579
- const leIconCss = () => `:host{display:inline-block;width:var(--le-icon-size, var(--le-size-4));height:var(--le-icon-size, var(--le-size-4));color:var(--le-icon-color, var(--le-color-text-primary))}:host svg{display:block;fill:currentColor}`;
1580
-
1581
- const iconCache = {};
1582
- const requestCache = {};
1583
- /**
1584
- * Get the URL for loading an icon.
1585
- * Uses configurable assetBasePath if set, otherwise falls back to Stencil's getAssetPath.
1586
- */
1587
- function getIconUrl(name) {
1588
- const basePath = index.getAssetBasePath();
1589
- if (basePath) {
1590
- // Use configured base path - normalize by removing trailing slash
1591
- const normalizedBase = basePath.endsWith('/') ? basePath.slice(0, -1) : basePath;
1592
- return `${normalizedBase}/icons/${name}.json`;
1593
- }
1594
- // Fall back to Stencil's getAssetPath for local development
1595
- return index.getAssetPath(`./assets/icons/${name}.json`);
1596
- }
1597
- async function fetchIcon({ name }) {
1598
- if (iconCache[name]) {
1599
- return iconCache[name];
1600
- }
1601
- if (!requestCache[name]) {
1602
- const iconUrl = getIconUrl(name);
1603
- requestCache[name] = fetch(iconUrl)
1604
- .then(resp => resp.json())
1605
- .catch(() => {
1606
- console.error(`Icon "${name}" could not be loaded from: ${iconUrl}`);
1607
- return '';
1608
- });
1609
- }
1610
- const path = await requestCache[name];
1611
- iconCache[name] = path;
1612
- return path;
1613
- }
1614
- const LeIcon = class {
1615
- constructor(hostRef) {
1616
- index.registerInstance(this, hostRef);
1617
- }
1618
- get el() { return index.getElement(this); }
1619
- /**
1620
- * Name of the icon to display. Corresponds to a JSON file in the assets folder.
1621
- * For example, "search" will load the "search.json" file.
1622
- */
1623
- name = null;
1624
- /**
1625
- * Size of the icon in pixels. Default is 16.
1626
- */
1627
- size = 16;
1628
- iconData = null;
1629
- visible = false;
1630
- async loadIconData() {
1631
- const { name, visible } = this;
1632
- if (!name || !visible) {
1633
- return;
1634
- }
1635
- this.iconData = await fetchIcon({ name });
1636
- }
1637
- intersectionObserver;
1638
- connectedCallback() {
1639
- this.waitUntilVisible(() => {
1640
- this.visible = true;
1641
- this.loadIconData();
1642
- });
1643
- }
1644
- disconnectedCallback() {
1645
- if (this.intersectionObserver) {
1646
- this.intersectionObserver.disconnect();
1647
- this.intersectionObserver = null;
1648
- }
1649
- }
1650
- async componentWillLoad() {
1651
- this.loadIconData();
1652
- }
1653
- waitUntilVisible(callback) {
1654
- if (typeof window === 'undefined' ||
1655
- !window.IntersectionObserver) {
1656
- callback();
1657
- return;
1658
- }
1659
- this.intersectionObserver = new IntersectionObserver(entries => {
1660
- entries.forEach(entry => {
1661
- if (entry.isIntersecting) {
1662
- this.intersectionObserver.disconnect();
1663
- this.intersectionObserver = null;
1664
- callback();
1665
- }
1666
- });
1667
- }, { rootMargin: '50px' });
1668
- this.intersectionObserver.observe(this.el);
1669
- }
1670
- /**
1671
- * Renders the SVG content out of a JSON data in a format:
1672
- * { "viewBox": "...", children: [{ "tag": "g", ""children": [ ... ], ...attrs }, ...] }
1673
- *
1674
- * @returns JSX.Element | null
1675
- */
1676
- renderSVGContent(children) {
1677
- if (!children || children.length === 0) {
1678
- return null;
1679
- }
1680
- const createElement = node => {
1681
- const { tag, children, ...attrs } = node;
1682
- return index.h(tag, attrs, children ? children.map(createElement) : null);
1683
- };
1684
- const svgElements = children.map(createElement);
1685
- return svgElements;
1686
- }
1687
- render() {
1688
- return (index.h("svg", { key: '679c2641c722c5321ffc90a372d3f5fa5d748946', xmlns: "http://www.w3.org/2000/svg", fill: "currentColor", height: this.size || 16, width: this.size || 16, viewBox: this.iconData?.viewBox || `0 0 ${this.size || 16} ${this.size || 16}` }, this.renderSVGContent(this.iconData?.children)));
1689
- }
1690
- static get assetsDirs() { return ["assets/icons"]; }
1691
- static get watchers() { return {
1692
- "name": ["loadIconData"]
1693
- }; }
1694
- };
1695
- LeIcon.style = leIconCss();
1696
-
1697
- const leNavigationCss = () => `:host{display:block;--le-nav-radius:var(--le-radius-md);--le-nav-gap:var(--le-spacing-2);--le-nav-item-padding-x:var(--le-spacing-1);--le-nav-item-padding-y:var(--le-spacing-2);--le-nav-item-gap:var(--le-spacing-2);--le-nav-link-padding-x:var(--le-spacing-2);--le-nav-link-padding-y:var(--le-spacing-1);--le-nav-link-gap:var(--le-spacing-1);--le-nav-color:var(--le-color-text-primary);--le-nav-muted:var(--le-color-text-secondary);--le-nav-hover-bg:var(--le-color-gray-100);--le-nav-selected-bg:var(--le-color-primary);--le-nav-selected-color:var(--le-color-primary-contrast)}:host([orientation='horizontal']:not([wrap])){flex:1 1 0%;min-width:0}.nav-vertical{display:flex;flex-direction:column;gap:var(--le-nav-gap)}.nav-search{width:100%}.nav-search-input{--le-input-radius:var(--le-radius-md)}.nav-empty{padding:var(--le-spacing-2);color:var(--le-nav-muted);font-size:var(--le-font-size-sm)}.nav-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--le-spacing-1)}.nav-row{display:flex;align-items:stretch;gap:var(--le-spacing-1);border-radius:var(--le-nav-radius)}.nav-row:hover{background:var(--le-nav-hover-bg)}.nav-toggle,.nav-toggle-spacer{box-sizing:border-box;width:var(--le-spacing-4);min-width:var(--le-spacing-4);display:inline-flex;align-items:center;justify-content:center;border:1px solid transparent;border-radius:var(--le-nav-radius);color:inherit}.nav-toggle{background:transparent;cursor:pointer;opacity:0.4}.nav-toggle:hover:not(:disabled){opacity:1}.nav-toggle:focus-visible{outline:2px solid var(--le-color-focus);outline-offset:2px;opacity:1}.nav-chevron{display:inline-block;transition:transform var(--le-transition-fast)}.nav-chevron svg{display:block;width:var(--le-spacing-4);height:var(--le-spacing-4)}.nav-node>div>button>.nav-chevron{transform:rotate(-90deg)}.nav-node.open>div>button>.nav-chevron{transform:rotate(0deg)}.nav-item{flex:1;display:inline-flex;align-items:center;gap:var(--le-nav-item-gap);padding:var(--le-nav-item-padding-y) var(--le-nav-item-padding-x) var(--le-nav-item-padding-y) 0;border-radius:var(--le-nav-radius);border:1px solid transparent;background:transparent;text-decoration:none;color:var(--le-nav-color);font-family:var(--le-font-family-base);font-size:var(--le-font-size-md);line-height:var(--le-line-height-tight);cursor:pointer}.nav-item:focus-visible{outline:2px solid var(--le-color-focus);outline-offset:2px}.nav-node.selected>.nav-row{background:var(--le-nav-selected-bg);color:var(--le-nav-selected-color)}.nav-node.selected>.nav-row>.nav-item{color:inherit}.nav-node.disabled>.nav-row>.nav-item{opacity:0.5;cursor:not-allowed}.nav-text{display:flex;flex-direction:column;min-width:0}.nav-label{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.nav-description{color:color-mix(in srgb, var(--le-nav-muted) 90%, transparent);font-size:var(--le-font-size-sm);line-height:var(--le-line-height-tight)}.nav-icon{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0}.nav-icon-end{margin-left:auto}.nav-children{margin-top:var(--le-spacing-1)}.nav-horizontal-wrapper{display:flex;align-items:center;gap:var(--le-nav-gap)}.nav-bar{flex:1 1 0%;min-width:0;--le-bar-gap:var(--le-nav-gap)}.nav-bar.align-end{--le-bar-justify:flex-end}.nav-bar.align-center{--le-bar-justify:center}.nav-bar.align-space-between{--le-bar-justify:space-between}.nav-bar.overflow-more.has-overflow,.nav-bar.overflow-hamburger.has-overflow{margin-inline-end:calc((var(--le-nav-gap) + (var(--le-nav-link-padding-x) * 2)) * -1)}.h-item{display:flex;align-items:center}.h-link{display:inline-flex;align-items:center;gap:var(--le-nav-link-gap);padding:var(--le-nav-link-padding-y) var(--le-nav-link-padding-x);border-radius:var(--le-nav-radius);border:1px solid transparent;background:transparent;text-decoration:none;color:var(--le-nav-color);font-family:var(--le-font-family-base);font-size:var(--le-font-size-md);cursor:pointer}.h-link:hover{background:var(--le-nav-hover-bg)}.h-link:focus-visible{outline:2px solid var(--le-color-focus);outline-offset:2px}.h-link.disabled,.h-trigger.disabled{opacity:0.5;pointer-events:none}.h-link.selected,.h-trigger.selected{background:var(--le-nav-selected-bg);color:var(--le-nav-selected-color)}.h-label{white-space:nowrap}.h-trigger{display:inline-flex;align-items:center;gap:var(--le-spacing-1);border-radius:var(--le-nav-radius)}.h-submenu-toggle{width:var(--le-spacing-3);height:var(--le-spacing-3);display:inline-flex;align-items:center;justify-content:center;border:1px solid transparent;border-radius:var(--le-nav-radius);background:transparent;color:currentColor;cursor:pointer}.h-submenu-toggle:hover{background:var(--le-nav-hover-bg)}.overflow-trigger{display:inline-flex;align-items:center;justify-content:center;gap:var(--le-nav-link-gap);padding:var(--le-nav-link-padding-y) var(--le-nav-link-padding-x);border-radius:var(--le-nav-radius);border:1px solid transparent;background:transparent;color:var(--le-nav-color);cursor:pointer;font-family:var(--le-font-family-base);font-size:var(--le-font-size-md)}.overflow-trigger:hover{background:var(--le-nav-hover-bg)}.overflow-trigger:focus-visible{outline:2px solid var(--le-color-focus);outline-offset:2px}.nav-overflow-trigger{display:flex;align-items:center}le-popover::part(content){padding:var(--le-spacing-1)}`;
1698
-
1699
- const LeNavigation = class {
1700
- constructor(hostRef) {
1701
- index.registerInstance(this, hostRef);
1702
- this.leNavItemSelect = index.createEvent(this, "leNavItemSelect");
1703
- this.leNavItemToggle = index.createEvent(this, "leNavItemToggle");
1704
- }
1705
- get el() { return index.getElement(this); }
1706
- /**
1707
- * Navigation items.
1708
- * Can be passed as an array or JSON string (same pattern as le-select).
1709
- */
1710
- items = [];
1711
- /**
1712
- * Layout orientation.
1713
- */
1714
- orientation = 'horizontal';
1715
- /**
1716
- * Horizontal wrapping behavior.
1717
- * If false, overflow behavior depends on `overflowMode`.
1718
- */
1719
- wrap = false;
1720
- /**
1721
- * Overflow behavior for horizontal, non-wrapping menus.
1722
- * - more: moves overflow items into a "More" popover
1723
- * - hamburger: turns the whole nav into a hamburger popover
1724
- */
1725
- overflowMode = 'more';
1726
- /**
1727
- * Minimum number of visible top-level items required to use the "More" overflow.
1728
- * If fewer would be visible, the navigation falls back to hamburger.
1729
- */
1730
- minVisibleItemsForMore = 2;
1731
- /**
1732
- * Alignment of the menu items within the navigation bar.
1733
- */
1734
- align = 'start';
1735
- /**
1736
- * Active url for automatic selection.
1737
- */
1738
- activeUrl = '';
1739
- /**
1740
- * Enables a search input for the vertical navigation.
1741
- */
1742
- searchable = false;
1743
- /**
1744
- * Placeholder text for the search input.
1745
- */
1746
- searchPlaceholder = 'Search...';
1747
- /**
1748
- * Text shown when no items match the filter.
1749
- */
1750
- emptyText = 'No results found';
1751
- /**
1752
- * Whether submenu popovers should include a filter input.
1753
- */
1754
- submenuSearchable = false;
1755
- /**
1756
- * Fired when a navigation item is activated.
1757
- *
1758
- * This event is cancelable. Call `event.preventDefault()` to prevent
1759
- * default browser navigation and implement custom routing.
1760
- */
1761
- leNavItemSelect;
1762
- /**
1763
- * Fired when a tree branch is toggled.
1764
- */
1765
- leNavItemToggle;
1766
- searchQuery = '';
1767
- openState = {};
1768
- /** IDs of items currently in overflow (from le-bar) */
1769
- overflowIds = [];
1770
- /** Whether hamburger mode is active (from le-bar) */
1771
- hamburgerActive = false;
1772
- submenuQueries = {};
1773
- /** Whether the overflow popover is open */
1774
- overflowPopoverOpen = false;
1775
- popoverRefs = new Map();
1776
- instanceId = utils.generateId('le-nav');
1777
- partFromOptionPart(base, part) {
1778
- const raw = (part ?? '').trim();
1779
- if (!raw)
1780
- return base;
1781
- const tokens = raw
1782
- .split(/\s+/)
1783
- .map(t => t.replace(/[^a-zA-Z0-9_-]/g, ''))
1784
- .filter(Boolean);
1785
- if (tokens.length === 0)
1786
- return base;
1787
- return [base, ...tokens.map(t => `${base}-${t}`)].join(' ');
1788
- }
1789
- handleLayoutInputsChange() {
1790
- // Reset overflow state when layout inputs change
1791
- this.overflowIds = [];
1792
- this.hamburgerActive = false;
1793
- }
1794
- disconnectedCallback() {
1795
- // Cleanup if needed
1796
- }
1797
- get parsedItems() {
1798
- if (typeof this.items === 'string') {
1799
- try {
1800
- return JSON.parse(this.items);
1801
- }
1802
- catch {
1803
- return [];
1804
- }
1805
- }
1806
- return this.items;
1807
- }
1808
- getItemId(item, path) {
1809
- return item.id ?? `${this.instanceId}:${path}`;
1810
- }
1811
- getChildItems(item) {
1812
- return Array.isArray(item.children) ? item.children : [];
1813
- }
1814
- isOpen(item, id) {
1815
- const fromState = this.openState[id];
1816
- if (typeof fromState === 'boolean')
1817
- return fromState;
1818
- return !!item.open;
1819
- }
1820
- setOpen(id, open) {
1821
- if (this.openState[id] === open)
1822
- return;
1823
- this.openState = {
1824
- ...this.openState,
1825
- [id]: open,
1826
- };
1827
- }
1828
- matchesQuery(option, query) {
1829
- if (!query)
1830
- return true;
1831
- const q = query.toLowerCase();
1832
- return (option.label.toLowerCase().includes(q) ||
1833
- (option.description?.toLowerCase().includes(q) ?? false));
1834
- }
1835
- filterTree(items, query, pathPrefix, autoOpen) {
1836
- if (!query)
1837
- return items;
1838
- const result = [];
1839
- items.forEach((item, index) => {
1840
- const path = pathPrefix ? `${pathPrefix}.${index}` : String(index);
1841
- const id = this.getItemId(item, path);
1842
- const children = this.getChildItems(item);
1843
- const filteredChildren = this.filterTree(children, query, path, autoOpen);
1844
- const selfMatch = this.matchesQuery(item, query);
1845
- const childMatch = filteredChildren.length > 0;
1846
- if (selfMatch || childMatch) {
1847
- if (childMatch) {
1848
- autoOpen.add(id);
1849
- }
1850
- if (childMatch && filteredChildren !== children) {
1851
- result.push({
1852
- ...item,
1853
- children: filteredChildren,
1854
- });
1855
- }
1856
- else {
1857
- result.push(item);
1858
- }
1859
- }
1860
- });
1861
- return result;
1862
- }
1863
- handleItemSelect = (e, item, id) => {
1864
- if (item.disabled) {
1865
- e.preventDefault();
1866
- e.stopPropagation();
1867
- return;
1868
- }
1869
- const emitted = this.leNavItemSelect.emit({
1870
- item,
1871
- id,
1872
- href: item.href,
1873
- originalEvent: e,
1874
- });
1875
- if (emitted.defaultPrevented) {
1876
- e.preventDefault();
1877
- }
1878
- };
1879
- handleToggle = (e, item, id) => {
1880
- e.preventDefault();
1881
- e.stopPropagation();
1882
- if (item.disabled)
1883
- return;
1884
- const next = !this.isOpen(item, id);
1885
- this.setOpen(id, next);
1886
- this.leNavItemToggle.emit({
1887
- item,
1888
- id,
1889
- open: next,
1890
- originalEvent: e,
1891
- });
1892
- };
1893
- handleSearchInput = (e) => {
1894
- const target = e.target;
1895
- this.searchQuery = target.value;
1896
- };
1897
- handleSubmenuSearchInput = (submenuId, e) => {
1898
- const target = e.target;
1899
- const value = target.value;
1900
- if (this.submenuQueries[submenuId] === value)
1901
- return;
1902
- this.submenuQueries = {
1903
- ...this.submenuQueries,
1904
- [submenuId]: value,
1905
- };
1906
- // Position may change as items filter.
1907
- requestAnimationFrame(() => this.popoverRefs.get(submenuId)?.updatePosition());
1908
- };
1909
- handleBarOverflowChange = (e) => {
1910
- this.overflowIds = e.detail.overflowingIds || [];
1911
- this.hamburgerActive = e.detail.hamburgerActive || false;
1912
- };
1913
- openOverflowPopover = () => {
1914
- this.overflowPopoverOpen = true;
1915
- };
1916
- closeOverflowPopover = () => {
1917
- this.overflowPopoverOpen = false;
1918
- };
1919
- renderVerticalList(items, { depth, pathPrefix, autoOpenIds, searchable, searchQuery, searchPlaceholder, emptyText, submenuId, closePopover, }) {
1920
- const query = searchQuery ?? '';
1921
- const openFromSearch = autoOpenIds ?? new Set();
1922
- const filtered = query ? this.filterTree(items, query, pathPrefix, openFromSearch) : items;
1923
- return (index.h("div", { class: utils.classnames('nav-vertical', { 'is-submenu': !!submenuId }) }, searchable && (index.h("div", { class: "nav-search" }, index.h("le-string-input", { mode: "default", class: "nav-search-input", placeholder: searchPlaceholder ?? 'Search...', value: query, onInput: (e) => submenuId ? this.handleSubmenuSearchInput(submenuId, e) : this.handleSearchInput(e) }))), filtered.length === 0 ? (index.h("div", { class: "nav-empty" }, emptyText ?? this.emptyText)) : (index.h("ul", { class: "nav-list", role: "tree" }, filtered.map((item, index$1) => {
1924
- const path = pathPrefix ? `${pathPrefix}.${index$1}` : String(index$1);
1925
- const id = this.getItemId(item, path);
1926
- const children = this.getChildItems(item);
1927
- const hasChildren = children.length > 0;
1928
- const open = hasChildren && (this.isOpen(item, id) || openFromSearch.has(id));
1929
- const paddingLeft = `calc(var(--le-nav-item-padding-x) + ${depth} * var(--le-spacing-4))`;
1930
- const TagType = item.href && !item.disabled ? 'a' : 'button';
1931
- const attrs = TagType === 'a'
1932
- ? { href: item.href, role: 'treeitem' }
1933
- : { type: 'button', role: 'treeitem' };
1934
- const itemPart = this.partFromOptionPart('item', item.part);
1935
- return (index.h("li", { class: utils.classnames('nav-node', {
1936
- 'disabled': item.disabled,
1937
- 'selected': item.selected || (this.activeUrl && item.href === this.activeUrl),
1938
- open,
1939
- 'has-children': hasChildren,
1940
- }), key: id, role: "none" }, index.h("div", { class: "nav-row", style: { paddingLeft } }, hasChildren ? (index.h("button", { type: "button", class: "nav-toggle", "aria-label": open ? 'Collapse' : 'Expand', "aria-expanded": open ? 'true' : 'false', onClick: (e) => this.handleToggle(e, item, id), disabled: item.disabled }, index.h("le-icon", { name: "chevron-down", class: "nav-chevron", "aria-hidden": "true" }))) : (index.h("span", { class: "nav-toggle-spacer", "aria-hidden": "true" })), index.h(TagType, { class: "nav-item", part: itemPart, ...attrs, "aria-disabled": item.disabled ? 'true' : undefined, onClick: (e) => {
1941
- // For buttons, also toggle if this is a purely structural node.
1942
- this.handleItemSelect(e, item, id);
1943
- if (!item.href && hasChildren && !item.disabled) {
1944
- this.handleToggle(e, item, id);
1945
- return;
1946
- }
1947
- if (!item.disabled && closePopover) {
1948
- closePopover();
1949
- }
1950
- } }, item.iconStart && (index.h("span", { class: "nav-icon", "aria-hidden": "true" }, item.iconStart)), index.h("span", { class: "nav-text" }, index.h("span", { class: "nav-label" }, item.label), item.description && (index.h("span", { class: "nav-description" }, item.description))), item.iconEnd && (index.h("span", { class: "nav-icon nav-icon-end", "aria-hidden": "true" }, item.iconEnd)))), hasChildren && (index.h("le-collapse", { class: "nav-children", closed: !open, noFading: true, role: "group" }, this.renderVerticalList(children, {
1951
- depth: depth + 1,
1952
- pathPrefix: path,
1953
- autoOpenIds: openFromSearch,
1954
- submenuId,
1955
- closePopover,
1956
- })))));
1957
- })))));
1958
- }
1959
- renderHorizontalItem(item, index$1) {
1960
- const id = this.getItemId(item, String(index$1));
1961
- const children = this.getChildItems(item);
1962
- const hasChildren = children.length > 0;
1963
- if (!hasChildren) {
1964
- const TagType = item.href && !item.disabled ? 'a' : 'button';
1965
- const attrs = TagType === 'a'
1966
- ? { href: item.href, role: 'menuitem' }
1967
- : { type: 'button', role: 'menuitem' };
1968
- const itemPart = this.partFromOptionPart('item', item.part);
1969
- return (index.h("div", { class: "h-item", "data-bar-id": id }, index.h(TagType, { class: utils.classnames('h-link', {
1970
- disabled: item.disabled,
1971
- selected: item.selected || (this.activeUrl && item.href === this.activeUrl),
1972
- }), part: itemPart, ...attrs, "aria-disabled": item.disabled ? 'true' : undefined, onClick: (e) => this.handleItemSelect(e, item, id) }, item.iconStart && (index.h("span", { class: "nav-icon", "aria-hidden": "true" }, item.iconStart)), index.h("span", { class: "h-label" }, item.label), item.iconEnd && (index.h("span", { class: "nav-icon nav-icon-end", "aria-hidden": "true" }, item.iconEnd)))));
1973
- }
1974
- const submenuId = id;
1975
- const itemPart = this.partFromOptionPart('item', item.part);
1976
- return (index.h("div", { class: "h-item", "data-bar-id": id }, index.h("le-popover", { ref: el => {
1977
- if (el)
1978
- this.popoverRefs.set(submenuId, el);
1979
- }, mode: "default", offset: 8, showClose: false, closeOnClickOutside: true, closeOnEscape: true, position: "bottom", align: "start", minWidth: "240px" }, index.h("div", { slot: "trigger", class: utils.classnames('h-trigger', {
1980
- disabled: item.disabled,
1981
- selected: item.selected || (this.activeUrl && item.href === this.activeUrl),
1982
- }), part: itemPart, role: "menuitem", "aria-disabled": item.disabled ? 'true' : undefined, onClick: (e) => {
1983
- // Don’t let le-popover auto-toggle from its internal wrapper.
1984
- e.stopPropagation();
1985
- if (item.disabled)
1986
- return;
1987
- if (item.href) {
1988
- this.handleItemSelect(e, item, id);
1989
- this.popoverRefs.get(submenuId)?.hide();
1990
- }
1991
- else {
1992
- this.popoverRefs.get(submenuId)?.toggle();
1993
- }
1994
- } }, item.href ? (index.h("a", { class: "h-link", href: item.href, onClick: (e) => {
1995
- e.stopPropagation();
1996
- this.handleItemSelect(e, item, id);
1997
- } }, item.iconStart && (index.h("span", { class: "nav-icon", "aria-hidden": "true" }, item.iconStart)), index.h("span", { class: "h-label" }, item.label), index.h("span", { class: "nav-chevron", "aria-hidden": "true" }, index.h("le-icon", { name: "chevron-down" })))) : (index.h("button", { type: "button", class: "h-link", onClick: (e) => {
1998
- e.stopPropagation();
1999
- if (item.disabled)
2000
- return;
2001
- this.popoverRefs.get(submenuId)?.toggle();
2002
- } }, item.iconStart && (index.h("span", { class: "nav-icon", "aria-hidden": "true" }, item.iconStart)), index.h("span", { class: "h-label" }, item.label), index.h("span", { class: "nav-chevron", "aria-hidden": "true" }, index.h("le-icon", { name: "chevron-down" }))))), index.h("div", { class: "popover-menu" }, this.renderVerticalList(children, {
2003
- depth: 0,
2004
- pathPrefix: String(index$1),
2005
- searchable: this.submenuSearchable,
2006
- searchQuery: this.submenuQueries[submenuId] ?? '',
2007
- searchPlaceholder: this.searchPlaceholder,
2008
- emptyText: this.emptyText,
2009
- submenuId,
2010
- closePopover: () => this.popoverRefs.get(submenuId)?.hide(),
2011
- })))));
2012
- }
2013
- getOverflowMode() {
2014
- if (this.wrap)
2015
- return 'wrap';
2016
- return this.overflowMode;
2017
- }
2018
- getBarAlignment() {
2019
- // Map le-navigation align to le-bar alignItems
2020
- // 'space-between' doesn't map directly, use 'stretch' as closest
2021
- if (this.align === 'space-between')
2022
- return 'stretch';
2023
- return this.align;
2024
- }
2025
- renderOverflowPopover() {
2026
- const items = this.parsedItems;
2027
- const overflowSet = new Set(this.overflowIds || []);
2028
- // Determine which items to show in the popover
2029
- let itemsToShow;
2030
- if (this.hamburgerActive) {
2031
- // In hamburger mode, show all items
2032
- itemsToShow = items;
2033
- }
2034
- else {
2035
- // In "more" mode, show only overflow items
2036
- itemsToShow = items.filter((item, index) => {
2037
- const id = this.getItemId(item, String(index));
2038
- return overflowSet.has(id);
2039
- });
2040
- }
2041
- if (itemsToShow.length === 0)
2042
- return null;
2043
- const isHamburger = this.hamburgerActive;
2044
- return (index.h("le-popover", { mode: "default", offset: 8, open: this.overflowPopoverOpen, showClose: false, closeOnClickOutside: true, closeOnEscape: true, position: "bottom", align: "end", minWidth: "260px", onLePopoverClose: this.closeOverflowPopover }, index.h("button", { slot: "trigger", type: "button", class: "overflow-trigger", part: isHamburger ? 'hamburger-trigger' : 'more-trigger', "aria-label": isHamburger ? 'Open menu' : 'More', onClick: this.openOverflowPopover }, index.h("slot", { name: isHamburger ? 'hamburger-trigger' : 'more-trigger' }, index.h("le-icon", { name: isHamburger ? 'hamburger' : 'ellipsis-horizontal' }))), index.h("div", { class: "popover-menu" }, this.renderVerticalList(itemsToShow, {
2045
- depth: 0,
2046
- pathPrefix: '',
2047
- closePopover: this.closeOverflowPopover,
2048
- }))));
2049
- }
2050
- renderHorizontal() {
2051
- const items = this.parsedItems;
2052
- const overflowMode = this.getOverflowMode();
2053
- const showOverflowButton = (this.overflowIds?.length ?? 0) > 0 || this.hamburgerActive;
2054
- return (index.h("div", { class: "nav-horizontal-wrapper" }, index.h("le-bar", { class: utils.classnames('nav-bar', {
2055
- 'align-end': this.align === 'end',
2056
- 'align-center': this.align === 'center',
2057
- 'align-space-between': this.align === 'space-between',
2058
- }), overflow: overflowMode, alignItems: this.getBarAlignment(), disablePopover: true, minVisibleItems: this.minVisibleItemsForMore, onLeBarOverflowChange: this.handleBarOverflowChange }, items.map((item, index) => this.renderHorizontalItem(item, index))), showOverflowButton && this.renderOverflowPopover()));
2059
- }
2060
- render() {
2061
- const items = this.parsedItems;
2062
- if (this.orientation === 'horizontal') {
2063
- return (index.h(index.Host, null, index.h("le-component", { component: "le-navigation" }, this.renderHorizontal())));
2064
- }
2065
- return (index.h(index.Host, null, index.h("le-component", { component: "le-navigation" }, this.renderVerticalList(items, {
2066
- depth: 0,
2067
- pathPrefix: '',
2068
- searchable: this.searchable,
2069
- searchQuery: this.searchQuery,
2070
- searchPlaceholder: this.searchPlaceholder,
2071
- emptyText: this.emptyText,
2072
- }))));
2073
- }
2074
- static get watchers() { return {
2075
- "items": ["handleLayoutInputsChange"],
2076
- "orientation": ["handleLayoutInputsChange"],
2077
- "wrap": ["handleLayoutInputsChange"],
2078
- "overflowMode": ["handleLayoutInputsChange"]
2079
- }; }
2080
- };
2081
- LeNavigation.style = leNavigationCss();
2082
-
2083
- const lePopoverCss = () => `/* ============================================
2084
- le-popover.css
2085
- Popover using native HTML Popover API
2086
- ============================================ */
2087
-
2088
- :host {
2089
- display: inline-block;
2090
- position: relative;
2091
- }
2092
-
2093
- :host([trigger-full-width]) {
2094
- display: block;
2095
- width: 100%;
2096
- }
2097
-
2098
- /* ============================================
2099
- Trigger
2100
- ============================================ */
2101
-
2102
- .le-popover-trigger {
2103
- display: inline-flex;
2104
- cursor: pointer;
2105
- }
2106
-
2107
- .le-popover-trigger-full-width {
2108
- display: flex;
2109
- width: 100%;
2110
- }
2111
-
2112
- .le-popover-default-trigger {
2113
- display: flex;
2114
- align-items: center;
2115
- justify-content: center;
2116
- width: 28px;
2117
- height: 28px;
2118
- padding: 0;
2119
- border: 1px solid var(--le-color-border, #e0e0e0);
2120
- border-radius: var(--le-radius-md, 6px);
2121
- background: var(--le-color-surface, #fff);
2122
- color: var(--le-color-text-secondary, #666);
2123
- font-size: 16px;
2124
- cursor: pointer;
2125
- transition: all var(--le-transition-fast, 0.15s ease);
2126
- }
2127
-
2128
- .le-popover-default-trigger:hover {
2129
- border-color: var(--le-color-primary, #2196f3);
2130
- color: var(--le-color-primary, #2196f3);
2131
- background: var(--le-color-primary-light, rgba(33, 150, 243, 0.1));
2132
- }
2133
-
2134
- /* ============================================
2135
- Popover Content (native popover)
2136
- ============================================ */
2137
-
2138
- .le-popover-content {
2139
- /* Reset native popover defaults */
2140
- margin: 0;
2141
- padding: 0;
2142
- border: none;
2143
- background: transparent;
2144
-
2145
- /* Positioning - will be set via JS */
2146
- position: fixed;
2147
- inset: unset;
2148
-
2149
- /* Styling */
2150
- background: var(--le-color-surface, #ffffff);
2151
- border: 1px solid var(--le-color-border, #e0e0e0);
2152
- border-radius: var(--le-radius-lg, 8px);
2153
- box-shadow: var(--le-shadow-lg, 0 4px 12px rgba(0, 0, 0, 0.15));
2154
- overflow: hidden;
2155
- font-family: var(--le-font-family, system-ui, -apple-system, sans-serif);
2156
- font-size: var(--le-font-size-sm, 0.875rem);
2157
- color: var(--le-color-text, #333);
2158
-
2159
- /* Animation */
2160
- opacity: 0;
2161
- transform: scale(0.95);
2162
- transition: opacity 0.15s ease, transform 0.15s ease, display 0.15s ease allow-discrete;
2163
- }
2164
-
2165
- /* When popover is open */
2166
- .le-popover-content:popover-open {
2167
- opacity: 1;
2168
- transform: scale(1);
2169
- }
2170
-
2171
- /* Fallback for browsers without the Popover API */
2172
- .le-popover-content[data-fallback-open="false"] {
2173
- display: none;
2174
- }
2175
-
2176
- .le-popover-content[data-fallback-open="true"] {
2177
- opacity: 1;
2178
- transform: scale(1);
2179
- }
2180
-
2181
- /* Starting style for animation (CSS Anchor Positioning spec) */
2182
- @starting-style {
2183
- .le-popover-content:popover-open {
2184
- opacity: 0;
2185
- transform: scale(0.95);
2186
- }
2187
- }
2188
-
2189
- /* ============================================
2190
- Header
2191
- ============================================ */
2192
-
2193
- .le-popover-header {
2194
- display: flex;
2195
- align-items: center;
2196
- justify-content: space-between;
2197
- padding: var(--le-space-xs, 4px) var(--le-space-xs, 4px) var(--le-space-xs, 4px) var(--le-space-sm, 8px);
2198
- border-bottom: 1px solid var(--le-color-border, #e0e0e0);
2199
- background: var(--le-color-surface-alt, #f9f9f9);
2200
- min-height: 32px;
2201
- }
2202
-
2203
- .le-popover-title {
2204
- font-weight: var(--le-font-weight-semibold, 600);
2205
- font-size: var(--le-font-size-sm, 0.875rem);
2206
- color: var(--le-color-text, #333);
2207
- }
2208
-
2209
- .le-popover-close {
2210
- display: flex;
2211
- align-items: center;
2212
- justify-content: center;
2213
- width: 24px;
2214
- height: 24px;
2215
- padding: 0;
2216
- border: none;
2217
- background: transparent;
2218
- color: var(--le-color-text-secondary, #666);
2219
- font-size: 18px;
2220
- line-height: 1;
2221
- cursor: pointer;
2222
- border-radius: var(--le-radius-sm, 4px);
2223
- transition: background-color 0.15s, color 0.15s;
2224
- }
2225
-
2226
- .le-popover-close:hover {
2227
- background: var(--le-color-surface-hover, rgba(0, 0, 0, 0.05));
2228
- color: var(--le-color-text, #333);
2229
- }
2230
-
2231
- /* ============================================
2232
- Body
2233
- ============================================ */
2234
-
2235
- .le-popover-body {
2236
- padding: var(--le-space-md, 12px);
2237
- }
2238
-
2239
- /* ============================================
2240
- Scrollable content
2241
- ============================================ */
2242
-
2243
- .le-popover-content[style*="overflow-y: auto"] .le-popover-body {
2244
- overflow-y: auto;
2245
- }
2246
- `;
2247
-
2248
- // Keep a simple stack so Escape closes the most recently opened popover first.
2249
- // This also helps nested popovers behave naturally.
2250
- const openPopoverStack = [];
2251
- const LePopover = class {
2252
- constructor(hostRef) {
2253
- index.registerInstance(this, hostRef);
2254
- this.lePopoverOpen = index.createEvent(this, "lePopoverOpen");
2255
- this.lePopoverClose = index.createEvent(this, "lePopoverClose");
2256
- }
2257
- get el() { return index.getElement(this); }
2258
- /**
2259
- * Mode of the popover should be 'default' for internal use
2260
- */
2261
- mode;
2262
- /**
2263
- * Whether the popover is currently open
2264
- */
2265
- open = false;
2266
- /**
2267
- * Position of the popover relative to its trigger
2268
- */
2269
- position = 'bottom';
2270
- /**
2271
- * Alignment of the popover
2272
- */
2273
- align = 'start';
2274
- /**
2275
- * Optional title for the popover header
2276
- */
2277
- popoverTitle;
2278
- /**
2279
- * Whether to show a close button in the header
2280
- */
2281
- showClose = true;
2282
- /**
2283
- * Whether clicking outside closes the popover
2284
- */
2285
- closeOnClickOutside = true;
2286
- /**
2287
- * Whether pressing Escape closes the popover
2288
- */
2289
- closeOnEscape = true;
2290
- /**
2291
- * Offset from the trigger element (in pixels)
2292
- */
2293
- offset = 8;
2294
- /**
2295
- * Fixed width for the popover (e.g., '300px', '20rem')
2296
- */
2297
- width;
2298
- /**
2299
- * Minimum width for the popover (e.g., '200px', '15rem')
2300
- */
2301
- minWidth = '200px';
2302
- /**
2303
- * Maximum width for the popover (e.g., '400px', '25rem')
2304
- */
2305
- maxWidth;
2306
- /**
2307
- * Should the popover's trigger take full width of its container
2308
- */
2309
- triggerFullWidth = false;
2310
- /**
2311
- * Emitted when the popover opens
2312
- */
2313
- lePopoverOpen;
2314
- /**
2315
- * Emitted when the popover closes
2316
- */
2317
- lePopoverClose;
2318
- /**
2319
- * Method to update the popover position from a parent component
2320
- */
2321
- async updatePosition() {
2322
- this._updatePosition();
2323
- }
2324
- isPositioned = false;
2325
- triggerEl;
2326
- popoverEl;
2327
- uniqueId = `le-popover-${Math.random().toString(36).substr(2, 9)}`;
2328
- scrollParents = [];
2329
- isListeningForDismiss = false;
2330
- get supportsPopoverApi() {
2331
- return typeof HTMLElement.prototype.showPopover === 'function';
2332
- }
2333
- shadowContains(container, node) {
2334
- let current = node;
2335
- while (current) {
2336
- if (current === container)
2337
- return true;
2338
- if (current instanceof ShadowRoot) {
2339
- current = current.host;
2340
- continue;
2341
- }
2342
- if (current.parentNode) {
2343
- current = current.parentNode;
2344
- continue;
2345
- }
2346
- const root = current.getRootNode?.();
2347
- if (root instanceof ShadowRoot) {
2348
- current = root.host;
2349
- continue;
2350
- }
2351
- break;
2352
- }
2353
- return false;
2354
- }
2355
- componentDidLoad() {
2356
- // Listen for toggle events from the native popover API
2357
- this.popoverEl?.addEventListener('toggle', this.handlePopoverToggle);
2358
- // Listen for other popovers opening to close this one
2359
- document.addEventListener('le-popover-will-open', this.handleOtherPopoverOpen);
2360
- // If the popover is initially open (unlikely, but possible), wire listeners.
2361
- if (this.open) {
2362
- this.addDismissListeners();
2363
- }
2364
- }
2365
- disconnectedCallback() {
2366
- this.popoverEl?.removeEventListener('toggle', this.handlePopoverToggle);
2367
- document.removeEventListener('le-popover-will-open', this.handleOtherPopoverOpen);
2368
- this.removeScrollListeners();
2369
- this.removeDismissListeners();
2370
- }
2371
- addDismissListeners() {
2372
- if (this.isListeningForDismiss)
2373
- return;
2374
- // Use capture so clicks inside the trigger (which stops propagation) are still observed.
2375
- document.addEventListener('pointerdown', this.handleDocumentPointerDown, true);
2376
- document.addEventListener('keydown', this.handleDocumentKeyDown, true);
2377
- this.isListeningForDismiss = true;
2378
- }
2379
- removeDismissListeners() {
2380
- if (!this.isListeningForDismiss)
2381
- return;
2382
- document.removeEventListener('pointerdown', this.handleDocumentPointerDown, true);
2383
- document.removeEventListener('keydown', this.handleDocumentKeyDown, true);
2384
- this.isListeningForDismiss = false;
2385
- }
2386
- handleDocumentPointerDown = (event) => {
2387
- if (!this.open || !this.closeOnClickOutside)
2388
- return;
2389
- // If the click happens inside this popover component (trigger OR content), don't close.
2390
- const path = (event.composedPath?.() ?? []);
2391
- if (path.includes(this.el))
2392
- return;
2393
- this.hide();
2394
- };
2395
- handleDocumentKeyDown = (event) => {
2396
- if (!this.open || !this.closeOnEscape)
2397
- return;
2398
- if (event.key !== 'Escape')
2399
- return;
2400
- // Only the top-most opened popover handles Escape.
2401
- const top = openPopoverStack[openPopoverStack.length - 1];
2402
- if (top !== this.el)
2403
- return;
2404
- event.preventDefault();
2405
- event.stopPropagation();
2406
- this.hide();
2407
- };
2408
- /**
2409
- * Find all scrollable parent elements
2410
- */
2411
- getScrollParents(element) {
2412
- const scrollParents = [];
2413
- let parent = element.parentElement;
2414
- while (parent) {
2415
- const style = getComputedStyle(parent);
2416
- const overflow = style.overflow + style.overflowY + style.overflowX;
2417
- if (/(auto|scroll)/.test(overflow)) {
2418
- scrollParents.push(parent);
2419
- }
2420
- parent = parent.parentElement;
2421
- }
2422
- // Always include window for page scroll
2423
- return scrollParents;
2424
- }
2425
- /**
2426
- * Add scroll listeners to all scrollable parents
2427
- */
2428
- addScrollListeners() {
2429
- if (!this.triggerEl)
2430
- return;
2431
- this.scrollParents = this.getScrollParents(this.triggerEl);
2432
- // Listen to each scroll parent
2433
- this.scrollParents.forEach(parent => {
2434
- parent.addEventListener('scroll', this.handleScroll, { passive: true });
2435
- });
2436
- // Also listen to window scroll and resize
2437
- window.addEventListener('scroll', this.handleScroll, { passive: true });
2438
- window.addEventListener('resize', this.handleScroll, { passive: true });
2439
- }
2440
- /**
2441
- * Remove scroll listeners
2442
- */
2443
- removeScrollListeners() {
2444
- this.scrollParents.forEach(parent => {
2445
- parent.removeEventListener('scroll', this.handleScroll);
2446
- });
2447
- window.removeEventListener('scroll', this.handleScroll);
2448
- window.removeEventListener('resize', this.handleScroll);
2449
- this.scrollParents = [];
2450
- }
2451
- handleScroll = () => {
2452
- if (this.open) {
2453
- this._updatePosition();
2454
- }
2455
- };
2456
- handleOpened() {
2457
- this.open = true;
2458
- // Track stack order for Escape handling.
2459
- const existingIndex = openPopoverStack.indexOf(this.el);
2460
- if (existingIndex >= 0)
2461
- openPopoverStack.splice(existingIndex, 1);
2462
- openPopoverStack.push(this.el);
2463
- this.addDismissListeners();
2464
- this.addScrollListeners();
2465
- this._updatePosition();
2466
- this.lePopoverOpen.emit();
2467
- }
2468
- handleClosed() {
2469
- this.open = false;
2470
- this.isPositioned = false;
2471
- this.removeScrollListeners();
2472
- this.removeDismissListeners();
2473
- const index = openPopoverStack.indexOf(this.el);
2474
- if (index >= 0)
2475
- openPopoverStack.splice(index, 1);
2476
- this.lePopoverClose.emit();
2477
- }
2478
- handlePopoverToggle = (event) => {
2479
- if (event.newState === 'open') {
2480
- this.handleOpened();
2481
- }
2482
- else {
2483
- this.handleClosed();
2484
- }
2485
- };
2486
- handleOtherPopoverOpen = (event) => {
2487
- const customEvent = event;
2488
- const openingPopover = customEvent.detail?.popover;
2489
- if (!openingPopover)
2490
- return;
2491
- if (openingPopover === this.el)
2492
- return;
2493
- // Allow nested popovers (e.g., le-select inside another popover).
2494
- // Use a shadow-DOM-aware containment check.
2495
- if (this.shadowContains(this.el, openingPopover) ||
2496
- this.shadowContains(openingPopover, this.el)) {
2497
- return;
2498
- }
2499
- if (this.open) {
2500
- this.hide();
2501
- }
2502
- };
2503
- /**
2504
- * Opens the popover
2505
- */
2506
- async show() {
2507
- document.dispatchEvent(new CustomEvent('le-popover-will-open', {
2508
- detail: { popover: this.el },
2509
- }));
2510
- if (this.supportsPopoverApi) {
2511
- this.popoverEl?.showPopover();
2512
- }
2513
- else {
2514
- this.handleOpened();
2515
- }
2516
- }
2517
- /**
2518
- * Closes the popover
2519
- */
2520
- async hide() {
2521
- if (this.supportsPopoverApi) {
2522
- this.popoverEl?.hidePopover();
2523
- }
2524
- else {
2525
- this.handleClosed();
2526
- }
2527
- }
2528
- /**
2529
- * Toggles the popover
2530
- */
2531
- async toggle() {
2532
- if (this.open) {
2533
- await this.hide();
2534
- }
2535
- else {
2536
- await this.show();
2537
- }
2538
- }
2539
- handleTriggerClick = (event) => {
2540
- event.stopPropagation();
2541
- this.toggle();
2542
- };
2543
- _updatePosition() {
2544
- if (!this.triggerEl || !this.popoverEl)
2545
- return;
2546
- const triggerRect = this.triggerEl.getBoundingClientRect();
2547
- const popoverRect = this.popoverEl.getBoundingClientRect();
2548
- const viewportWidth = window.innerWidth;
2549
- const viewportHeight = window.innerHeight;
2550
- const viewportPadding = 8;
2551
- let position = this.position;
2552
- let align = this.align;
2553
- // Auto-position logic
2554
- const spaceBelow = viewportHeight - triggerRect.bottom - viewportPadding;
2555
- const spaceAbove = triggerRect.top - viewportPadding;
2556
- const spaceRight = viewportWidth - triggerRect.right - viewportPadding;
2557
- const spaceLeft = triggerRect.left - viewportPadding;
2558
- if (position === 'auto') {
2559
- if (spaceBelow >= popoverRect.height + this.offset) {
2560
- position = 'bottom';
2561
- }
2562
- else if (spaceAbove >= popoverRect.height + this.offset) {
2563
- position = 'top';
2564
- }
2565
- else if (spaceRight >= popoverRect.width + this.offset) {
2566
- position = 'right';
2567
- }
2568
- else if (spaceLeft >= popoverRect.width + this.offset) {
2569
- position = 'left';
2570
- }
2571
- else {
2572
- const maxSpace = Math.max(spaceBelow, spaceAbove, spaceRight, spaceLeft);
2573
- if (maxSpace === spaceBelow)
2574
- position = 'bottom';
2575
- else if (maxSpace === spaceAbove)
2576
- position = 'top';
2577
- else if (maxSpace === spaceRight)
2578
- position = 'right';
2579
- else
2580
- position = 'left';
2581
- }
2582
- }
2583
- // Adjust alignment for horizontal overflow
2584
- if (position === 'top' || position === 'bottom') {
2585
- if (align === 'start' &&
2586
- triggerRect.left + popoverRect.width > viewportWidth - viewportPadding) {
2587
- align = 'end';
2588
- }
2589
- else if (align === 'end' && triggerRect.right - popoverRect.width < viewportPadding) {
2590
- align = 'start';
2591
- }
2592
- else if (align === 'center') {
2593
- const triggerCenter = triggerRect.left + triggerRect.width / 2;
2594
- if (triggerCenter - popoverRect.width / 2 < viewportPadding) {
2595
- align = 'start';
2596
- }
2597
- else if (triggerCenter + popoverRect.width / 2 > viewportWidth - viewportPadding) {
2598
- align = 'end';
2599
- }
2600
- }
2601
- }
2602
- // Calculate position
2603
- let top = 0;
2604
- let left = 0;
2605
- let maxHeight = null;
2606
- switch (position) {
2607
- case 'top':
2608
- top = triggerRect.top - popoverRect.height - this.offset;
2609
- if (top < viewportPadding) {
2610
- maxHeight = triggerRect.top - this.offset - viewportPadding * 2;
2611
- top = viewportPadding;
2612
- }
2613
- break;
2614
- case 'bottom':
2615
- top = triggerRect.bottom + this.offset;
2616
- if (top + popoverRect.height > viewportHeight - viewportPadding) {
2617
- maxHeight = viewportHeight - top - viewportPadding;
2618
- }
2619
- break;
2620
- case 'left':
2621
- left = triggerRect.left - popoverRect.width - this.offset;
2622
- top = triggerRect.top;
2623
- if (left < viewportPadding)
2624
- left = viewportPadding;
2625
- break;
2626
- case 'right':
2627
- left = triggerRect.right + this.offset;
2628
- top = triggerRect.top;
2629
- if (left + popoverRect.width > viewportWidth - viewportPadding) {
2630
- left = viewportWidth - popoverRect.width - viewportPadding;
2631
- }
2632
- break;
2633
- }
2634
- // Calculate horizontal alignment for top/bottom
2635
- if (position === 'top' || position === 'bottom') {
2636
- switch (align) {
2637
- case 'start':
2638
- left = triggerRect.left;
2639
- break;
2640
- case 'center':
2641
- left = triggerRect.left + triggerRect.width / 2 - popoverRect.width / 2;
2642
- break;
2643
- case 'end':
2644
- left = triggerRect.right - popoverRect.width;
2645
- break;
2646
- }
2647
- // Constrain to viewport
2648
- if (left < viewportPadding) {
2649
- left = viewportPadding;
2650
- }
2651
- else if (left + popoverRect.width > viewportWidth - viewportPadding) {
2652
- left = viewportWidth - popoverRect.width - viewportPadding;
2653
- }
2654
- }
2655
- // Calculate vertical alignment for left/right
2656
- if (position === 'left' || position === 'right') {
2657
- switch (align) {
2658
- case 'start':
2659
- top = triggerRect.top;
2660
- break;
2661
- case 'center':
2662
- top = triggerRect.top + triggerRect.height / 2 - popoverRect.height / 2;
2663
- break;
2664
- case 'end':
2665
- top = triggerRect.bottom - popoverRect.height;
2666
- break;
2667
- }
2668
- if (top < viewportPadding)
2669
- top = viewportPadding;
2670
- if (top + popoverRect.height > viewportHeight - viewportPadding) {
2671
- maxHeight = viewportHeight - top - viewportPadding;
2672
- }
2673
- }
2674
- // Apply styles
2675
- this.popoverEl.style.top = `${top}px`;
2676
- this.popoverEl.style.left = `${left}px`;
2677
- if (maxHeight !== null && maxHeight > 100) {
2678
- this.popoverEl.style.maxHeight = `${maxHeight}px`;
2679
- this.popoverEl.style.overflowY = 'auto';
2680
- }
2681
- else {
2682
- this.popoverEl.style.maxHeight = '';
2683
- this.popoverEl.style.overflowY = '';
2684
- }
2685
- this.isPositioned = true;
2686
- }
2687
- render() {
2688
- const popoverStyles = {
2689
- visibility: this.isPositioned ? 'visible' : 'hidden',
2690
- };
2691
- if (this.width)
2692
- popoverStyles.width = this.width;
2693
- if (this.minWidth)
2694
- popoverStyles.minWidth = this.minWidth;
2695
- if (this.maxWidth)
2696
- popoverStyles.maxWidth = this.maxWidth;
2697
- return (index.h(index.Host, { key: '542d4ab391a9b176d110fe00c0dd61f2c46d73ef', "trigger-full-width": this.triggerFullWidth }, index.h("div", { key: '2248ab763d65e8a9150e62644452085f01c6a238', class: utils.classnames('le-popover-trigger', {
2698
- 'le-popover-trigger-full-width': this.triggerFullWidth,
2699
- }), ref: el => (this.triggerEl = el), onClick: this.handleTriggerClick, part: "trigger" }, index.h("slot", { key: '9d9a5f05f4b9f1b11f1fcbb09d29f0c1e36a2cd6', name: "trigger" }, index.h("button", { key: 'ed573daefaffb7b1effb3c7891722aa40e4b606e', type: "button", class: "le-popover-default-trigger" }, index.h("span", { key: '61f00ea9761935224e1529adc3b102b85efd4f33' }, "\u2295")))), index.h("div", { key: 'bf447493c9c7dedb7f2ae7083c80a7a2d06aebe0', id: this.uniqueId, class: "le-popover-content",
2700
- // Always use manual mode so nested popovers can be open together.
2701
- // We implement click-outside and Escape handling ourselves.
2702
- popover: "manual", ref: el => (this.popoverEl = el), style: popoverStyles, "data-fallback-open": this.supportsPopoverApi ? undefined : String(this.open) }, (this.popoverTitle || this.showClose) && (index.h("div", { key: '50fe79bb415b5de315aea7f766d57ae911a90343', class: "le-popover-header" }, this.popoverTitle && index.h("span", { key: 'd809f12c512197bd34ca215a05820b533f4d8734', class: "le-popover-title" }, this.popoverTitle), this.showClose && (index.h("button", { key: '2baee3e910ad65243a66eee2bba1c46d9fa628b4', type: "button", class: "le-popover-close", onClick: () => this.hide(), "aria-label": "Close" }, "\u00D7")))), index.h("div", { key: 'eacad60a20becde7f4f8b9fa01d195dd3f4c08f1', class: "le-popover-body", part: "content" }, index.h("slot", { key: 'f8aa2edd170ee039d5277a1b9ebc8ee077a1a9dc' })))));
2703
- }
2704
- };
2705
- LePopover.style = lePopoverCss();
2706
-
2707
- const lePopupCss = () => `:host{display:contents}.le-popup-dialog{position:fixed;z-index:100;top:0;right:0;bottom:0;left:0;padding:0;border:none;background:transparent;max-width:none;max-height:none;overflow:visible;--_popup-min-width:var(--le-popup-min-width, 320px);--_popup-max-width:var(--le-popup-max-width, min(500px, 90vw));--_popup-min-height:var(--le-popup-min-height, auto)}.le-popup-dialog::backdrop{background:var(--le-popup-backdrop-color, rgba(0, 0, 0, 0.5));animation:le-popup-backdrop-fade 0.2s ease-out}@keyframes le-popup-backdrop-fade{from{opacity:0}to{opacity:1}}.le-popup-position-center{margin:auto}.le-popup-position-top{margin:var(--le-space-2xl, 48px) auto auto auto}.le-popup-position-top-left{margin:var(--le-space-lg, 24px) auto auto var(--le-space-lg, 24px)}.le-popup-position-top-right{margin:var(--le-space-lg, 24px) var(--le-space-lg, 24px) auto auto}.le-popup-position-bottom{margin:auto auto var(--le-space-2xl, 48px) auto}.le-popup-position-bottom-left{margin:auto auto var(--le-space-lg, 24px) var(--le-space-lg, 24px)}.le-popup-position-bottom-right{margin:auto var(--le-space-lg, 24px) var(--le-space-lg, 24px) auto}.le-popup-container{display:flex;flex-direction:column;min-width:var(--_popup-min-width);max-width:var(--_popup-max-width);min-height:var(--_popup-min-height);max-height:calc(100vh - var(--le-space-2xl, 48px) * 2);background:var(--le-color-surface, #ffffff);border:1px solid var(--le-color-border, #e0e0e0);border-radius:var(--le-radius-lg, 12px);box-shadow:var(--le-shadow-xl, 0 8px 32px rgba(0, 0, 0, 0.15));overflow:hidden;font-family:var(--le-font-family, system-ui, -apple-system, sans-serif);color:var(--le-color-text, #333);animation:le-popup-appear 0.2s ease-out}@keyframes le-popup-appear{from{opacity:0;transform:scale(0.95) translateY(-10px)}to{opacity:1;transform:scale(1) translateY(0)}}.le-popup-header{flex-shrink:0;padding:var(--le-space-md, 16px) var(--le-space-lg, 24px);border-bottom:1px solid var(--le-color-border, #e0e0e0);background:var(--le-color-surface-alt, #f9f9f9);font-size:var(--le-font-size-lg, 1.125rem);font-weight:var(--le-font-weight-semibold, 600);color:var(--le-color-text, #333)}.le-popup-body{flex:1;padding:var(--le-space-lg, 24px);overflow-y:auto}.le-popup-message{margin:0;font-size:var(--le-font-size-md, 1rem);line-height:var(--le-line-height-relaxed, 1.6);color:var(--le-color-text, #333)}.le-popup-message+::slotted(*){margin-top:var(--le-space-md, 16px)}.le-popup-input{display:block;width:100%;margin-top:var(--le-space-md, 16px);padding:var(--le-space-sm, 8px) var(--le-space-md, 16px);font-family:inherit;font-size:var(--le-font-size-md, 1rem);color:var(--le-color-text, #333);background:var(--le-color-background, #fff);border:1px solid var(--le-color-border, #e0e0e0);border-radius:var(--le-radius-md, 8px);outline:none;transition:border-color var(--le-transition-fast, 0.15s ease),
2708
- box-shadow var(--le-transition-fast, 0.15s ease);box-sizing:border-box}.le-popup-input:focus{border-color:var(--le-color-primary, #2196f3);box-shadow:0 0 0 3px var(--le-color-primary-light, rgba(33, 150, 243, 0.2))}.le-popup-input::placeholder{color:var(--le-color-text-muted, #999)}.le-popup-footer{flex-shrink:0;display:flex;justify-content:flex-end;gap:var(--le-space-sm, 8px);padding:var(--le-space-md, 16px) var(--le-space-lg, 24px);border-top:1px solid var(--le-color-border, #e0e0e0);background:var(--le-color-surface-alt, #f9f9f9)}.le-popup-btn{min-width:80px}@media (max-width: 480px){.le-popup-container{min-width:calc(100vw - var(--le-space-md, 16px) * 2);max-width:calc(100vw - var(--le-space-md, 16px) * 2)}.le-popup-footer{flex-direction:column-reverse}.le-popup-btn{width:100%}}`;
2709
-
2710
- const LePopup = class {
2711
- constructor(hostRef) {
2712
- index.registerInstance(this, hostRef);
2713
- this.leConfirm = index.createEvent(this, "leConfirm");
2714
- this.leCancel = index.createEvent(this, "leCancel");
2715
- this.leOpen = index.createEvent(this, "leOpen");
2716
- this.leClose = index.createEvent(this, "leClose");
2717
- }
2718
- get el() { return index.getElement(this); }
2719
- /**
2720
- * The mode of the Le Kit (e.g., 'default' or 'admin')
2721
- */
2722
- mode = 'default';
2723
- /**
2724
- * Whether the popup is currently visible
2725
- */
2726
- open = false;
2727
- /**
2728
- * Type of popup: alert (OK only), confirm (OK/Cancel), prompt (input + OK/Cancel), custom
2729
- */
2730
- type = 'alert';
2731
- /**
2732
- * Optional title for the popup header
2733
- */
2734
- popupTitle;
2735
- /**
2736
- * Message text to display (for alert/confirm/prompt types)
2737
- */
2738
- message;
2739
- /**
2740
- * Whether the popup is modal (blocks interaction with page behind)
2741
- */
2742
- modal = true;
2743
- /**
2744
- * Position of the popup on screen
2745
- */
2746
- position = 'center';
2747
- /**
2748
- * Text for the confirm/OK button
2749
- */
2750
- confirmText = 'OK';
2751
- /**
2752
- * Text for the cancel button
2753
- */
2754
- cancelText = 'Cancel';
2755
- /**
2756
- * Placeholder text for prompt input
2757
- */
2758
- placeholder = '';
2759
- /**
2760
- * Default value for prompt input
2761
- */
2762
- defaultValue = '';
2763
- /**
2764
- * Whether clicking the backdrop closes the popup (modal only)
2765
- */
2766
- closeOnBackdrop = true;
2767
- /**
2768
- * Internal state for prompt input value
2769
- */
2770
- inputValue = '';
2771
- /**
2772
- * Emitted when the popup is confirmed (OK clicked)
2773
- */
2774
- leConfirm;
2775
- /**
2776
- * Emitted when the popup is cancelled (Cancel clicked or dismissed)
2777
- */
2778
- leCancel;
2779
- /**
2780
- * Emitted when the popup opens
2781
- */
2782
- leOpen;
2783
- /**
2784
- * Emitted when the popup closes
2785
- */
2786
- leClose;
2787
- dialogEl;
2788
- inputEl;
2789
- resolvePromise;
2790
- componentWillLoad() {
2791
- this.inputValue = this.defaultValue;
2792
- }
2793
- componentDidLoad() {
2794
- // Native dialog handles Escape key automatically when modal
2795
- // We just need to listen for the cancel event
2796
- this.dialogEl?.addEventListener('cancel', this.handleDialogCancel);
2797
- }
2798
- disconnectedCallback() {
2799
- this.dialogEl?.removeEventListener('cancel', this.handleDialogCancel);
2800
- }
2801
- handleDialogCancel = (e) => {
2802
- e.preventDefault(); // Prevent default close to handle it ourselves
2803
- this.handleCancel();
2804
- };
2805
- /**
2806
- * Opens the popup and returns a promise that resolves when closed
2807
- */
2808
- async show() {
2809
- return new Promise(resolve => {
2810
- this.resolvePromise = resolve;
2811
- this.inputValue = this.defaultValue;
2812
- this.open = true;
2813
- // Use requestAnimationFrame to ensure the dialog element is rendered
2814
- requestAnimationFrame(() => {
2815
- if (this.dialogEl) {
2816
- if (this.modal) {
2817
- this.dialogEl.showModal();
2818
- }
2819
- else {
2820
- this.dialogEl.show();
2821
- }
2822
- this.leOpen.emit();
2823
- // Focus input for prompt type
2824
- if (this.type === 'prompt' && this.inputEl) {
2825
- this.inputEl.focus();
2826
- this.inputEl.select();
2827
- }
2828
- }
2829
- });
2830
- });
2831
- }
2832
- /**
2833
- * Closes the popup with a result
2834
- */
2835
- async hide(confirmed = false) {
2836
- const result = {
2837
- confirmed,
2838
- value: this.type === 'prompt' ? this.inputValue : undefined,
2839
- };
2840
- this.dialogEl?.close();
2841
- this.open = false;
2842
- this.leClose.emit(result);
2843
- if (this.resolvePromise) {
2844
- this.resolvePromise(result);
2845
- this.resolvePromise = undefined;
2846
- }
2847
- }
2848
- handleConfirm = () => {
2849
- const result = {
2850
- confirmed: true,
2851
- value: this.type === 'prompt' ? this.inputValue : undefined,
2852
- };
2853
- this.leConfirm.emit(result);
2854
- this.hide(true);
2855
- };
2856
- handleCancel = () => {
2857
- const result = {
2858
- confirmed: false,
2859
- value: undefined,
2860
- };
2861
- this.leCancel.emit(result);
2862
- this.hide(false);
2863
- };
2864
- handleBackdropClick = (e) => {
2865
- // Check if click was on the dialog backdrop (outside the dialog box)
2866
- if (this.closeOnBackdrop && e.target === this.dialogEl) {
2867
- const rect = this.dialogEl.getBoundingClientRect();
2868
- const clickedInDialog = e.clientX >= rect.left &&
2869
- e.clientX <= rect.right &&
2870
- e.clientY >= rect.top &&
2871
- e.clientY <= rect.bottom;
2872
- if (!clickedInDialog) {
2873
- this.handleCancel();
2874
- }
2875
- }
2876
- };
2877
- handleInputChange = (e) => {
2878
- this.inputValue = e.target.value;
2879
- };
2880
- handleKeyDown = (e) => {
2881
- if (e.key === 'Enter' && this.type !== 'custom') {
2882
- e.preventDefault();
2883
- this.handleConfirm();
2884
- }
2885
- };
2886
- hasSlot(name) {
2887
- return !!this.el.querySelector(`[slot="${name}"]`);
2888
- }
2889
- renderHeader() {
2890
- if (this.hasSlot('header')) {
2891
- return (index.h("div", { class: "le-popup-header", part: "header" }, index.h("slot", { name: "header" })));
2892
- }
2893
- if (this.popupTitle) {
2894
- return (index.h("div", { class: "le-popup-header", part: "header" }, this.popupTitle));
2895
- }
2896
- return null;
2897
- }
2898
- renderBody() {
2899
- return (index.h("div", { class: "le-popup-body", part: "body" }, this.message && index.h("p", { class: "le-popup-message" }, this.message), this.type === 'prompt' && (index.h("input", { type: "text", class: "le-popup-input", part: "input", placeholder: this.placeholder, value: this.inputValue, onInput: this.handleInputChange, onKeyDown: this.handleKeyDown, ref: el => (this.inputEl = el) })), index.h("le-slot", { name: "", tag: "div", description: "Custom popup content", type: "slot" }, index.h("slot", null))));
2900
- }
2901
- renderFooter() {
2902
- if (this.hasSlot('footer')) {
2903
- return (index.h("div", { class: "le-popup-footer", part: "footer" }, index.h("slot", { name: "footer" })));
2904
- }
2905
- // For custom type without footer slot, don't render default buttons
2906
- if (this.type === 'custom') {
2907
- return null;
2908
- }
2909
- return (index.h("div", { class: "le-popup-footer", part: "footer" }, (this.type === 'confirm' || this.type === 'prompt') && (index.h("le-button", { variant: "outlined", part: "button-cancel", class: "le-popup-btn", onClick: this.handleCancel }, this.cancelText)), index.h("le-button", { variant: "solid", part: "button-confirm", class: "le-popup-btn", onClick: this.handleConfirm }, this.confirmText)));
2910
- }
2911
- render() {
2912
- const positionClass = `le-popup-position-${this.position}`;
2913
- return (index.h("dialog", { key: '75ff06ac2532818f2951283fb455d165d1b13e89', class: `le-popup-dialog ${positionClass}`, part: "dialog", ref: el => (this.dialogEl = el), onClick: this.handleBackdropClick }, index.h("le-component", { key: '0fea1c4ea66d8457c66bf5e14b08b7ac3251a9cc', component: "le-popup" }, index.h("div", { key: 'eb95845004b27cdba2e817b2f296a59ae1d62552', class: "le-popup-container", part: "container" }, this.renderHeader(), this.renderBody(), this.renderFooter()))));
2914
- }
2915
- };
2916
- LePopup.style = lePopupCss();
2917
-
2918
- const leScrollProgressCss = () => `:host{display:block}:host([sticky]){position:sticky;top:var(--le-scroll-progress-sticky-top, 0);z-index:var(--le-scroll-progress-z, calc(var(--le-header-z, 1000) + 1))}:host([fixed]){position:fixed;top:var(--le-scroll-progress-fixed-top, 0);left:var(--le-scroll-progress-fixed-left, 0);right:var(--le-scroll-progress-fixed-right, 0);z-index:var(--le-scroll-progress-z, calc(var(--le-header-z, 1000) + 1))}.track{width:100%;height:var(--le-scroll-progress-height, 4px);background:var(--le-scroll-progress-bg, transparent)}.fill{height:100%;width:0;background:var(--le-scroll-progress-fill, var(--le-color-primary, currentColor));border-radius:var(--le-scroll-progress-border-radius, 2px);transition:width var(--le-transition-fast, 120ms linear)}`;
2919
-
2920
- const LeScrollProgress = class {
2921
- constructor(hostRef) {
2922
- index.registerInstance(this, hostRef);
2923
- }
2924
- get el() { return index.getElement(this); }
2925
- /** Boolean or selector string. */
2926
- trackScrollProgress;
2927
- progress = 0;
2928
- rafId = null;
2929
- targetEl = null;
2930
- componentWillLoad() {
2931
- this.updateProgress();
2932
- }
2933
- componentDidLoad() {
2934
- this.resolveTarget();
2935
- this.updateProgress();
2936
- }
2937
- disconnectedCallback() {
2938
- if (this.rafId != null) {
2939
- cancelAnimationFrame(this.rafId);
2940
- this.rafId = null;
2941
- }
2942
- }
2943
- onTrackChange() {
2944
- this.resolveTarget();
2945
- this.updateProgress();
2946
- }
2947
- onScroll() {
2948
- this.scheduleUpdate();
2949
- }
2950
- onResize() {
2951
- this.resolveTarget();
2952
- this.scheduleUpdate(true);
2953
- }
2954
- scheduleUpdate(force = false) {
2955
- if (this.rafId != null && !force)
2956
- return;
2957
- this.rafId = requestAnimationFrame(() => {
2958
- this.rafId = null;
2959
- this.updateProgress();
2960
- });
2961
- }
2962
- resolveTarget() {
2963
- if (typeof document === 'undefined')
2964
- return;
2965
- const raw = this.trackScrollProgress;
2966
- // If attribute missing, default to enabled (full document).
2967
- // If user explicitly sets 'false', treat as disabled.
2968
- if (raw == null) {
2969
- this.targetEl = null;
2970
- return;
2971
- }
2972
- const val = String(raw).trim();
2973
- if (val === '' || val === 'true') {
2974
- this.targetEl = null;
2975
- return;
2976
- }
2977
- if (val === 'false') {
2978
- this.targetEl = null;
2979
- return;
2980
- }
2981
- try {
2982
- this.targetEl = document.querySelector(val);
2983
- }
2984
- catch {
2985
- this.targetEl = null;
2986
- }
2987
- }
2988
- clamp01(n) {
2989
- return Math.max(0, Math.min(1, n));
2990
- }
2991
- updateProgress() {
2992
- if (typeof window === 'undefined' || typeof document === 'undefined')
2993
- return;
2994
- // If explicitly disabled.
2995
- if (this.trackScrollProgress === 'false') {
2996
- if (this.progress !== 0)
2997
- this.progress = 0;
2998
- return;
2999
- }
3000
- const scrollY = window.scrollY || 0;
3001
- let p = 0;
3002
- if (this.targetEl) {
3003
- const rect = this.targetEl.getBoundingClientRect();
3004
- const top = scrollY + rect.top;
3005
- const height = rect.height;
3006
- const viewport = window.innerHeight || 1;
3007
- const denom = Math.max(1, height - viewport);
3008
- p = this.clamp01((scrollY - top) / denom);
3009
- }
3010
- else {
3011
- const doc = document.documentElement;
3012
- const denom = Math.max(1, doc.scrollHeight - doc.clientHeight);
3013
- p = this.clamp01(scrollY / denom);
3014
- }
3015
- const next = Math.round(p * 1000) / 1000;
3016
- if (next !== this.progress)
3017
- this.progress = next;
3018
- }
3019
- render() {
3020
- const width = `${this.progress * 100}%`;
3021
- return (index.h(index.Host, { key: '7a93dbfd8b14e5e581ff1480ad0e657f8f33bcf4' }, index.h("div", { key: 'fe4ff14af541e2a610d0b32605aaadb87ccea957', class: "track", part: "track", "aria-hidden": "true" }, index.h("div", { key: 'f46a09358bf77a7cff64deafa63702360a686c6a', class: "fill", part: "fill", style: { width } }))));
3022
- }
3023
- static get watchers() { return {
3024
- "trackScrollProgress": ["onTrackChange"]
3025
- }; }
3026
- };
3027
- LeScrollProgress.style = leScrollProgressCss();
3028
-
3029
- const leSelectCss = () => `:host{display:inline-block;min-width:150px;--le-select-color:var(--le-color-text, #1f2937);--le-select-border-radius:var(--le-radius-md);--le-select-content-padding:var(--le-spacing-2)}:host([disabled]){opacity:0.5;pointer-events:none}:host([full-width]){width:100%}.select-trigger{display:flex;align-items:center;gap:0.5rem;width:100%;padding:0;--le-button-padding:var(--le-spacing-1) var(--le-spacing-1) var(--le-spacing-1) var(--le-spacing-2);font-size:var(--le-select-font-size, 0.875rem);font-family:inherit;line-height:1.4;color:var(--le-select-color);background:var(--le-select-bg, var(--le-color-surface, #fff));border-radius:var(--le-select-border-radius);cursor:pointer;text-align:left;transition:border-color 0.15s ease, box-shadow 0.15s ease}.select-trigger:focus{outline:2px solid var(--le-color-focus);outline-offset:2px}.select-trigger:not(.has-value) .trigger-label{color:color-mix(in srgb, var(--le-color-text-secondary) 66%, transparent)}.trigger-icon{display:flex;align-items:center;justify-content:center;flex-shrink:0;width:1.25rem;height:1.25rem}.trigger-icon-end{width:16px;height:16px}.trigger-icon img{width:100%;height:100%;object-fit:contain}.trigger-label{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--le-color-text)}.trigger-label::not(.has-value){color:var(--le-color-text-disabled, #9ca3af)}le-button::part(icon-end){display:flex;align-items:center;justify-content:center;flex-shrink:0;width:1.25rem;height:1.25rem;margin-left:auto;transition:transform 0.2s ease}le-button::part(icon-end) svg{width:1rem;height:1rem}le-button.is-open::part(icon-end){transform:rotate(180deg)}.search-input::part(container):focus-within{outline:none !important}.search-input{--le-input-radius:var(--le-radius-md)}`;
3030
-
3031
- const LeSelect = class {
3032
- constructor(hostRef) {
3033
- index.registerInstance(this, hostRef);
3034
- this.leChange = index.createEvent(this, "change");
3035
- this.leOpen = index.createEvent(this, "leOpen");
3036
- this.leClose = index.createEvent(this, "leClose");
3037
- }
3038
- get el() { return index.getElement(this); }
3039
- /**
3040
- * The options to display in the dropdown.
3041
- */
3042
- options = [];
3043
- /**
3044
- * The currently selected value.
3045
- */
3046
- value;
3047
- /**
3048
- * Placeholder text when no option is selected.
3049
- */
3050
- placeholder = 'Select an option';
3051
- /**
3052
- * Whether the select is disabled.
3053
- */
3054
- disabled = false;
3055
- /**
3056
- * Whether selection is required.
3057
- */
3058
- required = false;
3059
- /**
3060
- * Name attribute for form submission.
3061
- */
3062
- name;
3063
- /**
3064
- * Whether the select should take full width of its container.
3065
- */
3066
- fullWidth = false;
3067
- /**
3068
- * Size variant of the select.
3069
- */
3070
- size = 'medium';
3071
- /**
3072
- * Visual variant of the select.
3073
- */
3074
- variant = 'default';
3075
- /**
3076
- * Whether the input is searchable.
3077
- */
3078
- searchable = false;
3079
- /**
3080
- * Text to show when no options match the search.
3081
- */
3082
- emptyText = 'No results found';
3083
- /**
3084
- * Whether the dropdown is currently open.
3085
- */
3086
- open = false;
3087
- /**
3088
- * Emitted when the selected value changes.
3089
- */
3090
- leChange;
3091
- /**
3092
- * Emitted when the dropdown opens.
3093
- */
3094
- leOpen;
3095
- /**
3096
- * Emitted when the dropdown closes.
3097
- */
3098
- leClose;
3099
- selectedOption;
3100
- searchQuery = '';
3101
- dropdownEl;
3102
- inputEl;
3103
- handleValueChange() {
3104
- this.updateSelectedOption();
3105
- }
3106
- handleOptionsChange() {
3107
- this.updateSelectedOption();
3108
- }
3109
- componentWillLoad() {
3110
- this.updateSelectedOption();
3111
- }
3112
- get parsedOptions() {
3113
- if (typeof this.options === 'string') {
3114
- try {
3115
- return JSON.parse(this.options);
3116
- }
3117
- catch {
3118
- return [];
3119
- }
3120
- }
3121
- return this.options;
3122
- }
3123
- updateSelectedOption() {
3124
- if (this.value !== undefined) {
3125
- this.selectedOption = this.parsedOptions.find(opt => (opt.value ?? opt.label) === this.value);
3126
- }
3127
- else {
3128
- this.selectedOption = undefined;
3129
- }
3130
- }
3131
- filterOption = (option, query) => {
3132
- if (!query)
3133
- return true;
3134
- const searchLower = query.toLowerCase();
3135
- return (option.label.toLowerCase().includes(searchLower) ||
3136
- (option.description?.toLowerCase().includes(searchLower) ?? false));
3137
- };
3138
- handleOptionSelect = (e) => {
3139
- this.value = e.detail.value;
3140
- this.selectedOption = e.detail.option;
3141
- this.leChange.emit(e.detail);
3142
- };
3143
- handleDropdownOpen = () => {
3144
- this.open = true;
3145
- this.leOpen.emit();
3146
- // Focus search input if searchable
3147
- if (this.searchable) {
3148
- setTimeout(() => {
3149
- this.inputEl?.focus();
3150
- }, 50);
3151
- }
3152
- };
3153
- handleDropdownClose = () => {
3154
- this.open = false;
3155
- this.leClose.emit();
3156
- };
3157
- handleTriggerClick = () => {
3158
- if (!this.disabled) {
3159
- this.dropdownEl?.toggle();
3160
- }
3161
- };
3162
- handleTriggerKeyDown = (e) => {
3163
- if (this.disabled)
3164
- return;
3165
- if (e.key === 'Enter' || e.key === ' ' || e.key === 'ArrowDown') {
3166
- e.preventDefault();
3167
- this.dropdownEl?.show();
3168
- }
3169
- };
3170
- handleSearchInput = (e) => {
3171
- const target = e.target;
3172
- this.searchQuery = target.value;
3173
- };
3174
- /**
3175
- * Opens the dropdown.
3176
- */
3177
- async showDropdown() {
3178
- await this.dropdownEl?.show();
3179
- }
3180
- /**
3181
- * Closes the dropdown.
3182
- */
3183
- async hideDropdown() {
3184
- await this.dropdownEl?.hide();
3185
- }
3186
- renderIcon(icon) {
3187
- if (!icon)
3188
- return null;
3189
- if (icon.startsWith('http') || icon.startsWith('/')) {
3190
- return index.h("img", { class: "trigger-icon", src: icon, alt: "" });
3191
- }
3192
- return index.h("span", { class: "trigger-icon" }, icon);
3193
- }
3194
- render() {
3195
- const hasValue = this.selectedOption !== undefined;
3196
- return (index.h("le-component", { key: '670430a0ca8f310b7454c7dd93098b6b57f3bb82', component: "le-select" }, index.h("le-dropdown-base", { key: '58716c031b14513b67a1106b90b6174fa363c8b4', ref: el => (this.dropdownEl = el), options: this.parsedOptions, value: this.value, disabled: this.disabled, filterFn: this.searchable ? this.filterOption : undefined, filterQuery: this.searchQuery, onLeOptionSelect: this.handleOptionSelect, onLeDropdownOpen: this.handleDropdownOpen, onLeDropdownClose: this.handleDropdownClose, fullWidth: this.fullWidth }, index.h("le-button", { key: 'b9cb202179c8fbc6dea9bc7af5ccf11deeb7c0f1', variant: this.variant && this.variant !== 'default' ? this.variant : 'outlined', slot: "trigger", align: "space-between", class: {
3197
- 'select-trigger': true,
3198
- 'has-value': hasValue,
3199
- 'is-open': this.open,
3200
- }, mode: "default", size: this.size, disabled: this.disabled, "aria-haspopup": "listbox", "aria-expanded": this.open ? 'true' : 'false', onClick: this.handleTriggerClick, onKeyDown: this.handleTriggerKeyDown, fullWidth: this.fullWidth, iconStart: hasValue && this.selectedOption?.iconStart
3201
- ? this.renderIcon(this.selectedOption.iconStart)
3202
- : null, iconEnd: index.h("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", "stroke-width": "2" }, index.h("path", { d: "M4 6l4 4 4-4" })) }, index.h("span", { key: '592267788f6a02d6b501941863e91bcd68742233', class: "trigger-label" }, hasValue ? this.selectedOption.label : this.placeholder)), this.searchable && this.open && (index.h("div", { key: 'd4d2611e8b1f7fb243675cc11fa4982331b3c330', class: "multiselect-search", slot: "header" }, index.h("le-string-input", { key: 'f2a7e477eaf730173cfbca35b33d263eda860dc2', mode: "default", inputRef: el => (this.inputEl = el), class: "search-input", placeholder: "Search...", value: this.searchQuery, onInput: this.handleSearchInput })))), this.name && index.h("input", { key: 'bf9dc8141d02c272b0b81557dbc7c24a419ce558', type: "hidden", name: this.name, value: this.value?.toString() ?? '' })));
3203
- }
3204
- static get watchers() { return {
3205
- "value": ["handleValueChange"],
3206
- "options": ["handleOptionsChange"]
3207
- }; }
3208
- };
3209
- LeSelect.style = leSelectCss();
3210
-
3211
- const leSlotDefaultCss = () => `:host{display:contents;--le-slot-border-color:#0088ff;--le-slot-bg-color:rgba(0, 136, 255, 0.05);--le-slot-header-bg:rgb(218, 238, 255);--le-slot-label-color:#0066cc;--le-slot-description-color:#666;--le-slot-required-color:#e53935;--le-slot-dropzone-min-height:20px;--le-slot-dropzone-border-color:#ccc}.le-slot-container,.le-slot-header,.le-slot-description,.le-slot-dropzone,.le-slot-input{display:none}.hidden-slot{display:none}:host(.admin-mode){display:block;flex:1}:host(.admin-mode) .le-slot-container{position:relative;display:flex;flex-direction:column;border:2px dashed var(--le-slot-border-color);border-radius:4px;background:var(--le-slot-bg-color);margin:4px 0}:host(.admin-mode) .le-slot-header{display:flex;align-items:center;gap:4px;padding:0 0 0 var(--le-spacing-1, 4px);background:var(--le-slot-header-bg);border-bottom:1px solid var(--le-slot-border-color);font-size:var(--le-font-size-xs, 11px);font-weight:400;text-transform:capitalize}:host(.admin-mode) .le-slot-header-no-label{justify-content:flex-end;height:16px;border:none;background-color:transparent}.le-slot-label{color:var(--le-slot-label-color);text-align:start;overflow:hidden;width:0;flex:1 1 0%}.le-slot-required{color:var(--le-slot-required-color);font-weight:bold}:host(.admin-mode) .le-slot-description{display:block;padding:4px 8px;font-size:12px;color:var(--le-slot-description-color);font-style:italic}:host(.admin-mode) .le-slot-description-icon{display:inline-block;font-size:9px;line-height:1;cursor:pointer;color:var(--le-slot-description-color)}:host(.admin-mode) .le-slot-dropzone{display:block;min-height:var(--le-slot-dropzone-min-height);padding:var(--le-spacing-1, 4px);position:relative}:host(.admin-mode) .le-slot-dropzone:empty::before{content:'Drop content here';display:flex;align-items:center;justify-content:center;position:absolute;inset:8px;border:2px dashed var(--le-slot-dropzone-border-color);border-radius:4px;color:#999;font-size:12px;pointer-events:none}:host(.admin-mode.drag-over) .le-slot-container{border-color:#00cc66;background:rgba(0, 204, 102, 0.1)}:host(.admin-mode.drag-over) .le-slot-dropzone:empty::before{border-color:#00cc66;color:#00cc66;content:'Release to drop'}:host(.admin-mode) .le-slot-input{display:block;padding:var(--le-spacing-1, 4px)}:host(.admin-mode) .le-slot-input input,:host(.admin-mode) .le-slot-input textarea{display:block;width:100%;padding:8px 10px;border:1px solid var(--le-slot-dropzone-border-color);border-radius:4px;font-family:inherit;font-size:14px;line-height:1.4;background:#fff;color:#333;box-sizing:border-box;transition:border-color 0.2s, box-shadow 0.2s}:host(.admin-mode) .le-slot-input input:focus,:host(.admin-mode) .le-slot-input textarea:focus{outline:none;border-color:var(--le-slot-border-color);box-shadow:0 0 0 3px rgba(0, 136, 255, 0.15)}:host(.admin-mode) .le-slot-input input::placeholder,:host(.admin-mode) .le-slot-input textarea::placeholder{color:#999}:host(.admin-mode) .le-slot-input textarea{resize:vertical;min-height:60px}:host(.admin-mode) .le-slot-input slot{display:none}.le-slot-invalid{color:var(--le-slot-required-color);font-size:10px;margin-left:auto;font-weight:normal;text-transform:none}:host(.admin-mode) .le-slot-input.has-error input,:host(.admin-mode) .le-slot-input.has-error textarea{border-color:var(--le-slot-required-color);background:rgba(229, 57, 53, 0.05)}:host(.admin-mode) .le-slot-input.has-error input:focus,:host(.admin-mode) .le-slot-input.has-error textarea:focus{border-color:var(--le-slot-required-color);box-shadow:0 0 0 3px rgba(229, 57, 53, 0.15)}.le-slot-add-btn{font-size:24px;line-height:0px;width:12px;height:12px}.le-slot-header-no-label .le-slot-add-btn{font-size:16px}.le-slot-button{width:20px;height:20px}:host(.admin-mode) .le-slot-header-no-label.le-slot-header-text{height:0}`;
3212
-
3213
- const LeSlot = class {
3214
- constructor(hostRef) {
3215
- index.registerInstance(this, hostRef);
3216
- this.leSlotChange = index.createEvent(this, "leSlotChange");
3217
- }
3218
- get el() { return index.getElement(this); }
3219
- /**
3220
- * The type of slot content.
3221
- * - `slot`: Default, shows a dropzone for components (default)
3222
- * - `text`: Shows a single-line text input
3223
- * - `textarea`: Shows a multi-line text area
3224
- */
3225
- type = 'slot';
3226
- /**
3227
- * The name of the slot this placeholder represents.
3228
- * Should match the slot name in the parent component.
3229
- */
3230
- name = '';
3231
- /**
3232
- * Label to display in admin mode.
3233
- * If not provided, the slot name will be used.
3234
- */
3235
- label;
3236
- /**
3237
- * Description of what content this slot accepts.
3238
- * Shown in admin mode to guide content editors.
3239
- */
3240
- description;
3241
- /**
3242
- * Comma-separated list of allowed component tags for this slot.
3243
- * Used by CMS to filter available components.
3244
- *
3245
- * @example "le-card,le-button,le-text"
3246
- */
3247
- allowedComponents;
3248
- /**
3249
- * Whether multiple components can be dropped in this slot.
3250
- */
3251
- multiple = true;
3252
- /**
3253
- * Whether this slot is required to have content.
3254
- */
3255
- required = false;
3256
- /**
3257
- * Placeholder text for text/textarea inputs in admin mode.
3258
- */
3259
- placeholder;
3260
- /**
3261
- * The HTML tag to create when there's no slotted element.
3262
- * Used with type="text" or type="textarea" to auto-create elements.
3263
- *
3264
- * @example "h3" - creates <h3 slot="header">content</h3>
3265
- * @example "p" - creates <p slot="content">content</p>
3266
- */
3267
- tag;
3268
- /**
3269
- * CSS styles for the slot dropzone container.
3270
- * Useful for layouts - e.g., "flex-direction: row" for horizontal stacks.
3271
- * Only applies in admin mode for type="slot".
3272
- */
3273
- slotStyle;
3274
- /**
3275
- * Internal state to track admin mode
3276
- */
3277
- adminMode = false;
3278
- /**
3279
- * Internal state for text input value (synced from slot content)
3280
- */
3281
- textValue = '';
3282
- /**
3283
- * Whether the current textValue contains valid HTML
3284
- */
3285
- isValidHtml = true;
3286
- /**
3287
- * Available components loaded from Custom Elements Manifest
3288
- */
3289
- availableComponents = [];
3290
- /**
3291
- * Whether the component picker popover is open
3292
- */
3293
- pickerOpen = false;
3294
- /**
3295
- * Reference to the slot element to access assignedNodes
3296
- */
3297
- slotRef;
3298
- /**
3299
- * The original slotted element (e.g., <h3 slot="header">)
3300
- */
3301
- slottedElement;
3302
- /**
3303
- * Emitted when text content changes in admin mode.
3304
- * The event detail contains the new text value and validity.
3305
- */
3306
- leSlotChange;
3307
- disconnectModeObserver;
3308
- connectedCallback() {
3309
- this.disconnectModeObserver = utils.observeModeChanges(this.el, mode => {
3310
- const wasAdmin = this.adminMode;
3311
- this.adminMode = mode === 'admin';
3312
- // When entering admin mode, read content from slotted elements
3313
- if (this.adminMode && !wasAdmin) {
3314
- // Need to wait for render to access slot ref
3315
- requestAnimationFrame(() => this.readSlottedContent());
3316
- // Load available components for the component picker
3317
- if (this.type === 'slot') {
3318
- this.loadAvailableComponents();
3319
- }
3320
- }
3321
- });
3322
- }
3323
- disconnectedCallback() {
3324
- this.disconnectModeObserver?.();
3325
- }
3326
- /**
3327
- * Flag to prevent re-reading content right after we updated it
3328
- */
3329
- isUpdating = false;
3330
- /**
3331
- * Read content from slotted elements via assignedNodes()
3332
- */
3333
- readSlottedContent() {
3334
- if (!this.slotRef)
3335
- return;
3336
- // Skip if we just updated the content ourselves
3337
- if (this.isUpdating) {
3338
- this.isUpdating = false;
3339
- return;
3340
- }
3341
- const assignedNodes = this.slotRef.assignedNodes({ flatten: true });
3342
- // For text/textarea types, we want to edit the innerHTML of slotted elements
3343
- if (this.type === 'text' || this.type === 'textarea') {
3344
- // Find the first element node (skip text nodes that are just whitespace)
3345
- const elementNode = assignedNodes.find(node => node.nodeType === Node.ELEMENT_NODE);
3346
- if (elementNode) {
3347
- // Only update textValue if slotted element changed or we don't have one yet
3348
- if (this.slottedElement !== elementNode) {
3349
- this.slottedElement = elementNode;
3350
- this.textValue = elementNode.innerHTML?.trim() || '';
3351
- // console.log(`[le-slot "${this.name}"] Read slotted content:`, this.textValue);
3352
- }
3353
- }
3354
- else {
3355
- // No element, check for direct text content
3356
- const textContent = assignedNodes
3357
- .filter(node => node.nodeType === Node.TEXT_NODE)
3358
- .map(node => node.textContent)
3359
- .join('')
3360
- .trim();
3361
- if (textContent && !this.textValue) {
3362
- this.textValue = textContent;
3363
- // console.log(`[le-slot "${this.name}"] Read text content:`, this.textValue);
3364
- }
3365
- }
3366
- }
3367
- }
3368
- /**
3369
- * Validates if a string contains valid HTML
3370
- */
3371
- validateHtml(html) {
3372
- // Empty string is valid
3373
- if (!html.trim())
3374
- return true;
3375
- // Create a template element to parse the HTML
3376
- const template = document.createElement('template');
3377
- template.innerHTML = html;
3378
- // Check that we don't have obviously broken HTML
3379
- // Count opening and closing tags for common elements
3380
- const openTags = (html.match(/<[a-z][^>]*(?<!\/)>/gi) || []).length;
3381
- const closeTags = (html.match(/<\/[a-z][^>]*>/gi) || []).length;
3382
- const selfClosing = (html.match(/<[a-z][^>]*\/>/gi) || []).length;
3383
- // Simple validation: opening tags (minus self-closing) should roughly match closing tags
3384
- // Allow some tolerance for void elements like <br>, <img>, etc.
3385
- const voidElements = (html.match(/<(br|hr|img|input|meta|link|area|base|col|embed|param|source|track|wbr)[^>]*>/gi) || []).length;
3386
- const effectiveOpenTags = openTags - selfClosing - voidElements;
3387
- // If difference is too large, HTML is likely broken
3388
- if (Math.abs(effectiveOpenTags - closeTags) > 1) {
3389
- return false;
3390
- }
3391
- return true;
3392
- }
3393
- handleTextInput = (event) => {
3394
- const target = event.target;
3395
- this.textValue = target.value;
3396
- this.isValidHtml = this.validateHtml(this.textValue);
3397
- if (this.isValidHtml) {
3398
- // Set flag to prevent slotchange from re-reading what we just wrote
3399
- this.isUpdating = true;
3400
- console.log('Updating text value:', this.textValue, 'slottedElement:', this.slottedElement);
3401
- if (this.slottedElement) {
3402
- // Update existing slotted element's innerHTML
3403
- this.slottedElement.innerHTML = this.textValue;
3404
- }
3405
- else if (this.tag && this.textValue) {
3406
- // No slotted element exists
3407
- // If the slot doesn't have a name, then it's the default slot
3408
- // remove the existing non-slotted content (text nodes and elements without slot attribute)
3409
- const rootNode = this.el.getRootNode();
3410
- if (!this.name && rootNode instanceof ShadowRoot) {
3411
- const hostComponent = rootNode.host;
3412
- Array.from(hostComponent.childNodes).forEach(node => {
3413
- if (node.nodeType === Node.TEXT_NODE || (node.nodeType === Node.ELEMENT_NODE && !node.hasAttribute('slot'))) {
3414
- node.remove();
3415
- }
3416
- });
3417
- }
3418
- // create one using the specified tag
3419
- this.createSlottedElement();
3420
- }
3421
- else if (this.textValue) {
3422
- // no tag specified - just replace everything in the host component
3423
- const rootNode = this.el.getRootNode();
3424
- if (rootNode instanceof ShadowRoot) {
3425
- const hostComponent = rootNode.host;
3426
- hostComponent.innerHTML = this.textValue;
3427
- }
3428
- }
3429
- }
3430
- this.leSlotChange.emit({
3431
- name: this.name,
3432
- value: this.textValue,
3433
- isValid: this.isValidHtml,
3434
- });
3435
- };
3436
- /**
3437
- * Create a new slotted element when none exists.
3438
- * The element is appended to the host component's light DOM.
3439
- */
3440
- createSlottedElement() {
3441
- if (!this.tag)
3442
- return;
3443
- // Find the host component (le-card, etc.) by traversing up through shadow DOM
3444
- // le-slot is inside le-card's shadow DOM, so we need to find le-card's host
3445
- const rootNode = this.el.getRootNode();
3446
- if (!(rootNode instanceof ShadowRoot))
3447
- return;
3448
- const hostComponent = rootNode.host;
3449
- if (!hostComponent)
3450
- return;
3451
- // Create the new element
3452
- const newElement = document.createElement(this.tag);
3453
- newElement.innerHTML = this.textValue;
3454
- // Set the slot attribute if this is a named slot
3455
- if (this.name) {
3456
- newElement.setAttribute('slot', this.name);
3457
- }
3458
- // Append to the host component's light DOM
3459
- hostComponent.appendChild(newElement);
3460
- // Store reference to the new element
3461
- this.slottedElement = newElement;
3462
- // console.log(`[le-slot "${this.name}"] Created new <${this.tag}> element`);
3463
- }
3464
- /**
3465
- * Load available components from Custom Elements Manifest
3466
- */
3467
- async loadAvailableComponents() {
3468
- try {
3469
- const { manifestFile } = index.getLeKitConfig();
3470
- const manifestFileResolved = index.getAssetPath(`./assets/${manifestFile}`);
3471
- const response = await fetch(manifestFileResolved);
3472
- const manifest = await response.json();
3473
- const components = [];
3474
- const allowedList = this.allowedComponents?.split(',').map(s => s.trim()) || [];
3475
- for (const module of manifest.modules) {
3476
- for (const declaration of module.declarations || []) {
3477
- if (declaration.tagName && declaration.customElement) {
3478
- // Skip internal components (le-slot, le-component, le-popover)
3479
- const isInternal = ['le-slot', 'le-component', 'le-popover'].includes(declaration.tagName);
3480
- if (isInternal)
3481
- continue;
3482
- // If allowedComponents is specified, filter by it
3483
- if (allowedList.length > 0 && !allowedList.includes(declaration.tagName)) {
3484
- continue;
3485
- }
3486
- components.push({
3487
- tagName: declaration.tagName,
3488
- name: this.formatComponentName(declaration.tagName),
3489
- description: declaration.description || '',
3490
- });
3491
- }
3492
- }
3493
- }
3494
- this.availableComponents = components || [];
3495
- }
3496
- catch (error) {
3497
- console.warn('[le-slot] Failed to load component manifest:', error);
3498
- }
3499
- }
3500
- /**
3501
- * Format a tag name into a display name
3502
- * e.g., 'le-card' -> 'Card'
3503
- */
3504
- formatComponentName(tagName) {
3505
- return tagName
3506
- .replace(/^le-/, '')
3507
- .split('-')
3508
- .map(word => word.charAt(0).toUpperCase() + word.slice(1))
3509
- .join(' ');
3510
- }
3511
- /**
3512
- * Add a new component to the slot
3513
- */
3514
- addComponent(tagName) {
3515
- // Find the host component by traversing up through shadow DOM
3516
- const rootNode = this.el.getRootNode();
3517
- if (!(rootNode instanceof ShadowRoot))
3518
- return;
3519
- const hostComponent = rootNode.host;
3520
- if (!hostComponent)
3521
- return;
3522
- // Create the new component element
3523
- const newElement = document.createElement(tagName);
3524
- // Set the slot attribute if this is a named slot
3525
- if (this.name) {
3526
- newElement.setAttribute('slot', this.name);
3527
- }
3528
- // Append to the host component's light DOM
3529
- hostComponent.appendChild(newElement);
3530
- // Emit change event so the page can save
3531
- this.leSlotChange.emit({
3532
- name: this.name,
3533
- value: hostComponent.innerHTML,
3534
- isValid: true,
3535
- });
3536
- }
3537
- /**
3538
- * Handle slot change event to re-read content when nodes are assigned
3539
- */
3540
- handleSlotChange = () => {
3541
- this.readSlottedContent();
3542
- };
3543
- render() {
3544
- const displayLabel = this.label || this.name;
3545
- // Always render the same structure, CSS handles visibility via .admin-mode class
3546
- return (index.h(index.Host, { key: '8a48f7865340d9a2e201a2f878b2f1c66c55d86c', class: {
3547
- 'admin-mode': this.adminMode,
3548
- 'invalid-html': !this.isValidHtml,
3549
- }, role: this.adminMode ? 'region' : undefined, "aria-label": this.adminMode ? `Slot: ${displayLabel}` : undefined, "data-slot-name": this.name, "data-slot-type": this.type, "data-allowed": this.allowedComponents, "data-multiple": this.multiple, "data-required": this.required }, this.adminMode ? (index.h("div", { class: "le-slot-container" }, index.h("div", { class: utils.classnames('le-slot-header', {
3550
- 'le-slot-header-no-label': !displayLabel,
3551
- 'le-slot-header-text': this.type === 'text',
3552
- 'le-slot-header-error': !this.isValidHtml,
3553
- }) }, displayLabel && (index.h("span", { class: "le-slot-label" }, displayLabel, this.required && index.h("span", { class: "le-slot-required" }, "*"))), !this.isValidHtml && index.h("span", { class: "le-slot-invalid" }, "\u26A0 Invalid HTML"), this.type === 'slot' && this.adminMode && (index.h("le-popover", { mode: "default", showClose: true, align: "start", position: "right", popoverTitle: "Add Component", open: this.pickerOpen, onLePopoverOpen: () => (this.pickerOpen = true), onLePopoverClose: () => (this.pickerOpen = false) }, index.h("le-button", { type: "button", class: "le-slot-button", slot: "trigger", variant: "clear", size: "small", "aria-label": "Add component", "icon-only": true }, index.h("span", { class: "le-slot-add-btn", slot: "icon-only" }, "+")), index.h("div", { class: "le-slot-picker" }, this.availableComponents.length > 0 ? (index.h("ul", { class: "le-slot-picker-list" }, this.availableComponents.map(component => (index.h("li", { key: component.tagName }, index.h("button", { class: "le-slot-picker-item", onClick: () => {
3554
- this.addComponent(component.tagName);
3555
- this.pickerOpen = false;
3556
- } }, index.h("span", { class: "le-slot-picker-name" }, component.name), component.description && index.h("span", { class: "le-slot-picker-desc" }, component.description))))))) : (index.h("div", { class: "le-slot-picker-empty" }, "No components available")))))), this.renderContent())) : (
3557
- // In default mode, just pass through the slot - slotted content renders naturally
3558
- // Note: We use unnamed slot here because named slots from parent component
3559
- // are passed as le-slot's light DOM children
3560
- index.h("slot", null))));
3561
- }
3562
- renderContent() {
3563
- // Create the slot element with ref for reading assignedNodes
3564
- // Wrap in a hidden div since slot elements can't have style prop in Stencil
3565
- // Note: We use unnamed slot here because named slots from parent component
3566
- // are passed as le-slot's light DOM children
3567
- const slotElement = (index.h("div", { class: "hidden-slot" }, index.h("slot", { ref: el => (this.slotRef = el), onSlotchange: this.handleSlotChange })));
3568
- switch (this.type) {
3569
- case 'text':
3570
- return (index.h("div", { class: { 'le-slot-input': true, 'has-error': !this.isValidHtml } }, index.h("le-string-input", { mode: "default", value: this.textValue, placeholder: this.placeholder || `Enter ${this.label || this.name || 'text'}...`, onChange: this.handleTextInput }), slotElement));
3571
- case 'textarea':
3572
- return (index.h("div", { class: { 'le-slot-input': true, 'has-error': !this.isValidHtml } }, index.h("textarea", { value: this.textValue, placeholder: this.placeholder || `Enter ${this.label || this.name || 'text'}...`, onInput: this.handleTextInput, required: this.required, rows: 3 }), slotElement));
3573
- case 'slot':
3574
- default:
3575
- // Parse slotStyle string into style object if provided
3576
- const dropzoneStyle = {};
3577
- if (this.slotStyle) {
3578
- this.slotStyle.split(';').forEach(rule => {
3579
- const [prop, value] = rule.split(':').map(s => s.trim());
3580
- if (prop && value) {
3581
- // Convert kebab-case to camelCase for style object
3582
- const camelProp = prop.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
3583
- dropzoneStyle[camelProp] = value;
3584
- }
3585
- });
3586
- }
3587
- return (index.h("div", { class: "le-slot-dropzone", style: dropzoneStyle }, index.h("slot", { ref: el => (this.slotRef = el), onSlotchange: this.handleSlotChange })));
3588
- }
3589
- }
3590
- };
3591
- LeSlot.style = leSlotDefaultCss();
3592
-
3593
- const leStringInputCss = () => `:host{display:block;--le-input-bg:var(--le-color-surface, #ffffff);--le-input-color:var(--le-color-text-primary, #333333);--le-input-border:var(--le-border-width, 2px) solid var(--le-color-border-input, #007bff);--le-input-radius:var(--le-radius-sm, 4px);--le-input-padding:2px 6px;--le-input-height:1.5rem;--le-input-label-color:var(--le-color-text-primary, #333333);--le-input-desc-color:var(--le-color-text-secondary, #666666);--le-input-placeholder-color:#999999}.le-input-wrapper{display:flex;flex-direction:column;gap:2px}.le-input-label{display:block;font-size:0.9em;font-weight:500;color:var(--le-input-label-color);margin-bottom:2px}.le-input-container{position:relative;display:flex;align-items:center;background:var(--le-input-bg);border:var(--le-input-border);border-radius:var(--le-input-radius);transition:border-color 0.2s}.le-input-container:focus-within{outline:2px solid var(--le-color-focus);outline-offset:2px}:host([disabled]) .le-input-container{opacity:0.6;background-color:rgba(0,0,0,0.05);cursor:not-allowed}input{flex:1;min-height:var(--le-input-height);padding:var(--le-input-padding);border:none;background:transparent;color:var(--le-input-color);font-family:inherit;font-size:inherit;outline:none;width:100%}input::placeholder{color:var(--le-input-placeholder-color)}.icon-start,.icon-end{display:flex;align-items:center;justify-content:center;padding:0 8px;color:var(--le-input-desc-color)}.le-input-description{font-size:0.85em;color:var(--le-input-desc-color);margin-top:2px}.le-input-description::has(le-slot>slot[name=description]:empty){display:none}`;
3594
-
3595
- const LeStringInput = class {
3596
- constructor(hostRef) {
3597
- index.registerInstance(this, hostRef);
3598
- this.leChange = index.createEvent(this, "change");
3599
- this.leInput = index.createEvent(this, "input");
3600
- }
3601
- get el() { return index.getElement(this); }
3602
- /**
3603
- * Pass the ref of the input element to the parent component
3604
- */
3605
- inputRef;
3606
- /**
3607
- * Mode of the popover should be 'default' for internal use
3608
- */
3609
- mode;
3610
- /**
3611
- * The value of the input
3612
- */
3613
- value;
3614
- /**
3615
- * The name of the input
3616
- */
3617
- name;
3618
- /**
3619
- * The type of the input (text, email, password, etc.)
3620
- */
3621
- type = 'text';
3622
- /**
3623
- * Label for the input
3624
- */
3625
- label;
3626
- /**
3627
- * Icon for the start icon
3628
- */
3629
- iconStart;
3630
- /**
3631
- * Icon for the end icon
3632
- */
3633
- iconEnd;
3634
- /**
3635
- * Placeholder text
3636
- */
3637
- placeholder;
3638
- /**
3639
- * Hide description slot
3640
- */
3641
- hideDescription = false;
3642
- /**
3643
- * Whether the input is disabled
3644
- */
3645
- disabled = false;
3646
- /**
3647
- * Whether the input is read-only
3648
- */
3649
- readonly = false;
3650
- /**
3651
- * External ID for linking with external systems
3652
- */
3653
- externalId;
3654
- /**
3655
- * Emitted when the value changes (on blur or Enter)
3656
- */
3657
- leChange;
3658
- /**
3659
- * Emitted when the input value changes (on keystroke)
3660
- */
3661
- leInput;
3662
- handleInput = (ev) => {
3663
- const input = ev.target;
3664
- this.value = input.value;
3665
- this.leInput.emit({
3666
- value: this.value,
3667
- name: this.name,
3668
- externalId: this.externalId,
3669
- });
3670
- };
3671
- handleChange = (ev) => {
3672
- const input = ev.target;
3673
- this.value = input.value;
3674
- this.leChange.emit({
3675
- value: this.value,
3676
- name: this.name,
3677
- externalId: this.externalId,
3678
- });
3679
- };
3680
- handleClick = (ev) => {
3681
- ev.stopPropagation();
3682
- };
3683
- render() {
3684
- return (index.h("le-component", { key: '385fe7607d573ab4cfe81315f74e650f8e57faa1', component: "le-string-input", hostClass: utils.classnames({ disabled: this.disabled }) }, index.h("div", { key: 'a36c5a32238eb6c9c574aeb3955343e042d89225', class: "le-input-wrapper" }, this.label && (index.h("label", { key: 'c1ff01932ada19ecea0cd2e2b477615d0c873ff0', class: "le-input-label", htmlFor: this.name }, this.label)), index.h("div", { key: '7c8909ed5e4e3404a2900a3f9beef8b0f331d62c', class: "le-input-container", part: "container" }, this.iconStart && index.h("span", { key: 'cf7e7355e22de3100bade2399ba63702731350e0', class: "icon-start" }, this.iconStart), index.h("input", { key: '6d71245d395f6cd2c55110ca902fc0374c74026b', ref: el => {
3685
- if (this.inputRef) {
3686
- this.inputRef(el);
3687
- }
3688
- }, id: this.name, type: this.type, name: this.name, value: this.value, placeholder: this.placeholder, disabled: this.disabled, readOnly: this.readonly, onInput: this.handleInput, onChange: this.handleChange, onClick: this.handleClick }), this.iconEnd && index.h("span", { key: 'e086f9559b086212be3cd231b7c870cdb229a733', class: "icon-end" }, this.iconEnd)), !this.hideDescription && (index.h("div", { key: '531ac78b43382166023a79475152345956188bcc', class: "le-input-description" }, index.h("le-slot", { key: '38700e86adc0c3bf049b0ad828d14993ddef7844', name: "description", type: "text", tag: "p", label: "Description" }, index.h("slot", { key: 'c2982e93304a872ec79446fe525eb5448e764401', name: "description" })))))));
3689
- }
3690
- };
3691
- LeStringInput.style = leStringInputCss();
3692
-
3693
- exports.le_bar = LeBar;
3694
- exports.le_button = LeButton;
3695
- exports.le_checkbox = LeCheckbox;
3696
- exports.le_collapse = LeCollapse;
3697
- exports.le_component = LeComponent;
3698
- exports.le_current_heading = LeCurrentHeading;
3699
- exports.le_dropdown_base = LeDropdownBase;
3700
- exports.le_header = LeHeader;
3701
- exports.le_icon = LeIcon;
3702
- exports.le_navigation = LeNavigation;
3703
- exports.le_popover = LePopover;
3704
- exports.le_popup = LePopup;
3705
- exports.le_scroll_progress = LeScrollProgress;
3706
- exports.le_select = LeSelect;
3707
- exports.le_slot = LeSlot;
3708
- exports.le_string_input = LeStringInput;
3709
- //# sourceMappingURL=le-bar.le-button.le-checkbox.le-collapse.le-component.le-current-heading.le-dropdown-base.le-header.le-icon.le-navigation.le-popover.le-popup.le-scroll-progress.le-select.le-slot.le-string-input.entry.cjs.js.map