This commit is contained in:
Dan 2025-11-27 03:22:11 +01:00 committed by GitHub
commit 25b5fafcbf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 176 additions and 0 deletions

View File

@ -0,0 +1,65 @@
---
title: Reply by Email Button
tags:
- component
---
The Reply By Email Button is a feature that allows users to display a button under their notes, allowing visitors to comment or submited feedback for your notes via email.
- The email address is base64 encoded to provide some basic protection from bots.
- The subject line of the email will be taken from the page title where the button is clicked.
- You can specify on what notes the button should be displayed or excluded.
- The label on the button can also be customised.
## Configuration
The Reply By Email Button is disabled by default. To enable it, you can add the component to your layout configuration in `quartz.layout.ts`.
Minimal configuration:
```ts
Component.ReplyByEmail({
email: "contact@example.com"
}),
```
You can specify under which notes to display the button by adding the following:
```ts
includeTitles: ["Welcome to Quartz 4", "Reply by Email Button", "Contact"],
```
Alternatively, you can include the button by default by ommiting the `includeTitles` line, and specify notes under which the button should not be displayed:
```ts
excludeTitles: ["Welcome to Quartz 4", "Home"],
```
You can also override the default `buttonLabel` text:
```ts
buttonLabel: "Reply by email"
```
## Usage
The natural placement for the Reply By Email Button is within `afterBody` in the Content Pages, so that it is displayed right under the note:
```ts
afterBody: [
Component.ReplyByEmail({
email: "contact@example.com", // The email address to be linked to
// includeTitles: ["Welcome to Quartz 4"], // You can specify which page titles to include or comment out the line to include on all pages
excludeTitles: ["Welcome to Quartz 4", "Home"], // You can specify which page titles to exclude when includeTitles is empty
buttonLabel: "Submit feedback by email" // You can override the default button text "Reply by email"
}),
],
```
## Customization
You can customize the appearance of the ReplyByEmail button through CSS variables and styles. The component uses the following class:
- `.reply-by-email-button`
Example customization in your custom CSS:
```scss
.reply-by-email-button {
color: var(--tertiary);
}
```

View File

@ -0,0 +1,109 @@
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
import { classNames } from "../util/lang"
interface ReplyByEmailOptions {
email: string
includeTitles?: string[]
excludeTitles?: string[]
buttonLabel?: string
}
const defaultOptions: Partial<ReplyByEmailOptions> = {
includeTitles: [],
excludeTitles: [],
buttonLabel: "Reply by email"
}
const ReplyByEmail: QuartzComponent = ({
fileData,
displayClass,
email,
includeTitles,
excludeTitles,
buttonLabel
}: QuartzComponentProps & ReplyByEmailOptions) => {
const title = fileData.frontmatter?.title
const encodedEmail = btoa(email)
const includeList = includeTitles || defaultOptions.includeTitles
const excludeList = excludeTitles || defaultOptions.excludeTitles
const label = buttonLabel || defaultOptions.buttonLabel
// Display logic:
// 1. If includeTitles is not empty, only show on those pages
// 2. If includeTitles is empty, show on all pages except those in excludeTitles
const shouldDisplay = title && (
(includeList.length > 0 && includeList.includes(title)) ||
(includeList.length === 0 && !excludeList.includes(title))
)
if (shouldDisplay) {
return (
<div class="center-wrapper">
<button
class={classNames(displayClass, "reply-by-email-button")}
data-email={encodedEmail}
data-title={encodeURIComponent(title)}
onclick={`
const encodedEmail = this.getAttribute('data-email');
const email = atob(encodedEmail);
const title = this.getAttribute('data-title');
const mailtoLink = 'mailto:' + email + '?subject=' + title;
window.location.href = mailtoLink;
return false;
`}
>
{label}
</button>
</div>
)
} else {
return null
}
}
ReplyByEmail.css = `
.center-wrapper {
display: flex;
justify-content: center;
margin-bottom: 2rem;
}
.reply-by-email-button {
display: inline-block;
padding: 0.5rem 1rem;
background-color: var(--highlight);
color: var(--secondary);
border-radius: 5px;
transition: background-color 0.2s ease, transform 0.2s ease;
cursor: pointer;
border: none;
font-size: 1rem;
font-family: inherit;
}
.reply-by-email-button:hover {
transform: scale(1.05);
}
`
export default ((opts?: ReplyByEmailOptions) => {
if (!opts?.email) {
throw new Error("ReplyByEmail component requires an email parameter")
}
const component: QuartzComponent = (props) => {
return ReplyByEmail({
...props,
email: opts.email,
includeTitles: opts?.includeTitles,
excludeTitles: opts?.excludeTitles,
buttonLabel: opts?.buttonLabel
})
}
component.css = ReplyByEmail.css
return component
}) satisfies QuartzComponentConstructor

View File

@ -23,6 +23,7 @@ import Breadcrumbs from "./Breadcrumbs"
import Comments from "./Comments"
import Flex from "./Flex"
import ConditionalRender from "./ConditionalRender"
import ReplyByEmail from "./ReplyByEmail"
export {
ArticleTitle,
@ -50,4 +51,5 @@ export {
Comments,
Flex,
ConditionalRender,
ReplyByEmail,
}