Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wrapping text around an image #40

Open
wants to merge 7 commits into
base: f/modal-draggable
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
21 changes: 18 additions & 3 deletions packages/common/src/components/Icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,12 @@ export const SubscriptIcon = () => (
)

export const AlignLeftIcon = () => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512" fill="currentColor">
<svg
width="1rem"
height="1rem"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 640 512"
fill="currentColor">
<path d="M16 208h416c8.801 0 16-7.199 16-15.1S440.8 176 432 176h-416C7.199 176 0 183.2 0 192S7.199 208 16 208zM16 80h256c8.801 0 16-7.199 16-15.1S280.8 48 272 48h-256C7.199 48 0 55.2 0 64S7.199 80 16 80zM16 336h256c8.801 0 16-7.199 16-15.1S280.8 304 272 304h-256C7.199 304 0 311.2 0 320S7.199 336 16 336zM432 432h-416c-8.801 0-16 7.199-16 16S7.199 464 16 464h416c8.801 0 16-7.199 16-15.1S440.8 432 432 432z" />
</svg>
)
Expand All @@ -268,13 +273,23 @@ export const AlignCenterIcon = () => (
)

export const AlignRightIcon = () => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512" fill="currentColor">
<svg
width="1rem"
height="1rem"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 640 512"
fill="currentColor">
<path d="M176 80h256c8.801 0 16-7.199 16-15.1S440.8 48 432 48h-256C167.2 48 160 55.2 160 64S167.2 80 176 80zM432 432h-416c-8.799 0-16 7.199-16 16S7.201 464 16 464h416c8.801 0 16-7.199 16-15.1S440.8 432 432 432zM432 176h-416C7.201 176 0 183.2 0 192S7.201 208 16 208h416c8.801 0 16-7.199 16-15.1S440.8 176 432 176zM432 304h-256C167.2 304 160 311.2 160 320S167.2 336 176 336h256c8.801 0 16-7.199 16-15.1S440.8 304 432 304z" />
</svg>
)

export const AlignJustifyIcon = () => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512" fill="currentColor">
<svg
width="1rem"
height="1rem"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 640 512"
fill="currentColor">
<path d="M432 432h-416c-8.801 0-16 7.199-16 16S7.199 464 16 464h416c8.801 0 16-7.199 16-15.1S440.8 432 432 432zM16 80h416c8.801 0 16-7.199 16-15.1S440.8 48 432 48h-416C7.199 48 0 55.2 0 64S7.199 80 16 80zM432 176h-416C7.199 176 0 183.2 0 192S7.199 208 16 208h416c8.801 0 16-7.199 16-15.1S440.8 176 432 176zM432 304h-416C7.199 304 0 311.2 0 320S7.199 336 16 336h416c8.801 0 16-7.199 16-15.1S440.8 304 432 304z" />
</svg>
)
Expand Down
23 changes: 15 additions & 8 deletions packages/core/src/DreifussWysiwygEditorDemo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,26 @@ import {DreifussWysiwygEditor} from './index'
import {CustomImageToolbarProps} from '@dreifuss-wysiwyg-editor/image-ui'

const value: any = [
{type: 'action_item', checked: true, children: [{text: 'Slide to the right.'}], id: 10054},
{type: 'action_item', children: [{text: 'Criss-cross.'}], id: 10055},
{type: 'action_item', checked: true, children: [{text: 'Slide to the right.'}]},
{type: 'action_item', children: [{text: 'Criss-cross.'}]},
{
type: 'paragraph',
children: [{type: 'link', url: 'http://google.com', children: [{text: 'Links: Add links.'}]}],
id: 10052
children: [
{text: ''},
{type: 'link', url: 'http://google.com', children: [{text: 'Links: Add links.'}]},
{text: ''}
]
},
{type: 'paragraph', children: [{text: ''}]},
{
type: 'paragraph',
children: [{text: 'Bold: Make the selected text bold.', code: true}],
id: 10055
type: 'img',
url: 'https://picsum.photos/500/300',
size: 'medium',
children: [{text: ''}],
align: 'right'
},

{type: 'paragraph', children: [{text: ''}]},
{type: 'paragraph', children: [{text: 'Bold: Make the selected text bold.', code: true}]},
{type: 'paragraph', children: [{text: 'Italic: Make the selected text italic.', italic: true}]},
{
type: 'paragraph',
Expand Down
2 changes: 1 addition & 1 deletion packages/image-ui/src/ImageElement/ImageElement.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export const getImageElementStyles = (props: ImageElementStyleProps) => {
`
],
optionsToolbarButton: [
tw`bg-transparent text-gray-300 hover:text-white`,
tw`bg-transparent text-gray-300 hover:text-white cursor-pointer`,
css`
border: 0px;
`
Expand Down
159 changes: 126 additions & 33 deletions packages/image-ui/src/ImageElement/ImageElement.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
import React, {ChangeEventHandler, Dispatch, useCallback, useEffect, useMemo, useState} from 'react'
import React, {
ChangeEventHandler,
Dispatch,
ReactNode,
useCallback,
useEffect,
useMemo,
useState
} from 'react'
import TextareaAutosize from 'react-textarea-autosize'
import {setNodes} from '@udecode/plate-common'
import {useEventEditorId, useStoreEditorState} from '@udecode/plate-core'
import {Node} from 'slate'
import {ReactEditor, useFocused, useSelected} from 'slate-react'
import {getImageElementStyles} from './ImageElement.styles'
import {ImageElementProps} from './ImageElement.types'
import {ImageSizeType} from '@dreifuss-wysiwyg-editor/image'
import {ImageSizeType, ImageAlignmentType} from '@dreifuss-wysiwyg-editor/image'
import {AlignRightIcon, AlignLeftIcon, AlignJustifyIcon} from '@dreifuss-wysiwyg-editor/common'

export const imageSizeMap = {
[ImageSizeType.fullScreen]: '100%',
Expand Down Expand Up @@ -38,13 +47,39 @@ const ImageSizeButton = ({
)
}

const ImageAlignButton = ({
icon: Icon,
onClick,
styles,
isActive,
disabled = false
}: {
icon: ReactNode
onClick: Dispatch<any>
styles: any
isActive: boolean
disabled?: boolean
}) => (
<button
style={{cursor: !disabled ? 'pointer' : 'auto'}}
disabled={disabled}
css={styles?.css}
className={`${styles?.className} ${isActive && `slate-ToolbarButton-active`}`}
onClick={onClick}>
<span style={{width: 20}}>
<Icon />
</span>
</button>
)

export const ImageElement = (props: ImageElementProps) => {
const {attributes, children, element, nodeProps, caption = {}, draggable} = props

const {placeholder = 'Write a caption...'} = caption

const {
url,
align,
size = ImageSizeType.large,
caption: nodeCaption = [{children: [{text: ''}]}]
} = element
Expand All @@ -54,11 +89,33 @@ export const ImageElement = (props: ImageElementProps) => {

const [imageSize, setImageSize] = useState<ImageSizeType>(size || ImageSizeType.large)

const [imageAlignment, setImageAlignment] = useState<ImageAlignmentType | undefined>(align)

const [disableAlignmentBtn, setDisableAlignmentBtn] = useState<boolean>(true)

useEffect(() => {
const path = ReactEditor.findPath(editor, element)
setNodes(editor, {align: imageAlignment}, {at: path})
}, [imageAlignment])

useEffect(() => {
const path = ReactEditor.findPath(editor, element)
setNodes(editor, {size: imageSize}, {at: path})
}, [imageSize])

/**
* If image size = large | screen,
* Then no need for alignment buttons or its prop in Node
* */
useEffect(() => {
if ([ImageSizeType.large, ImageSizeType.fullScreen].includes(imageSize)) {
setImageAlignment(undefined)
setDisableAlignmentBtn(true)
} else {
setDisableAlignmentBtn(false)
}
}, [imageSize])

const styles = getImageElementStyles({...props, focused, selected})

const onChangeCaption: ChangeEventHandler<HTMLTextAreaElement> = useCallback(
Expand All @@ -74,41 +131,77 @@ export const ImageElement = (props: ImageElementProps) => {
}, [nodeCaption])

return (
<div {...attributes} css={styles.root.css} className={styles.root.className}>
<div
{...attributes}
css={styles.root.css}
className={styles.root.className}
style={{
margin: 'auto 10px',
width: imageSizeMap[imageSize],
float: imageAlignment ?? undefined
}}>
<div contentEditable={false}>
<figure
css={styles.figure?.css}
className={`group ${styles.figure?.className}`}
style={{width: imageSizeMap[imageSize]}}>
<div css={styles.optionsToolbar.css} className={styles.optionsToolbar.className}>
<ImageSizeButton
type={ImageSizeType.fullScreen}
label="screen"
onClick={setImageSize}
styles={styles.optionsToolbarButton}
isActive={imageSize === ImageSizeType.fullScreen}
/>
<ImageSizeButton
type={ImageSizeType.large}
label="lg"
onClick={setImageSize}
styles={styles.optionsToolbarButton}
isActive={imageSize === ImageSizeType.large}
/>
<ImageSizeButton
type={ImageSizeType.medium}
label="md"
onClick={setImageSize}
styles={styles.optionsToolbarButton}
isActive={imageSize === ImageSizeType.medium}
/>
<ImageSizeButton
type={ImageSizeType.small}
label="sm"
onClick={setImageSize}
styles={styles.optionsToolbarButton}
isActive={imageSize === ImageSizeType.small}
/>
style={{width: '100%'}}>
<div style={{display: 'flex'}}>
<div css={styles.optionsToolbar.css} className={styles.optionsToolbar.className}>
<ImageSizeButton
type={ImageSizeType.fullScreen}
label="screen"
onClick={setImageSize}
styles={styles.optionsToolbarButton}
isActive={imageSize === ImageSizeType.fullScreen}
/>
<ImageSizeButton
type={ImageSizeType.large}
label="lg"
onClick={setImageSize}
styles={styles.optionsToolbarButton}
isActive={imageSize === ImageSizeType.large}
/>
<ImageSizeButton
type={ImageSizeType.medium}
label="md"
onClick={setImageSize}
styles={styles.optionsToolbarButton}
isActive={imageSize === ImageSizeType.medium}
/>
<ImageSizeButton
type={ImageSizeType.small}
label="sm"
onClick={setImageSize}
styles={styles.optionsToolbarButton}
isActive={imageSize === ImageSizeType.small}
/>
</div>
<div
style={{right: '16px'}}
css={styles.optionsToolbar.css}
className={styles.optionsToolbar.className}>
<ImageAlignButton
icon={AlignLeftIcon}
disabled={disableAlignmentBtn}
onClick={() => setImageAlignment('left')}
styles={styles.optionsToolbarButton}
isActive={imageAlignment === ImageAlignmentType.left}
/>
<ImageAlignButton
icon={AlignRightIcon}
disabled={disableAlignmentBtn}
onClick={() => setImageAlignment('right')}
styles={styles.optionsToolbarButton}
isActive={imageAlignment === ImageAlignmentType.right}
/>
<ImageAlignButton
icon={AlignJustifyIcon}
disabled={disableAlignmentBtn}
onClick={() => setImageAlignment(null)}
styles={styles.optionsToolbarButton}
isActive={imageAlignment === ImageAlignmentType.justify}
/>
</div>
</div>
<img
data-testid="ImageElementImage"
Expand Down
6 changes: 6 additions & 0 deletions packages/image/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,15 @@ export enum ImageSizeType {
small = 'small'
}

export enum ImageAlignmentType {
right = 'right',
left = 'left'
}

export interface ImageNodeData {
url: string
size: ImageSizeType
align?: ImageAlignmentType
caption?: TDescendant[]
}

Expand Down