shadcn-treeview 0.1.0 → 1.0.1

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,10 @@ 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 TreeView = React__namespace.forwardRef(
33
+ (props, ref) => /* @__PURE__ */ jsxRuntime.jsx(TreeViewPrimitive__namespace.default, { ref, ...props })
34
+ );
35
+ TreeView.displayName = "TreeView";
33
36
  var TreeViewItem = React__namespace.forwardRef(
34
37
  ({
35
38
  level = 1,
@@ -108,67 +111,70 @@ var TreeViewItem = React__namespace.forwardRef(
108
111
  children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" })
109
112
  }
110
113
  );
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
- }
137
- ),
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
- }
114
+ return (
115
+ // biome-ignore lint/a11y: div is used as interactive tree item with proper keyboard handling
116
+ /* @__PURE__ */ jsxRuntime.jsxs(
117
+ "div",
118
+ {
119
+ ref,
120
+ "aria-expanded": !isEditing && isExpanded,
121
+ className: cn(
122
+ "group relative flex h-8 cursor-pointer select-none items-center gap-3 text-sm text-muted-foreground transition-colors hover:bg-muted",
123
+ isSelected && "bg-muted! text-foreground",
124
+ className
148
125
  ),
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
- }
126
+ style: {
127
+ paddingLeft,
128
+ ...style
129
+ },
130
+ "data-treeview-is-branch": isBranch,
131
+ "data-treeview-level": level,
132
+ ...props,
133
+ children: [
134
+ level > 1 && /* @__PURE__ */ jsxRuntime.jsx(
135
+ "div",
136
+ {
137
+ style: {
138
+ left: (levelIndentation / 2 + 4) * (level - 1) + indentation
139
+ },
140
+ className: "absolute h-full w-px group-data-[treeview-is-branch=false]:border"
141
+ }
142
+ ),
143
+ isSelected && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute left-0 h-full w-0.5 bg-foreground" }),
144
+ isBranch && (isLoading ? loadingIcon ?? defaultLoadingIcon : expandIcon ?? defaultExpandIcon),
145
+ icon,
146
+ /* @__PURE__ */ jsxRuntime.jsx(
147
+ "span",
148
+ {
149
+ className: cn("truncate text-sm", isEditing && "hidden"),
150
+ title: name,
151
+ children: name
152
+ }
153
+ ),
154
+ isEditing && /* @__PURE__ */ jsxRuntime.jsx("form", { onSubmit: handleSubmit, children: /* @__PURE__ */ jsxRuntime.jsx(
155
+ "input",
156
+ {
157
+ ref: inputRef,
158
+ onChange: (e) => {
159
+ setLocalValueState(e.target.value);
160
+ },
161
+ onKeyDownCapture: (e) => {
162
+ if (e.key === "Enter") {
163
+ onEditSubmit?.(localValueState);
164
+ } else if (e.key === "Escape") {
165
+ setLocalValueState(name);
166
+ onEditSubmit?.(name);
167
+ } else {
168
+ e.stopPropagation();
169
+ }
170
+ },
171
+ 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",
172
+ value: localValueState
173
+ }
174
+ ) })
175
+ ]
176
+ }
177
+ )
172
178
  );
173
179
  }
174
180
  );
@@ -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","jsx","TreeViewPrimitive","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 = React.forwardRef<HTMLUListElement, TreeViewProps>(\n\t(props, ref) => <TreeViewPrimitive.default ref={ref} {...props} />,\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,16 @@
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 { jsx, jsxs } 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 TreeView = React.forwardRef(
11
+ (props, ref) => /* @__PURE__ */ jsx(TreeViewPrimitive.default, { ref, ...props })
12
+ );
13
+ TreeView.displayName = "TreeView";
11
14
  var TreeViewItem = React.forwardRef(
12
15
  ({
13
16
  level = 1,
@@ -86,67 +89,70 @@ var TreeViewItem = React.forwardRef(
86
89
  children: /* @__PURE__ */ jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" })
87
90
  }
88
91
  );
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
- }
115
- ),
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
- }
92
+ return (
93
+ // biome-ignore lint/a11y: div is used as interactive tree item with proper keyboard handling
94
+ /* @__PURE__ */ jsxs(
95
+ "div",
96
+ {
97
+ ref,
98
+ "aria-expanded": !isEditing && isExpanded,
99
+ className: cn(
100
+ "group relative flex h-8 cursor-pointer select-none items-center gap-3 text-sm text-muted-foreground transition-colors hover:bg-muted",
101
+ isSelected && "bg-muted! text-foreground",
102
+ className
126
103
  ),
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
- }
104
+ style: {
105
+ paddingLeft,
106
+ ...style
107
+ },
108
+ "data-treeview-is-branch": isBranch,
109
+ "data-treeview-level": level,
110
+ ...props,
111
+ children: [
112
+ level > 1 && /* @__PURE__ */ jsx(
113
+ "div",
114
+ {
115
+ style: {
116
+ left: (levelIndentation / 2 + 4) * (level - 1) + indentation
117
+ },
118
+ className: "absolute h-full w-px group-data-[treeview-is-branch=false]:border"
119
+ }
120
+ ),
121
+ isSelected && /* @__PURE__ */ jsx("div", { className: "absolute left-0 h-full w-0.5 bg-foreground" }),
122
+ isBranch && (isLoading ? loadingIcon ?? defaultLoadingIcon : expandIcon ?? defaultExpandIcon),
123
+ icon,
124
+ /* @__PURE__ */ jsx(
125
+ "span",
126
+ {
127
+ className: cn("truncate text-sm", isEditing && "hidden"),
128
+ title: name,
129
+ children: name
130
+ }
131
+ ),
132
+ isEditing && /* @__PURE__ */ jsx("form", { onSubmit: handleSubmit, children: /* @__PURE__ */ jsx(
133
+ "input",
134
+ {
135
+ ref: inputRef,
136
+ onChange: (e) => {
137
+ setLocalValueState(e.target.value);
138
+ },
139
+ onKeyDownCapture: (e) => {
140
+ if (e.key === "Enter") {
141
+ onEditSubmit?.(localValueState);
142
+ } else if (e.key === "Escape") {
143
+ setLocalValueState(name);
144
+ onEditSubmit?.(name);
145
+ } else {
146
+ e.stopPropagation();
147
+ }
148
+ },
149
+ 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",
150
+ value: localValueState
151
+ }
152
+ ) })
153
+ ]
154
+ }
155
+ )
150
156
  );
151
157
  }
152
158
  );
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/**\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<HTMLUListElement, TreeViewProps>(\n\t(props, ref) => <TreeViewPrimitive.default ref={ref} {...props} />,\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.1",
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",