@synopackageland/ui 0.13.2 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.14.0 (2026-10-22)
4
+
5
+ Additive only: existing props, defaults and DOM stay as they were.
6
+
7
+ - Add `SplitPane`: two panes, horizontal or vertical, with a separator moved by pointer, arrow keys (Shift for larger steps, Home/End to the minimums) or reset by a double click. `min-start`/`min-end` in px; `storage-key` remembers the share per orientation in `localStorage` (guarded, falling back to `default-size`). The separator is a focusable `role="separator"` with value, orientation and `aria-controls`.
8
+ - Add `SizeBar`: a 4px two-part bar for a list cell (a size against the largest row, with an emphasised part such as what can be freed).
9
+ - Add `StorageMeter`: "Free now → Free after" with a used/freeing/free bar, an "At least" lower-bound state and a pending state; follows the surrounding text colour.
10
+ - `DataGrid`: `row-selectable(row)` disables a row's box and the header box skips it; `row-select-disabled-reason(row)` shows why in a keyboard-reachable tooltip; `select-all-scope="all"` makes the header box act on every filtered row across pages. The header box shows a mixed state when some rows are selected; a click when all are selected clears them. `labels.selectRow` also receives the row.
11
+ - `Checkbox`: `indeterminate` shows the mixed state (a dash, `aria-checked="mixed"`); a click emits `true`.
12
+ - `PageHeader`: each action keeps its content width, so a full-width control (a `Select`) in the actions no longer takes the whole row and pushes the other actions onto lines of their own.
13
+ - New strings in all nine built-in languages: "Resize panes", "Free now", "Free after", "At least {size}", "Free now {now}, free after {after}".
14
+ - `docs/size-and-split.md` documents the three components. A test now keeps `<style>` blocks out of library components: the embedded theme is built from classes only, so such styles would never reach an app.
15
+
3
16
  ## 0.13.2 (2026-10-21)
4
17
 
5
18
  - Fix inherited `--ui-nav-row-height` overrides: web and mobile layout modes now supply only an internal fallback. A theme can set the public token on any ancestor, including outside `AppLayout`; unset, window/web/mobile rows remain 42/40/46px. Sidebar, top-navigation and tree rows use the same fallback, and explicit density values remain supported.
package/README.md CHANGED
@@ -472,7 +472,7 @@ Primitives:
472
472
  - Input / Textarea
473
473
  - Checkbox / CheckboxGroup / RadioGroup / Switch
474
474
  - Select
475
- - Badge / StatusDot / Separator / Spinner / LoadingMask / LoadingArea / Progress / Skeleton / Surface
475
+ - Badge / StatusDot / Separator / Spinner / LoadingMask / LoadingArea / Progress / SizeBar / Skeleton / Surface
476
476
  - SplitButton
477
477
  - Tooltip / Popover / DropdownMenu / ContextMenu / Dialog
478
478
  - Sheet / Drawer
@@ -492,6 +492,7 @@ DSM-style application building blocks:
492
492
  - AppRoot (DSM-embedded app root)
493
493
  - AppLayout / AppSidebar / AppToolbar / AppActionBar
494
494
  - PageHeader / SectionTabs
495
+ - SplitPane / StorageMeter (see [docs/size-and-split.md](docs/size-and-split.md))
495
496
 
496
497
  `Tabs` and `SectionTabs` move a raised selection notch in 120ms. Selected labels
497
498
  are blue; main labels are bold 14px and section labels bold 13px. `Tabs` fades its
@@ -616,8 +617,9 @@ phone; use `DataGrid` for anything a user searches or arranges.
616
617
  - **Paging.** Long lists are paged in the footer (20 rows per page by default). `page` and `page-size` work unbound or with `v-model:page` / `v-model:page-size`; `page-sizes` adds the page-size choice. A new filter, sort or page size goes back to page 1, and a page past the end shows the last one. With `:local-pagination="false"` and `total-rows` the app pages on its server. Virtual scrolling is not provided.
617
618
  - **Saved view.** `state-key="downloads"` saves the user's column order, hidden columns, widths, sort and page size in this browser (localStorage) and restores them; bound props win. To keep them on a server, bind the v-models instead, or use the exported `loadGridState` / `saveGridState`. `resetColumns()` (exposed) and the chooser's Reset restore order, visibility and widths.
618
619
  - **Layout.** Sticky header (default), column dividers, hover and selected row colours, `max-height` to scroll inside the grid. `selectable` adds checkboxes; with `select-on-row-click` a row click selects the row and Ctrl/Cmd-click toggles it.
620
+ - **Selection.** The header box selects every selectable row of its scope: the current page (`select-all-scope="page"`, the default) or every row the filter leaves, across pages (`"all"`). It is checked when all of them are selected, mixed (`aria-checked="mixed"`, a dash) when some are, and a click when all are clears them; keys outside the scope (another page, rows the filter hides) are kept. `row-selectable(row)` returning `false` disables a row's box, and the header box skips that row and keeps its state. `row-select-disabled-reason(row)` says why: the box is wrapped in a Tab stop with that text as its accessible name and as a `Tooltip`, so the reason is visible on hover and keyboard focus, not only in a native `title`. With no selectable row the header box is disabled.
619
621
  - **Footer bar.** `footer` shows the DSM bar with the item count; `refreshable` adds the refresh button (`@refresh`, `refreshing` spins it). Without `footer` it appears when there is more than one page, a page-size choice that matters, or a `#footer` slot.
620
- - **Localisation.** Every built-in string goes through `labels`: `empty`, `noMatch`, `items(count)`, `filtered(count, total)`, `page(page, count)`, `previous`, `next`, `pageSize`, `refresh`, `selectAll`, `selectRow(key)`, `resizeColumn(label)`, `columns`, `resetColumns`, `hiddenByWidth`. `emptyText` still works.
622
+ - **Localisation.** Every built-in string goes through `labels`: `empty`, `noMatch`, `items(count)`, `filtered(count, total)`, `page(page, count)`, `previous`, `next`, `pageSize`, `refresh`, `selectAll`, `selectRow(key, row)`, `resizeColumn(label)`, `columns`, `resetColumns`, `hiddenByWidth`. `emptyText` still works.
621
623
  - **Loading.** `loading` masks the table with a centred indicator; `loading-text` adds a message.
622
624
  - **Footer layout.** The footer has top and bottom divider lines and a separator
623
625
  before Refresh. In a flex column with a bounded height, the grid fills the
@@ -134,6 +134,12 @@ type __VLS_Props = {
134
134
  selectable?: boolean;
135
135
  /** With `selectable`: a row click selects that row; Ctrl/Cmd-click toggles it. */
136
136
  selectOnRowClick?: boolean;
137
+ /** With `selectable`: `false` disables a row's box; the header box skips that row. */
138
+ rowSelectable?: (row: Record<string, unknown>) => boolean;
139
+ /** Why a row's box is disabled, in a tooltip on it that the keyboard also reaches. */
140
+ rowSelectDisabledReason?: (row: Record<string, unknown>) => string | undefined;
141
+ /** What the header box selects: the rows of this `page` (default), or `all` the rows the filter shows, on every page. */
142
+ selectAllScope?: 'page' | 'all';
137
143
  /** One selected row at a time, picked by clicking the row; no checkbox column. */
138
144
  singleSelect?: boolean;
139
145
  /** Bind to control the page; leave unset and the grid pages by itself. */
@@ -300,6 +306,9 @@ declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {
300
306
  defaultSort: DataGridSort | null;
301
307
  selectedKeys: string[];
302
308
  selectOnRowClick: boolean;
309
+ rowSelectable: (row: Record<string, unknown>) => boolean;
310
+ rowSelectDisabledReason: (row: Record<string, unknown>) => string | undefined;
311
+ selectAllScope: "page" | "all";
303
312
  singleSelect: boolean;
304
313
  localFilter: boolean;
305
314
  localSort: boolean;
@@ -11,7 +11,8 @@ export type DataGridLabels = {
11
11
  next: string;
12
12
  refresh: string;
13
13
  selectAll: string;
14
- selectRow: (key: string) => string;
14
+ /** The row's box; `row` is the row itself, to name it by its content rather than its key. */
15
+ selectRow: (key: string, row?: Record<string, unknown>) => string;
15
16
  resizeColumn: (label: string) => string;
16
17
  expandRow?: (key: string) => string;
17
18
  collapseRow?: (key: string) => string;
@@ -0,0 +1,27 @@
1
+ export type StorageMeterLabels = {
2
+ freeNow?: string;
3
+ freeAfter?: string;
4
+ };
5
+ type __VLS_Props = {
6
+ /** The volume's size in bytes. */
7
+ total: number;
8
+ /** Free bytes now. */
9
+ free: number;
10
+ /** Bytes the release frees: they move from used to free. */
11
+ freeing: number;
12
+ /** `freeing` is a lower bound: "at least". */
13
+ atLeast?: boolean;
14
+ /** A better number is being worked out (`aria-busy`). */
15
+ pending?: boolean;
16
+ labels?: StorageMeterLabels;
17
+ /** Formats bytes; DSM sizes (`formatFileSize`) by default. */
18
+ format?: (bytes: number) => string;
19
+ };
20
+ declare const __VLS_export: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
21
+ pending: boolean;
22
+ format: (bytes: number) => string;
23
+ labels: StorageMeterLabels;
24
+ atLeast: boolean;
25
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
26
+ declare const _default: typeof __VLS_export;
27
+ export default _default;
@@ -0,0 +1,49 @@
1
+ type __VLS_Props = {
2
+ /** The start pane's share of the room, 0–1. Bind to control it; leave unset and the pane tracks it. */
3
+ modelValue?: number;
4
+ /** The share it starts with and goes back to on a double click. */
5
+ defaultSize?: number;
6
+ /** `horizontal`: side by side, start on the left. `vertical`: start above end. */
7
+ orientation?: 'horizontal' | 'vertical';
8
+ /** The start pane's minimum size in px. */
9
+ minStart?: number;
10
+ /** The end pane's minimum size in px. */
11
+ minEnd?: number;
12
+ /** The separator's thickness in px: the room between the panes. */
13
+ gap?: number;
14
+ /** Remembers the share in `localStorage` under `<storageKey>:<orientation>`; unreadable storage falls back to `defaultSize`. */
15
+ storageKey?: string;
16
+ /** The separator's accessible name. */
17
+ label?: string;
18
+ };
19
+ declare function reset(): void;
20
+ declare var __VLS_1: {}, __VLS_3: {};
21
+ type __VLS_Slots = {} & {
22
+ start?: (props: typeof __VLS_1) => any;
23
+ } & {
24
+ end?: (props: typeof __VLS_3) => any;
25
+ };
26
+ declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {
27
+ reset: typeof reset;
28
+ }, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
29
+ "update:modelValue": (value: number) => any;
30
+ }, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{
31
+ "onUpdate:modelValue"?: ((value: number) => any) | undefined;
32
+ }>, {
33
+ label: string;
34
+ modelValue: number;
35
+ orientation: "horizontal" | "vertical";
36
+ gap: number;
37
+ defaultSize: number;
38
+ minStart: number;
39
+ minEnd: number;
40
+ storageKey: string;
41
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
42
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
43
+ declare const _default: typeof __VLS_export;
44
+ export default _default;
45
+ type __VLS_WithSlots<T, S> = T & {
46
+ new (): {
47
+ $slots: S;
48
+ };
49
+ };
@@ -3,6 +3,8 @@ type __VLS_Props = {
3
3
  disabled?: boolean;
4
4
  label?: string;
5
5
  description?: string;
6
+ /** Some but not all of what the box stands for is chosen (a header "select all"): a dash, `aria-checked="mixed"`. A click checks it. */
7
+ indeterminate?: boolean;
6
8
  };
7
9
  declare const __VLS_export: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
8
10
  "update:modelValue": (value: boolean) => any;
@@ -11,6 +13,7 @@ declare const __VLS_export: import('vue').DefineComponent<__VLS_Props, {}, {}, {
11
13
  }>, {
12
14
  disabled: boolean;
13
15
  modelValue: boolean;
16
+ indeterminate: boolean;
14
17
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
15
18
  declare const _default: typeof __VLS_export;
16
19
  export default _default;
@@ -0,0 +1,15 @@
1
+ type __VLS_Props = {
2
+ value: number | null | undefined;
3
+ max: number;
4
+ part?: number | null;
5
+ /** The bar's accessible name; without it the bar is decorative (`aria-hidden`). */
6
+ label?: string;
7
+ tone?: 'primary' | 'success' | 'warning' | 'danger';
8
+ };
9
+ declare const __VLS_export: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
10
+ label: string;
11
+ tone: "primary" | "success" | "warning" | "danger";
12
+ part: number | null;
13
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
14
+ declare const _default: typeof __VLS_export;
15
+ export default _default;
@@ -35,6 +35,11 @@ export declare const englishUiText: {
35
35
  readonly "Collapse navigation": "Collapse navigation";
36
36
  readonly "Expand navigation": "Expand navigation";
37
37
  readonly "Resize navigation": "Resize navigation";
38
+ readonly "Resize panes": "Resize panes";
39
+ readonly "Free now": "Free now";
40
+ readonly "Free after": "Free after";
41
+ readonly "At least {size}": "At least {size}";
42
+ readonly "Free now {now}, free after {after}": "Free now {now}, free after {after}";
38
43
  readonly Sections: "Sections";
39
44
  readonly Slider: "Slider";
40
45
  readonly "Increase value": "Increase value";
package/dist/index.d.ts CHANGED
@@ -30,6 +30,7 @@ export { default as LoadingMask } from './components/primitives/loading/LoadingM
30
30
  export { default as LoadingArea } from './components/primitives/loading/LoadingArea.vue';
31
31
  export { default as LoadingIndicator } from './components/primitives/loading/LoadingIndicator.vue';
32
32
  export { default as Progress } from './components/primitives/progress/Progress.vue';
33
+ export { default as SizeBar } from './components/primitives/size-bar/SizeBar.vue';
33
34
  export { default as Skeleton } from './components/primitives/skeleton/Skeleton.vue';
34
35
  export { default as SectionSkeleton } from './components/primitives/skeleton/SectionSkeleton.vue';
35
36
  export { default as Surface } from './components/primitives/surface/Surface.vue';
@@ -191,6 +192,9 @@ export type { AppSidebarGroup, AppSidebarItem } from './components/app/app-sideb
191
192
  export { default as AppToolbar } from './components/app/app-toolbar/AppToolbar.vue';
192
193
  export { default as AppActionBar } from './components/app/app-action-bar/AppActionBar.vue';
193
194
  export { default as PageHeader } from './components/app/page-header/PageHeader.vue';
195
+ export { default as SplitPane } from './components/containers/split-pane/SplitPane.vue';
196
+ export { default as StorageMeter } from './components/composite/storage-meter/StorageMeter.vue';
197
+ export type { StorageMeterLabels } from './components/composite/storage-meter/StorageMeter.vue';
194
198
  export { default as PageContent } from './components/app/page-content/PageContent.vue';
195
199
  export { default as SectionTabs } from './components/app/section-tabs/SectionTabs.vue';
196
200
  export type { SectionTab } from './components/app/section-tabs/SectionTabs.vue';