@hywax/cms 3.6.1 → 3.8.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/.nuxt/cms/config.ts +0 -3
- package/.nuxt/cms/prose/index.ts +2 -1
- package/.nuxt/cms/prose/uplora-image-gallery.ts +15 -0
- package/.nuxt/cms/uplora-image.ts +12 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +34 -4
- package/dist/runtime/components/EditorFull.vue +18 -3
- package/dist/runtime/components/FormPanel.vue +1 -1
- package/dist/runtime/components/FormSeo.d.vue.ts +4 -4
- package/dist/runtime/components/FormSeo.vue +28 -15
- package/dist/runtime/components/FormSeo.vue.d.ts +4 -4
- package/dist/runtime/components/FormSlug.vue +9 -2
- package/dist/runtime/components/FormUploraImage.d.vue.ts +7 -9
- package/dist/runtime/components/FormUploraImage.vue +73 -48
- package/dist/runtime/components/FormUploraImage.vue.d.ts +7 -9
- package/dist/runtime/components/UploraImage.d.vue.ts +4 -0
- package/dist/runtime/components/UploraImage.vue +13 -2
- package/dist/runtime/components/UploraImage.vue.d.ts +4 -0
- package/dist/runtime/components/prose/UploraImage.d.vue.ts +4 -7
- package/dist/runtime/components/prose/UploraImage.vue +13 -7
- package/dist/runtime/components/prose/UploraImage.vue.d.ts +4 -7
- package/dist/runtime/components/prose/UploraImageGallery.d.vue.ts +19 -0
- package/dist/runtime/components/prose/UploraImageGallery.vue +24 -0
- package/dist/runtime/components/prose/UploraImageGallery.vue.d.ts +19 -0
- package/dist/runtime/composables/useEditorSuggestions.d.ts +29 -6
- package/dist/runtime/composables/useEditorSuggestions.js +7 -3
- package/dist/runtime/editor/caution/EditorCaution.d.ts +18 -0
- package/dist/runtime/editor/caution/EditorCaution.js +36 -0
- package/dist/runtime/editor/caution/EditorCautionNode.d.vue.ts +4 -0
- package/dist/runtime/editor/caution/EditorCautionNode.vue +22 -0
- package/dist/runtime/editor/caution/EditorCautionNode.vue.d.ts +4 -0
- package/dist/runtime/editor/note/EditorNote.d.ts +18 -0
- package/dist/runtime/editor/note/EditorNote.js +36 -0
- package/dist/runtime/editor/note/EditorNoteNode.d.vue.ts +4 -0
- package/dist/runtime/editor/note/EditorNoteNode.vue +22 -0
- package/dist/runtime/editor/note/EditorNoteNode.vue.d.ts +4 -0
- package/dist/runtime/editor/tip/EditorTip.d.ts +18 -0
- package/dist/runtime/editor/tip/EditorTip.js +36 -0
- package/dist/runtime/editor/tip/EditorTipNode.d.vue.ts +4 -0
- package/dist/runtime/editor/tip/EditorTipNode.vue +22 -0
- package/dist/runtime/editor/tip/EditorTipNode.vue.d.ts +4 -0
- package/dist/runtime/editor/uplora-image/EditorUploraImage.js +10 -4
- package/dist/runtime/editor/uplora-image/EditorUploraImageNode.d.vue.ts +0 -5
- package/dist/runtime/editor/uplora-image/EditorUploraImageNode.vue +2 -2
- package/dist/runtime/editor/uplora-image/EditorUploraImageNode.vue.d.ts +0 -5
- package/dist/runtime/editor/uplora-image-gallery/EditorUploraImageGallery.d.ts +18 -0
- package/dist/runtime/editor/uplora-image-gallery/EditorUploraImageGallery.js +71 -0
- package/dist/runtime/editor/uplora-image-gallery/EditorUploraImageGalleryNode.d.vue.ts +4 -0
- package/dist/runtime/editor/uplora-image-gallery/EditorUploraImageGalleryNode.vue +93 -0
- package/dist/runtime/editor/uplora-image-gallery/EditorUploraImageGalleryNode.vue.d.ts +4 -0
- package/dist/runtime/editor/utils/attributes.d.ts +2 -0
- package/dist/runtime/editor/{utils.js → utils/attributes.js} +0 -29
- package/dist/runtime/editor/utils/createAtomBlockMarkdownSpec.d.ts +10 -0
- package/dist/runtime/editor/utils/createAtomBlockMarkdownSpec.js +32 -0
- package/dist/runtime/editor/utils/createBlockMarkdownSpec.d.ts +12 -0
- package/dist/runtime/editor/utils/createBlockMarkdownSpec.js +99 -0
- package/dist/runtime/editor/utils/index.d.ts +3 -0
- package/dist/runtime/editor/utils/index.js +3 -0
- package/dist/runtime/editor/warning/EditorWarning.d.ts +18 -0
- package/dist/runtime/editor/warning/EditorWarning.js +36 -0
- package/dist/runtime/editor/warning/EditorWarningNode.d.vue.ts +4 -0
- package/dist/runtime/editor/warning/EditorWarningNode.vue +22 -0
- package/dist/runtime/editor/warning/EditorWarningNode.vue.d.ts +4 -0
- package/dist/runtime/server/utils/pagination.d.ts +4 -0
- package/dist/runtime/server/utils/pagination.js +2 -2
- package/package.json +1 -1
- package/dist/runtime/editor/utils.d.ts +0 -13
package/.nuxt/cms/config.ts
CHANGED
package/.nuxt/cms/prose/index.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export { default as uploraImage } from './uplora-image'
|
|
1
|
+
export { default as uploraImage } from './uplora-image'
|
|
2
|
+
export { default as uploraImageGallery } from './uplora-image-gallery'
|
|
@@ -2,7 +2,18 @@ export default {
|
|
|
2
2
|
"slots": {
|
|
3
3
|
"root": "relative grid grid-cols-[100%] grid-rows-[100%] overflow-hidden",
|
|
4
4
|
"lqip": "bg-cover bg-no-repeat bg-center col-[1] row-[1]",
|
|
5
|
-
"picture": "
|
|
5
|
+
"picture": "col-[1] row-[1]",
|
|
6
6
|
"img": "w-full h-full block object-cover"
|
|
7
|
+
},
|
|
8
|
+
"variants": {
|
|
9
|
+
"aspect": {
|
|
10
|
+
"horizontal": {
|
|
11
|
+
"picture": "aspect-3/2"
|
|
12
|
+
},
|
|
13
|
+
"vertical": {
|
|
14
|
+
"picture": "aspect-2/3"
|
|
15
|
+
},
|
|
16
|
+
"dynamic": {}
|
|
17
|
+
}
|
|
7
18
|
}
|
|
8
19
|
}
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -7,7 +7,7 @@ import { pascalCase, kebabCase, camelCase } from 'scule';
|
|
|
7
7
|
import { globSync } from 'tinyglobby';
|
|
8
8
|
|
|
9
9
|
const name = "@hywax/cms";
|
|
10
|
-
const version = "3.
|
|
10
|
+
const version = "3.8.0";
|
|
11
11
|
|
|
12
12
|
function createContext(options, nuxt) {
|
|
13
13
|
const { resolve } = createResolver(import.meta.url);
|
|
@@ -181,6 +181,7 @@ const icons = {
|
|
|
181
181
|
strikethrough: "i-lucide-strikethrough",
|
|
182
182
|
code: "i-lucide-code",
|
|
183
183
|
image: "i-lucide-image",
|
|
184
|
+
images: "i-lucide-images",
|
|
184
185
|
editLine: "i-lucide-pen-line",
|
|
185
186
|
save: "i-lucide-cloud-check",
|
|
186
187
|
toc: "i-lucide-text-align-start"
|
|
@@ -356,7 +357,8 @@ async function prepareMergeConfigs({ nuxt, options, resolve, detectedComponents:
|
|
|
356
357
|
nuxt.options.mdc = defu(nuxt.options.mdc || {}, {
|
|
357
358
|
components: {
|
|
358
359
|
map: {
|
|
359
|
-
"uplora-image": "ProseUploraImage"
|
|
360
|
+
"uplora-image": "ProseUploraImage",
|
|
361
|
+
"uplora-image-gallery": "ProseUploraImageGallery"
|
|
360
362
|
}
|
|
361
363
|
}
|
|
362
364
|
});
|
|
@@ -563,8 +565,19 @@ const uploraImage$1 = {
|
|
|
563
565
|
slots: {
|
|
564
566
|
root: "relative grid grid-cols-[100%] grid-rows-[100%] overflow-hidden",
|
|
565
567
|
lqip: "bg-cover bg-no-repeat bg-center col-[1] row-[1]",
|
|
566
|
-
picture: "
|
|
568
|
+
picture: "col-[1] row-[1]",
|
|
567
569
|
img: "w-full h-full block object-cover"
|
|
570
|
+
},
|
|
571
|
+
variants: {
|
|
572
|
+
aspect: {
|
|
573
|
+
horizontal: {
|
|
574
|
+
picture: "aspect-3/2"
|
|
575
|
+
},
|
|
576
|
+
vertical: {
|
|
577
|
+
picture: "aspect-2/3"
|
|
578
|
+
},
|
|
579
|
+
dynamic: {}
|
|
580
|
+
}
|
|
568
581
|
}
|
|
569
582
|
};
|
|
570
583
|
|
|
@@ -601,9 +614,26 @@ const uploraImage = {
|
|
|
601
614
|
}
|
|
602
615
|
};
|
|
603
616
|
|
|
617
|
+
const uploraImageGallery = {
|
|
618
|
+
slots: {
|
|
619
|
+
base: "grid gap-4"
|
|
620
|
+
},
|
|
621
|
+
variants: {
|
|
622
|
+
layout: {
|
|
623
|
+
"two-columns": {
|
|
624
|
+
base: "grid-cols-2"
|
|
625
|
+
},
|
|
626
|
+
"three-columns": {
|
|
627
|
+
base: "grid-cols-3"
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
};
|
|
632
|
+
|
|
604
633
|
const themeProse = {
|
|
605
634
|
__proto__: null,
|
|
606
|
-
uploraImage: uploraImage
|
|
635
|
+
uploraImage: uploraImage,
|
|
636
|
+
uploraImageGallery: uploraImageGallery
|
|
607
637
|
};
|
|
608
638
|
|
|
609
639
|
async function getAppTemplates({ options, resolve, nuxt, detectedComponents: contextDetectedComponents }) {
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
v-bind="forwardedEditor"
|
|
17
17
|
class="min-h-screen"
|
|
18
18
|
content-type="markdown"
|
|
19
|
-
placeholder="Введите '/' для выбора блока..."
|
|
20
19
|
autofocus
|
|
20
|
+
:placeholder="{ placeholder: '\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0442\u0435\u043A\u0441\u0442...', includeChildren: true }"
|
|
21
21
|
:extensions="extensions"
|
|
22
22
|
:handlers="customHandlers"
|
|
23
23
|
>
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
:editor="editor"
|
|
65
65
|
:items="bubbleToolbarItems"
|
|
66
66
|
:should-show="({ view, state }) => {
|
|
67
|
-
if (editor.isActive('uplora-image')) {
|
|
67
|
+
if (editor.isActive('uplora-image') || editor.isActive('uplora-image-gallery')) {
|
|
68
68
|
return false;
|
|
69
69
|
}
|
|
70
70
|
const { selection } = state;
|
|
@@ -92,7 +92,12 @@ import { computed } from "vue";
|
|
|
92
92
|
import { useEditorDragHandle } from "../composables/useEditorDragHandle";
|
|
93
93
|
import { useEditorSuggestions } from "../composables/useEditorSuggestions";
|
|
94
94
|
import { useEditorToolbar } from "../composables/useEditorToolbar";
|
|
95
|
+
import { Caution as CautionExtension, CautionHandlers } from "../editor/caution/EditorCaution";
|
|
96
|
+
import { Note as NoteExtension, NoteHandlers } from "../editor/note/EditorNote";
|
|
97
|
+
import { Tip as TipExtension, TipHandlers } from "../editor/tip/EditorTip";
|
|
98
|
+
import { UploraImageGallery as UploraImageGalleryExtension, UploraImageGalleryHandlers } from "../editor/uplora-image-gallery/EditorUploraImageGallery";
|
|
95
99
|
import { UploraImage as UploraImageExtension, UploraImageHandlers } from "../editor/uplora-image/EditorUploraImage";
|
|
100
|
+
import { Warning as WarningExtension, WarningHandlers } from "../editor/warning/EditorWarning";
|
|
96
101
|
import CEditorLinkPopover from "./EditorLinkPopover.vue";
|
|
97
102
|
</script>
|
|
98
103
|
|
|
@@ -143,11 +148,21 @@ const forwardedTextarea = useForwardPropsEmits(reactivePick(props, "modelValue")
|
|
|
143
148
|
const appConfig = useAppConfig();
|
|
144
149
|
const extensions = computed(() => [
|
|
145
150
|
UploraImageExtension,
|
|
151
|
+
UploraImageGalleryExtension,
|
|
152
|
+
NoteExtension,
|
|
153
|
+
TipExtension,
|
|
154
|
+
WarningExtension,
|
|
155
|
+
CautionExtension,
|
|
146
156
|
...props.extensions || []
|
|
147
157
|
]);
|
|
148
158
|
const customHandlers = computed(() => ({
|
|
149
159
|
...props.customHandlers || {},
|
|
150
|
-
...UploraImageHandlers
|
|
160
|
+
...UploraImageHandlers,
|
|
161
|
+
...UploraImageGalleryHandlers,
|
|
162
|
+
...NoteHandlers,
|
|
163
|
+
...TipHandlers,
|
|
164
|
+
...WarningHandlers,
|
|
165
|
+
...CautionHandlers
|
|
151
166
|
}));
|
|
152
167
|
const appendToBody = false ? () => document.body : void 0;
|
|
153
168
|
const { items: suggestionItems } = useEditorSuggestions(customHandlers);
|
|
@@ -10,13 +10,13 @@ export interface FormSeoProps {
|
|
|
10
10
|
declare const _default: typeof __VLS_export;
|
|
11
11
|
export default _default;
|
|
12
12
|
declare const __VLS_export: import("vue").DefineComponent<FormSeoProps & {
|
|
13
|
-
modelValue?: SEO;
|
|
13
|
+
modelValue?: SEO | null;
|
|
14
14
|
}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
15
|
-
"update:modelValue": (value: SEO) => any;
|
|
15
|
+
"update:modelValue": (value: SEO | null | undefined) => any;
|
|
16
16
|
}, string, import("vue").PublicProps, Readonly<FormSeoProps & {
|
|
17
|
-
modelValue?: SEO;
|
|
17
|
+
modelValue?: SEO | null;
|
|
18
18
|
}> & Readonly<{
|
|
19
|
-
"onUpdate:modelValue"?: ((value: SEO) => any) | undefined;
|
|
19
|
+
"onUpdate:modelValue"?: ((value: SEO | null | undefined) => any) | undefined;
|
|
20
20
|
}>, {
|
|
21
21
|
name: string;
|
|
22
22
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
@@ -3,16 +3,21 @@
|
|
|
3
3
|
:name="name"
|
|
4
4
|
:schema="schema"
|
|
5
5
|
:class="ui.root({ class: [props.ui?.root, props.class] })"
|
|
6
|
-
:validate-on="['
|
|
6
|
+
:validate-on="['input']"
|
|
7
7
|
nested
|
|
8
8
|
>
|
|
9
9
|
<UFormField
|
|
10
10
|
name="title"
|
|
11
11
|
label="Заголовок"
|
|
12
|
-
:hint="`${
|
|
12
|
+
:hint="`${state.title.length}/60`"
|
|
13
13
|
:ui="{ hint: 'text-xs' }"
|
|
14
14
|
>
|
|
15
|
-
<UInput
|
|
15
|
+
<UInput
|
|
16
|
+
:model-value="state.title"
|
|
17
|
+
placeholder="Введите заголовок..."
|
|
18
|
+
class="w-full"
|
|
19
|
+
@update:model-value="state = { ...state, title: $event }"
|
|
20
|
+
/>
|
|
16
21
|
|
|
17
22
|
<template #help>
|
|
18
23
|
<p class="text-xs mb-2">
|
|
@@ -29,14 +34,15 @@
|
|
|
29
34
|
<UFormField
|
|
30
35
|
name="description"
|
|
31
36
|
label="Описание"
|
|
32
|
-
:hint="`${
|
|
37
|
+
:hint="`${state.description.length}/160`"
|
|
33
38
|
:ui="{ hint: 'text-xs' }"
|
|
34
39
|
>
|
|
35
40
|
<UTextarea
|
|
36
|
-
|
|
41
|
+
:model-value="state.description"
|
|
37
42
|
placeholder="Введите описание..."
|
|
38
43
|
class="w-full"
|
|
39
44
|
autoresize
|
|
45
|
+
@update:model-value="state = { ...state, description: $event }"
|
|
40
46
|
/>
|
|
41
47
|
|
|
42
48
|
<template #help>
|
|
@@ -68,17 +74,24 @@ const props = defineProps({
|
|
|
68
74
|
name: { type: String, required: false, default: "seo" },
|
|
69
75
|
ui: { type: null, required: false }
|
|
70
76
|
});
|
|
71
|
-
const model = defineModel({ type: Object,
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
+
const model = defineModel({ type: [Object, null] });
|
|
78
|
+
const state = computed({
|
|
79
|
+
get: () => {
|
|
80
|
+
return model.value ?? {
|
|
81
|
+
title: "",
|
|
82
|
+
description: ""
|
|
83
|
+
};
|
|
84
|
+
},
|
|
85
|
+
set: (value) => {
|
|
86
|
+
const isEmpty = Object.values(value).every((value2) => !value2);
|
|
87
|
+
model.value = isEmpty ? null : value;
|
|
88
|
+
}
|
|
89
|
+
});
|
|
77
90
|
const appConfig = useAppConfig();
|
|
78
91
|
const schema = z.object({
|
|
79
|
-
title: z.string()
|
|
80
|
-
description: z.string()
|
|
81
|
-
});
|
|
82
|
-
const { title, description } = useSeoStats(
|
|
92
|
+
title: z.string(),
|
|
93
|
+
description: z.string()
|
|
94
|
+
}).nullable();
|
|
95
|
+
const { title, description } = useSeoStats(() => state.value);
|
|
83
96
|
const ui = computed(() => tv({ extend: tv(theme), ...appConfig.cms?.formSeo || {} })());
|
|
84
97
|
</script>
|
|
@@ -10,13 +10,13 @@ export interface FormSeoProps {
|
|
|
10
10
|
declare const _default: typeof __VLS_export;
|
|
11
11
|
export default _default;
|
|
12
12
|
declare const __VLS_export: import("vue").DefineComponent<FormSeoProps & {
|
|
13
|
-
modelValue?: SEO;
|
|
13
|
+
modelValue?: SEO | null;
|
|
14
14
|
}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
15
|
-
"update:modelValue": (value: SEO) => any;
|
|
15
|
+
"update:modelValue": (value: SEO | null | undefined) => any;
|
|
16
16
|
}, string, import("vue").PublicProps, Readonly<FormSeoProps & {
|
|
17
|
-
modelValue?: SEO;
|
|
17
|
+
modelValue?: SEO | null;
|
|
18
18
|
}> & Readonly<{
|
|
19
|
-
"onUpdate:modelValue"?: ((value: SEO) => any) | undefined;
|
|
19
|
+
"onUpdate:modelValue"?: ((value: SEO | null | undefined) => any) | undefined;
|
|
20
20
|
}>, {
|
|
21
21
|
name: string;
|
|
22
22
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<UForm
|
|
3
|
+
ref="formRef"
|
|
3
4
|
:schema="schema"
|
|
4
5
|
:class="ui.root({ class: [props.ui?.root, props.class] })"
|
|
5
|
-
:validate-on="['
|
|
6
|
+
:validate-on="['input']"
|
|
6
7
|
nested
|
|
7
8
|
>
|
|
8
9
|
<UFormField
|
|
@@ -45,7 +46,7 @@
|
|
|
45
46
|
<script>
|
|
46
47
|
import theme from "#build/cms/form-slug";
|
|
47
48
|
import { useAppConfig } from "#imports";
|
|
48
|
-
import { computed, ref, watch } from "vue";
|
|
49
|
+
import { computed, ref, useTemplateRef, watch } from "vue";
|
|
49
50
|
import { z } from "zod";
|
|
50
51
|
import { tv } from "../tv";
|
|
51
52
|
import { slugify } from "../utils/slugify";
|
|
@@ -69,6 +70,7 @@ const schema = z.object({
|
|
|
69
70
|
title: z.string().min(1),
|
|
70
71
|
slug: z.string().min(1)
|
|
71
72
|
});
|
|
73
|
+
const formRef = useTemplateRef("formRef");
|
|
72
74
|
const isRegenerate = ref(props.regenerate);
|
|
73
75
|
watch(title, () => {
|
|
74
76
|
if (!isRegenerate.value) {
|
|
@@ -76,5 +78,10 @@ watch(title, () => {
|
|
|
76
78
|
}
|
|
77
79
|
slug.value = slugify(title.value);
|
|
78
80
|
});
|
|
81
|
+
watch(slug, () => {
|
|
82
|
+
if (formRef.value?.getErrors("slug")) {
|
|
83
|
+
formRef.value?.clear("slug");
|
|
84
|
+
}
|
|
85
|
+
});
|
|
79
86
|
const ui = computed(() => tv({ extend: tv(theme), ...appConfig.cms?.formSlug || {} })());
|
|
80
87
|
</script>
|
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
import type { AppConfig } from '@nuxt/schema';
|
|
2
2
|
import type { ComponentConfig } from '../types';
|
|
3
|
+
import type { UploraImageProps } from './UploraImage.vue';
|
|
3
4
|
import theme from '#build/cms/form-uplora-image';
|
|
4
5
|
type FormUploraImage = ComponentConfig<typeof theme, AppConfig, 'formUploraImage'>;
|
|
5
|
-
export interface FormUploraImageModelValue {
|
|
6
|
+
export interface FormUploraImageModelValue extends Pick<UploraImageProps, 'alt' | 'lqip' | 'color' | 'aspect' | 'width' | 'height'> {
|
|
6
7
|
image?: string;
|
|
7
|
-
alt?: string;
|
|
8
|
-
color?: string;
|
|
9
|
-
lqip?: string;
|
|
10
8
|
}
|
|
11
9
|
export interface FormUploraImageProps {
|
|
12
10
|
showExtensions?: boolean;
|
|
13
|
-
id?: string;
|
|
14
11
|
name?: string;
|
|
15
12
|
label?: string;
|
|
16
13
|
disabled?: boolean;
|
|
@@ -26,18 +23,19 @@ export interface FormUploraImageEmits {
|
|
|
26
23
|
declare const _default: typeof __VLS_export;
|
|
27
24
|
export default _default;
|
|
28
25
|
declare const __VLS_export: import("vue").DefineComponent<FormUploraImageProps & {
|
|
29
|
-
modelValue?: FormUploraImageModelValue;
|
|
26
|
+
modelValue?: FormUploraImageModelValue | null;
|
|
30
27
|
}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
31
28
|
delete: () => any;
|
|
32
|
-
"update:modelValue": (value: FormUploraImageModelValue) => any;
|
|
29
|
+
"update:modelValue": (value: FormUploraImageModelValue | null | undefined) => any;
|
|
33
30
|
upload: (args_0: FormUploraImageModelValue) => any;
|
|
34
31
|
}, string, import("vue").PublicProps, Readonly<FormUploraImageProps & {
|
|
35
|
-
modelValue?: FormUploraImageModelValue;
|
|
32
|
+
modelValue?: FormUploraImageModelValue | null;
|
|
36
33
|
}> & Readonly<{
|
|
37
34
|
onDelete?: (() => any) | undefined;
|
|
38
|
-
"onUpdate:modelValue"?: ((value: FormUploraImageModelValue) => any) | undefined;
|
|
35
|
+
"onUpdate:modelValue"?: ((value: FormUploraImageModelValue | null | undefined) => any) | undefined;
|
|
39
36
|
onUpload?: ((args_0: FormUploraImageModelValue) => any) | undefined;
|
|
40
37
|
}>, {
|
|
38
|
+
name: string;
|
|
41
39
|
showExtensions: boolean;
|
|
42
40
|
nested: boolean;
|
|
43
41
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
@@ -3,22 +3,39 @@
|
|
|
3
3
|
:name="name"
|
|
4
4
|
:schema="schema"
|
|
5
5
|
:class="ui.root({ class: [props.ui?.root, props.class] })"
|
|
6
|
-
:validate-on="['
|
|
6
|
+
:validate-on="['input']"
|
|
7
7
|
:nested="nested"
|
|
8
8
|
>
|
|
9
9
|
<UFormField name="image" :label="label">
|
|
10
10
|
<div :class="ui.base({ class: [props.ui?.base] })">
|
|
11
|
-
<template v-if="
|
|
12
|
-
<UPopover
|
|
11
|
+
<template v-if="state.image">
|
|
12
|
+
<UPopover
|
|
13
|
+
:content="{ align: 'center', side: 'top', sideOffset: 8 }"
|
|
14
|
+
:ui="{ content: 'p-1' }"
|
|
15
|
+
mode="click"
|
|
16
|
+
>
|
|
13
17
|
<CUploraImage
|
|
14
|
-
:image="
|
|
15
|
-
:alt="
|
|
16
|
-
:color="
|
|
17
|
-
:lqip="
|
|
18
|
+
:image="state.image"
|
|
19
|
+
:alt="state.alt"
|
|
20
|
+
:color="state.color"
|
|
21
|
+
:lqip="state.lqip"
|
|
22
|
+
:aspect="state.aspect"
|
|
23
|
+
:width="state.width"
|
|
24
|
+
:height="state.height"
|
|
18
25
|
/>
|
|
19
26
|
|
|
20
27
|
<template #content>
|
|
21
28
|
<div class="flex items-center gap-0.5">
|
|
29
|
+
<USelect
|
|
30
|
+
variant="ghost"
|
|
31
|
+
color="neutral"
|
|
32
|
+
size="sm"
|
|
33
|
+
:items="aspectItems"
|
|
34
|
+
:model-value="state.aspect"
|
|
35
|
+
default-value="horizontal"
|
|
36
|
+
@update:model-value="state = { ...state, aspect: $event === 'horizontal' ? void 0 : $event }"
|
|
37
|
+
/>
|
|
38
|
+
|
|
22
39
|
<UButton
|
|
23
40
|
color="neutral"
|
|
24
41
|
size="sm"
|
|
@@ -33,13 +50,13 @@
|
|
|
33
50
|
variant="ghost"
|
|
34
51
|
label=""
|
|
35
52
|
:icon="appConfig.ui.icons.trash"
|
|
36
|
-
@confirm="deleteExecute(
|
|
53
|
+
@confirm="deleteExecute(state.image)"
|
|
37
54
|
/>
|
|
38
55
|
</div>
|
|
39
56
|
</template>
|
|
40
57
|
</UPopover>
|
|
41
58
|
</template>
|
|
42
|
-
<div v-else :
|
|
59
|
+
<div v-else :class="ui.uploader({ class: [props.ui?.uploader] })">
|
|
43
60
|
<template v-if="uploadStatus === 'pending'">
|
|
44
61
|
<UIcon :name="appConfig.ui.icons.loading" :class="ui.uploaderPendingIcon({ class: [props.ui?.uploaderPendingIcon] })" />
|
|
45
62
|
</template>
|
|
@@ -48,7 +65,6 @@
|
|
|
48
65
|
type="button"
|
|
49
66
|
:class="ui.uploaderIdleButton({ class: [props.ui?.uploaderIdleButton] })"
|
|
50
67
|
:disabled="disabled"
|
|
51
|
-
v-bind="ariaAttrs"
|
|
52
68
|
@click="open"
|
|
53
69
|
>
|
|
54
70
|
<UAvatar :icon="appConfig.ui.icons.image" size="lg" />
|
|
@@ -85,12 +101,12 @@
|
|
|
85
101
|
</UFormField>
|
|
86
102
|
<UFormField name="alt">
|
|
87
103
|
<UInput
|
|
88
|
-
v-model="modelValue.alt"
|
|
89
104
|
placeholder="Введите описание..."
|
|
90
105
|
variant="none"
|
|
91
106
|
size="xs"
|
|
107
|
+
:model-value="state.alt"
|
|
92
108
|
:ui="{ base: 'text-center text-toned' }"
|
|
93
|
-
@update:model-value="
|
|
109
|
+
@update:model-value="state = { ...state, alt: $event || void 0 }"
|
|
94
110
|
/>
|
|
95
111
|
</UFormField>
|
|
96
112
|
</UForm>
|
|
@@ -98,9 +114,9 @@
|
|
|
98
114
|
|
|
99
115
|
<script>
|
|
100
116
|
import theme from "#build/cms/form-uplora-image";
|
|
101
|
-
import { useAppConfig,
|
|
117
|
+
import { useAppConfig, useUploraDelete, useUploraUpload } from "#imports";
|
|
102
118
|
import { imagesExtensions } from "@uplora/formats";
|
|
103
|
-
import { computed
|
|
119
|
+
import { computed } from "vue";
|
|
104
120
|
import { z } from "zod";
|
|
105
121
|
import { tv } from "../tv";
|
|
106
122
|
import CButtonDeleteConfirm from "./ButtonDeleteConfirm.vue";
|
|
@@ -109,8 +125,7 @@ import CButtonDeleteConfirm from "./ButtonDeleteConfirm.vue";
|
|
|
109
125
|
<script setup>
|
|
110
126
|
const props = defineProps({
|
|
111
127
|
showExtensions: { type: Boolean, required: false, default: true },
|
|
112
|
-
|
|
113
|
-
name: { type: String, required: false },
|
|
128
|
+
name: { type: String, required: false, default: "image" },
|
|
114
129
|
label: { type: String, required: false },
|
|
115
130
|
disabled: { type: Boolean, required: false },
|
|
116
131
|
as: { type: null, required: false },
|
|
@@ -119,63 +134,73 @@ const props = defineProps({
|
|
|
119
134
|
ui: { type: null, required: false }
|
|
120
135
|
});
|
|
121
136
|
const emit = defineEmits(["upload", "delete"]);
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
137
|
+
const model = defineModel({ type: [Object, null] });
|
|
138
|
+
const state = computed({
|
|
139
|
+
get: () => {
|
|
140
|
+
return model.value ?? {
|
|
141
|
+
image: void 0,
|
|
142
|
+
width: void 0,
|
|
143
|
+
height: void 0,
|
|
144
|
+
alt: void 0,
|
|
145
|
+
lqip: void 0,
|
|
146
|
+
color: void 0,
|
|
147
|
+
aspect: void 0
|
|
148
|
+
};
|
|
149
|
+
},
|
|
150
|
+
set: (value) => {
|
|
151
|
+
const isEmpty = Object.values(value).every((value2) => !value2);
|
|
152
|
+
model.value = isEmpty ? null : value;
|
|
153
|
+
}
|
|
154
|
+
});
|
|
130
155
|
const appConfig = useAppConfig();
|
|
131
156
|
const schema = z.object({
|
|
132
157
|
image: z.string().min(1),
|
|
133
158
|
alt: z.string().min(1),
|
|
159
|
+
width: z.number(),
|
|
160
|
+
height: z.number(),
|
|
134
161
|
lqip: z.string().min(1).optional(),
|
|
135
|
-
color: z.string().min(1).optional()
|
|
136
|
-
|
|
162
|
+
color: z.string().min(1).optional(),
|
|
163
|
+
aspect: z.union([z.literal("horizontal"), z.literal("vertical"), z.literal("dynamic")]).optional()
|
|
164
|
+
}).nullable();
|
|
137
165
|
const extensions = computed(() => imagesExtensions.filter((extension) => extension !== "jpeg").join(", "));
|
|
138
|
-
const
|
|
139
|
-
|
|
166
|
+
const aspectItems = [
|
|
167
|
+
{ value: "horizontal", label: "\u0413\u043E\u0440\u0438\u0437\u043E\u043D\u0442\u0430\u043B\u044C\u043D\u044B\u0439" },
|
|
168
|
+
{ value: "vertical", label: "\u0412\u0435\u0440\u0442\u0438\u043A\u0430\u043B\u044C\u043D\u044B\u0439" },
|
|
169
|
+
{ value: "dynamic", label: "\u0414\u0438\u043D\u0430\u043C\u0438\u0447\u0435\u0441\u043A\u0438\u0439" }
|
|
170
|
+
];
|
|
140
171
|
const { open, execute: uploadExecute, status: uploadStatus, reset: resetUpload, onUploaded } = useUploraUpload({
|
|
141
172
|
accept: "image/*"
|
|
142
173
|
});
|
|
143
174
|
const { execute: deleteExecute, onDeleted } = useUploraDelete();
|
|
144
|
-
function onAltInput(alt) {
|
|
145
|
-
modelValue.value = {
|
|
146
|
-
...modelValue.value,
|
|
147
|
-
alt: alt || void 0
|
|
148
|
-
};
|
|
149
|
-
emitFormChange();
|
|
150
|
-
emitFormInput();
|
|
151
|
-
}
|
|
152
175
|
function resetState() {
|
|
153
|
-
|
|
176
|
+
resetUpload();
|
|
177
|
+
state.value = {
|
|
154
178
|
image: void 0,
|
|
179
|
+
width: void 0,
|
|
180
|
+
height: void 0,
|
|
155
181
|
alt: void 0,
|
|
156
182
|
lqip: void 0,
|
|
157
|
-
color: void 0
|
|
183
|
+
color: void 0,
|
|
184
|
+
aspect: void 0
|
|
158
185
|
};
|
|
159
|
-
emitFormChange();
|
|
160
|
-
emitFormInput();
|
|
161
186
|
}
|
|
162
187
|
onUploaded((file) => {
|
|
163
|
-
|
|
164
|
-
...modelValue.value,
|
|
188
|
+
state.value = {
|
|
165
189
|
image: file.id,
|
|
190
|
+
width: file.width,
|
|
191
|
+
height: file.height,
|
|
192
|
+
alt: void 0,
|
|
166
193
|
lqip: file.lqip,
|
|
167
|
-
color: file.color
|
|
194
|
+
color: file.color,
|
|
195
|
+
aspect: void 0
|
|
168
196
|
};
|
|
169
|
-
emit("upload",
|
|
170
|
-
emitFormChange();
|
|
171
|
-
emitFormInput();
|
|
197
|
+
emit("upload", state.value);
|
|
172
198
|
});
|
|
173
199
|
onDeleted(() => {
|
|
174
|
-
resetUpload();
|
|
175
200
|
resetState();
|
|
176
201
|
emit("delete");
|
|
177
202
|
});
|
|
178
203
|
const ui = computed(() => tv({ extend: tv(theme), ...appConfig.cms?.formUploraImage || {} })({
|
|
179
|
-
disabled: disabled
|
|
204
|
+
disabled: props.disabled
|
|
180
205
|
}));
|
|
181
206
|
</script>
|
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
import type { AppConfig } from '@nuxt/schema';
|
|
2
2
|
import type { ComponentConfig } from '../types';
|
|
3
|
+
import type { UploraImageProps } from './UploraImage.vue';
|
|
3
4
|
import theme from '#build/cms/form-uplora-image';
|
|
4
5
|
type FormUploraImage = ComponentConfig<typeof theme, AppConfig, 'formUploraImage'>;
|
|
5
|
-
export interface FormUploraImageModelValue {
|
|
6
|
+
export interface FormUploraImageModelValue extends Pick<UploraImageProps, 'alt' | 'lqip' | 'color' | 'aspect' | 'width' | 'height'> {
|
|
6
7
|
image?: string;
|
|
7
|
-
alt?: string;
|
|
8
|
-
color?: string;
|
|
9
|
-
lqip?: string;
|
|
10
8
|
}
|
|
11
9
|
export interface FormUploraImageProps {
|
|
12
10
|
showExtensions?: boolean;
|
|
13
|
-
id?: string;
|
|
14
11
|
name?: string;
|
|
15
12
|
label?: string;
|
|
16
13
|
disabled?: boolean;
|
|
@@ -26,18 +23,19 @@ export interface FormUploraImageEmits {
|
|
|
26
23
|
declare const _default: typeof __VLS_export;
|
|
27
24
|
export default _default;
|
|
28
25
|
declare const __VLS_export: import("vue").DefineComponent<FormUploraImageProps & {
|
|
29
|
-
modelValue?: FormUploraImageModelValue;
|
|
26
|
+
modelValue?: FormUploraImageModelValue | null;
|
|
30
27
|
}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
31
28
|
delete: () => any;
|
|
32
|
-
"update:modelValue": (value: FormUploraImageModelValue) => any;
|
|
29
|
+
"update:modelValue": (value: FormUploraImageModelValue | null | undefined) => any;
|
|
33
30
|
upload: (args_0: FormUploraImageModelValue) => any;
|
|
34
31
|
}, string, import("vue").PublicProps, Readonly<FormUploraImageProps & {
|
|
35
|
-
modelValue?: FormUploraImageModelValue;
|
|
32
|
+
modelValue?: FormUploraImageModelValue | null;
|
|
36
33
|
}> & Readonly<{
|
|
37
34
|
onDelete?: (() => any) | undefined;
|
|
38
|
-
"onUpdate:modelValue"?: ((value: FormUploraImageModelValue) => any) | undefined;
|
|
35
|
+
"onUpdate:modelValue"?: ((value: FormUploraImageModelValue | null | undefined) => any) | undefined;
|
|
39
36
|
onUpload?: ((args_0: FormUploraImageModelValue) => any) | undefined;
|
|
40
37
|
}>, {
|
|
38
|
+
name: string;
|
|
41
39
|
showExtensions: boolean;
|
|
42
40
|
nested: boolean;
|
|
43
41
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
@@ -11,6 +11,9 @@ export interface UploraImageProps {
|
|
|
11
11
|
sizes?: ImageSize[];
|
|
12
12
|
lqip?: string;
|
|
13
13
|
color?: string;
|
|
14
|
+
aspect?: 'horizontal' | 'vertical' | 'dynamic';
|
|
15
|
+
width?: number | string;
|
|
16
|
+
height?: number | string;
|
|
14
17
|
loading?: 'lazy' | 'eager';
|
|
15
18
|
preload?: boolean | {
|
|
16
19
|
fetchPriority: 'auto' | 'high' | 'low';
|
|
@@ -34,4 +37,5 @@ declare const __VLS_export: import("vue").DefineComponent<UploraImageProps, {},
|
|
|
34
37
|
onLoad?: ((args_0: Event) => any) | undefined;
|
|
35
38
|
}>, {
|
|
36
39
|
loading: "lazy" | "eager";
|
|
40
|
+
aspect: "horizontal" | "vertical" | "dynamic";
|
|
37
41
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|