@finsweet/webflow-apps-utils 1.0.30 → 1.0.31

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.
@@ -11,6 +11,7 @@ export * from './input';
11
11
  export * from './layout';
12
12
  export * from './modal';
13
13
  export * from './notification';
14
+ export * from './progress-bar';
14
15
  export * from './regions';
15
16
  export * from './section';
16
17
  export * from './select';
@@ -11,6 +11,7 @@ export * from './input';
11
11
  export * from './layout';
12
12
  export * from './modal';
13
13
  export * from './notification';
14
+ export * from './progress-bar';
14
15
  export * from './regions';
15
16
  export * from './section';
16
17
  export * from './select';
@@ -0,0 +1,185 @@
1
+ <script module>
2
+ import { defineMeta } from '@storybook/addon-svelte-csf';
3
+ import { fn } from 'storybook/test';
4
+
5
+ import ProgressBar from './ProgressBar.svelte';
6
+
7
+ // More on how to set up stories at: https://storybook.js.org/docs/writing-stories
8
+ const { Story } = defineMeta({
9
+ title: 'UI/ProgressBar',
10
+ component: ProgressBar,
11
+ tags: ['autodocs'],
12
+ argTypes: {
13
+ value: {
14
+ control: { type: 'range', min: 0, max: 100, step: 1 }
15
+ },
16
+ max: {
17
+ control: { type: 'number' }
18
+ },
19
+ variant: {
20
+ control: { type: 'select' },
21
+ options: ['default', 'success', 'warning', 'error']
22
+ },
23
+ easing: {
24
+ control: { type: 'select' },
25
+ options: ['linear', 'cubicIn', 'cubicOut', 'cubicInOut', 'quartOut']
26
+ },
27
+ animated: {
28
+ control: { type: 'boolean' }
29
+ },
30
+ duration: {
31
+ control: { type: 'range', min: 100, max: 2000, step: 100 }
32
+ },
33
+ showPercentage: {
34
+ control: { type: 'boolean' }
35
+ },
36
+ showStatus: {
37
+ control: { type: 'boolean' }
38
+ },
39
+ showSpinner: {
40
+ control: { type: 'boolean' }
41
+ },
42
+ completed: {
43
+ control: { type: 'boolean' }
44
+ },
45
+ height: {
46
+ control: { type: 'range', min: 2, max: 20, step: 1 }
47
+ },
48
+ statusText: {
49
+ control: { type: 'text' }
50
+ },
51
+ onComplete: { action: 'completed' }
52
+ },
53
+ args: {
54
+ onComplete: fn()
55
+ }
56
+ });
57
+ </script>
58
+
59
+ <Story
60
+ name="Default"
61
+ args={{
62
+ value: 45,
63
+ showPercentage: true
64
+ }}
65
+ />
66
+
67
+ <Story
68
+ name="With Status"
69
+ args={{
70
+ value: 60,
71
+ showStatus: true,
72
+ showPercentage: true,
73
+ statusText: 'Processing files...'
74
+ }}
75
+ />
76
+
77
+ <Story
78
+ name="With Spinner"
79
+ args={{
80
+ value: 35,
81
+ showStatus: true,
82
+ showPercentage: true,
83
+ showSpinner: true,
84
+ statusText: 'Uploading data...'
85
+ }}
86
+ />
87
+
88
+ <Story
89
+ name="Completed State"
90
+ args={{
91
+ value: 100,
92
+ completed: true,
93
+ showStatus: true,
94
+ showPercentage: true,
95
+ statusText: 'Processing completed'
96
+ }}
97
+ />
98
+
99
+ <Story
100
+ name="Success Variant"
101
+ args={{
102
+ value: 80,
103
+ variant: 'success',
104
+ showStatus: true,
105
+ showPercentage: true,
106
+ statusText: 'Successfully processing...'
107
+ }}
108
+ />
109
+
110
+ <Story
111
+ name="Warning Variant"
112
+ args={{
113
+ value: 65,
114
+ variant: 'warning',
115
+ showStatus: true,
116
+ showPercentage: true,
117
+ statusText: 'Warning: Slow connection detected'
118
+ }}
119
+ />
120
+
121
+ <Story
122
+ name="Error Variant"
123
+ args={{
124
+ value: 25,
125
+ variant: 'error',
126
+ showStatus: true,
127
+ showPercentage: true,
128
+ statusText: 'Error: Connection failed'
129
+ }}
130
+ />
131
+
132
+ <Story
133
+ name="No Animation"
134
+ args={{
135
+ value: 70,
136
+ animated: false,
137
+ showPercentage: true,
138
+ showStatus: true,
139
+ statusText: 'Instant progress'
140
+ }}
141
+ />
142
+
143
+ <Story
144
+ name="Slow Animation"
145
+ args={{
146
+ value: 85,
147
+ duration: 2000,
148
+ easing: 'quartOut',
149
+ showPercentage: true,
150
+ showStatus: true,
151
+ statusText: 'Slow, smooth progress...'
152
+ }}
153
+ />
154
+
155
+ <Story
156
+ name="Custom Height"
157
+ args={{
158
+ value: 55,
159
+ height: 8,
160
+ showPercentage: true,
161
+ showStatus: true,
162
+ statusText: 'Thick progress bar'
163
+ }}
164
+ />
165
+
166
+ <Story
167
+ name="Minimal"
168
+ args={{
169
+ value: 40,
170
+ showPercentage: false,
171
+ showStatus: false
172
+ }}
173
+ />
174
+
175
+ <Story
176
+ name="Scan Progress Simulation"
177
+ args={{
178
+ value: 73,
179
+ showStatus: true,
180
+ showPercentage: true,
181
+ showSpinner: true,
182
+ statusText: 'Scanning: 73/100 pages',
183
+ height: 4
184
+ }}
185
+ />
@@ -0,0 +1,27 @@
1
+ export default ProgressBar;
2
+ type ProgressBar = SvelteComponent<{
3
+ [x: string]: never;
4
+ }, {
5
+ [evt: string]: CustomEvent<any>;
6
+ }, {}> & {
7
+ $$bindings?: string | undefined;
8
+ };
9
+ declare const ProgressBar: $$__sveltets_2_IsomorphicComponent<{
10
+ [x: string]: never;
11
+ }, {
12
+ [evt: string]: CustomEvent<any>;
13
+ }, {}, {}, string>;
14
+ import ProgressBar from './ProgressBar.svelte';
15
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
16
+ new (options: import("svelte").ComponentConstructorOptions<Props>): import("svelte").SvelteComponent<Props, Events, Slots> & {
17
+ $$bindings?: Bindings;
18
+ } & Exports;
19
+ (internal: unknown, props: {
20
+ $$events?: Events;
21
+ $$slots?: Slots;
22
+ }): Exports & {
23
+ $set?: any;
24
+ $on?: any;
25
+ };
26
+ z_$$bindings?: Bindings;
27
+ }
@@ -0,0 +1,290 @@
1
+ <script lang="ts">
2
+ import { cubicIn, cubicInOut, cubicOut, linear, quartOut } from 'svelte/easing';
3
+ import { tweened } from 'svelte/motion';
4
+
5
+ import { CheckCircleOutlinedIcon, WarningTriangleOutlineIcon } from '../../icons';
6
+ import Loader from '../Loader.svelte';
7
+ import { Text } from '../text';
8
+ import type { ProgressBarEasing, ProgressBarProps, ProgressBarVariant } from './types.js';
9
+
10
+ let {
11
+ value = 0,
12
+ max = 100,
13
+ animated = true,
14
+ duration = 400,
15
+ easing = 'cubicOut',
16
+ showPercentage = true,
17
+ showStatus = false,
18
+ statusText = '',
19
+ showSpinner = false,
20
+ variant = 'default',
21
+ height = 4,
22
+ completed = false,
23
+ class: className = '',
24
+ onComplete,
25
+ ...restProps
26
+ }: ProgressBarProps = $props();
27
+
28
+ const easingFunctions = {
29
+ linear,
30
+ cubicIn,
31
+ cubicOut,
32
+ cubicInOut,
33
+ quartOut
34
+ };
35
+
36
+ let currentProgress = $state(0);
37
+ let isCompleted = $state(false);
38
+ let completedValue = $state<number | null>(null);
39
+
40
+ let percentage = $derived(
41
+ Math.min(100, Math.round((Math.max(0, value) / Math.max(1, max)) * 100))
42
+ );
43
+
44
+ let tweenedProgress = $state(0);
45
+ const progress = tweened(0, {
46
+ duration,
47
+ easing: easingFunctions[easing]
48
+ });
49
+
50
+ $effect(() => {
51
+ const unsubscribe = progress.subscribe((val) => {
52
+ tweenedProgress = val;
53
+ });
54
+
55
+ return unsubscribe;
56
+ });
57
+
58
+ let displayWidth = $derived(
59
+ isCompleted && completedValue !== null ? completedValue : tweenedProgress
60
+ );
61
+
62
+ let displayPercentage = $derived(
63
+ isCompleted && completedValue !== null
64
+ ? Math.round(completedValue)
65
+ : Math.round(tweenedProgress)
66
+ );
67
+
68
+ $effect(() => {
69
+ const currentPercentage = percentage;
70
+ if (!completed) {
71
+ currentProgress = currentPercentage;
72
+ if (animated) {
73
+ progress.set(currentPercentage, {
74
+ duration,
75
+ easing: easingFunctions[easing]
76
+ });
77
+ } else {
78
+ progress.set(currentPercentage, { duration: 0 });
79
+ }
80
+ }
81
+ });
82
+
83
+ $effect(() => {
84
+ if (completed && !isCompleted) {
85
+ completedValue = tweenedProgress;
86
+ isCompleted = true;
87
+
88
+ onComplete?.();
89
+ }
90
+
91
+ if (!completed && isCompleted) {
92
+ isCompleted = false;
93
+ completedValue = null;
94
+ }
95
+ });
96
+
97
+ let containerClasses = $derived(() => {
98
+ const classes = ['progress-container'];
99
+ if (className) classes.push(className);
100
+ return classes.join(' ');
101
+ });
102
+
103
+ let progressClasses = $derived(() => {
104
+ const classes = ['progress-fill'];
105
+ if (isCompleted) classes.push('completed');
106
+ if (variant !== 'default') classes.push(`progress-fill--${variant}`);
107
+ return classes.join(' ');
108
+ });
109
+
110
+ let displayStatusText = $derived(() => {
111
+ if (isCompleted) {
112
+ return 'Completed successfully';
113
+ }
114
+ return statusText || `${displayPercentage}%`;
115
+ });
116
+
117
+ let StatusIcon = $derived(() => {
118
+ if (isCompleted) {
119
+ return CheckCircleOutlinedIcon;
120
+ }
121
+ return WarningTriangleOutlineIcon;
122
+ });
123
+
124
+ let statusColor = $derived(() => {
125
+ if (isCompleted) return 'var(--greenText)';
126
+
127
+ switch (variant) {
128
+ case 'success':
129
+ return 'var(--greenText)';
130
+ case 'warning':
131
+ return 'var(--yellowText)';
132
+ case 'error':
133
+ return 'var(--redText)';
134
+ default:
135
+ return 'var(--text1)';
136
+ }
137
+ });
138
+
139
+ let progressFillColor = $derived(() => {
140
+ if (isCompleted) return 'var(--greenText)';
141
+
142
+ switch (variant) {
143
+ case 'success':
144
+ return 'var(--greenText)';
145
+ case 'warning':
146
+ return 'var(--yellowText)';
147
+ case 'error':
148
+ return 'var(--redText)';
149
+ default:
150
+ return 'var(--actionPrimaryBackground)';
151
+ }
152
+ });
153
+ </script>
154
+
155
+ <div
156
+ class={containerClasses()}
157
+ role="progressbar"
158
+ aria-valuenow={displayPercentage}
159
+ aria-valuemin="0"
160
+ aria-valuemax="100"
161
+ aria-label={displayStatusText()}
162
+ aria-live="polite"
163
+ {...restProps}
164
+ >
165
+ {#if showStatus || showPercentage || showSpinner}
166
+ <div class="progress-header">
167
+ <div class="status-info">
168
+ {#if showSpinner && !isCompleted}
169
+ <div class="progress-spinner">
170
+ <Loader size={12} margin="0" />
171
+ </div>
172
+ {/if}
173
+
174
+ <div class="status-icon" class:success={isCompleted}>
175
+ <StatusIcon />
176
+ </div>
177
+
178
+ {#if showStatus}
179
+ <Text
180
+ label={displayStatusText()}
181
+ fontColor={statusColor()}
182
+ fontSize="normal"
183
+ class="progress-status"
184
+ />
185
+ {/if}
186
+ </div>
187
+
188
+ <div class="progress-details">
189
+ {#if showPercentage}
190
+ <Text
191
+ label="{displayPercentage}%"
192
+ fontColor={statusColor()}
193
+ fontSize="normal"
194
+ class="progress-percentage"
195
+ />
196
+ {/if}
197
+ </div>
198
+ </div>
199
+ {/if}
200
+
201
+ <div class="progress-track" style="height: {height}px;">
202
+ <div
203
+ class={progressClasses()}
204
+ style="width: {displayWidth}%; height: {height}px; background-color: {progressFillColor()};"
205
+ ></div>
206
+ </div>
207
+ </div>
208
+
209
+ <style>
210
+ .progress-container {
211
+ display: flex;
212
+ flex-direction: column;
213
+ gap: 8px;
214
+ width: 100%;
215
+ }
216
+
217
+ .progress-header {
218
+ display: flex;
219
+ justify-content: space-between;
220
+ align-items: center;
221
+ gap: 4px;
222
+ }
223
+
224
+ .status-info {
225
+ display: flex;
226
+ align-items: center;
227
+ gap: 4px;
228
+ flex: 1;
229
+ min-width: 0;
230
+ }
231
+
232
+ .status-icon :global(svg) {
233
+ width: 20px;
234
+ height: 20px;
235
+ color: var(--text1);
236
+ }
237
+
238
+ .status-icon.success :global(svg) {
239
+ color: var(--greenText);
240
+ }
241
+
242
+ .progress-details {
243
+ display: flex;
244
+ align-items: center;
245
+ gap: 8px;
246
+ flex-shrink: 0;
247
+ }
248
+
249
+ .progress-spinner {
250
+ display: flex;
251
+ align-items: center;
252
+ justify-content: center;
253
+ }
254
+
255
+ .progress-track {
256
+ width: 100%;
257
+ background-color: var(--black);
258
+ border-radius: 2px;
259
+ overflow: hidden;
260
+ position: relative;
261
+ }
262
+
263
+ .progress-fill {
264
+ height: 100%;
265
+ border-radius: 2px;
266
+ transition: width 0.4s cubic-bezier(0.215, 0.61, 0.355, 1);
267
+ min-width: 1px;
268
+ }
269
+
270
+ .progress-fill.completed {
271
+ background-color: var(--greenText) !important;
272
+ }
273
+
274
+ .progress-fill--success {
275
+ background-color: var(--greenText);
276
+ }
277
+
278
+ .progress-fill--warning {
279
+ background-color: var(--yellowText);
280
+ }
281
+
282
+ .progress-fill--error {
283
+ background-color: var(--redText);
284
+ }
285
+
286
+ /* Ensure smooth transitions */
287
+ .progress-fill {
288
+ will-change: width;
289
+ }
290
+ </style>
@@ -0,0 +1,4 @@
1
+ import type { ProgressBarProps } from './types.js';
2
+ declare const ProgressBar: import("svelte").Component<ProgressBarProps, {}, "">;
3
+ type ProgressBar = ReturnType<typeof ProgressBar>;
4
+ export default ProgressBar;
@@ -0,0 +1,2 @@
1
+ export { default as ProgressBar } from './ProgressBar.svelte';
2
+ export * from './types.js';
@@ -0,0 +1,2 @@
1
+ export { default as ProgressBar } from './ProgressBar.svelte';
2
+ export * from './types.js';
@@ -0,0 +1,63 @@
1
+ export type ProgressBarVariant = 'default' | 'success' | 'warning' | 'error';
2
+ export type ProgressBarEasing = 'linear' | 'cubicIn' | 'cubicOut' | 'cubicInOut' | 'quartOut';
3
+ export interface ProgressBarProps {
4
+ /**
5
+ * Current progress value (0-100)
6
+ */
7
+ value: number;
8
+ /**
9
+ * Maximum value (default: 100)
10
+ */
11
+ max?: number;
12
+ /**
13
+ * Enable smooth animations (default: true)
14
+ */
15
+ animated?: boolean;
16
+ /**
17
+ * Animation duration in milliseconds (default: 400)
18
+ */
19
+ duration?: number;
20
+ /**
21
+ * Easing function (default: 'cubicOut')
22
+ */
23
+ easing?: ProgressBarEasing;
24
+ /**
25
+ * Show percentage text (default: true)
26
+ */
27
+ showPercentage?: boolean;
28
+ /**
29
+ * Show status text (default: false)
30
+ */
31
+ showStatus?: boolean;
32
+ /**
33
+ * Custom status text
34
+ */
35
+ statusText?: string;
36
+ /**
37
+ * Show loading spinner during progress (default: false)
38
+ */
39
+ showSpinner?: boolean;
40
+ /**
41
+ * Color scheme
42
+ */
43
+ variant?: ProgressBarVariant;
44
+ /**
45
+ * Height in pixels (default: 4)
46
+ */
47
+ height?: number;
48
+ /**
49
+ * Completed state - locks progress at final value
50
+ */
51
+ completed?: boolean;
52
+ /**
53
+ * Custom CSS class
54
+ */
55
+ class?: string;
56
+ /**
57
+ * Callback when progress completes
58
+ */
59
+ onComplete?: () => void;
60
+ }
61
+ export interface ProgressBarEvents {
62
+ complete: CustomEvent<void>;
63
+ }
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@finsweet/webflow-apps-utils",
3
- "version": "1.0.30",
3
+ "version": "1.0.31",
4
4
  "description": "Shared utilities for Webflow apps",
5
5
  "homepage": "https://github.com/finsweet/webflow-apps-utils",
6
6
  "repository": {