@salt-ds/lab 1.0.0-alpha.86 → 1.0.0-alpha.88

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 (132) hide show
  1. package/CHANGELOG.md +126 -0
  2. package/css/salt-lab.css +145 -212
  3. package/dist-cjs/calendar/internal/CalendarDay.css.js +1 -1
  4. package/dist-cjs/common-hooks/useSelection.js +0 -2
  5. package/dist-cjs/common-hooks/useSelection.js.map +1 -1
  6. package/dist-cjs/contact-details/ContactDetails.css.js +1 -1
  7. package/dist-cjs/date-input/DateInput.css.js +1 -1
  8. package/dist-cjs/date-input/DateInputRange.js +2 -8
  9. package/dist-cjs/date-input/DateInputRange.js.map +1 -1
  10. package/dist-cjs/date-input/DateInputSingle.js +1 -1
  11. package/dist-cjs/date-input/DateInputSingle.js.map +1 -1
  12. package/dist-cjs/index.js +6 -2
  13. package/dist-cjs/index.js.map +1 -1
  14. package/dist-cjs/list-deprecated/ListItem.js.map +1 -1
  15. package/dist-cjs/rating/Rating.css.js +1 -1
  16. package/dist-cjs/rating/Rating.js +8 -7
  17. package/dist-cjs/rating/Rating.js.map +1 -1
  18. package/dist-cjs/tabs-next/TabListNext.js +1 -1
  19. package/dist-cjs/tabs-next/TabListNext.js.map +1 -1
  20. package/dist-cjs/tokenized-input-next/TokenizedInputNext.js +2 -2
  21. package/dist-cjs/tokenized-input-next/TokenizedInputNext.js.map +1 -1
  22. package/dist-cjs/tree/Tree.css.js +1 -1
  23. package/dist-cjs/tree/Tree.js +274 -207
  24. package/dist-cjs/tree/Tree.js.map +1 -1
  25. package/dist-cjs/tree/TreeContext.js +31 -0
  26. package/dist-cjs/tree/TreeContext.js.map +1 -0
  27. package/dist-cjs/tree/TreeNode.css.js +1 -1
  28. package/dist-cjs/tree/TreeNode.js +86 -42
  29. package/dist-cjs/tree/TreeNode.js.map +1 -1
  30. package/dist-cjs/tree/TreeNodeExpansionIcon.css.js +6 -0
  31. package/dist-cjs/tree/TreeNodeExpansionIcon.css.js.map +1 -0
  32. package/dist-cjs/tree/TreeNodeExpansionIcon.js +62 -0
  33. package/dist-cjs/tree/TreeNodeExpansionIcon.js.map +1 -0
  34. package/dist-cjs/tree/TreeNodeLabel.css.js +6 -0
  35. package/dist-cjs/tree/TreeNodeLabel.css.js.map +1 -0
  36. package/dist-cjs/tree/TreeNodeLabel.js +26 -0
  37. package/dist-cjs/tree/TreeNodeLabel.js.map +1 -0
  38. package/dist-cjs/tree/TreeNodeTrigger.css.js +6 -0
  39. package/dist-cjs/tree/TreeNodeTrigger.css.js.map +1 -0
  40. package/dist-cjs/tree/TreeNodeTrigger.js +152 -0
  41. package/dist-cjs/tree/TreeNodeTrigger.js.map +1 -0
  42. package/dist-cjs/tree/useTree.js +305 -133
  43. package/dist-cjs/tree/useTree.js.map +1 -1
  44. package/dist-es/calendar/internal/CalendarDay.css.js +1 -1
  45. package/dist-es/combo-box-deprecated/internal/DefaultComboBox.js +1 -1
  46. package/dist-es/combo-box-deprecated/internal/MultiSelectComboBox.js +1 -1
  47. package/dist-es/combo-box-deprecated/internal/useComboBox.js +1 -1
  48. package/dist-es/combo-box-deprecated/internal/useMultiSelectComboBox.js +1 -1
  49. package/dist-es/common-hooks/useCollectionItems.js +1 -1
  50. package/dist-es/common-hooks/useSelection.js +1 -2
  51. package/dist-es/common-hooks/useSelection.js.map +1 -1
  52. package/dist-es/contact-details/ContactDetails.css.js +1 -1
  53. package/dist-es/date-input/DateInput.css.js +1 -1
  54. package/dist-es/date-input/DateInputRange.js +2 -8
  55. package/dist-es/date-input/DateInputRange.js.map +1 -1
  56. package/dist-es/date-input/DateInputSingle.js +1 -1
  57. package/dist-es/date-input/DateInputSingle.js.map +1 -1
  58. package/dist-es/dropdown/DropdownBase.js +1 -1
  59. package/dist-es/index.js +3 -1
  60. package/dist-es/index.js.map +1 -1
  61. package/dist-es/list-deprecated/ListItem.js.map +1 -1
  62. package/dist-es/rating/Rating.css.js +1 -1
  63. package/dist-es/rating/Rating.js +8 -7
  64. package/dist-es/rating/Rating.js.map +1 -1
  65. package/dist-es/tabs/drag-drop/useDragDropNaturalMovement.js +1 -1
  66. package/dist-es/tabs-next/TabListNext.js +1 -1
  67. package/dist-es/tabs-next/TabListNext.js.map +1 -1
  68. package/dist-es/tokenized-input/TokenizedInputBase.js +1 -1
  69. package/dist-es/tokenized-input-next/TokenizedInputNext.js +2 -2
  70. package/dist-es/tokenized-input-next/TokenizedInputNext.js.map +1 -1
  71. package/dist-es/tree/Tree.css.js +1 -1
  72. package/dist-es/tree/Tree.js +275 -208
  73. package/dist-es/tree/Tree.js.map +1 -1
  74. package/dist-es/tree/TreeContext.js +26 -0
  75. package/dist-es/tree/TreeContext.js.map +1 -0
  76. package/dist-es/tree/TreeNode.css.js +1 -1
  77. package/dist-es/tree/TreeNode.js +87 -43
  78. package/dist-es/tree/TreeNode.js.map +1 -1
  79. package/dist-es/tree/TreeNodeExpansionIcon.css.js +4 -0
  80. package/dist-es/tree/TreeNodeExpansionIcon.css.js.map +1 -0
  81. package/dist-es/tree/TreeNodeExpansionIcon.js +60 -0
  82. package/dist-es/tree/TreeNodeExpansionIcon.js.map +1 -0
  83. package/dist-es/tree/TreeNodeLabel.css.js +4 -0
  84. package/dist-es/tree/TreeNodeLabel.css.js.map +1 -0
  85. package/dist-es/tree/TreeNodeLabel.js +24 -0
  86. package/dist-es/tree/TreeNodeLabel.js.map +1 -0
  87. package/dist-es/tree/TreeNodeTrigger.css.js +4 -0
  88. package/dist-es/tree/TreeNodeTrigger.css.js.map +1 -0
  89. package/dist-es/tree/TreeNodeTrigger.js +150 -0
  90. package/dist-es/tree/TreeNodeTrigger.js.map +1 -0
  91. package/dist-es/tree/useTree.js +306 -134
  92. package/dist-es/tree/useTree.js.map +1 -1
  93. package/dist-types/date-input/DateInputRange.d.ts +1 -1
  94. package/dist-types/date-input/DateInputSingle.d.ts +1 -1
  95. package/dist-types/index.d.ts +0 -1
  96. package/dist-types/list-deprecated/ListItem.d.ts +1 -0
  97. package/dist-types/rating/Rating.d.ts +5 -6
  98. package/dist-types/tokenized-input/internal/InputPill.d.ts +1 -1
  99. package/dist-types/tokenized-input-next/internal/InputPill.d.ts +1 -1
  100. package/dist-types/tree/Tree.d.ts +36 -3
  101. package/dist-types/tree/TreeContext.d.ts +71 -0
  102. package/dist-types/tree/TreeNode.d.ts +23 -10
  103. package/dist-types/tree/TreeNodeExpansionIcon.d.ts +4 -0
  104. package/dist-types/tree/TreeNodeLabel.d.ts +4 -0
  105. package/dist-types/tree/TreeNodeTrigger.d.ts +8 -0
  106. package/dist-types/tree/index.d.ts +3 -0
  107. package/dist-types/tree/useTree.d.ts +79 -3
  108. package/package.json +3 -3
  109. package/dist-cjs/common-hooks/calcPreferredHeight.js +0 -27
  110. package/dist-cjs/common-hooks/calcPreferredHeight.js.map +0 -1
  111. package/dist-cjs/common-hooks/useAutoSizer.js +0 -33
  112. package/dist-cjs/common-hooks/useAutoSizer.js.map +0 -1
  113. package/dist-cjs/kbd/Kbd.css.js +0 -6
  114. package/dist-cjs/kbd/Kbd.css.js.map +0 -1
  115. package/dist-cjs/kbd/Kbd.js +0 -34
  116. package/dist-cjs/kbd/Kbd.js.map +0 -1
  117. package/dist-cjs/tree/use-tree-keyboard-navigation.js +0 -51
  118. package/dist-cjs/tree/use-tree-keyboard-navigation.js.map +0 -1
  119. package/dist-es/common-hooks/calcPreferredHeight.js +0 -25
  120. package/dist-es/common-hooks/calcPreferredHeight.js.map +0 -1
  121. package/dist-es/common-hooks/useAutoSizer.js +0 -31
  122. package/dist-es/common-hooks/useAutoSizer.js.map +0 -1
  123. package/dist-es/kbd/Kbd.css.js +0 -4
  124. package/dist-es/kbd/Kbd.css.js.map +0 -1
  125. package/dist-es/kbd/Kbd.js +0 -32
  126. package/dist-es/kbd/Kbd.js.map +0 -1
  127. package/dist-es/tree/use-tree-keyboard-navigation.js +0 -48
  128. package/dist-es/tree/use-tree-keyboard-navigation.js.map +0 -1
  129. package/dist-types/kbd/Kbd.d.ts +0 -8
  130. package/dist-types/kbd/index.d.ts +0 -1
  131. package/dist-types/tree/treeTypes.d.ts +0 -42
  132. package/dist-types/tree/use-tree-keyboard-navigation.d.ts +0 -15
@@ -1,53 +1,97 @@
1
1
  import { jsxs, jsx } from 'react/jsx-runtime';
2
- import { makePrefixer } from '@salt-ds/core';
3
- import { TriangleRightIcon } from '@salt-ds/icons';
2
+ import { makePrefixer, useId } from '@salt-ds/core';
4
3
  import { useComponentCssInjection } from '@salt-ds/styles';
5
4
  import { useWindow } from '@salt-ds/window';
6
- import { clsx } from 'clsx';
5
+ import { forwardRef, useMemo, Children, useImperativeHandle, isValidElement } from 'react';
6
+ import { useTreeContext, useTreeNodeContext, TreeNodeProvider } from './TreeContext.js';
7
7
  import css_248z from './TreeNode.css.js';
8
+ import { TreeNodeLabel } from './TreeNodeLabel.js';
9
+ import { TreeNodeTrigger } from './TreeNodeTrigger.js';
8
10
 
9
11
  const withBaseName = makePrefixer("saltTreeNode");
10
- const TreeNode = ({
11
- "aria-level": ariaLevel,
12
- children,
13
- className: classNameProp,
14
- description,
15
- highlighted,
16
- idx,
17
- isLeaf = false,
18
- label,
19
- onMouseEnter,
20
- selected,
21
- ...props
22
- }) => {
23
- const targetWindow = useWindow();
24
- useComponentCssInjection({
25
- testId: "salt-tree-node",
26
- css: css_248z,
27
- window: targetWindow
12
+ function separateChildren(children) {
13
+ const contentChildren = [];
14
+ const nodeChildren = [];
15
+ Children.forEach(children, (child) => {
16
+ if (isValidElement(child) && typeof child.props.value === "string") {
17
+ nodeChildren.push(child);
18
+ } else if (child != null) {
19
+ contentChildren.push(child);
20
+ }
28
21
  });
29
- const className = clsx(withBaseName(), classNameProp, {
30
- [withBaseName("highlighted")]: highlighted
31
- });
32
- return /* @__PURE__ */ jsxs("li", { ...props, className, role: "presentation", children: [
33
- /* @__PURE__ */ jsxs(
34
- "div",
35
- {
36
- "aria-level": ariaLevel,
37
- "aria-selected": selected || void 0,
38
- className: withBaseName("item"),
39
- onMouseEnter,
40
- role: "treeitem",
41
- children: [
42
- isLeaf === false ? /* @__PURE__ */ jsx("span", { className: withBaseName("toggle"), "data-toggle": true, children: /* @__PURE__ */ jsx(TriangleRightIcon, {}) }) : null,
43
- /* @__PURE__ */ jsx("div", { className: withBaseName("label"), children: label }),
44
- description !== void 0 ? /* @__PURE__ */ jsx("div", { className: withBaseName("description"), children: description }) : null
45
- ]
46
- }
47
- ),
48
- children
49
- ] });
50
- };
22
+ return { contentChildren, nodeChildren };
23
+ }
24
+ const TreeNode = forwardRef(
25
+ function TreeNode2(props, ref) {
26
+ const {
27
+ value,
28
+ label,
29
+ icon: Icon,
30
+ disabled: disabledProp,
31
+ children
32
+ } = props;
33
+ const targetWindow = useWindow();
34
+ useComponentCssInjection({
35
+ testId: "salt-tree-node",
36
+ css: css_248z,
37
+ window: targetWindow
38
+ });
39
+ const id = useId(value) ?? value;
40
+ const {
41
+ expandedState,
42
+ selectedSet,
43
+ disabled: treeDisabled,
44
+ disabledIdsSet,
45
+ indeterminateState,
46
+ getElement
47
+ } = useTreeContext();
48
+ const parentContext = useTreeNodeContext();
49
+ const level = ((parentContext == null ? void 0 : parentContext.level) ?? 0) + 1;
50
+ const disabled = treeDisabled || disabledProp || disabledIdsSet.has(value);
51
+ const expanded = expandedState.has(value);
52
+ const selected = selectedSet.has(value);
53
+ const indeterminate = indeterminateState.has(value);
54
+ const usesLabelProp = label !== void 0;
55
+ const { contentChildren, nodeChildren } = useMemo(
56
+ () => usesLabelProp ? { contentChildren: [], nodeChildren: Children.toArray(children) } : separateChildren(children),
57
+ [children, usesLabelProp]
58
+ );
59
+ const hasChildren = nodeChildren.length > 0;
60
+ useImperativeHandle(ref, () => getElement(value), [
61
+ getElement,
62
+ value
63
+ ]);
64
+ const nodeContext = useMemo(
65
+ () => ({
66
+ value,
67
+ level,
68
+ hasChildren,
69
+ expanded,
70
+ disabled,
71
+ id,
72
+ selected,
73
+ indeterminate,
74
+ nodeChildren
75
+ }),
76
+ [
77
+ value,
78
+ level,
79
+ hasChildren,
80
+ expanded,
81
+ disabled,
82
+ id,
83
+ selected,
84
+ indeterminate,
85
+ nodeChildren
86
+ ]
87
+ );
88
+ const defaultContent = usesLabelProp ? /* @__PURE__ */ jsxs(TreeNodeTrigger, { children: [
89
+ Icon ? /* @__PURE__ */ jsx(Icon, { "aria-hidden": true, className: withBaseName("icon") }) : null,
90
+ /* @__PURE__ */ jsx(TreeNodeLabel, { children: label })
91
+ ] }) : null;
92
+ return /* @__PURE__ */ jsx(TreeNodeProvider, { value: nodeContext, children: usesLabelProp ? defaultContent : contentChildren });
93
+ }
94
+ );
51
95
 
52
96
  export { TreeNode };
53
97
  //# sourceMappingURL=TreeNode.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TreeNode.js","sources":["../src/tree/TreeNode.tsx"],"sourcesContent":["import { makePrefixer } from \"@salt-ds/core\";\nimport { TriangleRightIcon } from \"@salt-ds/icons\";\nimport { useComponentCssInjection } from \"@salt-ds/styles\";\nimport { useWindow } from \"@salt-ds/window\";\nimport { clsx } from \"clsx\";\nimport type { HTMLAttributes, MouseEventHandler, ReactNode } from \"react\";\n\nimport treeNodeCss from \"./TreeNode.css\";\n\nconst withBaseName = makePrefixer(\"saltTreeNode\");\nexport interface TreeNodeProps\n extends Omit<HTMLAttributes<HTMLLIElement>, \"onMouseEnter\"> {\n children?: ReactNode;\n description?: string;\n highlighted?: boolean;\n idx?: number;\n isLeaf?: boolean;\n label?: string;\n onMouseEnter?: MouseEventHandler;\n selected?: boolean;\n}\n\nexport const TreeNode = ({\n \"aria-level\": ariaLevel,\n children,\n className: classNameProp,\n description,\n highlighted,\n idx,\n isLeaf = false,\n label,\n onMouseEnter,\n selected,\n ...props\n}: TreeNodeProps) => {\n const targetWindow = useWindow();\n useComponentCssInjection({\n testId: \"salt-tree-node\",\n css: treeNodeCss,\n window: targetWindow,\n });\n\n const className = clsx(withBaseName(), classNameProp, {\n [withBaseName(\"highlighted\")]: highlighted,\n });\n\n return (\n <li {...props} className={className} role=\"presentation\">\n <div\n aria-level={ariaLevel}\n aria-selected={selected || undefined}\n className={withBaseName(\"item\")}\n onMouseEnter={onMouseEnter}\n role=\"treeitem\"\n >\n {isLeaf === false ? (\n <span className={withBaseName(\"toggle\")} data-toggle={true}>\n <TriangleRightIcon />\n </span>\n ) : null}\n <div className={withBaseName(\"label\")}>\n {/* {child.icon ? <span className={`${classBase}Node-icon`} /> : null} */}\n {label}\n </div>\n {description !== undefined ? (\n <div className={withBaseName(\"description\")}>{description}</div>\n ) : null}\n </div>\n {children}\n </li>\n );\n};\n"],"names":["treeNodeCss"],"mappings":";;;;;;;;AASA,MAAM,YAAA,GAAe,aAAa,cAAc,CAAA;AAazC,MAAM,WAAW,CAAC;AAAA,EACvB,YAAA,EAAc,SAAA;AAAA,EACd,QAAA;AAAA,EACA,SAAA,EAAW,aAAA;AAAA,EACX,WAAA;AAAA,EACA,WAAA;AAAA,EACA,GAAA;AAAA,EACA,MAAA,GAAS,KAAA;AAAA,EACT,KAAA;AAAA,EACA,YAAA;AAAA,EACA,QAAA;AAAA,EACA,GAAG;AACL,CAAA,KAAqB;AACnB,EAAA,MAAM,eAAe,SAAA,EAAU;AAC/B,EAAA,wBAAA,CAAyB;AAAA,IACvB,MAAA,EAAQ,gBAAA;AAAA,IACR,GAAA,EAAKA,QAAA;AAAA,IACL,MAAA,EAAQ;AAAA,GACT,CAAA;AAED,EAAA,MAAM,SAAA,GAAY,IAAA,CAAK,YAAA,EAAa,EAAG,aAAA,EAAe;AAAA,IACpD,CAAC,YAAA,CAAa,aAAa,CAAC,GAAG;AAAA,GAChC,CAAA;AAED,EAAA,4BACG,IAAA,EAAA,EAAI,GAAG,KAAA,EAAO,SAAA,EAAsB,MAAK,cAAA,EACxC,QAAA,EAAA;AAAA,oBAAA,IAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,YAAA,EAAY,SAAA;AAAA,QACZ,iBAAe,QAAA,IAAY,MAAA;AAAA,QAC3B,SAAA,EAAW,aAAa,MAAM,CAAA;AAAA,QAC9B,YAAA;AAAA,QACA,IAAA,EAAK,UAAA;AAAA,QAEJ,QAAA,EAAA;AAAA,UAAA,MAAA,KAAW,KAAA,mBACV,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAW,YAAA,CAAa,QAAQ,CAAA,EAAG,aAAA,EAAa,IAAA,EACpD,QAAA,kBAAA,GAAA,CAAC,iBAAA,EAAA,EAAkB,CAAA,EACrB,CAAA,GACE,IAAA;AAAA,8BACH,KAAA,EAAA,EAAI,SAAA,EAAW,YAAA,CAAa,OAAO,GAEjC,QAAA,EAAA,KAAA,EACH,CAAA;AAAA,UACC,WAAA,KAAgB,yBACf,GAAA,CAAC,KAAA,EAAA,EAAI,WAAW,YAAA,CAAa,aAAa,CAAA,EAAI,QAAA,EAAA,WAAA,EAAY,CAAA,GACxD;AAAA;AAAA;AAAA,KACN;AAAA,IACC;AAAA,GAAA,EACH,CAAA;AAEJ;;;;"}
1
+ {"version":3,"file":"TreeNode.js","sources":["../src/tree/TreeNode.tsx"],"sourcesContent":["import { makePrefixer, useId } from \"@salt-ds/core\";\nimport type { IconProps } from \"@salt-ds/icons\";\nimport { useComponentCssInjection } from \"@salt-ds/styles\";\nimport { useWindow } from \"@salt-ds/window\";\nimport {\n Children,\n type ComponentType,\n forwardRef,\n isValidElement,\n type ReactNode,\n useImperativeHandle,\n useMemo,\n} from \"react\";\nimport {\n TreeNodeProvider,\n useTreeContext,\n useTreeNodeContext,\n} from \"./TreeContext\";\nimport treeNodeCss from \"./TreeNode.css\";\nimport { TreeNodeLabel } from \"./TreeNodeLabel\";\nimport { TreeNodeTrigger } from \"./TreeNodeTrigger\";\n\nexport interface TreeNodeProps {\n /**\n * Unique value representing this node within the tree\n */\n value: string;\n /**\n * Label for the node. When provided, TreeNode automatically renders a TreeNodeTrigger.\n */\n label?: ReactNode;\n /**\n * Optional icon to display before the label\n */\n icon?: ComponentType<IconProps>;\n /**\n * Whether the node is disabled.\n */\n disabled?: boolean;\n /**\n * Child nodes or content.\n */\n children?: ReactNode;\n}\n\nconst withBaseName = makePrefixer(\"saltTreeNode\");\n\n// Need to take another look at this because its slightly brittle - alternative could be:\n// TreeNode having a 'content' prop that takes <TreeNodeTrigger> etc. and then `children` is reserved for other <TreeNode>'s\n// or a 'render' prop if we want to pass any state down. Simplifies it massively because then its clear children is for sub trees.\nfunction separateChildren(children: ReactNode): {\n contentChildren: ReactNode[];\n nodeChildren: ReactNode[];\n} {\n const contentChildren: ReactNode[] = [];\n const nodeChildren: ReactNode[] = [];\n\n Children.forEach(children, (child) => {\n if (isValidElement(child) && typeof child.props.value === \"string\") {\n nodeChildren.push(child);\n } else if (child != null) {\n contentChildren.push(child);\n }\n });\n\n return { contentChildren, nodeChildren };\n}\n\nexport const TreeNode = forwardRef<HTMLLIElement, TreeNodeProps>(\n function TreeNode(props, ref) {\n const {\n value,\n label,\n icon: Icon,\n disabled: disabledProp,\n children,\n } = props;\n\n const targetWindow = useWindow();\n useComponentCssInjection({\n testId: \"salt-tree-node\",\n css: treeNodeCss,\n window: targetWindow,\n });\n\n const id = useId(value) ?? value;\n\n const {\n expandedState,\n selectedSet,\n disabled: treeDisabled,\n disabledIdsSet,\n indeterminateState,\n getElement,\n } = useTreeContext();\n\n const parentContext = useTreeNodeContext();\n const level = (parentContext?.level ?? 0) + 1;\n\n const disabled = treeDisabled || disabledProp || disabledIdsSet.has(value);\n const expanded = expandedState.has(value);\n const selected = selectedSet.has(value);\n const indeterminate = indeterminateState.has(value);\n\n const usesLabelProp = label !== undefined;\n const { contentChildren, nodeChildren } = useMemo(\n () =>\n usesLabelProp\n ? { contentChildren: [], nodeChildren: Children.toArray(children) }\n : separateChildren(children),\n [children, usesLabelProp],\n );\n\n const hasChildren = nodeChildren.length > 0;\n\n // Forward ref to the <li> element rendered by TreeNodeTrigger\n useImperativeHandle(ref, () => getElement(value) as HTMLLIElement, [\n getElement,\n value,\n ]);\n\n const nodeContext = useMemo(\n () => ({\n value,\n level,\n hasChildren,\n expanded,\n disabled,\n id,\n selected,\n indeterminate,\n nodeChildren,\n }),\n [\n value,\n level,\n hasChildren,\n expanded,\n disabled,\n id,\n selected,\n indeterminate,\n nodeChildren,\n ],\n );\n\n const defaultContent = usesLabelProp ? (\n <TreeNodeTrigger>\n {Icon ? <Icon aria-hidden className={withBaseName(\"icon\")} /> : null}\n <TreeNodeLabel>{label}</TreeNodeLabel>\n </TreeNodeTrigger>\n ) : null;\n\n return (\n <TreeNodeProvider value={nodeContext}>\n {usesLabelProp ? defaultContent : contentChildren}\n </TreeNodeProvider>\n );\n },\n);\n"],"names":["TreeNode","treeNodeCss"],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ var css_248z = ".saltTreeNodeExpansionIcon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: var(--salt-size-selectable);\n min-width: var(--salt-size-selectable);\n height: var(--salt-size-selectable);\n flex-shrink: 0;\n position: relative;\n}\n\n.saltTreeNodeExpansionIcon-icon {\n color: var(--salt-content-primary-foreground);\n}\n\n.saltTreeNodeExpansionIcon::before {\n content: \"\";\n display: block;\n position: absolute;\n width: var(--salt-size-base);\n height: var(--salt-size-base);\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n}\n";
2
+
3
+ export { css_248z as default };
4
+ //# sourceMappingURL=TreeNodeExpansionIcon.css.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TreeNodeExpansionIcon.css.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
@@ -0,0 +1,60 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { makePrefixer, useIcon } from '@salt-ds/core';
3
+ import { useComponentCssInjection } from '@salt-ds/styles';
4
+ import { useWindow } from '@salt-ds/window';
5
+ import { clsx } from 'clsx';
6
+ import { forwardRef } from 'react';
7
+ import { useTreeNodeContext, useTreeContext } from './TreeContext.js';
8
+ import css_248z from './TreeNodeExpansionIcon.css.js';
9
+
10
+ const withBaseName = makePrefixer("saltTreeNodeExpansionIcon");
11
+ const TreeNodeExpansionIcon = forwardRef(function TreeNodeExpansionIcon2(props, ref) {
12
+ const { className, onClick, ...rest } = props;
13
+ const targetWindow = useWindow();
14
+ useComponentCssInjection({
15
+ testId: "salt-tree-node-expansion-icon",
16
+ css: css_248z,
17
+ window: targetWindow
18
+ });
19
+ const nodeContext = useTreeNodeContext();
20
+ if (!nodeContext) {
21
+ throw new Error("TreeNodeExpansionIcon must be used within a TreeNode");
22
+ }
23
+ const { value, hasChildren, expanded, disabled } = nodeContext;
24
+ const { toggleExpanded } = useTreeContext();
25
+ const { ExpandGroupIcon, CollapseGroupIcon } = useIcon();
26
+ const handleClick = (event) => {
27
+ onClick == null ? void 0 : onClick(event);
28
+ if (disabled || !hasChildren) return;
29
+ toggleExpanded(event, value);
30
+ };
31
+ if (!hasChildren) {
32
+ return /* @__PURE__ */ jsx(
33
+ "span",
34
+ {
35
+ ref,
36
+ className: clsx(withBaseName(), withBaseName("placeholder"), className),
37
+ "aria-hidden": true,
38
+ ...rest
39
+ }
40
+ );
41
+ }
42
+ const Icon = expanded ? CollapseGroupIcon : ExpandGroupIcon;
43
+ return (
44
+ // biome-ignore lint/a11y/useKeyWithClickEvents: keyboard handled at tree level, same as W3C
45
+ /* @__PURE__ */ jsx(
46
+ "span",
47
+ {
48
+ ref,
49
+ className: clsx(withBaseName(), className),
50
+ onClick: handleClick,
51
+ "aria-hidden": true,
52
+ ...rest,
53
+ children: /* @__PURE__ */ jsx(Icon, { className: withBaseName("icon") })
54
+ }
55
+ )
56
+ );
57
+ });
58
+
59
+ export { TreeNodeExpansionIcon };
60
+ //# sourceMappingURL=TreeNodeExpansionIcon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TreeNodeExpansionIcon.js","sources":["../src/tree/TreeNodeExpansionIcon.tsx"],"sourcesContent":["import { makePrefixer, useIcon } from \"@salt-ds/core\";\nimport { useComponentCssInjection } from \"@salt-ds/styles\";\nimport { useWindow } from \"@salt-ds/window\";\nimport { clsx } from \"clsx\";\nimport {\n type ComponentPropsWithoutRef,\n forwardRef,\n type MouseEvent,\n} from \"react\";\nimport { useTreeContext, useTreeNodeContext } from \"./TreeContext\";\nimport treeNodeExpansionIconCss from \"./TreeNodeExpansionIcon.css\";\n\nexport interface TreeNodeExpansionIconProps\n extends ComponentPropsWithoutRef<\"span\"> {}\n\nconst withBaseName = makePrefixer(\"saltTreeNodeExpansionIcon\");\n\nexport const TreeNodeExpansionIcon = forwardRef<\n HTMLSpanElement,\n TreeNodeExpansionIconProps\n>(function TreeNodeExpansionIcon(props, ref) {\n const { className, onClick, ...rest } = props;\n\n const targetWindow = useWindow();\n useComponentCssInjection({\n testId: \"salt-tree-node-expansion-icon\",\n css: treeNodeExpansionIconCss,\n window: targetWindow,\n });\n\n const nodeContext = useTreeNodeContext();\n if (!nodeContext) {\n throw new Error(\"TreeNodeExpansionIcon must be used within a TreeNode\");\n }\n\n const { value, hasChildren, expanded, disabled } = nodeContext;\n const { toggleExpanded } = useTreeContext();\n const { ExpandGroupIcon, CollapseGroupIcon } = useIcon();\n\n const handleClick = (event: MouseEvent<HTMLSpanElement>) => {\n onClick?.(event);\n if (disabled || !hasChildren) return;\n toggleExpanded(event, value);\n };\n\n if (!hasChildren) {\n return (\n <span\n ref={ref}\n className={clsx(withBaseName(), withBaseName(\"placeholder\"), className)}\n aria-hidden\n {...rest}\n />\n );\n }\n\n const Icon = expanded ? CollapseGroupIcon : ExpandGroupIcon;\n\n return (\n // biome-ignore lint/a11y/useKeyWithClickEvents: keyboard handled at tree level, same as W3C\n <span\n ref={ref}\n className={clsx(withBaseName(), className)}\n onClick={handleClick}\n aria-hidden\n {...rest}\n >\n <Icon className={withBaseName(\"icon\")} />\n </span>\n );\n});\n"],"names":["TreeNodeExpansionIcon","treeNodeExpansionIconCss"],"mappings":";;;;;;;;;AAeA,MAAM,YAAA,GAAe,aAAa,2BAA2B,CAAA;AAEtD,MAAM,qBAAA,GAAwB,UAAA,CAGnC,SAASA,sBAAAA,CAAsB,OAAO,GAAA,EAAK;AAC3C,EAAA,MAAM,EAAE,SAAA,EAAW,OAAA,EAAS,GAAG,MAAK,GAAI,KAAA;AAExC,EAAA,MAAM,eAAe,SAAA,EAAU;AAC/B,EAAA,wBAAA,CAAyB;AAAA,IACvB,MAAA,EAAQ,+BAAA;AAAA,IACR,GAAA,EAAKC,QAAA;AAAA,IACL,MAAA,EAAQ;AAAA,GACT,CAAA;AAED,EAAA,MAAM,cAAc,kBAAA,EAAmB;AACvC,EAAA,IAAI,CAAC,WAAA,EAAa;AAChB,IAAA,MAAM,IAAI,MAAM,sDAAsD,CAAA;AAAA,EACxE;AAEA,EAAA,MAAM,EAAE,KAAA,EAAO,WAAA,EAAa,QAAA,EAAU,UAAS,GAAI,WAAA;AACnD,EAAA,MAAM,EAAE,cAAA,EAAe,GAAI,cAAA,EAAe;AAC1C,EAAA,MAAM,EAAE,eAAA,EAAiB,iBAAA,EAAkB,GAAI,OAAA,EAAQ;AAEvD,EAAA,MAAM,WAAA,GAAc,CAAC,KAAA,KAAuC;AAC1D,IAAA,OAAA,IAAA,IAAA,GAAA,MAAA,GAAA,OAAA,CAAU,KAAA,CAAA;AACV,IAAA,IAAI,QAAA,IAAY,CAAC,WAAA,EAAa;AAC9B,IAAA,cAAA,CAAe,OAAO,KAAK,CAAA;AAAA,EAC7B,CAAA;AAEA,EAAA,IAAI,CAAC,WAAA,EAAa;AAChB,IAAA,uBACE,GAAA;AAAA,MAAC,MAAA;AAAA,MAAA;AAAA,QACC,GAAA;AAAA,QACA,WAAW,IAAA,CAAK,YAAA,IAAgB,YAAA,CAAa,aAAa,GAAG,SAAS,CAAA;AAAA,QACtE,aAAA,EAAW,IAAA;AAAA,QACV,GAAG;AAAA;AAAA,KACN;AAAA,EAEJ;AAEA,EAAA,MAAM,IAAA,GAAO,WAAW,iBAAA,GAAoB,eAAA;AAE5C,EAAA;AAAA;AAAA,oBAEE,GAAA;AAAA,MAAC,MAAA;AAAA,MAAA;AAAA,QACC,GAAA;AAAA,QACA,SAAA,EAAW,IAAA,CAAK,YAAA,EAAa,EAAG,SAAS,CAAA;AAAA,QACzC,OAAA,EAAS,WAAA;AAAA,QACT,aAAA,EAAW,IAAA;AAAA,QACV,GAAG,IAAA;AAAA,QAEJ,QAAA,kBAAA,GAAA,CAAC,IAAA,EAAA,EAAK,SAAA,EAAW,YAAA,CAAa,MAAM,CAAA,EAAG;AAAA;AAAA;AACzC;AAEJ,CAAC;;;;"}
@@ -0,0 +1,4 @@
1
+ var css_248z = ".saltTreeNodeLabel {\n flex: 1;\n font-family: var(--salt-text-fontFamily);\n font-size: var(--salt-text-fontSize);\n font-weight: var(--salt-text-fontWeight);\n line-height: var(--salt-text-lineHeight);\n letter-spacing: var(--salt-text-letterSpacing);\n word-break: break-word;\n}\n";
2
+
3
+ export { css_248z as default };
4
+ //# sourceMappingURL=TreeNodeLabel.css.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TreeNodeLabel.css.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
@@ -0,0 +1,24 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { makePrefixer } from '@salt-ds/core';
3
+ import { useComponentCssInjection } from '@salt-ds/styles';
4
+ import { useWindow } from '@salt-ds/window';
5
+ import { clsx } from 'clsx';
6
+ import { forwardRef } from 'react';
7
+ import css_248z from './TreeNodeLabel.css.js';
8
+
9
+ const withBaseName = makePrefixer("saltTreeNodeLabel");
10
+ const TreeNodeLabel = forwardRef(
11
+ function TreeNodeLabel2(props, ref) {
12
+ const { children, className, ...rest } = props;
13
+ const targetWindow = useWindow();
14
+ useComponentCssInjection({
15
+ testId: "salt-tree-node-label",
16
+ css: css_248z,
17
+ window: targetWindow
18
+ });
19
+ return /* @__PURE__ */ jsx("span", { ref, className: clsx(withBaseName(), className), ...rest, children });
20
+ }
21
+ );
22
+
23
+ export { TreeNodeLabel };
24
+ //# sourceMappingURL=TreeNodeLabel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TreeNodeLabel.js","sources":["../src/tree/TreeNodeLabel.tsx"],"sourcesContent":["import { makePrefixer } from \"@salt-ds/core\";\nimport { useComponentCssInjection } from \"@salt-ds/styles\";\nimport { useWindow } from \"@salt-ds/window\";\nimport { clsx } from \"clsx\";\nimport { type ComponentPropsWithoutRef, forwardRef } from \"react\";\nimport treeNodeLabelCss from \"./TreeNodeLabel.css\";\n\nexport interface TreeNodeLabelProps extends ComponentPropsWithoutRef<\"span\"> {}\n\nconst withBaseName = makePrefixer(\"saltTreeNodeLabel\");\n\nexport const TreeNodeLabel = forwardRef<HTMLSpanElement, TreeNodeLabelProps>(\n function TreeNodeLabel(props, ref) {\n const { children, className, ...rest } = props;\n\n const targetWindow = useWindow();\n useComponentCssInjection({\n testId: \"salt-tree-node-label\",\n css: treeNodeLabelCss,\n window: targetWindow,\n });\n\n return (\n <span ref={ref} className={clsx(withBaseName(), className)} {...rest}>\n {children}\n </span>\n );\n },\n);\n"],"names":["TreeNodeLabel","treeNodeLabelCss"],"mappings":";;;;;;;;AASA,MAAM,YAAA,GAAe,aAAa,mBAAmB,CAAA;AAE9C,MAAM,aAAA,GAAgB,UAAA;AAAA,EAC3B,SAASA,cAAAA,CAAc,KAAA,EAAO,GAAA,EAAK;AACjC,IAAA,MAAM,EAAE,QAAA,EAAU,SAAA,EAAW,GAAG,MAAK,GAAI,KAAA;AAEzC,IAAA,MAAM,eAAe,SAAA,EAAU;AAC/B,IAAA,wBAAA,CAAyB;AAAA,MACvB,MAAA,EAAQ,sBAAA;AAAA,MACR,GAAA,EAAKC,QAAA;AAAA,MACL,MAAA,EAAQ;AAAA,KACT,CAAA;AAED,IAAA,uBACE,GAAA,CAAC,MAAA,EAAA,EAAK,GAAA,EAAU,SAAA,EAAW,IAAA,CAAK,YAAA,EAAa,EAAG,SAAS,CAAA,EAAI,GAAG,IAAA,EAC7D,QAAA,EACH,CAAA;AAAA,EAEJ;AACF;;;;"}
@@ -0,0 +1,4 @@
1
+ var css_248z = ".saltTreeNodeTrigger {\n box-sizing: border-box;\n display: flex;\n align-items: flex-start;\n gap: var(--salt-spacing-100);\n width: 100%;\n min-height: var(--salt-size-base);\n\n padding-top: calc((var(--salt-size-base) - var(--salt-size-selectable)) / 2);\n padding-bottom: calc((var(--salt-size-base) - var(--salt-size-selectable)) / 2);\n padding-right: var(--salt-spacing-100);\n /* Base padding for level-1 nodes, plus one indent step per additional level.\n Each indent step = (selectable element width + gap).\n Level-1 nodes get no extra indent; level-2 gets 1 step, level-3 gets 2, etc.\n --saltTreeNodeTrigger-indentStep is overridden in multiselect to account\n for the wider spacing around the checkbox. */\n --saltTreeNodeTrigger-indentStep: calc(var(--salt-size-selectable) + var(--salt-spacing-100));\n padding-left: calc(var(--salt-spacing-100) + (var(--saltTreeNodeTrigger-indentStep) * (var(--saltTreeNode-level, 1) - 1)));\n background: var(--salt-selectable-background);\n color: var(--salt-content-primary-foreground);\n}\n\n[aria-multiselectable=\"true\"] .saltTreeNodeTrigger {\n --saltTreeNodeTrigger-indentStep: calc(var(--salt-size-selectable) + var(--salt-spacing-150));\n}\n\n.saltTreeNodeTrigger:hover {\n background: var(--salt-selectable-background-hover);\n}\n\n.saltTreeNode:focus-visible > .saltTreeNodeTrigger,\n.saltTreeNode-focusVisible > .saltTreeNodeTrigger {\n background: var(--salt-selectable-background-hover);\n}\n\n.saltTreeNode-selected > .saltTreeNodeTrigger {\n background: var(--salt-selectable-background-selected);\n box-shadow:\n 0 calc(var(--salt-size-border) * -1) 0 0 var(--salt-selectable-borderColor-selected),\n 0 var(--salt-size-border) 0 0 var(--salt-selectable-borderColor-selected);\n position: relative;\n z-index: var(--salt-zIndex-default);\n}\n\n.saltTreeNode-selected:focus-visible > .saltTreeNodeTrigger,\n.saltTreeNode-selected.saltTreeNode-focusVisible > .saltTreeNodeTrigger {\n background: var(--salt-selectable-background-selected);\n}\n\n.saltTreeNode-disabled > .saltTreeNodeTrigger,\n.saltTreeNode-disabled:hover > .saltTreeNodeTrigger {\n opacity: 0.4;\n cursor: var(--salt-cursor-disabled);\n\n background: var(--salt-selectable-background);\n color: var(--salt-content-primary-foreground);\n}\n\n.saltTreeNodeTrigger > .saltIcon {\n width: var(--salt-size-selectable);\n height: var(--salt-size-selectable);\n min-width: var(--salt-size-selectable);\n flex-shrink: 0;\n color: var(--salt-content-primary-foreground);\n}\n\n.saltTreeNodeTrigger > .saltTreeNode-checkbox {\n margin-left: calc(var(--salt-spacing-150) - var(--salt-spacing-100));\n margin-right: calc(var(--salt-spacing-150) - var(--salt-spacing-100));\n}\n";
2
+
3
+ export { css_248z as default };
4
+ //# sourceMappingURL=TreeNodeTrigger.css.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TreeNodeTrigger.css.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
@@ -0,0 +1,150 @@
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import { makePrefixer, useForkRef, CheckboxIcon } from '@salt-ds/core';
3
+ import { useComponentCssInjection } from '@salt-ds/styles';
4
+ import { useWindow } from '@salt-ds/window';
5
+ import { clsx } from 'clsx';
6
+ import { forwardRef, useRef, useState, useEffect } from 'react';
7
+ import { useTreeNodeContext, useTreeContext } from './TreeContext.js';
8
+ import { TreeNodeExpansionIcon } from './TreeNodeExpansionIcon.js';
9
+ import css_248z from './TreeNodeTrigger.css.js';
10
+
11
+ const withBaseName = makePrefixer("saltTreeNodeTrigger");
12
+ const withNodeBaseName = makePrefixer("saltTreeNode");
13
+ const TreeNodeTrigger = forwardRef(function TreeNodeTrigger2(props, ref) {
14
+ const {
15
+ className,
16
+ children,
17
+ style,
18
+ onClick,
19
+ onFocus,
20
+ onBlur,
21
+ onMouseDown,
22
+ onKeyDown,
23
+ ...rest
24
+ } = props;
25
+ const targetWindow = useWindow();
26
+ useComponentCssInjection({
27
+ testId: "salt-tree-node-trigger",
28
+ css: css_248z,
29
+ window: targetWindow
30
+ });
31
+ const nodeContext = useTreeNodeContext();
32
+ if (!nodeContext) {
33
+ throw new Error("TreeNodeTrigger must be used within a TreeNode");
34
+ }
35
+ const {
36
+ value,
37
+ level,
38
+ hasChildren,
39
+ expanded,
40
+ disabled,
41
+ id,
42
+ selected,
43
+ indeterminate,
44
+ nodeChildren
45
+ } = nodeContext;
46
+ const {
47
+ multiselect,
48
+ activeNode,
49
+ setActiveNode,
50
+ select,
51
+ tabbableNodeId,
52
+ registerElement
53
+ } = useTreeContext();
54
+ const nodeRef = useRef(null);
55
+ const triggerContentRef = useForkRef(useRef(null), ref);
56
+ const wasMouseDownRef = useRef(false);
57
+ const [focusVisible, setFocusVisible] = useState(false);
58
+ const isActive = activeNode === value;
59
+ const isTabbable = tabbableNodeId === value;
60
+ useEffect(() => {
61
+ if (nodeRef.current) {
62
+ return registerElement(value, nodeRef.current);
63
+ }
64
+ }, [value, registerElement]);
65
+ const handleClick = (event) => {
66
+ onClick == null ? void 0 : onClick(event);
67
+ if (disabled) return;
68
+ const target = event.target;
69
+ if (target.closest(".saltTreeNodeExpansionIcon")) return;
70
+ const nestedTreeItem = target.closest('[role="treeitem"]');
71
+ if (nestedTreeItem && nestedTreeItem !== nodeRef.current) {
72
+ return;
73
+ }
74
+ setActiveNode(value);
75
+ select(event, value);
76
+ };
77
+ const handleFocus = (event) => {
78
+ onFocus == null ? void 0 : onFocus(event);
79
+ if (event.target !== event.currentTarget) return;
80
+ if (!wasMouseDownRef.current) {
81
+ setFocusVisible(true);
82
+ }
83
+ wasMouseDownRef.current = false;
84
+ setActiveNode(value);
85
+ };
86
+ const handleBlur = (event) => {
87
+ onBlur == null ? void 0 : onBlur(event);
88
+ if (event.target !== event.currentTarget) return;
89
+ setFocusVisible(false);
90
+ };
91
+ const handleMouseDown = (event) => {
92
+ wasMouseDownRef.current = true;
93
+ onMouseDown == null ? void 0 : onMouseDown(event);
94
+ };
95
+ return /* @__PURE__ */ jsxs(
96
+ "li",
97
+ {
98
+ ref: nodeRef,
99
+ id,
100
+ role: "treeitem",
101
+ "aria-expanded": hasChildren ? expanded : void 0,
102
+ "aria-selected": multiselect ? void 0 : selected,
103
+ "aria-checked": multiselect ? indeterminate ? "mixed" : selected : void 0,
104
+ "aria-level": level,
105
+ "aria-disabled": disabled || void 0,
106
+ tabIndex: isTabbable ? 0 : -1,
107
+ className: clsx(
108
+ withNodeBaseName(),
109
+ {
110
+ [withNodeBaseName("expanded")]: expanded,
111
+ [withNodeBaseName("selected")]: selected && !multiselect,
112
+ [withNodeBaseName("active")]: isActive,
113
+ [withNodeBaseName("disabled")]: disabled,
114
+ [withNodeBaseName("hasChildren")]: hasChildren,
115
+ [withNodeBaseName("focusVisible")]: focusVisible
116
+ },
117
+ className
118
+ ),
119
+ style: {
120
+ "--saltTreeNode-level": level,
121
+ ...style
122
+ },
123
+ onClick: handleClick,
124
+ onFocus: handleFocus,
125
+ onBlur: handleBlur,
126
+ onMouseDown: handleMouseDown,
127
+ onKeyDown,
128
+ ...rest,
129
+ children: [
130
+ /* @__PURE__ */ jsxs("span", { ref: triggerContentRef, className: withBaseName(), children: [
131
+ /* @__PURE__ */ jsx(TreeNodeExpansionIcon, {}),
132
+ multiselect && /* @__PURE__ */ jsx(
133
+ CheckboxIcon,
134
+ {
135
+ checked: selected,
136
+ indeterminate,
137
+ disabled,
138
+ className: withNodeBaseName("checkbox")
139
+ }
140
+ ),
141
+ children
142
+ ] }),
143
+ hasChildren && expanded && /* @__PURE__ */ jsx("ul", { role: "group", className: withNodeBaseName("group"), children: nodeChildren })
144
+ ]
145
+ }
146
+ );
147
+ });
148
+
149
+ export { TreeNodeTrigger };
150
+ //# sourceMappingURL=TreeNodeTrigger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TreeNodeTrigger.js","sources":["../src/tree/TreeNodeTrigger.tsx"],"sourcesContent":["import { CheckboxIcon, makePrefixer, useForkRef } from \"@salt-ds/core\";\nimport { useComponentCssInjection } from \"@salt-ds/styles\";\nimport { useWindow } from \"@salt-ds/window\";\nimport { clsx } from \"clsx\";\nimport {\n type ComponentPropsWithoutRef,\n type CSSProperties,\n type FocusEvent,\n forwardRef,\n type MouseEvent,\n useEffect,\n useRef,\n useState,\n} from \"react\";\nimport { useTreeContext, useTreeNodeContext } from \"./TreeContext\";\nimport { TreeNodeExpansionIcon } from \"./TreeNodeExpansionIcon\";\nimport treeNodeTriggerCss from \"./TreeNodeTrigger.css\";\n\nexport interface TreeNodeTriggerProps extends ComponentPropsWithoutRef<\"li\"> {}\n\nconst withBaseName = makePrefixer(\"saltTreeNodeTrigger\");\nconst withNodeBaseName = makePrefixer(\"saltTreeNode\");\n\n/**\n * The forwarded ref points to the inner trigger content span (for tooltip positioning),\n * while the <li> handles focus, ARIA, and event handling.\n */\nexport const TreeNodeTrigger = forwardRef<\n HTMLSpanElement,\n TreeNodeTriggerProps\n>(function TreeNodeTrigger(props, ref) {\n const {\n className,\n children,\n style,\n onClick,\n onFocus,\n onBlur,\n onMouseDown,\n onKeyDown,\n ...rest\n } = props;\n\n const targetWindow = useWindow();\n useComponentCssInjection({\n testId: \"salt-tree-node-trigger\",\n css: treeNodeTriggerCss,\n window: targetWindow,\n });\n\n const nodeContext = useTreeNodeContext();\n if (!nodeContext) {\n throw new Error(\"TreeNodeTrigger must be used within a TreeNode\");\n }\n\n const {\n value,\n level,\n hasChildren,\n expanded,\n disabled,\n id,\n selected,\n indeterminate,\n nodeChildren,\n } = nodeContext;\n\n const {\n multiselect,\n activeNode,\n setActiveNode,\n select,\n tabbableNodeId,\n registerElement,\n } = useTreeContext();\n\n const nodeRef = useRef<HTMLLIElement>(null);\n const triggerContentRef = useForkRef(useRef<HTMLSpanElement>(null), ref);\n const wasMouseDownRef = useRef(false);\n const [focusVisible, setFocusVisible] = useState(false);\n\n const isActive = activeNode === value;\n\n const isTabbable = tabbableNodeId === value;\n\n useEffect(() => {\n if (nodeRef.current) {\n return registerElement(value, nodeRef.current);\n }\n }, [value, registerElement]);\n\n const handleClick = (event: MouseEvent<HTMLLIElement>) => {\n onClick?.(event);\n if (disabled) return;\n const target = event.target as HTMLElement;\n if (target.closest(\".saltTreeNodeExpansionIcon\")) return;\n const nestedTreeItem = target.closest('[role=\"treeitem\"]');\n if (nestedTreeItem && nestedTreeItem !== nodeRef.current) {\n return;\n }\n setActiveNode(value);\n select(event, value);\n };\n\n const handleFocus = (event: FocusEvent<HTMLLIElement>) => {\n onFocus?.(event);\n if (event.target !== event.currentTarget) return;\n if (!wasMouseDownRef.current) {\n setFocusVisible(true);\n }\n wasMouseDownRef.current = false;\n setActiveNode(value);\n };\n\n const handleBlur = (event: FocusEvent<HTMLLIElement>) => {\n onBlur?.(event);\n if (event.target !== event.currentTarget) return;\n setFocusVisible(false);\n };\n\n const handleMouseDown = (event: MouseEvent<HTMLLIElement>) => {\n wasMouseDownRef.current = true;\n onMouseDown?.(event);\n };\n\n return (\n <li\n ref={nodeRef}\n id={id}\n role=\"treeitem\"\n aria-expanded={hasChildren ? expanded : undefined}\n aria-selected={multiselect ? undefined : selected}\n aria-checked={\n multiselect ? (indeterminate ? \"mixed\" : selected) : undefined\n }\n aria-level={level}\n aria-disabled={disabled || undefined}\n tabIndex={isTabbable ? 0 : -1}\n className={clsx(\n withNodeBaseName(),\n {\n [withNodeBaseName(\"expanded\")]: expanded,\n [withNodeBaseName(\"selected\")]: selected && !multiselect,\n [withNodeBaseName(\"active\")]: isActive,\n [withNodeBaseName(\"disabled\")]: disabled,\n [withNodeBaseName(\"hasChildren\")]: hasChildren,\n [withNodeBaseName(\"focusVisible\")]: focusVisible,\n },\n className,\n )}\n style={\n {\n \"--saltTreeNode-level\": level,\n ...style,\n } as CSSProperties\n }\n onClick={handleClick}\n onFocus={handleFocus}\n onBlur={handleBlur}\n onMouseDown={handleMouseDown}\n onKeyDown={onKeyDown}\n {...rest}\n >\n <span ref={triggerContentRef} className={withBaseName()}>\n <TreeNodeExpansionIcon />\n {multiselect && (\n <CheckboxIcon\n checked={selected}\n indeterminate={indeterminate}\n disabled={disabled}\n className={withNodeBaseName(\"checkbox\")}\n />\n )}\n {children}\n </span>\n\n {hasChildren && expanded && (\n <ul role=\"group\" className={withNodeBaseName(\"group\")}>\n {nodeChildren}\n </ul>\n )}\n </li>\n );\n});\n"],"names":["TreeNodeTrigger","treeNodeTriggerCss"],"mappings":"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"}