@mitcsutt/kiln-ui 0.5.0 → 0.7.0
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/components/actions/CopyButton/CopyButton.d.ts +58 -0
- package/dist/components/actions/CopyButton/CopyButton.js +63 -0
- package/dist/components/actions/CopyButton/CopyButton.js.map +1 -0
- package/dist/components/actions/CopyButton/CopyButton.module.js +9 -0
- package/dist/components/actions/CopyButton/CopyButton.module.js.map +1 -0
- package/dist/components/actions/CopyButton/index.d.ts +4 -0
- package/dist/components/actions/CopyButton/useClipboard.d.ts +35 -0
- package/dist/components/actions/CopyButton/useClipboard.js +62 -0
- package/dist/components/actions/CopyButton/useClipboard.js.map +1 -0
- package/dist/components/actions/ToggleChip/ToggleChip.d.ts +6 -0
- package/dist/components/actions/ToggleChip/ToggleChip.js +26 -24
- package/dist/components/actions/ToggleChip/ToggleChip.js.map +1 -1
- package/dist/components/display/Badge/Badge.d.ts +6 -0
- package/dist/components/display/Badge/Badge.js +16 -15
- package/dist/components/display/Badge/Badge.js.map +1 -1
- package/dist/components/display/CodeBlock/CodeBlock.d.ts +44 -3
- package/dist/components/display/CodeBlock/CodeBlock.js +39 -47
- package/dist/components/display/CodeBlock/CodeBlock.js.map +1 -1
- package/dist/components/display/CodeBlock/CodeBlock.module.js +4 -3
- package/dist/components/display/CodeBlock/CodeBlock.module.js.map +1 -1
- package/dist/components/display/CodeBlock/index.d.ts +1 -1
- package/dist/components/display/Swatch/Swatch.d.ts +52 -0
- package/dist/components/display/Swatch/Swatch.js +27 -0
- package/dist/components/display/Swatch/Swatch.js.map +1 -0
- package/dist/components/display/Swatch/Swatch.module.js +6 -0
- package/dist/components/display/Swatch/Swatch.module.js.map +1 -0
- package/dist/components/display/Swatch/index.d.ts +2 -0
- package/dist/components/display/Tag/Tag.d.ts +7 -0
- package/dist/components/display/Tag/Tag.js +31 -26
- package/dist/components/display/Tag/Tag.js.map +1 -1
- package/dist/components/feedback/Indicator/Indicator.d.ts +74 -0
- package/dist/components/feedback/Indicator/Indicator.js +50 -0
- package/dist/components/feedback/Indicator/Indicator.js.map +1 -0
- package/dist/components/feedback/Indicator/Indicator.module.js +12 -0
- package/dist/components/feedback/Indicator/Indicator.module.js.map +1 -0
- package/dist/components/feedback/Indicator/index.d.ts +2 -0
- package/dist/components/feedback/Toaster/Toaster.d.ts +71 -0
- package/dist/components/feedback/Toaster/Toaster.js +121 -0
- package/dist/components/feedback/Toaster/Toaster.js.map +1 -0
- package/dist/components/feedback/Toaster/Toaster.module.js +20 -0
- package/dist/components/feedback/Toaster/Toaster.module.js.map +1 -0
- package/dist/components/feedback/Toaster/index.d.ts +4 -0
- package/dist/components/feedback/Toaster/toast.d.ts +96 -0
- package/dist/components/feedback/Toaster/toast.js +131 -0
- package/dist/components/feedback/Toaster/toast.js.map +1 -0
- package/dist/components/inputs/Calendar/Calendar.d.ts +87 -0
- package/dist/components/inputs/Calendar/Calendar.js +217 -0
- package/dist/components/inputs/Calendar/Calendar.js.map +1 -0
- package/dist/components/inputs/Calendar/Calendar.module.js +18 -0
- package/dist/components/inputs/Calendar/Calendar.module.js.map +1 -0
- package/dist/components/inputs/Calendar/index.d.ts +2 -0
- package/dist/components/inputs/Combobox/Combobox.js +36 -44
- package/dist/components/inputs/Combobox/Combobox.js.map +1 -1
- package/dist/components/inputs/DateField/DateField.d.ts +17 -0
- package/dist/components/inputs/DateField/DateField.js +26 -0
- package/dist/components/inputs/DateField/DateField.js.map +1 -0
- package/dist/components/inputs/DateField/index.d.ts +2 -0
- package/dist/components/inputs/DateInput/DateInput.d.ts +75 -0
- package/dist/components/inputs/DateInput/DateInput.js +139 -0
- package/dist/components/inputs/DateInput/DateInput.js.map +1 -0
- package/dist/components/inputs/DateInput/index.d.ts +3 -0
- package/dist/components/inputs/DateRangeField/DateRangeField.d.ts +18 -3
- package/dist/components/inputs/DateRangeField/DateRangeField.js.map +1 -1
- package/dist/components/inputs/DateRangeField/DateRangeField.module.js +8 -3
- package/dist/components/inputs/DateRangeField/DateRangeField.module.js.map +1 -1
- package/dist/components/inputs/DateRangeField/DateRangePickerField.d.ts +48 -0
- package/dist/components/inputs/DateRangeField/DateRangePickerField.js +246 -0
- package/dist/components/inputs/DateRangeField/DateRangePickerField.js.map +1 -0
- package/dist/components/inputs/DateRangeField/index.d.ts +2 -0
- package/dist/components/inputs/DateTimeField/DateTimeField.d.ts +17 -0
- package/dist/components/inputs/DateTimeField/DateTimeField.js +26 -0
- package/dist/components/inputs/DateTimeField/DateTimeField.js.map +1 -0
- package/dist/components/inputs/DateTimeField/index.d.ts +2 -0
- package/dist/components/inputs/DateTimeInput/DateTimeInput.d.ts +83 -0
- package/dist/components/inputs/DateTimeInput/DateTimeInput.js +216 -0
- package/dist/components/inputs/DateTimeInput/DateTimeInput.js.map +1 -0
- package/dist/components/inputs/DateTimeInput/index.d.ts +2 -0
- package/dist/components/inputs/JsonField/JsonField.d.ts +23 -0
- package/dist/components/inputs/JsonField/JsonField.js +33 -0
- package/dist/components/inputs/JsonField/JsonField.js.map +1 -0
- package/dist/components/inputs/JsonField/index.d.ts +2 -0
- package/dist/components/inputs/JsonInput/JsonInput.d.ts +119 -0
- package/dist/components/inputs/JsonInput/JsonInput.js +282 -0
- package/dist/components/inputs/JsonInput/JsonInput.js.map +1 -0
- package/dist/components/inputs/JsonInput/JsonInput.module.js +16 -0
- package/dist/components/inputs/JsonInput/JsonInput.module.js.map +1 -0
- package/dist/components/inputs/JsonInput/index.d.ts +4 -0
- package/dist/components/inputs/JsonInput/json.d.ts +59 -0
- package/dist/components/inputs/JsonInput/json.js +231 -0
- package/dist/components/inputs/JsonInput/json.js.map +1 -0
- package/dist/components/inputs/TagsInput/TagsInput.js +5 -5
- package/dist/components/inputs/TimeField/TimeField.d.ts +17 -0
- package/dist/components/inputs/TimeField/TimeField.js +26 -0
- package/dist/components/inputs/TimeField/TimeField.js.map +1 -0
- package/dist/components/inputs/TimeField/index.d.ts +2 -0
- package/dist/components/inputs/TimeInput/TimeInput.d.ts +54 -0
- package/dist/components/inputs/TimeInput/TimeInput.js +107 -0
- package/dist/components/inputs/TimeInput/TimeInput.js.map +1 -0
- package/dist/components/inputs/TimeInput/index.d.ts +2 -0
- package/dist/components/inputs/internal/DateSegments.d.ts +80 -0
- package/dist/components/inputs/internal/DateSegments.js +345 -0
- package/dist/components/inputs/internal/DateSegments.js.map +1 -0
- package/dist/components/inputs/internal/dates.d.ts +101 -0
- package/dist/components/inputs/internal/dates.js +262 -0
- package/dist/components/inputs/internal/dates.js.map +1 -0
- package/dist/components/inputs/internal/filterOptions.d.ts +22 -1
- package/dist/components/inputs/internal/filterOptions.js +32 -1
- package/dist/components/inputs/internal/filterOptions.js.map +1 -1
- package/dist/components/inputs/internal/picker.d.ts +31 -0
- package/dist/components/inputs/internal/picker.js +29 -0
- package/dist/components/inputs/internal/picker.js.map +1 -0
- package/dist/components/inputs/internal/picker.module.js +15 -0
- package/dist/components/inputs/internal/picker.module.js.map +1 -0
- package/dist/components/inputs/internal/segments.module.js +10 -0
- package/dist/components/inputs/internal/segments.module.js.map +1 -0
- package/dist/components/layout/Collapse/Collapse.d.ts +39 -0
- package/dist/components/layout/Collapse/Collapse.js +24 -0
- package/dist/components/layout/Collapse/Collapse.js.map +1 -0
- package/dist/components/layout/Collapse/Collapse.module.js +6 -0
- package/dist/components/layout/Collapse/Collapse.module.js.map +1 -0
- package/dist/components/layout/Collapse/index.d.ts +2 -0
- package/dist/components/layout/Collapse/useHeightMotion.d.ts +16 -0
- package/dist/components/layout/Collapse/useHeightMotion.js +60 -0
- package/dist/components/layout/Collapse/useHeightMotion.js.map +1 -0
- package/dist/components/layout/ShowMore/ShowMore.d.ts +59 -0
- package/dist/components/layout/ShowMore/ShowMore.js +114 -0
- package/dist/components/layout/ShowMore/ShowMore.js.map +1 -0
- package/dist/components/layout/ShowMore/ShowMore.module.js +12 -0
- package/dist/components/layout/ShowMore/ShowMore.module.js.map +1 -0
- package/dist/components/layout/ShowMore/index.d.ts +2 -0
- package/dist/components/layout/Transition/Transition.d.ts +63 -0
- package/dist/components/layout/Transition/Transition.js +63 -0
- package/dist/components/layout/Transition/Transition.js.map +1 -0
- package/dist/components/layout/Transition/Transition.module.js +17 -0
- package/dist/components/layout/Transition/Transition.module.js.map +1 -0
- package/dist/components/layout/Transition/index.d.ts +2 -0
- package/dist/components/navigation/Collapsible/Collapsible.d.ts +49 -0
- package/dist/components/navigation/Collapsible/Collapsible.js +83 -0
- package/dist/components/navigation/Collapsible/Collapsible.js.map +1 -0
- package/dist/components/navigation/Collapsible/Collapsible.module.js +11 -0
- package/dist/components/navigation/Collapsible/Collapsible.module.js.map +1 -0
- package/dist/components/navigation/Collapsible/index.d.ts +2 -0
- package/dist/components/navigation/NavLinks/NavLinks.d.ts +32 -4
- package/dist/components/navigation/NavLinks/NavLinks.js +69 -39
- package/dist/components/navigation/NavLinks/NavLinks.js.map +1 -1
- package/dist/components/navigation/NavLinks/NavLinks.module.js +13 -3
- package/dist/components/navigation/NavLinks/NavLinks.module.js.map +1 -1
- package/dist/components/navigation/NavLinks/NavLinksGroup.d.ts +49 -0
- package/dist/components/navigation/NavLinks/NavLinksGroup.js +185 -0
- package/dist/components/navigation/NavLinks/NavLinksGroup.js.map +1 -0
- package/dist/components/navigation/NavLinks/context.d.ts +28 -0
- package/dist/components/navigation/NavLinks/context.js +13 -0
- package/dist/components/navigation/NavLinks/context.js.map +1 -0
- package/dist/components/navigation/NavLinks/index.d.ts +2 -0
- package/dist/components/navigation/NavLinks/treeKeys.d.ts +8 -0
- package/dist/components/navigation/NavLinks/treeKeys.js +57 -0
- package/dist/components/navigation/NavLinks/treeKeys.js.map +1 -0
- package/dist/components/overlays/CommandPalette/CommandPalette.d.ts +120 -0
- package/dist/components/overlays/CommandPalette/CommandPalette.js +386 -0
- package/dist/components/overlays/CommandPalette/CommandPalette.js.map +1 -0
- package/dist/components/overlays/CommandPalette/CommandPalette.module.js +31 -0
- package/dist/components/overlays/CommandPalette/CommandPalette.module.js.map +1 -0
- package/dist/components/overlays/CommandPalette/index.d.ts +2 -0
- package/dist/components/overlays/CommandPalette/shortcut.d.ts +28 -0
- package/dist/components/overlays/CommandPalette/shortcut.js +104 -0
- package/dist/components/overlays/CommandPalette/shortcut.js.map +1 -0
- package/dist/highlight/index.d.ts +18 -0
- package/dist/highlight/index.js +103 -0
- package/dist/highlight/index.js.map +1 -0
- package/dist/index.d.ts +41 -5
- package/dist/index.js +123 -100
- package/dist/styles.css +1 -1
- package/dist/themes/fiesta.css +1 -1
- package/dist/themes/flightdeck.css +1 -1
- package/dist/themes/ledger.css +1 -1
- package/dist/themes/monograph.css +1 -1
- package/dist/themes/riso.css +1 -1
- package/dist/utils/newlines.d.ts +2 -0
- package/dist/utils/newlines.js +10 -0
- package/dist/utils/newlines.js.map +1 -0
- package/package.json +13 -2
- package/skills/custom-theme/SKILL.md +32 -0
- package/skills/custom-theme/references/colour.md +20 -0
- package/skills/custom-theme/references/motion.md +9 -0
- package/skills/custom-theme/references/tokens.md +35 -0
- package/skills/design-rules/SKILL.md +5 -2
- package/skills/setup-and-theming/SKILL.md +2 -1
- package/skills/setup-and-theming/references/paper.md +1 -1
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { useLatest as e } from "../../inputs/internal/useLatest.js";
|
|
2
|
+
import { useEffect as t, useLayoutEffect as n, useRef as r } from "react";
|
|
3
|
+
//#region src/components/layout/Collapse/useHeightMotion.ts
|
|
4
|
+
var i = typeof document > "u" ? t : n;
|
|
5
|
+
function a(e) {
|
|
6
|
+
let t = getComputedStyle(e), n = t.transitionDuration.split(","), r = t.transitionDelay.split(","), i = 0;
|
|
7
|
+
return n.forEach((e, t) => {
|
|
8
|
+
let n = parseFloat(e) + parseFloat(r[t] ?? r[0] ?? "0");
|
|
9
|
+
Number.isFinite(n) && (i = Math.max(i, n));
|
|
10
|
+
}), i * 1e3;
|
|
11
|
+
}
|
|
12
|
+
function o(n, o, s) {
|
|
13
|
+
let c = e(s), l = r(null), u = r(!0);
|
|
14
|
+
t(() => {
|
|
15
|
+
let e = n.current;
|
|
16
|
+
if (!e || typeof ResizeObserver > "u") return;
|
|
17
|
+
let t = new ResizeObserver(() => {
|
|
18
|
+
l.current = e.getBoundingClientRect().height;
|
|
19
|
+
});
|
|
20
|
+
return t.observe(e), () => {
|
|
21
|
+
t.disconnect();
|
|
22
|
+
};
|
|
23
|
+
}, [n]), i(() => {
|
|
24
|
+
let e = n.current;
|
|
25
|
+
if (u.current) {
|
|
26
|
+
u.current = !1, e && (l.current = e.getBoundingClientRect().height);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (!e) {
|
|
30
|
+
c.current?.(o);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
let t = e.hasAttribute("data-animating") ? e.getBoundingClientRect().height : l.current ?? e.getBoundingClientRect().height;
|
|
34
|
+
e.style.blockSize = "", e.removeAttribute("data-animating");
|
|
35
|
+
let r = e.getBoundingClientRect().height, i = () => {
|
|
36
|
+
e.style.blockSize = "", e.removeAttribute("data-animating"), l.current = e.getBoundingClientRect().height, c.current?.(o);
|
|
37
|
+
};
|
|
38
|
+
e.setAttribute("data-animating", "");
|
|
39
|
+
let s = a(e);
|
|
40
|
+
if (Math.abs(t - r) < .5 || s === 0) {
|
|
41
|
+
i();
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
e.removeAttribute("data-animating"), e.style.blockSize = `${String(t)}px`, e.getBoundingClientRect(), e.setAttribute("data-animating", ""), e.style.blockSize = `${String(r)}px`;
|
|
45
|
+
let d = !1, f = () => {
|
|
46
|
+
d || (d = !0, e.removeEventListener("transitionend", p), window.clearTimeout(m), i());
|
|
47
|
+
}, p = (t) => {
|
|
48
|
+
t.target === e && t.propertyName === "block-size" && f();
|
|
49
|
+
};
|
|
50
|
+
e.addEventListener("transitionend", p);
|
|
51
|
+
let m = window.setTimeout(f, s + 50);
|
|
52
|
+
return () => {
|
|
53
|
+
d = !0, e.removeEventListener("transitionend", p), window.clearTimeout(m);
|
|
54
|
+
};
|
|
55
|
+
}, [o]);
|
|
56
|
+
}
|
|
57
|
+
//#endregion
|
|
58
|
+
export { o as useHeightMotion };
|
|
59
|
+
|
|
60
|
+
//# sourceMappingURL=useHeightMotion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useHeightMotion.js","names":["useIsomorphicLayoutEffect","useEffect","useLayoutEffect","transitionMs","style","element","durations","delays","longest","total","duration","index","useHeightMotion","settled","useLatest","onSettled","before","useRef","first","ref","observer","key","from","to","finish","done","end","onEnd","timer","event"],"sources":["../../../../src/components/layout/Collapse/useHeightMotion.ts"],"sourcesContent":["import { useEffect, useLayoutEffect, useRef, type RefObject } from 'react'\nimport { useLatest } from '#components/inputs/internal/useLatest'\n\n// Layout effects warn during server rendering; on the server nothing moves anyway.\nconst useIsomorphicLayoutEffect = typeof document === 'undefined' ? useEffect : useLayoutEffect\n\n/** The longest of an element's computed transition durations, in milliseconds. */\nfunction transitionMs(element: HTMLElement): number {\n const style = getComputedStyle(element)\n const durations = style.transitionDuration.split(',')\n const delays = style.transitionDelay.split(',')\n let longest = 0\n durations.forEach((duration, index) => {\n const total = parseFloat(duration) + parseFloat(delays[index] ?? delays[0] ?? '0')\n if (Number.isFinite(total)) longest = Math.max(longest, total)\n })\n return longest * 1000\n}\n\n/**\n * Animates an element's height whenever `key` changes, from the height it had before the change\n * to its natural height after it. Shared by `Collapse`, `ShowMore` and `NavLinksGroup`, so every\n * open and close in Kiln moves the same way.\n *\n * The element's CSS decides the natural height in each state (`block-size: 0` when closed, a\n * `max-block-size` clamp when collapsed) and the transition itself, through `data-animating`:\n * `.el[data-animating] { transition: block-size var(--dur-3) var(--ease-out) }`. While it's set,\n * the CSS should lift any clamp, since the inline `block-size` does the work. The theme's motion\n * tokens set the timing, and under reduced motion they collapse, so the change is instant.\n *\n * `onSettled` runs once the height reaches its new value (at once when nothing moves), with the\n * key it settled on. A change during a transition starts from wherever the height is.\n */\nexport function useHeightMotion<K>(\n ref: RefObject<HTMLElement | null>,\n key: K,\n onSettled?: (key: K) => void,\n): void {\n const settled = useLatest(onSettled)\n // The height before a change: layout effects run after the DOM changes, so it's kept current\n // here instead, by an observer and at the end of every transition.\n const before = useRef<number | null>(null)\n const first = useRef(true)\n\n useEffect(() => {\n const element = ref.current\n if (!element || typeof ResizeObserver === 'undefined') return\n const observer = new ResizeObserver(() => {\n before.current = element.getBoundingClientRect().height\n })\n observer.observe(element)\n return () => {\n observer.disconnect()\n }\n }, [ref])\n\n useIsomorphicLayoutEffect(() => {\n const element = ref.current\n if (first.current) {\n first.current = false\n if (element) before.current = element.getBoundingClientRect().height\n return\n }\n if (!element) {\n settled.current?.(key)\n return\n }\n\n // Mid-transition, start from where it is now; otherwise from the last height we saw.\n const animating = element.hasAttribute('data-animating')\n const from = animating\n ? element.getBoundingClientRect().height\n : (before.current ?? element.getBoundingClientRect().height)\n element.style.blockSize = ''\n element.removeAttribute('data-animating')\n const to = element.getBoundingClientRect().height\n\n const finish = () => {\n element.style.blockSize = ''\n element.removeAttribute('data-animating')\n before.current = element.getBoundingClientRect().height\n settled.current?.(key)\n }\n\n element.setAttribute('data-animating', '')\n const duration = transitionMs(element)\n if (Math.abs(from - to) < 0.5 || duration === 0) {\n finish()\n return\n }\n\n // Commit the start height with the transition off, so it jumps there rather than animating\n // from the new state's height (a close would otherwise run 0 → 0 and snap shut).\n element.removeAttribute('data-animating')\n element.style.blockSize = `${String(from)}px`\n // Read layout so the browser commits the start height before the transition target.\n element.getBoundingClientRect()\n element.setAttribute('data-animating', '')\n element.style.blockSize = `${String(to)}px`\n\n let done = false\n const end = () => {\n if (done) return\n done = true\n element.removeEventListener('transitionend', onEnd)\n window.clearTimeout(timer)\n finish()\n }\n const onEnd = (event: TransitionEvent) => {\n if (event.target === element && event.propertyName === 'block-size') end()\n }\n element.addEventListener('transitionend', onEnd)\n // In case `transitionend` never comes (the element was hidden, or the tab is in the background).\n const timer = window.setTimeout(end, duration + 50)\n return () => {\n done = true\n element.removeEventListener('transitionend', onEnd)\n window.clearTimeout(timer)\n }\n }, [key])\n}\n"],"mappings":";;;AAIA,IAAMA,IAA4B,OAAO,WAAa,MAAcC,IAAYC;AAGhF,SAASC,EAAa,GAA8B;CAClD,IAAMC,IAAQ,iBAAiBC,CAAO,GAChCC,IAAYF,EAAM,mBAAmB,MAAM,GAAG,GAC9CG,IAASH,EAAM,gBAAgB,MAAM,GAAG,GAC1CI,IAAU;CAKd,OAJAF,EAAU,SAAS,GAAU,MAAU;EACrC,IAAMG,IAAQ,WAAWC,CAAQ,IAAI,WAAWH,EAAOI,MAAUJ,EAAO,MAAM,GAAG;EACjF,AAAI,OAAO,SAASE,CAAK,MAAG,IAAU,KAAK,IAAID,GAASC,CAAK;CAC/D,CAAC,GACMD,IAAU;AACnB;AAgBA,SAAgBI,EACd,GACA,GACA,GACM;CACN,IAAMC,IAAUC,EAAUC,CAAS,GAG7BC,IAASC,EAAsB,IAAI,GACnCC,IAAQD,EAAO,EAAI;CAczB,AAZAhB,QAAgB;EACd,IAAMI,IAAUc,EAAI;EACpB,IAAI,CAACd,KAAW,OAAO,iBAAmB,KAAa;EACvD,IAAMe,IAAW,IAAI,qBAAqB;GACxC,EAAO,UAAUf,EAAQ,sBAAsB,CAAC,CAAC;EACnD,CAAC;EAED,OADAe,EAAS,QAAQf,CAAO,SACX;GACX,EAAS,WAAW;EACtB;CACF,GAAG,CAACc,CAAG,CAAC,GAERnB,QAAgC;EAC9B,IAAMK,IAAUc,EAAI;EACpB,IAAID,EAAM,SAAS;GAEjB,AADA,EAAM,UAAU,IACZb,MAAS,EAAO,UAAUA,EAAQ,sBAAsB,CAAC,CAAC;GAC9D;EACF;EACA,IAAI,CAACA,GAAS;GACZ,EAAQ,UAAUgB,CAAG;GACrB;EACF;EAIA,IAAMC,IADYjB,EAAQ,aAAa,gBAC1B,IACTA,EAAQ,sBAAsB,CAAC,CAAC,SAC/BW,EAAO,WAAWX,EAAQ,sBAAsB,CAAC,CAAC;EAEvD,AADA,EAAQ,MAAM,YAAY,IAC1BA,EAAQ,gBAAgB,gBAAgB;EACxC,IAAMkB,IAAKlB,EAAQ,sBAAsB,CAAC,CAAC,QAErCmB,UAAe;GAInB,AAHA,EAAQ,MAAM,YAAY,IAC1BnB,EAAQ,gBAAgB,gBAAgB,GACxC,EAAO,UAAUA,EAAQ,sBAAsB,CAAC,CAAC,QACjDQ,EAAQ,UAAUQ,CAAG;EACvB;EAEA,EAAQ,aAAa,kBAAkB,EAAE;EACzC,IAAMX,IAAWP,EAAaE,CAAO;EACrC,IAAI,KAAK,IAAIiB,IAAOC,CAAE,IAAI,MAAOb,MAAa,GAAG;GAC/C,EAAO;GACP;EACF;EASA,AALAL,EAAQ,gBAAgB,gBAAgB,GACxC,EAAQ,MAAM,YAAY,GAAG,OAAOiB,CAAI,EAAE,KAE1CjB,EAAQ,sBAAsB,GAC9BA,EAAQ,aAAa,kBAAkB,EAAE,GACzC,EAAQ,MAAM,YAAY,GAAG,OAAOkB,CAAE,EAAE;EAExC,IAAIE,IAAO,IACLC,UAAY;GAChB,AAAID,MACJ,IAAO,IACPpB,EAAQ,oBAAoB,iBAAiBsB,CAAK,GAClD,OAAO,aAAaC,CAAK,GACzBJ,EAAO;EACT,GACMG,KAAS,MAA2B;GACxC,AAAIE,EAAM,WAAWxB,KAAWwB,EAAM,iBAAiB,gBAAcH,EAAI;EAC3E;EACA,EAAQ,iBAAiB,iBAAiBC,CAAK;EAE/C,IAAMC,IAAQ,OAAO,WAAWF,GAAKhB,IAAW,EAAE;EAClD,aAAa;GAGX,AAFA,IAAO,IACPL,EAAQ,oBAAoB,iBAAiBsB,CAAK,GAClD,OAAO,aAAaC,CAAK;EAC3B;CACF,GAAG,CAACP,CAAG,CAAC;AACV"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type HTMLAttributes, type ReactNode } from 'react';
|
|
2
|
+
import { type Space } from '../../../utils/tokens.js';
|
|
3
|
+
export type ShowMoreMode = 'lines' | 'height' | 'items';
|
|
4
|
+
export interface ShowMoreProps extends HTMLAttributes<HTMLDivElement> {
|
|
5
|
+
/** Clamp to this many lines of the content's text. For a description, a comment, a bio. */
|
|
6
|
+
lines?: number;
|
|
7
|
+
/**
|
|
8
|
+
* Clamp to this height, as a step on the space scale (`10` is `--space-10`). For anything that
|
|
9
|
+
* isn't lines of text: a log excerpt, a block of prose with headings, a code sample.
|
|
10
|
+
*/
|
|
11
|
+
maxHeight?: Space;
|
|
12
|
+
/**
|
|
13
|
+
* Show only the first this-many items of the list it wraps (the children of its one child), and
|
|
14
|
+
* say how many more there are. For tags, avatars, rows.
|
|
15
|
+
*/
|
|
16
|
+
maxItems?: number;
|
|
17
|
+
/** Controlled state. */
|
|
18
|
+
expanded?: boolean;
|
|
19
|
+
/** Uncontrolled initial state. Default `false`. */
|
|
20
|
+
defaultExpanded?: boolean;
|
|
21
|
+
/** Runs when the toggle asks to expand or collapse. */
|
|
22
|
+
onExpandedChange?: (expanded: boolean) => void;
|
|
23
|
+
/**
|
|
24
|
+
* The toggle's label while clamped. A function receives how many items are hidden (with
|
|
25
|
+
* `maxItems`; otherwise `0`). Default "Show more", or "Show 12 more" with `maxItems`.
|
|
26
|
+
*/
|
|
27
|
+
moreLabel?: ReactNode | ((hidden: number) => ReactNode);
|
|
28
|
+
/** The toggle's label while expanded. Default "Show less". */
|
|
29
|
+
lessLabel?: ReactNode;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Clamps any content to a few lines, a height or a number of items, with a toggle to show the
|
|
33
|
+
* rest.
|
|
34
|
+
*
|
|
35
|
+
* @remarks
|
|
36
|
+
* `ShowMore` wraps the content it clamps, whatever it is: a `Text`, a `Prose` block, a list of
|
|
37
|
+
* tags, a table, a code sample. None of those components takes a clamp prop of its own; you wrap
|
|
38
|
+
* them in `ShowMore` instead, so the toggle looks and behaves the same everywhere.
|
|
39
|
+
*
|
|
40
|
+
* - `lines` clamps to that many lines of the content's text.
|
|
41
|
+
* - `maxHeight` clamps to a height on the space scale.
|
|
42
|
+
* - `maxItems` shows the first few items of the list it wraps and says how many more there are.
|
|
43
|
+
*
|
|
44
|
+
* The toggle only appears when the content is actually longer than the clamp, so short content
|
|
45
|
+
* renders as it is. The cut fades out (with lines and a height) on any surface, and opening or
|
|
46
|
+
* closing animates with the same motion as {@link Collapse | Collapse}.
|
|
47
|
+
*
|
|
48
|
+
* The toggle is a button with `aria-expanded`. Clamped text stays readable to screen readers, and
|
|
49
|
+
* when keyboard focus lands on a link below the cut, the content opens so the focus is visible.
|
|
50
|
+
* Hidden items (with `maxItems`) are out of the page until the toggle shows them.
|
|
51
|
+
*
|
|
52
|
+
* @privateRemarks
|
|
53
|
+
* `maxItems` hides the wrapped list's children past N with one generated rule in a `<style>`
|
|
54
|
+
* scoped by `data-show-more`, so the server render is already clamped. `lines` measures the
|
|
55
|
+
* content's line height on the client; the server falls back to `1lh`.
|
|
56
|
+
*
|
|
57
|
+
* <ShowMore lines={3}><Text>{bio}</Text></ShowMore>
|
|
58
|
+
*/
|
|
59
|
+
export declare const ShowMore: import("react").ForwardRefExoticComponent<ShowMoreProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { cx as e } from "../../../utils/cx.js";
|
|
2
|
+
import { ChevronDownIcon as t } from "../../../icons/icons.js";
|
|
3
|
+
import { mergeStyles as n } from "../../../utils/responsive.js";
|
|
4
|
+
import { space as r } from "../../../utils/tokens.js";
|
|
5
|
+
import { useMergedRefs as i } from "../../inputs/internal/refs.js";
|
|
6
|
+
import { useLatest as a } from "../../inputs/internal/useLatest.js";
|
|
7
|
+
import { useHeightMotion as o } from "../Collapse/useHeightMotion.js";
|
|
8
|
+
import s from "./ShowMore.module.js";
|
|
9
|
+
import { Children as c, forwardRef as l, isValidElement as u, useCallback as d, useEffect as f, useId as p, useLayoutEffect as m, useRef as h, useState as g } from "react";
|
|
10
|
+
import { jsx as _, jsxs as v } from "react/jsx-runtime";
|
|
11
|
+
//#region src/components/layout/ShowMore/ShowMore.tsx
|
|
12
|
+
var y = typeof document > "u" ? f : m;
|
|
13
|
+
function ee({ lines: e, maxHeight: t, maxItems: n }) {
|
|
14
|
+
return n === void 0 ? e === void 0 ? t === void 0 ? "lines" : "height" : "lines" : "items";
|
|
15
|
+
}
|
|
16
|
+
function b(e) {
|
|
17
|
+
let t = c.toArray(e)[0];
|
|
18
|
+
return u(t) ? c.count(t.props.children) : 0;
|
|
19
|
+
}
|
|
20
|
+
function x(e) {
|
|
21
|
+
let t = getComputedStyle(e), n = parseFloat(t.lineHeight);
|
|
22
|
+
if (Number.isFinite(n)) return n;
|
|
23
|
+
let r = parseFloat(t.fontSize);
|
|
24
|
+
return Number.isFinite(r) ? r * 1.2 : void 0;
|
|
25
|
+
}
|
|
26
|
+
var S = l(function(c, l) {
|
|
27
|
+
let { lines: u, maxHeight: m, maxItems: S, expanded: C, defaultExpanded: w = !1, onExpandedChange: T, moreLabel: E, lessLabel: D = "Show less", className: O, style: k, children: A, ...j } = c, M = ee(c), N = p(), P = `${N}-content`, F = h(null), I = h(null), L = h(null), [R, z] = g(w), B = C ?? R, V = a(T), H = d((e) => {
|
|
28
|
+
C === void 0 && z(e), V.current?.(e);
|
|
29
|
+
}, [C, V]), [U, W] = g(() => b(A)), [G, K] = g(!1), [q, J] = g(), Y = M === "items" ? U > (S ?? 0) : G, X = d(() => {
|
|
30
|
+
let e = I.current;
|
|
31
|
+
if (!e) return;
|
|
32
|
+
let t = e.firstElementChild;
|
|
33
|
+
if (M === "items") {
|
|
34
|
+
W(t ? t.children.length : 0);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (M === "lines" && t) {
|
|
38
|
+
let e = x(t);
|
|
39
|
+
e !== void 0 && J((t) => t === e ? t : e);
|
|
40
|
+
}
|
|
41
|
+
let n = L.current?.getBoundingClientRect().height ?? 0;
|
|
42
|
+
K(n > 0 && e.scrollHeight > n + 1);
|
|
43
|
+
}, [M]);
|
|
44
|
+
y(X), f(() => {
|
|
45
|
+
let e = I.current;
|
|
46
|
+
if (!e) return;
|
|
47
|
+
let t = [];
|
|
48
|
+
if (typeof ResizeObserver < "u") {
|
|
49
|
+
let n = new ResizeObserver(X);
|
|
50
|
+
n.observe(e), e.firstElementChild && n.observe(e.firstElementChild), t.push(n);
|
|
51
|
+
}
|
|
52
|
+
if (typeof MutationObserver < "u") {
|
|
53
|
+
let n = new MutationObserver(X);
|
|
54
|
+
n.observe(e, {
|
|
55
|
+
childList: !0,
|
|
56
|
+
subtree: M === "items"
|
|
57
|
+
}), t.push(n);
|
|
58
|
+
}
|
|
59
|
+
return () => {
|
|
60
|
+
for (let e of t) e.disconnect();
|
|
61
|
+
};
|
|
62
|
+
}, [X, M]);
|
|
63
|
+
let Z = h(!1);
|
|
64
|
+
o(I, B, (e) => {
|
|
65
|
+
if (e || !Z.current) return;
|
|
66
|
+
Z.current = !1;
|
|
67
|
+
let t = F.current;
|
|
68
|
+
t && t.getBoundingClientRect().top < 0 && t.scrollIntoView({ block: "start" });
|
|
69
|
+
});
|
|
70
|
+
let Q = M === "height" ? r(m ?? 0) : M === "lines" ? q === void 0 ? `calc(${String(u ?? 3)} * 1lh)` : `${String((u ?? 3) * q)}px` : "none", $ = M === "items" ? Math.max(0, U - (S ?? 0)) : 0, te = typeof E == "function" ? E($) : E ?? (M === "items" ? `Show ${String($)} more` : "Show more"), ne = (e) => {
|
|
71
|
+
if (B || !Y || M === "items") return;
|
|
72
|
+
let t = e.currentTarget.getBoundingClientRect();
|
|
73
|
+
e.target.getBoundingClientRect().bottom > t.bottom + 1 && H(!0);
|
|
74
|
+
};
|
|
75
|
+
return /* @__PURE__ */ v("div", {
|
|
76
|
+
ref: i(l, F),
|
|
77
|
+
className: e(s.showMore, O),
|
|
78
|
+
"data-mode": M,
|
|
79
|
+
"data-state": B ? "expanded" : "collapsed",
|
|
80
|
+
"data-overflowing": Y || void 0,
|
|
81
|
+
style: n({ "--_clamp": Q }, k),
|
|
82
|
+
...j,
|
|
83
|
+
children: [
|
|
84
|
+
/* @__PURE__ */ _("span", {
|
|
85
|
+
ref: L,
|
|
86
|
+
className: s.probe,
|
|
87
|
+
"aria-hidden": "true"
|
|
88
|
+
}),
|
|
89
|
+
M === "items" && !B ? /* @__PURE__ */ _("style", { children: `[data-show-more="${N}"]>*>:nth-child(n+${String((S ?? 0) + 1)}){display:none}` }) : null,
|
|
90
|
+
/* @__PURE__ */ _("div", {
|
|
91
|
+
ref: I,
|
|
92
|
+
id: P,
|
|
93
|
+
className: s.clip,
|
|
94
|
+
"data-show-more": M === "items" ? N : void 0,
|
|
95
|
+
onFocus: ne,
|
|
96
|
+
children: A
|
|
97
|
+
}),
|
|
98
|
+
Y ? /* @__PURE__ */ v("button", {
|
|
99
|
+
type: "button",
|
|
100
|
+
className: s.toggle,
|
|
101
|
+
"aria-expanded": B,
|
|
102
|
+
"aria-controls": P,
|
|
103
|
+
onClick: () => {
|
|
104
|
+
Z.current = B, H(!B);
|
|
105
|
+
},
|
|
106
|
+
children: [B ? D : te, /* @__PURE__ */ _(t, { className: s.chevron })]
|
|
107
|
+
}) : null
|
|
108
|
+
]
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
//#endregion
|
|
112
|
+
export { S as ShowMore };
|
|
113
|
+
|
|
114
|
+
//# sourceMappingURL=ShowMore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ShowMore.js","names":["styles","ShowMore_module_default","useIsomorphicLayoutEffect","useEffect","useLayoutEffect","modeOf","maxItems","lines","maxHeight","countItems","only","Children","children","isValidElement","lineHeight","style","element","value","size","ShowMore","forwardRef","expandedProp","rest","props","mode","id","useId","contentId","root","useRef","clip","probe","uncontrolled","setUncontrolled","useState","defaultExpanded","expanded","change","useLatest","onExpandedChange","setExpanded","useCallback","next","itemCount","setItemCount","tooLong","setTooLong","lineHeightPx","setLineHeightPx","overflowing","measure","content","first","height","prev","clamp","observers","resize","mutation","observer","collapsedByToggle","settled","node","space","hidden","more","moreLabel","onFocus","box","event","useMergedRefs","ref","cx","className","mergeStyles","lessLabel","ChevronDownIcon"],"sources":["../../../../src/components/layout/ShowMore/ShowMore.tsx"],"sourcesContent":["import {\n Children,\n forwardRef,\n isValidElement,\n useCallback,\n useEffect,\n useId,\n useLayoutEffect,\n useRef,\n useState,\n type CSSProperties,\n type FocusEvent,\n type HTMLAttributes,\n type ReactNode,\n} from 'react'\nimport { cx } from '#utils/cx'\nimport { mergeStyles } from '#utils/responsive'\nimport { space, type Space } from '#utils/tokens'\nimport { ChevronDownIcon } from '#icons'\nimport { useMergedRefs } from '#components/inputs/internal/refs'\nimport { useLatest } from '#components/inputs/internal/useLatest'\nimport { useHeightMotion } from '#components/layout/Collapse/useHeightMotion'\nimport styles from './ShowMore.module.css'\n\n// Layout effects warn during server rendering; on the server nothing is measured.\nconst useIsomorphicLayoutEffect = typeof document === 'undefined' ? useEffect : useLayoutEffect\n\nexport type ShowMoreMode = 'lines' | 'height' | 'items'\n\nexport interface ShowMoreProps extends HTMLAttributes<HTMLDivElement> {\n /** Clamp to this many lines of the content's text. For a description, a comment, a bio. */\n lines?: number\n /**\n * Clamp to this height, as a step on the space scale (`10` is `--space-10`). For anything that\n * isn't lines of text: a log excerpt, a block of prose with headings, a code sample.\n */\n maxHeight?: Space\n /**\n * Show only the first this-many items of the list it wraps (the children of its one child), and\n * say how many more there are. For tags, avatars, rows.\n */\n maxItems?: number\n /** Controlled state. */\n expanded?: boolean\n /** Uncontrolled initial state. Default `false`. */\n defaultExpanded?: boolean\n /** Runs when the toggle asks to expand or collapse. */\n onExpandedChange?: (expanded: boolean) => void\n /**\n * The toggle's label while clamped. A function receives how many items are hidden (with\n * `maxItems`; otherwise `0`). Default \"Show more\", or \"Show 12 more\" with `maxItems`.\n */\n moreLabel?: ReactNode | ((hidden: number) => ReactNode)\n /** The toggle's label while expanded. Default \"Show less\". */\n lessLabel?: ReactNode\n}\n\nfunction modeOf({ lines, maxHeight, maxItems }: ShowMoreProps): ShowMoreMode {\n if (maxItems !== undefined) return 'items'\n if (lines !== undefined) return 'lines'\n if (maxHeight !== undefined) return 'height'\n return 'lines'\n}\n\n/** On the server, a best guess at the item count: the children of the one wrapped element. */\nfunction countItems(children: ReactNode): number {\n const only = Children.toArray(children)[0]\n if (!isValidElement<{ children?: ReactNode }>(only)) return 0\n return Children.count(only.props.children)\n}\n\n/** The line height of the content's first element, in px. */\nfunction lineHeight(element: Element): number | undefined {\n const style = getComputedStyle(element)\n const value = parseFloat(style.lineHeight)\n if (Number.isFinite(value)) return value\n const size = parseFloat(style.fontSize)\n return Number.isFinite(size) ? size * 1.2 : undefined\n}\n\n/**\n * Clamps any content to a few lines, a height or a number of items, with a toggle to show the\n * rest.\n *\n * @remarks\n * `ShowMore` wraps the content it clamps, whatever it is: a `Text`, a `Prose` block, a list of\n * tags, a table, a code sample. None of those components takes a clamp prop of its own; you wrap\n * them in `ShowMore` instead, so the toggle looks and behaves the same everywhere.\n *\n * - `lines` clamps to that many lines of the content's text.\n * - `maxHeight` clamps to a height on the space scale.\n * - `maxItems` shows the first few items of the list it wraps and says how many more there are.\n *\n * The toggle only appears when the content is actually longer than the clamp, so short content\n * renders as it is. The cut fades out (with lines and a height) on any surface, and opening or\n * closing animates with the same motion as {@link Collapse | Collapse}.\n *\n * The toggle is a button with `aria-expanded`. Clamped text stays readable to screen readers, and\n * when keyboard focus lands on a link below the cut, the content opens so the focus is visible.\n * Hidden items (with `maxItems`) are out of the page until the toggle shows them.\n *\n * @privateRemarks\n * `maxItems` hides the wrapped list's children past N with one generated rule in a `<style>`\n * scoped by `data-show-more`, so the server render is already clamped. `lines` measures the\n * content's line height on the client; the server falls back to `1lh`.\n *\n * <ShowMore lines={3}><Text>{bio}</Text></ShowMore>\n */\nexport const ShowMore = forwardRef<HTMLDivElement, ShowMoreProps>(function ShowMore(props, ref) {\n const {\n lines,\n maxHeight,\n maxItems,\n expanded: expandedProp,\n defaultExpanded = false,\n onExpandedChange,\n moreLabel,\n lessLabel = 'Show less',\n className,\n style,\n children,\n ...rest\n } = props\n const mode = modeOf(props)\n const id = useId()\n const contentId = `${id}-content`\n const root = useRef<HTMLDivElement>(null)\n const clip = useRef<HTMLDivElement>(null)\n const probe = useRef<HTMLSpanElement>(null)\n\n const [uncontrolled, setUncontrolled] = useState(defaultExpanded)\n const expanded = expandedProp ?? uncontrolled\n const change = useLatest(onExpandedChange)\n const setExpanded = useCallback(\n (next: boolean) => {\n if (expandedProp === undefined) setUncontrolled(next)\n change.current?.(next)\n },\n [expandedProp, change],\n )\n\n const [itemCount, setItemCount] = useState(() => countItems(children))\n const [tooLong, setTooLong] = useState(false)\n const [lineHeightPx, setLineHeightPx] = useState<number>()\n const overflowing = mode === 'items' ? itemCount > (maxItems ?? 0) : tooLong\n\n const measure = useCallback(() => {\n const content = clip.current\n if (!content) return\n const first = content.firstElementChild\n if (mode === 'items') {\n setItemCount(first ? first.children.length : 0)\n return\n }\n if (mode === 'lines' && first) {\n const height = lineHeight(first)\n if (height !== undefined) setLineHeightPx((prev) => (prev === height ? prev : height))\n }\n const clamp = probe.current?.getBoundingClientRect().height ?? 0\n setTooLong(clamp > 0 && content.scrollHeight > clamp + 1)\n }, [mode])\n\n useIsomorphicLayoutEffect(measure)\n\n useEffect(() => {\n const content = clip.current\n if (!content) return\n const observers: { disconnect: () => void }[] = []\n if (typeof ResizeObserver !== 'undefined') {\n const resize = new ResizeObserver(measure)\n resize.observe(content)\n if (content.firstElementChild) resize.observe(content.firstElementChild)\n observers.push(resize)\n }\n if (typeof MutationObserver !== 'undefined') {\n const mutation = new MutationObserver(measure)\n mutation.observe(content, { childList: true, subtree: mode === 'items' })\n observers.push(mutation)\n }\n return () => {\n for (const observer of observers) observer.disconnect()\n }\n }, [measure, mode])\n\n // Collapsing a long block can leave the reader below it: bring the top back into view.\n const collapsedByToggle = useRef(false)\n useHeightMotion(clip, expanded, (settled) => {\n if (settled || !collapsedByToggle.current) return\n collapsedByToggle.current = false\n const node = root.current\n if (node && node.getBoundingClientRect().top < 0) node.scrollIntoView({ block: 'start' })\n })\n\n const clamp =\n mode === 'height'\n ? space(maxHeight ?? 0)\n : mode === 'lines'\n ? lineHeightPx === undefined\n ? `calc(${String(lines ?? 3)} * 1lh)`\n : `${String((lines ?? 3) * lineHeightPx)}px`\n : 'none'\n\n const hidden = mode === 'items' ? Math.max(0, itemCount - (maxItems ?? 0)) : 0\n const more =\n typeof moreLabel === 'function'\n ? moreLabel(hidden)\n : (moreLabel ?? (mode === 'items' ? `Show ${String(hidden)} more` : 'Show more'))\n\n // Focus that lands below the cut opens the content, so the focused element is on screen.\n const onFocus = (event: FocusEvent<HTMLDivElement>) => {\n if (expanded || !overflowing || mode === 'items') return\n const box = event.currentTarget.getBoundingClientRect()\n const target = event.target.getBoundingClientRect()\n if (target.bottom > box.bottom + 1) setExpanded(true)\n }\n\n return (\n <div\n ref={useMergedRefs(ref, root)}\n className={cx(styles.showMore, className)}\n data-mode={mode}\n data-state={expanded ? 'expanded' : 'collapsed'}\n data-overflowing={overflowing || undefined}\n style={mergeStyles({ '--_clamp': clamp } as CSSProperties, style)}\n {...rest}\n >\n <span ref={probe} className={styles.probe} aria-hidden=\"true\" />\n {mode === 'items' && !expanded ? (\n <style>{`[data-show-more=\"${id}\"]>*>:nth-child(n+${String((maxItems ?? 0) + 1)}){display:none}`}</style>\n ) : null}\n <div\n ref={clip}\n id={contentId}\n className={styles.clip}\n data-show-more={mode === 'items' ? id : undefined}\n onFocus={onFocus}\n >\n {children}\n </div>\n {overflowing ? (\n <button\n type=\"button\"\n className={styles.toggle}\n aria-expanded={expanded}\n aria-controls={contentId}\n onClick={() => {\n collapsedByToggle.current = expanded\n setExpanded(!expanded)\n }}\n >\n {expanded ? lessLabel : more}\n <ChevronDownIcon className={styles.chevron} />\n </button>\n ) : null}\n </div>\n )\n})\n"],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//#region src/components/layout/ShowMore/ShowMore.module.css
|
|
2
|
+
var e = "kiln-ShowMore__showMore", t = "kiln-ShowMore__probe", n = "kiln-ShowMore__clip", r = "kiln-ShowMore__toggle", i = "kiln-ShowMore__chevron", a = {
|
|
3
|
+
showMore: e,
|
|
4
|
+
probe: t,
|
|
5
|
+
clip: n,
|
|
6
|
+
toggle: r,
|
|
7
|
+
chevron: i
|
|
8
|
+
};
|
|
9
|
+
//#endregion
|
|
10
|
+
export { i as chevron, n as clip, a as default, t as probe, e as showMore, r as toggle };
|
|
11
|
+
|
|
12
|
+
//# sourceMappingURL=ShowMore.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ShowMore.module.js","names":["showMore","probe","clip","toggle","chevron","ShowMore_module_default"],"sources":["../../../../src/components/layout/ShowMore/ShowMore.module.css"],"sourcesContent":["/*\n * Component tokens:\n * --show-more-fade how tall the fade at the cut is, default var(--space-7)\n *\n * The fade is a mask, not a colour laid over the content, so it works on the canvas, a surface,\n * a raised card or an inverted band without knowing which. While the height animates the clamp\n * is lifted (the inline block-size from useHeightMotion does the work) and the fade drops.\n */\n.showMore {\n --_clamp: none;\n\n position: relative;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n min-inline-size: 0;\n}\n\n.showMore[hidden] {\n display: none;\n}\n\n/* Measures the clamp in px, so the component can tell whether the content is longer. */\n.probe {\n position: absolute;\n inset-block-start: 0;\n inset-inline-start: 0;\n inline-size: 0;\n block-size: var(--_clamp);\n visibility: hidden;\n pointer-events: none;\n}\n\n.clip {\n --_fade: var(--show-more-fade, var(--space-7));\n\n align-self: stretch;\n min-inline-size: 0;\n}\n\n.showMore[data-state='collapsed'] .clip {\n max-block-size: var(--_clamp);\n overflow: hidden;\n}\n\n.showMore[data-state='collapsed'][data-overflowing]:not([data-mode='items']) .clip {\n mask-image: linear-gradient(to bottom, black calc(100% - var(--_fade)), transparent);\n}\n\n.clip[data-animating] {\n overflow: hidden;\n transition: block-size var(--dur-3) var(--ease-out);\n}\n.showMore .clip[data-animating] {\n max-block-size: none;\n mask-image: none;\n}\n\n.toggle {\n display: inline-flex;\n align-items: center;\n gap: var(--space-1);\n min-block-size: max(2.75rem, var(--control-sm));\n padding: 0;\n border: 0;\n background: transparent;\n color: var(--color-ink);\n font-family: var(--font-text);\n font-size: var(--text-sm);\n font-weight: var(--label-weight);\n letter-spacing: var(--label-tracking);\n text-transform: var(--label-transform);\n line-height: var(--leading-snug);\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n}\n\n.toggle:focus-visible {\n outline: 2px solid var(--color-focus);\n outline-offset: 2px;\n border-radius: var(--radius-field);\n}\n\n.chevron {\n flex: none;\n font-size: 1rem;\n color: var(--color-ink-subtle);\n transition:\n rotate var(--dur-2) var(--ease-spring),\n color var(--dur-1) var(--ease-out);\n}\n@media (hover: hover) {\n .toggle:hover .chevron {\n color: var(--color-ink);\n }\n}\n.toggle[aria-expanded='true'] .chevron {\n rotate: 180deg;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .chevron {\n transition: none;\n }\n}\n"],"mappings":""}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { type HTMLAttributes, type ReactElement } from 'react';
|
|
2
|
+
export type TransitionPreset = 'fade' | 'slide-up' | 'slide-down' | 'scale' | 'pop' | 'none';
|
|
3
|
+
export interface TransitionProps extends HTMLAttributes<HTMLElement> {
|
|
4
|
+
/** Whether the element shows. Turning it on plays the enter animation, turning it off the exit. */
|
|
5
|
+
open: boolean;
|
|
6
|
+
/**
|
|
7
|
+
* How it enters and leaves. `fade` (default); `slide-up` and `slide-down` travel a short way
|
|
8
|
+
* as they fade, for panels that come from below or above; `scale` grows slightly; `pop` lands
|
|
9
|
+
* with the theme's spring. `none` leaves the animation to your own CSS (see the examples).
|
|
10
|
+
*/
|
|
11
|
+
preset?: TransitionPreset;
|
|
12
|
+
/**
|
|
13
|
+
* Keep the element in the page once it has left, `hidden`. Default `false`: it unmounts after
|
|
14
|
+
* its exit animation finishes.
|
|
15
|
+
*/
|
|
16
|
+
keepMounted?: boolean;
|
|
17
|
+
/** Play the enter animation when it first renders open. Default `false`. */
|
|
18
|
+
appear?: boolean;
|
|
19
|
+
/** Runs when an enter or exit animation finishes, with the state it finished in. */
|
|
20
|
+
onOpenChangeComplete?: (open: boolean) => void;
|
|
21
|
+
/** The one element to animate. It receives the class, data attributes and ref. */
|
|
22
|
+
children: ReactElement;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Mounts an element with an enter animation and unmounts it after an exit animation.
|
|
26
|
+
*
|
|
27
|
+
* @remarks
|
|
28
|
+
* `Transition` wraps one element and plays an animation when `open` turns on and another when it
|
|
29
|
+
* turns off, keeping the element on screen until the exit has finished. Use it for something
|
|
30
|
+
* that appears because of an action: a panel that slides up, a banner that fades in, a chat
|
|
31
|
+
* window that opens. It adds no element of its own.
|
|
32
|
+
*
|
|
33
|
+
* The presets use the theme's durations and easings, so a Fiesta panel lands with a bounce and a
|
|
34
|
+
* Ledger one doesn't. Under reduced motion every preset becomes an instant change.
|
|
35
|
+
*
|
|
36
|
+
* Nothing animates on the first render unless you pass `appear`, so content that's already open
|
|
37
|
+
* when the page loads stays still.
|
|
38
|
+
*
|
|
39
|
+
* ## Your own animation
|
|
40
|
+
*
|
|
41
|
+
* With `preset="none"`, the element gets `data-state="open"` or `data-state="closed"` and no
|
|
42
|
+
* animation of its own, so your CSS can run any keyframes on those states. `data-animate` is set
|
|
43
|
+
* from the first change on, so including it keeps the first render still. `Transition` waits for
|
|
44
|
+
* your exit animation to finish before it unmounts the element:
|
|
45
|
+
*
|
|
46
|
+
* ```css
|
|
47
|
+
* .drawer[data-animate][data-state='open'] {
|
|
48
|
+
* animation: drawer-in var(--dur-3) var(--ease-out);
|
|
49
|
+
* }
|
|
50
|
+
* .drawer[data-animate][data-state='closed'] {
|
|
51
|
+
* animation: drawer-out var(--dur-2) var(--ease-in-out) both;
|
|
52
|
+
* }
|
|
53
|
+
* ```
|
|
54
|
+
*
|
|
55
|
+
* @privateRemarks
|
|
56
|
+
* Presence without Radix's internal Presence (not exported from `radix-ui`): the exit waits for
|
|
57
|
+
* `animationend`, with a timer from the computed duration in case it never comes. `data-animate`
|
|
58
|
+
* appears on the first change (or at once with `appear`) and stays, so removing it can never
|
|
59
|
+
* start an animation by itself.
|
|
60
|
+
*
|
|
61
|
+
* <Transition open={chatOpen} preset="slide-up"><ChatPanel /></Transition>
|
|
62
|
+
*/
|
|
63
|
+
export declare const Transition: import("react").ForwardRefExoticComponent<TransitionProps & import("react").RefAttributes<HTMLElement>>;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { cx as e } from "../../../utils/cx.js";
|
|
2
|
+
import { useMergedRefs as t } from "../../inputs/internal/refs.js";
|
|
3
|
+
import { useLatest as n } from "../../inputs/internal/useLatest.js";
|
|
4
|
+
import r from "./Transition.module.js";
|
|
5
|
+
import { forwardRef as i, useEffect as a, useLayoutEffect as o, useRef as s, useState as c } from "react";
|
|
6
|
+
import { jsx as l } from "react/jsx-runtime";
|
|
7
|
+
import { Slot as u } from "radix-ui";
|
|
8
|
+
//#region src/components/layout/Transition/Transition.tsx
|
|
9
|
+
var d = typeof document > "u" ? a : o;
|
|
10
|
+
function f(e) {
|
|
11
|
+
let t = getComputedStyle(e);
|
|
12
|
+
if (!t.animationName || t.animationName === "none") return 0;
|
|
13
|
+
let n = t.animationDuration.split(","), r = t.animationDelay.split(","), i = 0;
|
|
14
|
+
return n.forEach((e, t) => {
|
|
15
|
+
let n = parseFloat(e) + parseFloat(r[t] ?? r[0] ?? "0");
|
|
16
|
+
Number.isFinite(n) && (i = Math.max(i, n));
|
|
17
|
+
}), i * 1e3;
|
|
18
|
+
}
|
|
19
|
+
var p = i(function({ open: i, preset: a = "fade", keepMounted: o = !1, appear: p = !1, onOpenChangeComplete: m, className: h, children: g, ..._ }, v) {
|
|
20
|
+
let y = s(null), b = n(m), [x, S] = c(i);
|
|
21
|
+
i && !x && S(!0);
|
|
22
|
+
let [C, w] = c(p), [T, E] = c(i);
|
|
23
|
+
T !== i && (E(i), w(!0));
|
|
24
|
+
let D = s(!(p && i));
|
|
25
|
+
d(() => {
|
|
26
|
+
if (D.current) {
|
|
27
|
+
D.current = !1;
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
let e = y.current, t = () => {
|
|
31
|
+
i || S(!1), b.current?.(i);
|
|
32
|
+
}, n = e ? f(e) : 0;
|
|
33
|
+
if (!e || n === 0) {
|
|
34
|
+
t();
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
let r = !1, a = () => {
|
|
38
|
+
r || (r = !0, e.removeEventListener("animationend", o), window.clearTimeout(s), t());
|
|
39
|
+
}, o = (t) => {
|
|
40
|
+
t.target === e && a();
|
|
41
|
+
};
|
|
42
|
+
e.addEventListener("animationend", o);
|
|
43
|
+
let s = window.setTimeout(a, n + 50);
|
|
44
|
+
return () => {
|
|
45
|
+
r = !0, e.removeEventListener("animationend", o), window.clearTimeout(s);
|
|
46
|
+
};
|
|
47
|
+
}, [i]);
|
|
48
|
+
let O = t(v, y);
|
|
49
|
+
return !x && !o ? null : /* @__PURE__ */ l(u.Root, {
|
|
50
|
+
ref: O,
|
|
51
|
+
className: e(r.transition, h),
|
|
52
|
+
"data-state": i ? "open" : "closed",
|
|
53
|
+
"data-preset": a,
|
|
54
|
+
"data-animate": C || void 0,
|
|
55
|
+
hidden: !i && !x || void 0,
|
|
56
|
+
..._,
|
|
57
|
+
children: g
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
//#endregion
|
|
61
|
+
export { p as Transition };
|
|
62
|
+
|
|
63
|
+
//# sourceMappingURL=Transition.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Transition.js","names":["styles","Transition_module_default","useIsomorphicLayoutEffect","useEffect","useLayoutEffect","animationMs","style","element","durations","delays","longest","total","duration","index","Transition","forwardRef","rest","useRef","complete","useLatest","onOpenChangeComplete","present","setPresent","useState","open","animate","setAnimate","appear","previous","setPrevious","first","node","finish","done","end","onEnd","timer","event","mergedRef","useMergedRefs","ref","keepMounted","Slot","cx","className","preset"],"sources":["../../../../src/components/layout/Transition/Transition.tsx"],"sourcesContent":["import {\n forwardRef,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n type HTMLAttributes,\n type ReactElement,\n} from 'react'\nimport { Slot } from 'radix-ui'\nimport { cx } from '#utils/cx'\nimport { useMergedRefs } from '#components/inputs/internal/refs'\nimport { useLatest } from '#components/inputs/internal/useLatest'\nimport styles from './Transition.module.css'\n\n// Layout effects warn during server rendering; on the server nothing moves anyway.\nconst useIsomorphicLayoutEffect = typeof document === 'undefined' ? useEffect : useLayoutEffect\n\nexport type TransitionPreset = 'fade' | 'slide-up' | 'slide-down' | 'scale' | 'pop' | 'none'\n\nexport interface TransitionProps extends HTMLAttributes<HTMLElement> {\n /** Whether the element shows. Turning it on plays the enter animation, turning it off the exit. */\n open: boolean\n /**\n * How it enters and leaves. `fade` (default); `slide-up` and `slide-down` travel a short way\n * as they fade, for panels that come from below or above; `scale` grows slightly; `pop` lands\n * with the theme's spring. `none` leaves the animation to your own CSS (see the examples).\n */\n preset?: TransitionPreset\n /**\n * Keep the element in the page once it has left, `hidden`. Default `false`: it unmounts after\n * its exit animation finishes.\n */\n keepMounted?: boolean\n /** Play the enter animation when it first renders open. Default `false`. */\n appear?: boolean\n /** Runs when an enter or exit animation finishes, with the state it finished in. */\n onOpenChangeComplete?: (open: boolean) => void\n /** The one element to animate. It receives the class, data attributes and ref. */\n children: ReactElement\n}\n\n/** The longest of an element's computed animation durations, in milliseconds. */\nfunction animationMs(element: HTMLElement): number {\n const style = getComputedStyle(element)\n if (!style.animationName || style.animationName === 'none') return 0\n const durations = style.animationDuration.split(',')\n const delays = style.animationDelay.split(',')\n let longest = 0\n durations.forEach((duration, index) => {\n const total = parseFloat(duration) + parseFloat(delays[index] ?? delays[0] ?? '0')\n if (Number.isFinite(total)) longest = Math.max(longest, total)\n })\n return longest * 1000\n}\n\n/**\n * Mounts an element with an enter animation and unmounts it after an exit animation.\n *\n * @remarks\n * `Transition` wraps one element and plays an animation when `open` turns on and another when it\n * turns off, keeping the element on screen until the exit has finished. Use it for something\n * that appears because of an action: a panel that slides up, a banner that fades in, a chat\n * window that opens. It adds no element of its own.\n *\n * The presets use the theme's durations and easings, so a Fiesta panel lands with a bounce and a\n * Ledger one doesn't. Under reduced motion every preset becomes an instant change.\n *\n * Nothing animates on the first render unless you pass `appear`, so content that's already open\n * when the page loads stays still.\n *\n * ## Your own animation\n *\n * With `preset=\"none\"`, the element gets `data-state=\"open\"` or `data-state=\"closed\"` and no\n * animation of its own, so your CSS can run any keyframes on those states. `data-animate` is set\n * from the first change on, so including it keeps the first render still. `Transition` waits for\n * your exit animation to finish before it unmounts the element:\n *\n * ```css\n * .drawer[data-animate][data-state='open'] {\n * animation: drawer-in var(--dur-3) var(--ease-out);\n * }\n * .drawer[data-animate][data-state='closed'] {\n * animation: drawer-out var(--dur-2) var(--ease-in-out) both;\n * }\n * ```\n *\n * @privateRemarks\n * Presence without Radix's internal Presence (not exported from `radix-ui`): the exit waits for\n * `animationend`, with a timer from the computed duration in case it never comes. `data-animate`\n * appears on the first change (or at once with `appear`) and stays, so removing it can never\n * start an animation by itself.\n *\n * <Transition open={chatOpen} preset=\"slide-up\"><ChatPanel /></Transition>\n */\nexport const Transition = forwardRef<HTMLElement, TransitionProps>(function Transition(\n {\n open,\n preset = 'fade',\n keepMounted = false,\n appear = false,\n onOpenChangeComplete,\n className,\n children,\n ...rest\n },\n ref,\n) {\n const element = useRef<HTMLElement>(null)\n const complete = useLatest(onOpenChangeComplete)\n // On screen: true while open and while the exit animation runs.\n const [present, setPresent] = useState(open)\n if (open && !present) setPresent(true)\n const [animate, setAnimate] = useState(appear)\n const [previous, setPrevious] = useState(open)\n if (previous !== open) {\n setPrevious(open)\n setAnimate(true)\n }\n\n const first = useRef(!(appear && open))\n useIsomorphicLayoutEffect(() => {\n if (first.current) {\n first.current = false\n return\n }\n const node = element.current\n const finish = () => {\n if (!open) setPresent(false)\n complete.current?.(open)\n }\n const duration = node ? animationMs(node) : 0\n if (!node || duration === 0) {\n finish()\n return\n }\n let done = false\n const end = () => {\n if (done) return\n done = true\n node.removeEventListener('animationend', onEnd)\n window.clearTimeout(timer)\n finish()\n }\n const onEnd = (event: AnimationEvent) => {\n if (event.target === node) end()\n }\n node.addEventListener('animationend', onEnd)\n // In case `animationend` never comes (the tab is in the background, the element was hidden).\n const timer = window.setTimeout(end, duration + 50)\n return () => {\n done = true\n node.removeEventListener('animationend', onEnd)\n window.clearTimeout(timer)\n }\n }, [open])\n\n const mergedRef = useMergedRefs(ref, element)\n if (!present && !keepMounted) return null\n return (\n <Slot.Root\n ref={mergedRef}\n className={cx(styles.transition, className)}\n data-state={open ? 'open' : 'closed'}\n data-preset={preset}\n data-animate={animate || undefined}\n hidden={!open && !present ? true : undefined}\n {...rest}\n >\n {children}\n </Slot.Root>\n )\n})\n"],"mappings":";;;;;;;;AAgBA,IAAME,IAA4B,OAAO,WAAa,MAAcC,IAAYC;AA2BhF,SAASC,EAAY,GAA8B;CACjD,IAAMC,IAAQ,iBAAiBC,CAAO;CACtC,IAAI,CAACD,EAAM,iBAAiBA,EAAM,kBAAkB,QAAQ,OAAO;CACnE,IAAME,IAAYF,EAAM,kBAAkB,MAAM,GAAG,GAC7CG,IAASH,EAAM,eAAe,MAAM,GAAG,GACzCI,IAAU;CAKd,OAJAF,EAAU,SAAS,GAAU,MAAU;EACrC,IAAMG,IAAQ,WAAWC,CAAQ,IAAI,WAAWH,EAAOI,MAAUJ,EAAO,MAAM,GAAG;EACjF,AAAI,OAAO,SAASE,CAAK,MAAG,IAAU,KAAK,IAAID,GAASC,CAAK;CAC/D,CAAC,GACMD,IAAU;AACnB;AAyCA,IAAaI,IAAaC,EAAyC,SACjE,EACE,SACA,YAAS,QACT,iBAAc,IACd,YAAS,IACT,yBACA,cACA,aACA,GAAGC,KAEL,GACA;CACA,IAAMT,IAAUU,EAAoB,IAAI,GAClCC,IAAWC,EAAUC,CAAoB,GAEzC,CAACC,GAASC,KAAcC,EAASC,CAAI;CAC3C,AAAIA,KAAQ,CAACH,KAASC,EAAW,EAAI;CACrC,IAAM,CAACG,GAASC,KAAcH,EAASI,CAAM,GACvC,CAACC,GAAUC,KAAeN,EAASC,CAAI;CAC7C,AAAII,MAAaJ,MACfK,EAAYL,CAAI,GAChBE,EAAW,EAAI;CAGjB,IAAMI,IAAQb,EAAO,EAAEU,KAAUH,EAAK;CACtC,QAAgC;EAC9B,IAAIM,EAAM,SAAS;GACjB,EAAM,UAAU;GAChB;EACF;EACA,IAAMC,IAAOxB,EAAQ,SACfyB,UAAe;GAEnB,AADKR,KAAMF,EAAW,EAAK,GAC3BJ,EAAS,UAAUM,CAAI;EACzB,GACMZ,IAAWmB,IAAO1B,EAAY0B,CAAI,IAAI;EAC5C,IAAI,CAACA,KAAQnB,MAAa,GAAG;GAC3B,EAAO;GACP;EACF;EACA,IAAIqB,IAAO,IACLC,UAAY;GAChB,AAAID,MACJ,IAAO,IACPF,EAAK,oBAAoB,gBAAgBI,CAAK,GAC9C,OAAO,aAAaC,CAAK,GACzBJ,EAAO;EACT,GACMG,KAAS,MAA0B;GACvC,AAAIE,EAAM,WAAWN,KAAMG,EAAI;EACjC;EACA,EAAK,iBAAiB,gBAAgBC,CAAK;EAE3C,IAAMC,IAAQ,OAAO,WAAWF,GAAKtB,IAAW,EAAE;EAClD,aAAa;GAGX,AAFA,IAAO,IACPmB,EAAK,oBAAoB,gBAAgBI,CAAK,GAC9C,OAAO,aAAaC,CAAK;EAC3B;CACF,GAAG,CAACZ,CAAI,CAAC;CAET,IAAMc,IAAYC,EAAcC,GAAKjC,CAAO;CAE5C,OADI,CAACc,KAAW,CAACoB,IAAoB,OAEnC,kBAACC,EAAK,MAAN;EACE,KAAKJ;EACL,WAAWK,EAAG3C,EAAO,YAAY4C,CAAS;EAC1C,cAAYpB,IAAO,SAAS;EAC5B,eAAaqB;EACb,gBAAcpB,KAAW,KAAA;EACzB,QAAQ,CAACD,KAAQ,CAACH,KAAiB,KAAA;EACnC,GAAIL;EAEH;CACQ,CAAA;AAEf,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//#region src/components/layout/Transition/Transition.module.css
|
|
2
|
+
var e = "kiln-Transition__transition", t = {
|
|
3
|
+
transition: e,
|
|
4
|
+
"fade-in": "kiln-Transition__fade-in",
|
|
5
|
+
"fade-out": "kiln-Transition__fade-out",
|
|
6
|
+
"slide-up-in": "kiln-Transition__slide-up-in",
|
|
7
|
+
"slide-up-out": "kiln-Transition__slide-up-out",
|
|
8
|
+
"slide-down-in": "kiln-Transition__slide-down-in",
|
|
9
|
+
"slide-down-out": "kiln-Transition__slide-down-out",
|
|
10
|
+
"scale-in": "kiln-Transition__scale-in",
|
|
11
|
+
"scale-out": "kiln-Transition__scale-out",
|
|
12
|
+
"pop-in": "kiln-Transition__pop-in"
|
|
13
|
+
};
|
|
14
|
+
//#endregion
|
|
15
|
+
export { t as default, e as transition };
|
|
16
|
+
|
|
17
|
+
//# sourceMappingURL=Transition.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Transition.module.js","names":["transition","Transition_module_default"],"sources":["../../../../src/components/layout/Transition/Transition.module.css"],"sourcesContent":["/*\n * Component tokens:\n * --transition-distance how far slide-up and slide-down travel, default var(--space-4)\n *\n * Every preset fades; the movement on top is small, because motion here answers an action rather\n * than decorating it. Enter uses --dur-3 with --ease-out (things arriving), exit the shorter --dur-2\n * with --ease-in-out. `pop` lands on --ease-spring, so it bounces only in themes that bounce.\n * Animations run only once `data-animate` is set, so nothing moves on the first render.\n */\n.transition {\n --_distance: var(--transition-distance, var(--space-4));\n}\n\n.transition[hidden] {\n display: none;\n}\n\n.transition[data-animate][data-state='open']:not([data-preset='none']) {\n animation: fade-in var(--dur-3) var(--ease-out) both;\n}\n.transition[data-animate][data-state='closed']:not([data-preset='none']) {\n animation: fade-out var(--dur-2) var(--ease-in-out) both;\n}\n\n.transition[data-animate][data-state='open'][data-preset='slide-up'] {\n animation-name: slide-up-in;\n}\n.transition[data-animate][data-state='closed'][data-preset='slide-up'] {\n animation-name: slide-up-out;\n}\n.transition[data-animate][data-state='open'][data-preset='slide-down'] {\n animation-name: slide-down-in;\n}\n.transition[data-animate][data-state='closed'][data-preset='slide-down'] {\n animation-name: slide-down-out;\n}\n.transition[data-animate][data-state='open'][data-preset='scale'] {\n animation-name: scale-in;\n}\n.transition[data-animate][data-state='closed'][data-preset='scale'] {\n animation-name: scale-out;\n}\n.transition[data-animate][data-state='open'][data-preset='pop'] {\n animation-name: pop-in;\n animation-timing-function: var(--ease-spring);\n}\n.transition[data-animate][data-state='closed'][data-preset='pop'] {\n animation-name: scale-out;\n}\n\n@keyframes fade-in {\n from {\n opacity: 0;\n }\n}\n@keyframes fade-out {\n to {\n opacity: 0;\n }\n}\n@keyframes slide-up-in {\n from {\n opacity: 0;\n translate: 0 var(--_distance);\n }\n}\n@keyframes slide-up-out {\n to {\n opacity: 0;\n translate: 0 var(--_distance);\n }\n}\n@keyframes slide-down-in {\n from {\n opacity: 0;\n translate: 0 calc(var(--_distance) * -1);\n }\n}\n@keyframes slide-down-out {\n to {\n opacity: 0;\n translate: 0 calc(var(--_distance) * -1);\n }\n}\n@keyframes scale-in {\n from {\n opacity: 0;\n scale: 0.96;\n }\n}\n@keyframes scale-out {\n to {\n opacity: 0;\n scale: 0.96;\n }\n}\n@keyframes pop-in {\n from {\n opacity: 0;\n scale: 0.9;\n }\n}\n\n/* Reduced motion: nothing travels or grows. The durations are already ~0; this drops the movement. */\n@media (prefers-reduced-motion: reduce) {\n .transition[data-animate][data-state='open']:not([data-preset='none']) {\n animation-name: fade-in;\n }\n .transition[data-animate][data-state='closed']:not([data-preset='none']) {\n animation-name: fade-out;\n }\n}\n"],"mappings":""}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { type ButtonHTMLAttributes, type HTMLAttributes } from 'react';
|
|
2
|
+
import { type CollapseProps } from '../../layout/Collapse/index.js';
|
|
3
|
+
export interface CollapsibleProps extends HTMLAttributes<HTMLDivElement> {
|
|
4
|
+
/** Controlled open state. */
|
|
5
|
+
open?: boolean;
|
|
6
|
+
/** Uncontrolled initial state. Default `false`. */
|
|
7
|
+
defaultOpen?: boolean;
|
|
8
|
+
/** Runs when the trigger asks to open or close. */
|
|
9
|
+
onOpenChange?: (open: boolean) => void;
|
|
10
|
+
/** The trigger does nothing. */
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface CollapsibleTriggerProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
14
|
+
/**
|
|
15
|
+
* Render the single child (your own `Button`, a row, a heading's button) as the trigger
|
|
16
|
+
* instead of Kiln's label-and-chevron button. It gets `aria-expanded`, `aria-controls` and the
|
|
17
|
+
* click handler.
|
|
18
|
+
*/
|
|
19
|
+
asChild?: boolean;
|
|
20
|
+
}
|
|
21
|
+
/** `Collapse`'s props except `open`, which comes from the `Collapsible`. */
|
|
22
|
+
export type CollapsibleContentProps = Omit<CollapseProps, 'open'>;
|
|
23
|
+
/**
|
|
24
|
+
* A trigger that shows and hides one block of content: "Advanced options", "Show the formula".
|
|
25
|
+
*
|
|
26
|
+
* @remarks
|
|
27
|
+
* `Collapsible` is a button and the content it opens, wired together: the button gets
|
|
28
|
+
* `aria-expanded` and `aria-controls`, and the content animates with the same height motion as
|
|
29
|
+
* {@link Collapse | Collapse}. Unlike {@link Accordion | Accordion} it brings no heading, no
|
|
30
|
+
* divider and no list of items, so it fits inside a form, a card or a sidebar.
|
|
31
|
+
*
|
|
32
|
+
* The default trigger is a label and a chevron that turns. To use your own control, such as a
|
|
33
|
+
* ghost `Button`, pass it to `Collapsible.Trigger` with `asChild`.
|
|
34
|
+
*
|
|
35
|
+
* @privateRemarks
|
|
36
|
+
* Not Radix Collapsible: its Content owns mounting and `hidden`, so it can't run Collapse's
|
|
37
|
+
* height motion. The context here is the same contract (open, contentId, toggle) with Kiln's
|
|
38
|
+
* Collapse underneath.
|
|
39
|
+
*
|
|
40
|
+
* <Collapsible>
|
|
41
|
+
* <Collapsible.Trigger>Advanced options</Collapsible.Trigger>
|
|
42
|
+
* <Collapsible.Content>…</Collapsible.Content>
|
|
43
|
+
* </Collapsible>
|
|
44
|
+
*/
|
|
45
|
+
export declare const Collapsible: import("react").ForwardRefExoticComponent<CollapsibleProps & import("react").RefAttributes<HTMLDivElement>> & {
|
|
46
|
+
Root: import("react").ForwardRefExoticComponent<CollapsibleProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
47
|
+
Trigger: import("react").ForwardRefExoticComponent<CollapsibleTriggerProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
48
|
+
Content: import("react").ForwardRefExoticComponent<CollapsibleContentProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
49
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { cx as e } from "../../../utils/cx.js";
|
|
2
|
+
import { ChevronDownIcon as t } from "../../../icons/icons.js";
|
|
3
|
+
import { useLatest as n } from "../../inputs/internal/useLatest.js";
|
|
4
|
+
import { Collapse as r } from "../../layout/Collapse/Collapse.js";
|
|
5
|
+
import i from "./Collapsible.module.js";
|
|
6
|
+
import { createContext as a, forwardRef as o, useCallback as s, useContext as c, useId as l, useState as u } from "react";
|
|
7
|
+
import { jsx as d, jsxs as f } from "react/jsx-runtime";
|
|
8
|
+
import { Slot as p } from "radix-ui";
|
|
9
|
+
//#region src/components/navigation/Collapsible/Collapsible.tsx
|
|
10
|
+
var m = a(null);
|
|
11
|
+
function h(e) {
|
|
12
|
+
let t = c(m);
|
|
13
|
+
if (!t) throw Error(`Collapsible.${e} must be inside <Collapsible>.`);
|
|
14
|
+
return t;
|
|
15
|
+
}
|
|
16
|
+
var g = o(function({ open: t, defaultOpen: r = !1, onOpenChange: a, disabled: o = !1, className: c, children: f, ...p }, h) {
|
|
17
|
+
let [g, _] = u(r), v = t ?? g, y = l(), b = n(a), x = s(() => {
|
|
18
|
+
let e = !v;
|
|
19
|
+
t === void 0 && _(e), b.current?.(e);
|
|
20
|
+
}, [
|
|
21
|
+
v,
|
|
22
|
+
t,
|
|
23
|
+
b
|
|
24
|
+
]);
|
|
25
|
+
return /* @__PURE__ */ d(m.Provider, {
|
|
26
|
+
value: {
|
|
27
|
+
open: v,
|
|
28
|
+
disabled: o,
|
|
29
|
+
contentId: y,
|
|
30
|
+
toggle: x
|
|
31
|
+
},
|
|
32
|
+
children: /* @__PURE__ */ d("div", {
|
|
33
|
+
ref: h,
|
|
34
|
+
className: e(i.root, c),
|
|
35
|
+
"data-state": v ? "open" : "closed",
|
|
36
|
+
"data-disabled": o || void 0,
|
|
37
|
+
...p,
|
|
38
|
+
children: f
|
|
39
|
+
})
|
|
40
|
+
});
|
|
41
|
+
}), _ = o(function({ asChild: n = !1, onClick: r, className: a, children: o, ...s }, c) {
|
|
42
|
+
let { open: l, disabled: u, contentId: m, toggle: g } = h("Trigger"), _ = {
|
|
43
|
+
ref: c,
|
|
44
|
+
type: "button",
|
|
45
|
+
"aria-expanded": l,
|
|
46
|
+
"aria-controls": m,
|
|
47
|
+
"data-state": l ? "open" : "closed",
|
|
48
|
+
disabled: u || void 0,
|
|
49
|
+
onClick: (e) => {
|
|
50
|
+
r?.(e), e.defaultPrevented || g();
|
|
51
|
+
},
|
|
52
|
+
...s
|
|
53
|
+
};
|
|
54
|
+
return n ? /* @__PURE__ */ d(p.Root, {
|
|
55
|
+
className: a,
|
|
56
|
+
..._,
|
|
57
|
+
children: o
|
|
58
|
+
}) : /* @__PURE__ */ f("button", {
|
|
59
|
+
className: e(i.trigger, a),
|
|
60
|
+
..._,
|
|
61
|
+
children: [/* @__PURE__ */ d("span", {
|
|
62
|
+
className: i.label,
|
|
63
|
+
children: o
|
|
64
|
+
}), /* @__PURE__ */ d(t, { className: i.chevron })]
|
|
65
|
+
});
|
|
66
|
+
}), v = o(function({ className: t, ...n }, a) {
|
|
67
|
+
let { open: o, contentId: s } = h("Content");
|
|
68
|
+
return /* @__PURE__ */ d(r, {
|
|
69
|
+
ref: a,
|
|
70
|
+
id: s,
|
|
71
|
+
open: o,
|
|
72
|
+
className: e(i.content, t),
|
|
73
|
+
...n
|
|
74
|
+
});
|
|
75
|
+
}), y = Object.assign(g, {
|
|
76
|
+
Root: g,
|
|
77
|
+
Trigger: _,
|
|
78
|
+
Content: v
|
|
79
|
+
});
|
|
80
|
+
//#endregion
|
|
81
|
+
export { y as Collapsible };
|
|
82
|
+
|
|
83
|
+
//# sourceMappingURL=Collapsible.js.map
|