feat(coach): AI 코칭 버튼 및 버블 팝업 대화형 조언 기능 구현
This commit is contained in:
parent
8a34dd2afc
commit
70d7cb34a3
36 changed files with 4521 additions and 1264 deletions
|
|
@ -41,17 +41,17 @@
|
|||
--auth-room-veil:
|
||||
linear-gradient(
|
||||
90deg,
|
||||
rgba(247, 244, 236, 0.94) 0%,
|
||||
rgba(243, 238, 227, 0.86) 48%,
|
||||
rgba(238, 231, 217, 0.72) 74%,
|
||||
rgba(232, 223, 207, 0.62) 100%
|
||||
color-mix(in srgb, var(--bg-app) 94%, transparent) 0%,
|
||||
color-mix(in srgb, var(--bg-app) 86%, transparent) 48%,
|
||||
color-mix(in srgb, var(--bg-app) 72%, transparent) 74%,
|
||||
color-mix(in srgb, var(--bg-app) 62%, transparent) 100%
|
||||
);
|
||||
--auth-room-shade: linear-gradient(180deg, rgba(255, 253, 248, 0.08), rgba(120, 101, 72, 0.12));
|
||||
--auth-room-shade: linear-gradient(180deg, color-mix(in srgb, var(--bg-surface) 8%, transparent), color-mix(in srgb, var(--clay) 12%, transparent));
|
||||
--auth-hero-text: var(--text-strong);
|
||||
--auth-hero-muted: color-mix(in srgb, var(--text-body) 88%, transparent);
|
||||
--auth-hero-surface: rgba(255, 254, 251, 0.48);
|
||||
--auth-hero-border: rgba(101, 86, 62, 0.22);
|
||||
--auth-hero-chip: rgba(255, 254, 251, 0.62);
|
||||
--auth-hero-surface: color-mix(in srgb, var(--bg-surface) 48%, transparent);
|
||||
--auth-hero-border: color-mix(in srgb, var(--border-strong) 42%, transparent);
|
||||
--auth-hero-chip: color-mix(in srgb, var(--bg-surface) 62%, transparent);
|
||||
background:
|
||||
var(--auth-room-veil),
|
||||
var(--asset-login-room) center / cover no-repeat;
|
||||
|
|
@ -61,17 +61,17 @@
|
|||
--auth-room-veil:
|
||||
linear-gradient(
|
||||
90deg,
|
||||
rgba(7, 16, 14, 0.84) 0%,
|
||||
rgba(10, 21, 19, 0.72) 48%,
|
||||
rgba(10, 21, 19, 0.55) 74%,
|
||||
rgba(10, 21, 19, 0.62) 100%
|
||||
color-mix(in srgb, var(--bg-app) 84%, transparent) 0%,
|
||||
color-mix(in srgb, var(--bg-app) 72%, transparent) 48%,
|
||||
color-mix(in srgb, var(--bg-app) 55%, transparent) 74%,
|
||||
color-mix(in srgb, var(--bg-app) 62%, transparent) 100%
|
||||
);
|
||||
--auth-room-shade: linear-gradient(180deg, rgba(3, 9, 8, 0.18), rgba(3, 9, 8, 0.42));
|
||||
--auth-hero-text: #edf4f2;
|
||||
--auth-hero-muted: rgba(237, 244, 242, 0.72);
|
||||
--auth-hero-surface: rgba(237, 244, 242, 0.08);
|
||||
--auth-hero-border: rgba(255, 255, 255, 0.14);
|
||||
--auth-hero-chip: rgba(255, 255, 255, 0.08);
|
||||
--auth-room-shade: linear-gradient(180deg, color-mix(in srgb, var(--bg-app) 18%, transparent), color-mix(in srgb, var(--bg-app) 42%, transparent));
|
||||
--auth-hero-text: var(--text-strong);
|
||||
--auth-hero-muted: color-mix(in srgb, var(--text-strong) 72%, transparent);
|
||||
--auth-hero-surface: color-mix(in srgb, var(--text-strong) 8%, transparent);
|
||||
--auth-hero-border: color-mix(in srgb, var(--border-subtle) 80%, transparent);
|
||||
--auth-hero-chip: color-mix(in srgb, var(--text-strong) 8%, transparent);
|
||||
}
|
||||
|
||||
@media (max-width: 620px) {
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@
|
|||
position:relative;width:100%;border-radius:50%;
|
||||
display:flex;align-items:center;justify-content:center;overflow:hidden;
|
||||
background:
|
||||
radial-gradient(circle at 50% 38%, rgba(251,250,248,.98) 0%, rgba(246,240,236,.9) 42%, rgba(238,244,242,.76) 64%, rgba(145,200,189,.22) 84%, rgba(30,39,36,.18) 100%);
|
||||
radial-gradient(circle at 50% 38%, color-mix(in srgb, var(--bg-surface) 98%, transparent) 0%, color-mix(in srgb, var(--bg-surface-2) 90%, transparent) 42%, color-mix(in srgb, var(--accent-tint) 76%, transparent) 64%, color-mix(in srgb, var(--accent) 22%, transparent) 84%, color-mix(in srgb, var(--text-strong) 18%, transparent) 100%);
|
||||
box-shadow:
|
||||
inset 0 0 0 1px rgba(251,250,248,.28),
|
||||
inset 0 -24px 40px rgba(28,42,42,.13);
|
||||
inset 0 0 0 1px color-mix(in srgb, var(--bg-surface) 28%, transparent),
|
||||
inset 0 -24px 40px color-mix(in srgb, var(--text-strong) 13%, transparent);
|
||||
}
|
||||
.vg-avatar__aura{
|
||||
position:absolute;inset:-12%;border-radius:50%;pointer-events:none;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useEffect, useLayoutEffect, useRef, type ReactNode } from "react";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import { useLocation, useNavigate } from "react-router-dom";
|
||||
import { Topbar } from "./Topbar";
|
||||
import { Sidebar } from "./Sidebar";
|
||||
import { canAccessRole, designRoleOf, useAuth, type Role } from "../../lib/auth";
|
||||
|
|
@ -78,6 +78,79 @@ export function AppShell({ children, className, contextLabel, hideNav, bleed, wi
|
|||
return () => window.removeEventListener("pageshow", handlePageShow);
|
||||
}, []);
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
// ── 전역 단축키 핸들러 ──
|
||||
useEffect(() => {
|
||||
const handleGlobalKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.repeat) return;
|
||||
const target = e.target instanceof HTMLElement ? e.target : null;
|
||||
const isInput = target?.closest("input, textarea, select, [contenteditable='true']");
|
||||
|
||||
const isAlt = e.altKey && !e.ctrlKey && !e.metaKey;
|
||||
const isCtrl = (e.ctrlKey || e.metaKey) && !e.altKey;
|
||||
|
||||
// Ctrl+W 또는 Escape: 활성 모달 닫기
|
||||
if ((isCtrl && e.key.toLowerCase() === "w") || e.key === "Escape") {
|
||||
const openModalCloseBtn = document.querySelector<HTMLButtonElement>(
|
||||
"[role='dialog'] button:has(svg), [role='dialog'] .sx-panel-close-btn, .vg-dialog__close, [role='dialog'] button[aria-label*='닫기']"
|
||||
);
|
||||
if (openModalCloseBtn) {
|
||||
e.preventDefault();
|
||||
openModalCloseBtn.click();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Alt+H: 홈으로 이동
|
||||
if (isAlt && e.key.toLowerCase() === "h") {
|
||||
e.preventDefault();
|
||||
const homePath = user?.role === "admin" ? "/admin" : user?.role === "teacher" ? "/professor" : "/learn";
|
||||
navigate(homePath);
|
||||
return;
|
||||
}
|
||||
|
||||
// Alt+N 또는 Ctrl+N 또는 Ctrl+T (입력창 밖): 새 회기 시작 / 페르소나 선택
|
||||
if ((isAlt && e.key.toLowerCase() === "n") || (!isInput && isCtrl && (e.key.toLowerCase() === "n" || e.key.toLowerCase() === "t"))) {
|
||||
e.preventDefault();
|
||||
navigate("/learn");
|
||||
return;
|
||||
}
|
||||
|
||||
// Alt+K: 학습 기록으로 이동
|
||||
if (isAlt && e.key.toLowerCase() === "k") {
|
||||
e.preventDefault();
|
||||
navigate("/learn/history");
|
||||
return;
|
||||
}
|
||||
|
||||
// Alt+S: 설정 화면으로 이동
|
||||
if (isAlt && e.key.toLowerCase() === "s") {
|
||||
e.preventDefault();
|
||||
navigate("/settings");
|
||||
return;
|
||||
}
|
||||
|
||||
// Alt+T: 다크/라이트 테마 전환
|
||||
if (isAlt && e.key.toLowerCase() === "t") {
|
||||
e.preventDefault();
|
||||
const themeBtn = document.querySelector<HTMLButtonElement>(".vg-topbar__theme-btn, [data-testid='theme-toggle']");
|
||||
if (themeBtn) {
|
||||
themeBtn.click();
|
||||
} else {
|
||||
const currentTheme = document.documentElement.getAttribute("data-theme") || "light";
|
||||
const nextTheme = currentTheme === "dark" ? "light" : "dark";
|
||||
document.documentElement.setAttribute("data-theme", nextTheme);
|
||||
localStorage.setItem("vignette_theme", nextTheme);
|
||||
}
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("keydown", handleGlobalKeyDown);
|
||||
return () => window.removeEventListener("keydown", handleGlobalKeyDown);
|
||||
}, [navigate, user]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={[
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ export function Sidebar({ role, showAdminEntry = false, groupLabel = "메뉴" }:
|
|||
<span className="vg-nav__ic">
|
||||
<Icon name={item.icon} size={18} strokeWidth={1.75} />
|
||||
</span>
|
||||
<span>{item.label}</span>
|
||||
<span className="vg-nav__txt">{item.label}</span>
|
||||
</NavLink>
|
||||
))}
|
||||
|
||||
|
|
|
|||
|
|
@ -104,6 +104,10 @@
|
|||
font-weight: 650;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
transition: transform var(--dur-fast) var(--ease-spring), background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
|
||||
}
|
||||
.vg-topbar__switch-link:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
.vg-topbar__switch-link:hover,
|
||||
.vg-topbar__switch-link.is-active {
|
||||
|
|
@ -125,9 +129,13 @@
|
|||
color: var(--text-body);
|
||||
cursor: pointer;
|
||||
transition:
|
||||
transform var(--dur-fast) var(--ease-spring),
|
||||
background var(--dur-fast) var(--ease-out),
|
||||
color var(--dur-fast) var(--ease-out);
|
||||
}
|
||||
.vg-iconbtn:active {
|
||||
transform: scale(0.92);
|
||||
}
|
||||
.vg-iconbtn:hover {
|
||||
background: var(--bg-surface-2);
|
||||
color: var(--text-strong);
|
||||
|
|
@ -324,33 +332,46 @@
|
|||
}
|
||||
@media (max-width: 720px) {
|
||||
html {
|
||||
scroll-padding-top: calc(var(--topbar-h) + 68px);
|
||||
scroll-padding-bottom: var(--sp-6);
|
||||
scroll-padding-top: calc(var(--topbar-h) + var(--sp-2));
|
||||
scroll-padding-bottom: calc(var(--bottom-bar-h) + env(safe-area-inset-bottom) + var(--sp-4));
|
||||
}
|
||||
.vg-shell {
|
||||
height: 100dvh;
|
||||
}
|
||||
.vg-shell__body {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
grid-template-rows: 58px minmax(0, 1fr);
|
||||
background-image: none; /* 네비가 상단 가로바로 전환 — 세로 구분선 제거 */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
position: relative;
|
||||
background-image: none; /* 네비가 하단바로 전환 — 세로 구분선 제거 */
|
||||
}
|
||||
.vg-shell__body--bare {
|
||||
display: block;
|
||||
display: flex;
|
||||
}
|
||||
.vg-nav {
|
||||
position: relative;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: auto;
|
||||
z-index: 29;
|
||||
height: 58px;
|
||||
padding: 7px max(12px, env(safe-area-inset-left)) 7px max(12px, env(safe-area-inset-right));
|
||||
z-index: 40;
|
||||
height: calc(var(--bottom-bar-h) + env(safe-area-inset-bottom));
|
||||
padding: 6px max(10px, env(safe-area-inset-left)) max(6px, env(safe-area-inset-bottom)) max(10px, env(safe-area-inset-right));
|
||||
border-right: 0;
|
||||
border-top: 0;
|
||||
border-bottom: 1px solid var(--hair);
|
||||
border-top: 1px solid var(--hair);
|
||||
border-bottom: 0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: 8px;
|
||||
justify-content: space-around;
|
||||
gap: 4px;
|
||||
background: var(--bg-surface);
|
||||
box-shadow: var(--shadow-island);
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
-webkit-backdrop-filter: var(--glass-blur);
|
||||
backdrop-filter: var(--glass-blur);
|
||||
}
|
||||
.vg-nav__label,
|
||||
.vg-nav__spacer,
|
||||
|
|
@ -358,16 +379,43 @@
|
|||
display: none;
|
||||
}
|
||||
.vg-nav__item {
|
||||
min-width: 112px;
|
||||
height: 44px;
|
||||
flex: 1 1 0;
|
||||
min-width: 0;
|
||||
max-width: 96px;
|
||||
height: 48px;
|
||||
min-height: 48px;
|
||||
padding: 3px 2px;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
padding: 0 14px;
|
||||
gap: 8px;
|
||||
white-space: nowrap;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
border-radius: var(--radius);
|
||||
text-align: center;
|
||||
transition: transform var(--dur-fast) var(--ease-spring), background var(--dur-fast) var(--ease-out);
|
||||
}
|
||||
.vg-nav__item:active {
|
||||
transform: scale(0.93);
|
||||
}
|
||||
.vg-nav__item .vg-nav__ic {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.vg-nav__item .vg-nav__ic svg {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
.vg-nav__item .vg-nav__txt,
|
||||
.vg-nav__item span:not(.vg-nav__ic) {
|
||||
display: inline;
|
||||
font-size: 13px;
|
||||
display: block;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
line-height: 1.2;
|
||||
letter-spacing: -0.01em;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.vg-topbar__role {
|
||||
display: none;
|
||||
|
|
@ -376,50 +424,47 @@
|
|||
display: none;
|
||||
}
|
||||
.vg-topbar {
|
||||
padding: 0 4px;
|
||||
gap: 4px;
|
||||
height: 50px;
|
||||
min-height: 50px;
|
||||
padding: 0 var(--sp-3);
|
||||
gap: var(--sp-2);
|
||||
}
|
||||
.vg-topbar__brand {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
flex: 0 0 44px;
|
||||
justify-content: center;
|
||||
min-height: 44px;
|
||||
gap: 8px;
|
||||
}
|
||||
.vg-topbar__mark {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
.vg-topbar__wm {
|
||||
font-size: 16px;
|
||||
}
|
||||
.vg-topbar__wm,
|
||||
.vg-topbar__user {
|
||||
display: none;
|
||||
}
|
||||
.vg-topbar__actions {
|
||||
flex: 1 1 auto;
|
||||
flex: 0 0 auto;
|
||||
gap: 4px;
|
||||
}
|
||||
.vg-topbar__switch {
|
||||
flex: 1 1 auto;
|
||||
height: 44px;
|
||||
padding: 0;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
.vg-topbar__switch::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.vg-topbar__switch-link span {
|
||||
display: none;
|
||||
height: 38px;
|
||||
padding: 2px;
|
||||
}
|
||||
.vg-topbar__switch-link {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
flex: 0 0 44px;
|
||||
padding: 0;
|
||||
min-height: 34px;
|
||||
padding: 0 8px;
|
||||
font-size: 11.5px;
|
||||
}
|
||||
.vg-topbar__switch-link span {
|
||||
display: inline;
|
||||
}
|
||||
.vg-iconbtn {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
flex: 0 0 44px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
.vg-main {
|
||||
padding: var(--sp-5) var(--sp-4) var(--sp-7);
|
||||
padding: var(--sp-4) var(--sp-4) calc(var(--bottom-bar-h) + env(safe-area-inset-bottom) + var(--sp-6));
|
||||
}
|
||||
.vg-main--bleed {
|
||||
padding: 0;
|
||||
|
|
@ -584,9 +629,8 @@ body[data-role] .vg-shell--botanical .vg-nav__foot {
|
|||
|
||||
@media (max-width: 720px) {
|
||||
body[data-role] .vg-shell--botanical .vg-shell__body:not(.vg-shell__body--bare) {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
grid-template-rows: 58px minmax(0, 1fr);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-image:
|
||||
radial-gradient(circle at 74% 12%, color-mix(in srgb, var(--accent) 14%, transparent), transparent 22rem),
|
||||
radial-gradient(circle at 18% 82%, color-mix(in srgb, var(--botanical-gold) 14%, transparent), transparent 28rem),
|
||||
|
|
@ -596,46 +640,88 @@ body[data-role] .vg-shell--botanical .vg-nav__foot {
|
|||
background-size: 100% 34rem, 100% 38rem, 430px auto;
|
||||
}
|
||||
body[data-role] .vg-shell--botanical .vg-nav {
|
||||
position: relative;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: auto;
|
||||
z-index: 29;
|
||||
height: 58px;
|
||||
padding: 7px max(12px, env(safe-area-inset-left)) 7px max(12px, env(safe-area-inset-right));
|
||||
border-bottom: 1px solid var(--botanical-hair);
|
||||
background: color-mix(in srgb, var(--bg-surface) 72%, transparent);
|
||||
z-index: 40;
|
||||
height: calc(var(--bottom-bar-h) + env(safe-area-inset-bottom));
|
||||
padding: 6px max(10px, env(safe-area-inset-left)) max(6px, env(safe-area-inset-bottom)) max(10px, env(safe-area-inset-right));
|
||||
border-top: 1px solid var(--botanical-hair);
|
||||
border-bottom: 0;
|
||||
border-right: 0;
|
||||
border-left: 0;
|
||||
background: color-mix(in srgb, var(--bg-surface) 86%, transparent);
|
||||
box-shadow: var(--shadow-island);
|
||||
-webkit-backdrop-filter: var(--glass-blur);
|
||||
backdrop-filter: var(--glass-blur);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
gap: 4px;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
body[data-role] .vg-shell--botanical .vg-nav__item,
|
||||
body[data-role] .vg-shell--botanical .vg-nav__item.is-active {
|
||||
min-width: 112px;
|
||||
height: 44px;
|
||||
padding: 0 14px;
|
||||
gap: 8px;
|
||||
flex: 1 1 0;
|
||||
min-width: 0;
|
||||
max-width: 96px;
|
||||
height: 48px;
|
||||
min-height: 48px;
|
||||
padding: 3px 2px;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
border-radius: var(--radius);
|
||||
text-align: center;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
body[data-role] .vg-shell--botanical .vg-nav__item.is-active {
|
||||
padding-right: 2px;
|
||||
background: linear-gradient(
|
||||
112deg,
|
||||
color-mix(in srgb, var(--bg-surface) 92%, var(--botanical-gold)),
|
||||
color-mix(in srgb, var(--bg-surface-2) 80%, transparent)
|
||||
);
|
||||
border-color: color-mix(in srgb, var(--botanical-gold) 34%, var(--border-subtle));
|
||||
box-shadow: inset 0 1px 0 color-mix(in srgb, var(--bg-surface) 84%, transparent);
|
||||
}
|
||||
body[data-role] .vg-shell--botanical .vg-nav__item.is-active::after {
|
||||
display: none;
|
||||
}
|
||||
body[data-role] .vg-shell--botanical .vg-nav__item .vg-nav__ic svg {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
body[data-role] .vg-shell--botanical .vg-nav__item .vg-nav__txt,
|
||||
body[data-role] .vg-shell--botanical .vg-nav__item span:not(.vg-nav__ic) {
|
||||
display: block;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
line-height: 1.2;
|
||||
letter-spacing: -0.01em;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* 학습자는 네 항목이 고정이므로 숨은 가로 탐색보다 한 화면 4등분이 예측 가능하다.
|
||||
교수자·관리자처럼 항목 수가 많은 역할은 기존 가로 스크롤 계약을 유지한다. */
|
||||
body[data-role="learner"] .vg-shell--botanical .vg-nav {
|
||||
display:grid;
|
||||
grid-template-columns:repeat(4,minmax(0,1fr));
|
||||
gap:4px;
|
||||
overflow-x:visible;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 4px;
|
||||
overflow-x: visible;
|
||||
}
|
||||
body[data-role="learner"] .vg-shell--botanical .vg-nav__item,
|
||||
body[data-role="learner"] .vg-shell--botanical .vg-nav__item.is-active {
|
||||
width:100%;
|
||||
min-width:0;
|
||||
height:44px;
|
||||
padding:0 2px;
|
||||
gap:3px;
|
||||
font-size:12px;
|
||||
}
|
||||
body[data-role="learner"] .vg-shell--botanical .vg-nav__item span:not(.vg-nav__ic) {
|
||||
font-size:12px;
|
||||
}
|
||||
body[data-role="learner"] .vg-shell--botanical .vg-nav__ic svg {
|
||||
width:16px;
|
||||
height:16px;
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
min-width: 0;
|
||||
height: 48px;
|
||||
padding: 3px 2px;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ export type IconName =
|
|||
| "school"
|
||||
| "sun"
|
||||
| "moon"
|
||||
| "copy"
|
||||
| "refresh";
|
||||
|
||||
export interface IconProps extends Omit<SVGProps<SVGSVGElement>, "name"> {
|
||||
|
|
@ -227,6 +228,12 @@ const PATHS: Record<IconName, ReactNode> = {
|
|||
</>
|
||||
),
|
||||
moon: <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />,
|
||||
copy: (
|
||||
<>
|
||||
<rect width="14" height="14" x="8" y="8" rx="2" ry="2" />
|
||||
<path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" />
|
||||
</>
|
||||
),
|
||||
};
|
||||
|
||||
export function Icon({
|
||||
|
|
|
|||
|
|
@ -57,13 +57,14 @@
|
|||
transition:
|
||||
background var(--dur-base) var(--ease-out),
|
||||
border-color var(--dur-base) var(--ease-out),
|
||||
transform var(--dur-fast) var(--ease-out);
|
||||
transform var(--dur-fast) var(--ease-spring),
|
||||
box-shadow var(--dur-fast) var(--ease-out);
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
user-select: none;
|
||||
}
|
||||
.vg-btn:active {
|
||||
transform: translateY(0.5px);
|
||||
transform: translateY(1px) scale(0.98);
|
||||
}
|
||||
.vg-btn:disabled {
|
||||
opacity: 0.5;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue