first commit
Some checks failed
Build / run (push) Has been cancelled

This commit is contained in:
maher
2025-10-29 11:42:25 +01:00
commit 703f50a09d
4595 changed files with 385164 additions and 0 deletions

View File

@@ -0,0 +1,67 @@
import {Extension} from '@tiptap/core';
import '@tiptap/extension-text-style';
export type ColorOptions = {
types: string[];
};
declare module '@tiptap/core' {
interface Commands<ReturnType> {
bgColor: {
setBackgroundColor: (color: string) => ReturnType;
unsetBackgroundColor: () => ReturnType;
};
}
}
export const BackgroundColor = Extension.create<ColorOptions>({
name: 'backgroundColor',
addOptions() {
return {
types: ['textStyle'],
};
},
addGlobalAttributes() {
return [
{
types: this.options.types,
attributes: {
backgroundColor: {
default: null,
parseHTML: element =>
element.style.backgroundColor.replace(/['"]+/g, ''),
renderHTML: attributes => {
if (!attributes.backgroundColor) {
return {};
}
return {
style: `background-color: ${attributes.backgroundColor}`,
};
},
},
},
},
];
},
addCommands() {
return {
setBackgroundColor:
backgroundColor =>
({chain}) => {
return chain().setMark('textStyle', {backgroundColor}).run();
},
unsetBackgroundColor:
() =>
({chain}) => {
return chain()
.setMark('textStyle', {backgroundColor: null})
.removeEmptyTextStyle()
.run();
},
};
},
});

View File

@@ -0,0 +1,57 @@
import {mergeAttributes, Node} from '@tiptap/react';
declare module '@tiptap/core' {
interface Commands<ReturnType> {
embed: {
setEmbed: (options: {src: string}) => ReturnType;
};
}
}
export const Embed = Node.create({
name: 'embed',
group: 'block',
atom: true,
addAttributes() {
return {
allowfullscreen: {
default: null,
},
allow: {
default: 'autoplay; fullscreen; picture-in-picture',
},
src: {
default: null,
},
};
},
parseHTML() {
return [
{
tag: 'iframe',
},
];
},
renderHTML({HTMLAttributes}) {
return [
'iframe',
mergeAttributes(this.options.HTMLAttributes, HTMLAttributes),
];
},
addCommands() {
return {
setEmbed:
options =>
({commands}) => {
return commands.insertContent({
type: this.name,
attrs: options,
});
},
};
},
});

View File

@@ -0,0 +1,134 @@
import {Command, Extension} from '@tiptap/core';
import {AllSelection, TextSelection, Transaction} from 'prosemirror-state';
declare module '@tiptap/core' {
interface Commands<ReturnType> {
indent: {
indent: () => ReturnType;
outdent: () => ReturnType;
};
}
}
export const Indent = Extension.create({
name: 'indent',
addOptions: () => {
return {
types: ['listItem', 'paragraph'],
minLevel: 0,
maxLevel: 6,
};
},
addGlobalAttributes() {
return [
{
types: this.options.types,
attributes: {
indent: {
renderHTML: attributes => {
return attributes?.indent > this.options.minLevel
? {'data-indent': attributes.indent}
: null;
},
parseHTML: element => {
const level = Number(element.getAttribute('data-indent'));
return level && level > this.options.minLevel ? level : null;
},
},
},
},
];
},
addCommands() {
const setNodeIndentMarkup = (
tr: Transaction,
pos: number,
delta: number
): Transaction => {
const node = tr?.doc?.nodeAt(pos);
if (node) {
const nextLevel = (node.attrs.indent || 0) + delta;
const {minLevel, maxLevel} = this.options;
const indent =
// eslint-disable-next-line no-nested-ternary
nextLevel < minLevel
? minLevel
: nextLevel > maxLevel
? maxLevel
: nextLevel;
if (indent !== node.attrs.indent) {
const {indent: oldIndent, ...currentAttrs} = node.attrs;
const nodeAttrs =
indent > minLevel ? {...currentAttrs, indent} : currentAttrs;
return tr.setNodeMarkup(pos, node.type, nodeAttrs, node.marks);
}
}
return tr;
};
const updateIndentLevel = (tr: Transaction, delta: number): Transaction => {
const {doc, selection} = tr;
if (
doc &&
selection &&
(selection instanceof TextSelection ||
selection instanceof AllSelection)
) {
const {from, to} = selection;
doc.nodesBetween(from, to, (node, pos) => {
if (this.options.types.includes(node.type.name)) {
tr = setNodeIndentMarkup(tr, pos, delta);
return false;
}
return true;
});
}
return tr;
};
const applyIndent: (direction: number) => () => Command =
direction =>
() =>
({tr, state, dispatch}) => {
const {selection} = state;
tr = tr.setSelection(selection);
tr = updateIndentLevel(tr, direction);
if (tr.docChanged) {
dispatch?.(tr);
return true;
}
return false;
};
return {
indent: applyIndent(1),
outdent: applyIndent(-1),
};
},
addKeyboardShortcuts() {
return {
Tab: ({editor}) => {
if (editor.state.selection.to > editor.state.selection.from) {
return this.editor.commands.indent();
}
return false;
},
'Shift-Tab': ({editor}) => {
if (editor.state.selection.to > editor.state.selection.from) {
return this.editor.commands.outdent();
}
return false;
},
};
},
});

View File

@@ -0,0 +1,76 @@
import {mergeAttributes, Node} from '@tiptap/react';
declare module '@tiptap/core' {
interface Commands<ReturnType> {
important: {
addInfo: (attributes: {
type: 'important' | 'warning' | 'success';
}) => ReturnType;
};
}
}
export const InfoBlock = Node.create({
name: 'be-info-block',
group: 'block',
content: 'inline*',
defining: true,
addOptions() {
return {
types: ['important', 'warning', 'success'],
defaultType: 'success',
};
},
addAttributes() {
return {
type: {
default: this.options.defaultType,
parseHTML: element => {
for (const type of this.options.types) {
if (element.classList.contains(type)) {
return type;
}
}
return this.options.defaultType;
},
renderHTML: attrs => {
return {class: attrs.type};
},
},
};
},
parseHTML() {
return [
{
tag: 'div',
contentElement: 'p',
getAttrs: node =>
(node as HTMLElement).classList.contains('info-block') && null,
},
];
},
renderHTML({HTMLAttributes}) {
return [
'div',
mergeAttributes(HTMLAttributes, {
class: 'info-block',
}),
['div', {class: 'title'}, 'Important:'],
['p', 0],
];
},
addCommands() {
return {
addInfo:
attributes =>
({commands}) => {
return commands.setNode(this.name, attributes);
},
};
},
});