diff --git a/editors/editoria/src/EditorConfig.js b/editors/editoria/src/EditorConfig.js index d4dda8cb92fc8140b6776811660a85015f6e3b49..c3cffe3b8d4c868a7f87ec5e15674044eb61569f 100644 --- a/editors/editoria/src/EditorConfig.js +++ b/editors/editoria/src/EditorConfig.js @@ -100,7 +100,7 @@ const plugins = [ }) ]; -const services = [new LayoutService(), new LinkService()]; +const services = [new LinkService()]; // Add Rules const rules = [emDash, ellipsis]; diff --git a/editors/editoria/src/Editoria.js b/editors/editoria/src/Editoria.js index 4c68368d66360e3446bf8d74306bea394d1b5617..c2f30f2cfcf365c4aae528500cdf169422e1c99a 100644 --- a/editors/editoria/src/Editoria.js +++ b/editors/editoria/src/Editoria.js @@ -1,8 +1,10 @@ import React, { Fragment } from "react"; import styled, { createGlobalStyle } from "styled-components"; +import { EditoriaLayout } from "wax-prosemirror-layouts"; import { Wax } from "wax-prosemirror-core"; import { schema, keys, plugins, rules, services } from "./EditorConfig"; +import { menu } from "./config"; import text from "./text"; @@ -51,11 +53,13 @@ const Editoria = () => ( <Fragment> <GlobalStyle /> <StyledWax + config={menu} options={options} autoFocus placeholder="Type Something..." fileUpload={file => renderImage(file)} value="" + layout={EditoriaLayout} user={user} /> </Fragment> diff --git a/editors/editoria/src/config/index.js b/editors/editoria/src/config/index.js new file mode 100644 index 0000000000000000000000000000000000000000..bf58df0f49bc4730e365ff00cc870334fdc1e113 --- /dev/null +++ b/editors/editoria/src/config/index.js @@ -0,0 +1 @@ +export { default as menu } from "./menu"; diff --git a/editors/editoria/src/config/menu.js b/editors/editoria/src/config/menu.js new file mode 100644 index 0000000000000000000000000000000000000000..69523798b2e2fa49ac1f9e04617c1bebb627524f --- /dev/null +++ b/editors/editoria/src/config/menu.js @@ -0,0 +1,13 @@ +export default { + menus: [ + { + templateArea: "topBar", + tools: ["redo", "undo"], + groups: [ + "redo-undo", + "annotations", + { group: "Annotation", exclude: [], include: [] } + ] + } + ] +}; diff --git a/editors/editoria/src/config/menu1.js b/editors/editoria/src/config/menu1.js new file mode 100644 index 0000000000000000000000000000000000000000..4752caf4e4ff833d47d20a3cff25fff1bb6fb405 --- /dev/null +++ b/editors/editoria/src/config/menu1.js @@ -0,0 +1,307 @@ +import React from "react"; +import { v4 as uuid } from "uuid"; +import { + joinUp, + lift, + setBlockType, + toggleMark, + wrapIn, + selectParentNode +} from "prosemirror-commands"; + +import { addColumnBefore } from "prosemirror-tables"; + +import { redo, undo } from "prosemirror-history"; +import { wrapInList } from "prosemirror-schema-list"; + +import icons from "../icons/icons"; +//Components +import Button from "../components/Button"; +import TableDropDown from "../components/TableDropDown"; +import ImageUpload from "../components/ImageUpload"; + +import { + markActive, + blockActive, + canInsert, + promptForURL, + createTable +} from "./MainMenuCommands"; + +export default [ + { + groupName: "undo-redo", + tools: [ + { + name: "undo", + title: "Undo last change", + content: icons.undo, + enable: undo, + run: undo, + select: state => true, + menu: props => <Button key={uuid()} {...props} /> + }, + { + name: "redo", + title: "Redo last undone change", + content: icons.redo, + enable: redo, + run: redo, + select: state => true, + menu: props => <Button key={uuid()} {...props} /> + } + ] + }, + { + groupName: "Annotations", + tools: [ + { + name: "em", + title: "Toggle emphasis", + content: icons.em, + active: state => { + return markActive(state.config.schema.marks.em)(state); + }, + run(state, dispatch) { + toggleMark(state.config.schema.marks.em)(state, dispatch); + }, + select: state => true, + menu: props => <Button key={uuid()} {...props} /> + }, + { + name: "strong", + title: "Toggle strong", + content: icons.strong, + active: state => { + return markActive(state.config.schema.marks.strong)(state); + }, + run(state, dispatch) { + toggleMark(state.config.schema.marks.strong)(state, dispatch); + }, + select: state => true, + menu: props => <Button key={uuid()} {...props} /> + }, + { + name: "code", + title: "Toggle code", + content: icons.code, + active: state => { + return markActive(state.config.schema.marks.code)(state); + }, + run(state, dispatch) { + toggleMark(state.config.schema.marks.code)(state, dispatch); + }, + + select: state => true, + menu: props => <Button key={uuid()} {...props} /> + }, + { + name: "small_caps", + title: "Toggle Small Caps", + content: icons.small_caps, + active: state => { + return markActive(state.config.schema.marks.small_caps)(state); + }, + run(state, dispatch) { + toggleMark(state.config.schema.marks.small_caps)(state, dispatch); + }, + + select: state => true, + menu: props => <Button key={uuid()} {...props} /> + }, + { + name: "subscript", + title: "Toggle subscript", + content: icons.subscript, + active: state => { + return markActive(state.config.schema.marks.subscript)(state); + }, + run(state, dispatch) { + toggleMark(state.config.schema.marks.subscript)(state, dispatch); + }, + select: state => true, + menu: props => <Button key={uuid()} {...props} /> + }, + { + name: "superscript", + title: "Toggle superscript", + content: icons.superscript, + active: state => { + return markActive(state.config.schema.marks.superscript)(state); + }, + run(state, dispatch) { + toggleMark(state.config.schema.marks.superscript)(state, dispatch); + }, + select: state => true, + menu: props => <Button key={uuid()} {...props} /> + }, + { + name: "underline", + title: "Toggle underline", + content: icons.underline, + active: state => { + return markActive(state.config.schema.marks.underline)(state); + }, + run(state, dispatch) { + toggleMark(state.config.schema.marks.underline)(state, dispatch); + }, + + select: state => true, + menu: props => <Button key={uuid()} {...props} /> + }, + { + name: "strikethrough", + title: "Toggle strikethrough", + content: icons.strikethrough, + active: state => { + return markActive(state.config.schema.marks.strikethrough)(state); + }, + run(state, dispatch) { + toggleMark(state.config.schema.marks.strikethrough)(state, dispatch); + }, + select: state => true, + menu: props => <Button key={uuid()} {...props} /> + }, + { + name: "link", + title: "Add or remove link", + content: icons.link, + active: state => { + return markActive(state.config.schema.marks.link)(state); + }, + enable: state => !state.selection.empty, + run(state, dispatch) { + if (markActive(state.config.schema.marks.link)(state)) { + toggleMark(state.config.schema.marks.link)(state, dispatch); + return true; + } + + const href = promptForURL(); + if (!href) return false; + + toggleMark(state.config.schema.marks.link, { href })(state, dispatch); + }, + select: state => true, + menu: props => <Button key={uuid()} {...props} /> + }, + { + name: "blockquote", + title: "Wrap in block quote", + content: icons.blockquote, + active: state => { + return blockActive(state.config.schema.nodes.blockquote)(state); + }, + enable: state => { + return wrapIn(state.config.schema.nodes.blockquote)(state); + }, + run(state, dispatch) { + wrapIn(state.config.schema.nodes.blockquote)(state, dispatch); + }, + select: state => true, + menu: props => <Button key={uuid()} {...props} /> + }, + { + name: "bullet_list", + title: "Wrap in bullet list", + content: icons.bullet_list, + active: state => { + return blockActive(state.config.schema.nodes.bullet_list)(state); + }, + enable: state => { + return wrapInList(state.config.schema.nodes.bullet_list)(state); + }, + run(state, dispatch) { + wrapInList(state.config.schema.nodes.bullet_list)(state, dispatch); + }, + select: state => true, + menu: props => <Button key={uuid()} {...props} /> + }, + { + name: "ordered_list", + title: "Wrap in ordered list", + content: icons.ordered_list, + active: state => { + return blockActive(state.config.schema.nodes.ordered_list)(state); + }, + enable: state => { + return wrapInList(state.config.schema.nodes.ordered_list)(state); + }, + run(state, dispatch) { + wrapInList(state.config.schema.nodes.ordered_list)(state, dispatch); + }, + + select: state => true, + menu: props => <Button key={uuid()} {...props} /> + }, + { + name: "lift", + title: "Lift out of enclosing block", + content: icons.lift, + enable: lift, + run: lift, + select: state => lift(state), + menu: props => <Button key={uuid()} {...props} /> + }, + { + name: "join_up", + title: "Join with above block", + content: icons.join_up, + select: state => joinUp(state), + enable: joinUp, + run: joinUp, + menu: props => <Button key={uuid()} {...props} /> + }, + { + name: "image", + title: "Insert image", + content: icons.image, + enable: state => { + return canInsert(state.config.schema.nodes.image)(state); + }, + select: state => true, + run: option => true, + menu: props => <ImageUpload key={uuid()} {...props} /> + } + ] + }, + { + groupName: "Table", + tools: [ + { + name: "table", + title: "Insert table", + content: icons.table, + enable: state => { + return canInsert(state.config.schema.nodes.table)(state); + }, + run: (state, dispatch) => { + return createTable(state, dispatch); + }, + select: state => true, + menu: props => <Button key={uuid()} {...props} /> + }, + { + name: "tableDropDownOptions", + content: "table", + run: option => true, + title: "Select Options", + select: state => addColumnBefore(state), + menu: props => <TableDropDown key={uuid()} {...props} /> + } + ] + } +]; + +{ + [ + { + templateArea: "topBar", + tools: ["redo", "undo"], + groups: [ + "redo-undo", + "annotations", + { group: "Annotation", exclude: [], include: [] } + ] + } + ]; +} diff --git a/wax-prosemirror-core/src/Application.js b/wax-prosemirror-core/src/Application.js new file mode 100644 index 0000000000000000000000000000000000000000..fc602e1b3ae6ceffaf08a16ea928ba9bfd58f574 --- /dev/null +++ b/wax-prosemirror-core/src/Application.js @@ -0,0 +1,68 @@ +import { Container } from "inversify"; +import "reflect-metadata"; +import Config from "./Config/Config"; +import defaultConfig from "./Config/defaultConfig"; + +export default class Application { + container = {}; + config = {}; + constructor(container) { + this.container = container; + } + + registerServices() { + this.config.get("services").map(service => { + /* + set App to every service + so services can have access to containers and config + */ + service.setApp(this); + + if (service.register) { + service.register(); + } + }); + } + + setConfig(config) { + this.config = this.container.get("Config"); + + Object.keys(config).forEach(conf => { + this.config.pushToArray(conf, config[conf]); + }); + } + + bootServices() { + const services = this.config.get("services"); + services.map(plugin => { + if (plugin.boot) { + plugin.boot(); + } + }); + } + + static create(config) { + /* + Create Container + */ + const container = new Container(); + + /* + Set base bindings for the App to work + */ + container.bind("Wax").toFactory(() => new Application(container)); + container.bind("config").toConstantValue(defaultConfig); + container + .bind("Config") + .to(Config) + .inSingletonScope(); + + /* + Start the App + */ + const app = container.get("Wax"); + app.setConfig(config); + app.registerServices(); + return app; + } +} diff --git a/wax-prosemirror-core/src/Config/Config.js b/wax-prosemirror-core/src/Config/Config.js new file mode 100644 index 0000000000000000000000000000000000000000..a5b349e101d0cd9ec303e4ddd102660537772850 --- /dev/null +++ b/wax-prosemirror-core/src/Config/Config.js @@ -0,0 +1,29 @@ +import { set, get, isArrayLikeObject } from "lodash"; +import { injectable, inject } from "inversify"; + +@injectable() +export default class Config { + config = {}; + constructor(@inject("config") config) { + this.config = config; + } + + set(key, value) { + set(this.config, key, value); + return this.config; + } + + get(key) { + return get(this.config, key); + } + + pushToArray(key, value) { + const oldValue = this.get(key); + let newValue = value; + if (oldValue && isArrayLikeObject(oldValue)) { + newValue = oldValue.push(value); + } + + return this.set(key, newValue); + } +} diff --git a/wax-prosemirror-core/src/Config/defaultConfig.js b/wax-prosemirror-core/src/Config/defaultConfig.js new file mode 100644 index 0000000000000000000000000000000000000000..d1138e2f19fb262050438b463c99c621c7f4eecd --- /dev/null +++ b/wax-prosemirror-core/src/Config/defaultConfig.js @@ -0,0 +1,5 @@ +import LayoutService from "../services/LayoutService/LayoutService"; + +export default { + services: [new LayoutService()] +}; diff --git a/wax-prosemirror-core/src/Wax.js b/wax-prosemirror-core/src/Wax.js index 7622ff813b195580dbb0cbb2c40aa6d052b993b8..cdca230c39e34bfd27f1739775df2d44f3331990 100644 --- a/wax-prosemirror-core/src/Wax.js +++ b/wax-prosemirror-core/src/Wax.js @@ -1,7 +1,6 @@ import React, { Component } from "react"; import WaxProvider from "./ioc-react"; -import container from "./ioc"; -import LayoutService from "./services/LayoutService/LayoutService"; +import Application from "./Application"; import debounce from "lodash/debounce"; import styled from "styled-components"; @@ -42,21 +41,16 @@ const LayoutWrapper = styled.div` `; class Wax extends Component { + application = {}; constructor(props) { super(props); - const { options } = props; - const { services } = options; - - services.push(new LayoutService()); - services.map(plugin => { - if (plugin.register) { - plugin.register(); - } - }); + const { config } = props; + this.application = Application.create(config); } + componentWillMount() { const { value, onChange, options } = this.props; - const { schema, plugins, keys, rules, services } = options; + const { schema, plugins, keys, rules } = options; const WaxOnchange = onChange ? onChange : value => true; const WaxShortCuts = keys @@ -84,11 +78,7 @@ class Wax extends Component { const serialize = serializer(schema); this.WaxOptions.doc = parse(editorContent); - services.map(plugin => { - if (plugin.boot) { - plugin.boot(); - } - }); + this.application.bootServices(); this.onChange = debounce( value => { @@ -116,7 +106,7 @@ class Wax extends Component { layout } = this.props; - const Layout = container.get("Layout"); + const Layout = this.application.container.get("Layout"); if (layout) { Layout.setLayout(layout); } @@ -137,7 +127,7 @@ class Wax extends Component { user={user} > {({ view, editor }) => ( - <WaxProvider view={view} container={container}> + <WaxProvider view={view} app={this.application}> <WaxRender editor={editor} /> </WaxProvider> )} diff --git a/wax-prosemirror-core/src/config/Config/config.js b/wax-prosemirror-core/src/config/Config/config.js new file mode 100644 index 0000000000000000000000000000000000000000..3f6296f925705490bcd8d42b5ed9b2b413266f6b --- /dev/null +++ b/wax-prosemirror-core/src/config/Config/config.js @@ -0,0 +1,6 @@ +import LayoutService from "../services/LayoutService/LayoutService"; +import ConfigService from "./ConfigService"; + +export default { + services: [new LayoutService(), new ConfigService()] +}; diff --git a/wax-prosemirror-core/src/ioc-react.js b/wax-prosemirror-core/src/ioc-react.js index f66f07d7bdc1e28a8d12895947ff1f82ccfb5adf..538a79c517ef042c8f5121c3431d7bff1e3ac58b 100644 --- a/wax-prosemirror-core/src/ioc-react.js +++ b/wax-prosemirror-core/src/ioc-react.js @@ -1,15 +1,17 @@ import React, { useContext } from "react"; -const WaxContext = React.createContext({ view: null, container: null }); +const WaxContext = React.createContext({ view: null, app: null }); export default props => ( - <WaxContext.Provider value={{ container: props.container, view: props.view }}> + <WaxContext.Provider value={{ app: props.app, view: props.view }}> {props.children} </WaxContext.Provider> ); export function useInjection(identifier) { - const { container } = useContext(WaxContext); + const { + app: { container } + } = useContext(WaxContext); if (!container) { throw new Error(); diff --git a/wax-prosemirror-core/src/ioc.js b/wax-prosemirror-core/src/ioc.js deleted file mode 100644 index 5e07d5aee6d12d5d1dfabde867e3f707f3f4ca0b..0000000000000000000000000000000000000000 --- a/wax-prosemirror-core/src/ioc.js +++ /dev/null @@ -1,6 +0,0 @@ -import { Container } from "inversify"; -import "reflect-metadata"; - -const container = new Container(); - -export default container; diff --git a/wax-prosemirror-core/src/services/Config/config.js b/wax-prosemirror-core/src/services/Config/config.js new file mode 100644 index 0000000000000000000000000000000000000000..7ffbb9afe1bbf14b3d41e780efa41f944f900047 --- /dev/null +++ b/wax-prosemirror-core/src/services/Config/config.js @@ -0,0 +1,6 @@ +import LayoutService from "../../services/LayoutService/LayoutService"; +import ConfigService from "./ConfigService"; + +export default { + services: [new LayoutService(), new ConfigService()] +}; diff --git a/wax-prosemirror-core/src/services/LayoutService/LayoutService.js b/wax-prosemirror-core/src/services/LayoutService/LayoutService.js index cfe59837dbb6e485533b01c44b89fb5d143ebd4d..38183390157414490b8c1d55831050cb81c56625 100644 --- a/wax-prosemirror-core/src/services/LayoutService/LayoutService.js +++ b/wax-prosemirror-core/src/services/LayoutService/LayoutService.js @@ -3,9 +3,6 @@ import Layout from "./Layout"; export default class LayoutService extends Service { name = "LayoutService"; - boot() { - const Layout = this.container.get("Layout"); - } register() { this.container .bind("Layout") diff --git a/wax-prosemirror-core/src/services/MenuService/Menu.js b/wax-prosemirror-core/src/services/MenuService/Menu.js new file mode 100644 index 0000000000000000000000000000000000000000..b4b09ca10ad5276a21a7293e7aa5ce44a8470409 --- /dev/null +++ b/wax-prosemirror-core/src/services/MenuService/Menu.js @@ -0,0 +1,27 @@ +export default class Menu { + groups = new Map(); + constructor(config = []) { + + this.addGroup() + } + + addGroup(name, value) { + this.groups.set(name, value); + } + +} + + +{ + groupNanme : "Annotation", + tools: [ + {},{} + ] +} +, +{ + groupNanme : "Annotation", + tools: [ + {},{} + ] +} \ No newline at end of file diff --git a/wax-prosemirror-core/src/services/MenuService/MenuService.js b/wax-prosemirror-core/src/services/MenuService/MenuService.js new file mode 100644 index 0000000000000000000000000000000000000000..ed98e9525fe5e2e0434930bb3b2af4cde6ac83b1 --- /dev/null +++ b/wax-prosemirror-core/src/services/MenuService/MenuService.js @@ -0,0 +1,10 @@ +import Menu from "./Menu"; +export default class MenuService extends Services { + boot() {} + register() { + this.container + .bind("Menu") + .to(Menu) + .inSingletonScope(); + } +} diff --git a/wax-prosemirror-core/src/services/Service.js b/wax-prosemirror-core/src/services/Service.js index 63ce5dad6b2b790a6bca2ee393c0dca0984d5a5c..a7acc31ebe3a86b8fe1fc827b6c695e80650bd1e 100644 --- a/wax-prosemirror-core/src/services/Service.js +++ b/wax-prosemirror-core/src/services/Service.js @@ -1,8 +1,15 @@ -import Container from "../ioc"; - export default class Service { - constructor() { - this.container = Container; + app = {}; + setApp(app) { + this.app = app; + } + + get container() { + return this.app.container; + } + + get config() { + return this.app.config; } boot() {} diff --git a/wax-prosemirror-plugins/src/LinkService/LinkService.js b/wax-prosemirror-plugins/src/LinkService/LinkService.js index dd405e79734e27f8e4ff9e8c36957abb53f17114..4574ce001631137c96cd9eb6ea22c62fb199eb79 100644 --- a/wax-prosemirror-plugins/src/LinkService/LinkService.js +++ b/wax-prosemirror-plugins/src/LinkService/LinkService.js @@ -2,6 +2,7 @@ import LinkPlugin from "./LinkPlugin"; import Service from "wax-prosemirror-core/src/services/Service"; import find from "./pmPlugins/find"; import placeholder from "./pmPlugins/placeholder"; +import { config } from "@fortawesome/fontawesome"; export default class myLinkPluginService extends Service { name = "LinkPlugin";