@neo4j-ndl/react 4.21.2 → 4.22.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.
Files changed (82) hide show
  1. package/lib/cjs/dropzone/stories/dropzone-full.story.js +5 -5
  2. package/lib/cjs/dropzone/stories/dropzone-full.story.js.map +1 -1
  3. package/lib/cjs/helpers/index.js +1 -0
  4. package/lib/cjs/helpers/index.js.map +1 -1
  5. package/lib/cjs/helpers/platform.js +59 -0
  6. package/lib/cjs/helpers/platform.js.map +1 -0
  7. package/lib/cjs/kbd/kbd-utils.js +6 -22
  8. package/lib/cjs/kbd/kbd-utils.js.map +1 -1
  9. package/lib/cjs/next/tree-view/TreeView.js +384 -66
  10. package/lib/cjs/next/tree-view/TreeView.js.map +1 -1
  11. package/lib/cjs/next/tree-view/selection.js +181 -0
  12. package/lib/cjs/next/tree-view/selection.js.map +1 -0
  13. package/lib/cjs/next/tree-view/stories/index.js +13 -1
  14. package/lib/cjs/next/tree-view/stories/index.js.map +1 -1
  15. package/lib/cjs/next/tree-view/stories/tree-view-large.story.js +57 -0
  16. package/lib/cjs/next/tree-view/stories/tree-view-large.story.js.map +1 -0
  17. package/lib/cjs/next/tree-view/stories/tree-view-lazy.story.js +1 -0
  18. package/lib/cjs/next/tree-view/stories/tree-view-lazy.story.js.map +1 -1
  19. package/lib/cjs/next/tree-view/stories/tree-view-multi.story.js +16 -40
  20. package/lib/cjs/next/tree-view/stories/tree-view-multi.story.js.map +1 -1
  21. package/lib/cjs/next/tree-view/stories/tree-view-multiple-replace.story.js +38 -0
  22. package/lib/cjs/next/tree-view/stories/tree-view-multiple-replace.story.js.map +1 -0
  23. package/lib/cjs/next/tree-view/stories/tree-view-non-selectable-folders.story.js +7 -4
  24. package/lib/cjs/next/tree-view/stories/tree-view-non-selectable-folders.story.js.map +1 -1
  25. package/lib/cjs/next/tree-view/stories/tree-view-root-selection.story.js +125 -0
  26. package/lib/cjs/next/tree-view/stories/tree-view-root-selection.story.js.map +1 -0
  27. package/lib/cjs/toast/ToastControlled.js +1 -1
  28. package/lib/cjs/toast/ToastControlled.js.map +1 -1
  29. package/lib/esm/dropzone/stories/dropzone-full.story.js +5 -5
  30. package/lib/esm/dropzone/stories/dropzone-full.story.js.map +1 -1
  31. package/lib/esm/helpers/index.js +1 -0
  32. package/lib/esm/helpers/index.js.map +1 -1
  33. package/lib/esm/helpers/platform.js +55 -0
  34. package/lib/esm/helpers/platform.js.map +1 -0
  35. package/lib/esm/kbd/kbd-utils.js +2 -18
  36. package/lib/esm/kbd/kbd-utils.js.map +1 -1
  37. package/lib/esm/next/tree-view/TreeView.js +385 -67
  38. package/lib/esm/next/tree-view/TreeView.js.map +1 -1
  39. package/lib/esm/next/tree-view/selection.js +175 -0
  40. package/lib/esm/next/tree-view/selection.js.map +1 -0
  41. package/lib/esm/next/tree-view/stories/index.js +9 -0
  42. package/lib/esm/next/tree-view/stories/index.js.map +1 -1
  43. package/lib/esm/next/tree-view/stories/tree-view-large.story.js +55 -0
  44. package/lib/esm/next/tree-view/stories/tree-view-large.story.js.map +1 -0
  45. package/lib/esm/next/tree-view/stories/tree-view-lazy.story.js +1 -0
  46. package/lib/esm/next/tree-view/stories/tree-view-lazy.story.js.map +1 -1
  47. package/lib/esm/next/tree-view/stories/tree-view-multi.story.js +16 -40
  48. package/lib/esm/next/tree-view/stories/tree-view-multi.story.js.map +1 -1
  49. package/lib/esm/next/tree-view/stories/tree-view-multiple-replace.story.js +36 -0
  50. package/lib/esm/next/tree-view/stories/tree-view-multiple-replace.story.js.map +1 -0
  51. package/lib/esm/next/tree-view/stories/tree-view-non-selectable-folders.story.js +7 -4
  52. package/lib/esm/next/tree-view/stories/tree-view-non-selectable-folders.story.js.map +1 -1
  53. package/lib/esm/next/tree-view/stories/tree-view-root-selection.story.js +123 -0
  54. package/lib/esm/next/tree-view/stories/tree-view-root-selection.story.js.map +1 -0
  55. package/lib/esm/toast/ToastControlled.js +1 -1
  56. package/lib/esm/toast/ToastControlled.js.map +1 -1
  57. package/lib/types/helpers/index.d.ts +1 -0
  58. package/lib/types/helpers/index.d.ts.map +1 -1
  59. package/lib/types/helpers/platform.d.ts +29 -0
  60. package/lib/types/helpers/platform.d.ts.map +1 -0
  61. package/lib/types/kbd/kbd-utils.d.ts +2 -5
  62. package/lib/types/kbd/kbd-utils.d.ts.map +1 -1
  63. package/lib/types/next/tree-view/TreeView.d.ts +29 -5
  64. package/lib/types/next/tree-view/TreeView.d.ts.map +1 -1
  65. package/lib/types/next/tree-view/selection.d.ts +78 -0
  66. package/lib/types/next/tree-view/selection.d.ts.map +1 -0
  67. package/lib/types/next/tree-view/stories/index.d.ts +6 -0
  68. package/lib/types/next/tree-view/stories/index.d.ts.map +1 -1
  69. package/lib/types/next/tree-view/stories/tree-view-large.story.d.ts +24 -0
  70. package/lib/types/next/tree-view/stories/tree-view-large.story.d.ts.map +1 -0
  71. package/lib/types/next/tree-view/stories/tree-view-lazy.story.d.ts.map +1 -1
  72. package/lib/types/next/tree-view/stories/tree-view-multi.story.d.ts +10 -0
  73. package/lib/types/next/tree-view/stories/tree-view-multi.story.d.ts.map +1 -1
  74. package/lib/types/next/tree-view/stories/tree-view-multiple-replace.story.d.ts +29 -0
  75. package/lib/types/next/tree-view/stories/tree-view-multiple-replace.story.d.ts.map +1 -0
  76. package/lib/types/next/tree-view/stories/tree-view-non-selectable-folders.story.d.ts +5 -0
  77. package/lib/types/next/tree-view/stories/tree-view-non-selectable-folders.story.d.ts.map +1 -1
  78. package/lib/types/next/tree-view/stories/tree-view-root-selection.story.d.ts +24 -0
  79. package/lib/types/next/tree-view/stories/tree-view-root-selection.story.d.ts.map +1 -0
  80. package/package.json +2 -2
  81. package/skills/ndl-react/SKILL.md +1 -1
  82. package/skills/ndl-react/components/next/tree-view.md +358 -104
@@ -137,6 +137,7 @@ function renderNode(node, ctx) {
137
137
  node.children.length > 0;
138
138
  const state = ctx.getNodeState(node.id);
139
139
  return ((0, jsx_runtime_1.jsxs)(next_1.TreeView.Item, { title: node.label, hasChildren: hasChildren, isExpanded: state.isExpanded, isLoading: state.isLoading, isSelected: ctx.selectedId === node.id, leadingVisual: iconForNode(node), onExpandedChange: (expanded) => ctx.handleExpand(node.id, expanded), onSelectedChange: () => ctx.handleSelect(node.id), children: [hasChildren && state.isLoading && !state.hasLoaded && ((0, jsx_runtime_1.jsx)(next_1.TreeView.SkeletonItem, { rows: 2 })), hasChildren &&
140
+ state.isExpanded &&
140
141
  state.hasLoaded &&
141
142
  node.children.map((child) => renderNode(child, ctx))] }, node.id));
142
143
  }
@@ -1 +1 @@
1
- {"version":3,"file":"tree-view-lazy.story.js","sourceRoot":"","sources":["../../../../../src/next/tree-view/stories/tree-view-lazy.story.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n *\n * Copyright (c) \"Neo4j\"\n * Neo4j Sweden AB [http://neo4j.com]\n *\n * This file is part of Neo4j.\n *\n * Neo4j is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\nimport '@neo4j-ndl/base/lib/neo4j-ds-styles.css';\n\nimport {\n DocumentIconOutline,\n FolderIconSolid,\n PhotoIconOutline,\n} from '@neo4j-ndl/react/icons';\nimport { TreeView } from '@neo4j-ndl/react/next';\nimport { useCallback, useMemo, useRef, useState } from 'react';\n\ntype TreeNode = {\n id: string;\n label: string;\n children?: TreeNode[];\n};\n\nconst TREE_DATA: TreeNode[] = [\n {\n children: [\n {\n children: [\n { id: 'app-tsx', label: 'App.tsx' },\n { id: 'index-tsx', label: 'index.tsx' },\n { id: 'styles-css', label: 'styles.css' },\n ],\n id: 'frontend',\n label: 'Frontend',\n },\n {\n children: [\n { id: 'server-ts', label: 'server.ts' },\n { id: 'routes-ts', label: 'routes.ts' },\n ],\n id: 'backend',\n label: 'Backend',\n },\n { id: 'readme-md', label: 'README.md' },\n ],\n id: 'projects',\n label: 'Projects',\n },\n {\n children: [\n {\n children: [\n { id: 'beach-jpg', label: 'beach.jpg' },\n { id: 'sunset-jpg', label: 'sunset.jpg' },\n ],\n id: 'vacation',\n label: 'Vacation',\n },\n { id: 'profile-png', label: 'profile.png' },\n ],\n id: 'photos',\n label: 'Photos',\n },\n {\n children: [\n { id: 'resume-pdf', label: 'resume.pdf' },\n { id: 'cover-letter-pdf', label: 'cover-letter.pdf' },\n { id: 'notes-txt', label: 'notes.txt' },\n ],\n id: 'documents',\n label: 'Documents',\n },\n {\n children: [\n {\n children: [\n { id: 'chill-m3u', label: 'chill.m3u' },\n { id: 'workout-m3u', label: 'workout.m3u' },\n ],\n id: 'playlists',\n label: 'Playlists',\n },\n { id: 'favorites-m3u', label: 'favorites.m3u' },\n ],\n id: 'music',\n label: 'Music',\n },\n { id: 'todo-txt', label: 'TODO.txt' },\n];\n\nconst LOAD_DELAY_MS = 600;\n\nfunction iconForNode(node: TreeNode) {\n if (node.children) {\n return <FolderIconSolid />;\n }\n if (/\\.(jpg|jpeg|png|gif|svg)$/i.test(node.label)) {\n return <PhotoIconOutline />;\n }\n return <DocumentIconOutline />;\n}\n\ntype NodeState = {\n isExpanded: boolean;\n isLoading: boolean;\n hasLoaded: boolean;\n};\n\nconst INITIAL_NODE_STATE: NodeState = {\n hasLoaded: false,\n isExpanded: false,\n isLoading: false,\n};\n\nfunction useTreeState() {\n const [nodeStates, setNodeStates] = useState<Record<string, NodeState>>({});\n const [selectedId, setSelectedId] = useState<string | null>(null);\n const timers = useRef<Record<string, ReturnType<typeof setTimeout>>>({});\n\n const getNodeState = useCallback(\n (id: string): NodeState => nodeStates[id] ?? INITIAL_NODE_STATE,\n [nodeStates],\n );\n\n const handleExpand = useCallback((id: string, expanded: boolean) => {\n setNodeStates((prev) => {\n const current = prev[id] ?? INITIAL_NODE_STATE;\n if (expanded && !current.hasLoaded) {\n if (timers.current[id]) {\n clearTimeout(timers.current[id]);\n }\n timers.current[id] = setTimeout(() => {\n setNodeStates((prev) => ({\n ...prev,\n [id]: { hasLoaded: true, isExpanded: true, isLoading: false },\n }));\n delete timers.current[id];\n }, LOAD_DELAY_MS);\n return {\n ...prev,\n [id]: { hasLoaded: false, isExpanded: true, isLoading: true },\n };\n }\n return { ...prev, [id]: { ...current, isExpanded: expanded } };\n });\n }, []);\n\n const handleSelect = useCallback(\n (id: string) => setSelectedId((prev) => (prev === id ? null : id)),\n [],\n );\n\n return { getNodeState, handleExpand, handleSelect, selectedId };\n}\n\ntype RenderContext = {\n getNodeState: (id: string) => NodeState;\n selectedId: string | null;\n handleExpand: (id: string, expanded: boolean) => void;\n handleSelect: (id: string) => void;\n};\n\nfunction renderNode(node: TreeNode, ctx: RenderContext) {\n const hasChildren =\n node.children !== undefined &&\n node.children !== null &&\n node.children.length > 0;\n const state = ctx.getNodeState(node.id);\n\n return (\n <TreeView.Item\n key={node.id}\n title={node.label}\n hasChildren={hasChildren}\n isExpanded={state.isExpanded}\n isLoading={state.isLoading}\n isSelected={ctx.selectedId === node.id}\n leadingVisual={iconForNode(node)}\n onExpandedChange={(expanded: boolean) =>\n ctx.handleExpand(node.id, expanded)\n }\n onSelectedChange={() => ctx.handleSelect(node.id)}\n >\n {hasChildren && state.isLoading && !state.hasLoaded && (\n <TreeView.SkeletonItem rows={2} />\n )}\n {hasChildren &&\n state.hasLoaded &&\n node.children!.map((child) => renderNode(child, ctx))}\n </TreeView.Item>\n );\n}\n\nconst Component = () => {\n const { getNodeState, selectedId, handleExpand, handleSelect } =\n useTreeState();\n\n const ctx = useMemo<RenderContext>(\n () => ({ getNodeState, handleExpand, handleSelect, selectedId }),\n [getNodeState, selectedId, handleExpand, handleSelect],\n );\n\n return (\n <TreeView ariaLabel=\"Lazy-loaded file explorer\">\n {TREE_DATA.map((node) => renderNode(node, ctx))}\n </TreeView>\n );\n};\n\nexport default Component;\n"]}
1
+ {"version":3,"file":"tree-view-lazy.story.js","sourceRoot":"","sources":["../../../../../src/next/tree-view/stories/tree-view-lazy.story.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n *\n * Copyright (c) \"Neo4j\"\n * Neo4j Sweden AB [http://neo4j.com]\n *\n * This file is part of Neo4j.\n *\n * Neo4j is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\nimport '@neo4j-ndl/base/lib/neo4j-ds-styles.css';\n\nimport {\n DocumentIconOutline,\n FolderIconSolid,\n PhotoIconOutline,\n} from '@neo4j-ndl/react/icons';\nimport { TreeView } from '@neo4j-ndl/react/next';\nimport { useCallback, useMemo, useRef, useState } from 'react';\n\ntype TreeNode = {\n id: string;\n label: string;\n children?: TreeNode[];\n};\n\nconst TREE_DATA: TreeNode[] = [\n {\n children: [\n {\n children: [\n { id: 'app-tsx', label: 'App.tsx' },\n { id: 'index-tsx', label: 'index.tsx' },\n { id: 'styles-css', label: 'styles.css' },\n ],\n id: 'frontend',\n label: 'Frontend',\n },\n {\n children: [\n { id: 'server-ts', label: 'server.ts' },\n { id: 'routes-ts', label: 'routes.ts' },\n ],\n id: 'backend',\n label: 'Backend',\n },\n { id: 'readme-md', label: 'README.md' },\n ],\n id: 'projects',\n label: 'Projects',\n },\n {\n children: [\n {\n children: [\n { id: 'beach-jpg', label: 'beach.jpg' },\n { id: 'sunset-jpg', label: 'sunset.jpg' },\n ],\n id: 'vacation',\n label: 'Vacation',\n },\n { id: 'profile-png', label: 'profile.png' },\n ],\n id: 'photos',\n label: 'Photos',\n },\n {\n children: [\n { id: 'resume-pdf', label: 'resume.pdf' },\n { id: 'cover-letter-pdf', label: 'cover-letter.pdf' },\n { id: 'notes-txt', label: 'notes.txt' },\n ],\n id: 'documents',\n label: 'Documents',\n },\n {\n children: [\n {\n children: [\n { id: 'chill-m3u', label: 'chill.m3u' },\n { id: 'workout-m3u', label: 'workout.m3u' },\n ],\n id: 'playlists',\n label: 'Playlists',\n },\n { id: 'favorites-m3u', label: 'favorites.m3u' },\n ],\n id: 'music',\n label: 'Music',\n },\n { id: 'todo-txt', label: 'TODO.txt' },\n];\n\nconst LOAD_DELAY_MS = 600;\n\nfunction iconForNode(node: TreeNode) {\n if (node.children) {\n return <FolderIconSolid />;\n }\n if (/\\.(jpg|jpeg|png|gif|svg)$/i.test(node.label)) {\n return <PhotoIconOutline />;\n }\n return <DocumentIconOutline />;\n}\n\ntype NodeState = {\n isExpanded: boolean;\n isLoading: boolean;\n hasLoaded: boolean;\n};\n\nconst INITIAL_NODE_STATE: NodeState = {\n hasLoaded: false,\n isExpanded: false,\n isLoading: false,\n};\n\nfunction useTreeState() {\n const [nodeStates, setNodeStates] = useState<Record<string, NodeState>>({});\n const [selectedId, setSelectedId] = useState<string | null>(null);\n const timers = useRef<Record<string, ReturnType<typeof setTimeout>>>({});\n\n const getNodeState = useCallback(\n (id: string): NodeState => nodeStates[id] ?? INITIAL_NODE_STATE,\n [nodeStates],\n );\n\n const handleExpand = useCallback((id: string, expanded: boolean) => {\n setNodeStates((prev) => {\n const current = prev[id] ?? INITIAL_NODE_STATE;\n if (expanded && !current.hasLoaded) {\n if (timers.current[id]) {\n clearTimeout(timers.current[id]);\n }\n timers.current[id] = setTimeout(() => {\n setNodeStates((prev) => ({\n ...prev,\n [id]: { hasLoaded: true, isExpanded: true, isLoading: false },\n }));\n delete timers.current[id];\n }, LOAD_DELAY_MS);\n return {\n ...prev,\n [id]: { hasLoaded: false, isExpanded: true, isLoading: true },\n };\n }\n return { ...prev, [id]: { ...current, isExpanded: expanded } };\n });\n }, []);\n\n const handleSelect = useCallback(\n (id: string) => setSelectedId((prev) => (prev === id ? null : id)),\n [],\n );\n\n return { getNodeState, handleExpand, handleSelect, selectedId };\n}\n\ntype RenderContext = {\n getNodeState: (id: string) => NodeState;\n selectedId: string | null;\n handleExpand: (id: string, expanded: boolean) => void;\n handleSelect: (id: string) => void;\n};\n\nfunction renderNode(node: TreeNode, ctx: RenderContext) {\n const hasChildren =\n node.children !== undefined &&\n node.children !== null &&\n node.children.length > 0;\n const state = ctx.getNodeState(node.id);\n\n return (\n <TreeView.Item\n key={node.id}\n title={node.label}\n hasChildren={hasChildren}\n isExpanded={state.isExpanded}\n isLoading={state.isLoading}\n isSelected={ctx.selectedId === node.id}\n leadingVisual={iconForNode(node)}\n onExpandedChange={(expanded: boolean) =>\n ctx.handleExpand(node.id, expanded)\n }\n onSelectedChange={() => ctx.handleSelect(node.id)}\n >\n {hasChildren && state.isLoading && !state.hasLoaded && (\n <TreeView.SkeletonItem rows={2} />\n )}\n {/* Rendering children only while expanded keeps collapsed subtrees out\n of the DOM, so large trees only pay for the rows the user can see.\n All item state lives outside the tree (keyed by node id), so nothing\n is lost when a subtree unmounts on collapse. */}\n {hasChildren &&\n state.isExpanded &&\n state.hasLoaded &&\n node.children!.map((child) => renderNode(child, ctx))}\n </TreeView.Item>\n );\n}\n\nconst Component = () => {\n const { getNodeState, selectedId, handleExpand, handleSelect } =\n useTreeState();\n\n const ctx = useMemo<RenderContext>(\n () => ({ getNodeState, handleExpand, handleSelect, selectedId }),\n [getNodeState, selectedId, handleExpand, handleSelect],\n );\n\n return (\n <TreeView ariaLabel=\"Lazy-loaded file explorer\">\n {TREE_DATA.map((node) => renderNode(node, ctx))}\n </TreeView>\n );\n};\n\nexport default Component;\n"]}
@@ -25,49 +25,25 @@ require("@neo4j-ndl/base/lib/neo4j-ds-styles.css");
25
25
  const icons_1 = require("@neo4j-ndl/react/icons");
26
26
  const next_1 = require("@neo4j-ndl/react/next");
27
27
  const react_1 = require("react");
28
- const WORK_CHILDREN = ['report', 'presentation'];
29
- const DOC_CHILDREN = ['work', 'resume'];
30
- const IMAGE_CHILDREN = ['photo', 'screenshot'];
31
- const DISABLED_KEYS = new Set(['presentation']);
32
- function getParentChecked(checked, childKeys) {
33
- const enabledKeys = childKeys.filter((key) => !DISABLED_KEYS.has(key));
34
- if (enabledKeys.length === 0) {
35
- return { isChecked: false, isIndeterminate: false };
36
- }
37
- const checkedCount = enabledKeys.filter((key) => checked[key]).length;
38
- if (checkedCount === 0) {
39
- return { isChecked: false, isIndeterminate: false };
40
- }
41
- if (checkedCount === enabledKeys.length) {
42
- return { isChecked: true, isIndeterminate: false };
43
- }
44
- return { isChecked: false, isIndeterminate: true };
45
- }
28
+ /**
29
+ * Every row owns its own checkbox: `onSelectedChange` reports the state the row
30
+ * should end up in, and the story stores exactly that. Folders are ordinary
31
+ * selectable rows here, unrelated to what sits inside them.
32
+ *
33
+ * Deriving a folder's state from its children instead needs the tree to own the
34
+ * selection, since a Shift range moves several rows at once and a cascade has
35
+ * to be applied to the whole set in one pass. See the root-managed selection
36
+ * story for that.
37
+ */
46
38
  const Component = () => {
39
+ var _a, _b, _c, _d, _e, _f, _g, _h;
47
40
  const [expanded, setExpanded] = (0, react_1.useState)({});
48
- const [checked, setChecked] = (0, react_1.useState)({});
49
- const toggle = (key) => setExpanded((prev) => (Object.assign(Object.assign({}, prev), { [key]: !prev[key] })));
50
- const toggleCheck = (0, react_1.useCallback)((key) => {
51
- setChecked((prev) => (Object.assign(Object.assign({}, prev), { [key]: !prev[key] })));
41
+ const [selected, setSelected] = (0, react_1.useState)({});
42
+ const toggleExpanded = (key) => setExpanded((prev) => (Object.assign(Object.assign({}, prev), { [key]: !prev[key] })));
43
+ const setRowSelected = (0, react_1.useCallback)((key, isSelected) => {
44
+ setSelected((prev) => (Object.assign(Object.assign({}, prev), { [key]: isSelected })));
52
45
  }, []);
53
- const toggleParent = (0, react_1.useCallback)((childKeys) => {
54
- const enabledKeys = childKeys.filter((key) => !DISABLED_KEYS.has(key));
55
- const isAllChecked = enabledKeys.every((key) => checked[key]);
56
- setChecked((prev) => {
57
- const next = Object.assign({}, prev);
58
- for (const key of enabledKeys) {
59
- next[key] = !isAllChecked;
60
- }
61
- return next;
62
- });
63
- }, [checked]);
64
- const docState = getParentChecked(checked, [
65
- ...DOC_CHILDREN,
66
- ...WORK_CHILDREN,
67
- ]);
68
- const workState = getParentChecked(checked, WORK_CHILDREN);
69
- const imageState = getParentChecked(checked, IMAGE_CHILDREN);
70
- return ((0, jsx_runtime_1.jsxs)(next_1.TreeView, { ariaLabel: "File explorer", selectionMode: "multiple", children: [(0, jsx_runtime_1.jsxs)(next_1.TreeView.Item, { title: "Documents", hasChildren: true, isExpanded: expanded['documents'], onExpandedChange: () => toggle('documents'), isSelected: docState.isChecked, isIndeterminate: docState.isIndeterminate, onSelectedChange: () => toggleParent([...DOC_CHILDREN, ...WORK_CHILDREN]), leadingVisual: (0, jsx_runtime_1.jsx)(icons_1.FolderIconSolid, {}), children: [(0, jsx_runtime_1.jsxs)(next_1.TreeView.Item, { title: "Work", hasChildren: true, isExpanded: expanded['work'], onExpandedChange: () => toggle('work'), isSelected: workState.isChecked, isIndeterminate: workState.isIndeterminate, onSelectedChange: () => toggleParent(WORK_CHILDREN), leadingVisual: (0, jsx_runtime_1.jsx)(icons_1.FolderIconSolid, {}), children: [(0, jsx_runtime_1.jsx)(next_1.TreeView.Item, { title: "Report.pdf", isSelected: !!checked['report'], onSelectedChange: () => toggleCheck('report'), leadingVisual: (0, jsx_runtime_1.jsx)(icons_1.DocumentIconOutline, {}) }), (0, jsx_runtime_1.jsx)(next_1.TreeView.Item, { title: "Presentation.pptx", isDisabled: true, onSelectedChange: () => null, leadingVisual: (0, jsx_runtime_1.jsx)(icons_1.DocumentIconOutline, {}) })] }), (0, jsx_runtime_1.jsx)(next_1.TreeView.Item, { title: "Resume.pdf", isSelected: !!checked['resume'], onSelectedChange: () => toggleCheck('resume'), leadingVisual: (0, jsx_runtime_1.jsx)(icons_1.DocumentIconOutline, {}) })] }), (0, jsx_runtime_1.jsxs)(next_1.TreeView.Item, { title: "Images", hasChildren: true, isExpanded: expanded['images'], onExpandedChange: () => toggle('images'), isSelected: imageState.isChecked, isIndeterminate: imageState.isIndeterminate, onSelectedChange: () => toggleParent(IMAGE_CHILDREN), leadingVisual: (0, jsx_runtime_1.jsx)(icons_1.FolderIconSolid, {}), children: [(0, jsx_runtime_1.jsx)(next_1.TreeView.Item, { title: "Photo.jpg", isSelected: !!checked['photo'], onSelectedChange: () => toggleCheck('photo'), leadingVisual: (0, jsx_runtime_1.jsx)(icons_1.PhotoIconOutline, {}) }), (0, jsx_runtime_1.jsx)(next_1.TreeView.Item, { title: "Screenshot.png", isSelected: !!checked['screenshot'], onSelectedChange: () => toggleCheck('screenshot'), leadingVisual: (0, jsx_runtime_1.jsx)(icons_1.PhotoIconOutline, {}) })] })] }));
46
+ return ((0, jsx_runtime_1.jsxs)(next_1.TreeView, { ariaLabel: "File explorer", selectionMode: "multiple", children: [(0, jsx_runtime_1.jsxs)(next_1.TreeView.Item, { title: "Documents", hasChildren: true, isExpanded: expanded['documents'], onExpandedChange: () => toggleExpanded('documents'), isSelected: (_a = selected['documents']) !== null && _a !== void 0 ? _a : false, onSelectedChange: (isSelected) => setRowSelected('documents', isSelected), leadingVisual: (0, jsx_runtime_1.jsx)(icons_1.FolderIconSolid, {}), children: [(0, jsx_runtime_1.jsxs)(next_1.TreeView.Item, { title: "Work", hasChildren: true, isExpanded: expanded['work'], onExpandedChange: () => toggleExpanded('work'), isSelected: (_b = selected['work']) !== null && _b !== void 0 ? _b : false, onSelectedChange: (isSelected) => setRowSelected('work', isSelected), leadingVisual: (0, jsx_runtime_1.jsx)(icons_1.FolderIconSolid, {}), children: [(0, jsx_runtime_1.jsx)(next_1.TreeView.Item, { title: "Report.pdf", isSelected: (_c = selected['report']) !== null && _c !== void 0 ? _c : false, onSelectedChange: (isSelected) => setRowSelected('report', isSelected), leadingVisual: (0, jsx_runtime_1.jsx)(icons_1.DocumentIconOutline, {}) }), (0, jsx_runtime_1.jsx)(next_1.TreeView.Item, { title: "Presentation.pptx", isDisabled: true, isSelected: (_d = selected['presentation']) !== null && _d !== void 0 ? _d : false, onSelectedChange: (isSelected) => setRowSelected('presentation', isSelected), leadingVisual: (0, jsx_runtime_1.jsx)(icons_1.DocumentIconOutline, {}) })] }), (0, jsx_runtime_1.jsx)(next_1.TreeView.Item, { title: "Resume.pdf", isSelected: (_e = selected['resume']) !== null && _e !== void 0 ? _e : false, onSelectedChange: (isSelected) => setRowSelected('resume', isSelected), leadingVisual: (0, jsx_runtime_1.jsx)(icons_1.DocumentIconOutline, {}) })] }), (0, jsx_runtime_1.jsxs)(next_1.TreeView.Item, { title: "Images", hasChildren: true, isExpanded: expanded['images'], onExpandedChange: () => toggleExpanded('images'), isSelected: (_f = selected['images']) !== null && _f !== void 0 ? _f : false, onSelectedChange: (isSelected) => setRowSelected('images', isSelected), leadingVisual: (0, jsx_runtime_1.jsx)(icons_1.FolderIconSolid, {}), children: [(0, jsx_runtime_1.jsx)(next_1.TreeView.Item, { title: "Photo.jpg", isSelected: (_g = selected['photo']) !== null && _g !== void 0 ? _g : false, onSelectedChange: (isSelected) => setRowSelected('photo', isSelected), leadingVisual: (0, jsx_runtime_1.jsx)(icons_1.PhotoIconOutline, {}) }), (0, jsx_runtime_1.jsx)(next_1.TreeView.Item, { title: "Screenshot.png", isSelected: (_h = selected['screenshot']) !== null && _h !== void 0 ? _h : false, onSelectedChange: (isSelected) => setRowSelected('screenshot', isSelected), leadingVisual: (0, jsx_runtime_1.jsx)(icons_1.PhotoIconOutline, {}) })] })] }));
71
47
  };
72
48
  exports.default = Component;
73
49
  //# sourceMappingURL=tree-view-multi.story.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tree-view-multi.story.js","sourceRoot":"","sources":["../../../../../src/next/tree-view/stories/tree-view-multi.story.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n *\n * Copyright (c) \"Neo4j\"\n * Neo4j Sweden AB [http://neo4j.com]\n *\n * This file is part of Neo4j.\n *\n * Neo4j is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\nimport '@neo4j-ndl/base/lib/neo4j-ds-styles.css';\n\nimport {\n DocumentIconOutline,\n FolderIconSolid,\n PhotoIconOutline,\n} from '@neo4j-ndl/react/icons';\nimport { TreeView } from '@neo4j-ndl/react/next';\nimport { useCallback, useState } from 'react';\n\ntype CheckedState = Record<string, boolean>;\n\nconst WORK_CHILDREN = ['report', 'presentation'] as const;\nconst DOC_CHILDREN = ['work', 'resume'] as const;\nconst IMAGE_CHILDREN = ['photo', 'screenshot'] as const;\nconst DISABLED_KEYS = new Set(['presentation']);\n\nfunction getParentChecked(\n checked: CheckedState,\n childKeys: readonly string[],\n): { isChecked: boolean; isIndeterminate: boolean } {\n const enabledKeys = childKeys.filter((key) => !DISABLED_KEYS.has(key));\n if (enabledKeys.length === 0) {\n return { isChecked: false, isIndeterminate: false };\n }\n const checkedCount = enabledKeys.filter((key) => checked[key]).length;\n if (checkedCount === 0) {\n return { isChecked: false, isIndeterminate: false };\n }\n if (checkedCount === enabledKeys.length) {\n return { isChecked: true, isIndeterminate: false };\n }\n return { isChecked: false, isIndeterminate: true };\n}\n\nconst Component = () => {\n const [expanded, setExpanded] = useState<Record<string, boolean>>({});\n const [checked, setChecked] = useState<CheckedState>({});\n\n const toggle = (key: string) =>\n setExpanded((prev) => ({ ...prev, [key]: !prev[key] }));\n\n const toggleCheck = useCallback((key: string) => {\n setChecked((prev) => ({ ...prev, [key]: !prev[key] }));\n }, []);\n\n const toggleParent = useCallback(\n (childKeys: readonly string[]) => {\n const enabledKeys = childKeys.filter((key) => !DISABLED_KEYS.has(key));\n const isAllChecked = enabledKeys.every((key) => checked[key]);\n setChecked((prev) => {\n const next = { ...prev };\n for (const key of enabledKeys) {\n next[key] = !isAllChecked;\n }\n return next;\n });\n },\n [checked],\n );\n\n const docState = getParentChecked(checked, [\n ...DOC_CHILDREN,\n ...WORK_CHILDREN,\n ]);\n const workState = getParentChecked(checked, WORK_CHILDREN);\n const imageState = getParentChecked(checked, IMAGE_CHILDREN);\n\n return (\n <TreeView ariaLabel=\"File explorer\" selectionMode=\"multiple\">\n <TreeView.Item\n title=\"Documents\"\n hasChildren\n isExpanded={expanded['documents']}\n onExpandedChange={() => toggle('documents')}\n isSelected={docState.isChecked}\n isIndeterminate={docState.isIndeterminate}\n onSelectedChange={() =>\n toggleParent([...DOC_CHILDREN, ...WORK_CHILDREN])\n }\n leadingVisual={<FolderIconSolid />}\n >\n <TreeView.Item\n title=\"Work\"\n hasChildren\n isExpanded={expanded['work']}\n onExpandedChange={() => toggle('work')}\n isSelected={workState.isChecked}\n isIndeterminate={workState.isIndeterminate}\n onSelectedChange={() => toggleParent(WORK_CHILDREN)}\n leadingVisual={<FolderIconSolid />}\n >\n <TreeView.Item\n title=\"Report.pdf\"\n isSelected={!!checked['report']}\n onSelectedChange={() => toggleCheck('report')}\n leadingVisual={<DocumentIconOutline />}\n />\n <TreeView.Item\n title=\"Presentation.pptx\"\n isDisabled\n onSelectedChange={() => null}\n leadingVisual={<DocumentIconOutline />}\n />\n </TreeView.Item>\n <TreeView.Item\n title=\"Resume.pdf\"\n isSelected={!!checked['resume']}\n onSelectedChange={() => toggleCheck('resume')}\n leadingVisual={<DocumentIconOutline />}\n />\n </TreeView.Item>\n <TreeView.Item\n title=\"Images\"\n hasChildren\n isExpanded={expanded['images']}\n onExpandedChange={() => toggle('images')}\n isSelected={imageState.isChecked}\n isIndeterminate={imageState.isIndeterminate}\n onSelectedChange={() => toggleParent(IMAGE_CHILDREN)}\n leadingVisual={<FolderIconSolid />}\n >\n <TreeView.Item\n title=\"Photo.jpg\"\n isSelected={!!checked['photo']}\n onSelectedChange={() => toggleCheck('photo')}\n leadingVisual={<PhotoIconOutline />}\n />\n <TreeView.Item\n title=\"Screenshot.png\"\n isSelected={!!checked['screenshot']}\n onSelectedChange={() => toggleCheck('screenshot')}\n leadingVisual={<PhotoIconOutline />}\n />\n </TreeView.Item>\n </TreeView>\n );\n};\n\nexport default Component;\n"]}
1
+ {"version":3,"file":"tree-view-multi.story.js","sourceRoot":"","sources":["../../../../../src/next/tree-view/stories/tree-view-multi.story.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n *\n * Copyright (c) \"Neo4j\"\n * Neo4j Sweden AB [http://neo4j.com]\n *\n * This file is part of Neo4j.\n *\n * Neo4j is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\nimport '@neo4j-ndl/base/lib/neo4j-ds-styles.css';\n\nimport {\n DocumentIconOutline,\n FolderIconSolid,\n PhotoIconOutline,\n} from '@neo4j-ndl/react/icons';\nimport { TreeView } from '@neo4j-ndl/react/next';\nimport { useCallback, useState } from 'react';\n\n/**\n * Every row owns its own checkbox: `onSelectedChange` reports the state the row\n * should end up in, and the story stores exactly that. Folders are ordinary\n * selectable rows here, unrelated to what sits inside them.\n *\n * Deriving a folder's state from its children instead needs the tree to own the\n * selection, since a Shift range moves several rows at once and a cascade has\n * to be applied to the whole set in one pass. See the root-managed selection\n * story for that.\n */\nconst Component = () => {\n const [expanded, setExpanded] = useState<Record<string, boolean>>({});\n const [selected, setSelected] = useState<Record<string, boolean>>({});\n\n const toggleExpanded = (key: string) =>\n setExpanded((prev) => ({ ...prev, [key]: !prev[key] }));\n\n const setRowSelected = useCallback((key: string, isSelected: boolean) => {\n setSelected((prev) => ({ ...prev, [key]: isSelected }));\n }, []);\n\n return (\n <TreeView ariaLabel=\"File explorer\" selectionMode=\"multiple\">\n <TreeView.Item\n title=\"Documents\"\n hasChildren\n isExpanded={expanded['documents']}\n onExpandedChange={() => toggleExpanded('documents')}\n isSelected={selected['documents'] ?? false}\n onSelectedChange={(isSelected) =>\n setRowSelected('documents', isSelected)\n }\n leadingVisual={<FolderIconSolid />}\n >\n <TreeView.Item\n title=\"Work\"\n hasChildren\n isExpanded={expanded['work']}\n onExpandedChange={() => toggleExpanded('work')}\n isSelected={selected['work'] ?? false}\n onSelectedChange={(isSelected) => setRowSelected('work', isSelected)}\n leadingVisual={<FolderIconSolid />}\n >\n <TreeView.Item\n title=\"Report.pdf\"\n isSelected={selected['report'] ?? false}\n onSelectedChange={(isSelected) =>\n setRowSelected('report', isSelected)\n }\n leadingVisual={<DocumentIconOutline />}\n />\n <TreeView.Item\n title=\"Presentation.pptx\"\n isDisabled\n isSelected={selected['presentation'] ?? false}\n onSelectedChange={(isSelected) =>\n setRowSelected('presentation', isSelected)\n }\n leadingVisual={<DocumentIconOutline />}\n />\n </TreeView.Item>\n <TreeView.Item\n title=\"Resume.pdf\"\n isSelected={selected['resume'] ?? false}\n onSelectedChange={(isSelected) =>\n setRowSelected('resume', isSelected)\n }\n leadingVisual={<DocumentIconOutline />}\n />\n </TreeView.Item>\n <TreeView.Item\n title=\"Images\"\n hasChildren\n isExpanded={expanded['images']}\n onExpandedChange={() => toggleExpanded('images')}\n isSelected={selected['images'] ?? false}\n onSelectedChange={(isSelected) => setRowSelected('images', isSelected)}\n leadingVisual={<FolderIconSolid />}\n >\n <TreeView.Item\n title=\"Photo.jpg\"\n isSelected={selected['photo'] ?? false}\n onSelectedChange={(isSelected) => setRowSelected('photo', isSelected)}\n leadingVisual={<PhotoIconOutline />}\n />\n <TreeView.Item\n title=\"Screenshot.png\"\n isSelected={selected['screenshot'] ?? false}\n onSelectedChange={(isSelected) =>\n setRowSelected('screenshot', isSelected)\n }\n leadingVisual={<PhotoIconOutline />}\n />\n </TreeView.Item>\n </TreeView>\n );\n};\n\nexport default Component;\n"]}
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const jsx_runtime_1 = require("react/jsx-runtime");
4
+ /**
5
+ *
6
+ * Copyright (c) "Neo4j"
7
+ * Neo4j Sweden AB [http://neo4j.com]
8
+ *
9
+ * This file is part of Neo4j.
10
+ *
11
+ * Neo4j is free software: you can redistribute it and/or modify
12
+ * it under the terms of the GNU General Public License as published by
13
+ * the Free Software Foundation, either version 3 of the License, or
14
+ * (at your option) any later version.
15
+ *
16
+ * This program is distributed in the hope that it will be useful,
17
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
+ * GNU General Public License for more details.
20
+ *
21
+ * You should have received a copy of the GNU General Public License
22
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
23
+ */
24
+ require("@neo4j-ndl/base/lib/neo4j-ds-styles.css");
25
+ const icons_1 = require("@neo4j-ndl/react/icons");
26
+ const next_1 = require("@neo4j-ndl/react/next");
27
+ const react_1 = require("react");
28
+ /**
29
+ * Letting the tree own the selection keeps the items declarative: they carry an
30
+ * `id` and nothing else, and a Shift range that moves several rows at once
31
+ * arrives as a single `onSelectedIdsChange`.
32
+ */
33
+ const Component = () => {
34
+ const [selectedIds, setSelectedIds] = (0, react_1.useState)([]);
35
+ return ((0, jsx_runtime_1.jsxs)(next_1.TreeView, { ariaLabel: "File explorer", selectionMode: "multiple-replace", selectedIds: selectedIds, onSelectedIdsChange: setSelectedIds, children: [(0, jsx_runtime_1.jsxs)(next_1.TreeView.Item, { id: "documents", title: "Documents", hasChildren: true, defaultExpanded: true, leadingVisual: (0, jsx_runtime_1.jsx)(icons_1.FolderIconSolid, {}), children: [(0, jsx_runtime_1.jsxs)(next_1.TreeView.Item, { id: "work", title: "Work", hasChildren: true, defaultExpanded: true, leadingVisual: (0, jsx_runtime_1.jsx)(icons_1.FolderIconSolid, {}), children: [(0, jsx_runtime_1.jsx)(next_1.TreeView.Item, { id: "report", title: "Report.pdf", isDisabled: true, leadingVisual: (0, jsx_runtime_1.jsx)(icons_1.DocumentIconOutline, {}) }), (0, jsx_runtime_1.jsx)(next_1.TreeView.Item, { id: "presentation", title: "Presentation.pptx", leadingVisual: (0, jsx_runtime_1.jsx)(icons_1.DocumentIconOutline, {}) })] }), (0, jsx_runtime_1.jsx)(next_1.TreeView.Item, { id: "resume", title: "Resume.pdf", leadingVisual: (0, jsx_runtime_1.jsx)(icons_1.DocumentIconOutline, {}) })] }), (0, jsx_runtime_1.jsxs)(next_1.TreeView.Item, { id: "images", title: "Images", hasChildren: true, leadingVisual: (0, jsx_runtime_1.jsx)(icons_1.FolderIconSolid, {}), children: [(0, jsx_runtime_1.jsx)(next_1.TreeView.Item, { id: "photo", title: "Photo.jpg", leadingVisual: (0, jsx_runtime_1.jsx)(icons_1.PhotoIconOutline, {}) }), (0, jsx_runtime_1.jsx)(next_1.TreeView.Item, { id: "screenshot", title: "Screenshot.png", leadingVisual: (0, jsx_runtime_1.jsx)(icons_1.PhotoIconOutline, {}) })] })] }));
36
+ };
37
+ exports.default = Component;
38
+ //# sourceMappingURL=tree-view-multiple-replace.story.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tree-view-multiple-replace.story.js","sourceRoot":"","sources":["../../../../../src/next/tree-view/stories/tree-view-multiple-replace.story.tsx"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,mDAAiD;AAEjD,kDAIgC;AAChC,gDAAiD;AACjD,iCAAiC;AAEjC;;;;GAIG;AACH,MAAM,SAAS,GAAG,GAAG,EAAE;IACrB,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,IAAA,gBAAQ,EAAW,EAAE,CAAC,CAAC;IAE7D,OAAO,CACL,wBAAC,eAAQ,IACP,SAAS,EAAC,eAAe,EACzB,aAAa,EAAC,kBAAkB,EAChC,WAAW,EAAE,WAAW,EACxB,mBAAmB,EAAE,cAAc,aAEnC,wBAAC,eAAQ,CAAC,IAAI,IACZ,EAAE,EAAC,WAAW,EACd,KAAK,EAAC,WAAW,EACjB,WAAW,QACX,eAAe,QACf,aAAa,EAAE,uBAAC,uBAAe,KAAG,aAElC,wBAAC,eAAQ,CAAC,IAAI,IACZ,EAAE,EAAC,MAAM,EACT,KAAK,EAAC,MAAM,EACZ,WAAW,QACX,eAAe,QACf,aAAa,EAAE,uBAAC,uBAAe,KAAG,aAElC,uBAAC,eAAQ,CAAC,IAAI,IACZ,EAAE,EAAC,QAAQ,EACX,KAAK,EAAC,YAAY,EAClB,UAAU,QACV,aAAa,EAAE,uBAAC,2BAAmB,KAAG,GACtC,EACF,uBAAC,eAAQ,CAAC,IAAI,IACZ,EAAE,EAAC,cAAc,EACjB,KAAK,EAAC,mBAAmB,EACzB,aAAa,EAAE,uBAAC,2BAAmB,KAAG,GACtC,IACY,EAChB,uBAAC,eAAQ,CAAC,IAAI,IACZ,EAAE,EAAC,QAAQ,EACX,KAAK,EAAC,YAAY,EAClB,aAAa,EAAE,uBAAC,2BAAmB,KAAG,GACtC,IACY,EAChB,wBAAC,eAAQ,CAAC,IAAI,IACZ,EAAE,EAAC,QAAQ,EACX,KAAK,EAAC,QAAQ,EACd,WAAW,QACX,aAAa,EAAE,uBAAC,uBAAe,KAAG,aAElC,uBAAC,eAAQ,CAAC,IAAI,IACZ,EAAE,EAAC,OAAO,EACV,KAAK,EAAC,WAAW,EACjB,aAAa,EAAE,uBAAC,wBAAgB,KAAG,GACnC,EACF,uBAAC,eAAQ,CAAC,IAAI,IACZ,EAAE,EAAC,YAAY,EACf,KAAK,EAAC,gBAAgB,EACtB,aAAa,EAAE,uBAAC,wBAAgB,KAAG,GACnC,IACY,IACP,CACZ,CAAC;AACJ,CAAC,CAAC;AAEF,kBAAe,SAAS,CAAC","sourcesContent":["/**\n *\n * Copyright (c) \"Neo4j\"\n * Neo4j Sweden AB [http://neo4j.com]\n *\n * This file is part of Neo4j.\n *\n * Neo4j is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\nimport '@neo4j-ndl/base/lib/neo4j-ds-styles.css';\n\nimport {\n DocumentIconOutline,\n FolderIconSolid,\n PhotoIconOutline,\n} from '@neo4j-ndl/react/icons';\nimport { TreeView } from '@neo4j-ndl/react/next';\nimport { useState } from 'react';\n\n/**\n * Letting the tree own the selection keeps the items declarative: they carry an\n * `id` and nothing else, and a Shift range that moves several rows at once\n * arrives as a single `onSelectedIdsChange`.\n */\nconst Component = () => {\n const [selectedIds, setSelectedIds] = useState<string[]>([]);\n\n return (\n <TreeView\n ariaLabel=\"File explorer\"\n selectionMode=\"multiple-replace\"\n selectedIds={selectedIds}\n onSelectedIdsChange={setSelectedIds}\n >\n <TreeView.Item\n id=\"documents\"\n title=\"Documents\"\n hasChildren\n defaultExpanded\n leadingVisual={<FolderIconSolid />}\n >\n <TreeView.Item\n id=\"work\"\n title=\"Work\"\n hasChildren\n defaultExpanded\n leadingVisual={<FolderIconSolid />}\n >\n <TreeView.Item\n id=\"report\"\n title=\"Report.pdf\"\n isDisabled\n leadingVisual={<DocumentIconOutline />}\n />\n <TreeView.Item\n id=\"presentation\"\n title=\"Presentation.pptx\"\n leadingVisual={<DocumentIconOutline />}\n />\n </TreeView.Item>\n <TreeView.Item\n id=\"resume\"\n title=\"Resume.pdf\"\n leadingVisual={<DocumentIconOutline />}\n />\n </TreeView.Item>\n <TreeView.Item\n id=\"images\"\n title=\"Images\"\n hasChildren\n leadingVisual={<FolderIconSolid />}\n >\n <TreeView.Item\n id=\"photo\"\n title=\"Photo.jpg\"\n leadingVisual={<PhotoIconOutline />}\n />\n <TreeView.Item\n id=\"screenshot\"\n title=\"Screenshot.png\"\n leadingVisual={<PhotoIconOutline />}\n />\n </TreeView.Item>\n </TreeView>\n );\n};\n\nexport default Component;\n"]}
@@ -25,11 +25,14 @@ require("@neo4j-ndl/base/lib/neo4j-ds-styles.css");
25
25
  const icons_1 = require("@neo4j-ndl/react/icons");
26
26
  const next_1 = require("@neo4j-ndl/react/next");
27
27
  const react_1 = require("react");
28
+ /**
29
+ * Folders here are pure containers: `isSelectable={false}` makes them expand on
30
+ * click without ever becoming part of the selection, and screen readers
31
+ * announce them as non-selectable rather than as unselected rows.
32
+ */
28
33
  const Component = () => {
29
- const [expanded, setExpanded] = (0, react_1.useState)({});
30
- const [selected, setSelected] = (0, react_1.useState)(null);
31
- const toggle = (key) => setExpanded((prev) => (Object.assign(Object.assign({}, prev), { [key]: !prev[key] })));
32
- return ((0, jsx_runtime_1.jsxs)(next_1.TreeView, { ariaLabel: "File explorer", children: [(0, jsx_runtime_1.jsxs)(next_1.TreeView.Item, { title: "Documents", hasChildren: true, isExpanded: expanded['documents'], onExpandedChange: () => toggle('documents'), leadingVisual: (0, jsx_runtime_1.jsx)(icons_1.FolderIconSolid, {}), children: [(0, jsx_runtime_1.jsxs)(next_1.TreeView.Item, { title: "Work", hasChildren: true, isExpanded: expanded['work'], onExpandedChange: () => toggle('work'), leadingVisual: (0, jsx_runtime_1.jsx)(icons_1.FolderIconSolid, {}), children: [(0, jsx_runtime_1.jsx)(next_1.TreeView.Item, { title: "Report.pdf", isSelected: selected === 'report', onSelectedChange: () => setSelected('report'), leadingVisual: (0, jsx_runtime_1.jsx)(icons_1.DocumentIconOutline, {}) }), (0, jsx_runtime_1.jsx)(next_1.TreeView.Item, { title: "Presentation.pptx", isSelected: selected === 'presentation', onSelectedChange: () => setSelected('presentation'), leadingVisual: (0, jsx_runtime_1.jsx)(icons_1.DocumentIconOutline, {}) })] }), (0, jsx_runtime_1.jsx)(next_1.TreeView.Item, { title: "Resume.pdf", isSelected: selected === 'resume', onSelectedChange: () => setSelected('resume'), leadingVisual: (0, jsx_runtime_1.jsx)(icons_1.DocumentIconOutline, {}) })] }), (0, jsx_runtime_1.jsxs)(next_1.TreeView.Item, { title: "Images", hasChildren: true, isExpanded: expanded['images'], onExpandedChange: () => toggle('images'), leadingVisual: (0, jsx_runtime_1.jsx)(icons_1.FolderIconSolid, {}), children: [(0, jsx_runtime_1.jsx)(next_1.TreeView.Item, { title: "Photo.jpg", isSelected: selected === 'photo', onSelectedChange: () => setSelected('photo'), leadingVisual: (0, jsx_runtime_1.jsx)(icons_1.DocumentIconOutline, {}) }), (0, jsx_runtime_1.jsx)(next_1.TreeView.Item, { title: "Screenshot.png", isSelected: selected === 'screenshot', onSelectedChange: () => setSelected('screenshot'), leadingVisual: (0, jsx_runtime_1.jsx)(icons_1.DocumentIconOutline, {}) })] })] }));
34
+ const [selectedIds, setSelectedIds] = (0, react_1.useState)([]);
35
+ return ((0, jsx_runtime_1.jsxs)(next_1.TreeView, { ariaLabel: "File explorer", selectedIds: selectedIds, onSelectedIdsChange: setSelectedIds, children: [(0, jsx_runtime_1.jsxs)(next_1.TreeView.Item, { id: "documents", title: "Documents", hasChildren: true, isSelectable: false, leadingVisual: (0, jsx_runtime_1.jsx)(icons_1.FolderIconSolid, {}), children: [(0, jsx_runtime_1.jsxs)(next_1.TreeView.Item, { id: "work", title: "Work", hasChildren: true, isSelectable: false, leadingVisual: (0, jsx_runtime_1.jsx)(icons_1.FolderIconSolid, {}), children: [(0, jsx_runtime_1.jsx)(next_1.TreeView.Item, { id: "report", title: "Report.pdf", leadingVisual: (0, jsx_runtime_1.jsx)(icons_1.DocumentIconOutline, {}) }), (0, jsx_runtime_1.jsx)(next_1.TreeView.Item, { id: "presentation", title: "Presentation.pptx", leadingVisual: (0, jsx_runtime_1.jsx)(icons_1.DocumentIconOutline, {}) })] }), (0, jsx_runtime_1.jsx)(next_1.TreeView.Item, { id: "resume", title: "Resume.pdf", leadingVisual: (0, jsx_runtime_1.jsx)(icons_1.DocumentIconOutline, {}) })] }), (0, jsx_runtime_1.jsxs)(next_1.TreeView.Item, { id: "images", title: "Images", hasChildren: true, isSelectable: false, leadingVisual: (0, jsx_runtime_1.jsx)(icons_1.FolderIconSolid, {}), children: [(0, jsx_runtime_1.jsx)(next_1.TreeView.Item, { id: "photo", title: "Photo.jpg", leadingVisual: (0, jsx_runtime_1.jsx)(icons_1.DocumentIconOutline, {}) }), (0, jsx_runtime_1.jsx)(next_1.TreeView.Item, { id: "screenshot", title: "Screenshot.png", leadingVisual: (0, jsx_runtime_1.jsx)(icons_1.DocumentIconOutline, {}) })] })] }));
33
36
  };
34
37
  exports.default = Component;
35
38
  //# sourceMappingURL=tree-view-non-selectable-folders.story.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tree-view-non-selectable-folders.story.js","sourceRoot":"","sources":["../../../../../src/next/tree-view/stories/tree-view-non-selectable-folders.story.tsx"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,mDAAiD;AAEjD,kDAA8E;AAC9E,gDAAiD;AACjD,iCAAiC;AAEjC,MAAM,SAAS,GAAG,GAAG,EAAE;IACrB,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,IAAA,gBAAQ,EAA0B,EAAE,CAAC,CAAC;IACtE,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,IAAA,gBAAQ,EAAgB,IAAI,CAAC,CAAC;IAE9D,MAAM,MAAM,GAAG,CAAC,GAAW,EAAE,EAAE,CAC7B,WAAW,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,iCAAM,IAAI,KAAE,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,IAAG,CAAC,CAAC;IAE1D,OAAO,CACL,wBAAC,eAAQ,IAAC,SAAS,EAAC,eAAe,aACjC,wBAAC,eAAQ,CAAC,IAAI,IACZ,KAAK,EAAC,WAAW,EACjB,WAAW,QACX,UAAU,EAAE,QAAQ,CAAC,WAAW,CAAC,EACjC,gBAAgB,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,EAC3C,aAAa,EAAE,uBAAC,uBAAe,KAAG,aAElC,wBAAC,eAAQ,CAAC,IAAI,IACZ,KAAK,EAAC,MAAM,EACZ,WAAW,QACX,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,EAC5B,gBAAgB,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,EACtC,aAAa,EAAE,uBAAC,uBAAe,KAAG,aAElC,uBAAC,eAAQ,CAAC,IAAI,IACZ,KAAK,EAAC,YAAY,EAClB,UAAU,EAAE,QAAQ,KAAK,QAAQ,EACjC,gBAAgB,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC,EAC7C,aAAa,EAAE,uBAAC,2BAAmB,KAAG,GACtC,EACF,uBAAC,eAAQ,CAAC,IAAI,IACZ,KAAK,EAAC,mBAAmB,EACzB,UAAU,EAAE,QAAQ,KAAK,cAAc,EACvC,gBAAgB,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,cAAc,CAAC,EACnD,aAAa,EAAE,uBAAC,2BAAmB,KAAG,GACtC,IACY,EAChB,uBAAC,eAAQ,CAAC,IAAI,IACZ,KAAK,EAAC,YAAY,EAClB,UAAU,EAAE,QAAQ,KAAK,QAAQ,EACjC,gBAAgB,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC,EAC7C,aAAa,EAAE,uBAAC,2BAAmB,KAAG,GACtC,IACY,EAChB,wBAAC,eAAQ,CAAC,IAAI,IACZ,KAAK,EAAC,QAAQ,EACd,WAAW,QACX,UAAU,EAAE,QAAQ,CAAC,QAAQ,CAAC,EAC9B,gBAAgB,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,EACxC,aAAa,EAAE,uBAAC,uBAAe,KAAG,aAElC,uBAAC,eAAQ,CAAC,IAAI,IACZ,KAAK,EAAC,WAAW,EACjB,UAAU,EAAE,QAAQ,KAAK,OAAO,EAChC,gBAAgB,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,OAAO,CAAC,EAC5C,aAAa,EAAE,uBAAC,2BAAmB,KAAG,GACtC,EACF,uBAAC,eAAQ,CAAC,IAAI,IACZ,KAAK,EAAC,gBAAgB,EACtB,UAAU,EAAE,QAAQ,KAAK,YAAY,EACrC,gBAAgB,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,YAAY,CAAC,EACjD,aAAa,EAAE,uBAAC,2BAAmB,KAAG,GACtC,IACY,IACP,CACZ,CAAC;AACJ,CAAC,CAAC;AAEF,kBAAe,SAAS,CAAC","sourcesContent":["/**\n *\n * Copyright (c) \"Neo4j\"\n * Neo4j Sweden AB [http://neo4j.com]\n *\n * This file is part of Neo4j.\n *\n * Neo4j is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\nimport '@neo4j-ndl/base/lib/neo4j-ds-styles.css';\n\nimport { DocumentIconOutline, FolderIconSolid } from '@neo4j-ndl/react/icons';\nimport { TreeView } from '@neo4j-ndl/react/next';\nimport { useState } from 'react';\n\nconst Component = () => {\n const [expanded, setExpanded] = useState<Record<string, boolean>>({});\n const [selected, setSelected] = useState<string | null>(null);\n\n const toggle = (key: string) =>\n setExpanded((prev) => ({ ...prev, [key]: !prev[key] }));\n\n return (\n <TreeView ariaLabel=\"File explorer\">\n <TreeView.Item\n title=\"Documents\"\n hasChildren\n isExpanded={expanded['documents']}\n onExpandedChange={() => toggle('documents')}\n leadingVisual={<FolderIconSolid />}\n >\n <TreeView.Item\n title=\"Work\"\n hasChildren\n isExpanded={expanded['work']}\n onExpandedChange={() => toggle('work')}\n leadingVisual={<FolderIconSolid />}\n >\n <TreeView.Item\n title=\"Report.pdf\"\n isSelected={selected === 'report'}\n onSelectedChange={() => setSelected('report')}\n leadingVisual={<DocumentIconOutline />}\n />\n <TreeView.Item\n title=\"Presentation.pptx\"\n isSelected={selected === 'presentation'}\n onSelectedChange={() => setSelected('presentation')}\n leadingVisual={<DocumentIconOutline />}\n />\n </TreeView.Item>\n <TreeView.Item\n title=\"Resume.pdf\"\n isSelected={selected === 'resume'}\n onSelectedChange={() => setSelected('resume')}\n leadingVisual={<DocumentIconOutline />}\n />\n </TreeView.Item>\n <TreeView.Item\n title=\"Images\"\n hasChildren\n isExpanded={expanded['images']}\n onExpandedChange={() => toggle('images')}\n leadingVisual={<FolderIconSolid />}\n >\n <TreeView.Item\n title=\"Photo.jpg\"\n isSelected={selected === 'photo'}\n onSelectedChange={() => setSelected('photo')}\n leadingVisual={<DocumentIconOutline />}\n />\n <TreeView.Item\n title=\"Screenshot.png\"\n isSelected={selected === 'screenshot'}\n onSelectedChange={() => setSelected('screenshot')}\n leadingVisual={<DocumentIconOutline />}\n />\n </TreeView.Item>\n </TreeView>\n );\n};\n\nexport default Component;\n"]}
1
+ {"version":3,"file":"tree-view-non-selectable-folders.story.js","sourceRoot":"","sources":["../../../../../src/next/tree-view/stories/tree-view-non-selectable-folders.story.tsx"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,mDAAiD;AAEjD,kDAA8E;AAC9E,gDAAiD;AACjD,iCAAiC;AAEjC;;;;GAIG;AACH,MAAM,SAAS,GAAG,GAAG,EAAE;IACrB,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,IAAA,gBAAQ,EAAW,EAAE,CAAC,CAAC;IAE7D,OAAO,CACL,wBAAC,eAAQ,IACP,SAAS,EAAC,eAAe,EACzB,WAAW,EAAE,WAAW,EACxB,mBAAmB,EAAE,cAAc,aAEnC,wBAAC,eAAQ,CAAC,IAAI,IACZ,EAAE,EAAC,WAAW,EACd,KAAK,EAAC,WAAW,EACjB,WAAW,QACX,YAAY,EAAE,KAAK,EACnB,aAAa,EAAE,uBAAC,uBAAe,KAAG,aAElC,wBAAC,eAAQ,CAAC,IAAI,IACZ,EAAE,EAAC,MAAM,EACT,KAAK,EAAC,MAAM,EACZ,WAAW,QACX,YAAY,EAAE,KAAK,EACnB,aAAa,EAAE,uBAAC,uBAAe,KAAG,aAElC,uBAAC,eAAQ,CAAC,IAAI,IACZ,EAAE,EAAC,QAAQ,EACX,KAAK,EAAC,YAAY,EAClB,aAAa,EAAE,uBAAC,2BAAmB,KAAG,GACtC,EACF,uBAAC,eAAQ,CAAC,IAAI,IACZ,EAAE,EAAC,cAAc,EACjB,KAAK,EAAC,mBAAmB,EACzB,aAAa,EAAE,uBAAC,2BAAmB,KAAG,GACtC,IACY,EAChB,uBAAC,eAAQ,CAAC,IAAI,IACZ,EAAE,EAAC,QAAQ,EACX,KAAK,EAAC,YAAY,EAClB,aAAa,EAAE,uBAAC,2BAAmB,KAAG,GACtC,IACY,EAChB,wBAAC,eAAQ,CAAC,IAAI,IACZ,EAAE,EAAC,QAAQ,EACX,KAAK,EAAC,QAAQ,EACd,WAAW,QACX,YAAY,EAAE,KAAK,EACnB,aAAa,EAAE,uBAAC,uBAAe,KAAG,aAElC,uBAAC,eAAQ,CAAC,IAAI,IACZ,EAAE,EAAC,OAAO,EACV,KAAK,EAAC,WAAW,EACjB,aAAa,EAAE,uBAAC,2BAAmB,KAAG,GACtC,EACF,uBAAC,eAAQ,CAAC,IAAI,IACZ,EAAE,EAAC,YAAY,EACf,KAAK,EAAC,gBAAgB,EACtB,aAAa,EAAE,uBAAC,2BAAmB,KAAG,GACtC,IACY,IACP,CACZ,CAAC;AACJ,CAAC,CAAC;AAEF,kBAAe,SAAS,CAAC","sourcesContent":["/**\n *\n * Copyright (c) \"Neo4j\"\n * Neo4j Sweden AB [http://neo4j.com]\n *\n * This file is part of Neo4j.\n *\n * Neo4j is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\nimport '@neo4j-ndl/base/lib/neo4j-ds-styles.css';\n\nimport { DocumentIconOutline, FolderIconSolid } from '@neo4j-ndl/react/icons';\nimport { TreeView } from '@neo4j-ndl/react/next';\nimport { useState } from 'react';\n\n/**\n * Folders here are pure containers: `isSelectable={false}` makes them expand on\n * click without ever becoming part of the selection, and screen readers\n * announce them as non-selectable rather than as unselected rows.\n */\nconst Component = () => {\n const [selectedIds, setSelectedIds] = useState<string[]>([]);\n\n return (\n <TreeView\n ariaLabel=\"File explorer\"\n selectedIds={selectedIds}\n onSelectedIdsChange={setSelectedIds}\n >\n <TreeView.Item\n id=\"documents\"\n title=\"Documents\"\n hasChildren\n isSelectable={false}\n leadingVisual={<FolderIconSolid />}\n >\n <TreeView.Item\n id=\"work\"\n title=\"Work\"\n hasChildren\n isSelectable={false}\n leadingVisual={<FolderIconSolid />}\n >\n <TreeView.Item\n id=\"report\"\n title=\"Report.pdf\"\n leadingVisual={<DocumentIconOutline />}\n />\n <TreeView.Item\n id=\"presentation\"\n title=\"Presentation.pptx\"\n leadingVisual={<DocumentIconOutline />}\n />\n </TreeView.Item>\n <TreeView.Item\n id=\"resume\"\n title=\"Resume.pdf\"\n leadingVisual={<DocumentIconOutline />}\n />\n </TreeView.Item>\n <TreeView.Item\n id=\"images\"\n title=\"Images\"\n hasChildren\n isSelectable={false}\n leadingVisual={<FolderIconSolid />}\n >\n <TreeView.Item\n id=\"photo\"\n title=\"Photo.jpg\"\n leadingVisual={<DocumentIconOutline />}\n />\n <TreeView.Item\n id=\"screenshot\"\n title=\"Screenshot.png\"\n leadingVisual={<DocumentIconOutline />}\n />\n </TreeView.Item>\n </TreeView>\n );\n};\n\nexport default Component;\n"]}
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const jsx_runtime_1 = require("react/jsx-runtime");
4
+ /**
5
+ *
6
+ * Copyright (c) "Neo4j"
7
+ * Neo4j Sweden AB [http://neo4j.com]
8
+ *
9
+ * This file is part of Neo4j.
10
+ *
11
+ * Neo4j is free software: you can redistribute it and/or modify
12
+ * it under the terms of the GNU General Public License as published by
13
+ * the Free Software Foundation, either version 3 of the License, or
14
+ * (at your option) any later version.
15
+ *
16
+ * This program is distributed in the hope that it will be useful,
17
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
+ * GNU General Public License for more details.
20
+ *
21
+ * You should have received a copy of the GNU General Public License
22
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
23
+ */
24
+ require("@neo4j-ndl/base/lib/neo4j-ds-styles.css");
25
+ const icons_1 = require("@neo4j-ndl/react/icons");
26
+ const next_1 = require("@neo4j-ndl/react/next");
27
+ const react_1 = require("react");
28
+ const files = [
29
+ {
30
+ children: [
31
+ {
32
+ children: [
33
+ { id: 'report', title: 'Report.pdf' },
34
+ { id: 'presentation', title: 'Presentation.pptx' },
35
+ ],
36
+ id: 'work',
37
+ title: 'Work',
38
+ },
39
+ { id: 'resume', title: 'Resume.pdf' },
40
+ ],
41
+ id: 'documents',
42
+ title: 'Documents',
43
+ },
44
+ {
45
+ children: [
46
+ { id: 'photo', title: 'Photo.jpg' },
47
+ { id: 'screenshot', title: 'Screenshot.png' },
48
+ ],
49
+ id: 'images',
50
+ title: 'Images',
51
+ },
52
+ ];
53
+ const walk = (nodes, visit) => {
54
+ nodes.forEach((node) => {
55
+ var _a;
56
+ visit(node);
57
+ walk((_a = node.children) !== null && _a !== void 0 ? _a : [], visit);
58
+ });
59
+ };
60
+ const getDescendantIds = (node) => {
61
+ var _a;
62
+ return ((_a = node.children) !== null && _a !== void 0 ? _a : []).flatMap((child) => [
63
+ child.id,
64
+ ...getDescendantIds(child),
65
+ ]);
66
+ };
67
+ const getLeafIds = (node) => node.children === undefined ? [node.id] : node.children.flatMap(getLeafIds);
68
+ /** Folders follow their contents: selected only when every leaf below them is. */
69
+ const syncFolders = (nodes, selected) => {
70
+ nodes.forEach((node) => {
71
+ if (node.children === undefined) {
72
+ return;
73
+ }
74
+ syncFolders(node.children, selected);
75
+ if (getLeafIds(node).every((id) => selected.has(id))) {
76
+ selected.add(node.id);
77
+ }
78
+ else {
79
+ selected.delete(node.id);
80
+ }
81
+ });
82
+ };
83
+ const Component = () => {
84
+ const [selectedIds, setSelectedIds] = (0, react_1.useState)(['resume']);
85
+ const selected = new Set(selectedIds);
86
+ /**
87
+ * The tree reports the whole selection at once, so cascading folders onto
88
+ * their contents takes a single pass even when a Shift range flipped many
89
+ * rows.
90
+ */
91
+ const handleSelectedIdsChange = (0, react_1.useCallback)((nextIds) => {
92
+ setSelectedIds((previousIds) => {
93
+ const next = new Set(nextIds);
94
+ const previous = new Set(previousIds);
95
+ walk(files, (node) => {
96
+ const isSelected = next.has(node.id);
97
+ if (node.children === undefined ||
98
+ isSelected === previous.has(node.id)) {
99
+ return;
100
+ }
101
+ getDescendantIds(node).forEach((id) => {
102
+ if (isSelected) {
103
+ next.add(id);
104
+ }
105
+ else {
106
+ next.delete(id);
107
+ }
108
+ });
109
+ });
110
+ syncFolders(files, next);
111
+ return [...next];
112
+ });
113
+ }, []);
114
+ const renderNode = (node) => {
115
+ var _a;
116
+ const leafIds = getLeafIds(node);
117
+ const selectedLeafCount = leafIds.filter((id) => selected.has(id)).length;
118
+ return ((0, jsx_runtime_1.jsx)(next_1.TreeView.Item, { id: node.id, title: node.title, defaultExpanded: true, isIndeterminate: node.children !== undefined &&
119
+ selectedLeafCount > 0 &&
120
+ selectedLeafCount < leafIds.length, leadingVisual: node.children === undefined ? ((0, jsx_runtime_1.jsx)(icons_1.DocumentIconOutline, {})) : ((0, jsx_runtime_1.jsx)(icons_1.FolderIconSolid, {})), children: (_a = node.children) === null || _a === void 0 ? void 0 : _a.map(renderNode) }, node.id));
121
+ };
122
+ return ((0, jsx_runtime_1.jsx)(next_1.TreeView, { ariaLabel: "File explorer", selectionMode: "multiple", selectedIds: selectedIds, onSelectedIdsChange: handleSelectedIdsChange, children: files.map(renderNode) }));
123
+ };
124
+ exports.default = Component;
125
+ //# sourceMappingURL=tree-view-root-selection.story.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tree-view-root-selection.story.js","sourceRoot":"","sources":["../../../../../src/next/tree-view/stories/tree-view-root-selection.story.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n *\n * Copyright (c) \"Neo4j\"\n * Neo4j Sweden AB [http://neo4j.com]\n *\n * This file is part of Neo4j.\n *\n * Neo4j is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\nimport '@neo4j-ndl/base/lib/neo4j-ds-styles.css';\n\nimport { DocumentIconOutline, FolderIconSolid } from '@neo4j-ndl/react/icons';\nimport { TreeView } from '@neo4j-ndl/react/next';\nimport { useCallback, useState } from 'react';\n\ntype FileNode = {\n children?: FileNode[];\n id: string;\n title: string;\n};\n\nconst files: FileNode[] = [\n {\n children: [\n {\n children: [\n { id: 'report', title: 'Report.pdf' },\n { id: 'presentation', title: 'Presentation.pptx' },\n ],\n id: 'work',\n title: 'Work',\n },\n { id: 'resume', title: 'Resume.pdf' },\n ],\n id: 'documents',\n title: 'Documents',\n },\n {\n children: [\n { id: 'photo', title: 'Photo.jpg' },\n { id: 'screenshot', title: 'Screenshot.png' },\n ],\n id: 'images',\n title: 'Images',\n },\n];\n\nconst walk = (nodes: FileNode[], visit: (node: FileNode) => void) => {\n nodes.forEach((node) => {\n visit(node);\n walk(node.children ?? [], visit);\n });\n};\n\nconst getDescendantIds = (node: FileNode): string[] =>\n (node.children ?? []).flatMap((child) => [\n child.id,\n ...getDescendantIds(child),\n ]);\n\nconst getLeafIds = (node: FileNode): string[] =>\n node.children === undefined ? [node.id] : node.children.flatMap(getLeafIds);\n\n/** Folders follow their contents: selected only when every leaf below them is. */\nconst syncFolders = (nodes: FileNode[], selected: Set<string>) => {\n nodes.forEach((node) => {\n if (node.children === undefined) {\n return;\n }\n syncFolders(node.children, selected);\n if (getLeafIds(node).every((id) => selected.has(id))) {\n selected.add(node.id);\n } else {\n selected.delete(node.id);\n }\n });\n};\n\nconst Component = () => {\n const [selectedIds, setSelectedIds] = useState<string[]>(['resume']);\n const selected = new Set(selectedIds);\n\n /**\n * The tree reports the whole selection at once, so cascading folders onto\n * their contents takes a single pass even when a Shift range flipped many\n * rows.\n */\n const handleSelectedIdsChange = useCallback((nextIds: string[]) => {\n setSelectedIds((previousIds) => {\n const next = new Set(nextIds);\n const previous = new Set(previousIds);\n walk(files, (node) => {\n const isSelected = next.has(node.id);\n if (\n node.children === undefined ||\n isSelected === previous.has(node.id)\n ) {\n return;\n }\n getDescendantIds(node).forEach((id) => {\n if (isSelected) {\n next.add(id);\n } else {\n next.delete(id);\n }\n });\n });\n syncFolders(files, next);\n return [...next];\n });\n }, []);\n\n const renderNode = (node: FileNode) => {\n const leafIds = getLeafIds(node);\n const selectedLeafCount = leafIds.filter((id) => selected.has(id)).length;\n return (\n <TreeView.Item\n key={node.id}\n id={node.id}\n title={node.title}\n defaultExpanded\n isIndeterminate={\n node.children !== undefined &&\n selectedLeafCount > 0 &&\n selectedLeafCount < leafIds.length\n }\n leadingVisual={\n node.children === undefined ? (\n <DocumentIconOutline />\n ) : (\n <FolderIconSolid />\n )\n }\n >\n {node.children?.map(renderNode)}\n </TreeView.Item>\n );\n };\n\n return (\n <TreeView\n ariaLabel=\"File explorer\"\n selectionMode=\"multiple\"\n selectedIds={selectedIds}\n onSelectedIdsChange={handleSelectedIdsChange}\n >\n {files.map(renderNode)}\n </TreeView>\n );\n};\n\nexport default Component;\n"]}
@@ -63,7 +63,7 @@ const toastWithVariant = (input) => {
63
63
  case 'danger':
64
64
  return Toast_1.toast.danger(input.text, Object.assign(Object.assign({}, commonRootProps), { sonnerToastProps: commonSonnerProps }));
65
65
  case 'progress-bar':
66
- return Toast_1.toast.progressBar(input.text, Object.assign(Object.assign({}, commonRootProps), { sonnerToastProps: commonSonnerProps, initialProgressBarValue: input.progressBarValue }));
66
+ return Toast_1.toast.progressBar(input.text, Object.assign(Object.assign({}, commonRootProps), { initialProgressBarValue: input.progressBarValue, sonnerToastProps: commonSonnerProps }));
67
67
  }
68
68
  };
69
69
  const ToasterControlled = (_a) => {
@@ -1 +1 @@
1
- {"version":3,"file":"ToastControlled.js","sourceRoot":"","sources":["../../../src/toast/ToastControlled.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,iCAA4C;AAG5C,mCAA2E;AAsB3E,MAAM,gBAAgB,GAAG,CAAC,KAAsB,EAAE,EAAE;IAClD,MAAM,eAAe,GAAG;QACtB,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,WAAW,EAAE,KAAK,CAAC,OAAO,KAAK,SAAS;QACxC,YAAY,EAAE,IAAI;QAClB,QAAQ,CAAC,EAAW;;YAClB,MAAA,KAAK,CAAC,QAAQ,sDAAG,EAAE,CAAC,CAAC;QACvB,CAAC;QACD,OAAO,CAAC,EAAW;;YACjB,MAAA,KAAK,CAAC,OAAO,sDAAG,EAAE,CAAC,CAAC;QACtB,CAAC;QACD,eAAe,EAAE,KAAK;QACtB,mBAAmB,EAAE,KAAK;KAC3B,CAAC;IAEF,MAAM,iBAAiB,GAAkB;QACvC,WAAW,EAAE,KAAK;QAClB,EAAE,EAAE,KAAK,CAAC,EAAE;KACb,CAAC;IAEF,QAAQ,KAAK,CAAC,OAAO,EAAE,CAAC;QACtB,KAAK,SAAS;YACZ,OAAO,aAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,kCAC1B,eAAe,KAClB,gBAAgB,EAAE,iBAAiB,IACnC,CAAC;QACL,KAAK,SAAS;YACZ,OAAO,aAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,kCAC1B,eAAe,KAClB,gBAAgB,EAAE,iBAAiB,IACnC,CAAC;QACL,KAAK,QAAQ;YACX,OAAO,aAAK,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,kCACzB,eAAe,KAClB,gBAAgB,EAAE,iBAAiB,IACnC,CAAC;QACL,KAAK,cAAc;YACjB,OAAO,aAAK,CAAC,WAAW,CAAC,KAAK,CAAC,IAAI,kCAC9B,eAAe,KAClB,gBAAgB,EAAE,iBAAiB,EACnC,uBAAuB,EAAE,KAAK,CAAC,gBAAgB,IAC/C,CAAC;IACP,CAAC;AACH,CAAC,CAAC;AAEK,MAAM,iBAAiB,GAAG,CAAC,EAGX,EAAE,EAAE;QAHO,EAChC,MAAM,OAEe,EADlB,SAAS,cAFoB,UAGjC,CADa;IAEZ,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,IAAA,gBAAQ,EAAoB,EAAE,CAAC,CAAC;IAEtE,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,kCAAkC;QAClC,MAAM,aAAa,GAAG,WAAW,CAAC,MAAM,CACtC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,KAAK,CAAC,EAAE,CAAC,CACxD,CAAC;QACF,aAAa,CAAC,OAAO,CAAC,CAAC,YAAY,EAAE,EAAE;YACrC,aAAK,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;QAC/B,CAAC,CAAC,CAAC;QAEH,kDAAkD;QAClD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,gBAAgB,CAAC,KAAK,CAAC,CAAC;QAC1B,CAAC;QAED,cAAc,CAAC,MAAM,CAAC,CAAC;IACzB,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;IAE1B,OAAO,uBAAC,eAAO,kBAAC,aAAa,EAAE,CAAC,IAAM,SAAS,EAAI,CAAC;AACtD,CAAC,CAAC;AAxBW,QAAA,iBAAiB,qBAwB5B","sourcesContent":["/**\n *\n * Copyright (c) \"Neo4j\"\n * Neo4j Sweden AB [http://neo4j.com]\n *\n * This file is part of Neo4j.\n *\n * Neo4j is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\nimport { useEffect, useState } from 'react';\nimport { type ExternalToast } from 'sonner';\n\nimport { toast, Toaster, type ToastId, type ToastVariants } from './Toast';\n\ntype ControlledToast = {\n id: ToastId;\n text: string;\n onClose?: (id: ToastId) => void;\n actionLabel?: string;\n onAction?: (id: ToastId) => void;\n} & (\n | {\n variant: Exclude<ToastVariants, 'progress-bar' | 'loading-spinner'>;\n }\n | {\n id: ToastId;\n variant: Extract<ToastVariants, 'progress-bar'>;\n progressBarValue: number;\n }\n);\ntype ToastControlledProps = {\n toasts: ControlledToast[];\n} & Pick<React.ComponentProps<typeof Toaster>, 'visibleToasts'>;\n\nconst toastWithVariant = (input: ControlledToast) => {\n const commonRootProps = {\n actionLabel: input.actionLabel,\n isCloseable: input.onClose !== undefined,\n isControlled: true,\n onAction(id: ToastId) {\n input.onAction?.(id);\n },\n onClose(id: ToastId) {\n input.onClose?.(id);\n },\n shouldAutoClose: false,\n shouldCloseOnAction: false,\n };\n\n const commonSonnerProps: ExternalToast = {\n dismissible: false,\n id: input.id,\n };\n\n switch (input.variant) {\n case 'neutral':\n return toast.neutral(input.text, {\n ...commonRootProps,\n sonnerToastProps: commonSonnerProps,\n });\n case 'success':\n return toast.success(input.text, {\n ...commonRootProps,\n sonnerToastProps: commonSonnerProps,\n });\n case 'danger':\n return toast.danger(input.text, {\n ...commonRootProps,\n sonnerToastProps: commonSonnerProps,\n });\n case 'progress-bar':\n return toast.progressBar(input.text, {\n ...commonRootProps,\n sonnerToastProps: commonSonnerProps,\n initialProgressBarValue: input.progressBarValue,\n });\n }\n};\n\nexport const ToasterControlled = ({\n toasts,\n ...restProps\n}: ToastControlledProps) => {\n const [toastsState, setToastsState] = useState<ControlledToast[]>([]);\n\n useEffect(() => {\n // if a toast is removed, close it\n const removedToasts = toastsState.filter(\n (toast) => !toasts.some((item) => item.id === toast.id),\n );\n removedToasts.forEach((removedToast) => {\n toast.close(removedToast.id);\n });\n\n // Otherwise, update the toast or create a new one\n for (const toast of toasts) {\n toastWithVariant(toast);\n }\n\n setToastsState(toasts);\n }, [toasts, toastsState]);\n\n return <Toaster visibleToasts={1} {...restProps} />;\n};\n"]}
1
+ {"version":3,"file":"ToastControlled.js","sourceRoot":"","sources":["../../../src/toast/ToastControlled.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,iCAA4C;AAG5C,mCAA2E;AAsB3E,MAAM,gBAAgB,GAAG,CAAC,KAAsB,EAAE,EAAE;IAClD,MAAM,eAAe,GAAG;QACtB,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,WAAW,EAAE,KAAK,CAAC,OAAO,KAAK,SAAS;QACxC,YAAY,EAAE,IAAI;QAClB,QAAQ,CAAC,EAAW;;YAClB,MAAA,KAAK,CAAC,QAAQ,sDAAG,EAAE,CAAC,CAAC;QACvB,CAAC;QACD,OAAO,CAAC,EAAW;;YACjB,MAAA,KAAK,CAAC,OAAO,sDAAG,EAAE,CAAC,CAAC;QACtB,CAAC;QACD,eAAe,EAAE,KAAK;QACtB,mBAAmB,EAAE,KAAK;KAC3B,CAAC;IAEF,MAAM,iBAAiB,GAAkB;QACvC,WAAW,EAAE,KAAK;QAClB,EAAE,EAAE,KAAK,CAAC,EAAE;KACb,CAAC;IAEF,QAAQ,KAAK,CAAC,OAAO,EAAE,CAAC;QACtB,KAAK,SAAS;YACZ,OAAO,aAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,kCAC1B,eAAe,KAClB,gBAAgB,EAAE,iBAAiB,IACnC,CAAC;QACL,KAAK,SAAS;YACZ,OAAO,aAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,kCAC1B,eAAe,KAClB,gBAAgB,EAAE,iBAAiB,IACnC,CAAC;QACL,KAAK,QAAQ;YACX,OAAO,aAAK,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,kCACzB,eAAe,KAClB,gBAAgB,EAAE,iBAAiB,IACnC,CAAC;QACL,KAAK,cAAc;YACjB,OAAO,aAAK,CAAC,WAAW,CAAC,KAAK,CAAC,IAAI,kCAC9B,eAAe,KAClB,uBAAuB,EAAE,KAAK,CAAC,gBAAgB,EAC/C,gBAAgB,EAAE,iBAAiB,IACnC,CAAC;IACP,CAAC;AACH,CAAC,CAAC;AAEK,MAAM,iBAAiB,GAAG,CAAC,EAGX,EAAE,EAAE;QAHO,EAChC,MAAM,OAEe,EADlB,SAAS,cAFoB,UAGjC,CADa;IAEZ,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,IAAA,gBAAQ,EAAoB,EAAE,CAAC,CAAC;IAEtE,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,kCAAkC;QAClC,MAAM,aAAa,GAAG,WAAW,CAAC,MAAM,CACtC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,KAAK,CAAC,EAAE,CAAC,CACxD,CAAC;QACF,aAAa,CAAC,OAAO,CAAC,CAAC,YAAY,EAAE,EAAE;YACrC,aAAK,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;QAC/B,CAAC,CAAC,CAAC;QAEH,kDAAkD;QAClD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,gBAAgB,CAAC,KAAK,CAAC,CAAC;QAC1B,CAAC;QAED,cAAc,CAAC,MAAM,CAAC,CAAC;IACzB,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;IAE1B,OAAO,uBAAC,eAAO,kBAAC,aAAa,EAAE,CAAC,IAAM,SAAS,EAAI,CAAC;AACtD,CAAC,CAAC;AAxBW,QAAA,iBAAiB,qBAwB5B","sourcesContent":["/**\n *\n * Copyright (c) \"Neo4j\"\n * Neo4j Sweden AB [http://neo4j.com]\n *\n * This file is part of Neo4j.\n *\n * Neo4j is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\nimport { useEffect, useState } from 'react';\nimport { type ExternalToast } from 'sonner';\n\nimport { toast, Toaster, type ToastId, type ToastVariants } from './Toast';\n\ntype ControlledToast = {\n id: ToastId;\n text: string;\n onClose?: (id: ToastId) => void;\n actionLabel?: string;\n onAction?: (id: ToastId) => void;\n} & (\n | {\n variant: Exclude<ToastVariants, 'progress-bar' | 'loading-spinner'>;\n }\n | {\n id: ToastId;\n variant: Extract<ToastVariants, 'progress-bar'>;\n progressBarValue: number;\n }\n);\ntype ToastControlledProps = {\n toasts: ControlledToast[];\n} & Pick<React.ComponentProps<typeof Toaster>, 'visibleToasts'>;\n\nconst toastWithVariant = (input: ControlledToast) => {\n const commonRootProps = {\n actionLabel: input.actionLabel,\n isCloseable: input.onClose !== undefined,\n isControlled: true,\n onAction(id: ToastId) {\n input.onAction?.(id);\n },\n onClose(id: ToastId) {\n input.onClose?.(id);\n },\n shouldAutoClose: false,\n shouldCloseOnAction: false,\n };\n\n const commonSonnerProps: ExternalToast = {\n dismissible: false,\n id: input.id,\n };\n\n switch (input.variant) {\n case 'neutral':\n return toast.neutral(input.text, {\n ...commonRootProps,\n sonnerToastProps: commonSonnerProps,\n });\n case 'success':\n return toast.success(input.text, {\n ...commonRootProps,\n sonnerToastProps: commonSonnerProps,\n });\n case 'danger':\n return toast.danger(input.text, {\n ...commonRootProps,\n sonnerToastProps: commonSonnerProps,\n });\n case 'progress-bar':\n return toast.progressBar(input.text, {\n ...commonRootProps,\n initialProgressBarValue: input.progressBarValue,\n sonnerToastProps: commonSonnerProps,\n });\n }\n};\n\nexport const ToasterControlled = ({\n toasts,\n ...restProps\n}: ToastControlledProps) => {\n const [toastsState, setToastsState] = useState<ControlledToast[]>([]);\n\n useEffect(() => {\n // if a toast is removed, close it\n const removedToasts = toastsState.filter(\n (toast) => !toasts.some((item) => item.id === toast.id),\n );\n removedToasts.forEach((removedToast) => {\n toast.close(removedToast.id);\n });\n\n // Otherwise, update the toast or create a new one\n for (const toast of toasts) {\n toastWithVariant(toast);\n }\n\n setToastsState(toasts);\n }, [toasts, toastsState]);\n\n return <Toaster visibleToasts={1} {...restProps} />;\n};\n"]}
@@ -24,11 +24,11 @@ import { Dropzone, FilledButton, Typography } from '@neo4j-ndl/react';
24
24
  import { useCallback, useState } from 'react';
25
25
  // Configuration constants
26
26
  const CONFIG = {
27
- MAX_FILES: 3,
27
+ ACCEPTED_EXTENSIONS: ['.txt', '.json'],
28
28
  MAX_FILE_SIZE: 5 * 1024 * 1024, // 5MB
29
+ MAX_FILES: 3,
29
30
  PROGRESS_INCREMENT: 5,
30
31
  UPDATE_INTERVAL: 100,
31
- ACCEPTED_EXTENSIONS: ['.txt', '.json'],
32
32
  };
33
33
  var UploadState;
34
34
  (function (UploadState) {
@@ -53,7 +53,7 @@ const Component = () => {
53
53
  });
54
54
  }, []);
55
55
  const simulateUpload = useCallback((files) => {
56
- setUploadData((prev) => (Object.assign(Object.assign({}, prev), { state: UploadState.Uploading, files, progress: 0 })));
56
+ setUploadData((prev) => (Object.assign(Object.assign({}, prev), { files, progress: 0, state: UploadState.Uploading })));
57
57
  const interval = setInterval(() => {
58
58
  setUploadData((prev) => {
59
59
  const newProgress = prev.progress + CONFIG.PROGRESS_INCREMENT;
@@ -74,11 +74,11 @@ const Component = () => {
74
74
  }, [simulateUpload]);
75
75
  const handleDropRejected = useCallback((rejectedFiles) => {
76
76
  const errors = rejectedFiles.map((file) => `${file.file.name}: ${file.errors.map((error) => error.message).join(', ')}`);
77
- setUploadData((prev) => (Object.assign(Object.assign({}, prev), { state: UploadState.Error, errors })));
77
+ setUploadData((prev) => (Object.assign(Object.assign({}, prev), { errors, state: UploadState.Error })));
78
78
  }, []);
79
79
  const handleError = useCallback((error) => {
80
80
  console.error('Dropzone error:', error);
81
- setUploadData((prev) => (Object.assign(Object.assign({}, prev), { state: UploadState.Error, errors: [error.message] })));
81
+ setUploadData((prev) => (Object.assign(Object.assign({}, prev), { errors: [error.message], state: UploadState.Error })));
82
82
  }, []);
83
83
  const renderStatusContent = () => {
84
84
  const { state, progress, files, errors } = uploadData;
@@ -1 +1 @@
1
- {"version":3,"file":"dropzone-full.story.js","sourceRoot":"","sources":["../../../../src/dropzone/stories/dropzone-full.story.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n *\n * Copyright (c) \"Neo4j\"\n * Neo4j Sweden AB [http://neo4j.com]\n *\n * This file is part of Neo4j.\n *\n * Neo4j is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\nimport { XMarkIcon } from '@heroicons/react/24/solid';\nimport { Dropzone, FilledButton, Typography } from '@neo4j-ndl/react';\nimport { useCallback, useState } from 'react';\nimport {\n type DropEvent,\n type DropzoneOptions,\n type FileRejection,\n} from 'react-dropzone';\n\n// Configuration constants\nconst CONFIG = {\n MAX_FILES: 3,\n MAX_FILE_SIZE: 5 * 1024 * 1024, // 5MB\n PROGRESS_INCREMENT: 5,\n UPDATE_INTERVAL: 100,\n ACCEPTED_EXTENSIONS: ['.txt', '.json'] as string[],\n} as const;\n\nenum UploadState {\n Idle = 'idle',\n Uploading = 'uploading',\n Success = 'success',\n Error = 'error',\n}\n\ninterface UploadData {\n state: UploadState;\n progress: number;\n files: File[];\n errors: string[];\n}\n\nconst Component = () => {\n const [uploadData, setUploadData] = useState<UploadData>({\n errors: [],\n files: [],\n progress: 0,\n state: UploadState.Idle,\n });\n\n const resetUpload = useCallback(() => {\n setUploadData({\n errors: [],\n files: [],\n progress: 0,\n state: UploadState.Idle,\n });\n }, []);\n\n const simulateUpload = useCallback((files: File[]) => {\n setUploadData((prev) => ({\n ...prev,\n state: UploadState.Uploading,\n files,\n progress: 0,\n }));\n\n const interval = setInterval(() => {\n setUploadData((prev) => {\n const newProgress = prev.progress + CONFIG.PROGRESS_INCREMENT;\n if (newProgress >= 100) {\n clearInterval(interval);\n setTimeout(\n () =>\n setUploadData((current) => ({\n ...current,\n state: UploadState.Success,\n })),\n 500,\n );\n return { ...prev, progress: 100 };\n }\n return { ...prev, progress: newProgress };\n });\n }, CONFIG.UPDATE_INTERVAL);\n }, []);\n\n const handleDrop: DropzoneOptions['onDrop'] = useCallback(\n (\n acceptedFiles: File[],\n rejectedFiles: FileRejection[],\n event: DropEvent,\n ) => {\n console.info('Drop event:', { acceptedFiles, event, rejectedFiles });\n\n if (acceptedFiles.length > 0) {\n simulateUpload(acceptedFiles);\n }\n },\n [simulateUpload],\n );\n\n const handleDropRejected: DropzoneOptions['onDropRejected'] = useCallback(\n (rejectedFiles: FileRejection[]) => {\n const errors = rejectedFiles.map(\n (file) =>\n `${file.file.name}: ${file.errors.map((error) => error.message).join(', ')}`,\n );\n setUploadData((prev) => ({ ...prev, state: UploadState.Error, errors }));\n },\n [],\n );\n\n const handleError = useCallback((error: Error) => {\n console.error('Dropzone error:', error);\n setUploadData((prev) => ({\n ...prev,\n state: UploadState.Error,\n errors: [error.message],\n }));\n }, []);\n\n const renderStatusContent = () => {\n const { state, progress, files, errors } = uploadData;\n\n switch (state) {\n case UploadState.Uploading:\n return (\n <Dropzone.LoadingProgressBar\n progressBarPrecentage={Math.round(progress)}\n />\n );\n\n case UploadState.Success:\n return (\n <div className=\"n-flex n-flex-col n-justify-center n-items-center n-gap-y-token-32\">\n <Typography\n variant=\"title-4\"\n className=\"n-text-success-text n-font-semibold\"\n >\n Upload Complete!\n </Typography>\n <Typography\n variant=\"body-small\"\n className=\"n-text-neutral-text-weak\"\n >\n Successfully uploaded: {files.map((file) => file.name).join(', ')}\n </Typography>\n <FilledButton onClick={resetUpload}>Upload More Files</FilledButton>\n </div>\n );\n\n case UploadState.Error:\n return (\n <div className=\"n-flex n-flex-col n-justify-center n-items-center n-gap-y-token-32\">\n <XMarkIcon className=\"n-w-token-32 n-h-token-32 n-text-danger-text\" />\n <div className=\"n-flex n-flex-col n-gap-y-token-2\">\n {errors.map((message, index) => (\n <Typography\n key={index}\n variant=\"label\"\n className=\"n-text-danger-text n-font-semibold\"\n htmlAttributes={{ role: 'alert' }}\n >\n {message}\n </Typography>\n ))}\n </div>\n <FilledButton onClick={resetUpload}>Try Again</FilledButton>\n </div>\n );\n\n default:\n return undefined;\n }\n };\n\n return (\n <Dropzone\n dropZoneOptions={{\n disabled: uploadData.state === UploadState.Uploading,\n maxFiles: CONFIG.MAX_FILES,\n maxSize: CONFIG.MAX_FILE_SIZE,\n multiple: true,\n onDrop: handleDrop,\n onDropRejected: handleDropRejected,\n onError: handleError,\n }}\n supportedFilesDescription={\n <div className=\"n-text-center n-flex n-flex-col n-gap-y-token-2\">\n <Typography variant=\"body-small\" className=\"n-text-neutral-text-weak\">\n Supports: Text files (.txt, .json) only\n </Typography>\n <Typography variant=\"body-small\" className=\"n-text-neutral-text-weak\">\n Max {CONFIG.MAX_FILES} files •{' '}\n {CONFIG.MAX_FILE_SIZE / (1024 * 1024)}MB per file\n </Typography>\n </div>\n }\n acceptedFileExtensions={CONFIG.ACCEPTED_EXTENSIONS}\n loadingElement={renderStatusContent()}\n rejectedText=\"Error: Please make sure to only upload .json or .txt files\"\n heading=\"Drag & Drop your files\"\n />\n );\n};\n\nexport default Component;\n"]}
1
+ {"version":3,"file":"dropzone-full.story.js","sourceRoot":"","sources":["../../../../src/dropzone/stories/dropzone-full.story.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n *\n * Copyright (c) \"Neo4j\"\n * Neo4j Sweden AB [http://neo4j.com]\n *\n * This file is part of Neo4j.\n *\n * Neo4j is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\nimport { XMarkIcon } from '@heroicons/react/24/solid';\nimport { Dropzone, FilledButton, Typography } from '@neo4j-ndl/react';\nimport { useCallback, useState } from 'react';\nimport {\n type DropEvent,\n type DropzoneOptions,\n type FileRejection,\n} from 'react-dropzone';\n\n// Configuration constants\nconst CONFIG = {\n ACCEPTED_EXTENSIONS: ['.txt', '.json'] as string[],\n MAX_FILE_SIZE: 5 * 1024 * 1024, // 5MB\n MAX_FILES: 3,\n PROGRESS_INCREMENT: 5,\n UPDATE_INTERVAL: 100,\n} as const;\n\nenum UploadState {\n Idle = 'idle',\n Uploading = 'uploading',\n Success = 'success',\n Error = 'error',\n}\n\ninterface UploadData {\n state: UploadState;\n progress: number;\n files: File[];\n errors: string[];\n}\n\nconst Component = () => {\n const [uploadData, setUploadData] = useState<UploadData>({\n errors: [],\n files: [],\n progress: 0,\n state: UploadState.Idle,\n });\n\n const resetUpload = useCallback(() => {\n setUploadData({\n errors: [],\n files: [],\n progress: 0,\n state: UploadState.Idle,\n });\n }, []);\n\n const simulateUpload = useCallback((files: File[]) => {\n setUploadData((prev) => ({\n ...prev,\n files,\n progress: 0,\n state: UploadState.Uploading,\n }));\n\n const interval = setInterval(() => {\n setUploadData((prev) => {\n const newProgress = prev.progress + CONFIG.PROGRESS_INCREMENT;\n if (newProgress >= 100) {\n clearInterval(interval);\n setTimeout(\n () =>\n setUploadData((current) => ({\n ...current,\n state: UploadState.Success,\n })),\n 500,\n );\n return { ...prev, progress: 100 };\n }\n return { ...prev, progress: newProgress };\n });\n }, CONFIG.UPDATE_INTERVAL);\n }, []);\n\n const handleDrop: DropzoneOptions['onDrop'] = useCallback(\n (\n acceptedFiles: File[],\n rejectedFiles: FileRejection[],\n event: DropEvent,\n ) => {\n console.info('Drop event:', { acceptedFiles, event, rejectedFiles });\n\n if (acceptedFiles.length > 0) {\n simulateUpload(acceptedFiles);\n }\n },\n [simulateUpload],\n );\n\n const handleDropRejected: DropzoneOptions['onDropRejected'] = useCallback(\n (rejectedFiles: FileRejection[]) => {\n const errors = rejectedFiles.map(\n (file) =>\n `${file.file.name}: ${file.errors.map((error) => error.message).join(', ')}`,\n );\n setUploadData((prev) => ({ ...prev, errors, state: UploadState.Error }));\n },\n [],\n );\n\n const handleError = useCallback((error: Error) => {\n console.error('Dropzone error:', error);\n setUploadData((prev) => ({\n ...prev,\n errors: [error.message],\n state: UploadState.Error,\n }));\n }, []);\n\n const renderStatusContent = () => {\n const { state, progress, files, errors } = uploadData;\n\n switch (state) {\n case UploadState.Uploading:\n return (\n <Dropzone.LoadingProgressBar\n progressBarPrecentage={Math.round(progress)}\n />\n );\n\n case UploadState.Success:\n return (\n <div className=\"n-flex n-flex-col n-justify-center n-items-center n-gap-y-token-32\">\n <Typography\n variant=\"title-4\"\n className=\"n-text-success-text n-font-semibold\"\n >\n Upload Complete!\n </Typography>\n <Typography\n variant=\"body-small\"\n className=\"n-text-neutral-text-weak\"\n >\n Successfully uploaded: {files.map((file) => file.name).join(', ')}\n </Typography>\n <FilledButton onClick={resetUpload}>Upload More Files</FilledButton>\n </div>\n );\n\n case UploadState.Error:\n return (\n <div className=\"n-flex n-flex-col n-justify-center n-items-center n-gap-y-token-32\">\n <XMarkIcon className=\"n-w-token-32 n-h-token-32 n-text-danger-text\" />\n <div className=\"n-flex n-flex-col n-gap-y-token-2\">\n {errors.map((message, index) => (\n <Typography\n key={index}\n variant=\"label\"\n className=\"n-text-danger-text n-font-semibold\"\n htmlAttributes={{ role: 'alert' }}\n >\n {message}\n </Typography>\n ))}\n </div>\n <FilledButton onClick={resetUpload}>Try Again</FilledButton>\n </div>\n );\n\n default:\n return undefined;\n }\n };\n\n return (\n <Dropzone\n dropZoneOptions={{\n disabled: uploadData.state === UploadState.Uploading,\n maxFiles: CONFIG.MAX_FILES,\n maxSize: CONFIG.MAX_FILE_SIZE,\n multiple: true,\n onDrop: handleDrop,\n onDropRejected: handleDropRejected,\n onError: handleError,\n }}\n supportedFilesDescription={\n <div className=\"n-text-center n-flex n-flex-col n-gap-y-token-2\">\n <Typography variant=\"body-small\" className=\"n-text-neutral-text-weak\">\n Supports: Text files (.txt, .json) only\n </Typography>\n <Typography variant=\"body-small\" className=\"n-text-neutral-text-weak\">\n Max {CONFIG.MAX_FILES} files •{' '}\n {CONFIG.MAX_FILE_SIZE / (1024 * 1024)}MB per file\n </Typography>\n </div>\n }\n acceptedFileExtensions={CONFIG.ACCEPTED_EXTENSIONS}\n loadingElement={renderStatusContent()}\n rejectedText=\"Error: Please make sure to only upload .json or .txt files\"\n heading=\"Drag & Drop your files\"\n />\n );\n};\n\nexport default Component;\n"]}