2024-10-14 06:49:37 +00:00
|
|
|
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
2024-10-16 10:36:51 +00:00
|
|
|
import style from "./styles/buttons8831.scss"
|
|
|
|
import rawButtonsData from "./buttons.json"
|
|
|
|
import DOMPurify from "dompurify"
|
|
|
|
|
|
|
|
interface ButtonData {
|
|
|
|
url?: string
|
|
|
|
image?: string
|
|
|
|
alt?: string
|
|
|
|
title?: string
|
|
|
|
type?: string
|
|
|
|
contentType?: "image" | "iframe" | "text" | "custom"
|
|
|
|
iframeAttributes?: { [key: string]: string }
|
|
|
|
border?: [string, string] // Array of two colors for the border
|
|
|
|
text?: string // For "text" contentType
|
|
|
|
customContent?: string // For "custom" contentType
|
|
|
|
textColor?: string // For specifying text color in "text" contentType
|
|
|
|
}
|
2024-10-14 07:09:28 +00:00
|
|
|
|
|
|
|
export default (() => {
|
2024-10-16 10:36:51 +00:00
|
|
|
const Btn8831: QuartzComponent = (props: QuartzComponentProps) => {
|
|
|
|
const { displayClass } = props
|
|
|
|
|
|
|
|
const buttonsData = rawButtonsData as ButtonData[]
|
2024-10-14 10:32:16 +00:00
|
|
|
// Group buttons by type
|
|
|
|
const groupedButtons = groupButtonsByType(buttonsData)
|
2024-10-14 07:09:28 +00:00
|
|
|
|
2024-10-14 10:32:16 +00:00
|
|
|
// Define the order of types
|
2024-10-14 10:50:43 +00:00
|
|
|
const typeOrder = ["friend", "standard", "misc"]
|
2024-10-14 07:09:28 +00:00
|
|
|
|
|
|
|
return (
|
|
|
|
<div class={`btn8831-container ${displayClass ?? ""}`}>
|
2024-10-14 10:32:16 +00:00
|
|
|
{typeOrder.map((type, index) => {
|
|
|
|
const buttons = groupedButtons[type]
|
|
|
|
if (buttons && buttons.length > 0) {
|
|
|
|
return (
|
|
|
|
<div key={type}>
|
|
|
|
{/* Render buttons of the current type */}
|
|
|
|
<div class="button-group">
|
|
|
|
{buttons.map((button, idx) => (
|
2024-10-14 10:39:34 +00:00
|
|
|
<div key={idx} class="button-item">
|
|
|
|
{renderButtonContent(button)}
|
|
|
|
</div>
|
2024-10-14 10:32:16 +00:00
|
|
|
))}
|
|
|
|
</div>
|
|
|
|
{/* Add a horizontal line after the group except for the last one */}
|
|
|
|
{index < typeOrder.length - 1 && <hr />}
|
|
|
|
</div>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
return null
|
|
|
|
})}
|
2024-10-14 07:09:28 +00:00
|
|
|
</div>
|
|
|
|
)
|
2024-10-14 07:08:15 +00:00
|
|
|
}
|
2024-10-14 06:49:37 +00:00
|
|
|
|
2024-10-16 10:36:51 +00:00
|
|
|
Btn8831.css = style
|
|
|
|
return Btn8831
|
|
|
|
}) satisfies QuartzComponentConstructor
|
2024-10-14 10:32:16 +00:00
|
|
|
|
2024-10-16 10:36:51 +00:00
|
|
|
function groupButtonsByType(buttons: ButtonData[]) {
|
|
|
|
const groups: { [key: string]: ButtonData[] } = {
|
|
|
|
misc: [],
|
|
|
|
friend: [],
|
|
|
|
standard: [],
|
2024-10-14 10:32:16 +00:00
|
|
|
}
|
|
|
|
|
2024-10-16 10:36:51 +00:00
|
|
|
buttons.forEach((button) => {
|
|
|
|
let type = button.type?.toLowerCase() || "standard"
|
|
|
|
if (type === "fren") type = "friend"
|
|
|
|
if (!groups[type]) type = "standard" // Default to 'standard' if type is unrecognized
|
|
|
|
groups[type].push(button)
|
|
|
|
})
|
|
|
|
|
|
|
|
return groups
|
|
|
|
}
|
|
|
|
|
|
|
|
function renderButtonContent(button: ButtonData): preact.VNode | null {
|
|
|
|
const contentType = button.contentType?.toLowerCase() || "image"
|
|
|
|
|
|
|
|
let content: preact.VNode | null = null
|
|
|
|
|
|
|
|
if (contentType === "image") {
|
|
|
|
// ... existing code for image ...
|
|
|
|
} else if (contentType === "iframe") {
|
|
|
|
// ... existing code for iframe ...
|
|
|
|
} else if (contentType === "text") {
|
|
|
|
const borderColors = button.border || ["#000", "#000"]
|
|
|
|
const textContent = button.text || ""
|
|
|
|
const textColor = button.textColor || "#000"
|
|
|
|
|
|
|
|
// **Important:** Sanitize the text content to prevent XSS attacks.
|
|
|
|
// Install DOMPurify: npm install dompurify
|
|
|
|
const sanitizedTextContent = DOMPurify.sanitize(textContent)
|
|
|
|
|
|
|
|
const divStyle = {
|
|
|
|
width: "88px",
|
|
|
|
height: "31px",
|
|
|
|
boxSizing: "border-box",
|
|
|
|
borderStyle: "solid",
|
|
|
|
borderWidth: "2px",
|
|
|
|
borderTopColor: borderColors[0],
|
|
|
|
borderRightColor: borderColors[0],
|
|
|
|
borderBottomColor: borderColors[1],
|
|
|
|
borderLeftColor: borderColors[1],
|
|
|
|
display: "flex",
|
|
|
|
alignItems: "center",
|
|
|
|
justifyContent: "center",
|
|
|
|
fontSize: "12px",
|
|
|
|
overflow: "hidden",
|
|
|
|
color: textColor,
|
|
|
|
}
|
|
|
|
|
|
|
|
const textElement = (
|
|
|
|
<div style={divStyle} dangerouslySetInnerHTML={{ __html: sanitizedTextContent }}></div>
|
|
|
|
)
|
|
|
|
|
|
|
|
if (button.url) {
|
|
|
|
content = <a href={button.url}>{textElement}</a>
|
2024-10-14 10:39:34 +00:00
|
|
|
} else {
|
2024-10-16 10:36:51 +00:00
|
|
|
content = textElement
|
2024-10-14 10:39:34 +00:00
|
|
|
}
|
2024-10-16 10:36:51 +00:00
|
|
|
} else if (contentType === "custom") {
|
|
|
|
// ... existing code for custom ...
|
|
|
|
} else {
|
|
|
|
return null
|
2024-10-14 10:39:34 +00:00
|
|
|
}
|
2024-10-16 10:36:51 +00:00
|
|
|
|
|
|
|
return content
|
|
|
|
}
|
|
|
|
|