@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.
- package/.nuxt/cms/autocomplete-select.ts +5 -0
- package/.nuxt/cms/button-copy.ts +5 -0
- package/.nuxt/cms/button-delete.ts +5 -0
- package/.nuxt/cms/form-panel-aside-section.ts +9 -0
- package/.nuxt/cms/form-panel-section.ts +8 -0
- package/.nuxt/cms/form-panel.ts +15 -0
- package/.nuxt/cms/index.ts +16 -0
- package/.nuxt/cms/input-seo.ts +5 -0
- package/.nuxt/cms/input-slug.ts +5 -0
- package/.nuxt/cms/modal-confirm.ts +5 -0
- package/.nuxt/cms/table-cell-preview.ts +9 -0
- package/.nuxt/cms/table-cell-seo.ts +5 -0
- package/.nuxt/cms/table-cell-user.ts +5 -0
- package/.nuxt/cms/table-panel-column-sorting.ts +5 -0
- package/.nuxt/cms/table-panel-column-visibility.ts +5 -0
- package/.nuxt/cms/table-panel-filters.ts +5 -0
- package/.nuxt/cms/table-panel.ts +8 -0
- package/cli/templates.mjs +3 -2
- package/dist/module.json +1 -1
- package/dist/module.mjs +154 -4
- package/dist/runtime/components/AutocompleteSelect.vue +170 -0
- package/dist/runtime/components/AutocompleteSelect.vue.d.ts +42 -0
- package/dist/runtime/components/ButtonCopy.vue +40 -0
- package/dist/runtime/components/ButtonCopy.vue.d.ts +23 -0
- package/dist/runtime/components/ButtonDelete.vue +52 -0
- package/dist/runtime/components/ButtonDelete.vue.d.ts +28 -0
- package/dist/runtime/components/FormPanel.vue +70 -0
- package/dist/runtime/components/FormPanel.vue.d.ts +41 -0
- package/dist/runtime/components/FormPanelAsideSection.vue +41 -0
- package/dist/runtime/components/FormPanelAsideSection.vue.d.ts +23 -0
- package/dist/runtime/components/FormPanelSection.vue +31 -0
- package/dist/runtime/components/FormPanelSection.vue.d.ts +20 -0
- package/dist/runtime/components/InputSeo.vue +73 -0
- package/dist/runtime/components/InputSeo.vue.d.ts +19 -0
- package/dist/runtime/components/InputSlug.vue +70 -0
- package/dist/runtime/components/InputSlug.vue.d.ts +29 -0
- package/dist/runtime/components/ModalConfirm.vue +91 -0
- package/dist/runtime/components/ModalConfirm.vue.d.ts +26 -0
- package/dist/runtime/components/TableCellPreview.vue +40 -0
- package/dist/runtime/components/TableCellPreview.vue.d.ts +18 -0
- package/dist/runtime/components/TableCellSeo.vue +34 -0
- package/dist/runtime/components/TableCellSeo.vue.d.ts +13 -0
- package/dist/runtime/components/TableCellUser.vue +33 -0
- package/dist/runtime/components/TableCellUser.vue.d.ts +15 -0
- package/dist/runtime/components/TablePanel.vue +153 -0
- package/dist/runtime/components/TablePanel.vue.d.ts +50 -0
- package/dist/runtime/components/TablePanelColumnSorting.vue +72 -0
- package/dist/runtime/components/TablePanelColumnSorting.vue.d.ts +20 -0
- package/dist/runtime/components/TablePanelColumnVisibility.vue +49 -0
- package/dist/runtime/components/TablePanelColumnVisibility.vue.d.ts +20 -0
- package/dist/runtime/components/TablePanelFilters.vue +79 -0
- package/dist/runtime/components/TablePanelFilters.vue.d.ts +34 -0
- package/dist/runtime/components/prose/UploraImage.vue +8 -3
- package/dist/runtime/composables/useDeleteConfirm.d.ts +15 -0
- package/dist/runtime/composables/useDeleteConfirm.js +29 -0
- package/dist/runtime/composables/useSeoStats.d.ts +2 -2
- package/dist/runtime/composables/useSeoStats.js +1 -1
- package/dist/runtime/composables/useTable.d.ts +19 -0
- package/dist/runtime/composables/useTable.js +90 -0
- package/dist/runtime/editor/markdown/index.d.ts +3 -0
- package/dist/runtime/editor/markdown/index.js +47 -0
- package/dist/runtime/editor/markdown/nodes/callout.d.ts +2 -0
- package/dist/runtime/editor/markdown/nodes/callout.js +21 -0
- package/dist/runtime/editor/markdown/nodes/uploraImage.d.ts +2 -0
- package/dist/runtime/editor/markdown/nodes/uploraImage.js +31 -0
- package/dist/runtime/server/utils/validation.d.ts +2 -2
- package/dist/runtime/types/index.d.ts +16 -0
- package/dist/runtime/types/index.js +16 -0
- package/dist/runtime/types/query.d.ts +3 -1
- package/package.json +5 -5
|
@@ -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
|
+
}
|
package/.nuxt/cms/index.ts
CHANGED
|
@@ -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'
|
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, ${
|
|
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,
|
|
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
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.
|
|
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(
|
|
68
|
-
|
|
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;
|