@svelte-atoms/core 1.0.0-alpha.33 → 1.0.0-alpha.34
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/atom/html-atom.svelte +95 -93
- package/dist/components/atom/html-atom.svelte.d.ts +1 -1
- package/dist/components/button/button.svelte +31 -31
- package/dist/components/chip/chip.svelte +41 -0
- package/dist/components/chip/chip.svelte.d.ts +6 -0
- package/dist/components/chip/index.d.ts +1 -0
- package/dist/components/chip/index.js +1 -0
- package/dist/components/chip/types.d.ts +11 -0
- package/dist/components/chip/types.js +1 -0
- package/dist/components/combobox/atoms.d.ts +2 -1
- package/dist/components/combobox/atoms.js +2 -1
- package/dist/components/combobox/bond.svelte.d.ts +16 -13
- package/dist/components/combobox/bond.svelte.js +57 -13
- package/dist/components/combobox/combobox-control.svelte +27 -13
- package/dist/components/combobox/combobox-control.svelte.d.ts +2 -10
- package/dist/components/combobox/combobox-item.svelte +3 -3
- package/dist/components/combobox/combobox-root.svelte +65 -65
- package/dist/components/combobox/combobox-root.svelte.d.ts +4 -4
- package/dist/components/combobox/combobox-selections.svelte +17 -0
- package/dist/components/combobox/combobox-selections.svelte.d.ts +4 -0
- package/dist/components/combobox/combobox.stories.svelte +25 -12
- package/dist/components/combobox/types.d.ts +17 -3
- package/dist/components/dropdown/atoms.d.ts +2 -3
- package/dist/components/dropdown/atoms.js +3 -3
- package/dist/components/dropdown/bond.svelte.d.ts +4 -4
- package/dist/components/dropdown/bond.svelte.js +10 -11
- package/dist/components/dropdown/dropdown-query.svelte +43 -54
- package/dist/components/dropdown/dropdown-query.svelte.d.ts +3 -34
- package/dist/components/dropdown/dropdown-root.svelte +15 -2
- package/dist/components/dropdown/dropdown-root.svelte.d.ts +4 -16
- package/dist/components/dropdown/dropdown-selection.svelte +55 -0
- package/dist/components/dropdown/{dropdown-value.svelte.d.ts → dropdown-selection.svelte.d.ts} +5 -15
- package/dist/components/dropdown/dropdown-selections.svelte +62 -0
- package/dist/components/dropdown/dropdown-selections.svelte.d.ts +5 -0
- package/dist/components/dropdown/dropdown.stories.svelte +93 -49
- package/dist/components/dropdown/index.d.ts +5 -1
- package/dist/components/dropdown/index.js +5 -1
- package/dist/components/dropdown/item/controller.svelte.d.ts +1 -1
- package/dist/components/dropdown/item/controller.svelte.js +2 -2
- package/dist/components/dropdown/item/dropdown-item.svelte +0 -2
- package/dist/components/dropdown/runes.svelte.d.ts +6 -2
- package/dist/components/dropdown/runes.svelte.js +1 -1
- package/dist/components/dropdown/types.d.ts +34 -2
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.js +2 -0
- package/dist/components/menu/bond.svelte.js +4 -2
- package/dist/components/menu/item/controller.svelte.d.ts +1 -0
- package/dist/components/menu/item/controller.svelte.js +5 -0
- package/dist/components/popover/bond.svelte.js +1 -2
- package/dist/components/root/root.css +119 -119
- package/dist/components/stepper/README.md +38 -0
- package/dist/components/stepper/atoms.d.ts +5 -0
- package/dist/components/stepper/atoms.js +5 -0
- package/dist/components/stepper/attachments.svelte.d.ts +2 -0
- package/dist/components/stepper/attachments.svelte.js +5 -0
- package/dist/components/stepper/bond.svelte.d.ts +56 -0
- package/dist/components/stepper/bond.svelte.js +99 -0
- package/dist/components/stepper/index.d.ts +3 -0
- package/dist/components/stepper/index.js +3 -0
- package/dist/components/stepper/step/README.md +97 -0
- package/dist/components/stepper/step/atoms.d.ts +7 -0
- package/dist/components/stepper/step/atoms.js +7 -0
- package/dist/components/stepper/step/attachments.svelte.d.ts +2 -0
- package/dist/components/stepper/step/attachments.svelte.js +5 -0
- package/dist/components/stepper/step/bond.svelte.d.ts +99 -0
- package/dist/components/stepper/step/bond.svelte.js +153 -0
- package/dist/components/stepper/step/index.d.ts +3 -0
- package/dist/components/stepper/step/index.js +2 -0
- package/dist/components/stepper/step/step-body.svelte +39 -0
- package/dist/components/stepper/step/step-body.svelte.d.ts +26 -0
- package/dist/components/stepper/step/step-description.svelte +33 -0
- package/dist/components/stepper/step/step-description.svelte.d.ts +26 -0
- package/dist/components/stepper/step/step-header.svelte +34 -0
- package/dist/components/stepper/step/step-header.svelte.d.ts +26 -0
- package/dist/components/stepper/step/step-indicator.svelte +63 -0
- package/dist/components/stepper/step/step-indicator.svelte.d.ts +26 -0
- package/dist/components/stepper/step/step-root.svelte +42 -0
- package/dist/components/stepper/step/step-root.svelte.d.ts +31 -0
- package/dist/components/stepper/step/step-separator.svelte +48 -0
- package/dist/components/stepper/step/step-separator.svelte.d.ts +26 -0
- package/dist/components/stepper/step/step-title.svelte +33 -0
- package/dist/components/stepper/step/step-title.svelte.d.ts +26 -0
- package/dist/components/stepper/step/types.d.ts +91 -0
- package/dist/components/stepper/step/types.js +1 -0
- package/dist/components/stepper/stepper-body.svelte +43 -0
- package/dist/components/stepper/stepper-body.svelte.d.ts +26 -0
- package/dist/components/stepper/stepper-content.svelte +45 -0
- package/dist/components/stepper/stepper-content.svelte.d.ts +26 -0
- package/dist/components/stepper/stepper-footer.svelte +31 -0
- package/dist/components/stepper/stepper-footer.svelte.d.ts +26 -0
- package/dist/components/stepper/stepper-header.svelte +39 -0
- package/dist/components/stepper/stepper-header.svelte.d.ts +26 -0
- package/dist/components/stepper/stepper-root.svelte +60 -0
- package/dist/components/stepper/stepper-root.svelte.d.ts +31 -0
- package/dist/components/stepper/stepper.stories.svelte +264 -0
- package/dist/components/stepper/stepper.stories.svelte.d.ts +4 -0
- package/dist/components/stepper/types.d.ts +63 -0
- package/dist/components/stepper/types.js +1 -0
- package/dist/components/tabs/atoms.d.ts +1 -0
- package/dist/components/tabs/atoms.js +1 -0
- package/dist/components/tabs/bond.svelte.d.ts +18 -5
- package/dist/components/tabs/bond.svelte.js +13 -0
- package/dist/components/tabs/tab/bond.svelte.d.ts +7 -7
- package/dist/components/tabs/tab/bond.svelte.js +9 -15
- package/dist/components/tabs/tab/tab-body.svelte +43 -52
- package/dist/components/tabs/tab/tab-description.svelte +33 -41
- package/dist/components/tabs/tab/tab-header.svelte +61 -71
- package/dist/components/tabs/tab/tab-header.svelte.d.ts +1 -1
- package/dist/components/tabs/tab/tab-root.svelte +51 -86
- package/dist/components/tabs/tab/tab-root.svelte.d.ts +1 -7
- package/dist/components/tabs/tabs-body.svelte +35 -41
- package/dist/components/tabs/tabs-body.svelte.d.ts +2 -11
- package/dist/components/tabs/tabs-content.svelte +51 -0
- package/dist/components/tabs/tabs-content.svelte.d.ts +26 -0
- package/dist/components/tabs/tabs-header.svelte +32 -40
- package/dist/components/tabs/tabs-header.svelte.d.ts +2 -10
- package/dist/components/tabs/tabs-root.svelte +55 -66
- package/dist/components/tabs/tabs-root.svelte.d.ts +5 -16
- package/dist/components/tabs/tabs.stories.svelte +70 -56
- package/dist/components/tabs/types.d.ts +24 -29
- package/dist/context/preset.svelte.d.ts +1 -1
- package/package.json +33 -6
- package/dist/components/dropdown/dropdown-value.svelte +0 -60
- package/dist/components/dropdown/dropdown-values.svelte +0 -17
- package/dist/components/dropdown/dropdown-values.svelte.d.ts +0 -5
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
<script module>
|
|
2
|
+
import { defineMeta } from '@storybook/addon-svelte-csf';
|
|
3
|
+
|
|
4
|
+
// More on how to set up stories at: https://storybook.js.org/docs/writing-stories
|
|
5
|
+
const { Story } = defineMeta({
|
|
6
|
+
title: 'Atoms/Stepper',
|
|
7
|
+
// This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
|
|
8
|
+
|
|
9
|
+
parameters: {
|
|
10
|
+
// More on how to position stories at: https://storybook.js.org/docs/configure/story-layout
|
|
11
|
+
layout: 'fullscreen'
|
|
12
|
+
},
|
|
13
|
+
args: {}
|
|
14
|
+
});
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<script lang="ts">
|
|
18
|
+
import { Stepper, Step } from './';
|
|
19
|
+
import { Button } from '../button';
|
|
20
|
+
import { Textarea } from '../textarea';
|
|
21
|
+
import type { StepperBond } from './bond.svelte';
|
|
22
|
+
import { animate } from 'motion'
|
|
23
|
+
import { Stack } from '../stack';
|
|
24
|
+
|
|
25
|
+
const steps = $state([
|
|
26
|
+
{
|
|
27
|
+
header: 'Account Information',
|
|
28
|
+
body: 'Enter your personal details',
|
|
29
|
+
completed: false
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
header: 'Address',
|
|
33
|
+
body: 'Provide your shipping address',
|
|
34
|
+
completed: false
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
header: 'Payment',
|
|
38
|
+
body: 'Select payment method',
|
|
39
|
+
completed: false
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
header: 'Review',
|
|
43
|
+
body: 'Review and confirm',
|
|
44
|
+
optional: true,
|
|
45
|
+
completed: false
|
|
46
|
+
}
|
|
47
|
+
]);
|
|
48
|
+
let activeStepIndex = $state(0);
|
|
49
|
+
|
|
50
|
+
function handleNext(stepper: StepperBond) {
|
|
51
|
+
stepper.state.navigation.next();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function handlePrevious(stepper: StepperBond) {
|
|
55
|
+
stepper.state.navigation.previous();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function handleReset(stepper: StepperBond) {
|
|
59
|
+
stepper.state.navigation.reset();
|
|
60
|
+
}
|
|
61
|
+
</script>
|
|
62
|
+
|
|
63
|
+
<Story name="Horizontal" args={{}}>
|
|
64
|
+
<Stepper.Root bind:step= {activeStepIndex} linear={true}>
|
|
65
|
+
{#snippet children({ stepper })}
|
|
66
|
+
<!-- Header: Step indicators -->
|
|
67
|
+
<Stepper.Header class="flex justify-between">
|
|
68
|
+
{#each steps as stepData, i}
|
|
69
|
+
<Step.Root index={i} header={stepData.header} body={stepData.body}>
|
|
70
|
+
{#snippet children({ step })}
|
|
71
|
+
{@const isActive = step?.state?.isActive}
|
|
72
|
+
|
|
73
|
+
<Step.Header class="flex flex-col gap-2 flex-1">
|
|
74
|
+
<div class="flex items-center w-full">
|
|
75
|
+
<Step.Indicator />
|
|
76
|
+
<Step.Separator class="" />
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
<div class="flex flex-col pr-4">
|
|
80
|
+
<Step.Title class={isActive
|
|
81
|
+
? 'text-foreground font-semibold'
|
|
82
|
+
: 'text-muted-foreground'}>
|
|
83
|
+
{stepData?.header}
|
|
84
|
+
{#if stepData?.optional}
|
|
85
|
+
<span class="text-xs">(Optional)</span>
|
|
86
|
+
{/if}
|
|
87
|
+
</Step.Title>
|
|
88
|
+
|
|
89
|
+
<Step.Description class={['text-xs text-muted-foreground']}>
|
|
90
|
+
{stepData?.body}
|
|
91
|
+
</Step.Description>
|
|
92
|
+
</div>
|
|
93
|
+
</Step.Header>
|
|
94
|
+
|
|
95
|
+
<!-- Step Content (shown in Stepper.Body when active) -->
|
|
96
|
+
<Step.Body>
|
|
97
|
+
<h3 class="text-xl font-semibold mb-4">
|
|
98
|
+
Step {i + 1}: {stepData.header}
|
|
99
|
+
</h3>
|
|
100
|
+
<p class="text-muted-foreground mb-6">{stepData.body}</p>
|
|
101
|
+
|
|
102
|
+
<!-- Actual step content would go here -->
|
|
103
|
+
<div class="text-sm text-muted-foreground">
|
|
104
|
+
Content for {stepData.header} step...
|
|
105
|
+
</div>
|
|
106
|
+
</Step.Body>
|
|
107
|
+
{/snippet}
|
|
108
|
+
</Step.Root>
|
|
109
|
+
{/each}
|
|
110
|
+
</Stepper.Header>
|
|
111
|
+
|
|
112
|
+
<!-- Body: Automatically renders active step content -->
|
|
113
|
+
<Stepper.Body class="my-6">
|
|
114
|
+
<Textarea.Root base={Stack.Root} class="overflow-hidden">
|
|
115
|
+
<Stepper.Content
|
|
116
|
+
base={Stack.Item}
|
|
117
|
+
class="min-h-50 flex flex-col p-6"
|
|
118
|
+
enter={(node)=> {
|
|
119
|
+
const duration = 0.4;
|
|
120
|
+
animate(node, { opacity: [0, 1], y: [20, 0] }, { duration });
|
|
121
|
+
return { duration: duration * 1000 };
|
|
122
|
+
}}
|
|
123
|
+
exit={(node)=> {
|
|
124
|
+
const duration = 0.3;
|
|
125
|
+
animate(node, { opacity: [1, 0], y: [0, 0] }, { duration });
|
|
126
|
+
return { duration: duration * 1000 };
|
|
127
|
+
}}
|
|
128
|
+
/>
|
|
129
|
+
</Textarea.Root>
|
|
130
|
+
</Stepper.Body>
|
|
131
|
+
|
|
132
|
+
<!-- Footer: Navigation buttons -->
|
|
133
|
+
<Stepper.Footer class="flex justify-between">
|
|
134
|
+
<Button
|
|
135
|
+
variant="outline"
|
|
136
|
+
disabled={stepper.state.isFirstStep}
|
|
137
|
+
onclick={() => handlePrevious(stepper)}
|
|
138
|
+
>
|
|
139
|
+
Previous
|
|
140
|
+
</Button>
|
|
141
|
+
|
|
142
|
+
<div class="flex gap-2">
|
|
143
|
+
<Button variant="ghost" onclick={() => handleReset(stepper)}>Reset</Button>
|
|
144
|
+
|
|
145
|
+
{#if stepper.state.isLastStep}
|
|
146
|
+
<Button onclick={() => alert('Complete!')}>Complete</Button>
|
|
147
|
+
{:else}
|
|
148
|
+
<Button onclick={() => handleNext(stepper)}>Next</Button>
|
|
149
|
+
{/if}
|
|
150
|
+
</div>
|
|
151
|
+
</Stepper.Footer>
|
|
152
|
+
{/snippet}
|
|
153
|
+
</Stepper.Root>
|
|
154
|
+
</Story>
|
|
155
|
+
|
|
156
|
+
<Story name="Vertical" args={{}}>
|
|
157
|
+
<Stepper.Root class="flex gap-4" step={1}>
|
|
158
|
+
{#snippet children({ stepper })}
|
|
159
|
+
<div class="flex gap-4">
|
|
160
|
+
<Stepper.Header class="">
|
|
161
|
+
{#each steps as stepData, i}
|
|
162
|
+
<Step.Root
|
|
163
|
+
index={i}
|
|
164
|
+
header={stepData.header}
|
|
165
|
+
body={stepData.body}
|
|
166
|
+
completed={stepData.completed}
|
|
167
|
+
optional={stepData.optional}
|
|
168
|
+
>
|
|
169
|
+
{#snippet children({ step })}
|
|
170
|
+
<Step.Header class="flex w-full">
|
|
171
|
+
<div class="flex gap-2">
|
|
172
|
+
<div class="flex flex-col">
|
|
173
|
+
<Step.Indicator class="" />
|
|
174
|
+
<Step.Separator class="w-[2px] min-h-10 translate-x-[15px]" />
|
|
175
|
+
</div>
|
|
176
|
+
<div class="flex flex-col">
|
|
177
|
+
<Step.Title>
|
|
178
|
+
{stepData.header}
|
|
179
|
+
{#if stepData.optional}
|
|
180
|
+
<span class="text-xs">(Optional)</span>
|
|
181
|
+
{/if}
|
|
182
|
+
</Step.Title>
|
|
183
|
+
|
|
184
|
+
<Step.Description>
|
|
185
|
+
{stepData}
|
|
186
|
+
</Step.Description>
|
|
187
|
+
</div>
|
|
188
|
+
</div>
|
|
189
|
+
|
|
190
|
+
</Step.Header>
|
|
191
|
+
|
|
192
|
+
<!-- Step Content (shown in Stepper.Body when active) -->
|
|
193
|
+
<Step.Body
|
|
194
|
+
enter={(node)=> {
|
|
195
|
+
const duration = 0.4;
|
|
196
|
+
animate(node, { opacity: [0, 1], y: [20, 0] }, { duration });
|
|
197
|
+
return { duration: duration * 1000 };
|
|
198
|
+
}}
|
|
199
|
+
exit={(node)=> {
|
|
200
|
+
const duration = 0.2;
|
|
201
|
+
animate(node, { opacity: [1, 0], y: [0, 0] }, { duration });
|
|
202
|
+
return { duration: duration * 1000 };
|
|
203
|
+
}}
|
|
204
|
+
>
|
|
205
|
+
<h3 class="text-xl font-semibold mb-4">
|
|
206
|
+
Step {i + 1}: {stepData.header}
|
|
207
|
+
</h3>
|
|
208
|
+
<p class="text-muted-foreground mb-6">{stepData.body}</p>
|
|
209
|
+
|
|
210
|
+
<!-- Actual step content would go here -->
|
|
211
|
+
<div class="text-sm text-muted-foreground">
|
|
212
|
+
Content for {stepData.header} step...
|
|
213
|
+
</div>
|
|
214
|
+
</Step.Body>
|
|
215
|
+
{/snippet}
|
|
216
|
+
</Step.Root>
|
|
217
|
+
{/each}
|
|
218
|
+
</Stepper.Header>
|
|
219
|
+
|
|
220
|
+
<!-- Body: Automatically renders active step content -->
|
|
221
|
+
<Stepper.Body class="h-full">
|
|
222
|
+
<Textarea.Root base={Stack.Root} class="overflow-hidden h-full min-w-96">
|
|
223
|
+
<Stepper.Content
|
|
224
|
+
base={Stack.Item}
|
|
225
|
+
class="min-h-50 flex flex-col p-6"
|
|
226
|
+
enter={(node)=> {
|
|
227
|
+
const duration = 0.4;
|
|
228
|
+
animate(node, { opacity: [0, 1], y: [20, 0] }, { duration });
|
|
229
|
+
return{duration: duration * 1000};
|
|
230
|
+
}}
|
|
231
|
+
exit={(node)=> {
|
|
232
|
+
const duration = 0.3;
|
|
233
|
+
animate(node, { opacity: [1, 0], y: [0, 0] }, { duration });
|
|
234
|
+
return { duration: duration * 1000 };
|
|
235
|
+
}}
|
|
236
|
+
/>
|
|
237
|
+
</Textarea.Root>
|
|
238
|
+
</Stepper.Body>
|
|
239
|
+
</div>
|
|
240
|
+
|
|
241
|
+
<Stepper.Footer>
|
|
242
|
+
<div class="flex justify-between">
|
|
243
|
+
<Button
|
|
244
|
+
variant="outline"
|
|
245
|
+
disabled={stepper.state.isFirstStep}
|
|
246
|
+
onclick={() => handlePrevious(stepper)}
|
|
247
|
+
>
|
|
248
|
+
Previous
|
|
249
|
+
</Button>
|
|
250
|
+
|
|
251
|
+
<div class="flex gap-2">
|
|
252
|
+
<Button variant="ghost" onclick={() => handleReset(stepper)}>Reset</Button>
|
|
253
|
+
|
|
254
|
+
{#if stepper.state.isLastStep}
|
|
255
|
+
<Button onclick={() => alert('Complete!')}>Complete</Button>
|
|
256
|
+
{:else}
|
|
257
|
+
<Button onclick={() => handleNext(stepper)}>Next</Button>
|
|
258
|
+
{/if}
|
|
259
|
+
</div>
|
|
260
|
+
</div>
|
|
261
|
+
</Stepper.Footer>
|
|
262
|
+
{/snippet}
|
|
263
|
+
</Stepper.Root>
|
|
264
|
+
</Story>
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HtmlAtomProps, Base } from '../atom';
|
|
3
|
+
import type { Factory } from '../../types';
|
|
4
|
+
import type { StepperBond } from './bond.svelte';
|
|
5
|
+
export interface StepperRootProps<E extends keyof HTMLElementTagNameMap = 'div', B extends Base = Base> extends Omit<HtmlAtomProps<E, B>, 'children'> {
|
|
6
|
+
/**
|
|
7
|
+
* The active step index (0-based)
|
|
8
|
+
* @bindable
|
|
9
|
+
*/
|
|
10
|
+
step?: number;
|
|
11
|
+
/**
|
|
12
|
+
* Whether to enforce linear progression (users can only go to next/previous steps)
|
|
13
|
+
* @default false
|
|
14
|
+
*/
|
|
15
|
+
linear?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Whether the stepper is disabled
|
|
18
|
+
* @default false
|
|
19
|
+
*/
|
|
20
|
+
disabled?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Custom factory for creating stepper bond
|
|
23
|
+
*/
|
|
24
|
+
factory?: Factory<StepperBond>;
|
|
25
|
+
/**
|
|
26
|
+
* Child render function
|
|
27
|
+
*/
|
|
28
|
+
children?: Snippet<[{
|
|
29
|
+
stepper: StepperBond;
|
|
30
|
+
}]>;
|
|
31
|
+
}
|
|
32
|
+
export interface StepperHeaderProps<E extends keyof HTMLElementTagNameMap = 'div', B extends Base = Base> extends Omit<HtmlAtomProps<E, B>, 'children'> {
|
|
33
|
+
/**
|
|
34
|
+
* Child render function
|
|
35
|
+
*/
|
|
36
|
+
children?: Snippet<[{
|
|
37
|
+
stepper?: StepperBond;
|
|
38
|
+
}]>;
|
|
39
|
+
}
|
|
40
|
+
export interface StepperBodyProps<E extends keyof HTMLElementTagNameMap = 'div', B extends Base = Base> extends Omit<HtmlAtomProps<E, B>, 'children'> {
|
|
41
|
+
/**
|
|
42
|
+
* Child render function
|
|
43
|
+
*/
|
|
44
|
+
children?: Snippet<[{
|
|
45
|
+
stepper?: StepperBond;
|
|
46
|
+
}]>;
|
|
47
|
+
}
|
|
48
|
+
export interface StepperFooterProps<E extends keyof HTMLElementTagNameMap = 'div', B extends Base = Base> extends Omit<HtmlAtomProps<E, B>, 'children'> {
|
|
49
|
+
/**
|
|
50
|
+
* Child render function
|
|
51
|
+
*/
|
|
52
|
+
children?: Snippet<[{
|
|
53
|
+
stepper?: StepperBond;
|
|
54
|
+
}]>;
|
|
55
|
+
}
|
|
56
|
+
export interface StepperContentProps<E extends keyof HTMLElementTagNameMap = 'div', B extends Base = Base> extends Omit<HtmlAtomProps<E, B>, 'children'> {
|
|
57
|
+
/**
|
|
58
|
+
* Child render function
|
|
59
|
+
*/
|
|
60
|
+
children?: Snippet<[{
|
|
61
|
+
stepper?: StepperBond;
|
|
62
|
+
}]>;
|
|
63
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,22 +1,29 @@
|
|
|
1
1
|
import type { TabBond } from './tab/bond.svelte';
|
|
2
|
-
import { Bond, BondState } from '../../shared/bond.svelte';
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import { Bond, BondState, type BondStateProps } from '../../shared/bond.svelte';
|
|
3
|
+
import type { Snippet } from 'svelte';
|
|
4
|
+
export type TabsBondProps<T extends Record<string, unknown> = Record<string, unknown>> = BondStateProps & {
|
|
5
|
+
value?: string | undefined;
|
|
5
6
|
multiple?: boolean;
|
|
6
|
-
extend
|
|
7
|
+
extend?: T;
|
|
7
8
|
};
|
|
8
9
|
export type TabElements = {
|
|
9
10
|
root: HTMLElement;
|
|
10
11
|
header: HTMLElement;
|
|
11
12
|
body: HTMLElement;
|
|
12
13
|
};
|
|
14
|
+
export type TabContentSnippet = {
|
|
15
|
+
props: Record<string, unknown>;
|
|
16
|
+
children: Snippet<[{
|
|
17
|
+
tab?: TabBond;
|
|
18
|
+
}]>;
|
|
19
|
+
};
|
|
13
20
|
export declare class TabsBond<T = unknown> extends Bond<TabsBondProps, TabsBondState<T>, TabElements> {
|
|
14
21
|
static CONTEXT_KEY: string;
|
|
15
22
|
constructor(s: TabsBondState<T>);
|
|
16
23
|
share(): this;
|
|
17
24
|
root(props?: Record<string, unknown>): {
|
|
18
25
|
id: string;
|
|
19
|
-
'aria-orientation':
|
|
26
|
+
'aria-orientation': "horizontal";
|
|
20
27
|
'data-kind': string;
|
|
21
28
|
};
|
|
22
29
|
header(props?: Record<string, unknown>): {
|
|
@@ -36,8 +43,14 @@ export declare class TabsBondState<T> extends BondState<TabsBondProps> {
|
|
|
36
43
|
#private;
|
|
37
44
|
constructor(props: () => TabsBondProps);
|
|
38
45
|
get selectedItem(): TabBond<T>;
|
|
46
|
+
get activeTabContent(): TabContentSnippet | undefined;
|
|
39
47
|
mountItem<I extends T>(id: string, item: TabBond<I>): () => void;
|
|
40
48
|
unmountItem(id: string): void;
|
|
41
49
|
select(id: string): void;
|
|
42
50
|
unselect(): void;
|
|
51
|
+
registerTabContent(id: string, props: Record<string, unknown>, children: Snippet<[{
|
|
52
|
+
tab?: TabBond;
|
|
53
|
+
}]>): void;
|
|
54
|
+
unregisterTabContent(id: string): void;
|
|
55
|
+
getTab(id: string): TabBond<T> | undefined;
|
|
43
56
|
}
|
|
@@ -62,6 +62,7 @@ export class TabsBond extends Bond {
|
|
|
62
62
|
}
|
|
63
63
|
export class TabsBondState extends BondState {
|
|
64
64
|
#items = new SvelteMap();
|
|
65
|
+
#tabContents = new SvelteMap();
|
|
65
66
|
#selectedItem = $derived(this.props?.value ? this.#items.get(this.props?.value) : undefined);
|
|
66
67
|
constructor(props) {
|
|
67
68
|
super(props);
|
|
@@ -69,6 +70,9 @@ export class TabsBondState extends BondState {
|
|
|
69
70
|
get selectedItem() {
|
|
70
71
|
return this.#selectedItem;
|
|
71
72
|
}
|
|
73
|
+
get activeTabContent() {
|
|
74
|
+
return this.props?.value ? this.#tabContents.get(this.props.value) : undefined;
|
|
75
|
+
}
|
|
72
76
|
mountItem(id, item) {
|
|
73
77
|
if (this.#items.size && !this.props.value) {
|
|
74
78
|
this.props.value = item.state.props.value;
|
|
@@ -85,4 +89,13 @@ export class TabsBondState extends BondState {
|
|
|
85
89
|
unselect() {
|
|
86
90
|
this.props.value = undefined;
|
|
87
91
|
}
|
|
92
|
+
registerTabContent(id, props, children) {
|
|
93
|
+
this.#tabContents.set(id, { props, children });
|
|
94
|
+
}
|
|
95
|
+
unregisterTabContent(id) {
|
|
96
|
+
this.#tabContents.delete(id);
|
|
97
|
+
}
|
|
98
|
+
getTab(id) {
|
|
99
|
+
return this.#items.get(id);
|
|
100
|
+
}
|
|
88
101
|
}
|
|
@@ -18,11 +18,8 @@ export declare class TabBond<T = unknown> extends Bond<TabBondProps<T>, TabBondS
|
|
|
18
18
|
mount(): (() => void) | undefined;
|
|
19
19
|
unmount(): void;
|
|
20
20
|
share(): this;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
'data-kind': string;
|
|
24
|
-
};
|
|
25
|
-
header(props?: Record<string, unknown>): {
|
|
21
|
+
header(): {
|
|
22
|
+
[x: symbol]: (node: HTMLElement) => (() => void) | undefined;
|
|
26
23
|
id: string;
|
|
27
24
|
role: string;
|
|
28
25
|
'aria-controls': string;
|
|
@@ -31,7 +28,8 @@ export declare class TabBond<T = unknown> extends Bond<TabBondProps<T>, TabBondS
|
|
|
31
28
|
'data-active': boolean;
|
|
32
29
|
'data-kind': string;
|
|
33
30
|
};
|
|
34
|
-
body(
|
|
31
|
+
body(): {
|
|
32
|
+
[x: symbol]: (node: HTMLElement) => void;
|
|
35
33
|
id: string;
|
|
36
34
|
role: string;
|
|
37
35
|
'aria-labeledby': string;
|
|
@@ -40,7 +38,8 @@ export declare class TabBond<T = unknown> extends Bond<TabBondProps<T>, TabBondS
|
|
|
40
38
|
'data-active': boolean;
|
|
41
39
|
'data-kind': string;
|
|
42
40
|
};
|
|
43
|
-
description(
|
|
41
|
+
description(): {
|
|
42
|
+
[x: symbol]: (node: HTMLElement) => void;
|
|
44
43
|
id: string;
|
|
45
44
|
'data-kind': string;
|
|
46
45
|
};
|
|
@@ -51,6 +50,7 @@ export declare class TabBondState<T> extends BondState<TabBondProps<T>> {
|
|
|
51
50
|
#private;
|
|
52
51
|
constructor(props: () => TabBondProps<T>);
|
|
53
52
|
get isActive(): boolean;
|
|
53
|
+
get isDisabled(): boolean;
|
|
54
54
|
select(): void;
|
|
55
55
|
unselect(): void;
|
|
56
56
|
}
|
|
@@ -5,7 +5,6 @@ import { getElementId } from '../../../utils/dom.svelte';
|
|
|
5
5
|
import { portal } from '../../../attachments/portal.svelte';
|
|
6
6
|
import { Bond, BondState } from '../../../shared/bond.svelte';
|
|
7
7
|
const TAB_ELEMENTS_KIND = {
|
|
8
|
-
root: 'tab-root',
|
|
9
8
|
header: 'tab-header',
|
|
10
9
|
body: 'tab-body',
|
|
11
10
|
description: 'tab-description'
|
|
@@ -29,13 +28,7 @@ export class TabBond extends Bond {
|
|
|
29
28
|
share() {
|
|
30
29
|
return TabBond.set(this);
|
|
31
30
|
}
|
|
32
|
-
|
|
33
|
-
return {
|
|
34
|
-
'data-active': this.state.isActive,
|
|
35
|
-
'data-kind': 'tab-root'
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
header(props) {
|
|
31
|
+
header() {
|
|
39
32
|
const id = getElementId(this.id, TAB_ELEMENTS_KIND.header);
|
|
40
33
|
const tabBodyId = getElementId(this.id, TAB_ELEMENTS_KIND.body);
|
|
41
34
|
return {
|
|
@@ -46,7 +39,6 @@ export class TabBond extends Bond {
|
|
|
46
39
|
'data-controler-id': this.#tabs?.id,
|
|
47
40
|
'data-active': this.state.isActive,
|
|
48
41
|
'data-kind': 'tab-header',
|
|
49
|
-
...props,
|
|
50
42
|
[createAttachmentKey()]: (node) => {
|
|
51
43
|
this.elements.header = node;
|
|
52
44
|
if (!this.#tabs?.elements.header) {
|
|
@@ -58,7 +50,7 @@ export class TabBond extends Bond {
|
|
|
58
50
|
}
|
|
59
51
|
};
|
|
60
52
|
}
|
|
61
|
-
body(
|
|
53
|
+
body() {
|
|
62
54
|
const id = getElementId(this.id, TAB_ELEMENTS_KIND.body);
|
|
63
55
|
const tabHeaderId = getElementId(this.id, TAB_ELEMENTS_KIND.header);
|
|
64
56
|
const descriptionId = getElementId(this.id, TAB_ELEMENTS_KIND.description);
|
|
@@ -71,18 +63,16 @@ export class TabBond extends Bond {
|
|
|
71
63
|
'aria-controledby': tabHeaderId,
|
|
72
64
|
'data-active': isActive,
|
|
73
65
|
'data-kind': 'tab-body',
|
|
74
|
-
...props,
|
|
75
66
|
[createAttachmentKey()]: (node) => {
|
|
76
67
|
this.elements.body = node;
|
|
77
68
|
}
|
|
78
69
|
};
|
|
79
70
|
}
|
|
80
|
-
description(
|
|
71
|
+
description() {
|
|
81
72
|
const id = getElementId(this.id, TAB_ELEMENTS_KIND.description);
|
|
82
73
|
return {
|
|
83
74
|
id,
|
|
84
75
|
'data-kind': 'tab-description',
|
|
85
|
-
...props,
|
|
86
76
|
[createAttachmentKey()]: (node) => {
|
|
87
77
|
this.elements.description = node;
|
|
88
78
|
}
|
|
@@ -96,14 +86,18 @@ export class TabBond extends Bond {
|
|
|
96
86
|
}
|
|
97
87
|
}
|
|
98
88
|
export class TabBondState extends BondState {
|
|
99
|
-
#
|
|
89
|
+
#tabsBond;
|
|
90
|
+
#tabsState = $derived(this.#tabsBond?.state);
|
|
100
91
|
constructor(props) {
|
|
101
92
|
super(props);
|
|
102
|
-
this.#
|
|
93
|
+
this.#tabsBond = TabsBond.get();
|
|
103
94
|
}
|
|
104
95
|
get isActive() {
|
|
105
96
|
return this.#tabsState?.props.value === this.props.value;
|
|
106
97
|
}
|
|
98
|
+
get isDisabled() {
|
|
99
|
+
return this.props.disabled ?? (this.#tabsBond?.elements?.header?.getAttribute?.('aria-disabled') === "true");
|
|
100
|
+
}
|
|
107
101
|
select() {
|
|
108
102
|
this.#tabsState?.select(this.props.value);
|
|
109
103
|
}
|
|
@@ -1,52 +1,43 @@
|
|
|
1
|
-
<script lang="ts" generics="E extends keyof HTMLElementTagNameMap = 'div', B extends Base = Base">
|
|
2
|
-
import {
|
|
3
|
-
import type { TabBodyProps } from '../types';
|
|
4
|
-
import { TabBond } from './bond.svelte';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
...
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
$effect(() => {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
ondestroy={ondestroy?.bind(bond.state)}
|
|
45
|
-
enter={enter?.bind(bond.state)}
|
|
46
|
-
exit={exit?.bind(bond.state)}
|
|
47
|
-
initial={initial?.bind(bond.state)}
|
|
48
|
-
animate={animate?.bind(bond.state)}
|
|
49
|
-
{...bodyProps}
|
|
50
|
-
>
|
|
51
|
-
{@render children?.({ tab: bond })}
|
|
52
|
-
</HtmlAtom>
|
|
1
|
+
<script lang="ts" generics="E extends keyof HTMLElementTagNameMap = 'div', B extends Base = Base">
|
|
2
|
+
import { type Base } from '../../atom';
|
|
3
|
+
import type { TabBodyProps } from '../types';
|
|
4
|
+
import { TabBond } from './bond.svelte';
|
|
5
|
+
import { TabsBond } from '../bond.svelte';
|
|
6
|
+
import { Stack } from '../../stack';
|
|
7
|
+
|
|
8
|
+
const tabBond = TabBond.get();
|
|
9
|
+
const tabsBond = TabsBond.get();
|
|
10
|
+
|
|
11
|
+
if (!tabBond) {
|
|
12
|
+
throw new Error('TabBody must be used within a Tab');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let {
|
|
16
|
+
class: klass = '',
|
|
17
|
+
base= Stack.Item,
|
|
18
|
+
children,
|
|
19
|
+
preset = 'tab.body' as const,
|
|
20
|
+
...restProps
|
|
21
|
+
}: TabBodyProps<E, B> = $props();
|
|
22
|
+
|
|
23
|
+
const contentProps = $derived({
|
|
24
|
+
class: klass,
|
|
25
|
+
preset,
|
|
26
|
+
base,
|
|
27
|
+
...restProps
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// Register content snippet with props and children with tabs on mount
|
|
31
|
+
$effect(() => {
|
|
32
|
+
if (tabBond && tabsBond && children) {
|
|
33
|
+
const id = tabBond.state.props.value;
|
|
34
|
+
tabsBond.state.registerTabContent(id, contentProps, children);
|
|
35
|
+
|
|
36
|
+
return () => {
|
|
37
|
+
tabsBond.state.unregisterTabContent(id);
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
</script>
|
|
42
|
+
|
|
43
|
+
<!-- Content is teleported to Tabs.Content, so we don't render anything here -->
|