@x33025/sveltely 0.1.4 → 0.1.7
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/Library/AnimatedNumber/AnimatedNumber.demo.svelte +7 -1
- package/dist/components/Library/AsyncButton/AsyncButton.svelte +37 -88
- package/dist/components/Library/Button/Button.demo.svelte +11 -3
- package/dist/components/Library/Button/Button.svelte +24 -66
- package/dist/components/Library/Button/Button.svelte.d.ts +2 -9
- package/dist/components/Library/Dropdown/Dropdown.demo.svelte +14 -22
- package/dist/components/Library/Dropdown/Dropdown.svelte +24 -8
- package/dist/components/Library/Dropdown/Trigger.svelte +71 -0
- package/dist/components/Library/Dropdown/Trigger.svelte.d.ts +8 -0
- package/dist/components/Library/Dropdown/context.d.ts +2 -0
- package/dist/components/Library/Dropdown/context.js +6 -0
- package/dist/components/Library/Dropdown/index.d.ts +2 -0
- package/dist/components/Library/Dropdown/index.js +2 -0
- package/dist/components/Library/Label/Label.svelte +0 -2
- package/dist/components/Library/ScrollView/ScrollView.svelte +18 -27
- package/dist/components/Library/ScrollView/ScrollView.svelte.d.ts +2 -1
- package/dist/components/Local/ComponentGrid.svelte +28 -17
- package/dist/components/Local/HeroCard.svelte +9 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/style/layout.d.ts +0 -10
- package/dist/style/layout.js +0 -11
- package/dist/style.css +0 -15
- package/package.json +1 -1
|
@@ -20,5 +20,11 @@
|
|
|
20
20
|
<div class="text-3xl font-semibold text-[var(--sveltely-primary-color)]">
|
|
21
21
|
<AnimatedNumber {value} />
|
|
22
22
|
</div>
|
|
23
|
-
<Button
|
|
23
|
+
<Button
|
|
24
|
+
label="Change number"
|
|
25
|
+
background="var(--sveltely-active-color)"
|
|
26
|
+
borderColor="var(--sveltely-active-color)"
|
|
27
|
+
color="var(--sveltely-background-color)"
|
|
28
|
+
onclick={bump}
|
|
29
|
+
/>
|
|
24
30
|
</div>
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
import type { Component } from 'svelte';
|
|
4
4
|
import type { HTMLButtonAttributes } from 'svelte/elements';
|
|
5
5
|
import { tooltip } from '../../../actions/tooltip';
|
|
6
|
+
import Button from '../Button';
|
|
6
7
|
import Spinner from '../Spinner';
|
|
7
|
-
import { extractStyleProps,
|
|
8
|
+
import { extractStyleProps, type StyleProps } from '../../../style/surface';
|
|
8
9
|
|
|
9
10
|
type Props = {
|
|
10
11
|
icon?: Component<{ class?: string; size?: number | string }>;
|
|
@@ -45,6 +46,12 @@
|
|
|
45
46
|
const isControlledLoading = $derived(loading !== undefined);
|
|
46
47
|
const effectiveLoading = $derived(isControlledLoading ? loading : internalLoading);
|
|
47
48
|
const effectiveDisabled = $derived(disabled || (disableWhileLoading && effectiveLoading));
|
|
49
|
+
const buttonStyleProps = $derived({
|
|
50
|
+
...styleProps,
|
|
51
|
+
background: styleProps.background ?? 'var(--sveltely-active-color)',
|
|
52
|
+
borderColor: styleProps.borderColor ?? 'var(--sveltely-active-color)',
|
|
53
|
+
color: styleProps.color ?? 'var(--sveltely-background-color)'
|
|
54
|
+
});
|
|
48
55
|
|
|
49
56
|
const handleClick = async () => {
|
|
50
57
|
if (effectiveDisabled) return;
|
|
@@ -78,8 +85,6 @@
|
|
|
78
85
|
const toRem = (value: number | string | undefined) =>
|
|
79
86
|
value === undefined ? undefined : typeof value === 'number' ? `${value}rem` : value;
|
|
80
87
|
|
|
81
|
-
const triggerStyle = $derived.by(() => surfaceStyle(styleProps, 'async-button'));
|
|
82
|
-
|
|
83
88
|
const iconStyle = $derived.by(() => {
|
|
84
89
|
const declarations: string[] = [];
|
|
85
90
|
|
|
@@ -101,103 +106,47 @@
|
|
|
101
106
|
});
|
|
102
107
|
</script>
|
|
103
108
|
|
|
104
|
-
<button
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
<
|
|
109
|
+
<span use:tooltip={{ label: errorLabel ?? '', disabled: !errorLabel }} class="async-button">
|
|
110
|
+
<Button
|
|
111
|
+
{type}
|
|
112
|
+
{...buttonStyleProps}
|
|
113
|
+
disabled={effectiveDisabled}
|
|
114
|
+
aria-busy={effectiveLoading}
|
|
115
|
+
aria-label={variant === 'iconOnly' ? label : undefined}
|
|
116
|
+
data-error={error ? 'true' : 'false'}
|
|
117
|
+
{...props}
|
|
118
|
+
onclick={handleClick}
|
|
119
|
+
>
|
|
120
|
+
<span class="async-button-icon-frame inline-grid shrink-0 place-items-center" style={iconStyle}>
|
|
121
|
+
{#if effectiveLoading}
|
|
122
|
+
<Spinner size={iconSize !== undefined ? toRem(iconSize) : 'var(--button-icon-size)'} />
|
|
123
|
+
{:else if error}
|
|
124
|
+
<CircleAlertIcon class="async-button-icon text-red-600" />
|
|
125
|
+
{:else if icon}
|
|
126
|
+
{@const Icon = icon}
|
|
127
|
+
<Icon class="async-button-icon" />
|
|
128
|
+
{/if}
|
|
129
|
+
</span>
|
|
130
|
+
{#if variant === 'iconAndLabel'}
|
|
131
|
+
<span class="async-button-text" style={labelStyle}>{label}</span>
|
|
127
132
|
{/if}
|
|
128
|
-
</
|
|
129
|
-
|
|
130
|
-
<span class="async-button-text" style={labelStyle}>{label}</span>
|
|
131
|
-
{/if}
|
|
132
|
-
</button>
|
|
133
|
+
</Button>
|
|
134
|
+
</span>
|
|
133
135
|
|
|
134
136
|
<style>
|
|
135
137
|
.async-button {
|
|
136
|
-
|
|
137
|
-
--async-button-scale: calc(var(--async-button-font-size) / 1rem);
|
|
138
|
-
--async-button-icon-size: calc(var(--async-button-font-size) * 1.143);
|
|
139
|
-
border: 1px solid var(--async-button-border-color, var(--sveltely-active-color));
|
|
140
|
-
border-radius: var(--async-button-border-radius, var(--sveltely-border-radius));
|
|
141
|
-
background: var(--async-button-background, var(--sveltely-active-color));
|
|
142
|
-
color: var(--async-button-color, var(--sveltely-background-color));
|
|
143
|
-
gap: var(--async-button-gap, var(--sveltely-gap));
|
|
144
|
-
font-size: var(--async-button-font-size);
|
|
145
|
-
line-height: 1.25;
|
|
146
|
-
padding: var(
|
|
147
|
-
--async-button-padding-y,
|
|
148
|
-
calc(var(--sveltely-padding-y) * 0.67 * var(--async-button-scale))
|
|
149
|
-
)
|
|
150
|
-
var(--async-button-padding-x, calc(var(--sveltely-padding-x) * var(--async-button-scale)));
|
|
151
|
-
transition:
|
|
152
|
-
color 150ms,
|
|
153
|
-
border-color 150ms,
|
|
154
|
-
background-color 150ms;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
.async-button:hover {
|
|
158
|
-
background: var(--async-button-hover-background, var(--sveltely-active-hover-color));
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
.async-button-icon-only {
|
|
162
|
-
padding: var(
|
|
163
|
-
--async-button-padding-y,
|
|
164
|
-
calc(var(--sveltely-padding-y) * 0.67 * var(--async-button-scale))
|
|
165
|
-
)
|
|
166
|
-
var(
|
|
167
|
-
--async-button-padding-x,
|
|
168
|
-
calc(var(--sveltely-padding-x) * 0.67 * var(--async-button-scale))
|
|
169
|
-
);
|
|
170
|
-
min-width: calc(
|
|
171
|
-
(
|
|
172
|
-
var(
|
|
173
|
-
--async-button-padding-x,
|
|
174
|
-
calc(var(--sveltely-padding-x) * 0.67 * var(--async-button-scale))
|
|
175
|
-
) *
|
|
176
|
-
2
|
|
177
|
-
) +
|
|
178
|
-
1rem
|
|
179
|
-
);
|
|
180
|
-
min-height: calc(
|
|
181
|
-
(
|
|
182
|
-
var(
|
|
183
|
-
--async-button-padding-y,
|
|
184
|
-
calc(var(--sveltely-padding-y) * 0.67 * var(--async-button-scale))
|
|
185
|
-
) *
|
|
186
|
-
2
|
|
187
|
-
) +
|
|
188
|
-
1rem
|
|
189
|
-
);
|
|
138
|
+
display: inline-flex;
|
|
190
139
|
}
|
|
191
140
|
|
|
192
141
|
.async-button-icon-frame {
|
|
193
142
|
display: inline-flex;
|
|
194
143
|
align-items: center;
|
|
195
144
|
justify-content: center;
|
|
196
|
-
width: var(--
|
|
197
|
-
height: var(--
|
|
145
|
+
width: var(--button-icon-size);
|
|
146
|
+
height: var(--button-icon-size);
|
|
198
147
|
}
|
|
199
148
|
|
|
200
|
-
.async-button-icon {
|
|
149
|
+
:global(.async-button-icon) {
|
|
201
150
|
width: 100%;
|
|
202
151
|
height: 100%;
|
|
203
152
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script module lang="ts">
|
|
2
2
|
export const demo = {
|
|
3
3
|
name: 'Button',
|
|
4
|
-
description: 'Token-aware button primitive with
|
|
4
|
+
description: 'Token-aware button primitive with composable content.',
|
|
5
5
|
columnSpan: 2
|
|
6
6
|
};
|
|
7
7
|
</script>
|
|
@@ -14,6 +14,14 @@
|
|
|
14
14
|
|
|
15
15
|
<HStack align="center" gap={0.75}>
|
|
16
16
|
<Button label="Default" />
|
|
17
|
-
<Button
|
|
18
|
-
|
|
17
|
+
<Button
|
|
18
|
+
label="Solid"
|
|
19
|
+
background="var(--sveltely-active-color)"
|
|
20
|
+
borderColor="var(--sveltely-active-color)"
|
|
21
|
+
color="var(--sveltely-background-color)"
|
|
22
|
+
/>
|
|
23
|
+
<Button>
|
|
24
|
+
<SaveIcon />
|
|
25
|
+
<span>With icon</span>
|
|
26
|
+
</Button>
|
|
19
27
|
</HStack>
|
|
@@ -1,48 +1,33 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import type {
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
3
|
import type { HTMLButtonAttributes } from 'svelte/elements';
|
|
4
4
|
import { extractStyleProps, surfaceStyle, type StyleProps } from '../../../style/surface';
|
|
5
5
|
|
|
6
6
|
type Props = {
|
|
7
7
|
children?: Snippet;
|
|
8
8
|
label?: string;
|
|
9
|
-
icon?: Component<{ class?: string; size?: number | string }>;
|
|
10
|
-
iconSize?: number | string;
|
|
11
|
-
iconColor?: string;
|
|
12
|
-
variant?: 'default' | 'solid' | 'ghost';
|
|
13
9
|
} & StyleProps &
|
|
14
10
|
Omit<HTMLButtonAttributes, 'children' | 'class' | 'style'>;
|
|
15
11
|
|
|
16
|
-
let {
|
|
17
|
-
children,
|
|
18
|
-
label,
|
|
19
|
-
icon,
|
|
20
|
-
iconSize,
|
|
21
|
-
iconColor,
|
|
22
|
-
variant = 'default',
|
|
23
|
-
disabled = false,
|
|
24
|
-
type = 'button',
|
|
25
|
-
...restProps
|
|
26
|
-
}: Props = $props();
|
|
12
|
+
let { children, label, disabled = false, type = 'button', ...restProps }: Props = $props();
|
|
27
13
|
|
|
28
14
|
const extractedStyleProps = $derived.by(() => extractStyleProps(restProps));
|
|
29
15
|
const styleProps = $derived(extractedStyleProps.styleProps);
|
|
30
16
|
const props = $derived(extractedStyleProps.restProps);
|
|
31
17
|
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if (
|
|
39
|
-
declarations.push(
|
|
18
|
+
const rootStyle = $derived.by(() => {
|
|
19
|
+
const { background, borderColor, color, ...surfaceProps } = styleProps;
|
|
20
|
+
const declarations = [surfaceStyle(surfaceProps, 'button')];
|
|
21
|
+
if (background !== undefined) {
|
|
22
|
+
declarations.push(`--button-background: ${background};`);
|
|
23
|
+
}
|
|
24
|
+
if (borderColor !== undefined) {
|
|
25
|
+
declarations.push(`--button-border-color: ${borderColor};`);
|
|
40
26
|
}
|
|
41
|
-
if (
|
|
42
|
-
|
|
43
|
-
declarations.push(`width: ${size};`, `height: ${size};`);
|
|
27
|
+
if (color !== undefined) {
|
|
28
|
+
declarations.push(`--button-color: ${color};`);
|
|
44
29
|
}
|
|
45
|
-
return declarations.join(' ');
|
|
30
|
+
return declarations.filter(Boolean).join(' ');
|
|
46
31
|
});
|
|
47
32
|
const iconOnly = $derived(!children && !label);
|
|
48
33
|
</script>
|
|
@@ -52,18 +37,10 @@
|
|
|
52
37
|
class="button inline-flex items-center justify-center disabled:cursor-not-allowed disabled:opacity-50 {iconOnly
|
|
53
38
|
? 'button-icon-only'
|
|
54
39
|
: ''}"
|
|
55
|
-
data-variant={variant}
|
|
56
40
|
style={rootStyle}
|
|
57
41
|
{disabled}
|
|
58
42
|
{...props}
|
|
59
43
|
>
|
|
60
|
-
{#if icon}
|
|
61
|
-
{@const Icon = icon}
|
|
62
|
-
<span class="button-icon-frame inline-grid shrink-0 place-items-center" style={iconStyle}>
|
|
63
|
-
<Icon class="button-icon" />
|
|
64
|
-
</span>
|
|
65
|
-
{/if}
|
|
66
|
-
|
|
67
44
|
{#if children}
|
|
68
45
|
{@render children()}
|
|
69
46
|
{:else if label}
|
|
@@ -91,27 +68,15 @@
|
|
|
91
68
|
background-color 150ms;
|
|
92
69
|
}
|
|
93
70
|
|
|
94
|
-
.button
|
|
95
|
-
background: var(
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
.button[data-variant='solid']:hover {
|
|
105
|
-
background: var(--button-solid-hover-background, var(--sveltely-active-hover-color));
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
.button[data-variant='ghost'] {
|
|
109
|
-
border-color: transparent;
|
|
110
|
-
background: transparent;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
.button[data-variant='ghost']:hover {
|
|
114
|
-
background: var(--button-ghost-hover-background, var(--sveltely-hover-color));
|
|
71
|
+
.button:hover {
|
|
72
|
+
background: var(
|
|
73
|
+
--button-hover-background,
|
|
74
|
+
color-mix(
|
|
75
|
+
in oklab,
|
|
76
|
+
var(--button-background, var(--sveltely-background-color)) 88%,
|
|
77
|
+
var(--button-color, var(--sveltely-primary-color))
|
|
78
|
+
)
|
|
79
|
+
);
|
|
115
80
|
}
|
|
116
81
|
|
|
117
82
|
.button-icon-only {
|
|
@@ -127,16 +92,9 @@
|
|
|
127
92
|
);
|
|
128
93
|
}
|
|
129
94
|
|
|
130
|
-
.button
|
|
131
|
-
display: inline-flex;
|
|
132
|
-
align-items: center;
|
|
133
|
-
justify-content: center;
|
|
95
|
+
.button :global(svg) {
|
|
134
96
|
width: var(--button-icon-size);
|
|
135
97
|
height: var(--button-icon-size);
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
.button-icon {
|
|
139
|
-
width: 100%;
|
|
140
|
-
height: 100%;
|
|
98
|
+
flex-shrink: 0;
|
|
141
99
|
}
|
|
142
100
|
</style>
|
|
@@ -1,17 +1,10 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
2
|
import type { HTMLButtonAttributes } from 'svelte/elements';
|
|
3
3
|
import { type StyleProps } from '../../../style/surface';
|
|
4
4
|
type Props = {
|
|
5
5
|
children?: Snippet;
|
|
6
6
|
label?: string;
|
|
7
|
-
icon?: Component<{
|
|
8
|
-
class?: string;
|
|
9
|
-
size?: number | string;
|
|
10
|
-
}>;
|
|
11
|
-
iconSize?: number | string;
|
|
12
|
-
iconColor?: string;
|
|
13
|
-
variant?: 'default' | 'solid' | 'ghost';
|
|
14
7
|
} & StyleProps & Omit<HTMLButtonAttributes, 'children' | 'class' | 'style'>;
|
|
15
|
-
declare const Button: Component<Props, {}, "">;
|
|
8
|
+
declare const Button: import("svelte").Component<Props, {}, "">;
|
|
16
9
|
type Button = ReturnType<typeof Button>;
|
|
17
10
|
export default Button;
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
</script>
|
|
8
8
|
|
|
9
9
|
<script lang="ts">
|
|
10
|
-
import { CheckIcon,
|
|
10
|
+
import { CheckIcon, PlusIcon, UserRoundXIcon } from '@lucide/svelte';
|
|
11
11
|
import SearchField from '../SearchField';
|
|
12
12
|
import ScrollView, { type ScrollGeometry } from '../ScrollView';
|
|
13
13
|
import Dropdown from './index';
|
|
@@ -117,31 +117,23 @@
|
|
|
117
117
|
selectedLabel={selectedWebsite?.name ?? null}
|
|
118
118
|
showCheck={false}
|
|
119
119
|
>
|
|
120
|
-
{#snippet trigger(
|
|
121
|
-
<
|
|
122
|
-
use:dropdown.useTrigger
|
|
123
|
-
type="button"
|
|
124
|
-
class="hstack w-full items-center gap-2 rounded-full border px-3 py-2 pr-2 text-left text-sm"
|
|
125
|
-
style="border-color: var(--sveltely-border-color); background: var(--sveltely-background-color);"
|
|
126
|
-
aria-expanded={dropdown.open}
|
|
127
|
-
aria-haspopup="dialog"
|
|
128
|
-
onclick={dropdown.toggle}
|
|
129
|
-
>
|
|
120
|
+
{#snippet trigger()}
|
|
121
|
+
<Dropdown.Trigger>
|
|
130
122
|
{#if selectedWebsite}
|
|
131
|
-
<span class="min-w-0
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
{#if selectedWebsite.domain}
|
|
135
|
-
<span class="max-w-32 truncate text-xs text-[var(--sveltely-secondary-color)]"
|
|
136
|
-
>{selectedWebsite.domain}</span
|
|
123
|
+
<span class="hstack min-w-0 items-center gap-2">
|
|
124
|
+
<span class="truncate text-[var(--sveltely-primary-color)]"
|
|
125
|
+
>{selectedWebsite.name}</span
|
|
137
126
|
>
|
|
138
|
-
|
|
127
|
+
{#if selectedWebsite.domain}
|
|
128
|
+
<span class="truncate text-xs text-[var(--sveltely-secondary-color)]">
|
|
129
|
+
{selectedWebsite.domain}
|
|
130
|
+
</span>
|
|
131
|
+
{/if}
|
|
132
|
+
</span>
|
|
139
133
|
{:else}
|
|
140
|
-
<span class="
|
|
141
|
-
>
|
|
134
|
+
<span class="text-[var(--sveltely-secondary-color)]">Select website</span>
|
|
142
135
|
{/if}
|
|
143
|
-
|
|
144
|
-
</button>
|
|
136
|
+
</Dropdown.Trigger>
|
|
145
137
|
{/snippet}
|
|
146
138
|
|
|
147
139
|
<SearchField bind:value={websiteQuery} placeholder="Search websites..." />
|
|
@@ -4,7 +4,11 @@
|
|
|
4
4
|
import Floating from '../Floating/Floating.svelte';
|
|
5
5
|
import { surfaceStyle, type StyleProps } from '../../../style/surface';
|
|
6
6
|
import type { Anchor } from '../../../utils/positioning';
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
setDropdownContext,
|
|
9
|
+
setDropdownTriggerContext,
|
|
10
|
+
type DropdownTriggerState
|
|
11
|
+
} from './context';
|
|
8
12
|
|
|
9
13
|
type Props = {
|
|
10
14
|
value?: TValue | null;
|
|
@@ -80,7 +84,7 @@
|
|
|
80
84
|
>
|
|
81
85
|
{#snippet trigger(floating)}
|
|
82
86
|
{#if triggerContent}
|
|
83
|
-
{@
|
|
87
|
+
{@const triggerState = setDropdownTriggerContext({
|
|
84
88
|
useTrigger: floating.useTrigger,
|
|
85
89
|
open: floating.open,
|
|
86
90
|
value,
|
|
@@ -91,20 +95,32 @@
|
|
|
91
95
|
openPanel: floating.openPanel,
|
|
92
96
|
closePanel: floating.closePanel
|
|
93
97
|
})}
|
|
98
|
+
{@render triggerContent(triggerState)}
|
|
94
99
|
{:else}
|
|
100
|
+
{@const triggerState = {
|
|
101
|
+
useTrigger: floating.useTrigger,
|
|
102
|
+
open: floating.open,
|
|
103
|
+
value,
|
|
104
|
+
selectedLabel: triggerText,
|
|
105
|
+
placeholder,
|
|
106
|
+
disabled,
|
|
107
|
+
toggle: floating.toggle,
|
|
108
|
+
openPanel: floating.openPanel,
|
|
109
|
+
closePanel: floating.closePanel
|
|
110
|
+
}}
|
|
95
111
|
<button
|
|
96
|
-
use:
|
|
112
|
+
use:triggerState.useTrigger
|
|
97
113
|
type="button"
|
|
98
114
|
class="dropdown-trigger justify-between"
|
|
99
115
|
aria-label={label}
|
|
100
|
-
aria-disabled={disabled}
|
|
101
|
-
{disabled}
|
|
116
|
+
aria-disabled={triggerState.disabled}
|
|
117
|
+
disabled={triggerState.disabled}
|
|
102
118
|
style={dropdownStyle}
|
|
103
|
-
aria-expanded={
|
|
119
|
+
aria-expanded={triggerState.open}
|
|
104
120
|
aria-haspopup="dialog"
|
|
105
|
-
onclick={
|
|
121
|
+
onclick={triggerState.toggle}
|
|
106
122
|
>
|
|
107
|
-
<span class="dropdown-trigger-label">{
|
|
123
|
+
<span class="dropdown-trigger-label">{triggerState.selectedLabel}</span>
|
|
108
124
|
<ChevronDownIcon class="size-4 text-[var(--sveltely-secondary-color)]" />
|
|
109
125
|
</button>
|
|
110
126
|
{/if}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { ChevronDownIcon } from '@lucide/svelte';
|
|
3
|
+
import type { Snippet } from 'svelte';
|
|
4
|
+
import { getDropdownTriggerContext, type DropdownTriggerState } from './context';
|
|
5
|
+
|
|
6
|
+
type Props = {
|
|
7
|
+
children?: Snippet<[DropdownTriggerState]>;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
let { children }: Props = $props();
|
|
11
|
+
|
|
12
|
+
const dropdown = getDropdownTriggerContext();
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<button
|
|
16
|
+
use:dropdown.useTrigger
|
|
17
|
+
type="button"
|
|
18
|
+
class="dropdown-trigger justify-between"
|
|
19
|
+
aria-disabled={dropdown.disabled}
|
|
20
|
+
disabled={dropdown.disabled}
|
|
21
|
+
aria-expanded={dropdown.open}
|
|
22
|
+
aria-haspopup="dialog"
|
|
23
|
+
onclick={dropdown.toggle}
|
|
24
|
+
>
|
|
25
|
+
<span class="dropdown-trigger-label">
|
|
26
|
+
{#if children}
|
|
27
|
+
{@render children(dropdown)}
|
|
28
|
+
{:else}
|
|
29
|
+
{dropdown.selectedLabel}
|
|
30
|
+
{/if}
|
|
31
|
+
</span>
|
|
32
|
+
<ChevronDownIcon class="size-4 text-[var(--sveltely-secondary-color)]" />
|
|
33
|
+
</button>
|
|
34
|
+
|
|
35
|
+
<style>
|
|
36
|
+
.dropdown-trigger {
|
|
37
|
+
display: inline-flex;
|
|
38
|
+
min-width: 8rem;
|
|
39
|
+
max-width: 100%;
|
|
40
|
+
align-items: center;
|
|
41
|
+
border: 1px solid var(--sveltely-border-color);
|
|
42
|
+
border-radius: var(--sveltely-border-radius);
|
|
43
|
+
background: var(--sveltely-background-color);
|
|
44
|
+
color: var(--sveltely-primary-color);
|
|
45
|
+
gap: var(--sveltely-gap);
|
|
46
|
+
padding: calc(var(--sveltely-padding-y) * 0.67) var(--sveltely-padding-x);
|
|
47
|
+
font-size: 0.875rem;
|
|
48
|
+
line-height: 1.25rem;
|
|
49
|
+
text-align: left;
|
|
50
|
+
transition:
|
|
51
|
+
color 150ms,
|
|
52
|
+
border-color 150ms,
|
|
53
|
+
background-color 150ms;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.dropdown-trigger-label {
|
|
57
|
+
min-width: 0;
|
|
58
|
+
flex: 1 1 auto;
|
|
59
|
+
overflow: hidden;
|
|
60
|
+
text-overflow: ellipsis;
|
|
61
|
+
white-space: nowrap;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.dropdown-trigger :global(svg) {
|
|
65
|
+
flex: 0 0 auto;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.dropdown-trigger:hover {
|
|
69
|
+
background: var(--sveltely-hover-color);
|
|
70
|
+
}
|
|
71
|
+
</style>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import { type DropdownTriggerState } from './context';
|
|
3
|
+
type Props = {
|
|
4
|
+
children?: Snippet<[DropdownTriggerState]>;
|
|
5
|
+
};
|
|
6
|
+
declare const Trigger: import("svelte").Component<Props, {}, "">;
|
|
7
|
+
type Trigger = ReturnType<typeof Trigger>;
|
|
8
|
+
export default Trigger;
|
|
@@ -32,3 +32,5 @@ export type DropdownContext<TValue = DropdownValue> = {
|
|
|
32
32
|
};
|
|
33
33
|
export declare const setDropdownContext: <TValue>(context: DropdownContext<TValue>) => void;
|
|
34
34
|
export declare const getDropdownContext: <TValue>() => DropdownContext<TValue>;
|
|
35
|
+
export declare const setDropdownTriggerContext: <TValue>(context: DropdownTriggerState<TValue>) => DropdownTriggerState<TValue>;
|
|
36
|
+
export declare const getDropdownTriggerContext: <TValue>() => DropdownTriggerState<TValue>;
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { getContext, setContext } from 'svelte';
|
|
2
2
|
const dropdownContextKey = Symbol('dropdown');
|
|
3
|
+
const dropdownTriggerContextKey = Symbol('dropdown-trigger');
|
|
3
4
|
export const setDropdownContext = (context) => {
|
|
4
5
|
setContext(dropdownContextKey, context);
|
|
5
6
|
};
|
|
6
7
|
export const getDropdownContext = () => getContext(dropdownContextKey);
|
|
8
|
+
export const setDropdownTriggerContext = (context) => {
|
|
9
|
+
setContext(dropdownTriggerContextKey, context);
|
|
10
|
+
return context;
|
|
11
|
+
};
|
|
12
|
+
export const getDropdownTriggerContext = () => getContext(dropdownTriggerContextKey);
|
|
@@ -3,11 +3,13 @@ import Action from './Action.svelte';
|
|
|
3
3
|
import Divider from './Divider.svelte';
|
|
4
4
|
import Item from './Item.svelte';
|
|
5
5
|
import Section from './Section.svelte';
|
|
6
|
+
import Trigger from './Trigger.svelte';
|
|
6
7
|
declare const Dropdown: typeof DropdownComponent & {
|
|
7
8
|
Action: typeof Action;
|
|
8
9
|
Divider: typeof Divider;
|
|
9
10
|
Item: typeof Item;
|
|
10
11
|
Section: typeof Section;
|
|
12
|
+
Trigger: typeof Trigger;
|
|
11
13
|
};
|
|
12
14
|
export default Dropdown;
|
|
13
15
|
export type { DropdownActionState, DropdownItemState, DropdownTriggerState } from './context';
|
|
@@ -3,9 +3,11 @@ import Action from './Action.svelte';
|
|
|
3
3
|
import Divider from './Divider.svelte';
|
|
4
4
|
import Item from './Item.svelte';
|
|
5
5
|
import Section from './Section.svelte';
|
|
6
|
+
import Trigger from './Trigger.svelte';
|
|
6
7
|
const Dropdown = DropdownComponent;
|
|
7
8
|
Dropdown.Action = Action;
|
|
8
9
|
Dropdown.Divider = Divider;
|
|
9
10
|
Dropdown.Item = Item;
|
|
10
11
|
Dropdown.Section = Section;
|
|
12
|
+
Dropdown.Trigger = Trigger;
|
|
11
13
|
export default Dropdown;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { tick } from 'svelte';
|
|
3
3
|
import type { Snippet } from 'svelte';
|
|
4
|
-
import {
|
|
4
|
+
import { extractLayoutProps, layoutStyle, type LayoutProps } from '../../../style/layout';
|
|
5
|
+
import { extractStyleProps, surfaceStyle, type StyleProps } from '../../../style/surface';
|
|
5
6
|
import type { ScrollAxis } from '../../../style/scroll';
|
|
6
7
|
|
|
7
8
|
export type ScrollGeometry = {
|
|
@@ -38,7 +39,8 @@
|
|
|
38
39
|
onScroll?: (geometry: ScrollGeometry) => void;
|
|
39
40
|
scrollGradient?: boolean;
|
|
40
41
|
scrollGradientSize?: number | string;
|
|
41
|
-
} &
|
|
42
|
+
} & LayoutProps &
|
|
43
|
+
StyleProps;
|
|
42
44
|
|
|
43
45
|
let {
|
|
44
46
|
children,
|
|
@@ -48,36 +50,25 @@
|
|
|
48
50
|
onScroll,
|
|
49
51
|
scrollGradient = true,
|
|
50
52
|
scrollGradientSize = '1rem',
|
|
51
|
-
|
|
52
|
-
paddingX,
|
|
53
|
-
paddingY,
|
|
54
|
-
gap,
|
|
55
|
-
borderRadius,
|
|
56
|
-
inset,
|
|
57
|
-
background,
|
|
58
|
-
borderColor,
|
|
59
|
-
color
|
|
53
|
+
...restProps
|
|
60
54
|
}: Props = $props();
|
|
61
55
|
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
borderRadius,
|
|
68
|
-
inset,
|
|
69
|
-
background,
|
|
70
|
-
borderColor,
|
|
71
|
-
color
|
|
72
|
-
});
|
|
56
|
+
const extractedLayoutProps = $derived.by(() => extractLayoutProps(restProps));
|
|
57
|
+
const layoutProps = $derived(extractedLayoutProps.layoutProps);
|
|
58
|
+
const afterLayoutProps = $derived(extractedLayoutProps.restProps);
|
|
59
|
+
const extractedStyleProps = $derived.by(() => extractStyleProps(afterLayoutProps));
|
|
60
|
+
const styleProps = $derived(extractedStyleProps.styleProps);
|
|
73
61
|
const viewportStyle = $derived.by(() => surfaceStyle(styleProps, 'scroll-view'));
|
|
74
62
|
const contentStyle = $derived.by(() => surfaceStyle(contentStyles, 'scroll-view-content'));
|
|
75
|
-
let canScrollToTop = $state(false);
|
|
76
|
-
let canScrollToBottom = $state(false);
|
|
77
|
-
const scrollGradientEnabled = $derived(scrollGradient && axis !== 'horizontal');
|
|
78
63
|
const scrollGradientStyle = $derived(
|
|
79
64
|
`--scroll-view-gradient-size: ${typeof scrollGradientSize === 'number' ? `${scrollGradientSize}rem` : scrollGradientSize};`
|
|
80
65
|
);
|
|
66
|
+
const rootStyle = $derived.by(() =>
|
|
67
|
+
[layoutStyle(layoutProps), viewportStyle, scrollGradientStyle].filter(Boolean).join(' ')
|
|
68
|
+
);
|
|
69
|
+
let canScrollToTop = $state(false);
|
|
70
|
+
let canScrollToBottom = $state(false);
|
|
71
|
+
const scrollGradientEnabled = $derived(scrollGradient && axis !== 'horizontal');
|
|
81
72
|
|
|
82
73
|
function syncScrollGradient(geometry: ScrollGeometry) {
|
|
83
74
|
if (!scrollGradientEnabled) return;
|
|
@@ -146,10 +137,10 @@
|
|
|
146
137
|
class:scroll-view-vertical={axis === 'vertical'}
|
|
147
138
|
class:scroll-view-horizontal={axis === 'horizontal'}
|
|
148
139
|
class:scroll-view-both={axis === 'both'}
|
|
149
|
-
class:scroll-view-gradient={scrollGradientEnabled}
|
|
140
|
+
class:scroll-view-has-gradient={scrollGradientEnabled}
|
|
150
141
|
class:scroll-view-can-scroll-up={canScrollToTop}
|
|
151
142
|
class:scroll-view-can-scroll-down={canScrollToBottom}
|
|
152
|
-
style={
|
|
143
|
+
style={rootStyle}
|
|
153
144
|
onscroll={handleScroll}
|
|
154
145
|
onwheel={handleWheel}
|
|
155
146
|
>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Snippet } from 'svelte';
|
|
2
|
+
import { type LayoutProps } from '../../../style/layout';
|
|
2
3
|
import { type StyleProps } from '../../../style/surface';
|
|
3
4
|
import type { ScrollAxis } from '../../../style/scroll';
|
|
4
5
|
export type ScrollGeometry = {
|
|
@@ -34,7 +35,7 @@ type Props = {
|
|
|
34
35
|
onScroll?: (geometry: ScrollGeometry) => void;
|
|
35
36
|
scrollGradient?: boolean;
|
|
36
37
|
scrollGradientSize?: number | string;
|
|
37
|
-
} & StyleProps;
|
|
38
|
+
} & LayoutProps & StyleProps;
|
|
38
39
|
declare const ScrollView: import("svelte").Component<Props, {}, "viewport">;
|
|
39
40
|
type ScrollView = ReturnType<typeof ScrollView>;
|
|
40
41
|
export default ScrollView;
|
|
@@ -19,20 +19,31 @@
|
|
|
19
19
|
let { demos }: Props = $props();
|
|
20
20
|
</script>
|
|
21
21
|
|
|
22
|
-
<
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
>
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
</Grid>
|
|
22
|
+
<div class="component-grid-scroll">
|
|
23
|
+
<Grid
|
|
24
|
+
columns="repeat(auto-fit, minmax(min(100%, 18rem), 1fr))"
|
|
25
|
+
autoRows={14}
|
|
26
|
+
gap={1}
|
|
27
|
+
paddingX="calc(var(--sveltely-padding-x) * 2)"
|
|
28
|
+
paddingY="calc(var(--sveltely-padding-y) * 2)"
|
|
29
|
+
>
|
|
30
|
+
{#each demos as entry (entry.name)}
|
|
31
|
+
{@const DemoComponent = entry.component}
|
|
32
|
+
<GridItem columnSpan={entry.columnSpan ?? 1} rowSpan={entry.rowSpan ?? 1}>
|
|
33
|
+
<HeroCard title={entry.name} description={entry.description}>
|
|
34
|
+
<DemoComponent />
|
|
35
|
+
</HeroCard>
|
|
36
|
+
</GridItem>
|
|
37
|
+
{/each}
|
|
38
|
+
</Grid>
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
<style>
|
|
42
|
+
.component-grid-scroll {
|
|
43
|
+
width: 100%;
|
|
44
|
+
height: 100%;
|
|
45
|
+
min-width: 0;
|
|
46
|
+
min-height: 0;
|
|
47
|
+
overflow: auto;
|
|
48
|
+
}
|
|
49
|
+
</style>
|
|
@@ -32,13 +32,13 @@
|
|
|
32
32
|
<p class="max-w-3xl text-sm text-[var(--sveltely-secondary-color)]">{description}</p>
|
|
33
33
|
{/if}
|
|
34
34
|
{/if}
|
|
35
|
-
<
|
|
35
|
+
<div class="hero-card-content">
|
|
36
36
|
{#if children}
|
|
37
37
|
<HStack minWidth="min-content" minHeight="100%" align="center" justify="center">
|
|
38
38
|
{@render children()}
|
|
39
39
|
</HStack>
|
|
40
40
|
{/if}
|
|
41
|
-
</
|
|
41
|
+
</div>
|
|
42
42
|
</VStack>
|
|
43
43
|
|
|
44
44
|
<style>
|
|
@@ -50,4 +50,11 @@
|
|
|
50
50
|
p {
|
|
51
51
|
max-width: none;
|
|
52
52
|
}
|
|
53
|
+
|
|
54
|
+
.hero-card-content {
|
|
55
|
+
min-width: 0;
|
|
56
|
+
min-height: 0;
|
|
57
|
+
flex: 1 1 0;
|
|
58
|
+
overflow: hidden;
|
|
59
|
+
}
|
|
53
60
|
</style>
|
package/dist/index.d.ts
CHANGED
|
@@ -2,8 +2,8 @@ export { motion, hover } from './actions/motion';
|
|
|
2
2
|
export { portalHost, portalContent } from './actions/portal';
|
|
3
3
|
export { tooltip } from './actions/tooltip';
|
|
4
4
|
export type { TooltipOptions } from './actions/tooltip';
|
|
5
|
-
export { LayoutAlignment, LayoutJustification,
|
|
6
|
-
export type { LayoutAlignmentValue, LayoutJustificationValue,
|
|
5
|
+
export { LayoutAlignment, LayoutJustification, LayoutSize } from './style/layout';
|
|
6
|
+
export type { LayoutAlignmentValue, LayoutJustificationValue, LayoutProps, LayoutSizeValue } from './style/layout';
|
|
7
7
|
export { LabelOrientation } from './style/label';
|
|
8
8
|
export type { LabelOrientationValue } from './style/label';
|
|
9
9
|
export { ImageFit, ImageLoading } from './style/media';
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { motion, hover } from './actions/motion';
|
|
2
2
|
export { portalHost, portalContent } from './actions/portal';
|
|
3
3
|
export { tooltip } from './actions/tooltip';
|
|
4
|
-
export { LayoutAlignment, LayoutJustification,
|
|
4
|
+
export { LayoutAlignment, LayoutJustification, LayoutSize } from './style/layout';
|
|
5
5
|
export { LabelOrientation } from './style/label';
|
|
6
6
|
export { ImageFit, ImageLoading } from './style/media';
|
|
7
7
|
export { ScrollAxis } from './style/scroll';
|
package/dist/style/layout.d.ts
CHANGED
|
@@ -3,13 +3,6 @@ export declare const LayoutSize: {
|
|
|
3
3
|
readonly full: "full";
|
|
4
4
|
readonly fit: "fit";
|
|
5
5
|
};
|
|
6
|
-
export declare const LayoutOverflow: {
|
|
7
|
-
readonly visible: "visible";
|
|
8
|
-
readonly hidden: "hidden";
|
|
9
|
-
readonly clip: "clip";
|
|
10
|
-
readonly auto: "auto";
|
|
11
|
-
readonly scroll: "scroll";
|
|
12
|
-
};
|
|
13
6
|
export declare const LayoutAlignment: {
|
|
14
7
|
readonly start: "start";
|
|
15
8
|
readonly center: "center";
|
|
@@ -26,11 +19,9 @@ export declare const LayoutJustification: {
|
|
|
26
19
|
readonly evenly: "evenly";
|
|
27
20
|
};
|
|
28
21
|
export type LayoutSizeValue = (typeof LayoutSize)[keyof typeof LayoutSize];
|
|
29
|
-
export type LayoutOverflowValue = (typeof LayoutOverflow)[keyof typeof LayoutOverflow];
|
|
30
22
|
export type LayoutAlignmentValue = (typeof LayoutAlignment)[keyof typeof LayoutAlignment];
|
|
31
23
|
export type LayoutJustificationValue = (typeof LayoutJustification)[keyof typeof LayoutJustification];
|
|
32
24
|
export type LayoutSize = StringWithAutocomplete<LayoutSizeValue> | number;
|
|
33
|
-
export type LayoutOverflow = LayoutOverflowValue;
|
|
34
25
|
export type LayoutAlignment = LayoutAlignmentValue;
|
|
35
26
|
export type LayoutJustification = LayoutJustificationValue;
|
|
36
27
|
export type LayoutProps = {
|
|
@@ -45,7 +36,6 @@ export type LayoutProps = {
|
|
|
45
36
|
shrink?: boolean | number;
|
|
46
37
|
basis?: number | string;
|
|
47
38
|
border?: boolean | string;
|
|
48
|
-
overflow?: LayoutOverflow;
|
|
49
39
|
align?: LayoutAlignment;
|
|
50
40
|
justify?: LayoutJustification;
|
|
51
41
|
};
|
package/dist/style/layout.js
CHANGED
|
@@ -2,13 +2,6 @@ export const LayoutSize = {
|
|
|
2
2
|
full: 'full',
|
|
3
3
|
fit: 'fit'
|
|
4
4
|
};
|
|
5
|
-
export const LayoutOverflow = {
|
|
6
|
-
visible: 'visible',
|
|
7
|
-
hidden: 'hidden',
|
|
8
|
-
clip: 'clip',
|
|
9
|
-
auto: 'auto',
|
|
10
|
-
scroll: 'scroll'
|
|
11
|
-
};
|
|
12
5
|
export const LayoutAlignment = {
|
|
13
6
|
start: 'start',
|
|
14
7
|
center: 'center',
|
|
@@ -36,7 +29,6 @@ const LAYOUT_PROP_KEYS = new Set([
|
|
|
36
29
|
'shrink',
|
|
37
30
|
'basis',
|
|
38
31
|
'border',
|
|
39
|
-
'overflow',
|
|
40
32
|
'align',
|
|
41
33
|
'justify'
|
|
42
34
|
]);
|
|
@@ -115,9 +107,6 @@ export const layoutStyle = (styles) => {
|
|
|
115
107
|
: `1px solid ${styles.border}`;
|
|
116
108
|
declarations.push(`border: ${borderValue};`);
|
|
117
109
|
}
|
|
118
|
-
if (styles.overflow !== undefined) {
|
|
119
|
-
declarations.push(`overflow: ${styles.overflow};`);
|
|
120
|
-
}
|
|
121
110
|
if (styles.align !== undefined) {
|
|
122
111
|
declarations.push(`align-items: ${alignValues[styles.align]};`);
|
|
123
112
|
}
|
package/dist/style.css
CHANGED
|
@@ -298,9 +298,6 @@
|
|
|
298
298
|
.max-w-3xl {
|
|
299
299
|
max-width: var(--container-3xl);
|
|
300
300
|
}
|
|
301
|
-
.max-w-32 {
|
|
302
|
-
max-width: calc(var(--spacing) * 32);
|
|
303
|
-
}
|
|
304
301
|
.max-w-md {
|
|
305
302
|
max-width: var(--container-md);
|
|
306
303
|
}
|
|
@@ -412,9 +409,6 @@
|
|
|
412
409
|
.rounded {
|
|
413
410
|
border-radius: 0.25rem;
|
|
414
411
|
}
|
|
415
|
-
.rounded-full {
|
|
416
|
-
border-radius: calc(infinity * 1px);
|
|
417
|
-
}
|
|
418
412
|
.rounded-md {
|
|
419
413
|
border-radius: var(--radius-md);
|
|
420
414
|
}
|
|
@@ -458,18 +452,9 @@
|
|
|
458
452
|
.px-2 {
|
|
459
453
|
padding-inline: calc(var(--spacing) * 2);
|
|
460
454
|
}
|
|
461
|
-
.px-3 {
|
|
462
|
-
padding-inline: calc(var(--spacing) * 3);
|
|
463
|
-
}
|
|
464
455
|
.py-1 {
|
|
465
456
|
padding-block: calc(var(--spacing) * 1);
|
|
466
457
|
}
|
|
467
|
-
.py-2 {
|
|
468
|
-
padding-block: calc(var(--spacing) * 2);
|
|
469
|
-
}
|
|
470
|
-
.pr-2 {
|
|
471
|
-
padding-right: calc(var(--spacing) * 2);
|
|
472
|
-
}
|
|
473
458
|
.text-left {
|
|
474
459
|
text-align: left;
|
|
475
460
|
}
|