@fastkit/vui 0.7.59 → 0.7.65
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/tool/index.js +1 -0
- package/dist/tool/vite-plugin.d.ts.map +1 -1
- package/dist/vui.cjs.js +616 -39
- package/dist/vui.cjs.prod.js +616 -39
- package/dist/vui.css +219 -0
- package/dist/vui.d.ts +141 -4
- package/dist/vui.min.css +1 -1
- package/dist/vui.min.css.map +1 -1
- package/dist/vui.mjs +606 -39
- package/package.json +7 -6
- package/src/components/VButton/VButtonGroup.scss +4 -0
- package/src/components/VButton/VButtonGroup.tsx +12 -1
- package/src/components/VWysiwygEditor/.DS_Store +0 -0
- package/src/components/VWysiwygEditor/VWysiwygEditor.scss +4 -0
- package/src/components/VWysiwygEditor/VWysiwygEditor.tsx +65 -22
- package/src/components/VWysiwygEditor/extensions/.DS_Store +0 -0
- package/src/components/VWysiwygEditor/extensions/color.ts +72 -0
- package/src/components/VWysiwygEditor/extensions/index.ts +2 -0
- package/src/components/VWysiwygEditor/extensions/linter/.DS_Store +0 -0
- package/src/components/VWysiwygEditor/extensions/linter/Linter.scss +140 -0
- package/src/components/VWysiwygEditor/extensions/linter/Linter.tsx +214 -0
- package/src/components/VWysiwygEditor/extensions/linter/LinterPlugin.ts +65 -0
- package/src/components/VWysiwygEditor/extensions/linter/index.ts +5 -0
- package/src/components/VWysiwygEditor/extensions/linter/plugins/BadWords.tsx +50 -0
- package/src/components/VWysiwygEditor/extensions/linter/plugins/HeadingLevel.ts +39 -0
- package/src/components/VWysiwygEditor/extensions/linter/plugins/Punctuation.ts +49 -0
- package/src/components/VWysiwygEditor/extensions/linter/plugins/index.ts +3 -0
- package/src/components/VWysiwygEditor/extensions/linter/utils.ts +28 -0
- package/src/components/VWysiwygEditor/index.ts +1 -0
- package/src/components/VWysiwygEditor/schemes.ts +190 -4
- package/src/components/VWysiwygEditor/tools/color.scss +140 -0
- package/src/components/VWysiwygEditor/tools/color.tsx +90 -0
- package/src/components/VWysiwygEditor/tools/index.ts +1 -1
- package/src/service.tsx +5 -0
- package/src/tool/vite-plugin.ts +1 -0
- package/src/components/VWysiwygEditor/tools/text-color.ts +0 -14
package/dist/vui.mjs
CHANGED
|
@@ -14,6 +14,9 @@ import { VAppContainer, VAppLayoutControl } from '@fastkit/vue-app-layout';
|
|
|
14
14
|
import { createTextableProps, createTextableEmits, TextableControl } from '@fastkit/vue-form-control';
|
|
15
15
|
import { useEditor, EditorContent, BubbleMenu } from '@tiptap/vue-3';
|
|
16
16
|
import StarterKit from '@tiptap/starter-kit';
|
|
17
|
+
import { Extension } from '@tiptap/core';
|
|
18
|
+
import { Decoration, DecorationSet } from 'prosemirror-view';
|
|
19
|
+
import { Plugin, PluginKey, TextSelection } from 'prosemirror-state';
|
|
17
20
|
import { BulletList } from '@tiptap/extension-bullet-list';
|
|
18
21
|
import { Bold } from '@tiptap/extension-bold';
|
|
19
22
|
import { Italic } from '@tiptap/extension-italic';
|
|
@@ -22,6 +25,7 @@ import { History } from '@tiptap/extension-history';
|
|
|
22
25
|
import { Link } from '@tiptap/extension-link';
|
|
23
26
|
import { validateIf, url } from '@fastkit/rules';
|
|
24
27
|
import { OrderedList } from '@tiptap/extension-ordered-list';
|
|
28
|
+
import TextStyle from '@tiptap/extension-text-style';
|
|
25
29
|
|
|
26
30
|
const CONTROL_SIZES = ['sm', 'md', 'lg'];
|
|
27
31
|
const CONTROL_FIELD_VARIANTS = ['outlined', 'filled', 'flat'];
|
|
@@ -1224,6 +1228,7 @@ const VButtonGroup = defineComponent({
|
|
|
1224
1228
|
|
|
1225
1229
|
setup(props, ctx) {
|
|
1226
1230
|
return () => {
|
|
1231
|
+
let hasIcon = false;
|
|
1227
1232
|
let buttonLength = 0;
|
|
1228
1233
|
let tmp = renderSlotOrEmpty$1(ctx.slots) || [];
|
|
1229
1234
|
|
|
@@ -1240,6 +1245,11 @@ const VButtonGroup = defineComponent({
|
|
|
1240
1245
|
}
|
|
1241
1246
|
|
|
1242
1247
|
buttonLength++;
|
|
1248
|
+
|
|
1249
|
+
if (!hasIcon && node.props && !!node.props.icon) {
|
|
1250
|
+
hasIcon = true;
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1243
1253
|
return {
|
|
1244
1254
|
isButton: true,
|
|
1245
1255
|
node
|
|
@@ -1256,6 +1266,7 @@ const VButtonGroup = defineComponent({
|
|
|
1256
1266
|
...childProps,
|
|
1257
1267
|
rouded: false,
|
|
1258
1268
|
class: ['v-button-group__item', {
|
|
1269
|
+
'v-button--icon': hasIcon,
|
|
1259
1270
|
'v-button-group__item--has-left': hasLeft,
|
|
1260
1271
|
'v-button-group__item--has-right': hasRight
|
|
1261
1272
|
}]
|
|
@@ -1265,7 +1276,9 @@ const VButtonGroup = defineComponent({
|
|
|
1265
1276
|
return child.node;
|
|
1266
1277
|
});
|
|
1267
1278
|
return createVNode("div", {
|
|
1268
|
-
"class":
|
|
1279
|
+
"class": ['v-button-group', {
|
|
1280
|
+
'v-button-group--has-icon': hasIcon
|
|
1281
|
+
}]
|
|
1269
1282
|
}, [$children]);
|
|
1270
1283
|
};
|
|
1271
1284
|
}
|
|
@@ -2869,13 +2882,105 @@ const VTextarea = defineComponent({
|
|
|
2869
2882
|
|
|
2870
2883
|
});
|
|
2871
2884
|
|
|
2885
|
+
const EDITOR_EVENTS = [
|
|
2886
|
+
'beforeCreate',
|
|
2887
|
+
'create',
|
|
2888
|
+
'update',
|
|
2889
|
+
'selectionUpdate',
|
|
2890
|
+
'transaction',
|
|
2891
|
+
'focus',
|
|
2892
|
+
'blur',
|
|
2893
|
+
'destroy',
|
|
2894
|
+
];
|
|
2895
|
+
const prefixedEventName = (source) => {
|
|
2896
|
+
return `on${source.charAt(0).toUpperCase()}${source.slice(1)}`;
|
|
2897
|
+
};
|
|
2898
|
+
class WysiwygEditorInitializeContext {
|
|
2899
|
+
listeners = {};
|
|
2900
|
+
_vui;
|
|
2901
|
+
get vui() {
|
|
2902
|
+
return this._vui();
|
|
2903
|
+
}
|
|
2904
|
+
constructor(vuiGetter, opts = {}) {
|
|
2905
|
+
this._vui = vuiGetter;
|
|
2906
|
+
EDITOR_EVENTS.forEach((event) => {
|
|
2907
|
+
this.listeners[event] = [];
|
|
2908
|
+
const prefixed = prefixedEventName(event);
|
|
2909
|
+
const fn = opts[prefixed];
|
|
2910
|
+
fn && this.listeners[event].push(fn);
|
|
2911
|
+
});
|
|
2912
|
+
}
|
|
2913
|
+
on(ev, handler) {
|
|
2914
|
+
this.listeners[ev].push(handler);
|
|
2915
|
+
return () => this.off(ev, handler);
|
|
2916
|
+
}
|
|
2917
|
+
off(ev, handler) {
|
|
2918
|
+
this.listeners[ev] = this.listeners[ev].filter((_handler) => _handler !== handler);
|
|
2919
|
+
}
|
|
2920
|
+
editorOptions() {
|
|
2921
|
+
const opts = {};
|
|
2922
|
+
EDITOR_EVENTS.forEach((event) => {
|
|
2923
|
+
const prefixed = prefixedEventName(event);
|
|
2924
|
+
opts[prefixed] = (props) => {
|
|
2925
|
+
const handlers = this.listeners[event];
|
|
2926
|
+
handlers.forEach((handler) => {
|
|
2927
|
+
handler(props);
|
|
2928
|
+
});
|
|
2929
|
+
};
|
|
2930
|
+
});
|
|
2931
|
+
return opts;
|
|
2932
|
+
}
|
|
2933
|
+
}
|
|
2934
|
+
// export function createWysiwygExtension<Options = any, Storage = any>(
|
|
2935
|
+
// extension: Extension<Options, Storage>,
|
|
2936
|
+
// ): Extension<Options, Storage>;
|
|
2937
|
+
// export function createWysiwygExtension<Options = any, Storage = any>(
|
|
2938
|
+
// node: Node<Options, Storage>,
|
|
2939
|
+
// ): Node<Options, Storage>;
|
|
2940
|
+
// export function createWysiwygExtension<Options = any, Storage = any>(
|
|
2941
|
+
// mark: Mark<Options, Storage>,
|
|
2942
|
+
// ): Mark<Options, Storage>;
|
|
2943
|
+
// export function createWysiwygExtension<Options = any, Storage = any>(
|
|
2944
|
+
// factory: WysiwygExtensionFactory<Options, Storage>,
|
|
2945
|
+
// ): WysiwygExtensionFactory<Options, Storage>;
|
|
2946
|
+
function isCreatedWysiwygExtension(source) {
|
|
2947
|
+
return (!!source &&
|
|
2948
|
+
typeof source === 'object' &&
|
|
2949
|
+
source.__isCreatedWysiwygExtension === true);
|
|
2950
|
+
}
|
|
2951
|
+
function createWysiwygExtension(extension) {
|
|
2952
|
+
const ext = {
|
|
2953
|
+
__isCreatedWysiwygExtension: true,
|
|
2954
|
+
_configs: [],
|
|
2955
|
+
configure: (opts) => {
|
|
2956
|
+
opts && ext._configs.push(opts);
|
|
2957
|
+
return ext;
|
|
2958
|
+
},
|
|
2959
|
+
raw: extension,
|
|
2960
|
+
};
|
|
2961
|
+
return ext;
|
|
2962
|
+
}
|
|
2963
|
+
function resolveRawWysiwygExtension(raw, ctx) {
|
|
2964
|
+
if (isCreatedWysiwygExtension(raw)) {
|
|
2965
|
+
const { raw: _raw, _configs } = raw;
|
|
2966
|
+
let ext = typeof _raw === 'function' ? _raw(ctx) : _raw;
|
|
2967
|
+
_configs.forEach((config) => {
|
|
2968
|
+
ext = ext.configure(config);
|
|
2969
|
+
});
|
|
2970
|
+
return ext;
|
|
2971
|
+
}
|
|
2972
|
+
return typeof raw === 'function' ? raw(ctx) : raw;
|
|
2973
|
+
}
|
|
2974
|
+
function resolveRawWysiwygExtensions(raws, ctx) {
|
|
2975
|
+
return raws.map((raw) => resolveRawWysiwygExtension(raw, ctx));
|
|
2976
|
+
}
|
|
2872
2977
|
function resolveRawWysiwygEditorTool(raw, vui) {
|
|
2873
2978
|
return typeof raw === 'function' ? raw(vui) : raw;
|
|
2874
2979
|
}
|
|
2875
|
-
function resolveRawWysiwygEditorTools(
|
|
2980
|
+
function resolveRawWysiwygEditorTools(rawTools, vui) {
|
|
2876
2981
|
const tools = [];
|
|
2877
2982
|
const extensions = [];
|
|
2878
|
-
|
|
2983
|
+
rawTools.forEach((raw) => {
|
|
2879
2984
|
let resolved = resolveRawWysiwygEditorTool(raw, vui);
|
|
2880
2985
|
if (!Array.isArray(resolved)) {
|
|
2881
2986
|
resolved = [resolved];
|
|
@@ -2906,6 +3011,10 @@ const VWysiwygEditor = defineComponent({
|
|
|
2906
3011
|
...createControlFieldProviderProps(),
|
|
2907
3012
|
...createControlProps(),
|
|
2908
3013
|
...defineSlotsProps(),
|
|
3014
|
+
extensions: {
|
|
3015
|
+
type: Array,
|
|
3016
|
+
default: () => []
|
|
3017
|
+
},
|
|
2909
3018
|
tools: {
|
|
2910
3019
|
type: Array,
|
|
2911
3020
|
default: () => []
|
|
@@ -2928,13 +3037,40 @@ const VWysiwygEditor = defineComponent({
|
|
|
2928
3037
|
nodeType: VUI_WYSIWYG_EDITOR_SYMBOL,
|
|
2929
3038
|
validationValue: () => textRef.value
|
|
2930
3039
|
});
|
|
3040
|
+
const initializeCtx = new WysiwygEditorInitializeContext(() => vui, {
|
|
3041
|
+
onCreate: ({
|
|
3042
|
+
editor
|
|
3043
|
+
}) => {
|
|
3044
|
+
textRef.value = editor.getText();
|
|
3045
|
+
updateEditable();
|
|
3046
|
+
},
|
|
3047
|
+
onFocus: ({
|
|
3048
|
+
event
|
|
3049
|
+
}) => {
|
|
3050
|
+
inputControl.focusHandler(event);
|
|
3051
|
+
},
|
|
3052
|
+
onBlur: ({
|
|
3053
|
+
event
|
|
3054
|
+
}) => {
|
|
3055
|
+
inputControl.blurHandler(event);
|
|
3056
|
+
},
|
|
3057
|
+
onUpdate: ({
|
|
3058
|
+
editor
|
|
3059
|
+
}) => {
|
|
3060
|
+
inputControl.value = editor.getHTML();
|
|
3061
|
+
textRef.value = editor.getText();
|
|
3062
|
+
}
|
|
3063
|
+
});
|
|
2931
3064
|
const extensions = [StarterKit.configure({
|
|
2932
3065
|
bold: false,
|
|
2933
3066
|
bulletList: false,
|
|
2934
3067
|
orderedList: false,
|
|
2935
3068
|
history: false,
|
|
2936
3069
|
italic: false
|
|
2937
|
-
}),
|
|
3070
|
+
}), // Linter.configure({
|
|
3071
|
+
// plugins: [BadWords(['abc', 'evidently']), Punctuation, HeadingLevel],
|
|
3072
|
+
// }),
|
|
3073
|
+
...resolveRawWysiwygExtensions(props.extensions, initializeCtx), ...wysiwygSettings.value.extensions];
|
|
2938
3074
|
|
|
2939
3075
|
const updateEditable = () => {
|
|
2940
3076
|
if (!editor.value) return;
|
|
@@ -2948,23 +3084,13 @@ const VWysiwygEditor = defineComponent({
|
|
|
2948
3084
|
$editor.commands.setContent(modelValue == null ? '' : modelValue, false);
|
|
2949
3085
|
textRef.value = $editor.getText();
|
|
2950
3086
|
});
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
onFocus: ctx => {
|
|
2959
|
-
inputControl.focusHandler(ctx.event);
|
|
2960
|
-
},
|
|
2961
|
-
onBlur: ctx => {
|
|
2962
|
-
inputControl.blurHandler(ctx.event);
|
|
2963
|
-
},
|
|
2964
|
-
onUpdate: ev => {
|
|
2965
|
-
inputControl.value = ev.editor.getHTML();
|
|
2966
|
-
textRef.value = ev.editor.getText();
|
|
2967
|
-
},
|
|
3087
|
+
initializeCtx.on('create', ({
|
|
3088
|
+
editor
|
|
3089
|
+
}) => {
|
|
3090
|
+
textRef.value = editor.getText();
|
|
3091
|
+
updateEditable();
|
|
3092
|
+
});
|
|
3093
|
+
const editor = useEditor({ ...initializeCtx.editorOptions(),
|
|
2968
3094
|
autofocus: props.autofocus,
|
|
2969
3095
|
content: props.modelValue,
|
|
2970
3096
|
extensions,
|
|
@@ -3010,9 +3136,9 @@ const VWysiwygEditor = defineComponent({
|
|
|
3010
3136
|
const _editor = editor.value;
|
|
3011
3137
|
const variant = vui.setting(bubbleMenu ? 'containedVariant' : 'plainVariant');
|
|
3012
3138
|
return createVNode(VButtonGroup, {
|
|
3013
|
-
"class": {
|
|
3139
|
+
"class": ['v-wysiwyg-editor__toolbar', {
|
|
3014
3140
|
'v-wysiwyg-editor__floating-toolbar': props.floatingToolbar
|
|
3015
|
-
},
|
|
3141
|
+
}],
|
|
3016
3142
|
"variant": variant
|
|
3017
3143
|
}, _isSlot$7(_slot = tools.map(({
|
|
3018
3144
|
key,
|
|
@@ -3024,16 +3150,23 @@ const VWysiwygEditor = defineComponent({
|
|
|
3024
3150
|
const isActive = !!_editor && resolveContextableValue(context, active);
|
|
3025
3151
|
const isDisabled = !inputControl.canOperation || !!_editor && resolveContextableValue(context, disabled);
|
|
3026
3152
|
let iconName;
|
|
3153
|
+
let child;
|
|
3027
3154
|
|
|
3028
3155
|
if (typeof icon === 'function') {
|
|
3029
3156
|
if (_editor) {
|
|
3030
|
-
|
|
3157
|
+
const _child = icon(context);
|
|
3158
|
+
|
|
3159
|
+
if (typeof _child === 'function') {
|
|
3160
|
+
child = _child();
|
|
3161
|
+
} else {
|
|
3162
|
+
iconName = child;
|
|
3163
|
+
}
|
|
3031
3164
|
}
|
|
3032
3165
|
} else {
|
|
3033
3166
|
iconName = icon;
|
|
3034
3167
|
}
|
|
3035
3168
|
|
|
3036
|
-
if (!iconName) return;
|
|
3169
|
+
if (!iconName && child == null) return;
|
|
3037
3170
|
return createVNode(VButton, {
|
|
3038
3171
|
"tabindex": -1,
|
|
3039
3172
|
"key": key,
|
|
@@ -3041,12 +3174,17 @@ const VWysiwygEditor = defineComponent({
|
|
|
3041
3174
|
"onClick": ev => onClick(context, ev),
|
|
3042
3175
|
"color": isActive ? toolButtonActiveColor : undefined,
|
|
3043
3176
|
"disabled": isDisabled
|
|
3044
|
-
},
|
|
3177
|
+
}, _isSlot$7(child) ? child : {
|
|
3178
|
+
default: () => [child]
|
|
3179
|
+
});
|
|
3045
3180
|
})) ? _slot : {
|
|
3046
3181
|
default: () => [_slot]
|
|
3047
3182
|
});
|
|
3048
3183
|
};
|
|
3049
3184
|
|
|
3185
|
+
onBeforeUnmount(() => {
|
|
3186
|
+
editor.value && editor.value.destroy();
|
|
3187
|
+
});
|
|
3050
3188
|
return {
|
|
3051
3189
|
editor,
|
|
3052
3190
|
...inputControl.expose(),
|
|
@@ -3122,6 +3260,376 @@ function resolveContextableValue(ctx, source) {
|
|
|
3122
3260
|
return typeof source === 'function' ? source(ctx) : source;
|
|
3123
3261
|
}
|
|
3124
3262
|
|
|
3263
|
+
const PROBLEM_CLASS_NAME = 'v-linter__problem';
|
|
3264
|
+
const ISSUE_ICON_CLASS_NAME = 'v-linter__issue-icon';
|
|
3265
|
+
const ISSUE_DATASET_NAME = 'data-issue-id';
|
|
3266
|
+
|
|
3267
|
+
function resolveWysiwygLinterFixMessage(message) {
|
|
3268
|
+
return typeof message === 'function' ? message() : message;
|
|
3269
|
+
}
|
|
3270
|
+
|
|
3271
|
+
function renderIcon(view, issue) {
|
|
3272
|
+
const {
|
|
3273
|
+
level = 'warning'
|
|
3274
|
+
} = issue;
|
|
3275
|
+
const icon = document.createElement('div');
|
|
3276
|
+
const message = resolveWysiwygLinterFixMessage(issue.message);
|
|
3277
|
+
icon.className = `${ISSUE_ICON_CLASS_NAME} ${level}-scope`;
|
|
3278
|
+
icon.setAttribute(ISSUE_DATASET_NAME, issue.id);
|
|
3279
|
+
|
|
3280
|
+
if (typeof message === 'string') {
|
|
3281
|
+
icon.title = message;
|
|
3282
|
+
}
|
|
3283
|
+
|
|
3284
|
+
return icon;
|
|
3285
|
+
}
|
|
3286
|
+
|
|
3287
|
+
function runAllLinterPlugins(doc, plugins) {
|
|
3288
|
+
const decorations = [];
|
|
3289
|
+
const issues = plugins.map(RegisteredLinterPlugin => {
|
|
3290
|
+
return new RegisteredLinterPlugin(doc).scan().getResults();
|
|
3291
|
+
}).flat();
|
|
3292
|
+
issues.forEach(issue => {
|
|
3293
|
+
const {
|
|
3294
|
+
level = 'warning',
|
|
3295
|
+
icon
|
|
3296
|
+
} = issue;
|
|
3297
|
+
const message = resolveWysiwygLinterFixMessage(issue.message);
|
|
3298
|
+
decorations.push(Decoration.inline(issue.from, issue.to, {
|
|
3299
|
+
class: `${PROBLEM_CLASS_NAME} ${level}-scope`,
|
|
3300
|
+
'data-issue-id': issue.id,
|
|
3301
|
+
title: typeof message === 'string' ? message : undefined
|
|
3302
|
+
}));
|
|
3303
|
+
|
|
3304
|
+
if (icon) {
|
|
3305
|
+
decorations.push(Decoration.widget(issue.from, view => renderIcon(view, issue)));
|
|
3306
|
+
}
|
|
3307
|
+
});
|
|
3308
|
+
const decorationSet = DecorationSet.create(doc, decorations);
|
|
3309
|
+
return {
|
|
3310
|
+
issues,
|
|
3311
|
+
decorationSet
|
|
3312
|
+
};
|
|
3313
|
+
}
|
|
3314
|
+
|
|
3315
|
+
const WysiwygLinter = createWysiwygExtension(ctx => {
|
|
3316
|
+
function showMenu(view, issue, event) {
|
|
3317
|
+
const message = resolveWysiwygLinterFixMessage(issue.message);
|
|
3318
|
+
const variant = ctx.vui.setting('containedVariant');
|
|
3319
|
+
const scope = ctx.vui.setting(`${issue.level}Scope`);
|
|
3320
|
+
return ctx.vui.menu({
|
|
3321
|
+
activator: event,
|
|
3322
|
+
content: stack => createVNode("div", {
|
|
3323
|
+
"class": "v-linter__issue-menu__body"
|
|
3324
|
+
}, [createVNode("div", {
|
|
3325
|
+
"class": ['v-linter__issue-menu__message', `${scope}-scope ${variant}`]
|
|
3326
|
+
}, [message]), issue.fix.length && createVNode("div", {
|
|
3327
|
+
"class": "v-linter__issue-menu__fixers"
|
|
3328
|
+
}, [issue.fix.map((fixer, index) => createVNode("div", {
|
|
3329
|
+
"key": index,
|
|
3330
|
+
"class": "v-linter__issue-menu__fixer"
|
|
3331
|
+
}, [createVNode("button", {
|
|
3332
|
+
"type": "button",
|
|
3333
|
+
"class": "v-linter__issue-menu__fixer__button",
|
|
3334
|
+
"onClick": () => {
|
|
3335
|
+
stack.close();
|
|
3336
|
+
fixer.handler(view, issue);
|
|
3337
|
+
}
|
|
3338
|
+
}, [createVNode("span", {
|
|
3339
|
+
"class": "v-linter__issue-menu__fixer__button__message"
|
|
3340
|
+
}, [resolveWysiwygLinterFixMessage(fixer.message)])])]))])])
|
|
3341
|
+
});
|
|
3342
|
+
}
|
|
3343
|
+
|
|
3344
|
+
return Extension.create({
|
|
3345
|
+
name: 'linter',
|
|
3346
|
+
|
|
3347
|
+
addOptions() {
|
|
3348
|
+
return {
|
|
3349
|
+
plugins: []
|
|
3350
|
+
};
|
|
3351
|
+
},
|
|
3352
|
+
|
|
3353
|
+
addStorage() {
|
|
3354
|
+
return {
|
|
3355
|
+
issues: []
|
|
3356
|
+
};
|
|
3357
|
+
},
|
|
3358
|
+
|
|
3359
|
+
addProseMirrorPlugins() {
|
|
3360
|
+
const {
|
|
3361
|
+
plugins
|
|
3362
|
+
} = this.options;
|
|
3363
|
+
const {
|
|
3364
|
+
storage
|
|
3365
|
+
} = this;
|
|
3366
|
+
|
|
3367
|
+
const runAll = doc => {
|
|
3368
|
+
const {
|
|
3369
|
+
issues,
|
|
3370
|
+
decorationSet
|
|
3371
|
+
} = runAllLinterPlugins(doc, plugins);
|
|
3372
|
+
storage.issues = issues;
|
|
3373
|
+
return decorationSet;
|
|
3374
|
+
};
|
|
3375
|
+
|
|
3376
|
+
const getIssue = id => storage.issues.find(issue => issue.id === id);
|
|
3377
|
+
|
|
3378
|
+
const getIssueElementByEvent = source => {
|
|
3379
|
+
if (!source || !(source instanceof HTMLElement)) {
|
|
3380
|
+
return;
|
|
3381
|
+
}
|
|
3382
|
+
|
|
3383
|
+
const issueId = source.getAttribute(ISSUE_DATASET_NAME);
|
|
3384
|
+
const issue = issueId && getIssue(issueId);
|
|
3385
|
+
if (!issue) return;
|
|
3386
|
+
return {
|
|
3387
|
+
issue
|
|
3388
|
+
};
|
|
3389
|
+
};
|
|
3390
|
+
|
|
3391
|
+
return [new Plugin({
|
|
3392
|
+
key: new PluginKey('linter'),
|
|
3393
|
+
state: {
|
|
3394
|
+
init(_, {
|
|
3395
|
+
doc
|
|
3396
|
+
}) {
|
|
3397
|
+
return runAll(doc);
|
|
3398
|
+
},
|
|
3399
|
+
|
|
3400
|
+
apply(transaction, oldState) {
|
|
3401
|
+
return transaction.docChanged ? runAll(transaction.doc) : oldState;
|
|
3402
|
+
}
|
|
3403
|
+
|
|
3404
|
+
},
|
|
3405
|
+
props: {
|
|
3406
|
+
decorations(state) {
|
|
3407
|
+
return this.getState(state);
|
|
3408
|
+
},
|
|
3409
|
+
|
|
3410
|
+
handleClick(view, _, event) {
|
|
3411
|
+
const info = getIssueElementByEvent(event.target);
|
|
3412
|
+
|
|
3413
|
+
if (!info) {
|
|
3414
|
+
return false;
|
|
3415
|
+
}
|
|
3416
|
+
|
|
3417
|
+
const {
|
|
3418
|
+
issue
|
|
3419
|
+
} = info;
|
|
3420
|
+
const {
|
|
3421
|
+
from,
|
|
3422
|
+
to
|
|
3423
|
+
} = issue;
|
|
3424
|
+
|
|
3425
|
+
const focus = () => view.dispatch(view.state.tr.setSelection(TextSelection.create(view.state.doc, from, to)).scrollIntoView());
|
|
3426
|
+
|
|
3427
|
+
focus();
|
|
3428
|
+
showMenu(view, issue, event);
|
|
3429
|
+
return true;
|
|
3430
|
+
}
|
|
3431
|
+
|
|
3432
|
+
}
|
|
3433
|
+
})];
|
|
3434
|
+
}
|
|
3435
|
+
|
|
3436
|
+
});
|
|
3437
|
+
});
|
|
3438
|
+
|
|
3439
|
+
let IDX = 256, BUFFER;
|
|
3440
|
+
const HEX = [];
|
|
3441
|
+
while (IDX--)
|
|
3442
|
+
HEX[IDX] = (IDX + 256).toString(16).substring(1);
|
|
3443
|
+
function cheepUUID() {
|
|
3444
|
+
let i = 0, num, out = '';
|
|
3445
|
+
if (!BUFFER || IDX + 16 > 256) {
|
|
3446
|
+
BUFFER = Array((i = 256));
|
|
3447
|
+
while (i--)
|
|
3448
|
+
BUFFER[i] = (256 * Math.random()) | 0;
|
|
3449
|
+
i = IDX = 0;
|
|
3450
|
+
}
|
|
3451
|
+
for (; i < 16; i++) {
|
|
3452
|
+
num = BUFFER[IDX + i];
|
|
3453
|
+
if (i == 6)
|
|
3454
|
+
out += HEX[(num & 15) | 64];
|
|
3455
|
+
else if (i == 8)
|
|
3456
|
+
out += HEX[(num & 63) | 128];
|
|
3457
|
+
else
|
|
3458
|
+
out += HEX[num];
|
|
3459
|
+
if (i & 1 && i > 1 && i < 11)
|
|
3460
|
+
out += '-';
|
|
3461
|
+
}
|
|
3462
|
+
IDX++;
|
|
3463
|
+
return out;
|
|
3464
|
+
}
|
|
3465
|
+
|
|
3466
|
+
class WysiwygLinterPlugin {
|
|
3467
|
+
doc;
|
|
3468
|
+
results = [];
|
|
3469
|
+
constructor(doc) {
|
|
3470
|
+
this.doc = doc;
|
|
3471
|
+
}
|
|
3472
|
+
record(result) {
|
|
3473
|
+
const { fix = [], icon = false } = result;
|
|
3474
|
+
this.results.push({
|
|
3475
|
+
level: 'error',
|
|
3476
|
+
id: cheepUUID(),
|
|
3477
|
+
...result,
|
|
3478
|
+
fix: Array.isArray(fix) ? fix : [fix],
|
|
3479
|
+
icon,
|
|
3480
|
+
});
|
|
3481
|
+
}
|
|
3482
|
+
scan() {
|
|
3483
|
+
return this;
|
|
3484
|
+
}
|
|
3485
|
+
getResults() {
|
|
3486
|
+
return this.results;
|
|
3487
|
+
}
|
|
3488
|
+
}
|
|
3489
|
+
|
|
3490
|
+
function WysiwygLinterBadWords(words) {
|
|
3491
|
+
const regex = new RegExp(`\\b(${words.join('|')})\\b`);
|
|
3492
|
+
return class WysiwygLinterBadWords extends WysiwygLinterPlugin {
|
|
3493
|
+
scan() {
|
|
3494
|
+
this.doc.descendants((node, position) => {
|
|
3495
|
+
if (!node.isText) {
|
|
3496
|
+
return;
|
|
3497
|
+
}
|
|
3498
|
+
|
|
3499
|
+
const matches = regex.exec(node.text);
|
|
3500
|
+
|
|
3501
|
+
if (matches) {
|
|
3502
|
+
const fixValue = matches[0] + '!!!!!';
|
|
3503
|
+
this.record({
|
|
3504
|
+
level: 'warning',
|
|
3505
|
+
message: `Try not to say '${matches[0]}'`,
|
|
3506
|
+
from: position + matches.index,
|
|
3507
|
+
to: position + matches.index + matches[0].length,
|
|
3508
|
+
fix: [{
|
|
3509
|
+
message: () => createVNode("span", null, [createVNode("code", null, [fixValue]), createTextVNode("\u306B\u4FEE\u6B63\u3059\u308B\u3002")]),
|
|
3510
|
+
handler: () => {
|
|
3511
|
+
console.log('hoge');
|
|
3512
|
+
}
|
|
3513
|
+
}, {
|
|
3514
|
+
message: 'どうにかする',
|
|
3515
|
+
handler: () => {
|
|
3516
|
+
console.log('hoge');
|
|
3517
|
+
}
|
|
3518
|
+
}]
|
|
3519
|
+
});
|
|
3520
|
+
}
|
|
3521
|
+
});
|
|
3522
|
+
return this;
|
|
3523
|
+
}
|
|
3524
|
+
|
|
3525
|
+
};
|
|
3526
|
+
}
|
|
3527
|
+
|
|
3528
|
+
class WysiwygLinterHeadingLevel extends WysiwygLinterPlugin {
|
|
3529
|
+
fixHeader(level) {
|
|
3530
|
+
return function ({ state, dispatch }, issue) {
|
|
3531
|
+
dispatch(state.tr.setNodeMarkup(issue.from - 1, undefined, { level }));
|
|
3532
|
+
};
|
|
3533
|
+
}
|
|
3534
|
+
scan() {
|
|
3535
|
+
let lastHeadLevel = null;
|
|
3536
|
+
this.doc.descendants((node, position) => {
|
|
3537
|
+
if (node.type.name === 'heading') {
|
|
3538
|
+
// Check whether heading levels fit under the current level
|
|
3539
|
+
const { level } = node.attrs;
|
|
3540
|
+
if (lastHeadLevel != null && level > lastHeadLevel + 1) {
|
|
3541
|
+
this.record({
|
|
3542
|
+
message: `Heading too small (${level} under ${lastHeadLevel})`,
|
|
3543
|
+
from: position + 1,
|
|
3544
|
+
to: position + 1 + node.content.size,
|
|
3545
|
+
fix: {
|
|
3546
|
+
message: '修正する',
|
|
3547
|
+
handler: this.fixHeader(lastHeadLevel + 1),
|
|
3548
|
+
},
|
|
3549
|
+
});
|
|
3550
|
+
}
|
|
3551
|
+
lastHeadLevel = level;
|
|
3552
|
+
}
|
|
3553
|
+
});
|
|
3554
|
+
return this;
|
|
3555
|
+
}
|
|
3556
|
+
}
|
|
3557
|
+
|
|
3558
|
+
class WysiwygLinterPunctuation extends WysiwygLinterPlugin {
|
|
3559
|
+
regex = / ([,.!?:]) ?/g;
|
|
3560
|
+
fix(replacement) {
|
|
3561
|
+
return function ({ state, dispatch }, issue) {
|
|
3562
|
+
dispatch(state.tr.replaceWith(issue.from, issue.to, state.schema.text(replacement)));
|
|
3563
|
+
};
|
|
3564
|
+
}
|
|
3565
|
+
scan() {
|
|
3566
|
+
this.doc.descendants((node, position) => {
|
|
3567
|
+
if (!node.isText) {
|
|
3568
|
+
return;
|
|
3569
|
+
}
|
|
3570
|
+
if (!node.text) {
|
|
3571
|
+
return;
|
|
3572
|
+
}
|
|
3573
|
+
const matches = this.regex.exec(node.text);
|
|
3574
|
+
if (matches) {
|
|
3575
|
+
this.record({
|
|
3576
|
+
message: 'Suspicious spacing around punctuation',
|
|
3577
|
+
from: position + matches.index,
|
|
3578
|
+
to: position + matches.index + matches[0].length,
|
|
3579
|
+
fix: {
|
|
3580
|
+
message: 'Fix it!!!',
|
|
3581
|
+
handler: this.fix(`${matches[1]} `),
|
|
3582
|
+
},
|
|
3583
|
+
});
|
|
3584
|
+
}
|
|
3585
|
+
});
|
|
3586
|
+
return this;
|
|
3587
|
+
}
|
|
3588
|
+
}
|
|
3589
|
+
|
|
3590
|
+
const WysiwygColorExtension = Extension.create({
|
|
3591
|
+
name: 'color',
|
|
3592
|
+
addOptions() {
|
|
3593
|
+
return {
|
|
3594
|
+
types: ['textStyle'],
|
|
3595
|
+
};
|
|
3596
|
+
},
|
|
3597
|
+
addGlobalAttributes() {
|
|
3598
|
+
return [
|
|
3599
|
+
{
|
|
3600
|
+
types: this.options.types,
|
|
3601
|
+
attributes: {
|
|
3602
|
+
color: {
|
|
3603
|
+
default: null,
|
|
3604
|
+
parseHTML: (element) => element.style.color.replace(/['"]+/g, ''),
|
|
3605
|
+
renderHTML: (attributes) => {
|
|
3606
|
+
if (!attributes.color) {
|
|
3607
|
+
return {};
|
|
3608
|
+
}
|
|
3609
|
+
return {
|
|
3610
|
+
style: `color: ${attributes.color}`,
|
|
3611
|
+
};
|
|
3612
|
+
},
|
|
3613
|
+
},
|
|
3614
|
+
},
|
|
3615
|
+
},
|
|
3616
|
+
];
|
|
3617
|
+
},
|
|
3618
|
+
addCommands() {
|
|
3619
|
+
return {
|
|
3620
|
+
setColor: (color) => ({ chain }) => {
|
|
3621
|
+
return chain().setMark('textStyle', { color }).run();
|
|
3622
|
+
},
|
|
3623
|
+
unsetColor: () => ({ chain }) => {
|
|
3624
|
+
return chain()
|
|
3625
|
+
.setMark('textStyle', { color: null })
|
|
3626
|
+
.removeEmptyTextStyle()
|
|
3627
|
+
.run();
|
|
3628
|
+
},
|
|
3629
|
+
};
|
|
3630
|
+
},
|
|
3631
|
+
});
|
|
3632
|
+
|
|
3125
3633
|
const WysiwygBulletListTool = (vui, options) => {
|
|
3126
3634
|
const tool = {
|
|
3127
3635
|
key: 'bulletList',
|
|
@@ -3262,18 +3770,73 @@ const WysiwygOrderedListTool = (vui, options) => {
|
|
|
3262
3770
|
return tool;
|
|
3263
3771
|
};
|
|
3264
3772
|
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
};
|
|
3773
|
+
function createWysiwygColorTool(opts) {
|
|
3774
|
+
const WysiwygColorTool = vui => {
|
|
3775
|
+
const tool = {
|
|
3776
|
+
key: 'textColor',
|
|
3777
|
+
// active: ({ editor }) => editor.isActive('textStyle'),
|
|
3778
|
+
icon: ({
|
|
3779
|
+
editor
|
|
3780
|
+
}) => () => {
|
|
3781
|
+
const color = editor.getAttributes('textStyle').color;
|
|
3782
|
+
const style = {
|
|
3783
|
+
color
|
|
3784
|
+
};
|
|
3785
|
+
return createVNode("span", {
|
|
3786
|
+
"class": "v-wysiwyg-color-tool__button"
|
|
3787
|
+
}, [createVNode(VIcon, {
|
|
3788
|
+
"class": "v-wysiwyg-color-tool__button__icon",
|
|
3789
|
+
"name": vui.icon('editorTextColor')
|
|
3790
|
+
}, null), createVNode("span", {
|
|
3791
|
+
"class": "v-wysiwyg-color-tool__button__bar",
|
|
3792
|
+
"style": style
|
|
3793
|
+
}, null)]);
|
|
3794
|
+
},
|
|
3795
|
+
onClick: (ctx, ev) => {
|
|
3796
|
+
ctx.vui.menu({
|
|
3797
|
+
class: 'v-wysiwyg-color-tool__menu',
|
|
3798
|
+
activator: ev,
|
|
3799
|
+
content: stack => createVNode("div", {
|
|
3800
|
+
"class": ['v-wysiwyg-color-tool__items', {
|
|
3801
|
+
'v-wysiwyg-color-tool__items--with-label': opts.withLabel
|
|
3802
|
+
}]
|
|
3803
|
+
}, [opts.items.map((item, index) => createVNode("button", {
|
|
3804
|
+
"key": item.key == null ? index : item.key,
|
|
3805
|
+
"class": "v-wysiwyg-color-tool__item",
|
|
3806
|
+
"type": "button",
|
|
3807
|
+
"onClick": () => {
|
|
3808
|
+
const {
|
|
3809
|
+
color
|
|
3810
|
+
} = item;
|
|
3811
|
+
let command = ctx.editor.chain().focus();
|
|
3812
|
+
|
|
3813
|
+
if (color) {
|
|
3814
|
+
command = command.setColor(color);
|
|
3815
|
+
} else {
|
|
3816
|
+
command = command.unsetColor();
|
|
3817
|
+
}
|
|
3818
|
+
|
|
3819
|
+
command.run();
|
|
3820
|
+
stack.close();
|
|
3821
|
+
}
|
|
3822
|
+
}, [createVNode("span", {
|
|
3823
|
+
"class": "v-wysiwyg-color-tool__item__color",
|
|
3824
|
+
"style": item.color ? {
|
|
3825
|
+
color: item.color
|
|
3826
|
+
} : {}
|
|
3827
|
+
}, null), opts.withLabel && createVNode("span", {
|
|
3828
|
+
"class": "v-wysiwyg-color-tool__item__name"
|
|
3829
|
+
}, [item.name])]))])
|
|
3830
|
+
});
|
|
3831
|
+
},
|
|
3832
|
+
floating: true,
|
|
3833
|
+
extensions: [TextStyle, WysiwygColorExtension]
|
|
3834
|
+
};
|
|
3835
|
+
return tool;
|
|
3836
|
+
};
|
|
3837
|
+
|
|
3838
|
+
return WysiwygColorTool;
|
|
3839
|
+
}
|
|
3277
3840
|
|
|
3278
3841
|
const {
|
|
3279
3842
|
props,
|
|
@@ -4890,6 +5453,10 @@ class VuiService {
|
|
|
4890
5453
|
return this.stack.snackbar(...args);
|
|
4891
5454
|
}
|
|
4892
5455
|
|
|
5456
|
+
menu(...args) {
|
|
5457
|
+
return this.stack.menu(...args);
|
|
5458
|
+
}
|
|
5459
|
+
|
|
4893
5460
|
formPrompt(settings, slot) {
|
|
4894
5461
|
let form;
|
|
4895
5462
|
const options = {
|
|
@@ -5041,4 +5608,4 @@ function installVuiPlugin(app, opts) {
|
|
|
5041
5608
|
return app.use(VuiPlugin, opts);
|
|
5042
5609
|
}
|
|
5043
5610
|
|
|
5044
|
-
export { AVATAR_SIZES, CHIP_SIZES, CONTROL_FIELD_VARIANTS, CONTROL_LOADING_SPINNER_SIZES, CONTROL_SIZES, PAGINATION_ALIGNS, VApp, VAvatar, VBreadcrumbs, VBusyImage, VButton, VButtonGroup, VCard, VCardActions, VCardContent, VCheckbox, VCheckboxGroup, VChip, VContentSwitcher, VDataTable, VDrawerLayout, VForm, VFormControl, VGridContainer, VGridItem, VHero, VIcon, VListTile, VNavigation, VNavigationItem, VNumberField, VOption, VOptionGroup, VPagination, VPaper, VRadio, VRadioGroup, VSelect, VSkeltonLoaderBone, VSwitch, VSwitchGroup, VTabs, VTextField, VTextarea, VToolbar, VToolbarEdge, VToolbarMenu, VToolbarTitle, VUI_CHECKBOX_GROUP_SYMBOL, VUI_CHECKBOX_SYMBOL, VUI_FORM_SYMBOL, VUI_OPTION_SYMBOL, VUI_RADIO_GROUP_SYMBOL, VUI_RADIO_SYMBOL, VUI_SELECT_SYMBOL, VUI_SWITCH_GROUP_SYMBOL, VUI_SWITCH_SYMBOL, VUI_TEXTAREA_SYMBOL, VUI_TEXT_FIELD_SYMBOL, VUI_WYSIWYG_EDITOR_SYMBOL, VWysiwygEditor, VuiColorProviderInjectionKey, VuiControlFieldInjectionKey, VuiControlInjectionKey, VuiInjectionKey, VuiPlugin, VuiService, WysiwygBulletListTool, WysiwygFormatBoldTool, WysiwygFormatItalicTool, WysiwygFormatUnderlineTool, WysiwygHistoryTool, WysiwygLinkTool,
|
|
5611
|
+
export { AVATAR_SIZES, CHIP_SIZES, CONTROL_FIELD_VARIANTS, CONTROL_LOADING_SPINNER_SIZES, CONTROL_SIZES, PAGINATION_ALIGNS, VApp, VAvatar, VBreadcrumbs, VBusyImage, VButton, VButtonGroup, VCard, VCardActions, VCardContent, VCheckbox, VCheckboxGroup, VChip, VContentSwitcher, VDataTable, VDrawerLayout, VForm, VFormControl, VGridContainer, VGridItem, VHero, VIcon, VListTile, VNavigation, VNavigationItem, VNumberField, VOption, VOptionGroup, VPagination, VPaper, VRadio, VRadioGroup, VSelect, VSkeltonLoaderBone, VSwitch, VSwitchGroup, VTabs, VTextField, VTextarea, VToolbar, VToolbarEdge, VToolbarMenu, VToolbarTitle, VUI_CHECKBOX_GROUP_SYMBOL, VUI_CHECKBOX_SYMBOL, VUI_FORM_SYMBOL, VUI_OPTION_SYMBOL, VUI_RADIO_GROUP_SYMBOL, VUI_RADIO_SYMBOL, VUI_SELECT_SYMBOL, VUI_SWITCH_GROUP_SYMBOL, VUI_SWITCH_SYMBOL, VUI_TEXTAREA_SYMBOL, VUI_TEXT_FIELD_SYMBOL, VUI_WYSIWYG_EDITOR_SYMBOL, VWysiwygEditor, VuiColorProviderInjectionKey, VuiControlFieldInjectionKey, VuiControlInjectionKey, VuiInjectionKey, VuiPlugin, VuiService, WysiwygBulletListTool, WysiwygColorExtension, WysiwygEditorInitializeContext, WysiwygFormatBoldTool, WysiwygFormatItalicTool, WysiwygFormatUnderlineTool, WysiwygHistoryTool, WysiwygLinkTool, WysiwygLinter, WysiwygLinterBadWords, WysiwygLinterHeadingLevel, WysiwygLinterPlugin, WysiwygLinterPunctuation, WysiwygOrderedListTool, configureDataTableDefaults, createAvatarProps, createCardProps, createChipProps, createControlFieldProviderProps, createControlProps, createElevationProps, createListTileProps, createNavigationItemProps, createPaperBaseProps, createPaperProps, createRequiredChipRenderer, createVFormProps, createWysiwygColorTool, createWysiwygExtension, defineFormSelectorComponent, iconProps, installVuiPlugin, listTileEmits, mergeVuiPluginOptions, mergeVuiServiceIconSettings, mergeVuiServiceOptions, mergeVuiServiceUISettings, paginationProps, rawIconProp, renderNavigationItemInput, resolveNavigationItemInput, resolveRawIconProp, resolveRawWysiwygEditorTools, resolveRawWysiwygExtensions, splitAttrs, useControl, useControlField, useElevation, useVui, useVuiColorProvider, vueButtonProps };
|