@hywax/cms 0.0.6 → 0.0.7
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/.nuxt/cms/editor/uplora-image.ts +1 -1
- package/.nuxt/cms/editor-content-full.ts +6 -0
- package/.nuxt/cms/editor-content-light.ts +6 -0
- package/.nuxt/cms/index.ts +3 -0
- package/.nuxt/cms/input-uplora-image.ts +15 -0
- package/cli/templates.mjs +6 -6
- package/dist/module.d.mts +1 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +139 -54
- package/dist/runtime/components/ButtonDelete.vue +3 -2
- package/dist/runtime/components/ButtonDelete.vue.d.ts +1 -0
- package/dist/runtime/components/EditorContentFull.vue +53 -0
- package/dist/runtime/components/EditorContentFull.vue.d.ts +20 -0
- package/dist/runtime/components/EditorContentLight.vue +46 -0
- package/dist/runtime/components/EditorContentLight.vue.d.ts +20 -0
- package/dist/runtime/components/InputUploraImage.vue +131 -0
- package/dist/runtime/components/InputUploraImage.vue.d.ts +35 -0
- package/dist/runtime/components/ModalConfirm.vue +2 -1
- package/dist/runtime/components/TablePanel.vue +1 -1
- package/dist/runtime/components/TablePanelColumnSorting.vue +1 -1
- package/dist/runtime/components/UploraImage.vue +2 -2
- package/dist/runtime/components/UploraImage.vue.d.ts +1 -1
- package/dist/runtime/components/prose/UploraImage.vue +1 -1
- package/dist/runtime/composables/useAsyncHandler.d.ts +12 -0
- package/dist/runtime/composables/useAsyncHandler.js +30 -0
- package/dist/runtime/composables/useUplora.d.ts +20 -0
- package/dist/runtime/composables/useUplora.js +52 -0
- package/dist/runtime/editor/components/BlockMenu.vue +43 -0
- package/dist/runtime/editor/components/BlockMenu.vue.d.ts +2 -0
- package/dist/runtime/editor/components/SlashCommand.vue +92 -0
- package/dist/runtime/editor/components/SlashCommand.vue.d.ts +7 -0
- package/dist/runtime/editor/components/TooltipLink.vue +81 -0
- package/dist/runtime/editor/components/TooltipLink.vue.d.ts +9 -0
- package/dist/runtime/editor/components/TooltipMenu.vue +75 -0
- package/dist/runtime/editor/components/TooltipMenu.vue.d.ts +9 -0
- package/dist/runtime/editor/extensions/callout/CalloutView.vue +7 -1
- package/dist/runtime/editor/extensions/uplora-image/UploraImageView.vue +7 -4
- package/dist/runtime/index.css +1 -1
- package/dist/runtime/server/api/uplora/[id].delete.d.ts +1 -1
- package/dist/runtime/server/api/uplora/[id].delete.js +1 -1
- package/dist/runtime/server/api/uplora/index.post.d.ts +4 -1
- package/dist/runtime/server/api/uplora/index.post.js +6 -2
- package/dist/runtime/types/index.d.ts +4 -0
- package/dist/runtime/types/index.js +3 -0
- package/dist/runtime/types/tv.d.ts +2 -2
- package/package.json +1 -1
package/.nuxt/cms/index.ts
CHANGED
|
@@ -2,11 +2,14 @@ export { default as autocompleteSelect } from './autocomplete-select'
|
|
|
2
2
|
export { default as buttonClear } from './button-clear'
|
|
3
3
|
export { default as buttonCopy } from './button-copy'
|
|
4
4
|
export { default as buttonDelete } from './button-delete'
|
|
5
|
+
export { default as editorContentFull } from './editor-content-full'
|
|
6
|
+
export { default as editorContentLight } from './editor-content-light'
|
|
5
7
|
export { default as formPanel } from './form-panel'
|
|
6
8
|
export { default as formPanelAsideSection } from './form-panel-aside-section'
|
|
7
9
|
export { default as formPanelSection } from './form-panel-section'
|
|
8
10
|
export { default as inputSeo } from './input-seo'
|
|
9
11
|
export { default as inputSlug } from './input-slug'
|
|
12
|
+
export { default as inputUploraImage } from './input-uplora-image'
|
|
10
13
|
export { default as modalConfirm } from './modal-confirm'
|
|
11
14
|
export { default as tableCellPreview } from './table-cell-preview'
|
|
12
15
|
export { default as tableCellSeo } from './table-cell-seo'
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
"slots": {
|
|
3
|
+
"root": "relative w-full rounded-md overflow-hidden border border-default aspect-3/2 text-sm",
|
|
4
|
+
"image": "",
|
|
5
|
+
"imageActions": "absolute top-4 right-4 flex gap-2",
|
|
6
|
+
"uploader": "flex flex-col items-center justify-center w-full h-full p-4",
|
|
7
|
+
"uploaderPendingIcon": "size-6 animate-spin",
|
|
8
|
+
"uploaderIdleButton": "cursor-pointer bg-muted/50 border border-default border-dashed rounded-md h-full w-full",
|
|
9
|
+
"uploaderIdleIcon": "size-10 text-primary",
|
|
10
|
+
"uploaderIdleText": "mt-2 font-medium",
|
|
11
|
+
"uploaderIdleExtensions": "mt-2 text-xs text-muted uppercase",
|
|
12
|
+
"uploaderErrorText": "text-center font-medium",
|
|
13
|
+
"uploaderErrorActions": "flex gap-2 mt-4"
|
|
14
|
+
}
|
|
15
|
+
}
|
package/cli/templates.mjs
CHANGED
|
@@ -69,13 +69,13 @@ import ComponentRender from '../../component-render'
|
|
|
69
69
|
describe('${upperName}.vue', () => {
|
|
70
70
|
it.each([
|
|
71
71
|
// Props
|
|
72
|
-
['
|
|
73
|
-
['
|
|
74
|
-
['
|
|
75
|
-
['
|
|
72
|
+
['base component', { props: {} }],
|
|
73
|
+
['with as', { props: { as: 'section' } }],
|
|
74
|
+
['with class', { props: { class: '' } }],
|
|
75
|
+
['with ui', { props: { ui: {} } }],
|
|
76
76
|
// Slots
|
|
77
|
-
['
|
|
78
|
-
])('
|
|
77
|
+
['with default slot', { slots: { default: () => 'Default slot' } }],
|
|
78
|
+
])('renders %s correctly', async (nameOrHtml: string, options: { props?: ${upperName}Props, slots?: Partial<${upperName}Slots> }) => {
|
|
79
79
|
const html = await ComponentRender(nameOrHtml, options, ${upperName})
|
|
80
80
|
expect(html).toMatchSnapshot()
|
|
81
81
|
})
|
package/dist/module.d.mts
CHANGED
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { createResolver, addComponentsDir, addImportsDir, addPlugin, addImports, addServerImports, addServerImportsDir, addServerHandler, addTypeTemplate, addTemplate, addServerTemplate, defineNuxtModule
|
|
2
|
-
import { defu } from 'defu';
|
|
1
|
+
import { createResolver, addComponentsDir, addImportsDir, addPlugin, addImports, addServerImports, addServerImportsDir, hasNuxtModule, installModule, addServerHandler, addTypeTemplate, addTemplate, addServerTemplate, defineNuxtModule } from '@nuxt/kit';
|
|
3
2
|
import { fileURLToPath } from 'node:url';
|
|
4
|
-
import { dirname } from 'pathe';
|
|
3
|
+
import { dirname, join } from 'pathe';
|
|
4
|
+
import { defu } from 'defu';
|
|
5
5
|
import { snakeCase, kebabCase } from 'scule';
|
|
6
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
6
7
|
|
|
7
8
|
const name = "@hywax/cms";
|
|
8
|
-
const version = "0.0.
|
|
9
|
+
const version = "0.0.7";
|
|
9
10
|
|
|
10
11
|
function createContext(options, nuxt) {
|
|
11
12
|
const { resolve } = createResolver(import.meta.url);
|
|
@@ -52,7 +53,7 @@ function prepareAutoImports({ resolve, options, nuxt }) {
|
|
|
52
53
|
path: resolve("./runtime/components"),
|
|
53
54
|
pathPrefix: false,
|
|
54
55
|
prefix: options?.prefix,
|
|
55
|
-
ignore: ["prose/**"
|
|
56
|
+
ignore: ["prose/**"]
|
|
56
57
|
});
|
|
57
58
|
addComponentsDir({
|
|
58
59
|
path: resolve("./runtime/components/prose"),
|
|
@@ -75,6 +76,102 @@ function prepareAutoImports({ resolve, options, nuxt }) {
|
|
|
75
76
|
addServerImportsDir(resolve("./runtime/server/utils"));
|
|
76
77
|
nuxt.options.nitro.alias ||= {};
|
|
77
78
|
nuxt.options.nitro.alias["#cms/http-codes"] = httpCodesPath;
|
|
79
|
+
nuxt.options.alias["#cms"] = resolve("./runtime");
|
|
80
|
+
nuxt.options.appConfig.cms = defu(nuxt.options.appConfig.cms || {}, defaultCMSConfig);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function prepareGenerateEnv({ nuxt }) {
|
|
84
|
+
const runtimeConfig = nuxt.options.runtimeConfig;
|
|
85
|
+
if (nuxt.options.dev && !runtimeConfig.uploraApiKey) {
|
|
86
|
+
const envPath = join(nuxt.options.rootDir, ".env");
|
|
87
|
+
const envContent = await readFile(envPath, "utf-8").catch(() => "");
|
|
88
|
+
const envKey = `${runtimeConfig.nitro?.envPrefix || "NUXT_"}UPLORA_API_KEY`;
|
|
89
|
+
if (!envContent.includes(envKey)) {
|
|
90
|
+
await writeFile(
|
|
91
|
+
envPath,
|
|
92
|
+
`${envContent ? `${envContent}
|
|
93
|
+
` : envContent}${envKey}=put-your-api-key-here`,
|
|
94
|
+
"utf-8"
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async function prepareInstallModules(_context) {
|
|
101
|
+
if (!hasNuxtModule("@nuxt/ui-pro")) {
|
|
102
|
+
await installModule("@nuxt/ui-pro");
|
|
103
|
+
}
|
|
104
|
+
if (!hasNuxtModule("@nuxtjs/mdc")) {
|
|
105
|
+
await installModule("@nuxtjs/mdc");
|
|
106
|
+
}
|
|
107
|
+
if (!hasNuxtModule("@vueuse/nuxt")) {
|
|
108
|
+
await installModule("@vueuse/nuxt");
|
|
109
|
+
}
|
|
110
|
+
if (!hasNuxtModule("nuxt-auth-utils")) {
|
|
111
|
+
await installModule("nuxt-auth-utils");
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const icons = {
|
|
116
|
+
edit: "lucide:pencil",
|
|
117
|
+
trash: "lucide:trash-2",
|
|
118
|
+
imageUp: "lucide:image-up",
|
|
119
|
+
arrowDownUp: "lucide:arrow-down-up",
|
|
120
|
+
sortAsc: "lucide:arrow-up-wide-narrow",
|
|
121
|
+
sortDesc: "lucide:arrow-down-wide-narrow",
|
|
122
|
+
columns: "lucide:columns-3-cog",
|
|
123
|
+
filter: "lucide:filter",
|
|
124
|
+
link: "lucide:link-2",
|
|
125
|
+
linkOff: "lucide:link-2-off",
|
|
126
|
+
ellipsisVertical: "lucide:ellipsis-vertical",
|
|
127
|
+
heading2: "lucide:heading-2",
|
|
128
|
+
heading3: "lucide:heading-3",
|
|
129
|
+
heading4: "lucide:heading-4",
|
|
130
|
+
list: "lucide:list",
|
|
131
|
+
listOrdered: "lucide:list-ordered",
|
|
132
|
+
checkSquare: "lucide:check-square",
|
|
133
|
+
image: "lucide:image",
|
|
134
|
+
quote: "lucide:quote",
|
|
135
|
+
code: "lucide:code",
|
|
136
|
+
paragraph: "lucide:paragraph",
|
|
137
|
+
pilcrow: "lucide:pilcrow",
|
|
138
|
+
info: "lucide:info",
|
|
139
|
+
bold: "lucide:bold",
|
|
140
|
+
italic: "lucide:italic",
|
|
141
|
+
underline: "lucide:underline",
|
|
142
|
+
strike: "lucide:strikethrough"
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
function prepareMergeConfigs({ nuxt, options }) {
|
|
146
|
+
nuxt.options.runtimeConfig.public = defu(nuxt.options.runtimeConfig.public || {}, {
|
|
147
|
+
fluxorUrl: options.fluxorUrl,
|
|
148
|
+
version: ""
|
|
149
|
+
});
|
|
150
|
+
nuxt.options.runtimeConfig.nitro ||= {};
|
|
151
|
+
nuxt.options.runtimeConfig.nitro.envPrefix = options.envPrefix;
|
|
152
|
+
nuxt.options.appConfig.ui = defu(nuxt.options.appConfig.ui || {}, {
|
|
153
|
+
icons
|
|
154
|
+
});
|
|
155
|
+
nuxt.options.ui = defu(nuxt.options.ui || {}, {
|
|
156
|
+
colorMode: true,
|
|
157
|
+
fonts: true
|
|
158
|
+
});
|
|
159
|
+
nuxt.options.colorMode = defu(nuxt.options.colorMode || {}, {
|
|
160
|
+
storageKey: `${options.name}-color-mode`
|
|
161
|
+
});
|
|
162
|
+
nuxt.options.mdc = defu(nuxt.options.mdc || {}, {
|
|
163
|
+
components: {
|
|
164
|
+
map: {
|
|
165
|
+
"uplora-image": "ProseUploraImage"
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
nuxt.options.auth = defu(nuxt.options.auth || {}, {
|
|
170
|
+
name: `${options.name}-session`
|
|
171
|
+
});
|
|
172
|
+
nuxt.options.runtimeConfig.uplora = defu(nuxt.options.runtimeConfig.uplora || {}, {
|
|
173
|
+
apiKey: ""
|
|
174
|
+
});
|
|
78
175
|
}
|
|
79
176
|
|
|
80
177
|
function prepareServerRoutes({ resolve }) {
|
|
@@ -114,6 +211,20 @@ const buttonDelete = {
|
|
|
114
211
|
}
|
|
115
212
|
};
|
|
116
213
|
|
|
214
|
+
const editorContentFull = {
|
|
215
|
+
slots: {
|
|
216
|
+
root: "h-full w-full relative",
|
|
217
|
+
editor: "min-h-full w-full focus:outline-none editor-content-full relative"
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
const editorContentLight = {
|
|
222
|
+
slots: {
|
|
223
|
+
root: "relative",
|
|
224
|
+
editor: "w-full focus:outline-none editor-content-light relative"
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
|
|
117
228
|
const formPanel = {
|
|
118
229
|
slots: {
|
|
119
230
|
root: "",
|
|
@@ -161,6 +272,22 @@ const inputSlug = {
|
|
|
161
272
|
}
|
|
162
273
|
};
|
|
163
274
|
|
|
275
|
+
const inputUploraImage = {
|
|
276
|
+
slots: {
|
|
277
|
+
root: "relative w-full rounded-md overflow-hidden border border-default aspect-3/2 text-sm",
|
|
278
|
+
image: "",
|
|
279
|
+
imageActions: "absolute top-4 right-4 flex gap-2",
|
|
280
|
+
uploader: "flex flex-col items-center justify-center w-full h-full p-4",
|
|
281
|
+
uploaderPendingIcon: "size-6 animate-spin",
|
|
282
|
+
uploaderIdleButton: "cursor-pointer bg-muted/50 border border-default border-dashed rounded-md h-full w-full",
|
|
283
|
+
uploaderIdleIcon: "size-10 text-primary",
|
|
284
|
+
uploaderIdleText: "mt-2 font-medium",
|
|
285
|
+
uploaderIdleExtensions: "mt-2 text-xs text-muted uppercase",
|
|
286
|
+
uploaderErrorText: "text-center font-medium",
|
|
287
|
+
uploaderErrorActions: "flex gap-2 mt-4"
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
|
|
164
291
|
const modalConfirm = {
|
|
165
292
|
slots: {
|
|
166
293
|
root: ""
|
|
@@ -231,11 +358,14 @@ const theme = {
|
|
|
231
358
|
buttonClear: buttonClear,
|
|
232
359
|
buttonCopy: buttonCopy,
|
|
233
360
|
buttonDelete: buttonDelete,
|
|
361
|
+
editorContentFull: editorContentFull,
|
|
362
|
+
editorContentLight: editorContentLight,
|
|
234
363
|
formPanel: formPanel,
|
|
235
364
|
formPanelAsideSection: formPanelAsideSection,
|
|
236
365
|
formPanelSection: formPanelSection,
|
|
237
366
|
inputSeo: inputSeo,
|
|
238
367
|
inputSlug: inputSlug,
|
|
368
|
+
inputUploraImage: inputUploraImage,
|
|
239
369
|
modalConfirm: modalConfirm,
|
|
240
370
|
tableCellPreview: tableCellPreview,
|
|
241
371
|
tableCellSeo: tableCellSeo,
|
|
@@ -271,7 +401,7 @@ const callout = {
|
|
|
271
401
|
|
|
272
402
|
const uploraImage$1 = {
|
|
273
403
|
slots: {
|
|
274
|
-
root: "
|
|
404
|
+
root: ""
|
|
275
405
|
}
|
|
276
406
|
};
|
|
277
407
|
|
|
@@ -410,14 +540,6 @@ function prepareTemplates(context) {
|
|
|
410
540
|
}
|
|
411
541
|
}
|
|
412
542
|
|
|
413
|
-
const icons = {
|
|
414
|
-
sortAsc: "lucide:arrow-up-wide-narrow",
|
|
415
|
-
sortDesc: "lucide:arrow-down-wide-narrow",
|
|
416
|
-
columns: "lucide:columns-3-cog",
|
|
417
|
-
filter: "lucide:filter",
|
|
418
|
-
link: "lucide:link-2"
|
|
419
|
-
};
|
|
420
|
-
|
|
421
543
|
const module = defineNuxtModule({
|
|
422
544
|
meta: {
|
|
423
545
|
name,
|
|
@@ -427,49 +549,12 @@ const module = defineNuxtModule({
|
|
|
427
549
|
defaults: defaultModuleOptions,
|
|
428
550
|
async setup(options, nuxt) {
|
|
429
551
|
const context = createContext(options, nuxt);
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
version: ""
|
|
433
|
-
});
|
|
434
|
-
nuxt.options.runtimeConfig.nitro ||= {};
|
|
435
|
-
nuxt.options.runtimeConfig.nitro.envPrefix = options.envPrefix;
|
|
436
|
-
nuxt.options.appConfig.ui = defu(nuxt.options.appConfig.ui || {}, {
|
|
437
|
-
icons
|
|
438
|
-
});
|
|
439
|
-
nuxt.options.ui = defu(nuxt.options.ui || {}, {
|
|
440
|
-
colorMode: true,
|
|
441
|
-
fonts: true
|
|
442
|
-
});
|
|
443
|
-
nuxt.options.colorMode = defu(nuxt.options.colorMode || {}, {
|
|
444
|
-
storageKey: `${options.name}-color-mode`
|
|
445
|
-
});
|
|
446
|
-
nuxt.options.mdc = defu(nuxt.options.mdc || {}, {
|
|
447
|
-
components: {
|
|
448
|
-
map: {
|
|
449
|
-
"uplora-image": "ProseUploraImage"
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
});
|
|
453
|
-
nuxt.options.auth = defu(nuxt.options.auth || {}, {
|
|
454
|
-
name: `${options.name}-session`
|
|
455
|
-
});
|
|
456
|
-
if (!hasNuxtModule("@nuxt/ui-pro")) {
|
|
457
|
-
await installModule("@nuxt/ui-pro");
|
|
458
|
-
}
|
|
459
|
-
if (!hasNuxtModule("@nuxtjs/mdc")) {
|
|
460
|
-
await installModule("@nuxtjs/mdc");
|
|
461
|
-
}
|
|
462
|
-
if (!hasNuxtModule("@vueuse/nuxt")) {
|
|
463
|
-
await installModule("@vueuse/nuxt");
|
|
464
|
-
}
|
|
465
|
-
if (!hasNuxtModule("nuxt-auth-utils")) {
|
|
466
|
-
await installModule("nuxt-auth-utils");
|
|
467
|
-
}
|
|
468
|
-
nuxt.options.alias["#cms"] = context.resolve("./runtime");
|
|
469
|
-
nuxt.options.appConfig.cms = defu(nuxt.options.appConfig.cms || {}, defaultCMSConfig);
|
|
552
|
+
prepareMergeConfigs(context);
|
|
553
|
+
await prepareInstallModules();
|
|
470
554
|
prepareAutoImports(context);
|
|
471
555
|
prepareTemplates(context);
|
|
472
556
|
prepareServerRoutes(context);
|
|
557
|
+
await prepareGenerateEnv(context);
|
|
473
558
|
}
|
|
474
559
|
});
|
|
475
560
|
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
:color="color"
|
|
11
11
|
:variant="variant"
|
|
12
12
|
:size="size"
|
|
13
|
+
:icon="icon"
|
|
13
14
|
:class="ui.root({ class: [props.ui?.root, props.class] })"
|
|
14
15
|
auto-loading
|
|
15
16
|
@click="handleDeleteClick()"
|
|
@@ -34,6 +35,7 @@ const props = defineProps({
|
|
|
34
35
|
tooltipText: { type: String, required: false },
|
|
35
36
|
color: { type: null, required: false, default: "error" },
|
|
36
37
|
variant: { type: null, required: false },
|
|
38
|
+
icon: { type: null, required: false },
|
|
37
39
|
size: { type: null, required: false },
|
|
38
40
|
class: { type: null, required: false },
|
|
39
41
|
ui: { type: null, required: false },
|
|
@@ -47,9 +49,8 @@ function handleDeleteClick() {
|
|
|
47
49
|
...props.modalProps,
|
|
48
50
|
title: props.title ?? "\u0423\u0434\u0430\u043B\u0435\u043D\u0438\u0435",
|
|
49
51
|
message: props.message ?? "\u0412\u044B \u0434\u0435\u0438\u0306\u0441\u0442\u0432\u0438\u0442\u0435\u043B\u044C\u043D\u043E \u0445\u043E\u0442\u0438\u0442\u0435 \u0443\u0434\u0430\u043B\u0438\u0442\u044C?",
|
|
50
|
-
color:
|
|
52
|
+
color: "error",
|
|
51
53
|
variant: props.variant,
|
|
52
|
-
size: props.size,
|
|
53
54
|
confirmLabel: props.confirmLabel,
|
|
54
55
|
confirmText: props.confirmText,
|
|
55
56
|
onConfirm: props.onConfirm
|
|
@@ -15,6 +15,7 @@ export interface ButtonDeleteProps extends Pick<ModalConfirmProps, 'onConfirm'>
|
|
|
15
15
|
tooltipText?: string;
|
|
16
16
|
color?: ButtonProps['color'];
|
|
17
17
|
variant?: ButtonProps['variant'];
|
|
18
|
+
icon?: ButtonProps['icon'];
|
|
18
19
|
size?: ButtonProps['size'];
|
|
19
20
|
class?: any;
|
|
20
21
|
ui?: ButtonDelete['slots'];
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Primitive :as="as" :class="ui.root({ class: [props.ui?.root, props.class] })">
|
|
3
|
+
<ProseKit :editor="editor">
|
|
4
|
+
<div ref="editorRef" :class="ui.editor({ class: [props.ui?.editor, 'pl-14'] })" />
|
|
5
|
+
|
|
6
|
+
<BlockMenu />
|
|
7
|
+
<SlashCommand />
|
|
8
|
+
<TooltipMenu />
|
|
9
|
+
</ProseKit>
|
|
10
|
+
</Primitive>
|
|
11
|
+
</template>
|
|
12
|
+
|
|
13
|
+
<script>
|
|
14
|
+
import theme from "#build/cms/editor-content-full";
|
|
15
|
+
import { computed, useAppConfig, useTemplateRef, watchPostEffect } from "#imports";
|
|
16
|
+
import { createEditor } from "prosekit/core";
|
|
17
|
+
import { ProseKit, useDocChange } from "prosekit/vue";
|
|
18
|
+
import { Primitive } from "reka-ui";
|
|
19
|
+
import BlockMenu from "../editor/components/BlockMenu.vue";
|
|
20
|
+
import SlashCommand from "../editor/components/SlashCommand.vue";
|
|
21
|
+
import TooltipMenu from "../editor/components/TooltipMenu.vue";
|
|
22
|
+
import { defineFullExtension } from "../editor/extensions";
|
|
23
|
+
import { docToMarkdown, markdownToDoc } from "../editor/markdown";
|
|
24
|
+
import { tv } from "../utils/tv";
|
|
25
|
+
import "prosekit/basic/style.css";
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<script setup>
|
|
29
|
+
const props = defineProps({
|
|
30
|
+
as: { type: null, required: false },
|
|
31
|
+
class: { type: null, required: false },
|
|
32
|
+
ui: { type: null, required: false }
|
|
33
|
+
});
|
|
34
|
+
const modelValue = defineModel({ type: String, ...{ default: "" } });
|
|
35
|
+
const appConfig = useAppConfig();
|
|
36
|
+
const editor = createEditor({
|
|
37
|
+
extension: defineFullExtension(),
|
|
38
|
+
defaultContent: await markdownToDoc(modelValue.value)
|
|
39
|
+
});
|
|
40
|
+
const editorRef = useTemplateRef("editorRef");
|
|
41
|
+
useDocChange(async () => {
|
|
42
|
+
modelValue.value = await docToMarkdown(editor.getDocJSON());
|
|
43
|
+
}, { editor });
|
|
44
|
+
watchPostEffect((onCleanup) => {
|
|
45
|
+
editor.mount(editorRef.value);
|
|
46
|
+
onCleanup(() => editor.unmount());
|
|
47
|
+
});
|
|
48
|
+
const ui = computed(() => tv({ extend: tv(theme), ...appConfig.cms?.editorContentFull || {} })());
|
|
49
|
+
</script>
|
|
50
|
+
|
|
51
|
+
<style scoped>
|
|
52
|
+
::v-deep(.editor-content-full){line-height:calc(var(--spacing)*7)}::v-deep(.editor-content-full) [data-placeholder]:before{color:var(--ui-text-dimmed);content:attr(data-placeholder);height:0;pointer-events:none;position:absolute}::v-deep(.editor-content-full) h2,::v-deep(.editor-content-full) h3,::v-deep(.editor-content-full) h4{font-weight:600;margin-block:calc(var(--spacing)*5)}::v-deep(.editor-content-full) h2{font-size:var(--text-2xl)}::v-deep(.editor-content-full) h3{font-size:var(--text-xl)}::v-deep(.editor-content-full) h4{font-size:var(--text-lg)}::v-deep(.editor-content-full) blockquote{border-left:4px solid var(--ui-border-accented);margin-block:calc(var(--spacing)*4);padding-inline-start:calc(var(--spacing)*4)}::v-deep(.editor-content-full) [data-node-view-root=true],::v-deep(.editor-content-full)>p{margin-block:calc(var(--spacing)*5)}::v-deep(.editor-content-full) hr{border-block-start:1px solid var(--ui-border);margin-block:calc(var(--spacing)*5)}::v-deep(.editor-content-full) pre{background-color:var(--ui-bg-muted);border:1px solid var(--ui-border-muted);border-radius:calc(var(--ui-radius)*1.5);font-family:var(--font-mono);font-size:var(--text-sm);line-height:calc(var(--spacing)*6);overflow-x:auto;padding-block:calc(var(--spacing)*3);padding-inline:calc(var(--spacing)*4)}::v-deep(.editor-content-full) p>code{background-color:var(--ui-bg-muted);border:1px solid var(--ui-border-muted);border-radius:calc(var(--ui-radius)*1.5);font-family:var(--font-mono);font-size:var(--text-sm);padding-inline:var(--spacing)}::v-deep(.editor-content-full) a{color:var(--ui-primary);cursor:pointer;text-decoration:underline;-webkit-user-select:all;-moz-user-select:all;user-select:all}
|
|
53
|
+
</style>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { AppConfig } from '@nuxt/schema';
|
|
2
|
+
import type { ComponentConfig } from '../types';
|
|
3
|
+
import theme from '#build/cms/editor-content-full';
|
|
4
|
+
import 'prosekit/basic/style.css';
|
|
5
|
+
type EditorContentFull = ComponentConfig<typeof theme, AppConfig, 'editorContentFull'>;
|
|
6
|
+
export interface EditorContentFullProps {
|
|
7
|
+
as?: any;
|
|
8
|
+
class?: any;
|
|
9
|
+
ui?: EditorContentFull['slots'];
|
|
10
|
+
}
|
|
11
|
+
declare const _default: import("vue").DefineComponent<EditorContentFullProps & {
|
|
12
|
+
modelValue?: string;
|
|
13
|
+
}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
14
|
+
"update:modelValue": (value: string) => any;
|
|
15
|
+
}, string, import("vue").PublicProps, Readonly<EditorContentFullProps & {
|
|
16
|
+
modelValue?: string;
|
|
17
|
+
}> & Readonly<{
|
|
18
|
+
"onUpdate:modelValue"?: ((value: string) => any) | undefined;
|
|
19
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
20
|
+
export default _default;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Primitive :as="as" :class="ui.root({ class: [props.ui?.root, props.class] })">
|
|
3
|
+
<ProseKit :editor="editor">
|
|
4
|
+
<div ref="editorRef" :class="ui.editor({ class: props.ui?.editor })" />
|
|
5
|
+
</ProseKit>
|
|
6
|
+
</Primitive>
|
|
7
|
+
</template>
|
|
8
|
+
|
|
9
|
+
<script>
|
|
10
|
+
import theme from "#build/cms/editor-content-light";
|
|
11
|
+
import { computed, useAppConfig, useTemplateRef, watchPostEffect } from "#imports";
|
|
12
|
+
import { createEditor } from "prosekit/core";
|
|
13
|
+
import { ProseKit, useDocChange } from "prosekit/vue";
|
|
14
|
+
import { Primitive } from "reka-ui";
|
|
15
|
+
import { defineLightExtension } from "../editor/extensions";
|
|
16
|
+
import { docToMarkdown, markdownToDoc } from "../editor/markdown";
|
|
17
|
+
import { tv } from "../utils/tv";
|
|
18
|
+
import "prosekit/basic/style.css";
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<script setup>
|
|
22
|
+
const props = defineProps({
|
|
23
|
+
as: { type: null, required: false },
|
|
24
|
+
class: { type: null, required: false },
|
|
25
|
+
ui: { type: null, required: false }
|
|
26
|
+
});
|
|
27
|
+
const modelValue = defineModel({ type: String, ...{ default: "" } });
|
|
28
|
+
const appConfig = useAppConfig();
|
|
29
|
+
const editor = createEditor({
|
|
30
|
+
extension: defineLightExtension(),
|
|
31
|
+
defaultContent: await markdownToDoc(modelValue.value)
|
|
32
|
+
});
|
|
33
|
+
const editorRef = useTemplateRef("editorRef");
|
|
34
|
+
useDocChange(async () => {
|
|
35
|
+
modelValue.value = await docToMarkdown(editor.getDocJSON());
|
|
36
|
+
}, { editor });
|
|
37
|
+
watchPostEffect((onCleanup) => {
|
|
38
|
+
editor.mount(editorRef.value);
|
|
39
|
+
onCleanup(() => editor.unmount());
|
|
40
|
+
});
|
|
41
|
+
const ui = computed(() => tv({ extend: tv(theme), ...appConfig.cms?.editorContentLight || {} })());
|
|
42
|
+
</script>
|
|
43
|
+
|
|
44
|
+
<style scoped>
|
|
45
|
+
::v-deep(.editor-content-light){line-height:calc(var(--spacing)*7)}::v-deep(.editor-content-light) [data-placeholder]:before{color:var(--ui-text-dimmed);content:attr(data-placeholder);height:0;pointer-events:none;position:absolute}::v-deep(.editor-content-light) blockquote{border-left:4px solid var(--ui-border-accented);margin-block:calc(var(--spacing)*4);padding-inline-start:calc(var(--spacing)*4)}::v-deep(.editor-content-light) [data-node-view-root=true],::v-deep(.editor-content-light)>p{margin-block:calc(var(--spacing)*5)}::v-deep(.editor-content-light) pre{background-color:var(--ui-bg-muted);border:1px solid var(--ui-border-muted);border-radius:calc(var(--ui-radius)*1.5);font-family:var(--font-mono);font-size:var(--text-sm);line-height:calc(var(--spacing)*6);overflow-x:auto;padding-block:calc(var(--spacing)*3);padding-inline:calc(var(--spacing)*4)}::v-deep(.editor-content-light) p>code{background-color:var(--ui-bg-muted);border:1px solid var(--ui-border-muted);border-radius:calc(var(--ui-radius)*1.5);font-family:var(--font-mono);font-size:var(--text-sm);padding-inline:var(--spacing)}::v-deep(.editor-content-light) a{color:var(--ui-primary);cursor:pointer;text-decoration:underline;-webkit-user-select:all;-moz-user-select:all;user-select:all}
|
|
46
|
+
</style>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { AppConfig } from '@nuxt/schema';
|
|
2
|
+
import type { ComponentConfig } from '../types';
|
|
3
|
+
import theme from '#build/cms/editor-content-light';
|
|
4
|
+
import 'prosekit/basic/style.css';
|
|
5
|
+
type EditorContentLight = ComponentConfig<typeof theme, AppConfig, 'editorContentLight'>;
|
|
6
|
+
export interface EditorContentLightProps {
|
|
7
|
+
as?: any;
|
|
8
|
+
class?: any;
|
|
9
|
+
ui?: EditorContentLight['slots'];
|
|
10
|
+
}
|
|
11
|
+
declare const _default: import("vue").DefineComponent<EditorContentLightProps & {
|
|
12
|
+
modelValue?: string;
|
|
13
|
+
}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
14
|
+
"update:modelValue": (value: string) => any;
|
|
15
|
+
}, string, import("vue").PublicProps, Readonly<EditorContentLightProps & {
|
|
16
|
+
modelValue?: string;
|
|
17
|
+
}> & Readonly<{
|
|
18
|
+
"onUpdate:modelValue"?: ((value: string) => any) | undefined;
|
|
19
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
20
|
+
export default _default;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Primitive :as="as" :class="ui.root({ class: [props.ui?.root, props.class] })">
|
|
3
|
+
<template v-if="modelValue.image">
|
|
4
|
+
<UploraImage
|
|
5
|
+
:image="modelValue.image"
|
|
6
|
+
:alt="modelValue.alt"
|
|
7
|
+
:lqip="modelValue.lqip"
|
|
8
|
+
:class="ui.image({ class: [props.ui?.image] })"
|
|
9
|
+
/>
|
|
10
|
+
|
|
11
|
+
<div :class="ui.imageActions({ class: [props.ui?.imageActions] })">
|
|
12
|
+
<ButtonDelete
|
|
13
|
+
:icon="appConfig.ui.icons.trash"
|
|
14
|
+
color="neutral"
|
|
15
|
+
size="sm"
|
|
16
|
+
@confirm="deleteExecute(modelValue.image)"
|
|
17
|
+
/>
|
|
18
|
+
|
|
19
|
+
<UPopover
|
|
20
|
+
:ui="{ content: 'p-2' }"
|
|
21
|
+
:content="{ align: 'end', side: 'bottom', sideOffset: 8 }"
|
|
22
|
+
>
|
|
23
|
+
<UButton :icon="appConfig.ui.icons.ellipsisVertical" color="neutral" size="sm" />
|
|
24
|
+
|
|
25
|
+
<template #content>
|
|
26
|
+
<UFormField label="Описание изображения">
|
|
27
|
+
<UTextarea
|
|
28
|
+
:model-value="modelValue.alt"
|
|
29
|
+
placeholder="Введите описание..."
|
|
30
|
+
autoresize
|
|
31
|
+
@update:model-value="updateAlt"
|
|
32
|
+
/>
|
|
33
|
+
</UFormField>
|
|
34
|
+
</template>
|
|
35
|
+
</UPopover>
|
|
36
|
+
</div>
|
|
37
|
+
</template>
|
|
38
|
+
<div v-else :class="ui.uploader({ class: [props.ui?.uploader] })">
|
|
39
|
+
<template v-if="uploadStatus === 'pending'">
|
|
40
|
+
<UIcon :name="appConfig.ui.icons.loading" :class="ui.uploaderPendingIcon({ class: [props.ui?.uploaderPendingIcon] })" />
|
|
41
|
+
</template>
|
|
42
|
+
<template v-else-if="uploadStatus === 'idle'">
|
|
43
|
+
<button
|
|
44
|
+
:class="ui.uploaderIdleButton({ class: [props.ui?.uploaderIdleButton] })"
|
|
45
|
+
type="button"
|
|
46
|
+
@click="open"
|
|
47
|
+
>
|
|
48
|
+
<UIcon :name="appConfig.ui.icons.imageUp" :class="ui.uploaderIdleIcon({ class: [props.ui?.uploaderIdleIcon] })" />
|
|
49
|
+
<p :class="ui.uploaderIdleText({ class: [props.ui?.uploaderIdleText] })">
|
|
50
|
+
Нажмите, чтобы загрузить изображение
|
|
51
|
+
</p>
|
|
52
|
+
<p v-if="props.showExtensions" :class="ui.uploaderIdleExtensions({ class: [props.ui?.uploaderIdleExtensions] })">
|
|
53
|
+
{{ imagesExtensions.join(", ") }}
|
|
54
|
+
</p>
|
|
55
|
+
</button>
|
|
56
|
+
</template>
|
|
57
|
+
<template v-else-if="uploadStatus === 'error'">
|
|
58
|
+
<p :class="ui.uploaderErrorText({ class: [props.ui?.uploaderErrorText] })">
|
|
59
|
+
Произошла ошибка при загрузке изображения
|
|
60
|
+
</p>
|
|
61
|
+
<div :class="ui.uploaderErrorActions({ class: [props.ui?.uploaderErrorActions] })">
|
|
62
|
+
<UButton
|
|
63
|
+
label="Отменить"
|
|
64
|
+
variant="ghost"
|
|
65
|
+
color="neutral"
|
|
66
|
+
@click="resetUpload"
|
|
67
|
+
/>
|
|
68
|
+
<UButton
|
|
69
|
+
label="Повторить"
|
|
70
|
+
variant="soft"
|
|
71
|
+
color="neutral"
|
|
72
|
+
@click="uploadExecute"
|
|
73
|
+
/>
|
|
74
|
+
</div>
|
|
75
|
+
</template>
|
|
76
|
+
</div>
|
|
77
|
+
</Primitive>
|
|
78
|
+
</template>
|
|
79
|
+
|
|
80
|
+
<script>
|
|
81
|
+
import theme from "#build/cms/input-uplora-image";
|
|
82
|
+
import { computed, useAppConfig } from "#imports";
|
|
83
|
+
import { imagesExtensions } from "@uplora/formats";
|
|
84
|
+
import { Primitive } from "reka-ui";
|
|
85
|
+
import { useUploraDelete, useUploraUpload } from "../composables/useUplora";
|
|
86
|
+
import { tv } from "../utils/tv";
|
|
87
|
+
import ButtonDelete from "./ButtonDelete.vue";
|
|
88
|
+
import UploraImage from "./UploraImage.vue";
|
|
89
|
+
</script>
|
|
90
|
+
|
|
91
|
+
<script setup>
|
|
92
|
+
const props = defineProps({
|
|
93
|
+
showExtensions: { type: Boolean, required: false, default: true },
|
|
94
|
+
as: { type: null, required: false },
|
|
95
|
+
class: { type: null, required: false },
|
|
96
|
+
ui: { type: null, required: false }
|
|
97
|
+
});
|
|
98
|
+
const emit = defineEmits(["upload", "delete"]);
|
|
99
|
+
const modelValue = defineModel({ type: Object, ...{
|
|
100
|
+
default: () => ({
|
|
101
|
+
image: "",
|
|
102
|
+
alt: ""
|
|
103
|
+
})
|
|
104
|
+
} });
|
|
105
|
+
const appConfig = useAppConfig();
|
|
106
|
+
const { open, execute: uploadExecute, status: uploadStatus, reset: resetUpload, onUploaded } = useUploraUpload();
|
|
107
|
+
const { execute: deleteExecute, onDeleted } = useUploraDelete();
|
|
108
|
+
onUploaded((file) => {
|
|
109
|
+
modelValue.value = {
|
|
110
|
+
...modelValue.value,
|
|
111
|
+
image: file.id,
|
|
112
|
+
lqip: file.lqip
|
|
113
|
+
};
|
|
114
|
+
emit("upload", modelValue.value);
|
|
115
|
+
});
|
|
116
|
+
onDeleted(() => {
|
|
117
|
+
resetUpload();
|
|
118
|
+
modelValue.value = {
|
|
119
|
+
image: "",
|
|
120
|
+
alt: ""
|
|
121
|
+
};
|
|
122
|
+
emit("delete");
|
|
123
|
+
});
|
|
124
|
+
function updateAlt(alt) {
|
|
125
|
+
modelValue.value = {
|
|
126
|
+
...modelValue.value,
|
|
127
|
+
alt
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
const ui = computed(() => tv({ extend: tv(theme), ...appConfig.cms?.inputUploraImage || {} })());
|
|
131
|
+
</script>
|