feat: Response mobile layout support v1
Some checks failed
Queue Release Build / prepare (push) Successful in 30s
Deploy Web Apps / deploy (push) Successful in 7m8s
Queue Release Build / build-windows (push) Successful in 28m11s
Queue Release Build / finalize (push) Has been cancelled
Queue Release Build / build-linux (push) Has started running
Some checks failed
Queue Release Build / prepare (push) Successful in 30s
Deploy Web Apps / deploy (push) Successful in 7m8s
Queue Release Build / build-windows (push) Successful in 28m11s
Queue Release Build / finalize (push) Has been cancelled
Queue Release Build / build-linux (push) Has started running
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
import {
|
||||
Component,
|
||||
HostListener,
|
||||
computed,
|
||||
input,
|
||||
output,
|
||||
signal
|
||||
} from '@angular/core';
|
||||
import { ThemeNodeDirective } from '../../../domains/theme';
|
||||
|
||||
/**
|
||||
* Mobile bottom-sheet container.
|
||||
*
|
||||
* Renders a backdrop + a panel anchored to the bottom of the viewport that slides up from below.
|
||||
* Intended for use on phone-sized viewports where context menus, action sheets, and confirmation
|
||||
* dialogs are better presented as bottom sheets than as floating popovers or centered modals.
|
||||
*
|
||||
* The component is layout-only: callers project their content via `<ng-content>` and listen for
|
||||
* the `dismissed` output to close themselves. Drag-to-dismiss is supported via touch gestures.
|
||||
*
|
||||
* Desktop callers should not render this component; use the original popover/modal layout instead.
|
||||
*
|
||||
* @example
|
||||
* ```html
|
||||
* @if (isMobile()) {
|
||||
* <app-bottom-sheet (dismissed)="close()">
|
||||
* <my-menu-items />
|
||||
* </app-bottom-sheet>
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
@Component({
|
||||
selector: 'app-bottom-sheet',
|
||||
standalone: true,
|
||||
imports: [ThemeNodeDirective],
|
||||
templateUrl: './bottom-sheet.component.html',
|
||||
styleUrl: './bottom-sheet.component.scss'
|
||||
})
|
||||
export class BottomSheetComponent {
|
||||
/** Optional title rendered at the top of the sheet. Omit for an unlabeled action sheet. */
|
||||
readonly title = input<string | null>(null);
|
||||
|
||||
/** Optional ARIA label when no visible title is provided. */
|
||||
readonly ariaLabel = input<string>('Menu');
|
||||
|
||||
/** Emits when the user dismisses the sheet (backdrop tap, swipe-down, or Escape). */
|
||||
readonly dismissed = output<undefined>();
|
||||
|
||||
/** Pixels the sheet is currently dragged downward. Drives the translate transform. */
|
||||
protected readonly dragOffset = signal(0);
|
||||
|
||||
/** Visible transform offset in CSS pixels (only positive values move the sheet down). */
|
||||
protected readonly translateY = computed(() => Math.max(0, this.dragOffset()));
|
||||
|
||||
private touchStartY: number | null = null;
|
||||
|
||||
@HostListener('document:keydown.escape')
|
||||
protected onEscape(): void {
|
||||
this.dismissed.emit(undefined);
|
||||
}
|
||||
|
||||
protected onBackdropClick(): void {
|
||||
this.dismissed.emit(undefined);
|
||||
}
|
||||
|
||||
protected onHandleTouchStart(event: TouchEvent): void {
|
||||
const touch = event.touches[0];
|
||||
|
||||
if (!touch) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.touchStartY = touch.clientY;
|
||||
}
|
||||
|
||||
protected onHandleTouchMove(event: TouchEvent): void {
|
||||
if (this.touchStartY === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const touch = event.touches[0];
|
||||
|
||||
if (!touch) {
|
||||
return;
|
||||
}
|
||||
|
||||
const delta = touch.clientY - this.touchStartY;
|
||||
|
||||
// Only allow dragging downward; ignore upward drags.
|
||||
this.dragOffset.set(Math.max(0, delta));
|
||||
}
|
||||
|
||||
protected onHandleTouchEnd(): void {
|
||||
// Dismiss if the user dragged the sheet down by more than 80px; otherwise snap back.
|
||||
if (this.dragOffset() > 80) {
|
||||
this.dismissed.emit(undefined);
|
||||
}
|
||||
|
||||
this.touchStartY = null;
|
||||
this.dragOffset.set(0);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user