@synopackageland/ui 0.25.10 → 0.25.12

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.
@@ -141,6 +141,18 @@ state keys are copied through). The Android back button or the iOS edge swipe
141
141
  calls `close` for the top surface only; closing by code removes the entry.
142
142
  Pass `enabled: false` where the surface is a popover or inside a DSM window.
143
143
 
144
+ Inside the Package Land phone app, a root page also gets Back to the App list.
145
+ The app's bridge (`window.SynoPkgNative`, protocol 1) lists `close` in its
146
+ `features`; AppLayout then leads the phone bar with the shared back arrow, and
147
+ a tap posts `{ v: 1, type: 'close' }`, which the app honors only right after a
148
+ tap. A browser, a sub-page (which has its own Back) and anything wider than a
149
+ phone never show it. When the page hides the bar (`phone-bar-title="none"`
150
+ without a menu), the first `PageToolbar` or `SectionTabs` row on the page holds
151
+ the button at its start, so it costs no row; with no such row the bar is drawn
152
+ for it. The arrow is `app-layout/back-arrow.json`, a byte copy of the phone
153
+ app's drawing; `tests/ShellBack.test.ts` pins its SHA-256, as the phone app
154
+ does, so change both copies together.
155
+
144
156
  `node scripts/check-keyboard.mjs [out-dir]` (after `pnpm build`) checks all of
145
157
  this in Chromium at 390 and 768 with touch, with Android emulated by a
146
158
  viewport resize and iOS by a stand-in visual viewport. A real-device pass on
@@ -24,7 +24,7 @@ are in [dsm-native-metrics.md](dsm-native-metrics.md).
24
24
  | Form or window footer | `FormActions`, `AppActionBar`, `Footbar`, `Panel` `#fbar` | capsule | Reset `secondary`, then Apply `primary` |
25
25
  | Notification | `Notification` `confirm` / `cancel` | capsule | Cancel `secondary`, then the action `primary` |
26
26
  | Toolbar | `AppToolbar`, a row above a list | tool | `secondary`; `SplitButton` for Create and its variants |
27
- | Page header | `PageHeader` `#actions` | tool | one refresh `IconButton` per page |
27
+ | Page header | `PageHeader` `#actions`, the SDK `AppShell`'s `#actions` | tool | `secondary`, only the page's own actions (Add); no refresh |
28
28
  | Tab row | `SectionTabs` `#actions` | tool | `secondary`, for the shown section (Export, Check again) |
29
29
  | Section or card | `SettingsSection` / `Card` body, `#actions`, `#footer` | tool | `secondary`; a Save / Cancel row there goes in `FormActions` |
30
30
  | Beside a field | `InputGroup` | tool | `secondary` (Open, Copy, Test) |
@@ -19,7 +19,7 @@ These rules keep pages built from `@synopackageland/ui` consistent. The componen
19
19
  - Buttons that belong together sit in one row with an 8px gap: `FormActions` in a form, the `#footer` slot in a section or card.
20
20
  - Only the bar that closes a dialog or form draws capsule `action` buttons, without a `kind`: `FormActions`, `Footbar`, `AppActionBar`, a `Notification`'s actions, and the footers of `Dialog`, `Wizard`, `Sheet` and `Panel` (`#fbar`), with the main action last. `kind="tool"` opts one button out. A `SettingsSection` or `Card` footer, header actions and content stay `tool` buttons; put a section's Save / Cancel in `FormActions`.
21
21
  - A settings tab's Reset / Apply go in `SettingsPage`'s `#footer`: it stays at the bottom over a divider while only the sections scroll. Do not write your own sticky footer.
22
- - Each page has one refresh, with the page's other buttons: in the bar above the page (the SDK `AppShell`'s `#actions`), or in `PageHeader` where a page draws one. Panels expose `refresh()` for it instead of their own button.
22
+ - A page's header actions (the SDK `AppShell`'s `#actions`, or `PageHeader` `#actions` where a page draws one) hold only the page's own actions, such as Add. There is no page-wide refresh: a page reads its data again on `ctx.reopen` and whenever it is shown, and a failed read puts Retry next to its error. The SDK `AppShell` draws no bar above the page while its `#actions` is empty.
23
23
  - A disabled primary button keeps readable text. Put the reason next to it, such as "Enter the password to confirm."
24
24
  - Danger actions sit apart from the rest: the explanation comes first, then the button, or a confirm dialog.
25
25
  - Return the promise from a `Button` or `IconButton` click handler (`@click="save"` with an `async save`): the button shows a spinner, keeps its width and ignores further clicks until the promise settles. Use the `loading` prop only for work the button did not start.
@@ -42,7 +42,7 @@ the commit footer behave as before.
42
42
  | `--ui-radius-md` / `-lg` (and the 0.23.9 roles on them) | 4 / 6 | 12 / 14: md rounds the control, container and popover roles (fields, tool buttons, inset blocks, menus), lg the panel and nav roles |
43
43
  | `--ui-radius-dialog` / `-window` / `-sheet` | 8 / 8 / 12 | 24 |
44
44
  | Primary, danger and secondary buttons | capsule (action) or 4px (tool) | capsule; a primary's fill is accent step 10 (`#0075db`, 4.6:1 with white) |
45
- | Segmented control | accent pill on a framed track, equal columns | a pill in the primary button's fill (accent step 10, `#0075db`, with the accent's contrast colour, white: 4.6:1, light and dark) on a frameless mist track; 6px sides; up to three segments stay equal columns but one whose label is wider than its share grows to it, so a label is not cut (four ellipsize as before) (`check:phone-layout` checks it) |
45
+ | Segmented control | accent pill on a framed track, equal columns | a pill in the primary button's fill (accent step 10, `#0075db`, with the accent's contrast colour, white: 4.6:1, light and dark) on a frameless mist track; 6px sides; up to four segments stay equal columns but one whose label is wider than its share grows to it, so a label is not cut; labels too wide together scroll the track sideways (`check:phone-layout` checks it) |
46
46
  | Phone bar | white with a hairline | the page's colour, no hairline, 17px bold title, round pressed tint |
47
47
  | Tab bar | full-width white row with a top rule | a floating white pill inset 16px, 8px above the bottom (`--ui-tab-bar-float`); the current tab's icon is white on a round pad in the primary's fill (accent step 10); a tab with a label and no icon takes accent step 10 on its label over the mist pad |
48
48
  | `--ui-text-muted` | 40% grey (2:1) | 80% (4.8:1 on the mist; dark 62%) |
@@ -68,6 +68,18 @@ Numeric preset rows are at least 44px tall on touch. The rich text toolbar
68
68
  counts its 44px touch buttons when deciding which tools fit; the rest move
69
69
  into the existing More menu.
70
70
 
71
+ On a touch phone (`pointer: coarse`) the UI draws no `::-webkit-scrollbar`:
72
+ Chromium, Android included, turns any styled webkit bar into a classic one
73
+ that takes its width from the scroller, which left the right margin 10px
74
+ short of the left. Scrollers keep the browser's overlay bar, and a Select or
75
+ Combobox list a thin one. A natural-height page chain (`flex: 0 0 auto`)
76
+ keeps the full width of a flex row such as `ListDetail` (`inline-size:
77
+ stretch`), so a list is never shrunk to its content. A `SearchField`
78
+ (`data-ui-search-field`), alone or in a wrapper holding only it, grows to its
79
+ row and drops a fixed basis or `max-width`. A DataGrid group row's
80
+ `#group-title` wraps instead of running over the row's links. A page's
81
+ toolbar and header actions wrap within the page.
82
+
71
83
  `pnpm check:mobile` checks the gallery at both phone and tablet sizes. It is
72
84
  a blocking step of `gates:full`; the basic gate selection stays the same.
73
85
  Any remaining audit exceptions are bounded in
@@ -396,6 +408,12 @@ context menu for now; it becomes a phone sheet once pickers move to sheets.
396
408
  The keyboard depends on each field's type, so these are component defaults,
397
409
  not tokens.
398
410
 
411
+ A press on a text field (an `input` that takes text, a `textarea`, editable
412
+ content) is left to the browser's own focus. Android hides the soft keyboard
413
+ it just raised when script focuses the field during the press, so the app
414
+ root's focus handling focuses only buttons, links and other controls on
415
+ pointerdown.
416
+
399
417
  ### Keyboard hints by type
400
418
 
401
419
  `Input` sets these from its `type`, and `SearchField` from `search`. An
@@ -752,6 +770,11 @@ change: the same slot moves.
752
770
  name too long for the screen ends in an ellipsis inside the pill.
753
771
  - Rare or destructive items belong in `headerActions` ("…" in the bar), not
754
772
  in the page actions.
773
+ - Only actions float. While the actions hold anything else (a status badge, a
774
+ count, a date, a field), also content that arrives later through an App's
775
+ own teleport, they stay in the page's own row, which wraps, and no FAB is
776
+ drawn, so nothing floats over the page's rows. Put a page's status in the
777
+ page's meta (the SDK `PageInfo`), not in its actions.
755
778
  - `phone-actions` on `AppLayout`, and on a `PageHeader`, `AppToolbar` or
756
779
  `PushTransition` for one page: `fab` (default), `bar` (icons in the bar) or `inline` (the page's own header row).
757
780
  - `PageFab` picks the FAB explicitly: `<PageFab><Button variant="primary">…</Button></PageFab>`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@synopackageland/ui",
3
- "version": "0.25.10",
3
+ "version": "0.25.12",
4
4
  "description": "Shared Vue 3 UI component library with a DSM 7 inspired application theme",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -60,6 +60,67 @@
60
60
  "./themes/dsm7-embedded-reset.css": "./dist/themes/dsm7-embedded-reset.css",
61
61
  "./themes/dsm7-embedded-theme.css": "./dist/themes/dsm7-embedded-theme.css"
62
62
  },
63
+ "scripts": {
64
+ "check:docs": "node --test scripts/check-doc-versions.test.mjs && node scripts/check-doc-versions.mjs",
65
+ "dev": "vite build --watch",
66
+ "build": "vite build && vite build --mode json && vite build --mode rich-text && vite build --mode grid && vite build --mode cli && vite build --mode preview && vite build --mode embedded && node scripts/postbuild.mjs",
67
+ "lint": "eslint .",
68
+ "typecheck": "vue-tsc --noEmit -p tsconfig.json",
69
+ "test": "vitest run",
70
+ "test:watch": "vitest",
71
+ "test:changed": "echo \"test:changed skips the tests the changes since ${SYNOPKGLAND_GATE_BASE:-origin/master} do not reach (vitest --changed); pnpm check runs them all\" && vitest run --changed ${SYNOPKGLAND_GATE_BASE:-origin/master}",
72
+ "story": "histoire dev",
73
+ "story:build": "histoire build",
74
+ "check:host": "node scripts/dsm-host-shots.mjs",
75
+ "check:switch": "node scripts/check-switch.mjs",
76
+ "check:tabs": "node scripts/check-tabs.mjs",
77
+ "check:mobile": "node scripts/check-mobile-overflow.mjs",
78
+ "check:ui-patch": "node scripts/check-ui-patch.mjs",
79
+ "check:narrow": "node scripts/check-narrow-controls.mjs",
80
+ "check:drawer-form": "node scripts/check-drawer-form.mjs",
81
+ "check:responsive": "node scripts/check-responsive.mjs",
82
+ "check:adaptive": "node scripts/check-adaptive.mjs",
83
+ "check:sidebar-focus": "node scripts/check-sidebar-focus.mjs",
84
+ "check:json-render": "node scripts/check-json-render.mjs",
85
+ "check:motion": "node scripts/check-motion.mjs",
86
+ "check:topology": "node scripts/check-topology.mjs",
87
+ "check:wizard": "node scripts/check-wizard.mjs",
88
+ "check:press": "node scripts/check-press.mjs",
89
+ "check:edges": "node scripts/check-edges.mjs",
90
+ "check:data-grid": "node scripts/check-data-grid.mjs",
91
+ "check:truncation": "node scripts/check-truncation.mjs",
92
+ "check:link": "node scripts/check-link.mjs",
93
+ "check:text-select": "node scripts/check-text-select.mjs",
94
+ "check:date-picker": "node scripts/check-date-picker.mjs",
95
+ "check:full-page": "node scripts/check-full-page.mjs",
96
+ "check:short-viewport": "node scripts/check-short-viewport.mjs",
97
+ "check:phone-layout": "node scripts/check-phone-layout.mjs",
98
+ "check:phone-bars": "node scripts/check-phone-bars.mjs",
99
+ "check:phone-fill": "node scripts/check-phone-fill.mjs",
100
+ "check:text-zoom": "node scripts/check-text-zoom.mjs",
101
+ "check:focus-restore": "node scripts/check-focus-restore.mjs",
102
+ "check:detail-surface": "node scripts/check-detail-surface.mjs",
103
+ "check:internal": "node --test scripts/check-internal-content.test.mjs scripts/check-npm-metadata.test.mjs && node scripts/check-internal-content.mjs --repo .",
104
+ "check:npm-metadata": "node scripts/check-npm-metadata.mjs",
105
+ "check:package": "node scripts/check-package-content.mjs",
106
+ "trace:restyle": "node scripts/trace-restyle.mjs",
107
+ "check:public-api-consumer": "node scripts/check-public-api-consumer.mjs",
108
+ "notices:update": "node scripts/third-party-notices.mjs --write",
109
+ "notices:check": "node scripts/third-party-notices.mjs --check",
110
+ "check": "bash scripts/docker-check.sh \"npm run gates\"",
111
+ "check:fast": "bash scripts/docker-check.sh \"npm run gates\"",
112
+ "check:local-browser": "node scripts/app-local-browser.mjs",
113
+ "check:infotip": "node scripts/check-infotip.mjs",
114
+ "release:stage": "node scripts/stage-release.mjs",
115
+ "check:polish": "bash scripts/docker-check.sh --build --out .polish-shots 'node scripts/check-polish.mjs'",
116
+ "check:acceptance": "node scripts/check-acceptance.mjs",
117
+ "check:palette": "node scripts/check-palette.mjs",
118
+ "gates": "node scripts/gates.mjs",
119
+ "gates:full": "node scripts/gates.mjs --full",
120
+ "check:layout": "node scripts/check-layout.mjs",
121
+ "release:smoke": "node scripts/release-smoke.mjs",
122
+ "check:browser-install": "playwright install chromium webkit"
123
+ },
63
124
  "peerDependencies": {
64
125
  "vue": "^3.5.0"
65
126
  },
@@ -79,9 +140,44 @@
79
140
  "@tiptap/vue-3": "3.32.1",
80
141
  "zod": "^4.6.5"
81
142
  },
143
+ "devDependencies": {
144
+ "@eslint/js": "10.0.1",
145
+ "@histoire/plugin-vue": "1.0.0-beta.1",
146
+ "@tailwindcss/vite": "4.3.3",
147
+ "@tanstack/vue-virtual": "3.13.39",
148
+ "@vitejs/plugin-vue": "6.0.9",
149
+ "@vue/test-utils": "2.5.1",
150
+ "@vueuse/core": "14.4.0",
151
+ "eslint": "10.11.0",
152
+ "eslint-plugin-vue": "10.11.1",
153
+ "flexsearch": "0.8.212",
154
+ "globals": "17.12.0",
155
+ "histoire": "1.0.0-beta.1",
156
+ "jsdom": "30.1.1",
157
+ "playwright": "1.58.2",
158
+ "reka-ui": "2.11.0",
159
+ "tailwindcss": "4.3.3",
160
+ "typescript": "5.9.3",
161
+ "typescript-eslint": "8.71.0",
162
+ "vite": "7.3.6",
163
+ "vite-plugin-dts": "5.1.1",
164
+ "vitest": "5.0.2",
165
+ "vscode-oniguruma": "2.0.1",
166
+ "vscode-textmate": "9.3.2",
167
+ "vue": "3.5.43",
168
+ "vue-eslint-parser": "10.4.1",
169
+ "vue-tsc": "3.3.11"
170
+ },
82
171
  "engines": {
83
172
  "node": ">=20"
84
173
  },
174
+ "packageManager": "pnpm@10.33.2",
175
+ "pnpm": {
176
+ "onlyBuiltDependencies": [
177
+ "esbuild",
178
+ "vue-demi"
179
+ ]
180
+ },
85
181
  "config": {
86
182
  "bundledRuntime": {
87
183
  "@tanstack/vue-virtual": "3.13.39",