@makolabs/ripple 3.0.7 → 3.0.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.
@@ -1,51 +1,216 @@
1
1
  import { tv } from 'tailwind-variants';
2
- import { Color } from '../../variants.js';
2
+ import { Color, Size } from '../../variants.js';
3
3
  export const card = tv({
4
4
  slots: {
5
- base: 'flex flex-col p-4 gap-2 border rounded-md shadow-sm',
6
- title: 'text-base font-semibold',
7
- body: 'text-base'
5
+ base: 'flex flex-col overflow-hidden border bg-white',
6
+ header: 'flex items-center justify-between gap-3',
7
+ headerLeft: 'flex min-w-0 flex-col',
8
+ headerEnd: 'text-default-700 flex shrink-0 items-center gap-3',
9
+ title: 'font-semibold leading-tight',
10
+ subtitle: 'text-default-500 font-normal',
11
+ body: ''
8
12
  },
9
13
  variants: {
10
14
  color: {
11
15
  [Color.DEFAULT]: {
12
- base: 'bg-default-50 border-default-200',
16
+ base: 'border-default-200',
13
17
  title: 'text-default-900',
14
18
  body: 'text-default-700'
15
19
  },
16
20
  [Color.PRIMARY]: {
17
- base: 'bg-primary-50 border-primary-200',
21
+ base: 'border-primary-200',
18
22
  title: 'text-primary-900',
19
- body: 'text-primary-700'
23
+ body: 'text-primary-800'
20
24
  },
21
25
  [Color.SECONDARY]: {
22
- base: 'bg-secondary-50 border-secondary-200',
26
+ base: 'border-secondary-200',
23
27
  title: 'text-secondary-900',
24
- body: 'text-secondary-700'
28
+ body: 'text-secondary-800'
25
29
  },
26
30
  [Color.INFO]: {
27
- base: 'bg-info-50 border-info-200',
31
+ base: 'border-info-200',
28
32
  title: 'text-info-900',
29
- body: 'text-info-700'
33
+ body: 'text-info-800'
30
34
  },
31
35
  [Color.SUCCESS]: {
32
- base: 'bg-success-50 border-success-200',
36
+ base: 'border-success-200',
33
37
  title: 'text-success-900',
34
- body: 'text-success-700'
38
+ body: 'text-success-800'
35
39
  },
36
40
  [Color.WARNING]: {
37
- base: 'bg-warning-50 border-warning-200',
41
+ base: 'border-warning-200',
38
42
  title: 'text-warning-900',
39
- body: 'text-warning-700'
43
+ body: 'text-warning-800'
40
44
  },
41
45
  [Color.DANGER]: {
42
- base: 'bg-danger-50 border-danger-200',
46
+ base: 'border-danger-200',
43
47
  title: 'text-danger-900',
44
- body: 'text-danger-700'
48
+ body: 'text-danger-800'
49
+ }
50
+ },
51
+ size: {
52
+ [Size.XS]: {
53
+ title: 'text-[11px]',
54
+ subtitle: 'text-[10px]',
55
+ body: 'text-[11px]'
56
+ },
57
+ [Size.SM]: {
58
+ title: 'text-xs',
59
+ subtitle: 'text-[11px]',
60
+ body: 'text-xs'
61
+ },
62
+ [Size.MD]: {
63
+ title: 'text-sm',
64
+ subtitle: 'text-xs',
65
+ body: 'text-sm'
66
+ },
67
+ [Size.LG]: {
68
+ title: 'text-base',
69
+ subtitle: 'text-sm',
70
+ body: 'text-base'
71
+ },
72
+ [Size.XL]: {
73
+ title: 'text-lg',
74
+ subtitle: 'text-sm',
75
+ body: 'text-base'
76
+ },
77
+ [Size.XXL]: {
78
+ title: 'text-lg',
79
+ subtitle: 'text-sm',
80
+ body: 'text-base'
81
+ }
82
+ },
83
+ headerStyle: {
84
+ tinted: {
85
+ // `bg-default-50` is pure white in this theme, so the next step up
86
+ // (`default-100`) is needed for the tint to be visible. /50 keeps it
87
+ // soft so the chrome reads as ambient texture, not a hard fill.
88
+ header: 'bg-default-100/50 border-default-200 border-b'
89
+ },
90
+ plain: {
91
+ header: ''
45
92
  }
46
93
  }
47
94
  },
95
+ compoundVariants: [
96
+ // Tinted style: outer has no padding, regions own their padding (size variant adds shadow).
97
+ {
98
+ headerStyle: 'tinted',
99
+ class: { base: 'p-0' }
100
+ },
101
+ // Plain style: outer has padding (size variant) + a vertical gap between header & body.
102
+ {
103
+ headerStyle: 'plain',
104
+ class: { header: 'mb-2' }
105
+ },
106
+ // Size × headerStyle: per-region paddings + radius for tinted; uniform padding for plain.
107
+ // Tinted: chrome is generous-but-compact (matches dashboard-panel reference);
108
+ // typography is compact (set in size variant above).
109
+ {
110
+ size: Size.XS,
111
+ headerStyle: 'tinted',
112
+ class: { base: 'rounded-sm', header: 'px-2 py-1.5', body: 'p-2' }
113
+ },
114
+ {
115
+ size: Size.SM,
116
+ headerStyle: 'tinted',
117
+ class: { base: 'rounded-md shadow-xs', header: 'px-3 py-2', body: 'p-3' }
118
+ },
119
+ {
120
+ size: Size.MD,
121
+ headerStyle: 'tinted',
122
+ class: { base: 'rounded-lg shadow-sm', header: 'px-4 py-2.5', body: 'p-4' }
123
+ },
124
+ {
125
+ size: Size.LG,
126
+ headerStyle: 'tinted',
127
+ class: { base: 'rounded-lg shadow-sm', header: 'px-5 py-3', body: 'p-5' }
128
+ },
129
+ {
130
+ size: Size.XL,
131
+ headerStyle: 'tinted',
132
+ class: { base: 'rounded-xl shadow-md', header: 'px-6 py-4', body: 'p-6' }
133
+ },
134
+ {
135
+ size: Size.XXL,
136
+ headerStyle: 'tinted',
137
+ class: { base: 'rounded-xl shadow-md', header: 'px-6 py-4', body: 'p-6' }
138
+ },
139
+ // Plain (uniform outer padding; shadow ladder mirrors tinted)
140
+ { size: Size.XS, headerStyle: 'plain', class: { base: 'rounded-sm p-2' } },
141
+ { size: Size.SM, headerStyle: 'plain', class: { base: 'rounded-md shadow-xs p-3' } },
142
+ { size: Size.MD, headerStyle: 'plain', class: { base: 'rounded-lg shadow-sm p-4' } },
143
+ { size: Size.LG, headerStyle: 'plain', class: { base: 'rounded-lg shadow-sm p-5' } },
144
+ { size: Size.XL, headerStyle: 'plain', class: { base: 'rounded-xl shadow-md p-6' } },
145
+ { size: Size.XXL, headerStyle: 'plain', class: { base: 'rounded-xl shadow-md p-6' } },
146
+ // Color × tinted: replace header bg + body bg to keep the tint family.
147
+ // Default keeps the neutral header. Other colors get a slightly stronger
148
+ // tinted header so the chrome still reads as "header" within a tinted card.
149
+ {
150
+ color: Color.PRIMARY,
151
+ headerStyle: 'tinted',
152
+ class: { base: 'bg-primary-50/30', header: 'bg-primary-100/40 border-primary-200' }
153
+ },
154
+ {
155
+ color: Color.SECONDARY,
156
+ headerStyle: 'tinted',
157
+ class: { base: 'bg-secondary-50/30', header: 'bg-secondary-100/40 border-secondary-200' }
158
+ },
159
+ {
160
+ color: Color.INFO,
161
+ headerStyle: 'tinted',
162
+ class: { base: 'bg-info-50/30', header: 'bg-info-100/40 border-info-200' }
163
+ },
164
+ {
165
+ color: Color.SUCCESS,
166
+ headerStyle: 'tinted',
167
+ class: { base: 'bg-success-50/30', header: 'bg-success-100/40 border-success-200' }
168
+ },
169
+ {
170
+ color: Color.WARNING,
171
+ headerStyle: 'tinted',
172
+ class: { base: 'bg-warning-50/30', header: 'bg-warning-100/40 border-warning-200' }
173
+ },
174
+ {
175
+ color: Color.DANGER,
176
+ headerStyle: 'tinted',
177
+ class: { base: 'bg-danger-50/30', header: 'bg-danger-100/40 border-danger-200' }
178
+ },
179
+ // Color × plain: keep the legacy "tinted whole card" look (back-compat).
180
+ {
181
+ color: Color.PRIMARY,
182
+ headerStyle: 'plain',
183
+ class: { base: 'bg-primary-50' }
184
+ },
185
+ {
186
+ color: Color.SECONDARY,
187
+ headerStyle: 'plain',
188
+ class: { base: 'bg-secondary-50' }
189
+ },
190
+ {
191
+ color: Color.INFO,
192
+ headerStyle: 'plain',
193
+ class: { base: 'bg-info-50' }
194
+ },
195
+ {
196
+ color: Color.SUCCESS,
197
+ headerStyle: 'plain',
198
+ class: { base: 'bg-success-50' }
199
+ },
200
+ {
201
+ color: Color.WARNING,
202
+ headerStyle: 'plain',
203
+ class: { base: 'bg-warning-50' }
204
+ },
205
+ {
206
+ color: Color.DANGER,
207
+ headerStyle: 'plain',
208
+ class: { base: 'bg-danger-50' }
209
+ }
210
+ ],
48
211
  defaultVariants: {
49
- color: Color.DEFAULT
212
+ color: Color.DEFAULT,
213
+ size: Size.MD,
214
+ headerStyle: 'tinted'
50
215
  }
51
216
  });
@@ -607,29 +607,8 @@
607
607
  {/snippet}
608
608
 
609
609
  {#if hasHeader}
610
- <Card>
611
- {#snippet custom()}
612
- <!-- Header Section -->
613
- <div class="border-default-200 mb-4 border-b pb-3">
614
- <div class="flex items-center justify-between">
615
- <div>
616
- {#if title}
617
- <h2 class="text-default-900 text-lg font-semibold">{title}</h2>
618
- {/if}
619
- {#if subtitle}
620
- <p class="text-default-500 mt-1 text-xs">{subtitle}</p>
621
- {/if}
622
- </div>
623
- {#if headerActions}
624
- <div class="flex items-center">
625
- {@render headerActions()}
626
- </div>
627
- {/if}
628
- </div>
629
- </div>
630
-
631
- {@render tableContent()}
632
- {/snippet}
610
+ <Card {title} {subtitle} headerEnd={headerActions} bodyClass="p-0">
611
+ {@render tableContent()}
633
612
  </Card>
634
613
  {:else}
635
614
  {@render tableContent()}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@makolabs/ripple",
3
- "version": "3.0.7",
3
+ "version": "3.0.9",
4
4
  "description": "Simple Svelte 5 powered component library ✨",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "repository": {
@@ -1,79 +0,0 @@
1
- <script lang="ts">
2
- import { cn } from '../../helper/cls.js';
3
- import { buildTestId } from '../../helper/testid.js';
4
- import { rankedCard, type RankedCardProps } from './ranked-card.js';
5
-
6
- let { items, columns = 3, class: className = '', testId }: RankedCardProps = $props();
7
-
8
- const {
9
- container,
10
- card,
11
- header,
12
- rank,
13
- title,
14
- metricsContainer,
15
- metricRow,
16
- metricLabel,
17
- metricValue,
18
- actionContainer,
19
- actionText,
20
- actionLabel
21
- } = $derived(rankedCard({ columns }));
22
-
23
- const containerClass = $derived(cn(container(), className));
24
- </script>
25
-
26
- <div class={containerClass} data-testid={buildTestId('ranked-card', undefined, testId)}>
27
- {#each items as item, index (item.rank)}
28
- <div class={card()} data-testid={buildTestId('ranked-card', 'card', testId, index)}>
29
- <!-- Header with Rank and Title -->
30
- <div class={header()}>
31
- <span class={rank()} data-testid={buildTestId('ranked-card', 'rank', testId, index)}
32
- >#{item.rank}</span
33
- >
34
- <h4 class={title()} data-testid={buildTestId('ranked-card', 'title', testId, index)}>
35
- {item.title}
36
- </h4>
37
- </div>
38
-
39
- <!-- Metrics -->
40
- <div class={metricsContainer()}>
41
- {#each item.metrics as metric, metricIndex (metric.label)}
42
- <div class={metricRow()}>
43
- <span
44
- class={metricLabel()}
45
- data-testid={buildTestId(
46
- 'ranked-card',
47
- 'metric-label',
48
- testId,
49
- `${index}-${metricIndex}`
50
- )}>{metric.label}:</span
51
- >
52
- <span
53
- class={cn(metricValue(), metric.color || '')}
54
- data-testid={buildTestId(
55
- 'ranked-card',
56
- 'metric-value',
57
- testId,
58
- `${index}-${metricIndex}`
59
- )}>{metric.value}</span
60
- >
61
- </div>
62
- {/each}
63
- </div>
64
-
65
- <!-- Action/Recommendation -->
66
- {#if item.action}
67
- <div
68
- class={actionContainer()}
69
- data-testid={buildTestId('ranked-card', 'action', testId, index)}
70
- >
71
- <p class={actionText()}>
72
- <span class={actionLabel()}>Action:</span>
73
- <span class={item.action.color || ''}>{item.action.text}</span>
74
- </p>
75
- </div>
76
- {/if}
77
- </div>
78
- {/each}
79
- </div>
@@ -1,4 +0,0 @@
1
- import { type RankedCardProps } from './ranked-card.js';
2
- declare const RankedCard: import("svelte").Component<RankedCardProps, {}, "">;
3
- type RankedCard = ReturnType<typeof RankedCard>;
4
- export default RankedCard;
@@ -1,115 +0,0 @@
1
- import type { ClassValue } from 'tailwind-variants';
2
- export declare const rankedCard: import("tailwind-variants").TVReturnType<{
3
- columns: {
4
- 1: {
5
- container: string;
6
- };
7
- 2: {
8
- container: string;
9
- };
10
- 3: {
11
- container: string;
12
- };
13
- 4: {
14
- container: string;
15
- };
16
- };
17
- }, {
18
- container: string;
19
- card: string;
20
- header: string;
21
- rank: string;
22
- title: string;
23
- metricsContainer: string;
24
- metricRow: string;
25
- metricLabel: string;
26
- metricValue: string;
27
- actionContainer: string;
28
- actionText: string;
29
- actionLabel: string;
30
- }, undefined, {
31
- columns: {
32
- 1: {
33
- container: string;
34
- };
35
- 2: {
36
- container: string;
37
- };
38
- 3: {
39
- container: string;
40
- };
41
- 4: {
42
- container: string;
43
- };
44
- };
45
- }, {
46
- container: string;
47
- card: string;
48
- header: string;
49
- rank: string;
50
- title: string;
51
- metricsContainer: string;
52
- metricRow: string;
53
- metricLabel: string;
54
- metricValue: string;
55
- actionContainer: string;
56
- actionText: string;
57
- actionLabel: string;
58
- }, import("tailwind-variants").TVReturnType<{
59
- columns: {
60
- 1: {
61
- container: string;
62
- };
63
- 2: {
64
- container: string;
65
- };
66
- 3: {
67
- container: string;
68
- };
69
- 4: {
70
- container: string;
71
- };
72
- };
73
- }, {
74
- container: string;
75
- card: string;
76
- header: string;
77
- rank: string;
78
- title: string;
79
- metricsContainer: string;
80
- metricRow: string;
81
- metricLabel: string;
82
- metricValue: string;
83
- actionContainer: string;
84
- actionText: string;
85
- actionLabel: string;
86
- }, undefined, unknown, unknown, undefined>>;
87
- export type RankedCardMetric = {
88
- label: string;
89
- value: string;
90
- color?: string;
91
- };
92
- export type RankedCardItem = {
93
- rank: number;
94
- title: string;
95
- metrics: RankedCardMetric[];
96
- action?: {
97
- text: string;
98
- color?: string;
99
- };
100
- };
101
- export type RankedCardProps = {
102
- items: RankedCardItem[];
103
- columns?: 1 | 2 | 3 | 4;
104
- class?: ClassValue;
105
- /**
106
- * Test ID prefix. When set, the component emits these selectors:
107
- * - `{testId}-ranked-card` — root wrapper
108
- * - `{testId}-ranked-card-item-{i}` — each card
109
- * - `{testId}-ranked-card-rank-{i}` — rank number
110
- * - `{testId}-ranked-card-title-{i}` — title
111
- * - `{testId}-ranked-card-metric-label-{i}-{j}` — metric label
112
- * - `{testId}-ranked-card-metric-value-{i}-{j}` — metric value
113
- */
114
- testId?: string;
115
- };
@@ -1,28 +0,0 @@
1
- import { tv } from 'tailwind-variants';
2
- export const rankedCard = tv({
3
- slots: {
4
- container: 'grid gap-4',
5
- card: 'border border-default-200 rounded-lg p-4',
6
- header: 'flex items-center gap-2 mb-3',
7
- rank: 'text-base font-bold text-default-400',
8
- title: 'text-sm font-semibold text-default-900',
9
- metricsContainer: 'space-y-2 mb-3',
10
- metricRow: 'flex items-center justify-between text-sm',
11
- metricLabel: 'text-default-600',
12
- metricValue: 'font-medium text-default-900',
13
- actionContainer: 'pt-3 border-t border-default-200',
14
- actionText: 'text-xs text-default-600',
15
- actionLabel: 'font-semibold text-default-700'
16
- },
17
- variants: {
18
- columns: {
19
- 1: { container: 'grid-cols-1' },
20
- 2: { container: 'grid-cols-1 md:grid-cols-2' },
21
- 3: { container: 'grid-cols-1 md:grid-cols-3' },
22
- 4: { container: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-4' }
23
- }
24
- },
25
- defaultVariants: {
26
- columns: 3
27
- }
28
- });