@synopackageland/ui 0.20.11 → 0.20.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## Unreleased
4
+
5
+ ## 0.20.12 (2026-11-05)
6
+
7
+ - On a short screen (a phone in landscape, at most 480px tall) outside the phone layout, every App gets a short mode: the page title shows in the bar above the page, beside the toolbar's actions, instead of only for screen readers; a settings page (a `SettingsPage` or a `SectionTabs fill`) scrolls as one, so the bar and header scroll away while the section tabs stay at the top and Reset / Apply at the bottom; and a `PageHeader` scrolls with the page in a tight row. A phone in portrait and a taller window are unchanged. New exports `SHORT_VIEWPORT_QUERY` and `APP_SHORT_KEY` (docs/adaptive-layout.md, "Short screens")
8
+ - A `SectionTabs fill` strip, and on a phone the Back header of a pushed section, sticks below a sticky `PageHeader` in the same `PageContent` instead of sliding under it when the page scrolls as a whole
9
+ - FilePicker on Android no longer makes Chrome ask for the camera or microphone: when `accept` names a video or audio type (`video/*`, `.mp4`, `.mp3`), the file chooser gets `application/octet-stream` (every file, no recording), and a chosen file outside `accept` is still rejected. The new `native-accept` prop sets the chooser's type when it should differ from `accept` (README, FilePicker)
10
+ - Grid: a record link draws as a grey chip in the cell's text colour (like a select option without a colour), no longer in the link blue
11
+ - Grid: a click on an attachment thumbnail asks to open it (`open-attachment`) on the first click, as a checkbox or link icon does; record chips still open only on a cell already selected
12
+
3
13
  ## 0.20.11 (2026-11-05)
4
14
 
5
15
  - Button centres its icon on the label: an icon (an `svg`, or an element marked `data-icon`, which `Icon` and `StatusDot` now carry) sits on the centre of the label's line instead of DSM's x-height middle, which put a 16px icon 0.6-1.6px under the label depending on the font. The label keeps its ellipsis
package/README.md CHANGED
@@ -1210,7 +1210,18 @@ partial overrides of `title`, `description`, `chooseFiles`, `chooseFolder`,
1210
1210
  `titleOne`, `descriptionOne`, `chooseFile`, `acceptedTypes`, `images`,
1211
1211
  `videos`, `audio`, `maxSize`, `maxFiles`, `remove`,
1212
1212
  `typeRejected`, `sizeRejected`, `countRejected`, `duplicateRejected`.
1213
- The messages use `{name}`, `{size}`, `{count}` and `{types}` placeholders. Existing
1213
+ The messages use `{name}`, `{size}`, `{count}` and `{types}` placeholders.
1214
+
1215
+ On Android, Chrome asks for the camera when the file chooser's `accept` names
1216
+ a video type and for the microphone when it names an audio type (extensions
1217
+ count: `.mp4` is video, `.mp3` audio), so that it can offer recording. When
1218
+ `accept` names one, FilePicker gives the chooser `application/octet-stream`
1219
+ on Android instead: the plain document chooser, every file, no prompt. The hint
1220
+ still names the `accept` types, and a chosen file outside them is rejected with
1221
+ `type`. Desktop browsers and iOS get `accept` unchanged. `native-accept` sets
1222
+ the chooser's type everywhere when it should differ from `accept` (an empty
1223
+ string shows every file); set it to the `accept` value to keep recording on
1224
+ Android. Existing
1214
1225
  `title`/`description` props still win. Other controls retain their exposed text
1215
1226
  props and partial text objects; built-in text throughout the library now follows
1216
1227
  Provider locale. Explicit strings, including `''`, win over built-in translations.
@@ -3,21 +3,23 @@ interface PageTitleEntry {
3
3
  title: () => string;
4
4
  }
5
5
  /**
6
- * The phone bar's page title. A PageHeader in the layout's content registers
7
- * its title; the last one mounted is the page's, and the bar shows it unless
8
- * AppLayout's own `title` prop is set.
6
+ * The bar's page title (the phone bar, or the bar of a short screen). A
7
+ * PageHeader in the layout's content registers its title; the last one
8
+ * mounted is the page's, and the bar shows it unless AppLayout's own `title`
9
+ * prop is set.
9
10
  */
10
11
  export interface AppPageTitleHost {
11
12
  /** The content column: a PageHeader outside it (in a dialog or sheet) keeps its own title. */
12
13
  main: Readonly<Ref<HTMLElement | null>>;
13
14
  entries: Ref<readonly PageTitleEntry[]>;
14
- /** Whether the bar shows this entry's title (a phone, and no `title` prop). */
15
+ /** Whether the bar shows this entry's title, so its PageHeader keeps the heading for screen readers only. */
15
16
  shows: (entry: PageTitleEntry) => boolean;
16
17
  }
17
18
  export declare const APP_PAGE_TITLE_KEY: InjectionKey<AppPageTitleHost>;
18
- export declare function createPageTitleHost(main: Readonly<Ref<HTMLElement | null>>, active: () => boolean): AppPageTitleHost;
19
+ /** `active` says whether the bar shows a page title of this text (it is the page's last PageHeader). */
20
+ export declare function createPageTitleHost(main: Readonly<Ref<HTMLElement | null>>, active: (title: string) => boolean): AppPageTitleHost;
19
21
  /**
20
- * Hands a PageHeader's title to the phone bar. Returns whether the bar shows
22
+ * Hands a PageHeader's title to the layout's bar. Returns whether the bar shows
21
23
  * it, so the header can keep its own heading for screen readers only.
22
24
  */
23
25
  export declare function usePageTitle(element: Readonly<Ref<HTMLElement | null>>, title: () => string): ComputedRef<boolean>;
@@ -0,0 +1,5 @@
1
+ import { Ref } from 'vue';
2
+ /** For PageContent: the tallest height its sticky headers report, in px. */
3
+ export declare function providePageSticky(): Readonly<Ref<number>>;
4
+ /** For PageHeader: reports its height while it sticks, and nothing once it scrolls with the page. */
5
+ export declare function useStickyHeight(element: Readonly<Ref<HTMLElement | null>>, sticks: () => boolean): void;
@@ -8,7 +8,18 @@ export type FileRejection = {
8
8
  type __VLS_Props = {
9
9
  modelValue?: File[];
10
10
  multiple?: boolean;
11
+ /** The file types taken (`.mp3,audio/*`), as the input's `accept`: chosen, dropped or pasted files of other types are rejected. */
11
12
  accept?: string;
13
+ /**
14
+ * The `accept` the browser's file chooser gets, when it should differ from
15
+ * `accept`; `accept` still decides which chosen files are taken, and an
16
+ * empty string lets the chooser show every file. Unset, it is `accept`,
17
+ * except on Android when `accept` names a video or audio type (`video/*`,
18
+ * `.mp4`, `.mp3`): Chrome there asks for the camera or microphone to offer
19
+ * recording, so the chooser gets `application/octet-stream` (every file,
20
+ * nothing to record). Set it to `accept` to keep recording on Android.
21
+ */
22
+ nativeAccept?: string;
12
23
  disabled?: boolean;
13
24
  maxFiles?: number;
14
25
  maxSize?: number;
@@ -190,8 +190,8 @@ export interface GridCellKindRenderer<V extends GridCellValue = GridCellValue> {
190
190
  /** Plain text for assistive tech and copy. */
191
191
  describe: (value: V, context: GridCellTextContext) => string;
192
192
  /**
193
- * Target under the pointer, if any. Checkbox, rating, link icon and button
194
- * act on the first click; chips and thumbnails only when `wasActive`.
193
+ * Target under the pointer, if any. Checkbox, rating, link icon, button and
194
+ * attachment thumbnail act on the first click; record chips only when `wasActive`.
195
195
  */
196
196
  hitTest?: (value: V, input: GridCellHitInput) => GridCellHit | null;
197
197
  /** Keyboard equivalent of the hit areas (Enter, Space, digits, Backspace). */
package/dist/grid.js CHANGED
@@ -3668,15 +3668,15 @@ const Xe = (e, t) => e.x >= t.x && e.x < t.x + t.width && e.y >= t.y && e.y < t.
3668
3668
  describe: (e) => dt(e.options.map((t) => t.label))
3669
3669
  }, gr = {
3670
3670
  draw: (e, t, n) => {
3671
- const o = { fill: t.cellTheme.badgeFill, color: t.cellTheme.link }, i = xo(e, t, n.records.map((a) => ({ label: a.label, colors: o })));
3671
+ const o = { fill: t.cellTheme.badgeFill, color: t.cellTheme.text }, i = xo(e, t, n.records.map((a) => ({ label: a.label, colors: o })));
3672
3672
  Nt.set(He("link", t, n.records.map((a) => a.id)), i);
3673
3673
  },
3674
3674
  describe: (e) => dt(e.records.map((t) => t.label)),
3675
3675
  hitTest: (e, t) => Ro(Nt.get(He("link", Xt(t), e.records.map((n) => n.id))), t, (n) => ({ type: "open-record", id: e.records[n].id }))
3676
- }, Xt = (e) => ({ width: e.size.width, metrics: e.metrics }), Ro = (e, t, n) => {
3677
- if (!t.wasActive || !e) return null;
3678
- const o = e.findIndex((i) => Xe(t.point, i));
3679
- return o < 0 ? null : { action: n(o), area: e[o] };
3676
+ }, Xt = (e) => ({ width: e.size.width, metrics: e.metrics }), Ro = (e, t, n, o = !1) => {
3677
+ if (!t.wasActive && !o || !e) return null;
3678
+ const i = e.findIndex((a) => Xe(t.point, a));
3679
+ return i < 0 ? null : { action: n(i), area: e[i] };
3680
3680
  }, fr = (e, t, n, o, i, a, s) => {
3681
3681
  const { cellTheme: r, theme: c } = t, l = s ? t.images.get(s) : void 0;
3682
3682
  if (e.save(), e.beginPath(), e.arc(n + i / 2, o + i / 2, i / 2, 0, Math.PI * 2), e.closePath(), l)
@@ -3722,7 +3722,7 @@ const Xe = (e, t) => e.x >= t.x && e.x < t.x + t.width && e.y >= t.y && e.y < t.
3722
3722
  }), l.more > 0 && $t(e, t, h, l.more, l.badgeWidth), Lt.set(He("attachment", t, n.files.map((g) => g.id)), m);
3723
3723
  },
3724
3724
  describe: (e) => dt(e.files.map((t) => t.name)),
3725
- hitTest: (e, t) => Ro(Lt.get(He("attachment", Xt(t), e.files.map((n) => n.id))), t, (n) => ({ type: "open-attachment", id: e.files[n].id }))
3725
+ hitTest: (e, t) => Ro(Lt.get(He("attachment", Xt(t), e.files.map((n) => n.id))), t, (n) => ({ type: "open-attachment", id: e.files[n].id }), !0)
3726
3726
  }, Qn = (e) => `${Math.round(Math.max(0, Math.min(1, e)) * 100)}%`, wr = {
3727
3727
  draw: (e, t, n) => {
3728
3728
  if (n.value == null) return;
package/dist/index.d.ts CHANGED
@@ -240,7 +240,7 @@ export { default as AsidePanel } from './components/app/aside-panel/AsidePanel.v
240
240
  export { clampAsideWidth, readAsidePreference, useAsidePreference, writeAsidePreference } from './components/app/aside-panel/asidePreference';
241
241
  export type { AsidePreference } from './components/app/aside-panel/asidePreference';
242
242
  export { default as ResponsiveView } from './components/app/responsive-view/ResponsiveView.vue';
243
- export { APP_ASIDE_KEY, APP_SIDE_MOTION_KEY, appPresentationForWidth, MOBILE_LAYOUT_MAX, WEB_ASIDE_DEFAULT, WEB_ASIDE_MAX, WEB_ASIDE_MIN, WEB_LAYOUT_MIN, WEB_RAIL_BELOW, WEB_RAIL_WIDTH } from './lib/adaptiveLayout';
243
+ export { APP_ASIDE_KEY, APP_SHORT_KEY, APP_SIDE_MOTION_KEY, appPresentationForWidth, MOBILE_LAYOUT_MAX, SHORT_VIEWPORT_QUERY, WEB_ASIDE_DEFAULT, WEB_ASIDE_MAX, WEB_ASIDE_MIN, WEB_LAYOUT_MIN, WEB_RAIL_BELOW, WEB_RAIL_WIDTH } from './lib/adaptiveLayout';
244
244
  export type { AppAside, AppAsideState, AppNavigation, AppPresentation, WideLayout } from './lib/adaptiveLayout';
245
245
  export { default as AppTabBar } from './components/app/app-tab-bar/AppTabBar.vue';
246
246
  export type { AppTabBarItem } from './components/app/app-tab-bar/AppTabBar.vue';