@windmorph/react 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/checkbox/checkbox-tokens.d.ts +1 -1
- package/dist/components/date-picker/date-picker-styles.d.ts +6 -6
- package/dist/components/dialog/Dialog.d.ts +4 -2
- package/dist/components/dialog/Dialog.js +2 -2
- package/dist/components/dialog/Dialog.js.map +1 -1
- package/dist/components/icon-button/IconButton.d.ts +3 -0
- package/dist/components/icon-button/IconButton.js +3 -0
- package/dist/components/icon-button/IconButton.js.map +1 -1
- package/dist/components/navigation/nav-item-styles.js +4 -4
- package/dist/components/navigation/nav-item-styles.js.map +1 -1
- package/dist/components/sheet/SideSheet.d.ts +2 -0
- package/dist/components/sheet/SideSheet.js +1 -1
- package/dist/components/sheet/SideSheet.js.map +1 -1
- package/dist/components/snackbar/snackbar-tokens.d.ts +1 -1
- package/dist/primitives/ButtonBase.js +1 -0
- package/dist/primitives/ButtonBase.js.map +1 -1
- package/dist/styles/utilities.css +8 -0
- package/dist/styles.compiled.css +1 -1
- package/dist/theme/ThemeProvider.js +17 -0
- package/dist/theme/ThemeProvider.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SideSheet.js","names":["Overlay"],"sources":["../../../src/components/sheet/SideSheet.tsx"],"sourcesContent":["'use client';\n\nimport {\n useContext,\n useEffect,\n useId,\n useLayoutEffect,\n useRef,\n useState,\n type ComponentPropsWithRef,\n type CSSProperties,\n type FocusEvent,\n type ReactElement,\n type ReactNode,\n type Ref,\n} from 'react';\nimport {\n mergeProps,\n useDialog,\n useModalOverlay,\n useObjectRef,\n type AriaDialogProps,\n} from 'react-aria';\nimport { useOverlayTriggerState, type OverlayTriggerState } from 'react-stately';\nimport { Overlay } from '../../primitives/Overlay';\nimport { TriggerContext } from '../../primitives/TriggerContext';\nimport { usePresence } from '../../primitives/use-presence';\nimport { closedByOverlayOptions, type ClosedBy } from '../../utils/closed-by';\nimport { cn } from '../../utils/cn';\nimport { focusFirstInteractive, tabbables } from '../../utils/focus';\nimport { DensityZeroButtons } from '../button/DensityZeroButtons';\nimport { IconButton } from '../icon-button/IconButton';\nimport { BackIcon, CloseIcon } from './sheet-icons';\nimport { SHEET_EXIT_PROPERTIES, sideSheetStyles } from './sheet-styles';\nimport { SheetTriggerContext } from './SheetTrigger';\nimport { useSideSwipe } from './use-side-swipe';\nimport { mergeDataTokens } from '../../utils/merge-data-tokens';\n\nexport interface SideSheetClassNames {\n /**\n * The outermost element, which places the sheet (where `className` and `style` go too):\n * a modal sheet's box `fixed` to the window's `edge`, or a standard sheet's layout box.\n */\n container?: string;\n /** The dimmed layer behind a modal sheet. */\n scrim?: string;\n /** The sheet's surface: its colour, corners, width and shadow. */\n panel?: string;\n /** The header row: back button, title, actions and close button. */\n header?: string;\n /** The back button. */\n back?: string;\n /** The headline. */\n title?: string;\n /** The end of the header: `actions` and the close button. */\n headerActions?: string;\n /** The close button. */\n close?: string;\n /** The scrollable content area. */\n content?: string;\n /** The bottom row of action buttons. */\n footer?: string;\n}\n\n/** Words the side sheet announces. */\nexport interface SideSheetLabels {\n /** The close button. @default \"Close\" */\n close: string;\n /** The back button (with `onBack`). @default \"Back\" */\n back: string;\n}\n\nconst DEFAULT_LABELS: SideSheetLabels = { close: 'Close', back: 'Back' };\n\ninterface SideSheetRenderProps {\n /** Closes a modal sheet, or reports `onOpenChange(false)` for a standard one. */\n close: () => void;\n}\n\ninterface SideSheetOwnProps {\n /**\n * `modal` opens over a scrim as a dialog; `standard` sits in the layout beside the\n * content and opens and closes its width. @default \"modal\"\n */\n variant?: 'modal' | 'standard';\n /** Float 16px from the edges with corners all round. @default false */\n detached?: boolean;\n /**\n * The window edge the sheet is anchored to: `end` (the right in a left-to-right page, the\n * left in RTL, as M3 asks) or `start`. A modal sheet slides in from it and swipes out toward\n * it; a standard sheet faces it, with its divider on the other side. The root's `data-edge`\n * says which.\n * @default \"end\"\n */\n edge?: 'start' | 'end';\n /**\n * Whether a touch or pen swipe toward the sheet's edge closes a modal sheet (past half its\n * width, or a fling). The close button, Escape and the scrim still close it either way, and\n * a mouse drag never moves it. Off when `closedBy` is `\"none\"`; a standard sheet ignores\n * it. To keep the sheet open after a swipe (unsaved changes), control `open` and leave it\n * `true`: the sheet springs back.\n * @default true\n */\n swipeable?: boolean;\n /** Whether the sheet is open (controlled). */\n open?: boolean;\n /** Whether the sheet starts open when uncontrolled. @default false, or true when standard */\n defaultOpen?: boolean;\n /** Called when the sheet opens or closes. */\n onOpenChange?: (open: boolean) => void;\n /** The headline. It names the sheet. */\n title?: ReactNode;\n /** Shows a back button before the title, for a sheet with several levels. */\n onBack?: () => void;\n /** Header controls before the close button. */\n actions?: ReactNode | ((props: SideSheetRenderProps) => ReactNode);\n /** Bottom action buttons, start-aligned, e.g. Save and Cancel. */\n footer?: ReactNode | ((props: SideSheetRenderProps) => ReactNode);\n /** A divider between the content and `footer`. @default false */\n footerDivider?: boolean;\n /** A divider between a standard sheet and the content beside it. @default true */\n divider?: boolean;\n /**\n * How the user can close a modal sheet, as HTML's `<dialog closedby>`: `any` (Escape, a\n * press on the scrim or a swipe), `closerequest` (Escape or a swipe) or `none` (only its\n * buttons or the app).\n * @default \"any\"\n */\n closedBy?: ClosedBy;\n /** Replaces the close button's icon. */\n closeIcon?: ReactElement;\n /** Replaces the back button's icon. */\n backIcon?: ReactElement;\n /** Words the sheet announces, with English defaults. */\n labels?: Partial<SideSheetLabels>;\n children?: ReactNode | ((props: SideSheetRenderProps) => ReactNode);\n /** Classes for each part, by slot. */\n classNames?: SideSheetClassNames;\n /**\n * Goes with `style` on the outermost element, the box that places the sheet, so a layout class\n * moves the whole sheet and never clips or stretches its surface. Style the surface with\n * `classNames.panel`; `ref`, `data-*` and other attributes go on the panel (a standard\n * sheet's are on its outer box).\n */\n className?: string;\n /** Goes on the outermost element, with `className`. */\n style?: CSSProperties;\n /** Ref to the panel, the sheet's surface (`role=\"dialog\"`); a standard sheet's outer box. */\n ref?: Ref<HTMLDivElement>;\n}\n\ntype Naming =\n | {\n title: ReactNode;\n /** Accessible name when there's no visible title; overrides `title` when both are set. */\n 'aria-label'?: string;\n }\n | {\n /** Accessible name when there's no visible title. */\n 'aria-label': string;\n }\n | {\n /** Id of a visible element that names the sheet, when there's no `title`. */\n 'aria-labelledby': string;\n };\n\nexport type SideSheetProps = SideSheetOwnProps &\n Naming &\n Omit<ComponentPropsWithRef<'div'>, keyof SideSheetOwnProps | 'aria-label' | 'aria-labelledby'>;\n\n/**\n * M3 side sheet: supplementary content at the end edge of the window (`edge=\"start\"` for the\n * other). A modal sheet slides in over a scrim as a dialog (Escape, a press outside or a\n * touch swipe toward its edge closes it); a standard sheet sits in the layout as a non-modal\n * dialog and opens and closes its width. Both always show a close button, as M3 requires.\n *\n * @example\n * <SheetTrigger>\n * <Button>Filters</Button>\n * <SideSheet title=\"Filters\" footer={({ close }) => <Button onPress={close}>Apply</Button>}>\n * …\n * </SideSheet>\n * </SheetTrigger>\n */\nexport function SideSheet({\n variant = 'modal',\n open,\n defaultOpen,\n onOpenChange,\n ...props\n}: SideSheetProps) {\n const trigger = useContext(SheetTriggerContext);\n const ownState = useOverlayTriggerState({\n isOpen: open,\n defaultOpen: defaultOpen ?? (variant === 'standard' ? true : undefined),\n onOpenChange,\n });\n const state = (variant === 'modal' ? trigger?.state : undefined) ?? ownState;\n const { isPresent, isExiting, exitProps } = usePresence(state.isOpen, SHEET_EXIT_PROPERTIES);\n if (variant === 'standard') return <StandardSideSheet {...props} state={state} />;\n if (!isPresent) return null;\n return (\n <Overlay exiting={isExiting}>\n <ModalSideSheet\n {...props}\n state={state}\n isExiting={isExiting}\n exitProps={exitProps}\n triggerOverlayProps={trigger?.overlayProps}\n />\n </Overlay>\n );\n}\n\ntype InnerProps = Omit<SideSheetProps, 'variant' | 'open' | 'defaultOpen' | 'onOpenChange'> & {\n state: OverlayTriggerState;\n};\n\ntype BodyProps = Pick<\n SideSheetOwnProps,\n | 'title'\n | 'onBack'\n | 'actions'\n | 'footer'\n | 'children'\n | 'closeIcon'\n | 'backIcon'\n | 'labels'\n | 'classNames'\n> & {\n titleId: string;\n close: () => void;\n styles: ReturnType<typeof sideSheetStyles>;\n contentRef?: Ref<HTMLDivElement>;\n};\n\nfunction SheetBody({\n title,\n titleId,\n onBack,\n actions,\n footer,\n children,\n closeIcon,\n backIcon,\n labels: labelsProp,\n close,\n styles,\n classNames,\n contentRef,\n}: BodyProps) {\n const labels = { ...DEFAULT_LABELS, ...labelsProp };\n const renderProps = { close };\n const render = (slot: SideSheetOwnProps['children']) =>\n typeof slot === 'function' ? slot(renderProps) : slot;\n return (\n <>\n <div className={styles.header({ class: classNames?.header })}>\n {onBack && (\n <IconButton\n variant=\"standard\"\n density={0}\n icon={backIcon ?? <BackIcon />}\n aria-label={labels.back}\n onPress={onBack}\n className={classNames?.back}\n />\n )}\n {title != null && (\n <h2 id={titleId} className={styles.title({ class: classNames?.title })}>\n {title}\n </h2>\n )}\n <div className={styles.headerActions({ class: classNames?.headerActions })}>\n <DensityZeroButtons>{render(actions)}</DensityZeroButtons>\n <IconButton\n variant=\"standard\"\n density={0}\n icon={closeIcon ?? <CloseIcon />}\n aria-label={labels.close}\n onPress={close}\n className={classNames?.close}\n />\n </div>\n </div>\n <div ref={contentRef} className={styles.content({ class: classNames?.content })}>\n {render(children)}\n </div>\n {footer != null && (\n <div className={styles.footer({ class: classNames?.footer })}>{render(footer)}</div>\n )}\n </>\n );\n}\n\nconst BODY_KEYS = [\n 'title',\n 'onBack',\n 'actions',\n 'footer',\n 'children',\n 'closeIcon',\n 'backIcon',\n 'labels',\n 'classNames',\n] as const;\n\n/** Splits the header, body and footer props from those of the panel element. */\nfunction splitBodyProps<T extends Record<string, unknown>>(props: T) {\n const body: Record<string, unknown> = {};\n const rest: Record<string, unknown> = { ...props };\n for (const key of BODY_KEYS) {\n body[key] = rest[key];\n delete rest[key];\n }\n return {\n body: body as Omit<BodyProps, 'titleId' | 'close' | 'styles' | 'contentRef'>,\n rest: rest as Omit<T, (typeof BODY_KEYS)[number]>,\n };\n}\n\n/** `aria-labelledby` for the panel: the consumer's, else the title's unless `aria-label` names it. */\nfunction labelledBy(props: Record<string, unknown>, hasTitle: boolean, titleId: string) {\n const own = props['aria-labelledby'] as string | undefined;\n return own ?? (props['aria-label'] == null && hasTitle ? titleId : undefined);\n}\n\nfunction ModalSideSheet({\n state,\n isExiting,\n exitProps,\n triggerOverlayProps,\n detached = false,\n edge = 'end',\n swipeable = true,\n closedBy = 'any',\n divider: _divider,\n footerDivider = false,\n className,\n style,\n ref,\n ...props\n}: InnerProps & {\n isExiting: boolean;\n exitProps: ReturnType<typeof usePresence>['exitProps'];\n triggerOverlayProps?: { id?: string };\n}) {\n const { body, rest } = splitBodyProps(props);\n const panelRef = useObjectRef(ref as Ref<HTMLDivElement>);\n const motionRef = useRef<HTMLDivElement>(null);\n const contentRef = useRef<HTMLDivElement>(null);\n const titleId = useId();\n // `closedBy=\"none\"` leaves closing to the app, so a swipe doesn't close it either.\n const canSwipe = swipeable && closedBy !== 'none';\n const styles = sideSheetStyles({\n variant: 'modal',\n edge,\n swipeable: canSwipe,\n detached,\n hasBack: body.onBack != null,\n footerDivider,\n });\n const { panelHandlers, motionProps, dragging } = useSideSwipe({\n enabled: canSwipe && !isExiting,\n edge,\n isOpen: state.isOpen,\n close: state.close,\n panelRef,\n motionRef,\n contentRef,\n });\n const { modalProps, underlayProps } = useModalOverlay(\n closedByOverlayOptions(closedBy, isExiting),\n state,\n panelRef,\n );\n const { dialogProps } = useDialog(\n {\n ...(rest as AriaDialogProps),\n 'aria-labelledby': labelledBy(rest, body.title != null, titleId),\n },\n panelRef,\n );\n // M3: focus lands on the first (non-disabled) icon button, as in a Dialog, not the panel.\n useEffect(() => focusFirstInteractive(panelRef.current), [panelRef]);\n const exiting = isExiting || undefined;\n return (\n <>\n <div\n aria-hidden=\"true\"\n data-exiting={exiting}\n className={styles.scrim({ class: body.classNames?.scrim })}\n />\n <div {...underlayProps} className=\"fixed inset-0 z-(--md-sys-z-sheet)\">\n <div\n style={style}\n className={styles.container({ class: cn(body.classNames?.container, className) })}\n >\n <div\n {...mergeProps(exitProps, motionProps)}\n ref={motionRef}\n data-exiting={exiting}\n className={styles.motion()}\n >\n <div\n {...mergeProps(\n rest,\n triggerOverlayProps ?? {},\n modalProps,\n dialogProps,\n panelHandlers,\n )}\n ref={panelRef}\n // The component tokens' hooks (sheet-tokens.ts); `data-variant` and `data-edge`\n // are also public.\n data-tokens={mergeDataTokens('md-comp-sheet-side', rest)}\n data-variant=\"modal\"\n data-edge={edge}\n data-detached={detached || undefined}\n data-dragging={dragging || undefined}\n data-exiting={exiting}\n className={styles.panel({ class: body.classNames?.panel })}\n >\n <TriggerContext value={null}>\n <SheetBody\n {...body}\n titleId={titleId}\n close={state.close}\n styles={styles}\n contentRef={contentRef}\n />\n </TriggerContext>\n </div>\n </div>\n </div>\n </div>\n </>\n );\n}\n\nfunction StandardSideSheet({\n state,\n detached = false,\n edge = 'end',\n swipeable: _swipeable,\n divider = true,\n footerDivider = false,\n closedBy: _closedBy,\n className,\n style,\n ref,\n ...props\n}: InnerProps) {\n const { body, rest } = splitBodyProps(props);\n const titleId = useId();\n const styles = sideSheetStyles({\n variant: 'standard',\n edge,\n detached,\n divider,\n hasBack: body.onBack != null,\n footerDivider,\n });\n const closed = !state.isOpen || undefined;\n const rootRef = useObjectRef(ref as Ref<HTMLDivElement>);\n const frameRef = useRef<HTMLDivElement>(null);\n // The sheet's open width, measured, so the root's width (not a grid fraction, which takes\n // only part of the shrinking free space and left the panel floating off its edge) moves\n // between it and 0. Until it's measured (the server's HTML) the open root is `auto` wide.\n const [width, setWidth] = useState<number | null>(null);\n useLayoutEffect(() => {\n const frame = frameRef.current;\n if (!frame) return;\n // The layout width, not the box on screen, which a consumer transform scales or turns.\n const measure = () => setWidth(frame.offsetWidth);\n measure();\n if (typeof ResizeObserver === 'undefined') return;\n const observer = new ResizeObserver(measure);\n observer.observe(frame);\n return () => observer.disconnect();\n }, []);\n // Focus that came from outside returns there when the sheet closes with focus inside (its\n // close button, Escape in content, or the app closing it): the closed sheet is `inert`, and\n // focus would otherwise drop to the page.\n const returnFocus = useRef<HTMLElement | null>(null);\n useLayoutEffect(() => {\n const root = rootRef.current;\n const active = root?.ownerDocument.activeElement;\n if (state.isOpen || !root || !(active instanceof HTMLElement) || !root.contains(active)) return;\n const saved = returnFocus.current;\n const target =\n saved?.isConnected && !root.contains(saved) && !saved.closest('[inert]')\n ? saved\n : (tabbables(root.ownerDocument.body, (el) => root.contains(el))\n .filter((el) => el.compareDocumentPosition(root) & Node.DOCUMENT_POSITION_FOLLOWING)\n .at(-1) ?? null);\n if (target) target.focus({ preventScroll: true });\n else active.blur();\n }, [state.isOpen, rootRef]);\n const rootStyle = {\n ...style,\n ...(width === null ? {} : { '--m3-side-sheet-width': `${width}px` }),\n } as CSSProperties;\n // A non-modal dialog (m3.material.io: the side sheet's role is dialog); focus isn't moved.\n return (\n <div\n {...rest}\n onFocus={(event: FocusEvent<HTMLDivElement>) => {\n (rest as { onFocus?: (event: FocusEvent<HTMLDivElement>) => void }).onFocus?.(event);\n const from = event.relatedTarget;\n if (from instanceof HTMLElement && !event.currentTarget.contains(from)) {\n returnFocus.current = from;\n }\n }}\n ref={rootRef}\n role=\"dialog\"\n style={rootStyle}\n // The component tokens' hooks (sheet-tokens.ts), read by the panel inside.\n data-tokens={mergeDataTokens('md-comp-sheet-side', rest)}\n data-variant=\"standard\"\n data-edge={edge}\n data-detached={detached || undefined}\n data-closed={closed}\n inert={!state.isOpen}\n aria-labelledby={labelledBy(rest, body.title != null, titleId)}\n className={styles.standard({ class: cn(body.classNames?.container, className) })}\n >\n <div className={styles.standardClip()}>\n <div ref={frameRef} className={styles.standardFrame()}>\n <div data-slot=\"panel\" className={styles.panel({ class: body.classNames?.panel })}>\n <SheetBody {...body} titleId={titleId} close={state.close} styles={styles} />\n </div>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SideSheet.js","names":["Overlay"],"sources":["../../../src/components/sheet/SideSheet.tsx"],"sourcesContent":["'use client';\n\nimport {\n useContext,\n useEffect,\n useId,\n useLayoutEffect,\n useRef,\n useState,\n type ComponentPropsWithRef,\n type CSSProperties,\n type FocusEvent,\n type ReactElement,\n type ReactNode,\n type Ref,\n} from 'react';\nimport {\n mergeProps,\n useDialog,\n useModalOverlay,\n useObjectRef,\n type AriaDialogProps,\n} from 'react-aria';\nimport { useOverlayTriggerState, type OverlayTriggerState } from 'react-stately';\nimport { Overlay } from '../../primitives/Overlay';\nimport { TriggerContext } from '../../primitives/TriggerContext';\nimport { usePresence } from '../../primitives/use-presence';\nimport { closedByOverlayOptions, type ClosedBy } from '../../utils/closed-by';\nimport { cn } from '../../utils/cn';\nimport { focusFirstInteractive, tabbables } from '../../utils/focus';\nimport { DensityZeroButtons } from '../button/DensityZeroButtons';\nimport { IconButton } from '../icon-button/IconButton';\nimport { BackIcon, CloseIcon } from './sheet-icons';\nimport { SHEET_EXIT_PROPERTIES, sideSheetStyles } from './sheet-styles';\nimport { SheetTriggerContext } from './SheetTrigger';\nimport { useSideSwipe } from './use-side-swipe';\nimport { mergeDataTokens } from '../../utils/merge-data-tokens';\n\nexport interface SideSheetClassNames {\n /**\n * The outermost element, which places the sheet (where `className` and `style` go too):\n * a modal sheet's box `fixed` to the window's `edge`, or a standard sheet's layout box.\n */\n container?: string;\n /** The dimmed layer behind a modal sheet. */\n scrim?: string;\n /** The box around a standard sheet's surface, which holds a detached sheet's margins. */\n frame?: string;\n /** The sheet's surface: its colour, corners, width and shadow. */\n panel?: string;\n /** The header row: back button, title, actions and close button. */\n header?: string;\n /** The back button. */\n back?: string;\n /** The headline. */\n title?: string;\n /** The end of the header: `actions` and the close button. */\n headerActions?: string;\n /** The close button. */\n close?: string;\n /** The scrollable content area. */\n content?: string;\n /** The bottom row of action buttons. */\n footer?: string;\n}\n\n/** Words the side sheet announces. */\nexport interface SideSheetLabels {\n /** The close button. @default \"Close\" */\n close: string;\n /** The back button (with `onBack`). @default \"Back\" */\n back: string;\n}\n\nconst DEFAULT_LABELS: SideSheetLabels = { close: 'Close', back: 'Back' };\n\ninterface SideSheetRenderProps {\n /** Closes a modal sheet, or reports `onOpenChange(false)` for a standard one. */\n close: () => void;\n}\n\ninterface SideSheetOwnProps {\n /**\n * `modal` opens over a scrim as a dialog; `standard` sits in the layout beside the\n * content and opens and closes its width. @default \"modal\"\n */\n variant?: 'modal' | 'standard';\n /** Float 16px from the edges with corners all round. @default false */\n detached?: boolean;\n /**\n * The window edge the sheet is anchored to: `end` (the right in a left-to-right page, the\n * left in RTL, as M3 asks) or `start`. A modal sheet slides in from it and swipes out toward\n * it; a standard sheet faces it, with its divider on the other side. The root's `data-edge`\n * says which.\n * @default \"end\"\n */\n edge?: 'start' | 'end';\n /**\n * Whether a touch or pen swipe toward the sheet's edge closes a modal sheet (past half its\n * width, or a fling). The close button, Escape and the scrim still close it either way, and\n * a mouse drag never moves it. Off when `closedBy` is `\"none\"`; a standard sheet ignores\n * it. To keep the sheet open after a swipe (unsaved changes), control `open` and leave it\n * `true`: the sheet springs back.\n * @default true\n */\n swipeable?: boolean;\n /** Whether the sheet is open (controlled). */\n open?: boolean;\n /** Whether the sheet starts open when uncontrolled. @default false, or true when standard */\n defaultOpen?: boolean;\n /** Called when the sheet opens or closes. */\n onOpenChange?: (open: boolean) => void;\n /** The headline. It names the sheet. */\n title?: ReactNode;\n /** Shows a back button before the title, for a sheet with several levels. */\n onBack?: () => void;\n /** Header controls before the close button. */\n actions?: ReactNode | ((props: SideSheetRenderProps) => ReactNode);\n /** Bottom action buttons, start-aligned, e.g. Save and Cancel. */\n footer?: ReactNode | ((props: SideSheetRenderProps) => ReactNode);\n /** A divider between the content and `footer`. @default false */\n footerDivider?: boolean;\n /** A divider between a standard sheet and the content beside it. @default true */\n divider?: boolean;\n /**\n * How the user can close a modal sheet, as HTML's `<dialog closedby>`: `any` (Escape, a\n * press on the scrim or a swipe), `closerequest` (Escape or a swipe) or `none` (only its\n * buttons or the app).\n * @default \"any\"\n */\n closedBy?: ClosedBy;\n /** Replaces the close button's icon. */\n closeIcon?: ReactElement;\n /** Replaces the back button's icon. */\n backIcon?: ReactElement;\n /** Words the sheet announces, with English defaults. */\n labels?: Partial<SideSheetLabels>;\n children?: ReactNode | ((props: SideSheetRenderProps) => ReactNode);\n /** Classes for each part, by slot. */\n classNames?: SideSheetClassNames;\n /**\n * Goes with `style` on the outermost element, the box that places the sheet, so a layout class\n * moves the whole sheet and never clips or stretches its surface. Style the surface with\n * `classNames.panel`; `ref`, `data-*` and other attributes go on the panel (a standard\n * sheet's are on its outer box).\n */\n className?: string;\n /** Goes on the outermost element, with `className`. */\n style?: CSSProperties;\n /** Ref to the panel, the sheet's surface (`role=\"dialog\"`); a standard sheet's outer box. */\n ref?: Ref<HTMLDivElement>;\n}\n\ntype Naming =\n | {\n title: ReactNode;\n /** Accessible name when there's no visible title; overrides `title` when both are set. */\n 'aria-label'?: string;\n }\n | {\n /** Accessible name when there's no visible title. */\n 'aria-label': string;\n }\n | {\n /** Id of a visible element that names the sheet, when there's no `title`. */\n 'aria-labelledby': string;\n };\n\nexport type SideSheetProps = SideSheetOwnProps &\n Naming &\n Omit<ComponentPropsWithRef<'div'>, keyof SideSheetOwnProps | 'aria-label' | 'aria-labelledby'>;\n\n/**\n * M3 side sheet: supplementary content at the end edge of the window (`edge=\"start\"` for the\n * other). A modal sheet slides in over a scrim as a dialog (Escape, a press outside or a\n * touch swipe toward its edge closes it); a standard sheet sits in the layout as a non-modal\n * dialog and opens and closes its width. Both always show a close button, as M3 requires.\n *\n * @example\n * <SheetTrigger>\n * <Button>Filters</Button>\n * <SideSheet title=\"Filters\" footer={({ close }) => <Button onPress={close}>Apply</Button>}>\n * …\n * </SideSheet>\n * </SheetTrigger>\n */\nexport function SideSheet({\n variant = 'modal',\n open,\n defaultOpen,\n onOpenChange,\n ...props\n}: SideSheetProps) {\n const trigger = useContext(SheetTriggerContext);\n const ownState = useOverlayTriggerState({\n isOpen: open,\n defaultOpen: defaultOpen ?? (variant === 'standard' ? true : undefined),\n onOpenChange,\n });\n const state = (variant === 'modal' ? trigger?.state : undefined) ?? ownState;\n const { isPresent, isExiting, exitProps } = usePresence(state.isOpen, SHEET_EXIT_PROPERTIES);\n if (variant === 'standard') return <StandardSideSheet {...props} state={state} />;\n if (!isPresent) return null;\n return (\n <Overlay exiting={isExiting}>\n <ModalSideSheet\n {...props}\n state={state}\n isExiting={isExiting}\n exitProps={exitProps}\n triggerOverlayProps={trigger?.overlayProps}\n />\n </Overlay>\n );\n}\n\ntype InnerProps = Omit<SideSheetProps, 'variant' | 'open' | 'defaultOpen' | 'onOpenChange'> & {\n state: OverlayTriggerState;\n};\n\ntype BodyProps = Pick<\n SideSheetOwnProps,\n | 'title'\n | 'onBack'\n | 'actions'\n | 'footer'\n | 'children'\n | 'closeIcon'\n | 'backIcon'\n | 'labels'\n | 'classNames'\n> & {\n titleId: string;\n close: () => void;\n styles: ReturnType<typeof sideSheetStyles>;\n contentRef?: Ref<HTMLDivElement>;\n};\n\nfunction SheetBody({\n title,\n titleId,\n onBack,\n actions,\n footer,\n children,\n closeIcon,\n backIcon,\n labels: labelsProp,\n close,\n styles,\n classNames,\n contentRef,\n}: BodyProps) {\n const labels = { ...DEFAULT_LABELS, ...labelsProp };\n const renderProps = { close };\n const render = (slot: SideSheetOwnProps['children']) =>\n typeof slot === 'function' ? slot(renderProps) : slot;\n return (\n <>\n <div className={styles.header({ class: classNames?.header })}>\n {onBack && (\n <IconButton\n variant=\"standard\"\n density={0}\n icon={backIcon ?? <BackIcon />}\n aria-label={labels.back}\n onPress={onBack}\n className={classNames?.back}\n />\n )}\n {title != null && (\n <h2 id={titleId} className={styles.title({ class: classNames?.title })}>\n {title}\n </h2>\n )}\n <div className={styles.headerActions({ class: classNames?.headerActions })}>\n <DensityZeroButtons>{render(actions)}</DensityZeroButtons>\n <IconButton\n variant=\"standard\"\n density={0}\n icon={closeIcon ?? <CloseIcon />}\n aria-label={labels.close}\n onPress={close}\n className={classNames?.close}\n />\n </div>\n </div>\n <div ref={contentRef} className={styles.content({ class: classNames?.content })}>\n {render(children)}\n </div>\n {footer != null && (\n <div className={styles.footer({ class: classNames?.footer })}>{render(footer)}</div>\n )}\n </>\n );\n}\n\nconst BODY_KEYS = [\n 'title',\n 'onBack',\n 'actions',\n 'footer',\n 'children',\n 'closeIcon',\n 'backIcon',\n 'labels',\n 'classNames',\n] as const;\n\n/** Splits the header, body and footer props from those of the panel element. */\nfunction splitBodyProps<T extends Record<string, unknown>>(props: T) {\n const body: Record<string, unknown> = {};\n const rest: Record<string, unknown> = { ...props };\n for (const key of BODY_KEYS) {\n body[key] = rest[key];\n delete rest[key];\n }\n return {\n body: body as Omit<BodyProps, 'titleId' | 'close' | 'styles' | 'contentRef'>,\n rest: rest as Omit<T, (typeof BODY_KEYS)[number]>,\n };\n}\n\n/** `aria-labelledby` for the panel: the consumer's, else the title's unless `aria-label` names it. */\nfunction labelledBy(props: Record<string, unknown>, hasTitle: boolean, titleId: string) {\n const own = props['aria-labelledby'] as string | undefined;\n return own ?? (props['aria-label'] == null && hasTitle ? titleId : undefined);\n}\n\nfunction ModalSideSheet({\n state,\n isExiting,\n exitProps,\n triggerOverlayProps,\n detached = false,\n edge = 'end',\n swipeable = true,\n closedBy = 'any',\n divider: _divider,\n footerDivider = false,\n className,\n style,\n ref,\n ...props\n}: InnerProps & {\n isExiting: boolean;\n exitProps: ReturnType<typeof usePresence>['exitProps'];\n triggerOverlayProps?: { id?: string };\n}) {\n const { body, rest } = splitBodyProps(props);\n const panelRef = useObjectRef(ref as Ref<HTMLDivElement>);\n const motionRef = useRef<HTMLDivElement>(null);\n const contentRef = useRef<HTMLDivElement>(null);\n const titleId = useId();\n // `closedBy=\"none\"` leaves closing to the app, so a swipe doesn't close it either.\n const canSwipe = swipeable && closedBy !== 'none';\n const styles = sideSheetStyles({\n variant: 'modal',\n edge,\n swipeable: canSwipe,\n detached,\n hasBack: body.onBack != null,\n footerDivider,\n });\n const { panelHandlers, motionProps, dragging } = useSideSwipe({\n enabled: canSwipe && !isExiting,\n edge,\n isOpen: state.isOpen,\n close: state.close,\n panelRef,\n motionRef,\n contentRef,\n });\n const { modalProps, underlayProps } = useModalOverlay(\n closedByOverlayOptions(closedBy, isExiting),\n state,\n panelRef,\n );\n const { dialogProps } = useDialog(\n {\n ...(rest as AriaDialogProps),\n 'aria-labelledby': labelledBy(rest, body.title != null, titleId),\n },\n panelRef,\n );\n // M3: focus lands on the first (non-disabled) icon button, as in a Dialog, not the panel.\n useEffect(() => focusFirstInteractive(panelRef.current), [panelRef]);\n const exiting = isExiting || undefined;\n return (\n <>\n <div\n aria-hidden=\"true\"\n data-exiting={exiting}\n className={styles.scrim({ class: body.classNames?.scrim })}\n />\n <div {...underlayProps} className=\"fixed inset-0 z-(--md-sys-z-sheet)\">\n <div\n style={style}\n className={styles.container({ class: cn(body.classNames?.container, className) })}\n >\n <div\n {...mergeProps(exitProps, motionProps)}\n ref={motionRef}\n data-exiting={exiting}\n className={styles.motion()}\n >\n <div\n {...mergeProps(\n rest,\n triggerOverlayProps ?? {},\n modalProps,\n dialogProps,\n panelHandlers,\n )}\n ref={panelRef}\n // The component tokens' hooks (sheet-tokens.ts); `data-variant` and `data-edge`\n // are also public.\n data-tokens={mergeDataTokens('md-comp-sheet-side', rest)}\n data-variant=\"modal\"\n data-edge={edge}\n data-detached={detached || undefined}\n data-dragging={dragging || undefined}\n data-exiting={exiting}\n className={styles.panel({ class: body.classNames?.panel })}\n >\n <TriggerContext value={null}>\n <SheetBody\n {...body}\n titleId={titleId}\n close={state.close}\n styles={styles}\n contentRef={contentRef}\n />\n </TriggerContext>\n </div>\n </div>\n </div>\n </div>\n </>\n );\n}\n\nfunction StandardSideSheet({\n state,\n detached = false,\n edge = 'end',\n swipeable: _swipeable,\n divider = true,\n footerDivider = false,\n closedBy: _closedBy,\n className,\n style,\n ref,\n ...props\n}: InnerProps) {\n const { body, rest } = splitBodyProps(props);\n const titleId = useId();\n const styles = sideSheetStyles({\n variant: 'standard',\n edge,\n detached,\n divider,\n hasBack: body.onBack != null,\n footerDivider,\n });\n const closed = !state.isOpen || undefined;\n const rootRef = useObjectRef(ref as Ref<HTMLDivElement>);\n const frameRef = useRef<HTMLDivElement>(null);\n // The sheet's open width, measured, so the root's width (not a grid fraction, which takes\n // only part of the shrinking free space and left the panel floating off its edge) moves\n // between it and 0. Until it's measured (the server's HTML) the open root is `auto` wide.\n const [width, setWidth] = useState<number | null>(null);\n useLayoutEffect(() => {\n const frame = frameRef.current;\n if (!frame) return;\n // The layout width, not the box on screen, which a consumer transform scales or turns.\n const measure = () => setWidth(frame.offsetWidth);\n measure();\n if (typeof ResizeObserver === 'undefined') return;\n const observer = new ResizeObserver(measure);\n observer.observe(frame);\n return () => observer.disconnect();\n }, []);\n // Focus that came from outside returns there when the sheet closes with focus inside (its\n // close button, Escape in content, or the app closing it): the closed sheet is `inert`, and\n // focus would otherwise drop to the page.\n const returnFocus = useRef<HTMLElement | null>(null);\n useLayoutEffect(() => {\n const root = rootRef.current;\n const active = root?.ownerDocument.activeElement;\n if (state.isOpen || !root || !(active instanceof HTMLElement) || !root.contains(active)) return;\n const saved = returnFocus.current;\n const target =\n saved?.isConnected && !root.contains(saved) && !saved.closest('[inert]')\n ? saved\n : (tabbables(root.ownerDocument.body, (el) => root.contains(el))\n .filter((el) => el.compareDocumentPosition(root) & Node.DOCUMENT_POSITION_FOLLOWING)\n .at(-1) ?? null);\n if (target) target.focus({ preventScroll: true });\n else active.blur();\n }, [state.isOpen, rootRef]);\n const rootStyle = {\n ...style,\n ...(width === null ? {} : { '--m3-side-sheet-width': `${width}px` }),\n } as CSSProperties;\n // A non-modal dialog (m3.material.io: the side sheet's role is dialog); focus isn't moved.\n return (\n <div\n {...rest}\n onFocus={(event: FocusEvent<HTMLDivElement>) => {\n (rest as { onFocus?: (event: FocusEvent<HTMLDivElement>) => void }).onFocus?.(event);\n const from = event.relatedTarget;\n if (from instanceof HTMLElement && !event.currentTarget.contains(from)) {\n returnFocus.current = from;\n }\n }}\n ref={rootRef}\n role=\"dialog\"\n style={rootStyle}\n // The component tokens' hooks (sheet-tokens.ts), read by the panel inside.\n data-tokens={mergeDataTokens('md-comp-sheet-side', rest)}\n data-variant=\"standard\"\n data-edge={edge}\n data-detached={detached || undefined}\n data-closed={closed}\n inert={!state.isOpen}\n aria-labelledby={labelledBy(rest, body.title != null, titleId)}\n className={styles.standard({ class: cn(body.classNames?.container, className) })}\n >\n <div className={styles.standardClip()}>\n <div ref={frameRef} className={styles.standardFrame({ class: body.classNames?.frame })}>\n <div data-slot=\"panel\" className={styles.panel({ class: body.classNames?.panel })}>\n <SheetBody {...body} titleId={titleId} close={state.close} styles={styles} />\n </div>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -2,6 +2,6 @@ import { ComponentTokenSet } from "../../tokens/component.js";
|
|
|
2
2
|
import "../../index.js";
|
|
3
3
|
//#region src/components/snackbar/snackbar-tokens.d.ts
|
|
4
4
|
/** The tokens of {@link Snackbar}: `--md-comp-snackbar-<part>`. */
|
|
5
|
-
export declare const snackbarTokenSet: ComponentTokenSet<"
|
|
5
|
+
export declare const snackbarTokenSet: ComponentTokenSet<"container-color" | "container-elevation" | "icon-color" | "container-shape" | "supporting-text-color" | "action-label-text-color">;
|
|
6
6
|
//#endregion
|
|
7
7
|
//# sourceMappingURL=snackbar-tokens.d.ts.map
|
|
@@ -136,6 +136,7 @@ function LinkBase({ ref, href, ...props }) {
|
|
|
136
136
|
...pendingAttributes(pending, domRest),
|
|
137
137
|
...element,
|
|
138
138
|
href: disabled ? void 0 : href,
|
|
139
|
+
...disabled && dom.role === void 0 && { role: "link" },
|
|
139
140
|
ref: domRef,
|
|
140
141
|
children: render(children, false)
|
|
141
142
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ButtonBase.js","names":[],"sources":["../../src/primitives/ButtonBase.tsx"],"sourcesContent":["'use client';\n\nimport {\n createContext,\n useContext,\n useEffect,\n type RefObject,\n type ComponentPropsWithoutRef,\n type CSSProperties,\n type ReactNode,\n type Ref,\n} from 'react';\nimport {\n mergeProps,\n mergeRefs,\n useButton,\n useLink,\n useObjectRef,\n useToggleButton,\n useToggleButtonGroupItem,\n type PressEvents,\n} from 'react-aria';\nimport { useToggleState, type ToggleGroupState } from 'react-stately';\nimport { TriggerContext, type TriggerContextValue } from './TriggerContext';\nimport { useM3Interaction } from './use-m3-interaction';\n\n/**\n * Selection state of an enclosing toggle group (e.g. a connected `ButtonGroup`). Toggle\n * buttons with a `value` inside it are selected through the group instead of on their own.\n */\nexport const ToggleGroupStateContext = createContext<ToggleGroupState | null>(null);\n\ntype PressHandlers = Pick<\n PressEvents,\n 'onPress' | 'onPressStart' | 'onPressEnd' | 'onPressChange' | 'onPressUp'\n> & {\n /** Don't move focus to the element on press (set by menu triggers). @default false */\n preventFocusOnPress?: boolean;\n};\n\nexport interface ButtonBaseRenderState {\n isSelected: boolean;\n}\n\ninterface ButtonBaseCommonProps extends PressHandlers {\n /** Disables the element: it can't be pressed or focused, and shows the disabled style. @default false */\n disabled?: boolean;\n /** Classes for the root element, merged with `classNames.root`; consumer classes win. */\n className?: string;\n /** Inline styles for the root element. */\n style?: CSSProperties;\n /**\n * The element is being dragged (e.g. a card picked up to move it): sets `data-dragged`\n * for the dragged state layer. A consumer's own `data-dragged` attribute counts too.\n * @default false\n */\n dragged?: boolean;\n /** Content, or a function of the selection state (e.g. to swap a toggle's icon). */\n children?: ReactNode | ((state: ButtonBaseRenderState) => ReactNode);\n}\n\n/**\n * The pending state `Button` and `IconButton` build on. It isn't part of the other props types,\n * so components that extend them (FABs, chips, cards) don't inherit it; pass it to `ButtonBase`\n * alongside them.\n */\nexport interface ButtonBasePendingProps {\n /**\n * An action started by the element is running: presses are blocked (`onPress`, `onClick`,\n * Enter and Space do nothing, no hover or pressed state), but the element stays focusable and\n * in the Tab order, unlike `disabled`. Sets `aria-disabled=\"true\"` and `data-pending`; a\n * submit button renders `type=\"button\"` meanwhile, so the form isn't submitted again, and a\n * link keeps its `href` with its navigation prevented. `disabled` wins when both are set.\n * A toggle ignores it.\n * @default false\n */\n pending?: boolean;\n}\n\ntype OwnKeys = keyof ButtonBaseCommonProps | 'color';\n\nexport interface ButtonBaseActionProps\n extends ButtonBaseCommonProps, Omit<ComponentPropsWithoutRef<'button'>, OwnKeys> {\n /** Ref to the root `<button>` element. */\n ref?: Ref<HTMLButtonElement>;\n /** The element rendered: a native `<button>`. @default \"button\" */\n as?: 'button';\n /**\n * The value a submit button sends with its form, under its `name`. On a toggle (`toggle`)\n * it's a `string` naming the button in a selection group instead, required to take part in\n * the group's selection.\n */\n value?: ComponentPropsWithoutRef<'button'>['value'];\n /** Renders a link (`<a>`) instead of a button. */\n href?: undefined;\n /** Makes the button a toggle (`aria-pressed`) with `selected` and `onSelectedChange`. @default false */\n toggle?: false;\n}\n\n/**\n * A `div` with `role=\"button\"`, for pressable containers whose content a `<button>` may\n * not hold (headings, images, block layout), such as clickable cards.\n */\nexport interface ButtonBaseDivActionProps\n extends ButtonBaseCommonProps, Omit<ComponentPropsWithoutRef<'div'>, OwnKeys> {\n /** Ref to the root `<div>` element. */\n ref?: Ref<HTMLDivElement>;\n /** Renders a `div` with `role=\"button\"`, for content a `<button>` may not hold. */\n as: 'div';\n /** Renders a link (`<a>`) instead of a button. */\n href?: undefined;\n /** Makes the button a toggle (`aria-pressed`) with `selected` and `onSelectedChange`. @default false */\n toggle?: false;\n}\n\nexport interface ButtonBaseLinkProps\n extends ButtonBaseCommonProps, Omit<ComponentPropsWithoutRef<'a'>, OwnKeys | 'href'> {\n /** Ref to the root `<a>` element. */\n ref?: Ref<HTMLAnchorElement>;\n /** Renders a link. Navigation goes through React Aria's `RouterProvider` when one is set. */\n href: string;\n /** Makes the button a toggle (`aria-pressed`) with `selected` and `onSelectedChange`. @default false */\n toggle?: false;\n}\n\nexport interface ButtonBaseToggleProps\n extends\n ButtonBaseCommonProps,\n Omit<ComponentPropsWithoutRef<'button'>, OwnKeys | 'aria-pressed' | 'onChange'> {\n /** Ref to the root `<button>` element. */\n ref?: Ref<HTMLButtonElement>;\n /** Renders a link (`<a>`) instead of a button. */\n href?: undefined;\n /** Makes the button a toggle (`aria-pressed`). */\n toggle: true;\n /** Identifies the button in a selection group; required to take part in its selection. */\n value?: string;\n /** Controlled selection. Ignored inside a selection group. */\n selected?: boolean;\n /** Initial selection when uncontrolled. */\n defaultSelected?: boolean;\n /** Called with the new selection when the user toggles the button. */\n onSelectedChange?: (selected: boolean) => void;\n}\n\nexport type ButtonBaseProps =\n ButtonBaseActionProps | ButtonBaseDivActionProps | ButtonBaseLinkProps | ButtonBaseToggleProps;\n\nfunction splitProps<T extends ButtonBaseCommonProps & { pending?: boolean }>(props: T) {\n const {\n disabled = false,\n pending: pendingProp = false,\n className,\n style,\n children,\n onPress,\n onPressStart,\n onPressEnd,\n onPressChange,\n onPressUp,\n preventFocusOnPress,\n dragged,\n ...dom\n } = props;\n const consumerDragged = (dom as Record<string, unknown>)['data-dragged'];\n // `disabled` wins: a disabled element is natively disabled even while its action runs.\n const pending = pendingProp && !disabled;\n return {\n disabled,\n pending,\n /** Presses, hover and the pressed state are off: disabled, or pending. */\n blocked: disabled || pending,\n // Our `data-*` attributes are spread last, so a consumer's `data-dragged` feeds the state.\n isDragged:\n dragged ??\n (consumerDragged !== undefined && consumerDragged !== false && consumerDragged !== 'false'),\n element: { className, style },\n children,\n // React Aria options, not DOM attributes: they go to the press hooks.\n press: { onPress, onPressStart, onPressEnd, onPressChange, onPressUp, preventFocusOnPress },\n dom,\n };\n}\n\n/**\n * While a submit button is pending it renders `type=\"button\"`, but a form left without a submit\n * button still submits on Enter in its only field (HTML's implicit submission). This stops a\n * submission with no submitter (or this button) before it reaches React or the browser, so\n * Enter doesn't send the form again; another submit button of the form still works.\n */\nfunction useBlockImplicitSubmit(ref: RefObject<HTMLElement | null>, enabled: boolean) {\n useEffect(() => {\n const button = ref.current;\n const form = button instanceof HTMLButtonElement ? button.form : null;\n if (!enabled || !form) return;\n const block = (event: Event) => {\n // A plain `Event` (no `submitter`) counts as implicit too.\n const submitter = (event as Partial<SubmitEvent>).submitter;\n if (submitter && submitter !== button) return;\n event.preventDefault();\n event.stopPropagation();\n };\n form.addEventListener('submit', block, true);\n return () => form.removeEventListener('submit', block, true);\n }, [ref, enabled]);\n}\n\n/** A pending element ignores clicks: a consumer's `onClick` is left out while it waits. */\nfunction withoutClickWhilePending<T extends { onClick?: unknown }>(dom: T, pending: boolean) {\n if (!pending) return dom;\n const { onClick: _onClick, ...rest } = dom;\n return rest;\n}\n\n/**\n * What a pending element changes on top of the blocked hooks' props (React Aria's pending\n * `Button`): it stays focusable (React Aria's `isDisabled` would make a `<button>` natively\n * disabled and drop its tab stop), says it's unavailable with `aria-disabled`, and keeps the\n * enabled look (`data-disabled` only for `disabled`).\n */\nfunction pendingAttributes(pending: boolean, dom: { tabIndex?: number }) {\n return pending\n ? {\n disabled: undefined,\n tabIndex: dom.tabIndex ?? 0,\n 'aria-disabled': true as const,\n 'data-disabled': undefined,\n 'data-pending': true as const,\n }\n : {};\n}\n\nconst render = (children: ButtonBaseCommonProps['children'], isSelected: boolean) =>\n typeof children === 'function' ? children({ isSelected }) : children;\n\nfunction ActionBase(\n props: (ButtonBaseActionProps | ButtonBaseDivActionProps) & ButtonBasePendingProps,\n) {\n const { ref, as: elementType = 'button', ...rest } = props;\n const type =\n elementType === 'button' ? ((rest as ButtonBaseActionProps).type ?? 'button') : undefined;\n const { pending, blocked, isDragged, element, children, press, dom } = splitProps(rest);\n const domRest = withoutClickWhilePending(dom, pending);\n const domRef = useObjectRef(ref as Ref<HTMLElement>);\n const { buttonProps, isPressed } = useButton(\n { ...press, type, isDisabled: blocked, elementType },\n domRef,\n );\n const { interactionProps, dataAttributes } = useM3Interaction(\n { isDisabled: blocked, isPressed, isDragged },\n domRef,\n );\n useBlockImplicitSubmit(domRef, pending && type === 'submit');\n const elementProps = {\n ...mergeProps(domRest, buttonProps, interactionProps),\n ...dataAttributes,\n ...pendingAttributes(pending, domRest),\n // Enter in a form's field would submit it again through a pending submit button.\n ...(pending && type === 'submit' ? { type: 'button' as const } : {}),\n ...element,\n };\n return elementType === 'div' ? (\n <div {...elementProps} ref={domRef as Ref<HTMLDivElement>}>\n {render(children, false)}\n </div>\n ) : (\n <button {...elementProps} ref={domRef as Ref<HTMLButtonElement>}>\n {render(children, false)}\n </button>\n );\n}\n\nfunction LinkBase({ ref, href, ...props }: ButtonBaseLinkProps & ButtonBasePendingProps) {\n const { disabled, pending, blocked, isDragged, element, children, press, dom } =\n splitProps(props);\n const domRest = withoutClickWhilePending(dom, pending);\n const domRef = useObjectRef(ref);\n const { linkProps, isPressed } = useLink(\n // While blocked, the press hook prevents a click's navigation.\n { ...press, href, isDisabled: blocked, elementType: 'a' },\n domRef,\n );\n const { interactionProps, dataAttributes } = useM3Interaction(\n { isDisabled: blocked, isPressed, isDragged },\n domRef,\n );\n return (\n <a\n {...mergeProps(domRest, linkProps, interactionProps)}\n {...dataAttributes}\n {...pendingAttributes(pending, domRest)}\n {...element}\n // A pending link stays a link (role and tab stop); only a disabled one drops `href`.\n href={disabled ? undefined : href}\n ref={domRef}\n >\n {render(children, false)}\n </a>\n );\n}\n\nfunction ToggleBase({\n ref,\n type = 'button',\n selected,\n defaultSelected,\n onSelectedChange,\n toggle,\n ...props\n}: ButtonBaseToggleProps) {\n const { disabled, isDragged, element, children, press, dom } = splitProps(props);\n const domRef = useObjectRef(ref);\n const options = {\n ...press,\n type,\n isDisabled: disabled,\n isSelected: selected,\n defaultSelected,\n onChange: onSelectedChange,\n elementType: 'button' as const,\n };\n const state = useToggleState(options);\n const { buttonProps, isPressed, isSelected } = useToggleButton(options, state, domRef);\n const { interactionProps, dataAttributes } = useM3Interaction(\n { isDisabled: disabled, isPressed, isSelected, isDragged },\n domRef,\n );\n return (\n <button\n {...mergeProps(dom, buttonProps, interactionProps)}\n {...dataAttributes}\n {...element}\n ref={domRef}\n >\n {render(children, isSelected)}\n </button>\n );\n}\n\nfunction GroupItemBase({\n ref,\n type = 'button',\n value,\n state,\n ...props\n}: Omit<ButtonBaseToggleProps, 'selected' | 'defaultSelected' | 'onSelectedChange' | 'toggle'> & {\n value: string;\n state: ToggleGroupState;\n}) {\n const { disabled, isDragged, element, children, press, dom } = splitProps(props);\n const domRef = useObjectRef(ref);\n const { buttonProps, isPressed, isSelected } = useToggleButtonGroupItem(\n { ...press, id: value, isDisabled: disabled, elementType: 'button' },\n state,\n domRef,\n );\n const { interactionProps, dataAttributes } = useM3Interaction(\n { isDisabled: disabled || state.isDisabled, isPressed, isSelected, isDragged },\n domRef,\n );\n return (\n <button\n {...mergeProps(dom, buttonProps, interactionProps)}\n {...dataAttributes}\n {...element}\n type={type}\n value={value}\n ref={domRef}\n >\n {render(children, isSelected)}\n </button>\n );\n}\n\nfunction withTrigger(\n props: ButtonBaseProps,\n { ref: triggerRef, ...trigger }: TriggerContextValue,\n): ButtonBaseProps {\n const merged = mergeProps(trigger, props) as ButtonBaseProps;\n return {\n ...merged,\n ref: mergeRefs(props.ref as Ref<HTMLElement>, triggerRef as Ref<HTMLElement>),\n } as ButtonBaseProps;\n}\n\n/**\n * Unstyled button behaviour shared by every button-like component: a `<button>`, a\n * link (`href`) or a toggle (`toggle`), with React Aria press, focus and keyboard\n * handling, and the M3 interaction `data-*` attributes and ripple origin from\n * `useM3Interaction`. DOM props pass through; `className` and `style` go on the element.\n * `pending` blocks presses but keeps focus (see {@link ButtonBasePendingProps}).\n */\nexport function ButtonBase(ownProps: ButtonBaseProps & ButtonBasePendingProps) {\n const groupState = useContext(ToggleGroupStateContext);\n const trigger = useContext(TriggerContext);\n // An enclosing overlay trigger adds its press handler (chained), ARIA attributes and ref.\n const props = trigger ? withTrigger(ownProps, trigger) : ownProps;\n if (props.href !== undefined) return <LinkBase {...props} />;\n if (props.toggle) {\n if (groupState && props.value !== undefined) {\n const { selected, defaultSelected, onSelectedChange, toggle, value, ...item } = props;\n if (\n process.env.NODE_ENV !== 'production' &&\n (selected !== undefined || defaultSelected !== undefined || onSelectedChange)\n ) {\n console.warn(\n `[@windmorph/react] Toggle \"${value}\" is inside a selection group; use the group's selectedKeys instead of selected / onSelectedChange.`,\n );\n }\n return <GroupItemBase {...item} value={value} state={groupState} />;\n }\n return <ToggleBase {...props} />;\n }\n return <ActionBase {...props} />;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ButtonBase.js","names":[],"sources":["../../src/primitives/ButtonBase.tsx"],"sourcesContent":["'use client';\n\nimport {\n createContext,\n useContext,\n useEffect,\n type RefObject,\n type ComponentPropsWithoutRef,\n type CSSProperties,\n type ReactNode,\n type Ref,\n} from 'react';\nimport {\n mergeProps,\n mergeRefs,\n useButton,\n useLink,\n useObjectRef,\n useToggleButton,\n useToggleButtonGroupItem,\n type PressEvents,\n} from 'react-aria';\nimport { useToggleState, type ToggleGroupState } from 'react-stately';\nimport { TriggerContext, type TriggerContextValue } from './TriggerContext';\nimport { useM3Interaction } from './use-m3-interaction';\n\n/**\n * Selection state of an enclosing toggle group (e.g. a connected `ButtonGroup`). Toggle\n * buttons with a `value` inside it are selected through the group instead of on their own.\n */\nexport const ToggleGroupStateContext = createContext<ToggleGroupState | null>(null);\n\ntype PressHandlers = Pick<\n PressEvents,\n 'onPress' | 'onPressStart' | 'onPressEnd' | 'onPressChange' | 'onPressUp'\n> & {\n /** Don't move focus to the element on press (set by menu triggers). @default false */\n preventFocusOnPress?: boolean;\n};\n\nexport interface ButtonBaseRenderState {\n isSelected: boolean;\n}\n\ninterface ButtonBaseCommonProps extends PressHandlers {\n /** Disables the element: it can't be pressed or focused, and shows the disabled style. @default false */\n disabled?: boolean;\n /** Classes for the root element, merged with `classNames.root`; consumer classes win. */\n className?: string;\n /** Inline styles for the root element. */\n style?: CSSProperties;\n /**\n * The element is being dragged (e.g. a card picked up to move it): sets `data-dragged`\n * for the dragged state layer. A consumer's own `data-dragged` attribute counts too.\n * @default false\n */\n dragged?: boolean;\n /** Content, or a function of the selection state (e.g. to swap a toggle's icon). */\n children?: ReactNode | ((state: ButtonBaseRenderState) => ReactNode);\n}\n\n/**\n * The pending state `Button` and `IconButton` build on. It isn't part of the other props types,\n * so components that extend them (FABs, chips, cards) don't inherit it; pass it to `ButtonBase`\n * alongside them.\n */\nexport interface ButtonBasePendingProps {\n /**\n * An action started by the element is running: presses are blocked (`onPress`, `onClick`,\n * Enter and Space do nothing, no hover or pressed state), but the element stays focusable and\n * in the Tab order, unlike `disabled`. Sets `aria-disabled=\"true\"` and `data-pending`; a\n * submit button renders `type=\"button\"` meanwhile, so the form isn't submitted again, and a\n * link keeps its `href` with its navigation prevented. `disabled` wins when both are set.\n * A toggle ignores it.\n * @default false\n */\n pending?: boolean;\n}\n\ntype OwnKeys = keyof ButtonBaseCommonProps | 'color';\n\nexport interface ButtonBaseActionProps\n extends ButtonBaseCommonProps, Omit<ComponentPropsWithoutRef<'button'>, OwnKeys> {\n /** Ref to the root `<button>` element. */\n ref?: Ref<HTMLButtonElement>;\n /** The element rendered: a native `<button>`. @default \"button\" */\n as?: 'button';\n /**\n * The value a submit button sends with its form, under its `name`. On a toggle (`toggle`)\n * it's a `string` naming the button in a selection group instead, required to take part in\n * the group's selection.\n */\n value?: ComponentPropsWithoutRef<'button'>['value'];\n /** Renders a link (`<a>`) instead of a button. */\n href?: undefined;\n /** Makes the button a toggle (`aria-pressed`) with `selected` and `onSelectedChange`. @default false */\n toggle?: false;\n}\n\n/**\n * A `div` with `role=\"button\"`, for pressable containers whose content a `<button>` may\n * not hold (headings, images, block layout), such as clickable cards.\n */\nexport interface ButtonBaseDivActionProps\n extends ButtonBaseCommonProps, Omit<ComponentPropsWithoutRef<'div'>, OwnKeys> {\n /** Ref to the root `<div>` element. */\n ref?: Ref<HTMLDivElement>;\n /** Renders a `div` with `role=\"button\"`, for content a `<button>` may not hold. */\n as: 'div';\n /** Renders a link (`<a>`) instead of a button. */\n href?: undefined;\n /** Makes the button a toggle (`aria-pressed`) with `selected` and `onSelectedChange`. @default false */\n toggle?: false;\n}\n\nexport interface ButtonBaseLinkProps\n extends ButtonBaseCommonProps, Omit<ComponentPropsWithoutRef<'a'>, OwnKeys | 'href'> {\n /** Ref to the root `<a>` element. */\n ref?: Ref<HTMLAnchorElement>;\n /** Renders a link. Navigation goes through React Aria's `RouterProvider` when one is set. */\n href: string;\n /** Makes the button a toggle (`aria-pressed`) with `selected` and `onSelectedChange`. @default false */\n toggle?: false;\n}\n\nexport interface ButtonBaseToggleProps\n extends\n ButtonBaseCommonProps,\n Omit<ComponentPropsWithoutRef<'button'>, OwnKeys | 'aria-pressed' | 'onChange'> {\n /** Ref to the root `<button>` element. */\n ref?: Ref<HTMLButtonElement>;\n /** Renders a link (`<a>`) instead of a button. */\n href?: undefined;\n /** Makes the button a toggle (`aria-pressed`). */\n toggle: true;\n /** Identifies the button in a selection group; required to take part in its selection. */\n value?: string;\n /** Controlled selection. Ignored inside a selection group. */\n selected?: boolean;\n /** Initial selection when uncontrolled. */\n defaultSelected?: boolean;\n /** Called with the new selection when the user toggles the button. */\n onSelectedChange?: (selected: boolean) => void;\n}\n\nexport type ButtonBaseProps =\n ButtonBaseActionProps | ButtonBaseDivActionProps | ButtonBaseLinkProps | ButtonBaseToggleProps;\n\nfunction splitProps<T extends ButtonBaseCommonProps & { pending?: boolean }>(props: T) {\n const {\n disabled = false,\n pending: pendingProp = false,\n className,\n style,\n children,\n onPress,\n onPressStart,\n onPressEnd,\n onPressChange,\n onPressUp,\n preventFocusOnPress,\n dragged,\n ...dom\n } = props;\n const consumerDragged = (dom as Record<string, unknown>)['data-dragged'];\n // `disabled` wins: a disabled element is natively disabled even while its action runs.\n const pending = pendingProp && !disabled;\n return {\n disabled,\n pending,\n /** Presses, hover and the pressed state are off: disabled, or pending. */\n blocked: disabled || pending,\n // Our `data-*` attributes are spread last, so a consumer's `data-dragged` feeds the state.\n isDragged:\n dragged ??\n (consumerDragged !== undefined && consumerDragged !== false && consumerDragged !== 'false'),\n element: { className, style },\n children,\n // React Aria options, not DOM attributes: they go to the press hooks.\n press: { onPress, onPressStart, onPressEnd, onPressChange, onPressUp, preventFocusOnPress },\n dom,\n };\n}\n\n/**\n * While a submit button is pending it renders `type=\"button\"`, but a form left without a submit\n * button still submits on Enter in its only field (HTML's implicit submission). This stops a\n * submission with no submitter (or this button) before it reaches React or the browser, so\n * Enter doesn't send the form again; another submit button of the form still works.\n */\nfunction useBlockImplicitSubmit(ref: RefObject<HTMLElement | null>, enabled: boolean) {\n useEffect(() => {\n const button = ref.current;\n const form = button instanceof HTMLButtonElement ? button.form : null;\n if (!enabled || !form) return;\n const block = (event: Event) => {\n // A plain `Event` (no `submitter`) counts as implicit too.\n const submitter = (event as Partial<SubmitEvent>).submitter;\n if (submitter && submitter !== button) return;\n event.preventDefault();\n event.stopPropagation();\n };\n form.addEventListener('submit', block, true);\n return () => form.removeEventListener('submit', block, true);\n }, [ref, enabled]);\n}\n\n/** A pending element ignores clicks: a consumer's `onClick` is left out while it waits. */\nfunction withoutClickWhilePending<T extends { onClick?: unknown }>(dom: T, pending: boolean) {\n if (!pending) return dom;\n const { onClick: _onClick, ...rest } = dom;\n return rest;\n}\n\n/**\n * What a pending element changes on top of the blocked hooks' props (React Aria's pending\n * `Button`): it stays focusable (React Aria's `isDisabled` would make a `<button>` natively\n * disabled and drop its tab stop), says it's unavailable with `aria-disabled`, and keeps the\n * enabled look (`data-disabled` only for `disabled`).\n */\nfunction pendingAttributes(pending: boolean, dom: { tabIndex?: number }) {\n return pending\n ? {\n disabled: undefined,\n tabIndex: dom.tabIndex ?? 0,\n 'aria-disabled': true as const,\n 'data-disabled': undefined,\n 'data-pending': true as const,\n }\n : {};\n}\n\nconst render = (children: ButtonBaseCommonProps['children'], isSelected: boolean) =>\n typeof children === 'function' ? children({ isSelected }) : children;\n\nfunction ActionBase(\n props: (ButtonBaseActionProps | ButtonBaseDivActionProps) & ButtonBasePendingProps,\n) {\n const { ref, as: elementType = 'button', ...rest } = props;\n const type =\n elementType === 'button' ? ((rest as ButtonBaseActionProps).type ?? 'button') : undefined;\n const { pending, blocked, isDragged, element, children, press, dom } = splitProps(rest);\n const domRest = withoutClickWhilePending(dom, pending);\n const domRef = useObjectRef(ref as Ref<HTMLElement>);\n const { buttonProps, isPressed } = useButton(\n { ...press, type, isDisabled: blocked, elementType },\n domRef,\n );\n const { interactionProps, dataAttributes } = useM3Interaction(\n { isDisabled: blocked, isPressed, isDragged },\n domRef,\n );\n useBlockImplicitSubmit(domRef, pending && type === 'submit');\n const elementProps = {\n ...mergeProps(domRest, buttonProps, interactionProps),\n ...dataAttributes,\n ...pendingAttributes(pending, domRest),\n // Enter in a form's field would submit it again through a pending submit button.\n ...(pending && type === 'submit' ? { type: 'button' as const } : {}),\n ...element,\n };\n return elementType === 'div' ? (\n <div {...elementProps} ref={domRef as Ref<HTMLDivElement>}>\n {render(children, false)}\n </div>\n ) : (\n <button {...elementProps} ref={domRef as Ref<HTMLButtonElement>}>\n {render(children, false)}\n </button>\n );\n}\n\nfunction LinkBase({ ref, href, ...props }: ButtonBaseLinkProps & ButtonBasePendingProps) {\n const { disabled, pending, blocked, isDragged, element, children, press, dom } =\n splitProps(props);\n const domRest = withoutClickWhilePending(dom, pending);\n const domRef = useObjectRef(ref);\n const { linkProps, isPressed } = useLink(\n // While blocked, the press hook prevents a click's navigation.\n { ...press, href, isDisabled: blocked, elementType: 'a' },\n domRef,\n );\n const { interactionProps, dataAttributes } = useM3Interaction(\n { isDisabled: blocked, isPressed, isDragged },\n domRef,\n );\n return (\n <a\n {...mergeProps(domRest, linkProps, interactionProps)}\n {...dataAttributes}\n {...pendingAttributes(pending, domRest)}\n {...element}\n // A pending link stays a link (role and tab stop); only a disabled one drops `href`, and\n // keeps the role an `a` loses without one, so it can still be named.\n href={disabled ? undefined : href}\n {...(disabled && dom.role === undefined && { role: 'link' })}\n ref={domRef}\n >\n {render(children, false)}\n </a>\n );\n}\n\nfunction ToggleBase({\n ref,\n type = 'button',\n selected,\n defaultSelected,\n onSelectedChange,\n toggle,\n ...props\n}: ButtonBaseToggleProps) {\n const { disabled, isDragged, element, children, press, dom } = splitProps(props);\n const domRef = useObjectRef(ref);\n const options = {\n ...press,\n type,\n isDisabled: disabled,\n isSelected: selected,\n defaultSelected,\n onChange: onSelectedChange,\n elementType: 'button' as const,\n };\n const state = useToggleState(options);\n const { buttonProps, isPressed, isSelected } = useToggleButton(options, state, domRef);\n const { interactionProps, dataAttributes } = useM3Interaction(\n { isDisabled: disabled, isPressed, isSelected, isDragged },\n domRef,\n );\n return (\n <button\n {...mergeProps(dom, buttonProps, interactionProps)}\n {...dataAttributes}\n {...element}\n ref={domRef}\n >\n {render(children, isSelected)}\n </button>\n );\n}\n\nfunction GroupItemBase({\n ref,\n type = 'button',\n value,\n state,\n ...props\n}: Omit<ButtonBaseToggleProps, 'selected' | 'defaultSelected' | 'onSelectedChange' | 'toggle'> & {\n value: string;\n state: ToggleGroupState;\n}) {\n const { disabled, isDragged, element, children, press, dom } = splitProps(props);\n const domRef = useObjectRef(ref);\n const { buttonProps, isPressed, isSelected } = useToggleButtonGroupItem(\n { ...press, id: value, isDisabled: disabled, elementType: 'button' },\n state,\n domRef,\n );\n const { interactionProps, dataAttributes } = useM3Interaction(\n { isDisabled: disabled || state.isDisabled, isPressed, isSelected, isDragged },\n domRef,\n );\n return (\n <button\n {...mergeProps(dom, buttonProps, interactionProps)}\n {...dataAttributes}\n {...element}\n type={type}\n value={value}\n ref={domRef}\n >\n {render(children, isSelected)}\n </button>\n );\n}\n\nfunction withTrigger(\n props: ButtonBaseProps,\n { ref: triggerRef, ...trigger }: TriggerContextValue,\n): ButtonBaseProps {\n const merged = mergeProps(trigger, props) as ButtonBaseProps;\n return {\n ...merged,\n ref: mergeRefs(props.ref as Ref<HTMLElement>, triggerRef as Ref<HTMLElement>),\n } as ButtonBaseProps;\n}\n\n/**\n * Unstyled button behaviour shared by every button-like component: a `<button>`, a\n * link (`href`) or a toggle (`toggle`), with React Aria press, focus and keyboard\n * handling, and the M3 interaction `data-*` attributes and ripple origin from\n * `useM3Interaction`. DOM props pass through; `className` and `style` go on the element.\n * `pending` blocks presses but keeps focus (see {@link ButtonBasePendingProps}).\n */\nexport function ButtonBase(ownProps: ButtonBaseProps & ButtonBasePendingProps) {\n const groupState = useContext(ToggleGroupStateContext);\n const trigger = useContext(TriggerContext);\n // An enclosing overlay trigger adds its press handler (chained), ARIA attributes and ref.\n const props = trigger ? withTrigger(ownProps, trigger) : ownProps;\n if (props.href !== undefined) return <LinkBase {...props} />;\n if (props.toggle) {\n if (groupState && props.value !== undefined) {\n const { selected, defaultSelected, onSelectedChange, toggle, value, ...item } = props;\n if (\n process.env.NODE_ENV !== 'production' &&\n (selected !== undefined || defaultSelected !== undefined || onSelectedChange)\n ) {\n console.warn(\n `[@windmorph/react] Toggle \"${value}\" is inside a selection group; use the group's selectedKeys instead of selected / onSelectedChange.`,\n );\n }\n return <GroupItemBase {...item} value={value} state={groupState} />;\n }\n return <ToggleBase {...props} />;\n }\n return <ActionBase {...props} />;\n}\n"],"mappings":"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"}
|
|
@@ -281,3 +281,11 @@
|
|
|
281
281
|
[data-overlay-placement] {
|
|
282
282
|
inset: auto;
|
|
283
283
|
}
|
|
284
|
+
|
|
285
|
+
/* While ThemeProvider changes the colours: a control's fade is for its hover and press. */
|
|
286
|
+
[data-wm-theme-changing],
|
|
287
|
+
[data-wm-theme-changing] *,
|
|
288
|
+
[data-wm-theme-changing] *::before,
|
|
289
|
+
[data-wm-theme-changing] *::after {
|
|
290
|
+
transition: none !important;
|
|
291
|
+
}
|