@close-by/clay 0.2.4 → 0.2.6

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@close-by/clay",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
4
4
  "description": "NiceGUI-like ui.* facade for Clay apps",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -27,10 +27,10 @@
27
27
  "access": "public"
28
28
  },
29
29
  "dependencies": {
30
- "@close-by/clay-compiler": "0.2.4",
31
- "@close-by/clay-core": "0.2.4",
32
- "@close-by/clay-components": "0.2.4",
33
- "@close-by/clay-server": "0.2.4",
30
+ "@close-by/clay-compiler": "0.2.6",
31
+ "@close-by/clay-core": "0.2.6",
32
+ "@close-by/clay-components": "0.2.6",
33
+ "@close-by/clay-server": "0.2.6",
34
34
  "@tailwindcss/cli": "^4.3.3",
35
35
  "tailwindcss": "^4.3.3"
36
36
  }
package/src/index.ts CHANGED
@@ -9,6 +9,9 @@ import {
9
9
  radioGroup as radioGroupFactory,
10
10
  combobox as comboboxFactory,
11
11
  date as dateFactory,
12
+ dateRange as dateRangeFactory,
13
+ timeline as timelineFactory,
14
+ stepper as stepperFactory,
12
15
  tooltip as tooltipFactory,
13
16
  avatar as avatarFactory,
14
17
  skeleton as skeletonFactory,
@@ -34,6 +37,7 @@ import {
34
37
  radialChart as radialChartFactory,
35
38
  scatterChart as scatterChartFactory,
36
39
  composedChart as composedChartFactory,
40
+ sparkline as sparklineFactory,
37
41
  row as rowFactory,
38
42
  column as columnFactory,
39
43
  container as containerFactory,
@@ -101,6 +105,14 @@ import {
101
105
  type RadioGroupProps,
102
106
  type ComboboxProps,
103
107
  type DateProps,
108
+ type DateRangeProps,
109
+ type DateRangePreset,
110
+ type TimelineProps,
111
+ type TimelineItem,
112
+ type TimelineItemStatus,
113
+ type StepperProps,
114
+ type StepperStepOptions,
115
+ type StepperStepStatus,
104
116
  type TooltipProps,
105
117
  type AvatarProps,
106
118
  type SkeletonProps,
@@ -152,6 +164,11 @@ import {
152
164
  type ComposedSeriesType,
153
165
  type CartesianChartProps,
154
166
  type ChartSeries,
167
+ type ChartHeadline,
168
+ type ChartPeriod,
169
+ type ChartReferenceLine,
170
+ type ChartReferenceArea,
171
+ type SparklineProps,
155
172
  type CardProps,
156
173
  type DialogProps,
157
174
  type DialogStackProps,
@@ -318,6 +335,7 @@ import {
318
335
  TableBuilder,
319
336
  DialogElement,
320
337
  DialogStackElement,
338
+ StepperElement,
321
339
  AlertDialogElement,
322
340
  DropdownMenuElement,
323
341
  ContextMenuElement,
@@ -446,6 +464,14 @@ export type {
446
464
  DialogProps,
447
465
  DialogStackProps,
448
466
  DialogStackStepOptions,
467
+ StepperProps,
468
+ StepperStepOptions,
469
+ StepperStepStatus,
470
+ TimelineProps,
471
+ TimelineItem,
472
+ TimelineItemStatus,
473
+ DateRangeProps,
474
+ DateRangePreset,
449
475
  AlertDialogProps,
450
476
  DropdownMenuProps,
451
477
  DropdownItemOptions,
@@ -600,6 +626,7 @@ export {
600
626
  TableBuilder,
601
627
  DialogElement,
602
628
  DialogStackElement,
629
+ StepperElement,
603
630
  AlertDialogElement,
604
631
  DropdownMenuElement,
605
632
  SheetElement,
@@ -680,6 +707,29 @@ export function date(props?: DateProps): Element {
680
707
  return dateFactory(props);
681
708
  }
682
709
 
710
+ /** Date range picker with presets. Prefer `ui.dateRange`. See {@link DateRangeProps}. */
711
+ export function dateRange(props?: DateRangeProps): Element {
712
+ return dateRangeFactory(props);
713
+ }
714
+
715
+ /** Chronological events for feeds and deploy logs. See {@link TimelineProps}. */
716
+ export function timeline(props: TimelineProps): Element {
717
+ return timelineFactory(props);
718
+ }
719
+
720
+ /** Inline multi-step flow with step panels. See {@link StepperProps}. */
721
+ export function stepper(
722
+ fn: (s: StepperElement) => void,
723
+ props?: StepperProps,
724
+ ): StepperElement;
725
+ export function stepper(
726
+ props: StepperProps,
727
+ fn: (s: StepperElement) => void,
728
+ ): StepperElement;
729
+ export function stepper(a: any, b?: any): StepperElement {
730
+ return stepperFactory(a, b);
731
+ }
732
+
683
733
  /** Tooltip around content (`fn` or props-first). Prefer `ui.tooltip`. See {@link TooltipProps}. */
684
734
  export function tooltip(fn: () => void, props: TooltipProps): Element;
685
735
  export function tooltip(props: TooltipProps, fn: () => void): Element;
@@ -970,6 +1020,11 @@ export function composedChart(props: ComposedChartProps): Element {
970
1020
  return composedChartFactory(props);
971
1021
  }
972
1022
 
1023
+ /** KPI card with inline sparkline. Prefer `ui.sparkline`. See {@link SparklineProps}. */
1024
+ export function sparkline(props: SparklineProps): Element {
1025
+ return sparklineFactory(props);
1026
+ }
1027
+
973
1028
  /** Horizontal flex row (`fn` or props-first). Prefer `ui.row`. */
974
1029
  export function row(fn: () => void, props?: Parameters<typeof rowFactory>[1]): Element;
975
1030
  export function row(props: Parameters<typeof rowFactory>[0], fn: () => void): Element;
@@ -1522,6 +1577,9 @@ export const ui = {
1522
1577
  radioGroup,
1523
1578
  combobox,
1524
1579
  date,
1580
+ dateRange,
1581
+ timeline,
1582
+ stepper,
1525
1583
  tooltip,
1526
1584
  avatar,
1527
1585
  skeleton,
@@ -1568,6 +1626,7 @@ export const ui = {
1568
1626
  radialChart,
1569
1627
  scatterChart,
1570
1628
  composedChart,
1629
+ sparkline,
1571
1630
  row,
1572
1631
  column,
1573
1632
  container,
package/src/pages.ts CHANGED
@@ -58,6 +58,10 @@ export type PageMeta = {
58
58
  * UX only — still call `requireRole` in the page builder.
59
59
  */
60
60
  roles?: string[];
61
+ /** Nest this page under a collapsible sidebar group with the given label. */
62
+ group?: string;
63
+ /** Lucide icon for the group parent row (first page in the group with this set wins). */
64
+ groupIcon?: string;
61
65
  };
62
66
 
63
67
  const pageMetaByPath = new Map<string, PageMeta>();
@@ -140,8 +144,76 @@ export type NavFromPagesOptions = {
140
144
  role?: string;
141
145
  /** Viewer roles — page is shown if it has no `roles` meta, or any overlap. */
142
146
  roles?: string[];
147
+ /**
148
+ * When true, nest `/examples/*` routes (except `/examples/auth`) under an
149
+ * "Examples" group. Individual pages can override with `pageMeta.group`.
150
+ */
151
+ groupExamples?: boolean;
152
+ /** Label for the auto examples group. Default `"Examples"`. */
153
+ groupExamplesLabel?: string;
154
+ /** Icon for the auto examples group. Default `"layout-grid"`. */
155
+ groupExamplesIcon?: string;
143
156
  };
144
157
 
158
+ type RankedNavPage = {
159
+ href: string;
160
+ label: string;
161
+ icon: string;
162
+ order: number;
163
+ group?: string;
164
+ groupIcon?: string;
165
+ };
166
+
167
+ function resolvePageGroup(
168
+ href: string,
169
+ meta: PageMeta,
170
+ opts?: NavFromPagesOptions,
171
+ ): { group?: string; groupIcon?: string } {
172
+ if (meta.group !== undefined) {
173
+ return meta.group ? { group: meta.group, groupIcon: meta.groupIcon } : {};
174
+ }
175
+ if (opts?.groupExamples && href.startsWith('/examples/') && href !== '/examples/auth') {
176
+ return {
177
+ group: opts.groupExamplesLabel ?? 'Examples',
178
+ groupIcon: opts.groupExamplesIcon ?? 'layout-grid',
179
+ };
180
+ }
181
+ return {};
182
+ }
183
+
184
+ function buildNavFromRanked(ranked: RankedNavPage[]): AppNavItem[] {
185
+ const top: Array<{ order: number; node: AppNavItem }> = [];
186
+ const groups = new Map<string, AppNavItem>();
187
+
188
+ for (const row of ranked) {
189
+ const leaf: AppNavItem = { label: row.label, href: row.href, icon: row.icon };
190
+ if (!row.group) {
191
+ top.push({ order: row.order, node: leaf });
192
+ continue;
193
+ }
194
+
195
+ let parent = groups.get(row.group);
196
+ if (!parent) {
197
+ parent = {
198
+ label: row.group,
199
+ href: row.href,
200
+ icon: row.groupIcon ?? 'folder',
201
+ items: [],
202
+ };
203
+ groups.set(row.group, parent);
204
+ top.push({ order: row.order, node: parent });
205
+ } else {
206
+ const entry = top.find((t) => t.node === parent);
207
+ if (entry) entry.order = Math.min(entry.order, row.order);
208
+ if (row.groupIcon && parent.icon === 'folder') parent.icon = row.groupIcon;
209
+ }
210
+ parent.items!.push(leaf);
211
+ }
212
+
213
+ top.sort((a, b) => a.order - b.order || a.node.label.localeCompare(b.node.label));
214
+ return top.map((t) => t.node);
215
+ }
216
+
145
217
  /**
146
218
  * Build primary sidebar nav from registered pages + collected `pageMeta`.
147
219
  * Filters by `nav: false` and optional role overlap; sorts by `order` then path.
@@ -160,10 +232,11 @@ export function navFromPages(opts?: NavFromPagesOptions): AppNavItem[] {
160
232
  }
161
233
 
162
234
  const paths = getRegisteredPaths();
163
- const ranked = paths
235
+ const ranked: RankedNavPage[] = paths
164
236
  .map((href) => {
165
237
  const meta = pageMetaByPath.get(href) ?? {};
166
238
  const order = href === '/' ? 0 : (meta.order ?? 100);
239
+ const { group, groupIcon } = resolvePageGroup(href, meta, opts);
167
240
  return {
168
241
  href,
169
242
  label: meta.label ?? labelFromPath(href),
@@ -171,6 +244,8 @@ export function navFromPages(opts?: NavFromPagesOptions): AppNavItem[] {
171
244
  order,
172
245
  nav: meta.nav !== false,
173
246
  roles: meta.roles,
247
+ group,
248
+ groupIcon,
174
249
  };
175
250
  })
176
251
  .filter((item) => {
@@ -178,9 +253,10 @@ export function navFromPages(opts?: NavFromPagesOptions): AppNavItem[] {
178
253
  if (!item.roles || item.roles.length === 0) return true;
179
254
  if (viewer.size === 0) return false;
180
255
  return item.roles.some((r) => viewer.has(r));
181
- });
256
+ })
257
+ .map(({ nav: _nav, roles: _roles, ...rest }) => rest);
182
258
  ranked.sort((a, b) => a.order - b.order || a.href.localeCompare(b.href));
183
- return ranked.map(({ href, label, icon }) => ({ href, label, icon }));
259
+ return buildNavFromRanked(ranked);
184
260
  }
185
261
 
186
262
  /** Test helper: clear page registry + meta map. */