forked from t3-oss/create-t3-app
-
Notifications
You must be signed in to change notification settings - Fork 0
/
pageContent.astro
237 lines (208 loc) · 7.09 KB
/
pageContent.astro
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
---
import { type Frontmatter, type KnownLanguageCode } from "../../config";
import { getIsRtlFromLangCode } from "../../languages";
import OnThisPageLinks from "../navigation/OnThisPageLinks";
import BreadCrumbs from "./breadCrumbs";
import OutdatedDocsBanner from "./outdatedDocsBanner.astro";
import Pagination from "./pagination.astro";
import { type MarkdownHeading } from "astro";
export interface Props {
frontmatter: Frontmatter;
path: string;
headings: MarkdownHeading[];
}
const { frontmatter, path, headings } = Astro.props;
const title = frontmatter.title;
const [_1, _2, lang] = path.split("/");
const isRtl = getIsRtlFromLangCode((lang || "en") as KnownLanguageCode);
---
<article id="article" class="flex w-full max-w-screen-lg flex-col">
{lang && lang !== "en" && <OutdatedDocsBanner path={path} />}
<div class="h-10">
<BreadCrumbs client:only />
</div>
<OnThisPageLinks
client:media="(max-width: 1024px)"
headings={headings}
title={title}
isRtl={isRtl}
/>
<section class="w-full">
<h1
id="overview"
class="mb-4 px-4 text-3xl font-extrabold dark:text-t3-purple-100"
>
<a
href="#overview"
class="heading-link heading-link--hidden---effects"
data-heading-link
>
{title}
</a>
</h1>
<article class="markdown w-full">
<slot />
</article>
</section>
</article>
<Pagination />
<script>
const copySVG = `<svg width="1.3em" height="1.3em" viewBox="0 0 256 256"><path fill="currentColor" d="M216 36H88a4 4 0 0 0-4 4v44H40a4 4 0 0 0-4 4v128a4 4 0 0 0 4 4h128a4 4 0 0 0 4-4v-44h44a4 4 0 0 0 4-4V40a4 4 0 0 0-4-4Zm-52 176H44V92h120Zm48-48h-40V88a4 4 0 0 0-4-4H92V44h120Z"/></svg>`;
let titles = document.querySelectorAll(
".remark-code-title",
) as NodeListOf<HTMLDivElement>;
titles.forEach((title) => {
title.dir = "ltr";
});
let blocks = document.querySelectorAll("pre");
blocks.forEach((block) => {
block.className = "relative flex w-full h-full";
block.dir = "ltr";
let copyButton = document.createElement("button");
copyButton.className =
"inline-flex absolute right-[.45em] top-[.5em] bg-zinc-100/20 rounded px-1 py-1 text-sm text-zinc-50 hover:bg-zinc-100/25 focus:outline-none focus:shadow-outline text-brand-primary";
copyButton.innerHTML = copySVG;
copyButton.addEventListener("click", () => {
let text = "";
const pre = copyButton.parentElement;
let code = pre?.querySelector("code");
if (code) {
text = code.innerText;
} else {
const span = pre?.querySelector("span");
if (span) {
text = span.innerText;
}
}
if (text) {
try {
navigator.clipboard.writeText(text);
} catch (err) {
console.error("Failed to copy: ", err);
}
copyButton.innerHTML = `<svg width="1.3em" height="1.3em" viewBox="0 0 256 256"><path fill="#22c55e" d="M104 192a8.5 8.5 0 0 1-5.7-2.3l-56-56a8.1 8.1 0 0 1 11.4-11.4l50.3 50.4L210.3 66.3a8.1 8.1 0 0 1 11.4 11.4l-112 112a8.5 8.5 0 0 1-5.7 2.3Z"/></svg>`;
setTimeout(() => {
copyButton.innerHTML = copySVG;
}, 1000);
}
});
block.appendChild(copyButton);
block.setAttribute("tabindex", "0");
});
</script>
<script is:inline>
//FIXME: We can make this properly typed later
/**
* Allows us to animate the details element
* for it to work make sure the details element contains a summary element
* and a div element with the class of content
* @param {*} element the details element which we wish to animate
*/
const AnimateDetails = (element) => {
const summary = element.querySelector("summary");
const content = element.querySelector(".content");
let props = { animation: null, isClosing: false, isExpanding: false };
/**
* onClick event handler for the summary element
* @param {*} event the event object
*/
const onClick = (event) => {
event.preventDefault();
element.style.overflow = "hidden";
if (props.isClosing || !element.open) open();
else if (props.isExpanded || element.open) close();
};
/**
* Closes the details element
* This function is in charge of animating the closing of the
* details element
*/
const close = () => {
props.isClosing = true;
const startHeight = `${element.offsetHeight}px`;
const endHeight = `${summary.offsetHeight}px`;
if (props.animation) props.animation.cancel();
props.animation = element.animate(
{
height: [startHeight, endHeight],
},
{
duration: 300,
easing: "ease-in-out",
},
);
props.animation.onfinish = () => onAnimationFinish(false);
props.animation.oncancel = () => (props.isClosing = false);
};
/**
* Opens the details element
* requests the animation frame to animate the opening of the details element
*/
const open = () => {
element.style.height = `${summary.offsetHeight}px`;
element.open = true;
window.requestAnimationFrame(() => expand());
};
/**
* Expands the details element
* This function is in charge of animating the expansion of the
* details element
*/
const expand = () => {
props.isExpanding = true;
const startHeight = `${element.offsetHeight}px`;
const endHeight = `${content.offsetHeight + summary.offsetHeight}px`;
if (props.animation) props.animation.cancel();
props.animation = element.animate(
{
height: [startHeight, endHeight],
},
{
duration: 300,
easing: "ease-in-out",
},
);
props.animation.onfinish = () => onAnimationFinish(true);
props.animation.oncancel = () => (props.isExpanding = false);
};
/**
* Called when the animation is finished
* @param {*} open whether the details element is open or not
*/
const onAnimationFinish = (open) => {
element.open = open;
props.animation = null;
props.isClosing = false;
props.isExpanding = false;
element.style.height = element.style.overflow = "";
};
/**
* Adds our new event listener to the summary element
*/
summary.addEventListener("click", (event) => onClick(event));
};
/**
* Allows us to animate all the details elements on the page
* Will check if the details element contains a div element with the class of content
*/
const viableDetails = document.querySelectorAll("details");
viableDetails.forEach((element) => {
if (element.querySelector(".content")) {
AnimateDetails(element);
}
});
</script>
<script>
const main = () => {
const codeElements = Array.from(document.querySelectorAll("code")).filter(
(codeElement) => codeElement.parentNode?.nodeName !== "PRE",
);
codeElements.forEach((codeElement) => {
const codeWrapper = document.createElement("div");
codeWrapper.classList.add("code-wrapper");
codeElement.parentNode?.insertBefore(codeWrapper, codeElement);
codeWrapper.appendChild(codeElement);
});
};
main();
</script>