| philip.zeyliger | 26bc659 | 2025-06-30 20:15:30 -0700 | [diff] [blame] | 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ |
| banksean | d5c849d | 2025-06-26 15:48:31 +0000 | [diff] [blame] | 2 | /** |
| 3 | * Demo module for sketch-view-mode-select component |
| 4 | */ |
| 5 | |
| 6 | import { DemoModule } from "./demo-framework/types"; |
| 7 | import { |
| 8 | demoUtils, |
| 9 | sampleViewModeConfigs, |
| 10 | viewModeScenarios, |
| 11 | applyViewModeConfig, |
| 12 | createViewModeTestButtons, |
| 13 | } from "./demo-fixtures/index"; |
| 14 | |
| 15 | const demo: DemoModule = { |
| 16 | title: "View Mode Select Demo", |
| 17 | description: |
| 18 | "Interactive tab navigation for switching between chat, diff, and terminal views", |
| 19 | imports: ["../sketch-view-mode-select"], |
| 20 | styles: ["/dist/tailwind.css"], |
| 21 | |
| 22 | setup: async (container: HTMLElement) => { |
| 23 | // Create demo sections |
| 24 | const basicSection = demoUtils.createDemoSection( |
| 25 | "Basic View Mode Selector", |
| 26 | "Click buttons to switch between different views", |
| 27 | ); |
| 28 | |
| 29 | const scenariosSection = demoUtils.createDemoSection( |
| 30 | "Different Scenarios", |
| 31 | "Pre-configured scenarios showing various diff stats and active modes", |
| 32 | ); |
| 33 | |
| 34 | const interactiveSection = demoUtils.createDemoSection( |
| 35 | "Interactive Testing", |
| 36 | "Test different configurations and watch the component update", |
| 37 | ); |
| 38 | |
| 39 | // Basic view mode selector |
| 40 | const basicContainer = document.createElement("div"); |
| 41 | basicContainer.className = "@container"; |
| 42 | |
| 43 | const basicSelector = document.createElement( |
| 44 | "sketch-view-mode-select", |
| 45 | ) as any; |
| 46 | basicSelector.id = "basic-selector"; |
| 47 | applyViewModeConfig(basicSelector, sampleViewModeConfigs.basic); |
| 48 | |
| 49 | basicContainer.appendChild(basicSelector); |
| 50 | |
| 51 | // Status display for basic selector |
| 52 | const basicStatus = document.createElement("div"); |
| 53 | basicStatus.id = "basic-status"; |
| 54 | basicStatus.style.cssText = ` |
| 55 | margin-top: 15px; |
| 56 | padding: 10px; |
| 57 | background: #f6f8fa; |
| 58 | border-radius: 6px; |
| 59 | font-family: monospace; |
| 60 | font-size: 14px; |
| 61 | `; |
| 62 | |
| 63 | const updateBasicStatus = () => { |
| 64 | basicStatus.innerHTML = ` |
| 65 | <strong>Current State:</strong><br> |
| 66 | Active Mode: <code>${basicSelector.activeMode}</code><br> |
| 67 | Diff Stats: <code>+${basicSelector.diffLinesAdded} -${basicSelector.diffLinesRemoved}</code> |
| 68 | `; |
| 69 | }; |
| 70 | updateBasicStatus(); |
| 71 | |
| 72 | // Listen for view mode changes |
| 73 | basicSelector.addEventListener("view-mode-select", (event: CustomEvent) => { |
| 74 | console.log("View mode changed:", event.detail); |
| 75 | basicSelector.activeMode = event.detail.mode; |
| 76 | updateBasicStatus(); |
| 77 | }); |
| 78 | |
| 79 | // Create scenario examples |
| 80 | const scenarioContainer = document.createElement("div"); |
| 81 | scenarioContainer.style.cssText = ` |
| 82 | display: grid; |
| 83 | grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); |
| 84 | gap: 15px; |
| 85 | margin-top: 15px; |
| 86 | `; |
| 87 | |
| 88 | viewModeScenarios.forEach((scenario) => { |
| 89 | const scenarioCard = document.createElement("div"); |
| 90 | scenarioCard.style.cssText = ` |
| 91 | padding: 15px; |
| 92 | border: 1px solid #d0d7de; |
| 93 | border-radius: 8px; |
| 94 | background: white; |
| 95 | `; |
| 96 | |
| 97 | const scenarioTitle = document.createElement("h4"); |
| 98 | scenarioTitle.textContent = scenario.name; |
| 99 | scenarioTitle.style.cssText = "margin: 0 0 5px 0; color: #24292f;"; |
| 100 | |
| 101 | const scenarioDesc = document.createElement("p"); |
| 102 | scenarioDesc.textContent = scenario.description; |
| 103 | scenarioDesc.style.cssText = |
| 104 | "margin: 0 0 10px 0; color: #656d76; font-size: 14px;"; |
| 105 | |
| 106 | const scenarioWrapper = document.createElement("div"); |
| 107 | scenarioWrapper.className = "@container"; |
| 108 | |
| 109 | const scenarioSelector = document.createElement( |
| 110 | "sketch-view-mode-select", |
| 111 | ) as any; |
| 112 | applyViewModeConfig(scenarioSelector, scenario.config); |
| 113 | |
| 114 | scenarioWrapper.appendChild(scenarioSelector); |
| 115 | |
| 116 | scenarioCard.appendChild(scenarioTitle); |
| 117 | scenarioCard.appendChild(scenarioDesc); |
| 118 | scenarioCard.appendChild(scenarioWrapper); |
| 119 | scenarioContainer.appendChild(scenarioCard); |
| 120 | }); |
| 121 | |
| 122 | // Interactive testing component |
| 123 | const interactiveSelectorContainer = document.createElement("div"); |
| 124 | interactiveSelectorContainer.className = "@container"; |
| 125 | |
| 126 | const interactiveSelector = document.createElement( |
| 127 | "sketch-view-mode-select", |
| 128 | ) as any; |
| 129 | interactiveSelector.id = "interactive-selector"; |
| 130 | applyViewModeConfig(interactiveSelector, sampleViewModeConfigs.basic); |
| 131 | |
| 132 | interactiveSelectorContainer.appendChild(interactiveSelector); |
| 133 | |
| 134 | // Status display for interactive selector |
| 135 | const interactiveStatus = document.createElement("div"); |
| 136 | interactiveStatus.id = "interactive-status"; |
| 137 | interactiveStatus.style.cssText = basicStatus.style.cssText; |
| 138 | |
| 139 | const updateInteractiveStatus = () => { |
| 140 | interactiveStatus.innerHTML = ` |
| 141 | <strong>Interactive Component State:</strong><br> |
| 142 | Active Mode: <code>${interactiveSelector.activeMode}</code><br> |
| 143 | Diff Lines Added: <code>${interactiveSelector.diffLinesAdded}</code><br> |
| 144 | Diff Lines Removed: <code>${interactiveSelector.diffLinesRemoved}</code><br> |
| 145 | <em>Click scenario buttons above to test different configurations</em> |
| 146 | `; |
| 147 | }; |
| 148 | updateInteractiveStatus(); |
| 149 | |
| 150 | // Listen for view mode changes on interactive selector |
| 151 | interactiveSelector.addEventListener( |
| 152 | "view-mode-select", |
| 153 | (event: CustomEvent) => { |
| 154 | console.log("Interactive view mode changed:", event.detail); |
| 155 | interactiveSelector.activeMode = event.detail.mode; |
| 156 | updateInteractiveStatus(); |
| 157 | }, |
| 158 | ); |
| 159 | |
| 160 | // Custom controls for interactive testing |
| 161 | const customControls = document.createElement("div"); |
| 162 | customControls.style.cssText = ` |
| 163 | margin: 15px 0; |
| 164 | padding: 15px; |
| 165 | background: #f6f8fa; |
| 166 | border-radius: 6px; |
| 167 | `; |
| 168 | |
| 169 | const addLinesButton = demoUtils.createButton("Add +5 Lines", () => { |
| 170 | interactiveSelector.diffLinesAdded += 5; |
| 171 | interactiveSelector.requestUpdate(); |
| 172 | updateInteractiveStatus(); |
| 173 | }); |
| 174 | |
| 175 | const removeLinesButton = demoUtils.createButton("Add -3 Lines", () => { |
| 176 | interactiveSelector.diffLinesRemoved += 3; |
| 177 | interactiveSelector.requestUpdate(); |
| 178 | updateInteractiveStatus(); |
| 179 | }); |
| 180 | |
| 181 | const clearDiffButton = demoUtils.createButton("Clear Diff", () => { |
| 182 | interactiveSelector.diffLinesAdded = 0; |
| 183 | interactiveSelector.diffLinesRemoved = 0; |
| 184 | interactiveSelector.requestUpdate(); |
| 185 | updateInteractiveStatus(); |
| 186 | }); |
| 187 | |
| 188 | const randomDiffButton = demoUtils.createButton("Random Diff", () => { |
| 189 | interactiveSelector.diffLinesAdded = Math.floor(Math.random() * 100) + 1; |
| 190 | interactiveSelector.diffLinesRemoved = Math.floor(Math.random() * 50) + 1; |
| 191 | interactiveSelector.requestUpdate(); |
| 192 | updateInteractiveStatus(); |
| 193 | }); |
| 194 | |
| 195 | customControls.appendChild(addLinesButton); |
| 196 | customControls.appendChild(removeLinesButton); |
| 197 | customControls.appendChild(clearDiffButton); |
| 198 | customControls.appendChild(randomDiffButton); |
| 199 | |
| 200 | // Assemble the demo |
| 201 | basicSection.appendChild(basicContainer); |
| 202 | basicSection.appendChild(basicStatus); |
| 203 | |
| 204 | scenariosSection.appendChild(scenarioContainer); |
| 205 | |
| 206 | interactiveSection.appendChild(interactiveSelectorContainer); |
| 207 | |
| 208 | // Add test buttons for interactive section |
| 209 | createViewModeTestButtons(interactiveSelector, interactiveSection); |
| 210 | |
| 211 | interactiveSection.appendChild(customControls); |
| 212 | interactiveSection.appendChild(interactiveStatus); |
| 213 | |
| 214 | container.appendChild(basicSection); |
| 215 | container.appendChild(scenariosSection); |
| 216 | container.appendChild(interactiveSection); |
| 217 | |
| 218 | // Add container queries responsive testing section |
| 219 | const responsiveSection = demoUtils.createDemoSection( |
| 220 | "Container Query Responsive Testing", |
| 221 | "Demonstrates how the component behaves at different container sizes using Tailwind container queries", |
| 222 | ); |
| 223 | |
| 224 | // Create explanation text |
| 225 | const explanation = document.createElement("p"); |
| 226 | explanation.style.cssText = "margin: 10px 0; color: #666; font-size: 14px;"; |
| 227 | explanation.innerHTML = ` |
| 228 | <strong>Container Queries:</strong> The component now uses Tailwind <code>@container</code> queries instead of viewport media queries.<br> |
| 229 | This allows different sized containers to show different responsive behaviors simultaneously. |
| 230 | `; |
| 231 | responsiveSection.appendChild(explanation); |
| 232 | |
| 233 | // Create different sized container examples |
| 234 | const containerExamples = [ |
| 235 | { |
| 236 | title: "Extra Wide Container (700px)", |
| 237 | description: "Shows full text labels and complete layout", |
| 238 | width: "700px", |
| 239 | config: sampleViewModeConfigs.largeDiff, |
| 240 | containerClass: "w-[700px]", |
| 241 | borderColor: "border-green-500", |
| 242 | }, |
| 243 | { |
| 244 | title: "Very Narrow Container (250px)", |
| 245 | description: "Shows icons only (text hidden due to container query)", |
| 246 | width: "250px", |
| 247 | config: sampleViewModeConfigs.terminalActive, |
| 248 | containerClass: "w-[250px]", |
| 249 | borderColor: "border-orange-500", |
| 250 | }, |
| 251 | ]; |
| 252 | |
| 253 | const examplesContainer = document.createElement("div"); |
| 254 | examplesContainer.style.cssText = |
| 255 | "display: flex; flex-direction: column; gap: 20px; margin: 20px 0;"; |
| 256 | |
| philip.zeyliger | 26bc659 | 2025-06-30 20:15:30 -0700 | [diff] [blame] | 257 | containerExamples.forEach((example) => { |
| banksean | d5c849d | 2025-06-26 15:48:31 +0000 | [diff] [blame] | 258 | // Create container wrapper |
| 259 | const wrapper = document.createElement("div"); |
| 260 | wrapper.style.cssText = ` |
| 261 | border: 2px solid; |
| 262 | border-radius: 8px; |
| 263 | padding: 15px; |
| 264 | background: #f9f9f9; |
| 265 | `; |
| 266 | wrapper.className = example.borderColor; |
| 267 | |
| 268 | // Create title and description |
| 269 | const header = document.createElement("div"); |
| 270 | header.style.cssText = "margin-bottom: 10px;"; |
| 271 | header.innerHTML = ` |
| 272 | <h4 style="margin: 0 0 5px 0; font-weight: 600; color: #333;">${example.title}</h4> |
| 273 | <p style="margin: 0; font-size: 14px; color: #666;">${example.description}</p> |
| 274 | `; |
| 275 | wrapper.appendChild(header); |
| 276 | |
| 277 | // Create constrained container for the component |
| 278 | const componentContainer = document.createElement("div"); |
| 279 | componentContainer.className = "@container"; |
| 280 | componentContainer.style.cssText = ` |
| 281 | width: ${example.width}; |
| 282 | border: 1px dashed #ccc; |
| 283 | padding: 10px; |
| 284 | background: white; |
| 285 | border-radius: 4px; |
| 286 | `; |
| 287 | |
| 288 | // Create the component |
| 289 | const component = document.createElement( |
| 290 | "sketch-view-mode-select", |
| 291 | ) as any; |
| 292 | applyViewModeConfig(component, example.config); |
| 293 | |
| 294 | componentContainer.appendChild(component); |
| 295 | wrapper.appendChild(componentContainer); |
| 296 | examplesContainer.appendChild(wrapper); |
| 297 | }); |
| 298 | |
| 299 | responsiveSection.appendChild(examplesContainer); |
| 300 | |
| 301 | // Add interactive container size testing |
| 302 | const containerTestSection = document.createElement("div"); |
| 303 | containerTestSection.style.cssText = |
| 304 | "margin-top: 30px; padding-top: 20px; border-top: 1px solid #ddd;"; |
| 305 | |
| 306 | const interactiveTitle = document.createElement("h4"); |
| 307 | interactiveTitle.textContent = "Interactive Container Size Testing"; |
| 308 | interactiveTitle.style.cssText = "margin: 0 0 10px 0; font-weight: 600;"; |
| 309 | |
| 310 | const interactiveDesc = document.createElement("p"); |
| 311 | interactiveDesc.textContent = |
| 312 | "Use the buttons below to change the container size and see the responsive behavior in real-time."; |
| 313 | interactiveDesc.style.cssText = |
| 314 | "margin: 0 0 15px 0; color: #666; font-size: 14px;"; |
| 315 | |
| 316 | // Create interactive container |
| 317 | const interactiveContainer = document.createElement("div"); |
| 318 | interactiveContainer.className = "@container"; |
| 319 | interactiveContainer.style.cssText = ` |
| 320 | border: 2px solid #007acc; |
| 321 | border-radius: 8px; |
| 322 | padding: 15px; |
| 323 | background: #f0f8ff; |
| 324 | transition: width 0.3s ease; |
| 325 | width: 700px; |
| 326 | `; |
| 327 | |
| 328 | // Create interactive component |
| 329 | const interactiveComponent = document.createElement( |
| 330 | "sketch-view-mode-select", |
| 331 | ) as any; |
| 332 | applyViewModeConfig(interactiveComponent, sampleViewModeConfigs.largeDiff); |
| 333 | |
| 334 | // Size info display |
| 335 | const sizeInfo = document.createElement("div"); |
| 336 | sizeInfo.style.cssText = |
| 337 | "margin-bottom: 10px; font-family: monospace; font-size: 12px; color: #333;"; |
| 338 | sizeInfo.textContent = "Current container width: 700px"; |
| 339 | |
| 340 | interactiveContainer.appendChild(sizeInfo); |
| 341 | interactiveContainer.appendChild(interactiveComponent); |
| 342 | |
| 343 | // Control buttons |
| 344 | const controlButtons = document.createElement("div"); |
| 345 | controlButtons.style.cssText = |
| 346 | "margin-top: 15px; display: flex; gap: 8px; flex-wrap: wrap;"; |
| 347 | |
| 348 | const sizes = [ |
| 349 | { label: "Extra Wide (700px)", width: "700px" }, |
| 350 | { label: "Medium (400px)", width: "400px" }, |
| 351 | { label: "Very Narrow (250px)", width: "250px" }, |
| 352 | ]; |
| 353 | |
| 354 | sizes.forEach((size) => { |
| 355 | const button = demoUtils.createButton(size.label, () => { |
| 356 | interactiveContainer.style.width = size.width; |
| 357 | sizeInfo.textContent = `Current container width: ${size.width}`; |
| 358 | }); |
| 359 | controlButtons.appendChild(button); |
| 360 | }); |
| 361 | |
| 362 | containerTestSection.appendChild(interactiveTitle); |
| 363 | containerTestSection.appendChild(interactiveDesc); |
| 364 | containerTestSection.appendChild(interactiveContainer); |
| 365 | containerTestSection.appendChild(controlButtons); |
| 366 | |
| 367 | responsiveSection.appendChild(containerTestSection); |
| 368 | container.appendChild(responsiveSection); |
| 369 | }, |
| 370 | |
| 371 | cleanup: async () => { |
| 372 | // Clean up any event listeners or intervals if needed |
| 373 | console.log("Cleaning up sketch-view-mode-select demo"); |
| 374 | }, |
| 375 | }; |
| 376 | |
| 377 | export default demo; |