@hywax/cms 3.6.1 → 3.7.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/FormSeo.d.vue.ts +4 -4
- package/dist/runtime/components/FormSeo.vue +25 -13
- package/dist/runtime/components/FormSeo.vue.d.ts +4 -4
- package/dist/runtime/components/FormUploraImage.d.vue.ts +7 -9
- package/dist/runtime/components/FormUploraImage.vue +70 -46
- 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.7.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
|
+
columns: {
|
|
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>;
|
|
@@ -9,10 +9,15 @@
|
|
|
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,23 @@ 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
|
+
model.value = value;
|
|
87
|
+
}
|
|
88
|
+
});
|
|
77
89
|
const appConfig = useAppConfig();
|
|
78
90
|
const schema = z.object({
|
|
79
|
-
title: z.string()
|
|
80
|
-
description: z.string()
|
|
91
|
+
title: z.string(),
|
|
92
|
+
description: z.string()
|
|
81
93
|
});
|
|
82
|
-
const { title, description } = useSeoStats(
|
|
94
|
+
const { title, description } = useSeoStats(() => state.value);
|
|
83
95
|
const ui = computed(() => tv({ extend: tv(theme), ...appConfig.cms?.formSeo || {} })());
|
|
84
96
|
</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,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>;
|
|
@@ -8,17 +8,34 @@
|
|
|
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,72 @@ 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
|
+
model.value = value;
|
|
152
|
+
}
|
|
153
|
+
});
|
|
130
154
|
const appConfig = useAppConfig();
|
|
131
155
|
const schema = z.object({
|
|
132
156
|
image: z.string().min(1),
|
|
133
157
|
alt: z.string().min(1),
|
|
158
|
+
width: z.number(),
|
|
159
|
+
height: z.number(),
|
|
134
160
|
lqip: z.string().min(1).optional(),
|
|
135
|
-
color: z.string().min(1).optional()
|
|
161
|
+
color: z.string().min(1).optional(),
|
|
162
|
+
aspect: z.union([z.literal("horizontal"), z.literal("vertical"), z.literal("dynamic")]).optional()
|
|
136
163
|
});
|
|
137
164
|
const extensions = computed(() => imagesExtensions.filter((extension) => extension !== "jpeg").join(", "));
|
|
138
|
-
const
|
|
139
|
-
|
|
165
|
+
const aspectItems = [
|
|
166
|
+
{ value: "horizontal", label: "\u0413\u043E\u0440\u0438\u0437\u043E\u043D\u0442\u0430\u043B\u044C\u043D\u044B\u0439" },
|
|
167
|
+
{ value: "vertical", label: "\u0412\u0435\u0440\u0442\u0438\u043A\u0430\u043B\u044C\u043D\u044B\u0439" },
|
|
168
|
+
{ value: "dynamic", label: "\u0414\u0438\u043D\u0430\u043C\u0438\u0447\u0435\u0441\u043A\u0438\u0439" }
|
|
169
|
+
];
|
|
140
170
|
const { open, execute: uploadExecute, status: uploadStatus, reset: resetUpload, onUploaded } = useUploraUpload({
|
|
141
171
|
accept: "image/*"
|
|
142
172
|
});
|
|
143
173
|
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
174
|
function resetState() {
|
|
153
|
-
|
|
175
|
+
resetUpload();
|
|
176
|
+
state.value = {
|
|
154
177
|
image: void 0,
|
|
178
|
+
width: void 0,
|
|
179
|
+
height: void 0,
|
|
155
180
|
alt: void 0,
|
|
156
181
|
lqip: void 0,
|
|
157
|
-
color: void 0
|
|
182
|
+
color: void 0,
|
|
183
|
+
aspect: void 0
|
|
158
184
|
};
|
|
159
|
-
emitFormChange();
|
|
160
|
-
emitFormInput();
|
|
161
185
|
}
|
|
162
186
|
onUploaded((file) => {
|
|
163
|
-
|
|
164
|
-
...modelValue.value,
|
|
187
|
+
state.value = {
|
|
165
188
|
image: file.id,
|
|
189
|
+
width: file.width,
|
|
190
|
+
height: file.height,
|
|
191
|
+
alt: void 0,
|
|
166
192
|
lqip: file.lqip,
|
|
167
|
-
color: file.color
|
|
193
|
+
color: file.color,
|
|
194
|
+
aspect: void 0
|
|
168
195
|
};
|
|
169
|
-
emit("upload",
|
|
170
|
-
emitFormChange();
|
|
171
|
-
emitFormInput();
|
|
196
|
+
emit("upload", state.value);
|
|
172
197
|
});
|
|
173
198
|
onDeleted(() => {
|
|
174
|
-
resetUpload();
|
|
175
199
|
resetState();
|
|
176
200
|
emit("delete");
|
|
177
201
|
});
|
|
178
202
|
const ui = computed(() => tv({ extend: tv(theme), ...appConfig.cms?.formUploraImage || {} })({
|
|
179
|
-
disabled: disabled
|
|
203
|
+
disabled: props.disabled
|
|
180
204
|
}));
|
|
181
205
|
</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>;
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
backgroundImage: lqip ? `url(${lqip})` : void 0
|
|
9
9
|
}"
|
|
10
10
|
/>
|
|
11
|
-
<picture :class="ui.picture({ class: props.ui?.picture })">
|
|
11
|
+
<picture :class="ui.picture({ class: props.ui?.picture })" :style="{ aspectRatio }">
|
|
12
12
|
<template v-if="sources.length">
|
|
13
13
|
<source
|
|
14
14
|
v-for="(source, key) in sources"
|
|
@@ -47,6 +47,9 @@ const props = defineProps({
|
|
|
47
47
|
sizes: { type: Array, required: false },
|
|
48
48
|
lqip: { type: String, required: false },
|
|
49
49
|
color: { type: String, required: false },
|
|
50
|
+
aspect: { type: String, required: false, default: "horizontal" },
|
|
51
|
+
width: { type: [Number, String], required: false },
|
|
52
|
+
height: { type: [Number, String], required: false },
|
|
50
53
|
loading: { type: String, required: false, default: "lazy" },
|
|
51
54
|
preload: { type: [Boolean, Object], required: false },
|
|
52
55
|
nonce: { type: String, required: false },
|
|
@@ -90,6 +93,12 @@ const imageAttrs = computed(() => ({
|
|
|
90
93
|
...props.imgAttrs || {},
|
|
91
94
|
...import.meta.server ? { onerror: "this.setAttribute('data-error', 1)" } : {}
|
|
92
95
|
}));
|
|
96
|
+
const aspectRatio = computed(() => {
|
|
97
|
+
if (props.aspect === "dynamic" && props.width && props.height) {
|
|
98
|
+
return Number.parseInt(props.width) / Number(props.height);
|
|
99
|
+
}
|
|
100
|
+
return void 0;
|
|
101
|
+
});
|
|
93
102
|
onMounted(() => {
|
|
94
103
|
if (!imageRef.value) {
|
|
95
104
|
return;
|
|
@@ -108,5 +117,7 @@ onMounted(() => {
|
|
|
108
117
|
emit("error", event);
|
|
109
118
|
};
|
|
110
119
|
});
|
|
111
|
-
const ui = computed(() => tv({ extend: tv(theme), ...appConfig.cms?.uploraImage || {} })(
|
|
120
|
+
const ui = computed(() => tv({ extend: tv(theme), ...appConfig.cms?.uploraImage || {} })({
|
|
121
|
+
aspect: props.aspect
|
|
122
|
+
}));
|
|
112
123
|
</script>
|
|
@@ -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>;
|