@fleetbase/ember-ui
Version:
Fleetbase UI provides all the interface components, helpers, services and utilities for building a Fleetbase extension into the Console.
366 lines (325 loc) • 11.6 kB
CSS
.widget-title,
.fleetbase-dashboard-grid .dashboard-title {
@apply flex flex-col lg:flex-row lg:items-center lg:justify-between rounded-lg border border-gray-200 bg-gray-50 dark:border-gray-700 dark:bg-gray-800 shadow-sm px-4 py-2 mb-4;
}
.widget-count,
.fleetbase-dashboard-grid .dashboard-component-count {
@apply flex flex-col items-center justify-center rounded-lg border border-gray-200 bg-gray-50 dark:border-gray-700 dark:bg-gray-800 shadow-sm py-4;
}
.col-span-2 {
grid-column: span 2 / span 2;
}
.col-span-12 {
grid-column: span 12 / span 12;
}
.col-span-full {
grid-column: 1 / -1;
}
.fleetbase-dashboard-grid--sticky {
position: sticky;
top: var(--fleetbase-dashboard-sticky-top, 0);
z-index: var(--fleetbase-dashboard-sticky-z-index, 20);
}
.fleetbase-dashboard-grid .btn-wrapper > .btn,
.fleetbase-dashboard-grid .ember-basic-dropdown-trigger .btn-wrapper > .btn {
height: 30px;
font-size: 0.875rem;
line-height: 1.25rem;
}
/*
* The widget picker overlay opts out of the global `.next-content-overlay-panel-body`
* px-1 by adding `class="no-padding"` to the body, and zeroes the inner wrapper's
* own padding here. Children of the inner wrapper (sticky header, scrollable list)
* own their padding so the sticky element actually stretches edge-to-edge.
*/
.next-content-overlay-panel-body-inner-wrapper.dashboard-widget-panel-body {
@apply px-0 py-0;
}
.dashboard-widget-card {
/* Two-line description with a flexible second line and sane fallback. */
container-type: inline-size;
}
.dashboard-widget-card .line-clamp-2 {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
/*
* Chart.js needs an explicitly sized parent for `responsive: true +
* maintainAspectRatio: false` to work. The shared <Chart> component wraps the
* canvas in <div class="ui-chart relative"> with no built-in size, so sparklines
* and embedded charts inside widgets render at Chart.js's default 300×150 canvas
* and visually overflow under the card.
*
* Fix at the widget level: any .ui-chart inside a fleet-ops widget fills its
* parent, and its canvas is absolutely positioned so Chart.js sizes against
* the wrapper's bounding box (the standard Chart.js responsive recipe).
*/
.fleet-ops-widget .ui-chart {
position: relative;
width: 100%;
height: 100%;
min-height: 0;
}
.fleet-ops-widget .ui-chart > canvas {
position: absolute;
inset: 0;
width: 100% ;
height: 100% ;
}
/*
* KPI tile sparkline gets a small, fixed slot so it never crowds the
* value/badge row above it.
*/
.fleet-ops-kpi-tile .kpi-tile-sparkline {
height: 36px;
flex-shrink: 0;
}
/* ============================================================
* KPI TILE — neon pop styling.
*
* Three accent variants (neutral / good / bad) driven by the cardAccentClass
* getter on the component. Each adds a subtle gradient backdrop tinted by the
* trend status, plus a coloured border so the dashboard reads at a glance.
* ============================================================ */
.fleet-ops-kpi-tile-pop {
/* Smooth lift/scale on hover for a touch of life. */
transition:
transform 180ms ease-out,
box-shadow 180ms ease-out,
border-color 180ms ease-out;
background-color: #ffffff;
border-color: #e5e7eb;
}
body[data-theme='dark'] .fleet-ops-kpi-tile-pop {
background-color: #1f2937;
border-color: #374151;
}
.fleet-ops-kpi-tile-pop:hover {
transform: translateY(-1px);
box-shadow: 0 6px 20px -6px rgba(15, 23, 42, 0.18);
}
body[data-theme='dark'] .fleet-ops-kpi-tile-pop:hover {
box-shadow: 0 6px 22px -4px rgba(2, 6, 23, 0.6);
}
/* ── good (positive trend) ── */
.fleet-ops-kpi-tile-pop.kpi-accent-good {
background-image: linear-gradient(135deg, rgba(16, 185, 129, 0.06) 0%, rgba(16, 185, 129, 0) 60%);
border-color: rgba(16, 185, 129, 0.35);
}
body[data-theme='dark'] .fleet-ops-kpi-tile-pop.kpi-accent-good {
background-image: linear-gradient(135deg, rgba(16, 185, 129, 0.12) 0%, rgba(16, 185, 129, 0) 60%);
border-color: rgba(16, 185, 129, 0.4);
}
/* ── bad (negative trend) ── */
.fleet-ops-kpi-tile-pop.kpi-accent-bad {
background-image: linear-gradient(135deg, rgba(244, 63, 94, 0.06) 0%, rgba(244, 63, 94, 0) 60%);
border-color: rgba(244, 63, 94, 0.35);
}
body[data-theme='dark'] .fleet-ops-kpi-tile-pop.kpi-accent-bad {
background-image: linear-gradient(135deg, rgba(244, 63, 94, 0.12) 0%, rgba(244, 63, 94, 0) 60%);
border-color: rgba(244, 63, 94, 0.4);
}
/* ── neutral (no delta / zero) ── */
.fleet-ops-kpi-tile-pop.kpi-accent-neutral {
background-image: linear-gradient(135deg, rgba(52, 133, 226, 0.05) 0%, rgba(52, 133, 226, 0) 65%);
}
body[data-theme='dark'] .fleet-ops-kpi-tile-pop.kpi-accent-neutral {
background-image: linear-gradient(135deg, rgba(52, 133, 226, 0.1) 0%, rgba(52, 133, 226, 0) 65%);
}
/* Value-row entrance: gentle fade-up as data arrives. */
.fleet-ops-kpi-tile-pop .kpi-tile-value-row {
animation: fleetops-kpi-pop-in 420ms cubic-bezier(0.16, 1, 0.3, 1) both;
}
@keyframes fleetops-kpi-pop-in {
0% {
opacity: 0;
transform: translateY(4px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
/* Subtle pulse for live/real-time tiles when their counter changes. */
.fleet-ops-kpi-tile-pop.kpi-live .kpi-tile-value-row {
animation: fleetops-kpi-pulse 1.2s ease-in-out;
}
@keyframes fleetops-kpi-pulse {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.55;
}
}
/* ============================================================
* Operations Pulse tiles — five colour-coded micro-cards.
*
* Each tile gets:
* - A vertical accent stripe along the left edge (3px) in the tile's hue.
* - A soft radial-corner gradient in that hue for ambient colour.
* - A tinted icon chip in the top-right.
* - A neon-hued value number that pops against the dark theme.
* ============================================================ */
.fleet-ops-pulse-tile {
background-color: #f9fafb;
border-color: #e5e7eb;
transition:
transform 160ms ease-out,
box-shadow 160ms ease-out;
}
body[data-theme='dark'] .fleet-ops-pulse-tile {
background-color: #1f2937;
border-color: #374151;
}
.fleet-ops-pulse-tile:hover {
transform: translateY(-1px);
box-shadow: 0 4px 14px -4px rgba(15, 23, 42, 0.16);
}
body[data-theme='dark'] .fleet-ops-pulse-tile:hover {
box-shadow: 0 4px 16px -4px rgba(2, 6, 23, 0.55);
}
.fleet-ops-pulse-stripe {
width: 3px;
pointer-events: none;
}
.fleet-ops-pulse-icon {
width: 18px;
height: 18px;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 4px;
}
.fleet-ops-pulse-value-row {
animation: fleetops-kpi-pop-in 420ms cubic-bezier(0.16, 1, 0.3, 1) both;
}
/* ── per-accent palettes ─────────────────────────────────────── */
/* Blue (active orders) */
.fleet-ops-pulse-tile.pulse-accent-blue {
background-image: linear-gradient(135deg, rgba(59, 130, 246, 0.1) 0%, rgba(59, 130, 246, 0) 65%);
border-color: rgba(59, 130, 246, 0.25);
}
body[data-theme='dark'] .fleet-ops-pulse-tile.pulse-accent-blue {
background-image: linear-gradient(135deg, rgba(59, 130, 246, 0.18) 0%, rgba(59, 130, 246, 0) 65%);
border-color: rgba(59, 130, 246, 0.35);
}
.pulse-accent-blue .fleet-ops-pulse-stripe {
background-color: #3b82f6;
}
.pulse-accent-blue .fleet-ops-pulse-icon {
background-color: rgba(59, 130, 246, 0.18);
color: #2563eb;
}
body[data-theme='dark'] .pulse-accent-blue .fleet-ops-pulse-icon {
color: #60a5fa;
}
.pulse-accent-blue .fleet-ops-pulse-value {
color: #2563eb;
}
body[data-theme='dark'] .pulse-accent-blue .fleet-ops-pulse-value {
color: #60a5fa;
}
/* Green (completed today) */
.fleet-ops-pulse-tile.pulse-accent-green {
background-image: linear-gradient(135deg, rgba(16, 185, 129, 0.1) 0%, rgba(16, 185, 129, 0) 65%);
border-color: rgba(16, 185, 129, 0.3);
}
body[data-theme='dark'] .fleet-ops-pulse-tile.pulse-accent-green {
background-image: linear-gradient(135deg, rgba(16, 185, 129, 0.18) 0%, rgba(16, 185, 129, 0) 65%);
border-color: rgba(16, 185, 129, 0.4);
}
.pulse-accent-green .fleet-ops-pulse-stripe {
background-color: #10b981;
}
.pulse-accent-green .fleet-ops-pulse-icon {
background-color: rgba(16, 185, 129, 0.18);
color: #059669;
}
body[data-theme='dark'] .pulse-accent-green .fleet-ops-pulse-icon {
color: #34d399;
}
.pulse-accent-green .fleet-ops-pulse-value {
color: #059669;
}
body[data-theme='dark'] .pulse-accent-green .fleet-ops-pulse-value {
color: #34d399;
}
/* Sky (drivers online) */
.fleet-ops-pulse-tile.pulse-accent-sky {
background-image: linear-gradient(135deg, rgba(14, 165, 233, 0.1) 0%, rgba(14, 165, 233, 0) 65%);
border-color: rgba(14, 165, 233, 0.25);
}
body[data-theme='dark'] .fleet-ops-pulse-tile.pulse-accent-sky {
background-image: linear-gradient(135deg, rgba(14, 165, 233, 0.18) 0%, rgba(14, 165, 233, 0) 65%);
border-color: rgba(14, 165, 233, 0.35);
}
.pulse-accent-sky .fleet-ops-pulse-stripe {
background-color: #0ea5e9;
}
.pulse-accent-sky .fleet-ops-pulse-icon {
background-color: rgba(14, 165, 233, 0.18);
color: #0284c7;
}
body[data-theme='dark'] .pulse-accent-sky .fleet-ops-pulse-icon {
color: #38bdf8;
}
.pulse-accent-sky .fleet-ops-pulse-value {
color: #0284c7;
}
body[data-theme='dark'] .pulse-accent-sky .fleet-ops-pulse-value {
color: #38bdf8;
}
/* Indigo (vehicles deployed) */
.fleet-ops-pulse-tile.pulse-accent-indigo {
background-image: linear-gradient(135deg, rgba(99, 102, 241, 0.1) 0%, rgba(99, 102, 241, 0) 65%);
border-color: rgba(99, 102, 241, 0.25);
}
body[data-theme='dark'] .fleet-ops-pulse-tile.pulse-accent-indigo {
background-image: linear-gradient(135deg, rgba(99, 102, 241, 0.18) 0%, rgba(99, 102, 241, 0) 65%);
border-color: rgba(99, 102, 241, 0.35);
}
.pulse-accent-indigo .fleet-ops-pulse-stripe {
background-color: #6366f1;
}
.pulse-accent-indigo .fleet-ops-pulse-icon {
background-color: rgba(99, 102, 241, 0.18);
color: #4f46e5;
}
body[data-theme='dark'] .pulse-accent-indigo .fleet-ops-pulse-icon {
color: #818cf8;
}
.pulse-accent-indigo .fleet-ops-pulse-value {
color: #4f46e5;
}
body[data-theme='dark'] .pulse-accent-indigo .fleet-ops-pulse-value {
color: #818cf8;
}
/* Amber (issues open — lower is better) */
.fleet-ops-pulse-tile.pulse-accent-amber {
background-image: linear-gradient(135deg, rgba(245, 158, 11, 0.1) 0%, rgba(245, 158, 11, 0) 65%);
border-color: rgba(245, 158, 11, 0.3);
}
body[data-theme='dark'] .fleet-ops-pulse-tile.pulse-accent-amber {
background-image: linear-gradient(135deg, rgba(245, 158, 11, 0.18) 0%, rgba(245, 158, 11, 0) 65%);
border-color: rgba(245, 158, 11, 0.4);
}
.pulse-accent-amber .fleet-ops-pulse-stripe {
background-color: #f59e0b;
}
.pulse-accent-amber .fleet-ops-pulse-icon {
background-color: rgba(245, 158, 11, 0.18);
color: #d97706;
}
body[data-theme='dark'] .pulse-accent-amber .fleet-ops-pulse-icon {
color: #fbbf24;
}
.pulse-accent-amber .fleet-ops-pulse-value {
color: #d97706;
}
body[data-theme='dark'] .pulse-accent-amber .fleet-ops-pulse-value {
color: #fbbf24;
}