@makolabs/ripple 1.10.0 → 1.11.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.
package/dist/index.d.ts CHANGED
@@ -49,7 +49,8 @@ export { isRouteActive } from './helper/nav.svelte.js';
49
49
  export { default as Button } from './button/Button.svelte';
50
50
  export { default as Modal } from './modal/Modal.svelte';
51
51
  export { default as Pipeline } from './pipeline/Pipeline.svelte';
52
- export type { PipelineStage } from './pipeline/Pipeline.svelte';
52
+ export { pipelineVariants } from './pipeline/pipeline.js';
53
+ export type { PipelineStage, PipelineStageColor, PipelineStageEvent, PipelineStageClickEvent, PipelineStagePointerEvent, PipelineProps } from './pipeline/pipeline-types.js';
53
54
  export { default as Drawer } from './drawer/Drawer.svelte';
54
55
  export { default as PageHeader } from './header/PageHeader.svelte';
55
56
  export { default as Breadcrumbs } from './header/Breadcrumbs.svelte';
package/dist/index.js CHANGED
@@ -26,6 +26,7 @@ export { default as Button } from './button/Button.svelte';
26
26
  export { default as Modal } from './modal/Modal.svelte';
27
27
  // Pipeline
28
28
  export { default as Pipeline } from './pipeline/Pipeline.svelte';
29
+ export { pipelineVariants } from './pipeline/pipeline.js';
29
30
  // Drawer
30
31
  export { default as Drawer } from './drawer/Drawer.svelte';
31
32
  // Header
@@ -1,174 +1,123 @@
1
1
  <script lang="ts">
2
- import { tv } from 'tailwind-variants';
3
- import type { Snippet } from 'svelte';
2
+ import { cn } from '../helper/cls.js';
3
+ import { pipelineVariants } from './pipeline.js';
4
+ import type { PipelineProps, PipelineStage } from '../index.js';
4
5
 
5
- export type PipelineStage = {
6
- label: string;
7
- count?: number | string;
8
- color?: 'default' | 'primary' | 'success' | 'warning' | 'danger' | 'info';
9
- active?: boolean;
10
- };
11
-
12
- interface Props {
13
- stages: PipelineStage[];
14
- class?: string;
15
- size?: 'sm' | 'base' | 'lg';
16
- equalWidth?: boolean;
17
- children?: Snippet<[PipelineStage, number]>;
18
- beneathChildren?: Snippet<[PipelineStage, number]>;
19
- }
6
+ const CHEVRON_WIDTH = 20;
20
7
 
21
8
  let {
22
9
  stages = [],
23
10
  class: className = '',
24
11
  size = 'base',
25
12
  equalWidth = true,
13
+ selectedClass,
14
+ unselectedClass,
15
+ disabledClass,
16
+ onstageclick,
17
+ onstagehover,
18
+ onstageleave,
26
19
  children,
27
20
  beneathChildren
28
- }: Props = $props();
29
-
30
- const pipeline = tv({
31
- slots: {
32
- container: 'flex items-center gap-0 w-full',
33
- stage: 'relative transition-all duration-300',
34
- borderLayer: 'absolute inset-0 transition-all duration-300',
35
- innerLayer: 'relative flex items-center justify-center bg-white transition-all duration-300',
36
- content: 'text-center flex flex-col items-center justify-center',
37
- label: 'font-medium text-default-600 leading-tight break-words',
38
- count: 'font-bold text-default-900'
39
- },
40
- variants: {
41
- size: {
42
- sm: {
43
- stage: 'min-w-[130px]',
44
- borderLayer: 'h-20',
45
- innerLayer: 'h-20',
46
- label: 'text-[11px] max-w-[100px]',
47
- count: 'text-xl mt-0.5',
48
- content: 'px-4'
49
- },
50
- base: {
51
- stage: 'min-w-[150px]',
52
- borderLayer: 'h-24',
53
- innerLayer: 'h-24',
54
- label: 'text-xs max-w-[120px]',
55
- count: 'text-2xl mt-1',
56
- content: 'px-5'
57
- },
58
- lg: {
59
- stage: 'min-w-[180px]',
60
- borderLayer: 'h-28',
61
- innerLayer: 'h-28',
62
- label: 'text-sm max-w-[150px]',
63
- count: 'text-3xl mt-1',
64
- content: 'px-6'
65
- }
66
- },
67
- color: {
68
- default: {
69
- borderLayer: 'bg-default-200'
70
- },
71
- primary: {
72
- borderLayer: 'bg-blue-500',
73
- label: 'text-default-700',
74
- count: 'text-default-900'
75
- },
76
- success: {
77
- borderLayer: 'bg-green-500',
78
- label: 'text-default-700',
79
- count: 'text-default-900'
80
- },
81
- warning: {
82
- borderLayer: 'bg-amber-500',
83
- label: 'text-default-700',
84
- count: 'text-default-900'
85
- },
86
- danger: {
87
- borderLayer: 'bg-rose-500',
88
- label: 'text-default-700',
89
- count: 'text-default-900'
90
- },
91
- info: {
92
- borderLayer: 'bg-purple-500',
93
- label: 'text-default-700',
94
- count: 'text-default-900'
95
- }
96
- },
97
- active: {
98
- true: {
99
- borderLayer: 'ring-2 ring-blue-500 ring-offset-2'
100
- }
101
- },
102
- equalWidth: {
103
- true: {
104
- stage: 'flex-1'
105
- },
106
- false: {
107
- stage: ''
108
- }
109
- }
110
- }
111
- });
21
+ }: PipelineProps = $props();
112
22
 
113
- const styles = $derived(pipeline({ size, equalWidth }));
23
+ const containerStyles = $derived(pipelineVariants({ size, equalWidth }));
114
24
 
115
25
  function getStageStyles(stage: PipelineStage) {
116
- return pipeline({
26
+ return pipelineVariants({
117
27
  size,
118
- color: stage.color || 'default',
28
+ color: stage.color ?? 'default',
119
29
  active: stage.active,
120
- equalWidth
30
+ equalWidth,
31
+ interactive: !!onstageclick && !stage.disabled,
32
+ disabled: stage.disabled
121
33
  });
122
34
  }
35
+
36
+ function borderClassesFor(stage: PipelineStage, base: string): string {
37
+ return cn(base, stage.active ? selectedClass : unselectedClass);
38
+ }
39
+
40
+ function borderClipPath(isFirst: boolean): string {
41
+ const leftTip = isFirst ? '0' : `${CHEVRON_WIDTH}px`;
42
+ return `polygon(0 0%, calc(100% - ${CHEVRON_WIDTH}px) 0%, 100% 50%, calc(100% - ${CHEVRON_WIDTH}px) 100%, 0 100%, ${leftTip} 50%)`;
43
+ }
44
+
45
+ function innerClipPath(isFirst: boolean): string {
46
+ const leftTip = isFirst ? '2px' : `${CHEVRON_WIDTH + 2}px`;
47
+ return `polygon(2px 2px, calc(100% - ${CHEVRON_WIDTH}px - 2px) 2px, calc(100% - 2px) 50%, calc(100% - ${CHEVRON_WIDTH}px - 2px) calc(100% - 2px), 2px calc(100% - 2px), ${leftTip} 50%)`;
48
+ }
49
+
50
+ function handleStageClick(stage: PipelineStage, index: number, event: MouseEvent) {
51
+ if (stage.disabled || !onstageclick) return;
52
+ onstageclick({ stage, index, event });
53
+ }
54
+
55
+ function handleStageMouseEnter(stage: PipelineStage, index: number, event: MouseEvent) {
56
+ if (stage.disabled || !onstagehover) return;
57
+ onstagehover({ stage, index, event });
58
+ }
59
+
60
+ function handleStageMouseLeave(stage: PipelineStage, index: number, event: MouseEvent) {
61
+ if (stage.disabled || !onstageleave) return;
62
+ onstageleave({ stage, index, event });
63
+ }
123
64
  </script>
124
65
 
125
- <div class="{styles.container()} {className}">
66
+ {#snippet stageContent(stage: PipelineStage, index: number, s: ReturnType<typeof getStageStyles>)}
67
+ <div class={s.content()}>
68
+ {#if children}
69
+ {@render children(stage, index)}
70
+ {:else}
71
+ <span class={s.label()}>{stage.label}</span>
72
+ {#if stage.count !== undefined}
73
+ <span class={s.count()}>{stage.count}</span>
74
+ {/if}
75
+ {/if}
76
+ </div>
77
+ {/snippet}
78
+
79
+ <div class={cn(containerStyles.container(), className)}>
126
80
  {#each stages as stage, index (stage.label + index)}
81
+ {@const s = getStageStyles(stage)}
127
82
  {@const isFirst = index === 0}
128
- {@const chevronWidth = 20}
83
+ {@const renderAsButton = !!onstageclick}
84
+ {@const stageStyle = isFirst
85
+ ? `z-index: ${stages.length - index}`
86
+ : `margin-left: -${CHEVRON_WIDTH}px; z-index: ${stages.length - index}`}
129
87
 
130
88
  <div
131
- class="flex flex-col {getStageStyles(stage).stage()}"
132
- style={!isFirst
133
- ? `margin-left: -${chevronWidth}px; z-index: ${stages.length - index}`
134
- : `z-index: ${stages.length - index}`}
89
+ class={cn('flex flex-col', s.stage(), stage.disabled ? disabledClass : undefined)}
90
+ style={stageStyle}
91
+ data-pipeline-stage=""
92
+ role={onstagehover || onstageleave ? 'group' : undefined}
93
+ aria-label={onstagehover || onstageleave ? stage.label : undefined}
94
+ aria-disabled={stage.disabled ? 'true' : undefined}
95
+ onmouseenter={onstagehover ? (e) => handleStageMouseEnter(stage, index, e) : undefined}
96
+ onmouseleave={onstageleave ? (e) => handleStageMouseLeave(stage, index, e) : undefined}
135
97
  >
136
- <!-- BACKGROUND LAYER (Border color - larger) -->
137
98
  <div
138
- class={getStageStyles(stage).borderLayer()}
139
- style="clip-path: polygon({isFirst
140
- ? '0'
141
- : '0'} 0%, calc(100% - {chevronWidth}px) 0%, 100% 50%, calc(100% - {chevronWidth}px) 100%, {isFirst
142
- ? '0'
143
- : '0'} 100%, {isFirst ? '0' : `${chevronWidth}px`} 50%);"
99
+ class={borderClassesFor(stage, s.borderLayer())}
100
+ data-pipeline-border=""
101
+ style="clip-path: {borderClipPath(isFirst)};"
144
102
  ></div>
145
103
 
146
- <!-- FOREGROUND LAYER (White - smaller, creates border effect) -->
147
- <div
148
- class={getStageStyles(stage).innerLayer()}
149
- style="clip-path: polygon({isFirst
150
- ? '2px'
151
- : '2px'} 2px, calc(100% - {chevronWidth}px - 2px) 2px, calc(100% - 2px) 50%, calc(100% - {chevronWidth}px - 2px) calc(100% - 2px), {isFirst
152
- ? '2px'
153
- : '2px'} calc(100% - 2px), {isFirst ? '2px' : `${chevronWidth + 2}px`} 50%);"
154
- >
155
- <div class={getStageStyles(stage).content()}>
156
- {#if children}
157
- {@render children(stage, index)}
158
- {:else}
159
- <span class={getStageStyles(stage).label()}>
160
- {stage.label}
161
- </span>
162
- {#if stage.count !== undefined}
163
- <span class={getStageStyles(stage).count()}>
164
- {stage.count}
165
- </span>
166
- {/if}
167
- {/if}
104
+ {#if renderAsButton}
105
+ <button
106
+ type="button"
107
+ class={s.innerLayer()}
108
+ style="clip-path: {innerClipPath(isFirst)};"
109
+ disabled={stage.disabled}
110
+ aria-disabled={stage.disabled ? 'true' : undefined}
111
+ onclick={(e) => handleStageClick(stage, index, e)}
112
+ >
113
+ {@render stageContent(stage, index, s)}
114
+ </button>
115
+ {:else}
116
+ <div class={s.innerLayer()} style="clip-path: {innerClipPath(isFirst)};">
117
+ {@render stageContent(stage, index, s)}
168
118
  </div>
169
- </div>
119
+ {/if}
170
120
 
171
- <!-- Beneath Children - Rendered directly below this stage -->
172
121
  {#if beneathChildren}
173
122
  <div class="mt-4">
174
123
  {@render beneathChildren(stage, index)}
@@ -1,18 +1,4 @@
1
- import type { Snippet } from 'svelte';
2
- export type PipelineStage = {
3
- label: string;
4
- count?: number | string;
5
- color?: 'default' | 'primary' | 'success' | 'warning' | 'danger' | 'info';
6
- active?: boolean;
7
- };
8
- interface Props {
9
- stages: PipelineStage[];
10
- class?: string;
11
- size?: 'sm' | 'base' | 'lg';
12
- equalWidth?: boolean;
13
- children?: Snippet<[PipelineStage, number]>;
14
- beneathChildren?: Snippet<[PipelineStage, number]>;
15
- }
16
- declare const Pipeline: import("svelte").Component<Props, {}, "">;
1
+ import type { PipelineProps } from '../index.js';
2
+ declare const Pipeline: import("svelte").Component<PipelineProps, {}, "">;
17
3
  type Pipeline = ReturnType<typeof Pipeline>;
18
4
  export default Pipeline;
@@ -0,0 +1,31 @@
1
+ import type { ClassValue } from 'tailwind-variants';
2
+ import type { Snippet } from 'svelte';
3
+ export type PipelineStageColor = 'default' | 'primary' | 'success' | 'warning' | 'danger' | 'info';
4
+ export type PipelineStage = {
5
+ label: string;
6
+ count?: number | string;
7
+ color?: PipelineStageColor;
8
+ active?: boolean;
9
+ disabled?: boolean;
10
+ };
11
+ export type PipelineStageEvent<E extends Event = Event> = {
12
+ stage: PipelineStage;
13
+ index: number;
14
+ event: E;
15
+ };
16
+ export type PipelineStageClickEvent = PipelineStageEvent<MouseEvent>;
17
+ export type PipelineStagePointerEvent = PipelineStageEvent<MouseEvent>;
18
+ export type PipelineProps = {
19
+ stages: PipelineStage[];
20
+ class?: ClassValue;
21
+ size?: 'sm' | 'base' | 'lg';
22
+ equalWidth?: boolean;
23
+ children?: Snippet<[PipelineStage, number]>;
24
+ beneathChildren?: Snippet<[PipelineStage, number]>;
25
+ selectedClass?: ClassValue;
26
+ unselectedClass?: ClassValue;
27
+ disabledClass?: ClassValue;
28
+ onstageclick?: (e: PipelineStageClickEvent) => void;
29
+ onstagehover?: (e: PipelineStagePointerEvent) => void;
30
+ onstageleave?: (e: PipelineStagePointerEvent) => void;
31
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,256 @@
1
+ export declare const pipelineVariants: import("tailwind-variants").TVReturnType<{
2
+ size: {
3
+ sm: {
4
+ stage: string;
5
+ borderLayer: string;
6
+ innerLayer: string;
7
+ label: string;
8
+ count: string;
9
+ content: string;
10
+ };
11
+ base: {
12
+ stage: string;
13
+ borderLayer: string;
14
+ innerLayer: string;
15
+ label: string;
16
+ count: string;
17
+ content: string;
18
+ };
19
+ lg: {
20
+ stage: string;
21
+ borderLayer: string;
22
+ innerLayer: string;
23
+ label: string;
24
+ count: string;
25
+ content: string;
26
+ };
27
+ };
28
+ color: {
29
+ default: {
30
+ borderLayer: string;
31
+ };
32
+ primary: {
33
+ borderLayer: string;
34
+ label: string;
35
+ count: string;
36
+ };
37
+ success: {
38
+ borderLayer: string;
39
+ label: string;
40
+ count: string;
41
+ };
42
+ warning: {
43
+ borderLayer: string;
44
+ label: string;
45
+ count: string;
46
+ };
47
+ danger: {
48
+ borderLayer: string;
49
+ label: string;
50
+ count: string;
51
+ };
52
+ info: {
53
+ borderLayer: string;
54
+ label: string;
55
+ count: string;
56
+ };
57
+ };
58
+ active: {
59
+ true: {
60
+ borderLayer: string;
61
+ };
62
+ };
63
+ equalWidth: {
64
+ true: {
65
+ stage: string;
66
+ };
67
+ };
68
+ interactive: {
69
+ true: {
70
+ innerLayer: string;
71
+ };
72
+ };
73
+ disabled: {
74
+ true: {
75
+ stage: string;
76
+ };
77
+ };
78
+ }, {
79
+ container: string;
80
+ stage: string;
81
+ borderLayer: string;
82
+ innerLayer: string;
83
+ content: string;
84
+ label: string;
85
+ count: string;
86
+ }, undefined, {
87
+ size: {
88
+ sm: {
89
+ stage: string;
90
+ borderLayer: string;
91
+ innerLayer: string;
92
+ label: string;
93
+ count: string;
94
+ content: string;
95
+ };
96
+ base: {
97
+ stage: string;
98
+ borderLayer: string;
99
+ innerLayer: string;
100
+ label: string;
101
+ count: string;
102
+ content: string;
103
+ };
104
+ lg: {
105
+ stage: string;
106
+ borderLayer: string;
107
+ innerLayer: string;
108
+ label: string;
109
+ count: string;
110
+ content: string;
111
+ };
112
+ };
113
+ color: {
114
+ default: {
115
+ borderLayer: string;
116
+ };
117
+ primary: {
118
+ borderLayer: string;
119
+ label: string;
120
+ count: string;
121
+ };
122
+ success: {
123
+ borderLayer: string;
124
+ label: string;
125
+ count: string;
126
+ };
127
+ warning: {
128
+ borderLayer: string;
129
+ label: string;
130
+ count: string;
131
+ };
132
+ danger: {
133
+ borderLayer: string;
134
+ label: string;
135
+ count: string;
136
+ };
137
+ info: {
138
+ borderLayer: string;
139
+ label: string;
140
+ count: string;
141
+ };
142
+ };
143
+ active: {
144
+ true: {
145
+ borderLayer: string;
146
+ };
147
+ };
148
+ equalWidth: {
149
+ true: {
150
+ stage: string;
151
+ };
152
+ };
153
+ interactive: {
154
+ true: {
155
+ innerLayer: string;
156
+ };
157
+ };
158
+ disabled: {
159
+ true: {
160
+ stage: string;
161
+ };
162
+ };
163
+ }, {
164
+ container: string;
165
+ stage: string;
166
+ borderLayer: string;
167
+ innerLayer: string;
168
+ content: string;
169
+ label: string;
170
+ count: string;
171
+ }, import("tailwind-variants").TVReturnType<{
172
+ size: {
173
+ sm: {
174
+ stage: string;
175
+ borderLayer: string;
176
+ innerLayer: string;
177
+ label: string;
178
+ count: string;
179
+ content: string;
180
+ };
181
+ base: {
182
+ stage: string;
183
+ borderLayer: string;
184
+ innerLayer: string;
185
+ label: string;
186
+ count: string;
187
+ content: string;
188
+ };
189
+ lg: {
190
+ stage: string;
191
+ borderLayer: string;
192
+ innerLayer: string;
193
+ label: string;
194
+ count: string;
195
+ content: string;
196
+ };
197
+ };
198
+ color: {
199
+ default: {
200
+ borderLayer: string;
201
+ };
202
+ primary: {
203
+ borderLayer: string;
204
+ label: string;
205
+ count: string;
206
+ };
207
+ success: {
208
+ borderLayer: string;
209
+ label: string;
210
+ count: string;
211
+ };
212
+ warning: {
213
+ borderLayer: string;
214
+ label: string;
215
+ count: string;
216
+ };
217
+ danger: {
218
+ borderLayer: string;
219
+ label: string;
220
+ count: string;
221
+ };
222
+ info: {
223
+ borderLayer: string;
224
+ label: string;
225
+ count: string;
226
+ };
227
+ };
228
+ active: {
229
+ true: {
230
+ borderLayer: string;
231
+ };
232
+ };
233
+ equalWidth: {
234
+ true: {
235
+ stage: string;
236
+ };
237
+ };
238
+ interactive: {
239
+ true: {
240
+ innerLayer: string;
241
+ };
242
+ };
243
+ disabled: {
244
+ true: {
245
+ stage: string;
246
+ };
247
+ };
248
+ }, {
249
+ container: string;
250
+ stage: string;
251
+ borderLayer: string;
252
+ innerLayer: string;
253
+ content: string;
254
+ label: string;
255
+ count: string;
256
+ }, undefined, unknown, unknown, undefined>>;
@@ -0,0 +1,91 @@
1
+ import { tv } from 'tailwind-variants';
2
+ export const pipelineVariants = tv({
3
+ slots: {
4
+ container: 'flex items-center gap-0 w-full',
5
+ stage: 'relative transition-all duration-300',
6
+ borderLayer: 'absolute inset-0 transition-all duration-300',
7
+ innerLayer: 'relative flex items-center justify-center bg-white transition-all duration-300 focus:outline-none',
8
+ content: 'text-center flex flex-col items-center justify-center',
9
+ label: 'font-medium text-default-600 leading-tight break-words',
10
+ count: 'font-bold text-default-900'
11
+ },
12
+ variants: {
13
+ size: {
14
+ sm: {
15
+ stage: 'min-w-[130px]',
16
+ borderLayer: 'h-20',
17
+ innerLayer: 'h-20',
18
+ label: 'text-[11px] max-w-[100px]',
19
+ count: 'text-xl mt-0.5',
20
+ content: 'px-4'
21
+ },
22
+ base: {
23
+ stage: 'min-w-[150px]',
24
+ borderLayer: 'h-24',
25
+ innerLayer: 'h-24',
26
+ label: 'text-xs max-w-[120px]',
27
+ count: 'text-2xl mt-1',
28
+ content: 'px-5'
29
+ },
30
+ lg: {
31
+ stage: 'min-w-[180px]',
32
+ borderLayer: 'h-28',
33
+ innerLayer: 'h-28',
34
+ label: 'text-sm max-w-[150px]',
35
+ count: 'text-3xl mt-1',
36
+ content: 'px-6'
37
+ }
38
+ },
39
+ color: {
40
+ default: { borderLayer: 'bg-default-200' },
41
+ primary: {
42
+ borderLayer: 'bg-blue-500',
43
+ label: 'text-default-700',
44
+ count: 'text-default-900'
45
+ },
46
+ success: {
47
+ borderLayer: 'bg-green-500',
48
+ label: 'text-default-700',
49
+ count: 'text-default-900'
50
+ },
51
+ warning: {
52
+ borderLayer: 'bg-amber-500',
53
+ label: 'text-default-700',
54
+ count: 'text-default-900'
55
+ },
56
+ danger: {
57
+ borderLayer: 'bg-rose-500',
58
+ label: 'text-default-700',
59
+ count: 'text-default-900'
60
+ },
61
+ info: {
62
+ borderLayer: 'bg-purple-500',
63
+ label: 'text-default-700',
64
+ count: 'text-default-900'
65
+ }
66
+ },
67
+ active: {
68
+ true: {
69
+ borderLayer: 'ring-2 ring-blue-500 ring-offset-2'
70
+ }
71
+ },
72
+ equalWidth: {
73
+ true: { stage: 'flex-1' }
74
+ },
75
+ interactive: {
76
+ true: {
77
+ innerLayer: 'cursor-pointer focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2'
78
+ }
79
+ },
80
+ disabled: {
81
+ true: {
82
+ stage: 'opacity-50 cursor-not-allowed'
83
+ }
84
+ }
85
+ },
86
+ defaultVariants: {
87
+ size: 'base',
88
+ color: 'default',
89
+ equalWidth: true
90
+ }
91
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@makolabs/ripple",
3
- "version": "1.10.0",
3
+ "version": "1.11.0",
4
4
  "description": "Simple Svelte 5 powered component library ✨",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "repository": {