@sandboxsonny/ace-kit 0.4.0 → 0.6.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/CHANGELOG.md +17 -0
- package/README.md +1 -1
- package/dist/astro-integration.d.ts +3 -3
- package/dist/astro-integration.js +7 -4
- package/dist/chunk-7M26C36M.js +35 -0
- package/dist/{chunk-O3NAJNUN.js → chunk-G5FGBOFT.js} +7 -7
- package/dist/chunk-N5DIMG3X.js +16 -0
- package/dist/cli.js +14 -1169
- package/dist/environment-T5K5ZJ2M.js +222 -0
- package/dist/main-YRQQWLHX.js +1454 -0
- package/dist/react-router-server.js +3 -2
- package/dist/vite.js +2 -1
- package/docs/astro.md +12 -6
- package/docs/command-line.md +34 -5
- package/docs/dev-sites.md +2 -2
- package/docs/getting-started.md +8 -0
- package/docs/react-router.md +7 -5
- package/docs/sections.md +1 -1
- package/docs/typed-content.md +3 -3
- package/package.json +5 -2
- package/templates/astro/.dockerignore +7 -0
- package/templates/astro/.github/dependabot.yml +19 -0
- package/templates/astro/.github/workflows/ci.yml +23 -0
- package/templates/astro/.prettierignore +6 -0
- package/templates/astro/.prettierrc.json +20 -0
- package/templates/astro/.vscode/extensions.json +4 -0
- package/templates/astro/.vscode/launch.json +11 -0
- package/templates/astro/AGENTS.md +22 -0
- package/templates/astro/Dockerfile +17 -0
- package/templates/astro/_gitignore +26 -0
- package/templates/astro/astro.config.mjs +18 -0
- package/templates/astro/eslint.config.js +56 -0
- package/templates/astro/package.json +41 -0
- package/templates/astro/public/favicon.ico +0 -0
- package/templates/astro/public/favicon.svg +9 -0
- package/templates/astro/src/ace-kit.generated.d.ts +24 -0
- package/templates/astro/src/components/PageContent.astro +113 -0
- package/templates/astro/src/components/SiteFooter.astro +19 -0
- package/templates/astro/src/components/SiteHeader.astro +121 -0
- package/templates/astro/src/components/sections/Faq.astro +54 -0
- package/templates/astro/src/components/sections/Faq.schema.ts +17 -0
- package/templates/astro/src/components/sections/Gallery.astro +74 -0
- package/templates/astro/src/components/sections/Gallery.schema.ts +20 -0
- package/templates/astro/src/components/sections/Hero.astro +58 -0
- package/templates/astro/src/components/sections/Hero.schema.ts +14 -0
- package/templates/astro/src/layouts/BaseLayout.astro +36 -0
- package/templates/astro/src/pages/[...slug].astro +31 -0
- package/templates/astro/src/styles/global.css +56 -0
- package/templates/astro/src/types/page.d.ts +4 -0
- package/templates/astro/tsconfig.json +11 -0
- package/templates/astro/vitest.config.ts +4 -0
- package/templates/astro.section-types.json +109 -0
- package/templates/react-router/.prettierignore +5 -0
- package/templates/react-router/.prettierrc.json +11 -0
- package/templates/react-router/_gitignore +7 -0
- package/templates/react-router/app/ace-kit.generated.d.ts +18 -0
- package/templates/react-router/app/app.css +172 -0
- package/templates/react-router/app/components/page-content.tsx +32 -0
- package/templates/react-router/app/components/site-footer.tsx +9 -0
- package/templates/react-router/app/components/site-header.tsx +49 -0
- package/templates/react-router/app/entry.server.tsx +1 -0
- package/templates/react-router/app/root.tsx +75 -0
- package/templates/react-router/app/routes/page.test.tsx +64 -0
- package/templates/react-router/app/routes/page.tsx +22 -0
- package/templates/react-router/app/routes.ts +4 -0
- package/templates/react-router/app/types/page.d.ts +4 -0
- package/templates/react-router/eslint.config.js +76 -0
- package/templates/react-router/package.json +51 -0
- package/templates/react-router/public/favicon.svg +9 -0
- package/templates/react-router/react-router.config.ts +6 -0
- package/templates/react-router/tsconfig.json +22 -0
- package/templates/react-router/vite.config.ts +10 -0
- package/templates/react-router/vitest.config.ts +15 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
---
|
|
2
|
+
import AceImage from "@sandboxsonny/ace-kit/astro/AceImage.astro";
|
|
3
|
+
import AceLink from "@sandboxsonny/ace-kit/astro/AceLink.astro";
|
|
4
|
+
import AceRichText from "@sandboxsonny/ace-kit/astro/AceRichText.astro";
|
|
5
|
+
import AceTextArea from "@sandboxsonny/ace-kit/astro/AceTextArea.astro";
|
|
6
|
+
import { formatAceDate } from "@sandboxsonny/ace-kit";
|
|
7
|
+
import type { AceEntry } from "@sandboxsonny/ace-kit";
|
|
8
|
+
|
|
9
|
+
import AceSections from "@sandboxsonny/ace-kit/astro/AceSections.astro";
|
|
10
|
+
|
|
11
|
+
import Faq from "~/components/sections/Faq.astro";
|
|
12
|
+
import Gallery from "~/components/sections/Gallery.astro";
|
|
13
|
+
import Hero from "~/components/sections/Hero.astro";
|
|
14
|
+
|
|
15
|
+
import type { PageValues } from "~/types/page";
|
|
16
|
+
|
|
17
|
+
type Props = {
|
|
18
|
+
entry: AceEntry<PageValues>;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const { entry } = Astro.props;
|
|
22
|
+
const { headline, body, hero, published, button, content } = entry.values;
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
<section class="section">
|
|
26
|
+
<div class="container prose">
|
|
27
|
+
<h1>{headline || entry.title}</h1>
|
|
28
|
+
{entry.description && <p class="intro">{entry.description}</p>}
|
|
29
|
+
{
|
|
30
|
+
// A date field is a day with no time of day, so it is the same day wherever the server runs.
|
|
31
|
+
published && (
|
|
32
|
+
<p class="published">
|
|
33
|
+
Published <time datetime={published}>{formatAceDate(published, { locale: "en-GB" }) ?? published}</time>
|
|
34
|
+
</p>
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
{hero && (
|
|
38
|
+
<figure>
|
|
39
|
+
<AceImage image={hero} sizes="(min-width: 64rem) 64rem, 100vw" lazy={false} />
|
|
40
|
+
</figure>
|
|
41
|
+
)}
|
|
42
|
+
{
|
|
43
|
+
// A site made before its Body field was rich text still has plain text, with blank lines between paragraphs.
|
|
44
|
+
typeof body === "string" ? (
|
|
45
|
+
<AceTextArea value={body} />
|
|
46
|
+
) : body ? (
|
|
47
|
+
<div class="rich-text">
|
|
48
|
+
<AceRichText document={body} />
|
|
49
|
+
</div>
|
|
50
|
+
) : null
|
|
51
|
+
}
|
|
52
|
+
{
|
|
53
|
+
// A link field: the editor's words and where it goes, already worked out by the CMS.
|
|
54
|
+
button && (
|
|
55
|
+
<p class="actions">
|
|
56
|
+
<AceLink link={button} class="button" />
|
|
57
|
+
</p>
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
</div>
|
|
61
|
+
</section>
|
|
62
|
+
<AceSections sections={content} components={{ hero: Hero, faq: Faq, gallery: Gallery }} />
|
|
63
|
+
<style>
|
|
64
|
+
.prose {
|
|
65
|
+
max-width: 42rem;
|
|
66
|
+
}
|
|
67
|
+
.intro {
|
|
68
|
+
color: var(--colour-muted);
|
|
69
|
+
font-size: 1.125rem;
|
|
70
|
+
}
|
|
71
|
+
.published {
|
|
72
|
+
color: var(--colour-muted);
|
|
73
|
+
font-size: 0.9rem;
|
|
74
|
+
}
|
|
75
|
+
.actions {
|
|
76
|
+
margin-top: 2rem;
|
|
77
|
+
}
|
|
78
|
+
figure {
|
|
79
|
+
margin: 2rem 0;
|
|
80
|
+
}
|
|
81
|
+
figure :global(img) {
|
|
82
|
+
border-radius: 0.5rem;
|
|
83
|
+
}
|
|
84
|
+
.rich-text :global(* + *) {
|
|
85
|
+
margin-top: 1rem;
|
|
86
|
+
}
|
|
87
|
+
.rich-text :global(h2) {
|
|
88
|
+
font-size: 1.75rem;
|
|
89
|
+
}
|
|
90
|
+
.rich-text :global(h3) {
|
|
91
|
+
font-size: 1.4rem;
|
|
92
|
+
}
|
|
93
|
+
.rich-text :global(h4) {
|
|
94
|
+
font-size: 1.15rem;
|
|
95
|
+
}
|
|
96
|
+
.rich-text :global(ul) {
|
|
97
|
+
list-style: disc;
|
|
98
|
+
padding-left: 1.5rem;
|
|
99
|
+
}
|
|
100
|
+
.rich-text :global(ol) {
|
|
101
|
+
list-style: decimal;
|
|
102
|
+
padding-left: 1.5rem;
|
|
103
|
+
}
|
|
104
|
+
.rich-text :global(blockquote) {
|
|
105
|
+
border-left: 4px solid var(--colour-muted);
|
|
106
|
+
padding-left: 1rem;
|
|
107
|
+
font-style: italic;
|
|
108
|
+
}
|
|
109
|
+
.rich-text :global(img) {
|
|
110
|
+
max-width: 100%;
|
|
111
|
+
height: auto;
|
|
112
|
+
}
|
|
113
|
+
</style>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
const year = new Date().getFullYear();
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
<footer class="site-footer">
|
|
6
|
+
<div class="container">
|
|
7
|
+
<p>© {year} Example Site. A test site for CMS Ace.</p>
|
|
8
|
+
</div>
|
|
9
|
+
</footer>
|
|
10
|
+
|
|
11
|
+
<style>
|
|
12
|
+
.site-footer {
|
|
13
|
+
margin-top: 3rem;
|
|
14
|
+
padding-block: 2rem;
|
|
15
|
+
background: var(--colour-surface);
|
|
16
|
+
color: var(--colour-muted);
|
|
17
|
+
font-size: 0.9rem;
|
|
18
|
+
}
|
|
19
|
+
</style>
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { isCurrentPage } from "@sandboxsonny/ace-kit";
|
|
3
|
+
import { getMenuLinks } from "@sandboxsonny/ace-kit/astro";
|
|
4
|
+
|
|
5
|
+
// The CMS menu with the handle "main-menu", or the built-in links if the site has none yet or the CMS can't be reached.
|
|
6
|
+
const links = await getMenuLinks("main-menu", {
|
|
7
|
+
fallback: [
|
|
8
|
+
{ label: "Home", to: "/" },
|
|
9
|
+
{ label: "About", to: "/pages/about" },
|
|
10
|
+
{ label: "Contact", to: "/pages/contact" },
|
|
11
|
+
],
|
|
12
|
+
});
|
|
13
|
+
const { pathname } = Astro.url;
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
<header class="site-header">
|
|
17
|
+
<div class="container site-header__inner">
|
|
18
|
+
<a href="/" class="site-header__name">
|
|
19
|
+
Example Site
|
|
20
|
+
</a>
|
|
21
|
+
<nav aria-label="Main">
|
|
22
|
+
<ul class="site-header__links">
|
|
23
|
+
{links.map((link) => (
|
|
24
|
+
<li>
|
|
25
|
+
<a href={link.to} aria-current={isCurrentPage(link, pathname) ? "page" : undefined}>
|
|
26
|
+
{link.label}
|
|
27
|
+
</a>
|
|
28
|
+
{link.children && link.children.length > 0 && (
|
|
29
|
+
<ul class="site-header__sublinks">
|
|
30
|
+
{link.children.map((child) => (
|
|
31
|
+
<li>
|
|
32
|
+
<a href={child.to} aria-current={isCurrentPage(child, pathname) ? "page" : undefined}>
|
|
33
|
+
{child.label}
|
|
34
|
+
</a>
|
|
35
|
+
{child.children && child.children.length > 0 && (
|
|
36
|
+
<ul class="site-header__sublinks site-header__sublinks--deep">
|
|
37
|
+
{child.children.map((grandchild) => (
|
|
38
|
+
<li>
|
|
39
|
+
<a href={grandchild.to}>{grandchild.label}</a>
|
|
40
|
+
</li>
|
|
41
|
+
))}
|
|
42
|
+
</ul>
|
|
43
|
+
)}
|
|
44
|
+
</li>
|
|
45
|
+
))}
|
|
46
|
+
</ul>
|
|
47
|
+
)}
|
|
48
|
+
</li>
|
|
49
|
+
))}
|
|
50
|
+
</ul>
|
|
51
|
+
</nav>
|
|
52
|
+
</div>
|
|
53
|
+
</header>
|
|
54
|
+
|
|
55
|
+
<style>
|
|
56
|
+
.site-header {
|
|
57
|
+
border-bottom: 1px solid var(--colour-surface);
|
|
58
|
+
}
|
|
59
|
+
.site-header__inner {
|
|
60
|
+
display: flex;
|
|
61
|
+
align-items: center;
|
|
62
|
+
justify-content: space-between;
|
|
63
|
+
padding-block: 1rem;
|
|
64
|
+
}
|
|
65
|
+
.site-header__name {
|
|
66
|
+
font-weight: 700;
|
|
67
|
+
font-size: 1.25rem;
|
|
68
|
+
color: var(--colour-text);
|
|
69
|
+
text-decoration: none;
|
|
70
|
+
}
|
|
71
|
+
.site-header__links {
|
|
72
|
+
display: flex;
|
|
73
|
+
gap: 1.5rem;
|
|
74
|
+
margin: 0;
|
|
75
|
+
padding: 0;
|
|
76
|
+
list-style: none;
|
|
77
|
+
}
|
|
78
|
+
.site-header__links a {
|
|
79
|
+
color: var(--colour-muted);
|
|
80
|
+
text-decoration: none;
|
|
81
|
+
}
|
|
82
|
+
.site-header__links > li {
|
|
83
|
+
position: relative;
|
|
84
|
+
}
|
|
85
|
+
.site-header__sublinks {
|
|
86
|
+
display: none;
|
|
87
|
+
position: absolute;
|
|
88
|
+
z-index: 10;
|
|
89
|
+
top: 100%;
|
|
90
|
+
left: 0;
|
|
91
|
+
min-width: 12rem;
|
|
92
|
+
margin: 0;
|
|
93
|
+
padding: 0.5rem 0;
|
|
94
|
+
list-style: none;
|
|
95
|
+
background: var(--colour-background);
|
|
96
|
+
border: 1px solid var(--colour-surface);
|
|
97
|
+
border-radius: 0.5rem;
|
|
98
|
+
}
|
|
99
|
+
.site-header__sublinks li {
|
|
100
|
+
position: relative;
|
|
101
|
+
}
|
|
102
|
+
.site-header__sublinks a {
|
|
103
|
+
display: block;
|
|
104
|
+
padding: 0.4rem 1rem;
|
|
105
|
+
white-space: nowrap;
|
|
106
|
+
}
|
|
107
|
+
.site-header__sublinks--deep {
|
|
108
|
+
top: 0;
|
|
109
|
+
left: 100%;
|
|
110
|
+
}
|
|
111
|
+
.site-header__links > li:hover > .site-header__sublinks,
|
|
112
|
+
.site-header__links > li:focus-within > .site-header__sublinks,
|
|
113
|
+
.site-header__sublinks li:hover > .site-header__sublinks,
|
|
114
|
+
.site-header__sublinks li:focus-within > .site-header__sublinks {
|
|
115
|
+
display: block;
|
|
116
|
+
}
|
|
117
|
+
.site-header__links a[aria-current="page"] {
|
|
118
|
+
color: var(--colour-text);
|
|
119
|
+
font-weight: 600;
|
|
120
|
+
}
|
|
121
|
+
</style>
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
import AceRichText from "@sandboxsonny/ace-kit/astro/AceRichText.astro";
|
|
3
|
+
|
|
4
|
+
import type { SectionOf } from "@sandboxsonny/ace-kit";
|
|
5
|
+
import type faq from "./Faq.schema";
|
|
6
|
+
|
|
7
|
+
type Props = {
|
|
8
|
+
section: SectionOf<typeof faq>;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const { section } = Astro.props;
|
|
12
|
+
const { heading } = section.values;
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
<section class="section" data-section={section.type}>
|
|
16
|
+
<div class="container faq">
|
|
17
|
+
{heading && <h2>{heading}</h2>}
|
|
18
|
+
{section.blocks.map((block) => (
|
|
19
|
+
<details>
|
|
20
|
+
<summary>{block.values.question}</summary>
|
|
21
|
+
{block.values.answer && (
|
|
22
|
+
<div class="answer">
|
|
23
|
+
<AceRichText document={block.values.answer} />
|
|
24
|
+
</div>
|
|
25
|
+
)}
|
|
26
|
+
</details>
|
|
27
|
+
))}
|
|
28
|
+
</div>
|
|
29
|
+
</section>
|
|
30
|
+
|
|
31
|
+
<style>
|
|
32
|
+
.faq {
|
|
33
|
+
max-width: 42rem;
|
|
34
|
+
}
|
|
35
|
+
h2 {
|
|
36
|
+
margin: 0 0 1rem;
|
|
37
|
+
font-size: 1.75rem;
|
|
38
|
+
}
|
|
39
|
+
details {
|
|
40
|
+
border-bottom: 1px solid var(--colour-surface);
|
|
41
|
+
padding-block: 0.75rem;
|
|
42
|
+
}
|
|
43
|
+
summary {
|
|
44
|
+
cursor: pointer;
|
|
45
|
+
font-weight: 600;
|
|
46
|
+
}
|
|
47
|
+
.answer {
|
|
48
|
+
margin-top: 0.5rem;
|
|
49
|
+
color: var(--colour-muted);
|
|
50
|
+
}
|
|
51
|
+
.answer :global(* + *) {
|
|
52
|
+
margin-top: 0.75rem;
|
|
53
|
+
}
|
|
54
|
+
</style>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { defineSection } from "@sandboxsonny/ace-kit";
|
|
2
|
+
|
|
3
|
+
// An FAQ is a heading and a list of questions. The questions are blocks: the editor adds as many as they like.
|
|
4
|
+
export default defineSection({
|
|
5
|
+
handle: "faq",
|
|
6
|
+
name: "FAQ",
|
|
7
|
+
fields: [{ handle: "heading", label: "Heading", type: "text" }],
|
|
8
|
+
blocks: {
|
|
9
|
+
question: {
|
|
10
|
+
name: "Question",
|
|
11
|
+
fields: [
|
|
12
|
+
{ handle: "question", label: "Question", type: "text", required: true },
|
|
13
|
+
{ handle: "answer", label: "Answer", type: "richtext" },
|
|
14
|
+
],
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
});
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { formatAceDate } from "@sandboxsonny/ace-kit";
|
|
3
|
+
import AceImage from "@sandboxsonny/ace-kit/astro/AceImage.astro";
|
|
4
|
+
|
|
5
|
+
import type { SectionOf } from "@sandboxsonny/ace-kit";
|
|
6
|
+
import type gallery from "./Gallery.schema";
|
|
7
|
+
|
|
8
|
+
type Props = {
|
|
9
|
+
section: SectionOf<typeof gallery>;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const { section } = Astro.props;
|
|
13
|
+
const { heading, published } = section.values;
|
|
14
|
+
const photos = section.blocks.filter((block) => block.values.image || block.values.caption);
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
<section class="section" data-section={section.type}>
|
|
18
|
+
<div class="container">
|
|
19
|
+
{heading && <h2>{heading}</h2>}
|
|
20
|
+
{published && (
|
|
21
|
+
<p class="published">
|
|
22
|
+
<time datetime={published}>{formatAceDate(published, { locale: "en-GB" }) ?? published}</time>
|
|
23
|
+
</p>
|
|
24
|
+
)}
|
|
25
|
+
{photos.length > 0 ? (
|
|
26
|
+
<ul class="photos">
|
|
27
|
+
{photos.map((photo) => (
|
|
28
|
+
<li>
|
|
29
|
+
<figure>
|
|
30
|
+
{photo.values.image && <AceImage image={photo.values.image} sizes="(min-width: 48rem) 20rem, 100vw" />}
|
|
31
|
+
{photo.values.caption && <figcaption>{photo.values.caption}</figcaption>}
|
|
32
|
+
</figure>
|
|
33
|
+
</li>
|
|
34
|
+
))}
|
|
35
|
+
</ul>
|
|
36
|
+
) : (
|
|
37
|
+
<p class="empty">No photos yet.</p>
|
|
38
|
+
)}
|
|
39
|
+
</div>
|
|
40
|
+
</section>
|
|
41
|
+
|
|
42
|
+
<style>
|
|
43
|
+
h2 {
|
|
44
|
+
margin: 0 0 0.25rem;
|
|
45
|
+
font-size: 1.75rem;
|
|
46
|
+
}
|
|
47
|
+
.published,
|
|
48
|
+
.empty {
|
|
49
|
+
margin: 0 0 1rem;
|
|
50
|
+
color: var(--colour-muted);
|
|
51
|
+
font-size: 0.9rem;
|
|
52
|
+
}
|
|
53
|
+
.photos {
|
|
54
|
+
display: grid;
|
|
55
|
+
grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
|
|
56
|
+
gap: 1rem;
|
|
57
|
+
margin: 0;
|
|
58
|
+
padding: 0;
|
|
59
|
+
list-style: none;
|
|
60
|
+
}
|
|
61
|
+
figure {
|
|
62
|
+
margin: 0;
|
|
63
|
+
}
|
|
64
|
+
figure :global(img) {
|
|
65
|
+
border-radius: 0.5rem;
|
|
66
|
+
width: 100%;
|
|
67
|
+
object-fit: cover;
|
|
68
|
+
}
|
|
69
|
+
figcaption {
|
|
70
|
+
margin-top: 0.4rem;
|
|
71
|
+
color: var(--colour-muted);
|
|
72
|
+
font-size: 0.9rem;
|
|
73
|
+
}
|
|
74
|
+
</style>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { defineSection } from "@sandboxsonny/ace-kit";
|
|
2
|
+
|
|
3
|
+
// A gallery is a heading, a date and a list of photos. The photos are blocks.
|
|
4
|
+
export default defineSection({
|
|
5
|
+
handle: "gallery",
|
|
6
|
+
name: "Gallery",
|
|
7
|
+
fields: [
|
|
8
|
+
{ handle: "heading", label: "Heading", type: "text" },
|
|
9
|
+
{ handle: "published", label: "Published", type: "date" },
|
|
10
|
+
],
|
|
11
|
+
blocks: {
|
|
12
|
+
photo: {
|
|
13
|
+
name: "Photo",
|
|
14
|
+
fields: [
|
|
15
|
+
{ handle: "image", label: "Image", type: "image" },
|
|
16
|
+
{ handle: "caption", label: "Caption", type: "text" },
|
|
17
|
+
],
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
import AceImage from "@sandboxsonny/ace-kit/astro/AceImage.astro";
|
|
3
|
+
import AceLink from "@sandboxsonny/ace-kit/astro/AceLink.astro";
|
|
4
|
+
|
|
5
|
+
import type { SectionOf } from "@sandboxsonny/ace-kit";
|
|
6
|
+
import type hero from "./Hero.schema";
|
|
7
|
+
|
|
8
|
+
type Props = {
|
|
9
|
+
section: SectionOf<typeof hero>;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const { section } = Astro.props;
|
|
13
|
+
const { heading, subheading, picture, button } = section.values;
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
<section class="hero section--surface" data-section={section.type}>
|
|
17
|
+
<div class="container hero__inner">
|
|
18
|
+
<div class="hero__text">
|
|
19
|
+
<h2>{heading}</h2>
|
|
20
|
+
{subheading && <p class="hero__subheading">{subheading}</p>}
|
|
21
|
+
{button && <AceLink link={button} class="button" />}
|
|
22
|
+
</div>
|
|
23
|
+
{picture && (
|
|
24
|
+
<div class="hero__picture">
|
|
25
|
+
<AceImage image={picture} sizes="(min-width: 64rem) 32rem, 100vw" lazy={false} />
|
|
26
|
+
</div>
|
|
27
|
+
)}
|
|
28
|
+
</div>
|
|
29
|
+
</section>
|
|
30
|
+
|
|
31
|
+
<style>
|
|
32
|
+
.hero {
|
|
33
|
+
padding-block: 3rem;
|
|
34
|
+
}
|
|
35
|
+
.hero__inner {
|
|
36
|
+
display: grid;
|
|
37
|
+
gap: 2rem;
|
|
38
|
+
align-items: center;
|
|
39
|
+
}
|
|
40
|
+
@media (min-width: 48rem) {
|
|
41
|
+
.hero__inner:has(.hero__picture) {
|
|
42
|
+
grid-template-columns: 1fr 1fr;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
h2 {
|
|
46
|
+
margin: 0 0 0.75rem;
|
|
47
|
+
font-size: 2.25rem;
|
|
48
|
+
line-height: 1.15;
|
|
49
|
+
}
|
|
50
|
+
.hero__subheading {
|
|
51
|
+
margin: 0 0 1.5rem;
|
|
52
|
+
color: var(--colour-muted);
|
|
53
|
+
font-size: 1.15rem;
|
|
54
|
+
}
|
|
55
|
+
.hero__picture :global(img) {
|
|
56
|
+
border-radius: 0.5rem;
|
|
57
|
+
}
|
|
58
|
+
</style>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { defineSection } from "@sandboxsonny/ace-kit";
|
|
2
|
+
|
|
3
|
+
// What a hero can hold. The CMS learns it from this file (the dev server syncs it when it changes), and Hero.astro's
|
|
4
|
+
// props are typed from it.
|
|
5
|
+
export default defineSection({
|
|
6
|
+
handle: "hero",
|
|
7
|
+
name: "Hero",
|
|
8
|
+
fields: [
|
|
9
|
+
{ handle: "heading", label: "Heading", type: "text", required: true },
|
|
10
|
+
{ handle: "subheading", label: "Subheading", type: "textarea" },
|
|
11
|
+
{ handle: "picture", label: "Picture", type: "image" },
|
|
12
|
+
{ handle: "button", label: "Button", type: "link" },
|
|
13
|
+
],
|
|
14
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
import AcePreviewRefresh from "@sandboxsonny/ace-kit/astro/AcePreviewRefresh.astro";
|
|
3
|
+
import SiteFooter from "~/components/SiteFooter.astro";
|
|
4
|
+
import SiteHeader from "~/components/SiteHeader.astro";
|
|
5
|
+
import "~/styles/global.css";
|
|
6
|
+
|
|
7
|
+
type Props = {
|
|
8
|
+
title: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
/** True when the page is showing a draft inside the CMS's preview frame. */
|
|
11
|
+
preview?: boolean;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const { title, description = "A simple example site for testing CMS Ace.", preview = false } = Astro.props;
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
<!doctype html>
|
|
18
|
+
<html lang="en">
|
|
19
|
+
<head>
|
|
20
|
+
<meta charset="utf-8" />
|
|
21
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
22
|
+
<meta name="description" content={description} />
|
|
23
|
+
<meta name="generator" content={Astro.generator} />
|
|
24
|
+
{preview && <meta name="robots" content="noindex" />}
|
|
25
|
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
26
|
+
<title>{title}</title>
|
|
27
|
+
</head>
|
|
28
|
+
<body>
|
|
29
|
+
<SiteHeader />
|
|
30
|
+
<main>
|
|
31
|
+
<slot />
|
|
32
|
+
</main>
|
|
33
|
+
<SiteFooter />
|
|
34
|
+
<AcePreviewRefresh />
|
|
35
|
+
</body>
|
|
36
|
+
</html>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { getPage } from "@sandboxsonny/ace-kit/astro";
|
|
3
|
+
import AceNotice from "@sandboxsonny/ace-kit/astro/AceNotice.astro";
|
|
4
|
+
|
|
5
|
+
import PageContent from "~/components/PageContent.astro";
|
|
6
|
+
import BaseLayout from "~/layouts/BaseLayout.astro";
|
|
7
|
+
import type { PageValues } from "~/types/page";
|
|
8
|
+
|
|
9
|
+
// Addresses are /{content type}/{entry}, as on Shopify: "/pages/about" is the entry "about" of the content type
|
|
10
|
+
// "pages", and "/" is the home entry. getPage works out what to show and sets the status and headers.
|
|
11
|
+
const page = await getPage<PageValues>(Astro);
|
|
12
|
+
if (page.kind === "redirect") return Astro.redirect(page.location, page.status);
|
|
13
|
+
|
|
14
|
+
const isPreview = Boolean(page.headers["Cache-Control"]);
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
{page.kind === "entry" ? (
|
|
18
|
+
<BaseLayout
|
|
19
|
+
title={`${page.entry.title} | Example Site`}
|
|
20
|
+
description={page.entry.description || undefined}
|
|
21
|
+
preview={isPreview}
|
|
22
|
+
>
|
|
23
|
+
<PageContent entry={page.entry} />
|
|
24
|
+
</BaseLayout>
|
|
25
|
+
) : (
|
|
26
|
+
<BaseLayout title="Example Site" preview={isPreview}>
|
|
27
|
+
<section class="section">
|
|
28
|
+
<AceNotice page={page} class="container" />
|
|
29
|
+
</section>
|
|
30
|
+
</BaseLayout>
|
|
31
|
+
)}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--colour-background: #ffffff;
|
|
3
|
+
--colour-surface: #f4f6f8;
|
|
4
|
+
--colour-text: #1b2430;
|
|
5
|
+
--colour-muted: #5b6776;
|
|
6
|
+
--colour-accent: #2f5bea;
|
|
7
|
+
--colour-accent-text: #ffffff;
|
|
8
|
+
--content-width: 64rem;
|
|
9
|
+
--font-body: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
*,
|
|
13
|
+
*::before,
|
|
14
|
+
*::after {
|
|
15
|
+
box-sizing: border-box;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
body {
|
|
19
|
+
margin: 0;
|
|
20
|
+
font-family: var(--font-body);
|
|
21
|
+
line-height: 1.6;
|
|
22
|
+
color: var(--colour-text);
|
|
23
|
+
background: var(--colour-background);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
img {
|
|
27
|
+
max-width: 100%;
|
|
28
|
+
height: auto;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
a {
|
|
32
|
+
color: var(--colour-accent);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.container {
|
|
36
|
+
width: min(100% - 2rem, var(--content-width));
|
|
37
|
+
margin-inline: auto;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.button {
|
|
41
|
+
display: inline-block;
|
|
42
|
+
padding: 0.75rem 1.25rem;
|
|
43
|
+
border-radius: 0.5rem;
|
|
44
|
+
background: var(--colour-accent);
|
|
45
|
+
color: var(--colour-accent-text);
|
|
46
|
+
font-weight: 600;
|
|
47
|
+
text-decoration: none;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.section {
|
|
51
|
+
padding-block: 3rem;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.section--surface {
|
|
55
|
+
background: var(--colour-surface);
|
|
56
|
+
}
|