@hywax/cms 2.1.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.nuxt/cms/{input-uplora-image.ts → form-uplora-image.ts} +2 -9
- package/.nuxt/cms/http-statuses.ts +59 -0
- package/.nuxt/cms/index.ts +4 -4
- package/.nuxt/cms.css +3 -3
- package/dist/module.d.mts +5 -2
- package/dist/module.json +1 -1
- package/dist/module.mjs +75 -44
- package/dist/runtime/components/DatePicker.vue +13 -1
- package/dist/runtime/components/FormPanel.vue +2 -0
- package/dist/runtime/components/{InputSeo.d.vue.ts → FormSeo.d.vue.ts} +10 -8
- package/dist/runtime/components/{InputSeo.vue → FormSeo.vue} +18 -8
- package/dist/runtime/components/{InputSeo.vue.d.ts → FormSeo.vue.d.ts} +10 -8
- package/dist/runtime/components/{InputSlug.vue.d.ts → FormSlug.d.vue.ts} +8 -7
- package/dist/runtime/components/{InputSlug.vue → FormSlug.vue} +15 -6
- package/dist/runtime/components/{InputSlug.d.vue.ts → FormSlug.vue.d.ts} +8 -7
- package/dist/runtime/components/FormUploraImage.d.vue.ts +43 -0
- package/dist/runtime/components/FormUploraImage.vue +172 -0
- package/dist/runtime/components/FormUploraImage.vue.d.ts +43 -0
- package/dist/runtime/components/TablePreviewLink.vue +1 -2
- package/dist/runtime/editor/uplora-image/EditorUploraImageNode.vue +2 -2
- package/dist/runtime/server/api/uplora/[id].delete.js +1 -1
- package/dist/runtime/server/api/uplora/index.post.js +2 -4
- package/dist/runtime/server/errors/HttpError.d.ts +8 -0
- package/dist/runtime/server/errors/HttpError.js +7 -0
- package/dist/runtime/server/errors/index.d.ts +1 -1
- package/dist/runtime/server/errors/index.js +1 -1
- package/dist/runtime/server/utils/http.d.ts +43 -0
- package/dist/runtime/server/utils/http.js +86 -0
- package/dist/runtime/server/utils/validation.js +6 -7
- package/dist/runtime/types/index.d.ts +3 -3
- package/dist/runtime/types/index.js +3 -3
- package/package.json +5 -4
- package/.nuxt/cms/http-codes.ts +0 -8
- package/dist/runtime/components/InputUploraImage.d.vue.ts +0 -40
- package/dist/runtime/components/InputUploraImage.vue +0 -181
- package/dist/runtime/components/InputUploraImage.vue.d.ts +0 -40
- package/dist/runtime/server/errors/InternalHttpError.d.ts +0 -8
- package/dist/runtime/server/errors/InternalHttpError.js +0 -7
- package/dist/runtime/server/utils/errors.d.ts +0 -8
- package/dist/runtime/server/utils/errors.js +0 -57
- package/dist/runtime/server/utils/httpHandler.d.ts +0 -10
- package/dist/runtime/server/utils/httpHandler.js +0 -15
- /package/.nuxt/cms/{input-seo.ts → form-seo.ts} +0 -0
- /package/.nuxt/cms/{input-slug.ts → form-slug.ts} +0 -0
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
"slots": {
|
|
3
|
-
"root": "relative w-full
|
|
4
|
-
"
|
|
5
|
-
"imageActions": "absolute top-4 right-4 flex gap-2",
|
|
6
|
-
"imageActionsWarning": "absolute top-0 right-0 size-6 bg-linear-to-bl from-error/90 to-transparent rounded-bl-lg text-error flex items-center justify-center",
|
|
3
|
+
"root": "relative w-full gap-1",
|
|
4
|
+
"base": "rounded-md overflow-hidden border-1 border-default bg-default aspect-3/2",
|
|
7
5
|
"uploader": "flex flex-col items-center justify-center w-full h-full",
|
|
8
6
|
"uploaderPendingIcon": "size-6 animate-spin",
|
|
9
7
|
"uploaderIdleButton": "cursor-pointer h-full w-full",
|
|
@@ -17,11 +15,6 @@ export default {
|
|
|
17
15
|
"true": {
|
|
18
16
|
"root": "cursor-not-allowed opacity-75"
|
|
19
17
|
}
|
|
20
|
-
},
|
|
21
|
-
"uploaded": {
|
|
22
|
-
"false": {
|
|
23
|
-
"root": "border-dashed"
|
|
24
|
-
}
|
|
25
18
|
}
|
|
26
19
|
}
|
|
27
20
|
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export type HttpStatusKey
|
|
2
|
+
= ('badRequest' | 'unauthorized' | 'forbidden' | 'notFound' | 'alreadyExists' | 'notAllowed' | 'internalServerError' | 'dbNotDefined' | 'dbConnectionRefused' | 'dbInsertFailed' | 'badGateway' | 'serviceUnavailable')
|
|
3
|
+
& string
|
|
4
|
+
|
|
5
|
+
export interface HttpStatus {
|
|
6
|
+
code: number
|
|
7
|
+
message: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const httpStatuses: Record<HttpStatusKey, HttpStatus> = {
|
|
11
|
+
"badRequest": {
|
|
12
|
+
"code": 400,
|
|
13
|
+
"message": "Неверный запрос"
|
|
14
|
+
},
|
|
15
|
+
"unauthorized": {
|
|
16
|
+
"code": 401,
|
|
17
|
+
"message": "Неавторизован"
|
|
18
|
+
},
|
|
19
|
+
"forbidden": {
|
|
20
|
+
"code": 403,
|
|
21
|
+
"message": "Доступ запрещен"
|
|
22
|
+
},
|
|
23
|
+
"notFound": {
|
|
24
|
+
"code": 404,
|
|
25
|
+
"message": "Не найдено"
|
|
26
|
+
},
|
|
27
|
+
"alreadyExists": {
|
|
28
|
+
"code": 409,
|
|
29
|
+
"message": "Уже существует"
|
|
30
|
+
},
|
|
31
|
+
"notAllowed": {
|
|
32
|
+
"code": 405,
|
|
33
|
+
"message": "Не разрешено"
|
|
34
|
+
},
|
|
35
|
+
"internalServerError": {
|
|
36
|
+
"code": 500,
|
|
37
|
+
"message": "Внутренняя ошибка сервера"
|
|
38
|
+
},
|
|
39
|
+
"dbNotDefined": {
|
|
40
|
+
"code": 500,
|
|
41
|
+
"message": "База данных не определена"
|
|
42
|
+
},
|
|
43
|
+
"dbConnectionRefused": {
|
|
44
|
+
"code": 500,
|
|
45
|
+
"message": "Не удалось подключиться к базе данных"
|
|
46
|
+
},
|
|
47
|
+
"dbInsertFailed": {
|
|
48
|
+
"code": 500,
|
|
49
|
+
"message": "Ошибка при добавлении данных в базу данных"
|
|
50
|
+
},
|
|
51
|
+
"badGateway": {
|
|
52
|
+
"code": 502,
|
|
53
|
+
"message": "Ошибка шлюза"
|
|
54
|
+
},
|
|
55
|
+
"serviceUnavailable": {
|
|
56
|
+
"code": 503,
|
|
57
|
+
"message": "Сервис недоступен"
|
|
58
|
+
}
|
|
59
|
+
}
|
package/.nuxt/cms/index.ts
CHANGED
|
@@ -3,10 +3,10 @@ export { default as ButtonDeleteConfirm } from './button-delete-confirm'
|
|
|
3
3
|
export { default as FormPanel } from './form-panel'
|
|
4
4
|
export { default as FormPanelAsideSection } from './form-panel-aside-section'
|
|
5
5
|
export { default as FormPanelSection } from './form-panel-section'
|
|
6
|
-
export { default as
|
|
7
|
-
export { default as
|
|
6
|
+
export { default as FormSeo } from './form-seo'
|
|
7
|
+
export { default as FormSlug } from './form-slug'
|
|
8
|
+
export { default as FormUploraImage } from './form-uplora-image'
|
|
8
9
|
export { default as ModalConfirm } from './modal-confirm'
|
|
9
10
|
export { default as TablePanel } from './table-panel'
|
|
10
11
|
export { default as TableSearchInput } from './table-search-input'
|
|
11
|
-
export { default as UploraImage } from './uplora-image'
|
|
12
|
-
export { default as inputSlug } from './input-slug'
|
|
12
|
+
export { default as UploraImage } from './uplora-image'
|
package/.nuxt/cms.css
CHANGED
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
@source "./cms/form-panel.ts";
|
|
5
5
|
@source "./cms/editor-full.ts";
|
|
6
6
|
@source "./cms/form-panel-section.ts";
|
|
7
|
-
@source "./cms/
|
|
8
|
-
@source "./cms/
|
|
9
|
-
@source "./cms/input-uplora-image.ts";
|
|
7
|
+
@source "./cms/form-slug.ts";
|
|
8
|
+
@source "./cms/form-uplora-image.ts";
|
|
10
9
|
@source "./cms/uplora-image.ts";
|
|
10
|
+
@source "./cms/form-seo.ts";
|
|
11
11
|
@source "./cms/table-panel.ts";
|
|
12
12
|
@source "./cms/modal-confirm.ts";
|
|
13
13
|
@source "./cms/table-search-input.ts";
|
package/dist/module.d.mts
CHANGED
|
@@ -34,9 +34,12 @@ interface CMSCoreOptions {
|
|
|
34
34
|
*/
|
|
35
35
|
unovis?: boolean;
|
|
36
36
|
/**
|
|
37
|
-
* HTTP
|
|
37
|
+
* HTTP statuses
|
|
38
38
|
*/
|
|
39
|
-
|
|
39
|
+
httpStatuses?: Record<string, {
|
|
40
|
+
code: number;
|
|
41
|
+
message: string;
|
|
42
|
+
}>;
|
|
40
43
|
/**
|
|
41
44
|
* Component detection
|
|
42
45
|
*/
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { createResolver, addComponentsDir, addPlugin, addImports, addImportsDir, addServerImports, addServerImportsDir, addServerHandler, addTypeTemplate, addTemplate, addServerTemplate, updateTemplates, getLayerDirectories, logger, defineNuxtModule } from '@nuxt/kit';
|
|
2
2
|
import { fileURLToPath } from 'node:url';
|
|
3
3
|
import { dirname, join } from 'pathe';
|
|
4
|
-
import { snakeCase, pascalCase, kebabCase } from 'scule';
|
|
5
4
|
import { readFile, writeFile } from 'node:fs/promises';
|
|
6
5
|
import { defu } from 'defu';
|
|
6
|
+
import { pascalCase, kebabCase } from 'scule';
|
|
7
7
|
import { globSync } from 'tinyglobby';
|
|
8
8
|
|
|
9
9
|
const name = "@hywax/cms";
|
|
10
|
-
const version = "
|
|
10
|
+
const version = "3.0.0";
|
|
11
11
|
|
|
12
12
|
function createContext(options, nuxt) {
|
|
13
13
|
const { resolve } = createResolver(import.meta.url);
|
|
@@ -30,15 +30,55 @@ const defaultModuleOptions = {
|
|
|
30
30
|
envPrefix: "APP_",
|
|
31
31
|
unovis: false,
|
|
32
32
|
componentDetection: true,
|
|
33
|
-
|
|
34
|
-
badRequest:
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
33
|
+
httpStatuses: {
|
|
34
|
+
badRequest: {
|
|
35
|
+
code: 400,
|
|
36
|
+
message: "\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u0437\u0430\u043F\u0440\u043E\u0441"
|
|
37
|
+
},
|
|
38
|
+
unauthorized: {
|
|
39
|
+
code: 401,
|
|
40
|
+
message: "\u041D\u0435\u0430\u0432\u0442\u043E\u0440\u0438\u0437\u043E\u0432\u0430\u043D"
|
|
41
|
+
},
|
|
42
|
+
forbidden: {
|
|
43
|
+
code: 403,
|
|
44
|
+
message: "\u0414\u043E\u0441\u0442\u0443\u043F \u0437\u0430\u043F\u0440\u0435\u0449\u0435\u043D"
|
|
45
|
+
},
|
|
46
|
+
notFound: {
|
|
47
|
+
code: 404,
|
|
48
|
+
message: "\u041D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D\u043E"
|
|
49
|
+
},
|
|
50
|
+
alreadyExists: {
|
|
51
|
+
code: 409,
|
|
52
|
+
message: "\u0423\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442"
|
|
53
|
+
},
|
|
54
|
+
notAllowed: {
|
|
55
|
+
code: 405,
|
|
56
|
+
message: "\u041D\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043D\u043E"
|
|
57
|
+
},
|
|
58
|
+
internalServerError: {
|
|
59
|
+
code: 500,
|
|
60
|
+
message: "\u0412\u043D\u0443\u0442\u0440\u0435\u043D\u043D\u044F\u044F \u043E\u0448\u0438\u0431\u043A\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0430"
|
|
61
|
+
},
|
|
62
|
+
dbNotDefined: {
|
|
63
|
+
code: 500,
|
|
64
|
+
message: "\u0411\u0430\u0437\u0430 \u0434\u0430\u043D\u043D\u044B\u0445 \u043D\u0435 \u043E\u043F\u0440\u0435\u0434\u0435\u043B\u0435\u043D\u0430"
|
|
65
|
+
},
|
|
66
|
+
dbConnectionRefused: {
|
|
67
|
+
code: 500,
|
|
68
|
+
message: "\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0438\u0442\u044C\u0441\u044F \u043A \u0431\u0430\u0437\u0435 \u0434\u0430\u043D\u043D\u044B\u0445"
|
|
69
|
+
},
|
|
70
|
+
dbInsertFailed: {
|
|
71
|
+
code: 500,
|
|
72
|
+
message: "\u041E\u0448\u0438\u0431\u043A\u0430 \u043F\u0440\u0438 \u0434\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u0438\u0438 \u0434\u0430\u043D\u043D\u044B\u0445 \u0432 \u0431\u0430\u0437\u0443 \u0434\u0430\u043D\u043D\u044B\u0445"
|
|
73
|
+
},
|
|
74
|
+
badGateway: {
|
|
75
|
+
code: 502,
|
|
76
|
+
message: "\u041E\u0448\u0438\u0431\u043A\u0430 \u0448\u043B\u044E\u0437\u0430"
|
|
77
|
+
},
|
|
78
|
+
serviceUnavailable: {
|
|
79
|
+
code: 503,
|
|
80
|
+
message: "\u0421\u0435\u0440\u0432\u0438\u0441 \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D"
|
|
81
|
+
}
|
|
42
82
|
},
|
|
43
83
|
formats: {
|
|
44
84
|
serverDateTime: "YYYY-MM-DD HH:mm:ss"
|
|
@@ -51,17 +91,9 @@ const defaultModuleOptions = {
|
|
|
51
91
|
}
|
|
52
92
|
};
|
|
53
93
|
|
|
54
|
-
function transformHttpCodes(httpCodes) {
|
|
55
|
-
return Object.entries(httpCodes).map(([code, value]) => ({
|
|
56
|
-
code: `HTTP_CODE_${snakeCase(code).toUpperCase()}`,
|
|
57
|
-
value
|
|
58
|
-
}));
|
|
59
|
-
}
|
|
60
|
-
|
|
61
94
|
function prepareAutoImports({ resolve, options, nuxt }) {
|
|
62
95
|
const cmsConfigPath = resolve(nuxt.options.buildDir, "cms/config");
|
|
63
|
-
const
|
|
64
|
-
const httpCodesImports = transformHttpCodes(options.httpCodes).map(({ code }) => ({ name: code, from: httpCodesPath }));
|
|
96
|
+
const httpStatusesPath = resolve(nuxt.options.buildDir, "cms/http-statuses");
|
|
65
97
|
addComponentsDir({
|
|
66
98
|
path: resolve("./runtime/components/prose"),
|
|
67
99
|
prefix: "Prose",
|
|
@@ -77,7 +109,6 @@ function prepareAutoImports({ resolve, options, nuxt }) {
|
|
|
77
109
|
addPlugin(resolve("./runtime/plugins/api"));
|
|
78
110
|
addPlugin(resolve("./runtime/plugins/zod"));
|
|
79
111
|
addImports([
|
|
80
|
-
...httpCodesImports,
|
|
81
112
|
{ name: "getCmsConfig", from: cmsConfigPath }
|
|
82
113
|
]);
|
|
83
114
|
addImportsDir([
|
|
@@ -85,7 +116,6 @@ function prepareAutoImports({ resolve, options, nuxt }) {
|
|
|
85
116
|
resolve("./runtime/composables")
|
|
86
117
|
]);
|
|
87
118
|
addServerImports([
|
|
88
|
-
...httpCodesImports,
|
|
89
119
|
{ name: "getCmsConfig", from: cmsConfigPath }
|
|
90
120
|
]);
|
|
91
121
|
addServerImportsDir([
|
|
@@ -93,7 +123,7 @@ function prepareAutoImports({ resolve, options, nuxt }) {
|
|
|
93
123
|
resolve("./runtime/server/utils")
|
|
94
124
|
]);
|
|
95
125
|
nuxt.options.nitro.alias ||= {};
|
|
96
|
-
nuxt.options.nitro.alias["#cms/http-
|
|
126
|
+
nuxt.options.nitro.alias["#cms/http-statuses"] = httpStatusesPath;
|
|
97
127
|
nuxt.options.alias["#cms"] = resolve("./runtime");
|
|
98
128
|
}
|
|
99
129
|
|
|
@@ -146,7 +176,8 @@ const icons = {
|
|
|
146
176
|
strikethrough: "i-lucide-strikethrough",
|
|
147
177
|
code: "i-lucide-code",
|
|
148
178
|
image: "i-lucide-image",
|
|
149
|
-
editLine: "i-lucide-pen-line"
|
|
179
|
+
editLine: "i-lucide-pen-line",
|
|
180
|
+
save: "i-lucide-cloud-check"
|
|
150
181
|
};
|
|
151
182
|
|
|
152
183
|
async function buildComponentDependencyGraph(componentDir, componentPattern) {
|
|
@@ -437,24 +468,22 @@ const formPanelSection = {
|
|
|
437
468
|
}
|
|
438
469
|
};
|
|
439
470
|
|
|
440
|
-
const
|
|
471
|
+
const formSeo = {
|
|
441
472
|
slots: {
|
|
442
473
|
root: "flex flex-col gap-4"
|
|
443
474
|
}
|
|
444
475
|
};
|
|
445
476
|
|
|
446
|
-
const
|
|
477
|
+
const formSlug = {
|
|
447
478
|
slots: {
|
|
448
479
|
root: "flex flex-col gap-4"
|
|
449
480
|
}
|
|
450
481
|
};
|
|
451
482
|
|
|
452
|
-
const
|
|
483
|
+
const formUploraImage = {
|
|
453
484
|
slots: {
|
|
454
|
-
root: "relative w-full
|
|
455
|
-
|
|
456
|
-
imageActions: "absolute top-4 right-4 flex gap-2",
|
|
457
|
-
imageActionsWarning: "absolute top-0 right-0 size-6 bg-linear-to-bl from-error/90 to-transparent rounded-bl-lg text-error flex items-center justify-center",
|
|
485
|
+
root: "relative w-full gap-1",
|
|
486
|
+
base: "rounded-md overflow-hidden border-1 border-default bg-default aspect-3/2",
|
|
458
487
|
uploader: "flex flex-col items-center justify-center w-full h-full",
|
|
459
488
|
uploaderPendingIcon: "size-6 animate-spin",
|
|
460
489
|
uploaderIdleButton: "cursor-pointer h-full w-full",
|
|
@@ -468,11 +497,6 @@ const inputUploraImage = {
|
|
|
468
497
|
true: {
|
|
469
498
|
root: "cursor-not-allowed opacity-75"
|
|
470
499
|
}
|
|
471
|
-
},
|
|
472
|
-
uploaded: {
|
|
473
|
-
false: {
|
|
474
|
-
root: "border-dashed"
|
|
475
|
-
}
|
|
476
500
|
}
|
|
477
501
|
}
|
|
478
502
|
};
|
|
@@ -515,13 +539,13 @@ const theme = {
|
|
|
515
539
|
FormPanel: formPanel,
|
|
516
540
|
FormPanelAsideSection: formPanelAsideSection,
|
|
517
541
|
FormPanelSection: formPanelSection,
|
|
518
|
-
|
|
519
|
-
|
|
542
|
+
FormSeo: formSeo,
|
|
543
|
+
FormSlug: formSlug,
|
|
544
|
+
FormUploraImage: formUploraImage,
|
|
520
545
|
ModalConfirm: modalConfirm,
|
|
521
546
|
TablePanel: tablePanel,
|
|
522
547
|
TableSearchInput: tableSearchInput,
|
|
523
|
-
UploraImage: uploraImage$1
|
|
524
|
-
inputSlug: inputSlug
|
|
548
|
+
UploraImage: uploraImage$1
|
|
525
549
|
};
|
|
526
550
|
|
|
527
551
|
const themeEditor = {
|
|
@@ -694,12 +718,19 @@ export {}
|
|
|
694
718
|
`
|
|
695
719
|
});
|
|
696
720
|
templates.push({
|
|
697
|
-
filename: "cms/http-
|
|
721
|
+
filename: "cms/http-statuses.ts",
|
|
698
722
|
write: true,
|
|
699
723
|
getContents: () => {
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
724
|
+
return `export type HttpStatusKey
|
|
725
|
+
= (${Object.keys(options.httpStatuses).map((key) => `'${key}'`).join(" | ")})
|
|
726
|
+
& string
|
|
727
|
+
|
|
728
|
+
export interface HttpStatus {
|
|
729
|
+
code: number
|
|
730
|
+
message: string
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
export const httpStatuses: Record<HttpStatusKey, HttpStatus> = ${JSON.stringify(options.httpStatuses, null, 2)}
|
|
703
734
|
`;
|
|
704
735
|
}
|
|
705
736
|
});
|
|
@@ -12,7 +12,19 @@
|
|
|
12
12
|
:size="size"
|
|
13
13
|
v-bind="ariaAttrs"
|
|
14
14
|
>
|
|
15
|
-
<span class="truncate">{{ textValue }}
|
|
15
|
+
<span class="truncate">{{ textValue }}</span>
|
|
16
|
+
|
|
17
|
+
<template #trailing>
|
|
18
|
+
<UButton
|
|
19
|
+
:icon="appConfig.ui.icons.close"
|
|
20
|
+
as="div"
|
|
21
|
+
variant="link"
|
|
22
|
+
size="sm"
|
|
23
|
+
color="neutral"
|
|
24
|
+
class="p-0 ms-auto"
|
|
25
|
+
@click.prevent.stop="$emit('update:modelValue', void 0)"
|
|
26
|
+
/>
|
|
27
|
+
</template>
|
|
16
28
|
</UButton>
|
|
17
29
|
|
|
18
30
|
<template #content>
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
<UButton
|
|
16
16
|
type="submit"
|
|
17
17
|
label="Сохранить"
|
|
18
|
+
:icon="appConfig.ui.icons.save"
|
|
18
19
|
:form="formId"
|
|
19
20
|
:loading="loading"
|
|
20
21
|
loading-auto
|
|
@@ -28,6 +29,7 @@
|
|
|
28
29
|
:state="state"
|
|
29
30
|
:schema="schema"
|
|
30
31
|
:class="ui.form({ class: props.ui?.form })"
|
|
32
|
+
:validate-on="['change']"
|
|
31
33
|
@submit="submitHandler"
|
|
32
34
|
>
|
|
33
35
|
<div :class="ui.body({ class: props.ui?.body })">
|
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
import type { AppConfig } from '@nuxt/schema';
|
|
2
2
|
import type { ComponentConfig, SEO } from '../types';
|
|
3
|
-
import theme from '#build/cms/
|
|
4
|
-
type
|
|
5
|
-
export interface
|
|
6
|
-
as?: any;
|
|
3
|
+
import theme from '#build/cms/form-seo';
|
|
4
|
+
type FormSeo = ComponentConfig<typeof theme, AppConfig, 'formSeo'>;
|
|
5
|
+
export interface FormSeoProps {
|
|
7
6
|
class?: any;
|
|
8
|
-
|
|
7
|
+
name?: string;
|
|
8
|
+
ui?: FormSeo['slots'];
|
|
9
9
|
}
|
|
10
10
|
declare const _default: typeof __VLS_export;
|
|
11
11
|
export default _default;
|
|
12
|
-
declare const __VLS_export: import("vue").DefineComponent<
|
|
12
|
+
declare const __VLS_export: import("vue").DefineComponent<FormSeoProps & {
|
|
13
13
|
modelValue?: SEO;
|
|
14
14
|
}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
15
15
|
"update:modelValue": (value: SEO) => any;
|
|
16
|
-
}, string, import("vue").PublicProps, Readonly<
|
|
16
|
+
}, string, import("vue").PublicProps, Readonly<FormSeoProps & {
|
|
17
17
|
modelValue?: SEO;
|
|
18
18
|
}> & Readonly<{
|
|
19
19
|
"onUpdate:modelValue"?: ((value: SEO) => any) | undefined;
|
|
20
|
-
}>, {
|
|
20
|
+
}>, {
|
|
21
|
+
name: string;
|
|
22
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
2
|
+
<UForm
|
|
3
|
+
:name="name"
|
|
4
|
+
:schema="schema"
|
|
5
|
+
:class="ui.root({ class: [props.ui?.root, props.class] })"
|
|
6
|
+
:validate-on="['change']"
|
|
7
|
+
nested
|
|
8
|
+
>
|
|
3
9
|
<UFormField
|
|
4
|
-
name="
|
|
10
|
+
name="title"
|
|
5
11
|
label="Заголовок"
|
|
6
12
|
:hint="`${model.title.length}/60`"
|
|
7
13
|
:ui="{ hint: 'text-xs' }"
|
|
@@ -21,7 +27,7 @@
|
|
|
21
27
|
</UFormField>
|
|
22
28
|
|
|
23
29
|
<UFormField
|
|
24
|
-
name="
|
|
30
|
+
name="description"
|
|
25
31
|
label="Описание"
|
|
26
32
|
:hint="`${model.description.length}/160`"
|
|
27
33
|
:ui="{ hint: 'text-xs' }"
|
|
@@ -44,22 +50,22 @@
|
|
|
44
50
|
/>
|
|
45
51
|
</template>
|
|
46
52
|
</UFormField>
|
|
47
|
-
</
|
|
53
|
+
</UForm>
|
|
48
54
|
</template>
|
|
49
55
|
|
|
50
56
|
<script>
|
|
51
|
-
import theme from "#build/cms/
|
|
57
|
+
import theme from "#build/cms/form-seo";
|
|
52
58
|
import { useAppConfig } from "#imports";
|
|
53
|
-
import { Primitive } from "reka-ui";
|
|
54
59
|
import { computed } from "vue";
|
|
60
|
+
import { z } from "zod";
|
|
55
61
|
import { useSeoStats } from "../composables/useSeoStats";
|
|
56
62
|
import { tv } from "../tv";
|
|
57
63
|
</script>
|
|
58
64
|
|
|
59
65
|
<script setup>
|
|
60
66
|
const props = defineProps({
|
|
61
|
-
as: { type: null, required: false },
|
|
62
67
|
class: { type: null, required: false },
|
|
68
|
+
name: { type: String, required: false, default: "seo" },
|
|
63
69
|
ui: { type: null, required: false }
|
|
64
70
|
});
|
|
65
71
|
const model = defineModel({ type: Object, ...{
|
|
@@ -69,6 +75,10 @@ const model = defineModel({ type: Object, ...{
|
|
|
69
75
|
})
|
|
70
76
|
} });
|
|
71
77
|
const appConfig = useAppConfig();
|
|
78
|
+
const schema = z.object({
|
|
79
|
+
title: z.string().min(1),
|
|
80
|
+
description: z.string().min(1)
|
|
81
|
+
});
|
|
72
82
|
const { title, description } = useSeoStats(model);
|
|
73
|
-
const ui = computed(() => tv({ extend: tv(theme), ...appConfig.cms?.
|
|
83
|
+
const ui = computed(() => tv({ extend: tv(theme), ...appConfig.cms?.formSeo || {} })());
|
|
74
84
|
</script>
|
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
import type { AppConfig } from '@nuxt/schema';
|
|
2
2
|
import type { ComponentConfig, SEO } from '../types';
|
|
3
|
-
import theme from '#build/cms/
|
|
4
|
-
type
|
|
5
|
-
export interface
|
|
6
|
-
as?: any;
|
|
3
|
+
import theme from '#build/cms/form-seo';
|
|
4
|
+
type FormSeo = ComponentConfig<typeof theme, AppConfig, 'formSeo'>;
|
|
5
|
+
export interface FormSeoProps {
|
|
7
6
|
class?: any;
|
|
8
|
-
|
|
7
|
+
name?: string;
|
|
8
|
+
ui?: FormSeo['slots'];
|
|
9
9
|
}
|
|
10
10
|
declare const _default: typeof __VLS_export;
|
|
11
11
|
export default _default;
|
|
12
|
-
declare const __VLS_export: import("vue").DefineComponent<
|
|
12
|
+
declare const __VLS_export: import("vue").DefineComponent<FormSeoProps & {
|
|
13
13
|
modelValue?: SEO;
|
|
14
14
|
}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
15
15
|
"update:modelValue": (value: SEO) => any;
|
|
16
|
-
}, string, import("vue").PublicProps, Readonly<
|
|
16
|
+
}, string, import("vue").PublicProps, Readonly<FormSeoProps & {
|
|
17
17
|
modelValue?: SEO;
|
|
18
18
|
}> & Readonly<{
|
|
19
19
|
"onUpdate:modelValue"?: ((value: SEO) => any) | undefined;
|
|
20
|
-
}>, {
|
|
20
|
+
}>, {
|
|
21
|
+
name: string;
|
|
22
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
@@ -1,33 +1,34 @@
|
|
|
1
1
|
import type { AppConfig } from '@nuxt/schema';
|
|
2
2
|
import type { InputProps } from '@nuxt/ui';
|
|
3
3
|
import type { ComponentConfig } from '../types';
|
|
4
|
-
import theme from '#build/cms/
|
|
5
|
-
type
|
|
6
|
-
export interface
|
|
4
|
+
import theme from '#build/cms/form-slug';
|
|
5
|
+
type FormSlug = ComponentConfig<typeof theme, AppConfig, 'formSlug'>;
|
|
6
|
+
export interface FormSlugProps {
|
|
7
|
+
name?: string;
|
|
7
8
|
regenerate?: boolean;
|
|
8
9
|
label?: string;
|
|
9
10
|
titleKey?: string;
|
|
10
11
|
slugKey?: string;
|
|
11
12
|
inputProps?: Omit<InputProps, 'modelModifiers'>;
|
|
12
|
-
as?: any;
|
|
13
13
|
class?: any;
|
|
14
|
-
ui?:
|
|
14
|
+
ui?: FormSlug['slots'];
|
|
15
15
|
}
|
|
16
16
|
declare const _default: typeof __VLS_export;
|
|
17
17
|
export default _default;
|
|
18
|
-
declare const __VLS_export: import("vue").DefineComponent<
|
|
18
|
+
declare const __VLS_export: import("vue").DefineComponent<FormSlugProps & {
|
|
19
19
|
title?: string;
|
|
20
20
|
slug?: string;
|
|
21
21
|
}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
22
22
|
"update:title": (value: string | undefined) => any;
|
|
23
23
|
"update:slug": (value: string | undefined) => any;
|
|
24
|
-
}, string, import("vue").PublicProps, Readonly<
|
|
24
|
+
}, string, import("vue").PublicProps, Readonly<FormSlugProps & {
|
|
25
25
|
title?: string;
|
|
26
26
|
slug?: string;
|
|
27
27
|
}> & Readonly<{
|
|
28
28
|
"onUpdate:title"?: ((value: string | undefined) => any) | undefined;
|
|
29
29
|
"onUpdate:slug"?: ((value: string | undefined) => any) | undefined;
|
|
30
30
|
}>, {
|
|
31
|
+
name: string;
|
|
31
32
|
titleKey: string;
|
|
32
33
|
slugKey: string;
|
|
33
34
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
2
|
+
<UForm
|
|
3
|
+
:schema="schema"
|
|
4
|
+
:class="ui.root({ class: [props.ui?.root, props.class] })"
|
|
5
|
+
:validate-on="['change']"
|
|
6
|
+
nested
|
|
7
|
+
>
|
|
3
8
|
<UFormField
|
|
4
9
|
:label="label"
|
|
5
10
|
:name="titleKey"
|
|
@@ -34,32 +39,36 @@
|
|
|
34
39
|
</template>
|
|
35
40
|
</UInput>
|
|
36
41
|
</UFormField>
|
|
37
|
-
</
|
|
42
|
+
</UForm>
|
|
38
43
|
</template>
|
|
39
44
|
|
|
40
45
|
<script>
|
|
41
|
-
import theme from "#build/cms/
|
|
46
|
+
import theme from "#build/cms/form-slug";
|
|
42
47
|
import { useAppConfig } from "#imports";
|
|
43
|
-
import { Primitive } from "reka-ui";
|
|
44
48
|
import { computed, ref, watch } from "vue";
|
|
49
|
+
import { z } from "zod";
|
|
45
50
|
import { tv } from "../tv";
|
|
46
51
|
import { slugify } from "../utils/slugify";
|
|
47
52
|
</script>
|
|
48
53
|
|
|
49
54
|
<script setup>
|
|
50
55
|
const props = defineProps({
|
|
56
|
+
name: { type: String, required: false, default: "slug" },
|
|
51
57
|
regenerate: { type: Boolean, required: false },
|
|
52
58
|
label: { type: String, required: false },
|
|
53
59
|
titleKey: { type: String, required: false, default: "title" },
|
|
54
60
|
slugKey: { type: String, required: false, default: "slug" },
|
|
55
61
|
inputProps: { type: Object, required: false },
|
|
56
|
-
as: { type: null, required: false },
|
|
57
62
|
class: { type: null, required: false },
|
|
58
63
|
ui: { type: null, required: false }
|
|
59
64
|
});
|
|
60
65
|
const title = defineModel("title", { type: String });
|
|
61
66
|
const slug = defineModel("slug", { type: String });
|
|
62
67
|
const appConfig = useAppConfig();
|
|
68
|
+
const schema = z.object({
|
|
69
|
+
title: z.string().min(1),
|
|
70
|
+
slug: z.string().min(1)
|
|
71
|
+
});
|
|
63
72
|
const isRegenerate = ref(props.regenerate);
|
|
64
73
|
watch(title, () => {
|
|
65
74
|
if (!isRegenerate.value) {
|
|
@@ -67,5 +76,5 @@ watch(title, () => {
|
|
|
67
76
|
}
|
|
68
77
|
slug.value = slugify(title.value);
|
|
69
78
|
});
|
|
70
|
-
const ui = computed(() => tv({ extend: tv(theme), ...appConfig.cms?.
|
|
79
|
+
const ui = computed(() => tv({ extend: tv(theme), ...appConfig.cms?.formSlug || {} })());
|
|
71
80
|
</script>
|
|
@@ -1,33 +1,34 @@
|
|
|
1
1
|
import type { AppConfig } from '@nuxt/schema';
|
|
2
2
|
import type { InputProps } from '@nuxt/ui';
|
|
3
3
|
import type { ComponentConfig } from '../types';
|
|
4
|
-
import theme from '#build/cms/
|
|
5
|
-
type
|
|
6
|
-
export interface
|
|
4
|
+
import theme from '#build/cms/form-slug';
|
|
5
|
+
type FormSlug = ComponentConfig<typeof theme, AppConfig, 'formSlug'>;
|
|
6
|
+
export interface FormSlugProps {
|
|
7
|
+
name?: string;
|
|
7
8
|
regenerate?: boolean;
|
|
8
9
|
label?: string;
|
|
9
10
|
titleKey?: string;
|
|
10
11
|
slugKey?: string;
|
|
11
12
|
inputProps?: Omit<InputProps, 'modelModifiers'>;
|
|
12
|
-
as?: any;
|
|
13
13
|
class?: any;
|
|
14
|
-
ui?:
|
|
14
|
+
ui?: FormSlug['slots'];
|
|
15
15
|
}
|
|
16
16
|
declare const _default: typeof __VLS_export;
|
|
17
17
|
export default _default;
|
|
18
|
-
declare const __VLS_export: import("vue").DefineComponent<
|
|
18
|
+
declare const __VLS_export: import("vue").DefineComponent<FormSlugProps & {
|
|
19
19
|
title?: string;
|
|
20
20
|
slug?: string;
|
|
21
21
|
}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
22
22
|
"update:title": (value: string | undefined) => any;
|
|
23
23
|
"update:slug": (value: string | undefined) => any;
|
|
24
|
-
}, string, import("vue").PublicProps, Readonly<
|
|
24
|
+
}, string, import("vue").PublicProps, Readonly<FormSlugProps & {
|
|
25
25
|
title?: string;
|
|
26
26
|
slug?: string;
|
|
27
27
|
}> & Readonly<{
|
|
28
28
|
"onUpdate:title"?: ((value: string | undefined) => any) | undefined;
|
|
29
29
|
"onUpdate:slug"?: ((value: string | undefined) => any) | undefined;
|
|
30
30
|
}>, {
|
|
31
|
+
name: string;
|
|
31
32
|
titleKey: string;
|
|
32
33
|
slugKey: string;
|
|
33
34
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|