@bagelink/vue 0.0.978 → 0.0.982

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 (28) hide show
  1. package/dist/components/form/inputs/RichText/Toolbar.vue.d.ts +12 -0
  2. package/dist/components/form/inputs/RichText/Toolbar.vue.d.ts.map +1 -0
  3. package/dist/components/form/inputs/RichText/formatting.d.ts +11 -0
  4. package/dist/components/form/inputs/RichText/formatting.d.ts.map +1 -0
  5. package/dist/components/form/inputs/RichText/index.vue.d.ts +14 -0
  6. package/dist/components/form/inputs/RichText/index.vue.d.ts.map +1 -0
  7. package/dist/components/form/inputs/RichText/richtext-types.d.ts +3 -0
  8. package/dist/components/form/inputs/RichText/richtext-types.d.ts.map +1 -0
  9. package/dist/components/form/inputs/RichText2/Toolbar.vue.d.ts.map +1 -1
  10. package/dist/components/form/inputs/RichText2/index.vue.d.ts +0 -1
  11. package/dist/components/form/inputs/RichText2/index.vue.d.ts.map +1 -1
  12. package/dist/components/form/inputs/TelInput.vue.d.ts +2 -2
  13. package/dist/components/form/inputs/index.d.ts +1 -2
  14. package/dist/components/form/inputs/index.d.ts.map +1 -1
  15. package/dist/index.cjs +953 -27313
  16. package/dist/index.mjs +1044 -27404
  17. package/dist/plugins/bagel.d.ts +1 -1
  18. package/dist/plugins/bagel.d.ts.map +1 -1
  19. package/dist/style.css +53 -264
  20. package/package.json +1 -10
  21. package/src/components/form/inputs/{RichText2 → RichText}/Toolbar.vue +27 -20
  22. package/src/components/form/inputs/RichText/index.vue +253 -0
  23. package/src/components/form/inputs/index.ts +1 -2
  24. package/src/plugins/bagel.ts +1 -1
  25. package/src/components/form/inputs/RichText.vue +0 -420
  26. package/src/components/form/inputs/RichText2/index.vue +0 -301
  27. /package/src/components/form/inputs/{RichText2 → RichText}/formatting.ts +0 -0
  28. /package/src/components/form/inputs/{RichText2 → RichText}/richtext-types.ts +0 -0
@@ -8,7 +8,7 @@ export interface BagelOptions {
8
8
  availableLangs?: string[];
9
9
  defaultLang?: string;
10
10
  language?: string;
11
- host: string;
11
+ host?: string;
12
12
  onError?: (err: Error) => void;
13
13
  i18nT?: (key: string) => string;
14
14
  }
@@ -1 +1 @@
1
- {"version":3,"file":"bagel.d.ts","sourceRoot":"","sources":["../../src/plugins/bagel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,KAAK,CAAA;AAC/C,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAA;AAQrC,eAAO,MAAM,iBAAiB,EAAsB,YAAY,CAAC,KAAK,CAAC,CAAA;AACvE,eAAO,MAAM,iBAAiB,EAAsB,YAAY,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC,CAAA;AAEzF,wBAAgB,QAAQ,UAKvB;AAED,wBAAgB,QAAQ,UAT+C,MAAM,KAAK,MAAM,CAcvF;AAED,MAAM,WAAW,YAAY;IAC5B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAA;IACzB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IAEZ,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAA;IAE9B,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;CAC/B;AACD,eAAO,MAAM,QAAQ,EAAE,MAyBtB,CAAA"}
1
+ {"version":3,"file":"bagel.d.ts","sourceRoot":"","sources":["../../src/plugins/bagel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,KAAK,CAAA;AAC/C,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAA;AAQrC,eAAO,MAAM,iBAAiB,EAAsB,YAAY,CAAC,KAAK,CAAC,CAAA;AACvE,eAAO,MAAM,iBAAiB,EAAsB,YAAY,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC,CAAA;AAEzF,wBAAgB,QAAQ,UAKvB;AAED,wBAAgB,QAAQ,UAT+C,MAAM,KAAK,MAAM,CAcvF;AAED,MAAM,WAAW,YAAY;IAC5B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAA;IACzB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,MAAM,CAAA;IAEb,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAA;IAE9B,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;CAC/B;AACD,eAAO,MAAM,QAAQ,EAAE,MAyBtB,CAAA"}
package/dist/style.css CHANGED
@@ -1041,281 +1041,70 @@ data[data-v-6c27f163] {
1041
1041
  border-radius: 5px;
1042
1042
  }
1043
1043
 
1044
- /* Basic editor styles */
1045
- .tiptap > * + * {
1046
- margin-top: 0.75em;
1047
- }
1048
- .tiptap ul,
1049
- .tiptap ol {
1050
- padding: 0 1rem;
1051
- }
1052
- .tiptap h1,
1053
- .tiptap h2 {
1054
- line-height: 1;
1055
- }
1056
- .tiptap h3,
1057
- .tiptap h4,
1058
- .tiptap h5,
1059
- .tiptap h6 {
1060
- line-height: 1.1;
1061
- }
1062
- .tiptap pre {
1063
- background: #0d0d0d;
1064
- color: var(--bgl-light-text);
1065
- font-family: 'JetBrainsMono', monospace;
1066
- padding: 0.75rem 1rem;
1067
- border-radius: 0.5rem;
1068
- margin: 0;
1069
- }
1070
- .tiptap pre code {
1071
- color: inherit;
1072
- padding: 0;
1073
- background: none;
1074
- font-size: 0.8rem;
1075
- }
1076
- .tiptap img {
1077
- max-width: 100%;
1078
- height: auto;
1079
- }
1080
- .tiptap blockquote {
1081
- padding-inline-start: 0.5rem;
1082
- border-inline-start: 2px solid var(--border-color);
1083
- margin: 0.5rem
1084
- }
1085
- .tiptap hr {
1086
- border: none;
1087
- border-top: 2px solid var(--border-color);
1088
- margin: 1rem 0;
1089
- }
1090
- .tiptap {
1091
- min-height: 200px;
1092
- background: var(--input-bg);
1093
- overflow: auto;
1094
- max-height: 500px;
1095
- padding: 0.7rem;
1096
- color: var(--input-color);
1097
- border-radius: var(--input-border-radius);
1098
- font-size: var(--input-font-size);
1099
- line-height: 1.5;
1100
- }
1101
- .tiptap.ProseMirror-focused {
1102
- outline: none;
1103
- box-shadow: inset 0 0 10px #00000012;
1104
- }
1105
- .RichText {
1106
- margin-bottom: 0.5rem;
1107
- }
1108
- .RichText-tools {
1109
- background: var(--input-bg);
1110
- margin-bottom: 0.125rem;
1111
- border-radius: var(--input-border-radius);
1112
- }
1113
- .tiptap {
1114
- table {
1115
- border-collapse: collapse;
1116
- table-layout: fixed;
1117
- width: 100%;
1118
- margin: 0;
1119
- overflow: hidden;
1120
- td,
1121
- th {
1122
- min-width: 1em;
1123
- border: 2px solid var(--border-color);
1124
- padding: 3px 5px;
1125
- vertical-align: top;
1126
- box-sizing: border-box;
1127
- position: relative;
1128
- > * {
1129
- margin-bottom: 0;
1130
- margin-top: 0;
1131
- }
1132
- }
1133
- th {
1134
- font-weight: bold;
1135
- text-align: start;
1136
- background-color: var(--bgl-gray-80);
1137
- }
1138
- .selectedCell:after {
1139
- z-index: 2;
1140
- position: absolute;
1141
- content: '';
1142
- left: 0;
1143
- right: 0;
1144
- top: 0;
1145
- bottom: 0;
1146
- background: var(--bgl-gray-20);
1147
- pointer-events: none;
1148
- }
1149
- .selectedCell p::selection{
1150
- color: var(--bgl-popup-text)
1151
- }
1152
- .column-resize-handle {
1153
- position: absolute;
1154
- inset-inline-start: -2px;
1155
- top: 0;
1156
- bottom: -2px;
1157
- width: 4px;
1158
- background-color: #adf;
1159
- pointer-events: none;
1160
- }
1161
- p {
1162
- margin: 0;
1163
- }
1164
- }
1165
- }
1166
- .tableWrapper {
1167
- padding: 1rem 0;
1168
- overflow-x: auto;
1169
- }
1170
- .resize-cursor {
1171
- cursor: ew-resize;
1172
- cursor: col-resize;
1173
- }
1174
- .RichText-tools [disabled]{
1175
- color: var(--bgl-popup-text) !important;
1176
- opacity: 0.3;
1177
- }
1178
- .RichText iframe{
1179
- width: 100%;
1180
- max-width: 700px;
1181
- margin: 0 auto;
1182
- height: auto;
1183
- display: block;
1184
- border-radius: var(--input-border-radius);
1185
- border: none;
1186
- aspect-ratio: 16/9;
1187
- }
1188
- .RichText img{
1189
- width: 100%;
1190
- max-width: 700px;
1191
- margin: 0 auto;
1192
- height: auto;
1193
- display: block;
1194
- border-radius: var(--input-border-radius);
1044
+ .toolbar[data-v-0b9e3ede] .active {
1045
+ background: var(--primary-color);
1046
+ color: white;
1195
1047
  }
1196
1048
 
1197
- @font-face {
1198
- font-family: 'FontWithASyntaxHighlighter';
1199
- font-style: normal;
1200
- font-weight: 400;
1201
- font-stretch: 100%;
1202
- font-display: swap;
1203
- src: url(https://bagel.sfo2.cdn.digitaloceanspaces.com/Fonts/FontWithASyntaxHighlighter-Regular.woff2) format('woff2')
1204
- }
1205
- @font-palette-values --myCustomPalette {
1206
- font-family: 'FontWithASyntaxHighlighter';
1207
- override-colors:
1208
- 0 #6191c2, /* keywords, {} */
1209
- 1 #75975d, /* comments */
1210
- 2 yellow, /* literals */
1211
- 3 #a3c19a, /* numbers */
1212
- 4 lightblue, /* functions, [] */
1213
- 5 orange, /* js others */
1214
- 6 black, /* not in use */
1215
- 7 #bc8abd, /* inside quotes, css properties, few chars */
1216
- 8 #818181 /* few chars */
1217
- ;
1218
- }
1219
- .preview-area[data-v-cca783dc]{
1220
- font-family: "FontWithASyntaxHighlighter", monospace;
1221
- font-palette: --myCustomPalette;
1222
- background: var(--bgl-code-bg);
1223
- color: var(--bgl-code-color)
1224
- }
1225
- .rich-text-editor[data-v-cca783dc] {
1226
- background: var(--input-bg);
1227
- margin-bottom: 0.5rem;
1228
- width: 100%;
1049
+ .rich-text-editor[data-v-a9177fad] {
1050
+ background: var(--input-bg);
1051
+ border: 1px solid var(--border-color);
1052
+ transition: all 0.3s ease;
1229
1053
  }
1230
- .editableContent[data-v-cca783dc]{
1231
- min-height: 100%;
1232
- white-space: pre-wrap;
1233
- outline: none;
1234
- }
1235
- .content-area[data-v-cca783dc]{
1236
- background: var(--bgl-richtext-color);
1054
+ .editor-container[data-v-a9177fad] {
1055
+ display: flex;
1056
+ gap: 1rem;
1237
1057
  }
1238
- .content-area[data-v-cca783dc], .preview-area[data-v-cca783dc]{
1239
- min-height: 200px;
1058
+ .content-area[data-v-a9177fad], .preview-area[data-v-a9177fad] {
1059
+ flex: 1;
1060
+ min-height: 200px;
1061
+ background: var(--bgl-richtext-color);
1240
1062
  }
1241
- .fullscreen-mode[data-v-cca783dc] {
1242
- position: fixed;
1243
- top: 0;
1244
- left: 0;
1245
- width: 100%;
1246
- height: 100%;
1247
- z-index: 9999;
1248
- background: var(--input-bg);
1249
- padding: 2rem;
1250
- overflow: auto;
1251
- border-radius: 0;
1063
+ .split-view .content-area[data-v-a9177fad],
1064
+ .split-view .preview-area[data-v-a9177fad] {
1065
+ width: 50%;
1252
1066
  }
1253
- .fullscreen-mode .content-area[data-v-cca783dc]{
1254
- height: calc(100vh - 5rem);
1255
- padding: 4rem !important;
1256
- max-width: 900px;
1257
- margin-inline: auto ;
1258
- overflow-y: auto;
1067
+ .editableContent[data-v-a9177fad] {
1068
+ min-height: 100%;
1069
+ white-space: pre-wrap;
1070
+ outline: none;
1259
1071
  }
1260
- .fullscreen-mode .toolbar[data-v-cca783dc]{
1261
- max-width: 900px;
1262
- margin-inline: auto ;
1263
- /* border-bottom: 1px solid var(--border-color); */
1264
- /* margin-bottom: 2rem; */
1072
+ .html-editor[data-v-a9177fad] {
1073
+ width: 100%;
1074
+ height: 100%;
1075
+ min-height: 200px;
1076
+ font-family: monospace;
1077
+ background: transparent;
1078
+ border: none;
1079
+ outline: none;
1080
+ resize: none;
1265
1081
  }
1266
-
1267
- /* [contenteditable='true'] {
1268
- white-space: pre-wrap;
1269
- word-wrap: break-word;
1270
- outline: none;
1271
- height: 100%;
1272
- } */
1273
- /*
1274
-
1275
- .editor-container {
1276
- display: flex;
1277
- border: 1px solid #ccc;
1278
- border-radius: 4px;
1279
- overflow: hidden;
1082
+ .preview-area[data-v-a9177fad] {
1083
+ font-family: monospace;
1084
+ white-space: pre-wrap;
1085
+ overflow-x: auto;
1280
1086
  }
1281
-
1282
- .content-area,
1283
- .preview-area {
1284
- flex: 1;
1285
- min-height: 300px;
1286
- padding: 10px;
1087
+ .preview-area code[data-v-a9177fad] {
1088
+ display: block;
1089
+ padding: 1rem;
1090
+ }
1091
+ .fullscreen-mode[data-v-a9177fad] {
1092
+ position: fixed;
1093
+ top: 0;
1094
+ left: 0;
1095
+ width: 100vw;
1096
+ height: 100vh;
1097
+ z-index: 9999;
1098
+ padding: 2rem;
1099
+ }
1100
+ .fullscreen-mode .editor-container[data-v-a9177fad] {
1101
+ height: calc(100vh - 8rem);
1102
+ }
1103
+ .fullscreen-mode .content-area[data-v-a9177fad],
1104
+ .fullscreen-mode .preview-area[data-v-a9177fad] {
1105
+ height: 100%;
1287
1106
  overflow-y: auto;
1288
1107
  }
1289
-
1290
- .content-area:focus {
1291
- outline: none;
1292
- }
1293
-
1294
- .split-view .content-area,
1295
- .split-view .preview-area {
1296
- width: 50%;
1297
- }
1298
-
1299
- .preview-area {
1300
- background-color: #f9f9f9;
1301
- border-left: 1px solid #ccc;
1302
- } */
1303
-
1304
- /* @media (max-width: 768px) {
1305
- .split-view {
1306
- flex-direction: column;
1307
- }
1308
-
1309
- .split-view .content-area,
1310
- .split-view .preview-area {
1311
- width: 100%;
1312
- }
1313
-
1314
- .preview-area {
1315
- border-left: none;
1316
- border-top: 1px solid #ccc;
1317
- }
1318
- } */
1319
1108
  .resize-observer[data-v-b329ee4c]{position:absolute;top:0;left:0;z-index:-1;width:100%;height:100%;border:none;background-color:transparent;pointer-events:none;display:block;overflow:hidden;opacity:0}.resize-observer[data-v-b329ee4c] object{display:block;position:absolute;top:0;left:0;height:100%;width:100%;overflow:hidden;pointer-events:none;z-index:-1}.v-popper__popper{z-index:10000;top:0;left:0;outline:none}.v-popper__popper.v-popper__popper--hidden{visibility:hidden;opacity:0;transition:opacity .15s,visibility .15s;pointer-events:none}.v-popper__popper.v-popper__popper--shown{visibility:visible;opacity:1;transition:opacity .15s}.v-popper__popper.v-popper__popper--skip-transition,.v-popper__popper.v-popper__popper--skip-transition>.v-popper__wrapper{transition:none!important}.v-popper__backdrop{position:absolute;top:0;left:0;width:100%;height:100%;display:none}.v-popper__inner{position:relative;box-sizing:border-box;overflow-y:auto}.v-popper__inner>div{position:relative;z-index:1;max-width:inherit;max-height:inherit}.v-popper__arrow-container{position:absolute;width:10px;height:10px}.v-popper__popper--arrow-overflow .v-popper__arrow-container,.v-popper__popper--no-positioning .v-popper__arrow-container{display:none}.v-popper__arrow-inner,.v-popper__arrow-outer{border-style:solid;position:absolute;top:0;left:0;width:0;height:0}.v-popper__arrow-inner{visibility:hidden;border-width:7px}.v-popper__arrow-outer{border-width:6px}.v-popper__popper[data-popper-placement^=top] .v-popper__arrow-inner,.v-popper__popper[data-popper-placement^=bottom] .v-popper__arrow-inner{left:-2px}.v-popper__popper[data-popper-placement^=top] .v-popper__arrow-outer,.v-popper__popper[data-popper-placement^=bottom] .v-popper__arrow-outer{left:-1px}.v-popper__popper[data-popper-placement^=top] .v-popper__arrow-inner,.v-popper__popper[data-popper-placement^=top] .v-popper__arrow-outer{border-bottom-width:0;border-left-color:transparent!important;border-right-color:transparent!important;border-bottom-color:transparent!important}.v-popper__popper[data-popper-placement^=top] .v-popper__arrow-inner{top:-2px}.v-popper__popper[data-popper-placement^=bottom] .v-popper__arrow-container{top:0}.v-popper__popper[data-popper-placement^=bottom] .v-popper__arrow-inner,.v-popper__popper[data-popper-placement^=bottom] .v-popper__arrow-outer{border-top-width:0;border-left-color:transparent!important;border-right-color:transparent!important;border-top-color:transparent!important}.v-popper__popper[data-popper-placement^=bottom] .v-popper__arrow-inner{top:-4px}.v-popper__popper[data-popper-placement^=bottom] .v-popper__arrow-outer{top:-6px}.v-popper__popper[data-popper-placement^=left] .v-popper__arrow-inner,.v-popper__popper[data-popper-placement^=right] .v-popper__arrow-inner{top:-2px}.v-popper__popper[data-popper-placement^=left] .v-popper__arrow-outer,.v-popper__popper[data-popper-placement^=right] .v-popper__arrow-outer{top:-1px}.v-popper__popper[data-popper-placement^=right] .v-popper__arrow-inner,.v-popper__popper[data-popper-placement^=right] .v-popper__arrow-outer{border-left-width:0;border-left-color:transparent!important;border-top-color:transparent!important;border-bottom-color:transparent!important}.v-popper__popper[data-popper-placement^=right] .v-popper__arrow-inner{left:-4px}.v-popper__popper[data-popper-placement^=right] .v-popper__arrow-outer{left:-6px}.v-popper__popper[data-popper-placement^=left] .v-popper__arrow-container{right:-10px}.v-popper__popper[data-popper-placement^=left] .v-popper__arrow-inner,.v-popper__popper[data-popper-placement^=left] .v-popper__arrow-outer{border-right-width:0;border-top-color:transparent!important;border-right-color:transparent!important;border-bottom-color:transparent!important}.v-popper__popper[data-popper-placement^=left] .v-popper__arrow-inner{left:-2px}.v-popper--theme-tooltip .v-popper__inner{background:rgba(0,0,0,.8);color:#fff;border-radius:6px;padding:7px 12px 6px}.v-popper--theme-tooltip .v-popper__arrow-outer{border-color:#000c}.v-popper--theme-dropdown .v-popper__inner{background:#fff;color:#000;border-radius:6px;border:1px solid #ddd;box-shadow:0 6px 30px #0000001a}.v-popper--theme-dropdown .v-popper__arrow-inner{visibility:visible;border-color:#fff}.v-popper--theme-dropdown .v-popper__arrow-outer{border-color:#ddd}
1320
1109
 
1321
1110
  .selectinput[data-v-982f5078] {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bagelink/vue",
3
3
  "type": "module",
4
- "version": "0.0.978",
4
+ "version": "0.0.982",
5
5
  "description": "Bagel core sdk packages",
6
6
  "author": {
7
7
  "name": "Neveh Allon",
@@ -106,15 +106,6 @@
106
106
  "access": "public"
107
107
  },
108
108
  "dependencies": {
109
- "@tiptap/extension-image": "^2.10.3",
110
- "@tiptap/extension-table": "^2.10.3",
111
- "@tiptap/extension-table-cell": "^2.10.3",
112
- "@tiptap/extension-table-header": "^2.10.3",
113
- "@tiptap/extension-table-row": "^2.10.3",
114
- "@tiptap/extension-youtube": "^2.10.3",
115
- "@tiptap/pm": "^2.10.3",
116
- "@tiptap/starter-kit": "^2.10.3",
117
- "@tiptap/vue-3": "^2.10.3",
118
109
  "@vuepic/vue-datepicker": "^8.8.1",
119
110
  "@vueuse/core": "^12.0.0",
120
111
  "axios": "^1.7.9",
@@ -53,28 +53,35 @@ const toolbarOptions: toolbarOption[] = [
53
53
  </script>
54
54
 
55
55
  <template>
56
- <div class="toolbar flex gap-025 pb-05 flex-wrap" role="toolbar">
57
- <template v-for="(action, index) in toolbarOptions" :key="index">
58
- <Btn
59
- v-if="action.name !== 'separator' && config.includes(action.name)"
60
- v-tooltip="action.label"
61
- :icon="action.icon"
62
- thin
63
- flat
64
- :aria-label="action.name"
65
- :class="action.class"
66
- class="radius-05"
67
- @click="emit('action', action.name, action.label)"
68
- />
69
- <span
70
- v-else-if="action.name === 'separator'"
71
- :key="`separator-${index}`"
72
- class=" opacity-2 mb-025"
73
- >|</span>
74
- </template>
75
- </div>
56
+ <div class="toolbar flex gap-025 pb-05 flex-wrap" role="toolbar">
57
+ <template v-for="(action, index) in toolbarOptions" :key="index">
58
+ <Btn
59
+ v-if="action.name !== 'separator' && config.includes(action.name)"
60
+ v-tooltip="action.label"
61
+ :icon="action.icon"
62
+ thin
63
+ flat
64
+ :aria-label="action.name"
65
+ :class="[action.class, { active: selectedStyles?.has(action.name) }]"
66
+ class="radius-05"
67
+ @click="emit('action', action.name, action.label)"
68
+ />
69
+ <span
70
+ v-else-if="action.name === 'separator'"
71
+ :key="`separator-${index}`"
72
+ class="opacity-2 mb-025"
73
+ >|</span>
74
+ </template>
75
+ </div>
76
76
  </template>
77
77
 
78
+ <style scoped>
79
+ .toolbar :deep(.active) {
80
+ background: var(--primary-color);
81
+ color: white;
82
+ }
83
+ </style>
84
+
78
85
  <style scoped>
79
86
 
80
87
  </style>
@@ -0,0 +1,253 @@
1
+ <script lang="ts" setup>
2
+ import type { ToolbarConfig } from './richtext-types'
3
+ import { useModal } from '@bagelink/vue'
4
+ import { onMounted, watch, ref } from 'vue'
5
+ import { useFormatting } from './formatting'
6
+ import Toolbar from './Toolbar.vue'
7
+
8
+ const props = defineProps<{ modelValue: string, toolbarConfig?: ToolbarConfig }>()
9
+ const emit = defineEmits(['update:modelValue'])
10
+
11
+ const modal = useModal()
12
+ const { applyFormatting, clearFormatting } = useFormatting(modal)
13
+
14
+ const editableContent = ref<HTMLElement>()
15
+ const defaultConfig: ToolbarConfig = [
16
+ 'formatBlock',
17
+ 'bold',
18
+ 'italic',
19
+ 'underline',
20
+ 'fontSize',
21
+ 'fontFamily',
22
+ 'textColor',
23
+ 'backgroundColor',
24
+ 'alignLeft',
25
+ 'alignCenter',
26
+ 'alignRight',
27
+ 'alignJustify',
28
+ 'orderedList',
29
+ 'unorderedList',
30
+ 'indent',
31
+ 'outdent',
32
+ 'link',
33
+ 'image',
34
+ 'table',
35
+ 'splitView',
36
+ 'clear',
37
+ 'fullScreen'
38
+ ]
39
+
40
+ const config = ref<ToolbarConfig>(props.toolbarConfig || defaultConfig)
41
+ const contentHtml = ref(props.modelValue)
42
+ const isCodeView = ref(false)
43
+ const isSplitView = ref(false)
44
+ const selectedStyles = ref(new Set<string>())
45
+
46
+ function updateContent(source: 'html' | 'editor' = 'editor') {
47
+ if (source === 'editor' && !isCodeView.value) {
48
+ contentHtml.value = editableContent.value?.innerHTML ?? ''
49
+ }
50
+ emit('update:modelValue', contentHtml.value)
51
+ }
52
+
53
+ function handleHtmlInput(e: Event) {
54
+ const target = e.target as HTMLTextAreaElement
55
+ contentHtml.value = target.value
56
+ if (editableContent.value) {
57
+ editableContent.value.innerHTML = target.value
58
+ }
59
+ updateContent('html')
60
+ }
61
+
62
+ function updateToolbarHighlight() {
63
+ selectedStyles.value.clear()
64
+ const selection = window.getSelection()
65
+ if (!selection?.rangeCount) return
66
+
67
+ const range = selection.getRangeAt(0)
68
+ const element = range.commonAncestorContainer as HTMLElement
69
+ const parent = element.nodeType === 3 ? element.parentElement : element
70
+
71
+ if (!parent) return
72
+
73
+ if (parent.style.fontWeight === 'bold') selectedStyles.value.add('bold')
74
+ if (parent.style.fontStyle === 'italic') selectedStyles.value.add('italic')
75
+ if (parent.style.textDecoration === 'underline') selectedStyles.value.add('underline')
76
+ }
77
+
78
+ function handleToolbarAction(action: string, value?: string) {
79
+ if (!editableContent.value) return
80
+
81
+ if (['alignLeft', 'alignCenter', 'alignRight', 'alignJustify'].includes(action)) {
82
+ value = action.replace('align', '').toLowerCase()
83
+ }
84
+
85
+ switch (action) {
86
+ case 'splitView':
87
+ isSplitView.value = !isSplitView.value
88
+ break
89
+ case 'codeView':
90
+ isCodeView.value = !isCodeView.value
91
+ break
92
+ case 'fullScreen':
93
+ toggleFullScreen()
94
+ break
95
+ case 'clear':
96
+ clearFormatting()
97
+ break
98
+ default:
99
+ applyFormatting(action, value)
100
+ }
101
+ updateContent()
102
+ }
103
+
104
+ function toggleFullScreen() {
105
+ const editor = document.querySelector('.rich-text-editor')
106
+ editor?.classList.toggle('fullscreen-mode')
107
+ }
108
+
109
+ watch(() => props.modelValue, (newValue) => {
110
+ if (newValue !== contentHtml.value) {
111
+ contentHtml.value = newValue
112
+ if (editableContent.value) {
113
+ editableContent.value.innerHTML = newValue
114
+ }
115
+ }
116
+ })
117
+
118
+ onMounted(() => {
119
+ if (editableContent.value) {
120
+ editableContent.value.innerHTML = contentHtml.value
121
+ }
122
+ document.addEventListener('selectionchange', updateToolbarHighlight)
123
+ })
124
+
125
+ // Keyboard shortcuts
126
+ function handleKeyDown(event: KeyboardEvent) {
127
+ if (event.ctrlKey || event.metaKey) {
128
+ switch (event.key.toLowerCase()) {
129
+ case 'b':
130
+ event.preventDefault()
131
+ handleToolbarAction('bold')
132
+ break
133
+ case 'i':
134
+ event.preventDefault()
135
+ handleToolbarAction('italic')
136
+ break
137
+ case 'u':
138
+ event.preventDefault()
139
+ handleToolbarAction('underline')
140
+ break
141
+ }
142
+ }
143
+ }
144
+ </script>
145
+
146
+ <template>
147
+ <div class="rich-text-editor rounded pt-05 px-05 pb-1">
148
+ <Toolbar
149
+ :config="config"
150
+ :selected-styles="selectedStyles"
151
+ @action="handleToolbarAction"
152
+ />
153
+ <div class="editor-container" :class="{ 'split-view': isSplitView }">
154
+ <div class="content-area radius-05 p-1">
155
+ <textarea
156
+ v-if="isCodeView"
157
+ v-model="contentHtml"
158
+ class="html-editor"
159
+ @input="handleHtmlInput"
160
+ />
161
+ <div
162
+ v-else
163
+ ref="editableContent"
164
+ contenteditable="true"
165
+ class="editableContent"
166
+ role="textbox"
167
+ aria-multiline="true"
168
+ tabindex="0"
169
+ @input="updateContent()"
170
+ @keydown="handleKeyDown"
171
+ />
172
+ </div>
173
+ <div
174
+ v-if="isSplitView"
175
+ class="preview-area radius-05 p-1"
176
+ >
177
+ <pre><code v-text="contentHtml" /></pre>
178
+ </div>
179
+ </div>
180
+ </div>
181
+ </template>
182
+
183
+ <style scoped>
184
+ .rich-text-editor {
185
+ background: var(--input-bg);
186
+ border: 1px solid var(--border-color);
187
+ transition: all 0.3s ease;
188
+ }
189
+
190
+ .editor-container {
191
+ display: flex;
192
+ gap: 1rem;
193
+ }
194
+
195
+ .content-area, .preview-area {
196
+ flex: 1;
197
+ min-height: 200px;
198
+ background: var(--bgl-richtext-color);
199
+ }
200
+
201
+ .split-view .content-area,
202
+ .split-view .preview-area {
203
+ width: 50%;
204
+ }
205
+
206
+ .editableContent {
207
+ min-height: 100%;
208
+ white-space: pre-wrap;
209
+ outline: none;
210
+ }
211
+
212
+ .html-editor {
213
+ width: 100%;
214
+ height: 100%;
215
+ min-height: 200px;
216
+ font-family: monospace;
217
+ background: transparent;
218
+ border: none;
219
+ outline: none;
220
+ resize: none;
221
+ }
222
+
223
+ .preview-area {
224
+ font-family: monospace;
225
+ white-space: pre-wrap;
226
+ overflow-x: auto;
227
+ }
228
+
229
+ .preview-area code {
230
+ display: block;
231
+ padding: 1rem;
232
+ }
233
+
234
+ .fullscreen-mode {
235
+ position: fixed;
236
+ top: 0;
237
+ left: 0;
238
+ width: 100vw;
239
+ height: 100vh;
240
+ z-index: 9999;
241
+ padding: 2rem;
242
+ }
243
+
244
+ .fullscreen-mode .editor-container {
245
+ height: calc(100vh - 8rem);
246
+ }
247
+
248
+ .fullscreen-mode .content-area,
249
+ .fullscreen-mode .preview-area {
250
+ height: 100%;
251
+ overflow-y: auto;
252
+ }
253
+ </style>
@@ -10,8 +10,7 @@ export { default as PasswordInput } from './PasswordInput.vue'
10
10
  export { default as RadioGroup } from './RadioGroup.vue'
11
11
  export { default as RadioPillsInput } from './RadioPillsInput.vue'
12
12
  export { default as RangeInput } from './RangeInput.vue'
13
- export { default as RichText } from './RichText.vue'
14
- export { default as RichText2 } from './RichText2/index.vue'
13
+ export { default as RichText } from './RichText/index.vue'
15
14
  export { default as SelectInput } from './SelectInput.vue'
16
15
  export { default as SignaturePad } from './SignaturePad.vue'
17
16
  export { default as TableField } from './TableField.vue'