adminforth 2.25.0 → 2.26.0-next.2
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/bundle.js +2 -1
- package/commands/createApp/templates/Dockerfile.hbs +12 -0
- package/commands/createApp/templates/package.json.hbs +18 -6
- package/commands/createApp/templates/pnpm_templates/pnpm-lock.yaml.hbs +8 -0
- package/commands/createApp/templates/pnpm_templates/pnpm-workspace.yaml.hbs +2 -0
- package/commands/createApp/templates/readme.md.hbs +23 -4
- package/commands/createApp/utils.js +107 -26
- package/commands/createPlugin/utils.js +5 -6
- package/commands/postinstall.js +1 -1
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/modules/codeInjector.d.ts +11 -2
- package/dist/modules/codeInjector.d.ts.map +1 -1
- package/dist/modules/codeInjector.js +197 -49
- package/dist/modules/codeInjector.js.map +1 -1
- package/dist/modules/styles.d.ts +4 -0
- package/dist/modules/styles.d.ts.map +1 -1
- package/dist/modules/styles.js +4 -0
- package/dist/modules/styles.js.map +1 -1
- package/dist/modules/utils.d.ts.map +1 -1
- package/dist/servers/express.d.ts +0 -1
- package/dist/servers/express.d.ts.map +1 -1
- package/dist/spa/README.md +4 -4
- package/dist/spa/package-lock.json +1889 -1427
- package/dist/spa/package.json +3 -3
- package/dist/spa/pnpm-lock.yaml +3546 -0
- package/dist/spa/src/afcl/Button.vue +12 -5
- package/dist/spa/src/afcl/Dialog.vue +155 -126
- package/dist/spa/src/afcl/Modal.vue +25 -6
- package/dist/spa/src/afcl/Select.vue +6 -2
- package/dist/spa/src/components/MenuLink.vue +18 -4
- package/dist/spa/src/components/Sidebar.vue +1 -1
- package/dist/spa/src/components/ThreeDotsMenu.vue +1 -1
- package/dist/spa/src/components/ValueRenderer.vue +1 -1
- package/dist/spa/src/stores/core.ts +1 -2
- package/dist/spa/src/types/Back.ts +2 -2
- package/dist/spa/src/types/Common.ts +8 -0
- package/dist/spa/src/types/FrontendAPI.ts +0 -3
- package/dist/spa/src/utils/utils.ts +57 -2
- package/dist/spa/src/views/CreateView.vue +12 -11
- package/dist/spa/src/views/EditView.vue +9 -13
- package/dist/spa/vite.config.ts +29 -40
- package/dist/types/Back.d.ts +2 -3
- package/dist/types/Back.d.ts.map +1 -1
- package/dist/types/Common.d.ts +6 -0
- package/dist/types/Common.d.ts.map +1 -1
- package/dist/types/Common.js.map +1 -1
- package/package.json +15 -7
- package/scripts/postinstall.js +25 -0
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
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"
|
|
7
7
|
:class="{
|
|
8
8
|
'cursor-default opacity-50 pointer-events-none': props.disabled,
|
|
9
|
-
'active brightness-200 hover:brightness-150' : props.active
|
|
9
|
+
'active brightness-200 hover:brightness-150' : props.active,
|
|
10
|
+
'text-lightSecondaryContrast/70 bg-lightSecondary border-lightSecondaryContrast/30 dark:bg-darkSecondary hover:bg-lightSecondary/60 hover:border-lightSecondaryContrast/60 focus:ring-lightSecondary dark:focus:ring-darkSecondary/40 dark:text-darkSecondaryContrast dark:border-darkSecondaryContrast/40 dark:hover:bg-darkSecondary/60 dark:hover:border-darkSecondary/60': props.mode === 'secondary',
|
|
10
11
|
}"
|
|
11
12
|
>
|
|
12
13
|
<svg v-if="props.loader"
|
|
@@ -18,10 +19,16 @@
|
|
|
18
19
|
|
|
19
20
|
<script setup lang="ts">
|
|
20
21
|
|
|
21
|
-
const props = defineProps
|
|
22
|
-
loader
|
|
23
|
-
disabled
|
|
24
|
-
active
|
|
22
|
+
const props = withDefaults(defineProps<{
|
|
23
|
+
loader?: boolean;
|
|
24
|
+
disabled?: boolean;
|
|
25
|
+
active?: boolean;
|
|
26
|
+
mode?: 'primary' | 'secondary';
|
|
27
|
+
}>(), {
|
|
28
|
+
loader: false,
|
|
29
|
+
disabled: false,
|
|
30
|
+
active: false,
|
|
31
|
+
mode: 'primary'
|
|
25
32
|
});
|
|
26
33
|
|
|
27
34
|
</script>
|
|
@@ -1,124 +1,188 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
<Modal
|
|
3
|
+
ref="modalRef"
|
|
4
|
+
v-bind="$attrs"
|
|
5
|
+
:closeByClickOutside="clickToCloseOutside || closeByClickOutside"
|
|
6
|
+
:closeByEsc="closeByEsc || closable"
|
|
7
|
+
:beforeCloseFunction="beforeCloseFunction"
|
|
8
|
+
:beforeOpenFunction="beforeOpenFunction"
|
|
9
|
+
:askForCloseConfirmation="askForCloseConfirmation"
|
|
10
|
+
:closeConfirmationText="closeConfirmationText"
|
|
11
|
+
:removeFromDomOnClose="removeFromDomOnClose"
|
|
5
12
|
>
|
|
6
|
-
<
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
>
|
|
49
|
-
{{ button.label }}
|
|
50
|
-
</Button>
|
|
51
|
-
</div>
|
|
52
|
-
</div>
|
|
53
|
-
</div>
|
|
54
|
-
<div>
|
|
55
|
-
<!-- Confirmation Modal -->
|
|
56
|
-
<div
|
|
57
|
-
v-if="showConfirmationOnClose"
|
|
58
|
-
class="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50 z-[60]"
|
|
59
|
-
>
|
|
60
|
-
<div class="bg-white dark:bg-gray-800 p-6 rounded-lg shadow-lg max-w-sm w-full">
|
|
61
|
-
<h2 class="text-lg font-semibold mb-4 text-lightDialogHeaderText dark:text-darkDialogHeaderText">Confirm Close</h2>
|
|
62
|
-
<p class="mb-6 text-lightDialogBodyText dark:text-darkDialogBodyText">{{ props.closeConfirmationText }}</p>
|
|
63
|
-
<div class="flex justify-end">
|
|
64
|
-
<Button
|
|
65
|
-
class="me-3 !bg-gray-50 dark:!bg-gray-700 !text-lightDialogBodyText dark:!text-darkDialogBodyText hover:!bg-gray-100 dark:hover:!bg-gray-600 !border-gray-200 dark:!border-gray-600"
|
|
66
|
-
@click="showConfirmationOnClose = false"
|
|
67
|
-
>
|
|
68
|
-
Cancel
|
|
69
|
-
</Button>
|
|
70
|
-
<Button
|
|
71
|
-
@click="
|
|
72
|
-
showConfirmationOnClose = false;
|
|
73
|
-
modal?.hide();
|
|
74
|
-
"
|
|
75
|
-
>
|
|
76
|
-
Confirm
|
|
77
|
-
</Button>
|
|
78
|
-
</div>
|
|
79
|
-
</div>
|
|
80
|
-
</div>
|
|
81
|
-
</div>
|
|
13
|
+
<template v-if="$slots.trigger" #trigger>
|
|
14
|
+
<slot name="trigger"></slot>
|
|
15
|
+
</template>
|
|
16
|
+
|
|
17
|
+
<!-- Modal header -->
|
|
18
|
+
<div
|
|
19
|
+
v-if="header"
|
|
20
|
+
class="flex items-center justify-between p-4 md:p-5 border-b rounded-t dark:border-darkDialogBreakLine border-lightDialogBreakLine"
|
|
21
|
+
>
|
|
22
|
+
<h3 class="text-xl font-semibold text-lightDialogHeaderText dark:text-darkDialogHeaderText">
|
|
23
|
+
{{ header }}
|
|
24
|
+
</h3>
|
|
25
|
+
<button
|
|
26
|
+
v-if="headerCloseButton"
|
|
27
|
+
type="button"
|
|
28
|
+
class="text-lightDialogCloseButton bg-transparent hover:bg-lightDialogCloseButtonHoverBackground hover:text-lightDialogCloseButtonHover rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center dark:text-darkDialogCloseButton dark:hover:bg-darkDialogCloseButtonHoverBackground dark:hover:text-darkDialogCloseButtonHover"
|
|
29
|
+
@click="tryToHideModal"
|
|
30
|
+
>
|
|
31
|
+
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
|
32
|
+
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
|
|
33
|
+
</svg>
|
|
34
|
+
<span class="sr-only">{{ t('Close Modal') }}</span>
|
|
35
|
+
</button>
|
|
36
|
+
</div>
|
|
37
|
+
<!-- Modal body -->
|
|
38
|
+
<div class="p-4 md:p-5 text-lightDialogBodyText dark:text-darkDialogBodyText">
|
|
39
|
+
<slot></slot>
|
|
40
|
+
</div>
|
|
41
|
+
<!-- Modal footer -->
|
|
42
|
+
<div
|
|
43
|
+
v-if="buttons.length"
|
|
44
|
+
class="flex items-center p-4 md:p-5 border-t border-lightDialogBreakLine rounded-b dark:border-darkDialogBreakLine"
|
|
45
|
+
>
|
|
46
|
+
<Button
|
|
47
|
+
v-for="(button, buttonIndex) in buttons"
|
|
48
|
+
:key="buttonIndex"
|
|
49
|
+
v-bind="button.options"
|
|
50
|
+
:class="{ 'ms-3': buttonIndex > 0 }"
|
|
51
|
+
@click="button.onclick(dialog)"
|
|
52
|
+
>
|
|
53
|
+
{{ button.label }}
|
|
54
|
+
</Button>
|
|
82
55
|
</div>
|
|
83
|
-
</
|
|
56
|
+
</Modal>
|
|
84
57
|
</template>
|
|
85
58
|
|
|
86
59
|
<script setup lang="ts">
|
|
87
60
|
import Button from "./Button.vue";
|
|
88
|
-
import { ref,
|
|
89
|
-
import { Modal } from '
|
|
61
|
+
import { ref, computed, type Ref } from 'vue';
|
|
62
|
+
import { Modal } from '@/afcl'
|
|
63
|
+
import { useI18n } from "vue-i18n";
|
|
64
|
+
|
|
65
|
+
const { t } = useI18n();
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
interface IDialogInsideButtonClickHandler {
|
|
69
|
+
hide: () => void
|
|
70
|
+
}
|
|
90
71
|
|
|
91
|
-
const modalEl = ref(null);
|
|
92
|
-
const modal: Ref<Modal|null> = ref(null);
|
|
93
72
|
|
|
94
73
|
interface DialogButton {
|
|
95
74
|
label: string
|
|
96
|
-
onclick: (dialog:
|
|
75
|
+
onclick: (dialog: IDialogInsideButtonClickHandler) => void
|
|
97
76
|
options?: Record<string, any>
|
|
98
77
|
}
|
|
99
78
|
|
|
79
|
+
|
|
100
80
|
interface DialogProps {
|
|
81
|
+
/**
|
|
82
|
+
* The header text to display in the dialog. If not provided, no header will be displayed.
|
|
83
|
+
*/
|
|
101
84
|
header?: string
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* If true, a close button will be displayed in the dialog header. Default is true.
|
|
88
|
+
*/
|
|
102
89
|
headerCloseButton?: boolean
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* An array of buttons to display in the dialog footer.
|
|
93
|
+
*/
|
|
103
94
|
buttons?: DialogButton[]
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* If true, clicking outside the dialog will close it. Default is true.
|
|
98
|
+
*
|
|
99
|
+
* @deprecated Use `closeByClickOutside` instead
|
|
100
|
+
*/
|
|
104
101
|
clickToCloseOutside?: boolean
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* If true, pressing the Esc key will close the dialog. Default is true.
|
|
105
|
+
*/
|
|
106
|
+
closeByEsc?: boolean
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* If true, clicking outside the dialog will close it. Default is true.
|
|
110
|
+
*/
|
|
111
|
+
closeByClickOutside?: boolean
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Function that will be called before the dialog is closed.
|
|
115
|
+
*/
|
|
105
116
|
beforeCloseFunction?: (() => void | Promise<void>) | null
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Function that will be called before the dialog is opened.
|
|
120
|
+
*/
|
|
106
121
|
beforeOpenFunction?: (() => void | Promise<void>) | null
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Disables close on Ecs button
|
|
125
|
+
*
|
|
126
|
+
* @deprecated Use `closeByEsc` or instead
|
|
127
|
+
*/
|
|
107
128
|
closable?: boolean
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* If true, the dialog will ask for confirmation before closing. Default is false.
|
|
132
|
+
*/
|
|
108
133
|
askForCloseConfirmation?: boolean
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* The text to display in the close confirmation dialog. Default is "Are you sure you want to close this dialog?".
|
|
137
|
+
*/
|
|
109
138
|
closeConfirmationText?: string
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* If true, the dialog will be removed from the DOM when closed. Default is false.
|
|
142
|
+
*/
|
|
143
|
+
removeFromDomOnClose?: boolean
|
|
110
144
|
}
|
|
111
145
|
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
/********** for the backward compatibility ***************/
|
|
152
|
+
class Dialog implements IDialogInsideButtonClickHandler {
|
|
153
|
+
hide: () => void
|
|
154
|
+
constructor( hide: () => void ) {
|
|
155
|
+
this.hide = hide;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
const dialog: Ref<Dialog> = ref(
|
|
159
|
+
new Dialog(
|
|
160
|
+
() => {
|
|
161
|
+
if (dialog.value) {
|
|
162
|
+
tryToHideModal();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
)
|
|
166
|
+
);
|
|
167
|
+
/*************************************************************/
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
const modalRef = ref();
|
|
172
|
+
|
|
112
173
|
const props = withDefaults(defineProps<DialogProps>(), {
|
|
113
174
|
header: '',
|
|
114
175
|
headerCloseButton: true,
|
|
115
176
|
buttons: () => [],
|
|
116
|
-
clickToCloseOutside:
|
|
177
|
+
clickToCloseOutside: false,
|
|
178
|
+
closeByEsc: true,
|
|
179
|
+
closeByClickOutside: true,
|
|
117
180
|
beforeCloseFunction: null,
|
|
118
181
|
beforeOpenFunction: null,
|
|
119
|
-
closable:
|
|
182
|
+
closable: false,
|
|
120
183
|
askForCloseConfirmation: false,
|
|
121
184
|
closeConfirmationText: 'Are you sure you want to close this dialog?',
|
|
185
|
+
removeFromDomOnClose: false,
|
|
122
186
|
})
|
|
123
187
|
|
|
124
188
|
const buttons = computed<DialogButton[]>(() => {
|
|
@@ -129,51 +193,20 @@ const buttons = computed<DialogButton[]>(() => {
|
|
|
129
193
|
{
|
|
130
194
|
label: 'Close',
|
|
131
195
|
onclick: (dialog: any) => {
|
|
132
|
-
|
|
133
|
-
dialog.hide();
|
|
134
|
-
} else {
|
|
135
|
-
showConfirmationOnClose.value = true;
|
|
136
|
-
}
|
|
196
|
+
tryToHideModal();
|
|
137
197
|
},
|
|
138
198
|
options: {}
|
|
139
199
|
}
|
|
140
200
|
];
|
|
141
201
|
});
|
|
142
202
|
|
|
143
|
-
const showConfirmationOnClose = ref(false);
|
|
144
|
-
onMounted(async () => {
|
|
145
|
-
//await one tick when all is mounted
|
|
146
|
-
await nextTick();
|
|
147
|
-
modal.value = new Modal(
|
|
148
|
-
modalEl.value,
|
|
149
|
-
{
|
|
150
|
-
closable: props.closable,
|
|
151
|
-
backdrop: props.clickToCloseOutside ? 'dynamic' : 'static',
|
|
152
|
-
onHide: async () => {
|
|
153
|
-
if (props.beforeCloseFunction) {
|
|
154
|
-
await props.beforeCloseFunction();
|
|
155
|
-
}
|
|
156
|
-
},
|
|
157
|
-
onShow: async () => {
|
|
158
|
-
if (props.beforeOpenFunction) {
|
|
159
|
-
await props.beforeOpenFunction();
|
|
160
|
-
}
|
|
161
|
-
},
|
|
162
|
-
}
|
|
163
|
-
);
|
|
164
|
-
})
|
|
165
|
-
|
|
166
|
-
onUnmounted(() => {
|
|
167
|
-
//destroy tooltip
|
|
168
|
-
modal.value?.destroy();
|
|
169
|
-
})
|
|
170
203
|
|
|
171
204
|
function open() {
|
|
172
|
-
|
|
205
|
+
modalRef.value.open();
|
|
173
206
|
}
|
|
174
207
|
|
|
175
208
|
function close() {
|
|
176
|
-
|
|
209
|
+
modalRef.value.hide();
|
|
177
210
|
}
|
|
178
211
|
|
|
179
212
|
defineExpose({
|
|
@@ -183,11 +216,7 @@ defineExpose({
|
|
|
183
216
|
})
|
|
184
217
|
|
|
185
218
|
function tryToHideModal() {
|
|
186
|
-
|
|
187
|
-
modal.value?.hide();
|
|
188
|
-
} else {
|
|
189
|
-
showConfirmationOnClose.value = true;
|
|
190
|
-
}
|
|
219
|
+
modalRef.value?.tryToHideModal();
|
|
191
220
|
}
|
|
192
221
|
|
|
193
222
|
|
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
<slot name="trigger"></slot>
|
|
7
7
|
</div>
|
|
8
8
|
<Teleport to="body">
|
|
9
|
-
<div v-show="isModalOpen" v-if="!removeFromDom" @click="backdropClick"
|
|
9
|
+
<div v-show="isModalOpen" v-if="!removeFromDom" @click="backdropClick" class="bg-black/50 overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-50 justify-center items-center w-full h-full md:inset-0 h-1rem max-h-full flex" >
|
|
10
10
|
<!-- Modal content -->
|
|
11
|
-
<div class="relative bg-lightDialogBackgorund rounded-lg shadow-sm dark:bg-darkDialogBackgorund">
|
|
11
|
+
<div v-bind="$attrs" class="relative bg-lightDialogBackgorund rounded-lg shadow-sm dark:bg-darkDialogBackgorund">
|
|
12
12
|
|
|
13
13
|
<!-- Modal body -->
|
|
14
|
-
<div class="
|
|
14
|
+
<div class="text-lightDialogBodyText dark:text-darkDialogBodyText">
|
|
15
15
|
<slot ></slot>
|
|
16
16
|
</div>
|
|
17
17
|
|
|
@@ -57,7 +57,8 @@ const removeFromDom = computed(() => {
|
|
|
57
57
|
})
|
|
58
58
|
|
|
59
59
|
interface DialogProps {
|
|
60
|
-
|
|
60
|
+
closeByClickOutside?: boolean
|
|
61
|
+
closeByEsc?: boolean
|
|
61
62
|
beforeCloseFunction?: (() => void | Promise<void>) | null
|
|
62
63
|
beforeOpenFunction?: (() => void | Promise<void>) | null
|
|
63
64
|
askForCloseConfirmation?: boolean
|
|
@@ -66,7 +67,8 @@ interface DialogProps {
|
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
const props = withDefaults(defineProps<DialogProps>(), {
|
|
69
|
-
|
|
70
|
+
closeByClickOutside: true,
|
|
71
|
+
closeByEsc: true,
|
|
70
72
|
beforeCloseFunction: null,
|
|
71
73
|
beforeOpenFunction: null,
|
|
72
74
|
askForCloseConfirmation: false,
|
|
@@ -109,8 +111,25 @@ function toggleModal() {
|
|
|
109
111
|
}
|
|
110
112
|
}
|
|
111
113
|
|
|
114
|
+
function onEsc(event: KeyboardEvent) {
|
|
115
|
+
if (event.key === 'Escape') {
|
|
116
|
+
if (props.closeByEsc) {
|
|
117
|
+
tryToHideModal();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
onMounted(() => {
|
|
123
|
+
document.addEventListener('keydown', onEsc)
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
onUnmounted(() => {
|
|
127
|
+
document.removeEventListener('keydown', onEsc)
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
|
|
112
131
|
function backdropClick(e: MouseEvent) {
|
|
113
|
-
if (props.
|
|
132
|
+
if (props.closeByClickOutside && e.target === e.currentTarget) {
|
|
114
133
|
tryToHideModal();
|
|
115
134
|
}
|
|
116
135
|
}
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
class="block w-full pl-3 pr-10 py-2.5 border border-lightDropownButtonsBorder rounded-md leading-5 bg-lightDropdownButtonsBackground
|
|
14
14
|
placeholder-lightDropdownButtonsPlaceholderText text-lightDropdownButtonsText sm:text-sm transition duration-150 ease-in-out dark:bg-darkDropdownButtonsBackground dark:border-darkDropdownButtonsBorder dark:placeholder-darkDropdownButtonsPlaceholderText
|
|
15
15
|
dark:text-darkDropdownButtonsText focus:ring-lightPrimary focus:border-lightPrimary dark:focus:ring-darkPrimary dark:focus:border-darkPrimary"
|
|
16
|
-
:class="{'cursor-pointer': searchDisabled}"
|
|
16
|
+
:class="[{'cursor-pointer': searchDisabled}, classesForInput]"
|
|
17
17
|
autocomplete="off" data-custom="no-autofill"
|
|
18
18
|
:placeholder="
|
|
19
19
|
selectedItems.length && !multiple ? '' : (showDropdown ? $t('Search') : placeholder || $t('Select...'))
|
|
@@ -122,7 +122,7 @@ import { useElementSize } from '@vueuse/core'
|
|
|
122
122
|
const props = defineProps({
|
|
123
123
|
options: Array,
|
|
124
124
|
modelValue: {
|
|
125
|
-
type: Array as PropType<(string | number)[]>,
|
|
125
|
+
type: Array as PropType<(string | number)[] | (string | number)>,
|
|
126
126
|
default: () => [],
|
|
127
127
|
},
|
|
128
128
|
multiple: {
|
|
@@ -153,6 +153,10 @@ const props = defineProps({
|
|
|
153
153
|
type: Number,
|
|
154
154
|
default: 300,
|
|
155
155
|
},
|
|
156
|
+
classesForInput: {
|
|
157
|
+
type: String,
|
|
158
|
+
default: '',
|
|
159
|
+
},
|
|
156
160
|
});
|
|
157
161
|
|
|
158
162
|
const emit = defineEmits(['update:modelValue', 'scroll-near-end', 'search']);
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<RouterLink
|
|
3
|
-
:to="{name: item.resourceId ? 'resource-list' : item.path, params: item.resourceId ? { resourceId: item.resourceId }: {}}"
|
|
3
|
+
:to="item.url || { name: item.resourceId ? 'resource-list' : item.path, params: item.resourceId ? { resourceId: item.resourceId }: {}}"
|
|
4
|
+
:target="item.isOpenInNewTab ? '_blank' : '_self'"
|
|
4
5
|
class="af-menu-link flex group relative items-center w-full py-2 text-lightSidebarText dark:text-darkSidebarText rounded-default transition-all duration-200 ease-in-out"
|
|
5
6
|
:class="{
|
|
6
7
|
'hover:bg-lightSidebarItemHover hover:text-lightSidebarTextHover dark:hover:bg-darkSidebarItemHover dark:hover:text-darkSidebarTextHover active:bg-lightSidebarActive dark:active:bg-darkSidebarHover': !['divider', 'gap', 'heading'].includes(item.type),
|
|
7
8
|
'pl-6 pr-3.5': (isChild && !isSidebarIconOnly && !isSidebarHovering) || (isChild && isSidebarIconOnly && isSidebarHovering),
|
|
8
9
|
'px-3.5 ': !isChild || (isSidebarIconOnly && !isSidebarHovering),
|
|
9
10
|
'max-w-12': isSidebarIconOnly && !isSidebarHovering,
|
|
10
|
-
'bg-lightSidebarItemActive dark:bg-darkSidebarItemActive': item
|
|
11
|
-
($route.params.resourceId === item.resourceId && $route.name === 'resource-list') :
|
|
12
|
-
($route.name === item.path)
|
|
11
|
+
'bg-lightSidebarItemActive dark:bg-darkSidebarItemActive': isItemActive(item)
|
|
13
12
|
}"
|
|
14
13
|
>
|
|
15
14
|
<component v-if="item.icon" :is="getIcon(item.icon)"
|
|
@@ -61,6 +60,21 @@ import { Tooltip } from '@/afcl';
|
|
|
61
60
|
import { ref, watch, computed } from 'vue';
|
|
62
61
|
import { useCoreStore } from '@/stores/core';
|
|
63
62
|
import { IconFileImageOutline } from '@iconify-prerendered/vue-flowbite';
|
|
63
|
+
import { useRoute } from 'vue-router';
|
|
64
|
+
|
|
65
|
+
const route = useRoute();
|
|
66
|
+
|
|
67
|
+
const isItemActive = (item: any) => {
|
|
68
|
+
if (item.url) {
|
|
69
|
+
return route.fullPath === item.url;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (item.resourceId) {
|
|
73
|
+
return route.params.resourceId === item.resourceId && route.name === 'resource-list';
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return route.name === item.path;
|
|
77
|
+
};
|
|
64
78
|
|
|
65
79
|
const props = defineProps(['item', 'isChild', 'isSidebarIconOnly', 'isSidebarHovering']);
|
|
66
80
|
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
:class="{
|
|
29
29
|
'hidden': !(coreStore.config?.showBrandLogoInSidebar !== false && (!iconOnlySidebarEnabled || !isSidebarIconOnly || (isSidebarIconOnly && isSidebarHovering))) }"
|
|
30
30
|
/>
|
|
31
|
-
<img :src="loadFile(coreStore.config?.iconOnlySidebar?.logo || '')" :alt="`${ coreStore.config?.brandName } Logo`" class="af-sidebar-icon-only-logo h-8" :class="{ 'hidden': !(coreStore.config?.showBrandLogoInSidebar !== false && coreStore.config?.iconOnlySidebar?.logo && iconOnlySidebarEnabled && isSidebarIconOnly && !isSidebarHovering) }" />
|
|
31
|
+
<img v-if="coreStore.config?.iconOnlySidebar?.logo" :src="loadFile(coreStore.config?.iconOnlySidebar?.logo || '')" :alt="`${ coreStore.config?.brandName } Logo`" class="af-sidebar-icon-only-logo h-8" :class="{ 'hidden': !(coreStore.config?.showBrandLogoInSidebar !== false && coreStore.config?.iconOnlySidebar?.logo && iconOnlySidebarEnabled && isSidebarIconOnly && !isSidebarHovering) }" />
|
|
32
32
|
<span
|
|
33
33
|
v-if="coreStore.config?.showBrandNameInSidebar && (!iconOnlySidebarEnabled || !isSidebarIconOnly || (isSidebarIconOnly && isSidebarHovering))"
|
|
34
34
|
class="af-title self-center text-lightNavbarText-size font-semibold sm:text-lightNavbarText-size whitespace-nowrap dark:text-darkSidebarText text-lightSidebarText"
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
'hidden': !showDropdown,
|
|
19
19
|
'left-0 md:left-auto': checkboxes && checkboxes.length > 0
|
|
20
20
|
}"
|
|
21
|
-
class="absolute z-30 mt-3 bg-lightThreeDotsMenuBodyBackground divide-y divide-gray-100 rounded-lg shadow w-
|
|
21
|
+
class="absolute z-30 mt-3 bg-lightThreeDotsMenuBodyBackground divide-y divide-gray-100 rounded-lg shadow w-max max-w-64 dark:bg-darkThreeDotsMenuBodyBackground dark:divide-gray-600 right-0">
|
|
22
22
|
<ul class="py-2 text-sm text-lightThreeDotsMenuBodyText dark:text-darkThreeDotsMenuBodyText" aria-labelledby="dropdownMenuIconButton">
|
|
23
23
|
<li v-for="(item, i) in threeDotsDropdownItems" :key="`dropdown-item-${i}`">
|
|
24
24
|
<div
|
|
@@ -117,7 +117,7 @@ import timezone from 'dayjs/plugin/timezone';
|
|
|
117
117
|
import {checkEmptyValues} from '@/utils';
|
|
118
118
|
import { useRoute, useRouter } from 'vue-router';
|
|
119
119
|
import { JsonViewer } from "vue3-json-viewer";
|
|
120
|
-
import "vue3-json-viewer/dist/
|
|
120
|
+
import "vue3-json-viewer/dist/vue3-json-viewer.css";
|
|
121
121
|
import type { AdminForthResourceColumnCommon } from '@/types/Common';
|
|
122
122
|
|
|
123
123
|
import { useCoreStore } from '@/stores/core';
|
|
@@ -92,6 +92,7 @@ export const useCoreStore = defineStore('core', () => {
|
|
|
92
92
|
|
|
93
93
|
// console.log('🔔 subscribeToMenuBadges', mi.badge, JSON.stringify(mi));
|
|
94
94
|
if (mi.badge !== undefined) {
|
|
95
|
+
websocket.unsubscribe(`/opentopic/update-menu-badge/${mi.itemId}`);
|
|
95
96
|
websocket.subscribe(`/opentopic/update-menu-badge/${mi.itemId}`, ({ badge }) => {
|
|
96
97
|
mi.badge = badge;
|
|
97
98
|
});
|
|
@@ -121,8 +122,6 @@ export const useCoreStore = defineStore('core', () => {
|
|
|
121
122
|
item.badge = badge;
|
|
122
123
|
}
|
|
123
124
|
});
|
|
124
|
-
// TODO: This thing was created for something. Find out why
|
|
125
|
-
// websocket.unsubscribeAll();
|
|
126
125
|
subscribeToMenuBadges();
|
|
127
126
|
|
|
128
127
|
}
|
|
@@ -1206,8 +1206,8 @@ interface AdminForthInputConfigCustomization {
|
|
|
1206
1206
|
*
|
|
1207
1207
|
* ```bashcreating rec
|
|
1208
1208
|
* cd custom
|
|
1209
|
-
*
|
|
1210
|
-
*
|
|
1209
|
+
* pnpm init -y
|
|
1210
|
+
* pnpm install highcharts highcharts-vue
|
|
1211
1211
|
* ```
|
|
1212
1212
|
*
|
|
1213
1213
|
* And specify vueUsesFile in AdminForth config:
|
|
@@ -361,6 +361,12 @@ export interface AdminForthResourceInputCommon {
|
|
|
361
361
|
recordLabel?: (item: any) => string,
|
|
362
362
|
|
|
363
363
|
|
|
364
|
+
/**
|
|
365
|
+
* If true, user will not see warning about unsaved changes when tries to leave edit or create page with unsaved changes.
|
|
366
|
+
* default is false
|
|
367
|
+
*/
|
|
368
|
+
dontShowWarningAboutUnsavedChanges?: boolean,
|
|
369
|
+
|
|
364
370
|
/**
|
|
365
371
|
* General options for resource.
|
|
366
372
|
*/
|
|
@@ -1116,6 +1122,8 @@ export interface AdminForthConfigMenuItem {
|
|
|
1116
1122
|
* Item id will be automatically generated from hashed resourceId+Path+label
|
|
1117
1123
|
*/
|
|
1118
1124
|
itemId?: string, // todo move to runtime type
|
|
1125
|
+
|
|
1126
|
+
url?: string
|
|
1119
1127
|
}
|
|
1120
1128
|
|
|
1121
1129
|
|