@mrintel/villain-ui 0.6.3 → 0.7.1
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/components/cards/Card.svelte +35 -41
- package/dist/components/cards/Card.svelte.d.ts +3 -0
- package/dist/components/forms/Input.svelte +3 -1
- package/dist/components/forms/Input.svelte.d.ts +1 -0
- package/dist/components/forms/Step.svelte +25 -0
- package/dist/components/forms/Step.svelte.d.ts +12 -0
- package/dist/components/forms/StepContext.d.ts +3 -0
- package/dist/components/forms/StepContext.js +5 -0
- package/dist/components/forms/Stepper.types.d.ts +37 -0
- package/dist/components/forms/Stepper.types.js +1 -0
- package/dist/components/forms/StepperForm.svelte +183 -0
- package/dist/components/forms/StepperForm.svelte.d.ts +17 -0
- package/dist/components/forms/index.d.ts +3 -0
- package/dist/components/forms/index.js +2 -0
- package/dist/components/navigation/Stepper.svelte +204 -0
- package/dist/components/navigation/Stepper.svelte.d.ts +34 -0
- package/dist/components/navigation/index.d.ts +1 -0
- package/dist/components/navigation/index.js +1 -0
- package/dist/index.d.ts +10 -3
- package/dist/index.js +3 -3
- package/dist/theme.css +68 -0
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<script lang="ts">let { padding = 'md', href, target = '_self', rel, class: className = '', children, header, footer, iconBefore, ...restProps } = $props();
|
|
1
|
+
<script lang="ts">let { padding = 'md', href, target = '_self', rel, onclick, selected = false, disabled = false, class: className = '', children, header, footer, iconBefore, ...restProps } = $props();
|
|
2
2
|
// Compute rel attribute: use explicit rel if provided, otherwise default to 'noopener noreferrer' for target='_blank'
|
|
3
3
|
const relValue = $derived(rel ?? (target === '_blank' ? 'noopener noreferrer' : undefined));
|
|
4
4
|
const paddingClasses = {
|
|
@@ -7,54 +7,48 @@ const paddingClasses = {
|
|
|
7
7
|
md: 'p-6',
|
|
8
8
|
lg: 'p-8'
|
|
9
9
|
};
|
|
10
|
-
const
|
|
10
|
+
const isInteractive = $derived(!!href || !!onclick);
|
|
11
|
+
const baseClasses = $derived(`panel-raised rounded-[var(--radius-lg)] transition-all duration-300 ease-[var(--ease-luxe)]` +
|
|
12
|
+
`${href ? ' no-underline' : ''}` +
|
|
13
|
+
`${isInteractive ? ' cursor-pointer hover-lift' : ''}` +
|
|
14
|
+
`${selected ? ' ring-2 ring-accent ring-offset-2 ring-offset-base-0' : ''}` +
|
|
15
|
+
`${disabled ? ' opacity-50 pointer-events-none' : ''}`);
|
|
11
16
|
export {};
|
|
12
17
|
</script>
|
|
13
18
|
|
|
14
|
-
{#
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
19
|
+
{#snippet cardContent()}
|
|
20
|
+
{#if iconBefore}
|
|
21
|
+
<div class="card-icon">
|
|
22
|
+
{@render iconBefore()}
|
|
23
|
+
</div>
|
|
24
|
+
{/if}
|
|
25
|
+
{#if header}
|
|
26
|
+
<div class="pb-3 mb-3 border-b border-border-strong">
|
|
27
|
+
{@render header()}
|
|
28
|
+
</div>
|
|
29
|
+
{/if}
|
|
30
|
+
|
|
31
|
+
<div>
|
|
32
|
+
{@render children?.()}
|
|
33
|
+
</div>
|
|
26
34
|
|
|
27
|
-
|
|
28
|
-
|
|
35
|
+
{#if footer}
|
|
36
|
+
<div class="pt-3 mt-3 border-t border-border-strong">
|
|
37
|
+
{@render footer()}
|
|
29
38
|
</div>
|
|
39
|
+
{/if}
|
|
40
|
+
{/snippet}
|
|
30
41
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
</div>
|
|
35
|
-
{/if}
|
|
42
|
+
{#if href}
|
|
43
|
+
<a {href} {target} rel={relValue} class="{baseClasses} {paddingClasses[padding]} {className}" aria-disabled={disabled || undefined} {...restProps}>
|
|
44
|
+
{@render cardContent()}
|
|
36
45
|
</a>
|
|
46
|
+
{:else if onclick}
|
|
47
|
+
<button type="button" {onclick} {disabled} aria-pressed={selected} class="{baseClasses} {paddingClasses[padding]} {className} text-left w-full" {...restProps}>
|
|
48
|
+
{@render cardContent()}
|
|
49
|
+
</button>
|
|
37
50
|
{:else}
|
|
38
51
|
<div class="{baseClasses} {paddingClasses[padding]} {className}" {...restProps}>
|
|
39
|
-
{
|
|
40
|
-
<div class="card-icon">
|
|
41
|
-
{@render iconBefore()}
|
|
42
|
-
</div>
|
|
43
|
-
{/if}
|
|
44
|
-
{#if header}
|
|
45
|
-
<div class="pb-3 mb-3 border-b border-border-strong">
|
|
46
|
-
{@render header()}
|
|
47
|
-
</div>
|
|
48
|
-
{/if}
|
|
49
|
-
|
|
50
|
-
<div>
|
|
51
|
-
{@render children?.()}
|
|
52
|
-
</div>
|
|
53
|
-
|
|
54
|
-
{#if footer}
|
|
55
|
-
<div class="pt-3 mt-3 border-t border-border-strong">
|
|
56
|
-
{@render footer()}
|
|
57
|
-
</div>
|
|
58
|
-
{/if}
|
|
52
|
+
{@render cardContent()}
|
|
59
53
|
</div>
|
|
60
54
|
{/if}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script lang="ts">import { createId } from '../../lib/internal/id.js';
|
|
2
2
|
import { baseInputClasses, focusClasses, disabledClasses, } from './formClasses';
|
|
3
|
-
let { type = 'text', value = $bindable(''), placeholder, disabled = false, error = false, label, id = createId('input'), oninput, iconBefore, iconAfter, validate, validationMessage, showValidation = true, class: className = '', } = $props();
|
|
3
|
+
let { type = 'text', name, value = $bindable(''), placeholder, disabled = false, error = false, label, id = createId('input'), oninput, iconBefore, iconAfter, validate, validationMessage, showValidation = true, class: className = '', } = $props();
|
|
4
4
|
// Built-in validation patterns
|
|
5
5
|
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
6
6
|
const urlPattern = /^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/;
|
|
@@ -91,6 +91,7 @@ function decrement() {
|
|
|
91
91
|
<!-- INPUT FIELD -->
|
|
92
92
|
<input
|
|
93
93
|
{type}
|
|
94
|
+
{name}
|
|
94
95
|
{id}
|
|
95
96
|
{placeholder}
|
|
96
97
|
{disabled}
|
|
@@ -153,6 +154,7 @@ function decrement() {
|
|
|
153
154
|
|
|
154
155
|
<input
|
|
155
156
|
{type}
|
|
157
|
+
{name}
|
|
156
158
|
{id}
|
|
157
159
|
{placeholder}
|
|
158
160
|
{disabled}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<script lang="ts">import { onMount, onDestroy } from 'svelte';
|
|
2
|
+
import { getStepperContext } from './StepContext';
|
|
3
|
+
let { id, label = id, fields = [], optional = false, order, children, } = $props();
|
|
4
|
+
const context = getStepperContext();
|
|
5
|
+
onMount(() => {
|
|
6
|
+
context?.registerStep({ id, label, fields, optional, order });
|
|
7
|
+
});
|
|
8
|
+
onDestroy(() => {
|
|
9
|
+
context?.unregisterStep(id);
|
|
10
|
+
});
|
|
11
|
+
const isActive = $derived(context?.currentStepId === id);
|
|
12
|
+
const state = $derived(context?.getStepState(id) ?? 'idle');
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
{#if isActive && children}
|
|
16
|
+
<div
|
|
17
|
+
class="step-content"
|
|
18
|
+
data-step-id={id}
|
|
19
|
+
data-step-state={state}
|
|
20
|
+
role="tabpanel"
|
|
21
|
+
aria-labelledby={`step-${id}`}
|
|
22
|
+
>
|
|
23
|
+
{@render children()}
|
|
24
|
+
</div>
|
|
25
|
+
{/if}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
interface Props {
|
|
3
|
+
id: string;
|
|
4
|
+
label?: string;
|
|
5
|
+
fields?: string[];
|
|
6
|
+
optional?: boolean;
|
|
7
|
+
order?: number;
|
|
8
|
+
children?: Snippet;
|
|
9
|
+
}
|
|
10
|
+
declare const Step: import("svelte").Component<Props, {}, "">;
|
|
11
|
+
type Step = ReturnType<typeof Step>;
|
|
12
|
+
export default Step;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export type StepState = 'idle' | 'active' | 'completed' | 'error';
|
|
2
|
+
export type ValidationMode = 'strict' | 'loose' | 'submit-only';
|
|
3
|
+
export interface StepRegistration {
|
|
4
|
+
id: string;
|
|
5
|
+
label: string;
|
|
6
|
+
fields?: string[];
|
|
7
|
+
optional?: boolean;
|
|
8
|
+
order?: number;
|
|
9
|
+
}
|
|
10
|
+
export interface StepContext {
|
|
11
|
+
registerStep: (step: StepRegistration) => void;
|
|
12
|
+
unregisterStep: (id: string) => void;
|
|
13
|
+
currentStepId: string;
|
|
14
|
+
getStepState: (id: string) => StepState;
|
|
15
|
+
}
|
|
16
|
+
export interface StepMeta {
|
|
17
|
+
id: string;
|
|
18
|
+
label: string;
|
|
19
|
+
fields: string[];
|
|
20
|
+
optional: boolean;
|
|
21
|
+
state: StepState;
|
|
22
|
+
}
|
|
23
|
+
export interface StepperFormContext {
|
|
24
|
+
next: () => Promise<boolean>;
|
|
25
|
+
back: () => void;
|
|
26
|
+
goto: (stepId: string) => Promise<boolean>;
|
|
27
|
+
currentStep: number;
|
|
28
|
+
currentStepId: string;
|
|
29
|
+
totalSteps: number;
|
|
30
|
+
canNext: boolean;
|
|
31
|
+
canBack: boolean;
|
|
32
|
+
isFirstStep: boolean;
|
|
33
|
+
isLastStep: boolean;
|
|
34
|
+
steps: StepMeta[];
|
|
35
|
+
validateCurrentStep: () => Promise<boolean>;
|
|
36
|
+
getStepErrors: (stepId: string) => string[];
|
|
37
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
<script lang="ts">import { setContext, tick } from 'svelte';
|
|
2
|
+
import { STEPPER_CONTEXT_KEY } from './StepContext';
|
|
3
|
+
let { form, validationMode = 'strict', initialStep = 0, onStepChange, onValidationError, onComplete, header, footer, children, class: className = '', } = $props();
|
|
4
|
+
let registeredSteps = $state([]);
|
|
5
|
+
let currentStepIndex = $state(initialStep);
|
|
6
|
+
let stepStates = $state(new Map());
|
|
7
|
+
let stepErrors = $state(new Map());
|
|
8
|
+
const totalSteps = $derived(registeredSteps.length);
|
|
9
|
+
const currentStepId = $derived(registeredSteps[currentStepIndex]?.id ?? '');
|
|
10
|
+
const isFirstStep = $derived(currentStepIndex === 0);
|
|
11
|
+
const isLastStep = $derived(currentStepIndex === totalSteps - 1);
|
|
12
|
+
const canBack = $derived(!isFirstStep);
|
|
13
|
+
const canNext = $derived(validationMode === 'submit-only'
|
|
14
|
+
? true
|
|
15
|
+
: (() => {
|
|
16
|
+
const step = registeredSteps[currentStepIndex];
|
|
17
|
+
if (!step)
|
|
18
|
+
return false;
|
|
19
|
+
return (stepErrors.get(step.id) ?? []).length === 0;
|
|
20
|
+
})());
|
|
21
|
+
const steps = $derived(registeredSteps.map((step, i) => ({
|
|
22
|
+
id: step.id,
|
|
23
|
+
label: step.label,
|
|
24
|
+
fields: step.fields ?? [],
|
|
25
|
+
optional: step.optional ?? false,
|
|
26
|
+
state: stepStates.get(step.id) ??
|
|
27
|
+
(i < currentStepIndex
|
|
28
|
+
? 'completed'
|
|
29
|
+
: i === currentStepIndex
|
|
30
|
+
? 'active'
|
|
31
|
+
: 'idle'),
|
|
32
|
+
})));
|
|
33
|
+
async function validateStepFields(stepId) {
|
|
34
|
+
const step = registeredSteps.find((s) => s.id === stepId);
|
|
35
|
+
if (!step?.fields?.length)
|
|
36
|
+
return true;
|
|
37
|
+
const formErrors = form?.errors;
|
|
38
|
+
if (!formErrors)
|
|
39
|
+
return true;
|
|
40
|
+
const errors = [];
|
|
41
|
+
for (const field of step.fields) {
|
|
42
|
+
const fieldErrors = $state.snapshot(formErrors)?.[field];
|
|
43
|
+
if (Array.isArray(fieldErrors))
|
|
44
|
+
errors.push(...fieldErrors);
|
|
45
|
+
}
|
|
46
|
+
if (errors.length) {
|
|
47
|
+
stepErrors.set(stepId, errors);
|
|
48
|
+
stepStates.set(stepId, 'error');
|
|
49
|
+
onValidationError?.(stepId, errors);
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
stepErrors.delete(stepId);
|
|
53
|
+
stepStates.set(stepId, 'active');
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
async function validateCurrentStep() {
|
|
57
|
+
if (validationMode === 'submit-only')
|
|
58
|
+
return true;
|
|
59
|
+
return validateStepFields(currentStepId);
|
|
60
|
+
}
|
|
61
|
+
async function next() {
|
|
62
|
+
if (isLastStep) {
|
|
63
|
+
onComplete?.();
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
if (validationMode !== 'submit-only') {
|
|
67
|
+
const valid = validationMode === 'strict'
|
|
68
|
+
? await validateCurrentStep()
|
|
69
|
+
: (await validateCurrentStep(), true);
|
|
70
|
+
if (!valid)
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
stepStates.set(currentStepId, 'completed');
|
|
74
|
+
currentStepIndex++;
|
|
75
|
+
const newStepId = registeredSteps[currentStepIndex].id;
|
|
76
|
+
stepStates.set(newStepId, 'active');
|
|
77
|
+
await tick();
|
|
78
|
+
onStepChange?.(currentStepIndex, newStepId);
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
function back() {
|
|
82
|
+
if (isFirstStep)
|
|
83
|
+
return;
|
|
84
|
+
stepStates.set(currentStepId, 'idle');
|
|
85
|
+
currentStepIndex--;
|
|
86
|
+
const newStepId = registeredSteps[currentStepIndex].id;
|
|
87
|
+
stepStates.set(newStepId, 'active');
|
|
88
|
+
onStepChange?.(currentStepIndex, newStepId);
|
|
89
|
+
}
|
|
90
|
+
async function goto(stepId) {
|
|
91
|
+
const targetIndex = registeredSteps.findIndex((s) => s.id === stepId);
|
|
92
|
+
if (targetIndex === -1)
|
|
93
|
+
return false;
|
|
94
|
+
if (targetIndex > currentStepIndex && validationMode === 'strict') {
|
|
95
|
+
for (let i = currentStepIndex; i < targetIndex; i++) {
|
|
96
|
+
const ok = await validateStepFields(registeredSteps[i].id);
|
|
97
|
+
if (!ok)
|
|
98
|
+
return false;
|
|
99
|
+
stepStates.set(registeredSteps[i].id, 'completed');
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
for (let i = 0; i < registeredSteps.length; i++) {
|
|
103
|
+
stepStates.set(registeredSteps[i].id, i < targetIndex
|
|
104
|
+
? 'completed'
|
|
105
|
+
: i === targetIndex
|
|
106
|
+
? 'active'
|
|
107
|
+
: 'idle');
|
|
108
|
+
}
|
|
109
|
+
currentStepIndex = targetIndex;
|
|
110
|
+
await tick();
|
|
111
|
+
onStepChange?.(targetIndex, stepId);
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
function getStepErrors(stepId) {
|
|
115
|
+
return stepErrors.get(stepId) ?? [];
|
|
116
|
+
}
|
|
117
|
+
function getStepState(id) {
|
|
118
|
+
return stepStates.get(id) ?? 'idle';
|
|
119
|
+
}
|
|
120
|
+
function registerStep(step) {
|
|
121
|
+
if (registeredSteps.some((s) => s.id === step.id))
|
|
122
|
+
return;
|
|
123
|
+
registeredSteps = [...registeredSteps, step].sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
124
|
+
if (!registeredSteps[currentStepIndex]) {
|
|
125
|
+
currentStepIndex = 0;
|
|
126
|
+
}
|
|
127
|
+
const id = registeredSteps[currentStepIndex].id;
|
|
128
|
+
stepStates.set(id, 'active');
|
|
129
|
+
}
|
|
130
|
+
function unregisterStep(id) {
|
|
131
|
+
registeredSteps = registeredSteps.filter((s) => s.id !== id);
|
|
132
|
+
stepStates.delete(id);
|
|
133
|
+
stepErrors.delete(id);
|
|
134
|
+
if (currentStepIndex >= registeredSteps.length) {
|
|
135
|
+
currentStepIndex = Math.max(registeredSteps.length - 1, 0);
|
|
136
|
+
}
|
|
137
|
+
const active = registeredSteps[currentStepIndex];
|
|
138
|
+
if (active)
|
|
139
|
+
stepStates.set(active.id, 'active');
|
|
140
|
+
}
|
|
141
|
+
const stepContext = {
|
|
142
|
+
registerStep,
|
|
143
|
+
unregisterStep,
|
|
144
|
+
get currentStepId() {
|
|
145
|
+
return currentStepId;
|
|
146
|
+
},
|
|
147
|
+
getStepState,
|
|
148
|
+
};
|
|
149
|
+
setContext(STEPPER_CONTEXT_KEY, stepContext);
|
|
150
|
+
const slotContext = $derived({
|
|
151
|
+
next,
|
|
152
|
+
back,
|
|
153
|
+
goto,
|
|
154
|
+
currentStep: currentStepIndex,
|
|
155
|
+
currentStepId,
|
|
156
|
+
totalSteps,
|
|
157
|
+
canNext,
|
|
158
|
+
canBack,
|
|
159
|
+
isFirstStep,
|
|
160
|
+
isLastStep,
|
|
161
|
+
steps,
|
|
162
|
+
validateCurrentStep,
|
|
163
|
+
getStepErrors,
|
|
164
|
+
});
|
|
165
|
+
</script>
|
|
166
|
+
|
|
167
|
+
<div class={`stepper-form ${className}`} data-current-step={currentStepId}>
|
|
168
|
+
{#if header}
|
|
169
|
+
<div class="stepper-form-header">
|
|
170
|
+
{@render header(slotContext)}
|
|
171
|
+
</div>
|
|
172
|
+
{/if}
|
|
173
|
+
|
|
174
|
+
<div class="stepper-form-content">
|
|
175
|
+
{@render children?.(slotContext)}
|
|
176
|
+
</div>
|
|
177
|
+
|
|
178
|
+
{#if footer}
|
|
179
|
+
<div class="stepper-form-footer">
|
|
180
|
+
{@render footer(slotContext)}
|
|
181
|
+
</div>
|
|
182
|
+
{/if}
|
|
183
|
+
</div>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { ValidationMode, StepperFormContext } from './Stepper.types';
|
|
3
|
+
interface Props {
|
|
4
|
+
form: any;
|
|
5
|
+
validationMode?: ValidationMode;
|
|
6
|
+
initialStep?: number;
|
|
7
|
+
onStepChange?: (stepIndex: number, stepId: string) => void;
|
|
8
|
+
onValidationError?: (stepId: string, errors: string[]) => void;
|
|
9
|
+
onComplete?: () => void;
|
|
10
|
+
header?: Snippet<[StepperFormContext]>;
|
|
11
|
+
footer?: Snippet<[StepperFormContext]>;
|
|
12
|
+
children?: Snippet<[StepperFormContext]>;
|
|
13
|
+
class?: string;
|
|
14
|
+
}
|
|
15
|
+
declare const StepperForm: import("svelte").Component<Props, {}, "">;
|
|
16
|
+
type StepperForm = ReturnType<typeof StepperForm>;
|
|
17
|
+
export default StepperForm;
|
|
@@ -10,3 +10,6 @@ export { default as InputGroup } from './InputGroup.svelte';
|
|
|
10
10
|
export { default as DatePicker } from './DatePicker.svelte';
|
|
11
11
|
export { default as TimePicker } from './TimePicker.svelte';
|
|
12
12
|
export { default as DateTimePicker } from './DateTimePicker.svelte';
|
|
13
|
+
export { default as Step } from './Step.svelte';
|
|
14
|
+
export { default as StepperForm } from './StepperForm.svelte';
|
|
15
|
+
export type { StepState, ValidationMode, StepRegistration, StepperFormContext, StepMeta, StepContext } from './Stepper.types';
|
|
@@ -10,3 +10,5 @@ export { default as InputGroup } from './InputGroup.svelte';
|
|
|
10
10
|
export { default as DatePicker } from './DatePicker.svelte';
|
|
11
11
|
export { default as TimePicker } from './TimePicker.svelte';
|
|
12
12
|
export { default as DateTimePicker } from './DateTimePicker.svelte';
|
|
13
|
+
export { default as Step } from './Step.svelte';
|
|
14
|
+
export { default as StepperForm } from './StepperForm.svelte';
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
<script lang="ts">let { steps, current = 0, states, variant = 'default', orientation = 'horizontal', showNumbers = true, showProgress = true, clickable = false, onStepClick, class: className = '' } = $props();
|
|
2
|
+
/* ---------------------------------------------
|
|
3
|
+
* Normalize steps (stable IDs, StepForm friendly)
|
|
4
|
+
* --------------------------------------------- */
|
|
5
|
+
const normalizedSteps = $derived(steps.map((step, _) => typeof step === 'string'
|
|
6
|
+
? {
|
|
7
|
+
id: step.toLowerCase().replace(/\s+/g, '-'),
|
|
8
|
+
label: step
|
|
9
|
+
}
|
|
10
|
+
: step));
|
|
11
|
+
/* ---------------------------------------------
|
|
12
|
+
* Resolve step states
|
|
13
|
+
* Priority:
|
|
14
|
+
* 1. explicit `states` prop
|
|
15
|
+
* 2. per-step `state`
|
|
16
|
+
* 3. derive from `current`
|
|
17
|
+
* --------------------------------------------- */
|
|
18
|
+
const derivedStates = $derived(states && states.length === normalizedSteps.length
|
|
19
|
+
? states
|
|
20
|
+
: normalizedSteps.map((step, i) => {
|
|
21
|
+
if (step.state)
|
|
22
|
+
return step.state;
|
|
23
|
+
if (i < current)
|
|
24
|
+
return 'completed';
|
|
25
|
+
if (i === current)
|
|
26
|
+
return 'active';
|
|
27
|
+
return 'idle';
|
|
28
|
+
}));
|
|
29
|
+
/* ---------------------------------------------
|
|
30
|
+
* Progress bar percentage
|
|
31
|
+
* --------------------------------------------- */
|
|
32
|
+
const progressPercent = $derived(normalizedSteps.length > 1
|
|
33
|
+
? ((current + 1) / normalizedSteps.length) * 100
|
|
34
|
+
: 100);
|
|
35
|
+
/* ---------------------------------------------
|
|
36
|
+
* Interaction handlers
|
|
37
|
+
* --------------------------------------------- */
|
|
38
|
+
function handleStepClick(index) {
|
|
39
|
+
if (!clickable)
|
|
40
|
+
return;
|
|
41
|
+
const state = derivedStates[index];
|
|
42
|
+
if (state !== 'completed')
|
|
43
|
+
return;
|
|
44
|
+
onStepClick?.(index);
|
|
45
|
+
}
|
|
46
|
+
function handleKeyDown(event, index) {
|
|
47
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
48
|
+
event.preventDefault();
|
|
49
|
+
handleStepClick(index);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/* ---------------------------------------------
|
|
53
|
+
* Styling maps
|
|
54
|
+
* --------------------------------------------- */
|
|
55
|
+
const stateStyles = {
|
|
56
|
+
idle: {
|
|
57
|
+
circle: 'bg-[var(--color-base-2)] text-[var(--color-text-tertiary)]',
|
|
58
|
+
label: 'text-[var(--color-text-tertiary)]',
|
|
59
|
+
connector: 'bg-[var(--color-base-2)]'
|
|
60
|
+
},
|
|
61
|
+
active: {
|
|
62
|
+
circle: 'bg-[var(--color-accent-primary)] text-white shadow-[0_0_0_4px_rgba(139,92,246,0.3)]',
|
|
63
|
+
label: 'text-[var(--color-accent-primary)]',
|
|
64
|
+
connector: 'bg-[var(--color-base-2)]'
|
|
65
|
+
},
|
|
66
|
+
completed: {
|
|
67
|
+
circle: 'bg-[var(--color-accent-primary)] text-white',
|
|
68
|
+
label: 'text-[var(--color-text-secondary)]',
|
|
69
|
+
connector: 'bg-[var(--color-accent-primary)]'
|
|
70
|
+
},
|
|
71
|
+
error: {
|
|
72
|
+
circle: 'bg-[var(--color-error)] text-white',
|
|
73
|
+
label: 'text-[var(--color-error)]',
|
|
74
|
+
connector: 'bg-[var(--color-error)]'
|
|
75
|
+
},
|
|
76
|
+
disabled: {
|
|
77
|
+
circle: 'bg-[var(--color-base-1)] text-[var(--color-text-tertiary)] opacity-50',
|
|
78
|
+
label: 'text-[var(--color-text-tertiary)] opacity-50',
|
|
79
|
+
connector: 'bg-[var(--color-base-1)]'
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
const sizeConfig = {
|
|
83
|
+
default: { circle: 'w-8 h-8', text: 'text-sm', connector: 'h-0.5', gap: 'gap-3' },
|
|
84
|
+
compact: { circle: 'w-6 h-6', text: 'text-xs', connector: 'h-0.5', gap: 'gap-2' },
|
|
85
|
+
minimal: { circle: 'w-3 h-3', text: 'text-xs', connector: 'h-0.5', gap: 'gap-1' }
|
|
86
|
+
};
|
|
87
|
+
const sizes = $derived(sizeConfig[variant]);
|
|
88
|
+
export {};
|
|
89
|
+
</script>
|
|
90
|
+
|
|
91
|
+
<!-- ========================================================= -->
|
|
92
|
+
<!-- Root -->
|
|
93
|
+
<!-- ========================================================= -->
|
|
94
|
+
<div
|
|
95
|
+
class="stepper {className}"
|
|
96
|
+
class:stepper-horizontal={orientation === 'horizontal'}
|
|
97
|
+
class:stepper-vertical={orientation === 'vertical'}
|
|
98
|
+
role="group"
|
|
99
|
+
aria-label="Progress"
|
|
100
|
+
>
|
|
101
|
+
<!-- ========================================================= -->
|
|
102
|
+
<!-- Horizontal -->
|
|
103
|
+
<!-- ========================================================= -->
|
|
104
|
+
{#if orientation === 'horizontal'}
|
|
105
|
+
<div class="w-full">
|
|
106
|
+
{#if showProgress && variant !== 'minimal'}
|
|
107
|
+
<div class="relative mb-8">
|
|
108
|
+
<div class="h-2 rounded-full overflow-hidden bg-[var(--color-base-2)]">
|
|
109
|
+
<div
|
|
110
|
+
class="h-full transition-all duration-500 ease-out bg-[var(--color-accent-primary)]"
|
|
111
|
+
style="width: {progressPercent}%"
|
|
112
|
+
></div>
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
<div class="absolute top-0 left-0 right-0 flex justify-between items-center -mt-3">
|
|
116
|
+
{#each normalizedSteps as step, i}
|
|
117
|
+
{@const state = derivedStates[i]}
|
|
118
|
+
{@const styles = stateStyles[state]}
|
|
119
|
+
<div class="flex flex-col items-center">
|
|
120
|
+
<button
|
|
121
|
+
type="button"
|
|
122
|
+
class="{sizes.circle} rounded-full flex items-center justify-center transition-all duration-300 {styles.circle}"
|
|
123
|
+
class:cursor-pointer={clickable && state === 'completed'}
|
|
124
|
+
disabled={state === 'disabled'}
|
|
125
|
+
onclick={() => handleStepClick(i)}
|
|
126
|
+
onkeydown={(e) => handleKeyDown(e, i)}
|
|
127
|
+
aria-current={state === 'active' ? 'step' : undefined}
|
|
128
|
+
>
|
|
129
|
+
{#if state === 'completed' && variant !== 'minimal'}
|
|
130
|
+
✓
|
|
131
|
+
{:else if step.icon}
|
|
132
|
+
{@render step.icon()}
|
|
133
|
+
{:else if showNumbers && variant !== 'minimal'}
|
|
134
|
+
<span class="{sizes.text} font-bold">{i + 1}</span>
|
|
135
|
+
{/if}
|
|
136
|
+
</button>
|
|
137
|
+
|
|
138
|
+
{#if variant !== 'minimal'}
|
|
139
|
+
<span class="hidden md:block mt-2 {sizes.text} font-medium {styles.label}">
|
|
140
|
+
{step.label}
|
|
141
|
+
</span>
|
|
142
|
+
{/if}
|
|
143
|
+
</div>
|
|
144
|
+
{/each}
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|
|
147
|
+
{/if}
|
|
148
|
+
</div>
|
|
149
|
+
|
|
150
|
+
<!-- ========================================================= -->
|
|
151
|
+
<!-- Vertical -->
|
|
152
|
+
<!-- ========================================================= -->
|
|
153
|
+
{:else}
|
|
154
|
+
<div class="flex flex-col {sizes.gap}">
|
|
155
|
+
{#each normalizedSteps as step, i}
|
|
156
|
+
{@const state = derivedStates[i]}
|
|
157
|
+
{@const styles = stateStyles[state]}
|
|
158
|
+
{@const isLast = i === normalizedSteps.length - 1}
|
|
159
|
+
|
|
160
|
+
<div class="flex">
|
|
161
|
+
<div class="flex flex-col items-center mr-4">
|
|
162
|
+
<button
|
|
163
|
+
type="button"
|
|
164
|
+
class="{sizes.circle} rounded-full flex items-center justify-center transition-all duration-300 {styles.circle}"
|
|
165
|
+
class:cursor-pointer={clickable && state === 'completed'}
|
|
166
|
+
disabled={state === 'disabled'}
|
|
167
|
+
onclick={() => handleStepClick(i)}
|
|
168
|
+
onkeydown={(e) => handleKeyDown(e, i)}
|
|
169
|
+
aria-current={state === 'active' ? 'step' : undefined}
|
|
170
|
+
>
|
|
171
|
+
{#if state === 'completed' && variant !== 'minimal'}✓{/if}
|
|
172
|
+
</button>
|
|
173
|
+
|
|
174
|
+
{#if !isLast}
|
|
175
|
+
<div
|
|
176
|
+
class="w-0.5 flex-1 min-h-8 my-2 rounded-full transition-colors duration-300"
|
|
177
|
+
class:bg-[var(--color-accent-primary)]={state === 'completed'}
|
|
178
|
+
class:bg-[var(--color-base-2)]={state !== 'completed'}
|
|
179
|
+
></div>
|
|
180
|
+
{/if}
|
|
181
|
+
</div>
|
|
182
|
+
|
|
183
|
+
<div class="flex-1 pb-8">
|
|
184
|
+
<span class="{sizes.text} font-medium {styles.label}">
|
|
185
|
+
{step.label}
|
|
186
|
+
</span>
|
|
187
|
+
{#if step.description && variant === 'default'}
|
|
188
|
+
<p class="mt-1 text-xs text-[var(--color-text-tertiary)]">
|
|
189
|
+
{step.description}
|
|
190
|
+
</p>
|
|
191
|
+
{/if}
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
|
194
|
+
{/each}
|
|
195
|
+
</div>
|
|
196
|
+
{/if}
|
|
197
|
+
</div>
|
|
198
|
+
|
|
199
|
+
<style>
|
|
200
|
+
@media (prefers-reduced-motion: reduce) {
|
|
201
|
+
.stepper * {
|
|
202
|
+
transition-duration: 0.01ms !important;
|
|
203
|
+
}
|
|
204
|
+
}</style>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
export type StepState = 'idle' | 'active' | 'completed' | 'error' | 'disabled';
|
|
3
|
+
export interface StepConfig {
|
|
4
|
+
id: string;
|
|
5
|
+
label: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
icon?: Snippet;
|
|
8
|
+
state?: StepState;
|
|
9
|
+
}
|
|
10
|
+
interface Props {
|
|
11
|
+
/** Array of step labels or full step configs */
|
|
12
|
+
steps: (string | StepConfig)[];
|
|
13
|
+
/** Current step index (0-based) */
|
|
14
|
+
current?: number;
|
|
15
|
+
/** Optional explicit states (must match steps length) */
|
|
16
|
+
states?: StepState[];
|
|
17
|
+
/** Visual variant */
|
|
18
|
+
variant?: 'default' | 'compact' | 'minimal';
|
|
19
|
+
/** Orientation */
|
|
20
|
+
orientation?: 'horizontal' | 'vertical';
|
|
21
|
+
/** Show step numbers */
|
|
22
|
+
showNumbers?: boolean;
|
|
23
|
+
/** Show progress bar (horizontal only) */
|
|
24
|
+
showProgress?: boolean;
|
|
25
|
+
/** Allow clicking on completed steps */
|
|
26
|
+
clickable?: boolean;
|
|
27
|
+
/** Callback when a step is clicked */
|
|
28
|
+
onStepClick?: (index: number) => void;
|
|
29
|
+
/** Additional CSS class */
|
|
30
|
+
class?: string;
|
|
31
|
+
}
|
|
32
|
+
declare const Stepper: import("svelte").Component<Props, {}, "">;
|
|
33
|
+
type Stepper = ReturnType<typeof Stepper>;
|
|
34
|
+
export default Stepper;
|
|
@@ -5,3 +5,4 @@ export { default as Breadcrumbs } from './Breadcrumbs.svelte';
|
|
|
5
5
|
export { default as Menu } from './Menu.svelte';
|
|
6
6
|
export { default as DropdownMenu } from './DropdownMenu.svelte';
|
|
7
7
|
export { default as ContextMenu } from './ContextMenu.svelte';
|
|
8
|
+
export { default as Stepper } from './Stepper.svelte';
|
|
@@ -5,3 +5,4 @@ export { default as Breadcrumbs } from './Breadcrumbs.svelte';
|
|
|
5
5
|
export { default as Menu } from './Menu.svelte';
|
|
6
6
|
export { default as DropdownMenu } from './DropdownMenu.svelte';
|
|
7
7
|
export { default as ContextMenu } from './ContextMenu.svelte';
|
|
8
|
+
export { default as Stepper } from './Stepper.svelte';
|
package/dist/index.d.ts
CHANGED
|
@@ -4,11 +4,12 @@
|
|
|
4
4
|
* This is the main entry point for the component library.
|
|
5
5
|
*/
|
|
6
6
|
import './theme.css';
|
|
7
|
-
export declare const version = "0.
|
|
7
|
+
export declare const version = "0.7.1";
|
|
8
8
|
export { Button, IconButton, ButtonGroup, LinkButton, FloatingActionButton } from './components/buttons';
|
|
9
|
-
export { Input, Textarea, Select, Checkbox, Switch, RadioGroup, RangeSlider, FileUpload, InputGroup, DatePicker, TimePicker, DateTimePicker } from './components/forms';
|
|
9
|
+
export { Input, Textarea, Select, Checkbox, Switch, RadioGroup, RangeSlider, FileUpload, InputGroup, DatePicker, TimePicker, DateTimePicker, StepperForm, Step } from './components/forms';
|
|
10
|
+
export type { StepState, ValidationMode, StepRegistration, StepperFormContext, StepMeta, StepContext } from './components/forms';
|
|
10
11
|
export { Card, Panel, Grid, Container, SectionHeader, Divider } from './components/cards';
|
|
11
|
-
export { Navbar, Sidebar, Tabs, Breadcrumbs, Menu, DropdownMenu, ContextMenu } from './components/navigation';
|
|
12
|
+
export { Navbar, Sidebar, Tabs, Breadcrumbs, Menu, DropdownMenu, ContextMenu, Stepper } from './components/navigation';
|
|
12
13
|
export { Modal, Alert, Spinner, Tooltip, ProgressBar, SkeletonLoader, Toast, Drawer, Popover, Dropdown, CommandPalette } from './components/overlays';
|
|
13
14
|
export { Heading, Text, Code } from './components/typography';
|
|
14
15
|
export { Table, Pagination, Badge, Tag, List, Avatar, CodeBlock, Stat, CalendarGrid, Sparkline } from './components/data';
|
|
@@ -29,6 +30,8 @@ import type RadioGroup from './components/forms/RadioGroup.svelte';
|
|
|
29
30
|
import type DatePicker from './components/forms/DatePicker.svelte';
|
|
30
31
|
import type TimePicker from './components/forms/TimePicker.svelte';
|
|
31
32
|
import type DateTimePicker from './components/forms/DateTimePicker.svelte';
|
|
33
|
+
import type StepperForm from './components/forms/StepperForm.svelte';
|
|
34
|
+
import type Step from './components/forms/Step.svelte';
|
|
32
35
|
import type Card from './components/cards/Card.svelte';
|
|
33
36
|
import type Tabs from './components/navigation/Tabs.svelte';
|
|
34
37
|
import type Modal from './components/overlays/Modal.svelte';
|
|
@@ -37,6 +40,7 @@ import type Alert from './components/overlays/Alert.svelte';
|
|
|
37
40
|
import type Tooltip from './components/overlays/Tooltip.svelte';
|
|
38
41
|
import type Accordion from './components/utilities/Accordion.svelte';
|
|
39
42
|
import type Sparkline from './components/data/Sparkline.svelte';
|
|
43
|
+
import type Stepper from './components/navigation/Stepper.svelte';
|
|
40
44
|
export type ButtonProps = ComponentProps<Button>;
|
|
41
45
|
export type IconButtonProps = ComponentProps<IconButton>;
|
|
42
46
|
export type FloatingActionButtonProps = ComponentProps<FloatingActionButton>;
|
|
@@ -50,6 +54,8 @@ export type RadioGroupProps = ComponentProps<RadioGroup>;
|
|
|
50
54
|
export type DatePickerProps = ComponentProps<DatePicker>;
|
|
51
55
|
export type TimePickerProps = ComponentProps<TimePicker>;
|
|
52
56
|
export type DateTimePickerProps = ComponentProps<DateTimePicker>;
|
|
57
|
+
export type StepperFormProps = ComponentProps<StepperForm>;
|
|
58
|
+
export type StepProps = ComponentProps<Step>;
|
|
53
59
|
export type CardProps = ComponentProps<Card>;
|
|
54
60
|
export type TabsProps = ComponentProps<Tabs>;
|
|
55
61
|
export type ModalProps = ComponentProps<Modal>;
|
|
@@ -58,3 +64,4 @@ export type AlertProps = ComponentProps<Alert>;
|
|
|
58
64
|
export type TooltipProps = ComponentProps<Tooltip>;
|
|
59
65
|
export type AccordionProps = ComponentProps<Accordion>;
|
|
60
66
|
export type SparklineProps = ComponentProps<Sparkline>;
|
|
67
|
+
export type StepperProps = ComponentProps<Stepper>;
|
package/dist/index.js
CHANGED
|
@@ -5,15 +5,15 @@
|
|
|
5
5
|
*/
|
|
6
6
|
// Import theme CSS to ensure it's bundled
|
|
7
7
|
import './theme.css';
|
|
8
|
-
export const version = '0.
|
|
8
|
+
export const version = '0.7.1';
|
|
9
9
|
// ===== Button Components =====
|
|
10
10
|
export { Button, IconButton, ButtonGroup, LinkButton, FloatingActionButton } from './components/buttons';
|
|
11
11
|
// ===== Form Components =====
|
|
12
|
-
export { Input, Textarea, Select, Checkbox, Switch, RadioGroup, RangeSlider, FileUpload, InputGroup, DatePicker, TimePicker, DateTimePicker } from './components/forms';
|
|
12
|
+
export { Input, Textarea, Select, Checkbox, Switch, RadioGroup, RangeSlider, FileUpload, InputGroup, DatePicker, TimePicker, DateTimePicker, StepperForm, Step } from './components/forms';
|
|
13
13
|
// ===== Layout Components =====
|
|
14
14
|
export { Card, Panel, Grid, Container, SectionHeader, Divider } from './components/cards';
|
|
15
15
|
// ===== Navigation Components =====
|
|
16
|
-
export { Navbar, Sidebar, Tabs, Breadcrumbs, Menu, DropdownMenu, ContextMenu } from './components/navigation';
|
|
16
|
+
export { Navbar, Sidebar, Tabs, Breadcrumbs, Menu, DropdownMenu, ContextMenu, Stepper } from './components/navigation';
|
|
17
17
|
// ===== Overlay & Feedback Components =====
|
|
18
18
|
export { Modal, Alert, Spinner, Tooltip, ProgressBar, SkeletonLoader, Toast, Drawer, Popover, Dropdown, CommandPalette } from './components/overlays';
|
|
19
19
|
// ===== Typography Components =====
|
package/dist/theme.css
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
--color-red-400: oklch(70.4% 0.191 22.216);
|
|
10
10
|
--color-red-500: oklch(63.7% 0.237 25.331);
|
|
11
11
|
--color-neutral-400: oklch(70.8% 0 0);
|
|
12
|
+
--color-white: #fff;
|
|
12
13
|
--spacing: 0.25rem;
|
|
13
14
|
--breakpoint-sm: 40rem;
|
|
14
15
|
--breakpoint-md: 48rem;
|
|
@@ -43,6 +44,7 @@
|
|
|
43
44
|
--radius-lg: 14px;
|
|
44
45
|
--radius-xl: 18px;
|
|
45
46
|
--radius-2xl: 24px;
|
|
47
|
+
--ease-out: cubic-bezier(0, 0, 0.2, 1);
|
|
46
48
|
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
|
|
47
49
|
--animate-spin: spin 1s linear infinite;
|
|
48
50
|
--blur-md: 12px;
|
|
@@ -701,6 +703,9 @@
|
|
|
701
703
|
.my-6 {
|
|
702
704
|
margin-block: calc(var(--spacing) * 6);
|
|
703
705
|
}
|
|
706
|
+
.-mt-3 {
|
|
707
|
+
margin-top: calc(var(--spacing) * -3);
|
|
708
|
+
}
|
|
704
709
|
.mt-1 {
|
|
705
710
|
margin-top: calc(var(--spacing) * 1);
|
|
706
711
|
}
|
|
@@ -719,6 +724,9 @@
|
|
|
719
724
|
.mr-2 {
|
|
720
725
|
margin-right: calc(var(--spacing) * 2);
|
|
721
726
|
}
|
|
727
|
+
.mr-4 {
|
|
728
|
+
margin-right: calc(var(--spacing) * 4);
|
|
729
|
+
}
|
|
722
730
|
.mb-1 {
|
|
723
731
|
margin-bottom: calc(var(--spacing) * 1);
|
|
724
732
|
}
|
|
@@ -788,6 +796,9 @@
|
|
|
788
796
|
.table {
|
|
789
797
|
display: table;
|
|
790
798
|
}
|
|
799
|
+
.h-0\.5 {
|
|
800
|
+
height: calc(var(--spacing) * 0.5);
|
|
801
|
+
}
|
|
791
802
|
.h-2 {
|
|
792
803
|
height: calc(var(--spacing) * 2);
|
|
793
804
|
}
|
|
@@ -848,9 +859,15 @@
|
|
|
848
859
|
.max-h-\[400px\] {
|
|
849
860
|
max-height: 400px;
|
|
850
861
|
}
|
|
862
|
+
.min-h-8 {
|
|
863
|
+
min-height: calc(var(--spacing) * 8);
|
|
864
|
+
}
|
|
851
865
|
.min-h-\[100px\] {
|
|
852
866
|
min-height: 100px;
|
|
853
867
|
}
|
|
868
|
+
.w-0\.5 {
|
|
869
|
+
width: calc(var(--spacing) * 0.5);
|
|
870
|
+
}
|
|
854
871
|
.w-2 {
|
|
855
872
|
width: calc(var(--spacing) * 2);
|
|
856
873
|
}
|
|
@@ -1313,12 +1330,21 @@
|
|
|
1313
1330
|
.bg-\[var\(--color-accent-overlay-20\)\] {
|
|
1314
1331
|
background-color: var(--color-accent-overlay-20);
|
|
1315
1332
|
}
|
|
1333
|
+
.bg-\[var\(--color-accent-primary\)\] {
|
|
1334
|
+
background-color: var(--color-accent-primary);
|
|
1335
|
+
}
|
|
1336
|
+
.bg-\[var\(--color-base-1\)\] {
|
|
1337
|
+
background-color: var(--color-base-1);
|
|
1338
|
+
}
|
|
1316
1339
|
.bg-\[var\(--color-base-2\)\] {
|
|
1317
1340
|
background-color: var(--color-base-2);
|
|
1318
1341
|
}
|
|
1319
1342
|
.bg-\[var\(--color-base-3\)\] {
|
|
1320
1343
|
background-color: var(--color-base-3);
|
|
1321
1344
|
}
|
|
1345
|
+
.bg-\[var\(--color-error\)\] {
|
|
1346
|
+
background-color: var(--color-error);
|
|
1347
|
+
}
|
|
1322
1348
|
.bg-\[var\(--color-error-overlay-15\)\] {
|
|
1323
1349
|
background-color: var(--color-error-overlay-15);
|
|
1324
1350
|
}
|
|
@@ -1451,6 +1477,9 @@
|
|
|
1451
1477
|
.pb-3 {
|
|
1452
1478
|
padding-bottom: calc(var(--spacing) * 3);
|
|
1453
1479
|
}
|
|
1480
|
+
.pb-8 {
|
|
1481
|
+
padding-bottom: calc(var(--spacing) * 8);
|
|
1482
|
+
}
|
|
1454
1483
|
.pl-4 {
|
|
1455
1484
|
padding-left: calc(var(--spacing) * 4);
|
|
1456
1485
|
}
|
|
@@ -1545,6 +1574,9 @@
|
|
|
1545
1574
|
.text-\[var\(--color-accent\)\] {
|
|
1546
1575
|
color: var(--color-accent);
|
|
1547
1576
|
}
|
|
1577
|
+
.text-\[var\(--color-accent-primary\)\] {
|
|
1578
|
+
color: var(--color-accent-primary);
|
|
1579
|
+
}
|
|
1548
1580
|
.text-\[var\(--color-accent-soft\)\] {
|
|
1549
1581
|
color: var(--color-accent-soft);
|
|
1550
1582
|
}
|
|
@@ -1563,9 +1595,15 @@
|
|
|
1563
1595
|
.text-\[var\(--color-text-muted\)\] {
|
|
1564
1596
|
color: var(--color-text-muted);
|
|
1565
1597
|
}
|
|
1598
|
+
.text-\[var\(--color-text-secondary\)\] {
|
|
1599
|
+
color: var(--color-text-secondary);
|
|
1600
|
+
}
|
|
1566
1601
|
.text-\[var\(--color-text-soft\)\] {
|
|
1567
1602
|
color: var(--color-text-soft);
|
|
1568
1603
|
}
|
|
1604
|
+
.text-\[var\(--color-text-tertiary\)\] {
|
|
1605
|
+
color: var(--color-text-tertiary);
|
|
1606
|
+
}
|
|
1569
1607
|
.text-\[var\(--color-warning\)\] {
|
|
1570
1608
|
color: var(--color-warning);
|
|
1571
1609
|
}
|
|
@@ -1602,6 +1640,9 @@
|
|
|
1602
1640
|
.text-warning {
|
|
1603
1641
|
color: var(--color-warning);
|
|
1604
1642
|
}
|
|
1643
|
+
.text-white {
|
|
1644
|
+
color: var(--color-white);
|
|
1645
|
+
}
|
|
1605
1646
|
.lowercase {
|
|
1606
1647
|
text-transform: lowercase;
|
|
1607
1648
|
}
|
|
@@ -1636,6 +1677,10 @@
|
|
|
1636
1677
|
--tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
|
|
1637
1678
|
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
|
1638
1679
|
}
|
|
1680
|
+
.shadow-\[0_0_0_4px_rgba\(139\,92\,246\,0\.3\)\] {
|
|
1681
|
+
--tw-shadow: 0 0 0 4px var(--tw-shadow-color, rgba(139,92,246,0.3));
|
|
1682
|
+
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
|
1683
|
+
}
|
|
1639
1684
|
.shadow-\[0_0_12px_var\(--color-info-overlay-20\)\] {
|
|
1640
1685
|
--tw-shadow: 0 0 12px var(--tw-shadow-color, var(--color-info-overlay-20));
|
|
1641
1686
|
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
|
@@ -1656,6 +1701,20 @@
|
|
|
1656
1701
|
--tw-shadow: 0 20px 25px -5px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 8px 10px -6px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
|
|
1657
1702
|
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
|
1658
1703
|
}
|
|
1704
|
+
.ring-2 {
|
|
1705
|
+
--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
|
|
1706
|
+
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
|
1707
|
+
}
|
|
1708
|
+
.ring-accent {
|
|
1709
|
+
--tw-ring-color: var(--color-accent);
|
|
1710
|
+
}
|
|
1711
|
+
.ring-offset-2 {
|
|
1712
|
+
--tw-ring-offset-width: 2px;
|
|
1713
|
+
--tw-ring-offset-shadow: var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
|
|
1714
|
+
}
|
|
1715
|
+
.ring-offset-base-0 {
|
|
1716
|
+
--tw-ring-offset-color: var(--color-base-0);
|
|
1717
|
+
}
|
|
1659
1718
|
.outline {
|
|
1660
1719
|
outline-style: var(--tw-outline-style);
|
|
1661
1720
|
outline-width: 1px;
|
|
@@ -1738,6 +1797,10 @@
|
|
|
1738
1797
|
--tw-ease: var(--ease-luxe);
|
|
1739
1798
|
transition-timing-function: var(--ease-luxe);
|
|
1740
1799
|
}
|
|
1800
|
+
.ease-out {
|
|
1801
|
+
--tw-ease: var(--ease-out);
|
|
1802
|
+
transition-timing-function: var(--ease-out);
|
|
1803
|
+
}
|
|
1741
1804
|
.outline-none {
|
|
1742
1805
|
--tw-outline-style: none;
|
|
1743
1806
|
outline-style: none;
|
|
@@ -1997,6 +2060,11 @@
|
|
|
1997
2060
|
margin-left: calc(var(--spacing) * 64);
|
|
1998
2061
|
}
|
|
1999
2062
|
}
|
|
2063
|
+
.md\:block {
|
|
2064
|
+
@media (width >= 48rem) {
|
|
2065
|
+
display: block;
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
2000
2068
|
.md\:grid-cols-2 {
|
|
2001
2069
|
@media (width >= 48rem) {
|
|
2002
2070
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
package/package.json
CHANGED