Skip to content

Commit f860fd2

Browse files
committed
Improve usability of icon buttons in header with tooltip
1 parent 9c95795 commit f860fd2

File tree

4 files changed

+66
-53
lines changed

4 files changed

+66
-53
lines changed
Lines changed: 35 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as React from 'react';
2+
import { useState } from 'react';
23
import { Link, NavLink } from 'react-router-dom';
34
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
45
import { faCogs, faLayerGroup, faSignOutAlt, faUserCircle } from '@fortawesome/free-solid-svg-icons';
@@ -9,36 +10,18 @@ import tw, { theme } from 'twin.macro';
910
import styled from 'styled-components/macro';
1011
import http from '@/api/http';
1112
import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
12-
import { useState } from 'react';
13-
14-
const Navigation = styled.div`
15-
${tw`w-full bg-neutral-900 shadow-md overflow-x-auto`};
16-
17-
& > div {
18-
${tw`mx-auto w-full flex items-center`};
19-
}
20-
21-
& #logo {
22-
${tw`flex-1`};
23-
24-
& > a {
25-
${tw`text-2xl font-header px-4 no-underline text-neutral-200 hover:text-neutral-100 transition-colors duration-150`};
26-
}
27-
}
28-
`;
13+
import Tooltip from '@/components/elements/tooltip/Tooltip';
2914

3015
const RightNavigation = styled.div`
31-
${tw`flex h-full items-center justify-center`};
32-
3316
& > a, & > button, & > .navigation-link {
3417
${tw`flex items-center h-full no-underline text-neutral-300 px-6 cursor-pointer transition-all duration-150`};
35-
18+
3619
&:active, &:hover {
3720
${tw`text-neutral-100 bg-black`};
3821
}
39-
22+
4023
&:active, &:hover, &.active {
41-
box-shadow: inset 0 -2px ${theme`colors.cyan.700`.toString()};
24+
box-shadow: inset 0 -2px ${theme`colors.cyan.600`.toString()};
4225
}
4326
}
4427
`;
@@ -57,32 +40,43 @@ export default () => {
5740
};
5841

5942
return (
60-
<Navigation>
61-
<SpinnerOverlay visible={isLoggingOut} />
62-
<div css={tw`mx-auto w-full flex items-center`} style={{ maxWidth: '1200px', height: '3.5rem' }}>
63-
<div id={'logo'}>
64-
<Link to={'/'}>
43+
<div className={'w-full bg-neutral-900 shadow-md overflow-x-auto'}>
44+
<SpinnerOverlay visible={isLoggingOut}/>
45+
<div className={'mx-auto w-full flex items-center h-[3.5rem] max-w-[1200px]'}>
46+
<div id={'logo'} className={'flex-1'}>
47+
<Link
48+
to={'/'}
49+
className={'text-2xl font-header px-4 no-underline text-neutral-200 hover:text-neutral-100 transition-colors duration-150'}
50+
>
6551
{name}
6652
</Link>
6753
</div>
68-
<RightNavigation>
54+
<RightNavigation className={'flex h-full items-center justify-center'}>
6955
<SearchContainer/>
70-
<NavLink to={'/'} exact>
71-
<FontAwesomeIcon icon={faLayerGroup}/>
72-
</NavLink>
73-
<NavLink to={'/account'}>
74-
<FontAwesomeIcon icon={faUserCircle}/>
75-
</NavLink>
56+
<Tooltip placement={'bottom'} content={'Dashboard'}>
57+
<NavLink to={'/'} exact>
58+
<FontAwesomeIcon icon={faLayerGroup}/>
59+
</NavLink>
60+
</Tooltip>
61+
<Tooltip placement={'bottom'} content={'Account Settings'}>
62+
<NavLink to={'/account'}>
63+
<FontAwesomeIcon icon={faUserCircle}/>
64+
</NavLink>
65+
</Tooltip>
7666
{rootAdmin &&
77-
<a href={'/admin'} rel={'noreferrer'}>
78-
<FontAwesomeIcon icon={faCogs}/>
79-
</a>
67+
<Tooltip placement={'bottom'} content={'Admin'}>
68+
<a href={'/admin'} rel={'noreferrer'}>
69+
<FontAwesomeIcon icon={faCogs}/>
70+
</a>
71+
</Tooltip>
8072
}
81-
<button onClick={onTriggerLogout}>
82-
<FontAwesomeIcon icon={faSignOutAlt}/>
83-
</button>
73+
<Tooltip placement={'bottom'} content={'Sign Out'}>
74+
<button onClick={onTriggerLogout}>
75+
<FontAwesomeIcon icon={faSignOutAlt}/>
76+
</button>
77+
</Tooltip>
8478
</RightNavigation>
8579
</div>
86-
</Navigation>
80+
</div>
8781
);
8882
};

resources/scripts/components/dashboard/search/SearchContainer.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
33
import { faSearch } from '@fortawesome/free-solid-svg-icons';
44
import useEventListener from '@/plugins/useEventListener';
55
import SearchModal from '@/components/dashboard/search/SearchModal';
6+
import Tooltip from '@/components/elements/tooltip/Tooltip';
67

78
export default () => {
89
const [ visible, setVisible ] = useState(false);
@@ -24,9 +25,11 @@ export default () => {
2425
onDismissed={() => setVisible(false)}
2526
/>
2627
}
27-
<div className={'navigation-link'} onClick={() => setVisible(true)}>
28-
<FontAwesomeIcon icon={faSearch}/>
29-
</div>
28+
<Tooltip placement={'bottom'} content={'Search'}>
29+
<div className={'navigation-link'} onClick={() => setVisible(true)}>
30+
<FontAwesomeIcon icon={faSearch}/>
31+
</div>
32+
</Tooltip>
3033
</>
3134
);
3235
};

resources/scripts/components/elements/SubNavigation.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const SubNavigation = styled.div`
2121
2222
&:active, &.active {
2323
${tw`text-neutral-100`};
24-
box-shadow: inset 0 -2px ${theme`colors.cyan.700`.toString()};
24+
box-shadow: inset 0 -2px ${theme`colors.cyan.600`.toString()};
2525
}
2626
}
2727
}

resources/scripts/components/elements/tooltip/Tooltip.tsx

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ import {
1818
import { AnimatePresence, motion } from 'framer-motion';
1919

2020
interface Props {
21+
rest?: number;
22+
delay?: number | Partial<{ open: number; close: number }>;
23+
alwaysOpen?: boolean;
2124
content: string | React.ReactChild;
2225
disabled?: boolean;
2326
arrow?: boolean;
@@ -34,22 +37,35 @@ const arrowSides: Record<Side, Side> = {
3437
right: 'left',
3538
};
3639

37-
export default ({ content, children, disabled = false, ...props }: Props) => {
40+
export default ({
41+
content,
42+
children,
43+
disabled = false,
44+
alwaysOpen = false,
45+
delay = { open: 500 },
46+
rest = 30,
47+
...props
48+
}: Props) => {
3849
const arrowEl = useRef<HTMLDivElement>(null);
39-
const [ open, setOpen ] = useState(false);
50+
const [ open, setOpen ] = useState(alwaysOpen || false);
4051

4152
const { x, y, reference, floating, middlewareData, strategy, context } = useFloating({
4253
open,
4354
placement: props.placement || 'top',
4455
strategy: props.strategy || 'absolute',
45-
middleware: [ offset(6), flip(), shift({ padding: 6 }), arrow({ element: arrowEl, padding: 6 }) ],
46-
onOpenChange: setOpen,
56+
middleware: [
57+
offset(props.arrow ? 10 : 6),
58+
flip(),
59+
shift({ padding: 6 }),
60+
arrow({ element: arrowEl, padding: 6 }),
61+
],
62+
onOpenChange: (o) => setOpen(o || alwaysOpen || false),
4763
whileElementsMounted: autoUpdate,
4864
});
4965

5066
const { getReferenceProps, getFloatingProps } = useInteractions([
5167
useFocus(context),
52-
useHover(context, { restMs: 30 }),
68+
useHover(context, { restMs: rest, delay }),
5369
useRole(context, { role: 'tooltip' }),
5470
useDismiss(context),
5571
]);
@@ -70,7 +86,7 @@ export default ({ content, children, disabled = false, ...props }: Props) => {
7086
initial={{ opacity: 0, scale: 0.85 }}
7187
animate={{ opacity: 1, scale: 1 }}
7288
exit={{ opacity: 0 }}
73-
transition={{ type: 'easeIn', damping: 20, stiffness: 300, duration: 0.1 }}
89+
transition={{ type: 'spring', damping: 20, stiffness: 300, duration: 0.075 }}
7490
{...getFloatingProps({
7591
ref: floating,
7692
className: 'absolute top-0 left-0 bg-gray-900 text-sm text-gray-200 px-3 py-2 rounded pointer-events-none max-w-[90vw]',
@@ -86,10 +102,10 @@ export default ({ content, children, disabled = false, ...props }: Props) => {
86102
<div
87103
ref={arrowEl}
88104
style={{
89-
transform: `translate(${Math.round(ax || 0)}px, ${Math.round(ay || 0)}px)`,
105+
transform: `translate(${Math.round(ax || 0)}px, ${Math.round(ay || 0)}px) rotate(45deg)`,
90106
[side]: '-6px',
91107
}}
92-
className={'absolute top-0 left-0 bg-gray-900 w-3 h-3 rotate-45'}
108+
className={'absolute top-0 left-0 bg-gray-900 w-3 h-3'}
93109
/>
94110
}
95111
</motion.div>

0 commit comments

Comments
 (0)