Skip to content

Commit

Permalink
Merge pull request #39 from hunghg255/fix-image-extension
Browse files Browse the repository at this point in the history
  • Loading branch information
hunghg255 authored Sep 7, 2024
2 parents 2a45bb5 + 1fafca3 commit 3da1716
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 29 deletions.
2 changes: 1 addition & 1 deletion playground/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ const extensions = [
Excalidraw,
]

const DEFAULT = `<h1 style="text-align: center">Rich Text Editor</h1><p>A modern WYSIWYG rich text editor based on <a target="_blank" rel="noopener noreferrer nofollow" class="link" href="https://github.com/scrumpy/tiptap">tiptap</a> and <a target="_blank" rel="noopener noreferrer nofollow" class="link" href="https://ui.shadcn.com/">shadcn ui</a> for Reactjs</p><div class="excalidraw" defaultshowpicker="false" width="800" height="199" data="{&quot;elements&quot;:[{&quot;type&quot;:&quot;rectangle&quot;,&quot;version&quot;:15,&quot;versionNonce&quot;:1501503441,&quot;isDeleted&quot;:false,&quot;id&quot;:&quot;pjx5DYxD23xDHXQ85nOSw&quot;,&quot;fillStyle&quot;:&quot;cross-hatch&quot;,&quot;strokeWidth&quot;:2,&quot;strokeStyle&quot;:&quot;solid&quot;,&quot;roughness&quot;:1,&quot;opacity&quot;:100,&quot;angle&quot;:0,&quot;x&quot;:289.74224853515625,&quot;y&quot;:183.94967651367188,&quot;strokeColor&quot;:&quot;#e03131&quot;,&quot;backgroundColor&quot;:&quot;#ffc9c9&quot;,&quot;width&quot;:334.83209228515625,&quot;height&quot;:176.76864624023438,&quot;seed&quot;:1327620497,&quot;groupIds&quot;:[],&quot;frameId&quot;:null,&quot;roundness&quot;:{&quot;type&quot;:3},&quot;boundElements&quot;:[],&quot;updated&quot;:1725207092250,&quot;link&quot;:null,&quot;locked&quot;:false},{&quot;type&quot;:&quot;ellipse&quot;,&quot;version&quot;:18,&quot;versionNonce&quot;:1802132881,&quot;isDeleted&quot;:false,&quot;id&quot;:&quot;pHn8celdAeDKdhKVI6hCR&quot;,&quot;fillStyle&quot;:&quot;cross-hatch&quot;,&quot;strokeWidth&quot;:2,&quot;strokeStyle&quot;:&quot;solid&quot;,&quot;roughness&quot;:1,&quot;opacity&quot;:100,&quot;angle&quot;:0,&quot;x&quot;:706.022216796875,&quot;y&quot;:146.41253662109375,&quot;strokeColor&quot;:&quot;#e03131&quot;,&quot;backgroundColor&quot;:&quot;#ffc9c9&quot;,&quot;width&quot;:200.49169921875,&quot;height&quot;:213.36334228515625,&quot;seed&quot;:487390641,&quot;groupIds&quot;:[],&quot;frameId&quot;:null,&quot;roundness&quot;:{&quot;type&quot;:2},&quot;boundElements&quot;:[],&quot;updated&quot;:1725207094676,&quot;link&quot;:null,&quot;locked&quot;:false},{&quot;id&quot;:&quot;il96EMYE5vxuyhlTjVGCI&quot;,&quot;type&quot;:&quot;diamond&quot;,&quot;x&quot;:573.7800827026367,&quot;y&quot;:355.6528778076172,&quot;width&quot;:198.252685546875,&quot;height&quot;:149.70916748046875,&quot;angle&quot;:0,&quot;strokeColor&quot;:&quot;#1e1e1e&quot;,&quot;backgroundColor&quot;:&quot;transparent&quot;,&quot;fillStyle&quot;:&quot;solid&quot;,&quot;strokeWidth&quot;:2,&quot;strokeStyle&quot;:&quot;solid&quot;,&quot;roughness&quot;:1,&quot;opacity&quot;:100,&quot;groupIds&quot;:[],&quot;frameId&quot;:null,&quot;roundness&quot;:{&quot;type&quot;:2},&quot;seed&quot;:434826577,&quot;version&quot;:24,&quot;versionNonce&quot;:190824561,&quot;isDeleted&quot;:false,&quot;boundElements&quot;:null,&quot;updated&quot;:1725207125935,&quot;link&quot;:null,&quot;locked&quot;:false}],&quot;appState&quot;:{&quot;isLoading&quot;:false},&quot;files&quot;:{}}"></div><p style="text-align: center"></p><p style="text-align: center"><img height="auto" src="https://picsum.photos/1920/1080.webp?t=1" width="500"></p><p></p><div data-type="horizontalRule"><hr></div><h2>Demo</h2><p>👉<a target="_blank" rel="noopener noreferrer nofollow" class="link" href="https://reactjs-tiptap-editor.vercel.app/">Demo</a></p><h2>Features</h2><ul><li><p>Use <a target="_blank" rel="noopener noreferrer nofollow" class="link" href="https://ui.shadcn.com/">shadcn ui</a> components</p></li><li><p>Markdown support</p></li><li><p>TypeScript support</p></li><li><p>I18n support</p></li><li><p>React support</p></li><li><p>Slash Commands</p></li><li><p>Multi Column</p></li><li><p>TailwindCss</p></li><li><p>Support emoji</p></li><li><p>Support iframe</p></li></ul><h2>Installation</h2><pre><code class="language-bash">pnpm add reactjs-tiptap-editor</code></pre><p></p>`
const DEFAULT = `<h1 style="text-align: center">Rich Text Editor</h1><p>A modern WYSIWYG rich text editor based on <a target="_blank" rel="noopener noreferrer nofollow" class="link" href="https://github.com/scrumpy/tiptap">tiptap</a> and <a target="_blank" rel="noopener noreferrer nofollow" class="link" href="https://ui.shadcn.com/">shadcn ui</a> for Reactjs</p><div class="excalidraw" defaultshowpicker="false" width="800" height="199" data="{&quot;elements&quot;:[{&quot;type&quot;:&quot;rectangle&quot;,&quot;version&quot;:15,&quot;versionNonce&quot;:1501503441,&quot;isDeleted&quot;:false,&quot;id&quot;:&quot;pjx5DYxD23xDHXQ85nOSw&quot;,&quot;fillStyle&quot;:&quot;cross-hatch&quot;,&quot;strokeWidth&quot;:2,&quot;strokeStyle&quot;:&quot;solid&quot;,&quot;roughness&quot;:1,&quot;opacity&quot;:100,&quot;angle&quot;:0,&quot;x&quot;:289.74224853515625,&quot;y&quot;:183.94967651367188,&quot;strokeColor&quot;:&quot;#e03131&quot;,&quot;backgroundColor&quot;:&quot;#ffc9c9&quot;,&quot;width&quot;:334.83209228515625,&quot;height&quot;:176.76864624023438,&quot;seed&quot;:1327620497,&quot;groupIds&quot;:[],&quot;frameId&quot;:null,&quot;roundness&quot;:{&quot;type&quot;:3},&quot;boundElements&quot;:[],&quot;updated&quot;:1725207092250,&quot;link&quot;:null,&quot;locked&quot;:false},{&quot;type&quot;:&quot;ellipse&quot;,&quot;version&quot;:18,&quot;versionNonce&quot;:1802132881,&quot;isDeleted&quot;:false,&quot;id&quot;:&quot;pHn8celdAeDKdhKVI6hCR&quot;,&quot;fillStyle&quot;:&quot;cross-hatch&quot;,&quot;strokeWidth&quot;:2,&quot;strokeStyle&quot;:&quot;solid&quot;,&quot;roughness&quot;:1,&quot;opacity&quot;:100,&quot;angle&quot;:0,&quot;x&quot;:706.022216796875,&quot;y&quot;:146.41253662109375,&quot;strokeColor&quot;:&quot;#e03131&quot;,&quot;backgroundColor&quot;:&quot;#ffc9c9&quot;,&quot;width&quot;:200.49169921875,&quot;height&quot;:213.36334228515625,&quot;seed&quot;:487390641,&quot;groupIds&quot;:[],&quot;frameId&quot;:null,&quot;roundness&quot;:{&quot;type&quot;:2},&quot;boundElements&quot;:[],&quot;updated&quot;:1725207094676,&quot;link&quot;:null,&quot;locked&quot;:false},{&quot;id&quot;:&quot;il96EMYE5vxuyhlTjVGCI&quot;,&quot;type&quot;:&quot;diamond&quot;,&quot;x&quot;:573.7800827026367,&quot;y&quot;:355.6528778076172,&quot;width&quot;:198.252685546875,&quot;height&quot;:149.70916748046875,&quot;angle&quot;:0,&quot;strokeColor&quot;:&quot;#1e1e1e&quot;,&quot;backgroundColor&quot;:&quot;transparent&quot;,&quot;fillStyle&quot;:&quot;solid&quot;,&quot;strokeWidth&quot;:2,&quot;strokeStyle&quot;:&quot;solid&quot;,&quot;roughness&quot;:1,&quot;opacity&quot;:100,&quot;groupIds&quot;:[],&quot;frameId&quot;:null,&quot;roundness&quot;:{&quot;type&quot;:2},&quot;seed&quot;:434826577,&quot;version&quot;:24,&quot;versionNonce&quot;:190824561,&quot;isDeleted&quot;:false,&quot;boundElements&quot;:null,&quot;updated&quot;:1725207125935,&quot;link&quot;:null,&quot;locked&quot;:false}],&quot;appState&quot;:{&quot;isLoading&quot;:false},&quot;files&quot;:{}}"></div><p style="text-align: center"></p><div style="text-align: center;" class="image"><img height="auto" src="https://picsum.photos/1920/1080.webp?t=1" align="center" width="500"></div><div data-type="horizontalRule"><hr></div><h2>Demo</h2><p>👉<a target="_blank" rel="noopener noreferrer nofollow" class="link" href="https://reactjs-tiptap-editor.vercel.app/">Demo</a></p><h2>Features</h2><ul><li><p>Use <a target="_blank" rel="noopener noreferrer nofollow" class="link" href="https://ui.shadcn.com/">shadcn ui</a> components</p></li><li><p>Markdown support</p></li><li><p>TypeScript support</p></li><li><p>I18n support</p></li><li><p>React support</p></li><li><p>Slash Commands</p></li><li><p>Multi Column</p></li><li><p>TailwindCss</p></li><li><p>Support emoji</p></li><li><p>Support iframe</p></li></ul><h2>Installation</h2><pre><code class="language-bash">pnpm add reactjs-tiptap-editor</code></pre><p></p>`

function debounce(func: any, wait: number) {
let timeout: NodeJS.Timeout
Expand Down
6 changes: 3 additions & 3 deletions src/components/menus/bubble.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,9 @@ function imageAlignMenus(editor: Editor): BubbleMenuItem[] {
componentProps: {
tooltip: localeActions.t(`editor.textalign.${k}.tooltip`),
icon: iconMap[k],
action: () => editor.commands?.setTextAlign?.(k),
isActive: () => editor.isActive({ textAlign: k }) || false,
disabled: !editor.can()?.setTextAlign?.(k),
action: () => editor.commands?.setAlignImage?.(k),
isActive: () => editor.isActive({ align: k }) || false,
disabled: false,
},
}))
}
Expand Down
88 changes: 64 additions & 24 deletions src/extensions/Image/Image.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ export interface SetImageAttrsOptions {
title?: string
/** The width of the image. */
width?: number | string | null
/** The alignment of the image. */
align?: 'left' | 'center' | 'right'
}
declare module '@tiptap/core' {
interface Commands<ReturnType> {
Expand All @@ -26,15 +28,25 @@ declare module '@tiptap/core' {
* Update an image
*/
updateImage: (options: Partial<SetImageAttrsOptions>) => ReturnType
/**
* Set image alignment
*/
setAlignImage: (align: 'left' | 'center' | 'right') => ReturnType
}
}
}
export const Image = TiptapImage.extend({
inline() {
return true
},
group() {
return 'inline'
addOptions() {
return {
...this.parent?.(),
inline: false,
content: '',
marks: '',
group: 'block',
draggable: false,
selectable: true,
atom: true,
}
},
addAttributes() {
return {
Expand All @@ -51,14 +63,15 @@ export const Image = TiptapImage.extend({
}
},
},
}
},

addOptions() {
return {
...this.parent?.(),
inline: true,
upload: null,
align: {
default: 'left',
parseHTML: element => element.getAttribute('align'),
renderHTML: (attributes) => {
return {
align: attributes.align,
}
},
},
}
},

Expand All @@ -73,26 +86,53 @@ export const Image = TiptapImage.extend({
({ commands }) => {
return commands.updateAttributes(this.name, options)
},
setAlignImage:
align =>
({ commands }) => {
return commands.updateAttributes(this.name, { align })
},
}
},
renderHTML({ HTMLAttributes }) {
const { align } = HTMLAttributes

const style = align ? `text-align: ${align};` : ''
return [
'img',
mergeAttributes(
// Always render the `height="auto"
{
height: 'auto',
},
this.options.HTMLAttributes,
HTMLAttributes,
),
'div', // Parent element
{
style,
class: 'image',
},
[
'img',
mergeAttributes(
// Always render the `height="auto"`
{
height: 'auto',
},
this.options.HTMLAttributes,
HTMLAttributes,
),
],
]
},

parseHTML() {
return [
{
tag: 'img[src]',
tag: 'div[class=image]',
getAttrs: (element) => {
const img = element.querySelector('img')

const width = img?.getAttribute('width')

return {
src: img?.getAttribute('src'),
alt: img?.getAttribute('alt'),
title: img?.getAttribute('title'),
width: width ? Number.parseInt(width as string, 10) : null,
align: img?.getAttribute('align') || element.style.textAlign || null,
}
},
},
]
},
Expand Down
5 changes: 4 additions & 1 deletion src/extensions/Image/components/ImageView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,11 @@ function ImageView(props: any) {
dir: '',
})

const { align } = props?.node?.attrs

const imgAttrs = useMemo(() => {
const { src, alt, width: w, height: h } = props?.node?.attrs

const width = isNumber(w) ? `${w}px` : w
const height = isNumber(h) ? `${h}px` : h
return {
Expand Down Expand Up @@ -222,7 +225,7 @@ function ImageView(props: any) {
}, [props.editor.view.dom, resizeOb])

return (
<NodeViewWrapper className="image-view" style={{ ...imageMaxStyle, width: '100%' }}>
<NodeViewWrapper className="image-view" style={{ ...imageMaxStyle, width: '100%', textAlign: align }}>
<div
draggable="true"
data-drag-handle
Expand Down

0 comments on commit 3da1716

Please sign in to comment.