adminforth 2.4.0-next.21 → 2.4.0-next.210
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/commands/callTsProxy.js +14 -4
- package/commands/cli.js +10 -3
- package/commands/createApp/templates/custom/tsconfig.json.hbs +2 -3
- package/commands/createApp/templates/index.ts.hbs +10 -2
- package/commands/createApp/templates/package.json.hbs +1 -1
- package/commands/createApp/utils.js +27 -2
- package/commands/createCustomComponent/configLoader.js +3 -0
- package/commands/createCustomComponent/main.js +1 -0
- package/commands/createCustomComponent/templates/customCrud/beforeActionButtons.vue.hbs +38 -0
- package/commands/createPlugin/templates/custom/tsconfig.json.hbs +2 -5
- package/commands/createPlugin/templates/package.json.hbs +1 -1
- package/dist/auth.d.ts +9 -1
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +15 -2
- package/dist/auth.js.map +1 -1
- package/dist/dataConnectors/baseConnector.d.ts.map +1 -1
- package/dist/dataConnectors/baseConnector.js +46 -15
- package/dist/dataConnectors/baseConnector.js.map +1 -1
- package/dist/dataConnectors/clickhouse.d.ts.map +1 -1
- package/dist/dataConnectors/clickhouse.js +15 -0
- package/dist/dataConnectors/clickhouse.js.map +1 -1
- package/dist/dataConnectors/mongo.d.ts.map +1 -1
- package/dist/dataConnectors/mongo.js +44 -15
- package/dist/dataConnectors/mongo.js.map +1 -1
- package/dist/dataConnectors/mysql.d.ts.map +1 -1
- package/dist/dataConnectors/mysql.js +11 -0
- package/dist/dataConnectors/mysql.js.map +1 -1
- package/dist/dataConnectors/postgres.d.ts.map +1 -1
- package/dist/dataConnectors/postgres.js +11 -0
- package/dist/dataConnectors/postgres.js.map +1 -1
- package/dist/dataConnectors/sqlite.d.ts.map +1 -1
- package/dist/dataConnectors/sqlite.js +11 -0
- package/dist/dataConnectors/sqlite.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -9
- package/dist/index.js.map +1 -1
- package/dist/modules/codeInjector.d.ts +2 -0
- package/dist/modules/codeInjector.d.ts.map +1 -1
- package/dist/modules/codeInjector.js +52 -8
- package/dist/modules/codeInjector.js.map +1 -1
- package/dist/modules/configValidator.d.ts.map +1 -1
- package/dist/modules/configValidator.js +74 -7
- package/dist/modules/configValidator.js.map +1 -1
- package/dist/modules/restApi.d.ts.map +1 -1
- package/dist/modules/restApi.js +154 -26
- package/dist/modules/restApi.js.map +1 -1
- package/dist/modules/styles.d.ts +503 -13
- package/dist/modules/styles.d.ts.map +1 -1
- package/dist/modules/styles.js +559 -31
- package/dist/modules/styles.js.map +1 -1
- package/dist/modules/utils.d.ts +2 -0
- package/dist/modules/utils.d.ts.map +1 -1
- package/dist/modules/utils.js +16 -0
- package/dist/modules/utils.js.map +1 -1
- package/dist/servers/express.d.ts.map +1 -1
- package/dist/servers/express.js +14 -0
- package/dist/servers/express.js.map +1 -1
- package/dist/spa/index.html +1 -1
- package/dist/spa/package-lock.json +5 -4
- package/dist/spa/package.json +1 -1
- package/dist/spa/src/App.vue +54 -169
- package/dist/spa/src/adminforth.ts +42 -18
- package/dist/spa/src/afcl/BarChart.vue +2 -2
- package/dist/spa/src/afcl/Button.vue +6 -6
- package/dist/spa/src/afcl/ButtonGroup.vue +91 -0
- package/dist/spa/src/afcl/Card.vue +25 -0
- package/dist/spa/src/afcl/Checkbox.vue +21 -13
- package/dist/spa/src/afcl/CountryFlag.vue +4 -1
- package/dist/spa/src/{components/CustomDatePicker.vue → afcl/DatePicker.vue} +95 -9
- package/dist/spa/src/afcl/Dialog.vue +44 -27
- package/dist/spa/src/afcl/Dropzone.vue +12 -12
- package/dist/spa/src/afcl/Input.vue +6 -6
- package/dist/spa/src/afcl/JsonViewer.vue +25 -0
- package/dist/spa/src/afcl/LinkButton.vue +3 -3
- package/dist/spa/src/afcl/PieChart.vue +5 -5
- package/dist/spa/src/afcl/ProgressBar.vue +7 -7
- package/dist/spa/src/afcl/Select.vue +68 -34
- package/dist/spa/src/afcl/Skeleton.vue +6 -6
- package/dist/spa/src/afcl/Table.vue +213 -74
- package/dist/spa/src/afcl/Textarea.vue +31 -0
- package/dist/spa/src/afcl/Toggle.vue +32 -0
- package/dist/spa/src/afcl/Tooltip.vue +1 -2
- package/dist/spa/src/afcl/VerticalTabs.vue +16 -7
- package/dist/spa/src/afcl/index.ts +6 -3
- package/dist/spa/src/components/AcceptModal.vue +7 -7
- package/dist/spa/src/components/Breadcrumbs.vue +5 -5
- package/dist/spa/src/components/ColumnValueInput.vue +38 -18
- package/dist/spa/src/components/ColumnValueInputWrapper.vue +4 -3
- package/dist/spa/src/components/CustomDateRangePicker.vue +9 -8
- package/dist/spa/src/components/CustomRangePicker.vue +37 -8
- package/dist/spa/src/components/ErrorMessage.vue +21 -0
- package/dist/spa/src/components/Filters.vue +85 -39
- package/dist/spa/src/components/GroupsTable.vue +9 -8
- package/dist/spa/src/components/MenuLink.vue +90 -23
- package/dist/spa/src/components/ResourceForm.vue +94 -51
- package/dist/spa/src/components/ResourceListTable.vue +78 -80
- package/dist/spa/src/components/ResourceListTableVirtual.vue +71 -73
- package/dist/spa/src/components/ShowTable.vue +17 -12
- package/dist/spa/src/components/Sidebar.vue +448 -0
- package/dist/spa/src/components/SingleSkeletLoader.vue +6 -6
- package/dist/spa/src/components/SkeleteLoader.vue +3 -3
- package/dist/spa/src/components/ThreeDotsMenu.vue +73 -14
- package/dist/spa/src/components/Toast.vue +27 -9
- package/dist/spa/src/components/UserMenuSettingsButton.vue +70 -0
- package/dist/spa/src/components/ValueRenderer.vue +43 -16
- package/dist/spa/src/controls/BoolToggle.vue +34 -0
- package/dist/spa/src/i18n.ts +1 -1
- package/dist/spa/src/renderers/CompactField.vue +1 -1
- package/dist/spa/src/renderers/CompactUUID.vue +1 -1
- package/dist/spa/src/router/index.ts +8 -0
- package/dist/spa/src/shims-vue.d.ts +5 -0
- package/dist/spa/src/spa_types/core.ts +13 -1
- package/dist/spa/src/stores/core.ts +1 -1
- package/dist/spa/src/stores/filters.ts +29 -2
- package/dist/spa/src/stores/modal.ts +6 -1
- package/dist/spa/src/stores/toast.ts +22 -3
- package/dist/spa/src/types/Back.ts +137 -22
- package/dist/spa/src/types/Common.ts +67 -32
- package/dist/spa/src/types/FrontendAPI.ts +31 -5
- package/dist/spa/src/types/adapters/CaptchaAdapter.ts +34 -0
- package/dist/spa/src/types/adapters/CompletionAdapter.ts +25 -0
- package/dist/spa/src/types/adapters/EmailAdapter.ts +27 -0
- package/dist/spa/src/types/adapters/ImageGenerationAdapter.ts +50 -0
- package/dist/spa/src/types/adapters/ImageVisionAdapter.ts +30 -0
- package/dist/spa/src/types/adapters/KeyValueAdapter.ts +16 -0
- package/dist/spa/src/types/adapters/OAuth2Adapter.ts +34 -0
- package/dist/spa/src/types/adapters/StorageAdapter.ts +73 -0
- package/dist/spa/src/types/adapters/index.ts +8 -0
- package/dist/spa/src/utils.ts +219 -8
- package/dist/spa/src/views/CreateView.vue +18 -19
- package/dist/spa/src/views/EditView.vue +25 -19
- package/dist/spa/src/views/ListView.vue +139 -86
- package/dist/spa/src/views/LoginView.vue +31 -37
- package/dist/spa/src/views/ResourceParent.vue +2 -2
- package/dist/spa/src/views/SettingsView.vue +121 -0
- package/dist/spa/src/views/ShowView.vue +59 -39
- package/dist/spa/src/websocket.ts +6 -1
- package/dist/spa/tsconfig.app.json +1 -1
- package/dist/spa/vite.config.ts +45 -2
- package/dist/types/Back.d.ts +115 -14
- package/dist/types/Back.d.ts.map +1 -1
- package/dist/types/Back.js +15 -0
- package/dist/types/Back.js.map +1 -1
- package/dist/types/Common.d.ts +59 -29
- package/dist/types/Common.d.ts.map +1 -1
- package/dist/types/Common.js.map +1 -1
- package/dist/types/FrontendAPI.d.ts +31 -3
- package/dist/types/FrontendAPI.d.ts.map +1 -1
- package/dist/types/FrontendAPI.js.map +1 -1
- package/dist/types/adapters/CaptchaAdapter.d.ts +30 -0
- package/dist/types/adapters/CaptchaAdapter.d.ts.map +1 -0
- package/dist/types/adapters/CaptchaAdapter.js +5 -0
- package/dist/types/adapters/CaptchaAdapter.js.map +1 -0
- package/dist/types/adapters/CompletionAdapter.d.ts +20 -0
- package/dist/types/adapters/CompletionAdapter.d.ts.map +1 -0
- package/dist/types/adapters/CompletionAdapter.js +2 -0
- package/dist/types/adapters/CompletionAdapter.js.map +1 -0
- package/dist/types/adapters/EmailAdapter.d.ts +20 -0
- package/dist/types/adapters/EmailAdapter.d.ts.map +1 -0
- package/dist/types/adapters/EmailAdapter.js +2 -0
- package/dist/types/adapters/EmailAdapter.js.map +1 -0
- package/dist/types/adapters/ImageGenerationAdapter.d.ts +37 -0
- package/dist/types/adapters/ImageGenerationAdapter.d.ts.map +1 -0
- package/dist/types/adapters/ImageGenerationAdapter.js +2 -0
- package/dist/types/adapters/ImageGenerationAdapter.js.map +1 -0
- package/dist/types/adapters/ImageVisionAdapter.d.ts +25 -0
- package/dist/types/adapters/ImageVisionAdapter.d.ts.map +1 -0
- package/dist/types/adapters/ImageVisionAdapter.js +2 -0
- package/dist/types/adapters/ImageVisionAdapter.js.map +1 -0
- package/dist/types/adapters/KeyValueAdapter.d.ts +10 -0
- package/dist/types/adapters/KeyValueAdapter.d.ts.map +1 -0
- package/dist/types/adapters/KeyValueAdapter.js +2 -0
- package/dist/types/adapters/KeyValueAdapter.js.map +1 -0
- package/dist/types/adapters/OAuth2Adapter.d.ts +32 -0
- package/dist/types/adapters/OAuth2Adapter.d.ts.map +1 -0
- package/dist/types/adapters/OAuth2Adapter.js +2 -0
- package/dist/types/adapters/OAuth2Adapter.js.map +1 -0
- package/dist/types/adapters/StorageAdapter.d.ts +63 -0
- package/dist/types/adapters/StorageAdapter.d.ts.map +1 -0
- package/dist/types/adapters/StorageAdapter.js +2 -0
- package/dist/types/adapters/StorageAdapter.js.map +1 -0
- package/dist/types/adapters/index.d.ts +9 -0
- package/dist/types/adapters/index.d.ts.map +1 -0
- package/dist/types/adapters/index.js +2 -0
- package/dist/types/adapters/index.js.map +1 -0
- package/package.json +3 -2
- package/dist/spa/src/types/Adapters.ts +0 -213
- package/dist/types/Adapters.d.ts +0 -168
- package/dist/types/Adapters.d.ts.map +0 -1
- package/dist/types/Adapters.js +0 -2
- package/dist/types/Adapters.js.map +0 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
|
|
3
|
-
<div class="afcl-input flex z-0 relative" :class="{'opacity-50' : readonly}">
|
|
3
|
+
<div class="afcl-input-wrapper flex z-0 relative" :class="{'opacity-50' : readonly}">
|
|
4
4
|
<span
|
|
5
5
|
v-if="$slots.prefix || prefix"
|
|
6
|
-
class="inline-flex items-center px-3 text-sm text-
|
|
6
|
+
class="inline-flex items-center px-3 text-sm text-lightInputText bg-lightInputBackground border border-s-0 border-lightInputBorder rounded-s-md dark:bg-darkInputBackground dark:text-darkInputText dark:border-darkInputBorder">
|
|
7
7
|
<slot name="prefix">{{ prefix }}</slot>
|
|
8
8
|
</span>
|
|
9
9
|
|
|
@@ -12,11 +12,11 @@
|
|
|
12
12
|
ref="input"
|
|
13
13
|
v-bind="$attrs"
|
|
14
14
|
:type="type"
|
|
15
|
-
@input="$emit('update:modelValue', $event.target?.value)"
|
|
15
|
+
@input="$emit('update:modelValue', type === 'number' ? Number(($event.target as HTMLInputElement)?.value) : ($event.target as HTMLInputElement)?.value)"
|
|
16
16
|
:value="modelValue"
|
|
17
17
|
aria-describedby="helper-text-explanation"
|
|
18
|
-
class="inline-flex bg-
|
|
19
|
-
blue-500 focus:border-blue-500 block w-20 p-2.5 dark:bg-
|
|
18
|
+
class="afcl-input inline-flex bg-lightInputBackground border border-lightInputBorder text-lightInputText text-sm rounded-0 focus:ring-lightPrimary focus:border-lightPrimary dark:focus:ring-darkPrimary dark:focus:border-darkPrimary
|
|
19
|
+
blue-500 focus:border-blue-500 block w-20 p-2.5 dark:bg-darkInputBackground dark:border-darkInputBorder placeholder-lightInputPlaceholderText dark:placeholder-darkInputPlaceholderText dark:text-darkInputText translate-y-0"
|
|
20
20
|
:class="{'rounded-l-md': !$slots.prefix && !prefix, 'rounded-r-md': !$slots.suffix && !suffix, 'w-full': fullWidth}"
|
|
21
21
|
:disabled="readonly"
|
|
22
22
|
>
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
</div>
|
|
27
27
|
<span
|
|
28
28
|
v-if="$slots.suffix || suffix"
|
|
29
|
-
class="inline-flex items-center px-3 text-sm text-
|
|
29
|
+
class="inline-flex items-center px-3 text-sm text-lightInputText bg-lightInputBackground border border-s-0 border-lightInputBorder rounded-e-md dark:bg-darkInputBackground dark:text-darkInputText dark:border-darkInputBorder ">
|
|
30
30
|
<slot name="suffix">{{ suffix }}</slot>
|
|
31
31
|
</span>
|
|
32
32
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<JsonViewer
|
|
3
|
+
class="afcl-json-viewer min-w-[6rem]"
|
|
4
|
+
:value="value"
|
|
5
|
+
:expandDepth="expandDepth"
|
|
6
|
+
copyable
|
|
7
|
+
sort
|
|
8
|
+
:theme="currentTheme"
|
|
9
|
+
/>
|
|
10
|
+
</template>
|
|
11
|
+
|
|
12
|
+
<script setup lang="ts">
|
|
13
|
+
import { computed } from 'vue'
|
|
14
|
+
import { JsonViewer } from 'vue3-json-viewer'
|
|
15
|
+
import { useCoreStore } from '@/stores/core'
|
|
16
|
+
|
|
17
|
+
defineProps<{
|
|
18
|
+
value: any
|
|
19
|
+
expandDepth?: number
|
|
20
|
+
}>()
|
|
21
|
+
|
|
22
|
+
const coreStore = useCoreStore()
|
|
23
|
+
|
|
24
|
+
const currentTheme = computed(() => (coreStore.theme === 'dark' ? 'dark' : 'light'))
|
|
25
|
+
</script>
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
v-bind="$attrs"
|
|
4
4
|
:to="props.to"
|
|
5
5
|
type="submit"
|
|
6
|
-
class="afcl-link-button flex items-center justify-center gap-1 text-
|
|
7
|
-
focus:ring-4 focus:outline-none focus:ring-
|
|
6
|
+
class="afcl-link-button flex items-center justify-center gap-1 text-lightButtonsText bg-lightButtonsBackground border border-lightButtonsBorder dark:bg-darkButtonsBackground hover:bg-lightButtonsHover hover:border-lightButtonsBorderHover
|
|
7
|
+
focus:ring-4 focus:outline-none focus:ring-lightButtonFocusRing focus:ring-opacity-50 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:focus:ring-darkButtonFocusRing dark:text-darkButtonsText dark:border-darkButtonsBorder dark:hover:bg-darkButtonsHover dark:hover:border-darkButtonsBorderHover"
|
|
8
8
|
:class="{
|
|
9
9
|
'cursor-default': props.disabled,
|
|
10
10
|
'opacity-50': props.disabled,
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
<script setup lang="ts">
|
|
19
19
|
|
|
20
20
|
const props = defineProps<{
|
|
21
|
-
disabled
|
|
21
|
+
disabled?: boolean,
|
|
22
22
|
to: string,
|
|
23
23
|
}>();
|
|
24
24
|
|
|
@@ -63,8 +63,8 @@ const optionsBase = {
|
|
|
63
63
|
show: false,
|
|
64
64
|
fontFamily: "Inter, sans-serif",
|
|
65
65
|
label: "",
|
|
66
|
-
formatter: function (w) {
|
|
67
|
-
const sum = w.globals.seriesTotals.reduce((a, b) => {
|
|
66
|
+
formatter: function (w: any) {
|
|
67
|
+
const sum = w.globals.seriesTotals.reduce((a: any, b: any) => {
|
|
68
68
|
return a + b
|
|
69
69
|
}, 0)
|
|
70
70
|
return sum
|
|
@@ -74,7 +74,7 @@ const optionsBase = {
|
|
|
74
74
|
show: true,
|
|
75
75
|
fontFamily: "Inter, sans-serif",
|
|
76
76
|
offsetY: -20,
|
|
77
|
-
formatter: function (value) {
|
|
77
|
+
formatter: function (value: any) {
|
|
78
78
|
return value + "k"
|
|
79
79
|
},
|
|
80
80
|
},
|
|
@@ -100,14 +100,14 @@ const optionsBase = {
|
|
|
100
100
|
},
|
|
101
101
|
yaxis: {
|
|
102
102
|
labels: {
|
|
103
|
-
formatter: function (value) {
|
|
103
|
+
formatter: function (value: any) {
|
|
104
104
|
return value;
|
|
105
105
|
},
|
|
106
106
|
},
|
|
107
107
|
},
|
|
108
108
|
xaxis: {
|
|
109
109
|
labels: {
|
|
110
|
-
formatter: function (value) {
|
|
110
|
+
formatter: function (value: any) {
|
|
111
111
|
return value;
|
|
112
112
|
},
|
|
113
113
|
},
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="relative mt-4 lg:mt-10 w-full max-w-[700px] bg-
|
|
3
|
-
<span class="absolute -top-6 left-0 text-sm text-
|
|
4
|
-
<span class="absolute -top-6 right-0 text-sm text-
|
|
2
|
+
<div class="relative mt-4 lg:mt-10 w-full max-w-[700px] bg-lightProgressBarUnfilledColor rounded-full h-2.5 dark:bg-darkProgressBarUnfilledColor">
|
|
3
|
+
<span class="absolute -top-6 left-0 text-sm text-lightProgressBarText dark:text-darkProgressBarText">{{ leftLabel }}</span>
|
|
4
|
+
<span class="absolute -top-6 right-0 text-sm text-lightProgressBarText dark:text-darkProgressBarText">{{ rightLabel }}</span>
|
|
5
5
|
<div
|
|
6
|
-
class="bg-
|
|
6
|
+
class="bg-lightProgressBarFilledColor dark:bg-darkProgressBarFilledColor h-2.5 rounded-full transition-all duration-300 ease-in-out"
|
|
7
7
|
:style="{ width: `${percentage}%` }"
|
|
8
8
|
></div>
|
|
9
|
-
<span v-if="showValues" class="absolute top-4 left-0 text-sm text-
|
|
10
|
-
<span v-if="showProgress" class="absolute top-4 right-1/2 translate-x-1/2 text-sm text-
|
|
11
|
-
<span v-if="showValues" class="absolute top-4 right-0 text-sm text-
|
|
9
|
+
<span v-if="showValues" class="absolute top-4 left-0 text-sm text-lightProgressBarText dark:text-darkProgressBarText">{{ formatValue(minValue) }}</span>
|
|
10
|
+
<span v-if="showProgress" class="absolute top-4 right-1/2 translate-x-1/2 text-sm text-lightProgressBarText dark:text-darkProgressBarText">{{ progressText }}</span>
|
|
11
|
+
<span v-if="showValues" class="absolute top-4 right-0 text-sm text-lightProgressBarText dark:text-darkProgressBarText">{{ formatValue(maxValue) }}</span>
|
|
12
12
|
</div>
|
|
13
13
|
</template>
|
|
14
14
|
|
|
@@ -1,55 +1,58 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="afcl-select relative inline-block" ref="internalSelect"
|
|
2
|
+
<div class="afcl-select afcl-select-wrapper relative inline-block" ref="internalSelect"
|
|
3
3
|
:class="{'opacity-50': readonly}"
|
|
4
4
|
>
|
|
5
5
|
<div class="relative">
|
|
6
6
|
<input
|
|
7
7
|
ref="inputEl"
|
|
8
8
|
type="text"
|
|
9
|
-
:readonly="readonly"
|
|
9
|
+
:readonly="readonly || searchDisabled"
|
|
10
10
|
v-model="search"
|
|
11
11
|
@click="inputClick"
|
|
12
12
|
@input="inputInput"
|
|
13
|
-
class="block w-full pl-3 pr-10 py-2.5 border border-
|
|
13
|
+
class="block w-full pl-3 pr-10 py-2.5 border border-lightDropownButtonsBorder rounded-md leading-5 bg-lightDropdownButtonsBackground
|
|
14
|
+
placeholder-lightDropdownButtonsPlaceholderText text-lightDropdownButtonsText sm:text-sm transition duration-150 ease-in-out dark:bg-darkDropdownButtonsBackground dark:border-darkDropdownButtonsBorder dark:placeholder-darkDropdownButtonsPlaceholderText
|
|
15
|
+
dark:text-darkDropdownButtonsText focus:ring-lightPrimary focus:border-lightPrimary dark:focus:ring-darkPrimary dark:focus:border-darkPrimary"
|
|
14
16
|
autocomplete="off" data-custom="no-autofill"
|
|
15
17
|
:placeholder="
|
|
16
18
|
selectedItems.length && !multiple ? '' : (showDropdown ? $t('Search') : placeholder || $t('Select...'))
|
|
17
19
|
"
|
|
18
20
|
/>
|
|
19
21
|
|
|
20
|
-
<div v-if="!multiple && selectedItems.length" class="absolute pointer-events-none inset-y-0 left-2 flex items-center pr-2 px-1">
|
|
22
|
+
<div v-if="!multiple && selectedItems.length" class="text-lightDropdownButtonsText dark:text-darkDropdownButtonsText absolute pointer-events-none inset-y-0 left-2 flex items-center pr-2 px-1">
|
|
21
23
|
<slot
|
|
22
24
|
name="selected-item"
|
|
23
25
|
:option="selectedItems[0]"
|
|
24
26
|
></slot>
|
|
25
|
-
<span v-if="!$slots['selected-item']" class="text-
|
|
27
|
+
<span v-if="!$slots['selected-item']" class="text-lightDropdownButtonsText dark:text-darkDropdownButtonsText font-medium ">
|
|
26
28
|
{{ selectedItems[0]?.label }}
|
|
27
29
|
</span>
|
|
28
30
|
</div>
|
|
29
31
|
|
|
30
32
|
<div class="absolute inset-y-0 right-2 flex items-center pointer-events-none">
|
|
31
33
|
<!-- triangle icon -->
|
|
32
|
-
<IconCaretDownSolid class="h-5 w-5 text-lightPrimary dark:text-
|
|
34
|
+
<IconCaretDownSolid class="h-5 w-5 text-lightPrimary dark:text-darkPrimary opacity-50 transition duration-150 ease-in"
|
|
33
35
|
:class="{ 'transform rotate-180': showDropdown }"
|
|
34
36
|
/>
|
|
35
37
|
</div>
|
|
36
38
|
</div>
|
|
37
|
-
<teleport to="body" v-if="teleportToBody && showDropdown">
|
|
38
|
-
<div ref="dropdownEl" :style="getDropdownPosition" :class="{'shadow-none': isTop}"
|
|
39
|
-
class="fixed
|
|
40
|
-
dark:border-gray-600 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm max-h-48"
|
|
39
|
+
<teleport to="body" v-if="(teleportToBody || teleportToTop) && showDropdown">
|
|
40
|
+
<div ref="dropdownEl" :style="getDropdownPosition" :class="{'shadow-none': isTop, 'z-[5]': teleportToBody, 'z-[1000]': teleportToTop}"
|
|
41
|
+
class="fixed w-full bg-lightDropdownOptionsBackground shadow-lg dark:shadow-black dark:bg-darkDropdownOptionsBackground
|
|
42
|
+
dark:border-gray-600 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm max-h-48"
|
|
43
|
+
@scroll="handleDropdownScroll">
|
|
41
44
|
<div
|
|
42
45
|
v-for="item in filteredItems"
|
|
43
46
|
:key="item.value"
|
|
44
|
-
class="px-4 py-2 cursor-pointer hover:bg-
|
|
45
|
-
:class="{ 'bg-
|
|
47
|
+
class="px-4 py-2 cursor-pointer hover:bg-lightDropdownOptionsHoverBackground dark:hover:bg-darkDropdownOptionsHoverBackground text-lightDropdownOptionsText dark:text-darkDropdownOptionsText"
|
|
48
|
+
:class="{ 'bg-lightDropdownPicked dark:bg-darkDropdownPicked': selectedItems.includes(item) }"
|
|
46
49
|
@click="toogleItem(item)"
|
|
47
50
|
>
|
|
48
51
|
<slot name="item" :option="item"></slot>
|
|
49
52
|
<label v-if="!$slots.item" :for="item.value">{{ item.label }}</label>
|
|
50
53
|
</div>
|
|
51
|
-
<div v-if="!filteredItems.length" class="px-4 py-2 cursor-pointer text-
|
|
52
|
-
{{ options
|
|
54
|
+
<div v-if="!filteredItems.length" class="px-4 py-2 cursor-pointer text-lightDropdownOptionsText dark:text-darkDropdownOptionsText">
|
|
55
|
+
{{ options?.length ? $t('No results found') : $t('No items here') }}
|
|
53
56
|
</div>
|
|
54
57
|
|
|
55
58
|
<div v-if="$slots['extra-item']" class="px-4 py-2 dark:text-gray-400">
|
|
@@ -58,24 +61,24 @@
|
|
|
58
61
|
</div>
|
|
59
62
|
</teleport>
|
|
60
63
|
|
|
61
|
-
<div v-if="!teleportToBody && showDropdown" ref="dropdownEl" :style="dropdownStyle" :class="{'shadow-none': isTop}"
|
|
62
|
-
class="absolute z-10 mt-1 w-full bg-
|
|
63
|
-
dark:border-gray-600 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm max-h-48"
|
|
64
|
+
<div v-if="!teleportToBody && !teleportToTop && showDropdown" ref="dropdownEl" :style="dropdownStyle" :class="{'shadow-none': isTop}"
|
|
65
|
+
class="absolute z-10 mt-1 w-full bg-lightDropdownOptionsBackground shadow-lg text-lightDropdownButtonsText dark:shadow-black dark:bg-darkDropdownOptionsBackground
|
|
66
|
+
dark:border-gray-600 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm max-h-48"
|
|
67
|
+
@scroll="handleDropdownScroll">
|
|
64
68
|
<div
|
|
65
69
|
v-for="item in filteredItems"
|
|
66
70
|
:key="item.value"
|
|
67
|
-
class="px-4 py-2 cursor-pointer hover:bg-
|
|
68
|
-
:class="{ 'bg-
|
|
71
|
+
class="px-4 py-2 cursor-pointer text-lightDropdownOptionsText hover:bg-lightDropdownOptionsHoverBackground dark:hover:bg-darkDropdownOptionsHoverBackground dark:text-darkDropdownOptionsText"
|
|
72
|
+
:class="{ 'bg-lightDropdownPicked dark:bg-darkDropdownPicked': selectedItems.includes(item) }"
|
|
69
73
|
@click="toogleItem(item)"
|
|
70
74
|
>
|
|
71
75
|
<slot name="item" :option="item"></slot>
|
|
72
76
|
<label v-if="!$slots.item" :for="item.value">{{ item.label }}</label>
|
|
73
77
|
</div>
|
|
74
|
-
<div v-if="!filteredItems.length" class="px-4 py-2 cursor-pointer text-
|
|
75
|
-
{{ options
|
|
78
|
+
<div v-if="!filteredItems.length" class="px-4 py-2 cursor-pointer text-lightDropdownOptionsText dark:text-darkDropdownOptionsText">
|
|
79
|
+
{{ options?.length ? $t('No results found') : $t('No items here') }}
|
|
76
80
|
</div>
|
|
77
|
-
|
|
78
|
-
<div v-if="$slots['extra-item']" class="px-4 py-2 dark:text-gray-400">
|
|
81
|
+
<div v-if="$slots['extra-item']" class="px-4 py-2 dark:text-darkDropdownOptionsText">
|
|
79
82
|
<slot name="extra-item"></slot>
|
|
80
83
|
</div>
|
|
81
84
|
|
|
@@ -87,12 +90,12 @@
|
|
|
87
90
|
<template v-for="item in selectedItems" :key="`afcl-select-${item.value}`">
|
|
88
91
|
<slot name="selected-item" :item="item"></slot>
|
|
89
92
|
<div v-if="!$slots['selected-item']"
|
|
90
|
-
class="bg-
|
|
93
|
+
class="bg-lightDropdownMultipleSelectBackground text-lightDropdownMultipleSelectText text-xs font-medium me-2 px-2.5 py-0.5 rounded dark:bg-darkDropdownMultipleSelectBackground dark:text-darkDropdownMultipleSelectText">
|
|
91
94
|
<span>{{ item.label }}</span>
|
|
92
95
|
<button
|
|
93
96
|
type="button"
|
|
94
97
|
@click="toogleItem(item)"
|
|
95
|
-
class="z-index-100 flex-shrink-0 ml-1 h-4 w-4 -mr-1 rounded-full inline-flex items-center justify-center text-
|
|
98
|
+
class="z-index-100 flex-shrink-0 ml-1 h-4 w-4 -mr-1 rounded-full inline-flex items-center justify-center text-lightDropdownMultipleSelectIcon hover:text-lightDropdownMultipleSelectIconHover dark:text-darkDropdownMultipleSelectIcon dark:hover:text-darkDropdownMultipleSelectIconHover focus:outline-none focus:text-lightDropdownMultipleSelectIconFocus focus:bg-lightDropdownMultipleSelectIconFocusBackground dark:focus:text-darkDropdownMultipleSelectIconFocus dark:focus:bg-darkDropdownMultipleSelectIconFocusBackground"
|
|
96
99
|
>
|
|
97
100
|
<span class="sr-only">{{ $t('Remove item') }}</span>
|
|
98
101
|
<svg class="h-2 w-2" stroke="currentColor" fill="none" viewBox="0 0 8 8">
|
|
@@ -111,14 +114,15 @@
|
|
|
111
114
|
</template>
|
|
112
115
|
|
|
113
116
|
<script setup lang="ts">
|
|
114
|
-
import { ref, computed, onMounted, onUnmounted, watch, type Ref } from 'vue';
|
|
117
|
+
import { ref, computed, onMounted, onUnmounted, watch, nextTick,type PropType, type Ref } from 'vue';
|
|
115
118
|
import { IconCaretDownSolid } from '@iconify-prerendered/vue-flowbite';
|
|
116
119
|
import { useElementSize } from '@vueuse/core'
|
|
117
120
|
|
|
118
121
|
const props = defineProps({
|
|
119
122
|
options: Array,
|
|
120
123
|
modelValue: {
|
|
121
|
-
|
|
124
|
+
type: Array as PropType<(string | number)[]>,
|
|
125
|
+
default: () => [],
|
|
122
126
|
},
|
|
123
127
|
multiple: {
|
|
124
128
|
type: Boolean,
|
|
@@ -132,13 +136,21 @@ const props = defineProps({
|
|
|
132
136
|
type: Boolean,
|
|
133
137
|
default: false,
|
|
134
138
|
},
|
|
139
|
+
searchDisabled: {
|
|
140
|
+
type: Boolean,
|
|
141
|
+
default: false,
|
|
142
|
+
},
|
|
135
143
|
teleportToBody: {
|
|
136
144
|
type: Boolean,
|
|
137
145
|
default: false,
|
|
138
146
|
},
|
|
147
|
+
teleportToTop: {
|
|
148
|
+
type: Boolean,
|
|
149
|
+
default: false,
|
|
150
|
+
},
|
|
139
151
|
});
|
|
140
152
|
|
|
141
|
-
const emit = defineEmits(['update:modelValue']);
|
|
153
|
+
const emit = defineEmits(['update:modelValue', 'scroll-near-end', 'search']);
|
|
142
154
|
|
|
143
155
|
const search = ref('');
|
|
144
156
|
const showDropdown = ref(false);
|
|
@@ -159,24 +171,27 @@ function inputInput() {
|
|
|
159
171
|
selectedItems.value = [];
|
|
160
172
|
emit('update:modelValue', null);
|
|
161
173
|
}
|
|
174
|
+
if (!props.searchDisabled) {
|
|
175
|
+
emit('search', search.value);
|
|
176
|
+
}
|
|
162
177
|
}
|
|
163
178
|
|
|
164
179
|
function updateFromProps() {
|
|
165
180
|
if (props.modelValue !== undefined) {
|
|
166
181
|
if (!props.multiple) {
|
|
167
|
-
const el = props.options
|
|
182
|
+
const el = props.options?.find((item: any) => item.value === props.modelValue);
|
|
168
183
|
if (el) {
|
|
169
184
|
selectedItems.value = [el];
|
|
170
185
|
} else {
|
|
171
186
|
selectedItems.value = [];
|
|
172
187
|
}
|
|
173
188
|
} else {
|
|
174
|
-
selectedItems.value = props.options
|
|
189
|
+
selectedItems.value = props.options?.filter((item: any) => props.modelValue?.includes(item.value)) || [];
|
|
175
190
|
}
|
|
176
191
|
}
|
|
177
192
|
}
|
|
178
193
|
|
|
179
|
-
function inputClick() {
|
|
194
|
+
async function inputClick() {
|
|
180
195
|
if (props.readonly) return;
|
|
181
196
|
// Toggle local dropdown
|
|
182
197
|
showDropdown.value = !showDropdown.value;
|
|
@@ -184,6 +199,11 @@ function inputClick() {
|
|
|
184
199
|
if (!showDropdown.value && !search.value) {
|
|
185
200
|
search.value = '';
|
|
186
201
|
}
|
|
202
|
+
|
|
203
|
+
if(props.teleportToBody){
|
|
204
|
+
await nextTick();
|
|
205
|
+
handleScroll();
|
|
206
|
+
}
|
|
187
207
|
}
|
|
188
208
|
|
|
189
209
|
watch(
|
|
@@ -221,6 +241,15 @@ const handleScroll = () => {
|
|
|
221
241
|
}
|
|
222
242
|
};
|
|
223
243
|
|
|
244
|
+
const handleDropdownScroll = (event: Event) => {
|
|
245
|
+
const target = event.target as HTMLElement;
|
|
246
|
+
const threshold = 10; // pixels from bottom
|
|
247
|
+
|
|
248
|
+
if (target.scrollTop + target.clientHeight >= target.scrollHeight - threshold) {
|
|
249
|
+
emit('scroll-near-end');
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
|
|
224
253
|
onMounted(() => {
|
|
225
254
|
updateFromProps();
|
|
226
255
|
|
|
@@ -240,8 +269,13 @@ onMounted(() => {
|
|
|
240
269
|
}
|
|
241
270
|
});
|
|
242
271
|
|
|
243
|
-
const filteredItems = computed(() => {
|
|
244
|
-
|
|
272
|
+
const filteredItems: Ref<any[]> = computed(() => {
|
|
273
|
+
|
|
274
|
+
if (props.searchDisabled) {
|
|
275
|
+
return props.options || [];
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return (props.options || []).filter((item: any) =>
|
|
245
279
|
item.label.toLowerCase().includes(search.value.toLowerCase())
|
|
246
280
|
);
|
|
247
281
|
});
|
|
@@ -262,7 +296,7 @@ const removeClickListener = () => {
|
|
|
262
296
|
document.removeEventListener('click', handleClickOutside);
|
|
263
297
|
};
|
|
264
298
|
|
|
265
|
-
const toogleItem = (item) => {
|
|
299
|
+
const toogleItem = (item: any) => {
|
|
266
300
|
if (selectedItems.value.includes(item)) {
|
|
267
301
|
selectedItems.value = selectedItems.value.filter(i => i.value !== item.value);
|
|
268
302
|
} else {
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div v-if="type === 'image'" role="status" class="flex animate-pulse items-center justify-center bg-
|
|
3
|
-
<svg class="w-10 h-10 text-
|
|
2
|
+
<div v-if="type === 'image'" role="status" class="flex animate-pulse items-center justify-center bg-lightSkeletonBackgroundColor rounded dark:bg-darkSkeletonBackgroundColor">
|
|
3
|
+
<svg class="w-10 h-10 text-lightSkeletonIconColor dark:text-darkSkeletonIconColor" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 18">
|
|
4
4
|
<path d="M18 0H2a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2Zm-5.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3Zm4.376 10.481A1 1 0 0 1 16 15H4a1 1 0 0 1-.895-1.447l3.5-7A1 1 0 0 1 7.468 6a.965.965 0 0 1 .9.5l2.775 4.757 1.546-1.887a1 1 0 0 1 1.618.1l2.541 4a1 1 0 0 1 .028 1.011Z"/>
|
|
5
5
|
</svg>
|
|
6
6
|
</div>
|
|
7
|
-
<div v-else-if="type === 'video'" role="status" class="flex items-center justify-center max-w-sm bg-
|
|
8
|
-
<svg class="w-10 h-10 text-
|
|
7
|
+
<div v-else-if="type === 'video'" role="status" class="flex items-center justify-center max-w-sm bg-lightSkeletonBackgroundColor rounded-lg animate-pulse dark:bg-darkSkeletonBackgroundColor">
|
|
8
|
+
<svg class="w-10 h-10 text-lightSkeletonIconColor dark:text-darkSkeletonIconColor" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 16 20">
|
|
9
9
|
<path d="M5 5V.13a2.96 2.96 0 0 0-1.293.749L.879 3.707A2.98 2.98 0 0 0 .13 5H5Z"/>
|
|
10
10
|
<path d="M14.066 0H7v5a2 2 0 0 1-2 2H0v11a1.97 1.97 0 0 0 1.934 2h12.132A1.97 1.97 0 0 0 16 18V2a1.97 1.97 0 0 0-1.934-2ZM9 13a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-2a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v2Zm4 .382a1 1 0 0 1-1.447.894L10 13v-2l1.553-1.276a1 1 0 0 1 1.447.894v2.764Z"/>
|
|
11
11
|
</svg>
|
|
12
12
|
<span class="sr-only">Loading...</span>
|
|
13
13
|
</div>
|
|
14
|
-
<svg v-else-if="type === 'avatar'" class="me-3 animate-pulse text-
|
|
14
|
+
<svg v-else-if="type === 'avatar'" class="me-3 animate-pulse text-lightSkeletonIconColor dark:text-darkSkeletonBackgroundColor" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
|
|
15
15
|
<path d="M10 0a10 10 0 1 0 10 10A10.011 10.011 0 0 0 10 0Zm0 5a3 3 0 1 1 0 6 3 3 0 0 1 0-6Zm0 13a8.949 8.949 0 0 1-4.951-1.488A3.987 3.987 0 0 1 9 13h2a3.987 3.987 0 0 1 3.951 3.512A8.949 8.949 0 0 1 10 18Z"/>
|
|
16
16
|
</svg>
|
|
17
|
-
<div v-else role="status" class="flex items-center justify-center animate-pulse bg-
|
|
17
|
+
<div v-else role="status" class="flex items-center justify-center animate-pulse bg-lightSkeletonIconColor rounded-full dark:bg-darkSkeletonBackgroundColor">
|
|
18
18
|
<span class="sr-only">Loading...</span>
|
|
19
19
|
</div>
|
|
20
20
|
</template>
|