@synopackageland/ui 0.23.3 → 0.23.5
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 +22 -0
- package/dist/components/app/app-layout/hideOnScroll.d.ts +27 -14
- package/dist/index.d.ts +2 -2
- package/dist/index.js +13098 -12922
- package/dist/interaction/index.d.ts +1 -0
- package/dist/interaction/velocity.d.ts +65 -0
- package/dist/json-preview/preview.js +9320 -9161
- package/dist/lib/scroll.d.ts +6 -2
- package/dist/reka-runtime.json +1 -1
- package/dist/theme.css +1 -1
- package/dist/themes/dsm7-embedded-reset.css +1 -1
- package/dist/themes/dsm7-embedded-theme.css +1 -1
- package/dist/themes/dsm7-embedded.css +1 -1
- package/dist/themes/dsm7.css +1 -1
- package/docs/mobile-native.md +174 -18
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.23.5 (2026-11-07)
|
|
6
|
+
|
|
7
|
+
Phone fixes: a fling keeps its momentum while the bars hide, the page never pans sideways (also from a tap box at its edge), and the FAB is a circle. Gesture decisions share one velocity estimator, and a sticky row in an App's own side pane sticks in that pane. No App change is required; a DSM window and desktop are unchanged.
|
|
8
|
+
|
|
9
|
+
- **Hide on scroll no longer stops a fling on iOS.** Hiding or showing the bars changed the page's layout mid-scroll: the phone bar collapsed by a negative margin, the tab bar's room and the page's end space changed, and in an App shell whose content area scrolls itself the column's bottom padding animated. Each change resized or moved the scroller, and iOS Safari ends a momentum scroll when that happens. The bars now move by `transform` only, and the scroller is sized once. When the page scrolls in an area under the bar (a fill layout, or an App shell like the SDK's AppShell), the layout is marked `data-ui-bars-overlay`: the content runs under the bar, the tab bar and the accessory from the start, and a constant spacer at the end of what scrolls (`--ui-bars-room`) keeps the last row clear of them. Hiding translates the bar, the content area and the tab bar; when the document scrolls, only the tab bar, the accessory and the FAB slide. `--ui-page-end-space` no longer changes while the bars are hidden. While the content area is translated it is the containing block of a `position: fixed` element inside it (the UI's overlays render outside it)
|
|
10
|
+
- **Hide on scroll reads the scroll's velocity.** A slow scroll hides the bars after 48px down and shows them after 24px up (was 16px and 4px), so a slow or wobbly drag no longer flickers them. A flick at 1000 px/s or faster hides or shows them at once and moves them in 60% of `--ui-motion-bars` (at least 120ms). Reduced motion still moves them at once
|
|
11
|
+
- **New shared gesture velocity estimator** (`VelocityEstimator`, `GESTURE_THRESHOLDS`, `velocityDuration`, `swipeCommits`, exported): a least-squares fit over the last 100ms (at most 20 samples) that drops stale, duplicate and out-of-order samples and restarts on a reversal. Native scrolling is unchanged; the estimator only decides. Hide on scroll, the pushed page's edge swipe back (a swipe at 500 px/s or faster, or past 35% of the width; it used the speed of the last touch move) and the image lightbox's swipe (now also a short fast swipe; 48px still moves) use it. The BottomSheet, the navigation drawer and toast swipes are decided by Reka UI and keep its thresholds
|
|
12
|
+
- **No sideways pan on a phone.** The phone layout's content area and page scroller clip `overflow-x` (`clip`, so neither becomes a scroll container), so content wider than the screen can no longer pan the page; inner horizontal scrollers (tables, tab strips) still scroll. Measurement found no overflowing scene at 320 to 430px; the clip is a guard
|
|
13
|
+
- **A tap box at a page's edge no longer pans the page.** An IconButton narrower than 44px on the right edge of a page that is its App's scroller (a page-header "…" in an App shell that makes the PageContent scroll, like the SDK's `.app-page`) grew its tap box past the page, and the page scrolled sideways by about 13px (590/578 at 825x360, 518/506 at 753x279). The page's own sideways clip turns into `hidden` when it scrolls vertically, so it was a scroller again. Now the PageContent body clips too: it reaches over the page's side padding (`--ui-page-bleed`: a negative margin and the same padding inside, so nothing moves), so a box keeps its whole 44px over the gutter and loses only what would lie past the page's edge. The body's width and `max-width` (`readable`, a custom width, the phone's 100%) allow for the bleed. In a DSM window the clip is repeated above the host reset
|
|
14
|
+
- **A sticky row in an App's own nested scroller sticks in it.** A `data-ui-sticky` row inside a scroll container of the App's own in the page's body (a wide layout's side pane) was stuck the page's sticky height down (`--ui-sticky-row-top`) inside its pane, counted in the page's offset, and what scrolled in the pane stopped below the page's header. PageContent now finds the scroll container each row sticks in: rows in the page's scroller work as before; rows in a nested one stack from that scroller's top, are left out of the page's offset, and the scroller gets its own `--ui-sticky-top-offset` (its top padding plus its rows). A nested scroller without rows can be marked `data-ui-sticky-scope` to take no page offset; a fill page's list scroller (`data-ui-page-scroller`) never takes it
|
|
15
|
+
- **The FAB is a circle.** The single-action FAB and the speed dial's `+` are 56px circles (`border-radius: 50%`), keeping their elevation and icon; the speed dial's items stay labelled pills
|
|
16
|
+
- docs/mobile-native.md: a "Phone behaviour contract" section lists what every App gets on a phone, and a "Gesture velocity" section documents the estimator and its thresholds
|
|
17
|
+
- `check:phone-layout`: a `search` scene (a tab page with a search field, a four-way SegmentControl, a grouped list with badges and "…", a FAB, `?fill=1` too); every hide-on-scroll scene checks that the scroller's size is the same with the bars shown and hidden; the FAB is checked to be a 56px circle; a side-pan check measures the document and the page's scrollers at 320, 360, 390 and 430px for every built-in scene, shown and after scrolling. The side-pan check also runs at the short landscape sizes 825x360 and 753x279 and on a `search-edge` scene (a "…" flush with the right edge of a page that is the App's scroller). A swipe that Chrome's synthetic touch gesture does not scroll (a headless Linux gate) falls back to scrolling the page's scroller natively in frame-sized steps, and the run prints how many swipes each way took. `--scene` can repeat. `phone-layout.html?probe=1` adds a momentum probe for a real phone or a simulator (momentum after the lift, size changes of the scroller, the longest frame). The run removes its Chrome profile when it ends
|
|
18
|
+
|
|
19
|
+
## 0.23.4 (2026-11-07)
|
|
20
|
+
|
|
21
|
+
An App's own sticky rows count in the sticky offset. No App change is required; an App that measured its row by hand can mark it instead.
|
|
22
|
+
|
|
23
|
+
- **New `data-ui-sticky` for a row an App keeps at the top of its page** (a sticky audio player on a detail page). PageContent measures each marked row in its body (ResizeObserver; rows that mount later are found) and, while it sticks, adds its height to `--ui-sticky-top-offset`, so `scrollToStart`, `scrollIntoViewWithinScroller`, a `#hash` link and focus land below it instead of under it. The row is stuck right under what sticks above it (`--ui-sticky-row-top`: the covered padding of a padded page scroller, a sticky `PageHeader`, the phone's tabs strip and earlier rows, stacking in document order), with the page header's background. A row whose `position` is not `sticky` is not counted; a row in a nested PageContent counts for that page. An App's own class still wins for `position`, `background` or `z-index`. Before, an App measured the row and added it to the token by hand
|
|
24
|
+
- docs/mobile-native.md (and `nearestScroller`'s doc comment): `nearestScroller` skips a scroller whose content is not taller than it, so a jsdom test defines `scrollHeight` / `clientHeight` on its scroller; a short example shows how
|
|
25
|
+
- `check:phone-layout` adds a `record-player` scene: a detail page with a sticky audio player marked `data-ui-sticky`, at every viewport, where the player sticks right under what sticks above it, counts in the offset, and `scrollToStart` lands a transcript line right below it
|
|
26
|
+
|
|
5
27
|
## 0.23.3 (2026-11-07)
|
|
6
28
|
|
|
7
29
|
Phone fixes: the FAB no longer leaves a blank band on a page whose list scrolls itself, and no focus ring on the drawer's close button after a tap. No App change is required; a DSM window and desktop are unchanged.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { InjectionKey, Ref, WatchSource } from 'vue';
|
|
2
|
-
/**
|
|
3
|
-
export declare const HIDE_AFTER_PX
|
|
4
|
-
/**
|
|
5
|
-
export declare const SHOW_AFTER_PX
|
|
2
|
+
/** A slow scroll down this far hides the bars (GESTURE_THRESHOLDS.hideDistance). */
|
|
3
|
+
export declare const HIDE_AFTER_PX: 48;
|
|
4
|
+
/** A slow scroll up this far brings them back (GESTURE_THRESHOLDS.showDistance). */
|
|
5
|
+
export declare const SHOW_AFTER_PX: 24;
|
|
6
6
|
/**
|
|
7
7
|
* Which bars a scroll hides: `all` (the phone bar, the tab bar and the FAB)
|
|
8
8
|
* when the page scrolls in an area under the bar, `bottom` (the tab bar and
|
|
@@ -12,26 +12,39 @@ export type BarsHidden = false | 'all' | 'bottom';
|
|
|
12
12
|
export interface HideOnScrollOptions {
|
|
13
13
|
/** The layout's content column: scrolls in it (or of the document) count. */
|
|
14
14
|
column: Readonly<Ref<HTMLElement | null>>;
|
|
15
|
-
/**
|
|
16
|
-
|
|
15
|
+
/** Whether the phone bar lies over the page's scroller (AppLayout `data-ui-bars-overlay`), so it can slide away. */
|
|
16
|
+
overlay: () => boolean;
|
|
17
17
|
/** On only for a phone with the feature on (no page opted out). */
|
|
18
18
|
enabled: () => boolean;
|
|
19
19
|
/** Never hide while this holds (the speed dial or the navigation drawer is open). */
|
|
20
20
|
blocked: () => boolean;
|
|
21
21
|
/** The room the bars take: a page that scrolls less than this keeps them, or hiding them would end its scroll. */
|
|
22
22
|
room: () => number;
|
|
23
|
-
/** How long hiding or showing moves the layout; scrolls in that time are its own, not the user's. */
|
|
24
|
-
settleMs: () => number;
|
|
25
23
|
/** Each change shows the bars again (a new page). */
|
|
26
24
|
reset: WatchSource[];
|
|
27
25
|
}
|
|
26
|
+
/** What a decision reports besides the state: the scroll's speed (px/s) when it was made, for the motion's length. */
|
|
27
|
+
export interface HideOnScrollState {
|
|
28
|
+
hidden: Ref<BarsHidden>;
|
|
29
|
+
/** The scroll velocity (px/s) of the last change; 0 for a change not made by a scroll. */
|
|
30
|
+
velocity: Ref<number>;
|
|
31
|
+
}
|
|
28
32
|
/**
|
|
29
|
-
* Hide-on-scroll for the phone shell
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
33
|
+
* Hide-on-scroll for the phone shell. The page scrolls natively; each scroll
|
|
34
|
+
* event feeds the shared velocity estimator (interaction/velocity.ts) with
|
|
35
|
+
* the scroller's position, and the decision reads the velocity and the
|
|
36
|
+
* distance travelled in one direction, never one event's delta:
|
|
37
|
+
* - a flick down at GESTURE_THRESHOLDS.flick or faster hides the bars at
|
|
38
|
+
* once, a flick up that fast shows them at once;
|
|
39
|
+
* - a slower scroll hides them after `hideDistance` down and shows them after
|
|
40
|
+
* `showDistance` up, so a slow or wobbly drag does not flicker them;
|
|
41
|
+
* - the top of the page and a scroll that ends at its bottom show them.
|
|
42
|
+
* The bars move by transform only (adaptive.css), so a decision never
|
|
43
|
+
* changes the scroller's size or position and the scroll keeps its momentum.
|
|
44
|
+
* It never hides while a sheet, dialog, the speed dial or the drawer is
|
|
45
|
+
* open, or while a text field has focus, and focus moving into the bars
|
|
46
|
+
* shows them.
|
|
34
47
|
*/
|
|
35
|
-
export declare function useHideOnScroll(options: HideOnScrollOptions):
|
|
48
|
+
export declare function useHideOnScroll(options: HideOnScrollOptions): HideOnScrollState;
|
|
36
49
|
/** A page's opt-out from hide-on-scroll (PageContent `hide-on-scroll="false"`): counts while it is mounted. */
|
|
37
50
|
export declare const APP_KEEP_BARS_KEY: InjectionKey<() => () => void>;
|
package/dist/index.d.ts
CHANGED
|
@@ -307,8 +307,8 @@ export { computeOverflowFit, ITEM_GAP } from './components/composite/overflow-to
|
|
|
307
307
|
export type { OverflowToolbarItem, OverflowToolbarMenuEntry, OverflowToolbarMoreMenu, OverflowToolbarMoreState, OverflowToolbarMoreText, } from './components/composite/overflow-toolbar/types';
|
|
308
308
|
export { MOTION_OFF_SELECTOR, motionMs, motionOff, transitionMs, useInsertedKeys, useSwapMotion } from './lib/motion';
|
|
309
309
|
export type { InsertedKeysOptions, MotionToken } from './lib/motion';
|
|
310
|
-
export { COMPOSITION_END_GRACE_MS, createCompositionGuard, createEditSession, createFocusOwner, DIALOG_SELECTOR, DRAG_THRESHOLD_PX, dropIndexAt, editSessionOutside, focusSync, IGNORE_OUTSIDE_ATTR, IGNORE_OUTSIDE_SELECTOR, isComposingKey, isInForeignDialog, isPrintableKey, outsideLayerCount, pathInForeignDialog, registerOutsideLayer, swallowNextClick, trackPointerGesture, useEditSession, useOutsideLayer, usePointerGesture, } from './interaction';
|
|
311
|
-
export type { CompositionGuard, EditCommitResult, EditSession, EditSessionOptions, EditSessionState, FocusOwner, GestureAxis, GestureCancelReason, OutsideLayer, OutsideLayerOptions, OutsideResult, PointerGesture, PointerGestureOptions, PointerGestureState, } from './interaction';
|
|
310
|
+
export { COMPOSITION_END_GRACE_MS, createCompositionGuard, createEditSession, createFocusOwner, DIALOG_SELECTOR, DRAG_THRESHOLD_PX, dropIndexAt, editSessionOutside, focusSync, GESTURE_THRESHOLDS, IGNORE_OUTSIDE_ATTR, IGNORE_OUTSIDE_SELECTOR, isComposingKey, isInForeignDialog, isPrintableKey, outsideLayerCount, pathInForeignDialog, registerOutsideLayer, swallowNextClick, swipeCommits, trackPointerGesture, useEditSession, useOutsideLayer, usePointerGesture, VELOCITY_MAX_SAMPLES, VELOCITY_REVERSAL_PX, VELOCITY_WINDOW_MS, VelocityEstimator, velocityDuration, } from './interaction';
|
|
311
|
+
export type { CompositionGuard, EditCommitResult, EditSession, EditSessionOptions, EditSessionState, FocusOwner, GestureAxis, GestureCancelReason, OutsideLayer, OutsideLayerOptions, OutsideResult, PointerGesture, PointerGestureOptions, PointerGestureState, VelocitySample, } from './interaction';
|
|
312
312
|
export { AUTO_SCROLL_MAX_SPEED, AUTO_SCROLL_MIN_ZONE_PX, AUTO_SCROLL_ZONE_PERCENT, createAutoScroll, createDragKit, DND_GHOST_ATTR, DND_INDEX_ATTR, DND_KEY_ATTR, DND_LANE_ATTR, DND_STATE_ATTR, edgeSpeed, gridGeometry as dragGridGeometry, listGeometry as dragListGeometry, measuredGeometry as dragMeasuredGeometry, SWAP_LOCK_MS, TOUCH_HOLD_MS, TOUCH_HOLD_TOLERANCE_PX, useDragKit, } from './dnd';
|
|
313
313
|
export type { Arrangement as DragArrangement, AutoScroll, AutoScrollOptions, Axis as DragAxis, DragDrop, DragItemBinding, DragKey, DragKit, DragKitOptions, DragLaneOptions, DragMode, DragPhase, DragPlaceholder, DragPosition, DragState, LaneGeometry as DragLaneGeometry, LaneLayout as DragLaneLayout, Rect as DragRect, UseDragKit, } from './dnd';
|
|
314
314
|
export type { UiLocale } from './i18n/locale';
|