diff --git a/.changeset/flat-goats-confess.md b/.changeset/flat-goats-confess.md
new file mode 100644
index 00000000..5306e821
--- /dev/null
+++ b/.changeset/flat-goats-confess.md
@@ -0,0 +1,6 @@
+---
+"@genseki/react": patch
+---
+
+fix: rich text bug useEffect and z index
+fix: rich text link to use prompt instead of markdown
diff --git a/examples/ui-playground/src/components/slot-before.tsx b/examples/ui-playground/src/components/slot-before.tsx
index 74ac9bdf..29c3f786 100644
--- a/examples/ui-playground/src/components/slot-before.tsx
+++ b/examples/ui-playground/src/components/slot-before.tsx
@@ -19,6 +19,7 @@ import {
EditorBar,
EditorBgColorPicker,
EditorTextColorPicker,
+ LinkButton,
MarkButton,
RedoButton,
SelectionExtension,
@@ -37,7 +38,7 @@ export const EditorSlotBefore = () => {
-
+
diff --git a/packages/react/v2/components/compound/editor/components/editor-bar.tsx b/packages/react/v2/components/compound/editor/components/editor-bar.tsx
index 3f5b5b66..f709658d 100644
--- a/packages/react/v2/components/compound/editor/components/editor-bar.tsx
+++ b/packages/react/v2/components/compound/editor/components/editor-bar.tsx
@@ -19,7 +19,7 @@ export const EditorBar: React.FC<{ className?: string; children?: React.ReactNod
return (
diff --git a/packages/react/v2/components/compound/editor/components/index.ts b/packages/react/v2/components/compound/editor/components/index.ts
index 01cd1311..0561b084 100644
--- a/packages/react/v2/components/compound/editor/components/index.ts
+++ b/packages/react/v2/components/compound/editor/components/index.ts
@@ -1,5 +1,6 @@
export * from './editor-bar'
export * from './editor-color-picker'
+export * from './link-button'
export * from './mark-button'
export * from './redo-undo-buttons'
export * from './rich-text-editor'
diff --git a/packages/react/v2/components/compound/editor/components/link-button.tsx b/packages/react/v2/components/compound/editor/components/link-button.tsx
new file mode 100644
index 00000000..6c4231af
--- /dev/null
+++ b/packages/react/v2/components/compound/editor/components/link-button.tsx
@@ -0,0 +1,50 @@
+import { useCallback } from 'react'
+
+import { LinkIcon } from '@phosphor-icons/react'
+import { useCurrentEditor } from '@tiptap/react'
+
+import { ToolbarItem } from '@genseki/react'
+
+export const LinkButton = () => {
+ const { editor } = useCurrentEditor()
+ const isLinkActive = editor?.isActive('link') ?? false
+
+ const handleLinkToggle = useCallback(() => {
+ if (!editor) return
+
+ if (isLinkActive) {
+ editor.chain().focus().unsetLink().run()
+ return
+ }
+
+ const { from, to } = editor.state.selection
+ const selectedText = editor.state.doc.textBetween(from, to, '')
+
+ if (!selectedText) {
+ alert('Please select text first')
+ return
+ }
+
+ const url = prompt('Enter URL:', '')
+
+ if (!url) return
+
+ editor.chain().focus().extendMarkRange('link').setLink({ href: url }).run()
+ }, [editor, isLinkActive])
+
+ if (!editor) return null
+
+ return (
+
+
+
+ )
+}
diff --git a/packages/react/v2/components/compound/editor/components/mark-button.tsx b/packages/react/v2/components/compound/editor/components/mark-button.tsx
index 8bc6f5d8..b1798a7e 100644
--- a/packages/react/v2/components/compound/editor/components/mark-button.tsx
+++ b/packages/react/v2/components/compound/editor/components/mark-button.tsx
@@ -2,7 +2,6 @@
import {
type Icon,
- LinkIcon,
ListBulletsIcon,
TextBolderIcon,
TextItalicIcon,
@@ -13,7 +12,7 @@ import { useCurrentEditor } from '@tiptap/react'
import { ToolbarItem } from '../../../../../src/react/components/primitives/toolbar'
-type MarkType = 'bold' | 'italic' | 'underline' | 'strike' | 'bulletList' | 'link'
+type MarkType = 'bold' | 'italic' | 'underline' | 'strike' | 'bulletList'
type MarkOptions = Record<
MarkType,
@@ -66,22 +65,6 @@ const useMark = (type: MarkType) => {
editor.chain().focus().toggleBulletList().run()
},
},
- link: {
- label: 'Link',
- icon: LinkIcon,
- isSelected: editor.isActive('link'),
- onClick() {
- if (!editor.isActive('link')) {
- const { state } = editor
- const { from, to } = state.selection
- const url = state.doc.textBetween(from, to, '')
-
- editor.chain().focus().insertContent(`[](${url})`).run()
- return
- }
- editor.chain().focus().unsetMark('link').run()
- },
- },
}
return options[type]
@@ -97,7 +80,7 @@ export const MarkButton = (props: { type: MarkType }) => {
diff --git a/packages/react/v2/components/compound/editor/components/rich-text-editor.tsx b/packages/react/v2/components/compound/editor/components/rich-text-editor.tsx
index 90c3ad59..e4f1f914 100644
--- a/packages/react/v2/components/compound/editor/components/rich-text-editor.tsx
+++ b/packages/react/v2/components/compound/editor/components/rich-text-editor.tsx
@@ -37,8 +37,12 @@ export const RichTextEditor = (props: RichTextEditorProps) => {
if (props.value !== undefined && !isDeepEqual(editor.getJSON(), props.value)) {
editor.commands.setContent(props.value)
}
+ }, [editor, props.value])
+
+ useEffect(() => {
+ if (!editor) return
editor.setEditable(!props.isDisabled)
- }, [editor, props.value, props.isDisabled])
+ }, [editor, props.isDisabled])
return (
diff --git a/packages/react/v2/components/compound/editor/extensions/custom-link-extension.ts b/packages/react/v2/components/compound/editor/extensions/custom-link-extension.ts
index 01b790c6..6a3927d2 100644
--- a/packages/react/v2/components/compound/editor/extensions/custom-link-extension.ts
+++ b/packages/react/v2/components/compound/editor/extensions/custom-link-extension.ts
@@ -1,96 +1,15 @@
-import { InputRule, markInputRule, markPasteRule, PasteRule } from '@tiptap/core'
-import type { LinkOptions } from '@tiptap/extension-link'
-import { Link } from '@tiptap/extension-link'
+import Link from '@tiptap/extension-link'
-const inputRegex = /(?:^|\s)\[([^\]]*)?\]\((\S+)(?: ["“](.+)["”])?\)$/i
-
-const pasteRegex = /(?:^|\s)\[([^\]]*)?\]\((\S+)(?: ["“](.+)["”])?\)/gi
-
-function linkInputRule(config: Parameters[0]) {
- const defaultMarkInputRule = markInputRule(config)
-
- return new InputRule({
- find: config.find,
- handler(props) {
- const { tr } = props.state
-
- defaultMarkInputRule.handler(props)
- tr.setMeta('preventAutolink', true)
- },
- })
-}
-
-function linkPasteRule(config: Parameters[0]) {
- const defaultMarkPasteRule = markPasteRule(config)
-
- return new PasteRule({
- find: config.find,
- handler(props) {
- const { tr } = props.state
-
- defaultMarkPasteRule.handler(props)
- tr.setMeta('preventAutolink', true)
- },
- })
-}
-
-type CustomLinkExtensionOptions = LinkOptions
-
-const CustomLinkExtension = Link.extend({
+export const CustomLinkExtension = Link.extend({
inclusive: false,
- addOptions() {
- return {
- ...this.parent?.(),
- openOnClick: 'whenNotEditable',
- }
- },
- addAttributes() {
- return {
- ...this.parent?.(),
- title: {
- default: null,
- },
- }
- },
-
- addInputRules() {
- return [
- linkInputRule({
- find: inputRegex,
- type: this.type,
-
- // We need to use `pop()` to remove the last capture groups from the match to
- // satisfy Tiptap's `markPasteRule` expectation of having the content as the last
- // capture group in the match (this makes the attribute order important)
- getAttributes(match) {
- return {
- title: match.pop()?.trim(),
- href: match.pop()?.trim(),
- }
- },
- }),
- ]
- },
- addPasteRules() {
- return [
- linkPasteRule({
- find: pasteRegex,
- type: this.type,
-
- // We need to use `pop()` to remove the last capture groups from the match to
- // satisfy Tiptap's `markInputRule` expectation of having the content as the last
- // capture group in the match (this makes the attribute order important)
- getAttributes(match) {
- return {
- title: match.pop()?.trim(),
- href: match.pop()?.trim(),
- }
- },
- }),
- ]
- },
+}).configure({
+ openOnClick: false,
+ HTMLAttributes: {
+ class: 'custom-link',
+ rel: 'noopener noreferrer',
+ target: '_blank',
+ },
+ protocols: ['http', 'https', 'mailto', 'tel'],
+ autolink: true,
+ linkOnPaste: true,
})
-
-export { CustomLinkExtension }
-
-export type { CustomLinkExtensionOptions }