@sqlrooms/ui 0.29.0-rc.12 → 0.29.0-rc.13
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":"scrollable-row.d.ts","sourceRoot":"","sources":["../../src/components/scrollable-row.tsx"],"names":[],"mappings":"AACA,OAAO,KAAoC,MAAM,OAAO,CAAC;AAmBzD;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa;cAvBd,KAAK,CAAC,SAAS;gBACb,MAAM;sBACA,MAAM;IACxB;;;OAGG;gBACS,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC;IAC3C,6DAA6D;mBAC9C,MAAM;sBACH,OAAO,GAAG,QAAQ;qBACnB,MAAM;yBACF,MAAM;
|
|
1
|
+
{"version":3,"file":"scrollable-row.d.ts","sourceRoot":"","sources":["../../src/components/scrollable-row.tsx"],"names":[],"mappings":"AACA,OAAO,KAAoC,MAAM,OAAO,CAAC;AAmBzD;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa;cAvBd,KAAK,CAAC,SAAS;gBACb,MAAM;sBACA,MAAM;IACxB;;;OAGG;gBACS,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC;IAC3C,6DAA6D;mBAC9C,MAAM;sBACH,OAAO,GAAG,QAAQ;qBACnB,MAAM;yBACF,MAAM;qKA+K3B,CAAC"}
|
|
@@ -40,11 +40,53 @@ export const ScrollableRow = React.forwardRef(function ScrollableRow({ children,
|
|
|
40
40
|
container.addEventListener('scroll', updateScrollState);
|
|
41
41
|
const resizeObserver = new ResizeObserver(updateScrollState);
|
|
42
42
|
resizeObserver.observe(container);
|
|
43
|
+
// The container's own box stays fixed when its content changes size (e.g.
|
|
44
|
+
// loading placeholders replaced by wider suggestions), so ResizeObserver
|
|
45
|
+
// on the container alone would leave the arrows stale. Observe each child
|
|
46
|
+
// element instead, and re-observe as children are added or removed, so
|
|
47
|
+
// overflow changes track content-size changes too.
|
|
48
|
+
const contentObserver = new ResizeObserver(updateScrollState);
|
|
49
|
+
const observeChildren = () => {
|
|
50
|
+
contentObserver.disconnect();
|
|
51
|
+
for (const child of Array.from(container.children)) {
|
|
52
|
+
contentObserver.observe(child);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
observeChildren();
|
|
56
|
+
const mutationObserver = new MutationObserver((mutations) => {
|
|
57
|
+
// Only direct child additions/removals change the resize-observer targets.
|
|
58
|
+
if (mutations.some(({ type, target }) => type === 'childList' && target === container)) {
|
|
59
|
+
observeChildren();
|
|
60
|
+
}
|
|
61
|
+
updateScrollState();
|
|
62
|
+
});
|
|
63
|
+
// Text and styling can change overflow without resizing a direct child.
|
|
64
|
+
// Observe all attributes: hidden and arbitrary CSS selectors affect layout too.
|
|
65
|
+
mutationObserver.observe(container, {
|
|
66
|
+
childList: true,
|
|
67
|
+
characterData: true,
|
|
68
|
+
attributes: true,
|
|
69
|
+
subtree: true,
|
|
70
|
+
});
|
|
71
|
+
// Font metrics can change direct-text overflow without a DOM mutation or
|
|
72
|
+
// an element resize. Refresh after font loading settles, including failures.
|
|
73
|
+
const fonts = container.ownerDocument.fonts;
|
|
74
|
+
fonts?.addEventListener('loadingdone', updateScrollState);
|
|
75
|
+
fonts?.addEventListener('loadingerror', updateScrollState);
|
|
43
76
|
return () => {
|
|
44
77
|
container.removeEventListener('scroll', updateScrollState);
|
|
45
78
|
resizeObserver.disconnect();
|
|
79
|
+
contentObserver.disconnect();
|
|
80
|
+
mutationObserver.disconnect();
|
|
81
|
+
fonts?.removeEventListener('loadingdone', updateScrollState);
|
|
82
|
+
fonts?.removeEventListener('loadingerror', updateScrollState);
|
|
46
83
|
};
|
|
47
|
-
|
|
84
|
+
// `children` is intentionally excluded from the deps: it is typically a
|
|
85
|
+
// fresh array on every parent render, and re-running this effect (which
|
|
86
|
+
// calls setState) on every render would trip React's "Maximum update depth
|
|
87
|
+
// exceeded" warning. DOM observers and font events handle content changes;
|
|
88
|
+
// the scroll listener covers user/dnd scrolling.
|
|
89
|
+
}, [containerRef]);
|
|
48
90
|
const arrowBaseClass = cn('absolute top-0 z-10 flex h-full w-8 items-center backdrop-blur-md bg-background/50 transition-colors', arrowVisibility === 'hover'
|
|
49
91
|
? 'opacity-0 transition-opacity hover:opacity-100'
|
|
50
92
|
: 'opacity-100', arrowClassName);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scrollable-row.js","sourceRoot":"","sources":["../../src/components/scrollable-row.tsx"],"names":[],"mappings":"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","sourcesContent":["import {ChevronLeft, ChevronRight} from 'lucide-react';\nimport React, {useEffect, useRef, useState} from 'react';\nimport {cn} from '../lib/utils';\n\ntype ScrollableRowProps = {\n children: React.ReactNode;\n className?: string;\n scrollClassName?: string;\n /**\n * Ref to the inner scrolling container. Replaces the internal ref; the\n * arrows keep working.\n */\n scrollRef?: React.RefObject<HTMLDivElement>;\n /** Pixels scrolled per arrow activation. Defaults to 200. */\n scrollAmount?: number;\n arrowVisibility?: 'hover' | 'always';\n arrowClassName?: string;\n arrowIconClassName?: string;\n} & Omit<React.ComponentPropsWithoutRef<'div'>, 'children' | 'className'>;\n\n/**\n * A horizontally scrolling row with arrows that appear only where there is more\n * content.\n *\n * The forwarded ref and `className` target the outer wrapper (so `Slot` or\n * drop-target wrapping works); `scrollRef` targets the inner scrolling\n * container.\n */\nexport const ScrollableRow = React.forwardRef<\n HTMLDivElement,\n ScrollableRowProps\n>(function ScrollableRow(\n {\n children,\n className,\n scrollClassName,\n scrollRef,\n scrollAmount = 200,\n arrowVisibility = 'hover',\n arrowClassName,\n arrowIconClassName,\n ...rest\n },\n forwardedRef,\n) {\n const internalRef = useRef<HTMLDivElement>(null);\n const containerRef = scrollRef ?? internalRef;\n const [canScrollLeft, setCanScrollLeft] = useState(false);\n const [canScrollRight, setCanScrollRight] = useState(false);\n\n const scrollBy = (direction: 'left' | 'right') => {\n const container = containerRef.current;\n if (!container) return;\n\n container.scrollBy({\n left: direction === 'left' ? -scrollAmount : scrollAmount,\n behavior: 'smooth',\n });\n };\n\n useEffect(() => {\n const updateScrollState = () => {\n const container = containerRef.current;\n if (!container) return;\n\n const {scrollLeft, scrollWidth, clientWidth} = container;\n setCanScrollLeft(scrollLeft > 0);\n setCanScrollRight(scrollLeft + clientWidth < scrollWidth - 1);\n };\n\n const container = containerRef.current;\n if (!container) return;\n\n updateScrollState();\n\n container.addEventListener('scroll', updateScrollState);\n const resizeObserver = new ResizeObserver(updateScrollState);\n resizeObserver.observe(container);\n\n return () => {\n container.removeEventListener('scroll', updateScrollState);\n resizeObserver.disconnect();\n };\n }, [children, containerRef]);\n\n const arrowBaseClass = cn(\n 'absolute top-0 z-10 flex h-full w-8 items-center backdrop-blur-md bg-background/50 transition-colors',\n arrowVisibility === 'hover'\n ? 'opacity-0 transition-opacity hover:opacity-100'\n : 'opacity-100',\n arrowClassName,\n );\n\n return (\n <div ref={forwardedRef} className={cn('relative', className)} {...rest}>\n <button\n type=\"button\"\n onClick={() => scrollBy('left')}\n disabled={!canScrollLeft}\n className={cn(\n arrowBaseClass,\n 'left-0 justify-start pl-1',\n 'from-background/90 via-background/60 group bg-gradient-to-r to-transparent',\n !canScrollLeft && 'pointer-events-none opacity-0',\n )}\n aria-label=\"Scroll left\"\n title=\"Scroll left\"\n >\n <ChevronLeft\n className={cn(\n 'text-muted-foreground group-hover:text-foreground h-5 w-5 transition-colors',\n arrowIconClassName,\n )}\n />\n </button>\n\n <div ref={containerRef} className={scrollClassName}>\n {children}\n </div>\n\n <button\n type=\"button\"\n onClick={() => scrollBy('right')}\n disabled={!canScrollRight}\n className={cn(\n arrowBaseClass,\n 'right-0 justify-end pr-1',\n 'from-background/90 via-background/60 group bg-gradient-to-l to-transparent',\n !canScrollRight && 'pointer-events-none opacity-0',\n )}\n aria-label=\"Scroll right\"\n title=\"Scroll right\"\n >\n <ChevronRight\n className={cn(\n 'text-muted-foreground group-hover:text-foreground h-5 w-5 transition-colors',\n arrowIconClassName,\n )}\n />\n </button>\n </div>\n );\n});\n\nScrollableRow.displayName = 'ScrollableRow';\n"]}
|
|
1
|
+
{"version":3,"file":"scrollable-row.js","sourceRoot":"","sources":["../../src/components/scrollable-row.tsx"],"names":[],"mappings":"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","sourcesContent":["import {ChevronLeft, ChevronRight} from 'lucide-react';\nimport React, {useEffect, useRef, useState} from 'react';\nimport {cn} from '../lib/utils';\n\ntype ScrollableRowProps = {\n children: React.ReactNode;\n className?: string;\n scrollClassName?: string;\n /**\n * Ref to the inner scrolling container. Replaces the internal ref; the\n * arrows keep working.\n */\n scrollRef?: React.RefObject<HTMLDivElement>;\n /** Pixels scrolled per arrow activation. Defaults to 200. */\n scrollAmount?: number;\n arrowVisibility?: 'hover' | 'always';\n arrowClassName?: string;\n arrowIconClassName?: string;\n} & Omit<React.ComponentPropsWithoutRef<'div'>, 'children' | 'className'>;\n\n/**\n * A horizontally scrolling row with arrows that appear only where there is more\n * content.\n *\n * The forwarded ref and `className` target the outer wrapper (so `Slot` or\n * drop-target wrapping works); `scrollRef` targets the inner scrolling\n * container.\n */\nexport const ScrollableRow = React.forwardRef<\n HTMLDivElement,\n ScrollableRowProps\n>(function ScrollableRow(\n {\n children,\n className,\n scrollClassName,\n scrollRef,\n scrollAmount = 200,\n arrowVisibility = 'hover',\n arrowClassName,\n arrowIconClassName,\n ...rest\n },\n forwardedRef,\n) {\n const internalRef = useRef<HTMLDivElement>(null);\n const containerRef = scrollRef ?? internalRef;\n const [canScrollLeft, setCanScrollLeft] = useState(false);\n const [canScrollRight, setCanScrollRight] = useState(false);\n\n const scrollBy = (direction: 'left' | 'right') => {\n const container = containerRef.current;\n if (!container) return;\n\n container.scrollBy({\n left: direction === 'left' ? -scrollAmount : scrollAmount,\n behavior: 'smooth',\n });\n };\n\n useEffect(() => {\n const updateScrollState = () => {\n const container = containerRef.current;\n if (!container) return;\n\n const {scrollLeft, scrollWidth, clientWidth} = container;\n setCanScrollLeft(scrollLeft > 0);\n setCanScrollRight(scrollLeft + clientWidth < scrollWidth - 1);\n };\n\n const container = containerRef.current;\n if (!container) return;\n\n updateScrollState();\n\n container.addEventListener('scroll', updateScrollState);\n\n const resizeObserver = new ResizeObserver(updateScrollState);\n resizeObserver.observe(container);\n\n // The container's own box stays fixed when its content changes size (e.g.\n // loading placeholders replaced by wider suggestions), so ResizeObserver\n // on the container alone would leave the arrows stale. Observe each child\n // element instead, and re-observe as children are added or removed, so\n // overflow changes track content-size changes too.\n const contentObserver = new ResizeObserver(updateScrollState);\n const observeChildren = () => {\n contentObserver.disconnect();\n for (const child of Array.from(container.children)) {\n contentObserver.observe(child);\n }\n };\n observeChildren();\n\n const mutationObserver = new MutationObserver((mutations) => {\n // Only direct child additions/removals change the resize-observer targets.\n if (\n mutations.some(\n ({type, target}) => type === 'childList' && target === container,\n )\n ) {\n observeChildren();\n }\n updateScrollState();\n });\n // Text and styling can change overflow without resizing a direct child.\n // Observe all attributes: hidden and arbitrary CSS selectors affect layout too.\n mutationObserver.observe(container, {\n childList: true,\n characterData: true,\n attributes: true,\n subtree: true,\n });\n\n // Font metrics can change direct-text overflow without a DOM mutation or\n // an element resize. Refresh after font loading settles, including failures.\n const fonts = container.ownerDocument.fonts;\n fonts?.addEventListener('loadingdone', updateScrollState);\n fonts?.addEventListener('loadingerror', updateScrollState);\n\n return () => {\n container.removeEventListener('scroll', updateScrollState);\n resizeObserver.disconnect();\n contentObserver.disconnect();\n mutationObserver.disconnect();\n fonts?.removeEventListener('loadingdone', updateScrollState);\n fonts?.removeEventListener('loadingerror', updateScrollState);\n };\n // `children` is intentionally excluded from the deps: it is typically a\n // fresh array on every parent render, and re-running this effect (which\n // calls setState) on every render would trip React's \"Maximum update depth\n // exceeded\" warning. DOM observers and font events handle content changes;\n // the scroll listener covers user/dnd scrolling.\n }, [containerRef]);\n\n const arrowBaseClass = cn(\n 'absolute top-0 z-10 flex h-full w-8 items-center backdrop-blur-md bg-background/50 transition-colors',\n arrowVisibility === 'hover'\n ? 'opacity-0 transition-opacity hover:opacity-100'\n : 'opacity-100',\n arrowClassName,\n );\n\n return (\n <div ref={forwardedRef} className={cn('relative', className)} {...rest}>\n <button\n type=\"button\"\n onClick={() => scrollBy('left')}\n disabled={!canScrollLeft}\n className={cn(\n arrowBaseClass,\n 'left-0 justify-start pl-1',\n 'from-background/90 via-background/60 group bg-gradient-to-r to-transparent',\n !canScrollLeft && 'pointer-events-none opacity-0',\n )}\n aria-label=\"Scroll left\"\n title=\"Scroll left\"\n >\n <ChevronLeft\n className={cn(\n 'text-muted-foreground group-hover:text-foreground h-5 w-5 transition-colors',\n arrowIconClassName,\n )}\n />\n </button>\n\n <div ref={containerRef} className={scrollClassName}>\n {children}\n </div>\n\n <button\n type=\"button\"\n onClick={() => scrollBy('right')}\n disabled={!canScrollRight}\n className={cn(\n arrowBaseClass,\n 'right-0 justify-end pr-1',\n 'from-background/90 via-background/60 group bg-gradient-to-l to-transparent',\n !canScrollRight && 'pointer-events-none opacity-0',\n )}\n aria-label=\"Scroll right\"\n title=\"Scroll right\"\n >\n <ChevronRight\n className={cn(\n 'text-muted-foreground group-hover:text-foreground h-5 w-5 transition-colors',\n arrowIconClassName,\n )}\n />\n </button>\n </div>\n );\n});\n\nScrollableRow.displayName = 'ScrollableRow';\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sqlrooms/ui",
|
|
3
|
-
"version": "0.29.0-rc.
|
|
3
|
+
"version": "0.29.0-rc.13",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/sqlrooms/sqlrooms.git"
|
|
@@ -78,5 +78,5 @@
|
|
|
78
78
|
"publishConfig": {
|
|
79
79
|
"access": "public"
|
|
80
80
|
},
|
|
81
|
-
"gitHead": "
|
|
81
|
+
"gitHead": "d183fe9164e5f67c23b358328ca03dbf77a035a4"
|
|
82
82
|
}
|