@deque/cauldron-react 6.12.0-canary.4505ef2e → 6.12.0-canary.67739754

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.
@@ -0,0 +1,20 @@
1
+ import { type Placement } from '@floating-ui/dom';
2
+ import React from 'react';
3
+ declare const AnchoredOverlay: React.ForwardRefExoticComponent<{
4
+ /** A target element or ref to attach the overlay anchor element. */
5
+ target: HTMLElement | React.RefObject<HTMLElement> | React.MutableRefObject<HTMLElement>;
6
+ /** Positional placement value to anchor the overlay element relative to its anchored target. */
7
+ placement?: "auto" | Placement | "auto-start" | "auto-end" | undefined;
8
+ /** Determines if the overlay anchor is currently visible. */
9
+ open?: boolean | undefined;
10
+ /** A callback function that is called when the overlay state changes. */
11
+ onOpenChange?: ((open: boolean) => void) | undefined;
12
+ /** A callback function that is called when the placement of the overlay changes. */
13
+ onPlacementChange?: ((placement: Placement) => void) | undefined;
14
+ /** An optional offset number to position the anchor element from its anchored target. */
15
+ offset?: number | undefined;
16
+ children?: React.ReactNode;
17
+ } & React.HTMLAttributes<HTMLElement> & {
18
+ as?: React.ElementType<any, keyof React.JSX.IntrinsicElements> | undefined;
19
+ } & React.RefAttributes<HTMLElement>>;
20
+ export default AnchoredOverlay;
@@ -2,15 +2,29 @@ import React from 'react';
2
2
  import type { ListboxOption } from './ListboxContext';
3
3
  import type { ListboxValue } from './ListboxOption';
4
4
  import type { PolymorphicProps, PolymorphicComponent } from '../../utils/polymorphicComponent';
5
- interface ListboxProps extends PolymorphicProps<Omit<React.HTMLAttributes<HTMLElement>, 'onSelect'>> {
6
- value?: ListboxValue;
5
+ interface BaseListboxProps extends PolymorphicProps<Omit<React.HTMLAttributes<HTMLElement>, 'onSelect' | 'defaultValue'>> {
7
6
  navigation?: 'cycle' | 'bound';
8
- onSelectionChange?: <T extends HTMLElement = HTMLElement>({ value }: {
7
+ onActiveChange?: (option: ListboxOption) => void;
8
+ }
9
+ interface SingleSelectListboxProps extends BaseListboxProps {
10
+ multiselect?: false;
11
+ value?: ListboxValue;
12
+ defaultValue?: ListboxValue;
13
+ onSelectionChange?: <T extends HTMLElement = HTMLElement>(props: {
9
14
  target: T;
10
15
  previousValue: ListboxValue;
11
16
  value: ListboxValue;
12
17
  }) => void;
13
- onActiveChange?: (option: ListboxOption) => void;
14
18
  }
15
- declare const Listbox: PolymorphicComponent<ListboxProps>;
19
+ interface MultiSelectListboxProps extends BaseListboxProps {
20
+ multiselect: true;
21
+ value?: ListboxValue[];
22
+ defaultValue?: ListboxValue[];
23
+ onSelectionChange?: <T extends HTMLElement = HTMLElement>(props: {
24
+ target: T;
25
+ previousValue: ListboxValue[];
26
+ value: ListboxValue[];
27
+ }) => void;
28
+ }
29
+ declare const Listbox: PolymorphicComponent<SingleSelectListboxProps | MultiSelectListboxProps>;
16
30
  export default Listbox;
@@ -8,7 +8,8 @@ type ListboxOption<Element = HTMLElement, Value = string | number> = {
8
8
  type ListboxContext<T extends ListboxOption> = {
9
9
  options: T[];
10
10
  active: T | null;
11
- selected: T | null;
11
+ selected: T[] | null;
12
+ multiselect: boolean;
12
13
  setOptions: React.Dispatch<React.SetStateAction<T[]>>;
13
14
  onSelect: (option: T) => void;
14
15
  };
@@ -19,9 +20,10 @@ declare const ListboxContext: React.Context<{
19
20
  options: never[];
20
21
  active: null;
21
22
  selected: null;
23
+ multiselect: boolean;
22
24
  setOptions: () => null;
23
25
  onSelect: () => null;
24
26
  }>;
25
- declare function ListboxProvider<T extends ListboxOption>({ options, active, selected, setOptions, onSelect, children }: ListboxProvider<T>): React.JSX.Element;
27
+ declare function ListboxProvider<T extends ListboxOption>({ options, active, selected, multiselect, setOptions, onSelect, children }: ListboxProvider<T>): React.JSX.Element;
26
28
  declare function useListboxContext<T extends ListboxOption>(): ListboxContext<T>;
27
29
  export { ListboxProvider, useListboxContext, ListboxOption };
@@ -4,6 +4,7 @@ export type ListboxValue = Readonly<string | number | undefined>;
4
4
  interface ListboxOptionProps extends PolymorphicProps<React.HTMLAttributes<HTMLElement>> {
5
5
  value?: ListboxValue;
6
6
  disabled?: boolean;
7
+ selected?: boolean;
7
8
  activeClass?: string;
8
9
  }
9
10
  declare const ListboxOption: PolymorphicComponent<ListboxOptionProps>;
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { Placement } from '@popperjs/core';
2
+ import type AnchoredOverlay from '../AnchoredOverlay';
3
3
  import { ContentNode } from '../../types';
4
4
  interface Props extends React.HTMLAttributes<HTMLDivElement> {
5
5
  totalItems: number;
@@ -15,7 +15,7 @@ interface Props extends React.HTMLAttributes<HTMLDivElement> {
15
15
  onPreviousPageClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
16
16
  onFirstPageClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
17
17
  onLastPageClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
18
- tooltipPlacement?: Placement;
18
+ tooltipPlacement?: React.ComponentProps<typeof AnchoredOverlay>['placement'];
19
19
  thin?: boolean;
20
20
  className?: string;
21
21
  }
@@ -1,13 +1,13 @@
1
1
  import React, { ReactNode } from 'react';
2
- import { Placement } from '@popperjs/core';
3
2
  import { Cauldron } from '../../types';
3
+ import AnchoredOverlay from '../AnchoredOverlay';
4
4
  export type PopoverVariant = 'prompt' | 'custom';
5
5
  type BaseProps = React.HTMLAttributes<HTMLDivElement> & {
6
6
  target: React.RefObject<HTMLElement> | HTMLElement;
7
7
  variant?: PopoverVariant;
8
8
  show: boolean;
9
9
  onClose: () => void;
10
- placement?: Placement;
10
+ placement?: React.ComponentProps<typeof AnchoredOverlay>['placement'];
11
11
  portal?: React.RefObject<HTMLElement> | HTMLElement;
12
12
  };
13
13
  type CustomProps = BaseProps & {
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { Placement } from '@popperjs/core';
2
+ import AnchoredOverlay from '../AnchoredOverlay';
3
3
  export interface TooltipProps extends React.HTMLAttributes<HTMLDivElement> {
4
4
  children: React.ReactNode;
5
5
  className?: string;
@@ -8,7 +8,7 @@ export interface TooltipProps extends React.HTMLAttributes<HTMLDivElement> {
8
8
  association?: 'aria-labelledby' | 'aria-describedby' | 'none';
9
9
  show?: boolean | undefined;
10
10
  defaultShow?: boolean;
11
- placement?: Placement;
11
+ placement?: React.ComponentProps<typeof AnchoredOverlay>['placement'];
12
12
  portal?: React.RefObject<HTMLElement> | HTMLElement;
13
13
  hideElementOnHidden?: boolean;
14
14
  }
package/lib/index.d.ts CHANGED
@@ -61,6 +61,7 @@ export { default as TextEllipsis } from './components/TextEllipsis';
61
61
  export { default as CopyButton } from './components/CopyButton';
62
62
  export { default as Drawer } from './components/Drawer';
63
63
  export { default as BottomSheet } from './components/BottomSheet';
64
+ export { default as AnchoredOverlay } from './components/AnchoredOverlay';
64
65
  /**
65
66
  * Helpers / Utils
66
67
  */
package/lib/index.js CHANGED
@@ -9,7 +9,8 @@ var nextId = require('react-id-generator');
9
9
  var keyname = require('keyname');
10
10
  var reactDom = require('react-dom');
11
11
  var FocusTrap = require('focus-trap-react');
12
- var reactPopper = require('react-popper');
12
+ var dom = require('@floating-ui/dom');
13
+ var reactDom$1 = require('@floating-ui/react-dom');
13
14
  var SyntaxHighlighter = require('react-syntax-highlighter/dist/cjs/light');
14
15
  var js = require('react-syntax-highlighter/dist/cjs/languages/hljs/javascript');
15
16
  var css = require('react-syntax-highlighter/dist/cjs/languages/hljs/css');
@@ -1584,40 +1585,25 @@ var Button = React.forwardRef(function (_a, ref) {
1584
1585
  Button.displayName = 'Button';
1585
1586
 
1586
1587
  /**
1587
- * Returns a unique set of id refs from the provided string
1588
- * @param ids - string of id refs
1589
- */
1590
- function idRefs(ids) {
1591
- if (!ids || !ids.trim()) {
1592
- return new Set();
1593
- }
1594
- return new Set(ids.trim().split(/\s+/));
1595
- }
1596
- /**
1597
- * Returns an updated id ref string with the provided id value added
1598
- * @param ids - string of id refs
1599
- * @param id - id to add
1600
- */
1601
- function addIdRef(ids, id) {
1602
- return tslib.__spreadArray([], tslib.__read(idRefs(ids).add(id)), false).join(' ');
1603
- }
1604
- /**
1605
- * Returns an updated id ref string with the provided id value removed
1606
- * @param ids - string of id refs
1607
- * @param id - id to remove
1608
- */
1609
- function removeIdRef(_ids, id) {
1610
- var ids = idRefs(_ids);
1611
- ids.delete(id);
1612
- return tslib.__spreadArray([], tslib.__read(ids), false).join(' ');
1613
- }
1614
- /**
1615
- * Returns if an id ref string contains the provided id value
1616
- * @param ids - string of id refs
1617
- * @param id - id to check if it exists in the provided idRef string
1588
+ * When a component needs to track an internal ref on a component that has a
1589
+ * forwarded ref, useSharedRef will return a MutableRefObject<T> that will
1590
+ * correctly invoke the parent ref as well providing an internal ref.
1591
+ *
1592
+ * @example
1593
+ * React.forwardRef(function Component({ children }, ref) {
1594
+ * const internalRef = useSharedRef<HTMLElement>(ref)
1595
+ * if (internalRef.current) {
1596
+ * // do something with the internal ref...
1597
+ * }
1598
+ * return (<div ref={internalRef}>...</div>)
1599
+ * })
1618
1600
  */
1619
- function hasIdRef(ids, id) {
1620
- return idRefs(ids).has(id);
1601
+ function useSharedRef(ref) {
1602
+ var internalRef = React.useRef();
1603
+ React.useEffect(function () {
1604
+ setRef(ref, internalRef.current);
1605
+ }, [ref]);
1606
+ return internalRef;
1621
1607
  }
1622
1608
 
1623
1609
  var isEscapeKey = function (event) {
@@ -1660,6 +1646,99 @@ function useEscapeKey(options, dependencies) {
1660
1646
  ], tslib.__read(dependencies), false));
1661
1647
  }
1662
1648
 
1649
+ function getAutoAlignment(placement) {
1650
+ switch (placement) {
1651
+ case 'auto-start':
1652
+ return 'start';
1653
+ case 'auto-end':
1654
+ return 'end';
1655
+ default:
1656
+ return null;
1657
+ }
1658
+ }
1659
+ var AnchoredOverlay = React.forwardRef(function (_a, refProp) {
1660
+ var as = _a.as, _b = _a.placement, initialPlacement = _b === void 0 ? 'auto' : _b, target = _a.target, children = _a.children, style = _a.style, _c = _a.open, open = _c === void 0 ? false : _c, offset = _a.offset, onOpenChange = _a.onOpenChange, onPlacementChange = _a.onPlacementChange, props = tslib.__rest(_a, ["as", "placement", "target", "children", "style", "open", "offset", "onOpenChange", "onPlacementChange"]);
1661
+ var ref = useSharedRef(refProp);
1662
+ var Component = as || 'div';
1663
+ var _d = reactDom$1.useFloating({
1664
+ open: open,
1665
+ // default to initial placement on top when placement is auto
1666
+ // @ts-expect-error auto placement is not a valid placement for floating-ui
1667
+ placement: initialPlacement.startsWith('auto') ? 'top' : initialPlacement,
1668
+ middleware: [
1669
+ reactDom$1.offset(offset !== null && offset !== void 0 ? offset : 0),
1670
+ initialPlacement.startsWith('auto')
1671
+ ? reactDom$1.autoPlacement({
1672
+ alignment: getAutoAlignment(initialPlacement)
1673
+ })
1674
+ : reactDom$1.flip()
1675
+ ].filter(Boolean),
1676
+ elements: {
1677
+ reference: resolveElement(target),
1678
+ floating: ref.current
1679
+ },
1680
+ whileElementsMounted: dom.autoUpdate
1681
+ }), floatingStyles = _d.floatingStyles, placement = _d.placement;
1682
+ useEscapeKey({
1683
+ active: open,
1684
+ capture: true,
1685
+ defaultPrevented: true,
1686
+ callback: function (event) {
1687
+ // when an anchored overlay is open, we want to prevent other potential "escape"
1688
+ // keypress events, like the closing of modals from occurring
1689
+ event.preventDefault();
1690
+ // istanbul ignore else
1691
+ if (typeof onOpenChange === 'function') {
1692
+ onOpenChange(!open);
1693
+ }
1694
+ }
1695
+ });
1696
+ React.useEffect(function () {
1697
+ if (typeof onPlacementChange === 'function') {
1698
+ onPlacementChange(placement);
1699
+ }
1700
+ }, [placement]);
1701
+ return (React__default["default"].createElement(Component, tslib.__assign({ ref: ref }, props, { style: tslib.__assign(tslib.__assign({}, floatingStyles), style) }), children));
1702
+ });
1703
+ AnchoredOverlay.displayName = 'AnchoredOverlay';
1704
+
1705
+ /**
1706
+ * Returns a unique set of id refs from the provided string
1707
+ * @param ids - string of id refs
1708
+ */
1709
+ function idRefs(ids) {
1710
+ if (!ids || !ids.trim()) {
1711
+ return new Set();
1712
+ }
1713
+ return new Set(ids.trim().split(/\s+/));
1714
+ }
1715
+ /**
1716
+ * Returns an updated id ref string with the provided id value added
1717
+ * @param ids - string of id refs
1718
+ * @param id - id to add
1719
+ */
1720
+ function addIdRef(ids, id) {
1721
+ return tslib.__spreadArray([], tslib.__read(idRefs(ids).add(id)), false).join(' ');
1722
+ }
1723
+ /**
1724
+ * Returns an updated id ref string with the provided id value removed
1725
+ * @param ids - string of id refs
1726
+ * @param id - id to remove
1727
+ */
1728
+ function removeIdRef(_ids, id) {
1729
+ var ids = idRefs(_ids);
1730
+ ids.delete(id);
1731
+ return tslib.__spreadArray([], tslib.__read(ids), false).join(' ');
1732
+ }
1733
+ /**
1734
+ * Returns if an id ref string contains the provided id value
1735
+ * @param ids - string of id refs
1736
+ * @param id - id to check if it exists in the provided idRef string
1737
+ */
1738
+ function hasIdRef(ids, id) {
1739
+ return idRefs(ids).has(id);
1740
+ }
1741
+
1663
1742
  var TIP_HIDE_DELAY = 100;
1664
1743
  // fires a custom "cauldron:tooltip:show" / "cauldron:tooltip:hide" event
1665
1744
  // to allow projects using cauldron to hook into when a tooltip is shown/hidden
@@ -1678,50 +1757,27 @@ function Tooltip(_a) {
1678
1757
  var _g = tslib.__read(propId ? [propId] : nextId.useId(1, 'tooltip'), 1), id = _g[0];
1679
1758
  var hideTimeoutRef = React.useRef(null);
1680
1759
  var _h = tslib.__read(React.useState(!!showProp || defaultShow), 2), showTooltip = _h[0], setShowTooltip = _h[1];
1681
- var _j = tslib.__read(React.useState(null), 2), targetElement = _j[0], setTargetElement = _j[1];
1682
- var _k = tslib.__read(React.useState(null), 2), tooltipElement = _k[0], setTooltipElement = _k[1];
1683
- var _l = tslib.__read(React.useState(null), 2), arrowElement = _l[0], setArrowElement = _l[1];
1760
+ var _j = tslib.__read(React.useState(null), 2), tooltipElement = _j[0], setTooltipElement = _j[1];
1761
+ var _k = tslib.__read(React.useState(initialPlacement), 2), placement = _k[0], setPlacement = _k[1];
1684
1762
  var hasAriaAssociation = association !== 'none';
1685
- var _m = reactPopper.usePopper(targetElement, tooltipElement, {
1686
- placement: initialPlacement,
1687
- modifiers: [
1688
- { name: 'preventOverflow', options: { padding: 8 } },
1689
- {
1690
- name: 'flip',
1691
- options: { fallbackPlacements: ['left', 'right', 'top', 'bottom'] }
1692
- },
1693
- { name: 'offset', options: { offset: [0, 8] } },
1694
- { name: 'arrow', options: { padding: 5, element: arrowElement } }
1695
- ]
1696
- }), styles = _m.styles, attributes = _m.attributes, update = _m.update;
1697
1763
  // Show the tooltip
1698
1764
  var show = React.useCallback(function () { return tslib.__awaiter(_this, void 0, void 0, function () {
1765
+ var targetElement;
1699
1766
  return tslib.__generator(this, function (_a) {
1700
- switch (_a.label) {
1701
- case 0:
1702
- // Clear the hide timeout if there was one pending
1703
- if (hideTimeoutRef.current) {
1704
- clearTimeout(hideTimeoutRef.current);
1705
- hideTimeoutRef.current = null;
1706
- }
1707
- if (!update) return [3 /*break*/, 2];
1708
- return [4 /*yield*/, update()];
1709
- case 1:
1710
- _a.sent();
1711
- _a.label = 2;
1712
- case 2:
1713
- setShowTooltip(true);
1714
- fireCustomEvent(true, targetElement);
1715
- return [2 /*return*/];
1767
+ targetElement = resolveElement(target);
1768
+ // Clear the hide timeout if there was one pending
1769
+ if (hideTimeoutRef.current) {
1770
+ clearTimeout(hideTimeoutRef.current);
1771
+ hideTimeoutRef.current = null;
1716
1772
  }
1773
+ setShowTooltip(true);
1774
+ fireCustomEvent(true, targetElement);
1775
+ return [2 /*return*/];
1717
1776
  });
1718
- }); }, [
1719
- targetElement,
1720
- // update starts off as null
1721
- update
1722
- ]);
1777
+ }); }, [target]);
1723
1778
  // Hide the tooltip
1724
1779
  var hide = React.useCallback(function () {
1780
+ var targetElement = resolveElement(target);
1725
1781
  if (!hideTimeoutRef.current) {
1726
1782
  hideTimeoutRef.current = setTimeout(function () {
1727
1783
  hideTimeoutRef.current = null;
@@ -1732,32 +1788,15 @@ function Tooltip(_a) {
1732
1788
  return function () {
1733
1789
  clearTimeout(hideTimeoutRef.current);
1734
1790
  };
1735
- }, [targetElement]);
1736
- // Keep targetElement in sync with target prop
1737
- React.useEffect(function () {
1738
- var targetElement = target && 'current' in target ? target.current : target;
1739
- setTargetElement(targetElement);
1740
1791
  }, [target]);
1741
1792
  React.useEffect(function () {
1742
1793
  if (typeof showProp === 'boolean') {
1743
1794
  setShowTooltip(showProp);
1744
1795
  }
1745
1796
  }, [showProp]);
1746
- // Get popper placement
1747
- var placement = (attributes.popper &&
1748
- attributes.popper['data-popper-placement']) ||
1749
- initialPlacement;
1750
- // Only listen to key ups when the tooltip is visible
1751
- useEscapeKey({
1752
- callback: function (event) {
1753
- event.preventDefault();
1754
- setShowTooltip(false);
1755
- },
1756
- capture: true,
1757
- active: showTooltip && typeof showProp !== 'boolean'
1758
- }, [setShowTooltip]);
1759
1797
  // Handle hover and focus events for the targetElement
1760
1798
  React.useEffect(function () {
1799
+ var targetElement = resolveElement(target);
1761
1800
  if (typeof showProp !== 'boolean') {
1762
1801
  targetElement === null || targetElement === void 0 ? void 0 : targetElement.addEventListener('mouseenter', show);
1763
1802
  targetElement === null || targetElement === void 0 ? void 0 : targetElement.addEventListener('mouseleave', hide);
@@ -1770,7 +1809,7 @@ function Tooltip(_a) {
1770
1809
  targetElement === null || targetElement === void 0 ? void 0 : targetElement.removeEventListener('focusin', show);
1771
1810
  targetElement === null || targetElement === void 0 ? void 0 : targetElement.removeEventListener('focusout', hide);
1772
1811
  };
1773
- }, [targetElement, show, hide, showProp]);
1812
+ }, [target, show, hide, showProp]);
1774
1813
  // Handle hover events for the tooltipElement
1775
1814
  React.useEffect(function () {
1776
1815
  if (typeof showProp !== 'boolean') {
@@ -1784,6 +1823,7 @@ function Tooltip(_a) {
1784
1823
  }, [tooltipElement, show, hide, showProp]);
1785
1824
  // Keep the target's id in sync
1786
1825
  React.useEffect(function () {
1826
+ var targetElement = resolveElement(target);
1787
1827
  if (hasAriaAssociation) {
1788
1828
  var idRefs = targetElement === null || targetElement === void 0 ? void 0 : targetElement.getAttribute(association);
1789
1829
  if (!hasIdRef(idRefs, id)) {
@@ -1796,14 +1836,14 @@ function Tooltip(_a) {
1796
1836
  targetElement.setAttribute(association, removeIdRef(idRefs, id));
1797
1837
  }
1798
1838
  };
1799
- }, [targetElement, id, association]);
1839
+ }, [target, id, association]);
1800
1840
  return (React__default["default"].createElement(React__default["default"].Fragment, null, (showTooltip || hideElementOnHidden) && isBrowser()
1801
- ? reactDom.createPortal(React__default["default"].createElement("div", tslib.__assign({ id: id, className: classNames__default["default"]('Tooltip', "Tooltip--".concat(placement), className, {
1841
+ ? reactDom.createPortal(React__default["default"].createElement(AnchoredOverlay, tslib.__assign({ id: id, target: target, placement: initialPlacement, onPlacementChange: setPlacement, open: showTooltip && typeof showProp !== 'boolean', onOpenChange: setShowTooltip, className: classNames__default["default"]('Tooltip', "Tooltip--".concat(placement), className, {
1802
1842
  TooltipInfo: variant === 'info',
1803
1843
  'Tooltip--hidden': !showTooltip && hideElementOnHidden,
1804
1844
  'Tooltip--big': variant === 'big'
1805
- }), ref: setTooltipElement, role: "tooltip", style: styles.popper }, attributes.popper, props),
1806
- variant !== 'big' && (React__default["default"].createElement("div", { className: "TooltipArrow", ref: setArrowElement, style: styles.arrow })),
1845
+ }), ref: setTooltipElement, role: "tooltip", offset: 8 }, props),
1846
+ variant !== 'big' && React__default["default"].createElement("div", { className: "TooltipArrow" }),
1807
1847
  children), (portal && 'current' in portal ? portal.current : portal) ||
1808
1848
  (
1809
1849
  // eslint-disable-next-line ssr-friendly/no-dom-globals-in-react-fc
@@ -2519,28 +2559,6 @@ var SearchField = React.forwardRef(function (_a, ref) {
2519
2559
  });
2520
2560
  SearchField.displayName = 'SearchField';
2521
2561
 
2522
- /**
2523
- * When a component needs to track an internal ref on a component that has a
2524
- * forwarded ref, useSharedRef will return a MutableRefObject<T> that will
2525
- * correctly invoke the parent ref as well providing an internal ref.
2526
- *
2527
- * @example
2528
- * React.forwardRef(function Component({ children }, ref) {
2529
- * const internalRef = useSharedRef<HTMLElement>(ref)
2530
- * if (internalRef.current) {
2531
- * // do something with the internal ref...
2532
- * }
2533
- * return (<div ref={internalRef}>...</div>)
2534
- * })
2535
- */
2536
- function useSharedRef(ref) {
2537
- var internalRef = React.useRef();
2538
- React.useEffect(function () {
2539
- setRef(ref, internalRef.current);
2540
- }, [ref]);
2541
- return internalRef;
2542
- }
2543
-
2544
2562
  function copyTextToClipboard(text) {
2545
2563
  return tslib.__awaiter(this, void 0, void 0, function () {
2546
2564
  var copied, element, range, selection;
@@ -3444,19 +3462,21 @@ var ListboxContext = React.createContext({
3444
3462
  options: [],
3445
3463
  active: null,
3446
3464
  selected: null,
3465
+ multiselect: false,
3447
3466
  setOptions: function () { return null; },
3448
3467
  onSelect: function () { return null; }
3449
3468
  });
3450
3469
  function ListboxProvider(_a) {
3451
- var options = _a.options, active = _a.active, selected = _a.selected, setOptions = _a.setOptions, onSelect = _a.onSelect, children = _a.children;
3470
+ var options = _a.options, active = _a.active, selected = _a.selected, multiselect = _a.multiselect, setOptions = _a.setOptions, onSelect = _a.onSelect, children = _a.children;
3452
3471
  var Provider = ListboxContext.Provider;
3453
3472
  var value = React.useMemo(function () { return ({
3454
3473
  options: options,
3455
3474
  active: active,
3456
3475
  selected: selected,
3476
+ multiselect: multiselect,
3457
3477
  setOptions: setOptions,
3458
3478
  onSelect: onSelect
3459
- }); }, [options, active, selected, setOptions]);
3479
+ }); }, [options, active, selected, multiselect, setOptions]);
3460
3480
  return React__default["default"].createElement(Provider, { value: value }, children);
3461
3481
  }
3462
3482
  function useListboxContext() {
@@ -3478,40 +3498,78 @@ var optionMatchesValue = function (option, value) {
3478
3498
  option.value === value;
3479
3499
  };
3480
3500
  var Listbox = React.forwardRef(function (_a, ref) {
3481
- var _b = _a.as, Component = _b === void 0 ? 'ul' : _b, children = _a.children, defaultValue = _a.defaultValue, value = _a.value, _c = _a.navigation, navigation = _c === void 0 ? 'bound' : _c, onKeyDown = _a.onKeyDown, onFocus = _a.onFocus, onSelectionChange = _a.onSelectionChange, onActiveChange = _a.onActiveChange, props = tslib.__rest(_a, ["as", "children", "defaultValue", "value", "navigation", "onKeyDown", "onFocus", "onSelectionChange", "onActiveChange"]);
3482
- var _d = tslib.__read(React.useState([]), 2), options = _d[0], setOptions = _d[1];
3483
- var _e = tslib.__read(React.useState(null), 2), activeOption = _e[0], setActiveOption = _e[1];
3484
- var _f = tslib.__read(React.useState(null), 2), selectedOption = _f[0], setSelectedOption = _f[1];
3501
+ var _b = _a.as, Component = _b === void 0 ? 'ul' : _b, children = _a.children, defaultValue = _a.defaultValue, value = _a.value, _c = _a.navigation, navigation = _c === void 0 ? 'bound' : _c, _d = _a.multiselect, multiselect = _d === void 0 ? false : _d, onKeyDown = _a.onKeyDown, onFocus = _a.onFocus, onSelectionChange = _a.onSelectionChange, onActiveChange = _a.onActiveChange, props = tslib.__rest(_a, ["as", "children", "defaultValue", "value", "navigation", "multiselect", "onKeyDown", "onFocus", "onSelectionChange", "onActiveChange"]);
3502
+ var _e = tslib.__read(React.useState([]), 2), options = _e[0], setOptions = _e[1];
3503
+ var _f = tslib.__read(React.useState(null), 2), activeOption = _f[0], setActiveOption = _f[1];
3504
+ var _g = tslib.__read(React.useState([]), 2), selectedOptions = _g[0], setSelectedOptions = _g[1];
3485
3505
  var listboxRef = useSharedRef(ref);
3486
3506
  var isControlled = typeof value !== 'undefined';
3487
3507
  React.useLayoutEffect(function () {
3488
- if (!isControlled && selectedOption) {
3508
+ if (!isControlled && selectedOptions.length > 0) {
3489
3509
  return;
3490
3510
  }
3491
3511
  var listboxValue = isControlled ? value : defaultValue;
3492
- var matchingOption = options.find(function (option) {
3493
- return optionMatchesValue(option, listboxValue);
3494
- });
3495
- setSelectedOption(matchingOption || null);
3496
- setActiveOption(matchingOption || null);
3497
- }, [isControlled, options, value]);
3512
+ if (!listboxValue) {
3513
+ return;
3514
+ }
3515
+ if (multiselect) {
3516
+ var matchingOptions = options.filter(function (option) {
3517
+ return listboxValue.find(function (value) {
3518
+ return optionMatchesValue(option, value);
3519
+ });
3520
+ });
3521
+ setSelectedOptions(matchingOptions);
3522
+ setActiveOption(matchingOptions[0] || null);
3523
+ }
3524
+ else {
3525
+ var matchingOption = options.find(function (option) {
3526
+ return optionMatchesValue(option, listboxValue);
3527
+ });
3528
+ setSelectedOptions(matchingOption ? [matchingOption] : []);
3529
+ setActiveOption(matchingOption || null);
3530
+ }
3531
+ }, [isControlled, options, value, defaultValue]);
3498
3532
  React.useEffect(function () {
3499
3533
  if (activeOption) {
3500
3534
  onActiveChange === null || onActiveChange === void 0 ? void 0 : onActiveChange(activeOption);
3501
3535
  }
3502
3536
  }, [activeOption]);
3503
3537
  var handleSelect = React.useCallback(function (option) {
3538
+ var _a;
3504
3539
  setActiveOption(option);
3540
+ var optionIsSelected = selectedOptions.some(function (selected) { return selected.element === option.element; });
3541
+ var previousValues = selectedOptions.map(function (selected) { return selected.value; });
3505
3542
  // istanbul ignore else
3506
3543
  if (!isControlled) {
3507
- setSelectedOption(option);
3544
+ if (!multiselect) {
3545
+ setSelectedOptions([option]);
3546
+ }
3547
+ else {
3548
+ setSelectedOptions(optionIsSelected
3549
+ ? tslib.__spreadArray([], tslib.__read(selectedOptions.filter(function (selected) { return selected.element !== option.element; })), false) : tslib.__spreadArray(tslib.__spreadArray([], tslib.__read(selectedOptions), false), [option], false));
3550
+ }
3508
3551
  }
3509
- onSelectionChange === null || onSelectionChange === void 0 ? void 0 : onSelectionChange({
3510
- target: option.element,
3511
- value: option.value,
3512
- previousValue: selectedOption === null || selectedOption === void 0 ? void 0 : selectedOption.value
3513
- });
3514
- }, [isControlled, selectedOption]);
3552
+ if (multiselect) {
3553
+ onSelectionChange === null || onSelectionChange === void 0 ? void 0 : onSelectionChange({
3554
+ target: option.element,
3555
+ value: optionIsSelected
3556
+ ? selectedOptions
3557
+ .filter(function (selectedOption) {
3558
+ return selectedOption.element !== option.element;
3559
+ })
3560
+ .map(function (selectedOption) { return selectedOption.value; })
3561
+ : tslib.__spreadArray(tslib.__spreadArray([], tslib.__read(previousValues), false), [option.value], false),
3562
+ previousValue: previousValues
3563
+ });
3564
+ }
3565
+ else {
3566
+ onSelectionChange === null || onSelectionChange === void 0 ? void 0 : onSelectionChange({
3567
+ target: option.element,
3568
+ value: option.value,
3569
+ previousValue: (_a = selectedOptions[0]) === null || _a === void 0 ? void 0 : _a.value
3570
+ });
3571
+ }
3572
+ }, [isControlled, selectedOptions, multiselect, onSelectionChange]);
3515
3573
  var handleKeyDown = React.useCallback(function (event) {
3516
3574
  onKeyDown === null || onKeyDown === void 0 ? void 0 : onKeyDown(event);
3517
3575
  if (!keys.includes(event.key)) {
@@ -3561,9 +3619,9 @@ var Listbox = React.forwardRef(function (_a, ref) {
3561
3619
  activeOption && handleSelect(activeOption);
3562
3620
  break;
3563
3621
  }
3564
- }, [options, activeOption, navigation]);
3622
+ }, [options, activeOption, navigation, handleSelect]);
3565
3623
  var handleFocus = React.useCallback(function (event) {
3566
- if (!activeOption && !selectedOption) {
3624
+ if (!activeOption) {
3567
3625
  var firstOption = options.find(function (option) { return !isDisabledOption(option); });
3568
3626
  // istanbul ignore else
3569
3627
  if (firstOption) {
@@ -3571,13 +3629,14 @@ var Listbox = React.forwardRef(function (_a, ref) {
3571
3629
  }
3572
3630
  // istanbul ignore else
3573
3631
  }
3574
- else if (event.target === listboxRef.current) {
3575
- setActiveOption(selectedOption);
3632
+ else if (selectedOptions.length &&
3633
+ event.target === listboxRef.current) {
3634
+ setActiveOption(selectedOptions[selectedOptions.length - 1]);
3576
3635
  }
3577
3636
  onFocus === null || onFocus === void 0 ? void 0 : onFocus(event);
3578
- }, [options, activeOption, selectedOption]);
3579
- return (React__default["default"].createElement(Component, tslib.__assign({ role: "listbox", ref: listboxRef, tabIndex: "0", onKeyDown: handleKeyDown, onFocus: handleFocus, "aria-activedescendant": activeOption ? getOptionId(activeOption) : undefined }, props),
3580
- React__default["default"].createElement(ListboxProvider, { options: options, active: activeOption, selected: selectedOption, setOptions: setOptions, onSelect: handleSelect }, children)));
3637
+ }, [options, activeOption, selectedOptions]);
3638
+ return (React__default["default"].createElement(Component, tslib.__assign({ role: "listbox", ref: listboxRef, tabIndex: "0", onKeyDown: handleKeyDown, onFocus: handleFocus, "aria-multiselectable": multiselect ? true : undefined, "aria-activedescendant": activeOption ? getOptionId(activeOption) : undefined }, props),
3639
+ React__default["default"].createElement(ListboxProvider, { options: options, active: activeOption, multiselect: multiselect, selected: selectedOptions, setOptions: setOptions, onSelect: handleSelect }, children)));
3581
3640
  });
3582
3641
  Listbox.displayName = 'Listbox';
3583
3642
 
@@ -3587,12 +3646,15 @@ function isElementPreceding(a, b) {
3587
3646
  var ListboxOption = React.forwardRef(function (_a, ref) {
3588
3647
  var _b;
3589
3648
  var _c;
3590
- var propId = _a.id, className = _a.className, _d = _a.as, Component = _d === void 0 ? 'li' : _d, children = _a.children, value = _a.value, disabled = _a.disabled, _e = _a.activeClass, activeClass = _e === void 0 ? 'ListboxOption--active' : _e, onClick = _a.onClick, props = tslib.__rest(_a, ["id", "className", "as", "children", "value", "disabled", "activeClass", "onClick"]);
3649
+ var propId = _a.id, className = _a.className, _d = _a.as, Component = _d === void 0 ? 'li' : _d, children = _a.children, value = _a.value, disabled = _a.disabled, selectedProp = _a.selected, _e = _a.activeClass, activeClass = _e === void 0 ? 'ListboxOption--active' : _e, onClick = _a.onClick, props = tslib.__rest(_a, ["id", "className", "as", "children", "value", "disabled", "selected", "activeClass", "onClick"]);
3591
3650
  var _f = useListboxContext(), active = _f.active, selected = _f.selected, setOptions = _f.setOptions, onSelect = _f.onSelect;
3592
3651
  var listboxOptionRef = useSharedRef(ref);
3593
3652
  var _g = tslib.__read(propId ? [propId] : nextId.useId(1, 'listbox-option'), 1), id = _g[0];
3594
- var isActive = active !== null && active.element === listboxOptionRef.current;
3595
- var isSelected = selected !== null && selected.element === listboxOptionRef.current;
3653
+ var isActive = (active === null || active === void 0 ? void 0 : active.element) === listboxOptionRef.current;
3654
+ var isSelected = typeof selectedProp === 'boolean'
3655
+ ? selectedProp
3656
+ : selected !== null &&
3657
+ !!selected.find(function (option) { return option.element === listboxOptionRef.current; });
3596
3658
  var optionValue = typeof value !== 'undefined'
3597
3659
  ? value
3598
3660
  : (_c = listboxOptionRef.current) === null || _c === void 0 ? void 0 : _c.innerText;
@@ -3642,7 +3704,7 @@ var ListboxOption = React.forwardRef(function (_a, ref) {
3642
3704
  }
3643
3705
  onSelect({ element: listboxOptionRef.current, value: optionValue });
3644
3706
  onClick === null || onClick === void 0 ? void 0 : onClick(event);
3645
- }, [optionValue]);
3707
+ }, [optionValue, onSelect, onClick, disabled]);
3646
3708
  return (React__default["default"].createElement(Component, tslib.__assign({ id: id, className: classNames__default["default"](className, (_b = {},
3647
3709
  _b[activeClass] = isActive,
3648
3710
  _b)), role: "option", ref: listboxOptionRef, "aria-disabled": typeof disabled === 'boolean' ? disabled : undefined, "aria-selected": isSelected, onClick: handleClick }, props), children));
@@ -3764,17 +3826,20 @@ var ComboboxMatch = function (_a) {
3764
3826
  React__default["default"].createElement("span", null, matchAfter)));
3765
3827
  };
3766
3828
  var ComboboxOption = React.forwardRef(function (_a, ref) {
3829
+ var _b;
3767
3830
  var className = _a.className, children = _a.children, disabled = _a.disabled, propId = _a.id, description = _a.description, propValue = _a.value, formValue = _a.formValue, props = tslib.__rest(_a, ["className", "children", "disabled", "id", "description", "value", "formValue"]);
3768
- var _b = tslib.__read(propId ? [propId] : nextId.useId(1, 'combobox-option'), 1), id = _b[0];
3769
- var _c = useListboxContext(), selected = _c.selected, active = _c.active;
3770
- var _d = useComboboxContext(), selectedValue = _d.selectedValue, matches = _d.matches, setMatchingOptions = _d.setMatchingOptions, setFormValue = _d.setFormValue;
3831
+ var _c = tslib.__read(propId ? [propId] : nextId.useId(1, 'combobox-option'), 1), id = _c[0];
3832
+ var _d = useListboxContext(), selected = _d.selected, active = _d.active;
3833
+ var _e = useComboboxContext(), selectedValue = _e.selectedValue, matches = _e.matches, setMatchingOptions = _e.setMatchingOptions, setFormValue = _e.setFormValue;
3771
3834
  var comboboxOptionRef = useSharedRef(ref);
3772
3835
  var intersectionRef = useIntersectionRef(comboboxOptionRef, {
3773
3836
  root: null,
3774
3837
  threshold: 1.0
3775
3838
  });
3776
3839
  var isActive = !!(active === null || active === void 0 ? void 0 : active.element) && active.element === comboboxOptionRef.current;
3777
- var isSelected = !!(selected === null || selected === void 0 ? void 0 : selected.element) && selected.element === comboboxOptionRef.current;
3840
+ var isSelected = !!(selected &&
3841
+ !!((_b = selected[0]) === null || _b === void 0 ? void 0 : _b.element) &&
3842
+ selected[0].element === comboboxOptionRef.current);
3778
3843
  var isMatching = (typeof matches === 'boolean' && matches) ||
3779
3844
  (typeof matches === 'function' && matches(children));
3780
3845
  // istanbul ignore next
@@ -4110,19 +4175,7 @@ var Popover = React.forwardRef(function (_a, ref) {
4110
4175
  var _f = tslib.__read(React.useState(null), 2), targetElement = _f[0], setTargetElement = _f[1];
4111
4176
  var _g = tslib.__read(React.useState(null), 2), isolator = _g[0], setIsolator = _g[1];
4112
4177
  var popoverRef = useSharedRef(ref);
4113
- var _h = tslib.__read(React.useState(null), 2), arrowElement = _h[0], setArrowElement = _h[1];
4114
- var _j = reactPopper.usePopper(targetElement, popoverRef === null || popoverRef === void 0 ? void 0 : popoverRef.current, {
4115
- placement: initialPlacement,
4116
- modifiers: [
4117
- { name: 'preventOverflow', options: { padding: 8 } },
4118
- { name: 'flip' },
4119
- { name: 'offset', options: { offset: [0, 8] } },
4120
- { name: 'arrow', options: { padding: 5, element: arrowElement } }
4121
- ]
4122
- }), styles = _j.styles, attributes = _j.attributes;
4123
- var placement = (attributes.popper &&
4124
- attributes.popper['data-popper-placement']) ||
4125
- initialPlacement;
4178
+ var _h = tslib.__read(React.useState(initialPlacement), 2), placement = _h[0], setPlacement = _h[1];
4126
4179
  var additionalProps = variant === 'prompt' && !props['aria-label']
4127
4180
  ? { 'aria-labelledby': "".concat(id, "-label") }
4128
4181
  : {};
@@ -4203,11 +4256,11 @@ var Popover = React.forwardRef(function (_a, ref) {
4203
4256
  fallbackFocus: '.Popover__borderLeft'
4204
4257
  } },
4205
4258
  React__default["default"].createElement(ClickOutsideListener$1, { onClickOutside: handleClickOutside },
4206
- React__default["default"].createElement("div", tslib.__assign({ id: id, className: classNames__default["default"]('Popover', "Popover--".concat(placement), className, {
4259
+ React__default["default"].createElement(AnchoredOverlay, tslib.__assign({ id: id, className: classNames__default["default"]('Popover', "Popover--".concat(placement), className, {
4207
4260
  'Popover--hidden': !show,
4208
4261
  'Popover--prompt': variant === 'prompt'
4209
- }), ref: popoverRef, role: "dialog", style: styles.popper }, attributes.popper, additionalProps, props),
4210
- React__default["default"].createElement("div", { className: "Popover__popoverArrow", ref: setArrowElement, style: styles.arrow }),
4262
+ }), ref: popoverRef, role: "dialog", target: target, open: show, placement: initialPlacement, onPlacementChange: setPlacement, offset: 8 }, additionalProps, props),
4263
+ React__default["default"].createElement("div", { className: "Popover__popoverArrow" }),
4211
4264
  React__default["default"].createElement("div", { className: "Popover__borderLeft" }),
4212
4265
  variant === 'prompt' ? (React__default["default"].createElement(PromptPopoverContent, { applyButtonText: applyButtonText, onApply: onApply, closeButtonText: closeButtonText, infoText: infoText || '', onClose: handleClosePopover, infoTextId: "".concat(id, "-label") })) : (children)))), (portal && 'current' in portal ? portal.current : portal) ||
4213
4266
  // Dependent on "isBrowser" check above:
@@ -4476,6 +4529,7 @@ exports.AddressLine = AddressLine;
4476
4529
  exports.Alert = Alert;
4477
4530
  exports.AlertActions = AlertActions;
4478
4531
  exports.AlertContent = AlertContent;
4532
+ exports.AnchoredOverlay = AnchoredOverlay;
4479
4533
  exports.AriaIsolate = AriaIsolate;
4480
4534
  exports.Badge = Badge;
4481
4535
  exports.BadgeLabel = BadgeLabel;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deque/cauldron-react",
3
- "version": "6.12.0-canary.4505ef2e",
3
+ "version": "6.12.0-canary.67739754",
4
4
  "license": "MPL-2.0",
5
5
  "description": "Fully accessible react components library for Deque Cauldron",
6
6
  "homepage": "https://cauldron.dequelabs.com/",
@@ -23,13 +23,12 @@
23
23
  "test": "jest --maxWorkers=1 --coverage"
24
24
  },
25
25
  "dependencies": {
26
- "@popperjs/core": "^2.5.4",
26
+ "@floating-ui/react-dom": "^2.1.2",
27
27
  "classnames": "^2.2.6",
28
28
  "focus-trap-react": "^10.2.3",
29
29
  "focusable": "^2.3.0",
30
30
  "keyname": "^0.1.0",
31
31
  "react-id-generator": "^3.0.1",
32
- "react-popper": "^2.2.4",
33
32
  "react-syntax-highlighter": "^15.5.0",
34
33
  "tslib": "^2.4.0"
35
34
  },