Files
C.E.L_Slide_test2/samples/src/components/SidebarToggle.astro

191 lines
5.0 KiB
Plaintext

---
const labelOpen = "사이드바 접기";
const labelClosed = "사이드바 펼치기";
---
<button
type="button"
class="sidebar-toggle"
data-sidebar-toggle
data-label-open={labelOpen}
data-label-closed={labelClosed}
aria-pressed="false"
>
<span class="toggle-icon" aria-hidden="true">⟫</span>
</button>
<script>
const root = document.documentElement;
const toggleBtn = document.querySelector("[data-sidebar-toggle]");
const collapsedClass = "sidebar-collapsed";
const storageKey = "cel:sidebar-collapsed";
const getDefaultCollapsed = () => {
const width = window.innerWidth;
if (width < 800) return true;
if (width < 1000) return true;
return false;
};
let savedState = null;
const applyState = (isCollapsed, { persist = true } = {}) => {
root.classList.toggle(collapsedClass, isCollapsed);
if (!toggleBtn) return;
const label = isCollapsed ? toggleBtn.dataset.labelClosed ?? "사이드바 펼치기" : toggleBtn.dataset.labelOpen ?? "사이드바 접기";
const icon = toggleBtn.querySelector(".toggle-icon");
toggleBtn.setAttribute("aria-pressed", isCollapsed ? "true" : "false");
toggleBtn.setAttribute("aria-label", label);
if (icon) icon.textContent = isCollapsed ? "⟫" : "⟪";
if (persist) {
savedState = isCollapsed ? "1" : "0";
try {
window.sessionStorage.setItem(storageKey, isCollapsed ? "1" : "0");
} catch (error) {
console.warn("Failed to persist sidebar toggle state", error);
}
}
};
const getToggleTop = () => {
const header = document.querySelector("header.header") ?? document.querySelector("header");
const headerRect = header?.getBoundingClientRect();
if (headerRect) return headerRect.bottom + 14;
return 22;
};
const setTogglePosition = () => {
if (!toggleBtn) return;
const isCollapsed = root.classList.contains(collapsedClass);
const sidebarPane = document.getElementById("starlight__sidebar");
const rect = sidebarPane?.getBoundingClientRect();
const sidebarWidth = parseFloat(getComputedStyle(root).getPropertyValue("--sl-sidebar-width") || "0") || 0;
const fallbackLeft = sidebarWidth > 0 ? sidebarWidth : 12;
const anchorLeft = rect && rect.width > 0 ? rect.right : fallbackLeft;
const top = Math.max(12, getToggleTop());
toggleBtn.style.left = `${anchorLeft}px`;
toggleBtn.style.top = `${top}px`;
toggleBtn.style.transform = "translate(-90%, 0)";
};
if (toggleBtn) {
document.body.appendChild(toggleBtn);
try {
savedState = window.sessionStorage.getItem(storageKey);
} catch (error) {
console.warn("Failed to read sidebar toggle state", error);
}
const resolveState = () => {
const width = window.innerWidth;
if (width < 800) return true;
if (savedState !== null) return savedState === "1";
return getDefaultCollapsed();
};
const initialCollapsed = resolveState();
applyState(initialCollapsed, { persist: savedState !== null });
setTogglePosition();
toggleBtn.addEventListener("click", () => {
const nextState = !root.classList.contains(collapsedClass);
applyState(nextState, { persist: true });
setTogglePosition();
});
window.addEventListener("resize", () => {
window.requestAnimationFrame(() => {
const nextState = resolveState();
const shouldPersist = savedState !== null && window.innerWidth >= 800;
applyState(nextState, { persist: shouldPersist });
setTogglePosition();
});
});
}
</script>
<style>
@layer starlight.core {
.sidebar-toggle {
position: fixed;
top: auto;
left: 12px;
transform: translate(-90%);
z-index: calc(var(--sl-z-index-navbar) + 8);
display: none;
align-items: center;
justify-content: center;
width: 30px;
height: 40px;
padding: 0;
background: var(--sl-color-bg-sidebar);
border: 1px solid var(--sl-color-hairline);
border-right: 0;
border-radius: 0;
color: color-mix(in srgb, var(--sl-color-hairline-shade) 85%, var(--sl-color-text) 15%);
font-size: 0.95rem;
font-weight: 600;
cursor: pointer;
transition: transform 0.15s ease, background-color 0.2s ease, border-color 0.2s ease;
}
.sidebar-toggle:hover {
transform: translate(-90%, -1px);
background: var(--sl-color-bg);
border-color: var(--sl-color-hairline-shade);
}
.sidebar-toggle::before {
content: "";
position: absolute;
top: 0;
left: auto;
right: 0;
width: 1px;
height: 100%;
background: var(--sl-color-hairline);
}
.sidebar-toggle:hover::before {
background: var(--sl-color-hairline-shade);
}
@media (min-width: 50rem) {
.sidebar-toggle {
display: inline-flex;
}
:global(.sidebar-collapsed [data-has-sidebar]) {
--sl-content-inline-start: 0 !important;
}
:global(.sidebar-collapsed nav.sidebar) {
transform: translateX(-100%);
visibility: hidden;
pointer-events: none;
width: 0;
border: 0;
}
:global(.sidebar-collapsed .sidebar-pane) {
display: none;
}
:global(.sidebar-collapsed .main-frame) {
padding-inline-start: 0 !important;
}
:global(.sidebar-collapsed .main-pane) {
width: 100% !important;
max-width: 100% !important;
}
}
}
</style>