update
This commit is contained in:
@ -1,4 +1,5 @@
|
||||
import { html, css, cssManager } from '@design.estate/dees-element';
|
||||
import './dees-shopping-productcard.js';
|
||||
|
||||
export const demoFunc = () => html`
|
||||
<dees-demowrapper>
|
||||
@ -19,108 +20,6 @@ export const demoFunc = () => html`
|
||||
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;
|
||||
padding: 20px;
|
||||
@ -176,9 +75,13 @@ export const demoFunc = () => html`
|
||||
|
||||
<dees-panel .title=${'Shopping Cart'} .subtitle=${'Modern e-commerce product cards with interactive quantity selectors'} .runAfterRender=${async (elementArg: HTMLElement) => {
|
||||
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);
|
||||
});
|
||||
}}>
|
||||
<div class="shopping-grid">
|
||||
<div class="product-card">
|
||||
<div class="product-image">
|
||||
<dees-icon .iconName=${'lucide:headphones'}></dees-icon>
|
||||
</div>
|
||||
<div class="product-content">
|
||||
<div class="product-header">
|
||||
<div class="product-category">Audio</div>
|
||||
<div class="product-name">Sony WH-1000XM5 Wireless Headphones</div>
|
||||
</div>
|
||||
<div class="product-description">
|
||||
Industry-leading noise canceling with Auto NC Optimizer
|
||||
</div>
|
||||
<div class="in-stock">
|
||||
<dees-icon .iconName=${'lucide:check-circle'}></dees-icon>
|
||||
In Stock
|
||||
</div>
|
||||
<div class="product-footer">
|
||||
<div class="product-price">
|
||||
<span class="price-current">$349.99</span>
|
||||
<span class="price-original">$399.99</span>
|
||||
</div>
|
||||
<dees-input-quantityselector
|
||||
<dees-shopping-productcard
|
||||
id="headphones-qty"
|
||||
.value=${1}
|
||||
></dees-input-quantityselector>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
.productData=${{
|
||||
name: 'Sony WH-1000XM5 Wireless Headphones',
|
||||
category: 'Audio',
|
||||
description: 'Industry-leading noise canceling with Auto NC Optimizer',
|
||||
price: 349.99,
|
||||
originalPrice: 399.99,
|
||||
iconName: 'lucide:headphones'
|
||||
}}
|
||||
.quantity=${1}
|
||||
></dees-shopping-productcard>
|
||||
|
||||
<div class="product-card">
|
||||
<div class="product-image">
|
||||
<dees-icon .iconName=${'lucide:mouse-pointer'}></dees-icon>
|
||||
</div>
|
||||
<div class="product-content">
|
||||
<div class="product-header">
|
||||
<div class="product-category">Accessories</div>
|
||||
<div class="product-name">Logitech MX Master 3S</div>
|
||||
</div>
|
||||
<div class="product-description">
|
||||
Performance wireless mouse with ultra-fast scrolling
|
||||
</div>
|
||||
<div class="in-stock">
|
||||
<dees-icon .iconName=${'lucide:check-circle'}></dees-icon>
|
||||
In Stock
|
||||
</div>
|
||||
<div class="product-footer">
|
||||
<div class="product-price">
|
||||
<span class="price-current">$99.99</span>
|
||||
</div>
|
||||
<dees-input-quantityselector
|
||||
<dees-shopping-productcard
|
||||
id="mouse-qty"
|
||||
.value=${2}
|
||||
></dees-input-quantityselector>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
.productData=${{
|
||||
name: 'Logitech MX Master 3S',
|
||||
category: 'Accessories',
|
||||
description: 'Performance wireless mouse with ultra-fast scrolling',
|
||||
price: 99.99,
|
||||
iconName: 'lucide:mouse-pointer'
|
||||
}}
|
||||
.quantity=${2}
|
||||
></dees-shopping-productcard>
|
||||
|
||||
<div class="product-card">
|
||||
<div class="product-image">
|
||||
<dees-icon .iconName=${'lucide:keyboard'}></dees-icon>
|
||||
</div>
|
||||
<div class="product-content">
|
||||
<div class="product-header">
|
||||
<div class="product-category">Keyboards</div>
|
||||
<div class="product-name">Keychron K2 Wireless Mechanical Keyboard</div>
|
||||
</div>
|
||||
<div class="product-description">
|
||||
Compact 75% layout with hot-swappable switches
|
||||
</div>
|
||||
<div class="in-stock">
|
||||
<dees-icon .iconName=${'lucide:check-circle'}></dees-icon>
|
||||
In Stock
|
||||
</div>
|
||||
<div class="product-footer">
|
||||
<div class="product-price">
|
||||
<span class="price-current">$79.99</span>
|
||||
<span class="price-original">$94.99</span>
|
||||
</div>
|
||||
<dees-input-quantityselector
|
||||
<dees-shopping-productcard
|
||||
id="keyboard-qty"
|
||||
.value=${1}
|
||||
></dees-input-quantityselector>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
.productData=${{
|
||||
name: 'Keychron K2 Wireless Mechanical Keyboard',
|
||||
category: 'Keyboards',
|
||||
description: 'Compact 75% layout with hot-swappable switches',
|
||||
price: 79.99,
|
||||
originalPrice: 94.99,
|
||||
iconName: 'lucide:keyboard'
|
||||
}}
|
||||
.quantity=${1}
|
||||
></dees-shopping-productcard>
|
||||
</div>
|
||||
|
||||
<div class="cart-summary">
|
||||
|
@ -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;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
|
||||
<div class="demo-background">
|
||||
<div class="demo-container">
|
||||
<dees-panel .title=${'Panel Component'}>
|
||||
<h2 class="section-title">Default Panels</h2>
|
||||
|
||||
<dees-panel .title=${'Panel Component'} .subtitle=${'The default panel variant with shadcn-inspired styling'}>
|
||||
<p>The panel component automatically follows the theme and provides consistent styling for grouped content.</p>
|
||||
<p>It's perfect for creating sections in your application with proper spacing and borders.</p>
|
||||
</dees-panel>
|
||||
|
||||
<dees-panel .title=${'Panel with Subtitle'} .subtitle=${'Additional context information'}>
|
||||
<p>Panels can have both a title and subtitle to provide more context.</p>
|
||||
<p>The subtitle appears in a smaller, muted text below the title.</p>
|
||||
</dees-panel>
|
||||
|
||||
<div class="grid-layout">
|
||||
<dees-panel .title=${'Feature 1'}>
|
||||
<dees-panel .title=${'Feature Overview'} .subtitle=${'Key capabilities'}>
|
||||
<p>Grid layouts work great with panels for creating dashboards and feature sections.</p>
|
||||
<dees-button>Action</dees-button>
|
||||
<dees-button>Learn More</dees-button>
|
||||
</dees-panel>
|
||||
|
||||
<dees-panel .title=${'Feature 2'}>
|
||||
<p>Each panel maintains consistent spacing and styling.</p>
|
||||
<dees-button>Another Action</dees-button>
|
||||
<dees-panel .title=${'Quick Actions'} .subtitle=${'Common tasks'}>
|
||||
<p>Each panel maintains consistent spacing and styling across your application.</p>
|
||||
<dees-button>Get Started</dees-button>
|
||||
</dees-panel>
|
||||
</div>
|
||||
|
||||
<dees-panel .title=${'Complex Content'}>
|
||||
<h4>Nested Elements</h4>
|
||||
<p>Panels can contain any type of content:</p>
|
||||
<ul>
|
||||
<li>Text and paragraphs</li>
|
||||
<li>Lists and tables</li>
|
||||
<li>Form inputs</li>
|
||||
<li>Other components</li>
|
||||
</ul>
|
||||
<h2 class="section-title">Panel Variants</h2>
|
||||
|
||||
<dees-input-text .label=${'Example Input'} .description=${'Input inside a panel'}></dees-input-text>
|
||||
|
||||
<div style="margin-top: 16px;">
|
||||
<dees-button>Submit</dees-button>
|
||||
</div>
|
||||
<dees-panel .title=${'Default Variant'} .variant=${'default'}>
|
||||
<p>The default variant has a white background, subtle border, and minimal shadow. It's the standard choice for most content.</p>
|
||||
<p>Use <code>variant="default"</code> or omit the variant property.</p>
|
||||
</dees-panel>
|
||||
|
||||
<dees-panel .title=${'Outline Variant'} .subtitle=${'Transparent background with border'} .variant=${'outline'}>
|
||||
<p>The outline variant removes the background color and shadow, keeping only the border.</p>
|
||||
<p>Use <code>variant="outline"</code> for a lighter visual weight.</p>
|
||||
</dees-panel>
|
||||
|
||||
<dees-panel .title=${'Ghost Variant'} .subtitle=${'Minimal styling for subtle sections'} .variant=${'ghost'}>
|
||||
<p>The ghost variant has no border or background by default, only showing a subtle background on hover.</p>
|
||||
<p>Use <code>variant="ghost"</code> for the most minimal appearance.</p>
|
||||
</dees-panel>
|
||||
|
||||
<h2 class="section-title">Panel Sizes</h2>
|
||||
|
||||
<div class="grid-3col">
|
||||
<dees-panel .title=${'Small Panel'} .size=${'sm'}>
|
||||
<p>Compact padding for dense layouts.</p>
|
||||
<p>Use <code>size="sm"</code></p>
|
||||
</dees-panel>
|
||||
|
||||
<dees-panel .title=${'Medium Panel'} .size=${'md'}>
|
||||
<p>Default size with balanced spacing.</p>
|
||||
<p>Use <code>size="md"</code> or omit.</p>
|
||||
</dees-panel>
|
||||
|
||||
<dees-panel .title=${'Large Panel'} .size=${'lg'}>
|
||||
<p>Generous padding for prominent sections.</p>
|
||||
<p>Use <code>size="lg"</code></p>
|
||||
</dees-panel>
|
||||
</div>
|
||||
|
||||
<h2 class="section-title">Complex Examples</h2>
|
||||
|
||||
<dees-panel .title=${'Form Example'} .subtitle=${'Panels work great for organizing form sections'}>
|
||||
<dees-form>
|
||||
<dees-input-text .label=${'Project Name'} .required=${true}></dees-input-text>
|
||||
<dees-input-text .label=${'Description'} .inputType=${'textarea'}></dees-input-text>
|
||||
<dees-input-dropdown
|
||||
.label=${'Category'}
|
||||
.options=${[
|
||||
{ option: 'Web Development', key: 'web' },
|
||||
{ option: 'Mobile App', key: 'mobile' },
|
||||
{ option: 'Desktop Software', key: 'desktop' }
|
||||
]}
|
||||
></dees-input-dropdown>
|
||||
<dees-form-submit>Create Project</dees-form-submit>
|
||||
</dees-form>
|
||||
</dees-panel>
|
||||
|
||||
<dees-panel .title=${'Nested Panels'} .subtitle=${'Panels can be nested for hierarchical organization'}>
|
||||
<p>You can nest panels to create more complex layouts:</p>
|
||||
|
||||
<dees-panel .title=${'Nested Panel 1'} .variant=${'outline'} .size=${'sm'}>
|
||||
<p>This is a nested panel with outline variant and small size.</p>
|
||||
</dees-panel>
|
||||
|
||||
<dees-panel .title=${'Nested Panel 2'} .variant=${'ghost'} .size=${'sm'}>
|
||||
<p>This is another nested panel with ghost variant.</p>
|
||||
</dees-panel>
|
||||
</dees-panel>
|
||||
|
||||
<h2 class="section-title">Untitled Panels</h2>
|
||||
|
||||
<dees-panel>
|
||||
<p>Panels work great even without a title for simple content grouping.</p>
|
||||
<p>They provide visual separation and consistent padding.</p>
|
||||
<p>They provide visual separation and consistent padding throughout your interface.</p>
|
||||
</dees-panel>
|
||||
|
||||
<div class="grid-layout">
|
||||
<dees-panel .variant=${'outline'}>
|
||||
<h4 style="margin-top: 0;">Custom Content</h4>
|
||||
<p>You can add your own headings and structure within untitled panels.</p>
|
||||
</dees-panel>
|
||||
|
||||
<dees-panel .variant=${'ghost'}>
|
||||
<h4 style="margin-top: 0;">Minimal Style</h4>
|
||||
<p>Ghost panels without titles create very subtle content sections.</p>
|
||||
</dees-panel>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
@ -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<void>;
|
||||
|
||||
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`
|
||||
<div class="header">
|
||||
${this.title ? html`<h3 class="title">${this.title}</h3>` : ''}
|
||||
${this.subtitle ? html`<p class="subtitle">${this.subtitle}</p>` : ''}
|
||||
</div>
|
||||
<div class="content">
|
||||
<slot></slot>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
public async firstUpdated() {
|
||||
if (this.runAfterRender) {
|
||||
await this.runAfterRender(this);
|
||||
}
|
||||
}
|
||||
}
|
332
ts_web/elements/dees-shopping-productcard.demo.ts
Normal file
332
ts_web/elements/dees-shopping-productcard.demo.ts
Normal file
@ -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`
|
||||
<dees-demowrapper>
|
||||
<style>
|
||||
${css`
|
||||
.demo-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24px;
|
||||
padding: 24px;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.product-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.cart-summary {
|
||||
margin-top: 24px;
|
||||
padding: 20px;
|
||||
background: ${cssManager.bdTheme('hsl(210 40% 96.1%)', 'hsl(215 20.2% 16.8%)')};
|
||||
border: 1px solid ${cssManager.bdTheme('hsl(214.3 31.8% 91.4%)', 'hsl(215 20.2% 21.8%)')};
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.cart-summary-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 16px;
|
||||
color: ${cssManager.bdTheme('hsl(0 0% 9%)', 'hsl(0 0% 95%)')};
|
||||
}
|
||||
|
||||
.cart-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 0;
|
||||
font-size: 14px;
|
||||
color: ${cssManager.bdTheme('hsl(215.3 25% 26.7%)', 'hsl(217.9 10.6% 74.9%)')};
|
||||
}
|
||||
|
||||
.cart-total {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-top: 16px;
|
||||
margin-top: 16px;
|
||||
border-top: 2px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')};
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: ${cssManager.bdTheme('hsl(0 0% 9%)', 'hsl(0 0% 95%)')};
|
||||
}
|
||||
|
||||
.selected-products {
|
||||
padding: 16px;
|
||||
background: ${cssManager.bdTheme('hsl(210 40% 96.1%)', 'hsl(215 20.2% 16.8%)')};
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
color: ${cssManager.bdTheme('hsl(215.3 25% 26.7%)', 'hsl(217.9 10.6% 74.9%)')};
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
|
||||
<div class="demo-container">
|
||||
<dees-panel .title=${'Basic Product Cards'} .subtitle=${'Simple product display with various configurations'}>
|
||||
<div class="product-grid">
|
||||
<dees-shopping-productcard
|
||||
.productData=${{
|
||||
name: 'Wireless Bluetooth Headphones',
|
||||
category: 'Audio',
|
||||
description: 'Premium sound quality with active noise cancellation',
|
||||
price: 149.99,
|
||||
originalPrice: 199.99,
|
||||
iconName: 'lucide:headphones'
|
||||
}}
|
||||
.quantity=${1}
|
||||
></dees-shopping-productcard>
|
||||
|
||||
<dees-shopping-productcard
|
||||
.productData=${{
|
||||
name: 'Smart Watch Series 7',
|
||||
category: 'Wearables',
|
||||
description: 'Track your fitness and stay connected on the go',
|
||||
price: 399.00,
|
||||
iconName: 'lucide:watch'
|
||||
}}
|
||||
.quantity=${1}
|
||||
></dees-shopping-productcard>
|
||||
|
||||
<dees-shopping-productcard
|
||||
.productData=${{
|
||||
name: 'USB-C Hub',
|
||||
category: 'Accessories',
|
||||
price: 49.99,
|
||||
iconName: 'lucide:usb',
|
||||
inStock: false
|
||||
}}
|
||||
.quantity=${0}
|
||||
></dees-shopping-productcard>
|
||||
</div>
|
||||
</dees-panel>
|
||||
|
||||
<dees-panel .title=${'Interactive Shopping Cart'} .subtitle=${'Product cards with dynamic cart calculation'} .runAfterRender=${async (elementArg: HTMLElement) => {
|
||||
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(`
|
||||
<div class="cart-item">
|
||||
<span>${product.data.name} (${element.quantity})</span>
|
||||
<span>$${subtotal.toFixed(2)}</span>
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
});
|
||||
|
||||
const summary = elementArg.querySelector('#interactive-cart-summary');
|
||||
if (summary) {
|
||||
summary.innerHTML = `
|
||||
${items.join('')}
|
||||
${items.length === 0 ? '<div class="cart-item" style="text-align: center; color: #999;">Your cart is empty</div>' : ''}
|
||||
<div class="cart-total">
|
||||
<span>Total</span>
|
||||
<span>$${total.toFixed(2)}</span>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
};
|
||||
|
||||
// Initial update
|
||||
setTimeout(updateCartSummary, 100);
|
||||
|
||||
// Set up listeners
|
||||
elementArg.querySelectorAll('dees-shopping-productcard').forEach(card => {
|
||||
card.addEventListener('quantityChange', updateCartSummary);
|
||||
});
|
||||
}}>
|
||||
<div class="product-grid">
|
||||
<dees-shopping-productcard
|
||||
id="laptop"
|
||||
.productData=${{
|
||||
name: 'MacBook Pro 14"',
|
||||
category: 'Computers',
|
||||
description: 'M3 Pro chip with 18GB RAM',
|
||||
price: 1999,
|
||||
originalPrice: 2199,
|
||||
iconName: 'lucide:laptop'
|
||||
}}
|
||||
.quantity=${1}
|
||||
></dees-shopping-productcard>
|
||||
|
||||
<dees-shopping-productcard
|
||||
id="ipad"
|
||||
.productData=${{
|
||||
name: 'iPad Air',
|
||||
category: 'Tablets',
|
||||
description: '10.9" Liquid Retina display',
|
||||
price: 599,
|
||||
iconName: 'lucide:tablet'
|
||||
}}
|
||||
.quantity=${0}
|
||||
></dees-shopping-productcard>
|
||||
|
||||
<dees-shopping-productcard
|
||||
id="keyboard"
|
||||
.productData=${{
|
||||
name: 'Magic Keyboard',
|
||||
category: 'Accessories',
|
||||
description: 'Wireless keyboard with Touch ID',
|
||||
price: 149,
|
||||
iconName: 'lucide:keyboard'
|
||||
}}
|
||||
.quantity=${2}
|
||||
></dees-shopping-productcard>
|
||||
</div>
|
||||
|
||||
<div class="cart-summary">
|
||||
<h3 class="cart-summary-title">Shopping Cart</h3>
|
||||
<div id="interactive-cart-summary">
|
||||
<!-- Dynamically updated -->
|
||||
</div>
|
||||
</div>
|
||||
</dees-panel>
|
||||
|
||||
<dees-panel .title=${'Selectable Product Cards'} .subtitle=${'Click cards or checkboxes to select products'}>
|
||||
<div class="product-grid">
|
||||
<dees-shopping-productcard
|
||||
.productData=${{
|
||||
name: 'Sony Alpha 7 IV',
|
||||
category: 'Cameras',
|
||||
description: 'Full-frame mirrorless camera',
|
||||
price: 2498,
|
||||
iconName: 'lucide:camera'
|
||||
}}
|
||||
.selectable=${true}
|
||||
.showQuantitySelector=${false}
|
||||
@selectionChange=${(e: CustomEvent) => {
|
||||
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';
|
||||
}
|
||||
}}
|
||||
></dees-shopping-productcard>
|
||||
|
||||
<dees-shopping-productcard
|
||||
.productData=${{
|
||||
name: 'DJI Mini 3 Pro',
|
||||
category: 'Drones',
|
||||
description: 'Lightweight drone with 4K camera',
|
||||
price: 759,
|
||||
iconName: 'lucide:plane'
|
||||
}}
|
||||
.selectable=${true}
|
||||
.showQuantitySelector=${false}
|
||||
@selectionChange=${(e: CustomEvent) => {
|
||||
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';
|
||||
}
|
||||
}}
|
||||
></dees-shopping-productcard>
|
||||
|
||||
<dees-shopping-productcard
|
||||
.productData=${{
|
||||
name: 'GoPro HERO12',
|
||||
category: 'Action Cameras',
|
||||
description: '5.3K video with HyperSmooth 6.0',
|
||||
price: 399,
|
||||
originalPrice: 449,
|
||||
iconName: 'lucide:video'
|
||||
}}
|
||||
.selectable=${true}
|
||||
.showQuantitySelector=${false}
|
||||
@selectionChange=${(e: CustomEvent) => {
|
||||
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';
|
||||
}
|
||||
}}
|
||||
></dees-shopping-productcard>
|
||||
</div>
|
||||
|
||||
<div class="selected-products" id="selection-output" style="margin-top: 16px;">
|
||||
No products selected
|
||||
</div>
|
||||
</dees-panel>
|
||||
|
||||
<dees-panel .title=${'Product Variations'} .subtitle=${'Different states and configurations'}>
|
||||
<div class="product-grid">
|
||||
<dees-shopping-productcard
|
||||
.productData=${{
|
||||
name: 'Limited Edition Sneakers',
|
||||
category: 'Footwear',
|
||||
description: 'Exclusive colorway - Only 500 pairs',
|
||||
price: 299,
|
||||
iconName: 'lucide:footprints',
|
||||
inStock: false,
|
||||
stockText: 'Sold Out'
|
||||
}}
|
||||
.quantity=${0}
|
||||
></dees-shopping-productcard>
|
||||
|
||||
<dees-shopping-productcard
|
||||
.productData=${{
|
||||
name: 'Minimalist Wallet',
|
||||
price: 39.99,
|
||||
iconName: 'lucide:wallet'
|
||||
}}
|
||||
.quantity=${1}
|
||||
></dees-shopping-productcard>
|
||||
|
||||
<dees-shopping-productcard
|
||||
.productData=${{
|
||||
name: 'Premium Coffee Beans',
|
||||
category: 'Food & Beverage',
|
||||
description: 'Single origin, medium roast',
|
||||
price: 18.50,
|
||||
iconName: 'lucide:coffee',
|
||||
currency: '€'
|
||||
}}
|
||||
.quantity=${2}
|
||||
></dees-shopping-productcard>
|
||||
</div>
|
||||
</dees-panel>
|
||||
</div>
|
||||
</dees-demowrapper>
|
||||
`;
|
335
ts_web/elements/dees-shopping-productcard.ts
Normal file
335
ts_web/elements/dees-shopping-productcard.ts
Normal file
@ -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`
|
||||
<div
|
||||
class="product-card ${this.selectable ? 'selectable' : ''} ${this.selected ? 'selected' : ''}"
|
||||
@click=${this.handleCardClick}
|
||||
>
|
||||
<div class="product-image">
|
||||
${imageUrl ? html`
|
||||
<img src="${imageUrl}" alt="${name}">
|
||||
` : html`
|
||||
<dees-icon .iconName=${iconName}></dees-icon>
|
||||
`}
|
||||
${this.selectable ? html`
|
||||
<div
|
||||
class="selection-checkbox ${this.selected ? 'checked' : ''}"
|
||||
@click=${(e: Event) => {
|
||||
e.stopPropagation();
|
||||
this.handleSelectionToggle();
|
||||
}}
|
||||
>
|
||||
<dees-icon .iconName=${'lucide:check'}></dees-icon>
|
||||
</div>
|
||||
` : ''}
|
||||
</div>
|
||||
<div class="product-content">
|
||||
<div class="product-header">
|
||||
${category ? html`<div class="product-category">${category}</div>` : ''}
|
||||
<div class="product-name">${name}</div>
|
||||
</div>
|
||||
${description ? html`
|
||||
<div class="product-description">${description}</div>
|
||||
` : ''}
|
||||
<div class="stock-status ${inStock ? 'in-stock' : 'out-of-stock'}">
|
||||
<dees-icon .iconName=${inStock ? 'lucide:check-circle' : 'lucide:x-circle'}></dees-icon>
|
||||
${stockText}
|
||||
</div>
|
||||
<div class="product-footer">
|
||||
<div class="product-price">
|
||||
<span class="price-current">${formatPrice(price)}</span>
|
||||
${originalPrice && originalPrice > price ? html`
|
||||
<span class="price-original">${formatPrice(originalPrice)}</span>
|
||||
` : ''}
|
||||
</div>
|
||||
${this.showQuantitySelector ? html`
|
||||
<dees-input-quantityselector
|
||||
.value=${this.quantity}
|
||||
@changeSubject=${(e: CustomEvent) => {
|
||||
this.quantity = e.detail.getValue();
|
||||
this.dispatchEvent(new CustomEvent('quantityChange', {
|
||||
detail: {
|
||||
quantity: this.quantity,
|
||||
productData: this.productData
|
||||
},
|
||||
bubbles: true,
|
||||
composed: true
|
||||
}));
|
||||
}}
|
||||
></dees-input-quantityselector>
|
||||
` : ''}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
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
|
||||
}));
|
||||
}
|
||||
}
|
@ -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';
|
||||
|
Reference in New Issue
Block a user