@elliemae/ds-chip 3.70.0-next.71 → 3.70.0-next.73

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.
@@ -61,20 +61,14 @@ const DSChip = (props) => {
61
61
  disabled,
62
62
  className,
63
63
  onClick,
64
- // Composed into the expansion handlers below. `onKeyDown` is not intercepted Escape is handled
65
- // at the document level, so it passes through untouched.
66
- onMouseEnter: consumerMouseEnter,
67
- onMouseLeave: consumerMouseLeave,
68
- onFocus: consumerFocus,
69
- onBlur: consumerBlur,
64
+ // Consumer-supplied pointer/focus handlers stay in `restGlobals` so they remain on the button:
65
+ // their target contract is preserved (e.g. onBlur must bubble from the control for tooltips), and
66
+ // React's suppression of events on a natively disabled button applies to them, as before. Internal
67
+ // label expansion is driven separately from the non-disabled wrapper (see useLabelExpansion).
68
+ // `onKeyDown` is not intercepted — Escape is handled at the document level, so it passes untouched.
70
69
  ...restGlobals
71
70
  } = globalAttrs;
72
- const { expanded, labelRef, expansionHandlers } = (0, import_useLabelExpansion.useLabelExpansion)(wrapText, {
73
- onMouseEnter: consumerMouseEnter,
74
- onMouseLeave: consumerMouseLeave,
75
- onFocus: consumerFocus,
76
- onBlur: consumerBlur
77
- });
71
+ const { expanded, labelRef, expansionHandlers } = (0, import_useLabelExpansion.useLabelExpansion)(wrapText);
78
72
  const handleOnClick = (0, import_react.useCallback)(
79
73
  (e) => {
80
74
  if (applyAriaDisabled) return;
@@ -93,6 +87,7 @@ const DSChip = (props) => {
93
87
  className,
94
88
  ...xStyledAttrs,
95
89
  ...ownerPropsConfig,
90
+ ...expansionHandlers,
96
91
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
97
92
  import_styles.StyledChip,
98
93
  {
@@ -106,7 +101,6 @@ const DSChip = (props) => {
106
101
  buttonSize: size,
107
102
  expanded,
108
103
  "data-expanded": expanded || void 0,
109
- ...expansionHandlers,
110
104
  ...restGlobals,
111
105
  ...ownerPropsConfig,
112
106
  children: [
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/DSChip.tsx", "../../../../../scripts/build/transpile/react-shim.js"],
4
- "sourcesContent": ["import React, { useCallback } from 'react';\nimport { describe } from '@elliemae/ds-props-helpers';\nimport { TYPOGRAPHY_VARIANTS } from '@elliemae/ds-typography';\nimport { DSIconSizes } from '@elliemae/ds-icon';\nimport { useConfig } from './config/useConfig.js';\nimport { useLabelExpansion } from './config/useLabelExpansion.js';\nimport { StyledChip, StyledChipLabel, StyledWrapper, StyledRoundShape, StyledSpanWithEllipsis } from './styles.js';\nimport { CHIP_SHAPES } from './constants/index.js';\nimport type { DSChipT } from './react-desc-prop-types.js';\nimport { DSChipPropTypesSchema } from './react-desc-prop-types.js';\n\nconst DSChip: React.ComponentType<DSChipT.Props> = (props) => {\n const {\n size,\n shape,\n selected,\n applyAriaDisabled,\n innerRef,\n icon: Icon,\n label,\n globalAttrs,\n xStyledAttrs,\n ownerPropsConfig,\n wrapText,\n } = useConfig(props);\n\n const {\n disabled,\n className,\n onClick,\n // Composed into the expansion handlers below. `onKeyDown` is not intercepted \u2014 Escape is handled\n // at the document level, so it passes through untouched.\n onMouseEnter: consumerMouseEnter,\n onMouseLeave: consumerMouseLeave,\n onFocus: consumerFocus,\n onBlur: consumerBlur,\n ...restGlobals\n } = globalAttrs;\n\n const { expanded, labelRef, expansionHandlers } = useLabelExpansion(wrapText, {\n onMouseEnter: consumerMouseEnter,\n onMouseLeave: consumerMouseLeave,\n onFocus: consumerFocus,\n onBlur: consumerBlur,\n });\n\n const handleOnClick = useCallback(\n (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {\n if (applyAriaDisabled) return;\n if (onClick) {\n onClick(e);\n }\n },\n [applyAriaDisabled, onClick],\n );\n\n return (\n <StyledWrapper\n wrapText={wrapText}\n buttonShape={shape}\n buttonSize={size}\n className={className}\n {...xStyledAttrs}\n {...ownerPropsConfig}\n >\n <StyledChip\n buttonShape={shape}\n aria-disabled={disabled || applyAriaDisabled}\n disabled={disabled}\n innerRef={innerRef}\n selected={selected}\n role=\"button\"\n onClick={handleOnClick}\n buttonSize={size}\n expanded={expanded}\n data-expanded={expanded || undefined}\n {...expansionHandlers}\n {...restGlobals}\n {...ownerPropsConfig}\n >\n {shape === CHIP_SHAPES.ROUND ? (\n <StyledRoundShape disabled={disabled} size={size} {...ownerPropsConfig}>\n <Icon size={DSIconSizes.M} />\n </StyledRoundShape>\n ) : (\n <Icon size={DSIconSizes.M} />\n )}\n <StyledChipLabel size={size} {...ownerPropsConfig}>\n <StyledSpanWithEllipsis\n component=\"span\"\n variant={TYPOGRAPHY_VARIANTS.B4}\n wrapText={wrapText}\n innerRef={labelRef}\n {...ownerPropsConfig}\n >\n {label}\n </StyledSpanWithEllipsis>\n </StyledChipLabel>\n </StyledChip>\n </StyledWrapper>\n );\n};\n\nDSChip.displayName = 'DSChip';\nconst DSChipWithSchema = describe(DSChip);\nDSChipWithSchema.propTypes = DSChipPropTypesSchema;\n\nexport { DSChip, DSChipWithSchema };\n", "import * as React from 'react';\nexport { React };\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;ADiEjB;AAjEN,mBAAmC;AACnC,8BAAyB;AACzB,2BAAoC;AACpC,qBAA4B;AAC5B,uBAA0B;AAC1B,+BAAkC;AAClC,oBAAqG;AACrG,uBAA4B;AAE5B,mCAAsC;AAEtC,MAAM,SAA6C,CAAC,UAAU;AAC5D,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,QAAI,4BAAU,KAAK;AAEnB,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA;AAAA;AAAA,IAGA,cAAc;AAAA,IACd,cAAc;AAAA,IACd,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,GAAG;AAAA,EACL,IAAI;AAEJ,QAAM,EAAE,UAAU,UAAU,kBAAkB,QAAI,4CAAkB,UAAU;AAAA,IAC5E,cAAc;AAAA,IACd,cAAc;AAAA,IACd,SAAS;AAAA,IACT,QAAQ;AAAA,EACV,CAAC;AAED,QAAM,oBAAgB;AAAA,IACpB,CAAC,MAAuD;AACtD,UAAI,kBAAmB;AACvB,UAAI,SAAS;AACX,gBAAQ,CAAC;AAAA,MACX;AAAA,IACF;AAAA,IACA,CAAC,mBAAmB,OAAO;AAAA,EAC7B;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,aAAa;AAAA,MACb,YAAY;AAAA,MACZ;AAAA,MACC,GAAG;AAAA,MACH,GAAG;AAAA,MAEJ;AAAA,QAAC;AAAA;AAAA,UACC,aAAa;AAAA,UACb,iBAAe,YAAY;AAAA,UAC3B;AAAA,UACA;AAAA,UACA;AAAA,UACA,MAAK;AAAA,UACL,SAAS;AAAA,UACT,YAAY;AAAA,UACZ;AAAA,UACA,iBAAe,YAAY;AAAA,UAC1B,GAAG;AAAA,UACH,GAAG;AAAA,UACH,GAAG;AAAA,UAEH;AAAA,sBAAU,6BAAY,QACrB,4CAAC,kCAAiB,UAAoB,MAAa,GAAG,kBACpD,sDAAC,QAAK,MAAM,2BAAY,GAAG,GAC7B,IAEA,4CAAC,QAAK,MAAM,2BAAY,GAAG;AAAA,YAE7B,4CAAC,iCAAgB,MAAa,GAAG,kBAC/B;AAAA,cAAC;AAAA;AAAA,gBACC,WAAU;AAAA,gBACV,SAAS,yCAAoB;AAAA,gBAC7B;AAAA,gBACA,UAAU;AAAA,gBACT,GAAG;AAAA,gBAEH;AAAA;AAAA,YACH,GACF;AAAA;AAAA;AAAA,MACF;AAAA;AAAA,EACF;AAEJ;AAEA,OAAO,cAAc;AACrB,MAAM,uBAAmB,kCAAS,MAAM;AACxC,iBAAiB,YAAY;",
4
+ "sourcesContent": ["import React, { useCallback } from 'react';\nimport { describe } from '@elliemae/ds-props-helpers';\nimport { TYPOGRAPHY_VARIANTS } from '@elliemae/ds-typography';\nimport { DSIconSizes } from '@elliemae/ds-icon';\nimport { useConfig } from './config/useConfig.js';\nimport { useLabelExpansion } from './config/useLabelExpansion.js';\nimport { StyledChip, StyledChipLabel, StyledWrapper, StyledRoundShape, StyledSpanWithEllipsis } from './styles.js';\nimport { CHIP_SHAPES } from './constants/index.js';\nimport type { DSChipT } from './react-desc-prop-types.js';\nimport { DSChipPropTypesSchema } from './react-desc-prop-types.js';\n\nconst DSChip: React.ComponentType<DSChipT.Props> = (props) => {\n const {\n size,\n shape,\n selected,\n applyAriaDisabled,\n innerRef,\n icon: Icon,\n label,\n globalAttrs,\n xStyledAttrs,\n ownerPropsConfig,\n wrapText,\n } = useConfig(props);\n\n const {\n disabled,\n className,\n onClick,\n // Consumer-supplied pointer/focus handlers stay in `restGlobals` so they remain on the button:\n // their target contract is preserved (e.g. onBlur must bubble from the control for tooltips), and\n // React's suppression of events on a natively disabled button applies to them, as before. Internal\n // label expansion is driven separately from the non-disabled wrapper (see useLabelExpansion).\n // `onKeyDown` is not intercepted \u2014 Escape is handled at the document level, so it passes untouched.\n ...restGlobals\n } = globalAttrs;\n\n const { expanded, labelRef, expansionHandlers } = useLabelExpansion(wrapText);\n\n const handleOnClick = useCallback(\n (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {\n if (applyAriaDisabled) return;\n if (onClick) {\n onClick(e);\n }\n },\n [applyAriaDisabled, onClick],\n );\n\n return (\n <StyledWrapper\n wrapText={wrapText}\n buttonShape={shape}\n buttonSize={size}\n className={className}\n {...xStyledAttrs}\n {...ownerPropsConfig}\n {...expansionHandlers}\n >\n <StyledChip\n buttonShape={shape}\n aria-disabled={disabled || applyAriaDisabled}\n disabled={disabled}\n innerRef={innerRef}\n selected={selected}\n role=\"button\"\n onClick={handleOnClick}\n buttonSize={size}\n expanded={expanded}\n data-expanded={expanded || undefined}\n {...restGlobals}\n {...ownerPropsConfig}\n >\n {shape === CHIP_SHAPES.ROUND ? (\n <StyledRoundShape disabled={disabled} size={size} {...ownerPropsConfig}>\n <Icon size={DSIconSizes.M} />\n </StyledRoundShape>\n ) : (\n <Icon size={DSIconSizes.M} />\n )}\n <StyledChipLabel size={size} {...ownerPropsConfig}>\n <StyledSpanWithEllipsis\n component=\"span\"\n variant={TYPOGRAPHY_VARIANTS.B4}\n wrapText={wrapText}\n innerRef={labelRef}\n {...ownerPropsConfig}\n >\n {label}\n </StyledSpanWithEllipsis>\n </StyledChipLabel>\n </StyledChip>\n </StyledWrapper>\n );\n};\n\nDSChip.displayName = 'DSChip';\nconst DSChipWithSchema = describe(DSChip);\nDSChipWithSchema.propTypes = DSChipPropTypesSchema;\n\nexport { DSChip, DSChipWithSchema };\n", "import * as React from 'react';\nexport { React };\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;AD4DjB;AA5DN,mBAAmC;AACnC,8BAAyB;AACzB,2BAAoC;AACpC,qBAA4B;AAC5B,uBAA0B;AAC1B,+BAAkC;AAClC,oBAAqG;AACrG,uBAA4B;AAE5B,mCAAsC;AAEtC,MAAM,SAA6C,CAAC,UAAU;AAC5D,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,QAAI,4BAAU,KAAK;AAEnB,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMA,GAAG;AAAA,EACL,IAAI;AAEJ,QAAM,EAAE,UAAU,UAAU,kBAAkB,QAAI,4CAAkB,QAAQ;AAE5E,QAAM,oBAAgB;AAAA,IACpB,CAAC,MAAuD;AACtD,UAAI,kBAAmB;AACvB,UAAI,SAAS;AACX,gBAAQ,CAAC;AAAA,MACX;AAAA,IACF;AAAA,IACA,CAAC,mBAAmB,OAAO;AAAA,EAC7B;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,aAAa;AAAA,MACb,YAAY;AAAA,MACZ;AAAA,MACC,GAAG;AAAA,MACH,GAAG;AAAA,MACH,GAAG;AAAA,MAEJ;AAAA,QAAC;AAAA;AAAA,UACC,aAAa;AAAA,UACb,iBAAe,YAAY;AAAA,UAC3B;AAAA,UACA;AAAA,UACA;AAAA,UACA,MAAK;AAAA,UACL,SAAS;AAAA,UACT,YAAY;AAAA,UACZ;AAAA,UACA,iBAAe,YAAY;AAAA,UAC1B,GAAG;AAAA,UACH,GAAG;AAAA,UAEH;AAAA,sBAAU,6BAAY,QACrB,4CAAC,kCAAiB,UAAoB,MAAa,GAAG,kBACpD,sDAAC,QAAK,MAAM,2BAAY,GAAG,GAC7B,IAEA,4CAAC,QAAK,MAAM,2BAAY,GAAG;AAAA,YAE7B,4CAAC,iCAAgB,MAAa,GAAG,kBAC/B;AAAA,cAAC;AAAA;AAAA,gBACC,WAAU;AAAA,gBACV,SAAS,yCAAoB;AAAA,gBAC7B;AAAA,gBACA,UAAU;AAAA,gBACT,GAAG;AAAA,gBAEH;AAAA;AAAA,YACH,GACF;AAAA;AAAA;AAAA,MACF;AAAA;AAAA,EACF;AAEJ;AAEA,OAAO,cAAc;AACrB,MAAM,uBAAmB,kCAAS,MAAM;AACxC,iBAAiB,YAAY;",
6
6
  "names": []
7
7
  }
@@ -34,7 +34,7 @@ module.exports = __toCommonJS(useLabelExpansion_exports);
34
34
  var React = __toESM(require("react"));
35
35
  var import_react = require("react");
36
36
  var import_useEscapeDismiss = require("./useEscapeDismiss.js");
37
- const useLabelExpansion = (wrapText, consumer) => {
37
+ const useLabelExpansion = (wrapText) => {
38
38
  const [isHovered, setIsHovered] = (0, import_react.useState)(false);
39
39
  const [isFocused, setIsFocused] = (0, import_react.useState)(false);
40
40
  const [isDismissed, setIsDismissed] = (0, import_react.useState)(false);
@@ -55,43 +55,26 @@ const useLabelExpansion = (wrapText, consumer) => {
55
55
  setIsDismissed(false);
56
56
  }
57
57
  }, []);
58
- const { onMouseEnter, onMouseLeave, onFocus, onBlur } = consumer;
59
- const handleMouseEnter = (0, import_react.useCallback)(
60
- (e) => {
61
- measureOverflowOnEntry();
62
- hoveredRef.current = true;
63
- setIsHovered(true);
64
- onMouseEnter?.(e);
65
- },
66
- [onMouseEnter, measureOverflowOnEntry]
67
- );
68
- const handleMouseLeave = (0, import_react.useCallback)(
69
- (e) => {
70
- hoveredRef.current = false;
71
- setIsHovered(false);
72
- rearmIfLeft();
73
- onMouseLeave?.(e);
74
- },
75
- [onMouseLeave, rearmIfLeft]
76
- );
77
- const handleFocus = (0, import_react.useCallback)(
78
- (e) => {
79
- measureOverflowOnEntry();
80
- focusedRef.current = true;
81
- setIsFocused(true);
82
- onFocus?.(e);
83
- },
84
- [onFocus, measureOverflowOnEntry]
85
- );
86
- const handleBlur = (0, import_react.useCallback)(
87
- (e) => {
88
- focusedRef.current = false;
89
- setIsFocused(false);
90
- rearmIfLeft();
91
- onBlur?.(e);
92
- },
93
- [onBlur, rearmIfLeft]
94
- );
58
+ const handleMouseEnter = (0, import_react.useCallback)(() => {
59
+ measureOverflowOnEntry();
60
+ hoveredRef.current = true;
61
+ setIsHovered(true);
62
+ }, [measureOverflowOnEntry]);
63
+ const handleMouseLeave = (0, import_react.useCallback)(() => {
64
+ hoveredRef.current = false;
65
+ setIsHovered(false);
66
+ rearmIfLeft();
67
+ }, [rearmIfLeft]);
68
+ const handleFocus = (0, import_react.useCallback)(() => {
69
+ measureOverflowOnEntry();
70
+ focusedRef.current = true;
71
+ setIsFocused(true);
72
+ }, [measureOverflowOnEntry]);
73
+ const handleBlur = (0, import_react.useCallback)(() => {
74
+ focusedRef.current = false;
75
+ setIsFocused(false);
76
+ rearmIfLeft();
77
+ }, [rearmIfLeft]);
95
78
  const handleEscapeDismiss = (0, import_react.useCallback)(() => {
96
79
  dismissedRef.current = true;
97
80
  setIsDismissed(true);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/config/useLabelExpansion.ts", "../../../../../../scripts/build/transpile/react-shim.js"],
4
- "sourcesContent": ["import type React from 'react';\nimport { useCallback, useRef, useState } from 'react';\nimport { useEscapeDismiss } from './useEscapeDismiss.js';\n\ninterface ConsumerHandlersT {\n onMouseEnter?: React.MouseEventHandler<HTMLButtonElement>;\n onMouseLeave?: React.MouseEventHandler<HTMLButtonElement>;\n onFocus?: React.FocusEventHandler<HTMLButtonElement>;\n onBlur?: React.FocusEventHandler<HTMLButtonElement>;\n}\n\n// PUI-17950 \u2014 the expanded label overlays adjacent content, so WCAG 1.4.13 requires it be dismissible\n// without moving pointer or focus. That rules out CSS :hover/:focus, which a keypress cannot switch off.\n// `isOverflowing` matters beyond the visuals: without it a label that already fits would still claim an\n// expansion and swallow an Escape meant for an ancestor.\nexport const useLabelExpansion = (wrapText: boolean, consumer: ConsumerHandlersT) => {\n const [isHovered, setIsHovered] = useState(false);\n const [isFocused, setIsFocused] = useState(false);\n const [isDismissed, setIsDismissed] = useState(false);\n const [isOverflowing, setIsOverflowing] = useState(false);\n\n // Mirrored so the leave handlers read both signals in the same tick, without a useEffect.\n const hoveredRef = useRef(false);\n const focusedRef = useRef(false);\n const dismissedRef = useRef(false);\n const labelRef = useRef<HTMLDivElement | null>(null);\n\n const expanded = !wrapText && isOverflowing && (isHovered || isFocused) && !isDismissed;\n\n // Only on a fresh entry: an already-expanded label is no longer clipped and would measure as fitting.\n const measureOverflowOnEntry = useCallback(() => {\n if (wrapText || hoveredRef.current || focusedRef.current) return;\n const node = labelRef.current;\n setIsOverflowing(!!node && node.scrollWidth > node.clientWidth);\n }, [wrapText]);\n\n const rearmIfLeft = useCallback(() => {\n if (!hoveredRef.current && !focusedRef.current && dismissedRef.current) {\n dismissedRef.current = false;\n setIsDismissed(false);\n }\n }, []);\n\n const { onMouseEnter, onMouseLeave, onFocus, onBlur } = consumer;\n\n const handleMouseEnter = useCallback(\n (e: React.MouseEvent<HTMLButtonElement>) => {\n measureOverflowOnEntry();\n hoveredRef.current = true;\n setIsHovered(true);\n onMouseEnter?.(e);\n },\n [onMouseEnter, measureOverflowOnEntry],\n );\n\n const handleMouseLeave = useCallback(\n (e: React.MouseEvent<HTMLButtonElement>) => {\n hoveredRef.current = false;\n setIsHovered(false);\n rearmIfLeft();\n onMouseLeave?.(e);\n },\n [onMouseLeave, rearmIfLeft],\n );\n\n const handleFocus = useCallback(\n (e: React.FocusEvent<HTMLButtonElement>) => {\n measureOverflowOnEntry();\n focusedRef.current = true;\n setIsFocused(true);\n onFocus?.(e);\n },\n [onFocus, measureOverflowOnEntry],\n );\n\n const handleBlur = useCallback(\n (e: React.FocusEvent<HTMLButtonElement>) => {\n focusedRef.current = false;\n setIsFocused(false);\n rearmIfLeft();\n onBlur?.(e);\n },\n [onBlur, rearmIfLeft],\n );\n\n // Stable, or useEscapeDismiss resubscribes every render.\n const handleEscapeDismiss = useCallback(() => {\n dismissedRef.current = true;\n setIsDismissed(true);\n }, []);\n\n useEscapeDismiss(expanded, handleEscapeDismiss);\n\n return {\n expanded,\n labelRef,\n expansionHandlers: {\n onMouseEnter: handleMouseEnter,\n onMouseLeave: handleMouseLeave,\n onFocus: handleFocus,\n onBlur: handleBlur,\n },\n };\n};\n", "import * as React from 'react';\nexport { React };\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;ADCvB,mBAA8C;AAC9C,8BAAiC;AAa1B,MAAM,oBAAoB,CAAC,UAAmB,aAAgC;AACnF,QAAM,CAAC,WAAW,YAAY,QAAI,uBAAS,KAAK;AAChD,QAAM,CAAC,WAAW,YAAY,QAAI,uBAAS,KAAK;AAChD,QAAM,CAAC,aAAa,cAAc,QAAI,uBAAS,KAAK;AACpD,QAAM,CAAC,eAAe,gBAAgB,QAAI,uBAAS,KAAK;AAGxD,QAAM,iBAAa,qBAAO,KAAK;AAC/B,QAAM,iBAAa,qBAAO,KAAK;AAC/B,QAAM,mBAAe,qBAAO,KAAK;AACjC,QAAM,eAAW,qBAA8B,IAAI;AAEnD,QAAM,WAAW,CAAC,YAAY,kBAAkB,aAAa,cAAc,CAAC;AAG5E,QAAM,6BAAyB,0BAAY,MAAM;AAC/C,QAAI,YAAY,WAAW,WAAW,WAAW,QAAS;AAC1D,UAAM,OAAO,SAAS;AACtB,qBAAiB,CAAC,CAAC,QAAQ,KAAK,cAAc,KAAK,WAAW;AAAA,EAChE,GAAG,CAAC,QAAQ,CAAC;AAEb,QAAM,kBAAc,0BAAY,MAAM;AACpC,QAAI,CAAC,WAAW,WAAW,CAAC,WAAW,WAAW,aAAa,SAAS;AACtE,mBAAa,UAAU;AACvB,qBAAe,KAAK;AAAA,IACtB;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,EAAE,cAAc,cAAc,SAAS,OAAO,IAAI;AAExD,QAAM,uBAAmB;AAAA,IACvB,CAAC,MAA2C;AAC1C,6BAAuB;AACvB,iBAAW,UAAU;AACrB,mBAAa,IAAI;AACjB,qBAAe,CAAC;AAAA,IAClB;AAAA,IACA,CAAC,cAAc,sBAAsB;AAAA,EACvC;AAEA,QAAM,uBAAmB;AAAA,IACvB,CAAC,MAA2C;AAC1C,iBAAW,UAAU;AACrB,mBAAa,KAAK;AAClB,kBAAY;AACZ,qBAAe,CAAC;AAAA,IAClB;AAAA,IACA,CAAC,cAAc,WAAW;AAAA,EAC5B;AAEA,QAAM,kBAAc;AAAA,IAClB,CAAC,MAA2C;AAC1C,6BAAuB;AACvB,iBAAW,UAAU;AACrB,mBAAa,IAAI;AACjB,gBAAU,CAAC;AAAA,IACb;AAAA,IACA,CAAC,SAAS,sBAAsB;AAAA,EAClC;AAEA,QAAM,iBAAa;AAAA,IACjB,CAAC,MAA2C;AAC1C,iBAAW,UAAU;AACrB,mBAAa,KAAK;AAClB,kBAAY;AACZ,eAAS,CAAC;AAAA,IACZ;AAAA,IACA,CAAC,QAAQ,WAAW;AAAA,EACtB;AAGA,QAAM,0BAAsB,0BAAY,MAAM;AAC5C,iBAAa,UAAU;AACvB,mBAAe,IAAI;AAAA,EACrB,GAAG,CAAC,CAAC;AAEL,gDAAiB,UAAU,mBAAmB;AAE9C,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,mBAAmB;AAAA,MACjB,cAAc;AAAA,MACd,cAAc;AAAA,MACd,SAAS;AAAA,MACT,QAAQ;AAAA,IACV;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["import { useCallback, useRef, useState } from 'react';\nimport { useEscapeDismiss } from './useEscapeDismiss.js';\n\n// PUI-17950 \u2014 the expanded label overlays adjacent content, so WCAG 1.4.13 requires it be dismissible\n// without moving pointer or focus. That rules out CSS :hover/:focus, which a keypress cannot switch off.\n// `isOverflowing` matters beyond the visuals: without it a label that already fits would still claim an\n// expansion and swallow an Escape meant for an ancestor.\n//\n// PUI-18894 \u2014 these handlers drive expansion internally and are attached to the chip's root wrapper (a\n// `<div>`), not the button. React does not dispatch synthetic mouse/focus events to a natively disabled\n// form control, so a disabled chip would never expand if they lived on the button; the non-disabled\n// wrapper still receives the (bubbling) native events. Consumer-supplied handlers are intentionally NOT\n// composed here \u2014 they stay on the button so their target contract (e.g. onBlur bubbling for tooltips)\n// is preserved.\nexport const useLabelExpansion = (wrapText: boolean) => {\n const [isHovered, setIsHovered] = useState(false);\n const [isFocused, setIsFocused] = useState(false);\n const [isDismissed, setIsDismissed] = useState(false);\n const [isOverflowing, setIsOverflowing] = useState(false);\n\n // Mirrored so the leave handlers read both signals in the same tick, without a useEffect.\n const hoveredRef = useRef(false);\n const focusedRef = useRef(false);\n const dismissedRef = useRef(false);\n const labelRef = useRef<HTMLDivElement | null>(null);\n\n const expanded = !wrapText && isOverflowing && (isHovered || isFocused) && !isDismissed;\n\n // Only on a fresh entry: an already-expanded label is no longer clipped and would measure as fitting.\n const measureOverflowOnEntry = useCallback(() => {\n if (wrapText || hoveredRef.current || focusedRef.current) return;\n const node = labelRef.current;\n setIsOverflowing(!!node && node.scrollWidth > node.clientWidth);\n }, [wrapText]);\n\n const rearmIfLeft = useCallback(() => {\n if (!hoveredRef.current && !focusedRef.current && dismissedRef.current) {\n dismissedRef.current = false;\n setIsDismissed(false);\n }\n }, []);\n\n const handleMouseEnter = useCallback(() => {\n measureOverflowOnEntry();\n hoveredRef.current = true;\n setIsHovered(true);\n }, [measureOverflowOnEntry]);\n\n const handleMouseLeave = useCallback(() => {\n hoveredRef.current = false;\n setIsHovered(false);\n rearmIfLeft();\n }, [rearmIfLeft]);\n\n // Focus/blur bubble from the button up to the wrapper (focusin/focusout), so keyboard-focus\n // expansion keeps working even though the handlers no longer sit on the control itself.\n const handleFocus = useCallback(() => {\n measureOverflowOnEntry();\n focusedRef.current = true;\n setIsFocused(true);\n }, [measureOverflowOnEntry]);\n\n const handleBlur = useCallback(() => {\n focusedRef.current = false;\n setIsFocused(false);\n rearmIfLeft();\n }, [rearmIfLeft]);\n\n // Stable, or useEscapeDismiss resubscribes every render.\n const handleEscapeDismiss = useCallback(() => {\n dismissedRef.current = true;\n setIsDismissed(true);\n }, []);\n\n useEscapeDismiss(expanded, handleEscapeDismiss);\n\n return {\n expanded,\n labelRef,\n expansionHandlers: {\n onMouseEnter: handleMouseEnter,\n onMouseLeave: handleMouseLeave,\n onFocus: handleFocus,\n onBlur: handleBlur,\n },\n };\n};\n", "import * as React from 'react';\nexport { React };\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;ADAvB,mBAA8C;AAC9C,8BAAiC;AAa1B,MAAM,oBAAoB,CAAC,aAAsB;AACtD,QAAM,CAAC,WAAW,YAAY,QAAI,uBAAS,KAAK;AAChD,QAAM,CAAC,WAAW,YAAY,QAAI,uBAAS,KAAK;AAChD,QAAM,CAAC,aAAa,cAAc,QAAI,uBAAS,KAAK;AACpD,QAAM,CAAC,eAAe,gBAAgB,QAAI,uBAAS,KAAK;AAGxD,QAAM,iBAAa,qBAAO,KAAK;AAC/B,QAAM,iBAAa,qBAAO,KAAK;AAC/B,QAAM,mBAAe,qBAAO,KAAK;AACjC,QAAM,eAAW,qBAA8B,IAAI;AAEnD,QAAM,WAAW,CAAC,YAAY,kBAAkB,aAAa,cAAc,CAAC;AAG5E,QAAM,6BAAyB,0BAAY,MAAM;AAC/C,QAAI,YAAY,WAAW,WAAW,WAAW,QAAS;AAC1D,UAAM,OAAO,SAAS;AACtB,qBAAiB,CAAC,CAAC,QAAQ,KAAK,cAAc,KAAK,WAAW;AAAA,EAChE,GAAG,CAAC,QAAQ,CAAC;AAEb,QAAM,kBAAc,0BAAY,MAAM;AACpC,QAAI,CAAC,WAAW,WAAW,CAAC,WAAW,WAAW,aAAa,SAAS;AACtE,mBAAa,UAAU;AACvB,qBAAe,KAAK;AAAA,IACtB;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,uBAAmB,0BAAY,MAAM;AACzC,2BAAuB;AACvB,eAAW,UAAU;AACrB,iBAAa,IAAI;AAAA,EACnB,GAAG,CAAC,sBAAsB,CAAC;AAE3B,QAAM,uBAAmB,0BAAY,MAAM;AACzC,eAAW,UAAU;AACrB,iBAAa,KAAK;AAClB,gBAAY;AAAA,EACd,GAAG,CAAC,WAAW,CAAC;AAIhB,QAAM,kBAAc,0BAAY,MAAM;AACpC,2BAAuB;AACvB,eAAW,UAAU;AACrB,iBAAa,IAAI;AAAA,EACnB,GAAG,CAAC,sBAAsB,CAAC;AAE3B,QAAM,iBAAa,0BAAY,MAAM;AACnC,eAAW,UAAU;AACrB,iBAAa,KAAK;AAClB,gBAAY;AAAA,EACd,GAAG,CAAC,WAAW,CAAC;AAGhB,QAAM,0BAAsB,0BAAY,MAAM;AAC5C,iBAAa,UAAU;AACvB,mBAAe,IAAI;AAAA,EACrB,GAAG,CAAC,CAAC;AAEL,gDAAiB,UAAU,mBAAmB;AAE9C,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,mBAAmB;AAAA,MACjB,cAAc;AAAA,MACd,cAAc;AAAA,MACd,SAAS;AAAA,MACT,QAAQ;AAAA,IACV;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }
@@ -27,20 +27,14 @@ const DSChip = (props) => {
27
27
  disabled,
28
28
  className,
29
29
  onClick,
30
- // Composed into the expansion handlers below. `onKeyDown` is not intercepted Escape is handled
31
- // at the document level, so it passes through untouched.
32
- onMouseEnter: consumerMouseEnter,
33
- onMouseLeave: consumerMouseLeave,
34
- onFocus: consumerFocus,
35
- onBlur: consumerBlur,
30
+ // Consumer-supplied pointer/focus handlers stay in `restGlobals` so they remain on the button:
31
+ // their target contract is preserved (e.g. onBlur must bubble from the control for tooltips), and
32
+ // React's suppression of events on a natively disabled button applies to them, as before. Internal
33
+ // label expansion is driven separately from the non-disabled wrapper (see useLabelExpansion).
34
+ // `onKeyDown` is not intercepted — Escape is handled at the document level, so it passes untouched.
36
35
  ...restGlobals
37
36
  } = globalAttrs;
38
- const { expanded, labelRef, expansionHandlers } = useLabelExpansion(wrapText, {
39
- onMouseEnter: consumerMouseEnter,
40
- onMouseLeave: consumerMouseLeave,
41
- onFocus: consumerFocus,
42
- onBlur: consumerBlur
43
- });
37
+ const { expanded, labelRef, expansionHandlers } = useLabelExpansion(wrapText);
44
38
  const handleOnClick = useCallback(
45
39
  (e) => {
46
40
  if (applyAriaDisabled) return;
@@ -59,6 +53,7 @@ const DSChip = (props) => {
59
53
  className,
60
54
  ...xStyledAttrs,
61
55
  ...ownerPropsConfig,
56
+ ...expansionHandlers,
62
57
  children: /* @__PURE__ */ jsxs(
63
58
  StyledChip,
64
59
  {
@@ -72,7 +67,6 @@ const DSChip = (props) => {
72
67
  buttonSize: size,
73
68
  expanded,
74
69
  "data-expanded": expanded || void 0,
75
- ...expansionHandlers,
76
70
  ...restGlobals,
77
71
  ...ownerPropsConfig,
78
72
  children: [
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../scripts/build/transpile/react-shim.js", "../../src/DSChip.tsx"],
4
- "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import React, { useCallback } from 'react';\nimport { describe } from '@elliemae/ds-props-helpers';\nimport { TYPOGRAPHY_VARIANTS } from '@elliemae/ds-typography';\nimport { DSIconSizes } from '@elliemae/ds-icon';\nimport { useConfig } from './config/useConfig.js';\nimport { useLabelExpansion } from './config/useLabelExpansion.js';\nimport { StyledChip, StyledChipLabel, StyledWrapper, StyledRoundShape, StyledSpanWithEllipsis } from './styles.js';\nimport { CHIP_SHAPES } from './constants/index.js';\nimport type { DSChipT } from './react-desc-prop-types.js';\nimport { DSChipPropTypesSchema } from './react-desc-prop-types.js';\n\nconst DSChip: React.ComponentType<DSChipT.Props> = (props) => {\n const {\n size,\n shape,\n selected,\n applyAriaDisabled,\n innerRef,\n icon: Icon,\n label,\n globalAttrs,\n xStyledAttrs,\n ownerPropsConfig,\n wrapText,\n } = useConfig(props);\n\n const {\n disabled,\n className,\n onClick,\n // Composed into the expansion handlers below. `onKeyDown` is not intercepted \u2014 Escape is handled\n // at the document level, so it passes through untouched.\n onMouseEnter: consumerMouseEnter,\n onMouseLeave: consumerMouseLeave,\n onFocus: consumerFocus,\n onBlur: consumerBlur,\n ...restGlobals\n } = globalAttrs;\n\n const { expanded, labelRef, expansionHandlers } = useLabelExpansion(wrapText, {\n onMouseEnter: consumerMouseEnter,\n onMouseLeave: consumerMouseLeave,\n onFocus: consumerFocus,\n onBlur: consumerBlur,\n });\n\n const handleOnClick = useCallback(\n (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {\n if (applyAriaDisabled) return;\n if (onClick) {\n onClick(e);\n }\n },\n [applyAriaDisabled, onClick],\n );\n\n return (\n <StyledWrapper\n wrapText={wrapText}\n buttonShape={shape}\n buttonSize={size}\n className={className}\n {...xStyledAttrs}\n {...ownerPropsConfig}\n >\n <StyledChip\n buttonShape={shape}\n aria-disabled={disabled || applyAriaDisabled}\n disabled={disabled}\n innerRef={innerRef}\n selected={selected}\n role=\"button\"\n onClick={handleOnClick}\n buttonSize={size}\n expanded={expanded}\n data-expanded={expanded || undefined}\n {...expansionHandlers}\n {...restGlobals}\n {...ownerPropsConfig}\n >\n {shape === CHIP_SHAPES.ROUND ? (\n <StyledRoundShape disabled={disabled} size={size} {...ownerPropsConfig}>\n <Icon size={DSIconSizes.M} />\n </StyledRoundShape>\n ) : (\n <Icon size={DSIconSizes.M} />\n )}\n <StyledChipLabel size={size} {...ownerPropsConfig}>\n <StyledSpanWithEllipsis\n component=\"span\"\n variant={TYPOGRAPHY_VARIANTS.B4}\n wrapText={wrapText}\n innerRef={labelRef}\n {...ownerPropsConfig}\n >\n {label}\n </StyledSpanWithEllipsis>\n </StyledChipLabel>\n </StyledChip>\n </StyledWrapper>\n );\n};\n\nDSChip.displayName = 'DSChip';\nconst DSChipWithSchema = describe(DSChip);\nDSChipWithSchema.propTypes = DSChipPropTypesSchema;\n\nexport { DSChip, DSChipWithSchema };\n"],
5
- "mappings": "AAAA,YAAY,WAAW;ACiEjB,SAiBM,KAjBN;AAjEN,SAAgB,mBAAmB;AACnC,SAAS,gBAAgB;AACzB,SAAS,2BAA2B;AACpC,SAAS,mBAAmB;AAC5B,SAAS,iBAAiB;AAC1B,SAAS,yBAAyB;AAClC,SAAS,YAAY,iBAAiB,eAAe,kBAAkB,8BAA8B;AACrG,SAAS,mBAAmB;AAE5B,SAAS,6BAA6B;AAEtC,MAAM,SAA6C,CAAC,UAAU;AAC5D,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI,UAAU,KAAK;AAEnB,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA;AAAA;AAAA,IAGA,cAAc;AAAA,IACd,cAAc;AAAA,IACd,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,GAAG;AAAA,EACL,IAAI;AAEJ,QAAM,EAAE,UAAU,UAAU,kBAAkB,IAAI,kBAAkB,UAAU;AAAA,IAC5E,cAAc;AAAA,IACd,cAAc;AAAA,IACd,SAAS;AAAA,IACT,QAAQ;AAAA,EACV,CAAC;AAED,QAAM,gBAAgB;AAAA,IACpB,CAAC,MAAuD;AACtD,UAAI,kBAAmB;AACvB,UAAI,SAAS;AACX,gBAAQ,CAAC;AAAA,MACX;AAAA,IACF;AAAA,IACA,CAAC,mBAAmB,OAAO;AAAA,EAC7B;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,aAAa;AAAA,MACb,YAAY;AAAA,MACZ;AAAA,MACC,GAAG;AAAA,MACH,GAAG;AAAA,MAEJ;AAAA,QAAC;AAAA;AAAA,UACC,aAAa;AAAA,UACb,iBAAe,YAAY;AAAA,UAC3B;AAAA,UACA;AAAA,UACA;AAAA,UACA,MAAK;AAAA,UACL,SAAS;AAAA,UACT,YAAY;AAAA,UACZ;AAAA,UACA,iBAAe,YAAY;AAAA,UAC1B,GAAG;AAAA,UACH,GAAG;AAAA,UACH,GAAG;AAAA,UAEH;AAAA,sBAAU,YAAY,QACrB,oBAAC,oBAAiB,UAAoB,MAAa,GAAG,kBACpD,8BAAC,QAAK,MAAM,YAAY,GAAG,GAC7B,IAEA,oBAAC,QAAK,MAAM,YAAY,GAAG;AAAA,YAE7B,oBAAC,mBAAgB,MAAa,GAAG,kBAC/B;AAAA,cAAC;AAAA;AAAA,gBACC,WAAU;AAAA,gBACV,SAAS,oBAAoB;AAAA,gBAC7B;AAAA,gBACA,UAAU;AAAA,gBACT,GAAG;AAAA,gBAEH;AAAA;AAAA,YACH,GACF;AAAA;AAAA;AAAA,MACF;AAAA;AAAA,EACF;AAEJ;AAEA,OAAO,cAAc;AACrB,MAAM,mBAAmB,SAAS,MAAM;AACxC,iBAAiB,YAAY;",
4
+ "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import React, { useCallback } from 'react';\nimport { describe } from '@elliemae/ds-props-helpers';\nimport { TYPOGRAPHY_VARIANTS } from '@elliemae/ds-typography';\nimport { DSIconSizes } from '@elliemae/ds-icon';\nimport { useConfig } from './config/useConfig.js';\nimport { useLabelExpansion } from './config/useLabelExpansion.js';\nimport { StyledChip, StyledChipLabel, StyledWrapper, StyledRoundShape, StyledSpanWithEllipsis } from './styles.js';\nimport { CHIP_SHAPES } from './constants/index.js';\nimport type { DSChipT } from './react-desc-prop-types.js';\nimport { DSChipPropTypesSchema } from './react-desc-prop-types.js';\n\nconst DSChip: React.ComponentType<DSChipT.Props> = (props) => {\n const {\n size,\n shape,\n selected,\n applyAriaDisabled,\n innerRef,\n icon: Icon,\n label,\n globalAttrs,\n xStyledAttrs,\n ownerPropsConfig,\n wrapText,\n } = useConfig(props);\n\n const {\n disabled,\n className,\n onClick,\n // Consumer-supplied pointer/focus handlers stay in `restGlobals` so they remain on the button:\n // their target contract is preserved (e.g. onBlur must bubble from the control for tooltips), and\n // React's suppression of events on a natively disabled button applies to them, as before. Internal\n // label expansion is driven separately from the non-disabled wrapper (see useLabelExpansion).\n // `onKeyDown` is not intercepted \u2014 Escape is handled at the document level, so it passes untouched.\n ...restGlobals\n } = globalAttrs;\n\n const { expanded, labelRef, expansionHandlers } = useLabelExpansion(wrapText);\n\n const handleOnClick = useCallback(\n (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {\n if (applyAriaDisabled) return;\n if (onClick) {\n onClick(e);\n }\n },\n [applyAriaDisabled, onClick],\n );\n\n return (\n <StyledWrapper\n wrapText={wrapText}\n buttonShape={shape}\n buttonSize={size}\n className={className}\n {...xStyledAttrs}\n {...ownerPropsConfig}\n {...expansionHandlers}\n >\n <StyledChip\n buttonShape={shape}\n aria-disabled={disabled || applyAriaDisabled}\n disabled={disabled}\n innerRef={innerRef}\n selected={selected}\n role=\"button\"\n onClick={handleOnClick}\n buttonSize={size}\n expanded={expanded}\n data-expanded={expanded || undefined}\n {...restGlobals}\n {...ownerPropsConfig}\n >\n {shape === CHIP_SHAPES.ROUND ? (\n <StyledRoundShape disabled={disabled} size={size} {...ownerPropsConfig}>\n <Icon size={DSIconSizes.M} />\n </StyledRoundShape>\n ) : (\n <Icon size={DSIconSizes.M} />\n )}\n <StyledChipLabel size={size} {...ownerPropsConfig}>\n <StyledSpanWithEllipsis\n component=\"span\"\n variant={TYPOGRAPHY_VARIANTS.B4}\n wrapText={wrapText}\n innerRef={labelRef}\n {...ownerPropsConfig}\n >\n {label}\n </StyledSpanWithEllipsis>\n </StyledChipLabel>\n </StyledChip>\n </StyledWrapper>\n );\n};\n\nDSChip.displayName = 'DSChip';\nconst DSChipWithSchema = describe(DSChip);\nDSChipWithSchema.propTypes = DSChipPropTypesSchema;\n\nexport { DSChip, DSChipWithSchema };\n"],
5
+ "mappings": "AAAA,YAAY,WAAW;AC4DjB,SAgBM,KAhBN;AA5DN,SAAgB,mBAAmB;AACnC,SAAS,gBAAgB;AACzB,SAAS,2BAA2B;AACpC,SAAS,mBAAmB;AAC5B,SAAS,iBAAiB;AAC1B,SAAS,yBAAyB;AAClC,SAAS,YAAY,iBAAiB,eAAe,kBAAkB,8BAA8B;AACrG,SAAS,mBAAmB;AAE5B,SAAS,6BAA6B;AAEtC,MAAM,SAA6C,CAAC,UAAU;AAC5D,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI,UAAU,KAAK;AAEnB,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMA,GAAG;AAAA,EACL,IAAI;AAEJ,QAAM,EAAE,UAAU,UAAU,kBAAkB,IAAI,kBAAkB,QAAQ;AAE5E,QAAM,gBAAgB;AAAA,IACpB,CAAC,MAAuD;AACtD,UAAI,kBAAmB;AACvB,UAAI,SAAS;AACX,gBAAQ,CAAC;AAAA,MACX;AAAA,IACF;AAAA,IACA,CAAC,mBAAmB,OAAO;AAAA,EAC7B;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,aAAa;AAAA,MACb,YAAY;AAAA,MACZ;AAAA,MACC,GAAG;AAAA,MACH,GAAG;AAAA,MACH,GAAG;AAAA,MAEJ;AAAA,QAAC;AAAA;AAAA,UACC,aAAa;AAAA,UACb,iBAAe,YAAY;AAAA,UAC3B;AAAA,UACA;AAAA,UACA;AAAA,UACA,MAAK;AAAA,UACL,SAAS;AAAA,UACT,YAAY;AAAA,UACZ;AAAA,UACA,iBAAe,YAAY;AAAA,UAC1B,GAAG;AAAA,UACH,GAAG;AAAA,UAEH;AAAA,sBAAU,YAAY,QACrB,oBAAC,oBAAiB,UAAoB,MAAa,GAAG,kBACpD,8BAAC,QAAK,MAAM,YAAY,GAAG,GAC7B,IAEA,oBAAC,QAAK,MAAM,YAAY,GAAG;AAAA,YAE7B,oBAAC,mBAAgB,MAAa,GAAG,kBAC/B;AAAA,cAAC;AAAA;AAAA,gBACC,WAAU;AAAA,gBACV,SAAS,oBAAoB;AAAA,gBAC7B;AAAA,gBACA,UAAU;AAAA,gBACT,GAAG;AAAA,gBAEH;AAAA;AAAA,YACH,GACF;AAAA;AAAA;AAAA,MACF;AAAA;AAAA,EACF;AAEJ;AAEA,OAAO,cAAc;AACrB,MAAM,mBAAmB,SAAS,MAAM;AACxC,iBAAiB,YAAY;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  import * as React from "react";
2
2
  import { useCallback, useRef, useState } from "react";
3
3
  import { useEscapeDismiss } from "./useEscapeDismiss.js";
4
- const useLabelExpansion = (wrapText, consumer) => {
4
+ const useLabelExpansion = (wrapText) => {
5
5
  const [isHovered, setIsHovered] = useState(false);
6
6
  const [isFocused, setIsFocused] = useState(false);
7
7
  const [isDismissed, setIsDismissed] = useState(false);
@@ -22,43 +22,26 @@ const useLabelExpansion = (wrapText, consumer) => {
22
22
  setIsDismissed(false);
23
23
  }
24
24
  }, []);
25
- const { onMouseEnter, onMouseLeave, onFocus, onBlur } = consumer;
26
- const handleMouseEnter = useCallback(
27
- (e) => {
28
- measureOverflowOnEntry();
29
- hoveredRef.current = true;
30
- setIsHovered(true);
31
- onMouseEnter?.(e);
32
- },
33
- [onMouseEnter, measureOverflowOnEntry]
34
- );
35
- const handleMouseLeave = useCallback(
36
- (e) => {
37
- hoveredRef.current = false;
38
- setIsHovered(false);
39
- rearmIfLeft();
40
- onMouseLeave?.(e);
41
- },
42
- [onMouseLeave, rearmIfLeft]
43
- );
44
- const handleFocus = useCallback(
45
- (e) => {
46
- measureOverflowOnEntry();
47
- focusedRef.current = true;
48
- setIsFocused(true);
49
- onFocus?.(e);
50
- },
51
- [onFocus, measureOverflowOnEntry]
52
- );
53
- const handleBlur = useCallback(
54
- (e) => {
55
- focusedRef.current = false;
56
- setIsFocused(false);
57
- rearmIfLeft();
58
- onBlur?.(e);
59
- },
60
- [onBlur, rearmIfLeft]
61
- );
25
+ const handleMouseEnter = useCallback(() => {
26
+ measureOverflowOnEntry();
27
+ hoveredRef.current = true;
28
+ setIsHovered(true);
29
+ }, [measureOverflowOnEntry]);
30
+ const handleMouseLeave = useCallback(() => {
31
+ hoveredRef.current = false;
32
+ setIsHovered(false);
33
+ rearmIfLeft();
34
+ }, [rearmIfLeft]);
35
+ const handleFocus = useCallback(() => {
36
+ measureOverflowOnEntry();
37
+ focusedRef.current = true;
38
+ setIsFocused(true);
39
+ }, [measureOverflowOnEntry]);
40
+ const handleBlur = useCallback(() => {
41
+ focusedRef.current = false;
42
+ setIsFocused(false);
43
+ rearmIfLeft();
44
+ }, [rearmIfLeft]);
62
45
  const handleEscapeDismiss = useCallback(() => {
63
46
  dismissedRef.current = true;
64
47
  setIsDismissed(true);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../scripts/build/transpile/react-shim.js", "../../../src/config/useLabelExpansion.ts"],
4
- "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import type React from 'react';\nimport { useCallback, useRef, useState } from 'react';\nimport { useEscapeDismiss } from './useEscapeDismiss.js';\n\ninterface ConsumerHandlersT {\n onMouseEnter?: React.MouseEventHandler<HTMLButtonElement>;\n onMouseLeave?: React.MouseEventHandler<HTMLButtonElement>;\n onFocus?: React.FocusEventHandler<HTMLButtonElement>;\n onBlur?: React.FocusEventHandler<HTMLButtonElement>;\n}\n\n// PUI-17950 \u2014 the expanded label overlays adjacent content, so WCAG 1.4.13 requires it be dismissible\n// without moving pointer or focus. That rules out CSS :hover/:focus, which a keypress cannot switch off.\n// `isOverflowing` matters beyond the visuals: without it a label that already fits would still claim an\n// expansion and swallow an Escape meant for an ancestor.\nexport const useLabelExpansion = (wrapText: boolean, consumer: ConsumerHandlersT) => {\n const [isHovered, setIsHovered] = useState(false);\n const [isFocused, setIsFocused] = useState(false);\n const [isDismissed, setIsDismissed] = useState(false);\n const [isOverflowing, setIsOverflowing] = useState(false);\n\n // Mirrored so the leave handlers read both signals in the same tick, without a useEffect.\n const hoveredRef = useRef(false);\n const focusedRef = useRef(false);\n const dismissedRef = useRef(false);\n const labelRef = useRef<HTMLDivElement | null>(null);\n\n const expanded = !wrapText && isOverflowing && (isHovered || isFocused) && !isDismissed;\n\n // Only on a fresh entry: an already-expanded label is no longer clipped and would measure as fitting.\n const measureOverflowOnEntry = useCallback(() => {\n if (wrapText || hoveredRef.current || focusedRef.current) return;\n const node = labelRef.current;\n setIsOverflowing(!!node && node.scrollWidth > node.clientWidth);\n }, [wrapText]);\n\n const rearmIfLeft = useCallback(() => {\n if (!hoveredRef.current && !focusedRef.current && dismissedRef.current) {\n dismissedRef.current = false;\n setIsDismissed(false);\n }\n }, []);\n\n const { onMouseEnter, onMouseLeave, onFocus, onBlur } = consumer;\n\n const handleMouseEnter = useCallback(\n (e: React.MouseEvent<HTMLButtonElement>) => {\n measureOverflowOnEntry();\n hoveredRef.current = true;\n setIsHovered(true);\n onMouseEnter?.(e);\n },\n [onMouseEnter, measureOverflowOnEntry],\n );\n\n const handleMouseLeave = useCallback(\n (e: React.MouseEvent<HTMLButtonElement>) => {\n hoveredRef.current = false;\n setIsHovered(false);\n rearmIfLeft();\n onMouseLeave?.(e);\n },\n [onMouseLeave, rearmIfLeft],\n );\n\n const handleFocus = useCallback(\n (e: React.FocusEvent<HTMLButtonElement>) => {\n measureOverflowOnEntry();\n focusedRef.current = true;\n setIsFocused(true);\n onFocus?.(e);\n },\n [onFocus, measureOverflowOnEntry],\n );\n\n const handleBlur = useCallback(\n (e: React.FocusEvent<HTMLButtonElement>) => {\n focusedRef.current = false;\n setIsFocused(false);\n rearmIfLeft();\n onBlur?.(e);\n },\n [onBlur, rearmIfLeft],\n );\n\n // Stable, or useEscapeDismiss resubscribes every render.\n const handleEscapeDismiss = useCallback(() => {\n dismissedRef.current = true;\n setIsDismissed(true);\n }, []);\n\n useEscapeDismiss(expanded, handleEscapeDismiss);\n\n return {\n expanded,\n labelRef,\n expansionHandlers: {\n onMouseEnter: handleMouseEnter,\n onMouseLeave: handleMouseLeave,\n onFocus: handleFocus,\n onBlur: handleBlur,\n },\n };\n};\n"],
5
- "mappings": "AAAA,YAAY,WAAW;ACCvB,SAAS,aAAa,QAAQ,gBAAgB;AAC9C,SAAS,wBAAwB;AAa1B,MAAM,oBAAoB,CAAC,UAAmB,aAAgC;AACnF,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,KAAK;AAChD,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,KAAK;AAChD,QAAM,CAAC,aAAa,cAAc,IAAI,SAAS,KAAK;AACpD,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAAS,KAAK;AAGxD,QAAM,aAAa,OAAO,KAAK;AAC/B,QAAM,aAAa,OAAO,KAAK;AAC/B,QAAM,eAAe,OAAO,KAAK;AACjC,QAAM,WAAW,OAA8B,IAAI;AAEnD,QAAM,WAAW,CAAC,YAAY,kBAAkB,aAAa,cAAc,CAAC;AAG5E,QAAM,yBAAyB,YAAY,MAAM;AAC/C,QAAI,YAAY,WAAW,WAAW,WAAW,QAAS;AAC1D,UAAM,OAAO,SAAS;AACtB,qBAAiB,CAAC,CAAC,QAAQ,KAAK,cAAc,KAAK,WAAW;AAAA,EAChE,GAAG,CAAC,QAAQ,CAAC;AAEb,QAAM,cAAc,YAAY,MAAM;AACpC,QAAI,CAAC,WAAW,WAAW,CAAC,WAAW,WAAW,aAAa,SAAS;AACtE,mBAAa,UAAU;AACvB,qBAAe,KAAK;AAAA,IACtB;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,EAAE,cAAc,cAAc,SAAS,OAAO,IAAI;AAExD,QAAM,mBAAmB;AAAA,IACvB,CAAC,MAA2C;AAC1C,6BAAuB;AACvB,iBAAW,UAAU;AACrB,mBAAa,IAAI;AACjB,qBAAe,CAAC;AAAA,IAClB;AAAA,IACA,CAAC,cAAc,sBAAsB;AAAA,EACvC;AAEA,QAAM,mBAAmB;AAAA,IACvB,CAAC,MAA2C;AAC1C,iBAAW,UAAU;AACrB,mBAAa,KAAK;AAClB,kBAAY;AACZ,qBAAe,CAAC;AAAA,IAClB;AAAA,IACA,CAAC,cAAc,WAAW;AAAA,EAC5B;AAEA,QAAM,cAAc;AAAA,IAClB,CAAC,MAA2C;AAC1C,6BAAuB;AACvB,iBAAW,UAAU;AACrB,mBAAa,IAAI;AACjB,gBAAU,CAAC;AAAA,IACb;AAAA,IACA,CAAC,SAAS,sBAAsB;AAAA,EAClC;AAEA,QAAM,aAAa;AAAA,IACjB,CAAC,MAA2C;AAC1C,iBAAW,UAAU;AACrB,mBAAa,KAAK;AAClB,kBAAY;AACZ,eAAS,CAAC;AAAA,IACZ;AAAA,IACA,CAAC,QAAQ,WAAW;AAAA,EACtB;AAGA,QAAM,sBAAsB,YAAY,MAAM;AAC5C,iBAAa,UAAU;AACvB,mBAAe,IAAI;AAAA,EACrB,GAAG,CAAC,CAAC;AAEL,mBAAiB,UAAU,mBAAmB;AAE9C,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,mBAAmB;AAAA,MACjB,cAAc;AAAA,MACd,cAAc;AAAA,MACd,SAAS;AAAA,MACT,QAAQ;AAAA,IACV;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import { useCallback, useRef, useState } from 'react';\nimport { useEscapeDismiss } from './useEscapeDismiss.js';\n\n// PUI-17950 \u2014 the expanded label overlays adjacent content, so WCAG 1.4.13 requires it be dismissible\n// without moving pointer or focus. That rules out CSS :hover/:focus, which a keypress cannot switch off.\n// `isOverflowing` matters beyond the visuals: without it a label that already fits would still claim an\n// expansion and swallow an Escape meant for an ancestor.\n//\n// PUI-18894 \u2014 these handlers drive expansion internally and are attached to the chip's root wrapper (a\n// `<div>`), not the button. React does not dispatch synthetic mouse/focus events to a natively disabled\n// form control, so a disabled chip would never expand if they lived on the button; the non-disabled\n// wrapper still receives the (bubbling) native events. Consumer-supplied handlers are intentionally NOT\n// composed here \u2014 they stay on the button so their target contract (e.g. onBlur bubbling for tooltips)\n// is preserved.\nexport const useLabelExpansion = (wrapText: boolean) => {\n const [isHovered, setIsHovered] = useState(false);\n const [isFocused, setIsFocused] = useState(false);\n const [isDismissed, setIsDismissed] = useState(false);\n const [isOverflowing, setIsOverflowing] = useState(false);\n\n // Mirrored so the leave handlers read both signals in the same tick, without a useEffect.\n const hoveredRef = useRef(false);\n const focusedRef = useRef(false);\n const dismissedRef = useRef(false);\n const labelRef = useRef<HTMLDivElement | null>(null);\n\n const expanded = !wrapText && isOverflowing && (isHovered || isFocused) && !isDismissed;\n\n // Only on a fresh entry: an already-expanded label is no longer clipped and would measure as fitting.\n const measureOverflowOnEntry = useCallback(() => {\n if (wrapText || hoveredRef.current || focusedRef.current) return;\n const node = labelRef.current;\n setIsOverflowing(!!node && node.scrollWidth > node.clientWidth);\n }, [wrapText]);\n\n const rearmIfLeft = useCallback(() => {\n if (!hoveredRef.current && !focusedRef.current && dismissedRef.current) {\n dismissedRef.current = false;\n setIsDismissed(false);\n }\n }, []);\n\n const handleMouseEnter = useCallback(() => {\n measureOverflowOnEntry();\n hoveredRef.current = true;\n setIsHovered(true);\n }, [measureOverflowOnEntry]);\n\n const handleMouseLeave = useCallback(() => {\n hoveredRef.current = false;\n setIsHovered(false);\n rearmIfLeft();\n }, [rearmIfLeft]);\n\n // Focus/blur bubble from the button up to the wrapper (focusin/focusout), so keyboard-focus\n // expansion keeps working even though the handlers no longer sit on the control itself.\n const handleFocus = useCallback(() => {\n measureOverflowOnEntry();\n focusedRef.current = true;\n setIsFocused(true);\n }, [measureOverflowOnEntry]);\n\n const handleBlur = useCallback(() => {\n focusedRef.current = false;\n setIsFocused(false);\n rearmIfLeft();\n }, [rearmIfLeft]);\n\n // Stable, or useEscapeDismiss resubscribes every render.\n const handleEscapeDismiss = useCallback(() => {\n dismissedRef.current = true;\n setIsDismissed(true);\n }, []);\n\n useEscapeDismiss(expanded, handleEscapeDismiss);\n\n return {\n expanded,\n labelRef,\n expansionHandlers: {\n onMouseEnter: handleMouseEnter,\n onMouseLeave: handleMouseLeave,\n onFocus: handleFocus,\n onBlur: handleBlur,\n },\n };\n};\n"],
5
+ "mappings": "AAAA,YAAY,WAAW;ACAvB,SAAS,aAAa,QAAQ,gBAAgB;AAC9C,SAAS,wBAAwB;AAa1B,MAAM,oBAAoB,CAAC,aAAsB;AACtD,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,KAAK;AAChD,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,KAAK;AAChD,QAAM,CAAC,aAAa,cAAc,IAAI,SAAS,KAAK;AACpD,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAAS,KAAK;AAGxD,QAAM,aAAa,OAAO,KAAK;AAC/B,QAAM,aAAa,OAAO,KAAK;AAC/B,QAAM,eAAe,OAAO,KAAK;AACjC,QAAM,WAAW,OAA8B,IAAI;AAEnD,QAAM,WAAW,CAAC,YAAY,kBAAkB,aAAa,cAAc,CAAC;AAG5E,QAAM,yBAAyB,YAAY,MAAM;AAC/C,QAAI,YAAY,WAAW,WAAW,WAAW,QAAS;AAC1D,UAAM,OAAO,SAAS;AACtB,qBAAiB,CAAC,CAAC,QAAQ,KAAK,cAAc,KAAK,WAAW;AAAA,EAChE,GAAG,CAAC,QAAQ,CAAC;AAEb,QAAM,cAAc,YAAY,MAAM;AACpC,QAAI,CAAC,WAAW,WAAW,CAAC,WAAW,WAAW,aAAa,SAAS;AACtE,mBAAa,UAAU;AACvB,qBAAe,KAAK;AAAA,IACtB;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,mBAAmB,YAAY,MAAM;AACzC,2BAAuB;AACvB,eAAW,UAAU;AACrB,iBAAa,IAAI;AAAA,EACnB,GAAG,CAAC,sBAAsB,CAAC;AAE3B,QAAM,mBAAmB,YAAY,MAAM;AACzC,eAAW,UAAU;AACrB,iBAAa,KAAK;AAClB,gBAAY;AAAA,EACd,GAAG,CAAC,WAAW,CAAC;AAIhB,QAAM,cAAc,YAAY,MAAM;AACpC,2BAAuB;AACvB,eAAW,UAAU;AACrB,iBAAa,IAAI;AAAA,EACnB,GAAG,CAAC,sBAAsB,CAAC;AAE3B,QAAM,aAAa,YAAY,MAAM;AACnC,eAAW,UAAU;AACrB,iBAAa,KAAK;AAClB,gBAAY;AAAA,EACd,GAAG,CAAC,WAAW,CAAC;AAGhB,QAAM,sBAAsB,YAAY,MAAM;AAC5C,iBAAa,UAAU;AACvB,mBAAe,IAAI;AAAA,EACrB,GAAG,CAAC,CAAC;AAEL,mBAAiB,UAAU,mBAAmB;AAE9C,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,mBAAmB;AAAA,MACjB,cAAc;AAAA,MACd,cAAc;AAAA,MACd,SAAS;AAAA,MACT,QAAQ;AAAA,IACV;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }
@@ -1,18 +1,10 @@
1
- import type React from 'react';
2
- interface ConsumerHandlersT {
3
- onMouseEnter?: React.MouseEventHandler<HTMLButtonElement>;
4
- onMouseLeave?: React.MouseEventHandler<HTMLButtonElement>;
5
- onFocus?: React.FocusEventHandler<HTMLButtonElement>;
6
- onBlur?: React.FocusEventHandler<HTMLButtonElement>;
7
- }
8
- export declare const useLabelExpansion: (wrapText: boolean, consumer: ConsumerHandlersT) => {
1
+ export declare const useLabelExpansion: (wrapText: boolean) => {
9
2
  expanded: boolean;
10
- labelRef: React.MutableRefObject<HTMLDivElement | null>;
3
+ labelRef: import("react").MutableRefObject<HTMLDivElement | null>;
11
4
  expansionHandlers: {
12
- onMouseEnter: (e: React.MouseEvent<HTMLButtonElement>) => void;
13
- onMouseLeave: (e: React.MouseEvent<HTMLButtonElement>) => void;
14
- onFocus: (e: React.FocusEvent<HTMLButtonElement>) => void;
15
- onBlur: (e: React.FocusEvent<HTMLButtonElement>) => void;
5
+ onMouseEnter: () => void;
6
+ onMouseLeave: () => void;
7
+ onFocus: () => void;
8
+ onBlur: () => void;
16
9
  };
17
10
  };
18
- export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elliemae/ds-chip",
3
- "version": "3.70.0-next.71",
3
+ "version": "3.70.0-next.73",
4
4
  "license": "MIT",
5
5
  "description": "ICE MT - Dimsum - Chip",
6
6
  "files": [
@@ -36,16 +36,16 @@
36
36
  "indent": 4
37
37
  },
38
38
  "dependencies": {
39
- "@elliemae/ds-props-helpers": "3.70.0-next.71",
40
- "@elliemae/ds-typescript-helpers": "3.70.0-next.71",
41
- "@elliemae/ds-system": "3.70.0-next.71"
39
+ "@elliemae/ds-system": "3.70.0-next.73",
40
+ "@elliemae/ds-typescript-helpers": "3.70.0-next.73",
41
+ "@elliemae/ds-props-helpers": "3.70.0-next.73"
42
42
  },
43
43
  "devDependencies": {
44
44
  "jest": "^30.0.0",
45
45
  "styled-components": "~5.3.11",
46
- "@elliemae/ds-monorepo-devops": "3.70.0-next.71",
47
- "@elliemae/ds-test-utils": "3.70.0-next.71",
48
- "@elliemae/ds-icons": "3.70.0-next.71"
46
+ "@elliemae/ds-monorepo-devops": "3.70.0-next.73",
47
+ "@elliemae/ds-test-utils": "3.70.0-next.73",
48
+ "@elliemae/ds-icons": "3.70.0-next.73"
49
49
  },
50
50
  "peerDependencies": {
51
51
  "lodash-es": "^4.18.1",