@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.
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 +615 -44
  4. package/dist/vui.cjs.prod.js +615 -44
  5. package/dist/vui.css +221 -0
  6. package/dist/vui.d.ts +139 -4
  7. package/dist/vui.min.css +1 -1
  8. package/dist/vui.min.css.map +1 -1
  9. package/dist/vui.mjs +605 -44
  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 +6 -0
  15. package/src/components/VWysiwygEditor/VWysiwygEditor.tsx +97 -56
  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 +210 -0
  22. package/src/components/VWysiwygEditor/extensions/linter/LinterPlugin.ts +62 -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
@@ -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(),
@@ -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("div", {
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(vue.Fragment, null, [vue.createVNode(vue3.EditorContent, {
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
- 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
- };
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;