{{-- An M3 side sheet: detail or controls that slide in from the edge over a scrim; on a phone it is the whole screen. The open state is the Livewire property in `wire:model` (entangled live, so a detail held in the URL follows a close) — a flag or an id — and closing writes back `false` or `null`. Without `wire:model` it reads and writes `open` in the Alpine scope around it. `close()` is in scope for anything inside the sheet, so it can draw its own close button. As a sheet it is modal: the page inert and still (`x-trap.inert.noscroll`), and it enters on emphasized decelerate rather than a spring — a sheet anchored to the edge that overshot would open a gap. M3's modal side sheet (docs/reference/m3/components-actions-communication- containment.md § Side sheets; Compose has no side-sheet token file): surface-container-low, a large corner on its inner edge, elevation 1; `side` `end` (the default) or `start`, mirrored in a right-to-left document; `width` from `medium`, 400px (M3's 400dp cap) unless it says otherwise — a compact window gets the full-bleed sheet. As a **standard** side sheet (`standard`, from `expanded`) it is M3's other variant: co-planar with the content rather than over it — no scrim, no focus trap, nothing inert, 0dp elevation (material-components-android's `SideSheet.md`: "standard side sheet elevation = 0dp, coplanar"), `surface` rather than surface-container-low, no corner, and an outline-variant divider down its inner edge in place of the scrim. It sits in the page flow beside the content, spans the window's height and scrolls on its own; below `expanded` (840px) it is the modal sheet — M3 calls the standard sheet "supplementary surfaces mainly for medium to expanded breakpoints" and the modal one "preferred at compact breakpoints", and the switch sits at `expanded` rather than `medium` because M3 also caps a side sheet at 400dp and a 600px window has too little left beside one. Opening one shrinks the body beside it and closing one gives the room back (M3's side sheets, "Adaptive"): the root's inline size, and a negative margin as wide as its flex parent's gap, spring between none and the sheet's width while the sheet fades, and `data-md-drawer-collapsed` — which takes the closed sheet out of the layout — waits for that exit (`closing`, `settle()`) instead of cutting it off on its first frame. It binds `collapsed`, which only `settle()` and the window's width write: a binding that read `open` itself would run before `settle()` in the same flush and, already queued, not run again once `closing` changed. Nothing moves while the page loads: `data-md-drawer-settled` comes two frames after the state Alpine starts with, and the standard sheet has no transitions until then, so a sheet that starts open stands open rather than growing in. For the second pane of a list-detail layout use `` instead (step 35's canonical layout); the two are not the same thing — a pane shows what the list beside it selected, a standard sheet is supplementary content (filters, details, a list of actions) beside the primary content, divided from it by a rule rather than being part of its selection. M3 **requires** a close affordance on a side sheet — without one nobody can predict the sheet's open/close flow or tell whether it is transient or permanent (docs/reference/m3/components-actions-communication-containment.md § Side sheets → Accessibility, C-04) — so `with-close-button` is on by default, and `:with-close-button="false"` is ignored where nothing else closes the sheet: Escape off, the scrim off, or `standard`, which has neither from `expanded`. maryUI's API, kept: `title`, `subtitle`, `separator`, `with-close-button`, `close-on-escape`, `without-backdrop-close`, `right` (ignored; use `side`), and an `actions` slot. The root renders `data-md-drawer`, `data-md-open` and `data-md-standard`; the sheet is `data-md-drawer-sheet` with `data-md-side`, its parts `data-md-drawer-head`/-head-row/ -heading/-title/-subtitle/-close/-body/-actions — drawn by resources/css/components/ drawer.css, which imports button.css and divider.css for what the view renders. --}} @props([ 'title' => null, 'subtitle' => null, 'separator' => false, 'side' => 'end', 'right' => true, 'withCloseButton' => true, 'closeOnEscape' => true, 'withoutBackdropClose' => false, 'width' => '400px', 'standard' => false, ]) @php $model = $attributes->wire('model')->value() ?: null; $id = $attributes->get('id') ?? 'material-sheet-'.substr(md5($model.'|'.$title), 0, 10); $side = $side === 'start' ? 'start' : 'end'; $standard = (bool) $standard; // The side-sheet specs table caps the sheet at 400dp, which is where `width` already starts; // a wider one is the modal sheet's to take, not the co-planar standard sheet's. $sheetWidth = $standard ? "min({$width}, 400px)" : $width; // M3 requires a close affordance; the prop can only ever add one, never take away the last // way out of the sheet. A standard sheet keeps no scrim and no trap from `expanded`, and // Escape leaves it open there, so it always draws one. $closeButton = $withCloseButton || ! $closeOnEscape || $withoutBackdropClose || $standard; @endphp
{{-- `md-transition` is a class name only to turn on Alpine's CSS transition: `x-show` then keeps the scrim and the sheet displayed for their computed transition-duration (drawer.css's tokens) before hiding them, so they fade and slide out rather than vanish — Firefox does not transition `display`, even with `allow-discrete`. Both stages, so reopening during the exit cancels the pending hide. Nothing styles the class. --}}