@sveltier/ui 0.0.0-stage → 0.1.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +82 -2
- package/dist/components/Alert/Alert.svelte +88 -0
- package/dist/components/Alert/Alert.svelte.d.ts +4 -0
- package/dist/components/Alert/alert.css +78 -0
- package/dist/components/Alert/index.d.ts +2 -0
- package/dist/components/Alert/index.js +1 -0
- package/dist/components/Alert/types.d.ts +67 -0
- package/dist/components/Alert/types.js +1 -0
- package/dist/components/Avatar/Avatar.svelte +47 -0
- package/dist/components/Avatar/Avatar.svelte.d.ts +4 -0
- package/dist/components/Avatar/avatar.css +34 -0
- package/dist/components/Avatar/index.d.ts +2 -0
- package/dist/components/Avatar/index.js +1 -0
- package/dist/components/Avatar/types.d.ts +47 -0
- package/dist/components/Avatar/types.js +1 -0
- package/dist/components/Badge/Badge.svelte +53 -0
- package/dist/components/Badge/Badge.svelte.d.ts +4 -0
- package/dist/components/Badge/badge.css +57 -0
- package/dist/components/Badge/index.d.ts +2 -0
- package/dist/components/Badge/index.js +1 -0
- package/dist/components/Badge/types.d.ts +43 -0
- package/dist/components/Badge/types.js +1 -0
- package/dist/components/Button/Button.svelte +85 -0
- package/dist/components/Button/Button.svelte.d.ts +4 -0
- package/dist/components/Button/button.css +132 -0
- package/dist/components/Button/index.d.ts +2 -0
- package/dist/components/Button/index.js +1 -0
- package/dist/components/Button/types.d.ts +76 -0
- package/dist/components/Button/types.js +1 -0
- package/dist/components/ButtonGroup/ButtonGroup.svelte +61 -0
- package/dist/components/ButtonGroup/ButtonGroup.svelte.d.ts +4 -0
- package/dist/components/ButtonGroup/button-group.css +39 -0
- package/dist/components/ButtonGroup/index.d.ts +2 -0
- package/dist/components/ButtonGroup/index.js +1 -0
- package/dist/components/ButtonGroup/types.d.ts +48 -0
- package/dist/components/ButtonGroup/types.js +1 -0
- package/dist/components/Card/Card.svelte +53 -0
- package/dist/components/Card/Card.svelte.d.ts +4 -0
- package/dist/components/Card/card.css +62 -0
- package/dist/components/Card/index.d.ts +2 -0
- package/dist/components/Card/index.js +1 -0
- package/dist/components/Card/types.d.ts +56 -0
- package/dist/components/Card/types.js +1 -0
- package/dist/components/Checkbox/Checkbox.svelte +105 -0
- package/dist/components/Checkbox/Checkbox.svelte.d.ts +4 -0
- package/dist/components/Checkbox/checkbox.css +138 -0
- package/dist/components/Checkbox/index.d.ts +2 -0
- package/dist/components/Checkbox/index.js +1 -0
- package/dist/components/Checkbox/types.d.ts +76 -0
- package/dist/components/Checkbox/types.js +1 -0
- package/dist/components/Chip/Chip.svelte +109 -0
- package/dist/components/Chip/Chip.svelte.d.ts +4 -0
- package/dist/components/Chip/chip.css +101 -0
- package/dist/components/Chip/index.d.ts +2 -0
- package/dist/components/Chip/index.js +1 -0
- package/dist/components/Chip/types.d.ts +69 -0
- package/dist/components/Chip/types.js +1 -0
- package/dist/components/Choice/Choice.svelte +60 -0
- package/dist/components/Choice/Choice.svelte.d.ts +4 -0
- package/dist/components/Choice/choice.css +75 -0
- package/dist/components/Choice/types.d.ts +43 -0
- package/dist/components/Choice/types.js +1 -0
- package/dist/components/Divider/Divider.svelte +19 -0
- package/dist/components/Divider/Divider.svelte.d.ts +4 -0
- package/dist/components/Divider/divider.css +23 -0
- package/dist/components/Divider/index.d.ts +2 -0
- package/dist/components/Divider/index.js +1 -0
- package/dist/components/Divider/types.d.ts +10 -0
- package/dist/components/Divider/types.js +1 -0
- package/dist/components/Field/Field.svelte +95 -0
- package/dist/components/Field/Field.svelte.d.ts +4 -0
- package/dist/components/Field/field.css +210 -0
- package/dist/components/Field/index.d.ts +3 -0
- package/dist/components/Field/index.js +2 -0
- package/dist/components/Field/types.d.ts +109 -0
- package/dist/components/Field/types.js +2 -0
- package/dist/components/Form/Form.svelte +93 -0
- package/dist/components/Form/Form.svelte.d.ts +8 -0
- package/dist/components/Form/form.css +16 -0
- package/dist/components/Form/index.d.ts +2 -0
- package/dist/components/Form/index.js +1 -0
- package/dist/components/Form/types.d.ts +38 -0
- package/dist/components/Form/types.js +1 -0
- package/dist/components/Icon/Icon.svelte +38 -0
- package/dist/components/Icon/Icon.svelte.d.ts +4 -0
- package/dist/components/Icon/icon.css +33 -0
- package/dist/components/Icon/index.d.ts +2 -0
- package/dist/components/Icon/index.js +1 -0
- package/dist/components/Icon/types.d.ts +34 -0
- package/dist/components/Icon/types.js +1 -0
- package/dist/components/List/List.svelte +39 -0
- package/dist/components/List/List.svelte.d.ts +4 -0
- package/dist/components/List/ListItem.svelte +57 -0
- package/dist/components/List/ListItem.svelte.d.ts +4 -0
- package/dist/components/List/index.d.ts +3 -0
- package/dist/components/List/index.js +2 -0
- package/dist/components/List/list.css +127 -0
- package/dist/components/List/types.d.ts +67 -0
- package/dist/components/List/types.js +1 -0
- package/dist/components/NativeSelect/NativeSelect.svelte +110 -0
- package/dist/components/NativeSelect/NativeSelect.svelte.d.ts +25 -0
- package/dist/components/NativeSelect/index.d.ts +2 -0
- package/dist/components/NativeSelect/index.js +1 -0
- package/dist/components/NativeSelect/native-select.css +45 -0
- package/dist/components/NativeSelect/types.d.ts +58 -0
- package/dist/components/NativeSelect/types.js +1 -0
- package/dist/components/NumberField/NumberField.svelte +246 -0
- package/dist/components/NumberField/NumberField.svelte.d.ts +4 -0
- package/dist/components/NumberField/index.d.ts +2 -0
- package/dist/components/NumberField/index.js +1 -0
- package/dist/components/NumberField/number-field.css +74 -0
- package/dist/components/NumberField/types.d.ts +73 -0
- package/dist/components/NumberField/types.js +1 -0
- package/dist/components/Progress/ProgressCircular.svelte +61 -0
- package/dist/components/Progress/ProgressCircular.svelte.d.ts +4 -0
- package/dist/components/Progress/ProgressLinear.svelte +44 -0
- package/dist/components/Progress/ProgressLinear.svelte.d.ts +4 -0
- package/dist/components/Progress/index.d.ts +3 -0
- package/dist/components/Progress/index.js +2 -0
- package/dist/components/Progress/progress-circular.css +61 -0
- package/dist/components/Progress/progress-linear.css +67 -0
- package/dist/components/Progress/types.d.ts +53 -0
- package/dist/components/Progress/types.js +1 -0
- package/dist/components/RadioGroup/RadioGroup.svelte +149 -0
- package/dist/components/RadioGroup/RadioGroup.svelte.d.ts +25 -0
- package/dist/components/RadioGroup/index.d.ts +2 -0
- package/dist/components/RadioGroup/index.js +1 -0
- package/dist/components/RadioGroup/radio-group.css +139 -0
- package/dist/components/RadioGroup/types.d.ts +93 -0
- package/dist/components/RadioGroup/types.js +1 -0
- package/dist/components/SegmentedControl/SegmentedControl.svelte +70 -0
- package/dist/components/SegmentedControl/SegmentedControl.svelte.d.ts +25 -0
- package/dist/components/SegmentedControl/index.d.ts +2 -0
- package/dist/components/SegmentedControl/index.js +1 -0
- package/dist/components/SegmentedControl/segmented-control.css +68 -0
- package/dist/components/SegmentedControl/types.d.ts +60 -0
- package/dist/components/SegmentedControl/types.js +1 -0
- package/dist/components/Skeleton/Skeleton.svelte +33 -0
- package/dist/components/Skeleton/Skeleton.svelte.d.ts +4 -0
- package/dist/components/Skeleton/index.d.ts +2 -0
- package/dist/components/Skeleton/index.js +1 -0
- package/dist/components/Skeleton/skeleton.css +70 -0
- package/dist/components/Skeleton/types.d.ts +25 -0
- package/dist/components/Skeleton/types.js +1 -0
- package/dist/components/Slider/Slider.svelte +153 -0
- package/dist/components/Slider/Slider.svelte.d.ts +4 -0
- package/dist/components/Slider/index.d.ts +2 -0
- package/dist/components/Slider/index.js +1 -0
- package/dist/components/Slider/slider.css +206 -0
- package/dist/components/Slider/types.d.ts +102 -0
- package/dist/components/Slider/types.js +1 -0
- package/dist/components/Switch/Switch.svelte +94 -0
- package/dist/components/Switch/Switch.svelte.d.ts +4 -0
- package/dist/components/Switch/index.d.ts +2 -0
- package/dist/components/Switch/index.js +1 -0
- package/dist/components/Switch/switch.css +118 -0
- package/dist/components/Switch/types.d.ts +67 -0
- package/dist/components/Switch/types.js +1 -0
- package/dist/components/Tabs/Tabs.svelte +93 -0
- package/dist/components/Tabs/Tabs.svelte.d.ts +25 -0
- package/dist/components/Tabs/index.d.ts +2 -0
- package/dist/components/Tabs/index.js +1 -0
- package/dist/components/Tabs/tabs.css +73 -0
- package/dist/components/Tabs/types.d.ts +59 -0
- package/dist/components/Tabs/types.js +1 -0
- package/dist/components/TextField/TextField.svelte +115 -0
- package/dist/components/TextField/TextField.svelte.d.ts +4 -0
- package/dist/components/TextField/index.d.ts +2 -0
- package/dist/components/TextField/index.js +1 -0
- package/dist/components/TextField/types.d.ts +57 -0
- package/dist/components/TextField/types.js +1 -0
- package/dist/components/Textarea/Textarea.svelte +116 -0
- package/dist/components/Textarea/Textarea.svelte.d.ts +4 -0
- package/dist/components/Textarea/index.d.ts +2 -0
- package/dist/components/Textarea/index.js +1 -0
- package/dist/components/Textarea/textarea.css +35 -0
- package/dist/components/Textarea/types.d.ts +60 -0
- package/dist/components/Textarea/types.js +1 -0
- package/dist/core/defaults/DefaultsProvider.svelte +30 -0
- package/dist/core/defaults/DefaultsProvider.svelte.d.ts +13 -0
- package/dist/core/defaults/context.d.ts +45 -0
- package/dist/core/defaults/context.js +38 -0
- package/dist/core/display/display.d.ts +39 -0
- package/dist/core/display/display.js +53 -0
- package/dist/core/errors/codes.d.ts +112 -0
- package/dist/core/errors/codes.js +96 -0
- package/dist/core/errors/errors.d.ts +8 -0
- package/dist/core/errors/errors.js +7 -0
- package/dist/core/form/constraints.d.ts +15 -0
- package/dist/core/form/constraints.js +42 -0
- package/dist/core/form/context.d.ts +19 -0
- package/dist/core/form/context.js +4 -0
- package/dist/core/form/field.svelte.d.ts +34 -0
- package/dist/core/form/field.svelte.js +66 -0
- package/dist/core/form/number.d.ts +24 -0
- package/dist/core/form/number.js +71 -0
- package/dist/core/form/types.d.ts +22 -0
- package/dist/core/form/types.js +1 -0
- package/dist/core/locale/locale.d.ts +55 -0
- package/dist/core/locale/locale.js +80 -0
- package/dist/core/props/classes.d.ts +21 -0
- package/dist/core/props/classes.js +44 -0
- package/dist/core/props/element.d.ts +18 -0
- package/dist/core/props/element.js +1 -0
- package/dist/core/props/types.d.ts +47 -0
- package/dist/core/props/types.js +16 -0
- package/dist/core/provider/UIProvider.svelte +60 -0
- package/dist/core/provider/UIProvider.svelte.d.ts +29 -0
- package/dist/core/theme/ThemeProvider.svelte +178 -0
- package/dist/core/theme/ThemeProvider.svelte.d.ts +25 -0
- package/dist/core/theme/color.d.ts +23 -0
- package/dist/core/theme/color.js +54 -0
- package/dist/core/theme/context.d.ts +25 -0
- package/dist/core/theme/context.js +14 -0
- package/dist/core/theme/css.d.ts +25 -0
- package/dist/core/theme/css.js +69 -0
- package/dist/core/theme/define.d.ts +11 -0
- package/dist/core/theme/define.js +21 -0
- package/dist/core/theme/resolve.d.ts +6 -0
- package/dist/core/theme/resolve.js +18 -0
- package/dist/core/theme/theme-provider.css +13 -0
- package/dist/core/theme/types.d.ts +35 -0
- package/dist/core/theme/types.js +61 -0
- package/dist/dev/off.d.ts +4 -0
- package/dist/dev/off.js +3 -0
- package/dist/dev/on.d.ts +8 -0
- package/dist/dev/on.js +33 -0
- package/dist/index.d.ts +58 -0
- package/dist/index.js +72 -0
- package/dist/primitives/anchored/anchored.d.ts +39 -0
- package/dist/primitives/anchored/anchored.js +104 -0
- package/dist/primitives/anchored/fallback.d.ts +31 -0
- package/dist/primitives/anchored/fallback.js +73 -0
- package/dist/primitives/focus/focus-trap.d.ts +25 -0
- package/dist/primitives/focus/focus-trap.js +86 -0
- package/dist/primitives/focus/roving-focus.d.ts +22 -0
- package/dist/primitives/focus/roving-focus.js +86 -0
- package/dist/primitives/focus/tabbable.d.ts +9 -0
- package/dist/primitives/focus/tabbable.js +75 -0
- package/dist/primitives/inert/inert-others.d.ts +12 -0
- package/dist/primitives/inert/inert-others.js +44 -0
- package/dist/primitives/keyboard/keymap.d.ts +17 -0
- package/dist/primitives/keyboard/keymap.js +39 -0
- package/dist/primitives/keyboard/typeahead.d.ts +18 -0
- package/dist/primitives/keyboard/typeahead.js +50 -0
- package/dist/primitives/layer/dismissable.d.ts +26 -0
- package/dist/primitives/layer/dismissable.js +84 -0
- package/dist/primitives/motion/easing.d.ts +9 -0
- package/dist/primitives/motion/easing.js +46 -0
- package/dist/primitives/motion/motion.d.ts +59 -0
- package/dist/primitives/motion/motion.js +80 -0
- package/dist/primitives/portal/Portal.svelte +37 -0
- package/dist/primitives/portal/Portal.svelte.d.ts +9 -0
- package/dist/primitives/portal/PortalContent.svelte +15 -0
- package/dist/primitives/portal/PortalContent.svelte.d.ts +9 -0
- package/dist/primitives/portal/portal.css +8 -0
- package/dist/primitives/scroll-lock/scroll-lock.d.ts +10 -0
- package/dist/primitives/scroll-lock/scroll-lock.js +33 -0
- package/dist/primitives/virtual/virtual-list.svelte.d.ts +65 -0
- package/dist/primitives/virtual/virtual-list.svelte.js +291 -0
- package/dist/styles/base.css +44 -0
- package/dist/styles/props.css +137 -0
- package/dist/styles/tokens.css +84 -0
- package/dist/styles/visually-hidden.css +16 -0
- package/dist/themes/aurora.d.ts +13 -0
- package/dist/themes/aurora.js +103 -0
- package/dist/themes/heritage.d.ts +13 -0
- package/dist/themes/heritage.js +102 -0
- package/dist/themes/maison.d.ts +13 -0
- package/dist/themes/maison.js +99 -0
- package/dist/themes/milano.d.ts +13 -0
- package/dist/themes/milano.js +101 -0
- package/dist/themes/monolith.d.ts +13 -0
- package/dist/themes/monolith.js +101 -0
- package/package.json +87 -5
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// A button, or a link styled as one. See ButtonProps for the props.
|
|
3
|
+
import { useDefaults } from '../../core/defaults/context.js';
|
|
4
|
+
import { useLocale } from '../../core/locale/locale.js';
|
|
5
|
+
import { sharedClasses } from '../../core/props/classes.js';
|
|
6
|
+
import '../../styles/visually-hidden.css';
|
|
7
|
+
import './button.css';
|
|
8
|
+
import type { ButtonDefaults, ButtonProps } from './types.js';
|
|
9
|
+
|
|
10
|
+
let {
|
|
11
|
+
variant,
|
|
12
|
+
size,
|
|
13
|
+
density,
|
|
14
|
+
color,
|
|
15
|
+
rounded,
|
|
16
|
+
block,
|
|
17
|
+
disabled,
|
|
18
|
+
as,
|
|
19
|
+
icon = false,
|
|
20
|
+
loading = false,
|
|
21
|
+
href,
|
|
22
|
+
type,
|
|
23
|
+
onclick,
|
|
24
|
+
'aria-disabled': ariaDisabled,
|
|
25
|
+
prepend,
|
|
26
|
+
append,
|
|
27
|
+
children,
|
|
28
|
+
class: className,
|
|
29
|
+
ref = $bindable(null),
|
|
30
|
+
...rest
|
|
31
|
+
}: ButtonProps = $props();
|
|
32
|
+
|
|
33
|
+
const resolve = useDefaults<ButtonDefaults>('Button', {
|
|
34
|
+
variant: 'flat',
|
|
35
|
+
size: 'md',
|
|
36
|
+
density: 'default',
|
|
37
|
+
color: 'primary',
|
|
38
|
+
rounded: true,
|
|
39
|
+
block: false,
|
|
40
|
+
disabled: false
|
|
41
|
+
});
|
|
42
|
+
const p = $derived(resolve({ variant, size, density, color, rounded, block, disabled }));
|
|
43
|
+
const locale = useLocale();
|
|
44
|
+
|
|
45
|
+
const link = $derived((as ?? (href == null ? 'button' : 'a')) === 'a');
|
|
46
|
+
// Only a <button> can be disabled natively. A disabled link loses its href; a loading button
|
|
47
|
+
// and one marked aria-disabled stay focusable. None of them takes presses.
|
|
48
|
+
const native = $derived(!link && p.disabled);
|
|
49
|
+
const inert = $derived(p.disabled || loading || ariaDisabled === true || ariaDisabled === 'true');
|
|
50
|
+
|
|
51
|
+
function click(event: MouseEvent) {
|
|
52
|
+
if (inert) event.preventDefault();
|
|
53
|
+
else (onclick as ((event: MouseEvent) => void) | null | undefined)?.(event);
|
|
54
|
+
}
|
|
55
|
+
</script>
|
|
56
|
+
|
|
57
|
+
<svelte:element
|
|
58
|
+
this={link ? 'a' : 'button'}
|
|
59
|
+
bind:this={ref}
|
|
60
|
+
{...rest}
|
|
61
|
+
href={link && !p.disabled ? href : undefined}
|
|
62
|
+
type={link ? undefined : (type ?? 'button')}
|
|
63
|
+
role={link && p.disabled ? 'link' : undefined}
|
|
64
|
+
disabled={native || undefined}
|
|
65
|
+
aria-disabled={(inert && !native) || undefined}
|
|
66
|
+
class={[
|
|
67
|
+
'sv-button',
|
|
68
|
+
sharedClasses(p, 'Button'),
|
|
69
|
+
inert && 'sv-button--inert',
|
|
70
|
+
p.disabled && 'sv-button--disabled',
|
|
71
|
+
p.block && 'sv-button--block',
|
|
72
|
+
icon && 'sv-button--icon',
|
|
73
|
+
loading && 'sv-button--loading',
|
|
74
|
+
className
|
|
75
|
+
]}
|
|
76
|
+
onclick={click}
|
|
77
|
+
>
|
|
78
|
+
{#if prepend}<span class="sv-button__prepend">{@render prepend()}</span>{/if}
|
|
79
|
+
{@render children?.()}
|
|
80
|
+
{#if append}<span class="sv-button__append">{@render append()}</span>{/if}
|
|
81
|
+
{#if loading}
|
|
82
|
+
<span class="sv-button__loader" aria-hidden="true"></span>
|
|
83
|
+
<span class="sv-visually-hidden">{locale.t('loading')}</span>
|
|
84
|
+
{/if}
|
|
85
|
+
</svelte:element>
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
@layer sv.tokens, sv.base, sv.components, sv.utilities;
|
|
2
|
+
|
|
3
|
+
@layer sv.components {
|
|
4
|
+
/*
|
|
5
|
+
* Height and padding follow size and density; text follows size only. Colours come from
|
|
6
|
+
* the variant classes, which read the colour class.
|
|
7
|
+
*/
|
|
8
|
+
.sv-button {
|
|
9
|
+
position: relative;
|
|
10
|
+
isolation: isolate;
|
|
11
|
+
display: inline-flex;
|
|
12
|
+
align-items: center;
|
|
13
|
+
justify-content: center;
|
|
14
|
+
gap: 0.618em;
|
|
15
|
+
box-sizing: border-box;
|
|
16
|
+
block-size: calc(var(--sv-size-height) * var(--sv-density));
|
|
17
|
+
margin: 0;
|
|
18
|
+
padding: 0 calc(var(--sv-size-height) * var(--sv-density) * 0.382);
|
|
19
|
+
border: var(--sv-hairline) solid var(--sv-variant-border);
|
|
20
|
+
border-radius: var(--sv-rounded, var(--sv-radius-md));
|
|
21
|
+
background-color: var(--sv-variant-bg);
|
|
22
|
+
box-shadow: var(--sv-variant-shadow);
|
|
23
|
+
color: var(--sv-variant-fg);
|
|
24
|
+
font: var(--sv-weight-medium) var(--sv-size-text) / 1 var(--sv-font-ui);
|
|
25
|
+
letter-spacing: var(--sv-button-tracking);
|
|
26
|
+
text-transform: var(--sv-button-transform);
|
|
27
|
+
text-decoration: none;
|
|
28
|
+
white-space: nowrap;
|
|
29
|
+
vertical-align: middle;
|
|
30
|
+
cursor: pointer;
|
|
31
|
+
user-select: none;
|
|
32
|
+
-webkit-tap-highlight-color: transparent;
|
|
33
|
+
transition: transform var(--sv-duration-fast) var(--sv-ease);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/* The theme's bevel: a hairline of light along the top edge of filled buttons. */
|
|
37
|
+
.sv-button.sv-variant-flat {
|
|
38
|
+
box-shadow: inset 0 var(--sv-hairline) 0 var(--sv-bevel);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/* State layer: the foreground colour over the background, only its opacity changes. */
|
|
42
|
+
.sv-button::before {
|
|
43
|
+
content: '';
|
|
44
|
+
position: absolute;
|
|
45
|
+
inset: calc(var(--sv-hairline) * -1);
|
|
46
|
+
z-index: -1;
|
|
47
|
+
border-radius: inherit;
|
|
48
|
+
background-color: currentColor;
|
|
49
|
+
opacity: 0;
|
|
50
|
+
pointer-events: none;
|
|
51
|
+
transition: opacity var(--sv-duration-fast) var(--sv-ease);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@media (hover: hover) {
|
|
55
|
+
.sv-button:where(:not(.sv-button--inert)):hover::before {
|
|
56
|
+
opacity: var(--sv-state-hover);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.sv-button:where(:not(.sv-button--inert)):active::before {
|
|
61
|
+
opacity: var(--sv-state-press);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.sv-button:where(:not(.sv-button--inert)):active {
|
|
65
|
+
transform: translateY(var(--sv-press-offset));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.sv-button--inert {
|
|
69
|
+
cursor: not-allowed;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* Muted at φ⁻². A loading button keeps its colour: it is busy, not unavailable. */
|
|
73
|
+
.sv-button--disabled {
|
|
74
|
+
opacity: 0.382;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.sv-button--block {
|
|
78
|
+
display: flex;
|
|
79
|
+
inline-size: 100%;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.sv-button--icon {
|
|
83
|
+
inline-size: calc(var(--sv-size-height) * var(--sv-density));
|
|
84
|
+
padding: 0;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.sv-button__prepend,
|
|
88
|
+
.sv-button__append {
|
|
89
|
+
display: inline-flex;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/* Icons are φ^½ of the text. */
|
|
93
|
+
:where(.sv-button) svg {
|
|
94
|
+
inline-size: 1.272em;
|
|
95
|
+
block-size: 1.272em;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/*
|
|
99
|
+
* Loading: the label and icons turn transparent but keep their width and, for screen
|
|
100
|
+
* readers, the name. The spinner is painted in the foreground colour.
|
|
101
|
+
*/
|
|
102
|
+
.sv-button--loading {
|
|
103
|
+
color: transparent;
|
|
104
|
+
cursor: wait;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.sv-button__loader {
|
|
108
|
+
position: absolute;
|
|
109
|
+
inset: 0;
|
|
110
|
+
box-sizing: border-box;
|
|
111
|
+
inline-size: 1.272em;
|
|
112
|
+
block-size: 1.272em;
|
|
113
|
+
margin: auto;
|
|
114
|
+
border: 0.146em solid var(--sv-variant-fg);
|
|
115
|
+
border-inline-end-color: transparent;
|
|
116
|
+
border-radius: 50%;
|
|
117
|
+
animation: sv-spin 987ms linear infinite;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/* A spinner that stops reads as a frozen interface, so it slows down instead. */
|
|
121
|
+
@media (prefers-reduced-motion: reduce) {
|
|
122
|
+
.sv-button__loader {
|
|
123
|
+
animation-duration: 2961ms;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
@keyframes sv-spin {
|
|
128
|
+
to {
|
|
129
|
+
transform: rotate(1turn);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Button } from './Button.svelte';
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLAnchorAttributes, HTMLButtonAttributes } from 'svelte/elements';
|
|
3
|
+
import type { ElementProps } from '../../core/props/element.js';
|
|
4
|
+
import type { Color, Density, Rounded, Size, Variant } from '../../core/props/types.js';
|
|
5
|
+
/** The Button props that `DefaultsProvider` and `UIProvider` can set for a subtree. */
|
|
6
|
+
export interface ButtonDefaults {
|
|
7
|
+
/**
|
|
8
|
+
* Visual weight.
|
|
9
|
+
* @default 'flat'
|
|
10
|
+
*/
|
|
11
|
+
variant: Variant;
|
|
12
|
+
/**
|
|
13
|
+
* Height and text size.
|
|
14
|
+
* @default 'md'
|
|
15
|
+
*/
|
|
16
|
+
size: Size;
|
|
17
|
+
/**
|
|
18
|
+
* Multiplies the height and padding, never the text.
|
|
19
|
+
* @default 'default'
|
|
20
|
+
*/
|
|
21
|
+
density: Density;
|
|
22
|
+
/**
|
|
23
|
+
* The colour the variant is painted with.
|
|
24
|
+
* @default 'primary'
|
|
25
|
+
*/
|
|
26
|
+
color: Color;
|
|
27
|
+
/**
|
|
28
|
+
* Corner rounding. `true` keeps the theme's radius.
|
|
29
|
+
* @default true
|
|
30
|
+
*/
|
|
31
|
+
rounded: Rounded;
|
|
32
|
+
/**
|
|
33
|
+
* Takes the full width of its container.
|
|
34
|
+
* @default false
|
|
35
|
+
*/
|
|
36
|
+
block: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* A disabled button cannot be focused or pressed; a disabled link loses its `href`.
|
|
39
|
+
* @default false
|
|
40
|
+
*/
|
|
41
|
+
disabled: boolean;
|
|
42
|
+
}
|
|
43
|
+
declare module '../../core/defaults/context.js' {
|
|
44
|
+
interface ComponentDefaults {
|
|
45
|
+
Button: ButtonDefaults;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
type ButtonOwnProps = Partial<ButtonDefaults> & {
|
|
49
|
+
/**
|
|
50
|
+
* The element to render. Defaults to `'a'` when `href` is given, otherwise `'button'`.
|
|
51
|
+
*/
|
|
52
|
+
as?: 'button' | 'a';
|
|
53
|
+
/**
|
|
54
|
+
* Square, for a single icon as the content. Give it an `aria-label`.
|
|
55
|
+
* @default false
|
|
56
|
+
*/
|
|
57
|
+
icon?: boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Shows a spinner and ignores presses, while the button keeps focus and its label for
|
|
60
|
+
* screen readers, which also hear that it is loading.
|
|
61
|
+
* @default false
|
|
62
|
+
*/
|
|
63
|
+
loading?: boolean;
|
|
64
|
+
/** Content before the label, usually an icon. */
|
|
65
|
+
prepend?: Snippet;
|
|
66
|
+
/** Content after the label, usually an icon. */
|
|
67
|
+
append?: Snippet;
|
|
68
|
+
/** The label. */
|
|
69
|
+
children?: Snippet;
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* A button, or a link that looks like one (`href`). Every other attribute of `<button>` and
|
|
73
|
+
* `<a>` passes through to the element.
|
|
74
|
+
*/
|
|
75
|
+
export type ButtonProps = ElementProps<HTMLButtonElement | HTMLAnchorElement, HTMLButtonAttributes & HTMLAnchorAttributes, ButtonOwnProps>;
|
|
76
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// Buttons joined into one control. See ButtonGroupProps for the props.
|
|
3
|
+
import {
|
|
4
|
+
getDefaultsContext,
|
|
5
|
+
mergeDefaults,
|
|
6
|
+
setDefaultsContext,
|
|
7
|
+
useDefaults
|
|
8
|
+
} from '../../core/defaults/context.js';
|
|
9
|
+
import { sharedClasses } from '../../core/props/classes.js';
|
|
10
|
+
import './button-group.css';
|
|
11
|
+
import type { ButtonGroupDefaults, ButtonGroupProps } from './types.js';
|
|
12
|
+
|
|
13
|
+
let {
|
|
14
|
+
variant,
|
|
15
|
+
size,
|
|
16
|
+
density,
|
|
17
|
+
color,
|
|
18
|
+
rounded,
|
|
19
|
+
children,
|
|
20
|
+
class: className,
|
|
21
|
+
ref = $bindable(null),
|
|
22
|
+
...rest
|
|
23
|
+
}: ButtonGroupProps = $props();
|
|
24
|
+
|
|
25
|
+
const resolve = useDefaults<ButtonGroupDefaults>('ButtonGroup', {
|
|
26
|
+
variant: 'outlined',
|
|
27
|
+
size: 'md',
|
|
28
|
+
density: 'default',
|
|
29
|
+
color: 'primary',
|
|
30
|
+
rounded: true
|
|
31
|
+
});
|
|
32
|
+
const p = $derived(resolve({ variant, size, density, color, rounded }));
|
|
33
|
+
|
|
34
|
+
// The Buttons inside take the group's props as their defaults, so their own props still win.
|
|
35
|
+
const parent = getDefaultsContext();
|
|
36
|
+
const merged = $derived(
|
|
37
|
+
mergeDefaults(parent?.config, {
|
|
38
|
+
Button: {
|
|
39
|
+
variant: p.variant,
|
|
40
|
+
size: p.size,
|
|
41
|
+
density: p.density,
|
|
42
|
+
color: p.color,
|
|
43
|
+
rounded: p.rounded
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
);
|
|
47
|
+
setDefaultsContext({
|
|
48
|
+
get config() {
|
|
49
|
+
return merged;
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
</script>
|
|
53
|
+
|
|
54
|
+
<div
|
|
55
|
+
bind:this={ref}
|
|
56
|
+
{...rest}
|
|
57
|
+
role="group"
|
|
58
|
+
class={['sv-button-group', sharedClasses({ rounded: p.rounded }, 'ButtonGroup'), className]}
|
|
59
|
+
>
|
|
60
|
+
{@render children?.()}
|
|
61
|
+
</div>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
@layer sv.tokens, sv.base, sv.components, sv.utilities;
|
|
2
|
+
|
|
3
|
+
@layer sv.components {
|
|
4
|
+
.sv-button-group {
|
|
5
|
+
display: inline-flex;
|
|
6
|
+
isolation: isolate;
|
|
7
|
+
vertical-align: middle;
|
|
8
|
+
border-radius: var(--sv-rounded, var(--sv-radius-md));
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/* Square inside; the group's own rounding on the outer corners. */
|
|
12
|
+
.sv-button-group > .sv-button {
|
|
13
|
+
border-radius: 0;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.sv-button-group > .sv-button:first-child {
|
|
17
|
+
border-start-start-radius: inherit;
|
|
18
|
+
border-end-start-radius: inherit;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.sv-button-group > .sv-button:last-child {
|
|
22
|
+
border-start-end-radius: inherit;
|
|
23
|
+
border-end-end-radius: inherit;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/* Neighbours share one border; the focused one is drawn on top. */
|
|
27
|
+
.sv-button-group > .sv-button + .sv-button {
|
|
28
|
+
margin-inline-start: calc(var(--sv-hairline) * -1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.sv-button-group > .sv-button:focus-visible {
|
|
32
|
+
z-index: 1;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/* Filled and borderless buttons get a faint divider where they meet. */
|
|
36
|
+
.sv-button-group > .sv-button:not(.sv-variant-outlined) + .sv-button:not(.sv-variant-outlined) {
|
|
37
|
+
border-inline-start-color: color-mix(in oklch, var(--sv-variant-fg) 21%, transparent);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as ButtonGroup } from './ButtonGroup.svelte';
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
import type { ElementProps } from '../../core/props/element.js';
|
|
4
|
+
import type { Color, Density, Rounded, Size, Variant } from '../../core/props/types.js';
|
|
5
|
+
/**
|
|
6
|
+
* The ButtonGroup props that `DefaultsProvider` and `UIProvider` can set for a subtree. The
|
|
7
|
+
* group passes them on to its Buttons.
|
|
8
|
+
*/
|
|
9
|
+
export interface ButtonGroupDefaults {
|
|
10
|
+
/**
|
|
11
|
+
* The variant of the Buttons.
|
|
12
|
+
* @default 'outlined'
|
|
13
|
+
*/
|
|
14
|
+
variant: Variant;
|
|
15
|
+
/**
|
|
16
|
+
* The size of the Buttons.
|
|
17
|
+
* @default 'md'
|
|
18
|
+
*/
|
|
19
|
+
size: Size;
|
|
20
|
+
/**
|
|
21
|
+
* The density of the Buttons.
|
|
22
|
+
* @default 'default'
|
|
23
|
+
*/
|
|
24
|
+
density: Density;
|
|
25
|
+
/**
|
|
26
|
+
* The colour of the Buttons.
|
|
27
|
+
* @default 'primary'
|
|
28
|
+
*/
|
|
29
|
+
color: Color;
|
|
30
|
+
/**
|
|
31
|
+
* The rounding of the group's outer corners; inner corners are square.
|
|
32
|
+
* @default true
|
|
33
|
+
*/
|
|
34
|
+
rounded: Rounded;
|
|
35
|
+
}
|
|
36
|
+
declare module '../../core/defaults/context.js' {
|
|
37
|
+
interface ComponentDefaults {
|
|
38
|
+
ButtonGroup: ButtonGroupDefaults;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Buttons joined into one control: shared borders, square inner corners, and the same
|
|
43
|
+
* variant, size, density and colour unless a Button says otherwise. Give it an `aria-label`.
|
|
44
|
+
*/
|
|
45
|
+
export type ButtonGroupProps = ElementProps<HTMLDivElement, HTMLAttributes<HTMLDivElement>, Partial<ButtonGroupDefaults> & {
|
|
46
|
+
/** The Buttons. */
|
|
47
|
+
children?: Snippet;
|
|
48
|
+
}>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// A surface with a header, content and actions. See CardProps for the props.
|
|
3
|
+
import { useDefaults } from '../../core/defaults/context.js';
|
|
4
|
+
import { sharedClasses } from '../../core/props/classes.js';
|
|
5
|
+
import './card.css';
|
|
6
|
+
import type { CardDefaults, CardProps } from './types.js';
|
|
7
|
+
|
|
8
|
+
let {
|
|
9
|
+
variant,
|
|
10
|
+
color,
|
|
11
|
+
density,
|
|
12
|
+
rounded,
|
|
13
|
+
title,
|
|
14
|
+
subtitle,
|
|
15
|
+
level,
|
|
16
|
+
prepend,
|
|
17
|
+
append,
|
|
18
|
+
actions,
|
|
19
|
+
children,
|
|
20
|
+
class: className,
|
|
21
|
+
ref = $bindable(null),
|
|
22
|
+
...rest
|
|
23
|
+
}: CardProps = $props();
|
|
24
|
+
|
|
25
|
+
const resolve = useDefaults<CardDefaults>('Card', {
|
|
26
|
+
variant: 'elevated',
|
|
27
|
+
color: 'neutral',
|
|
28
|
+
density: 'default',
|
|
29
|
+
rounded: true
|
|
30
|
+
});
|
|
31
|
+
const p = $derived(resolve({ variant, color, density, rounded }));
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
<div bind:this={ref} {...rest} class={['sv-card', sharedClasses(p, 'Card'), className]}>
|
|
35
|
+
{#if title || subtitle || prepend || append}
|
|
36
|
+
<div class="sv-card__header">
|
|
37
|
+
{#if prepend}<div class="sv-card__prepend">{@render prepend()}</div>{/if}
|
|
38
|
+
{#if title || subtitle}
|
|
39
|
+
<div class="sv-card__heading">
|
|
40
|
+
{#if title}
|
|
41
|
+
<svelte:element this={level ? `h${level}` : 'div'} class="sv-card__title">
|
|
42
|
+
{title}
|
|
43
|
+
</svelte:element>
|
|
44
|
+
{/if}
|
|
45
|
+
{#if subtitle}<div class="sv-card__subtitle">{subtitle}</div>{/if}
|
|
46
|
+
</div>
|
|
47
|
+
{/if}
|
|
48
|
+
{#if append}<div class="sv-card__append">{@render append()}</div>{/if}
|
|
49
|
+
</div>
|
|
50
|
+
{/if}
|
|
51
|
+
{@render children?.()}
|
|
52
|
+
{#if actions}<div class="sv-card__actions">{@render actions()}</div>{/if}
|
|
53
|
+
</div>
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
@layer sv.tokens, sv.base, sv.components, sv.utilities;
|
|
2
|
+
|
|
3
|
+
@layer sv.components {
|
|
4
|
+
/* Sections a φ step apart inside Fibonacci padding, both times the density. */
|
|
5
|
+
.sv-card {
|
|
6
|
+
display: flex;
|
|
7
|
+
flex-direction: column;
|
|
8
|
+
gap: calc(var(--sv-space-13) * var(--sv-density));
|
|
9
|
+
box-sizing: border-box;
|
|
10
|
+
min-inline-size: 0;
|
|
11
|
+
padding: calc(var(--sv-space-21) * var(--sv-density));
|
|
12
|
+
border: var(--sv-hairline) solid var(--sv-variant-border);
|
|
13
|
+
border-radius: var(--sv-rounded, var(--sv-radius-lg));
|
|
14
|
+
background-color: var(--sv-variant-bg);
|
|
15
|
+
box-shadow: var(--sv-variant-shadow);
|
|
16
|
+
color: var(--sv-variant-fg);
|
|
17
|
+
font-size: var(--sv-text-sm);
|
|
18
|
+
line-height: var(--sv-leading-normal);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/* A neutral outline is the theme's border, not the text colour. */
|
|
22
|
+
.sv-card.sv-variant-outlined.sv-color-neutral {
|
|
23
|
+
--sv-variant-border: var(--sv-color-border);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.sv-card__header {
|
|
27
|
+
display: flex;
|
|
28
|
+
gap: var(--sv-space-13);
|
|
29
|
+
align-items: center;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.sv-card__heading {
|
|
33
|
+
display: grid;
|
|
34
|
+
flex: 1;
|
|
35
|
+
gap: var(--sv-space-3);
|
|
36
|
+
min-inline-size: 0;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.sv-card__title {
|
|
40
|
+
margin: 0;
|
|
41
|
+
font: var(--sv-weight-display) var(--sv-text-lg) / var(--sv-leading-snug) var(--sv-font-display);
|
|
42
|
+
letter-spacing: var(--sv-tracking-display);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.sv-card__subtitle {
|
|
46
|
+
font-size: var(--sv-text-xs);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.sv-card__prepend,
|
|
50
|
+
.sv-card__append {
|
|
51
|
+
display: flex;
|
|
52
|
+
flex: none;
|
|
53
|
+
align-items: center;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.sv-card__actions {
|
|
57
|
+
display: flex;
|
|
58
|
+
flex-wrap: wrap;
|
|
59
|
+
gap: var(--sv-space-8);
|
|
60
|
+
align-items: center;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Card } from './Card.svelte';
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
import type { ElementProps } from '../../core/props/element.js';
|
|
4
|
+
import type { Color, Density, Rounded, Variant } from '../../core/props/types.js';
|
|
5
|
+
/** The Card props that `DefaultsProvider` and `UIProvider` can set for a subtree. */
|
|
6
|
+
export interface CardDefaults {
|
|
7
|
+
/**
|
|
8
|
+
* Visual weight: a raised surface by default.
|
|
9
|
+
* @default 'elevated'
|
|
10
|
+
*/
|
|
11
|
+
variant: Variant;
|
|
12
|
+
/**
|
|
13
|
+
* The colour the variant is painted with. Neutral cards use the theme's surface, text and
|
|
14
|
+
* border colours.
|
|
15
|
+
* @default 'neutral'
|
|
16
|
+
*/
|
|
17
|
+
color: Color;
|
|
18
|
+
/**
|
|
19
|
+
* Multiplies the padding and the space between sections.
|
|
20
|
+
* @default 'default'
|
|
21
|
+
*/
|
|
22
|
+
density: Density;
|
|
23
|
+
/**
|
|
24
|
+
* Corner rounding. `true` keeps the theme's radius.
|
|
25
|
+
* @default true
|
|
26
|
+
*/
|
|
27
|
+
rounded: Rounded;
|
|
28
|
+
}
|
|
29
|
+
declare module '../../core/defaults/context.js' {
|
|
30
|
+
interface ComponentDefaults {
|
|
31
|
+
Card: CardDefaults;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* A surface that groups content about one thing: a header, the content and actions. Links
|
|
36
|
+
* and buttons inside it are what is clickable, not the card.
|
|
37
|
+
*/
|
|
38
|
+
export type CardProps = ElementProps<HTMLDivElement, HTMLAttributes<HTMLDivElement>, Partial<CardDefaults> & {
|
|
39
|
+
/** The title, in the header. */
|
|
40
|
+
title?: string;
|
|
41
|
+
/** A line under the title. */
|
|
42
|
+
subtitle?: string;
|
|
43
|
+
/**
|
|
44
|
+
* Makes the title a heading of this level (`<h2>`, `<h3>`…), so it is in the page's
|
|
45
|
+
* outline. Without it the title is styled text.
|
|
46
|
+
*/
|
|
47
|
+
level?: 1 | 2 | 3 | 4 | 5 | 6;
|
|
48
|
+
/** Content before the title, such as an avatar or an icon. */
|
|
49
|
+
prepend?: Snippet;
|
|
50
|
+
/** Content after the title, such as a menu button. */
|
|
51
|
+
append?: Snippet;
|
|
52
|
+
/** Buttons or links at the bottom. */
|
|
53
|
+
actions?: Snippet;
|
|
54
|
+
/** The content. */
|
|
55
|
+
children?: Snippet;
|
|
56
|
+
}>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|