Scroll the full-screen carousel down the page, as M3's does
Plan step 19, containment.md C-11. The full-screen layout was another horizontal row with a 28px corner and a mask, where M3's "shows one edge-to-edge large item at a time and scrolls vertically". It is now a vertical scroll-snap column: items fill the row with no corner and no mask, 16dp apart, no end padding, the previous/next buttons point up and down and the arrow keys are Up and Down. The row is capped at the 840px medium window, which with the portrait rule in the header comment is as far as CSS can hold M3's "compact and medium, portrait only". The ported FullScreenCarouselStrategy keylines go with it: nothing measures them any more. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
This commit is contained in:
co-authored by
Claude Opus 5
parent
9875ba156e
commit
5e58dd5449
@@ -19,15 +19,28 @@
|
||||
the surface inside is clipped by `--material-carousel-inset` from both sides with M3's
|
||||
extra-large corner (28px, CarouselDefaults' item shape) and moved by
|
||||
`--material-carousel-shift`, both written by resources/js/carousel.js. Without script the
|
||||
item shows unmasked, at its `item-width`. Only inside `<x-carousel>`. --}}
|
||||
item shows unmasked, at its `item-width`. In a `full-screen` carousel it is none of that: the
|
||||
item fills the row, edge to edge, with no corner and no mask. Only inside `<x-carousel>`. --}}
|
||||
|
||||
@props([
|
||||
'label' => null,
|
||||
])
|
||||
|
||||
{{-- The layout of the `<x-carousel>` around it: the full-screen one is a vertical row of
|
||||
edge-to-edge items, which M3 gives no corner and no mask. --}}
|
||||
@aware([
|
||||
'layout' => 'multi-browse',
|
||||
])
|
||||
|
||||
@php
|
||||
$vertical = $layout === 'full-screen';
|
||||
@endphp
|
||||
|
||||
<div {{ $attributes
|
||||
->class([
|
||||
'focus-ring relative h-full w-(--material-carousel-slot) max-w-full shrink-0 snap-start snap-always rounded-corner-xl focus-visible:-outline-offset-3',
|
||||
'focus-ring relative h-full shrink-0 snap-start snap-always focus-visible:-outline-offset-3',
|
||||
'w-full' => $vertical,
|
||||
'w-(--material-carousel-slot) max-w-full rounded-corner-xl' => ! $vertical,
|
||||
])
|
||||
->merge([
|
||||
'role' => 'group',
|
||||
@@ -38,7 +51,10 @@
|
||||
]) }}>
|
||||
<div
|
||||
data-material-carousel-surface
|
||||
class="relative size-full overflow-hidden rounded-corner-xl bg-surface-container-highest text-on-surface translate-x-(--material-carousel-shift) [clip-path:inset(0_var(--material-carousel-inset,0px)_round_var(--md-sys-shape-corner-xl))]"
|
||||
@class([
|
||||
'relative size-full overflow-hidden bg-surface-container-highest text-on-surface',
|
||||
'rounded-corner-xl translate-x-(--material-carousel-shift) [clip-path:inset(0_var(--material-carousel-inset,0px)_round_var(--md-sys-shape-corner-xl))]' => ! $vertical,
|
||||
])
|
||||
>
|
||||
<div data-material-carousel-content class="size-full [&>img]:size-full [&>img]:object-cover">
|
||||
{{ $slot }}
|
||||
|
||||
@@ -19,8 +19,13 @@
|
||||
does.
|
||||
- `uncontained`: items keep `item-width`; the one cut off at the end narrows as it leaves —
|
||||
HorizontalUncontainedCarousel. No snapping, as Compose's uncontained fling.
|
||||
- `full-screen`: one item the width of the carousel at a time
|
||||
(FullScreenCarouselStrategy).
|
||||
- `full-screen`: one edge-to-edge item at a time, scrolled **vertically** — M3: "this layout
|
||||
works best with content that is taller than it is wide, and scrolls vertically. It only
|
||||
works in portrait orientation in compact and medium breakpoints. Don't use this layout in
|
||||
landscape orientation." Items have no corner and no mask, 16px apart, no end padding, and
|
||||
the row is never wider than the 840px medium window it is meant for. `item-width` and
|
||||
`padding` do not apply; `height` is the height of each item, so give it the room a
|
||||
portrait image wants.
|
||||
`item-width` takes pixels or any CSS length. `height` is the items' height (205px, Compose's
|
||||
sample). `padding` is Compose's `contentPadding` in pixels: the first and last items rest
|
||||
that far in from the edges while items in between scroll to them. M3's specs table gives
|
||||
@@ -66,6 +71,7 @@
|
||||
|
||||
@php
|
||||
$layout = in_array($layout, ['multi-browse', 'hero', 'uncontained', 'full-screen'], true) ? $layout : 'multi-browse';
|
||||
$vertical = $layout === 'full-screen';
|
||||
$controls = $controls === null ? null : filter_var($controls, FILTER_VALIDATE_BOOL);
|
||||
$label ??= __('Carousel');
|
||||
$scrollerId = 'material-carousel-'.\Illuminate\Support\Str::lower(\Illuminate\Support\Str::random(10));
|
||||
@@ -82,8 +88,9 @@
|
||||
'hero' => $cssLength($itemWidth),
|
||||
'full-screen' => null,
|
||||
};
|
||||
// The full-screen item is `h-full w-full`: it takes the row, not a slot.
|
||||
$slotWidth = match (true) {
|
||||
$layout === 'full-screen' => '100%',
|
||||
$vertical => null,
|
||||
$preferredWidth === null => 'calc(100% - 64px)',
|
||||
default => $preferredWidth,
|
||||
};
|
||||
@@ -106,8 +113,8 @@
|
||||
// The specs table's leading and trailing padding: 16dp for multi-browse and hero, leading
|
||||
// only for uncontained, none for full-screen, which is edge to edge.
|
||||
$padding = max(0, (float) $padding);
|
||||
$paddingStart = $layout === 'full-screen' ? 0.0 : $padding;
|
||||
$paddingEnd = in_array($layout, ['uncontained', 'full-screen'], true) ? 0.0 : $padding;
|
||||
$paddingStart = $vertical ? 0.0 : $padding;
|
||||
$paddingEnd = $vertical || $layout === 'uncontained' ? 0.0 : $padding;
|
||||
|
||||
$attributes = $attributes
|
||||
->class('relative')
|
||||
@@ -118,7 +125,7 @@
|
||||
'data-padding-end' => (string) $paddingEnd,
|
||||
'style' => implode('; ', array_filter([
|
||||
$preferredWidth ? "--material-carousel-item-width: {$preferredWidth}" : null,
|
||||
"--material-carousel-slot: {$slotWidth}",
|
||||
$slotWidth ? "--material-carousel-slot: {$slotWidth}" : null,
|
||||
'--material-carousel-height: '.($cssLength($height) ?? '205px'),
|
||||
])),
|
||||
], fn ($value): bool => $value !== null));
|
||||
@@ -137,11 +144,11 @@
|
||||
aria-roledescription="{{ __('carousel') }}"
|
||||
aria-label="{{ $label }}"
|
||||
@class([
|
||||
'flex gap-2 overflow-x-auto overflow-y-hidden overscroll-x-contain',
|
||||
'flex',
|
||||
'[scrollbar-width:none] [&::-webkit-scrollbar]:hidden',
|
||||
'h-(--material-carousel-height)' => $layout === 'full-screen',
|
||||
'h-[calc(var(--material-carousel-height)+1rem)] py-2' => $layout !== 'full-screen',
|
||||
'snap-x snap-mandatory' => $layout !== 'uncontained',
|
||||
'mx-auto h-(--material-carousel-height) max-w-210 snap-y snap-mandatory flex-col gap-4 overflow-x-hidden overflow-y-auto overscroll-y-contain' => $vertical,
|
||||
'h-[calc(var(--material-carousel-height)+1rem)] gap-2 overflow-x-auto overflow-y-hidden overscroll-x-contain py-2' => ! $vertical,
|
||||
'snap-x snap-mandatory' => ! $vertical && $layout !== 'uncontained',
|
||||
])
|
||||
>
|
||||
{!! $slides !!}
|
||||
@@ -154,22 +161,22 @@
|
||||
'flex' => $controls === true,
|
||||
])>
|
||||
<x-livewire-material::button
|
||||
icon="chevron_left"
|
||||
:icon="$vertical ? 'keyboard_arrow_up' : 'chevron_left'"
|
||||
variant="tonal"
|
||||
:tooltip="__('Previous')"
|
||||
aria-controls="{{ $scrollerId }}"
|
||||
x-ref="previous"
|
||||
x-on:click="previous()"
|
||||
class="rtl:-scale-x-100"
|
||||
:class="$vertical ? '' : 'rtl:-scale-x-100'"
|
||||
/>
|
||||
<x-livewire-material::button
|
||||
icon="chevron_right"
|
||||
:icon="$vertical ? 'keyboard_arrow_down' : 'chevron_right'"
|
||||
variant="tonal"
|
||||
:tooltip="__('Next')"
|
||||
aria-controls="{{ $scrollerId }}"
|
||||
x-ref="next"
|
||||
x-on:click="next()"
|
||||
class="rtl:-scale-x-100"
|
||||
:class="$vertical ? '' : 'rtl:-scale-x-100'"
|
||||
/>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@@ -55,8 +55,8 @@
|
||||
@endforeach
|
||||
</x-carousel>
|
||||
BLADE,
|
||||
'Full-screen' => <<<'BLADE'
|
||||
<x-carousel layout="full-screen" label="Wallpapers" height="240" :controls="true">
|
||||
'Full-screen: one item at a time, scrolled down' => <<<'BLADE'
|
||||
<x-carousel layout="full-screen" label="Wallpapers" height="320" :controls="true">
|
||||
@foreach ([
|
||||
['Morning', 'very-sunny', 'bg-linear-to-br from-primary-container to-tertiary-container text-on-primary-container'],
|
||||
['Noon', 'clam-shell', 'bg-linear-to-br from-secondary-container to-primary-container text-on-secondary-container'],
|
||||
|
||||
Reference in New Issue
Block a user