@umamichi-ui/common-components 0.3.0 → 0.3.1
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useOverlayPresence.d.ts","sourceRoot":"","sources":["../../src/presence/useOverlayPresence.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"useOverlayPresence.d.ts","sourceRoot":"","sources":["../../src/presence/useOverlayPresence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgD,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AA0ErF,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EAAE,MAAM,EAAE,OAAO;;;;EA4DtF;AAED,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,UAEjE;AAED,yFAAyF;AACzF,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,WAAW,EACpD,UAAU,EAAE,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,EAC/B,GAAG,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC,IAE3E,MAAM,CAAC,GAAG,IAAI,UAavB"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useRef, useState, useEffect } from "react";
|
|
1
|
+
import { useRef, useState, useLayoutEffect, useEffect } from "react";
|
|
2
2
|
const FALLBACK_OVERLAY_MS = 200;
|
|
3
3
|
function parseCssTime(value) {
|
|
4
4
|
const trimmed = value.trim();
|
|
@@ -63,17 +63,30 @@ function useOverlayPresence(active) {
|
|
|
63
63
|
const overlayRef = useRef(null);
|
|
64
64
|
const [mounted, setMounted] = useState(active);
|
|
65
65
|
const [isOpen, setIsOpen] = useState(false);
|
|
66
|
-
|
|
66
|
+
useLayoutEffect(() => {
|
|
67
67
|
if (active) {
|
|
68
68
|
setMounted(true);
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
return
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
69
|
+
}
|
|
70
|
+
}, [active]);
|
|
71
|
+
useLayoutEffect(() => {
|
|
72
|
+
if (!active || !mounted) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const el = overlayRef.current;
|
|
76
|
+
if (!el) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
void el.getBoundingClientRect();
|
|
80
|
+
const rafId = requestAnimationFrame(() => {
|
|
81
|
+
setIsOpen(true);
|
|
82
|
+
});
|
|
83
|
+
return () => {
|
|
84
|
+
cancelAnimationFrame(rafId);
|
|
85
|
+
};
|
|
86
|
+
}, [active, mounted]);
|
|
87
|
+
useEffect(() => {
|
|
88
|
+
if (active) {
|
|
89
|
+
return;
|
|
77
90
|
}
|
|
78
91
|
setIsOpen(false);
|
|
79
92
|
let cancelWait;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useOverlayPresence.js","sources":["../../src/presence/useOverlayPresence.ts"],"sourcesContent":["import { useEffect, useRef, useState, type RefObject } from 'react';\n\n/** Fallback when computed transition duration is unavailable (see `--transition-overlay` in common-css). */\nconst FALLBACK_OVERLAY_MS = 200;\n\nfunction parseCssTime(value: string): number {\n const trimmed = value.trim();\n if (!trimmed || trimmed === '0') {\n return 0;\n }\n if (trimmed.endsWith('ms')) {\n return Number.parseFloat(trimmed);\n }\n if (trimmed.endsWith('s')) {\n return Number.parseFloat(trimmed) * 1000;\n }\n return Number.parseFloat(trimmed) || 0;\n}\n\nfunction getMaxTransitionMs(el: HTMLElement): number {\n const style = getComputedStyle(el);\n const durations = style.transitionDuration.split(',').map(parseCssTime);\n const delays = style.transitionDelay.split(',').map(parseCssTime);\n let max = 0;\n const count = Math.max(durations.length, delays.length, 1);\n\n for (let i = 0; i < count; i += 1) {\n const duration = durations[i % durations.length] ?? durations[0] ?? 0;\n const delay = delays[i % delays.length] ?? delays[0] ?? 0;\n max = Math.max(max, duration + delay);\n }\n\n return max > 0 ? max : FALLBACK_OVERLAY_MS;\n}\n\nfunction waitForSubtreeTransitions(el: HTMLElement, onDone: () => void): () => void {\n let settled = false;\n let debounceId = 0;\n let safetyId = 0;\n\n const cleanup = () => {\n el.removeEventListener('transitionend', onTransitionEnd);\n window.clearTimeout(debounceId);\n window.clearTimeout(safetyId);\n };\n\n const finish = () => {\n if (settled) {\n return;\n }\n settled = true;\n cleanup();\n onDone();\n };\n\n const onTransitionEnd = (event: TransitionEvent) => {\n if (event.target instanceof Node && !el.contains(event.target)) {\n return;\n }\n window.clearTimeout(debounceId);\n debounceId = window.setTimeout(finish, 0);\n };\n\n safetyId = window.setTimeout(finish, getMaxTransitionMs(el) + 50);\n el.addEventListener('transitionend', onTransitionEnd);\n\n return () => {\n if (!settled) {\n settled = true;\n cleanup();\n }\n };\n}\n\nexport function useOverlayPresence<T extends HTMLElement = HTMLElement>(active: boolean) {\n const overlayRef = useRef<T | null>(null);\n const [mounted, setMounted] = useState(active);\n const [isOpen, setIsOpen] = useState(false);\n\n
|
|
1
|
+
{"version":3,"file":"useOverlayPresence.js","sources":["../../src/presence/useOverlayPresence.ts"],"sourcesContent":["import { useEffect, useLayoutEffect, useRef, useState, type RefObject } from 'react';\n\n/** Fallback when computed transition duration is unavailable (see `--transition-overlay` in common-css). */\nconst FALLBACK_OVERLAY_MS = 200;\n\nfunction parseCssTime(value: string): number {\n const trimmed = value.trim();\n if (!trimmed || trimmed === '0') {\n return 0;\n }\n if (trimmed.endsWith('ms')) {\n return Number.parseFloat(trimmed);\n }\n if (trimmed.endsWith('s')) {\n return Number.parseFloat(trimmed) * 1000;\n }\n return Number.parseFloat(trimmed) || 0;\n}\n\nfunction getMaxTransitionMs(el: HTMLElement): number {\n const style = getComputedStyle(el);\n const durations = style.transitionDuration.split(',').map(parseCssTime);\n const delays = style.transitionDelay.split(',').map(parseCssTime);\n let max = 0;\n const count = Math.max(durations.length, delays.length, 1);\n\n for (let i = 0; i < count; i += 1) {\n const duration = durations[i % durations.length] ?? durations[0] ?? 0;\n const delay = delays[i % delays.length] ?? delays[0] ?? 0;\n max = Math.max(max, duration + delay);\n }\n\n return max > 0 ? max : FALLBACK_OVERLAY_MS;\n}\n\nfunction waitForSubtreeTransitions(el: HTMLElement, onDone: () => void): () => void {\n let settled = false;\n let debounceId = 0;\n let safetyId = 0;\n\n const cleanup = () => {\n el.removeEventListener('transitionend', onTransitionEnd);\n window.clearTimeout(debounceId);\n window.clearTimeout(safetyId);\n };\n\n const finish = () => {\n if (settled) {\n return;\n }\n settled = true;\n cleanup();\n onDone();\n };\n\n const onTransitionEnd = (event: TransitionEvent) => {\n if (event.target instanceof Node && !el.contains(event.target)) {\n return;\n }\n window.clearTimeout(debounceId);\n debounceId = window.setTimeout(finish, 0);\n };\n\n safetyId = window.setTimeout(finish, getMaxTransitionMs(el) + 50);\n el.addEventListener('transitionend', onTransitionEnd);\n\n return () => {\n if (!settled) {\n settled = true;\n cleanup();\n }\n };\n}\n\nexport function useOverlayPresence<T extends HTMLElement = HTMLElement>(active: boolean) {\n const overlayRef = useRef<T | null>(null);\n const [mounted, setMounted] = useState(active);\n const [isOpen, setIsOpen] = useState(false);\n\n useLayoutEffect(() => {\n if (active) {\n setMounted(true);\n }\n }, [active]);\n\n useLayoutEffect(() => {\n if (!active || !mounted) {\n return;\n }\n\n const el = overlayRef.current;\n if (!el) {\n return;\n }\n\n // Commit closed styles before toggling is-open so CSS transitions always have a start state.\n void el.getBoundingClientRect();\n\n const rafId = requestAnimationFrame(() => {\n setIsOpen(true);\n });\n\n return () => {\n cancelAnimationFrame(rafId);\n };\n }, [active, mounted]);\n\n useEffect(() => {\n if (active) {\n return;\n }\n\n setIsOpen(false);\n\n let cancelWait: (() => void) | undefined;\n let exitRaf = 0;\n\n exitRaf = requestAnimationFrame(() => {\n const el = overlayRef.current;\n if (!el) {\n setMounted(false);\n return;\n }\n\n cancelWait = waitForSubtreeTransitions(el, () => setMounted(false));\n });\n\n return () => {\n cancelAnimationFrame(exitRaf);\n cancelWait?.();\n };\n }, [active]);\n\n return { mounted, isOpen, overlayRef };\n}\n\nexport function withOverlayOpen(className: string, isOpen: boolean) {\n return isOpen ? `${className} is-open` : className;\n}\n\n/** Attach the same DOM node to multiple refs (e.g. layout measure + transition root). */\nexport function mergeOverlayRefs<T extends HTMLElement>(\n overlayRef: RefObject<T | null>,\n ...refs: Array<RefObject<T | null> | ((node: T | null) => void) | null | undefined>\n) {\n return (node: T | null) => {\n overlayRef.current = node;\n for (const ref of refs) {\n if (!ref) {\n continue;\n }\n if (typeof ref === 'function') {\n ref(node);\n continue;\n }\n ref.current = node;\n }\n };\n}\n"],"names":[],"mappings":"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"}
|