@syware/jlib 0.0.0-stage → 0.1.0

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 (331) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +116 -2
  3. package/dist/all.d.ts +1 -0
  4. package/dist/all.js +75 -0
  5. package/dist/atoms/jl-avatar.css_raw.js +3 -0
  6. package/dist/atoms/jl-avatar.d.ts +52 -0
  7. package/dist/atoms/jl-avatar.js +167 -0
  8. package/dist/atoms/jl-badge.css_raw.js +3 -0
  9. package/dist/atoms/jl-badge.d.ts +32 -0
  10. package/dist/atoms/jl-badge.js +60 -0
  11. package/dist/atoms/jl-button.css_raw.js +3 -0
  12. package/dist/atoms/jl-button.d.ts +70 -0
  13. package/dist/atoms/jl-button.js +146 -0
  14. package/dist/atoms/jl-checkbox.css_raw.js +3 -0
  15. package/dist/atoms/jl-checkbox.d.ts +61 -0
  16. package/dist/atoms/jl-checkbox.js +149 -0
  17. package/dist/atoms/jl-chip.css_raw.js +3 -0
  18. package/dist/atoms/jl-chip.d.ts +59 -0
  19. package/dist/atoms/jl-chip.js +164 -0
  20. package/dist/atoms/jl-divider.css_raw.js +3 -0
  21. package/dist/atoms/jl-divider.d.ts +26 -0
  22. package/dist/atoms/jl-divider.js +59 -0
  23. package/dist/atoms/jl-icon.css_raw.js +3 -0
  24. package/dist/atoms/jl-icon.d.ts +25 -0
  25. package/dist/atoms/jl-icon.js +41 -0
  26. package/dist/atoms/jl-input.css_raw.js +3 -0
  27. package/dist/atoms/jl-input.d.ts +112 -0
  28. package/dist/atoms/jl-input.js +202 -0
  29. package/dist/atoms/jl-kbd.css_raw.js +3 -0
  30. package/dist/atoms/jl-kbd.d.ts +17 -0
  31. package/dist/atoms/jl-kbd.js +62 -0
  32. package/dist/atoms/jl-link.css_raw.js +3 -0
  33. package/dist/atoms/jl-link.d.ts +37 -0
  34. package/dist/atoms/jl-link.js +86 -0
  35. package/dist/atoms/jl-progress-ring.css_raw.js +3 -0
  36. package/dist/atoms/jl-progress-ring.d.ts +49 -0
  37. package/dist/atoms/jl-progress-ring.js +132 -0
  38. package/dist/atoms/jl-progress.css_raw.js +3 -0
  39. package/dist/atoms/jl-progress.d.ts +43 -0
  40. package/dist/atoms/jl-progress.js +109 -0
  41. package/dist/atoms/jl-rating.css_raw.js +3 -0
  42. package/dist/atoms/jl-rating.d.ts +73 -0
  43. package/dist/atoms/jl-rating.js +184 -0
  44. package/dist/atoms/jl-select.css_raw.js +3 -0
  45. package/dist/atoms/jl-select.d.ts +70 -0
  46. package/dist/atoms/jl-select.js +146 -0
  47. package/dist/atoms/jl-skeleton.css_raw.js +3 -0
  48. package/dist/atoms/jl-skeleton.d.ts +34 -0
  49. package/dist/atoms/jl-skeleton.js +63 -0
  50. package/dist/atoms/jl-slider.css_raw.js +3 -0
  51. package/dist/atoms/jl-slider.d.ts +65 -0
  52. package/dist/atoms/jl-slider.js +143 -0
  53. package/dist/atoms/jl-spinner.css_raw.js +3 -0
  54. package/dist/atoms/jl-spinner.d.ts +16 -0
  55. package/dist/atoms/jl-spinner.js +26 -0
  56. package/dist/atoms/jl-switch.css_raw.js +3 -0
  57. package/dist/atoms/jl-switch.d.ts +57 -0
  58. package/dist/atoms/jl-switch.js +125 -0
  59. package/dist/atoms/jl-textarea.css_raw.js +3 -0
  60. package/dist/atoms/jl-textarea.d.ts +81 -0
  61. package/dist/atoms/jl-textarea.js +148 -0
  62. package/dist/base/jl-element.d.ts +16 -0
  63. package/dist/base/jl-element.js +35 -0
  64. package/dist/base/jl-form-control.d.ts +29 -0
  65. package/dist/base/jl-form-control.js +44 -0
  66. package/dist/index.d.ts +119 -0
  67. package/dist/index.js +72 -0
  68. package/dist/lib/arrow.d.ts +6 -0
  69. package/dist/lib/arrow.js +15 -0
  70. package/dist/lib/attributes.d.ts +9 -0
  71. package/dist/lib/attributes.js +22 -0
  72. package/dist/lib/avatar.d.ts +11 -0
  73. package/dist/lib/avatar.js +37 -0
  74. package/dist/lib/calendar.d.ts +48 -0
  75. package/dist/lib/calendar.js +166 -0
  76. package/dist/lib/css-length.d.ts +2 -0
  77. package/dist/lib/css-length.js +8 -0
  78. package/dist/lib/define.d.ts +9 -0
  79. package/dist/lib/define.js +9 -0
  80. package/dist/lib/dom.d.ts +4 -0
  81. package/dist/lib/dom.js +16 -0
  82. package/dist/lib/files.d.ts +18 -0
  83. package/dist/lib/files.js +89 -0
  84. package/dist/lib/floating.d.ts +13 -0
  85. package/dist/lib/floating.js +127 -0
  86. package/dist/lib/fuzzy.d.ts +26 -0
  87. package/dist/lib/fuzzy.js +149 -0
  88. package/dist/lib/highlight.d.ts +5 -0
  89. package/dist/lib/highlight.js +267 -0
  90. package/dist/lib/hotkey.d.ts +9 -0
  91. package/dist/lib/hotkey.js +101 -0
  92. package/dist/lib/icon-set.d.ts +95 -0
  93. package/dist/lib/icon-set.js +97 -0
  94. package/dist/lib/icons.d.ts +12 -0
  95. package/dist/lib/icons.js +39 -0
  96. package/dist/lib/indicator.d.ts +6 -0
  97. package/dist/lib/indicator.js +18 -0
  98. package/dist/lib/keep-in-view.d.ts +5 -0
  99. package/dist/lib/keep-in-view.js +15 -0
  100. package/dist/lib/key-shortcut.d.ts +20 -0
  101. package/dist/lib/key-shortcut.js +100 -0
  102. package/dist/lib/keycaps.d.ts +8 -0
  103. package/dist/lib/keycaps.js +162 -0
  104. package/dist/lib/locale.d.ts +4 -0
  105. package/dist/lib/locale.js +13 -0
  106. package/dist/lib/nav-tree.d.ts +9 -0
  107. package/dist/lib/nav-tree.js +30 -0
  108. package/dist/lib/numeric.d.ts +20 -0
  109. package/dist/lib/numeric.js +50 -0
  110. package/dist/lib/option-filter.d.ts +29 -0
  111. package/dist/lib/option-filter.js +108 -0
  112. package/dist/lib/option-groups.d.ts +6 -0
  113. package/dist/lib/option-groups.js +26 -0
  114. package/dist/lib/options.d.ts +5 -0
  115. package/dist/lib/options.js +24 -0
  116. package/dist/lib/pagination.d.ts +9 -0
  117. package/dist/lib/pagination.js +52 -0
  118. package/dist/lib/palette.d.ts +8 -0
  119. package/dist/lib/palette.js +46 -0
  120. package/dist/lib/progress.d.ts +20 -0
  121. package/dist/lib/progress.js +33 -0
  122. package/dist/lib/rating.d.ts +7 -0
  123. package/dist/lib/rating.js +26 -0
  124. package/dist/lib/roving-index.d.ts +10 -0
  125. package/dist/lib/roving-index.js +32 -0
  126. package/dist/lib/roving.d.ts +11 -0
  127. package/dist/lib/roving.js +21 -0
  128. package/dist/lib/slots.d.ts +2 -0
  129. package/dist/lib/slots.js +6 -0
  130. package/dist/lib/sparkline.d.ts +11 -0
  131. package/dist/lib/sparkline.js +42 -0
  132. package/dist/lib/split.d.ts +12 -0
  133. package/dist/lib/split.js +37 -0
  134. package/dist/lib/surface.d.ts +7 -0
  135. package/dist/lib/surface.js +28 -0
  136. package/dist/lib/table.d.ts +30 -0
  137. package/dist/lib/table.js +110 -0
  138. package/dist/lib/tags.d.ts +14 -0
  139. package/dist/lib/tags.js +29 -0
  140. package/dist/lib/text.d.ts +5 -0
  141. package/dist/lib/text.js +13 -0
  142. package/dist/lib/tokens.d.ts +5 -0
  143. package/dist/lib/tokens.js +15 -0
  144. package/dist/lib/tree.d.ts +16 -0
  145. package/dist/lib/tree.js +89 -0
  146. package/dist/lib/trend.d.ts +3 -0
  147. package/dist/lib/trend.js +15 -0
  148. package/dist/lib/trigger.d.ts +11 -0
  149. package/dist/lib/trigger.js +34 -0
  150. package/dist/molecules/jl-accordion-item.css_raw.js +3 -0
  151. package/dist/molecules/jl-accordion-item.d.ts +34 -0
  152. package/dist/molecules/jl-accordion-item.js +82 -0
  153. package/dist/molecules/jl-accordion.css_raw.js +3 -0
  154. package/dist/molecules/jl-accordion.d.ts +27 -0
  155. package/dist/molecules/jl-accordion.js +61 -0
  156. package/dist/molecules/jl-alert.css_raw.js +3 -0
  157. package/dist/molecules/jl-alert.d.ts +38 -0
  158. package/dist/molecules/jl-alert.js +131 -0
  159. package/dist/molecules/jl-avatar-group.css_raw.js +3 -0
  160. package/dist/molecules/jl-avatar-group.d.ts +28 -0
  161. package/dist/molecules/jl-avatar-group.js +69 -0
  162. package/dist/molecules/jl-breadcrumb.css_raw.js +3 -0
  163. package/dist/molecules/jl-breadcrumb.d.ts +35 -0
  164. package/dist/molecules/jl-breadcrumb.js +126 -0
  165. package/dist/molecules/jl-calendar.css_raw.js +3 -0
  166. package/dist/molecules/jl-calendar.d.ts +101 -0
  167. package/dist/molecules/jl-calendar.js +314 -0
  168. package/dist/molecules/jl-card.css_raw.js +3 -0
  169. package/dist/molecules/jl-card.d.ts +46 -0
  170. package/dist/molecules/jl-card.js +172 -0
  171. package/dist/molecules/jl-code-block.css_raw.js +3 -0
  172. package/dist/molecules/jl-code-block.d.ts +44 -0
  173. package/dist/molecules/jl-code-block.js +106 -0
  174. package/dist/molecules/jl-combobox.css_raw.js +3 -0
  175. package/dist/molecules/jl-combobox.d.ts +120 -0
  176. package/dist/molecules/jl-combobox.js +450 -0
  177. package/dist/molecules/jl-copy-button.css_raw.js +3 -0
  178. package/dist/molecules/jl-copy-button.d.ts +31 -0
  179. package/dist/molecules/jl-copy-button.js +69 -0
  180. package/dist/molecules/jl-date-picker.css_raw.js +3 -0
  181. package/dist/molecules/jl-date-picker.d.ts +109 -0
  182. package/dist/molecules/jl-date-picker.js +322 -0
  183. package/dist/molecules/jl-empty.css_raw.js +3 -0
  184. package/dist/molecules/jl-empty.d.ts +32 -0
  185. package/dist/molecules/jl-empty.js +94 -0
  186. package/dist/molecules/jl-field.css_raw.js +3 -0
  187. package/dist/molecules/jl-field.d.ts +49 -0
  188. package/dist/molecules/jl-field.js +147 -0
  189. package/dist/molecules/jl-file-drop.css_raw.js +3 -0
  190. package/dist/molecules/jl-file-drop.d.ts +93 -0
  191. package/dist/molecules/jl-file-drop.js +366 -0
  192. package/dist/molecules/jl-list-item.css_raw.js +3 -0
  193. package/dist/molecules/jl-list-item.d.ts +56 -0
  194. package/dist/molecules/jl-list-item.js +176 -0
  195. package/dist/molecules/jl-menu.css_raw.js +3 -0
  196. package/dist/molecules/jl-menu.d.ts +62 -0
  197. package/dist/molecules/jl-menu.js +288 -0
  198. package/dist/molecules/jl-number-input.css_raw.js +3 -0
  199. package/dist/molecules/jl-number-input.d.ts +81 -0
  200. package/dist/molecules/jl-number-input.js +220 -0
  201. package/dist/molecules/jl-pagination.css_raw.js +3 -0
  202. package/dist/molecules/jl-pagination.d.ts +59 -0
  203. package/dist/molecules/jl-pagination.js +172 -0
  204. package/dist/molecules/jl-popover.css_raw.js +3 -0
  205. package/dist/molecules/jl-popover.d.ts +49 -0
  206. package/dist/molecules/jl-popover.js +152 -0
  207. package/dist/molecules/jl-radio-group.css_raw.js +3 -0
  208. package/dist/molecules/jl-radio-group.d.ts +71 -0
  209. package/dist/molecules/jl-radio-group.js +202 -0
  210. package/dist/molecules/jl-search.css_raw.js +3 -0
  211. package/dist/molecules/jl-search.d.ts +71 -0
  212. package/dist/molecules/jl-search.js +210 -0
  213. package/dist/molecules/jl-segmented.css_raw.js +3 -0
  214. package/dist/molecules/jl-segmented.d.ts +65 -0
  215. package/dist/molecules/jl-segmented.js +187 -0
  216. package/dist/molecules/jl-stat.css_raw.js +3 -0
  217. package/dist/molecules/jl-stat.d.ts +55 -0
  218. package/dist/molecules/jl-stat.js +169 -0
  219. package/dist/molecules/jl-steps.css_raw.js +3 -0
  220. package/dist/molecules/jl-steps.d.ts +44 -0
  221. package/dist/molecules/jl-steps.js +118 -0
  222. package/dist/molecules/jl-tab-panel.css_raw.js +3 -0
  223. package/dist/molecules/jl-tab-panel.d.ts +35 -0
  224. package/dist/molecules/jl-tab-panel.js +35 -0
  225. package/dist/molecules/jl-tabs.css_raw.js +3 -0
  226. package/dist/molecules/jl-tabs.d.ts +51 -0
  227. package/dist/molecules/jl-tabs.js +198 -0
  228. package/dist/molecules/jl-tag-input.css_raw.js +3 -0
  229. package/dist/molecules/jl-tag-input.d.ts +103 -0
  230. package/dist/molecules/jl-tag-input.js +429 -0
  231. package/dist/molecules/jl-timeline.css_raw.js +3 -0
  232. package/dist/molecules/jl-timeline.d.ts +28 -0
  233. package/dist/molecules/jl-timeline.js +81 -0
  234. package/dist/molecules/jl-toast.css_raw.js +3 -0
  235. package/dist/molecules/jl-toast.d.ts +75 -0
  236. package/dist/molecules/jl-toast.js +274 -0
  237. package/dist/molecules/jl-tooltip.css_raw.js +3 -0
  238. package/dist/molecules/jl-tooltip.d.ts +44 -0
  239. package/dist/molecules/jl-tooltip.js +160 -0
  240. package/dist/organisms/jl-command-palette.css_raw.js +3 -0
  241. package/dist/organisms/jl-command-palette.d.ts +60 -0
  242. package/dist/organisms/jl-command-palette.js +285 -0
  243. package/dist/organisms/jl-confirm.css_raw.js +3 -0
  244. package/dist/organisms/jl-confirm.d.ts +56 -0
  245. package/dist/organisms/jl-confirm.js +132 -0
  246. package/dist/organisms/jl-dialog.css_raw.js +3 -0
  247. package/dist/organisms/jl-dialog.d.ts +43 -0
  248. package/dist/organisms/jl-dialog.js +120 -0
  249. package/dist/organisms/jl-drawer.css_raw.js +3 -0
  250. package/dist/organisms/jl-drawer.d.ts +50 -0
  251. package/dist/organisms/jl-drawer.js +141 -0
  252. package/dist/organisms/jl-navbar.css_raw.js +3 -0
  253. package/dist/organisms/jl-navbar.d.ts +32 -0
  254. package/dist/organisms/jl-navbar.js +121 -0
  255. package/dist/organisms/jl-page-header.css_raw.js +3 -0
  256. package/dist/organisms/jl-page-header.d.ts +26 -0
  257. package/dist/organisms/jl-page-header.js +85 -0
  258. package/dist/organisms/jl-side-nav.css_raw.js +3 -0
  259. package/dist/organisms/jl-side-nav.d.ts +60 -0
  260. package/dist/organisms/jl-side-nav.js +269 -0
  261. package/dist/organisms/jl-table.css_raw.js +3 -0
  262. package/dist/organisms/jl-table.d.ts +109 -0
  263. package/dist/organisms/jl-table.js +318 -0
  264. package/dist/organisms/jl-toaster.css_raw.js +3 -0
  265. package/dist/organisms/jl-toaster.d.ts +48 -0
  266. package/dist/organisms/jl-toaster.js +142 -0
  267. package/dist/organisms/jl-tree.css_raw.js +3 -0
  268. package/dist/organisms/jl-tree.d.ts +59 -0
  269. package/dist/organisms/jl-tree.js +198 -0
  270. package/dist/styles/base.css_raw.js +3 -0
  271. package/dist/styles/control.css_raw.js +3 -0
  272. package/dist/styles/floating.css_raw.js +3 -0
  273. package/dist/styles/shared.d.ts +8 -0
  274. package/dist/styles/shared.js +14 -0
  275. package/dist/styles/tokens.css_raw.js +3 -0
  276. package/dist/templates/jl-app-shell.css_raw.js +3 -0
  277. package/dist/templates/jl-app-shell.d.ts +37 -0
  278. package/dist/templates/jl-app-shell.js +150 -0
  279. package/dist/templates/jl-split-view.css_raw.js +3 -0
  280. package/dist/templates/jl-split-view.d.ts +49 -0
  281. package/dist/templates/jl-split-view.js +149 -0
  282. package/dist/tokens.css +188 -0
  283. package/dist/types/calendar.types.d.ts +11 -0
  284. package/dist/types/files.types.d.ts +23 -0
  285. package/dist/types/floating.types.d.ts +13 -0
  286. package/dist/types/fuzzy.types.d.ts +10 -0
  287. package/dist/types/highlight.types.d.ts +6 -0
  288. package/dist/types/hotkey.types.d.ts +19 -0
  289. package/dist/types/jl-accordion.types.d.ts +2 -0
  290. package/dist/types/jl-avatar.types.d.ts +10 -0
  291. package/dist/types/jl-breadcrumb.types.d.ts +6 -0
  292. package/dist/types/jl-button.types.d.ts +3 -0
  293. package/dist/types/jl-card.types.d.ts +2 -0
  294. package/dist/types/jl-chip.types.d.ts +1 -0
  295. package/dist/types/jl-combobox.types.d.ts +8 -0
  296. package/dist/types/jl-command-palette.types.d.ts +22 -0
  297. package/dist/types/jl-confirm.types.d.ts +10 -0
  298. package/dist/types/jl-dialog.types.d.ts +1 -0
  299. package/dist/types/jl-divider.types.d.ts +2 -0
  300. package/dist/types/jl-drawer.types.d.ts +4 -0
  301. package/dist/types/jl-empty.types.d.ts +2 -0
  302. package/dist/types/jl-field.types.d.ts +2 -0
  303. package/dist/types/jl-input.types.d.ts +1 -0
  304. package/dist/types/jl-link.types.d.ts +2 -0
  305. package/dist/types/jl-menu.types.d.ts +22 -0
  306. package/dist/types/jl-navbar.types.d.ts +6 -0
  307. package/dist/types/jl-progress.types.d.ts +3 -0
  308. package/dist/types/jl-radio-group.types.d.ts +10 -0
  309. package/dist/types/jl-segmented.types.d.ts +7 -0
  310. package/dist/types/jl-select.types.d.ts +12 -0
  311. package/dist/types/jl-side-nav.types.d.ts +17 -0
  312. package/dist/types/jl-skeleton.types.d.ts +2 -0
  313. package/dist/types/jl-slider.types.d.ts +5 -0
  314. package/dist/types/jl-split-view.types.d.ts +2 -0
  315. package/dist/types/jl-stat.types.d.ts +2 -0
  316. package/dist/types/jl-steps.types.d.ts +8 -0
  317. package/dist/types/jl-switch.types.d.ts +1 -0
  318. package/dist/types/jl-table.types.d.ts +30 -0
  319. package/dist/types/jl-tabs.types.d.ts +2 -0
  320. package/dist/types/jl-timeline.types.d.ts +10 -0
  321. package/dist/types/jl-toast.types.d.ts +2 -0
  322. package/dist/types/jl-toaster.types.d.ts +23 -0
  323. package/dist/types/jl-tree.types.d.ts +26 -0
  324. package/dist/types/key-shortcut.types.d.ts +9 -0
  325. package/dist/types/keycaps.types.d.ts +7 -0
  326. package/dist/types/option-filter.types.d.ts +10 -0
  327. package/dist/types/options.types.d.ts +4 -0
  328. package/dist/types/pagination.types.d.ts +8 -0
  329. package/dist/types/roving-index.types.d.ts +2 -0
  330. package/dist/types/sparkline.types.d.ts +11 -0
  331. package/package.json +96 -4
@@ -0,0 +1,198 @@
1
+ import { BASE_STYLES } from "../styles/shared.js";
2
+ import { JlElement } from "../base/jl-element.js";
3
+ import { jsonOf } from "../lib/attributes.js";
4
+ import { registerIcons } from "../lib/icons.js";
5
+ import { setOrRemove, uniqueId } from "../lib/dom.js";
6
+ import { JlIcon } from "../atoms/jl-icon.js";
7
+ import { allItems, isFolder, treeKeyAction, withExpanded } from "../lib/tree.js";
8
+ import jl_tree_default from "./jl-tree.css_raw.js";
9
+ import { css } from "@jadis/core";
10
+ import { ChevronRight, File, Folder, FolderOpen } from "lucide";
11
+ import { jsx, jsxs } from "@jadis/core/jsx-runtime";
12
+ // src/organisms/jl-tree.tsx
13
+ registerIcons({
14
+ "chevron-right": ChevronRight,
15
+ file: File,
16
+ folder: Folder,
17
+ "folder-open": FolderOpen
18
+ });
19
+ var ICON_SIZE = 16;
20
+ var CHEVRON_SIZE = 14;
21
+ var FOLDER_ICON = "folder";
22
+ var OPEN_FOLDER_ICON = "folder-open";
23
+ var FILE_ICON = "file";
24
+ /**
25
+ * Nested items that open and close, as files in folders: `items` holds them, `expanded` lists the open folders and
26
+ * `value` is the selected item. The keyboard follows the tree pattern: Up and Down move, Right opens or enters a
27
+ * folder, Left closes it or goes to the parent, Home and End jump, Enter selects. Emits `select` and `toggle`.
28
+ */
29
+ var JlTree = class extends JlElement {
30
+ static selector = "jl-tree";
31
+ events = this.useEvents();
32
+ items = this.useChange([], () => this.renderItems(), {
33
+ attribute: "items",
34
+ parse: jsonOf([])
35
+ });
36
+ /** The ids of the open folders. */
37
+ expanded = this.useChange([], () => this.renderExpanded(), {
38
+ attribute: "expanded",
39
+ parse: jsonOf([])
40
+ });
41
+ /** The id of the selected item. */
42
+ value = this.useChange("", () => this.renderSelection(), { attribute: "value" });
43
+ /** The tree's name for screen readers. */
44
+ label = this.useChange("", (value) => setOrRemove(this._refs.tree, "aria-label", value), { attribute: "label" });
45
+ _rows = /* @__PURE__ */ new Map();
46
+ _focus = { id: "" };
47
+ _refs = this.useRefs((ref) => ({ tree: ref(".tree") }));
48
+ /** Opens or closes a folder, as a press on its chevron would. */
49
+ toggle(id, open) {
50
+ this.expanded.set(withExpanded(this.expanded.get(), id, open));
51
+ this.events.emit("toggle", {
52
+ id,
53
+ open
54
+ });
55
+ }
56
+ templateHtml() {
57
+ return /* @__PURE__ */ jsx("div", {
58
+ class: "tree",
59
+ role: "tree",
60
+ attrs: { part: "base" }
61
+ });
62
+ }
63
+ templateCss() {
64
+ return css`${BASE_STYLES}${jl_tree_default}`;
65
+ }
66
+ onConnect() {
67
+ const tree = this._refs.tree;
68
+ this.on(tree, "click", (event) => this.press(event));
69
+ this.on(tree, "keydown", (event) => this.handleKey(event));
70
+ this.on(tree, "focusin", (event) => {
71
+ const id = this.idOf(event.target);
72
+ if (id !== null) this.moveFocus(id, false);
73
+ });
74
+ }
75
+ idOf(target) {
76
+ return (target instanceof Element ? target.closest("[role=\"treeitem\"]") : null)?.dataset.id ?? null;
77
+ }
78
+ /** A press selects the item; on a folder it also opens or closes it, and the chevron only does that. */
79
+ press(event) {
80
+ const id = this.idOf(event.target);
81
+ const item = id === null ? void 0 : this.findItem(id);
82
+ if (id === null || item === void 0) return;
83
+ const onChevron = event.target instanceof Element && event.target.closest(".chevron") !== null;
84
+ if (isFolder(item)) this.toggle(id, !this.expanded.get().includes(id));
85
+ if (!onChevron) this.select(id);
86
+ this.moveFocus(id, true);
87
+ }
88
+ handleKey(event) {
89
+ const focused = this._focus.id;
90
+ const action = treeKeyAction(this.items.get(), new Set(this.expanded.get()), focused, event.key);
91
+ if (action === null) return;
92
+ event.preventDefault();
93
+ ({
94
+ focus: () => this.moveFocus(action.id, true),
95
+ select: () => this.select(action.id),
96
+ toggle: () => this.toggle(action.id, action.kind === "toggle" && action.open)
97
+ })[action.kind]();
98
+ }
99
+ select(id) {
100
+ this.value.set(id);
101
+ this.events.emit("select", id);
102
+ }
103
+ findItem(id) {
104
+ return allItems(this.items.get()).find((entry) => entry.item.id === id)?.item;
105
+ }
106
+ /** One item takes the Tab stop: the focused one, else the selected one, else the first. */
107
+ moveFocus(id, focus) {
108
+ const row = this._rows.get(id);
109
+ if (row === void 0) return;
110
+ this._rows.get(this._focus.id)?.setAttribute("tabindex", "-1");
111
+ this._focus.id = id;
112
+ row.setAttribute("tabindex", "0");
113
+ if (focus) row.focus();
114
+ }
115
+ renderItems() {
116
+ this._rows.clear();
117
+ this._refs.tree.replaceChildren(...this.items.get().map((item) => this.itemNode(item, 1)));
118
+ this.renderExpanded();
119
+ this.renderSelection();
120
+ const start = this._rows.has(this.value.get()) ? this.value.get() : this.items.get()[0]?.id ?? "";
121
+ this.moveFocus(this._rows.has(this._focus.id) ? this._focus.id : start, false);
122
+ }
123
+ itemNode(item, level) {
124
+ const folder = isFolder(item);
125
+ const labelId = uniqueId("tree-label");
126
+ const iconClass = item.icon === void 0 && folder ? "icon auto" : "icon";
127
+ const node = /* @__PURE__ */ jsx("div", {
128
+ class: "item",
129
+ role: "treeitem",
130
+ "data-id": item.id,
131
+ tabIndex: -1,
132
+ "aria-selected": "false",
133
+ "aria-labelledby": labelId,
134
+ children: /* @__PURE__ */ jsxs("div", {
135
+ class: "row",
136
+ children: [
137
+ /* @__PURE__ */ jsx("span", {
138
+ class: "chevron",
139
+ "aria-hidden": "true",
140
+ children: folder ? /* @__PURE__ */ jsx(JlIcon, {
141
+ name: "chevron-right",
142
+ size: CHEVRON_SIZE
143
+ }) : null
144
+ }),
145
+ /* @__PURE__ */ jsx(JlIcon, {
146
+ class: iconClass,
147
+ size: ICON_SIZE,
148
+ name: item.icon ?? (folder ? FOLDER_ICON : FILE_ICON)
149
+ }),
150
+ /* @__PURE__ */ jsx("span", {
151
+ class: "label",
152
+ id: labelId,
153
+ children: item.label
154
+ })
155
+ ]
156
+ })
157
+ });
158
+ node.style.setProperty("--level", String(level - 1));
159
+ this._rows.set(item.id, node);
160
+ if (folder) {
161
+ const group = /* @__PURE__ */ jsx("div", {
162
+ class: "group",
163
+ role: "group"
164
+ });
165
+ group.style.setProperty("--level", String(level - 1));
166
+ group.replaceChildren(...(item.children ?? []).map((child) => this.itemNode(child, level + 1)));
167
+ node.append(group);
168
+ }
169
+ return node;
170
+ }
171
+ renderExpanded() {
172
+ const expanded = new Set(this.expanded.get());
173
+ for (const [id, row] of this._rows) {
174
+ const group = row.querySelector(":scope > .group");
175
+ if (group === null) continue;
176
+ const open = expanded.has(id);
177
+ row.setAttribute("aria-expanded", String(open));
178
+ group.hidden = !open;
179
+ row.querySelector(":scope > .row > .icon.auto")?.name.set(open ? OPEN_FOLDER_ICON : FOLDER_ICON);
180
+ }
181
+ if (!this.isVisible(this._focus.id)) this.moveFocus(this.visibleAncestor(this._focus.id), false);
182
+ }
183
+ isVisible(id) {
184
+ const row = this._rows.get(id);
185
+ return row !== void 0 && row.closest(".group[hidden]") === null;
186
+ }
187
+ /** The item itself when it shows, else the nearest folder around it that does: the focus goes there. */
188
+ visibleAncestor(id) {
189
+ const owner = (this._rows.get(id)?.closest(".group[hidden]"))?.closest("[role=\"treeitem\"]");
190
+ return owner?.dataset.id === void 0 ? id : this.visibleAncestor(owner.dataset.id);
191
+ }
192
+ renderSelection() {
193
+ const value = this.value.get();
194
+ for (const [id, row] of this._rows) row.setAttribute("aria-selected", String(id === value));
195
+ }
196
+ };
197
+ JlTree.register();
198
+ export { JlTree };
@@ -0,0 +1,3 @@
1
+ // src/styles/base.css?raw
2
+ var base_default = "/* Shared by every component, first in its shadow root. */\n:host {\n box-sizing: border-box;\n font-family: var(--jl-font-sans);\n -webkit-tap-highlight-color: transparent;\n}\n\n/* A component's own display would otherwise win over the browser's rule for hidden. */\n:host([hidden]) {\n /* biome-ignore lint/complexity/noImportantStyles: hidden must win over the component's own display */\n display: none !important;\n}\n\n*,\n*::before,\n*::after {\n box-sizing: inherit;\n}\n\n[hidden] {\n /* biome-ignore lint/complexity/noImportantStyles: hidden must win over every component's own display rule */\n display: none !important;\n}\n\nbutton,\ninput,\nselect,\ntextarea {\n font: inherit;\n color: inherit;\n}\n\n:focus-visible {\n outline: none;\n box-shadow: var(--jl-focus-ring);\n}\n\nh1,\nh2,\nh3,\nh4,\np,\nul,\nol,\nfigure {\n margin: 0;\n}\n\ncode,\nkbd,\npre {\n font-family: var(--jl-font-mono);\n}\n\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n}\n\n@media (prefers-reduced-motion: reduce) {\n *,\n *::before,\n *::after {\n /* biome-ignore lint/complexity/noImportantStyles: reduced motion must win over every component's animation */\n animation-duration: 1ms !important;\n /* biome-ignore lint/complexity/noImportantStyles: an endless animation cut to 1ms would flicker */\n animation-iteration-count: 1 !important;\n /* biome-ignore lint/complexity/noImportantStyles: reduced motion must win over every component's transition */\n transition-duration: 1ms !important;\n }\n}\n";
3
+ export { base_default as default };
@@ -0,0 +1,3 @@
1
+ // src/styles/control.css?raw
2
+ var control_default = "/* The look of the fields people type in: text fields, selects and areas share it. */\n.control {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n min-height: var(--height, var(--jl-control-md));\n padding: 0 12px;\n border: 1px solid var(--jl-line-strong);\n border-radius: var(--jl-radius-sm);\n background: var(--jl-surface);\n color: var(--jl-text);\n font-size: var(--jl-font-md);\n line-height: 20px;\n transition:\n border-color var(--jl-fast) var(--jl-ease),\n box-shadow var(--jl-fast) var(--jl-ease);\n}\n\n.control:hover {\n border-color: color-mix(in srgb, var(--jl-line-strong) 60%, var(--jl-text) 18%);\n}\n\n.control:focus-within {\n border-color: var(--jl-accent);\n box-shadow: var(--jl-focus-ring);\n}\n\n:host([invalid]) .control {\n border-color: var(--jl-danger);\n}\n\n:host([invalid]) .control:focus-within {\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--jl-danger) 25%, transparent);\n}\n\n:host([disabled]) .control,\n:host(:disabled) .control {\n background: var(--jl-surface-2);\n color: var(--jl-muted);\n cursor: not-allowed;\n}\n\n:host([size=\"sm\"]) {\n --height: var(--jl-control-sm);\n}\n\n:host([size=\"lg\"]) {\n --height: var(--jl-control-lg);\n}\n\n/* The native element inside the frame: it takes the room and leaves the frame to draw the border. */\n.native {\n flex: 1;\n min-width: 0;\n height: calc(var(--height, var(--jl-control-md)) - 2px);\n padding: 0;\n border: none;\n outline: none;\n background: transparent;\n color: inherit;\n font: inherit;\n}\n\n.native::placeholder {\n color: var(--jl-faint);\n}\n\n.native:focus-visible {\n box-shadow: none;\n}\n\n.native:disabled {\n cursor: not-allowed;\n}\n\n.adornment {\n display: inline-flex;\n flex: none;\n align-items: center;\n color: var(--jl-faint);\n}\n\n/* A small button inside the frame: clear, reveal the password, step a number. */\n.action {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n margin-right: -4px;\n padding: 0;\n border: none;\n border-radius: var(--jl-radius-xs);\n background: none;\n color: var(--jl-faint);\n cursor: pointer;\n}\n\n.action:hover {\n background: var(--jl-surface-3);\n color: var(--jl-text);\n}\n";
3
+ export { control_default as default };
@@ -0,0 +1,3 @@
1
+ // src/styles/floating.css?raw
2
+ var floating_default = "/*\n * What floats over the page in the top layer (the popover API): tooltips, menus, popovers, lists of options.\n * The browser's own popover box is centred in the window; this one is placed by script next to its anchor.\n */\n.floating {\n position: fixed;\n inset: auto;\n margin: 0;\n padding: 0;\n border: none;\n background: none;\n color: var(--jl-text);\n overflow: visible;\n}\n\n.floating:popover-open {\n animation: jl-float-in var(--jl-fast) var(--jl-ease);\n}\n\n/* The look of a floating panel: menus, popovers and lists of options share it. */\n.panel {\n border: 1px solid var(--jl-line);\n border-radius: var(--jl-radius);\n background: var(--jl-surface);\n box-shadow: var(--jl-shadow-lg);\n}\n\n@keyframes jl-float-in {\n from {\n opacity: 0;\n transform: translateY(-2px) scale(0.98);\n }\n}\n";
3
+ export { floating_default as default };
@@ -0,0 +1,8 @@
1
+ /** First in every shadow root. */
2
+ export declare const BASE_STYLES: string;
3
+ /** For the components that hold a text field, a select or a text area: the `.control` frame. */
4
+ export declare const CONTROL_STYLES: string;
5
+ /** For the components that open something over the page: `.floating` (the top-layer box) and `.panel`. */
6
+ export declare const FLOATING_STYLES: string;
7
+ /** The design tokens, adopted by the document when the first component registers. */
8
+ export declare const TOKEN_STYLES: string;
@@ -0,0 +1,14 @@
1
+ import base_default from "./base.css_raw.js";
2
+ import control_default from "./control.css_raw.js";
3
+ import floating_default from "./floating.css_raw.js";
4
+ import tokens_default from "./tokens.css_raw.js";
5
+ // src/styles/shared.ts
6
+ /** First in every shadow root. */
7
+ var BASE_STYLES = base_default;
8
+ /** For the components that hold a text field, a select or a text area: the `.control` frame. */
9
+ var CONTROL_STYLES = control_default;
10
+ /** For the components that open something over the page: `.floating` (the top-layer box) and `.panel`. */
11
+ var FLOATING_STYLES = floating_default;
12
+ /** The design tokens, adopted by the document when the first component registers. */
13
+ var TOKEN_STYLES = tokens_default;
14
+ export { BASE_STYLES, CONTROL_STYLES, FLOATING_STYLES, TOKEN_STYLES };
@@ -0,0 +1,3 @@
1
+ // src/styles/tokens.css?raw
2
+ var tokens_default = "/*\n * The design tokens, as swai and swcode have them: ink and white, flat surfaces separated by lines, and one blue\n * that marks what is live, selected or waiting for a person. Custom properties cross shadow roots, so every\n * component reads them. They sit in the `jlib` layer: any unlayered rule of the page overrides them.\n */\n@layer jlib {\n :root,\n [data-theme=\"light\"] {\n color-scheme: light;\n --jl-font-sans: \"Manrope Variable\", \"Manrope\", system-ui, -apple-system, \"Segoe UI\", sans-serif;\n --jl-font-mono: \"JetBrains Mono Variable\", \"JetBrains Mono\", ui-monospace, \"SF Mono\", Menlo, monospace;\n\n --jl-font-xs: 12px;\n --jl-font-sm: 13px;\n --jl-font-md: 14px;\n --jl-font-lg: 16px;\n --jl-font-xl: 20px;\n --jl-font-2xl: 28px;\n\n --jl-text: #0b0b0c;\n --jl-text-2: #3a3f47;\n /* Secondary text: 6.2:1 on white. */\n --jl-muted: #5b616b;\n /* Icons and decorations only: 3.2:1 on white is enough for them, not for text. */\n --jl-faint: #8a9099;\n\n --jl-bg: #ffffff;\n --jl-surface: #ffffff;\n --jl-surface-2: #f7f8f9;\n --jl-surface-3: #eef0f2;\n --jl-code-bg: #f0f2f4;\n --jl-line: #e6e8eb;\n --jl-line-soft: #eef0f2;\n --jl-line-strong: #d5d9de;\n\n /* The SyWare logo's blue: marks, focus rings, progress. 3.8:1 on white: enough for shapes, never for text. */\n --jl-brand: #0e88d3;\n /* The same hue, deep enough to carry text: buttons, links, the selection. 5.2:1 on white. */\n --jl-accent: #0c72b0;\n --jl-accent-strong: #095a8b;\n --jl-accent-soft: #e9f4fb;\n --jl-accent-line: #b7dbf2;\n --jl-on-accent: #ffffff;\n\n --jl-ok: #17784a;\n --jl-ok-soft: #ecf6f0;\n --jl-warn: #9a5b00;\n --jl-warn-soft: #fcf4e6;\n --jl-danger: #b42318;\n --jl-danger-soft: #fdf0ef;\n --jl-on-danger: #ffffff;\n --jl-info: #0c72b0;\n --jl-info-soft: #e9f4fb;\n\n /* Code: the accent for keywords, quiet colours from the same family for the rest. */\n --jl-code-keyword: #0c72b0;\n --jl-code-tag: #095a8b;\n --jl-code-attribute: #5b48c8;\n --jl-code-string: #17784a;\n --jl-code-number: #9a5b00;\n --jl-code-function: #3d5a80;\n --jl-code-property: #127a72;\n --jl-code-comment: #8a9099;\n\n --jl-radius-xs: 6px;\n --jl-radius-sm: 8px;\n --jl-radius: 10px;\n --jl-radius-lg: 12px;\n --jl-radius-xl: 20px;\n --jl-radius-pill: 999px;\n\n /* Spacing: a 4px grid, for the layouts pages build around the components. */\n --jl-space-1: 4px;\n --jl-space-2: 8px;\n --jl-space-3: 12px;\n --jl-space-4: 16px;\n --jl-space-5: 20px;\n --jl-space-6: 24px;\n --jl-space-8: 32px;\n --jl-space-10: 40px;\n --jl-space-12: 48px;\n --jl-space-16: 64px;\n\n --jl-control-sm: 30px;\n --jl-control-md: 36px;\n --jl-control-lg: 44px;\n\n /* Flat: lines separate; only what floats (menus, dialogs, toasts) casts a shadow. */\n --jl-shadow-sm: 0 1px 2px rgb(11 11 12 / 0.05);\n --jl-shadow: 0 6px 16px -6px rgb(11 11 12 / 0.12);\n --jl-shadow-lg: 0 18px 44px -14px rgb(11 11 12 / 0.22);\n --jl-focus-ring: 0 0 0 3px color-mix(in srgb, var(--jl-brand) 35%, transparent);\n --jl-scrim: rgb(11 11 12 / 0.38);\n\n --jl-ease: cubic-bezier(0.2, 0.7, 0.2, 1);\n --jl-fast: 120ms;\n --jl-slow: 240ms;\n }\n\n @media (prefers-color-scheme: dark) {\n :root:not([data-theme=\"light\"]) {\n color-scheme: dark;\n --jl-text: #eceef1;\n --jl-text-2: #c3c8cf;\n --jl-muted: #959ca6;\n --jl-faint: #6b727c;\n --jl-bg: #0f1113;\n --jl-surface: #0f1113;\n --jl-surface-2: #16191c;\n --jl-surface-3: #1d2125;\n --jl-code-bg: #1d2125;\n --jl-line: #262a2f;\n --jl-line-soft: #1f2327;\n --jl-line-strong: #343a41;\n --jl-brand: #1a9be0;\n --jl-accent: #3ea6e8;\n --jl-accent-strong: #6cc0f2;\n --jl-accent-soft: #0f2a3b;\n --jl-accent-line: #1e4a66;\n --jl-on-accent: #04131c;\n --jl-ok: #4cc38a;\n --jl-ok-soft: #10261c;\n --jl-warn: #e5a54b;\n --jl-warn-soft: #2a2012;\n --jl-danger: #f0786c;\n --jl-danger-soft: #3a1a17;\n --jl-on-danger: #1c0604;\n --jl-info: #3ea6e8;\n --jl-info-soft: #0f2a3b;\n --jl-code-keyword: #3ea6e8;\n --jl-code-tag: #6cc0f2;\n --jl-code-attribute: #a99bf0;\n --jl-code-string: #4cc38a;\n --jl-code-number: #e5a54b;\n --jl-code-function: #8fb0d6;\n --jl-code-property: #4fc1b5;\n --jl-code-comment: #6b727c;\n --jl-shadow-sm: 0 1px 2px rgb(0 0 0 / 0.4);\n --jl-shadow: 0 6px 18px -6px rgb(0 0 0 / 0.6);\n --jl-shadow-lg: 0 18px 44px -12px rgb(0 0 0 / 0.7);\n --jl-scrim: rgb(0 0 0 / 0.55);\n }\n }\n\n /* Dark: the same blue, lifted to read on a dark ground, with dark text on the buttons. */\n [data-theme=\"dark\"] {\n color-scheme: dark;\n --jl-text: #eceef1;\n --jl-text-2: #c3c8cf;\n --jl-muted: #959ca6;\n --jl-faint: #6b727c;\n --jl-bg: #0f1113;\n --jl-surface: #0f1113;\n --jl-surface-2: #16191c;\n --jl-surface-3: #1d2125;\n --jl-code-bg: #1d2125;\n --jl-line: #262a2f;\n --jl-line-soft: #1f2327;\n --jl-line-strong: #343a41;\n --jl-brand: #1a9be0;\n --jl-accent: #3ea6e8;\n --jl-accent-strong: #6cc0f2;\n --jl-accent-soft: #0f2a3b;\n --jl-accent-line: #1e4a66;\n --jl-on-accent: #04131c;\n --jl-ok: #4cc38a;\n --jl-ok-soft: #10261c;\n --jl-warn: #e5a54b;\n --jl-warn-soft: #2a2012;\n --jl-danger: #f0786c;\n --jl-danger-soft: #3a1a17;\n --jl-on-danger: #1c0604;\n --jl-info: #3ea6e8;\n --jl-info-soft: #0f2a3b;\n --jl-code-keyword: #3ea6e8;\n --jl-code-tag: #6cc0f2;\n --jl-code-attribute: #a99bf0;\n --jl-code-string: #4cc38a;\n --jl-code-number: #e5a54b;\n --jl-code-function: #8fb0d6;\n --jl-code-property: #4fc1b5;\n --jl-code-comment: #6b727c;\n --jl-shadow-sm: 0 1px 2px rgb(0 0 0 / 0.4);\n --jl-shadow: 0 6px 18px -6px rgb(0 0 0 / 0.6);\n --jl-shadow-lg: 0 18px 44px -12px rgb(0 0 0 / 0.7);\n --jl-scrim: rgb(0 0 0 / 0.55);\n }\n}\n";
3
+ export { tokens_default as default };
@@ -0,0 +1,3 @@
1
+ // src/templates/jl-app-shell.css?raw
2
+ var jl_app_shell_default = ":host {\n display: block;\n height: 100%;\n min-height: 0;\n container: jl-app-shell / inline-size;\n --header-height: 56px;\n --drawer-width: min(300px, 86%);\n}\n\n.shell {\n position: relative;\n display: grid;\n grid-template:\n \"top top top\" auto\n \"nav main aside\" minmax(0, 1fr)\n / auto minmax(0, 1fr) auto;\n height: 100%;\n overflow: hidden;\n background: var(--jl-bg);\n color: var(--jl-text);\n}\n\n.top {\n grid-area: top;\n display: flex;\n align-items: center;\n gap: 8px;\n min-height: var(--header-height);\n padding: 0 16px;\n border-bottom: 1px solid var(--jl-line);\n background: var(--jl-surface);\n}\n\n:host(:not([has-header])) .top {\n display: none;\n}\n\n::slotted([slot=\"header\"]) {\n flex: 1;\n min-width: 0;\n}\n\n.menu {\n display: none;\n flex: none;\n align-items: center;\n justify-content: center;\n width: var(--jl-control-md);\n height: var(--jl-control-md);\n margin-left: -8px;\n padding: 0;\n border: none;\n border-radius: var(--jl-radius-sm);\n background: none;\n color: var(--jl-text-2);\n cursor: pointer;\n}\n\n.menu:hover {\n background: var(--jl-surface-3);\n color: var(--jl-text);\n}\n\n.nav {\n grid-area: nav;\n min-height: 0;\n overflow-y: auto;\n border-right: 1px solid var(--jl-line);\n background: var(--jl-surface);\n}\n\n.nav:focus-visible {\n box-shadow: none;\n}\n\n:host(:not([has-nav])) .nav {\n display: none;\n}\n\n.main {\n grid-area: main;\n min-width: 0;\n min-height: 0;\n overflow: auto;\n}\n\n.aside {\n grid-area: aside;\n min-height: 0;\n overflow-y: auto;\n border-left: 1px solid var(--jl-line);\n background: var(--jl-surface);\n}\n\n:host(:not([has-aside])) .aside {\n display: none;\n}\n\n.scrim {\n display: none;\n}\n\n/* Narrow: the sidebar slides over the content as a drawer, the menu button opens it, the panel steps aside. */\n@container jl-app-shell (width < 960px) {\n .shell {\n grid-template:\n \"top\" auto\n \"main\" minmax(0, 1fr)\n / minmax(0, 1fr);\n }\n\n :host([has-nav]) .top {\n display: flex;\n }\n\n :host([has-nav]) .menu {\n display: inline-flex;\n }\n\n .nav {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n z-index: 2;\n max-width: var(--drawer-width);\n box-shadow: var(--jl-shadow-lg);\n transform: translateX(-100%);\n visibility: hidden;\n transition:\n transform var(--jl-slow) var(--jl-ease),\n visibility 0s linear var(--jl-slow);\n }\n\n :host([nav-open]) .nav {\n transform: none;\n visibility: visible;\n transition: transform var(--jl-slow) var(--jl-ease);\n }\n\n .scrim {\n position: absolute;\n inset: 0;\n z-index: 1;\n display: block;\n background: var(--jl-scrim);\n opacity: 0;\n pointer-events: none;\n transition: opacity var(--jl-slow) var(--jl-ease);\n }\n\n :host([nav-open]) .scrim {\n opacity: 1;\n pointer-events: auto;\n }\n\n .aside {\n display: none;\n }\n}\n";
3
+ export { jl_app_shell_default as default };
@@ -0,0 +1,37 @@
1
+ import { JlElement } from '../base/jl-element';
2
+ /**
3
+ * The frame of an app: a top bar (`header`), a sidebar (`nav`), the main area (the default slot), which scrolls
4
+ * on its own, and an optional panel on the right (`aside`). Narrower than 960 pixels, the sidebar becomes a drawer
5
+ * over the content, opened by a menu button in the top bar (or `nav-open`, or `toggleNav()`), and the panel hides.
6
+ * It takes the height of its container: give that one a height (`100dvh` for a whole page).
7
+ */
8
+ export declare class JlAppShell extends JlElement {
9
+ static readonly selector = "jl-app-shell";
10
+ readonly events: Readonly<import("@jadis/core").UseEventsHandler<{
11
+ navtoggle: boolean;
12
+ }>>;
13
+ /** Whether the drawer is open, on narrow screens; wide ones always show the sidebar. */
14
+ readonly navOpen: Readonly<Readonly<{
15
+ get(): boolean;
16
+ set(setter: boolean | ((prevState: boolean) => boolean)): void;
17
+ }>>;
18
+ private readonly _state;
19
+ private readonly _resize;
20
+ private readonly _refs;
21
+ /** Opens or closes the drawer; `force` sets the state instead of flipping it. */
22
+ toggleNav(force?: boolean): void;
23
+ templateHtml(): Node;
24
+ templateCss(): string;
25
+ onConnect(): void;
26
+ onDisconnect(): void;
27
+ /** Whether the shell is narrow: the menu button shows only then. */
28
+ private isNarrow;
29
+ private onMenu;
30
+ private onKey;
31
+ /** The drawer as people open and close it: says so. */
32
+ private setNavOpen;
33
+ private renderNavOpen;
34
+ private renderInert;
35
+ /** What was slotted: empty areas take no room, and a `select` from the nav (a side nav's) closes the drawer. */
36
+ private renderSlots;
37
+ }
@@ -0,0 +1,150 @@
1
+ import { BASE_STYLES } from "../styles/shared.js";
2
+ import { JlElement } from "../base/jl-element.js";
3
+ import { registerIcons } from "../lib/icons.js";
4
+ import { JlIcon } from "../atoms/jl-icon.js";
5
+ import jl_app_shell_default from "./jl-app-shell.css_raw.js";
6
+ import { css } from "@jadis/core";
7
+ import { Menu } from "lucide";
8
+ import { jsx, jsxs } from "@jadis/core/jsx-runtime";
9
+ // src/templates/jl-app-shell.tsx
10
+ registerIcons({ menu: Menu });
11
+ var MENU_ICON_SIZE = 18;
12
+ var HIDDEN = "none";
13
+ /** Whether a click inside the drawer followed a link: the page changes, so the drawer gets out of the way. */
14
+ var followsLink = (event) => event.composedPath().some((node) => node instanceof HTMLAnchorElement && node.href !== "");
15
+ /**
16
+ * The frame of an app: a top bar (`header`), a sidebar (`nav`), the main area (the default slot), which scrolls
17
+ * on its own, and an optional panel on the right (`aside`). Narrower than 960 pixels, the sidebar becomes a drawer
18
+ * over the content, opened by a menu button in the top bar (or `nav-open`, or `toggleNav()`), and the panel hides.
19
+ * It takes the height of its container: give that one a height (`100dvh` for a whole page).
20
+ */
21
+ var JlAppShell = class extends JlElement {
22
+ static selector = "jl-app-shell";
23
+ events = this.useEvents();
24
+ /** Whether the drawer is open, on narrow screens; wide ones always show the sidebar. */
25
+ navOpen = this.useChange(false, (value) => this.renderNavOpen(value), { attribute: "nav-open" });
26
+ _state = { navListeners: new AbortController() };
27
+ _resize = new ResizeObserver(() => this.renderInert());
28
+ _refs = this.useRefs((ref) => ({
29
+ aside: ref(".aside"),
30
+ asideSlot: ref("slot[name=\"aside\"]"),
31
+ headerSlot: ref("slot[name=\"header\"]"),
32
+ main: ref("main"),
33
+ menu: ref(".menu"),
34
+ nav: ref(".nav"),
35
+ navSlot: ref("slot[name=\"nav\"]"),
36
+ scrim: ref(".scrim")
37
+ }));
38
+ /** Opens or closes the drawer; `force` sets the state instead of flipping it. */
39
+ toggleNav(force) {
40
+ this.setNavOpen(force ?? !this.navOpen.get());
41
+ }
42
+ templateHtml() {
43
+ return /* @__PURE__ */ jsxs("div", {
44
+ class: "shell",
45
+ attrs: { part: "base" },
46
+ children: [
47
+ /* @__PURE__ */ jsxs("header", {
48
+ class: "top",
49
+ attrs: { part: "header" },
50
+ children: [/* @__PURE__ */ jsx("button", {
51
+ class: "menu",
52
+ type: "button",
53
+ "aria-label": "Navigation",
54
+ "aria-expanded": "false",
55
+ "aria-controls": "nav",
56
+ children: /* @__PURE__ */ jsx(JlIcon, {
57
+ name: "menu",
58
+ size: MENU_ICON_SIZE
59
+ })
60
+ }), /* @__PURE__ */ jsx("slot", { name: "header" })]
61
+ }),
62
+ /* @__PURE__ */ jsx("div", {
63
+ class: "nav",
64
+ id: "nav",
65
+ tabIndex: -1,
66
+ attrs: { part: "nav" },
67
+ children: /* @__PURE__ */ jsx("slot", { name: "nav" })
68
+ }),
69
+ /* @__PURE__ */ jsx("main", {
70
+ class: "main",
71
+ attrs: { part: "main" },
72
+ children: /* @__PURE__ */ jsx("slot", {})
73
+ }),
74
+ /* @__PURE__ */ jsx("aside", {
75
+ class: "aside",
76
+ attrs: { part: "aside" },
77
+ children: /* @__PURE__ */ jsx("slot", { name: "aside" })
78
+ }),
79
+ /* @__PURE__ */ jsx("div", { class: "scrim" })
80
+ ]
81
+ });
82
+ }
83
+ templateCss() {
84
+ return css`${BASE_STYLES}${jl_app_shell_default}`;
85
+ }
86
+ onConnect() {
87
+ this._resize.observe(this);
88
+ this.on(this._refs.menu, "click", () => this.onMenu());
89
+ this.on(this._refs.scrim, "click", () => this.setNavOpen(false));
90
+ this.on(this._refs.nav, "click", (event) => {
91
+ if (followsLink(event)) this.setNavOpen(false);
92
+ });
93
+ this.on(this, "keydown", (event) => this.onKey(event));
94
+ this.renderSlots();
95
+ for (const slot of [
96
+ this._refs.headerSlot,
97
+ this._refs.navSlot,
98
+ this._refs.asideSlot
99
+ ]) this.on(slot, "slotchange", () => this.renderSlots());
100
+ }
101
+ onDisconnect() {
102
+ this._resize.disconnect();
103
+ this._state.navListeners.abort();
104
+ }
105
+ /** Whether the shell is narrow: the menu button shows only then. */
106
+ isNarrow() {
107
+ return getComputedStyle(this._refs.menu).display !== HIDDEN;
108
+ }
109
+ onMenu() {
110
+ const opening = !this.navOpen.get();
111
+ this.setNavOpen(opening);
112
+ if (opening) this._refs.nav.focus();
113
+ }
114
+ onKey(event) {
115
+ if (event.key !== "Escape" || !this.navOpen.get() || !this.isNarrow()) return;
116
+ event.preventDefault();
117
+ this.setNavOpen(false);
118
+ this._refs.menu.focus();
119
+ }
120
+ /** The drawer as people open and close it: says so. */
121
+ setNavOpen(open) {
122
+ if (open === this.navOpen.get()) return;
123
+ this.navOpen.set(open);
124
+ this.events.emit("navtoggle", open);
125
+ }
126
+ renderNavOpen(open) {
127
+ this.reflect("nav-open", open);
128
+ this._refs.menu.setAttribute("aria-expanded", String(open));
129
+ this.renderInert();
130
+ }
131
+ renderInert() {
132
+ const covered = this.navOpen.get() && this.isNarrow();
133
+ this._refs.main.inert = covered;
134
+ this._refs.aside.inert = covered;
135
+ }
136
+ /** What was slotted: empty areas take no room, and a `select` from the nav (a side nav's) closes the drawer. */
137
+ renderSlots() {
138
+ this.reflect("has-header", this._refs.headerSlot.assignedElements().length > 0);
139
+ this.reflect("has-aside", this._refs.asideSlot.assignedElements().length > 0);
140
+ const navElements = this._refs.navSlot.assignedElements();
141
+ this.reflect("has-nav", navElements.length > 0);
142
+ this._state.navListeners.abort();
143
+ this._state.navListeners = new AbortController();
144
+ const signal = this._state.navListeners.signal;
145
+ for (const element of navElements) element.addEventListener("select", () => this.setNavOpen(false), { signal });
146
+ this.renderInert();
147
+ }
148
+ };
149
+ JlAppShell.register();
150
+ export { JlAppShell };
@@ -0,0 +1,3 @@
1
+ // src/templates/jl-split-view.css?raw
2
+ var jl_split_view_default = ":host {\n display: block;\n height: 100%;\n min-height: 0;\n}\n\n.split {\n display: grid;\n grid-template-columns: minmax(0, var(--position, 50%)) 1px minmax(0, 1fr);\n height: 100%;\n}\n\n:host([orientation=\"vertical\"]) .split {\n grid-template-columns: none;\n grid-template-rows: minmax(0, var(--position, 50%)) 1px minmax(0, 1fr);\n}\n\n.pane {\n min-width: 0;\n min-height: 0;\n overflow: auto;\n}\n\n.divider {\n position: relative;\n z-index: 1;\n background: var(--jl-line);\n cursor: col-resize;\n touch-action: none;\n transition: background var(--jl-fast) var(--jl-ease);\n}\n\n:host([orientation=\"vertical\"]) .divider {\n cursor: row-resize;\n}\n\n/* The line is one pixel; the pointer gets a wider band to catch. */\n.divider::before {\n content: \"\";\n position: absolute;\n inset: 0 -5px;\n}\n\n:host([orientation=\"vertical\"]) .divider::before {\n inset: -5px 0;\n}\n\n.divider:hover,\n.divider:focus-visible,\n:host([dragging]) .divider {\n background: var(--jl-accent);\n}\n\n.divider:focus-visible {\n box-shadow: var(--jl-focus-ring);\n}\n\n.grip {\n position: absolute;\n top: 50%;\n left: 50%;\n width: 6px;\n height: 28px;\n border: 1px solid var(--jl-line-strong);\n border-radius: var(--jl-radius-pill);\n background: var(--jl-surface);\n transform: translate(-50%, -50%);\n transition: border-color var(--jl-fast) var(--jl-ease);\n}\n\n:host([orientation=\"vertical\"]) .grip {\n width: 28px;\n height: 6px;\n}\n\n.divider:hover .grip,\n.divider:focus-visible .grip,\n:host([dragging]) .grip {\n border-color: var(--jl-accent);\n}\n\n:host([dragging]) {\n cursor: col-resize;\n user-select: none;\n}\n\n:host([dragging][orientation=\"vertical\"]) {\n cursor: row-resize;\n}\n\n/* While dragging, the panes ignore the pointer: a frame inside one would otherwise swallow it. */\n:host([dragging]) .pane {\n pointer-events: none;\n}\n";
3
+ export { jl_split_view_default as default };
@@ -0,0 +1,49 @@
1
+ import { JlElement } from '../base/jl-element';
2
+ import type { SplitOrientation } from '../types/jl-split-view.types';
3
+ export declare const SPLIT_ORIENTATIONS: readonly SplitOrientation[];
4
+ /**
5
+ * Two panes, `start` and `end`, side by side or one above the other, with a divider to drag between them. The
6
+ * divider also moves with the arrows (Shift for large steps), Home and End; a double click puts it back. `position`
7
+ * is the start pane's share, in percent; each pane keeps at least `min` percent.
8
+ */
9
+ export declare class JlSplitView extends JlElement {
10
+ static readonly selector = "jl-split-view";
11
+ /** The new position, in percent, once a drag ends or a key moved the divider. */
12
+ readonly events: Readonly<import("@jadis/core").UseEventsHandler<{
13
+ change: number;
14
+ }>>;
15
+ readonly orientation: Readonly<Readonly<{
16
+ get(): SplitOrientation;
17
+ set(setter: ((prevState: SplitOrientation) => SplitOrientation) | SplitOrientation): void;
18
+ }>>;
19
+ /** The start pane's share of the room, in percent. */
20
+ readonly position: Readonly<Readonly<{
21
+ get(): number;
22
+ set(setter: number | ((prevState: number) => number)): void;
23
+ }>>;
24
+ /** The smallest share each pane keeps, in percent (0 to 50). */
25
+ readonly min: Readonly<Readonly<{
26
+ get(): number;
27
+ set(setter: number | ((prevState: number) => number)): void;
28
+ }>>;
29
+ /** The divider's name for screen readers. */
30
+ readonly label: Readonly<Readonly<{
31
+ get(): string;
32
+ set(setter: string | ((prevState: string) => string)): void;
33
+ }>>;
34
+ private readonly _drag;
35
+ private readonly _refs;
36
+ /** Puts the divider back where the page first placed it. */
37
+ reset(): void;
38
+ templateHtml(): Node;
39
+ templateCss(): string;
40
+ onConnect(): void;
41
+ private current;
42
+ private startDrag;
43
+ private drag;
44
+ private endDrag;
45
+ private onKey;
46
+ /** Moves the divider as people asked, and says so when it moved. */
47
+ private commit;
48
+ private renderPosition;
49
+ }