@sqlrooms/ui 0.29.0-rc.8 → 0.29.0-rc.9

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.
@@ -6,6 +6,8 @@ declare const ProgressModal: FC<{
6
6
  loadingStage?: string;
7
7
  progress?: number;
8
8
  indeterminate?: boolean;
9
+ error?: string;
10
+ errorDetails?: string;
9
11
  }>;
10
12
  export { ProgressModal };
11
13
  //# sourceMappingURL=progress-modal.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"progress-modal.d.ts","sourceRoot":"","sources":["../../src/components/progress-modal.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAC,EAAE,EAAC,MAAM,OAAO,CAAC;AAWzB,QAAA,MAAM,aAAa,EAAE,EAAE,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CA6BA,CAAC;AAEF,OAAO,EAAC,aAAa,EAAC,CAAC"}
1
+ {"version":3,"file":"progress-modal.d.ts","sourceRoot":"","sources":["../../src/components/progress-modal.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAC,EAAE,EAAC,MAAM,OAAO,CAAC;AAWzB,QAAA,MAAM,aAAa,EAAE,EAAE,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAwDA,CAAC;AAEF,OAAO,EAAC,aAAa,EAAC,CAAC"}
@@ -4,8 +4,8 @@ import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, }
4
4
  import { Progress } from './progress';
5
5
  import { cn } from '../lib/utils';
6
6
  const ProgressModal = (props) => {
7
- const { className, isOpen, title, loadingStage, progress, indeterminate } = props;
8
- return (_jsx(Dialog, { open: isOpen, onOpenChange: () => { }, children: _jsxs(DialogContent, { className: cn('rounded-md border-none focus:outline-hidden sm:max-w-[425px]', className), showCloseButton: false, children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: title ?? '' }) }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Progress, { value: progress, className: "h-2 w-full", indeterminate: indeterminate }), _jsxs(DialogDescription, { className: "text-muted-foreground flex justify-between text-sm", children: [_jsx("span", { className: "text-sm", children: loadingStage ?? '' }), progress ? _jsxs("span", { children: [progress, "%"] }) : null] })] })] }) }));
7
+ const { className, isOpen, title, loadingStage, progress, indeterminate, error, errorDetails, } = props;
8
+ return (_jsx(Dialog, { open: isOpen, onOpenChange: () => { }, children: _jsxs(DialogContent, { className: cn('rounded-md border-none focus:outline-hidden sm:max-w-[425px]', className), showCloseButton: false, children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: title ?? '' }) }), error ? (_jsxs("div", { className: "flex flex-col gap-3", children: [_jsx(DialogDescription, { className: "text-destructive text-sm font-medium", children: loadingStage ?? 'Initialization failed' }), _jsx("p", { className: "text-muted-foreground text-sm", children: error }), errorDetails ? (_jsxs("details", { className: "text-muted-foreground text-sm", children: [_jsx("summary", { className: "hover:text-foreground cursor-pointer font-medium", children: "Details" }), _jsx("pre", { className: "bg-muted text-muted-foreground mt-2 max-h-40 overflow-auto rounded-md p-3 text-xs whitespace-pre-wrap", children: errorDetails })] })) : null] })) : (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Progress, { value: progress, className: "h-2 w-full", indeterminate: indeterminate }), _jsxs(DialogDescription, { className: "text-muted-foreground flex justify-between text-sm", children: [_jsx("span", { className: "text-sm", children: loadingStage ?? '' }), progress !== undefined ? _jsxs("span", { children: [progress, "%"] }) : null] })] }))] }) }));
9
9
  };
10
10
  export { ProgressModal };
11
11
  //# sourceMappingURL=progress-modal.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"progress-modal.js","sourceRoot":"","sources":["../../src/components/progress-modal.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAGb,OAAO,EACL,MAAM,EACN,aAAa,EACb,iBAAiB,EACjB,YAAY,EACZ,WAAW,GACZ,MAAM,UAAU,CAAC;AAClB,OAAO,EAAC,QAAQ,EAAC,MAAM,YAAY,CAAC;AACpC,OAAO,EAAC,EAAE,EAAC,MAAM,cAAc,CAAC;AAEhC,MAAM,aAAa,GAOd,CAAC,KAAK,EAAE,EAAE;IACb,MAAM,EAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,aAAa,EAAC,GACrE,KAAK,CAAC;IACR,OAAO,CACL,KAAC,MAAM,IAAC,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,GAAG,EAAE,GAAE,CAAC,YAC1C,MAAC,aAAa,IACZ,SAAS,EAAE,EAAE,CACX,8DAA8D,EAC9D,SAAS,CACV,EACD,eAAe,EAAE,KAAK,aAEtB,KAAC,YAAY,cACX,KAAC,WAAW,cAAE,KAAK,IAAI,EAAE,GAAe,GAC3B,EACf,eAAK,SAAS,EAAC,qBAAqB,aAClC,KAAC,QAAQ,IACP,KAAK,EAAE,QAAQ,EACf,SAAS,EAAC,YAAY,EACtB,aAAa,EAAE,aAAa,GAC5B,EACF,MAAC,iBAAiB,IAAC,SAAS,EAAC,oDAAoD,aAC/E,eAAM,SAAS,EAAC,SAAS,YAAE,YAAY,IAAI,EAAE,GAAQ,EACpD,QAAQ,CAAC,CAAC,CAAC,2BAAO,QAAQ,SAAS,CAAC,CAAC,CAAC,IAAI,IACzB,IAChB,IACQ,GACT,CACV,CAAC;AACJ,CAAC,CAAC;AAEF,OAAO,EAAC,aAAa,EAAC,CAAC","sourcesContent":["'use client';\n\nimport {FC} from 'react';\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n} from './dialog';\nimport {Progress} from './progress';\nimport {cn} from '../lib/utils';\n\nconst ProgressModal: FC<{\n className?: string;\n isOpen: boolean;\n title?: string;\n loadingStage?: string;\n progress?: number;\n indeterminate?: boolean;\n}> = (props) => {\n const {className, isOpen, title, loadingStage, progress, indeterminate} =\n props;\n return (\n <Dialog open={isOpen} onOpenChange={() => {}}>\n <DialogContent\n className={cn(\n 'rounded-md border-none focus:outline-hidden sm:max-w-[425px]',\n className,\n )}\n showCloseButton={false}\n >\n <DialogHeader>\n <DialogTitle>{title ?? ''}</DialogTitle>\n </DialogHeader>\n <div className=\"flex flex-col gap-2\">\n <Progress\n value={progress}\n className=\"h-2 w-full\"\n indeterminate={indeterminate}\n />\n <DialogDescription className=\"text-muted-foreground flex justify-between text-sm\">\n <span className=\"text-sm\">{loadingStage ?? ''}</span>\n {progress ? <span>{progress}%</span> : null}\n </DialogDescription>\n </div>\n </DialogContent>\n </Dialog>\n );\n};\n\nexport {ProgressModal};\n"]}
1
+ {"version":3,"file":"progress-modal.js","sourceRoot":"","sources":["../../src/components/progress-modal.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAGb,OAAO,EACL,MAAM,EACN,aAAa,EACb,iBAAiB,EACjB,YAAY,EACZ,WAAW,GACZ,MAAM,UAAU,CAAC;AAClB,OAAO,EAAC,QAAQ,EAAC,MAAM,YAAY,CAAC;AACpC,OAAO,EAAC,EAAE,EAAC,MAAM,cAAc,CAAC;AAEhC,MAAM,aAAa,GASd,CAAC,KAAK,EAAE,EAAE;IACb,MAAM,EACJ,SAAS,EACT,MAAM,EACN,KAAK,EACL,YAAY,EACZ,QAAQ,EACR,aAAa,EACb,KAAK,EACL,YAAY,GACb,GAAG,KAAK,CAAC;IACV,OAAO,CACL,KAAC,MAAM,IAAC,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,GAAG,EAAE,GAAE,CAAC,YAC1C,MAAC,aAAa,IACZ,SAAS,EAAE,EAAE,CACX,8DAA8D,EAC9D,SAAS,CACV,EACD,eAAe,EAAE,KAAK,aAEtB,KAAC,YAAY,cACX,KAAC,WAAW,cAAE,KAAK,IAAI,EAAE,GAAe,GAC3B,EACd,KAAK,CAAC,CAAC,CAAC,CACP,eAAK,SAAS,EAAC,qBAAqB,aAClC,KAAC,iBAAiB,IAAC,SAAS,EAAC,sCAAsC,YAChE,YAAY,IAAI,uBAAuB,GACtB,EACpB,YAAG,SAAS,EAAC,+BAA+B,YAAE,KAAK,GAAK,EACvD,YAAY,CAAC,CAAC,CAAC,CACd,mBAAS,SAAS,EAAC,+BAA+B,aAChD,kBAAS,SAAS,EAAC,kDAAkD,wBAE3D,EACV,cAAK,SAAS,EAAC,uGAAuG,YACnH,YAAY,GACT,IACE,CACX,CAAC,CAAC,CAAC,IAAI,IACJ,CACP,CAAC,CAAC,CAAC,CACF,eAAK,SAAS,EAAC,qBAAqB,aAClC,KAAC,QAAQ,IACP,KAAK,EAAE,QAAQ,EACf,SAAS,EAAC,YAAY,EACtB,aAAa,EAAE,aAAa,GAC5B,EACF,MAAC,iBAAiB,IAAC,SAAS,EAAC,oDAAoD,aAC/E,eAAM,SAAS,EAAC,SAAS,YAAE,YAAY,IAAI,EAAE,GAAQ,EACpD,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,2BAAO,QAAQ,SAAS,CAAC,CAAC,CAAC,IAAI,IACvC,IAChB,CACP,IACa,GACT,CACV,CAAC;AACJ,CAAC,CAAC;AAEF,OAAO,EAAC,aAAa,EAAC,CAAC","sourcesContent":["'use client';\n\nimport {FC} from 'react';\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n} from './dialog';\nimport {Progress} from './progress';\nimport {cn} from '../lib/utils';\n\nconst ProgressModal: FC<{\n className?: string;\n isOpen: boolean;\n title?: string;\n loadingStage?: string;\n progress?: number;\n indeterminate?: boolean;\n error?: string;\n errorDetails?: string;\n}> = (props) => {\n const {\n className,\n isOpen,\n title,\n loadingStage,\n progress,\n indeterminate,\n error,\n errorDetails,\n } = props;\n return (\n <Dialog open={isOpen} onOpenChange={() => {}}>\n <DialogContent\n className={cn(\n 'rounded-md border-none focus:outline-hidden sm:max-w-[425px]',\n className,\n )}\n showCloseButton={false}\n >\n <DialogHeader>\n <DialogTitle>{title ?? ''}</DialogTitle>\n </DialogHeader>\n {error ? (\n <div className=\"flex flex-col gap-3\">\n <DialogDescription className=\"text-destructive text-sm font-medium\">\n {loadingStage ?? 'Initialization failed'}\n </DialogDescription>\n <p className=\"text-muted-foreground text-sm\">{error}</p>\n {errorDetails ? (\n <details className=\"text-muted-foreground text-sm\">\n <summary className=\"hover:text-foreground cursor-pointer font-medium\">\n Details\n </summary>\n <pre className=\"bg-muted text-muted-foreground mt-2 max-h-40 overflow-auto rounded-md p-3 text-xs whitespace-pre-wrap\">\n {errorDetails}\n </pre>\n </details>\n ) : null}\n </div>\n ) : (\n <div className=\"flex flex-col gap-2\">\n <Progress\n value={progress}\n className=\"h-2 w-full\"\n indeterminate={indeterminate}\n />\n <DialogDescription className=\"text-muted-foreground flex justify-between text-sm\">\n <span className=\"text-sm\">{loadingStage ?? ''}</span>\n {progress !== undefined ? <span>{progress}%</span> : null}\n </DialogDescription>\n </div>\n )}\n </DialogContent>\n </Dialog>\n );\n};\n\nexport {ProgressModal};\n"]}
@@ -2,6 +2,9 @@ import * as React from 'react';
2
2
  type TextareaProps = React.ComponentProps<'textarea'> & {
3
3
  autoResize?: boolean;
4
4
  };
5
+ /**
6
+ * Textarea component with optional auto-resize behavior.
7
+ */
5
8
  declare const Textarea: React.ForwardRefExoticComponent<Omit<TextareaProps, "ref"> & React.RefAttributes<HTMLTextAreaElement>>;
6
9
  export { Textarea };
7
10
  //# sourceMappingURL=textarea.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"textarea.d.ts","sourceRoot":"","sources":["../../src/components/textarea.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,KAAK,aAAa,GAAG,KAAK,CAAC,cAAc,CAAC,UAAU,CAAC,GAAG;IACtD,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF,QAAA,MAAM,QAAQ,wGAqEb,CAAC;AAGF,OAAO,EAAC,QAAQ,EAAC,CAAC"}
1
+ {"version":3,"file":"textarea.d.ts","sourceRoot":"","sources":["../../src/components/textarea.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,KAAK,aAAa,GAAG,KAAK,CAAC,cAAc,CAAC,UAAU,CAAC,GAAG;IACtD,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAmHF;;GAEG;AACH,QAAA,MAAM,QAAQ,wGAoDb,CAAC;AAGF,OAAO,EAAC,QAAQ,EAAC,CAAC"}
@@ -1,17 +1,26 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import * as React from 'react';
3
3
  import { cn } from '../lib/utils';
4
- const Textarea = React.forwardRef(({ className, autoResize = false, onInput, value, defaultValue, rows, ...props }, ref) => {
5
- const localRef = React.useRef(null);
6
- React.useImperativeHandle(ref, () => localRef.current);
4
+ /**
5
+ * Uses layout effect in the browser and falls back to effect during SSR.
6
+ */
7
+ const useIsomorphicLayoutEffect = typeof window === 'undefined' ? React.useEffect : React.useLayoutEffect;
8
+ /**
9
+ * Keeps a textarea height synchronized with its content and container width.
10
+ *
11
+ * The resize path is intentionally triggered from multiple sources (input,
12
+ * value changes, and width changes) and then deduped per frame.
13
+ */
14
+ function useAutoResizeTextarea({ autoResize, textareaRef, value, defaultValue, }) {
7
15
  const [hasOverflow, setHasOverflow] = React.useState(false);
8
- const resizeToFitContent = React.useCallback(() => {
9
- const el = localRef.current;
10
- if (!el || !autoResize)
16
+ const lastMeasuredWidthRef = React.useRef(0);
17
+ const resizeFrameRef = React.useRef(null);
18
+ const applyResizeToFitContent = React.useCallback(() => {
19
+ const el = textareaRef.current;
20
+ if (!el || !autoResize || typeof window === 'undefined')
11
21
  return;
12
22
  el.style.height = 'auto';
13
23
  el.style.height = `${el.scrollHeight}px`;
14
- // Check if content exceeds the max-height constraint
15
24
  const computedStyle = window.getComputedStyle(el);
16
25
  const maxHeight = computedStyle.maxHeight;
17
26
  if (maxHeight && maxHeight !== 'none') {
@@ -21,11 +30,78 @@ const Textarea = React.forwardRef(({ className, autoResize = false, onInput, val
21
30
  else {
22
31
  setHasOverflow(false);
23
32
  }
24
- }, [autoResize]);
25
- React.useEffect(() => {
26
- // Trigger on mount and whenever the controlled value changes
33
+ }, [autoResize, textareaRef]);
34
+ const resizeToFitContent = React.useCallback(() => {
35
+ if (!autoResize || typeof window === 'undefined')
36
+ return;
37
+ // Keep input/effect-triggered resize paths for robustness, but collapse
38
+ // same-frame calls into one DOM measurement/write cycle.
39
+ if (resizeFrameRef.current !== null)
40
+ return;
41
+ resizeFrameRef.current = window.requestAnimationFrame(() => {
42
+ resizeFrameRef.current = null;
43
+ applyResizeToFitContent();
44
+ });
45
+ }, [autoResize, applyResizeToFitContent]);
46
+ useIsomorphicLayoutEffect(() => {
27
47
  resizeToFitContent();
28
48
  }, [resizeToFitContent, value, defaultValue]);
49
+ React.useEffect(() => {
50
+ if (!autoResize)
51
+ return;
52
+ const el = textareaRef.current;
53
+ if (!el || typeof window === 'undefined')
54
+ return;
55
+ // Re-measure after layout settles so hidden/collapsed mount states
56
+ // don't leave a stale oversized height.
57
+ resizeToFitContent();
58
+ if (typeof ResizeObserver === 'undefined') {
59
+ return;
60
+ }
61
+ lastMeasuredWidthRef.current = el.getBoundingClientRect().width;
62
+ const observer = new ResizeObserver((entries) => {
63
+ const entry = entries[0];
64
+ if (!entry)
65
+ return;
66
+ const nextWidth = entry.contentRect.width;
67
+ if (Math.abs(nextWidth - lastMeasuredWidthRef.current) < 1) {
68
+ return;
69
+ }
70
+ lastMeasuredWidthRef.current = nextWidth;
71
+ resizeToFitContent();
72
+ });
73
+ observer.observe(el);
74
+ return () => {
75
+ observer.disconnect();
76
+ };
77
+ }, [autoResize, resizeToFitContent, textareaRef]);
78
+ React.useEffect(() => {
79
+ if (typeof window === 'undefined')
80
+ return;
81
+ return () => {
82
+ if (resizeFrameRef.current !== null) {
83
+ window.cancelAnimationFrame(resizeFrameRef.current);
84
+ resizeFrameRef.current = null;
85
+ }
86
+ };
87
+ }, []);
88
+ return {
89
+ hasOverflow,
90
+ resizeToFitContent,
91
+ };
92
+ }
93
+ /**
94
+ * Textarea component with optional auto-resize behavior.
95
+ */
96
+ const Textarea = React.forwardRef(({ className, autoResize = false, onInput, value, defaultValue, rows, ...props }, ref) => {
97
+ const localRef = React.useRef(null);
98
+ React.useImperativeHandle(ref, () => localRef.current);
99
+ const { hasOverflow, resizeToFitContent } = useAutoResizeTextarea({
100
+ autoResize,
101
+ textareaRef: localRef,
102
+ value,
103
+ defaultValue,
104
+ });
29
105
  const handleInput = (e) => {
30
106
  if (autoResize)
31
107
  resizeToFitContent();
@@ -1 +1 @@
1
- {"version":3,"file":"textarea.js","sourceRoot":"","sources":["../../src/components/textarea.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAC,EAAE,EAAC,MAAM,cAAc,CAAC;AAMhC,MAAM,QAAQ,GAAG,KAAK,CAAC,UAAU,CAC/B,CACE,EACE,SAAS,EACT,UAAU,GAAG,KAAK,EAClB,OAAO,EACP,KAAK,EACL,YAAY,EACZ,IAAI,EACJ,GAAG,KAAK,EACT,EACD,GAAG,EACH,EAAE;IACF,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAsB,IAAI,CAAC,CAAC;IAEzD,KAAK,CAAC,mBAAmB,CACvB,GAAG,EACH,GAAG,EAAE,CAAC,QAAQ,CAAC,OAA8B,CAC9C,CAAC;IAEF,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAE5D,MAAM,kBAAkB,GAAG,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE;QAChD,MAAM,EAAE,GAAG,QAAQ,CAAC,OAAO,CAAC;QAC5B,IAAI,CAAC,EAAE,IAAI,CAAC,UAAU;YAAE,OAAO;QAC/B,EAAE,CAAC,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC;QACzB,EAAE,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,IAAI,CAAC;QAEzC,qDAAqD;QACrD,MAAM,aAAa,GAAG,MAAM,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC;QAClD,MAAM,SAAS,GAAG,aAAa,CAAC,SAAS,CAAC;QAC1C,IAAI,SAAS,IAAI,SAAS,KAAK,MAAM,EAAE,CAAC;YACtC,MAAM,cAAc,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC;YAC7C,cAAc,CAAC,EAAE,CAAC,YAAY,GAAG,cAAc,CAAC,CAAC;QACnD,CAAC;aAAM,CAAC;YACN,cAAc,CAAC,KAAK,CAAC,CAAC;QACxB,CAAC;IACH,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;IAEjB,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,6DAA6D;QAC7D,kBAAkB,EAAE,CAAC;IACvB,CAAC,EAAE,CAAC,kBAAkB,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC;IAE9C,MAAM,WAAW,GAAG,CAAC,CAAuC,EAAE,EAAE;QAC9D,IAAI,UAAU;YAAE,kBAAkB,EAAE,CAAC;QACrC,IAAI,OAAO;YAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IAC1B,CAAC,CAAC;IAEF,OAAO,CACL,mBACE,SAAS,EAAE,EAAE,CACX,6QAA6Q,EAC7Q,UAAU;YACR,CAAC,CAAC,WAAW;gBACX,CAAC,CAAC,iBAAiB;gBACnB,CAAC,CAAC,mBAAmB;YACvB,CAAC,CAAC,SAAS,EACb,SAAS,CACV,EACD,IAAI,EAAE,IAAI,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,EAC1C,GAAG,EAAE,QAAQ,EACb,OAAO,EAAE,WAAW,EACpB,KAAK,EAAE,KAAK,EACZ,YAAY,EAAE,YAAY,KACtB,KAAK,GACT,CACH,CAAC;AACJ,CAAC,CACF,CAAC;AACF,QAAQ,CAAC,WAAW,GAAG,UAAU,CAAC;AAElC,OAAO,EAAC,QAAQ,EAAC,CAAC","sourcesContent":["import * as React from 'react';\nimport {cn} from '../lib/utils';\n\ntype TextareaProps = React.ComponentProps<'textarea'> & {\n autoResize?: boolean;\n};\n\nconst Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(\n (\n {\n className,\n autoResize = false,\n onInput,\n value,\n defaultValue,\n rows,\n ...props\n },\n ref,\n ) => {\n const localRef = React.useRef<HTMLTextAreaElement>(null);\n\n React.useImperativeHandle(\n ref,\n () => localRef.current as HTMLTextAreaElement,\n );\n\n const [hasOverflow, setHasOverflow] = React.useState(false);\n\n const resizeToFitContent = React.useCallback(() => {\n const el = localRef.current;\n if (!el || !autoResize) return;\n el.style.height = 'auto';\n el.style.height = `${el.scrollHeight}px`;\n\n // Check if content exceeds the max-height constraint\n const computedStyle = window.getComputedStyle(el);\n const maxHeight = computedStyle.maxHeight;\n if (maxHeight && maxHeight !== 'none') {\n const maxHeightValue = parseFloat(maxHeight);\n setHasOverflow(el.scrollHeight > maxHeightValue);\n } else {\n setHasOverflow(false);\n }\n }, [autoResize]);\n\n React.useEffect(() => {\n // Trigger on mount and whenever the controlled value changes\n resizeToFitContent();\n }, [resizeToFitContent, value, defaultValue]);\n\n const handleInput = (e: React.FormEvent<HTMLTextAreaElement>) => {\n if (autoResize) resizeToFitContent();\n if (onInput) onInput(e);\n };\n\n return (\n <textarea\n className={cn(\n 'border-input placeholder:text-muted-foreground focus-visible:ring-ring flex min-h-[60px] w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-sm focus-visible:ring-1 focus-visible:outline-hidden disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',\n autoResize\n ? hasOverflow\n ? 'overflow-y-auto'\n : 'overflow-y-hidden'\n : undefined,\n className,\n )}\n rows={rows ?? (autoResize ? 1 : undefined)}\n ref={localRef}\n onInput={handleInput}\n value={value}\n defaultValue={defaultValue}\n {...props}\n />\n );\n },\n);\nTextarea.displayName = 'Textarea';\n\nexport {Textarea};\n"]}
1
+ {"version":3,"file":"textarea.js","sourceRoot":"","sources":["../../src/components/textarea.tsx"],"names":[],"mappings":"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","sourcesContent":["import * as React from 'react';\nimport {cn} from '../lib/utils';\n\ntype TextareaProps = React.ComponentProps<'textarea'> & {\n autoResize?: boolean;\n};\n\n/**\n * Uses layout effect in the browser and falls back to effect during SSR.\n */\nconst useIsomorphicLayoutEffect =\n typeof window === 'undefined' ? React.useEffect : React.useLayoutEffect;\n\n/**\n * Keeps a textarea height synchronized with its content and container width.\n *\n * The resize path is intentionally triggered from multiple sources (input,\n * value changes, and width changes) and then deduped per frame.\n */\nfunction useAutoResizeTextarea({\n autoResize,\n textareaRef,\n value,\n defaultValue,\n}: {\n autoResize: boolean;\n textareaRef: React.RefObject<HTMLTextAreaElement | null>;\n value: React.ComponentProps<'textarea'>['value'];\n defaultValue: React.ComponentProps<'textarea'>['defaultValue'];\n}) {\n const [hasOverflow, setHasOverflow] = React.useState(false);\n const lastMeasuredWidthRef = React.useRef<number>(0);\n const resizeFrameRef = React.useRef<number | null>(null);\n\n const applyResizeToFitContent = React.useCallback(() => {\n const el = textareaRef.current;\n if (!el || !autoResize || typeof window === 'undefined') return;\n\n el.style.height = 'auto';\n el.style.height = `${el.scrollHeight}px`;\n\n const computedStyle = window.getComputedStyle(el);\n const maxHeight = computedStyle.maxHeight;\n if (maxHeight && maxHeight !== 'none') {\n const maxHeightValue = parseFloat(maxHeight);\n setHasOverflow(el.scrollHeight > maxHeightValue);\n } else {\n setHasOverflow(false);\n }\n }, [autoResize, textareaRef]);\n\n const resizeToFitContent = React.useCallback(() => {\n if (!autoResize || typeof window === 'undefined') return;\n\n // Keep input/effect-triggered resize paths for robustness, but collapse\n // same-frame calls into one DOM measurement/write cycle.\n if (resizeFrameRef.current !== null) return;\n\n resizeFrameRef.current = window.requestAnimationFrame(() => {\n resizeFrameRef.current = null;\n applyResizeToFitContent();\n });\n }, [autoResize, applyResizeToFitContent]);\n\n useIsomorphicLayoutEffect(() => {\n resizeToFitContent();\n }, [resizeToFitContent, value, defaultValue]);\n\n React.useEffect(() => {\n if (!autoResize) return;\n\n const el = textareaRef.current;\n if (!el || typeof window === 'undefined') return;\n\n // Re-measure after layout settles so hidden/collapsed mount states\n // don't leave a stale oversized height.\n resizeToFitContent();\n\n if (typeof ResizeObserver === 'undefined') {\n return;\n }\n\n lastMeasuredWidthRef.current = el.getBoundingClientRect().width;\n const observer = new ResizeObserver((entries) => {\n const entry = entries[0];\n if (!entry) return;\n\n const nextWidth = entry.contentRect.width;\n if (Math.abs(nextWidth - lastMeasuredWidthRef.current) < 1) {\n return;\n }\n\n lastMeasuredWidthRef.current = nextWidth;\n resizeToFitContent();\n });\n\n observer.observe(el);\n\n return () => {\n observer.disconnect();\n };\n }, [autoResize, resizeToFitContent, textareaRef]);\n\n React.useEffect(() => {\n if (typeof window === 'undefined') return;\n\n return () => {\n if (resizeFrameRef.current !== null) {\n window.cancelAnimationFrame(resizeFrameRef.current);\n resizeFrameRef.current = null;\n }\n };\n }, []);\n\n return {\n hasOverflow,\n resizeToFitContent,\n };\n}\n\n/**\n * Textarea component with optional auto-resize behavior.\n */\nconst Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(\n (\n {\n className,\n autoResize = false,\n onInput,\n value,\n defaultValue,\n rows,\n ...props\n },\n ref,\n ) => {\n const localRef = React.useRef<HTMLTextAreaElement>(null);\n\n React.useImperativeHandle(\n ref,\n () => localRef.current as HTMLTextAreaElement,\n );\n\n const {hasOverflow, resizeToFitContent} = useAutoResizeTextarea({\n autoResize,\n textareaRef: localRef,\n value,\n defaultValue,\n });\n\n const handleInput = (e: React.FormEvent<HTMLTextAreaElement>) => {\n if (autoResize) resizeToFitContent();\n if (onInput) onInput(e);\n };\n\n return (\n <textarea\n className={cn(\n 'border-input placeholder:text-muted-foreground focus-visible:ring-ring flex min-h-[60px] w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-sm focus-visible:ring-1 focus-visible:outline-hidden disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',\n autoResize\n ? hasOverflow\n ? 'overflow-y-auto'\n : 'overflow-y-hidden'\n : undefined,\n className,\n )}\n rows={rows ?? (autoResize ? 1 : undefined)}\n ref={localRef}\n onInput={handleInput}\n value={value}\n defaultValue={defaultValue}\n {...props}\n />\n );\n },\n);\nTextarea.displayName = 'Textarea';\n\nexport {Textarea};\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sqlrooms/ui",
3
- "version": "0.29.0-rc.8",
3
+ "version": "0.29.0-rc.9",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/sqlrooms/sqlrooms.git"
@@ -71,5 +71,5 @@
71
71
  "publishConfig": {
72
72
  "access": "public"
73
73
  },
74
- "gitHead": "dcb1a51631c5fa87b49d3e78ae6c0023eccd4189"
74
+ "gitHead": "4a477560c0a6509ca00076d74eb3ce8f581c9e4f"
75
75
  }