@elliemae/ds-floating-context 3.70.0-next.52 → 3.70.0-next.54
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/dist/cjs/DSFloatingContext.js +83 -33
- package/dist/cjs/DSFloatingContext.js.map +2 -2
- package/dist/cjs/hooks/useFloatingClickOutside.js +8 -8
- package/dist/cjs/hooks/useFloatingClickOutside.js.map +2 -2
- package/dist/cjs/hooks/useFloatingResizeObserver.js +8 -4
- package/dist/cjs/hooks/useFloatingResizeObserver.js.map +2 -2
- package/dist/cjs/hooks/useResolvedReference.js +2 -2
- package/dist/cjs/hooks/useResolvedReference.js.map +2 -2
- package/dist/cjs/hooks/{useFloatingEscape.js → useSyntheticEventFromControlledState.js} +19 -40
- package/dist/cjs/hooks/useSyntheticEventFromControlledState.js.map +7 -0
- package/dist/cjs/parts/FloatingWrapper/react-desc-prop-types.js +1 -1
- package/dist/cjs/parts/FloatingWrapper/react-desc-prop-types.js.map +2 -2
- package/dist/cjs/parts/PopoverArrow.js.map +2 -2
- package/dist/cjs/react-desc-prop-types.js +8 -4
- package/dist/cjs/react-desc-prop-types.js.map +2 -2
- package/dist/cjs/useComputedPositionStyles.js +24 -8
- package/dist/cjs/useComputedPositionStyles.js.map +2 -2
- package/dist/cjs/utils/computePosition.js +14 -7
- package/dist/cjs/utils/computePosition.js.map +2 -2
- package/dist/esm/DSFloatingContext.js +84 -34
- package/dist/esm/DSFloatingContext.js.map +2 -2
- package/dist/esm/hooks/useFloatingClickOutside.js +8 -8
- package/dist/esm/hooks/useFloatingClickOutside.js.map +2 -2
- package/dist/esm/hooks/useFloatingResizeObserver.js +8 -4
- package/dist/esm/hooks/useFloatingResizeObserver.js.map +2 -2
- package/dist/esm/hooks/useResolvedReference.js +2 -2
- package/dist/esm/hooks/useResolvedReference.js.map +2 -2
- package/dist/esm/hooks/useSyntheticEventFromControlledState.js +23 -0
- package/dist/esm/hooks/useSyntheticEventFromControlledState.js.map +7 -0
- package/dist/esm/parts/FloatingWrapper/react-desc-prop-types.js +1 -1
- package/dist/esm/parts/FloatingWrapper/react-desc-prop-types.js.map +2 -2
- package/dist/esm/parts/PopoverArrow.js.map +2 -2
- package/dist/esm/react-desc-prop-types.js +8 -4
- package/dist/esm/react-desc-prop-types.js.map +2 -2
- package/dist/esm/useComputedPositionStyles.js +24 -8
- package/dist/esm/useComputedPositionStyles.js.map +2 -2
- package/dist/esm/utils/computePosition.js +14 -7
- package/dist/esm/utils/computePosition.js.map +2 -2
- package/dist/types/DSFloatingContext.d.ts +46 -6
- package/dist/types/hooks/useFloatingClickOutside.d.ts +5 -5
- package/dist/types/hooks/useFloatingResizeObserver.d.ts +3 -3
- package/dist/types/hooks/useResolvedReference.d.ts +1 -1
- package/dist/types/hooks/useSyntheticEventFromControlledState.d.ts +32 -0
- package/dist/types/react-desc-prop-types.d.ts +39 -4
- package/dist/types/useComputedPositionStyles.d.ts +3 -3
- package/dist/types/utils/computePosition.d.ts +2 -2
- package/package.json +6 -6
- package/dist/cjs/hooks/useFloatingEscape.js.map +0 -7
- package/dist/esm/hooks/useFloatingEscape.js +0 -44
- package/dist/esm/hooks/useFloatingEscape.js.map +0 -7
- package/dist/types/hooks/useFloatingEscape.d.ts +0 -19
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../scripts/build/transpile/react-shim.js", "../../../src/hooks/useFloatingResizeObserver.ts"],
|
|
4
|
-
"sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import { useEffect, useRef } from 'react';\n\ninterface UseFloatingResizeObserverParams {\n
|
|
5
|
-
"mappings": "AAAA,YAAY,WAAW;ACAvB,SAAS,WAAW,cAAc;AAY3B,MAAM,4BAA4B,CAAC,
|
|
4
|
+
"sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import { useEffect, useRef } from 'react';\n\ninterface UseFloatingResizeObserverParams {\n isOpen: boolean;\n floatingWrapperNode: HTMLElement | null;\n onResize: () => void;\n}\n\n/**\n * Observes the floating element's size and calls `onResize` when its bounding box changes.\n * Used to re-run position computation when the floating content reflows (e.g. async-loaded data).\n */\nexport const useFloatingResizeObserver = ({\n isOpen,\n floatingWrapperNode,\n onResize,\n}: UseFloatingResizeObserverParams) => {\n // Latest-ref so an inline `onResize` does not invalidate the effect on every render.\n // ResizeObserver fires an initial callback on `observe()`, so re-subscribing every render\n // would call `onResize` \u2192 setState \u2192 re-render \u2192 re-subscribe \u2192 infinite loop.\n const onResizeRef = useRef(onResize);\n onResizeRef.current = onResize;\n\n useEffect(() => {\n if (!isOpen || !floatingWrapperNode) return undefined;\n\n const observer = new ResizeObserver(() => {\n onResizeRef.current();\n });\n observer.observe(floatingWrapperNode);\n\n return () => observer.disconnect();\n }, [isOpen, floatingWrapperNode]);\n};\n"],
|
|
5
|
+
"mappings": "AAAA,YAAY,WAAW;ACAvB,SAAS,WAAW,cAAc;AAY3B,MAAM,4BAA4B,CAAC;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AACF,MAAuC;AAIrC,QAAM,cAAc,OAAO,QAAQ;AACnC,cAAY,UAAU;AAEtB,YAAU,MAAM;AACd,QAAI,CAAC,UAAU,CAAC,oBAAqB,QAAO;AAE5C,UAAM,WAAW,IAAI,eAAe,MAAM;AACxC,kBAAY,QAAQ;AAAA,IACtB,CAAC;AACD,aAAS,QAAQ,mBAAmB;AAEpC,WAAO,MAAM,SAAS,WAAW;AAAA,EACnC,GAAG,CAAC,QAAQ,mBAAmB,CAAC;AAClC;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -6,7 +6,7 @@ const useResolvedReference = ({
|
|
|
6
6
|
setInternalReferenceElement
|
|
7
7
|
}) => {
|
|
8
8
|
const hasExternalReference = externalReferenceElement !== void 0;
|
|
9
|
-
const
|
|
9
|
+
const triggerElementReference = hasExternalReference ? externalReferenceElement : internalReferenceElement;
|
|
10
10
|
const setReferenceElement = useCallback(
|
|
11
11
|
(el) => {
|
|
12
12
|
if (hasExternalReference) return;
|
|
@@ -14,7 +14,7 @@ const useResolvedReference = ({
|
|
|
14
14
|
},
|
|
15
15
|
[hasExternalReference, setInternalReferenceElement]
|
|
16
16
|
);
|
|
17
|
-
return {
|
|
17
|
+
return { triggerElementReference, setReferenceElement };
|
|
18
18
|
};
|
|
19
19
|
export {
|
|
20
20
|
useResolvedReference
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../scripts/build/transpile/react-shim.js", "../../../src/hooks/useResolvedReference.ts"],
|
|
4
|
-
"sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import { useCallback } from 'react';\n\ninterface UseResolvedReferenceParams {\n externalReferenceElement: Element | null | undefined;\n internalReferenceElement: Element | null;\n setInternalReferenceElement: (el: Element | null) => void;\n}\n\n/**\n * Resolves the active reference element for the floating context.\n *\n * - When `externalReferenceElement` is provided (anything other than `undefined`, including `null`),\n * it is used as the source of truth. `setReference` becomes a no-op so consumers that mistakenly\n * call `refs.setReference()` don't desynchronize the two sources.\n * - Otherwise the internally-managed state is used, populated by the consumer via `refs.setReference()`\n * (typically as a callback ref: `innerRef={refs.setReference}`).\n */\nexport const useResolvedReference = ({\n externalReferenceElement,\n internalReferenceElement,\n setInternalReferenceElement,\n}: UseResolvedReferenceParams) => {\n const hasExternalReference = externalReferenceElement !== undefined;\n const
|
|
5
|
-
"mappings": "AAAA,YAAY,WAAW;ACAvB,SAAS,mBAAmB;AAiBrB,MAAM,uBAAuB,CAAC;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AACF,MAAkC;AAChC,QAAM,uBAAuB,6BAA6B;AAC1D,QAAM,
|
|
4
|
+
"sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import { useCallback } from 'react';\n\ninterface UseResolvedReferenceParams {\n externalReferenceElement: Element | null | undefined;\n internalReferenceElement: Element | null;\n setInternalReferenceElement: (el: Element | null) => void;\n}\n\n/**\n * Resolves the active reference element for the floating context.\n *\n * - When `externalReferenceElement` is provided (anything other than `undefined`, including `null`),\n * it is used as the source of truth. `setReference` becomes a no-op so consumers that mistakenly\n * call `refs.setReference()` don't desynchronize the two sources.\n * - Otherwise the internally-managed state is used, populated by the consumer via `refs.setReference()`\n * (typically as a callback ref: `innerRef={refs.setReference}`).\n */\nexport const useResolvedReference = ({\n externalReferenceElement,\n internalReferenceElement,\n setInternalReferenceElement,\n}: UseResolvedReferenceParams) => {\n const hasExternalReference = externalReferenceElement !== undefined;\n const triggerElementReference = hasExternalReference ? externalReferenceElement : internalReferenceElement;\n\n const setReferenceElement = useCallback(\n (el: Element | null) => {\n if (hasExternalReference) return;\n setInternalReferenceElement(el);\n },\n [hasExternalReference, setInternalReferenceElement],\n );\n\n return { triggerElementReference, setReferenceElement };\n};\n"],
|
|
5
|
+
"mappings": "AAAA,YAAY,WAAW;ACAvB,SAAS,mBAAmB;AAiBrB,MAAM,uBAAuB,CAAC;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AACF,MAAkC;AAChC,QAAM,uBAAuB,6BAA6B;AAC1D,QAAM,0BAA0B,uBAAuB,2BAA2B;AAElF,QAAM,sBAAsB;AAAA,IAC1B,CAAC,OAAuB;AACtB,UAAI,qBAAsB;AAC1B,kCAA4B,EAAE;AAAA,IAChC;AAAA,IACA,CAAC,sBAAsB,2BAA2B;AAAA,EACpD;AAEA,SAAO,EAAE,yBAAyB,oBAAoB;AACxD;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { useEffect, useRef } from "react";
|
|
3
|
+
import { useLatestRef } from "./useLatestRef.js";
|
|
4
|
+
const useSyntheticEventFromControlledState = ({
|
|
5
|
+
controlledValue,
|
|
6
|
+
onEnter,
|
|
7
|
+
onExit
|
|
8
|
+
}) => {
|
|
9
|
+
const onEnterRef = useLatestRef(onEnter);
|
|
10
|
+
const onExitRef = useLatestRef(onExit);
|
|
11
|
+
const previousValueRef = useRef(controlledValue);
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
const previousValue = previousValueRef.current;
|
|
14
|
+
previousValueRef.current = controlledValue;
|
|
15
|
+
if (previousValue === controlledValue) return;
|
|
16
|
+
if (controlledValue === true) onEnterRef.current?.();
|
|
17
|
+
else if (controlledValue === false) onExitRef.current?.();
|
|
18
|
+
}, [controlledValue, onEnterRef, onExitRef]);
|
|
19
|
+
};
|
|
20
|
+
export {
|
|
21
|
+
useSyntheticEventFromControlledState
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=useSyntheticEventFromControlledState.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../scripts/build/transpile/react-shim.js", "../../../src/hooks/useSyntheticEventFromControlledState.ts"],
|
|
4
|
+
"sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import { useEffect, useRef } from 'react';\nimport { useLatestRef } from './useLatestRef.js';\n\ninterface UseSyntheticEventFromControlledStateParams {\n /**\n * The externally-controlled value whose transitions are turned into events. `undefined` means\n * the value is not being controlled (uncontrolled usage) \u2014 no events are synthesized in that case.\n */\n controlledValue: boolean | undefined;\n /** Fired when `controlledValue` transitions to `true`. Passed inline is fine (latest-ref'd). */\n onEnter?: () => void;\n /** Fired when `controlledValue` transitions to `false`. Passed inline is fine (latest-ref'd). */\n onExit?: () => void;\n}\n\n/**\n * Synthesizes semantic enter/exit events from transitions of an **externally-controlled** value.\n *\n * Why this is a hook (and an effect), not something a caller should hand-roll: when a value is\n * controlled by the consumer, its changes originate *outside* this component \u2014 there is no\n * interaction handler in here to hang a notification on. Reacting to that change and emitting an\n * event is the *sanctioned* `useEffect` use: **synchronizing with an external system**, where the\n * external system is the consumer that owns the controlled state. This is categorically different\n * from the `useEffect` misuse (deriving/coordinating internal state), and it is the reason the\n * emitted callback is a genuine \"for whatever reason the owner changed it\" event rather than a\n * per-interaction one.\n *\n * Guarantees:\n * - Never fires on mount \u2014 only on real transitions after the initial value.\n * - Never fires while uncontrolled (`undefined`); only on explicit `false \u2194 true` edges.\n * - The prev-value bookkeeping is a ref (not state) *on purpose*: it is read/written only inside\n * the effect (post-commit), never during render, so it carries none of the render-purity hazards\n * that would make a ref wrong for an in-render transition guard.\n */\nexport const useSyntheticEventFromControlledState = ({\n controlledValue,\n onEnter,\n onExit,\n}: UseSyntheticEventFromControlledStateParams) => {\n const onEnterRef = useLatestRef(onEnter);\n const onExitRef = useLatestRef(onExit);\n const previousValueRef = useRef(controlledValue);\n\n useEffect(() => {\n const previousValue = previousValueRef.current;\n previousValueRef.current = controlledValue;\n if (previousValue === controlledValue) return;\n if (controlledValue === true) onEnterRef.current?.();\n else if (controlledValue === false) onExitRef.current?.();\n }, [controlledValue, onEnterRef, onExitRef]);\n};\n"],
|
|
5
|
+
"mappings": "AAAA,YAAY,WAAW;ACAvB,SAAS,WAAW,cAAc;AAClC,SAAS,oBAAoB;AAiCtB,MAAM,uCAAuC,CAAC;AAAA,EACnD;AAAA,EACA;AAAA,EACA;AACF,MAAkD;AAChD,QAAM,aAAa,aAAa,OAAO;AACvC,QAAM,YAAY,aAAa,MAAM;AACrC,QAAM,mBAAmB,OAAO,eAAe;AAE/C,YAAU,MAAM;AACd,UAAM,gBAAgB,iBAAiB;AACvC,qBAAiB,UAAU;AAC3B,QAAI,kBAAkB,gBAAiB;AACvC,QAAI,oBAAoB,KAAM,YAAW,UAAU;AAAA,aAC1C,oBAAoB,MAAO,WAAU,UAAU;AAAA,EAC1D,GAAG,CAAC,iBAAiB,YAAY,SAAS,CAAC;AAC7C;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -23,7 +23,7 @@ const DSFloatingWrapperPropTypes = {
|
|
|
23
23
|
isOpen: PropTypes.bool.description("Whether the floating wrapper is open").isRequired,
|
|
24
24
|
floatingStyles: PropTypes.object.description("Style for the floating wrapper").isRequired,
|
|
25
25
|
context: PropTypes.shape({
|
|
26
|
-
portalDOMContainer: PropTypes.
|
|
26
|
+
portalDOMContainer: PropTypes.object.description("The DOM element where the tooltip will be rendered.").defaultValue('the first "main" landmark available in the document or the body if no "main" is found'),
|
|
27
27
|
withoutPortal: PropTypes.bool.description("Whether to render the floating wrapper without a portal"),
|
|
28
28
|
animationDuration: PropTypes.number.description("Duration of the animation"),
|
|
29
29
|
withoutAnimation: PropTypes.bool.description("Whether to render the floating wrapper without animation")
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../scripts/build/transpile/react-shim.js", "../../../../src/parts/FloatingWrapper/react-desc-prop-types.ts"],
|
|
4
|
-
"sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable @typescript-eslint/no-empty-interface */\nimport type { GlobalAttributesT, XstyledProps, DSPropTypesSchema, ValidationMap } from '@elliemae/ds-props-helpers';\nimport {\n PropTypes,\n getPropsPerSlotPropTypes,\n globalAttributesPropTypes,\n xstyledPropTypes,\n} from '@elliemae/ds-props-helpers';\nimport { type TypescriptHelpersT } from '@elliemae/ds-typescript-helpers';\nimport { DSFloatingWrapperName, DSFloatingWrapperSlots } from '../../constants/index.js';\n\nexport declare namespace DSFloatingWrapperT {\n export interface RequiredProps {\n children: TypescriptHelpersT.ReactChildrenComplete;\n innerRef: TypescriptHelpersT.AnyRef<HTMLDivElement>;\n isOpen: boolean;\n floatingStyles: React.CSSProperties;\n }\n\n export interface DefaultProps {\n context: {\n portalDOMContainer?: HTMLElement;\n withoutPortal: boolean;\n animationDuration: number;\n withoutAnimation: boolean;\n };\n customOffset: [number, number];\n }\n\n export interface OptionalProps
|
|
5
|
-
"mappings": "AAAA,YAAY,WAAW;ACEvB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,SAAS,uBAAuB,8BAA8B;
|
|
4
|
+
"sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable @typescript-eslint/no-empty-interface */\nimport type { GlobalAttributesT, XstyledProps, DSPropTypesSchema, ValidationMap } from '@elliemae/ds-props-helpers';\nimport {\n PropTypes,\n getPropsPerSlotPropTypes,\n globalAttributesPropTypes,\n xstyledPropTypes,\n} from '@elliemae/ds-props-helpers';\nimport { type TypescriptHelpersT } from '@elliemae/ds-typescript-helpers';\nimport { DSFloatingWrapperName, DSFloatingWrapperSlots } from '../../constants/index.js';\n\nexport declare namespace DSFloatingWrapperT {\n export interface RequiredProps {\n children: TypescriptHelpersT.ReactChildrenComplete;\n innerRef: TypescriptHelpersT.AnyRef<HTMLDivElement>;\n isOpen: boolean;\n floatingStyles: React.CSSProperties;\n }\n\n export interface DefaultProps {\n context: {\n portalDOMContainer?: HTMLElement;\n withoutPortal: boolean;\n animationDuration: number;\n withoutAnimation: boolean;\n };\n customOffset: [number, number];\n }\n\n export interface OptionalProps extends TypescriptHelpersT.PropsForGlobalOnSlots<\n typeof DSFloatingWrapperName,\n typeof DSFloatingWrapperSlots\n > {\n onAnimationEnd?: React.AnimationEventHandler<HTMLDivElement>;\n onAnimationStartTriggered?: () => void;\n }\n\n export interface Props\n extends\n Partial<DefaultProps>,\n RequiredProps,\n OptionalProps,\n Omit<\n GlobalAttributesT<HTMLElement>,\n keyof DefaultProps | keyof OptionalProps | keyof RequiredProps | keyof XstyledProps\n >,\n XstyledProps {}\n\n export interface InternalProps\n extends\n DefaultProps,\n RequiredProps,\n OptionalProps,\n Omit<\n GlobalAttributesT<HTMLElement>,\n keyof DefaultProps | keyof OptionalProps | keyof RequiredProps | keyof XstyledProps\n >,\n XstyledProps {}\n}\n\nexport const defaultProps: Partial<DSFloatingWrapperT.DefaultProps> = {\n context: {\n withoutPortal: false,\n animationDuration: 300,\n withoutAnimation: false,\n },\n customOffset: [0, 12],\n};\n\nexport const DSFloatingWrapperPropTypes: DSPropTypesSchema<DSFloatingWrapperT.InternalProps> = {\n ...getPropsPerSlotPropTypes(DSFloatingWrapperName, DSFloatingWrapperSlots),\n ...globalAttributesPropTypes,\n ...xstyledPropTypes,\n children: PropTypes.node.description('Content of the floating wrapper').isRequired,\n innerRef: PropTypes.oneOfType([PropTypes.object, PropTypes.func]).description('Ref for the floating wrapper')\n .isRequired,\n isOpen: PropTypes.bool.description('Whether the floating wrapper is open').isRequired,\n floatingStyles: PropTypes.object.description('Style for the floating wrapper').isRequired,\n context: PropTypes.shape({\n portalDOMContainer: PropTypes.object\n .description('The DOM element where the tooltip will be rendered.')\n .defaultValue('the first \"main\" landmark available in the document or the body if no \"main\" is found'),\n withoutPortal: PropTypes.bool.description('Whether to render the floating wrapper without a portal'),\n animationDuration: PropTypes.number.description('Duration of the animation'),\n withoutAnimation: PropTypes.bool.description('Whether to render the floating wrapper without animation'),\n }).description('Context for the floating wrapper'),\n onAnimationStartTriggered: PropTypes.func.description(\n 'Callback invoked when the component trigger the animation start. Required to properly position nested floating context without visual artefacts in case animations are used.',\n ),\n onAnimationEnd: PropTypes.func.description(\n 'Callback when the animation ends. Required to properly position nested floating context without visual artefacts in case animations are used.',\n ),\n customOffset: PropTypes.arrayOf(PropTypes.number).description('Custom offset for the floating wrapper'),\n};\n\nexport const DSFloatingWrapperPropTypesSchema =\n DSFloatingWrapperPropTypes as unknown as ValidationMap<DSFloatingWrapperT.Props>;\n"],
|
|
5
|
+
"mappings": "AAAA,YAAY,WAAW;ACEvB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,SAAS,uBAAuB,8BAA8B;AAmDvD,MAAM,eAAyD;AAAA,EACpE,SAAS;AAAA,IACP,eAAe;AAAA,IACf,mBAAmB;AAAA,IACnB,kBAAkB;AAAA,EACpB;AAAA,EACA,cAAc,CAAC,GAAG,EAAE;AACtB;AAEO,MAAM,6BAAkF;AAAA,EAC7F,GAAG,yBAAyB,uBAAuB,sBAAsB;AAAA,EACzE,GAAG;AAAA,EACH,GAAG;AAAA,EACH,UAAU,UAAU,KAAK,YAAY,iCAAiC,EAAE;AAAA,EACxE,UAAU,UAAU,UAAU,CAAC,UAAU,QAAQ,UAAU,IAAI,CAAC,EAAE,YAAY,8BAA8B,EACzG;AAAA,EACH,QAAQ,UAAU,KAAK,YAAY,sCAAsC,EAAE;AAAA,EAC3E,gBAAgB,UAAU,OAAO,YAAY,gCAAgC,EAAE;AAAA,EAC/E,SAAS,UAAU,MAAM;AAAA,IACvB,oBAAoB,UAAU,OAC3B,YAAY,qDAAqD,EACjE,aAAa,uFAAuF;AAAA,IACvG,eAAe,UAAU,KAAK,YAAY,yDAAyD;AAAA,IACnG,mBAAmB,UAAU,OAAO,YAAY,2BAA2B;AAAA,IAC3E,kBAAkB,UAAU,KAAK,YAAY,0DAA0D;AAAA,EACzG,CAAC,EAAE,YAAY,kCAAkC;AAAA,EACjD,2BAA2B,UAAU,KAAK;AAAA,IACxC;AAAA,EACF;AAAA,EACA,gBAAgB,UAAU,KAAK;AAAA,IAC7B;AAAA,EACF;AAAA,EACA,cAAc,UAAU,QAAQ,UAAU,MAAM,EAAE,YAAY,wCAAwC;AACxG;AAEO,MAAM,mCACX;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../scripts/build/transpile/react-shim.js", "../../../src/parts/PopoverArrow.tsx"],
|
|
4
|
-
"sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import React from 'react';\nimport { useOwnerProps, useGetGlobalAttributes, useGetXstyledProps } from '@elliemae/ds-props-helpers';\nimport { styled } from '@elliemae/ds-system';\nimport type { TypescriptHelpersT } from '@elliemae/ds-typescript-helpers';\nimport { DSFloatingWrapperSlots, DSFloatingWrapperName } from '../constants/index.js';\nexport interface PopoverArrowT
|
|
5
|
-
"mappings": "AAAA,YAAY,WAAW;
|
|
4
|
+
"sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import React from 'react';\nimport { useOwnerProps, useGetGlobalAttributes, useGetXstyledProps } from '@elliemae/ds-props-helpers';\nimport { styled } from '@elliemae/ds-system';\nimport type { TypescriptHelpersT } from '@elliemae/ds-typescript-helpers';\nimport { DSFloatingWrapperSlots, DSFloatingWrapperName } from '../constants/index.js';\nexport interface PopoverArrowT extends TypescriptHelpersT.PropsForGlobalOnSlots<\n typeof DSFloatingWrapperName,\n typeof DSFloatingWrapperSlots\n> {\n placement: string;\n style: React.CSSProperties;\n arrowElementRef?: React.Dispatch<React.SetStateAction<HTMLDivElement | null>>;\n}\nconst arrowWidth = 18;\nconst OFFSET_FIX_SHADOW_DEFECT = 0.25;\n\nconst arrowDimensionBase13 = arrowWidth / 13;\nconst OFFSET_FIX_SHADOW_DEFECT_BASE13 = OFFSET_FIX_SHADOW_DEFECT / 13;\nconst arrowDimensionBase16 = arrowWidth / 16;\nconst OFFSET_FIX_SHADOW_DEFECT_BASE16 = OFFSET_FIX_SHADOW_DEFECT / 16;\n\nconst arrowPlacementBase13 = arrowDimensionBase13 - OFFSET_FIX_SHADOW_DEFECT_BASE13;\nconst arrowPlacementBase16 = arrowDimensionBase16 - OFFSET_FIX_SHADOW_DEFECT_BASE16;\n\nconst isTopOrBottom = (placement: string) => placement.startsWith('top') || placement.startsWith('bottom');\n\nconst isLeftOrRight = (placement: string) => placement.startsWith('left') || placement.startsWith('right');\n\nconst StyledArrow = styled('div', {\n name: DSFloatingWrapperName,\n slot: DSFloatingWrapperSlots.ARROW,\n})<{ 'data-placement': string }>`\n line-height: 0;\n position: absolute;\n width: ${arrowDimensionBase16}rem;\n height: ${arrowDimensionBase16}rem;\n pointer-events: none;\n background-color: transparent;\n & .stroke {\n fill: rgb(105, 116, 137);\n fill-opacity: 0.4;\n }\n & .fill {\n fill: rgb(255, 255, 255);\n }\n\n &[data-placement^='top'] {\n svg {\n transform: rotateZ(180deg);\n }\n bottom: -${arrowPlacementBase16}rem;\n left: ${(props) => {\n if (props['data-placement'].endsWith('start')) return `25%`;\n if (props['data-placement'].endsWith('end')) return `75%`;\n return '';\n }};\n }\n &[data-placement^='right'] {\n svg {\n transform: rotateZ(-90deg);\n }\n left: -${arrowPlacementBase16}rem;\n }\n &[data-placement^='bottom'] {\n top: -${arrowPlacementBase16}rem;\n left: ${(props) => {\n if (props['data-placement'].endsWith('start')) return `25%`;\n if (props['data-placement'].endsWith('end')) return `75%`;\n return '';\n }};\n }\n &[data-placement^='left'] {\n svg {\n transform: rotateZ(90deg);\n }\n right: -${arrowPlacementBase16}rem;\n }\n margin-left: ${(props) => (isTopOrBottom(props['data-placement']) ? `-${arrowDimensionBase16 / 2}rem` : '0')};\n margin-top: ${(props) => (isLeftOrRight(props['data-placement']) ? `-${arrowDimensionBase16 / 2}rem` : '0')};\n\n @media (min-width: ${({ theme }) => theme.breakpoints.small}) {\n width: ${arrowDimensionBase13}rem;\n height: ${arrowDimensionBase13}rem;\n\n &[data-placement^='top'] {\n bottom: -${arrowPlacementBase13}rem;\n }\n\n &[data-placement^='bottom'] {\n top: -${arrowPlacementBase13}rem;\n }\n\n &[data-placement^='left'] {\n right: -${arrowPlacementBase13}rem;\n }\n\n &[data-placement^='right'] {\n left: -${arrowPlacementBase13}rem;\n }\n\n margin-left: ${(props) => (isTopOrBottom(props['data-placement']) ? `-${arrowDimensionBase13 / 2}rem` : '0')};\n\n margin-top: ${(props) => (isLeftOrRight(props['data-placement']) ? `-${arrowDimensionBase13 / 2}rem` : '0')};\n }\n`;\n\nconst StylePathShadow = styled('path', {\n name: DSFloatingWrapperName,\n slot: DSFloatingWrapperSlots.ARROW_SHADOW,\n})``;\n\nconst StylePathFill = styled('path', {\n name: DSFloatingWrapperName,\n slot: DSFloatingWrapperSlots.ARROW_FILL,\n})``;\n\nexport const PopoverArrow = ({ placement, style, arrowElementRef, ...rest }: PopoverArrowT): React.ReactElement => {\n const ownerProps = useOwnerProps(rest);\n const globalAttributes = useGetGlobalAttributes(rest);\n const xstyledProps = useGetXstyledProps(rest);\n return (\n <StyledArrow\n key=\"popper-arrow\"\n data-placement={placement}\n style={style}\n innerRef={arrowElementRef}\n data-testid=\"ds-tooltip-arrow\"\n aria-hidden=\"true\"\n {...ownerProps}\n {...globalAttributes}\n {...xstyledProps}\n >\n <svg viewBox=\"0 0 30 30\">\n <StylePathShadow\n {...ownerProps}\n className=\"stroke\"\n d=\"M23.7,27.1L17,19.9C16.5,19.3,15.8,19,15,19s-1.6,0.3-2.1,0.9l-6.6,7.2C5.3,28.1,3.4,29,2,29h26\n C26.7,29,24.6,28.1,23.7,27.1z\"\n />\n <StylePathFill\n {...ownerProps}\n className=\"fill\"\n d=\"M23,27.8c1.1,1.2,3.4,2.2,5,2.2h2H0h2c1.7,0,3.9-1,5-2.2l6.6-7.2c0.7-0.8,2-0.8,2.7,0L23,27.8L23,27.8z\"\n />\n </svg>\n </StyledArrow>\n );\n};\n"],
|
|
5
|
+
"mappings": "AAAA,YAAY,WAAW;ACoIjB,SACE,KADF;AAnIN,SAAS,eAAe,wBAAwB,0BAA0B;AAC1E,SAAS,cAAc;AAEvB,SAAS,wBAAwB,6BAA6B;AAS9D,MAAM,aAAa;AACnB,MAAM,2BAA2B;AAEjC,MAAM,uBAAuB,aAAa;AAC1C,MAAM,kCAAkC,2BAA2B;AACnE,MAAM,uBAAuB,aAAa;AAC1C,MAAM,kCAAkC,2BAA2B;AAEnE,MAAM,uBAAuB,uBAAuB;AACpD,MAAM,uBAAuB,uBAAuB;AAEpD,MAAM,gBAAgB,CAAC,cAAsB,UAAU,WAAW,KAAK,KAAK,UAAU,WAAW,QAAQ;AAEzG,MAAM,gBAAgB,CAAC,cAAsB,UAAU,WAAW,MAAM,KAAK,UAAU,WAAW,OAAO;AAEzG,MAAM,cAAc,OAAO,OAAO;AAAA,EAChC,MAAM;AAAA,EACN,MAAM,uBAAuB;AAC/B,CAAC;AAAA;AAAA;AAAA,WAGU,oBAAoB;AAAA,YACnB,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,eAejB,oBAAoB;AAAA,YACvB,CAAC,UAAU;AACjB,MAAI,MAAM,gBAAgB,EAAE,SAAS,OAAO,EAAG,QAAO;AACtD,MAAI,MAAM,gBAAgB,EAAE,SAAS,KAAK,EAAG,QAAO;AACpD,SAAO;AACT,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,aAMQ,oBAAoB;AAAA;AAAA;AAAA,YAGrB,oBAAoB;AAAA,YACpB,CAAC,UAAU;AACjB,MAAI,MAAM,gBAAgB,EAAE,SAAS,OAAO,EAAG,QAAO;AACtD,MAAI,MAAM,gBAAgB,EAAE,SAAS,KAAK,EAAG,QAAO;AACpD,SAAO;AACT,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAMS,oBAAoB;AAAA;AAAA,iBAEjB,CAAC,UAAW,cAAc,MAAM,gBAAgB,CAAC,IAAI,IAAI,uBAAuB,CAAC,QAAQ,GAAI;AAAA,gBAC9F,CAAC,UAAW,cAAc,MAAM,gBAAgB,CAAC,IAAI,IAAI,uBAAuB,CAAC,QAAQ,GAAI;AAAA;AAAA,uBAEtF,CAAC,EAAE,MAAM,MAAM,MAAM,YAAY,KAAK;AAAA,aAChD,oBAAoB;AAAA,cACnB,oBAAoB;AAAA;AAAA;AAAA,iBAGjB,oBAAoB;AAAA;AAAA;AAAA;AAAA,cAIvB,oBAAoB;AAAA;AAAA;AAAA;AAAA,gBAIlB,oBAAoB;AAAA;AAAA;AAAA;AAAA,eAIrB,oBAAoB;AAAA;AAAA;AAAA,mBAGhB,CAAC,UAAW,cAAc,MAAM,gBAAgB,CAAC,IAAI,IAAI,uBAAuB,CAAC,QAAQ,GAAI;AAAA;AAAA,kBAE9F,CAAC,UAAW,cAAc,MAAM,gBAAgB,CAAC,IAAI,IAAI,uBAAuB,CAAC,QAAQ,GAAI;AAAA;AAAA;AAI/G,MAAM,kBAAkB,OAAO,QAAQ;AAAA,EACrC,MAAM;AAAA,EACN,MAAM,uBAAuB;AAC/B,CAAC;AAED,MAAM,gBAAgB,OAAO,QAAQ;AAAA,EACnC,MAAM;AAAA,EACN,MAAM,uBAAuB;AAC/B,CAAC;AAEM,MAAM,eAAe,CAAC,EAAE,WAAW,OAAO,iBAAiB,GAAG,KAAK,MAAyC;AACjH,QAAM,aAAa,cAAc,IAAI;AACrC,QAAM,mBAAmB,uBAAuB,IAAI;AACpD,QAAM,eAAe,mBAAmB,IAAI;AAC5C,SACE;AAAA,IAAC;AAAA;AAAA,MAEC,kBAAgB;AAAA,MAChB;AAAA,MACA,UAAU;AAAA,MACV,eAAY;AAAA,MACZ,eAAY;AAAA,MACX,GAAG;AAAA,MACH,GAAG;AAAA,MACH,GAAG;AAAA,MAEJ,+BAAC,SAAI,SAAQ,aACX;AAAA;AAAA,UAAC;AAAA;AAAA,YACE,GAAG;AAAA,YACJ,WAAU;AAAA,YACV,GAAE;AAAA;AAAA,QAEJ;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACE,GAAG;AAAA,YACJ,WAAU;AAAA,YACV,GAAE;AAAA;AAAA,QACJ;AAAA,SACF;AAAA;AAAA,IAtBI;AAAA,EAuBN;AAEJ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -12,7 +12,7 @@ const defaultProps = {
|
|
|
12
12
|
const DSFloatingContextPropTypes = {
|
|
13
13
|
withoutPortal: PropTypes.bool.description("If true, the tooltip will not be rendered inside a portal.").defaultValue(false),
|
|
14
14
|
withoutAnimation: PropTypes.bool.description("If true, the tooltip will not have an animation.").defaultValue(false),
|
|
15
|
-
portalDOMContainer: PropTypes.
|
|
15
|
+
portalDOMContainer: PropTypes.object.description("The DOM element where the tooltip will be rendered.").defaultValue('the first "main" landmark available in the document or the body if no "main" is found'),
|
|
16
16
|
animationDuration: PropTypes.number.description("The duration of the animation in milliseconds.").defaultValue(300),
|
|
17
17
|
placement: PropTypes.oneOf([
|
|
18
18
|
"top-start",
|
|
@@ -43,12 +43,16 @@ const DSFloatingContextPropTypes = {
|
|
|
43
43
|
PropTypes.tuple([PropTypes.oneOf(["left"])]),
|
|
44
44
|
PropTypes.tuple([PropTypes.oneOf(["left-start"])])
|
|
45
45
|
]).description("The order of the placement preference."),
|
|
46
|
-
onOpen: PropTypes.func.description(
|
|
47
|
-
|
|
46
|
+
onOpen: PropTypes.func.description(
|
|
47
|
+
"Called on every open transition (interaction or controlled-flip); receives arguments with a `reason`."
|
|
48
|
+
),
|
|
49
|
+
onClose: PropTypes.func.description(
|
|
50
|
+
"Called on every close transition (interaction or controlled-flip); receives arguments with a `reason`."
|
|
51
|
+
),
|
|
48
52
|
externallyControlledIsOpen: PropTypes.bool.description(
|
|
49
53
|
"If true, the context open/close state will be controlled externally."
|
|
50
54
|
),
|
|
51
|
-
externalReferenceElement: PropTypes.
|
|
55
|
+
externalReferenceElement: PropTypes.object.description(
|
|
52
56
|
"Pre-resolved reference element. When provided, used as the source of truth for positioning, avoiding the need for a follow-up refs.setReference() effect and the visibility:hidden race on open."
|
|
53
57
|
),
|
|
54
58
|
onClickOutside: PropTypes.func.description(
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../scripts/build/transpile/react-shim.js", "../../src/react-desc-prop-types.ts"],
|
|
4
|
-
"sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable @typescript-eslint/no-empty-interface */\nimport type { DSPropTypesSchema } from '@elliemae/ds-props-helpers';\nimport { PropTypes } from '@elliemae/ds-props-helpers';\nexport declare namespace DSHookFloatingContextT {\n export interface DefaultProps {\n withoutPortal: boolean;\n withoutAnimation: boolean;\n portalDOMContainer?: HTMLElement;\n animationDuration: number;\n placement: PopperPlacementsT;\n customOffset: [number, number];\n closeOnEscape: boolean;\n returnFocusToReference: boolean;\n }\n\n export interface OptionalProps {\n placementOrderPreference?: PopperPlacementsT[];\n onOpen?: () => void;\n onClose?: () => void;\n externallyControlledIsOpen?: boolean;\n /**\n * Pre-resolved reference element. When provided, the hook uses this as the\n * source of truth for positioning and ignores its internal reference state.\n * Eliminates the need for a follow-up `refs.setReference(...)` effect and\n * removes the visibility:hidden race that breaks programmatic focus on open.\n */\n externalReferenceElement?: Element | null;\n /**\n * Called when the user clicks/taps outside both the floating element and\n * the reference element while the floating is open.\n */\n onClickOutside?: (event: MouseEvent | TouchEvent) => void;\n /**\n * Called when Escape is pressed while focus is within the floating element
|
|
5
|
-
"mappings": "AAAA,YAAY,WAAW;
|
|
4
|
+
"sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable @typescript-eslint/no-empty-interface */\nimport type React from 'react';\nimport type { DSPropTypesSchema } from '@elliemae/ds-props-helpers';\nimport { PropTypes } from '@elliemae/ds-props-helpers';\nexport declare namespace DSHookFloatingContextT {\n export interface DefaultProps {\n withoutPortal: boolean;\n withoutAnimation: boolean;\n portalDOMContainer?: HTMLElement;\n animationDuration: number;\n placement: PopperPlacementsT;\n customOffset: [number, number];\n closeOnEscape: boolean;\n returnFocusToReference: boolean;\n }\n\n /**\n * Describes why onOpen/onClose fired.\n * - `interaction`: driven by this hook's own interaction handling (focus/hover/escape, via\n * ds-hooks-headless-tooltip). Fires synchronously in the event catch phase and carries the\n * originating DOM event when there is one (absent for imperative show/hide). Fires for\n * controlled contexts too \u2014 it is the pre-flip, event-carrying signal a controlled consumer can\n * choose to act on.\n * - `controlled-flip`: driven by the consumer flipping `externallyControlledIsOpen`. Fires from a\n * post-commit effect \u2014 the change originates outside this hook, so there is no event to carry.\n *\n * A controlled consumer may see BOTH for one logical transition (the pre-flip interaction, then\n * the post-flip controlled-flip when they echo it into the prop); discriminate on `reason` (and\n * `isControlled`) to pick which one your logic cares about. That choice is the consumer's \u2014 this\n * hook does not decide it for them.\n */\n export type OpenChange =\n | {\n reason: 'interaction';\n event?: React.FocusEvent | React.MouseEvent | React.KeyboardEvent | KeyboardEvent;\n isControlled: boolean;\n }\n | {\n reason: 'controlled-flip';\n isControlled: true;\n };\n\n export interface OptionalProps {\n placementOrderPreference?: PopperPlacementsT[];\n /**\n * Called on every open transition \u2014 both interaction-driven (pre-flip, with `event`) and\n * controlled-flip-driven (post-flip). Discriminate on the `OpenChange` argument's `reason`.\n */\n onOpen?: (info: OpenChange) => void;\n /**\n * Called on every close transition \u2014 both interaction-driven and controlled-flip-driven.\n * Discriminate on `reason`. Note a scoped Escape close routes to `onEscape` instead of `onClose`\n * (see `onEscape`).\n */\n onClose?: (info: OpenChange) => void;\n externallyControlledIsOpen?: boolean;\n /**\n * Pre-resolved reference element. When provided, the hook uses this as the\n * source of truth for positioning and ignores its internal reference state.\n * Eliminates the need for a follow-up `refs.setReference(...)` effect and\n * removes the visibility:hidden race that breaks programmatic focus on open.\n */\n externalReferenceElement?: Element | null;\n /**\n * Called when the user clicks/taps outside both the floating element and\n * the reference element while the floating is open.\n */\n onClickOutside?: (event: MouseEvent | TouchEvent) => void;\n /**\n * Called when Escape is pressed while the floating element is open and focus is within the\n * floating element or the reference element. Only fires when `closeOnEscape` is true.\n * Receives the native KeyboardEvent \u2014 this is delivered via a document-level listener, not a\n * JSX handler, so there is no React SyntheticEvent to hand back.\n */\n onEscape?: (event: KeyboardEvent) => void;\n }\n export interface Props extends Partial<DefaultProps>, OptionalProps {}\n\n export interface InternalProps extends DefaultProps, OptionalProps {}\n\n export type PopperPlacementsT =\n | 'top-start'\n | 'top'\n | 'top-end'\n | 'right-start'\n | 'right'\n | 'right-end'\n | 'bottom-end'\n | 'bottom'\n | 'bottom-start'\n | 'left-end'\n | 'left'\n | 'left-start';\n}\n\nexport const defaultProps: DSHookFloatingContextT.DefaultProps = {\n withoutAnimation: false,\n animationDuration: 300,\n withoutPortal: false,\n placement: 'top',\n customOffset: [0, 12],\n closeOnEscape: false,\n returnFocusToReference: false,\n};\n\nexport const DSFloatingContextPropTypes: DSPropTypesSchema<DSHookFloatingContextT.Props> = {\n withoutPortal: PropTypes.bool\n .description('If true, the tooltip will not be rendered inside a portal.')\n .defaultValue(false),\n withoutAnimation: PropTypes.bool.description('If true, the tooltip will not have an animation.').defaultValue(false),\n portalDOMContainer: PropTypes.object\n .description('The DOM element where the tooltip will be rendered.')\n .defaultValue('the first \"main\" landmark available in the document or the body if no \"main\" is found'),\n animationDuration: PropTypes.number.description('The duration of the animation in milliseconds.').defaultValue(300),\n placement: PropTypes.oneOf([\n 'top-start',\n 'top',\n 'top-end',\n 'right-start',\n 'right',\n 'right-end',\n 'bottom-end',\n 'bottom',\n 'bottom-start',\n 'left-end',\n 'left',\n 'left-start',\n ])\n .description('The placement of the tooltip.')\n .defaultValue('top'),\n customOffset: PropTypes.arrayOf(PropTypes.number)\n .description('The custom offset of the tooltip.')\n .defaultValue([12, 12]),\n placementOrderPreference: PropTypes.oneOfType([\n PropTypes.tuple([PropTypes.oneOf(['top-start'])]),\n PropTypes.tuple([PropTypes.oneOf(['top'])]),\n PropTypes.tuple([PropTypes.oneOf(['top-end'])]),\n PropTypes.tuple([PropTypes.oneOf(['right-start'])]),\n PropTypes.tuple([PropTypes.oneOf(['right'])]),\n PropTypes.tuple([PropTypes.oneOf(['right-end'])]),\n PropTypes.tuple([PropTypes.oneOf(['bottom-end'])]),\n PropTypes.tuple([PropTypes.oneOf(['bottom'])]),\n PropTypes.tuple([PropTypes.oneOf(['bottom-start'])]),\n PropTypes.tuple([PropTypes.oneOf(['left-end'])]),\n PropTypes.tuple([PropTypes.oneOf(['left'])]),\n PropTypes.tuple([PropTypes.oneOf(['left-start'])]),\n ]).description('The order of the placement preference.'),\n onOpen: PropTypes.func.description(\n 'Called on every open transition (interaction or controlled-flip); receives arguments with a `reason`.',\n ),\n onClose: PropTypes.func.description(\n 'Called on every close transition (interaction or controlled-flip); receives arguments with a `reason`.',\n ),\n externallyControlledIsOpen: PropTypes.bool.description(\n 'If true, the context open/close state will be controlled externally.',\n ),\n externalReferenceElement: PropTypes.object.description(\n 'Pre-resolved reference element. When provided, used as the source of truth for positioning, ' +\n 'avoiding the need for a follow-up refs.setReference() effect and the visibility:hidden race on open.',\n ),\n onClickOutside: PropTypes.func.description(\n 'Called on mousedown/touchstart outside both the floating element and the reference element while open.',\n ),\n closeOnEscape: PropTypes.bool\n .description('If true, listens for Escape on the floating element and calls onEscape (or onClose).')\n .defaultValue(false),\n onEscape: PropTypes.func.description('Called when Escape is pressed while focus is within the floating element.'),\n returnFocusToReference: PropTypes.bool\n .description('If true, returns focus to the reference element after Escape-close.')\n .defaultValue(false),\n};\n"],
|
|
5
|
+
"mappings": "AAAA,YAAY,WAAW;ACGvB,SAAS,iBAAiB;AA4FnB,MAAM,eAAoD;AAAA,EAC/D,kBAAkB;AAAA,EAClB,mBAAmB;AAAA,EACnB,eAAe;AAAA,EACf,WAAW;AAAA,EACX,cAAc,CAAC,GAAG,EAAE;AAAA,EACpB,eAAe;AAAA,EACf,wBAAwB;AAC1B;AAEO,MAAM,6BAA8E;AAAA,EACzF,eAAe,UAAU,KACtB,YAAY,4DAA4D,EACxE,aAAa,KAAK;AAAA,EACrB,kBAAkB,UAAU,KAAK,YAAY,kDAAkD,EAAE,aAAa,KAAK;AAAA,EACnH,oBAAoB,UAAU,OAC3B,YAAY,qDAAqD,EACjE,aAAa,uFAAuF;AAAA,EACvG,mBAAmB,UAAU,OAAO,YAAY,gDAAgD,EAAE,aAAa,GAAG;AAAA,EAClH,WAAW,UAAU,MAAM;AAAA,IACzB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC,EACE,YAAY,+BAA+B,EAC3C,aAAa,KAAK;AAAA,EACrB,cAAc,UAAU,QAAQ,UAAU,MAAM,EAC7C,YAAY,mCAAmC,EAC/C,aAAa,CAAC,IAAI,EAAE,CAAC;AAAA,EACxB,0BAA0B,UAAU,UAAU;AAAA,IAC5C,UAAU,MAAM,CAAC,UAAU,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC;AAAA,IAChD,UAAU,MAAM,CAAC,UAAU,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;AAAA,IAC1C,UAAU,MAAM,CAAC,UAAU,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC;AAAA,IAC9C,UAAU,MAAM,CAAC,UAAU,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC;AAAA,IAClD,UAAU,MAAM,CAAC,UAAU,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;AAAA,IAC5C,UAAU,MAAM,CAAC,UAAU,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC;AAAA,IAChD,UAAU,MAAM,CAAC,UAAU,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;AAAA,IACjD,UAAU,MAAM,CAAC,UAAU,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;AAAA,IAC7C,UAAU,MAAM,CAAC,UAAU,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC;AAAA,IACnD,UAAU,MAAM,CAAC,UAAU,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;AAAA,IAC/C,UAAU,MAAM,CAAC,UAAU,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;AAAA,IAC3C,UAAU,MAAM,CAAC,UAAU,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;AAAA,EACnD,CAAC,EAAE,YAAY,wCAAwC;AAAA,EACvD,QAAQ,UAAU,KAAK;AAAA,IACrB;AAAA,EACF;AAAA,EACA,SAAS,UAAU,KAAK;AAAA,IACtB;AAAA,EACF;AAAA,EACA,4BAA4B,UAAU,KAAK;AAAA,IACzC;AAAA,EACF;AAAA,EACA,0BAA0B,UAAU,OAAO;AAAA,IACzC;AAAA,EAEF;AAAA,EACA,gBAAgB,UAAU,KAAK;AAAA,IAC7B;AAAA,EACF;AAAA,EACA,eAAe,UAAU,KACtB,YAAY,sFAAsF,EAClG,aAAa,KAAK;AAAA,EACrB,UAAU,UAAU,KAAK,YAAY,2EAA2E;AAAA,EAChH,wBAAwB,UAAU,KAC/B,YAAY,qEAAqE,EACjF,aAAa,KAAK;AACvB;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -4,13 +4,13 @@ import { debounce } from "lodash-es";
|
|
|
4
4
|
import { computePosition } from "./utils/computePosition.js";
|
|
5
5
|
const useComputedPositionStyles = (config) => {
|
|
6
6
|
const {
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
triggerElementReference,
|
|
8
|
+
floatingWrapperNode,
|
|
9
9
|
placement,
|
|
10
10
|
placementOrderPreference,
|
|
11
11
|
customOffset,
|
|
12
12
|
withoutPortal,
|
|
13
|
-
|
|
13
|
+
isClose = false,
|
|
14
14
|
debounceMs = 150
|
|
15
15
|
} = config;
|
|
16
16
|
const [arrowStyles, setArrowStyles] = useState({ style: { left: 0 }, placement: "top" });
|
|
@@ -22,12 +22,12 @@ const useComputedPositionStyles = (config) => {
|
|
|
22
22
|
willChange: "transform"
|
|
23
23
|
});
|
|
24
24
|
const [hasComputedOnce, setHasComputedOnce] = useState(false);
|
|
25
|
-
const canCompute =
|
|
25
|
+
const canCompute = triggerElementReference !== null && floatingWrapperNode !== null && !isClose;
|
|
26
26
|
const updateStyles = useCallback(() => {
|
|
27
27
|
if (!canCompute) return;
|
|
28
28
|
const { coordsStyle, finalPlacement, coordsArrow } = computePosition({
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
triggerElementReference,
|
|
30
|
+
floatingWrapperNode,
|
|
31
31
|
placement,
|
|
32
32
|
placementOrderPreference,
|
|
33
33
|
customOffset,
|
|
@@ -40,7 +40,15 @@ const useComputedPositionStyles = (config) => {
|
|
|
40
40
|
});
|
|
41
41
|
setArrowStyles({ style: coordsArrow, placement: finalPlacement });
|
|
42
42
|
setHasComputedOnce(true);
|
|
43
|
-
}, [
|
|
43
|
+
}, [
|
|
44
|
+
canCompute,
|
|
45
|
+
triggerElementReference,
|
|
46
|
+
floatingWrapperNode,
|
|
47
|
+
placement,
|
|
48
|
+
placementOrderPreference,
|
|
49
|
+
customOffset,
|
|
50
|
+
withoutPortal
|
|
51
|
+
]);
|
|
44
52
|
const mutableUpdateStyles = useRef(updateStyles);
|
|
45
53
|
mutableUpdateStyles.current = updateStyles;
|
|
46
54
|
const debouncedUpdateStyles = useMemo(() => {
|
|
@@ -61,7 +69,15 @@ const useComputedPositionStyles = (config) => {
|
|
|
61
69
|
if (canCompute) {
|
|
62
70
|
mutableUpdateStyles.current();
|
|
63
71
|
}
|
|
64
|
-
}, [
|
|
72
|
+
}, [
|
|
73
|
+
canCompute,
|
|
74
|
+
triggerElementReference,
|
|
75
|
+
floatingWrapperNode,
|
|
76
|
+
placement,
|
|
77
|
+
placementOrderPreference,
|
|
78
|
+
customOffset,
|
|
79
|
+
withoutPortal
|
|
80
|
+
]);
|
|
65
81
|
const forceUpdatePosition = useCallback(() => {
|
|
66
82
|
mutableUpdateStyles.current();
|
|
67
83
|
}, []);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../scripts/build/transpile/react-shim.js", "../../src/useComputedPositionStyles.tsx"],
|
|
4
|
-
"sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable max-statements */\nimport { useLayoutEffect, useMemo, useRef, useState, useCallback } from 'react';\nimport { debounce } from 'lodash-es';\nimport { type CSSProperties } from 'styled-components';\nimport { computePosition } from './utils/computePosition.js';\nimport type { DSHookFloatingContextT } from './react-desc-prop-types.js';\nimport type { PopoverArrowT } from './parts/PopoverArrow.js';\n\ntype UseComputedPositionStylesT = {\n /** Prevent computing when closed (optimization + avoids unnecessary frames) */\n
|
|
5
|
-
"mappings": "AAAA,YAAY,WAAW;ACCvB,SAAS,iBAAiB,SAAS,QAAQ,UAAU,mBAAmB;AACxE,SAAS,gBAAgB;AAEzB,SAAS,uBAAuB;AAiBzB,MAAM,4BAA4B,CAAC,WAAuC;AAC/E,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,
|
|
4
|
+
"sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable max-statements */\nimport { useLayoutEffect, useMemo, useRef, useState, useCallback } from 'react';\nimport { debounce } from 'lodash-es';\nimport { type CSSProperties } from 'styled-components';\nimport { computePosition } from './utils/computePosition.js';\nimport type { DSHookFloatingContextT } from './react-desc-prop-types.js';\nimport type { PopoverArrowT } from './parts/PopoverArrow.js';\n\ntype UseComputedPositionStylesT = {\n /** Prevent computing when closed (optimization + avoids unnecessary frames) */\n isClose?: boolean;\n triggerElementReference: Element | null;\n floatingWrapperNode: HTMLElement | null;\n placement: DSHookFloatingContextT.PopperPlacementsT;\n placementOrderPreference?: DSHookFloatingContextT.PopperPlacementsT[];\n customOffset: [number, number];\n withoutPortal: boolean;\n /** Debounce ms for scroll/resize/observer events */\n debounceMs?: number;\n};\n\nexport const useComputedPositionStyles = (config: UseComputedPositionStylesT) => {\n const {\n triggerElementReference,\n floatingWrapperNode,\n placement,\n placementOrderPreference,\n customOffset,\n withoutPortal,\n isClose = false,\n debounceMs = 150,\n } = config;\n\n const [arrowStyles, setArrowStyles] = useState<PopoverArrowT>({ style: { left: 0 }, placement: 'top' });\n\n // Initial state: invisible (opacity:0) but FOCUSABLE.\n // We intentionally use opacity instead of visibility:hidden \u2014 `visibility:hidden` blocks\n // programmatic focus on descendants (including React's `autoFocus` attribute on inputs),\n // which causes a race on first-open: the floating content's autoFocus fires before the\n // position-computation useLayoutEffect can flip visibility to `visible`, so the focus\n // silently no-ops. Opacity:0 keeps the element invisible while letting `.focus()` work.\n // pointer-events:none prevents accidental clicks on the still-unpositioned (0,0) area.\n const [floatingStyles, setFloatingStyles] = useState<CSSProperties>({\n position: 'absolute',\n zIndex: 3000,\n opacity: 0,\n pointerEvents: 'none',\n willChange: 'transform',\n });\n\n const [hasComputedOnce, setHasComputedOnce] = useState(false);\n\n const canCompute = triggerElementReference !== null && floatingWrapperNode !== null && !isClose;\n\n const updateStyles = useCallback(() => {\n if (!canCompute) return;\n\n const { coordsStyle, finalPlacement, coordsArrow } = computePosition({\n triggerElementReference,\n floatingWrapperNode,\n placement,\n placementOrderPreference,\n customOffset,\n withoutPortal,\n });\n // INTENTIONAL explicit destructure \u2014 do NOT replace with `...coordsStyle`.\n //\n // PUI-18470 is a ghost bug: an infinite ResizeObserver \u2192 setState \u2192 re-render loop\n // that only triggers at specific viewport pixel combinations (\"magic pixel\") and is\n // nearly impossible to reproduce consistently across machines. It took a synthetic\n // Playwright ResizeObserver-intercept test to surface it at all.\n //\n // Part of the fix is the bail-out below (`prev.transform === transform`): if the\n // computed position hasn't changed we return the same state reference, preventing a\n // re-render and breaking the loop. That bail-out only works if we know exactly which\n // properties coordsStyle contributes. Spreading `...coordsStyle` hides that contract:\n // if computePosition ever extends coordsStyle with a new property, the bail-out\n // silently becomes incomplete and the loop can re-emerge with no obvious cause.\n //\n // Keeping the destructure explicit forces any future change to computePosition's\n // coordsStyle contract to be a conscious, visible decision here \u2014 not a silent pass-through.\n const { transform, top, left } = coordsStyle;\n\n // Do not touch visibility here; it is managed outside depending on open/hasComputedOnce\n setFloatingStyles((prev) => {\n if (prev.transform === transform) return prev;\n return { position: 'absolute', zIndex: 3000, ...prev, transform, top, left };\n });\n setArrowStyles({ style: coordsArrow, placement: finalPlacement });\n setHasComputedOnce(true);\n }, [\n canCompute,\n triggerElementReference,\n floatingWrapperNode,\n placement,\n placementOrderPreference,\n customOffset,\n withoutPortal,\n ]);\n\n // Store latest update function in a ref to keep debounced stable\n const mutableUpdateStyles = useRef(updateStyles);\n mutableUpdateStyles.current = updateStyles;\n\n const debouncedUpdateStyles = useMemo(() => {\n const d = debounce(() => {\n mutableUpdateStyles.current();\n }, debounceMs);\n return d;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [debounceMs]);\n\n const mutableDebouncedStyles = useRef(debouncedUpdateStyles);\n mutableDebouncedStyles.current = debouncedUpdateStyles;\n\n // Clean up debounce on unmount\n useLayoutEffect(\n () => () => {\n debouncedUpdateStyles.cancel();\n },\n [debouncedUpdateStyles],\n );\n\n // Recalculate BEFORE paint when dependencies change\n useLayoutEffect(() => {\n if (canCompute) {\n mutableUpdateStyles.current();\n }\n }, [\n canCompute,\n triggerElementReference,\n floatingWrapperNode,\n placement,\n placementOrderPreference,\n customOffset,\n withoutPortal,\n ]);\n\n const forceUpdatePosition = useCallback(() => {\n mutableUpdateStyles.current();\n }, []);\n\n // Do not reset coordinates when closing; just hide via opacity (keeps element focusable\n // if anything inside needs to remain focusable during animations).\n const resetVisibilityOnly = useCallback(() => {\n setFloatingStyles((prev) => ({\n ...prev,\n opacity: 0,\n pointerEvents: 'none',\n }));\n }, []);\n\n return useMemo(\n () => ({\n arrowStyles,\n floatingStyles,\n hasComputedOnce,\n updateStyles: forceUpdatePosition,\n debouncedUpdateStyles,\n mutableUpdateStyles: mutableDebouncedStyles,\n resetVisibilityOnly,\n }),\n [arrowStyles, floatingStyles, hasComputedOnce, forceUpdatePosition, debouncedUpdateStyles, resetVisibilityOnly],\n );\n};\n"],
|
|
5
|
+
"mappings": "AAAA,YAAY,WAAW;ACCvB,SAAS,iBAAiB,SAAS,QAAQ,UAAU,mBAAmB;AACxE,SAAS,gBAAgB;AAEzB,SAAS,uBAAuB;AAiBzB,MAAM,4BAA4B,CAAC,WAAuC;AAC/E,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,UAAU;AAAA,IACV,aAAa;AAAA,EACf,IAAI;AAEJ,QAAM,CAAC,aAAa,cAAc,IAAI,SAAwB,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,WAAW,MAAM,CAAC;AAStG,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,SAAwB;AAAA,IAClE,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,eAAe;AAAA,IACf,YAAY;AAAA,EACd,CAAC;AAED,QAAM,CAAC,iBAAiB,kBAAkB,IAAI,SAAS,KAAK;AAE5D,QAAM,aAAa,4BAA4B,QAAQ,wBAAwB,QAAQ,CAAC;AAExF,QAAM,eAAe,YAAY,MAAM;AACrC,QAAI,CAAC,WAAY;AAEjB,UAAM,EAAE,aAAa,gBAAgB,YAAY,IAAI,gBAAgB;AAAA,MACnE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAiBD,UAAM,EAAE,WAAW,KAAK,KAAK,IAAI;AAGjC,sBAAkB,CAAC,SAAS;AAC1B,UAAI,KAAK,cAAc,UAAW,QAAO;AACzC,aAAO,EAAE,UAAU,YAAY,QAAQ,KAAM,GAAG,MAAM,WAAW,KAAK,KAAK;AAAA,IAC7E,CAAC;AACD,mBAAe,EAAE,OAAO,aAAa,WAAW,eAAe,CAAC;AAChE,uBAAmB,IAAI;AAAA,EACzB,GAAG;AAAA,IACD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAGD,QAAM,sBAAsB,OAAO,YAAY;AAC/C,sBAAoB,UAAU;AAE9B,QAAM,wBAAwB,QAAQ,MAAM;AAC1C,UAAM,IAAI,SAAS,MAAM;AACvB,0BAAoB,QAAQ;AAAA,IAC9B,GAAG,UAAU;AACb,WAAO;AAAA,EAET,GAAG,CAAC,UAAU,CAAC;AAEf,QAAM,yBAAyB,OAAO,qBAAqB;AAC3D,yBAAuB,UAAU;AAGjC;AAAA,IACE,MAAM,MAAM;AACV,4BAAsB,OAAO;AAAA,IAC/B;AAAA,IACA,CAAC,qBAAqB;AAAA,EACxB;AAGA,kBAAgB,MAAM;AACpB,QAAI,YAAY;AACd,0BAAoB,QAAQ;AAAA,IAC9B;AAAA,EACF,GAAG;AAAA,IACD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,QAAM,sBAAsB,YAAY,MAAM;AAC5C,wBAAoB,QAAQ;AAAA,EAC9B,GAAG,CAAC,CAAC;AAIL,QAAM,sBAAsB,YAAY,MAAM;AAC5C,sBAAkB,CAAC,UAAU;AAAA,MAC3B,GAAG;AAAA,MACH,SAAS;AAAA,MACT,eAAe;AAAA,IACjB,EAAE;AAAA,EACJ,GAAG,CAAC,CAAC;AAEL,SAAO;AAAA,IACL,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA,cAAc;AAAA,MACd;AAAA,MACA,qBAAqB;AAAA,MACrB;AAAA,IACF;AAAA,IACA,CAAC,aAAa,gBAAgB,iBAAiB,qBAAqB,uBAAuB,mBAAmB;AAAA,EAChH;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -13,15 +13,22 @@ import {
|
|
|
13
13
|
getViewportRect
|
|
14
14
|
} from "./floatingPositioning.js";
|
|
15
15
|
const computePosition = (props) => {
|
|
16
|
-
const {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
const {
|
|
17
|
+
triggerElementReference,
|
|
18
|
+
floatingWrapperNode,
|
|
19
|
+
placement,
|
|
20
|
+
placementOrderPreference,
|
|
21
|
+
customOffset,
|
|
22
|
+
withoutPortal
|
|
23
|
+
} = props;
|
|
24
|
+
const parentOffsets = withoutPortal ? adjustForFixedParent(triggerElementReference) : { top: 0, left: 0 };
|
|
25
|
+
const referenceRect = triggerElementReference.getBoundingClientRect();
|
|
26
|
+
const floatingRect = floatingWrapperNode.getBoundingClientRect();
|
|
20
27
|
const fallbackPlacements = placementOrderPreference || getExpandedFallbackPlacements(placement);
|
|
21
28
|
const placements = expandWithVariations(
|
|
22
29
|
[placement].concat(fallbackPlacements)
|
|
23
30
|
);
|
|
24
|
-
const clippingParent = withoutPortal ? getClippingParent(
|
|
31
|
+
const clippingParent = withoutPortal ? getClippingParent(triggerElementReference) : null;
|
|
25
32
|
const clippingRect = clippingParent ? clippingParent.getBoundingClientRect() : getViewportRect();
|
|
26
33
|
let bestPlacement = placement;
|
|
27
34
|
let bestOverflows = null;
|
|
@@ -50,7 +57,7 @@ const computePosition = (props) => {
|
|
|
50
57
|
x += window.scrollX;
|
|
51
58
|
y += window.scrollY;
|
|
52
59
|
} else {
|
|
53
|
-
const op = getOffsetParentData(
|
|
60
|
+
const op = getOffsetParentData(floatingWrapperNode);
|
|
54
61
|
x = x + clippingRect.left - op.left + op.scrollLeft;
|
|
55
62
|
y = y + clippingRect.top - op.top + op.scrollTop;
|
|
56
63
|
}
|
|
@@ -63,7 +70,7 @@ const computePosition = (props) => {
|
|
|
63
70
|
y,
|
|
64
71
|
withoutPortal,
|
|
65
72
|
parentOffsets,
|
|
66
|
-
floatingEl:
|
|
73
|
+
floatingEl: floatingWrapperNode,
|
|
67
74
|
arrowPadding: 12
|
|
68
75
|
});
|
|
69
76
|
return {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../scripts/build/transpile/react-shim.js", "../../../src/utils/computePosition.ts"],
|
|
4
|
-
"sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable max-lines */\n/* eslint-disable no-nested-ternary */\n/* eslint-disable complexity */\n/* eslint-disable max-statements */\n/* eslint-disable @typescript-eslint/no-use-before-define */\n/* eslint-disable max-params */\n/* eslint-disable @typescript-eslint/no-unsafe-assignment */\n/* eslint-disable arrow-body-style */\nimport type { DSHookFloatingContextT } from '../react-desc-prop-types.js';\nimport { getExpandedFallbackPlacements } from './getExpandedFallbackPlacements.js';\nimport { getArrowOffsetDynamic } from './getArrowOffset.js';\nimport { detectOverflow } from './detectOverflow.js';\nimport {\n applyShift,\n adjustForFixedParent,\n expandWithVariations,\n fits,\n getClippingParent,\n getOverflowScore,\n getOffsetParentData,\n getViewportRect,\n type RectLike,\n type OverflowOffsets,\n} from './floatingPositioning.js';\n\ninterface ComputePositionProps {\n
|
|
5
|
-
"mappings": "AAAA,YAAY,WAAW;ACSvB,SAAS,qCAAqC;AAC9C,SAAS,6BAA6B;AACtC,SAAS,sBAAsB;AAC/B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AAWA,MAAM,kBAAkB,CAAC,UAAgC;AAC9D,QAAM,
|
|
4
|
+
"sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable max-lines */\n/* eslint-disable no-nested-ternary */\n/* eslint-disable complexity */\n/* eslint-disable max-statements */\n/* eslint-disable @typescript-eslint/no-use-before-define */\n/* eslint-disable max-params */\n/* eslint-disable @typescript-eslint/no-unsafe-assignment */\n/* eslint-disable arrow-body-style */\nimport type { DSHookFloatingContextT } from '../react-desc-prop-types.js';\nimport { getExpandedFallbackPlacements } from './getExpandedFallbackPlacements.js';\nimport { getArrowOffsetDynamic } from './getArrowOffset.js';\nimport { detectOverflow } from './detectOverflow.js';\nimport {\n applyShift,\n adjustForFixedParent,\n expandWithVariations,\n fits,\n getClippingParent,\n getOverflowScore,\n getOffsetParentData,\n getViewportRect,\n type RectLike,\n type OverflowOffsets,\n} from './floatingPositioning.js';\n\ninterface ComputePositionProps {\n triggerElementReference: Element;\n floatingWrapperNode: HTMLElement;\n placement: DSHookFloatingContextT.PopperPlacementsT;\n placementOrderPreference?: DSHookFloatingContextT.PopperPlacementsT[];\n customOffset: [number, number];\n withoutPortal: boolean;\n}\n\nexport const computePosition = (props: ComputePositionProps) => {\n const {\n triggerElementReference,\n floatingWrapperNode,\n placement,\n placementOrderPreference,\n customOffset,\n withoutPortal,\n } = props;\n\n // When WITHOUT portal: only apply fixed-parent offsets (absolute parents scroll and must NOT be treated as fixed)\n const parentOffsets = withoutPortal ? adjustForFixedParent(triggerElementReference) : { top: 0, left: 0 };\n\n const referenceRect = triggerElementReference.getBoundingClientRect();\n const floatingRect = floatingWrapperNode.getBoundingClientRect();\n\n const fallbackPlacements = placementOrderPreference || getExpandedFallbackPlacements(placement);\n\n const placements = expandWithVariations(\n [placement].concat(fallbackPlacements as DSHookFloatingContextT.PopperPlacementsT[]),\n );\n\n // Boundary selection:\n // - Portal => viewport (inset by body padding for Storybook)\n // - No portal => nearest clipping/scroll container (fallback viewport rect)\n const clippingParent = withoutPortal ? getClippingParent(triggerElementReference) : null;\n const clippingRect: RectLike = clippingParent ? clippingParent.getBoundingClientRect() : getViewportRect();\n\n // Best-fit selection:\n // 1) choose first placement that fully fits\n // 2) otherwise choose placement with smallest max overflow, tie-break by total overflow\n let bestPlacement = placement;\n let bestOverflows: OverflowOffsets | null = null;\n let bestScore = { total: Number.POSITIVE_INFINITY, maxSide: Number.POSITIVE_INFINITY };\n\n for (let i = 0; i < placements.length; i += 1) {\n const currentPlacement = placements[i];\n\n const overflows = detectOverflow(referenceRect, floatingRect, currentPlacement, customOffset, clippingRect);\n\n if (fits(overflows)) {\n bestPlacement = currentPlacement;\n bestOverflows = overflows;\n break;\n }\n\n const score = getOverflowScore(overflows);\n\n const isBetter =\n score.maxSide < bestScore.maxSide || (score.maxSide === bestScore.maxSide && score.total < bestScore.total);\n\n if (isBetter) {\n bestPlacement = currentPlacement;\n bestOverflows = overflows;\n bestScore = score;\n }\n }\n\n const finalPlacement = bestPlacement;\n\n const overflows =\n bestOverflows ?? detectOverflow(referenceRect, floatingRect, finalPlacement, customOffset, clippingRect);\n\n // Convert overflow -> coordinates.\n // detectOverflow uses viewport/clipping-rect coordinates.\n //\n // - If tooltip is rendered IN A PORTAL (withoutPortal === false) and is positioned with `position: absolute`,\n // convert viewport coords to page coords by adding window.scrollX/Y.\n //\n // - If tooltip is rendered WITHOUT portal, convert viewport coords to offsetParent coords\n // (subtract offsetParent rect, add its scroll).\n let x = -overflows.left - parentOffsets.left;\n let y = -overflows.top - parentOffsets.top;\n\n if (!withoutPortal) {\n x += window.scrollX;\n y += window.scrollY;\n } else {\n // clippingRect.top/left may be non-zero (e.g. a scroll container below the viewport top).\n // The overflow values already subtracted it; add it back so we convert pure viewport coords\n // to offset-parent-relative coords: viewportCoord - offsetParent.top + offsetParent.scrollTop\n const op = getOffsetParentData(floatingWrapperNode);\n x = x + clippingRect.left - op.left + op.scrollLeft;\n y = y + clippingRect.top - op.top + op.scrollTop;\n }\n\n // Always shift back inside boundary\n ({ x, y } = applyShift(x, y, overflows));\n\n const coordsArrow = getArrowOffsetDynamic({\n placement: finalPlacement,\n referenceRect,\n floatingRect,\n x,\n y,\n withoutPortal,\n parentOffsets,\n floatingEl: floatingWrapperNode,\n arrowPadding: 12,\n });\n\n return {\n coordsStyle: {\n transform: `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`,\n top: 0,\n left: 0,\n },\n finalPlacement,\n coordsArrow,\n };\n};\n"],
|
|
5
|
+
"mappings": "AAAA,YAAY,WAAW;ACSvB,SAAS,qCAAqC;AAC9C,SAAS,6BAA6B;AACtC,SAAS,sBAAsB;AAC/B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AAWA,MAAM,kBAAkB,CAAC,UAAgC;AAC9D,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AAGJ,QAAM,gBAAgB,gBAAgB,qBAAqB,uBAAuB,IAAI,EAAE,KAAK,GAAG,MAAM,EAAE;AAExG,QAAM,gBAAgB,wBAAwB,sBAAsB;AACpE,QAAM,eAAe,oBAAoB,sBAAsB;AAE/D,QAAM,qBAAqB,4BAA4B,8BAA8B,SAAS;AAE9F,QAAM,aAAa;AAAA,IACjB,CAAC,SAAS,EAAE,OAAO,kBAAgE;AAAA,EACrF;AAKA,QAAM,iBAAiB,gBAAgB,kBAAkB,uBAAuB,IAAI;AACpF,QAAM,eAAyB,iBAAiB,eAAe,sBAAsB,IAAI,gBAAgB;AAKzG,MAAI,gBAAgB;AACpB,MAAI,gBAAwC;AAC5C,MAAI,YAAY,EAAE,OAAO,OAAO,mBAAmB,SAAS,OAAO,kBAAkB;AAErF,WAAS,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK,GAAG;AAC7C,UAAM,mBAAmB,WAAW,CAAC;AAErC,UAAMA,aAAY,eAAe,eAAe,cAAc,kBAAkB,cAAc,YAAY;AAE1G,QAAI,KAAKA,UAAS,GAAG;AACnB,sBAAgB;AAChB,sBAAgBA;AAChB;AAAA,IACF;AAEA,UAAM,QAAQ,iBAAiBA,UAAS;AAExC,UAAM,WACJ,MAAM,UAAU,UAAU,WAAY,MAAM,YAAY,UAAU,WAAW,MAAM,QAAQ,UAAU;AAEvG,QAAI,UAAU;AACZ,sBAAgB;AAChB,sBAAgBA;AAChB,kBAAY;AAAA,IACd;AAAA,EACF;AAEA,QAAM,iBAAiB;AAEvB,QAAM,YACJ,iBAAiB,eAAe,eAAe,cAAc,gBAAgB,cAAc,YAAY;AAUzG,MAAI,IAAI,CAAC,UAAU,OAAO,cAAc;AACxC,MAAI,IAAI,CAAC,UAAU,MAAM,cAAc;AAEvC,MAAI,CAAC,eAAe;AAClB,SAAK,OAAO;AACZ,SAAK,OAAO;AAAA,EACd,OAAO;AAIL,UAAM,KAAK,oBAAoB,mBAAmB;AAClD,QAAI,IAAI,aAAa,OAAO,GAAG,OAAO,GAAG;AACzC,QAAI,IAAI,aAAa,MAAM,GAAG,MAAM,GAAG;AAAA,EACzC;AAGA,GAAC,EAAE,GAAG,EAAE,IAAI,WAAW,GAAG,GAAG,SAAS;AAEtC,QAAM,cAAc,sBAAsB;AAAA,IACxC,WAAW;AAAA,IACX;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAY;AAAA,IACZ,cAAc;AAAA,EAChB,CAAC;AAED,SAAO;AAAA,IACL,aAAa;AAAA,MACX,WAAW,eAAe,KAAK,MAAM,CAAC,CAAC,OAAO,KAAK,MAAM,CAAC,CAAC;AAAA,MAC3D,KAAK;AAAA,MACL,MAAM;AAAA,IACR;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;",
|
|
6
6
|
"names": ["overflows"]
|
|
7
7
|
}
|
|
@@ -1,5 +1,45 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { DSHookFloatingContextT } from './react-desc-prop-types.js';
|
|
3
|
+
/**
|
|
4
|
+
* useFloatingContext — headless positioning + open/close lifecycle for a floating surface
|
|
5
|
+
* (popover / tooltip / dialog-like panel). Returns positioning styles + refs, imperative show/hide
|
|
6
|
+
* helpers, interaction handlers to spread on a trigger, and a discriminated open/close notification.
|
|
7
|
+
*
|
|
8
|
+
* COUPLING: this hook is the shared substrate under ds-app-picker, ds-tooltip and others; its
|
|
9
|
+
* open/close/escape/reason contract is relied on across packages. The consumer that leans hardest on
|
|
10
|
+
* the exact semantics below is ds-app-picker
|
|
11
|
+
* (layout/ds-app-picker/src/parts/AppPickerFloatingContext/useAppPickerFloatingContext.ts). Treat
|
|
12
|
+
* changes to these semantics as cross-package.
|
|
13
|
+
*
|
|
14
|
+
* OPEN STATE — controlled vs uncontrolled
|
|
15
|
+
* - Uncontrolled: `externallyControlledIsOpen` undefined → open state lives here (in the underlying
|
|
16
|
+
* ds-hooks-headless-tooltip) and is driven by the focus/hover handlers + the show/hide helpers.
|
|
17
|
+
* - Controlled: `externallyControlledIsOpen` is a boolean → the consumer owns visibility and the
|
|
18
|
+
* internal open state is inert. `isControlled` is surfaced on every OpenChange payload.
|
|
19
|
+
*
|
|
20
|
+
* NOTIFICATION — one onOpen/onClose pair, two reasons (see OpenChange in react-desc-prop-types)
|
|
21
|
+
* - 'interaction': fired synchronously in the event catch phase by this hook's own interaction
|
|
22
|
+
* handling — focus/hover/blur/mouseleave, an imperative show/hide, or a NON-scoped Escape falling
|
|
23
|
+
* through to onClose — carrying the DOM event when there is one. Fires for controlled contexts too
|
|
24
|
+
* (the pre-flip signal a controlled consumer may act on).
|
|
25
|
+
* - 'controlled-flip': fired from a post-commit effect (useSyntheticEventFromControlledState) when
|
|
26
|
+
* `externallyControlledIsOpen` transitions. No event. NEVER fires on mount — a surface rendered
|
|
27
|
+
* already-open gets no onOpen and must seed any initial focus itself.
|
|
28
|
+
*
|
|
29
|
+
* ESCAPE — one listener, not two
|
|
30
|
+
* Escape dismissal rides ds-hooks-headless-tooltip's single always-on document keydown listener (it
|
|
31
|
+
* calls hideTooltip on ANY Escape, even when this context never opened). handleClose is the one place
|
|
32
|
+
* that interprets it:
|
|
33
|
+
* - SCOPED Escape (was open + closeOnEscape + key 'Escape' + focus within the panel/reference)
|
|
34
|
+
* → routes to `onEscape` (NOT onClose); if returnFocusToReference, focuses the reference.
|
|
35
|
+
* - anything else, including a non-scoped Escape → `onClose` with reason 'interaction'.
|
|
36
|
+
* returnFocusToReference is a SCOPED-ESCAPE-ONLY affordance; no other close path moves focus.
|
|
37
|
+
*
|
|
38
|
+
* FRAGILITY: closeOnEscape is honored independently of who owns open state. A CONTROLLED consumer that
|
|
39
|
+
* leaves closeOnEscape on but does not itself close on Escape will have focus returned to the reference
|
|
40
|
+
* on a scoped Escape while the consumer-owned open state has not changed — focus can leave a panel that
|
|
41
|
+
* stays open. Controlled consumers should own Escape (ds-app-picker uses `closeOnEscape: !onKeyDown`).
|
|
42
|
+
*/
|
|
3
43
|
declare const useFloatingContext: {
|
|
4
44
|
(props?: DSHookFloatingContextT.Props): {
|
|
5
45
|
refs: {
|
|
@@ -10,15 +50,15 @@ declare const useFloatingContext: {
|
|
|
10
50
|
};
|
|
11
51
|
floatingStyles: React.CSSProperties;
|
|
12
52
|
handlers: {
|
|
13
|
-
onMouseEnter:
|
|
14
|
-
onMouseLeave:
|
|
15
|
-
onFocus:
|
|
16
|
-
onBlur:
|
|
53
|
+
onMouseEnter: React.MouseEventHandler<Element>;
|
|
54
|
+
onMouseLeave: React.MouseEventHandler<Element>;
|
|
55
|
+
onFocus: React.FocusEventHandler<Element>;
|
|
56
|
+
onBlur: React.FocusEventHandler<Element>;
|
|
17
57
|
};
|
|
18
58
|
isOpen: boolean;
|
|
19
59
|
arrowStyles: import("./parts/PopoverArrow.js").PopoverArrowT;
|
|
20
|
-
hideTooltip: () => void;
|
|
21
|
-
showTooltip: () => void;
|
|
60
|
+
hideTooltip: (event?: React.FocusEvent | React.MouseEvent | KeyboardEvent) => void;
|
|
61
|
+
showTooltip: (event?: React.FocusEvent | React.MouseEvent | React.KeyboardEvent) => void;
|
|
22
62
|
context: {
|
|
23
63
|
withoutPortal: boolean;
|
|
24
64
|
withoutAnimation: boolean;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { type MutableRefObject } from 'react';
|
|
2
2
|
interface UseFloatingClickOutsideParams {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
isOpen: boolean;
|
|
4
|
+
floatingWrapperNode: HTMLElement | null;
|
|
5
|
+
triggerElementReference: Element | null;
|
|
6
6
|
/**
|
|
7
7
|
* Latest-ref of the click-outside callback. Passed as a ref (rather than a value)
|
|
8
8
|
* so consumers can pass inline functions without re-attaching listeners on every render.
|
|
9
9
|
* Use `useLatestRef(onClickOutside)` to construct it.
|
|
10
10
|
*/
|
|
11
|
-
|
|
11
|
+
onClickOutsideRef: MutableRefObject<((event: MouseEvent | TouchEvent) => void) | undefined>;
|
|
12
12
|
}
|
|
13
13
|
/**
|
|
14
14
|
* Attaches mousedown/touchstart listeners on the document that fire when the click target
|
|
@@ -16,5 +16,5 @@ interface UseFloatingClickOutsideParams {
|
|
|
16
16
|
*
|
|
17
17
|
* Only active when `enabled === true` and both elements exist.
|
|
18
18
|
*/
|
|
19
|
-
export declare const useFloatingClickOutside: ({
|
|
19
|
+
export declare const useFloatingClickOutside: ({ isOpen, floatingWrapperNode, triggerElementReference, onClickOutsideRef, }: UseFloatingClickOutsideParams) => void;
|
|
20
20
|
export {};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
interface UseFloatingResizeObserverParams {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
isOpen: boolean;
|
|
3
|
+
floatingWrapperNode: HTMLElement | null;
|
|
4
4
|
onResize: () => void;
|
|
5
5
|
}
|
|
6
6
|
/**
|
|
7
7
|
* Observes the floating element's size and calls `onResize` when its bounding box changes.
|
|
8
8
|
* Used to re-run position computation when the floating content reflows (e.g. async-loaded data).
|
|
9
9
|
*/
|
|
10
|
-
export declare const useFloatingResizeObserver: ({
|
|
10
|
+
export declare const useFloatingResizeObserver: ({ isOpen, floatingWrapperNode, onResize, }: UseFloatingResizeObserverParams) => void;
|
|
11
11
|
export {};
|
|
@@ -13,7 +13,7 @@ interface UseResolvedReferenceParams {
|
|
|
13
13
|
* (typically as a callback ref: `innerRef={refs.setReference}`).
|
|
14
14
|
*/
|
|
15
15
|
export declare const useResolvedReference: ({ externalReferenceElement, internalReferenceElement, setInternalReferenceElement, }: UseResolvedReferenceParams) => {
|
|
16
|
-
|
|
16
|
+
triggerElementReference: Element | null;
|
|
17
17
|
setReferenceElement: (el: Element | null) => void;
|
|
18
18
|
};
|
|
19
19
|
export {};
|