@xtyle/astro 0.5.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/README.md +42 -0
- package/package.json +25 -0
- package/src/Accordion.astro +79 -0
- package/src/Alert.astro +57 -0
- package/src/AppShell.astro +97 -0
- package/src/Avatar.astro +71 -0
- package/src/AvatarGroup.astro +44 -0
- package/src/Badge.astro +73 -0
- package/src/Bar.astro +83 -0
- package/src/Breadcrumb.astro +54 -0
- package/src/Button.astro +91 -0
- package/src/Card.astro +44 -0
- package/src/CardLink.astro +60 -0
- package/src/Carousel.astro +39 -0
- package/src/Checkbox.astro +68 -0
- package/src/CheckboxGroup.astro +54 -0
- package/src/Cluster.astro +46 -0
- package/src/Code.astro +42 -0
- package/src/ColorPicker.astro +184 -0
- package/src/Dialog.astro +69 -0
- package/src/Dock.astro +79 -0
- package/src/DockZone.astro +17 -0
- package/src/Eyebrow.astro +30 -0
- package/src/Field.astro +117 -0
- package/src/FormGroup.astro +71 -0
- package/src/Grid.astro +57 -0
- package/src/Heading.astro +34 -0
- package/src/Heatmap.astro +116 -0
- package/src/Hero.astro +19 -0
- package/src/Icon.astro +53 -0
- package/src/Image.astro +56 -0
- package/src/Kbd.astro +27 -0
- package/src/Link.astro +56 -0
- package/src/Menu.astro +38 -0
- package/src/NumberInput.astro +86 -0
- package/src/Pagination.astro +53 -0
- package/src/Panel.astro +68 -0
- package/src/Parallax.astro +18 -0
- package/src/Pie.astro +65 -0
- package/src/Progress.astro +96 -0
- package/src/Radio.astro +71 -0
- package/src/RadioGroup.astro +44 -0
- package/src/Section.astro +61 -0
- package/src/Segment.astro +15 -0
- package/src/Segmented.astro +95 -0
- package/src/Select.astro +79 -0
- package/src/Separator.astro +36 -0
- package/src/Skeleton.astro +25 -0
- package/src/Slider.astro +93 -0
- package/src/Sparkline.astro +79 -0
- package/src/Spinner.astro +37 -0
- package/src/Splitter.astro +87 -0
- package/src/Stack.astro +42 -0
- package/src/Stat.astro +70 -0
- package/src/Statusbar.astro +56 -0
- package/src/Swatch.astro +69 -0
- package/src/Switch.astro +89 -0
- package/src/Table.astro +46 -0
- package/src/Tabs.astro +94 -0
- package/src/Text.astro +54 -0
- package/src/Textarea.astro +81 -0
- package/src/Toast.astro +62 -0
- package/src/ToastRegion.astro +39 -0
- package/src/Toc.astro +35 -0
- package/src/Toolbar.astro +68 -0
- package/src/Tooltip.astro +68 -0
- package/src/Tree.astro +78 -0
- package/src/internal/palette.ts +92 -0
package/src/Button.astro
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { renderFragmentLight } from "@xtyle/core/elements/ssr";
|
|
3
|
+
|
|
4
|
+
import type { FullTone, ButtonAlign } from "@xtyle/core";
|
|
5
|
+
|
|
6
|
+
type ButtonVariant = "solid" | "outline" | "ghost" | "subtle" | "link";
|
|
7
|
+
type Size = "xs" | "sm" | "md" | "lg";
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
variant?: ButtonVariant;
|
|
11
|
+
tone?: FullTone;
|
|
12
|
+
size?: Size;
|
|
13
|
+
align?: ButtonAlign;
|
|
14
|
+
type?: "button" | "submit" | "reset";
|
|
15
|
+
href?: string;
|
|
16
|
+
disabled?: boolean;
|
|
17
|
+
loading?: boolean;
|
|
18
|
+
block?: boolean;
|
|
19
|
+
iconOnly?: boolean;
|
|
20
|
+
pressed?: boolean;
|
|
21
|
+
selected?: boolean;
|
|
22
|
+
/** Render zero-JS: emit the resolved light-DOM markup but never load the runtime to hydrate it. */
|
|
23
|
+
static?: boolean;
|
|
24
|
+
class?: string;
|
|
25
|
+
/** Any other attribute (`id`, `title`, `data-*`, `aria-*`, …) passes through to the element. */
|
|
26
|
+
[key: string]: unknown;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const {
|
|
30
|
+
variant = "solid",
|
|
31
|
+
tone = "accent",
|
|
32
|
+
size = "md",
|
|
33
|
+
align = "center",
|
|
34
|
+
type = "button",
|
|
35
|
+
href,
|
|
36
|
+
disabled = false,
|
|
37
|
+
loading = false,
|
|
38
|
+
block = false,
|
|
39
|
+
iconOnly = false,
|
|
40
|
+
pressed,
|
|
41
|
+
selected,
|
|
42
|
+
static: isStatic = false,
|
|
43
|
+
class: extra,
|
|
44
|
+
...rest
|
|
45
|
+
} = Astro.props;
|
|
46
|
+
|
|
47
|
+
const bindings = {
|
|
48
|
+
variant,
|
|
49
|
+
tone,
|
|
50
|
+
size,
|
|
51
|
+
align,
|
|
52
|
+
type,
|
|
53
|
+
href: href ?? null,
|
|
54
|
+
disabled,
|
|
55
|
+
loading,
|
|
56
|
+
block,
|
|
57
|
+
iconOnly,
|
|
58
|
+
pressed: pressed ?? null,
|
|
59
|
+
selected: selected ?? null,
|
|
60
|
+
ariaLabel: (rest as Record<string, unknown>)["aria-label"] ?? null,
|
|
61
|
+
ariaLabelledby: (rest as Record<string, unknown>)["aria-labelledby"] ?? null,
|
|
62
|
+
};
|
|
63
|
+
const chrome = await renderFragmentLight("button", bindings);
|
|
64
|
+
const iconStartHtml = Astro.slots.has("icon-start") ? await Astro.slots.render("icon-start") : "";
|
|
65
|
+
const labelHtml = Astro.slots.has("default") ? await Astro.slots.render("default") : "";
|
|
66
|
+
const iconEndHtml = Astro.slots.has("icon-end") ? await Astro.slots.render("icon-end") : "";
|
|
67
|
+
const composed = chrome
|
|
68
|
+
.replace('<slot name="icon-start"></slot>', () => iconStartHtml)
|
|
69
|
+
.replace('<slot name="icon-end"></slot>', () => iconEndHtml)
|
|
70
|
+
.replace("<slot></slot>", () => labelHtml);
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
<xtyle-button
|
|
74
|
+
variant={variant}
|
|
75
|
+
tone={tone}
|
|
76
|
+
size={size}
|
|
77
|
+
align={align}
|
|
78
|
+
type={type}
|
|
79
|
+
href={href}
|
|
80
|
+
disabled={disabled || undefined}
|
|
81
|
+
loading={loading || undefined}
|
|
82
|
+
block={block || undefined}
|
|
83
|
+
icon-only={iconOnly || undefined}
|
|
84
|
+
pressed={pressed === undefined ? undefined : String(pressed)}
|
|
85
|
+
selected={selected === undefined ? undefined : String(selected)}
|
|
86
|
+
class={extra}
|
|
87
|
+
set:html={composed}
|
|
88
|
+
{...rest}
|
|
89
|
+
/>
|
|
90
|
+
|
|
91
|
+
{!isStatic && <script>import "@xtyle/core/elements";</script>}
|
package/src/Card.astro
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { renderFragmentLight } from "@xtyle/core/elements/ssr";
|
|
3
|
+
|
|
4
|
+
import type { FullTone as Tone } from "@xtyle/core";
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
overlay?: boolean;
|
|
8
|
+
interactive?: boolean;
|
|
9
|
+
action?: boolean;
|
|
10
|
+
compact?: boolean;
|
|
11
|
+
tone?: Tone;
|
|
12
|
+
/** Render zero-JS: emit the resolved light-DOM markup but never load the runtime to hydrate it. */
|
|
13
|
+
static?: boolean;
|
|
14
|
+
class?: string;
|
|
15
|
+
/** Any other attribute (`id`, `title`, `data-*`, `aria-*`, …) passes through to the element. */
|
|
16
|
+
[key: string]: unknown;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const { overlay = false, interactive = false, action = false, compact = false, tone, static: isStatic = false, class: extra, ...rest } =
|
|
20
|
+
Astro.props;
|
|
21
|
+
|
|
22
|
+
const bindings = { overlay, interactive, action, compact, tone: tone ?? null };
|
|
23
|
+
const chrome = await renderFragmentLight("card", bindings);
|
|
24
|
+
const headerHtml = Astro.slots.has("header") ? await Astro.slots.render("header") : "";
|
|
25
|
+
const bodyHtml = Astro.slots.has("default") ? await Astro.slots.render("default") : "";
|
|
26
|
+
const footerHtml = Astro.slots.has("footer") ? await Astro.slots.render("footer") : "";
|
|
27
|
+
const composed = chrome
|
|
28
|
+
.replace('<slot name="header"></slot>', () => headerHtml)
|
|
29
|
+
.replace('<slot name="footer"></slot>', () => footerHtml)
|
|
30
|
+
.replace("<slot></slot>", () => bodyHtml);
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
<xtyle-card
|
|
34
|
+
overlay={overlay || undefined}
|
|
35
|
+
interactive={interactive || undefined}
|
|
36
|
+
action={action || undefined}
|
|
37
|
+
compact={compact || undefined}
|
|
38
|
+
tone={tone}
|
|
39
|
+
class={extra}
|
|
40
|
+
set:html={composed}
|
|
41
|
+
{...rest}
|
|
42
|
+
/>
|
|
43
|
+
|
|
44
|
+
{!isStatic && <script>import "@xtyle/core/elements";</script>}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { renderFragmentLight } from "@xtyle/core/elements/ssr";
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
href: string;
|
|
6
|
+
target?: string;
|
|
7
|
+
rel?: string;
|
|
8
|
+
interactive?: boolean;
|
|
9
|
+
overlay?: boolean;
|
|
10
|
+
compact?: boolean;
|
|
11
|
+
/** Render zero-JS: emit the resolved light-DOM markup but never load the runtime to hydrate it. */
|
|
12
|
+
static?: boolean;
|
|
13
|
+
class?: string;
|
|
14
|
+
/** Any other attribute (`id`, `title`, `data-*`, `aria-*`, …) passes through to the element. */
|
|
15
|
+
[key: string]: unknown;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const {
|
|
19
|
+
href,
|
|
20
|
+
target,
|
|
21
|
+
rel,
|
|
22
|
+
interactive = true,
|
|
23
|
+
overlay = false,
|
|
24
|
+
compact = false,
|
|
25
|
+
static: isStatic = false,
|
|
26
|
+
class: extra,
|
|
27
|
+
...rest
|
|
28
|
+
} = Astro.props;
|
|
29
|
+
|
|
30
|
+
const bindings = {
|
|
31
|
+
href,
|
|
32
|
+
target: target ?? null,
|
|
33
|
+
rel: rel ?? null,
|
|
34
|
+
interactive,
|
|
35
|
+
overlay,
|
|
36
|
+
compact,
|
|
37
|
+
};
|
|
38
|
+
const chrome = await renderFragmentLight("card-link", bindings);
|
|
39
|
+
const headerHtml = Astro.slots.has("header") ? await Astro.slots.render("header") : "";
|
|
40
|
+
const bodyHtml = Astro.slots.has("default") ? await Astro.slots.render("default") : "";
|
|
41
|
+
const footerHtml = Astro.slots.has("footer") ? await Astro.slots.render("footer") : "";
|
|
42
|
+
const composed = chrome
|
|
43
|
+
.replace('<slot name="header"></slot>', () => headerHtml)
|
|
44
|
+
.replace('<slot name="footer"></slot>', () => footerHtml)
|
|
45
|
+
.replace("<slot></slot>", () => bodyHtml);
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
<xtyle-card-link
|
|
49
|
+
href={href}
|
|
50
|
+
target={target}
|
|
51
|
+
rel={rel}
|
|
52
|
+
interactive={interactive ? undefined : "false"}
|
|
53
|
+
overlay={overlay || undefined}
|
|
54
|
+
compact={compact || undefined}
|
|
55
|
+
class={extra}
|
|
56
|
+
set:html={composed}
|
|
57
|
+
{...rest}
|
|
58
|
+
/>
|
|
59
|
+
|
|
60
|
+
{!isStatic && <script>import "@xtyle/core/elements";</script>}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface Props {
|
|
3
|
+
label?: string;
|
|
4
|
+
autoplay?: boolean;
|
|
5
|
+
interval?: number;
|
|
6
|
+
loop?: boolean;
|
|
7
|
+
controls?: boolean;
|
|
8
|
+
dots?: boolean;
|
|
9
|
+
class?: string;
|
|
10
|
+
/** Any other attribute (`id`, `title`, `data-*`, `aria-*`, …) passes through to the element. */
|
|
11
|
+
[key: string]: unknown;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const {
|
|
15
|
+
label,
|
|
16
|
+
autoplay = false,
|
|
17
|
+
interval,
|
|
18
|
+
loop = false,
|
|
19
|
+
controls = true,
|
|
20
|
+
dots = true,
|
|
21
|
+
class: extra,
|
|
22
|
+
...rest
|
|
23
|
+
} = Astro.props;
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
<xtyle-carousel
|
|
27
|
+
label={label}
|
|
28
|
+
interval={interval}
|
|
29
|
+
autoplay={autoplay ? "" : undefined}
|
|
30
|
+
loop={loop ? "" : undefined}
|
|
31
|
+
controls={controls ? undefined : "false"}
|
|
32
|
+
dots={dots ? undefined : "false"}
|
|
33
|
+
class={extra}
|
|
34
|
+
{...rest}
|
|
35
|
+
>
|
|
36
|
+
<slot />
|
|
37
|
+
</xtyle-carousel>
|
|
38
|
+
|
|
39
|
+
<script>import "@xtyle/core/elements";</script>
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { renderFragmentLight } from "@xtyle/core/elements/ssr";
|
|
3
|
+
|
|
4
|
+
import type { FullTone as Tone } from "@xtyle/core";
|
|
5
|
+
type Size = "sm" | "md" | "lg";
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
checked?: boolean;
|
|
9
|
+
indeterminate?: boolean;
|
|
10
|
+
disabled?: boolean;
|
|
11
|
+
size?: Size;
|
|
12
|
+
tone?: Tone;
|
|
13
|
+
name?: string;
|
|
14
|
+
value?: string;
|
|
15
|
+
label?: string;
|
|
16
|
+
labelledby?: string;
|
|
17
|
+
/** Render zero-JS: emit the declarative shadow but never load the runtime to hydrate it. */
|
|
18
|
+
static?: boolean;
|
|
19
|
+
class?: string;
|
|
20
|
+
/** Any other attribute (`id`, `title`, `data-*`, `aria-*`, …) passes through to the element. */
|
|
21
|
+
[key: string]: unknown;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const {
|
|
25
|
+
checked = false,
|
|
26
|
+
indeterminate = false,
|
|
27
|
+
disabled = false,
|
|
28
|
+
size = "md",
|
|
29
|
+
tone = "accent",
|
|
30
|
+
name,
|
|
31
|
+
value,
|
|
32
|
+
label,
|
|
33
|
+
labelledby,
|
|
34
|
+
static: isStatic = false,
|
|
35
|
+
class: extra,
|
|
36
|
+
...rest
|
|
37
|
+
} = Astro.props;
|
|
38
|
+
|
|
39
|
+
const bindings = {
|
|
40
|
+
checked,
|
|
41
|
+
indeterminate,
|
|
42
|
+
disabled,
|
|
43
|
+
size,
|
|
44
|
+
tone,
|
|
45
|
+
label: label ?? null,
|
|
46
|
+
labelledby: labelledby ?? null,
|
|
47
|
+
};
|
|
48
|
+
const light = await renderFragmentLight("checkbox", bindings);
|
|
49
|
+
const slotHtml = Astro.slots.has("default") ? await Astro.slots.render("default") : "";
|
|
50
|
+
const composed = light.replace("<slot></slot>", () => slotHtml);
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
<xtyle-checkbox
|
|
54
|
+
checked={checked || undefined}
|
|
55
|
+
indeterminate={indeterminate || undefined}
|
|
56
|
+
disabled={disabled || undefined}
|
|
57
|
+
size={size}
|
|
58
|
+
tone={tone}
|
|
59
|
+
name={name}
|
|
60
|
+
value={value}
|
|
61
|
+
label={label}
|
|
62
|
+
labelledby={labelledby}
|
|
63
|
+
class={extra}
|
|
64
|
+
set:html={composed}
|
|
65
|
+
{...rest}
|
|
66
|
+
/>
|
|
67
|
+
|
|
68
|
+
{!isStatic && <script>import "@xtyle/core/elements";</script>}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { checkboxGroupMarkup, checkboxGroupHostCss } from "@xtyle/core/markup";
|
|
3
|
+
import { checkboxCss } from "@xtyle/core/css";
|
|
4
|
+
|
|
5
|
+
import type { FullTone as Tone } from "@xtyle/core";
|
|
6
|
+
type Size = "sm" | "md" | "lg";
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
label?: string;
|
|
10
|
+
labelledby?: string;
|
|
11
|
+
tone?: Tone;
|
|
12
|
+
size?: Size;
|
|
13
|
+
disabled?: boolean;
|
|
14
|
+
/** Keep the heading checkbox but stop the automatic all/some/none roll-up. */
|
|
15
|
+
manual?: boolean;
|
|
16
|
+
/** Render zero-JS: emit the declarative shadow but never load the runtime to hydrate it. */
|
|
17
|
+
static?: boolean;
|
|
18
|
+
class?: string;
|
|
19
|
+
/** Any other attribute (`id`, `title`, `data-*`, `aria-*`, …) passes through to the element. */
|
|
20
|
+
[key: string]: unknown;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const {
|
|
24
|
+
label,
|
|
25
|
+
labelledby,
|
|
26
|
+
tone = "accent",
|
|
27
|
+
size = "md",
|
|
28
|
+
disabled = false,
|
|
29
|
+
manual = false,
|
|
30
|
+
static: isStatic = false,
|
|
31
|
+
class: extra,
|
|
32
|
+
...rest
|
|
33
|
+
} = Astro.props;
|
|
34
|
+
|
|
35
|
+
const labelId = `xtyle-checkbox-group-${Math.random().toString(36).slice(2, 8)}-label`;
|
|
36
|
+
|
|
37
|
+
const shadow = `<style>${checkboxGroupHostCss}${checkboxCss}</style>${checkboxGroupMarkup({ label, labelledby, tone, size, disabled, labelId })}`;
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
<xtyle-checkbox-group
|
|
41
|
+
label={label}
|
|
42
|
+
labelledby={labelledby}
|
|
43
|
+
tone={tone}
|
|
44
|
+
size={size}
|
|
45
|
+
disabled={disabled || undefined}
|
|
46
|
+
manual={manual || undefined}
|
|
47
|
+
class={extra}
|
|
48
|
+
{...rest}
|
|
49
|
+
>
|
|
50
|
+
<template shadowrootmode="open" set:html={shadow}></template>
|
|
51
|
+
<slot />
|
|
52
|
+
</xtyle-checkbox-group>
|
|
53
|
+
|
|
54
|
+
{!isStatic && <script>import "@xtyle/core/elements";</script>}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { renderFragmentLight } from "@xtyle/core/elements/ssr";
|
|
3
|
+
|
|
4
|
+
type ClusterAlign = "start" | "center" | "end" | "stretch" | "baseline";
|
|
5
|
+
type ClusterJustify = "start" | "center" | "end" | "between" | "around" | "evenly";
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
gap?: number;
|
|
9
|
+
align?: ClusterAlign;
|
|
10
|
+
justify?: ClusterJustify;
|
|
11
|
+
nowrap?: boolean;
|
|
12
|
+
inline?: boolean;
|
|
13
|
+
/** Render zero-JS: emit the resolved light-DOM markup but never load the runtime to hydrate it. */
|
|
14
|
+
static?: boolean;
|
|
15
|
+
class?: string;
|
|
16
|
+
/** Any other attribute (`id`, `title`, `data-*`, `aria-*`, …) passes through to the element. */
|
|
17
|
+
[key: string]: unknown;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const { gap = 2, align, justify, nowrap = false, inline = false, static: isStatic = false, class: extra, ...rest } =
|
|
21
|
+
Astro.props;
|
|
22
|
+
|
|
23
|
+
const bindings = {
|
|
24
|
+
gap,
|
|
25
|
+
align: align ?? null,
|
|
26
|
+
justify: justify ?? null,
|
|
27
|
+
nowrap,
|
|
28
|
+
inline,
|
|
29
|
+
};
|
|
30
|
+
const chrome = await renderFragmentLight("cluster", bindings);
|
|
31
|
+
const slotHtml = Astro.slots.has("default") ? await Astro.slots.render("default") : "";
|
|
32
|
+
const composed = chrome.replace("<slot></slot>", () => slotHtml);
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
<xtyle-cluster
|
|
36
|
+
gap={gap}
|
|
37
|
+
align={align}
|
|
38
|
+
justify={justify}
|
|
39
|
+
nowrap={nowrap || undefined}
|
|
40
|
+
inline={inline || undefined}
|
|
41
|
+
class={extra}
|
|
42
|
+
set:html={composed}
|
|
43
|
+
{...rest}
|
|
44
|
+
/>
|
|
45
|
+
|
|
46
|
+
{!isStatic && <script>import "@xtyle/core/elements";</script>}
|
package/src/Code.astro
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { plainCodeHtml, splitCodeLines, codeGutterWidth, parseLineSpec } from "@xtyle/core/markup";
|
|
3
|
+
import { highlight } from "@xtyle/core/elements/code-highlight";
|
|
4
|
+
import { renderFragmentLight } from "@xtyle/core/elements/ssr";
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
lang?: string;
|
|
8
|
+
code?: string;
|
|
9
|
+
/** Warm this block's grammar eagerly in the browser and emit a `modulepreload` hint. */
|
|
10
|
+
preload?: boolean;
|
|
11
|
+
/** Render zero-JS: emit the pre-colored light-DOM markup but never load the runtime. */
|
|
12
|
+
static?: boolean;
|
|
13
|
+
/** The copy-to-clipboard button; on by default, set `false` to drop it. Inert on the `static` (zero-JS) path. */
|
|
14
|
+
copy?: boolean;
|
|
15
|
+
/** Soft-wrap long lines instead of scrolling them horizontally. Works on the zero-JS path. */
|
|
16
|
+
wrap?: boolean;
|
|
17
|
+
/** Number each line in a counter gutter. Works on the zero-JS path. */
|
|
18
|
+
lineNumbers?: boolean;
|
|
19
|
+
/** Tint chosen lines; a 1-based spec like `2`, `2,4`, or `4-6`. Works on the zero-JS path. */
|
|
20
|
+
highlight?: string;
|
|
21
|
+
/** A caption header above the block, e.g. a filename. Works on the zero-JS path. */
|
|
22
|
+
caption?: string;
|
|
23
|
+
class?: string;
|
|
24
|
+
/** Any other attribute (`id`, `title`, `data-*`, `aria-*`, …) passes through to the element. */
|
|
25
|
+
[key: string]: unknown;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const { lang, code = "", preload = false, static: isStatic = false, copy = true, wrap = false, lineNumbers = false, highlight: highlightSpec, caption, class: extra, ...rest } = Astro.props;
|
|
29
|
+
|
|
30
|
+
const result = await highlight(code, lang ?? null);
|
|
31
|
+
const base = result.language === null ? plainCodeHtml(code) : result.html;
|
|
32
|
+
const litSet = highlightSpec ? parseLineSpec(highlightSpec) : undefined;
|
|
33
|
+
const split = lineNumbers || litSet ? splitCodeLines(base, litSet) : null;
|
|
34
|
+
const inner = split ? split.html : base;
|
|
35
|
+
const gutter = split && lineNumbers ? `--xtyle-code-gutter: ${codeGutterWidth(split.lines)}` : undefined;
|
|
36
|
+
const bindings = { html: inner, language: result.language, caption: caption ?? null };
|
|
37
|
+
const light = await renderFragmentLight("code", bindings);
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
<xtyle-code language={lang ?? undefined} code={code} preload={preload || undefined} copy={copy ? undefined : "false"} wrap={wrap || undefined} line-numbers={lineNumbers || undefined} highlight={highlightSpec || undefined} caption={caption || undefined} style={gutter} class={extra} set:html={light} {...rest} />
|
|
41
|
+
|
|
42
|
+
{!isStatic && <script>import "@xtyle/core/elements";</script>}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { formatChannelValue, namedSnapLabel, PLANE_H, PLANE_W } from "@xtyle/core/markup";
|
|
3
|
+
import { renderFragmentLight } from "@xtyle/core/elements/ssr";
|
|
4
|
+
import {
|
|
5
|
+
channelModels,
|
|
6
|
+
channelsOf,
|
|
7
|
+
colorFormats,
|
|
8
|
+
colorToChannels,
|
|
9
|
+
formatColor,
|
|
10
|
+
harmonySchemes,
|
|
11
|
+
hsvToRgb,
|
|
12
|
+
nearestNamedColor,
|
|
13
|
+
parseColor,
|
|
14
|
+
rgbToHsv,
|
|
15
|
+
type ChannelModel,
|
|
16
|
+
type ColorFormat,
|
|
17
|
+
type HarmonyScheme,
|
|
18
|
+
} from "@xtyle/core";
|
|
19
|
+
|
|
20
|
+
interface Props {
|
|
21
|
+
value?: string;
|
|
22
|
+
format?: ColorFormat;
|
|
23
|
+
modes?: string;
|
|
24
|
+
channels?: string;
|
|
25
|
+
snap?: string;
|
|
26
|
+
plane?: boolean;
|
|
27
|
+
trigger?: boolean;
|
|
28
|
+
swatches?: string;
|
|
29
|
+
harmony?: string;
|
|
30
|
+
contrastAgainst?: string;
|
|
31
|
+
alpha?: boolean;
|
|
32
|
+
disabled?: boolean;
|
|
33
|
+
label?: string;
|
|
34
|
+
labelledby?: string;
|
|
35
|
+
name?: string;
|
|
36
|
+
/** Render zero-JS: emit the resolved light-DOM markup but never load the runtime to hydrate it. */
|
|
37
|
+
static?: boolean;
|
|
38
|
+
class?: string;
|
|
39
|
+
/** Any other attribute (`id`, `title`, `data-*`, `aria-*`, …) passes through to the element. */
|
|
40
|
+
[key: string]: unknown;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const {
|
|
44
|
+
value = "#5b8cff",
|
|
45
|
+
format,
|
|
46
|
+
modes,
|
|
47
|
+
channels,
|
|
48
|
+
snap,
|
|
49
|
+
plane = false,
|
|
50
|
+
trigger = false,
|
|
51
|
+
swatches,
|
|
52
|
+
harmony,
|
|
53
|
+
contrastAgainst,
|
|
54
|
+
alpha = false,
|
|
55
|
+
disabled = false,
|
|
56
|
+
label,
|
|
57
|
+
labelledby,
|
|
58
|
+
name,
|
|
59
|
+
static: isStatic = false,
|
|
60
|
+
class: extra,
|
|
61
|
+
...rest
|
|
62
|
+
} = Astro.props;
|
|
63
|
+
|
|
64
|
+
const elementId = `xtyle-color-picker-${Math.random().toString(36).slice(2, 8)}`;
|
|
65
|
+
|
|
66
|
+
// Resolve the markup props exactly as the custom element's getters do, so the SSR
|
|
67
|
+
// declarative shadow is byte-identical to the post-hydration client re-render.
|
|
68
|
+
const resolvedModes: ColorFormat[] = modes
|
|
69
|
+
? (() => {
|
|
70
|
+
const picked = modes
|
|
71
|
+
.split(",")
|
|
72
|
+
.map((entry) => entry.trim())
|
|
73
|
+
.filter((entry): entry is ColorFormat => (colorFormats as string[]).includes(entry));
|
|
74
|
+
return picked.length ? picked : colorFormats;
|
|
75
|
+
})()
|
|
76
|
+
: colorFormats;
|
|
77
|
+
const resolvedFormat: ColorFormat = format && resolvedModes.includes(format) ? format : (resolvedModes[0] ?? "hex");
|
|
78
|
+
|
|
79
|
+
const channelModel: ChannelModel | null =
|
|
80
|
+
channels === undefined ? null : channels.trim() && (channelModels as string[]).includes(channels.trim()) ? (channels.trim() as ChannelModel) : "rgb";
|
|
81
|
+
|
|
82
|
+
const SNAP_TARGETS = ["web-safe", "named"] as const;
|
|
83
|
+
const snapTargets: string[] =
|
|
84
|
+
snap === undefined
|
|
85
|
+
? []
|
|
86
|
+
: (() => {
|
|
87
|
+
const raw = snap.trim();
|
|
88
|
+
if (!raw) return [...SNAP_TARGETS];
|
|
89
|
+
const picked = raw
|
|
90
|
+
.split(",")
|
|
91
|
+
.map((entry) => entry.trim())
|
|
92
|
+
.filter((entry) => (SNAP_TARGETS as readonly string[]).includes(entry));
|
|
93
|
+
return picked.length ? picked : [...SNAP_TARGETS];
|
|
94
|
+
})();
|
|
95
|
+
|
|
96
|
+
const swatchList = swatches ? swatches.split(",").map((entry) => entry.trim()).filter(Boolean) : [];
|
|
97
|
+
|
|
98
|
+
const harmonyScheme: HarmonyScheme | null = harmony && (harmonySchemes as string[]).includes(harmony) ? (harmony as HarmonyScheme) : null;
|
|
99
|
+
|
|
100
|
+
const parsed = parseColor(value);
|
|
101
|
+
const baseHsv = parsed ? rgbToHsv(parsed) : { h: 217, s: 0.64, v: 1 };
|
|
102
|
+
const hsv = { h: baseHsv.h, s: baseHsv.s, v: baseHsv.v };
|
|
103
|
+
const alphaValue = parsed && alpha ? parsed.alpha : 1;
|
|
104
|
+
const currentAlpha = alpha ? alphaValue : 1;
|
|
105
|
+
|
|
106
|
+
const currentHex = formatColor(hsvToRgb({ ...hsv, alpha: alphaValue }), "hex");
|
|
107
|
+
const resolvedSwatches = swatchList
|
|
108
|
+
.map((entry) => {
|
|
109
|
+
const swatchColor = parseColor(entry);
|
|
110
|
+
if (!swatchColor) return null;
|
|
111
|
+
const hex = formatColor(swatchColor, "hex");
|
|
112
|
+
return { entry, hex, pressed: hex.toLowerCase() === currentHex.toLowerCase() };
|
|
113
|
+
})
|
|
114
|
+
.filter((s): s is { entry: string; hex: string; pressed: boolean } => s !== null);
|
|
115
|
+
|
|
116
|
+
const resolvedChannels = channelModel
|
|
117
|
+
? (() => {
|
|
118
|
+
const defs = channelsOf(channelModel);
|
|
119
|
+
const values = colorToChannels(hsvToRgb({ ...hsv, alpha: currentAlpha }), channelModel);
|
|
120
|
+
return defs.map((def, i) => {
|
|
121
|
+
const channelValue = values[i] ?? def.min;
|
|
122
|
+
return {
|
|
123
|
+
label: def.label,
|
|
124
|
+
name: `${def.label}${def.unit ? ` (${def.unit})` : ""}`,
|
|
125
|
+
min: def.min,
|
|
126
|
+
max: def.max,
|
|
127
|
+
step: def.step,
|
|
128
|
+
value: channelValue,
|
|
129
|
+
formatted: formatChannelValue(channelValue, def),
|
|
130
|
+
};
|
|
131
|
+
});
|
|
132
|
+
})()
|
|
133
|
+
: [];
|
|
134
|
+
|
|
135
|
+
const namedSnap = snapTargets.includes("named")
|
|
136
|
+
? namedSnapLabel(nearestNamedColor(hsvToRgb({ ...hsv, alpha: currentAlpha })).name)
|
|
137
|
+
: null;
|
|
138
|
+
|
|
139
|
+
const bindings = {
|
|
140
|
+
disabled,
|
|
141
|
+
label: label ?? null,
|
|
142
|
+
labelledby: labelledby ?? null,
|
|
143
|
+
elementId,
|
|
144
|
+
formatLabel: resolvedFormat.toUpperCase(),
|
|
145
|
+
planeEnabled: plane,
|
|
146
|
+
triggerEnabled: trigger,
|
|
147
|
+
alphaEnabled: alpha,
|
|
148
|
+
channelModelLabel: channelModel ? channelModel.toUpperCase() : null,
|
|
149
|
+
harmonyScheme,
|
|
150
|
+
hasContrast: !!(contrastAgainst && parseColor(contrastAgainst)),
|
|
151
|
+
hasEyeDropper: false,
|
|
152
|
+
planeWidth: PLANE_W,
|
|
153
|
+
planeHeight: PLANE_H,
|
|
154
|
+
swatches: resolvedSwatches,
|
|
155
|
+
channels: resolvedChannels,
|
|
156
|
+
snapTargets,
|
|
157
|
+
namedSnap,
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
const light = await renderFragmentLight("color-picker", bindings);
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
<xtyle-color-picker
|
|
164
|
+
value={value}
|
|
165
|
+
format={format}
|
|
166
|
+
modes={modes}
|
|
167
|
+
channels={channels}
|
|
168
|
+
snap={snap}
|
|
169
|
+
plane={plane || undefined}
|
|
170
|
+
trigger={trigger || undefined}
|
|
171
|
+
swatches={swatches}
|
|
172
|
+
harmony={harmony}
|
|
173
|
+
contrast-against={contrastAgainst}
|
|
174
|
+
alpha={alpha || undefined}
|
|
175
|
+
disabled={disabled || undefined}
|
|
176
|
+
label={label}
|
|
177
|
+
labelledby={labelledby}
|
|
178
|
+
name={name}
|
|
179
|
+
class={extra}
|
|
180
|
+
{...rest}
|
|
181
|
+
set:html={light}
|
|
182
|
+
/>
|
|
183
|
+
|
|
184
|
+
{!isStatic && <script>import "@xtyle/core/elements";</script>}
|
package/src/Dialog.astro
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { renderFragmentLight, composeFallbackSlot } from "@xtyle/core/elements/ssr";
|
|
3
|
+
|
|
4
|
+
type Size = "sm" | "md" | "lg";
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
open?: boolean;
|
|
8
|
+
size?: Size;
|
|
9
|
+
heading?: string;
|
|
10
|
+
label?: string;
|
|
11
|
+
labelledby?: string;
|
|
12
|
+
closeLabel?: string;
|
|
13
|
+
noCloseButton?: boolean;
|
|
14
|
+
/** Render zero-JS: emit the resolved light-DOM markup but never load the runtime to hydrate it. */
|
|
15
|
+
static?: boolean;
|
|
16
|
+
class?: string;
|
|
17
|
+
/** Any other attribute (`id`, `title`, `data-*`, `aria-*`, …) passes through to the element. */
|
|
18
|
+
[key: string]: unknown;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const {
|
|
22
|
+
open = false,
|
|
23
|
+
size = "md",
|
|
24
|
+
heading,
|
|
25
|
+
label,
|
|
26
|
+
labelledby,
|
|
27
|
+
closeLabel,
|
|
28
|
+
noCloseButton = false,
|
|
29
|
+
static: isStatic = false,
|
|
30
|
+
class: extra,
|
|
31
|
+
...rest
|
|
32
|
+
} = Astro.props;
|
|
33
|
+
|
|
34
|
+
const hasHeader = Astro.slots.has("header");
|
|
35
|
+
const hasFooter = Astro.slots.has("footer");
|
|
36
|
+
const elementId = `xtyle-dialog-${Math.random().toString(36).slice(2, 8)}`;
|
|
37
|
+
|
|
38
|
+
const bindings = {
|
|
39
|
+
size,
|
|
40
|
+
heading: heading ?? null,
|
|
41
|
+
label: label ?? null,
|
|
42
|
+
labelledby: labelledby ?? null,
|
|
43
|
+
closeLabel: closeLabel ?? null,
|
|
44
|
+
noCloseButton,
|
|
45
|
+
elementId,
|
|
46
|
+
};
|
|
47
|
+
const light = await renderFragmentLight("dialog", bindings);
|
|
48
|
+
const headerHtml = hasHeader ? await Astro.slots.render("header") : null;
|
|
49
|
+
const bodyHtml = Astro.slots.has("default") ? await Astro.slots.render("default") : "";
|
|
50
|
+
const footerHtml = hasFooter ? await Astro.slots.render("footer") : "";
|
|
51
|
+
const composed = composeFallbackSlot(light, "header", headerHtml)
|
|
52
|
+
.replace("<slot></slot>", () => bodyHtml)
|
|
53
|
+
.replace('<slot name="footer"></slot>', () => footerHtml);
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
<xtyle-dialog
|
|
57
|
+
open={open || undefined}
|
|
58
|
+
size={size}
|
|
59
|
+
heading={heading}
|
|
60
|
+
label={label}
|
|
61
|
+
labelledby={labelledby}
|
|
62
|
+
close-label={closeLabel}
|
|
63
|
+
no-close-button={noCloseButton || undefined}
|
|
64
|
+
class={extra}
|
|
65
|
+
set:html={composed}
|
|
66
|
+
{...rest}
|
|
67
|
+
/>
|
|
68
|
+
|
|
69
|
+
{!isStatic && <script>import "@xtyle/core/elements";</script>}
|