@mortenbrudvik/waveui 0.10.0 → 0.12.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 +454 -74
- 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/button/Toolbar.cjs +1 -1
- package/dist/components/button/Toolbar.cjs.map +1 -1
- package/dist/components/button/Toolbar.mjs +25 -30
- package/dist/components/button/Toolbar.mjs.map +1 -1
- package/dist/components/button/Toolbar.navigation.cjs +2 -0
- package/dist/components/button/Toolbar.navigation.cjs.map +1 -0
- package/dist/components/button/Toolbar.navigation.mjs +17 -0
- package/dist/components/button/Toolbar.navigation.mjs.map +1 -0
- 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.context.cjs +2 -0
- package/dist/components/data-display/Avatar.context.cjs.map +1 -0
- package/dist/components/data-display/Avatar.context.mjs +9 -0
- package/dist/components/data-display/Avatar.context.mjs.map +1 -0
- package/dist/components/data-display/Avatar.mjs +66 -61
- package/dist/components/data-display/Avatar.mjs.map +1 -1
- 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.mjs +10 -10
- 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 +2 -0
- package/dist/components/data-display/InteractionTag.cjs.map +1 -0
- package/dist/components/data-display/InteractionTag.mjs +234 -0
- package/dist/components/data-display/InteractionTag.mjs.map +1 -0
- 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 +222 -90
- package/dist/components/data-display/Tag.mjs.map +1 -1
- package/dist/components/data-display/TagGroup.cjs +2 -0
- package/dist/components/data-display/TagGroup.cjs.map +1 -0
- package/dist/components/data-display/TagGroup.context.cjs +2 -0
- package/dist/components/data-display/TagGroup.context.cjs.map +1 -0
- package/dist/components/data-display/TagGroup.context.mjs +113 -0
- package/dist/components/data-display/TagGroup.context.mjs.map +1 -0
- package/dist/components/data-display/TagGroup.mjs +149 -0
- package/dist/components/data-display/TagGroup.mjs.map +1 -0
- package/dist/components/data-display/tagStyles.cjs +2 -0
- package/dist/components/data-display/tagStyles.cjs.map +1 -0
- package/dist/components/data-display/tagStyles.mjs +108 -0
- package/dist/components/data-display/tagStyles.mjs.map +1 -0
- package/dist/components/feedback/Toaster.cjs +1 -1
- package/dist/components/feedback/Toaster.cjs.map +1 -1
- package/dist/components/feedback/Toaster.mjs +287 -255
- 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.expand.cjs +1 -1
- package/dist/components/input/Combobox.expand.cjs.map +1 -1
- package/dist/components/input/Combobox.expand.mjs +7 -7
- package/dist/components/input/Combobox.expand.mjs.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 +101 -94
- package/dist/components/input/Option.mjs.map +1 -1
- package/dist/components/input/SwatchPicker.cjs.map +1 -1
- package/dist/components/input/SwatchPicker.context.cjs.map +1 -1
- package/dist/components/input/SwatchPicker.context.mjs.map +1 -1
- package/dist/components/input/SwatchPicker.mjs.map +1 -1
- package/dist/components/input/TagPicker.cjs +1 -1
- package/dist/components/input/TagPicker.cjs.map +1 -1
- package/dist/components/input/TagPicker.context.cjs +2 -0
- package/dist/components/input/TagPicker.context.cjs.map +1 -0
- package/dist/components/input/TagPicker.context.mjs +20 -0
- package/dist/components/input/TagPicker.context.mjs.map +1 -0
- package/dist/components/input/TagPicker.create.cjs +2 -0
- package/dist/components/input/TagPicker.create.cjs.map +1 -0
- package/dist/components/input/TagPicker.create.mjs +24 -0
- package/dist/components/input/TagPicker.create.mjs.map +1 -0
- package/dist/components/input/TagPicker.mjs +13 -279
- package/dist/components/input/TagPicker.mjs.map +1 -1
- package/dist/components/input/TagPicker.option.cjs +2 -0
- package/dist/components/input/TagPicker.option.cjs.map +1 -0
- package/dist/components/input/TagPicker.option.mjs +45 -0
- package/dist/components/input/TagPicker.option.mjs.map +1 -0
- package/dist/components/input/TagPicker.overflow.cjs +2 -0
- package/dist/components/input/TagPicker.overflow.cjs.map +1 -0
- package/dist/components/input/TagPicker.overflow.mjs +153 -0
- package/dist/components/input/TagPicker.overflow.mjs.map +1 -0
- package/dist/components/input/TagPicker.parts.cjs +2 -0
- package/dist/components/input/TagPicker.parts.cjs.map +1 -0
- package/dist/components/input/TagPicker.parts.mjs +425 -0
- package/dist/components/input/TagPicker.parts.mjs.map +1 -0
- package/dist/components/input/TagPicker.root.cjs +2 -0
- package/dist/components/input/TagPicker.root.cjs.map +1 -0
- package/dist/components/input/TagPicker.root.mjs +417 -0
- package/dist/components/input/TagPicker.root.mjs.map +1 -0
- 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/Accordion.cjs.map +1 -1
- package/dist/components/layout/Accordion.mjs.map +1 -1
- package/dist/components/navigation/Hamburger.cjs +2 -0
- package/dist/components/navigation/Hamburger.cjs.map +1 -0
- package/dist/components/navigation/Hamburger.mjs +54 -0
- package/dist/components/navigation/Hamburger.mjs.map +1 -0
- 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/navigation/Nav.cjs +1 -1
- package/dist/components/navigation/Nav.cjs.map +1 -1
- package/dist/components/navigation/Nav.context.cjs +2 -0
- package/dist/components/navigation/Nav.context.cjs.map +1 -0
- package/dist/components/navigation/Nav.context.mjs +137 -0
- package/dist/components/navigation/Nav.context.mjs.map +1 -0
- package/dist/components/navigation/Nav.mjs +151 -258
- package/dist/components/navigation/Nav.mjs.map +1 -1
- package/dist/components/navigation/Nav.parts.cjs +2 -0
- package/dist/components/navigation/Nav.parts.cjs.map +1 -0
- package/dist/components/navigation/Nav.parts.mjs +77 -0
- package/dist/components/navigation/Nav.parts.mjs.map +1 -0
- package/dist/components/navigation/Nav.root.cjs +2 -0
- package/dist/components/navigation/Nav.root.cjs.map +1 -0
- package/dist/components/navigation/Nav.root.mjs +78 -0
- package/dist/components/navigation/Nav.root.mjs.map +1 -0
- package/dist/components/navigation/NavDrawer.cjs +2 -0
- package/dist/components/navigation/NavDrawer.cjs.map +1 -0
- package/dist/components/navigation/NavDrawer.context.cjs +2 -0
- package/dist/components/navigation/NavDrawer.context.cjs.map +1 -0
- package/dist/components/navigation/NavDrawer.context.mjs +11 -0
- package/dist/components/navigation/NavDrawer.context.mjs.map +1 -0
- package/dist/components/navigation/NavDrawer.mjs +179 -0
- package/dist/components/navigation/NavDrawer.mjs.map +1 -0
- package/dist/components/overlays/Dialog.cjs +1 -1
- package/dist/components/overlays/Dialog.cjs.map +1 -1
- package/dist/components/overlays/Dialog.mjs +188 -123
- package/dist/components/overlays/Dialog.mjs.map +1 -1
- package/dist/components/overlays/Dialog.shared.cjs +1 -1
- package/dist/components/overlays/Dialog.shared.cjs.map +1 -1
- package/dist/components/overlays/Dialog.shared.mjs +157 -81
- package/dist/components/overlays/Dialog.shared.mjs.map +1 -1
- package/dist/components/overlays/Dialog.slots.cjs +2 -0
- package/dist/components/overlays/Dialog.slots.cjs.map +1 -0
- package/dist/components/overlays/Dialog.slots.mjs +129 -0
- package/dist/components/overlays/Dialog.slots.mjs.map +1 -0
- package/dist/components/overlays/Drawer.cjs +1 -1
- package/dist/components/overlays/Drawer.cjs.map +1 -1
- package/dist/components/overlays/Drawer.context.cjs +2 -0
- package/dist/components/overlays/Drawer.context.cjs.map +1 -0
- package/dist/components/overlays/Drawer.context.mjs +29 -0
- package/dist/components/overlays/Drawer.context.mjs.map +1 -0
- package/dist/components/overlays/Drawer.mjs +12 -187
- package/dist/components/overlays/Drawer.mjs.map +1 -1
- package/dist/components/overlays/Drawer.panel.cjs +2 -0
- package/dist/components/overlays/Drawer.panel.cjs.map +1 -0
- package/dist/components/overlays/Drawer.panel.mjs +159 -0
- package/dist/components/overlays/Drawer.panel.mjs.map +1 -0
- package/dist/components/overlays/Drawer.parts.cjs +2 -0
- package/dist/components/overlays/Drawer.parts.cjs.map +1 -0
- package/dist/components/overlays/Drawer.parts.mjs +152 -0
- package/dist/components/overlays/Drawer.parts.mjs.map +1 -0
- package/dist/components/overlays/Drawer.root.cjs +2 -0
- package/dist/components/overlays/Drawer.root.cjs.map +1 -0
- package/dist/components/overlays/Drawer.root.mjs +207 -0
- package/dist/components/overlays/Drawer.root.mjs.map +1 -0
- package/dist/components/overlays/Drawer.scroll.cjs +2 -0
- package/dist/components/overlays/Drawer.scroll.cjs.map +1 -0
- package/dist/components/overlays/Drawer.scroll.mjs +42 -0
- package/dist/components/overlays/Drawer.scroll.mjs.map +1 -0
- 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 +1 -1
- package/dist/hooks/useFocusTrap.cjs.map +1 -1
- package/dist/hooks/useFocusTrap.mjs +115 -98
- 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 +34 -21
- package/dist/hooks/useModalLayer.mjs.map +1 -1
- 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/useRestoreFocus.cjs +1 -1
- package/dist/hooks/useRestoreFocus.cjs.map +1 -1
- package/dist/hooks/useRestoreFocus.mjs +91 -78
- package/dist/hooks/useRestoreFocus.mjs.map +1 -1
- 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 +180 -121
- package/dist/hooks/useRovingTabIndex.mjs.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +3070 -269
- package/dist/index.d.ts +3070 -269
- package/dist/index.mjs +127 -107
- package/dist/lib/classNames.cjs +1 -1
- package/dist/lib/classNames.cjs.map +1 -1
- package/dist/lib/classNames.mjs +137 -77
- package/dist/lib/classNames.mjs.map +1 -1
- package/dist/lib/icons.cjs +1 -1
- package/dist/lib/icons.cjs.map +1 -1
- package/dist/lib/icons.mjs +13 -6
- package/dist/lib/icons.mjs.map +1 -1
- package/dist/lib/layers.cjs +1 -1
- package/dist/lib/layers.cjs.map +1 -1
- package/dist/lib/layers.mjs +120 -111
- package/dist/lib/layers.mjs.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/styles/tokens.css +25 -0
|
@@ -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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"}
|
|
@@ -8,12 +8,12 @@ import { getFirstTabbable as a } from "../../lib/focus.mjs";
|
|
|
8
8
|
import { useMergedRefs as o } from "../../hooks/useMergedRefs.mjs";
|
|
9
9
|
import { joinIds as s } from "../../lib/aria.mjs";
|
|
10
10
|
import { usePresence as ee } from "../../hooks/usePresence.mjs";
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
11
|
+
import { usePopupPosition as te } from "../../hooks/usePopupPosition.mjs";
|
|
12
|
+
import { useDismiss as ne } from "../../hooks/useDismiss.mjs";
|
|
13
|
+
import { useRestoreFocus as re } from "../../hooks/useRestoreFocus.mjs";
|
|
14
|
+
import { focusRing as ie } from "../../lib/styles.mjs";
|
|
15
|
+
import { Portal as ae } from "../portal/Portal.mjs";
|
|
16
|
+
import { InfoIcon as oe } from "../../lib/icons.mjs";
|
|
17
17
|
import { useHoverIntent as se } from "../../hooks/useHoverIntent.mjs";
|
|
18
18
|
import { PopoverBeak as ce, isFocusInSurfaceTree as c, usePopoverTabOrder as l } from "../overlays/Popover.shared.mjs";
|
|
19
19
|
import * as u from "react";
|
|
@@ -39,13 +39,13 @@ var _ = ({ info: _, size: v, openOnHover: y = !0, id: le, type: ue = "button", "
|
|
|
39
39
|
u.useLayoutEffect(() => {
|
|
40
40
|
H.current = F;
|
|
41
41
|
});
|
|
42
|
-
let U = u.useRef(!1), W = u.useRef(!1), G = u.useRef(void 0), K = u.useRef(!1), { isMounted: q, ref: Ce, presenceProps: we } = ee(L), { setReference: Te, setFloating: Ee, floatingProps: De, arrowStyles: Oe, side: ke } =
|
|
42
|
+
let U = u.useRef(!1), W = u.useRef(!1), G = u.useRef(void 0), K = u.useRef(!1), { isMounted: q, ref: Ce, presenceProps: we } = ee(L), { setReference: Te, setFloating: Ee, floatingProps: De, arrowStyles: Oe, side: ke } = te({
|
|
43
43
|
open: L,
|
|
44
44
|
side: "top",
|
|
45
45
|
align: "center",
|
|
46
46
|
offset: 8,
|
|
47
47
|
arrowRef: B
|
|
48
|
-
}), { layerId: J } =
|
|
48
|
+
}), { layerId: J } = ne({
|
|
49
49
|
open: L,
|
|
50
50
|
onDismiss: (e) => {
|
|
51
51
|
e !== "focus-outside" && (U.current = !0), K.current = !1, Z(), I(null);
|
|
@@ -54,7 +54,7 @@ var _ = ({ info: _, size: v, openOnHover: y = !0, id: le, type: ue = "button", "
|
|
|
54
54
|
anchorRef: R,
|
|
55
55
|
focusOutside: !0
|
|
56
56
|
});
|
|
57
|
-
|
|
57
|
+
re({
|
|
58
58
|
enabled: L,
|
|
59
59
|
container: V,
|
|
60
60
|
triggerRef: R,
|
|
@@ -120,7 +120,7 @@ var _ = ({ info: _, size: v, openOnHover: y = !0, id: le, type: ue = "button", "
|
|
|
120
120
|
"aria-describedby": s(fe, P),
|
|
121
121
|
"aria-expanded": L,
|
|
122
122
|
"aria-controls": L ? N : void 0,
|
|
123
|
-
className: e(n.root, "inline-flex size-6 shrink-0 items-center justify-center rounded-full border-0 bg-transparent p-0 text-muted-foreground", m[D].margin, "not-disabled:not-aria-disabled:hover:text-foreground",
|
|
123
|
+
className: e(n.root, "inline-flex size-6 shrink-0 items-center justify-center rounded-full border-0 bg-transparent p-0 text-muted-foreground", m[D].margin, "not-disabled:not-aria-disabled:hover:text-foreground", ie, pe),
|
|
124
124
|
onClick: t(me, Ae),
|
|
125
125
|
onFocus: t(he, je),
|
|
126
126
|
onKeyDown: t(_e, Me),
|
|
@@ -130,14 +130,14 @@ var _ = ({ info: _, size: v, openOnHover: y = !0, id: le, type: ue = "button", "
|
|
|
130
130
|
onBlur: t(ge, Q, { checkDefaultPrevented: !1 }),
|
|
131
131
|
onPointerDown: t(ve, $, { checkDefaultPrevented: !1 }),
|
|
132
132
|
onMouseDown: t(ye, $, { checkDefaultPrevented: !1 }),
|
|
133
|
-
children: /* @__PURE__ */ f(
|
|
133
|
+
children: /* @__PURE__ */ f(oe, { size: m[D].size })
|
|
134
134
|
}),
|
|
135
135
|
/* @__PURE__ */ f("span", {
|
|
136
136
|
id: q ? void 0 : P,
|
|
137
137
|
hidden: !0,
|
|
138
138
|
children: q ? null : _
|
|
139
139
|
}),
|
|
140
|
-
q && /* @__PURE__ */ f(
|
|
140
|
+
q && /* @__PURE__ */ f(ae, {
|
|
141
141
|
layerId: J,
|
|
142
142
|
children: /* @__PURE__ */ p("div", {
|
|
143
143
|
ref: Fe,
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use client";const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../../lib/cn.cjs"),n=require("../../lib/dev.cjs"),r=require("../../lib/slot.cjs"),i=require("../../lib/composeEventHandlers.cjs"),a=require("../../lib/classNames.cjs"),o=require("../../hooks/useEventCallback.cjs"),s=require("../../hooks/useId.cjs"),c=require("../../hooks/useMergedRefs.cjs"),l=require("../../lib/aria.cjs"),u=require("../../lib/styles.cjs"),d=require("../button/Button.slots.cjs"),f=require("../../lib/icons.cjs"),p=require("../../lib/labelInName.cjs"),m=require("../../lib/events.cjs"),h=require("./TagGroup.context.cjs"),g=require("./Avatar.context.cjs"),_=require("./tagStyles.cjs");let v=require("react");v=e.__toESM(v,1);let y=require("react/jsx-runtime");var b=()=>{};function x(){let e=0,t=!1,n=new Set,r=()=>{for(let e of n)e()};return{subscribe:e=>(n.add(e),()=>{n.delete(e)}),hasPrimary:()=>!t||e>0,register:()=>(e+=1,r(),()=>{--e,r()}),settle:()=>{t||(t=!0,r())}}}var S={subscribe:()=>b,hasPrimary:()=>!0,register:()=>b,settle:b},C=v.createContext(null);C.displayName=`InteractionTagContext`;var w={size:`medium`,shape:`circular`,look:`filled`,disabled:!1,selected:!1,pressed:void 0,dimDisabled:!0,primaryTextId:``,primaries:S,secondaryRef:null,dismiss:()=>{},toggle:()=>{}};function T(e){return v.useContext(C)||(n.reportMissingContext(e,`InteractionTag`),w)}function E(e,t){return typeof e==`string`&&Object.hasOwn(t,e)}function D(e,n){return t.cn(n?[_.INTERACTION_TAG_DISABLED_CLASSES,d.mergedAriaDisabledClasses]:_.INTERACTION_TAG_UNDIMMED_DISABLED_CLASSES,e&&u.forcedColors.disabled)}var O="InteractionTag: `selected` has no effect in a TagGroup that selects (`value`, `defaultValue` or `onValueChange`); the group's `value` decides.";function k(e,t){if(e)return t?d.MERGED_DISABLED_FOCUSABLE_PROPS:{disabled:!0}}var A=({value:e,appearance:r,size:i,shape:l,disabled:u=!1,selected:d,role:f,hidden:p,className:m,children:g,ref:b,...S})=>{let w=h.useTagGroupContext(),T=v.useRef(null),D=c.useMergedRefs(T,b),k=v.useRef(null),A=s.useId(`interaction-tag-text`),[j]=v.useState(x);v.useLayoutEffect(()=>{j.settle()},[j]);let M=E(i,_.tagSizeClasses)?i:w?.size??`medium`,N=E(r,_.tagAppearanceClasses)?r:w?.appearance??`filled`,P=E(l,_.tagShapeClasses)?l:w?.shape??`circular`,F=(w?.disabled??!1)||u,I=w?.dimDisabled??!0,L=h.useTagGroupItem(T,e),R=w?.selecting??!1,z=R?w?.isSelected(L)??!1:typeof d==`boolean`?d:void 0,B=z===!0,V=B&&!F?`selected`:N,H=R&&d!==void 0;v.useEffect(()=>{H&&n.warnOnce(`InteractionTag:selected-in-group`,O)},[H]);let U=o.useEventCallback(()=>{w?.dismiss(L),w?.deselect(L)}),W=o.useEventCallback(()=>{w?.toggle(L)}),G=w?.mode===`list`?`listitem`:void 0,K=e!==void 0&&(w?.hiddenValues?.has(e)??!1),q=v.useMemo(()=>({size:M,shape:P,look:V,disabled:F,selected:B,pressed:z,dimDisabled:I,primaryTextId:A,primaries:j,secondaryRef:k,dismiss:U,toggle:W}),[M,P,V,F,B,z,I,A,j,U,W]);return(0,y.jsx)(C.Provider,{value:q,children:(0,y.jsx)(`div`,{"data-appearance":N,"data-size":M,"data-shape":P,"data-selected":B?``:void 0,"data-disabled":F?``:void 0,"data-wave-tag-item":``,role:f??G,hidden:K||p,...S,ref:D,className:t.cn(a.interactionTagClassNames.root,`group/interaction-tag inline-flex items-stretch`,m),children:g})})};A.displayName=`InteractionTag`;var j=({media:e,icon:n,secondaryText:o,disabled:s=!1,"aria-pressed":c,className:l,children:d,onClick:f,onKeyDown:p,onFocus:b,onBlur:x,ref:S,...C})=>{let w=T(`InteractionTag.Primary`),{size:E,look:O}=w,A=w.disabled||s,j=h.useHeldFocus(A),M=w.primaries.register;v.useLayoutEffect(()=>M(),[M]);let N=r.slotRendersContent(e),P=r.slotRendersContent(n),F=r.materialiseSlotContent(o),I=r.slotRendersContent(F),L=v.useMemo(()=>({size:_.TAG_AVATAR_SIZE[E]}),[E]),R=e=>{!A&&m.isOwnEvent(e)&&w.toggle()},z=e=>{if(A||e.target!==e.currentTarget||!h.isDismissKey(e))return;let t=w.secondaryRef?.current;t&&!t.disabled&&(e.preventDefault(),t.click())},B=(0,y.jsx)(`span`,{id:w.primaryTextId||void 0,className:t.cn(a.interactionTagPrimaryClassNames.primaryText,I&&`text-caption-1 leading-3.5`),children:d});return(0,y.jsxs)(`button`,{type:`button`,"aria-pressed":w.pressed??c,"data-selected":w.selected?``:void 0,...C,ref:S,onClick:i.composeEventHandlers(f,R),...k(A,j.held),onKeyDown:i.composeEventHandlers(p,z),onFocus:i.composeEventHandlers(b,j.onFocus,{checkDefaultPrevented:!1}),onBlur:i.composeEventHandlers(x,j.onBlur,{checkDefaultPrevented:!1}),className:t.cn(a.interactionTagPrimaryClassNames.root,_.INTERACTION_TAG_BUTTON_BASE_CLASSES,_.INTERACTION_TAG_PRIMARY_BASE_CLASSES,_.tagSizeClasses[E],_.tagStartInsetClasses[E][N?`media`:P?`icon`:`text`],_.interactionTagPrimaryEndInsetClasses[E],_.interactionTagLookClasses[O],O===`selected`&&[u.forcedColors.selectedContainer,`forced-colors:border-[Highlight]`],_.tagShapeClasses[w.shape],_.INTERACTION_TAG_PRIMARY_JOINED_CLASSES,D(A,w.dimDisabled),u.focusRing,E===`extra-small`&&`before:absolute before:inset-x-0 before:-inset-y-0.5`,l),children:[N&&(0,y.jsx)(g.AvatarSizeContext.Provider,{value:L,children:r.renderSlot(e,`span`,t.cn(a.interactionTagPrimaryClassNames.media,_.tagMediaClasses[E]),{"aria-hidden":!0})}),P&&r.renderSlot(n,`span`,t.cn(a.interactionTagPrimaryClassNames.icon,_.tagIconClasses[E]),{"aria-hidden":!0}),I?(0,y.jsxs)(`span`,{className:_.TAG_TEXT_COLUMN_CLASSES,children:[B,` `,(0,y.jsx)(`span`,{className:t.cn(a.interactionTagPrimaryClassNames.secondaryText,_.TAG_SECONDARY_TEXT_CLASSES),children:F})]}):B]})};j.displayName=`InteractionTagPrimary`;var M="InteractionTag.Secondary: its content received a button. A button cannot contain a button: its children became the content of the Dismiss button and its props were dropped. Pass icon content, and handle the dismissal in `onClick` or in the TagGroup's `onDismiss`.",N=({dismissLabel:e=`Dismiss`,disabled:o=!1,"aria-label":g,"aria-labelledby":x,className:S,children:C,onClick:w,onKeyDown:E,onFocus:O,onBlur:A,ref:j,...N})=>{let P=T(`InteractionTag.Secondary`),{size:F,look:I}=P,L=P.disabled||o,R=h.useHeldFocus(L),z=s.useId(`interaction-tag-dismiss`),B=s.useId(`interaction-tag-dismiss-content`),V=v.useRef(null),H=c.useMergedRefs(V,P.secondaryRef,j);h.useDismissControlFocus(V);let{glyph:U,button:W}=d.unwrapButtonGlyph(C);v.useEffect(()=>{W&&n.warnOnce(`InteractionTag.Secondary:button-child`,M)},[W]);let G=r.materialiseSlotContent(U),K=r.slotRendersContent(G),q=K&&p.hasTextLabel(G),[J,Y]=v.useState(null),X=v.useCallback(e=>J?p.observeTextLabel(J,e):b,[J]),Z=v.useSyncExternalStore(X,()=>J?p.hasRenderedTextLabel(J):q,()=>q),Q=K&&Z,ee=v.useSyncExternalStore(P.primaries.subscribe,P.primaries.hasPrimary,()=>!0),$=g!==void 0||x!==void 0,te=$?x:l.joinIds(Q?B:z,ee?P.primaryTextId:void 0),ne=e=>{m.isOwnEvent(e)&&P.dismiss()},re=e=>{if(e.target===e.currentTarget){if(h.isRepeatedActivation(e)){e.preventDefault();return}h.isDismissKey(e)&&(e.preventDefault(),e.currentTarget.click())}};return(0,y.jsxs)(`button`,{type:`button`,"data-wave-tag-dismiss":``,"data-selected":P.selected?``:void 0,...N,ref:H,"aria-label":g,"aria-labelledby":te,onClick:i.composeEventHandlers(w,ne),...k(L,R.held),onKeyDown:i.composeEventHandlers(E,re),onFocus:i.composeEventHandlers(O,R.onFocus,{checkDefaultPrevented:!1}),onBlur:i.composeEventHandlers(A,R.onBlur,{checkDefaultPrevented:!1}),className:t.cn(a.interactionTagSecondaryClassNames.root,_.INTERACTION_TAG_BUTTON_BASE_CLASSES,_.INTERACTION_TAG_SECONDARY_BASE_CLASSES,_.interactionTagSecondarySizeClasses[F],_.interactionTagLookClasses[I],I===`selected`&&[u.forcedColors.selectedContainer,`forced-colors:border-[Highlight]`],_.interactionTagDividerClasses[I],_.interactionTagSecondaryShapeClasses[P.shape],D(L,P.dimDisabled),u.focusRing,F===`extra-small`&&`before:absolute before:inset-x-0 before:-inset-y-0.5`,S),children:[!$&&(0,y.jsx)(`span`,{id:z,className:`sr-only`,children:e}),K?(0,y.jsx)(`span`,{id:B,ref:Y,className:t.cn(a.interactionTagSecondaryClassNames.dismissIcon,`inline-flex items-center justify-center`,Q&&`px-1.5`),children:G}):(0,y.jsx)(f.DismissIcon,{size:_.TAG_DISMISS_GLYPH_SIZE[F],className:a.interactionTagSecondaryClassNames.dismissIcon})]})};N.displayName=`InteractionTagSecondary`;var P=Object.assign(A,{Primary:j,Secondary:N});exports.InteractionTag=P,exports.InteractionTagPrimary=j,exports.InteractionTagSecondary=N;
|
|
2
|
+
//# sourceMappingURL=InteractionTag.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InteractionTag.cjs","names":["React","cn","INTERACTION_TAG_DISABLED_CLASSES","mergedAriaDisabledClasses","INTERACTION_TAG_UNDIMMED_DISABLED_CLASSES","forcedColors","MERGED_DISABLED_FOCUSABLE_PROPS","useTagGroupContext","useMergedRefs","useId","tagSizeClasses","tagAppearanceClasses","tagShapeClasses","useTagGroupItem","useEventCallback","interactionTagClassNames","useHeldFocus","slotRendersContent","materialiseSlotContent","TAG_AVATAR_SIZE","isOwnEvent","isDismissKey","interactionTagPrimaryClassNames","composeEventHandlers","INTERACTION_TAG_BUTTON_BASE_CLASSES","INTERACTION_TAG_PRIMARY_BASE_CLASSES","tagStartInsetClasses","interactionTagPrimaryEndInsetClasses","interactionTagLookClasses","INTERACTION_TAG_PRIMARY_JOINED_CLASSES","focusRing","AvatarSizeContext","renderSlot","tagMediaClasses","tagIconClasses","TAG_TEXT_COLUMN_CLASSES","TAG_SECONDARY_TEXT_CLASSES","unwrapButtonGlyph","hasTextLabel","observeTextLabel","hasRenderedTextLabel","joinIds","isRepeatedActivation","interactionTagSecondaryClassNames","INTERACTION_TAG_SECONDARY_BASE_CLASSES","interactionTagSecondarySizeClasses","interactionTagDividerClasses","interactionTagSecondaryShapeClasses","DismissIcon","TAG_DISMISS_GLYPH_SIZE","require_runtime","require_cn","require_dev","require_slot","require_composeEventHandlers","require_classNames","require_useEventCallback","require_useId","require_useMergedRefs","require_aria","require_styles","require_Button_slots","require_icons","require_labelInName","require_events","require_TagGroup_context","require_Avatar_context","require_tagStyles","react","react_jsx_runtime","noop","createPrimaryRegistry","count","settled","listeners","notify","listener","onChange","INERT_PRIMARY_REGISTRY","InteractionTagContext","INERT_CONTEXT","useInteractionTagContext","componentName","isKeyOf","value","table","disabledClasses","dimDisabled","disabled","SELECTED_IN_GROUP_MESSAGE","buttonStateProps","held","InteractionTagRoot","appearanceProp","sizeProp","shapeProp","disabledProp","selectedProp","rest","group","rootRef","mergedRef","ref","secondaryRef","primaryTextId","primaries","size","appearance","shape","reportedValue","selecting","pressed","selected","look","selectedIgnored","dismiss","toggle","listItemRole","hiddenByHost","context","role","hidden","className","InteractionTagPrimary","ariaPressed","tag","hold","registerPrimary","hasMedia","media","hasIcon","icon","secondary","secondaryText","hasSecondary","avatarBox","handleClick","event","handleKeyDown","secondaryButton","primaryText","onClick","onKeyDown","onFocus","onBlur","BUTTON_CHILD_MESSAGE","InteractionTagSecondary","ariaLabel","ariaLabelledBy","dismissLabelId","contentId","ownRef","unwrapped","children","content","glyph","hasContent","literalTextLabel","labelTarget","setLabelTarget","subscribeToContent","contentHasTextLabel","hasLabel","hasPrimary","namedByConsumer","labelledBy","dismissLabel","InteractionTag"],"sources":["../../../src/components/data-display/InteractionTag.tsx"],"sourcesContent":["import * as React from 'react';\nimport { joinIds } from '../../lib/aria';\nimport { cn } from '../../lib/cn';\nimport {\n interactionTagClassNames,\n interactionTagPrimaryClassNames,\n interactionTagSecondaryClassNames,\n} from '../../lib/classNames';\nimport { composeEventHandlers } from '../../lib/composeEventHandlers';\nimport { reportMissingContext, warnOnce } from '../../lib/dev';\nimport { isOwnEvent } from '../../lib/events';\nimport { DismissIcon } from '../../lib/icons';\nimport { hasRenderedTextLabel, hasTextLabel, observeTextLabel } from '../../lib/labelInName';\nimport { materialiseSlotContent, renderSlot, slotRendersContent } from '../../lib/slot';\nimport type { Slot } from '../../lib/slot';\nimport { focusRing, forcedColors } from '../../lib/styles';\nimport type { TagAppearance, TagShape, TagSize } from '../../lib/types';\nimport { useEventCallback } from '../../hooks/useEventCallback';\nimport { useId } from '../../hooks/useId';\nimport { useMergedRefs } from '../../hooks/useMergedRefs';\nimport {\n MERGED_DISABLED_FOCUSABLE_PROPS,\n mergedAriaDisabledClasses,\n unwrapButtonGlyph,\n} from '../button/Button.slots';\nimport { AvatarSizeContext } from './Avatar.context';\nimport {\n isDismissKey,\n isRepeatedActivation,\n useDismissControlFocus,\n useHeldFocus,\n useTagGroupContext,\n useTagGroupItem,\n} from './TagGroup.context';\nimport {\n INTERACTION_TAG_BUTTON_BASE_CLASSES,\n INTERACTION_TAG_DISABLED_CLASSES,\n INTERACTION_TAG_EXTRA_SMALL_HIT_CLASSES,\n INTERACTION_TAG_PRIMARY_BASE_CLASSES,\n INTERACTION_TAG_PRIMARY_JOINED_CLASSES,\n INTERACTION_TAG_SECONDARY_BASE_CLASSES,\n INTERACTION_TAG_SECONDARY_LABEL_CLASSES,\n INTERACTION_TAG_SELECTED_FORCED_COLORS,\n INTERACTION_TAG_UNDIMMED_DISABLED_CLASSES,\n TAG_AVATAR_SIZE,\n TAG_DISMISS_GLYPH_SIZE,\n TAG_PRIMARY_TEXT_TWO_LINE_CLASSES,\n TAG_SECONDARY_TEXT_CLASSES,\n TAG_TEXT_COLUMN_CLASSES,\n interactionTagDividerClasses,\n interactionTagLookClasses,\n interactionTagPrimaryEndInsetClasses,\n interactionTagSecondaryShapeClasses,\n interactionTagSecondarySizeClasses,\n tagAppearanceClasses,\n tagIconClasses,\n tagMediaClasses,\n tagShapeClasses,\n tagSizeClasses,\n tagStartInsetClasses,\n} from './tagStyles';\nimport type { TagLook } from './tagStyles';\n\n/** Props of InteractionTag: the element that holds the Primary and Secondary buttons. */\nexport interface InteractionTagProps extends React.HTMLAttributes<HTMLDivElement> {\n /**\n * Identifies the tag in a TagGroup: the value `onDismiss` receives and the selection holds. Give\n * every tag of a group its own value.\n * @default a generated id (a development warning in a group that reports values)\n */\n value?: string;\n /**\n * Colour scheme of both buttons: `filled` (a neutral fill), `outline` (a border on the surface\n * behind it) or `brand` (a light brand tint that follows the brand ramp).\n * @default the TagGroup's `appearance`, else 'filled'\n */\n appearance?: TagAppearance;\n /**\n * `medium` (28px tall), `small` (24px) or `extra-small` (20px); an Avatar in the Primary's\n * `media` is 24, 20 or 16px. Fluent's medium tag is 32px.\n * @default the TagGroup's `size`, else 'medium'\n */\n size?: TagSize;\n /**\n * `circular` (a pill) or `rounded` (4px corners). Fluent's default is `rounded`.\n * @default the TagGroup's `shape`, else 'circular'\n */\n shape?: TagShape;\n /**\n * Disables both buttons natively (a disabled TagGroup does too; a part's own `disabled` cannot\n * re-enable it). A button that has focus when it is disabled keeps it, unavailable, until focus\n * leaves it. Rendered as `data-disabled` on the root.\n * @default false\n */\n disabled?: boolean;\n /**\n * Outside a selecting TagGroup: draws the selected look and makes the Primary a toggle:\n * `aria-pressed=\"true\"`, or `\"false\"` for `selected={false}`. Leave it `undefined` for an\n * action that is not a toggle (no `aria-pressed`). Inside a selecting TagGroup (`value`,\n * `defaultValue` or `onValueChange`) the group's `value` decides, and a defined `selected` is\n * ignored (a development warning). Rendered as `data-selected` on the root and both buttons.\n * Disabled wins over the selected look (`aria-pressed` and `data-selected` still render).\n * Fluent's `selected` is a look only.\n */\n selected?: boolean;\n ref?: React.Ref<HTMLDivElement>;\n}\n\n/** Props of InteractionTag.Primary: the tag's main action (or its toggle in a selecting TagGroup). */\nexport interface InteractionTagPrimaryProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n /**\n * A visual before the text (an Avatar, sized for the tag: 24, 20 or 16px, unless it sets its\n * own `size`). Decorative (`aria-hidden`): the button is named by its text.\n */\n media?: Slot<'span'>;\n /** A decorative icon before the text (`aria-hidden`), in a 20, 16 or 12px box. */\n icon?: Slot<'span'>;\n /**\n * A second, smaller line under the text. Part of the Primary's name, not of the Secondary's. A\n * small or extra-small tag grows to 28px to fit it.\n */\n secondaryText?: React.ReactNode;\n /**\n * Disables the Primary natively; or-ed with the InteractionTag's `disabled` (it cannot\n * re-enable a disabled tag).\n * @default false\n */\n disabled?: boolean;\n ref?: React.Ref<HTMLButtonElement>;\n}\n\n/** Props of InteractionTag.Secondary: the tag's separate Dismiss button. */\nexport interface InteractionTagSecondaryProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n /**\n * Visually hidden text that names the button with the Primary's text (\"Dismiss Cherry\"). A text\n * label in the children replaces it (\"Remove Cherry\"); an `aria-label` replaces the whole name.\n * Localise it here. Fluent's order is the primary text, then the label (\"Cherry remove\").\n * @default 'Dismiss'\n */\n dismissLabel?: string;\n /**\n * Disables the Secondary natively; or-ed with the InteractionTag's `disabled` (it cannot\n * re-enable a disabled tag).\n * @default false\n */\n disabled?: boolean;\n ref?: React.Ref<HTMLButtonElement>;\n}\n\n// ---------------------------------------------------------------------------\n// Context\n// ---------------------------------------------------------------------------\n\n/** What an InteractionTag gives its two buttons (resolved values, never `undefined` but `pressed`). */\ninterface InteractionTagContextValue {\n size: TagSize;\n shape: TagShape;\n /** The look of both buttons: the appearance, or the selected look over it. */\n look: TagLook;\n /** Whether the tag is disabled (its own `disabled`, or its TagGroup's). */\n disabled: boolean;\n /** Whether the tag is selected (`data-selected`). */\n selected: boolean;\n /** The Primary's `aria-pressed`: `undefined` for an action that is not a toggle. */\n pressed: boolean | undefined;\n /** Whether a disabled button dims itself (`false` under a host that dims the tags as a whole). */\n dimDisabled: boolean;\n /** The id of the Primary's text, the second half of the Secondary's name. */\n primaryTextId: string;\n /** Which Primaries render, so the Secondary points at the primary text only while one does. */\n primaries: PrimaryRegistry;\n /** The Secondary, which Delete and Backspace on the Primary click (read at key time). */\n secondaryRef: React.RefObject<HTMLButtonElement | null> | null;\n /**\n * Reports the dismissal to the TagGroup (its `onDismiss(value)`), after the Secondary's\n * `onClick`, and then removes the tag's value from the group's selection (D39).\n */\n dismiss: () => void;\n /** Toggles the tag's value in a selecting TagGroup's selection (the Primary's click). */\n toggle: () => void;\n}\n\nconst noop = () => {};\n\n/**\n * The Primaries an InteractionTag renders, read by its Secondary, whose name points at the\n * primary text only while a Primary renders it (no id reference to a missing element). Until the\n * root's first commit (the server render, hydration and the first client render) a Primary is\n * assumed, so the server HTML holds the whole name and a tag with a Primary renders once; a\n * Secondary without one drops the id after that commit, and takes it again when one mounts. An\n * external store (C-HOOKS): a Primary registers from a layout effect.\n */\ninterface PrimaryRegistry {\n subscribe: (onChange: () => void) => () => void;\n /** Whether the Secondary's name may point at the primary text. */\n hasPrimary: () => boolean;\n /** For a Primary's layout effect: returns its cleanup. */\n register: () => () => void;\n /** For the root's layout effect: from now on the registrations decide. */\n settle: () => void;\n}\n\nfunction createPrimaryRegistry(): PrimaryRegistry {\n let count = 0;\n let settled = false;\n const listeners = new Set<() => void>();\n const notify = () => {\n for (const listener of listeners) listener();\n };\n return {\n subscribe: (onChange) => {\n listeners.add(onChange);\n return () => {\n listeners.delete(onChange);\n };\n },\n hasPrimary: () => !settled || count > 0,\n register: () => {\n count += 1;\n notify();\n return () => {\n count -= 1;\n notify();\n };\n },\n settle: () => {\n if (settled) return;\n settled = true;\n notify();\n },\n };\n}\n\n/** The registry of the inert context: a Primary is always assumed. */\nconst INERT_PRIMARY_REGISTRY: PrimaryRegistry = {\n subscribe: () => noop,\n hasPrimary: () => true,\n register: () => noop,\n settle: noop,\n};\n\n/** Internal: `null` outside an InteractionTag. */\nconst InteractionTagContext = React.createContext<InteractionTagContextValue | null>(null);\nInteractionTagContext.displayName = 'InteractionTagContext';\n\n/** What a part reads outside an InteractionTag in production (C-CONTEXT): an enabled, unnamed tag. */\nconst INERT_CONTEXT: InteractionTagContextValue = {\n size: 'medium',\n shape: 'circular',\n look: 'filled',\n disabled: false,\n selected: false,\n pressed: undefined,\n dimDisabled: true,\n primaryTextId: '',\n primaries: INERT_PRIMARY_REGISTRY,\n secondaryRef: null,\n dismiss: () => {},\n toggle: () => {},\n};\n\nfunction useInteractionTagContext(componentName: string): InteractionTagContextValue {\n const context = React.useContext(InteractionTagContext);\n if (context) return context;\n reportMissingContext(componentName, 'InteractionTag');\n return INERT_CONTEXT;\n}\n\n/** Whether `value` is a key of `table` (an untyped value outside a union falls back). */\nfunction isKeyOf<K extends string>(\n value: unknown,\n table: Readonly<Record<K, unknown>>,\n): value is K {\n return typeof value === 'string' && Object.hasOwn(table, value);\n}\n\n/** The classes of a button's disabled states: dimmed, unless the host dims the tags itself. */\nfunction disabledClasses(disabled: boolean, dimDisabled: boolean): string {\n return cn(\n dimDisabled\n ? [INTERACTION_TAG_DISABLED_CLASSES, mergedAriaDisabledClasses]\n : INTERACTION_TAG_UNDIMMED_DISABLED_CLASSES,\n disabled && forcedColors.disabled,\n );\n}\n\nconst SELECTED_IN_GROUP_MESSAGE =\n \"InteractionTag: `selected` has no effect in a TagGroup that selects (`value`, `defaultValue` or `onValueChange`); the group's `value` decides.\";\n\n/**\n * The state props of a tag button: natively disabled, or, while it keeps the focus it had when it\n * was disabled, the focusable disabled state (its click blocked).\n */\nfunction buttonStateProps(disabled: boolean, held: boolean) {\n if (!disabled) return undefined;\n return held ? MERGED_DISABLED_FOCUSABLE_PROPS : { disabled: true };\n}\n\n// ---------------------------------------------------------------------------\n// Root\n// ---------------------------------------------------------------------------\n\n// The InteractionTag root, documented on the exported `InteractionTag` const.\nconst InteractionTagRoot = ({\n value,\n appearance: appearanceProp,\n size: sizeProp,\n shape: shapeProp,\n disabled: disabledProp = false,\n selected: selectedProp,\n role,\n hidden,\n className,\n children,\n ref,\n ...rest\n}: InteractionTagProps) => {\n const group = useTagGroupContext();\n const rootRef = React.useRef<HTMLDivElement>(null);\n const mergedRef = useMergedRefs<HTMLDivElement>(rootRef, ref);\n const secondaryRef = React.useRef<HTMLButtonElement>(null);\n const primaryTextId = useId('interaction-tag-text');\n // After the first commit, the Primaries that registered decide the Secondary's name (D43).\n const [primaries] = React.useState(createPrimaryRegistry);\n React.useLayoutEffect(() => {\n primaries.settle();\n }, [primaries]);\n\n // The tag's own look, else its TagGroup's, else the default look.\n const size: TagSize = isKeyOf(sizeProp, tagSizeClasses) ? sizeProp : (group?.size ?? 'medium');\n const appearance: TagAppearance = isKeyOf(appearanceProp, tagAppearanceClasses)\n ? appearanceProp\n : (group?.appearance ?? 'filled');\n const shape: TagShape = isKeyOf(shapeProp, tagShapeClasses)\n ? shapeProp\n : (group?.shape ?? 'circular');\n // A disabled group disables every tag: the tag's own `disabled={false}` cannot re-enable it.\n const disabled = (group?.disabled ?? false) || disabledProp;\n const dimDisabled = group?.dimDisabled ?? true;\n\n // In a group that reports dismissals or selects, the tag's value (else a generated id).\n const reportedValue = useTagGroupItem(rootRef, value);\n // In a selecting TagGroup the group's selection decides (the Primary is always a toggle there);\n // elsewhere a boolean `selected` does, and `undefined` is an action that is no toggle.\n const selecting = group?.selecting ?? false;\n const pressed = selecting\n ? (group?.isSelected(reportedValue) ?? false)\n : typeof selectedProp === 'boolean'\n ? selectedProp\n : undefined;\n const selected = pressed === true;\n // Disabled wins over the selected look; the attributes still reflect the state.\n const look: TagLook = selected && !disabled ? 'selected' : appearance;\n\n const selectedIgnored = selecting && selectedProp !== undefined;\n React.useEffect(() => {\n if (selectedIgnored) warnOnce('InteractionTag:selected-in-group', SELECTED_IN_GROUP_MESSAGE);\n }, [selectedIgnored]);\n\n // The group's `onDismiss(value)`, then the value leaves the selection (D39), in one click.\n const dismiss = useEventCallback(() => {\n group?.dismiss(reportedValue);\n group?.deselect(reportedValue);\n });\n const toggle = useEventCallback(() => {\n group?.toggle(reportedValue);\n });\n\n // In a list TagGroup the root is a list item; the consumer's role wins.\n const listItemRole = group?.mode === 'list' ? 'listitem' : undefined;\n // A host hides the tags it counts instead of showing them (TagPicker's `overflow=\"count\"`).\n const hiddenByHost = value !== undefined && (group?.hiddenValues?.has(value) ?? false);\n\n const context = React.useMemo<InteractionTagContextValue>(\n () => ({\n size,\n shape,\n look,\n disabled,\n selected,\n pressed,\n dimDisabled,\n primaryTextId,\n primaries,\n secondaryRef,\n dismiss,\n toggle,\n }),\n [\n size,\n shape,\n look,\n disabled,\n selected,\n pressed,\n dimDisabled,\n primaryTextId,\n primaries,\n dismiss,\n toggle,\n ],\n );\n\n return (\n <InteractionTagContext.Provider value={context}>\n <div\n data-appearance={appearance}\n data-size={size}\n data-shape={shape}\n data-selected={selected ? '' : undefined}\n data-disabled={disabled ? '' : undefined}\n data-wave-tag-item=\"\"\n role={role ?? listItemRole}\n hidden={hiddenByHost || hidden}\n {...rest}\n ref={mergedRef}\n className={cn(\n interactionTagClassNames.root,\n 'group/interaction-tag inline-flex items-stretch',\n className,\n )}\n >\n {children}\n </div>\n </InteractionTagContext.Provider>\n );\n};\nInteractionTagRoot.displayName = 'InteractionTag';\n\n// ---------------------------------------------------------------------------\n// Primary\n// ---------------------------------------------------------------------------\n\n/**\n * The main action of an {@link InteractionTag}: a `<button type=\"button\">` named by its text and\n * `secondaryText` (`media` and `icon` are decorative), with the InteractionTag's look. In a\n * selecting TagGroup it is a toggle (`aria-pressed`): a click, Enter or Space adds the tag's\n * `value` to the group's selection or removes it, after its `onClick` (whose `preventDefault()`\n * cancels the toggle). Elsewhere the InteractionTag's `selected` makes it a toggle. Either state\n * renders its `aria-pressed` over one passed here, which applies only when neither is set. Delete\n * and Backspace on it click the tag's Secondary when there is one. Its props, `ref` and\n * `className` go to the button. Flat name of `InteractionTag.Primary`, for React Server\n * Components.\n */\nexport const InteractionTagPrimary = ({\n media,\n icon,\n secondaryText,\n disabled: disabledProp = false,\n 'aria-pressed': ariaPressed,\n className,\n children,\n onClick,\n onKeyDown,\n onFocus,\n onBlur,\n ref,\n ...rest\n}: InteractionTagPrimaryProps) => {\n const tag = useInteractionTagContext('InteractionTag.Primary');\n const { size, look } = tag;\n const disabled = tag.disabled || disabledProp;\n const hold = useHeldFocus(disabled);\n // Its text names the Secondary while it renders (D43).\n const registerPrimary = tag.primaries.register;\n React.useLayoutEffect(() => registerPrimary(), [registerPrimary]);\n\n // Slots that render nothing render no element and take no room.\n const hasMedia = slotRendersContent(media);\n const hasIcon = slotRendersContent(icon);\n const secondary = materialiseSlotContent(secondaryText);\n const hasSecondary = slotRendersContent(secondary);\n const avatarBox = React.useMemo(() => ({ size: TAG_AVATAR_SIZE[size] }), [size]);\n\n // In a selecting TagGroup a click (also Enter and Space) toggles the tag's value, after the\n // consumer's `onClick`; never for a click from a portal opened from the Primary (C-COMPOSE).\n const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {\n if (!disabled && isOwnEvent(event)) tag.toggle();\n };\n\n // Delete and Backspace dismiss the tag through the Secondary's own click, so whatever cancels\n // that click (its `onClick`'s `preventDefault()`, a disabled or held Secondary) cancels them\n // too. Not from a portal opened from the Primary, and not while the Primary itself is held.\n const handleKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>) => {\n if (disabled || event.target !== event.currentTarget || !isDismissKey(event)) return;\n const secondaryButton = tag.secondaryRef?.current;\n if (!secondaryButton || secondaryButton.disabled) return;\n event.preventDefault();\n secondaryButton.click();\n };\n\n const primaryText = (\n <span\n id={tag.primaryTextId || undefined}\n className={cn(\n interactionTagPrimaryClassNames.primaryText,\n hasSecondary && TAG_PRIMARY_TEXT_TWO_LINE_CLASSES,\n )}\n >\n {children}\n </span>\n );\n\n return (\n <button\n type=\"button\"\n // The tag's state (`selected`, or a selecting TagGroup's value) decides; a consumer\n // `aria-pressed` applies only to a Primary that is no toggle of the tag's.\n aria-pressed={tag.pressed ?? ariaPressed}\n data-selected={tag.selected ? '' : undefined}\n {...rest}\n ref={ref}\n onClick={composeEventHandlers(onClick, handleClick)}\n // After `onClick`: a held Primary's click is blocked, the toggle included.\n {...buttonStateProps(disabled, hold.held)}\n onKeyDown={composeEventHandlers(onKeyDown, handleKeyDown)}\n onFocus={composeEventHandlers(onFocus, hold.onFocus, { checkDefaultPrevented: false })}\n onBlur={composeEventHandlers(onBlur, hold.onBlur, { checkDefaultPrevented: false })}\n className={cn(\n interactionTagPrimaryClassNames.root,\n INTERACTION_TAG_BUTTON_BASE_CLASSES,\n INTERACTION_TAG_PRIMARY_BASE_CLASSES,\n tagSizeClasses[size],\n tagStartInsetClasses[size][hasMedia ? 'media' : hasIcon ? 'icon' : 'text'],\n interactionTagPrimaryEndInsetClasses[size],\n interactionTagLookClasses[look],\n look === 'selected' && [\n forcedColors.selectedContainer,\n INTERACTION_TAG_SELECTED_FORCED_COLORS,\n ],\n tagShapeClasses[tag.shape],\n INTERACTION_TAG_PRIMARY_JOINED_CLASSES,\n disabledClasses(disabled, tag.dimDisabled),\n focusRing,\n size === 'extra-small' && INTERACTION_TAG_EXTRA_SMALL_HIT_CLASSES,\n className,\n )}\n >\n {hasMedia && (\n // An Avatar without its own `size` takes the tag's box.\n <AvatarSizeContext.Provider value={avatarBox}>\n {renderSlot(\n media,\n 'span',\n cn(interactionTagPrimaryClassNames.media, tagMediaClasses[size]),\n { 'aria-hidden': true },\n )}\n </AvatarSizeContext.Provider>\n )}\n {hasIcon &&\n renderSlot(icon, 'span', cn(interactionTagPrimaryClassNames.icon, tagIconClasses[size]), {\n 'aria-hidden': true,\n })}\n {hasSecondary ? (\n <span className={TAG_TEXT_COLUMN_CLASSES}>\n {primaryText}\n {/* A space between the two texts in the button's name (\"Katri Designer\"), whatever a\n name computation makes of the column; the column layout never renders it. */}{' '}\n <span\n className={cn(\n interactionTagPrimaryClassNames.secondaryText,\n TAG_SECONDARY_TEXT_CLASSES,\n )}\n >\n {secondary}\n </span>\n </span>\n ) : (\n primaryText\n )}\n </button>\n );\n};\nInteractionTagPrimary.displayName = 'InteractionTagPrimary';\n\n// ---------------------------------------------------------------------------\n// Secondary\n// ---------------------------------------------------------------------------\n\nconst BUTTON_CHILD_MESSAGE =\n \"InteractionTag.Secondary: its content received a button. A button cannot contain a button: its children became the content of the Dismiss button and its props were dropped. Pass icon content, and handle the dismissal in `onClick` or in the TagGroup's `onDismiss`.\";\n\n/**\n * The separate Dismiss button of an {@link InteractionTag}: a `<button type=\"button\">` after the\n * Primary, named `dismissLabel` plus the Primary's text (\"Dismiss Cherry\"). Its children replace\n * the default dismiss glyph; a text label among them replaces `dismissLabel` in the name (\"Remove\n * Cherry\"), an `aria-label` the whole name. A button passed as its child is unwrapped (its children\n * become the content). Enter, Space, a click, Delete and Backspace call its `onClick`, then, in a\n * TagGroup, the group's `onDismiss(value)`. Its props, `ref` and `className` go to the button.\n * Flat name of `InteractionTag.Secondary`, for React Server Components.\n */\nexport const InteractionTagSecondary = ({\n dismissLabel = 'Dismiss',\n disabled: disabledProp = false,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n className,\n children,\n onClick,\n onKeyDown,\n onFocus,\n onBlur,\n ref,\n ...rest\n}: InteractionTagSecondaryProps) => {\n const tag = useInteractionTagContext('InteractionTag.Secondary');\n const { size, look } = tag;\n const disabled = tag.disabled || disabledProp;\n const hold = useHeldFocus(disabled);\n const dismissLabelId = useId('interaction-tag-dismiss');\n const contentId = useId('interaction-tag-dismiss-content');\n const ownRef = React.useRef<HTMLButtonElement>(null);\n const mergedRef = useMergedRefs<HTMLButtonElement>(ownRef, tag.secondaryRef, ref);\n // Unmounting while it has focus (rendered conditionally), the Secondary hands focus to its own\n // tag's Primary; in a TagGroup, when the whole tag goes, the tag's own move applies (D35).\n useDismissControlFocus(ownRef);\n\n // Its children are the glyph inside the Dismiss button: a button there would nest buttons, so\n // it is unwrapped (C-SLOTS glyph rule).\n const { glyph, button: unwrapped } = unwrapButtonGlyph(children as Slot<'span'>);\n React.useEffect(() => {\n if (unwrapped) warnOnce('InteractionTag.Secondary:button-child', BUTTON_CHILD_MESSAGE);\n }, [unwrapped]);\n const content = materialiseSlotContent(glyph);\n const hasContent = slotRendersContent(content);\n const literalTextLabel = hasContent && hasTextLabel(content);\n\n // C-SLOTS naming (WCAG 2.5.3 Label in Name): a text label in the children replaces\n // `dismissLabel` (\"Remove Cherry\"). The literal children decide on the server and on the first\n // render; after mount, the rendered content does (text from components, later changes).\n const [labelTarget, setLabelTarget] = React.useState<HTMLSpanElement | null>(null);\n const subscribeToContent = React.useCallback(\n (onChange: () => void) => (labelTarget ? observeTextLabel(labelTarget, onChange) : noop),\n [labelTarget],\n );\n const contentHasTextLabel = React.useSyncExternalStore(\n subscribeToContent,\n () => (labelTarget ? hasRenderedTextLabel(labelTarget) : literalTextLabel),\n () => literalTextLabel,\n );\n const hasLabel = hasContent && contentHasTextLabel;\n // The primary text joins the name while a Primary renders it.\n const hasPrimary = React.useSyncExternalStore(\n tag.primaries.subscribe,\n tag.primaries.hasPrimary,\n () => true,\n );\n // A consumer `aria-label` replaces the whole name; a consumer `aria-labelledby` replaces ours.\n const namedByConsumer = ariaLabel !== undefined || ariaLabelledBy !== undefined;\n const labelledBy = namedByConsumer\n ? ariaLabelledBy\n : joinIds(hasLabel ? contentId : dismissLabelId, hasPrimary ? tag.primaryTextId : undefined);\n\n // The consumer's `onClick` first (its `preventDefault()` keeps the group from hearing of it),\n // then the TagGroup's `onDismiss(value)`; never for a click from a portal opened inside.\n const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {\n if (isOwnEvent(event)) tag.dismiss();\n };\n // Delete and Backspace dismiss as Enter does: through the button's own click, so whatever\n // cancels a click (its `onClick`'s `preventDefault()`, a held or disabled button) cancels them.\n // A held key dismisses one tag: the browser clicks a button on every repeated Enter, which\n // would reach the next tag's Dismiss control once focus moved there (D35).\n const handleKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>) => {\n if (event.target !== event.currentTarget) return;\n if (isRepeatedActivation(event)) {\n event.preventDefault();\n return;\n }\n if (!isDismissKey(event)) return;\n event.preventDefault();\n event.currentTarget.click();\n };\n\n return (\n <button\n type=\"button\"\n data-wave-tag-dismiss=\"\"\n data-selected={tag.selected ? '' : undefined}\n {...rest}\n ref={mergedRef}\n aria-label={ariaLabel}\n aria-labelledby={labelledBy}\n onClick={composeEventHandlers(onClick, handleClick)}\n {...buttonStateProps(disabled, hold.held)}\n onKeyDown={composeEventHandlers(onKeyDown, handleKeyDown)}\n onFocus={composeEventHandlers(onFocus, hold.onFocus, { checkDefaultPrevented: false })}\n onBlur={composeEventHandlers(onBlur, hold.onBlur, { checkDefaultPrevented: false })}\n className={cn(\n interactionTagSecondaryClassNames.root,\n INTERACTION_TAG_BUTTON_BASE_CLASSES,\n INTERACTION_TAG_SECONDARY_BASE_CLASSES,\n interactionTagSecondarySizeClasses[size],\n interactionTagLookClasses[look],\n look === 'selected' && [\n forcedColors.selectedContainer,\n INTERACTION_TAG_SELECTED_FORCED_COLORS,\n ],\n interactionTagDividerClasses[look],\n interactionTagSecondaryShapeClasses[tag.shape],\n disabledClasses(disabled, tag.dimDisabled),\n focusRing,\n size === 'extra-small' && INTERACTION_TAG_EXTRA_SMALL_HIT_CLASSES,\n className,\n )}\n >\n {!namedByConsumer && (\n <span id={dismissLabelId} className=\"sr-only\">\n {dismissLabel}\n </span>\n )}\n {hasContent ? (\n <span\n id={contentId}\n ref={setLabelTarget}\n className={cn(\n interactionTagSecondaryClassNames.dismissIcon,\n 'inline-flex items-center justify-center',\n // A text label keeps clear of the divider and the tag's end; a glyph needs no room.\n hasLabel && INTERACTION_TAG_SECONDARY_LABEL_CLASSES,\n )}\n >\n {content}\n </span>\n ) : (\n <DismissIcon\n size={TAG_DISMISS_GLYPH_SIZE[size]}\n className={interactionTagSecondaryClassNames.dismissIcon}\n />\n )}\n </button>\n );\n};\nInteractionTagSecondary.displayName = 'InteractionTagSecondary';\n\n// ---------------------------------------------------------------------------\n// Compound\n// ---------------------------------------------------------------------------\n\n/**\n * A tag with a main action and a separate Dismiss button: an element that holds two sibling\n * buttons, `InteractionTag.Primary` (the action) and `InteractionTag.Secondary` (Dismiss), never\n * one inside the other. Use it for a chip the user acts on (opens, toggles) and can also remove;\n * a `Tag` is a chip that only displays, or only dismisses.\n *\n * - **Two Tab stops**, the Primary then the Secondary. In a TagGroup toolbar both join its arrow\n * order (Primary, Secondary, next tag).\n * - **The Secondary is named after the Primary's text**: its `dismissLabel` plus the primary\n * text, \"Dismiss Cherry\" (never the secondary text). A text label in its children replaces\n * `dismissLabel` (\"Remove Cherry\"); an `aria-label` replaces the whole name.\n * - **Delete and Backspace** on either button dismiss the tag through the Secondary's click (a\n * held key dismisses once); without a Secondary they do nothing. Enter, Space and a click on the\n * Secondary dismiss too: remove the tag in its `onClick`, or in a TagGroup's `onDismiss(value)`,\n * which is called after it.\n * - **In a selecting TagGroup** (`value`, `defaultValue` or `onValueChange` on the group) the\n * Primary toggles the tag's `value` in the group's selection (`aria-pressed`, the selected\n * look), and dismissing a selected tag also removes its value from the selection.\n * - **`selected`**, outside a selecting TagGroup, draws the selected look and makes the Primary a\n * toggle (`aria-pressed`); leave it `undefined` for an action that is not a toggle.\n * - **Look**: `size`, `appearance` and `shape`, Tag's sizes and colours (a TagGroup gives its\n * defaults); `disabled` disables both buttons natively. The Primary takes `media` (an Avatar,\n * sized for the tag), `icon` and `secondaryText`.\n * - Fluent's `hasSecondaryAction` is not needed: the Primary squares its end corners next to a\n * Secondary by itself, also when the Secondary is rendered conditionally or wrapped.\n * - React Server Components cannot dot into a client module: import the flat names\n * `InteractionTagPrimary` and `InteractionTagSecondary` there.\n * - Renders `data-appearance`, `data-size` and `data-shape`, and while set `data-selected` and\n * `data-disabled`, on the root; `data-selected` on both buttons.\n *\n * @example\n * <InteractionTag value=\"cherry\">\n * <InteractionTag.Primary icon={<FruitIcon />} onClick={openDetails}>\n * Cherry\n * </InteractionTag.Primary>\n * <InteractionTag.Secondary onClick={() => remove('cherry')} />\n * </InteractionTag>\n */\nexport const InteractionTag = /* @__PURE__ */ Object.assign(InteractionTagRoot, {\n Primary: InteractionTagPrimary,\n Secondary: InteractionTagSecondary,\n});\n"],"mappings":"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"}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn as e } from "../../lib/cn.mjs";
|
|
3
|
+
import { reportMissingContext as t, warnOnce as n } from "../../lib/dev.mjs";
|
|
4
|
+
import { materialiseSlotContent as r, renderSlot as i, slotRendersContent as a } from "../../lib/slot.mjs";
|
|
5
|
+
import { composeEventHandlers as o } from "../../lib/composeEventHandlers.mjs";
|
|
6
|
+
import { interactionTagClassNames as s, interactionTagPrimaryClassNames as c, interactionTagSecondaryClassNames as l } from "../../lib/classNames.mjs";
|
|
7
|
+
import { useEventCallback as u } from "../../hooks/useEventCallback.mjs";
|
|
8
|
+
import { useId as d } from "../../hooks/useId.mjs";
|
|
9
|
+
import { useMergedRefs as f } from "../../hooks/useMergedRefs.mjs";
|
|
10
|
+
import { joinIds as ee } from "../../lib/aria.mjs";
|
|
11
|
+
import { focusRing as te, forcedColors as p } from "../../lib/styles.mjs";
|
|
12
|
+
import { MERGED_DISABLED_FOCUSABLE_PROPS as m, mergedAriaDisabledClasses as h, unwrapButtonGlyph as g } from "../button/Button.slots.mjs";
|
|
13
|
+
import { DismissIcon as _ } from "../../lib/icons.mjs";
|
|
14
|
+
import { hasRenderedTextLabel as v, hasTextLabel as y, observeTextLabel as b } from "../../lib/labelInName.mjs";
|
|
15
|
+
import { isOwnEvent as x } from "../../lib/events.mjs";
|
|
16
|
+
import { isDismissKey as S, isRepeatedActivation as C, useDismissControlFocus as w, useHeldFocus as T, useTagGroupContext as E, useTagGroupItem as D } from "./TagGroup.context.mjs";
|
|
17
|
+
import { AvatarSizeContext as O } from "./Avatar.context.mjs";
|
|
18
|
+
import { INTERACTION_TAG_BUTTON_BASE_CLASSES as k, INTERACTION_TAG_DISABLED_CLASSES as A, INTERACTION_TAG_PRIMARY_BASE_CLASSES as j, INTERACTION_TAG_PRIMARY_JOINED_CLASSES as M, INTERACTION_TAG_SECONDARY_BASE_CLASSES as N, INTERACTION_TAG_UNDIMMED_DISABLED_CLASSES as P, TAG_AVATAR_SIZE as F, TAG_DISMISS_GLYPH_SIZE as I, TAG_SECONDARY_TEXT_CLASSES as L, TAG_TEXT_COLUMN_CLASSES as R, interactionTagDividerClasses as z, interactionTagLookClasses as ne, interactionTagPrimaryEndInsetClasses as B, interactionTagSecondaryShapeClasses as re, interactionTagSecondarySizeClasses as ie, tagAppearanceClasses as V, tagIconClasses as ae, tagMediaClasses as oe, tagShapeClasses as se, tagSizeClasses as H, tagStartInsetClasses as U } from "./tagStyles.mjs";
|
|
19
|
+
import * as W from "react";
|
|
20
|
+
import { jsx as G, jsxs as K } from "react/jsx-runtime";
|
|
21
|
+
//#region src/components/data-display/InteractionTag.tsx
|
|
22
|
+
var q = () => {};
|
|
23
|
+
function J() {
|
|
24
|
+
let e = 0, t = !1, n = /* @__PURE__ */ new Set(), r = () => {
|
|
25
|
+
for (let e of n) e();
|
|
26
|
+
};
|
|
27
|
+
return {
|
|
28
|
+
subscribe: (e) => (n.add(e), () => {
|
|
29
|
+
n.delete(e);
|
|
30
|
+
}),
|
|
31
|
+
hasPrimary: () => !t || e > 0,
|
|
32
|
+
register: () => (e += 1, r(), () => {
|
|
33
|
+
--e, r();
|
|
34
|
+
}),
|
|
35
|
+
settle: () => {
|
|
36
|
+
t || (t = !0, r());
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
var ce = {
|
|
41
|
+
subscribe: () => q,
|
|
42
|
+
hasPrimary: () => !0,
|
|
43
|
+
register: () => q,
|
|
44
|
+
settle: q
|
|
45
|
+
}, Y = W.createContext(null);
|
|
46
|
+
Y.displayName = "InteractionTagContext";
|
|
47
|
+
var le = {
|
|
48
|
+
size: "medium",
|
|
49
|
+
shape: "circular",
|
|
50
|
+
look: "filled",
|
|
51
|
+
disabled: !1,
|
|
52
|
+
selected: !1,
|
|
53
|
+
pressed: void 0,
|
|
54
|
+
dimDisabled: !0,
|
|
55
|
+
primaryTextId: "",
|
|
56
|
+
primaries: ce,
|
|
57
|
+
secondaryRef: null,
|
|
58
|
+
dismiss: () => {},
|
|
59
|
+
toggle: () => {}
|
|
60
|
+
};
|
|
61
|
+
function ue(e) {
|
|
62
|
+
return W.useContext(Y) || (t(e, "InteractionTag"), le);
|
|
63
|
+
}
|
|
64
|
+
function X(e, t) {
|
|
65
|
+
return typeof e == "string" && Object.hasOwn(t, e);
|
|
66
|
+
}
|
|
67
|
+
function de(t, n) {
|
|
68
|
+
return e(n ? [A, h] : P, t && p.disabled);
|
|
69
|
+
}
|
|
70
|
+
var fe = "InteractionTag: `selected` has no effect in a TagGroup that selects (`value`, `defaultValue` or `onValueChange`); the group's `value` decides.";
|
|
71
|
+
function pe(e, t) {
|
|
72
|
+
if (e) return t ? m : { disabled: !0 };
|
|
73
|
+
}
|
|
74
|
+
var Z = ({ value: t, appearance: r, size: i, shape: a, disabled: o = !1, selected: c, role: l, hidden: ee, className: te, children: p, ref: m, ...h }) => {
|
|
75
|
+
let g = E(), _ = W.useRef(null), v = f(_, m), y = W.useRef(null), b = d("interaction-tag-text"), [x] = W.useState(J);
|
|
76
|
+
W.useLayoutEffect(() => {
|
|
77
|
+
x.settle();
|
|
78
|
+
}, [x]);
|
|
79
|
+
let S = X(i, H) ? i : g?.size ?? "medium", C = X(r, V) ? r : g?.appearance ?? "filled", w = X(a, se) ? a : g?.shape ?? "circular", T = (g?.disabled ?? !1) || o, O = g?.dimDisabled ?? !0, k = D(_, t), A = g?.selecting ?? !1, j = A ? g?.isSelected(k) ?? !1 : typeof c == "boolean" ? c : void 0, M = j === !0, N = M && !T ? "selected" : C, P = A && c !== void 0;
|
|
80
|
+
W.useEffect(() => {
|
|
81
|
+
P && n("InteractionTag:selected-in-group", fe);
|
|
82
|
+
}, [P]);
|
|
83
|
+
let F = u(() => {
|
|
84
|
+
g?.dismiss(k), g?.deselect(k);
|
|
85
|
+
}), I = u(() => {
|
|
86
|
+
g?.toggle(k);
|
|
87
|
+
}), L = g?.mode === "list" ? "listitem" : void 0, R = t !== void 0 && (g?.hiddenValues?.has(t) ?? !1), z = W.useMemo(() => ({
|
|
88
|
+
size: S,
|
|
89
|
+
shape: w,
|
|
90
|
+
look: N,
|
|
91
|
+
disabled: T,
|
|
92
|
+
selected: M,
|
|
93
|
+
pressed: j,
|
|
94
|
+
dimDisabled: O,
|
|
95
|
+
primaryTextId: b,
|
|
96
|
+
primaries: x,
|
|
97
|
+
secondaryRef: y,
|
|
98
|
+
dismiss: F,
|
|
99
|
+
toggle: I
|
|
100
|
+
}), [
|
|
101
|
+
S,
|
|
102
|
+
w,
|
|
103
|
+
N,
|
|
104
|
+
T,
|
|
105
|
+
M,
|
|
106
|
+
j,
|
|
107
|
+
O,
|
|
108
|
+
b,
|
|
109
|
+
x,
|
|
110
|
+
F,
|
|
111
|
+
I
|
|
112
|
+
]);
|
|
113
|
+
return /* @__PURE__ */ G(Y.Provider, {
|
|
114
|
+
value: z,
|
|
115
|
+
children: /* @__PURE__ */ G("div", {
|
|
116
|
+
"data-appearance": C,
|
|
117
|
+
"data-size": S,
|
|
118
|
+
"data-shape": w,
|
|
119
|
+
"data-selected": M ? "" : void 0,
|
|
120
|
+
"data-disabled": T ? "" : void 0,
|
|
121
|
+
"data-wave-tag-item": "",
|
|
122
|
+
role: l ?? L,
|
|
123
|
+
hidden: R || ee,
|
|
124
|
+
...h,
|
|
125
|
+
ref: v,
|
|
126
|
+
className: e(s.root, "group/interaction-tag inline-flex items-stretch", te),
|
|
127
|
+
children: p
|
|
128
|
+
})
|
|
129
|
+
});
|
|
130
|
+
};
|
|
131
|
+
Z.displayName = "InteractionTag";
|
|
132
|
+
var Q = ({ media: t, icon: n, secondaryText: s, disabled: l = !1, "aria-pressed": u, className: d, children: f, onClick: ee, onKeyDown: m, onFocus: h, onBlur: g, ref: _, ...v }) => {
|
|
133
|
+
let y = ue("InteractionTag.Primary"), { size: b, look: C } = y, w = y.disabled || l, E = T(w), D = y.primaries.register;
|
|
134
|
+
W.useLayoutEffect(() => D(), [D]);
|
|
135
|
+
let A = a(t), N = a(n), P = r(s), I = a(P), z = W.useMemo(() => ({ size: F[b] }), [b]), re = (e) => {
|
|
136
|
+
!w && x(e) && y.toggle();
|
|
137
|
+
}, ie = (e) => {
|
|
138
|
+
if (w || e.target !== e.currentTarget || !S(e)) return;
|
|
139
|
+
let t = y.secondaryRef?.current;
|
|
140
|
+
t && !t.disabled && (e.preventDefault(), t.click());
|
|
141
|
+
}, V = /* @__PURE__ */ G("span", {
|
|
142
|
+
id: y.primaryTextId || void 0,
|
|
143
|
+
className: e(c.primaryText, I && "text-caption-1 leading-3.5"),
|
|
144
|
+
children: f
|
|
145
|
+
});
|
|
146
|
+
return /* @__PURE__ */ K("button", {
|
|
147
|
+
type: "button",
|
|
148
|
+
"aria-pressed": y.pressed ?? u,
|
|
149
|
+
"data-selected": y.selected ? "" : void 0,
|
|
150
|
+
...v,
|
|
151
|
+
ref: _,
|
|
152
|
+
onClick: o(ee, re),
|
|
153
|
+
...pe(w, E.held),
|
|
154
|
+
onKeyDown: o(m, ie),
|
|
155
|
+
onFocus: o(h, E.onFocus, { checkDefaultPrevented: !1 }),
|
|
156
|
+
onBlur: o(g, E.onBlur, { checkDefaultPrevented: !1 }),
|
|
157
|
+
className: e(c.root, k, j, H[b], U[b][A ? "media" : N ? "icon" : "text"], B[b], ne[C], C === "selected" && [p.selectedContainer, "forced-colors:border-[Highlight]"], se[y.shape], M, de(w, y.dimDisabled), te, b === "extra-small" && "before:absolute before:inset-x-0 before:-inset-y-0.5", d),
|
|
158
|
+
children: [
|
|
159
|
+
A && /* @__PURE__ */ G(O.Provider, {
|
|
160
|
+
value: z,
|
|
161
|
+
children: i(t, "span", e(c.media, oe[b]), { "aria-hidden": !0 })
|
|
162
|
+
}),
|
|
163
|
+
N && i(n, "span", e(c.icon, ae[b]), { "aria-hidden": !0 }),
|
|
164
|
+
I ? /* @__PURE__ */ K("span", {
|
|
165
|
+
className: R,
|
|
166
|
+
children: [
|
|
167
|
+
V,
|
|
168
|
+
" ",
|
|
169
|
+
/* @__PURE__ */ G("span", {
|
|
170
|
+
className: e(c.secondaryText, L),
|
|
171
|
+
children: P
|
|
172
|
+
})
|
|
173
|
+
]
|
|
174
|
+
}) : V
|
|
175
|
+
]
|
|
176
|
+
});
|
|
177
|
+
};
|
|
178
|
+
Q.displayName = "InteractionTagPrimary";
|
|
179
|
+
var me = "InteractionTag.Secondary: its content received a button. A button cannot contain a button: its children became the content of the Dismiss button and its props were dropped. Pass icon content, and handle the dismissal in `onClick` or in the TagGroup's `onDismiss`.", $ = ({ dismissLabel: t = "Dismiss", disabled: i = !1, "aria-label": s, "aria-labelledby": c, className: u, children: m, onClick: h, onKeyDown: E, onFocus: D, onBlur: O, ref: A, ...j }) => {
|
|
180
|
+
let M = ue("InteractionTag.Secondary"), { size: P, look: F } = M, L = M.disabled || i, R = T(L), B = d("interaction-tag-dismiss"), V = d("interaction-tag-dismiss-content"), ae = W.useRef(null), oe = f(ae, M.secondaryRef, A);
|
|
181
|
+
w(ae);
|
|
182
|
+
let { glyph: se, button: H } = g(m);
|
|
183
|
+
W.useEffect(() => {
|
|
184
|
+
H && n("InteractionTag.Secondary:button-child", me);
|
|
185
|
+
}, [H]);
|
|
186
|
+
let U = r(se), J = a(U), ce = J && y(U), [Y, le] = W.useState(null), X = W.useCallback((e) => Y ? b(Y, e) : q, [Y]), fe = W.useSyncExternalStore(X, () => Y ? v(Y) : ce, () => ce), Z = J && fe, Q = W.useSyncExternalStore(M.primaries.subscribe, M.primaries.hasPrimary, () => !0), $ = s !== void 0 || c !== void 0, he = $ ? c : ee(Z ? V : B, Q ? M.primaryTextId : void 0), ge = (e) => {
|
|
187
|
+
x(e) && M.dismiss();
|
|
188
|
+
}, _e = (e) => {
|
|
189
|
+
if (e.target === e.currentTarget) {
|
|
190
|
+
if (C(e)) {
|
|
191
|
+
e.preventDefault();
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
S(e) && (e.preventDefault(), e.currentTarget.click());
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
return /* @__PURE__ */ K("button", {
|
|
198
|
+
type: "button",
|
|
199
|
+
"data-wave-tag-dismiss": "",
|
|
200
|
+
"data-selected": M.selected ? "" : void 0,
|
|
201
|
+
...j,
|
|
202
|
+
ref: oe,
|
|
203
|
+
"aria-label": s,
|
|
204
|
+
"aria-labelledby": he,
|
|
205
|
+
onClick: o(h, ge),
|
|
206
|
+
...pe(L, R.held),
|
|
207
|
+
onKeyDown: o(E, _e),
|
|
208
|
+
onFocus: o(D, R.onFocus, { checkDefaultPrevented: !1 }),
|
|
209
|
+
onBlur: o(O, R.onBlur, { checkDefaultPrevented: !1 }),
|
|
210
|
+
className: e(l.root, k, N, ie[P], ne[F], F === "selected" && [p.selectedContainer, "forced-colors:border-[Highlight]"], z[F], re[M.shape], de(L, M.dimDisabled), te, P === "extra-small" && "before:absolute before:inset-x-0 before:-inset-y-0.5", u),
|
|
211
|
+
children: [!$ && /* @__PURE__ */ G("span", {
|
|
212
|
+
id: B,
|
|
213
|
+
className: "sr-only",
|
|
214
|
+
children: t
|
|
215
|
+
}), J ? /* @__PURE__ */ G("span", {
|
|
216
|
+
id: V,
|
|
217
|
+
ref: le,
|
|
218
|
+
className: e(l.dismissIcon, "inline-flex items-center justify-center", Z && "px-1.5"),
|
|
219
|
+
children: U
|
|
220
|
+
}) : /* @__PURE__ */ G(_, {
|
|
221
|
+
size: I[P],
|
|
222
|
+
className: l.dismissIcon
|
|
223
|
+
})]
|
|
224
|
+
});
|
|
225
|
+
};
|
|
226
|
+
$.displayName = "InteractionTagSecondary";
|
|
227
|
+
var he = /* @__PURE__ */ Object.assign(Z, {
|
|
228
|
+
Primary: Q,
|
|
229
|
+
Secondary: $
|
|
230
|
+
});
|
|
231
|
+
//#endregion
|
|
232
|
+
export { he as InteractionTag, Q as InteractionTagPrimary, $ as InteractionTagSecondary };
|
|
233
|
+
|
|
234
|
+
//# sourceMappingURL=InteractionTag.mjs.map
|