feat(ui): animate sidebar icons on hover
This commit is contained in:
@@ -13,6 +13,83 @@
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.navIcon {
|
||||||
|
display: inline-flex;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: no-preference) {
|
||||||
|
.drawStroke {
|
||||||
|
stroke-dasharray: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.topSlider {
|
||||||
|
--slider-offset: -3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottomSlider {
|
||||||
|
--slider-offset: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navLink:focus-visible {
|
||||||
|
.portfolioIcon {
|
||||||
|
animation: portfolio-sway 650ms ease-in-out;
|
||||||
|
}
|
||||||
|
.drawStroke {
|
||||||
|
animation: stroke-draw 650ms ease-in-out;
|
||||||
|
}
|
||||||
|
:is(.topSlider, .bottomSlider) {
|
||||||
|
animation: slider-slide 650ms ease-in-out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (hover: hover) {
|
||||||
|
.navLink:hover {
|
||||||
|
.portfolioIcon {
|
||||||
|
animation: portfolio-sway 650ms ease-in-out;
|
||||||
|
}
|
||||||
|
.drawStroke {
|
||||||
|
animation: stroke-draw 650ms ease-in-out;
|
||||||
|
}
|
||||||
|
:is(.topSlider, .bottomSlider) {
|
||||||
|
animation: slider-slide 650ms ease-in-out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes portfolio-sway {
|
||||||
|
0%,
|
||||||
|
100% {
|
||||||
|
transform: rotate(0);
|
||||||
|
}
|
||||||
|
30% {
|
||||||
|
transform: translateY(-1px) rotate(-10deg);
|
||||||
|
}
|
||||||
|
65% {
|
||||||
|
transform: rotate(7deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes stroke-draw {
|
||||||
|
0% {
|
||||||
|
stroke-dashoffset: 1;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
stroke-dashoffset: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slider-slide {
|
||||||
|
0%,
|
||||||
|
100% {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: translateX(var(--slider-offset));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 47.99375em) {
|
@media (max-width: 47.99375em) {
|
||||||
.bottomNav {
|
.bottomNav {
|
||||||
background: var(--mantine-color-white);
|
background: var(--mantine-color-white);
|
||||||
|
|||||||
+46
-16
@@ -16,6 +16,7 @@ const AppShell: FC<AppShellProps> = (props) => {
|
|||||||
{
|
{
|
||||||
to: "/accounts",
|
to: "/accounts",
|
||||||
label: t("navigation.accounts"),
|
label: t("navigation.accounts"),
|
||||||
|
iconClassName: classes.portfolioIcon,
|
||||||
icon: (
|
icon: (
|
||||||
<path d="M8 6V4h8v2M3 10h18M9 10v3h6v-3M5 6h14a2 2 0 0 1 2 2v11H3V8a2 2 0 0 1 2-2Z" />
|
<path d="M8 6V4h8v2M3 10h18M9 10v3h6v-3M5 6h14a2 2 0 0 1 2 2v11H3V8a2 2 0 0 1 2-2Z" />
|
||||||
),
|
),
|
||||||
@@ -23,18 +24,42 @@ const AppShell: FC<AppShellProps> = (props) => {
|
|||||||
{
|
{
|
||||||
to: "/stocks",
|
to: "/stocks",
|
||||||
label: t("navigation.stocks"),
|
label: t("navigation.stocks"),
|
||||||
icon: <path d="M3 3v18h18M7 14l5-5 4 3 5-7M16 5h5v5" />,
|
icon: (
|
||||||
|
<>
|
||||||
|
<path d="M3 3v18h18" />
|
||||||
|
<path
|
||||||
|
className={classes.drawStroke}
|
||||||
|
pathLength={1}
|
||||||
|
d="M7 14l5-5 4 3 5-7M16 5h5v5"
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
to: "/bonds",
|
to: "/bonds",
|
||||||
label: t("navigation.bonds"),
|
label: t("navigation.bonds"),
|
||||||
icon: <path d="M6 3h12v18l-3-2-3 2-3-2-3 2V3ZM9 7h6M9 11h6M9 15h3" />,
|
icon: (
|
||||||
|
<>
|
||||||
|
<path d="M6 3h12v18l-3-2-3 2-3-2-3 2V3Z" />
|
||||||
|
<path
|
||||||
|
className={classes.drawStroke}
|
||||||
|
pathLength={1}
|
||||||
|
d="M9 7h6M9 11h6M9 15h3"
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
to: "/settings",
|
to: "/settings",
|
||||||
label: t("navigation.settings"),
|
label: t("navigation.settings"),
|
||||||
icon: (
|
icon: (
|
||||||
<path d="M4 7h9m4 0h3M4 17h3m4 0h9M13 4v6h4V4h-4ZM7 14v6h4v-6H7Z" />
|
<>
|
||||||
|
<path className={classes.topSlider} d="M4 7h9m4 0h3M13 4v6h4V4h-4Z" />
|
||||||
|
<path
|
||||||
|
className={classes.bottomSlider}
|
||||||
|
d="M4 17h3m4 0h9M7 14v6h4v-6H7Z"
|
||||||
|
/>
|
||||||
|
</>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@@ -52,24 +77,29 @@ const AppShell: FC<AppShellProps> = (props) => {
|
|||||||
{items.map((item) => (
|
{items.map((item) => (
|
||||||
<NavLink
|
<NavLink
|
||||||
component={RouterNavLink}
|
component={RouterNavLink}
|
||||||
|
className={classes.navLink}
|
||||||
key={item.to}
|
key={item.to}
|
||||||
to={item.to}
|
to={item.to}
|
||||||
label={item.label}
|
label={item.label}
|
||||||
leftSection={
|
leftSection={
|
||||||
<svg
|
<span
|
||||||
width={20}
|
className={`${classes.navIcon} ${item.iconClassName ?? ""}`}
|
||||||
height={20}
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="1.5"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
aria-hidden="true"
|
|
||||||
focusable="false"
|
|
||||||
>
|
>
|
||||||
{item.icon}
|
<svg
|
||||||
</svg>
|
width={20}
|
||||||
|
height={20}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
aria-hidden="true"
|
||||||
|
focusable="false"
|
||||||
|
>
|
||||||
|
{item.icon}
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
}
|
}
|
||||||
active={pathname.startsWith(item.to)}
|
active={pathname.startsWith(item.to)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user