feat(rcal): replace static demo with interactive folk-calendar-view
The demo page was a frozen July 2026 static HTML grid with no real functionality. Now embeds <folk-calendar-view space="demo"> which provides full month/week/day views, navigation, lunar overlay, source filtering, event modals, keyboard shortcuts, and 37 demo events. Keeps hero, temporal zoom showcase, feature cards, ecosystem integrations, and CTA sections around the interactive component. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
317bc46de6
commit
4979c3d80c
|
|
@ -1,46 +1,12 @@
|
|||
/**
|
||||
* rCal demo page — server-rendered HTML body.
|
||||
*
|
||||
* Static July 2026 calendar grid with Alpine Explorer trip events,
|
||||
* tab switching (Temporal/Spatial/Lunar/Context), zoom panel,
|
||||
* and feature cards. Entirely local state, no WebSocket.
|
||||
* Embeds the full <folk-calendar-view space="demo"> component for
|
||||
* real interactivity (month/week/day views, navigation, lunar overlay,
|
||||
* source filtering, event modals, keyboard shortcuts) plus showcase
|
||||
* sections explaining the rCal vision.
|
||||
*/
|
||||
|
||||
/* ─── Event Data ──────────────────────────────────────────── */
|
||||
|
||||
interface CalEvent {
|
||||
day: number;
|
||||
emoji: string;
|
||||
label: string;
|
||||
color: string;
|
||||
bg: string;
|
||||
}
|
||||
|
||||
const TRIP_EVENTS: CalEvent[] = [
|
||||
{ day: 6, emoji: "\u2708\uFE0F", label: "Arrive Chamonix", color: "#2dd4bf", bg: "rgba(20,184,166,0.15)" },
|
||||
{ day: 7, emoji: "\u{1F97E}", label: "Lac Blanc Hike", color: "#34d399", bg: "rgba(16,185,129,0.15)" },
|
||||
{ day: 8, emoji: "\u{1F9D7}", label: "Aiguille du Midi", color: "#fbbf24", bg: "rgba(245,158,11,0.15)" },
|
||||
{ day: 9, emoji: "\u{1F97E}", label: "Mer de Glace", color: "#34d399", bg: "rgba(16,185,129,0.15)" },
|
||||
{ day: 10, emoji: "\u{1F682}", label: "Train to Zermatt", color: "#22d3ee", bg: "rgba(6,182,212,0.15)" },
|
||||
{ day: 11, emoji: "\u{1F97E}", label: "Five Lakes Walk", color: "#34d399", bg: "rgba(16,185,129,0.15)" },
|
||||
{ day: 12, emoji: "\u26F7", label: "Glacier Paradise", color: "#fbbf24", bg: "rgba(245,158,11,0.15)" },
|
||||
{ day: 13, emoji: "\u{1F3DB}", label: "Alpine Museum", color: "#a78bfa", bg: "rgba(139,92,246,0.15)" },
|
||||
{ day: 14, emoji: "\u{1F68C}", label: "Bus to Dolomites", color: "#22d3ee", bg: "rgba(6,182,212,0.15)" },
|
||||
{ day: 15, emoji: "\u{1F97E}", label: "Tre Cime Circuit", color: "#34d399", bg: "rgba(16,185,129,0.15)" },
|
||||
{ day: 16, emoji: "\u{1FA82}", label: "Paragliding", color: "#fbbf24", bg: "rgba(245,158,11,0.15)" },
|
||||
{ day: 17, emoji: "\u{1F6F6}", label: "Lago di Braies", color: "#fbbf24", bg: "rgba(245,158,11,0.15)" },
|
||||
{ day: 18, emoji: "\u{1F97E}", label: "Seceda Ridge", color: "#34d399", bg: "rgba(16,185,129,0.15)" },
|
||||
{ day: 19, emoji: "\u{1F4F8}", label: "Rest & Photos", color: "#94a3b8", bg: "rgba(100,116,139,0.15)" },
|
||||
{ day: 20, emoji: "\u2708\uFE0F", label: "Depart", color: "#2dd4bf", bg: "rgba(20,184,166,0.15)" },
|
||||
];
|
||||
|
||||
const TABS = ["Temporal", "Spatial", "Lunar", "Context"];
|
||||
|
||||
const ZOOM_LEVELS = [
|
||||
"Era", "Century", "Decade", "Year", "Quarter",
|
||||
"Month", "Week", "Day", "Hour", "Minute",
|
||||
];
|
||||
|
||||
const FEATURES = [
|
||||
{
|
||||
icon: "\u{1F50D}",
|
||||
|
|
@ -64,62 +30,21 @@ const FEATURES = [
|
|||
},
|
||||
];
|
||||
|
||||
const LEGEND = [
|
||||
{ color: "#2dd4bf", label: "Travel" },
|
||||
{ color: "#34d399", label: "Hike" },
|
||||
{ color: "#fbbf24", label: "Adventure" },
|
||||
{ color: "#22d3ee", label: "Transit" },
|
||||
{ color: "#a78bfa", label: "Culture" },
|
||||
{ color: "#94a3b8", label: "Rest" },
|
||||
const INTEGRATIONS = [
|
||||
{ icon: "\u{1F5FA}", name: "rTrips", desc: "Travel itineraries surface as calendar events with departure/arrival times and locations." },
|
||||
{ icon: "\u{1F30D}", name: "rMaps", desc: "Events appear on the map. Zoom the map and the calendar filters to show only visible events." },
|
||||
{ icon: "\u{1F465}", name: "rNetwork", desc: "See availability across your network. Coordinate meetings without back-and-forth." },
|
||||
{ icon: "\u{1F4DD}", name: "rNotes", desc: "Link notes to calendar events. Meeting agendas, daily journals, and retrospective logs." },
|
||||
{ icon: "\u{1F4B0}", name: "rFunds", desc: "Budget reviews, treasury flows, and governance votes appear on the calendar timeline." },
|
||||
{ icon: "\u2615\uFE0F", name: "rSpace", desc: "Each space has its own calendar layer. Nest calendars across spaces for cross-community coordination." },
|
||||
];
|
||||
|
||||
/* ─── Helpers ─────────────────────────────────────────────── */
|
||||
|
||||
function eventForDay(day: number): CalEvent | undefined {
|
||||
return TRIP_EVENTS.find((e) => e.day === day);
|
||||
}
|
||||
|
||||
function isTripDay(day: number): boolean {
|
||||
return day >= 6 && day <= 20;
|
||||
}
|
||||
|
||||
/* ─── Render ──────────────────────────────────────────────── */
|
||||
const ZOOM_LEVELS = [
|
||||
"Era", "Century", "Decade", "Year", "Quarter",
|
||||
"Month", "Week", "Day", "Hour", "Minute",
|
||||
];
|
||||
|
||||
export function renderDemo(): string {
|
||||
// July 2026: starts on Wednesday (offset 2 for Mon-based grid), 31 days
|
||||
const firstDayOffset = 2; // Mon=0, Tue=1, Wed=2
|
||||
const totalDays = 31;
|
||||
const dayNames = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
|
||||
|
||||
// Build calendar cells
|
||||
const calendarCells: string[] = [];
|
||||
|
||||
// Empty offset cells
|
||||
for (let i = 0; i < firstDayOffset; i++) {
|
||||
calendarCells.push(`<div class="rcal-cell rcal-cell--empty"></div>`);
|
||||
}
|
||||
|
||||
// Day cells
|
||||
for (let d = 1; d <= totalDays; d++) {
|
||||
const ev = eventForDay(d);
|
||||
const trip = isTripDay(d);
|
||||
const todayClass = d === 15 ? " rcal-cell--today" : "";
|
||||
const tripClass = trip ? " rcal-cell--trip" : "";
|
||||
|
||||
let pill = "";
|
||||
if (ev) {
|
||||
pill = `<div class="rcal-pill" style="background:${ev.bg};color:${ev.color};border:1px solid ${ev.color}22;">
|
||||
<span class="rcal-pill__emoji">${ev.emoji}</span>
|
||||
<span class="rcal-pill__label">${ev.label}</span>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
calendarCells.push(`<div class="rcal-cell${tripClass}${todayClass}">
|
||||
<span class="rcal-cell__num${trip ? " rcal-cell__num--trip" : ""}">${d}</span>
|
||||
${pill}
|
||||
</div>`);
|
||||
}
|
||||
|
||||
return `
|
||||
<div class="rd-root" style="--rd-accent-from:#6366f1; --rd-accent-to:#a78bfa;">
|
||||
|
||||
|
|
@ -129,7 +54,7 @@ export function renderDemo(): string {
|
|||
Multi-Dimensional Calendar
|
||||
</div>
|
||||
<h1>rCal Demo</h1>
|
||||
<p class="rd-subtitle">Multi-dimensional calendar with temporal zoom</p>
|
||||
<p class="rd-subtitle">Temporal coordination with lunar cycles, spatial context, and multi-scale zoom</p>
|
||||
<div class="rd-meta">
|
||||
<span>\u{1F50D} Temporal Zoom</span>
|
||||
<span style="color:#475569">|</span>
|
||||
|
|
@ -141,62 +66,35 @@ export function renderDemo(): string {
|
|||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ── Calendar Section ── -->
|
||||
<!-- ── Interactive Calendar ── -->
|
||||
<section class="rd-section rd-section--narrow">
|
||||
|
||||
<!-- Header bar -->
|
||||
<div class="rd-card" style="margin-bottom:0;">
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;padding:1rem 1.25rem;border-bottom:1px solid rgba(51,65,85,0.3);flex-wrap:wrap;gap:0.75rem;">
|
||||
<h2 style="font-size:1.25rem;font-weight:700;color:#f1f5f9;margin:0;display:flex;align-items:center;gap:0.5rem;">
|
||||
\u{1F4C5} July 2026
|
||||
</h2>
|
||||
<div style="display:flex;gap:0.25rem;" id="rcal-tabs">
|
||||
${TABS.map(
|
||||
(tab, i) => `<button data-cal-tab="${tab.toLowerCase()}" style="
|
||||
padding:0.375rem 0.875rem;
|
||||
border-radius:0.5rem;
|
||||
font-size:0.8rem;
|
||||
font-weight:500;
|
||||
border:none;
|
||||
cursor:pointer;
|
||||
transition:all 0.15s;
|
||||
${i === 0 ? "background:rgba(99,102,241,0.15);color:#818cf8;" : "background:transparent;color:#94a3b8;"}
|
||||
">${tab}</button>`,
|
||||
).join("\n ")}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Day header row -->
|
||||
<div class="rcal-grid rcal-grid--header">
|
||||
${dayNames.map((d) => `<div class="rcal-day-header">${d}</div>`).join("\n ")}
|
||||
</div>
|
||||
|
||||
<!-- Calendar grid -->
|
||||
<div class="rcal-grid">
|
||||
${calendarCells.join("\n ")}
|
||||
</div>
|
||||
|
||||
<!-- Legend -->
|
||||
<div style="display:flex;align-items:center;gap:1rem;padding:0.75rem 1.25rem;border-top:1px solid rgba(51,65,85,0.3);flex-wrap:wrap;">
|
||||
<span style="font-size:0.75rem;color:#64748b;font-weight:500;">Legend:</span>
|
||||
${LEGEND.map(
|
||||
(l) => `<span style="display:flex;align-items:center;gap:0.375rem;font-size:0.75rem;color:#94a3b8;">
|
||||
<span style="width:0.5rem;height:0.5rem;border-radius:9999px;background:${l.color};display:inline-block;"></span>
|
||||
${l.label}
|
||||
</span>`,
|
||||
).join("\n ")}
|
||||
</div>
|
||||
<div class="rd-card" style="padding:0;overflow:hidden;">
|
||||
<folk-calendar-view space="demo"></folk-calendar-view>
|
||||
</div>
|
||||
<div style="text-align:center;padding:0.75rem 0;font-size:0.8rem;color:#64748b;">
|
||||
<kbd style="background:#1e293b;padding:2px 6px;border-radius:4px;border:1px solid #334155;font-size:0.75rem;">1</kbd>
|
||||
<kbd style="background:#1e293b;padding:2px 6px;border-radius:4px;border:1px solid #334155;font-size:0.75rem;">2</kbd>
|
||||
<kbd style="background:#1e293b;padding:2px 6px;border-radius:4px;border:1px solid #334155;font-size:0.75rem;">3</kbd>
|
||||
Day / Week / Month \u00B7
|
||||
<kbd style="background:#1e293b;padding:2px 6px;border-radius:4px;border:1px solid #334155;font-size:0.75rem;">\u2190</kbd>
|
||||
<kbd style="background:#1e293b;padding:2px 6px;border-radius:4px;border:1px solid #334155;font-size:0.75rem;">\u2192</kbd>
|
||||
Navigate \u00B7
|
||||
<kbd style="background:#1e293b;padding:2px 6px;border-radius:4px;border:1px solid #334155;font-size:0.75rem;">L</kbd>
|
||||
Lunar \u00B7
|
||||
<kbd style="background:#1e293b;padding:2px 6px;border-radius:4px;border:1px solid #334155;font-size:0.75rem;">T</kbd>
|
||||
Today
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ── Zoom Panel ── -->
|
||||
<!-- ── Temporal Zoom Showcase ── -->
|
||||
<section class="rd-section rd-section--narrow">
|
||||
<div class="rd-card" style="padding:1.5rem;">
|
||||
<h2 style="font-size:1.125rem;font-weight:600;color:#f1f5f9;margin:0 0 1rem;display:flex;align-items:center;gap:0.5rem;">
|
||||
<h2 style="font-size:1.125rem;font-weight:600;color:#f1f5f9;margin:0 0 0.75rem;display:flex;align-items:center;gap:0.5rem;">
|
||||
\u{1F50D} Temporal Zoom
|
||||
</h2>
|
||||
<p style="font-size:0.875rem;color:#94a3b8;margin:0 0 1rem;">
|
||||
Navigate across temporal granularities. The calendar grid adapts at each zoom level.
|
||||
<p style="font-size:0.875rem;color:#94a3b8;margin:0 0 1rem;line-height:1.5;">
|
||||
Navigate across 10 temporal granularities. The calendar adapts its grid at each level —
|
||||
from geological eras to individual minutes.
|
||||
</p>
|
||||
<div style="display:flex;flex-wrap:wrap;gap:0.5rem;">
|
||||
${ZOOM_LEVELS.map(
|
||||
|
|
@ -218,7 +116,7 @@ export function renderDemo(): string {
|
|||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ── Features ── -->
|
||||
<!-- ── Core Concepts ── -->
|
||||
<section class="rd-section">
|
||||
<div class="rd-grid rd-grid--2">
|
||||
${FEATURES.map(
|
||||
|
|
@ -232,6 +130,23 @@ export function renderDemo(): string {
|
|||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ── Ecosystem Integrations ── -->
|
||||
<section class="rd-section">
|
||||
<h2 style="text-align:center;font-size:1.25rem;font-weight:700;color:#f1f5f9;margin:0 0 1.5rem;">
|
||||
r* Ecosystem Integrations
|
||||
</h2>
|
||||
<div class="rd-grid rd-grid--3">
|
||||
${INTEGRATIONS.map(
|
||||
(i) => `
|
||||
<div class="rd-card" style="padding:1.25rem;">
|
||||
<div style="font-size:1.5rem;margin-bottom:0.5rem;">${i.icon}</div>
|
||||
<h3 style="font-size:0.875rem;font-weight:600;color:#e2e8f0;margin:0 0 0.375rem;">${i.name}</h3>
|
||||
<p style="font-size:0.8rem;color:#94a3b8;margin:0;line-height:1.4;">${i.desc}</p>
|
||||
</div>`,
|
||||
).join("")}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ── CTA ── -->
|
||||
<section class="rd-section rd-section--narrow">
|
||||
<div class="rd-cta">
|
||||
|
|
@ -246,99 +161,5 @@ export function renderDemo(): string {
|
|||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
<style>
|
||||
/* ── rCal demo grid ── */
|
||||
.rcal-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
gap: 1px;
|
||||
background: rgba(51,65,85,0.2);
|
||||
padding: 0 1px 1px;
|
||||
}
|
||||
.rcal-grid--header {
|
||||
gap: 0;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
border-bottom: 1px solid rgba(51,65,85,0.3);
|
||||
}
|
||||
.rcal-day-header {
|
||||
padding: 0.5rem 0;
|
||||
text-align: center;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
color: #64748b;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
.rcal-cell {
|
||||
min-height: 3.5rem;
|
||||
padding: 0.375rem;
|
||||
background: rgba(15,23,42,0.6);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
.rcal-cell:hover {
|
||||
background: rgba(30,41,59,0.8);
|
||||
}
|
||||
.rcal-cell--empty {
|
||||
background: rgba(15,23,42,0.3);
|
||||
}
|
||||
.rcal-cell--trip {
|
||||
background: rgba(99,102,241,0.04);
|
||||
}
|
||||
.rcal-cell--today {
|
||||
outline: 2px solid rgba(99,102,241,0.5);
|
||||
outline-offset: -2px;
|
||||
background: rgba(99,102,241,0.08);
|
||||
}
|
||||
.rcal-cell__num {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
color: #64748b;
|
||||
line-height: 1;
|
||||
}
|
||||
.rcal-cell__num--trip {
|
||||
color: #a5b4fc;
|
||||
font-weight: 600;
|
||||
}
|
||||
.rcal-pill {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
padding: 0.125rem 0.375rem;
|
||||
border-radius: 0.25rem;
|
||||
font-size: 0.625rem;
|
||||
line-height: 1.2;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
.rcal-pill__emoji {
|
||||
flex-shrink: 0;
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
.rcal-pill__label {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* Responsive: stack pill text on small screens */
|
||||
@media (max-width: 640px) {
|
||||
.rcal-cell {
|
||||
min-height: 2.75rem;
|
||||
padding: 0.25rem;
|
||||
}
|
||||
.rcal-pill__label {
|
||||
display: none;
|
||||
}
|
||||
.rcal-pill {
|
||||
justify-content: center;
|
||||
padding: 0.125rem;
|
||||
}
|
||||
}
|
||||
</style>`;
|
||||
</div>`;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -385,8 +385,8 @@ routes.get("/", (c) => {
|
|||
modules: getModuleInfoList(),
|
||||
theme: "dark",
|
||||
body: renderDemo(),
|
||||
scripts: `<script type="module" src="/modules/rcal/cal-demo.js"></script>`,
|
||||
styles: `<link rel="stylesheet" href="/modules/rcal/cal.css">`,
|
||||
scripts: `<script type="module" src="/modules/rcal/folk-calendar-view.js?v=3"></script>`,
|
||||
styles: `<link rel="stylesheet" href="/modules/rcal/cal.css?v=2">`,
|
||||
}));
|
||||
}
|
||||
return c.html(renderShell({
|
||||
|
|
@ -426,4 +426,8 @@ export const calModule: RSpaceModule = {
|
|||
},
|
||||
],
|
||||
acceptsFeeds: ["data", "governance"],
|
||||
outputPaths: [
|
||||
{ path: "saved-views", name: "Saved Views", icon: "👁️", description: "Custom calendar views and filters" },
|
||||
{ path: "events", name: "Events", icon: "📅", description: "Calendar events across all systems" },
|
||||
],
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue