@react-spectrum/s2 3.0.0-nightly-fd7075c5f-250128 → 3.0.0-nightly-e3ed3c7f6-250130

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 (268) hide show
  1. package/dist/Accordion.cjs +3 -3
  2. package/dist/Accordion.css +2 -2
  3. package/dist/Accordion.mjs +3 -3
  4. package/dist/ActionBar.cjs +47 -47
  5. package/dist/ActionBar.css +51 -51
  6. package/dist/ActionBar.mjs +47 -47
  7. package/dist/ActionButton.cjs +97 -97
  8. package/dist/ActionButton.css +96 -96
  9. package/dist/ActionButton.mjs +97 -97
  10. package/dist/ActionButtonGroup.cjs +11 -11
  11. package/dist/ActionButtonGroup.css +9 -9
  12. package/dist/ActionButtonGroup.mjs +11 -11
  13. package/dist/AlertDialog.cjs +3 -3
  14. package/dist/AlertDialog.css +3 -3
  15. package/dist/AlertDialog.mjs +3 -3
  16. package/dist/Avatar.cjs +17 -17
  17. package/dist/Avatar.css +14 -14
  18. package/dist/Avatar.mjs +17 -17
  19. package/dist/AvatarGroup.cjs +100 -100
  20. package/dist/AvatarGroup.css +34 -34
  21. package/dist/AvatarGroup.mjs +100 -100
  22. package/dist/Badge.cjs +91 -67
  23. package/dist/Badge.cjs.map +1 -1
  24. package/dist/Badge.css +163 -91
  25. package/dist/Badge.css.map +1 -1
  26. package/dist/Badge.mjs +91 -67
  27. package/dist/Badge.mjs.map +1 -1
  28. package/dist/Breadcrumbs.cjs +115 -115
  29. package/dist/Breadcrumbs.css +89 -89
  30. package/dist/Breadcrumbs.mjs +115 -115
  31. package/dist/Button.cjs +236 -152
  32. package/dist/Button.cjs.map +1 -1
  33. package/dist/Button.css +309 -165
  34. package/dist/Button.css.map +1 -1
  35. package/dist/Button.mjs +236 -152
  36. package/dist/Button.mjs.map +1 -1
  37. package/dist/ButtonGroup.cjs +19 -19
  38. package/dist/ButtonGroup.css +15 -15
  39. package/dist/ButtonGroup.mjs +19 -19
  40. package/dist/Card.cjs +264 -261
  41. package/dist/Card.cjs.map +1 -1
  42. package/dist/Card.css +210 -198
  43. package/dist/Card.css.map +1 -1
  44. package/dist/Card.mjs +264 -261
  45. package/dist/Card.mjs.map +1 -1
  46. package/dist/CardView.cjs +15 -15
  47. package/dist/CardView.css +18 -18
  48. package/dist/CardView.mjs +15 -15
  49. package/dist/CenterBaseline.cjs +1 -1
  50. package/dist/CenterBaseline.css +2 -2
  51. package/dist/CenterBaseline.mjs +1 -1
  52. package/dist/Checkbox.cjs +124 -73
  53. package/dist/Checkbox.cjs.map +1 -1
  54. package/dist/Checkbox.css +201 -81
  55. package/dist/Checkbox.css.map +1 -1
  56. package/dist/Checkbox.mjs +124 -73
  57. package/dist/Checkbox.mjs.map +1 -1
  58. package/dist/CheckboxGroup.cjs +49 -49
  59. package/dist/CheckboxGroup.css +41 -41
  60. package/dist/CheckboxGroup.mjs +49 -49
  61. package/dist/ClearButton.cjs +15 -15
  62. package/dist/ClearButton.css +17 -17
  63. package/dist/ClearButton.mjs +15 -15
  64. package/dist/CloseButton.cjs +35 -35
  65. package/dist/CloseButton.css +33 -33
  66. package/dist/CloseButton.mjs +35 -35
  67. package/dist/ColorArea.cjs +22 -22
  68. package/dist/ColorArea.css +15 -15
  69. package/dist/ColorArea.mjs +22 -22
  70. package/dist/ColorField.cjs +38 -38
  71. package/dist/ColorField.css +32 -32
  72. package/dist/ColorField.mjs +38 -38
  73. package/dist/ColorHandle.cjs +27 -21
  74. package/dist/ColorHandle.cjs.map +1 -1
  75. package/dist/ColorHandle.css +93 -45
  76. package/dist/ColorHandle.css.map +1 -1
  77. package/dist/ColorHandle.mjs +27 -21
  78. package/dist/ColorHandle.mjs.map +1 -1
  79. package/dist/ColorSlider.cjs +52 -52
  80. package/dist/ColorSlider.css +51 -51
  81. package/dist/ColorSlider.mjs +52 -52
  82. package/dist/ColorSwatch.cjs +27 -24
  83. package/dist/ColorSwatch.cjs.map +1 -1
  84. package/dist/ColorSwatch.css +41 -29
  85. package/dist/ColorSwatch.css.map +1 -1
  86. package/dist/ColorSwatch.mjs +27 -24
  87. package/dist/ColorSwatch.mjs.map +1 -1
  88. package/dist/ColorSwatchPicker.cjs +23 -23
  89. package/dist/ColorSwatchPicker.css +60 -48
  90. package/dist/ColorSwatchPicker.css.map +1 -1
  91. package/dist/ColorSwatchPicker.mjs +23 -23
  92. package/dist/ColorWheel.cjs +22 -22
  93. package/dist/ColorWheel.css +16 -16
  94. package/dist/ColorWheel.mjs +22 -22
  95. package/dist/ComboBox.cjs +80 -80
  96. package/dist/ComboBox.css +88 -88
  97. package/dist/ComboBox.mjs +80 -80
  98. package/dist/ContextualHelp.cjs +5 -5
  99. package/dist/ContextualHelp.css +38 -38
  100. package/dist/ContextualHelp.mjs +5 -5
  101. package/dist/CustomDialog.cjs +31 -31
  102. package/dist/CustomDialog.css +25 -25
  103. package/dist/CustomDialog.mjs +31 -31
  104. package/dist/Dialog.cjs +17 -17
  105. package/dist/Dialog.css +64 -64
  106. package/dist/Dialog.mjs +17 -17
  107. package/dist/Disclosure.cjs +111 -108
  108. package/dist/Disclosure.cjs.map +1 -1
  109. package/dist/Disclosure.css +124 -112
  110. package/dist/Disclosure.css.map +1 -1
  111. package/dist/Disclosure.mjs +111 -108
  112. package/dist/Disclosure.mjs.map +1 -1
  113. package/dist/Divider.cjs +26 -26
  114. package/dist/Divider.css +16 -16
  115. package/dist/Divider.mjs +26 -26
  116. package/dist/DropZone.cjs +56 -47
  117. package/dist/DropZone.cjs.map +1 -1
  118. package/dist/DropZone.css +80 -56
  119. package/dist/DropZone.css.map +1 -1
  120. package/dist/DropZone.mjs +56 -47
  121. package/dist/DropZone.mjs.map +1 -1
  122. package/dist/Field.cjs +204 -150
  123. package/dist/Field.cjs.map +1 -1
  124. package/dist/Field.css +246 -150
  125. package/dist/Field.css.map +1 -1
  126. package/dist/Field.mjs +204 -150
  127. package/dist/Field.mjs.map +1 -1
  128. package/dist/Form.cjs +10 -10
  129. package/dist/Form.css +9 -9
  130. package/dist/Form.mjs +10 -10
  131. package/dist/FullscreenDialog.cjs +5 -5
  132. package/dist/FullscreenDialog.css +72 -72
  133. package/dist/FullscreenDialog.mjs +5 -5
  134. package/dist/IllustratedMessage.cjs +134 -134
  135. package/dist/IllustratedMessage.css +69 -69
  136. package/dist/IllustratedMessage.mjs +134 -134
  137. package/dist/Image.cjs +12 -12
  138. package/dist/Image.css +13 -13
  139. package/dist/Image.mjs +12 -12
  140. package/dist/InlineAlert.cjs +104 -77
  141. package/dist/InlineAlert.cjs.map +1 -1
  142. package/dist/InlineAlert.css +149 -77
  143. package/dist/InlineAlert.css.map +1 -1
  144. package/dist/InlineAlert.mjs +104 -77
  145. package/dist/InlineAlert.mjs.map +1 -1
  146. package/dist/Link.cjs +31 -31
  147. package/dist/Link.css +30 -30
  148. package/dist/Link.mjs +31 -31
  149. package/dist/Menu.cjs +269 -264
  150. package/dist/Menu.cjs.map +1 -1
  151. package/dist/Menu.css +152 -152
  152. package/dist/Menu.css.map +1 -1
  153. package/dist/Menu.mjs +270 -265
  154. package/dist/Menu.mjs.map +1 -1
  155. package/dist/Meter.cjs +85 -85
  156. package/dist/Meter.css +81 -81
  157. package/dist/Meter.mjs +85 -85
  158. package/dist/Modal.cjs +48 -48
  159. package/dist/Modal.css +46 -46
  160. package/dist/Modal.mjs +48 -48
  161. package/dist/NumberField.cjs +115 -115
  162. package/dist/NumberField.css +114 -114
  163. package/dist/NumberField.mjs +115 -115
  164. package/dist/Picker.cjs +193 -175
  165. package/dist/Picker.cjs.map +1 -1
  166. package/dist/Picker.css +223 -163
  167. package/dist/Picker.css.map +1 -1
  168. package/dist/Picker.mjs +193 -175
  169. package/dist/Picker.mjs.map +1 -1
  170. package/dist/Popover.cjs +84 -78
  171. package/dist/Popover.cjs.map +1 -1
  172. package/dist/Popover.css +89 -65
  173. package/dist/Popover.css.map +1 -1
  174. package/dist/Popover.mjs +84 -78
  175. package/dist/Popover.mjs.map +1 -1
  176. package/dist/ProgressBar.cjs +98 -98
  177. package/dist/ProgressBar.css +92 -92
  178. package/dist/ProgressBar.mjs +98 -98
  179. package/dist/ProgressCircle.cjs +17 -17
  180. package/dist/ProgressCircle.css +15 -15
  181. package/dist/ProgressCircle.mjs +17 -17
  182. package/dist/Provider.cjs +4 -4
  183. package/dist/Provider.css +5 -5
  184. package/dist/Provider.mjs +4 -4
  185. package/dist/Radio.cjs +152 -80
  186. package/dist/Radio.cjs.map +1 -1
  187. package/dist/Radio.css +240 -96
  188. package/dist/Radio.css.map +1 -1
  189. package/dist/Radio.mjs +152 -80
  190. package/dist/Radio.mjs.map +1 -1
  191. package/dist/RadioGroup.cjs +47 -47
  192. package/dist/RadioGroup.css +41 -41
  193. package/dist/RadioGroup.mjs +47 -47
  194. package/dist/SearchField.cjs +42 -42
  195. package/dist/SearchField.css +47 -47
  196. package/dist/SearchField.mjs +42 -42
  197. package/dist/SegmentedControl.cjs +101 -89
  198. package/dist/SegmentedControl.cjs.map +1 -1
  199. package/dist/SegmentedControl.css +140 -92
  200. package/dist/SegmentedControl.css.map +1 -1
  201. package/dist/SegmentedControl.mjs +101 -89
  202. package/dist/SegmentedControl.mjs.map +1 -1
  203. package/dist/Slider.cjs +229 -196
  204. package/dist/Slider.cjs.map +1 -1
  205. package/dist/Slider.css +221 -149
  206. package/dist/Slider.css.map +1 -1
  207. package/dist/Slider.mjs +229 -196
  208. package/dist/Slider.mjs.map +1 -1
  209. package/dist/StatusLight.cjs +56 -56
  210. package/dist/StatusLight.css +59 -59
  211. package/dist/StatusLight.mjs +56 -56
  212. package/dist/Switch.cjs +107 -74
  213. package/dist/Switch.cjs.map +1 -1
  214. package/dist/Switch.css +141 -69
  215. package/dist/Switch.css.map +1 -1
  216. package/dist/Switch.mjs +107 -74
  217. package/dist/Switch.mjs.map +1 -1
  218. package/dist/TableView.cjs +280 -253
  219. package/dist/TableView.cjs.map +1 -1
  220. package/dist/TableView.css +199 -163
  221. package/dist/TableView.css.map +1 -1
  222. package/dist/TableView.mjs +280 -253
  223. package/dist/TableView.mjs.map +1 -1
  224. package/dist/Tabs.cjs +83 -83
  225. package/dist/Tabs.css +58 -58
  226. package/dist/Tabs.mjs +83 -83
  227. package/dist/TagGroup.cjs +148 -148
  228. package/dist/TagGroup.css +134 -134
  229. package/dist/TagGroup.mjs +148 -148
  230. package/dist/TextField.cjs +59 -59
  231. package/dist/TextField.css +62 -62
  232. package/dist/TextField.mjs +59 -59
  233. package/dist/ToggleButton.cjs +3 -3
  234. package/dist/ToggleButton.css +12 -12
  235. package/dist/ToggleButton.mjs +3 -3
  236. package/dist/Tooltip.cjs +60 -57
  237. package/dist/Tooltip.cjs.map +1 -1
  238. package/dist/Tooltip.css +83 -71
  239. package/dist/Tooltip.css.map +1 -1
  240. package/dist/Tooltip.mjs +60 -57
  241. package/dist/Tooltip.mjs.map +1 -1
  242. package/dist/TreeView.cjs +465 -0
  243. package/dist/TreeView.cjs.map +1 -0
  244. package/dist/TreeView.css +632 -0
  245. package/dist/TreeView.css.map +1 -0
  246. package/dist/TreeView.mjs +455 -0
  247. package/dist/TreeView.mjs.map +1 -0
  248. package/dist/main.cjs +4 -0
  249. package/dist/main.cjs.map +1 -1
  250. package/dist/module.mjs +3 -1
  251. package/dist/module.mjs.map +1 -1
  252. package/dist/types.d.ts +21 -1
  253. package/dist/types.d.ts.map +1 -1
  254. package/icons/Skeleton.cjs +2 -2
  255. package/icons/Skeleton.css +5 -5
  256. package/icons/Skeleton.mjs +2 -2
  257. package/package.json +20 -19
  258. package/src/Menu.tsx +2 -0
  259. package/src/TreeView.tsx +497 -0
  260. package/src/index.ts +2 -0
  261. package/style/__tests__/style-macro.test.js +18 -18
  262. package/style/dist/spectrum-theme.cjs +20 -10
  263. package/style/dist/spectrum-theme.cjs.map +1 -1
  264. package/style/dist/spectrum-theme.mjs +20 -10
  265. package/style/dist/spectrum-theme.mjs.map +1 -1
  266. package/style/dist/types.d.ts +4 -0
  267. package/style/dist/types.d.ts.map +1 -1
  268. package/style/spectrum-theme.ts +18 -11
@@ -0,0 +1,497 @@
1
+ /*
2
+ * Copyright 2024 Adobe. All rights reserved.
3
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License. You may obtain a copy
5
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ *
7
+ * Unless required by applicable law or agreed to in writing, software distributed under
8
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ * OF ANY KIND, either express or implied. See the License for the specific language
10
+ * governing permissions and limitations under the License.
11
+ */
12
+
13
+ import {ActionButtonGroupContext} from './ActionButtonGroup';
14
+ import {ActionMenuContext} from './ActionMenu';
15
+ import {
16
+ Button,
17
+ ButtonContext,
18
+ Collection,
19
+ Provider,
20
+ TreeItemProps as RACTreeItemProps,
21
+ TreeProps as RACTreeProps,
22
+ UNSTABLE_ListLayout,
23
+ UNSTABLE_Tree,
24
+ UNSTABLE_TreeItem,
25
+ UNSTABLE_TreeItemContent,
26
+ UNSTABLE_Virtualizer,
27
+ useContextProps
28
+ } from 'react-aria-components';
29
+ import {centerBaseline} from './CenterBaseline';
30
+ import {Checkbox} from './Checkbox';
31
+ import Chevron from '../ui-icons/Chevron';
32
+ import {colorMix, fontRelative, lightDark, style} from '../style' with {type: 'macro'};
33
+ import {DOMRef, Key} from '@react-types/shared';
34
+ import {getAllowedOverrides, StylesPropWithHeight, UnsafeStyles} from './style-utils' with {type: 'macro'};
35
+ import {IconContext} from './Icon';
36
+ import {isAndroid} from '@react-aria/utils';
37
+ import {raw} from '../style/style-macro' with {type: 'macro'};
38
+ import React, {createContext, forwardRef, isValidElement, JSXElementConstructor, ReactElement, useContext, useMemo, useRef} from 'react';
39
+ import {Text, TextContext} from './Content';
40
+ import {useDOMRef} from '@react-spectrum/utils';
41
+ import {useLocale} from 'react-aria';
42
+
43
+ interface S2TreeProps {
44
+ // Only detatched is supported right now with the current styles from Spectrum
45
+ isDetached?: boolean,
46
+ onAction?: (key: Key) => void,
47
+ // not fully supported yet
48
+ isEmphasized?: boolean
49
+ }
50
+
51
+ export interface TreeViewProps extends Omit<RACTreeProps<any>, 'style' | 'className' | 'onRowAction' | 'selectionBehavior' | 'onScroll' | 'onCellAction' | 'dragAndDropHooks'>, UnsafeStyles, S2TreeProps {
52
+ /** Spectrum-defined styles, returned by the `style()` macro. */
53
+ styles?: StylesPropWithHeight
54
+ }
55
+
56
+ export interface TreeViewItemProps<T extends object = object> extends Omit<RACTreeItemProps, 'className' | 'style'> {
57
+ /** Whether this item has children, even if not loaded yet. */
58
+ hasChildItems?: boolean,
59
+ /** A list of child tree item objects used when dynamically rendering the tree item children. */
60
+ childItems?: Iterable<T>
61
+ }
62
+
63
+ interface TreeRendererContextValue {
64
+ renderer?: (item) => ReactElement<any, string | JSXElementConstructor<any>>
65
+ }
66
+ const TreeRendererContext = createContext<TreeRendererContextValue>({});
67
+
68
+ function useTreeRendererContext(): TreeRendererContextValue {
69
+ return useContext(TreeRendererContext)!;
70
+ }
71
+
72
+
73
+ let InternalTreeContext = createContext<{isDetached?: boolean, isEmphasized?: boolean}>({});
74
+
75
+ // TODO: the below is needed so the borders of the top and bottom row isn't cut off if the TreeView is wrapped within a container by always reserving the 2px needed for the
76
+ // keyboard focus ring. Perhaps find a different way of rendering the outlines since the top of the item doesn't
77
+ // scroll into view due to how the ring is offset. Alternatively, have the tree render the top/bottom outline like it does in Listview
78
+ const tree = style({
79
+ userSelect: 'none',
80
+ minHeight: 0,
81
+ minWidth: 0,
82
+ width: 'full',
83
+ overflow: 'auto',
84
+ boxSizing: 'border-box',
85
+ justifyContent: {
86
+ isEmpty: 'center'
87
+ },
88
+ alignItems: {
89
+ isEmpty: 'center'
90
+ },
91
+ height: {
92
+ isEmpty: 'full'
93
+ },
94
+ '--indent': {
95
+ type: 'width',
96
+ value: 16
97
+ }
98
+ }, getAllowedOverrides({height: true}));
99
+
100
+ function TreeView(props: TreeViewProps, ref: DOMRef<HTMLDivElement>) {
101
+ let {children, isDetached, isEmphasized} = props;
102
+
103
+ let renderer;
104
+ if (typeof children === 'function') {
105
+ renderer = children;
106
+ }
107
+
108
+ let domRef = useDOMRef(ref);
109
+
110
+ let layout = useMemo(() => {
111
+ return new UNSTABLE_ListLayout({
112
+ rowHeight: isDetached ? 42 : 40
113
+ });
114
+ }, [isDetached]);
115
+
116
+ return (
117
+ <UNSTABLE_Virtualizer layout={layout}>
118
+ <TreeRendererContext.Provider value={{renderer}}>
119
+ <InternalTreeContext.Provider value={{isDetached, isEmphasized}}>
120
+ <UNSTABLE_Tree
121
+ {...props}
122
+ className={({isEmpty}) => tree({isEmpty, isDetached}, props.styles)}
123
+ selectionBehavior="toggle"
124
+ ref={domRef}>
125
+ {props.children}
126
+ </UNSTABLE_Tree>
127
+ </InternalTreeContext.Provider>
128
+ </TreeRendererContext.Provider>
129
+ </UNSTABLE_Virtualizer>
130
+ );
131
+ }
132
+
133
+ const selectedBackground = lightDark(colorMix('gray-25', 'informative-900', 10), colorMix('gray-25', 'informative-700', 10));
134
+ const selectedActiveBackground = lightDark(colorMix('gray-25', 'informative-900', 15), colorMix('gray-25', 'informative-700', 15));
135
+
136
+ const rowBackgroundColor = {
137
+ default: '--s2-container-bg',
138
+ isFocusVisibleWithin: colorMix('gray-25', 'gray-900', 7),
139
+ isHovered: colorMix('gray-25', 'gray-900', 7),
140
+ isPressed: colorMix('gray-25', 'gray-900', 10),
141
+ isSelected: {
142
+ default: colorMix('gray-25', 'gray-900', 7),
143
+ isEmphasized: selectedBackground,
144
+ isFocusVisibleWithin: {
145
+ default: colorMix('gray-25', 'gray-900', 10),
146
+ isEmphasized: selectedActiveBackground
147
+ },
148
+ isHovered: {
149
+ default: colorMix('gray-25', 'gray-900', 10),
150
+ isEmphasized: selectedActiveBackground
151
+ },
152
+ isPressed: {
153
+ default: colorMix('gray-25', 'gray-900', 10),
154
+ isEmphasized: selectedActiveBackground
155
+ }
156
+ },
157
+ forcedColors: {
158
+ default: 'Background'
159
+ }
160
+ } as const;
161
+
162
+ const treeRow = style({
163
+ position: 'relative',
164
+ display: 'flex',
165
+ height: 40,
166
+ width: 'full',
167
+ boxSizing: 'border-box',
168
+ font: 'ui',
169
+ color: 'body',
170
+ outlineStyle: 'none',
171
+ cursor: {
172
+ default: 'default',
173
+ isLink: 'pointer'
174
+ },
175
+ '--rowBackgroundColor': {
176
+ type: 'backgroundColor',
177
+ value: rowBackgroundColor
178
+ },
179
+ '--rowFocusIndicatorColor': {
180
+ type: 'outlineColor',
181
+ value: {
182
+ default: 'focus-ring',
183
+ forcedColors: 'Highlight'
184
+ }
185
+ }
186
+ });
187
+
188
+
189
+ const treeCellGrid = style({
190
+ display: 'grid',
191
+ width: 'full',
192
+ alignContent: 'center',
193
+ alignItems: 'center',
194
+ gridTemplateColumns: ['minmax(0, auto)', 'minmax(0, auto)', 'minmax(0, auto)', 40, 'minmax(0, auto)', '1fr', 'minmax(0, auto)', 'auto'],
195
+ gridTemplateRows: '1fr',
196
+ gridTemplateAreas: [
197
+ 'drag-handle checkbox level-padding expand-button icon content actions actionmenu'
198
+ ],
199
+ backgroundColor: '--rowBackgroundColor',
200
+ paddingEnd: 4, // account for any focus rings on the last item in the cell
201
+ color: {
202
+ isDisabled: {
203
+ default: 'gray-400',
204
+ forcedColors: 'GrayText'
205
+ }
206
+ },
207
+ '--rowSelectedBorderColor': {
208
+ type: 'outlineColor',
209
+ value: {
210
+ default: 'gray-800',
211
+ isFocusVisible: 'focus-ring',
212
+ forcedColors: 'Highlight'
213
+ }
214
+ },
215
+ '--rowForcedFocusBorderColor': {
216
+ type: 'outlineColor',
217
+ value: {
218
+ default: 'focus-ring',
219
+ forcedColors: 'Highlight'
220
+ }
221
+ },
222
+ borderTopColor: {
223
+ default: 'transparent',
224
+ isSelected: {
225
+ isFirst: 'transparent'
226
+ },
227
+ isDetached: {
228
+ default: 'transparent',
229
+ isSelected: '--rowSelectedBorderColor'
230
+ }
231
+ },
232
+ borderInlineEndColor: {
233
+ default: 'transparent',
234
+ isSelected: 'transparent',
235
+ isDetached: {
236
+ default: 'transparent',
237
+ isSelected: '--rowSelectedBorderColor'
238
+ }
239
+ },
240
+ borderBottomColor: {
241
+ default: 'transparent',
242
+ isSelected: 'transparent',
243
+ isNextSelected: 'transparent',
244
+ isNextFocused: 'transparent',
245
+ isDetached: {
246
+ default: 'transparent',
247
+ isSelected: '--rowSelectedBorderColor'
248
+ }
249
+ },
250
+ borderInlineStartColor: {
251
+ default: 'transparent',
252
+ isSelected: 'transparent',
253
+ isDetached: {
254
+ default: 'transparent',
255
+ isSelected: '--rowSelectedBorderColor'
256
+ }
257
+ },
258
+ borderTopWidth: {
259
+ default: 0,
260
+ isFirst: {
261
+ default: 1,
262
+ forcedColors: 0
263
+ },
264
+ isDetached: 1
265
+ },
266
+ borderBottomWidth: {
267
+ default: 0,
268
+ isDetached: 1
269
+ },
270
+ borderStartWidth: {
271
+ default: 0,
272
+ isDetached: 1
273
+ },
274
+ borderEndWidth: {
275
+ default: 0,
276
+ isDetached: 1
277
+ },
278
+ borderRadius: {
279
+ isDetached: 'default'
280
+ },
281
+ borderStyle: 'solid'
282
+ });
283
+
284
+ const treeCheckbox = style({
285
+ gridArea: 'checkbox',
286
+ marginStart: 12,
287
+ marginEnd: 0,
288
+ paddingEnd: 0
289
+ });
290
+
291
+ const treeIcon = style({
292
+ gridArea: 'icon',
293
+ marginEnd: 'text-to-visual',
294
+ '--iconPrimary': {
295
+ type: 'fill',
296
+ value: 'currentColor'
297
+ }
298
+ });
299
+
300
+ const treeContent = style({
301
+ gridArea: 'content',
302
+ textOverflow: 'ellipsis',
303
+ whiteSpace: 'nowrap',
304
+ overflow: 'hidden'
305
+ });
306
+
307
+ const treeActions = style({
308
+ gridArea: 'actions',
309
+ flexGrow: 0,
310
+ flexShrink: 0,
311
+ /* TODO: I made this one up, confirm desired behavior. These paddings are to make sure the action group has enough padding for the focus ring */
312
+ marginStart: 2,
313
+ marginEnd: 4
314
+ });
315
+
316
+ const treeActionMenu = style({
317
+ gridArea: 'actionmenu'
318
+ });
319
+
320
+ const cellFocus = {
321
+ outlineStyle: {
322
+ default: 'none',
323
+ isFocusVisible: 'solid'
324
+ },
325
+ outlineOffset: -2,
326
+ outlineWidth: 2,
327
+ outlineColor: 'focus-ring',
328
+ borderRadius: '[6px]'
329
+ } as const;
330
+
331
+ const treeRowFocusIndicator = raw(`
332
+ &:before {
333
+ content: "";
334
+ display: inline-block;
335
+ position: sticky;
336
+ inset-inline-start: 0;
337
+ width: 3px;
338
+ height: 100%;
339
+ margin-inline-end: -3px;
340
+ margin-block-end: 1px;
341
+ z-index: 3;
342
+ background-color: var(--rowFocusIndicatorColor);
343
+ }`
344
+ );
345
+
346
+
347
+ export const TreeViewItem = <T extends object>(props: TreeViewItemProps<T>) => {
348
+ let {
349
+ children,
350
+ childItems,
351
+ hasChildItems,
352
+ href
353
+ } = props;
354
+
355
+ let content;
356
+ let nestedRows;
357
+ let {renderer} = useTreeRendererContext();
358
+ let {isDetached, isEmphasized} = useContext(InternalTreeContext);
359
+
360
+ if (typeof children === 'string') {
361
+ content = <Text>{children}</Text>;
362
+ } else {
363
+ content = [];
364
+ nestedRows = [];
365
+ React.Children.forEach(children, node => {
366
+ if (isValidElement(node) && node.type === TreeViewItem) {
367
+ nestedRows.push(node);
368
+ } else {
369
+ content.push(node);
370
+ }
371
+ });
372
+ }
373
+
374
+ if (childItems != null && renderer) {
375
+ nestedRows = (
376
+ <Collection items={childItems}>
377
+ {renderer}
378
+ </Collection>
379
+ );
380
+ }
381
+
382
+ return (
383
+ <UNSTABLE_TreeItem
384
+ {...props}
385
+ className={(renderProps) => treeRow({...renderProps, isLink: !!href, isEmphasized}) + (renderProps.isFocusVisible && !isDetached ? ' ' + treeRowFocusIndicator : '')}>
386
+ <UNSTABLE_TreeItemContent>
387
+ {({isExpanded, hasChildRows, selectionMode, selectionBehavior, isDisabled, isFocusVisible, isSelected, id, state}) => {
388
+ let isNextSelected = false;
389
+ let isNextFocused = false;
390
+ let keyAfter = state.collection.getKeyAfter(id);
391
+ if (keyAfter != null) {
392
+ isNextSelected = state.selectionManager.isSelected(keyAfter);
393
+ }
394
+ let isFirst = state.collection.getFirstKey() === id;
395
+ return (
396
+ <div className={treeCellGrid({isDisabled, isNextSelected, isSelected, isFirst, isNextFocused, isDetached})}>
397
+ {selectionMode !== 'none' && selectionBehavior === 'toggle' && (
398
+ // TODO: add transition?
399
+ <div className={treeCheckbox}>
400
+ <Checkbox
401
+ isEmphasized={isEmphasized}
402
+ slot="selection" />
403
+ </div>
404
+ )}
405
+ <div
406
+ className={style({
407
+ gridArea: 'level-padding',
408
+ width: '[calc(calc(var(--tree-item-level, 0) - 1) * var(--indent))]'
409
+ })} />
410
+ {/* TODO: revisit when we do async loading, at the moment hasChildItems will only cause the chevron to be rendered, no aria/data attributes indicating the row's expandability are added */}
411
+ {(hasChildRows || hasChildItems) && <ExpandableRowChevron isDisabled={isDisabled} isExpanded={isExpanded} />}
412
+ <Provider
413
+ values={[
414
+ [TextContext, {styles: treeContent}],
415
+ [IconContext, {
416
+ render: centerBaseline({slot: 'icon', styles: treeIcon}),
417
+ styles: style({size: fontRelative(20), flexShrink: 0})
418
+ }],
419
+ [ActionButtonGroupContext, {styles: treeActions}],
420
+ [ActionMenuContext, {styles: treeActionMenu, isQuiet: true}]
421
+ ]}>
422
+ {content}
423
+ </Provider>
424
+ {isFocusVisible && isDetached && <div role="presentation" className={style({...cellFocus, position: 'absolute', inset: 0})({isFocusVisible: true})} />}
425
+ </div>
426
+ );
427
+ }}
428
+ </UNSTABLE_TreeItemContent>
429
+ {nestedRows}
430
+ </UNSTABLE_TreeItem>
431
+ );
432
+ };
433
+
434
+ interface ExpandableRowChevronProps {
435
+ isExpanded?: boolean,
436
+ isDisabled?: boolean,
437
+ isRTL?: boolean
438
+ }
439
+
440
+ const expandButton = style<ExpandableRowChevronProps>({
441
+ gridArea: 'expand-button',
442
+ height: 'full',
443
+ aspectRatio: 'square',
444
+ display: 'flex',
445
+ flexWrap: 'wrap',
446
+ alignContent: 'center',
447
+ justifyContent: 'center',
448
+ outlineStyle: 'none',
449
+ cursor: 'default',
450
+ transform: {
451
+ isExpanded: {
452
+ default: 'rotate(90deg)',
453
+ isRTL: 'rotate(-90deg)'
454
+ }
455
+ },
456
+ transition: 'default',
457
+ backgroundColor: 'transparent',
458
+ borderStyle: 'none'
459
+ });
460
+
461
+ function ExpandableRowChevron(props: ExpandableRowChevronProps) {
462
+ let expandButtonRef = useRef<HTMLButtonElement>(null);
463
+ let [fullProps, ref] = useContextProps({...props, slot: 'chevron'}, expandButtonRef, ButtonContext);
464
+ let {isExpanded, isDisabled} = fullProps;
465
+ let {direction} = useLocale();
466
+
467
+ return (
468
+ <Button
469
+ {...props}
470
+ ref={ref}
471
+ slot="chevron"
472
+ // Override tabindex so that grid keyboard nav skips over it. Needs -1 so android talkback can actually "focus" it
473
+ excludeFromTabOrder={isAndroid() && !isDisabled}
474
+ preventFocusOnPress
475
+ className={renderProps => expandButton({...renderProps, isExpanded, isRTL: direction === 'rtl'})}>
476
+ <Chevron
477
+ className={style({
478
+ scale: {
479
+ direction: {
480
+ ltr: '1',
481
+ rtl: '-1'
482
+ }
483
+ },
484
+ '--iconPrimary': {
485
+ type: 'fill',
486
+ value: 'currentColor'
487
+ }
488
+ })({direction})} />
489
+ </Button>
490
+ );
491
+ }
492
+
493
+ /**
494
+ * A tree view provides users with a way to navigate nested hierarchical information.
495
+ */
496
+ const _TreeView = forwardRef(TreeView);
497
+ export {_TreeView as TreeView};
package/src/index.ts CHANGED
@@ -76,6 +76,7 @@ export {TextArea, TextField, TextAreaContext, TextFieldContext} from './TextFiel
76
76
  export {ToggleButton, ToggleButtonContext} from './ToggleButton';
77
77
  export {ToggleButtonGroup, ToggleButtonGroupContext} from './ToggleButtonGroup';
78
78
  export {Tooltip, TooltipTrigger} from './Tooltip';
79
+ export {TreeView, TreeViewItem} from './TreeView';
79
80
 
80
81
  export {pressScale} from './pressScale';
81
82
 
@@ -144,4 +145,5 @@ export type {TextFieldProps, TextAreaProps} from './TextField';
144
145
  export type {ToggleButtonProps} from './ToggleButton';
145
146
  export type {ToggleButtonGroupProps} from './ToggleButtonGroup';
146
147
  export type {TooltipProps} from './Tooltip';
148
+ export type {TreeViewProps, TreeViewItemProps} from './TreeView';
147
149
  export type {FileTriggerProps, TooltipTriggerComponentProps as TooltipTriggerProps} from 'react-aria-components';
@@ -40,7 +40,7 @@ describe('style-macro', () => {
40
40
  "@layer _.a, _.b, _.c;
41
41
 
42
42
  @layer _.b {
43
- .A-13alit4c {
43
+ .D-13alit4c {
44
44
  &:first-child {
45
45
  margin-top: 0.25rem;
46
46
  }
@@ -49,7 +49,7 @@ describe('style-macro', () => {
49
49
 
50
50
  @layer _.c.e {
51
51
  @media (min-width: 1024px) {
52
- .A-13alit4ed {
52
+ .D-13alit4ed {
53
53
  &:first-child {
54
54
  margin-top: 0.5rem;
55
55
  }
@@ -59,7 +59,7 @@ describe('style-macro', () => {
59
59
 
60
60
  "
61
61
  `);
62
- expect(js).toMatchInlineSnapshot('" A-13alit4c A-13alit4ed"');
62
+ expect(js).toMatchInlineSnapshot('" D-13alit4c D-13alit4ed"');
63
63
  });
64
64
 
65
65
  it('should support self references', () => {
@@ -73,48 +73,48 @@ describe('style-macro', () => {
73
73
  "@layer _.a, _.b;
74
74
 
75
75
  @layer _.a {
76
- .uc {
76
+ .tc {
77
77
  border-top-width: 2px;
78
78
  }
79
79
 
80
80
 
81
- .vc {
81
+ .uc {
82
82
  border-bottom-width: 2px;
83
83
  }
84
84
 
85
85
 
86
- .s-375toy {
87
- border-inline-start-width: var(--s);
86
+ .r-375tox {
87
+ border-inline-start-width: var(--r);
88
88
  }
89
89
 
90
90
 
91
- .tc {
91
+ .sc {
92
92
  border-inline-end-width: 2px;
93
93
  }
94
94
 
95
95
 
96
- .C-375tnm {
97
- padding-inline-start: var(--C);
96
+ .F-375tnp {
97
+ padding-inline-start: var(--F);
98
98
  }
99
99
 
100
100
 
101
- .DI {
102
- padding-inline-end: calc(var(--k, var(--o)) * 3 / 8);
101
+ .GI {
102
+ padding-inline-end: calc(var(--j, var(--n)) * 3 / 8);
103
103
  }
104
104
 
105
105
 
106
- .l-4s570k {
107
- width: calc(200px - var(--s) - var(--C));
106
+ .k-4s570k {
107
+ width: calc(200px - var(--r) - var(--F));
108
108
  }
109
109
 
110
110
 
111
- .-_375toy_s-c {
112
- --s: 2px;
111
+ .-_375tox_r-c {
112
+ --r: 2px;
113
113
  }
114
114
 
115
115
 
116
- .-_375tnm_C-I {
117
- --C: calc(var(--k, var(--o)) * 3 / 8);
116
+ .-_375tnp_F-I {
117
+ --F: calc(var(--j, var(--n)) * 3 / 8);
118
118
  }
119
119
  }
120
120
 
@@ -669,6 +669,16 @@ const $7dddb03c6ef7d79c$var$borderWidth = {
669
669
  2: "2px",
670
670
  4: "4px"
671
671
  };
672
+ const $7dddb03c6ef7d79c$var$borderColor = (0, $b3643cb9d2948e30$exports.createColorProperty)({
673
+ ...$7dddb03c6ef7d79c$var$color,
674
+ negative: {
675
+ default: "light-dark(rgb(215, 50, 32), rgb(252, 67, 46))",
676
+ isHovered: "light-dark(rgb(183, 40, 24), rgb(255, 103, 86))",
677
+ isFocusVisible: "light-dark(rgb(183, 40, 24), rgb(255, 103, 86))",
678
+ isPressed: "light-dark(rgb(156, 33, 19), rgb(255, 134, 120))"
679
+ },
680
+ disabled: "light-dark(rgb(218, 218, 218), rgb(57, 57, 57))"
681
+ });
672
682
  const $7dddb03c6ef7d79c$var$radius = {
673
683
  none: "0px",
674
684
  sm: $7dddb03c6ef7d79c$var$pxToRem("4px"),
@@ -1043,16 +1053,6 @@ const $7dddb03c6ef7d79c$export$1d567c320f4763bc = (0, $b3643cb9d2948e30$exports.
1043
1053
  pasteboard: "light-dark(rgb(233, 233, 233), rgb(17, 17, 17))",
1044
1054
  elevated: "light-dark(rgb(255, 255, 255), rgb(34, 34, 34))"
1045
1055
  }),
1046
- borderColor: (0, $b3643cb9d2948e30$exports.createColorProperty)({
1047
- ...$7dddb03c6ef7d79c$var$color,
1048
- negative: {
1049
- default: "light-dark(rgb(215, 50, 32), rgb(252, 67, 46))",
1050
- isHovered: "light-dark(rgb(183, 40, 24), rgb(255, 103, 86))",
1051
- isFocusVisible: "light-dark(rgb(183, 40, 24), rgb(255, 103, 86))",
1052
- isPressed: "light-dark(rgb(156, 33, 19), rgb(255, 134, 120))"
1053
- },
1054
- disabled: "light-dark(rgb(218, 218, 218), rgb(57, 57, 57))"
1055
- }),
1056
1056
  outlineColor: (0, $b3643cb9d2948e30$exports.createColorProperty)({
1057
1057
  ...$7dddb03c6ef7d79c$var$color,
1058
1058
  'focus-ring': {
@@ -1125,6 +1125,10 @@ const $7dddb03c6ef7d79c$export$1d567c320f4763bc = (0, $b3643cb9d2948e30$exports.
1125
1125
  borderEndWidth: (0, $b3643cb9d2948e30$exports.createRenamedProperty)('borderInlineEndWidth', $7dddb03c6ef7d79c$var$borderWidth),
1126
1126
  borderTopWidth: $7dddb03c6ef7d79c$var$borderWidth,
1127
1127
  borderBottomWidth: $7dddb03c6ef7d79c$var$borderWidth,
1128
+ borderInlineStartColor: $7dddb03c6ef7d79c$var$borderColor,
1129
+ borderInlineEndColor: $7dddb03c6ef7d79c$var$borderColor,
1130
+ borderTopColor: $7dddb03c6ef7d79c$var$borderColor,
1131
+ borderBottomColor: $7dddb03c6ef7d79c$var$borderColor,
1128
1132
  borderStyle: [
1129
1133
  'solid',
1130
1134
  'dashed',
@@ -1919,6 +1923,12 @@ const $7dddb03c6ef7d79c$export$1d567c320f4763bc = (0, $b3643cb9d2948e30$exports.
1919
1923
  'borderStartWidth',
1920
1924
  'borderEndWidth'
1921
1925
  ],
1926
+ borderColor: [
1927
+ 'borderTopColor',
1928
+ 'borderBottomColor',
1929
+ 'borderInlineStartColor',
1930
+ 'borderInlineEndColor'
1931
+ ],
1922
1932
  borderXWidth: [
1923
1933
  'borderStartWidth',
1924
1934
  'borderEndWidth'