@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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fastkit/vui",
3
- "version": "0.7.57",
3
+ "version": "0.7.64",
4
4
  "description": "@fastkit/vui",
5
5
  "buildOptions": {
6
6
  "name": "Vui",
@@ -32,13 +32,14 @@
32
32
  "vue": "^3.2.26"
33
33
  },
34
34
  "dependencies": {
35
- "@fastkit/color-scheme": "0.7.57",
36
- "@fastkit/icon-font": "0.7.57",
37
- "@fastkit/tiny-logger": "0.7.57",
38
- "@fastkit/vite-kit": "0.7.57",
39
- "@fastkit/vue-kit": "0.7.57",
35
+ "@fastkit/color-scheme": "0.7.64",
36
+ "@fastkit/icon-font": "0.7.64",
37
+ "@fastkit/tiny-logger": "0.7.64",
38
+ "@fastkit/vite-kit": "0.7.64",
39
+ "@fastkit/vue-kit": "0.7.64",
40
40
  "@tiptap/extension-link": "^2.0.0-beta.36",
41
41
  "@tiptap/extension-underline": "^2.0.0-beta.23",
42
+ "@tiptap/extension-text-style": "^2.0.0-beta.23",
42
43
  "@tiptap/starter-kit": "^2.0.0-beta.183",
43
44
  "@tiptap/vue-3": "^2.0.0-beta.90",
44
45
  "eta": "^1.12.3",
@@ -20,4 +20,8 @@
20
20
  z-index: 1;
21
21
  }
22
22
  }
23
+
24
+ // &--has-icon &__item {
25
+
26
+ // }
23
27
  }
@@ -25,6 +25,7 @@ export const VButtonGroup = defineComponent({
25
25
  },
26
26
  setup(props, ctx) {
27
27
  return () => {
28
+ let hasIcon = false;
28
29
  let buttonLength = 0;
29
30
 
30
31
  let tmp = renderSlotOrEmpty(ctx.slots) || [];
@@ -43,6 +44,10 @@ export const VButtonGroup = defineComponent({
43
44
 
44
45
  buttonLength++;
45
46
 
47
+ if (!hasIcon && node.props && !!node.props.icon) {
48
+ hasIcon = true;
49
+ }
50
+
46
51
  return {
47
52
  isButton: true,
48
53
  node,
@@ -67,6 +72,7 @@ export const VButtonGroup = defineComponent({
67
72
  class: [
68
73
  'v-button-group__item',
69
74
  {
75
+ 'v-button--icon': hasIcon,
70
76
  'v-button-group__item--has-left': hasLeft,
71
77
  'v-button-group__item--has-right': hasRight,
72
78
  },
@@ -75,7 +81,12 @@ export const VButtonGroup = defineComponent({
75
81
  }
76
82
  return child.node;
77
83
  });
78
- return <div class="v-button-group">{$children}</div>;
84
+ return (
85
+ <div
86
+ class={['v-button-group', { 'v-button-group--has-icon': hasIcon }]}>
87
+ {$children}
88
+ </div>
89
+ );
79
90
  };
80
91
  },
81
92
  });
@@ -20,6 +20,10 @@
20
20
  position: relative;
21
21
  }
22
22
 
23
+ .v-control-field__body {
24
+ width: 100%;
25
+ }
26
+
23
27
  &__floating-toolbar {
24
28
  position: absolute;
25
29
  right: 0;
@@ -29,6 +33,7 @@
29
33
 
30
34
  &__body {
31
35
  position: relative;
36
+ width: 100%;
32
37
  max-height: var(--max-height);
33
38
  // max-height: 200px;
34
39
  overflow: auto;
@@ -45,6 +50,7 @@
45
50
  }
46
51
 
47
52
  &__prose {
53
+ width: 100%;
48
54
  min-height: var(--min-height);
49
55
  outline: none;
50
56
  }
@@ -1,5 +1,13 @@
1
1
  import './VWysiwygEditor.scss';
2
- import { defineComponent, Fragment, PropType, computed, ref, watch } from 'vue';
2
+ import {
3
+ defineComponent,
4
+ PropType,
5
+ computed,
6
+ ref,
7
+ watch,
8
+ onBeforeUnmount,
9
+ VNodeChild,
10
+ } from 'vue';
3
11
  import {
4
12
  createFormControlProps,
5
13
  FormControlSlots,
@@ -34,9 +42,13 @@ import {
34
42
  import StarterKit from '@tiptap/starter-kit';
35
43
  import { VButton, VButtonGroup } from '../VButton';
36
44
  import {
45
+ RawWysiwygExtension,
46
+ resolveRawWysiwygExtensions,
37
47
  RawWysiwygEditorTool,
38
48
  resolveRawWysiwygEditorTools,
39
49
  WysiwygEditorContext,
50
+ // EditorEventsOptions,
51
+ WysiwygEditorInitializeContext,
40
52
  } from './schemes';
41
53
 
42
54
  export const VWysiwygEditor = defineComponent({
@@ -49,6 +61,10 @@ export const VWysiwygEditor = defineComponent({
49
61
  ...createControlFieldProviderProps(),
50
62
  ...createControlProps(),
51
63
  ...defineSlotsProps<FormControlSlots & InputBoxSlots>(),
64
+ extensions: {
65
+ type: Array as PropType<RawWysiwygExtension[]>,
66
+ default: () => [],
67
+ },
52
68
  tools: {
53
69
  type: Array as PropType<RawWysiwygEditorTool[]>,
54
70
  default: () => [],
@@ -78,6 +94,23 @@ export const VWysiwygEditor = defineComponent({
78
94
  validationValue: () => textRef.value,
79
95
  });
80
96
 
97
+ const initializeCtx = new WysiwygEditorInitializeContext(() => vui, {
98
+ onCreate: ({ editor }) => {
99
+ textRef.value = editor.getText();
100
+ updateEditable();
101
+ },
102
+ onFocus: ({ event }) => {
103
+ inputControl.focusHandler(event);
104
+ },
105
+ onBlur: ({ event }) => {
106
+ inputControl.blurHandler(event);
107
+ },
108
+ onUpdate: ({ editor }) => {
109
+ inputControl.value = editor.getHTML();
110
+ textRef.value = editor.getText();
111
+ },
112
+ });
113
+
81
114
  const extensions = [
82
115
  StarterKit.configure({
83
116
  bold: false,
@@ -86,6 +119,10 @@ export const VWysiwygEditor = defineComponent({
86
119
  history: false,
87
120
  italic: false,
88
121
  }),
122
+ // Linter.configure({
123
+ // plugins: [BadWords(['abc', 'evidently']), Punctuation, HeadingLevel],
124
+ // }),
125
+ ...resolveRawWysiwygExtensions(props.extensions, initializeCtx),
89
126
  ...wysiwygSettings.value.extensions,
90
127
  ];
91
128
 
@@ -110,21 +147,13 @@ export const VWysiwygEditor = defineComponent({
110
147
  },
111
148
  );
112
149
 
150
+ initializeCtx.on('create', ({ editor }) => {
151
+ textRef.value = editor.getText();
152
+ updateEditable();
153
+ });
154
+
113
155
  const editor = useEditor({
114
- onCreate: ({ editor }) => {
115
- textRef.value = editor.getText();
116
- updateEditable();
117
- },
118
- onFocus: (ctx) => {
119
- inputControl.focusHandler(ctx.event);
120
- },
121
- onBlur: (ctx) => {
122
- inputControl.blurHandler(ctx.event);
123
- },
124
- onUpdate: (ev) => {
125
- inputControl.value = ev.editor.getHTML();
126
- textRef.value = ev.editor.getText();
127
- },
156
+ ...initializeCtx.editorOptions(),
128
157
  autofocus: props.autofocus,
129
158
  content: props.modelValue,
130
159
  extensions,
@@ -177,9 +206,12 @@ export const VWysiwygEditor = defineComponent({
177
206
 
178
207
  return (
179
208
  <VButtonGroup
180
- class={{
181
- 'v-wysiwyg-editor__floating-toolbar': props.floatingToolbar,
182
- }}
209
+ class={[
210
+ 'v-wysiwyg-editor__toolbar',
211
+ {
212
+ 'v-wysiwyg-editor__floating-toolbar': props.floatingToolbar,
213
+ },
214
+ ]}
183
215
  variant={variant}>
184
216
  {tools.map(({ key, icon, onClick, disabled, active }) => {
185
217
  const isActive =
@@ -188,16 +220,22 @@ export const VWysiwygEditor = defineComponent({
188
220
  !inputControl.canOperation ||
189
221
  (!!_editor && resolveContextableValue(context, disabled));
190
222
  let iconName: IconName | undefined;
223
+ let child: VNodeChild;
191
224
 
192
225
  if (typeof icon === 'function') {
193
226
  if (_editor) {
194
- iconName = icon(context);
227
+ const _child = icon(context);
228
+ if (typeof _child === 'function') {
229
+ child = _child();
230
+ } else {
231
+ iconName = child as any;
232
+ }
195
233
  }
196
234
  } else {
197
235
  iconName = icon;
198
236
  }
199
237
 
200
- if (!iconName) return;
238
+ if (!iconName && child == null) return;
201
239
 
202
240
  return (
203
241
  <VButton
@@ -206,14 +244,19 @@ export const VWysiwygEditor = defineComponent({
206
244
  icon={iconName}
207
245
  onClick={(ev) => onClick(context, ev)}
208
246
  color={isActive ? toolButtonActiveColor : undefined}
209
- disabled={isDisabled}
210
- />
247
+ disabled={isDisabled}>
248
+ {child}
249
+ </VButton>
211
250
  );
212
251
  })}
213
252
  </VButtonGroup>
214
253
  );
215
254
  };
216
255
 
256
+ onBeforeUnmount(() => {
257
+ editor.value && editor.value.destroy();
258
+ });
259
+
217
260
  return {
218
261
  editor,
219
262
  ...inputControl.expose(),
@@ -253,40 +296,38 @@ export const VWysiwygEditor = defineComponent({
253
296
  return (
254
297
  <div class="v-wysiwyg-editor__wrapper">
255
298
  {!this.isReadonly && this.createTools()}
256
- <div class="v-wysiwyg-editor__body">
257
- <VControlField
258
- class="v-wysiwyg-editor__input"
259
- autoHeight
260
- startAdornment={this.startAdornment}
261
- endAdornment={this.endAdornment}
262
- size={this.size}
263
- // onClickHost={(ev) => {
264
- // this.focus();
265
- // }}
266
- v-slots={{
267
- ...this.$slots,
268
- default: () => {
269
- return (
270
- <Fragment>
271
- <EditorContent
272
- class="v-wysiwyg-editor__input__element wysiwyg"
273
- editor={editor}
274
- />
275
- {!this.floatingToolbar &&
276
- !!editor &&
277
- !this.isReadonly && (
278
- <BubbleMenu
279
- class="v-wysiwyg-editor__bubble-menu"
280
- editor={editor}>
281
- {this.createTools(true)}
282
- </BubbleMenu>
283
- )}
284
- </Fragment>
285
- );
286
- },
287
- }}
288
- />
289
- </div>
299
+ <VControlField
300
+ class="v-wysiwyg-editor__input"
301
+ autoHeight
302
+ startAdornment={this.startAdornment}
303
+ endAdornment={this.endAdornment}
304
+ size={this.size}
305
+ // onClickHost={(ev) => {
306
+ // this.focus();
307
+ // }}
308
+ v-slots={{
309
+ ...this.$slots,
310
+ default: () => {
311
+ return (
312
+ <div class="v-wysiwyg-editor__body">
313
+ <EditorContent
314
+ class="v-wysiwyg-editor__input__element wysiwyg"
315
+ editor={editor}
316
+ />
317
+ {!this.floatingToolbar &&
318
+ !!editor &&
319
+ !this.isReadonly && (
320
+ <BubbleMenu
321
+ class="v-wysiwyg-editor__bubble-menu"
322
+ editor={editor}>
323
+ {this.createTools(true)}
324
+ </BubbleMenu>
325
+ )}
326
+ </div>
327
+ );
328
+ },
329
+ }}
330
+ />
290
331
  </div>
291
332
  );
292
333
  },
@@ -0,0 +1,72 @@
1
+ import { Extension } from '@tiptap/core';
2
+ import '@tiptap/extension-text-style';
3
+
4
+ export type WysiwygColorOptions = {
5
+ types: string[];
6
+ };
7
+
8
+ declare module '@tiptap/core' {
9
+ interface Commands<ReturnType> {
10
+ color: {
11
+ /**
12
+ * Set the text color
13
+ */
14
+ setColor: (color: string) => ReturnType;
15
+ /**
16
+ * Unset the text color
17
+ */
18
+ unsetColor: () => ReturnType;
19
+ };
20
+ }
21
+ }
22
+
23
+ export const WysiwygColorExtension = Extension.create<WysiwygColorOptions>({
24
+ name: 'color',
25
+
26
+ addOptions() {
27
+ return {
28
+ types: ['textStyle'],
29
+ };
30
+ },
31
+
32
+ addGlobalAttributes() {
33
+ return [
34
+ {
35
+ types: this.options.types,
36
+ attributes: {
37
+ color: {
38
+ default: null,
39
+ parseHTML: (element) => element.style.color.replace(/['"]+/g, ''),
40
+ renderHTML: (attributes) => {
41
+ if (!attributes.color) {
42
+ return {};
43
+ }
44
+
45
+ return {
46
+ style: `color: ${attributes.color}`,
47
+ };
48
+ },
49
+ },
50
+ },
51
+ },
52
+ ];
53
+ },
54
+
55
+ addCommands() {
56
+ return {
57
+ setColor:
58
+ (color) =>
59
+ ({ chain }) => {
60
+ return chain().setMark('textStyle', { color }).run();
61
+ },
62
+ unsetColor:
63
+ () =>
64
+ ({ chain }) => {
65
+ return chain()
66
+ .setMark('textStyle', { color: null })
67
+ .removeEmptyTextStyle()
68
+ .run();
69
+ },
70
+ };
71
+ },
72
+ });
@@ -0,0 +1,2 @@
1
+ export * from './linter';
2
+ export * from './color';
@@ -0,0 +1,140 @@
1
+ .v-linter {
2
+ &__problem {
3
+ margin-bottom: -1px;
4
+ cursor: help;
5
+ background: var(--deep-color);
6
+ border-bottom: 1px solid var(--main-color);
7
+ }
8
+
9
+ &__issue-icon {
10
+ position: absolute;
11
+ right: 2px;
12
+ display: inline-flex;
13
+ align-items: center;
14
+ justify-content: center;
15
+ width: 1.2em;
16
+ height: 1.2em;
17
+ margin-top: 0.2em;
18
+ // padding-left: 0.5px;
19
+ font-family: times, georgia, serif;
20
+ font-size: 15px;
21
+ font-weight: 700;
22
+ line-height: 1;
23
+ color: var(--text-color);
24
+ text-align: center;
25
+ cursor: pointer;
26
+ background: var(--main-color);
27
+ border-radius: 100px;
28
+
29
+ &::before {
30
+ content: '!';
31
+ }
32
+ }
33
+
34
+ &__issue-menu-wrapper {
35
+ position: relative;
36
+ display: inline-block;
37
+ width: 0px;
38
+ height: 1em;
39
+ overflow: visible;
40
+ }
41
+
42
+ &__issue-menu {
43
+ font-size: 87.5%;
44
+ // font-size: 12px;
45
+ line-height: 1.5;
46
+
47
+ .v-dialog__body {
48
+ padding: 0;
49
+ font-size: inherit;
50
+ }
51
+
52
+ &__message {
53
+ padding: 8px;
54
+ }
55
+
56
+ &__fixers {
57
+ position: relative;
58
+ padding: 4px 0;
59
+ // padding: 8px;
60
+ // border-top: solid 1px var(--divider-color);
61
+ &::before {
62
+ position: absolute;
63
+ top: 0;
64
+ right: 0;
65
+ left: 0;
66
+ display: block;
67
+ height: 1px;
68
+ content: '';
69
+ background: currentColor;
70
+ opacity: 0.5;
71
+ }
72
+ }
73
+
74
+ &__fixer {
75
+ &__button {
76
+ position: relative;
77
+ display: block;
78
+ width: 100%;
79
+ padding: 8px;
80
+ font: inherit;
81
+ font-size: 12px;
82
+ // margin-top: 8px;
83
+ // color: var(--palette-link);
84
+ text-align: left;
85
+ // text-decoration: underline;
86
+ cursor: pointer;
87
+ background: transparent;
88
+ border: 0;
89
+ outline: 0;
90
+ appearance: none;
91
+
92
+ &::before {
93
+ position: absolute;
94
+ top: 0;
95
+ right: 0;
96
+ bottom: 0;
97
+ left: 0;
98
+ display: block;
99
+ content: '';
100
+ background: currentColor;
101
+ opacity: 0;
102
+ transition: 0.15s;
103
+ }
104
+
105
+ &:hover,
106
+ &:focus {
107
+ &::before {
108
+ opacity: 0.1;
109
+ }
110
+ }
111
+
112
+ &__message {
113
+ position: relative;
114
+ }
115
+ }
116
+ }
117
+ // pointer-events: none;
118
+ // background: #fff;
119
+ // border-radius: 4px;
120
+ // opacity: 0;
121
+ // transition: opacity 0.15s;
122
+
123
+ // &--active {
124
+ // pointer-events: auto;
125
+ // opacity: 1;
126
+ // }
127
+
128
+ &__fix {
129
+ display: inline;
130
+ margin-top: 8px;
131
+ color: var(--palette-link);
132
+ text-decoration: underline;
133
+ cursor: pointer;
134
+ background: transparent;
135
+ border: 0;
136
+ outline: 0;
137
+ appearance: none;
138
+ }
139
+ }
140
+ }