From 0c5bd28be985f3771222853989f1673c9c6f5e30 Mon Sep 17 00:00:00 2001 From: leecason Date: Wed, 1 Apr 2020 21:00:47 +0800 Subject: [PATCH] =?UTF-8?q?feat(font-type):=20=E2=9C=A8=20add=20new=20exte?= =?UTF-8?q?nsion=20FontType?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 +- README_ZH.md | 3 +- examples/views/AllExtensions.vue | 2 + src/components/MenuCommands/CommandButton.vue | 1 + .../MenuCommands/FontTypeDropdown.vue | 86 +++++++++++++++++++ src/extensions/font_type.ts | 73 ++++++++++++++++ src/extensions/index.ts | 1 + src/extensions/text_color.ts | 2 +- src/i18n/de/index.ts | 3 + src/i18n/en/index.ts | 3 + src/i18n/pl/index.ts | 3 + src/i18n/ru/index.ts | 3 + src/i18n/zh/index.ts | 3 + src/utils/font_type.ts | 68 +++++++++++++++ src/utils/shared.ts | 8 ++ 15 files changed, 259 insertions(+), 3 deletions(-) create mode 100644 src/components/MenuCommands/FontTypeDropdown.vue create mode 100644 src/extensions/font_type.ts create mode 100644 src/utils/font_type.ts diff --git a/README.md b/README.md index b7828973..6398d9b5 100644 --- a/README.md +++ b/README.md @@ -220,6 +220,7 @@ All available extensions: - `Print` (New) - `Fullscreen` (New) - `SelectAll` (New) +- `FontType` (New) You can customize the extension menu button view @@ -495,7 +496,7 @@ I'm continuously working to add in new features 💪. - [x] demo page - [x] `Table` extension - [x] `Iframe` extension -- [ ] `FontFamily` extension +- [x] `FontType` extension - [ ] `FontSize` extension - [x] `TextColor` extension - [x] `TextHighlight` extension diff --git a/README_ZH.md b/README_ZH.md index e0df2ed8..21da4b24 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -215,6 +215,7 @@ export default { - `Print` (新) - `Fullscreen` (新) - `SelectAll` (新) +- `FontType` (新) 你可以自定义菜单按钮的渲染视图 @@ -490,7 +491,7 @@ _OR_ - [x] demo 页面 - [x] `Table` extension - [x] `Iframe` extension -- [ ] `FontFamily` extension +- [x] `FontType` extension - [ ] `FontSize` extension - [x] `TextColor` extension - [x] `TextHighlight` extension diff --git a/examples/views/AllExtensions.vue b/examples/views/AllExtensions.vue index fd06d1f7..9205acc0 100644 --- a/examples/views/AllExtensions.vue +++ b/examples/views/AllExtensions.vue @@ -28,6 +28,7 @@ import { Italic, Strike, Code, + FontType, TextColor, TextHighlight, FormatClear, @@ -72,6 +73,7 @@ export default { new Italic({ bubble: true }), new Strike({ bubble: true }), new Code(), + new FontType(), new TextColor({ bubble: true }), new TextHighlight({ bubble: true }), new FormatClear(), diff --git a/src/components/MenuCommands/CommandButton.vue b/src/components/MenuCommands/CommandButton.vue index a19e06e6..56641eb2 100644 --- a/src/components/MenuCommands/CommandButton.vue +++ b/src/components/MenuCommands/CommandButton.vue @@ -20,6 +20,7 @@ import Icon from 'vue-awesome/components/Icon.vue'; import 'vue-awesome/icons/heading'; import 'vue-awesome/icons/font'; +import 'vue-awesome/icons/tint'; import 'vue-awesome/icons/highlighter'; import 'vue-awesome/icons/bold'; import 'vue-awesome/icons/underline'; diff --git a/src/components/MenuCommands/FontTypeDropdown.vue b/src/components/MenuCommands/FontTypeDropdown.vue new file mode 100644 index 00000000..6748088e --- /dev/null +++ b/src/components/MenuCommands/FontTypeDropdown.vue @@ -0,0 +1,86 @@ + + + diff --git a/src/extensions/font_type.ts b/src/extensions/font_type.ts new file mode 100644 index 00000000..f45b28ab --- /dev/null +++ b/src/extensions/font_type.ts @@ -0,0 +1,73 @@ +// @ts-nocheck +import { Mark, MenuData } from 'tiptap'; +import { CommandFunction } from 'tiptap-commands'; +import { Node as ProsemirrorNode, MarkType } from 'prosemirror-model'; +import { MenuBtnView } from '@/../types'; +import FontTypeDropdown from '@/components/MenuCommands/FontTypeDropdown.vue'; +import { DEFAULT_FONT_TYPE_MAP, setFontType } from '@/utils/font_type'; + +export default class FontType extends Mark implements MenuBtnView { + get name () { + return 'font_type'; + } + + get defaultOptions () { + return { + fontTypes: DEFAULT_FONT_TYPE_MAP, + }; + } + + get schema () { + return { + attrs: { + name: '', + }, + inline: true, + group: 'inline', + parseDOM: [ + { + style: 'font-family', + getAttrs: (name: string) => { + return { + name: name ? name.replace(/["']/g, '') : '', + }; + }, + }, + ], + toDOM (node: ProsemirrorNode) { + const { name } = node.attrs; + const attrs: { style?: string } = {}; + + if (name) { + attrs.style = `font-family: ${name}`; + } + return ['span', attrs, 0]; + }, + }; + } + + commands ({ type }: { type: MarkType }) { + return (name: string): CommandFunction => (state, dispatch) => { + let { tr } = state; + tr = setFontType( + state.tr.setSelection(state.selection), + type, + name, + ); + if (tr.docChanged || tr.storedMarksSet) { + dispatch && dispatch(tr); + return true; + } + return false; + }; + } + + menuBtnView (editorContext: MenuData) { + return { + component: FontTypeDropdown, + componentProps: { + editorContext, + }, + }; + } +} diff --git a/src/extensions/index.ts b/src/extensions/index.ts index 4ccc7edb..30844faf 100644 --- a/src/extensions/index.ts +++ b/src/extensions/index.ts @@ -31,6 +31,7 @@ export { default as Strike } from './strike'; export { default as Link } from './link'; export { default as TextColor } from './text_color'; export { default as TextHighlight } from './text_highlight'; +export { default as FontType } from './font_type'; // extensions export { default as HorizontalRule } from './horizontal_rule'; diff --git a/src/extensions/text_color.ts b/src/extensions/text_color.ts index 8753921f..24e1093c 100644 --- a/src/extensions/text_color.ts +++ b/src/extensions/text_color.ts @@ -72,7 +72,7 @@ export default class TextColor extends Mark implements MenuBtnView { colorSet: this.options.colors, selectedColor: getMarkAttrs('text_color').color, tooltip: t('editor.extensions.TextColor.tooltip'), - icon: 'font', + icon: 'tint', }, componentEvents: { confirm: (color: string) => commands.text_color(color), diff --git a/src/i18n/de/index.ts b/src/i18n/de/index.ts index a5500758..00f7a6a4 100644 --- a/src/i18n/de/index.ts +++ b/src/i18n/de/index.ts @@ -132,6 +132,9 @@ export default { }, }, }, + FontType: { + tooltip: 'Schriftfamilie', + }, TextColor: { tooltip: 'Textfarbe', }, diff --git a/src/i18n/en/index.ts b/src/i18n/en/index.ts index b842aff2..278a49f1 100644 --- a/src/i18n/en/index.ts +++ b/src/i18n/en/index.ts @@ -132,6 +132,9 @@ export default { }, }, }, + FontType: { + tooltip: 'Font family', + }, TextColor: { tooltip: 'Text color', }, diff --git a/src/i18n/pl/index.ts b/src/i18n/pl/index.ts index f19633fe..5eef702e 100644 --- a/src/i18n/pl/index.ts +++ b/src/i18n/pl/index.ts @@ -132,6 +132,9 @@ export default { }, }, }, + FontType: { + tooltip: 'Rodzina czcionek', + }, TextColor: { tooltip: 'Kolor tekstu', }, diff --git a/src/i18n/ru/index.ts b/src/i18n/ru/index.ts index 8a400e5d..e6436994 100644 --- a/src/i18n/ru/index.ts +++ b/src/i18n/ru/index.ts @@ -132,6 +132,9 @@ export default { }, }, }, + FontType: { + tooltip: 'Семейство шрифтов', + }, TextColor: { tooltip: 'Цвет текста', }, diff --git a/src/i18n/zh/index.ts b/src/i18n/zh/index.ts index 7e16ec02..294e6fc4 100644 --- a/src/i18n/zh/index.ts +++ b/src/i18n/zh/index.ts @@ -132,6 +132,9 @@ export default { }, }, }, + FontType: { + tooltip: '字体', + }, TextColor: { tooltip: '文本颜色', }, diff --git a/src/utils/font_type.ts b/src/utils/font_type.ts new file mode 100644 index 00000000..958e0c48 --- /dev/null +++ b/src/utils/font_type.ts @@ -0,0 +1,68 @@ +// @ts-ignore +import { getMarkAttrs } from 'tiptap-utils'; +import { Transaction, TextSelection, AllSelection, EditorState } from 'prosemirror-state'; +import { Mark as ProsemirrorMark, MarkType } from 'prosemirror-model'; +import applyMark from './apply_mark'; + +const DEFAULT_FONT_TYPE_NAMES = [ + 'Arial', + 'Arial Black', + 'Georgia', + 'Impact', + 'Tahoma', + 'Times New Roman', + 'Verdana', + 'Courier New', + 'Lucida Console', + 'Monaco', + 'monospace', +]; + +export const DEFAULT_FONT_TYPE_MAP = DEFAULT_FONT_TYPE_NAMES.reduce((obj: { [key: string]: string }, type: string) => { + obj[type] = type; + return obj; +}, {}); + +export function setFontType (tr: Transaction, type: MarkType, name: string): Transaction { + const { selection } = tr; + + if (!(selection instanceof TextSelection || selection instanceof AllSelection)) { + return tr; + } + const attrs = name ? { name } : null; + tr = applyMark(tr, type, attrs); + return tr; +} + +const DEFAULT_FONT_TYPE = ''; + +export function findActiveFontType (state: EditorState): string { + const { schema, selection, tr } = state; + const markType = schema.marks.font_type; + + if (!markType) return DEFAULT_FONT_TYPE; + + const { empty } = selection; + + if (empty) { + const storedMarks = tr.storedMarks || + state.storedMarks || + ( + selection instanceof TextSelection && + selection.$cursor && + selection.$cursor.marks && + selection.$cursor.marks() + ) || + []; + + const sm = storedMarks.find((m: ProsemirrorMark) => m.type === markType); + return (sm && sm.attrs.name) || DEFAULT_FONT_TYPE; + } + + const attrs = getMarkAttrs(state, markType); + const fontName = attrs.name; + + if (!fontName) return DEFAULT_FONT_TYPE; + + return fontName; +} diff --git a/src/utils/shared.ts b/src/utils/shared.ts index fca1d613..23ef942c 100644 --- a/src/utils/shared.ts +++ b/src/utils/shared.ts @@ -47,3 +47,11 @@ export function cached (fn: Function): Function { export const capitalize = cached((str: string): string => { return str.charAt(0).toUpperCase() + str.slice(1); }); + +/** + * Strict object type check. Only returns true + * for plain JavaScript objects. + */ +export function isPlainObject (obj: any): boolean { + return Object.prototype.toString.call(obj) === '[object Object]'; +}