diff --git a/resources/icons/editor/fullscreen.svg b/resources/icons/editor/fullscreen.svg new file mode 100644 index 000000000..3cca3097a --- /dev/null +++ b/resources/icons/editor/fullscreen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/js/components/wysiwyg-editor.js b/resources/js/components/wysiwyg-editor.js index 98732dab7..bdcdd5c51 100644 --- a/resources/js/components/wysiwyg-editor.js +++ b/resources/js/components/wysiwyg-editor.js @@ -4,10 +4,12 @@ export class WysiwygEditor extends Component { setup() { this.elem = this.$el; - this.editArea = this.$refs.editArea; + this.editContainer = this.$refs.editContainer; + this.editContent = this.$refs.editContent; window.importVersioned('wysiwyg').then(wysiwyg => { - wysiwyg.createPageEditorInstance(this.editArea); + const editorContent = this.editContent.textContent; + wysiwyg.createPageEditorInstance(this.editContainer, editorContent); }); } diff --git a/resources/js/wysiwyg/index.ts b/resources/js/wysiwyg/index.ts index 9f2f1645a..3ca01835f 100644 --- a/resources/js/wysiwyg/index.ts +++ b/resources/js/wysiwyg/index.ts @@ -6,8 +6,9 @@ import {getNodesForPageEditor} from './nodes'; import {buildEditorUI} from "./ui"; import {setEditorContentFromHtml} from "./actions"; import {registerTableResizer} from "./ui/framework/helpers/table-resizer"; +import {el} from "./helpers"; -export function createPageEditorInstance(editArea: HTMLElement) { +export function createPageEditorInstance(container: HTMLElement, htmlContent: string) { const config: CreateEditorArgs = { namespace: 'BookStackPageEditor', nodes: getNodesForPageEditor(), @@ -26,7 +27,11 @@ export function createPageEditorInstance(editArea: HTMLElement) { } }; - const startingHtml = editArea.innerHTML; + const editArea = el('div', { + contenteditable: 'true', + }); + container.append(editArea); + container.classList.add('editor-container'); const editor = createEditor(config); editor.setRootElement(editArea); @@ -37,7 +42,7 @@ export function createPageEditorInstance(editArea: HTMLElement) { registerTableResizer(editor, editArea), ); - setEditorContentFromHtml(editor, startingHtml); + setEditorContentFromHtml(editor, htmlContent); const debugView = document.getElementById('lexical-debug'); editor.registerUpdateListener(({editorState}) => { @@ -47,24 +52,5 @@ export function createPageEditorInstance(editArea: HTMLElement) { } }); - buildEditorUI(editArea, editor); - - // Example of creating, registering and using a custom command - - // const SET_BLOCK_CALLOUT_COMMAND = createCommand(); - // editor.registerCommand(SET_BLOCK_CALLOUT_COMMAND, (category: CalloutCategory = 'info') => { - // const selection = $getSelection(); - // const blockElement = $getNearestBlockElementAncestorOrThrow(selection.getNodes()[0]); - // if ($isCalloutNode(blockElement)) { - // $setBlocksType(selection, $createParagraphNode); - // } else { - // $setBlocksType(selection, () => $createCalloutNode(category)); - // } - // return true; - // }, COMMAND_PRIORITY_LOW); - // - // const button = document.getElementById('lexical-button'); - // button.addEventListener('click', event => { - // editor.dispatchCommand(SET_BLOCK_CALLOUT_COMMAND, 'info'); - // }); + buildEditorUI(container, editArea, editor); } diff --git a/resources/js/wysiwyg/ui/defaults/button-definitions.ts b/resources/js/wysiwyg/ui/defaults/button-definitions.ts index cebf25807..4a45ef75d 100644 --- a/resources/js/wysiwyg/ui/defaults/button-definitions.ts +++ b/resources/js/wysiwyg/ui/defaults/button-definitions.ts @@ -51,6 +51,7 @@ import imageIcon from "@icons/editor/image.svg" import horizontalRuleIcon from "@icons/editor/horizontal-rule.svg" import detailsIcon from "@icons/editor/details.svg" import sourceIcon from "@icons/editor/source-view.svg" +import fullscreenIcon from "@icons/editor/fullscreen.svg" import {$createHorizontalRuleNode, $isHorizontalRuleNode} from "../../nodes/horizontal-rule"; export const undo: EditorButtonDefinition = { @@ -206,7 +207,7 @@ function buildListButton(label: string, type: ListType, icon: string): EditorBut action(context: EditorUiContext) { context.editor.getEditorState().read(() => { const selection = $getSelection(); - if (this.isActive(selection)) { + if (this.isActive(selection, context)) { removeList(context.editor); } else { insertList(context.editor, type); @@ -374,4 +375,18 @@ export const source: EditorButtonDefinition = { isActive() { return false; } +}; + +export const fullscreen: EditorButtonDefinition = { + label: 'Fullscreen', + icon: fullscreenIcon, + async action(context: EditorUiContext, button: EditorButton) { + const isFullScreen = context.containerDOM.classList.contains('fullscreen'); + context.containerDOM.classList.toggle('fullscreen', !isFullScreen); + (context.containerDOM.closest('body') as HTMLElement).classList.toggle('editor-is-fullscreen', !isFullScreen); + button.setActiveState(!isFullScreen); + }, + isActive(selection, context: EditorUiContext) { + return context.containerDOM.classList.contains('fullscreen'); + } }; \ No newline at end of file diff --git a/resources/js/wysiwyg/ui/framework/buttons.ts b/resources/js/wysiwyg/ui/framework/buttons.ts index 7e8df076a..f74201ff7 100644 --- a/resources/js/wysiwyg/ui/framework/buttons.ts +++ b/resources/js/wysiwyg/ui/framework/buttons.ts @@ -8,8 +8,8 @@ export interface EditorBasicButtonDefinition { } export interface EditorButtonDefinition extends EditorBasicButtonDefinition { - action: (context: EditorUiContext) => void; - isActive: (selection: BaseSelection|null) => boolean; + action: (context: EditorUiContext, button: EditorButton) => void; + isActive: (selection: BaseSelection|null, context: EditorUiContext) => boolean; setup?: (context: EditorUiContext, button: EditorButton) => void; } @@ -68,11 +68,16 @@ export class EditorButton extends EditorUiElement { } protected onClick() { - this.definition.action(this.getContext()); + this.definition.action(this.getContext(), this); } updateActiveState(selection: BaseSelection|null) { - this.active = this.definition.isActive(selection); + const isActive = this.definition.isActive(selection, this.getContext()); + this.setActiveState(isActive); + } + + setActiveState(active: boolean) { + this.active = active; this.dom?.classList.toggle('editor-button-active', this.active); } diff --git a/resources/js/wysiwyg/ui/framework/core.ts b/resources/js/wysiwyg/ui/framework/core.ts index 2972c9821..465765caa 100644 --- a/resources/js/wysiwyg/ui/framework/core.ts +++ b/resources/js/wysiwyg/ui/framework/core.ts @@ -10,6 +10,7 @@ export type EditorUiStateUpdate = { export type EditorUiContext = { editor: LexicalEditor, editorDOM: HTMLElement, + containerDOM: HTMLElement, translate: (text: string) => string, manager: EditorUIManager, lastSelection: BaseSelection|null, diff --git a/resources/js/wysiwyg/ui/framework/manager.ts b/resources/js/wysiwyg/ui/framework/manager.ts index 4ca90a12a..3c2ad8926 100644 --- a/resources/js/wysiwyg/ui/framework/manager.ts +++ b/resources/js/wysiwyg/ui/framework/manager.ts @@ -79,7 +79,7 @@ export class EditorUIManager { this.toolbar = toolbar; toolbar.setContext(this.getContext()); - this.getContext().editorDOM.before(toolbar.getDOMElement()); + this.getContext().containerDOM.prepend(toolbar.getDOMElement()); } registerContextToolbar(key: string, definition: EditorContextToolbarDefinition) { @@ -97,6 +97,13 @@ export class EditorUIManager { // console.log('selection update', update.selection); } + triggerStateRefresh(): void { + this.triggerStateUpdate({ + editor: this.getContext().editor, + selection: this.getContext().lastSelection, + }); + } + protected updateContextToolbars(update: EditorUiStateUpdate): void { for (const toolbar of this.activeContextToolbars) { toolbar.empty(); @@ -133,7 +140,7 @@ export class EditorUIManager { toolbar.setContext(this.getContext()); this.activeContextToolbars.push(toolbar); - this.getContext().editorDOM.after(toolbar.getDOMElement()); + this.getContext().containerDOM.append(toolbar.getDOMElement()); toolbar.attachTo(target); } } diff --git a/resources/js/wysiwyg/ui/index.ts b/resources/js/wysiwyg/ui/index.ts index 1f07fe710..3501ed557 100644 --- a/resources/js/wysiwyg/ui/index.ts +++ b/resources/js/wysiwyg/ui/index.ts @@ -5,10 +5,11 @@ import {image as imageFormDefinition, link as linkFormDefinition, source as sour import {ImageDecorator} from "./decorators/image"; import {EditorUiContext} from "./framework/core"; -export function buildEditorUI(element: HTMLElement, editor: LexicalEditor) { +export function buildEditorUI(container: HTMLElement, element: HTMLElement, editor: LexicalEditor) { const manager = new EditorUIManager(); const context: EditorUiContext = { editor, + containerDOM: container, editorDOM: element, manager, translate: (text: string): string => text, diff --git a/resources/js/wysiwyg/ui/toolbars.ts b/resources/js/wysiwyg/ui/toolbars.ts index bb3b436b9..550c798c2 100644 --- a/resources/js/wysiwyg/ui/toolbars.ts +++ b/resources/js/wysiwyg/ui/toolbars.ts @@ -1,7 +1,7 @@ import {EditorButton} from "./framework/buttons"; import { blockquote, bold, bulletList, clearFormating, code, - dangerCallout, details, + dangerCallout, details, fullscreen, h2, h3, h4, h5, highlightColor, horizontalRule, image, infoCallout, italic, link, numberList, paragraph, redo, source, strikethrough, subscript, @@ -73,6 +73,7 @@ export function getMainEditorFullToolbar(): EditorContainerUiElement { // Meta elements new EditorButton(source), + new EditorButton(fullscreen), // Test new EditorButton({ diff --git a/resources/sass/_editor.scss b/resources/sass/_editor.scss index 2fcf4edb3..79cbc73e8 100644 --- a/resources/sass/_editor.scss +++ b/resources/sass/_editor.scss @@ -4,11 +4,25 @@ } // Main UI elements +.editor-container { + background-color: #FFF; + position: relative; + &.fullscreen { + z-index: 500; + } +} .editor-toolbar-main { display: flex; flex-wrap: wrap; } +body.editor-is-fullscreen { + overflow: hidden; + .edit-area { + z-index: 20; + } +} + // Buttons .editor-button { border: 1px solid #DDD; diff --git a/resources/views/pages/parts/wysiwyg-editor.blade.php b/resources/views/pages/parts/wysiwyg-editor.blade.php index 5cd60bbc6..8fc0dc55a 100644 --- a/resources/views/pages/parts/wysiwyg-editor.blade.php +++ b/resources/views/pages/parts/wysiwyg-editor.blade.php @@ -6,48 +6,49 @@ option:wysiwyg-editor:server-upload-limit-text="{{ trans('errors.server_upload_limit') }}" class=""> -
-
-

Some content here

-

Content with image in, before text. Sleepy meow After text.

-

This has a link in it

-

List below this h2 header

- - -
- Collapsible details/summary block -

Inner text here

-

Inner Header

-

More text with bold in it

-
- -

- Hello there, this is an info callout -

- -

Table

- - - - - - - - - - - - - - - - -
Cell ACell BCell C
Cell DCell ECell F
-
+
+ +
{{--