mirror of
https://github.com/jackyzha0/quartz.git
synced 2025-12-19 10:54:06 -06:00
Merge 47667eefd4 into c99c8070f2
This commit is contained in:
commit
25b5fafcbf
65
docs/features/replybyemail button.md
Normal file
65
docs/features/replybyemail button.md
Normal 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);
|
||||
}
|
||||
```
|
||||
109
quartz/components/ReplyByEmail.tsx
Normal file
109
quartz/components/ReplyByEmail.tsx
Normal 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
|
||||
@ -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,
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user