@r2digisolutions/components 0.19.8 → 0.19.9

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.
@@ -178,6 +178,7 @@
178
178
  };
179
179
  check();
180
180
  });
181
+ // Do not preventDefault — Kit remote attachment owns progressive enhancement.
181
182
  return;
182
183
  }
183
184
  e.preventDefault();
@@ -279,8 +279,13 @@
279
279
  {#if children}
280
280
  <div
281
281
  class={[
282
- 'px-5 py-2 text-sm text-primary',
283
- scrollable && 'max-h-[min(60vh,28rem)] overflow-y-auto'
282
+ 'min-h-0 flex-1 px-5 py-2 text-sm text-primary',
283
+ scrollable &&
284
+ (size === 'full'
285
+ ? 'max-h-[calc(100vh-11rem)] overflow-y-auto'
286
+ : size === 'xl'
287
+ ? 'max-h-[min(70vh,36rem)] overflow-y-auto'
288
+ : 'max-h-[min(60vh,28rem)] overflow-y-auto')
284
289
  ]}
285
290
  >
286
291
  {@render children()}
@@ -62,6 +62,7 @@ export declare const Bento: Story;
62
62
  export declare const Compact: Story;
63
63
  export declare const Split: Story;
64
64
  export declare const Table: Story;
65
+ export declare const ListCompare: Story;
65
66
  export declare const CardsPlusComparison: Story;
66
67
  export declare const TwoPlans: Story;
67
68
  export declare const FourPlans: Story;
@@ -9,7 +9,17 @@ const meta = {
9
9
  argTypes: {
10
10
  layout: {
11
11
  control: 'select',
12
- options: ['grid', 'horizontal', 'vertical', 'bento', 'compact', 'split', 'table']
12
+ options: [
13
+ 'grid',
14
+ 'horizontal',
15
+ 'vertical',
16
+ 'bento',
17
+ 'compact',
18
+ 'split',
19
+ 'table',
20
+ 'list',
21
+ 'compare'
22
+ ]
13
23
  },
14
24
  columns: { control: 'select', options: [2, 3, 4] },
15
25
  planSet: {
@@ -39,6 +49,7 @@ export const Bento = { args: { layout: 'bento', planSet: 'bento' } };
39
49
  export const Compact = { args: { layout: 'compact', planSet: 'four', columns: 4 } };
40
50
  export const Split = { args: { layout: 'split' } };
41
51
  export const Table = { args: { layout: 'table', planSet: 'comparison' } };
52
+ export const ListCompare = { args: { layout: 'list', planSet: 'comparison' } };
42
53
  export const CardsPlusComparison = {
43
54
  args: { layout: 'grid', showComparison: true, planSet: 'comparison' }
44
55
  };
@@ -6,7 +6,9 @@
6
6
  | 'bento'
7
7
  | 'compact'
8
8
  | 'split'
9
- | 'table';
9
+ | 'table'
10
+ | 'list'
11
+ | 'compare';
10
12
 
11
13
  export type PricingFeature =
12
14
  | string
@@ -22,8 +24,12 @@
22
24
  price: string;
23
25
  /** Yearly price when billing toggle is yearly */
24
26
  priceYearly?: string;
27
+ /** Struck-through list price (optional) */
28
+ compareAtPrice?: string;
25
29
  period?: string;
26
30
  periodYearly?: string;
31
+ /** Small hint under the price (e.g. savings) */
32
+ priceNote?: string;
27
33
  description?: string;
28
34
  features: PricingFeature[];
29
35
  cta?: string;
@@ -45,6 +51,8 @@
45
51
 
46
52
  <script lang="ts">
47
53
  import type { Snippet } from 'svelte';
54
+ import Check from '@lucide/svelte/icons/check';
55
+ import X from '@lucide/svelte/icons/x';
48
56
  import Button from '../../atoms/Button/Button.svelte';
49
57
  import Badge from '../../atoms/Badge/Badge.svelte';
50
58
  import SegmentedControl from '../../molecules/SegmentedControl/SegmentedControl.svelte';
@@ -61,7 +69,8 @@
61
69
  /** Show monthly/yearly toggle when plans have priceYearly */
62
70
  showBillingToggle?: boolean;
63
71
  billingPeriod?: 'monthly' | 'yearly';
64
- /** Explicit feature matrix for `layout="table"` (auto-built when omitted) */
72
+ billingLabels?: { monthly?: string; yearly?: string };
73
+ /** Explicit feature matrix for compare/list/table (auto-built when omitted) */
65
74
  comparisonRows?: PricingComparisonRow[];
66
75
  /** Append comparison matrix below card layouts */
67
76
  showComparison?: boolean;
@@ -81,6 +90,7 @@
81
90
  maxFeatures,
82
91
  showBillingToggle = false,
83
92
  billingPeriod = $bindable<'monthly' | 'yearly'>('monthly'),
93
+ billingLabels,
84
94
  comparisonRows,
85
95
  showComparison = false,
86
96
  class: className = '',
@@ -89,6 +99,10 @@
89
99
  onbillingperiodchange
90
100
  }: PricingTableProps = $props();
91
101
 
102
+ const isCompareLayout = $derived(
103
+ layout === 'table' || layout === 'list' || layout === 'compare'
104
+ );
105
+
92
106
  const resolvedColumns = $derived.by(() => {
93
107
  if (columns) return columns;
94
108
  const n = plans.length;
@@ -138,12 +152,16 @@
138
152
  }
139
153
 
140
154
  function displayPeriod(plan: PricingPlan) {
141
- if (billingPeriod === 'yearly') {
155
+ if (billingPeriod === 'yearly' && plan.priceYearly) {
142
156
  return normalizePeriod(plan.periodYearly ?? 'yr');
143
157
  }
144
158
  return normalizePeriod(plan.period);
145
159
  }
146
160
 
161
+ const canToggleBilling = $derived(
162
+ showBillingToggle && plans.some((p) => Boolean(p.priceYearly))
163
+ );
164
+
147
165
  function featureList(plan: PricingPlan) {
148
166
  const list = plan.features.map((f) =>
149
167
  typeof f === 'string' ? { label: f, included: true as boolean | undefined } : f
@@ -189,7 +207,7 @@
189
207
  }));
190
208
  });
191
209
 
192
- const showMatrix = $derived(layout === 'table' || showComparison);
210
+ const showMatrix = $derived(isCompareLayout || showComparison);
193
211
 
194
212
  function select(id: string, disabled?: boolean) {
195
213
  if (disabled) return;
@@ -221,8 +239,52 @@
221
239
  billingPeriod = id as 'monthly' | 'yearly';
222
240
  onbillingperiodchange?.(billingPeriod);
223
241
  }
242
+
243
+ function planColClass(plan: PricingPlan) {
244
+ const selected = selectedId === plan.id;
245
+ return [
246
+ plan.featured && 'bg-brand-50/50 dark:bg-brand-950/20',
247
+ selected && 'bg-brand-50/80 ring-inset ring-2 ring-brand-500/35 dark:bg-brand-950/30'
248
+ ];
249
+ }
224
250
  </script>
225
251
 
252
+ {#snippet priceBlock(plan: PricingPlan, opts?: { large?: boolean; dense?: boolean; align?: 'left' | 'center' | 'right' })}
253
+ {@const large = opts?.large ?? false}
254
+ {@const dense = opts?.dense ?? false}
255
+ {@const align = opts?.align ?? 'left'}
256
+ <div
257
+ class={[
258
+ align === 'center' && 'text-center',
259
+ align === 'right' && 'text-right'
260
+ ]}
261
+ >
262
+ {#if plan.compareAtPrice}
263
+ <p class={['text-muted line-through', dense ? 'text-[11px]' : 'text-xs']}>
264
+ {plan.compareAtPrice}
265
+ </p>
266
+ {/if}
267
+ <p>
268
+ <span
269
+ class={[
270
+ 'font-semibold text-primary',
271
+ large ? 'text-4xl' : dense ? 'text-2xl' : 'text-3xl'
272
+ ]}
273
+ >
274
+ {displayPrice(plan)}
275
+ </span>
276
+ {#if displayPeriod(plan)}
277
+ <span class="text-sm text-muted">{displayPeriod(plan)}</span>
278
+ {/if}
279
+ </p>
280
+ {#if plan.priceNote}
281
+ <p class={['mt-1 font-medium text-brand-600 dark:text-brand-400', dense ? 'text-[11px]' : 'text-xs']}>
282
+ {plan.priceNote}
283
+ </p>
284
+ {/if}
285
+ </div>
286
+ {/snippet}
287
+
226
288
  {#snippet featureRows(plan: PricingPlan, dense = false)}
227
289
  {@const { visible, more } = featureList(plan)}
228
290
  {#if showFeatures && visible.length}
@@ -288,15 +350,8 @@
288
350
  </div>
289
351
  {/if}
290
352
  </div>
291
- <div class="flex shrink-0 flex-col items-stretch gap-3 sm:w-40 sm:items-stretch">
292
- <p class="sm:text-right">
293
- <span class={['font-semibold text-primary', large ? 'text-4xl' : 'text-2xl']}>
294
- {displayPrice(plan)}
295
- </span>
296
- {#if displayPeriod(plan)}
297
- <span class="text-sm text-muted">{displayPeriod(plan)}</span>
298
- {/if}
299
- </p>
353
+ <div class="flex shrink-0 flex-col items-stretch gap-3 sm:w-44 sm:items-stretch">
354
+ {@render priceBlock(plan, { large, dense: true, align: 'right' })}
300
355
  <Button
301
356
  variant={plan.featured || selectedId === plan.id ? 'primary' : 'secondary'}
302
357
  size="sm"
@@ -328,19 +383,9 @@
328
383
  {/if}
329
384
  </div>
330
385
 
331
- <p class={dense ? 'mb-3' : 'mb-4'}>
332
- <span
333
- class={[
334
- 'font-semibold text-primary',
335
- large ? 'text-4xl' : dense ? 'text-2xl' : 'text-3xl'
336
- ]}
337
- >
338
- {displayPrice(plan)}
339
- </span>
340
- {#if displayPeriod(plan)}
341
- <span class="text-sm text-muted">{displayPeriod(plan)}</span>
342
- {/if}
343
- </p>
386
+ <div class={dense ? 'mb-3' : 'mb-4'}>
387
+ {@render priceBlock(plan, { large, dense })}
388
+ </div>
344
389
 
345
390
  <div class="mb-auto flex-1">
346
391
  {@render featureRows(plan, dense)}
@@ -364,13 +409,16 @@
364
409
  {#snippet matrixCell(value: boolean | string)}
365
410
  {#if typeof value === 'boolean'}
366
411
  {#if value}
367
- <span class="inline-flex text-brand-600 dark:text-brand-400" aria-label="Included">
368
- <svg class="mx-auto h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true">
369
- <path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" />
370
- </svg>
412
+ <span
413
+ class="inline-flex text-brand-600 dark:text-brand-400"
414
+ aria-label="Included"
415
+ >
416
+ <Check class="mx-auto h-4 w-4" strokeWidth={2.75} aria-hidden="true" />
371
417
  </span>
372
418
  {:else}
373
- <span class="text-muted" aria-label="Not included">—</span>
419
+ <span class="inline-flex text-muted" aria-label="Not included">
420
+ <X class="mx-auto h-4 w-4" strokeWidth={2.25} aria-hidden="true" />
421
+ </span>
374
422
  {/if}
375
423
  {:else}
376
424
  <span class="text-sm font-medium text-primary">{value}</span>
@@ -378,124 +426,150 @@
378
426
  {/snippet}
379
427
 
380
428
  {#snippet comparisonTable(withPricingHeader: boolean)}
381
- {@const colCount = Math.max(plans.length, 1)}
382
- {@const labelCol = 'minmax(10rem, 13rem)'}
383
- {@const planCols = `repeat(${colCount}, minmax(9rem, 1fr))`}
384
- {@const matrixCols = `${labelCol} ${planCols}`}
385
- <div class="w-full overflow-x-auto rounded-2xl border border-border bg-surface-elevated shadow-sm">
386
- {#if withPricingHeader}
387
- <!-- Same column template as matrix rows so cards line up with values -->
388
- <div
389
- class="grid min-w-160 divide-x divide-border border-b border-border"
390
- style:grid-template-columns={matrixCols}
391
- >
392
- <div class="bg-surface-overlay/30" aria-hidden="true"></div>
393
- {#each plans as plan (plan.id)}
394
- {@const badgeText = plan.badge ?? (plan.featured ? featuredBadgeLabel : undefined)}
395
- <div
396
- class={[
397
- 'flex flex-col items-center px-4 py-6 text-center',
398
- plan.featured && 'bg-brand-50/70 dark:bg-brand-950/25'
399
- ]}
400
- >
401
- <div class="flex min-h-6 flex-wrap items-center justify-center gap-1.5">
402
- <span class="text-sm font-semibold text-primary">{plan.name}</span>
403
- {#if badgeText}
404
- <Badge size="sm" variant="primary" class="whitespace-nowrap">{badgeText}</Badge>
405
- {/if}
406
- </div>
407
- <p class="mt-1 min-h-8 max-w-44 text-xs leading-snug text-secondary">
408
- {plan.description ?? '\u00a0'}
409
- </p>
410
- <p class="mt-3">
411
- <span class="text-3xl font-semibold tracking-tight text-primary">
412
- {displayPrice(plan)}
413
- </span>
414
- {#if displayPeriod(plan)}
415
- <span class="text-xs text-muted">{displayPeriod(plan)}</span>
416
- {/if}
417
- </p>
418
- <Button
419
- variant={plan.featured || selectedId === plan.id ? 'primary' : 'secondary'}
420
- size="sm"
421
- class="mt-4 w-full max-w-40"
422
- disabled={plan.disabled}
423
- onclick={() => select(plan.id, plan.disabled)}
429
+ <div class="w-full overflow-x-auto rounded-xl border border-border bg-surface-elevated">
430
+ <table class="w-full min-w-160 border-collapse text-sm" aria-label="Plan feature comparison">
431
+ <thead class="sticky top-0 z-30">
432
+ {#if withPricingHeader}
433
+ <tr class="border-b border-border bg-surface-elevated shadow-sm">
434
+ <th
435
+ scope="col"
436
+ class="sticky left-0 z-40 bg-surface-elevated px-4 py-4 text-left align-bottom"
424
437
  >
425
- {plan.cta ?? 'Get started'}
426
- </Button>
427
- </div>
428
- {/each}
429
- </div>
430
- {/if}
431
-
432
- <div
433
- class="border-b border-border bg-surface-overlay/60 px-4 py-2.5 text-[11px] font-semibold uppercase tracking-wider text-muted"
434
- >
435
- What's included
436
- </div>
437
-
438
- <div class="min-w-160" role="table" aria-label="Plan feature comparison">
439
- <div
440
- class="grid divide-x divide-border border-b border-border bg-surface/80"
441
- style:grid-template-columns={matrixCols}
442
- role="row"
443
- >
444
- <div class="px-4 py-2.5" role="columnheader"></div>
445
- {#each plans as plan (plan.id)}
446
- <div
438
+ <span class="text-muted text-[11px] font-semibold tracking-wide uppercase">
439
+ Incluye
440
+ </span>
441
+ </th>
442
+ {#each plans as plan (plan.id)}
443
+ {@const badgeText =
444
+ plan.badge ?? (plan.featured ? featuredBadgeLabel : undefined)}
445
+ {@const selected = selectedId === plan.id}
446
+ <th
447
+ scope="col"
448
+ class={[
449
+ 'min-w-40 px-3 py-4 text-center align-top font-normal',
450
+ planColClass(plan)
451
+ ]}
452
+ >
453
+ <button
454
+ type="button"
455
+ class={[
456
+ 'flex w-full flex-col items-center gap-2 rounded-xl px-2 py-2 text-center transition-colors',
457
+ selected && 'bg-brand-500/10',
458
+ !plan.disabled && 'hover:bg-surface-overlay/50',
459
+ plan.disabled && 'cursor-not-allowed opacity-50'
460
+ ]}
461
+ disabled={plan.disabled}
462
+ aria-pressed={selected}
463
+ onclick={() => select(plan.id, plan.disabled)}
464
+ >
465
+ <div class="flex min-h-6 flex-wrap items-center justify-center gap-1.5">
466
+ <span class="text-sm font-semibold text-primary">{plan.name}</span>
467
+ {#if badgeText}
468
+ <Badge size="sm" variant="primary" class="whitespace-nowrap">
469
+ {badgeText}
470
+ </Badge>
471
+ {/if}
472
+ </div>
473
+ {#if plan.description}
474
+ <p class="line-clamp-2 max-w-44 text-xs leading-snug text-secondary">
475
+ {plan.description}
476
+ </p>
477
+ {/if}
478
+ {@render priceBlock(plan, { dense: true, align: 'center' })}
479
+ <span
480
+ class={[
481
+ 'inline-flex h-8 w-full max-w-40 items-center justify-center rounded-lg px-3 text-xs font-medium',
482
+ selected || plan.featured
483
+ ? 'bg-brand-600 text-white'
484
+ : 'bg-surface-overlay text-primary ring-1 ring-border'
485
+ ]}
486
+ >
487
+ {plan.cta ?? 'Select'}
488
+ </span>
489
+ </button>
490
+ </th>
491
+ {/each}
492
+ </tr>
493
+ {:else}
494
+ <tr class="border-b border-border bg-surface/80">
495
+ <th
496
+ scope="col"
497
+ class="sticky left-0 z-40 bg-surface px-4 py-2.5 text-left"
498
+ ></th>
499
+ {#each plans as plan (plan.id)}
500
+ <th
501
+ scope="col"
502
+ class={[
503
+ 'px-3 py-2.5 text-center text-xs font-semibold text-secondary',
504
+ planColClass(plan)
505
+ ]}
506
+ >
507
+ <button
508
+ type="button"
509
+ class="w-full rounded-md px-1 py-0.5 hover:bg-surface-overlay/60"
510
+ disabled={plan.disabled}
511
+ aria-pressed={selectedId === plan.id}
512
+ onclick={() => select(plan.id, plan.disabled)}
513
+ >
514
+ {plan.name}
515
+ </button>
516
+ </th>
517
+ {/each}
518
+ </tr>
519
+ {/if}
520
+ </thead>
521
+ <tbody>
522
+ {#each resolvedComparisonRows as row, ri (row.id)}
523
+ <tr
447
524
  class={[
448
- 'px-4 py-2.5 text-center text-xs font-semibold text-secondary',
449
- plan.featured && 'bg-brand-50/40 text-brand-700 dark:bg-brand-950/15 dark:text-brand-300'
525
+ 'border-b border-border last:border-b-0',
526
+ ri % 2 === 1 && 'bg-surface-overlay/25'
450
527
  ]}
451
- role="columnheader"
452
528
  >
453
- {plan.name}
454
- </div>
455
- {/each}
456
- </div>
457
-
458
- {#each resolvedComparisonRows as row, ri (row.id)}
459
- <div
460
- class={[
461
- 'grid divide-x divide-border border-b border-border last:border-b-0',
462
- ri % 2 === 1 && 'bg-surface-overlay/20'
463
- ]}
464
- style:grid-template-columns={matrixCols}
465
- role="row"
466
- >
467
- <div
468
- class="flex items-center px-4 py-3.5 text-sm font-medium text-primary"
469
- role="rowheader"
470
- >
471
- {row.label}
472
- </div>
473
- {#each row.values as value, vi}
474
- {@const plan = plans[vi]}
475
- <div
529
+ <th
530
+ scope="row"
476
531
  class={[
477
- 'flex items-center justify-center px-4 py-3.5 text-center',
478
- plan?.featured && 'bg-brand-50/30 dark:bg-brand-950/10'
532
+ 'sticky left-0 z-10 border-r border-border px-4 py-3 text-left text-sm font-medium text-primary',
533
+ ri % 2 === 1 ? 'bg-surface-overlay/40' : 'bg-surface-elevated'
479
534
  ]}
480
- role="cell"
481
535
  >
482
- {@render matrixCell(value)}
483
- </div>
484
- {/each}
485
- </div>
486
- {/each}
487
- </div>
536
+ {row.label}
537
+ </th>
538
+ {#each row.values as value, vi (`${row.id}-${plans[vi]?.id ?? vi}`)}
539
+ {@const plan = plans[vi]}
540
+ <td
541
+ class={[
542
+ 'px-3 py-3 text-center',
543
+ plan && planColClass(plan)
544
+ ]}
545
+ >
546
+ {@render matrixCell(value)}
547
+ </td>
548
+ {/each}
549
+ </tr>
550
+ {:else}
551
+ <tr>
552
+ <td
553
+ class="text-muted px-4 py-8 text-center text-sm"
554
+ colspan={Math.max(plans.length, 1) + 1}
555
+ >
556
+ Sin características para comparar.
557
+ </td>
558
+ </tr>
559
+ {/each}
560
+ </tbody>
561
+ </table>
488
562
  </div>
489
563
  {/snippet}
490
564
 
491
565
  <div class={['w-full space-y-4', className]}>
492
- {#if showBillingToggle}
566
+ {#if canToggleBilling}
493
567
  <div class="flex justify-center">
494
568
  <SegmentedControl
495
569
  size="sm"
496
570
  items={[
497
- { id: 'monthly', label: 'Monthly' },
498
- { id: 'yearly', label: 'Yearly' }
571
+ { id: 'monthly', label: billingLabels?.monthly ?? 'Monthly' },
572
+ { id: 'yearly', label: billingLabels?.yearly ?? 'Yearly' }
499
573
  ]}
500
574
  bind:value={billingPeriod}
501
575
  onchange={onBillingChange}
@@ -503,7 +577,11 @@
503
577
  </div>
504
578
  {/if}
505
579
 
506
- {#if layout === 'table'}
580
+ {#if plans.length === 0}
581
+ <p class="text-muted rounded-xl border border-border bg-surface-elevated px-4 py-8 text-center text-sm">
582
+ No hay tarifas disponibles para este periodo.
583
+ </p>
584
+ {:else if isCompareLayout}
507
585
  {@render comparisonTable(true)}
508
586
  {:else if layout === 'split' && featuredPlan}
509
587
  <div class={containerClass} role="list">
@@ -553,7 +631,7 @@
553
631
  </div>
554
632
  {/if}
555
633
 
556
- {#if showMatrix && layout !== 'table'}
634
+ {#if showMatrix && !isCompareLayout}
557
635
  <div class="pt-2">
558
636
  {@render comparisonTable(false)}
559
637
  </div>
@@ -1,4 +1,4 @@
1
- export type PricingLayout = 'grid' | 'horizontal' | 'vertical' | 'bento' | 'compact' | 'split' | 'table';
1
+ export type PricingLayout = 'grid' | 'horizontal' | 'vertical' | 'bento' | 'compact' | 'split' | 'table' | 'list' | 'compare';
2
2
  export type PricingFeature = string | {
3
3
  label: string;
4
4
  included?: boolean;
@@ -10,8 +10,12 @@ export interface PricingPlan {
10
10
  price: string;
11
11
  /** Yearly price when billing toggle is yearly */
12
12
  priceYearly?: string;
13
+ /** Struck-through list price (optional) */
14
+ compareAtPrice?: string;
13
15
  period?: string;
14
16
  periodYearly?: string;
17
+ /** Small hint under the price (e.g. savings) */
18
+ priceNote?: string;
15
19
  description?: string;
16
20
  features: PricingFeature[];
17
21
  cta?: string;
@@ -41,7 +45,11 @@ interface PricingTableProps {
41
45
  /** Show monthly/yearly toggle when plans have priceYearly */
42
46
  showBillingToggle?: boolean;
43
47
  billingPeriod?: 'monthly' | 'yearly';
44
- /** Explicit feature matrix for `layout="table"` (auto-built when omitted) */
48
+ billingLabels?: {
49
+ monthly?: string;
50
+ yearly?: string;
51
+ };
52
+ /** Explicit feature matrix for compare/list/table (auto-built when omitted) */
45
53
  comparisonRows?: PricingComparisonRow[];
46
54
  /** Append comparison matrix below card layouts */
47
55
  showComparison?: boolean;
@@ -190,7 +190,9 @@
190
190
  const widthClass = $derived.by(() => {
191
191
  const layout = props.layout ?? 'grid';
192
192
  if (layout === 'vertical') return 'w-112 max-w-full';
193
- if (layout === 'table' || props.showComparison) return 'w-208 max-w-full';
193
+ if (layout === 'table' || layout === 'list' || layout === 'compare' || props.showComparison) {
194
+ return 'w-208 max-w-full';
195
+ }
194
196
  if (layout === 'horizontal') return 'w-192 max-w-full';
195
197
  if (layout === 'bento' || layout === 'split') return 'w-208 max-w-full';
196
198
  if ((props.columns ?? 3) >= 4 || planSet === 'four') return 'w-224 max-w-full';
@@ -209,7 +211,12 @@
209
211
  showFeatures={props.showFeatures ?? true}
210
212
  showComparison={props.showComparison ?? false}
211
213
  maxFeatures={props.maxFeatures}
212
- comparisonRows={planSet === 'comparison' || props.layout === 'table' ? comparisonRows : undefined}
214
+ comparisonRows={planSet === 'comparison' ||
215
+ props.layout === 'table' ||
216
+ props.layout === 'list' ||
217
+ props.layout === 'compare'
218
+ ? comparisonRows
219
+ : undefined}
213
220
  onselect={(id) => (selectedId = id)}
214
221
  >
215
222
  {#snippet footer()}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@r2digisolutions/components",
3
- "version": "0.19.8",
3
+ "version": "0.19.9",
4
4
  "private": false,
5
5
  "description": "R2DigiSolutions Svelte 5 component library — Atomic Design, Tailwind 4, Light/Dark mode",
6
6
  "license": "MIT",