@fastkit/vui 0.7.59 → 0.7.65

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/tool/index.js +1 -0
  2. package/dist/tool/vite-plugin.d.ts.map +1 -1
  3. package/dist/vui.cjs.js +616 -39
  4. package/dist/vui.cjs.prod.js +616 -39
  5. package/dist/vui.css +219 -0
  6. package/dist/vui.d.ts +141 -4
  7. package/dist/vui.min.css +1 -1
  8. package/dist/vui.min.css.map +1 -1
  9. package/dist/vui.mjs +606 -39
  10. package/package.json +7 -6
  11. package/src/components/VButton/VButtonGroup.scss +4 -0
  12. package/src/components/VButton/VButtonGroup.tsx +12 -1
  13. package/src/components/VWysiwygEditor/.DS_Store +0 -0
  14. package/src/components/VWysiwygEditor/VWysiwygEditor.scss +4 -0
  15. package/src/components/VWysiwygEditor/VWysiwygEditor.tsx +65 -22
  16. package/src/components/VWysiwygEditor/extensions/.DS_Store +0 -0
  17. package/src/components/VWysiwygEditor/extensions/color.ts +72 -0
  18. package/src/components/VWysiwygEditor/extensions/index.ts +2 -0
  19. package/src/components/VWysiwygEditor/extensions/linter/.DS_Store +0 -0
  20. package/src/components/VWysiwygEditor/extensions/linter/Linter.scss +140 -0
  21. package/src/components/VWysiwygEditor/extensions/linter/Linter.tsx +214 -0
  22. package/src/components/VWysiwygEditor/extensions/linter/LinterPlugin.ts +65 -0
  23. package/src/components/VWysiwygEditor/extensions/linter/index.ts +5 -0
  24. package/src/components/VWysiwygEditor/extensions/linter/plugins/BadWords.tsx +50 -0
  25. package/src/components/VWysiwygEditor/extensions/linter/plugins/HeadingLevel.ts +39 -0
  26. package/src/components/VWysiwygEditor/extensions/linter/plugins/Punctuation.ts +49 -0
  27. package/src/components/VWysiwygEditor/extensions/linter/plugins/index.ts +3 -0
  28. package/src/components/VWysiwygEditor/extensions/linter/utils.ts +28 -0
  29. package/src/components/VWysiwygEditor/index.ts +1 -0
  30. package/src/components/VWysiwygEditor/schemes.ts +190 -4
  31. package/src/components/VWysiwygEditor/tools/color.scss +140 -0
  32. package/src/components/VWysiwygEditor/tools/color.tsx +90 -0
  33. package/src/components/VWysiwygEditor/tools/index.ts +1 -1
  34. package/src/service.tsx +5 -0
  35. package/src/tool/vite-plugin.ts +1 -0
  36. package/src/components/VWysiwygEditor/tools/text-color.ts +0 -14
@@ -0,0 +1,65 @@
1
+ import { VNodeChild } from 'vue';
2
+ import { Node as ProsemirrorNode } from 'prosemirror-model';
3
+ import { EditorView } from 'prosemirror-view';
4
+ import { cheepUUID } from './utils';
5
+
6
+ export type WysiwygLinterFixFn = (
7
+ view: EditorView,
8
+ issue: WysiwygLinterResult,
9
+ ) => any;
10
+
11
+ export type WysiwygLinterFixMessage = string | (() => VNodeChild);
12
+
13
+ export interface WysiwygLinterFixer {
14
+ message: WysiwygLinterFixMessage;
15
+ handler: WysiwygLinterFixFn;
16
+ }
17
+
18
+ export type WysiwygLinterResultLevel = 'warning' | 'error';
19
+
20
+ // fixers
21
+ export interface WysiwygLinterResult {
22
+ id: string;
23
+ icon: boolean;
24
+ level: WysiwygLinterResultLevel;
25
+ message: WysiwygLinterFixMessage;
26
+ from: number;
27
+ to: number;
28
+ fix: WysiwygLinterFixer[];
29
+ }
30
+
31
+ export interface RawLinterResult
32
+ extends Omit<WysiwygLinterResult, 'level' | 'fix' | 'id' | 'icon'> {
33
+ level?: WysiwygLinterResultLevel;
34
+ icon?: boolean;
35
+ fix?: WysiwygLinterFixer | WysiwygLinterFixer[];
36
+ }
37
+
38
+ export class WysiwygLinterPlugin {
39
+ protected doc: ProsemirrorNode;
40
+
41
+ private results: Array<WysiwygLinterResult> = [];
42
+
43
+ constructor(doc: ProsemirrorNode) {
44
+ this.doc = doc;
45
+ }
46
+
47
+ record(result: RawLinterResult) {
48
+ const { fix = [], icon = false } = result;
49
+ this.results.push({
50
+ level: 'error',
51
+ id: cheepUUID(),
52
+ ...result,
53
+ fix: Array.isArray(fix) ? fix : [fix],
54
+ icon,
55
+ });
56
+ }
57
+
58
+ scan() {
59
+ return this;
60
+ }
61
+
62
+ getResults() {
63
+ return this.results;
64
+ }
65
+ }
@@ -0,0 +1,5 @@
1
+ export { WysiwygLinter } from './Linter';
2
+ export { WysiwygLinterPlugin } from './LinterPlugin';
3
+ export type { WysiwygLinterOptions } from './Linter';
4
+
5
+ export * from './plugins';
@@ -0,0 +1,50 @@
1
+ import { WysiwygLinterPlugin } from '../LinterPlugin';
2
+
3
+ export function WysiwygLinterBadWords(
4
+ words: string[],
5
+ ): typeof WysiwygLinterPlugin {
6
+ const regex = new RegExp(`\\b(${words.join('|')})\\b`);
7
+
8
+ return class WysiwygLinterBadWords extends WysiwygLinterPlugin {
9
+ scan() {
10
+ this.doc.descendants((node: any, position: number) => {
11
+ if (!node.isText) {
12
+ return;
13
+ }
14
+
15
+ const matches = regex.exec(node.text);
16
+
17
+ if (matches) {
18
+ const fixValue = matches[0] + '!!!!!';
19
+
20
+ this.record({
21
+ level: 'warning',
22
+ message: `Try not to say '${matches[0]}'`,
23
+ from: position + matches.index,
24
+ to: position + matches.index + matches[0].length,
25
+ fix: [
26
+ {
27
+ message: () => (
28
+ <span>
29
+ <code>{fixValue}</code>に修正する。
30
+ </span>
31
+ ),
32
+ handler: () => {
33
+ console.log('hoge');
34
+ },
35
+ },
36
+ {
37
+ message: 'どうにかする',
38
+ handler: () => {
39
+ console.log('hoge');
40
+ },
41
+ },
42
+ ],
43
+ });
44
+ }
45
+ });
46
+
47
+ return this;
48
+ }
49
+ };
50
+ }
@@ -0,0 +1,39 @@
1
+ import { EditorView } from 'prosemirror-view';
2
+ import {
3
+ WysiwygLinterPlugin,
4
+ WysiwygLinterResult as Issue,
5
+ } from '../LinterPlugin';
6
+
7
+ export class WysiwygLinterHeadingLevel extends WysiwygLinterPlugin {
8
+ fixHeader(level: number) {
9
+ return function ({ state, dispatch }: EditorView, issue: Issue) {
10
+ dispatch(state.tr.setNodeMarkup(issue.from - 1, undefined, { level }));
11
+ };
12
+ }
13
+
14
+ scan() {
15
+ let lastHeadLevel: number | null = null;
16
+
17
+ this.doc.descendants((node, position) => {
18
+ if (node.type.name === 'heading') {
19
+ // Check whether heading levels fit under the current level
20
+ const { level } = node.attrs;
21
+
22
+ if (lastHeadLevel != null && level > lastHeadLevel + 1) {
23
+ this.record({
24
+ message: `Heading too small (${level} under ${lastHeadLevel})`,
25
+ from: position + 1,
26
+ to: position + 1 + node.content.size,
27
+ fix: {
28
+ message: '修正する',
29
+ handler: this.fixHeader(lastHeadLevel + 1),
30
+ },
31
+ });
32
+ }
33
+ lastHeadLevel = level;
34
+ }
35
+ });
36
+
37
+ return this;
38
+ }
39
+ }
@@ -0,0 +1,49 @@
1
+ import { EditorView } from 'prosemirror-view';
2
+ import {
3
+ WysiwygLinterPlugin,
4
+ WysiwygLinterResult as Issue,
5
+ } from '../LinterPlugin';
6
+
7
+ export class WysiwygLinterPunctuation extends WysiwygLinterPlugin {
8
+ public regex = / ([,.!?:]) ?/g;
9
+
10
+ fix(replacement: any) {
11
+ return function ({ state, dispatch }: EditorView, issue: Issue) {
12
+ dispatch(
13
+ state.tr.replaceWith(
14
+ issue.from,
15
+ issue.to,
16
+ state.schema.text(replacement),
17
+ ),
18
+ );
19
+ };
20
+ }
21
+
22
+ scan() {
23
+ this.doc.descendants((node, position) => {
24
+ if (!node.isText) {
25
+ return;
26
+ }
27
+
28
+ if (!node.text) {
29
+ return;
30
+ }
31
+
32
+ const matches = this.regex.exec(node.text);
33
+
34
+ if (matches) {
35
+ this.record({
36
+ message: 'Suspicious spacing around punctuation',
37
+ from: position + matches.index,
38
+ to: position + matches.index + matches[0].length,
39
+ fix: {
40
+ message: 'Fix it!!!',
41
+ handler: this.fix(`${matches[1]} `),
42
+ },
43
+ });
44
+ }
45
+ });
46
+
47
+ return this;
48
+ }
49
+ }
@@ -0,0 +1,3 @@
1
+ export * from './BadWords';
2
+ export * from './HeadingLevel';
3
+ export * from './Punctuation';
@@ -0,0 +1,28 @@
1
+ let IDX = 256,
2
+ BUFFER: any;
3
+ const HEX: any = [];
4
+ while (IDX--) HEX[IDX] = (IDX + 256).toString(16).substring(1);
5
+
6
+ export function cheepUUID() {
7
+ let i = 0,
8
+ num,
9
+ out = '';
10
+
11
+ if (!BUFFER || IDX + 16 > 256) {
12
+ BUFFER = Array((i = 256));
13
+ while (i--) BUFFER[i] = (256 * Math.random()) | 0;
14
+ i = IDX = 0;
15
+ }
16
+
17
+ for (; i < 16; i++) {
18
+ num = BUFFER[IDX + i];
19
+ if (i == 6) out += HEX[(num & 15) | 64];
20
+ else if (i == 8) out += HEX[(num & 63) | 128];
21
+ else out += HEX[num];
22
+
23
+ if (i & 1 && i > 1 && i < 11) out += '-';
24
+ }
25
+
26
+ IDX++;
27
+ return out;
28
+ }
@@ -1,3 +1,4 @@
1
1
  export * from './VWysiwygEditor';
2
+ export * from './extensions';
2
3
  export * from './tools';
3
4
  export * from './schemes';
@@ -1,15 +1,201 @@
1
1
  import { type VuiService } from '../../service';
2
- import { type Editor, type Extensions } from '@tiptap/vue-3';
2
+ import {
3
+ type Editor,
4
+ type Extensions,
5
+ type Node,
6
+ type Extension,
7
+ type Mark,
8
+ type AnyExtension,
9
+ type EditorOptions,
10
+ } from '@tiptap/vue-3';
3
11
  import { type IconName } from '../VIcon';
12
+ import { VNodeChild } from 'vue';
13
+
14
+ const EDITOR_EVENTS = [
15
+ 'beforeCreate',
16
+ 'create',
17
+ 'update',
18
+ 'selectionUpdate',
19
+ 'transaction',
20
+ 'focus',
21
+ 'blur',
22
+ 'destroy',
23
+ ] as const;
24
+
25
+ type PrefixedEventName<S extends string> = `on${Capitalize<S>}`;
26
+
27
+ const prefixedEventName = <S extends string>(
28
+ source: S,
29
+ ): PrefixedEventName<S> => {
30
+ return `on${source.charAt(0).toUpperCase()}${source.slice(1)}` as any;
31
+ };
32
+
33
+ export type WysiwygEditorEvent = typeof EDITOR_EVENTS[number];
34
+
35
+ export type WysiwygEditorPrefixedEvent = PrefixedEventName<WysiwygEditorEvent>;
36
+
37
+ // eslint-disable-next-line @typescript-eslint/no-empty-interface
38
+ export interface WysiwygEditorEventsOptions
39
+ extends Partial<Pick<EditorOptions, WysiwygEditorPrefixedEvent>> {}
40
+
41
+ export type WysiwygEditorEventsBucket = {
42
+ [EV in WysiwygEditorEvent]: NonNullable<
43
+ WysiwygEditorEventsOptions[PrefixedEventName<EV>]
44
+ >[];
45
+ };
46
+
47
+ export class WysiwygEditorInitializeContext {
48
+ readonly listeners: WysiwygEditorEventsBucket = {} as any;
49
+ private readonly _vui: () => VuiService;
50
+
51
+ get vui() {
52
+ return this._vui();
53
+ }
54
+
55
+ constructor(
56
+ vuiGetter: () => VuiService,
57
+ opts: WysiwygEditorEventsOptions = {},
58
+ ) {
59
+ this._vui = vuiGetter;
60
+
61
+ EDITOR_EVENTS.forEach((event) => {
62
+ this.listeners[event] = [];
63
+ const prefixed = prefixedEventName(event);
64
+ const fn = opts[prefixed];
65
+ fn && this.listeners[event].push(fn as any);
66
+ });
67
+ }
68
+
69
+ on<EV extends WysiwygEditorEvent>(
70
+ ev: EV,
71
+ handler: NonNullable<WysiwygEditorEventsOptions[PrefixedEventName<EV>]>,
72
+ ) {
73
+ this.listeners[ev].push(handler);
74
+ return () => this.off(ev, handler);
75
+ }
76
+
77
+ off<EV extends WysiwygEditorEvent>(
78
+ ev: EV,
79
+ handler: NonNullable<WysiwygEditorEventsOptions[PrefixedEventName<EV>]>,
80
+ ) {
81
+ this.listeners[ev] = this.listeners[ev].filter(
82
+ (_handler) => _handler !== handler,
83
+ ) as any;
84
+ }
85
+
86
+ editorOptions() {
87
+ const opts: WysiwygEditorEventsOptions = {};
88
+
89
+ EDITOR_EVENTS.forEach((event) => {
90
+ const prefixed = prefixedEventName(event);
91
+ opts[prefixed] = (props) => {
92
+ const handlers = this.listeners[event];
93
+ handlers.forEach((handler) => {
94
+ handler(props as any);
95
+ });
96
+ };
97
+ });
98
+
99
+ return opts;
100
+ }
101
+ }
4
102
 
5
103
  export interface WysiwygEditorContext {
6
104
  editor: Editor;
7
105
  vui: VuiService;
8
106
  }
9
107
 
108
+ export type WysiwygExtensionFactory<Options = any, Storage = any> = (
109
+ ctx: WysiwygEditorInitializeContext,
110
+ ) =>
111
+ | Extension<Options, Storage>
112
+ | Node<Options, Storage>
113
+ | Mark<Options, Storage>;
114
+
115
+ export interface CreatedWysiwygExtension<Options = any, Storage = any> {
116
+ __isCreatedWysiwygExtension: true;
117
+ _configs: Partial<Options>[];
118
+ configure(
119
+ options?: Partial<Options>,
120
+ ): CreatedWysiwygExtension<Options, Storage>;
121
+ raw: WysiwygExtensionSource<Options, Storage>;
122
+ }
123
+
124
+ export type WysiwygExtensionSource<Options = any, Storage = any> =
125
+ | Extension<Options, Storage>
126
+ | Node<Options, Storage>
127
+ | Mark<Options, Storage>
128
+ | WysiwygExtensionFactory<Options, Storage>;
129
+
130
+ export type RawWysiwygExtension<Options = any, Storage = any> =
131
+ | WysiwygExtensionSource<Options, Storage>
132
+ | CreatedWysiwygExtension<Options, Storage>;
133
+
134
+ // export function createWysiwygExtension<Options = any, Storage = any>(
135
+ // extension: Extension<Options, Storage>,
136
+ // ): Extension<Options, Storage>;
137
+ // export function createWysiwygExtension<Options = any, Storage = any>(
138
+ // node: Node<Options, Storage>,
139
+ // ): Node<Options, Storage>;
140
+ // export function createWysiwygExtension<Options = any, Storage = any>(
141
+ // mark: Mark<Options, Storage>,
142
+ // ): Mark<Options, Storage>;
143
+ // export function createWysiwygExtension<Options = any, Storage = any>(
144
+ // factory: WysiwygExtensionFactory<Options, Storage>,
145
+ // ): WysiwygExtensionFactory<Options, Storage>;
146
+
147
+ function isCreatedWysiwygExtension<Options = any, Storage = any>(
148
+ source: unknown,
149
+ ): source is CreatedWysiwygExtension<Options, Storage> {
150
+ return (
151
+ !!source &&
152
+ typeof source === 'object' &&
153
+ (source as CreatedWysiwygExtension).__isCreatedWysiwygExtension === true
154
+ );
155
+ }
156
+
157
+ export function createWysiwygExtension<Options = any, Storage = any>(
158
+ extension: WysiwygExtensionSource<Options, Storage>,
159
+ ) {
160
+ const ext: CreatedWysiwygExtension<Options, Storage> = {
161
+ __isCreatedWysiwygExtension: true,
162
+ _configs: [],
163
+ configure: (opts) => {
164
+ opts && ext._configs.push(opts);
165
+ return ext;
166
+ },
167
+ raw: extension,
168
+ };
169
+ return ext;
170
+ }
171
+
172
+ function resolveRawWysiwygExtension(
173
+ raw: RawWysiwygExtension,
174
+ ctx: WysiwygEditorInitializeContext,
175
+ ): AnyExtension {
176
+ if (isCreatedWysiwygExtension(raw)) {
177
+ const { raw: _raw, _configs } = raw;
178
+ let ext = typeof _raw === 'function' ? _raw(ctx) : _raw;
179
+ _configs.forEach((config) => {
180
+ ext = ext.configure(config);
181
+ });
182
+ return ext;
183
+ }
184
+ return typeof raw === 'function' ? raw(ctx) : raw;
185
+ }
186
+
187
+ export function resolveRawWysiwygExtensions(
188
+ raws: RawWysiwygExtension[],
189
+ ctx: WysiwygEditorInitializeContext,
190
+ ) {
191
+ return raws.map((raw) => resolveRawWysiwygExtension(raw, ctx));
192
+ }
193
+
10
194
  export interface WysiwygEditorTool {
11
195
  key: string;
12
- icon: IconName | ((ctx: WysiwygEditorContext) => IconName);
196
+ icon:
197
+ | IconName
198
+ | ((ctx: WysiwygEditorContext) => IconName | (() => VNodeChild));
13
199
  active?: boolean | ((ctx: WysiwygEditorContext) => boolean);
14
200
  disabled?: boolean | ((ctx: WysiwygEditorContext) => boolean);
15
201
  onClick: (ctx: WysiwygEditorContext, ev: MouseEvent) => any;
@@ -39,13 +225,13 @@ export interface ResolvedWysiwygEditorSettings {
39
225
  }
40
226
 
41
227
  export function resolveRawWysiwygEditorTools(
42
- raws: RawWysiwygEditorTool[],
228
+ rawTools: RawWysiwygEditorTool[],
43
229
  vui: VuiService,
44
230
  ): ResolvedWysiwygEditorSettings {
45
231
  const tools: WysiwygEditorTool[] = [];
46
232
  const extensions: Extensions = [];
47
233
 
48
- raws.forEach((raw) => {
234
+ rawTools.forEach((raw) => {
49
235
  let resolved = resolveRawWysiwygEditorTool(raw, vui);
50
236
  if (!Array.isArray(resolved)) {
51
237
  resolved = [resolved];
@@ -0,0 +1,140 @@
1
+ :root {
2
+ --v-wysiwyg-color-tool: 30px;
3
+ }
4
+
5
+ .v-wysiwyg-color-tool {
6
+ &__button {
7
+ position: relative;
8
+ display: inline-flex;
9
+ align-items: center;
10
+ justify-content: center;
11
+ vertical-align: bottom;
12
+
13
+ &__bar {
14
+ position: absolute;
15
+ right: 0;
16
+ bottom: 0;
17
+ left: 0;
18
+ display: block;
19
+ height: 2px;
20
+ background: currentColor;
21
+ }
22
+ }
23
+
24
+ &__menu {
25
+ .v-dialog__content {
26
+ min-width: 0;
27
+ }
28
+
29
+ .v-dialog__body {
30
+ padding: 2px;
31
+ }
32
+ }
33
+
34
+ &__items {
35
+ --item-size: var(--v-wysiwyg-color-tool);
36
+
37
+ display: inline-flex;
38
+ flex-wrap: wrap;
39
+ align-items: center;
40
+ max-width: calc(var(--item-size) * 5);
41
+ vertical-align: bottom;
42
+
43
+ &--with-label {
44
+ display: flex;
45
+ flex-direction: column;
46
+ // flex-wrap: nowrap;
47
+ align-items: flex-start;
48
+ max-width: none;
49
+ padding: 2px;
50
+ }
51
+ }
52
+
53
+ &__item {
54
+ --focus-offset: calc(var(--item-size) * 0.05);
55
+
56
+ position: relative;
57
+ display: flex;
58
+ flex-basis: var(--item-size);
59
+ align-items: center;
60
+ width: var(--item-size);
61
+ height: var(--item-size);
62
+ padding: 0;
63
+ margin: 0;
64
+ // margin: 2px;
65
+ cursor: pointer;
66
+ background: transparent;
67
+ border: 0;
68
+ border-radius: 0;
69
+ outline: 0;
70
+ appearance: none;
71
+
72
+ &__color {
73
+ position: relative;
74
+ flex: 0 0 var(--item-size);
75
+ width: var(--item-size);
76
+ height: var(--item-size);
77
+
78
+ &::before {
79
+ position: absolute;
80
+ top: 0;
81
+ right: 0;
82
+ bottom: 0;
83
+ left: 0;
84
+ display: block;
85
+ content: '';
86
+ background: currentColor;
87
+ border: solid 1px transparent;
88
+ transition: all 0.15s;
89
+ }
90
+ }
91
+
92
+ &__name {
93
+ position: relative;
94
+ padding: 0 8px;
95
+ font-size: 12px;
96
+ white-space: nowrap;
97
+ }
98
+
99
+ &:hover &__color,
100
+ &:focus &__color {
101
+ &::before {
102
+ top: var(--focus-offset);
103
+ right: var(--focus-offset);
104
+ bottom: var(--focus-offset);
105
+ left: var(--focus-offset);
106
+ border-color: rgba(0, 0, 0, 0.1);
107
+ }
108
+ }
109
+ }
110
+
111
+ &__items--with-label &__item {
112
+ flex-basis: auto;
113
+ width: auto;
114
+ width: 100%;
115
+
116
+ &::before {
117
+ position: absolute;
118
+ top: 0;
119
+ right: 0;
120
+ bottom: 0;
121
+ left: 0;
122
+ display: block;
123
+ content: '';
124
+ background: currentColor;
125
+ opacity: 0;
126
+ transition: opacity 0.15s;
127
+ }
128
+
129
+ &:hover,
130
+ &:focus {
131
+ &::before {
132
+ opacity: 0.1;
133
+ }
134
+ }
135
+
136
+ & + .v-wysiwyg-color-tool__item {
137
+ margin-top: 2px;
138
+ }
139
+ }
140
+ }
@@ -0,0 +1,90 @@
1
+ import './color.scss';
2
+
3
+ import { VNodeChild } from 'vue';
4
+ import { WysiwygColorExtension } from '../extensions';
5
+ import { type VuiService } from '../../../service';
6
+ import { WysiwygEditorToolFactory, WysiwygEditorTool } from '../schemes';
7
+ import TextStyle from '@tiptap/extension-text-style';
8
+ import { VIcon } from '../../VIcon';
9
+
10
+ export interface WysiwygColorItem {
11
+ key?: string | number;
12
+ name?: VNodeChild | ((vui: VuiService) => VNodeChild);
13
+ color: string | null;
14
+ }
15
+
16
+ export interface CreateWysiwygColorToolOptions {
17
+ items: WysiwygColorItem[];
18
+ withLabel?: boolean;
19
+ }
20
+
21
+ export function createWysiwygColorTool(opts: CreateWysiwygColorToolOptions) {
22
+ const WysiwygColorTool: WysiwygEditorToolFactory = (vui) => {
23
+ const tool: WysiwygEditorTool = {
24
+ key: 'textColor',
25
+ // active: ({ editor }) => editor.isActive('textStyle'),
26
+ icon:
27
+ ({ editor }) =>
28
+ () => {
29
+ const color: string | undefined =
30
+ editor.getAttributes('textStyle').color;
31
+ const style = { color };
32
+ return (
33
+ <span class="v-wysiwyg-color-tool__button">
34
+ <VIcon
35
+ class="v-wysiwyg-color-tool__button__icon"
36
+ name={vui.icon('editorTextColor')}
37
+ />
38
+ <span class="v-wysiwyg-color-tool__button__bar" style={style} />
39
+ </span>
40
+ );
41
+ },
42
+ onClick: (ctx, ev) => {
43
+ ctx.vui.menu({
44
+ class: 'v-wysiwyg-color-tool__menu',
45
+ activator: ev,
46
+ content: (stack) => (
47
+ <div
48
+ class={[
49
+ 'v-wysiwyg-color-tool__items',
50
+ { 'v-wysiwyg-color-tool__items--with-label': opts.withLabel },
51
+ ]}>
52
+ {opts.items.map((item, index) => (
53
+ <button
54
+ key={item.key == null ? index : item.key}
55
+ class="v-wysiwyg-color-tool__item"
56
+ type="button"
57
+ onClick={() => {
58
+ const { color } = item;
59
+ let command = ctx.editor.chain().focus();
60
+ if (color) {
61
+ command = command.setColor(color);
62
+ } else {
63
+ command = command.unsetColor();
64
+ }
65
+ command.run();
66
+ stack.close();
67
+ }}>
68
+ <span
69
+ class="v-wysiwyg-color-tool__item__color"
70
+ style={item.color ? { color: item.color } : {}}
71
+ />
72
+ {opts.withLabel && (
73
+ <span class="v-wysiwyg-color-tool__item__name">
74
+ {item.name}
75
+ </span>
76
+ )}
77
+ </button>
78
+ ))}
79
+ </div>
80
+ ),
81
+ });
82
+ },
83
+ floating: true,
84
+ extensions: [TextStyle, WysiwygColorExtension],
85
+ };
86
+ return tool;
87
+ };
88
+
89
+ return WysiwygColorTool;
90
+ }
@@ -5,4 +5,4 @@ export * from './format-underline';
5
5
  export * from './history';
6
6
  export * from './link';
7
7
  export * from './ordered-list';
8
- export * from './text-color';
8
+ export * from './color';