@sveltier/ui 0.0.0-stage → 0.1.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +82 -2
- package/dist/components/Accordion/Accordion.svelte +74 -0
- package/dist/components/Accordion/Accordion.svelte.d.ts +25 -0
- package/dist/components/Accordion/accordion.css +88 -0
- package/dist/components/Accordion/index.d.ts +2 -0
- package/dist/components/Accordion/index.js +1 -0
- package/dist/components/Accordion/types.d.ts +64 -0
- package/dist/components/Accordion/types.js +1 -0
- 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/Dialog/Dialog.svelte +36 -0
- package/dist/components/Dialog/Dialog.svelte.d.ts +4 -0
- package/dist/components/Dialog/index.d.ts +2 -0
- package/dist/components/Dialog/index.js +1 -0
- package/dist/components/Dialog/types.d.ts +50 -0
- package/dist/components/Dialog/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/Menu/Menu.svelte +195 -0
- package/dist/components/Menu/Menu.svelte.d.ts +4 -0
- package/dist/components/Menu/index.d.ts +2 -0
- package/dist/components/Menu/index.js +1 -0
- package/dist/components/Menu/menu.css +103 -0
- package/dist/components/Menu/types.d.ts +90 -0
- package/dist/components/Menu/types.js +1 -0
- package/dist/components/Modal/Modal.svelte +146 -0
- package/dist/components/Modal/Modal.svelte.d.ts +4 -0
- package/dist/components/Modal/modal.css +150 -0
- package/dist/components/Modal/types.d.ts +54 -0
- package/dist/components/Modal/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/Popover/Popover.svelte +115 -0
- package/dist/components/Popover/Popover.svelte.d.ts +4 -0
- package/dist/components/Popover/index.d.ts +2 -0
- package/dist/components/Popover/index.js +1 -0
- package/dist/components/Popover/popover.css +67 -0
- package/dist/components/Popover/types.d.ts +83 -0
- package/dist/components/Popover/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/Sheet/Sheet.svelte +56 -0
- package/dist/components/Sheet/Sheet.svelte.d.ts +4 -0
- package/dist/components/Sheet/index.d.ts +2 -0
- package/dist/components/Sheet/index.js +2 -0
- package/dist/components/Sheet/sheet.css +61 -0
- package/dist/components/Sheet/types.d.ts +51 -0
- package/dist/components/Sheet/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/components/Tooltip/Tooltip.svelte +161 -0
- package/dist/components/Tooltip/Tooltip.svelte.d.ts +4 -0
- package/dist/components/Tooltip/index.d.ts +2 -0
- package/dist/components/Tooltip/index.js +1 -0
- package/dist/components/Tooltip/tooltip.css +44 -0
- package/dist/components/Tooltip/types.d.ts +55 -0
- package/dist/components/Tooltip/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 +124 -0
- package/dist/core/errors/codes.js +108 -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 +56 -0
- package/dist/core/locale/locale.js +82 -0
- package/dist/core/overlay/types.d.ts +33 -0
- package/dist/core/overlay/types.js +1 -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 +63 -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 +73 -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/core/toast/ToastOutlet.svelte +192 -0
- package/dist/core/toast/ToastOutlet.svelte.d.ts +11 -0
- package/dist/core/toast/ToastProvider.svelte +39 -0
- package/dist/core/toast/ToastProvider.svelte.d.ts +19 -0
- package/dist/core/toast/toast.css +134 -0
- package/dist/core/toast/toast.svelte.d.ts +117 -0
- package/dist/core/toast/toast.svelte.js +133 -0
- package/dist/dev/off.d.ts +5 -0
- package/dist/dev/off.js +4 -0
- package/dist/dev/on.d.ts +20 -0
- package/dist/dev/on.js +49 -0
- package/dist/index.d.ts +67 -0
- package/dist/index.js +83 -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 +32 -0
- package/dist/primitives/layer/dismissable.js +91 -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 +140 -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,34 @@
|
|
|
1
|
+
@layer sv.tokens, sv.base, sv.components, sv.utilities;
|
|
2
|
+
|
|
3
|
+
@layer sv.components {
|
|
4
|
+
/* As tall as a control of its size; initials are φ⁻² of that. */
|
|
5
|
+
.sv-avatar {
|
|
6
|
+
position: relative;
|
|
7
|
+
display: inline-flex;
|
|
8
|
+
flex: none;
|
|
9
|
+
align-items: center;
|
|
10
|
+
justify-content: center;
|
|
11
|
+
box-sizing: border-box;
|
|
12
|
+
inline-size: var(--sv-size-height);
|
|
13
|
+
block-size: var(--sv-size-height);
|
|
14
|
+
overflow: hidden;
|
|
15
|
+
border: var(--sv-hairline) solid var(--sv-variant-border);
|
|
16
|
+
border-radius: var(--sv-rounded, var(--sv-radius-full));
|
|
17
|
+
background-color: var(--sv-variant-bg);
|
|
18
|
+
box-shadow: var(--sv-variant-shadow);
|
|
19
|
+
color: var(--sv-variant-fg);
|
|
20
|
+
font: var(--sv-weight-medium) calc(var(--sv-size-height) * 0.382) / 1 var(--sv-font-ui);
|
|
21
|
+
letter-spacing: 0.02em;
|
|
22
|
+
text-transform: uppercase;
|
|
23
|
+
vertical-align: middle;
|
|
24
|
+
user-select: none;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.sv-avatar__image {
|
|
28
|
+
position: absolute;
|
|
29
|
+
inset: 0;
|
|
30
|
+
inline-size: 100%;
|
|
31
|
+
block-size: 100%;
|
|
32
|
+
object-fit: cover;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Avatar } from './Avatar.svelte';
|
|
@@ -0,0 +1,47 @@
|
|
|
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, Rounded, Size, Variant } from '../../core/props/types.js';
|
|
5
|
+
/** The Avatar props that `DefaultsProvider` and `UIProvider` can set for a subtree. */
|
|
6
|
+
export interface AvatarDefaults {
|
|
7
|
+
/**
|
|
8
|
+
* Width and height: the control height of the size.
|
|
9
|
+
* @default 'md'
|
|
10
|
+
*/
|
|
11
|
+
size: Size;
|
|
12
|
+
/**
|
|
13
|
+
* How the fallback is painted.
|
|
14
|
+
* @default 'tonal'
|
|
15
|
+
*/
|
|
16
|
+
variant: Variant;
|
|
17
|
+
/**
|
|
18
|
+
* The colour of the fallback.
|
|
19
|
+
* @default 'neutral'
|
|
20
|
+
*/
|
|
21
|
+
color: Color;
|
|
22
|
+
/**
|
|
23
|
+
* Corner rounding.
|
|
24
|
+
* @default 'full'
|
|
25
|
+
*/
|
|
26
|
+
rounded: Rounded;
|
|
27
|
+
}
|
|
28
|
+
declare module '../../core/defaults/context.js' {
|
|
29
|
+
interface ComponentDefaults {
|
|
30
|
+
Avatar: AvatarDefaults;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* A picture of a person or an account: an image, or initials or an icon when there is none
|
|
35
|
+
* or it fails to load.
|
|
36
|
+
*/
|
|
37
|
+
export type AvatarProps = ElementProps<HTMLSpanElement, HTMLAttributes<HTMLSpanElement>, Partial<AvatarDefaults> & {
|
|
38
|
+
/** The image. */
|
|
39
|
+
src?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Who or what the avatar shows, for screen readers. Leave it out when a name is shown next
|
|
42
|
+
* to the avatar: it is then decorative and hidden from them.
|
|
43
|
+
*/
|
|
44
|
+
alt?: string;
|
|
45
|
+
/** Initials or an icon, shown while there is no image or it failed to load. */
|
|
46
|
+
children?: Snippet;
|
|
47
|
+
}>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// A count or a dot on its content, or alone in a line. See BadgeProps for the props.
|
|
3
|
+
import { useDefaults } from '../../core/defaults/context.js';
|
|
4
|
+
import { sharedClasses } from '../../core/props/classes.js';
|
|
5
|
+
import '../../styles/visually-hidden.css';
|
|
6
|
+
import './badge.css';
|
|
7
|
+
import type { BadgeDefaults, BadgeProps } from './types.js';
|
|
8
|
+
|
|
9
|
+
let {
|
|
10
|
+
color,
|
|
11
|
+
max,
|
|
12
|
+
content,
|
|
13
|
+
dot = false,
|
|
14
|
+
label,
|
|
15
|
+
children,
|
|
16
|
+
id,
|
|
17
|
+
class: className,
|
|
18
|
+
ref = $bindable(null),
|
|
19
|
+
...rest
|
|
20
|
+
}: BadgeProps = $props();
|
|
21
|
+
|
|
22
|
+
const resolve = useDefaults<BadgeDefaults>('Badge', { color: 'primary', max: 99 });
|
|
23
|
+
const p = $derived(resolve({ color, max }));
|
|
24
|
+
|
|
25
|
+
const visible = $derived(dot || (content != null && content !== 0 && content !== ''));
|
|
26
|
+
const shown = $derived(
|
|
27
|
+
typeof content === 'number' && content > p.max ? `${p.max}+` : String(content ?? '')
|
|
28
|
+
);
|
|
29
|
+
// Screen readers hear the real count, with what it counts.
|
|
30
|
+
const spoken = $derived(dot ? label : [content, label].filter((part) => part != null).join(' '));
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<span
|
|
34
|
+
bind:this={ref}
|
|
35
|
+
{...rest}
|
|
36
|
+
{id}
|
|
37
|
+
class={['sv-badge', !children && 'sv-badge--inline', className]}
|
|
38
|
+
>
|
|
39
|
+
{@render children?.()}
|
|
40
|
+
{#if visible}
|
|
41
|
+
<span class="sv-badge__anchor" aria-hidden="true">
|
|
42
|
+
<span
|
|
43
|
+
class={[
|
|
44
|
+
'sv-badge__badge',
|
|
45
|
+
sharedClasses({ variant: 'flat', color: p.color }, 'Badge'),
|
|
46
|
+
dot && 'sv-badge__badge--dot'
|
|
47
|
+
]}>{dot ? '' : shown}</span
|
|
48
|
+
>
|
|
49
|
+
</span>
|
|
50
|
+
<!-- With an id, the content can point at this text: aria-describedby="{id}-label". -->
|
|
51
|
+
{#if spoken}<span class="sv-visually-hidden" id={id && `${id}-label`}>{spoken}</span>{/if}
|
|
52
|
+
{/if}
|
|
53
|
+
</span>
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
@layer sv.tokens, sv.base, sv.components, sv.utilities;
|
|
2
|
+
|
|
3
|
+
@layer sv.components {
|
|
4
|
+
.sv-badge {
|
|
5
|
+
position: relative;
|
|
6
|
+
display: inline-flex;
|
|
7
|
+
flex: none;
|
|
8
|
+
vertical-align: middle;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/*
|
|
12
|
+
* A point at the top end corner of the content. The badge is centred on it by flex
|
|
13
|
+
* alignment, overflowing evenly, so it follows the writing direction with no direction
|
|
14
|
+
* selector (minifiers rewrite :dir() into language guesses for older browsers).
|
|
15
|
+
*/
|
|
16
|
+
.sv-badge__anchor {
|
|
17
|
+
position: absolute;
|
|
18
|
+
inset-block-start: 0;
|
|
19
|
+
inset-inline-end: 0;
|
|
20
|
+
display: flex;
|
|
21
|
+
align-items: center;
|
|
22
|
+
justify-content: center;
|
|
23
|
+
inline-size: 0;
|
|
24
|
+
block-size: 0;
|
|
25
|
+
pointer-events: none;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.sv-badge--inline .sv-badge__anchor {
|
|
29
|
+
position: static;
|
|
30
|
+
display: contents;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* A pill φ times its text. */
|
|
34
|
+
.sv-badge__badge {
|
|
35
|
+
display: inline-flex;
|
|
36
|
+
flex: none;
|
|
37
|
+
align-items: center;
|
|
38
|
+
justify-content: center;
|
|
39
|
+
box-sizing: border-box;
|
|
40
|
+
min-inline-size: 1.618em;
|
|
41
|
+
block-size: 1.618em;
|
|
42
|
+
padding-inline: 0.382em;
|
|
43
|
+
border-radius: var(--sv-radius-full);
|
|
44
|
+
background-color: var(--sv-variant-bg);
|
|
45
|
+
color: var(--sv-variant-fg);
|
|
46
|
+
font: var(--sv-weight-medium) var(--sv-text-label) / 1 var(--sv-font-ui);
|
|
47
|
+
font-variant-numeric: tabular-nums;
|
|
48
|
+
white-space: nowrap;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.sv-badge__badge--dot {
|
|
52
|
+
min-inline-size: 0.618em;
|
|
53
|
+
inline-size: 0.618em;
|
|
54
|
+
block-size: 0.618em;
|
|
55
|
+
padding: 0;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Badge } from './Badge.svelte';
|
|
@@ -0,0 +1,43 @@
|
|
|
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 } from '../../core/props/types.js';
|
|
5
|
+
/** The Badge props that `DefaultsProvider` and `UIProvider` can set for a subtree. */
|
|
6
|
+
export interface BadgeDefaults {
|
|
7
|
+
/**
|
|
8
|
+
* The badge's fill.
|
|
9
|
+
* @default 'primary'
|
|
10
|
+
*/
|
|
11
|
+
color: Color;
|
|
12
|
+
/**
|
|
13
|
+
* Counts above it show as `max+`.
|
|
14
|
+
* @default 99
|
|
15
|
+
*/
|
|
16
|
+
max: number;
|
|
17
|
+
}
|
|
18
|
+
declare module '../../core/defaults/context.js' {
|
|
19
|
+
interface ComponentDefaults {
|
|
20
|
+
Badge: BadgeDefaults;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* A count or a dot pinned to the top end corner of its content, or standing alone in a line
|
|
25
|
+
* when it has none. A count of 0 hides it.
|
|
26
|
+
*/
|
|
27
|
+
export type BadgeProps = ElementProps<HTMLSpanElement, HTMLAttributes<HTMLSpanElement>, Partial<BadgeDefaults> & {
|
|
28
|
+
/** What the badge says: usually a count. */
|
|
29
|
+
content?: number | string;
|
|
30
|
+
/**
|
|
31
|
+
* A dot instead of the content.
|
|
32
|
+
* @default false
|
|
33
|
+
*/
|
|
34
|
+
dot?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* What the content counts, read after it by screen readers: `unread messages` reads
|
|
37
|
+
* "3 unread messages". With `dot`, the whole announcement. With an `id`, this text has
|
|
38
|
+
* the id `{id}-label`, for the `aria-describedby` of a button the badge is pinned to.
|
|
39
|
+
*/
|
|
40
|
+
label?: string;
|
|
41
|
+
/** The content the badge is pinned to. */
|
|
42
|
+
children?: Snippet;
|
|
43
|
+
}>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -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
|
+
}
|