### In-Progress
-- Modal Dialogs for details such as links
- - Got dialog + form + input ready
- - Next stage is creating a button (eg, anchor insert) which toggles/shows dialog box.
- - Dialog box should attach at bottom of dom (Prevent z-index issues).
- - At some level a layer is needed to wire up the existing components.
-
### Features
- Tables
-- Links
- Images
- Image Resizing in editor
- Drawings
- Color picker buttons should be split, with button to re-apply last selected color.
- Color picker options should change color if different instead of remove.
- Clear formatting, If no selection range, clear the formatting of parent block.
- - If no marks, clear the block type if text type?
\ No newline at end of file
+ - If no marks, clear the block type if text type?
+- Remove links button? (Action already in place if link href is empty).
+- Links - Limit target attribute options and validate URL.
+- Links - Integrate entity picker.
\ No newline at end of file
if (this.options.closer) {
this.options.closer();
}
-
- if (this.closeMouseDownListener) {
- this.wrap.removeEventListener("click", this.closeMouseDownListener);
- }
}
}
// holding an object that can be used as the `icon` option to
// `MenuItem`.
export const icons = {
- join: {
- width: 800, height: 900,
- path: "M0 75h800v125h-800z M0 825h800v-125h-800z M250 400h100v-100h100v100h100v100h-100v100h-100v-100h-100z"
- },
- lift: {
- width: 1024, height: 1024,
- path: "M219 310v329q0 7-5 12t-12 5q-8 0-13-5l-164-164q-5-5-5-13t5-13l164-164q5-5 13-5 7 0 12 5t5 12zM1024 749v109q0 7-5 12t-12 5h-987q-7 0-12-5t-5-12v-109q0-7 5-12t12-5h987q7 0 12 5t5 12zM1024 530v109q0 7-5 12t-12 5h-621q-7 0-12-5t-5-12v-109q0-7 5-12t12-5h621q7 0 12 5t5 12zM1024 310v109q0 7-5 12t-12 5h-621q-7 0-12-5t-5-12v-109q0-7 5-12t12-5h621q7 0 12 5t5 12zM1024 91v109q0 7-5 12t-12 5h-987q-7 0-12-5t-5-12v-109q0-7 5-12t12-5h987q7 0 12 5t5 12z"
- },
- selectParentNode: {text: "\u2b1a", css: "font-weight: bold"},
undo: {
width: 24, height: 24,
path: "M12.5 8c-2.65 0-5.05.99-6.9 2.6L2 7v9h9l-3.62-3.62c1.39-1.16 3.16-1.88 5.12-1.88 3.54 0 6.55 2.31 7.6 5.5l2.37-.78C21.08 11.03 17.15 8 12.5 8z"
width: 24, height: 24,
path: "M10 4v3h2.21l-3.42 8H6v3h8v-3h-2.21l3.42-8H18V4z"
},
- code: {
- width: 896, height: 1024,
- path: "M608 192l-96 96 224 224-224 224 96 96 288-320-288-320zM288 192l-288 320 288 320 96-96-224-224 224-224-96-96z"
- },
link: {
- width: 951, height: 1024,
- path: "M832 694q0-22-16-38l-118-118q-16-16-38-16-24 0-41 18 1 1 10 10t12 12 8 10 7 14 2 15q0 22-16 38t-38 16q-8 0-15-2t-14-7-10-8-12-12-10-10q-18 17-18 41 0 22 16 38l117 118q15 15 38 15 22 0 38-14l84-83q16-16 16-38zM430 292q0-22-16-38l-117-118q-16-16-38-16-22 0-38 15l-84 83q-16 16-16 38 0 22 16 38l118 118q15 15 38 15 24 0 41-17-1-1-10-10t-12-12-8-10-7-14-2-15q0-22 16-38t38-16q8 0 15 2t14 7 10 8 12 12 10 10q18-17 18-41zM941 694q0 68-48 116l-84 83q-47 47-116 47-69 0-116-48l-117-118q-47-47-47-116 0-70 50-119l-50-50q-49 50-118 50-68 0-116-48l-118-118q-48-48-48-116t48-116l84-83q47-47 116-47 69 0 116 48l117 118q47 47 47 116 0 70-50 119l50 50q49-50 118-50 68 0 116 48l118 118q48 48 48 116z"
+ width: 24, height: 24,
+ path: "M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"
},
bullet_list: {
width: 24, height: 24,
width: 24, height: 24,
path: "M22,7h-9v2h9V7z M22,15h-9v2h9V15z M5.54,11L2,7.46l1.41-1.41l2.12,2.12l4.24-4.24l1.41,1.41L5.54,11z M5.54,19L2,15.46 l1.41-1.41l2.12,2.12l4.24-4.24l1.41,1.41L5.54,19z"
},
- blockquote: {
- width: 640, height: 896,
- path: "M0 448v256h256v-256h-128c0 0 0-128 128-128v-128c0 0-256 0-256 256zM640 320v-128c0 0-256 0-256 256v256h256v-256h-128c0 0 0-128 128-128z"
- },
underline: {
width: 24, height: 24,
path: "M12 17c3.31 0 6-2.69 6-6V3h-2.5v8c0 1.93-1.57 3.5-3.5 3.5S8.5 12.93 8.5 11V3H6v8c0 3.31 2.69 6 6 6zm-7 2v2h14v-2H5z"
import DialogForm from "./DialogForm";
import DialogInput from "./DialogInput";
+import itemAnchorButtonItem from "./item-anchor-button";
+
function cmdItem(cmd, options) {
const passedOptions = {
];
const inserts = [
+ itemAnchorButtonItem(),
insertBlockBeforeItem(schema.nodes.horizontal_rule, {
title: "Horizontal Rule",
icon: icons.horizontal_rule,
}),
];
-function getMarkAttribute(markType, attribute) {
- return function(state) {
- const marks = state.selection.$head.marks();
- for (const mark of marks) {
- if (mark.type === markType) {
- return mark.attrs[attribute];
- }
- }
-
- return null;
- };
-}
-
-let box = new DialogBox([
- new DialogForm([
- new DialogInput({
- label: 'URL',
- id: 'url',
- value: getMarkAttribute(schema.marks.link, 'href'),
- }),
- new DialogInput({
- label: 'Title',
- id: 'title',
- value: getMarkAttribute(schema.marks.link, 'title'),
- })
- ], {
- canceler: () => box.close(),
- action: (data) => console.log('submit', data),
- }),
-], {label: 'Insert Link', closer: () => {console.log('close')}});
-
const menu = menuBar({
floating: false,
content: [
lists,
inserts,
utilities,
- [box]
],
});
--- /dev/null
+import DialogBox from "./DialogBox";
+import DialogForm from "./DialogForm";
+import DialogInput from "./DialogInput";
+import schema from "../schema";
+
+import {MenuItem} from "./menu";
+import {icons} from "./icons";
+
+
+function getMarkAttribute(markType, attribute) {
+ return function (state) {
+ const marks = state.selection.$head.marks();
+ for (const mark of marks) {
+ if (mark.type === markType) {
+ return mark.attrs[attribute];
+ }
+ }
+
+ return null;
+ };
+}
+
+function getLinkDialog(submitter, closer) {
+ return new DialogBox([
+ new DialogForm([
+ new DialogInput({
+ label: 'URL',
+ id: 'href',
+ value: getMarkAttribute(schema.marks.link, 'href'),
+ }),
+ new DialogInput({
+ label: 'Title',
+ id: 'title',
+ value: getMarkAttribute(schema.marks.link, 'title'),
+ }),
+ new DialogInput({
+ label: 'Target',
+ id: 'target',
+ value: getMarkAttribute(schema.marks.link, 'target'),
+ })
+ ], {
+ canceler: closer,
+ action: submitter,
+ }),
+ ], {
+ label: 'Insert Link',
+ closer: closer,
+ });
+}
+
+function applyLink(formData, state, dispatch) {
+ const selection = state.selection;
+ const attrs = Object.fromEntries(formData);
+ if (dispatch) {
+ const tr = state.tr;
+
+ if (attrs.href) {
+ tr.addMark(selection.from, selection.to, schema.marks.link.create(attrs));
+ } else {
+ tr.removeMark(selection.from, selection.to, schema.marks.link);
+ }
+ dispatch(tr);
+ }
+ return true;
+}
+
+function onPress(state, dispatch, view, e) {
+ const dialog = getLinkDialog((data) => {
+ applyLink(data, state, dispatch);
+ dom.remove();
+ }, () => {
+ dom.remove();
+ })
+
+ const {dom, update} = dialog.render(view);
+ update(state);
+ document.body.appendChild(dom);
+}
+
+function anchorButtonItem() {
+ return new MenuItem({
+ title: "Insert/Edit Anchor Link",
+ run: onPress,
+ enable: state => true,
+ icon: icons.link,
+ });
+}
+
+export default anchorButtonItem;
\ No newline at end of file
const link = {
attrs: {
href: {},
- title: {default: null}
+ title: {default: null},
+ target: {default: null}
},
inclusive: false,
parseDOM: [{
tag: "a[href]", getAttrs: function getAttrs(dom) {
- return {href: dom.getAttribute("href"), title: dom.getAttribute("title")}
+ return {
+ href: dom.getAttribute("href"),
+ title: dom.getAttribute("title"),
+ target: dom.getAttribute("target"),
+ }
}
}],
toDOM: function toDOM(node) {
const ref = node.attrs;
const href = ref.href;
const title = ref.title;
- return ["a", {href: href, title: title}, 0]
+ const target = ref.target;
+ return ["a", {href, title, target}, 0]
}
};