@flanksource/clicky-ui 0.2.20 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (233) hide show
  1. package/dist/components/Combobox.cjs +5 -2
  2. package/dist/components/Combobox.cjs.map +1 -1
  3. package/dist/components/Combobox.d.ts.map +1 -1
  4. package/dist/components/Combobox.js +5 -2
  5. package/dist/components/Combobox.js.map +1 -1
  6. package/dist/components/FilterBar.cjs +280 -253
  7. package/dist/components/FilterBar.cjs.map +1 -1
  8. package/dist/components/FilterBar.d.ts.map +1 -1
  9. package/dist/components/FilterBar.js +280 -253
  10. package/dist/components/FilterBar.js.map +1 -1
  11. package/dist/components/JsonSchemaForm.cjs +3 -1
  12. package/dist/components/JsonSchemaForm.cjs.map +1 -1
  13. package/dist/components/JsonSchemaForm.d.ts.map +1 -1
  14. package/dist/components/JsonSchemaForm.js +4 -2
  15. package/dist/components/JsonSchemaForm.js.map +1 -1
  16. package/dist/components/MultiSelect.cjs +29 -32
  17. package/dist/components/MultiSelect.cjs.map +1 -1
  18. package/dist/components/MultiSelect.d.ts.map +1 -1
  19. package/dist/components/MultiSelect.js +30 -33
  20. package/dist/components/MultiSelect.js.map +1 -1
  21. package/dist/components/TimeRange.cjs +29 -37
  22. package/dist/components/TimeRange.cjs.map +1 -1
  23. package/dist/components/TimeRange.d.ts.map +1 -1
  24. package/dist/components/TimeRange.js +30 -38
  25. package/dist/components/TimeRange.js.map +1 -1
  26. package/dist/components/TreePickerField.cjs +6 -3
  27. package/dist/components/TreePickerField.cjs.map +1 -1
  28. package/dist/components/TreePickerField.d.ts.map +1 -1
  29. package/dist/components/TreePickerField.js +6 -3
  30. package/dist/components/TreePickerField.js.map +1 -1
  31. package/dist/components/icon-menu-picker.cjs +9 -3
  32. package/dist/components/icon-menu-picker.cjs.map +1 -1
  33. package/dist/components/icon-menu-picker.d.ts.map +1 -1
  34. package/dist/components/icon-menu-picker.js +9 -3
  35. package/dist/components/icon-menu-picker.js.map +1 -1
  36. package/dist/components/json-schema-form-fields.cjs +80 -2
  37. package/dist/components/json-schema-form-fields.cjs.map +1 -1
  38. package/dist/components/json-schema-form-fields.d.ts +15 -0
  39. package/dist/components/json-schema-form-fields.d.ts.map +1 -1
  40. package/dist/components/json-schema-form-fields.js +81 -3
  41. package/dist/components/json-schema-form-fields.js.map +1 -1
  42. package/dist/components/json-schema-form-refs.cjs +60 -0
  43. package/dist/components/json-schema-form-refs.cjs.map +1 -0
  44. package/dist/components/json-schema-form-refs.d.ts +3 -0
  45. package/dist/components/json-schema-form-refs.d.ts.map +1 -0
  46. package/dist/components/json-schema-form-refs.js +60 -0
  47. package/dist/components/json-schema-form-refs.js.map +1 -0
  48. package/dist/components/json-schema-form-render.cjs +14 -2
  49. package/dist/components/json-schema-form-render.cjs.map +1 -1
  50. package/dist/components/json-schema-form-render.d.ts.map +1 -1
  51. package/dist/components/json-schema-form-render.js +15 -3
  52. package/dist/components/json-schema-form-render.js.map +1 -1
  53. package/dist/components/json-schema-form-resolve.cjs +5 -0
  54. package/dist/components/json-schema-form-resolve.cjs.map +1 -1
  55. package/dist/components/json-schema-form-resolve.d.ts.map +1 -1
  56. package/dist/components/json-schema-form-resolve.js +5 -0
  57. package/dist/components/json-schema-form-resolve.js.map +1 -1
  58. package/dist/components/json-schema-form-template-menu.cjs +83 -0
  59. package/dist/components/json-schema-form-template-menu.cjs.map +1 -0
  60. package/dist/components/json-schema-form-template-menu.d.ts +15 -0
  61. package/dist/components/json-schema-form-template-menu.d.ts.map +1 -0
  62. package/dist/components/json-schema-form-template-menu.js +83 -0
  63. package/dist/components/json-schema-form-template-menu.js.map +1 -0
  64. package/dist/components/json-schema-form-template.cjs +18 -0
  65. package/dist/components/json-schema-form-template.cjs.map +1 -0
  66. package/dist/components/json-schema-form-template.d.ts +13 -0
  67. package/dist/components/json-schema-form-template.d.ts.map +1 -0
  68. package/dist/components/json-schema-form-template.js +18 -0
  69. package/dist/components/json-schema-form-template.js.map +1 -0
  70. package/dist/components/json-schema-form-types.d.ts +5 -1
  71. package/dist/components/json-schema-form-types.d.ts.map +1 -1
  72. package/dist/components.cjs +4 -0
  73. package/dist/components.cjs.map +1 -1
  74. package/dist/components.d.ts +3 -0
  75. package/dist/components.d.ts.map +1 -1
  76. package/dist/components.js +4 -0
  77. package/dist/components.js.map +1 -1
  78. package/dist/data/Clicky.cjs +6 -9
  79. package/dist/data/Clicky.cjs.map +1 -1
  80. package/dist/data/Clicky.d.ts.map +1 -1
  81. package/dist/data/Clicky.js +6 -9
  82. package/dist/data/Clicky.js.map +1 -1
  83. package/dist/data/DataTable.cjs +23 -17
  84. package/dist/data/DataTable.cjs.map +1 -1
  85. package/dist/data/DataTable.d.ts +7 -1
  86. package/dist/data/DataTable.d.ts.map +1 -1
  87. package/dist/data/DataTable.js +24 -18
  88. package/dist/data/DataTable.js.map +1 -1
  89. package/dist/data/Gauge.cjs +37 -4
  90. package/dist/data/Gauge.cjs.map +1 -1
  91. package/dist/data/Gauge.d.ts +6 -1
  92. package/dist/data/Gauge.d.ts.map +1 -1
  93. package/dist/data/Gauge.js +37 -4
  94. package/dist/data/Gauge.js.map +1 -1
  95. package/dist/data/TimeseriesCoreBars.cjs +127 -29
  96. package/dist/data/TimeseriesCoreBars.cjs.map +1 -1
  97. package/dist/data/TimeseriesCoreBars.d.ts +11 -1
  98. package/dist/data/TimeseriesCoreBars.d.ts.map +1 -1
  99. package/dist/data/TimeseriesCoreBars.js +128 -30
  100. package/dist/data/TimeseriesCoreBars.js.map +1 -1
  101. package/dist/data/TimeseriesGauge.cjs +105 -47
  102. package/dist/data/TimeseriesGauge.cjs.map +1 -1
  103. package/dist/data/TimeseriesGauge.d.ts +6 -1
  104. package/dist/data/TimeseriesGauge.d.ts.map +1 -1
  105. package/dist/data/TimeseriesGauge.js +105 -47
  106. package/dist/data/TimeseriesGauge.js.map +1 -1
  107. package/dist/data/WorkloadCard.cjs +371 -0
  108. package/dist/data/WorkloadCard.cjs.map +1 -0
  109. package/dist/data/WorkloadCard.d.ts +24 -0
  110. package/dist/data/WorkloadCard.d.ts.map +1 -0
  111. package/dist/data/WorkloadCard.js +371 -0
  112. package/dist/data/WorkloadCard.js.map +1 -0
  113. package/dist/data/WorkloadCard.model.cjs +51 -0
  114. package/dist/data/WorkloadCard.model.cjs.map +1 -0
  115. package/dist/data/WorkloadCard.model.d.ts +53 -0
  116. package/dist/data/WorkloadCard.model.d.ts.map +1 -0
  117. package/dist/data/WorkloadCard.model.js +51 -0
  118. package/dist/data/WorkloadCard.model.js.map +1 -0
  119. package/dist/data/ai/ChatFab.cjs +3 -1
  120. package/dist/data/ai/ChatFab.cjs.map +1 -1
  121. package/dist/data/ai/ChatFab.d.ts.map +1 -1
  122. package/dist/data/ai/ChatFab.js +3 -1
  123. package/dist/data/ai/ChatFab.js.map +1 -1
  124. package/dist/data/ai/ChatWindow.cjs +25 -7
  125. package/dist/data/ai/ChatWindow.cjs.map +1 -1
  126. package/dist/data/ai/ChatWindow.d.ts +11 -3
  127. package/dist/data/ai/ChatWindow.d.ts.map +1 -1
  128. package/dist/data/ai/ChatWindow.js +26 -8
  129. package/dist/data/ai/ChatWindow.js.map +1 -1
  130. package/dist/data/ai/chat-window-context.cjs +2 -1
  131. package/dist/data/ai/chat-window-context.cjs.map +1 -1
  132. package/dist/data/ai/chat-window-context.d.ts +1 -1
  133. package/dist/data/ai/chat-window-context.d.ts.map +1 -1
  134. package/dist/data/ai/chat-window-context.js +2 -1
  135. package/dist/data/ai/chat-window-context.js.map +1 -1
  136. package/dist/data/ai/context.cjs.map +1 -1
  137. package/dist/data/ai/context.d.ts +3 -0
  138. package/dist/data/ai/context.d.ts.map +1 -1
  139. package/dist/data/ai/context.js.map +1 -1
  140. package/dist/data/chat/Chat.cjs +13 -2
  141. package/dist/data/chat/Chat.cjs.map +1 -1
  142. package/dist/data/chat/Chat.d.ts +4 -2
  143. package/dist/data/chat/Chat.d.ts.map +1 -1
  144. package/dist/data/chat/Chat.js +14 -3
  145. package/dist/data/chat/Chat.js.map +1 -1
  146. package/dist/data/chat/Conversation.cjs +47 -2
  147. package/dist/data/chat/Conversation.cjs.map +1 -1
  148. package/dist/data/chat/Conversation.d.ts +8 -2
  149. package/dist/data/chat/Conversation.d.ts.map +1 -1
  150. package/dist/data/chat/Conversation.js +47 -2
  151. package/dist/data/chat/Conversation.js.map +1 -1
  152. package/dist/data/chat/Message.cjs +13 -4
  153. package/dist/data/chat/Message.cjs.map +1 -1
  154. package/dist/data/chat/Message.d.ts +4 -2
  155. package/dist/data/chat/Message.d.ts.map +1 -1
  156. package/dist/data/chat/Message.js +13 -4
  157. package/dist/data/chat/Message.js.map +1 -1
  158. package/dist/data/chat/ToolCall.cjs +9 -4
  159. package/dist/data/chat/ToolCall.cjs.map +1 -1
  160. package/dist/data/chat/ToolCall.d.ts +3 -2
  161. package/dist/data/chat/ToolCall.d.ts.map +1 -1
  162. package/dist/data/chat/ToolCall.js +9 -4
  163. package/dist/data/chat/ToolCall.js.map +1 -1
  164. package/dist/data/chat/index.d.ts +1 -1
  165. package/dist/data/chat/index.d.ts.map +1 -1
  166. package/dist/data/chat/types.cjs.map +1 -1
  167. package/dist/data/chat/types.d.ts +7 -0
  168. package/dist/data/chat/types.d.ts.map +1 -1
  169. package/dist/data/chat/types.js.map +1 -1
  170. package/dist/data/version-info.cjs +3 -3
  171. package/dist/data/version-info.js +3 -3
  172. package/dist/data.cjs +7 -0
  173. package/dist/data.cjs.map +1 -1
  174. package/dist/data.d.ts +5 -3
  175. package/dist/data.d.ts.map +1 -1
  176. package/dist/data.js +8 -1
  177. package/dist/data.js.map +1 -1
  178. package/dist/hooks/use-sort.cjs +19 -10
  179. package/dist/hooks/use-sort.cjs.map +1 -1
  180. package/dist/hooks/use-sort.d.ts +3 -0
  181. package/dist/hooks/use-sort.d.ts.map +1 -1
  182. package/dist/hooks/use-sort.js +19 -10
  183. package/dist/hooks/use-sort.js.map +1 -1
  184. package/dist/index.cjs +11 -0
  185. package/dist/index.cjs.map +1 -1
  186. package/dist/index.js +11 -0
  187. package/dist/index.js.map +1 -1
  188. package/dist/layout/AppShell.cjs +6 -10
  189. package/dist/layout/AppShell.cjs.map +1 -1
  190. package/dist/layout/AppShell.d.ts +2 -0
  191. package/dist/layout/AppShell.d.ts.map +1 -1
  192. package/dist/layout/AppShell.js +6 -10
  193. package/dist/layout/AppShell.js.map +1 -1
  194. package/dist/overlay/DropdownMenu.cjs +50 -38
  195. package/dist/overlay/DropdownMenu.cjs.map +1 -1
  196. package/dist/overlay/DropdownMenu.d.ts +5 -1
  197. package/dist/overlay/DropdownMenu.d.ts.map +1 -1
  198. package/dist/overlay/DropdownMenu.js +50 -38
  199. package/dist/overlay/DropdownMenu.js.map +1 -1
  200. package/dist/overlay/HoverCard.cjs +7 -2
  201. package/dist/overlay/HoverCard.cjs.map +1 -1
  202. package/dist/overlay/HoverCard.d.ts.map +1 -1
  203. package/dist/overlay/HoverCard.js +7 -2
  204. package/dist/overlay/HoverCard.js.map +1 -1
  205. package/dist/overlay/Modal.cjs +20 -17
  206. package/dist/overlay/Modal.cjs.map +1 -1
  207. package/dist/overlay/Modal.d.ts.map +1 -1
  208. package/dist/overlay/Modal.js +21 -18
  209. package/dist/overlay/Modal.js.map +1 -1
  210. package/dist/overlay/Toast.cjs +9 -1
  211. package/dist/overlay/Toast.cjs.map +1 -1
  212. package/dist/overlay/Toast.d.ts.map +1 -1
  213. package/dist/overlay/Toast.js +9 -1
  214. package/dist/overlay/Toast.js.map +1 -1
  215. package/dist/overlay/modalStack.cjs +53 -1
  216. package/dist/overlay/modalStack.cjs.map +1 -1
  217. package/dist/overlay/modalStack.d.ts +12 -0
  218. package/dist/overlay/modalStack.d.ts.map +1 -1
  219. package/dist/overlay/modalStack.js +54 -2
  220. package/dist/overlay/modalStack.js.map +1 -1
  221. package/dist/overlay/zIndex.cjs +20 -0
  222. package/dist/overlay/zIndex.cjs.map +1 -0
  223. package/dist/overlay/zIndex.d.ts +39 -0
  224. package/dist/overlay/zIndex.d.ts.map +1 -0
  225. package/dist/overlay/zIndex.js +20 -0
  226. package/dist/overlay/zIndex.js.map +1 -0
  227. package/dist/rpc/AcceptPicker.cjs +28 -26
  228. package/dist/rpc/AcceptPicker.cjs.map +1 -1
  229. package/dist/rpc/AcceptPicker.d.ts.map +1 -1
  230. package/dist/rpc/AcceptPicker.js +29 -27
  231. package/dist/rpc/AcceptPicker.js.map +1 -1
  232. package/dist/styles.css +1 -1
  233. package/package.json +1 -1
package/dist/data.js CHANGED
@@ -36,6 +36,8 @@ import { mergeSeries, resolveBreakdown, resolveSeries } from "./data/TimeseriesP
36
36
  import { assignAxes, latestOf } from "./data/TimeseriesPanel.axes.js";
37
37
  import { TimeseriesGauge } from "./data/TimeseriesGauge.js";
38
38
  import { TimeseriesCoreBars } from "./data/TimeseriesCoreBars.js";
39
+ import { WorkloadCard } from "./data/WorkloadCard.js";
40
+ import { formatReplicaCounts, workloadKindLabel, workloadStatusLabel, workloadStatusTone } from "./data/WorkloadCard.model.js";
39
41
  import { deriveCoreBars } from "./data/TimeseriesCoreBars.model.js";
40
42
  import { StackedStatusBar, StatusRows } from "./data/StatusBreakdown.js";
41
43
  import { segment } from "./data/status-breakdown-utils.js";
@@ -903,6 +905,7 @@ export {
903
905
  UiZoomOut,
904
906
  UiZoomOutFilled,
905
907
  Version,
908
+ WorkloadCard,
906
909
  allGroupsTerminal,
907
910
  annotateRoutePaths,
908
911
  assignAxes,
@@ -934,6 +937,7 @@ export {
934
937
  formatDateTimeRelative,
935
938
  formatPropertyLabel,
936
939
  formatRelativeTime,
940
+ formatReplicaCounts,
937
941
  formatTestDuration,
938
942
  formatTimestamp,
939
943
  frameHasSource,
@@ -987,6 +991,9 @@ export {
987
991
  totalDuration,
988
992
  useTagActions,
989
993
  useTagActionsValue,
990
- useTestRunner
994
+ useTestRunner,
995
+ workloadKindLabel,
996
+ workloadStatusLabel,
997
+ workloadStatusTone
991
998
  };
992
999
  //# sourceMappingURL=data.js.map
package/dist/data.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"data.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"data.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -8,21 +8,30 @@ function resolvePath(obj, path) {
8
8
  }, obj);
9
9
  }
10
10
  function useSort(items, options = {}) {
11
- const { defaultKey, defaultDir = "asc", resolvers } = options;
12
- const [sort, setSort] = react.useState(
11
+ const {
12
+ defaultKey,
13
+ defaultDir = "asc",
14
+ sort: controlledSort,
15
+ onSortChange,
16
+ manual = false,
17
+ resolvers
18
+ } = options;
19
+ const [localSort, setLocalSort] = react.useState(
13
20
  defaultKey ? { key: defaultKey, dir: defaultDir } : null
14
21
  );
22
+ const isControlled = controlledSort !== void 0;
23
+ const sort = isControlled ? controlledSort : localSort;
15
24
  function toggle(key) {
16
- setSort((prev) => {
17
- if ((prev == null ? void 0 : prev.key) === key) {
18
- return prev.dir === "asc" ? { key, dir: "desc" } : null;
19
- }
20
- return { key, dir: "asc" };
21
- });
25
+ const next = (sort == null ? void 0 : sort.key) === key ? sort.dir === "asc" ? { key, dir: "desc" } : null : { key, dir: "asc" };
26
+ if (isControlled) {
27
+ onSortChange == null ? void 0 : onSortChange(next);
28
+ } else {
29
+ setLocalSort(next);
30
+ }
22
31
  }
23
32
  const sorted = react.useMemo(() => {
24
33
  if (!items) return [];
25
- if (!sort) return items;
34
+ if (manual || !sort) return items;
26
35
  const { key, dir } = sort;
27
36
  const resolver = resolvers == null ? void 0 : resolvers[key];
28
37
  const get = (item) => resolver ? resolver(item) : resolvePath(item, key);
@@ -35,7 +44,7 @@ function useSort(items, options = {}) {
35
44
  const cmp = typeof av === "number" && typeof bv === "number" ? av - bv : String(av).localeCompare(String(bv));
36
45
  return dir === "asc" ? cmp : -cmp;
37
46
  });
38
- }, [items, sort, resolvers]);
47
+ }, [items, manual, sort, resolvers]);
39
48
  return { sorted, sort, toggle };
40
49
  }
41
50
  exports.useSort = useSort;
@@ -1 +1 @@
1
- {"version":3,"file":"use-sort.cjs","sources":["../../src/hooks/use-sort.ts"],"sourcesContent":["import { useMemo, useState } from \"react\";\n\nexport type SortDir = \"asc\" | \"desc\";\n\nexport type SortState = {\n key: string;\n dir: SortDir;\n};\n\nexport type UseSortOptions<T> = {\n defaultKey?: string;\n defaultDir?: SortDir;\n resolvers?: Record<string, (item: T) => unknown>;\n};\n\nexport type UseSortReturn<T> = {\n sorted: T[];\n sort: SortState | null;\n toggle: (key: string) => void;\n};\n\nfunction resolvePath(obj: unknown, path: string): unknown {\n return path.split(\".\").reduce<unknown>((o, k) => {\n if (o && typeof o === \"object\") return (o as Record<string, unknown>)[k];\n return undefined;\n }, obj);\n}\n\nexport function useSort<T>(items: T[], options: UseSortOptions<T> = {}): UseSortReturn<T> {\n const { defaultKey, defaultDir = \"asc\", resolvers } = options;\n\n const [sort, setSort] = useState<SortState | null>(\n defaultKey ? { key: defaultKey, dir: defaultDir } : null,\n );\n\n function toggle(key: string) {\n setSort((prev) => {\n if (prev?.key === key) {\n return prev.dir === \"asc\" ? { key, dir: \"desc\" } : null;\n }\n return { key, dir: \"asc\" };\n });\n }\n\n const sorted = useMemo(() => {\n if (!items) return [];\n if (!sort) return items;\n const { key, dir } = sort;\n const resolver = resolvers?.[key];\n const get = (item: T) => (resolver ? resolver(item) : resolvePath(item, key));\n return [...items].sort((a, b) => {\n const av = get(a);\n const bv = get(b);\n if (av == null && bv == null) return 0;\n if (av == null) return 1;\n if (bv == null) return -1;\n const cmp =\n typeof av === \"number\" && typeof bv === \"number\"\n ? av - bv\n : String(av).localeCompare(String(bv));\n return dir === \"asc\" ? cmp : -cmp;\n });\n }, [items, sort, resolvers]);\n\n return { sorted, sort, toggle };\n}\n"],"names":["useState","useMemo"],"mappings":";;;AAqBA,SAAS,YAAY,KAAc,MAAuB;AACxD,SAAO,KAAK,MAAM,GAAG,EAAE,OAAgB,CAAC,GAAG,MAAM;AAC/C,QAAI,KAAK,OAAO,MAAM,SAAU,QAAQ,EAA8B,CAAC;AACvE,WAAO;AAAA,EACT,GAAG,GAAG;AACR;AAEO,SAAS,QAAW,OAAY,UAA6B,IAAsB;AACxF,QAAM,EAAE,YAAY,aAAa,OAAO,cAAc;AAEtD,QAAM,CAAC,MAAM,OAAO,IAAIA,MAAAA;AAAAA,IACtB,aAAa,EAAE,KAAK,YAAY,KAAK,eAAe;AAAA,EAAA;AAGtD,WAAS,OAAO,KAAa;AAC3B,YAAQ,CAAC,SAAS;AAChB,WAAI,6BAAM,SAAQ,KAAK;AACrB,eAAO,KAAK,QAAQ,QAAQ,EAAE,KAAK,KAAK,WAAW;AAAA,MACrD;AACA,aAAO,EAAE,KAAK,KAAK,MAAA;AAAA,IACrB,CAAC;AAAA,EACH;AAEA,QAAM,SAASC,MAAAA,QAAQ,MAAM;AAC3B,QAAI,CAAC,MAAO,QAAO,CAAA;AACnB,QAAI,CAAC,KAAM,QAAO;AAClB,UAAM,EAAE,KAAK,IAAA,IAAQ;AACrB,UAAM,WAAW,uCAAY;AAC7B,UAAM,MAAM,CAAC,SAAa,WAAW,SAAS,IAAI,IAAI,YAAY,MAAM,GAAG;AAC3E,WAAO,CAAC,GAAG,KAAK,EAAE,KAAK,CAAC,GAAG,MAAM;AAC/B,YAAM,KAAK,IAAI,CAAC;AAChB,YAAM,KAAK,IAAI,CAAC;AAChB,UAAI,MAAM,QAAQ,MAAM,KAAM,QAAO;AACrC,UAAI,MAAM,KAAM,QAAO;AACvB,UAAI,MAAM,KAAM,QAAO;AACvB,YAAM,MACJ,OAAO,OAAO,YAAY,OAAO,OAAO,WACpC,KAAK,KACL,OAAO,EAAE,EAAE,cAAc,OAAO,EAAE,CAAC;AACzC,aAAO,QAAQ,QAAQ,MAAM,CAAC;AAAA,IAChC,CAAC;AAAA,EACH,GAAG,CAAC,OAAO,MAAM,SAAS,CAAC;AAE3B,SAAO,EAAE,QAAQ,MAAM,OAAA;AACzB;;"}
1
+ {"version":3,"file":"use-sort.cjs","sources":["../../src/hooks/use-sort.ts"],"sourcesContent":["import { useMemo, useState } from \"react\";\n\nexport type SortDir = \"asc\" | \"desc\";\n\nexport type SortState = {\n key: string;\n dir: SortDir;\n};\n\nexport type UseSortOptions<T> = {\n defaultKey?: string;\n defaultDir?: SortDir;\n sort?: SortState | null;\n onSortChange?: (sort: SortState | null) => void;\n manual?: boolean;\n resolvers?: Record<string, (item: T) => unknown>;\n};\n\nexport type UseSortReturn<T> = {\n sorted: T[];\n sort: SortState | null;\n toggle: (key: string) => void;\n};\n\nfunction resolvePath(obj: unknown, path: string): unknown {\n return path.split(\".\").reduce<unknown>((o, k) => {\n if (o && typeof o === \"object\") return (o as Record<string, unknown>)[k];\n return undefined;\n }, obj);\n}\n\nexport function useSort<T>(\n items: T[],\n options: UseSortOptions<T> = {},\n): UseSortReturn<T> {\n const {\n defaultKey,\n defaultDir = \"asc\",\n sort: controlledSort,\n onSortChange,\n manual = false,\n resolvers,\n } = options;\n\n const [localSort, setLocalSort] = useState<SortState | null>(\n defaultKey ? { key: defaultKey, dir: defaultDir } : null,\n );\n const isControlled = controlledSort !== undefined;\n const sort = isControlled ? controlledSort : localSort;\n\n function toggle(key: string) {\n const next =\n sort?.key === key\n ? sort.dir === \"asc\"\n ? { key, dir: \"desc\" as const }\n : null\n : { key, dir: \"asc\" as const };\n if (isControlled) {\n onSortChange?.(next);\n } else {\n setLocalSort(next);\n }\n }\n\n const sorted = useMemo(() => {\n if (!items) return [];\n if (manual || !sort) return items;\n const { key, dir } = sort;\n const resolver = resolvers?.[key];\n const get = (item: T) =>\n resolver ? resolver(item) : resolvePath(item, key);\n return [...items].sort((a, b) => {\n const av = get(a);\n const bv = get(b);\n if (av == null && bv == null) return 0;\n if (av == null) return 1;\n if (bv == null) return -1;\n const cmp =\n typeof av === \"number\" && typeof bv === \"number\"\n ? av - bv\n : String(av).localeCompare(String(bv));\n return dir === \"asc\" ? cmp : -cmp;\n });\n }, [items, manual, sort, resolvers]);\n\n return { sorted, sort, toggle };\n}\n"],"names":["useState","useMemo"],"mappings":";;;AAwBA,SAAS,YAAY,KAAc,MAAuB;AACxD,SAAO,KAAK,MAAM,GAAG,EAAE,OAAgB,CAAC,GAAG,MAAM;AAC/C,QAAI,KAAK,OAAO,MAAM,SAAU,QAAQ,EAA8B,CAAC;AACvE,WAAO;AAAA,EACT,GAAG,GAAG;AACR;AAEO,SAAS,QACd,OACA,UAA6B,IACX;AAClB,QAAM;AAAA,IACJ;AAAA,IACA,aAAa;AAAA,IACb,MAAM;AAAA,IACN;AAAA,IACA,SAAS;AAAA,IACT;AAAA,EAAA,IACE;AAEJ,QAAM,CAAC,WAAW,YAAY,IAAIA,MAAAA;AAAAA,IAChC,aAAa,EAAE,KAAK,YAAY,KAAK,eAAe;AAAA,EAAA;AAEtD,QAAM,eAAe,mBAAmB;AACxC,QAAM,OAAO,eAAe,iBAAiB;AAE7C,WAAS,OAAO,KAAa;AAC3B,UAAM,QACJ,6BAAM,SAAQ,MACV,KAAK,QAAQ,QACX,EAAE,KAAK,KAAK,WACZ,OACF,EAAE,KAAK,KAAK,MAAA;AAClB,QAAI,cAAc;AAChB,mDAAe;AAAA,IACjB,OAAO;AACL,mBAAa,IAAI;AAAA,IACnB;AAAA,EACF;AAEA,QAAM,SAASC,MAAAA,QAAQ,MAAM;AAC3B,QAAI,CAAC,MAAO,QAAO,CAAA;AACnB,QAAI,UAAU,CAAC,KAAM,QAAO;AAC5B,UAAM,EAAE,KAAK,IAAA,IAAQ;AACrB,UAAM,WAAW,uCAAY;AAC7B,UAAM,MAAM,CAAC,SACX,WAAW,SAAS,IAAI,IAAI,YAAY,MAAM,GAAG;AACnD,WAAO,CAAC,GAAG,KAAK,EAAE,KAAK,CAAC,GAAG,MAAM;AAC/B,YAAM,KAAK,IAAI,CAAC;AAChB,YAAM,KAAK,IAAI,CAAC;AAChB,UAAI,MAAM,QAAQ,MAAM,KAAM,QAAO;AACrC,UAAI,MAAM,KAAM,QAAO;AACvB,UAAI,MAAM,KAAM,QAAO;AACvB,YAAM,MACJ,OAAO,OAAO,YAAY,OAAO,OAAO,WACpC,KAAK,KACL,OAAO,EAAE,EAAE,cAAc,OAAO,EAAE,CAAC;AACzC,aAAO,QAAQ,QAAQ,MAAM,CAAC;AAAA,IAChC,CAAC;AAAA,EACH,GAAG,CAAC,OAAO,QAAQ,MAAM,SAAS,CAAC;AAEnC,SAAO,EAAE,QAAQ,MAAM,OAAA;AACzB;;"}
@@ -6,6 +6,9 @@ export type SortState = {
6
6
  export type UseSortOptions<T> = {
7
7
  defaultKey?: string;
8
8
  defaultDir?: SortDir;
9
+ sort?: SortState | null;
10
+ onSortChange?: (sort: SortState | null) => void;
11
+ manual?: boolean;
9
12
  resolvers?: Record<string, (item: T) => unknown>;
10
13
  };
11
14
  export type UseSortReturn<T> = {
@@ -1 +1 @@
1
- {"version":3,"file":"use-sort.d.ts","sourceRoot":"","sources":["../../src/hooks/use-sort.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,OAAO,GAAG,KAAK,GAAG,MAAM,CAAC;AAErC,MAAM,MAAM,SAAS,GAAG;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,OAAO,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC;CAClD,CAAC;AAEF,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI;IAC7B,MAAM,EAAE,CAAC,EAAE,CAAC;IACZ,IAAI,EAAE,SAAS,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/B,CAAC;AASF,wBAAgB,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,OAAO,GAAE,cAAc,CAAC,CAAC,CAAM,GAAG,aAAa,CAAC,CAAC,CAAC,CAqCxF"}
1
+ {"version":3,"file":"use-sort.d.ts","sourceRoot":"","sources":["../../src/hooks/use-sort.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,OAAO,GAAG,KAAK,GAAG,MAAM,CAAC;AAErC,MAAM,MAAM,SAAS,GAAG;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,OAAO,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,IAAI,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IACxB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,KAAK,IAAI,CAAC;IAChD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC;CAClD,CAAC;AAEF,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI;IAC7B,MAAM,EAAE,CAAC,EAAE,CAAC;IACZ,IAAI,EAAE,SAAS,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/B,CAAC;AASF,wBAAgB,OAAO,CAAC,CAAC,EACvB,KAAK,EAAE,CAAC,EAAE,EACV,OAAO,GAAE,cAAc,CAAC,CAAC,CAAM,GAC9B,aAAa,CAAC,CAAC,CAAC,CAoDlB"}
@@ -6,21 +6,30 @@ function resolvePath(obj, path) {
6
6
  }, obj);
7
7
  }
8
8
  function useSort(items, options = {}) {
9
- const { defaultKey, defaultDir = "asc", resolvers } = options;
10
- const [sort, setSort] = useState(
9
+ const {
10
+ defaultKey,
11
+ defaultDir = "asc",
12
+ sort: controlledSort,
13
+ onSortChange,
14
+ manual = false,
15
+ resolvers
16
+ } = options;
17
+ const [localSort, setLocalSort] = useState(
11
18
  defaultKey ? { key: defaultKey, dir: defaultDir } : null
12
19
  );
20
+ const isControlled = controlledSort !== void 0;
21
+ const sort = isControlled ? controlledSort : localSort;
13
22
  function toggle(key) {
14
- setSort((prev) => {
15
- if ((prev == null ? void 0 : prev.key) === key) {
16
- return prev.dir === "asc" ? { key, dir: "desc" } : null;
17
- }
18
- return { key, dir: "asc" };
19
- });
23
+ const next = (sort == null ? void 0 : sort.key) === key ? sort.dir === "asc" ? { key, dir: "desc" } : null : { key, dir: "asc" };
24
+ if (isControlled) {
25
+ onSortChange == null ? void 0 : onSortChange(next);
26
+ } else {
27
+ setLocalSort(next);
28
+ }
20
29
  }
21
30
  const sorted = useMemo(() => {
22
31
  if (!items) return [];
23
- if (!sort) return items;
32
+ if (manual || !sort) return items;
24
33
  const { key, dir } = sort;
25
34
  const resolver = resolvers == null ? void 0 : resolvers[key];
26
35
  const get = (item) => resolver ? resolver(item) : resolvePath(item, key);
@@ -33,7 +42,7 @@ function useSort(items, options = {}) {
33
42
  const cmp = typeof av === "number" && typeof bv === "number" ? av - bv : String(av).localeCompare(String(bv));
34
43
  return dir === "asc" ? cmp : -cmp;
35
44
  });
36
- }, [items, sort, resolvers]);
45
+ }, [items, manual, sort, resolvers]);
37
46
  return { sorted, sort, toggle };
38
47
  }
39
48
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"use-sort.js","sources":["../../src/hooks/use-sort.ts"],"sourcesContent":["import { useMemo, useState } from \"react\";\n\nexport type SortDir = \"asc\" | \"desc\";\n\nexport type SortState = {\n key: string;\n dir: SortDir;\n};\n\nexport type UseSortOptions<T> = {\n defaultKey?: string;\n defaultDir?: SortDir;\n resolvers?: Record<string, (item: T) => unknown>;\n};\n\nexport type UseSortReturn<T> = {\n sorted: T[];\n sort: SortState | null;\n toggle: (key: string) => void;\n};\n\nfunction resolvePath(obj: unknown, path: string): unknown {\n return path.split(\".\").reduce<unknown>((o, k) => {\n if (o && typeof o === \"object\") return (o as Record<string, unknown>)[k];\n return undefined;\n }, obj);\n}\n\nexport function useSort<T>(items: T[], options: UseSortOptions<T> = {}): UseSortReturn<T> {\n const { defaultKey, defaultDir = \"asc\", resolvers } = options;\n\n const [sort, setSort] = useState<SortState | null>(\n defaultKey ? { key: defaultKey, dir: defaultDir } : null,\n );\n\n function toggle(key: string) {\n setSort((prev) => {\n if (prev?.key === key) {\n return prev.dir === \"asc\" ? { key, dir: \"desc\" } : null;\n }\n return { key, dir: \"asc\" };\n });\n }\n\n const sorted = useMemo(() => {\n if (!items) return [];\n if (!sort) return items;\n const { key, dir } = sort;\n const resolver = resolvers?.[key];\n const get = (item: T) => (resolver ? resolver(item) : resolvePath(item, key));\n return [...items].sort((a, b) => {\n const av = get(a);\n const bv = get(b);\n if (av == null && bv == null) return 0;\n if (av == null) return 1;\n if (bv == null) return -1;\n const cmp =\n typeof av === \"number\" && typeof bv === \"number\"\n ? av - bv\n : String(av).localeCompare(String(bv));\n return dir === \"asc\" ? cmp : -cmp;\n });\n }, [items, sort, resolvers]);\n\n return { sorted, sort, toggle };\n}\n"],"names":[],"mappings":";AAqBA,SAAS,YAAY,KAAc,MAAuB;AACxD,SAAO,KAAK,MAAM,GAAG,EAAE,OAAgB,CAAC,GAAG,MAAM;AAC/C,QAAI,KAAK,OAAO,MAAM,SAAU,QAAQ,EAA8B,CAAC;AACvE,WAAO;AAAA,EACT,GAAG,GAAG;AACR;AAEO,SAAS,QAAW,OAAY,UAA6B,IAAsB;AACxF,QAAM,EAAE,YAAY,aAAa,OAAO,cAAc;AAEtD,QAAM,CAAC,MAAM,OAAO,IAAI;AAAA,IACtB,aAAa,EAAE,KAAK,YAAY,KAAK,eAAe;AAAA,EAAA;AAGtD,WAAS,OAAO,KAAa;AAC3B,YAAQ,CAAC,SAAS;AAChB,WAAI,6BAAM,SAAQ,KAAK;AACrB,eAAO,KAAK,QAAQ,QAAQ,EAAE,KAAK,KAAK,WAAW;AAAA,MACrD;AACA,aAAO,EAAE,KAAK,KAAK,MAAA;AAAA,IACrB,CAAC;AAAA,EACH;AAEA,QAAM,SAAS,QAAQ,MAAM;AAC3B,QAAI,CAAC,MAAO,QAAO,CAAA;AACnB,QAAI,CAAC,KAAM,QAAO;AAClB,UAAM,EAAE,KAAK,IAAA,IAAQ;AACrB,UAAM,WAAW,uCAAY;AAC7B,UAAM,MAAM,CAAC,SAAa,WAAW,SAAS,IAAI,IAAI,YAAY,MAAM,GAAG;AAC3E,WAAO,CAAC,GAAG,KAAK,EAAE,KAAK,CAAC,GAAG,MAAM;AAC/B,YAAM,KAAK,IAAI,CAAC;AAChB,YAAM,KAAK,IAAI,CAAC;AAChB,UAAI,MAAM,QAAQ,MAAM,KAAM,QAAO;AACrC,UAAI,MAAM,KAAM,QAAO;AACvB,UAAI,MAAM,KAAM,QAAO;AACvB,YAAM,MACJ,OAAO,OAAO,YAAY,OAAO,OAAO,WACpC,KAAK,KACL,OAAO,EAAE,EAAE,cAAc,OAAO,EAAE,CAAC;AACzC,aAAO,QAAQ,QAAQ,MAAM,CAAC;AAAA,IAChC,CAAC;AAAA,EACH,GAAG,CAAC,OAAO,MAAM,SAAS,CAAC;AAE3B,SAAO,EAAE,QAAQ,MAAM,OAAA;AACzB;"}
1
+ {"version":3,"file":"use-sort.js","sources":["../../src/hooks/use-sort.ts"],"sourcesContent":["import { useMemo, useState } from \"react\";\n\nexport type SortDir = \"asc\" | \"desc\";\n\nexport type SortState = {\n key: string;\n dir: SortDir;\n};\n\nexport type UseSortOptions<T> = {\n defaultKey?: string;\n defaultDir?: SortDir;\n sort?: SortState | null;\n onSortChange?: (sort: SortState | null) => void;\n manual?: boolean;\n resolvers?: Record<string, (item: T) => unknown>;\n};\n\nexport type UseSortReturn<T> = {\n sorted: T[];\n sort: SortState | null;\n toggle: (key: string) => void;\n};\n\nfunction resolvePath(obj: unknown, path: string): unknown {\n return path.split(\".\").reduce<unknown>((o, k) => {\n if (o && typeof o === \"object\") return (o as Record<string, unknown>)[k];\n return undefined;\n }, obj);\n}\n\nexport function useSort<T>(\n items: T[],\n options: UseSortOptions<T> = {},\n): UseSortReturn<T> {\n const {\n defaultKey,\n defaultDir = \"asc\",\n sort: controlledSort,\n onSortChange,\n manual = false,\n resolvers,\n } = options;\n\n const [localSort, setLocalSort] = useState<SortState | null>(\n defaultKey ? { key: defaultKey, dir: defaultDir } : null,\n );\n const isControlled = controlledSort !== undefined;\n const sort = isControlled ? controlledSort : localSort;\n\n function toggle(key: string) {\n const next =\n sort?.key === key\n ? sort.dir === \"asc\"\n ? { key, dir: \"desc\" as const }\n : null\n : { key, dir: \"asc\" as const };\n if (isControlled) {\n onSortChange?.(next);\n } else {\n setLocalSort(next);\n }\n }\n\n const sorted = useMemo(() => {\n if (!items) return [];\n if (manual || !sort) return items;\n const { key, dir } = sort;\n const resolver = resolvers?.[key];\n const get = (item: T) =>\n resolver ? resolver(item) : resolvePath(item, key);\n return [...items].sort((a, b) => {\n const av = get(a);\n const bv = get(b);\n if (av == null && bv == null) return 0;\n if (av == null) return 1;\n if (bv == null) return -1;\n const cmp =\n typeof av === \"number\" && typeof bv === \"number\"\n ? av - bv\n : String(av).localeCompare(String(bv));\n return dir === \"asc\" ? cmp : -cmp;\n });\n }, [items, manual, sort, resolvers]);\n\n return { sorted, sort, toggle };\n}\n"],"names":[],"mappings":";AAwBA,SAAS,YAAY,KAAc,MAAuB;AACxD,SAAO,KAAK,MAAM,GAAG,EAAE,OAAgB,CAAC,GAAG,MAAM;AAC/C,QAAI,KAAK,OAAO,MAAM,SAAU,QAAQ,EAA8B,CAAC;AACvE,WAAO;AAAA,EACT,GAAG,GAAG;AACR;AAEO,SAAS,QACd,OACA,UAA6B,IACX;AAClB,QAAM;AAAA,IACJ;AAAA,IACA,aAAa;AAAA,IACb,MAAM;AAAA,IACN;AAAA,IACA,SAAS;AAAA,IACT;AAAA,EAAA,IACE;AAEJ,QAAM,CAAC,WAAW,YAAY,IAAI;AAAA,IAChC,aAAa,EAAE,KAAK,YAAY,KAAK,eAAe;AAAA,EAAA;AAEtD,QAAM,eAAe,mBAAmB;AACxC,QAAM,OAAO,eAAe,iBAAiB;AAE7C,WAAS,OAAO,KAAa;AAC3B,UAAM,QACJ,6BAAM,SAAQ,MACV,KAAK,QAAQ,QACX,EAAE,KAAK,KAAK,WACZ,OACF,EAAE,KAAK,KAAK,MAAA;AAClB,QAAI,cAAc;AAChB,mDAAe;AAAA,IACjB,OAAO;AACL,mBAAa,IAAI;AAAA,IACnB;AAAA,EACF;AAEA,QAAM,SAAS,QAAQ,MAAM;AAC3B,QAAI,CAAC,MAAO,QAAO,CAAA;AACnB,QAAI,UAAU,CAAC,KAAM,QAAO;AAC5B,UAAM,EAAE,KAAK,IAAA,IAAQ;AACrB,UAAM,WAAW,uCAAY;AAC7B,UAAM,MAAM,CAAC,SACX,WAAW,SAAS,IAAI,IAAI,YAAY,MAAM,GAAG;AACnD,WAAO,CAAC,GAAG,KAAK,EAAE,KAAK,CAAC,GAAG,MAAM;AAC/B,YAAM,KAAK,IAAI,CAAC;AAChB,YAAM,KAAK,IAAI,CAAC;AAChB,UAAI,MAAM,QAAQ,MAAM,KAAM,QAAO;AACrC,UAAI,MAAM,KAAM,QAAO;AACvB,UAAI,MAAM,KAAM,QAAO;AACvB,YAAM,MACJ,OAAO,OAAO,YAAY,OAAO,OAAO,WACpC,KAAK,KACL,OAAO,EAAE,EAAE,cAAc,OAAO,EAAE,CAAC;AACzC,aAAO,QAAQ,QAAQ,MAAM,CAAC;AAAA,IAChC,CAAC;AAAA,EACH,GAAG,CAAC,OAAO,QAAQ,MAAM,SAAS,CAAC;AAEnC,SAAO,EAAE,QAAQ,MAAM,OAAA;AACzB;"}
package/dist/index.cjs CHANGED
@@ -44,6 +44,8 @@ const JsonSchemaForm = require("./components/JsonSchemaForm.cjs");
44
44
  const jsonSchemaFormSize = require("./components/json-schema-form-size.cjs");
45
45
  const jsonSchemaFormPreferences = require("./components/json-schema-form-preferences.cjs");
46
46
  const jsonSchemaFormResolve = require("./components/json-schema-form-resolve.cjs");
47
+ const jsonSchemaFormRefs = require("./components/json-schema-form-refs.cjs");
48
+ const jsonSchemaFormTemplate = require("./components/json-schema-form-template.cjs");
47
49
  const SplitPane = require("./layout/SplitPane.cjs");
48
50
  const AppShell = require("./layout/AppShell.cjs");
49
51
  const AppSidebar = require("./layout/AppSidebar.cjs");
@@ -94,6 +96,8 @@ const TimeseriesPanel_model = require("./data/TimeseriesPanel.model.cjs");
94
96
  const TimeseriesPanel_axes = require("./data/TimeseriesPanel.axes.cjs");
95
97
  const TimeseriesGauge = require("./data/TimeseriesGauge.cjs");
96
98
  const TimeseriesCoreBars = require("./data/TimeseriesCoreBars.cjs");
99
+ const WorkloadCard = require("./data/WorkloadCard.cjs");
100
+ const WorkloadCard_model = require("./data/WorkloadCard.model.cjs");
97
101
  const TimeseriesCoreBars_model = require("./data/TimeseriesCoreBars.model.cjs");
98
102
  const StatusBreakdown = require("./data/StatusBreakdown.cjs");
99
103
  const statusBreakdownUtils = require("./data/status-breakdown-utils.cjs");
@@ -489,6 +493,8 @@ exports.effectiveProperties = jsonSchemaFormResolve.effectiveProperties;
489
493
  exports.isOpenStringMap = jsonSchemaFormResolve.isOpenStringMap;
490
494
  exports.matchesIf = jsonSchemaFormResolve.matchesIf;
491
495
  exports.resolveControl = jsonSchemaFormResolve.resolveControl;
496
+ exports.rehydrateRefs = jsonSchemaFormRefs.rehydrateRefs;
497
+ exports.templateValuePre = jsonSchemaFormTemplate.templateValuePre;
492
498
  exports.SplitPane = SplitPane.SplitPane;
493
499
  exports.AppShell = AppShell.AppShell;
494
500
  exports.AppLayout = AppSidebar.AppLayout;
@@ -559,6 +565,11 @@ exports.assignAxes = TimeseriesPanel_axes.assignAxes;
559
565
  exports.latestOf = TimeseriesPanel_axes.latestOf;
560
566
  exports.TimeseriesGauge = TimeseriesGauge.TimeseriesGauge;
561
567
  exports.TimeseriesCoreBars = TimeseriesCoreBars.TimeseriesCoreBars;
568
+ exports.WorkloadCard = WorkloadCard.WorkloadCard;
569
+ exports.formatReplicaCounts = WorkloadCard_model.formatReplicaCounts;
570
+ exports.workloadKindLabel = WorkloadCard_model.workloadKindLabel;
571
+ exports.workloadStatusLabel = WorkloadCard_model.workloadStatusLabel;
572
+ exports.workloadStatusTone = WorkloadCard_model.workloadStatusTone;
562
573
  exports.deriveCoreBars = TimeseriesCoreBars_model.deriveCoreBars;
563
574
  exports.StackedStatusBar = StatusBreakdown.StackedStatusBar;
564
575
  exports.StatusRows = StatusBreakdown.StatusRows;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/dist/index.js CHANGED
@@ -42,6 +42,8 @@ import { JsonSchemaForm } from "./components/JsonSchemaForm.js";
42
42
  import { DEFAULT_FORM_SIZE } from "./components/json-schema-form-size.js";
43
43
  import { DEFAULT_PREFERENCES_STORAGE_KEY, readPreferences, writePreferences } from "./components/json-schema-form-preferences.js";
44
44
  import { effectiveProperties, isOpenStringMap, matchesIf, resolveControl } from "./components/json-schema-form-resolve.js";
45
+ import { rehydrateRefs } from "./components/json-schema-form-refs.js";
46
+ import { templateValuePre } from "./components/json-schema-form-template.js";
45
47
  import { SplitPane } from "./layout/SplitPane.js";
46
48
  import { AppShell } from "./layout/AppShell.js";
47
49
  import { AppLayout, AppSidebar } from "./layout/AppSidebar.js";
@@ -92,6 +94,8 @@ import { mergeSeries, resolveBreakdown, resolveSeries } from "./data/TimeseriesP
92
94
  import { assignAxes, latestOf } from "./data/TimeseriesPanel.axes.js";
93
95
  import { TimeseriesGauge } from "./data/TimeseriesGauge.js";
94
96
  import { TimeseriesCoreBars } from "./data/TimeseriesCoreBars.js";
97
+ import { WorkloadCard } from "./data/WorkloadCard.js";
98
+ import { formatReplicaCounts, workloadKindLabel, workloadStatusLabel, workloadStatusTone } from "./data/WorkloadCard.model.js";
95
99
  import { deriveCoreBars } from "./data/TimeseriesCoreBars.model.js";
96
100
  import { StackedStatusBar, StatusRows } from "./data/StatusBreakdown.js";
97
101
  import { segment } from "./data/status-breakdown-utils.js";
@@ -1045,6 +1049,7 @@ export {
1045
1049
  UiZoomOutFilled,
1046
1050
  VIEW_OPTIONS,
1047
1051
  Version,
1052
+ WorkloadCard,
1048
1053
  WorkloadPicker,
1049
1054
  allGroupsTerminal,
1050
1055
  annotateRoutePaths,
@@ -1091,6 +1096,7 @@ export {
1091
1096
  formatDuration,
1092
1097
  formatPropertyLabel,
1093
1098
  formatRelativeTime,
1099
+ formatReplicaCounts,
1094
1100
  formatShort,
1095
1101
  formatTestDuration,
1096
1102
  formatTimestamp,
@@ -1140,6 +1146,7 @@ export {
1140
1146
  processStateColor,
1141
1147
  processStateIcon,
1142
1148
  readPreferences,
1149
+ rehydrateRefs,
1143
1150
  resolveBreakdown,
1144
1151
  resolveControl,
1145
1152
  resolveSeries,
@@ -1159,6 +1166,7 @@ export {
1159
1166
  taskStatusBg,
1160
1167
  taskStatusColor,
1161
1168
  taskStatusIcon,
1169
+ templateValuePre,
1162
1170
  testStatus,
1163
1171
  totalDuration,
1164
1172
  useBrowserRouter,
@@ -1184,6 +1192,9 @@ export {
1184
1192
  useToast,
1185
1193
  withBasePath,
1186
1194
  workloadKey,
1195
+ workloadKindLabel,
1196
+ workloadStatusLabel,
1197
+ workloadStatusTone,
1187
1198
  writePreferences
1188
1199
  };
1189
1200
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -5,6 +5,7 @@ const react = require("react");
5
5
  const utils = require("../lib/utils.cjs");
6
6
  const Icon = require("../data/Icon.cjs");
7
7
  const router = require("../rpc/router.cjs");
8
+ const modalStack = require("../overlay/modalStack.cjs");
8
9
  const SplitPane = require("./SplitPane.cjs");
9
10
  const UiSidebar = require("../icons/components/UiSidebar.cjs");
10
11
  const UiMenu = require("../icons/components/UiMenu.cjs");
@@ -19,6 +20,7 @@ function AppShell(props) {
19
20
  nav,
20
21
  search,
21
22
  actions,
23
+ mobileActions,
22
24
  toolbar,
23
25
  searchMaxWidth = "28rem",
24
26
  navSections,
@@ -52,16 +54,10 @@ function AppShell(props) {
52
54
  if (collapsedStorageKey && typeof window !== "undefined")
53
55
  window.localStorage.setItem(collapsedStorageKey, String(collapsed));
54
56
  }, [collapsed, collapsedStorageKey]);
55
- react.useEffect(() => {
56
- if (!mobileSidebarOpen) return;
57
- const onKeyDown = (event) => {
58
- if (event.key === "Escape") setMobileSidebarOpen(false);
59
- };
60
- document.addEventListener("keydown", onKeyDown);
61
- return () => document.removeEventListener("keydown", onKeyDown);
62
- }, [mobileSidebarOpen]);
57
+ modalStack.useEscapeLayer(mobileSidebarOpen, () => setMobileSidebarOpen(false));
63
58
  const railWidth = collapsed ? collapsedWidth : sidebarWidth;
64
- const hasTopBar = !hasSidebar && brand !== void 0 || nav !== void 0 || search !== void 0 || actions !== void 0 || toolbar !== void 0;
59
+ const compactActions = mobileActions ?? actions;
60
+ const hasTopBar = !hasSidebar && brand !== void 0 || nav !== void 0 || search !== void 0 || actions !== void 0 || mobileActions !== void 0 || toolbar !== void 0;
65
61
  const hasMobileHeader = hasSidebar;
66
62
  const hasBodyHeader = bodyHeader !== void 0 || bodyActions !== void 0;
67
63
  const renderSidebarContent = (collapsedValue, onNavigate) => typeof sidebar === "function" ? sidebar(collapsedValue) : sidebar !== void 0 ? sidebar : navSections && /* @__PURE__ */ jsxRuntime.jsx(
@@ -126,7 +122,7 @@ function AppShell(props) {
126
122
  }
127
123
  ),
128
124
  brand && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-w-0 flex-1 items-center gap-density-2", children: brand }),
129
- actions && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ml-auto flex shrink-0 items-center gap-density-2", children: actions }),
125
+ compactActions && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ml-auto flex min-w-0 shrink items-center justify-end gap-density-2", children: compactActions }),
130
126
  search !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "basis-full min-w-0", children: search }),
131
127
  nav && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "basis-full min-w-0 overflow-x-auto", children: nav })
132
128
  ] }),
@@ -1 +1 @@
1
- {"version":3,"file":"AppShell.cjs","sources":["../../src/layout/AppShell.tsx"],"sourcesContent":["import { useEffect, useState, type ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Icon, type StaticIconComponent } from \"../data/Icon\";\nimport { UiClose, UiMenu, UiSidebar } from \"../icons\";\nimport { useRouter } from \"../rpc/router\";\nimport type { RenderLink } from \"../rpc/EndpointList\";\nimport { SplitPane } from \"./SplitPane\";\n\n// AppShell is a sidebar-first application shell. The full-height DARK nav rail\n// owns the brand + collapse toggle and renders grouped nav sections; the top bar\n// (search, actions) sits to the RIGHT of the rail — there is no full-width top\n// bar when a rail is present. With no rail, the top bar spans full width. The\n// body carries a fixed bodyHeader + bodyActions row over an optional\n// bodySidebar | body-main split (body-main scrolls; bodySidebar — e.g. a tree —\n// scrolls independently via a resizable SplitPane).\n\nexport type AppShellNavItem = {\n /** Stable key; also the React key. */\n key: string;\n /** Visible label (hidden when the rail is collapsed). */\n label: ReactNode;\n /** Leading icon (icon name or component). */\n icon?: string | StaticIconComponent;\n /** Highlights the item as the current location. */\n active?: boolean;\n /** Routing destination; rendered as an anchor via the RouterAdapter. */\n to: string;\n /** Opens `to` in a new tab. */\n external?: boolean;\n /** Trailing adornment (count, status dot). */\n badge?: ReactNode;\n};\n\nexport type AppShellNavSection = {\n /** Section heading; collapses to a divider when the rail is collapsed. */\n label?: string;\n items: AppShellNavItem[];\n};\n\nexport type AppShellProps = {\n // ── Top bar (right of the rail; full width when no rail) ─\n /** Brand mark / wordmark. Shown in the rail header when a rail is present, else in the top bar. */\n brand?: ReactNode;\n /** Inline primary nav (e.g. tabs) shown after the brand in the top bar. */\n nav?: ReactNode;\n /** Centered search slot; grows to fill and is width-capped. */\n search?: ReactNode;\n /** Right-aligned cluster (icon buttons, settings/org picker, …). */\n actions?: ReactNode;\n /** Optional second top-bar row for filters / bulk actions. */\n toolbar?: ReactNode;\n /** Max width of the centered search slot. Defaults to 28rem. */\n searchMaxWidth?: string | number;\n\n // ── Left nav rail (dark, collapsible, fixed width) ───────\n /** Declarative nav sections rendered into the rail (collapse-aware). */\n navSections?: AppShellNavSection[];\n /** Custom sidebar content; receives the collapsed flag. Overrides navSections. */\n sidebar?: ReactNode | ((collapsed: boolean) => ReactNode);\n /** Pinned below the rail header (e.g. a context switcher). */\n sidebarHeader?: ReactNode;\n /** Pinned to the bottom of the rail (version, account). */\n sidebarFooter?: ReactNode;\n /** Show the collapse toggle. Defaults to true when a rail is present. */\n collapsible?: boolean;\n /** Initial collapsed state (uncontrolled). */\n defaultCollapsed?: boolean;\n /** localStorage key persisting the collapsed state. */\n collapsedStorageKey?: string;\n /** Expanded rail width in px. Defaults to 240. */\n sidebarWidth?: number;\n /** Collapsed rail width in px. Defaults to 56. */\n collapsedWidth?: number;\n /** Accessible label for the mobile navigation drawer. Defaults to \"Navigation\". */\n mobileSidebarLabel?: string;\n\n // ── Body ─────────────────────────────────────────────────\n /** Fixed header row, left side (breadcrumb, title, tabs). */\n bodyHeader?: ReactNode;\n /** Fixed header row, right side (entity actions) — same row as bodyHeader. */\n bodyActions?: ReactNode;\n /** Optional independent-scroll pane (e.g. a tree). Renders a SplitPane vs body-main. */\n bodySidebar?: ReactNode;\n /** bodySidebar width as a percent when present. Defaults to 24. */\n bodySplit?: number;\n /** Main content (body-main); fills the remaining space and scrolls. */\n children: ReactNode;\n\n className?: string;\n headerClassName?: string;\n toolbarClassName?: string;\n sidebarClassName?: string;\n bodyHeaderClassName?: string;\n contentClassName?: string;\n};\n\nfunction readStored(key: string | undefined): string | null {\n if (!key || typeof window === \"undefined\") return null;\n return window.localStorage.getItem(key);\n}\n\nexport function AppShell(props: AppShellProps) {\n const {\n brand,\n nav,\n search,\n actions,\n toolbar,\n searchMaxWidth = \"28rem\",\n navSections,\n sidebar,\n sidebarHeader,\n sidebarFooter,\n collapsible = true,\n defaultCollapsed = false,\n collapsedStorageKey,\n sidebarWidth = 240,\n collapsedWidth = 56,\n mobileSidebarLabel = \"Navigation\",\n bodyHeader,\n bodyActions,\n bodySidebar,\n bodySplit = 24,\n children,\n className,\n headerClassName,\n toolbarClassName,\n sidebarClassName,\n bodyHeaderClassName,\n contentClassName,\n } = props;\n\n const hasSidebar = sidebar !== undefined || (navSections?.length ?? 0) > 0;\n const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false);\n\n const [collapsed, setCollapsed] = useState(\n () =>\n readStored(collapsedStorageKey) === \"true\" ||\n (readStored(collapsedStorageKey) === null && defaultCollapsed),\n );\n\n useEffect(() => {\n if (collapsedStorageKey && typeof window !== \"undefined\")\n window.localStorage.setItem(collapsedStorageKey, String(collapsed));\n }, [collapsed, collapsedStorageKey]);\n\n useEffect(() => {\n if (!mobileSidebarOpen) return;\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") setMobileSidebarOpen(false);\n };\n document.addEventListener(\"keydown\", onKeyDown);\n return () => document.removeEventListener(\"keydown\", onKeyDown);\n }, [mobileSidebarOpen]);\n\n const railWidth = collapsed ? collapsedWidth : sidebarWidth;\n const hasTopBar =\n (!hasSidebar && brand !== undefined) ||\n nav !== undefined ||\n search !== undefined ||\n actions !== undefined ||\n toolbar !== undefined;\n const hasMobileHeader = hasSidebar;\n const hasBodyHeader = bodyHeader !== undefined || bodyActions !== undefined;\n const renderSidebarContent = (collapsedValue: boolean, onNavigate?: () => void) =>\n typeof sidebar === \"function\"\n ? sidebar(collapsedValue)\n : sidebar !== undefined\n ? sidebar\n : navSections && (\n <NavSections\n sections={navSections}\n collapsed={collapsedValue}\n {...(onNavigate ? { onNavigate } : {})}\n />\n );\n\n return (\n <div className={cn(\"flex h-full min-h-0 w-full bg-background\", className)}>\n {hasSidebar && (\n <aside\n style={{ width: railWidth }}\n className={cn(\n \"hidden shrink-0 flex-col overflow-hidden border-r border-sidebar-border bg-sidebar text-sidebar-foreground transition-[width] duration-200 md:flex\",\n sidebarClassName,\n )}\n >\n <div\n className={cn(\n \"flex h-14 shrink-0 items-center border-b border-sidebar-border\",\n collapsed\n ? \"justify-center px-2\"\n : \"justify-between px-density-3\",\n )}\n >\n {!collapsed && brand && (\n <div className=\"flex min-w-0 items-center gap-2\">{brand}</div>\n )}\n {collapsible && (\n <button\n type=\"button\"\n onClick={() => setCollapsed((c) => !c)}\n aria-label={collapsed ? \"Expand sidebar\" : \"Collapse sidebar\"}\n title={collapsed ? \"Expand sidebar\" : \"Collapse sidebar\"}\n className=\"flex h-8 w-8 shrink-0 items-center justify-center rounded-md text-sidebar-foreground/70 transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground\"\n >\n <Icon icon={UiSidebar} className=\"h-4 w-4\" />\n </button>\n )}\n </div>\n {sidebarHeader && (\n <div className=\"shrink-0 border-b border-sidebar-border px-density-3 py-density-2\">\n {sidebarHeader}\n </div>\n )}\n <div className=\"min-h-0 flex-1 overflow-y-auto py-density-2\">\n {renderSidebarContent(collapsed)}\n </div>\n {sidebarFooter && (\n <div className=\"mt-auto shrink-0 border-t border-sidebar-border px-density-3 py-density-2\">\n {sidebarFooter}\n </div>\n )}\n </aside>\n )}\n\n <div className=\"flex min-h-0 min-w-0 flex-1 flex-col\">\n {(hasTopBar || hasMobileHeader) && (\n <header className=\"shrink-0 border-b border-border bg-card\">\n {hasMobileHeader && (\n <div className=\"flex flex-wrap items-center gap-density-2 px-density-3 py-density-2 md:hidden\">\n <button\n type=\"button\"\n aria-label={`Open ${mobileSidebarLabel}`}\n title={mobileSidebarLabel}\n className=\"inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-md border border-border bg-background text-foreground transition-colors hover:bg-accent\"\n onClick={() => setMobileSidebarOpen(true)}\n >\n <Icon icon={UiMenu} className=\"h-4 w-4\" />\n </button>\n {brand && (\n <div className=\"flex min-w-0 flex-1 items-center gap-density-2\">\n {brand}\n </div>\n )}\n {actions && (\n <div className=\"ml-auto flex shrink-0 items-center gap-density-2\">\n {actions}\n </div>\n )}\n {search !== undefined && (\n <div className=\"basis-full min-w-0\">\n {search}\n </div>\n )}\n {nav && (\n <div className=\"basis-full min-w-0 overflow-x-auto\">\n {nav}\n </div>\n )}\n </div>\n )}\n <div\n className={cn(\n \"hidden h-14 items-center gap-density-3 px-density-4 md:flex\",\n !hasSidebar && \"flex\",\n headerClassName,\n )}\n >\n {!hasSidebar && brand && (\n <div className=\"flex shrink-0 items-center gap-density-2\">\n {brand}\n </div>\n )}\n {nav && <div className=\"flex shrink-0 items-center\">{nav}</div>}\n {search !== undefined && (\n <div className=\"flex min-w-0 flex-1 justify-center\">\n <div className=\"w-full\" style={{ maxWidth: searchMaxWidth }}>\n {search}\n </div>\n </div>\n )}\n {search === undefined && <div className=\"flex-1\" />}\n {actions && (\n <div className=\"flex shrink-0 items-center gap-density-2\">\n {actions}\n </div>\n )}\n </div>\n {toolbar && (\n <div\n className={cn(\n \"flex flex-wrap items-center gap-density-2 border-t border-border bg-muted px-density-3 py-density-2 md:px-density-4\",\n toolbarClassName,\n )}\n >\n {toolbar}\n </div>\n )}\n </header>\n )}\n\n {hasBodyHeader && (\n <div\n className={cn(\n \"flex shrink-0 flex-col items-stretch gap-density-2 border-b border-border bg-card px-density-3 py-density-2 md:flex-row md:items-start md:justify-between md:gap-density-3 md:px-density-4\",\n bodyHeaderClassName,\n )}\n >\n <div className=\"min-w-0 flex-1\">{bodyHeader}</div>\n {bodyActions && (\n <div className=\"flex shrink-0 flex-wrap items-center gap-density-2\">\n {bodyActions}\n </div>\n )}\n </div>\n )}\n\n {bodySidebar !== undefined ? (\n <>\n <div className=\"flex min-h-0 flex-1 flex-col overflow-hidden md:hidden\">\n <div className=\"max-h-[40vh] shrink-0 overflow-y-auto border-b border-border bg-background\">\n {bodySidebar}\n </div>\n <main\n className={cn(\n \"min-h-0 min-w-0 flex-1 overflow-auto\",\n contentClassName,\n )}\n >\n {children}\n </main>\n </div>\n <SplitPane\n className=\"hidden min-h-0 flex-1 md:flex\"\n defaultSplit={bodySplit}\n minLeft={12}\n minRight={30}\n left={bodySidebar}\n right={\n <div className={cn(\"h-full min-w-0\", contentClassName)}>\n {children}\n </div>\n }\n rightClass=\"overflow-y-auto\"\n />\n </>\n ) : (\n <main\n className={cn(\n \"min-h-0 min-w-0 flex-1 overflow-auto\",\n contentClassName,\n )}\n >\n {children}\n </main>\n )}\n </div>\n {hasSidebar && mobileSidebarOpen && (\n <div\n className=\"fixed inset-0 z-50 md:hidden\"\n role=\"presentation\"\n onClick={() => setMobileSidebarOpen(false)}\n >\n <div className=\"absolute inset-0 bg-black/40\" />\n <aside\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={mobileSidebarLabel}\n className={cn(\n \"relative z-10 flex h-full w-[min(20rem,85vw)] flex-col overflow-hidden border-r border-sidebar-border bg-sidebar text-sidebar-foreground shadow-xl\",\n sidebarClassName,\n )}\n onClick={(event) => event.stopPropagation()}\n >\n <div className=\"flex h-14 shrink-0 items-center justify-between border-b border-sidebar-border px-density-3\">\n {brand && <div className=\"flex min-w-0 items-center gap-2\">{brand}</div>}\n <button\n type=\"button\"\n aria-label={`Close ${mobileSidebarLabel}`}\n title=\"Close\"\n className=\"flex h-8 w-8 shrink-0 items-center justify-center rounded-md text-sidebar-foreground/70 transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground\"\n onClick={() => setMobileSidebarOpen(false)}\n >\n <Icon icon={UiClose} className=\"h-4 w-4\" />\n </button>\n </div>\n {sidebarHeader && (\n <div className=\"shrink-0 border-b border-sidebar-border px-density-3 py-density-2\">\n {sidebarHeader}\n </div>\n )}\n <div className=\"min-h-0 flex-1 overflow-y-auto py-density-2\">\n {renderSidebarContent(false, () => setMobileSidebarOpen(false))}\n </div>\n {sidebarFooter && (\n <div className=\"mt-auto shrink-0 border-t border-sidebar-border px-density-3 py-density-2\">\n {sidebarFooter}\n </div>\n )}\n </aside>\n </div>\n )}\n </div>\n );\n}\n\nfunction NavSections({\n sections,\n collapsed,\n onNavigate,\n}: {\n sections: AppShellNavSection[];\n collapsed: boolean;\n onNavigate?: () => void;\n}) {\n const { renderLink } = useRouter();\n return (\n <nav\n className={cn(\n \"flex flex-col gap-0.5\",\n collapsed ? \"px-2\" : \"px-density-2\",\n )}\n >\n {sections.map((section, i) => (\n <div key={section.label ?? `section-${i}`} className=\"flex flex-col\">\n {section.label &&\n (collapsed ? (\n <div className=\"mx-2 mb-1 mt-3 border-t border-sidebar-border first:mt-1\" />\n ) : (\n <div className=\"mb-0.5 mt-3 px-density-2 text-[10px] font-semibold uppercase tracking-[0.18em] text-sidebar-foreground/55 first:mt-1\">\n {section.label}\n </div>\n ))}\n {section.items.map((item) => (\n <NavItemRow\n key={item.key}\n item={item}\n collapsed={collapsed}\n renderLink={renderLink}\n {...(onNavigate ? { onNavigate } : {})}\n />\n ))}\n </div>\n ))}\n </nav>\n );\n}\n\nfunction NavItemRow({\n item,\n collapsed,\n renderLink,\n onNavigate,\n}: {\n item: AppShellNavItem;\n collapsed: boolean;\n renderLink: RenderLink;\n onNavigate?: () => void;\n}) {\n const className = cn(\n \"flex w-full items-center gap-2.5 rounded-md px-density-2 py-1.5 text-left text-[13px] text-sidebar-foreground transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground\",\n collapsed && \"justify-center px-0\",\n item.active && \"bg-sidebar-accent font-medium text-sidebar-primary\",\n );\n const inner = (\n <>\n {item.icon && (\n <span className=\"flex h-4 w-4 shrink-0 items-center justify-center\">\n <Icon\n {...(typeof item.icon === \"string\"\n ? { name: item.icon }\n : { icon: item.icon })}\n />\n </span>\n )}\n {!collapsed && <span className=\"flex-1 truncate\">{item.label}</span>}\n {!collapsed && item.badge}\n </>\n );\n const title =\n collapsed && typeof item.label === \"string\" ? item.label : undefined;\n\n if (item.external) {\n return (\n <a\n href={item.to}\n className={className}\n title={title}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n onClick={onNavigate}\n >\n {inner}\n </a>\n );\n }\n const link = renderLink({\n key: item.key,\n to: item.to,\n className,\n children: inner,\n ...(title ? { title } : {}),\n });\n\n if (!onNavigate) return link;\n return (\n <div onClick={onNavigate}>\n {link}\n </div>\n );\n}\n"],"names":["useState","useEffect","jsx","cn","jsxs","Icon","UiSidebar","UiMenu","Fragment","SplitPane","UiClose","useRouter"],"mappings":"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"}
1
+ {"version":3,"file":"AppShell.cjs","sources":["../../src/layout/AppShell.tsx"],"sourcesContent":["import { useEffect, useState, type ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Icon, type StaticIconComponent } from \"../data/Icon\";\nimport { UiClose, UiMenu, UiSidebar } from \"../icons\";\nimport { useRouter } from \"../rpc/router\";\nimport type { RenderLink } from \"../rpc/EndpointList\";\nimport { useEscapeLayer } from \"../overlay/modalStack\";\nimport { SplitPane } from \"./SplitPane\";\n\n// AppShell is a sidebar-first application shell. The full-height DARK nav rail\n// owns the brand + collapse toggle and renders grouped nav sections; the top bar\n// (search, actions) sits to the RIGHT of the rail — there is no full-width top\n// bar when a rail is present. With no rail, the top bar spans full width. The\n// body carries a fixed bodyHeader + bodyActions row over an optional\n// bodySidebar | body-main split (body-main scrolls; bodySidebar — e.g. a tree —\n// scrolls independently via a resizable SplitPane).\n\nexport type AppShellNavItem = {\n /** Stable key; also the React key. */\n key: string;\n /** Visible label (hidden when the rail is collapsed). */\n label: ReactNode;\n /** Leading icon (icon name or component). */\n icon?: string | StaticIconComponent;\n /** Highlights the item as the current location. */\n active?: boolean;\n /** Routing destination; rendered as an anchor via the RouterAdapter. */\n to: string;\n /** Opens `to` in a new tab. */\n external?: boolean;\n /** Trailing adornment (count, status dot). */\n badge?: ReactNode;\n};\n\nexport type AppShellNavSection = {\n /** Section heading; collapses to a divider when the rail is collapsed. */\n label?: string;\n items: AppShellNavItem[];\n};\n\nexport type AppShellProps = {\n // ── Top bar (right of the rail; full width when no rail) ─\n /** Brand mark / wordmark. Shown in the rail header when a rail is present, else in the top bar. */\n brand?: ReactNode;\n /** Inline primary nav (e.g. tabs) shown after the brand in the top bar. */\n nav?: ReactNode;\n /** Centered search slot; grows to fill and is width-capped. */\n search?: ReactNode;\n /** Right-aligned cluster (icon buttons, settings/org picker, …). */\n actions?: ReactNode;\n /** Compact action cluster used in the mobile header. Defaults to `actions`. */\n mobileActions?: ReactNode;\n /** Optional second top-bar row for filters / bulk actions. */\n toolbar?: ReactNode;\n /** Max width of the centered search slot. Defaults to 28rem. */\n searchMaxWidth?: string | number;\n\n // ── Left nav rail (dark, collapsible, fixed width) ───────\n /** Declarative nav sections rendered into the rail (collapse-aware). */\n navSections?: AppShellNavSection[];\n /** Custom sidebar content; receives the collapsed flag. Overrides navSections. */\n sidebar?: ReactNode | ((collapsed: boolean) => ReactNode);\n /** Pinned below the rail header (e.g. a context switcher). */\n sidebarHeader?: ReactNode;\n /** Pinned to the bottom of the rail (version, account). */\n sidebarFooter?: ReactNode;\n /** Show the collapse toggle. Defaults to true when a rail is present. */\n collapsible?: boolean;\n /** Initial collapsed state (uncontrolled). */\n defaultCollapsed?: boolean;\n /** localStorage key persisting the collapsed state. */\n collapsedStorageKey?: string;\n /** Expanded rail width in px. Defaults to 240. */\n sidebarWidth?: number;\n /** Collapsed rail width in px. Defaults to 56. */\n collapsedWidth?: number;\n /** Accessible label for the mobile navigation drawer. Defaults to \"Navigation\". */\n mobileSidebarLabel?: string;\n\n // ── Body ─────────────────────────────────────────────────\n /** Fixed header row, left side (breadcrumb, title, tabs). */\n bodyHeader?: ReactNode;\n /** Fixed header row, right side (entity actions) — same row as bodyHeader. */\n bodyActions?: ReactNode;\n /** Optional independent-scroll pane (e.g. a tree). Renders a SplitPane vs body-main. */\n bodySidebar?: ReactNode;\n /** bodySidebar width as a percent when present. Defaults to 24. */\n bodySplit?: number;\n /** Main content (body-main); fills the remaining space and scrolls. */\n children: ReactNode;\n\n className?: string;\n headerClassName?: string;\n toolbarClassName?: string;\n sidebarClassName?: string;\n bodyHeaderClassName?: string;\n contentClassName?: string;\n};\n\nfunction readStored(key: string | undefined): string | null {\n if (!key || typeof window === \"undefined\") return null;\n return window.localStorage.getItem(key);\n}\n\nexport function AppShell(props: AppShellProps) {\n const {\n brand,\n nav,\n search,\n actions,\n mobileActions,\n toolbar,\n searchMaxWidth = \"28rem\",\n navSections,\n sidebar,\n sidebarHeader,\n sidebarFooter,\n collapsible = true,\n defaultCollapsed = false,\n collapsedStorageKey,\n sidebarWidth = 240,\n collapsedWidth = 56,\n mobileSidebarLabel = \"Navigation\",\n bodyHeader,\n bodyActions,\n bodySidebar,\n bodySplit = 24,\n children,\n className,\n headerClassName,\n toolbarClassName,\n sidebarClassName,\n bodyHeaderClassName,\n contentClassName,\n } = props;\n\n const hasSidebar = sidebar !== undefined || (navSections?.length ?? 0) > 0;\n const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false);\n\n const [collapsed, setCollapsed] = useState(\n () =>\n readStored(collapsedStorageKey) === \"true\" ||\n (readStored(collapsedStorageKey) === null && defaultCollapsed),\n );\n\n useEffect(() => {\n if (collapsedStorageKey && typeof window !== \"undefined\")\n window.localStorage.setItem(collapsedStorageKey, String(collapsed));\n }, [collapsed, collapsedStorageKey]);\n\n useEscapeLayer(mobileSidebarOpen, () => setMobileSidebarOpen(false));\n\n const railWidth = collapsed ? collapsedWidth : sidebarWidth;\n const compactActions = mobileActions ?? actions;\n const hasTopBar =\n (!hasSidebar && brand !== undefined) ||\n nav !== undefined ||\n search !== undefined ||\n actions !== undefined ||\n mobileActions !== undefined ||\n toolbar !== undefined;\n const hasMobileHeader = hasSidebar;\n const hasBodyHeader = bodyHeader !== undefined || bodyActions !== undefined;\n const renderSidebarContent = (collapsedValue: boolean, onNavigate?: () => void) =>\n typeof sidebar === \"function\"\n ? sidebar(collapsedValue)\n : sidebar !== undefined\n ? sidebar\n : navSections && (\n <NavSections\n sections={navSections}\n collapsed={collapsedValue}\n {...(onNavigate ? { onNavigate } : {})}\n />\n );\n\n return (\n <div className={cn(\"flex h-full min-h-0 w-full bg-background\", className)}>\n {hasSidebar && (\n <aside\n style={{ width: railWidth }}\n className={cn(\n \"hidden shrink-0 flex-col overflow-hidden border-r border-sidebar-border bg-sidebar text-sidebar-foreground transition-[width] duration-200 md:flex\",\n sidebarClassName,\n )}\n >\n <div\n className={cn(\n \"flex h-14 shrink-0 items-center border-b border-sidebar-border\",\n collapsed\n ? \"justify-center px-2\"\n : \"justify-between px-density-3\",\n )}\n >\n {!collapsed && brand && (\n <div className=\"flex min-w-0 items-center gap-2\">{brand}</div>\n )}\n {collapsible && (\n <button\n type=\"button\"\n onClick={() => setCollapsed((c) => !c)}\n aria-label={collapsed ? \"Expand sidebar\" : \"Collapse sidebar\"}\n title={collapsed ? \"Expand sidebar\" : \"Collapse sidebar\"}\n className=\"flex h-8 w-8 shrink-0 items-center justify-center rounded-md text-sidebar-foreground/70 transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground\"\n >\n <Icon icon={UiSidebar} className=\"h-4 w-4\" />\n </button>\n )}\n </div>\n {sidebarHeader && (\n <div className=\"shrink-0 border-b border-sidebar-border px-density-3 py-density-2\">\n {sidebarHeader}\n </div>\n )}\n <div className=\"min-h-0 flex-1 overflow-y-auto py-density-2\">\n {renderSidebarContent(collapsed)}\n </div>\n {sidebarFooter && (\n <div className=\"mt-auto shrink-0 border-t border-sidebar-border px-density-3 py-density-2\">\n {sidebarFooter}\n </div>\n )}\n </aside>\n )}\n\n <div className=\"flex min-h-0 min-w-0 flex-1 flex-col\">\n {(hasTopBar || hasMobileHeader) && (\n <header className=\"shrink-0 border-b border-border bg-card\">\n {hasMobileHeader && (\n <div className=\"flex flex-wrap items-center gap-density-2 px-density-3 py-density-2 md:hidden\">\n <button\n type=\"button\"\n aria-label={`Open ${mobileSidebarLabel}`}\n title={mobileSidebarLabel}\n className=\"inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-md border border-border bg-background text-foreground transition-colors hover:bg-accent\"\n onClick={() => setMobileSidebarOpen(true)}\n >\n <Icon icon={UiMenu} className=\"h-4 w-4\" />\n </button>\n {brand && (\n <div className=\"flex min-w-0 flex-1 items-center gap-density-2\">\n {brand}\n </div>\n )}\n {compactActions && (\n <div className=\"ml-auto flex min-w-0 shrink items-center justify-end gap-density-2\">\n {compactActions}\n </div>\n )}\n {search !== undefined && (\n <div className=\"basis-full min-w-0\">\n {search}\n </div>\n )}\n {nav && (\n <div className=\"basis-full min-w-0 overflow-x-auto\">\n {nav}\n </div>\n )}\n </div>\n )}\n <div\n className={cn(\n \"hidden h-14 items-center gap-density-3 px-density-4 md:flex\",\n !hasSidebar && \"flex\",\n headerClassName,\n )}\n >\n {!hasSidebar && brand && (\n <div className=\"flex shrink-0 items-center gap-density-2\">\n {brand}\n </div>\n )}\n {nav && <div className=\"flex shrink-0 items-center\">{nav}</div>}\n {search !== undefined && (\n <div className=\"flex min-w-0 flex-1 justify-center\">\n <div className=\"w-full\" style={{ maxWidth: searchMaxWidth }}>\n {search}\n </div>\n </div>\n )}\n {search === undefined && <div className=\"flex-1\" />}\n {actions && (\n <div className=\"flex shrink-0 items-center gap-density-2\">\n {actions}\n </div>\n )}\n </div>\n {toolbar && (\n <div\n className={cn(\n \"flex flex-wrap items-center gap-density-2 border-t border-border bg-muted px-density-3 py-density-2 md:px-density-4\",\n toolbarClassName,\n )}\n >\n {toolbar}\n </div>\n )}\n </header>\n )}\n\n {hasBodyHeader && (\n <div\n className={cn(\n \"flex shrink-0 flex-col items-stretch gap-density-2 border-b border-border bg-card px-density-3 py-density-2 md:flex-row md:items-start md:justify-between md:gap-density-3 md:px-density-4\",\n bodyHeaderClassName,\n )}\n >\n <div className=\"min-w-0 flex-1\">{bodyHeader}</div>\n {bodyActions && (\n <div className=\"flex shrink-0 flex-wrap items-center gap-density-2\">\n {bodyActions}\n </div>\n )}\n </div>\n )}\n\n {bodySidebar !== undefined ? (\n <>\n <div className=\"flex min-h-0 flex-1 flex-col overflow-hidden md:hidden\">\n <div className=\"max-h-[40vh] shrink-0 overflow-y-auto border-b border-border bg-background\">\n {bodySidebar}\n </div>\n <main\n className={cn(\n \"min-h-0 min-w-0 flex-1 overflow-auto\",\n contentClassName,\n )}\n >\n {children}\n </main>\n </div>\n <SplitPane\n className=\"hidden min-h-0 flex-1 md:flex\"\n defaultSplit={bodySplit}\n minLeft={12}\n minRight={30}\n left={bodySidebar}\n right={\n <div className={cn(\"h-full min-w-0\", contentClassName)}>\n {children}\n </div>\n }\n rightClass=\"overflow-y-auto\"\n />\n </>\n ) : (\n <main\n className={cn(\n \"min-h-0 min-w-0 flex-1 overflow-auto\",\n contentClassName,\n )}\n >\n {children}\n </main>\n )}\n </div>\n {hasSidebar && mobileSidebarOpen && (\n <div\n className=\"fixed inset-0 z-50 md:hidden\"\n role=\"presentation\"\n onClick={() => setMobileSidebarOpen(false)}\n >\n <div className=\"absolute inset-0 bg-black/40\" />\n <aside\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={mobileSidebarLabel}\n className={cn(\n \"relative z-10 flex h-full w-[min(20rem,85vw)] flex-col overflow-hidden border-r border-sidebar-border bg-sidebar text-sidebar-foreground shadow-xl\",\n sidebarClassName,\n )}\n onClick={(event) => event.stopPropagation()}\n >\n <div className=\"flex h-14 shrink-0 items-center justify-between border-b border-sidebar-border px-density-3\">\n {brand && <div className=\"flex min-w-0 items-center gap-2\">{brand}</div>}\n <button\n type=\"button\"\n aria-label={`Close ${mobileSidebarLabel}`}\n title=\"Close\"\n className=\"flex h-8 w-8 shrink-0 items-center justify-center rounded-md text-sidebar-foreground/70 transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground\"\n onClick={() => setMobileSidebarOpen(false)}\n >\n <Icon icon={UiClose} className=\"h-4 w-4\" />\n </button>\n </div>\n {sidebarHeader && (\n <div className=\"shrink-0 border-b border-sidebar-border px-density-3 py-density-2\">\n {sidebarHeader}\n </div>\n )}\n <div className=\"min-h-0 flex-1 overflow-y-auto py-density-2\">\n {renderSidebarContent(false, () => setMobileSidebarOpen(false))}\n </div>\n {sidebarFooter && (\n <div className=\"mt-auto shrink-0 border-t border-sidebar-border px-density-3 py-density-2\">\n {sidebarFooter}\n </div>\n )}\n </aside>\n </div>\n )}\n </div>\n );\n}\n\nfunction NavSections({\n sections,\n collapsed,\n onNavigate,\n}: {\n sections: AppShellNavSection[];\n collapsed: boolean;\n onNavigate?: () => void;\n}) {\n const { renderLink } = useRouter();\n return (\n <nav\n className={cn(\n \"flex flex-col gap-0.5\",\n collapsed ? \"px-2\" : \"px-density-2\",\n )}\n >\n {sections.map((section, i) => (\n <div key={section.label ?? `section-${i}`} className=\"flex flex-col\">\n {section.label &&\n (collapsed ? (\n <div className=\"mx-2 mb-1 mt-3 border-t border-sidebar-border first:mt-1\" />\n ) : (\n <div className=\"mb-0.5 mt-3 px-density-2 text-[10px] font-semibold uppercase tracking-[0.18em] text-sidebar-foreground/55 first:mt-1\">\n {section.label}\n </div>\n ))}\n {section.items.map((item) => (\n <NavItemRow\n key={item.key}\n item={item}\n collapsed={collapsed}\n renderLink={renderLink}\n {...(onNavigate ? { onNavigate } : {})}\n />\n ))}\n </div>\n ))}\n </nav>\n );\n}\n\nfunction NavItemRow({\n item,\n collapsed,\n renderLink,\n onNavigate,\n}: {\n item: AppShellNavItem;\n collapsed: boolean;\n renderLink: RenderLink;\n onNavigate?: () => void;\n}) {\n const className = cn(\n \"flex w-full items-center gap-2.5 rounded-md px-density-2 py-1.5 text-left text-[13px] text-sidebar-foreground transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground\",\n collapsed && \"justify-center px-0\",\n item.active && \"bg-sidebar-accent font-medium text-sidebar-primary\",\n );\n const inner = (\n <>\n {item.icon && (\n <span className=\"flex h-4 w-4 shrink-0 items-center justify-center\">\n <Icon\n {...(typeof item.icon === \"string\"\n ? { name: item.icon }\n : { icon: item.icon })}\n />\n </span>\n )}\n {!collapsed && <span className=\"flex-1 truncate\">{item.label}</span>}\n {!collapsed && item.badge}\n </>\n );\n const title =\n collapsed && typeof item.label === \"string\" ? item.label : undefined;\n\n if (item.external) {\n return (\n <a\n href={item.to}\n className={className}\n title={title}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n onClick={onNavigate}\n >\n {inner}\n </a>\n );\n }\n const link = renderLink({\n key: item.key,\n to: item.to,\n className,\n children: inner,\n ...(title ? { title } : {}),\n });\n\n if (!onNavigate) return link;\n return (\n <div onClick={onNavigate}>\n {link}\n </div>\n );\n}\n"],"names":["useState","useEffect","useEscapeLayer","jsx","cn","jsxs","Icon","UiSidebar","UiMenu","Fragment","SplitPane","UiClose","useRouter"],"mappings":"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"}
@@ -30,6 +30,8 @@ export type AppShellProps = {
30
30
  search?: ReactNode;
31
31
  /** Right-aligned cluster (icon buttons, settings/org picker, …). */
32
32
  actions?: ReactNode;
33
+ /** Compact action cluster used in the mobile header. Defaults to `actions`. */
34
+ mobileActions?: ReactNode;
33
35
  /** Optional second top-bar row for filters / bulk actions. */
34
36
  toolbar?: ReactNode;
35
37
  /** Max width of the centered search slot. Defaults to 28rem. */
@@ -1 +1 @@
1
- {"version":3,"file":"AppShell.d.ts","sourceRoot":"","sources":["../../src/layout/AppShell.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAE5D,OAAO,EAAQ,KAAK,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAc9D,MAAM,MAAM,eAAe,GAAG;IAC5B,sCAAsC;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,yDAAyD;IACzD,KAAK,EAAE,SAAS,CAAC;IACjB,6CAA6C;IAC7C,IAAI,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAAC;IACpC,mDAAmD;IACnD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,wEAAwE;IACxE,EAAE,EAAE,MAAM,CAAC;IACX,+BAA+B;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,8CAA8C;IAC9C,KAAK,CAAC,EAAE,SAAS,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,0EAA0E;IAC1E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,eAAe,EAAE,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAE1B,mGAAmG;IACnG,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,2EAA2E;IAC3E,GAAG,CAAC,EAAE,SAAS,CAAC;IAChB,+DAA+D;IAC/D,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,oEAAoE;IACpE,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,8DAA8D;IAC9D,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,gEAAgE;IAChE,cAAc,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAGjC,wEAAwE;IACxE,WAAW,CAAC,EAAE,kBAAkB,EAAE,CAAC;IACnC,kFAAkF;IAClF,OAAO,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,SAAS,EAAE,OAAO,KAAK,SAAS,CAAC,CAAC;IAC1D,8DAA8D;IAC9D,aAAa,CAAC,EAAE,SAAS,CAAC;IAC1B,2DAA2D;IAC3D,aAAa,CAAC,EAAE,SAAS,CAAC;IAC1B,yEAAyE;IACzE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,8CAA8C;IAC9C,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,uDAAuD;IACvD,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,kDAAkD;IAClD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kDAAkD;IAClD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mFAAmF;IACnF,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAG5B,6DAA6D;IAC7D,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,wFAAwF;IACxF,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uEAAuE;IACvE,QAAQ,EAAE,SAAS,CAAC;IAEpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAOF,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,2CAgT5C"}
1
+ {"version":3,"file":"AppShell.d.ts","sourceRoot":"","sources":["../../src/layout/AppShell.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAE5D,OAAO,EAAQ,KAAK,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAe9D,MAAM,MAAM,eAAe,GAAG;IAC5B,sCAAsC;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,yDAAyD;IACzD,KAAK,EAAE,SAAS,CAAC;IACjB,6CAA6C;IAC7C,IAAI,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAAC;IACpC,mDAAmD;IACnD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,wEAAwE;IACxE,EAAE,EAAE,MAAM,CAAC;IACX,+BAA+B;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,8CAA8C;IAC9C,KAAK,CAAC,EAAE,SAAS,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,0EAA0E;IAC1E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,eAAe,EAAE,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAE1B,mGAAmG;IACnG,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,2EAA2E;IAC3E,GAAG,CAAC,EAAE,SAAS,CAAC;IAChB,+DAA+D;IAC/D,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,oEAAoE;IACpE,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,+EAA+E;IAC/E,aAAa,CAAC,EAAE,SAAS,CAAC;IAC1B,8DAA8D;IAC9D,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,gEAAgE;IAChE,cAAc,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAGjC,wEAAwE;IACxE,WAAW,CAAC,EAAE,kBAAkB,EAAE,CAAC;IACnC,kFAAkF;IAClF,OAAO,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,SAAS,EAAE,OAAO,KAAK,SAAS,CAAC,CAAC;IAC1D,8DAA8D;IAC9D,aAAa,CAAC,EAAE,SAAS,CAAC;IAC1B,2DAA2D;IAC3D,aAAa,CAAC,EAAE,SAAS,CAAC;IAC1B,yEAAyE;IACzE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,8CAA8C;IAC9C,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,uDAAuD;IACvD,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,kDAAkD;IAClD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kDAAkD;IAClD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mFAAmF;IACnF,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAG5B,6DAA6D;IAC7D,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,wFAAwF;IACxF,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uEAAuE;IACvE,QAAQ,EAAE,SAAS,CAAC;IAEpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAOF,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,2CA4S5C"}
@@ -3,6 +3,7 @@ import { useState, useEffect } from "react";
3
3
  import { cn } from "../lib/utils.js";
4
4
  import { Icon } from "../data/Icon.js";
5
5
  import { useRouter } from "../rpc/router.js";
6
+ import { useEscapeLayer } from "../overlay/modalStack.js";
6
7
  import { SplitPane } from "./SplitPane.js";
7
8
  import { UiSidebar } from "../icons/components/UiSidebar.js";
8
9
  import { UiMenu } from "../icons/components/UiMenu.js";
@@ -17,6 +18,7 @@ function AppShell(props) {
17
18
  nav,
18
19
  search,
19
20
  actions,
21
+ mobileActions,
20
22
  toolbar,
21
23
  searchMaxWidth = "28rem",
22
24
  navSections,
@@ -50,16 +52,10 @@ function AppShell(props) {
50
52
  if (collapsedStorageKey && typeof window !== "undefined")
51
53
  window.localStorage.setItem(collapsedStorageKey, String(collapsed));
52
54
  }, [collapsed, collapsedStorageKey]);
53
- useEffect(() => {
54
- if (!mobileSidebarOpen) return;
55
- const onKeyDown = (event) => {
56
- if (event.key === "Escape") setMobileSidebarOpen(false);
57
- };
58
- document.addEventListener("keydown", onKeyDown);
59
- return () => document.removeEventListener("keydown", onKeyDown);
60
- }, [mobileSidebarOpen]);
55
+ useEscapeLayer(mobileSidebarOpen, () => setMobileSidebarOpen(false));
61
56
  const railWidth = collapsed ? collapsedWidth : sidebarWidth;
62
- const hasTopBar = !hasSidebar && brand !== void 0 || nav !== void 0 || search !== void 0 || actions !== void 0 || toolbar !== void 0;
57
+ const compactActions = mobileActions ?? actions;
58
+ const hasTopBar = !hasSidebar && brand !== void 0 || nav !== void 0 || search !== void 0 || actions !== void 0 || mobileActions !== void 0 || toolbar !== void 0;
63
59
  const hasMobileHeader = hasSidebar;
64
60
  const hasBodyHeader = bodyHeader !== void 0 || bodyActions !== void 0;
65
61
  const renderSidebarContent = (collapsedValue, onNavigate) => typeof sidebar === "function" ? sidebar(collapsedValue) : sidebar !== void 0 ? sidebar : navSections && /* @__PURE__ */ jsx(
@@ -124,7 +120,7 @@ function AppShell(props) {
124
120
  }
125
121
  ),
126
122
  brand && /* @__PURE__ */ jsx("div", { className: "flex min-w-0 flex-1 items-center gap-density-2", children: brand }),
127
- actions && /* @__PURE__ */ jsx("div", { className: "ml-auto flex shrink-0 items-center gap-density-2", children: actions }),
123
+ compactActions && /* @__PURE__ */ jsx("div", { className: "ml-auto flex min-w-0 shrink items-center justify-end gap-density-2", children: compactActions }),
128
124
  search !== void 0 && /* @__PURE__ */ jsx("div", { className: "basis-full min-w-0", children: search }),
129
125
  nav && /* @__PURE__ */ jsx("div", { className: "basis-full min-w-0 overflow-x-auto", children: nav })
130
126
  ] }),