@elliemae/ds-floating-context 3.70.0-next.54 → 3.70.0-next.59

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.
Files changed (36) hide show
  1. package/dist/cjs/DSFloatingContext.js +10 -3
  2. package/dist/cjs/DSFloatingContext.js.map +2 -2
  3. package/dist/cjs/hooks/useReferenceHiddenByBoundary.js +59 -0
  4. package/dist/cjs/hooks/useReferenceHiddenByBoundary.js.map +7 -0
  5. package/dist/cjs/package.json +1 -4
  6. package/dist/cjs/react-desc-prop-types.js +3 -0
  7. package/dist/cjs/react-desc-prop-types.js.map +2 -2
  8. package/dist/cjs/typescript-testing/typescript-floating-context-valid.js +3 -1
  9. package/dist/cjs/typescript-testing/typescript-floating-context-valid.js.map +2 -2
  10. package/dist/cjs/useComputedPositionStyles.js +7 -3
  11. package/dist/cjs/useComputedPositionStyles.js.map +2 -2
  12. package/dist/cjs/utils/computePosition.js +13 -4
  13. package/dist/cjs/utils/computePosition.js.map +2 -2
  14. package/dist/cjs/utils/floatingPositioning.js +11 -0
  15. package/dist/cjs/utils/floatingPositioning.js.map +2 -2
  16. package/dist/esm/DSFloatingContext.js +10 -3
  17. package/dist/esm/DSFloatingContext.js.map +2 -2
  18. package/dist/esm/hooks/useReferenceHiddenByBoundary.js +29 -0
  19. package/dist/esm/hooks/useReferenceHiddenByBoundary.js.map +7 -0
  20. package/dist/esm/package.json +1 -4
  21. package/dist/esm/react-desc-prop-types.js +3 -0
  22. package/dist/esm/react-desc-prop-types.js.map +2 -2
  23. package/dist/esm/typescript-testing/typescript-floating-context-valid.js +3 -1
  24. package/dist/esm/typescript-testing/typescript-floating-context-valid.js.map +2 -2
  25. package/dist/esm/useComputedPositionStyles.js +7 -3
  26. package/dist/esm/useComputedPositionStyles.js.map +2 -2
  27. package/dist/esm/utils/computePosition.js +14 -4
  28. package/dist/esm/utils/computePosition.js.map +2 -2
  29. package/dist/esm/utils/floatingPositioning.js +11 -0
  30. package/dist/esm/utils/floatingPositioning.js.map +2 -2
  31. package/dist/types/hooks/useReferenceHiddenByBoundary.d.ts +17 -0
  32. package/dist/types/react-desc-prop-types.d.ts +20 -0
  33. package/dist/types/useComputedPositionStyles.d.ts +3 -0
  34. package/dist/types/utils/computePosition.d.ts +23 -0
  35. package/dist/types/utils/floatingPositioning.d.ts +13 -1
  36. package/package.json +8 -8
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../scripts/build/transpile/react-shim.js", "../../../src/utils/floatingPositioning.ts"],
4
- "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import type { DSHookFloatingContextT } from '../react-desc-prop-types.js';\n\ntype RectLike = { top: number; left: number; right: number; bottom: number };\ntype OverflowOffsets = { top: number; bottom: number; left: number; right: number };\n\nconst findFixedParent = (el: Element | null) => {\n let element = el;\n while (element && element !== document.body) {\n const style = getComputedStyle(element);\n // IMPORTANT: only treat FIXED as special. Absolute parents are common and scroll,\n // and using their rect as an offset breaks positioning on scroll.\n if (style.position === 'fixed') {\n return element;\n }\n element = element.parentElement;\n }\n return null;\n};\n\nconst adjustForFixedParent = (child: Element) => {\n const fixedParent = findFixedParent(child);\n if (fixedParent) {\n const rect = fixedParent.getBoundingClientRect();\n return { top: rect.top, left: rect.left };\n }\n return { top: 0, left: 0 };\n};\n\n/**\n * Finds nearest ancestor that can CLIP the floating element.\n * This is typically the correct boundary when rendering without portal.\n */\nconst getClippingParent = (el: Element | null): Element | null => {\n let node = el?.parentElement ?? null;\n\n while (node && node !== document.body) {\n const style = getComputedStyle(node);\n\n const overflowX = style.overflowX || style.overflow;\n const overflowY = style.overflowY || style.overflow;\n\n const clipsX = /(auto|scroll|hidden|clip)/.test(overflowX);\n const clipsY = /(auto|scroll|hidden|clip)/.test(overflowY);\n\n if (clipsX || clipsY) return node;\n\n node = node.parentElement;\n }\n\n return null;\n};\n\nconst getViewportRect = (): RectLike => ({\n top: 0,\n left: 0,\n right: window.innerWidth,\n bottom: window.innerHeight,\n});\n\n/**\n * Shift behavior: if any overflow side is positive, push the popper back inside.\n * Similar to Floating UI \"shift\" middleware.\n */\nconst applyShift = (x: number, y: number, overflow: OverflowOffsets) => {\n let nextX = x;\n let nextY = y;\n\n if (overflow.left > 0) nextX += overflow.left;\n if (overflow.right > 0) nextX -= overflow.right;\n\n if (overflow.top > 0) nextY += overflow.top;\n if (overflow.bottom > 0) nextY -= overflow.bottom;\n\n return { x: nextX, y: nextY };\n};\n\nconst fits = (o: OverflowOffsets) => o.top <= 0 && o.bottom <= 0 && o.left <= 0 && o.right <= 0;\n\nconst getOverflowScore = (o: OverflowOffsets) => {\n const top = Math.max(0, o.top);\n const bottom = Math.max(0, o.bottom);\n const left = Math.max(0, o.left);\n const right = Math.max(0, o.right);\n\n const total = top + bottom + left + right;\n const maxSide = Math.max(top, bottom, left, right);\n\n return { total, maxSide };\n};\n\n/**\n * Ensure we actually TRY variations (start/end) for each base placement.\n * Fixes cases where only e.g. \"bottom-end\" fits but would never be tried.\n */\nconst expandWithVariations = (pls: DSHookFloatingContextT.PopperPlacementsT[]) => {\n const out: DSHookFloatingContextT.PopperPlacementsT[] = [];\n const seen = new Set<string>();\n\n const add = (p: string) => {\n if (!seen.has(p)) {\n seen.add(p);\n out.push(p as DSHookFloatingContextT.PopperPlacementsT);\n }\n };\n\n pls.forEach((p) => {\n add(p);\n\n const [base, variation] = p.split('-');\n if (!variation) {\n add(`${base}-start`);\n add(`${base}-end`);\n }\n });\n\n return out;\n};\n\n/**\n * When rendering WITHOUT portal, the floating element is positioned relative to its offsetParent.\n * But referenceRect/floatingRect are in viewport coordinates, so we must convert x/y from viewport\n * space into the offsetParent's coordinate space (including its scroll offsets).\n */\nconst getOffsetParentData = (floating: HTMLElement) => {\n const op = floating.offsetParent as HTMLElement | null;\n\n // If there's no offsetParent (rare), assume viewport.\n if (!op) {\n return { left: 0, top: 0, scrollLeft: 0, scrollTop: 0 };\n }\n\n const opRect = op.getBoundingClientRect();\n\n return {\n left: opRect.left,\n top: opRect.top,\n scrollLeft: op.scrollLeft ?? 0,\n scrollTop: op.scrollTop ?? 0,\n };\n};\n\nexport {\n applyShift,\n adjustForFixedParent,\n expandWithVariations,\n fits,\n getClippingParent,\n getOverflowScore,\n getOffsetParentData,\n getViewportRect,\n type RectLike,\n type OverflowOffsets,\n};\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import type { DSHookFloatingContextT } from '../react-desc-prop-types.js';\n\ntype RectLike = { top: number; left: number; right: number; bottom: number };\ntype OverflowOffsets = { top: number; bottom: number; left: number; right: number };\n\nconst findFixedParent = (el: Element | null) => {\n let element = el;\n while (element && element !== document.body) {\n const style = getComputedStyle(element);\n // IMPORTANT: only treat FIXED as special. Absolute parents are common and scroll,\n // and using their rect as an offset breaks positioning on scroll.\n if (style.position === 'fixed') {\n return element;\n }\n element = element.parentElement;\n }\n return null;\n};\n\nconst adjustForFixedParent = (child: Element) => {\n const fixedParent = findFixedParent(child);\n if (fixedParent) {\n const rect = fixedParent.getBoundingClientRect();\n return { top: rect.top, left: rect.left };\n }\n return { top: 0, left: 0 };\n};\n\n/**\n * Finds nearest ancestor that can CLIP the floating element.\n * This is typically the correct boundary when rendering without portal.\n */\nconst getClippingParent = (el: Element | null): Element | null => {\n let node = el?.parentElement ?? null;\n\n while (node && node !== document.body) {\n const style = getComputedStyle(node);\n\n const overflowX = style.overflowX || style.overflow;\n const overflowY = style.overflowY || style.overflow;\n\n const clipsX = /(auto|scroll|hidden|clip)/.test(overflowX);\n const clipsY = /(auto|scroll|hidden|clip)/.test(overflowY);\n\n if (clipsX || clipsY) return node;\n\n node = node.parentElement;\n }\n\n return null;\n};\n\nconst getViewportRect = (): RectLike => ({\n top: 0,\n left: 0,\n right: window.innerWidth,\n bottom: window.innerHeight,\n});\n\n/**\n * Intersects the viewport with an explicit boundary (e.g. a scrollable ancestor the consumer\n * wants the floating element visually contained within), one axis at a time. An axis is only\n * constrained to the boundary when the floating element's own size actually fits within it \u2014\n * otherwise that axis falls back to the plain viewport. Without this fallback, content larger\n * than the boundary (e.g. a full calendar inside a short table row) gets forced into an\n * impossible position instead of being allowed to escape it, as it does today.\n */\nconst getBoundaryConstrainedRect = (\n viewportRect: RectLike,\n boundaryRect: RectLike,\n floatingSize: { width: number; height: number },\n): RectLike => {\n const fitsWidth = floatingSize.width <= boundaryRect.right - boundaryRect.left;\n const fitsHeight = floatingSize.height <= boundaryRect.bottom - boundaryRect.top;\n\n return {\n top: fitsHeight ? Math.max(viewportRect.top, boundaryRect.top) : viewportRect.top,\n bottom: fitsHeight ? Math.min(viewportRect.bottom, boundaryRect.bottom) : viewportRect.bottom,\n left: fitsWidth ? Math.max(viewportRect.left, boundaryRect.left) : viewportRect.left,\n right: fitsWidth ? Math.min(viewportRect.right, boundaryRect.right) : viewportRect.right,\n };\n};\n\n/**\n * Shift behavior: if any overflow side is positive, push the popper back inside.\n * Similar to Floating UI \"shift\" middleware.\n */\nconst applyShift = (x: number, y: number, overflow: OverflowOffsets) => {\n let nextX = x;\n let nextY = y;\n\n if (overflow.left > 0) nextX += overflow.left;\n if (overflow.right > 0) nextX -= overflow.right;\n\n if (overflow.top > 0) nextY += overflow.top;\n if (overflow.bottom > 0) nextY -= overflow.bottom;\n\n return { x: nextX, y: nextY };\n};\n\nconst fits = (o: OverflowOffsets) => o.top <= 0 && o.bottom <= 0 && o.left <= 0 && o.right <= 0;\n\nconst getOverflowScore = (o: OverflowOffsets) => {\n const top = Math.max(0, o.top);\n const bottom = Math.max(0, o.bottom);\n const left = Math.max(0, o.left);\n const right = Math.max(0, o.right);\n\n const total = top + bottom + left + right;\n const maxSide = Math.max(top, bottom, left, right);\n\n return { total, maxSide };\n};\n\n/**\n * Ensure we actually TRY variations (start/end) for each base placement.\n * Fixes cases where only e.g. \"bottom-end\" fits but would never be tried.\n */\nconst expandWithVariations = (pls: DSHookFloatingContextT.PopperPlacementsT[]) => {\n const out: DSHookFloatingContextT.PopperPlacementsT[] = [];\n const seen = new Set<string>();\n\n const add = (p: string) => {\n if (!seen.has(p)) {\n seen.add(p);\n out.push(p as DSHookFloatingContextT.PopperPlacementsT);\n }\n };\n\n pls.forEach((p) => {\n add(p);\n\n const [base, variation] = p.split('-');\n if (!variation) {\n add(`${base}-start`);\n add(`${base}-end`);\n }\n });\n\n return out;\n};\n\n/**\n * When rendering WITHOUT portal, the floating element is positioned relative to its offsetParent.\n * But referenceRect/floatingRect are in viewport coordinates, so we must convert x/y from viewport\n * space into the offsetParent's coordinate space (including its scroll offsets).\n */\nconst getOffsetParentData = (floating: HTMLElement) => {\n const op = floating.offsetParent as HTMLElement | null;\n\n // If there's no offsetParent (rare), assume viewport.\n if (!op) {\n return { left: 0, top: 0, scrollLeft: 0, scrollTop: 0 };\n }\n\n const opRect = op.getBoundingClientRect();\n\n return {\n left: opRect.left,\n top: opRect.top,\n scrollLeft: op.scrollLeft ?? 0,\n scrollTop: op.scrollTop ?? 0,\n };\n};\n\nexport {\n applyShift,\n adjustForFixedParent,\n expandWithVariations,\n fits,\n getBoundaryConstrainedRect,\n getClippingParent,\n getOverflowScore,\n getOffsetParentData,\n getViewportRect,\n type RectLike,\n type OverflowOffsets,\n};\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Tracks whether `reference` is currently visible at all within `boundaryElement` — e.g. a
3
+ * scrollable ancestor the reference lives inside. Returns true the moment the reference scrolls
4
+ * fully out of that boundary's visible area (intersection ratio 0), even though it may still be
5
+ * within the browser viewport; flips back to false as soon as it's visible again. This is what
6
+ * boundaryElement/clamping cannot solve on its own: a clamped floating element just sticks at
7
+ * the boundary's edge, orphaned, once its reference is no longer visible to anchor it to — the
8
+ * consumer still considers it "open," it should just not be shown while its anchor isn't there.
9
+ *
10
+ * Always false (inert) when boundaryElement is not provided — this is opt-in, paired with
11
+ * useFloatingContext's boundaryElement prop.
12
+ */
13
+ export declare const useReferenceHiddenByBoundary: ({ reference, boundaryElement, enabled, }: {
14
+ reference: Element | null;
15
+ boundaryElement?: Element | null;
16
+ enabled: boolean;
17
+ }) => boolean;
@@ -55,6 +55,26 @@ export declare namespace DSHookFloatingContextT {
55
55
  * removes the visibility:hidden race that breaks programmatic focus on open.
56
56
  */
57
57
  externalReferenceElement?: Element | null;
58
+ /**
59
+ * Opt-in extra boundary — e.g. a scrollable ancestor of the reference — the floating
60
+ * element should stay visually contained within, in addition to the viewport. Only
61
+ * applied when portaled (withoutPortal: false, the default).
62
+ *
63
+ * Not the same as `withoutPortal: true`, and not a substitute for it: withoutPortal already
64
+ * derives a clipping ancestor automatically, but does so by rendering the floating element
65
+ * as a literal DOM descendant of it — so the browser's own CSS overflow clipping applies
66
+ * unconditionally, cutting off any floating content taller/wider than that ancestor, with no
67
+ * way for position math to prevent it. boundaryElement keeps the portal, so oversized
68
+ * content (e.g. a full calendar popover inside a short container) still escapes fully into
69
+ * the page, unclipped — it only changes the clipping *math* used for shift/flip, ignored
70
+ * per-axis when the floating content doesn't fit that axis of the boundary, falling back to
71
+ * the plain viewport for that axis instead of forcing an impossible position.
72
+ *
73
+ * Also hides the floating element (opacity/pointer-events only — isOpen state is untouched)
74
+ * whenever the reference scrolls fully out of this boundary's visible area, and shows it
75
+ * again automatically once the reference is back in view.
76
+ */
77
+ boundaryElement?: Element | null;
58
78
  /**
59
79
  * Called when the user clicks/taps outside both the floating element and
60
80
  * the reference element while the floating is open.
@@ -10,6 +10,9 @@ type UseComputedPositionStylesT = {
10
10
  placementOrderPreference?: DSHookFloatingContextT.PopperPlacementsT[];
11
11
  customOffset: [number, number];
12
12
  withoutPortal: boolean;
13
+ /** Opt-in extra boundary the floating element should stay visually contained within, in
14
+ * addition to the viewport, when portaled. See computePosition's boundaryElement doc. */
15
+ boundaryElement?: Element | null;
13
16
  /** Debounce ms for scroll/resize/observer events */
14
17
  debounceMs?: number;
15
18
  };
@@ -6,6 +6,29 @@ interface ComputePositionProps {
6
6
  placementOrderPreference?: DSHookFloatingContextT.PopperPlacementsT[];
7
7
  customOffset: [number, number];
8
8
  withoutPortal: boolean;
9
+ /**
10
+ * Opt-in extra boundary (e.g. a scrollable ancestor of the reference) the floating element
11
+ * should stay visually contained within, in addition to the viewport. Only applied when
12
+ * portaled — the non-portal path already derives its own clipping ancestor automatically via
13
+ * getClippingParent, by walking up from the reference.
14
+ *
15
+ * Why this exists instead of just setting `withoutPortal: true` to get that same automatic
16
+ * clipping-ancestor derivation: withoutPortal renders the floating element as a literal DOM
17
+ * descendant of the clipping ancestor, so the browser's own CSS overflow clipping applies
18
+ * unconditionally — any floating content taller/wider than that ancestor gets visually cut
19
+ * off, with no way for position math to prevent it. That is fine for content that always fits
20
+ * (e.g. a small filter's low/high inputs), but breaks content that can legitimately be larger
21
+ * than its reference's scroll container (e.g. a date-range calendar popover inside a short
22
+ * table row) — see PUI-17187 in ds-data-table's FilterPopover for the exact regression this
23
+ * caused when withoutPortal was tried first.
24
+ *
25
+ * boundaryElement keeps the portal (so oversized content still escapes fully into the page,
26
+ * unclipped, exactly like the no-boundaryElement case) and only changes the clipping *rect*
27
+ * used for shift/flip math — ignored per-axis when the floating content doesn't fit that axis
28
+ * of the boundary (see getBoundaryConstrainedRect), falling back to the plain viewport for
29
+ * that axis so oversized content is never forced into an impossible position.
30
+ */
31
+ boundaryElement?: Element | null;
9
32
  }
10
33
  export declare const computePosition: (props: ComputePositionProps) => {
11
34
  coordsStyle: {
@@ -21,6 +21,18 @@ declare const adjustForFixedParent: (child: Element) => {
21
21
  */
22
22
  declare const getClippingParent: (el: Element | null) => Element | null;
23
23
  declare const getViewportRect: () => RectLike;
24
+ /**
25
+ * Intersects the viewport with an explicit boundary (e.g. a scrollable ancestor the consumer
26
+ * wants the floating element visually contained within), one axis at a time. An axis is only
27
+ * constrained to the boundary when the floating element's own size actually fits within it —
28
+ * otherwise that axis falls back to the plain viewport. Without this fallback, content larger
29
+ * than the boundary (e.g. a full calendar inside a short table row) gets forced into an
30
+ * impossible position instead of being allowed to escape it, as it does today.
31
+ */
32
+ declare const getBoundaryConstrainedRect: (viewportRect: RectLike, boundaryRect: RectLike, floatingSize: {
33
+ width: number;
34
+ height: number;
35
+ }) => RectLike;
24
36
  /**
25
37
  * Shift behavior: if any overflow side is positive, push the popper back inside.
26
38
  * Similar to Floating UI "shift" middleware.
@@ -50,4 +62,4 @@ declare const getOffsetParentData: (floating: HTMLElement) => {
50
62
  scrollLeft: number;
51
63
  scrollTop: number;
52
64
  };
53
- export { applyShift, adjustForFixedParent, expandWithVariations, fits, getClippingParent, getOverflowScore, getOffsetParentData, getViewportRect, type RectLike, type OverflowOffsets, };
65
+ export { applyShift, adjustForFixedParent, expandWithVariations, fits, getBoundaryConstrainedRect, getClippingParent, getOverflowScore, getOffsetParentData, getViewportRect, type RectLike, type OverflowOffsets, };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elliemae/ds-floating-context",
3
- "version": "3.70.0-next.54",
3
+ "version": "3.70.0-next.59",
4
4
  "license": "MIT",
5
5
  "description": "ICE MT - Dimsum - Popper Hook",
6
6
  "files": [
@@ -36,20 +36,20 @@
36
36
  "indent": 4
37
37
  },
38
38
  "dependencies": {
39
- "@elliemae/ds-hooks-headless-tooltip": "3.70.0-next.54",
40
- "@elliemae/ds-props-helpers": "3.70.0-next.54",
41
- "@elliemae/ds-typescript-helpers": "3.70.0-next.54",
42
- "@elliemae/ds-system": "3.70.0-next.54"
39
+ "@elliemae/ds-hooks-headless-tooltip": "3.70.0-next.59",
40
+ "@elliemae/ds-props-helpers": "3.70.0-next.59",
41
+ "@elliemae/ds-typescript-helpers": "3.70.0-next.59",
42
+ "@elliemae/ds-system": "3.70.0-next.59"
43
43
  },
44
44
  "devDependencies": {
45
45
  "jest": "^30.0.0",
46
- "@elliemae/ds-monorepo-devops": "3.70.0-next.54"
46
+ "@elliemae/ds-monorepo-devops": "3.70.0-next.59"
47
47
  },
48
48
  "peerDependencies": {
49
49
  "lodash-es": "^4.18.1",
50
50
  "react": "^18.3.1",
51
51
  "react-dom": "^18.3.1",
52
- "styled-components": "~5.3.9",
52
+ "styled-components": "~5.3.11",
53
53
  "styled-system": "^5.1.5"
54
54
  },
55
55
  "publishConfig": {
@@ -65,4 +65,4 @@
65
65
  "build": "cross-env NODE_ENV=production node ../../../scripts/build/build.mjs",
66
66
  "checkDeps": "npx -yes ../../util/ds-codemods check-missing-packages --projectFolderPath=\"./\" --ignorePackagesGlobPattern=\"\" --ignoreFilesGlobPattern=\"**/test-ables/*,**/tests/*\""
67
67
  }
68
- }
68
+ }