@synopackageland/ui 0.20.18 → 0.20.20

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
@@ -2,6 +2,25 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.20.20 (2026-11-07)
6
+
7
+ Phone layout (AppLayout 640px or narrower). No App change is required; `PageHeader` `back` is new and optional.
8
+
9
+ - **One header row in a settings drill-down.** A section pushed from a `SectionTabs` list (`PushTransition` with a `title` inside an `AppLayout`) takes the phone bar over while it is shown: Back, the section's title and its `header-actions`, in one row. The page title row and the pushed page's own 48px back header are no longer drawn under it. The list view keeps the page title. Back, the browser's Back and the edge swipe still pop the section
10
+ - **Page actions join the title row.** The `#actions` of the `PageHeader` whose title is in the bar, a pushed page's `#header-actions` and the `#actions` of an `AppToolbar` in AppLayout's `toolbar` slot render in the phone bar after the title (`[data-ui-mobile-page-actions]`), before `headerActions`. They no longer get a bar of their own (the SDK AppShell's Refresh bar is gone on phones). A Button with an icon is drawn as its 44px icon, with its label as its accessible name and tooltip. A text-only Button stays text, in the primary colour. A primary or danger Button keeps its fill. An `AppToolbar` with nothing else takes no row. If the actions do not fit beside the title (the title would be under 80px, or the row would overflow), they stay in the page's own row. Wider layouts are unchanged
11
+ - **One header row on a detail page: `PageHeader` `back`.** `<PageHeader title="Meeting record" back @back="…">` marks a sub-page. On a phone the bar leads with Back, shows this page's title (whatever AppLayout's `title` says) and holds its actions; Back emits `back` and the App pops its own route. Wider, the header leads its title with a Back button. Use it instead of a hand-drawn Back row above the page
12
+ - **No stacked dividers above the footer.** A collapsed `SettingsSection` (and a fixed one with an empty body) draws no rule under its title. Collapsed sections in a row form one list, with a rule between rows and none after the last, so the footer's rule is the only one under them. Desktop too. `SettingsSection` carries `data-state="open|closed"`
13
+ - **One divider in the SettingsPage footer (and other footer bars).** A `FormActions` in a footer that draws its own rule no longer adds its own sticky border, padding and background. This covers the SettingsPage footer (the page's own Reset / Apply and an App's `footer` slot) and the Dialog, Sheet, BottomSheet, Card, Panel, Wizard and AppActionBar footers, which now carry `data-ui-footer-bar`. On a phone the SettingsPage footer drew two rules
14
+ - **Buttons one size smaller on a phone.** Under a coarse pointer in the phone layout, a `Button` and a `SplitButton` are drawn md 36px, sm 28px and lg 44px (`--ui-button-md/-sm/-lg`), one size below the 44px field beside them. Their tap box stays 44px through the shared box (`data-ui-hit`); the SplitButton's two halves now join it too, and its label, not the button, cuts its overflow. An `IconButton` keeps its 36/44px. A mouse-driven narrow window and wider layouts are unchanged
15
+ - **Whole-page scroll on a phone.** At any height (390x664 portrait included), a page with a `SettingsPage` or a `SectionTabs fill` scrolls as one, in the area under the bar. Before, it squeezed the settings body into an inner scroller about 310–540px tall whose scrollbar iOS hides, so the sections below the first one looked gone. Every element between the area and the settings, an App shell's fill wrappers included, takes its content's height. The bar stays, the section strip and a sticky `PageHeader` stick under it, and Reset / Apply stick at the bottom (a short page keeps them at the screen's bottom). The root carries `data-ui-layout-scroll="page"`. Short landscape screens keep their short mode; desktop and DSM windows wider than 640px keep the settings body's own scroller
16
+ - **Disclosure rows on the content edge.** On a touch phone, a collapsible `SettingsSection` header and an `Accordion` trigger start their title on the content edge, where the rule and the content below start, and put the chevron at the row's end (down when closed, up when open) instead of a leading chevron that put the title 24px in. A mouse-driven window keeps the leading chevron. A SegmentControl and the SectionTabs segments already sit on the content edges; a wider one in an App comes from App markup
17
+ - New browser check `check:phone-layout` (in the full gate): the settings list, a pushed section, a long settings page and a detail page at 390x844, 360x800 and 390x664 with touch, in a 390px DSM window and on a 1280px desktop, in English and Traditional Chinese. It checks for one header row, the page's actions in the bar, no stacked rules, footer buttons drawn smaller with a 44px tap box, disclosures on the content edges and a single page scroller on a phone
18
+
19
+ ## 0.20.19 (2026-11-06)
20
+
21
+ - The phone navigation drawer returns focus to its opener inside the shell (the menu button or a More tab) after Escape, Close, a navigation pick, Back or a swipe. If a touch tap left focus on the host page outside the shell, it falls back to the menu button instead of returning focus to the host. It waits until the drawer has left and the background is no longer inert
22
+ - Dialog, Sheet, BottomSheet, RecordPanel and notifications remember focus only inside their AppRoot (including its floating dialog host). A touch-opened overlay ignores the host page's focused element and returns to the pressed control inside the app; existing closeAutoFocus overrides still take precedence
23
+
5
24
  ## 0.20.18 (2026-11-06)
6
25
 
7
26
  - `Callout` takes `dismissScope`. `persistent` (the default) keeps today's behaviour: a closed callout stays closed after a reload until what it says changes. `occurrence` is for a notice about a passing state (reconnecting, an error, a stalled capture) that the App shows with `v-if` while the state lasts: closing hides this appearance only, nothing is stored, and the dismissal is forgotten when the Callout unmounts, so the next time the state starts the notice shows again. `dismissalKey` and `forgetDismissal` are exported: `forgetDismissal(dismissalKey('reconnecting'))` shows the callout with that `dismissKey` again (docs/callout.md, "Closing")
package/dist/cli.js CHANGED
File without changes
@@ -0,0 +1,9 @@
1
+ import { Ref } from 'vue';
2
+ /**
3
+ * A page's actions in the phone bar draw a labelled Button as its icon alone
4
+ * (adaptive.css): its text stays its accessible name, and this gives it the
5
+ * same text as a tooltip, as the bar's own icon actions have. The title goes
6
+ * again when the button leaves the bar (a wider layout puts it back in its
7
+ * header), and a title the App set is kept.
8
+ */
9
+ export declare function useIconActionTitles(container: Readonly<Ref<HTMLElement | null>>): void;
@@ -1,12 +1,18 @@
1
1
  import { ComputedRef, InjectionKey, Ref } from 'vue';
2
2
  interface PageTitleEntry {
3
3
  title: () => string;
4
+ /**
5
+ * A sub-page's Back (a detail page's PageHeader `back`, a pushed page). On a
6
+ * phone the bar then leads with Back and shows this title, whatever
7
+ * AppLayout's own `title` says: the page and its way back are one row.
8
+ */
9
+ back?: () => void;
4
10
  }
5
11
  /**
6
12
  * The bar's page title (the phone bar, or the bar of a short screen). A
7
13
  * 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.
14
+ * registered is the page's, and the bar shows it unless AppLayout's own `title`
15
+ * prop is set. A sub-page (an entry with `back`) takes the phone bar over.
10
16
  */
11
17
  export interface AppPageTitleHost {
12
18
  /** The content column: a PageHeader outside it (in a dialog or sheet) keeps its own title. */
@@ -14,13 +20,44 @@ export interface AppPageTitleHost {
14
20
  entries: Ref<readonly PageTitleEntry[]>;
15
21
  /** Whether the bar shows this entry's title, so its PageHeader keeps the heading for screen readers only. */
16
22
  shows: (entry: PageTitleEntry) => boolean;
23
+ /**
24
+ * The phone bar's place for the page's own actions (PageHeader `#actions`,
25
+ * a pushed page's `#header-actions`, an AppToolbar's `#actions` above the
26
+ * page); null where there is no phone bar. They render there, after the
27
+ * title and before the bar's `headerActions`, instead of in a row of their own.
28
+ */
29
+ actions: Readonly<Ref<HTMLElement | null>>;
30
+ /** Counts what renders into `actions`, so the bar knows the title is not alone. Returns the release. */
31
+ useActions: () => () => void;
17
32
  }
18
33
  export declare const APP_PAGE_TITLE_KEY: InjectionKey<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;
34
+ /**
35
+ * `active` says whether the bar shows a page title of this text (it is the
36
+ * page's last PageHeader); `phone` whether the phone bar is up, where a
37
+ * sub-page's title always shows.
38
+ */
39
+ export declare function createPageTitleHost(main: Readonly<Ref<HTMLElement | null>>, active: (title: string) => boolean, options?: {
40
+ phone?: () => boolean;
41
+ actions?: Readonly<Ref<HTMLElement | null>>;
42
+ actionUsers?: Ref<number>;
43
+ }): AppPageTitleHost;
21
44
  /**
22
45
  * Hands a PageHeader's title to the layout's bar. Returns whether the bar shows
23
- * it, so the header can keep its own heading for screen readers only.
46
+ * it, so the header can keep its own heading for screen readers only. With
47
+ * `back` the page is a sub-page: on a phone the bar leads with Back and calls
48
+ * it. With `when`, the title is handed over only while it holds (a pushed page
49
+ * while it is shown).
50
+ */
51
+ export declare function usePageTitle(element: Readonly<Ref<HTMLElement | null>>, title: () => string, options?: {
52
+ back?: () => (() => void) | undefined;
53
+ when?: () => boolean;
54
+ }): ComputedRef<boolean>;
55
+ /**
56
+ * Whether a component's actions render in the phone bar, and counts them while
57
+ * they do. `wanted` says whether the component would put them there (its title
58
+ * is in the bar, or it is the bar above the page).
24
59
  */
25
- export declare function usePageTitle(element: Readonly<Ref<HTMLElement | null>>, title: () => string): ComputedRef<boolean>;
60
+ export declare function usePageActionsInBar(wanted: () => boolean): {
61
+ target: ComputedRef<HTMLElement | null>;
62
+ };
26
63
  export {};
@@ -1,13 +1,10 @@
1
1
  type __VLS_Props = {
2
2
  height?: string;
3
3
  };
4
- declare var __VLS_1: {}, __VLS_8: {}, __VLS_10: {};
5
- type __VLS_Slots = {} & {
6
- leading?: (props: typeof __VLS_1) => any;
7
- } & {
8
- default?: (props: typeof __VLS_8) => any;
9
- } & {
10
- actions?: (props: typeof __VLS_10) => any;
4
+ type __VLS_Slots = {
5
+ default?: () => unknown;
6
+ leading?: () => unknown;
7
+ actions?: () => unknown;
11
8
  };
12
9
  declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
13
10
  height: string;
@@ -2,15 +2,24 @@ type __VLS_Props = {
2
2
  title: string;
3
3
  description?: string;
4
4
  sticky?: boolean;
5
+ /** A sub-page's header: Back before the title, emitting `back`. On a phone, the bar's Back. */
6
+ back?: boolean;
5
7
  };
6
- declare var __VLS_6: {}, __VLS_8: {};
8
+ declare var __VLS_19: {}, __VLS_27: {}, __VLS_29: {};
7
9
  type __VLS_Slots = {} & {
8
- meta?: (props: typeof __VLS_6) => any;
10
+ meta?: (props: typeof __VLS_19) => any;
9
11
  } & {
10
- actions?: (props: typeof __VLS_8) => any;
12
+ actions?: (props: typeof __VLS_27) => any;
13
+ } & {
14
+ actions?: (props: typeof __VLS_29) => any;
11
15
  };
12
- declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
16
+ declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
17
+ back: () => any;
18
+ }, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{
19
+ onBack?: (() => any) | undefined;
20
+ }>, {
13
21
  sticky: boolean;
22
+ back: boolean;
14
23
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
15
24
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
16
25
  declare const _default: typeof __VLS_export;
@@ -0,0 +1,4 @@
1
+ import { InjectionKey, Ref } from 'vue';
2
+ export declare const APP_FOCUS_ROOT_KEY: InjectionKey<Ref<HTMLElement | null>>;
3
+ /** Remember only this app's controls, including its viewport portal. */
4
+ export declare function useFocusOwner(): (element: HTMLElement) => boolean;
@@ -6,7 +6,8 @@ export declare const OPENER_FOCUS_FRAMES = 120;
6
6
  * an element that had focus when the content mounted, so a bottom sheet, and
7
7
  * a dialog opened by a click that did not focus its button (Safari, touch),
8
8
  * left focus on <body> when Escape or the Close button shut it. The opener is
9
- * the focused element when `open` turns true, else the control just pressed.
9
+ * the focused element inside the app when `open` turns true, else the control
10
+ * just pressed inside the app, else the supplied fallback trigger.
10
11
  *
11
12
  * The returned handler, called from the content's `closeAutoFocus` after the
12
13
  * consumer's own listener, runs when the overlay starts to close: its content
@@ -21,4 +22,7 @@ export declare const OPENER_FOCUS_FRAMES = 120;
21
22
  * document. A consumer that prevents the default
22
23
  * keeps full control.
23
24
  */
24
- export declare function useOpenerFocus(open: () => boolean): (event: Event) => void;
25
+ export declare function useOpenerFocus(open: () => boolean, options?: {
26
+ contains?: (element: HTMLElement) => boolean;
27
+ fallback?: () => HTMLElement | null;
28
+ }): (event: Event) => void;