Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
F
frontend
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
vicotor
frontend
Commits
388ec42a
Commit
388ec42a
authored
Jan 10, 2025
by
tom
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
use tooltips for navigation groups
parent
989cb7c4
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
146 additions
and
138 deletions
+146
-138
tooltip.tsx
toolkit/chakra/tooltip.tsx
+11
-4
tooltip.recipe.ts
toolkit/theme/recipes/tooltip.recipe.ts
+10
-0
NavLinkGroup.tsx
ui/snippets/navigation/horizontal/NavLinkGroup.tsx
+57
-66
NavLinkGroup.tsx
ui/snippets/navigation/vertical/NavLinkGroup.tsx
+68
-68
No files found.
toolkit/chakra/tooltip.tsx
View file @
388ec42a
...
...
@@ -17,7 +17,9 @@ export interface TooltipProps extends ChakraTooltip.RootProps {
export
const
Tooltip
=
React
.
forwardRef
<
HTMLDivElement
,
TooltipProps
>
(
function
Tooltip
(
props
,
ref
)
{
const
{
showArrow
=
true
,
showArrow
:
showArrowProp
,
onOpenChange
,
visual
,
selected
,
children
,
disabled
,
...
...
@@ -33,9 +35,10 @@ export const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>(
const
isMobile
=
useIsMobile
();
const
triggerRef
=
useClickAway
<
HTMLButtonElement
>
(()
=>
setOpen
(
false
));
const
handleOpenChange
=
React
.
useCallback
(({
open
}:
{
open
:
boolean
})
=>
{
setOpen
(
open
);
},
[]);
const
handleOpenChange
=
React
.
useCallback
((
details
:
{
open
:
boolean
})
=>
{
setOpen
(
details
.
open
);
onOpenChange
?.(
details
);
},
[
onOpenChange
]);
const
handleTriggerClick
=
React
.
useCallback
(()
=>
{
setOpen
((
prev
)
=>
!
prev
);
...
...
@@ -43,6 +46,9 @@ export const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>(
if
(
disabled
)
return
children
;
const
defaultShowArrow
=
visual
===
'
popover
'
?
false
:
true
;
const
showArrow
=
showArrowProp
!==
undefined
?
showArrowProp
:
defaultShowArrow
;
const
positioning
=
{
...
rest
.
positioning
,
offset
:
{
...
...
@@ -58,6 +64,7 @@ export const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>(
open=
{
open
}
onOpenChange=
{
isMobile
?
undefined
:
handleOpenChange
}
closeOnClick=
{
false
}
visual=
{
visual
}
{
...
rest
}
positioning=
{
positioning
}
>
...
...
toolkit/theme/recipes/tooltip.recipe.ts
View file @
388ec42a
...
...
@@ -63,6 +63,16 @@ export const recipe = defineSlotRecipe({
display
:
'
none
'
,
},
},
popover
:
{
content
:
{
bg
:
'
popover.bg
'
,
color
:
'
popover.fg
'
,
p
:
'
4
'
,
boxShadow
:
'
popover
'
,
boxShadowColor
:
'
popover.shadow
'
,
borderRadius
:
'
md
'
,
},
},
},
},
defaultVariants
:
{
...
...
ui/snippets/navigation/horizontal/NavLinkGroup.tsx
View file @
388ec42a
...
...
@@ -3,7 +3,8 @@ import React from 'react';
import
type
{
NavGroupItem
}
from
'
types/client/navigation
'
;
import
{
PopoverRoot
,
PopoverBody
,
PopoverContent
,
PopoverTrigger
}
from
'
toolkit/chakra/popover
'
;
import
{
Tooltip
}
from
'
toolkit/chakra/tooltip
'
;
import
{
useDisclosure
}
from
'
toolkit/hooks/useDisclosure
'
;
import
IconSvg
from
'
ui/shared/IconSvg
'
;
import
LightningLabel
from
'
../LightningLabel
'
;
...
...
@@ -14,83 +15,73 @@ interface Props {
}
const
NavLinkGroup
=
({
item
}:
Props
)
=>
{
const
[
isOpen
,
setIsOpen
]
=
React
.
useState
(
false
);
// const bgColor = item.isActive ? colors.bg.active : colors.bg.default;
// const color = item.isActive ? colors.text.active : colors.text.default;
const
{
open
,
onOpenChange
}
=
useDisclosure
();
const
isHighlighted
=
checkRouteHighlight
(
item
.
subItems
);
const
hasGroups
=
item
.
subItems
.
some
((
subItem
)
=>
Array
.
isArray
(
subItem
));
const
handleOpenChange
=
React
.
useCallback
(({
open
}:
{
open
:
boolean
})
=>
{
setIsOpen
(
open
);
},
[]);
const
content
=
hasGroups
?
(
<
HStack
separator=
{
<
Separator
/>
}
alignItems=
"flex-start"
>
{
item
.
subItems
.
map
((
subItem
,
index
)
=>
{
if
(
!
Array
.
isArray
(
subItem
))
{
return
<
NavLink
key=
{
subItem
.
text
}
item=
{
subItem
}
/>;
}
return
(
<
chakra
.
ul
key=
{
index
}
display=
"flex"
flexDir=
"column"
rowGap=
{
1
}
>
{
subItem
.
map
((
navItem
)
=>
<
NavLink
key=
{
navItem
.
text
}
item=
{
navItem
}
/>)
}
</
chakra
.
ul
>
);
})
}
</
HStack
>
)
:
(
<
chakra
.
ul
display=
"flex"
flexDir=
"column"
rowGap=
{
1
}
>
{
item
.
subItems
.
map
((
subItem
)
=>
{
if
(
Array
.
isArray
(
subItem
))
{
return
null
;
}
return
<
NavLink
key=
{
subItem
.
text
}
item=
{
subItem
}
/>;
})
}
</
chakra
.
ul
>
);
return
(
<
PopoverRoot
// TODO @tom2drum make menu open on hover
// trigger="hover"
onOpenChange=
{
handle
OpenChange
}
<
Tooltip
visual=
"popover"
content=
{
content
}
onOpenChange=
{
on
OpenChange
}
lazyMount
positioning=
{
{
placement
:
'
bottom
'
,
offset
:
{
mainAxis
:
8
},
}
}
interactive
>
<
PopoverTrigger
>
<
Link
as=
"li"
listStyleType=
"none"
display=
"flex"
alignItems=
"center"
px=
{
2
}
py=
{
1.5
}
textStyle=
"sm"
fontWeight=
{
500
}
visual=
"navigation"
{
...
(
item
.
isActive
?
{
'
data
-
selected
':
true
}
:
{})
}
{
...
(
isOpen
?
{
'
data
-
active
':
true
}
:
{})
}
borderRadius=
"base"
>
{
item
.
text
}
{
isHighlighted
&&
(
<
LightningLabel
iconColor=
{
item
.
isActive
?
'
link.navigation.bg.selected
'
:
'
link.navigation.bg
'
}
position=
{
{
lg
:
'
static
'
}
}
ml=
{
{
lg
:
'
2px
'
}
}
/>
)
}
<
IconSvg
name=
"arrows/east-mini"
boxSize=
{
5
}
transform=
"rotate(-90deg)"
ml=
{
1
}
/>
</
Link
>
</
PopoverTrigger
>
<
PopoverContent
>
<
PopoverBody
>
{
hasGroups
?
(
<
HStack
separator=
{
<
Separator
/>
}
alignItems=
"flex-start"
>
{
item
.
subItems
.
map
((
subItem
,
index
)
=>
{
if
(
!
Array
.
isArray
(
subItem
))
{
return
<
NavLink
key=
{
subItem
.
text
}
item=
{
subItem
}
/>;
}
return
(
<
chakra
.
ul
key=
{
index
}
display=
"flex"
flexDir=
"column"
rowGap=
{
1
}
>
{
subItem
.
map
((
navItem
)
=>
<
NavLink
key=
{
navItem
.
text
}
item=
{
navItem
}
/>)
}
</
chakra
.
ul
>
);
})
}
</
HStack
>
)
:
(
<
chakra
.
ul
display=
"flex"
flexDir=
"column"
rowGap=
{
1
}
>
{
item
.
subItems
.
map
((
subItem
)
=>
{
if
(
Array
.
isArray
(
subItem
))
{
return
null
;
}
return
<
NavLink
key=
{
subItem
.
text
}
item=
{
subItem
}
/>;
})
}
</
chakra
.
ul
>
)
}
</
PopoverBody
>
</
PopoverContent
>
</
PopoverRoot
>
<
Link
as=
"li"
listStyleType=
"none"
display=
"flex"
alignItems=
"center"
px=
{
2
}
py=
{
1.5
}
textStyle=
"sm"
fontWeight=
{
500
}
visual=
"navigation"
{
...
(
item
.
isActive
?
{
'
data
-
selected
':
true
}
:
{})
}
{
...
(
open
?
{
'
data
-
active
':
true
}
:
{})
}
borderRadius=
"base"
>
{
item
.
text
}
{
isHighlighted
&&
(
<
LightningLabel
iconColor=
{
item
.
isActive
?
'
link.navigation.bg.selected
'
:
'
link.navigation.bg
'
}
position=
{
{
lg
:
'
static
'
}
}
ml=
{
{
lg
:
'
2px
'
}
}
/>
)
}
<
IconSvg
name=
"arrows/east-mini"
boxSize=
{
5
}
transform=
"rotate(-90deg)"
ml=
{
1
}
/>
</
Link
>
</
Tooltip
>
);
};
...
...
ui/snippets/navigation/vertical/NavLinkGroup.tsx
View file @
388ec42a
...
...
@@ -3,7 +3,7 @@ import React from 'react';
import
type
{
NavGroupItem
}
from
'
types/client/navigation
'
;
import
{
PopoverBody
,
PopoverContent
,
PopoverRoot
,
PopoverTrigger
}
from
'
toolkit/chakra/popover
'
;
import
{
Tooltip
}
from
'
toolkit/chakra/tooltip
'
;
import
IconSvg
from
'
ui/shared/IconSvg
'
;
import
LightningLabel
from
'
../LightningLabel
'
;
...
...
@@ -24,80 +24,80 @@ const NavLinkGroup = ({ item, isCollapsed }: Props) => {
const
isHighlighted
=
checkRouteHighlight
(
item
.
subItems
);
const
content
=
(
<
Box
width=
"220px"
top=
{
{
lg
:
isExpanded
?
'
-16px
'
:
0
,
xl
:
isCollapsed
?
0
:
'
-16px
'
}
}
>
<
Text
color=
"text.secondary"
fontSize=
"sm"
mb=
{
1
}
display=
{
{
lg
:
isExpanded
?
'
none
'
:
'
block
'
,
xl
:
isCollapsed
?
'
block
'
:
'
none
'
}
}
>
{
item
.
text
}
</
Text
>
<
VStack
gap=
{
1
}
alignItems=
"start"
as=
"ul"
>
{
item
.
subItems
.
map
((
subItem
,
index
)
=>
Array
.
isArray
(
subItem
)
?
(
<
Box
key=
{
index
}
w=
"100%"
as=
"ul"
_notLast=
{
{
mb
:
2
,
pb
:
2
,
borderBottomWidth
:
'
1px
'
,
borderColor
:
'
border.divider
'
,
}
}
>
{
subItem
.
map
(
subSubItem
=>
<
NavLink
key=
{
subSubItem
.
text
}
item=
{
subSubItem
}
isCollapsed=
{
false
}
/>)
}
</
Box
>
)
:
<
NavLink
key=
{
subItem
.
text
}
item=
{
subItem
}
isCollapsed=
{
false
}
/>,
)
}
</
VStack
>
</
Box
>
);
return
(
<
Box
as=
"li"
listStyleType=
"none"
w=
"100%"
>
<
PopoverRoot
// TODO @tom2drum fix trigger
// trigger="hover"
positioning=
{
{
placement
:
'
right-start
'
,
offset
:
{
crossAxis
:
8
,
mainAxis
:
8
}
}
}
<
Tooltip
content=
{
content
}
positioning=
{
{
placement
:
'
right-start
'
,
offset
:
{
crossAxis
:
0
,
mainAxis
:
8
}
}
}
// should not be lazy to help google indexing pages
lazyMount=
{
false
}
visual=
"popover"
interactive
>
<
PopoverTrigger
>
<
Box
{
...
styleProps
.
itemProps
}
w=
{
{
lg
:
isExpanded
?
'
180px
'
:
'
60px
'
,
xl
:
isCollapsed
?
'
60px
'
:
'
180px
'
}
}
pl=
{
{
lg
:
isExpanded
?
2
:
'
15px
'
,
xl
:
isCollapsed
?
'
15px
'
:
2
}
}
pr=
{
{
lg
:
isExpanded
?
0
:
'
15px
'
,
xl
:
isCollapsed
?
'
15px
'
:
0
}
}
aria
-
label=
{
`${ item.text } link group`
}
position=
"relative"
bgColor=
{
item
.
isActive
?
'
link.navigation.bg.selected
'
:
'
link.navigation.bg
'
}
>
<
HStack
gap=
{
0
}
overflow=
"hidden"
>
<
NavLinkIcon
item=
{
item
}
/>
<
Text
{
...
styleProps
.
textProps
}
ml=
{
3
}
>
{
item
.
text
}
</
Text
>
{
isHighlighted
&&
(
<
LightningLabel
iconColor=
{
item
.
isActive
?
'
link.navigation.bg.selected
'
:
'
link.navigation.bg
'
}
isCollapsed=
{
isCollapsed
}
/>
)
}
<
IconSvg
name=
"arrows/east-mini"
position=
"absolute"
right=
"7px"
transform=
"rotate(180deg)"
boxSize=
{
6
}
opacity=
{
{
lg
:
isExpanded
?
'
1
'
:
'
0
'
,
xl
:
isCollapsed
?
'
0
'
:
'
1
'
}
}
transitionProperty=
"opacity"
transitionDuration=
"normal"
transitionTimingFunction=
"ease"
/>
</
HStack
>
</
Box
>
</
PopoverTrigger
>
<
PopoverContent
width=
"252px"
top=
{
{
lg
:
isExpanded
?
'
-16px
'
:
0
,
xl
:
isCollapsed
?
0
:
'
-16px
'
}
}
>
<
PopoverBody
p=
{
4
}
>
<
Text
color=
"text.secondary"
fontSize=
"sm"
mb=
{
1
}
display=
{
{
lg
:
isExpanded
?
'
none
'
:
'
block
'
,
xl
:
isCollapsed
?
'
block
'
:
'
none
'
}
}
>
<
Box
{
...
styleProps
.
itemProps
}
w=
{
{
lg
:
isExpanded
?
'
180px
'
:
'
60px
'
,
xl
:
isCollapsed
?
'
60px
'
:
'
180px
'
}
}
pl=
{
{
lg
:
isExpanded
?
2
:
'
15px
'
,
xl
:
isCollapsed
?
'
15px
'
:
2
}
}
pr=
{
{
lg
:
isExpanded
?
0
:
'
15px
'
,
xl
:
isCollapsed
?
'
15px
'
:
0
}
}
aria
-
label=
{
`${ item.text } link group`
}
position=
"relative"
bgColor=
{
item
.
isActive
?
'
link.navigation.bg.selected
'
:
'
link.navigation.bg
'
}
>
<
HStack
gap=
{
0
}
overflow=
"hidden"
>
<
NavLinkIcon
item=
{
item
}
/>
<
Text
{
...
styleProps
.
textProps
}
ml=
{
3
}
>
{
item
.
text
}
</
Text
>
<
VStack
gap=
{
1
}
alignItems=
"start"
as=
"ul"
>
{
item
.
subItems
.
map
((
subItem
,
index
)
=>
Array
.
isArray
(
subItem
)
?
(
<
Box
key=
{
index
}
w=
"100%"
as=
"ul"
_notLast=
{
{
mb
:
2
,
pb
:
2
,
borderBottomWidth
:
'
1px
'
,
borderColor
:
'
border.divider
'
,
}
}
>
{
subItem
.
map
(
subSubItem
=>
<
NavLink
key=
{
subSubItem
.
text
}
item=
{
subSubItem
}
isCollapsed=
{
false
}
/>)
}
</
Box
>
)
:
<
NavLink
key=
{
subItem
.
text
}
item=
{
subItem
}
isCollapsed=
{
false
}
/>,
)
}
</
VStack
>
</
PopoverBody
>
</
PopoverContent
>
</
PopoverRoot
>
{
isHighlighted
&&
(
<
LightningLabel
iconColor=
{
item
.
isActive
?
'
link.navigation.bg.selected
'
:
'
link.navigation.bg
'
}
isCollapsed=
{
isCollapsed
}
/>
)
}
<
IconSvg
name=
"arrows/east-mini"
position=
"absolute"
right=
"7px"
transform=
"rotate(180deg)"
boxSize=
{
6
}
opacity=
{
{
lg
:
isExpanded
?
'
1
'
:
'
0
'
,
xl
:
isCollapsed
?
'
0
'
:
'
1
'
}
}
transitionProperty=
"opacity"
transitionDuration=
"normal"
transitionTimingFunction=
"ease"
/>
</
HStack
>
</
Box
>
</
Tooltip
>
</
Box
>
);
};
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment