@treeseed/core 0.8.8 → 0.8.9
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/dist/components/content/ContentStatusLegend.astro +4 -4
- package/dist/components/docs/BookFontControls.astro +9 -9
- package/dist/components/docs/DesktopSidebarToggle.astro +8 -8
- package/dist/components/docs/Footer.astro +6 -6
- package/dist/components/docs/PageTitle.astro +1 -1
- package/dist/components/docs/ThemeSelect.astro +3 -1
- package/dist/components/forms/ContactForm.astro +21 -21
- package/dist/components/forms/FooterSubscribeForm.astro +9 -9
- package/dist/components/site/BookList.astro +7 -7
- package/dist/components/site/CTASection.astro +4 -4
- package/dist/components/site/ChronicleList.astro +6 -6
- package/dist/components/site/Hero.astro +3 -3
- package/dist/components/site/PathCard.astro +5 -5
- package/dist/components/site/ProfileList.astro +5 -5
- package/dist/components/site/RouteNotFound.astro +5 -5
- package/dist/components/site/SectionIntro.astro +3 -3
- package/dist/components/site/StageBanner.astro +2 -2
- package/dist/components/site/TrustCallout.astro +3 -3
- package/dist/components/ui/data/ActionList.astro +51 -0
- package/dist/components/ui/data/Badge.astro +19 -0
- package/dist/components/ui/data/DataTable.astro +51 -0
- package/dist/components/ui/data/KeyValueList.astro +28 -0
- package/dist/components/ui/data/MetricCard.astro +25 -0
- package/dist/components/ui/data/MetricGrid.astro +27 -0
- package/dist/components/ui/data/StatusPill.astro +20 -0
- package/dist/components/ui/forms/Button.astro +52 -0
- package/dist/components/ui/forms/Field.astro +39 -0
- package/dist/components/ui/forms/FormActions.astro +12 -0
- package/dist/components/ui/forms/PasswordMeter.astro +80 -0
- package/dist/components/ui/forms/RadioGroup.astro +55 -0
- package/dist/components/ui/forms/Select.astro +44 -0
- package/dist/components/ui/forms/TextInput.astro +58 -0
- package/dist/components/ui/forms/Textarea.astro +45 -0
- package/dist/components/ui/layout/PageHeader.astro +45 -0
- package/dist/components/ui/shell/AppShell.astro +110 -0
- package/dist/components/ui/shell/BottomNav.astro +35 -0
- package/dist/components/ui/shell/ProjectHeader.astro +66 -0
- package/dist/components/ui/shell/PublicShell.astro +108 -0
- package/dist/components/ui/shell/RailNav.astro +35 -0
- package/dist/components/ui/shell/TopBar.astro +52 -0
- package/dist/components/ui/surface/Card.astro +46 -0
- package/dist/components/ui/surface/EmptyState.astro +45 -0
- package/dist/components/ui/surface/Panel.astro +54 -0
- package/dist/components/ui/theme/ThemeMenu.astro +32 -0
- package/dist/components/ui/theme/ThemePreviewSwatch.astro +18 -0
- package/dist/components/ui/theme/ThemeScript.astro +105 -0
- package/dist/components/ui/theme/ThemeSelector.astro +202 -0
- package/dist/components/ui/types.js +0 -0
- package/dist/dev.js +14 -2
- package/dist/layouts/AuthoredEntryLayout.astro +27 -27
- package/dist/layouts/BookLayout.astro +10 -10
- package/dist/layouts/ContentLayout.astro +4 -4
- package/dist/layouts/MainLayout.astro +27 -25
- package/dist/layouts/NoteLayout.astro +6 -6
- package/dist/layouts/ProfileLayout.astro +17 -17
- package/dist/pages/404.astro +6 -6
- package/dist/pages/contact.astro +4 -4
- package/dist/pages/docs-runtime/[...slug].astro +12 -12
- package/dist/pages/docs-runtime/index.astro +13 -13
- package/dist/pages/index.astro +28 -28
- package/dist/pages/ui/index.astro +216 -0
- package/dist/site.js +3 -2
- package/dist/styles/app-shell.css +398 -0
- package/dist/styles/forms.css +258 -0
- package/dist/styles/global.css +119 -119
- package/dist/styles/prose.css +11 -11
- package/dist/styles/theme.css +177 -0
- package/dist/styles/tokens.css +62 -22
- package/dist/styles/ui.css +551 -0
- package/dist/utils/content-status.js +5 -5
- package/dist/utils/site-config.js +2 -2
- package/dist/utils/theme.js +352 -40
- package/package.json +35 -2
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { Tone } from '../types.js';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
tone?: Tone;
|
|
6
|
+
size?: 'sm' | 'md';
|
|
7
|
+
class?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const {
|
|
11
|
+
tone = 'default',
|
|
12
|
+
size = 'md',
|
|
13
|
+
class: className,
|
|
14
|
+
} = Astro.props as Props;
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
<span class:list={['ts-badge', className]} data-tone={tone} data-size={size}>
|
|
18
|
+
<slot />
|
|
19
|
+
</span>
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface DataTableColumn {
|
|
3
|
+
key: string;
|
|
4
|
+
label: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
columns: DataTableColumn[];
|
|
9
|
+
rows: Array<Record<string, unknown>>;
|
|
10
|
+
caption?: string;
|
|
11
|
+
emptyLabel?: string;
|
|
12
|
+
class?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const {
|
|
16
|
+
columns,
|
|
17
|
+
rows,
|
|
18
|
+
caption,
|
|
19
|
+
emptyLabel = 'No rows to show.',
|
|
20
|
+
class: className,
|
|
21
|
+
} = Astro.props as Props;
|
|
22
|
+
|
|
23
|
+
function cellValue(row: Record<string, unknown>, key: string) {
|
|
24
|
+
const value = row[key];
|
|
25
|
+
return value === null || value === undefined ? '' : String(value);
|
|
26
|
+
}
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
<div class:list={['ts-data-table-wrap', className]}>
|
|
30
|
+
<table class="ts-data-table">
|
|
31
|
+
{caption ? <caption>{caption}</caption> : null}
|
|
32
|
+
<thead>
|
|
33
|
+
<tr>
|
|
34
|
+
{columns.map((column) => <th scope="col">{column.label}</th>)}
|
|
35
|
+
</tr>
|
|
36
|
+
</thead>
|
|
37
|
+
<tbody>
|
|
38
|
+
{rows.length > 0 ? rows.map((row) => (
|
|
39
|
+
<tr>
|
|
40
|
+
{columns.map((column) => (
|
|
41
|
+
<td data-label={column.label}>{cellValue(row, column.key)}</td>
|
|
42
|
+
))}
|
|
43
|
+
</tr>
|
|
44
|
+
)) : (
|
|
45
|
+
<tr>
|
|
46
|
+
<td colspan={columns.length}>{emptyLabel}</td>
|
|
47
|
+
</tr>
|
|
48
|
+
)}
|
|
49
|
+
</tbody>
|
|
50
|
+
</table>
|
|
51
|
+
</div>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { Tone } from '../types.js';
|
|
3
|
+
|
|
4
|
+
interface KeyValueItem {
|
|
5
|
+
key: string;
|
|
6
|
+
value: string | number;
|
|
7
|
+
tone?: Tone;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface Props {
|
|
11
|
+
items: KeyValueItem[];
|
|
12
|
+
class?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const {
|
|
16
|
+
items,
|
|
17
|
+
class: className,
|
|
18
|
+
} = Astro.props as Props;
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
<dl class:list={['ts-key-value-list', className]}>
|
|
22
|
+
{items.map((item) => (
|
|
23
|
+
<div class="ts-key-value-list__item" data-tone={item.tone ?? 'default'}>
|
|
24
|
+
<dt>{item.key}</dt>
|
|
25
|
+
<dd>{item.value}</dd>
|
|
26
|
+
</div>
|
|
27
|
+
))}
|
|
28
|
+
</dl>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { Tone } from '../types.js';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
label: string;
|
|
6
|
+
value: string | number;
|
|
7
|
+
description?: string;
|
|
8
|
+
tone?: Tone;
|
|
9
|
+
class?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const {
|
|
13
|
+
label,
|
|
14
|
+
value,
|
|
15
|
+
description,
|
|
16
|
+
tone = 'default',
|
|
17
|
+
class: className,
|
|
18
|
+
} = Astro.props as Props;
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
<article class:list={['ts-metric-card', className]} data-tone={tone}>
|
|
22
|
+
<p class="ts-metric-card__label">{label}</p>
|
|
23
|
+
<p class="ts-metric-card__value">{value}</p>
|
|
24
|
+
{description ? <p class="ts-metric-card__description">{description}</p> : null}
|
|
25
|
+
</article>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
import MetricCard from './MetricCard.astro';
|
|
3
|
+
import type { Tone } from '../types.js';
|
|
4
|
+
|
|
5
|
+
interface MetricItem {
|
|
6
|
+
label: string;
|
|
7
|
+
value: string | number;
|
|
8
|
+
description?: string;
|
|
9
|
+
tone?: Tone;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface Props {
|
|
13
|
+
metrics?: MetricItem[];
|
|
14
|
+
min?: string;
|
|
15
|
+
class?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const {
|
|
19
|
+
metrics = [],
|
|
20
|
+
min = '12rem',
|
|
21
|
+
class: className,
|
|
22
|
+
} = Astro.props as Props;
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
<div class:list={['ts-metric-grid', className]} style={`--ts-metric-grid-min: ${min}`}>
|
|
26
|
+
{metrics.length > 0 ? metrics.map((metric) => <MetricCard {...metric} />) : <slot />}
|
|
27
|
+
</div>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { Tone } from '../types.js';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
tone?: Tone;
|
|
6
|
+
label?: string;
|
|
7
|
+
class?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const {
|
|
11
|
+
tone = 'default',
|
|
12
|
+
label,
|
|
13
|
+
class: className,
|
|
14
|
+
} = Astro.props as Props;
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
<span class:list={['ts-status-pill', className]} data-tone={tone}>
|
|
18
|
+
<span class="ts-status-pill__dot" aria-hidden="true"></span>
|
|
19
|
+
<span><slot>{label}</slot></span>
|
|
20
|
+
</span>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { ButtonSize, ButtonVariant } from '../types.js';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
href?: string;
|
|
6
|
+
type?: 'button' | 'submit' | 'reset';
|
|
7
|
+
variant?: ButtonVariant;
|
|
8
|
+
size?: ButtonSize;
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
ariaLabel?: string;
|
|
11
|
+
class?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const {
|
|
15
|
+
href,
|
|
16
|
+
type = 'button',
|
|
17
|
+
variant = 'primary',
|
|
18
|
+
size = 'md',
|
|
19
|
+
disabled = false,
|
|
20
|
+
ariaLabel,
|
|
21
|
+
class: className,
|
|
22
|
+
} = Astro.props as Props;
|
|
23
|
+
|
|
24
|
+
const classes = ['ts-button', className].filter(Boolean).join(' ');
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
{
|
|
28
|
+
href ? (
|
|
29
|
+
<a
|
|
30
|
+
href={disabled ? undefined : href}
|
|
31
|
+
class={classes}
|
|
32
|
+
data-variant={variant}
|
|
33
|
+
data-size={size}
|
|
34
|
+
aria-label={ariaLabel}
|
|
35
|
+
aria-disabled={disabled ? 'true' : undefined}
|
|
36
|
+
tabindex={disabled ? -1 : undefined}
|
|
37
|
+
>
|
|
38
|
+
<slot />
|
|
39
|
+
</a>
|
|
40
|
+
) : (
|
|
41
|
+
<button
|
|
42
|
+
type={type}
|
|
43
|
+
class={classes}
|
|
44
|
+
data-variant={variant}
|
|
45
|
+
data-size={size}
|
|
46
|
+
disabled={disabled}
|
|
47
|
+
aria-label={ariaLabel}
|
|
48
|
+
>
|
|
49
|
+
<slot />
|
|
50
|
+
</button>
|
|
51
|
+
)
|
|
52
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface Props {
|
|
3
|
+
label: string;
|
|
4
|
+
id?: string;
|
|
5
|
+
name?: string;
|
|
6
|
+
help?: string;
|
|
7
|
+
error?: string;
|
|
8
|
+
required?: boolean;
|
|
9
|
+
full?: boolean;
|
|
10
|
+
class?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const {
|
|
14
|
+
label,
|
|
15
|
+
id,
|
|
16
|
+
name,
|
|
17
|
+
help,
|
|
18
|
+
error,
|
|
19
|
+
required = false,
|
|
20
|
+
full = false,
|
|
21
|
+
class: className,
|
|
22
|
+
} = Astro.props as Props;
|
|
23
|
+
|
|
24
|
+
const fieldId = id ?? name;
|
|
25
|
+
const helpId = fieldId && help ? `${fieldId}-help` : undefined;
|
|
26
|
+
const errorId = fieldId && error ? `${fieldId}-error` : undefined;
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
<div class:list={['ts-field', full && 'ts-field--full', className]}>
|
|
30
|
+
<label class="ts-field__label" for={fieldId}>
|
|
31
|
+
<span>{label}</span>
|
|
32
|
+
{required ? <span class="ts-field__required" aria-hidden="true">*</span> : null}
|
|
33
|
+
</label>
|
|
34
|
+
<div class="ts-field__control">
|
|
35
|
+
<slot helpId={helpId} errorId={errorId} describedBy={[helpId, errorId].filter(Boolean).join(' ') || undefined} />
|
|
36
|
+
</div>
|
|
37
|
+
{help ? <p class="ts-field__help" id={helpId}>{help}</p> : null}
|
|
38
|
+
{error ? <p class="ts-field__error" id={errorId}>{error}</p> : null}
|
|
39
|
+
</div>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface Props {
|
|
3
|
+
align?: 'start' | 'end' | 'between';
|
|
4
|
+
class?: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const { align = 'end', class: className } = Astro.props as Props;
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
<div class:list={['ts-form-actions', `ts-form-actions--${align}`, className]}>
|
|
11
|
+
<slot />
|
|
12
|
+
</div>
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface Props {
|
|
3
|
+
inputId?: string;
|
|
4
|
+
label?: string;
|
|
5
|
+
minLength?: number;
|
|
6
|
+
class?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const {
|
|
10
|
+
inputId,
|
|
11
|
+
label = 'Password strength',
|
|
12
|
+
minLength = 12,
|
|
13
|
+
class: className,
|
|
14
|
+
} = Astro.props as Props;
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
<div
|
|
18
|
+
class:list={['ts-password-meter', className]}
|
|
19
|
+
data-ts-password-meter
|
|
20
|
+
data-input-id={inputId}
|
|
21
|
+
data-min-length={minLength}
|
|
22
|
+
>
|
|
23
|
+
<div class="ts-password-meter__header">
|
|
24
|
+
<span>{label}</span>
|
|
25
|
+
<span class="ts-password-meter__status" data-ts-password-meter-status>Not started</span>
|
|
26
|
+
</div>
|
|
27
|
+
<div class="ts-password-meter__track" aria-hidden="true">
|
|
28
|
+
<span class="ts-password-meter__bar" data-ts-password-meter-bar></span>
|
|
29
|
+
</div>
|
|
30
|
+
<ul class="ts-password-meter__rules">
|
|
31
|
+
<li data-ts-password-rule="length">Use at least {minLength} characters</li>
|
|
32
|
+
<li data-ts-password-rule="case">Mix upper and lower case letters</li>
|
|
33
|
+
<li data-ts-password-rule="number">Include a number</li>
|
|
34
|
+
<li data-ts-password-rule="symbol">Include a symbol</li>
|
|
35
|
+
</ul>
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
<script is:inline>
|
|
39
|
+
(() => {
|
|
40
|
+
function scorePassword(value, minLength) {
|
|
41
|
+
const checks = {
|
|
42
|
+
length: value.length >= minLength,
|
|
43
|
+
case: /[a-z]/.test(value) && /[A-Z]/.test(value),
|
|
44
|
+
number: /\d/.test(value),
|
|
45
|
+
symbol: /[^A-Za-z0-9]/.test(value),
|
|
46
|
+
};
|
|
47
|
+
return { checks, score: Object.values(checks).filter(Boolean).length };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function bindMeter(meter) {
|
|
51
|
+
if (!(meter instanceof HTMLElement)) return;
|
|
52
|
+
const minLength = Number(meter.dataset.minLength || 12);
|
|
53
|
+
const inputId = meter.dataset.inputId;
|
|
54
|
+
const input = inputId
|
|
55
|
+
? document.getElementById(inputId)
|
|
56
|
+
: meter.closest('form')?.querySelector('[data-ts-password-input], input[type="password"]');
|
|
57
|
+
if (!(input instanceof HTMLInputElement)) return;
|
|
58
|
+
|
|
59
|
+
const status = meter.querySelector('[data-ts-password-meter-status]');
|
|
60
|
+
const bar = meter.querySelector('[data-ts-password-meter-bar]');
|
|
61
|
+
const labels = ['Not started', 'Weak', 'Fair', 'Good', 'Strong'];
|
|
62
|
+
|
|
63
|
+
function update() {
|
|
64
|
+
const { checks, score } = scorePassword(input.value, minLength);
|
|
65
|
+
meter.dataset.strength = String(score);
|
|
66
|
+
if (status) status.textContent = input.value ? labels[score] : labels[0];
|
|
67
|
+
if (bar instanceof HTMLElement) bar.style.setProperty('--ts-password-strength', String(score));
|
|
68
|
+
for (const [rule, passed] of Object.entries(checks)) {
|
|
69
|
+
const item = meter.querySelector(`[data-ts-password-rule="${rule}"]`);
|
|
70
|
+
if (item instanceof HTMLElement) item.dataset.passed = passed ? 'true' : 'false';
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
input.addEventListener('input', update);
|
|
75
|
+
update();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
document.querySelectorAll('[data-ts-password-meter]').forEach(bindMeter);
|
|
79
|
+
})();
|
|
80
|
+
</script>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface RadioOption {
|
|
3
|
+
label: string;
|
|
4
|
+
value: string;
|
|
5
|
+
help?: string;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
name: string;
|
|
11
|
+
legend: string;
|
|
12
|
+
value?: string;
|
|
13
|
+
options: RadioOption[];
|
|
14
|
+
required?: boolean;
|
|
15
|
+
class?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const {
|
|
19
|
+
name,
|
|
20
|
+
legend,
|
|
21
|
+
value,
|
|
22
|
+
options,
|
|
23
|
+
required = false,
|
|
24
|
+
class: className,
|
|
25
|
+
} = Astro.props as Props;
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
<fieldset class:list={['ts-radio-group', className]}>
|
|
29
|
+
<legend class="ts-radio-group__legend">
|
|
30
|
+
{legend}
|
|
31
|
+
{required ? <span class="ts-field__required" aria-hidden="true">*</span> : null}
|
|
32
|
+
</legend>
|
|
33
|
+
<div class="ts-radio-group__options">
|
|
34
|
+
{options.map((option) => {
|
|
35
|
+
const optionId = `${name}-${option.value}`;
|
|
36
|
+
return (
|
|
37
|
+
<label class="ts-radio-option" for={optionId} data-disabled={option.disabled ? 'true' : undefined}>
|
|
38
|
+
<input
|
|
39
|
+
id={optionId}
|
|
40
|
+
type="radio"
|
|
41
|
+
name={name}
|
|
42
|
+
value={option.value}
|
|
43
|
+
checked={option.value === value}
|
|
44
|
+
required={required}
|
|
45
|
+
disabled={option.disabled}
|
|
46
|
+
/>
|
|
47
|
+
<span class="ts-radio-option__body">
|
|
48
|
+
<span class="ts-radio-option__label">{option.label}</span>
|
|
49
|
+
{option.help ? <span class="ts-radio-option__help">{option.help}</span> : null}
|
|
50
|
+
</span>
|
|
51
|
+
</label>
|
|
52
|
+
);
|
|
53
|
+
})}
|
|
54
|
+
</div>
|
|
55
|
+
</fieldset>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface SelectOption {
|
|
3
|
+
label: string;
|
|
4
|
+
value: string;
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
name: string;
|
|
10
|
+
id?: string;
|
|
11
|
+
value?: string;
|
|
12
|
+
options: SelectOption[];
|
|
13
|
+
required?: boolean;
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
ariaDescribedby?: string;
|
|
16
|
+
class?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const {
|
|
20
|
+
name,
|
|
21
|
+
id = name,
|
|
22
|
+
value,
|
|
23
|
+
options,
|
|
24
|
+
required = false,
|
|
25
|
+
disabled = false,
|
|
26
|
+
ariaDescribedby,
|
|
27
|
+
class: className,
|
|
28
|
+
} = Astro.props as Props;
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
<select
|
|
32
|
+
class:list={['ts-control', 'ts-control--select', className]}
|
|
33
|
+
id={id}
|
|
34
|
+
name={name}
|
|
35
|
+
required={required}
|
|
36
|
+
disabled={disabled}
|
|
37
|
+
aria-describedby={ariaDescribedby}
|
|
38
|
+
>
|
|
39
|
+
{options.map((option) => (
|
|
40
|
+
<option value={option.value} selected={option.value === value} disabled={option.disabled}>
|
|
41
|
+
{option.label}
|
|
42
|
+
</option>
|
|
43
|
+
))}
|
|
44
|
+
</select>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface Props {
|
|
3
|
+
name: string;
|
|
4
|
+
id?: string;
|
|
5
|
+
type?: 'text' | 'email' | 'url' | 'password' | 'search' | 'number' | 'tel';
|
|
6
|
+
value?: string | number;
|
|
7
|
+
placeholder?: string;
|
|
8
|
+
autocomplete?: string;
|
|
9
|
+
required?: boolean;
|
|
10
|
+
disabled?: boolean;
|
|
11
|
+
readonly?: boolean;
|
|
12
|
+
minlength?: number;
|
|
13
|
+
maxlength?: number;
|
|
14
|
+
pattern?: string;
|
|
15
|
+
inputmode?: 'none' | 'text' | 'tel' | 'url' | 'email' | 'numeric' | 'decimal' | 'search';
|
|
16
|
+
ariaDescribedby?: string;
|
|
17
|
+
class?: string;
|
|
18
|
+
[key: string]: unknown;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const {
|
|
22
|
+
name,
|
|
23
|
+
id = name,
|
|
24
|
+
type = 'text',
|
|
25
|
+
value,
|
|
26
|
+
placeholder,
|
|
27
|
+
autocomplete,
|
|
28
|
+
required = false,
|
|
29
|
+
disabled = false,
|
|
30
|
+
readonly = false,
|
|
31
|
+
minlength,
|
|
32
|
+
maxlength,
|
|
33
|
+
pattern,
|
|
34
|
+
inputmode,
|
|
35
|
+
ariaDescribedby,
|
|
36
|
+
class: className,
|
|
37
|
+
...inputAttributes
|
|
38
|
+
} = Astro.props as Props;
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
<input
|
|
42
|
+
{...inputAttributes}
|
|
43
|
+
class:list={['ts-control', className]}
|
|
44
|
+
id={id}
|
|
45
|
+
name={name}
|
|
46
|
+
type={type}
|
|
47
|
+
value={value}
|
|
48
|
+
placeholder={placeholder}
|
|
49
|
+
autocomplete={autocomplete}
|
|
50
|
+
required={required}
|
|
51
|
+
disabled={disabled}
|
|
52
|
+
readonly={readonly}
|
|
53
|
+
minlength={minlength}
|
|
54
|
+
maxlength={maxlength}
|
|
55
|
+
pattern={pattern}
|
|
56
|
+
inputmode={inputmode}
|
|
57
|
+
aria-describedby={ariaDescribedby}
|
|
58
|
+
/>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface Props {
|
|
3
|
+
name: string;
|
|
4
|
+
id?: string;
|
|
5
|
+
value?: string;
|
|
6
|
+
placeholder?: string;
|
|
7
|
+
rows?: number;
|
|
8
|
+
required?: boolean;
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
readonly?: boolean;
|
|
11
|
+
minlength?: number;
|
|
12
|
+
maxlength?: number;
|
|
13
|
+
ariaDescribedby?: string;
|
|
14
|
+
class?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const {
|
|
18
|
+
name,
|
|
19
|
+
id = name,
|
|
20
|
+
value,
|
|
21
|
+
placeholder,
|
|
22
|
+
rows = 4,
|
|
23
|
+
required = false,
|
|
24
|
+
disabled = false,
|
|
25
|
+
readonly = false,
|
|
26
|
+
minlength,
|
|
27
|
+
maxlength,
|
|
28
|
+
ariaDescribedby,
|
|
29
|
+
class: className,
|
|
30
|
+
} = Astro.props as Props;
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
<textarea
|
|
34
|
+
class:list={['ts-control', 'ts-control--textarea', className]}
|
|
35
|
+
id={id}
|
|
36
|
+
name={name}
|
|
37
|
+
placeholder={placeholder}
|
|
38
|
+
rows={rows}
|
|
39
|
+
required={required}
|
|
40
|
+
disabled={disabled}
|
|
41
|
+
readonly={readonly}
|
|
42
|
+
minlength={minlength}
|
|
43
|
+
maxlength={maxlength}
|
|
44
|
+
aria-describedby={ariaDescribedby}
|
|
45
|
+
>{value}</textarea>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
import Button from '../forms/Button.astro';
|
|
3
|
+
import type { ButtonAction } from '../types.js';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
eyebrow?: string;
|
|
7
|
+
title: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
actions?: ButtonAction[];
|
|
10
|
+
class?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const {
|
|
14
|
+
eyebrow,
|
|
15
|
+
title,
|
|
16
|
+
description,
|
|
17
|
+
actions = [],
|
|
18
|
+
class: className,
|
|
19
|
+
} = Astro.props as Props;
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
<header class:list={['ts-page-header', className]}>
|
|
23
|
+
<div class="ts-page-header__content">
|
|
24
|
+
{eyebrow ? <p class="ts-page-header__eyebrow">{eyebrow}</p> : null}
|
|
25
|
+
<h1 class="ts-page-header__title">{title}</h1>
|
|
26
|
+
{description ? <p class="ts-page-header__description">{description}</p> : null}
|
|
27
|
+
<slot />
|
|
28
|
+
</div>
|
|
29
|
+
{actions.length > 0 || Astro.slots.has('actions') ? (
|
|
30
|
+
<div class="ts-page-header__actions">
|
|
31
|
+
{actions.map((action) => (
|
|
32
|
+
<Button
|
|
33
|
+
href={action.href}
|
|
34
|
+
type={action.type}
|
|
35
|
+
variant={action.variant ?? 'secondary'}
|
|
36
|
+
ariaLabel={action.ariaLabel}
|
|
37
|
+
disabled={action.disabled}
|
|
38
|
+
>
|
|
39
|
+
{action.label}
|
|
40
|
+
</Button>
|
|
41
|
+
))}
|
|
42
|
+
<slot name="actions" />
|
|
43
|
+
</div>
|
|
44
|
+
) : null}
|
|
45
|
+
</header>
|