@design.estate/dees-catalog 9.3.0 → 9.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/dist_bundle/bundle.js +1372 -2727
  2. package/dist_bundle/bundle.js.map +1 -1
  3. package/dist_ts_web/00_commitinfo_data.js +1 -1
  4. package/dist_ts_web/elements/00group-button/dees-button/dees-button.js +2 -1
  5. package/dist_ts_web/elements/00group-input/dees-input-quantityselector/dees-input-quantityselector.js +4 -2
  6. package/dist_ts_web/elements/00group-simple/dees-shopping-productcard/dees-shopping-productcard.d.ts +11 -9
  7. package/dist_ts_web/elements/00group-simple/dees-shopping-productcard/dees-shopping-productcard.demo.d.ts +2 -2
  8. package/dist_ts_web/elements/00group-simple/dees-shopping-productcard/dees-shopping-productcard.demo.js +148 -319
  9. package/dist_ts_web/elements/00group-simple/dees-shopping-productcard/dees-shopping-productcard.js +60 -279
  10. package/dist_ts_web/elements/00group-simple/dees-simple-appdash/dees-simple-appdash.d.ts +25 -10
  11. package/dist_ts_web/elements/00group-simple/dees-simple-appdash/dees-simple-appdash.demo.d.ts +3 -4
  12. package/dist_ts_web/elements/00group-simple/dees-simple-appdash/dees-simple-appdash.demo.js +229 -388
  13. package/dist_ts_web/elements/00group-simple/dees-simple-appdash/dees-simple-appdash.js +253 -813
  14. package/dist_ts_web/elements/00group-simple/dees-simple-login/dees-simple-login.demo.d.ts +2 -0
  15. package/dist_ts_web/elements/00group-simple/dees-simple-login/dees-simple-login.demo.js +150 -389
  16. package/dist_ts_web/elements/00group-simple/dees-simple-login/dees-simple-login.js +67 -11
  17. package/package.json +1 -1
  18. package/readme.hints.md +26 -0
  19. package/readme.md +76 -15
  20. package/scripts/bdtheme-baseline.json +0 -2
  21. package/ts_web/00_commitinfo_data.ts +1 -1
  22. package/ts_web/elements/00group-button/dees-button/dees-button.ts +1 -0
  23. package/ts_web/elements/00group-input/dees-input-quantityselector/dees-input-quantityselector.ts +3 -1
  24. package/ts_web/elements/00group-simple/dees-shopping-productcard/dees-shopping-productcard.demo.ts +67 -332
  25. package/ts_web/elements/00group-simple/dees-shopping-productcard/dees-shopping-productcard.ts +65 -322
  26. package/ts_web/elements/00group-simple/dees-simple-appdash/dees-simple-appdash.demo.ts +136 -382
  27. package/ts_web/elements/00group-simple/dees-simple-appdash/dees-simple-appdash.ts +224 -872
  28. package/ts_web/elements/00group-simple/dees-simple-login/dees-simple-login.demo.ts +116 -421
  29. package/ts_web/elements/00group-simple/dees-simple-login/dees-simple-login.ts +66 -10
@@ -1,21 +1,9 @@
1
- import {
2
- customElement,
3
- property,
4
- html,
5
- css,
6
- cssManager,
7
- type TemplateResult,
8
- DeesElement,
9
- } from '@design.estate/dees-element';
1
+ import { customElement, DeesElement, property, html, css, cssManager, type TemplateResult } from '@design.estate/dees-element';
10
2
  import { demoFunc } from './dees-shopping-productcard.demo.js';
11
3
  import { themeDefaultStyles } from '../../00theme.js';
12
- import '../../00group-layout/dees-tile/dees-tile.js';
13
-
14
- declare global {
15
- interface HTMLElementTagNameMap {
16
- 'dees-shopping-productcard': DeesShoppingProductcard;
17
- }
18
- }
4
+ import '../../00group-utility/dees-icon/dees-icon.js';
5
+ import '../../00group-input/dees-input-checkbox/dees-input-checkbox.js';
6
+ import '../../00group-input/dees-input-quantityselector/dees-input-quantityselector.js';
19
7
 
20
8
  export interface IProductData {
21
9
  name: string;
@@ -30,318 +18,73 @@ export interface IProductData {
30
18
  iconName?: string;
31
19
  }
32
20
 
21
+ declare global { interface HTMLElementTagNameMap { 'dees-shopping-productcard': DeesShoppingProductcard; } }
22
+
33
23
  @customElement('dees-shopping-productcard')
34
24
  export class DeesShoppingProductcard extends DeesElement {
35
25
  public static demo = demoFunc;
36
26
  public static demoGroups = ['Simple'];
37
-
38
- @property({ type: Object })
39
- accessor productData: IProductData = {
40
- name: 'Product Name',
41
- price: 0,
42
- };
43
-
44
- @property({ type: Number })
45
- accessor quantity: number = 0;
46
-
47
- @property({ type: Boolean })
48
- accessor showQuantitySelector: boolean = true;
49
-
50
- @property({ type: Boolean })
51
- accessor selectable: boolean = false;
52
-
53
- @property({ type: Boolean })
54
- accessor selected: boolean = false;
55
-
56
- public static styles = [
57
- themeDefaultStyles,
58
- cssManager.defaultStyles,
59
- css`
60
- /* TODO: Migrate hardcoded values to --dees-* CSS variables */
61
- :host {
62
- display: block;
63
- }
64
-
65
- dees-tile {
66
- height: 100%;
67
- }
68
-
69
- dees-tile:hover::part(outer) {
70
- border-color: var(--dees-color-border-strong);
71
- box-shadow: var(--dees-shadow-md);
72
- }
73
-
74
- dees-tile.selectable {
75
- cursor: pointer;
76
- }
77
-
78
- dees-tile.selected::part(outer) {
79
- border-color: var(--dees-color-link);
80
- box-shadow: 0 0 0 3px color-mix(in srgb, var(--dees-color-link) 10%, transparent);
81
- }
82
-
83
- .product-image {
84
- width: 100%;
85
- height: 180px;
86
- background: var(--dees-color-badge-default-bg);
87
- display: flex;
88
- align-items: center;
89
- justify-content: center;
90
- position: relative;
91
- overflow: hidden;
92
- }
93
-
94
- .product-image img {
95
- width: 100%;
96
- height: 100%;
97
- object-fit: cover;
98
- }
99
-
100
- .product-image dees-icon {
101
- font-size: 48px;
102
- color: var(--dees-color-text-muted);
103
- }
104
-
105
- .selection-checkbox {
106
- position: absolute;
107
- top: var(--dees-spacing-md);
108
- right: var(--dees-spacing-md);
109
- width: 20px;
110
- height: 20px;
111
- background: var(--dees-color-bg-primary);
112
- border: 2px solid color-mix(in srgb, var(--dees-color-text-primary) 35%, transparent);
113
- border-radius: var(--dees-radius-sm);
114
- display: flex;
115
- align-items: center;
116
- justify-content: center;
117
- transition: all var(--dees-transition-default) var(--dees-ease-standard);
118
- cursor: pointer;
119
- }
120
-
121
- .selection-checkbox.checked {
122
- background: var(--dees-color-link);
123
- border-color: var(--dees-color-link);
124
- }
125
-
126
- .selection-checkbox dees-icon {
127
- color: var(--dees-color-on-accent);
128
- font-size: 12px;
129
- opacity: 0;
130
- transform: scale(0);
131
- transition: all var(--dees-transition-default) var(--dees-ease-standard);
132
- }
133
-
134
- .selection-checkbox.checked dees-icon {
135
- opacity: 1;
136
- transform: scale(1);
137
- }
138
-
139
- .product-header-bar {
140
- display: flex;
141
- align-items: center;
142
- justify-content: space-between;
143
- height: var(--dees-control-height-sm);
144
- padding: 0 var(--dees-spacing-lg);
145
- gap: var(--dees-spacing-sm);
146
- }
147
-
148
- .product-name {
149
- font-size: var(--dees-font-control-size);
150
- font-weight: var(--dees-font-control-weight);
151
- color: var(--dees-color-text-secondary);
152
- white-space: nowrap;
153
- overflow: hidden;
154
- text-overflow: ellipsis;
155
- }
156
-
157
- .product-category {
158
- font-size: var(--dees-font-caption2-size);
159
- font-weight: var(--dees-font-control-weight);
160
- color: var(--dees-color-text-secondary);
161
- text-transform: uppercase;
162
- letter-spacing: 0.05em;
163
- white-space: nowrap;
164
- flex-shrink: 0;
165
- }
166
-
167
- .product-body {
168
- display: flex;
169
- flex-direction: column;
170
- height: 100%;
171
- }
172
-
173
- .product-content {
174
- padding: var(--dees-spacing-md) var(--dees-spacing-lg);
175
- display: flex;
176
- flex-direction: column;
177
- gap: var(--dees-spacing-sm);
178
- flex: 1;
179
- }
180
-
181
- .product-description {
182
- font-size: var(--dees-font-control-size-sm);
183
- color: var(--dees-color-text-muted);
184
- line-height: 1.5;
185
- flex: 1;
186
- }
187
-
188
- .product-footer {
189
- display: flex;
190
- align-items: center;
191
- justify-content: space-between;
192
- gap: var(--dees-spacing-lg);
193
- padding: var(--dees-spacing-md) var(--dees-spacing-lg);
194
- width: 100%;
195
- box-sizing: border-box;
196
- }
197
-
198
- .product-price {
199
- display: flex;
200
- flex-direction: column;
201
- gap: 2px;
202
- }
203
-
204
- .price-current {
205
- font-size: var(--dees-font-title3-size);
206
- font-weight: 600;
207
- color: var(--dees-color-text-primary);
208
- }
209
-
210
- .price-original {
211
- font-size: var(--dees-font-control-size);
212
- color: var(--dees-color-text-muted);
213
- text-decoration: line-through;
214
- }
215
-
216
- .stock-status {
217
- display: inline-flex;
218
- align-items: center;
219
- gap: var(--dees-spacing-xs);
220
- font-size: var(--dees-font-caption1-size);
221
- margin-top: var(--dees-spacing-sm);
222
- }
223
-
224
- .stock-status.in-stock {
225
- color: var(--dees-color-text-success);
226
- }
227
-
228
- .stock-status.out-of-stock {
229
- color: var(--dees-color-accent-error);
230
- }
231
-
232
- .stock-status dees-icon {
233
- font-size: 14px;
234
- }
235
- `,
236
- ];
27
+ @property({ type: Object }) accessor productData: IProductData = { name: 'Product Name', price: 0 };
28
+ @property({ type: Number }) accessor quantity = 0;
29
+ @property({ type: Boolean }) accessor showQuantitySelector = true;
30
+ @property({ type: Boolean }) accessor selectable = false;
31
+ @property({ type: Boolean }) accessor selected = false;
32
+
33
+ public static styles = [themeDefaultStyles, cssManager.defaultStyles, css`
34
+ :host { display: flex; min-width: 0; font: 13px/1.5 var(--dees-font-family); color: var(--dees-color-text-primary); }
35
+ * { box-sizing: border-box; }
36
+ article { display: flex; flex-direction: column; width: 100%; min-width: 0; position: relative; background: var(--dees-color-bg-primary); border: 1px solid var(--dees-color-border-subtle); border-radius: var(--dees-radius-2xl); corner-shape: var(--dees-corner-shape); transition: border-color 140ms ease, box-shadow 140ms ease; }
37
+ article.selectable { cursor: pointer; }
38
+ article.selected { border-color: var(--dees-color-accent-primary); box-shadow: 0 0 0 1px var(--dees-color-accent-primary); }
39
+ .product-image { display: grid; place-items: center; aspect-ratio: 4 / 3; margin: 8px 8px 0; min-width: 0; border-radius: var(--dees-radius-xl); corner-shape: var(--dees-corner-shape); background: var(--dees-color-bg-secondary); overflow: hidden; color: var(--dees-color-text-secondary); }
40
+ .product-image img { display: block; width: 100%; height: 100%; object-fit: contain; }
41
+ .selection { position: absolute; inset: 16px 16px auto auto; display: grid; place-items: center; min-width: 32px; min-height: 32px; border-radius: var(--dees-radius-md); background: var(--dees-color-bg-primary); box-shadow: var(--dees-shadow-xs); }
42
+ .selection dees-input-checkbox { --dees-input-spacing: 0px; }
43
+ .product-content { display: flex; flex-direction: column; flex: 1; gap: 8px; padding: 20px; }
44
+ .category { color: var(--dees-color-text-secondary); font-size: 11px; font-weight: 500; }
45
+ h3 { font-size: 19px; font-weight: 600; line-height: 1.25; letter-spacing: -.025em; margin: 0; overflow-wrap: anywhere; }
46
+ .description { color: var(--dees-color-text-secondary); margin: 0; }
47
+ .stock { display: flex; align-items: center; gap: 5px; color: var(--dees-color-text-secondary); font-size: 11px; margin-top: auto; padding-top: 8px; }
48
+ .stock.unavailable { color: var(--dees-color-text-warning); }
49
+ .product-footer { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 12px; padding: 0 20px 20px; }
50
+ .price { display: grid; align-content: center; min-height: 40px; gap: 2px; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
51
+ .current-price { font-size: 17px; line-height: 1.3; font-weight: 600; letter-spacing: -.02em; }
52
+ .original-price { font-size: 12px; color: var(--dees-color-text-secondary); }
53
+ .quantity { border-radius: var(--dees-radius-lg); background: var(--dees-color-fill-secondary); }
54
+ @media (hover: hover) { article.selectable:hover:not(.selected) { border-color: var(--dees-color-border-strong); box-shadow: var(--dees-shadow-sm); } }
55
+ @media (prefers-reduced-motion: reduce) { article { transition: none; } }
56
+ `];
237
57
 
238
58
  public render(): TemplateResult {
239
- const {
240
- name,
241
- category,
242
- description,
243
- price,
244
- originalPrice,
245
- currency = '$',
246
- inStock = true,
247
- stockText = inStock ? 'In Stock' : 'Out of Stock',
248
- imageUrl,
249
- iconName = 'lucide:package',
250
- } = this.productData;
251
-
252
- const formatPrice = (value: number) => {
253
- return `${currency}${value.toFixed(2)}`;
254
- };
255
-
256
- return html`
257
- <dees-tile
258
- class="${this.selectable ? 'selectable' : ''} ${this.selected ? 'selected' : ''}"
259
- @click=${this.handleCardClick}
260
- >
261
- <div slot="header" class="product-header-bar">
262
- <span class="product-name">${name}</span>
263
- ${category ? html`<span class="product-category">${category}</span>` : ''}
264
- </div>
265
- <div class="product-body">
266
- <div class="product-image">
267
- ${imageUrl ? html`
268
- <img src="${imageUrl}" alt="${name}">
269
- ` : html`
270
- <dees-icon .icon=${iconName}></dees-icon>
271
- `}
272
- ${this.selectable ? html`
273
- <div
274
- class="selection-checkbox ${this.selected ? 'checked' : ''}"
275
- @click=${(e: Event) => {
276
- e.stopPropagation();
277
- this.handleSelectionToggle();
278
- }}
279
- >
280
- <dees-icon .icon=${'lucide:check'}></dees-icon>
281
- </div>
282
- ` : ''}
283
- </div>
284
- <div class="product-content">
285
- ${description ? html`
286
- <div class="product-description">${description}</div>
287
- ` : ''}
288
- <div class="stock-status ${inStock ? 'in-stock' : 'out-of-stock'}">
289
- <dees-icon .icon=${inStock ? 'lucide:check-circle' : 'lucide:x-circle'}></dees-icon>
290
- ${stockText}
291
- </div>
292
- </div>
293
- </div>
294
- <div slot="footer" class="product-footer">
295
- <div class="product-price">
296
- <span class="price-current">${formatPrice(price)}</span>
297
- ${originalPrice && originalPrice > price ? html`
298
- <span class="price-original">${formatPrice(originalPrice)}</span>
299
- ` : ''}
300
- </div>
301
- ${this.showQuantitySelector ? html`
302
- <dees-input-quantityselector
303
- .value=${this.quantity}
304
- @changeSubject=${(e: CustomEvent) => {
305
- this.quantity = e.detail.getValue();
306
- this.dispatchEvent(new CustomEvent('quantityChange', {
307
- detail: {
308
- quantity: this.quantity,
309
- productData: this.productData
310
- },
311
- bubbles: true,
312
- composed: true
313
- }));
314
- }}
315
- ></dees-input-quantityselector>
316
- ` : ''}
317
- </div>
318
- </dees-tile>
319
- `;
59
+ const product = this.productData;
60
+ const available = product.inStock !== false;
61
+ const currency = product.currency || '$';
62
+ return html`<article class="${this.selectable ? 'selectable' : ''} ${this.selected ? 'selected' : ''}" aria-labelledby="product-name" @click=${this.toggleSelection}>
63
+ <div class="product-image">${product.imageUrl ? html`<img src=${product.imageUrl} alt=${product.name} loading="lazy" decoding="async">` : html`<dees-icon .icon=${product.iconName || 'lucide:package'} iconSize="64"></dees-icon>`}</div>
64
+ ${this.selectable ? html`<div class="selection" @click=${(event: Event) => event.stopPropagation()}>
65
+ <dees-input-checkbox .ariaLabel=${`Select ${product.name}`} .value=${this.selected}
66
+ @newValue=${(event: CustomEvent<boolean>) => this.setSelection(event.detail)}></dees-input-checkbox>
67
+ </div>` : ''}
68
+ <div class="product-content">${product.category ? html`<div class="category">${product.category}</div>` : ''}
69
+ <h3 id="product-name">${product.name}</h3>${product.description ? html`<p class="description">${product.description}</p>` : ''}
70
+ <div class="stock ${available ? '' : 'unavailable'}"><dees-icon .icon=${available ? 'lucide:check' : 'lucide:clock'} iconSize="12"></dees-icon>${product.stockText || (available ? 'In stock' : 'Out of stock')}</div>
71
+ </div>
72
+ <footer class="product-footer"><div class="price"><span class="current-price">${currency}${product.price.toFixed(2)}</span>
73
+ ${product.originalPrice !== undefined && product.originalPrice > product.price ? html`<s class="original-price">${currency}${product.originalPrice.toFixed(2)}</s>` : ''}</div>
74
+ ${this.showQuantitySelector ? html`<div class="quantity" role="group" aria-label=${`Quantity for ${product.name}`} @click=${(event: Event) => event.stopPropagation()}>
75
+ <dees-input-quantityselector .value=${this.quantity}
76
+ @newValue=${(event: CustomEvent<number>) => {
77
+ this.quantity = event.detail;
78
+ this.dispatchEvent(new CustomEvent('quantityChange', { detail: { quantity: this.quantity, productData: this.productData }, bubbles: true, composed: true }));
79
+ }}></dees-input-quantityselector></div>` : ''}
80
+ </footer>
81
+ </article>`;
320
82
  }
321
83
 
322
- private handleCardClick() {
323
- if (this.selectable) {
324
- this.selected = !this.selected;
325
- this.dispatchEvent(new CustomEvent('selectionChange', {
326
- detail: {
327
- selected: this.selected,
328
- productData: this.productData
329
- },
330
- bubbles: true,
331
- composed: true
332
- }));
333
- }
84
+ private toggleSelection(): void { if (this.selectable) this.setSelection(!this.selected); }
85
+ private setSelection(selected: boolean): void {
86
+ if (!this.selectable || this.selected === selected) return;
87
+ this.selected = selected;
88
+ this.dispatchEvent(new CustomEvent('selectionChange', { detail: { selected, productData: this.productData }, bubbles: true, composed: true }));
334
89
  }
335
-
336
- private handleSelectionToggle() {
337
- this.selected = !this.selected;
338
- this.dispatchEvent(new CustomEvent('selectionChange', {
339
- detail: {
340
- selected: this.selected,
341
- productData: this.productData
342
- },
343
- bubbles: true,
344
- composed: true
345
- }));
346
- }
347
- }
90
+ }