@elysn/astro 0.0.3 → 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/index.ts +46 -0
- package/package.json +21 -5
- 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 +2 -2
- 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
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from "astro/types";
|
|
3
|
+
|
|
4
|
+
interface Props extends Omit<HTMLAttributes<"button">, "value"> {
|
|
5
|
+
className?: string;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
value: string;
|
|
8
|
+
}
|
|
9
|
+
const { className, disabled, value, ...rest } = Astro.props as Props;
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
<button
|
|
13
|
+
{...rest}
|
|
14
|
+
role="tab"
|
|
15
|
+
type="button"
|
|
16
|
+
disabled={disabled}
|
|
17
|
+
data-scope="tabs"
|
|
18
|
+
data-part="trigger"
|
|
19
|
+
data-value={value}
|
|
20
|
+
class:list={["el-Tabs-trigger", className]}
|
|
21
|
+
>
|
|
22
|
+
<slot />
|
|
23
|
+
</button>
|
package/src/Text.astro
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from "astro/types";
|
|
3
|
+
|
|
4
|
+
interface Props extends Omit<HTMLAttributes<"p">, "color"> {
|
|
5
|
+
align?: "left" | "center" | "right";
|
|
6
|
+
as?: "p" | "span" | "div" | "label" | "b" | "i" | "s" | "u";
|
|
7
|
+
className?: string;
|
|
8
|
+
color?: string;
|
|
9
|
+
highContrast?: boolean;
|
|
10
|
+
size?: "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9";
|
|
11
|
+
trim?: "normal" | "start" | "end" | "both";
|
|
12
|
+
truncate?: boolean;
|
|
13
|
+
weight?: "light" | "regular" | "medium" | "bold";
|
|
14
|
+
wrap?: "normal" | "nowrap" | "pretty" | "balance";
|
|
15
|
+
}
|
|
16
|
+
const {
|
|
17
|
+
align,
|
|
18
|
+
as: Tag = "p",
|
|
19
|
+
className,
|
|
20
|
+
color,
|
|
21
|
+
highContrast,
|
|
22
|
+
size,
|
|
23
|
+
trim,
|
|
24
|
+
truncate,
|
|
25
|
+
weight,
|
|
26
|
+
wrap,
|
|
27
|
+
...rest
|
|
28
|
+
} = Astro.props as Props;
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
<Tag
|
|
32
|
+
{...rest}
|
|
33
|
+
data-accent-color={color}
|
|
34
|
+
data-align={align}
|
|
35
|
+
data-high-contrast={highContrast ? true : undefined}
|
|
36
|
+
data-size={size}
|
|
37
|
+
data-trim={trim}
|
|
38
|
+
data-weight={weight}
|
|
39
|
+
data-wrap={wrap}
|
|
40
|
+
class:list={["el-Text", truncate && "el-Text-truncate", className]}
|
|
41
|
+
>
|
|
42
|
+
<slot />
|
|
43
|
+
</Tag>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from "astro/types";
|
|
3
|
+
|
|
4
|
+
type Size = "xs" | "sm" | "md" | "lg";
|
|
5
|
+
|
|
6
|
+
interface Props extends Omit<HTMLAttributes<"textarea">, "color"> {
|
|
7
|
+
className?: string;
|
|
8
|
+
color?: string;
|
|
9
|
+
resize?: "none" | "vertical" | "horizontal" | "both";
|
|
10
|
+
size?: Size;
|
|
11
|
+
variant?: "soft" | "surface" | "outline";
|
|
12
|
+
}
|
|
13
|
+
const { className, color, resize = "vertical", size = "md", variant = "surface", ...rest } =
|
|
14
|
+
Astro.props as Props;
|
|
15
|
+
|
|
16
|
+
const sizes: Record<Size, string> = {
|
|
17
|
+
xs: "el-TextArea-size-xs",
|
|
18
|
+
sm: "el-TextArea-size-sm",
|
|
19
|
+
md: "el-TextArea-size-md",
|
|
20
|
+
lg: "el-TextArea-size-lg",
|
|
21
|
+
};
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
<textarea
|
|
25
|
+
{...rest}
|
|
26
|
+
data-accent-color={color}
|
|
27
|
+
data-resize={resize}
|
|
28
|
+
data-variant={variant}
|
|
29
|
+
class:list={["el-TextArea", sizes[size], className]}><slot /></textarea
|
|
30
|
+
>
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from "astro/types";
|
|
3
|
+
|
|
4
|
+
type Size = "xs" | "sm" | "md" | "lg" | "xl";
|
|
5
|
+
|
|
6
|
+
interface Props extends Omit<HTMLAttributes<"div">, "color"> {
|
|
7
|
+
className?: string;
|
|
8
|
+
color?: string;
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
errorText?: string;
|
|
11
|
+
helperText?: string;
|
|
12
|
+
invalid?: boolean;
|
|
13
|
+
label?: string;
|
|
14
|
+
required?: boolean;
|
|
15
|
+
size?: Size;
|
|
16
|
+
variant?: "surface" | "soft";
|
|
17
|
+
}
|
|
18
|
+
const {
|
|
19
|
+
className,
|
|
20
|
+
color,
|
|
21
|
+
disabled,
|
|
22
|
+
errorText,
|
|
23
|
+
helperText,
|
|
24
|
+
invalid,
|
|
25
|
+
label,
|
|
26
|
+
required,
|
|
27
|
+
size = "md",
|
|
28
|
+
variant = "surface",
|
|
29
|
+
...rest
|
|
30
|
+
} = Astro.props as Props;
|
|
31
|
+
|
|
32
|
+
const sizes: Record<Size, string> = {
|
|
33
|
+
xs: "el-TextField-size-xs",
|
|
34
|
+
sm: "el-TextField-size-sm",
|
|
35
|
+
md: "el-TextField-size-md",
|
|
36
|
+
lg: "el-TextField-size-lg",
|
|
37
|
+
xl: "el-TextField-size-xl",
|
|
38
|
+
};
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
<div
|
|
42
|
+
{...rest}
|
|
43
|
+
data-accent-color={color}
|
|
44
|
+
data-disabled={disabled || undefined}
|
|
45
|
+
data-invalid={invalid || undefined}
|
|
46
|
+
data-required={required ? true : undefined}
|
|
47
|
+
data-variant={variant}
|
|
48
|
+
class:list={["el-TextField", sizes[size], className]}
|
|
49
|
+
>
|
|
50
|
+
{
|
|
51
|
+
label && (
|
|
52
|
+
<label class="el-TextField-label">
|
|
53
|
+
{label}
|
|
54
|
+
{required && (
|
|
55
|
+
<span aria-hidden="true" class="el-TextField-required">
|
|
56
|
+
*
|
|
57
|
+
</span>
|
|
58
|
+
)}
|
|
59
|
+
</label>
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
<div class="el-TextField-control"><slot /></div>
|
|
63
|
+
{
|
|
64
|
+
invalid && errorText ? (
|
|
65
|
+
<p class="el-TextField-error">{errorText}</p>
|
|
66
|
+
) : helperText ? (
|
|
67
|
+
<p class="el-TextField-helper">{helperText}</p>
|
|
68
|
+
) : null
|
|
69
|
+
}
|
|
70
|
+
</div>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from "astro/types";
|
|
3
|
+
|
|
4
|
+
interface Props extends HTMLAttributes<"span"> {
|
|
5
|
+
className?: string;
|
|
6
|
+
}
|
|
7
|
+
const { className, ...rest } = Astro.props as Props;
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
<span {...rest} class:list={["el-VisuallyHidden", className]}><slot /></span>
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import * as zradio from "@zag-js/radio-group";
|
|
2
|
+
import { VanillaMachine, normalizeProps } from "@zag-js/vanilla";
|
|
3
|
+
import { createSpreader, nextId, textDirection } from "./zag";
|
|
4
|
+
|
|
5
|
+
type Machine = ReturnType<typeof createMachine>;
|
|
6
|
+
const createMachine = (props: zradio.Props) => new VanillaMachine(zradio.machine, props);
|
|
7
|
+
|
|
8
|
+
class ElRadioGroup extends HTMLElement {
|
|
9
|
+
#machine?: Machine;
|
|
10
|
+
#unsubscribe?: () => void;
|
|
11
|
+
#spread = createSpreader("");
|
|
12
|
+
|
|
13
|
+
connectedCallback() {
|
|
14
|
+
if (this.#machine) return;
|
|
15
|
+
const id = nextId("el-radio-group");
|
|
16
|
+
this.#spread = createSpreader(id);
|
|
17
|
+
const machine = createMachine({
|
|
18
|
+
id,
|
|
19
|
+
defaultValue: this.dataset["defaultValue"] ?? null,
|
|
20
|
+
name: this.dataset["name"],
|
|
21
|
+
disabled: this.hasAttribute("data-disabled"),
|
|
22
|
+
required: this.dataset["required"] === "true",
|
|
23
|
+
orientation: this.dataset["orientation"] === "horizontal" ? "horizontal" : "vertical",
|
|
24
|
+
dir: textDirection(this),
|
|
25
|
+
getRootNode: () => this.getRootNode(),
|
|
26
|
+
onValueChange: ({ value }: { value: string | null }) =>
|
|
27
|
+
this.dispatchEvent(
|
|
28
|
+
new CustomEvent("el-radio-group:value-change", { detail: { value }, bubbles: true }),
|
|
29
|
+
),
|
|
30
|
+
});
|
|
31
|
+
this.#machine = machine;
|
|
32
|
+
machine.start();
|
|
33
|
+
this.#render();
|
|
34
|
+
this.#unsubscribe = machine.subscribe(() => this.#render());
|
|
35
|
+
this.dataset["ready"] = "";
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
disconnectedCallback() {
|
|
39
|
+
this.#unsubscribe?.();
|
|
40
|
+
this.#spread.clear();
|
|
41
|
+
this.#machine?.stop();
|
|
42
|
+
this.#machine = undefined;
|
|
43
|
+
delete this.dataset["ready"];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Programmatic API: `el.value` / `el.value = "b"`. */
|
|
47
|
+
get value() {
|
|
48
|
+
return this.#api()?.value ?? null;
|
|
49
|
+
}
|
|
50
|
+
set value(value: string | null) {
|
|
51
|
+
this.#api()?.setValue(value as string);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
#api() {
|
|
55
|
+
return this.#machine && zradio.connect(this.#machine.service, normalizeProps);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
#render() {
|
|
59
|
+
const api = this.#api();
|
|
60
|
+
if (!api) return;
|
|
61
|
+
// Keep the author's `id`, and `role`/`dir` come from the markup
|
|
62
|
+
const { id: _id, ...rootProps } = api.getRootProps();
|
|
63
|
+
this.#spread.apply(this, rootProps);
|
|
64
|
+
for (const item of this.querySelectorAll<HTMLElement>("[data-part='item']")) {
|
|
65
|
+
if (item.closest("el-radio-group") !== this) continue;
|
|
66
|
+
const props = {
|
|
67
|
+
value: item.dataset["itemValue"] ?? "",
|
|
68
|
+
disabled: item.hasAttribute("data-item-disabled"),
|
|
69
|
+
};
|
|
70
|
+
const part = (name: string) => item.querySelector<HTMLElement>(`[data-part='${name}']`);
|
|
71
|
+
this.#spread.apply(item, api.getItemProps(props));
|
|
72
|
+
this.#spread.apply(part("item-text"), api.getItemTextProps(props));
|
|
73
|
+
this.#spread.apply(part("item-control"), api.getItemControlProps(props));
|
|
74
|
+
this.#spread.apply(part("hidden-input"), api.getItemHiddenInputProps(props));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Registers `<el-radio-group>`. Called explicitly: the package is `sideEffects: false`. */
|
|
80
|
+
export function defineRadioGroup() {
|
|
81
|
+
if (!customElements.get("el-radio-group")) customElements.define("el-radio-group", ElRadioGroup);
|
|
82
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const escapeAttr = (value: string) => value.replaceAll("&", "&").replaceAll('"', """);
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Pre-hydration state for item-based controls. Astro has no parent -> child context, so items
|
|
5
|
+
* always render unchecked and the root patches the rendered HTML: every tag carrying
|
|
6
|
+
* `data-item-value="<value>"` becomes checked (and its hidden input gets `checked`).
|
|
7
|
+
*/
|
|
8
|
+
export function markChecked(html: string, value: string | undefined) {
|
|
9
|
+
if (value === undefined) return html;
|
|
10
|
+
const needle = `data-item-value="${escapeAttr(value)}"`;
|
|
11
|
+
return html.replaceAll(/<[^<>]*>/gu, (tag) => {
|
|
12
|
+
if (!tag.includes(needle)) return tag;
|
|
13
|
+
const next = tag.replace('data-state="unchecked"', 'data-state="checked"');
|
|
14
|
+
return tag.includes('data-part="hidden-input"')
|
|
15
|
+
? next.replace("<input", "<input checked")
|
|
16
|
+
: next;
|
|
17
|
+
});
|
|
18
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { spreadProps } from "@zag-js/vanilla";
|
|
2
|
+
|
|
3
|
+
let instances = 0;
|
|
4
|
+
|
|
5
|
+
/** Unique per connection so cloned markup never collides on Zag-derived ids. */
|
|
6
|
+
export const nextId = (prefix: string) => `${prefix}-${++instances}`;
|
|
7
|
+
|
|
8
|
+
/** Applies Zag prop objects to DOM elements and tracks the cleanups for `disconnectedCallback`. */
|
|
9
|
+
export function createSpreader(id: string) {
|
|
10
|
+
const cleanups = new Map<Element, () => void>();
|
|
11
|
+
return {
|
|
12
|
+
apply(el: Element | null | undefined, props: object) {
|
|
13
|
+
if (!el) return;
|
|
14
|
+
cleanups.set(el, spreadProps(el as HTMLElement, props as Record<string, unknown>, id));
|
|
15
|
+
},
|
|
16
|
+
clear() {
|
|
17
|
+
cleanups.forEach((fn) => fn());
|
|
18
|
+
cleanups.clear();
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const textDirection = (el: Element) =>
|
|
24
|
+
el.getAttribute("data-dir") === "rtl" || getComputedStyle(el).direction === "rtl" ? "rtl" : "ltr";
|