From c9555b4648b78013c4d89c42b63a7e20930c6f5c Mon Sep 17 00:00:00 2001 From: sibiraj-s Date: Sun, 31 May 2020 16:08:00 +0530 Subject: [PATCH] chore: update css identifiers --- demo/src/app/app.component.scss | 10 - demo/src/app/app.component.spec.ts | 2 +- demo/src/app/plugins/menu/codemirror.ts | 17 +- src/lib/ngx-editor.component.html | 2 +- src/lib/ngx-editor.component.scss | 236 ++++++++++-------- src/lib/ngx-editor.component.spec.ts | 2 +- src/lib/ngx-editor.component.ts | 3 +- .../prosemirror/plugins/menu/MenuBarView.ts | 2 +- src/lib/prosemirror/plugins/menu/menu.ts | 88 +++---- src/lib/prosemirror/plugins/placeholder.ts | 3 +- src/lib/types.ts | 2 + 11 files changed, 191 insertions(+), 176 deletions(-) diff --git a/demo/src/app/app.component.scss b/demo/src/app/app.component.scss index e2b5683a..10bfccb8 100644 --- a/demo/src/app/app.component.scss +++ b/demo/src/app/app.component.scss @@ -34,16 +34,6 @@ } } -.CustomMenuItem { - padding: 0 0.3rem; - border-radius: 2px; - - &.NgxEditor-MenuItem__Active { - background-color: #e8f0fe; - color: #1a73e8; - } -} - .CodeMirror { border: 1px solid #eee; height: auto; diff --git a/demo/src/app/app.component.spec.ts b/demo/src/app/app.component.spec.ts index 15d85b27..95eb655c 100644 --- a/demo/src/app/app.component.spec.ts +++ b/demo/src/app/app.component.spec.ts @@ -34,6 +34,6 @@ describe('AppComponent', () => { it('should render title in a h6 tag', () => { const compiled: DebugElement = fixture.debugElement; - expect(compiled.query(By.css('.NgxEditor-MenuBar'))).toBeDefined(); + expect(compiled.query(By.css('.NgxEditor__MenuBar'))).toBeDefined(); }); }); diff --git a/demo/src/app/plugins/menu/codemirror.ts b/demo/src/app/plugins/menu/codemirror.ts index c53397ff..a4d8ab25 100644 --- a/demo/src/app/plugins/menu/codemirror.ts +++ b/demo/src/app/plugins/menu/codemirror.ts @@ -7,12 +7,12 @@ const codeMirror: ToolbarCustomMenuItem = (editorView) => { const dom: HTMLElement = document.createElement('div'); dom.innerHTML = 'CodeMirror'; - dom.classList.add('NgxEditor-MenuItem'); - dom.classList.add('CustomMenuItem'); + dom.classList.add('NgxEditor__MenuItem'); + dom.classList.add('NgxEditor__MenuItem--Text'); const type = schema.nodes.code_block; - let command; + const command = toggleBlockType(type, schema.nodes.paragraph); dom.addEventListener('mousedown', (e: MouseEvent) => { e.preventDefault(); @@ -22,21 +22,16 @@ const codeMirror: ToolbarCustomMenuItem = (editorView) => { return; } - command = toggleBlockType(type, schema.nodes.paragraph); command(editorView.state, editorView.dispatch); }); - const update = (state: EditorState): void => { const isActive = isNodeActive(state, type); - let canExecute = true; - if (command) { - canExecute = command(state, null); - } + const canExecute = command(state, null); - dom.classList.toggle(`NgxEditor-MenuItem__Active`, isActive); - dom.classList.toggle(`disabled`, !canExecute); + dom.classList.toggle(`NgxEditor__MenuItem--Active`, isActive); + dom.classList.toggle(`NgxEditor--Disabled`, !canExecute); }; return { diff --git a/src/lib/ngx-editor.component.html b/src/lib/ngx-editor.component.html index 0a3e5c21..28fb0a45 100644 --- a/src/lib/ngx-editor.component.html +++ b/src/lib/ngx-editor.component.html @@ -1 +1 @@ -
+
diff --git a/src/lib/ngx-editor.component.scss b/src/lib/ngx-editor.component.scss index 9f703c7b..248d092d 100644 --- a/src/lib/ngx-editor.component.scss +++ b/src/lib/ngx-editor.component.scss @@ -1,10 +1,16 @@ -.ProseMirror { - outline: none; -} - $icon-size: 30px; -.NgxEditor-Wrapper { +$menubar-border-color: #ddd; + +$menu-item-border-radius: 2px; +$menu-item-hover-bg-color: #f1f1f1; +$menu-item-active-bg-color: #e8f0fe; +$menu-item-active-color: #1a73e8; + +$dropdown-menu-hover-bg-color: #f1f1f1; +$dropdown-item-active-bg-color: #f1f1f1; + +.NgxEditor { background: white; color: black; background-clip: padding-box; @@ -12,127 +18,145 @@ $icon-size: 30px; border: 2px solid rgba(0, 0, 0, 0.2); } -.NgxEditor-MenuBar { +.NgxEditor__Content { + padding: 0.5rem; + white-space: pre-wrap; + + p { + margin: 0; + margin-bottom: 0.7rem; + } +} + +.NgxEditor__MenuBar { display: flex; padding: 0.2rem; - border-bottom: 1px solid #ddd; + border-bottom: 1px solid $menubar-border-color; + cursor: default; +} - .NgxEditor-MenuItem { - display: flex; - align-items: center; - justify-content: center; +.NgxEditor__MenuItem { + $self: &; + border-radius: 2px; + display: flex; + align-items: center; + justify-content: center; - &:hover { - cursor: pointer; - background-color: #f1f1f1; - } + &:hover { + background-color: $menu-item-hover-bg-color; + } - &.NgxEditor-MenuItem__Icon { - height: $icon-size; - width: $icon-size; - transition: 0.3s ease-in-out; - border-radius: 2px; - margin-right: 2px; + &#{ $self }--Icon { + height: $icon-size; + width: $icon-size; + transition: 0.3s ease-in-out; + margin-right: 2px; + } + + &#{ $self }--Text { + padding: 0 0.3rem; + } - &.NgxEditor-MenuItem__Active { - background-color: #e8f0fe; + &#{ $self }--Active { + background-color: $menu-item-active-bg-color; + color: $menu-item-active-color; - svg { - fill: #1a73e8; - } - } + svg { + fill: $menu-item-active-color; } + } +} + +.NgxEditor--Disabled { + opacity: 0.5; + pointer-events: none; +} - &.NgxEditor-MenuItem__Dropdown-Wrapper { - position: relative; - max-width: 100px; - width: 100%; - justify-content: start; - - .NgxEditor-MenuItem__Dropdown { - display: flex; - align-items: center; - width: 100%; - - .NgxEditor-MenuItem__Dropdown-Text { - margin-left: 5px; - } - - .NgxEditor-MenuItem__Dropdown-Icon { - margin-left: auto; - position: relative; - top: 2px; - } - } - - .NgxEditor-MenuItem__Dropdown-Menu { - position: absolute; - left: 0; - top: 30px; - box-shadow: rgba(60, 64, 67, 0.15) 0px 2px 6px 2px; - border-radius: 4px; - background-color: white; - display: none; - z-index: 10; - - .NgxEditor-MenuItem__Dropdown-Item { - cursor: pointer; - padding: 0.5rem 1rem; - white-space: nowrap; - - &:hover, - &.NgxEditor-MenuItem__Active { - background-color: #f1f1f1; - } - } - } - - &.NgxEditor-MenuItem__Dropdown-Wrapper-Selected, - &.NgxEditor-MenuItem__Dropdown-Wrapper-Open { - background-color: #e8f0fe; - color: #1a73e8; - } - - &.NgxEditor-MenuItem__Dropdown-Wrapper-Open { - background-color: #e8f0fe; - color: #1a73e8; - - .NgxEditor-MenuItem__Dropdown-Icon { - svg { - fill: #1a73e8; - } - } - - .NgxEditor-MenuItem__Dropdown-Menu { - display: block; - color: initial; - } - } +.NgxEditor__Seperator { + border-left: 1px solid #ccc; + margin: 0 5px; +} + +.NgxEditor__Dropdown { + $self: &; + min-width: 4rem; + position: relative; + display: flex; + align-items: center; + + &:hover { + background-color: $dropdown-menu-hover-bg-color; + } + + #{ $self }__Text { + display: flex; + align-items: center; + padding: 0 0.3rem; + + &::after { + display: inline-block; + content: ""; + margin-left: 25px; + vertical-align: 0.255em; + border-top: 0.3rem solid; + border-right: 0.3rem solid transparent; + border-bottom: 0; + border-left: 0.3rem solid transparent; } } - .NgxEditor-MenuItem__Seperator { - border-left: 1px solid #ccc; - margin: 0 5px; + #{ $self }__DropdownMenu { + position: absolute; + left: 0; + top: 30px; + box-shadow: rgba(60, 64, 67, 0.15) 0px 2px 6px 2px; + border-radius: 4px; + background-color: white; + display: none; + z-index: 10; + width: 100%; } - .disabled { - opacity: 0.5; - pointer-events: none; + #{ $self }__Item { + padding: 0.5rem; + white-space: nowrap; + color: inherit; + + &:hover { + background-color: darken($dropdown-item-active-bg-color, 2%); + } } -} -.NgxEditor-Content { - padding: 0.5rem; - white-space: pre-wrap; + &#{ $self }--Selected, + &#{ $self }--Open { + background-color: $menu-item-active-bg-color; - p { - margin: 0; - margin-bottom: 0.7rem; + #{ $self }__Text { + color: $menu-item-active-color; + } + } + + #{ $self }--Active { + background-color: $dropdown-item-active-bg-color; + + &:hover { + background-color: darken($dropdown-item-active-bg-color, 4%); + } + } + + &#{ $self }--Open { + #{ $self }__DropdownMenu { + display: block; + } } } -.NgxEditor-Placeholder { +.NgxEditor__Placeholder { color: #6c757d; opacity: 1; } + +// prosemirror +.ProseMirror { + outline: none; +} diff --git a/src/lib/ngx-editor.component.spec.ts b/src/lib/ngx-editor.component.spec.ts index d0bac439..a46a3514 100644 --- a/src/lib/ngx-editor.component.spec.ts +++ b/src/lib/ngx-editor.component.spec.ts @@ -33,6 +33,6 @@ describe('NgxEditorComponent', () => { const compiled: DebugElement = fixture.debugElement; // expect menubar to be rendered - expect(compiled.query(By.css('.NgxEditor-MenuBar'))).toBeDefined(); + expect(compiled.query(By.css('.NgxEditor__MenuBar'))).toBeDefined(); }); }); diff --git a/src/lib/ngx-editor.component.ts b/src/lib/ngx-editor.component.ts index 179f2c61..a0533ba5 100644 --- a/src/lib/ngx-editor.component.ts +++ b/src/lib/ngx-editor.component.ts @@ -56,7 +56,6 @@ export class NgxEditorComponent implements ControlValueAccessor, OnInit, OnDestr private updateContent(value: object) { try { const doc = this.parseDoc(value); - const state = this.view.state; const tr = state.tr; tr.replaceWith(0, state.doc.content.size, doc); @@ -88,7 +87,7 @@ export class NgxEditorComponent implements ControlValueAccessor, OnInit, OnDestr nodeViews, dispatchTransaction: this.handleTransactions.bind(this), attributes: { - class: 'NgxEditor-Content' + class: 'NgxEditor__Content' }, }); } diff --git a/src/lib/prosemirror/plugins/menu/MenuBarView.ts b/src/lib/prosemirror/plugins/menu/MenuBarView.ts index ef3dc6bb..5a7ca4e0 100644 --- a/src/lib/prosemirror/plugins/menu/MenuBarView.ts +++ b/src/lib/prosemirror/plugins/menu/MenuBarView.ts @@ -24,7 +24,7 @@ class MenuBarView { render() { const menuDom = document.createElement('div'); - menuDom.className = 'NgxEditor-MenuBar'; + menuDom.className = 'NgxEditor__MenuBar'; const { update } = renderMenu(this.options, this.view, menuDom); this.updateMenuItems = update; diff --git a/src/lib/prosemirror/plugins/menu/menu.ts b/src/lib/prosemirror/plugins/menu/menu.ts index c302d16c..5c5f9851 100644 --- a/src/lib/prosemirror/plugins/menu/menu.ts +++ b/src/lib/prosemirror/plugins/menu/menu.ts @@ -20,7 +20,16 @@ import flatDeep from '../../../utils/flatDeep'; import menuItemsMeta, { MenuItemMeta } from './meta'; -const MENU_ITEM_CLASSNAME = 'NgxEditor-MenuItem'; +const SEPERATOR_CLASSNAME = 'NgxEditor__Seperator'; + +const MENU_ITEM_CLASSNAME = 'NgxEditor__MenuItem'; +const ACTIVE_MENU_ITEM_CLASSNAME = `${MENU_ITEM_CLASSNAME}--Active`; +const DISABLED_CLASSNAME = 'NgxEditor--Disabled'; + +const DROPDWON_ITEM_CLASSNAME = 'NgxEditor__Dropdown'; +const DROPWDOWN_OPEN_CLASSNAME = `${DROPDWON_ITEM_CLASSNAME}--Open`; +const ACTIVE_DROPDOWN_ITEM_CLASSNAME = `${DROPDWON_ITEM_CLASSNAME}--Active`; +const SELECTED_DROPDOWN_ITEM_CLASSNAME = `${DROPDWON_ITEM_CLASSNAME}--Selected`; const DROPDOWN_ITEMS = new Map(); DROPDOWN_ITEMS.set('heading', ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']); @@ -57,45 +66,43 @@ class DropDownView { getWrapperDom(): HTMLElement { let isDropdownOpen = false; - const dropdownWrapper = document.createElement('div'); + const dropdown = document.createElement('div'); const labels = this.options.labels; - dropdownWrapper.classList.add(MENU_ITEM_CLASSNAME); - dropdownWrapper.classList.add(`${MENU_ITEM_CLASSNAME}__Dropdown-Wrapper`); - - // create dropdown content - const dropdown = document.createElement('div'); - dropdown.classList.add(`${MENU_ITEM_CLASSNAME}__Dropdown`); + dropdown.classList.add(DROPDWON_ITEM_CLASSNAME); const dropdownText = document.createElement('div'); - dropdownText.classList.add(`${MENU_ITEM_CLASSNAME}__Dropdown-Text`); + dropdownText.classList.add(`${DROPDWON_ITEM_CLASSNAME}__Text`); dropdownText.textContent = labels[this.dropdownGroup]; - const dropdownIcon = document.createElement('div'); - dropdownIcon.classList.add(`${MENU_ITEM_CLASSNAME}__Dropdown-Icon`); - dropdownIcon.innerHTML = getIconSvg('arrow_drop_down'); - dropdown.appendChild(dropdownText); - dropdown.appendChild(dropdownIcon); - const dropdownOpenClassName = `${MENU_ITEM_CLASSNAME}__Dropdown-Wrapper-Open`; + // create dropdown list + const dropdownMenu = document.createElement('div'); + dropdownMenu.classList.add(`${DROPDWON_ITEM_CLASSNAME}__DropdownMenu`); const mouseDownHandler = (e: MouseEvent) => { e.preventDefault(); - if (!dropdownWrapper.contains(e.target as Node)) { + if (!dropdown.contains(e.target as Node)) { closeDropdown(); } }; - const openDropdown = () => { - dropdownWrapper.classList.add(dropdownOpenClassName); + const openDropdown = (e: MouseEvent) => { + const target = e.target as HTMLElement; + + if (dropdownMenu.contains(target)) { + return; + } + + dropdown.classList.add(DROPWDOWN_OPEN_CLASSNAME); isDropdownOpen = true; window.addEventListener('mousedown', mouseDownHandler); }; const closeDropdown = () => { - dropdownWrapper.classList.remove(dropdownOpenClassName); + dropdown.classList.remove(DROPWDOWN_OPEN_CLASSNAME); isDropdownOpen = false; window.removeEventListener('mousedown', mouseDownHandler); }; @@ -103,16 +110,12 @@ class DropDownView { dropdown.addEventListener('click', (e: MouseEvent) => { e.preventDefault(); if (!isDropdownOpen) { - openDropdown(); + openDropdown(e); } else { closeDropdown(); } }); - // create dropdown list - const dropdownList = document.createElement('div'); - dropdownList.classList.add(`${MENU_ITEM_CLASSNAME}__Dropdown-Menu`); - this.dropdownFields.forEach(dropdownItem => { const menuItem = menuItemsMeta[dropdownItem]; @@ -124,12 +127,14 @@ class DropDownView { const spec: MenuItemViewSpec = { classNames: [ - `${MENU_ITEM_CLASSNAME}__Dropdown-Item` + `${DROPDWON_ITEM_CLASSNAME}__Item` ], textContent: text, attrs: { title: text - } + }, + activeClass: ACTIVE_DROPDOWN_ITEM_CLASSNAME, + disabledClass: DISABLED_CLASSNAME }; const menuItemView = new MenuItemView(menuItem, this.editorView, spec); @@ -145,29 +150,25 @@ class DropDownView { const dropUpdate = (state: EditorState) => { update(state); - const selectedClass = `${MENU_ITEM_CLASSNAME}__Dropdown-Wrapper-Selected`; - // update the dropdown content heading when a class is selected - const activeEl = dropdownList.getElementsByClassName(`${MENU_ITEM_CLASSNAME}__Active`); + const activeEl = dropdownMenu.getElementsByClassName(ACTIVE_DROPDOWN_ITEM_CLASSNAME); if (activeEl.length) { const el = activeEl[0]; dropdownText.textContent = el.textContent; - dropdownWrapper.classList.add(selectedClass); + dropdown.classList.add(SELECTED_DROPDOWN_ITEM_CLASSNAME); } else { // restore default value dropdownText.textContent = labels[this.dropdownGroup]; - dropdownWrapper.classList.remove(selectedClass); + dropdown.classList.remove(SELECTED_DROPDOWN_ITEM_CLASSNAME); } }; - dropdownList.appendChild(dom); + dropdownMenu.appendChild(dom); this.updates.push(dropUpdate); }); - dropdownWrapper.appendChild(dropdown); - dropdownWrapper.appendChild(dropdownList); - - return dropdownWrapper; + dropdown.appendChild(dropdownMenu); + return dropdown; } render() { @@ -198,6 +199,8 @@ class MenuItemView { const { schema } = this.editorView.state; const { command } = this.setupCommandListeners(); + const { activeClass, disabledClass } = this.spec; + const update = (state: EditorState): void => { const menuItem = this.menuItem; let isActive = false; @@ -214,8 +217,8 @@ class MenuItemView { isActive = isNodeActive(state, type, menuItem.attrs); } - dom.classList.toggle(`${MENU_ITEM_CLASSNAME}__Active`, isActive); - dom.classList.toggle(`disabled`, !canExecute); + dom.classList.toggle(activeClass, isActive); + dom.classList.toggle(disabledClass, !canExecute); }; return { @@ -289,7 +292,7 @@ class MenuItemView { const getSeperatorDom = (): HTMLElement => { const div = document.createElement('div'); - div.className = `${MENU_ITEM_CLASSNAME}__Seperator`; + div.className = SEPERATOR_CLASSNAME; return div; }; @@ -330,13 +333,14 @@ export const renderMenu = (options: MenuOptions, editorView: EditorView, menuDom const spec: MenuItemViewSpec = { classNames: [ MENU_ITEM_CLASSNAME, - `${MENU_ITEM_CLASSNAME}__Icon`, - `${MENU_ITEM_CLASSNAME}__${menuItem.key}` + `${MENU_ITEM_CLASSNAME}--Icon`, ], innerHTML: getIconSvg(menuItem.icon), attrs: { title: labels[menuItem.i18nKey] - } + }, + activeClass: ACTIVE_MENU_ITEM_CLASSNAME, + disabledClass: DISABLED_CLASSNAME }; const menuItemView = new MenuItemView(menuItem, editorView, spec); diff --git a/src/lib/prosemirror/plugins/placeholder.ts b/src/lib/prosemirror/plugins/placeholder.ts index 08e385e9..50495bd7 100644 --- a/src/lib/prosemirror/plugins/placeholder.ts +++ b/src/lib/prosemirror/plugins/placeholder.ts @@ -2,6 +2,7 @@ import { Plugin, EditorState, PluginKey } from 'prosemirror-state'; import { DecorationSet, Decoration } from 'prosemirror-view'; const DEFAULT_PLACEHOLDER = 'Type Here...'; +const PLACEHOLDER_CLASSNAME = 'NgxEditor__Placeholder'; function placeholderPlugin(text: string = DEFAULT_PLACEHOLDER) { return new Plugin({ @@ -12,7 +13,7 @@ function placeholderPlugin(text: string = DEFAULT_PLACEHOLDER) { if (doc.childCount === 1 && doc.firstChild.isTextblock && doc.firstChild.content.size === 0) { const placeHolderEl = document.createElement('span'); - placeHolderEl.classList.add('NgxEditor-Placeholder'); + placeHolderEl.classList.add(PLACEHOLDER_CLASSNAME); placeHolderEl.textContent = text; return DecorationSet.create(doc, [Decoration.widget(1, placeHolderEl)]); } diff --git a/src/lib/types.ts b/src/lib/types.ts index 7ca3df76..83576c9a 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -16,6 +16,8 @@ export interface MenuItemViewSpec { innerHTML?: string; textContent?: string; attrs?: { [key: string]: string }; + activeClass: string; + disabledClass: string; } export interface NodeViews {