@@ -4,12 +4,14 @@ import { palette } from '@leafygreen-ui/palette';
44import Icon from '@leafygreen-ui/icon' ;
55import { useDarkMode } from '@leafygreen-ui/leafygreen-provider' ;
66import { useTheme } from '@emotion/react' ;
7+ import { MouseEvent as ReactMouseEvent , useMemo } from 'react' ;
78
89import { animatedBlueBorder , ellipsisTruncation } from '@/styles/styles' ;
910import { DEFAULT_DEPTH_SPACING , DEFAULT_FIELD_HEIGHT } from '@/utilities/constants' ;
1011import { FieldDepth } from '@/components/field/field-depth' ;
1112import { NodeField , NodeGlyph , NodeType } from '@/types' ;
1213import { PreviewGroupArea } from '@/utilities/get-preview-group-area' ;
14+ import { useFieldSelection } from '@/hooks/use-field-selection' ;
1315
1416const FIELD_BORDER_ANIMATED_PADDING = spacing [ 100 ] ;
1517const FIELD_GLYPH_SPACING = spacing [ 400 ] ;
@@ -19,12 +21,44 @@ const GlyphToIcon: Record<NodeGlyph, string> = {
1921 link : 'Link' ,
2022} ;
2123
22- const FieldWrapper = styled . div < { color : string } > `
24+ const SELECTED_FIELD_BORDER_PADDING = spacing [ 100 ] ;
25+
26+ const FieldWrapper = styled . div < {
27+ color : string ;
28+ selectableHoverBackgroundColor ?: string ;
29+ selectable ?: boolean ;
30+ selected ?: boolean ;
31+ selectedGroupHeight : number ;
32+ } > `
2333 display: flex;
2434 align-items: center;
2535 width: auto;
2636 height: ${ DEFAULT_FIELD_HEIGHT } px;
2737 color: ${ props => props . color } ;
38+ ${ props =>
39+ props . selectable &&
40+ `&:hover {
41+ cursor: pointer;
42+ background-color: ${ props . selectableHoverBackgroundColor } ;
43+ box-shadow: -${ spacing [ 100 ] } px 0px 0px 0px ${ props . selectableHoverBackgroundColor } , ${ spacing [ 100 ] } px 0px 0px 0px ${ props . selectableHoverBackgroundColor } ;
44+ }` }
45+ ${ props =>
46+ props . selected &&
47+ `
48+ position: relative;
49+
50+ &::before {
51+ content: '';
52+ pointer-events: none;
53+ position: absolute;
54+ outline: 2px solid ${ palette . blue . base } ;
55+ width: calc(100% + ${ SELECTED_FIELD_BORDER_PADDING * 2 } px);
56+ border-radius: ${ spacing [ 50 ] } px;
57+ height: ${ props . selectedGroupHeight * DEFAULT_FIELD_HEIGHT } px;
58+ left: -${ SELECTED_FIELD_BORDER_PADDING } px;
59+ top: 0px;
60+ }
61+ ` }
2862` ;
2963
3064const InnerFieldWrapper = styled . div < { width : number } > `
@@ -90,31 +124,56 @@ const IconWrapper = styled(Icon)`
90124` ;
91125
92126interface Props extends NodeField {
127+ nodeId : string ;
93128 nodeType : NodeType ;
94129 spacing : number ;
95130 isHovering ?: boolean ;
96131 previewGroupArea : PreviewGroupArea ;
132+ selectedGroupHeight ?: number ;
97133}
98134
99135export const Field = ( {
100136 hoverVariant,
101137 isHovering = false ,
102138 name,
139+ nodeId,
140+ id = name ,
103141 depth = 0 ,
104142 type,
105143 nodeType,
106144 glyphs = [ ] ,
145+ selectedGroupHeight = 0 ,
107146 previewGroupArea,
108147 glyphSize = LGSpacing [ 300 ] ,
109148 spacing = 0 ,
149+ selectable = false ,
150+ selected = false ,
110151 variant,
111152} : Props ) => {
112153 const { theme } = useDarkMode ( ) ;
113154
155+ const { fieldProps } = useFieldSelection ( ) ;
156+
114157 const internalTheme = useTheme ( ) ;
115158
116159 const isDisabled = variant === 'disabled' && ! ( hoverVariant === 'default' && isHovering ) ;
117160
161+ const getSelectableHoverBackgroundColor = ( ) => {
162+ return fieldSelectionProps ?. selectable ? color [ theme ] . background . primary . hover : undefined ;
163+ } ;
164+
165+ /**
166+ * Create the field selection props when the field is selectable.
167+ */
168+ const fieldSelectionProps = useMemo ( ( ) => {
169+ return selectable && fieldProps
170+ ? {
171+ selectable : true ,
172+ onClick : ( event : ReactMouseEvent ) => fieldProps . onClick ( event , { id, nodeId } ) ,
173+ }
174+ : undefined ;
175+ } , [ fieldProps , selectable , id , nodeId ] ) ;
176+
118177 const getTextColor = ( ) => {
119178 if ( isDisabled ) {
120179 return internalTheme . node . disabledColor ;
@@ -188,7 +247,13 @@ export const Field = ({
188247 const previewBorderLeft = `${ depth * DEFAULT_DEPTH_SPACING - previewWidthGlyphOffset } px` ;
189248
190249 return (
191- < FieldWrapper color = { getTextColor ( ) } >
250+ < FieldWrapper
251+ selected = { selected }
252+ color = { getTextColor ( ) }
253+ selectableHoverBackgroundColor = { getSelectableHoverBackgroundColor ( ) }
254+ selectedGroupHeight = { selectedGroupHeight }
255+ { ...fieldSelectionProps }
256+ >
192257 < InnerFieldWrapper width = { spacing } >
193258 { glyphs . map ( glyph => (
194259 < IconWrapper key = { glyph } color = { getIconColor ( glyph ) } glyph = { GlyphToIcon [ glyph ] } size = { glyphSize } />
0 commit comments