@poodle64/ui 2026.10.0 → 2026.10.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.
- package/README.md +79 -1
- package/dist/components/ui/ledger/index.d.ts +3 -0
- package/dist/components/ui/ledger/index.js +2 -0
- package/dist/components/ui/ledger/ledger-columns.svelte +107 -0
- package/dist/components/ui/ledger/ledger-columns.svelte.d.ts +15 -0
- package/dist/components/ui/ledger/ledger.d.ts +77 -0
- package/dist/components/ui/ledger/ledger.js +221 -0
- package/dist/components/ui/ledger/ledger.svelte +905 -0
- package/dist/components/ui/ledger/ledger.svelte.d.ts +78 -0
- package/dist/components/ui/ledger/types.d.ts +79 -0
- package/dist/components/ui/ledger/types.js +1 -0
- package/dist/format.d.ts +8 -0
- package/dist/format.js +9 -3
- package/package.json +2 -1
- package/registry/component-map.json +17 -3
- package/registry/component-map.md +9 -1
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The household ledger: one component for every money list, read the same
|
|
3
|
+
* way in every module. The consumer brings its rows and the columns it
|
|
4
|
+
* offers; the ledger owns the look (PocketSmith's: a head card, one card per
|
|
5
|
+
* period, the group's label on the page ground), the grouping and the
|
|
6
|
+
* viewer's toggles, which it emits for the consumer to keep.
|
|
7
|
+
*
|
|
8
|
+
* It fills its parent's height and scrolls its own rows, so the head card
|
|
9
|
+
* stays at the top: give it a bounded parent (`flex-1 min-h-0` in a
|
|
10
|
+
* full-height page). Width picks the layout: a phone's list below 600px, the
|
|
11
|
+
* wide column widths from 1200px.
|
|
12
|
+
*/
|
|
13
|
+
import type { Snippet } from 'svelte';
|
|
14
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
15
|
+
import type { LedgerColumn, LedgerOpenContext, LedgerPreferences, LedgerRow } from './types.js';
|
|
16
|
+
declare function $$render<R extends LedgerRow>(): {
|
|
17
|
+
props: Omit<HTMLAttributes<HTMLDivElement>, "title"> & {
|
|
18
|
+
rows: readonly R[];
|
|
19
|
+
/** The module's columns, in order after the amount. See `LedgerColumn`. */
|
|
20
|
+
columns?: LedgerColumn<R>[];
|
|
21
|
+
/** The viewer's columns and period. Missing fields take the module's defaults. */
|
|
22
|
+
preferences?: Partial<LedgerPreferences>;
|
|
23
|
+
onPreferencesChange?: (next: LedgerPreferences) => void;
|
|
24
|
+
/** The Columns menu's footnote: where the choice is kept. */
|
|
25
|
+
preferencesNote?: string;
|
|
26
|
+
/** The month a financial year starts, 1–12. July by default. */
|
|
27
|
+
fyStart?: number;
|
|
28
|
+
currency?: string;
|
|
29
|
+
/** BCP 47 locale for every figure: amounts, nets and counts. */
|
|
30
|
+
locale?: string;
|
|
31
|
+
/** What a row is called, one and many: a printed layer's "line". */
|
|
32
|
+
noun?: [string, string];
|
|
33
|
+
title?: string;
|
|
34
|
+
/** Beside the title. Defaults to the row count. */
|
|
35
|
+
meta?: string;
|
|
36
|
+
/** The toolbar's own actions, after the Columns menu: export, add. */
|
|
37
|
+
actions?: Snippet;
|
|
38
|
+
/** The ticked rows' ids. Ticking needs `bulkActions`. */
|
|
39
|
+
selected?: R["id"][];
|
|
40
|
+
/** What the ticked rows allow, in the bar that replaces the toolbar. */
|
|
41
|
+
bulkActions?: Snippet<[R[]]>;
|
|
42
|
+
/** The row opened in place. Opening needs `editor`. */
|
|
43
|
+
open?: R["id"] | null;
|
|
44
|
+
onOpenChange?: (id: R["id"] | null) => void;
|
|
45
|
+
/** The opened row's content. */
|
|
46
|
+
editor?: Snippet<[R, LedgerOpenContext]>;
|
|
47
|
+
/** Under a synthetic row's statement lines: split, join, undo. */
|
|
48
|
+
originActions?: Snippet<[R]>;
|
|
49
|
+
/** Makes the paperclip a button. */
|
|
50
|
+
onAttachment?: (row: R) => void;
|
|
51
|
+
empty?: Snippet;
|
|
52
|
+
/** After the last group, inside the scroll: "Show more". */
|
|
53
|
+
footer?: Snippet;
|
|
54
|
+
layout?: "auto" | "phone" | "narrow" | "wide";
|
|
55
|
+
class?: string;
|
|
56
|
+
};
|
|
57
|
+
exports: {};
|
|
58
|
+
bindings: "open" | "selected" | "preferences";
|
|
59
|
+
slots: {};
|
|
60
|
+
events: {};
|
|
61
|
+
};
|
|
62
|
+
declare class __sveltets_Render<R extends LedgerRow> {
|
|
63
|
+
props(): ReturnType<typeof $$render<R>>['props'];
|
|
64
|
+
events(): ReturnType<typeof $$render<R>>['events'];
|
|
65
|
+
slots(): ReturnType<typeof $$render<R>>['slots'];
|
|
66
|
+
bindings(): "open" | "selected" | "preferences";
|
|
67
|
+
exports(): {};
|
|
68
|
+
}
|
|
69
|
+
interface $$IsomorphicComponent {
|
|
70
|
+
new <R extends LedgerRow>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<R>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<R>['props']>, ReturnType<__sveltets_Render<R>['events']>, ReturnType<__sveltets_Render<R>['slots']>> & {
|
|
71
|
+
$$bindings?: ReturnType<__sveltets_Render<R>['bindings']>;
|
|
72
|
+
} & ReturnType<__sveltets_Render<R>['exports']>;
|
|
73
|
+
<R extends LedgerRow>(internal: unknown, props: ReturnType<__sveltets_Render<R>['props']> & {}): ReturnType<__sveltets_Render<R>['exports']>;
|
|
74
|
+
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
75
|
+
}
|
|
76
|
+
declare const Ledger: $$IsomorphicComponent;
|
|
77
|
+
type Ledger<R extends LedgerRow> = InstanceType<typeof Ledger<R>>;
|
|
78
|
+
export default Ledger;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { Status } from '../status/index.js';
|
|
3
|
+
/** How the viewer groups rows. `fy` honours the ledger's `fyStart`. */
|
|
4
|
+
export type LedgerPeriod = 'fy' | 'cy' | 'month' | 'week' | 'day' | 'none';
|
|
5
|
+
/** One statement line a synthetic row (a split, a join) was made from. */
|
|
6
|
+
export interface LedgerOrigin {
|
|
7
|
+
/** ISO `YYYY-MM-DD`. */
|
|
8
|
+
date: string;
|
|
9
|
+
/** The line as the statement printed it. */
|
|
10
|
+
description: string;
|
|
11
|
+
/** Where it was printed: "Statement 23, line 14". */
|
|
12
|
+
source?: string;
|
|
13
|
+
amount: number | string;
|
|
14
|
+
}
|
|
15
|
+
/** What the ledger reads off a row. A consumer extends it with its own fields for its columns. */
|
|
16
|
+
export interface LedgerRow {
|
|
17
|
+
id: string | number;
|
|
18
|
+
/** ISO `YYYY-MM-DD`. */
|
|
19
|
+
date: string;
|
|
20
|
+
/** Line one: the counterparty the money moved with, the asset, the security. */
|
|
21
|
+
title: string;
|
|
22
|
+
/** Line two, under the title. */
|
|
23
|
+
note?: string | null;
|
|
24
|
+
/** Signed: money in is positive, money out negative. */
|
|
25
|
+
amount: number | string;
|
|
26
|
+
/** What the running balance counts after this row: money, a coin, units. */
|
|
27
|
+
balance?: number | string | null;
|
|
28
|
+
/** A dot at the row's left edge. `true` is a warning. */
|
|
29
|
+
review?: boolean | Status;
|
|
30
|
+
/** The attachment's name: a paperclip after the title, the name on hover. */
|
|
31
|
+
attachment?: string | null;
|
|
32
|
+
/** A row not as printed: the statement lines it was made from. */
|
|
33
|
+
origins?: {
|
|
34
|
+
summary?: string;
|
|
35
|
+
lines: LedgerOrigin[];
|
|
36
|
+
} | null;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* A column the module offers. The keys `date`, `title`, `amount` and `balance`
|
|
40
|
+
* are the ledger's own cells: list one to rename or resize it (and, for
|
|
41
|
+
* `balance`, to offer the running balance at all, with `text` saying what it
|
|
42
|
+
* counts). Date, title and amount always show, first and in that order; the
|
|
43
|
+
* balance is always last. Every other key is the module's, in its order.
|
|
44
|
+
*/
|
|
45
|
+
export interface LedgerColumn<R extends LedgerRow = LedgerRow> {
|
|
46
|
+
key: string;
|
|
47
|
+
/** Its name in the Columns menu, and its head unless `head` is shorter. */
|
|
48
|
+
label: string;
|
|
49
|
+
head?: string;
|
|
50
|
+
/** A grid track, or one for a narrow ledger and one for a wide one. */
|
|
51
|
+
width?: string | {
|
|
52
|
+
narrow: string;
|
|
53
|
+
wide: string;
|
|
54
|
+
};
|
|
55
|
+
align?: 'start' | 'end';
|
|
56
|
+
/** Ticked until the viewer chooses. Defaults to true, and to false for `balance`. */
|
|
57
|
+
on?: boolean;
|
|
58
|
+
/** A plain cell. */
|
|
59
|
+
text?: (row: R) => string;
|
|
60
|
+
/**
|
|
61
|
+
* A cell of the module's own markup. On a row that opens, a click passes
|
|
62
|
+
* through the cell to the row; a control inside takes `pointer-events-auto`.
|
|
63
|
+
*/
|
|
64
|
+
cell?: Snippet<[R]>;
|
|
65
|
+
}
|
|
66
|
+
/** The viewer's own choices, which the consumer persists. */
|
|
67
|
+
export interface LedgerPreferences {
|
|
68
|
+
/** The ticked columns' keys, `balance` included. */
|
|
69
|
+
columns: string[];
|
|
70
|
+
period: LedgerPeriod;
|
|
71
|
+
}
|
|
72
|
+
/** What an opened row's content gets, to line its fields up under the columns. */
|
|
73
|
+
export interface LedgerOpenContext {
|
|
74
|
+
close: () => void;
|
|
75
|
+
/** The row's `grid-template-columns`, gutter first. */
|
|
76
|
+
template: string;
|
|
77
|
+
/** A column's grid line, 1-based with the gutter at 1; 0 when it is not shown. */
|
|
78
|
+
column: (key: string) => number;
|
|
79
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/format.d.ts
CHANGED
|
@@ -61,6 +61,13 @@ export interface CurrencyOptions {
|
|
|
61
61
|
* must not confuse the two.
|
|
62
62
|
*/
|
|
63
63
|
currency?: string;
|
|
64
|
+
/**
|
|
65
|
+
* How a negative reads. `sign` (the default) is `-$500`; `brackets` is the
|
|
66
|
+
* accountant's `($500)`, which a ledger's amount column wants.
|
|
67
|
+
*/
|
|
68
|
+
negative?: 'sign' | 'brackets';
|
|
69
|
+
/** BCP 47 locale for the digits and separators. Defaults to `AU_LOCALE`. */
|
|
70
|
+
locale?: string;
|
|
64
71
|
}
|
|
65
72
|
/**
|
|
66
73
|
* Format a DOLLAR value as currency.
|
|
@@ -69,6 +76,7 @@ export interface CurrencyOptions {
|
|
|
69
76
|
* formatCurrency(1234.56) // '$1,234.56'
|
|
70
77
|
* formatCurrency('1234.56', { decimals: 0 }) // '$1,235'
|
|
71
78
|
* formatCurrency(-500, { decimals: 0 }) // '-$500'
|
|
79
|
+
* formatCurrency(-500, { negative: 'brackets' }) // '($500.00)'
|
|
72
80
|
* formatCurrency(null) // 'N/A'
|
|
73
81
|
*/
|
|
74
82
|
export declare function formatCurrency(value: string | number | null | undefined, options?: CurrencyOptions): string;
|
package/dist/format.js
CHANGED
|
@@ -72,19 +72,25 @@ function toFiniteNumber(value) {
|
|
|
72
72
|
* formatCurrency(1234.56) // '$1,234.56'
|
|
73
73
|
* formatCurrency('1234.56', { decimals: 0 }) // '$1,235'
|
|
74
74
|
* formatCurrency(-500, { decimals: 0 }) // '-$500'
|
|
75
|
+
* formatCurrency(-500, { negative: 'brackets' }) // '($500.00)'
|
|
75
76
|
* formatCurrency(null) // 'N/A'
|
|
76
77
|
*/
|
|
77
78
|
export function formatCurrency(value, options = {}) {
|
|
78
|
-
const { decimals = 2, fallback = FALLBACK, currency = 'AUD' } = options;
|
|
79
|
+
const { decimals = 2, fallback = FALLBACK, currency = 'AUD', negative = 'sign', locale = AU_LOCALE } = options;
|
|
79
80
|
const num = toFiniteNumber(value);
|
|
80
81
|
if (num === null)
|
|
81
82
|
return fallback;
|
|
82
|
-
|
|
83
|
+
const format = new Intl.NumberFormat(locale, {
|
|
83
84
|
style: 'currency',
|
|
84
85
|
currency,
|
|
85
86
|
minimumFractionDigits: decimals,
|
|
86
87
|
maximumFractionDigits: decimals
|
|
87
|
-
})
|
|
88
|
+
});
|
|
89
|
+
if (negative !== 'brackets' || num >= 0)
|
|
90
|
+
return format.format(num);
|
|
91
|
+
// A negative that rounds to nothing is not money out: no brackets around a zero.
|
|
92
|
+
const magnitude = format.format(-num);
|
|
93
|
+
return magnitude === format.format(0) ? magnitude : `(${magnitude})`;
|
|
88
94
|
}
|
|
89
95
|
/**
|
|
90
96
|
* Format an INTEGER-CENTS value as currency.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@poodle64/ui",
|
|
3
|
-
"version": "2026.10.
|
|
3
|
+
"version": "2026.10.1",
|
|
4
4
|
"description": "Household shared component layer: shadcn-svelte primitives (bits-ui) plus the composed page chrome every app builds its routes from, restyled by each app's @poodle64/design-tokens alias layer. One fix reaches every app.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -83,6 +83,7 @@
|
|
|
83
83
|
"@lucide/svelte": "^1.25.0",
|
|
84
84
|
"@tanstack/table-core": "^8.21.3",
|
|
85
85
|
"clsx": "^2.1.1",
|
|
86
|
+
"esm-env": "^1.2.2",
|
|
86
87
|
"html-to-image": "^1.11.13",
|
|
87
88
|
"tailwind-merge": "^3.6.0",
|
|
88
89
|
"tailwind-variants": "^3.2.2",
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"meta": {
|
|
3
3
|
"package": "@poodle64/ui",
|
|
4
|
-
"version": "2026.10.
|
|
4
|
+
"version": "2026.10.1",
|
|
5
5
|
"generatedBy": "scripts/generate-registry.mjs",
|
|
6
6
|
"source": "scripts/situations.json + package source (DO NOT EDIT the outputs by hand)",
|
|
7
|
-
"componentCount":
|
|
8
|
-
"situationCount":
|
|
7
|
+
"componentCount": 62,
|
|
8
|
+
"situationCount": 18
|
|
9
9
|
},
|
|
10
10
|
"situations": [
|
|
11
11
|
{
|
|
@@ -148,6 +148,20 @@
|
|
|
148
148
|
}
|
|
149
149
|
]
|
|
150
150
|
},
|
|
151
|
+
{
|
|
152
|
+
"key": "ledger",
|
|
153
|
+
"title": "A ledger of what moved",
|
|
154
|
+
"description": "Dated rows of money, coins or units moving — a bank account, a property's books, a wallet, a brokerage — read the same way in every module: two lines a row, grouped by the period the viewer picks, the viewer's own columns and running balance. The module brings its rows and offers its columns; the look, the grouping and the toggles are the ledger's.",
|
|
155
|
+
"components": [
|
|
156
|
+
{
|
|
157
|
+
"name": "Ledger",
|
|
158
|
+
"dir": "ledger",
|
|
159
|
+
"import": "@poodle64/ui/ledger",
|
|
160
|
+
"props": "rows, columns?, preferences?, onPreferencesChange?, title?, actions?, selected?, bulkActions?, open?, editor?",
|
|
161
|
+
"insteadOf": "a DataTableTanstack of transactions, or a hand-built grouped list with its own period headers and column picker"
|
|
162
|
+
}
|
|
163
|
+
]
|
|
164
|
+
},
|
|
151
165
|
{
|
|
152
166
|
"key": "sectioning-card",
|
|
153
167
|
"title": "A titled section of a route",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @poodle64/ui — situation → component map
|
|
2
2
|
|
|
3
3
|
<!-- GENERATED by scripts/generate-registry.mjs from scripts/situations.json + package source. DO NOT EDIT. -->
|
|
4
|
-
Generated from `@poodle64/ui@2026.10.
|
|
4
|
+
Generated from `@poodle64/ui@2026.10.1`. 62 components, 18 situations.
|
|
5
5
|
|
|
6
6
|
**Read this before writing a `<div>`.** Find the SITUATION you are in below, then compose the component named for it — do not hand-build it from raw `Card` or utility classes. Import is `import { Name } from '<import path>'`. Props marked `?` are optional. This map is the retrieval step the [`frontend-design` skill] makes mandatory; the [CHI 2026 study] measured composing-from-a-registry at 95% design-system compliance against 71% for writing the CSS from a prose style guide.
|
|
7
7
|
|
|
@@ -76,6 +76,14 @@ _An index of many peer items — a responsive tile wall, or a sortable/filterabl
|
|
|
76
76
|
| `DataTableTanstack` | `@poodle64/ui/data-table-tanstack` | `table, selectable?, selectedId?, onSelect?, emptyMessage?` | a hand-built <table>, or the raw Table primitive wired to TanStack by hand |
|
|
77
77
|
| `DataTableToolbar` | `@poodle64/ui/data-table-toolbar` | `searchValue, onSearch, searchPlaceholder?, chipGroups?, trailing?` | a hand-built search + filter-chip bar above a table |
|
|
78
78
|
|
|
79
|
+
## A ledger of what moved
|
|
80
|
+
|
|
81
|
+
_Dated rows of money, coins or units moving — a bank account, a property's books, a wallet, a brokerage — read the same way in every module: two lines a row, grouped by the period the viewer picks, the viewer's own columns and running balance. The module brings its rows and offers its columns; the look, the grouping and the toggles are the ledger's._
|
|
82
|
+
|
|
83
|
+
| Component | Import | Key props | Reach for it instead of |
|
|
84
|
+
| --- | --- | --- | --- |
|
|
85
|
+
| `Ledger` | `@poodle64/ui/ledger` | `rows, columns?, preferences?, onPreferencesChange?, title?, actions?, selected?, bulkActions?, open?, editor?` | a DataTableTanstack of transactions, or a hand-built grouped list with its own period headers and column picker |
|
|
86
|
+
|
|
79
87
|
## A titled section of a route
|
|
80
88
|
|
|
81
89
|
_The generic titled card every route reaches for — an optional header with icon, subtitle and actions over a body. The default answer to 'I need a card here'._
|