@adminforth/rich-editor 2.1.1 → 3.0.0
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.
- package/README.md +3 -0
- package/build.log +2 -2
- package/custom/quillEditor.vue +90 -45
- package/dist/custom/quillEditor.vue +90 -45
- package/dist/index.d.ts +0 -1
- package/dist/index.js +2 -15
- package/index.ts +2 -19
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
[](https://tluma.ai/ask-ai/devforth/adminforth)
|
|
6
6
|
|
|
7
|
+
> [!WARNING]
|
|
8
|
+
> **This plugin is not recommended for new projects.** Use the [Markdown plugin](https://adminforth.dev/docs/tutorial/Plugins/markdown/) (`@adminforth/markdown`) for editing formatted text instead. It stores content as Markdown, which suits the agentic era much better than HTML: AI agents read and write Markdown reliably, it stays readable as plain text and gives clean diffs. RichEditor keeps working for apps which already store HTML.
|
|
9
|
+
|
|
7
10
|
Allows to add a rich text editor to your AdminForth text columns.
|
|
8
11
|
|
|
9
12
|
## Features
|
package/build.log
CHANGED
|
@@ -10,5 +10,5 @@ custom/package.json
|
|
|
10
10
|
custom/quillEditor.vue
|
|
11
11
|
custom/tsconfig.json
|
|
12
12
|
|
|
13
|
-
sent
|
|
14
|
-
total size is
|
|
13
|
+
sent 38,509 bytes received 115 bytes 77,248.00 bytes/sec
|
|
14
|
+
total size is 38,086 speedup is 0.99
|
package/custom/quillEditor.vue
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div
|
|
3
3
|
v-if="wasQuillInitializedSuccessfully"
|
|
4
|
-
class="
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
class="af-quill-editor block w-full whitespace-normal rounded-default border shadow-customLight transition-colors
|
|
5
|
+
bg-lightInputBackground text-lightInputText border-lightInputBorder hover:border-lightInputBorderHover
|
|
6
|
+
focus-within:border-lightInputFocusBorder focus-within:ring-[3px] focus-within:ring-lightInputFocusRing
|
|
7
|
+
dark:bg-darkInputBackground dark:text-darkInputText dark:border-darkInputBorder dark:hover:border-darkInputBorderHover
|
|
8
|
+
dark:focus-within:border-darkInputFocusBorder dark:focus-within:ring-darkInputFocusRing"
|
|
8
9
|
>
|
|
9
10
|
<div
|
|
10
11
|
ref="editor"
|
|
@@ -28,7 +29,7 @@
|
|
|
28
29
|
</template>
|
|
29
30
|
|
|
30
31
|
<script setup lang="ts">
|
|
31
|
-
import { onMounted, ref, onUnmounted, watch, type Ref } from "vue";
|
|
32
|
+
import { onMounted, ref, onUnmounted, watch, h, render, type Ref, type FunctionalComponent } from "vue";
|
|
32
33
|
import { callAdminForthApi } from '@/utils';
|
|
33
34
|
import { AdminForthColumnCommon } from '@/types/Common';
|
|
34
35
|
import adminforth from '@/adminforth';
|
|
@@ -39,6 +40,11 @@ import "quill/dist/quill.snow.css";
|
|
|
39
40
|
import QuillTableBetter from 'quill-table-better-yaroslav8765';
|
|
40
41
|
import 'quill-table-better-yaroslav8765/dist/quill-table-better.css';
|
|
41
42
|
import { Textarea } from '@/afcl'
|
|
43
|
+
import {
|
|
44
|
+
IconAlignCenter, IconAlignJustify, IconAlignLeft, IconAlignRight, IconBold, IconCode, IconHeading2, IconHeading3,
|
|
45
|
+
IconImage, IconItalic, IconLink, IconList, IconListChecks, IconListOrdered, IconRemoveFormatting, IconStrikethrough,
|
|
46
|
+
IconTable, IconTextQuote, IconUnderline,
|
|
47
|
+
} from '@iconify-prerendered/vue-lucide';
|
|
42
48
|
|
|
43
49
|
import { useI18n } from 'vue-i18n';
|
|
44
50
|
import { ok } from "assert";
|
|
@@ -131,6 +137,31 @@ Quill.register({
|
|
|
131
137
|
'modules/table-better': QuillTableBetter
|
|
132
138
|
}, true);
|
|
133
139
|
|
|
140
|
+
// the toolbar draws Lucide icons like the rest of the UI; Quill takes icons as SVG strings,
|
|
141
|
+
// so they are set after the table module registered its own one
|
|
142
|
+
function iconSvg(icon: FunctionalComponent) {
|
|
143
|
+
const holder = document.createElement('div');
|
|
144
|
+
render(h(icon), holder);
|
|
145
|
+
return holder.innerHTML;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const quillIcons = Quill.import('ui/icons') as Record<string, any>;
|
|
149
|
+
Object.assign(quillIcons, {
|
|
150
|
+
bold: iconSvg(IconBold),
|
|
151
|
+
italic: iconSvg(IconItalic),
|
|
152
|
+
underline: iconSvg(IconUnderline),
|
|
153
|
+
strike: iconSvg(IconStrikethrough),
|
|
154
|
+
'table-better': iconSvg(IconTable),
|
|
155
|
+
blockquote: iconSvg(IconTextQuote),
|
|
156
|
+
'code-block': iconSvg(IconCode),
|
|
157
|
+
link: iconSvg(IconLink),
|
|
158
|
+
image: iconSvg(IconImage),
|
|
159
|
+
clean: iconSvg(IconRemoveFormatting),
|
|
160
|
+
header: { ...quillIcons.header, 2: iconSvg(IconHeading2), 3: iconSvg(IconHeading3) },
|
|
161
|
+
list: { ...quillIcons.list, ordered: iconSvg(IconListOrdered), bullet: iconSvg(IconList), check: iconSvg(IconListChecks) },
|
|
162
|
+
align: { ...quillIcons.align, '': iconSvg(IconAlignLeft), center: iconSvg(IconAlignCenter), right: iconSvg(IconAlignRight), justify: iconSvg(IconAlignJustify) },
|
|
163
|
+
});
|
|
164
|
+
|
|
134
165
|
|
|
135
166
|
const updaterQueue = new AsyncQueue();
|
|
136
167
|
|
|
@@ -630,34 +661,61 @@ function removeCompletionOnBlur() {
|
|
|
630
661
|
|
|
631
662
|
.af-quill-editor {
|
|
632
663
|
|
|
664
|
+
// the toolbar sits on a hairline over the text, its buttons are quiet like AFCL ghost icon buttons
|
|
633
665
|
.ql-toolbar.ql-snow[class] {
|
|
634
|
-
border:
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
666
|
+
@apply flex flex-wrap items-center gap-0.5 border-0 border-b border-lightInputBorder px-1.5 py-1 dark:border-darkInputBorder;
|
|
667
|
+
font-family: inherit;
|
|
668
|
+
|
|
669
|
+
.ql-formats {
|
|
670
|
+
@apply inline-flex items-center gap-0.5 mr-2;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
button, .ql-picker-label {
|
|
674
|
+
@apply w-7 h-7 p-1.5 rounded-default border-0 text-lightInputIconColor transition-colors dark:text-darkInputIconColor;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
button svg, .ql-picker-label svg {
|
|
678
|
+
@apply w-4 h-4;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
button:hover, .ql-picker-label:hover {
|
|
682
|
+
@apply bg-lightSecondaryDarken text-lightInputText dark:bg-darkSecondaryLighten dark:text-darkInputText;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
button.ql-active, .ql-picker-label.ql-active, .ql-picker-item.ql-selected {
|
|
686
|
+
@apply text-lightPrimary dark:text-darkPrimary;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
.ql-picker.ql-icon-picker {
|
|
690
|
+
@apply w-7 h-7;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
.ql-picker-options {
|
|
694
|
+
@apply p-1 rounded-lg border-0 bg-lightDropdownOptionsBackground shadow-popover dark:bg-darkDropdownOptionsBackground dark:shadow-darkPopover;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
.ql-picker-item {
|
|
698
|
+
@apply w-7 h-7 p-1.5 rounded-md text-lightInputIconColor hover:bg-lightDropdownOptionsHoverBackground hover:text-lightInputText dark:text-darkInputIconColor dark:hover:bg-darkDropdownOptionsHoverBackground dark:hover:text-darkInputText;
|
|
638
699
|
}
|
|
639
700
|
}
|
|
640
701
|
|
|
641
702
|
.ql-container {
|
|
642
703
|
border: 0;
|
|
704
|
+
font-family: inherit;
|
|
705
|
+
font-size: inherit;
|
|
706
|
+
|
|
643
707
|
.ql-editor {
|
|
644
708
|
position: relative;
|
|
645
|
-
|
|
709
|
+
@apply px-3 py-2.5 text-sm;
|
|
646
710
|
min-height: 100px;
|
|
647
711
|
&.ql-blank::before {
|
|
648
|
-
left:
|
|
649
|
-
font-style: normal;
|
|
712
|
+
@apply left-3 right-3 not-italic text-lightInputPlaceholderText dark:text-darkInputPlaceholderText;
|
|
650
713
|
}
|
|
651
714
|
}
|
|
652
715
|
}
|
|
653
716
|
|
|
654
|
-
// .ql-editor:not(:focus) [completer] {
|
|
655
|
-
// display: none;
|
|
656
|
-
// }
|
|
657
|
-
|
|
658
717
|
.ql-editor [completer] {
|
|
659
|
-
|
|
660
|
-
font-style: italic;
|
|
718
|
+
@apply italic text-lightInputPlaceholderText dark:text-darkInputPlaceholderText;
|
|
661
719
|
}
|
|
662
720
|
|
|
663
721
|
// basic typography
|
|
@@ -690,23 +748,21 @@ function removeCompletionOnBlur() {
|
|
|
690
748
|
/* Horizontal rule */
|
|
691
749
|
hr {
|
|
692
750
|
margin: 0.8rem 0;
|
|
693
|
-
border:
|
|
694
|
-
border-top: 1px solid #ddd;
|
|
751
|
+
@apply border-0 border-t border-lightInputBorder dark:border-darkInputBorder;
|
|
695
752
|
}
|
|
696
753
|
|
|
697
754
|
/* Blockquotes */
|
|
698
755
|
blockquote {
|
|
699
756
|
margin: 0.8rem 0;
|
|
700
757
|
padding-left: 0.8rem;
|
|
701
|
-
border-
|
|
758
|
+
@apply border-0 border-l-2 border-lightInputBorder text-lightInputIconColor dark:border-darkInputBorder dark:text-darkInputIconColor;
|
|
702
759
|
}
|
|
703
760
|
|
|
704
761
|
/* Preformatted text */
|
|
705
762
|
pre {
|
|
706
763
|
margin: 0.8rem 0;
|
|
707
764
|
padding: 0.4rem;
|
|
708
|
-
|
|
709
|
-
border-radius: 3px;
|
|
765
|
+
@apply rounded bg-lightSecondaryDarken text-lightInputText dark:bg-darkSecondaryLighten dark:text-darkInputText;
|
|
710
766
|
}
|
|
711
767
|
|
|
712
768
|
/* Tables */
|
|
@@ -717,11 +773,11 @@ function removeCompletionOnBlur() {
|
|
|
717
773
|
|
|
718
774
|
th, td {
|
|
719
775
|
padding: 0.3rem;
|
|
720
|
-
border
|
|
776
|
+
@apply border border-lightInputBorder dark:border-darkInputBorder;
|
|
721
777
|
}
|
|
722
778
|
|
|
723
779
|
thead th {
|
|
724
|
-
border-
|
|
780
|
+
@apply border-b-2;
|
|
725
781
|
}
|
|
726
782
|
|
|
727
783
|
/* Images */
|
|
@@ -732,27 +788,21 @@ function removeCompletionOnBlur() {
|
|
|
732
788
|
}
|
|
733
789
|
}
|
|
734
790
|
|
|
791
|
+
// Quill's own icons which are left (table menus) follow the muted icon color too
|
|
735
792
|
.ql-snow .ql-stroke {
|
|
736
|
-
@apply dark:stroke-
|
|
737
|
-
@apply stroke-lightPrimary;
|
|
738
|
-
|
|
739
|
-
}
|
|
740
|
-
.ql-snow button:hover .ql-stroke,
|
|
741
|
-
.ql-snow [role="button"]:hover .ql-stroke {
|
|
742
|
-
@apply dark:stroke-darkPrimary;
|
|
743
|
-
@apply stroke-lightPrimary;
|
|
744
|
-
filter: brightness(1.3);
|
|
793
|
+
@apply stroke-lightInputIconColor dark:stroke-darkInputIconColor;
|
|
745
794
|
}
|
|
746
795
|
|
|
747
796
|
.ql-snow .ql-fill {
|
|
748
|
-
@apply dark:fill-
|
|
749
|
-
@apply fill-lightPrimary;
|
|
797
|
+
@apply fill-lightInputIconColor dark:fill-darkInputIconColor;
|
|
750
798
|
}
|
|
751
799
|
|
|
752
|
-
.ql-snow
|
|
753
|
-
@apply dark:
|
|
754
|
-
|
|
755
|
-
|
|
800
|
+
.ql-snow .ql-active .ql-stroke {
|
|
801
|
+
@apply stroke-lightPrimary dark:stroke-darkPrimary;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
.ql-snow .ql-active .ql-fill {
|
|
805
|
+
@apply fill-lightPrimary dark:fill-darkPrimary;
|
|
756
806
|
}
|
|
757
807
|
|
|
758
808
|
}
|
|
@@ -779,11 +829,6 @@ function removeCompletionOnBlur() {
|
|
|
779
829
|
}
|
|
780
830
|
}
|
|
781
831
|
|
|
782
|
-
.ql-table-better svg path {
|
|
783
|
-
@apply dark:fill-darkPrimary;
|
|
784
|
-
@apply fill-lightPrimary;
|
|
785
|
-
}
|
|
786
|
-
|
|
787
832
|
.ql-table-select-container {
|
|
788
833
|
@apply dark:bg-darkForm;
|
|
789
834
|
@apply bg-lightForm;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div
|
|
3
3
|
v-if="wasQuillInitializedSuccessfully"
|
|
4
|
-
class="
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
class="af-quill-editor block w-full whitespace-normal rounded-default border shadow-customLight transition-colors
|
|
5
|
+
bg-lightInputBackground text-lightInputText border-lightInputBorder hover:border-lightInputBorderHover
|
|
6
|
+
focus-within:border-lightInputFocusBorder focus-within:ring-[3px] focus-within:ring-lightInputFocusRing
|
|
7
|
+
dark:bg-darkInputBackground dark:text-darkInputText dark:border-darkInputBorder dark:hover:border-darkInputBorderHover
|
|
8
|
+
dark:focus-within:border-darkInputFocusBorder dark:focus-within:ring-darkInputFocusRing"
|
|
8
9
|
>
|
|
9
10
|
<div
|
|
10
11
|
ref="editor"
|
|
@@ -28,7 +29,7 @@
|
|
|
28
29
|
</template>
|
|
29
30
|
|
|
30
31
|
<script setup lang="ts">
|
|
31
|
-
import { onMounted, ref, onUnmounted, watch, type Ref } from "vue";
|
|
32
|
+
import { onMounted, ref, onUnmounted, watch, h, render, type Ref, type FunctionalComponent } from "vue";
|
|
32
33
|
import { callAdminForthApi } from '@/utils';
|
|
33
34
|
import { AdminForthColumnCommon } from '@/types/Common';
|
|
34
35
|
import adminforth from '@/adminforth';
|
|
@@ -39,6 +40,11 @@ import "quill/dist/quill.snow.css";
|
|
|
39
40
|
import QuillTableBetter from 'quill-table-better-yaroslav8765';
|
|
40
41
|
import 'quill-table-better-yaroslav8765/dist/quill-table-better.css';
|
|
41
42
|
import { Textarea } from '@/afcl'
|
|
43
|
+
import {
|
|
44
|
+
IconAlignCenter, IconAlignJustify, IconAlignLeft, IconAlignRight, IconBold, IconCode, IconHeading2, IconHeading3,
|
|
45
|
+
IconImage, IconItalic, IconLink, IconList, IconListChecks, IconListOrdered, IconRemoveFormatting, IconStrikethrough,
|
|
46
|
+
IconTable, IconTextQuote, IconUnderline,
|
|
47
|
+
} from '@iconify-prerendered/vue-lucide';
|
|
42
48
|
|
|
43
49
|
import { useI18n } from 'vue-i18n';
|
|
44
50
|
import { ok } from "assert";
|
|
@@ -131,6 +137,31 @@ Quill.register({
|
|
|
131
137
|
'modules/table-better': QuillTableBetter
|
|
132
138
|
}, true);
|
|
133
139
|
|
|
140
|
+
// the toolbar draws Lucide icons like the rest of the UI; Quill takes icons as SVG strings,
|
|
141
|
+
// so they are set after the table module registered its own one
|
|
142
|
+
function iconSvg(icon: FunctionalComponent) {
|
|
143
|
+
const holder = document.createElement('div');
|
|
144
|
+
render(h(icon), holder);
|
|
145
|
+
return holder.innerHTML;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const quillIcons = Quill.import('ui/icons') as Record<string, any>;
|
|
149
|
+
Object.assign(quillIcons, {
|
|
150
|
+
bold: iconSvg(IconBold),
|
|
151
|
+
italic: iconSvg(IconItalic),
|
|
152
|
+
underline: iconSvg(IconUnderline),
|
|
153
|
+
strike: iconSvg(IconStrikethrough),
|
|
154
|
+
'table-better': iconSvg(IconTable),
|
|
155
|
+
blockquote: iconSvg(IconTextQuote),
|
|
156
|
+
'code-block': iconSvg(IconCode),
|
|
157
|
+
link: iconSvg(IconLink),
|
|
158
|
+
image: iconSvg(IconImage),
|
|
159
|
+
clean: iconSvg(IconRemoveFormatting),
|
|
160
|
+
header: { ...quillIcons.header, 2: iconSvg(IconHeading2), 3: iconSvg(IconHeading3) },
|
|
161
|
+
list: { ...quillIcons.list, ordered: iconSvg(IconListOrdered), bullet: iconSvg(IconList), check: iconSvg(IconListChecks) },
|
|
162
|
+
align: { ...quillIcons.align, '': iconSvg(IconAlignLeft), center: iconSvg(IconAlignCenter), right: iconSvg(IconAlignRight), justify: iconSvg(IconAlignJustify) },
|
|
163
|
+
});
|
|
164
|
+
|
|
134
165
|
|
|
135
166
|
const updaterQueue = new AsyncQueue();
|
|
136
167
|
|
|
@@ -630,34 +661,61 @@ function removeCompletionOnBlur() {
|
|
|
630
661
|
|
|
631
662
|
.af-quill-editor {
|
|
632
663
|
|
|
664
|
+
// the toolbar sits on a hairline over the text, its buttons are quiet like AFCL ghost icon buttons
|
|
633
665
|
.ql-toolbar.ql-snow[class] {
|
|
634
|
-
border:
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
666
|
+
@apply flex flex-wrap items-center gap-0.5 border-0 border-b border-lightInputBorder px-1.5 py-1 dark:border-darkInputBorder;
|
|
667
|
+
font-family: inherit;
|
|
668
|
+
|
|
669
|
+
.ql-formats {
|
|
670
|
+
@apply inline-flex items-center gap-0.5 mr-2;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
button, .ql-picker-label {
|
|
674
|
+
@apply w-7 h-7 p-1.5 rounded-default border-0 text-lightInputIconColor transition-colors dark:text-darkInputIconColor;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
button svg, .ql-picker-label svg {
|
|
678
|
+
@apply w-4 h-4;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
button:hover, .ql-picker-label:hover {
|
|
682
|
+
@apply bg-lightSecondaryDarken text-lightInputText dark:bg-darkSecondaryLighten dark:text-darkInputText;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
button.ql-active, .ql-picker-label.ql-active, .ql-picker-item.ql-selected {
|
|
686
|
+
@apply text-lightPrimary dark:text-darkPrimary;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
.ql-picker.ql-icon-picker {
|
|
690
|
+
@apply w-7 h-7;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
.ql-picker-options {
|
|
694
|
+
@apply p-1 rounded-lg border-0 bg-lightDropdownOptionsBackground shadow-popover dark:bg-darkDropdownOptionsBackground dark:shadow-darkPopover;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
.ql-picker-item {
|
|
698
|
+
@apply w-7 h-7 p-1.5 rounded-md text-lightInputIconColor hover:bg-lightDropdownOptionsHoverBackground hover:text-lightInputText dark:text-darkInputIconColor dark:hover:bg-darkDropdownOptionsHoverBackground dark:hover:text-darkInputText;
|
|
638
699
|
}
|
|
639
700
|
}
|
|
640
701
|
|
|
641
702
|
.ql-container {
|
|
642
703
|
border: 0;
|
|
704
|
+
font-family: inherit;
|
|
705
|
+
font-size: inherit;
|
|
706
|
+
|
|
643
707
|
.ql-editor {
|
|
644
708
|
position: relative;
|
|
645
|
-
|
|
709
|
+
@apply px-3 py-2.5 text-sm;
|
|
646
710
|
min-height: 100px;
|
|
647
711
|
&.ql-blank::before {
|
|
648
|
-
left:
|
|
649
|
-
font-style: normal;
|
|
712
|
+
@apply left-3 right-3 not-italic text-lightInputPlaceholderText dark:text-darkInputPlaceholderText;
|
|
650
713
|
}
|
|
651
714
|
}
|
|
652
715
|
}
|
|
653
716
|
|
|
654
|
-
// .ql-editor:not(:focus) [completer] {
|
|
655
|
-
// display: none;
|
|
656
|
-
// }
|
|
657
|
-
|
|
658
717
|
.ql-editor [completer] {
|
|
659
|
-
|
|
660
|
-
font-style: italic;
|
|
718
|
+
@apply italic text-lightInputPlaceholderText dark:text-darkInputPlaceholderText;
|
|
661
719
|
}
|
|
662
720
|
|
|
663
721
|
// basic typography
|
|
@@ -690,23 +748,21 @@ function removeCompletionOnBlur() {
|
|
|
690
748
|
/* Horizontal rule */
|
|
691
749
|
hr {
|
|
692
750
|
margin: 0.8rem 0;
|
|
693
|
-
border:
|
|
694
|
-
border-top: 1px solid #ddd;
|
|
751
|
+
@apply border-0 border-t border-lightInputBorder dark:border-darkInputBorder;
|
|
695
752
|
}
|
|
696
753
|
|
|
697
754
|
/* Blockquotes */
|
|
698
755
|
blockquote {
|
|
699
756
|
margin: 0.8rem 0;
|
|
700
757
|
padding-left: 0.8rem;
|
|
701
|
-
border-
|
|
758
|
+
@apply border-0 border-l-2 border-lightInputBorder text-lightInputIconColor dark:border-darkInputBorder dark:text-darkInputIconColor;
|
|
702
759
|
}
|
|
703
760
|
|
|
704
761
|
/* Preformatted text */
|
|
705
762
|
pre {
|
|
706
763
|
margin: 0.8rem 0;
|
|
707
764
|
padding: 0.4rem;
|
|
708
|
-
|
|
709
|
-
border-radius: 3px;
|
|
765
|
+
@apply rounded bg-lightSecondaryDarken text-lightInputText dark:bg-darkSecondaryLighten dark:text-darkInputText;
|
|
710
766
|
}
|
|
711
767
|
|
|
712
768
|
/* Tables */
|
|
@@ -717,11 +773,11 @@ function removeCompletionOnBlur() {
|
|
|
717
773
|
|
|
718
774
|
th, td {
|
|
719
775
|
padding: 0.3rem;
|
|
720
|
-
border
|
|
776
|
+
@apply border border-lightInputBorder dark:border-darkInputBorder;
|
|
721
777
|
}
|
|
722
778
|
|
|
723
779
|
thead th {
|
|
724
|
-
border-
|
|
780
|
+
@apply border-b-2;
|
|
725
781
|
}
|
|
726
782
|
|
|
727
783
|
/* Images */
|
|
@@ -732,27 +788,21 @@ function removeCompletionOnBlur() {
|
|
|
732
788
|
}
|
|
733
789
|
}
|
|
734
790
|
|
|
791
|
+
// Quill's own icons which are left (table menus) follow the muted icon color too
|
|
735
792
|
.ql-snow .ql-stroke {
|
|
736
|
-
@apply dark:stroke-
|
|
737
|
-
@apply stroke-lightPrimary;
|
|
738
|
-
|
|
739
|
-
}
|
|
740
|
-
.ql-snow button:hover .ql-stroke,
|
|
741
|
-
.ql-snow [role="button"]:hover .ql-stroke {
|
|
742
|
-
@apply dark:stroke-darkPrimary;
|
|
743
|
-
@apply stroke-lightPrimary;
|
|
744
|
-
filter: brightness(1.3);
|
|
793
|
+
@apply stroke-lightInputIconColor dark:stroke-darkInputIconColor;
|
|
745
794
|
}
|
|
746
795
|
|
|
747
796
|
.ql-snow .ql-fill {
|
|
748
|
-
@apply dark:fill-
|
|
749
|
-
@apply fill-lightPrimary;
|
|
797
|
+
@apply fill-lightInputIconColor dark:fill-darkInputIconColor;
|
|
750
798
|
}
|
|
751
799
|
|
|
752
|
-
.ql-snow
|
|
753
|
-
@apply dark:
|
|
754
|
-
|
|
755
|
-
|
|
800
|
+
.ql-snow .ql-active .ql-stroke {
|
|
801
|
+
@apply stroke-lightPrimary dark:stroke-darkPrimary;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
.ql-snow .ql-active .ql-fill {
|
|
805
|
+
@apply fill-lightPrimary dark:fill-darkPrimary;
|
|
756
806
|
}
|
|
757
807
|
|
|
758
808
|
}
|
|
@@ -779,11 +829,6 @@ function removeCompletionOnBlur() {
|
|
|
779
829
|
}
|
|
780
830
|
}
|
|
781
831
|
|
|
782
|
-
.ql-table-better svg path {
|
|
783
|
-
@apply dark:fill-darkPrimary;
|
|
784
|
-
@apply fill-lightPrimary;
|
|
785
|
-
}
|
|
786
|
-
|
|
787
832
|
.ql-table-select-container {
|
|
788
833
|
@apply dark:bg-darkForm;
|
|
789
834
|
@apply bg-lightForm;
|
package/dist/index.d.ts
CHANGED
|
@@ -14,6 +14,5 @@ export default class RichEditorPlugin extends AdminForthPlugin {
|
|
|
14
14
|
validateConfigAfterDiscover(adminforth: IAdminForth, resourceConfig: AdminForthResource): void;
|
|
15
15
|
instanceUniqueRepresentation(pluginOptions: any): string;
|
|
16
16
|
generateRecordContext(record: any, maxFields: number, inputFieldLimit: number, partsCount: number): string;
|
|
17
|
-
private parseBody;
|
|
18
17
|
setupEndpoints(server: IHttpServer): void;
|
|
19
18
|
}
|
package/dist/index.js
CHANGED
|
@@ -218,27 +218,14 @@ export default class RichEditorPlugin extends AdminForthPlugin {
|
|
|
218
218
|
});
|
|
219
219
|
return JSON.stringify(Object.fromEntries(fields));
|
|
220
220
|
}
|
|
221
|
-
parseBody(schema, body, response) {
|
|
222
|
-
const parsed = schema.safeParse(body !== null && body !== void 0 ? body : {});
|
|
223
|
-
if (!parsed.success) {
|
|
224
|
-
response.setStatus(400, '');
|
|
225
|
-
return {
|
|
226
|
-
ok: false,
|
|
227
|
-
error: { error: 'Request body validation failed', details: parsed.error.issues },
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
return { ok: true, data: parsed.data };
|
|
231
|
-
}
|
|
232
221
|
setupEndpoints(server) {
|
|
233
222
|
server.endpoint({
|
|
234
223
|
method: 'POST',
|
|
235
224
|
path: `/plugin/${this.pluginInstanceId}/doComplete`,
|
|
225
|
+
request_schema: doCompleteBodySchema,
|
|
236
226
|
handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, headers, response }) {
|
|
237
227
|
var _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
238
|
-
const
|
|
239
|
-
if ('error' in parsed)
|
|
240
|
-
return parsed.error;
|
|
241
|
-
const data = parsed.data;
|
|
228
|
+
const data = body;
|
|
242
229
|
const { record } = data;
|
|
243
230
|
if (!record) {
|
|
244
231
|
return { completion: [] };
|
package/index.ts
CHANGED
|
@@ -275,30 +275,13 @@ export default class RichEditorPlugin extends AdminForthPlugin {
|
|
|
275
275
|
return JSON.stringify(Object.fromEntries(fields));
|
|
276
276
|
}
|
|
277
277
|
|
|
278
|
-
private parseBody<T>(
|
|
279
|
-
schema: z.ZodType<T>,
|
|
280
|
-
body: unknown,
|
|
281
|
-
response: { setStatus: (code: number, message: string) => void },
|
|
282
|
-
): { ok: true; data: T } | { ok: false; error: { error: string; details: unknown } } {
|
|
283
|
-
const parsed = schema.safeParse(body ?? {});
|
|
284
|
-
if (!parsed.success) {
|
|
285
|
-
response.setStatus(400, '');
|
|
286
|
-
return {
|
|
287
|
-
ok: false,
|
|
288
|
-
error: { error: 'Request body validation failed', details: parsed.error.issues },
|
|
289
|
-
};
|
|
290
|
-
}
|
|
291
|
-
return { ok: true, data: parsed.data };
|
|
292
|
-
}
|
|
293
|
-
|
|
294
278
|
setupEndpoints(server: IHttpServer) {
|
|
295
279
|
server.endpoint({
|
|
296
280
|
method: 'POST',
|
|
297
281
|
path: `/plugin/${this.pluginInstanceId}/doComplete`,
|
|
282
|
+
request_schema: doCompleteBodySchema,
|
|
298
283
|
handler: async ({ body, headers, response }) => {
|
|
299
|
-
const
|
|
300
|
-
if ('error' in parsed) return parsed.error;
|
|
301
|
-
const data = parsed.data;
|
|
284
|
+
const data = body as z.infer<typeof doCompleteBodySchema>;
|
|
302
285
|
const { record } = data;
|
|
303
286
|
|
|
304
287
|
if (!record) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adminforth/rich-editor",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "Rich editor plugin for adminforth",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -27,11 +27,11 @@
|
|
|
27
27
|
"zod": "^4.3.6"
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
|
-
"adminforth": "^
|
|
30
|
+
"adminforth": "^4.4.0"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@types/node": "^22.10.7",
|
|
34
|
-
"adminforth": "^3.
|
|
34
|
+
"adminforth": "^3.8.2",
|
|
35
35
|
"semantic-release": "^24.2.1",
|
|
36
36
|
"semantic-release-slack-bot": "^4.0.2",
|
|
37
37
|
"typescript": "^5.7.3"
|