feat(rflows): redesign canvas toolbar + zoom controls, remove sufficiency badge
Move toolbar from horizontal top-right strip to vertical left-side panel with larger, more prominent buttons and emoji icons. Replace basic +/- zoom buttons with a pill-shaped widget showing live zoom percentage and a fit-to-view button. Remove the "0% Enough" sufficiency badge overlay. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
cf7fab25f3
commit
e37044f599
|
|
@ -234,8 +234,8 @@
|
|||
.flows-nav-overlay .rapp-nav__title { color: var(--rs-text-primary); font-size: 14px; font-weight: 600; }
|
||||
.flows-nav-overlay .rapp-nav__badge { font-size: 10px; color: var(--rs-warning); background: rgba(251,191,36,0.15); padding: 2px 8px; border-radius: 4px; }
|
||||
|
||||
/* Badge offset when nav overlay present */
|
||||
.flows-canvas-container--fullpage .flows-canvas-badge { top: 54px; }
|
||||
/* Toolbar offset when nav overlay present */
|
||||
.flows-canvas-container--fullpage .flows-canvas-toolbar { top: 54px; }
|
||||
|
||||
.flows-canvas-svg {
|
||||
width: 100%; height: 100%; display: block;
|
||||
|
|
@ -247,26 +247,32 @@
|
|||
.flows-canvas-svg.panning { cursor: grabbing; }
|
||||
.flows-canvas-svg.dragging { cursor: move; }
|
||||
|
||||
/* Toolbar — top-right overlay */
|
||||
/* Toolbar — left-side vertical panel */
|
||||
.flows-canvas-toolbar {
|
||||
position: absolute; top: 10px; right: 10px; z-index: 10;
|
||||
display: flex; gap: 4px; flex-wrap: wrap; align-items: center;
|
||||
position: absolute; top: 10px; left: 10px; z-index: 10;
|
||||
display: flex; flex-direction: column; gap: 4px; align-items: stretch;
|
||||
background: var(--rs-bg-surface); border: 1px solid var(--rs-border);
|
||||
border-radius: 10px; padding: 6px; box-shadow: 0 2px 8px rgba(0,0,0,0.12);
|
||||
width: 150px;
|
||||
}
|
||||
.flows-canvas-btn {
|
||||
padding: 5px 10px; border: 1px solid var(--rs-bg-surface-raised); border-radius: 6px;
|
||||
background: var(--rs-bg-surface); color: var(--rs-text-primary); font-size: 11px; font-weight: 500;
|
||||
.flows-toolbar-btn {
|
||||
padding: 7px 10px; border: 1px solid transparent; border-radius: 6px;
|
||||
background: transparent; color: var(--rs-text-primary); font-size: 12px; font-weight: 600;
|
||||
cursor: pointer; white-space: nowrap; transition: background 0.15s, border-color 0.15s;
|
||||
text-align: left;
|
||||
}
|
||||
.flows-canvas-btn:hover { background: var(--rs-border-strong); border-color: var(--rs-text-muted); }
|
||||
.flows-canvas-btn--source { border-color: #10b981; color: var(--rflows-source-text, #6ee7b7); }
|
||||
.flows-canvas-btn--source:hover { background: var(--rflows-source-hover-bg, #064e3b); }
|
||||
.flows-canvas-btn--funnel { border-color: #3b82f6; color: var(--rflows-funnel-text, #93c5fd); }
|
||||
.flows-canvas-btn--funnel:hover { background: var(--rflows-funnel-hover-bg, #1e3a5f); }
|
||||
.flows-canvas-btn--outcome { border-color: #ec4899; color: var(--rflows-outcome-text, #f9a8d4); }
|
||||
.flows-canvas-btn--outcome:hover { background: var(--rflows-outcome-hover-bg, #4a1942); }
|
||||
.flows-canvas-btn--active { background: var(--rs-primary); border-color: var(--rs-primary-hover); color: #fff; }
|
||||
.flows-canvas-sep {
|
||||
width: 1px; height: 20px; background: var(--rs-border-strong); margin: 0 4px;
|
||||
.flows-toolbar-btn:hover { background: var(--rs-bg-hover); }
|
||||
.flows-toolbar-btn--fund { background: var(--rs-primary); color: #fff; border-color: var(--rs-primary); text-align: center; }
|
||||
.flows-toolbar-btn--fund:hover { background: var(--rs-primary-hover); }
|
||||
.flows-toolbar-btn--source { color: var(--rflows-source-text, #6ee7b7); }
|
||||
.flows-toolbar-btn--source:hover { background: var(--rflows-source-hover-bg, #064e3b); }
|
||||
.flows-toolbar-btn--funnel { color: var(--rflows-funnel-text, #93c5fd); }
|
||||
.flows-toolbar-btn--funnel:hover { background: var(--rflows-funnel-hover-bg, #1e3a5f); }
|
||||
.flows-toolbar-btn--outcome { color: var(--rflows-outcome-text, #f9a8d4); }
|
||||
.flows-toolbar-btn--outcome:hover { background: var(--rflows-outcome-hover-bg, #4a1942); }
|
||||
.flows-toolbar-btn--active { background: var(--rs-primary); border-color: var(--rs-primary-hover); color: #fff; }
|
||||
.flows-toolbar-sep {
|
||||
height: 1px; background: var(--rs-border); margin: 2px 0;
|
||||
}
|
||||
|
||||
/* SVG node styles */
|
||||
|
|
@ -389,20 +395,27 @@
|
|||
.flows-canvas-legend-item { display: flex; align-items: center; gap: 4px; }
|
||||
.flows-canvas-legend-dot { width: 8px; height: 8px; border-radius: 2px; flex-shrink: 0; }
|
||||
|
||||
/* Zoom controls — bottom-right */
|
||||
/* Zoom controls — bottom-right pill */
|
||||
.flows-canvas-zoom {
|
||||
position: absolute; bottom: 10px; right: 10px; z-index: 10;
|
||||
display: flex; gap: 4px;
|
||||
position: absolute; bottom: 14px; right: 14px; z-index: 10;
|
||||
display: flex; align-items: center; gap: 0;
|
||||
background: var(--rs-bg-surface); border: 1px solid var(--rs-border);
|
||||
border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.12);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Sufficiency badge — top-left */
|
||||
.flows-canvas-badge {
|
||||
position: absolute; top: 10px; left: 10px; z-index: 10;
|
||||
background: var(--rs-glass-bg); border-radius: 8px; padding: 8px 14px;
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
.flows-zoom-btn {
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
width: 32px; height: 32px; border: none; background: transparent;
|
||||
color: var(--rs-text-primary); cursor: pointer; transition: background 0.15s;
|
||||
}
|
||||
.flows-zoom-btn:hover { background: var(--rs-bg-hover); }
|
||||
.flows-zoom-level {
|
||||
font-size: 11px; font-weight: 600; color: var(--rs-text-secondary);
|
||||
min-width: 40px; text-align: center; user-select: none;
|
||||
}
|
||||
.flows-zoom-sep {
|
||||
width: 1px; height: 18px; background: var(--rs-border); margin: 0 2px;
|
||||
}
|
||||
.flows-canvas-badge__score { font-size: 20px; font-weight: 700; }
|
||||
.flows-canvas-badge__label { font-size: 10px; color: var(--rs-text-secondary); text-transform: uppercase; letter-spacing: 0.05em; }
|
||||
|
||||
/* Legacy diagram (kept for compat) */
|
||||
.flows-diagram { overflow-x: auto; }
|
||||
|
|
@ -972,11 +985,11 @@
|
|||
|
||||
/* ── Flow dropdown (toolbar) ──────────────────────── */
|
||||
.flows-dropdown {
|
||||
position: relative; display: inline-block;
|
||||
position: relative; display: block;
|
||||
}
|
||||
.flows-dropdown__trigger {
|
||||
display: flex; align-items: center; gap: 4px;
|
||||
max-width: 180px;
|
||||
display: flex; align-items: center; gap: 4px; width: 100%;
|
||||
max-width: none;
|
||||
}
|
||||
.flows-dropdown__name {
|
||||
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
||||
|
|
@ -1147,8 +1160,8 @@
|
|||
.flows-cards { grid-template-columns: 1fr; }
|
||||
.flows-canvas-container { height: 50vh; min-height: 300px; }
|
||||
.flows-canvas-container--fullpage { height: 100%; min-height: unset; }
|
||||
.flows-canvas-toolbar { flex-wrap: wrap; gap: 3px; top: 6px; right: 6px; }
|
||||
.flows-canvas-btn { padding: 6px 10px; font-size: 11px; min-height: 44px; min-width: 44px; }
|
||||
.flows-canvas-toolbar { width: 130px; top: 6px; left: 6px; padding: 4px; }
|
||||
.flows-toolbar-btn { padding: 8px 8px; font-size: 12px; min-height: 40px; }
|
||||
.flows-editor-panel { width: 100%; }
|
||||
.flows-analytics-panel { width: 100%; }
|
||||
.flows-canvas-legend { font-size: 10px; gap: 8px; }
|
||||
|
|
|
|||
|
|
@ -891,15 +891,9 @@ class FolkFlowsApp extends HTMLElement {
|
|||
<span class="rapp-nav__title">${this.esc(this.flowName || "Flow Detail")}</span>
|
||||
${this.isDemo ? '<span class="rapp-nav__badge">Demo</span>' : ""}
|
||||
</div>
|
||||
<div class="flows-canvas-badge" id="canvas-badge">
|
||||
<div>
|
||||
<div class="flows-canvas-badge__score" id="badge-score" style="color:${scoreColor}">${scorePct}%</div>
|
||||
<div class="flows-canvas-badge__label">ENOUGH</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flows-canvas-toolbar">
|
||||
<div class="flows-dropdown" id="flow-dropdown">
|
||||
<button class="flows-canvas-btn flows-dropdown__trigger" data-canvas-action="flow-picker">
|
||||
<button class="flows-toolbar-btn flows-dropdown__trigger" data-canvas-action="flow-picker">
|
||||
<span class="flows-dropdown__name">${this.esc(this.flowName || 'Untitled')}</span>
|
||||
<span class="flows-dropdown__chevron">▾</span>
|
||||
</button>
|
||||
|
|
@ -910,17 +904,16 @@ class FolkFlowsApp extends HTMLElement {
|
|||
<button class="flows-dropdown__item" data-flow-action="manage-flows">Manage Flows...</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flows-canvas-sep"></div>
|
||||
<button class="flows-canvas-btn flows-canvas-btn--fund" data-canvas-action="quick-fund" style="background:var(--rs-primary);color:white;font-weight:600">Fund</button>
|
||||
<div class="flows-canvas-sep"></div>
|
||||
<button class="flows-canvas-btn flows-canvas-btn--source" data-canvas-action="add-source">+ Source</button>
|
||||
<button class="flows-canvas-btn flows-canvas-btn--funnel" data-canvas-action="add-funnel">+ Funnel</button>
|
||||
<button class="flows-canvas-btn flows-canvas-btn--outcome" data-canvas-action="add-outcome">+ Outcome</button>
|
||||
<div class="flows-canvas-sep"></div>
|
||||
<button class="flows-canvas-btn" data-canvas-action="sim" id="sim-btn">${this.isSimulating ? "Pause" : "Play"}</button>
|
||||
<button class="flows-canvas-btn" data-canvas-action="fit">Fit</button>
|
||||
<button class="flows-canvas-btn ${this.analyticsOpen ? "flows-canvas-btn--active" : ""}" data-canvas-action="analytics">Analytics</button>
|
||||
<button class="flows-canvas-btn" data-canvas-action="share">Share</button>
|
||||
<div class="flows-toolbar-sep"></div>
|
||||
<button class="flows-toolbar-btn flows-toolbar-btn--fund" data-canvas-action="quick-fund">💰 Fund</button>
|
||||
<div class="flows-toolbar-sep"></div>
|
||||
<button class="flows-toolbar-btn flows-toolbar-btn--source" data-canvas-action="add-source">+ Source</button>
|
||||
<button class="flows-toolbar-btn flows-toolbar-btn--funnel" data-canvas-action="add-funnel">+ Funnel</button>
|
||||
<button class="flows-toolbar-btn flows-toolbar-btn--outcome" data-canvas-action="add-outcome">+ Outcome</button>
|
||||
<div class="flows-toolbar-sep"></div>
|
||||
<button class="flows-toolbar-btn" data-canvas-action="sim" id="sim-btn">${this.isSimulating ? "⏸ Pause" : "▶ Play"}</button>
|
||||
<button class="flows-toolbar-btn ${this.analyticsOpen ? "flows-toolbar-btn--active" : ""}" data-canvas-action="analytics">📊 Analytics</button>
|
||||
<button class="flows-toolbar-btn" data-canvas-action="share">🔗 Share</button>
|
||||
</div>
|
||||
<svg class="flows-canvas-svg" id="flow-canvas">
|
||||
<defs>
|
||||
|
|
@ -956,8 +949,17 @@ class FolkFlowsApp extends HTMLElement {
|
|||
<span class="flows-canvas-legend-item"><span class="flows-canvas-legend-dot" style="background:var(--rflows-status-thriving);border-radius:50%"></span>Thriving</span>
|
||||
</div>
|
||||
<div class="flows-canvas-zoom">
|
||||
<button class="flows-canvas-btn" data-canvas-action="zoom-in">+</button>
|
||||
<button class="flows-canvas-btn" data-canvas-action="zoom-out">−</button>
|
||||
<button class="flows-zoom-btn" data-canvas-action="zoom-in" title="Zoom in">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 3v10M3 8h10" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
|
||||
</button>
|
||||
<span class="flows-zoom-level" id="zoom-level">${Math.round(this.canvasZoom * 100)}%</span>
|
||||
<button class="flows-zoom-btn" data-canvas-action="zoom-out" title="Zoom out">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8h10" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
|
||||
</button>
|
||||
<div class="flows-zoom-sep"></div>
|
||||
<button class="flows-zoom-btn" data-canvas-action="fit" title="Fit to view">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 6V3a1 1 0 011-1h3M10 2h3a1 1 0 011 1v3M14 10v3a1 1 0 01-1 1h-3M6 14H3a1 1 0 01-1-1v-3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="flows-sim-speed" id="sim-speed-container" style="display:${this.isSimulating ? "flex" : "none"}">
|
||||
<input type="range" class="flows-speed-slider" id="sim-speed-slider" min="20" max="1000" value="${this.simSpeedMs}" step="10"/>
|
||||
|
|
@ -1040,6 +1042,8 @@ class FolkFlowsApp extends HTMLElement {
|
|||
const g = this.shadow.getElementById("canvas-transform");
|
||||
if (g) g.setAttribute("transform", `translate(${this.canvasPanX},${this.canvasPanY}) scale(${this.canvasZoom})`);
|
||||
this.saveViewport();
|
||||
const zl = this.shadow.getElementById("zoom-level");
|
||||
if (zl) zl.textContent = `${Math.round(this.canvasZoom * 100)}%`;
|
||||
}
|
||||
|
||||
private fitView() {
|
||||
|
|
@ -4640,7 +4644,7 @@ class FolkFlowsApp extends HTMLElement {
|
|||
panel.classList.toggle("open", this.analyticsOpen);
|
||||
}
|
||||
const btn = this.shadow.querySelector('[data-canvas-action="analytics"]');
|
||||
if (btn) btn.classList.toggle("flows-canvas-btn--active", this.analyticsOpen);
|
||||
if (btn) btn.classList.toggle("flows-toolbar-btn--active", this.analyticsOpen);
|
||||
}
|
||||
|
||||
private attachAnalyticsListeners() {
|
||||
|
|
|
|||
Loading…
Reference in New Issue