shadcn-treeview 0.1.0 → 1.0.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/index.cjs CHANGED
@@ -29,7 +29,23 @@ var React__namespace = /*#__PURE__*/_interopNamespace(React);
29
29
  function cn(...inputs) {
30
30
  return inputs.flat().filter((x) => typeof x === "string" && x.length > 0).join(" ");
31
31
  }
32
- var TreeView = TreeViewPrimitive__namespace.default;
32
+ var treeViewStyles = `
33
+ [role="tree"],
34
+ [role="tree"] ul,
35
+ [role="group"] {
36
+ list-style: none;
37
+ margin: 0;
38
+ padding: 0;
39
+ }
40
+ [role="treeitem"] {
41
+ list-style: none;
42
+ }
43
+ `;
44
+ var TreeView = React__namespace.forwardRef((props, ref) => /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
45
+ /* @__PURE__ */ jsxRuntime.jsx("style", { children: treeViewStyles }),
46
+ /* @__PURE__ */ jsxRuntime.jsx(TreeViewPrimitive__namespace.default, { ref, ...props })
47
+ ] }));
48
+ TreeView.displayName = "TreeView";
33
49
  var TreeViewItem = React__namespace.forwardRef(
34
50
  ({
35
51
  level = 1,
@@ -108,67 +124,70 @@ var TreeViewItem = React__namespace.forwardRef(
108
124
  children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" })
109
125
  }
110
126
  );
111
- return /* @__PURE__ */ jsxRuntime.jsxs(
112
- "div",
113
- {
114
- ref,
115
- "aria-expanded": !isEditing && isExpanded,
116
- className: cn(
117
- "group relative flex h-8 cursor-pointer select-none items-center gap-3 text-sm text-muted-foreground transition-colors hover:bg-muted",
118
- isSelected && "!bg-muted text-foreground",
119
- className
120
- ),
121
- style: {
122
- paddingLeft,
123
- ...style
124
- },
125
- "data-treeview-is-branch": isBranch,
126
- "data-treeview-level": level,
127
- ...props,
128
- children: [
129
- level > 1 && /* @__PURE__ */ jsxRuntime.jsx(
130
- "div",
131
- {
132
- style: {
133
- left: (levelIndentation / 2 + 4) * (level - 1) + indentation
134
- },
135
- className: "absolute h-full w-px group-data-[treeview-is-branch=false]:border"
136
- }
127
+ return (
128
+ // biome-ignore lint/a11y: div is used as interactive tree item with proper keyboard handling
129
+ /* @__PURE__ */ jsxRuntime.jsxs(
130
+ "div",
131
+ {
132
+ ref,
133
+ "aria-expanded": !isEditing && isExpanded,
134
+ className: cn(
135
+ "group relative flex h-8 cursor-pointer select-none items-center gap-3 text-sm text-muted-foreground transition-colors hover:bg-muted",
136
+ isSelected && "bg-muted! text-foreground",
137
+ className
137
138
  ),
138
- isSelected && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute left-0 h-full w-0.5 bg-foreground" }),
139
- isBranch && (isLoading ? loadingIcon ?? defaultLoadingIcon : expandIcon ?? defaultExpandIcon),
140
- icon,
141
- /* @__PURE__ */ jsxRuntime.jsx(
142
- "span",
143
- {
144
- className: cn("truncate text-sm", isEditing && "hidden"),
145
- title: name,
146
- children: name
147
- }
148
- ),
149
- isEditing && /* @__PURE__ */ jsxRuntime.jsx("form", { onSubmit: handleSubmit, children: /* @__PURE__ */ jsxRuntime.jsx(
150
- "input",
151
- {
152
- ref: inputRef,
153
- onChange: (e) => {
154
- setLocalValueState(e.target.value);
155
- },
156
- onKeyDownCapture: (e) => {
157
- if (e.key === "Enter") {
158
- onEditSubmit?.(localValueState);
159
- } else if (e.key === "Escape") {
160
- setLocalValueState(name);
161
- onEditSubmit?.(name);
162
- } else {
163
- e.stopPropagation();
164
- }
165
- },
166
- className: "block h-7 w-full rounded border bg-background px-2 py-1 text-sm focus:outline-none focus:ring-1 focus:ring-ring",
167
- value: localValueState
168
- }
169
- ) })
170
- ]
171
- }
139
+ style: {
140
+ paddingLeft,
141
+ ...style
142
+ },
143
+ "data-treeview-is-branch": isBranch,
144
+ "data-treeview-level": level,
145
+ ...props,
146
+ children: [
147
+ level > 1 && /* @__PURE__ */ jsxRuntime.jsx(
148
+ "div",
149
+ {
150
+ style: {
151
+ left: (levelIndentation / 2 + 4) * (level - 1) + indentation
152
+ },
153
+ className: "absolute h-full w-px group-data-[treeview-is-branch=false]:border"
154
+ }
155
+ ),
156
+ isSelected && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute left-0 h-full w-0.5 bg-foreground" }),
157
+ isBranch && (isLoading ? loadingIcon ?? defaultLoadingIcon : expandIcon ?? defaultExpandIcon),
158
+ icon,
159
+ /* @__PURE__ */ jsxRuntime.jsx(
160
+ "span",
161
+ {
162
+ className: cn("truncate text-sm", isEditing && "hidden"),
163
+ title: name,
164
+ children: name
165
+ }
166
+ ),
167
+ isEditing && /* @__PURE__ */ jsxRuntime.jsx("form", { onSubmit: handleSubmit, children: /* @__PURE__ */ jsxRuntime.jsx(
168
+ "input",
169
+ {
170
+ ref: inputRef,
171
+ onChange: (e) => {
172
+ setLocalValueState(e.target.value);
173
+ },
174
+ onKeyDownCapture: (e) => {
175
+ if (e.key === "Enter") {
176
+ onEditSubmit?.(localValueState);
177
+ } else if (e.key === "Escape") {
178
+ setLocalValueState(name);
179
+ onEditSubmit?.(name);
180
+ } else {
181
+ e.stopPropagation();
182
+ }
183
+ },
184
+ className: "block h-7 w-full rounded border bg-background px-2 py-1 text-sm focus:outline-none focus:ring-1 focus:ring-ring",
185
+ value: localValueState
186
+ }
187
+ ) })
188
+ ]
189
+ }
190
+ )
172
191
  );
173
192
  }
174
193
  );
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils.ts","../src/tree-view.tsx"],"names":["TreeViewPrimitive","React","jsx","jsxs"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { ClassValue } from \"clsx\";\n\n/**\n * Utility function to merge class names\n * This is a minimal implementation that doesn't require clsx/tailwind-merge as dependencies\n */\nexport function cn(...inputs: ClassValue[]): string {\n\treturn inputs\n\t\t.flat()\n\t\t.filter((x) => typeof x === \"string\" && x.length > 0)\n\t\t.join(\" \");\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport * as TreeViewPrimitive from \"react-accessible-treeview\";\n\nimport { cn } from \"./utils\";\n\n/**\n * TreeView component props - extends the underlying react-accessible-treeview props\n */\nexport type TreeViewProps = React.ComponentPropsWithoutRef<\n\ttypeof TreeViewPrimitive.default\n>;\n\n/**\n * TreeView component - a wrapper around react-accessible-treeview\n *\n * @example\n * ```tsx\n * import { TreeView, TreeViewItem, flattenTree } from \"shadcn-treeview\";\n *\n * const data = flattenTree({\n * name: \"Root\",\n * children: [{ name: \"Child 1\" }, { name: \"Child 2\" }]\n * });\n *\n * <TreeView\n * data={data}\n * nodeRenderer={({ element, getNodeProps, level, isBranch, isExpanded, isSelected }) => (\n * <TreeViewItem\n * {...getNodeProps()}\n * name={element.name}\n * level={level}\n * isBranch={isBranch}\n * isExpanded={isExpanded}\n * isSelected={isSelected}\n * indentation={16}\n * />\n * )}\n * />\n * ```\n */\nconst TreeView = TreeViewPrimitive.default;\n\n/**\n * Props for the TreeViewItem component\n */\nexport type TreeViewItemProps = React.ComponentPropsWithoutRef<\"div\"> & {\n\t/** The nesting level of the item (1-based) */\n\tlevel: number;\n\t/** Whether this item is expanded (only applicable for branches) */\n\tisExpanded?: boolean;\n\t/** Whether this item is a branch (has children) */\n\tisBranch?: boolean;\n\t/** Whether this item is currently selected */\n\tisSelected?: boolean;\n\t/** Base indentation in pixels */\n\tindentation?: number;\n\t/** Indentation per level in pixels */\n\tlevelIndentation?: number;\n\t/** The display name of the item */\n\tname: string;\n\t/** Optional icon to display before the name */\n\ticon?: React.ReactNode;\n\t/** Whether the item is in editing mode */\n\tisEditing?: boolean;\n\t/** Callback when editing is submitted */\n\tonEditSubmit?: (value: string) => void;\n\t/** Whether the item is in a loading state */\n\tisLoading?: boolean;\n\t/** Custom expand/collapse icon */\n\texpandIcon?: React.ReactNode;\n\t/** Custom loading icon */\n\tloadingIcon?: React.ReactNode;\n};\n\n/**\n * TreeViewItem component - renders a single item in the tree\n *\n * @example\n * ```tsx\n * <TreeViewItem\n * name=\"My File\"\n * level={1}\n * isBranch={false}\n * isSelected={false}\n * indentation={16}\n * icon={<FileIcon className=\"h-4 w-4\" />}\n * />\n * ```\n */\nconst TreeViewItem = React.forwardRef<HTMLDivElement, TreeViewItemProps>(\n\t(\n\t\t{\n\t\t\tlevel = 1,\n\t\t\tisExpanded = false,\n\t\t\tisBranch = false,\n\t\t\tisSelected = false,\n\t\t\tisLoading = false,\n\t\t\tindentation = 16,\n\t\t\tlevelIndentation = 48,\n\t\t\tname = \"\",\n\t\t\ticon,\n\t\t\tisEditing = false,\n\t\t\tonEditSubmit,\n\t\t\texpandIcon,\n\t\t\tloadingIcon,\n\t\t\tclassName,\n\t\t\tstyle,\n\t\t\t...props\n\t\t},\n\t\tref,\n\t) => {\n\t\tconst [localValueState, setLocalValueState] = React.useState(name);\n\t\tconst inputRef = React.useRef<HTMLInputElement>(null);\n\n\t\tReact.useEffect(() => {\n\t\t\tconst handleClickOutside = (event: MouseEvent) => {\n\t\t\t\tif (!inputRef.current?.contains(event.target as Node)) {\n\t\t\t\t\tonEditSubmit?.(localValueState);\n\t\t\t\t}\n\t\t\t};\n\t\t\tif (isEditing) {\n\t\t\t\tdocument.addEventListener(\"mousedown\", handleClickOutside);\n\t\t\t}\n\t\t\treturn () => {\n\t\t\t\tif (isEditing) {\n\t\t\t\t\tdocument.removeEventListener(\"mousedown\", handleClickOutside);\n\t\t\t\t}\n\t\t\t};\n\t\t}, [isEditing, localValueState, onEditSubmit]);\n\n\t\tReact.useEffect(() => {\n\t\t\tif (isEditing) {\n\t\t\t\tinputRef.current?.focus();\n\t\t\t}\n\t\t}, [isEditing]);\n\n\t\tconst handleSubmit = (e: React.FormEvent<HTMLFormElement>): void => {\n\t\t\te.preventDefault();\n\t\t\tonEditSubmit?.(localValueState);\n\t\t};\n\n\t\tconst paddingLeft =\n\t\t\tlevel === 1 && !isBranch\n\t\t\t\t? indentation\n\t\t\t\t: levelIndentation * (level - 1) + indentation;\n\n\t\tconst defaultExpandIcon = (\n\t\t\t<svg\n\t\t\t\taria-hidden=\"true\"\n\t\t\t\tclassName=\"text-muted-foreground transition-transform duration-200 group-aria-expanded:rotate-90\"\n\t\t\t\twidth=\"14\"\n\t\t\t\theight=\"14\"\n\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\tfill=\"none\"\n\t\t\t\tstroke=\"currentColor\"\n\t\t\t\tstrokeWidth=\"2\"\n\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t>\n\t\t\t\t<path d=\"m9 18 6-6-6-6\" />\n\t\t\t</svg>\n\t\t);\n\n\t\tconst defaultLoadingIcon = (\n\t\t\t<svg\n\t\t\t\taria-hidden=\"true\"\n\t\t\t\tclassName=\"animate-spin text-muted-foreground\"\n\t\t\t\twidth=\"14\"\n\t\t\t\theight=\"14\"\n\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\tfill=\"none\"\n\t\t\t\tstroke=\"currentColor\"\n\t\t\t\tstrokeWidth=\"2\"\n\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t>\n\t\t\t\t<path d=\"M21 12a9 9 0 1 1-6.219-8.56\" />\n\t\t\t</svg>\n\t\t);\n\n\t\treturn (\n\t\t\t<div\n\t\t\t\tref={ref}\n\t\t\t\taria-expanded={!isEditing && isExpanded}\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"group relative flex h-8 cursor-pointer select-none items-center gap-3 text-sm text-muted-foreground transition-colors hover:bg-muted\",\n\t\t\t\t\tisSelected && \"!bg-muted text-foreground\",\n\t\t\t\t\tclassName,\n\t\t\t\t)}\n\t\t\t\tstyle={{\n\t\t\t\t\tpaddingLeft,\n\t\t\t\t\t...style,\n\t\t\t\t}}\n\t\t\t\tdata-treeview-is-branch={isBranch}\n\t\t\t\tdata-treeview-level={level}\n\t\t\t\t{...props}\n\t\t\t>\n\t\t\t\t{level > 1 && (\n\t\t\t\t\t<div\n\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\tleft: (levelIndentation / 2 + 4) * (level - 1) + indentation,\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tclassName=\"absolute h-full w-px group-data-[treeview-is-branch=false]:border\"\n\t\t\t\t\t/>\n\t\t\t\t)}\n\t\t\t\t{isSelected && (\n\t\t\t\t\t<div className=\"absolute left-0 h-full w-0.5 bg-foreground\" />\n\t\t\t\t)}\n\t\t\t\t{isBranch &&\n\t\t\t\t\t(isLoading\n\t\t\t\t\t\t? (loadingIcon ?? defaultLoadingIcon)\n\t\t\t\t\t\t: (expandIcon ?? defaultExpandIcon))}\n\t\t\t\t{icon}\n\t\t\t\t<span\n\t\t\t\t\tclassName={cn(\"truncate text-sm\", isEditing && \"hidden\")}\n\t\t\t\t\ttitle={name}\n\t\t\t\t>\n\t\t\t\t\t{name}\n\t\t\t\t</span>\n\t\t\t\t{isEditing && (\n\t\t\t\t\t<form onSubmit={handleSubmit}>\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tref={inputRef}\n\t\t\t\t\t\t\tonChange={(e) => {\n\t\t\t\t\t\t\t\tsetLocalValueState(e.target.value);\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\tonKeyDownCapture={(e) => {\n\t\t\t\t\t\t\t\tif (e.key === \"Enter\") {\n\t\t\t\t\t\t\t\t\tonEditSubmit?.(localValueState);\n\t\t\t\t\t\t\t\t} else if (e.key === \"Escape\") {\n\t\t\t\t\t\t\t\t\tsetLocalValueState(name);\n\t\t\t\t\t\t\t\t\tonEditSubmit?.(name);\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\te.stopPropagation();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\tclassName=\"block h-7 w-full rounded border bg-background px-2 py-1 text-sm focus:outline-none focus:ring-1 focus:ring-ring\"\n\t\t\t\t\t\t\tvalue={localValueState}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</form>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t);\n\t},\n);\nTreeViewItem.displayName = \"TreeViewItem\";\n\nexport { TreeView, TreeViewItem };\n"]}
1
+ {"version":3,"sources":["../src/utils.ts","../src/tree-view.tsx"],"names":["React","jsxs","Fragment","jsx","TreeViewPrimitive"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { ClassValue } from \"clsx\";\n\n/**\n * Utility function to merge class names\n * This is a minimal implementation that doesn't require clsx/tailwind-merge as dependencies\n */\nexport function cn(...inputs: ClassValue[]): string {\n\treturn inputs\n\t\t.flat()\n\t\t.filter((x) => typeof x === \"string\" && x.length > 0)\n\t\t.join(\" \");\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport * as TreeViewPrimitive from \"react-accessible-treeview\";\n\nimport { cn } from \"./utils\";\n\n/**\n * TreeView component props - extends the underlying react-accessible-treeview props\n */\nexport type TreeViewProps = React.ComponentPropsWithoutRef<\n\ttypeof TreeViewPrimitive.default\n>;\n\n// CSS to reset list styles for tree view elements\nconst treeViewStyles = `\n[role=\"tree\"],\n[role=\"tree\"] ul,\n[role=\"group\"] {\n list-style: none;\n margin: 0;\n padding: 0;\n}\n[role=\"treeitem\"] {\n list-style: none;\n}\n`;\n\n/**\n * TreeView component - a wrapper around react-accessible-treeview\n *\n * @example\n * ```tsx\n * import { TreeView, TreeViewItem, flattenTree } from \"shadcn-treeview\";\n *\n * const data = flattenTree({\n * name: \"Root\",\n * children: [{ name: \"Child 1\" }, { name: \"Child 2\" }]\n * });\n *\n * <TreeView\n * data={data}\n * nodeRenderer={({ element, getNodeProps, level, isBranch, isExpanded, isSelected }) => (\n * <TreeViewItem\n * {...getNodeProps()}\n * name={element.name}\n * level={level}\n * isBranch={isBranch}\n * isExpanded={isExpanded}\n * isSelected={isSelected}\n * indentation={16}\n * />\n * )}\n * />\n * ```\n */\nconst TreeView = React.forwardRef<\n\tHTMLUListElement,\n\tTreeViewProps\n>((props, ref) => (\n\t<>\n\t\t<style>{treeViewStyles}</style>\n\t\t<TreeViewPrimitive.default ref={ref} {...props} />\n\t</>\n));\nTreeView.displayName = \"TreeView\";\n\n/**\n * Props for the TreeViewItem component\n */\nexport type TreeViewItemProps = React.ComponentPropsWithoutRef<\"div\"> & {\n\t/** The nesting level of the item (1-based) */\n\tlevel: number;\n\t/** Whether this item is expanded (only applicable for branches) */\n\tisExpanded?: boolean;\n\t/** Whether this item is a branch (has children) */\n\tisBranch?: boolean;\n\t/** Whether this item is currently selected */\n\tisSelected?: boolean;\n\t/** Base indentation in pixels */\n\tindentation?: number;\n\t/** Indentation per level in pixels */\n\tlevelIndentation?: number;\n\t/** The display name of the item */\n\tname: string;\n\t/** Optional icon to display before the name */\n\ticon?: React.ReactNode;\n\t/** Whether the item is in editing mode */\n\tisEditing?: boolean;\n\t/** Callback when editing is submitted */\n\tonEditSubmit?: (value: string) => void;\n\t/** Whether the item is in a loading state */\n\tisLoading?: boolean;\n\t/** Custom expand/collapse icon */\n\texpandIcon?: React.ReactNode;\n\t/** Custom loading icon */\n\tloadingIcon?: React.ReactNode;\n};\n\n/**\n * TreeViewItem component - renders a single item in the tree\n *\n * @example\n * ```tsx\n * <TreeViewItem\n * name=\"My File\"\n * level={1}\n * isBranch={false}\n * isSelected={false}\n * indentation={16}\n * icon={<FileIcon className=\"h-4 w-4\" />}\n * />\n * ```\n */\nconst TreeViewItem = React.forwardRef<HTMLDivElement, TreeViewItemProps>(\n\t(\n\t\t{\n\t\t\tlevel = 1,\n\t\t\tisExpanded = false,\n\t\t\tisBranch = false,\n\t\t\tisSelected = false,\n\t\t\tisLoading = false,\n\t\t\tindentation = 16,\n\t\t\tlevelIndentation = 48,\n\t\t\tname = \"\",\n\t\t\ticon,\n\t\t\tisEditing = false,\n\t\t\tonEditSubmit,\n\t\t\texpandIcon,\n\t\t\tloadingIcon,\n\t\t\tclassName,\n\t\t\tstyle,\n\t\t\t...props\n\t\t},\n\t\tref,\n\t) => {\n\t\tconst [localValueState, setLocalValueState] = React.useState(name);\n\t\tconst inputRef = React.useRef<HTMLInputElement>(null);\n\n\t\tReact.useEffect(() => {\n\t\t\tconst handleClickOutside = (event: MouseEvent) => {\n\t\t\t\tif (!inputRef.current?.contains(event.target as Node)) {\n\t\t\t\t\tonEditSubmit?.(localValueState);\n\t\t\t\t}\n\t\t\t};\n\t\t\tif (isEditing) {\n\t\t\t\tdocument.addEventListener(\"mousedown\", handleClickOutside);\n\t\t\t}\n\t\t\treturn () => {\n\t\t\t\tif (isEditing) {\n\t\t\t\t\tdocument.removeEventListener(\"mousedown\", handleClickOutside);\n\t\t\t\t}\n\t\t\t};\n\t\t}, [isEditing, localValueState, onEditSubmit]);\n\n\t\tReact.useEffect(() => {\n\t\t\tif (isEditing) {\n\t\t\t\tinputRef.current?.focus();\n\t\t\t}\n\t\t}, [isEditing]);\n\n\t\tconst handleSubmit = (e: React.FormEvent<HTMLFormElement>): void => {\n\t\t\te.preventDefault();\n\t\t\tonEditSubmit?.(localValueState);\n\t\t};\n\n\t\tconst paddingLeft =\n\t\t\tlevel === 1 && !isBranch\n\t\t\t\t? indentation\n\t\t\t\t: levelIndentation * (level - 1) + indentation;\n\n\t\tconst defaultExpandIcon = (\n\t\t\t<svg\n\t\t\t\taria-hidden=\"true\"\n\t\t\t\tclassName=\"text-muted-foreground transition-transform duration-200 group-aria-expanded:rotate-90\"\n\t\t\t\twidth=\"14\"\n\t\t\t\theight=\"14\"\n\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\tfill=\"none\"\n\t\t\t\tstroke=\"currentColor\"\n\t\t\t\tstrokeWidth=\"2\"\n\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t>\n\t\t\t\t<path d=\"m9 18 6-6-6-6\" />\n\t\t\t</svg>\n\t\t);\n\n\t\tconst defaultLoadingIcon = (\n\t\t\t<svg\n\t\t\t\taria-hidden=\"true\"\n\t\t\t\tclassName=\"animate-spin text-muted-foreground\"\n\t\t\t\twidth=\"14\"\n\t\t\t\theight=\"14\"\n\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\tfill=\"none\"\n\t\t\t\tstroke=\"currentColor\"\n\t\t\t\tstrokeWidth=\"2\"\n\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t>\n\t\t\t\t<path d=\"M21 12a9 9 0 1 1-6.219-8.56\" />\n\t\t\t</svg>\n\t\t);\n\n\t\treturn (\n\t\t\t// biome-ignore lint/a11y: div is used as interactive tree item with proper keyboard handling\n\t\t\t<div\n\t\t\t\tref={ref}\n\t\t\t\taria-expanded={!isEditing && isExpanded}\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"group relative flex h-8 cursor-pointer select-none items-center gap-3 text-sm text-muted-foreground transition-colors hover:bg-muted\",\n\t\t\t\t\tisSelected && \"bg-muted! text-foreground\",\n\t\t\t\t\tclassName,\n\t\t\t\t)}\n\t\t\t\tstyle={{\n\t\t\t\t\tpaddingLeft,\n\t\t\t\t\t...style,\n\t\t\t\t}}\n\t\t\t\tdata-treeview-is-branch={isBranch}\n\t\t\t\tdata-treeview-level={level}\n\t\t\t\t{...props}\n\t\t\t>\n\t\t\t\t{level > 1 && (\n\t\t\t\t\t<div\n\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\tleft: (levelIndentation / 2 + 4) * (level - 1) + indentation,\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tclassName=\"absolute h-full w-px group-data-[treeview-is-branch=false]:border\"\n\t\t\t\t\t/>\n\t\t\t\t)}\n\t\t\t\t{isSelected && (\n\t\t\t\t\t<div className=\"absolute left-0 h-full w-0.5 bg-foreground\" />\n\t\t\t\t)}\n\t\t\t\t{isBranch &&\n\t\t\t\t\t(isLoading\n\t\t\t\t\t\t? (loadingIcon ?? defaultLoadingIcon)\n\t\t\t\t\t\t: (expandIcon ?? defaultExpandIcon))}\n\t\t\t\t{icon}\n\t\t\t\t<span\n\t\t\t\t\tclassName={cn(\"truncate text-sm\", isEditing && \"hidden\")}\n\t\t\t\t\ttitle={name}\n\t\t\t\t>\n\t\t\t\t\t{name}\n\t\t\t\t</span>\n\t\t\t\t{isEditing && (\n\t\t\t\t\t<form onSubmit={handleSubmit}>\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tref={inputRef}\n\t\t\t\t\t\t\tonChange={(e) => {\n\t\t\t\t\t\t\t\tsetLocalValueState(e.target.value);\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\tonKeyDownCapture={(e) => {\n\t\t\t\t\t\t\t\tif (e.key === \"Enter\") {\n\t\t\t\t\t\t\t\t\tonEditSubmit?.(localValueState);\n\t\t\t\t\t\t\t\t} else if (e.key === \"Escape\") {\n\t\t\t\t\t\t\t\t\tsetLocalValueState(name);\n\t\t\t\t\t\t\t\t\tonEditSubmit?.(name);\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\te.stopPropagation();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\tclassName=\"block h-7 w-full rounded border bg-background px-2 py-1 text-sm focus:outline-none focus:ring-1 focus:ring-ring\"\n\t\t\t\t\t\t\tvalue={localValueState}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</form>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t);\n\t},\n);\nTreeViewItem.displayName = \"TreeViewItem\";\n\nexport { TreeView, TreeViewItem };\n"]}
package/dist/index.d.cts CHANGED
@@ -35,7 +35,7 @@ type TreeViewProps = React.ComponentPropsWithoutRef<typeof TreeViewPrimitive.def
35
35
  * />
36
36
  * ```
37
37
  */
38
- declare const TreeView: React.ForwardRefExoticComponent<TreeViewPrimitive.ITreeViewProps<react_accessible_treeview_dist_TreeView_utils.IFlatMetadata> & React.RefAttributes<HTMLUListElement>>;
38
+ declare const TreeView: React.ForwardRefExoticComponent<Omit<TreeViewPrimitive.ITreeViewProps<react_accessible_treeview_dist_TreeView_utils.IFlatMetadata> & React.RefAttributes<HTMLUListElement>, "ref"> & React.RefAttributes<HTMLUListElement>>;
39
39
  /**
40
40
  * Props for the TreeViewItem component
41
41
  */
package/dist/index.d.ts CHANGED
@@ -35,7 +35,7 @@ type TreeViewProps = React.ComponentPropsWithoutRef<typeof TreeViewPrimitive.def
35
35
  * />
36
36
  * ```
37
37
  */
38
- declare const TreeView: React.ForwardRefExoticComponent<TreeViewPrimitive.ITreeViewProps<react_accessible_treeview_dist_TreeView_utils.IFlatMetadata> & React.RefAttributes<HTMLUListElement>>;
38
+ declare const TreeView: React.ForwardRefExoticComponent<Omit<TreeViewPrimitive.ITreeViewProps<react_accessible_treeview_dist_TreeView_utils.IFlatMetadata> & React.RefAttributes<HTMLUListElement>, "ref"> & React.RefAttributes<HTMLUListElement>>;
39
39
  /**
40
40
  * Props for the TreeViewItem component
41
41
  */
package/dist/index.js CHANGED
@@ -1,13 +1,29 @@
1
1
  import * as TreeViewPrimitive from 'react-accessible-treeview';
2
2
  export { flattenTree } from 'react-accessible-treeview';
3
3
  import * as React from 'react';
4
- import { jsxs, jsx } from 'react/jsx-runtime';
4
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
5
5
 
6
6
  // src/utils.ts
7
7
  function cn(...inputs) {
8
8
  return inputs.flat().filter((x) => typeof x === "string" && x.length > 0).join(" ");
9
9
  }
10
- var TreeView = TreeViewPrimitive.default;
10
+ var treeViewStyles = `
11
+ [role="tree"],
12
+ [role="tree"] ul,
13
+ [role="group"] {
14
+ list-style: none;
15
+ margin: 0;
16
+ padding: 0;
17
+ }
18
+ [role="treeitem"] {
19
+ list-style: none;
20
+ }
21
+ `;
22
+ var TreeView = React.forwardRef((props, ref) => /* @__PURE__ */ jsxs(Fragment, { children: [
23
+ /* @__PURE__ */ jsx("style", { children: treeViewStyles }),
24
+ /* @__PURE__ */ jsx(TreeViewPrimitive.default, { ref, ...props })
25
+ ] }));
26
+ TreeView.displayName = "TreeView";
11
27
  var TreeViewItem = React.forwardRef(
12
28
  ({
13
29
  level = 1,
@@ -86,67 +102,70 @@ var TreeViewItem = React.forwardRef(
86
102
  children: /* @__PURE__ */ jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" })
87
103
  }
88
104
  );
89
- return /* @__PURE__ */ jsxs(
90
- "div",
91
- {
92
- ref,
93
- "aria-expanded": !isEditing && isExpanded,
94
- className: cn(
95
- "group relative flex h-8 cursor-pointer select-none items-center gap-3 text-sm text-muted-foreground transition-colors hover:bg-muted",
96
- isSelected && "!bg-muted text-foreground",
97
- className
98
- ),
99
- style: {
100
- paddingLeft,
101
- ...style
102
- },
103
- "data-treeview-is-branch": isBranch,
104
- "data-treeview-level": level,
105
- ...props,
106
- children: [
107
- level > 1 && /* @__PURE__ */ jsx(
108
- "div",
109
- {
110
- style: {
111
- left: (levelIndentation / 2 + 4) * (level - 1) + indentation
112
- },
113
- className: "absolute h-full w-px group-data-[treeview-is-branch=false]:border"
114
- }
105
+ return (
106
+ // biome-ignore lint/a11y: div is used as interactive tree item with proper keyboard handling
107
+ /* @__PURE__ */ jsxs(
108
+ "div",
109
+ {
110
+ ref,
111
+ "aria-expanded": !isEditing && isExpanded,
112
+ className: cn(
113
+ "group relative flex h-8 cursor-pointer select-none items-center gap-3 text-sm text-muted-foreground transition-colors hover:bg-muted",
114
+ isSelected && "bg-muted! text-foreground",
115
+ className
115
116
  ),
116
- isSelected && /* @__PURE__ */ jsx("div", { className: "absolute left-0 h-full w-0.5 bg-foreground" }),
117
- isBranch && (isLoading ? loadingIcon ?? defaultLoadingIcon : expandIcon ?? defaultExpandIcon),
118
- icon,
119
- /* @__PURE__ */ jsx(
120
- "span",
121
- {
122
- className: cn("truncate text-sm", isEditing && "hidden"),
123
- title: name,
124
- children: name
125
- }
126
- ),
127
- isEditing && /* @__PURE__ */ jsx("form", { onSubmit: handleSubmit, children: /* @__PURE__ */ jsx(
128
- "input",
129
- {
130
- ref: inputRef,
131
- onChange: (e) => {
132
- setLocalValueState(e.target.value);
133
- },
134
- onKeyDownCapture: (e) => {
135
- if (e.key === "Enter") {
136
- onEditSubmit?.(localValueState);
137
- } else if (e.key === "Escape") {
138
- setLocalValueState(name);
139
- onEditSubmit?.(name);
140
- } else {
141
- e.stopPropagation();
142
- }
143
- },
144
- className: "block h-7 w-full rounded border bg-background px-2 py-1 text-sm focus:outline-none focus:ring-1 focus:ring-ring",
145
- value: localValueState
146
- }
147
- ) })
148
- ]
149
- }
117
+ style: {
118
+ paddingLeft,
119
+ ...style
120
+ },
121
+ "data-treeview-is-branch": isBranch,
122
+ "data-treeview-level": level,
123
+ ...props,
124
+ children: [
125
+ level > 1 && /* @__PURE__ */ jsx(
126
+ "div",
127
+ {
128
+ style: {
129
+ left: (levelIndentation / 2 + 4) * (level - 1) + indentation
130
+ },
131
+ className: "absolute h-full w-px group-data-[treeview-is-branch=false]:border"
132
+ }
133
+ ),
134
+ isSelected && /* @__PURE__ */ jsx("div", { className: "absolute left-0 h-full w-0.5 bg-foreground" }),
135
+ isBranch && (isLoading ? loadingIcon ?? defaultLoadingIcon : expandIcon ?? defaultExpandIcon),
136
+ icon,
137
+ /* @__PURE__ */ jsx(
138
+ "span",
139
+ {
140
+ className: cn("truncate text-sm", isEditing && "hidden"),
141
+ title: name,
142
+ children: name
143
+ }
144
+ ),
145
+ isEditing && /* @__PURE__ */ jsx("form", { onSubmit: handleSubmit, children: /* @__PURE__ */ jsx(
146
+ "input",
147
+ {
148
+ ref: inputRef,
149
+ onChange: (e) => {
150
+ setLocalValueState(e.target.value);
151
+ },
152
+ onKeyDownCapture: (e) => {
153
+ if (e.key === "Enter") {
154
+ onEditSubmit?.(localValueState);
155
+ } else if (e.key === "Escape") {
156
+ setLocalValueState(name);
157
+ onEditSubmit?.(name);
158
+ } else {
159
+ e.stopPropagation();
160
+ }
161
+ },
162
+ className: "block h-7 w-full rounded border bg-background px-2 py-1 text-sm focus:outline-none focus:ring-1 focus:ring-ring",
163
+ value: localValueState
164
+ }
165
+ ) })
166
+ ]
167
+ }
168
+ )
150
169
  );
151
170
  }
152
171
  );
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils.ts","../src/tree-view.tsx"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { ClassValue } from \"clsx\";\n\n/**\n * Utility function to merge class names\n * This is a minimal implementation that doesn't require clsx/tailwind-merge as dependencies\n */\nexport function cn(...inputs: ClassValue[]): string {\n\treturn inputs\n\t\t.flat()\n\t\t.filter((x) => typeof x === \"string\" && x.length > 0)\n\t\t.join(\" \");\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport * as TreeViewPrimitive from \"react-accessible-treeview\";\n\nimport { cn } from \"./utils\";\n\n/**\n * TreeView component props - extends the underlying react-accessible-treeview props\n */\nexport type TreeViewProps = React.ComponentPropsWithoutRef<\n\ttypeof TreeViewPrimitive.default\n>;\n\n/**\n * TreeView component - a wrapper around react-accessible-treeview\n *\n * @example\n * ```tsx\n * import { TreeView, TreeViewItem, flattenTree } from \"shadcn-treeview\";\n *\n * const data = flattenTree({\n * name: \"Root\",\n * children: [{ name: \"Child 1\" }, { name: \"Child 2\" }]\n * });\n *\n * <TreeView\n * data={data}\n * nodeRenderer={({ element, getNodeProps, level, isBranch, isExpanded, isSelected }) => (\n * <TreeViewItem\n * {...getNodeProps()}\n * name={element.name}\n * level={level}\n * isBranch={isBranch}\n * isExpanded={isExpanded}\n * isSelected={isSelected}\n * indentation={16}\n * />\n * )}\n * />\n * ```\n */\nconst TreeView = TreeViewPrimitive.default;\n\n/**\n * Props for the TreeViewItem component\n */\nexport type TreeViewItemProps = React.ComponentPropsWithoutRef<\"div\"> & {\n\t/** The nesting level of the item (1-based) */\n\tlevel: number;\n\t/** Whether this item is expanded (only applicable for branches) */\n\tisExpanded?: boolean;\n\t/** Whether this item is a branch (has children) */\n\tisBranch?: boolean;\n\t/** Whether this item is currently selected */\n\tisSelected?: boolean;\n\t/** Base indentation in pixels */\n\tindentation?: number;\n\t/** Indentation per level in pixels */\n\tlevelIndentation?: number;\n\t/** The display name of the item */\n\tname: string;\n\t/** Optional icon to display before the name */\n\ticon?: React.ReactNode;\n\t/** Whether the item is in editing mode */\n\tisEditing?: boolean;\n\t/** Callback when editing is submitted */\n\tonEditSubmit?: (value: string) => void;\n\t/** Whether the item is in a loading state */\n\tisLoading?: boolean;\n\t/** Custom expand/collapse icon */\n\texpandIcon?: React.ReactNode;\n\t/** Custom loading icon */\n\tloadingIcon?: React.ReactNode;\n};\n\n/**\n * TreeViewItem component - renders a single item in the tree\n *\n * @example\n * ```tsx\n * <TreeViewItem\n * name=\"My File\"\n * level={1}\n * isBranch={false}\n * isSelected={false}\n * indentation={16}\n * icon={<FileIcon className=\"h-4 w-4\" />}\n * />\n * ```\n */\nconst TreeViewItem = React.forwardRef<HTMLDivElement, TreeViewItemProps>(\n\t(\n\t\t{\n\t\t\tlevel = 1,\n\t\t\tisExpanded = false,\n\t\t\tisBranch = false,\n\t\t\tisSelected = false,\n\t\t\tisLoading = false,\n\t\t\tindentation = 16,\n\t\t\tlevelIndentation = 48,\n\t\t\tname = \"\",\n\t\t\ticon,\n\t\t\tisEditing = false,\n\t\t\tonEditSubmit,\n\t\t\texpandIcon,\n\t\t\tloadingIcon,\n\t\t\tclassName,\n\t\t\tstyle,\n\t\t\t...props\n\t\t},\n\t\tref,\n\t) => {\n\t\tconst [localValueState, setLocalValueState] = React.useState(name);\n\t\tconst inputRef = React.useRef<HTMLInputElement>(null);\n\n\t\tReact.useEffect(() => {\n\t\t\tconst handleClickOutside = (event: MouseEvent) => {\n\t\t\t\tif (!inputRef.current?.contains(event.target as Node)) {\n\t\t\t\t\tonEditSubmit?.(localValueState);\n\t\t\t\t}\n\t\t\t};\n\t\t\tif (isEditing) {\n\t\t\t\tdocument.addEventListener(\"mousedown\", handleClickOutside);\n\t\t\t}\n\t\t\treturn () => {\n\t\t\t\tif (isEditing) {\n\t\t\t\t\tdocument.removeEventListener(\"mousedown\", handleClickOutside);\n\t\t\t\t}\n\t\t\t};\n\t\t}, [isEditing, localValueState, onEditSubmit]);\n\n\t\tReact.useEffect(() => {\n\t\t\tif (isEditing) {\n\t\t\t\tinputRef.current?.focus();\n\t\t\t}\n\t\t}, [isEditing]);\n\n\t\tconst handleSubmit = (e: React.FormEvent<HTMLFormElement>): void => {\n\t\t\te.preventDefault();\n\t\t\tonEditSubmit?.(localValueState);\n\t\t};\n\n\t\tconst paddingLeft =\n\t\t\tlevel === 1 && !isBranch\n\t\t\t\t? indentation\n\t\t\t\t: levelIndentation * (level - 1) + indentation;\n\n\t\tconst defaultExpandIcon = (\n\t\t\t<svg\n\t\t\t\taria-hidden=\"true\"\n\t\t\t\tclassName=\"text-muted-foreground transition-transform duration-200 group-aria-expanded:rotate-90\"\n\t\t\t\twidth=\"14\"\n\t\t\t\theight=\"14\"\n\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\tfill=\"none\"\n\t\t\t\tstroke=\"currentColor\"\n\t\t\t\tstrokeWidth=\"2\"\n\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t>\n\t\t\t\t<path d=\"m9 18 6-6-6-6\" />\n\t\t\t</svg>\n\t\t);\n\n\t\tconst defaultLoadingIcon = (\n\t\t\t<svg\n\t\t\t\taria-hidden=\"true\"\n\t\t\t\tclassName=\"animate-spin text-muted-foreground\"\n\t\t\t\twidth=\"14\"\n\t\t\t\theight=\"14\"\n\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\tfill=\"none\"\n\t\t\t\tstroke=\"currentColor\"\n\t\t\t\tstrokeWidth=\"2\"\n\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t>\n\t\t\t\t<path d=\"M21 12a9 9 0 1 1-6.219-8.56\" />\n\t\t\t</svg>\n\t\t);\n\n\t\treturn (\n\t\t\t<div\n\t\t\t\tref={ref}\n\t\t\t\taria-expanded={!isEditing && isExpanded}\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"group relative flex h-8 cursor-pointer select-none items-center gap-3 text-sm text-muted-foreground transition-colors hover:bg-muted\",\n\t\t\t\t\tisSelected && \"!bg-muted text-foreground\",\n\t\t\t\t\tclassName,\n\t\t\t\t)}\n\t\t\t\tstyle={{\n\t\t\t\t\tpaddingLeft,\n\t\t\t\t\t...style,\n\t\t\t\t}}\n\t\t\t\tdata-treeview-is-branch={isBranch}\n\t\t\t\tdata-treeview-level={level}\n\t\t\t\t{...props}\n\t\t\t>\n\t\t\t\t{level > 1 && (\n\t\t\t\t\t<div\n\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\tleft: (levelIndentation / 2 + 4) * (level - 1) + indentation,\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tclassName=\"absolute h-full w-px group-data-[treeview-is-branch=false]:border\"\n\t\t\t\t\t/>\n\t\t\t\t)}\n\t\t\t\t{isSelected && (\n\t\t\t\t\t<div className=\"absolute left-0 h-full w-0.5 bg-foreground\" />\n\t\t\t\t)}\n\t\t\t\t{isBranch &&\n\t\t\t\t\t(isLoading\n\t\t\t\t\t\t? (loadingIcon ?? defaultLoadingIcon)\n\t\t\t\t\t\t: (expandIcon ?? defaultExpandIcon))}\n\t\t\t\t{icon}\n\t\t\t\t<span\n\t\t\t\t\tclassName={cn(\"truncate text-sm\", isEditing && \"hidden\")}\n\t\t\t\t\ttitle={name}\n\t\t\t\t>\n\t\t\t\t\t{name}\n\t\t\t\t</span>\n\t\t\t\t{isEditing && (\n\t\t\t\t\t<form onSubmit={handleSubmit}>\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tref={inputRef}\n\t\t\t\t\t\t\tonChange={(e) => {\n\t\t\t\t\t\t\t\tsetLocalValueState(e.target.value);\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\tonKeyDownCapture={(e) => {\n\t\t\t\t\t\t\t\tif (e.key === \"Enter\") {\n\t\t\t\t\t\t\t\t\tonEditSubmit?.(localValueState);\n\t\t\t\t\t\t\t\t} else if (e.key === \"Escape\") {\n\t\t\t\t\t\t\t\t\tsetLocalValueState(name);\n\t\t\t\t\t\t\t\t\tonEditSubmit?.(name);\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\te.stopPropagation();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\tclassName=\"block h-7 w-full rounded border bg-background px-2 py-1 text-sm focus:outline-none focus:ring-1 focus:ring-ring\"\n\t\t\t\t\t\t\tvalue={localValueState}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</form>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t);\n\t},\n);\nTreeViewItem.displayName = \"TreeViewItem\";\n\nexport { TreeView, TreeViewItem };\n"]}
1
+ {"version":3,"sources":["../src/utils.ts","../src/tree-view.tsx"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { ClassValue } from \"clsx\";\n\n/**\n * Utility function to merge class names\n * This is a minimal implementation that doesn't require clsx/tailwind-merge as dependencies\n */\nexport function cn(...inputs: ClassValue[]): string {\n\treturn inputs\n\t\t.flat()\n\t\t.filter((x) => typeof x === \"string\" && x.length > 0)\n\t\t.join(\" \");\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport * as TreeViewPrimitive from \"react-accessible-treeview\";\n\nimport { cn } from \"./utils\";\n\n/**\n * TreeView component props - extends the underlying react-accessible-treeview props\n */\nexport type TreeViewProps = React.ComponentPropsWithoutRef<\n\ttypeof TreeViewPrimitive.default\n>;\n\n// CSS to reset list styles for tree view elements\nconst treeViewStyles = `\n[role=\"tree\"],\n[role=\"tree\"] ul,\n[role=\"group\"] {\n list-style: none;\n margin: 0;\n padding: 0;\n}\n[role=\"treeitem\"] {\n list-style: none;\n}\n`;\n\n/**\n * TreeView component - a wrapper around react-accessible-treeview\n *\n * @example\n * ```tsx\n * import { TreeView, TreeViewItem, flattenTree } from \"shadcn-treeview\";\n *\n * const data = flattenTree({\n * name: \"Root\",\n * children: [{ name: \"Child 1\" }, { name: \"Child 2\" }]\n * });\n *\n * <TreeView\n * data={data}\n * nodeRenderer={({ element, getNodeProps, level, isBranch, isExpanded, isSelected }) => (\n * <TreeViewItem\n * {...getNodeProps()}\n * name={element.name}\n * level={level}\n * isBranch={isBranch}\n * isExpanded={isExpanded}\n * isSelected={isSelected}\n * indentation={16}\n * />\n * )}\n * />\n * ```\n */\nconst TreeView = React.forwardRef<\n\tHTMLUListElement,\n\tTreeViewProps\n>((props, ref) => (\n\t<>\n\t\t<style>{treeViewStyles}</style>\n\t\t<TreeViewPrimitive.default ref={ref} {...props} />\n\t</>\n));\nTreeView.displayName = \"TreeView\";\n\n/**\n * Props for the TreeViewItem component\n */\nexport type TreeViewItemProps = React.ComponentPropsWithoutRef<\"div\"> & {\n\t/** The nesting level of the item (1-based) */\n\tlevel: number;\n\t/** Whether this item is expanded (only applicable for branches) */\n\tisExpanded?: boolean;\n\t/** Whether this item is a branch (has children) */\n\tisBranch?: boolean;\n\t/** Whether this item is currently selected */\n\tisSelected?: boolean;\n\t/** Base indentation in pixels */\n\tindentation?: number;\n\t/** Indentation per level in pixels */\n\tlevelIndentation?: number;\n\t/** The display name of the item */\n\tname: string;\n\t/** Optional icon to display before the name */\n\ticon?: React.ReactNode;\n\t/** Whether the item is in editing mode */\n\tisEditing?: boolean;\n\t/** Callback when editing is submitted */\n\tonEditSubmit?: (value: string) => void;\n\t/** Whether the item is in a loading state */\n\tisLoading?: boolean;\n\t/** Custom expand/collapse icon */\n\texpandIcon?: React.ReactNode;\n\t/** Custom loading icon */\n\tloadingIcon?: React.ReactNode;\n};\n\n/**\n * TreeViewItem component - renders a single item in the tree\n *\n * @example\n * ```tsx\n * <TreeViewItem\n * name=\"My File\"\n * level={1}\n * isBranch={false}\n * isSelected={false}\n * indentation={16}\n * icon={<FileIcon className=\"h-4 w-4\" />}\n * />\n * ```\n */\nconst TreeViewItem = React.forwardRef<HTMLDivElement, TreeViewItemProps>(\n\t(\n\t\t{\n\t\t\tlevel = 1,\n\t\t\tisExpanded = false,\n\t\t\tisBranch = false,\n\t\t\tisSelected = false,\n\t\t\tisLoading = false,\n\t\t\tindentation = 16,\n\t\t\tlevelIndentation = 48,\n\t\t\tname = \"\",\n\t\t\ticon,\n\t\t\tisEditing = false,\n\t\t\tonEditSubmit,\n\t\t\texpandIcon,\n\t\t\tloadingIcon,\n\t\t\tclassName,\n\t\t\tstyle,\n\t\t\t...props\n\t\t},\n\t\tref,\n\t) => {\n\t\tconst [localValueState, setLocalValueState] = React.useState(name);\n\t\tconst inputRef = React.useRef<HTMLInputElement>(null);\n\n\t\tReact.useEffect(() => {\n\t\t\tconst handleClickOutside = (event: MouseEvent) => {\n\t\t\t\tif (!inputRef.current?.contains(event.target as Node)) {\n\t\t\t\t\tonEditSubmit?.(localValueState);\n\t\t\t\t}\n\t\t\t};\n\t\t\tif (isEditing) {\n\t\t\t\tdocument.addEventListener(\"mousedown\", handleClickOutside);\n\t\t\t}\n\t\t\treturn () => {\n\t\t\t\tif (isEditing) {\n\t\t\t\t\tdocument.removeEventListener(\"mousedown\", handleClickOutside);\n\t\t\t\t}\n\t\t\t};\n\t\t}, [isEditing, localValueState, onEditSubmit]);\n\n\t\tReact.useEffect(() => {\n\t\t\tif (isEditing) {\n\t\t\t\tinputRef.current?.focus();\n\t\t\t}\n\t\t}, [isEditing]);\n\n\t\tconst handleSubmit = (e: React.FormEvent<HTMLFormElement>): void => {\n\t\t\te.preventDefault();\n\t\t\tonEditSubmit?.(localValueState);\n\t\t};\n\n\t\tconst paddingLeft =\n\t\t\tlevel === 1 && !isBranch\n\t\t\t\t? indentation\n\t\t\t\t: levelIndentation * (level - 1) + indentation;\n\n\t\tconst defaultExpandIcon = (\n\t\t\t<svg\n\t\t\t\taria-hidden=\"true\"\n\t\t\t\tclassName=\"text-muted-foreground transition-transform duration-200 group-aria-expanded:rotate-90\"\n\t\t\t\twidth=\"14\"\n\t\t\t\theight=\"14\"\n\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\tfill=\"none\"\n\t\t\t\tstroke=\"currentColor\"\n\t\t\t\tstrokeWidth=\"2\"\n\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t>\n\t\t\t\t<path d=\"m9 18 6-6-6-6\" />\n\t\t\t</svg>\n\t\t);\n\n\t\tconst defaultLoadingIcon = (\n\t\t\t<svg\n\t\t\t\taria-hidden=\"true\"\n\t\t\t\tclassName=\"animate-spin text-muted-foreground\"\n\t\t\t\twidth=\"14\"\n\t\t\t\theight=\"14\"\n\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\tfill=\"none\"\n\t\t\t\tstroke=\"currentColor\"\n\t\t\t\tstrokeWidth=\"2\"\n\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t>\n\t\t\t\t<path d=\"M21 12a9 9 0 1 1-6.219-8.56\" />\n\t\t\t</svg>\n\t\t);\n\n\t\treturn (\n\t\t\t// biome-ignore lint/a11y: div is used as interactive tree item with proper keyboard handling\n\t\t\t<div\n\t\t\t\tref={ref}\n\t\t\t\taria-expanded={!isEditing && isExpanded}\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"group relative flex h-8 cursor-pointer select-none items-center gap-3 text-sm text-muted-foreground transition-colors hover:bg-muted\",\n\t\t\t\t\tisSelected && \"bg-muted! text-foreground\",\n\t\t\t\t\tclassName,\n\t\t\t\t)}\n\t\t\t\tstyle={{\n\t\t\t\t\tpaddingLeft,\n\t\t\t\t\t...style,\n\t\t\t\t}}\n\t\t\t\tdata-treeview-is-branch={isBranch}\n\t\t\t\tdata-treeview-level={level}\n\t\t\t\t{...props}\n\t\t\t>\n\t\t\t\t{level > 1 && (\n\t\t\t\t\t<div\n\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\tleft: (levelIndentation / 2 + 4) * (level - 1) + indentation,\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tclassName=\"absolute h-full w-px group-data-[treeview-is-branch=false]:border\"\n\t\t\t\t\t/>\n\t\t\t\t)}\n\t\t\t\t{isSelected && (\n\t\t\t\t\t<div className=\"absolute left-0 h-full w-0.5 bg-foreground\" />\n\t\t\t\t)}\n\t\t\t\t{isBranch &&\n\t\t\t\t\t(isLoading\n\t\t\t\t\t\t? (loadingIcon ?? defaultLoadingIcon)\n\t\t\t\t\t\t: (expandIcon ?? defaultExpandIcon))}\n\t\t\t\t{icon}\n\t\t\t\t<span\n\t\t\t\t\tclassName={cn(\"truncate text-sm\", isEditing && \"hidden\")}\n\t\t\t\t\ttitle={name}\n\t\t\t\t>\n\t\t\t\t\t{name}\n\t\t\t\t</span>\n\t\t\t\t{isEditing && (\n\t\t\t\t\t<form onSubmit={handleSubmit}>\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tref={inputRef}\n\t\t\t\t\t\t\tonChange={(e) => {\n\t\t\t\t\t\t\t\tsetLocalValueState(e.target.value);\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\tonKeyDownCapture={(e) => {\n\t\t\t\t\t\t\t\tif (e.key === \"Enter\") {\n\t\t\t\t\t\t\t\t\tonEditSubmit?.(localValueState);\n\t\t\t\t\t\t\t\t} else if (e.key === \"Escape\") {\n\t\t\t\t\t\t\t\t\tsetLocalValueState(name);\n\t\t\t\t\t\t\t\t\tonEditSubmit?.(name);\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\te.stopPropagation();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\tclassName=\"block h-7 w-full rounded border bg-background px-2 py-1 text-sm focus:outline-none focus:ring-1 focus:ring-ring\"\n\t\t\t\t\t\t\tvalue={localValueState}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</form>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t);\n\t},\n);\nTreeViewItem.displayName = \"TreeViewItem\";\n\nexport { TreeView, TreeViewItem };\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shadcn-treeview",
3
- "version": "0.1.0",
3
+ "version": "1.0.0",
4
4
  "description": "Accessible tree view component for React with Shadcn UI styling. Built on react-accessible-treeview.",
5
5
  "license": "MIT",
6
6
  "type": "module",