Skip to content

Commit

Permalink
Merge pull request #108 from hunghg255/feat-flip-image
Browse files Browse the repository at this point in the history
  • Loading branch information
hunghg255 authored Nov 9, 2024
2 parents 1ef27db + 1d46b59 commit 93a8fbb
Show file tree
Hide file tree
Showing 13 changed files with 694 additions and 356 deletions.
1 change: 1 addition & 0 deletions docs/guide/bubble-menu.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ The system provides the following default bubble menus:
| ContentMenu | Provides general content-related operations like copy, paste, delete, etc. | floatingMenuConfig |
| BubbleMenuImageGif | Provides general content-related operations like copy, paste, delete, image gif etc. | imageGifConfig |
| BubbleMenuMermaid | Provides general content-related operations like copy, paste, delete, mermaid etc. | mermaidConfig |
| BubbleMenuTwitter | Provides general content-related operations like copy, paste, delete, twitter etc. | twitterConfig |

## Disabling the Bubble Menu

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@
"homepage": "https://reactjs-tiptap-editor.vercel.app/",
"repository": {
"type": "git",
"url": "https://github.com/hunghg255/reactjs-tiptap-editor.git"
"url": "git+https://github.com/hunghg255/reactjs-tiptap-editor.git"
},
"bugs": "https://github.com/hunghg255/reactjs-tiptap-editor/issues"
}
2 changes: 1 addition & 1 deletion playground/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ const extensions = [
Twitter,
]

const DEFAULT = ``
const DEFAULT = `<p dir="auto"></p><p dir="auto"></p><p dir="auto"></p><p dir="auto"><div style="text-align: center;" class="image"><img height="auto" style="transform: rotateX(0deg) rotateY(180deg);" src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729198819038/684c0adb-b189-4af8-b9d8-d26e4097ce27.png?auto=compress,format&amp;format=webp" flipx="false" flipy="true" align="center" inline="false"></div></p><p dir="auto"></p><p dir="auto"></p><p dir="auto"></p>`

function debounce(func: any, wait: number) {
let timeout: NodeJS.Timeout
Expand Down
935 changes: 594 additions & 341 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions src/components/icons/icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import {
CropIcon,
Eraser,
Eye,
FlipHorizontal,
FlipVertical,
Frame,
GripVertical,
Heading1,
Expand Down Expand Up @@ -201,4 +203,6 @@ export const icons = {
Crop: CropIcon,
Mermaid,
Twitter,
FlipX: FlipVertical,
FlipY: FlipHorizontal,
} as any
42 changes: 40 additions & 2 deletions src/components/menus/bubble.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@ function imageGifSizeMenus(editor: Editor): BubbleMenuItem[] {
componentProps: {
tooltip: localeActions.t(`editor.${size.replace('-', '.')}.tooltip` as any),
icon: icons[i],
// @ts-expect-error
action: () => editor.commands.updateImageGif({ width: IMAGE_SIZE[size] }),
isActive: () => editor.isActive('image', { width: IMAGE_SIZE[size] }),
},
Expand Down Expand Up @@ -146,7 +145,6 @@ function imageGifAlignMenus(editor: Editor): BubbleMenuItem[] {
componentProps: {
tooltip: localeActions.t(`editor.textalign.${k}.tooltip`),
icon: iconMap[k],
// @ts-expect-error
action: () => editor.commands?.setAlignImageGif?.(k),
isActive: () => editor.isActive({ align: k }) || false,
disabled: false,
Expand Down Expand Up @@ -195,6 +193,46 @@ function videoSizeMenus(editor: Editor): BubbleMenuItem[] {
}
export function getBubbleImage(editor: Editor): BubbleMenuItem[] {
return [
{
type: 'flipX',
component: ActionButton,
componentProps: {
editor,
tooltip: localeActions.t('editor.tooltip.flipX'),
icon: 'FlipX',
action: () => {
const image = editor.getAttributes('image')
const { flipX } = image as any
editor
.chain()
.focus(undefined, { scrollIntoView: false })
.updateImage({
flipX: !flipX,
})
.run()
},
},
},
{
type: 'flipY',
component: ActionButton,
componentProps: {
editor,
tooltip: localeActions.t('editor.tooltip.flipY'),
icon: 'FlipY',
action: () => {
const image = editor.getAttributes('image')
const { flipY } = image as any
editor
.chain()
.focus(undefined, { scrollIntoView: false })
.updateImage({
flipY: !flipY,
})
.run()
},
},
},
...imageSizeMenus(editor),
...imageAlignMenus(editor),
{
Expand Down
34 changes: 28 additions & 6 deletions src/extensions/Image/Image.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,12 @@ export interface SetImageAttrsOptions {
width?: number | string | null
/** The alignment of the image. */
align?: 'left' | 'center' | 'right'

/** Whether the image is inline. */
inline?: boolean
/** image FlipX */
flipX?: boolean
/** image FlipY */
flipY?: boolean
}

const DEFAULT_OPTIONS: any = {
Expand Down Expand Up @@ -94,6 +98,12 @@ export const Image = TiptapImage.extend<IImageOptions>({
addAttributes() {
return {
...this.parent?.(),
flipX: {
default: false,
},
flipY: {
default: false,
},
width: {
default: null,
parseHTML: (element) => {
Expand Down Expand Up @@ -152,22 +162,25 @@ export const Image = TiptapImage.extend<IImageOptions>({
}
},
renderHTML({ HTMLAttributes }) {
const { align, inline } = HTMLAttributes
const { flipX, flipY, align, inline } = HTMLAttributes

const transformStyle
= flipX || flipY ? `transform: rotateX(${flipX ? '180' : '0'}deg) rotateY(${flipY ? '180' : '0'}deg);` : ''

const style = align ? `text-align: ${align};` : ''
const textAlignStyle = align ? `text-align: ${align};` : ''

return [
inline ? 'span' : 'div', // Parent element
inline ? 'span' : 'div',
{
style,
style: textAlignStyle,
class: 'image',
},
[
'img',
mergeAttributes(
// Always render the `height="auto"`
{
height: 'auto',
style: transformStyle,
},
this.options.HTMLAttributes,
HTMLAttributes,
Expand All @@ -184,13 +197,18 @@ export const Image = TiptapImage.extend<IImageOptions>({

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

const flipX = img?.getAttribute('flipx') || false
const flipY = img?.getAttribute('flipy') || false

return {
src: img?.getAttribute('src'),
alt: img?.getAttribute('alt'),
caption: img?.getAttribute('caption'),
width: width ? Number.parseInt(width as string, 10) : null,
align: img?.getAttribute('align') || element?.style?.textAlign || null,
inline: img?.getAttribute('inline') || false,
flipX: flipX === 'true',
flipY: flipY === 'true',
}
},
},
Expand All @@ -200,6 +218,8 @@ export const Image = TiptapImage.extend<IImageOptions>({
const img = element.querySelector('img')

const width = img?.getAttribute('width')
const flipX = img?.getAttribute('flipx') || false
const flipY = img?.getAttribute('flipy') || false

return {
src: img?.getAttribute('src'),
Expand All @@ -208,6 +228,8 @@ export const Image = TiptapImage.extend<IImageOptions>({
width: width ? Number.parseInt(width as string, 10) : null,
align: img?.getAttribute('align') || element.style.textAlign || null,
inline: img?.getAttribute('inline') || false,
flipX: flipX === 'true',
flipY: flipY === 'true',
}
},
},
Expand Down
11 changes: 10 additions & 1 deletion src/extensions/Image/components/ImageView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,25 @@ function ImageView(props: any) {
const { align, inline } = props?.node?.attrs

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

const width = isNumber(w) ? `${w}px` : w
const height = isNumber(h) ? `${h}px` : h
const transformStyles: any = []

if (flipX)
transformStyles.push('rotateX(180deg)')
if (flipY)
transformStyles.push('rotateY(180deg)')
const transform = transformStyles.join(' ')

return {
src: src || undefined,
alt: alt || undefined,
style: {
width: width || undefined,
height: height || undefined,
transform: transform || 'none',
},
}
}, [props?.node?.attrs])
Expand Down
2 changes: 2 additions & 0 deletions src/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,8 @@ const locale = {
'editor.replace.caseSensitive': 'Case Sensitive',
'editor.mermaid.tooltip': 'Mermaid',
'editor.twitter.tooltip': 'Twitter',
'editor.tooltip.flipX': 'Flip Horizontal',
'editor.tooltip.flipY': 'Flip Vertical',
}

export default locale
2 changes: 2 additions & 0 deletions src/locales/pt-br.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@ const locale = {
'editor.replace.caseSensitive': 'Sensível a maiúsculas e minúsculas',
'editor.mermaid.tooltip': 'Mermaid',
'editor.twitter.tooltip': 'Twitter',
'editor.tooltip.flipX': 'Inverter Horizontal',
'editor.tooltip.flipY': 'Inverter Vertical',
}

export default locale
2 changes: 2 additions & 0 deletions src/locales/vi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@ const locale = {
'editor.replace.caseSensitive': 'Phân biệt chữ hoa chữ thường',
'editor.mermaid.tooltip': 'Mermaid',
'editor.twitter.tooltip': 'Twitter',
'editor.tooltip.flipX': 'Lật Ngang',
'editor.tooltip.flipY': 'Lật Dọc',
}

export default locale
2 changes: 2 additions & 0 deletions src/locales/zh-cn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@ const locale = {
'editor.replace.caseSensitive': '区分大小写',
'editor.mermaid.tooltip': 'Mermaid',
'editor.twitter.tooltip': 'Twitter',
'editor.tooltip.flipX': '水平翻转',
'editor.tooltip.flipY': '垂直翻转',
}

export default locale
11 changes: 7 additions & 4 deletions src/styles/editor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@

&--focused:hover,
&--resizing:hover {
outline-color: transparent;
outline-color: hsl(var(--primary));
}

&__placeholder {
Expand All @@ -164,13 +164,16 @@
}
}

.image-view__body--focused {
outline-color: hsl(var(--primary)) !important;
}

&.focus {
img {
@apply richtext-outline-primary richtext-outline-2 richtext-outline;
}
}


img {
display: inline;
vertical-align: baseline;
Expand All @@ -188,7 +191,7 @@
height: 100%;
@apply richtext-border !important;
@apply richtext-border-border !important;
border-style: dashed;


&__handler {
position: absolute;
Expand All @@ -199,7 +202,7 @@
height: 12px;
border: 1px solid #fff;
border-radius: 2px;
@apply richtext-bg-blue-500;
background-color: hsl(var(--primary));

&--tl {
top: -6px;
Expand Down

0 comments on commit 93a8fbb

Please sign in to comment.