@poodle64/ui 2026.10.3 → 2026.10.5

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 (46) hide show
  1. package/README.md +105 -7
  2. package/dist/components/ui/context-column/context-column.svelte +118 -21
  3. package/dist/components/ui/context-column/context-column.svelte.d.ts +24 -1
  4. package/dist/components/ui/document-pane/document-pane.svelte +136 -0
  5. package/dist/components/ui/document-pane/document-pane.svelte.d.ts +43 -0
  6. package/dist/components/ui/document-pane/index.d.ts +2 -0
  7. package/dist/components/ui/document-pane/index.js +2 -0
  8. package/dist/components/ui/ledger/head-cell.svelte +73 -0
  9. package/dist/components/ui/ledger/head-cell.svelte.d.ts +19 -0
  10. package/dist/components/ui/ledger/index.d.ts +1 -1
  11. package/dist/components/ui/ledger/ledger-columns.svelte +29 -21
  12. package/dist/components/ui/ledger/ledger-columns.svelte.d.ts +50 -13
  13. package/dist/components/ui/ledger/ledger.d.ts +6 -0
  14. package/dist/components/ui/ledger/ledger.js +9 -1
  15. package/dist/components/ui/ledger/ledger.svelte +65 -84
  16. package/dist/components/ui/ledger/ledger.svelte.d.ts +6 -1
  17. package/dist/components/ui/ledger/look.d.ts +32 -0
  18. package/dist/components/ui/ledger/look.js +32 -0
  19. package/dist/components/ui/ledger/types.d.ts +21 -11
  20. package/dist/components/ui/list-toolbar/icon-action.svelte +42 -0
  21. package/dist/components/ui/list-toolbar/icon-action.svelte.d.ts +7 -0
  22. package/dist/components/ui/list-toolbar/index.d.ts +3 -0
  23. package/dist/components/ui/list-toolbar/index.js +2 -0
  24. package/dist/components/ui/list-toolbar/list-toolbar.svelte +85 -0
  25. package/dist/components/ui/list-toolbar/list-toolbar.svelte.d.ts +32 -0
  26. package/dist/components/ui/list-toolbar/types.d.ts +18 -0
  27. package/dist/components/ui/list-toolbar/types.js +1 -0
  28. package/dist/components/ui/panel/panel.svelte +8 -1
  29. package/dist/components/ui/panel/panel.svelte.d.ts +6 -0
  30. package/dist/components/ui/record-list/index.d.ts +3 -0
  31. package/dist/components/ui/record-list/index.js +2 -0
  32. package/dist/components/ui/record-list/record-list.svelte +489 -0
  33. package/dist/components/ui/record-list/record-list.svelte.d.ts +89 -0
  34. package/dist/components/ui/record-list/thumbnail.svelte +32 -0
  35. package/dist/components/ui/record-list/thumbnail.svelte.d.ts +14 -0
  36. package/dist/components/ui/record-list/types.d.ts +40 -0
  37. package/dist/components/ui/record-list/types.js +1 -0
  38. package/dist/components/ui/record-switcher/index.d.ts +3 -0
  39. package/dist/components/ui/record-switcher/index.js +2 -0
  40. package/dist/components/ui/record-switcher/record-switcher.svelte +167 -0
  41. package/dist/components/ui/record-switcher/record-switcher.svelte.d.ts +28 -0
  42. package/dist/components/ui/record-switcher/types.d.ts +22 -0
  43. package/dist/components/ui/record-switcher/types.js +1 -0
  44. package/package.json +1 -1
  45. package/registry/component-map.json +34 -6
  46. package/registry/component-map.md +8 -4
@@ -45,30 +45,31 @@ export interface LedgerRow {
45
45
  } | null;
46
46
  }
47
47
  /**
48
- * A column the module offers. The keys `date`, `title`, `amount` and `balance`
49
- * are the ledger's own cells: list one to rename or resize it (and, for
50
- * `balance`, to offer the running balance at all, with `text` saying what it
51
- * counts). Date, title and amount always show, first and in that order; the
52
- * balance is always last. Every other key is the module's, in its order.
48
+ * A column a module offers a list in the ledger's look: the Ledger, and the
49
+ * RecordList for rows that are not money.
53
50
  */
54
- export interface LedgerColumn<R extends LedgerRow = LedgerRow> {
51
+ export interface ListColumn<R> {
55
52
  key: string;
56
53
  /** Its name in the Columns menu, and its head unless `head` is shorter. */
57
54
  label: string;
58
55
  head?: string;
59
- /** A grid track, or one for a narrow ledger and one for a wide one. */
56
+ /** A grid track, or one for a narrow list and one for a wide one (1200px and up). */
60
57
  width?: string | {
61
58
  narrow: string;
62
59
  wide: string;
63
60
  };
64
61
  align?: 'start' | 'end';
65
- /** Ticked until the viewer chooses. Defaults to true, and to false for `balance`. */
66
- on?: boolean;
62
+ /**
63
+ * Ticked until the viewer chooses. Defaults to true. `'wide'` is ticked
64
+ * only on a wide list, so extra width shows more columns and the viewer
65
+ * can still tick it back on a narrow one.
66
+ */
67
+ on?: boolean | 'wide';
67
68
  /**
68
69
  * What a click on the head sorts this column by: the real value, a number
69
70
  * rather than its display text. Without it a column with `text` sorts by
70
- * that text and a `cell`-only column does not sort. The running balance
71
- * never sorts, since it depends on the order.
71
+ * that text and a `cell`-only column does not sort. The ledger's running
72
+ * balance never sorts, since it depends on the order.
72
73
  */
73
74
  sort?: (row: R) => number | string | null | undefined;
74
75
  /** A plain cell. */
@@ -79,6 +80,15 @@ export interface LedgerColumn<R extends LedgerRow = LedgerRow> {
79
80
  */
80
81
  cell?: Snippet<[R]>;
81
82
  }
83
+ /**
84
+ * A column the module offers the ledger. The keys `date`, `title`, `amount`
85
+ * and `balance` are the ledger's own cells: list one to rename or resize it
86
+ * (and, for `balance`, to offer the running balance at all, with `text` saying
87
+ * what it counts). Date, title and amount always show, first and in that
88
+ * order; the balance is always last, and off until ticked. Every other key is
89
+ * the module's, in its order.
90
+ */
91
+ export type LedgerColumn<R extends LedgerRow = LedgerRow> = ListColumn<R>;
82
92
  /** The viewer's own choices, which the consumer persists. */
83
93
  export interface LedgerPreferences {
84
94
  /** The ticked columns' keys, `balance` included. */
@@ -0,0 +1,42 @@
1
+ <script lang="ts">
2
+ /** An icon button, or an icon link, named by its tooltip. Inside a `Tooltip.Provider`. */
3
+ import { mergeProps } from 'bits-ui';
4
+ import { buttonVariants } from '../button/index.js';
5
+ import * as Tooltip from '../tooltip/index.js';
6
+ import type { ListIconAction } from './types.js';
7
+
8
+ let { action }: { action: ListIconAction } = $props();
9
+ const Icon = $derived(action.icon);
10
+ const klass = buttonVariants({ variant: 'ghost', size: 'icon-sm' });
11
+
12
+ /** The trigger's own handlers (a click closes the tooltip) chained with the action's. */
13
+ const merged = (props: Record<string, unknown>) =>
14
+ mergeProps(props, { onclick: action.onclick, class: klass, 'aria-label': action.label });
15
+ /** A link takes no button `type`. */
16
+ function asLink(props: Record<string, unknown>) {
17
+ const { type: _type, ...rest } = merged(props);
18
+ return rest;
19
+ }
20
+ </script>
21
+
22
+ <Tooltip.Root>
23
+ <Tooltip.Trigger>
24
+ {#snippet child({ props })}
25
+ {#if action.href && !action.disabled}
26
+ <a
27
+ {...asLink(props)}
28
+ href={action.href}
29
+ download={action.download === true ? '' : action.download || undefined}
30
+ data-slot="list-action"
31
+ >
32
+ <Icon />
33
+ </a>
34
+ {:else}
35
+ <button {...merged(props)} type="button" disabled={action.disabled} data-slot="list-action">
36
+ <Icon />
37
+ </button>
38
+ {/if}
39
+ {/snippet}
40
+ </Tooltip.Trigger>
41
+ <Tooltip.Content side="bottom">{action.label}</Tooltip.Content>
42
+ </Tooltip.Root>
@@ -0,0 +1,7 @@
1
+ import type { ListIconAction } from './types.js';
2
+ type $$ComponentProps = {
3
+ action: ListIconAction;
4
+ };
5
+ declare const IconAction: import("svelte").Component<$$ComponentProps, {}, "">;
6
+ type IconAction = ReturnType<typeof IconAction>;
7
+ export default IconAction;
@@ -0,0 +1,3 @@
1
+ export { default as ListToolbar } from './list-toolbar.svelte';
2
+ export { default } from './list-toolbar.svelte';
3
+ export type { ListAction, ListIconAction } from './types.js';
@@ -0,0 +1,2 @@
1
+ export { default as ListToolbar } from './list-toolbar.svelte';
2
+ export { default } from './list-toolbar.svelte';
@@ -0,0 +1,85 @@
1
+ <script lang="ts">
2
+ /**
3
+ * The bar above a list, on the page ground: the list's title and count,
4
+ * then its icon actions (each named by its tooltip) and its one labelled
5
+ * action. The ledger draws its own with this, so every list's bar is the
6
+ * same bar.
7
+ *
8
+ * <ListToolbar title="Owned properties" meta="3 properties"
9
+ * tools={[{ label: 'Download as CSV', icon: Download, onclick: save }]}
10
+ * action={{ label: 'Add property', icon: Plus, href: '/property/new' }} />
11
+ */
12
+ import type { Snippet } from 'svelte';
13
+ import type { HTMLAttributes } from 'svelte/elements';
14
+ import Button from '../button/button.svelte';
15
+ import * as Tooltip from '../tooltip/index.js';
16
+ import IconAction from './icon-action.svelte';
17
+ import { cn } from '../../../utils.js';
18
+ import type { ListAction, ListIconAction } from './types.js';
19
+
20
+ let {
21
+ title,
22
+ meta,
23
+ leading,
24
+ children,
25
+ tools = [],
26
+ action,
27
+ label,
28
+ class: className,
29
+ ...restProps
30
+ }: Omit<HTMLAttributes<HTMLDivElement>, 'title'> & {
31
+ title?: string;
32
+ /** Beside the title, quieter: the count, or what the list is of. */
33
+ meta?: string;
34
+ /** Before the title: a switch between views of the same list. */
35
+ leading?: Snippet;
36
+ /** The list's own controls, before its icon actions: a menu, a filter. */
37
+ children?: Snippet;
38
+ /** Icon actions, each named by its tooltip. */
39
+ tools?: ListIconAction[];
40
+ /** The one labelled action, last. */
41
+ action?: ListAction;
42
+ /** The toolbar's accessible name. Defaults to the title. */
43
+ label?: string;
44
+ class?: string;
45
+ } = $props();
46
+
47
+ const ActionIcon = $derived(action?.icon);
48
+ </script>
49
+
50
+ <div
51
+ role="toolbar"
52
+ aria-label={label ?? title}
53
+ class={cn('flex min-h-11 flex-none items-center gap-2 px-2 pb-2', className)}
54
+ data-slot="list-toolbar"
55
+ {...restProps}
56
+ >
57
+ {@render leading?.()}
58
+ <!-- Crowded, the count gives way before the title, and the title ends in an ellipsis. -->
59
+ <div class="flex min-w-0 items-baseline gap-2.5 whitespace-nowrap">
60
+ {#if title}<h2 class="text-body min-w-0 truncate font-semibold">{title}</h2>{/if}
61
+ {#if meta}<span class="text-muted-foreground min-w-0 shrink-[100] truncate text-sm">{meta}</span>{/if}
62
+ </div>
63
+ <span class="min-w-0 flex-1"></span>
64
+ {@render children?.()}
65
+ {#if tools.length}
66
+ <Tooltip.Provider delayDuration={150}>
67
+ {#each tools as tool (tool.label)}
68
+ <IconAction action={tool} />
69
+ {/each}
70
+ </Tooltip.Provider>
71
+ {/if}
72
+ {#if action}
73
+ <Button
74
+ variant="outline"
75
+ size="sm"
76
+ href={action.href}
77
+ download={action.href ? (action.download === true ? '' : action.download || undefined) : undefined}
78
+ disabled={action.disabled}
79
+ onclick={action.onclick}
80
+ data-slot="list-toolbar-action"
81
+ >
82
+ {#if ActionIcon}<ActionIcon />{/if}{action.label}
83
+ </Button>
84
+ {/if}
85
+ </div>
@@ -0,0 +1,32 @@
1
+ /**
2
+ * The bar above a list, on the page ground: the list's title and count,
3
+ * then its icon actions (each named by its tooltip) and its one labelled
4
+ * action. The ledger draws its own with this, so every list's bar is the
5
+ * same bar.
6
+ *
7
+ * <ListToolbar title="Owned properties" meta="3 properties"
8
+ * tools={[{ label: 'Download as CSV', icon: Download, onclick: save }]}
9
+ * action={{ label: 'Add property', icon: Plus, href: '/property/new' }} />
10
+ */
11
+ import type { Snippet } from 'svelte';
12
+ import type { HTMLAttributes } from 'svelte/elements';
13
+ import type { ListAction, ListIconAction } from './types.js';
14
+ type $$ComponentProps = Omit<HTMLAttributes<HTMLDivElement>, 'title'> & {
15
+ title?: string;
16
+ /** Beside the title, quieter: the count, or what the list is of. */
17
+ meta?: string;
18
+ /** Before the title: a switch between views of the same list. */
19
+ leading?: Snippet;
20
+ /** The list's own controls, before its icon actions: a menu, a filter. */
21
+ children?: Snippet;
22
+ /** Icon actions, each named by its tooltip. */
23
+ tools?: ListIconAction[];
24
+ /** The one labelled action, last. */
25
+ action?: ListAction;
26
+ /** The toolbar's accessible name. Defaults to the title. */
27
+ label?: string;
28
+ class?: string;
29
+ };
30
+ declare const ListToolbar: import("svelte").Component<$$ComponentProps, {}, "">;
31
+ type ListToolbar = ReturnType<typeof ListToolbar>;
32
+ export default ListToolbar;
@@ -0,0 +1,18 @@
1
+ import type { IconComponent } from '../app-shell/types.js';
2
+ /**
3
+ * One action a list or a pane offers: a button, or a link when it has `href`.
4
+ * As an icon action its `label` is the tooltip and the accessible name.
5
+ */
6
+ export interface ListAction {
7
+ label: string;
8
+ icon?: IconComponent;
9
+ onclick?: (event: MouseEvent) => void;
10
+ href?: string;
11
+ /** On a link, saves the target rather than opening it; a string names the file. */
12
+ download?: boolean | string;
13
+ disabled?: boolean;
14
+ }
15
+ /** An action drawn as its icon alone. */
16
+ export type ListIconAction = ListAction & {
17
+ icon: IconComponent;
18
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -25,6 +25,7 @@
25
25
  tone = 'default',
26
26
  children,
27
27
  pad = true,
28
+ scroll = false,
28
29
  class: klass = '',
29
30
  ref = $bindable(null),
30
31
  ...restProps
@@ -70,6 +71,12 @@
70
71
  tone?: 'default' | 'destructive';
71
72
  children: Snippet;
72
73
  pad?: boolean;
74
+ /**
75
+ * The body scrolls inside the card, under a header that stays: the card
76
+ * is as tall as what it holds up to the height its container gives it,
77
+ * and the full height when given one (`h-full`).
78
+ */
79
+ scroll?: boolean;
73
80
  } = $props();
74
81
 
75
82
  const destructive = $derived(tone === 'destructive');
@@ -124,7 +131,7 @@
124
131
  {/if}
125
132
  </header>
126
133
  {/if}
127
- <div class={pad ? 'p-4' : ''}>
134
+ <div class={cn(pad && 'p-4', scroll && 'min-h-0 flex-1 overflow-y-auto')} data-slot="panel-body">
128
135
  {@render children()}
129
136
  </div>
130
137
  {#if footer}
@@ -45,6 +45,12 @@ type $$ComponentProps = WithElementRef<HTMLAttributes<HTMLElement>> & {
45
45
  tone?: 'default' | 'destructive';
46
46
  children: Snippet;
47
47
  pad?: boolean;
48
+ /**
49
+ * The body scrolls inside the card, under a header that stays: the card
50
+ * is as tall as what it holds up to the height its container gives it,
51
+ * and the full height when given one (`h-full`).
52
+ */
53
+ scroll?: boolean;
48
54
  };
49
55
  declare const Panel: Component<$$ComponentProps, {}, "ref">;
50
56
  type Panel = ReturnType<typeof Panel>;
@@ -0,0 +1,3 @@
1
+ export { default as RecordList } from './record-list.svelte';
2
+ export { default } from './record-list.svelte';
3
+ export type { RecordFlag, RecordListColumn, RecordListPreferences, RecordListRow } from './types.js';
@@ -0,0 +1,2 @@
1
+ export { default as RecordList } from './record-list.svelte';
2
+ export { default } from './record-list.svelte';