@mortenbrudvik/waveui 0.11.0 → 0.13.0
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/README.md +157 -13
- package/dist/components/button/Link.cjs +1 -1
- package/dist/components/button/Link.cjs.map +1 -1
- package/dist/components/button/Link.mjs +4 -4
- package/dist/components/data-display/Avatar.cjs +1 -1
- package/dist/components/data-display/Avatar.cjs.map +1 -1
- package/dist/components/data-display/Avatar.colors.cjs +2 -0
- package/dist/components/data-display/Avatar.colors.cjs.map +1 -0
- package/dist/components/data-display/Avatar.colors.mjs +45 -0
- package/dist/components/data-display/Avatar.colors.mjs.map +1 -0
- package/dist/components/data-display/Avatar.context.cjs +1 -1
- package/dist/components/data-display/Avatar.context.cjs.map +1 -1
- package/dist/components/data-display/Avatar.context.mjs +4 -2
- package/dist/components/data-display/Avatar.context.mjs.map +1 -1
- package/dist/components/data-display/Avatar.mjs +103 -96
- package/dist/components/data-display/Avatar.mjs.map +1 -1
- package/dist/components/data-display/Avatar.sizes.cjs +2 -0
- package/dist/components/data-display/Avatar.sizes.cjs.map +1 -0
- package/dist/components/data-display/Avatar.sizes.mjs +41 -0
- package/dist/components/data-display/Avatar.sizes.mjs.map +1 -0
- package/dist/components/data-display/AvatarGroup.cjs +1 -1
- package/dist/components/data-display/AvatarGroup.cjs.map +1 -1
- package/dist/components/data-display/AvatarGroup.context.cjs +2 -0
- package/dist/components/data-display/AvatarGroup.context.cjs.map +1 -0
- package/dist/components/data-display/AvatarGroup.context.mjs +9 -0
- package/dist/components/data-display/AvatarGroup.context.mjs.map +1 -0
- package/dist/components/data-display/AvatarGroup.mjs +65 -122
- package/dist/components/data-display/AvatarGroup.mjs.map +1 -1
- package/dist/components/data-display/AvatarGroup.partition.cjs +2 -0
- package/dist/components/data-display/AvatarGroup.partition.cjs.map +1 -0
- package/dist/components/data-display/AvatarGroup.partition.mjs +14 -0
- package/dist/components/data-display/AvatarGroup.partition.mjs.map +1 -0
- package/dist/components/data-display/AvatarGroup.parts.cjs +2 -0
- package/dist/components/data-display/AvatarGroup.parts.cjs.map +1 -0
- package/dist/components/data-display/AvatarGroup.parts.mjs +178 -0
- package/dist/components/data-display/AvatarGroup.parts.mjs.map +1 -0
- package/dist/components/data-display/InfoButton.cjs +1 -1
- package/dist/components/data-display/InfoButton.cjs.map +1 -1
- package/dist/components/data-display/InfoButton.mjs +12 -12
- package/dist/components/data-display/InteractionTag.cjs +1 -1
- package/dist/components/data-display/InteractionTag.cjs.map +1 -1
- package/dist/components/data-display/InteractionTag.mjs +36 -33
- package/dist/components/data-display/InteractionTag.mjs.map +1 -1
- package/dist/components/data-display/Persona.cjs +1 -1
- package/dist/components/data-display/Persona.cjs.map +1 -1
- package/dist/components/data-display/Persona.mjs +96 -37
- package/dist/components/data-display/Persona.mjs.map +1 -1
- package/dist/components/data-display/PresenceBadge.cjs +1 -1
- package/dist/components/data-display/PresenceBadge.cjs.map +1 -1
- package/dist/components/data-display/PresenceBadge.mjs +42 -28
- package/dist/components/data-display/PresenceBadge.mjs.map +1 -1
- package/dist/components/data-display/Tag.cjs +1 -1
- package/dist/components/data-display/Tag.cjs.map +1 -1
- package/dist/components/data-display/Tag.mjs +111 -108
- package/dist/components/data-display/Tag.mjs.map +1 -1
- package/dist/components/feedback/Toaster.cjs +1 -1
- package/dist/components/feedback/Toaster.cjs.map +1 -1
- package/dist/components/feedback/Toaster.mjs +235 -235
- package/dist/components/feedback/Toaster.mjs.map +1 -1
- package/dist/components/input/Combobox.cjs +1 -1
- package/dist/components/input/Combobox.cjs.map +1 -1
- package/dist/components/input/Combobox.mjs +176 -175
- package/dist/components/input/Combobox.mjs.map +1 -1
- package/dist/components/input/DatePicker.cjs +1 -1
- package/dist/components/input/DatePicker.cjs.map +1 -1
- package/dist/components/input/DatePicker.mjs +188 -187
- package/dist/components/input/DatePicker.mjs.map +1 -1
- package/dist/components/input/Dropdown.cjs +1 -1
- package/dist/components/input/Dropdown.cjs.map +1 -1
- package/dist/components/input/Dropdown.mjs +41 -40
- package/dist/components/input/Dropdown.mjs.map +1 -1
- package/dist/components/input/Option.cjs +1 -1
- package/dist/components/input/Option.cjs.map +1 -1
- package/dist/components/input/Option.mjs +89 -87
- package/dist/components/input/Option.mjs.map +1 -1
- package/dist/components/input/TagPicker.root.cjs +1 -1
- package/dist/components/input/TagPicker.root.cjs.map +1 -1
- package/dist/components/input/TagPicker.root.mjs +136 -135
- package/dist/components/input/TagPicker.root.mjs.map +1 -1
- package/dist/components/input/TimePicker.cjs +1 -1
- package/dist/components/input/TimePicker.cjs.map +1 -1
- package/dist/components/input/TimePicker.mjs +156 -154
- package/dist/components/input/TimePicker.mjs.map +1 -1
- package/dist/components/layout/Card.cjs +1 -1
- package/dist/components/layout/Card.cjs.map +1 -1
- package/dist/components/layout/Card.context.cjs +2 -0
- package/dist/components/layout/Card.context.cjs.map +1 -0
- package/dist/components/layout/Card.context.mjs +14 -0
- package/dist/components/layout/Card.context.mjs.map +1 -0
- package/dist/components/layout/Card.mjs +138 -92
- package/dist/components/layout/Card.mjs.map +1 -1
- package/dist/components/layout/Card.preview.cjs +2 -0
- package/dist/components/layout/Card.preview.cjs.map +1 -0
- package/dist/components/layout/Card.preview.mjs +22 -0
- package/dist/components/layout/Card.preview.mjs.map +1 -0
- package/dist/components/layout/Carousel.button.cjs +2 -0
- package/dist/components/layout/Carousel.button.cjs.map +1 -0
- package/dist/components/layout/Carousel.button.mjs +34 -0
- package/dist/components/layout/Carousel.button.mjs.map +1 -0
- package/dist/components/layout/Carousel.cjs +1 -1
- package/dist/components/layout/Carousel.cjs.map +1 -1
- package/dist/components/layout/Carousel.context.cjs +2 -0
- package/dist/components/layout/Carousel.context.cjs.map +1 -0
- package/dist/components/layout/Carousel.context.mjs +24 -0
- package/dist/components/layout/Carousel.context.mjs.map +1 -0
- package/dist/components/layout/Carousel.drag.cjs +2 -0
- package/dist/components/layout/Carousel.drag.cjs.map +1 -0
- package/dist/components/layout/Carousel.drag.mjs +79 -0
- package/dist/components/layout/Carousel.drag.mjs.map +1 -0
- package/dist/components/layout/Carousel.groups.cjs +2 -0
- package/dist/components/layout/Carousel.groups.cjs.map +1 -0
- package/dist/components/layout/Carousel.groups.mjs +23 -0
- package/dist/components/layout/Carousel.groups.mjs.map +1 -0
- package/dist/components/layout/Carousel.mjs +288 -150
- package/dist/components/layout/Carousel.mjs.map +1 -1
- package/dist/components/layout/Carousel.styles.cjs +2 -0
- package/dist/components/layout/Carousel.styles.cjs.map +1 -0
- package/dist/components/layout/Carousel.styles.mjs +30 -0
- package/dist/components/layout/Carousel.styles.mjs.map +1 -0
- package/dist/components/layout/TabList.cjs +1 -1
- package/dist/components/layout/TabList.cjs.map +1 -1
- package/dist/components/layout/TabList.mjs +163 -124
- package/dist/components/layout/TabList.mjs.map +1 -1
- package/dist/components/navigation/Menu.popover.cjs +1 -1
- package/dist/components/navigation/Menu.popover.cjs.map +1 -1
- package/dist/components/navigation/Menu.popover.mjs +137 -127
- package/dist/components/navigation/Menu.popover.mjs.map +1 -1
- package/dist/components/navigation/Menu.root.cjs +1 -1
- package/dist/components/navigation/Menu.root.cjs.map +1 -1
- package/dist/components/navigation/Menu.root.mjs +4 -4
- package/dist/components/overlays/Dialog.cjs +1 -1
- package/dist/components/overlays/Dialog.cjs.map +1 -1
- package/dist/components/overlays/Dialog.mjs +123 -123
- package/dist/components/overlays/Dialog.mjs.map +1 -1
- package/dist/components/overlays/Drawer.panel.cjs +1 -1
- package/dist/components/overlays/Drawer.panel.cjs.map +1 -1
- package/dist/components/overlays/Drawer.panel.mjs +12 -12
- package/dist/components/overlays/Drawer.panel.mjs.map +1 -1
- package/dist/components/overlays/Popover.cjs +1 -1
- package/dist/components/overlays/Popover.cjs.map +1 -1
- package/dist/components/overlays/Popover.mjs +173 -162
- package/dist/components/overlays/Popover.mjs.map +1 -1
- package/dist/components/overlays/TeachingPopover.cjs +1 -1
- package/dist/components/overlays/TeachingPopover.cjs.map +1 -1
- package/dist/components/overlays/TeachingPopover.mjs +96 -91
- package/dist/components/overlays/TeachingPopover.mjs.map +1 -1
- package/dist/components/overlays/Tooltip.cjs +1 -1
- package/dist/components/overlays/Tooltip.cjs.map +1 -1
- package/dist/components/overlays/Tooltip.mjs +47 -47
- package/dist/components/overlays/Tooltip.mjs.map +1 -1
- package/dist/components/portal/Portal.cjs +1 -1
- package/dist/components/portal/Portal.cjs.map +1 -1
- package/dist/components/portal/Portal.mjs +5 -5
- package/dist/components/table/DataGrid.cjs +1 -1
- package/dist/components/table/DataGrid.cjs.map +1 -1
- package/dist/components/table/DataGrid.mjs +1 -1
- package/dist/hooks/useDismiss.cjs.map +1 -1
- package/dist/hooks/useDismiss.mjs.map +1 -1
- package/dist/hooks/useFocusTrap.cjs.map +1 -1
- package/dist/hooks/useFocusTrap.mjs.map +1 -1
- package/dist/hooks/useFocusVisible.cjs +2 -0
- package/dist/hooks/useFocusVisible.cjs.map +1 -0
- package/dist/hooks/useFocusVisible.mjs +49 -0
- package/dist/hooks/useFocusVisible.mjs.map +1 -0
- package/dist/hooks/useFocusableGroup.cjs +2 -0
- package/dist/hooks/useFocusableGroup.cjs.map +1 -0
- package/dist/hooks/useFocusableGroup.mjs +70 -0
- package/dist/hooks/useFocusableGroup.mjs.map +1 -0
- package/dist/hooks/useModalLayer.cjs +1 -1
- package/dist/hooks/useModalLayer.cjs.map +1 -1
- package/dist/hooks/useModalLayer.mjs +4 -4
- package/dist/hooks/useModalOpen.cjs +2 -0
- package/dist/hooks/useModalOpen.cjs.map +1 -0
- package/dist/hooks/useModalOpen.mjs +14 -0
- package/dist/hooks/useModalOpen.mjs.map +1 -0
- package/dist/hooks/usePopupPosition.cjs +1 -1
- package/dist/hooks/usePopupPosition.cjs.map +1 -1
- package/dist/hooks/usePopupPosition.mjs +82 -53
- package/dist/hooks/usePopupPosition.mjs.map +1 -1
- package/dist/hooks/useResolvedTarget.cjs +2 -0
- package/dist/hooks/useResolvedTarget.cjs.map +1 -0
- package/dist/hooks/useResolvedTarget.mjs +37 -0
- package/dist/hooks/useResolvedTarget.mjs.map +1 -0
- package/dist/hooks/useRovingGrid.cjs +1 -1
- package/dist/hooks/useRovingGrid.cjs.map +1 -1
- package/dist/hooks/useRovingGrid.mjs +8 -57
- package/dist/hooks/useRovingGrid.mjs.map +1 -1
- package/dist/hooks/useRovingTabIndex.cjs +1 -1
- package/dist/hooks/useRovingTabIndex.cjs.map +1 -1
- package/dist/hooks/useRovingTabIndex.mjs +142 -107
- package/dist/hooks/useRovingTabIndex.mjs.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +1364 -66
- package/dist/index.d.ts +1364 -66
- package/dist/index.mjs +132 -115
- package/dist/lib/classNames.cjs +1 -1
- package/dist/lib/classNames.cjs.map +1 -1
- package/dist/lib/classNames.mjs +112 -92
- package/dist/lib/classNames.mjs.map +1 -1
- package/dist/lib/initials.cjs +2 -0
- package/dist/lib/initials.cjs.map +1 -0
- package/dist/lib/initials.mjs +21 -0
- package/dist/lib/initials.mjs.map +1 -0
- package/dist/lib/layers.cjs +1 -1
- package/dist/lib/layers.cjs.map +1 -1
- package/dist/lib/layers.mjs +112 -109
- package/dist/lib/layers.mjs.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/styles/tokens.css +102 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AvatarGroup.mjs","names":["useId","useId$1","React","DEFAULT_UNNAMED_MEMBER_LABEL","nonBlank","value","getMemberName","member","isElementOfType","Avatar","label","labelledBy","decorative","name","rendersUnnamed","imageProps","image","AvatarGroup","ariaLabel","ariaLabelledBy","props","items","flattenChildren","children","limit","max","visible","hidden","overflow","unnamed","warnOnce","popupId","open","setOpen","isOpen","overflowLabel","parentLayerId","DismissLayerContext","triggerRef","surfaceRef","surface","setSurface","usePopupPosition","useDismiss","triggerRefs","useMergedRefs","setReference","surfaceRefs","setFloating","handleSurfaceKeyDown","trigger","event","popup","origin","inner","getTabbableElements","next","el","target","order","index","isInsideLayerTree","hasUnnamedMember","node","unnamedMemberLabel","cn","avatarGroupClassNames","className","i","key","avatarSizeClasses","size","Portal","floatingProps"],"sources":["../../../src/components/data-display/AvatarGroup.tsx"],"sourcesContent":["import * as React from 'react';\nimport { flattenChildren, isElementOfType } from '../../lib/children';\nimport { cn } from '../../lib/cn';\nimport { avatarGroupClassNames } from '../../lib/classNames';\nimport { warnOnce } from '../../lib/dev';\nimport { getTabbableElements } from '../../lib/focus';\nimport { isInsideLayerTree } from '../../lib/layers';\nimport { focusRing } from '../../lib/styles';\nimport type { Size } from '../../lib/types';\nimport { useId } from '../../hooks/useId';\nimport { DismissLayerContext, useDismiss } from '../../hooks/useDismiss';\nimport { useMergedRefs } from '../../hooks/useMergedRefs';\nimport { usePopupPosition } from '../../hooks/usePopupPosition';\nimport { useRestoreFocus } from '../../hooks/useRestoreFocus';\nimport { Portal } from '../portal/Portal';\nimport { Avatar, avatarSizeClasses } from './Avatar';\nimport type { AvatarProps } from './Avatar';\n\n/** Properties for the AvatarGroup component. */\nexport interface AvatarGroupProps extends React.HTMLAttributes<HTMLDivElement> {\n /**\n * Maximum number of avatars to display. The others are replaced by an overflow button that\n * lists their names. `0` or a negative value shows only the overflow button, a fraction is\n * rounded down, and `NaN` shows every member (as without `max`). The members of a Fragment\n * count one by one.\n */\n max?: number;\n /** Size of the overflow button (match it to the avatars).\n * @default 'medium'\n */\n size?: Size;\n /**\n * Accessible name of the overflow button (and its popup). A function receives the number of\n * hidden members. Localise it here.\n * @default (count) => `${count} more`\n */\n overflowLabel?: string | ((count: number) => string);\n /**\n * Text listed in the overflow popup for a hidden member that has no accessible name (an\n * `Avatar` without `name`, `aria-label`, `aria-labelledby` or image `alt`, or blank text).\n * Localise it here; better still, give every member a name (a development warning asks for it).\n * @default 'Unnamed member'\n */\n unnamedMemberLabel?: string;\n /** Ref to the root `<div>`. */\n ref?: React.Ref<HTMLDivElement>;\n}\n\n/** Default text listed for a hidden member that would render with no accessible name. */\nconst DEFAULT_UNNAMED_MEMBER_LABEL = 'Unnamed member';\n\nconst nonBlank = (value: unknown): value is string =>\n typeof value === 'string' && value.trim() !== '';\n\n/**\n * The text name of a member for the overflow list: the text of a text member, or the accessible\n * name of an `Avatar` given by its props (`aria-label` before `name`). An `Avatar` labelled by\n * `aria-labelledby` (unless `decorative`) and every other element have none: they are rendered as\n * they are, so a link keeps working and keeps its own label.\n */\nfunction getMemberName(member: React.ReactNode): string | undefined {\n if (typeof member === 'string' || typeof member === 'number') {\n return String(member).trim() || undefined;\n }\n if (!isElementOfType<AvatarProps>(member, Avatar)) return undefined;\n const { decorative, name, 'aria-label': label, 'aria-labelledby': labelledBy } = member.props;\n if (!decorative && nonBlank(labelledBy)) return undefined;\n for (const value of [label, name]) {\n if (nonBlank(value)) return value.trim();\n }\n return undefined;\n}\n\n/**\n * Whether a member without a {@link getMemberName} name would render with no accessible name,\n * so its list item would be empty for assistive technology: blank text, or an `Avatar` that is\n * neither labelled by `aria-labelledby` nor named by its image's `alt` (or is `decorative`).\n * Other elements are rendered as they are, since their content is unknown here.\n */\nfunction rendersUnnamed(member: React.ReactNode): boolean {\n if (typeof member === 'string' || typeof member === 'number') return true;\n if (!isElementOfType<AvatarProps>(member, Avatar)) return false;\n const { decorative, image, 'aria-labelledby': labelledBy } = member.props;\n if (decorative) return true;\n if (nonBlank(labelledBy)) return false;\n const imageProps: unknown = React.isValidElement(image) ? image.props : image;\n const alt =\n typeof imageProps === 'object' && imageProps !== null && 'alt' in imageProps\n ? imageProps.alt\n : undefined;\n return !nonBlank(alt);\n}\n\n/**\n * A row of overlapping avatars (`role=\"group\"`; name it with `aria-label`).\n *\n * With `max`, the avatars beyond the limit are replaced by an overflow `<button>` (\"+3\", named\n * \"3 more\", see `overflowLabel`). It toggles a popup that lists the hidden members' names: an\n * `Avatar` by its `aria-label` or `name` prop, text as it is. Other members, such as an `Avatar`\n * inside a link or one labelled by `aria-labelledby`, are rendered as they are, and an `Avatar` or\n * text with no accessible name is listed as `unnamedMemberLabel` with a development warning.\n * Focus moves into the popup; Escape, a press outside or the button close it and focus returns to\n * the button. Tab and Shift+Tab move through the popup's own tab stops (links in rendered\n * members); Tab from the last one closes the popup and moves on to the element after the button,\n * Shift+Tab from the first one closes it and returns to the button. When the button is the last\n * tab stop (of the page, or of the Wave layer the group is in, such as a dialog), Tab is left to\n * the browser or the enclosing focus trap.\n *\n * @example\n * <AvatarGroup aria-label=\"Project team\" max={3}>\n * <Avatar name=\"Ada Lovelace\" />\n * <Avatar name=\"Alan Turing\" />\n * </AvatarGroup>\n */\nexport const AvatarGroup = ({\n max,\n size = 'medium',\n overflowLabel,\n unnamedMemberLabel = DEFAULT_UNNAMED_MEMBER_LABEL,\n className,\n children,\n ref,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n ...props\n}: AvatarGroupProps) => {\n // Fragments are flattened, so each member counts, overlaps and is listed on its own (C-COMPOUND).\n const items = flattenChildren(children);\n const limit = max === undefined || Number.isNaN(max) ? Infinity : Math.max(0, Math.floor(max));\n const visible = items.slice(0, limit);\n const hidden = items.slice(visible.length);\n const overflow = hidden.length;\n\n const unnamed = ariaLabel === undefined && ariaLabelledBy === undefined;\n React.useEffect(() => {\n if (unnamed) {\n warnOnce(\n 'AvatarGroup:unnamed',\n 'AvatarGroup: the group has no accessible name. Pass `aria-label` (or `aria-labelledby`) describing the people it shows.',\n );\n }\n }, [unnamed]);\n\n const popupId = useId('avatar-group-overflow');\n const [open, setOpen] = React.useState(false);\n // Nothing is hidden any more: close, so the popup does not reappear when members overflow again.\n if (open && overflow === 0) setOpen(false);\n const isOpen = open && overflow > 0;\n const label =\n typeof overflowLabel === 'function'\n ? overflowLabel(overflow)\n : (overflowLabel ?? `${overflow} more`);\n\n // The layer the group is rendered in (a dialog or popover), if any: the popup's parent layer.\n const parentLayerId = React.useContext(DismissLayerContext);\n const triggerRef = React.useRef<HTMLButtonElement | null>(null);\n const surfaceRef = React.useRef<HTMLDivElement | null>(null);\n const [surface, setSurface] = React.useState<HTMLDivElement | null>(null);\n\n const { setReference, setFloating, floatingProps } = usePopupPosition({\n open: isOpen,\n side: 'bottom',\n align: 'start',\n });\n const { layerId } = useDismiss({\n open: isOpen,\n onDismiss: () => setOpen(false),\n refs: [triggerRef, surfaceRef],\n anchorRef: triggerRef,\n focusOutside: true,\n });\n useRestoreFocus({ enabled: isOpen, container: surface, triggerRef, onlyIfFocusInside: true });\n const triggerRefs = useMergedRefs<HTMLButtonElement>(triggerRef, setReference);\n const surfaceRefs = useMergedRefs<HTMLDivElement>(surfaceRef, setFloating, setSurface);\n\n // Move focus into the popup once it is mounted, so its list is read next.\n React.useEffect(() => {\n if (isOpen && surface) surface.focus({ preventScroll: true });\n }, [isOpen, surface]);\n\n // The popup is portaled, so native tabbing would leave it for the end of the page. Tab moves\n // through the popup's own tab stops (a rendered member can be a link); leaving the last one\n // closes the popup and moves on from the button instead (Shift+Tab before the first one: back to\n // the button), like a focus trap does for a popup anchored in a dialog. When nothing tabbable\n // follows the button, Tab is not prevented: focus returns to the button as the popup closes and\n // the browser (leaving the page) or an enclosing focus trap (wrapping) takes it from there.\n // Inside a parent layer (a dialog, a popover), a tab stop after the button that lies outside that\n // layer is treated the same way: a focus trap that leaves the page outside it interactive would\n // pull focus back from there, while its descendant-layer rule (§2.4) wraps from the button.\n // Tab stops are computed at keydown time, never cached (§2.2 focus utilities). A key that\n // bubbles through React from a portal a member opened is not the popup's: it is ignored.\n const handleSurfaceKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n const trigger = triggerRef.current;\n if (event.key !== 'Tab' || event.defaultPrevented || !trigger) return;\n const popup = event.currentTarget;\n // Duck typed (`nodeType`), not `instanceof Node`: a popup portaled into another document (an\n // iframe, a popout window) holds nodes of that realm.\n const origin = event.target as Partial<Node> | null;\n if (!origin || typeof origin.nodeType !== 'number' || !popup.contains(origin as Node)) return;\n const inner = getTabbableElements(popup);\n // -1 while focus is on the popup itself (or on something that is not a tab stop).\n const current = inner.findIndex((el) => el.contains(origin as Node));\n const next = current + (event.shiftKey ? -1 : 1);\n if (next >= 0 && next < inner.length) {\n event.preventDefault();\n inner[next]!.focus();\n return;\n }\n let target: HTMLElement | undefined = trigger;\n if (!event.shiftKey) {\n const order = getTabbableElements(trigger.ownerDocument.body).filter(\n (el) => !popup.contains(el),\n );\n const index = order.indexOf(trigger);\n target = index === -1 ? trigger : order[index + 1];\n if (target && parentLayerId !== null && !isInsideLayerTree(parentLayerId, target)) {\n target = undefined;\n }\n }\n setOpen(false);\n if (!target) return;\n event.preventDefault();\n target.focus();\n };\n\n const hasUnnamedMember =\n isOpen && hidden.some(({ node }) => getMemberName(node) === undefined && rendersUnnamed(node));\n React.useEffect(() => {\n if (hasUnnamedMember) {\n warnOnce(\n 'AvatarGroup:unnamed-member',\n `AvatarGroup: a hidden member has no accessible name and is listed as \"${unnamedMemberLabel}\". Give each member a \\`name\\` or \\`aria-label\\`.`,\n );\n }\n }, [hasUnnamedMember, unnamedMemberLabel]);\n\n return (\n <div\n ref={ref}\n role=\"group\"\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n {...props}\n className={cn(avatarGroupClassNames.root, 'inline-flex items-center', className)}\n >\n {visible.map(({ key, node }, i) => (\n <span\n key={key}\n className={cn(\n avatarGroupClassNames.item,\n 'inline-flex rounded-full ring-2 ring-background',\n i > 0 && '-ms-2',\n )}\n >\n {node}\n </span>\n ))}\n {overflow > 0 && (\n <button\n ref={triggerRefs}\n type=\"button\"\n aria-label={label}\n aria-haspopup=\"dialog\"\n aria-expanded={isOpen}\n aria-controls={isOpen ? popupId : undefined}\n className={cn(\n avatarGroupClassNames.overflowButton,\n 'inline-flex shrink-0 cursor-pointer items-center justify-center rounded-full border-0 p-0 font-semibold',\n 'bg-muted text-muted-foreground ring-2 ring-background',\n 'not-disabled:not-aria-disabled:hover:bg-subtle-pressed',\n focusRing,\n visible.length > 0 && '-ms-2',\n avatarSizeClasses[size],\n )}\n onClick={() => setOpen((value) => !value)}\n >\n +{overflow}\n </button>\n )}\n {isOpen && (\n <Portal layerId={layerId}>\n <div\n ref={surfaceRefs}\n id={popupId}\n role=\"dialog\"\n aria-label={label}\n tabIndex={-1}\n data-state=\"open\"\n {...floatingProps}\n className={cn(\n avatarGroupClassNames.surface,\n 'min-w-32 max-w-xs rounded-md border border-border bg-background p-2 text-body-1 text-foreground shadow-16',\n focusRing,\n )}\n onKeyDown={handleSurfaceKeyDown}\n >\n <ul className={cn(avatarGroupClassNames.list, 'm-0 flex list-none flex-col gap-1 p-0')}>\n {hidden.map(({ key, node }) => (\n <li key={key} className={cn(avatarGroupClassNames.listItem, 'px-1 py-0.5')}>\n {getMemberName(node) ?? (rendersUnnamed(node) ? unnamedMemberLabel : node)}\n </li>\n ))}\n </ul>\n </div>\n </Portal>\n )}\n </div>\n );\n};\n\nAvatarGroup.displayName = 'AvatarGroup';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AvatarGroup.mjs","names":["React","pieWedgeClasses","AvatarGroupRoot","DEFAULT_UNNAMED_MEMBER_LABEL","ariaLabel","ariaLabelledBy","props","parts","flattenChildren","children","customPopover","isElementOfType","node","AvatarGroupPopover","items","resolvedSize","size","layout","visible","partitionAvatarGroupItems","max","hidden","overflowItems","hasInlineItems","groupContext","unnamed","warnOnce","cn","avatarGroupClassNames","className","AvatarGroupContext","avatarSizeClassesFor","i","key","overflowIndicator","overflowLabel","AvatarGroup","AvatarGroupItem"],"sources":["../../../src/components/data-display/AvatarGroup.tsx"],"sourcesContent":["import * as React from 'react';\nimport { flattenChildren, isElementOfType } from '../../lib/children';\nimport { cn } from '../../lib/cn';\nimport { avatarGroupClassNames } from '../../lib/classNames';\nimport { warnOnce } from '../../lib/dev';\nimport { avatarSizeClassesFor, type AvatarSize } from './Avatar.sizes';\nimport { AvatarGroupContext } from './AvatarGroup.context';\nimport { partitionAvatarGroupItems, type AvatarGroupLayout } from './AvatarGroup.partition';\nimport {\n AvatarGroupItem,\n AvatarGroupPopover,\n DEFAULT_UNNAMED_MEMBER_LABEL,\n type AvatarGroupItemProps,\n type AvatarGroupOpenChangeDetails,\n type AvatarGroupPopoverProps,\n} from './AvatarGroup.parts';\n\nexport { AvatarGroupItem, AvatarGroupPopover };\nexport type { AvatarGroupItemProps, AvatarGroupOpenChangeDetails, AvatarGroupPopoverProps };\nexport type { AvatarGroupLayout };\n\n/** Properties for the AvatarGroup component. */\nexport interface AvatarGroupProps extends React.HTMLAttributes<HTMLDivElement> {\n /**\n * Maximum number of avatars to display. The others are replaced by an overflow button that\n * lists their names. `0` or a negative value shows only the overflow button, a fraction is\n * rounded down, and `NaN` shows every member (as without `max`). The members of a Fragment\n * count one by one. A `pie` shows at most three.\n */\n max?: number;\n /**\n * Size of the avatars. It reaches the members that set no `size` of their own, and the overflow\n * button.\n * @default 'medium' ('large' in a `pie`)\n */\n size?: AvatarSize;\n /**\n * How the members are laid out: `stack` overlaps them, `spread` puts them side by side, `pie`\n * draws up to three of them as the wedges of one circle (the group's name is then the only name).\n * @default 'stack'\n */\n layout?: AvatarGroupLayout;\n /**\n * What the overflow button shows: the number of hidden members (`+3`, capped at `99+`), or an\n * icon.\n * @default 'count'\n */\n overflowIndicator?: 'count' | 'icon';\n /**\n * Accessible name of the overflow button (and its popup). A function receives the number of\n * hidden members. Localise it here.\n * @default (count) => `${count} more`\n */\n overflowLabel?: string | ((count: number) => string);\n /**\n * Text listed in the overflow popup for a hidden member that has no accessible name (an\n * `Avatar` without `name`, `aria-label`, `aria-labelledby` or image `alt`, or blank text).\n * Localise it here; better still, give every member a name (a development warning asks for it).\n * @default 'Unnamed member'\n */\n unnamedMemberLabel?: string;\n /** Whether the overflow popup is open (controlled). */\n open?: boolean;\n /**\n * Whether the overflow popup starts open (uncontrolled).\n * @default false\n */\n defaultOpen?: boolean;\n /**\n * Called when the overflow popup asks to open or close, with the reason: `trigger` (the button),\n * `escape`, `outside-press` or `focus-outside`.\n */\n onOpenChange?: (open: boolean, details?: AvatarGroupOpenChangeDetails) => void;\n /** Ref to the root `<div>`. */\n ref?: React.Ref<HTMLDivElement>;\n}\n\n/** The clip of each wedge of a pie, by the number of wedges (one wedge is a full circle). Literal strings only. */\nconst pieWedgeClasses: ReadonlyArray<ReadonlyArray<string>> = [\n [''],\n ['[clip-path:inset(0_50%_0_0)]', '[clip-path:inset(0_0_0_50%)]'],\n [\n '[clip-path:inset(0_50%_0_0)]',\n '[clip-path:inset(0_0_50%_50%)]',\n '[clip-path:inset(50%_0_0_50%)]',\n ],\n];\n\nconst AvatarGroupRoot = ({\n max,\n size,\n layout = 'stack',\n overflowIndicator = 'count',\n overflowLabel,\n unnamedMemberLabel = DEFAULT_UNNAMED_MEMBER_LABEL,\n open,\n defaultOpen,\n onOpenChange,\n className,\n children,\n ref,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n ...props\n}: AvatarGroupProps) => {\n // Fragments are flattened, so each member counts, overlaps and is listed on its own (C-COMPOUND).\n // A consumer-written AvatarGroup.Popover is the overflow part, not a member.\n const parts = flattenChildren(children);\n const customPopover = parts.find(({ node }) => isElementOfType(node, AvatarGroupPopover));\n const items = parts.filter(({ node }) => !isElementOfType(node, AvatarGroupPopover));\n const resolvedSize: AvatarSize = size ?? (layout === 'pie' ? 'large' : 'medium');\n const { inlineItems: visible, overflowItems } = partitionAvatarGroupItems({\n items,\n maxInlineItems: max,\n layout,\n });\n const hidden = overflowItems ?? [];\n const hasInlineItems = visible.length > 0;\n const groupContext = React.useMemo(\n () => ({ size: resolvedSize, layout, hasInlineItems }),\n [resolvedSize, layout, hasInlineItems],\n );\n\n const unnamed = ariaLabel === undefined && ariaLabelledBy === undefined;\n React.useEffect(() => {\n if (unnamed) {\n warnOnce(\n 'AvatarGroup:unnamed',\n 'AvatarGroup: the group has no accessible name. Pass `aria-label` (or `aria-labelledby`) describing the people it shows.',\n );\n }\n }, [unnamed]);\n\n return (\n <div\n ref={ref}\n role=\"group\"\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n data-layout={layout}\n {...props}\n className={cn(\n avatarGroupClassNames.root,\n 'inline-flex items-center',\n layout !== 'stack' && 'gap-1',\n className,\n )}\n >\n <AvatarGroupContext.Provider value={groupContext}>\n {layout === 'pie'\n ? visible.length > 0 && (\n <span\n className={cn(\n avatarGroupClassNames.pie,\n 'relative inline-block shrink-0 overflow-hidden rounded-full',\n avatarSizeClassesFor(resolvedSize),\n )}\n >\n {visible.map(({ key, node }, i) => (\n <span\n key={key}\n aria-hidden=\"true\"\n className={cn(\n avatarGroupClassNames.item,\n 'absolute inset-0 inline-flex',\n pieWedgeClasses[visible.length - 1]?.[i],\n )}\n >\n {node}\n </span>\n ))}\n </span>\n )\n : visible.map(({ key, node }, i) => (\n <span\n key={key}\n className={cn(\n avatarGroupClassNames.item,\n 'inline-flex rounded-full ring-2 ring-background',\n layout === 'stack' && i > 0 && '-ms-2',\n )}\n >\n {node}\n </span>\n ))}\n {customPopover\n ? customPopover.node\n : hidden.length > 0 && (\n <AvatarGroupPopover\n count={hidden.length}\n indicator={overflowIndicator}\n label={overflowLabel}\n unnamedMemberLabel={unnamedMemberLabel}\n open={open}\n defaultOpen={defaultOpen}\n onOpenChange={onOpenChange}\n >\n {hidden.map(({ key, node }) => (\n <React.Fragment key={key}>{node}</React.Fragment>\n ))}\n </AvatarGroupPopover>\n )}\n </AvatarGroupContext.Provider>\n </div>\n );\n};\n\nAvatarGroupRoot.displayName = 'AvatarGroup';\n\n/**\n * A row of avatars (`role=\"group\"`; name it with `aria-label`), laid out as a `stack` (overlapping),\n * `spread` or `pie` (`layout`). `size` reaches the members.\n *\n * With `max`, the avatars beyond the limit are replaced by an overflow `<button>` (\"+3\", named\n * \"3 more\", see `overflowLabel`; `overflowIndicator=\"icon\"` shows an icon, and a count above 99\n * reads \"99+\"). It toggles a popup that lists the hidden members: an `AvatarGroup.Item` as a row\n * with its avatar and name, an `Avatar` by its `aria-label` or `name` prop, text as it is. Other\n * members, such as an `Avatar` inside a link or one labelled by `aria-labelledby`, are rendered as\n * they are, and an `Avatar` or text with no accessible name is listed as `unnamedMemberLabel` with\n * a development warning. Focus moves into the popup; Escape, a press outside or the button close\n * it and focus returns to the button (`open`, `defaultOpen` and `onOpenChange(open, details)`\n * control it). Tab and Shift+Tab move through the popup's own tab stops (links in rendered\n * members); Tab from the last one closes the popup and moves on to the element after the button,\n * Shift+Tab from the first one closes it and returns to the button. When the button is the last\n * tab stop (of the page, or of the Wave layer the group is in, such as a dialog), Tab is left to\n * the browser or the enclosing focus trap.\n *\n * To split the members yourself, render `AvatarGroup.Item`s and an `AvatarGroup.Popover` holding\n * the overflow items (see `partitionAvatarGroupItems`).\n *\n * @example\n * <AvatarGroup aria-label=\"Project team\" max={3}>\n * <AvatarGroup.Item name=\"Ada Lovelace\" />\n * <AvatarGroup.Item name=\"Alan Turing\" />\n * </AvatarGroup>\n */\nexport const AvatarGroup = /* @__PURE__ */ Object.assign(AvatarGroupRoot, {\n Item: AvatarGroupItem,\n Popover: AvatarGroupPopover,\n});\n"],"mappings":";;;;;;;;;;;;AA8EA,IAAMC,IAAwD;CAC5D,CAAC,EAAE;CACH,CAAC,gCAAgC,8BAA8B;CAC/D;EACE;EACA;EACA;CACF;AACF,GAEMC,KAAmB,EACvB,QACA,SACA,YAAS,SACT,uBAAoB,SACpB,kBACA,wBAAqBC,GACrB,SACA,gBACA,iBACA,cACA,aACA,QACA,cAAcC,GACd,mBAAmBC,GACnB,GAAGC,QACmB;CAGtB,IAAMC,IAAQC,EAAgBC,CAAQ,GAChCC,IAAgBH,EAAM,MAAM,EAAE,cAAWI,EAAgBC,GAAMC,CAAkB,CAAC,GAClFC,IAAQP,EAAM,QAAQ,EAAE,cAAW,CAACI,EAAgBC,GAAMC,CAAkB,CAAC,GAC7EE,IAA2BC,MAASC,MAAW,QAAQ,UAAU,WACjE,EAAE,aAAaC,GAAS,qBAAkBC,EAA0B;EACxE;EACA,gBAAgBC;EAChB;CACF,CAAC,GACKC,IAASC,KAAiB,CAAC,GAC3BC,IAAiBL,EAAQ,SAAS,GAClCM,IAAexB,EAAM,eAClB;EAAE,MAAMe;EAAc;EAAQ;CAAe,IACpD;EAACA;EAAcE;EAAQM;CAAc,CACvC,GAEME,IAAUrB,MAAc,KAAA,KAAaC,MAAmB,KAAA;CAU9D,OATAL,EAAM,gBAAgB;EACpB,AAAIyB,KACFC,EACE,uBACA,yHACF;CAEJ,GAAG,CAACD,CAAO,CAAC,GAGV,kBAAC,OAAD;EACO;EACL,MAAK;EACL,cAAYrB;EACZ,mBAAiBC;EACjB,eAAaY;EACb,GAAIX;EACJ,WAAWqB,EACTC,EAAsB,MACtB,4BACAX,MAAW,WAAW,SACtBY,CACF;EAEA,UAAA,kBAACC,EAAmB,UAApB;GAA6B,OAAON;GAApC,UAAA,CACGP,MAAW,QACRC,EAAQ,SAAS,KACf,kBAAC,QAAD;IACE,WAAWS,EACTC,EAAsB,KACtB,+DACAG,EAAqBhB,CAAY,CACnC;IAEC,UAAAG,EAAQ,KAAK,EAAE,QAAK,WAAQ,MAC3B,kBAAC,QAAD;KAEE,eAAY;KACZ,WAAWS,EACTC,EAAsB,MACtB,gCACA3B,EAAgBiB,EAAQ,SAAS,EAAE,GAAGc,EACxC;KAEC,UAAApB;IACG,GATCqB,CASD,CACP;GACG,CAAA,IAERf,EAAQ,KAAK,EAAE,QAAK,WAAQ,MAC1B,kBAAC,QAAD;IAEE,WAAWS,EACTC,EAAsB,MACtB,mDACAX,MAAW,WAAWe,IAAI,KAAK,OACjC;IAEC,UAAApB;GACG,GARCqB,CAQD,CACP,GACJvB,IACGA,EAAc,OACdW,EAAO,SAAS,KACd,kBAACR,GAAD;IACE,OAAOQ,EAAO;IACd,WAAWa;IACX,OAAOC;IACa;IACd;IACO;IACC;IAEb,UAAAd,EAAO,KAAK,EAAE,QAAK,cAClB,kBAACrB,EAAM,UAAP,EAAA,UAA2BY,EAAqB,GAA3BqB,CAA2B,CACjD;GACiB,CAAA,CAEC;;CAC1B,CAAA;AAET;AAEA,EAAgB,cAAc;AA6B9B,IAAaG,IAA8B,uBAAO,OAAOlC,GAAiB;CACxE,MAAMmC;CACN,SAASxB;AACX,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use client";function e({items:e,maxInlineItems:t,layout:n=`stack`}){let r=t===void 0||Number.isNaN(t)?1/0:Math.max(0,Math.floor(t));n===`pie`&&(r=Math.min(r,3));let i=e.slice(0,r);return{inlineItems:i,overflowItems:e.length>i.length?e.slice(i.length):void 0}}exports.partitionAvatarGroupItems=e;
|
|
2
|
+
//# sourceMappingURL=AvatarGroup.partition.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AvatarGroup.partition.cjs","names":["partitionAvatarGroupItems","limit","maxInlineItems","layout","inlineItems","items"],"sources":["../../../src/components/data-display/AvatarGroup.partition.ts"],"sourcesContent":["/** The layouts of an AvatarGroup. */\nexport type AvatarGroupLayout = 'stack' | 'spread' | 'pie';\n\n/** Options of {@link partitionAvatarGroupItems}. */\nexport interface PartitionAvatarGroupItemsOptions<T> {\n /** All the items of the group. */\n items: readonly T[];\n /**\n * How many items are shown inline; the rest go to the overflow. A fraction is rounded down, `0`\n * or a negative value shows none, and `undefined` or `NaN` shows all (up to the layout's cap).\n */\n maxInlineItems?: number;\n /**\n * The layout of the group. A `pie` shows at most three items inline.\n * @default 'stack'\n */\n layout?: AvatarGroupLayout;\n}\n\n/** Result of {@link partitionAvatarGroupItems}. */\nexport interface PartitionAvatarGroupItemsResult<T> {\n /** The items shown inline, in order. */\n inlineItems: T[];\n /** The items in the overflow list, in order; `undefined` when nothing overflows. */\n overflowItems: T[] | undefined;\n}\n\n/** The most items a pie shows inline. */\nconst PIE_INLINE_LIMIT = 3;\n\n/**\n * Splits the items of an AvatarGroup into the ones shown inline and the ones in the overflow list.\n * `AvatarGroup` uses it for `max`; use it yourself to render `AvatarGroup.Item`s and\n * `AvatarGroup.Popover` from your own list. Server-safe: it imports nothing.\n *\n * @example\n * const { inlineItems, overflowItems } = partitionAvatarGroupItems({ items: people, maxInlineItems: 4 });\n */\nexport function partitionAvatarGroupItems<T>({\n items,\n maxInlineItems,\n layout = 'stack',\n}: PartitionAvatarGroupItemsOptions<T>): PartitionAvatarGroupItemsResult<T> {\n let limit =\n maxInlineItems === undefined || Number.isNaN(maxInlineItems)\n ? Infinity\n : Math.max(0, Math.floor(maxInlineItems));\n if (layout === 'pie') limit = Math.min(limit, PIE_INLINE_LIMIT);\n const inlineItems = items.slice(0, limit);\n return {\n inlineItems,\n overflowItems: items.length > inlineItems.length ? items.slice(inlineItems.length) : undefined,\n };\n}\n"],"mappings":"aAsCA,SAAgBA,EAA6B,CAC3C,QACA,iBACA,SAAS,SACiE,CAC1E,IAAIC,EACFC,IAAmB,IAAA,IAAa,OAAO,MAAMA,CAAc,EACvD,IACA,KAAK,IAAI,EAAG,KAAK,MAAMA,CAAc,CAAC,EACxCC,IAAW,QAAO,EAAQ,KAAK,IAAIF,EAAO,CAAgB,GAC9D,IAAMG,EAAcC,EAAM,MAAM,EAAGJ,CAAK,EACxC,MAAO,CACL,cACA,cAAeI,EAAM,OAASD,EAAY,OAASC,EAAM,MAAMD,EAAY,MAAM,EAAI,IAAA,EACvF,CACF"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
function e({ items: e, maxInlineItems: t, layout: n = "stack" }) {
|
|
3
|
+
let r = t === void 0 || Number.isNaN(t) ? Infinity : Math.max(0, Math.floor(t));
|
|
4
|
+
n === "pie" && (r = Math.min(r, 3));
|
|
5
|
+
let i = e.slice(0, r);
|
|
6
|
+
return {
|
|
7
|
+
inlineItems: i,
|
|
8
|
+
overflowItems: e.length > i.length ? e.slice(i.length) : void 0
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
//#endregion
|
|
12
|
+
export { e as partitionAvatarGroupItems };
|
|
13
|
+
|
|
14
|
+
//# sourceMappingURL=AvatarGroup.partition.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AvatarGroup.partition.mjs","names":["partitionAvatarGroupItems","limit","maxInlineItems","layout","inlineItems","items"],"sources":["../../../src/components/data-display/AvatarGroup.partition.ts"],"sourcesContent":["/** The layouts of an AvatarGroup. */\nexport type AvatarGroupLayout = 'stack' | 'spread' | 'pie';\n\n/** Options of {@link partitionAvatarGroupItems}. */\nexport interface PartitionAvatarGroupItemsOptions<T> {\n /** All the items of the group. */\n items: readonly T[];\n /**\n * How many items are shown inline; the rest go to the overflow. A fraction is rounded down, `0`\n * or a negative value shows none, and `undefined` or `NaN` shows all (up to the layout's cap).\n */\n maxInlineItems?: number;\n /**\n * The layout of the group. A `pie` shows at most three items inline.\n * @default 'stack'\n */\n layout?: AvatarGroupLayout;\n}\n\n/** Result of {@link partitionAvatarGroupItems}. */\nexport interface PartitionAvatarGroupItemsResult<T> {\n /** The items shown inline, in order. */\n inlineItems: T[];\n /** The items in the overflow list, in order; `undefined` when nothing overflows. */\n overflowItems: T[] | undefined;\n}\n\n/** The most items a pie shows inline. */\nconst PIE_INLINE_LIMIT = 3;\n\n/**\n * Splits the items of an AvatarGroup into the ones shown inline and the ones in the overflow list.\n * `AvatarGroup` uses it for `max`; use it yourself to render `AvatarGroup.Item`s and\n * `AvatarGroup.Popover` from your own list. Server-safe: it imports nothing.\n *\n * @example\n * const { inlineItems, overflowItems } = partitionAvatarGroupItems({ items: people, maxInlineItems: 4 });\n */\nexport function partitionAvatarGroupItems<T>({\n items,\n maxInlineItems,\n layout = 'stack',\n}: PartitionAvatarGroupItemsOptions<T>): PartitionAvatarGroupItemsResult<T> {\n let limit =\n maxInlineItems === undefined || Number.isNaN(maxInlineItems)\n ? Infinity\n : Math.max(0, Math.floor(maxInlineItems));\n if (layout === 'pie') limit = Math.min(limit, PIE_INLINE_LIMIT);\n const inlineItems = items.slice(0, limit);\n return {\n inlineItems,\n overflowItems: items.length > inlineItems.length ? items.slice(inlineItems.length) : undefined,\n };\n}\n"],"mappings":";AAsCA,SAAgBA,EAA6B,EAC3C,UACA,mBACA,YAAS,WACiE;CAC1E,IAAIC,IACFC,MAAmB,KAAA,KAAa,OAAO,MAAMA,CAAc,IACvD,WACA,KAAK,IAAI,GAAG,KAAK,MAAMA,CAAc,CAAC;CAC5C,AAAIC,MAAW,UAAO,IAAQ,KAAK,IAAIF,GAAO,CAAgB;CAC9D,IAAMG,IAAcC,EAAM,MAAM,GAAGJ,CAAK;CACxC,OAAO;EACL;EACA,eAAeI,EAAM,SAASD,EAAY,SAASC,EAAM,MAAMD,EAAY,MAAM,IAAI,KAAA;CACvF;AACF"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use client";const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../../lib/cn.cjs"),n=require("../../lib/children.cjs"),r=require("../../lib/dev.cjs"),i=require("../../lib/classNames.cjs"),a=require("../../hooks/useControllable.cjs"),o=require("../../hooks/useId.cjs"),s=require("../../lib/focus.cjs"),c=require("../../hooks/useMergedRefs.cjs"),l=require("../../hooks/usePopupPosition.cjs"),u=require("../../lib/layers.cjs"),d=require("../../hooks/useDismiss.cjs"),f=require("../../hooks/useRestoreFocus.cjs"),p=require("../../lib/styles.cjs"),m=require("../portal/Portal.cjs"),ee=require("./AvatarGroup.context.cjs"),h=require("./Avatar.sizes.cjs"),g=require("./Avatar.cjs");let _=require("react");_=e.__toESM(_,1);let v=require("react/jsx-runtime");var y=`Unnamed member`,b=e=>typeof e==`string`&&e.trim()!==``,x=_.createContext(null);x.displayName=`AvatarGroupOverflowContext`;var S=({className:e,...n})=>{let r=_.useContext(x);if(!r)return(0,v.jsx)(g.Avatar,{...n,className:t.cn(i.avatarGroupItemClassNames.root,e)});let{name:a,"aria-label":o,size:s,ref:c,...l}=n,u=[o,a].find(b)?.trim()??r.unnamedMemberLabel;return(0,v.jsxs)(`span`,{className:t.cn(i.avatarGroupItemClassNames.root,`inline-flex items-center gap-2`,e),children:[(0,v.jsx)(g.Avatar,{...l,name:a,size:s??`small`,decorative:!0}),(0,v.jsx)(`span`,{className:i.avatarGroupItemClassNames.name,children:u})]})};S.displayName=`AvatarGroupItem`;var C=S;function w(e){if(typeof e==`string`||typeof e==`number`)return String(e).trim()||void 0;if(!n.isElementOfType(e,g.Avatar)&&!n.isElementOfType(e,C))return;let{decorative:t,name:r,"aria-label":i,"aria-labelledby":a}=e.props;if(t||!b(a)){for(let e of[i,r])if(b(e))return e.trim()}}function T(e){if(typeof e==`string`||typeof e==`number`)return!0;if(!n.isElementOfType(e,g.Avatar))return!1;let{decorative:t,image:r,"aria-labelledby":i}=e.props;if(t)return!0;if(b(i))return!1;let a=_.isValidElement(r)?r.props:r;return!b(typeof a==`object`&&a&&`alt`in a?a.alt:void 0)}function E(e){return e>99?`99+`:`+${e}`}var D=()=>(0,v.jsxs)(`svg`,{viewBox:`0 0 16 16`,width:`16`,height:`16`,fill:`currentColor`,"aria-hidden":`true`,focusable:`false`,children:[(0,v.jsx)(`circle`,{cx:`3`,cy:`8`,r:`1.5`}),(0,v.jsx)(`circle`,{cx:`8`,cy:`8`,r:`1.5`}),(0,v.jsx)(`circle`,{cx:`13`,cy:`8`,r:`1.5`})]}),O=({children:e,indicator:g=`count`,count:b,label:S,unnamedMemberLabel:O=y,open:k,defaultOpen:A=!1,onOpenChange:j,className:M,ref:N})=>{let P=_.useContext(ee.AvatarGroupContext),F=n.flattenChildren(e),I=b??F.length,L=P?.size??`medium`,R=typeof S==`function`?S(I):S??`${I} more`,z=o.useId(`avatar-group-overflow`),[B,V]=a.useControllable(k,A),H=B&&I>0,U=(e,t,n)=>{e!==H&&(V(e),j?.(e,{reason:t,event:n}))},W=_.useContext(d.DismissLayerContext),G=_.useRef(null),K=_.useRef(null),[q,J]=_.useState(null),{setReference:Y,setFloating:X,floatingProps:Z}=l.usePopupPosition({open:H,side:`bottom`,align:`start`}),{layerId:Q}=d.useDismiss({open:H,onDismiss:(e,t)=>U(!1,e,t),refs:[G,K],anchorRef:G,focusOutside:!0});f.useRestoreFocus({enabled:H,container:q,triggerRef:G,onlyIfFocusInside:!0});let te=c.useMergedRefs(G,Y,N),ne=c.useMergedRefs(K,X,J);_.useEffect(()=>{H&&q&&q.focus({preventScroll:!0})},[H,q]);let re=e=>{let t=G.current;if(e.key!==`Tab`||e.defaultPrevented||!t)return;let n=e.currentTarget,r=e.target;if(!r||typeof r.nodeType!=`number`||!n.contains(r))return;let i=s.getTabbableElements(n),a=i.findIndex(e=>e.contains(r))+(e.shiftKey?-1:1);if(a>=0&&a<i.length){e.preventDefault(),i[a].focus();return}let o=t;if(!e.shiftKey){let e=s.getTabbableElements(t.ownerDocument.body).filter(e=>!n.contains(e)),r=e.indexOf(t);o=r===-1?t:e[r+1],o&&W!==null&&!u.isInsideLayerTree(W,o)&&(o=void 0)}U(!1,`escape`,e.nativeEvent),o&&(e.preventDefault(),o.focus())},$=H&&F.some(({node:e})=>n.isElementOfType(e,C)?w(e)===void 0:w(e)===void 0&&T(e));_.useEffect(()=>{$&&r.warnOnce(`AvatarGroup:unnamed-member`,`AvatarGroup: a hidden member has no accessible name and is listed as "${O}". Give each member a \`name\` or \`aria-label\`.`)},[$,O]);let ie=_.useMemo(()=>({unnamedMemberLabel:O}),[O]);return(0,v.jsxs)(v.Fragment,{children:[(0,v.jsx)(`button`,{ref:te,type:`button`,"aria-label":R,"aria-haspopup":`dialog`,"aria-expanded":H,"aria-controls":H?z:void 0,className:t.cn(i.avatarGroupClassNames.overflowButton,i.avatarGroupPopoverClassNames.root,`inline-flex shrink-0 cursor-pointer items-center justify-center rounded-full border-0 p-0 font-semibold`,`bg-muted text-muted-foreground ring-2 ring-background`,`not-disabled:not-aria-disabled:hover:bg-subtle-pressed`,p.focusRing,P?.layout===`stack`&&P.hasInlineItems&&`-ms-2`,h.avatarSizeClassesFor(L),M),onClick:e=>U(!H,`trigger`,e.nativeEvent),children:g===`icon`?(0,v.jsx)(D,{}):E(I)}),H&&(0,v.jsx)(m.Portal,{layerId:Q,children:(0,v.jsx)(`div`,{ref:ne,id:z,role:`dialog`,"aria-label":R,tabIndex:-1,"data-state":`open`,...Z,className:t.cn(i.avatarGroupClassNames.surface,`min-w-32 max-w-xs rounded-md border border-border bg-background p-2 text-body-1 text-foreground shadow-16`,`focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring`),onKeyDown:re,children:(0,v.jsx)(x.Provider,{value:ie,children:(0,v.jsx)(`ul`,{className:t.cn(i.avatarGroupClassNames.list,`m-0 flex list-none flex-col gap-1 p-0`),children:F.map(({key:e,node:r})=>(0,v.jsx)(`li`,{className:t.cn(i.avatarGroupClassNames.listItem,`px-1 py-0.5`),children:n.isElementOfType(r,C)?r:w(r)??(T(r)?O:r)},e))})})})})]})};O.displayName=`AvatarGroupPopover`;var k=O;exports.AvatarGroupItem=C,exports.AvatarGroupPopover=k,exports.DEFAULT_UNNAMED_MEMBER_LABEL=y,exports.getMemberName=w,exports.rendersUnnamed=T;
|
|
2
|
+
//# sourceMappingURL=AvatarGroup.parts.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AvatarGroup.parts.cjs","names":["React","Avatar","cn","avatarGroupItemClassNames","isElementOfType","AvatarGroupContext","flattenChildren","useId","useControllable","DismissLayerContext","usePopupPosition","useDismiss","useMergedRefs","getTabbableElements","isInsideLayerTree","_Fragment","avatarGroupClassNames","avatarGroupPopoverClassNames","focusRing","avatarSizeClassesFor","Portal","require_runtime","require_cn","require_children","require_dev","require_classNames","require_useControllable","require_useId","require_focus","require_useMergedRefs","require_usePopupPosition","require_layers","require_useDismiss","require_useRestoreFocus","require_styles","require_Portal","require_AvatarGroup_context","require_Avatar_sizes","require_Avatar","react","react_jsx_runtime","DEFAULT_UNNAMED_MEMBER_LABEL","nonBlank","value","OverflowContext","AvatarGroupItemImpl","props","overflow","className","ariaLabel","avatarProps","text","name","size","AvatarGroupItem","getMemberName","member","label","labelledBy","decorative","rendersUnnamed","imageProps","image","countText","count","MoreIcon","AvatarGroupPopoverImpl","labelProp","openProp","group","items","children","popupId","openState","setOpenState","defaultOpen","isOpen","requestOpen","next","onOpenChange","parentLayerId","triggerRef","surfaceRef","surface","setSurface","reason","event","triggerRefs","setReference","ref","surfaceRefs","setFloating","handleSurfaceKeyDown","trigger","popup","origin","inner","el","target","order","index","hasUnnamedMember","node","unnamedMemberLabel","overflowContext","indicator","floatingProps","key","AvatarGroupPopover"],"sources":["../../../src/components/data-display/AvatarGroup.parts.tsx"],"sourcesContent":["import * as React from 'react';\nimport { flattenChildren, isElementOfType } from '../../lib/children';\nimport { cn } from '../../lib/cn';\nimport {\n avatarGroupClassNames,\n avatarGroupItemClassNames,\n avatarGroupPopoverClassNames,\n} from '../../lib/classNames';\nimport { warnOnce } from '../../lib/dev';\nimport { getTabbableElements } from '../../lib/focus';\nimport { isInsideLayerTree } from '../../lib/layers';\nimport { focusRing } from '../../lib/styles';\nimport type { OpenChangeDetails } from '../../lib/types';\nimport { useControllable } from '../../hooks/useControllable';\nimport { DismissLayerContext, useDismiss, type DismissReason } from '../../hooks/useDismiss';\nimport { useId } from '../../hooks/useId';\nimport { useMergedRefs } from '../../hooks/useMergedRefs';\nimport { usePopupPosition } from '../../hooks/usePopupPosition';\nimport { useRestoreFocus } from '../../hooks/useRestoreFocus';\nimport { Portal } from '../portal/Portal';\nimport { Avatar } from './Avatar';\nimport type { AvatarProps } from './Avatar';\nimport { avatarSizeClassesFor, type AvatarSize } from './Avatar.sizes';\nimport { AvatarGroupContext } from './AvatarGroup.context';\n\n/** Why the popup of an AvatarGroup opens or closes: its trigger, or a dismissal. */\nexport type AvatarGroupOpenChangeDetails = OpenChangeDetails<DismissReason | 'trigger'>;\n\n/** Default text listed for a hidden member that would render with no accessible name. */\nexport const DEFAULT_UNNAMED_MEMBER_LABEL = 'Unnamed member';\n\nconst nonBlank = (value: unknown): value is string =>\n typeof value === 'string' && value.trim() !== '';\n\n/** Set by the popup around its list: an Item renders as a row (avatar and name) there. */\nconst OverflowContext = React.createContext<{ unnamedMemberLabel: string } | null>(null);\nOverflowContext.displayName = 'AvatarGroupOverflowContext';\n\n/** Properties for the AvatarGroup.Item sub-component: the props of an Avatar. */\nexport type AvatarGroupItemProps = AvatarProps;\n\nconst AvatarGroupItemImpl = ({ className, ...props }: AvatarGroupItemProps) => {\n const overflow = React.useContext(OverflowContext);\n if (!overflow) {\n return <Avatar {...props} className={cn(avatarGroupItemClassNames.root, className)} />;\n }\n const { name, 'aria-label': ariaLabel, size, ref, ...avatarProps } = props;\n const text = [ariaLabel, name].find(nonBlank)?.trim() ?? overflow.unnamedMemberLabel;\n return (\n <span\n className={cn(avatarGroupItemClassNames.root, 'inline-flex items-center gap-2', className)}\n >\n <Avatar {...avatarProps} name={name} size={size ?? 'small'} decorative />\n <span className={avatarGroupItemClassNames.name}>{text}</span>\n </span>\n );\n};\nAvatarGroupItemImpl.displayName = 'AvatarGroupItem';\n/**\n * A member of an AvatarGroup. In the group it renders as an `Avatar`; in the overflow list of the\n * popup it renders as a row with the avatar and the name. It takes the props of an `Avatar`.\n */\nexport const AvatarGroupItem = AvatarGroupItemImpl;\n\n/**\n * The text name of a member for the overflow list: the text of a text member, or the accessible\n * name of an `Avatar` or Item given by its props (`aria-label` before `name`). An `Avatar`\n * labelled by `aria-labelledby` (unless `decorative`) and every other element have none: they are\n * rendered as they are, so a link keeps working and keeps its own label.\n */\nexport function getMemberName(member: React.ReactNode): string | undefined {\n if (typeof member === 'string' || typeof member === 'number') {\n return String(member).trim() || undefined;\n }\n if (\n !isElementOfType<AvatarProps>(member, Avatar) &&\n !isElementOfType<AvatarProps>(member, AvatarGroupItem)\n ) {\n return undefined;\n }\n const { decorative, name, 'aria-label': label, 'aria-labelledby': labelledBy } = member.props;\n if (!decorative && nonBlank(labelledBy)) return undefined;\n for (const value of [label, name]) {\n if (nonBlank(value)) return value.trim();\n }\n return undefined;\n}\n\n/**\n * Whether a member without a {@link getMemberName} name would render with no accessible name,\n * so its list item would be empty for assistive technology: blank text, or an `Avatar` that is\n * neither labelled by `aria-labelledby` nor named by its image's `alt` (or is `decorative`).\n * Other elements are rendered as they are, since their content is unknown here.\n */\nexport function rendersUnnamed(member: React.ReactNode): boolean {\n if (typeof member === 'string' || typeof member === 'number') return true;\n if (!isElementOfType<AvatarProps>(member, Avatar)) return false;\n const { decorative, image, 'aria-labelledby': labelledBy } = member.props;\n if (decorative) return true;\n if (nonBlank(labelledBy)) return false;\n const imageProps: unknown = React.isValidElement(image) ? image.props : image;\n const alt =\n typeof imageProps === 'object' && imageProps !== null && 'alt' in imageProps\n ? imageProps.alt\n : undefined;\n return !nonBlank(alt);\n}\n\n/** The shown count of the overflow button: capped at `99+`. */\nfunction countText(count: number): string {\n return count > 99 ? '99+' : `+${count}`;\n}\n\n/** Properties for the AvatarGroup.Popover sub-component. */\nexport interface AvatarGroupPopoverProps {\n /** The overflow members: `AvatarGroup.Item`s, `Avatar`s, text or other elements. */\n children?: React.ReactNode;\n /**\n * What the overflow button shows: the number of hidden members (`+3`, capped at `99+`), or an\n * icon.\n * @default 'count'\n */\n indicator?: 'count' | 'icon';\n /** The number of hidden members. @default the number of `children` */\n count?: number;\n /**\n * Accessible name of the overflow button and its popup. A function receives the number of hidden\n * members.\n * @default (count) => `${count} more`\n */\n label?: string | ((count: number) => string);\n /**\n * Text listed in the overflow popup for a hidden member that has no accessible name.\n * @default 'Unnamed member'\n */\n unnamedMemberLabel?: string;\n /** Whether the popup is open (controlled). */\n open?: boolean;\n /**\n * Whether the popup starts open (uncontrolled).\n * @default false\n */\n defaultOpen?: boolean;\n /** Called when the popup asks to open or close, with the reason (`trigger`, `escape`, …). */\n onOpenChange?: (open: boolean, details?: AvatarGroupOpenChangeDetails) => void;\n /** Extra classes for the overflow button. */\n className?: string;\n /** Ref to the overflow `<button>`. */\n ref?: React.Ref<HTMLButtonElement>;\n}\n\nconst MoreIcon = () => (\n <svg\n viewBox=\"0 0 16 16\"\n width=\"16\"\n height=\"16\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n focusable=\"false\"\n >\n <circle cx=\"3\" cy=\"8\" r=\"1.5\" />\n <circle cx=\"8\" cy=\"8\" r=\"1.5\" />\n <circle cx=\"13\" cy=\"8\" r=\"1.5\" />\n </svg>\n);\n\nconst AvatarGroupPopoverImpl = ({\n children,\n indicator = 'count',\n count,\n label: labelProp,\n unnamedMemberLabel = DEFAULT_UNNAMED_MEMBER_LABEL,\n open: openProp,\n defaultOpen = false,\n onOpenChange,\n className,\n ref,\n}: AvatarGroupPopoverProps) => {\n const group = React.useContext(AvatarGroupContext);\n const items = flattenChildren(children);\n const overflow = count ?? items.length;\n const size: AvatarSize = group?.size ?? 'medium';\n const label =\n typeof labelProp === 'function' ? labelProp(overflow) : (labelProp ?? `${overflow} more`);\n\n const popupId = useId('avatar-group-overflow');\n const [openState, setOpenState] = useControllable(openProp, defaultOpen);\n // Nothing is hidden any more: the popup is closed, and does not reappear when members overflow\n // again unless asked.\n const isOpen = openState && overflow > 0;\n const requestOpen = (\n next: boolean,\n reason: AvatarGroupOpenChangeDetails['reason'],\n event: Event,\n ) => {\n if (next === isOpen) return;\n setOpenState(next);\n onOpenChange?.(next, { reason, event });\n };\n\n // The layer the group is rendered in (a dialog or popover), if any: the popup's parent layer.\n const parentLayerId = React.useContext(DismissLayerContext);\n const triggerRef = React.useRef<HTMLButtonElement | null>(null);\n const surfaceRef = React.useRef<HTMLDivElement | null>(null);\n const [surface, setSurface] = React.useState<HTMLDivElement | null>(null);\n\n const { setReference, setFloating, floatingProps } = usePopupPosition({\n open: isOpen,\n side: 'bottom',\n align: 'start',\n });\n const { layerId } = useDismiss({\n open: isOpen,\n onDismiss: (reason, event) => requestOpen(false, reason, event),\n refs: [triggerRef, surfaceRef],\n anchorRef: triggerRef,\n focusOutside: true,\n });\n useRestoreFocus({ enabled: isOpen, container: surface, triggerRef, onlyIfFocusInside: true });\n const triggerRefs = useMergedRefs<HTMLButtonElement>(triggerRef, setReference, ref);\n const surfaceRefs = useMergedRefs<HTMLDivElement>(surfaceRef, setFloating, setSurface);\n\n // Move focus into the popup once it is mounted, so its list is read next.\n React.useEffect(() => {\n if (isOpen && surface) surface.focus({ preventScroll: true });\n }, [isOpen, surface]);\n\n // The popup is portaled, so native tabbing would leave it for the end of the page. Tab moves\n // through the popup's own tab stops (a rendered member can be a link); leaving the last one\n // closes the popup and moves on from the button instead (Shift+Tab before the first one: back to\n // the button), like a focus trap does for a popup anchored in a dialog. When nothing tabbable\n // follows the button, Tab is not prevented: focus returns to the button as the popup closes and\n // the browser (leaving the page) or an enclosing focus trap (wrapping) takes it from there.\n // Inside a parent layer (a dialog, a popover), a tab stop after the button that lies outside that\n // layer is treated the same way: a focus trap that leaves the page outside it interactive would\n // pull focus back from there, while its descendant-layer rule (§2.4) wraps from the button.\n // Tab stops are computed at keydown time, never cached (§2.2 focus utilities). A key that\n // bubbles through React from a portal a member opened is not the popup's: it is ignored.\n const handleSurfaceKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n const trigger = triggerRef.current;\n if (event.key !== 'Tab' || event.defaultPrevented || !trigger) return;\n const popup = event.currentTarget;\n // Duck typed (`nodeType`), not `instanceof Node`: a popup portaled into another document (an\n // iframe, a popout window) holds nodes of that realm.\n const origin = event.target as Partial<Node> | null;\n if (!origin || typeof origin.nodeType !== 'number' || !popup.contains(origin as Node)) return;\n const inner = getTabbableElements(popup);\n // -1 while focus is on the popup itself (or on something that is not a tab stop).\n const current = inner.findIndex((el) => el.contains(origin as Node));\n const next = current + (event.shiftKey ? -1 : 1);\n if (next >= 0 && next < inner.length) {\n event.preventDefault();\n inner[next]!.focus();\n return;\n }\n let target: HTMLElement | undefined = trigger;\n if (!event.shiftKey) {\n const order = getTabbableElements(trigger.ownerDocument.body).filter(\n (el) => !popup.contains(el),\n );\n const index = order.indexOf(trigger);\n target = index === -1 ? trigger : order[index + 1];\n if (target && parentLayerId !== null && !isInsideLayerTree(parentLayerId, target)) {\n target = undefined;\n }\n }\n requestOpen(false, 'escape', event.nativeEvent);\n if (!target) return;\n event.preventDefault();\n target.focus();\n };\n\n const hasUnnamedMember =\n isOpen &&\n items.some(({ node }) =>\n isElementOfType<AvatarProps>(node, AvatarGroupItem)\n ? getMemberName(node) === undefined\n : getMemberName(node) === undefined && rendersUnnamed(node),\n );\n React.useEffect(() => {\n if (hasUnnamedMember) {\n warnOnce(\n 'AvatarGroup:unnamed-member',\n `AvatarGroup: a hidden member has no accessible name and is listed as \"${unnamedMemberLabel}\". Give each member a \\`name\\` or \\`aria-label\\`.`,\n );\n }\n }, [hasUnnamedMember, unnamedMemberLabel]);\n\n const overflowContext = React.useMemo(() => ({ unnamedMemberLabel }), [unnamedMemberLabel]);\n\n return (\n <>\n <button\n ref={triggerRefs}\n type=\"button\"\n aria-label={label}\n aria-haspopup=\"dialog\"\n aria-expanded={isOpen}\n aria-controls={isOpen ? popupId : undefined}\n className={cn(\n avatarGroupClassNames.overflowButton,\n avatarGroupPopoverClassNames.root,\n 'inline-flex shrink-0 cursor-pointer items-center justify-center rounded-full border-0 p-0 font-semibold',\n 'bg-muted text-muted-foreground ring-2 ring-background',\n 'not-disabled:not-aria-disabled:hover:bg-subtle-pressed',\n focusRing,\n group?.layout === 'stack' && group.hasInlineItems && '-ms-2',\n avatarSizeClassesFor(size),\n className,\n )}\n onClick={(event) => requestOpen(!isOpen, 'trigger', event.nativeEvent)}\n >\n {indicator === 'icon' ? <MoreIcon /> : countText(overflow)}\n </button>\n {isOpen && (\n <Portal layerId={layerId}>\n <div\n ref={surfaceRefs}\n id={popupId}\n role=\"dialog\"\n aria-label={label}\n tabIndex={-1}\n data-state=\"open\"\n {...floatingProps}\n className={cn(\n avatarGroupClassNames.surface,\n 'min-w-32 max-w-xs rounded-md border border-border bg-background p-2 text-body-1 text-foreground shadow-16',\n focusRing,\n )}\n onKeyDown={handleSurfaceKeyDown}\n >\n <OverflowContext.Provider value={overflowContext}>\n <ul\n className={cn(avatarGroupClassNames.list, 'm-0 flex list-none flex-col gap-1 p-0')}\n >\n {items.map(({ key, node }) => (\n <li key={key} className={cn(avatarGroupClassNames.listItem, 'px-1 py-0.5')}>\n {isElementOfType<AvatarProps>(node, AvatarGroupItem)\n ? node\n : (getMemberName(node) ?? (rendersUnnamed(node) ? unnamedMemberLabel : node))}\n </li>\n ))}\n </ul>\n </OverflowContext.Provider>\n </div>\n </Portal>\n )}\n </>\n );\n};\nAvatarGroupPopoverImpl.displayName = 'AvatarGroupPopover';\n/**\n * The overflow of an AvatarGroup, written by hand: a \"+N\" button that opens a popup listing its\n * children (`AvatarGroup.Item`s, `Avatar`s, text or other elements). Write it as a child of the\n * group to replace the overflow that `max` makes; its `count`, `indicator`, `label`, `open` and\n * `onOpenChange` take over what `max` would have derived. Tab leaves the popup from the button.\n */\nexport const AvatarGroupPopover = AvatarGroupPopoverImpl;\n"],"mappings":"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"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn as e } from "../../lib/cn.mjs";
|
|
3
|
+
import { flattenChildren as t, isElementOfType as n } from "../../lib/children.mjs";
|
|
4
|
+
import { warnOnce as r } from "../../lib/dev.mjs";
|
|
5
|
+
import { avatarGroupClassNames as i, avatarGroupItemClassNames as a, avatarGroupPopoverClassNames as o } from "../../lib/classNames.mjs";
|
|
6
|
+
import { useControllable as s } from "../../hooks/useControllable.mjs";
|
|
7
|
+
import { useId as c } from "../../hooks/useId.mjs";
|
|
8
|
+
import { getTabbableElements as l } from "../../lib/focus.mjs";
|
|
9
|
+
import { useMergedRefs as u } from "../../hooks/useMergedRefs.mjs";
|
|
10
|
+
import { usePopupPosition as ee } from "../../hooks/usePopupPosition.mjs";
|
|
11
|
+
import { isInsideLayerTree as d } from "../../lib/layers.mjs";
|
|
12
|
+
import { DismissLayerContext as f, useDismiss as p } from "../../hooks/useDismiss.mjs";
|
|
13
|
+
import { useRestoreFocus as m } from "../../hooks/useRestoreFocus.mjs";
|
|
14
|
+
import { focusRing as h } from "../../lib/styles.mjs";
|
|
15
|
+
import { Portal as g } from "../portal/Portal.mjs";
|
|
16
|
+
import { AvatarGroupContext as _ } from "./AvatarGroup.context.mjs";
|
|
17
|
+
import { avatarSizeClassesFor as v } from "./Avatar.sizes.mjs";
|
|
18
|
+
import { Avatar as y } from "./Avatar.mjs";
|
|
19
|
+
import * as b from "react";
|
|
20
|
+
import { Fragment as te, jsx as x, jsxs as S } from "react/jsx-runtime";
|
|
21
|
+
//#region src/components/data-display/AvatarGroup.parts.tsx
|
|
22
|
+
var C = "Unnamed member", w = (e) => typeof e == "string" && e.trim() !== "", T = b.createContext(null);
|
|
23
|
+
T.displayName = "AvatarGroupOverflowContext";
|
|
24
|
+
var E = ({ className: t, ...n }) => {
|
|
25
|
+
let r = b.useContext(T);
|
|
26
|
+
if (!r) return /* @__PURE__ */ x(y, {
|
|
27
|
+
...n,
|
|
28
|
+
className: e(a.root, t)
|
|
29
|
+
});
|
|
30
|
+
let { name: i, "aria-label": o, size: s, ref: c, ...l } = n, u = [o, i].find(w)?.trim() ?? r.unnamedMemberLabel;
|
|
31
|
+
return /* @__PURE__ */ S("span", {
|
|
32
|
+
className: e(a.root, "inline-flex items-center gap-2", t),
|
|
33
|
+
children: [/* @__PURE__ */ x(y, {
|
|
34
|
+
...l,
|
|
35
|
+
name: i,
|
|
36
|
+
size: s ?? "small",
|
|
37
|
+
decorative: !0
|
|
38
|
+
}), /* @__PURE__ */ x("span", {
|
|
39
|
+
className: a.name,
|
|
40
|
+
children: u
|
|
41
|
+
})]
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
E.displayName = "AvatarGroupItem";
|
|
45
|
+
var D = E;
|
|
46
|
+
function O(e) {
|
|
47
|
+
if (typeof e == "string" || typeof e == "number") return String(e).trim() || void 0;
|
|
48
|
+
if (!n(e, y) && !n(e, D)) return;
|
|
49
|
+
let { decorative: t, name: r, "aria-label": i, "aria-labelledby": a } = e.props;
|
|
50
|
+
if (t || !w(a)) {
|
|
51
|
+
for (let e of [i, r]) if (w(e)) return e.trim();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function k(e) {
|
|
55
|
+
if (typeof e == "string" || typeof e == "number") return !0;
|
|
56
|
+
if (!n(e, y)) return !1;
|
|
57
|
+
let { decorative: t, image: r, "aria-labelledby": i } = e.props;
|
|
58
|
+
if (t) return !0;
|
|
59
|
+
if (w(i)) return !1;
|
|
60
|
+
let a = b.isValidElement(r) ? r.props : r;
|
|
61
|
+
return !w(typeof a == "object" && a && "alt" in a ? a.alt : void 0);
|
|
62
|
+
}
|
|
63
|
+
function A(e) {
|
|
64
|
+
return e > 99 ? "99+" : `+${e}`;
|
|
65
|
+
}
|
|
66
|
+
var j = () => /* @__PURE__ */ S("svg", {
|
|
67
|
+
viewBox: "0 0 16 16",
|
|
68
|
+
width: "16",
|
|
69
|
+
height: "16",
|
|
70
|
+
fill: "currentColor",
|
|
71
|
+
"aria-hidden": "true",
|
|
72
|
+
focusable: "false",
|
|
73
|
+
children: [
|
|
74
|
+
/* @__PURE__ */ x("circle", {
|
|
75
|
+
cx: "3",
|
|
76
|
+
cy: "8",
|
|
77
|
+
r: "1.5"
|
|
78
|
+
}),
|
|
79
|
+
/* @__PURE__ */ x("circle", {
|
|
80
|
+
cx: "8",
|
|
81
|
+
cy: "8",
|
|
82
|
+
r: "1.5"
|
|
83
|
+
}),
|
|
84
|
+
/* @__PURE__ */ x("circle", {
|
|
85
|
+
cx: "13",
|
|
86
|
+
cy: "8",
|
|
87
|
+
r: "1.5"
|
|
88
|
+
})
|
|
89
|
+
]
|
|
90
|
+
}), M = ({ children: a, indicator: y = "count", count: w, label: E, unnamedMemberLabel: M = C, open: N, defaultOpen: P = !1, onOpenChange: F, className: I, ref: L }) => {
|
|
91
|
+
let R = b.useContext(_), z = t(a), B = w ?? z.length, V = R?.size ?? "medium", H = typeof E == "function" ? E(B) : E ?? `${B} more`, U = c("avatar-group-overflow"), [W, G] = s(N, P), K = W && B > 0, q = (e, t, n) => {
|
|
92
|
+
e !== K && (G(e), F?.(e, {
|
|
93
|
+
reason: t,
|
|
94
|
+
event: n
|
|
95
|
+
}));
|
|
96
|
+
}, J = b.useContext(f), Y = b.useRef(null), X = b.useRef(null), [Z, ne] = b.useState(null), { setReference: re, setFloating: ie, floatingProps: ae } = ee({
|
|
97
|
+
open: K,
|
|
98
|
+
side: "bottom",
|
|
99
|
+
align: "start"
|
|
100
|
+
}), { layerId: oe } = p({
|
|
101
|
+
open: K,
|
|
102
|
+
onDismiss: (e, t) => q(!1, e, t),
|
|
103
|
+
refs: [Y, X],
|
|
104
|
+
anchorRef: Y,
|
|
105
|
+
focusOutside: !0
|
|
106
|
+
});
|
|
107
|
+
m({
|
|
108
|
+
enabled: K,
|
|
109
|
+
container: Z,
|
|
110
|
+
triggerRef: Y,
|
|
111
|
+
onlyIfFocusInside: !0
|
|
112
|
+
});
|
|
113
|
+
let se = u(Y, re, L), Q = u(X, ie, ne);
|
|
114
|
+
b.useEffect(() => {
|
|
115
|
+
K && Z && Z.focus({ preventScroll: !0 });
|
|
116
|
+
}, [K, Z]);
|
|
117
|
+
let ce = (e) => {
|
|
118
|
+
let t = Y.current;
|
|
119
|
+
if (e.key !== "Tab" || e.defaultPrevented || !t) return;
|
|
120
|
+
let n = e.currentTarget, r = e.target;
|
|
121
|
+
if (!r || typeof r.nodeType != "number" || !n.contains(r)) return;
|
|
122
|
+
let i = l(n), a = i.findIndex((e) => e.contains(r)) + (e.shiftKey ? -1 : 1);
|
|
123
|
+
if (a >= 0 && a < i.length) {
|
|
124
|
+
e.preventDefault(), i[a].focus();
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
let o = t;
|
|
128
|
+
if (!e.shiftKey) {
|
|
129
|
+
let e = l(t.ownerDocument.body).filter((e) => !n.contains(e)), r = e.indexOf(t);
|
|
130
|
+
o = r === -1 ? t : e[r + 1], o && J !== null && !d(J, o) && (o = void 0);
|
|
131
|
+
}
|
|
132
|
+
q(!1, "escape", e.nativeEvent), o && (e.preventDefault(), o.focus());
|
|
133
|
+
}, $ = K && z.some(({ node: e }) => n(e, D) ? O(e) === void 0 : O(e) === void 0 && k(e));
|
|
134
|
+
b.useEffect(() => {
|
|
135
|
+
$ && r("AvatarGroup:unnamed-member", `AvatarGroup: a hidden member has no accessible name and is listed as "${M}". Give each member a \`name\` or \`aria-label\`.`);
|
|
136
|
+
}, [$, M]);
|
|
137
|
+
let le = b.useMemo(() => ({ unnamedMemberLabel: M }), [M]);
|
|
138
|
+
return /* @__PURE__ */ S(te, { children: [/* @__PURE__ */ x("button", {
|
|
139
|
+
ref: se,
|
|
140
|
+
type: "button",
|
|
141
|
+
"aria-label": H,
|
|
142
|
+
"aria-haspopup": "dialog",
|
|
143
|
+
"aria-expanded": K,
|
|
144
|
+
"aria-controls": K ? U : void 0,
|
|
145
|
+
className: e(i.overflowButton, o.root, "inline-flex shrink-0 cursor-pointer items-center justify-center rounded-full border-0 p-0 font-semibold", "bg-muted text-muted-foreground ring-2 ring-background", "not-disabled:not-aria-disabled:hover:bg-subtle-pressed", h, R?.layout === "stack" && R.hasInlineItems && "-ms-2", v(V), I),
|
|
146
|
+
onClick: (e) => q(!K, "trigger", e.nativeEvent),
|
|
147
|
+
children: y === "icon" ? /* @__PURE__ */ x(j, {}) : A(B)
|
|
148
|
+
}), K && /* @__PURE__ */ x(g, {
|
|
149
|
+
layerId: oe,
|
|
150
|
+
children: /* @__PURE__ */ x("div", {
|
|
151
|
+
ref: Q,
|
|
152
|
+
id: U,
|
|
153
|
+
role: "dialog",
|
|
154
|
+
"aria-label": H,
|
|
155
|
+
tabIndex: -1,
|
|
156
|
+
"data-state": "open",
|
|
157
|
+
...ae,
|
|
158
|
+
className: e(i.surface, "min-w-32 max-w-xs rounded-md border border-border bg-background p-2 text-body-1 text-foreground shadow-16", "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring"),
|
|
159
|
+
onKeyDown: ce,
|
|
160
|
+
children: /* @__PURE__ */ x(T.Provider, {
|
|
161
|
+
value: le,
|
|
162
|
+
children: /* @__PURE__ */ x("ul", {
|
|
163
|
+
className: e(i.list, "m-0 flex list-none flex-col gap-1 p-0"),
|
|
164
|
+
children: z.map(({ key: t, node: r }) => /* @__PURE__ */ x("li", {
|
|
165
|
+
className: e(i.listItem, "px-1 py-0.5"),
|
|
166
|
+
children: n(r, D) ? r : O(r) ?? (k(r) ? M : r)
|
|
167
|
+
}, t))
|
|
168
|
+
})
|
|
169
|
+
})
|
|
170
|
+
})
|
|
171
|
+
})] });
|
|
172
|
+
};
|
|
173
|
+
M.displayName = "AvatarGroupPopover";
|
|
174
|
+
var N = M;
|
|
175
|
+
//#endregion
|
|
176
|
+
export { D as AvatarGroupItem, N as AvatarGroupPopover, C as DEFAULT_UNNAMED_MEMBER_LABEL, O as getMemberName, k as rendersUnnamed };
|
|
177
|
+
|
|
178
|
+
//# sourceMappingURL=AvatarGroup.parts.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AvatarGroup.parts.mjs","names":["useId","_Fragment","useId$1","React","DEFAULT_UNNAMED_MEMBER_LABEL","nonBlank","value","OverflowContext","AvatarGroupItemImpl","props","overflow","Avatar","cn","avatarGroupItemClassNames","className","ariaLabel","avatarProps","text","name","size","AvatarGroupItem","getMemberName","member","isElementOfType","label","labelledBy","decorative","rendersUnnamed","imageProps","image","countText","count","MoreIcon","AvatarGroupPopoverImpl","labelProp","openProp","group","AvatarGroupContext","items","flattenChildren","children","popupId","openState","setOpenState","useControllable","defaultOpen","isOpen","requestOpen","next","onOpenChange","parentLayerId","DismissLayerContext","triggerRef","surfaceRef","surface","setSurface","usePopupPosition","useDismiss","reason","event","triggerRefs","useMergedRefs","setReference","ref","surfaceRefs","setFloating","handleSurfaceKeyDown","trigger","popup","origin","inner","getTabbableElements","el","target","order","index","isInsideLayerTree","hasUnnamedMember","node","warnOnce","unnamedMemberLabel","overflowContext","Fragment","avatarGroupClassNames","avatarGroupPopoverClassNames","focusRing","avatarSizeClassesFor","indicator","Portal","floatingProps","key","AvatarGroupPopover"],"sources":["../../../src/components/data-display/AvatarGroup.parts.tsx"],"sourcesContent":["import * as React from 'react';\nimport { flattenChildren, isElementOfType } from '../../lib/children';\nimport { cn } from '../../lib/cn';\nimport {\n avatarGroupClassNames,\n avatarGroupItemClassNames,\n avatarGroupPopoverClassNames,\n} from '../../lib/classNames';\nimport { warnOnce } from '../../lib/dev';\nimport { getTabbableElements } from '../../lib/focus';\nimport { isInsideLayerTree } from '../../lib/layers';\nimport { focusRing } from '../../lib/styles';\nimport type { OpenChangeDetails } from '../../lib/types';\nimport { useControllable } from '../../hooks/useControllable';\nimport { DismissLayerContext, useDismiss, type DismissReason } from '../../hooks/useDismiss';\nimport { useId } from '../../hooks/useId';\nimport { useMergedRefs } from '../../hooks/useMergedRefs';\nimport { usePopupPosition } from '../../hooks/usePopupPosition';\nimport { useRestoreFocus } from '../../hooks/useRestoreFocus';\nimport { Portal } from '../portal/Portal';\nimport { Avatar } from './Avatar';\nimport type { AvatarProps } from './Avatar';\nimport { avatarSizeClassesFor, type AvatarSize } from './Avatar.sizes';\nimport { AvatarGroupContext } from './AvatarGroup.context';\n\n/** Why the popup of an AvatarGroup opens or closes: its trigger, or a dismissal. */\nexport type AvatarGroupOpenChangeDetails = OpenChangeDetails<DismissReason | 'trigger'>;\n\n/** Default text listed for a hidden member that would render with no accessible name. */\nexport const DEFAULT_UNNAMED_MEMBER_LABEL = 'Unnamed member';\n\nconst nonBlank = (value: unknown): value is string =>\n typeof value === 'string' && value.trim() !== '';\n\n/** Set by the popup around its list: an Item renders as a row (avatar and name) there. */\nconst OverflowContext = React.createContext<{ unnamedMemberLabel: string } | null>(null);\nOverflowContext.displayName = 'AvatarGroupOverflowContext';\n\n/** Properties for the AvatarGroup.Item sub-component: the props of an Avatar. */\nexport type AvatarGroupItemProps = AvatarProps;\n\nconst AvatarGroupItemImpl = ({ className, ...props }: AvatarGroupItemProps) => {\n const overflow = React.useContext(OverflowContext);\n if (!overflow) {\n return <Avatar {...props} className={cn(avatarGroupItemClassNames.root, className)} />;\n }\n const { name, 'aria-label': ariaLabel, size, ref, ...avatarProps } = props;\n const text = [ariaLabel, name].find(nonBlank)?.trim() ?? overflow.unnamedMemberLabel;\n return (\n <span\n className={cn(avatarGroupItemClassNames.root, 'inline-flex items-center gap-2', className)}\n >\n <Avatar {...avatarProps} name={name} size={size ?? 'small'} decorative />\n <span className={avatarGroupItemClassNames.name}>{text}</span>\n </span>\n );\n};\nAvatarGroupItemImpl.displayName = 'AvatarGroupItem';\n/**\n * A member of an AvatarGroup. In the group it renders as an `Avatar`; in the overflow list of the\n * popup it renders as a row with the avatar and the name. It takes the props of an `Avatar`.\n */\nexport const AvatarGroupItem = AvatarGroupItemImpl;\n\n/**\n * The text name of a member for the overflow list: the text of a text member, or the accessible\n * name of an `Avatar` or Item given by its props (`aria-label` before `name`). An `Avatar`\n * labelled by `aria-labelledby` (unless `decorative`) and every other element have none: they are\n * rendered as they are, so a link keeps working and keeps its own label.\n */\nexport function getMemberName(member: React.ReactNode): string | undefined {\n if (typeof member === 'string' || typeof member === 'number') {\n return String(member).trim() || undefined;\n }\n if (\n !isElementOfType<AvatarProps>(member, Avatar) &&\n !isElementOfType<AvatarProps>(member, AvatarGroupItem)\n ) {\n return undefined;\n }\n const { decorative, name, 'aria-label': label, 'aria-labelledby': labelledBy } = member.props;\n if (!decorative && nonBlank(labelledBy)) return undefined;\n for (const value of [label, name]) {\n if (nonBlank(value)) return value.trim();\n }\n return undefined;\n}\n\n/**\n * Whether a member without a {@link getMemberName} name would render with no accessible name,\n * so its list item would be empty for assistive technology: blank text, or an `Avatar` that is\n * neither labelled by `aria-labelledby` nor named by its image's `alt` (or is `decorative`).\n * Other elements are rendered as they are, since their content is unknown here.\n */\nexport function rendersUnnamed(member: React.ReactNode): boolean {\n if (typeof member === 'string' || typeof member === 'number') return true;\n if (!isElementOfType<AvatarProps>(member, Avatar)) return false;\n const { decorative, image, 'aria-labelledby': labelledBy } = member.props;\n if (decorative) return true;\n if (nonBlank(labelledBy)) return false;\n const imageProps: unknown = React.isValidElement(image) ? image.props : image;\n const alt =\n typeof imageProps === 'object' && imageProps !== null && 'alt' in imageProps\n ? imageProps.alt\n : undefined;\n return !nonBlank(alt);\n}\n\n/** The shown count of the overflow button: capped at `99+`. */\nfunction countText(count: number): string {\n return count > 99 ? '99+' : `+${count}`;\n}\n\n/** Properties for the AvatarGroup.Popover sub-component. */\nexport interface AvatarGroupPopoverProps {\n /** The overflow members: `AvatarGroup.Item`s, `Avatar`s, text or other elements. */\n children?: React.ReactNode;\n /**\n * What the overflow button shows: the number of hidden members (`+3`, capped at `99+`), or an\n * icon.\n * @default 'count'\n */\n indicator?: 'count' | 'icon';\n /** The number of hidden members. @default the number of `children` */\n count?: number;\n /**\n * Accessible name of the overflow button and its popup. A function receives the number of hidden\n * members.\n * @default (count) => `${count} more`\n */\n label?: string | ((count: number) => string);\n /**\n * Text listed in the overflow popup for a hidden member that has no accessible name.\n * @default 'Unnamed member'\n */\n unnamedMemberLabel?: string;\n /** Whether the popup is open (controlled). */\n open?: boolean;\n /**\n * Whether the popup starts open (uncontrolled).\n * @default false\n */\n defaultOpen?: boolean;\n /** Called when the popup asks to open or close, with the reason (`trigger`, `escape`, …). */\n onOpenChange?: (open: boolean, details?: AvatarGroupOpenChangeDetails) => void;\n /** Extra classes for the overflow button. */\n className?: string;\n /** Ref to the overflow `<button>`. */\n ref?: React.Ref<HTMLButtonElement>;\n}\n\nconst MoreIcon = () => (\n <svg\n viewBox=\"0 0 16 16\"\n width=\"16\"\n height=\"16\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n focusable=\"false\"\n >\n <circle cx=\"3\" cy=\"8\" r=\"1.5\" />\n <circle cx=\"8\" cy=\"8\" r=\"1.5\" />\n <circle cx=\"13\" cy=\"8\" r=\"1.5\" />\n </svg>\n);\n\nconst AvatarGroupPopoverImpl = ({\n children,\n indicator = 'count',\n count,\n label: labelProp,\n unnamedMemberLabel = DEFAULT_UNNAMED_MEMBER_LABEL,\n open: openProp,\n defaultOpen = false,\n onOpenChange,\n className,\n ref,\n}: AvatarGroupPopoverProps) => {\n const group = React.useContext(AvatarGroupContext);\n const items = flattenChildren(children);\n const overflow = count ?? items.length;\n const size: AvatarSize = group?.size ?? 'medium';\n const label =\n typeof labelProp === 'function' ? labelProp(overflow) : (labelProp ?? `${overflow} more`);\n\n const popupId = useId('avatar-group-overflow');\n const [openState, setOpenState] = useControllable(openProp, defaultOpen);\n // Nothing is hidden any more: the popup is closed, and does not reappear when members overflow\n // again unless asked.\n const isOpen = openState && overflow > 0;\n const requestOpen = (\n next: boolean,\n reason: AvatarGroupOpenChangeDetails['reason'],\n event: Event,\n ) => {\n if (next === isOpen) return;\n setOpenState(next);\n onOpenChange?.(next, { reason, event });\n };\n\n // The layer the group is rendered in (a dialog or popover), if any: the popup's parent layer.\n const parentLayerId = React.useContext(DismissLayerContext);\n const triggerRef = React.useRef<HTMLButtonElement | null>(null);\n const surfaceRef = React.useRef<HTMLDivElement | null>(null);\n const [surface, setSurface] = React.useState<HTMLDivElement | null>(null);\n\n const { setReference, setFloating, floatingProps } = usePopupPosition({\n open: isOpen,\n side: 'bottom',\n align: 'start',\n });\n const { layerId } = useDismiss({\n open: isOpen,\n onDismiss: (reason, event) => requestOpen(false, reason, event),\n refs: [triggerRef, surfaceRef],\n anchorRef: triggerRef,\n focusOutside: true,\n });\n useRestoreFocus({ enabled: isOpen, container: surface, triggerRef, onlyIfFocusInside: true });\n const triggerRefs = useMergedRefs<HTMLButtonElement>(triggerRef, setReference, ref);\n const surfaceRefs = useMergedRefs<HTMLDivElement>(surfaceRef, setFloating, setSurface);\n\n // Move focus into the popup once it is mounted, so its list is read next.\n React.useEffect(() => {\n if (isOpen && surface) surface.focus({ preventScroll: true });\n }, [isOpen, surface]);\n\n // The popup is portaled, so native tabbing would leave it for the end of the page. Tab moves\n // through the popup's own tab stops (a rendered member can be a link); leaving the last one\n // closes the popup and moves on from the button instead (Shift+Tab before the first one: back to\n // the button), like a focus trap does for a popup anchored in a dialog. When nothing tabbable\n // follows the button, Tab is not prevented: focus returns to the button as the popup closes and\n // the browser (leaving the page) or an enclosing focus trap (wrapping) takes it from there.\n // Inside a parent layer (a dialog, a popover), a tab stop after the button that lies outside that\n // layer is treated the same way: a focus trap that leaves the page outside it interactive would\n // pull focus back from there, while its descendant-layer rule (§2.4) wraps from the button.\n // Tab stops are computed at keydown time, never cached (§2.2 focus utilities). A key that\n // bubbles through React from a portal a member opened is not the popup's: it is ignored.\n const handleSurfaceKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n const trigger = triggerRef.current;\n if (event.key !== 'Tab' || event.defaultPrevented || !trigger) return;\n const popup = event.currentTarget;\n // Duck typed (`nodeType`), not `instanceof Node`: a popup portaled into another document (an\n // iframe, a popout window) holds nodes of that realm.\n const origin = event.target as Partial<Node> | null;\n if (!origin || typeof origin.nodeType !== 'number' || !popup.contains(origin as Node)) return;\n const inner = getTabbableElements(popup);\n // -1 while focus is on the popup itself (or on something that is not a tab stop).\n const current = inner.findIndex((el) => el.contains(origin as Node));\n const next = current + (event.shiftKey ? -1 : 1);\n if (next >= 0 && next < inner.length) {\n event.preventDefault();\n inner[next]!.focus();\n return;\n }\n let target: HTMLElement | undefined = trigger;\n if (!event.shiftKey) {\n const order = getTabbableElements(trigger.ownerDocument.body).filter(\n (el) => !popup.contains(el),\n );\n const index = order.indexOf(trigger);\n target = index === -1 ? trigger : order[index + 1];\n if (target && parentLayerId !== null && !isInsideLayerTree(parentLayerId, target)) {\n target = undefined;\n }\n }\n requestOpen(false, 'escape', event.nativeEvent);\n if (!target) return;\n event.preventDefault();\n target.focus();\n };\n\n const hasUnnamedMember =\n isOpen &&\n items.some(({ node }) =>\n isElementOfType<AvatarProps>(node, AvatarGroupItem)\n ? getMemberName(node) === undefined\n : getMemberName(node) === undefined && rendersUnnamed(node),\n );\n React.useEffect(() => {\n if (hasUnnamedMember) {\n warnOnce(\n 'AvatarGroup:unnamed-member',\n `AvatarGroup: a hidden member has no accessible name and is listed as \"${unnamedMemberLabel}\". Give each member a \\`name\\` or \\`aria-label\\`.`,\n );\n }\n }, [hasUnnamedMember, unnamedMemberLabel]);\n\n const overflowContext = React.useMemo(() => ({ unnamedMemberLabel }), [unnamedMemberLabel]);\n\n return (\n <>\n <button\n ref={triggerRefs}\n type=\"button\"\n aria-label={label}\n aria-haspopup=\"dialog\"\n aria-expanded={isOpen}\n aria-controls={isOpen ? popupId : undefined}\n className={cn(\n avatarGroupClassNames.overflowButton,\n avatarGroupPopoverClassNames.root,\n 'inline-flex shrink-0 cursor-pointer items-center justify-center rounded-full border-0 p-0 font-semibold',\n 'bg-muted text-muted-foreground ring-2 ring-background',\n 'not-disabled:not-aria-disabled:hover:bg-subtle-pressed',\n focusRing,\n group?.layout === 'stack' && group.hasInlineItems && '-ms-2',\n avatarSizeClassesFor(size),\n className,\n )}\n onClick={(event) => requestOpen(!isOpen, 'trigger', event.nativeEvent)}\n >\n {indicator === 'icon' ? <MoreIcon /> : countText(overflow)}\n </button>\n {isOpen && (\n <Portal layerId={layerId}>\n <div\n ref={surfaceRefs}\n id={popupId}\n role=\"dialog\"\n aria-label={label}\n tabIndex={-1}\n data-state=\"open\"\n {...floatingProps}\n className={cn(\n avatarGroupClassNames.surface,\n 'min-w-32 max-w-xs rounded-md border border-border bg-background p-2 text-body-1 text-foreground shadow-16',\n focusRing,\n )}\n onKeyDown={handleSurfaceKeyDown}\n >\n <OverflowContext.Provider value={overflowContext}>\n <ul\n className={cn(avatarGroupClassNames.list, 'm-0 flex list-none flex-col gap-1 p-0')}\n >\n {items.map(({ key, node }) => (\n <li key={key} className={cn(avatarGroupClassNames.listItem, 'px-1 py-0.5')}>\n {isElementOfType<AvatarProps>(node, AvatarGroupItem)\n ? node\n : (getMemberName(node) ?? (rendersUnnamed(node) ? unnamedMemberLabel : node))}\n </li>\n ))}\n </ul>\n </OverflowContext.Provider>\n </div>\n </Portal>\n )}\n </>\n );\n};\nAvatarGroupPopoverImpl.displayName = 'AvatarGroupPopover';\n/**\n * The overflow of an AvatarGroup, written by hand: a \"+N\" button that opens a popup listing its\n * children (`AvatarGroup.Item`s, `Avatar`s, text or other elements). Write it as a child of the\n * group to replace the overflow that `max` makes; its `count`, `indicator`, `label`, `open` and\n * `onOpenChange` take over what `max` would have derived. Tab leaves the popup from the button.\n */\nexport const AvatarGroupPopover = AvatarGroupPopoverImpl;\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../../lib/cn.cjs"),n=require("../../lib/composeEventHandlers.cjs"),r=require("../../lib/classNames.cjs"),i=require("../../hooks/useEventCallback.cjs"),a=require("../../hooks/useId.cjs"),o=require("../../lib/focus.cjs"),s=require("../../hooks/useMergedRefs.cjs"),ee=require("../../lib/aria.cjs"),te=require("../../hooks/usePresence.cjs"),ne=require("../../hooks/
|
|
1
|
+
"use client";const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../../lib/cn.cjs"),n=require("../../lib/composeEventHandlers.cjs"),r=require("../../lib/classNames.cjs"),i=require("../../hooks/useEventCallback.cjs"),a=require("../../hooks/useId.cjs"),o=require("../../lib/focus.cjs"),s=require("../../hooks/useMergedRefs.cjs"),ee=require("../../lib/aria.cjs"),te=require("../../hooks/usePresence.cjs"),ne=require("../../hooks/usePopupPosition.cjs"),c=require("../../hooks/useDismiss.cjs"),l=require("../../hooks/useRestoreFocus.cjs"),u=require("../../lib/styles.cjs"),d=require("../portal/Portal.cjs"),f=require("../../lib/icons.cjs"),p=require("../../hooks/useHoverIntent.cjs"),m=require("../overlays/Popover.shared.cjs");let h=require("react");h=e.__toESM(h,1);let g=require("react/jsx-runtime");var _={small:{size:12,margin:`-my-1`},medium:{size:16,margin:`-my-0.5`},large:{size:20,margin:`-my-px`}},v=250,y=h.createContext(null);y.displayName=`InfoNoteClassContext`;var b=({info:e,size:b,openOnHover:x=!0,id:re,type:ie=`button`,"aria-label":ae,"aria-labelledby":S,"aria-describedby":C,className:w,disabled:T,onClick:oe,onFocus:se,onBlur:ce,onKeyDown:le,onPointerDown:ue,onMouseDown:de,onPointerEnter:fe,onPointerMove:pe,onPointerLeave:me,ref:he,...E})=>{let D=b===`small`||b===`large`?b:`medium`,O=a.useId(`info-button`),k=re||O,A=h.useContext(y),j=A?.buttonId===k?A.className:void 0,M=ae||`Information`,N=a.useId(`info-note`),P=a.useId(`info`),[F,I]=h.useState(null),L=F!==null,R=h.useRef(null),z=h.useRef(null),B=h.useRef(null),[ge,_e]=h.useState(null),[V,H]=h.useState(null),U=h.useRef(F);h.useLayoutEffect(()=>{U.current=F});let W=h.useRef(!1),G=h.useRef(!1),K=h.useRef(void 0),q=h.useRef(!1),{isMounted:J,ref:ve,presenceProps:ye}=te.usePresence(L),{setReference:be,setFloating:xe,floatingProps:Se,arrowStyles:Ce,side:we}=ne.usePopupPosition({open:L,side:`top`,align:`center`,offset:8,arrowRef:B}),{layerId:Y}=c.useDismiss({open:L,onDismiss:e=>{e!==`focus-outside`&&(W.current=!0),q.current=!1,Q(),I(null)},refs:[R,z],anchorRef:R,focusOutside:!0});l.useRestoreFocus({enabled:L,container:V,triggerRef:R,onlyIfFocusInside:!0,isHoverClose:()=>q.current});let{triggerHandlers:X,surfaceHandlers:Z,cancel:Q}=p.useHoverIntent({enabled:x&&!T,open:L,openDelay:v,closeDelay:v,trigger:ge,surface:V,onOpen:()=>{q.current=!1,I(e=>e??`hover`)},onClose:()=>{q.current=!0,I(e=>e===`hover`?null:e)},canClose:()=>U.current===`hover`&&!m.isFocusInSurfaceTree(V,Y)});h.useEffect(()=>()=>clearTimeout(K.current),[]),h.useEffect(()=>{if(!V)return;let e=e=>{let t=e.relatedTarget;!t||V.contains(t)||R.current?.contains(t)||(W.current=!1)};return V.addEventListener(`focusout`,e),()=>V.removeEventListener(`focusout`,e)},[V]);let Te=i.useEventCallback(()=>{Q(),q.current=!1;let e=F===`click`;e&&(W.current=!0),I(e?null:`click`)}),Ee=i.useEventCallback(()=>{!x||G.current||W.current&&!L||(q.current=!1,Q(),I(e=>e===`click`?e:`focus`))}),De=i.useEventCallback(e=>{let t=e.currentTarget,n=e.relatedTarget;n&&V?.contains(n)||(n||t.ownerDocument.activeElement!==t)&&(W.current=!1)}),$=i.useEventCallback(()=>{G.current=!0,clearTimeout(K.current),K.current=setTimeout(()=>{K.current=void 0,G.current=!1},0)}),Oe=e=>{if(e.key!==`Tab`||e.shiftKey||!L||!V)return;let t=o.getFirstTabbable(V);t&&(e.preventDefault(),t.focus())},ke=m.usePopoverTabOrder({enabled:L,surface:V,anchorRef:R,getPreviousStop:()=>R.current}),Ae=s.useMergedRefs(he,R,_e,be),je=s.useMergedRefs(z,H,xe,ve);return(0,g.jsxs)(g.Fragment,{children:[(0,g.jsx)(`button`,{type:ie,id:k,"data-size":D,...E,ref:Ae,disabled:T,"aria-label":M,"aria-labelledby":S,"aria-describedby":ee.joinIds(C,P),"aria-expanded":L,"aria-controls":L?N:void 0,className:t.cn(r.infoButtonClassNames.root,`inline-flex size-6 shrink-0 items-center justify-center rounded-full border-0 bg-transparent p-0 text-muted-foreground`,_[D].margin,`not-disabled:not-aria-disabled:hover:text-foreground`,u.focusRing,w),onClick:n.composeEventHandlers(oe,Te),onFocus:n.composeEventHandlers(se,Ee),onKeyDown:n.composeEventHandlers(le,Oe),onPointerEnter:n.composeEventHandlers(fe,X.onPointerEnter),onPointerMove:n.composeEventHandlers(pe,X.onPointerMove),onPointerLeave:n.composeEventHandlers(me,X.onPointerLeave),onBlur:n.composeEventHandlers(ce,De,{checkDefaultPrevented:!1}),onPointerDown:n.composeEventHandlers(ue,$,{checkDefaultPrevented:!1}),onMouseDown:n.composeEventHandlers(de,$,{checkDefaultPrevented:!1}),children:(0,g.jsx)(f.InfoIcon,{size:_[D].size})}),(0,g.jsx)(`span`,{id:J?void 0:P,hidden:!0,children:J?null:e}),J&&(0,g.jsx)(d.Portal,{layerId:Y,children:(0,g.jsxs)(`div`,{ref:je,id:N,role:`note`,"aria-labelledby":S||k,"data-wave-infolabel-surface":``,"data-state":L?`open`:`closed`,...ye,...Se,onKeyDown:ke,onPointerEnter:Z.onPointerEnter,onPointerLeave:Z.onPointerLeave,className:t.cn(r.infoButtonClassNames.info,j,`w-max max-w-[min(20rem,calc(100vw-1rem))] rounded-md border border-border bg-background px-3 py-2 text-caption-1 text-foreground shadow-16`),children:[(0,g.jsx)(m.PopoverBeak,{ref:B,side:we,style:Ce,"data-wave-popover-arrow":``,className:r.infoButtonClassNames.beak}),(0,g.jsx)(`div`,{id:P,children:e})]})})]})};b.displayName=`InfoButton`,exports.InfoButton=b,exports.InfoNoteClassContext=y;
|
|
2
2
|
//# sourceMappingURL=InfoButton.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InfoButton.cjs","names":["React","useId","usePresence","usePopupPosition","useDismiss","useHoverIntent","isFocusInSurfaceTree","useEventCallback","getFirstTabbable","usePopoverTabOrder","useMergedRefs","_Fragment","joinIds","cn","infoButtonClassNames","focusRing","composeEventHandlers","InfoIcon","Portal","PopoverBeak","require_runtime","require_cn","require_composeEventHandlers","require_classNames","require_useEventCallback","require_useId","require_focus","require_useMergedRefs","require_aria","require_usePresence","require_useDismiss","require_Portal","require_styles","require_icons","require_usePopupPosition","require_useRestoreFocus","require_useHoverIntent","require_Popover_shared","react","react_jsx_runtime","GLYPH","HOVER_DELAY","InfoNoteClassContext","InfoButton","sizeProp","idProp","ariaLabelProp","ariaLabelledBy","ariaDescribedBy","rest","size","generatedId","id","noteClass","noteClassName","ariaLabel","noteId","infoId","reason","setReason","open","buttonRef","noteRef","arrowRef","buttonElement","setButtonElement","note","setNote","reasonRef","dismissedRef","pointerPressRef","pressTimerRef","hoverCloseRef","presenceRef","dismissReason","cancelHover","hoverTriggerHandlers","hoverSurfaceHandlers","openOnHover","disabled","current","layerId","onFocusOut","next","event","handleClick","closing","handleFocus","handleBlur","button","handlePointerPress","onButtonKeyDown","first","onNoteKeyDown","buttonRefs","ref","setReference","noteRefs","setFloating","className","onClick","onFocus","onKeyDown","onPointerEnter","onPointerMove","onPointerLeave","onBlur","onPointerDown","onMouseDown","isMounted","info","presenceProps","floatingProps","arrowStyles"],"sources":["../../../src/components/data-display/InfoButton.tsx"],"sourcesContent":["import * as React from 'react';\nimport { cn } from '../../lib/cn';\nimport { joinIds } from '../../lib/aria';\nimport { infoButtonClassNames } from '../../lib/classNames';\nimport { composeEventHandlers } from '../../lib/composeEventHandlers';\nimport { getFirstTabbable } from '../../lib/focus';\nimport { InfoIcon } from '../../lib/icons';\nimport { focusRing } from '../../lib/styles';\nimport type { CoreSize } from '../../lib/types';\nimport { useDismiss } from '../../hooks/useDismiss';\nimport { useEventCallback } from '../../hooks/useEventCallback';\nimport { useHoverIntent } from '../../hooks/useHoverIntent';\nimport { useId } from '../../hooks/useId';\nimport { useMergedRefs } from '../../hooks/useMergedRefs';\nimport { usePopupPosition } from '../../hooks/usePopupPosition';\nimport { usePresence } from '../../hooks/usePresence';\nimport { useRestoreFocus } from '../../hooks/useRestoreFocus';\nimport { isFocusInSurfaceTree, PopoverBeak, usePopoverTabOrder } from '../overlays/Popover.shared';\nimport { Portal } from '../portal/Portal';\n\n/** Properties for the InfoButton component. */\nexport interface InfoButtonProps extends Omit<\n React.ButtonHTMLAttributes<HTMLButtonElement>,\n 'children'\n> {\n /**\n * The information the note shows: text, links or other content. It also describes the button\n * (`aria-describedby`), from the server HTML on. It is rendered once: next to the button, not\n * displayed, while the note is closed, and inside the note while it is shown.\n */\n info: React.ReactNode;\n /**\n * Size of the glyph: 12, 16 or 20px. The button box stays 24×24px at every size, with negative\n * margins above and below, so it never makes a row that centres its items (as InfoLabel does)\n * taller.\n * @default 'medium'\n */\n size?: CoreSize;\n /**\n * Open the note when a mouse pointer rests on the button (after 250 ms; it closes 250 ms after\n * the pointer has left the button and the note, touch and pen never hover), and when the button\n * receives keyboard focus. `false` opens it only on a click, and so on Enter and Space (Fluent's\n * InfoButton opens on click only). A click always toggles the note: it pins a note that hover or\n * keyboard focus opened, and closes a pinned one.\n * @default true\n */\n openOnHover?: boolean;\n /**\n * Accessible name of the button, and of the note, which is labelled like the button. Localize it\n * here. With `aria-labelledby` listing the id of your text and the button's own `id`, the name is\n * your text followed by this one (\"Billing Information\").\n * @default 'Information'\n */\n 'aria-label'?: string;\n /** Ref to the button. */\n ref?: React.Ref<HTMLButtonElement>;\n}\n\n/** Why the note is open: a click pins it; keyboard focus owns it; hover opens it transiently. */\ntype OpenReason = 'hover' | 'focus' | 'click';\n\n/**\n * Glyph size and negative margins per size: the 24px box never makes a row that centres its items\n * taller.\n */\nconst GLYPH: Readonly<Record<CoreSize, { size: number; margin: string }>> = {\n small: { size: 12, margin: '-my-1' },\n medium: { size: 16, margin: '-my-0.5' },\n large: { size: 20, margin: '-my-px' },\n};\n\n/**\n * Milliseconds from a mouse pointer resting on the button to the opening, and from the pointer\n * leaving the button and the note to the hover close (the 0.7 InfoLabel delays).\n */\nconst HOVER_DELAY = 250;\n\n/** What InfoLabel tells the InfoButton it renders: see {@link InfoNoteClassContext}. */\ninterface InfoNoteClass {\n /** The `id` of the InfoButton the class is for. */\n buttonId: string;\n /** The class added to that InfoButton's note. */\n className: string;\n}\n\n/**\n * Internal (P3-00, D5): the class InfoLabel adds to the note of the InfoButton it renders, its\n * 0.8 `info` part. Only the InfoButton whose `id` matches takes it, so an InfoButton inside the\n * label's `info` content takes none. Not exported from the barrels.\n */\nexport const InfoNoteClassContext = React.createContext<InfoNoteClass | null>(null);\nInfoNoteClassContext.displayName = 'InfoNoteClassContext';\n\n/**\n * A button with the info glyph that shows `info` in a note next to it: text, links or other rich\n * content, placed above the button with a beak (a toggletip).\n *\n * - **Naming.** The button is named \"Information\" (`aria-label`, which localizes it). Next to a\n * heading or other text, pass `aria-labelledby` with that text's id and the button's own `id`:\n * the button is then named \"‹text› Information\", so several info buttons on a page are told\n * apart. The note (`role=\"note\"`) is labelled like the button: by the same `aria-labelledby`\n * ids, else by the button. The button has `aria-expanded`, and `aria-controls` while the note is\n * open, but no `aria-haspopup`: a note is none of the popup kinds it names.\n * - **Opening.** A click pins the note open, and a click on a pinned note closes it. With\n * `openOnHover` (the default) a mouse pointer resting on the button opens it after 250 ms, and\n * it closes 250 ms after the pointer has left the button and the note (a pointer moving\n * diagonally from the button into the note keeps it open; touch and pen never hover); keyboard\n * focus on the button opens it too, while focus that comes from a pointer press waits for the\n * click. A hover close moves no focus, and a note that keyboard focus opened or keeps never\n * closes because of pointer movement. Opening never moves focus. Escape, a press outside and\n * focus moving outside the button and the note close it; Escape returns focus to the button when\n * it was in the note, and a dismissed note opens again on keyboard focus only once focus has\n * left both.\n * - **Tab path.** The note is portaled, but it keeps its place right after the button in the\n * keyboard order: Tab from the open button enters the note's first element (a link in it), Tab\n * past its last element continues after the button, and Shift+Tab from its first element\n * returns to the button.\n * - **One copy of `info`.** While the note is closed, `info` renders next to the button in an\n * element that is not displayed and describes the button (`aria-describedby`), so it is in the\n * server HTML and is read when the button takes focus. While the note is shown, `info` renders\n * only inside it, in an unnamed element that describes the button. `info` is never in the\n * document twice, so content with ids or effects is safe; it remounts each time the note opens\n * and closes, so state inside it starts over and its effects run again.\n * - **Structure.** The button is the root element: `ref`, `className`, `style` and every other\n * attribute go to it, the consumer's handlers first. The description and the note render next\n * to it, never inside it. Its box is 24×24px at every `size` and renders `data-size`. The note\n * mounts through the presence core: it carries `data-presence`, `data-state` (`open`, and\n * `closed` while it exits, `inert`) and the `data-wave-infolabel-surface` marker; it ships no\n * motion of its own.\n *\n * Fluent's InfoButton takes the same `info` and `size`. It opens on click only\n * (`openOnHover={false}` here), renders its note in place, and moves focus into the note; this one\n * keeps focus on the button and renders the note in a portal. Fluent's `popover` props and a\n * controlled open state are not offered.\n *\n * @example\n * <h2 id=\"billing\">Billing</h2>\n * <InfoButton\n * id=\"billing-info\"\n * aria-labelledby=\"billing billing-info\"\n * info={<>Invoices are sent monthly. <a href=\"/help/billing\">Learn more</a></>}\n * />\n */\nexport const InfoButton = ({\n info,\n size: sizeProp,\n openOnHover = true,\n id: idProp,\n type = 'button',\n 'aria-label': ariaLabelProp,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n className,\n disabled,\n onClick,\n onFocus,\n onBlur,\n onKeyDown,\n onPointerDown,\n onMouseDown,\n onPointerEnter,\n onPointerMove,\n onPointerLeave,\n ref,\n ...rest\n}: InfoButtonProps) => {\n // A size from untyped code renders, and reports, the default.\n const size: CoreSize = sizeProp === 'small' || sizeProp === 'large' ? sizeProp : 'medium';\n const generatedId = useId('info-button');\n const id = idProp || generatedId;\n const noteClass = React.useContext(InfoNoteClassContext);\n const noteClassName = noteClass?.buttonId === id ? noteClass.className : undefined;\n // An empty name would leave the button and the note unnamed: the glyph is decorative.\n const ariaLabel = ariaLabelProp || 'Information';\n const noteId = useId('info-note');\n // The one copy of `info`: not displayed while the note is closed, inside the note while shown.\n const infoId = useId('info');\n const [reason, setReason] = React.useState<OpenReason | null>(null);\n const open = reason !== null;\n\n const buttonRef = React.useRef<HTMLButtonElement | null>(null);\n const noteRef = React.useRef<HTMLDivElement | null>(null);\n const arrowRef = React.useRef<HTMLDivElement | null>(null);\n // The button and the note held in state too (C-POPUPS): for the hover intent (the safe zone\n // measures both) and the focus restore.\n const [buttonElement, setButtonElement] = React.useState<HTMLButtonElement | null>(null);\n const [note, setNote] = React.useState<HTMLDivElement | null>(null);\n\n // The latest reason, for the hover close (read in the hover intent's pointer events and timers).\n const reasonRef = React.useRef(reason);\n React.useLayoutEffect(() => {\n reasonRef.current = reason;\n });\n // Set by a dismissal (Escape, outside press): keyboard focus reopens only after focus has left\n // both the button and the note (a focus restore must not reopen it). A click that closes the\n // note sets it too: in Safari a click leaves focus on <body>, and the focus restore follows. It\n // blocks only a reopening: keyboard focus still takes over a note that a click or hover opened.\n const dismissedRef = React.useRef(false);\n // A pointer press focuses the button; that focus waits for the click (0.7).\n const pointerPressRef = React.useRef(false);\n const pressTimerRef = React.useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n // A hover close in flight, set before its request: that close moves no focus. Every other close\n // request and every opening clears it.\n const hoverCloseRef = React.useRef(false);\n\n // The note mounts and unmounts through the presence core; the dismiss layer, the focus restore\n // and the positioning below key on `open`, so they go on close, not after an exit motion.\n // Destructured: react-hooks/refs treats an object whose member is passed to `ref` as a ref.\n const { isMounted, ref: presenceRef, presenceProps } = usePresence(open);\n const { setReference, setFloating, floatingProps, arrowStyles, side } = usePopupPosition({\n open,\n side: 'top',\n align: 'center',\n offset: 8,\n arrowRef,\n });\n const { layerId } = useDismiss({\n open,\n onDismiss: (dismissReason) => {\n // Only a focusin elsewhere dismisses with 'focus-outside' (focus lost to <body> never does):\n // focus has left both then, so keyboard focus may reopen the note at once.\n if (dismissReason !== 'focus-outside') dismissedRef.current = true;\n hoverCloseRef.current = false;\n // Declared below; the layer calls this only from its document listeners, after render.\n cancelHover();\n setReason(null);\n },\n refs: [buttonRef, noteRef],\n anchorRef: buttonRef,\n focusOutside: true,\n });\n // Focus returns to the button when the note closes with focus inside it (or lost to <body>); a\n // hover close returns only focus that is inside it, so it never moves focus.\n useRestoreFocus({\n enabled: open,\n container: note,\n triggerRef: buttonRef,\n onlyIfFocusInside: true,\n isHoverClose: () => hoverCloseRef.current,\n });\n\n const {\n triggerHandlers: hoverTriggerHandlers,\n surfaceHandlers: hoverSurfaceHandlers,\n cancel: cancelHover,\n } = useHoverIntent({\n // Browsers send pointer events to a disabled button too, and the hook skips only an\n // `aria-disabled` trigger.\n enabled: openOnHover && !disabled,\n open,\n openDelay: HOVER_DELAY,\n closeDelay: HOVER_DELAY,\n trigger: buttonElement,\n surface: note,\n onOpen: () => {\n hoverCloseRef.current = false;\n setReason((current) => current ?? 'hover');\n },\n onClose: () => {\n hoverCloseRef.current = true;\n setReason((current) => (current === 'hover' ? null : current));\n },\n // Only a note opened by hover, not pinned and not keyboard-owned, closes by hover; focus\n // inside the note (or a layer opened from it) blocks it too (Phase 2 D18).\n canClose: () => reasonRef.current === 'hover' && !isFocusInSurfaceTree(note, layerId),\n });\n\n React.useEffect(() => () => clearTimeout(pressTimerRef.current), []);\n\n // A dismissal ends once focus has left both the button and the note: at the button's blur\n // (below) and at a focusout of the note. Focus the note loses to nothing is the note closing\n // (removed, or inert while it exits): that ends nothing.\n React.useEffect(() => {\n if (!note) return undefined;\n const onFocusOut = (event: FocusEvent) => {\n const next = event.relatedTarget as Node | null;\n if (!next || note.contains(next) || buttonRef.current?.contains(next)) return;\n dismissedRef.current = false;\n };\n note.addEventListener('focusout', onFocusOut);\n return () => note.removeEventListener('focusout', onFocusOut);\n }, [note]);\n\n // The handlers below read and write refs: useEventCallback keeps them out of render (C-HOOKS).\n const handleClick = useEventCallback(() => {\n cancelHover();\n hoverCloseRef.current = false;\n // Closed, or opened by hover or keyboard focus: pinned. Pinned: closed.\n const closing = reason === 'click';\n if (closing) dismissedRef.current = true;\n setReason(closing ? null : 'click');\n });\n\n const handleFocus = useEventCallback(() => {\n // Keyboard focus opens, as hovering's keyboard counterpart.\n if (!openOnHover || pointerPressRef.current || (dismissedRef.current && !open)) return;\n hoverCloseRef.current = false;\n cancelHover();\n // A note that hover opened becomes focus-owned; a pinned one stays pinned.\n setReason((current) => (current === 'click' ? current : 'focus'));\n });\n\n const handleBlur = useEventCallback((event: React.FocusEvent<HTMLButtonElement>) => {\n const button = event.currentTarget;\n const next = event.relatedTarget as Node | null;\n // Tab from the button into the note keeps a dismissal.\n if (next && note?.contains(next)) return;\n // So does the window losing focus (a switch of window or tab): the button stays the document's\n // focused element, and the browser focuses it again, from nothing, when the window comes back.\n if (!next && button.ownerDocument.activeElement === button) return;\n // Focus lost to nothing in the page (a press on a spot that takes no focus) has left both.\n dismissedRef.current = false;\n });\n\n const handlePointerPress = useEventCallback(() => {\n pointerPressRef.current = true;\n // A press focuses the button (where it does) as the default action of `mousedown`, in the same\n // event turn. Forget the press after that turn: a press that does not focus the button (Safari\n // and Firefox on macOS, iOS) and ends without a click (dragged off, or a touch that becomes a\n // scroll) must not turn a later keyboard focus into pointer focus. A touch tap dispatches its\n // compatibility `mousedown` (and the focus) after the touch ends; that `mousedown` sets the\n // flag again.\n clearTimeout(pressTimerRef.current);\n pressTimerRef.current = setTimeout(() => {\n pressTimerRef.current = undefined;\n pointerPressRef.current = false;\n }, 0);\n });\n\n // Tab from the open button enters the note. The button's own handler runs before a modal focus\n // trap's document listener, which would wrap the Tab when the button is the trap's last element.\n const onButtonKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>) => {\n if (event.key !== 'Tab' || event.shiftKey || !open || !note) return;\n const first = getFirstTabbable(note);\n if (!first) return;\n event.preventDefault();\n first.focus();\n };\n // Tab past the note's last element continues after the button, Shift+Tab from its first element\n // returns to it, and the page order skips the note where its portal is.\n const onNoteKeyDown = usePopoverTabOrder({\n enabled: open,\n surface: note,\n anchorRef: buttonRef,\n getPreviousStop: () => buttonRef.current,\n });\n\n const buttonRefs = useMergedRefs<HTMLButtonElement>(\n ref,\n buttonRef,\n setButtonElement,\n setReference,\n );\n const noteRefs = useMergedRefs<HTMLDivElement>(noteRef, setNote, setFloating, presenceRef);\n\n return (\n <>\n <button\n type={type}\n id={id}\n data-size={size}\n {...rest}\n ref={buttonRefs}\n disabled={disabled}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-describedby={joinIds(ariaDescribedBy, infoId)}\n aria-expanded={open}\n aria-controls={open ? noteId : undefined}\n className={cn(\n infoButtonClassNames.root,\n 'inline-flex size-6 shrink-0 items-center justify-center rounded-full border-0 bg-transparent p-0 text-muted-foreground',\n GLYPH[size].margin,\n 'not-disabled:not-aria-disabled:hover:text-foreground',\n focusRing,\n className,\n )}\n onClick={composeEventHandlers(onClick, handleClick)}\n onFocus={composeEventHandlers(onFocus, handleFocus)}\n onKeyDown={composeEventHandlers(onKeyDown, onButtonKeyDown)}\n onPointerEnter={composeEventHandlers(onPointerEnter, hoverTriggerHandlers.onPointerEnter)}\n onPointerMove={composeEventHandlers(onPointerMove, hoverTriggerHandlers.onPointerMove)}\n onPointerLeave={composeEventHandlers(onPointerLeave, hoverTriggerHandlers.onPointerLeave)}\n // Bookkeeping that follows the DOM (where focus went, whether a press is under way): it\n // runs even when the consumer prevents the default.\n onBlur={composeEventHandlers(onBlur, handleBlur, { checkDefaultPrevented: false })}\n onPointerDown={composeEventHandlers(onPointerDown, handlePointerPress, {\n checkDefaultPrevented: false,\n })}\n onMouseDown={composeEventHandlers(onMouseDown, handlePointerPress, {\n checkDefaultPrevented: false,\n })}\n >\n <InfoIcon size={GLYPH[size].size} />\n </button>\n {/* The same element follows the button in every phase, so a row styled by position never\n shifts: it holds `info` while the note is not mounted, and nothing while the note does. */}\n <span id={isMounted ? undefined : infoId} hidden>\n {isMounted ? null : info}\n </span>\n {isMounted && (\n <Portal layerId={layerId}>\n <div\n ref={noteRefs}\n id={noteId}\n role=\"note\"\n aria-labelledby={ariaLabelledBy || id}\n data-wave-infolabel-surface=\"\"\n data-state={open ? 'open' : 'closed'}\n {...presenceProps}\n {...floatingProps}\n onKeyDown={onNoteKeyDown}\n onPointerEnter={hoverSurfaceHandlers.onPointerEnter}\n onPointerLeave={hoverSurfaceHandlers.onPointerLeave}\n className={cn(\n infoButtonClassNames.info,\n noteClassName,\n 'w-max max-w-[min(20rem,calc(100vw-1rem))] rounded-md border border-border bg-background px-3 py-2 text-caption-1 text-foreground shadow-16',\n )}\n >\n <PopoverBeak\n ref={arrowRef}\n side={side}\n style={arrowStyles}\n data-wave-popover-arrow=\"\"\n className={infoButtonClassNames.beak}\n />\n <div id={infoId}>{info}</div>\n </div>\n </Portal>\n )}\n </>\n );\n};\nInfoButton.displayName = 'InfoButton';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"InfoButton.cjs","names":["React","useId","usePresence","usePopupPosition","useDismiss","useHoverIntent","isFocusInSurfaceTree","useEventCallback","getFirstTabbable","usePopoverTabOrder","useMergedRefs","_Fragment","joinIds","cn","infoButtonClassNames","focusRing","composeEventHandlers","InfoIcon","Portal","PopoverBeak","require_runtime","require_cn","require_composeEventHandlers","require_classNames","require_useEventCallback","require_useId","require_focus","require_useMergedRefs","require_aria","require_usePresence","require_usePopupPosition","require_useDismiss","require_useRestoreFocus","require_styles","require_Portal","require_icons","require_useHoverIntent","require_Popover_shared","react","react_jsx_runtime","GLYPH","HOVER_DELAY","InfoNoteClassContext","InfoButton","sizeProp","idProp","ariaLabelProp","ariaLabelledBy","ariaDescribedBy","rest","size","generatedId","id","noteClass","noteClassName","ariaLabel","noteId","infoId","reason","setReason","open","buttonRef","noteRef","arrowRef","buttonElement","setButtonElement","note","setNote","reasonRef","dismissedRef","pointerPressRef","pressTimerRef","hoverCloseRef","presenceRef","dismissReason","cancelHover","hoverTriggerHandlers","hoverSurfaceHandlers","openOnHover","disabled","current","layerId","onFocusOut","next","event","handleClick","closing","handleFocus","handleBlur","button","handlePointerPress","onButtonKeyDown","first","onNoteKeyDown","buttonRefs","ref","setReference","noteRefs","setFloating","className","onClick","onFocus","onKeyDown","onPointerEnter","onPointerMove","onPointerLeave","onBlur","onPointerDown","onMouseDown","isMounted","info","presenceProps","floatingProps","arrowStyles"],"sources":["../../../src/components/data-display/InfoButton.tsx"],"sourcesContent":["import * as React from 'react';\nimport { cn } from '../../lib/cn';\nimport { joinIds } from '../../lib/aria';\nimport { infoButtonClassNames } from '../../lib/classNames';\nimport { composeEventHandlers } from '../../lib/composeEventHandlers';\nimport { getFirstTabbable } from '../../lib/focus';\nimport { InfoIcon } from '../../lib/icons';\nimport { focusRing } from '../../lib/styles';\nimport type { CoreSize } from '../../lib/types';\nimport { useDismiss } from '../../hooks/useDismiss';\nimport { useEventCallback } from '../../hooks/useEventCallback';\nimport { useHoverIntent } from '../../hooks/useHoverIntent';\nimport { useId } from '../../hooks/useId';\nimport { useMergedRefs } from '../../hooks/useMergedRefs';\nimport { usePopupPosition } from '../../hooks/usePopupPosition';\nimport { usePresence } from '../../hooks/usePresence';\nimport { useRestoreFocus } from '../../hooks/useRestoreFocus';\nimport { isFocusInSurfaceTree, PopoverBeak, usePopoverTabOrder } from '../overlays/Popover.shared';\nimport { Portal } from '../portal/Portal';\n\n/** Properties for the InfoButton component. */\nexport interface InfoButtonProps extends Omit<\n React.ButtonHTMLAttributes<HTMLButtonElement>,\n 'children'\n> {\n /**\n * The information the note shows: text, links or other content. It also describes the button\n * (`aria-describedby`), from the server HTML on. It is rendered once: next to the button, not\n * displayed, while the note is closed, and inside the note while it is shown.\n */\n info: React.ReactNode;\n /**\n * Size of the glyph: 12, 16 or 20px. The button box stays 24×24px at every size, with negative\n * margins above and below, so it never makes a row that centres its items (as InfoLabel does)\n * taller.\n * @default 'medium'\n */\n size?: CoreSize;\n /**\n * Open the note when a mouse pointer rests on the button (after 250 ms; it closes 250 ms after\n * the pointer has left the button and the note, touch and pen never hover), and when the button\n * receives keyboard focus. `false` opens it only on a click, and so on Enter and Space (Fluent's\n * InfoButton opens on click only). A click always toggles the note: it pins a note that hover or\n * keyboard focus opened, and closes a pinned one.\n * @default true\n */\n openOnHover?: boolean;\n /**\n * Accessible name of the button, and of the note, which is labelled like the button. Localize it\n * here. With `aria-labelledby` listing the id of your text and the button's own `id`, the name is\n * your text followed by this one (\"Billing Information\").\n * @default 'Information'\n */\n 'aria-label'?: string;\n /** Ref to the button. */\n ref?: React.Ref<HTMLButtonElement>;\n}\n\n/** Why the note is open: a click pins it; keyboard focus owns it; hover opens it transiently. */\ntype OpenReason = 'hover' | 'focus' | 'click';\n\n/**\n * Glyph size and negative margins per size: the 24px box never makes a row that centres its items\n * taller.\n */\nconst GLYPH: Readonly<Record<CoreSize, { size: number; margin: string }>> = {\n small: { size: 12, margin: '-my-1' },\n medium: { size: 16, margin: '-my-0.5' },\n large: { size: 20, margin: '-my-px' },\n};\n\n/**\n * Milliseconds from a mouse pointer resting on the button to the opening, and from the pointer\n * leaving the button and the note to the hover close (the 0.7 InfoLabel delays).\n */\nconst HOVER_DELAY = 250;\n\n/** What InfoLabel tells the InfoButton it renders: see {@link InfoNoteClassContext}. */\ninterface InfoNoteClass {\n /** The `id` of the InfoButton the class is for. */\n buttonId: string;\n /** The class added to that InfoButton's note. */\n className: string;\n}\n\n/**\n * Internal (P3-00, D5): the class InfoLabel adds to the note of the InfoButton it renders, its\n * 0.8 `info` part. Only the InfoButton whose `id` matches takes it, so an InfoButton inside the\n * label's `info` content takes none. Not exported from the barrels.\n */\nexport const InfoNoteClassContext = React.createContext<InfoNoteClass | null>(null);\nInfoNoteClassContext.displayName = 'InfoNoteClassContext';\n\n/**\n * A button with the info glyph that shows `info` in a note next to it: text, links or other rich\n * content, placed above the button with a beak (a toggletip).\n *\n * - **Naming.** The button is named \"Information\" (`aria-label`, which localizes it). Next to a\n * heading or other text, pass `aria-labelledby` with that text's id and the button's own `id`:\n * the button is then named \"‹text› Information\", so several info buttons on a page are told\n * apart. The note (`role=\"note\"`) is labelled like the button: by the same `aria-labelledby`\n * ids, else by the button. The button has `aria-expanded`, and `aria-controls` while the note is\n * open, but no `aria-haspopup`: a note is none of the popup kinds it names.\n * - **Opening.** A click pins the note open, and a click on a pinned note closes it. With\n * `openOnHover` (the default) a mouse pointer resting on the button opens it after 250 ms, and\n * it closes 250 ms after the pointer has left the button and the note (a pointer moving\n * diagonally from the button into the note keeps it open; touch and pen never hover); keyboard\n * focus on the button opens it too, while focus that comes from a pointer press waits for the\n * click. A hover close moves no focus, and a note that keyboard focus opened or keeps never\n * closes because of pointer movement. Opening never moves focus. Escape, a press outside and\n * focus moving outside the button and the note close it; Escape returns focus to the button when\n * it was in the note, and a dismissed note opens again on keyboard focus only once focus has\n * left both.\n * - **Tab path.** The note is portaled, but it keeps its place right after the button in the\n * keyboard order: Tab from the open button enters the note's first element (a link in it), Tab\n * past its last element continues after the button, and Shift+Tab from its first element\n * returns to the button.\n * - **One copy of `info`.** While the note is closed, `info` renders next to the button in an\n * element that is not displayed and describes the button (`aria-describedby`), so it is in the\n * server HTML and is read when the button takes focus. While the note is shown, `info` renders\n * only inside it, in an unnamed element that describes the button. `info` is never in the\n * document twice, so content with ids or effects is safe; it remounts each time the note opens\n * and closes, so state inside it starts over and its effects run again.\n * - **Structure.** The button is the root element: `ref`, `className`, `style` and every other\n * attribute go to it, the consumer's handlers first. The description and the note render next\n * to it, never inside it. Its box is 24×24px at every `size` and renders `data-size`. The note\n * mounts through the presence core: it carries `data-presence`, `data-state` (`open`, and\n * `closed` while it exits, `inert`) and the `data-wave-infolabel-surface` marker; it ships no\n * motion of its own.\n *\n * Fluent's InfoButton takes the same `info` and `size`. It opens on click only\n * (`openOnHover={false}` here), renders its note in place, and moves focus into the note; this one\n * keeps focus on the button and renders the note in a portal. Fluent's `popover` props and a\n * controlled open state are not offered.\n *\n * @example\n * <h2 id=\"billing\">Billing</h2>\n * <InfoButton\n * id=\"billing-info\"\n * aria-labelledby=\"billing billing-info\"\n * info={<>Invoices are sent monthly. <a href=\"/help/billing\">Learn more</a></>}\n * />\n */\nexport const InfoButton = ({\n info,\n size: sizeProp,\n openOnHover = true,\n id: idProp,\n type = 'button',\n 'aria-label': ariaLabelProp,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n className,\n disabled,\n onClick,\n onFocus,\n onBlur,\n onKeyDown,\n onPointerDown,\n onMouseDown,\n onPointerEnter,\n onPointerMove,\n onPointerLeave,\n ref,\n ...rest\n}: InfoButtonProps) => {\n // A size from untyped code renders, and reports, the default.\n const size: CoreSize = sizeProp === 'small' || sizeProp === 'large' ? sizeProp : 'medium';\n const generatedId = useId('info-button');\n const id = idProp || generatedId;\n const noteClass = React.useContext(InfoNoteClassContext);\n const noteClassName = noteClass?.buttonId === id ? noteClass.className : undefined;\n // An empty name would leave the button and the note unnamed: the glyph is decorative.\n const ariaLabel = ariaLabelProp || 'Information';\n const noteId = useId('info-note');\n // The one copy of `info`: not displayed while the note is closed, inside the note while shown.\n const infoId = useId('info');\n const [reason, setReason] = React.useState<OpenReason | null>(null);\n const open = reason !== null;\n\n const buttonRef = React.useRef<HTMLButtonElement | null>(null);\n const noteRef = React.useRef<HTMLDivElement | null>(null);\n const arrowRef = React.useRef<HTMLDivElement | null>(null);\n // The button and the note held in state too (C-POPUPS): for the hover intent (the safe zone\n // measures both) and the focus restore.\n const [buttonElement, setButtonElement] = React.useState<HTMLButtonElement | null>(null);\n const [note, setNote] = React.useState<HTMLDivElement | null>(null);\n\n // The latest reason, for the hover close (read in the hover intent's pointer events and timers).\n const reasonRef = React.useRef(reason);\n React.useLayoutEffect(() => {\n reasonRef.current = reason;\n });\n // Set by a dismissal (Escape, outside press): keyboard focus reopens only after focus has left\n // both the button and the note (a focus restore must not reopen it). A click that closes the\n // note sets it too: in Safari a click leaves focus on <body>, and the focus restore follows. It\n // blocks only a reopening: keyboard focus still takes over a note that a click or hover opened.\n const dismissedRef = React.useRef(false);\n // A pointer press focuses the button; that focus waits for the click (0.7).\n const pointerPressRef = React.useRef(false);\n const pressTimerRef = React.useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n // A hover close in flight, set before its request: that close moves no focus. Every other close\n // request and every opening clears it.\n const hoverCloseRef = React.useRef(false);\n\n // The note mounts and unmounts through the presence core; the dismiss layer, the focus restore\n // and the positioning below key on `open`, so they go on close, not after an exit motion.\n // Destructured: react-hooks/refs treats an object whose member is passed to `ref` as a ref.\n const { isMounted, ref: presenceRef, presenceProps } = usePresence(open);\n const { setReference, setFloating, floatingProps, arrowStyles, side } = usePopupPosition({\n open,\n side: 'top',\n align: 'center',\n offset: 8,\n arrowRef,\n });\n const { layerId } = useDismiss({\n open,\n onDismiss: (dismissReason) => {\n // Only a focusin elsewhere dismisses with 'focus-outside' (focus lost to <body> never does):\n // focus has left both then, so keyboard focus may reopen the note at once.\n if (dismissReason !== 'focus-outside') dismissedRef.current = true;\n hoverCloseRef.current = false;\n // Declared below; the layer calls this only from its document listeners, after render.\n cancelHover();\n setReason(null);\n },\n refs: [buttonRef, noteRef],\n anchorRef: buttonRef,\n focusOutside: true,\n });\n // Focus returns to the button when the note closes with focus inside it (or lost to <body>); a\n // hover close returns only focus that is inside it, so it never moves focus.\n useRestoreFocus({\n enabled: open,\n container: note,\n triggerRef: buttonRef,\n onlyIfFocusInside: true,\n isHoverClose: () => hoverCloseRef.current,\n });\n\n const {\n triggerHandlers: hoverTriggerHandlers,\n surfaceHandlers: hoverSurfaceHandlers,\n cancel: cancelHover,\n } = useHoverIntent({\n // Browsers send pointer events to a disabled button too, and the hook skips only an\n // `aria-disabled` trigger.\n enabled: openOnHover && !disabled,\n open,\n openDelay: HOVER_DELAY,\n closeDelay: HOVER_DELAY,\n trigger: buttonElement,\n surface: note,\n onOpen: () => {\n hoverCloseRef.current = false;\n setReason((current) => current ?? 'hover');\n },\n onClose: () => {\n hoverCloseRef.current = true;\n setReason((current) => (current === 'hover' ? null : current));\n },\n // Only a note opened by hover, not pinned and not keyboard-owned, closes by hover; focus\n // inside the note (or a layer opened from it) blocks it too (Phase 2 D18).\n canClose: () => reasonRef.current === 'hover' && !isFocusInSurfaceTree(note, layerId),\n });\n\n React.useEffect(() => () => clearTimeout(pressTimerRef.current), []);\n\n // A dismissal ends once focus has left both the button and the note: at the button's blur\n // (below) and at a focusout of the note. Focus the note loses to nothing is the note closing\n // (removed, or inert while it exits): that ends nothing.\n React.useEffect(() => {\n if (!note) return undefined;\n const onFocusOut = (event: FocusEvent) => {\n const next = event.relatedTarget as Node | null;\n if (!next || note.contains(next) || buttonRef.current?.contains(next)) return;\n dismissedRef.current = false;\n };\n note.addEventListener('focusout', onFocusOut);\n return () => note.removeEventListener('focusout', onFocusOut);\n }, [note]);\n\n // The handlers below read and write refs: useEventCallback keeps them out of render (C-HOOKS).\n const handleClick = useEventCallback(() => {\n cancelHover();\n hoverCloseRef.current = false;\n // Closed, or opened by hover or keyboard focus: pinned. Pinned: closed.\n const closing = reason === 'click';\n if (closing) dismissedRef.current = true;\n setReason(closing ? null : 'click');\n });\n\n const handleFocus = useEventCallback(() => {\n // Keyboard focus opens, as hovering's keyboard counterpart.\n if (!openOnHover || pointerPressRef.current || (dismissedRef.current && !open)) return;\n hoverCloseRef.current = false;\n cancelHover();\n // A note that hover opened becomes focus-owned; a pinned one stays pinned.\n setReason((current) => (current === 'click' ? current : 'focus'));\n });\n\n const handleBlur = useEventCallback((event: React.FocusEvent<HTMLButtonElement>) => {\n const button = event.currentTarget;\n const next = event.relatedTarget as Node | null;\n // Tab from the button into the note keeps a dismissal.\n if (next && note?.contains(next)) return;\n // So does the window losing focus (a switch of window or tab): the button stays the document's\n // focused element, and the browser focuses it again, from nothing, when the window comes back.\n if (!next && button.ownerDocument.activeElement === button) return;\n // Focus lost to nothing in the page (a press on a spot that takes no focus) has left both.\n dismissedRef.current = false;\n });\n\n const handlePointerPress = useEventCallback(() => {\n pointerPressRef.current = true;\n // A press focuses the button (where it does) as the default action of `mousedown`, in the same\n // event turn. Forget the press after that turn: a press that does not focus the button (Safari\n // and Firefox on macOS, iOS) and ends without a click (dragged off, or a touch that becomes a\n // scroll) must not turn a later keyboard focus into pointer focus. A touch tap dispatches its\n // compatibility `mousedown` (and the focus) after the touch ends; that `mousedown` sets the\n // flag again.\n clearTimeout(pressTimerRef.current);\n pressTimerRef.current = setTimeout(() => {\n pressTimerRef.current = undefined;\n pointerPressRef.current = false;\n }, 0);\n });\n\n // Tab from the open button enters the note. The button's own handler runs before a modal focus\n // trap's document listener, which would wrap the Tab when the button is the trap's last element.\n const onButtonKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>) => {\n if (event.key !== 'Tab' || event.shiftKey || !open || !note) return;\n const first = getFirstTabbable(note);\n if (!first) return;\n event.preventDefault();\n first.focus();\n };\n // Tab past the note's last element continues after the button, Shift+Tab from its first element\n // returns to it, and the page order skips the note where its portal is.\n const onNoteKeyDown = usePopoverTabOrder({\n enabled: open,\n surface: note,\n anchorRef: buttonRef,\n getPreviousStop: () => buttonRef.current,\n });\n\n const buttonRefs = useMergedRefs<HTMLButtonElement>(\n ref,\n buttonRef,\n setButtonElement,\n setReference,\n );\n const noteRefs = useMergedRefs<HTMLDivElement>(noteRef, setNote, setFloating, presenceRef);\n\n return (\n <>\n <button\n type={type}\n id={id}\n data-size={size}\n {...rest}\n ref={buttonRefs}\n disabled={disabled}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-describedby={joinIds(ariaDescribedBy, infoId)}\n aria-expanded={open}\n aria-controls={open ? noteId : undefined}\n className={cn(\n infoButtonClassNames.root,\n 'inline-flex size-6 shrink-0 items-center justify-center rounded-full border-0 bg-transparent p-0 text-muted-foreground',\n GLYPH[size].margin,\n 'not-disabled:not-aria-disabled:hover:text-foreground',\n focusRing,\n className,\n )}\n onClick={composeEventHandlers(onClick, handleClick)}\n onFocus={composeEventHandlers(onFocus, handleFocus)}\n onKeyDown={composeEventHandlers(onKeyDown, onButtonKeyDown)}\n onPointerEnter={composeEventHandlers(onPointerEnter, hoverTriggerHandlers.onPointerEnter)}\n onPointerMove={composeEventHandlers(onPointerMove, hoverTriggerHandlers.onPointerMove)}\n onPointerLeave={composeEventHandlers(onPointerLeave, hoverTriggerHandlers.onPointerLeave)}\n // Bookkeeping that follows the DOM (where focus went, whether a press is under way): it\n // runs even when the consumer prevents the default.\n onBlur={composeEventHandlers(onBlur, handleBlur, { checkDefaultPrevented: false })}\n onPointerDown={composeEventHandlers(onPointerDown, handlePointerPress, {\n checkDefaultPrevented: false,\n })}\n onMouseDown={composeEventHandlers(onMouseDown, handlePointerPress, {\n checkDefaultPrevented: false,\n })}\n >\n <InfoIcon size={GLYPH[size].size} />\n </button>\n {/* The same element follows the button in every phase, so a row styled by position never\n shifts: it holds `info` while the note is not mounted, and nothing while the note does. */}\n <span id={isMounted ? undefined : infoId} hidden>\n {isMounted ? null : info}\n </span>\n {isMounted && (\n <Portal layerId={layerId}>\n <div\n ref={noteRefs}\n id={noteId}\n role=\"note\"\n aria-labelledby={ariaLabelledBy || id}\n data-wave-infolabel-surface=\"\"\n data-state={open ? 'open' : 'closed'}\n {...presenceProps}\n {...floatingProps}\n onKeyDown={onNoteKeyDown}\n onPointerEnter={hoverSurfaceHandlers.onPointerEnter}\n onPointerLeave={hoverSurfaceHandlers.onPointerLeave}\n className={cn(\n infoButtonClassNames.info,\n noteClassName,\n 'w-max max-w-[min(20rem,calc(100vw-1rem))] rounded-md border border-border bg-background px-3 py-2 text-caption-1 text-foreground shadow-16',\n )}\n >\n <PopoverBeak\n ref={arrowRef}\n side={side}\n style={arrowStyles}\n data-wave-popover-arrow=\"\"\n className={infoButtonClassNames.beak}\n />\n <div id={infoId}>{info}</div>\n </div>\n </Portal>\n )}\n </>\n );\n};\nInfoButton.displayName = 'InfoButton';\n"],"mappings":"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"}
|