refactor(console-web): centralize page lifecycle and UI composition
Declare navigation, page metadata, lazy loaders, and keep-alive policy in one registry. Retain primary iframe pages until a continuous inactive TTL expires and expose typed show, hide, and unload events through the Page SDK. Move business-aware components out of the UI primitive directory, migrate views and Host dialogs to variant primitives and local Tailwind classes, and remove the global component style layer. Keep native dialogs inside the overlay flex context so they remain centered. Cover iframe expiration, lifecycle dispatch, UI dependency direction, and global style ownership with focused tests.
This commit is contained in:
@@ -64,273 +64,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
.btn {
|
||||
@apply inline-flex h-10 items-center justify-center gap-2 rounded-md border px-4 text-sm font-semibold transition-colors disabled:cursor-not-allowed disabled:opacity-45;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
@apply border-brand bg-brand text-white hover:border-brand-strong hover:bg-brand-strong;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
@apply border-line bg-white text-ink hover:bg-canvas;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
@apply border-coral-strong bg-white text-coral-strong hover:bg-coral-soft;
|
||||
}
|
||||
|
||||
.icon-btn {
|
||||
@apply inline-flex size-9 shrink-0 items-center justify-center rounded-md border border-line bg-white text-muted transition-colors hover:bg-canvas hover:text-ink disabled:cursor-not-allowed disabled:opacity-45;
|
||||
}
|
||||
|
||||
.panel {
|
||||
@apply border-line bg-surface rounded-md border;
|
||||
}
|
||||
|
||||
.panel-header {
|
||||
@apply border-line flex min-h-16 items-center justify-between gap-4 border-b px-5 py-3;
|
||||
}
|
||||
|
||||
.panel-header h2 {
|
||||
@apply text-base font-semibold;
|
||||
}
|
||||
|
||||
.panel-header p {
|
||||
@apply mt-0.5 text-xs text-muted;
|
||||
}
|
||||
|
||||
.page {
|
||||
@apply mx-auto w-full max-w-[1480px] px-4 py-5 sm:px-6 lg:px-8 lg:py-7;
|
||||
}
|
||||
|
||||
.page-heading {
|
||||
@apply mb-5 flex flex-col justify-between gap-4 sm:flex-row sm:items-end;
|
||||
}
|
||||
|
||||
.page-heading h1 {
|
||||
@apply mt-1 text-2xl font-bold text-ink;
|
||||
}
|
||||
|
||||
.page-heading p {
|
||||
@apply mt-1 max-w-3xl text-sm text-muted;
|
||||
}
|
||||
|
||||
.eyebrow {
|
||||
@apply text-cyan-strong text-[11px] font-bold uppercase;
|
||||
}
|
||||
|
||||
.heading-actions {
|
||||
@apply flex shrink-0 items-center gap-2;
|
||||
}
|
||||
|
||||
.metric-grid {
|
||||
@apply mb-5 grid grid-cols-1 border-l border-t border-line sm:grid-cols-2 xl:grid-cols-4;
|
||||
}
|
||||
|
||||
.metric {
|
||||
@apply flex min-h-24 items-center gap-3 border-b border-r border-line bg-white px-4 py-3;
|
||||
}
|
||||
|
||||
.metric-icon {
|
||||
@apply flex size-10 shrink-0 items-center justify-center rounded-md;
|
||||
}
|
||||
|
||||
.metric-copy {
|
||||
@apply min-w-0 flex-1;
|
||||
}
|
||||
|
||||
.metric-copy span {
|
||||
@apply block text-xs text-muted;
|
||||
}
|
||||
|
||||
.metric-copy strong {
|
||||
@apply mt-1 block text-2xl font-bold;
|
||||
}
|
||||
|
||||
.metric > small {
|
||||
@apply self-end whitespace-nowrap pb-1 text-[11px] text-muted;
|
||||
}
|
||||
|
||||
.table-wrap {
|
||||
@apply w-full overflow-x-auto;
|
||||
}
|
||||
|
||||
.data-table {
|
||||
@apply w-full min-w-[760px] border-collapse text-left text-sm;
|
||||
}
|
||||
|
||||
.data-table th {
|
||||
@apply border-line bg-canvas border-b px-4 py-2.5 text-[11px] font-semibold uppercase text-muted;
|
||||
}
|
||||
|
||||
.data-table td {
|
||||
@apply border-line border-b px-4 py-3 align-middle;
|
||||
}
|
||||
|
||||
.data-table tbody tr {
|
||||
@apply transition-colors hover:bg-[#f8faf9];
|
||||
}
|
||||
|
||||
.data-table tbody tr:last-child td {
|
||||
@apply border-b-0;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
@apply inline-flex items-center gap-1.5 whitespace-nowrap text-xs font-semibold;
|
||||
}
|
||||
|
||||
.status-badge::before {
|
||||
content: "";
|
||||
@apply size-1.5 rounded-full bg-current;
|
||||
}
|
||||
|
||||
.status-running {
|
||||
@apply text-brand;
|
||||
}
|
||||
|
||||
.status-stopped {
|
||||
@apply text-muted;
|
||||
}
|
||||
|
||||
.status-faulted {
|
||||
@apply text-coral-strong;
|
||||
}
|
||||
|
||||
.row-actions {
|
||||
@apply flex items-center justify-end gap-1;
|
||||
}
|
||||
|
||||
.row-actions button {
|
||||
@apply inline-flex size-8 items-center justify-center rounded-md text-muted hover:bg-canvas hover:text-ink disabled:cursor-not-allowed disabled:opacity-35;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
@apply flex min-h-56 flex-col items-center justify-center gap-2 px-6 text-center text-muted;
|
||||
}
|
||||
|
||||
.empty-state strong {
|
||||
@apply text-sm text-ink;
|
||||
}
|
||||
|
||||
.empty-state span {
|
||||
@apply text-xs;
|
||||
}
|
||||
|
||||
.code {
|
||||
@apply font-mono text-xs;
|
||||
}
|
||||
|
||||
.field {
|
||||
@apply block;
|
||||
}
|
||||
|
||||
.field > span {
|
||||
@apply mb-1.5 block text-xs font-semibold text-muted;
|
||||
}
|
||||
|
||||
.input,
|
||||
.select,
|
||||
.textarea {
|
||||
@apply border-line w-full rounded-md border bg-white px-3 text-sm text-ink outline-none transition-colors focus:border-brand;
|
||||
}
|
||||
|
||||
.input,
|
||||
.select {
|
||||
@apply h-10;
|
||||
}
|
||||
|
||||
.textarea {
|
||||
@apply min-h-36 resize-y py-3 font-mono;
|
||||
}
|
||||
|
||||
.dialog-backdrop {
|
||||
@apply fixed inset-0 z-50 flex items-center justify-center bg-[#0d1715]/55 p-3 backdrop-blur-[1px];
|
||||
}
|
||||
|
||||
.dialog {
|
||||
@apply border-line max-h-[calc(100dvh-24px)] w-full max-w-3xl overflow-auto rounded-md border bg-white p-0 shadow-2xl;
|
||||
}
|
||||
|
||||
.dialog-header {
|
||||
@apply border-line flex items-start justify-between gap-4 border-b px-5 py-4 sm:px-7 sm:py-5;
|
||||
}
|
||||
|
||||
.dialog-title {
|
||||
@apply flex min-w-0 items-start gap-3;
|
||||
}
|
||||
|
||||
.dialog-title > span {
|
||||
@apply bg-brand-soft text-brand flex size-10 shrink-0 items-center justify-center rounded-md;
|
||||
}
|
||||
|
||||
.dialog-title h2 {
|
||||
@apply text-lg font-bold sm:text-xl;
|
||||
}
|
||||
|
||||
.dialog-title p {
|
||||
@apply mt-1 text-sm text-muted;
|
||||
}
|
||||
|
||||
.dialog-body {
|
||||
@apply px-5 py-5 sm:px-7;
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
@apply border-line flex items-center justify-end gap-2 border-t bg-[#fafbfb] px-5 py-4 sm:px-7;
|
||||
}
|
||||
|
||||
.upload-zone {
|
||||
@apply border-line flex min-h-48 cursor-pointer flex-col items-center justify-center gap-2 rounded-md border border-dashed bg-[#fbfcfc] px-5 text-center text-muted transition-colors hover:border-brand hover:bg-brand-soft;
|
||||
}
|
||||
|
||||
.upload-zone input {
|
||||
@apply sr-only;
|
||||
}
|
||||
|
||||
.upload-zone strong {
|
||||
@apply text-sm text-ink;
|
||||
}
|
||||
|
||||
.upload-zone span {
|
||||
@apply text-xs;
|
||||
}
|
||||
|
||||
.form-error {
|
||||
@apply border-coral-strong/30 bg-coral-soft text-coral-strong mt-3 rounded-md border px-3 py-2 text-sm;
|
||||
}
|
||||
|
||||
.segmented {
|
||||
@apply border-line inline-flex rounded-md border bg-canvas p-1;
|
||||
}
|
||||
|
||||
.segmented button {
|
||||
@apply h-8 rounded-sm px-3 text-xs font-semibold text-muted;
|
||||
}
|
||||
|
||||
.segmented button.active {
|
||||
@apply bg-white text-ink shadow-sm;
|
||||
}
|
||||
|
||||
.skeleton-line {
|
||||
@apply h-3 animate-pulse rounded-sm bg-[#dfe6e4];
|
||||
}
|
||||
|
||||
.loading-progress-track {
|
||||
@apply h-1 w-40 overflow-hidden rounded-sm bg-line;
|
||||
}
|
||||
|
||||
.loading-progress-indicator {
|
||||
@apply block h-full w-[36%] rounded-sm bg-brand;
|
||||
animation: loading-progress-swing 1s ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
.spin {
|
||||
animation: spin 0.8s linear infinite;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes loading-progress-swing {
|
||||
from {
|
||||
transform: translateX(0);
|
||||
@@ -340,16 +73,3 @@
|
||||
transform: translateX(178%);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.loading-progress-indicator {
|
||||
animation: none;
|
||||
transform: translateX(89%);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user