@sqlrooms/schema-tree 0.27.0 → 0.28.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/README.md CHANGED
@@ -1,19 +1,58 @@
1
- This package is part of the SQLRooms framework.
1
+ React components for rendering DuckDB database/schema/table/column trees in SQLRooms apps.
2
2
 
3
- # DuckDB schema tree
3
+ ## Installation
4
4
 
5
- A React component library for rendering DuckDB schema trees in a hierarchical view. It provides components to display database schemas, tables, and columns in an interactive tree structure with features like:
5
+ ```bash
6
+ npm install @sqlrooms/schema-tree @sqlrooms/duckdb @sqlrooms/ui
7
+ ```
6
8
 
7
- - Expandable/collapsible tree nodes
8
- - Column type badges
9
- - Context menus for actions (e.g. copying column names)
10
- - Customizable node rendering
11
- - Hover states and visual feedback
9
+ ## What this package provides
12
10
 
13
- The main components are:
11
+ - `TableSchemaTree` for rendering full schema hierarchies
12
+ - node components (`DatabaseTreeNode`, `SchemaTreeNode`, `TableTreeNode`, `ColumnTreeNode`)
13
+ - `TreeNodeActionsMenu` for context-style node actions
14
+ - `defaultRenderTableSchemaNode` for quick customization
14
15
 
15
- - `TableSchemaTree`: The root tree component that renders the full schema hierarchy
16
- - `ColumnTreeNode`: Specialized node for displaying column information
17
- - `TreeNodeActionsMenu`: Reusable menu component for node actions
16
+ ## Basic usage
18
17
 
19
- This package is used by SQLRooms to provide schema browsing capabilities in the database explorer interface.
18
+ ```tsx
19
+ import {TableSchemaTree} from '@sqlrooms/schema-tree';
20
+ import {useRoomStore} from './store';
21
+
22
+ export function SchemaExplorer() {
23
+ const schemaTrees = useRoomStore((state) => state.db.schemaTrees ?? []);
24
+
25
+ if (!schemaTrees.length) {
26
+ return <div className="p-2 text-sm">No schema loaded yet.</div>;
27
+ }
28
+
29
+ return (
30
+ <TableSchemaTree
31
+ className="h-full"
32
+ schemaTrees={schemaTrees}
33
+ skipSingleDatabaseOrSchema
34
+ />
35
+ );
36
+ }
37
+ ```
38
+
39
+ ## Custom node rendering
40
+
41
+ ```tsx
42
+ import {DbSchemaNode} from '@sqlrooms/duckdb';
43
+ import {defaultRenderTableSchemaNode, TableSchemaTree} from '@sqlrooms/schema-tree';
44
+
45
+ const renderNode = (node: DbSchemaNode, isOpen: boolean) => (
46
+ <div className={isOpen ? 'opacity-100' : 'opacity-90'}>
47
+ {defaultRenderTableSchemaNode(node)}
48
+ </div>
49
+ );
50
+
51
+ <TableSchemaTree schemaTrees={schemaTrees} renderNode={renderNode} />;
52
+ ```
53
+
54
+ ## Notes
55
+
56
+ - `schemaTrees` comes from the DuckDB slice (`state.db.schemaTrees`).
57
+ - Call `state.db.refreshTableSchemas()` after table changes to keep the tree up to date.
58
+ - This package is used by SQLRooms SQL editor table-structure panels.
@@ -3,6 +3,6 @@ import { cn, Slot } from '@sqlrooms/ui';
3
3
  export function BaseTreeNode(props) {
4
4
  const { className, asChild, children } = props;
5
5
  const Comp = asChild ? Slot : 'div';
6
- return (_jsx(Comp, { className: cn('hover:bg-foreground/10 h-[22px] w-full flex-grow cursor-pointer select-none rounded-sm p-[1px]', className), children: _jsx("div", { className: cn('group relative flex w-full items-center overflow-hidden', className), children: _jsx("div", { className: "absolute flex h-full w-full items-center whitespace-nowrap", children: children }) }) }));
6
+ return (_jsx(Comp, { className: cn('hover:bg-foreground/10 h-[22px] w-full grow cursor-pointer rounded-sm p-px select-none', className), children: _jsx("div", { className: cn('group relative flex w-full items-center overflow-hidden', className), children: _jsx("div", { className: "absolute flex h-full w-full items-center whitespace-nowrap", children: children }) }) }));
7
7
  }
8
8
  //# sourceMappingURL=BaseTreeNode.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"BaseTreeNode.js","sourceRoot":"","sources":["../../src/nodes/BaseTreeNode.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAC,EAAE,EAAE,IAAI,EAAC,MAAM,cAAc,CAAC;AAGtC,MAAM,UAAU,YAAY,CAC1B,KAIE;IAEF,MAAM,EAAC,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAC,GAAG,KAAK,CAAC;IAC7C,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;IACpC,OAAO,CACL,KAAC,IAAI,IACH,SAAS,EAAE,EAAE,CACX,gGAAgG,EAChG,SAAS,CACV,YAED,cACE,SAAS,EAAE,EAAE,CACX,yDAAyD,EACzD,SAAS,CACV,YAED,cAAK,SAAS,EAAC,4DAA4D,YACxE,QAAQ,GACL,GACF,GACD,CACR,CAAC;AACJ,CAAC","sourcesContent":["import {cn, Slot} from '@sqlrooms/ui';\nimport {PropsWithChildren} from 'react';\n\nexport function BaseTreeNode<T>(\n props: PropsWithChildren<{\n className?: string;\n nodeObject: T;\n asChild?: boolean;\n }>,\n) {\n const {className, asChild, children} = props;\n const Comp = asChild ? Slot : 'div';\n return (\n <Comp\n className={cn(\n 'hover:bg-foreground/10 h-[22px] w-full flex-grow cursor-pointer select-none rounded-sm p-[1px]',\n className,\n )}\n >\n <div\n className={cn(\n 'group relative flex w-full items-center overflow-hidden',\n className,\n )}\n >\n <div className=\"absolute flex h-full w-full items-center whitespace-nowrap\">\n {children}\n </div>\n </div>\n </Comp>\n );\n}\n"]}
1
+ {"version":3,"file":"BaseTreeNode.js","sourceRoot":"","sources":["../../src/nodes/BaseTreeNode.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAC,EAAE,EAAE,IAAI,EAAC,MAAM,cAAc,CAAC;AAGtC,MAAM,UAAU,YAAY,CAC1B,KAIE;IAEF,MAAM,EAAC,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAC,GAAG,KAAK,CAAC;IAC7C,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;IACpC,OAAO,CACL,KAAC,IAAI,IACH,SAAS,EAAE,EAAE,CACX,wFAAwF,EACxF,SAAS,CACV,YAED,cACE,SAAS,EAAE,EAAE,CACX,yDAAyD,EACzD,SAAS,CACV,YAED,cAAK,SAAS,EAAC,4DAA4D,YACxE,QAAQ,GACL,GACF,GACD,CACR,CAAC;AACJ,CAAC","sourcesContent":["import {cn, Slot} from '@sqlrooms/ui';\nimport {PropsWithChildren} from 'react';\n\nexport function BaseTreeNode<T>(\n props: PropsWithChildren<{\n className?: string;\n nodeObject: T;\n asChild?: boolean;\n }>,\n) {\n const {className, asChild, children} = props;\n const Comp = asChild ? Slot : 'div';\n return (\n <Comp\n className={cn(\n 'hover:bg-foreground/10 h-[22px] w-full grow cursor-pointer rounded-sm p-px select-none',\n className,\n )}\n >\n <div\n className={cn(\n 'group relative flex w-full items-center overflow-hidden',\n className,\n )}\n >\n <div className=\"absolute flex h-full w-full items-center whitespace-nowrap\">\n {children}\n </div>\n </div>\n </Comp>\n );\n}\n"]}
@@ -15,7 +15,7 @@ import { forwardRef, } from 'react';
15
15
  */
16
16
  export const TreeNodeActionsMenu = (props) => {
17
17
  const { children } = props;
18
- return (_jsx("div", { className: "absolute right-0 top-[1px] h-full opacity-0 outline-none group-hover:opacity-100", children: _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx("div", { className: "bg-muted text-muted-foreground hover:text-foreground flex h-full w-7 items-center justify-center p-0 outline-none transition-colors", children: _jsx(EllipsisVerticalIcon, { size: "15px", className: "relative top-[-1px]" }) }) }), _jsx(DropdownMenuContent, { align: "start", side: "bottom", children: children })] }) }));
18
+ return (_jsx("div", { className: "absolute top-px right-0 h-full opacity-0 outline-hidden group-hover:opacity-100", children: _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx("div", { className: "bg-muted text-muted-foreground hover:text-foreground flex h-full w-7 items-center justify-center p-0 outline-hidden transition-colors", children: _jsx(EllipsisVerticalIcon, { size: "15px", className: "relative top-[-1px]" }) }) }), _jsx(DropdownMenuContent, { align: "start", side: "bottom", children: children })] }) }));
19
19
  };
20
20
  export const TreeNodeActionsMenuItem = forwardRef(({ className, ...props }, ref) => {
21
21
  const { onClick } = props;
@@ -1 +1 @@
1
- {"version":3,"file":"TreeNodeActionsMenu.js","sourceRoot":"","sources":["../../src/nodes/TreeNodeActionsMenu.tsx"],"names":[],"mappings":";AAAA,4DAA4D;AAE5D,OAAO,EACL,EAAE,EACF,YAAY,EACZ,mBAAmB,EACnB,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAC,oBAAoB,EAAC,MAAM,cAAc,CAAC;AAClD,OAAO,EAIL,UAAU,GAEX,MAAM,OAAO,CAAC;AAMf;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAiC,CAAC,KAAK,EAAE,EAAE;IACzE,MAAM,EAAC,QAAQ,EAAC,GAAG,KAAK,CAAC;IACzB,OAAO,CACL,cAAK,SAAS,EAAC,kFAAkF,YAC/F,MAAC,YAAY,eACX,KAAC,mBAAmB,IAAC,OAAO,kBAC1B,cAAK,SAAS,EAAC,qIAAqI,YAClJ,KAAC,oBAAoB,IAAC,IAAI,EAAC,MAAM,EAAC,SAAS,EAAC,qBAAqB,GAAG,GAChE,GACc,EACtB,KAAC,mBAAmB,IAAC,KAAK,EAAC,OAAO,EAAC,IAAI,EAAC,QAAQ,YAC7C,QAAQ,GACW,IACT,GACX,CACP,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,uBAAuB,GAAG,UAAU,CAG/C,CAAC,EAAC,SAAS,EAAE,GAAG,KAAK,EAAC,EAAE,GAAG,EAAE,EAAE;IAC/B,MAAM,EAAC,OAAO,EAAC,GAAG,KAAK,CAAC;IACxB,OAAO,CACL,KAAC,gBAAgB,IACf,GAAG,EAAE,GAAG,EACR,SAAS,EAAE,EAAE,CAAC,eAAe,EAAE,SAAS,CAAC,KACrC,KAAK,EACT,OAAO,EACL,OAAO;YACL,CAAC,CAAC,CAAC,GAAG,EAAE,EAAE;gBACN,GAAG,CAAC,eAAe,EAAE,CAAC,CAAC,qCAAqC;gBAC5D,OAAO,CAAC,GAAG,CAAC,CAAC;YACf,CAAC;YACH,CAAC,CAAC,SAAS,GAEf,CACH,CAAC;AACJ,CAAC,CAAC,CAAC;AACH,uBAAuB,CAAC,WAAW,GAAG,yBAAyB,CAAC","sourcesContent":["// Copyright 2022 Foursquare Labs, Inc. All Rights Reserved.\n\nimport {\n cn,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from '@sqlrooms/ui';\nimport {EllipsisVerticalIcon} from 'lucide-react';\nimport {\n ComponentPropsWithoutRef,\n ElementRef,\n FC,\n forwardRef,\n PropsWithChildren,\n} from 'react';\n\nexport type TreeNodeActionsMenuProps = PropsWithChildren<{\n className?: string;\n}>;\n\n/**\n * Component that renders a tree node \"more actions\" menu.\n * The menu items are passed as children.\n *\n * The menu is hidden by default and is shown when the user hovers over the node.\n * For this to work the parent element must have the `group` class.\n * It should also have classes `relative overflow-hidden`.\n *\n * @param children - The menu items.\n */\nexport const TreeNodeActionsMenu: FC<TreeNodeActionsMenuProps> = (props) => {\n const {children} = props;\n return (\n <div className=\"absolute right-0 top-[1px] h-full opacity-0 outline-none group-hover:opacity-100\">\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <div className=\"bg-muted text-muted-foreground hover:text-foreground flex h-full w-7 items-center justify-center p-0 outline-none transition-colors\">\n <EllipsisVerticalIcon size=\"15px\" className=\"relative top-[-1px]\" />\n </div>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\" side=\"bottom\">\n {children}\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n );\n};\n\nexport const TreeNodeActionsMenuItem = forwardRef<\n ElementRef<typeof DropdownMenuItem>,\n ComponentPropsWithoutRef<typeof DropdownMenuItem>\n>(({className, ...props}, ref) => {\n const {onClick} = props;\n return (\n <DropdownMenuItem\n ref={ref}\n className={cn('gap-2 text-xs', className)}\n {...props}\n onClick={\n onClick\n ? (evt) => {\n evt.stopPropagation(); // prevent the tree node from closing\n onClick(evt);\n }\n : undefined\n }\n />\n );\n});\nTreeNodeActionsMenuItem.displayName = 'TreeNodeActionsMenuItem';\n"]}
1
+ {"version":3,"file":"TreeNodeActionsMenu.js","sourceRoot":"","sources":["../../src/nodes/TreeNodeActionsMenu.tsx"],"names":[],"mappings":";AAAA,4DAA4D;AAE5D,OAAO,EACL,EAAE,EACF,YAAY,EACZ,mBAAmB,EACnB,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAC,oBAAoB,EAAC,MAAM,cAAc,CAAC;AAClD,OAAO,EAIL,UAAU,GAEX,MAAM,OAAO,CAAC;AAMf;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAiC,CAAC,KAAK,EAAE,EAAE;IACzE,MAAM,EAAC,QAAQ,EAAC,GAAG,KAAK,CAAC;IACzB,OAAO,CACL,cAAK,SAAS,EAAC,iFAAiF,YAC9F,MAAC,YAAY,eACX,KAAC,mBAAmB,IAAC,OAAO,kBAC1B,cAAK,SAAS,EAAC,uIAAuI,YACpJ,KAAC,oBAAoB,IAAC,IAAI,EAAC,MAAM,EAAC,SAAS,EAAC,qBAAqB,GAAG,GAChE,GACc,EACtB,KAAC,mBAAmB,IAAC,KAAK,EAAC,OAAO,EAAC,IAAI,EAAC,QAAQ,YAC7C,QAAQ,GACW,IACT,GACX,CACP,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,uBAAuB,GAAG,UAAU,CAG/C,CAAC,EAAC,SAAS,EAAE,GAAG,KAAK,EAAC,EAAE,GAAG,EAAE,EAAE;IAC/B,MAAM,EAAC,OAAO,EAAC,GAAG,KAAK,CAAC;IACxB,OAAO,CACL,KAAC,gBAAgB,IACf,GAAG,EAAE,GAAG,EACR,SAAS,EAAE,EAAE,CAAC,eAAe,EAAE,SAAS,CAAC,KACrC,KAAK,EACT,OAAO,EACL,OAAO;YACL,CAAC,CAAC,CAAC,GAAG,EAAE,EAAE;gBACN,GAAG,CAAC,eAAe,EAAE,CAAC,CAAC,qCAAqC;gBAC5D,OAAO,CAAC,GAAG,CAAC,CAAC;YACf,CAAC;YACH,CAAC,CAAC,SAAS,GAEf,CACH,CAAC;AACJ,CAAC,CAAC,CAAC;AACH,uBAAuB,CAAC,WAAW,GAAG,yBAAyB,CAAC","sourcesContent":["// Copyright 2022 Foursquare Labs, Inc. All Rights Reserved.\n\nimport {\n cn,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from '@sqlrooms/ui';\nimport {EllipsisVerticalIcon} from 'lucide-react';\nimport {\n ComponentPropsWithoutRef,\n ElementRef,\n FC,\n forwardRef,\n PropsWithChildren,\n} from 'react';\n\nexport type TreeNodeActionsMenuProps = PropsWithChildren<{\n className?: string;\n}>;\n\n/**\n * Component that renders a tree node \"more actions\" menu.\n * The menu items are passed as children.\n *\n * The menu is hidden by default and is shown when the user hovers over the node.\n * For this to work the parent element must have the `group` class.\n * It should also have classes `relative overflow-hidden`.\n *\n * @param children - The menu items.\n */\nexport const TreeNodeActionsMenu: FC<TreeNodeActionsMenuProps> = (props) => {\n const {children} = props;\n return (\n <div className=\"absolute top-px right-0 h-full opacity-0 outline-hidden group-hover:opacity-100\">\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <div className=\"bg-muted text-muted-foreground hover:text-foreground flex h-full w-7 items-center justify-center p-0 outline-hidden transition-colors\">\n <EllipsisVerticalIcon size=\"15px\" className=\"relative top-[-1px]\" />\n </div>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\" side=\"bottom\">\n {children}\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n );\n};\n\nexport const TreeNodeActionsMenuItem = forwardRef<\n ElementRef<typeof DropdownMenuItem>,\n ComponentPropsWithoutRef<typeof DropdownMenuItem>\n>(({className, ...props}, ref) => {\n const {onClick} = props;\n return (\n <DropdownMenuItem\n ref={ref}\n className={cn('gap-2 text-xs', className)}\n {...props}\n onClick={\n onClick\n ? (evt) => {\n evt.stopPropagation(); // prevent the tree node from closing\n onClick(evt);\n }\n : undefined\n }\n />\n );\n});\nTreeNodeActionsMenuItem.displayName = 'TreeNodeActionsMenuItem';\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sqlrooms/schema-tree",
3
- "version": "0.27.0",
3
+ "version": "0.28.0",
4
4
  "author": "SQLRooms Contributors",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -25,10 +25,10 @@
25
25
  "typedoc": "typedoc"
26
26
  },
27
27
  "dependencies": {
28
- "@sqlrooms/data-table": "0.27.0",
29
- "@sqlrooms/duckdb": "0.27.0",
30
- "@sqlrooms/ui": "0.27.0",
31
- "@sqlrooms/utils": "0.27.0",
28
+ "@sqlrooms/data-table": "0.28.0",
29
+ "@sqlrooms/duckdb": "0.28.0",
30
+ "@sqlrooms/ui": "0.28.0",
31
+ "@sqlrooms/utils": "0.28.0",
32
32
  "lucide-react": "^0.556.0"
33
33
  },
34
34
  "peerDependencies": {
@@ -39,5 +39,5 @@
39
39
  "@types/react": "^19.1.13",
40
40
  "@types/react-dom": "^19.1.9"
41
41
  },
42
- "gitHead": "f215995ab4adeac4c58171739261a15cbba9e82b"
42
+ "gitHead": "dcac54f8adf77240e293c93d224a0ce9fd8142a9"
43
43
  }