@fastkit/vui 0.7.61 → 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 +437 -228
- package/dist/vui.cjs.prod.js +437 -228
- package/dist/vui.css +166 -12
- package/dist/vui.d.ts +101 -54
- package/dist/vui.min.css +1 -1
- package/dist/vui.min.css.map +1 -1
- package/dist/vui.mjs +431 -225
- 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/VWysiwygEditor.scss +4 -0
- package/src/components/VWysiwygEditor/VWysiwygEditor.tsx +19 -9
- 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 +1 -0
- package/src/components/VWysiwygEditor/extensions/linter/.DS_Store +0 -0
- package/src/components/VWysiwygEditor/extensions/linter/Linter.scss +80 -15
- package/src/components/VWysiwygEditor/extensions/linter/Linter.tsx +210 -0
- package/src/components/VWysiwygEditor/extensions/linter/LinterPlugin.ts +29 -11
- package/src/components/VWysiwygEditor/extensions/linter/index.ts +3 -3
- package/src/components/VWysiwygEditor/extensions/linter/plugins/BadWords.tsx +50 -0
- package/src/components/VWysiwygEditor/extensions/linter/plugins/HeadingLevel.ts +9 -3
- package/src/components/VWysiwygEditor/extensions/linter/plugins/Punctuation.ts +9 -3
- package/src/components/VWysiwygEditor/extensions/linter/plugins/index.ts +3 -3
- package/src/components/VWysiwygEditor/extensions/linter/utils.ts +28 -0
- package/src/components/VWysiwygEditor/schemes.ts +87 -5
- 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/extensions/linter/Linter.ts +0 -262
- package/src/components/VWysiwygEditor/extensions/linter/plugins/BadWords.ts +0 -34
- package/src/components/VWysiwygEditor/tools/text-color.ts +0 -14
package/dist/vui.mjs
CHANGED
|
@@ -8,14 +8,14 @@ import { getDocumentScroller as getDocumentScroller$1 } from '@fastkit/vue-scrol
|
|
|
8
8
|
import { VProgressCircular } from '@fastkit/vue-loading';
|
|
9
9
|
export * from '@fastkit/vue-loading';
|
|
10
10
|
import { useScopeColorClass, colorSchemeProps, useColorClasses, toScopeColorClass } from '@fastkit/vue-color-scheme';
|
|
11
|
-
import { loadImage, IN_WINDOW, isPromise
|
|
11
|
+
import { loadImage, IN_WINDOW, isPromise } from '@fastkit/helpers';
|
|
12
12
|
export { ICON_NAMES } from '@fastkit/icon-font';
|
|
13
13
|
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
17
|
import { Extension } from '@tiptap/core';
|
|
18
|
-
import {
|
|
18
|
+
import { Decoration, DecorationSet } from 'prosemirror-view';
|
|
19
19
|
import { Plugin, PluginKey, TextSelection } from 'prosemirror-state';
|
|
20
20
|
import { BulletList } from '@tiptap/extension-bullet-list';
|
|
21
21
|
import { Bold } from '@tiptap/extension-bold';
|
|
@@ -25,6 +25,7 @@ import { History } from '@tiptap/extension-history';
|
|
|
25
25
|
import { Link } from '@tiptap/extension-link';
|
|
26
26
|
import { validateIf, url } from '@fastkit/rules';
|
|
27
27
|
import { OrderedList } from '@tiptap/extension-ordered-list';
|
|
28
|
+
import TextStyle from '@tiptap/extension-text-style';
|
|
28
29
|
|
|
29
30
|
const CONTROL_SIZES = ['sm', 'md', 'lg'];
|
|
30
31
|
const CONTROL_FIELD_VARIANTS = ['outlined', 'filled', 'flat'];
|
|
@@ -1227,6 +1228,7 @@ const VButtonGroup = defineComponent({
|
|
|
1227
1228
|
|
|
1228
1229
|
setup(props, ctx) {
|
|
1229
1230
|
return () => {
|
|
1231
|
+
let hasIcon = false;
|
|
1230
1232
|
let buttonLength = 0;
|
|
1231
1233
|
let tmp = renderSlotOrEmpty$1(ctx.slots) || [];
|
|
1232
1234
|
|
|
@@ -1243,6 +1245,11 @@ const VButtonGroup = defineComponent({
|
|
|
1243
1245
|
}
|
|
1244
1246
|
|
|
1245
1247
|
buttonLength++;
|
|
1248
|
+
|
|
1249
|
+
if (!hasIcon && node.props && !!node.props.icon) {
|
|
1250
|
+
hasIcon = true;
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1246
1253
|
return {
|
|
1247
1254
|
isButton: true,
|
|
1248
1255
|
node
|
|
@@ -1259,6 +1266,7 @@ const VButtonGroup = defineComponent({
|
|
|
1259
1266
|
...childProps,
|
|
1260
1267
|
rouded: false,
|
|
1261
1268
|
class: ['v-button-group__item', {
|
|
1269
|
+
'v-button--icon': hasIcon,
|
|
1262
1270
|
'v-button-group__item--has-left': hasLeft,
|
|
1263
1271
|
'v-button-group__item--has-right': hasRight
|
|
1264
1272
|
}]
|
|
@@ -1268,7 +1276,9 @@ const VButtonGroup = defineComponent({
|
|
|
1268
1276
|
return child.node;
|
|
1269
1277
|
});
|
|
1270
1278
|
return createVNode("div", {
|
|
1271
|
-
"class":
|
|
1279
|
+
"class": ['v-button-group', {
|
|
1280
|
+
'v-button-group--has-icon': hasIcon
|
|
1281
|
+
}]
|
|
1272
1282
|
}, [$children]);
|
|
1273
1283
|
};
|
|
1274
1284
|
}
|
|
@@ -2887,7 +2897,12 @@ const prefixedEventName = (source) => {
|
|
|
2887
2897
|
};
|
|
2888
2898
|
class WysiwygEditorInitializeContext {
|
|
2889
2899
|
listeners = {};
|
|
2890
|
-
|
|
2900
|
+
_vui;
|
|
2901
|
+
get vui() {
|
|
2902
|
+
return this._vui();
|
|
2903
|
+
}
|
|
2904
|
+
constructor(vuiGetter, opts = {}) {
|
|
2905
|
+
this._vui = vuiGetter;
|
|
2891
2906
|
EDITOR_EVENTS.forEach((event) => {
|
|
2892
2907
|
this.listeners[event] = [];
|
|
2893
2908
|
const prefixed = prefixedEventName(event);
|
|
@@ -2916,7 +2931,44 @@ class WysiwygEditorInitializeContext {
|
|
|
2916
2931
|
return opts;
|
|
2917
2932
|
}
|
|
2918
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
|
+
}
|
|
2919
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
|
+
}
|
|
2920
2972
|
return typeof raw === 'function' ? raw(ctx) : raw;
|
|
2921
2973
|
}
|
|
2922
2974
|
function resolveRawWysiwygExtensions(raws, ctx) {
|
|
@@ -2985,7 +3037,7 @@ const VWysiwygEditor = defineComponent({
|
|
|
2985
3037
|
nodeType: VUI_WYSIWYG_EDITOR_SYMBOL,
|
|
2986
3038
|
validationValue: () => textRef.value
|
|
2987
3039
|
});
|
|
2988
|
-
const initializeCtx = new WysiwygEditorInitializeContext({
|
|
3040
|
+
const initializeCtx = new WysiwygEditorInitializeContext(() => vui, {
|
|
2989
3041
|
onCreate: ({
|
|
2990
3042
|
editor
|
|
2991
3043
|
}) => {
|
|
@@ -3084,9 +3136,9 @@ const VWysiwygEditor = defineComponent({
|
|
|
3084
3136
|
const _editor = editor.value;
|
|
3085
3137
|
const variant = vui.setting(bubbleMenu ? 'containedVariant' : 'plainVariant');
|
|
3086
3138
|
return createVNode(VButtonGroup, {
|
|
3087
|
-
"class": {
|
|
3139
|
+
"class": ['v-wysiwyg-editor__toolbar', {
|
|
3088
3140
|
'v-wysiwyg-editor__floating-toolbar': props.floatingToolbar
|
|
3089
|
-
},
|
|
3141
|
+
}],
|
|
3090
3142
|
"variant": variant
|
|
3091
3143
|
}, _isSlot$7(_slot = tools.map(({
|
|
3092
3144
|
key,
|
|
@@ -3098,16 +3150,23 @@ const VWysiwygEditor = defineComponent({
|
|
|
3098
3150
|
const isActive = !!_editor && resolveContextableValue(context, active);
|
|
3099
3151
|
const isDisabled = !inputControl.canOperation || !!_editor && resolveContextableValue(context, disabled);
|
|
3100
3152
|
let iconName;
|
|
3153
|
+
let child;
|
|
3101
3154
|
|
|
3102
3155
|
if (typeof icon === 'function') {
|
|
3103
3156
|
if (_editor) {
|
|
3104
|
-
|
|
3157
|
+
const _child = icon(context);
|
|
3158
|
+
|
|
3159
|
+
if (typeof _child === 'function') {
|
|
3160
|
+
child = _child();
|
|
3161
|
+
} else {
|
|
3162
|
+
iconName = child;
|
|
3163
|
+
}
|
|
3105
3164
|
}
|
|
3106
3165
|
} else {
|
|
3107
3166
|
iconName = icon;
|
|
3108
3167
|
}
|
|
3109
3168
|
|
|
3110
|
-
if (!iconName) return;
|
|
3169
|
+
if (!iconName && child == null) return;
|
|
3111
3170
|
return createVNode(VButton, {
|
|
3112
3171
|
"tabindex": -1,
|
|
3113
3172
|
"key": key,
|
|
@@ -3115,7 +3174,9 @@ const VWysiwygEditor = defineComponent({
|
|
|
3115
3174
|
"onClick": ev => onClick(context, ev),
|
|
3116
3175
|
"color": isActive ? toolButtonActiveColor : undefined,
|
|
3117
3176
|
"disabled": isDisabled
|
|
3118
|
-
},
|
|
3177
|
+
}, _isSlot$7(child) ? child : {
|
|
3178
|
+
default: () => [child]
|
|
3179
|
+
});
|
|
3119
3180
|
})) ? _slot : {
|
|
3120
3181
|
default: () => [_slot]
|
|
3121
3182
|
});
|
|
@@ -3199,190 +3260,217 @@ function resolveContextableValue(ctx, source) {
|
|
|
3199
3260
|
return typeof source === 'function' ? source(ctx) : source;
|
|
3200
3261
|
}
|
|
3201
3262
|
|
|
3202
|
-
const PROBLEM_CLASS_NAME = 'v-linter__problem';
|
|
3203
|
-
const ISSUE_ICON_CLASS_NAME = 'v-linter__issue-icon';
|
|
3204
|
-
const
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
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
|
+
} = issue;
|
|
3296
|
+
const message = resolveWysiwygLinterFixMessage(issue.message);
|
|
3297
|
+
decorations.push(Decoration.inline(issue.from, issue.to, {
|
|
3298
|
+
class: `${PROBLEM_CLASS_NAME} ${level}-scope`,
|
|
3299
|
+
'data-issue-id': issue.id,
|
|
3300
|
+
title: typeof message === 'string' ? message : undefined
|
|
3301
|
+
}), Decoration.widget(issue.from, view => renderIcon(view, issue)));
|
|
3302
|
+
});
|
|
3303
|
+
const decorationSet = DecorationSet.create(doc, decorations);
|
|
3304
|
+
return {
|
|
3305
|
+
issues,
|
|
3306
|
+
decorationSet
|
|
3307
|
+
};
|
|
3308
|
+
}
|
|
3309
|
+
|
|
3310
|
+
const WysiwygLinter = createWysiwygExtension(ctx => {
|
|
3311
|
+
function showMenu(view, issue, event) {
|
|
3312
|
+
const message = resolveWysiwygLinterFixMessage(issue.message);
|
|
3313
|
+
const variant = ctx.vui.setting('containedVariant');
|
|
3314
|
+
const scope = ctx.vui.setting(`${issue.level}Scope`);
|
|
3315
|
+
return ctx.vui.menu({
|
|
3316
|
+
activator: event,
|
|
3317
|
+
content: stack => createVNode("div", {
|
|
3318
|
+
"class": "v-linter__issue-menu__body"
|
|
3319
|
+
}, [createVNode("div", {
|
|
3320
|
+
"class": ['v-linter__issue-menu__message', `${scope}-scope ${variant}`]
|
|
3321
|
+
}, [message]), issue.fix.length && createVNode("div", {
|
|
3322
|
+
"class": "v-linter__issue-menu__fixers"
|
|
3323
|
+
}, [issue.fix.map((fixer, index) => createVNode("div", {
|
|
3324
|
+
"key": index,
|
|
3325
|
+
"class": "v-linter__issue-menu__fixer"
|
|
3326
|
+
}, [createVNode("button", {
|
|
3327
|
+
"type": "button",
|
|
3328
|
+
"class": "v-linter__issue-menu__fixer__button",
|
|
3329
|
+
"onClick": () => {
|
|
3330
|
+
stack.close();
|
|
3331
|
+
fixer.handler(view, issue);
|
|
3332
|
+
}
|
|
3333
|
+
}, [createVNode("span", {
|
|
3334
|
+
"class": "v-linter__issue-menu__fixer__button__message"
|
|
3335
|
+
}, [resolveWysiwygLinterFixMessage(fixer.message)])])]))])])
|
|
3336
|
+
});
|
|
3337
|
+
}
|
|
3338
|
+
|
|
3339
|
+
return Extension.create({
|
|
3340
|
+
name: 'linter',
|
|
3341
|
+
|
|
3342
|
+
addOptions() {
|
|
3343
|
+
return {
|
|
3344
|
+
plugins: []
|
|
3345
|
+
};
|
|
3346
|
+
},
|
|
3347
|
+
|
|
3348
|
+
addStorage() {
|
|
3349
|
+
return {
|
|
3350
|
+
issues: []
|
|
3351
|
+
};
|
|
3352
|
+
},
|
|
3353
|
+
|
|
3354
|
+
addProseMirrorPlugins() {
|
|
3355
|
+
const {
|
|
3356
|
+
plugins
|
|
3357
|
+
} = this.options;
|
|
3358
|
+
const {
|
|
3359
|
+
storage
|
|
3360
|
+
} = this;
|
|
3361
|
+
|
|
3362
|
+
const runAll = doc => {
|
|
3363
|
+
const {
|
|
3364
|
+
issues,
|
|
3365
|
+
decorationSet
|
|
3366
|
+
} = runAllLinterPlugins(doc, plugins);
|
|
3367
|
+
storage.issues = issues;
|
|
3368
|
+
return decorationSet;
|
|
3369
|
+
};
|
|
3370
|
+
|
|
3371
|
+
const getIssue = id => storage.issues.find(issue => issue.id === id);
|
|
3372
|
+
|
|
3373
|
+
const getIssueElementByEvent = source => {
|
|
3374
|
+
if (!source || !(source instanceof HTMLElement)) {
|
|
3375
|
+
return;
|
|
3376
|
+
}
|
|
3377
|
+
|
|
3378
|
+
const issueId = source.getAttribute(ISSUE_DATASET_NAME);
|
|
3379
|
+
const issue = issueId && getIssue(issueId);
|
|
3380
|
+
if (!issue) return;
|
|
3381
|
+
return {
|
|
3382
|
+
issue
|
|
3383
|
+
};
|
|
3384
|
+
};
|
|
3385
|
+
|
|
3386
|
+
return [new Plugin({
|
|
3387
|
+
key: new PluginKey('linter'),
|
|
3388
|
+
state: {
|
|
3389
|
+
init(_, {
|
|
3390
|
+
doc
|
|
3391
|
+
}) {
|
|
3392
|
+
return runAll(doc);
|
|
3393
|
+
},
|
|
3394
|
+
|
|
3395
|
+
apply(transaction, oldState) {
|
|
3396
|
+
return transaction.docChanged ? runAll(transaction.doc) : oldState;
|
|
3397
|
+
}
|
|
3398
|
+
|
|
3399
|
+
},
|
|
3400
|
+
props: {
|
|
3401
|
+
decorations(state) {
|
|
3402
|
+
return this.getState(state);
|
|
3403
|
+
},
|
|
3404
|
+
|
|
3405
|
+
handleClick(view, _, event) {
|
|
3406
|
+
const info = getIssueElementByEvent(event.target);
|
|
3407
|
+
|
|
3408
|
+
if (!info) {
|
|
3409
|
+
return false;
|
|
3410
|
+
}
|
|
3411
|
+
|
|
3412
|
+
const {
|
|
3413
|
+
issue
|
|
3414
|
+
} = info;
|
|
3415
|
+
const {
|
|
3416
|
+
from,
|
|
3417
|
+
to
|
|
3418
|
+
} = issue;
|
|
3419
|
+
|
|
3420
|
+
const focus = () => view.dispatch(view.state.tr.setSelection(TextSelection.create(view.state.doc, from, to)).scrollIntoView());
|
|
3421
|
+
|
|
3422
|
+
focus();
|
|
3423
|
+
showMenu(view, issue, event);
|
|
3424
|
+
return true;
|
|
3425
|
+
}
|
|
3426
|
+
|
|
3427
|
+
}
|
|
3428
|
+
})];
|
|
3429
|
+
}
|
|
3430
|
+
|
|
3431
|
+
});
|
|
3432
|
+
});
|
|
3433
|
+
|
|
3434
|
+
let IDX = 256, BUFFER;
|
|
3435
|
+
const HEX = [];
|
|
3436
|
+
while (IDX--)
|
|
3437
|
+
HEX[IDX] = (IDX + 256).toString(16).substring(1);
|
|
3438
|
+
function cheepUUID() {
|
|
3439
|
+
let i = 0, num, out = '';
|
|
3440
|
+
if (!BUFFER || IDX + 16 > 256) {
|
|
3441
|
+
BUFFER = Array((i = 256));
|
|
3442
|
+
while (i--)
|
|
3443
|
+
BUFFER[i] = (256 * Math.random()) | 0;
|
|
3444
|
+
i = IDX = 0;
|
|
3240
3445
|
}
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3446
|
+
for (; i < 16; i++) {
|
|
3447
|
+
num = BUFFER[IDX + i];
|
|
3448
|
+
if (i == 6)
|
|
3449
|
+
out += HEX[(num & 15) | 64];
|
|
3450
|
+
else if (i == 8)
|
|
3451
|
+
out += HEX[(num & 63) | 128];
|
|
3452
|
+
else
|
|
3453
|
+
out += HEX[num];
|
|
3454
|
+
if (i & 1 && i > 1 && i < 11)
|
|
3455
|
+
out += '-';
|
|
3244
3456
|
}
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
}
|
|
3249
|
-
function getMenuElement(source) {
|
|
3250
|
-
return getIssueElement(source, ISSUE_MENU_CLASS_NAME);
|
|
3251
|
-
}
|
|
3252
|
-
function getProblemElement(source) {
|
|
3253
|
-
return getIssueElement(source, PROBLEM_CLASS_NAME);
|
|
3254
|
-
}
|
|
3255
|
-
function runAllLinterPlugins(doc, plugins) {
|
|
3256
|
-
const decorations = [];
|
|
3257
|
-
const results = plugins
|
|
3258
|
-
.map((RegisteredLinterPlugin) => {
|
|
3259
|
-
return new RegisteredLinterPlugin(doc).scan().getResults();
|
|
3260
|
-
})
|
|
3261
|
-
.flat();
|
|
3262
|
-
results.forEach((issue) => {
|
|
3263
|
-
const { level = 'warning' } = issue;
|
|
3264
|
-
decorations.push(Decoration.inline(issue.from, issue.to, {
|
|
3265
|
-
class: `${PROBLEM_CLASS_NAME} ${level}-scope`,
|
|
3266
|
-
'data-issue': JSON.stringify(issue),
|
|
3267
|
-
title: issue.message,
|
|
3268
|
-
}), Decoration.widget(issue.from, (view) => renderIcon(view, issue)), Decoration.widget(issue.from, (view) => renderMenu(view, issue)));
|
|
3269
|
-
});
|
|
3270
|
-
return DecorationSet.create(doc, decorations);
|
|
3271
|
-
}
|
|
3272
|
-
function updateMenusPosition(editorElement) {
|
|
3273
|
-
const { left, right } = editorElement.getBoundingClientRect();
|
|
3274
|
-
const menus = Array.from(editorElement.querySelectorAll(`.${ISSUE_MENU_CLASS_NAME}`));
|
|
3275
|
-
menus.forEach((menu) => {
|
|
3276
|
-
const { left: menuLeft, right: menuRight } = menu.getBoundingClientRect();
|
|
3277
|
-
const overflow = menuRight - right;
|
|
3278
|
-
let offset = 0;
|
|
3279
|
-
if (overflow > 0) {
|
|
3280
|
-
offset = -overflow;
|
|
3281
|
-
if (menuLeft + offset < left) {
|
|
3282
|
-
offset = 0;
|
|
3283
|
-
}
|
|
3284
|
-
}
|
|
3285
|
-
menu.style.transform = `translateX(${offset}px)`;
|
|
3286
|
-
});
|
|
3287
|
-
}
|
|
3288
|
-
const debouncedUpdateMenusPosition = debounce(updateMenusPosition, 250);
|
|
3289
|
-
const Linter = Extension.create({
|
|
3290
|
-
name: 'linter',
|
|
3291
|
-
addOptions() {
|
|
3292
|
-
return {
|
|
3293
|
-
plugins: [],
|
|
3294
|
-
};
|
|
3295
|
-
},
|
|
3296
|
-
onCreate() {
|
|
3297
|
-
const { dom } = this.editor.view;
|
|
3298
|
-
debouncedUpdateMenusPosition(dom);
|
|
3299
|
-
},
|
|
3300
|
-
onUpdate() {
|
|
3301
|
-
const { dom } = this.editor.view;
|
|
3302
|
-
debouncedUpdateMenusPosition(dom);
|
|
3303
|
-
},
|
|
3304
|
-
addProseMirrorPlugins() {
|
|
3305
|
-
const { plugins } = this.options;
|
|
3306
|
-
return [
|
|
3307
|
-
new Plugin({
|
|
3308
|
-
key: new PluginKey('linter'),
|
|
3309
|
-
state: {
|
|
3310
|
-
init(_, { doc }) {
|
|
3311
|
-
return runAllLinterPlugins(doc, plugins);
|
|
3312
|
-
},
|
|
3313
|
-
apply(transaction, oldState) {
|
|
3314
|
-
return transaction.docChanged
|
|
3315
|
-
? runAllLinterPlugins(transaction.doc, plugins)
|
|
3316
|
-
: oldState;
|
|
3317
|
-
},
|
|
3318
|
-
},
|
|
3319
|
-
props: {
|
|
3320
|
-
decorations(state) {
|
|
3321
|
-
return this.getState(state);
|
|
3322
|
-
},
|
|
3323
|
-
handleClick(view, _, event) {
|
|
3324
|
-
const activeMenus = Array.from(view.dom.querySelectorAll(`.${ISSUE_MENU_CLASS_NAME}--active`));
|
|
3325
|
-
activeMenus.forEach((menu) => menu.classList.remove(`${ISSUE_MENU_CLASS_NAME}--active`));
|
|
3326
|
-
const problem = getProblemElement(event.target);
|
|
3327
|
-
const issueString = problem && problem.dataset['issue'];
|
|
3328
|
-
const issue = issueString && JSON.parse(issueString);
|
|
3329
|
-
if (issue) {
|
|
3330
|
-
const menuWrapper = problem.previousElementSibling;
|
|
3331
|
-
const menu = menuWrapper &&
|
|
3332
|
-
menuWrapper.querySelector(`.${ISSUE_MENU_CLASS_NAME}`);
|
|
3333
|
-
menu && menu.classList.add(`${ISSUE_MENU_CLASS_NAME}--active`);
|
|
3334
|
-
const { from, to } = issue;
|
|
3335
|
-
view.dispatch(view.state.tr
|
|
3336
|
-
.setSelection(TextSelection.create(view.state.doc, from, to))
|
|
3337
|
-
.scrollIntoView());
|
|
3338
|
-
return true;
|
|
3339
|
-
}
|
|
3340
|
-
const menu = getMenuElement(event.target);
|
|
3341
|
-
if (menu) {
|
|
3342
|
-
menu.classList.add(`${ISSUE_MENU_CLASS_NAME}--active`);
|
|
3343
|
-
}
|
|
3344
|
-
const target = getIconElement(event.target);
|
|
3345
|
-
if (target && target.issue) {
|
|
3346
|
-
const menuWrapper = target.nextElementSibling;
|
|
3347
|
-
const menu = menuWrapper &&
|
|
3348
|
-
menuWrapper.querySelector(`.${ISSUE_MENU_CLASS_NAME}`);
|
|
3349
|
-
menu && menu.classList.add(`${ISSUE_MENU_CLASS_NAME}--active`);
|
|
3350
|
-
const { from, to } = target.issue;
|
|
3351
|
-
view.dispatch(view.state.tr
|
|
3352
|
-
.setSelection(TextSelection.create(view.state.doc, from, to))
|
|
3353
|
-
.scrollIntoView());
|
|
3354
|
-
return true;
|
|
3355
|
-
}
|
|
3356
|
-
return false;
|
|
3357
|
-
},
|
|
3358
|
-
handleDoubleClick(view, _, event) {
|
|
3359
|
-
const target = getIconElement(event.target);
|
|
3360
|
-
if (target && target.issue) {
|
|
3361
|
-
const prob = target.issue;
|
|
3362
|
-
if (prob.fix) {
|
|
3363
|
-
prob.fix(view, prob);
|
|
3364
|
-
view.focus();
|
|
3365
|
-
return true;
|
|
3366
|
-
}
|
|
3367
|
-
}
|
|
3368
|
-
return false;
|
|
3369
|
-
},
|
|
3370
|
-
},
|
|
3371
|
-
}),
|
|
3372
|
-
];
|
|
3373
|
-
},
|
|
3374
|
-
});
|
|
3457
|
+
IDX++;
|
|
3458
|
+
return out;
|
|
3459
|
+
}
|
|
3375
3460
|
|
|
3376
|
-
class
|
|
3461
|
+
class WysiwygLinterPlugin {
|
|
3377
3462
|
doc;
|
|
3378
3463
|
results = [];
|
|
3379
3464
|
constructor(doc) {
|
|
3380
3465
|
this.doc = doc;
|
|
3381
3466
|
}
|
|
3382
3467
|
record(result) {
|
|
3468
|
+
const { fix = [] } = result;
|
|
3383
3469
|
this.results.push({
|
|
3384
3470
|
level: 'error',
|
|
3471
|
+
id: cheepUUID(),
|
|
3385
3472
|
...result,
|
|
3473
|
+
fix: Array.isArray(fix) ? fix : [fix],
|
|
3386
3474
|
});
|
|
3387
3475
|
}
|
|
3388
3476
|
scan() {
|
|
@@ -3393,35 +3481,45 @@ class LinterPlugin {
|
|
|
3393
3481
|
}
|
|
3394
3482
|
}
|
|
3395
3483
|
|
|
3396
|
-
function
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3484
|
+
function WysiwygLinterBadWords(words) {
|
|
3485
|
+
const regex = new RegExp(`\\b(${words.join('|')})\\b`);
|
|
3486
|
+
return class WysiwygLinterBadWords extends WysiwygLinterPlugin {
|
|
3487
|
+
scan() {
|
|
3488
|
+
this.doc.descendants((node, position) => {
|
|
3489
|
+
if (!node.isText) {
|
|
3490
|
+
return;
|
|
3491
|
+
}
|
|
3492
|
+
|
|
3493
|
+
const matches = regex.exec(node.text);
|
|
3494
|
+
|
|
3495
|
+
if (matches) {
|
|
3496
|
+
const fixValue = matches[0] + '!!!!!';
|
|
3497
|
+
this.record({
|
|
3498
|
+
level: 'warning',
|
|
3499
|
+
message: `Try not to say '${matches[0]}'`,
|
|
3500
|
+
from: position + matches.index,
|
|
3501
|
+
to: position + matches.index + matches[0].length,
|
|
3502
|
+
fix: [{
|
|
3503
|
+
message: () => createVNode("span", null, [createVNode("code", null, [fixValue]), createTextVNode("\u306B\u4FEE\u6B63\u3059\u308B\u3002")]),
|
|
3504
|
+
handler: () => {
|
|
3505
|
+
console.log('hoge');
|
|
3506
|
+
}
|
|
3507
|
+
}, {
|
|
3508
|
+
message: 'どうにかする',
|
|
3509
|
+
handler: () => {
|
|
3510
|
+
console.log('hoge');
|
|
3511
|
+
}
|
|
3512
|
+
}]
|
|
3513
|
+
});
|
|
3514
|
+
}
|
|
3515
|
+
});
|
|
3516
|
+
return this;
|
|
3517
|
+
}
|
|
3518
|
+
|
|
3519
|
+
};
|
|
3422
3520
|
}
|
|
3423
3521
|
|
|
3424
|
-
class
|
|
3522
|
+
class WysiwygLinterHeadingLevel extends WysiwygLinterPlugin {
|
|
3425
3523
|
fixHeader(level) {
|
|
3426
3524
|
return function ({ state, dispatch }, issue) {
|
|
3427
3525
|
dispatch(state.tr.setNodeMarkup(issue.from - 1, undefined, { level }));
|
|
@@ -3438,7 +3536,10 @@ class HeadingLevel extends LinterPlugin {
|
|
|
3438
3536
|
message: `Heading too small (${level} under ${lastHeadLevel})`,
|
|
3439
3537
|
from: position + 1,
|
|
3440
3538
|
to: position + 1 + node.content.size,
|
|
3441
|
-
fix:
|
|
3539
|
+
fix: {
|
|
3540
|
+
message: '修正する',
|
|
3541
|
+
handler: this.fixHeader(lastHeadLevel + 1),
|
|
3542
|
+
},
|
|
3442
3543
|
});
|
|
3443
3544
|
}
|
|
3444
3545
|
lastHeadLevel = level;
|
|
@@ -3448,7 +3549,7 @@ class HeadingLevel extends LinterPlugin {
|
|
|
3448
3549
|
}
|
|
3449
3550
|
}
|
|
3450
3551
|
|
|
3451
|
-
class
|
|
3552
|
+
class WysiwygLinterPunctuation extends WysiwygLinterPlugin {
|
|
3452
3553
|
regex = / ([,.!?:]) ?/g;
|
|
3453
3554
|
fix(replacement) {
|
|
3454
3555
|
return function ({ state, dispatch }, issue) {
|
|
@@ -3469,7 +3570,10 @@ class Punctuation extends LinterPlugin {
|
|
|
3469
3570
|
message: 'Suspicious spacing around punctuation',
|
|
3470
3571
|
from: position + matches.index,
|
|
3471
3572
|
to: position + matches.index + matches[0].length,
|
|
3472
|
-
fix:
|
|
3573
|
+
fix: {
|
|
3574
|
+
message: 'Fix it!!!',
|
|
3575
|
+
handler: this.fix(`${matches[1]} `),
|
|
3576
|
+
},
|
|
3473
3577
|
});
|
|
3474
3578
|
}
|
|
3475
3579
|
});
|
|
@@ -3477,6 +3581,49 @@ class Punctuation extends LinterPlugin {
|
|
|
3477
3581
|
}
|
|
3478
3582
|
}
|
|
3479
3583
|
|
|
3584
|
+
const WysiwygColorExtension = Extension.create({
|
|
3585
|
+
name: 'color',
|
|
3586
|
+
addOptions() {
|
|
3587
|
+
return {
|
|
3588
|
+
types: ['textStyle'],
|
|
3589
|
+
};
|
|
3590
|
+
},
|
|
3591
|
+
addGlobalAttributes() {
|
|
3592
|
+
return [
|
|
3593
|
+
{
|
|
3594
|
+
types: this.options.types,
|
|
3595
|
+
attributes: {
|
|
3596
|
+
color: {
|
|
3597
|
+
default: null,
|
|
3598
|
+
parseHTML: (element) => element.style.color.replace(/['"]+/g, ''),
|
|
3599
|
+
renderHTML: (attributes) => {
|
|
3600
|
+
if (!attributes.color) {
|
|
3601
|
+
return {};
|
|
3602
|
+
}
|
|
3603
|
+
return {
|
|
3604
|
+
style: `color: ${attributes.color}`,
|
|
3605
|
+
};
|
|
3606
|
+
},
|
|
3607
|
+
},
|
|
3608
|
+
},
|
|
3609
|
+
},
|
|
3610
|
+
];
|
|
3611
|
+
},
|
|
3612
|
+
addCommands() {
|
|
3613
|
+
return {
|
|
3614
|
+
setColor: (color) => ({ chain }) => {
|
|
3615
|
+
return chain().setMark('textStyle', { color }).run();
|
|
3616
|
+
},
|
|
3617
|
+
unsetColor: () => ({ chain }) => {
|
|
3618
|
+
return chain()
|
|
3619
|
+
.setMark('textStyle', { color: null })
|
|
3620
|
+
.removeEmptyTextStyle()
|
|
3621
|
+
.run();
|
|
3622
|
+
},
|
|
3623
|
+
};
|
|
3624
|
+
},
|
|
3625
|
+
});
|
|
3626
|
+
|
|
3480
3627
|
const WysiwygBulletListTool = (vui, options) => {
|
|
3481
3628
|
const tool = {
|
|
3482
3629
|
key: 'bulletList',
|
|
@@ -3617,18 +3764,73 @@ const WysiwygOrderedListTool = (vui, options) => {
|
|
|
3617
3764
|
return tool;
|
|
3618
3765
|
};
|
|
3619
3766
|
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
};
|
|
3767
|
+
function createWysiwygColorTool(opts) {
|
|
3768
|
+
const WysiwygColorTool = vui => {
|
|
3769
|
+
const tool = {
|
|
3770
|
+
key: 'textColor',
|
|
3771
|
+
// active: ({ editor }) => editor.isActive('textStyle'),
|
|
3772
|
+
icon: ({
|
|
3773
|
+
editor
|
|
3774
|
+
}) => () => {
|
|
3775
|
+
const color = editor.getAttributes('textStyle').color;
|
|
3776
|
+
const style = {
|
|
3777
|
+
color
|
|
3778
|
+
};
|
|
3779
|
+
return createVNode("span", {
|
|
3780
|
+
"class": "v-wysiwyg-color-tool__button"
|
|
3781
|
+
}, [createVNode(VIcon, {
|
|
3782
|
+
"class": "v-wysiwyg-color-tool__button__icon",
|
|
3783
|
+
"name": vui.icon('editorTextColor')
|
|
3784
|
+
}, null), createVNode("span", {
|
|
3785
|
+
"class": "v-wysiwyg-color-tool__button__bar",
|
|
3786
|
+
"style": style
|
|
3787
|
+
}, null)]);
|
|
3788
|
+
},
|
|
3789
|
+
onClick: (ctx, ev) => {
|
|
3790
|
+
ctx.vui.menu({
|
|
3791
|
+
class: 'v-wysiwyg-color-tool__menu',
|
|
3792
|
+
activator: ev,
|
|
3793
|
+
content: stack => createVNode("div", {
|
|
3794
|
+
"class": ['v-wysiwyg-color-tool__items', {
|
|
3795
|
+
'v-wysiwyg-color-tool__items--with-label': opts.withLabel
|
|
3796
|
+
}]
|
|
3797
|
+
}, [opts.items.map((item, index) => createVNode("button", {
|
|
3798
|
+
"key": item.key == null ? index : item.key,
|
|
3799
|
+
"class": "v-wysiwyg-color-tool__item",
|
|
3800
|
+
"type": "button",
|
|
3801
|
+
"onClick": () => {
|
|
3802
|
+
const {
|
|
3803
|
+
color
|
|
3804
|
+
} = item;
|
|
3805
|
+
let command = ctx.editor.chain().focus();
|
|
3806
|
+
|
|
3807
|
+
if (color) {
|
|
3808
|
+
command = command.setColor(color);
|
|
3809
|
+
} else {
|
|
3810
|
+
command = command.unsetColor();
|
|
3811
|
+
}
|
|
3812
|
+
|
|
3813
|
+
command.run();
|
|
3814
|
+
stack.close();
|
|
3815
|
+
}
|
|
3816
|
+
}, [createVNode("span", {
|
|
3817
|
+
"class": "v-wysiwyg-color-tool__item__color",
|
|
3818
|
+
"style": item.color ? {
|
|
3819
|
+
color: item.color
|
|
3820
|
+
} : {}
|
|
3821
|
+
}, null), opts.withLabel && createVNode("span", {
|
|
3822
|
+
"class": "v-wysiwyg-color-tool__item__name"
|
|
3823
|
+
}, [item.name])]))])
|
|
3824
|
+
});
|
|
3825
|
+
},
|
|
3826
|
+
floating: true,
|
|
3827
|
+
extensions: [TextStyle, WysiwygColorExtension]
|
|
3828
|
+
};
|
|
3829
|
+
return tool;
|
|
3830
|
+
};
|
|
3831
|
+
|
|
3832
|
+
return WysiwygColorTool;
|
|
3833
|
+
}
|
|
3632
3834
|
|
|
3633
3835
|
const {
|
|
3634
3836
|
props,
|
|
@@ -5245,6 +5447,10 @@ class VuiService {
|
|
|
5245
5447
|
return this.stack.snackbar(...args);
|
|
5246
5448
|
}
|
|
5247
5449
|
|
|
5450
|
+
menu(...args) {
|
|
5451
|
+
return this.stack.menu(...args);
|
|
5452
|
+
}
|
|
5453
|
+
|
|
5248
5454
|
formPrompt(settings, slot) {
|
|
5249
5455
|
let form;
|
|
5250
5456
|
const options = {
|
|
@@ -5396,4 +5602,4 @@ function installVuiPlugin(app, opts) {
|
|
|
5396
5602
|
return app.use(VuiPlugin, opts);
|
|
5397
5603
|
}
|
|
5398
5604
|
|
|
5399
|
-
export { AVATAR_SIZES,
|
|
5605
|
+
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 };
|