@mortenbrudvik/waveui 0.9.0 → 0.11.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 +758 -152
- 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/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 +213 -181
- package/dist/components/feedback/Toaster.mjs.map +1 -1
- package/dist/components/input/Calendar.cjs +2 -0
- package/dist/components/input/Calendar.cjs.map +1 -0
- package/dist/components/input/Calendar.mjs +352 -0
- package/dist/components/input/Calendar.mjs.map +1 -0
- package/dist/components/input/Calendar.views.cjs +2 -0
- package/dist/components/input/Calendar.views.cjs.map +1 -0
- package/dist/components/input/Calendar.views.mjs +194 -0
- package/dist/components/input/Calendar.views.mjs.map +1 -0
- package/dist/components/input/ColorArea.cjs +1 -1
- package/dist/components/input/ColorArea.cjs.map +1 -1
- package/dist/components/input/ColorArea.mjs +18 -18
- package/dist/components/input/ColorArea.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 +232 -156
- package/dist/components/input/Combobox.mjs.map +1 -1
- package/dist/components/input/Combobox.text.cjs +2 -0
- package/dist/components/input/Combobox.text.cjs.map +1 -0
- package/dist/components/input/Combobox.text.mjs +24 -0
- package/dist/components/input/Combobox.text.mjs.map +1 -0
- package/dist/components/input/DatePicker.cjs +1 -1
- package/dist/components/input/DatePicker.cjs.map +1 -1
- package/dist/components/input/DatePicker.mjs +203 -288
- 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 +135 -109
- package/dist/components/input/Dropdown.mjs.map +1 -1
- package/dist/components/input/Listbox.cjs +2 -0
- package/dist/components/input/Listbox.cjs.map +1 -0
- package/dist/components/input/Listbox.mjs +134 -0
- package/dist/components/input/Listbox.mjs.map +1 -0
- package/dist/components/input/Option.cjs +1 -1
- package/dist/components/input/Option.cjs.map +1 -1
- package/dist/components/input/Option.mjs +163 -97
- package/dist/components/input/Option.mjs.map +1 -1
- package/dist/components/input/SwatchPicker.cjs +1 -1
- package/dist/components/input/SwatchPicker.cjs.map +1 -1
- package/dist/components/input/SwatchPicker.context.cjs +2 -0
- package/dist/components/input/SwatchPicker.context.cjs.map +1 -0
- package/dist/components/input/SwatchPicker.context.mjs +35 -0
- package/dist/components/input/SwatchPicker.context.mjs.map +1 -0
- package/dist/components/input/SwatchPicker.mjs +131 -103
- package/dist/components/input/SwatchPicker.mjs.map +1 -1
- package/dist/components/input/SwatchPicker.swatches.cjs +2 -0
- package/dist/components/input/SwatchPicker.swatches.cjs.map +1 -0
- package/dist/components/input/SwatchPicker.swatches.mjs +207 -0
- package/dist/components/input/SwatchPicker.swatches.mjs.map +1 -0
- 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 -274
- 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 +416 -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 +77 -77
- package/dist/components/input/TimePicker.mjs.map +1 -1
- package/dist/components/input/dateUtils.cjs +1 -1
- package/dist/components/input/dateUtils.cjs.map +1 -1
- package/dist/components/input/dateUtils.mjs +114 -96
- package/dist/components/input/dateUtils.mjs.map +1 -1
- package/dist/components/input/pickerLabels.cjs +2 -0
- package/dist/components/input/pickerLabels.cjs.map +1 -0
- package/dist/components/input/pickerLabels.mjs +16 -0
- package/dist/components/input/pickerLabels.mjs.map +1 -0
- package/dist/components/input/pickerValues.cjs +2 -0
- package/dist/components/input/pickerValues.cjs.map +1 -0
- package/dist/components/input/pickerValues.mjs +24 -0
- package/dist/components/input/pickerValues.mjs.map +1 -0
- 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/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 +190 -125
- 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/table/useGridNavigation.cjs +1 -1
- package/dist/components/table/useGridNavigation.cjs.map +1 -1
- package/dist/components/table/useGridNavigation.mjs +67 -70
- package/dist/components/table/useGridNavigation.mjs.map +1 -1
- package/dist/hooks/useActiveDescendant.cjs +2 -0
- package/dist/hooks/useActiveDescendant.cjs.map +1 -0
- package/dist/hooks/useActiveDescendant.mjs +56 -0
- package/dist/hooks/useActiveDescendant.mjs.map +1 -0
- 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/useListbox.cjs +1 -1
- package/dist/hooks/useListbox.cjs.map +1 -1
- package/dist/hooks/useListbox.mjs +250 -210
- package/dist/hooks/useListbox.mjs.map +1 -1
- package/dist/hooks/useModalLayer.cjs +1 -1
- package/dist/hooks/useModalLayer.cjs.map +1 -1
- package/dist/hooks/useModalLayer.mjs +35 -22
- package/dist/hooks/useModalLayer.mjs.map +1 -1
- 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 +2 -0
- package/dist/hooks/useRovingGrid.cjs.map +1 -0
- package/dist/hooks/useRovingGrid.mjs +69 -0
- package/dist/hooks/useRovingGrid.mjs.map +1 -0
- package/dist/hooks/useRovingTabIndex.cjs +1 -1
- package/dist/hooks/useRovingTabIndex.cjs.map +1 -1
- package/dist/hooks/useRovingTabIndex.mjs +102 -78
- package/dist/hooks/useRovingTabIndex.mjs.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +4391 -394
- package/dist/index.d.ts +4391 -394
- package/dist/index.mjs +115 -101
- package/dist/lib/classNames.cjs +1 -1
- package/dist/lib/classNames.cjs.map +1 -1
- package/dist/lib/classNames.mjs +170 -74
- package/dist/lib/classNames.mjs.map +1 -1
- package/dist/lib/date.cjs +2 -0
- package/dist/lib/date.cjs.map +1 -0
- package/dist/lib/date.mjs +44 -0
- package/dist/lib/date.mjs.map +1 -0
- package/dist/lib/focus.cjs +1 -1
- package/dist/lib/focus.cjs.map +1 -1
- package/dist/lib/focus.mjs +5 -2
- package/dist/lib/focus.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 +53 -47
- 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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SwatchPicker.swatches.cjs","names":["CheckIcon","cn","unwrapButtonGlyph","useSwatchPickerContext","motionSafeTransition","focusRing","composeEventHandlers","swatchPickerClassNames","forcedColors","slotRendersContent","renderSlot","colorSwatchClassNames","getCheckColors","imageSwatchClassNames","useId","AddIcon","emptySwatchClassNames","require_runtime","require_cn","require_dev","require_slot","require_composeEventHandlers","require_classNames","require_useId","require_styles","require_Button_slots","require_icons","require_SwatchPicker_context","require_colorUtils","react","react_jsx_runtime","sizeMap","glyphSizeMap","shapeMap","SwatchCheck","colors","SwatchSlash","warnUnnamedSwatch","kind","value","fallback","useSwatchIcon","icon","useSwatchIconWarning","button","glyph","useSwatchContext","context","size","shape","outlineOffset","spacing","SwatchGridCell","selected","swatchBaseClasses","SwatchRadio","sizeProp","shapeProp","ariaLabel","ariaLabelledBy","rest","selectedValue","isSelected","isTab","focusMode","isGrid","layout","register","named","fallbackKind","defaultName","getTabIndex","onClick","select","rootClassName","paintClassName","keepsColors","className","style","paint","checkColors","iconClassName","disabled","ColorSwatch","color","borderColor","ImageSwatch","src","EmptySwatch","cellValue","content","control"],"sources":["../../../src/components/input/SwatchPicker.swatches.tsx"],"sourcesContent":["import * as React from 'react';\nimport {\n colorSwatchClassNames,\n emptySwatchClassNames,\n imageSwatchClassNames,\n swatchPickerClassNames,\n} from '../../lib/classNames';\nimport { cn } from '../../lib/cn';\nimport { composeEventHandlers } from '../../lib/composeEventHandlers';\nimport { warnOnce } from '../../lib/dev';\nimport { AddIcon, CheckIcon } from '../../lib/icons';\nimport { renderSlot, slotRendersContent } from '../../lib/slot';\nimport type { Slot } from '../../lib/slot';\nimport { focusRing, forcedColors, motionSafeTransition } from '../../lib/styles';\nimport type { Shape } from '../../lib/types';\nimport { useId } from '../../hooks/useId';\nimport { unwrapButtonGlyph, type UnwrappedButton } from '../button/Button.slots';\nimport { useSwatchPickerContext, type SwatchPickerSize } from './SwatchPicker.context';\nimport { getCheckColors, type CheckColors } from './colorUtils';\n\n/*\n * The swatch kinds a `SwatchPicker` renders `items` through and a consumer can render as children\n * (D18, D21): `ColorSwatch`, `ImageSwatch` and `EmptySwatch`. Every swatch reads\n * `SwatchPickerContext` and puts its `ref`, `className`, `style`, name and rest props on its own\n * `<button>` (an explicit exception to C-ROUTING: there is no separate root, so a wrapping\n * Tooltip's ARIA and handlers reach the focused element without extra wiring).\n */\n\nconst sizeMap: Record<SwatchPickerSize, string> = {\n 'extra-small': 'w-5 h-5',\n small: 'w-6 h-6',\n medium: 'w-8 h-8',\n large: 'w-10 h-10',\n};\n\nconst glyphSizeMap: Record<SwatchPickerSize, number> = {\n 'extra-small': 10,\n small: 12,\n medium: 16,\n large: 20,\n};\n\nconst shapeMap: Record<Shape, string> = {\n circular: 'rounded-full',\n square: 'rounded-none',\n rounded: 'rounded',\n};\n\n/**\n * The check glyph of the selected swatch: black or white by the swatch's luminance, drawn over a\n * halo in the other color so it stays visible on any swatch (C-TOKENS exception for\n * user-supplied colors, `input-pickers#16`). Without `colors` (an image has no single luminance)\n * it is the theme's foreground glyph over a background halo.\n */\nfunction SwatchCheck({ colors, size }: { colors: CheckColors | null; size: number }) {\n return (\n <span aria-hidden=\"true\" className=\"pointer-events-none grid\">\n <CheckIcon\n size={size}\n strokeWidth={4}\n className={cn('col-start-1 row-start-1', !colors && 'text-background')}\n style={colors ? { color: colors.halo } : undefined}\n />\n <CheckIcon\n size={size}\n strokeWidth={2}\n className={cn('col-start-1 row-start-1', !colors && 'text-foreground')}\n style={colors ? { color: colors.glyph } : undefined}\n />\n </span>\n );\n}\n\n/**\n * The diagonal slash of a disabled swatch, drawn like the check glyph (a halo line under a glyph\n * line), so the state does not rely on the swatch color or its opacity.\n */\nfunction SwatchSlash({ colors }: { colors: CheckColors | null }) {\n return (\n <svg\n data-wave-swatch-slash=\"\"\n aria-hidden=\"true\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n className=\"pointer-events-none absolute inset-0 h-full w-full\"\n >\n <line\n x1=\"5\"\n y1=\"19\"\n x2=\"19\"\n y2=\"5\"\n stroke=\"currentColor\"\n strokeLinecap=\"round\"\n strokeWidth={4}\n className={colors ? undefined : 'text-background'}\n style={colors ? { color: colors.halo } : undefined}\n />\n <line\n x1=\"5\"\n y1=\"19\"\n x2=\"19\"\n y2=\"5\"\n stroke=\"currentColor\"\n strokeLinecap=\"round\"\n strokeWidth={2}\n className={colors ? undefined : 'text-foreground'}\n style={colors ? { color: colors.glyph } : undefined}\n />\n </svg>\n );\n}\n\n/** Warns once per swatch kind (not per value: `<Kind>:unnamed`, e.g. `ColorSwatch:unnamed`). */\nfunction warnUnnamedSwatch(kind: string, value: string, fallback: string): void {\n warnOnce(\n `${kind}:unnamed`,\n `${kind}: swatch \"${value}\" has no accessible name, so it is announced by its ${fallback}. ` +\n 'Pass `aria-label`, or wrap it in a Tooltip with `relationship=\"label\"`.',\n );\n}\n\n/**\n * A swatch's `icon` as a glyph inside its wired button (C-SLOTS): a `<button>` or `Button` is\n * unwrapped (its children become the glyph) with a one-time warning (`<Kind>:icon-button`).\n */\nfunction useSwatchIcon(kind: string, icon: Slot<'span'> | undefined): Slot<'span'> | undefined {\n const { glyph, button } = unwrapButtonGlyph(icon);\n useSwatchIconWarning(kind, button);\n return glyph;\n}\n\n/** The one-time `<Kind>:icon-button` warning for an unwrapped button. */\nfunction useSwatchIconWarning(kind: string, button: UnwrappedButton | null): void {\n React.useEffect(() => {\n if (button) {\n warnOnce(\n `${kind}:icon-button`,\n `${kind}: \\`icon\\` received ${button}; its children render as the glyph of the swatch ` +\n 'and its props were dropped (buttons cannot be nested). Pass icon content instead, ' +\n 'e.g. `icon={<MyIcon />}`.',\n );\n }\n }, [button, kind]);\n}\n\n/** The picker's context with this swatch's own `size` and `shape` applied over the picker's. */\nfunction useSwatchContext(kind: string, size?: SwatchPickerSize, shape?: Shape) {\n const context = useSwatchPickerContext(kind);\n return { ...context, size: size ?? context.size, shape: shape ?? context.shape };\n}\n\n/**\n * Focus outline offset: 4px, clear of a selected swatch's ring band (ring and offset, 4px); 2px at\n * `spacing=\"small\"` so it does not cover the neighbour (D22), where it lies over that band.\n */\nfunction outlineOffset(spacing: 'small' | 'medium'): string {\n return spacing === 'small' ? 'focus-visible:outline-offset-2' : 'focus-visible:outline-offset-4';\n}\n\n/**\n * The cell around a swatch's button in the grid layout: it carries only the role and, for a swatch\n * with a value, `aria-selected` (a `gridcell` role on the button would replace its button role,\n * which `aria-pressed` needs). Every consumer prop stays on the button.\n */\nfunction SwatchGridCell({ selected, children }: { selected?: boolean; children: React.ReactNode }) {\n return (\n <div role=\"gridcell\" aria-selected={selected} className=\"flex shrink-0\">\n {children}\n </div>\n );\n}\n\n/** The classes every swatch kind shares: size, shape, box, motion, focus ring and disabled look. */\nfunction swatchBaseClasses(\n size: SwatchPickerSize,\n shape: Shape,\n spacing: 'small' | 'medium',\n): string {\n return cn(\n sizeMap[size],\n shapeMap[shape],\n // The padding is set here (C-NATIVE).\n 'relative flex shrink-0 items-center justify-center border-2 p-0',\n motionSafeTransition,\n focusRing,\n outlineOffset(spacing),\n 'disabled:cursor-not-allowed disabled:opacity-50',\n );\n}\n\n/** Properties for the ColorSwatch component. */\nexport interface ColorSwatchProps extends Omit<\n React.ButtonHTMLAttributes<HTMLButtonElement>,\n 'value' | 'color'\n> {\n /** Unique identifier for the swatch (the value the picker selects and a form submits). */\n value: string;\n /** CSS color value (e.g., hex, rgb) used as the swatch background (a runtime user color). */\n color: string;\n /**\n * Decorative glyph (`aria-hidden`) shown while the swatch is not selected; the check replaces it\n * while selected. A `<button>` or `Button` passed here is not nested: its children become the\n * glyph and a one-time development warning is logged.\n */\n icon?: Slot<'span'>;\n /**\n * CSS color of the swatch's border, for a color close to the page background (set by hand, as\n * in Fluent). In forced colors the system border color replaces it.\n */\n borderColor?: string;\n /**\n * Size of the swatch (20, 24, 32 or 40px).\n * @default the picker's\n */\n size?: SwatchPickerSize;\n /**\n * Shape of the swatch.\n * @default the picker's\n */\n shape?: Shape;\n /**\n * Ref to the swatch button: `role=\"radio\"` in the row layout, inside its cell in the grid\n * layout, a toggle button in tab mode.\n */\n ref?: React.Ref<HTMLButtonElement>;\n}\n\n/** What `ColorSwatch` and `ImageSwatch` do differently. */\ninterface SwatchRadioProps extends Omit<\n React.ButtonHTMLAttributes<HTMLButtonElement>,\n 'value' | 'color'\n> {\n kind: 'ColorSwatch' | 'ImageSwatch';\n /** The kind's stable root class, named by the module that sets its `displayName` (P3-00). */\n rootClassName: string;\n /** The kind's stable `icon` part class (ColorSwatch only). */\n iconClassName?: string;\n value: string;\n /** What names the swatch when it has no name: 'color value' or 'value' (the warning's text). */\n fallbackKind: string;\n /** The accessible name of an unnamed swatch. */\n defaultName: string;\n /** The luminance-picked glyph colors; `null`: the theme's foreground over a background halo. */\n checkColors: CheckColors | null;\n /** The swatch's own background, border color and image (they win over a consumer `style`). */\n paint: Record<string, string>;\n /** Classes of the image swatch's covering background. */\n paintClassName?: string;\n /** A color swatch keeps its colors in forced colors (the color is the content). */\n keepsColors: boolean;\n icon?: Slot<'span'>;\n size?: SwatchPickerSize;\n shape?: Shape;\n ref?: React.Ref<HTMLButtonElement>;\n}\n\n/** The radio button shared by `ColorSwatch` and `ImageSwatch`. */\nconst SwatchRadio = ({\n kind,\n rootClassName,\n iconClassName,\n value,\n fallbackKind,\n defaultName,\n checkColors,\n paint,\n paintClassName,\n keepsColors,\n icon,\n size: sizeProp,\n shape: shapeProp,\n className,\n style,\n onClick,\n disabled,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n ref,\n ...rest\n}: SwatchRadioProps) => {\n const {\n value: selectedValue,\n select,\n size,\n shape,\n layout,\n focusMode,\n spacing,\n getTabIndex,\n register,\n } = useSwatchContext(kind, sizeProp, shapeProp);\n const isSelected = selectedValue === value;\n const isTab = focusMode === 'tab';\n const isGrid = layout === 'grid' && !isTab;\n const glyph = useSwatchIcon(kind, icon);\n\n React.useLayoutEffect(() => register(value), [register, value]);\n\n const named = ariaLabel !== undefined || ariaLabelledBy !== undefined;\n React.useEffect(() => {\n if (!named) warnUnnamedSwatch(kind, value, fallbackKind);\n }, [named, kind, value, fallbackKind]);\n\n const button = (\n <button\n type=\"button\"\n // The grid layout: a plain button inside its gridcell, pressed while selected (D20); tab\n // mode (D19): a toggle button of a group, `aria-pressed` always present.\n role={isGrid || isTab ? undefined : 'radio'}\n {...rest}\n ref={ref}\n disabled={disabled}\n aria-label={ariaLabel ?? (ariaLabelledBy ? undefined : defaultName)}\n aria-labelledby={ariaLabelledBy}\n aria-checked={isGrid || isTab ? undefined : isSelected}\n aria-pressed={isTab ? isSelected : isGrid ? isSelected || undefined : undefined}\n data-roving-value={value}\n data-selected={isSelected ? '' : undefined}\n tabIndex={getTabIndex(value)}\n onClick={composeEventHandlers(onClick, () => select(value))}\n className={cn(\n rootClassName,\n // Every swatch in a picker carries the picker's part class, children included (0.8).\n swatchPickerClassNames.swatch,\n swatchBaseClasses(size, shape, spacing),\n // A `borderColor` arrives as a variable, so the forced-colors edge below still wins.\n 'border-[color:var(--wave-swatch-border,transparent)]',\n paintClassName,\n // User colors are the content: keep them (and the check glyph) in forced colors, with a\n // Highlight outline, which an opted-out element would otherwise draw in author colors.\n keepsColors &&\n 'forced-colors:forced-color-adjust-none forced-colors:focus-visible:outline-[Highlight]',\n forcedColors.border,\n // An image stays in forced colors, so its selection needs a sign of its own.\n isSelected && !keepsColors && 'forced-colors:border-[Highlight]',\n isSelected\n ? cn(\n 'ring-2 ring-foreground ring-offset-2 ring-offset-background',\n keepsColors && 'forced-colors:ring-[Highlight]',\n )\n : 'not-disabled:not-aria-disabled:hover:border-stroke-hover',\n className,\n )}\n style={{ ...style, ...paint }}\n >\n {isSelected ? (\n <SwatchCheck colors={checkColors} size={glyphSizeMap[size]} />\n ) : (\n glyph != null &&\n slotRendersContent(glyph) &&\n renderSlot(glyph, 'span', cn(iconClassName, 'pointer-events-none inline-flex'), {\n 'aria-hidden': true,\n style: checkColors ? { color: checkColors.glyph } : undefined,\n })\n )}\n {disabled && <SwatchSlash colors={checkColors} />}\n </button>\n );\n return isGrid ? <SwatchGridCell selected={isSelected}>{button}</SwatchGridCell> : button;\n};\nSwatchRadio.displayName = 'SwatchRadio';\n\n// D18: swatches read SwatchPickerContext; their own ref/className/style/rest go straight to the\n// button (an explicit exception to C-ROUTING's root/control split).\n/**\n * One color swatch of a `SwatchPicker`, read from `items` or rendered directly as a child.\n * Outside a `SwatchPicker` it throws in development and renders inertly in production.\n *\n * **Naming**: give it `aria-label`, or wrap it in a `Tooltip` with `relationship=\"label\"` (its\n * `aria-labelledby` reaches this element, since every rest prop lands on the swatch's own\n * button). Without either it is named by its `color` value and a development warning is logged.\n *\n * **Disabled**: `disabled` is native (out of the arrow keys and the tab order, never selected);\n * the swatch is dimmed and crossed by a diagonal slash, so the state does not rely on its color.\n *\n * @example\n * <SwatchPicker aria-label=\"Accent color\">\n * <ColorSwatch value=\"red\" color=\"#d13438\" aria-label=\"Red\" />\n * </SwatchPicker>\n */\nexport const ColorSwatch = ({ color, borderColor, ...rest }: ColorSwatchProps) => {\n return (\n <SwatchRadio\n {...rest}\n kind=\"ColorSwatch\"\n rootClassName={colorSwatchClassNames.root}\n iconClassName={colorSwatchClassNames.icon}\n fallbackKind=\"color value\"\n defaultName={color}\n checkColors={getCheckColors(color)}\n keepsColors\n paint={{\n backgroundColor: color,\n ...(borderColor !== undefined && { '--wave-swatch-border': borderColor }),\n }}\n />\n );\n};\nColorSwatch.displayName = 'ColorSwatch';\n\n/** Properties for the ImageSwatch component. */\nexport interface ImageSwatchProps extends Omit<\n React.ButtonHTMLAttributes<HTMLButtonElement>,\n 'value'\n> {\n /** Unique identifier for the swatch (the value the picker selects and a form submits). */\n value: string;\n /** URL of the image (drawn as a covering background image). */\n src: string;\n /**\n * Size of the swatch (20, 24, 32 or 40px).\n * @default the picker's\n */\n size?: SwatchPickerSize;\n /**\n * Shape of the swatch.\n * @default the picker's\n */\n shape?: Shape;\n /**\n * Ref to the swatch button: `role=\"radio\"` in the row layout, inside its cell in the grid\n * layout, a toggle button in tab mode.\n */\n ref?: React.Ref<HTMLButtonElement>;\n}\n\n/**\n * One image swatch of a `SwatchPicker`: a pattern or texture drawn as a covering background\n * image. Outside a `SwatchPicker` it throws in development and renders inertly in production.\n *\n * **Naming**: give it `aria-label`, or wrap it in a `Tooltip` with `relationship=\"label\"`.\n * Without either it is named by its `value` and a development warning is logged.\n *\n * **Selected and disabled**: an image has no single luminance, so the check and the disabled slash\n * are the theme's foreground glyph over a background halo. Unlike `ColorSwatch` it does not opt\n * out of forced colors (a background image stays).\n *\n * @example\n * <SwatchPicker aria-label=\"Pattern\">\n * <ImageSwatch value=\"dots\" src=\"/dots.png\" aria-label=\"Dots\" />\n * </SwatchPicker>\n */\nexport const ImageSwatch = ({ src, ...rest }: ImageSwatchProps) => {\n return (\n <SwatchRadio\n {...rest}\n kind=\"ImageSwatch\"\n rootClassName={imageSwatchClassNames.root}\n fallbackKind=\"value\"\n defaultName={rest.value}\n checkColors={null}\n keepsColors={false}\n paintClassName=\"bg-cover bg-center bg-no-repeat\"\n paint={{ backgroundImage: `url(${JSON.stringify(src)})` }}\n />\n );\n};\nImageSwatch.displayName = 'ImageSwatch';\n\n/** Properties for the EmptySwatch component. */\nexport interface EmptySwatchProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n /**\n * Accessible name of the button.\n * @default 'Add color'\n */\n 'aria-label'?: string;\n /**\n * Decorative glyph (`aria-hidden`). `null` and `undefined` keep the plus glyph; `false` or\n * anything that renders nothing shows none. A `<button>` or `Button` passed here is not nested:\n * its children become the glyph and a one-time development warning is logged.\n * @default a plus glyph\n */\n icon?: Slot<'span'>;\n /**\n * Size of the swatch (20, 24, 32 or 40px).\n * @default the picker's\n */\n size?: SwatchPickerSize;\n /**\n * Shape of the swatch.\n * @default the picker's\n */\n shape?: Shape;\n /** Ref to the button. */\n ref?: React.Ref<HTMLButtonElement>;\n}\n\n/**\n * An \"add\" button inside a `SwatchPicker` (for example to open a color dialog): it has no value\n * and is never selected. In the row layout it is a plain button with its own tab stop, outside\n * the radio group's arrow order, and arrow keys pressed on it do nothing. In the grid layout it\n * is a cell like the others, in the arrow order (without `aria-selected`). Outside a\n * `SwatchPicker` it throws in development and renders inertly in production.\n *\n * @example\n * <SwatchPicker aria-label=\"Accent color\" items={items}>\n * <EmptySwatch onClick={openColorDialog} />\n * </SwatchPicker>\n */\nexport const EmptySwatch = ({\n icon,\n size: sizeProp,\n shape: shapeProp,\n className,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n ref,\n ...rest\n}: EmptySwatchProps) => {\n const { size, shape, layout, focusMode, spacing, getTabIndex } = useSwatchContext(\n 'EmptySwatch',\n sizeProp,\n shapeProp,\n );\n // Tab mode has no grid cells: the button is a tab stop of its own, like in the row layout.\n const isGrid = layout === 'grid' && focusMode !== 'tab';\n // The grid's roving keys address cells by value: an EmptySwatch, which has none, gets its own\n // (never selected, never submitted).\n const cellValue = useId('wave-empty-swatch');\n const { glyph, button } = unwrapButtonGlyph(icon);\n useSwatchIconWarning('EmptySwatch', button);\n // The optional-indicator rule: `null`/`undefined` keep the plus, anything that renders nothing\n // hides it; an unwrapped button whose children render nothing keeps the plus too.\n const content =\n icon == null || (button !== null && !slotRendersContent(glyph)) ? (\n <AddIcon size={glyphSizeMap[size]} />\n ) : (\n glyph\n );\n\n const control = (\n <button\n type=\"button\"\n {...rest}\n ref={ref}\n aria-label={ariaLabel ?? (ariaLabelledBy ? undefined : 'Add color')}\n aria-labelledby={ariaLabelledBy}\n // The row layout's root skips its arrow handling for events from here.\n data-wave-empty-swatch=\"\"\n {...(isGrid && { 'data-roving-value': cellValue, tabIndex: getTabIndex(cellValue) })}\n className={cn(\n emptySwatchClassNames.root,\n swatchPickerClassNames.swatch,\n swatchBaseClasses(size, shape, spacing),\n 'border-dashed border-stroke-accessible bg-transparent text-foreground',\n forcedColors.control,\n 'not-disabled:not-aria-disabled:hover:bg-subtle-hover',\n className,\n )}\n >\n {slotRendersContent(content) &&\n renderSlot(\n content,\n 'span',\n cn(emptySwatchClassNames.icon, 'pointer-events-none inline-flex'),\n {\n 'aria-hidden': true,\n },\n )}\n </button>\n );\n return isGrid ? <SwatchGridCell>{control}</SwatchGridCell> : control;\n};\nEmptySwatch.displayName = 'EmptySwatch';\n"],"mappings":"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"}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn as e } from "../../lib/cn.mjs";
|
|
3
|
+
import { warnOnce as t } from "../../lib/dev.mjs";
|
|
4
|
+
import { renderSlot as n, slotRendersContent as r } from "../../lib/slot.mjs";
|
|
5
|
+
import { composeEventHandlers as i } from "../../lib/composeEventHandlers.mjs";
|
|
6
|
+
import { colorSwatchClassNames as a, emptySwatchClassNames as o, imageSwatchClassNames as s, swatchPickerClassNames as c } from "../../lib/classNames.mjs";
|
|
7
|
+
import { useId as l } from "../../hooks/useId.mjs";
|
|
8
|
+
import { focusRing as u, forcedColors as d, motionSafeTransition as f } from "../../lib/styles.mjs";
|
|
9
|
+
import { unwrapButtonGlyph as p } from "../button/Button.slots.mjs";
|
|
10
|
+
import { AddIcon as m, CheckIcon as h } from "../../lib/icons.mjs";
|
|
11
|
+
import { useSwatchPickerContext as g } from "./SwatchPicker.context.mjs";
|
|
12
|
+
import { getCheckColors as _ } from "./colorUtils.mjs";
|
|
13
|
+
import * as v from "react";
|
|
14
|
+
import { jsx as y, jsxs as b } from "react/jsx-runtime";
|
|
15
|
+
//#region src/components/input/SwatchPicker.swatches.tsx
|
|
16
|
+
var x = {
|
|
17
|
+
"extra-small": "w-5 h-5",
|
|
18
|
+
small: "w-6 h-6",
|
|
19
|
+
medium: "w-8 h-8",
|
|
20
|
+
large: "w-10 h-10"
|
|
21
|
+
}, S = {
|
|
22
|
+
"extra-small": 10,
|
|
23
|
+
small: 12,
|
|
24
|
+
medium: 16,
|
|
25
|
+
large: 20
|
|
26
|
+
}, C = {
|
|
27
|
+
circular: "rounded-full",
|
|
28
|
+
square: "rounded-none",
|
|
29
|
+
rounded: "rounded"
|
|
30
|
+
};
|
|
31
|
+
function w({ colors: t, size: n }) {
|
|
32
|
+
return /* @__PURE__ */ b("span", {
|
|
33
|
+
"aria-hidden": "true",
|
|
34
|
+
className: "pointer-events-none grid",
|
|
35
|
+
children: [/* @__PURE__ */ y(h, {
|
|
36
|
+
size: n,
|
|
37
|
+
strokeWidth: 4,
|
|
38
|
+
className: e("col-start-1 row-start-1", !t && "text-background"),
|
|
39
|
+
style: t ? { color: t.halo } : void 0
|
|
40
|
+
}), /* @__PURE__ */ y(h, {
|
|
41
|
+
size: n,
|
|
42
|
+
strokeWidth: 2,
|
|
43
|
+
className: e("col-start-1 row-start-1", !t && "text-foreground"),
|
|
44
|
+
style: t ? { color: t.glyph } : void 0
|
|
45
|
+
})]
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
function T({ colors: e }) {
|
|
49
|
+
return /* @__PURE__ */ b("svg", {
|
|
50
|
+
"data-wave-swatch-slash": "",
|
|
51
|
+
"aria-hidden": "true",
|
|
52
|
+
viewBox: "0 0 24 24",
|
|
53
|
+
fill: "none",
|
|
54
|
+
className: "pointer-events-none absolute inset-0 h-full w-full",
|
|
55
|
+
children: [/* @__PURE__ */ y("line", {
|
|
56
|
+
x1: "5",
|
|
57
|
+
y1: "19",
|
|
58
|
+
x2: "19",
|
|
59
|
+
y2: "5",
|
|
60
|
+
stroke: "currentColor",
|
|
61
|
+
strokeLinecap: "round",
|
|
62
|
+
strokeWidth: 4,
|
|
63
|
+
className: e ? void 0 : "text-background",
|
|
64
|
+
style: e ? { color: e.halo } : void 0
|
|
65
|
+
}), /* @__PURE__ */ y("line", {
|
|
66
|
+
x1: "5",
|
|
67
|
+
y1: "19",
|
|
68
|
+
x2: "19",
|
|
69
|
+
y2: "5",
|
|
70
|
+
stroke: "currentColor",
|
|
71
|
+
strokeLinecap: "round",
|
|
72
|
+
strokeWidth: 2,
|
|
73
|
+
className: e ? void 0 : "text-foreground",
|
|
74
|
+
style: e ? { color: e.glyph } : void 0
|
|
75
|
+
})]
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
function E(e, n, r) {
|
|
79
|
+
t(`${e}:unnamed`, `${e}: swatch "${n}" has no accessible name, so it is announced by its ${r}. Pass \`aria-label\`, or wrap it in a Tooltip with \`relationship="label"\`.`);
|
|
80
|
+
}
|
|
81
|
+
function D(e, t) {
|
|
82
|
+
let { glyph: n, button: r } = p(t);
|
|
83
|
+
return O(e, r), n;
|
|
84
|
+
}
|
|
85
|
+
function O(e, n) {
|
|
86
|
+
v.useEffect(() => {
|
|
87
|
+
n && t(`${e}:icon-button`, `${e}: \`icon\` received ${n}; its children render as the glyph of the swatch and its props were dropped (buttons cannot be nested). Pass icon content instead, e.g. \`icon={<MyIcon />}\`.`);
|
|
88
|
+
}, [n, e]);
|
|
89
|
+
}
|
|
90
|
+
function k(e, t, n) {
|
|
91
|
+
let r = g(e);
|
|
92
|
+
return {
|
|
93
|
+
...r,
|
|
94
|
+
size: t ?? r.size,
|
|
95
|
+
shape: n ?? r.shape
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function A(e) {
|
|
99
|
+
return e === "small" ? "focus-visible:outline-offset-2" : "focus-visible:outline-offset-4";
|
|
100
|
+
}
|
|
101
|
+
function j({ selected: e, children: t }) {
|
|
102
|
+
return /* @__PURE__ */ y("div", {
|
|
103
|
+
role: "gridcell",
|
|
104
|
+
"aria-selected": e,
|
|
105
|
+
className: "flex shrink-0",
|
|
106
|
+
children: t
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
function M(t, n, r) {
|
|
110
|
+
return e(x[t], C[n], "relative flex shrink-0 items-center justify-center border-2 p-0", f, u, A(r), "disabled:cursor-not-allowed disabled:opacity-50");
|
|
111
|
+
}
|
|
112
|
+
var N = ({ kind: t, rootClassName: a, iconClassName: o, value: s, fallbackKind: l, defaultName: u, checkColors: f, paint: p, paintClassName: m, keepsColors: h, icon: g, size: _, shape: x, className: C, style: O, onClick: A, disabled: N, "aria-label": P, "aria-labelledby": F, ref: I, ...L }) => {
|
|
113
|
+
let { value: R, select: z, size: B, shape: V, layout: H, focusMode: U, spacing: W, getTabIndex: G, register: K } = k(t, _, x), q = R === s, J = U === "tab", Y = H === "grid" && !J, X = D(t, g);
|
|
114
|
+
v.useLayoutEffect(() => K(s), [K, s]);
|
|
115
|
+
let Z = P !== void 0 || F !== void 0;
|
|
116
|
+
v.useEffect(() => {
|
|
117
|
+
Z || E(t, s, l);
|
|
118
|
+
}, [
|
|
119
|
+
Z,
|
|
120
|
+
t,
|
|
121
|
+
s,
|
|
122
|
+
l
|
|
123
|
+
]);
|
|
124
|
+
let Q = /* @__PURE__ */ b("button", {
|
|
125
|
+
type: "button",
|
|
126
|
+
role: Y || J ? void 0 : "radio",
|
|
127
|
+
...L,
|
|
128
|
+
ref: I,
|
|
129
|
+
disabled: N,
|
|
130
|
+
"aria-label": P ?? (F ? void 0 : u),
|
|
131
|
+
"aria-labelledby": F,
|
|
132
|
+
"aria-checked": Y || J ? void 0 : q,
|
|
133
|
+
"aria-pressed": J ? q : Y && q || void 0,
|
|
134
|
+
"data-roving-value": s,
|
|
135
|
+
"data-selected": q ? "" : void 0,
|
|
136
|
+
tabIndex: G(s),
|
|
137
|
+
onClick: i(A, () => z(s)),
|
|
138
|
+
className: e(a, c.swatch, M(B, V, W), "border-[color:var(--wave-swatch-border,transparent)]", m, h && "forced-colors:forced-color-adjust-none forced-colors:focus-visible:outline-[Highlight]", d.border, q && !h && "forced-colors:border-[Highlight]", q ? e("ring-2 ring-foreground ring-offset-2 ring-offset-background", h && "forced-colors:ring-[Highlight]") : "not-disabled:not-aria-disabled:hover:border-stroke-hover", C),
|
|
139
|
+
style: {
|
|
140
|
+
...O,
|
|
141
|
+
...p
|
|
142
|
+
},
|
|
143
|
+
children: [q ? /* @__PURE__ */ y(w, {
|
|
144
|
+
colors: f,
|
|
145
|
+
size: S[B]
|
|
146
|
+
}) : X != null && r(X) && n(X, "span", e(o, "pointer-events-none inline-flex"), {
|
|
147
|
+
"aria-hidden": !0,
|
|
148
|
+
style: f ? { color: f.glyph } : void 0
|
|
149
|
+
}), N && /* @__PURE__ */ y(T, { colors: f })]
|
|
150
|
+
});
|
|
151
|
+
return Y ? /* @__PURE__ */ y(j, {
|
|
152
|
+
selected: q,
|
|
153
|
+
children: Q
|
|
154
|
+
}) : Q;
|
|
155
|
+
};
|
|
156
|
+
N.displayName = "SwatchRadio";
|
|
157
|
+
var P = ({ color: e, borderColor: t, ...n }) => /* @__PURE__ */ y(N, {
|
|
158
|
+
...n,
|
|
159
|
+
kind: "ColorSwatch",
|
|
160
|
+
rootClassName: a.root,
|
|
161
|
+
iconClassName: a.icon,
|
|
162
|
+
fallbackKind: "color value",
|
|
163
|
+
defaultName: e,
|
|
164
|
+
checkColors: _(e),
|
|
165
|
+
keepsColors: !0,
|
|
166
|
+
paint: {
|
|
167
|
+
backgroundColor: e,
|
|
168
|
+
...t !== void 0 && { "--wave-swatch-border": t }
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
P.displayName = "ColorSwatch";
|
|
172
|
+
var F = ({ src: e, ...t }) => /* @__PURE__ */ y(N, {
|
|
173
|
+
...t,
|
|
174
|
+
kind: "ImageSwatch",
|
|
175
|
+
rootClassName: s.root,
|
|
176
|
+
fallbackKind: "value",
|
|
177
|
+
defaultName: t.value,
|
|
178
|
+
checkColors: null,
|
|
179
|
+
keepsColors: !1,
|
|
180
|
+
paintClassName: "bg-cover bg-center bg-no-repeat",
|
|
181
|
+
paint: { backgroundImage: `url(${JSON.stringify(e)})` }
|
|
182
|
+
});
|
|
183
|
+
F.displayName = "ImageSwatch";
|
|
184
|
+
var I = ({ icon: t, size: i, shape: a, className: s, "aria-label": u, "aria-labelledby": f, ref: h, ...g }) => {
|
|
185
|
+
let { size: _, shape: v, layout: b, focusMode: x, spacing: C, getTabIndex: w } = k("EmptySwatch", i, a), T = b === "grid" && x !== "tab", E = l("wave-empty-swatch"), { glyph: D, button: A } = p(t);
|
|
186
|
+
O("EmptySwatch", A);
|
|
187
|
+
let N = t == null || A !== null && !r(D) ? /* @__PURE__ */ y(m, { size: S[_] }) : D, P = /* @__PURE__ */ y("button", {
|
|
188
|
+
type: "button",
|
|
189
|
+
...g,
|
|
190
|
+
ref: h,
|
|
191
|
+
"aria-label": u ?? (f ? void 0 : "Add color"),
|
|
192
|
+
"aria-labelledby": f,
|
|
193
|
+
"data-wave-empty-swatch": "",
|
|
194
|
+
...T && {
|
|
195
|
+
"data-roving-value": E,
|
|
196
|
+
tabIndex: w(E)
|
|
197
|
+
},
|
|
198
|
+
className: e(o.root, c.swatch, M(_, v, C), "border-dashed border-stroke-accessible bg-transparent text-foreground", d.control, "not-disabled:not-aria-disabled:hover:bg-subtle-hover", s),
|
|
199
|
+
children: r(N) && n(N, "span", e(o.icon, "pointer-events-none inline-flex"), { "aria-hidden": !0 })
|
|
200
|
+
});
|
|
201
|
+
return T ? /* @__PURE__ */ y(j, { children: P }) : P;
|
|
202
|
+
};
|
|
203
|
+
I.displayName = "EmptySwatch";
|
|
204
|
+
//#endregion
|
|
205
|
+
export { P as ColorSwatch, I as EmptySwatch, F as ImageSwatch };
|
|
206
|
+
|
|
207
|
+
//# sourceMappingURL=SwatchPicker.swatches.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SwatchPicker.swatches.mjs","names":["useId","useId$1","React","sizeMap","glyphSizeMap","shapeMap","SwatchCheck","CheckIcon","cn","colors","SwatchSlash","warnUnnamedSwatch","kind","value","fallback","useSwatchIcon","unwrapButtonGlyph","icon","useSwatchIconWarning","button","glyph","warnOnce","useSwatchContext","context","useSwatchPickerContext","size","shape","outlineOffset","spacing","SwatchGridCell","selected","swatchBaseClasses","motionSafeTransition","focusRing","SwatchRadio","sizeProp","shapeProp","ariaLabel","ariaLabelledBy","rest","selectedValue","isSelected","isTab","focusMode","isGrid","layout","register","named","fallbackKind","defaultName","getTabIndex","composeEventHandlers","onClick","select","rootClassName","swatchPickerClassNames","paintClassName","keepsColors","forcedColors","className","style","paint","checkColors","slotRendersContent","renderSlot","iconClassName","disabled","ColorSwatch","colorSwatchClassNames","color","getCheckColors","borderColor","ImageSwatch","imageSwatchClassNames","src","EmptySwatch","cellValue","content","AddIcon","control","emptySwatchClassNames"],"sources":["../../../src/components/input/SwatchPicker.swatches.tsx"],"sourcesContent":["import * as React from 'react';\nimport {\n colorSwatchClassNames,\n emptySwatchClassNames,\n imageSwatchClassNames,\n swatchPickerClassNames,\n} from '../../lib/classNames';\nimport { cn } from '../../lib/cn';\nimport { composeEventHandlers } from '../../lib/composeEventHandlers';\nimport { warnOnce } from '../../lib/dev';\nimport { AddIcon, CheckIcon } from '../../lib/icons';\nimport { renderSlot, slotRendersContent } from '../../lib/slot';\nimport type { Slot } from '../../lib/slot';\nimport { focusRing, forcedColors, motionSafeTransition } from '../../lib/styles';\nimport type { Shape } from '../../lib/types';\nimport { useId } from '../../hooks/useId';\nimport { unwrapButtonGlyph, type UnwrappedButton } from '../button/Button.slots';\nimport { useSwatchPickerContext, type SwatchPickerSize } from './SwatchPicker.context';\nimport { getCheckColors, type CheckColors } from './colorUtils';\n\n/*\n * The swatch kinds a `SwatchPicker` renders `items` through and a consumer can render as children\n * (D18, D21): `ColorSwatch`, `ImageSwatch` and `EmptySwatch`. Every swatch reads\n * `SwatchPickerContext` and puts its `ref`, `className`, `style`, name and rest props on its own\n * `<button>` (an explicit exception to C-ROUTING: there is no separate root, so a wrapping\n * Tooltip's ARIA and handlers reach the focused element without extra wiring).\n */\n\nconst sizeMap: Record<SwatchPickerSize, string> = {\n 'extra-small': 'w-5 h-5',\n small: 'w-6 h-6',\n medium: 'w-8 h-8',\n large: 'w-10 h-10',\n};\n\nconst glyphSizeMap: Record<SwatchPickerSize, number> = {\n 'extra-small': 10,\n small: 12,\n medium: 16,\n large: 20,\n};\n\nconst shapeMap: Record<Shape, string> = {\n circular: 'rounded-full',\n square: 'rounded-none',\n rounded: 'rounded',\n};\n\n/**\n * The check glyph of the selected swatch: black or white by the swatch's luminance, drawn over a\n * halo in the other color so it stays visible on any swatch (C-TOKENS exception for\n * user-supplied colors, `input-pickers#16`). Without `colors` (an image has no single luminance)\n * it is the theme's foreground glyph over a background halo.\n */\nfunction SwatchCheck({ colors, size }: { colors: CheckColors | null; size: number }) {\n return (\n <span aria-hidden=\"true\" className=\"pointer-events-none grid\">\n <CheckIcon\n size={size}\n strokeWidth={4}\n className={cn('col-start-1 row-start-1', !colors && 'text-background')}\n style={colors ? { color: colors.halo } : undefined}\n />\n <CheckIcon\n size={size}\n strokeWidth={2}\n className={cn('col-start-1 row-start-1', !colors && 'text-foreground')}\n style={colors ? { color: colors.glyph } : undefined}\n />\n </span>\n );\n}\n\n/**\n * The diagonal slash of a disabled swatch, drawn like the check glyph (a halo line under a glyph\n * line), so the state does not rely on the swatch color or its opacity.\n */\nfunction SwatchSlash({ colors }: { colors: CheckColors | null }) {\n return (\n <svg\n data-wave-swatch-slash=\"\"\n aria-hidden=\"true\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n className=\"pointer-events-none absolute inset-0 h-full w-full\"\n >\n <line\n x1=\"5\"\n y1=\"19\"\n x2=\"19\"\n y2=\"5\"\n stroke=\"currentColor\"\n strokeLinecap=\"round\"\n strokeWidth={4}\n className={colors ? undefined : 'text-background'}\n style={colors ? { color: colors.halo } : undefined}\n />\n <line\n x1=\"5\"\n y1=\"19\"\n x2=\"19\"\n y2=\"5\"\n stroke=\"currentColor\"\n strokeLinecap=\"round\"\n strokeWidth={2}\n className={colors ? undefined : 'text-foreground'}\n style={colors ? { color: colors.glyph } : undefined}\n />\n </svg>\n );\n}\n\n/** Warns once per swatch kind (not per value: `<Kind>:unnamed`, e.g. `ColorSwatch:unnamed`). */\nfunction warnUnnamedSwatch(kind: string, value: string, fallback: string): void {\n warnOnce(\n `${kind}:unnamed`,\n `${kind}: swatch \"${value}\" has no accessible name, so it is announced by its ${fallback}. ` +\n 'Pass `aria-label`, or wrap it in a Tooltip with `relationship=\"label\"`.',\n );\n}\n\n/**\n * A swatch's `icon` as a glyph inside its wired button (C-SLOTS): a `<button>` or `Button` is\n * unwrapped (its children become the glyph) with a one-time warning (`<Kind>:icon-button`).\n */\nfunction useSwatchIcon(kind: string, icon: Slot<'span'> | undefined): Slot<'span'> | undefined {\n const { glyph, button } = unwrapButtonGlyph(icon);\n useSwatchIconWarning(kind, button);\n return glyph;\n}\n\n/** The one-time `<Kind>:icon-button` warning for an unwrapped button. */\nfunction useSwatchIconWarning(kind: string, button: UnwrappedButton | null): void {\n React.useEffect(() => {\n if (button) {\n warnOnce(\n `${kind}:icon-button`,\n `${kind}: \\`icon\\` received ${button}; its children render as the glyph of the swatch ` +\n 'and its props were dropped (buttons cannot be nested). Pass icon content instead, ' +\n 'e.g. `icon={<MyIcon />}`.',\n );\n }\n }, [button, kind]);\n}\n\n/** The picker's context with this swatch's own `size` and `shape` applied over the picker's. */\nfunction useSwatchContext(kind: string, size?: SwatchPickerSize, shape?: Shape) {\n const context = useSwatchPickerContext(kind);\n return { ...context, size: size ?? context.size, shape: shape ?? context.shape };\n}\n\n/**\n * Focus outline offset: 4px, clear of a selected swatch's ring band (ring and offset, 4px); 2px at\n * `spacing=\"small\"` so it does not cover the neighbour (D22), where it lies over that band.\n */\nfunction outlineOffset(spacing: 'small' | 'medium'): string {\n return spacing === 'small' ? 'focus-visible:outline-offset-2' : 'focus-visible:outline-offset-4';\n}\n\n/**\n * The cell around a swatch's button in the grid layout: it carries only the role and, for a swatch\n * with a value, `aria-selected` (a `gridcell` role on the button would replace its button role,\n * which `aria-pressed` needs). Every consumer prop stays on the button.\n */\nfunction SwatchGridCell({ selected, children }: { selected?: boolean; children: React.ReactNode }) {\n return (\n <div role=\"gridcell\" aria-selected={selected} className=\"flex shrink-0\">\n {children}\n </div>\n );\n}\n\n/** The classes every swatch kind shares: size, shape, box, motion, focus ring and disabled look. */\nfunction swatchBaseClasses(\n size: SwatchPickerSize,\n shape: Shape,\n spacing: 'small' | 'medium',\n): string {\n return cn(\n sizeMap[size],\n shapeMap[shape],\n // The padding is set here (C-NATIVE).\n 'relative flex shrink-0 items-center justify-center border-2 p-0',\n motionSafeTransition,\n focusRing,\n outlineOffset(spacing),\n 'disabled:cursor-not-allowed disabled:opacity-50',\n );\n}\n\n/** Properties for the ColorSwatch component. */\nexport interface ColorSwatchProps extends Omit<\n React.ButtonHTMLAttributes<HTMLButtonElement>,\n 'value' | 'color'\n> {\n /** Unique identifier for the swatch (the value the picker selects and a form submits). */\n value: string;\n /** CSS color value (e.g., hex, rgb) used as the swatch background (a runtime user color). */\n color: string;\n /**\n * Decorative glyph (`aria-hidden`) shown while the swatch is not selected; the check replaces it\n * while selected. A `<button>` or `Button` passed here is not nested: its children become the\n * glyph and a one-time development warning is logged.\n */\n icon?: Slot<'span'>;\n /**\n * CSS color of the swatch's border, for a color close to the page background (set by hand, as\n * in Fluent). In forced colors the system border color replaces it.\n */\n borderColor?: string;\n /**\n * Size of the swatch (20, 24, 32 or 40px).\n * @default the picker's\n */\n size?: SwatchPickerSize;\n /**\n * Shape of the swatch.\n * @default the picker's\n */\n shape?: Shape;\n /**\n * Ref to the swatch button: `role=\"radio\"` in the row layout, inside its cell in the grid\n * layout, a toggle button in tab mode.\n */\n ref?: React.Ref<HTMLButtonElement>;\n}\n\n/** What `ColorSwatch` and `ImageSwatch` do differently. */\ninterface SwatchRadioProps extends Omit<\n React.ButtonHTMLAttributes<HTMLButtonElement>,\n 'value' | 'color'\n> {\n kind: 'ColorSwatch' | 'ImageSwatch';\n /** The kind's stable root class, named by the module that sets its `displayName` (P3-00). */\n rootClassName: string;\n /** The kind's stable `icon` part class (ColorSwatch only). */\n iconClassName?: string;\n value: string;\n /** What names the swatch when it has no name: 'color value' or 'value' (the warning's text). */\n fallbackKind: string;\n /** The accessible name of an unnamed swatch. */\n defaultName: string;\n /** The luminance-picked glyph colors; `null`: the theme's foreground over a background halo. */\n checkColors: CheckColors | null;\n /** The swatch's own background, border color and image (they win over a consumer `style`). */\n paint: Record<string, string>;\n /** Classes of the image swatch's covering background. */\n paintClassName?: string;\n /** A color swatch keeps its colors in forced colors (the color is the content). */\n keepsColors: boolean;\n icon?: Slot<'span'>;\n size?: SwatchPickerSize;\n shape?: Shape;\n ref?: React.Ref<HTMLButtonElement>;\n}\n\n/** The radio button shared by `ColorSwatch` and `ImageSwatch`. */\nconst SwatchRadio = ({\n kind,\n rootClassName,\n iconClassName,\n value,\n fallbackKind,\n defaultName,\n checkColors,\n paint,\n paintClassName,\n keepsColors,\n icon,\n size: sizeProp,\n shape: shapeProp,\n className,\n style,\n onClick,\n disabled,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n ref,\n ...rest\n}: SwatchRadioProps) => {\n const {\n value: selectedValue,\n select,\n size,\n shape,\n layout,\n focusMode,\n spacing,\n getTabIndex,\n register,\n } = useSwatchContext(kind, sizeProp, shapeProp);\n const isSelected = selectedValue === value;\n const isTab = focusMode === 'tab';\n const isGrid = layout === 'grid' && !isTab;\n const glyph = useSwatchIcon(kind, icon);\n\n React.useLayoutEffect(() => register(value), [register, value]);\n\n const named = ariaLabel !== undefined || ariaLabelledBy !== undefined;\n React.useEffect(() => {\n if (!named) warnUnnamedSwatch(kind, value, fallbackKind);\n }, [named, kind, value, fallbackKind]);\n\n const button = (\n <button\n type=\"button\"\n // The grid layout: a plain button inside its gridcell, pressed while selected (D20); tab\n // mode (D19): a toggle button of a group, `aria-pressed` always present.\n role={isGrid || isTab ? undefined : 'radio'}\n {...rest}\n ref={ref}\n disabled={disabled}\n aria-label={ariaLabel ?? (ariaLabelledBy ? undefined : defaultName)}\n aria-labelledby={ariaLabelledBy}\n aria-checked={isGrid || isTab ? undefined : isSelected}\n aria-pressed={isTab ? isSelected : isGrid ? isSelected || undefined : undefined}\n data-roving-value={value}\n data-selected={isSelected ? '' : undefined}\n tabIndex={getTabIndex(value)}\n onClick={composeEventHandlers(onClick, () => select(value))}\n className={cn(\n rootClassName,\n // Every swatch in a picker carries the picker's part class, children included (0.8).\n swatchPickerClassNames.swatch,\n swatchBaseClasses(size, shape, spacing),\n // A `borderColor` arrives as a variable, so the forced-colors edge below still wins.\n 'border-[color:var(--wave-swatch-border,transparent)]',\n paintClassName,\n // User colors are the content: keep them (and the check glyph) in forced colors, with a\n // Highlight outline, which an opted-out element would otherwise draw in author colors.\n keepsColors &&\n 'forced-colors:forced-color-adjust-none forced-colors:focus-visible:outline-[Highlight]',\n forcedColors.border,\n // An image stays in forced colors, so its selection needs a sign of its own.\n isSelected && !keepsColors && 'forced-colors:border-[Highlight]',\n isSelected\n ? cn(\n 'ring-2 ring-foreground ring-offset-2 ring-offset-background',\n keepsColors && 'forced-colors:ring-[Highlight]',\n )\n : 'not-disabled:not-aria-disabled:hover:border-stroke-hover',\n className,\n )}\n style={{ ...style, ...paint }}\n >\n {isSelected ? (\n <SwatchCheck colors={checkColors} size={glyphSizeMap[size]} />\n ) : (\n glyph != null &&\n slotRendersContent(glyph) &&\n renderSlot(glyph, 'span', cn(iconClassName, 'pointer-events-none inline-flex'), {\n 'aria-hidden': true,\n style: checkColors ? { color: checkColors.glyph } : undefined,\n })\n )}\n {disabled && <SwatchSlash colors={checkColors} />}\n </button>\n );\n return isGrid ? <SwatchGridCell selected={isSelected}>{button}</SwatchGridCell> : button;\n};\nSwatchRadio.displayName = 'SwatchRadio';\n\n// D18: swatches read SwatchPickerContext; their own ref/className/style/rest go straight to the\n// button (an explicit exception to C-ROUTING's root/control split).\n/**\n * One color swatch of a `SwatchPicker`, read from `items` or rendered directly as a child.\n * Outside a `SwatchPicker` it throws in development and renders inertly in production.\n *\n * **Naming**: give it `aria-label`, or wrap it in a `Tooltip` with `relationship=\"label\"` (its\n * `aria-labelledby` reaches this element, since every rest prop lands on the swatch's own\n * button). Without either it is named by its `color` value and a development warning is logged.\n *\n * **Disabled**: `disabled` is native (out of the arrow keys and the tab order, never selected);\n * the swatch is dimmed and crossed by a diagonal slash, so the state does not rely on its color.\n *\n * @example\n * <SwatchPicker aria-label=\"Accent color\">\n * <ColorSwatch value=\"red\" color=\"#d13438\" aria-label=\"Red\" />\n * </SwatchPicker>\n */\nexport const ColorSwatch = ({ color, borderColor, ...rest }: ColorSwatchProps) => {\n return (\n <SwatchRadio\n {...rest}\n kind=\"ColorSwatch\"\n rootClassName={colorSwatchClassNames.root}\n iconClassName={colorSwatchClassNames.icon}\n fallbackKind=\"color value\"\n defaultName={color}\n checkColors={getCheckColors(color)}\n keepsColors\n paint={{\n backgroundColor: color,\n ...(borderColor !== undefined && { '--wave-swatch-border': borderColor }),\n }}\n />\n );\n};\nColorSwatch.displayName = 'ColorSwatch';\n\n/** Properties for the ImageSwatch component. */\nexport interface ImageSwatchProps extends Omit<\n React.ButtonHTMLAttributes<HTMLButtonElement>,\n 'value'\n> {\n /** Unique identifier for the swatch (the value the picker selects and a form submits). */\n value: string;\n /** URL of the image (drawn as a covering background image). */\n src: string;\n /**\n * Size of the swatch (20, 24, 32 or 40px).\n * @default the picker's\n */\n size?: SwatchPickerSize;\n /**\n * Shape of the swatch.\n * @default the picker's\n */\n shape?: Shape;\n /**\n * Ref to the swatch button: `role=\"radio\"` in the row layout, inside its cell in the grid\n * layout, a toggle button in tab mode.\n */\n ref?: React.Ref<HTMLButtonElement>;\n}\n\n/**\n * One image swatch of a `SwatchPicker`: a pattern or texture drawn as a covering background\n * image. Outside a `SwatchPicker` it throws in development and renders inertly in production.\n *\n * **Naming**: give it `aria-label`, or wrap it in a `Tooltip` with `relationship=\"label\"`.\n * Without either it is named by its `value` and a development warning is logged.\n *\n * **Selected and disabled**: an image has no single luminance, so the check and the disabled slash\n * are the theme's foreground glyph over a background halo. Unlike `ColorSwatch` it does not opt\n * out of forced colors (a background image stays).\n *\n * @example\n * <SwatchPicker aria-label=\"Pattern\">\n * <ImageSwatch value=\"dots\" src=\"/dots.png\" aria-label=\"Dots\" />\n * </SwatchPicker>\n */\nexport const ImageSwatch = ({ src, ...rest }: ImageSwatchProps) => {\n return (\n <SwatchRadio\n {...rest}\n kind=\"ImageSwatch\"\n rootClassName={imageSwatchClassNames.root}\n fallbackKind=\"value\"\n defaultName={rest.value}\n checkColors={null}\n keepsColors={false}\n paintClassName=\"bg-cover bg-center bg-no-repeat\"\n paint={{ backgroundImage: `url(${JSON.stringify(src)})` }}\n />\n );\n};\nImageSwatch.displayName = 'ImageSwatch';\n\n/** Properties for the EmptySwatch component. */\nexport interface EmptySwatchProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n /**\n * Accessible name of the button.\n * @default 'Add color'\n */\n 'aria-label'?: string;\n /**\n * Decorative glyph (`aria-hidden`). `null` and `undefined` keep the plus glyph; `false` or\n * anything that renders nothing shows none. A `<button>` or `Button` passed here is not nested:\n * its children become the glyph and a one-time development warning is logged.\n * @default a plus glyph\n */\n icon?: Slot<'span'>;\n /**\n * Size of the swatch (20, 24, 32 or 40px).\n * @default the picker's\n */\n size?: SwatchPickerSize;\n /**\n * Shape of the swatch.\n * @default the picker's\n */\n shape?: Shape;\n /** Ref to the button. */\n ref?: React.Ref<HTMLButtonElement>;\n}\n\n/**\n * An \"add\" button inside a `SwatchPicker` (for example to open a color dialog): it has no value\n * and is never selected. In the row layout it is a plain button with its own tab stop, outside\n * the radio group's arrow order, and arrow keys pressed on it do nothing. In the grid layout it\n * is a cell like the others, in the arrow order (without `aria-selected`). Outside a\n * `SwatchPicker` it throws in development and renders inertly in production.\n *\n * @example\n * <SwatchPicker aria-label=\"Accent color\" items={items}>\n * <EmptySwatch onClick={openColorDialog} />\n * </SwatchPicker>\n */\nexport const EmptySwatch = ({\n icon,\n size: sizeProp,\n shape: shapeProp,\n className,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n ref,\n ...rest\n}: EmptySwatchProps) => {\n const { size, shape, layout, focusMode, spacing, getTabIndex } = useSwatchContext(\n 'EmptySwatch',\n sizeProp,\n shapeProp,\n );\n // Tab mode has no grid cells: the button is a tab stop of its own, like in the row layout.\n const isGrid = layout === 'grid' && focusMode !== 'tab';\n // The grid's roving keys address cells by value: an EmptySwatch, which has none, gets its own\n // (never selected, never submitted).\n const cellValue = useId('wave-empty-swatch');\n const { glyph, button } = unwrapButtonGlyph(icon);\n useSwatchIconWarning('EmptySwatch', button);\n // The optional-indicator rule: `null`/`undefined` keep the plus, anything that renders nothing\n // hides it; an unwrapped button whose children render nothing keeps the plus too.\n const content =\n icon == null || (button !== null && !slotRendersContent(glyph)) ? (\n <AddIcon size={glyphSizeMap[size]} />\n ) : (\n glyph\n );\n\n const control = (\n <button\n type=\"button\"\n {...rest}\n ref={ref}\n aria-label={ariaLabel ?? (ariaLabelledBy ? undefined : 'Add color')}\n aria-labelledby={ariaLabelledBy}\n // The row layout's root skips its arrow handling for events from here.\n data-wave-empty-swatch=\"\"\n {...(isGrid && { 'data-roving-value': cellValue, tabIndex: getTabIndex(cellValue) })}\n className={cn(\n emptySwatchClassNames.root,\n swatchPickerClassNames.swatch,\n swatchBaseClasses(size, shape, spacing),\n 'border-dashed border-stroke-accessible bg-transparent text-foreground',\n forcedColors.control,\n 'not-disabled:not-aria-disabled:hover:bg-subtle-hover',\n className,\n )}\n >\n {slotRendersContent(content) &&\n renderSlot(\n content,\n 'span',\n cn(emptySwatchClassNames.icon, 'pointer-events-none inline-flex'),\n {\n 'aria-hidden': true,\n },\n )}\n </button>\n );\n return isGrid ? <SwatchGridCell>{control}</SwatchGridCell> : control;\n};\nEmptySwatch.displayName = 'EmptySwatch';\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";const e=require("
|
|
1
|
+
"use client";const e=require("./TagPicker.option.cjs"),t=require("./TagPicker.parts.cjs"),n=require("./TagPicker.root.cjs");var r=Object.assign(n.TagPickerRoot,{Control:t.TagPickerControl,Group:t.TagPickerGroup,Input:t.TagPickerInput,Button:t.TagPickerButton,List:t.TagPickerList,Option:e.TagPickerOption,OptionGroup:t.TagPickerOptionGroup});exports.TagPicker=r,exports.TagPickerButton=t.TagPickerButton,exports.TagPickerControl=t.TagPickerControl,exports.TagPickerGroup=t.TagPickerGroup,exports.TagPickerInput=t.TagPickerInput,exports.TagPickerList=t.TagPickerList,exports.TagPickerOption=e.TagPickerOption,exports.TagPickerOptionGroup=t.TagPickerOptionGroup;
|
|
2
2
|
//# sourceMappingURL=TagPicker.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TagPicker.cjs","names":["React","cn","tagPickerClassNames","focusRing","DismissIcon","useFieldContext","useFieldControl","isInvalidLook","useInputLook","useControllable","useIsClient","useAnnounce","useId","useListbox","useListboxPopup","useMergedRefs","getArrowIntent","getDirection","inputAppearanceClasses","inputFocusWithin","joinIds","composeEventHandlers","ListboxSurface","Option","HiddenInput","require_runtime","require_cn","require_dev","require_composeEventHandlers","require_classNames","require_useControllable","require_useId","require_direction","require_useMergedRefs","require_useIsClient","require_aria","require_useFieldControl","require_useAnnounce","require_styles","require_icons","require_inputLook","require_Input","require_useFormReset","require_HiddenInput","require_useListbox","require_Option","require_usePreserveFocus","react","react_jsx_runtime","defaultRemoveLabel","label","defaultSummaryLabel","labels","defaultAddedLabel","count","defaultRemovedLabel","TAG_PICKER_SIZES","TAG_PICKER_SIZE","EMPTY","sameTags","a","b","value","index","TagRemoveButton","ref","getFallback","TagPicker","valueProp","openProp","sizeProp","appearanceProp","ariaLabel","ariaLabelledBy","ariaDescribedBy","ariaInvalid","ariaRequired","ariaErrorMessage","ariaDetails","rest","props","onChange","field","validates","required","readOnly","fieldProps","invalidLook","sizeClasses","size","selected","setSelected","defaultValue","list","next","onValueChange","interactive","disabled","openState","setOpen","defaultOpen","onOpenChange","isClient","open","query","setQuery","wasInteractive","setWasInteractive","lockedOpen","announce","rootRef","inputRef","summaryId","labelByValue","map","option","options","labelOf","available","chosen","filter","item","unknownValues","v","getInput","focusInput","addTag","removeTag","listbox","expanded","noMatchesText","surfaceOpen","noMatches","rootMergedRef","inputMergedRef","controlRef","initial","current","form","removeButtons","from","handleInputKeyDown","event","handleTagKeyDown","buttons","intent","listLabelledBy","appearance","className","setReference","placeholder","onKeyDown","onKeyUp"],"sources":["../../../src/components/input/TagPicker.tsx"],"sourcesContent":["import * as React from 'react';\nimport { cn } from '../../lib/cn';\nimport { joinIds } from '../../lib/aria';\nimport { tagPickerClassNames } from '../../lib/classNames';\nimport { composeEventHandlers } from '../../lib/composeEventHandlers';\nimport { warnDeprecated, warnOnce } from '../../lib/dev';\nimport { getArrowIntent, getDirection } from '../../lib/direction';\nimport { DismissIcon } from '../../lib/icons';\nimport {\n focusRing,\n inputAppearanceClasses,\n inputFocusWithin,\n inputInvalidWithin,\n} from '../../lib/styles';\nimport type { InputAppearance, Size } from '../../lib/types';\nimport { useAnnounce } from '../../hooks/useAnnounce';\nimport { useControllable } from '../../hooks/useControllable';\nimport { useFieldContext, useFieldControl } from '../../hooks/useFieldControl';\nimport { useFormReset } from '../../hooks/useFormReset';\nimport { useId } from '../../hooks/useId';\nimport { useIsClient } from '../../hooks/useIsClient';\nimport { useListbox, type ListboxItem } from '../../hooks/useListbox';\nimport { useMergedRefs } from '../../hooks/useMergedRefs';\nimport { usePreserveFocus } from '../../hooks/usePreserveFocus';\nimport { HiddenInput } from '../internal/HiddenInput';\nimport { useInputLook } from './inputLook';\nimport { isInvalidLook } from './Input';\nimport { ListboxSurface, Option, useListboxPopup } from './Option';\nimport type { RoutedHandlers } from './routedHandlers';\n\n/** Represents a single selectable tag option. */\nexport interface TagPickerOption {\n /** Unique identifier for the option. */\n value: string;\n /** Display label for the option. */\n label: string;\n}\n\n/**\n * The TagPicker's built-in names, descriptions and announcements, for localization. Each member is\n * optional and falls back to its English default.\n */\nexport interface TagPickerLabels {\n /** Name of a tag's remove button.\n * @default (label) => `Remove ${label}`\n */\n remove?: (label: string) => string;\n /** Name of the list of selected tags.\n * @default 'Selected'\n */\n selected?: string;\n /** Description of the input that sums up the selected tags (their labels, in order).\n * @default (labels) => `Selected: ${labels.join(', ')}`\n */\n summary?: (labels: string[]) => string;\n /** Announcement when a tag is added; `count` is the number of tags after the change.\n * @default (label, count) => `${label} added, ${count} selected`\n */\n added?: (label: string, count: number) => string;\n /** Announcement when a tag is removed; `count` is the number of tags after the change.\n * @default (label, count) => `${label} removed, ${count} selected`\n */\n removed?: (label: string, count: number) => string;\n /** Status text (announced, and shown in the popup) when the typed text matches no option.\n * @default 'No matches'\n */\n noMatches?: string;\n}\n\nconst defaultRemoveLabel = (label: string) => `Remove ${label}`;\nconst defaultSummaryLabel = (labels: string[]) => `Selected: ${labels.join(', ')}`;\nconst defaultAddedLabel = (label: string, count: number) => `${label} added, ${count} selected`;\nconst defaultRemovedLabel = (label: string, count: number) => `${label} removed, ${count} selected`;\n\n/** Properties for the TagPicker component. */\nexport interface TagPickerProps extends Omit<\n React.HTMLAttributes<HTMLDivElement>,\n 'onChange' | 'defaultValue' | RoutedHandlers\n> {\n /** Available options to choose from. */\n options: readonly TagPickerOption[];\n /** Controlled array of selected option values (`[]` for none). */\n value?: readonly string[];\n /**\n * Initial selected values for uncontrolled usage; a form reset restores them.\n * @default []\n */\n defaultValue?: readonly string[];\n /**\n * Called with the new selection when it changes: a tag is added or removed, or a form reset\n * restores different tags.\n */\n onValueChange?: (value: string[]) => void;\n /**\n * Called with the new selection when it changes: a tag is added or removed, or a form reset\n * restores different tags.\n * @deprecated Use `onValueChange`.\n */\n onChange?: (value: string[]) => void;\n /**\n * Controlled open state of the option list. The list renders only in the browser: an open\n * picker is closed in the server HTML and opens once it has hydrated.\n */\n open?: boolean;\n /**\n * Initial open state for uncontrolled usage. A picker that starts disabled or read-only starts\n * closed. The list renders only in the browser: it is closed in the server HTML and opens once\n * the picker has hydrated.\n * @default false\n */\n defaultOpen?: boolean;\n /** Called when the option list opens or closes. */\n onOpenChange?: (open: boolean) => void;\n /**\n * Placeholder text shown when no tags are selected.\n * @default 'Select...'\n */\n placeholder?: string;\n /**\n * Whether the tag picker is disabled: nothing can be added or removed, and the remove buttons\n * leave the tab order.\n * @default false\n */\n disabled?: boolean;\n /**\n * Size of the tags and the field: `medium` (the 0.7 look), `large` or `extra-large` (Fluent's\n * union; TagPicker has no `small`). Default: the surrounding Field's `size`, else `WaveProvider\n * inputDefaults.size`, else `'medium'`; a Field or provider `small` gives `'medium'` too. The\n * option list keeps its size.\n */\n size?: TagPickerSize;\n /**\n * Look of the field: `outline` (a full border), `underline` (a bottom stroke only),\n * `filled-darker` or `filled-lighter` (a fill without a visible stroke: give the field a visible\n * label). Default: `WaveProvider inputDefaults.appearance`, else `'outline'`.\n */\n appearance?: InputAppearance;\n /** Name of the values in form submissions (one hidden input per value). */\n name?: string;\n /** Id of the form the values belong to, when the TagPicker is outside it. */\n form?: string;\n /**\n * At least one tag is required to submit the form (native constraint validation). Like a native\n * readonly input, a `readOnly` TagPicker does not block submission.\n */\n required?: boolean;\n /**\n * The `autocomplete` attribute of the `<input role=\"combobox\">`. Browser autofill is off unless\n * you set it (for example `'on'`).\n * @default 'off'\n */\n autoComplete?: string;\n /** The maximum length of the typed text (`maxlength` of the `<input role=\"combobox\">`). */\n maxLength?: number;\n /**\n * The selection cannot be changed: the input is read-only, the tags have no remove buttons, and\n * the option list neither opens (click, keyboard, a controlled `open`) nor adds tags; Escape is\n * left to the page. Turning it (or `disabled`) on while the list is open closes it\n * (`onOpenChange(false)`); a focused remove button hands focus to the input.\n */\n readOnly?: boolean;\n /**\n * Names of the remove buttons and the tag list, the summary that describes the input, the\n * announcements and the \"No matches\" text, for localization. Unset members keep their English\n * defaults.\n */\n labels?: TagPickerLabels;\n /** Called when the `<input role=\"combobox\">` receives focus (the root keeps other handlers). */\n onFocus?: React.FocusEventHandler<HTMLInputElement>;\n /** Called when the `<input role=\"combobox\">` loses focus. */\n onBlur?: React.FocusEventHandler<HTMLInputElement>;\n /**\n * Called on a key press in the `<input role=\"combobox\">`, before the built-in listbox and tag\n * keys; `event.preventDefault()` skips them.\n */\n onKeyDown?: React.KeyboardEventHandler<HTMLInputElement>;\n /** Called when a key is released in the `<input role=\"combobox\">`. */\n onKeyUp?: React.KeyboardEventHandler<HTMLInputElement>;\n /** Ref to the `<input role=\"combobox\">` (the focusable element). */\n controlRef?: React.Ref<HTMLInputElement>;\n /** Ref to the root `<div>`. */\n ref?: React.Ref<HTMLDivElement>;\n}\n\n/** Sizes of TagPicker (Fluent's): medium (the 0.7 look), large and extra-large. */\nexport type TagPickerSize = Extract<Size, 'medium' | 'large' | 'extra-large'>;\n\nconst TAG_PICKER_SIZES: readonly TagPickerSize[] = ['medium', 'large', 'extra-large'];\n\n/** Per-size classes of the control, the tags and the input (medium is the 0.7 look). */\nconst TAG_PICKER_SIZE: Readonly<\n Record<TagPickerSize, { control: string; tag: string; input: string }>\n> = {\n medium: {\n control: 'gap-1 px-2 py-1.5',\n tag: 'px-2 py-0.5 text-body-1',\n input: 'py-0.5 text-body-1',\n },\n large: {\n control: 'gap-1.5 px-2.5 py-2',\n tag: 'px-2 py-1 text-body-1',\n input: 'py-1 text-body-1',\n },\n 'extra-large': {\n control: 'gap-1.5 px-3 py-2.5',\n tag: 'px-2.5 py-1 text-body-2',\n input: 'py-1 text-body-2',\n },\n};\n\nconst EMPTY: readonly string[] = [];\n\n/** Whether two selections hold the same values in the same order. */\nfunction sameTags(a: readonly string[], b: readonly string[]): boolean {\n return a.length === b.length && a.every((value, index) => value === b[index]);\n}\n\ninterface TagRemoveButtonProps {\n /** The accessible name of the button. */\n label: string;\n disabled: boolean;\n onClick: () => void;\n onKeyDown: React.KeyboardEventHandler<HTMLButtonElement>;\n /** The element that receives focus when this button unmounts while focused (the input). */\n getFallback: () => HTMLElement | null;\n}\n\n/**\n * A tag's remove button. When it unmounts while focused (`readOnly` turned on, or a controlled\n * value dropped its tag), focus moves to the input instead of falling to `<body>` (C-DISABLED).\n */\nfunction TagRemoveButton({\n label,\n disabled,\n onClick,\n onKeyDown,\n getFallback,\n}: TagRemoveButtonProps) {\n const ref = React.useRef<HTMLButtonElement | null>(null);\n usePreserveFocus(ref, getFallback);\n return (\n <button\n ref={ref}\n type=\"button\"\n data-wave-tagpicker-remove=\"\"\n disabled={disabled}\n aria-label={label}\n className={cn(\n tagPickerClassNames.removeButton,\n // Padding and background set here (C-NATIVE), not left to an app-wide rule.\n 'ms-0.5 inline-flex rounded-sm bg-transparent p-0 text-muted-foreground not-disabled:not-aria-disabled:hover:text-foreground disabled:cursor-not-allowed',\n focusRing,\n )}\n onClick={onClick}\n onKeyDown={onKeyDown}\n >\n <DismissIcon size={12} />\n </button>\n );\n}\n\n/**\n * A multi-select combobox that shows the selected values as removable tags. Typing filters the\n * options and makes the first match active; ArrowDown/ArrowUp move the highlight\n * (`aria-activedescendant`), Enter adds the highlighted option and keeps the list open (with\n * nothing highlighted — no typed text, or text that matches no option — Enter is left to the\n * surrounding form), Escape closes the list (and then clears the typed text). Backspace in the\n * empty input moves focus to the last tag; Backspace or Delete there removes it. Additions and\n * removals are announced (\"Cherry removed, 2 selected\"), and so is \"No matches\" for text that\n * matches no option.\n *\n * The tags form a list named \"Selected\"; the input is described by a summary of the selected\n * labels (\"Selected: Apple, Banana\"). These built-in texts are English; `labels` localizes them.\n * The `<input>` receives `id`, `aria-label`, `aria-labelledby`, `aria-describedby`,\n * `aria-invalid`, `aria-required`, `aria-errormessage`, `aria-details`, `tabIndex`, `autoFocus`,\n * `onFocus`/`onBlur`/`onKeyDown`/`onKeyUp` and the text input attributes `autoComplete`,\n * `autoCapitalize`, `autoCorrect`, `maxLength`, `inputMode`, `spellCheck` and `enterKeyHint`.\n * `ref`, `className`, `style`, other `aria-*` attributes and the remaining props stay on the root\n * `<div>`. Inside a `Field` the input is labelled and described by it. The tag area shows the\n * error look whenever the input ends up `aria-invalid` (its own `aria-invalid` or a `Field`\n * error). With `name`/`required` the values take part in form submission, validation and reset.\n * Selected values without a matching option are shown with their raw value. The open list renders\n * only in the browser: an open list (`defaultOpen`, `open`) is closed in the server HTML and opens\n * once the picker has hydrated.\n *\n * **Size and appearance**: `size` resolves from its own prop, then the surrounding `Field`'s\n * `size`, then `WaveProvider inputDefaults.size`, else `'medium'` (a Field or provider `small`\n * gives `'medium'` too, since TagPicker has no `small`); `appearance` from its own prop, then\n * `WaveProvider inputDefaults.appearance`, else `'outline'`; both render as `data-size` and\n * `data-appearance` on the root `<div>`. The option list keeps its size.\n */\nexport const TagPicker = (props: TagPickerProps) => {\n const {\n options,\n value: valueProp,\n defaultValue,\n onValueChange,\n onChange,\n open: openProp,\n defaultOpen,\n onOpenChange,\n placeholder = 'Select...',\n disabled = false,\n size: sizeProp,\n appearance: appearanceProp,\n name,\n form,\n required,\n autoComplete = 'off',\n autoCapitalize,\n autoCorrect,\n maxLength,\n readOnly,\n labels,\n inputMode,\n spellCheck,\n enterKeyHint,\n autoFocus,\n tabIndex,\n id,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n 'aria-invalid': ariaInvalid,\n 'aria-required': ariaRequired,\n 'aria-errormessage': ariaErrorMessage,\n 'aria-details': ariaDetails,\n onFocus,\n onBlur,\n onKeyDown,\n onKeyUp,\n controlRef,\n className,\n ref,\n ...rest\n } = props;\n\n if (onChange !== undefined) warnDeprecated('TagPicker', 'onChange', 'onValueChange');\n\n const field = useFieldContext();\n const isRequired = required ?? field?.required ?? false;\n // Like a native readonly input, a read-only TagPicker is barred from constraint validation (the\n // user could not fix it); its values are still submitted.\n const validates = isRequired && !readOnly;\n const fieldProps = useFieldControl({\n id,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n 'aria-invalid': ariaInvalid,\n // An explicit `required={false}` wins over a required Field, so aria-required matches\n // `isRequired`.\n 'aria-required': ariaRequired ?? required,\n });\n // The error look follows the resolved state: the consumer's `aria-invalid` or the Field's.\n const invalidLook = isInvalidLook(false, fieldProps['aria-invalid']);\n const { size, appearance } = useInputLook(sizeProp, appearanceProp, {\n sizes: TAG_PICKER_SIZES,\n defaultSize: 'medium',\n });\n // A size outside TAG_PICKER_SIZES (from untyped code) is skipped by useInputLook, so `size` is\n // always one of them and the data attribute carries the resolved value.\n const sizeClasses = TAG_PICKER_SIZE[size];\n\n const [selected, setSelected] = useControllable<readonly string[]>(\n valueProp,\n defaultValue ?? EMPTY,\n (next) => {\n // The props are read only; the callbacks receive an array of their own.\n const list = [...next];\n onValueChange?.(list);\n onChange?.(list);\n },\n );\n const interactive = !disabled && !readOnly;\n // A picker that starts disabled or read-only never shows its list, so it starts closed (no close\n // to report later).\n const [openState, setOpen] = useControllable(\n openProp,\n (defaultOpen ?? false) && interactive,\n onOpenChange,\n );\n // The open list lives in a portal, which renders only in the browser: until then (the server\n // HTML, hydration) the picker shows the closed inline list and reports it closed, so\n // aria-controls and aria-activedescendant never name a missing element.\n const isClient = useIsClient();\n const open = openState && interactive && isClient;\n const [query, setQuery] = React.useState('');\n // Locking the control (readOnly/disabled) while typing drops the typed text (adjust-during-render\n // pattern, C-HOOKS).\n const [wasInteractive, setWasInteractive] = React.useState(interactive);\n if (wasInteractive !== interactive) {\n setWasInteractive(interactive);\n if (!interactive) setQuery('');\n }\n // Locking also closes the list itself, not only the derived `open`, so unlocking does not reopen\n // it without a user action. The close is reported through onOpenChange (a consumer callback from\n // an effect, C-HOOKS); a controlled `open` that stays true is still not shown while locked.\n const lockedOpen = !interactive && openState;\n React.useEffect(() => {\n if (lockedOpen) setOpen(false);\n }, [lockedOpen, setOpen]);\n const announce = useAnnounce();\n\n const rootRef = React.useRef<HTMLDivElement | null>(null);\n const inputRef = React.useRef<HTMLInputElement | null>(null);\n const summaryId = useId('tagpicker-summary');\n\n const labelByValue = React.useMemo(() => {\n const map = new Map<string, string>();\n for (const option of options) if (!map.has(option.value)) map.set(option.value, option.label);\n return map;\n }, [options]);\n const labelOf = (value: string) => labelByValue.get(value) ?? value;\n\n const available = React.useMemo<ListboxItem[]>(() => {\n const chosen = new Set(selected);\n return options\n .filter((option) => !chosen.has(option.value))\n .map((option) => ({ value: option.value, label: option.label }));\n }, [options, selected]);\n const filter = React.useMemo(\n () =>\n query\n ? (item: ListboxItem) => item.label.toLowerCase().includes(query.toLowerCase())\n : undefined,\n [query],\n );\n\n // Unknown values render as tags labelled by the raw value (input-pickers#22).\n const unknownValues = selected.filter((value) => !labelByValue.has(value)).join('\\u0000');\n React.useEffect(() => {\n if (!unknownValues) return;\n warnOnce(\n 'TagPicker:unknown-value',\n `TagPicker: the selected value(s) ${unknownValues\n .split('\\u0000')\n .map((v) => `\"${v}\"`)\n .join(', ')} match no option; the raw value is shown as the tag label.`,\n );\n }, [unknownValues]);\n\n const getInput = () => inputRef.current;\n const focusInput = () => inputRef.current?.focus();\n\n const addTag = (value: string) => {\n if (!interactive || selected.includes(value)) return;\n const next = [...selected, value];\n setSelected(next);\n setQuery('');\n announce((labels?.added ?? defaultAddedLabel)(labelOf(value), next.length));\n };\n\n const removeTag = (value: string) => {\n if (!interactive) return;\n const next = selected.filter((v) => v !== value);\n setSelected(next);\n announce((labels?.removed ?? defaultRemovedLabel)(labelOf(value), next.length));\n focusInput();\n };\n\n const listbox = useListbox({\n open,\n onOpenChange: (next) => setOpen(next),\n mode: 'editable',\n multiple: true,\n selectedValues: selected,\n onSelect: (value) => addTag(value),\n items: available,\n filter,\n // Typing a filter makes its first match active (as in Combobox), so Enter adds what the list\n // shows instead of submitting the form. Nothing is active on open or with the text cleared.\n autoHighlight: query ? 'first' : false,\n idPrefix: 'tagpicker-listbox',\n onClearDraft: query ? () => setQuery('') : undefined,\n });\n\n // aria-expanded only while a listbox with options is shown (input-pickers#21).\n const expanded = open && listbox.items.length > 0;\n const noMatchesText = labels?.noMatches ?? 'No matches';\n // The popup shows the options, or \"No matches\" for a query.\n const surfaceOpen = open && (expanded || query !== '');\n const noMatches = surfaceOpen && !expanded;\n const { layerId, setReference, surfaceRef, floatingProps } = useListboxPopup({\n open,\n surfaceOpen,\n onDismiss: () => setOpen(false),\n rootRef,\n anchorRef: inputRef,\n });\n\n const rootMergedRef = useMergedRefs<HTMLDivElement>(rootRef, ref);\n const inputMergedRef = useMergedRefs<HTMLInputElement>(inputRef, controlRef);\n\n useFormReset(\n inputRef,\n () => {\n // Compared by content (C-FORMS): an inline default is a new array on every render, and a\n // reset that keeps the same tags reports nothing.\n const initial = defaultValue ?? EMPTY;\n setSelected((current) => (sameTags(current, initial) ? current : [...initial]));\n setQuery('');\n },\n form,\n );\n\n /** The tags' remove buttons, found from an element inside the component (no ref read). */\n const removeButtons = (from: Element) =>\n Array.from(\n from\n .closest('[data-wave-tagpicker-control]')\n ?.querySelectorAll<HTMLButtonElement>('[data-wave-tagpicker-remove]') ?? [],\n );\n\n const handleInputKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {\n // Read-only: no listbox keys at all (they would open the list, add or remove tags).\n if (!interactive) return;\n if (event.key === 'Escape' && open && !surfaceOpen && !event.nativeEvent.isComposing) {\n // Open with nothing shown (every option selected, no query): close, but leave Escape to an\n // enclosing layer (overlays#1) instead of consuming it for an invisible list.\n setOpen(false);\n return;\n }\n if (event.key === 'Backspace' && query === '' && selected.length > 0) {\n // First Backspace moves to the last tag; Backspace/Delete there removes it (input-pickers#14).\n event.preventDefault();\n removeButtons(event.currentTarget).at(-1)?.focus();\n return;\n }\n listbox.onKeyDown(event);\n };\n\n const handleTagKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>, value: string) => {\n if (!interactive) return;\n const buttons = removeButtons(event.currentTarget);\n const index = buttons.indexOf(event.currentTarget);\n if (event.key === 'Backspace' || event.key === 'Delete') {\n event.preventDefault();\n removeTag(value);\n return;\n }\n if (event.key === 'Escape') {\n // Consumed here, so an enclosing layer ignores it (spec §5.4).\n event.preventDefault();\n focusInput();\n return;\n }\n const intent = getArrowIntent(event.key, {\n orientation: 'horizontal',\n dir: getDirection(event.currentTarget),\n });\n if (intent === 'prev' && index > 0) {\n event.preventDefault();\n buttons[index - 1].focus();\n } else if (intent === 'next') {\n event.preventDefault();\n if (index < buttons.length - 1) buttons[index + 1].focus();\n else focusInput();\n }\n };\n\n const listLabelledBy =\n fieldProps['aria-label'] === undefined\n ? (fieldProps['aria-labelledby'] ?? field?.labelId)\n : fieldProps['aria-labelledby'];\n\n return (\n <div\n data-size={size}\n data-appearance={appearance}\n {...rest}\n ref={rootMergedRef}\n className={cn(tagPickerClassNames.root, 'relative', className)}\n >\n <div\n ref={setReference}\n data-wave-tagpicker-control=\"\"\n role=\"group\"\n aria-disabled={disabled || undefined}\n className={cn(\n tagPickerClassNames.control,\n 'flex flex-wrap items-center',\n sizeClasses.control,\n inputAppearanceClasses[appearance],\n inputFocusWithin,\n invalidLook && inputInvalidWithin,\n disabled && 'cursor-not-allowed opacity-50',\n )}\n onClick={(event) => {\n if (disabled || (event.target as Element).closest('button')) return;\n focusInput();\n if (!open && interactive) setOpen(true);\n }}\n >\n {selected.length > 0 && (\n <div\n role=\"list\"\n aria-label={labels?.selected ?? 'Selected'}\n className={cn(tagPickerClassNames.group, 'flex flex-wrap items-center gap-1')}\n >\n {selected.map((value) => {\n const label = labelOf(value);\n return (\n <div\n key={value}\n role=\"listitem\"\n className={cn(\n tagPickerClassNames.tag,\n 'inline-flex items-center gap-1 rounded bg-muted text-foreground',\n sizeClasses.tag,\n )}\n >\n {label}\n {!readOnly && (\n <TagRemoveButton\n label={(labels?.remove ?? defaultRemoveLabel)(label)}\n disabled={disabled}\n onClick={() => removeTag(value)}\n onKeyDown={(event) => handleTagKeyDown(event, value)}\n getFallback={getInput}\n />\n )}\n </div>\n );\n })}\n </div>\n )}\n {selected.length > 0 && (\n // Describes the input. The list itself would read every \"Remove …\" button name too.\n <span id={summaryId} hidden>\n {(labels?.summary ?? defaultSummaryLabel)(selected.map(labelOf))}\n </span>\n )}\n <input\n type=\"text\"\n autoComplete={autoComplete}\n {...fieldProps}\n {...listbox.getComboboxProps()}\n aria-expanded={expanded}\n aria-describedby={joinIds(\n fieldProps['aria-describedby'],\n selected.length > 0 ? summaryId : undefined,\n )}\n aria-errormessage={ariaErrorMessage}\n aria-details={ariaDetails}\n ref={inputMergedRef}\n disabled={disabled}\n readOnly={readOnly}\n placeholder={selected.length === 0 ? placeholder : ''}\n autoCapitalize={autoCapitalize}\n autoCorrect={autoCorrect}\n maxLength={maxLength}\n inputMode={inputMode}\n spellCheck={spellCheck}\n enterKeyHint={enterKeyHint}\n autoFocus={autoFocus}\n tabIndex={tabIndex}\n value={query}\n onChange={(event) => {\n if (!interactive) return;\n setQuery(event.target.value);\n if (!open) setOpen(true);\n }}\n onKeyDown={composeEventHandlers(onKeyDown, handleInputKeyDown)}\n onKeyUp={composeEventHandlers(onKeyUp, listbox.onKeyUp)}\n onFocus={onFocus}\n onBlur={onBlur}\n className={cn(\n tagPickerClassNames.input,\n 'min-w-15 flex-1 bg-transparent text-foreground placeholder:text-muted-foreground focus:outline-hidden',\n sizeClasses.input,\n )}\n />\n </div>\n {/* Mounted before its text: a live region added together with its text is not announced by\n every screen reader. The row in the popup is the visible copy. */}\n <span role=\"status\" className=\"sr-only\">\n {noMatches && noMatchesText}\n </span>\n <ListboxSurface\n listbox={listbox}\n layerId={layerId}\n surfaceRef={surfaceRef}\n floatingProps={floatingProps}\n open={open}\n expanded={expanded}\n showCheck={false}\n listboxClassName={tagPickerClassNames.listbox}\n surfaceClassName={tagPickerClassNames.surface}\n emptyContent={\n query !== '' ? (\n <div\n aria-hidden=\"true\"\n className={cn(\n tagPickerClassNames.emptyMessage,\n 'px-3 py-1.5 text-body-1 text-muted-foreground',\n )}\n >\n {noMatchesText}\n </div>\n ) : undefined\n }\n aria-label={fieldProps['aria-label']}\n aria-labelledby={listLabelledBy}\n >\n {expanded &&\n listbox.items.map((item) => (\n <Option\n key={item.value}\n value={item.value}\n label={item.label}\n className={tagPickerClassNames.option}\n >\n {item.label}\n </Option>\n ))}\n </ListboxSurface>\n <HiddenInput\n name={name}\n form={form}\n disabled={disabled}\n value={selected}\n type=\"text\"\n required={validates}\n onInvalid={focusInput}\n />\n </div>\n );\n};\nTagPicker.displayName = 'TagPicker';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TagPicker.cjs","names":["TagPickerRoot","TagPickerControl","TagPickerGroup","TagPickerInput","TagPickerButton","TagPickerList","TagPickerOption","TagPickerOptionGroup","require_TagPicker_option","require_TagPicker_parts","require_TagPicker_root","TagPicker"],"sources":["../../../src/components/input/TagPicker.tsx"],"sourcesContent":["import { TagPickerRoot } from './TagPicker.root';\nimport {\n TagPickerButton,\n TagPickerControl,\n TagPickerGroup,\n TagPickerInput,\n TagPickerList,\n TagPickerOptionGroup,\n} from './TagPicker.parts';\nimport { TagPickerOption } from './TagPicker.option';\n\nexport type { TagPickerLabels, TagPickerProps, TagPickerSize } from './TagPicker.root';\nexport type {\n TagPickerButtonProps,\n TagPickerControlProps,\n TagPickerGroupProps,\n TagPickerInputProps,\n TagPickerListProps,\n} from './TagPicker.parts';\nexport type { TagPickerOptionProps } from './TagPicker.option';\n\n/**\n * A multi-select combobox that shows the selected values as removable tags.\n *\n * - **Two forms.** The array form, `<TagPicker options={…} />`, is the parts with their defaults:\n * `TagPicker.Control` (holding `TagPicker.Group` and `TagPicker.Input`) and `TagPicker.List`.\n * The parts form replaces only the part you need: your own Tags in `TagPicker.Group` (an\n * Avatar in each), rich `TagPicker.Option`s in `TagPicker.List`, or `TagPicker.Button`, a\n * select-only trigger, in place of the Input. Render `TagPicker.Control` and `TagPicker.List`\n * directly inside TagPicker (a Fragment is fine); without a List the default list is added.\n * `options` names the values in both forms (`media` and `secondaryContent` show in the default\n * options, `media` in the default tags too); with a List that has children, its options are\n * the options, and the options of selected values stay rendered (TagPicker hides them).\n * - **Keyboard.** Typing filters the options (`filter`, by default a case-insensitive match on\n * `textValue ?? label`); the option whose label equals the typed text becomes active, else the\n * first match. ArrowDown/ArrowUp move the highlight (`aria-activedescendant`), Enter adds the\n * active option and keeps the list open (with nothing active, Enter is left to the surrounding\n * form), Escape closes the list (and then clears the typed text). Backspace in the empty input,\n * and Left (Right in RTL) with the caret at its start, focus the last tag; Delete, Backspace,\n * Enter or Space on a remove button remove its tag and focus the next tag, else the input; a\n * click on one focuses the input. Additions and removals are announced (\"Cherry removed, 2\n * selected\"), and so is \"No matches\" for text that matches no option.\n * - **Free tagging.** With `allowCreate`, text that matches no option label, no selected value\n * (ignoring case) and no option value ends the list with a create option (`Create \"infra\"`);\n * Enter on it or a click adds the text as a tag after `onOptionCreate(text)` and clears the\n * text.\n * - **End buttons.** A chevron (`expandIcon`, `false` hides it) opens and closes the list without\n * taking focus; `clearable` adds a clear button, a Tab stop after the input, which removes every\n * tag. A press on the field's empty area toggles the list.\n * - **One line.** `overflow=\"count\"` keeps the tags on one line while focus is outside the picker\n * and its list is closed; the tags that do not fit give way to \"+N\".\n * - **Names.** The tags form a list named \"Selected\"; the trigger is described by a summary of the\n * selected labels (\"Selected: Apple, Banana\"). These built-in texts are English; `labels`\n * localizes them.\n * - **Routing.** The trigger (`TagPicker.Input` or `TagPicker.Button`) receives `id`,\n * `aria-label`, `aria-labelledby`, `aria-describedby`, `aria-invalid`, `aria-required`,\n * `aria-errormessage`, `aria-details`, `tabIndex` and `autoFocus`; the Input also\n * `onFocus`/`onBlur`/`onKeyDown`/`onKeyUp`, `controlRef` and the text input attributes\n * `autoComplete`, `autoCapitalize`, `autoCorrect`, `maxLength`, `inputMode`, `spellCheck` and\n * `enterKeyHint` (with the Button, pass handlers and `ref` on it). `ref`, `className`, `style`,\n * other `aria-*` attributes and the remaining props stay on the root `<div>`. Inside a `Field`\n * the trigger is labelled and described by it; the field shows the error look whenever the\n * trigger ends up `aria-invalid`. With `name`/`required` the values take part in form\n * submission, validation and reset. Selected values without a matching option show their raw\n * value.\n * - **Size and appearance**: `size` resolves from its own prop, then the surrounding `Field`'s\n * `size`, then `WaveProvider inputDefaults.size`, else `'medium'` (a Field or provider `small`\n * gives `'medium'` too, since TagPicker has no `small`); `appearance` from its own prop, then\n * `WaveProvider inputDefaults.appearance`, else `'outline'`; both render as `data-size` and\n * `data-appearance` on the root `<div>`, and the parts take neither. The tags are one Tag size\n * smaller. The option list keeps its size.\n * - **Server rendering.** The open list renders only in the browser: an open list (`defaultOpen`,\n * `open`) is closed in the server HTML and opens once the picker has hydrated; the tags of\n * registered options show their labels in the server HTML.\n *\n * Fluent's `selectedOptions` and `onOptionSelect` are `value` and `onValueChange` (plus\n * `onOptionCreate`). `TagPickerOption` is both the type of an `options` entry and the flat name of\n * `TagPicker.Option`.\n *\n * @example\n * <TagPicker aria-label=\"Fruits\" options={[{ value: 'apple', label: 'Apple' }]} clearable />\n *\n * @example\n * <TagPicker aria-label=\"People\" value={people} onValueChange={setPeople}>\n * <TagPicker.Control>\n * <TagPicker.Group>\n * {people.map((id) => (\n * <Tag key={id} value={id} media={<Avatar name={nameOf(id)} />} dismissLabel=\"Remove\">\n * {nameOf(id)}\n * </Tag>\n * ))}\n * </TagPicker.Group>\n * <TagPicker.Input />\n * </TagPicker.Control>\n * <TagPicker.List>\n * <TagPicker.Option value=\"katri\" label=\"Katri Athokas\" secondaryContent=\"Designer\">\n * Katri Athokas\n * </TagPicker.Option>\n * </TagPicker.List>\n * </TagPicker>\n */\nexport const TagPicker = /* @__PURE__ */ Object.assign(TagPickerRoot, {\n Control: TagPickerControl,\n Group: TagPickerGroup,\n Input: TagPickerInput,\n Button: TagPickerButton,\n List: TagPickerList,\n Option: TagPickerOption,\n OptionGroup: TagPickerOptionGroup,\n});\n\nexport {\n TagPickerControl,\n TagPickerGroup,\n TagPickerInput,\n TagPickerButton,\n TagPickerList,\n // The component (`TagPicker.Option`) and the interface of an `options` entry, one name (D64).\n TagPickerOption,\n TagPickerOptionGroup,\n};\n"],"mappings":"4HAqGA,IAAaW,EAA4B,OAAO,OAAOX,EAAAA,cAAe,CACpE,QAASC,EAAAA,iBACT,MAAOC,EAAAA,eACP,MAAOC,EAAAA,eACP,OAAQC,EAAAA,gBACR,KAAMC,EAAAA,cACN,OAAQC,EAAAA,gBACR,YAAaC,EAAAA,oBACf,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use client";const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../../lib/dev.cjs");let n=require("react");n=e.__toESM(n,1);var r=n.createContext(null);r.displayName=`TagPickerContext`;var i=n.createContext(null);i.displayName=`TagPickerControlContext`;function a(e){let i=n.useContext(r);return i||t.reportMissingContext(e,`TagPicker`),i}function o(e,r){let a=n.useContext(i);return!a&&r&&t.reportMissingContext(e,`TagPicker.Control`),a}exports.TagPickerContext=r,exports.TagPickerControlContext=i,exports.useTagPickerContext=a,exports.useTagPickerControlContext=o;
|
|
2
|
+
//# sourceMappingURL=TagPicker.context.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TagPicker.context.cjs","names":["React","require_runtime","require_dev","react","TagPickerContext","TagPickerControlContext","useTagPickerContext","context","part","useTagPickerControlContext","inPicker"],"sources":["../../../src/components/input/TagPicker.context.ts"],"sourcesContent":["import * as React from 'react';\nimport { reportMissingContext } from '../../lib/dev';\nimport type { CoreSize, InputAppearance, TagAppearance, TagSize } from '../../lib/types';\nimport type { UseListboxPopupResult } from './Option';\nimport type { UseListboxResult } from '../../hooks/useListbox';\nimport type { Slot } from '../../lib/slot';\nimport type { TagPickerOption } from './TagPicker.option';\nimport type { TagPickerSize } from './TagPicker.root';\n\n/*\n * The contexts of TagPicker's parts: the root's state (TagPickerContext) and the field the\n * trigger draws in (TagPickerControlContext). Module-only (never exported from a barrel); it\n * imports no component.\n */\n\n/** Which part is the combobox: the text field (`TagPicker.Input`) or a button (`TagPicker.Button`). */\nexport type TagPickerTriggerKind = 'input' | 'button';\n\n/** The built-in texts, resolved (every member set). */\nexport interface ResolvedTagPickerLabels {\n remove: (label: string) => string;\n selected: string;\n create: (text: string) => string;\n clear: string;\n expand: string;\n overflowCount: (count: number) => string;\n noMatches: string;\n}\n\n/** The attributes the root routes to its trigger part, whichever it is (C-ROUTING). */\nexport interface TagPickerTriggerProps {\n id?: string;\n 'aria-label'?: string;\n 'aria-labelledby'?: string;\n /** The root's and the Field's description (the part joins its own first and the summary last). */\n 'aria-describedby'?: string;\n 'aria-invalid'?: React.AriaAttributes['aria-invalid'];\n 'aria-required'?: React.AriaAttributes['aria-required'];\n 'aria-errormessage'?: string;\n 'aria-details'?: string;\n tabIndex?: number;\n autoFocus?: boolean;\n}\n\n/** The root's props that reach `TagPicker.Input` only (its `<input>`). */\nexport interface TagPickerInputOnlyProps {\n autoComplete?: string;\n autoCapitalize?: string;\n autoCorrect?: string;\n maxLength?: number;\n inputMode?: React.HTMLAttributes<HTMLInputElement>['inputMode'];\n spellCheck?: React.HTMLAttributes<HTMLInputElement>['spellCheck'];\n enterKeyHint?: React.InputHTMLAttributes<HTMLInputElement>['enterKeyHint'];\n onFocus?: React.FocusEventHandler<HTMLInputElement>;\n onBlur?: React.FocusEventHandler<HTMLInputElement>;\n onKeyDown?: React.KeyboardEventHandler<HTMLInputElement>;\n onKeyUp?: React.KeyboardEventHandler<HTMLInputElement>;\n controlRef?: React.Ref<HTMLInputElement>;\n}\n\n/** What TagPicker gives its parts. */\nexport interface TagPickerContextValue {\n /** The resolved size and appearance (the root's `data-size` and `data-appearance`). */\n size: TagPickerSize;\n appearance: InputAppearance;\n /** The size of the end buttons' box and glyph: medium, else large. */\n coreSize: CoreSize;\n /** The tags' size and appearance for the picker's look (D54). */\n tagSize: TagSize;\n tagAppearance: TagAppearance;\n disabled: boolean;\n readOnly: boolean;\n /** Neither disabled nor read-only. */\n interactive: boolean;\n /** Whether the field shows the error look. */\n invalidLook: boolean;\n /** The selected values, in order. */\n selected: readonly string[];\n /** The label of a value: its option's, else the raw value. */\n labelOf: (value: string) => string;\n /** The `options` entry of a value (its label, `media` and `secondaryContent`). */\n entryOf: (value: string) => TagPickerOption | undefined;\n labels: ResolvedTagPickerLabels;\n placeholder: string;\n options: readonly TagPickerOption[];\n /** Whether the default tags warn for selected values without an option (D58's 0.10 configuration). */\n warnsUnknownValues: boolean;\n /** Whether the listbox is in data mode (no `TagPicker.List` children). */\n dataMode: boolean;\n listbox: UseListboxResult;\n /** The popup (open state, list shown with options or the create option). */\n open: boolean;\n expanded: boolean;\n /** The typed text (always `''` while locked). */\n query: string;\n /** The popup surface shows: the list, or \"No matches\" for typed text. */\n surfaceOpen: boolean;\n /** The create option: shown, its text, its reserved value and its name. */\n showCreate: boolean;\n createText: string;\n createValue: string;\n /** The \"No matches\" row of the popup, while the open list has text and nothing to show. */\n emptyContent: boolean;\n layerId: string;\n surfaceRef: UseListboxPopupResult['surfaceRef'];\n floatingProps: UseListboxPopupResult['floatingProps'];\n /** The listbox's name: TagPicker's (the List's own pair replaces it). */\n listLabel: { 'aria-label'?: string; 'aria-labelledby'?: string };\n triggerProps: TagPickerTriggerProps;\n inputProps: TagPickerInputOnlyProps;\n /** The summary's id while tags exist (it describes the trigger). */\n summaryId: string | undefined;\n /** The trigger element (the Input or the Button). */\n triggerRef: React.RefObject<HTMLElement | null>;\n /** Places the popup against the Control (as wide as the field): merged into its ref. */\n setReference: (element: HTMLElement | null) => void;\n /** The control element, the tag list and the \"+N\" chip (the single-line layout measures them). */\n controlRef: React.RefObject<HTMLDivElement | null>;\n groupRef: React.RefObject<HTMLDivElement | null>;\n chipRef: React.RefObject<HTMLSpanElement | null>;\n /** Registers a trigger part from its layout effect; returns the remover. */\n registerTrigger: (kind: TagPickerTriggerKind) => () => void;\n /** Registers a List part from its layout effect; returns the remover. */\n registerList: () => () => void;\n triggerKind: TagPickerTriggerKind;\n setOpen: (open: boolean) => void;\n /** Opens a closed list, closes an open one. */\n toggleOpen: () => void;\n focusTrigger: () => void;\n /** Removes a tag (a dismissal of the tag list, D56); announced. */\n removeTag: (value: string) => void;\n /** Removes every tag, clears the text, closes the list and focuses the trigger (D61). */\n clear: () => void;\n /** A user edit of the input's text: the query, the exact-match ranking and the open state. */\n editText: (text: string) => void;\n /** Focuses the last focusable control of the tag list; `false` when there is none. */\n focusLastTagControl: () => boolean;\n /** The root's `expandIcon` and `clearable`. */\n expandIcon: Slot<'span'> | undefined;\n clearable: boolean;\n /** The single-line layout: the mode, whether it is collapsed, the hidden values and the count. */\n overflow: 'wrap' | 'count';\n collapsed: boolean;\n hiddenValues: ReadonlySet<string> | undefined;\n}\n\n/** Internal: read with {@link useTagPickerContext}. `null` outside a TagPicker. */\nexport const TagPickerContext = React.createContext<TagPickerContextValue | null>(null);\nTagPickerContext.displayName = 'TagPickerContext';\n\n/** What `TagPicker.Control` gives its trigger part. */\nexport interface TagPickerControlContextValue {\n /** Registers the trigger with the root (the kind decides the listbox mode). */\n registerTrigger: (kind: TagPickerTriggerKind) => () => void;\n}\n\n/** Internal: read with {@link useTagPickerControlContext}. `null` outside a TagPicker.Control. */\nexport const TagPickerControlContext = React.createContext<TagPickerControlContextValue | null>(\n null,\n);\nTagPickerControlContext.displayName = 'TagPickerControlContext';\n\n/**\n * TagPicker's context for one of its parts. Outside a TagPicker it throws in development\n * (`[WaveUI] TagPicker.Control must be used within TagPicker`) and logs once in production,\n * returning `null`: the part then renders nothing.\n *\n * @param part The part's dotted name (`'TagPicker.Control'`).\n */\nexport function useTagPickerContext(part: string): TagPickerContextValue | null {\n const context = React.useContext(TagPickerContext);\n if (!context) reportMissingContext(part, 'TagPicker');\n return context;\n}\n\n/**\n * The context of the field a trigger part draws in, for a part inside a TagPicker (`inPicker`).\n * Outside a TagPicker.Control it throws in development\n * (`[WaveUI] TagPicker.Input must be used within TagPicker.Control`) and logs once in production,\n * returning `null`: the part then renders nothing. Outside a TagPicker the root's check has\n * reported already, so nothing more is reported.\n *\n * @param part The part's dotted name (`'TagPicker.Input'`).\n * @param inPicker Whether the part has TagPicker's context.\n */\nexport function useTagPickerControlContext(\n part: string,\n inPicker: boolean,\n): TagPickerControlContextValue | null {\n const context = React.useContext(TagPickerControlContext);\n if (!context && inPicker) reportMissingContext(part, 'TagPicker.Control');\n return context;\n}\n"],"mappings":"4IAmJA,IAAaI,EAAmBJ,EAAM,cAA4C,IAAI,EACtF,EAAiB,YAAc,mBAS/B,IAAaK,EAA0BL,EAAM,cAC3C,IACF,EACA,EAAwB,YAAc,0BAStC,SAAgBM,EAAoB,EAA4C,CAC9E,IAAMC,EAAUP,EAAM,WAAWI,CAAgB,EAEjD,OADKG,GAASL,EAAA,qBAAqBM,EAAM,WAAW,EAC7CD,CACT,CAYA,SAAgBE,EACd,EACA,EACqC,CACrC,IAAMF,EAAUP,EAAM,WAAWK,CAAuB,EAExD,MADI,CAACE,GAAWG,GAAUR,EAAA,qBAAqBM,EAAM,mBAAmB,EACjED,CACT"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { reportMissingContext as e } from "../../lib/dev.mjs";
|
|
3
|
+
import * as t from "react";
|
|
4
|
+
//#region src/components/input/TagPicker.context.ts
|
|
5
|
+
var n = t.createContext(null);
|
|
6
|
+
n.displayName = "TagPickerContext";
|
|
7
|
+
var r = t.createContext(null);
|
|
8
|
+
r.displayName = "TagPickerControlContext";
|
|
9
|
+
function i(r) {
|
|
10
|
+
let i = t.useContext(n);
|
|
11
|
+
return i || e(r, "TagPicker"), i;
|
|
12
|
+
}
|
|
13
|
+
function a(n, i) {
|
|
14
|
+
let a = t.useContext(r);
|
|
15
|
+
return !a && i && e(n, "TagPicker.Control"), a;
|
|
16
|
+
}
|
|
17
|
+
//#endregion
|
|
18
|
+
export { n as TagPickerContext, r as TagPickerControlContext, i as useTagPickerContext, a as useTagPickerControlContext };
|
|
19
|
+
|
|
20
|
+
//# sourceMappingURL=TagPicker.context.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TagPicker.context.mjs","names":["React","TagPickerContext","TagPickerControlContext","useTagPickerContext","context","reportMissingContext","part","useTagPickerControlContext","inPicker"],"sources":["../../../src/components/input/TagPicker.context.ts"],"sourcesContent":["import * as React from 'react';\nimport { reportMissingContext } from '../../lib/dev';\nimport type { CoreSize, InputAppearance, TagAppearance, TagSize } from '../../lib/types';\nimport type { UseListboxPopupResult } from './Option';\nimport type { UseListboxResult } from '../../hooks/useListbox';\nimport type { Slot } from '../../lib/slot';\nimport type { TagPickerOption } from './TagPicker.option';\nimport type { TagPickerSize } from './TagPicker.root';\n\n/*\n * The contexts of TagPicker's parts: the root's state (TagPickerContext) and the field the\n * trigger draws in (TagPickerControlContext). Module-only (never exported from a barrel); it\n * imports no component.\n */\n\n/** Which part is the combobox: the text field (`TagPicker.Input`) or a button (`TagPicker.Button`). */\nexport type TagPickerTriggerKind = 'input' | 'button';\n\n/** The built-in texts, resolved (every member set). */\nexport interface ResolvedTagPickerLabels {\n remove: (label: string) => string;\n selected: string;\n create: (text: string) => string;\n clear: string;\n expand: string;\n overflowCount: (count: number) => string;\n noMatches: string;\n}\n\n/** The attributes the root routes to its trigger part, whichever it is (C-ROUTING). */\nexport interface TagPickerTriggerProps {\n id?: string;\n 'aria-label'?: string;\n 'aria-labelledby'?: string;\n /** The root's and the Field's description (the part joins its own first and the summary last). */\n 'aria-describedby'?: string;\n 'aria-invalid'?: React.AriaAttributes['aria-invalid'];\n 'aria-required'?: React.AriaAttributes['aria-required'];\n 'aria-errormessage'?: string;\n 'aria-details'?: string;\n tabIndex?: number;\n autoFocus?: boolean;\n}\n\n/** The root's props that reach `TagPicker.Input` only (its `<input>`). */\nexport interface TagPickerInputOnlyProps {\n autoComplete?: string;\n autoCapitalize?: string;\n autoCorrect?: string;\n maxLength?: number;\n inputMode?: React.HTMLAttributes<HTMLInputElement>['inputMode'];\n spellCheck?: React.HTMLAttributes<HTMLInputElement>['spellCheck'];\n enterKeyHint?: React.InputHTMLAttributes<HTMLInputElement>['enterKeyHint'];\n onFocus?: React.FocusEventHandler<HTMLInputElement>;\n onBlur?: React.FocusEventHandler<HTMLInputElement>;\n onKeyDown?: React.KeyboardEventHandler<HTMLInputElement>;\n onKeyUp?: React.KeyboardEventHandler<HTMLInputElement>;\n controlRef?: React.Ref<HTMLInputElement>;\n}\n\n/** What TagPicker gives its parts. */\nexport interface TagPickerContextValue {\n /** The resolved size and appearance (the root's `data-size` and `data-appearance`). */\n size: TagPickerSize;\n appearance: InputAppearance;\n /** The size of the end buttons' box and glyph: medium, else large. */\n coreSize: CoreSize;\n /** The tags' size and appearance for the picker's look (D54). */\n tagSize: TagSize;\n tagAppearance: TagAppearance;\n disabled: boolean;\n readOnly: boolean;\n /** Neither disabled nor read-only. */\n interactive: boolean;\n /** Whether the field shows the error look. */\n invalidLook: boolean;\n /** The selected values, in order. */\n selected: readonly string[];\n /** The label of a value: its option's, else the raw value. */\n labelOf: (value: string) => string;\n /** The `options` entry of a value (its label, `media` and `secondaryContent`). */\n entryOf: (value: string) => TagPickerOption | undefined;\n labels: ResolvedTagPickerLabels;\n placeholder: string;\n options: readonly TagPickerOption[];\n /** Whether the default tags warn for selected values without an option (D58's 0.10 configuration). */\n warnsUnknownValues: boolean;\n /** Whether the listbox is in data mode (no `TagPicker.List` children). */\n dataMode: boolean;\n listbox: UseListboxResult;\n /** The popup (open state, list shown with options or the create option). */\n open: boolean;\n expanded: boolean;\n /** The typed text (always `''` while locked). */\n query: string;\n /** The popup surface shows: the list, or \"No matches\" for typed text. */\n surfaceOpen: boolean;\n /** The create option: shown, its text, its reserved value and its name. */\n showCreate: boolean;\n createText: string;\n createValue: string;\n /** The \"No matches\" row of the popup, while the open list has text and nothing to show. */\n emptyContent: boolean;\n layerId: string;\n surfaceRef: UseListboxPopupResult['surfaceRef'];\n floatingProps: UseListboxPopupResult['floatingProps'];\n /** The listbox's name: TagPicker's (the List's own pair replaces it). */\n listLabel: { 'aria-label'?: string; 'aria-labelledby'?: string };\n triggerProps: TagPickerTriggerProps;\n inputProps: TagPickerInputOnlyProps;\n /** The summary's id while tags exist (it describes the trigger). */\n summaryId: string | undefined;\n /** The trigger element (the Input or the Button). */\n triggerRef: React.RefObject<HTMLElement | null>;\n /** Places the popup against the Control (as wide as the field): merged into its ref. */\n setReference: (element: HTMLElement | null) => void;\n /** The control element, the tag list and the \"+N\" chip (the single-line layout measures them). */\n controlRef: React.RefObject<HTMLDivElement | null>;\n groupRef: React.RefObject<HTMLDivElement | null>;\n chipRef: React.RefObject<HTMLSpanElement | null>;\n /** Registers a trigger part from its layout effect; returns the remover. */\n registerTrigger: (kind: TagPickerTriggerKind) => () => void;\n /** Registers a List part from its layout effect; returns the remover. */\n registerList: () => () => void;\n triggerKind: TagPickerTriggerKind;\n setOpen: (open: boolean) => void;\n /** Opens a closed list, closes an open one. */\n toggleOpen: () => void;\n focusTrigger: () => void;\n /** Removes a tag (a dismissal of the tag list, D56); announced. */\n removeTag: (value: string) => void;\n /** Removes every tag, clears the text, closes the list and focuses the trigger (D61). */\n clear: () => void;\n /** A user edit of the input's text: the query, the exact-match ranking and the open state. */\n editText: (text: string) => void;\n /** Focuses the last focusable control of the tag list; `false` when there is none. */\n focusLastTagControl: () => boolean;\n /** The root's `expandIcon` and `clearable`. */\n expandIcon: Slot<'span'> | undefined;\n clearable: boolean;\n /** The single-line layout: the mode, whether it is collapsed, the hidden values and the count. */\n overflow: 'wrap' | 'count';\n collapsed: boolean;\n hiddenValues: ReadonlySet<string> | undefined;\n}\n\n/** Internal: read with {@link useTagPickerContext}. `null` outside a TagPicker. */\nexport const TagPickerContext = React.createContext<TagPickerContextValue | null>(null);\nTagPickerContext.displayName = 'TagPickerContext';\n\n/** What `TagPicker.Control` gives its trigger part. */\nexport interface TagPickerControlContextValue {\n /** Registers the trigger with the root (the kind decides the listbox mode). */\n registerTrigger: (kind: TagPickerTriggerKind) => () => void;\n}\n\n/** Internal: read with {@link useTagPickerControlContext}. `null` outside a TagPicker.Control. */\nexport const TagPickerControlContext = React.createContext<TagPickerControlContextValue | null>(\n null,\n);\nTagPickerControlContext.displayName = 'TagPickerControlContext';\n\n/**\n * TagPicker's context for one of its parts. Outside a TagPicker it throws in development\n * (`[WaveUI] TagPicker.Control must be used within TagPicker`) and logs once in production,\n * returning `null`: the part then renders nothing.\n *\n * @param part The part's dotted name (`'TagPicker.Control'`).\n */\nexport function useTagPickerContext(part: string): TagPickerContextValue | null {\n const context = React.useContext(TagPickerContext);\n if (!context) reportMissingContext(part, 'TagPicker');\n return context;\n}\n\n/**\n * The context of the field a trigger part draws in, for a part inside a TagPicker (`inPicker`).\n * Outside a TagPicker.Control it throws in development\n * (`[WaveUI] TagPicker.Input must be used within TagPicker.Control`) and logs once in production,\n * returning `null`: the part then renders nothing. Outside a TagPicker the root's check has\n * reported already, so nothing more is reported.\n *\n * @param part The part's dotted name (`'TagPicker.Input'`).\n * @param inPicker Whether the part has TagPicker's context.\n */\nexport function useTagPickerControlContext(\n part: string,\n inPicker: boolean,\n): TagPickerControlContextValue | null {\n const context = React.useContext(TagPickerControlContext);\n if (!context && inPicker) reportMissingContext(part, 'TagPicker.Control');\n return context;\n}\n"],"mappings":";;;;AAmJA,IAAaC,IAAmBD,EAAM,cAA4C,IAAI;AACtF,EAAiB,cAAc;AAS/B,IAAaE,IAA0BF,EAAM,cAC3C,IACF;AACA,EAAwB,cAAc;AAStC,SAAgBG,EAAoB,GAA4C;CAC9E,IAAMC,IAAUJ,EAAM,WAAWC,CAAgB;CAEjD,OADKG,KAASC,EAAqBC,GAAM,WAAW,GAC7CF;AACT;AAYA,SAAgBG,EACd,GACA,GACqC;CACrC,IAAMH,IAAUJ,EAAM,WAAWE,CAAuB;CAExD,OADI,CAACE,KAAWI,KAAUH,EAAqBC,GAAM,mBAAmB,GACjEF;AACT"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use client";function e(e,t){for(let n of e)if(n.toLowerCase()===t)return!0;return!1}function t(t,n){let r=t.trim();if(r===``)return null;let i=r.toLowerCase();if(e(n.labels,i)||e(n.selected,i))return null;for(let e of n.values)if(e===r)return null;return r}function n(e,t,n){let r=e.trim().toLowerCase();if(r===``)return null;for(let{value:e,label:i}of t)if(!n.has(e)&&i.toLowerCase()===r)return e;return null}exports.createOptionText=t,exports.findExactMatch=n;
|
|
2
|
+
//# sourceMappingURL=TagPicker.create.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TagPicker.create.cjs","names":["foldedIncludes","text","texts","folded","createOptionText","query","known","value","findExactMatch","candidates","selected","label"],"sources":["../../../src/components/input/TagPicker.create.ts"],"sourcesContent":["/**\n * The pure rules of TagPicker's free tagging (`allowCreate`) and of its exact-match ranking:\n * which typed text offers a create option, and which option a typed text makes active.\n * Module-only (never exported from a barrel); it imports no component.\n */\n\n/** What the create option is checked against (case folding as the default filter's). */\nexport interface CreateOptionKnown {\n /** The labels of every known option: listed, filtered out or selected alike. */\n labels: Iterable<string>;\n /** The values of every known option, compared exactly. */\n values: Iterable<string>;\n /** The selected values (a created tag has no option), compared ignoring case. */\n selected: Iterable<string>;\n}\n\nfunction foldedIncludes(texts: Iterable<string>, folded: string): boolean {\n for (const text of texts) if (text.toLowerCase() === folded) return true;\n return false;\n}\n\n/**\n * The text a create option would add for the typed `query`, or `null` when none is offered: the\n * trimmed text, unless it is empty, equals a known option label or a selected value ignoring case\n * (`toLowerCase()`, the default filter's folding), or equals a known option value exactly\n * (creating must not select another option by its value).\n *\n * @param query The typed text.\n * @param known The labels, values and selected values to check against.\n */\nexport function createOptionText(query: string, known: CreateOptionKnown): string | null {\n const text = query.trim();\n if (text === '') return null;\n const folded = text.toLowerCase();\n if (foldedIncludes(known.labels, folded)) return null;\n if (foldedIncludes(known.selected, folded)) return null;\n for (const value of known.values) if (value === text) return null;\n return text;\n}\n\n/** An option the exact match may pick: its value and its label. */\nexport interface ExactMatchCandidate {\n value: string;\n label: string;\n}\n\n/**\n * The value of the first option in list order whose label equals the trimmed `text` ignoring case\n * and that is not selected, or `null` (no text, or no such option).\n *\n * @param text The typed text.\n * @param candidates The known options, in list order.\n * @param selected The selected values (skipped).\n */\nexport function findExactMatch(\n text: string,\n candidates: Iterable<ExactMatchCandidate>,\n selected: ReadonlySet<string>,\n): string | null {\n const folded = text.trim().toLowerCase();\n if (folded === '') return null;\n for (const { value, label } of candidates) {\n if (!selected.has(value) && label.toLowerCase() === folded) return value;\n }\n return null;\n}\n"],"mappings":"aAgBA,SAASA,EAAe,EAAyB,EAAyB,CACxE,IAAK,IAAMC,KAAQC,EAAO,GAAID,EAAK,YAAY,IAAME,EAAQ,MAAO,GACpE,MAAO,EACT,CAWA,SAAgBC,EAAiB,EAAe,EAAyC,CACvF,IAAMH,EAAOI,EAAM,KAAK,EACxB,GAAIJ,IAAS,GAAI,OAAO,KACxB,IAAME,EAASF,EAAK,YAAY,EAEhC,GADID,EAAeM,EAAM,OAAQH,CAAM,GACnCH,EAAeM,EAAM,SAAUH,CAAM,EAAG,OAAO,KACnD,IAAK,IAAMI,KAASD,EAAM,OAAQ,GAAIC,IAAUN,EAAM,OAAO,KAC7D,OAAOA,CACT,CAgBA,SAAgBO,EACd,EACA,EACA,EACe,CACf,IAAML,EAASF,EAAK,KAAK,CAAC,CAAC,YAAY,EACvC,GAAIE,IAAW,GAAI,OAAO,KAC1B,IAAK,GAAM,CAAE,QAAO,WAAWM,EAC7B,GAAI,CAACC,EAAS,IAAIH,CAAK,GAAKI,EAAM,YAAY,IAAMR,EAAQ,OAAOI,EAErE,OAAO,IACT"}
|