@ansiversa/components 0.0.17 → 0.0.19
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 +12 -1
- package/package.json +1 -1
- package/src/AvActionSwitcher.astro +177 -0
- package/src/AvBrand.astro +20 -3
- package/src/AvBreadcrumb.astro +110 -0
- package/src/AvButton.astro +11 -2
- package/src/AvCard.astro +5 -1
- package/src/AvCheckbox.astro +5 -0
- package/src/AvChipList.astro +18 -0
- package/src/AvContainer.astro +6 -2
- package/src/AvDivider.astro +8 -2
- package/src/AvDrawer.astro +133 -0
- package/src/AvEmptyState.astro +25 -0
- package/src/AvFeatureItem.astro +5 -1
- package/src/AvFeatureList.astro +8 -2
- package/src/AvFooter.astro +6 -2
- package/src/AvInput.astro +3 -0
- package/src/AvItemList.astro +25 -0
- package/src/AvNavbar.astro +6 -2
- package/src/AvNavbarActions.astro +9 -2
- package/src/AvPageHeader.astro +42 -0
- package/src/AvSelect.astro +59 -0
- package/src/AvTemplateCard.astro +24 -0
- package/src/AvTemplateGrid.astro +9 -0
- package/src/AvTextarea.astro +35 -0
- package/src/AvTimeline.astro +13 -2
- package/src/AvToolbar.astro +42 -0
package/index.ts
CHANGED
|
@@ -10,7 +10,18 @@ export { default as AvFeatureItem } from './src/AvFeatureItem.astro';
|
|
|
10
10
|
export { default as AvFeatureList } from './src/AvFeatureList.astro';
|
|
11
11
|
export { default as AvFooter } from './src/AvFooter.astro';
|
|
12
12
|
export { default as AvInput } from './src/AvInput.astro';
|
|
13
|
+
export { default as AvSelect } from './src/AvSelect.astro';
|
|
14
|
+
export { default as AvTextarea } from './src/AvTextarea.astro';
|
|
13
15
|
export { default as AvNavbar } from './src/AvNavbar.astro';
|
|
14
16
|
export { default as AvNavbarActions } from './src/AvNavbarActions.astro';
|
|
15
17
|
export { default as AvTimeline } from './src/AvTimeline.astro';
|
|
16
|
-
|
|
18
|
+
export { default as AvBreadcrumb } from './src/AvBreadcrumb.astro';
|
|
19
|
+
export { default as AvActionSwitcher } from './src/AvActionSwitcher.astro';
|
|
20
|
+
export { default as AvPageHeader } from './src/AvPageHeader.astro';
|
|
21
|
+
export { default as AvToolbar } from './src/AvToolbar.astro';
|
|
22
|
+
export { default as AvEmptyState } from './src/AvEmptyState.astro';
|
|
23
|
+
export { default as AvItemList } from './src/AvItemList.astro';
|
|
24
|
+
export { default as AvChipList } from './src/AvChipList.astro';
|
|
25
|
+
export { default as AvTemplateCard } from './src/AvTemplateCard.astro';
|
|
26
|
+
export { default as AvTemplateGrid } from './src/AvTemplateGrid.astro';
|
|
27
|
+
export { default as AvDrawer } from './src/AvDrawer.astro';
|
package/package.json
CHANGED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
---
|
|
2
|
+
import AvButton from "./AvButton.astro";
|
|
3
|
+
import AvCard from "./AvCard.astro";
|
|
4
|
+
|
|
5
|
+
const actions = [
|
|
6
|
+
{
|
|
7
|
+
label: "Home",
|
|
8
|
+
description: "Learn what the builder offers and start a resume quickly.",
|
|
9
|
+
href: "/",
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
label: "Resume Templates",
|
|
13
|
+
description: "Pick a layout before you start editing.",
|
|
14
|
+
href: "/templates",
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
label: "Create Resume",
|
|
18
|
+
description: "Open the guided editor with your chosen template.",
|
|
19
|
+
href: "/app/resume/new",
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
label: "My Resumes",
|
|
23
|
+
description: "Review and edit saved resumes in one place.",
|
|
24
|
+
href: "/app/resumes",
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
label: "Edit Sample Resume",
|
|
28
|
+
description: "Jump into the sample editor to see the workflow.",
|
|
29
|
+
href: "/app/resume/r1",
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
label: "Download Sample PDF",
|
|
33
|
+
description: "Preview the PDF output for a resume.",
|
|
34
|
+
href: "/app/resume/r1/pdf",
|
|
35
|
+
},
|
|
36
|
+
];
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
<div
|
|
40
|
+
class="av-action-switcher"
|
|
41
|
+
x-data="{ open: false }"
|
|
42
|
+
x-init="window.addEventListener('keydown', (event) => { if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === 'k') { event.preventDefault(); open = !open; } })"
|
|
43
|
+
@keydown.escape.window="open = false"
|
|
44
|
+
@click.outside="open = false"
|
|
45
|
+
>
|
|
46
|
+
<button
|
|
47
|
+
class="av-action-switcher__toggle"
|
|
48
|
+
type="button"
|
|
49
|
+
aria-label="Open quick actions"
|
|
50
|
+
:aria-expanded="open"
|
|
51
|
+
aria-haspopup="true"
|
|
52
|
+
@keydown.enter.prevent="open = !open"
|
|
53
|
+
@click="open = !open"
|
|
54
|
+
>
|
|
55
|
+
<span x-show="!open">Open actions</span>
|
|
56
|
+
<span x-show="open">Close actions</span>
|
|
57
|
+
</button>
|
|
58
|
+
|
|
59
|
+
<div
|
|
60
|
+
class="av-action-switcher__panel"
|
|
61
|
+
x-show="open"
|
|
62
|
+
x-cloak
|
|
63
|
+
x-transition.origin.bottom.right
|
|
64
|
+
role="menu"
|
|
65
|
+
aria-label="Quick navigation"
|
|
66
|
+
>
|
|
67
|
+
<AvCard className="av-action-switcher__card">
|
|
68
|
+
<div class="av-action-switcher__header">
|
|
69
|
+
<p class="av-action-switcher__title">Quick actions</p>
|
|
70
|
+
<p class="av-action-switcher__subtitle">Move between pages without losing context.</p>
|
|
71
|
+
</div>
|
|
72
|
+
<ul class="av-action-switcher__list">
|
|
73
|
+
{actions.map((action) => (
|
|
74
|
+
<li role="none">
|
|
75
|
+
<div class="av-action-switcher__item">
|
|
76
|
+
<div class="av-action-switcher__item-text">
|
|
77
|
+
<p class="av-action-switcher__item-label">{action.label}</p>
|
|
78
|
+
<p class="av-action-switcher__item-description">{action.description}</p>
|
|
79
|
+
</div>
|
|
80
|
+
<AvButton href={action.href} size="sm" aria-label={`Go to ${action.label}`} role="menuitem">
|
|
81
|
+
Go
|
|
82
|
+
</AvButton>
|
|
83
|
+
</div>
|
|
84
|
+
</li>
|
|
85
|
+
))}
|
|
86
|
+
</ul>
|
|
87
|
+
</AvCard>
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
<style>
|
|
92
|
+
.av-action-switcher {
|
|
93
|
+
position: fixed;
|
|
94
|
+
right: 1.5rem;
|
|
95
|
+
bottom: 1.5rem;
|
|
96
|
+
display: flex;
|
|
97
|
+
flex-direction: column;
|
|
98
|
+
gap: 0.75rem;
|
|
99
|
+
z-index: 40;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.av-action-switcher__toggle {
|
|
103
|
+
background: linear-gradient(135deg, #2563eb, #0ea5e9);
|
|
104
|
+
color: #fff;
|
|
105
|
+
border: none;
|
|
106
|
+
border-radius: 9999px;
|
|
107
|
+
padding: 0.75rem 1.25rem;
|
|
108
|
+
font-weight: 600;
|
|
109
|
+
box-shadow: 0 10px 25px rgba(37, 99, 235, 0.25);
|
|
110
|
+
cursor: pointer;
|
|
111
|
+
transition: transform 0.15s ease, box-shadow 0.15s ease;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.av-action-switcher__toggle:hover {
|
|
115
|
+
transform: translateY(-1px);
|
|
116
|
+
box-shadow: 0 14px 30px rgba(37, 99, 235, 0.35);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.av-action-switcher__panel {
|
|
120
|
+
width: min(420px, calc(100vw - 2rem));
|
|
121
|
+
align-self: flex-end;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.av-action-switcher__card {
|
|
125
|
+
border: 1px solid rgb(226, 232, 240);
|
|
126
|
+
box-shadow: 0 10px 30px rgba(15, 23, 42, 0.14);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.av-action-switcher__header {
|
|
130
|
+
margin-bottom: 0.75rem;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.av-action-switcher__title {
|
|
134
|
+
font-weight: 700;
|
|
135
|
+
color: rgb(15, 23, 42);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.av-action-switcher__subtitle {
|
|
139
|
+
font-size: 0.9375rem;
|
|
140
|
+
color: rgb(100, 116, 139);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.av-action-switcher__list {
|
|
144
|
+
display: flex;
|
|
145
|
+
flex-direction: column;
|
|
146
|
+
gap: 0.75rem;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.av-action-switcher__item {
|
|
150
|
+
display: flex;
|
|
151
|
+
align-items: center;
|
|
152
|
+
justify-content: space-between;
|
|
153
|
+
gap: 0.75rem;
|
|
154
|
+
padding: 0.75rem;
|
|
155
|
+
border-radius: 0.75rem;
|
|
156
|
+
background-color: rgb(248, 250, 252);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.av-action-switcher__item-text {
|
|
160
|
+
display: grid;
|
|
161
|
+
gap: 0.25rem;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.av-action-switcher__item-label {
|
|
165
|
+
font-weight: 600;
|
|
166
|
+
color: rgb(30, 41, 59);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.av-action-switcher__item-description {
|
|
170
|
+
font-size: 0.9375rem;
|
|
171
|
+
color: rgb(100, 116, 139);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
[x-cloak] {
|
|
175
|
+
display: none !important;
|
|
176
|
+
}
|
|
177
|
+
</style>
|
package/src/AvBrand.astro
CHANGED
|
@@ -14,22 +14,39 @@ interface Props {
|
|
|
14
14
|
|
|
15
15
|
/** Extra classes if needed */
|
|
16
16
|
className?: string;
|
|
17
|
+
|
|
18
|
+
/** Additional HTML attributes */
|
|
19
|
+
[attrs: string]: any;
|
|
17
20
|
}
|
|
18
21
|
|
|
19
22
|
const {
|
|
20
|
-
href =
|
|
23
|
+
href = (() => {
|
|
24
|
+
const rawDomain = (import.meta.env.ANSIVERSA_COOKIE_DOMAIN || "ansiversa.com").trim();
|
|
25
|
+
const normalizedDomain = rawDomain.replace(/^(https?:\/\/)/i, "").replace(/\/+$/, "");
|
|
26
|
+
const protocol = rawDomain.match(/^https?:\/\//i)
|
|
27
|
+
? undefined
|
|
28
|
+
: import.meta.env.PROD
|
|
29
|
+
? "https"
|
|
30
|
+
: "http";
|
|
31
|
+
const ROOT_URL = rawDomain.match(/^https?:\/\//i)
|
|
32
|
+
? rawDomain.replace(/\/+$/, "")
|
|
33
|
+
: `${protocol}://${normalizedDomain}`;
|
|
34
|
+
|
|
35
|
+
return ROOT_URL;
|
|
36
|
+
})(),
|
|
21
37
|
title = "ANSIVERSA",
|
|
22
38
|
subtitle = "Curated Apps",
|
|
23
39
|
logo = "/favicon.svg",
|
|
24
40
|
className = "",
|
|
25
|
-
|
|
41
|
+
...attrs
|
|
42
|
+
} = Astro.props as Props;
|
|
26
43
|
|
|
27
44
|
const classes = ["av-navbar-brand"];
|
|
28
45
|
if (className) classes.push(className);
|
|
29
46
|
const classAttr = classes.join(" ");
|
|
30
47
|
---
|
|
31
48
|
|
|
32
|
-
<a href={href} class={classAttr}>
|
|
49
|
+
<a href={href} class={classAttr} {...attrs}>
|
|
33
50
|
<div class="av-navbar-brand__logo">
|
|
34
51
|
<img src={logo} alt="Ansiversa logo" />
|
|
35
52
|
</div>
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
---
|
|
2
|
+
import AvButton from "./AvButton.astro";
|
|
3
|
+
import AvContainer from "./AvContainer.astro";
|
|
4
|
+
|
|
5
|
+
interface Crumb {
|
|
6
|
+
label: string;
|
|
7
|
+
href?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface Props {
|
|
11
|
+
items: Crumb[];
|
|
12
|
+
className?: string;
|
|
13
|
+
[attr: string]: any; // allow extra attrs like x-data, x-ref, etc.
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const {
|
|
17
|
+
items,
|
|
18
|
+
className = "",
|
|
19
|
+
...attrs
|
|
20
|
+
} = Astro.props as Props;
|
|
21
|
+
|
|
22
|
+
const rootClasses = ["av-breadcrumb"];
|
|
23
|
+
if (className) rootClasses.push(className);
|
|
24
|
+
const rootClassAttr = rootClasses.join(" ");
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
<nav class={rootClassAttr} aria-label="Breadcrumb" {...attrs}>
|
|
28
|
+
<AvContainer className="av-breadcrumb__container">
|
|
29
|
+
<ol class="av-breadcrumb__list">
|
|
30
|
+
{items.map((item, index) => {
|
|
31
|
+
const isLast = index === items.length - 1;
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<li
|
|
35
|
+
class="av-breadcrumb__item"
|
|
36
|
+
aria-current={isLast ? "page" : undefined}
|
|
37
|
+
>
|
|
38
|
+
{item.href && !isLast ? (
|
|
39
|
+
<AvButton
|
|
40
|
+
href={item.href}
|
|
41
|
+
variant="ghost"
|
|
42
|
+
size="sm"
|
|
43
|
+
className="av-breadcrumb__link"
|
|
44
|
+
>
|
|
45
|
+
{item.label}
|
|
46
|
+
</AvButton>
|
|
47
|
+
) : (
|
|
48
|
+
<span class={isLast ? "av-breadcrumb__current" : "av-breadcrumb__label"}>
|
|
49
|
+
{item.label}
|
|
50
|
+
</span>
|
|
51
|
+
)}
|
|
52
|
+
|
|
53
|
+
{!isLast && (
|
|
54
|
+
<span class="av-breadcrumb__separator" aria-hidden="true">
|
|
55
|
+
/
|
|
56
|
+
</span>
|
|
57
|
+
)}
|
|
58
|
+
</li>
|
|
59
|
+
);
|
|
60
|
+
})}
|
|
61
|
+
</ol>
|
|
62
|
+
</AvContainer>
|
|
63
|
+
</nav>
|
|
64
|
+
|
|
65
|
+
<style>
|
|
66
|
+
.av-breadcrumb {
|
|
67
|
+
width: 100%;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.av-breadcrumb__container {
|
|
71
|
+
/* Left aligned by default – container just gives padding/max-width */
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.av-breadcrumb__list {
|
|
75
|
+
display: flex;
|
|
76
|
+
align-items: center;
|
|
77
|
+
gap: 0.25rem;
|
|
78
|
+
padding: 0;
|
|
79
|
+
margin: 0;
|
|
80
|
+
list-style: none;
|
|
81
|
+
font-size: var(--ans-font-size-caption, 0.8125rem);
|
|
82
|
+
color: var(--ans-muted-fg, #6b7280);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.av-breadcrumb__item {
|
|
86
|
+
display: inline-flex;
|
|
87
|
+
align-items: center;
|
|
88
|
+
gap: 0.25rem;
|
|
89
|
+
white-space: nowrap;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.av-breadcrumb__link {
|
|
93
|
+
padding-inline: 0.5rem;
|
|
94
|
+
padding-block: 0.25rem;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.av-breadcrumb__label {
|
|
98
|
+
color: var(--ans-muted-fg, #6b7280);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.av-breadcrumb__current {
|
|
102
|
+
font-weight: 500;
|
|
103
|
+
color: var(--ans-fg, #111827);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.av-breadcrumb__separator {
|
|
107
|
+
margin-inline: 0.125rem;
|
|
108
|
+
color: var(--ans-border-subtle, #d1d5db);
|
|
109
|
+
}
|
|
110
|
+
</style>
|
package/src/AvButton.astro
CHANGED
|
@@ -14,6 +14,10 @@ interface Props {
|
|
|
14
14
|
disabled?: boolean;
|
|
15
15
|
/** extra classes if ever needed */
|
|
16
16
|
className?: string;
|
|
17
|
+
/** allow plain class attribute passthrough */
|
|
18
|
+
class?: string;
|
|
19
|
+
/** forward any other HTML attributes (e.g., Alpine x-*) */
|
|
20
|
+
[key: string]: any;
|
|
17
21
|
}
|
|
18
22
|
|
|
19
23
|
const {
|
|
@@ -24,6 +28,8 @@ const {
|
|
|
24
28
|
type = "button",
|
|
25
29
|
disabled = false,
|
|
26
30
|
className,
|
|
31
|
+
class: extraClass,
|
|
32
|
+
...rest
|
|
27
33
|
} = Astro.props;
|
|
28
34
|
|
|
29
35
|
// Build class list based on our global.css system
|
|
@@ -54,16 +60,19 @@ if (block) {
|
|
|
54
60
|
if (className) {
|
|
55
61
|
classes.push(className);
|
|
56
62
|
}
|
|
63
|
+
if (extraClass) {
|
|
64
|
+
classes.push(extraClass as string);
|
|
65
|
+
}
|
|
57
66
|
|
|
58
67
|
const classAttr = classes.join(" ");
|
|
59
68
|
---
|
|
60
69
|
|
|
61
70
|
{href ? (
|
|
62
|
-
<a href={href} class={classAttr}>
|
|
71
|
+
<a href={href} class={classAttr} {...rest}>
|
|
63
72
|
<slot />
|
|
64
73
|
</a>
|
|
65
74
|
) : (
|
|
66
|
-
<button type={type} class={classAttr} disabled={disabled}>
|
|
75
|
+
<button type={type} class={classAttr} disabled={disabled} {...rest}>
|
|
67
76
|
<slot />
|
|
68
77
|
</button>
|
|
69
78
|
)}
|
package/src/AvCard.astro
CHANGED
|
@@ -4,11 +4,15 @@ interface Props {
|
|
|
4
4
|
variant?: "default" | "auth" | "soft";
|
|
5
5
|
/** Extra classes for the outer wrapper */
|
|
6
6
|
className?: string;
|
|
7
|
+
|
|
8
|
+
/** Additional HTML attributes */
|
|
9
|
+
[attrs: string]: any;
|
|
7
10
|
}
|
|
8
11
|
|
|
9
12
|
const {
|
|
10
13
|
variant = "default",
|
|
11
14
|
className = "",
|
|
15
|
+
...attrs
|
|
12
16
|
} = Astro.props as Props;
|
|
13
17
|
|
|
14
18
|
const classes: string[] = [];
|
|
@@ -33,6 +37,6 @@ if (className) {
|
|
|
33
37
|
const classAttr = classes.join(" ");
|
|
34
38
|
---
|
|
35
39
|
|
|
36
|
-
<div class={classAttr}>
|
|
40
|
+
<div class={classAttr} {...attrs}>
|
|
37
41
|
<slot />
|
|
38
42
|
</div>
|
package/src/AvCheckbox.astro
CHANGED
|
@@ -14,6 +14,9 @@ interface Props {
|
|
|
14
14
|
className?: string;
|
|
15
15
|
/** Optional hint text below the checkbox */
|
|
16
16
|
hint?: string;
|
|
17
|
+
|
|
18
|
+
/** Additional HTML attributes */
|
|
19
|
+
[attrs: string]: any;
|
|
17
20
|
}
|
|
18
21
|
|
|
19
22
|
const props = Astro.props as Props;
|
|
@@ -25,6 +28,7 @@ const {
|
|
|
25
28
|
checked = false,
|
|
26
29
|
className = "",
|
|
27
30
|
hint = "",
|
|
31
|
+
...attrs
|
|
28
32
|
} = props;
|
|
29
33
|
|
|
30
34
|
const id = props.id ?? name ?? `checkbox-${Math.random().toString(36).slice(2)}`;
|
|
@@ -42,6 +46,7 @@ if (className) wrapperClasses.push(className);
|
|
|
42
46
|
class="av-checkbox"
|
|
43
47
|
required={required}
|
|
44
48
|
checked={checked}
|
|
49
|
+
{...attrs}
|
|
45
50
|
/>
|
|
46
51
|
<span>
|
|
47
52
|
{label ? label : <slot />}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface Chip {
|
|
3
|
+
id: string;
|
|
4
|
+
label: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
chips: Chip[];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const { chips } = Astro.props as Props;
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
<div class="av-chip-list">
|
|
15
|
+
{chips.map((chip) => (
|
|
16
|
+
<span class="av-chip-list__chip">{chip.label}</span>
|
|
17
|
+
))}
|
|
18
|
+
</div>
|
package/src/AvContainer.astro
CHANGED
|
@@ -5,12 +5,16 @@ interface Props {
|
|
|
5
5
|
|
|
6
6
|
/** Extra classes */
|
|
7
7
|
className?: string;
|
|
8
|
+
|
|
9
|
+
/** Additional HTML attributes */
|
|
10
|
+
[attrs: string]: any;
|
|
8
11
|
}
|
|
9
12
|
|
|
10
13
|
const {
|
|
11
14
|
size = "default",
|
|
12
15
|
className = "",
|
|
13
|
-
|
|
16
|
+
...attrs
|
|
17
|
+
} = Astro.props as Props;
|
|
14
18
|
|
|
15
19
|
// Base
|
|
16
20
|
const classes = ["av-container"];
|
|
@@ -25,6 +29,6 @@ if (className) classes.push(className);
|
|
|
25
29
|
const classAttr = classes.join(" ");
|
|
26
30
|
---
|
|
27
31
|
|
|
28
|
-
<div class={classAttr}>
|
|
32
|
+
<div class={classAttr} {...attrs}>
|
|
29
33
|
<slot />
|
|
30
34
|
</div>
|
package/src/AvDivider.astro
CHANGED
|
@@ -2,12 +2,18 @@
|
|
|
2
2
|
interface Props {
|
|
3
3
|
/** Extra classes for customization */
|
|
4
4
|
className?: string;
|
|
5
|
+
|
|
6
|
+
/** Additional HTML attributes */
|
|
7
|
+
[attrs: string]: any;
|
|
5
8
|
}
|
|
6
9
|
|
|
7
|
-
const {
|
|
10
|
+
const {
|
|
11
|
+
className = "",
|
|
12
|
+
...attrs
|
|
13
|
+
} = Astro.props as Props;
|
|
8
14
|
const classes = ["av-divider"];
|
|
9
15
|
if (className) classes.push(className);
|
|
10
16
|
const classAttr = classes.join(" ");
|
|
11
17
|
---
|
|
12
18
|
|
|
13
|
-
<div class={classAttr} />
|
|
19
|
+
<div class={classAttr} {...attrs} />
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface Props {
|
|
3
|
+
title?: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const { title, description } = Astro.props as Props;
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
<div class="av-drawer-root">
|
|
11
|
+
<div class="av-drawer-backdrop" aria-hidden="true"></div>
|
|
12
|
+
<aside
|
|
13
|
+
class="av-drawer"
|
|
14
|
+
role="dialog"
|
|
15
|
+
aria-modal="true"
|
|
16
|
+
aria-labelledby={title ? "av-drawer-title" : undefined}
|
|
17
|
+
>
|
|
18
|
+
<header class="av-drawer__header">
|
|
19
|
+
{title && (
|
|
20
|
+
<h2 id="av-drawer-title" class="av-drawer__title">
|
|
21
|
+
{title}
|
|
22
|
+
</h2>
|
|
23
|
+
)}
|
|
24
|
+
{description && <p class="av-drawer__description">{description}</p>}
|
|
25
|
+
<button
|
|
26
|
+
type="button"
|
|
27
|
+
class="av-drawer__close"
|
|
28
|
+
aria-label="Close"
|
|
29
|
+
x-on:click="$dispatch('close-drawer')"
|
|
30
|
+
>
|
|
31
|
+
<span aria-hidden="true">×</span>
|
|
32
|
+
</button>
|
|
33
|
+
</header>
|
|
34
|
+
|
|
35
|
+
<div class="av-drawer__body">
|
|
36
|
+
<slot />
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<footer class="av-drawer__footer">
|
|
40
|
+
<slot name="footer" />
|
|
41
|
+
</footer>
|
|
42
|
+
</aside>
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<style>
|
|
46
|
+
.av-drawer-root {
|
|
47
|
+
position: fixed;
|
|
48
|
+
inset: 0;
|
|
49
|
+
z-index: 40;
|
|
50
|
+
display: flex;
|
|
51
|
+
justify-content: flex-end;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.av-drawer-backdrop {
|
|
55
|
+
position: absolute;
|
|
56
|
+
inset: 0;
|
|
57
|
+
background: rgba(15, 23, 42, 0.55);
|
|
58
|
+
backdrop-filter: blur(2px);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.av-drawer {
|
|
62
|
+
position: relative;
|
|
63
|
+
width: 100%;
|
|
64
|
+
max-width: 420px;
|
|
65
|
+
height: 100%;
|
|
66
|
+
background: var(--av-surface, #020617);
|
|
67
|
+
border-left: 1px solid rgba(148, 163, 184, 0.35);
|
|
68
|
+
box-shadow: -12px 0 40px rgba(15, 23, 42, 0.7);
|
|
69
|
+
display: flex;
|
|
70
|
+
flex-direction: column;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.av-drawer__header {
|
|
74
|
+
padding: 1.25rem 1.5rem;
|
|
75
|
+
border-bottom: 1px solid rgba(51, 65, 85, 0.8);
|
|
76
|
+
display: flex;
|
|
77
|
+
flex-direction: column;
|
|
78
|
+
gap: 0.25rem;
|
|
79
|
+
position: relative;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.av-drawer__title {
|
|
83
|
+
font-size: 1rem;
|
|
84
|
+
font-weight: 600;
|
|
85
|
+
color: var(--av-fg-strong, #e5e7eb);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.av-drawer__description {
|
|
89
|
+
font-size: 0.875rem;
|
|
90
|
+
color: var(--av-fg-muted, #9ca3af);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.av-drawer__close {
|
|
94
|
+
position: absolute;
|
|
95
|
+
right: 1.25rem;
|
|
96
|
+
top: 1.1rem;
|
|
97
|
+
border-radius: 999px;
|
|
98
|
+
width: 1.75rem;
|
|
99
|
+
height: 1.75rem;
|
|
100
|
+
display: inline-flex;
|
|
101
|
+
align-items: center;
|
|
102
|
+
justify-content: center;
|
|
103
|
+
border: none;
|
|
104
|
+
background: transparent;
|
|
105
|
+
color: var(--av-fg-muted, #9ca3af);
|
|
106
|
+
cursor: pointer;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.av-drawer__close:hover {
|
|
110
|
+
background: rgba(15, 23, 42, 0.8);
|
|
111
|
+
color: var(--av-fg-strong, #e5e7eb);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.av-drawer__body {
|
|
115
|
+
padding: 1.25rem 1.5rem;
|
|
116
|
+
flex: 1;
|
|
117
|
+
overflow-y: auto;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.av-drawer__footer {
|
|
121
|
+
padding: 0.75rem 1.5rem;
|
|
122
|
+
border-top: 1px solid rgba(51, 65, 85, 0.8);
|
|
123
|
+
display: flex;
|
|
124
|
+
justify-content: flex-end;
|
|
125
|
+
gap: 0.5rem;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
@media (max-width: 640px) {
|
|
129
|
+
.av-drawer {
|
|
130
|
+
max-width: 100%;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
</style>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
import AvButton from "./AvButton.astro";
|
|
3
|
+
import AvCard from "./AvCard.astro";
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
title: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
ctaLabel?: string;
|
|
9
|
+
ctaHref?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const { title, description, ctaLabel, ctaHref } = Astro.props as Props;
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
<AvCard className="av-empty-state">
|
|
16
|
+
<div class="av-empty-state__body">
|
|
17
|
+
<h3 class="av-empty-state__title">{title}</h3>
|
|
18
|
+
{description && <p class="av-empty-state__description">{description}</p>}
|
|
19
|
+
{ctaLabel && ctaHref && (
|
|
20
|
+
<AvButton href={ctaHref} variant="primary">
|
|
21
|
+
{ctaLabel}
|
|
22
|
+
</AvButton>
|
|
23
|
+
)}
|
|
24
|
+
</div>
|
|
25
|
+
</AvCard>
|
package/src/AvFeatureItem.astro
CHANGED
|
@@ -4,11 +4,15 @@ interface Props {
|
|
|
4
4
|
variant?: "primary" | "secondary" | "default";
|
|
5
5
|
/** Extra classes for the item wrapper */
|
|
6
6
|
className?: string;
|
|
7
|
+
|
|
8
|
+
/** Additional HTML attributes */
|
|
9
|
+
[attrs: string]: any;
|
|
7
10
|
}
|
|
8
11
|
|
|
9
12
|
const {
|
|
10
13
|
variant = "default",
|
|
11
14
|
className = "",
|
|
15
|
+
...attrs
|
|
12
16
|
} = Astro.props as Props;
|
|
13
17
|
|
|
14
18
|
const itemClasses = ["av-feature-item"];
|
|
@@ -19,7 +23,7 @@ if (variant === "primary") dotClasses.push("av-feature-dot--primary");
|
|
|
19
23
|
if (variant === "secondary") dotClasses.push("av-feature-dot--secondary");
|
|
20
24
|
---
|
|
21
25
|
|
|
22
|
-
<div class={itemClasses.join(" ")}>
|
|
26
|
+
<div class={itemClasses.join(" ")} {...attrs}>
|
|
23
27
|
<span class={dotClasses.join(" ")}></span>
|
|
24
28
|
<span>
|
|
25
29
|
<slot />
|
package/src/AvFeatureList.astro
CHANGED
|
@@ -2,14 +2,20 @@
|
|
|
2
2
|
interface Props {
|
|
3
3
|
/** Extra classes for the list wrapper */
|
|
4
4
|
className?: string;
|
|
5
|
+
|
|
6
|
+
/** Additional HTML attributes */
|
|
7
|
+
[attrs: string]: any;
|
|
5
8
|
}
|
|
6
9
|
|
|
7
|
-
const {
|
|
10
|
+
const {
|
|
11
|
+
className = "",
|
|
12
|
+
...attrs
|
|
13
|
+
} = Astro.props as Props;
|
|
8
14
|
const classes = ["av-feature-list"];
|
|
9
15
|
if (className) classes.push(className);
|
|
10
16
|
const classAttr = classes.join(" ");
|
|
11
17
|
---
|
|
12
18
|
|
|
13
|
-
<div class={classAttr}>
|
|
19
|
+
<div class={classAttr} {...attrs}>
|
|
14
20
|
<slot />
|
|
15
21
|
</div>
|
package/src/AvFooter.astro
CHANGED
|
@@ -13,6 +13,9 @@ interface Props {
|
|
|
13
13
|
|
|
14
14
|
/** Extra classes for the wrapper */
|
|
15
15
|
className?: string;
|
|
16
|
+
|
|
17
|
+
/** Additional HTML attributes */
|
|
18
|
+
[attrs: string]: any;
|
|
16
19
|
}
|
|
17
20
|
|
|
18
21
|
const {
|
|
@@ -22,14 +25,15 @@ const {
|
|
|
22
25
|
],
|
|
23
26
|
tagline = "Advanced Next-Gen Software Innovation and Versatility.",
|
|
24
27
|
className = "",
|
|
25
|
-
|
|
28
|
+
...attrs
|
|
29
|
+
} = Astro.props as Props;
|
|
26
30
|
|
|
27
31
|
const classes = ["av-footer"];
|
|
28
32
|
if (className) classes.push(className);
|
|
29
33
|
const classAttr = classes.join(" ");
|
|
30
34
|
---
|
|
31
35
|
|
|
32
|
-
<footer class={classAttr}>
|
|
36
|
+
<footer class={classAttr} {...attrs}>
|
|
33
37
|
<div class="av-footer-inner">
|
|
34
38
|
<p class="av-caption">
|
|
35
39
|
© {new Date().getFullYear()} Ansiversa. All rights reserved.
|
package/src/AvInput.astro
CHANGED
|
@@ -12,6 +12,7 @@ interface Props {
|
|
|
12
12
|
id?: string;
|
|
13
13
|
hint?: string;
|
|
14
14
|
autocomplete?: string;
|
|
15
|
+
[attrs: string]: any;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
const props = Astro.props as Props;
|
|
@@ -29,6 +30,7 @@ const {
|
|
|
29
30
|
id = name ?? `input-${Math.random().toString(36).slice(2)}`,
|
|
30
31
|
hint = "",
|
|
31
32
|
autocomplete,
|
|
33
|
+
...attrs
|
|
32
34
|
} = props;
|
|
33
35
|
|
|
34
36
|
const wrapperClasses = ["av-form-group", "av-input-wrapper"];
|
|
@@ -63,6 +65,7 @@ const isPassword = type === "password";
|
|
|
63
65
|
required={required}
|
|
64
66
|
disabled={disabled}
|
|
65
67
|
autocomplete={autocomplete}
|
|
68
|
+
{...attrs}
|
|
66
69
|
/>
|
|
67
70
|
|
|
68
71
|
<!-- PASSWORD TOGGLE BUTTON -->
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface Item {
|
|
3
|
+
id: string;
|
|
4
|
+
title: string;
|
|
5
|
+
subtitle?: string;
|
|
6
|
+
meta?: string;
|
|
7
|
+
isCurrent?: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface Props {
|
|
11
|
+
items: Item[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const { items } = Astro.props as Props;
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
<ul class="av-item-list">
|
|
18
|
+
{items.map((item) => (
|
|
19
|
+
<li class="av-item-list__item" data-current={item.isCurrent ? "true" : undefined}>
|
|
20
|
+
<p class="av-item-list__title">{item.title}</p>
|
|
21
|
+
{item.subtitle && <p class="av-item-list__subtitle">{item.subtitle}</p>}
|
|
22
|
+
{item.meta && <p class="av-item-list__meta">{item.meta}</p>}
|
|
23
|
+
</li>
|
|
24
|
+
))}
|
|
25
|
+
</ul>
|
package/src/AvNavbar.astro
CHANGED
|
@@ -2,18 +2,22 @@
|
|
|
2
2
|
interface Props {
|
|
3
3
|
/** Extra classes for the header element */
|
|
4
4
|
className?: string;
|
|
5
|
+
|
|
6
|
+
/** Additional HTML attributes */
|
|
7
|
+
[attrs: string]: any;
|
|
5
8
|
}
|
|
6
9
|
|
|
7
10
|
const {
|
|
8
11
|
className = "",
|
|
9
|
-
|
|
12
|
+
...attrs
|
|
13
|
+
} = Astro.props as Props;
|
|
10
14
|
|
|
11
15
|
const classes = ["av-navbar"];
|
|
12
16
|
if (className) classes.push(className);
|
|
13
17
|
const classAttr = classes.join(" ");
|
|
14
18
|
---
|
|
15
19
|
|
|
16
|
-
<header class={classAttr}>
|
|
20
|
+
<header class={classAttr} {...attrs}>
|
|
17
21
|
<div class="av-navbar-inner">
|
|
18
22
|
<slot />
|
|
19
23
|
</div>
|
|
@@ -17,9 +17,16 @@ interface User {
|
|
|
17
17
|
interface Props {
|
|
18
18
|
className?: string;
|
|
19
19
|
user?: User;
|
|
20
|
+
|
|
21
|
+
/** Additional HTML attributes */
|
|
22
|
+
[attrs: string]: any;
|
|
20
23
|
}
|
|
21
24
|
|
|
22
|
-
const {
|
|
25
|
+
const {
|
|
26
|
+
className = "",
|
|
27
|
+
user,
|
|
28
|
+
...attrs
|
|
29
|
+
} = Astro.props as Props;
|
|
23
30
|
|
|
24
31
|
const classes = ["av-navbar-actions"];
|
|
25
32
|
if (className) classes.push(className);
|
|
@@ -87,7 +94,7 @@ const userInitial =
|
|
|
87
94
|
(user?.name?.charAt(0) ?? user?.email?.charAt(0) ?? "?").toUpperCase();
|
|
88
95
|
---
|
|
89
96
|
|
|
90
|
-
<div class={classAttr}>
|
|
97
|
+
<div class={classAttr} {...attrs}>
|
|
91
98
|
<!-- Left: Always visible -->
|
|
92
99
|
{baseLinks.map((item) => (
|
|
93
100
|
<AvButton
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
import AvButton from "./AvButton.astro";
|
|
3
|
+
import AvContainer from "./AvContainer.astro";
|
|
4
|
+
|
|
5
|
+
interface Action {
|
|
6
|
+
label: string;
|
|
7
|
+
href: string;
|
|
8
|
+
variant?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface Props {
|
|
12
|
+
title: string;
|
|
13
|
+
description?: string;
|
|
14
|
+
primaryAction?: Action;
|
|
15
|
+
secondaryAction?: Action;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const { title, description, primaryAction, secondaryAction } = Astro.props as Props;
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
<section class="av-page-header">
|
|
22
|
+
<AvContainer className="av-page-header__container">
|
|
23
|
+
<div class="av-page-header__body">
|
|
24
|
+
<h1 class="av-page-header__title">{title}</h1>
|
|
25
|
+
{description && <p class="av-page-header__description">{description}</p>}
|
|
26
|
+
{(primaryAction || secondaryAction) && (
|
|
27
|
+
<div class="av-page-header__actions">
|
|
28
|
+
{primaryAction && (
|
|
29
|
+
<AvButton href={primaryAction.href} variant={primaryAction.variant ?? "primary"}>
|
|
30
|
+
{primaryAction.label}
|
|
31
|
+
</AvButton>
|
|
32
|
+
)}
|
|
33
|
+
{secondaryAction && (
|
|
34
|
+
<AvButton href={secondaryAction.href} variant={secondaryAction.variant ?? "ghost"}>
|
|
35
|
+
{secondaryAction.label}
|
|
36
|
+
</AvButton>
|
|
37
|
+
)}
|
|
38
|
+
</div>
|
|
39
|
+
)}
|
|
40
|
+
</div>
|
|
41
|
+
</AvContainer>
|
|
42
|
+
</section>
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface Props {
|
|
3
|
+
label?: string;
|
|
4
|
+
name?: string;
|
|
5
|
+
required?: boolean;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
className?: string;
|
|
8
|
+
selectClass?: string;
|
|
9
|
+
id?: string;
|
|
10
|
+
hint?: string;
|
|
11
|
+
|
|
12
|
+
/** Additional HTML attributes */
|
|
13
|
+
[attrs: string]: any;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const props = Astro.props as Props;
|
|
17
|
+
|
|
18
|
+
const {
|
|
19
|
+
label,
|
|
20
|
+
name,
|
|
21
|
+
required = false,
|
|
22
|
+
disabled = false,
|
|
23
|
+
className = "",
|
|
24
|
+
selectClass = "",
|
|
25
|
+
id = name ?? `select-${Math.random().toString(36).slice(2)}`,
|
|
26
|
+
hint = "",
|
|
27
|
+
...attrs
|
|
28
|
+
} = props;
|
|
29
|
+
|
|
30
|
+
const wrapperClasses: string[] = ["av-form-group", "av-input-wrapper"];
|
|
31
|
+
if (className) wrapperClasses.push(className);
|
|
32
|
+
|
|
33
|
+
const selectClasses: string[] = ["av-input"];
|
|
34
|
+
if (selectClass) selectClasses.push(selectClass);
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
<div class={wrapperClasses.join(" ")}>
|
|
38
|
+
{label && (
|
|
39
|
+
<label class="av-input-label" for={id}>
|
|
40
|
+
{label}
|
|
41
|
+
{required && <span class="av-input-required">*</span>}
|
|
42
|
+
</label>
|
|
43
|
+
)}
|
|
44
|
+
|
|
45
|
+
<div class="av-input-select-wrapper">
|
|
46
|
+
<select
|
|
47
|
+
id={id}
|
|
48
|
+
name={name}
|
|
49
|
+
class={selectClasses.join(" ")}
|
|
50
|
+
disabled={disabled}
|
|
51
|
+
required={required}
|
|
52
|
+
{...attrs}
|
|
53
|
+
>
|
|
54
|
+
<slot />
|
|
55
|
+
</select>
|
|
56
|
+
</div>
|
|
57
|
+
|
|
58
|
+
{hint && <p class="av-form-hint">{hint}</p>}
|
|
59
|
+
</div>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
import AvButton from "./AvButton.astro";
|
|
3
|
+
import AvCard from "./AvCard.astro";
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
name: string;
|
|
7
|
+
description: string;
|
|
8
|
+
accentClass?: string;
|
|
9
|
+
href?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const { name, description, accentClass, href = "#" } = Astro.props as Props;
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
<AvCard className={`av-template-card ${accentClass ?? ""}`.trim()}>
|
|
16
|
+
<div class="av-template-card__header">
|
|
17
|
+
<p class="av-template-card__name">{name}</p>
|
|
18
|
+
<span class="av-template-card__accent" aria-hidden="true"></span>
|
|
19
|
+
</div>
|
|
20
|
+
<p class="av-template-card__description">{description}</p>
|
|
21
|
+
<div class="av-template-card__actions">
|
|
22
|
+
<AvButton href={href}>Use Template</AvButton>
|
|
23
|
+
</div>
|
|
24
|
+
</AvCard>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface Props {
|
|
3
|
+
label?: string;
|
|
4
|
+
name?: string;
|
|
5
|
+
placeholder?: string;
|
|
6
|
+
rows?: number;
|
|
7
|
+
value?: string;
|
|
8
|
+
required?: boolean;
|
|
9
|
+
|
|
10
|
+
/** Additional HTML attributes */
|
|
11
|
+
[attrs: string]: any;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const {
|
|
15
|
+
label,
|
|
16
|
+
name,
|
|
17
|
+
placeholder,
|
|
18
|
+
rows = 4,
|
|
19
|
+
value,
|
|
20
|
+
required,
|
|
21
|
+
...attrs
|
|
22
|
+
} = Astro.props as Props;
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
<div class="av-field">
|
|
26
|
+
{label ? <label class="av-field__label">{label}</label> : null}
|
|
27
|
+
<textarea
|
|
28
|
+
class="av-textarea"
|
|
29
|
+
placeholder={placeholder}
|
|
30
|
+
rows={rows}
|
|
31
|
+
name={name}
|
|
32
|
+
required={required}
|
|
33
|
+
{...attrs}
|
|
34
|
+
>{value}</textarea>
|
|
35
|
+
</div>
|
package/src/AvTimeline.astro
CHANGED
|
@@ -7,12 +7,23 @@ export interface TimelineItem {
|
|
|
7
7
|
|
|
8
8
|
interface Props {
|
|
9
9
|
items: TimelineItem[];
|
|
10
|
+
className?: string;
|
|
11
|
+
|
|
12
|
+
/** Additional HTML attributes */
|
|
13
|
+
[attrs: string]: any;
|
|
10
14
|
}
|
|
11
15
|
|
|
12
|
-
const {
|
|
16
|
+
const {
|
|
17
|
+
items = [],
|
|
18
|
+
className = "",
|
|
19
|
+
...attrs
|
|
20
|
+
} = Astro.props as Props;
|
|
21
|
+
|
|
22
|
+
const classes = ["av-timeline"];
|
|
23
|
+
if (className) classes.push(className);
|
|
13
24
|
---
|
|
14
25
|
|
|
15
|
-
<div class="
|
|
26
|
+
<div class={classes.join(" ")} {...attrs}>
|
|
16
27
|
{items.map((item, index) => (
|
|
17
28
|
<div
|
|
18
29
|
class={`av-timeline-row${
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
import AvButton from "./AvButton.astro";
|
|
3
|
+
import AvContainer from "./AvContainer.astro";
|
|
4
|
+
import AvDivider from "./AvDivider.astro";
|
|
5
|
+
|
|
6
|
+
interface ToolbarAction {
|
|
7
|
+
label: string;
|
|
8
|
+
href: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface Props {
|
|
12
|
+
saveStatus: "idle" | "saving" | "saved";
|
|
13
|
+
lastSavedAt?: string;
|
|
14
|
+
actions?: ToolbarAction[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const { saveStatus, lastSavedAt, actions = [] } = Astro.props as Props;
|
|
18
|
+
|
|
19
|
+
const statusLabel =
|
|
20
|
+
saveStatus === "saving" ? "Saving…" : saveStatus === "saved" ? "Changes saved" : "Not saved yet";
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
<div class="av-toolbar">
|
|
24
|
+
<AvContainer className="av-toolbar__container">
|
|
25
|
+
<div class="av-toolbar__status">
|
|
26
|
+
<span class="av-toolbar__status-dot" data-state={saveStatus}></span>
|
|
27
|
+
<span class="av-toolbar__status-text">{statusLabel}</span>
|
|
28
|
+
{lastSavedAt && <span class="av-toolbar__meta">{lastSavedAt}</span>}
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
{actions.length > 0 && (
|
|
32
|
+
<div class="av-toolbar__actions">
|
|
33
|
+
{actions.map((action) => (
|
|
34
|
+
<AvButton variant="ghost" href={action.href} size="sm">
|
|
35
|
+
{action.label}
|
|
36
|
+
</AvButton>
|
|
37
|
+
))}
|
|
38
|
+
</div>
|
|
39
|
+
)}
|
|
40
|
+
</AvContainer>
|
|
41
|
+
<AvDivider />
|
|
42
|
+
</div>
|