Skip to content

Commit

Permalink
feat: 🚧 support indent, outdent
Browse files Browse the repository at this point in the history
  • Loading branch information
Leecason committed Dec 3, 2019
1 parent f9a3298 commit 7451271
Show file tree
Hide file tree
Showing 9 changed files with 167 additions and 5 deletions.
8 changes: 8 additions & 0 deletions src/components/ElTiptap.vue
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,14 @@ export default {
*[data-text-align=justify] {
text-align: justify!important;
}
@for $i from 1 through 7 /* max-indent */ {
$indent-margin-base: 30px;
*[data-indent="#{$i}"] {
margin-left: $indent-margin-base * $i!important;
}
}
}
}
</style>
13 changes: 13 additions & 0 deletions src/components/MenuBar/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,18 @@
icon="list-ol"
/>

<command-button
:command="editorContext.commands.indent"
tooltip="Indent"
icon="indent"
/>

<command-button
:command="editorContext.commands.outdent"
tooltip="Outdent"
icon="outdent"
/>

<command-button
:command="editorContext.commands.horizontal_rule"
tooltip="Horizontal line"
Expand Down Expand Up @@ -162,6 +174,7 @@ export default {
background-color: #F5F7FA;
border-bottom: 1px solid #EBEEF5;
display: flex;
flex-wrap: wrap;
padding: 5px;
}
}
Expand Down
102 changes: 102 additions & 0 deletions src/extensions/indent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { Extension } from 'tiptap';
import { TextSelection, AllSelection } from 'prosemirror-state';

import { clamp } from '../utils/shared';
import { isListNode } from '../utils/list';

export default class Indent extends Extension {
get name () {
return 'indent';
}

get defaultOptions () {
return {
minIndent: 0,
maxIndent: 7,
};
}

commands () {
return {
indent: () => this.createIndentCommand(1),
outdent: () => this.createIndentCommand(-1),
};
}

keys () {
return {
Tab: this.createIndentCommand(1),
'Shift-Tab': this.createIndentCommand(-1),
};
}

createIndentCommand (delta) {
return (state, dispatch) => {
const { selection } = state;
let { tr } = state;
tr = tr.setSelection(selection);
tr = this.updateIndentLevel(tr, delta);

if (tr.docChanged) {
dispatch && dispatch(tr);
return true;
}

return false;
};
}

updateIndentLevel (tr, delta) {
const { doc, selection } = tr;

if (!doc || !selection) return tr;

if (!(selection instanceof TextSelection || selection instanceof AllSelection)) {
return tr;
}

const { from, to } = selection;

doc.nodesBetween(from, to, (node, pos) => {
const nodeType = node.type;

if (
nodeType.name === 'paragraph' ||
nodeType.name === 'heading' ||
nodeType.name === 'blockquote'
) {
tr = this.setNodeIndentMarkup(tr, pos, delta);
return false;
} else if (isListNode(node)) {
return false;
}
return true;
});

return tr;
}

setNodeIndentMarkup (tr, pos, delta) {
if (!tr.doc) return tr;

const node = tr.doc.nodeAt(pos);
if (!node) return tr;

const { minIndent, maxIndent } = this.options;

const indent = clamp(
(node.attrs.indent || 0) + delta,
minIndent,
maxIndent,
);

if (indent === node.attrs.indent) return tr;

const nodeAttrs = {
...node.attrs,
indent,
};

return tr.setNodeMarkup(pos, node.type, nodeAttrs, node.marks);
}
}
1 change: 1 addition & 0 deletions src/extensions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export { default as ListItem } from './list_item';

// extensions
export { default as TextAlign } from './text_align';
export { default as Indent } from './indent';
9 changes: 9 additions & 0 deletions src/extensions/paragraph.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ALIGN_PATTERN } from './text_align';
export const ParagraphNodeSpec = {
attrs: {
textAlign: { default: null },
indent: { default: null },
},
content: 'inline*',
group: 'block',
Expand All @@ -23,14 +24,18 @@ function getAttrs (dom) {
let align = dom.getAttribute('align') || textAlign || '';
align = ALIGN_PATTERN.test(align) ? align : null;

const indent = parseInt(dom.getAttribute('data-indent'), 10) || 0;

return {
textAlign: align,
indent,
};
}

function toDOM (node) {
const {
textAlign,
indent,
} = node.attrs;

const attrs = {};
Expand All @@ -39,6 +44,10 @@ function toDOM (node) {
attrs['data-text-align'] = textAlign;
}

if (indent) {
attrs['data-indent'] = indent;
}

return ['p', attrs, 0];
}

Expand Down
10 changes: 5 additions & 5 deletions src/extensions/text_align.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ export default class TextAlign extends Extension {

commands () {
return {
align_left: this.setTextAlign({ alignment: 'null' }),
align_center: this.setTextAlign({ alignment: 'center' }),
align_right: this.setTextAlign({ alignment: 'right' }),
align_justify: this.setTextAlign({ alignment: 'justify' }),
align_left: () => this.setTextAlign({ alignment: 'null' }),
align_center: () => this.setTextAlign({ alignment: 'center' }),
align_right: () => this.setTextAlign({ alignment: 'right' }),
align_justify: () => this.setTextAlign({ alignment: 'justify' }),
};
}

setTextAlign ({ alignment }) {
return () => (state, dispatch) => {
return (state, dispatch) => {
let { tr } = state;
const { selection, doc } = tr;

Expand Down
17 changes: 17 additions & 0 deletions src/utils/list.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Node } from 'prosemirror-model';

export function isBulletListNode (node) {
return node.type.name === 'bullet_list';
}

export function isOrderedListNode (node) {
return node.type.name === 'order_list';
}

export function isListNode (node) {
if (node instanceof Node) {
return isBulletListNode(node) ||
isOrderedListNode(node);
}
return false;
}
10 changes: 10 additions & 0 deletions src/utils/shared.js
Original file line number Diff line number Diff line change
@@ -1 +1,11 @@
export function noop () {}

export function clamp (val, min, max) {
if (val < min) {
return min;
}
if (val > max) {
return max;
}
return val;
}
2 changes: 2 additions & 0 deletions src/views/Index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {
ListItem,
TextAlign,
Indent,
} from '@/extensions';
import ElTiptap from '../components/ElTiptap';
Expand All @@ -54,6 +55,7 @@ export default {
return {
extensions: [
new TextAlign(),
new Indent(),
new Doc(),
new Text(),
Expand Down

0 comments on commit 7451271

Please sign in to comment.