adminforth 4.4.0 → 4.5.1
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/dist/spa/package-lock.json +0 -14
- package/dist/spa/package.json +0 -1
- package/dist/spa/pnpm-lock.yaml +0 -15
- package/dist/spa/src/afcl/CountryFlag.vue +39 -14
- package/dist/spa/src/afcl/Tooltip.vue +75 -51
- package/dist/spa/src/components/ValueRenderer.vue +1 -1
- package/dist/spa/src/views/ListView.vue +0 -1
- package/package.json +1 -1
|
@@ -50,7 +50,6 @@
|
|
|
50
50
|
"eslint": "^8.57.0",
|
|
51
51
|
"eslint-plugin-vue": "^9.23.0",
|
|
52
52
|
"flag-icons": "^7.2.3",
|
|
53
|
-
"flowbite": "^3.1.2",
|
|
54
53
|
"i18n-iso-countries": "^7.12.0",
|
|
55
54
|
"npm-run-all2": "^9.0.2",
|
|
56
55
|
"portfinder": "^1.0.32",
|
|
@@ -3111,19 +3110,6 @@
|
|
|
3111
3110
|
"dev": true,
|
|
3112
3111
|
"license": "ISC"
|
|
3113
3112
|
},
|
|
3114
|
-
"node_modules/flowbite": {
|
|
3115
|
-
"version": "3.1.2",
|
|
3116
|
-
"resolved": "https://registry.npmjs.org/flowbite/-/flowbite-3.1.2.tgz",
|
|
3117
|
-
"integrity": "sha512-MkwSgbbybCYgMC+go6Da5idEKUFfMqc/AmSjm/2ZbdmvoKf5frLPq/eIhXc9P+rC8t9boZtUXzHDgt5whZ6A/Q==",
|
|
3118
|
-
"dev": true,
|
|
3119
|
-
"license": "MIT",
|
|
3120
|
-
"dependencies": {
|
|
3121
|
-
"@popperjs/core": "^2.9.3",
|
|
3122
|
-
"flowbite-datepicker": "^1.3.1",
|
|
3123
|
-
"mini-svg-data-uri": "^1.4.3",
|
|
3124
|
-
"postcss": "^8.5.1"
|
|
3125
|
-
}
|
|
3126
|
-
},
|
|
3127
3113
|
"node_modules/flowbite-datepicker": {
|
|
3128
3114
|
"version": "1.3.2",
|
|
3129
3115
|
"resolved": "https://registry.npmjs.org/flowbite-datepicker/-/flowbite-datepicker-1.3.2.tgz",
|
package/dist/spa/package.json
CHANGED
package/dist/spa/pnpm-lock.yaml
CHANGED
|
@@ -129,9 +129,6 @@ importers:
|
|
|
129
129
|
flag-icons:
|
|
130
130
|
specifier: ^7.2.3
|
|
131
131
|
version: 7.5.0
|
|
132
|
-
flowbite:
|
|
133
|
-
specifier: ^3.1.2
|
|
134
|
-
version: 3.1.2(rollup@4.60.2)
|
|
135
132
|
i18n-iso-countries:
|
|
136
133
|
specifier: ^7.12.0
|
|
137
134
|
version: 7.14.0
|
|
@@ -1308,9 +1305,6 @@ packages:
|
|
|
1308
1305
|
flowbite@2.5.2:
|
|
1309
1306
|
resolution: {integrity: sha512-kwFD3n8/YW4EG8GlY3Od9IoKND97kitO+/ejISHSqpn3vw2i5K/+ZI8Jm2V+KC4fGdnfi0XZ+TzYqQb4Q1LshA==}
|
|
1310
1307
|
|
|
1311
|
-
flowbite@3.1.2:
|
|
1312
|
-
resolution: {integrity: sha512-MkwSgbbybCYgMC+go6Da5idEKUFfMqc/AmSjm/2ZbdmvoKf5frLPq/eIhXc9P+rC8t9boZtUXzHDgt5whZ6A/Q==}
|
|
1313
|
-
|
|
1314
1308
|
fraction.js@5.3.4:
|
|
1315
1309
|
resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==}
|
|
1316
1310
|
|
|
@@ -3263,15 +3257,6 @@ snapshots:
|
|
|
3263
3257
|
transitivePeerDependencies:
|
|
3264
3258
|
- rollup
|
|
3265
3259
|
|
|
3266
|
-
flowbite@3.1.2(rollup@4.60.2):
|
|
3267
|
-
dependencies:
|
|
3268
|
-
'@popperjs/core': 2.11.8
|
|
3269
|
-
flowbite-datepicker: 1.3.2(rollup@4.60.2)
|
|
3270
|
-
mini-svg-data-uri: 1.4.4
|
|
3271
|
-
postcss: 8.5.30
|
|
3272
|
-
transitivePeerDependencies:
|
|
3273
|
-
- rollup
|
|
3274
|
-
|
|
3275
3260
|
fraction.js@5.3.4: {}
|
|
3276
3261
|
|
|
3277
3262
|
fs.realpath@1.0.0: {}
|
|
@@ -1,14 +1,48 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
|
|
2
|
+
<span v-bind="restAttrs" :class="rootClasses">
|
|
3
|
+
<!-- a hairline edge keeps white stripes visible on a white page -->
|
|
4
|
+
<img
|
|
5
|
+
v-if="flagUrl"
|
|
6
|
+
:src="flagUrl"
|
|
7
|
+
:alt="countryCode"
|
|
8
|
+
class="block w-full h-full object-cover ring-1 ring-black/10 dark:ring-white/10"
|
|
9
|
+
:class="IMAGE_RADIUS[size]"
|
|
10
|
+
/>
|
|
4
11
|
<span v-else-if="getFlagUrl && countryCode">{{ countryCode }}</span>
|
|
5
|
-
</
|
|
12
|
+
</span>
|
|
6
13
|
</template>
|
|
7
14
|
|
|
8
15
|
<script setup lang="ts">
|
|
9
|
-
import { computed, shallowRef } from 'vue';
|
|
16
|
+
import { computed, shallowRef, useAttrs } from 'vue';
|
|
17
|
+
import { type ClassNameValue } from 'tailwind-merge';
|
|
18
|
+
import { mergeClasses } from './classMerge';
|
|
10
19
|
|
|
11
|
-
|
|
20
|
+
type CountryFlagSize = 'sm' | 'md';
|
|
21
|
+
|
|
22
|
+
defineOptions({ inheritAttrs: false });
|
|
23
|
+
|
|
24
|
+
const props = withDefaults(defineProps<{
|
|
25
|
+
countryCode?: string,
|
|
26
|
+
// sm is 16×12 for menus, lists and selects, md is 32×24
|
|
27
|
+
size?: CountryFlagSize,
|
|
28
|
+
}>(), {
|
|
29
|
+
size: 'md',
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const SIZES: Record<CountryFlagSize, string> = { sm: 'w-4 h-3', md: 'w-8 h-6' };
|
|
33
|
+
const IMAGE_RADIUS: Record<CountryFlagSize, string> = { sm: 'rounded-[2px]', md: 'rounded-[3px]' };
|
|
34
|
+
|
|
35
|
+
const attrs = useAttrs();
|
|
36
|
+
const restAttrs = computed(() => {
|
|
37
|
+
const { class: _class, ...rest } = attrs;
|
|
38
|
+
return rest;
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const rootClasses = computed(() => mergeClasses(
|
|
42
|
+
'afcl-country-flag inline-flex shrink-0 items-center justify-center',
|
|
43
|
+
SIZES[props.size],
|
|
44
|
+
attrs.class as ClassNameValue,
|
|
45
|
+
));
|
|
12
46
|
|
|
13
47
|
// the map of flag URLs is loaded when a flag is shown for the first time
|
|
14
48
|
const getFlagUrl = shallowRef<(countryCode: string) => string | undefined>();
|
|
@@ -18,12 +52,3 @@ import('./countryFlagUrls').then((module) => {
|
|
|
18
52
|
|
|
19
53
|
const flagUrl = computed(() => props.countryCode && getFlagUrl.value?.(props.countryCode));
|
|
20
54
|
</script>
|
|
21
|
-
|
|
22
|
-
<style scoped>
|
|
23
|
-
.flag-icon {
|
|
24
|
-
box-shadow: inset -0.3px -0.3px 0.3px 0px rgba(0 0 0 / 0.2),
|
|
25
|
-
inset 0.3px 0.3px 0.3px 0px rgba(255 255 255 / 0.2),
|
|
26
|
-
0px 0px 3px #00000030;
|
|
27
|
-
width: auto;
|
|
28
|
-
}
|
|
29
|
-
</style>
|
|
@@ -1,87 +1,111 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div ref="triggerEl" class="afcl-tooltip inline-flex items-center" @mouseenter="
|
|
2
|
+
<div ref="triggerEl" class="afcl-tooltip inline-flex items-center" :style="{ anchorName }" @mouseenter="show" @mouseleave="hide">
|
|
3
3
|
<slot></slot>
|
|
4
4
|
</div>
|
|
5
|
-
<teleport to="body" v-if="
|
|
5
|
+
<teleport to="body" v-if="isOpen">
|
|
6
6
|
<div
|
|
7
|
+
ref="tooltipEl"
|
|
8
|
+
popover="manual"
|
|
7
9
|
role="tooltip"
|
|
8
|
-
|
|
9
|
-
|
|
10
|
+
:data-placement="placement"
|
|
11
|
+
:style="{ positionAnchor: anchorName }"
|
|
12
|
+
class="afcl-tooltip-popover inline-block px-2.5 py-1.5 text-xs font-medium text-lightTooltipText dark:text-darkTooltipText transition-opacity duration-150 bg-lightTooltipBackground rounded-md shadow-popover dark:bg-darkTooltipBackground dark:shadow-darkPopover"
|
|
10
13
|
>
|
|
11
14
|
<slot name="tooltip"></slot>
|
|
12
|
-
<div class="tooltip-arrow"
|
|
15
|
+
<div class="afcl-tooltip-arrow" :style="{ left: `${arrowLeft}px` }"></div>
|
|
13
16
|
</div>
|
|
14
17
|
</teleport>
|
|
15
18
|
</template>
|
|
16
19
|
|
|
17
20
|
<script setup lang="ts">
|
|
18
|
-
import { ref, nextTick,
|
|
19
|
-
import { Tooltip } from 'flowbite';
|
|
21
|
+
import { ref, nextTick, onBeforeUnmount, useId } from 'vue';
|
|
20
22
|
|
|
21
|
-
const
|
|
22
|
-
const
|
|
23
|
-
const showTooltip = ref(false);
|
|
24
|
-
const tp: Ref<Tooltip|null> = ref(null);
|
|
23
|
+
const OFFSET = 8;
|
|
24
|
+
const SUPPORTS_ANCHOR = CSS.supports('anchor-name: --a');
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
const anchorName = `--afcl-tooltip-${useId()}`;
|
|
27
|
+
const triggerEl = ref<HTMLElement>();
|
|
28
|
+
const tooltipEl = ref<HTMLElement>();
|
|
29
|
+
const isOpen = ref(false);
|
|
30
|
+
const placement = ref<'top' | 'bottom'>('bottom');
|
|
31
|
+
const arrowLeft = ref(0);
|
|
32
|
+
|
|
33
|
+
async function show() {
|
|
34
|
+
isOpen.value = true;
|
|
28
35
|
await nextTick();
|
|
36
|
+
tooltipEl.value!.showPopover();
|
|
29
37
|
|
|
30
|
-
|
|
38
|
+
const trigger = triggerEl.value!.getBoundingClientRect();
|
|
39
|
+
if (!SUPPORTS_ANCHOR) {
|
|
40
|
+
placeByRect(trigger);
|
|
41
|
+
}
|
|
42
|
+
const tooltip = tooltipEl.value!.getBoundingClientRect();
|
|
43
|
+
placement.value = tooltip.top < trigger.top ? 'top' : 'bottom';
|
|
44
|
+
arrowLeft.value = trigger.left + trigger.width / 2 - tooltip.left;
|
|
31
45
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
triggerEl.value,
|
|
35
|
-
{
|
|
36
|
-
placement: 'bottom',
|
|
37
|
-
triggerType: 'hover',
|
|
38
|
-
},
|
|
39
|
-
);
|
|
46
|
+
document.addEventListener('keydown', onKeydown);
|
|
47
|
+
}
|
|
40
48
|
|
|
41
|
-
|
|
49
|
+
function hide() {
|
|
50
|
+
isOpen.value = false;
|
|
51
|
+
document.removeEventListener('keydown', onKeydown);
|
|
42
52
|
}
|
|
43
53
|
|
|
44
|
-
function
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
showTooltip.value = false;
|
|
54
|
+
function onKeydown(e: KeyboardEvent) {
|
|
55
|
+
if (e.key === 'Escape') {
|
|
56
|
+
hide();
|
|
57
|
+
}
|
|
49
58
|
}
|
|
50
59
|
|
|
60
|
+
// Safari before 26 and Firefox before 147 have no CSS anchor positioning
|
|
61
|
+
function placeByRect(trigger: DOMRect) {
|
|
62
|
+
const el = tooltipEl.value!;
|
|
63
|
+
const { width, height } = el.getBoundingClientRect();
|
|
64
|
+
const fitsBelow = trigger.bottom + OFFSET + height <= window.innerHeight;
|
|
65
|
+
const top = fitsBelow ? trigger.bottom + OFFSET : trigger.top - OFFSET - height;
|
|
66
|
+
const left = Math.min(Math.max(trigger.left + (trigger.width - width) / 2, 0), window.innerWidth - width);
|
|
67
|
+
el.style.top = `${top}px`;
|
|
68
|
+
el.style.left = `${left}px`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
onBeforeUnmount(hide);
|
|
51
72
|
</script>
|
|
52
73
|
|
|
53
74
|
<style>
|
|
54
|
-
|
|
55
|
-
.tooltip
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
background: inherit;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
.tooltip .tooltip-arrow {
|
|
63
|
-
visibility: hidden;
|
|
75
|
+
/* popover puts the tooltip into the top layer, so no z-index and no clipping by overflow of the parents */
|
|
76
|
+
.afcl-tooltip-popover {
|
|
77
|
+
inset: auto;
|
|
78
|
+
margin: 0;
|
|
79
|
+
overflow: visible;
|
|
64
80
|
}
|
|
65
81
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
82
|
+
@supports (anchor-name: --a) {
|
|
83
|
+
.afcl-tooltip-popover {
|
|
84
|
+
position-area: bottom;
|
|
85
|
+
position-try-fallbacks: flip-block;
|
|
86
|
+
margin-top: 8px;
|
|
87
|
+
}
|
|
70
88
|
}
|
|
71
89
|
|
|
72
|
-
|
|
73
|
-
|
|
90
|
+
@starting-style {
|
|
91
|
+
.afcl-tooltip-popover {
|
|
92
|
+
opacity: 0;
|
|
93
|
+
}
|
|
74
94
|
}
|
|
75
95
|
|
|
76
|
-
.
|
|
77
|
-
|
|
96
|
+
.afcl-tooltip-arrow {
|
|
97
|
+
position: absolute;
|
|
98
|
+
width: 8px;
|
|
99
|
+
height: 8px;
|
|
100
|
+
background: inherit;
|
|
101
|
+
transform: translateX(-50%) rotate(45deg);
|
|
78
102
|
}
|
|
79
103
|
|
|
80
|
-
.tooltip[data-
|
|
81
|
-
|
|
104
|
+
.afcl-tooltip-popover[data-placement='top'] > .afcl-tooltip-arrow {
|
|
105
|
+
bottom: -4px;
|
|
82
106
|
}
|
|
83
107
|
|
|
84
|
-
.tooltip[data-
|
|
85
|
-
|
|
108
|
+
.afcl-tooltip-popover[data-placement='bottom'] > .afcl-tooltip-arrow {
|
|
109
|
+
top: -4px;
|
|
86
110
|
}
|
|
87
|
-
</style>
|
|
111
|
+
</style>
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
>
|
|
13
13
|
<RouterLink
|
|
14
14
|
v-if="foreignResource && foreignResource?.pk"
|
|
15
|
-
class="font-medium
|
|
15
|
+
class="font-medium hover:underline whitespace-nowrap"
|
|
16
16
|
:to="{
|
|
17
17
|
name: 'resource-show',
|
|
18
18
|
params: {
|