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

BUGFIX: 3839 show warn dialog before ckeditor formatting gets lost #3842

Draft
wants to merge 2 commits into
base: 8.3
Choose a base branch
from
Draft
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
42 changes: 41 additions & 1 deletion packages/neos-ui-ckeditor5-bindings/src/ckEditorApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,18 @@ export const bootstrap = _editorConfig => {

export const createEditor = store => async options => {
const {propertyDomNode, propertyName, editorOptions, globalRegistry, userPreferences, onChange} = options;

const clonedTemporaryPropertyDomNode = propertyDomNode.cloneNode(true);

const ckEditorConfig = editorConfig.configRegistry.getCkeditorConfig({
editorOptions,
userPreferences,
globalRegistry,
propertyDomNode
});

const initialData = propertyDomNode.innerHTML;

class NeosEditor extends DecoupledEditor {
constructor(...args) {
super(...args);
Expand All @@ -55,9 +60,44 @@ export const createEditor = store => async options => {
}

return NeosEditor
.create(propertyDomNode, ckEditorConfig)
.create(clonedTemporaryPropertyDomNode, {...ckEditorConfig, initialData})
.then(editor => {
const debouncedOnChange = debounce(() => onChange(cleanupContentBeforeCommit(editor.getData())), 1500, {maxWait: 5000});

const firstUpcastedData = cleanupContentBeforeCommit(editor.getData());
const hasMarkupDerivation = firstUpcastedData !== initialData;

if (!hasMarkupDerivation) {
propertyDomNode.replaceWith(clonedTemporaryPropertyDomNode)
} else {
const openMarkupDerivationDialogOnClickInText = () => {
let cleanupSubscription = null;
const waitForConfirmation = () => {
const {acknowledgement} = store.getState().ui.inlineEditorMarkupDerivationDialog;
switch (acknowledgement) {
case 'CONFIRMED':
cleanupSubscription?.()
propertyDomNode.removeEventListener('click', openMarkupDerivationDialogOnClickInText)

debouncedOnChange()
propertyDomNode.replaceWith(clonedTemporaryPropertyDomNode)

editor.editing.view.focus();
break;
case 'CANCELED':
cleanupSubscription?.()
break;
}
};
cleanupSubscription = store.subscribe(waitForConfirmation)
store.dispatch(actions.UI.InlineEditorMarkupDerivationDialog.open())
console.warn('ckeditor formatting derivation', initialData, firstUpcastedData)
};

propertyDomNode.dataset.neosInlineEditorMarkupDerivation = true
propertyDomNode.addEventListener('click', openMarkupDerivationDialogOnClickInText)
}

editor.model.document.on('change:data', debouncedOnChange);
editor.ui.focusTracker.on('change:isFocused', event => {
if (!event.source.isFocused) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export default ({store, globalRegistry, nodeTypesRegistry, inlineEditorRegistry,
}

try {
if (!propertyDomNode.dataset.neosInlineEditorIsInitialized) {
if (!propertyDomNode.ckeditorInstance) {
const userPreferences = $get('user.preferences', store.getState());

createInlineEditor({
Expand Down Expand Up @@ -96,8 +96,6 @@ export default ({store, globalRegistry, nodeTypesRegistry, inlineEditorRegistry,
}
}
});

propertyDomNode.dataset.neosInlineEditorIsInitialized = true;
}
} catch (err) {
//
Expand Down
9 changes: 7 additions & 2 deletions packages/neos-ui-guest-frame/src/style.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,20 @@
outline-color: var(--colors-Warn);
}

:global(.neos-inline-editable:focus) {
:global([data-__neos-property].ck-content:focus) {
outline: none;
}

:global([data-neos-inline-editor-is-initialized]:hover) {
:global([data-__neos-property].ck-content:hover) {
outline-offset: 5px;
outline: 2px dashed var(--colors-PrimaryBlue);
}

:global([data-neos-inline-editor-markup-derivation]) {
outline-offset: 5px;
outline: 2px dashed var(--colors-Warn);
}

.notInlineEditableOverlay {
position: absolute;
top: 0;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import produce from 'immer';
import {action as createAction, ActionType} from 'typesafe-actions';

import {InitAction} from '../../System';

export interface State extends Readonly<{
isOpen: boolean;
acknowledgement?: 'CANCELED' | 'CONFIRMED'
}> {}

export const defaultState: State = {
isOpen: false
};

//
// Export the action types
//
export enum actionTypes {
OPEN = '@neos/neos-ui/UI/InlineEditorMarkupDerivationDialog/OPEN',
CANCEL = '@neos/neos-ui/UI/InlineEditorMarkupDerivationDialog/CANCEL',
CONFIRM = '@neos/neos-ui/UI/InlineEditorMarkupDerivationDialog/CONFIRM'
}

/**
* Opens the modal
*/
const open = () => createAction(actionTypes.OPEN);

/**
* Closes the modal
*/
const cancel = () => createAction(actionTypes.CANCEL);

/**
* Confirms the modal
*/
const confirm = () => createAction(actionTypes.CONFIRM);

//
// Export the actions
//
export const actions = {
open,
cancel,
confirm
};

export type Action = ActionType<typeof actions>;

//
// Export the reducer
//
export const reducer = (state: State = defaultState, action: InitAction | Action) => produce(state, draft => {
switch (action.type) {
case actionTypes.OPEN: {
draft.isOpen = true;
draft.acknowledgement = undefined;
break;
}
case actionTypes.CANCEL: {
draft.isOpen = false;
draft.acknowledgement = 'CANCELED';
break;
}
case actionTypes.CONFIRM: {
draft.isOpen = false;
draft.acknowledgement = 'CONFIRMED';
break;
}
}
});

export const selectors = {};
8 changes: 6 additions & 2 deletions packages/neos-ui-redux-store/src/UI/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import * as SelectNodeTypeModal from './SelectNodeTypeModal';
import * as NodeCreationDialog from './NodeCreationDialog';
import * as NodeVariantCreationDialog from './NodeVariantCreationDialog';
import * as ContentTree from './ContentTree';
import * as InlineEditorMarkupDerivationDialog from './InlineEditorMarkupDerivationDialog';

const all = {
FlashMessages,
Expand All @@ -37,7 +38,8 @@ const all = {
SelectNodeTypeModal,
NodeCreationDialog,
NodeVariantCreationDialog,
ContentTree
ContentTree,
InlineEditorMarkupDerivationDialog
};

function typedKeys<T>(o: T) : Array<keyof T> {
Expand Down Expand Up @@ -65,6 +67,7 @@ export interface State {
nodeCreationDialog: NodeCreationDialog.State;
nodeVariantCreationDialog: NodeVariantCreationDialog.State;
contentTree: ContentTree.State;
inlineEditorMarkupDerivationDialog: InlineEditorMarkupDerivationDialog.State;
}

//
Expand Down Expand Up @@ -97,7 +100,8 @@ export const reducer = combineReducers({
selectNodeTypeModal: SelectNodeTypeModal.reducer,
nodeCreationDialog: NodeCreationDialog.reducer,
nodeVariantCreationDialog: NodeVariantCreationDialog.reducer,
contentTree: ContentTree.reducer
contentTree: ContentTree.reducer,
inlineEditorMarkupDerivationDialog: InlineEditorMarkupDerivationDialog.reducer
} as any); // TODO: when we update redux, this shouldn't be necessary https://github.com/reduxjs/redux/issues/2709#issuecomment-357328709

//
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React from 'react';
import {connect} from 'react-redux';

import {actions} from '@neos-project/neos-ui-redux-store';
import {Button, Dialog, Icon} from '@neos-project/react-ui-components';
import I18n from '@neos-project/neos-ui-i18n';

import style from './style.module.css';

const withReduxState = connect((state) => ({
isOpen: state?.ui?.inlineEditorMarkupDerivationDialog?.isOpen
}), {
onCancel: actions.UI.InlineEditorMarkupDerivationDialog.cancel,
onConfirm: actions.UI.InlineEditorMarkupDerivationDialog.confirm
});

export const InlineEditorMarkupDerivationDialog = withReduxState((props) => {
if (!props.isOpen) {
return null;
}

return (
<Dialog
actions={[
<Button
key="cancel"
style="lighter"
hoverStyle="brand"
onClick={props.onCancel}
>
<I18n
id="Neos.Neos.Ui:InlineEditorMarkupDerivationDialog:cancel"
fallback="Cancel"
/>
</Button>,
<Button
key="confirm"
style="warn"
hoverStyle="warn"
onClick={props.onConfirm}
>
<Icon icon="i-cursor" className={style.buttonIcon} />
<I18n
id="Neos.Neos.Ui:InlineEditorMarkupDerivationDialog:confirm"
fallback="Edit"
/>
</Button>
]}
title={<div>
<Icon icon="highlighter"/>
<span className={style.modalTitle}>
<I18n
id="Neos.Neos.Ui:InlineEditorMarkupDerivationDialog:title"
fallback="Unexpected formatting in text"
/>
</span>
</div>}
onRequestClose={props.onCancel}
type="warn"
isOpen
autoFocus
>
<div className={style.modalContents}>
<I18n
id="Neos.Neos.Ui:InlineEditorMarkupDerivationDialog:message"
fallback="By editing this text its formatting might partially be lost."
/>
</div>
</Dialog>
);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

export {InlineEditorMarkupDerivationDialog} from './InlineEditorMarkupDerivationDialog';
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.modalTitle {
margin-left: var(--spacing-Full);
}
.modalContents {
padding: var(--spacing-Full);
}
.buttonIcon {
margin-right: var(--spacing-Half);
}
2 changes: 2 additions & 0 deletions packages/neos-ui/src/manifest.containers.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import NodeVariantCreationDialog from './Containers/Modals/NodeVariantCreationDi
import ReloginDialog from './Containers/Modals/ReloginDialog/index';
import KeyboardShortcutModal from './Containers/Modals/KeyboardShortcutModal/index';
import UnappliedChangesDialog from './Containers/Modals/UnappliedChangesDialog/index';
import {InlineEditorMarkupDerivationDialog} from './Containers/Modals/InlineEditorMarkupDerivationDialog/index';

import PrimaryToolbar from './Containers/PrimaryToolbar/index';
import DimensionSwitcher from './Containers/PrimaryToolbar/DimensionSwitcher/index';
Expand Down Expand Up @@ -55,6 +56,7 @@ manifest('main.containers', {}, globalRegistry => {
containerRegistry.set('Modals/ReloginDialog', ReloginDialog);
containerRegistry.set('Modals/KeyboardShortcutModal', KeyboardShortcutModal);
containerRegistry.set('Modals/UnappliedChangesDialog', UnappliedChangesDialog);
containerRegistry.set('Modals/InlineEditorMarkupDerivationDialog', InlineEditorMarkupDerivationDialog);

containerRegistry.set('PrimaryToolbar', PrimaryToolbar);
containerRegistry.set('PrimaryToolbar/Left/MenuToggler', MenuToggler);
Expand Down
Loading