working on making nojs workable
This commit is contained in:
@@ -13,12 +13,13 @@ export function Typewriter(props: {
|
||||
const [isTyping, setIsTyping] = createSignal(false);
|
||||
const [isDelaying, setIsDelaying] = createSignal(delay > 0);
|
||||
const [shouldHide, setShouldHide] = createSignal(false);
|
||||
const [animated, setAnimated] = createSignal(false);
|
||||
const resolved = children(() => props.children);
|
||||
|
||||
onMount(() => {
|
||||
if (!containerRef || !cursorRef) return;
|
||||
|
||||
// FIRST: Walk DOM and hide all text immediately
|
||||
// FIRST: Walk DOM and split text into character spans
|
||||
const textNodes: { node: Text; text: string; startIndex: number }[] = [];
|
||||
let totalChars = 0;
|
||||
|
||||
@@ -38,7 +39,7 @@ export function Typewriter(props: {
|
||||
text.split("").forEach((char, i) => {
|
||||
const charSpan = document.createElement("span");
|
||||
charSpan.textContent = char;
|
||||
charSpan.style.opacity = "0";
|
||||
// Don't set opacity here - CSS will handle it based on data-typewriter state
|
||||
charSpan.setAttribute(
|
||||
"data-char-index",
|
||||
String(totalChars - text.length + i)
|
||||
@@ -54,6 +55,12 @@ export function Typewriter(props: {
|
||||
|
||||
walkDOM(containerRef);
|
||||
|
||||
// Mark as animated AFTER DOM manipulation - this triggers CSS to hide characters
|
||||
setAnimated(true);
|
||||
|
||||
// Mark container as ready for animation
|
||||
containerRef.setAttribute("data-typewriter-ready", "true");
|
||||
|
||||
// Position cursor at the first character location
|
||||
const firstChar = containerRef.querySelector(
|
||||
'[data-char-index="0"]'
|
||||
@@ -143,7 +150,11 @@ export function Typewriter(props: {
|
||||
};
|
||||
|
||||
return (
|
||||
<div ref={containerRef} class={props.class}>
|
||||
<div
|
||||
ref={containerRef}
|
||||
class={props.class}
|
||||
data-typewriter={!animated() ? "static" : "animated"}
|
||||
>
|
||||
{resolved()}
|
||||
<span ref={cursorRef} class={getCursorClass()}></span>
|
||||
</div>
|
||||
|
||||
@@ -16,6 +16,8 @@ import DetailsContent from "@tiptap/extension-details-content";
|
||||
import { Node } from "@tiptap/core";
|
||||
import { createLowlight, common } from "lowlight";
|
||||
import { Mermaid } from "./extensions/Mermaid";
|
||||
import { ConditionalBlock } from "./extensions/ConditionalBlock";
|
||||
import { ConditionalInline } from "./extensions/ConditionalInline";
|
||||
import TextAlign from "@tiptap/extension-text-align";
|
||||
import Superscript from "@tiptap/extension-superscript";
|
||||
import Subscript from "@tiptap/extension-subscript";
|
||||
@@ -380,6 +382,24 @@ export default function TextEditor(props: TextEditorProps) {
|
||||
|
||||
const [showKeyboardHelp, setShowKeyboardHelp] = createSignal(false);
|
||||
|
||||
const [showConditionalConfig, setShowConditionalConfig] = createSignal(false);
|
||||
const [conditionalConfigPosition, setConditionalConfigPosition] =
|
||||
createSignal({
|
||||
top: 0,
|
||||
left: 0
|
||||
});
|
||||
const [conditionalForm, setConditionalForm] = createSignal<{
|
||||
conditionType: "auth" | "privilege" | "date" | "feature" | "env";
|
||||
conditionValue: string;
|
||||
showWhen: "true" | "false";
|
||||
inline: boolean; // New field for inline vs block
|
||||
}>({
|
||||
conditionType: "auth",
|
||||
conditionValue: "authenticated",
|
||||
showWhen: "true",
|
||||
inline: false
|
||||
});
|
||||
|
||||
const [isFullscreen, setIsFullscreen] = createSignal(false);
|
||||
const [keyboardVisible, setKeyboardVisible] = createSignal(false);
|
||||
const [keyboardHeight, setKeyboardHeight] = createSignal(0);
|
||||
@@ -434,6 +454,8 @@ export default function TextEditor(props: TextEditorProps) {
|
||||
}
|
||||
}),
|
||||
Mermaid,
|
||||
ConditionalBlock,
|
||||
ConditionalInline,
|
||||
TextAlign.configure({
|
||||
types: ["heading", "paragraph"],
|
||||
alignments: ["left", "center", "right", "justify"],
|
||||
@@ -1005,6 +1027,27 @@ export default function TextEditor(props: TextEditorProps) {
|
||||
}
|
||||
});
|
||||
|
||||
// Close conditional config on outside click
|
||||
createEffect(() => {
|
||||
if (showConditionalConfig()) {
|
||||
const handleClickOutside = (e: MouseEvent) => {
|
||||
const target = e.target as HTMLElement;
|
||||
if (
|
||||
!target.closest(".conditional-config") &&
|
||||
!target.closest("[data-conditional-trigger]")
|
||||
) {
|
||||
setShowConditionalConfig(false);
|
||||
}
|
||||
};
|
||||
|
||||
setTimeout(() => {
|
||||
document.addEventListener("click", handleClickOutside);
|
||||
}, 0);
|
||||
|
||||
return () => document.removeEventListener("click", handleClickOutside);
|
||||
}
|
||||
});
|
||||
|
||||
const showMermaidSelector = (e: MouseEvent) => {
|
||||
const buttonRect = (e.currentTarget as HTMLElement).getBoundingClientRect();
|
||||
setMermaidMenuPosition({
|
||||
@@ -1022,6 +1065,108 @@ export default function TextEditor(props: TextEditorProps) {
|
||||
setShowMermaidTemplates(false);
|
||||
};
|
||||
|
||||
// Conditional block functions
|
||||
const showConditionalConfigurator = (e: MouseEvent) => {
|
||||
const buttonRect = (e.currentTarget as HTMLElement).getBoundingClientRect();
|
||||
setConditionalConfigPosition({
|
||||
top: buttonRect.bottom + 5,
|
||||
left: buttonRect.left
|
||||
});
|
||||
|
||||
// If cursor is in existing conditional, load its values
|
||||
const instance = editor();
|
||||
if (instance?.isActive("conditionalBlock")) {
|
||||
const attrs = instance.getAttributes("conditionalBlock");
|
||||
setConditionalForm({
|
||||
conditionType: attrs.conditionType || "auth",
|
||||
conditionValue: attrs.conditionValue || "authenticated",
|
||||
showWhen: attrs.showWhen || "true",
|
||||
inline: false
|
||||
});
|
||||
} else if (instance?.isActive("conditionalInline")) {
|
||||
const attrs = instance.getAttributes("conditionalInline");
|
||||
setConditionalForm({
|
||||
conditionType: attrs.conditionType || "auth",
|
||||
conditionValue: attrs.conditionValue || "authenticated",
|
||||
showWhen: attrs.showWhen || "true",
|
||||
inline: true
|
||||
});
|
||||
} else {
|
||||
// Reset to defaults for new conditional
|
||||
setConditionalForm({
|
||||
conditionType: "auth",
|
||||
conditionValue: "authenticated",
|
||||
showWhen: "true",
|
||||
inline: false
|
||||
});
|
||||
}
|
||||
|
||||
setShowConditionalConfig(!showConditionalConfig());
|
||||
};
|
||||
|
||||
const insertConditionalBlock = () => {
|
||||
const instance = editor();
|
||||
if (!instance) return;
|
||||
|
||||
const { conditionType, conditionValue, showWhen, inline } =
|
||||
conditionalForm();
|
||||
|
||||
if (inline) {
|
||||
// Handle inline conditionals (Mark)
|
||||
if (instance.isActive("conditionalInline")) {
|
||||
// Update existing inline conditional
|
||||
instance
|
||||
.chain()
|
||||
.focus()
|
||||
.unsetConditionalInline()
|
||||
.setConditionalInline({
|
||||
conditionType,
|
||||
conditionValue,
|
||||
showWhen
|
||||
})
|
||||
.run();
|
||||
} else {
|
||||
// Apply inline conditional to selection
|
||||
instance
|
||||
.chain()
|
||||
.focus()
|
||||
.setConditionalInline({
|
||||
conditionType,
|
||||
conditionValue,
|
||||
showWhen
|
||||
})
|
||||
.run();
|
||||
}
|
||||
} else {
|
||||
// Handle block conditionals (Node)
|
||||
if (instance.isActive("conditionalBlock")) {
|
||||
// Update existing conditional
|
||||
instance
|
||||
.chain()
|
||||
.focus()
|
||||
.updateConditionalBlock({
|
||||
conditionType,
|
||||
conditionValue,
|
||||
showWhen
|
||||
})
|
||||
.run();
|
||||
} else {
|
||||
// Wrap selection in new conditional
|
||||
instance
|
||||
.chain()
|
||||
.focus()
|
||||
.setConditionalBlock({
|
||||
conditionType,
|
||||
conditionValue,
|
||||
showWhen
|
||||
})
|
||||
.run();
|
||||
}
|
||||
}
|
||||
|
||||
setShowConditionalConfig(false);
|
||||
};
|
||||
|
||||
// Toggle fullscreen mode
|
||||
const toggleFullscreen = () => {
|
||||
setIsFullscreen(!isFullscreen());
|
||||
@@ -1123,6 +1268,200 @@ export default function TextEditor(props: TextEditorProps) {
|
||||
);
|
||||
};
|
||||
|
||||
// Conditional Configurator Component
|
||||
const ConditionalConfigurator = () => {
|
||||
return (
|
||||
<div class="bg-mantle border-surface2 w-80 rounded border p-4 shadow-lg">
|
||||
<h3 class="text-text mb-3 font-semibold">Conditional Block</h3>
|
||||
|
||||
{/* Condition Type Selector */}
|
||||
<label class="text-subtext0 mb-2 block text-xs">Condition Type</label>
|
||||
<select
|
||||
class="bg-surface0 text-text border-surface2 mb-3 w-full rounded border px-2 py-1"
|
||||
value={conditionalForm().conditionType}
|
||||
onInput={(e) =>
|
||||
setConditionalForm({
|
||||
...conditionalForm(),
|
||||
conditionType: e.currentTarget.value as any
|
||||
})
|
||||
}
|
||||
>
|
||||
<option value="auth">User Authentication</option>
|
||||
<option value="privilege">Privilege Level</option>
|
||||
<option value="date">Date Range</option>
|
||||
<option value="feature">Feature Flag</option>
|
||||
<option value="env">Environment Variable</option>
|
||||
</select>
|
||||
|
||||
{/* Dynamic Condition Value Input based on type */}
|
||||
<Show when={conditionalForm().conditionType === "auth"}>
|
||||
<label class="text-subtext0 mb-2 block text-xs">User State</label>
|
||||
<select
|
||||
class="bg-surface0 text-text border-surface2 mb-3 w-full rounded border px-2 py-1"
|
||||
value={conditionalForm().conditionValue}
|
||||
onInput={(e) =>
|
||||
setConditionalForm({
|
||||
...conditionalForm(),
|
||||
conditionValue: e.currentTarget.value
|
||||
})
|
||||
}
|
||||
>
|
||||
<option value="authenticated">Authenticated</option>
|
||||
<option value="anonymous">Anonymous</option>
|
||||
</select>
|
||||
</Show>
|
||||
|
||||
<Show when={conditionalForm().conditionType === "privilege"}>
|
||||
<label class="text-subtext0 mb-2 block text-xs">
|
||||
Privilege Level
|
||||
</label>
|
||||
<select
|
||||
class="bg-surface0 text-text border-surface2 mb-3 w-full rounded border px-2 py-1"
|
||||
value={conditionalForm().conditionValue}
|
||||
onInput={(e) =>
|
||||
setConditionalForm({
|
||||
...conditionalForm(),
|
||||
conditionValue: e.currentTarget.value
|
||||
})
|
||||
}
|
||||
>
|
||||
<option value="admin">Admin</option>
|
||||
<option value="user">User</option>
|
||||
<option value="anonymous">Anonymous</option>
|
||||
</select>
|
||||
</Show>
|
||||
|
||||
<Show when={conditionalForm().conditionType === "date"}>
|
||||
<label class="text-subtext0 mb-2 block text-xs">Date Condition</label>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="before:2026-01-01 or after:2025-01-01"
|
||||
class="bg-surface0 text-text border-surface2 mb-3 w-full rounded border px-2 py-1"
|
||||
value={conditionalForm().conditionValue}
|
||||
onInput={(e) =>
|
||||
setConditionalForm({
|
||||
...conditionalForm(),
|
||||
conditionValue: e.currentTarget.value
|
||||
})
|
||||
}
|
||||
/>
|
||||
<div class="text-subtext0 mb-3 text-xs">
|
||||
Format: before:YYYY-MM-DD, after:YYYY-MM-DD, or
|
||||
between:YYYY-MM-DD,YYYY-MM-DD
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
<Show when={conditionalForm().conditionType === "feature"}>
|
||||
<label class="text-subtext0 mb-2 block text-xs">
|
||||
Feature Flag Name
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="feature-name"
|
||||
class="bg-surface0 text-text border-surface2 mb-3 w-full rounded border px-2 py-1"
|
||||
value={conditionalForm().conditionValue}
|
||||
onInput={(e) =>
|
||||
setConditionalForm({
|
||||
...conditionalForm(),
|
||||
conditionValue: e.currentTarget.value
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Show>
|
||||
|
||||
<Show when={conditionalForm().conditionType === "env"}>
|
||||
<label class="text-subtext0 mb-2 block text-xs">
|
||||
Environment Variable
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
list="env-variables"
|
||||
placeholder="NODE_ENV:production"
|
||||
class="bg-surface0 text-text border-surface2 mb-3 w-full rounded border px-2 py-1"
|
||||
value={conditionalForm().conditionValue}
|
||||
onInput={(e) =>
|
||||
setConditionalForm({
|
||||
...conditionalForm(),
|
||||
conditionValue: e.currentTarget.value
|
||||
})
|
||||
}
|
||||
/>
|
||||
<datalist id="env-variables">
|
||||
<option value="NODE_ENV:development">
|
||||
Development environment
|
||||
</option>
|
||||
<option value="NODE_ENV:production">Production environment</option>
|
||||
<option value="NODE_ENV:test">Test environment</option>
|
||||
<option value="VERCEL_ENV:preview">
|
||||
Vercel preview deployment
|
||||
</option>
|
||||
<option value="VERCEL_ENV:production">Vercel production</option>
|
||||
<option value="VITE_DOMAIN:*">Any domain configured</option>
|
||||
<option value="VITE_AWS_BUCKET_STRING:*">
|
||||
S3 bucket configured
|
||||
</option>
|
||||
<option value="VITE_GOOGLE_CLIENT_ID:*">Google auth enabled</option>
|
||||
<option value="VITE_GITHUB_CLIENT_ID:*">GitHub auth enabled</option>
|
||||
<option value="VITE_WEBSOCKET:*">WebSocket configured</option>
|
||||
</datalist>
|
||||
<div class="text-subtext0 mb-3 text-xs">
|
||||
Format: VAR_NAME:value or VAR_NAME:* for any truthy value
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
{/* Show When Toggle */}
|
||||
<label class="text-subtext0 mb-2 block text-xs">Show When</label>
|
||||
<select
|
||||
class="bg-surface0 text-text border-surface2 mb-3 w-full rounded border px-2 py-1"
|
||||
value={conditionalForm().showWhen}
|
||||
onInput={(e) =>
|
||||
setConditionalForm({
|
||||
...conditionalForm(),
|
||||
showWhen: e.currentTarget.value as "true" | "false"
|
||||
})
|
||||
}
|
||||
>
|
||||
<option value="true">Condition is TRUE</option>
|
||||
<option value="false">Condition is FALSE</option>
|
||||
</select>
|
||||
|
||||
{/* Inline Toggle */}
|
||||
<label class="text-subtext0 mb-3 flex items-center gap-2 text-xs">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={conditionalForm().inline}
|
||||
onChange={(e) =>
|
||||
setConditionalForm({
|
||||
...conditionalForm(),
|
||||
inline: e.currentTarget.checked
|
||||
})
|
||||
}
|
||||
class="rounded"
|
||||
/>
|
||||
<span>Inline (no line break)</span>
|
||||
</label>
|
||||
|
||||
{/* Action Buttons */}
|
||||
<div class="flex gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={insertConditionalBlock}
|
||||
class="bg-blue rounded px-3 py-1 text-sm hover:brightness-125"
|
||||
>
|
||||
Apply
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowConditionalConfig(false)}
|
||||
class="hover:bg-surface1 rounded px-3 py-1 text-sm"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={containerRef}
|
||||
@@ -1475,6 +1814,19 @@ export default function TextEditor(props: TextEditorProps) {
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
{/* Conditional Configurator */}
|
||||
<Show when={showConditionalConfig()}>
|
||||
<div
|
||||
class="conditional-config fixed z-110"
|
||||
style={{
|
||||
top: `${conditionalConfigPosition().top}px`,
|
||||
left: `${conditionalConfigPosition().left}px`
|
||||
}}
|
||||
>
|
||||
<ConditionalConfigurator />
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
{/* Main Toolbar - Pinned at top in fullscreen */}
|
||||
<div
|
||||
class="border-surface2 bg-base border-b"
|
||||
@@ -1782,6 +2134,19 @@ export default function TextEditor(props: TextEditorProps) {
|
||||
>
|
||||
📊 Diagram
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={showConditionalConfigurator}
|
||||
data-conditional-trigger
|
||||
class={`${
|
||||
instance().isActive("conditionalBlock")
|
||||
? "bg-surface2"
|
||||
: "hover:bg-surface1"
|
||||
} rounded px-2 py-1 text-xs`}
|
||||
title="Insert Conditional Block"
|
||||
>
|
||||
🔒 Conditional
|
||||
</button>
|
||||
<div class="border-surface2 mx-1 border-l"></div>
|
||||
<button
|
||||
type="button"
|
||||
|
||||
103
src/components/blog/extensions/ConditionalBlock.ts
Normal file
103
src/components/blog/extensions/ConditionalBlock.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
import { Node, mergeAttributes } from "@tiptap/core";
|
||||
|
||||
export interface ConditionalBlockOptions {
|
||||
HTMLAttributes: Record<string, any>;
|
||||
}
|
||||
|
||||
export interface ConditionalBlockAttributes {
|
||||
conditionType: "auth" | "privilege" | "date" | "feature" | "env";
|
||||
conditionValue: string;
|
||||
showWhen: "true" | "false";
|
||||
}
|
||||
|
||||
declare module "@tiptap/core" {
|
||||
interface Commands<ReturnType> {
|
||||
conditionalBlock: {
|
||||
setConditionalBlock: (
|
||||
attributes: ConditionalBlockAttributes
|
||||
) => ReturnType;
|
||||
updateConditionalBlock: (
|
||||
attributes: Partial<ConditionalBlockAttributes>
|
||||
) => ReturnType;
|
||||
removeConditionalBlock: () => ReturnType;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const ConditionalBlock = Node.create<ConditionalBlockOptions>({
|
||||
name: "conditionalBlock",
|
||||
group: "block",
|
||||
content: "block+",
|
||||
defining: true,
|
||||
isolating: true,
|
||||
|
||||
addOptions() {
|
||||
return {
|
||||
HTMLAttributes: {
|
||||
class: "conditional-block"
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
addAttributes() {
|
||||
return {
|
||||
conditionType: {
|
||||
default: "auth",
|
||||
parseHTML: (element) => element.getAttribute("data-condition-type"),
|
||||
renderHTML: (attributes) => ({
|
||||
"data-condition-type": attributes.conditionType
|
||||
})
|
||||
},
|
||||
conditionValue: {
|
||||
default: "authenticated",
|
||||
parseHTML: (element) => element.getAttribute("data-condition-value"),
|
||||
renderHTML: (attributes) => ({
|
||||
"data-condition-value": attributes.conditionValue
|
||||
})
|
||||
},
|
||||
showWhen: {
|
||||
default: "true",
|
||||
parseHTML: (element) => element.getAttribute("data-show-when"),
|
||||
renderHTML: (attributes) => ({
|
||||
"data-show-when": attributes.showWhen
|
||||
})
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
parseHTML() {
|
||||
return [
|
||||
{
|
||||
tag: "div.conditional-block[data-condition-type]"
|
||||
}
|
||||
];
|
||||
},
|
||||
|
||||
renderHTML({ HTMLAttributes }) {
|
||||
return [
|
||||
"div",
|
||||
mergeAttributes(this.options.HTMLAttributes, HTMLAttributes),
|
||||
["div", { class: "conditional-content" }, 0]
|
||||
];
|
||||
},
|
||||
|
||||
addCommands() {
|
||||
return {
|
||||
setConditionalBlock:
|
||||
(attributes) =>
|
||||
({ commands }) => {
|
||||
return commands.wrapIn(this.name, attributes);
|
||||
},
|
||||
updateConditionalBlock:
|
||||
(attributes) =>
|
||||
({ commands }) => {
|
||||
return commands.updateAttributes(this.name, attributes);
|
||||
},
|
||||
removeConditionalBlock:
|
||||
() =>
|
||||
({ commands }) => {
|
||||
return commands.lift(this.name);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
101
src/components/blog/extensions/ConditionalInline.ts
Normal file
101
src/components/blog/extensions/ConditionalInline.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
import { Mark, mergeAttributes } from "@tiptap/core";
|
||||
|
||||
export interface ConditionalInlineOptions {
|
||||
HTMLAttributes: Record<string, any>;
|
||||
}
|
||||
|
||||
export interface ConditionalInlineAttributes {
|
||||
conditionType: "auth" | "privilege" | "date" | "feature" | "env";
|
||||
conditionValue: string;
|
||||
showWhen: "true" | "false";
|
||||
}
|
||||
|
||||
declare module "@tiptap/core" {
|
||||
interface Commands<ReturnType> {
|
||||
conditionalInline: {
|
||||
setConditionalInline: (
|
||||
attributes: ConditionalInlineAttributes
|
||||
) => ReturnType;
|
||||
toggleConditionalInline: (
|
||||
attributes: ConditionalInlineAttributes
|
||||
) => ReturnType;
|
||||
unsetConditionalInline: () => ReturnType;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const ConditionalInline = Mark.create<ConditionalInlineOptions>({
|
||||
name: "conditionalInline",
|
||||
priority: 1000,
|
||||
keepOnSplit: false,
|
||||
|
||||
addOptions() {
|
||||
return {
|
||||
HTMLAttributes: {
|
||||
class: "conditional-inline"
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
addAttributes() {
|
||||
return {
|
||||
conditionType: {
|
||||
default: "auth",
|
||||
parseHTML: (element) => element.getAttribute("data-condition-type"),
|
||||
renderHTML: (attributes) => ({
|
||||
"data-condition-type": attributes.conditionType
|
||||
})
|
||||
},
|
||||
conditionValue: {
|
||||
default: "authenticated",
|
||||
parseHTML: (element) => element.getAttribute("data-condition-value"),
|
||||
renderHTML: (attributes) => ({
|
||||
"data-condition-value": attributes.conditionValue
|
||||
})
|
||||
},
|
||||
showWhen: {
|
||||
default: "true",
|
||||
parseHTML: (element) => element.getAttribute("data-show-when"),
|
||||
renderHTML: (attributes) => ({
|
||||
"data-show-when": attributes.showWhen
|
||||
})
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
parseHTML() {
|
||||
return [
|
||||
{
|
||||
tag: "span.conditional-inline[data-condition-type]"
|
||||
}
|
||||
];
|
||||
},
|
||||
|
||||
renderHTML({ HTMLAttributes }) {
|
||||
return [
|
||||
"span",
|
||||
mergeAttributes(this.options.HTMLAttributes, HTMLAttributes),
|
||||
0
|
||||
];
|
||||
},
|
||||
|
||||
addCommands() {
|
||||
return {
|
||||
setConditionalInline:
|
||||
(attributes) =>
|
||||
({ commands }) => {
|
||||
return commands.setMark(this.name, attributes);
|
||||
},
|
||||
toggleConditionalInline:
|
||||
(attributes) =>
|
||||
({ commands }) => {
|
||||
return commands.toggleMark(this.name, attributes);
|
||||
},
|
||||
unsetConditionalInline:
|
||||
() =>
|
||||
({ commands }) => {
|
||||
return commands.unsetMark(this.name);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user