Skip to content

Commit

Permalink
feature: tinymce editor v1
Browse files Browse the repository at this point in the history
  • Loading branch information
hassanad94 committed Jul 18, 2024
1 parent c171d27 commit 1762ec9
Show file tree
Hide file tree
Showing 18 changed files with 2,350 additions and 4,114 deletions.
2 changes: 2 additions & 0 deletions apps/sensenet/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,5 @@ cypress/screenshots

# Instrumented code for cypress code coverage
instrumented

/tinymce
11 changes: 5 additions & 6 deletions apps/sensenet/src/components/content-list/content-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -716,16 +716,15 @@ export const ContentList = <T extends GenericContent = GenericContent>(props: Co
/* If the Order by Column Is The Display. The client will sort it. Due to some locale and indexing issues */
items={
currentOrder === 'DisplayName'
? children.sort((a, b) => {
? children?.sort((a, b) => {
// If no display Name
if (!a?.DisplayName || !b?.DisplayName) {
return 0
}
const nameA = a?.DisplayName ?? '' // Provide a default value if displayName is undefined
const nameB = b?.DisplayName ?? '' // Provide a default value if displayName is undefined

if (currentDirection === 'asc') {
return a?.DisplayName.localeCompare(b?.DisplayName)
return nameA.localeCompare(nameB)
}
return b?.DisplayName.localeCompare(a?.DisplayName)
return nameB.localeCompare(nameA)
})
: children
}
Expand Down
1 change: 1 addition & 0 deletions apps/sensenet/src/components/field-controls/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export * from './webhook-trigger'
export * from './webhook-headers'
export * from './webhook-payload'
export * from './html-editor'
export * from './tinymce-editor'
15 changes: 15 additions & 0 deletions apps/sensenet/src/components/field-controls/tinymce-editor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
* @module FieldControls
*/
import { ReactClientFieldSetting, TinymceEditor as SnTinymceEditor } from '@sensenet/controls-react'
import React from 'react'
import { useLocalization } from '../../hooks'

/**
* Field control that represents a RichText field. Available values will be populated from the FieldSettings.
*/
export const TinymceEditor: React.FC<ReactClientFieldSetting> = (props) => {
const localization = useLocalization()

return <SnTinymceEditor {...props} localization={{ richTextEditor: localization.editor }} />
}
2 changes: 2 additions & 0 deletions apps/sensenet/src/components/react-control-mapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ export const reactControlMapper = (repository: Repository) => {
case 'sn:RichText':
case 'sn:TipTapEditor':
return FieldControls.RichTextEditor
case 'sn:TinymceEditor':
return FieldControls.TinymceEditor
default:
}

Expand Down
4 changes: 4 additions & 0 deletions apps/sensenet/webpack.common.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const path = require('path')
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin')
const CopyPlugin = require('copy-webpack-plugin')
const webpack = require('webpack')

module.exports = {
Expand Down Expand Up @@ -28,6 +29,9 @@ module.exports = {
],
},
plugins: [
new CopyPlugin({
patterns: [{ from: './tinymce/**/*' }],
}),
new webpack.EnvironmentPlugin({
APP_VERSION: require('./package.json').version,
}),
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
"yarn": "^1.22.19"
},
"scripts": {
"postinstall": "husky install",
"postinstall": "husky install && node postinstall.mjs",
"cy:run:dms": "cypress run --project ./examples/sn-dms-demo",
"start:dms:e2e": "cross-env NODE_OPTIONS=--openssl-legacy-provider yarn workspace sn-dms-demo start:e2e",
"test:dms:e2e": "cross-env NODE_OPTIONS=--openssl-legacy-provider start-server-and-test start:dms:e2e http-get://localhost:3000 cy:run:dms",
Expand Down
3 changes: 2 additions & 1 deletion packages/sn-client-utils/src/utc-to-locale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export function convertUtcToLocale(utcDateString: string) {

return new Intl.DateTimeFormat(window.navigator.language, options).format(date)
} catch (error) {
return console.error(error.message)
console.error(error.message)
return ''
}
}
2 changes: 1 addition & 1 deletion packages/sn-controls-react/src/editor-wrapper.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { Editor as default } from '@sensenet/editor-react'
export { Editor as default, TinymceEditor } from '@sensenet/editor-react'
3 changes: 2 additions & 1 deletion packages/sn-controls-react/src/fieldcontrols/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export * from './radio-button-group'
export * from './short-text'
export * from './tags-input'
export * from './textarea'
export * from './rich-text-editor'
export * from './tinymce-editor'
export * from './name'
export * from './password'
export * from './file-name'
Expand All @@ -33,3 +33,4 @@ export * from './localization'
export * from './file-size'
export * from './page-count'
export * from './html-editor'
export * from './rich-text-editor'
108 changes: 108 additions & 0 deletions packages/sn-controls-react/src/fieldcontrols/tinymce-editor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/**
* @module FieldControls
*/
import { CircularProgress, createStyles, FormHelperText, InputLabel, makeStyles, Typography } from '@material-ui/core'
import { deepMerge } from '@sensenet/client-utils'
import { renderHtml } from '@sensenet/editor-react'
import React, { lazy, Suspense } from 'react'
import { changeTemplatedValue } from '../helpers'
import { ReactClientFieldSetting } from './client-field-setting'
import { defaultLocalization } from './localization'
const Editor = lazy(() => import('../editor-wrapper').then((module) => ({ default: module.TinymceEditor })))

const useStyles = makeStyles(() =>
createStyles({
richTextEditor: {},
}),
)

type RichTextEditorClassKey = Partial<ReturnType<typeof useStyles>>

interface ParsedRichTextFieldValue {
text: string
editor: string
}

const getFieldValue = (rawValue?: string) => {
let value

if (rawValue === undefined || rawValue === null) {
return undefined
}

try {
value = JSON.parse(rawValue) as ParsedRichTextFieldValue
} catch (_) {
return rawValue
}

try {
return value.editor ? JSON.parse(value.editor) : value.text
} catch (_) {
return value.text
}
}

/**
* Field control that represents a LongText field. Available values will be populated from the FieldSettings.
*/
export const TinymceEditor: React.FC<
ReactClientFieldSetting & { classes?: RichTextEditorClassKey; fieldValue?: string }
> = (props) => {
const localization = deepMerge(defaultLocalization.richTextEditor, props.localization?.richTextEditor)

const initialState =
getFieldValue(props.fieldValue) ||
(props.actionName === 'new' && changeTemplatedValue(props.settings.DefaultValue)) ||
''
const classes = useStyles(props)

switch (props.actionName) {
case 'edit':
case 'new':
return (
<div className={classes.richTextEditor}>
<InputLabel shrink htmlFor={props.settings.Name} required={props.settings.Compulsory}>
{props.settings.DisplayName}
</InputLabel>

<Suspense
fallback={
<div style={{ textAlign: 'center' }}>
<CircularProgress />
<div>{localization.loading}</div>
</div>
}>
<Editor
initvalue={initialState}
onChange={(content) => {
props.fieldOnChange?.(props.settings.Name, content)
}}
/>
</Suspense>

{!props.hideDescription && <FormHelperText>{props.settings.Description}</FormHelperText>}
</div>
)
case 'browse':
default:
return (
<div>
<Typography variant="caption" gutterBottom={true}>
{props.settings.DisplayName}
</Typography>
{initialState ? (
<div
dangerouslySetInnerHTML={{
__html: typeof initialState === 'string' ? initialState : renderHtml(initialState),
}}
/>
) : (
<Typography variant="body1" gutterBottom={true}>
{localization.noValue}
</Typography>
)}
</div>
)
}
}
1 change: 1 addition & 0 deletions packages/sn-editor-react/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,4 @@ typings/
dist
temp
bundle
/public/tinymce/
3 changes: 3 additions & 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.18.9",
"@material-ui/icons": "^4.11.3",
"@sensenet/client-utils": "^2.3.0",
"@tinymce/tinymce-react": "^5.1.1",
"@tiptap/core": "^2.0.0-beta.102",
"@tiptap/extension-blockquote": "^2.0.0-beta.15",
"@tiptap/extension-bold": "^2.0.0-beta.15",
Expand Down Expand Up @@ -74,6 +75,8 @@
"@tiptap/extension-underline": "^2.0.0-beta.16",
"@tiptap/html": "^2.0.0-beta.101",
"@tiptap/react": "^2.0.0-beta.63",
"fs-extra": "^11.2.0",
"tinymce": "^7",
"tslib": "^2.4.0"
},
"devDependencies": {
Expand Down
1 change: 1 addition & 0 deletions packages/sn-editor-react/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './components'
export * from './context'
export * from './utils'
export * from './tinymce'
56 changes: 56 additions & 0 deletions packages/sn-editor-react/src/tinymce/Editor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { Editor, IAllProps } from '@tinymce/tinymce-react'
import React, { FC, useRef } from 'react'
import { Editor as TinyMCEEditor } from 'tinymce'

export interface TinymceEditorProps {
onChange?: IAllProps['onEditorChange']
initvalue?: string
}

export const TinymceEditor: FC<TinymceEditorProps> = (props) => {
const editorRef = useRef<TinyMCEEditor | null>(null)
return (
<>
<Editor
tinymceScriptSrc="/tinymce/tinymce.min.js"
licenseKey="gpl"
onInit={(_evt, editor) => (editorRef.current = editor)}
initialValue={props.initvalue}
init={{
height: 500,
menubar: false,
plugins: [
'advlist',
'autolink',
'lists',
'link',
'image',
'charmap',
'anchor',
'searchreplace',
'visualblocks',
'code',
'fullscreen',
'insertdatetime',
'media',
'table',
'preview',
'help',
'wordcount',
],
toolbar:
'undo redo | blocks | ' +
'bold italic forecolor | alignleft aligncenter ' +
'alignright alignjustify | bullist numlist outdent indent | ' +
'removeformat | help',
content_style: 'body { font-family:Helvetica,Arial,sans-serif; font-size:14px }',
}}
onEditorChange={(e, editor) => {
if (props.onChange) {
props.onChange(e, editor)
}
}}
/>
</>
)
}
1 change: 1 addition & 0 deletions packages/sn-editor-react/src/tinymce/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './Editor'
16 changes: 16 additions & 0 deletions postinstall.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import path from 'path'
import * as url from 'url'
import fs from 'fs-extra'
// const topDir = import.meta.dirname

try {
const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
fs.emptyDirSync(path.join(__dirname, 'apps', 'sensenet', 'tinymce'))
fs.copySync(path.join(__dirname, 'node_modules', 'tinymce'), path.join(__dirname, 'apps', 'sensenet', 'tinymce'), {
overwrite: true,
})

console.log('tinymcye has been installed')
} catch (error) {
console.error(error)
}
Loading

0 comments on commit 1762ec9

Please sign in to comment.