@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,110 @@
|
|
|
1
|
+
---
|
|
2
|
+
import '../../../styles/tokens.css';
|
|
3
|
+
import '../../../styles/theme.css';
|
|
4
|
+
import '../../../styles/ui.css';
|
|
5
|
+
import '../../../styles/forms.css';
|
|
6
|
+
import '../../../styles/app-shell.css';
|
|
7
|
+
import ThemeScript from '../theme/ThemeScript.astro';
|
|
8
|
+
import RailNav from './RailNav.astro';
|
|
9
|
+
import BottomNav from './BottomNav.astro';
|
|
10
|
+
import TopBar from './TopBar.astro';
|
|
11
|
+
import Button from '../forms/Button.astro';
|
|
12
|
+
import type { ButtonAction } from '../types.js';
|
|
13
|
+
import type { ThemePreference } from '../../../utils/theme.js';
|
|
14
|
+
|
|
15
|
+
interface Brand {
|
|
16
|
+
name: string;
|
|
17
|
+
tag?: string;
|
|
18
|
+
href: string;
|
|
19
|
+
logoSrc?: string;
|
|
20
|
+
logoAlt?: string;
|
|
21
|
+
mark?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface NavItem {
|
|
25
|
+
label: string;
|
|
26
|
+
href: string;
|
|
27
|
+
ariaLabel?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface Props {
|
|
31
|
+
title: string;
|
|
32
|
+
description: string;
|
|
33
|
+
currentPath: string;
|
|
34
|
+
appearance: ThemePreference;
|
|
35
|
+
brand: Brand;
|
|
36
|
+
navItems: NavItem[];
|
|
37
|
+
quickActions?: ButtonAction[];
|
|
38
|
+
bottomNavItems?: NavItem[];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const {
|
|
42
|
+
title,
|
|
43
|
+
description,
|
|
44
|
+
currentPath,
|
|
45
|
+
appearance,
|
|
46
|
+
brand,
|
|
47
|
+
navItems,
|
|
48
|
+
quickActions = [],
|
|
49
|
+
bottomNavItems = navItems.slice(0, 5),
|
|
50
|
+
} = Astro.props as Props;
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
<!doctype html>
|
|
54
|
+
<html lang="en">
|
|
55
|
+
<head>
|
|
56
|
+
<meta charset="utf-8" />
|
|
57
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
58
|
+
<title>{title}</title>
|
|
59
|
+
<meta name="description" content={description} />
|
|
60
|
+
<ThemeScript defaultScheme={appearance.scheme} defaultMode={appearance.mode} preferDefaultPreference />
|
|
61
|
+
</head>
|
|
62
|
+
<body>
|
|
63
|
+
<a class="ts-skip-link" href="#main-content">Skip to content</a>
|
|
64
|
+
<div class="ts-app-shell">
|
|
65
|
+
<aside class="ts-app-shell__rail">
|
|
66
|
+
<TopBar brand={brand} />
|
|
67
|
+
<div class="ts-app-shell__rail-context">
|
|
68
|
+
<slot name="railContext" />
|
|
69
|
+
</div>
|
|
70
|
+
<RailNav items={navItems} currentPath={currentPath} />
|
|
71
|
+
{quickActions.length > 0 ? (
|
|
72
|
+
<div class="ts-app-shell__quick-actions">
|
|
73
|
+
<p class="ts-app-shell__eyebrow">Quick actions</p>
|
|
74
|
+
<div class="ts-app-shell__quick-list">
|
|
75
|
+
{quickActions.map((action) => (
|
|
76
|
+
<Button
|
|
77
|
+
href={action.href}
|
|
78
|
+
type={action.type}
|
|
79
|
+
variant={action.variant ?? 'secondary'}
|
|
80
|
+
ariaLabel={action.ariaLabel}
|
|
81
|
+
disabled={action.disabled}
|
|
82
|
+
size="sm"
|
|
83
|
+
>
|
|
84
|
+
{action.label}
|
|
85
|
+
</Button>
|
|
86
|
+
))}
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
) : null}
|
|
90
|
+
</aside>
|
|
91
|
+
<main class="ts-app-shell__main" id="main-content">
|
|
92
|
+
<TopBar brand={brand} class="ts-app-shell__mobile-top">
|
|
93
|
+
</TopBar>
|
|
94
|
+
<header class="ts-app-shell__header">
|
|
95
|
+
<div class="ts-app-shell__title">
|
|
96
|
+
<h1>{title}</h1>
|
|
97
|
+
<p>{description}</p>
|
|
98
|
+
</div>
|
|
99
|
+
<div class="ts-app-shell__header-actions">
|
|
100
|
+
<slot name="headerAction" />
|
|
101
|
+
</div>
|
|
102
|
+
</header>
|
|
103
|
+
<slot name="projectContext" />
|
|
104
|
+
<slot />
|
|
105
|
+
</main>
|
|
106
|
+
{bottomNavItems.length > 0 ? <BottomNav items={bottomNavItems} currentPath={currentPath} /> : null}
|
|
107
|
+
</div>
|
|
108
|
+
<slot name="sensitiveModal" />
|
|
109
|
+
</body>
|
|
110
|
+
</html>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface NavItem {
|
|
3
|
+
label: string;
|
|
4
|
+
href: string;
|
|
5
|
+
ariaLabel?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
items: NavItem[];
|
|
10
|
+
currentPath: string;
|
|
11
|
+
label?: string;
|
|
12
|
+
class?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const {
|
|
16
|
+
items,
|
|
17
|
+
currentPath,
|
|
18
|
+
label = 'Primary',
|
|
19
|
+
class: className,
|
|
20
|
+
} = Astro.props as Props;
|
|
21
|
+
|
|
22
|
+
function isCurrentPath(href: string) {
|
|
23
|
+
if (href === '/') return currentPath === '/';
|
|
24
|
+
if (href.endsWith('/')) return currentPath === href || currentPath.startsWith(href);
|
|
25
|
+
return currentPath === href || currentPath.startsWith(`${href}/`);
|
|
26
|
+
}
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
<nav class:list={['ts-bottom-nav', className]} aria-label={label}>
|
|
30
|
+
{items.map((item) => (
|
|
31
|
+
<a class="ts-bottom-nav__link" href={item.href} aria-label={item.ariaLabel} aria-current={isCurrentPath(item.href) ? 'page' : undefined}>
|
|
32
|
+
<span>{item.label}</span>
|
|
33
|
+
</a>
|
|
34
|
+
))}
|
|
35
|
+
</nav>
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
import Badge from '../data/Badge.astro';
|
|
3
|
+
import Button from '../forms/Button.astro';
|
|
4
|
+
import type { ButtonAction } from '../types.js';
|
|
5
|
+
|
|
6
|
+
interface TabItem {
|
|
7
|
+
label: string;
|
|
8
|
+
href: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface Props {
|
|
12
|
+
title: string;
|
|
13
|
+
description?: string;
|
|
14
|
+
badges?: string[];
|
|
15
|
+
actions?: ButtonAction[];
|
|
16
|
+
tabs?: TabItem[];
|
|
17
|
+
currentPath?: string;
|
|
18
|
+
class?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const {
|
|
22
|
+
title,
|
|
23
|
+
description,
|
|
24
|
+
badges = [],
|
|
25
|
+
actions = [],
|
|
26
|
+
tabs = [],
|
|
27
|
+
currentPath = '',
|
|
28
|
+
class: className,
|
|
29
|
+
} = Astro.props as Props;
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
<section class:list={['ts-project-header', className]}>
|
|
33
|
+
<div class="ts-project-header__main">
|
|
34
|
+
{badges.length > 0 ? (
|
|
35
|
+
<div class="ts-project-header__badges">
|
|
36
|
+
{badges.map((badge) => <Badge>{badge}</Badge>)}
|
|
37
|
+
</div>
|
|
38
|
+
) : null}
|
|
39
|
+
<h2>{title}</h2>
|
|
40
|
+
{description ? <p>{description}</p> : null}
|
|
41
|
+
</div>
|
|
42
|
+
{actions.length > 0 || Astro.slots.has('actions') ? (
|
|
43
|
+
<div class="ts-project-header__actions">
|
|
44
|
+
{actions.map((action) => (
|
|
45
|
+
<Button
|
|
46
|
+
href={action.href}
|
|
47
|
+
type={action.type}
|
|
48
|
+
variant={action.variant ?? 'secondary'}
|
|
49
|
+
ariaLabel={action.ariaLabel}
|
|
50
|
+
disabled={action.disabled}
|
|
51
|
+
size="sm"
|
|
52
|
+
>
|
|
53
|
+
{action.label}
|
|
54
|
+
</Button>
|
|
55
|
+
))}
|
|
56
|
+
<slot name="actions" />
|
|
57
|
+
</div>
|
|
58
|
+
) : null}
|
|
59
|
+
{tabs.length > 0 ? (
|
|
60
|
+
<nav class="ts-shell-tabs" aria-label="Project">
|
|
61
|
+
{tabs.map((tab) => (
|
|
62
|
+
<a class="ts-shell-tab" href={tab.href} aria-current={currentPath === tab.href ? 'page' : undefined}>{tab.label}</a>
|
|
63
|
+
))}
|
|
64
|
+
</nav>
|
|
65
|
+
) : null}
|
|
66
|
+
</section>
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
---
|
|
2
|
+
import '../../../styles/tokens.css';
|
|
3
|
+
import '../../../styles/theme.css';
|
|
4
|
+
import '../../../styles/ui.css';
|
|
5
|
+
import '../../../styles/forms.css';
|
|
6
|
+
import '../../../styles/app-shell.css';
|
|
7
|
+
import ThemeScript from '../theme/ThemeScript.astro';
|
|
8
|
+
import ThemeMenu from '../theme/ThemeMenu.astro';
|
|
9
|
+
import TopBar from './TopBar.astro';
|
|
10
|
+
import Button from '../forms/Button.astro';
|
|
11
|
+
import type { ButtonAction } from '../types.js';
|
|
12
|
+
import type { ThemePreference } from '../../../utils/theme.js';
|
|
13
|
+
|
|
14
|
+
interface Brand {
|
|
15
|
+
name: string;
|
|
16
|
+
tag?: string;
|
|
17
|
+
href: string;
|
|
18
|
+
logoSrc?: string;
|
|
19
|
+
logoAlt?: string;
|
|
20
|
+
mark?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface NavItem {
|
|
24
|
+
label: string;
|
|
25
|
+
href: string;
|
|
26
|
+
ariaLabel?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface Props {
|
|
30
|
+
title: string;
|
|
31
|
+
description: string;
|
|
32
|
+
currentPath: string;
|
|
33
|
+
appearance: ThemePreference;
|
|
34
|
+
brand: Brand;
|
|
35
|
+
navItems: NavItem[];
|
|
36
|
+
actions?: ButtonAction[];
|
|
37
|
+
showAppearanceControl?: boolean;
|
|
38
|
+
preferServerAppearance?: boolean;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const {
|
|
42
|
+
title,
|
|
43
|
+
description,
|
|
44
|
+
currentPath,
|
|
45
|
+
appearance,
|
|
46
|
+
brand,
|
|
47
|
+
navItems,
|
|
48
|
+
actions = [],
|
|
49
|
+
showAppearanceControl = true,
|
|
50
|
+
preferServerAppearance = false,
|
|
51
|
+
} = Astro.props as Props;
|
|
52
|
+
|
|
53
|
+
function isCurrentPath(href: string) {
|
|
54
|
+
if (href === '/') return currentPath === '/';
|
|
55
|
+
return currentPath === href || currentPath.startsWith(href);
|
|
56
|
+
}
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
<!doctype html>
|
|
60
|
+
<html lang="en">
|
|
61
|
+
<head>
|
|
62
|
+
<meta charset="utf-8" />
|
|
63
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
64
|
+
<title>{title}</title>
|
|
65
|
+
<meta name="description" content={description} />
|
|
66
|
+
<ThemeScript
|
|
67
|
+
defaultScheme={appearance.scheme}
|
|
68
|
+
defaultMode={appearance.mode}
|
|
69
|
+
preferDefaultPreference={preferServerAppearance}
|
|
70
|
+
/>
|
|
71
|
+
</head>
|
|
72
|
+
<body>
|
|
73
|
+
<a class="ts-skip-link" href="#main-content">Skip to content</a>
|
|
74
|
+
<div class="ts-public-shell">
|
|
75
|
+
<header class="ts-public-shell__header">
|
|
76
|
+
<TopBar brand={brand}>
|
|
77
|
+
<Fragment slot="actions">
|
|
78
|
+
{showAppearanceControl ? <ThemeMenu selectedScheme={appearance.scheme} selectedMode={appearance.mode} /> : null}
|
|
79
|
+
{actions.map((action) => (
|
|
80
|
+
<Button
|
|
81
|
+
href={action.href}
|
|
82
|
+
type={action.type}
|
|
83
|
+
variant={action.variant ?? 'secondary'}
|
|
84
|
+
ariaLabel={action.ariaLabel}
|
|
85
|
+
disabled={action.disabled}
|
|
86
|
+
size="sm"
|
|
87
|
+
>
|
|
88
|
+
{action.label}
|
|
89
|
+
</Button>
|
|
90
|
+
))}
|
|
91
|
+
<slot name="actions" />
|
|
92
|
+
</Fragment>
|
|
93
|
+
</TopBar>
|
|
94
|
+
<nav class="ts-public-shell__nav" aria-label="Primary">
|
|
95
|
+
{navItems.map((item) => (
|
|
96
|
+
<a class="ts-public-shell__link" href={item.href} aria-label={item.ariaLabel} aria-current={isCurrentPath(item.href) ? 'page' : undefined}>
|
|
97
|
+
{item.label}
|
|
98
|
+
</a>
|
|
99
|
+
))}
|
|
100
|
+
</nav>
|
|
101
|
+
</header>
|
|
102
|
+
<main class="ts-public-shell__main" id="main-content">
|
|
103
|
+
<slot />
|
|
104
|
+
</main>
|
|
105
|
+
<slot name="footer" />
|
|
106
|
+
</div>
|
|
107
|
+
</body>
|
|
108
|
+
</html>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface NavItem {
|
|
3
|
+
label: string;
|
|
4
|
+
href: string;
|
|
5
|
+
ariaLabel?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
items: NavItem[];
|
|
10
|
+
currentPath: string;
|
|
11
|
+
label?: string;
|
|
12
|
+
class?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const {
|
|
16
|
+
items,
|
|
17
|
+
currentPath,
|
|
18
|
+
label = 'Application',
|
|
19
|
+
class: className,
|
|
20
|
+
} = Astro.props as Props;
|
|
21
|
+
|
|
22
|
+
function isCurrentPath(href: string) {
|
|
23
|
+
if (href === '/') return currentPath === '/';
|
|
24
|
+
if (href.endsWith('/')) return currentPath === href || currentPath.startsWith(href);
|
|
25
|
+
return currentPath === href || currentPath.startsWith(`${href}/`);
|
|
26
|
+
}
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
<nav class:list={['ts-rail-nav', className]} aria-label={label}>
|
|
30
|
+
{items.map((item) => (
|
|
31
|
+
<a class="ts-rail-nav__link" href={item.href} aria-label={item.ariaLabel} aria-current={isCurrentPath(item.href) ? 'page' : undefined}>
|
|
32
|
+
<span>{item.label}</span>
|
|
33
|
+
</a>
|
|
34
|
+
))}
|
|
35
|
+
</nav>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
import Button from '../forms/Button.astro';
|
|
3
|
+
import type { ButtonAction } from '../types.js';
|
|
4
|
+
|
|
5
|
+
interface Brand {
|
|
6
|
+
name: string;
|
|
7
|
+
tag?: string;
|
|
8
|
+
href: string;
|
|
9
|
+
logoSrc?: string;
|
|
10
|
+
logoAlt?: string;
|
|
11
|
+
mark?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface Props {
|
|
15
|
+
brand: Brand;
|
|
16
|
+
actions?: ButtonAction[];
|
|
17
|
+
class?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const {
|
|
21
|
+
brand,
|
|
22
|
+
actions = [],
|
|
23
|
+
class: className,
|
|
24
|
+
} = Astro.props as Props;
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
<div class:list={['ts-top-bar', className]}>
|
|
28
|
+
<a class="ts-shell-brand" href={brand.href}>
|
|
29
|
+
<span class="ts-shell-brand__mark">
|
|
30
|
+
{brand.logoSrc ? <img src={brand.logoSrc} alt={brand.logoAlt ?? ''} width="40" height="40" loading="eager" /> : brand.mark}
|
|
31
|
+
</span>
|
|
32
|
+
<span class="ts-shell-brand__text">
|
|
33
|
+
<span class="ts-shell-brand__name">{brand.name}</span>
|
|
34
|
+
{brand.tag ? <span class="ts-shell-brand__tag">{brand.tag}</span> : null}
|
|
35
|
+
</span>
|
|
36
|
+
</a>
|
|
37
|
+
<div class="ts-top-bar__actions">
|
|
38
|
+
<slot name="actions" />
|
|
39
|
+
{actions.map((action) => (
|
|
40
|
+
<Button
|
|
41
|
+
href={action.href}
|
|
42
|
+
type={action.type}
|
|
43
|
+
variant={action.variant ?? 'secondary'}
|
|
44
|
+
ariaLabel={action.ariaLabel}
|
|
45
|
+
disabled={action.disabled}
|
|
46
|
+
size="sm"
|
|
47
|
+
>
|
|
48
|
+
{action.label}
|
|
49
|
+
</Button>
|
|
50
|
+
))}
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { Tone } from '../types.js';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
href?: string;
|
|
6
|
+
eyebrow?: string;
|
|
7
|
+
title?: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
tone?: Tone;
|
|
10
|
+
interactive?: boolean;
|
|
11
|
+
class?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const {
|
|
15
|
+
href,
|
|
16
|
+
eyebrow,
|
|
17
|
+
title,
|
|
18
|
+
description,
|
|
19
|
+
tone = 'default',
|
|
20
|
+
interactive = false,
|
|
21
|
+
class: className,
|
|
22
|
+
} = Astro.props as Props;
|
|
23
|
+
|
|
24
|
+
const classes = ['ts-card', className].filter(Boolean).join(' ');
|
|
25
|
+
const isInteractive = href || interactive;
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
{
|
|
29
|
+
href ? (
|
|
30
|
+
<a href={href} class={classes} data-tone={tone} data-interactive="true">
|
|
31
|
+
{eyebrow ? <p class="ts-card__eyebrow">{eyebrow}</p> : null}
|
|
32
|
+
{title ? <h3 class="ts-card__title">{title}</h3> : null}
|
|
33
|
+
{description ? <p class="ts-card__description">{description}</p> : null}
|
|
34
|
+
<div class="ts-card__body"><slot /></div>
|
|
35
|
+
<slot name="footer" />
|
|
36
|
+
</a>
|
|
37
|
+
) : (
|
|
38
|
+
<article class={classes} data-tone={tone} data-interactive={isInteractive ? 'true' : undefined}>
|
|
39
|
+
{eyebrow ? <p class="ts-card__eyebrow">{eyebrow}</p> : null}
|
|
40
|
+
{title ? <h3 class="ts-card__title">{title}</h3> : null}
|
|
41
|
+
{description ? <p class="ts-card__description">{description}</p> : null}
|
|
42
|
+
<div class="ts-card__body"><slot /></div>
|
|
43
|
+
<slot name="footer" />
|
|
44
|
+
</article>
|
|
45
|
+
)
|
|
46
|
+
}
|
|
@@ -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
|
+
<section class:list={['ts-empty-state', className]}>
|
|
23
|
+
{eyebrow ? <p class="ts-empty-state__eyebrow">{eyebrow}</p> : null}
|
|
24
|
+
<h2 class="ts-empty-state__title">{title}</h2>
|
|
25
|
+
{description ? <p class="ts-empty-state__description">{description}</p> : null}
|
|
26
|
+
<div class="ts-empty-state__body">
|
|
27
|
+
<slot />
|
|
28
|
+
</div>
|
|
29
|
+
{actions.length > 0 || Astro.slots.has('actions') ? (
|
|
30
|
+
<div class="ts-empty-state__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
|
+
</section>
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
import Button from '../forms/Button.astro';
|
|
3
|
+
import type { ButtonAction, Tone } from '../types.js';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
eyebrow?: string;
|
|
7
|
+
title?: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
tone?: Tone;
|
|
10
|
+
actions?: ButtonAction[];
|
|
11
|
+
class?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const {
|
|
15
|
+
eyebrow,
|
|
16
|
+
title,
|
|
17
|
+
description,
|
|
18
|
+
tone = 'default',
|
|
19
|
+
actions = [],
|
|
20
|
+
class: className,
|
|
21
|
+
} = Astro.props as Props;
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
<section class:list={['ts-panel', className]} data-tone={tone}>
|
|
25
|
+
{eyebrow || title || description || actions.length > 0 || Astro.slots.has('actions') ? (
|
|
26
|
+
<header class="ts-panel__header">
|
|
27
|
+
<div class="ts-panel__heading">
|
|
28
|
+
{eyebrow ? <p class="ts-panel__eyebrow">{eyebrow}</p> : null}
|
|
29
|
+
{title ? <h2 class="ts-panel__title">{title}</h2> : null}
|
|
30
|
+
{description ? <p class="ts-panel__description">{description}</p> : null}
|
|
31
|
+
</div>
|
|
32
|
+
{actions.length > 0 || Astro.slots.has('actions') ? (
|
|
33
|
+
<div class="ts-panel__actions">
|
|
34
|
+
{actions.map((action) => (
|
|
35
|
+
<Button
|
|
36
|
+
href={action.href}
|
|
37
|
+
type={action.type}
|
|
38
|
+
variant={action.variant ?? 'secondary'}
|
|
39
|
+
ariaLabel={action.ariaLabel}
|
|
40
|
+
disabled={action.disabled}
|
|
41
|
+
size="sm"
|
|
42
|
+
>
|
|
43
|
+
{action.label}
|
|
44
|
+
</Button>
|
|
45
|
+
))}
|
|
46
|
+
<slot name="actions" />
|
|
47
|
+
</div>
|
|
48
|
+
) : null}
|
|
49
|
+
</header>
|
|
50
|
+
) : null}
|
|
51
|
+
<div class="ts-panel__body">
|
|
52
|
+
<slot />
|
|
53
|
+
</div>
|
|
54
|
+
</section>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
import ThemeSelector from './ThemeSelector.astro';
|
|
3
|
+
import type { ThemePreference } from '../../../utils/theme.js';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
selectedScheme?: ThemePreference['scheme'];
|
|
7
|
+
selectedMode?: ThemePreference['mode'];
|
|
8
|
+
label?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const {
|
|
12
|
+
selectedScheme,
|
|
13
|
+
selectedMode,
|
|
14
|
+
label = 'Appearance',
|
|
15
|
+
} = Astro.props as Props;
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
<details class="ts-theme-menu" data-ts-theme-menu>
|
|
19
|
+
<summary class="ts-theme-menu__trigger" aria-label={label} title={label}>
|
|
20
|
+
<svg aria-hidden="true" viewBox="0 0 24 24" focusable="false">
|
|
21
|
+
<path d="M12 3.25a8.75 8.75 0 0 0 0 17.5h1.1a2.05 2.05 0 0 0 1.46-3.49 0.55 0.55 0 0 1 .39-.94h1.45a4.35 4.35 0 0 0 4.35-4.35A8.72 8.72 0 0 0 12 3.25Zm-4.1 9.1a1.35 1.35 0 1 1 0-2.7 1.35 1.35 0 0 1 0 2.7Zm2.3-4.1a1.35 1.35 0 1 1 0-2.7 1.35 1.35 0 0 1 0 2.7Zm3.9 0a1.35 1.35 0 1 1 0-2.7 1.35 1.35 0 0 1 0 2.7Zm2.6 4.1a1.35 1.35 0 1 1 0-2.7 1.35 1.35 0 0 1 0 2.7Z" />
|
|
22
|
+
</svg>
|
|
23
|
+
</summary>
|
|
24
|
+
<div class="ts-theme-menu__panel">
|
|
25
|
+
<ThemeSelector
|
|
26
|
+
label={label}
|
|
27
|
+
selectedScheme={selectedScheme}
|
|
28
|
+
selectedMode={selectedMode}
|
|
29
|
+
compact={false}
|
|
30
|
+
/>
|
|
31
|
+
</div>
|
|
32
|
+
</details>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface Props {
|
|
3
|
+
swatches: string[];
|
|
4
|
+
label?: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const { swatches, label = 'Color scheme preview' } = Astro.props as Props;
|
|
8
|
+
const visibleSwatches = swatches.slice(0, 4);
|
|
9
|
+
const swatchStyle = visibleSwatches
|
|
10
|
+
.map((swatch, index) => `--ts-preview-swatch-${index + 1}: ${swatch}`)
|
|
11
|
+
.join('; ');
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
<span class="ts-theme-swatch" style={swatchStyle} role="img" aria-label={label}>
|
|
15
|
+
{visibleSwatches.map((_, index) => (
|
|
16
|
+
<span class="ts-theme-swatch__dot" data-swatch-index={index + 1} aria-hidden="true"></span>
|
|
17
|
+
))}
|
|
18
|
+
</span>
|