@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.
Files changed (36) hide show
  1. package/dist/tool/index.js +1 -0
  2. package/dist/tool/vite-plugin.d.ts.map +1 -1
  3. package/dist/vui.cjs.js +616 -39
  4. package/dist/vui.cjs.prod.js +616 -39
  5. package/dist/vui.css +219 -0
  6. package/dist/vui.d.ts +141 -4
  7. package/dist/vui.min.css +1 -1
  8. package/dist/vui.min.css.map +1 -1
  9. package/dist/vui.mjs +606 -39
  10. package/package.json +7 -6
  11. package/src/components/VButton/VButtonGroup.scss +4 -0
  12. package/src/components/VButton/VButtonGroup.tsx +12 -1
  13. package/src/components/VWysiwygEditor/.DS_Store +0 -0
  14. package/src/components/VWysiwygEditor/VWysiwygEditor.scss +4 -0
  15. package/src/components/VWysiwygEditor/VWysiwygEditor.tsx +65 -22
  16. package/src/components/VWysiwygEditor/extensions/.DS_Store +0 -0
  17. package/src/components/VWysiwygEditor/extensions/color.ts +72 -0
  18. package/src/components/VWysiwygEditor/extensions/index.ts +2 -0
  19. package/src/components/VWysiwygEditor/extensions/linter/.DS_Store +0 -0
  20. package/src/components/VWysiwygEditor/extensions/linter/Linter.scss +140 -0
  21. package/src/components/VWysiwygEditor/extensions/linter/Linter.tsx +214 -0
  22. package/src/components/VWysiwygEditor/extensions/linter/LinterPlugin.ts +65 -0
  23. package/src/components/VWysiwygEditor/extensions/linter/index.ts +5 -0
  24. package/src/components/VWysiwygEditor/extensions/linter/plugins/BadWords.tsx +50 -0
  25. package/src/components/VWysiwygEditor/extensions/linter/plugins/HeadingLevel.ts +39 -0
  26. package/src/components/VWysiwygEditor/extensions/linter/plugins/Punctuation.ts +49 -0
  27. package/src/components/VWysiwygEditor/extensions/linter/plugins/index.ts +3 -0
  28. package/src/components/VWysiwygEditor/extensions/linter/utils.ts +28 -0
  29. package/src/components/VWysiwygEditor/index.ts +1 -0
  30. package/src/components/VWysiwygEditor/schemes.ts +190 -4
  31. package/src/components/VWysiwygEditor/tools/color.scss +140 -0
  32. package/src/components/VWysiwygEditor/tools/color.tsx +90 -0
  33. package/src/components/VWysiwygEditor/tools/index.ts +1 -1
  34. package/src/service.tsx +5 -0
  35. package/src/tool/vite-plugin.ts +1 -0
  36. 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": "v-button-group"
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(raws, vui) {
2986
+ function resolveRawWysiwygEditorTools(rawTools, vui) {
2881
2987
  const tools = [];
2882
2988
  const extensions = [];
2883
- raws.forEach((raw) => {
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
- }), ...wysiwygSettings.value.extensions];
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
- const editor = vue3.useEditor({
2957
- onCreate: ({
2958
- editor
2959
- }) => {
2960
- textRef.value = editor.getText();
2961
- updateEditable();
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
- iconName = icon(context);
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
- }, null);
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(),
@@ -3127,6 +3266,376 @@ 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
+ icon
3302
+ } = issue;
3303
+ const message = resolveWysiwygLinterFixMessage(issue.message);
3304
+ decorations.push(prosemirrorView.Decoration.inline(issue.from, issue.to, {
3305
+ class: `${PROBLEM_CLASS_NAME} ${level}-scope`,
3306
+ 'data-issue-id': issue.id,
3307
+ title: typeof message === 'string' ? message : undefined
3308
+ }));
3309
+
3310
+ if (icon) {
3311
+ decorations.push(prosemirrorView.Decoration.widget(issue.from, view => renderIcon(view, issue)));
3312
+ }
3313
+ });
3314
+ const decorationSet = prosemirrorView.DecorationSet.create(doc, decorations);
3315
+ return {
3316
+ issues,
3317
+ decorationSet
3318
+ };
3319
+ }
3320
+
3321
+ const WysiwygLinter = createWysiwygExtension(ctx => {
3322
+ function showMenu(view, issue, event) {
3323
+ const message = resolveWysiwygLinterFixMessage(issue.message);
3324
+ const variant = ctx.vui.setting('containedVariant');
3325
+ const scope = ctx.vui.setting(`${issue.level}Scope`);
3326
+ return ctx.vui.menu({
3327
+ activator: event,
3328
+ content: stack => vue.createVNode("div", {
3329
+ "class": "v-linter__issue-menu__body"
3330
+ }, [vue.createVNode("div", {
3331
+ "class": ['v-linter__issue-menu__message', `${scope}-scope ${variant}`]
3332
+ }, [message]), issue.fix.length && vue.createVNode("div", {
3333
+ "class": "v-linter__issue-menu__fixers"
3334
+ }, [issue.fix.map((fixer, index) => vue.createVNode("div", {
3335
+ "key": index,
3336
+ "class": "v-linter__issue-menu__fixer"
3337
+ }, [vue.createVNode("button", {
3338
+ "type": "button",
3339
+ "class": "v-linter__issue-menu__fixer__button",
3340
+ "onClick": () => {
3341
+ stack.close();
3342
+ fixer.handler(view, issue);
3343
+ }
3344
+ }, [vue.createVNode("span", {
3345
+ "class": "v-linter__issue-menu__fixer__button__message"
3346
+ }, [resolveWysiwygLinterFixMessage(fixer.message)])])]))])])
3347
+ });
3348
+ }
3349
+
3350
+ return core.Extension.create({
3351
+ name: 'linter',
3352
+
3353
+ addOptions() {
3354
+ return {
3355
+ plugins: []
3356
+ };
3357
+ },
3358
+
3359
+ addStorage() {
3360
+ return {
3361
+ issues: []
3362
+ };
3363
+ },
3364
+
3365
+ addProseMirrorPlugins() {
3366
+ const {
3367
+ plugins
3368
+ } = this.options;
3369
+ const {
3370
+ storage
3371
+ } = this;
3372
+
3373
+ const runAll = doc => {
3374
+ const {
3375
+ issues,
3376
+ decorationSet
3377
+ } = runAllLinterPlugins(doc, plugins);
3378
+ storage.issues = issues;
3379
+ return decorationSet;
3380
+ };
3381
+
3382
+ const getIssue = id => storage.issues.find(issue => issue.id === id);
3383
+
3384
+ const getIssueElementByEvent = source => {
3385
+ if (!source || !(source instanceof HTMLElement)) {
3386
+ return;
3387
+ }
3388
+
3389
+ const issueId = source.getAttribute(ISSUE_DATASET_NAME);
3390
+ const issue = issueId && getIssue(issueId);
3391
+ if (!issue) return;
3392
+ return {
3393
+ issue
3394
+ };
3395
+ };
3396
+
3397
+ return [new prosemirrorState.Plugin({
3398
+ key: new prosemirrorState.PluginKey('linter'),
3399
+ state: {
3400
+ init(_, {
3401
+ doc
3402
+ }) {
3403
+ return runAll(doc);
3404
+ },
3405
+
3406
+ apply(transaction, oldState) {
3407
+ return transaction.docChanged ? runAll(transaction.doc) : oldState;
3408
+ }
3409
+
3410
+ },
3411
+ props: {
3412
+ decorations(state) {
3413
+ return this.getState(state);
3414
+ },
3415
+
3416
+ handleClick(view, _, event) {
3417
+ const info = getIssueElementByEvent(event.target);
3418
+
3419
+ if (!info) {
3420
+ return false;
3421
+ }
3422
+
3423
+ const {
3424
+ issue
3425
+ } = info;
3426
+ const {
3427
+ from,
3428
+ to
3429
+ } = issue;
3430
+
3431
+ const focus = () => view.dispatch(view.state.tr.setSelection(prosemirrorState.TextSelection.create(view.state.doc, from, to)).scrollIntoView());
3432
+
3433
+ focus();
3434
+ showMenu(view, issue, event);
3435
+ return true;
3436
+ }
3437
+
3438
+ }
3439
+ })];
3440
+ }
3441
+
3442
+ });
3443
+ });
3444
+
3445
+ let IDX = 256, BUFFER;
3446
+ const HEX = [];
3447
+ while (IDX--)
3448
+ HEX[IDX] = (IDX + 256).toString(16).substring(1);
3449
+ function cheepUUID() {
3450
+ let i = 0, num, out = '';
3451
+ if (!BUFFER || IDX + 16 > 256) {
3452
+ BUFFER = Array((i = 256));
3453
+ while (i--)
3454
+ BUFFER[i] = (256 * Math.random()) | 0;
3455
+ i = IDX = 0;
3456
+ }
3457
+ for (; i < 16; i++) {
3458
+ num = BUFFER[IDX + i];
3459
+ if (i == 6)
3460
+ out += HEX[(num & 15) | 64];
3461
+ else if (i == 8)
3462
+ out += HEX[(num & 63) | 128];
3463
+ else
3464
+ out += HEX[num];
3465
+ if (i & 1 && i > 1 && i < 11)
3466
+ out += '-';
3467
+ }
3468
+ IDX++;
3469
+ return out;
3470
+ }
3471
+
3472
+ class WysiwygLinterPlugin {
3473
+ doc;
3474
+ results = [];
3475
+ constructor(doc) {
3476
+ this.doc = doc;
3477
+ }
3478
+ record(result) {
3479
+ const { fix = [], icon = false } = result;
3480
+ this.results.push({
3481
+ level: 'error',
3482
+ id: cheepUUID(),
3483
+ ...result,
3484
+ fix: Array.isArray(fix) ? fix : [fix],
3485
+ icon,
3486
+ });
3487
+ }
3488
+ scan() {
3489
+ return this;
3490
+ }
3491
+ getResults() {
3492
+ return this.results;
3493
+ }
3494
+ }
3495
+
3496
+ function WysiwygLinterBadWords(words) {
3497
+ const regex = new RegExp(`\\b(${words.join('|')})\\b`);
3498
+ return class WysiwygLinterBadWords extends WysiwygLinterPlugin {
3499
+ scan() {
3500
+ this.doc.descendants((node, position) => {
3501
+ if (!node.isText) {
3502
+ return;
3503
+ }
3504
+
3505
+ const matches = regex.exec(node.text);
3506
+
3507
+ if (matches) {
3508
+ const fixValue = matches[0] + '!!!!!';
3509
+ this.record({
3510
+ level: 'warning',
3511
+ message: `Try not to say '${matches[0]}'`,
3512
+ from: position + matches.index,
3513
+ to: position + matches.index + matches[0].length,
3514
+ fix: [{
3515
+ message: () => vue.createVNode("span", null, [vue.createVNode("code", null, [fixValue]), vue.createTextVNode("\u306B\u4FEE\u6B63\u3059\u308B\u3002")]),
3516
+ handler: () => {
3517
+ console.log('hoge');
3518
+ }
3519
+ }, {
3520
+ message: 'どうにかする',
3521
+ handler: () => {
3522
+ console.log('hoge');
3523
+ }
3524
+ }]
3525
+ });
3526
+ }
3527
+ });
3528
+ return this;
3529
+ }
3530
+
3531
+ };
3532
+ }
3533
+
3534
+ class WysiwygLinterHeadingLevel extends WysiwygLinterPlugin {
3535
+ fixHeader(level) {
3536
+ return function ({ state, dispatch }, issue) {
3537
+ dispatch(state.tr.setNodeMarkup(issue.from - 1, undefined, { level }));
3538
+ };
3539
+ }
3540
+ scan() {
3541
+ let lastHeadLevel = null;
3542
+ this.doc.descendants((node, position) => {
3543
+ if (node.type.name === 'heading') {
3544
+ // Check whether heading levels fit under the current level
3545
+ const { level } = node.attrs;
3546
+ if (lastHeadLevel != null && level > lastHeadLevel + 1) {
3547
+ this.record({
3548
+ message: `Heading too small (${level} under ${lastHeadLevel})`,
3549
+ from: position + 1,
3550
+ to: position + 1 + node.content.size,
3551
+ fix: {
3552
+ message: '修正する',
3553
+ handler: this.fixHeader(lastHeadLevel + 1),
3554
+ },
3555
+ });
3556
+ }
3557
+ lastHeadLevel = level;
3558
+ }
3559
+ });
3560
+ return this;
3561
+ }
3562
+ }
3563
+
3564
+ class WysiwygLinterPunctuation extends WysiwygLinterPlugin {
3565
+ regex = / ([,.!?:]) ?/g;
3566
+ fix(replacement) {
3567
+ return function ({ state, dispatch }, issue) {
3568
+ dispatch(state.tr.replaceWith(issue.from, issue.to, state.schema.text(replacement)));
3569
+ };
3570
+ }
3571
+ scan() {
3572
+ this.doc.descendants((node, position) => {
3573
+ if (!node.isText) {
3574
+ return;
3575
+ }
3576
+ if (!node.text) {
3577
+ return;
3578
+ }
3579
+ const matches = this.regex.exec(node.text);
3580
+ if (matches) {
3581
+ this.record({
3582
+ message: 'Suspicious spacing around punctuation',
3583
+ from: position + matches.index,
3584
+ to: position + matches.index + matches[0].length,
3585
+ fix: {
3586
+ message: 'Fix it!!!',
3587
+ handler: this.fix(`${matches[1]} `),
3588
+ },
3589
+ });
3590
+ }
3591
+ });
3592
+ return this;
3593
+ }
3594
+ }
3595
+
3596
+ const WysiwygColorExtension = core.Extension.create({
3597
+ name: 'color',
3598
+ addOptions() {
3599
+ return {
3600
+ types: ['textStyle'],
3601
+ };
3602
+ },
3603
+ addGlobalAttributes() {
3604
+ return [
3605
+ {
3606
+ types: this.options.types,
3607
+ attributes: {
3608
+ color: {
3609
+ default: null,
3610
+ parseHTML: (element) => element.style.color.replace(/['"]+/g, ''),
3611
+ renderHTML: (attributes) => {
3612
+ if (!attributes.color) {
3613
+ return {};
3614
+ }
3615
+ return {
3616
+ style: `color: ${attributes.color}`,
3617
+ };
3618
+ },
3619
+ },
3620
+ },
3621
+ },
3622
+ ];
3623
+ },
3624
+ addCommands() {
3625
+ return {
3626
+ setColor: (color) => ({ chain }) => {
3627
+ return chain().setMark('textStyle', { color }).run();
3628
+ },
3629
+ unsetColor: () => ({ chain }) => {
3630
+ return chain()
3631
+ .setMark('textStyle', { color: null })
3632
+ .removeEmptyTextStyle()
3633
+ .run();
3634
+ },
3635
+ };
3636
+ },
3637
+ });
3638
+
3130
3639
  const WysiwygBulletListTool = (vui, options) => {
3131
3640
  const tool = {
3132
3641
  key: 'bulletList',
@@ -3267,18 +3776,73 @@ const WysiwygOrderedListTool = (vui, options) => {
3267
3776
  return tool;
3268
3777
  };
3269
3778
 
3270
- const WysiwygTextColorTool = (vui) => {
3271
- const tool = {
3272
- key: 'textColor',
3273
- icon: vui.icon('editorTextColor'),
3274
- // icon: (gen) => vui.icon('editorTextColor'),
3275
- onClick: (ctx) => {
3276
- ctx.vui.alert('ok');
3277
- },
3278
- floating: true,
3279
- };
3280
- return tool;
3281
- };
3779
+ function createWysiwygColorTool(opts) {
3780
+ const WysiwygColorTool = vui => {
3781
+ const tool = {
3782
+ key: 'textColor',
3783
+ // active: ({ editor }) => editor.isActive('textStyle'),
3784
+ icon: ({
3785
+ editor
3786
+ }) => () => {
3787
+ const color = editor.getAttributes('textStyle').color;
3788
+ const style = {
3789
+ color
3790
+ };
3791
+ return vue.createVNode("span", {
3792
+ "class": "v-wysiwyg-color-tool__button"
3793
+ }, [vue.createVNode(VIcon, {
3794
+ "class": "v-wysiwyg-color-tool__button__icon",
3795
+ "name": vui.icon('editorTextColor')
3796
+ }, null), vue.createVNode("span", {
3797
+ "class": "v-wysiwyg-color-tool__button__bar",
3798
+ "style": style
3799
+ }, null)]);
3800
+ },
3801
+ onClick: (ctx, ev) => {
3802
+ ctx.vui.menu({
3803
+ class: 'v-wysiwyg-color-tool__menu',
3804
+ activator: ev,
3805
+ content: stack => vue.createVNode("div", {
3806
+ "class": ['v-wysiwyg-color-tool__items', {
3807
+ 'v-wysiwyg-color-tool__items--with-label': opts.withLabel
3808
+ }]
3809
+ }, [opts.items.map((item, index) => vue.createVNode("button", {
3810
+ "key": item.key == null ? index : item.key,
3811
+ "class": "v-wysiwyg-color-tool__item",
3812
+ "type": "button",
3813
+ "onClick": () => {
3814
+ const {
3815
+ color
3816
+ } = item;
3817
+ let command = ctx.editor.chain().focus();
3818
+
3819
+ if (color) {
3820
+ command = command.setColor(color);
3821
+ } else {
3822
+ command = command.unsetColor();
3823
+ }
3824
+
3825
+ command.run();
3826
+ stack.close();
3827
+ }
3828
+ }, [vue.createVNode("span", {
3829
+ "class": "v-wysiwyg-color-tool__item__color",
3830
+ "style": item.color ? {
3831
+ color: item.color
3832
+ } : {}
3833
+ }, null), opts.withLabel && vue.createVNode("span", {
3834
+ "class": "v-wysiwyg-color-tool__item__name"
3835
+ }, [item.name])]))])
3836
+ });
3837
+ },
3838
+ floating: true,
3839
+ extensions: [TextStyle__default, WysiwygColorExtension]
3840
+ };
3841
+ return tool;
3842
+ };
3843
+
3844
+ return WysiwygColorTool;
3845
+ }
3282
3846
 
3283
3847
  const {
3284
3848
  props,
@@ -4895,6 +5459,10 @@ class VuiService {
4895
5459
  return this.stack.snackbar(...args);
4896
5460
  }
4897
5461
 
5462
+ menu(...args) {
5463
+ return this.stack.menu(...args);
5464
+ }
5465
+
4898
5466
  formPrompt(settings, slot) {
4899
5467
  let form;
4900
5468
  const options = {
@@ -5119,13 +5687,19 @@ exports.VuiInjectionKey = VuiInjectionKey;
5119
5687
  exports.VuiPlugin = VuiPlugin;
5120
5688
  exports.VuiService = VuiService;
5121
5689
  exports.WysiwygBulletListTool = WysiwygBulletListTool;
5690
+ exports.WysiwygColorExtension = WysiwygColorExtension;
5691
+ exports.WysiwygEditorInitializeContext = WysiwygEditorInitializeContext;
5122
5692
  exports.WysiwygFormatBoldTool = WysiwygFormatBoldTool;
5123
5693
  exports.WysiwygFormatItalicTool = WysiwygFormatItalicTool;
5124
5694
  exports.WysiwygFormatUnderlineTool = WysiwygFormatUnderlineTool;
5125
5695
  exports.WysiwygHistoryTool = WysiwygHistoryTool;
5126
5696
  exports.WysiwygLinkTool = WysiwygLinkTool;
5697
+ exports.WysiwygLinter = WysiwygLinter;
5698
+ exports.WysiwygLinterBadWords = WysiwygLinterBadWords;
5699
+ exports.WysiwygLinterHeadingLevel = WysiwygLinterHeadingLevel;
5700
+ exports.WysiwygLinterPlugin = WysiwygLinterPlugin;
5701
+ exports.WysiwygLinterPunctuation = WysiwygLinterPunctuation;
5127
5702
  exports.WysiwygOrderedListTool = WysiwygOrderedListTool;
5128
- exports.WysiwygTextColorTool = WysiwygTextColorTool;
5129
5703
  exports.configureDataTableDefaults = configureDataTableDefaults;
5130
5704
  exports.createAvatarProps = createAvatarProps;
5131
5705
  exports.createCardProps = createCardProps;
@@ -5139,6 +5713,8 @@ exports.createPaperBaseProps = createPaperBaseProps;
5139
5713
  exports.createPaperProps = createPaperProps;
5140
5714
  exports.createRequiredChipRenderer = createRequiredChipRenderer;
5141
5715
  exports.createVFormProps = createVFormProps;
5716
+ exports.createWysiwygColorTool = createWysiwygColorTool;
5717
+ exports.createWysiwygExtension = createWysiwygExtension;
5142
5718
  exports.defineFormSelectorComponent = defineFormSelectorComponent;
5143
5719
  exports.iconProps = iconProps;
5144
5720
  exports.installVuiPlugin = installVuiPlugin;
@@ -5153,6 +5729,7 @@ exports.renderNavigationItemInput = renderNavigationItemInput;
5153
5729
  exports.resolveNavigationItemInput = resolveNavigationItemInput;
5154
5730
  exports.resolveRawIconProp = resolveRawIconProp;
5155
5731
  exports.resolveRawWysiwygEditorTools = resolveRawWysiwygEditorTools;
5732
+ exports.resolveRawWysiwygExtensions = resolveRawWysiwygExtensions;
5156
5733
  exports.splitAttrs = splitAttrs;
5157
5734
  exports.useControl = useControl;
5158
5735
  exports.useControlField = useControlField;