gridsmith-ui 0.22.0 → 0.23.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.
@@ -0,0 +1,72 @@
1
+ import { useRef, useState, useCallback } from 'react';
2
+
3
+ // ../../showcase/src/lib/use-scroll-edges.ts
4
+ var EDGE_FADE = 24;
5
+ var NONE = { left: false, right: false, vertical: false, gutterRight: 0, gutterBottom: 0 };
6
+ var same = (a, b) => a.left === b.left && a.right === b.right && a.vertical === b.vertical && a.gutterRight === b.gutterRight && a.gutterBottom === b.gutterBottom;
7
+ function useScrollEdges(options = {}) {
8
+ const { enabled = true, fade = EDGE_FADE, noFade } = options;
9
+ const elRef = useRef(null);
10
+ const observer = useRef(null);
11
+ const borders = useRef({ x: 0, y: 0 });
12
+ const [state, setState] = useState(NONE);
13
+ const measure = useCallback(() => {
14
+ const el = elRef.current;
15
+ if (!el) return;
16
+ const next = {
17
+ left: el.scrollLeft > 1,
18
+ right: el.scrollLeft + el.clientWidth < el.scrollWidth - 1,
19
+ vertical: el.scrollHeight > el.clientHeight + 1,
20
+ gutterRight: Math.max(0, el.offsetWidth - el.clientWidth - borders.current.x),
21
+ gutterBottom: Math.max(0, el.offsetHeight - el.clientHeight - borders.current.y)
22
+ };
23
+ setState((prev) => same(prev, next) ? prev : next);
24
+ }, []);
25
+ const ref = useCallback((el) => {
26
+ observer.current?.disconnect();
27
+ observer.current = null;
28
+ elRef.current = el;
29
+ if (!el || !enabled) return;
30
+ const style = getComputedStyle(el);
31
+ const px = (v) => parseFloat(v) || 0;
32
+ borders.current = { x: px(style.borderLeftWidth) + px(style.borderRightWidth), y: px(style.borderTopWidth) + px(style.borderBottomWidth) };
33
+ measure();
34
+ if (typeof ResizeObserver === "undefined") return;
35
+ const o = new ResizeObserver(measure);
36
+ o.observe(el);
37
+ for (const child of Array.from(el.children)) o.observe(child);
38
+ observer.current = o;
39
+ }, [enabled, measure]);
40
+ const fadeLeft = state.left && !noFade?.left;
41
+ const fadeRight = state.right && !noFade?.right;
42
+ const shown = enabled && (fadeLeft || fadeRight);
43
+ const { gutterRight: gr, gutterBottom: gb } = state;
44
+ const fadeLayer = `linear-gradient(to right, ${fadeLeft ? `transparent 0, black ${fade}px` : "black 0"}, ${fadeRight ? `black calc(100% - ${gr + fade}px), transparent calc(100% - ${gr}px)` : "black 100%"})`;
45
+ const gutters = [gb > 0 && { size: `100% ${gb}px`, position: "0 100%" }, gr > 0 && { size: `${gr}px 100%`, position: "100% 0" }].filter(Boolean);
46
+ const mask = shown ? {
47
+ image: [fadeLayer, ...gutters.map(() => "linear-gradient(black, black)")].join(", "),
48
+ size: ["100% 100%", ...gutters.map((g) => g.size)].join(", "),
49
+ position: ["0 0", ...gutters.map((g) => g.position)].join(", ")
50
+ } : void 0;
51
+ return {
52
+ /** The scroller's ref (a callback). */
53
+ ref,
54
+ /** The scroller itself, for code that moves it. */
55
+ elRef,
56
+ measure,
57
+ /** Content hides past a side edge: the scroller moves sideways, and its edges fade (but those in `noFade`). */
58
+ overflowing: enabled && (state.left || state.right),
59
+ /** It scrolls either way, sideways or up and down: a keyboard needs to reach it. */
60
+ scrollable: enabled && (state.left || state.right || state.vertical),
61
+ edges: { left: state.left, right: state.right },
62
+ /** For the scroller: `data-fade` names the faded edges ("left", "right" or "left right"), the style masks them. */
63
+ fadeProps: {
64
+ "data-fade": shown ? [fadeLeft && "left", fadeRight && "right"].filter(Boolean).join(" ") : void 0,
65
+ style: mask ? { maskImage: mask.image, WebkitMaskImage: mask.image, maskSize: mask.size, WebkitMaskSize: mask.size, maskPosition: mask.position, WebkitMaskPosition: mask.position, maskRepeat: "no-repeat", WebkitMaskRepeat: "no-repeat" } : void 0
66
+ }
67
+ };
68
+ }
69
+
70
+ export { EDGE_FADE, useScrollEdges };
71
+ //# sourceMappingURL=chunk-3BRQRKDO.js.map
72
+ //# sourceMappingURL=chunk-3BRQRKDO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../showcase/src/lib/use-scroll-edges.ts"],"names":[],"mappings":"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","file":"chunk-3BRQRKDO.js","sourcesContent":["import { useCallback, useRef, useState } from \"react\";\n\n/** Which edges of a horizontal scroller have content past them. */\nexport interface ScrollEdges { left: boolean; right: boolean }\n\n/** The width of an edge fade, in px (Sprint 25.4 chunk 5's, on the Tabs strip). */\nexport const EDGE_FADE = 24;\n\n/** What a measure reads: the edges with more, whether it scrolls up and down, and the room its scrollbars take. */\ninterface ScrollState extends ScrollEdges { vertical: boolean; gutterRight: number; gutterBottom: number }\n\nconst NONE: ScrollState = { left: false, right: false, vertical: false, gutterRight: 0, gutterBottom: 0 };\nconst same = (a: ScrollState, b: ScrollState) => a.left === b.left && a.right === b.right && a.vertical === b.vertical && a.gutterRight === b.gutterRight && a.gutterBottom === b.gutterBottom;\n\n/**\n * A horizontal scroller's edge fades — one mechanism for the Tabs strip (Sprint 25.4 chunk 5) and the Table's scroll\n * area (Sprint 25.10 T2, round 25's R25-2: a four-column table in half a row scrolled sideways, and on a Mac, where the\n * scrollbar hides until you scroll, nothing showed there was more). It reports which edges hide content and masks the\n * scroller on those edges only: none at either end, none when everything fits. A mask fades what is under it, so it\n * follows whatever surface the scroller sits on, and it takes no clicks. Scrollbars that take room (Windows, Linux, a\n * Mac set to always show them) stay whole: the mask leaves their gutters opaque and the fade ends where they start\n * (Sprint 25.10 review). Measured on scroll (pass `measure` as onScroll) and whenever the scroller or its content\n * changes size. Left to right only, as the library is.\n */\nexport function useScrollEdges<T extends HTMLElement = HTMLDivElement>(options: { enabled?: boolean; fade?: number; noFade?: { left?: boolean; right?: boolean } } = {}) {\n // `noFade`: an edge that shows what it hides some other way, as a table's pinned columns do with a shadow at their inner\n // edge (Sprint 25.13 A35); the other edge still fades (the review of 30 September: one pinned column dropped both)\n const { enabled = true, fade = EDGE_FADE, noFade } = options;\n const elRef = useRef<T | null>(null);\n const observer = useRef<ResizeObserver | null>(null);\n // the scroller's own borders, read once when it attaches: what offsetWidth counts beyond clientWidth and a scrollbar\n const borders = useRef({ x: 0, y: 0 });\n const [state, setState] = useState<ScrollState>(NONE);\n const measure = useCallback(() => {\n const el = elRef.current;\n if (!el) return;\n const next: ScrollState = {\n left: el.scrollLeft > 1,\n right: el.scrollLeft + el.clientWidth < el.scrollWidth - 1,\n vertical: el.scrollHeight > el.clientHeight + 1,\n gutterRight: Math.max(0, el.offsetWidth - el.clientWidth - borders.current.x),\n gutterBottom: Math.max(0, el.offsetHeight - el.clientHeight - borders.current.y),\n };\n setState((prev) => (same(prev, next) ? prev : next));\n }, []);\n // A callback ref, so the observer follows the element (a Table's scroll area mounts only on desktop, and again after\n // a switch from cards) and nothing re-renders when it attaches: state changes only when what it measures does.\n const ref = useCallback((el: T | null) => {\n observer.current?.disconnect();\n observer.current = null;\n elRef.current = el;\n if (!el || !enabled) return;\n const style = getComputedStyle(el);\n const px = (v: string) => parseFloat(v) || 0;\n borders.current = { x: px(style.borderLeftWidth) + px(style.borderRightWidth), y: px(style.borderTopWidth) + px(style.borderBottomWidth) };\n measure();\n if (typeof ResizeObserver === \"undefined\") return;\n // the content too: a table's rows arriving widen it without the scroller changing size\n const o = new ResizeObserver(measure);\n o.observe(el);\n for (const child of Array.from(el.children)) o.observe(child);\n observer.current = o;\n }, [enabled, measure]);\n const fadeLeft = state.left && !noFade?.left;\n const fadeRight = state.right && !noFade?.right;\n const shown = enabled && (fadeLeft || fadeRight);\n const { gutterRight: gr, gutterBottom: gb } = state;\n const fadeLayer = `linear-gradient(to right, ${fadeLeft ? `transparent 0, black ${fade}px` : \"black 0\"}, ${fadeRight ? `black calc(100% - ${gr + fade}px), transparent calc(100% - ${gr}px)` : \"black 100%\"})`;\n // an opaque layer over each scrollbar gutter; mask layers add up, so a scrollbar shows whole over the fade\n const gutters = [gb > 0 && { size: `100% ${gb}px`, position: \"0 100%\" }, gr > 0 && { size: `${gr}px 100%`, position: \"100% 0\" }].filter(Boolean) as Array<{ size: string; position: string }>;\n const mask = shown\n ? {\n image: [fadeLayer, ...gutters.map(() => \"linear-gradient(black, black)\")].join(\", \"),\n size: [\"100% 100%\", ...gutters.map((g) => g.size)].join(\", \"),\n position: [\"0 0\", ...gutters.map((g) => g.position)].join(\", \"),\n }\n : undefined;\n return {\n /** The scroller's ref (a callback). */\n ref,\n /** The scroller itself, for code that moves it. */\n elRef,\n measure,\n /** Content hides past a side edge: the scroller moves sideways, and its edges fade (but those in `noFade`). */\n overflowing: enabled && (state.left || state.right),\n /** It scrolls either way, sideways or up and down: a keyboard needs to reach it. */\n scrollable: enabled && (state.left || state.right || state.vertical),\n edges: { left: state.left, right: state.right } as ScrollEdges,\n /** For the scroller: `data-fade` names the faded edges (\"left\", \"right\" or \"left right\"), the style masks them. */\n fadeProps: {\n \"data-fade\": shown ? [fadeLeft && \"left\", fadeRight && \"right\"].filter(Boolean).join(\" \") : undefined,\n style: mask\n ? { maskImage: mask.image, WebkitMaskImage: mask.image, maskSize: mask.size, WebkitMaskSize: mask.size, maskPosition: mask.position, WebkitMaskPosition: mask.position, maskRepeat: \"no-repeat\", WebkitMaskRepeat: \"no-repeat\" }\n : undefined,\n },\n };\n}\n"]}