@hywax/cms 0.0.4 → 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 (70) 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/index.ts +16 -0
  8. package/.nuxt/cms/input-seo.ts +5 -0
  9. package/.nuxt/cms/input-slug.ts +5 -0
  10. package/.nuxt/cms/modal-confirm.ts +5 -0
  11. package/.nuxt/cms/table-cell-preview.ts +9 -0
  12. package/.nuxt/cms/table-cell-seo.ts +5 -0
  13. package/.nuxt/cms/table-cell-user.ts +5 -0
  14. package/.nuxt/cms/table-panel-column-sorting.ts +5 -0
  15. package/.nuxt/cms/table-panel-column-visibility.ts +5 -0
  16. package/.nuxt/cms/table-panel-filters.ts +5 -0
  17. package/.nuxt/cms/table-panel.ts +8 -0
  18. package/cli/templates.mjs +3 -2
  19. package/dist/module.json +1 -1
  20. package/dist/module.mjs +154 -4
  21. package/dist/runtime/components/AutocompleteSelect.vue +170 -0
  22. package/dist/runtime/components/AutocompleteSelect.vue.d.ts +42 -0
  23. package/dist/runtime/components/ButtonCopy.vue +40 -0
  24. package/dist/runtime/components/ButtonCopy.vue.d.ts +23 -0
  25. package/dist/runtime/components/ButtonDelete.vue +52 -0
  26. package/dist/runtime/components/ButtonDelete.vue.d.ts +28 -0
  27. package/dist/runtime/components/FormPanel.vue +70 -0
  28. package/dist/runtime/components/FormPanel.vue.d.ts +41 -0
  29. package/dist/runtime/components/FormPanelAsideSection.vue +41 -0
  30. package/dist/runtime/components/FormPanelAsideSection.vue.d.ts +23 -0
  31. package/dist/runtime/components/FormPanelSection.vue +31 -0
  32. package/dist/runtime/components/FormPanelSection.vue.d.ts +20 -0
  33. package/dist/runtime/components/InputSeo.vue +73 -0
  34. package/dist/runtime/components/InputSeo.vue.d.ts +19 -0
  35. package/dist/runtime/components/InputSlug.vue +70 -0
  36. package/dist/runtime/components/InputSlug.vue.d.ts +29 -0
  37. package/dist/runtime/components/ModalConfirm.vue +91 -0
  38. package/dist/runtime/components/ModalConfirm.vue.d.ts +26 -0
  39. package/dist/runtime/components/TableCellPreview.vue +40 -0
  40. package/dist/runtime/components/TableCellPreview.vue.d.ts +18 -0
  41. package/dist/runtime/components/TableCellSeo.vue +34 -0
  42. package/dist/runtime/components/TableCellSeo.vue.d.ts +13 -0
  43. package/dist/runtime/components/TableCellUser.vue +33 -0
  44. package/dist/runtime/components/TableCellUser.vue.d.ts +15 -0
  45. package/dist/runtime/components/TablePanel.vue +153 -0
  46. package/dist/runtime/components/TablePanel.vue.d.ts +50 -0
  47. package/dist/runtime/components/TablePanelColumnSorting.vue +72 -0
  48. package/dist/runtime/components/TablePanelColumnSorting.vue.d.ts +20 -0
  49. package/dist/runtime/components/TablePanelColumnVisibility.vue +49 -0
  50. package/dist/runtime/components/TablePanelColumnVisibility.vue.d.ts +20 -0
  51. package/dist/runtime/components/TablePanelFilters.vue +79 -0
  52. package/dist/runtime/components/TablePanelFilters.vue.d.ts +34 -0
  53. package/dist/runtime/components/prose/UploraImage.vue +8 -3
  54. package/dist/runtime/composables/useDeleteConfirm.d.ts +15 -0
  55. package/dist/runtime/composables/useDeleteConfirm.js +29 -0
  56. package/dist/runtime/composables/useSeoStats.d.ts +2 -2
  57. package/dist/runtime/composables/useSeoStats.js +1 -1
  58. package/dist/runtime/composables/useTable.d.ts +19 -0
  59. package/dist/runtime/composables/useTable.js +90 -0
  60. package/dist/runtime/editor/markdown/index.d.ts +3 -0
  61. package/dist/runtime/editor/markdown/index.js +47 -0
  62. package/dist/runtime/editor/markdown/nodes/callout.d.ts +2 -0
  63. package/dist/runtime/editor/markdown/nodes/callout.js +21 -0
  64. package/dist/runtime/editor/markdown/nodes/uploraImage.d.ts +2 -0
  65. package/dist/runtime/editor/markdown/nodes/uploraImage.js +31 -0
  66. package/dist/runtime/server/utils/validation.d.ts +2 -2
  67. package/dist/runtime/types/index.d.ts +16 -0
  68. package/dist/runtime/types/index.js +16 -0
  69. package/dist/runtime/types/query.d.ts +3 -1
  70. package/package.json +5 -5
@@ -0,0 +1,5 @@
1
+ export default {
2
+ "slots": {
3
+ "root": ""
4
+ }
5
+ }
@@ -0,0 +1,5 @@
1
+ export default {
2
+ "slots": {
3
+ "root": ""
4
+ }
5
+ }
@@ -0,0 +1,5 @@
1
+ export default {
2
+ "slots": {
3
+ "root": ""
4
+ }
5
+ }
@@ -0,0 +1,9 @@
1
+ export default {
2
+ "slots": {
3
+ "root": "",
4
+ "header": "flex items-center gap-2 mb-2",
5
+ "icon": "size-5",
6
+ "title": "break-words text-sm font-semibold",
7
+ "body": ""
8
+ }
9
+ }
@@ -0,0 +1,8 @@
1
+ export default {
2
+ "slots": {
3
+ "root": "",
4
+ "title": "text-base text-pretty font-semibold text-highlighted",
5
+ "description": "text-[15px] text-pretty text-muted mt-1",
6
+ "body": "relative rounded-lg bg-elevated/50 ring ring-default grid gap-x-8 gap-y-4 p-4 sm:p-6 mt-4"
7
+ }
8
+ }
@@ -0,0 +1,15 @@
1
+ export default {
2
+ "slots": {
3
+ "root": "",
4
+ "form": "flex flex-1 flex-row",
5
+ "body": "flex-1 overflow-y-auto w-full lg:max-w-2xl mx-auto p-4 sm:p-6",
6
+ "sidebar": "flex-1 overflow-y-auto border-l border-default w-full max-w-xs p-4 sm:p-4"
7
+ },
8
+ "variants": {
9
+ "asideDivide": {
10
+ "true": {
11
+ "sidebar": "*:not-last:after:absolute *:not-last:after:inset-x-1 *:not-last:after:bottom-0 *:not-last:after:bg-border *:not-last:after:h-px *:not-last:relative *:not-last:pb-4 flex flex-col gap-4"
12
+ }
13
+ }
14
+ }
15
+ }
@@ -1,2 +1,18 @@
1
+ export { default as autocompleteSelect } from './autocomplete-select'
1
2
  export { default as buttonClear } from './button-clear'
3
+ export { default as buttonCopy } from './button-copy'
4
+ export { default as buttonDelete } from './button-delete'
5
+ export { default as formPanel } from './form-panel'
6
+ export { default as formPanelAsideSection } from './form-panel-aside-section'
7
+ export { default as formPanelSection } from './form-panel-section'
8
+ export { default as inputSeo } from './input-seo'
9
+ export { default as inputSlug } from './input-slug'
10
+ export { default as modalConfirm } from './modal-confirm'
11
+ export { default as tableCellPreview } from './table-cell-preview'
12
+ export { default as tableCellSeo } from './table-cell-seo'
13
+ export { default as tableCellUser } from './table-cell-user'
14
+ export { default as tablePanel } from './table-panel'
15
+ export { default as tablePanelColumnSorting } from './table-panel-column-sorting'
16
+ export { default as tablePanelColumnVisibility } from './table-panel-column-visibility'
17
+ export { default as tablePanelFilters } from './table-panel-filters'
2
18
  export { default as uploraImage } from './uplora-image'
@@ -0,0 +1,5 @@
1
+ export default {
2
+ "slots": {
3
+ "root": "flex flex-col gap-4"
4
+ }
5
+ }
@@ -0,0 +1,5 @@
1
+ export default {
2
+ "slots": {
3
+ "root": "flex flex-col gap-4"
4
+ }
5
+ }
@@ -0,0 +1,5 @@
1
+ export default {
2
+ "slots": {
3
+ "root": ""
4
+ }
5
+ }
@@ -0,0 +1,9 @@
1
+ export default {
2
+ "slots": {
3
+ "root": "flex items-center gap-2",
4
+ "image": "bg-elevated size-7 rounded-md",
5
+ "container": "text-xs",
6
+ "title": "font-medium text-default",
7
+ "description": "text-muted truncate max-w-40"
8
+ }
9
+ }
@@ -0,0 +1,5 @@
1
+ export default {
2
+ "slots": {
3
+ "root": "flex gap-2"
4
+ }
5
+ }
@@ -0,0 +1,5 @@
1
+ export default {
2
+ "slots": {
3
+ "root": ""
4
+ }
5
+ }
@@ -0,0 +1,5 @@
1
+ export default {
2
+ "slots": {
3
+ "root": ""
4
+ }
5
+ }
@@ -0,0 +1,5 @@
1
+ export default {
2
+ "slots": {
3
+ "root": ""
4
+ }
5
+ }
@@ -0,0 +1,5 @@
1
+ export default {
2
+ "slots": {
3
+ "root": ""
4
+ }
5
+ }
@@ -0,0 +1,8 @@
1
+ export default {
2
+ "slots": {
3
+ "root": "",
4
+ "table": "",
5
+ "loader": "absolute z-10 inset-0 left-0 bg-default/90 flex items-center justify-center",
6
+ "loaderIcon": "animate-spin size-6"
7
+ }
8
+ }
package/cli/templates.mjs CHANGED
@@ -23,7 +23,7 @@ import { computed, useAppConfig } from '#imports'
23
23
  import { Primitive } from 'reka-ui'
24
24
  import { tv } from '${prose ? '../../utils/tv' : '../utils/tv'}'
25
25
 
26
- type ${upperName} = ComponentConfig<typeof theme, AppConfig, ${upperName}>
26
+ type ${upperName} = ComponentConfig<typeof theme, AppConfig, '${camelName}'>
27
27
 
28
28
  export interface ${upperName}Props {
29
29
  as?: any
@@ -62,13 +62,14 @@ function test({ name, prose }) {
62
62
  ? undefined
63
63
  : `
64
64
  import type { ${upperName}Props, ${upperName}Slots } from '../../../src/runtime/components/${upperName}.vue'
65
- import { describe, it, expect } from 'vitest'
65
+ import { describe, expect, it } from 'vitest'
66
66
  import ${upperName} from '../../../src/runtime/components/${upperName}.vue'
67
67
  import ComponentRender from '../../component-render'
68
68
 
69
69
  describe('${upperName}', () => {
70
70
  it.each([
71
71
  // Props
72
+ ['базовый компонент', { props: {} }],
72
73
  ['с алиасом', { props: { as: 'section' } }],
73
74
  ['с class', { props: { class: '' } }],
74
75
  ['с ui', { props: { ui: {} } }],
package/dist/module.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hywax/cms",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "configKey": "cms",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.1",
package/dist/module.mjs CHANGED
@@ -5,7 +5,7 @@ import { dirname } from 'pathe';
5
5
  import { snakeCase, kebabCase } from 'scule';
6
6
 
7
7
  const name = "@hywax/cms";
8
- const version = "0.0.4";
8
+ const version = "0.0.5";
9
9
 
10
10
  function createContext(options, nuxt) {
11
11
  const { resolve } = createResolver(import.meta.url);
@@ -64,19 +64,147 @@ function prepareAutoImports({ resolve, options, nuxt }) {
64
64
  });
65
65
  addImportsDir(resolve("./runtime/composables"));
66
66
  addPlugin(resolve("./runtime/plugins/api.ts"));
67
- addImports(httpCodesImports);
68
- addServerImports(httpCodesImports);
67
+ addImports([
68
+ ...httpCodesImports,
69
+ { name: "docToMarkdown", from: resolve("./runtime/editor/markdown") },
70
+ { name: "markdownToDoc", from: resolve("./runtime/editor/markdown") }
71
+ ]);
72
+ addServerImports([
73
+ ...httpCodesImports,
74
+ { name: "docToMarkdown", from: resolve("./runtime/editor/markdown") },
75
+ { name: "markdownToDoc", from: resolve("./runtime/editor/markdown") }
76
+ ]);
69
77
  addServerImportsDir(resolve("./runtime/server/utils"));
70
78
  nuxt.options.nitro.alias ||= {};
71
79
  nuxt.options.nitro.alias["#cms/http-codes"] = httpCodesPath;
72
80
  }
73
81
 
82
+ const autocompleteSelect = {
83
+ slots: {
84
+ root: ""
85
+ }
86
+ };
87
+
74
88
  const buttonClear = {
75
89
  slots: {
76
90
  base: "p-0"
77
91
  }
78
92
  };
79
93
 
94
+ const buttonCopy = {
95
+ slots: {
96
+ root: ""
97
+ }
98
+ };
99
+
100
+ const buttonDelete = {
101
+ slots: {
102
+ root: ""
103
+ }
104
+ };
105
+
106
+ const formPanel = {
107
+ slots: {
108
+ root: "",
109
+ form: "flex flex-1 flex-row",
110
+ body: "flex-1 overflow-y-auto w-full lg:max-w-2xl mx-auto p-4 sm:p-6",
111
+ sidebar: "flex-1 overflow-y-auto border-l border-default w-full max-w-xs p-4 sm:p-4"
112
+ },
113
+ variants: {
114
+ asideDivide: {
115
+ true: {
116
+ sidebar: "*:not-last:after:absolute *:not-last:after:inset-x-1 *:not-last:after:bottom-0 *:not-last:after:bg-border *:not-last:after:h-px *:not-last:relative *:not-last:pb-4 flex flex-col gap-4"
117
+ }
118
+ }
119
+ }
120
+ };
121
+
122
+ const formPanelAsideSection = {
123
+ slots: {
124
+ root: "",
125
+ header: "flex items-center gap-2 mb-2",
126
+ icon: "size-5",
127
+ title: "break-words text-sm font-semibold",
128
+ body: ""
129
+ }
130
+ };
131
+
132
+ const formPanelSection = {
133
+ slots: {
134
+ root: "",
135
+ title: "text-base text-pretty font-semibold text-highlighted",
136
+ description: "text-[15px] text-pretty text-muted mt-1",
137
+ body: "relative rounded-lg bg-elevated/50 ring ring-default grid gap-x-8 gap-y-4 p-4 sm:p-6 mt-4"
138
+ }
139
+ };
140
+
141
+ const inputSeo = {
142
+ slots: {
143
+ root: "flex flex-col gap-4"
144
+ }
145
+ };
146
+
147
+ const inputSlug = {
148
+ slots: {
149
+ root: "flex flex-col gap-4"
150
+ }
151
+ };
152
+
153
+ const modalConfirm = {
154
+ slots: {
155
+ root: ""
156
+ }
157
+ };
158
+
159
+ const tableCellPreview = {
160
+ slots: {
161
+ root: "flex items-center gap-2",
162
+ image: "bg-elevated size-7 rounded-md",
163
+ container: "text-xs",
164
+ title: "font-medium text-default",
165
+ description: "text-muted truncate max-w-40"
166
+ }
167
+ };
168
+
169
+ const tableCellSeo = {
170
+ slots: {
171
+ root: "flex gap-2"
172
+ }
173
+ };
174
+
175
+ const tableCellUser = {
176
+ slots: {
177
+ root: ""
178
+ }
179
+ };
180
+
181
+ const tablePanel = {
182
+ slots: {
183
+ root: "",
184
+ table: "",
185
+ loader: "absolute z-10 inset-0 left-0 bg-default/90 flex items-center justify-center",
186
+ loaderIcon: "animate-spin size-6"
187
+ }
188
+ };
189
+
190
+ const tablePanelColumnSorting = {
191
+ slots: {
192
+ root: ""
193
+ }
194
+ };
195
+
196
+ const tablePanelColumnVisibility = {
197
+ slots: {
198
+ root: ""
199
+ }
200
+ };
201
+
202
+ const tablePanelFilters = {
203
+ slots: {
204
+ root: ""
205
+ }
206
+ };
207
+
80
208
  const uploraImage$1 = {
81
209
  slots: {
82
210
  root: "relative grid grid-cols-[100%] grid-rows-[100%] overflow-hidden",
@@ -88,7 +216,23 @@ const uploraImage$1 = {
88
216
 
89
217
  const theme = {
90
218
  __proto__: null,
219
+ autocompleteSelect: autocompleteSelect,
91
220
  buttonClear: buttonClear,
221
+ buttonCopy: buttonCopy,
222
+ buttonDelete: buttonDelete,
223
+ formPanel: formPanel,
224
+ formPanelAsideSection: formPanelAsideSection,
225
+ formPanelSection: formPanelSection,
226
+ inputSeo: inputSeo,
227
+ inputSlug: inputSlug,
228
+ modalConfirm: modalConfirm,
229
+ tableCellPreview: tableCellPreview,
230
+ tableCellSeo: tableCellSeo,
231
+ tableCellUser: tableCellUser,
232
+ tablePanel: tablePanel,
233
+ tablePanelColumnSorting: tablePanelColumnSorting,
234
+ tablePanelColumnVisibility: tablePanelColumnVisibility,
235
+ tablePanelFilters: tablePanelFilters,
92
236
  uploraImage: uploraImage$1
93
237
  };
94
238
 
@@ -215,7 +359,13 @@ function prepareTemplates(context) {
215
359
  }
216
360
  }
217
361
 
218
- const icons = {};
362
+ const icons = {
363
+ sortAsc: "lucide:arrow-up-wide-narrow",
364
+ sortDesc: "lucide:arrow-down-wide-narrow",
365
+ columns: "lucide:columns-3-cog",
366
+ filter: "lucide:filter",
367
+ link: "lucide:link-2"
368
+ };
219
369
 
220
370
  const module = defineNuxtModule({
221
371
  meta: {
@@ -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;