@@ -26,13 +26,14 @@ import {
26
26
import { Checkbox , IconContext , Text , TextContext } from '@react-spectrum/s2' ;
27
27
import Chevron from '../ui-icons/Chevron' ;
28
28
import { DOMRef , Key } from '@react-types/shared' ;
29
- import { focusRing , style } from '../style' with { type : 'macro' } ;
29
+ import { colorMix , focusRing , lightDark , style } from '../style' with { type : 'macro' } ;
30
30
import { isAndroid } from '@react-aria/utils' ;
31
31
import React , { createContext , forwardRef , isValidElement , JSXElementConstructor , ReactElement , useContext , useRef } from 'react' ;
32
32
import { StylesPropWithHeight , UnsafeStyles } from './style-utils' ;
33
33
import { useButton } from '@react-aria/button' ;
34
34
import { useDOMRef } from '@react-spectrum/utils' ;
35
35
import { useLocale } from '@react-aria/i18n' ;
36
+ import { raw } from '../style/style-macro' ;
36
37
37
38
interface S2TreeProps {
38
39
isDetached ?: boolean ,
@@ -62,15 +63,15 @@ function useTreeRendererContext(): TreeRendererContextValue {
62
63
}
63
64
64
65
65
- let InternalTreeContext = createContext ( { } ) ;
66
+ let InternalTreeContext = createContext < { isDetached ?: boolean } > ( { } ) ;
66
67
67
68
68
69
// TODO: add animations for rows appearing and disappearing
69
70
70
71
// 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
71
72
// keyboard focus ring. Perhaps find a different way of rendering the outlines since the top of the item doesn't
72
73
// scroll into view due to how the ring is offset. Alternatively, have the tree render the top/bottom outline like it does in Listview
73
- const tree = style < Pick < TreeRenderProps , 'isEmpty' > > ( {
74
+ const tree = style ( {
74
75
borderWidth : 2 ,
75
76
boxSizing : 'border-box' ,
76
77
borderXWidth : 0 ,
@@ -91,8 +92,14 @@ const tree = style<Pick<TreeRenderProps, 'isEmpty'>>({
91
92
height : {
92
93
isEmpty : 'full'
93
94
} ,
94
- display : {
95
- isEmpty : 'flex'
95
+ display : 'flex' ,
96
+ flexDirection : 'column' ,
97
+ gap : {
98
+ isDetached : 2
99
+ } ,
100
+ '--indent' : {
101
+ type : 'width' ,
102
+ value : 16
96
103
}
97
104
} ) ;
98
105
@@ -109,7 +116,11 @@ function TreeView(props: TreeViewProps, ref: DOMRef<HTMLDivElement>) {
109
116
return (
110
117
< TreeRendererContext . Provider value = { { renderer} } >
111
118
< InternalTreeContext . Provider value = { { isDetached} } >
112
- < UNSTABLE_Tree { ...props } className = { ( { isEmpty} ) => tree ( { isEmpty} ) } selectionBehavior = "toggle" ref = { domRef } >
119
+ < UNSTABLE_Tree
120
+ { ...props }
121
+ className = { ( { isEmpty} ) => tree ( { isEmpty, isDetached} ) }
122
+ selectionBehavior = "toggle"
123
+ ref = { domRef } >
113
124
{ props . children }
114
125
</ UNSTABLE_Tree >
115
126
</ InternalTreeContext . Provider >
@@ -121,6 +132,28 @@ interface TreeRowRenderProps extends TreeItemRenderProps {
121
132
isLink ?: boolean
122
133
}
123
134
135
+ const selectedBackground = lightDark ( colorMix ( 'gray-25' , 'informative-900' , 10 ) , colorMix ( 'gray-25' , 'informative-700' , 10 ) ) ;
136
+ const selectedActiveBackground = lightDark ( colorMix ( 'gray-25' , 'informative-900' , 15 ) , colorMix ( 'gray-25' , 'informative-700' , 15 ) ) ;
137
+
138
+ const rowBackgroundColor = {
139
+ default : {
140
+ default : 'gray-25' ,
141
+ isQuiet : '--s2-container-bg'
142
+ } ,
143
+ isFocusVisibleWithin : colorMix ( 'gray-25' , 'gray-900' , 7 ) , // table-row-hover-color
144
+ isHovered : colorMix ( 'gray-25' , 'gray-900' , 7 ) , // table-row-hover-color
145
+ isPressed : colorMix ( 'gray-25' , 'gray-900' , 10 ) , // table-row-hover-color
146
+ isSelected : {
147
+ default : selectedBackground , // table-selected-row-background-color, opacity /10
148
+ isFocusVisibleWithin : selectedActiveBackground , // table-selected-row-background-color, opacity /15
149
+ isHovered : selectedActiveBackground , // table-selected-row-background-color, opacity /15
150
+ isPressed : selectedActiveBackground // table-selected-row-background-color, opacity /15
151
+ } ,
152
+ forcedColors : {
153
+ default : 'Background'
154
+ }
155
+ } as const ;
156
+
124
157
const treeRow = style < TreeRowRenderProps > ( {
125
158
position : 'relative' ,
126
159
display : 'flex' ,
@@ -135,17 +168,23 @@ const treeRow = style<TreeRowRenderProps>({
135
168
default : 'default' ,
136
169
isLink : 'pointer'
137
170
} ,
138
- // TODO: not sure where to get the equivalent colors here, for instance isHovered is spectrum 600 with 10% opacity but I don't think that exists in the theme
139
- backgroundColor : {
140
- isHovered : '[var(--spectrum-table-row-background-color-hover)]' ,
141
- isFocusVisibleWithin : '[var(--spectrum-table-row-background-color-hover)]' ,
142
- isPressed : '[var(--spectrum-table-row-background-color-down)]' ,
143
- isSelected : '[var(--spectrum-table-row-background-color-selected)]'
171
+ backgroundColor : '--rowBackgroundColor' ,
172
+ '--rowBackgroundColor' : {
173
+ type : 'backgroundColor' ,
174
+ value : rowBackgroundColor
144
175
} ,
145
- '--indent' : {
146
- type : 'width' ,
147
- value : 16
148
- }
176
+ '--rowFocusIndicatorColor' : {
177
+ type : 'outlineColor' ,
178
+ value : {
179
+ default : 'focus-ring' ,
180
+ forcedColors : 'Highlight'
181
+ }
182
+ } ,
183
+ borderTopWidth : 0 ,
184
+ borderBottomWidth : 0 ,
185
+ borderStartWidth : 0 ,
186
+ borderEndWidth : 0 ,
187
+ borderStyle : 'none'
149
188
} ) ;
150
189
151
190
const treeCellGrid = style ( {
@@ -184,26 +223,6 @@ const treeContent = style({
184
223
overflow : 'hidden'
185
224
} ) ;
186
225
187
- const treeRowOutline = style ( {
188
- ...focusRing ( ) ,
189
- content : '' ,
190
- display : 'block' ,
191
- position : 'absolute' ,
192
- insetStart : '[8px]' ,
193
- insetEnd : '[8px]' ,
194
- top : {
195
- default : '[8px]' ,
196
- isFocusVisible : '[8px]' ,
197
- isSelected : {
198
- default : '[1px]' ,
199
- isFocusVisible : '[8px]'
200
- }
201
- } ,
202
- bottom : '[8px]' ,
203
- pointerEvents : 'none' ,
204
- forcedColorAdjust : 'none'
205
- } ) ;
206
-
207
226
export const TreeViewItem = < T extends object > ( props : TreeViewItemProps < T > ) => {
208
227
let {
209
228
children,
@@ -215,6 +234,7 @@ export const TreeViewItem = <T extends object>(props: TreeViewItemProps<T>) => {
215
234
let content ;
216
235
let nestedRows ;
217
236
let { renderer} = useTreeRendererContext ( ) ;
237
+ let { isDetached} = useContext ( InternalTreeContext ) ;
218
238
// TODO alternative api is that we have a separate prop for the TreeItems contents and expect the child to then be
219
239
// a nested tree item
220
240
@@ -247,7 +267,7 @@ export const TreeViewItem = <T extends object>(props: TreeViewItemProps<T>) => {
247
267
className = { renderProps => treeRow ( {
248
268
...renderProps ,
249
269
isLink : ! ! href
250
- } ) } >
270
+ } ) + ( renderProps . isFocusVisible && ' ' + raw ( '&:before { content: ""; display: inline-block; position: sticky; inset-inline-start: 0; width: 3px; height: 100%; margin-inline-end: -3px; margin-block-end: 1px; z-index: 3; background-color: var(--rowFocusIndicatorColor)' ) ) } >
251
271
< UNSTABLE_TreeItemContent >
252
272
{ ( { isExpanded, hasChildRows, selectionMode, selectionBehavior, isDisabled, isSelected, isFocusVisible} ) => (
253
273
< div className = { treeCellGrid ( { isDisabled} ) } >
@@ -273,7 +293,6 @@ export const TreeViewItem = <T extends object>(props: TreeViewItemProps<T>) => {
273
293
] } >
274
294
{ content }
275
295
</ Provider >
276
- < div className = { treeRowOutline ( { isFocusVisible, isSelected} ) } />
277
296
</ div >
278
297
) }
279
298
</ UNSTABLE_TreeItemContent >
0 commit comments