Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 135 additions & 18 deletions examples/erp/genseki/editor/slot-before.tsx
Original file line number Diff line number Diff line change
@@ -1,56 +1,173 @@
'use client'
import type React from 'react'

import Color from '@tiptap/extension-color'
import Link from '@tiptap/extension-link'
import TextAlign from '@tiptap/extension-text-align'
import TextStyle from '@tiptap/extension-text-style'
import Underline from '@tiptap/extension-underline'
import StarterKit from '@tiptap/starter-kit'

import {
EditorBar,
EditorBarGroup,
EditorBgColorPicker,
EditorTextColorPicker,
MarkButton,
RedoButton,
SelectTextStyle,
TextAlignButton,
TextAlignButtonsGroup,
ToggleGroup,
ToolbarGroup,
ToolbarSeparator,
UndoButton,
UploadImageButton,
} from '@genseki/react'
import {
BackColorExtension,
CustomImageExtension,
ImageUploadNodeExtension,
SelectionExtension,
} from '@genseki/react'

export const EditorSlotBefore = () => {
return (
<EditorBar>
<EditorBarGroup>
<EditorBgColorPicker />
<EditorTextColorPicker />
<SelectTextStyle />
</EditorBarGroup>
<EditorBarGroup className="items-center">
<UndoButton />
<RedoButton />
</EditorBarGroup>
<ToolbarSeparator className="h-auto" />
<SelectTextStyle />
<ToolbarGroup className="items-center">
<MarkButton type="bold" />
<MarkButton type="italic" />
<MarkButton type="underline" />
<MarkButton type="strike" />
<MarkButton type="link" />
</ToolbarGroup>
<ToolbarSeparator className="h-auto" />
<ToggleGroup className="items-center">
<MarkButton type="bulletList" />
</ToggleGroup>
<EditorTextColorPicker />
<ToolbarSeparator className="h-auto" />
<ToolbarGroup className="items-center">
<TextAlignButtonsGroup>
<TextAlignButton type="left" />
<TextAlignButton type="center" />
<TextAlignButton type="right" />
<TextAlignButton type="justify" />
<MarkButton type="bulletList" />
</TextAlignButtonsGroup>
</ToolbarGroup>
<ToolbarSeparator />
<ToolbarSeparator className="h-auto" />
<UploadImageButton />
<RedoButton />
<UndoButton />
</EditorBar>
)
}

export const postEditorProviderProps = {
immediatelyRender: false,
shouldRerenderOnTransaction: true,
content: {
type: 'doc',
content: [
{
type: 'paragraph',
content: [
{
type: 'text',
text: '',
},
],
},
],
},
slotBefore: <EditorSlotBefore />,
extensions: [
Color,
BackColorExtension,
Underline.configure({ HTMLAttributes: { class: 'earth-underline' } }),
SelectionExtension,
TextStyle,
TextAlign.configure({
types: ['heading', 'paragraph'],
alignments: ['left', 'center', 'right', 'justify'],
defaultAlignment: 'left',
}),
StarterKit.configure({
bold: { HTMLAttributes: { class: 'bold large-black' } },
paragraph: { HTMLAttributes: { class: 'paragraph-custom' } },
heading: { HTMLAttributes: { class: 'heading-custom' } },
bulletList: { HTMLAttributes: { class: 'list-custom' } },
orderedList: { HTMLAttributes: { class: 'ordered-list' } },
code: { HTMLAttributes: { class: 'code' } },
codeBlock: { HTMLAttributes: { class: 'code-block' } },
horizontalRule: { HTMLAttributes: { class: 'hr-custom' } },
italic: { HTMLAttributes: { class: 'italic-text' } },
strike: { HTMLAttributes: { class: 'strikethrough' } },
blockquote: { HTMLAttributes: { class: 'blockquote-custom' } },
}),
CustomImageExtension.configure({ HTMLAttributes: { className: 'image-displayer' } }),
ImageUploadNodeExtension.configure({
showProgress: false,
accept: 'image/*',
maxSize: 1024 * 1024 * 10, // 10MB
limit: 3,
pathName: 'posts/rich-text',
}),
Link.configure({
openOnClick: false,
autolink: true,
defaultProtocol: 'https',
protocols: ['http', 'https'],
isAllowedUri: (url, ctx) => {
try {
// construct URL
const parsedUrl = url.includes(':')
? new URL(url)
: new URL(`${ctx.defaultProtocol}://${url}`)

// use default validation
if (!ctx.defaultValidate(parsedUrl.href)) {
return false
}

// disallowed protocols
const disallowedProtocols = ['ftp', 'file', 'mailto']
const protocol = parsedUrl.protocol.replace(':', '')

if (disallowedProtocols.includes(protocol)) {
return false
}

// only allow protocols specified in ctx.protocols
const allowedProtocols = ctx.protocols.map((p) => (typeof p === 'string' ? p : p.scheme))

if (!allowedProtocols.includes(protocol)) {
return false
}

// disallowed domains
const disallowedDomains = ['example-phishing.com', 'malicious-site.net']
const domain = parsedUrl.hostname

if (disallowedDomains.includes(domain)) {
return false
}

// all checks have passed
return true
} catch {
return false
}
},
shouldAutoLink: (url) => {
try {
// construct URL
const parsedUrl = url.includes(':') ? new URL(url) : new URL(`https://${url}`)

// only auto-link if the domain is not in the disallowed list
const disallowedDomains = ['example-no-autolink.com', 'another-no-autolink.com']
const domain = parsedUrl.hostname

return !disallowedDomains.includes(domain)
} catch {
return false
}
},
}),
],
}
1 change: 1 addition & 0 deletions examples/erp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"@tanstack/react-table": "^8.21.3",
"@tiptap/extension-color": "^2.26.3",
"@tiptap/extension-image": "^2.26.3",
"@tiptap/extension-link": "2.26.3",
"@tiptap/extension-text-align": "^2.26.3",
"@tiptap/extension-text-style": "^2.26.3",
"@tiptap/extension-underline": "^2.26.3",
Expand Down
5 changes: 5 additions & 0 deletions examples/ui-playground/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@
"@phosphor-icons/react": "^2.1.8",
"@tailwindcss/postcss": "^4.1.7",
"@tanstack/react-query": "^5.71.5",
"@tiptap/extension-color": "^2.26.3",
"@tiptap/extension-link": "2.26.3",
"@tiptap/extension-text-align": "^2.26.3",
"@tiptap/extension-text-style": "^2.26.3",
"@tiptap/extension-underline": "^2.26.3",
"@tiptap/starter-kit": "^2.26.3",
"date-fns": "^4.1.0",
"next": "15.2.2",
Expand Down
Loading
Loading