commit bcb51269c9b54091df460c96d5652b00d5bca11f
parent fd3426866dda07bcb1d1854f28aefba5b71cee8a
Author: Yongbin Kim <iam@yongbin.kim>
Date: Thu, 19 Jan 2023 15:57:07 +0900
feat: NavbarDropdownItem 컴포넌트 추가
Signed-off-by: Yongbin Kim <iam@yongbin.kim>
Diffstat:
6 files changed, 174 insertions(+), 35 deletions(-)
diff --git a/components/layout/Header.tsx b/components/layout/Header.tsx
@@ -1,5 +1,7 @@
import { useToken } from '@/components/contexts/TokenContext'
import Navbar from '@/components/layout/Navbar'
+import NavbarDropdownItem from '@/components/layout/NavbarDropdownItem'
+import NavbarDropdownLabel from '@/components/layout/NavbarDropdownLabel'
import NavbarItem from '@/components/layout/NavbarItem'
import classNames from '@/lib/classnames'
import { UserProfile } from '@/lib/models/user_profile'
@@ -36,12 +38,25 @@ export default function Header () {
</>
) : (
<>
- <NavbarItem href={`/wiki/`}>
- 내 위키
- </NavbarItem>
- <NavbarItem href={`/users/${userProfile.loginId}`}>
- {userProfile.nickname}
- </NavbarItem>
+ <NavbarDropdownItem label={userProfile.nickname}>
+ <NavbarItem href={`/users/${userProfile.loginId}`}>
+ 내 프로필
+ </NavbarItem>
+
+ <hr />
+
+ <NavbarDropdownLabel>내 위키</NavbarDropdownLabel>
+
+ <NavbarItem href={`/wiki/test`}>
+ 테스트
+ </NavbarItem>
+
+ <hr />
+
+ <NavbarItem href={`/users/logout`}>
+ 로그아웃
+ </NavbarItem>
+ </NavbarDropdownItem>
</>
)}
</Navbar>
diff --git a/components/layout/Navbar.module.css b/components/layout/Navbar.module.css
@@ -1 +1 @@
-.navbar{padding:.5rem 0;z-index:10;background-color:#fafdfa;color:#191c1b}.navbar .item:hover{background-color:rgba(25,28,27,.1)}.navbar.is-primary{background-color:#58fbda;color:#00201a}.navbar.is-primary .item:hover{background-color:rgba(0,32,26,.1)}.navbar.is-secondary{background-color:#f5d9ff;color:#30004a}.navbar.is-secondary .item:hover{background-color:rgba(48,0,74,.1)}.navbar.is-tertiary{background-color:#c7e7ff;color:#001e2e}.navbar.is-tertiary .item:hover{background-color:rgba(0,30,46,.1)}.navbar.is-error{background-color:#ffdad6;color:#410002}.navbar.is-error .item:hover{background-color:rgba(65,0,2,.1)}@media(prefers-color-scheme: dark){.navbar{background-color:#191c1b;color:#e1e3e0}.navbar .item:hover{background-color:rgba(225,227,224,.1)}.navbar.is-primary{background-color:#005143;color:#58fbda}.navbar.is-primary .item:hover{background-color:rgba(88,251,218,.1)}.navbar.is-secondary{background-color:#61317e;color:#f5d9ff}.navbar.is-secondary .item:hover{background-color:rgba(245,217,255,.1)}.navbar.is-tertiary{background-color:#2a4a5f;color:#c7e7ff}.navbar.is-tertiary .item:hover{background-color:rgba(199,231,255,.1)}.navbar.is-error{background-color:#93000a;color:#ffdad6}.navbar.is-error .item:hover{background-color:rgba(255,218,214,.1)}}.navbar>.container{display:flex;padding:0 1rem;align-items:stretch;min-height:2.5rem}.navbar .brand,.navbar .menu{display:flex;align-items:stretch}.navbar .brand{flex-shrink:0;line-height:1.5rem;font-size:1rem;letter-spacing:.009375rem;font-weight:500}.navbar .brand .navbar-item{font-weight:bold}.navbar .menu{flex:1;justify-content:flex-end}.navbar .menu.is-centered{justify-content:center;margin-right:0}.navbar .item{display:flex;align-items:center;padding:0 1rem;line-height:2.5rem;border-radius:9999px}.navbar a.item{color:inherit}.navbar a.item,.navbar a.item:hover{text-decoration:none}/*# sourceMappingURL=Navbar.module.css.map */
+.navbar{padding:.5rem 0;z-index:10;background-color:#fafdfa;color:#191c1b}.navbar .item:hover{background-color:rgba(25,28,27,.1)}.navbar.is-primary{background-color:#58fbda;color:#00201a}.navbar.is-primary .item:hover{background-color:rgba(0,32,26,.1)}.navbar.is-secondary{background-color:#f5d9ff;color:#30004a}.navbar.is-secondary .item:hover{background-color:rgba(48,0,74,.1)}.navbar.is-tertiary{background-color:#c7e7ff;color:#001e2e}.navbar.is-tertiary .item:hover{background-color:rgba(0,30,46,.1)}.navbar.is-error{background-color:#ffdad6;color:#410002}.navbar.is-error .item:hover{background-color:rgba(65,0,2,.1)}@media(prefers-color-scheme: dark){.navbar{background-color:#191c1b;color:#e1e3e0}.navbar .item:hover{background-color:rgba(225,227,224,.1)}.navbar.is-primary{background-color:#005143;color:#58fbda}.navbar.is-primary .item:hover{background-color:rgba(88,251,218,.1)}.navbar.is-secondary{background-color:#61317e;color:#f5d9ff}.navbar.is-secondary .item:hover{background-color:rgba(245,217,255,.1)}.navbar.is-tertiary{background-color:#2a4a5f;color:#c7e7ff}.navbar.is-tertiary .item:hover{background-color:rgba(199,231,255,.1)}.navbar.is-error{background-color:#93000a;color:#ffdad6}.navbar.is-error .item:hover{background-color:rgba(255,218,214,.1)}}.navbar>.container{display:flex;padding:0 1rem;align-items:stretch;min-height:2.5rem}.brand,.menu{display:flex;align-items:stretch}.brand{flex-shrink:0;line-height:1.5rem;font-size:1rem;letter-spacing:.009375rem;font-weight:500}.brand .navbar-item{font-weight:bold}.menu{flex:1;justify-content:flex-end}.menu.is-centered{justify-content:center;margin-right:0}.item{display:flex;align-items:center;padding:0 1rem;line-height:2.5rem;border-radius:9999px}.item[href]{color:inherit}.item[href],.item[href]:hover{text-decoration:none}.item.is-dropdown{position:relative;cursor:default}.item.is-dropdown .dropdown{position:absolute;top:100%;z-index:10;min-width:10rem;margin-top:.5rem;padding:.5rem 0;box-shadow:0 14px 28px rgba(0,0,0,.25),0 10px 10px rgba(0,0,0,.22);border-radius:.5rem;opacity:0;visibility:hidden;transform:translateY(-0.5rem);transition:transform 100ms ease-in-out,opacity 150ms,visibility 150ms;box-shadow:0px 4px 4px 0px rgba(0, 0, 0, 0.2),0px 8px 12px 6px rgba(0, 0, 0, 0.1);background-color:#fafdfa;color:#191c1b;background-color:#f1f6f2}.menu .item.is-dropdown .dropdown{right:0}.brand .item.is-dropdown .dropdown{left:0}@media(prefers-color-scheme: dark){.item.is-dropdown .dropdown{background-color:#191c1b;color:#e1e3e0;background-color:#242a28}}.item.is-dropdown .dropdown hr{margin:.5rem 0;border:0;border-top:1px solid rgba(63,73,70,.24)}@media(prefers-color-scheme: dark){.item.is-dropdown .dropdown hr{border-top:1px solid rgba(191,201,196,.24)}}.item.is-dropdown .dropdown .item{padding:0 1rem;line-height:2.5rem;border-radius:0}.item.is-dropdown:hover .dropdown{opacity:1;visibility:visible;transform:translateY(0)}.dropdown-label{display:flex;align-items:center;padding:.5rem 1rem;line-height:2.5rem;border-radius:9999px;cursor:default;line-height:1rem;font-size:.75rem;letter-spacing:.0416666667rem;font-weight:500}/*# sourceMappingURL=Navbar.module.css.map */
diff --git a/components/layout/Navbar.module.css.map b/components/layout/Navbar.module.css.map
@@ -1 +1 @@
-{"version":3,"sourceRoot":"","sources":["Navbar.module.scss","../../styles/core/_colors.scss","../../styles/core/_typography.scss"],"names":[],"mappings":"AAsBA,QACE,gBACA,WAVA,yBACA,cAEA,oBACE,mCAYE,mBAhBJ,yBACA,cAEA,+BACE,kCAYE,qBAhBJ,yBACA,cAEA,iCACE,kCAYE,oBAhBJ,yBACA,cAEA,gCACE,kCAYE,iBAhBJ,yBACA,cAEA,6BACE,iCC8GF,mCD1GF,QARE,yBACA,cAEA,oBACE,sCAYE,mBAhBJ,yBACA,cAEA,+BACE,qCAYE,qBAhBJ,yBACA,cAEA,iCACE,sCAYE,oBAhBJ,yBACA,cAEA,gCACE,sCAYE,iBAhBJ,yBACA,cAEA,6BACE,uCAkBF,mBACE,aACA,eACA,oBACA,WAhCU,OAmCZ,6BAEE,aACA,oBAGF,eACE,cEoEA,mBACA,eACA,0BACA,gBFpEA,4BACE,iBAIJ,cACE,OACA,yBAEA,0BACE,uBACA,eAIJ,cACE,aACA,mBACA,QA9DW,OA+DX,YAhEU,OAiEV,cA/DU,OAkEZ,eACE,cAEA,oCACE","file":"Navbar.module.css"}
-\ No newline at end of file
+{"version":3,"sourceRoot":"","sources":["Navbar.module.scss","../../styles/core/_colors.scss","../../styles/core/_typography.scss","../../styles/core/_elevate.scss"],"names":[],"mappings":"AA0BA,QACE,gBACA,WAVA,yBACA,cAEA,oBACE,mCAYE,mBAhBJ,yBACA,cAEA,+BACE,kCAYE,qBAhBJ,yBACA,cAEA,iCACE,kCAYE,oBAhBJ,yBACA,cAEA,gCACE,kCAYE,iBAhBJ,yBACA,cAEA,6BACE,iCC0GF,mCDtGF,QARE,yBACA,cAEA,oBACE,sCAYE,mBAhBJ,yBACA,cAEA,+BACE,qCAYE,qBAhBJ,yBACA,cAEA,iCACE,sCAYE,oBAhBJ,yBACA,cAEA,gCACE,sCAYE,iBAhBJ,yBACA,cAEA,6BACE,uCAkBF,mBACE,aACA,eACA,oBACA,WAnCU,OA2Cd,aAEE,aACA,oBAGF,OACE,cE2DE,mBACA,eACA,0BACA,gBF3DF,oBACE,iBAIJ,MACE,OACA,yBAEA,kBACE,uBACA,eAQJ,MACE,aACA,mBACA,QA1Ea,OA2Eb,YA5EY,OA6EZ,cA3EY,OA6EZ,YACE,cAEA,8BACE,qBASN,kBACE,kBACA,eAEA,4BACE,kBACA,SACA,WACA,UAhGa,MAiGb,iBACA,gBACA,WAzGK,wDA0GL,cAnGc,MAoGd,UACA,kBACA,8BACA,sEGvDF,kFHoEI,yBACA,cG/EJ,yBHmEE,kCACE,QAGF,mCACE,OCIJ,mCDvBA,4BAyBI,yBACA,cG/EJ,0BHoFE,+BACE,eACA,SAEE,wCCZN,mCDQE,+BAII,4CAIJ,kCACE,QAvIS,OAwIT,YAzIQ,OA0IR,gBASF,kCACE,UACA,mBACA,wBAKN,gBACE,aACA,mBACA,QAxJuB,WAyJvB,YA/JY,OAgKZ,cA9JY,OA+JZ,eEpDE,iBACA,iBACA,8BACA","file":"Navbar.module.css"}
+\ No newline at end of file
diff --git a/components/layout/Navbar.module.scss b/components/layout/Navbar.module.scss
@@ -2,6 +2,7 @@
@use 'core/colors';
@use 'core/vars';
@use 'core/typography';
+@use 'core/elevate';
$height: 3.5rem;
$shadow: 0 14px 28px rgba(0,0,0,0.25), 0 10px 10px rgba(0,0,0,0.22);
@@ -10,6 +11,9 @@ $item-height: 2.5rem;
$item-padding: 0 vars.$gap;
$item-radius: 9999px;
$item-hover-background-opacity: 10%;
+$dropdown-width: 10rem;
+$dropdown-radius: 0.5rem;
+$dropdown-label-padding: 0.5rem vars.$gap;
@mixin _apply-navbar-color($theme, $color, $on-color) {
background-color: colors.get($theme, $color);
@@ -40,41 +44,49 @@ $item-hover-background-opacity: 10%;
align-items: stretch;
min-height: $item-height;
}
+}
- .brand,
- .menu {
- display: flex;
- align-items: stretch;
- }
+// ----------------------------------------
+// Branding, Menu
+// ----------------------------------------
+
+.brand,
+.menu {
+ display: flex;
+ align-items: stretch;
+}
- .brand {
- flex-shrink: 0;
- @include typography.apply('title-medium');
+.brand {
+ flex-shrink: 0;
+ @include typography.apply('title-medium');
- .navbar-item {
- font-weight: bold;
- }
+ .navbar-item {
+ font-weight: bold;
}
+}
- .menu {
- flex: 1;
- justify-content: flex-end;
+.menu {
+ flex: 1;
+ justify-content: flex-end;
- &.is-centered {
- justify-content: center;
- margin-right: 0;
- }
+ &.is-centered {
+ justify-content: center;
+ margin-right: 0;
}
+}
- .item {
- display: flex;
- align-items: center;
- padding: $item-padding;
- line-height: $item-height;
- border-radius: $item-radius;
- }
+// ----------------------------------------
+// Item
+// ----------------------------------------
+
+.item {
+ display: flex;
+ align-items: center;
+ padding: $item-padding;
+ line-height: $item-height;
+ border-radius: $item-radius;
- a.item {
+ &[href] {
color: inherit;
&, &:hover {
@@ -82,3 +94,81 @@ $item-hover-background-opacity: 10%;
}
}
}
+
+// ----------------------------------------
+// Dropdown item
+// ----------------------------------------
+
+.item.is-dropdown {
+ position: relative;
+ cursor: default;
+
+ .dropdown {
+ position: absolute;
+ top: 100%;
+ z-index: 10;
+ min-width: $dropdown-width;
+ margin-top: 0.5rem;
+ padding: 0.5rem 0;
+ box-shadow: $shadow;
+ border-radius: $dropdown-radius;
+ opacity: 0;
+ visibility: hidden;
+ transform: translateY(-0.5rem);
+ transition: transform 100ms ease-in-out, opacity 150ms, visibility 150ms;
+
+ .menu & {
+ right: 0;
+ }
+
+ .brand & {
+ left: 0;
+ }
+
+ @include elevate.apply-box-shadow(5);
+
+ @include colors.apply-themes() using ($theme) {
+ background-color: colors.get($theme, 'surface');
+ color: colors.get($theme, 'on-surface');
+
+ @include elevate.apply-background-color($theme, 5, 'surface-variant');
+ }
+
+ hr {
+ margin: 0.5rem 0;
+ border: 0;
+ @include colors.apply-themes() using ($theme) {
+ border-top: 1px solid rgba(colors.get($theme, 'on-surface-variant'), 0.24);
+ }
+ }
+
+ .item {
+ padding: $item-padding;
+ line-height: $item-height;
+ border-radius: 0;
+ }
+
+ .item:hover {
+ //background-color: rgba(colors.get('on-surface'), $item-hover-background-opacity);
+ }
+ }
+
+ &:hover {
+ .dropdown {
+ opacity: 1;
+ visibility: visible;
+ transform: translateY(0);
+ }
+ }
+}
+
+.dropdown-label {
+ display: flex;
+ align-items: center;
+ padding: $dropdown-label-padding;
+ line-height: $item-height;
+ border-radius: $item-radius;
+ cursor: default;
+
+ @include typography.apply('label-medium');
+}
diff --git a/components/layout/NavbarDropdownItem.tsx b/components/layout/NavbarDropdownItem.tsx
@@ -0,0 +1,21 @@
+import classNames from '@/lib/classnames'
+import Link, { LinkProps } from 'next/link'
+import { AnchorHTMLAttributes, ReactNode } from 'react'
+import styles from './Navbar.module.css'
+
+export interface NavbarItemProps {
+ label: ReactNode
+ children?: ReactNode
+}
+
+export default function NavbarDropdownItem (props: NavbarItemProps) {
+ return (
+ <div {...classNames(styles['item'], styles['is-dropdown'])}>
+ {props.label}
+
+ <div {...classNames(styles['dropdown'])}>
+ {props.children}
+ </div>
+ </div>
+ )
+}
diff --git a/components/layout/NavbarDropdownLabel.tsx b/components/layout/NavbarDropdownLabel.tsx
@@ -0,0 +1,13 @@
+import classNames from '@/lib/classnames'
+import { ReactNode } from 'react'
+import styles from './Navbar.module.css'
+
+export interface NavbarDropdownLabelProps {
+ children?: ReactNode
+}
+
+export default function NavbarDropdownLabel (props: NavbarDropdownLabelProps) {
+ return (
+ <div {...classNames(styles['dropdown-label'])}>{props.children}</div>
+ )
+}