@fastkit/vui 0.7.57 → 0.7.64
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 +615 -44
- package/dist/vui.cjs.prod.js +615 -44
- package/dist/vui.css +221 -0
- package/dist/vui.d.ts +139 -4
- package/dist/vui.min.css +1 -1
- package/dist/vui.min.css.map +1 -1
- package/dist/vui.mjs +605 -44
- 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 +6 -0
- package/src/components/VWysiwygEditor/VWysiwygEditor.tsx +97 -56
- 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 +210 -0
- package/src/components/VWysiwygEditor/extensions/linter/LinterPlugin.ts +62 -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.cjs.js
CHANGED
|
@@ -15,6 +15,9 @@ var vueAppLayout = require('@fastkit/vue-app-layout');
|
|
|
15
15
|
var vueFormControl = require('@fastkit/vue-form-control');
|
|
16
16
|
var vue3 = require('@tiptap/vue-3');
|
|
17
17
|
var StarterKit = require('@tiptap/starter-kit');
|
|
18
|
+
var core = require('@tiptap/core');
|
|
19
|
+
var prosemirrorView = require('prosemirror-view');
|
|
20
|
+
var prosemirrorState = require('prosemirror-state');
|
|
18
21
|
var extensionBulletList = require('@tiptap/extension-bullet-list');
|
|
19
22
|
var extensionBold = require('@tiptap/extension-bold');
|
|
20
23
|
var extensionItalic = require('@tiptap/extension-italic');
|
|
@@ -23,10 +26,12 @@ var extensionHistory = require('@tiptap/extension-history');
|
|
|
23
26
|
var extensionLink = require('@tiptap/extension-link');
|
|
24
27
|
var rules = require('@fastkit/rules');
|
|
25
28
|
var extensionOrderedList = require('@tiptap/extension-ordered-list');
|
|
29
|
+
var TextStyle = require('@tiptap/extension-text-style');
|
|
26
30
|
|
|
27
31
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e["default"] : e; }
|
|
28
32
|
|
|
29
33
|
var StarterKit__default = /*#__PURE__*/_interopDefaultLegacy(StarterKit);
|
|
34
|
+
var TextStyle__default = /*#__PURE__*/_interopDefaultLegacy(TextStyle);
|
|
30
35
|
|
|
31
36
|
const CONTROL_SIZES = ['sm', 'md', 'lg'];
|
|
32
37
|
const CONTROL_FIELD_VARIANTS = ['outlined', 'filled', 'flat'];
|
|
@@ -1229,6 +1234,7 @@ const VButtonGroup = vue.defineComponent({
|
|
|
1229
1234
|
|
|
1230
1235
|
setup(props, ctx) {
|
|
1231
1236
|
return () => {
|
|
1237
|
+
let hasIcon = false;
|
|
1232
1238
|
let buttonLength = 0;
|
|
1233
1239
|
let tmp = vueUtils.renderSlotOrEmpty(ctx.slots) || [];
|
|
1234
1240
|
|
|
@@ -1245,6 +1251,11 @@ const VButtonGroup = vue.defineComponent({
|
|
|
1245
1251
|
}
|
|
1246
1252
|
|
|
1247
1253
|
buttonLength++;
|
|
1254
|
+
|
|
1255
|
+
if (!hasIcon && node.props && !!node.props.icon) {
|
|
1256
|
+
hasIcon = true;
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1248
1259
|
return {
|
|
1249
1260
|
isButton: true,
|
|
1250
1261
|
node
|
|
@@ -1261,6 +1272,7 @@ const VButtonGroup = vue.defineComponent({
|
|
|
1261
1272
|
...childProps,
|
|
1262
1273
|
rouded: false,
|
|
1263
1274
|
class: ['v-button-group__item', {
|
|
1275
|
+
'v-button--icon': hasIcon,
|
|
1264
1276
|
'v-button-group__item--has-left': hasLeft,
|
|
1265
1277
|
'v-button-group__item--has-right': hasRight
|
|
1266
1278
|
}]
|
|
@@ -1270,7 +1282,9 @@ const VButtonGroup = vue.defineComponent({
|
|
|
1270
1282
|
return child.node;
|
|
1271
1283
|
});
|
|
1272
1284
|
return vue.createVNode("div", {
|
|
1273
|
-
"class":
|
|
1285
|
+
"class": ['v-button-group', {
|
|
1286
|
+
'v-button-group--has-icon': hasIcon
|
|
1287
|
+
}]
|
|
1274
1288
|
}, [$children]);
|
|
1275
1289
|
};
|
|
1276
1290
|
}
|
|
@@ -2874,13 +2888,105 @@ const VTextarea = vue.defineComponent({
|
|
|
2874
2888
|
|
|
2875
2889
|
});
|
|
2876
2890
|
|
|
2891
|
+
const EDITOR_EVENTS = [
|
|
2892
|
+
'beforeCreate',
|
|
2893
|
+
'create',
|
|
2894
|
+
'update',
|
|
2895
|
+
'selectionUpdate',
|
|
2896
|
+
'transaction',
|
|
2897
|
+
'focus',
|
|
2898
|
+
'blur',
|
|
2899
|
+
'destroy',
|
|
2900
|
+
];
|
|
2901
|
+
const prefixedEventName = (source) => {
|
|
2902
|
+
return `on${source.charAt(0).toUpperCase()}${source.slice(1)}`;
|
|
2903
|
+
};
|
|
2904
|
+
class WysiwygEditorInitializeContext {
|
|
2905
|
+
listeners = {};
|
|
2906
|
+
_vui;
|
|
2907
|
+
get vui() {
|
|
2908
|
+
return this._vui();
|
|
2909
|
+
}
|
|
2910
|
+
constructor(vuiGetter, opts = {}) {
|
|
2911
|
+
this._vui = vuiGetter;
|
|
2912
|
+
EDITOR_EVENTS.forEach((event) => {
|
|
2913
|
+
this.listeners[event] = [];
|
|
2914
|
+
const prefixed = prefixedEventName(event);
|
|
2915
|
+
const fn = opts[prefixed];
|
|
2916
|
+
fn && this.listeners[event].push(fn);
|
|
2917
|
+
});
|
|
2918
|
+
}
|
|
2919
|
+
on(ev, handler) {
|
|
2920
|
+
this.listeners[ev].push(handler);
|
|
2921
|
+
return () => this.off(ev, handler);
|
|
2922
|
+
}
|
|
2923
|
+
off(ev, handler) {
|
|
2924
|
+
this.listeners[ev] = this.listeners[ev].filter((_handler) => _handler !== handler);
|
|
2925
|
+
}
|
|
2926
|
+
editorOptions() {
|
|
2927
|
+
const opts = {};
|
|
2928
|
+
EDITOR_EVENTS.forEach((event) => {
|
|
2929
|
+
const prefixed = prefixedEventName(event);
|
|
2930
|
+
opts[prefixed] = (props) => {
|
|
2931
|
+
const handlers = this.listeners[event];
|
|
2932
|
+
handlers.forEach((handler) => {
|
|
2933
|
+
handler(props);
|
|
2934
|
+
});
|
|
2935
|
+
};
|
|
2936
|
+
});
|
|
2937
|
+
return opts;
|
|
2938
|
+
}
|
|
2939
|
+
}
|
|
2940
|
+
// export function createWysiwygExtension<Options = any, Storage = any>(
|
|
2941
|
+
// extension: Extension<Options, Storage>,
|
|
2942
|
+
// ): Extension<Options, Storage>;
|
|
2943
|
+
// export function createWysiwygExtension<Options = any, Storage = any>(
|
|
2944
|
+
// node: Node<Options, Storage>,
|
|
2945
|
+
// ): Node<Options, Storage>;
|
|
2946
|
+
// export function createWysiwygExtension<Options = any, Storage = any>(
|
|
2947
|
+
// mark: Mark<Options, Storage>,
|
|
2948
|
+
// ): Mark<Options, Storage>;
|
|
2949
|
+
// export function createWysiwygExtension<Options = any, Storage = any>(
|
|
2950
|
+
// factory: WysiwygExtensionFactory<Options, Storage>,
|
|
2951
|
+
// ): WysiwygExtensionFactory<Options, Storage>;
|
|
2952
|
+
function isCreatedWysiwygExtension(source) {
|
|
2953
|
+
return (!!source &&
|
|
2954
|
+
typeof source === 'object' &&
|
|
2955
|
+
source.__isCreatedWysiwygExtension === true);
|
|
2956
|
+
}
|
|
2957
|
+
function createWysiwygExtension(extension) {
|
|
2958
|
+
const ext = {
|
|
2959
|
+
__isCreatedWysiwygExtension: true,
|
|
2960
|
+
_configs: [],
|
|
2961
|
+
configure: (opts) => {
|
|
2962
|
+
opts && ext._configs.push(opts);
|
|
2963
|
+
return ext;
|
|
2964
|
+
},
|
|
2965
|
+
raw: extension,
|
|
2966
|
+
};
|
|
2967
|
+
return ext;
|
|
2968
|
+
}
|
|
2969
|
+
function resolveRawWysiwygExtension(raw, ctx) {
|
|
2970
|
+
if (isCreatedWysiwygExtension(raw)) {
|
|
2971
|
+
const { raw: _raw, _configs } = raw;
|
|
2972
|
+
let ext = typeof _raw === 'function' ? _raw(ctx) : _raw;
|
|
2973
|
+
_configs.forEach((config) => {
|
|
2974
|
+
ext = ext.configure(config);
|
|
2975
|
+
});
|
|
2976
|
+
return ext;
|
|
2977
|
+
}
|
|
2978
|
+
return typeof raw === 'function' ? raw(ctx) : raw;
|
|
2979
|
+
}
|
|
2980
|
+
function resolveRawWysiwygExtensions(raws, ctx) {
|
|
2981
|
+
return raws.map((raw) => resolveRawWysiwygExtension(raw, ctx));
|
|
2982
|
+
}
|
|
2877
2983
|
function resolveRawWysiwygEditorTool(raw, vui) {
|
|
2878
2984
|
return typeof raw === 'function' ? raw(vui) : raw;
|
|
2879
2985
|
}
|
|
2880
|
-
function resolveRawWysiwygEditorTools(
|
|
2986
|
+
function resolveRawWysiwygEditorTools(rawTools, vui) {
|
|
2881
2987
|
const tools = [];
|
|
2882
2988
|
const extensions = [];
|
|
2883
|
-
|
|
2989
|
+
rawTools.forEach((raw) => {
|
|
2884
2990
|
let resolved = resolveRawWysiwygEditorTool(raw, vui);
|
|
2885
2991
|
if (!Array.isArray(resolved)) {
|
|
2886
2992
|
resolved = [resolved];
|
|
@@ -2911,6 +3017,10 @@ const VWysiwygEditor = vue.defineComponent({
|
|
|
2911
3017
|
...createControlFieldProviderProps(),
|
|
2912
3018
|
...createControlProps(),
|
|
2913
3019
|
...vueKit.defineSlotsProps(),
|
|
3020
|
+
extensions: {
|
|
3021
|
+
type: Array,
|
|
3022
|
+
default: () => []
|
|
3023
|
+
},
|
|
2914
3024
|
tools: {
|
|
2915
3025
|
type: Array,
|
|
2916
3026
|
default: () => []
|
|
@@ -2933,13 +3043,40 @@ const VWysiwygEditor = vue.defineComponent({
|
|
|
2933
3043
|
nodeType: VUI_WYSIWYG_EDITOR_SYMBOL,
|
|
2934
3044
|
validationValue: () => textRef.value
|
|
2935
3045
|
});
|
|
3046
|
+
const initializeCtx = new WysiwygEditorInitializeContext(() => vui, {
|
|
3047
|
+
onCreate: ({
|
|
3048
|
+
editor
|
|
3049
|
+
}) => {
|
|
3050
|
+
textRef.value = editor.getText();
|
|
3051
|
+
updateEditable();
|
|
3052
|
+
},
|
|
3053
|
+
onFocus: ({
|
|
3054
|
+
event
|
|
3055
|
+
}) => {
|
|
3056
|
+
inputControl.focusHandler(event);
|
|
3057
|
+
},
|
|
3058
|
+
onBlur: ({
|
|
3059
|
+
event
|
|
3060
|
+
}) => {
|
|
3061
|
+
inputControl.blurHandler(event);
|
|
3062
|
+
},
|
|
3063
|
+
onUpdate: ({
|
|
3064
|
+
editor
|
|
3065
|
+
}) => {
|
|
3066
|
+
inputControl.value = editor.getHTML();
|
|
3067
|
+
textRef.value = editor.getText();
|
|
3068
|
+
}
|
|
3069
|
+
});
|
|
2936
3070
|
const extensions = [StarterKit__default.configure({
|
|
2937
3071
|
bold: false,
|
|
2938
3072
|
bulletList: false,
|
|
2939
3073
|
orderedList: false,
|
|
2940
3074
|
history: false,
|
|
2941
3075
|
italic: false
|
|
2942
|
-
}),
|
|
3076
|
+
}), // Linter.configure({
|
|
3077
|
+
// plugins: [BadWords(['abc', 'evidently']), Punctuation, HeadingLevel],
|
|
3078
|
+
// }),
|
|
3079
|
+
...resolveRawWysiwygExtensions(props.extensions, initializeCtx), ...wysiwygSettings.value.extensions];
|
|
2943
3080
|
|
|
2944
3081
|
const updateEditable = () => {
|
|
2945
3082
|
if (!editor.value) return;
|
|
@@ -2953,23 +3090,13 @@ const VWysiwygEditor = vue.defineComponent({
|
|
|
2953
3090
|
$editor.commands.setContent(modelValue == null ? '' : modelValue, false);
|
|
2954
3091
|
textRef.value = $editor.getText();
|
|
2955
3092
|
});
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
onFocus: ctx => {
|
|
2964
|
-
inputControl.focusHandler(ctx.event);
|
|
2965
|
-
},
|
|
2966
|
-
onBlur: ctx => {
|
|
2967
|
-
inputControl.blurHandler(ctx.event);
|
|
2968
|
-
},
|
|
2969
|
-
onUpdate: ev => {
|
|
2970
|
-
inputControl.value = ev.editor.getHTML();
|
|
2971
|
-
textRef.value = ev.editor.getText();
|
|
2972
|
-
},
|
|
3093
|
+
initializeCtx.on('create', ({
|
|
3094
|
+
editor
|
|
3095
|
+
}) => {
|
|
3096
|
+
textRef.value = editor.getText();
|
|
3097
|
+
updateEditable();
|
|
3098
|
+
});
|
|
3099
|
+
const editor = vue3.useEditor({ ...initializeCtx.editorOptions(),
|
|
2973
3100
|
autofocus: props.autofocus,
|
|
2974
3101
|
content: props.modelValue,
|
|
2975
3102
|
extensions,
|
|
@@ -3015,9 +3142,9 @@ const VWysiwygEditor = vue.defineComponent({
|
|
|
3015
3142
|
const _editor = editor.value;
|
|
3016
3143
|
const variant = vui.setting(bubbleMenu ? 'containedVariant' : 'plainVariant');
|
|
3017
3144
|
return vue.createVNode(VButtonGroup, {
|
|
3018
|
-
"class": {
|
|
3145
|
+
"class": ['v-wysiwyg-editor__toolbar', {
|
|
3019
3146
|
'v-wysiwyg-editor__floating-toolbar': props.floatingToolbar
|
|
3020
|
-
},
|
|
3147
|
+
}],
|
|
3021
3148
|
"variant": variant
|
|
3022
3149
|
}, _isSlot$7(_slot = tools.map(({
|
|
3023
3150
|
key,
|
|
@@ -3029,16 +3156,23 @@ const VWysiwygEditor = vue.defineComponent({
|
|
|
3029
3156
|
const isActive = !!_editor && resolveContextableValue(context, active);
|
|
3030
3157
|
const isDisabled = !inputControl.canOperation || !!_editor && resolveContextableValue(context, disabled);
|
|
3031
3158
|
let iconName;
|
|
3159
|
+
let child;
|
|
3032
3160
|
|
|
3033
3161
|
if (typeof icon === 'function') {
|
|
3034
3162
|
if (_editor) {
|
|
3035
|
-
|
|
3163
|
+
const _child = icon(context);
|
|
3164
|
+
|
|
3165
|
+
if (typeof _child === 'function') {
|
|
3166
|
+
child = _child();
|
|
3167
|
+
} else {
|
|
3168
|
+
iconName = child;
|
|
3169
|
+
}
|
|
3036
3170
|
}
|
|
3037
3171
|
} else {
|
|
3038
3172
|
iconName = icon;
|
|
3039
3173
|
}
|
|
3040
3174
|
|
|
3041
|
-
if (!iconName) return;
|
|
3175
|
+
if (!iconName && child == null) return;
|
|
3042
3176
|
return vue.createVNode(VButton, {
|
|
3043
3177
|
"tabindex": -1,
|
|
3044
3178
|
"key": key,
|
|
@@ -3046,12 +3180,17 @@ const VWysiwygEditor = vue.defineComponent({
|
|
|
3046
3180
|
"onClick": ev => onClick(context, ev),
|
|
3047
3181
|
"color": isActive ? toolButtonActiveColor : undefined,
|
|
3048
3182
|
"disabled": isDisabled
|
|
3049
|
-
},
|
|
3183
|
+
}, _isSlot$7(child) ? child : {
|
|
3184
|
+
default: () => [child]
|
|
3185
|
+
});
|
|
3050
3186
|
})) ? _slot : {
|
|
3051
3187
|
default: () => [_slot]
|
|
3052
3188
|
});
|
|
3053
3189
|
};
|
|
3054
3190
|
|
|
3191
|
+
vue.onBeforeUnmount(() => {
|
|
3192
|
+
editor.value && editor.value.destroy();
|
|
3193
|
+
});
|
|
3055
3194
|
return {
|
|
3056
3195
|
editor,
|
|
3057
3196
|
...inputControl.expose(),
|
|
@@ -3087,9 +3226,7 @@ const VWysiwygEditor = vue.defineComponent({
|
|
|
3087
3226
|
} = this;
|
|
3088
3227
|
return vue.createVNode("div", {
|
|
3089
3228
|
"class": "v-wysiwyg-editor__wrapper"
|
|
3090
|
-
}, [!this.isReadonly && this.createTools(), vue.createVNode(
|
|
3091
|
-
"class": "v-wysiwyg-editor__body"
|
|
3092
|
-
}, [vue.createVNode(VControlField, {
|
|
3229
|
+
}, [!this.isReadonly && this.createTools(), vue.createVNode(VControlField, {
|
|
3093
3230
|
"class": "v-wysiwyg-editor__input",
|
|
3094
3231
|
"autoHeight": true,
|
|
3095
3232
|
"startAdornment": this.startAdornment,
|
|
@@ -3099,7 +3236,9 @@ const VWysiwygEditor = vue.defineComponent({
|
|
|
3099
3236
|
default: () => {
|
|
3100
3237
|
let _slot2;
|
|
3101
3238
|
|
|
3102
|
-
return vue.createVNode(
|
|
3239
|
+
return vue.createVNode("div", {
|
|
3240
|
+
"class": "v-wysiwyg-editor__body"
|
|
3241
|
+
}, [vue.createVNode(vue3.EditorContent, {
|
|
3103
3242
|
"class": "v-wysiwyg-editor__input__element wysiwyg",
|
|
3104
3243
|
"editor": editor
|
|
3105
3244
|
}, null), !this.floatingToolbar && !!editor && !this.isReadonly && vue.createVNode(vue3.BubbleMenu, {
|
|
@@ -3109,7 +3248,7 @@ const VWysiwygEditor = vue.defineComponent({
|
|
|
3109
3248
|
default: () => [_slot2]
|
|
3110
3249
|
})]);
|
|
3111
3250
|
}
|
|
3112
|
-
})])
|
|
3251
|
+
})]);
|
|
3113
3252
|
},
|
|
3114
3253
|
infoAppends: () => {
|
|
3115
3254
|
const {
|
|
@@ -3127,6 +3266,370 @@ function resolveContextableValue(ctx, source) {
|
|
|
3127
3266
|
return typeof source === 'function' ? source(ctx) : source;
|
|
3128
3267
|
}
|
|
3129
3268
|
|
|
3269
|
+
const PROBLEM_CLASS_NAME = 'v-linter__problem';
|
|
3270
|
+
const ISSUE_ICON_CLASS_NAME = 'v-linter__issue-icon';
|
|
3271
|
+
const ISSUE_DATASET_NAME = 'data-issue-id';
|
|
3272
|
+
|
|
3273
|
+
function resolveWysiwygLinterFixMessage(message) {
|
|
3274
|
+
return typeof message === 'function' ? message() : message;
|
|
3275
|
+
}
|
|
3276
|
+
|
|
3277
|
+
function renderIcon(view, issue) {
|
|
3278
|
+
const {
|
|
3279
|
+
level = 'warning'
|
|
3280
|
+
} = issue;
|
|
3281
|
+
const icon = document.createElement('div');
|
|
3282
|
+
const message = resolveWysiwygLinterFixMessage(issue.message);
|
|
3283
|
+
icon.className = `${ISSUE_ICON_CLASS_NAME} ${level}-scope`;
|
|
3284
|
+
icon.setAttribute(ISSUE_DATASET_NAME, issue.id);
|
|
3285
|
+
|
|
3286
|
+
if (typeof message === 'string') {
|
|
3287
|
+
icon.title = message;
|
|
3288
|
+
}
|
|
3289
|
+
|
|
3290
|
+
return icon;
|
|
3291
|
+
}
|
|
3292
|
+
|
|
3293
|
+
function runAllLinterPlugins(doc, plugins) {
|
|
3294
|
+
const decorations = [];
|
|
3295
|
+
const issues = plugins.map(RegisteredLinterPlugin => {
|
|
3296
|
+
return new RegisteredLinterPlugin(doc).scan().getResults();
|
|
3297
|
+
}).flat();
|
|
3298
|
+
issues.forEach(issue => {
|
|
3299
|
+
const {
|
|
3300
|
+
level = 'warning'
|
|
3301
|
+
} = issue;
|
|
3302
|
+
const message = resolveWysiwygLinterFixMessage(issue.message);
|
|
3303
|
+
decorations.push(prosemirrorView.Decoration.inline(issue.from, issue.to, {
|
|
3304
|
+
class: `${PROBLEM_CLASS_NAME} ${level}-scope`,
|
|
3305
|
+
'data-issue-id': issue.id,
|
|
3306
|
+
title: typeof message === 'string' ? message : undefined
|
|
3307
|
+
}), prosemirrorView.Decoration.widget(issue.from, view => renderIcon(view, issue)));
|
|
3308
|
+
});
|
|
3309
|
+
const decorationSet = prosemirrorView.DecorationSet.create(doc, decorations);
|
|
3310
|
+
return {
|
|
3311
|
+
issues,
|
|
3312
|
+
decorationSet
|
|
3313
|
+
};
|
|
3314
|
+
}
|
|
3315
|
+
|
|
3316
|
+
const WysiwygLinter = createWysiwygExtension(ctx => {
|
|
3317
|
+
function showMenu(view, issue, event) {
|
|
3318
|
+
const message = resolveWysiwygLinterFixMessage(issue.message);
|
|
3319
|
+
const variant = ctx.vui.setting('containedVariant');
|
|
3320
|
+
const scope = ctx.vui.setting(`${issue.level}Scope`);
|
|
3321
|
+
return ctx.vui.menu({
|
|
3322
|
+
activator: event,
|
|
3323
|
+
content: stack => vue.createVNode("div", {
|
|
3324
|
+
"class": "v-linter__issue-menu__body"
|
|
3325
|
+
}, [vue.createVNode("div", {
|
|
3326
|
+
"class": ['v-linter__issue-menu__message', `${scope}-scope ${variant}`]
|
|
3327
|
+
}, [message]), issue.fix.length && vue.createVNode("div", {
|
|
3328
|
+
"class": "v-linter__issue-menu__fixers"
|
|
3329
|
+
}, [issue.fix.map((fixer, index) => vue.createVNode("div", {
|
|
3330
|
+
"key": index,
|
|
3331
|
+
"class": "v-linter__issue-menu__fixer"
|
|
3332
|
+
}, [vue.createVNode("button", {
|
|
3333
|
+
"type": "button",
|
|
3334
|
+
"class": "v-linter__issue-menu__fixer__button",
|
|
3335
|
+
"onClick": () => {
|
|
3336
|
+
stack.close();
|
|
3337
|
+
fixer.handler(view, issue);
|
|
3338
|
+
}
|
|
3339
|
+
}, [vue.createVNode("span", {
|
|
3340
|
+
"class": "v-linter__issue-menu__fixer__button__message"
|
|
3341
|
+
}, [resolveWysiwygLinterFixMessage(fixer.message)])])]))])])
|
|
3342
|
+
});
|
|
3343
|
+
}
|
|
3344
|
+
|
|
3345
|
+
return core.Extension.create({
|
|
3346
|
+
name: 'linter',
|
|
3347
|
+
|
|
3348
|
+
addOptions() {
|
|
3349
|
+
return {
|
|
3350
|
+
plugins: []
|
|
3351
|
+
};
|
|
3352
|
+
},
|
|
3353
|
+
|
|
3354
|
+
addStorage() {
|
|
3355
|
+
return {
|
|
3356
|
+
issues: []
|
|
3357
|
+
};
|
|
3358
|
+
},
|
|
3359
|
+
|
|
3360
|
+
addProseMirrorPlugins() {
|
|
3361
|
+
const {
|
|
3362
|
+
plugins
|
|
3363
|
+
} = this.options;
|
|
3364
|
+
const {
|
|
3365
|
+
storage
|
|
3366
|
+
} = this;
|
|
3367
|
+
|
|
3368
|
+
const runAll = doc => {
|
|
3369
|
+
const {
|
|
3370
|
+
issues,
|
|
3371
|
+
decorationSet
|
|
3372
|
+
} = runAllLinterPlugins(doc, plugins);
|
|
3373
|
+
storage.issues = issues;
|
|
3374
|
+
return decorationSet;
|
|
3375
|
+
};
|
|
3376
|
+
|
|
3377
|
+
const getIssue = id => storage.issues.find(issue => issue.id === id);
|
|
3378
|
+
|
|
3379
|
+
const getIssueElementByEvent = source => {
|
|
3380
|
+
if (!source || !(source instanceof HTMLElement)) {
|
|
3381
|
+
return;
|
|
3382
|
+
}
|
|
3383
|
+
|
|
3384
|
+
const issueId = source.getAttribute(ISSUE_DATASET_NAME);
|
|
3385
|
+
const issue = issueId && getIssue(issueId);
|
|
3386
|
+
if (!issue) return;
|
|
3387
|
+
return {
|
|
3388
|
+
issue
|
|
3389
|
+
};
|
|
3390
|
+
};
|
|
3391
|
+
|
|
3392
|
+
return [new prosemirrorState.Plugin({
|
|
3393
|
+
key: new prosemirrorState.PluginKey('linter'),
|
|
3394
|
+
state: {
|
|
3395
|
+
init(_, {
|
|
3396
|
+
doc
|
|
3397
|
+
}) {
|
|
3398
|
+
return runAll(doc);
|
|
3399
|
+
},
|
|
3400
|
+
|
|
3401
|
+
apply(transaction, oldState) {
|
|
3402
|
+
return transaction.docChanged ? runAll(transaction.doc) : oldState;
|
|
3403
|
+
}
|
|
3404
|
+
|
|
3405
|
+
},
|
|
3406
|
+
props: {
|
|
3407
|
+
decorations(state) {
|
|
3408
|
+
return this.getState(state);
|
|
3409
|
+
},
|
|
3410
|
+
|
|
3411
|
+
handleClick(view, _, event) {
|
|
3412
|
+
const info = getIssueElementByEvent(event.target);
|
|
3413
|
+
|
|
3414
|
+
if (!info) {
|
|
3415
|
+
return false;
|
|
3416
|
+
}
|
|
3417
|
+
|
|
3418
|
+
const {
|
|
3419
|
+
issue
|
|
3420
|
+
} = info;
|
|
3421
|
+
const {
|
|
3422
|
+
from,
|
|
3423
|
+
to
|
|
3424
|
+
} = issue;
|
|
3425
|
+
|
|
3426
|
+
const focus = () => view.dispatch(view.state.tr.setSelection(prosemirrorState.TextSelection.create(view.state.doc, from, to)).scrollIntoView());
|
|
3427
|
+
|
|
3428
|
+
focus();
|
|
3429
|
+
showMenu(view, issue, event);
|
|
3430
|
+
return true;
|
|
3431
|
+
}
|
|
3432
|
+
|
|
3433
|
+
}
|
|
3434
|
+
})];
|
|
3435
|
+
}
|
|
3436
|
+
|
|
3437
|
+
});
|
|
3438
|
+
});
|
|
3439
|
+
|
|
3440
|
+
let IDX = 256, BUFFER;
|
|
3441
|
+
const HEX = [];
|
|
3442
|
+
while (IDX--)
|
|
3443
|
+
HEX[IDX] = (IDX + 256).toString(16).substring(1);
|
|
3444
|
+
function cheepUUID() {
|
|
3445
|
+
let i = 0, num, out = '';
|
|
3446
|
+
if (!BUFFER || IDX + 16 > 256) {
|
|
3447
|
+
BUFFER = Array((i = 256));
|
|
3448
|
+
while (i--)
|
|
3449
|
+
BUFFER[i] = (256 * Math.random()) | 0;
|
|
3450
|
+
i = IDX = 0;
|
|
3451
|
+
}
|
|
3452
|
+
for (; i < 16; i++) {
|
|
3453
|
+
num = BUFFER[IDX + i];
|
|
3454
|
+
if (i == 6)
|
|
3455
|
+
out += HEX[(num & 15) | 64];
|
|
3456
|
+
else if (i == 8)
|
|
3457
|
+
out += HEX[(num & 63) | 128];
|
|
3458
|
+
else
|
|
3459
|
+
out += HEX[num];
|
|
3460
|
+
if (i & 1 && i > 1 && i < 11)
|
|
3461
|
+
out += '-';
|
|
3462
|
+
}
|
|
3463
|
+
IDX++;
|
|
3464
|
+
return out;
|
|
3465
|
+
}
|
|
3466
|
+
|
|
3467
|
+
class WysiwygLinterPlugin {
|
|
3468
|
+
doc;
|
|
3469
|
+
results = [];
|
|
3470
|
+
constructor(doc) {
|
|
3471
|
+
this.doc = doc;
|
|
3472
|
+
}
|
|
3473
|
+
record(result) {
|
|
3474
|
+
const { fix = [] } = result;
|
|
3475
|
+
this.results.push({
|
|
3476
|
+
level: 'error',
|
|
3477
|
+
id: cheepUUID(),
|
|
3478
|
+
...result,
|
|
3479
|
+
fix: Array.isArray(fix) ? fix : [fix],
|
|
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: () => vue.createVNode("span", null, [vue.createVNode("code", null, [fixValue]), vue.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 = core.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
|
+
|
|
3130
3633
|
const WysiwygBulletListTool = (vui, options) => {
|
|
3131
3634
|
const tool = {
|
|
3132
3635
|
key: 'bulletList',
|
|
@@ -3267,18 +3770,73 @@ const WysiwygOrderedListTool = (vui, options) => {
|
|
|
3267
3770
|
return tool;
|
|
3268
3771
|
};
|
|
3269
3772
|
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
};
|
|
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 vue.createVNode("span", {
|
|
3786
|
+
"class": "v-wysiwyg-color-tool__button"
|
|
3787
|
+
}, [vue.createVNode(VIcon, {
|
|
3788
|
+
"class": "v-wysiwyg-color-tool__button__icon",
|
|
3789
|
+
"name": vui.icon('editorTextColor')
|
|
3790
|
+
}, null), vue.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 => vue.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) => vue.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
|
+
}, [vue.createVNode("span", {
|
|
3823
|
+
"class": "v-wysiwyg-color-tool__item__color",
|
|
3824
|
+
"style": item.color ? {
|
|
3825
|
+
color: item.color
|
|
3826
|
+
} : {}
|
|
3827
|
+
}, null), opts.withLabel && vue.createVNode("span", {
|
|
3828
|
+
"class": "v-wysiwyg-color-tool__item__name"
|
|
3829
|
+
}, [item.name])]))])
|
|
3830
|
+
});
|
|
3831
|
+
},
|
|
3832
|
+
floating: true,
|
|
3833
|
+
extensions: [TextStyle__default, WysiwygColorExtension]
|
|
3834
|
+
};
|
|
3835
|
+
return tool;
|
|
3836
|
+
};
|
|
3837
|
+
|
|
3838
|
+
return WysiwygColorTool;
|
|
3839
|
+
}
|
|
3282
3840
|
|
|
3283
3841
|
const {
|
|
3284
3842
|
props,
|
|
@@ -4895,6 +5453,10 @@ class VuiService {
|
|
|
4895
5453
|
return this.stack.snackbar(...args);
|
|
4896
5454
|
}
|
|
4897
5455
|
|
|
5456
|
+
menu(...args) {
|
|
5457
|
+
return this.stack.menu(...args);
|
|
5458
|
+
}
|
|
5459
|
+
|
|
4898
5460
|
formPrompt(settings, slot) {
|
|
4899
5461
|
let form;
|
|
4900
5462
|
const options = {
|
|
@@ -5119,13 +5681,19 @@ exports.VuiInjectionKey = VuiInjectionKey;
|
|
|
5119
5681
|
exports.VuiPlugin = VuiPlugin;
|
|
5120
5682
|
exports.VuiService = VuiService;
|
|
5121
5683
|
exports.WysiwygBulletListTool = WysiwygBulletListTool;
|
|
5684
|
+
exports.WysiwygColorExtension = WysiwygColorExtension;
|
|
5685
|
+
exports.WysiwygEditorInitializeContext = WysiwygEditorInitializeContext;
|
|
5122
5686
|
exports.WysiwygFormatBoldTool = WysiwygFormatBoldTool;
|
|
5123
5687
|
exports.WysiwygFormatItalicTool = WysiwygFormatItalicTool;
|
|
5124
5688
|
exports.WysiwygFormatUnderlineTool = WysiwygFormatUnderlineTool;
|
|
5125
5689
|
exports.WysiwygHistoryTool = WysiwygHistoryTool;
|
|
5126
5690
|
exports.WysiwygLinkTool = WysiwygLinkTool;
|
|
5691
|
+
exports.WysiwygLinter = WysiwygLinter;
|
|
5692
|
+
exports.WysiwygLinterBadWords = WysiwygLinterBadWords;
|
|
5693
|
+
exports.WysiwygLinterHeadingLevel = WysiwygLinterHeadingLevel;
|
|
5694
|
+
exports.WysiwygLinterPlugin = WysiwygLinterPlugin;
|
|
5695
|
+
exports.WysiwygLinterPunctuation = WysiwygLinterPunctuation;
|
|
5127
5696
|
exports.WysiwygOrderedListTool = WysiwygOrderedListTool;
|
|
5128
|
-
exports.WysiwygTextColorTool = WysiwygTextColorTool;
|
|
5129
5697
|
exports.configureDataTableDefaults = configureDataTableDefaults;
|
|
5130
5698
|
exports.createAvatarProps = createAvatarProps;
|
|
5131
5699
|
exports.createCardProps = createCardProps;
|
|
@@ -5139,6 +5707,8 @@ exports.createPaperBaseProps = createPaperBaseProps;
|
|
|
5139
5707
|
exports.createPaperProps = createPaperProps;
|
|
5140
5708
|
exports.createRequiredChipRenderer = createRequiredChipRenderer;
|
|
5141
5709
|
exports.createVFormProps = createVFormProps;
|
|
5710
|
+
exports.createWysiwygColorTool = createWysiwygColorTool;
|
|
5711
|
+
exports.createWysiwygExtension = createWysiwygExtension;
|
|
5142
5712
|
exports.defineFormSelectorComponent = defineFormSelectorComponent;
|
|
5143
5713
|
exports.iconProps = iconProps;
|
|
5144
5714
|
exports.installVuiPlugin = installVuiPlugin;
|
|
@@ -5153,6 +5723,7 @@ exports.renderNavigationItemInput = renderNavigationItemInput;
|
|
|
5153
5723
|
exports.resolveNavigationItemInput = resolveNavigationItemInput;
|
|
5154
5724
|
exports.resolveRawIconProp = resolveRawIconProp;
|
|
5155
5725
|
exports.resolveRawWysiwygEditorTools = resolveRawWysiwygEditorTools;
|
|
5726
|
+
exports.resolveRawWysiwygExtensions = resolveRawWysiwygExtensions;
|
|
5156
5727
|
exports.splitAttrs = splitAttrs;
|
|
5157
5728
|
exports.useControl = useControl;
|
|
5158
5729
|
exports.useControlField = useControlField;
|