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