From e094e172cb1a91bf280f00ee1ba9cde9b0de01ad Mon Sep 17 00:00:00 2001 From: Viktor Nagy Date: Wed, 27 Mar 2024 13:38:16 +0100 Subject: [PATCH 01/14] fix: downloaded file should be shown on a new page --- packages/sn-hooks-react/src/hooks/use-download.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/sn-hooks-react/src/hooks/use-download.ts b/packages/sn-hooks-react/src/hooks/use-download.ts index a5a05019d..7bd80b564 100644 --- a/packages/sn-hooks-react/src/hooks/use-download.ts +++ b/packages/sn-hooks-react/src/hooks/use-download.ts @@ -10,6 +10,7 @@ export const fakeClick = (obj: EventTarget) => { export const downloadFile = (name: string, repositoryUrl: string) => { const saveLink = document.createElement('a') saveLink.href = `${repositoryUrl}${name}?download` + saveLink.target = '_blank'; fakeClick(saveLink) } From cf3dd2094a5c51bf8bf65c4e0f3bc58b35ecb28c Mon Sep 17 00:00:00 2001 From: Viktor Nagy Date: Wed, 27 Mar 2024 13:39:56 +0100 Subject: [PATCH 02/14] mod: user path should be shown next to the group/user identifier --- .../sensenet/src/components/view-controls/permission-view.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/sensenet/src/components/view-controls/permission-view.tsx b/apps/sensenet/src/components/view-controls/permission-view.tsx index c04fac43f..a451abb8f 100644 --- a/apps/sensenet/src/components/view-controls/permission-view.tsx +++ b/apps/sensenet/src/components/view-controls/permission-view.tsx @@ -292,7 +292,7 @@ export const PermissionView: React.FC = (props) => { - {inheritedEntry.identity.displayName} + {inheritedEntry.identity.displayName} ({inheritedEntry.identity.path}) = (props) => { - {setOnThisEntry.identity.displayName} + {setOnThisEntry.identity.displayName} ({setOnThisEntry.identity.path}) {!setOnThisEntry.propagates && ( From 46ab66607a510cb4bf55462c16c85a06165cb7cf Mon Sep 17 00:00:00 2001 From: Viktor Nagy Date: Tue, 16 Apr 2024 10:23:49 +0200 Subject: [PATCH 03/14] mod: renamed configuration to settings, and settings to system --- apps/sensenet/src/application-paths.ts | 44 ++++++++-------- .../src/components/settings/index.tsx | 8 +-- .../src/components/settings/setup.tsx | 2 +- apps/sensenet/src/hooks/use-drawer-items.tsx | 52 +++++++++---------- apps/sensenet/src/localization/default.ts | 2 + .../sensenet/src/services/PersonalSettings.ts | 10 ++-- 6 files changed, 60 insertions(+), 58 deletions(-) diff --git a/apps/sensenet/src/application-paths.ts b/apps/sensenet/src/application-paths.ts index caae61c9e..0d640e2ba 100644 --- a/apps/sensenet/src/application-paths.ts +++ b/apps/sensenet/src/application-paths.ts @@ -13,11 +13,11 @@ export const PATHS = { content: { appPath: '/content/:browseType/:action?', snPath: '/Root/Content' }, contentTemplates: { appPath: '/content-templates/:browseType/:action?', snPath: '/Root/ContentTemplates' }, custom: { appPath: '/custom/:browseType/:path/:action?' }, - configuration: { appPath: '/settings/configuration/:action?', snPath: '/Root/System/Settings' }, - localization: { appPath: '/settings/localization/:action?', snPath: '/Root/Localization' }, - webhooks: { appPath: '/settings/webhooks/:action?', snPath: '/Root/System/WebHooks' }, - settings: { appPath: '/settings/:submenu?' }, - apiKeys: { appPath: '/settings/apikeys' }, + configuration: { appPath: '/system/settings/:action?', snPath: '/Root/System/Settings' }, + localization: { appPath: '/system/localization/:action?', snPath: '/Root/Localization' }, + webhooks: { appPath: '/system/webhooks/:action?', snPath: '/Root/System/WebHooks' }, + settings: { appPath: '/system/:submenu?' }, + apiKeys: { appPath: '/system/apikeys' }, } as const type SettingsItemType = 'stats' | 'apikeys' | 'webhooks' | 'adminui' @@ -30,28 +30,28 @@ type RoutesWithContentBrowser = keyof Pick< type RoutesWithActionParam = keyof Pick type Options = - | { path: (typeof PATHS)['events']['appPath']; params?: { eventGuid: string; [index: string]: string } } + | { path: (typeof PATHS)['events']['appPath']; params?: { eventGuid: string;[index: string]: string } } | { - path: (typeof PATHS)[RoutesWithContentBrowser]['appPath'] - params: { browseType: (typeof BrowseType)[number]; action?: string; [index: string]: string | undefined } - } + path: (typeof PATHS)[RoutesWithContentBrowser]['appPath'] + params: { browseType: (typeof BrowseType)[number]; action?: string;[index: string]: string | undefined } + } | { - path: (typeof PATHS)['custom']['appPath'] - params: { - browseType: (typeof BrowseType)[number] - path: string - action?: string - [index: string]: string | undefined - } + path: (typeof PATHS)['custom']['appPath'] + params: { + browseType: (typeof BrowseType)[number] + path: string + action?: string + [index: string]: string | undefined } + } | { - path: (typeof PATHS)[RoutesWithActionParam]['appPath'] - params?: { action: string; [index: string]: string } - } + path: (typeof PATHS)[RoutesWithActionParam]['appPath'] + params?: { action: string;[index: string]: string } + } | { - path: (typeof PATHS)['settings']['appPath'] - params?: { submenu: SettingsItemType; [index: string]: string | SettingsItemType } - } + path: (typeof PATHS)['settings']['appPath'] + params?: { submenu: SettingsItemType;[index: string]: string | SettingsItemType } + } export const resolvePathParams = ({ path, params }: Options) => { let currentPath: string = path diff --git a/apps/sensenet/src/components/settings/index.tsx b/apps/sensenet/src/components/settings/index.tsx index 22d42ea8f..ca0cb12b9 100644 --- a/apps/sensenet/src/components/settings/index.tsx +++ b/apps/sensenet/src/components/settings/index.tsx @@ -53,8 +53,8 @@ export const Settings: React.FunctionComponent = () => { const settingsItems = [ { - name: 'configuration', - displayName: localizationDrawer.titles.Configuration, + name: 'settings', + displayName: localizationDrawer.titles.Settings, url: resolvePathParams({ path: PATHS.configuration.appPath }), }, { @@ -83,7 +83,7 @@ export const Settings: React.FunctionComponent = () => { switch (routeMatch.params.submenu) { case 'localization': return - case 'configuration': + case 'settings': return case 'adminui': return @@ -117,7 +117,7 @@ export const Settings: React.FunctionComponent = () => { return (
- {localizationDrawer.titles.Settings} + {localizationDrawer.titles.System}
diff --git a/apps/sensenet/src/components/settings/setup.tsx b/apps/sensenet/src/components/settings/setup.tsx index c3a88df3b..495e78e93 100644 --- a/apps/sensenet/src/components/settings/setup.tsx +++ b/apps/sensenet/src/components/settings/setup.tsx @@ -128,7 +128,7 @@ const Setup = () => { return (
- {localizationDrawerTitles.Configuration} + {localizationDrawerTitles.Settings}
{renderContent()}
diff --git a/apps/sensenet/src/hooks/use-drawer-items.tsx b/apps/sensenet/src/hooks/use-drawer-items.tsx index 558ca0978..b53e80ab9 100644 --- a/apps/sensenet/src/hooks/use-drawer-items.tsx +++ b/apps/sensenet/src/hooks/use-drawer-items.tsx @@ -89,7 +89,7 @@ export const useDrawerItems = () => { systemItem: true, }, { - itemType: 'Settings', + itemType: 'System', systemItem: true, }, ], @@ -131,7 +131,7 @@ export const useDrawerItems = () => { return case 'CustomContent': return item.settings?.icon ? : - case 'Settings': + case 'System': return // no default } @@ -174,7 +174,7 @@ export const useDrawerItems = () => { path: (item as CustomContentDrawerItem).settings?.appPath || '', }, }) - case 'Settings': + case 'System': return resolvePathParams({ path: PATHS.settings.appPath, params: { submenu: 'stats' } }) default: return '/' @@ -194,32 +194,32 @@ export const useDrawerItems = () => { return drawerItem } - ;[...settings.drawer.items, ...builtInDrawerItems] - .filterAsync(async (item) => { - if (!item.permissions?.length) { - return true - } + ;[...settings.drawer.items, ...builtInDrawerItems] + .filterAsync(async (item) => { + if (!item.permissions?.length) { + return true + } - try { - for (const permission of item.permissions) { - const actions = await repo.getActions({ idOrPath: permission.path }) - const actionIndex = actions.d.results.findIndex((action) => action.Name === permission.action) - if (actionIndex === -1 || actions.d.results[actionIndex].Forbidden) { - return false + try { + for (const permission of item.permissions) { + const actions = await repo.getActions({ idOrPath: permission.path }) + const actionIndex = actions.d.results.findIndex((action) => action.Name === permission.action) + if (actionIndex === -1 || actions.d.results[actionIndex].Forbidden) { + return false + } } + } catch (error) { + logger.debug({ + message: error.message, + data: { + error, + }, + }) + return false } - } catch (error) { - logger.debug({ - message: error.message, - data: { - error, - }, - }) - return false - } - return true - }) - .then((items) => setDrawerItems(items.map(getItemFromSettings))) + return true + }) + .then((items) => setDrawerItems(items.map(getItemFromSettings))) }, [ localization.descriptions, localization.titles, diff --git a/apps/sensenet/src/localization/default.ts b/apps/sensenet/src/localization/default.ts index e169886d2..238588fa1 100644 --- a/apps/sensenet/src/localization/default.ts +++ b/apps/sensenet/src/localization/default.ts @@ -128,6 +128,7 @@ const values = { Settings: 'Settings', Configuration: 'Configuration', Stats: 'Stats', + System: 'System', ApiAndSecurity: 'Api and Security', Webhooks: 'Webhooks', AdminUiCustomization: 'Admin-ui customization', @@ -143,6 +144,7 @@ const values = { UsersAndGroups: 'Manage users and groups, roles and identities', CustomContent: 'Explore and manage your content from the configured path', Settings: 'Configure the sensenet system', + System: 'Configure the sensenet system', ContentTemplates: 'Manage content templates', }, personalSettingsTitle: 'Edit personal settings', diff --git a/apps/sensenet/src/services/PersonalSettings.ts b/apps/sensenet/src/services/PersonalSettings.ts index 315ffe892..30a3a830c 100644 --- a/apps/sensenet/src/services/PersonalSettings.ts +++ b/apps/sensenet/src/services/PersonalSettings.ts @@ -29,7 +29,7 @@ export const BuiltInDrawerItemType = tuple( 'SavedQueries', 'Trash', 'UsersAndGroups', - 'Settings', + 'System', 'ContentTemplates', ) @@ -149,19 +149,19 @@ export const defaultSettings: PersonalSettingsType = { export class PersonalSettings { private checkDrawerItems(settings: Partial): Partial { if (settings.default?.drawer?.items?.find((i) => typeof i === 'string')) { - ;(settings.default.drawer.items as any) = undefined + ; (settings.default.drawer.items as any) = undefined } if (settings.desktop?.drawer?.items?.find((i) => typeof i === 'string')) { - ;(settings.desktop.drawer.items as any) = undefined + ; (settings.desktop.drawer.items as any) = undefined } if (settings.tablet?.drawer?.items?.find((i) => typeof i === 'string')) { - ;(settings.tablet.drawer.items as any) = undefined + ; (settings.tablet.drawer.items as any) = undefined } if (settings.mobile?.drawer?.items?.find((i) => typeof i === 'string')) { - ;(settings.mobile.drawer.items as any) = undefined + ; (settings.mobile.drawer.items as any) = undefined } return settings From 7b54885a60c9fae48bc59cd92cc6d1b14331d477 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81d=C3=A1m=20Hassan?= Date: Thu, 9 May 2024 16:00:55 +0200 Subject: [PATCH 04/14] feautre: html edit button --- .../src/components/react-control-mapper.ts | 2 + .../src/fieldcontrols/rich-text-editor.tsx | 7 + .../controls/html-editor-control.tsx | 155 ++++++++++++++++++ .../src/components/controls/index.ts | 1 + .../src/components/menu-bar.tsx | 6 +- .../src/context/localization-context.tsx | 1 + 6 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 packages/sn-editor-react/src/components/controls/html-editor-control.tsx diff --git a/apps/sensenet/src/components/react-control-mapper.ts b/apps/sensenet/src/components/react-control-mapper.ts index eb73c9419..8b4166bef 100644 --- a/apps/sensenet/src/components/react-control-mapper.ts +++ b/apps/sensenet/src/components/react-control-mapper.ts @@ -39,6 +39,8 @@ export const reactControlMapper = (repository: Repository) => { return FieldControls.WebhookPayload case 'sn:HtmlEditor': return FieldControls.HtmlEditor + case 'sn:RichText': + return FieldControls.RichTextEditor default: } diff --git a/packages/sn-controls-react/src/fieldcontrols/rich-text-editor.tsx b/packages/sn-controls-react/src/fieldcontrols/rich-text-editor.tsx index 402ca15e5..dca6439a2 100644 --- a/packages/sn-controls-react/src/fieldcontrols/rich-text-editor.tsx +++ b/packages/sn-controls-react/src/fieldcontrols/rich-text-editor.tsx @@ -57,6 +57,8 @@ export const RichTextEditor: React.FC< '' const classes = useStyles(props) + console.log(props.settings.ControlHint) + switch (props.actionName) { case 'edit': case 'new': @@ -80,6 +82,11 @@ export const RichTextEditor: React.FC< readOnly={props.settings.ReadOnly} localization={props.localization?.richTextEditor} onChange={({ editor }) => { + if (props.settings.ControlHint === 'sn:RichText') { + props.fieldOnChange?.(props.settings.Name, editor.getHTML()) + return + } + props.fieldOnChange?.(props.settings.Name, { text: editor.getHTML(), editor: JSON.stringify(editor.getJSON()), diff --git a/packages/sn-editor-react/src/components/controls/html-editor-control.tsx b/packages/sn-editor-react/src/components/controls/html-editor-control.tsx new file mode 100644 index 000000000..52c04f8a5 --- /dev/null +++ b/packages/sn-editor-react/src/components/controls/html-editor-control.tsx @@ -0,0 +1,155 @@ +import { + Button, + CircularProgress, + createStyles, + Dialog, + DialogActions, + DialogContent, + DialogTitle, + IconButton, + IconButtonProps, + makeStyles, + Tooltip, +} from '@material-ui/core' +import CodeIcon from '@material-ui/icons/Code' +import { Editor } from '@tiptap/react' +import React, { FC, useRef, useState } from 'react' +import { useLocalization } from '../../hooks' + +const useStyles = makeStyles(() => { + return createStyles({ + image: { + maxWidth: '100%', + height: 'auto', + }, + fileName: { + marginTop: '0.5rem', + textAlign: 'center', + }, + }) +}) + +interface ImageControlProps { + editor: Editor + buttonProps?: Partial +} + +type ImageFile = File & { src?: string } + +export const HtmlEditorControl: FC = ({ buttonProps, editor }) => { + const [open, setOpen] = useState(false) + const [uploadedFile, setUploadedFile] = useState() + const [isUploadInProgress, setIsUploadInProgress] = useState(false) + const fileInput = useRef(null) + + const classes = useStyles() + const localization = useLocalization() + + const handleClickOpen = () => { + if (isUploadInProgress) { + return + } + fileInput.current && fileInput.current.click() + } + + const handleClose = () => { + setOpen(false) + } + + const addImage = () => { + if (uploadedFile?.src) { + editor.chain().focus().setImage({ src: uploadedFile.src }).run() + } + } + + const imageToBase64 = (image: File) => { + return new Promise((resolve, reject) => { + const reader = new FileReader() + reader.onload = (e) => { + if (e.target?.result) { + resolve(e.target.result as string) + } else { + reject(e) + } + } + reader.readAsDataURL(image) + }) + } + + const upload = async (fileToUpload: File) => { + if (!fileToUpload) { + return + } + + setIsUploadInProgress(true) + + const imageSrc = await imageToBase64(fileToUpload) + const image: ImageFile = new File([fileToUpload], fileToUpload.name, { type: fileToUpload.type }) + image.src = imageSrc + + setIsUploadInProgress(false) + setUploadedFile(image) + } + + return ( + <> + + editor.chain().focus().toggleCode().run()} + color={editor.isActive('code') ? 'primary' : 'default'} + {...buttonProps}> + + html + + + { + setOpen(true) + + ev.target.files && upload(ev.target.files[0]) + }} + style={{ display: 'none' }} + ref={fileInput} + type="file" + accept="image/*" + multiple={false} + /> + { + setUploadedFile(undefined) + + if (fileInput.current) { + fileInput.current.value = '' + } + }}> + {localization.imageControl.title} + + {uploadedFile ? ( + <> + +
{uploadedFile.name}
+ + ) : ( +
+ +
+ )} +
+ + + + +
+ + ) +} diff --git a/packages/sn-editor-react/src/components/controls/index.ts b/packages/sn-editor-react/src/components/controls/index.ts index e012cdc90..77af3fb13 100644 --- a/packages/sn-editor-react/src/components/controls/index.ts +++ b/packages/sn-editor-react/src/components/controls/index.ts @@ -2,3 +2,4 @@ export * from './image-control' export * from './link-control' export * from './table-control' export * from './typography-control' +export * from './html-editor-control' diff --git a/packages/sn-editor-react/src/components/menu-bar.tsx b/packages/sn-editor-react/src/components/menu-bar.tsx index 58fbf74c3..5da04e2db 100644 --- a/packages/sn-editor-react/src/components/menu-bar.tsx +++ b/packages/sn-editor-react/src/components/menu-bar.tsx @@ -15,7 +15,7 @@ import { Editor } from '@tiptap/react' import React, { FC } from 'react' import { useLocalization } from '../hooks' import { getCommonStyles } from '../styles' -import { ImageControl, LinkControl, TableControl, TypographyControl } from './controls' +import { HtmlEditorControl, ImageControl, LinkControl, TableControl, TypographyControl } from './controls' const useStyles = makeStyles((theme) => { const commonStyles = getCommonStyles(theme) @@ -201,6 +201,10 @@ export const MenuBar: FC = ({ editor }) => { +
) } diff --git a/packages/sn-editor-react/src/context/localization-context.tsx b/packages/sn-editor-react/src/context/localization-context.tsx index afba7a666..bf79cb10d 100644 --- a/packages/sn-editor-react/src/context/localization-context.tsx +++ b/packages/sn-editor-react/src/context/localization-context.tsx @@ -23,6 +23,7 @@ export const defaultLocalization = { clearFormat: 'Clear format', undo: 'Undo', redo: 'Redo', + EditHtml: 'Edit HTML', }, bubbleMenu: { removeImage: 'remove image', From dcf0ef0b3a0854abe703a77130c7d2b37220ee71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81d=C3=A1m=20Hassan?= Date: Fri, 10 May 2024 15:59:55 +0200 Subject: [PATCH 05/14] feature: html editor v1 --- .../src/fieldcontrols/rich-text-editor.tsx | 2 - .../controls/html-editor-control.tsx | 126 ++++-------------- .../src/components/html-editor.tsx | 83 ++++++++++++ .../src/components/menu-bar.tsx | 6 +- .../src/context/localization-context.tsx | 4 + 5 files changed, 119 insertions(+), 102 deletions(-) create mode 100644 packages/sn-editor-react/src/components/html-editor.tsx diff --git a/packages/sn-controls-react/src/fieldcontrols/rich-text-editor.tsx b/packages/sn-controls-react/src/fieldcontrols/rich-text-editor.tsx index dca6439a2..a790ba4e4 100644 --- a/packages/sn-controls-react/src/fieldcontrols/rich-text-editor.tsx +++ b/packages/sn-controls-react/src/fieldcontrols/rich-text-editor.tsx @@ -57,8 +57,6 @@ export const RichTextEditor: React.FC< '' const classes = useStyles(props) - console.log(props.settings.ControlHint) - switch (props.actionName) { case 'edit': case 'new': diff --git a/packages/sn-editor-react/src/components/controls/html-editor-control.tsx b/packages/sn-editor-react/src/components/controls/html-editor-control.tsx index 52c04f8a5..ecd7a36fb 100644 --- a/packages/sn-editor-react/src/components/controls/html-editor-control.tsx +++ b/packages/sn-editor-react/src/components/controls/html-editor-control.tsx @@ -1,152 +1,84 @@ import { Button, - CircularProgress, - createStyles, Dialog, DialogActions, DialogContent, DialogTitle, IconButton, IconButtonProps, - makeStyles, Tooltip, } from '@material-ui/core' import CodeIcon from '@material-ui/icons/Code' import { Editor } from '@tiptap/react' -import React, { FC, useRef, useState } from 'react' +import React, { FC, useState } from 'react' import { useLocalization } from '../../hooks' +import { HtmlEditor } from '../html-editor' -const useStyles = makeStyles(() => { - return createStyles({ - image: { - maxWidth: '100%', - height: 'auto', - }, - fileName: { - marginTop: '0.5rem', - textAlign: 'center', - }, - }) -}) - -interface ImageControlProps { +interface HTMLEditorControlProps { editor: Editor buttonProps?: Partial } -type ImageFile = File & { src?: string } - -export const HtmlEditorControl: FC = ({ buttonProps, editor }) => { +export const HTMLEditorControl: FC = ({ editor, buttonProps }) => { const [open, setOpen] = useState(false) - const [uploadedFile, setUploadedFile] = useState() - const [isUploadInProgress, setIsUploadInProgress] = useState(false) - const fileInput = useRef(null) - - const classes = useStyles() + const [html, setHtml] = useState(editor.getHTML()) const localization = useLocalization() const handleClickOpen = () => { - if (isUploadInProgress) { - return - } - fileInput.current && fileInput.current.click() + setOpen(true) } const handleClose = () => { - setOpen(false) - } - - const addImage = () => { - if (uploadedFile?.src) { - editor.chain().focus().setImage({ src: uploadedFile.src }).run() + if (editor.getHTML() === html) { + setOpen(false) + return } - } - const imageToBase64 = (image: File) => { - return new Promise((resolve, reject) => { - const reader = new FileReader() - reader.onload = (e) => { - if (e.target?.result) { - resolve(e.target.result as string) - } else { - reject(e) - } - } - reader.readAsDataURL(image) - }) - } - - const upload = async (fileToUpload: File) => { - if (!fileToUpload) { + const confirmResult = window.confirm(localization.HTMLEditorControl.confirm) + if (!confirmResult) { return } + editor.commands.setContent(html) - setIsUploadInProgress(true) - - const imageSrc = await imageToBase64(fileToUpload) - const image: ImageFile = new File([fileToUpload], fileToUpload.name, { type: fileToUpload.type }) - image.src = imageSrc - - setIsUploadInProgress(false) - setUploadedFile(image) + setOpen(false) } return ( <> editor.chain().focus().toggleCode().run()} + onClick={() => handleClickOpen()} color={editor.isActive('code') ? 'primary' : 'default'} {...buttonProps}> html - { - setOpen(true) - - ev.target.files && upload(ev.target.files[0]) - }} - style={{ display: 'none' }} - ref={fileInput} - type="file" - accept="image/*" - multiple={false} - /> { - setUploadedFile(undefined) - - if (fileInput.current) { - fileInput.current.value = '' - } - }}> - {localization.imageControl.title} + aria-labelledby="html-editor-control-title" + fullWidth + maxWidth="sm" + onExited={() => {}}> + {localization.HTMLEditorControl.title} - {uploadedFile ? ( - <> - -
{uploadedFile.name}
- - ) : ( -
- -
- )} +
- +
diff --git a/packages/sn-editor-react/src/components/html-editor.tsx b/packages/sn-editor-react/src/components/html-editor.tsx new file mode 100644 index 000000000..fb40a177d --- /dev/null +++ b/packages/sn-editor-react/src/components/html-editor.tsx @@ -0,0 +1,83 @@ +// import { InputLabel, Theme } from '@material-ui/core' +import React, { useEffect, useRef, useState } from 'react' +import MonacoEditor from 'react-monaco-editor' + +/** + * Field control that represents a HTMLEDITOr. + */ +export const HtmlEditor: React.FC<{ + initialState?: string + fieldOnChange?: (value: string) => void +}> = (props) => { + const [value, setValue] = useState(props.initialState) + + const editorRef = useRef(null) + const containerRef = useRef(null) + + useEffect(() => { + if (!editorRef.current) { + return + } + editorRef.current.editor!.onDidContentSizeChange(() => { + containerRef.current!.style.height = `${400}px` + }) + }, [editorRef]) + + const editorChangeHandler = (newValue: string) => { + setValue(newValue) + props.fieldOnChange?.(newValue) + } + + return ( +
+ { + monaco.editor.defineTheme('admin-ui-dark', { + base: 'vs-dark', + inherit: true, + rules: [], + colors: { + 'editor.background': '#121212', + }, + }) + }} + /> +
+ ) +} diff --git a/packages/sn-editor-react/src/components/menu-bar.tsx b/packages/sn-editor-react/src/components/menu-bar.tsx index 5da04e2db..216471e7a 100644 --- a/packages/sn-editor-react/src/components/menu-bar.tsx +++ b/packages/sn-editor-react/src/components/menu-bar.tsx @@ -15,7 +15,7 @@ import { Editor } from '@tiptap/react' import React, { FC } from 'react' import { useLocalization } from '../hooks' import { getCommonStyles } from '../styles' -import { HtmlEditorControl, ImageControl, LinkControl, TableControl, TypographyControl } from './controls' +import { HTMLEditorControl, ImageControl, TableControl, TypographyControl } from './controls' const useStyles = makeStyles((theme) => { const commonStyles = getCommonStyles(theme) @@ -163,7 +163,7 @@ export const MenuBar: FC = ({ editor }) => { - @@ -201,7 +201,7 @@ export const MenuBar: FC = ({ editor }) => { - diff --git a/packages/sn-editor-react/src/context/localization-context.tsx b/packages/sn-editor-react/src/context/localization-context.tsx index bf79cb10d..4222d985c 100644 --- a/packages/sn-editor-react/src/context/localization-context.tsx +++ b/packages/sn-editor-react/src/context/localization-context.tsx @@ -52,6 +52,10 @@ export const defaultLocalization = { openInNewTab: 'Open link in a new tab', submit: 'Insert', }, + HTMLEditorControl: { + title: 'Edit HTML', + confirm: 'Would you like to save the changes?', + }, tableControl: { title: 'Insert table', rows: 'Rows', From 7b1be1abe4b146f0330f8a343848dae4a2d46329 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81d=C3=A1m=20Hassan?= Date: Mon, 13 May 2024 10:16:13 +0200 Subject: [PATCH 06/14] fix: remove duplicated icon --- packages/sn-editor-react/src/components/menu-bar.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/sn-editor-react/src/components/menu-bar.tsx b/packages/sn-editor-react/src/components/menu-bar.tsx index 216471e7a..5050336cd 100644 --- a/packages/sn-editor-react/src/components/menu-bar.tsx +++ b/packages/sn-editor-react/src/components/menu-bar.tsx @@ -163,10 +163,6 @@ export const MenuBar: FC = ({ editor }) => { - Date: Thu, 23 May 2024 15:48:55 +0200 Subject: [PATCH 07/14] add tiptap control hint --- apps/sensenet/src/components/react-control-mapper.ts | 1 + .../sn-controls-react/src/fieldcontrols/rich-text-editor.tsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/sensenet/src/components/react-control-mapper.ts b/apps/sensenet/src/components/react-control-mapper.ts index 8b4166bef..c1213d2d5 100644 --- a/apps/sensenet/src/components/react-control-mapper.ts +++ b/apps/sensenet/src/components/react-control-mapper.ts @@ -40,6 +40,7 @@ export const reactControlMapper = (repository: Repository) => { case 'sn:HtmlEditor': return FieldControls.HtmlEditor case 'sn:RichText': + case 'sn:TipTapEditor': return FieldControls.RichTextEditor default: } diff --git a/packages/sn-controls-react/src/fieldcontrols/rich-text-editor.tsx b/packages/sn-controls-react/src/fieldcontrols/rich-text-editor.tsx index a790ba4e4..a19c2fbc5 100644 --- a/packages/sn-controls-react/src/fieldcontrols/rich-text-editor.tsx +++ b/packages/sn-controls-react/src/fieldcontrols/rich-text-editor.tsx @@ -80,7 +80,7 @@ export const RichTextEditor: React.FC< readOnly={props.settings.ReadOnly} localization={props.localization?.richTextEditor} onChange={({ editor }) => { - if (props.settings.ControlHint === 'sn:RichText') { + if (props.settings.ControlHint === 'sn:RichText' || props.settings.ControlHint === 'sn:TipTapEditor') { props.fieldOnChange?.(props.settings.Name, editor.getHTML()) return } From ee1cf3034543d3a50bfcfdb3d0d2d9255890b5d4 Mon Sep 17 00:00:00 2001 From: "SN\\plastic" Date: Thu, 23 May 2024 16:08:15 +0200 Subject: [PATCH 08/14] bigger html editor dialog --- .../src/components/controls/html-editor-control.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/sn-editor-react/src/components/controls/html-editor-control.tsx b/packages/sn-editor-react/src/components/controls/html-editor-control.tsx index ecd7a36fb..a410f737a 100644 --- a/packages/sn-editor-react/src/components/controls/html-editor-control.tsx +++ b/packages/sn-editor-react/src/components/controls/html-editor-control.tsx @@ -59,8 +59,8 @@ export const HTMLEditorControl: FC = ({ editor, buttonPr onClose={handleClose} aria-labelledby="html-editor-control-title" fullWidth - maxWidth="sm" - onExited={() => {}}> + maxWidth="lg" + onExited={() => { }}> {localization.HTMLEditorControl.title} From 944899dd380eea44bcc533c558177aa095a6ce2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81d=C3=A1m=20Hassan?= Date: Fri, 24 May 2024 14:28:15 +0200 Subject: [PATCH 09/14] chore: Update pre-commit script to check if running in a terminal --- .husky/pre-commit | 9 ++++++++- packages/sn-editor-react/test/image-control.test.tsx | 10 ++++++++++ packages/sn-editor-react/test/link-control.test.tsx | 11 +++++++++++ packages/sn-editor-react/test/table-control.test.tsx | 11 +++++++++++ .../sn-editor-react/test/typography-control.test.tsx | 11 +++++++++++ 5 files changed, 51 insertions(+), 1 deletion(-) diff --git a/.husky/pre-commit b/.husky/pre-commit index f25cba788..ad30e6209 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,4 +1,11 @@ #!/bin/sh . "$(dirname "$0")/_/husky.sh" -exec >/dev/tty 2>&1 + +# Check if running in a terminal +if [[ "$OSTYPE" == "linux-gnu"* || "$OSTYPE" == "darwin"* ]]; then + # Unix-like OS detected, proceed with redirection + exec >/dev/tty 2>&1 +fi + +# Continue with the rest of the script yarn lint-staged diff --git a/packages/sn-editor-react/test/image-control.test.tsx b/packages/sn-editor-react/test/image-control.test.tsx index d928f1428..261b64ffc 100644 --- a/packages/sn-editor-react/test/image-control.test.tsx +++ b/packages/sn-editor-react/test/image-control.test.tsx @@ -8,6 +8,16 @@ import { defaultLocalization } from '../src/context' import { createExtensions } from '../src/extension-list' import { FileReaderMock } from './__mocks__/file-reader' +jest.mock('react-monaco-editor', () => + jest.fn((props) => { + console.log(props) + return ( +
+ {props.value} +
+ ) + }), +) describe('image control', () => { const onChange = jest.fn(({ editor }) => editor.getHTML()) const editor = new TiptapEditor({ diff --git a/packages/sn-editor-react/test/link-control.test.tsx b/packages/sn-editor-react/test/link-control.test.tsx index 8c017437d..050e3981a 100644 --- a/packages/sn-editor-react/test/link-control.test.tsx +++ b/packages/sn-editor-react/test/link-control.test.tsx @@ -7,6 +7,17 @@ import { LinkControl } from '../src/components/controls' import { defaultLocalization } from '../src/context' import { createExtensions } from '../src/extension-list' +jest.mock('react-monaco-editor', () => + jest.fn((props) => { + console.log(props) + return ( +
+ {props.value} +
+ ) + }), +) + describe('link control', () => { const onChange = jest.fn(({ editor }) => editor.getHTML()) const editor = new TiptapEditor({ diff --git a/packages/sn-editor-react/test/table-control.test.tsx b/packages/sn-editor-react/test/table-control.test.tsx index a4e78b136..e5cf9d3c8 100644 --- a/packages/sn-editor-react/test/table-control.test.tsx +++ b/packages/sn-editor-react/test/table-control.test.tsx @@ -7,6 +7,17 @@ import { TableControl } from '../src/components/controls' import { defaultLocalization } from '../src/context' import { createExtensions } from '../src/extension-list' +jest.mock('react-monaco-editor', () => + jest.fn((props) => { + console.log(props) + return ( +
+ {props.value} +
+ ) + }), +) + describe('table control', () => { const onChange = jest.fn(({ editor }) => editor.getHTML()) const editor = new TiptapEditor({ diff --git a/packages/sn-editor-react/test/typography-control.test.tsx b/packages/sn-editor-react/test/typography-control.test.tsx index 6a20d4bf5..d6ba998e2 100644 --- a/packages/sn-editor-react/test/typography-control.test.tsx +++ b/packages/sn-editor-react/test/typography-control.test.tsx @@ -6,6 +6,17 @@ import { TypographyControl } from '../src/components/controls' import { defaultLocalization } from '../src/context' import { createExtensions } from '../src/extension-list' +jest.mock('react-monaco-editor', () => + jest.fn((props) => { + console.log(props) + return ( +
+ {props.value} +
+ ) + }), +) + describe('typography control', () => { const editor = new TiptapEditor({ content: '
Hello

world

', From 096e7aaa951c2cb2aaf278f117d9813624cd8dec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81d=C3=A1m=20Hassan?= Date: Fri, 24 May 2024 14:35:54 +0200 Subject: [PATCH 10/14] refactor: test precommit --- .husky/pre-commit | 9 ++++----- packages/sn-editor-react/test/image-control.test.tsx | 1 - 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/.husky/pre-commit b/.husky/pre-commit index ad30e6209..77a72061f 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,11 +1,10 @@ #!/bin/sh . "$(dirname "$0")/_/husky.sh" - -# Check if running in a terminal -if [[ "$OSTYPE" == "linux-gnu"* || "$OSTYPE" == "darwin"* ]]; then +if [[ "$OSTYPE" == "linux-gnu"* || "$OSTYPE" == "darwin"* ]] &&!("$PSVersionTable.PSVersion.Major" -ge 5); then # Unix-like OS detected, proceed with redirection exec >/dev/tty 2>&1 +elif $PSVersionTable.PSVersion.Major -lt 5; then + # PowerShell version less than 5, assume Unix-like environment + exec >/dev/tty 2>&1 fi - -# Continue with the rest of the script yarn lint-staged diff --git a/packages/sn-editor-react/test/image-control.test.tsx b/packages/sn-editor-react/test/image-control.test.tsx index 261b64ffc..59d84a8de 100644 --- a/packages/sn-editor-react/test/image-control.test.tsx +++ b/packages/sn-editor-react/test/image-control.test.tsx @@ -10,7 +10,6 @@ import { FileReaderMock } from './__mocks__/file-reader' jest.mock('react-monaco-editor', () => jest.fn((props) => { - console.log(props) return (
{props.value} From ef5b90ebb2e28f457be0c731887b5cc2af74026d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81d=C3=A1m=20Hassan?= Date: Fri, 24 May 2024 14:42:26 +0200 Subject: [PATCH 11/14] refactor: test precommit --- .husky/pre-commit | 16 ++++++++++------ .../sn-editor-react/test/link-control.test.tsx | 1 - 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/.husky/pre-commit b/.husky/pre-commit index 77a72061f..4cb55fda0 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,10 +1,14 @@ #!/bin/sh . "$(dirname "$0")/_/husky.sh" -if [[ "$OSTYPE" == "linux-gnu"* || "$OSTYPE" == "darwin"* ]] &&!("$PSVersionTable.PSVersion.Major" -ge 5); then - # Unix-like OS detected, proceed with redirection - exec >/dev/tty 2>&1 -elif $PSVersionTable.PSVersion.Major -lt 5; then - # PowerShell version less than 5, assume Unix-like environment - exec >/dev/tty 2>&1 + +# Detect /dev/tty using readlink and fd/2 +tty=$(readlink /proc/$$/fd/2) + +# Use the detected tty for redirection +if [[ -n "$tty" ]]; then + exec >"$tty" 2>&1 +else + echo "Could not detect /dev/tty" fi + yarn lint-staged diff --git a/packages/sn-editor-react/test/link-control.test.tsx b/packages/sn-editor-react/test/link-control.test.tsx index 050e3981a..5b72c1e05 100644 --- a/packages/sn-editor-react/test/link-control.test.tsx +++ b/packages/sn-editor-react/test/link-control.test.tsx @@ -9,7 +9,6 @@ import { createExtensions } from '../src/extension-list' jest.mock('react-monaco-editor', () => jest.fn((props) => { - console.log(props) return (
{props.value} From 33d2861aeacbce12044678b6c653bf9ca4cb12a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81d=C3=A1m=20Hassan?= Date: Fri, 24 May 2024 14:53:36 +0200 Subject: [PATCH 12/14] fix: unite test fix --- packages/sn-editor-react/test/editor.test.tsx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/sn-editor-react/test/editor.test.tsx b/packages/sn-editor-react/test/editor.test.tsx index 9fac49803..e78b27dea 100644 --- a/packages/sn-editor-react/test/editor.test.tsx +++ b/packages/sn-editor-react/test/editor.test.tsx @@ -8,6 +8,17 @@ import { Editor } from '../src/components/editor' import { MenuBar } from '../src/components/menu-bar' import { defaultLocalization } from '../src/context' +jest.mock('react-monaco-editor', () => + jest.fn((props) => { + console.log(props) + return ( +
+ {props.value} +
+ ) + }), +) + describe('editor', () => { it('should be rendered properly', () => { const wrapper = shallow() From 1f36e622b3d92ea644282cbf72d79e5de4f66b67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81d=C3=A1m=20Hassan?= Date: Fri, 24 May 2024 15:03:12 +0200 Subject: [PATCH 13/14] fix: unit test fix --- packages/sn-editor-react/test/bubble-menu.test.tsx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/sn-editor-react/test/bubble-menu.test.tsx b/packages/sn-editor-react/test/bubble-menu.test.tsx index 3f0601617..85acffb50 100644 --- a/packages/sn-editor-react/test/bubble-menu.test.tsx +++ b/packages/sn-editor-react/test/bubble-menu.test.tsx @@ -7,6 +7,17 @@ import { Editor } from '../src/components/editor' import { BubbleMenu } from './../src/components/bubble-menu' import { mockInstance } from './__mocks__/mokInstance' +jest.mock('react-monaco-editor', () => + jest.fn((props) => { + console.log(props) + return ( +
+ {props.value} +
+ ) + }), +) + describe('BubbleMenu', () => { it('should be rendered BubbleMenu', () => { const wrapper = mount() From 65f195af4d52689732bbf36825a761bab224d4b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81d=C3=A1m=20Hassan?= Date: Fri, 24 May 2024 15:25:49 +0200 Subject: [PATCH 14/14] fix: unremoe Link Control --- packages/sn-editor-react/src/components/menu-bar.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/sn-editor-react/src/components/menu-bar.tsx b/packages/sn-editor-react/src/components/menu-bar.tsx index 5050336cd..af6c52d68 100644 --- a/packages/sn-editor-react/src/components/menu-bar.tsx +++ b/packages/sn-editor-react/src/components/menu-bar.tsx @@ -15,7 +15,7 @@ import { Editor } from '@tiptap/react' import React, { FC } from 'react' import { useLocalization } from '../hooks' import { getCommonStyles } from '../styles' -import { HTMLEditorControl, ImageControl, TableControl, TypographyControl } from './controls' +import { HTMLEditorControl, ImageControl, LinkControl, TableControl, TypographyControl } from './controls' const useStyles = makeStyles((theme) => { const commonStyles = getCommonStyles(theme) @@ -163,6 +163,10 @@ export const MenuBar: FC = ({ editor }) => { +