feat: enhance UI with responsive sidebar toggle and filter functionality for projects and scenarios
This commit is contained in:
@@ -52,8 +52,8 @@ body {
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', 'Roboto',
|
||||
Helvetica, Arial, 'Apple Color Emoji', 'Segoe UI Emoji';
|
||||
font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", "Roboto",
|
||||
Helvetica, Arial, "Apple Color Emoji", "Segoe UI Emoji";
|
||||
color: var(--text);
|
||||
background: linear-gradient(180deg, var(--bg) 0%, var(--bg-2) 100%);
|
||||
line-height: 1.45;
|
||||
@@ -337,7 +337,7 @@ a {
|
||||
gap: var(--space-sm);
|
||||
font-weight: 600;
|
||||
color: var(--text);
|
||||
font-family: 'Fira Code', 'Consolas', 'Courier New', monospace;
|
||||
font-family: "Fira Code", "Consolas", "Courier New", monospace;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
@@ -366,7 +366,7 @@ a {
|
||||
}
|
||||
|
||||
.color-value-input {
|
||||
font-family: 'Fira Code', 'Consolas', 'Courier New', monospace;
|
||||
font-family: "Fira Code", "Consolas", "Courier New", monospace;
|
||||
}
|
||||
|
||||
.color-value-input[disabled] {
|
||||
@@ -395,7 +395,7 @@ a {
|
||||
}
|
||||
|
||||
.env-overrides-table code {
|
||||
font-family: 'Fira Code', 'Consolas', 'Courier New', monospace;
|
||||
font-family: "Fira Code", "Consolas", "Courier New", monospace;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
@@ -550,7 +550,7 @@ a {
|
||||
}
|
||||
|
||||
.btn.is-loading::after {
|
||||
content: '';
|
||||
content: "";
|
||||
width: 0.85rem;
|
||||
height: 0.85rem;
|
||||
border: 2px solid rgba(255, 255, 255, 0.6);
|
||||
@@ -656,14 +656,14 @@ a {
|
||||
color: var(--color-surface-alt);
|
||||
padding: 1rem;
|
||||
border-radius: 8px;
|
||||
font-family: 'Fira Code', 'Consolas', 'Courier New', monospace;
|
||||
font-family: "Fira Code", "Consolas", "Courier New", monospace;
|
||||
overflow-x: auto;
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
.monospace-input {
|
||||
width: 100%;
|
||||
font-family: 'Fira Code', 'Consolas', 'Courier New', monospace;
|
||||
font-family: "Fira Code", "Consolas", "Courier New", monospace;
|
||||
min-height: 120px;
|
||||
}
|
||||
|
||||
@@ -740,6 +740,72 @@ tbody tr:nth-child(even) {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.sidebar-toggle {
|
||||
display: none;
|
||||
align-items: center;
|
||||
gap: 0.6rem;
|
||||
padding: 0.55rem 1rem;
|
||||
border-radius: 999px;
|
||||
border: none;
|
||||
background: linear-gradient(135deg, var(--brand-2), var(--brand));
|
||||
color: var(--color-text-dark);
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.25);
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.sidebar-toggle:hover,
|
||||
.sidebar-toggle:focus-visible {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.sidebar-toggle:focus-visible {
|
||||
outline: 2px solid rgba(255, 255, 255, 0.65);
|
||||
outline-offset: 3px;
|
||||
}
|
||||
|
||||
.sidebar-toggle-icon {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 18px;
|
||||
height: 2px;
|
||||
background-color: currentColor;
|
||||
}
|
||||
|
||||
.sidebar-toggle-icon::before,
|
||||
.sidebar-toggle-icon::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 18px;
|
||||
height: 2px;
|
||||
background-color: currentColor;
|
||||
}
|
||||
|
||||
.sidebar-toggle-icon::before {
|
||||
top: -6px;
|
||||
}
|
||||
|
||||
.sidebar-toggle-icon::after {
|
||||
top: 6px;
|
||||
}
|
||||
|
||||
.sidebar-toggle-label {
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.sidebar-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(7, 11, 17, 0.6);
|
||||
z-index: 800;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.25s ease;
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.app-sidebar {
|
||||
width: 240px;
|
||||
@@ -790,4 +856,39 @@ tbody tr:nth-child(even) {
|
||||
.dashboard-columns {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.sidebar-toggle {
|
||||
display: inline-flex;
|
||||
margin: 1rem auto 1.5rem;
|
||||
}
|
||||
|
||||
body.sidebar-collapsed .app-sidebar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body.sidebar-open {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body.sidebar-open .app-sidebar {
|
||||
display: block;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: min(320px, 82vw);
|
||||
height: 100vh;
|
||||
overflow-y: auto;
|
||||
z-index: 900;
|
||||
box-shadow: 0 12px 30px rgba(8, 14, 25, 0.4);
|
||||
}
|
||||
|
||||
body.sidebar-open .sidebar-overlay {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
body.sidebar-open .app-main {
|
||||
position: relative;
|
||||
z-index: 950;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,3 +76,86 @@
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 10px 24px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
.scenario-filters {
|
||||
display: grid;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.scenario-filters .filter-field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.35rem;
|
||||
}
|
||||
|
||||
.scenario-filters .filter-actions {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.scenario-filters input,
|
||||
.scenario-filters select {
|
||||
width: 100%;
|
||||
padding: 0.6rem 0.75rem;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid var(--color-border);
|
||||
background: rgba(8, 12, 19, 0.75);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.table-responsive {
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
border-radius: var(--table-radius);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.table-responsive .table {
|
||||
min-width: 640px;
|
||||
}
|
||||
|
||||
.table-responsive::-webkit-scrollbar {
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
.table-responsive::-webkit-scrollbar-thumb {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
@media (min-width: 720px) {
|
||||
.scenario-filters {
|
||||
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||
align-items: end;
|
||||
}
|
||||
|
||||
.scenario-filters .filter-actions {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.table-responsive .table {
|
||||
min-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.breadcrumb {
|
||||
flex-wrap: wrap;
|
||||
gap: 0.35rem;
|
||||
}
|
||||
|
||||
.table th,
|
||||
.table td {
|
||||
padding: 0.55rem 0.65rem;
|
||||
font-size: 0.9rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.table tbody tr {
|
||||
border-radius: var(--radius-sm);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const table = document.querySelector("[data-project-table]");
|
||||
if (!table) {
|
||||
return;
|
||||
}
|
||||
|
||||
const rows = Array.from(table.querySelectorAll("tbody tr"));
|
||||
const rows = table ? Array.from(table.querySelectorAll("tbody tr")) : [];
|
||||
const filterInput = document.querySelector("[data-project-filter]");
|
||||
|
||||
if (filterInput) {
|
||||
if (table && filterInput) {
|
||||
filterInput.addEventListener("input", () => {
|
||||
const query = filterInput.value.trim().toLowerCase();
|
||||
rows.forEach((row) => {
|
||||
@@ -16,4 +12,105 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const sidebar = document.querySelector(".app-sidebar");
|
||||
const appMain = document.querySelector(".app-main");
|
||||
if (!sidebar || !appMain) {
|
||||
return;
|
||||
}
|
||||
|
||||
const body = document.body;
|
||||
const mobileQuery = window.matchMedia("(max-width: 900px)");
|
||||
let toggleButton = document.querySelector("[data-sidebar-toggle]");
|
||||
|
||||
if (!toggleButton) {
|
||||
toggleButton = document.createElement("button");
|
||||
toggleButton.type = "button";
|
||||
toggleButton.className = "sidebar-toggle";
|
||||
toggleButton.setAttribute("data-sidebar-toggle", "");
|
||||
toggleButton.setAttribute("aria-expanded", "false");
|
||||
toggleButton.setAttribute("aria-label", "Toggle primary navigation");
|
||||
toggleButton.hidden = true;
|
||||
toggleButton.innerHTML = [
|
||||
'<span class="sidebar-toggle-icon" aria-hidden="true"></span>',
|
||||
'<span class="sidebar-toggle-label">Menu</span>',
|
||||
].join("");
|
||||
appMain.insertBefore(toggleButton, appMain.firstChild);
|
||||
}
|
||||
|
||||
let overlay = document.querySelector("[data-sidebar-overlay]");
|
||||
if (!overlay) {
|
||||
overlay = document.createElement("div");
|
||||
overlay.className = "sidebar-overlay";
|
||||
overlay.setAttribute("data-sidebar-overlay", "");
|
||||
overlay.setAttribute("aria-hidden", "true");
|
||||
document.body.appendChild(overlay);
|
||||
}
|
||||
|
||||
const primaryNav = document.querySelector(".sidebar-nav");
|
||||
if (primaryNav) {
|
||||
if (!primaryNav.id) {
|
||||
primaryNav.id = "primary-navigation";
|
||||
}
|
||||
toggleButton.setAttribute("aria-controls", primaryNav.id);
|
||||
}
|
||||
|
||||
const openSidebar = () => {
|
||||
body.classList.remove("sidebar-collapsed");
|
||||
body.classList.add("sidebar-open");
|
||||
toggleButton.setAttribute("aria-expanded", "true");
|
||||
overlay.setAttribute("aria-hidden", "false");
|
||||
};
|
||||
|
||||
const closeSidebar = (focusToggle = false) => {
|
||||
body.classList.add("sidebar-collapsed");
|
||||
body.classList.remove("sidebar-open");
|
||||
toggleButton.setAttribute("aria-expanded", "false");
|
||||
overlay.setAttribute("aria-hidden", "true");
|
||||
if (focusToggle) {
|
||||
toggleButton.focus({ preventScroll: true });
|
||||
}
|
||||
};
|
||||
|
||||
const toggleSidebar = () => {
|
||||
if (body.classList.contains("sidebar-open")) {
|
||||
closeSidebar();
|
||||
} else {
|
||||
openSidebar();
|
||||
sidebar.setAttribute("aria-hidden", "false");
|
||||
}
|
||||
};
|
||||
|
||||
const applyResponsiveState = (mql) => {
|
||||
if (!mql.matches) {
|
||||
toggleButton.hidden = true;
|
||||
body.classList.remove("sidebar-open", "sidebar-collapsed");
|
||||
sidebar.setAttribute("aria-hidden", "true");
|
||||
overlay.setAttribute("aria-hidden", "true");
|
||||
sidebar.removeAttribute("aria-hidden");
|
||||
return;
|
||||
}
|
||||
|
||||
toggleButton.hidden = false;
|
||||
if (!body.classList.contains("sidebar-open")) {
|
||||
body.classList.add("sidebar-collapsed");
|
||||
sidebar.setAttribute("aria-hidden", "true");
|
||||
}
|
||||
};
|
||||
|
||||
toggleButton.addEventListener("click", toggleSidebar);
|
||||
overlay.addEventListener("click", () => closeSidebar());
|
||||
|
||||
document.addEventListener("keydown", (event) => {
|
||||
if (event.key === "Escape" && body.classList.contains("sidebar-open")) {
|
||||
closeSidebar(true);
|
||||
}
|
||||
});
|
||||
|
||||
applyResponsiveState(mobileQuery);
|
||||
if (typeof mobileQuery.addEventListener === "function") {
|
||||
mobileQuery.addEventListener("change", applyResponsiveState);
|
||||
} else if (typeof mobileQuery.addListener === "function") {
|
||||
mobileQuery.addListener(applyResponsiveState);
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user