radiant-docs 0.1.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/dist/index.js +312 -0
- package/package.json +38 -0
- package/template/.vscode/extensions.json +4 -0
- package/template/.vscode/launch.json +11 -0
- package/template/astro.config.mjs +216 -0
- package/template/ec.config.mjs +51 -0
- package/template/package-lock.json +12546 -0
- package/template/package.json +51 -0
- package/template/public/favicon.svg +9 -0
- package/template/src/assets/icons/check.svg +33 -0
- package/template/src/assets/icons/danger.svg +37 -0
- package/template/src/assets/icons/info.svg +36 -0
- package/template/src/assets/icons/lightbulb.svg +74 -0
- package/template/src/assets/icons/warning.svg +37 -0
- package/template/src/components/Header.astro +176 -0
- package/template/src/components/MdxPage.astro +49 -0
- package/template/src/components/OpenApiPage.astro +270 -0
- package/template/src/components/Search.astro +362 -0
- package/template/src/components/Sidebar.astro +19 -0
- package/template/src/components/SidebarDropdown.astro +149 -0
- package/template/src/components/SidebarGroup.astro +51 -0
- package/template/src/components/SidebarLink.astro +56 -0
- package/template/src/components/SidebarMenu.astro +46 -0
- package/template/src/components/SidebarSubgroup.astro +136 -0
- package/template/src/components/TableOfContents.astro +480 -0
- package/template/src/components/ThemeSwitcher.astro +84 -0
- package/template/src/components/endpoint/PlaygroundBar.astro +68 -0
- package/template/src/components/endpoint/PlaygroundButton.astro +44 -0
- package/template/src/components/endpoint/PlaygroundField.astro +54 -0
- package/template/src/components/endpoint/PlaygroundForm.astro +203 -0
- package/template/src/components/endpoint/RequestSnippets.astro +308 -0
- package/template/src/components/endpoint/ResponseDisplay.astro +177 -0
- package/template/src/components/endpoint/ResponseFields.astro +224 -0
- package/template/src/components/endpoint/ResponseSnippets.astro +247 -0
- package/template/src/components/sidebar/SidebarEndpointLink.astro +51 -0
- package/template/src/components/sidebar/SidebarOpenApi.astro +207 -0
- package/template/src/components/ui/Field.astro +69 -0
- package/template/src/components/ui/Tag.astro +5 -0
- package/template/src/components/ui/demo/CodeDemo.astro +15 -0
- package/template/src/components/ui/demo/Demo.astro +3 -0
- package/template/src/components/ui/demo/UiDisplay.astro +13 -0
- package/template/src/components/user/Accordian.astro +69 -0
- package/template/src/components/user/AccordianGroup.astro +13 -0
- package/template/src/components/user/Callout.astro +101 -0
- package/template/src/components/user/Step.astro +51 -0
- package/template/src/components/user/Steps.astro +9 -0
- package/template/src/components/user/Tab.astro +25 -0
- package/template/src/components/user/Tabs.astro +122 -0
- package/template/src/content.config.ts +11 -0
- package/template/src/entrypoint.ts +9 -0
- package/template/src/layouts/Layout.astro +92 -0
- package/template/src/lib/component-error.ts +163 -0
- package/template/src/lib/frontmatter-schema.ts +9 -0
- package/template/src/lib/oas.ts +24 -0
- package/template/src/lib/pagefind.ts +88 -0
- package/template/src/lib/routes.ts +316 -0
- package/template/src/lib/utils.ts +59 -0
- package/template/src/lib/validation.ts +1097 -0
- package/template/src/pages/[...slug].astro +77 -0
- package/template/src/styles/global.css +209 -0
- package/template/tsconfig.json +5 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { NavOpenApi } from "../../lib/validation";
|
|
3
|
+
import { loadOpenApiSpec } from "../../lib/validation";
|
|
4
|
+
import SidebarEndpointLink from "./SidebarEndpointLink.astro";
|
|
5
|
+
import fs from "node:fs";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import yaml from "yaml";
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
openapi: string | NavOpenApi;
|
|
11
|
+
parentSlug?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const { openapi: openApiPathOrConfig, parentSlug = "" } = Astro.props;
|
|
15
|
+
|
|
16
|
+
// Extract file path and filter options
|
|
17
|
+
let openApiPath: string;
|
|
18
|
+
let include: string[] | undefined;
|
|
19
|
+
let exclude: string[] | undefined;
|
|
20
|
+
|
|
21
|
+
if (typeof openApiPathOrConfig === "string") {
|
|
22
|
+
openApiPath = openApiPathOrConfig;
|
|
23
|
+
} else {
|
|
24
|
+
openApiPath = openApiPathOrConfig.source;
|
|
25
|
+
include = openApiPathOrConfig.include;
|
|
26
|
+
exclude = openApiPathOrConfig.exclude;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Load the OpenAPI spec
|
|
30
|
+
const openApiDoc = await loadOpenApiSpec(openApiPath);
|
|
31
|
+
|
|
32
|
+
// Helper function to parse endpoint string
|
|
33
|
+
function parseEndpointString(
|
|
34
|
+
endpointStr: string
|
|
35
|
+
): { method: string; path: string } | null {
|
|
36
|
+
const trimmed = endpointStr.trim();
|
|
37
|
+
const parts = trimmed.split(/\s+/);
|
|
38
|
+
|
|
39
|
+
if (parts.length !== 2) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const method = parts[0].toUpperCase();
|
|
44
|
+
let path = parts[1];
|
|
45
|
+
|
|
46
|
+
if (!path.startsWith("/")) {
|
|
47
|
+
path = "/" + path;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const normalizedPath = path.toLowerCase();
|
|
51
|
+
|
|
52
|
+
return { method, path: normalizedPath };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Helper function to check if an endpoint should be included
|
|
56
|
+
function shouldIncludeEndpoint(
|
|
57
|
+
method: string,
|
|
58
|
+
pathStr: string,
|
|
59
|
+
include?: string[],
|
|
60
|
+
exclude?: string[]
|
|
61
|
+
): boolean {
|
|
62
|
+
const normalizedMethod = method.toUpperCase();
|
|
63
|
+
const normalizedPath = pathStr.toLowerCase();
|
|
64
|
+
const endpointKey = `${normalizedMethod} ${normalizedPath}`;
|
|
65
|
+
|
|
66
|
+
if (include) {
|
|
67
|
+
return include.some((entry) => {
|
|
68
|
+
const parsed = parseEndpointString(entry);
|
|
69
|
+
if (!parsed) return false;
|
|
70
|
+
return `${parsed.method} ${parsed.path}` === endpointKey;
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (exclude) {
|
|
75
|
+
return !exclude.some((entry) => {
|
|
76
|
+
const parsed = parseEndpointString(entry);
|
|
77
|
+
if (!parsed) return false;
|
|
78
|
+
return `${parsed.method} ${parsed.path}` === endpointKey;
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Extract endpoints from OpenAPI spec
|
|
86
|
+
const paths = openApiDoc.paths || {};
|
|
87
|
+
const httpMethods = [
|
|
88
|
+
"get",
|
|
89
|
+
"post",
|
|
90
|
+
"put",
|
|
91
|
+
"delete",
|
|
92
|
+
"patch",
|
|
93
|
+
"head",
|
|
94
|
+
"options",
|
|
95
|
+
"trace",
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
interface Endpoint {
|
|
99
|
+
method: string;
|
|
100
|
+
path: string;
|
|
101
|
+
summary?: string;
|
|
102
|
+
tags?: string[];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const endpoints: Endpoint[] = [];
|
|
106
|
+
|
|
107
|
+
for (const [pathStr, pathItem] of Object.entries(paths)) {
|
|
108
|
+
if (!pathItem || typeof pathItem !== "object") continue;
|
|
109
|
+
|
|
110
|
+
for (const method of httpMethods) {
|
|
111
|
+
const operation = (pathItem as any)[method];
|
|
112
|
+
if (!operation) continue;
|
|
113
|
+
|
|
114
|
+
if (!shouldIncludeEndpoint(method, pathStr, include, exclude)) {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
endpoints.push({
|
|
119
|
+
method,
|
|
120
|
+
path: pathStr,
|
|
121
|
+
summary: operation.summary,
|
|
122
|
+
tags: operation.tags,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Group endpoints by first tag
|
|
128
|
+
interface TagGroup {
|
|
129
|
+
tag: string;
|
|
130
|
+
endpoints: Endpoint[];
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const tagGroups = new Map<string, Endpoint[]>();
|
|
134
|
+
const untaggedEndpoints: Endpoint[] = [];
|
|
135
|
+
|
|
136
|
+
for (const endpoint of endpoints) {
|
|
137
|
+
if (endpoint.tags && endpoint.tags.length > 0) {
|
|
138
|
+
const firstTag = endpoint.tags[0];
|
|
139
|
+
if (!tagGroups.has(firstTag)) {
|
|
140
|
+
tagGroups.set(firstTag, []);
|
|
141
|
+
}
|
|
142
|
+
tagGroups.get(firstTag)!.push(endpoint);
|
|
143
|
+
} else {
|
|
144
|
+
untaggedEndpoints.push(endpoint);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Only add "Other" group if there are other tagged groups
|
|
149
|
+
// If no tags exist at all, we'll render untagged endpoints without a group header
|
|
150
|
+
const hasTaggedGroups = tagGroups.size > 0;
|
|
151
|
+
if (untaggedEndpoints.length > 0 && hasTaggedGroups) {
|
|
152
|
+
tagGroups.set("Other", untaggedEndpoints);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Convert to array and sort by tag name
|
|
156
|
+
const sortedTagGroups: TagGroup[] = Array.from(tagGroups.entries())
|
|
157
|
+
.map(([tag, endpoints]) => ({ tag, endpoints }))
|
|
158
|
+
.sort((a, b) => {
|
|
159
|
+
// Put "Other" at the end
|
|
160
|
+
if (a.tag === "Other") return 1;
|
|
161
|
+
if (b.tag === "Other") return -1;
|
|
162
|
+
return a.tag.localeCompare(b.tag);
|
|
163
|
+
});
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
<ul class="px-2 pt-4 pb-3">
|
|
167
|
+
{
|
|
168
|
+
// If there are tagged groups, render them (including "Other" if it exists)
|
|
169
|
+
hasTaggedGroups
|
|
170
|
+
? sortedTagGroups.map((group, index) => (
|
|
171
|
+
<li
|
|
172
|
+
class={
|
|
173
|
+
index > 0
|
|
174
|
+
? "mt-5 pt-[25px] relative before:absolute before:top-0 before:inset-x-0 before:h-px before:bg-linear-[90deg,transparent,var(--color-neutral-200)_20%,var(--color-neutral-200)_80%,transparent] dark:before:bg-linear-[90deg,transparent,var(--color-neutral-700)_20%,var(--color-neutral-700)_80%,transparent]"
|
|
175
|
+
: ""
|
|
176
|
+
}
|
|
177
|
+
>
|
|
178
|
+
<div class="text-sm font-semibold mb-2 flex items-center gap-2 px-2">
|
|
179
|
+
{group.tag}
|
|
180
|
+
</div>
|
|
181
|
+
<ul>
|
|
182
|
+
{group.endpoints.map((endpoint) => (
|
|
183
|
+
<li>
|
|
184
|
+
<SidebarEndpointLink
|
|
185
|
+
method={endpoint.method}
|
|
186
|
+
path={endpoint.path}
|
|
187
|
+
summary={endpoint.summary}
|
|
188
|
+
parentSlug={parentSlug}
|
|
189
|
+
/>
|
|
190
|
+
</li>
|
|
191
|
+
))}
|
|
192
|
+
</ul>
|
|
193
|
+
</li>
|
|
194
|
+
))
|
|
195
|
+
: // If no tags at all, render untagged endpoints directly without group headers
|
|
196
|
+
untaggedEndpoints.map((endpoint, index) => (
|
|
197
|
+
<li>
|
|
198
|
+
<SidebarEndpointLink
|
|
199
|
+
method={endpoint.method}
|
|
200
|
+
path={endpoint.path}
|
|
201
|
+
summary={endpoint.summary}
|
|
202
|
+
parentSlug={parentSlug}
|
|
203
|
+
/>
|
|
204
|
+
</li>
|
|
205
|
+
))
|
|
206
|
+
}
|
|
207
|
+
</ul>
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { renderMarkdown } from "../../lib/utils";
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
name: string;
|
|
6
|
+
type: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
required?: boolean;
|
|
9
|
+
optional?: boolean;
|
|
10
|
+
enum?: (string | number)[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const {
|
|
14
|
+
name,
|
|
15
|
+
type,
|
|
16
|
+
description,
|
|
17
|
+
required,
|
|
18
|
+
optional,
|
|
19
|
+
enum: enumValues,
|
|
20
|
+
} = Astro.props;
|
|
21
|
+
|
|
22
|
+
const formattedDescription = description
|
|
23
|
+
? await renderMarkdown(description)
|
|
24
|
+
: null;
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
<div>
|
|
28
|
+
<div class="flex items-center gap-2">
|
|
29
|
+
<h5 class="font-normal font-mono text-[15px] text-neutral-400">
|
|
30
|
+
{name.split(".").map((part, i, a) => (
|
|
31
|
+
<>{i > 0 && <span class="inline">.</span>}<span class:list={["inline", a.length - 1 === i && "text-neutral-900 font-semibold"]}>{part}</span></>
|
|
32
|
+
))}
|
|
33
|
+
<!-- {name.split(".").map((part, i, a) => (
|
|
34
|
+
<>{i > 0 && <span class="inline">.</span>}<span class:list={["inline", a.length - 1 === i && "text-neutral-900 font-semibold"]}>{part}</span></>
|
|
35
|
+
))} -->
|
|
36
|
+
</h5>
|
|
37
|
+
<code
|
|
38
|
+
class="text-[11px] font-medium text-neutral-600 border border-neutral-200 bg-neutral-50 px-1 rounded-md"
|
|
39
|
+
>{type}</code
|
|
40
|
+
>
|
|
41
|
+
{
|
|
42
|
+
required && (
|
|
43
|
+
<div class="text-red-700/70 bg-red-50 border border-red-700/10 rounded-full px-2 text-[11px] font-mono leading-none py-0.5 pb-0.5 font-[450] h-fit">
|
|
44
|
+
required
|
|
45
|
+
</div>
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
{
|
|
49
|
+
optional && (
|
|
50
|
+
<div class="text-blue-700/70 bg-blue-50 border border-blue-700/10 rounded-full px-2 text-[13px] leading-none py-px pb-0.5 font-[450] h-fit">
|
|
51
|
+
optional
|
|
52
|
+
</div>
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
</div>
|
|
56
|
+
{
|
|
57
|
+
enumValues && (
|
|
58
|
+
<div class="text-sm mt-1.5">
|
|
59
|
+
<span class="font-medium.">Options:</span>
|
|
60
|
+
{enumValues.map((v, i, a) => (
|
|
61
|
+
<>
|
|
62
|
+
<code class="text-[11px] font-medium text-neutral-600 border border-neutral-200 bg-neutral-50 px-1 py-px rounded-md">{v}</code>{a.length - 2 === i ? " or " : a.length - 1 !== i && ", "}
|
|
63
|
+
</>
|
|
64
|
+
))}
|
|
65
|
+
</div>
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
<div class="mt-1.5 prose-rules prose-sm!" set:html={formattedDescription} />
|
|
69
|
+
</div>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<span class="demo-component">
|
|
2
|
+
<slot />
|
|
3
|
+
</span>
|
|
4
|
+
|
|
5
|
+
<style>
|
|
6
|
+
@reference "../../../styles/global.css";
|
|
7
|
+
|
|
8
|
+
:global(.demo-component .frame) {
|
|
9
|
+
@apply shadow-none! my-0!;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
:global(.demo-component .frame pre) {
|
|
13
|
+
@apply border-none! rounded-none rounded-b-xl bg-neutral-50! inset-shadow-xs;
|
|
14
|
+
}
|
|
15
|
+
</style>
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { Icon } from "astro-icon/components";
|
|
3
|
+
import { validateProps } from "../../lib/component-error";
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
title: string;
|
|
7
|
+
icon?: string;
|
|
8
|
+
defaultOpen?: boolean;
|
|
9
|
+
titleSize?: "xs" | "sm" | "md" | "lg" | "xl" | "2xl";
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const { title, icon, defaultOpen = false, titleSize } = Astro.props;
|
|
13
|
+
|
|
14
|
+
validateProps(
|
|
15
|
+
"Accordion",
|
|
16
|
+
{ title, icon, defaultOpen, titleSize },
|
|
17
|
+
{
|
|
18
|
+
title: { required: true, type: "string" },
|
|
19
|
+
icon: { type: "string" },
|
|
20
|
+
defaultOpen: { type: "boolean" },
|
|
21
|
+
titleSize: { enum: ["xs", "sm", "md", "lg", "xl", "2xl"] },
|
|
22
|
+
},
|
|
23
|
+
Astro.url.pathname
|
|
24
|
+
);
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
<div
|
|
28
|
+
x-data=`{
|
|
29
|
+
id: null,
|
|
30
|
+
localExpanded: ${defaultOpen},
|
|
31
|
+
get expanded() {
|
|
32
|
+
return (typeof active !== 'undefined') ? (active === this.id) : this.localExpanded;
|
|
33
|
+
},
|
|
34
|
+
set expanded(value) {
|
|
35
|
+
if (typeof active !== 'undefined') {
|
|
36
|
+
active = value ? this.id : null;
|
|
37
|
+
} else {
|
|
38
|
+
this.localExpanded = value;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}`
|
|
42
|
+
x-init="id = (typeof register === 'function') ? register() : Math.random()"
|
|
43
|
+
role="region"
|
|
44
|
+
class="block border-b border-neutral-800/10 pb-4 pt-4 first:pt-0 last:border-b-0 last:pb-0"
|
|
45
|
+
>
|
|
46
|
+
<h4 class="not-prose">
|
|
47
|
+
<button
|
|
48
|
+
type="button"
|
|
49
|
+
x-on:click="expanded = !expanded"
|
|
50
|
+
:aria-expanded="expanded"
|
|
51
|
+
class="group flex w-full items-center justify-between gap-2.5 text-left font-medium text-neutral-800"
|
|
52
|
+
>
|
|
53
|
+
{icon && <Icon name={`lucide:${icon}`} />}
|
|
54
|
+
<span class:list={["flex-1", titleSize && `text-${titleSize}`]}
|
|
55
|
+
>{title}</span
|
|
56
|
+
>
|
|
57
|
+
<Icon
|
|
58
|
+
name="lucide:chevron-down"
|
|
59
|
+
class="size-5 shrink-0 text-neutral-400 group-hover:text-neutral-600 transition duration-200"
|
|
60
|
+
x-bind:class="expanded && 'rotate-180'"
|
|
61
|
+
/>
|
|
62
|
+
</button>
|
|
63
|
+
</h4>
|
|
64
|
+
<div x-show="expanded" x-collapse>
|
|
65
|
+
<div class="pt-2 *:first:mt-0 *:last:mb-0">
|
|
66
|
+
<slot />
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { Icon } from "astro-icon/components";
|
|
3
|
+
import warningIcon from "../../assets/icons/warning.svg?url";
|
|
4
|
+
import infoIcon from "../../assets/icons/info.svg?url";
|
|
5
|
+
import lightbulbIcon from "../../assets/icons/lightbulb.svg?url";
|
|
6
|
+
import dangerIcon from "../../assets/icons/danger.svg?url";
|
|
7
|
+
import checkIcon from "../../assets/icons/check.svg?url";
|
|
8
|
+
import { validateProps } from "../../lib/component-error";
|
|
9
|
+
|
|
10
|
+
type CalloutType = "warning" | "info" | "tip" | "danger" | "check";
|
|
11
|
+
|
|
12
|
+
interface Props {
|
|
13
|
+
type?: CalloutType;
|
|
14
|
+
title?: string;
|
|
15
|
+
icon?: string;
|
|
16
|
+
color?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const typeDefaults: Record<
|
|
20
|
+
CalloutType,
|
|
21
|
+
{ icon: string; color: string; title: string }
|
|
22
|
+
> = {
|
|
23
|
+
warning: {
|
|
24
|
+
icon: warningIcon,
|
|
25
|
+
color: "bg-amber-500",
|
|
26
|
+
title: "Warning",
|
|
27
|
+
},
|
|
28
|
+
info: {
|
|
29
|
+
icon: infoIcon,
|
|
30
|
+
color: "bg-sky-600/80",
|
|
31
|
+
title: "Info",
|
|
32
|
+
},
|
|
33
|
+
tip: {
|
|
34
|
+
icon: lightbulbIcon,
|
|
35
|
+
color: "bg-yellow-500",
|
|
36
|
+
title: "Tip",
|
|
37
|
+
},
|
|
38
|
+
danger: {
|
|
39
|
+
icon: dangerIcon,
|
|
40
|
+
color: "bg-red-600",
|
|
41
|
+
title: "Danger",
|
|
42
|
+
},
|
|
43
|
+
check: {
|
|
44
|
+
icon: checkIcon,
|
|
45
|
+
color: "bg-green-600",
|
|
46
|
+
title: "Success",
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const { type = "info", title, icon, color } = Astro.props;
|
|
51
|
+
|
|
52
|
+
validateProps(
|
|
53
|
+
"Callout",
|
|
54
|
+
{ type, title, icon, color },
|
|
55
|
+
{
|
|
56
|
+
type: { enum: ["warning", "info", "tip", "danger", "check"] },
|
|
57
|
+
title: { type: "string" },
|
|
58
|
+
icon: { type: "string" },
|
|
59
|
+
color: { type: "string" },
|
|
60
|
+
},
|
|
61
|
+
Astro.url.pathname
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const defaults = typeDefaults[type];
|
|
65
|
+
const resolvedTitle = title ?? defaults.title;
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
<aside
|
|
69
|
+
class:list={[
|
|
70
|
+
"my-5 space-y-1 rounded-lg border border-neutral-200/80 shadow-xs px-4 py-3.5 pl-6 relative",
|
|
71
|
+
]}
|
|
72
|
+
role="note"
|
|
73
|
+
>
|
|
74
|
+
<div
|
|
75
|
+
class:list={[
|
|
76
|
+
"absolute left-2 inset-y-2 w-[2.5px] rounded-full",
|
|
77
|
+
defaults.color,
|
|
78
|
+
]}
|
|
79
|
+
style={color && { backgroundColor: color }}
|
|
80
|
+
>
|
|
81
|
+
</div>
|
|
82
|
+
<div class="flex items-center gap-2 not-prose">
|
|
83
|
+
{
|
|
84
|
+
icon ? (
|
|
85
|
+
<Icon
|
|
86
|
+
name={`lucide:${icon}`}
|
|
87
|
+
class=""
|
|
88
|
+
style={color && { color: color }}
|
|
89
|
+
/>
|
|
90
|
+
) : (
|
|
91
|
+
<img src={defaults.icon} alt="" width="16" height="16" class="" />
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
<h6 class:list={["font-semibold text-sm"]}>
|
|
95
|
+
{resolvedTitle}
|
|
96
|
+
</h6>
|
|
97
|
+
</div>
|
|
98
|
+
<div class="[&>p]:m-0 [&>p:not(:last-child)]:mb-2">
|
|
99
|
+
<slot />
|
|
100
|
+
</div>
|
|
101
|
+
</aside>
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { Icon } from "astro-icon/components";
|
|
3
|
+
import { validateProps } from "../../lib/component-error";
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
title: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const { title } = Astro.props;
|
|
10
|
+
|
|
11
|
+
validateProps(
|
|
12
|
+
"Step",
|
|
13
|
+
{ title },
|
|
14
|
+
{
|
|
15
|
+
title: { required: true, type: "string" },
|
|
16
|
+
},
|
|
17
|
+
Astro.url.pathname
|
|
18
|
+
);
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
<div
|
|
22
|
+
class:list={[
|
|
23
|
+
"relative pl-10 step-item pb-4 last:pb-0 space-y-4",
|
|
24
|
+
"before:absolute before:left-[10.5px] before:top-8 before:bottom-0 before:w-px before:bg-linear-[transparent,var(--color-neutral-200)_10%,var(--color-neutral-200)_90%,transparent]",
|
|
25
|
+
]}
|
|
26
|
+
data-step-panel
|
|
27
|
+
>
|
|
28
|
+
<div
|
|
29
|
+
class:list={[
|
|
30
|
+
"flex items-center gap-1.5 not-prose",
|
|
31
|
+
"step-number before:bg-neutral-900 before:size-[22px] before:rounded-full before:text-white before:flex before:items-center before:justify-center before:text-xs before:font-bold before:absolute before:left-px before:top-[3px] before:shadow-[inset_0_1px_0_rgb(255,255,255,0.2),0_0_0_1px_var(--color-neutral-800),var(--shadow-md)]",
|
|
32
|
+
]}
|
|
33
|
+
>
|
|
34
|
+
<h3 class="text-lg font-semibold text-neutral-900">
|
|
35
|
+
{title}
|
|
36
|
+
</h3>
|
|
37
|
+
</div>
|
|
38
|
+
<div class="[&>p]:m-0 [&>p:not(:last-child)]:mb-2">
|
|
39
|
+
<slot />
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
<style>
|
|
44
|
+
.step-item {
|
|
45
|
+
counter-increment: step-counter;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.step-number::before {
|
|
49
|
+
content: counter(step-counter);
|
|
50
|
+
}
|
|
51
|
+
</style>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { validateProps, validateRequired } from "../../lib/component-error";
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
label: string;
|
|
6
|
+
icon?: string;
|
|
7
|
+
}
|
|
8
|
+
const { label, icon } = Astro.props;
|
|
9
|
+
|
|
10
|
+
validateProps(
|
|
11
|
+
"Tab",
|
|
12
|
+
{ label, icon },
|
|
13
|
+
{
|
|
14
|
+
label: { required: true, type: "string" },
|
|
15
|
+
icon: { type: "string" },
|
|
16
|
+
},
|
|
17
|
+
Astro.url.pathname
|
|
18
|
+
);
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
<section data-label={label} data-icon={icon || ""}>
|
|
22
|
+
<div class="*:m-0! *:mb-2">
|
|
23
|
+
<slot />
|
|
24
|
+
</div>
|
|
25
|
+
</section>
|