@hywax/cms 0.0.3 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/.nuxt/cms/autocomplete-select.ts +5 -0
  2. package/.nuxt/cms/button-copy.ts +5 -0
  3. package/.nuxt/cms/button-delete.ts +5 -0
  4. package/.nuxt/cms/form-panel-aside-section.ts +9 -0
  5. package/.nuxt/cms/form-panel-section.ts +8 -0
  6. package/.nuxt/cms/form-panel.ts +15 -0
  7. package/.nuxt/cms/http-codes.ts +8 -0
  8. package/.nuxt/cms/index.ts +16 -0
  9. package/.nuxt/cms/input-seo.ts +5 -0
  10. package/.nuxt/cms/input-slug.ts +5 -0
  11. package/.nuxt/cms/modal-confirm.ts +5 -0
  12. package/.nuxt/cms/table-cell-preview.ts +9 -0
  13. package/.nuxt/cms/table-cell-seo.ts +5 -0
  14. package/.nuxt/cms/table-cell-user.ts +5 -0
  15. package/.nuxt/cms/table-panel-column-sorting.ts +5 -0
  16. package/.nuxt/cms/table-panel-column-visibility.ts +5 -0
  17. package/.nuxt/cms/table-panel-filters.ts +5 -0
  18. package/.nuxt/cms/table-panel.ts +8 -0
  19. package/cli/templates.mjs +3 -2
  20. package/dist/module.d.mts +22 -13
  21. package/dist/module.json +1 -1
  22. package/dist/module.mjs +261 -35
  23. package/dist/runtime/components/AutocompleteSelect.vue +170 -0
  24. package/dist/runtime/components/AutocompleteSelect.vue.d.ts +42 -0
  25. package/dist/runtime/components/ButtonCopy.vue +40 -0
  26. package/dist/runtime/components/ButtonCopy.vue.d.ts +23 -0
  27. package/dist/runtime/components/ButtonDelete.vue +52 -0
  28. package/dist/runtime/components/ButtonDelete.vue.d.ts +28 -0
  29. package/dist/runtime/components/FormPanel.vue +70 -0
  30. package/dist/runtime/components/FormPanel.vue.d.ts +41 -0
  31. package/dist/runtime/components/FormPanelAsideSection.vue +41 -0
  32. package/dist/runtime/components/FormPanelAsideSection.vue.d.ts +23 -0
  33. package/dist/runtime/components/FormPanelSection.vue +31 -0
  34. package/dist/runtime/components/FormPanelSection.vue.d.ts +20 -0
  35. package/dist/runtime/components/InputSeo.vue +73 -0
  36. package/dist/runtime/components/InputSeo.vue.d.ts +19 -0
  37. package/dist/runtime/components/InputSlug.vue +70 -0
  38. package/dist/runtime/components/InputSlug.vue.d.ts +29 -0
  39. package/dist/runtime/components/ModalConfirm.vue +91 -0
  40. package/dist/runtime/components/ModalConfirm.vue.d.ts +26 -0
  41. package/dist/runtime/components/TableCellPreview.vue +40 -0
  42. package/dist/runtime/components/TableCellPreview.vue.d.ts +18 -0
  43. package/dist/runtime/components/TableCellSeo.vue +34 -0
  44. package/dist/runtime/components/TableCellSeo.vue.d.ts +13 -0
  45. package/dist/runtime/components/TableCellUser.vue +33 -0
  46. package/dist/runtime/components/TableCellUser.vue.d.ts +15 -0
  47. package/dist/runtime/components/TablePanel.vue +153 -0
  48. package/dist/runtime/components/TablePanel.vue.d.ts +50 -0
  49. package/dist/runtime/components/TablePanelColumnSorting.vue +72 -0
  50. package/dist/runtime/components/TablePanelColumnSorting.vue.d.ts +20 -0
  51. package/dist/runtime/components/TablePanelColumnVisibility.vue +49 -0
  52. package/dist/runtime/components/TablePanelColumnVisibility.vue.d.ts +20 -0
  53. package/dist/runtime/components/TablePanelFilters.vue +79 -0
  54. package/dist/runtime/components/TablePanelFilters.vue.d.ts +34 -0
  55. package/dist/runtime/components/prose/UploraImage.vue +8 -3
  56. package/dist/runtime/composables/useDeleteConfirm.d.ts +15 -0
  57. package/dist/runtime/composables/useDeleteConfirm.js +29 -0
  58. package/dist/runtime/composables/useSeoStats.d.ts +2 -2
  59. package/dist/runtime/composables/useSeoStats.js +1 -1
  60. package/dist/runtime/composables/useTable.d.ts +19 -0
  61. package/dist/runtime/composables/useTable.js +90 -0
  62. package/dist/runtime/editor/markdown/index.d.ts +3 -0
  63. package/dist/runtime/editor/markdown/index.js +47 -0
  64. package/dist/runtime/editor/markdown/nodes/callout.d.ts +2 -0
  65. package/dist/runtime/editor/markdown/nodes/callout.js +21 -0
  66. package/dist/runtime/editor/markdown/nodes/uploraImage.d.ts +2 -0
  67. package/dist/runtime/editor/markdown/nodes/uploraImage.js +31 -0
  68. package/dist/runtime/plugins/api.js +1 -4
  69. package/dist/runtime/server/errors/InternalHttpError.d.ts +4 -0
  70. package/dist/runtime/server/errors/InternalHttpError.js +6 -0
  71. package/dist/runtime/server/errors/TimeoutError.d.ts +2 -0
  72. package/dist/runtime/server/errors/TimeoutError.js +2 -0
  73. package/dist/runtime/server/errors/index.d.ts +2 -0
  74. package/dist/runtime/server/errors/index.js +2 -0
  75. package/dist/runtime/server/types/errors.d.ts +8 -0
  76. package/dist/runtime/server/types/errors.js +0 -0
  77. package/dist/runtime/server/types/index.d.ts +1 -0
  78. package/dist/runtime/server/types/index.js +1 -0
  79. package/dist/runtime/server/utils/errors.d.ts +8 -0
  80. package/dist/runtime/server/utils/errors.js +57 -0
  81. package/dist/runtime/server/utils/httpHandler.d.ts +10 -0
  82. package/dist/runtime/server/utils/httpHandler.js +15 -0
  83. package/dist/runtime/server/utils/pagination.d.ts +11 -0
  84. package/dist/runtime/server/utils/pagination.js +12 -0
  85. package/dist/runtime/server/utils/timeout.d.ts +7 -0
  86. package/dist/runtime/server/utils/timeout.js +9 -0
  87. package/dist/runtime/server/utils/validation.d.ts +3 -0
  88. package/dist/runtime/server/utils/validation.js +26 -0
  89. package/dist/runtime/types/index.d.ts +17 -0
  90. package/dist/runtime/types/index.js +17 -0
  91. package/dist/runtime/types/query.d.ts +22 -0
  92. package/dist/runtime/types/query.js +0 -0
  93. package/dist/runtime/types/utils.d.ts +4 -1
  94. package/dist/runtime/utils/avatar.d.ts +1 -0
  95. package/dist/runtime/utils/avatar.js +9 -0
  96. package/dist/runtime/utils/dictionaries.d.ts +4 -0
  97. package/dist/runtime/utils/dictionaries.js +6 -0
  98. package/dist/runtime/utils/image.js +1 -1
  99. package/dist/runtime/utils/index.d.ts +3 -0
  100. package/dist/runtime/utils/index.js +3 -0
  101. package/dist/runtime/utils/slugify.d.ts +1 -0
  102. package/dist/runtime/utils/slugify.js +12 -0
  103. package/dist/types.d.mts +6 -2
  104. package/package.json +12 -7
@@ -0,0 +1,170 @@
1
+ <template>
2
+ <USelectMenu
3
+ v-model:search-term="searchTerm"
4
+ v-model:open="isOpen"
5
+ :model-value="modelValue"
6
+ :items="items"
7
+ :loading="isLoading"
8
+ ignore-filter
9
+ :value-key="valueKey"
10
+ :label-key="labelKey"
11
+ :placeholder="placeholder"
12
+ :content="{ 'data-autocomplete-select-content': autocompleteSelectId }"
13
+ :multiple="multiple"
14
+ @update:model-value="emit('update:modelValue', $event)"
15
+ >
16
+ <template v-if="modelValue && !props.multiple">
17
+ <span class="truncate pointer-events-none">
18
+ {{ getCachedLabel(modelValue) }}
19
+ </span>
20
+ </template>
21
+
22
+ <template v-if="!multiple" #trailing>
23
+ <ButtonClear
24
+ :model-value="modelValue"
25
+ :reset-value="resetValue"
26
+ @update:model-value="$emit('update:modelValue', $event)"
27
+ />
28
+ </template>
29
+ </USelectMenu>
30
+ </template>
31
+
32
+ <script>
33
+ import theme from "#build/cms/autocomplete-select";
34
+ import { computed, ref, refDebounced, shallowRef, toRaw, triggerRef, useAppConfig, useAsyncData, useId, useNuxtData, watch } from "#imports";
35
+ import { useInfiniteScroll, useOffsetPagination } from "@vueuse/core";
36
+ import { tv } from "../utils/tv";
37
+ </script>
38
+
39
+ <script setup>
40
+ const props = defineProps({
41
+ modelValue: { type: null, required: true },
42
+ cacheKey: { type: String, required: true },
43
+ handler: { type: Function, required: true },
44
+ valueKey: { type: null, required: false },
45
+ labelKey: { type: null, required: false },
46
+ multiple: { type: null, required: false },
47
+ placeholder: { type: String, required: false },
48
+ resetValue: { type: [String, Number, Boolean, null], required: false, skipCheck: true },
49
+ class: { type: null, required: false },
50
+ ui: { type: null, required: false }
51
+ });
52
+ const emit = defineEmits(["update:modelValue"]);
53
+ const appConfig = useAppConfig();
54
+ const autocompleteSelectId = `autocomplete-select-${useId()}`;
55
+ const { initialPagination, initialData, initialStatus } = useInitialData();
56
+ const { fetchPage, nextPage, isLastPage, pageDataStatus, allPagesData } = usePageData({
57
+ pagination: initialPagination,
58
+ total: () => initialData.value?.pagination.total ?? 0
59
+ });
60
+ const { searchTerm, searchData, searchStatus } = useSearch({ pagination: initialPagination });
61
+ const { isOpen } = useInfiniteScrollData({ nextPage, isLastPage, fetchPage, searchTerm });
62
+ const isLoading = computed(() => [
63
+ initialStatus.value,
64
+ pageDataStatus.value,
65
+ searchStatus.value
66
+ ].includes("pending"));
67
+ const items = computed(() => {
68
+ if (searchTerm.value) {
69
+ return searchData.value?.items ?? [];
70
+ }
71
+ return [
72
+ ...initialData.value?.items ?? [],
73
+ ...allPagesData.value
74
+ ];
75
+ });
76
+ const { getCachedLabel } = useAutocompleteCache({ items });
77
+ function useInitialData() {
78
+ const initialPagination2 = { page: "1", perPage: "25" };
79
+ const { data: initialData2, status: initialStatus2 } = useAsyncData(
80
+ `autocomplete-select:${props.cacheKey}`,
81
+ () => props.handler({ ...initialPagination2, search: "" }),
82
+ {
83
+ lazy: true,
84
+ getCachedData: (key) => useNuxtData(key).data.value ?? void 0,
85
+ dedupe: "defer"
86
+ }
87
+ );
88
+ return {
89
+ initialPagination: initialPagination2,
90
+ initialData: initialData2,
91
+ initialStatus: initialStatus2
92
+ };
93
+ }
94
+ function usePageData(options) {
95
+ const { currentPage, currentPageSize, next, isLastPage: isLastPage2 } = useOffsetPagination({
96
+ total: options.total,
97
+ pageSize: Number(options.pagination.perPage),
98
+ page: Number(options.pagination.page)
99
+ });
100
+ const { execute: fetchPage2, data: pageData, status: pageDataStatus2 } = useAsyncData(() => {
101
+ return props.handler({
102
+ page: String(currentPage.value),
103
+ perPage: String(currentPageSize.value),
104
+ search: ""
105
+ });
106
+ }, { immediate: false });
107
+ const allPagesData2 = shallowRef([]);
108
+ watch(pageData, (value) => {
109
+ if (value?.items) {
110
+ allPagesData2.value.push(...value.items);
111
+ triggerRef(allPagesData2);
112
+ }
113
+ });
114
+ return { fetchPage: fetchPage2, pageData, nextPage: next, isLastPage: isLastPage2, pageDataStatus: pageDataStatus2, allPagesData: allPagesData2 };
115
+ }
116
+ function useInfiniteScrollData(options) {
117
+ const isOpen2 = ref(false);
118
+ const contentDiv = ref(null);
119
+ watch(isOpen2, (value) => {
120
+ if (value) {
121
+ contentDiv.value = document.querySelector(`
122
+ [data-autocomplete-select-content="${autocompleteSelectId}"] [data-reka-combobox-viewport]
123
+ `) ?? null;
124
+ }
125
+ }, { flush: "post" });
126
+ useInfiniteScroll(contentDiv, () => {
127
+ options.nextPage();
128
+ return fetchPage();
129
+ }, { distance: 10, canLoadMore: () => !searchTerm.value && !isLastPage.value });
130
+ return { isOpen: isOpen2 };
131
+ }
132
+ function useSearch(options) {
133
+ const searchTerm2 = ref("");
134
+ const searchTermDebounced = refDebounced(searchTerm2, 500);
135
+ let controller;
136
+ const { data: searchData2, status: searchStatus2, execute: search } = useAsyncData(() => {
137
+ controller?.abort();
138
+ controller = new AbortController();
139
+ return props.handler({
140
+ ...options.pagination,
141
+ search: searchTermDebounced.value
142
+ }, controller.signal);
143
+ }, { lazy: true, immediate: false });
144
+ watch(searchTermDebounced, (value) => {
145
+ value && search();
146
+ });
147
+ return { searchData: searchData2, searchStatus: searchStatus2, searchTerm: searchTerm2 };
148
+ }
149
+ function useAutocompleteCache(options) {
150
+ const selectedItemCache = shallowRef(/* @__PURE__ */ new Map());
151
+ watch(() => [props.modelValue, options.items.value], ([value]) => {
152
+ const cacheSize = selectedItemCache.value.size;
153
+ (Array.isArray(value) ? value : [value]).forEach((code) => {
154
+ if (!code || selectedItemCache.value.has(code)) {
155
+ return;
156
+ }
157
+ const item = options.items.value.find((item2) => item2[props.valueKey] === code);
158
+ if (item) {
159
+ selectedItemCache.value.set(code, structuredClone(toRaw(item)));
160
+ }
161
+ });
162
+ cacheSize !== selectedItemCache.value.size && triggerRef(selectedItemCache);
163
+ }, { immediate: true });
164
+ const getCachedLabel2 = (value) => {
165
+ return selectedItemCache.value.get(value)?.[props.labelKey] ?? value;
166
+ };
167
+ return { selectedItemCache, getCachedLabel: getCachedLabel2 };
168
+ }
169
+ const _ui = computed(() => tv({ extend: tv(theme), ...appConfig.cms?.autocompleteSelect || {} })());
170
+ </script>
@@ -0,0 +1,42 @@
1
+ import type { AppConfig } from '@nuxt/schema';
2
+ import type { ComponentConfig, Pagination, PaginationQueryRaw } from '../types';
3
+ import type { ResetValue } from './ButtonClear.vue';
4
+ import theme from '#build/cms/autocomplete-select';
5
+ type AutocompleteSelect = ComponentConfig<typeof theme, AppConfig, 'autocompleteSelect'>;
6
+ type PaginationQuery = Required<PaginationQueryRaw>;
7
+ type HandlerQuery = PaginationQuery & {
8
+ search: string;
9
+ };
10
+ export interface AutocompleteSelectProps<T extends object, M extends boolean = true> {
11
+ modelValue: M extends true ? (string | number)[] : (string | number | null | undefined);
12
+ cacheKey: string;
13
+ handler: (query: HandlerQuery, signal?: AbortSignal) => Promise<{
14
+ items: T[];
15
+ pagination: Pagination;
16
+ }>;
17
+ valueKey?: keyof T;
18
+ labelKey?: keyof T;
19
+ multiple?: M;
20
+ placeholder?: string;
21
+ resetValue?: ResetValue;
22
+ class?: any;
23
+ ui?: AutocompleteSelect['slots'];
24
+ }
25
+ export interface AutocompleteSelectEmits {
26
+ 'update:modelValue': [string[]];
27
+ }
28
+ declare const _default: <T extends object, M extends boolean = true>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
29
+ props: __VLS_PrettifyLocal<Pick<Partial<{}> & Omit<{
30
+ readonly "onUpdate:modelValue"?: ((args_0: string[]) => any) | undefined;
31
+ } & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, never>, "onUpdate:modelValue"> & AutocompleteSelectProps<T, M> & Partial<{}>> & import("vue").PublicProps;
32
+ expose(exposed: import("vue").ShallowUnwrapRef<{}>): void;
33
+ attrs: any;
34
+ slots: {};
35
+ emit: (evt: "update:modelValue", args_0: string[]) => void;
36
+ }>) => import("vue").VNode & {
37
+ __ctx?: Awaited<typeof __VLS_setup>;
38
+ };
39
+ export default _default;
40
+ type __VLS_PrettifyLocal<T> = {
41
+ [K in keyof T]: T[K];
42
+ } & {};
@@ -0,0 +1,40 @@
1
+ <template>
2
+ <UTooltip
3
+ :text="tooltipText"
4
+ :content="{ side: 'top' }"
5
+ disable-hoverable-content
6
+ disable-closing-trigger
7
+ >
8
+ <UButton
9
+ :icon="copied ? appConfig.ui.icons.copyCheck : appConfig.ui.icons.copy"
10
+ :color="color"
11
+ :variant="variant"
12
+ :size="size"
13
+ :class="ui.root({ class: [props.ui?.root, props.class] })"
14
+ @click="copy(value)"
15
+ />
16
+ </UTooltip>
17
+ </template>
18
+
19
+ <script>
20
+ import theme from "#build/cms/button-copy";
21
+ import { computed, useAppConfig, useClipboard } from "#imports";
22
+ import { tv } from "../utils/tv";
23
+ </script>
24
+
25
+ <script setup>
26
+ const props = defineProps({
27
+ label: { type: String, required: false, default: "\u0421\u043A\u043E\u043F\u0438\u0440\u043E\u0432\u0430\u0442\u044C" },
28
+ copiedLabel: { type: String, required: false, default: "\u0421\u043A\u043E\u043F\u0438\u0440\u043E\u0432\u0430\u043D\u043E" },
29
+ value: { type: String, required: true },
30
+ color: { type: null, required: false, default: "neutral" },
31
+ variant: { type: null, required: false, default: "link" },
32
+ size: { type: null, required: false, default: "sm" },
33
+ class: { type: null, required: false },
34
+ ui: { type: null, required: false }
35
+ });
36
+ const appConfig = useAppConfig();
37
+ const { copy, copied } = useClipboard();
38
+ const tooltipText = computed(() => copied.value ? props.copiedLabel : props.label);
39
+ const ui = computed(() => tv({ extend: tv(theme), ...appConfig.cms?.buttonCopy || {} })());
40
+ </script>
@@ -0,0 +1,23 @@
1
+ import type { AppConfig } from '@nuxt/schema';
2
+ import type { ButtonProps } from '@nuxt/ui';
3
+ import type { ComponentConfig } from '../types';
4
+ import theme from '#build/cms/button-copy';
5
+ type ButtonCopy = ComponentConfig<typeof theme, AppConfig, 'buttonCopy'>;
6
+ export interface ButtonCopyProps {
7
+ label?: string;
8
+ copiedLabel?: string;
9
+ value: string;
10
+ color?: ButtonProps['color'];
11
+ variant?: ButtonProps['variant'];
12
+ size?: ButtonProps['size'];
13
+ class?: any;
14
+ ui?: ButtonCopy['slots'];
15
+ }
16
+ declare const _default: import("vue").DefineComponent<ButtonCopyProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<ButtonCopyProps> & Readonly<{}>, {
17
+ size: "xs" | "sm" | "md" | "lg" | "xl";
18
+ color: "primary" | "secondary" | "success" | "info" | "warning" | "error" | "neutral";
19
+ variant: "solid" | "outline" | "soft" | "subtle" | "ghost" | "link";
20
+ label: string;
21
+ copiedLabel: string;
22
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
23
+ export default _default;
@@ -0,0 +1,52 @@
1
+ <template>
2
+ <UTooltip
3
+ :text="tooltipText"
4
+ :content="{ side: 'top' }"
5
+ :disabled="!tooltipText"
6
+ disable-hoverable-content
7
+ disable-closing-trigger
8
+ >
9
+ <UButton
10
+ v-bind="buttonProps"
11
+ color="error"
12
+ auto-loading
13
+ :class="ui.root({ class: [props.ui?.root, props.class] })"
14
+ @click="handleDeleteClick()"
15
+ />
16
+ </UTooltip>
17
+ </template>
18
+
19
+ <script>
20
+ import theme from "#build/cms/button-delete";
21
+ import { computed, useAppConfig, useOverlay } from "#imports";
22
+ import { tv } from "../utils/tv";
23
+ import ModalConfirm from "./ModalConfirm.vue";
24
+ </script>
25
+
26
+ <script setup>
27
+ const props = defineProps({
28
+ modalProps: { type: Object, required: false },
29
+ buttonProps: { type: Object, required: false },
30
+ confirmText: { type: String, required: false },
31
+ title: { type: String, required: false },
32
+ message: { type: String, required: false },
33
+ tooltipText: { type: String, required: false },
34
+ class: { type: null, required: false },
35
+ ui: { type: null, required: false },
36
+ onConfirm: { type: Function, required: false }
37
+ });
38
+ defineEmits(["confirm", "close"]);
39
+ const appConfig = useAppConfig();
40
+ const modal = useOverlay().create(ModalConfirm);
41
+ function handleDeleteClick() {
42
+ return modal.open({
43
+ ...props.modalProps,
44
+ title: props.title ?? "\u0423\u0434\u0430\u043B\u0435\u043D\u0438\u0435",
45
+ 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?",
46
+ confirmButton: { color: "error", label: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C" },
47
+ confirmText: props.confirmText,
48
+ onConfirm: props.onConfirm
49
+ });
50
+ }
51
+ const ui = computed(() => tv({ extend: tv(theme), ...appConfig.cms?.buttonDelete || {} })());
52
+ </script>
@@ -0,0 +1,28 @@
1
+ import type { AppConfig } from '@nuxt/schema';
2
+ import type { ButtonProps, ModalProps } from '@nuxt/ui';
3
+ import type { ComponentProps } from 'vue-component-type-helpers';
4
+ import type { ComponentConfig } from '../types';
5
+ import type { ModalConfirmEmits, ModalConfirmProps } from './ModalConfirm.vue';
6
+ import theme from '#build/cms/button-delete';
7
+ import ModalConfirm from './ModalConfirm.vue';
8
+ type ButtonDelete = ComponentConfig<typeof theme, AppConfig, 'buttonDelete'>;
9
+ export interface ButtonDeleteProps extends Pick<ModalConfirmProps, 'onConfirm'> {
10
+ modalProps?: Omit<ModalProps & ComponentProps<typeof ModalConfirm>, 'title' | 'message' | 'onConfirm' | 'confirmButton'>;
11
+ buttonProps?: Omit<ButtonProps, 'color'>;
12
+ confirmText?: string;
13
+ title?: string;
14
+ message?: string;
15
+ tooltipText?: string;
16
+ class?: any;
17
+ ui?: ButtonDelete['slots'];
18
+ }
19
+ export interface ButtonDeleteEmits extends ModalConfirmEmits {
20
+ }
21
+ declare const _default: import("vue").DefineComponent<ButtonDeleteProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
22
+ close: () => any;
23
+ confirm: () => any;
24
+ }, string, import("vue").PublicProps, Readonly<ButtonDeleteProps> & Readonly<{
25
+ onClose?: (() => any) | undefined;
26
+ onConfirm?: (() => any) | undefined;
27
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
28
+ export default _default;
@@ -0,0 +1,70 @@
1
+ <template>
2
+ <UDashboardPanel
3
+ :ui="{
4
+ body: 'flex-row p-0 sm:p-0 gap-0 sm:gap-0'
5
+ }"
6
+ >
7
+ <template #header>
8
+ <UDashboardNavbar :title="title" :ui="{ right: 'gap-2' }">
9
+ <template v-if="toBack" #leading>
10
+ <UButton :to="toBack" variant="link" :icon="appConfig.ui.icons.arrowLeft" />
11
+ </template>
12
+ <template #right>
13
+ <slot name="actions" />
14
+ <UButton
15
+ type="submit"
16
+ label="Сохранить"
17
+ :form="formId"
18
+ :loading="form?.loading"
19
+ loading-auto
20
+ />
21
+ </template>
22
+ </UDashboardNavbar>
23
+ </template>
24
+ <template #body>
25
+ <UForm
26
+ :id="formId"
27
+ ref="form"
28
+ :state="state"
29
+ :schema="schema"
30
+ :class="ui.form({ class: props.ui?.form })"
31
+ @submit="submitHandler"
32
+ >
33
+ <div :class="ui.body({ class: props.ui?.body })">
34
+ <slot />
35
+ </div>
36
+ <div v-if="$slots.sidebar" :class="ui.sidebar({ class: props.ui?.sidebar })">
37
+ <slot name="sidebar" />
38
+ </div>
39
+ </UForm>
40
+ </template>
41
+ </UDashboardPanel>
42
+ </template>
43
+
44
+ <script>
45
+ import theme from "#build/cms/form-panel";
46
+ import { computed, useAppConfig, useTemplateRef } from "#imports";
47
+ import { tv } from "../utils/tv";
48
+ </script>
49
+
50
+ <script setup>
51
+ const props = defineProps({
52
+ title: { type: String, required: false },
53
+ toBack: { type: null, required: false },
54
+ formId: { type: String, required: true },
55
+ schema: { type: Object, required: true },
56
+ state: { type: Object, required: true },
57
+ asideDivide: { type: Boolean, required: false },
58
+ handler: { type: Function, required: false },
59
+ class: { type: null, required: false },
60
+ ui: { type: null, required: false }
61
+ });
62
+ defineEmits(["update:state"]);
63
+ defineSlots();
64
+ const appConfig = useAppConfig();
65
+ const form = useTemplateRef("form");
66
+ async function submitHandler(event) {
67
+ await props.handler?.(event);
68
+ }
69
+ const ui = computed(() => tv({ extend: tv(theme), ...appConfig.cms?.formPanel || {} })());
70
+ </script>
@@ -0,0 +1,41 @@
1
+ import type { AppConfig } from '@nuxt/schema';
2
+ import type { FormSubmitEvent } from '@nuxt/ui';
3
+ import type { RouteLocationRaw } from 'vue-router';
4
+ import type { ZodSchema } from 'zod';
5
+ import type { ComponentConfig } from '../types';
6
+ import theme from '#build/cms/form-panel';
7
+ type FormPanel = ComponentConfig<typeof theme, AppConfig, 'formPanel'>;
8
+ export interface FormPanelProps<T> {
9
+ title?: string;
10
+ toBack?: RouteLocationRaw;
11
+ formId: string;
12
+ schema: ZodSchema<any>;
13
+ state: Partial<T>;
14
+ asideDivide?: boolean;
15
+ handler?: (event: FormSubmitEvent<T>) => Promise<void>;
16
+ class?: any;
17
+ ui?: FormPanel['slots'];
18
+ }
19
+ export interface FormPanelEmits<T> {
20
+ 'update:state': [value: T];
21
+ }
22
+ export interface FormPanelSlots {
23
+ default?: () => any;
24
+ sidebar?: () => any;
25
+ actions?: () => any;
26
+ }
27
+ declare const _default: <T>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
28
+ props: __VLS_PrettifyLocal<Pick<Partial<{}> & Omit<{
29
+ readonly "onUpdate:state"?: ((value: T) => any) | undefined;
30
+ } & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, never>, "onUpdate:state"> & FormPanelProps<T> & Partial<{}>> & import("vue").PublicProps;
31
+ expose(exposed: import("vue").ShallowUnwrapRef<{}>): void;
32
+ attrs: any;
33
+ slots: FormPanelSlots;
34
+ emit: (evt: "update:state", value: T) => void;
35
+ }>) => import("vue").VNode & {
36
+ __ctx?: Awaited<typeof __VLS_setup>;
37
+ };
38
+ export default _default;
39
+ type __VLS_PrettifyLocal<T> = {
40
+ [K in keyof T]: T[K];
41
+ } & {};
@@ -0,0 +1,41 @@
1
+ <template>
2
+ <UCollapsible :class="ui.root({ class: [props.ui?.root, props.class] })" default-open>
3
+ <div v-if="title || $slots.title || icon || $slots.icon" :class="ui.header({ class: props.ui?.header })">
4
+ <div v-if="icon || $slots.icon" :class="ui.icon({ class: props.ui?.icon })">
5
+ <slot name="icon">
6
+ <UIcon v-if="icon" :name="icon" />
7
+ </slot>
8
+ </div>
9
+ <div v-if="title || $slots.title" :class="ui.title({ class: props.ui?.title })">
10
+ <slot name="title">
11
+ {{ title }}
12
+ </slot>
13
+ </div>
14
+ </div>
15
+
16
+ <template #content>
17
+ <div :class="ui.body({ class: props.ui?.body })">
18
+ <slot />
19
+ </div>
20
+ </template>
21
+ </UCollapsible>
22
+ </template>
23
+
24
+ <script>
25
+ import theme from "#build/cms/form-panel-aside-section";
26
+ import { computed, useAppConfig } from "#imports";
27
+ import { tv } from "../utils/tv";
28
+ </script>
29
+
30
+ <script setup>
31
+ const props = defineProps({
32
+ title: { type: String, required: false },
33
+ icon: { type: String, required: false },
34
+ divide: { type: Boolean, required: false },
35
+ class: { type: null, required: false },
36
+ ui: { type: null, required: false }
37
+ });
38
+ defineSlots();
39
+ const appConfig = useAppConfig();
40
+ const ui = computed(() => tv({ extend: tv(theme), ...appConfig.cms?.formPanelAsideSection || {} })());
41
+ </script>
@@ -0,0 +1,23 @@
1
+ import type { AppConfig } from '@nuxt/schema';
2
+ import type { ComponentConfig } from '../types';
3
+ import theme from '#build/cms/form-panel-aside-section';
4
+ type FormPanelAsideSection = ComponentConfig<typeof theme, AppConfig, 'formPanelAsideSection'>;
5
+ export interface FormPanelAsideSectionProps {
6
+ title?: string;
7
+ icon?: string;
8
+ divide?: boolean;
9
+ class?: any;
10
+ ui?: FormPanelAsideSection['slots'];
11
+ }
12
+ export interface FormPanelAsideSectionSlots {
13
+ default?: () => any;
14
+ icon?: () => any;
15
+ title?: () => any;
16
+ }
17
+ declare const _default: __VLS_WithSlots<import("vue").DefineComponent<FormPanelAsideSectionProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<FormPanelAsideSectionProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, FormPanelAsideSectionSlots>;
18
+ export default _default;
19
+ type __VLS_WithSlots<T, S> = T & {
20
+ new (): {
21
+ $slots: S;
22
+ };
23
+ };
@@ -0,0 +1,31 @@
1
+ <template>
2
+ <div :class="ui.root({ class: [props.ui?.root, props.class] })">
3
+ <div :class="ui.title({ class: props.ui?.title })">
4
+ {{ title }}
5
+ </div>
6
+ <div v-if="description" :class="ui.description({ class: props.ui?.description })">
7
+ {{ description }}
8
+ </div>
9
+ <div :class="ui.body({ class: props.ui?.body })">
10
+ <slot />
11
+ </div>
12
+ </div>
13
+ </template>
14
+
15
+ <script>
16
+ import theme from "#build/cms/form-panel-section";
17
+ import { computed, useAppConfig } from "#imports";
18
+ import { tv } from "../utils/tv";
19
+ </script>
20
+
21
+ <script setup>
22
+ const props = defineProps({
23
+ title: { type: String, required: true },
24
+ description: { type: String, required: false },
25
+ class: { type: null, required: false },
26
+ ui: { type: null, required: false }
27
+ });
28
+ defineSlots();
29
+ const appConfig = useAppConfig();
30
+ const ui = computed(() => tv({ extend: tv(theme), ...appConfig.cms?.formPanelSection || {} })());
31
+ </script>
@@ -0,0 +1,20 @@
1
+ import type { AppConfig } from '@nuxt/schema';
2
+ import type { ComponentConfig } from '../types';
3
+ import theme from '#build/cms/form-panel-section';
4
+ type FormPanelSection = ComponentConfig<typeof theme, AppConfig, 'formPanelSection'>;
5
+ export interface FormPanelSectionProps {
6
+ title: string;
7
+ description?: string;
8
+ class?: any;
9
+ ui?: FormPanelSection['slots'];
10
+ }
11
+ export interface FormPanelSectionSlots {
12
+ default?: () => any;
13
+ }
14
+ declare const _default: __VLS_WithSlots<import("vue").DefineComponent<FormPanelSectionProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<FormPanelSectionProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, FormPanelSectionSlots>;
15
+ export default _default;
16
+ type __VLS_WithSlots<T, S> = T & {
17
+ new (): {
18
+ $slots: S;
19
+ };
20
+ };
@@ -0,0 +1,73 @@
1
+ <template>
2
+ <Primitive :as="as" :class="ui.root({ class: [props.ui?.root, props.class] })">
3
+ <UFormField
4
+ name="seo.title"
5
+ label="Заголовок"
6
+ :hint="`${model.title.length}/60`"
7
+ :ui="{ hint: 'text-xs' }"
8
+ >
9
+ <UInput v-model="model.title" placeholder="Введите заголовок..." class="w-full" />
10
+
11
+ <template #help>
12
+ <p class="text-xs mb-2">
13
+ Рекомендуемая длина: 45-60 символов
14
+ </p>
15
+ <UProgress
16
+ :model-value="title.progress"
17
+ :color="title.color"
18
+ size="sm"
19
+ />
20
+ </template>
21
+ </UFormField>
22
+
23
+ <UFormField
24
+ name="seo.description"
25
+ label="Описание"
26
+ :hint="`${model.description.length}/160`"
27
+ :ui="{ hint: 'text-xs' }"
28
+ >
29
+ <UTextarea
30
+ v-model="model.description"
31
+ placeholder="Введите описание..."
32
+ class="w-full"
33
+ autoresize
34
+ />
35
+
36
+ <template #help>
37
+ <p class="text-xs mb-2">
38
+ Рекомендуемая длина: 130-160 символов
39
+ </p>
40
+ <UProgress
41
+ :model-value="description.progress"
42
+ :color="description.color"
43
+ size="sm"
44
+ />
45
+ </template>
46
+ </UFormField>
47
+ </Primitive>
48
+ </template>
49
+
50
+ <script>
51
+ import theme from "#build/cms/input-seo";
52
+ import { computed, useAppConfig } from "#imports";
53
+ import { Primitive } from "reka-ui";
54
+ import { useSeoStats } from "../composables/useSeoStats";
55
+ import { tv } from "../utils/tv";
56
+ </script>
57
+
58
+ <script setup>
59
+ const props = defineProps({
60
+ as: { type: null, required: false },
61
+ class: { type: null, required: false },
62
+ ui: { type: null, required: false }
63
+ });
64
+ const model = defineModel({ type: Object, ...{
65
+ default: () => ({
66
+ title: "",
67
+ description: ""
68
+ })
69
+ } });
70
+ const appConfig = useAppConfig();
71
+ const { title, description } = useSeoStats(model);
72
+ const ui = computed(() => tv({ extend: tv(theme), ...appConfig.cms?.inputSeo || {} })());
73
+ </script>