@elysn/astro 0.0.2 → 0.0.4
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 +201 -0
- package/index.ts +46 -0
- package/package.json +36 -16
- package/src/AccessibleIcon.astro +14 -0
- package/src/AspectRatio.astro +14 -0
- package/src/Avatar.astro +54 -0
- package/src/Badge.astro +34 -0
- package/src/Blockquote.astro +21 -0
- package/src/Box.astro +13 -0
- package/src/Button.astro +27 -8
- package/src/Callout.astro +34 -0
- package/src/CalloutIcon.astro +12 -0
- package/src/CalloutText.astro +12 -0
- package/src/Card.astro +30 -0
- package/src/Checkbox.astro +212 -0
- package/src/Code.astro +21 -0
- package/src/Container.astro +26 -0
- package/src/DecoratedBox.astro +22 -0
- package/src/Em.astro +10 -0
- package/src/Flex.astro +25 -0
- package/src/Grid.astro +21 -0
- package/src/Heading.astro +43 -0
- package/src/InputControl.astro +40 -0
- package/src/InputField.astro +10 -0
- package/src/InputSlot.astro +41 -0
- package/src/Kbd.astro +11 -0
- package/src/Link.astro +33 -0
- package/src/Panel.astro +12 -0
- package/src/Progress.astro +63 -0
- package/src/Quote.astro +10 -0
- package/src/RadioCards.astro +71 -0
- package/src/RadioCardsItem.astro +41 -0
- package/src/RadioGroup.astro +70 -0
- package/src/RadioGroupItem.astro +53 -0
- package/src/Section.astro +23 -0
- package/src/SegmentedControl.astro +189 -0
- package/src/Separator.astro +25 -0
- package/src/Skeleton.astro +30 -0
- package/src/Spinner.astro +31 -0
- package/src/Strong.astro +10 -0
- package/src/Switch.astro +164 -0
- package/src/Tabs.astro +179 -0
- package/src/TabsContent.astro +20 -0
- package/src/TabsIndicator.astro +17 -0
- package/src/TabsList.astro +12 -0
- package/src/TabsTrigger.astro +23 -0
- package/src/Text.astro +43 -0
- package/src/TextArea.astro +30 -0
- package/src/TextField.astro +70 -0
- package/src/VisuallyHidden.astro +10 -0
- package/src/internal/radio-group.ts +82 -0
- package/src/internal/ssr.ts +18 -0
- package/src/internal/zag.ts +24 -0
- package/style.css +5 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from "astro/types";
|
|
3
|
+
|
|
4
|
+
interface Props extends Omit<HTMLAttributes<"label">, "value" | "color"> {
|
|
5
|
+
className?: string;
|
|
6
|
+
color?: string;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
value: string;
|
|
9
|
+
}
|
|
10
|
+
const { className, color, disabled, value, ...rest } = Astro.props as Props;
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
<label
|
|
14
|
+
{...rest}
|
|
15
|
+
data-scope="radio-group"
|
|
16
|
+
data-part="item"
|
|
17
|
+
data-state="unchecked"
|
|
18
|
+
data-accent-color={color}
|
|
19
|
+
data-item-value={value}
|
|
20
|
+
data-item-disabled={disabled ? "" : undefined}
|
|
21
|
+
data-disabled={disabled ? "" : undefined}
|
|
22
|
+
class:list={["el-BaseCard el-RadioCardsItem", className]}
|
|
23
|
+
>
|
|
24
|
+
<span
|
|
25
|
+
data-scope="radio-group"
|
|
26
|
+
data-part="item-text"
|
|
27
|
+
data-state="unchecked"
|
|
28
|
+
data-item-value={value}
|
|
29
|
+
>
|
|
30
|
+
<slot />
|
|
31
|
+
</span>
|
|
32
|
+
<input
|
|
33
|
+
type="radio"
|
|
34
|
+
data-part="hidden-input"
|
|
35
|
+
data-state="unchecked"
|
|
36
|
+
data-item-value={value}
|
|
37
|
+
value={value}
|
|
38
|
+
disabled={disabled}
|
|
39
|
+
style="border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;white-space:nowrap;word-wrap:normal"
|
|
40
|
+
/>
|
|
41
|
+
</label>
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from "astro/types";
|
|
3
|
+
import { markChecked } from "./internal/ssr";
|
|
4
|
+
|
|
5
|
+
type Size = "xs" | "sm" | "md" | "lg";
|
|
6
|
+
|
|
7
|
+
interface Props extends Omit<HTMLAttributes<"div">, "color" | "dir" | "name"> {
|
|
8
|
+
className?: string;
|
|
9
|
+
color?: string;
|
|
10
|
+
defaultValue?: string;
|
|
11
|
+
dir?: "ltr" | "rtl";
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
highContrast?: boolean;
|
|
14
|
+
name?: string;
|
|
15
|
+
orientation?: "horizontal" | "vertical";
|
|
16
|
+
radius?: "none" | "small" | "medium" | "large" | "full";
|
|
17
|
+
required?: boolean;
|
|
18
|
+
size?: Size;
|
|
19
|
+
variant?: "surface" | "soft";
|
|
20
|
+
}
|
|
21
|
+
const {
|
|
22
|
+
className,
|
|
23
|
+
color,
|
|
24
|
+
defaultValue,
|
|
25
|
+
dir,
|
|
26
|
+
disabled,
|
|
27
|
+
highContrast,
|
|
28
|
+
name,
|
|
29
|
+
orientation = "vertical",
|
|
30
|
+
radius,
|
|
31
|
+
required,
|
|
32
|
+
size = "md",
|
|
33
|
+
variant = "surface",
|
|
34
|
+
...rest
|
|
35
|
+
} = Astro.props as Props;
|
|
36
|
+
|
|
37
|
+
const sizes: Record<Size, string> = {
|
|
38
|
+
xs: "el-RadioGroup-size-xs",
|
|
39
|
+
sm: "el-RadioGroup-size-sm",
|
|
40
|
+
md: "el-RadioGroup-size-md",
|
|
41
|
+
lg: "el-RadioGroup-size-lg",
|
|
42
|
+
};
|
|
43
|
+
const items = markChecked(await Astro.slots.render("default"), defaultValue);
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
<el-radio-group
|
|
47
|
+
{...rest}
|
|
48
|
+
role="radiogroup"
|
|
49
|
+
data-scope="radio-group"
|
|
50
|
+
data-part="root"
|
|
51
|
+
data-accent-color={color}
|
|
52
|
+
data-default-value={defaultValue}
|
|
53
|
+
data-dir={dir}
|
|
54
|
+
data-disabled={disabled ? "" : undefined}
|
|
55
|
+
data-high-contrast={highContrast ? "true" : undefined}
|
|
56
|
+
data-name={name}
|
|
57
|
+
data-orientation={orientation}
|
|
58
|
+
data-radius={radius}
|
|
59
|
+
data-required={required ? "true" : undefined}
|
|
60
|
+
data-variant={variant}
|
|
61
|
+
class:list={["el-RadioGroup", sizes[size], className]}
|
|
62
|
+
>
|
|
63
|
+
<Fragment set:html={items} />
|
|
64
|
+
</el-radio-group>
|
|
65
|
+
|
|
66
|
+
<script>
|
|
67
|
+
import { defineRadioGroup } from "./internal/radio-group";
|
|
68
|
+
|
|
69
|
+
defineRadioGroup();
|
|
70
|
+
</script>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from "astro/types";
|
|
3
|
+
|
|
4
|
+
interface Props extends Omit<HTMLAttributes<"label">, "value"> {
|
|
5
|
+
className?: string;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
value: string;
|
|
8
|
+
}
|
|
9
|
+
const { className, disabled, value, ...rest } = Astro.props as Props;
|
|
10
|
+
const hasText = Astro.slots.has("default");
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
<label
|
|
14
|
+
{...rest}
|
|
15
|
+
data-scope="radio-group"
|
|
16
|
+
data-part="item"
|
|
17
|
+
data-state="unchecked"
|
|
18
|
+
data-item-value={value}
|
|
19
|
+
data-item-disabled={disabled ? "" : undefined}
|
|
20
|
+
data-disabled={disabled ? "" : undefined}
|
|
21
|
+
class:list={["el-RadioGroup-item", className]}
|
|
22
|
+
>
|
|
23
|
+
<div
|
|
24
|
+
data-scope="radio-group"
|
|
25
|
+
data-part="item-control"
|
|
26
|
+
data-state="unchecked"
|
|
27
|
+
data-item-value={value}
|
|
28
|
+
class="el-RadioGroup-itemControl"
|
|
29
|
+
>
|
|
30
|
+
</div>
|
|
31
|
+
{
|
|
32
|
+
hasText && (
|
|
33
|
+
<span
|
|
34
|
+
data-scope="radio-group"
|
|
35
|
+
data-part="item-text"
|
|
36
|
+
data-state="unchecked"
|
|
37
|
+
data-item-value={value}
|
|
38
|
+
class="el-RadioGroup-itemText"
|
|
39
|
+
>
|
|
40
|
+
<slot />
|
|
41
|
+
</span>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
<input
|
|
45
|
+
type="radio"
|
|
46
|
+
data-part="hidden-input"
|
|
47
|
+
data-state="unchecked"
|
|
48
|
+
data-item-value={value}
|
|
49
|
+
value={value}
|
|
50
|
+
disabled={disabled}
|
|
51
|
+
style="border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;white-space:nowrap;word-wrap:normal"
|
|
52
|
+
/>
|
|
53
|
+
</label>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from "astro/types";
|
|
3
|
+
|
|
4
|
+
type Size = "xs" | "sm" | "md" | "lg";
|
|
5
|
+
|
|
6
|
+
interface Props extends Omit<HTMLAttributes<"div">, "color"> {
|
|
7
|
+
className?: string;
|
|
8
|
+
color?: string;
|
|
9
|
+
size?: Size;
|
|
10
|
+
}
|
|
11
|
+
const { className, color, size = "md", ...rest } = Astro.props as Props;
|
|
12
|
+
|
|
13
|
+
const sizes: Record<Size, string> = {
|
|
14
|
+
xs: "el-Section-size-xs",
|
|
15
|
+
sm: "el-Section-size-sm",
|
|
16
|
+
md: "el-Section-size-md",
|
|
17
|
+
lg: "el-Section-size-lg",
|
|
18
|
+
};
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
<div {...rest} data-accent-color={color} class:list={["el-Section", sizes[size], className]}>
|
|
22
|
+
<slot />
|
|
23
|
+
</div>
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from "astro/types";
|
|
3
|
+
|
|
4
|
+
type Size = "xs" | "sm" | "md" | "lg";
|
|
5
|
+
|
|
6
|
+
interface Item {
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
label: string;
|
|
9
|
+
value: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface Props extends Omit<HTMLAttributes<"div">, "color" | "dir" | "name"> {
|
|
13
|
+
className?: string;
|
|
14
|
+
color?: string;
|
|
15
|
+
defaultValue?: string;
|
|
16
|
+
dir?: "ltr" | "rtl";
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
items: Item[];
|
|
19
|
+
name?: string;
|
|
20
|
+
radius?: "none" | "small" | "medium" | "large" | "full";
|
|
21
|
+
size?: Size;
|
|
22
|
+
}
|
|
23
|
+
const {
|
|
24
|
+
className,
|
|
25
|
+
color,
|
|
26
|
+
defaultValue,
|
|
27
|
+
dir,
|
|
28
|
+
disabled,
|
|
29
|
+
items,
|
|
30
|
+
name,
|
|
31
|
+
radius,
|
|
32
|
+
size = "md",
|
|
33
|
+
...rest
|
|
34
|
+
} = Astro.props as Props;
|
|
35
|
+
|
|
36
|
+
const sizes: Record<Size, string> = {
|
|
37
|
+
xs: "el-SegmentedControl-size-xs",
|
|
38
|
+
sm: "el-SegmentedControl-size-sm",
|
|
39
|
+
md: "el-SegmentedControl-size-md",
|
|
40
|
+
lg: "el-SegmentedControl-size-lg",
|
|
41
|
+
};
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
<el-segmented-control
|
|
45
|
+
{...rest}
|
|
46
|
+
role="radiogroup"
|
|
47
|
+
data-scope="segment-group"
|
|
48
|
+
data-part="root"
|
|
49
|
+
data-accent-color={color}
|
|
50
|
+
data-default-value={defaultValue}
|
|
51
|
+
data-dir={dir}
|
|
52
|
+
data-disabled={disabled ? "" : undefined}
|
|
53
|
+
data-name={name}
|
|
54
|
+
data-orientation="horizontal"
|
|
55
|
+
data-radius={radius}
|
|
56
|
+
class:list={["el-SegmentedControl", sizes[size], className]}
|
|
57
|
+
>
|
|
58
|
+
{
|
|
59
|
+
items.map((item) => {
|
|
60
|
+
const state = item.value === defaultValue ? "checked" : "unchecked";
|
|
61
|
+
return (
|
|
62
|
+
<label
|
|
63
|
+
data-scope="segment-group"
|
|
64
|
+
data-part="item"
|
|
65
|
+
data-state={state}
|
|
66
|
+
data-item-value={item.value}
|
|
67
|
+
data-item-disabled={item.disabled ? "" : undefined}
|
|
68
|
+
data-disabled={item.disabled || disabled ? "" : undefined}
|
|
69
|
+
class="el-SegmentedControl-item"
|
|
70
|
+
>
|
|
71
|
+
<span
|
|
72
|
+
data-scope="segment-group"
|
|
73
|
+
data-part="item-text"
|
|
74
|
+
data-state={state}
|
|
75
|
+
data-text={item.label}
|
|
76
|
+
class="el-SegmentedControl-itemText"
|
|
77
|
+
>
|
|
78
|
+
{item.label}
|
|
79
|
+
</span>
|
|
80
|
+
<input
|
|
81
|
+
type="radio"
|
|
82
|
+
data-part="hidden-input"
|
|
83
|
+
data-item-value={item.value}
|
|
84
|
+
value={item.value}
|
|
85
|
+
checked={state === "checked"}
|
|
86
|
+
disabled={item.disabled || disabled}
|
|
87
|
+
style="border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;white-space:nowrap;word-wrap:normal"
|
|
88
|
+
/>
|
|
89
|
+
</label>
|
|
90
|
+
);
|
|
91
|
+
})
|
|
92
|
+
}
|
|
93
|
+
<div
|
|
94
|
+
data-scope="segment-group"
|
|
95
|
+
data-part="indicator"
|
|
96
|
+
data-orientation="horizontal"
|
|
97
|
+
class="el-SegmentedControl-indicator"
|
|
98
|
+
>
|
|
99
|
+
</div>
|
|
100
|
+
</el-segmented-control>
|
|
101
|
+
|
|
102
|
+
<script>
|
|
103
|
+
import * as zradio from "@zag-js/radio-group";
|
|
104
|
+
import { VanillaMachine, normalizeProps } from "@zag-js/vanilla";
|
|
105
|
+
import { createSpreader, nextId, textDirection } from "./internal/zag";
|
|
106
|
+
|
|
107
|
+
type Machine = ReturnType<typeof createMachine>;
|
|
108
|
+
const createMachine = (props: zradio.Props) => new VanillaMachine(zradio.machine, props);
|
|
109
|
+
|
|
110
|
+
class ElSegmentedControl extends HTMLElement {
|
|
111
|
+
#machine?: Machine;
|
|
112
|
+
#unsubscribe?: () => void;
|
|
113
|
+
#spread = createSpreader("");
|
|
114
|
+
|
|
115
|
+
connectedCallback() {
|
|
116
|
+
if (this.#machine) return;
|
|
117
|
+
const id = nextId("el-segmented-control");
|
|
118
|
+
this.#spread = createSpreader(id);
|
|
119
|
+
const machine = createMachine({
|
|
120
|
+
id,
|
|
121
|
+
defaultValue: this.dataset["defaultValue"] ?? null,
|
|
122
|
+
name: this.dataset["name"],
|
|
123
|
+
disabled: this.hasAttribute("data-disabled"),
|
|
124
|
+
orientation: "horizontal",
|
|
125
|
+
dir: textDirection(this),
|
|
126
|
+
getRootNode: () => this.getRootNode(),
|
|
127
|
+
onValueChange: ({ value }: { value: string | null }) =>
|
|
128
|
+
this.dispatchEvent(
|
|
129
|
+
new CustomEvent("el-segmented-control:value-change", {
|
|
130
|
+
detail: { value },
|
|
131
|
+
bubbles: true,
|
|
132
|
+
}),
|
|
133
|
+
),
|
|
134
|
+
});
|
|
135
|
+
this.#machine = machine;
|
|
136
|
+
// The indicator is positioned from the item rects, which need the ids on the DOM first
|
|
137
|
+
this.#render();
|
|
138
|
+
machine.start();
|
|
139
|
+
this.#render();
|
|
140
|
+
this.#unsubscribe = machine.subscribe(() => this.#render());
|
|
141
|
+
this.dataset["ready"] = "";
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
disconnectedCallback() {
|
|
145
|
+
this.#unsubscribe?.();
|
|
146
|
+
this.#spread.clear();
|
|
147
|
+
this.#machine?.stop();
|
|
148
|
+
this.#machine = undefined;
|
|
149
|
+
delete this.dataset["ready"];
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Programmatic API: `el.value` / `el.value = "b"`. */
|
|
153
|
+
get value() {
|
|
154
|
+
return this.#api()?.value ?? null;
|
|
155
|
+
}
|
|
156
|
+
set value(value: string | null) {
|
|
157
|
+
this.#api()?.setValue(value as string);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
#api() {
|
|
161
|
+
return this.#machine && zradio.connect(this.#machine.service, normalizeProps);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
#render() {
|
|
165
|
+
const api = this.#api();
|
|
166
|
+
if (!api) return;
|
|
167
|
+
const { id: _id, ...rootProps } = api.getRootProps();
|
|
168
|
+
this.#spread.apply(this, rootProps);
|
|
169
|
+
for (const item of this.querySelectorAll<HTMLElement>("[data-part='item']")) {
|
|
170
|
+
const props = {
|
|
171
|
+
value: item.dataset["itemValue"] ?? "",
|
|
172
|
+
disabled: item.hasAttribute("data-item-disabled"),
|
|
173
|
+
};
|
|
174
|
+
const part = (name: string) => item.querySelector<HTMLElement>(`[data-part='${name}']`);
|
|
175
|
+
this.#spread.apply(item, api.getItemProps(props));
|
|
176
|
+
this.#spread.apply(part("item-text"), api.getItemTextProps(props));
|
|
177
|
+
this.#spread.apply(part("hidden-input"), api.getItemHiddenInputProps(props));
|
|
178
|
+
}
|
|
179
|
+
this.#spread.apply(
|
|
180
|
+
this.querySelector("[data-part='indicator']"),
|
|
181
|
+
api.getIndicatorProps(),
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (!customElements.get("el-segmented-control")) {
|
|
187
|
+
customElements.define("el-segmented-control", ElSegmentedControl);
|
|
188
|
+
}
|
|
189
|
+
</script>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from "astro/types";
|
|
3
|
+
|
|
4
|
+
interface Props extends Omit<HTMLAttributes<"div">, "color"> {
|
|
5
|
+
className?: string;
|
|
6
|
+
color?: string;
|
|
7
|
+
decorative?: boolean;
|
|
8
|
+
orientation?: "horizontal" | "vertical";
|
|
9
|
+
size?: "1" | "2" | "3" | "4";
|
|
10
|
+
}
|
|
11
|
+
const { className, color, decorative, orientation = "horizontal", size = "1", ...rest } =
|
|
12
|
+
Astro.props as Props;
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
<div
|
|
16
|
+
{...rest}
|
|
17
|
+
role={decorative ? undefined : "separator"}
|
|
18
|
+
aria-hidden={decorative ? "true" : undefined}
|
|
19
|
+
aria-orientation={decorative ? undefined : orientation}
|
|
20
|
+
data-accent-color={color}
|
|
21
|
+
data-orientation={orientation}
|
|
22
|
+
data-size={size}
|
|
23
|
+
class:list={["el-Separator", className]}
|
|
24
|
+
>
|
|
25
|
+
</div>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from "astro/types";
|
|
3
|
+
|
|
4
|
+
interface Props extends Omit<HTMLAttributes<"span">, "style"> {
|
|
5
|
+
className?: string;
|
|
6
|
+
height?: string;
|
|
7
|
+
loading?: boolean;
|
|
8
|
+
style?: Record<string, string | number>;
|
|
9
|
+
width?: string;
|
|
10
|
+
}
|
|
11
|
+
const { className, height, loading = true, style, width, ...rest } = Astro.props as Props;
|
|
12
|
+
// Like Radix: when not loading, render the bare slot (no wrapper element).
|
|
13
|
+
// Whitespace-only slots are emptied so the `:empty` block placeholder rule still applies.
|
|
14
|
+
const html = loading && Astro.slots.has("default") ? await Astro.slots.render("default") : "";
|
|
15
|
+
const content = html.trim() === "" ? "" : html;
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
{
|
|
19
|
+
loading ? (
|
|
20
|
+
<span
|
|
21
|
+
{...rest}
|
|
22
|
+
data-loading
|
|
23
|
+
class:list={["el-Skeleton", className]}
|
|
24
|
+
style={{ width, height, ...style }}
|
|
25
|
+
set:html={content}
|
|
26
|
+
/>
|
|
27
|
+
) : (
|
|
28
|
+
<slot />
|
|
29
|
+
)
|
|
30
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from "astro/types";
|
|
3
|
+
|
|
4
|
+
type Size = "xs" | "sm" | "md" | "lg" | "xl";
|
|
5
|
+
|
|
6
|
+
interface Props extends HTMLAttributes<"span"> {
|
|
7
|
+
className?: string;
|
|
8
|
+
loading?: boolean;
|
|
9
|
+
size?: Size;
|
|
10
|
+
}
|
|
11
|
+
const { className, loading = true, size = "md", ...rest } = Astro.props as Props;
|
|
12
|
+
|
|
13
|
+
const sizes: Record<Size, string> = {
|
|
14
|
+
xs: "el-Spinner-size-xs",
|
|
15
|
+
sm: "el-Spinner-size-sm",
|
|
16
|
+
md: "el-Spinner-size-md",
|
|
17
|
+
lg: "el-Spinner-size-lg",
|
|
18
|
+
xl: "el-Spinner-size-xl",
|
|
19
|
+
};
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
{
|
|
23
|
+
loading && (
|
|
24
|
+
<span
|
|
25
|
+
{...rest}
|
|
26
|
+
role="status"
|
|
27
|
+
aria-label="Loading"
|
|
28
|
+
class:list={["el-Spinner", sizes[size], className]}
|
|
29
|
+
/>
|
|
30
|
+
)
|
|
31
|
+
}
|
package/src/Strong.astro
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from "astro/types";
|
|
3
|
+
|
|
4
|
+
interface Props extends HTMLAttributes<"strong"> {
|
|
5
|
+
className?: string;
|
|
6
|
+
}
|
|
7
|
+
const { className, ...rest } = Astro.props as Props;
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
<strong {...rest} class:list={["el-Strong", className]}><slot /></strong>
|
package/src/Switch.astro
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from "astro/types";
|
|
3
|
+
|
|
4
|
+
type Size = "xs" | "sm" | "md" | "lg";
|
|
5
|
+
|
|
6
|
+
interface Props extends Omit<HTMLAttributes<"label">, "color" | "dir" | "name"> {
|
|
7
|
+
className?: string;
|
|
8
|
+
color?: string;
|
|
9
|
+
defaultChecked?: boolean;
|
|
10
|
+
dir?: "ltr" | "rtl";
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
highContrast?: boolean;
|
|
13
|
+
name?: string;
|
|
14
|
+
radius?: "none" | "small" | "medium" | "large" | "full";
|
|
15
|
+
required?: boolean;
|
|
16
|
+
size?: Size;
|
|
17
|
+
value?: string;
|
|
18
|
+
variant?: "surface" | "soft";
|
|
19
|
+
}
|
|
20
|
+
const {
|
|
21
|
+
className,
|
|
22
|
+
color,
|
|
23
|
+
defaultChecked,
|
|
24
|
+
dir,
|
|
25
|
+
disabled,
|
|
26
|
+
highContrast,
|
|
27
|
+
name,
|
|
28
|
+
radius,
|
|
29
|
+
required,
|
|
30
|
+
size = "md",
|
|
31
|
+
value = "on",
|
|
32
|
+
variant = "surface",
|
|
33
|
+
...rest
|
|
34
|
+
} = Astro.props as Props;
|
|
35
|
+
|
|
36
|
+
const sizes: Record<Size, string> = {
|
|
37
|
+
xs: "el-Switch-size-xs",
|
|
38
|
+
sm: "el-Switch-size-sm",
|
|
39
|
+
md: "el-Switch-size-md",
|
|
40
|
+
lg: "el-Switch-size-lg",
|
|
41
|
+
};
|
|
42
|
+
const state = defaultChecked ? "checked" : "unchecked";
|
|
43
|
+
const hasLabel = Astro.slots.has("default");
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
<!-- display: contents keeps the Zag root (a <label>) as the visual box -->
|
|
47
|
+
<el-switch
|
|
48
|
+
style="display: contents"
|
|
49
|
+
data-default-checked={defaultChecked ? "true" : undefined}
|
|
50
|
+
data-dir={dir}
|
|
51
|
+
>
|
|
52
|
+
<label
|
|
53
|
+
{...rest}
|
|
54
|
+
data-scope="switch"
|
|
55
|
+
data-part="root"
|
|
56
|
+
data-state={state}
|
|
57
|
+
data-disabled={disabled ? "" : undefined}
|
|
58
|
+
data-accent-color={color}
|
|
59
|
+
data-high-contrast={highContrast ? "true" : undefined}
|
|
60
|
+
data-radius={radius}
|
|
61
|
+
data-variant={variant}
|
|
62
|
+
class:list={["el-Switch", sizes[size], className]}
|
|
63
|
+
>
|
|
64
|
+
<span data-scope="switch" data-part="control" data-state={state} class="el-Switch-control">
|
|
65
|
+
<span data-scope="switch" data-part="thumb" data-state={state} class="el-Switch-thumb"></span>
|
|
66
|
+
</span>
|
|
67
|
+
{
|
|
68
|
+
hasLabel && (
|
|
69
|
+
<span data-scope="switch" data-part="label" data-state={state} class="el-Switch-label">
|
|
70
|
+
<slot />
|
|
71
|
+
</span>
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
<input
|
|
75
|
+
type="checkbox"
|
|
76
|
+
data-part="hidden-input"
|
|
77
|
+
name={name}
|
|
78
|
+
value={value}
|
|
79
|
+
checked={defaultChecked}
|
|
80
|
+
disabled={disabled}
|
|
81
|
+
required={required}
|
|
82
|
+
style="border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;white-space:nowrap;word-wrap:normal"
|
|
83
|
+
/>
|
|
84
|
+
</label>
|
|
85
|
+
</el-switch>
|
|
86
|
+
|
|
87
|
+
<script>
|
|
88
|
+
import * as zswitch from "@zag-js/switch";
|
|
89
|
+
import { VanillaMachine, normalizeProps } from "@zag-js/vanilla";
|
|
90
|
+
import { createSpreader, nextId, textDirection } from "./internal/zag";
|
|
91
|
+
|
|
92
|
+
type Machine = ReturnType<typeof createMachine>;
|
|
93
|
+
const createMachine = (props: zswitch.Props) => new VanillaMachine(zswitch.machine, props);
|
|
94
|
+
|
|
95
|
+
class ElSwitch extends HTMLElement {
|
|
96
|
+
#machine?: Machine;
|
|
97
|
+
#unsubscribe?: () => void;
|
|
98
|
+
#spread = createSpreader("");
|
|
99
|
+
|
|
100
|
+
connectedCallback() {
|
|
101
|
+
if (this.#machine) return;
|
|
102
|
+
const id = nextId("el-switch");
|
|
103
|
+
this.#spread = createSpreader(id);
|
|
104
|
+
const input = this.#part("hidden-input") as HTMLInputElement | undefined;
|
|
105
|
+
const machine = createMachine({
|
|
106
|
+
id,
|
|
107
|
+
defaultChecked: this.dataset["defaultChecked"] === "true",
|
|
108
|
+
disabled: input?.disabled,
|
|
109
|
+
required: input?.required,
|
|
110
|
+
name: input?.name || undefined,
|
|
111
|
+
value: input?.value,
|
|
112
|
+
dir: textDirection(this),
|
|
113
|
+
getRootNode: () => this.getRootNode(),
|
|
114
|
+
onCheckedChange: ({ checked }: { checked: boolean }) =>
|
|
115
|
+
this.dispatchEvent(
|
|
116
|
+
new CustomEvent("el-switch:checked-change", { detail: { checked }, bubbles: true }),
|
|
117
|
+
),
|
|
118
|
+
});
|
|
119
|
+
this.#machine = machine;
|
|
120
|
+
machine.start();
|
|
121
|
+
this.#render();
|
|
122
|
+
this.#unsubscribe = machine.subscribe(() => this.#render());
|
|
123
|
+
this.dataset["ready"] = "";
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
disconnectedCallback() {
|
|
127
|
+
this.#unsubscribe?.();
|
|
128
|
+
this.#spread.clear();
|
|
129
|
+
this.#machine?.stop();
|
|
130
|
+
this.#machine = undefined;
|
|
131
|
+
delete this.dataset["ready"];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** Programmatic API: `el.checked` / `el.checked = true`. */
|
|
135
|
+
get checked() {
|
|
136
|
+
return this.#api()?.checked ?? false;
|
|
137
|
+
}
|
|
138
|
+
set checked(checked: boolean) {
|
|
139
|
+
this.#api()?.setChecked(checked);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
#api() {
|
|
143
|
+
return this.#machine && zswitch.connect(this.#machine.service, normalizeProps);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
#part(name: string) {
|
|
147
|
+
return this.querySelector<HTMLElement>(`[data-part='${name}']`) ?? undefined;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
#render() {
|
|
151
|
+
const api = this.#api();
|
|
152
|
+
if (!api) return;
|
|
153
|
+
// Zag never looks the root up by id, so keep the author's `id` instead of its generated one
|
|
154
|
+
const { id: _id, ...rootProps } = api.getRootProps();
|
|
155
|
+
this.#spread.apply(this.#part("root"), rootProps);
|
|
156
|
+
this.#spread.apply(this.#part("control"), api.getControlProps());
|
|
157
|
+
this.#spread.apply(this.#part("thumb"), api.getThumbProps());
|
|
158
|
+
this.#spread.apply(this.#part("label"), api.getLabelProps());
|
|
159
|
+
this.#spread.apply(this.#part("hidden-input"), api.getHiddenInputProps());
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (!customElements.get("el-switch")) customElements.define("el-switch", ElSwitch);
|
|
164
|
+
</script>
|