@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.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, debounce } from '@fastkit/helpers';
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 { DecorationSet, Decoration } from 'prosemirror-view';
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": "v-button-group"
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
- constructor(opts = {}) {
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
- iconName = icon(context);
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
- }, null);
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 ISSUE_MENU_CLASS_NAME = 'v-linter__issue-menu';
3205
- function renderIcon(view, issue) {
3206
- const { level = 'warning' } = issue;
3207
- const icon = document.createElement('div');
3208
- icon.className = `${ISSUE_ICON_CLASS_NAME} ${level}-scope`;
3209
- icon.title = issue.message;
3210
- icon.issue = issue;
3211
- return icon;
3212
- }
3213
- function renderMenu(view, issue) {
3214
- const menuWrapper = document.createElement('div');
3215
- menuWrapper.className = 'v-linter__issue-menu-wrapper';
3216
- const menu = document.createElement('div');
3217
- menu.className = `${ISSUE_MENU_CLASS_NAME} elevation-3`;
3218
- menu.innerText = issue.message;
3219
- const { fix, fixMessage } = issue;
3220
- if (fix && fixMessage) {
3221
- const $fix = document.createElement('button');
3222
- $fix.type = 'button';
3223
- $fix.innerHTML = fixMessage;
3224
- $fix.addEventListener('click', () => {
3225
- fix(view, issue);
3226
- });
3227
- $fix.className = `v-linter__issue-menu__fix`;
3228
- menu.appendChild($fix);
3229
- }
3230
- menu.issue = issue;
3231
- menuWrapper.appendChild(menu);
3232
- return menuWrapper;
3233
- }
3234
- function getIssueElement(source, className) {
3235
- if (!source || !(source instanceof HTMLElement)) {
3236
- return;
3237
- }
3238
- if (source.classList.contains(className)) {
3239
- return source;
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
- const el = source.closest(`.${className}`);
3242
- if (el) {
3243
- return el;
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
- function getIconElement(source) {
3247
- return getIssueElement(source, ISSUE_ICON_CLASS_NAME);
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 LinterPlugin {
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 BadWords(words) {
3397
- const regex = new RegExp(`\\b(${words.join('|')})\\b`);
3398
- return class BadWords extends LinterPlugin {
3399
- scan() {
3400
- this.doc.descendants((node, position) => {
3401
- if (!node.isText) {
3402
- return;
3403
- }
3404
- const matches = regex.exec(node.text);
3405
- if (matches) {
3406
- const fixValue = matches[0] + '!!!!!';
3407
- this.record({
3408
- level: 'warning',
3409
- message: `Try not to say '${matches[0]}'`,
3410
- from: position + matches.index,
3411
- to: position + matches.index + matches[0].length,
3412
- fix: () => {
3413
- console.log('hoge');
3414
- },
3415
- fixMessage: `「${fixValue}」に修正する。`,
3416
- });
3417
- }
3418
- });
3419
- return this;
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 HeadingLevel extends LinterPlugin {
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: this.fixHeader(lastHeadLevel + 1),
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 Punctuation extends LinterPlugin {
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: this.fix(`${matches[1]} `),
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
- const WysiwygTextColorTool = (vui) => {
3621
- const tool = {
3622
- key: 'textColor',
3623
- icon: vui.icon('editorTextColor'),
3624
- // icon: (gen) => vui.icon('editorTextColor'),
3625
- onClick: (ctx) => {
3626
- ctx.vui.alert('ok');
3627
- },
3628
- floating: true,
3629
- };
3630
- return tool;
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, BadWords, CHIP_SIZES, CONTROL_FIELD_VARIANTS, CONTROL_LOADING_SPINNER_SIZES, CONTROL_SIZES, HeadingLevel, Linter, LinterPlugin, PAGINATION_ALIGNS, Punctuation, 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, WysiwygEditorInitializeContext, WysiwygFormatBoldTool, WysiwygFormatItalicTool, WysiwygFormatUnderlineTool, WysiwygHistoryTool, WysiwygLinkTool, WysiwygOrderedListTool, WysiwygTextColorTool, configureDataTableDefaults, createAvatarProps, createCardProps, createChipProps, createControlFieldProviderProps, createControlProps, createElevationProps, createListTileProps, createNavigationItemProps, createPaperBaseProps, createPaperProps, createRequiredChipRenderer, createVFormProps, defineFormSelectorComponent, iconProps, installVuiPlugin, listTileEmits, mergeVuiPluginOptions, mergeVuiServiceIconSettings, mergeVuiServiceOptions, mergeVuiServiceUISettings, paginationProps, rawIconProp, renderNavigationItemInput, resolveNavigationItemInput, resolveRawIconProp, resolveRawWysiwygEditorTools, resolveRawWysiwygExtensions, splitAttrs, useControl, useControlField, useElevation, useVui, useVuiColorProvider, vueButtonProps };
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 };