@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.
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 +437 -228
  4. package/dist/vui.cjs.prod.js +437 -228
  5. package/dist/vui.css +166 -12
  6. package/dist/vui.d.ts +101 -54
  7. package/dist/vui.min.css +1 -1
  8. package/dist/vui.min.css.map +1 -1
  9. package/dist/vui.mjs +431 -225
  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/VWysiwygEditor.scss +4 -0
  14. package/src/components/VWysiwygEditor/VWysiwygEditor.tsx +19 -9
  15. package/src/components/VWysiwygEditor/extensions/.DS_Store +0 -0
  16. package/src/components/VWysiwygEditor/extensions/color.ts +72 -0
  17. package/src/components/VWysiwygEditor/extensions/index.ts +1 -0
  18. package/src/components/VWysiwygEditor/extensions/linter/.DS_Store +0 -0
  19. package/src/components/VWysiwygEditor/extensions/linter/Linter.scss +80 -15
  20. package/src/components/VWysiwygEditor/extensions/linter/Linter.tsx +210 -0
  21. package/src/components/VWysiwygEditor/extensions/linter/LinterPlugin.ts +29 -11
  22. package/src/components/VWysiwygEditor/extensions/linter/index.ts +3 -3
  23. package/src/components/VWysiwygEditor/extensions/linter/plugins/BadWords.tsx +50 -0
  24. package/src/components/VWysiwygEditor/extensions/linter/plugins/HeadingLevel.ts +9 -3
  25. package/src/components/VWysiwygEditor/extensions/linter/plugins/Punctuation.ts +9 -3
  26. package/src/components/VWysiwygEditor/extensions/linter/plugins/index.ts +3 -3
  27. package/src/components/VWysiwygEditor/extensions/linter/utils.ts +28 -0
  28. package/src/components/VWysiwygEditor/schemes.ts +87 -5
  29. package/src/components/VWysiwygEditor/tools/color.scss +140 -0
  30. package/src/components/VWysiwygEditor/tools/color.tsx +90 -0
  31. package/src/components/VWysiwygEditor/tools/index.ts +1 -1
  32. package/src/service.tsx +5 -0
  33. package/src/tool/vite-plugin.ts +1 -0
  34. package/src/components/VWysiwygEditor/extensions/linter/Linter.ts +0 -262
  35. package/src/components/VWysiwygEditor/extensions/linter/plugins/BadWords.ts +0 -34
  36. package/src/components/VWysiwygEditor/tools/text-color.ts +0 -14
package/dist/vui.cjs.js CHANGED
@@ -26,10 +26,12 @@ var extensionHistory = require('@tiptap/extension-history');
26
26
  var extensionLink = require('@tiptap/extension-link');
27
27
  var rules = require('@fastkit/rules');
28
28
  var extensionOrderedList = require('@tiptap/extension-ordered-list');
29
+ var TextStyle = require('@tiptap/extension-text-style');
29
30
 
30
31
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e["default"] : e; }
31
32
 
32
33
  var StarterKit__default = /*#__PURE__*/_interopDefaultLegacy(StarterKit);
34
+ var TextStyle__default = /*#__PURE__*/_interopDefaultLegacy(TextStyle);
33
35
 
34
36
  const CONTROL_SIZES = ['sm', 'md', 'lg'];
35
37
  const CONTROL_FIELD_VARIANTS = ['outlined', 'filled', 'flat'];
@@ -1232,6 +1234,7 @@ const VButtonGroup = vue.defineComponent({
1232
1234
 
1233
1235
  setup(props, ctx) {
1234
1236
  return () => {
1237
+ let hasIcon = false;
1235
1238
  let buttonLength = 0;
1236
1239
  let tmp = vueUtils.renderSlotOrEmpty(ctx.slots) || [];
1237
1240
 
@@ -1248,6 +1251,11 @@ const VButtonGroup = vue.defineComponent({
1248
1251
  }
1249
1252
 
1250
1253
  buttonLength++;
1254
+
1255
+ if (!hasIcon && node.props && !!node.props.icon) {
1256
+ hasIcon = true;
1257
+ }
1258
+
1251
1259
  return {
1252
1260
  isButton: true,
1253
1261
  node
@@ -1264,6 +1272,7 @@ const VButtonGroup = vue.defineComponent({
1264
1272
  ...childProps,
1265
1273
  rouded: false,
1266
1274
  class: ['v-button-group__item', {
1275
+ 'v-button--icon': hasIcon,
1267
1276
  'v-button-group__item--has-left': hasLeft,
1268
1277
  'v-button-group__item--has-right': hasRight
1269
1278
  }]
@@ -1273,7 +1282,9 @@ const VButtonGroup = vue.defineComponent({
1273
1282
  return child.node;
1274
1283
  });
1275
1284
  return vue.createVNode("div", {
1276
- "class": "v-button-group"
1285
+ "class": ['v-button-group', {
1286
+ 'v-button-group--has-icon': hasIcon
1287
+ }]
1277
1288
  }, [$children]);
1278
1289
  };
1279
1290
  }
@@ -2892,7 +2903,12 @@ const prefixedEventName = (source) => {
2892
2903
  };
2893
2904
  class WysiwygEditorInitializeContext {
2894
2905
  listeners = {};
2895
- constructor(opts = {}) {
2906
+ _vui;
2907
+ get vui() {
2908
+ return this._vui();
2909
+ }
2910
+ constructor(vuiGetter, opts = {}) {
2911
+ this._vui = vuiGetter;
2896
2912
  EDITOR_EVENTS.forEach((event) => {
2897
2913
  this.listeners[event] = [];
2898
2914
  const prefixed = prefixedEventName(event);
@@ -2921,7 +2937,44 @@ class WysiwygEditorInitializeContext {
2921
2937
  return opts;
2922
2938
  }
2923
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
+ }
2924
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
+ }
2925
2978
  return typeof raw === 'function' ? raw(ctx) : raw;
2926
2979
  }
2927
2980
  function resolveRawWysiwygExtensions(raws, ctx) {
@@ -2990,7 +3043,7 @@ const VWysiwygEditor = vue.defineComponent({
2990
3043
  nodeType: VUI_WYSIWYG_EDITOR_SYMBOL,
2991
3044
  validationValue: () => textRef.value
2992
3045
  });
2993
- const initializeCtx = new WysiwygEditorInitializeContext({
3046
+ const initializeCtx = new WysiwygEditorInitializeContext(() => vui, {
2994
3047
  onCreate: ({
2995
3048
  editor
2996
3049
  }) => {
@@ -3089,9 +3142,9 @@ const VWysiwygEditor = vue.defineComponent({
3089
3142
  const _editor = editor.value;
3090
3143
  const variant = vui.setting(bubbleMenu ? 'containedVariant' : 'plainVariant');
3091
3144
  return vue.createVNode(VButtonGroup, {
3092
- "class": {
3145
+ "class": ['v-wysiwyg-editor__toolbar', {
3093
3146
  'v-wysiwyg-editor__floating-toolbar': props.floatingToolbar
3094
- },
3147
+ }],
3095
3148
  "variant": variant
3096
3149
  }, _isSlot$7(_slot = tools.map(({
3097
3150
  key,
@@ -3103,16 +3156,23 @@ const VWysiwygEditor = vue.defineComponent({
3103
3156
  const isActive = !!_editor && resolveContextableValue(context, active);
3104
3157
  const isDisabled = !inputControl.canOperation || !!_editor && resolveContextableValue(context, disabled);
3105
3158
  let iconName;
3159
+ let child;
3106
3160
 
3107
3161
  if (typeof icon === 'function') {
3108
3162
  if (_editor) {
3109
- iconName = icon(context);
3163
+ const _child = icon(context);
3164
+
3165
+ if (typeof _child === 'function') {
3166
+ child = _child();
3167
+ } else {
3168
+ iconName = child;
3169
+ }
3110
3170
  }
3111
3171
  } else {
3112
3172
  iconName = icon;
3113
3173
  }
3114
3174
 
3115
- if (!iconName) return;
3175
+ if (!iconName && child == null) return;
3116
3176
  return vue.createVNode(VButton, {
3117
3177
  "tabindex": -1,
3118
3178
  "key": key,
@@ -3120,7 +3180,9 @@ const VWysiwygEditor = vue.defineComponent({
3120
3180
  "onClick": ev => onClick(context, ev),
3121
3181
  "color": isActive ? toolButtonActiveColor : undefined,
3122
3182
  "disabled": isDisabled
3123
- }, null);
3183
+ }, _isSlot$7(child) ? child : {
3184
+ default: () => [child]
3185
+ });
3124
3186
  })) ? _slot : {
3125
3187
  default: () => [_slot]
3126
3188
  });
@@ -3204,190 +3266,217 @@ function resolveContextableValue(ctx, source) {
3204
3266
  return typeof source === 'function' ? source(ctx) : source;
3205
3267
  }
3206
3268
 
3207
- const PROBLEM_CLASS_NAME = 'v-linter__problem';
3208
- const ISSUE_ICON_CLASS_NAME = 'v-linter__issue-icon';
3209
- const ISSUE_MENU_CLASS_NAME = 'v-linter__issue-menu';
3210
- function renderIcon(view, issue) {
3211
- const { level = 'warning' } = issue;
3212
- const icon = document.createElement('div');
3213
- icon.className = `${ISSUE_ICON_CLASS_NAME} ${level}-scope`;
3214
- icon.title = issue.message;
3215
- icon.issue = issue;
3216
- return icon;
3217
- }
3218
- function renderMenu(view, issue) {
3219
- const menuWrapper = document.createElement('div');
3220
- menuWrapper.className = 'v-linter__issue-menu-wrapper';
3221
- const menu = document.createElement('div');
3222
- menu.className = `${ISSUE_MENU_CLASS_NAME} elevation-3`;
3223
- menu.innerText = issue.message;
3224
- const { fix, fixMessage } = issue;
3225
- if (fix && fixMessage) {
3226
- const $fix = document.createElement('button');
3227
- $fix.type = 'button';
3228
- $fix.innerHTML = fixMessage;
3229
- $fix.addEventListener('click', () => {
3230
- fix(view, issue);
3231
- });
3232
- $fix.className = `v-linter__issue-menu__fix`;
3233
- menu.appendChild($fix);
3234
- }
3235
- menu.issue = issue;
3236
- menuWrapper.appendChild(menu);
3237
- return menuWrapper;
3238
- }
3239
- function getIssueElement(source, className) {
3240
- if (!source || !(source instanceof HTMLElement)) {
3241
- return;
3242
- }
3243
- if (source.classList.contains(className)) {
3244
- return source;
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;
3245
3451
  }
3246
- const el = source.closest(`.${className}`);
3247
- if (el) {
3248
- return el;
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 += '-';
3249
3462
  }
3250
- }
3251
- function getIconElement(source) {
3252
- return getIssueElement(source, ISSUE_ICON_CLASS_NAME);
3253
- }
3254
- function getMenuElement(source) {
3255
- return getIssueElement(source, ISSUE_MENU_CLASS_NAME);
3256
- }
3257
- function getProblemElement(source) {
3258
- return getIssueElement(source, PROBLEM_CLASS_NAME);
3259
- }
3260
- function runAllLinterPlugins(doc, plugins) {
3261
- const decorations = [];
3262
- const results = plugins
3263
- .map((RegisteredLinterPlugin) => {
3264
- return new RegisteredLinterPlugin(doc).scan().getResults();
3265
- })
3266
- .flat();
3267
- results.forEach((issue) => {
3268
- const { level = 'warning' } = issue;
3269
- decorations.push(prosemirrorView.Decoration.inline(issue.from, issue.to, {
3270
- class: `${PROBLEM_CLASS_NAME} ${level}-scope`,
3271
- 'data-issue': JSON.stringify(issue),
3272
- title: issue.message,
3273
- }), prosemirrorView.Decoration.widget(issue.from, (view) => renderIcon(view, issue)), prosemirrorView.Decoration.widget(issue.from, (view) => renderMenu(view, issue)));
3274
- });
3275
- return prosemirrorView.DecorationSet.create(doc, decorations);
3276
- }
3277
- function updateMenusPosition(editorElement) {
3278
- const { left, right } = editorElement.getBoundingClientRect();
3279
- const menus = Array.from(editorElement.querySelectorAll(`.${ISSUE_MENU_CLASS_NAME}`));
3280
- menus.forEach((menu) => {
3281
- const { left: menuLeft, right: menuRight } = menu.getBoundingClientRect();
3282
- const overflow = menuRight - right;
3283
- let offset = 0;
3284
- if (overflow > 0) {
3285
- offset = -overflow;
3286
- if (menuLeft + offset < left) {
3287
- offset = 0;
3288
- }
3289
- }
3290
- menu.style.transform = `translateX(${offset}px)`;
3291
- });
3292
- }
3293
- const debouncedUpdateMenusPosition = helpers.debounce(updateMenusPosition, 250);
3294
- const Linter = core.Extension.create({
3295
- name: 'linter',
3296
- addOptions() {
3297
- return {
3298
- plugins: [],
3299
- };
3300
- },
3301
- onCreate() {
3302
- const { dom } = this.editor.view;
3303
- debouncedUpdateMenusPosition(dom);
3304
- },
3305
- onUpdate() {
3306
- const { dom } = this.editor.view;
3307
- debouncedUpdateMenusPosition(dom);
3308
- },
3309
- addProseMirrorPlugins() {
3310
- const { plugins } = this.options;
3311
- return [
3312
- new prosemirrorState.Plugin({
3313
- key: new prosemirrorState.PluginKey('linter'),
3314
- state: {
3315
- init(_, { doc }) {
3316
- return runAllLinterPlugins(doc, plugins);
3317
- },
3318
- apply(transaction, oldState) {
3319
- return transaction.docChanged
3320
- ? runAllLinterPlugins(transaction.doc, plugins)
3321
- : oldState;
3322
- },
3323
- },
3324
- props: {
3325
- decorations(state) {
3326
- return this.getState(state);
3327
- },
3328
- handleClick(view, _, event) {
3329
- const activeMenus = Array.from(view.dom.querySelectorAll(`.${ISSUE_MENU_CLASS_NAME}--active`));
3330
- activeMenus.forEach((menu) => menu.classList.remove(`${ISSUE_MENU_CLASS_NAME}--active`));
3331
- const problem = getProblemElement(event.target);
3332
- const issueString = problem && problem.dataset['issue'];
3333
- const issue = issueString && JSON.parse(issueString);
3334
- if (issue) {
3335
- const menuWrapper = problem.previousElementSibling;
3336
- const menu = menuWrapper &&
3337
- menuWrapper.querySelector(`.${ISSUE_MENU_CLASS_NAME}`);
3338
- menu && menu.classList.add(`${ISSUE_MENU_CLASS_NAME}--active`);
3339
- const { from, to } = issue;
3340
- view.dispatch(view.state.tr
3341
- .setSelection(prosemirrorState.TextSelection.create(view.state.doc, from, to))
3342
- .scrollIntoView());
3343
- return true;
3344
- }
3345
- const menu = getMenuElement(event.target);
3346
- if (menu) {
3347
- menu.classList.add(`${ISSUE_MENU_CLASS_NAME}--active`);
3348
- }
3349
- const target = getIconElement(event.target);
3350
- if (target && target.issue) {
3351
- const menuWrapper = target.nextElementSibling;
3352
- const menu = menuWrapper &&
3353
- menuWrapper.querySelector(`.${ISSUE_MENU_CLASS_NAME}`);
3354
- menu && menu.classList.add(`${ISSUE_MENU_CLASS_NAME}--active`);
3355
- const { from, to } = target.issue;
3356
- view.dispatch(view.state.tr
3357
- .setSelection(prosemirrorState.TextSelection.create(view.state.doc, from, to))
3358
- .scrollIntoView());
3359
- return true;
3360
- }
3361
- return false;
3362
- },
3363
- handleDoubleClick(view, _, event) {
3364
- const target = getIconElement(event.target);
3365
- if (target && target.issue) {
3366
- const prob = target.issue;
3367
- if (prob.fix) {
3368
- prob.fix(view, prob);
3369
- view.focus();
3370
- return true;
3371
- }
3372
- }
3373
- return false;
3374
- },
3375
- },
3376
- }),
3377
- ];
3378
- },
3379
- });
3463
+ IDX++;
3464
+ return out;
3465
+ }
3380
3466
 
3381
- class LinterPlugin {
3467
+ class WysiwygLinterPlugin {
3382
3468
  doc;
3383
3469
  results = [];
3384
3470
  constructor(doc) {
3385
3471
  this.doc = doc;
3386
3472
  }
3387
3473
  record(result) {
3474
+ const { fix = [] } = result;
3388
3475
  this.results.push({
3389
3476
  level: 'error',
3477
+ id: cheepUUID(),
3390
3478
  ...result,
3479
+ fix: Array.isArray(fix) ? fix : [fix],
3391
3480
  });
3392
3481
  }
3393
3482
  scan() {
@@ -3398,35 +3487,45 @@ class LinterPlugin {
3398
3487
  }
3399
3488
  }
3400
3489
 
3401
- function BadWords(words) {
3402
- const regex = new RegExp(`\\b(${words.join('|')})\\b`);
3403
- return class BadWords extends LinterPlugin {
3404
- scan() {
3405
- this.doc.descendants((node, position) => {
3406
- if (!node.isText) {
3407
- return;
3408
- }
3409
- const matches = regex.exec(node.text);
3410
- if (matches) {
3411
- const fixValue = matches[0] + '!!!!!';
3412
- this.record({
3413
- level: 'warning',
3414
- message: `Try not to say '${matches[0]}'`,
3415
- from: position + matches.index,
3416
- to: position + matches.index + matches[0].length,
3417
- fix: () => {
3418
- console.log('hoge');
3419
- },
3420
- fixMessage: `「${fixValue}」に修正する。`,
3421
- });
3422
- }
3423
- });
3424
- return this;
3425
- }
3426
- };
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
+ };
3427
3526
  }
3428
3527
 
3429
- class HeadingLevel extends LinterPlugin {
3528
+ class WysiwygLinterHeadingLevel extends WysiwygLinterPlugin {
3430
3529
  fixHeader(level) {
3431
3530
  return function ({ state, dispatch }, issue) {
3432
3531
  dispatch(state.tr.setNodeMarkup(issue.from - 1, undefined, { level }));
@@ -3443,7 +3542,10 @@ class HeadingLevel extends LinterPlugin {
3443
3542
  message: `Heading too small (${level} under ${lastHeadLevel})`,
3444
3543
  from: position + 1,
3445
3544
  to: position + 1 + node.content.size,
3446
- fix: this.fixHeader(lastHeadLevel + 1),
3545
+ fix: {
3546
+ message: '修正する',
3547
+ handler: this.fixHeader(lastHeadLevel + 1),
3548
+ },
3447
3549
  });
3448
3550
  }
3449
3551
  lastHeadLevel = level;
@@ -3453,7 +3555,7 @@ class HeadingLevel extends LinterPlugin {
3453
3555
  }
3454
3556
  }
3455
3557
 
3456
- class Punctuation extends LinterPlugin {
3558
+ class WysiwygLinterPunctuation extends WysiwygLinterPlugin {
3457
3559
  regex = / ([,.!?:]) ?/g;
3458
3560
  fix(replacement) {
3459
3561
  return function ({ state, dispatch }, issue) {
@@ -3474,7 +3576,10 @@ class Punctuation extends LinterPlugin {
3474
3576
  message: 'Suspicious spacing around punctuation',
3475
3577
  from: position + matches.index,
3476
3578
  to: position + matches.index + matches[0].length,
3477
- fix: this.fix(`${matches[1]} `),
3579
+ fix: {
3580
+ message: 'Fix it!!!',
3581
+ handler: this.fix(`${matches[1]} `),
3582
+ },
3478
3583
  });
3479
3584
  }
3480
3585
  });
@@ -3482,6 +3587,49 @@ class Punctuation extends LinterPlugin {
3482
3587
  }
3483
3588
  }
3484
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
+
3485
3633
  const WysiwygBulletListTool = (vui, options) => {
3486
3634
  const tool = {
3487
3635
  key: 'bulletList',
@@ -3622,18 +3770,73 @@ const WysiwygOrderedListTool = (vui, options) => {
3622
3770
  return tool;
3623
3771
  };
3624
3772
 
3625
- const WysiwygTextColorTool = (vui) => {
3626
- const tool = {
3627
- key: 'textColor',
3628
- icon: vui.icon('editorTextColor'),
3629
- // icon: (gen) => vui.icon('editorTextColor'),
3630
- onClick: (ctx) => {
3631
- ctx.vui.alert('ok');
3632
- },
3633
- floating: true,
3634
- };
3635
- return tool;
3636
- };
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
+ }
3637
3840
 
3638
3841
  const {
3639
3842
  props,
@@ -5250,6 +5453,10 @@ class VuiService {
5250
5453
  return this.stack.snackbar(...args);
5251
5454
  }
5252
5455
 
5456
+ menu(...args) {
5457
+ return this.stack.menu(...args);
5458
+ }
5459
+
5253
5460
  formPrompt(settings, slot) {
5254
5461
  let form;
5255
5462
  const options = {
@@ -5407,16 +5614,11 @@ exports.VSnackbar = vueKit.VSnackbar;
5407
5614
  exports.VTooltip = vueKit.VTooltip;
5408
5615
  exports.ICON_NAMES = iconFont.ICON_NAMES;
5409
5616
  exports.AVATAR_SIZES = AVATAR_SIZES;
5410
- exports.BadWords = BadWords;
5411
5617
  exports.CHIP_SIZES = CHIP_SIZES;
5412
5618
  exports.CONTROL_FIELD_VARIANTS = CONTROL_FIELD_VARIANTS;
5413
5619
  exports.CONTROL_LOADING_SPINNER_SIZES = CONTROL_LOADING_SPINNER_SIZES;
5414
5620
  exports.CONTROL_SIZES = CONTROL_SIZES;
5415
- exports.HeadingLevel = HeadingLevel;
5416
- exports.Linter = Linter;
5417
- exports.LinterPlugin = LinterPlugin;
5418
5621
  exports.PAGINATION_ALIGNS = PAGINATION_ALIGNS;
5419
- exports.Punctuation = Punctuation;
5420
5622
  exports.VApp = VApp;
5421
5623
  exports.VAvatar = VAvatar;
5422
5624
  exports.VBreadcrumbs = VBreadcrumbs;
@@ -5479,14 +5681,19 @@ exports.VuiInjectionKey = VuiInjectionKey;
5479
5681
  exports.VuiPlugin = VuiPlugin;
5480
5682
  exports.VuiService = VuiService;
5481
5683
  exports.WysiwygBulletListTool = WysiwygBulletListTool;
5684
+ exports.WysiwygColorExtension = WysiwygColorExtension;
5482
5685
  exports.WysiwygEditorInitializeContext = WysiwygEditorInitializeContext;
5483
5686
  exports.WysiwygFormatBoldTool = WysiwygFormatBoldTool;
5484
5687
  exports.WysiwygFormatItalicTool = WysiwygFormatItalicTool;
5485
5688
  exports.WysiwygFormatUnderlineTool = WysiwygFormatUnderlineTool;
5486
5689
  exports.WysiwygHistoryTool = WysiwygHistoryTool;
5487
5690
  exports.WysiwygLinkTool = WysiwygLinkTool;
5691
+ exports.WysiwygLinter = WysiwygLinter;
5692
+ exports.WysiwygLinterBadWords = WysiwygLinterBadWords;
5693
+ exports.WysiwygLinterHeadingLevel = WysiwygLinterHeadingLevel;
5694
+ exports.WysiwygLinterPlugin = WysiwygLinterPlugin;
5695
+ exports.WysiwygLinterPunctuation = WysiwygLinterPunctuation;
5488
5696
  exports.WysiwygOrderedListTool = WysiwygOrderedListTool;
5489
- exports.WysiwygTextColorTool = WysiwygTextColorTool;
5490
5697
  exports.configureDataTableDefaults = configureDataTableDefaults;
5491
5698
  exports.createAvatarProps = createAvatarProps;
5492
5699
  exports.createCardProps = createCardProps;
@@ -5500,6 +5707,8 @@ exports.createPaperBaseProps = createPaperBaseProps;
5500
5707
  exports.createPaperProps = createPaperProps;
5501
5708
  exports.createRequiredChipRenderer = createRequiredChipRenderer;
5502
5709
  exports.createVFormProps = createVFormProps;
5710
+ exports.createWysiwygColorTool = createWysiwygColorTool;
5711
+ exports.createWysiwygExtension = createWysiwygExtension;
5503
5712
  exports.defineFormSelectorComponent = defineFormSelectorComponent;
5504
5713
  exports.iconProps = iconProps;
5505
5714
  exports.installVuiPlugin = installVuiPlugin;