From 44aeca414b021e69c326152725dce37067982368 Mon Sep 17 00:00:00 2001 From: Simon Hamp Date: Sat, 17 Oct 2026 02:07:40 +0100 Subject: [PATCH] Document pager tabs and report-on-settle for mobile-ui 0.9 The pager page gains report-on-settle, for screens whose render is too big to land during a swipe, and a Tabs section: the native tab row a pager can draw above its pages, how it follows the swipe, reselect, the scrollable layout and the tab-* styling attributes. Badged mobile-ui 0.9, which also needs 0.8 and 0.9 in the released versions. Co-Authored-By: Claude Opus 5.5 --- config/docs.php | 2 +- .../docs/mobile/4/edge-components/pager.md | 123 ++++++++++++++++++ 2 files changed, 124 insertions(+), 1 deletion(-) diff --git a/config/docs.php b/config/docs.php index fa3fabd0..f5e38f4c 100644 --- a/config/docs.php +++ b/config/docs.php @@ -86,7 +86,7 @@ 'mobile-ui' => [ 'name' => 'nativephp/mobile-ui', 'baseline' => '0.3', - 'released_versions' => ['0.1', '0.2', '0.3', '0.4', '0.5', '0.6', '0.7'], + 'released_versions' => ['0.1', '0.2', '0.3', '0.4', '0.5', '0.6', '0.7', '0.8', '0.9'], ], ], diff --git a/resources/views/docs/mobile/4/edge-components/pager.md b/resources/views/docs/mobile/4/edge-components/pager.md index 149230bf..4602d6fa 100644 --- a/resources/views/docs/mobile/4/edge-components/pager.md +++ b/resources/views/docs/mobile/4/edge-components/pager.md @@ -44,6 +44,12 @@ the pager where that window sits. See [Windowed feeds](#windowed-feeds). this render has not shipped. See [Placeholders](#placeholders). - `horizontal` - Page sideways instead of vertically (optional, boolean, default: `false`). - `on-page-change` - Component method called with `(int $index)` as the current page changes. +- `report-on-settle` - Call `on-page-change` once the swipe comes to rest instead of mid-swipe (optional, boolean, + default: `false`) . See [Pager position](#pager-position). +- `tabs` - One tab label per page, drawn natively above the pages (optional, array) + . See [Tabs](#tabs). +- `tabs-scrollable` - Size the tabs to their labels in a row that scrolls (optional, boolean, default: `false`) +- `on-tab-reselect` - Component method called with `(int $index)` when the tab already showing is tapped - `a11y-label` - Accessibility label (optional) - `a11y-hint` - Accessibility hint (optional) [Android] @@ -86,6 +92,106 @@ public function onPagerPage(int $index): void } ``` +That suits a media feed, where the next video should start loading early. On a screen whose render is large, such as +paged lists, the frame that the change causes can land during the swipe animation and make it stutter. Add +`report-on-settle` to call `on-page-change` only once the +swipe has come to rest. Paged tabs usually want it, because the [tabs](#tabs) already follow the swipe without PHP. + +A page you jump to with `page` is never reported back, because PHP already knows it. + +## Tabs + + + +Pass `tabs` to draw a row of tabs above the pages, one label per page. This is the pattern for paged tabs such as +Following, Latest and Trending: + +@verbatim +```blade + + @foreach ($feeds as $feed) + @include('native.feed-page', ['feed' => $feed]) + @endforeach + +``` +@endverbatim + +The row is native and reads the pager's own scroll position, so none of this waits for PHP: + +- The indicator under the labels follows the swipe frame by frame. +- The highlighted label switches as the swipe passes halfway to the next page. +- Tapping a tab scrolls the pager to that page. `on-page-change` is called when it gets there, the same as for a swipe. +- Tapping the tab that is already showing calls `on-tab-reselect` with its index instead. Use it to take that page + back to its top. + +```php +public function showPage(int $index): void +{ + $this->page = $index; +} + +public function scrollToTop(int $index): void +{ + // A new key on the page's list mounts it afresh, at the top. + $this->listKeys[$index]++; +} +``` + +By default the tabs share the row equally, which suits two to four short labels. With more, add `tabs-scrollable`: +each tab is as wide as its label and the row scrolls sideways, keeping the current tab in view as you swipe. + +A tab can also be an array with an `a11y-label`, for when VoiceOver and TalkBack should say more than the label shows: + +@verbatim +```blade +:tabs="[['label' => 'Mentions', 'a11y-label' => 'Mentions, 3 new'], 'Follows']" +``` +@endverbatim + +The tabs are announced as tabs, with the selected one marked. + +### Styling tabs + +The row takes its colors from the [theme](../digging-deeper/theming): `on-surface` for the active label, +`on-surface-variant` for the others, `primary` for the indicator and `outline-variant` for the hairline under the row. +Its background is the pager's own `bg-*`. These attributes change it for one pager: + +- `tab-active-color` - Active label color +- `tab-color` - Other labels' color +- `tab-indicator-color` - Indicator color +- `tab-divider-color` - Hairline color. `transparent` hides it. +- `tab-font` - Label font: a bundled font's file name or a `fonts` alias (default: the theme's font) +- `tab-font-size` - Label size in points (default: the theme's `font-md`) +- `tab-indicator-height` - Indicator thickness in points (default: `3`) +- `tab-padding-x` - Space either side of each label (default: `12`) +- `tab-padding-top` - Space above the labels (default: `14`) +- `tab-padding-bottom` - Space between the labels and the indicator (default: `11`) +- `tab-edge-padding` - Space before the first tab and after the last in a scrollable row (default: `4`) + +Colors take any [color value](../digging-deeper/theming#color-values), and `theme-` for any token in your +theme, custom ones included. A theme token also brings its dark mode shade: + +@verbatim +```blade + +``` +@endverbatim + ## Placeholders `placeholders` is an image URL per loaded page index, for every loaded item and not just the shipped window. A page @@ -242,6 +348,13 @@ Pager::make($page1, $page2, $page3) ->hasMore() ->placeholders($posters) ->onPageChange('onPagerPage'); + +Pager::make(...$pages) + ->horizontal() + ->reportOnSettle() + ->tabs(['Following', 'Latest', 'Trending']) + ->onPageChange('showPage') + ->onTabReselect('scrollToTop'); ``` - `make(Element ...$children)` - Create a pager whose children are the pages @@ -251,6 +364,16 @@ Pager::make($page1, $page2, $page3) - `hasMore(bool $value = true)` - Append the trailing loading page - `placeholders(array $urls)` - One image URL per loaded page index - `onPageChange(string $method)` - Page-change handler +- `reportOnSettle(bool $value = true)` - Report the page once the swipe comes to rest +- `tabs(array $tabs)` - One tab per page, each a label or `['label' => ..., 'a11y-label' => ...]` +- `tabsScrollable(bool $value = true)` - Size the tabs to their labels in a row that scrolls +- `onTabReselect(string $method)` - Handler for a tap on the tab already showing +- `tabActiveColor(string $color)`, `tabColor(string $color)`, `tabIndicatorColor(string $color)`, + `tabDividerColor(string $color)` - Tab colors +- `tabFont(string $font)`, `tabFontSize(float $size)` - Tab label font +- `tabIndicatorHeight(float $height)` - Indicator thickness +- `tabPadding(?float $x = null, ?float $top = null, ?float $bottom = null)` - Space around the labels +- `tabEdgePadding(float $padding)` - Space at the ends of a scrollable row - `a11yLabel(string $value)` - Accessibility label - `a11yHint(string $value)` - Accessibility hint