store user login
This commit is contained in:
135
src/features/store-admin/Drawer.tsx
Normal file
135
src/features/store-admin/Drawer.tsx
Normal file
@@ -0,0 +1,135 @@
|
||||
import { useEffect, type ReactNode } from 'react';
|
||||
import { HStack } from '@astryxdesign/core/HStack';
|
||||
import { Text } from '@astryxdesign/core/Text';
|
||||
import { VStack } from '@astryxdesign/core/VStack';
|
||||
import { X } from 'lucide-react';
|
||||
|
||||
/**
|
||||
* The side drawer, matched to `OrderDetailDrawer`.
|
||||
*
|
||||
* That one is the site's drawer and every measurement here is taken from it:
|
||||
* the flex-end wrapper at z-index 70, a full-bleed scrim BUTTON rather than a
|
||||
* div (so it is reachable and announces itself), a 460px panel capped at 92vw,
|
||||
* `-24px 0 56px -12px` of shadow, a STICKY header on a hairline, and a 30px
|
||||
* rounded close button on `--color-surface-sunken`.
|
||||
*
|
||||
* Extracted rather than copied a third time. The drawers I added had drifted —
|
||||
* different scrim opacity, different shadow, a bare `X`, a header that scrolled
|
||||
* away — which is exactly how two screens stop looking like one product.
|
||||
*/
|
||||
export interface DrawerProps {
|
||||
title: string;
|
||||
/** One line under the title. */
|
||||
subtitle?: string;
|
||||
/** Sits beside the title — a status chip, usually. */
|
||||
titleAside?: ReactNode;
|
||||
/**
|
||||
* Panel width in px. 460 is the site default; only widen for content that
|
||||
* genuinely cannot fold, such as a grid of product cards.
|
||||
*/
|
||||
width?: number;
|
||||
/** Renders the body unpadded, for panels that manage their own layout. */
|
||||
isBare?: boolean;
|
||||
onClose: () => void;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export function Drawer({
|
||||
title,
|
||||
subtitle,
|
||||
titleAside,
|
||||
width = 460,
|
||||
isBare,
|
||||
onClose,
|
||||
children,
|
||||
}: DrawerProps) {
|
||||
useEffect(() => {
|
||||
const onKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Escape') onClose();
|
||||
};
|
||||
window.addEventListener('keydown', onKeyDown);
|
||||
return () => window.removeEventListener('keydown', onKeyDown);
|
||||
}, [onClose]);
|
||||
|
||||
return (
|
||||
<div
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-label={title}
|
||||
style={{ position: 'fixed', inset: 0, zIndex: 70, display: 'flex', justifyContent: 'flex-end' }}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Close"
|
||||
onClick={onClose}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
inset: 0,
|
||||
border: 0,
|
||||
background: 'rgb(15 23 42 / .32)',
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
className="dialog-panel"
|
||||
style={{
|
||||
position: 'relative',
|
||||
width,
|
||||
maxWidth: '92vw',
|
||||
height: '100%',
|
||||
overflowY: 'auto',
|
||||
background: 'var(--color-surface)',
|
||||
borderLeft: '1px solid var(--color-line)',
|
||||
boxShadow: '-24px 0 56px -12px rgb(15 23 42 / .18)',
|
||||
}}
|
||||
>
|
||||
<HStack
|
||||
justify="between"
|
||||
align="start"
|
||||
gap={1}
|
||||
style={{
|
||||
position: 'sticky',
|
||||
top: 0,
|
||||
zIndex: 1,
|
||||
padding: 20,
|
||||
background: 'var(--color-surface)',
|
||||
borderBottom: '1px solid var(--color-line)',
|
||||
}}
|
||||
>
|
||||
<VStack gap={0.5} style={{ minWidth: 0 }}>
|
||||
<Text type="label" size="lg" weight="semibold">
|
||||
{title}
|
||||
</Text>
|
||||
{subtitle ? (
|
||||
<Text type="body" size="sm" color="secondary">
|
||||
{subtitle}
|
||||
</Text>
|
||||
) : null}
|
||||
{titleAside}
|
||||
</VStack>
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Close"
|
||||
onClick={onClose}
|
||||
style={{
|
||||
width: 30,
|
||||
height: 30,
|
||||
borderRadius: 10,
|
||||
border: 0,
|
||||
flex: 'none',
|
||||
display: 'grid',
|
||||
placeItems: 'center',
|
||||
background: 'var(--color-surface-sunken)',
|
||||
color: 'var(--color-ink-3)',
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
>
|
||||
<X size={15} />
|
||||
</button>
|
||||
</HStack>
|
||||
|
||||
{isBare ? children : <VStack gap={3} padding={2}>{children}</VStack>}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user