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

editor localization #1351

Merged
merged 5 commits into from
Jun 25, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions packages/sn-editor-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"@babel/runtime": "^7.13.9",
"@material-ui/core": "^4.11.4",
"@material-ui/icons": "^4.11.2",
"@sensenet/client-utils": "^2.1.0",
"@tiptap/core": "^2.0.0-beta.73",
"@tiptap/extension-blockquote": "^2.0.0-beta.13",
"@tiptap/extension-bold": "^2.0.0-beta.13",
Expand Down
7 changes: 5 additions & 2 deletions packages/sn-editor-react/src/components/bubble-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import CloseIcon from '@material-ui/icons/Close'
import DeleteIcon from '@material-ui/icons/Delete'
import { Editor, posToDOMRect, BubbleMenu as TiptapBubbleMenu } from '@tiptap/react'
import React, { FC } from 'react'
import { useLocalization } from '../hooks'

const useStyles = makeStyles((theme) => {
return createStyles({
Expand All @@ -28,6 +29,8 @@ interface BubbleMenuProps {

export const BubbleMenu: FC<BubbleMenuProps> = (props) => {
const classes = useStyles()
const localization = useLocalization()

return (
<TiptapBubbleMenu
className={classes.root}
Expand Down Expand Up @@ -55,7 +58,7 @@ export const BubbleMenu: FC<BubbleMenuProps> = (props) => {
editor={props.editor}>
{props.editor.isActive('image') && (
<IconButton
aria-label="remove image"
aria-label={localization.bubbleMenu.removeImage}
size="small"
onClick={() => props.editor.chain().focus().deleteSelection().run()}>
<DeleteIcon fontSize="inherit" />
Expand All @@ -72,7 +75,7 @@ export const BubbleMenu: FC<BubbleMenuProps> = (props) => {
{props.editor.getMarkAttributes('link').href}
</Link>
<IconButton
aria-label="remove link"
aria-label={localization.bubbleMenu.removeLink}
size="small"
onClick={() => props.editor.chain().focus().unsetLink().run()}>
<CloseIcon fontSize="inherit" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
import ImageIcon from '@material-ui/icons/Image'
import { Editor } from '@tiptap/react'
import React, { FC, useRef, useState } from 'react'
import { useLocalization } from '../../hooks'

const useStyles = makeStyles(() => {
return createStyles({
Expand Down Expand Up @@ -42,6 +43,7 @@ export const ImageControl: FC<ImageControlProps> = ({ buttonProps, editor }) =>
const fileInput = useRef<HTMLInputElement>(null)

const classes = useStyles()
const localization = useLocalization()

const handleClickOpen = () => {
if (isUploadInProgress) {
Expand Down Expand Up @@ -91,7 +93,7 @@ export const ImageControl: FC<ImageControlProps> = ({ buttonProps, editor }) =>

return (
<>
<Tooltip title="Insert image">
<Tooltip title={localization.imageControl.title}>
<IconButton onClick={handleClickOpen} {...buttonProps}>
<ImageIcon />
</IconButton>
Expand Down Expand Up @@ -119,7 +121,7 @@ export const ImageControl: FC<ImageControlProps> = ({ buttonProps, editor }) =>
fileInput.current.value = ''
}
}}>
<DialogTitle id="form-dialog-title">Image insert</DialogTitle>
<DialogTitle id="form-dialog-title">{localization.imageControl.title}</DialogTitle>
<DialogContent>
{uploadedFile ? (
<>
Expand All @@ -133,14 +135,14 @@ export const ImageControl: FC<ImageControlProps> = ({ buttonProps, editor }) =>
)}
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>Cancel</Button>
<Button onClick={handleClose}>{localization.common.cancel}</Button>
<Button
onClick={() => {
handleClose()
addImage()
}}
color="primary">
Insert
{localization.imageControl.submit}
</Button>
</DialogActions>
</Dialog>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
import { Link } from '@material-ui/icons'
import { Editor } from '@tiptap/react'
import React, { FC, useState } from 'react'
import { useLocalization } from '../../hooks'

const useStyles = makeStyles(() => {
return createStyles({
Expand All @@ -35,6 +36,7 @@ export const LinkControl: FC<LinkControlProps> = ({ editor, buttonProps }) => {
const [link, setLink] = useState('')
const [inNewTab, setInNewTab] = useState(false)
const classes = useStyles()
const localization = useLocalization()

const handleClickOpen = () => {
if (editor.state.selection.empty) {
Expand Down Expand Up @@ -65,7 +67,7 @@ export const LinkControl: FC<LinkControlProps> = ({ editor, buttonProps }) => {

return (
<>
<Tooltip title="Link">
<Tooltip title={localization.menubar.link}>
<IconButton onClick={handleClickOpen} color={editor.isActive('link') ? 'primary' : 'default'} {...buttonProps}>
<Link />
</IconButton>
Expand All @@ -80,12 +82,12 @@ export const LinkControl: FC<LinkControlProps> = ({ editor, buttonProps }) => {
setLink('')
setInNewTab(false)
}}>
<DialogTitle id="link-control-title">Insert a link</DialogTitle>
<DialogTitle id="link-control-title">{localization.linkControl.title}</DialogTitle>
<DialogContent>
<TextField
autoFocus
margin="dense"
label="Url"
label={localization.linkControl.url}
type="url"
required
fullWidth
Expand All @@ -97,19 +99,19 @@ export const LinkControl: FC<LinkControlProps> = ({ editor, buttonProps }) => {
control={
<Checkbox checked={inNewTab} onChange={(event) => setInNewTab(event.target.checked)} color="primary" />
}
label="Open link in a new tab"
label={localization.linkControl.openInNewTab}
className={classes.inNewTab}
/>
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>Cancel</Button>
<Button onClick={handleClose}>{localization.common.cancel}</Button>
<Button
onClick={() => {
handleClose()
addLink()
}}
color="primary">
Insert
{localization.linkControl.submit}
</Button>
</DialogActions>
</Dialog>
Expand Down
14 changes: 9 additions & 5 deletions packages/sn-editor-react/src/components/editor.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createStyles, makeStyles } from '@material-ui/core'
import { EditorContent, EditorOptions, useEditor } from '@tiptap/react'
import React, { FC } from 'react'
import { LocalizationProvider, LocalizationType } from '../context'
import { createExtensions } from '../extension-list'
import { getCommonStyles } from '../styles'
import { BubbleMenu } from './bubble-menu'
Expand Down Expand Up @@ -68,6 +69,7 @@ interface EditorProps {
onChange?: EditorOptions['onUpdate']
readOnly?: boolean
placeholder?: string
localization?: Partial<LocalizationType>
}

export const Editor: FC<EditorProps> = (props) => {
Expand All @@ -89,10 +91,12 @@ export const Editor: FC<EditorProps> = (props) => {
})

return (
<div className={classes.root}>
<MenuBar editor={sensenetEditor} />
{sensenetEditor && <BubbleMenu editor={sensenetEditor} />}
<EditorContent editor={sensenetEditor} className={classes.editorWrapper} contentEditable={false} />
</div>
<LocalizationProvider localization={props.localization}>
<div className={classes.root}>
<MenuBar editor={sensenetEditor} />
{sensenetEditor && <BubbleMenu editor={sensenetEditor} />}
<EditorContent editor={sensenetEditor} className={classes.editorWrapper} contentEditable={false} />
</div>
</LocalizationProvider>
)
}
32 changes: 16 additions & 16 deletions packages/sn-editor-react/src/components/menu-bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@ import RedoIcon from '@material-ui/icons/Redo'
import UndoIcon from '@material-ui/icons/Undo'
import { Editor } from '@tiptap/react'
import React, { FC } from 'react'

import { useLocalization } from '../hooks'
import { getCommonStyles } from '../styles'

import { ImageControl, LinkControl, TypographyControl } from './controls'

const useStyles = makeStyles((theme) => {
Expand Down Expand Up @@ -61,6 +60,7 @@ interface MenuBarProps {

export const MenuBar: FC<MenuBarProps> = ({ editor }) => {
const classes = useStyles()
const localization = useLocalization()

if (!editor) {
return null
Expand All @@ -70,23 +70,23 @@ export const MenuBar: FC<MenuBarProps> = ({ editor }) => {
<div className={classes.root}>
<TypographyControl editor={editor} />
<div className={classes.divider} />
<Tooltip title="Bold (Ctrl + B)">
<Tooltip title={`${localization.menubar.bold} (Ctrl + B)`}>
<IconButton
onClick={() => editor.chain().focus().toggleBold().run()}
color={editor.isActive('bold') ? 'primary' : 'default'}
classes={{ root: classes.button, colorPrimary: classes.buttonPrimary }}>
<strong>B</strong>
</IconButton>
</Tooltip>
<Tooltip title="Italic (Ctrl + I)">
<Tooltip title={`${localization.menubar.italic} (Ctrl + I)`}>
<IconButton
onClick={() => editor.chain().focus().toggleItalic().run()}
color={editor.isActive('italic') ? 'primary' : 'default'}
classes={{ root: classes.button, colorPrimary: classes.buttonPrimary }}>
<em>I</em>
</IconButton>
</Tooltip>
<Tooltip title="Underline (Ctrl + U)">
<Tooltip title={`${localization.menubar.underline} (Ctrl + U)`}>
<IconButton
onClick={() => editor.chain().focus().toggleUnderline().run()}
color={editor.isActive('underline') ? 'primary' : 'default'}
Expand All @@ -95,15 +95,15 @@ export const MenuBar: FC<MenuBarProps> = ({ editor }) => {
</IconButton>
</Tooltip>
<div className={classes.divider} />
<Tooltip title="Block quote (Ctrl + Shift + B)">
<Tooltip title={`${localization.menubar.blockquote} (Ctrl + Shift + B)`}>
<IconButton
onClick={() => editor.chain().focus().toggleBlockquote().run()}
color={editor.isActive('blockquote') ? 'primary' : 'default'}
classes={{ root: classes.button, colorPrimary: classes.buttonPrimary }}>
<FormatQuoteIcon />
</IconButton>
</Tooltip>
<Tooltip title="Code (Ctrl + E)">
<Tooltip title={`${localization.menubar.code} (Ctrl + E)`}>
<IconButton
onClick={() => editor.chain().focus().toggleCode().run()}
color={editor.isActive('code') ? 'primary' : 'default'}
Expand All @@ -112,15 +112,15 @@ export const MenuBar: FC<MenuBarProps> = ({ editor }) => {
</IconButton>
</Tooltip>
<div className={classes.divider} />
<Tooltip title="Align left (Ctrl + Shift + L)">
<Tooltip title={`${localization.menubar.alignLeft} (Ctrl + Shift + L)`}>
<IconButton
onClick={() => editor.chain().focus().setTextAlign('left').run()}
color={editor.isActive({ textAlign: 'left' }) ? 'primary' : 'default'}
classes={{ root: classes.button, colorPrimary: classes.buttonPrimary }}>
<FormatAlignLeftIcon />
</IconButton>
</Tooltip>
<Tooltip title="Align center (Ctrl + Shift + E)">
<Tooltip title={`${localization.menubar.alignCenter} (Ctrl + Shift + E)`}>
<IconButton
onClick={() => {
editor.chain().focus().setTextAlign('center').run()
Expand All @@ -130,15 +130,15 @@ export const MenuBar: FC<MenuBarProps> = ({ editor }) => {
<FormatAlignCenterIcon />
</IconButton>
</Tooltip>
<Tooltip title="Align right (Ctrl + Shift + R)">
<Tooltip title={`${localization.menubar.alignRight} (Ctrl + Shift + R)`}>
<IconButton
onClick={() => editor.chain().focus().setTextAlign('right').run()}
color={editor.isActive({ textAlign: 'right' }) ? 'primary' : 'default'}
classes={{ root: classes.button, colorPrimary: classes.buttonPrimary }}>
<FormatAlignRightIcon />
</IconButton>
</Tooltip>
<Tooltip title="Align justify (Ctrl + Shift + J)">
<Tooltip title={`${localization.menubar.alignJustify} (Ctrl + Shift + J)`}>
<IconButton
onClick={() => editor.chain().focus().setTextAlign('justify').run()}
color={editor.isActive({ textAlign: 'justify' }) ? 'primary' : 'default'}
Expand All @@ -147,15 +147,15 @@ export const MenuBar: FC<MenuBarProps> = ({ editor }) => {
</IconButton>
</Tooltip>
<div className={classes.divider} />
<Tooltip title="Bullet list">
<Tooltip title={localization.menubar.bulletList}>
<IconButton
onClick={() => editor.chain().focus().toggleBulletList().run()}
className={editor.isActive('bulletList') ? 'is-active' : ''}
classes={{ root: classes.button, colorPrimary: classes.buttonPrimary }}>
<FormatListBulletedIcon />
</IconButton>
</Tooltip>
<Tooltip title="Ordered list">
<Tooltip title={localization.menubar.orderedList}>
<IconButton
onClick={() => editor.chain().focus().toggleOrderedList().run()}
color={editor.isActive('orderedList') ? 'primary' : 'default'}
Expand All @@ -171,7 +171,7 @@ export const MenuBar: FC<MenuBarProps> = ({ editor }) => {
editor={editor}
buttonProps={{ classes: { root: classes.button, colorPrimary: classes.buttonPrimary } }}
/>
<Tooltip title="Clear format">
<Tooltip title={localization.menubar.clearFormat}>
<IconButton
classes={{ root: classes.button, colorPrimary: classes.buttonPrimary }}
onClick={() => {
Expand All @@ -181,15 +181,15 @@ export const MenuBar: FC<MenuBarProps> = ({ editor }) => {
<FormatClearIcon />
</IconButton>
</Tooltip>
<Tooltip title="Undo (Ctrl + Z)">
<Tooltip title={`${localization.menubar.undo} (Ctrl + Z)`}>
<IconButton
onClick={() => editor.chain().focus().undo().run()}
disabled={!editor.can().undo()}
classes={{ root: classes.button, colorPrimary: classes.buttonPrimary }}>
<UndoIcon />
</IconButton>
</Tooltip>
<Tooltip title="Redo (Ctrl + Y)">
<Tooltip title={`${localization.menubar.redo} (Ctrl + Y)`}>
<IconButton
onClick={() => editor.chain().focus().redo().run()}
disabled={!editor.can().redo()}
Expand Down
1 change: 1 addition & 0 deletions packages/sn-editor-react/src/context/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './localization-context'
57 changes: 57 additions & 0 deletions packages/sn-editor-react/src/context/localization-context.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { deepMerge } from '@sensenet/client-utils'
import React, { createContext, FC, useEffect, useState } from 'react'

export const defaultLocalization = {
taki9 marked this conversation as resolved.
Show resolved Hide resolved
common: {
cancel: 'Cancel',
},
menubar: {
bold: 'Bold',
italic: 'Italic',
underline: 'Underline',
blockquote: 'Block quote',
code: 'Code',
alignLeft: 'Align left',
alignCenter: 'Align center',
alignRight: 'Align right',
alignJustify: 'Align justify',
taki9 marked this conversation as resolved.
Show resolved Hide resolved
bulletList: 'Bullet list',
orderedList: 'Ordered list',
link: 'Link',
clearFormat: 'Clear format',
undo: 'Undo',
redo: 'Redo',
},
bubbleMenu: {
removeImage: 'remove image',
removeLink: 'remove link',
},
imageControl: {
title: 'Insert image',
submit: 'Insert',
},
linkControl: {
title: 'Insert a link',
url: 'Url',
openInNewTab: 'Open link in a new tab',
submit: 'Insert',
},
}

export const LocalizationContext = createContext(defaultLocalization)

export type LocalizationType = typeof defaultLocalization

interface LocalizationProviderProps {
localization?: Partial<LocalizationType>
}

export const LocalizationProvider: FC<LocalizationProviderProps> = (props) => {
const [currentValue, setCurrentValue] = useState(deepMerge(defaultLocalization, props.localization))

useEffect(() => {
setCurrentValue(deepMerge(defaultLocalization, props.localization))
}, [props.localization])

return <LocalizationContext.Provider value={currentValue}>{props.children}</LocalizationContext.Provider>
}
1 change: 1 addition & 0 deletions packages/sn-editor-react/src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './use-localization'
Loading