From 330f70240960382dfa94e7d1242b117befd1c5d6 Mon Sep 17 00:00:00 2001 From: Jan Andrle Date: Mon, 17 Mar 2025 12:53:50 +0100 Subject: [PATCH] :abc: examples+best paractises --- .../examples/case-studies/data-dashboard.js | 52 ++++++------------- .../examples/case-studies/image-gallery.js | 15 ++---- .../examples/case-studies/interactive-form.js | 23 ++++---- 3 files changed, 31 insertions(+), 59 deletions(-) diff --git a/docs/components/examples/case-studies/data-dashboard.js b/docs/components/examples/case-studies/data-dashboard.js index b588485..9aa78e9 100644 --- a/docs/components/examples/case-studies/data-dashboard.js +++ b/docs/components/examples/case-studies/data-dashboard.js @@ -23,14 +23,6 @@ export function DataDashboard() { conversion: [2.9, 3.5, 3.7, 2.6, 3.4, 3.5, 2.8, 2.8, 2.8, 3.1, 3.0, 2.7], months: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] }; - - // Application state - const selectedYear = S(2024); - const selectedDataType = S(/** @type {'sales' | 'visitors' | 'conversion'} */ ('sales')); - const isLoading = S(false); - const error = S(null); - - // Filter options const years = [2022, 2023, 2024]; const dataTypes = [ { id: 'sales', label: 'Sales', unit: 'K' }, @@ -38,42 +30,32 @@ export function DataDashboard() { { id: 'conversion', label: 'Conversion Rate', unit: '%' } ]; - // Computed values - const selectedData = S(() => { - return DATA[selectedDataType.get()]; - }); - - const currentDataType = S(() => { - return dataTypes.find(type => type.id === selectedDataType.get()); - }); - - const totalValue = S(() => { - const data = selectedData.get(); - return data.reduce((sum, value) => sum + value, 0); - }); - - const averageValue = S(() => { - const data = selectedData.get(); - return data.reduce((sum, value) => sum + value, 0) / data.length; - }); - - const highestValue = S(() => { - return Math.max(...selectedData.get()); - }); - - // Event handlers + // Filter options + const selectedYear = S(2024); const onYearChange = on("change", e => { selectedYear.set(parseInt(/** @type {HTMLSelectElement} */(e.target).value)); loadData(); }); - + const selectedDataType = S(/** @type {'sales' | 'visitors' | 'conversion'} */ ('sales')); const onDataTypeChange = on("click", e => { const type = /** @type {'sales' | 'visitors' | 'conversion'} */( /** @type {HTMLButtonElement} */(e.currentTarget).dataset.type); selectedDataType.set(type); }); + const currentDataType = S(() => dataTypes.find(type => type.id === selectedDataType.get())); + const selectedData = S(() => DATA[selectedDataType.get()]); + + // Values based on filters + const totalValue = S(() => selectedData.get().reduce((sum, value) => sum + value, 0)); + const averageValue = S(() => { + const data = selectedData.get(); + return data.reduce((sum, value) => sum + value, 0) / data.length; + }); + const highestValue = S(() => Math.max(...selectedData.get())); // Simulate data loading + const isLoading = S(false); + const error = S(null); function loadData() { isLoading.set(true); error.set(null); @@ -114,7 +96,7 @@ export function DataDashboard() { // Draw grid labels ctx.fillStyle = '#999'; ctx.font = '12px Arial'; - ctx.fillText(Math.round(maxValue * (i / 5)), 20, y + 5); + ctx.fillText(Math.round(maxValue * (i / 5)).toString(), 20, y + 5); } ctx.stroke(); @@ -154,7 +136,6 @@ export function DataDashboard() { ) ), - // Error message (only shown when there's an error) S.el(error, errorMsg => !errorMsg ? el() : el("div", { className: "error-message" }).append( @@ -163,7 +144,6 @@ export function DataDashboard() { ), ), - // Loading indicator S.el(isLoading, loading => !loading ? el() : el("div", { className: "loading-spinner" }) diff --git a/docs/components/examples/case-studies/image-gallery.js b/docs/components/examples/case-studies/image-gallery.js index e4dcec7..411e73a 100644 --- a/docs/components/examples/case-studies/image-gallery.js +++ b/docs/components/examples/case-studies/image-gallery.js @@ -27,25 +27,23 @@ const imagesSample = (url=> [ * @returns {HTMLElement} Gallery element */ export function ImageGallery(images= imagesSample) { - - // Application state - const selectedImageId = S(null); const filterTag = S('all'); const imagesToDisplay = S(() => { const tag = filterTag.get(); if (tag === 'all') return images; else return images.filter(img => img.alt.toLowerCase() === tag); }) + const onFilterChange = tag => on("click", () => { + filterTag.set(tag); + }); - // Derived state + // Lightbox + const selectedImageId = S(null); const selectedImage = S(() => { const id = selectedImageId.get(); return id ? images.find(img => img.id === id) : null; }); - const isLightboxOpen = S(() => selectedImage.get() !== null); - - // Event handlers const onImageClick = id => on("click", () => { selectedImageId.set(id); document.body.style.overflow = 'hidden'; // Prevent scrolling when lightbox is open @@ -76,9 +74,6 @@ export function ImageGallery(images= imagesSample) { const nextIndex = (currentIndex + 1) % images.length; selectedImageId.set(images[nextIndex].id); }; - const onFilterChange = tag => on("click", () => { - filterTag.set(tag); - }); // Keyboard navigation handler function handleKeyDown(e) { diff --git a/docs/components/examples/case-studies/interactive-form.js b/docs/components/examples/case-studies/interactive-form.js index 42b3ce1..0d2eced 100644 --- a/docs/components/examples/case-studies/interactive-form.js +++ b/docs/components/examples/case-studies/interactive-form.js @@ -73,8 +73,17 @@ export function Form({ initial }) { this.value[key] = value; } }); + /** + * Event handler for input events + * @param {"value"|"checked"} prop + * @returns {(ev: Event) => void} + * */ + const onChange= prop => ev => { + const input = /** @type {HTMLInputElement} */(ev.target); + S.action(formState, "update", /** @type {keyof FormState} */(input.id), input[prop]); + }; - // Derived signals for validation + // Form validate state const nameValid = S(() => formState.get().name.length >= 3); const emailValid = S(() => { const email = formState.get().email; @@ -89,8 +98,6 @@ export function Form({ initial }) { return password === confirmPassword && confirmPassword !== ''; }); const termsAgreed = S(() => formState.get().agreedToTerms); - - // Overall form validity const formValid = S(() => nameValid.get() && emailValid.get() && @@ -99,16 +106,6 @@ export function Form({ initial }) { termsAgreed.get() ); - // Event handlers - /** - * Event handler for input events - * @param {"value"|"checked"} prop - * @returns {(ev: Event) => void} - * */ - const onChange= prop => ev => { - const input = /** @type {HTMLInputElement} */(ev.target); - S.action(formState, "update", /** @type {keyof FormState} */(input.id), input[prop]); - }; const dispatcSubmit = dispatchEvent("form:submit", host); const onSubmit = on("submit", e => { e.preventDefault();