@r2digisolutions/components 0.19.3 → 0.19.5

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.
@@ -58,7 +58,7 @@
58
58
 
59
59
  <span
60
60
  class={[
61
- 'inline-flex items-center font-medium',
61
+ 'inline-flex shrink-0 items-center whitespace-nowrap font-medium',
62
62
  rounded ? 'rounded-full' : 'rounded-md',
63
63
  variantClasses[variant],
64
64
  sizeClasses[size],
@@ -17,15 +17,19 @@
17
17
  categoryLabel?: string;
18
18
  installed?: boolean;
19
19
  /**
20
- * Estado legible en el listado (p. ej. «Instalado», «En licencia», «No incluido»).
21
- * Si no se pasa y `installed`, se usa «Instalado».
20
+ * Estado legible en el listado (p. ej. «Activo», «En licencia», «No incluido»).
21
+ * Si no se pasa y `installed`, se usa «Activo».
22
22
  */
23
23
  statusLabel?: string;
24
24
  /** landscape promotional card */
25
25
  featured?: boolean;
26
+ /** Compact row for list view (ignored when featured). */
27
+ layout?: 'grid' | 'list';
26
28
  tags?: StoreProductTag[];
27
29
  icon: Component;
28
30
  accentClass?: string;
31
+ /** Prefer link navigation when set (store detail URLs). */
32
+ href?: string;
29
33
  onclick?: () => void;
30
34
  /** Optional custom badge area */
31
35
  badge?: Snippet;
@@ -39,9 +43,11 @@
39
43
  installed = false,
40
44
  statusLabel = '',
41
45
  featured = false,
46
+ layout = 'grid',
42
47
  tags = [],
43
48
  icon: Icon,
44
49
  accentClass = 'from-neutral-800 to-neutral-600',
50
+ href = '',
45
51
  onclick,
46
52
  badge
47
53
  }: Props = $props();
@@ -58,13 +64,24 @@
58
64
  );
59
65
 
60
66
  const resolvedStatus = $derived(
61
- statusLabel.trim() || (installed ? 'Instalado' : '')
67
+ statusLabel.trim() || (installed ? 'Activo' : '')
68
+ );
69
+ const statusToneOk = $derived(
70
+ installed || resolvedStatus === 'Activo' || resolvedStatus === 'Instalado'
71
+ );
72
+ const isList = $derived(!featured && layout === 'list');
73
+ const rootClass = $derived(
74
+ [
75
+ 'group ms-tile',
76
+ featured && 'ms-tile--featured',
77
+ isList && 'ms-tile--list'
78
+ ]
79
+ .filter(Boolean)
80
+ .join(' ')
62
81
  );
63
- const statusToneOk = $derived(installed || resolvedStatus === 'Instalado');
64
82
 
65
83
  function tagVariant(tag: StoreProductTag): 'info' | 'warning' | 'default' {
66
84
  if (tag === 'new') return 'info';
67
- // Featured uses custom amber + white via tagClass (avoid warning’s amber text).
68
85
  if (tag === 'featured') return 'default';
69
86
  return 'default';
70
87
  }
@@ -78,14 +95,15 @@
78
95
  }
79
96
  return '';
80
97
  }
98
+
99
+ function onActivate(e: MouseEvent) {
100
+ if (href) return;
101
+ e.preventDefault();
102
+ onclick?.();
103
+ }
81
104
  </script>
82
105
 
83
- <button
84
- type="button"
85
- class="group ms-tile"
86
- class:ms-tile--featured={featured}
87
- {onclick}
88
- >
106
+ {#snippet body()}
89
107
  {#if featured}
90
108
  <div class="ms-tile__promo bg-gradient-to-br {accentClass}">
91
109
  <div class="ms-tile__promo-mesh" aria-hidden="true"></div>
@@ -127,6 +145,41 @@
127
145
  <Icon class="size-10 text-white drop-shadow" />
128
146
  </div>
129
147
  </div>
148
+ {:else if isList}
149
+ <div class="ms-tile__list-icon bg-gradient-to-br {accentClass}">
150
+ <div class="ms-tile__icon-shine" aria-hidden="true"></div>
151
+ <Icon class="relative size-7 text-white drop-shadow-sm" />
152
+ </div>
153
+ <div class="ms-tile__list-body">
154
+ <div class="ms-tile__list-top">
155
+ <span class="ms-tile__name">{name}</span>
156
+ {#if resolvedStatus}
157
+ <Badge variant={statusToneOk ? 'success' : 'secondary'} size="sm">
158
+ {resolvedStatus}
159
+ </Badge>
160
+ {:else if displayTags[0]}
161
+ <Badge
162
+ variant={tagVariant(displayTags[0])}
163
+ size="sm"
164
+ class={tagClass(displayTags[0])}
165
+ >
166
+ {TAG_LABELS[displayTags[0]]}
167
+ </Badge>
168
+ {/if}
169
+ </div>
170
+ {#if description}
171
+ <span class="ms-tile__list-desc">{description}</span>
172
+ {/if}
173
+ <div class="ms-tile__list-meta">
174
+ {#if categoryLabel}
175
+ <span>{categoryLabel}</span>
176
+ <span aria-hidden="true">·</span>
177
+ {/if}
178
+ <span class="ms-tile__price">{priceLabel}</span>
179
+ <span aria-hidden="true">·</span>
180
+ <span class="ms-tile__publisher">Evoteg</span>
181
+ </div>
182
+ </div>
130
183
  {:else}
131
184
  <div class="ms-tile__icon-shell">
132
185
  <div class="ms-tile__icon bg-gradient-to-br {accentClass}">
@@ -135,7 +188,7 @@
135
188
  </div>
136
189
  {#if statusToneOk}
137
190
  <span class="ms-tile__corner">
138
- <Badge variant="success" size="sm">{resolvedStatus || 'Instalado'}</Badge>
191
+ <Badge variant="success" size="sm">{resolvedStatus || 'Activo'}</Badge>
139
192
  </span>
140
193
  {:else if displayTags[0]}
141
194
  <span class="ms-tile__corner">
@@ -167,7 +220,17 @@
167
220
  <span class="ms-tile__price">{priceLabel}</span>
168
221
  </div>
169
222
  {/if}
170
- </button>
223
+ {/snippet}
224
+
225
+ {#if href}
226
+ <a class={rootClass} {href} data-sveltekit-preload-data="hover">
227
+ {@render body()}
228
+ </a>
229
+ {:else}
230
+ <button type="button" class={rootClass} onclick={onActivate}>
231
+ {@render body()}
232
+ </button>
233
+ {/if}
171
234
 
172
235
  <style>
173
236
  .ms-tile {
@@ -182,6 +245,8 @@
182
245
  padding: 0;
183
246
  cursor: pointer;
184
247
  border-radius: 1rem;
248
+ text-decoration: none;
249
+ color: inherit;
185
250
  transition:
186
251
  transform 200ms cubic-bezier(0.22, 1, 0.36, 1),
187
252
  filter 200ms ease;
@@ -193,6 +258,71 @@
193
258
  outline: 2px solid var(--color-brand-500, #2563eb);
194
259
  outline-offset: 4px;
195
260
  }
261
+ .ms-tile--list {
262
+ flex-direction: row;
263
+ align-items: center;
264
+ gap: 0.9rem;
265
+ padding: 0.75rem 0.85rem;
266
+ border-radius: 1rem;
267
+ background: rgb(255 255 255 / 0.78);
268
+ border: 1px solid rgb(0 0 0 / 0.06);
269
+ box-shadow: 0 6px 18px rgb(0 0 0 / 0.04);
270
+ }
271
+ .ms-tile--list:hover {
272
+ transform: translateY(-1px);
273
+ border-color: rgb(0 0 0 / 0.1);
274
+ }
275
+ :global(.dark) .ms-tile--list {
276
+ background: rgb(23 23 23 / 0.88);
277
+ border-color: rgb(255 255 255 / 0.06);
278
+ }
279
+ .ms-tile__list-icon {
280
+ position: relative;
281
+ display: grid;
282
+ place-items: center;
283
+ width: 3.25rem;
284
+ height: 3.25rem;
285
+ border-radius: 22%;
286
+ flex-shrink: 0;
287
+ overflow: hidden;
288
+ box-shadow:
289
+ 0 8px 18px rgb(0 0 0 / 0.2),
290
+ 0 1px 0 rgb(255 255 255 / 0.2) inset;
291
+ }
292
+ .ms-tile__list-body {
293
+ min-width: 0;
294
+ flex: 1;
295
+ display: flex;
296
+ flex-direction: column;
297
+ gap: 0.2rem;
298
+ }
299
+ .ms-tile__list-top {
300
+ display: flex;
301
+ align-items: center;
302
+ justify-content: space-between;
303
+ gap: 0.75rem;
304
+ }
305
+ .ms-tile__list-desc {
306
+ font-size: 0.8rem;
307
+ line-height: 1.35;
308
+ color: var(--color-neutral-600, #525252);
309
+ display: -webkit-box;
310
+ -webkit-line-clamp: 1;
311
+ line-clamp: 1;
312
+ -webkit-box-orient: vertical;
313
+ overflow: hidden;
314
+ }
315
+ :global(.dark) .ms-tile__list-desc {
316
+ color: var(--color-neutral-400, #a3a3a3);
317
+ }
318
+ .ms-tile__list-meta {
319
+ display: flex;
320
+ flex-wrap: wrap;
321
+ align-items: center;
322
+ gap: 0.35rem;
323
+ font-size: 0.75rem;
324
+ color: var(--color-neutral-500, #737373);
325
+ }
196
326
  .ms-tile__icon-shell {
197
327
  position: relative;
198
328
  width: 100%;
@@ -7,15 +7,19 @@ type Props = {
7
7
  categoryLabel?: string;
8
8
  installed?: boolean;
9
9
  /**
10
- * Estado legible en el listado (p. ej. «Instalado», «En licencia», «No incluido»).
11
- * Si no se pasa y `installed`, se usa «Instalado».
10
+ * Estado legible en el listado (p. ej. «Activo», «En licencia», «No incluido»).
11
+ * Si no se pasa y `installed`, se usa «Activo».
12
12
  */
13
13
  statusLabel?: string;
14
14
  /** landscape promotional card */
15
15
  featured?: boolean;
16
+ /** Compact row for list view (ignored when featured). */
17
+ layout?: 'grid' | 'list';
16
18
  tags?: StoreProductTag[];
17
19
  icon: Component;
18
20
  accentClass?: string;
21
+ /** Prefer link navigation when set (store detail URLs). */
22
+ href?: string;
19
23
  onclick?: () => void;
20
24
  /** Optional custom badge area */
21
25
  badge?: Snippet;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@r2digisolutions/components",
3
- "version": "0.19.3",
3
+ "version": "0.19.5",
4
4
  "private": false,
5
5
  "description": "R2DigiSolutions Svelte 5 component library — Atomic Design, Tailwind 4, Light/Dark mode",
6
6
  "license": "MIT",
@@ -66,7 +66,7 @@
66
66
  },
67
67
  "devDependencies": {
68
68
  "@eslint/js": "^10.0.1",
69
- "@lucide/svelte": "^1.44.0",
69
+ "@lucide/svelte": "^1.45.0",
70
70
  "@playwright/test": "^1.63.0",
71
71
  "@storybook/addon-a11y": "^10.6.0",
72
72
  "@storybook/addon-docs": "^10.6.0",
@@ -98,7 +98,7 @@
98
98
  "tailwindcss": "^4.3.3",
99
99
  "typescript": "6.0.3",
100
100
  "typescript-eslint": "^8.70.0",
101
- "vite": "^8.2.2",
101
+ "vite": "^8.3.0",
102
102
  "vitest": "^5.0.0",
103
103
  "vitest-browser-svelte": "^3.1.0"
104
104
  },