From ea7da1c9b953f98e0dd8ad7beb864379ae1c2a93 Mon Sep 17 00:00:00 2001 From: Juergen Kunz Date: Fri, 27 Jun 2025 16:39:17 +0000 Subject: [PATCH] update --- .../dees-input-quantityselector.demo.ts | 235 +++--------- ts_web/elements/dees-panel.demo.ts | 147 ++++++-- ts_web/elements/dees-panel.ts | 140 +++++++- .../dees-shopping-productcard.demo.ts | 332 +++++++++++++++++ ts_web/elements/dees-shopping-productcard.ts | 335 ++++++++++++++++++ ts_web/elements/index.ts | 1 + 6 files changed, 961 insertions(+), 229 deletions(-) create mode 100644 ts_web/elements/dees-shopping-productcard.demo.ts create mode 100644 ts_web/elements/dees-shopping-productcard.ts diff --git a/ts_web/elements/dees-input-quantityselector.demo.ts b/ts_web/elements/dees-input-quantityselector.demo.ts index c0488a2..bdd03b4 100644 --- a/ts_web/elements/dees-input-quantityselector.demo.ts +++ b/ts_web/elements/dees-input-quantityselector.demo.ts @@ -1,4 +1,5 @@ import { html, css, cssManager } from '@design.estate/dees-element'; +import './dees-shopping-productcard.js'; export const demoFunc = () => html` @@ -18,108 +19,6 @@ export const demoFunc = () => html` grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 20px; } - - .product-card { - background: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(215 20.2% 11.8%)')}; - border: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')}; - border-radius: 8px; - overflow: hidden; - transition: all 0.2s ease; - display: flex; - flex-direction: column; - } - - .product-card:hover { - border-color: ${cssManager.bdTheme('hsl(0 0% 79.8%)', 'hsl(0 0% 20.9%)')}; - box-shadow: 0 4px 6px -1px hsl(0 0% 0% / 0.1), 0 2px 4px -2px hsl(0 0% 0% / 0.1); - } - - .product-image { - width: 100%; - height: 180px; - background: ${cssManager.bdTheme('hsl(210 40% 96.1%)', 'hsl(215 20.2% 16.8%)')}; - display: flex; - align-items: center; - justify-content: center; - font-size: 48px; - color: ${cssManager.bdTheme('hsl(215 20.2% 65.1%)', 'hsl(215 20.2% 35.1%)')}; - } - - .product-content { - padding: 16px; - display: flex; - flex-direction: column; - gap: 12px; - flex: 1; - } - - .product-header { - display: flex; - flex-direction: column; - gap: 4px; - } - - .product-category { - font-size: 12px; - font-weight: 500; - color: ${cssManager.bdTheme('hsl(215.4 16.3% 56.9%)', 'hsl(215 20.2% 55.1%)')}; - text-transform: uppercase; - letter-spacing: 0.05em; - } - - .product-name { - font-size: 16px; - font-weight: 600; - color: ${cssManager.bdTheme('hsl(0 0% 9%)', 'hsl(0 0% 95%)')}; - line-height: 1.4; - } - - .product-description { - font-size: 13px; - color: ${cssManager.bdTheme('hsl(215.4 16.3% 46.9%)', 'hsl(215 20.2% 65.1%)')}; - line-height: 1.5; - flex: 1; - } - - .product-footer { - display: flex; - align-items: center; - justify-content: space-between; - gap: 16px; - padding-top: 12px; - border-top: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')}; - } - - .product-price { - display: flex; - flex-direction: column; - gap: 2px; - } - - .price-current { - font-size: 20px; - font-weight: 600; - color: ${cssManager.bdTheme('hsl(0 0% 9%)', 'hsl(0 0% 95%)')}; - } - - .price-original { - font-size: 14px; - color: ${cssManager.bdTheme('hsl(215.4 16.3% 56.9%)', 'hsl(215 20.2% 55.1%)')}; - text-decoration: line-through; - } - - .in-stock { - display: inline-flex; - align-items: center; - gap: 4px; - font-size: 12px; - color: ${cssManager.bdTheme('hsl(142.1 70.6% 45.3%)', 'hsl(142.1 76.2% 36.3%)')}; - margin-top: 8px; - } - - .in-stock dees-icon { - font-size: 14px; - } .cart-summary { margin-top: 24px; @@ -176,9 +75,13 @@ export const demoFunc = () => html` { const updateCartSummary = () => { - const qty1 = (elementArg.querySelector('#headphones-qty') as any)?.getValue() || 0; - const qty2 = (elementArg.querySelector('#mouse-qty') as any)?.getValue() || 0; - const qty3 = (elementArg.querySelector('#keyboard-qty') as any)?.getValue() || 0; + const card1 = elementArg.querySelector('#headphones-qty') as any; + const card2 = elementArg.querySelector('#mouse-qty') as any; + const card3 = elementArg.querySelector('#keyboard-qty') as any; + + const qty1 = card1?.quantity || 0; + const qty2 = card2?.quantity || 0; + const qty3 = card3?.quantity || 0; const price1 = 349.99 * qty1; const price2 = 99.99 * qty2; @@ -213,96 +116,48 @@ export const demoFunc = () => html` setTimeout(updateCartSummary, 100); // Set up listeners - elementArg.querySelectorAll('dees-input-quantityselector').forEach(selector => { - selector.addEventListener('changeSubject', updateCartSummary); + elementArg.querySelectorAll('dees-shopping-productcard').forEach(card => { + card.addEventListener('quantityChange', updateCartSummary); }); }}>
-
-
- -
-
-
-
Audio
-
Sony WH-1000XM5 Wireless Headphones
-
-
- Industry-leading noise canceling with Auto NC Optimizer -
-
- - In Stock -
- -
-
+ -
-
- -
-
-
-
Accessories
-
Logitech MX Master 3S
-
-
- Performance wireless mouse with ultra-fast scrolling -
-
- - In Stock -
- -
-
+ -
-
- -
-
-
-
Keyboards
-
Keychron K2 Wireless Mechanical Keyboard
-
-
- Compact 75% layout with hot-swappable switches -
-
- - In Stock -
- -
-
+
diff --git a/ts_web/elements/dees-panel.demo.ts b/ts_web/elements/dees-panel.demo.ts index d29ebbd..ff0fb5c 100644 --- a/ts_web/elements/dees-panel.demo.ts +++ b/ts_web/elements/dees-panel.demo.ts @@ -5,7 +5,7 @@ export const demoFunc = () => html` ${css` .demo-background { padding: 24px; - background: ${cssManager.bdTheme('#f0f0f0', '#0a0a0a')}; + background: ${cssManager.bdTheme('hsl(0 0% 95%)', 'hsl(0 0% 5%)')}; min-height: 100vh; } @@ -17,65 +17,156 @@ export const demoFunc = () => html` gap: 24px; } + .section-title { + font-size: 24px; + font-weight: 700; + margin: 32px 0 16px 0; + color: ${cssManager.bdTheme('hsl(0 0% 9%)', 'hsl(0 0% 95%)')}; + letter-spacing: -0.025em; + } + + .section-title:first-child { + margin-top: 0; + } + .grid-layout { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; } + .grid-3col { + display: grid; + grid-template-columns: 1fr 1fr 1fr; + gap: 24px; + } + + @media (max-width: 968px) { + .grid-3col { + grid-template-columns: 1fr; + } + } + @media (max-width: 768px) { .grid-layout { grid-template-columns: 1fr; } } + + code { + background: ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')}; + padding: 2px 6px; + border-radius: 3px; + font-size: 13px; + } `}
- +

Default Panels

+ +

The panel component automatically follows the theme and provides consistent styling for grouped content.

It's perfect for creating sections in your application with proper spacing and borders.

- -

Panels can have both a title and subtitle to provide more context.

-

The subtitle appears in a smaller, muted text below the title.

-
-
- +

Grid layouts work great with panels for creating dashboards and feature sections.

- Action + Learn More
- -

Each panel maintains consistent spacing and styling.

- Another Action + +

Each panel maintains consistent spacing and styling across your application.

+ Get Started
- -

Nested Elements

-

Panels can contain any type of content:

-
    -
  • Text and paragraphs
  • -
  • Lists and tables
  • -
  • Form inputs
  • -
  • Other components
  • -
- - - -
- Submit -
+

Panel Variants

+ + +

The default variant has a white background, subtle border, and minimal shadow. It's the standard choice for most content.

+

Use variant="default" or omit the variant property.

+ +

The outline variant removes the background color and shadow, keeping only the border.

+

Use variant="outline" for a lighter visual weight.

+
+ + +

The ghost variant has no border or background by default, only showing a subtle background on hover.

+

Use variant="ghost" for the most minimal appearance.

+
+ +

Panel Sizes

+ +
+ +

Compact padding for dense layouts.

+

Use size="sm"

+
+ + +

Default size with balanced spacing.

+

Use size="md" or omit.

+
+ + +

Generous padding for prominent sections.

+

Use size="lg"

+
+
+ +

Complex Examples

+ + + + + + + Create Project + + + + +

You can nest panels to create more complex layouts:

+ + +

This is a nested panel with outline variant and small size.

+
+ + +

This is another nested panel with ghost variant.

+
+
+ +

Untitled Panels

+

Panels work great even without a title for simple content grouping.

-

They provide visual separation and consistent padding.

+

They provide visual separation and consistent padding throughout your interface.

+ +
+ +

Custom Content

+

You can add your own headings and structure within untitled panels.

+
+ + +

Minimal Style

+

Ghost panels without titles create very subtle content sections.

+
+
`; \ No newline at end of file diff --git a/ts_web/elements/dees-panel.ts b/ts_web/elements/dees-panel.ts index 4f0994a..a309fef 100644 --- a/ts_web/elements/dees-panel.ts +++ b/ts_web/elements/dees-panel.ts @@ -25,35 +25,112 @@ export class DeesPanel extends DeesElement { @property({ type: String }) public subtitle: string = ''; + @property({ type: String }) + public variant: 'default' | 'outline' | 'ghost' = 'default'; + + @property({ type: String }) + public size: 'sm' | 'md' | 'lg' = 'md'; + + @property({ attribute: false }) + public runAfterRender?: (elementArg: HTMLElement) => void | Promise; + public static styles = [ cssManager.defaultStyles, css` :host { display: block; - background: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(215 20.2% 11.8%)')}; - border-radius: 8px; + background: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(0 0% 3.9%)')}; + border-radius: 6px; padding: 24px; - border: 1px solid ${cssManager.bdTheme('hsl(214.3 31.8% 91.4%)', 'hsl(215 20.2% 16.8%)')}; - box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); + border: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')}; + transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); + } + + /* Variant: default */ + :host([variant="default"]) { + box-shadow: 0 1px 2px 0 hsl(0 0% 0% / 0.05); + } + + /* Variant: outline */ + :host([variant="outline"]) { + background: transparent; + box-shadow: none; + } + + /* Variant: ghost */ + :host([variant="ghost"]) { + background: transparent; + border-color: transparent; + box-shadow: none; + padding: 16px; + } + + /* Size variations */ + :host([size="sm"]) { + padding: 16px; + } + + :host([size="lg"]) { + padding: 32px; + } + + .header { + margin-bottom: 16px; + } + + .header:empty { + display: none; } .title { - margin: 0 0 8px 0; + margin: 0; font-size: 18px; font-weight: 600; - color: ${cssManager.bdTheme('hsl(215.3 25% 8.8%)', 'hsl(210 40% 98%)')}; + color: ${cssManager.bdTheme('hsl(0 0% 9%)', 'hsl(0 0% 95%)')}; letter-spacing: -0.025em; + line-height: 1.5; + } + + /* Title size variations */ + :host([size="sm"]) .title { + font-size: 16px; + } + + :host([size="lg"]) .title { + font-size: 20px; } .subtitle { - margin: 0 0 16px 0; + margin: 4px 0 0 0; font-size: 14px; color: ${cssManager.bdTheme('hsl(215.4 16.3% 56.9%)', 'hsl(215 20.2% 55.1%)')}; - letter-spacing: -0.01em; + letter-spacing: -0.006em; + line-height: 1.5; + } + + /* Subtitle size variations */ + :host([size="sm"]) .subtitle { + font-size: 13px; + } + + :host([size="lg"]) .subtitle { + font-size: 15px; + margin-top: 6px; } .content { - color: ${cssManager.bdTheme('hsl(215.3 25% 8.8%)', 'hsl(210 40% 98%)')}; + color: ${cssManager.bdTheme('hsl(215.3 25% 26.7%)', 'hsl(217.9 10.6% 84.9%)')}; + font-size: 14px; + line-height: 1.6; + } + + /* Content size variations */ + :host([size="sm"]) .content { + font-size: 13px; + } + + :host([size="lg"]) .content { + font-size: 15px; } /* Remove margins from first and last children */ @@ -64,16 +141,57 @@ export class DeesPanel extends DeesElement { .content ::slotted(*:last-child) { margin-bottom: 0; } + + /* Interactive states for default variant */ + :host([variant="default"]:hover) { + border-color: ${cssManager.bdTheme('hsl(0 0% 79.8%)', 'hsl(0 0% 20.9%)')}; + box-shadow: 0 4px 6px -1px hsl(0 0% 0% / 0.1), 0 2px 4px -2px hsl(0 0% 0% / 0.1); + } + + /* Interactive states for outline variant */ + :host([variant="outline"]:hover) { + border-color: ${cssManager.bdTheme('hsl(0 0% 79.8%)', 'hsl(0 0% 20.9%)')}; + background: ${cssManager.bdTheme('hsl(0 0% 98%)', 'hsl(0 0% 7.8%)')}; + } + + /* Interactive states for ghost variant */ + :host([variant="ghost"]:hover) { + background: ${cssManager.bdTheme('hsl(0 0% 95.1%)', 'hsl(0 0% 14.9%)')}; + } + + /* Focus states */ + :host(:focus-within) { + outline: none; + border-color: ${cssManager.bdTheme('hsl(222.2 47.4% 51.2%)', 'hsl(217.2 91.2% 59.8%)')}; + box-shadow: 0 0 0 3px ${cssManager.bdTheme('hsl(222.2 47.4% 51.2% / 0.1)', 'hsl(217.2 91.2% 59.8% / 0.1)')}; + } + + /* Nested panels spacing */ + ::slotted(dees-panel) { + margin-top: 16px; + } + + ::slotted(dees-panel:first-child) { + margin-top: 0; + } `, ]; public render(): TemplateResult { return html` - ${this.title ? html`

${this.title}

` : ''} - ${this.subtitle ? html`

${this.subtitle}

` : ''} +
+ ${this.title ? html`

${this.title}

` : ''} + ${this.subtitle ? html`

${this.subtitle}

` : ''} +
`; } + + public async firstUpdated() { + if (this.runAfterRender) { + await this.runAfterRender(this); + } + } } \ No newline at end of file diff --git a/ts_web/elements/dees-shopping-productcard.demo.ts b/ts_web/elements/dees-shopping-productcard.demo.ts new file mode 100644 index 0000000..567f68c --- /dev/null +++ b/ts_web/elements/dees-shopping-productcard.demo.ts @@ -0,0 +1,332 @@ +import { html, css, cssManager } from '@design.estate/dees-element'; +import '@design.estate/dees-wcctools/demotools'; +import './dees-panel.js'; +import type { DeesShoppingProductcard } from './dees-shopping-productcard.js'; + +export const demoFunc = () => html` + + + +
+ +
+ + + + + +
+
+ + { + const products = [ + { id: 'laptop', element: null, data: { name: 'MacBook Pro 14"', category: 'Computers', description: 'M3 Pro chip with 18GB RAM', price: 1999, originalPrice: 2199, iconName: 'lucide:laptop' }}, + { id: 'ipad', element: null, data: { name: 'iPad Air', category: 'Tablets', description: '10.9" Liquid Retina display', price: 599, iconName: 'lucide:tablet' }}, + { id: 'keyboard', element: null, data: { name: 'Magic Keyboard', category: 'Accessories', description: 'Wireless keyboard with Touch ID', price: 149, iconName: 'lucide:keyboard' }} + ]; + + const updateCartSummary = () => { + let total = 0; + const items = []; + + products.forEach(product => { + const element = elementArg.querySelector(`#${product.id}`) as DeesShoppingProductcard; + if (element && element.quantity > 0) { + const subtotal = product.data.price * element.quantity; + total += subtotal; + items.push(` +
+ ${product.data.name} (${element.quantity}) + $${subtotal.toFixed(2)} +
+ `); + } + }); + + const summary = elementArg.querySelector('#interactive-cart-summary'); + if (summary) { + summary.innerHTML = ` + ${items.join('')} + ${items.length === 0 ? '
Your cart is empty
' : ''} +
+ Total + $${total.toFixed(2)} +
+ `; + } + }; + + // Initial update + setTimeout(updateCartSummary, 100); + + // Set up listeners + elementArg.querySelectorAll('dees-shopping-productcard').forEach(card => { + card.addEventListener('quantityChange', updateCartSummary); + }); + }}> +
+ + + + + +
+ +
+

Shopping Cart

+
+ +
+
+
+ + +
+ { + const output = document.querySelector('#selection-output'); + if (output) { + const selectedCards = document.querySelectorAll('dees-shopping-productcard[selectable]'); + const selectedProducts = []; + selectedCards.forEach((card: DeesShoppingProductcard) => { + if (card.selected) { + selectedProducts.push(card.productData.name); + } + }); + output.textContent = selectedProducts.length > 0 + ? `Selected: ${selectedProducts.join(', ')}` + : 'No products selected'; + } + }} + > + + { + const output = document.querySelector('#selection-output'); + if (output) { + const selectedCards = document.querySelectorAll('dees-shopping-productcard[selectable]'); + const selectedProducts = []; + selectedCards.forEach((card: DeesShoppingProductcard) => { + if (card.selected) { + selectedProducts.push(card.productData.name); + } + }); + output.textContent = selectedProducts.length > 0 + ? `Selected: ${selectedProducts.join(', ')}` + : 'No products selected'; + } + }} + > + + { + const output = document.querySelector('#selection-output'); + if (output) { + const selectedCards = document.querySelectorAll('dees-shopping-productcard[selectable]'); + const selectedProducts = []; + selectedCards.forEach((card: DeesShoppingProductcard) => { + if (card.selected) { + selectedProducts.push(card.productData.name); + } + }); + output.textContent = selectedProducts.length > 0 + ? `Selected: ${selectedProducts.join(', ')}` + : 'No products selected'; + } + }} + > +
+ +
+ No products selected +
+
+ + +
+ + + + + +
+
+
+
+`; \ No newline at end of file diff --git a/ts_web/elements/dees-shopping-productcard.ts b/ts_web/elements/dees-shopping-productcard.ts new file mode 100644 index 0000000..df7a5d3 --- /dev/null +++ b/ts_web/elements/dees-shopping-productcard.ts @@ -0,0 +1,335 @@ +import { + customElement, + property, + html, + css, + cssManager, + type TemplateResult, + DeesElement, +} from '@design.estate/dees-element'; +import { demoFunc } from './dees-shopping-productcard.demo.js'; + +declare global { + interface HTMLElementTagNameMap { + 'dees-shopping-productcard': DeesShoppingProductcard; + } +} + +export interface IProductData { + name: string; + category?: string; + description?: string; + price: number; + originalPrice?: number; + currency?: string; + inStock?: boolean; + stockText?: string; + imageUrl?: string; + iconName?: string; +} + +@customElement('dees-shopping-productcard') +export class DeesShoppingProductcard extends DeesElement { + public static demo = demoFunc; + + @property({ type: Object }) + public productData: IProductData = { + name: 'Product Name', + price: 0, + }; + + @property({ type: Number }) + public quantity: number = 0; + + @property({ type: Boolean }) + public showQuantitySelector: boolean = true; + + @property({ type: Boolean }) + public selectable: boolean = false; + + @property({ type: Boolean }) + public selected: boolean = false; + + public static styles = [ + cssManager.defaultStyles, + css` + :host { + display: block; + } + + .product-card { + background: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(215 20.2% 11.8%)')}; + border: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')}; + border-radius: 8px; + overflow: hidden; + transition: all 0.2s ease; + display: flex; + flex-direction: column; + height: 100%; + position: relative; + } + + .product-card:hover { + border-color: ${cssManager.bdTheme('hsl(0 0% 79.8%)', 'hsl(0 0% 20.9%)')}; + box-shadow: 0 4px 6px -1px hsl(0 0% 0% / 0.1), 0 2px 4px -2px hsl(0 0% 0% / 0.1); + } + + .product-card.selectable { + cursor: pointer; + } + + .product-card.selected { + border-color: ${cssManager.bdTheme('hsl(217.2 91.2% 59.8%)', 'hsl(213.1 93.9% 67.8%)')}; + box-shadow: 0 0 0 3px ${cssManager.bdTheme('hsl(217.2 91.2% 59.8% / 0.1)', 'hsl(213.1 93.9% 67.8% / 0.1)')}; + } + + .product-image { + width: 100%; + height: 180px; + background: ${cssManager.bdTheme('hsl(210 40% 96.1%)', 'hsl(215 20.2% 16.8%)')}; + display: flex; + align-items: center; + justify-content: center; + position: relative; + overflow: hidden; + } + + .product-image img { + width: 100%; + height: 100%; + object-fit: cover; + } + + .product-image dees-icon { + font-size: 48px; + color: ${cssManager.bdTheme('hsl(215 20.2% 65.1%)', 'hsl(215 20.2% 35.1%)')}; + } + + .selection-checkbox { + position: absolute; + top: 12px; + right: 12px; + width: 20px; + height: 20px; + background: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(215 30% 6.8%)')}; + border: 2px solid ${cssManager.bdTheme('hsl(215 20.2% 65.1%)', 'hsl(215 20.2% 35.1%)')}; + border-radius: 4px; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s ease; + cursor: pointer; + } + + .selection-checkbox.checked { + background: ${cssManager.bdTheme('hsl(217.2 91.2% 59.8%)', 'hsl(213.1 93.9% 67.8%)')}; + border-color: ${cssManager.bdTheme('hsl(217.2 91.2% 59.8%)', 'hsl(213.1 93.9% 67.8%)')}; + } + + .selection-checkbox dees-icon { + color: white; + font-size: 12px; + opacity: 0; + transform: scale(0); + transition: all 0.2s ease; + } + + .selection-checkbox.checked dees-icon { + opacity: 1; + transform: scale(1); + } + + .product-content { + padding: 16px; + display: flex; + flex-direction: column; + gap: 12px; + flex: 1; + } + + .product-header { + display: flex; + flex-direction: column; + gap: 4px; + } + + .product-category { + font-size: 12px; + font-weight: 500; + color: ${cssManager.bdTheme('hsl(215.4 16.3% 56.9%)', 'hsl(215 20.2% 55.1%)')}; + text-transform: uppercase; + letter-spacing: 0.05em; + line-height: 1.3; + } + + .product-name { + font-size: 16px; + font-weight: 600; + color: ${cssManager.bdTheme('hsl(0 0% 9%)', 'hsl(0 0% 95%)')}; + line-height: 1.4; + } + + .product-description { + font-size: 13px; + color: ${cssManager.bdTheme('hsl(215.4 16.3% 46.9%)', 'hsl(215 20.2% 65.1%)')}; + line-height: 1.5; + flex: 1; + } + + .product-footer { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + padding-top: 12px; + border-top: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')}; + } + + .product-price { + display: flex; + flex-direction: column; + gap: 2px; + } + + .price-current { + font-size: 20px; + font-weight: 600; + color: ${cssManager.bdTheme('hsl(0 0% 9%)', 'hsl(0 0% 95%)')}; + } + + .price-original { + font-size: 14px; + color: ${cssManager.bdTheme('hsl(215.4 16.3% 56.9%)', 'hsl(215 20.2% 55.1%)')}; + text-decoration: line-through; + } + + .stock-status { + display: inline-flex; + align-items: center; + gap: 4px; + font-size: 12px; + margin-top: 8px; + } + + .stock-status.in-stock { + color: ${cssManager.bdTheme('hsl(142.1 70.6% 45.3%)', 'hsl(142.1 76.2% 36.3%)')}; + } + + .stock-status.out-of-stock { + color: ${cssManager.bdTheme('hsl(0 72.2% 50.6%)', 'hsl(0 62.8% 30.6%)')}; + } + + .stock-status dees-icon { + font-size: 14px; + } + `, + ]; + + public render(): TemplateResult { + const { + name, + category, + description, + price, + originalPrice, + currency = '$', + inStock = true, + stockText = inStock ? 'In Stock' : 'Out of Stock', + imageUrl, + iconName = 'lucide:package', + } = this.productData; + + const formatPrice = (value: number) => { + return `${currency}${value.toFixed(2)}`; + }; + + return html` +
+
+ ${imageUrl ? html` + ${name} + ` : html` + + `} + ${this.selectable ? html` +
{ + e.stopPropagation(); + this.handleSelectionToggle(); + }} + > + +
+ ` : ''} +
+
+
+ ${category ? html`
${category}
` : ''} +
${name}
+
+ ${description ? html` +
${description}
+ ` : ''} +
+ + ${stockText} +
+ +
+
+ `; + } + + private handleCardClick() { + if (this.selectable) { + this.selected = !this.selected; + this.dispatchEvent(new CustomEvent('selectionChange', { + detail: { + selected: this.selected, + productData: this.productData + }, + bubbles: true, + composed: true + })); + } + } + + private handleSelectionToggle() { + this.selected = !this.selected; + this.dispatchEvent(new CustomEvent('selectionChange', { + detail: { + selected: this.selected, + productData: this.productData + }, + bubbles: true, + composed: true + })); + } +} \ No newline at end of file diff --git a/ts_web/elements/index.ts b/ts_web/elements/index.ts index 3ad11b8..87e0f77 100644 --- a/ts_web/elements/index.ts +++ b/ts_web/elements/index.ts @@ -46,6 +46,7 @@ export * from './dees-input-multitoggle.js'; export * from './dees-panel.js'; export * from './dees-pdf.js'; export * from './dees-searchbar.js'; +export * from './dees-shopping-productcard.js'; export * from './dees-simple-appdash.js'; export * from './dees-simple-login.js'; export * from './dees-speechbubble.js';