@signal24/vue-foundation 4.3.7 → 4.5.0
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/src/components/index.d.ts +1 -1
- package/dist/src/components/overlay-anchor.vue.d.ts +25 -0
- package/dist/src/components/overlay-container.d.ts +51 -0
- package/dist/src/components/overlay-types.d.ts +9 -0
- package/dist/src/helpers/mask.d.ts +1 -1
- package/dist/src/helpers/openapi.d.ts +10 -0
- package/dist/vue-foundation.css +1 -1
- package/dist/vue-foundation.es.js +533 -449
- package/package.json +1 -1
- package/src/components/alert-helpers.ts +6 -6
- package/src/components/index.ts +1 -1
- package/src/components/modal.vue +16 -18
- package/src/components/overlay-anchor.vue +103 -0
- package/src/components/overlay-container.ts +176 -0
- package/src/components/overlay-types.ts +10 -0
- package/src/helpers/mask.ts +1 -1
- package/src/helpers/openapi.ts +38 -0
- package/dist/src/components/modal-container.d.ts +0 -44
- package/src/components/modal-container.ts +0 -147
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import AlertModal from './alert-modal.vue';
|
|
2
|
-
import {
|
|
2
|
+
import { createOverlayInjection, presentOverlay, removeOverlayInjection } from './overlay-container';
|
|
3
3
|
|
|
4
4
|
function resolveAlertParams(titleOrMessage: string | Error, message?: string | Error) {
|
|
5
5
|
const title = message ? (titleOrMessage as string) : undefined;
|
|
@@ -10,13 +10,13 @@ function resolveAlertParams(titleOrMessage: string | Error, message?: string | E
|
|
|
10
10
|
export async function showAlert(title: string, message: string | Error): Promise<void>;
|
|
11
11
|
export async function showAlert(message: string | Error): Promise<void>;
|
|
12
12
|
export async function showAlert(titleOrMessage: string | Error, message?: string | Error): Promise<void> {
|
|
13
|
-
await
|
|
13
|
+
await presentOverlay(AlertModal, resolveAlertParams(titleOrMessage, message));
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export async function showConfirm(title: string, message: string): Promise<boolean>;
|
|
17
17
|
export async function showConfirm(message: string): Promise<boolean>;
|
|
18
18
|
export async function showConfirm(titleOrMessage: string, message?: string): Promise<boolean> {
|
|
19
|
-
const result = await
|
|
19
|
+
const result = await presentOverlay(AlertModal, {
|
|
20
20
|
...resolveAlertParams(titleOrMessage, message),
|
|
21
21
|
shouldConfirm: true
|
|
22
22
|
});
|
|
@@ -26,7 +26,7 @@ export async function showConfirm(titleOrMessage: string, message?: string): Pro
|
|
|
26
26
|
export async function showConfirmDestroy(title: string, message: string): Promise<boolean>;
|
|
27
27
|
export async function showConfirmDestroy(message: string): Promise<boolean>;
|
|
28
28
|
export async function showConfirmDestroy(titleOrMessage: string, message?: string): Promise<boolean> {
|
|
29
|
-
const result = await
|
|
29
|
+
const result = await presentOverlay(AlertModal, {
|
|
30
30
|
...resolveAlertParams(titleOrMessage, message),
|
|
31
31
|
shouldConfirm: true,
|
|
32
32
|
classes: ['destructive']
|
|
@@ -37,11 +37,11 @@ export async function showConfirmDestroy(titleOrMessage: string, message?: strin
|
|
|
37
37
|
export function showWait(title: string, message: string): () => void;
|
|
38
38
|
export function showWait(message: string): () => void;
|
|
39
39
|
export function showWait(titleOrMessage: string, message?: string): () => void {
|
|
40
|
-
const injection =
|
|
40
|
+
const injection = createOverlayInjection(AlertModal, {
|
|
41
41
|
...resolveAlertParams(titleOrMessage, message),
|
|
42
42
|
isBare: true,
|
|
43
43
|
classes: ['wait'],
|
|
44
44
|
callback: () => {}
|
|
45
45
|
});
|
|
46
|
-
return () =>
|
|
46
|
+
return () => removeOverlayInjection(injection);
|
|
47
47
|
}
|
package/src/components/index.ts
CHANGED
|
@@ -5,6 +5,6 @@ import VfModal from './modal.vue';
|
|
|
5
5
|
import VfSmartSelect from './smart-select.vue';
|
|
6
6
|
|
|
7
7
|
export * from './alert-helpers';
|
|
8
|
-
export * from './
|
|
8
|
+
export * from './overlay-container';
|
|
9
9
|
|
|
10
10
|
export { VfAjaxSelect, VfAlertModal, VfEzSmartSelect, VfModal, VfSmartSelect };
|
package/src/components/modal.vue
CHANGED
|
@@ -1,27 +1,25 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
<
|
|
4
|
-
<
|
|
5
|
-
<
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
<
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
<
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
</div>
|
|
17
|
-
</Teleport>
|
|
2
|
+
<div :id="id" class="vf-overlay vf-modal-wrap" :class="props.class" ref="overlay">
|
|
3
|
+
<form action="." class="vf-modal" :class="{ scrolls }" @submit.prevent="$emit('formSubmit')" ref="form">
|
|
4
|
+
<div v-if="$slots.header" class="vf-modal-header">
|
|
5
|
+
<slot name="header" />
|
|
6
|
+
<i v-if="props.closeX" class="close" @click="closeParent"></i>
|
|
7
|
+
</div>
|
|
8
|
+
<div class="vf-modal-content">
|
|
9
|
+
<slot />
|
|
10
|
+
</div>
|
|
11
|
+
<div v-if="$slots.footer" class="vf-modal-footer">
|
|
12
|
+
<slot name="footer" />
|
|
13
|
+
</div>
|
|
14
|
+
</form>
|
|
15
|
+
</div>
|
|
18
16
|
</template>
|
|
19
17
|
|
|
20
18
|
<script lang="ts" setup>
|
|
21
19
|
import { getCurrentInstance, onBeforeUnmount, onMounted, ref } from 'vue';
|
|
22
20
|
|
|
23
21
|
import { maskForm, unmaskForm } from '../helpers/mask';
|
|
24
|
-
import {
|
|
22
|
+
import { dismissOverlayInjectionByInternalInstance } from './overlay-container';
|
|
25
23
|
|
|
26
24
|
const instance = getCurrentInstance();
|
|
27
25
|
|
|
@@ -72,7 +70,7 @@ function handleEscapeKey(e: KeyboardEvent) {
|
|
|
72
70
|
}
|
|
73
71
|
|
|
74
72
|
function closeParent() {
|
|
75
|
-
|
|
73
|
+
dismissOverlayInjectionByInternalInstance(instance!);
|
|
76
74
|
}
|
|
77
75
|
|
|
78
76
|
function mask() {
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="vf-overlay-anchor" :class="anchorClasses" :style="anchorStyles" @click.stop="removeOverlay">
|
|
3
|
+
<slot></slot>
|
|
4
|
+
</div>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script lang="ts" setup>
|
|
8
|
+
import type { CSSProperties } from 'vue';
|
|
9
|
+
import { getCurrentInstance, onMounted, ref } from 'vue';
|
|
10
|
+
|
|
11
|
+
import { dismissOverlayInjectionById } from '.';
|
|
12
|
+
import type { OverlayAnchorOptions, OverlayAnchorOptionsObject } from './overlay-types';
|
|
13
|
+
|
|
14
|
+
const props = defineProps<{
|
|
15
|
+
overlayId: string;
|
|
16
|
+
anchor: OverlayAnchorOptions;
|
|
17
|
+
}>();
|
|
18
|
+
|
|
19
|
+
const anchorEl = props.anchor instanceof HTMLElement ? props.anchor : props.anchor.el;
|
|
20
|
+
|
|
21
|
+
const anchorStyles = ref<CSSProperties>({ visibility: 'hidden', top: '0', left: '0' });
|
|
22
|
+
const anchorClasses = ref<string[]>([]);
|
|
23
|
+
const instance = getCurrentInstance();
|
|
24
|
+
|
|
25
|
+
onMounted(updateAttributes);
|
|
26
|
+
|
|
27
|
+
function updateAttributes() {
|
|
28
|
+
const overlayEl = instance!.vnode.el as HTMLElement;
|
|
29
|
+
const { styles, classes } = computeAnchoredStyle(overlayEl, anchorEl);
|
|
30
|
+
anchorStyles.value = styles;
|
|
31
|
+
anchorClasses.value = classes;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function computeAnchoredStyle(
|
|
35
|
+
overlayEl: HTMLElement,
|
|
36
|
+
anchorEl: HTMLElement
|
|
37
|
+
): {
|
|
38
|
+
styles: CSSProperties;
|
|
39
|
+
classes: string[];
|
|
40
|
+
} {
|
|
41
|
+
const anchorOpts: Omit<OverlayAnchorOptionsObject, 'el'> = props.anchor instanceof HTMLElement ? {} : props.anchor;
|
|
42
|
+
|
|
43
|
+
const anchorRect = anchorEl.getBoundingClientRect();
|
|
44
|
+
const overlayRect = overlayEl.getBoundingClientRect();
|
|
45
|
+
|
|
46
|
+
if (anchorOpts.matchWidth) {
|
|
47
|
+
overlayRect.width = anchorRect.width;
|
|
48
|
+
}
|
|
49
|
+
if (anchorOpts.matchHeight) {
|
|
50
|
+
overlayRect.height = anchorRect.height;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const classes: string[] = anchorOpts.class ? (Array.isArray(anchorOpts.class) ? anchorOpts.class : [anchorOpts.class]) : [];
|
|
54
|
+
let top: number, left: number;
|
|
55
|
+
|
|
56
|
+
if (anchorOpts.y === 'center') {
|
|
57
|
+
top = anchorRect.top + anchorRect.height / 2 - overlayRect.height / 2;
|
|
58
|
+
classes.push('anchored-center-y');
|
|
59
|
+
} else {
|
|
60
|
+
const canDisplayBelow = anchorRect.bottom + overlayRect.height < window.innerHeight || anchorOpts.y === 'below';
|
|
61
|
+
const canDisplayBelowResolved = canDisplayBelow && anchorOpts.y !== 'above';
|
|
62
|
+
top = canDisplayBelowResolved ? anchorRect.bottom : anchorRect.top - overlayRect.height;
|
|
63
|
+
classes.push(canDisplayBelowResolved ? 'anchored-top' : 'anchored-bottom');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (anchorOpts.x === 'center') {
|
|
67
|
+
left = anchorRect.left + anchorRect.width / 2 - overlayRect.width / 2;
|
|
68
|
+
classes.push('anchored-center-x');
|
|
69
|
+
} else {
|
|
70
|
+
const canAlignLefts = anchorRect.left + overlayRect.width < window.innerWidth || anchorOpts.x === 'left';
|
|
71
|
+
const canAlignLeftsResolved = canAlignLefts && anchorOpts.x !== 'right';
|
|
72
|
+
left = canAlignLeftsResolved ? anchorRect.left : anchorRect.right - overlayRect.width;
|
|
73
|
+
classes.push(canAlignLeftsResolved ? 'anchored-left' : 'anchored-right');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
styles: {
|
|
78
|
+
top: `${top}px`,
|
|
79
|
+
left: `${left}px`,
|
|
80
|
+
...(anchorOpts.matchWidth ? { width: `${overlayRect.width}px` } : {}),
|
|
81
|
+
...(anchorOpts.matchHeight ? { height: `${overlayRect.height}px` } : {})
|
|
82
|
+
},
|
|
83
|
+
classes
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function removeOverlay() {
|
|
88
|
+
window.removeEventListener('click', removeOverlay);
|
|
89
|
+
dismissOverlayInjectionById(props.overlayId);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
onMounted(() => {
|
|
93
|
+
setTimeout(() => {
|
|
94
|
+
window.addEventListener('click', removeOverlay);
|
|
95
|
+
}, 10);
|
|
96
|
+
});
|
|
97
|
+
</script>
|
|
98
|
+
|
|
99
|
+
<style lang="scss">
|
|
100
|
+
.vf-overlay-anchor {
|
|
101
|
+
position: absolute;
|
|
102
|
+
}
|
|
103
|
+
</style>
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import type { Writable } from 'type-fest';
|
|
2
|
+
import {
|
|
3
|
+
type AllowedComponentProps,
|
|
4
|
+
type ComponentInternalInstance,
|
|
5
|
+
type ComponentPublicInstance,
|
|
6
|
+
type ComputedOptions,
|
|
7
|
+
defineComponent,
|
|
8
|
+
h,
|
|
9
|
+
markRaw,
|
|
10
|
+
type MethodOptions,
|
|
11
|
+
type Raw,
|
|
12
|
+
reactive,
|
|
13
|
+
renderList,
|
|
14
|
+
Teleport,
|
|
15
|
+
type VNode,
|
|
16
|
+
type VNodeProps
|
|
17
|
+
} from 'vue';
|
|
18
|
+
|
|
19
|
+
import OverlayAnchor from './overlay-anchor.vue';
|
|
20
|
+
import type { OverlayAnchorOptions } from './overlay-types';
|
|
21
|
+
|
|
22
|
+
interface OverlayOptions {
|
|
23
|
+
anchor?: OverlayAnchorOptions;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface OverlayInjection<C extends OverlayComponent> {
|
|
27
|
+
id: string;
|
|
28
|
+
component: OverlayComponentUnwrapped<C>;
|
|
29
|
+
props: OverlayComponentProps<C>;
|
|
30
|
+
options: OverlayOptions;
|
|
31
|
+
vnode: VNode;
|
|
32
|
+
wrapperVnode?: VNode;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let overlayCount = 0;
|
|
36
|
+
const OverlayInjections: OverlayInjection<any>[] = reactive([]);
|
|
37
|
+
|
|
38
|
+
export const OverlayContainer = defineComponent({
|
|
39
|
+
setup() {
|
|
40
|
+
return () =>
|
|
41
|
+
h('div', [
|
|
42
|
+
renderList(OverlayInjections, injection => {
|
|
43
|
+
return h(Teleport, { key: injection.id, to: '#vf-overlay-target' }, [injection.wrapperVnode ?? injection.vnode]);
|
|
44
|
+
})
|
|
45
|
+
]);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// copied in from Vue since it's not exported
|
|
50
|
+
// tood: it may be a lot easier than this. see the docs for props passed to "h"
|
|
51
|
+
export type Vue__ComponentPublicInstanceConstructor<
|
|
52
|
+
T extends ComponentPublicInstance<Props, RawBindings, D, C, M> = ComponentPublicInstance<any>,
|
|
53
|
+
Props = any,
|
|
54
|
+
RawBindings = any,
|
|
55
|
+
D = any,
|
|
56
|
+
C extends ComputedOptions = ComputedOptions,
|
|
57
|
+
M extends MethodOptions = MethodOptions
|
|
58
|
+
> = {
|
|
59
|
+
__isFragment?: never;
|
|
60
|
+
__isTeleport?: never;
|
|
61
|
+
__isSuspense?: never;
|
|
62
|
+
new (...args: any[]): T;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export type ObjectComponentConfig<T extends Vue__ComponentPublicInstanceConstructor> = T extends Vue__ComponentPublicInstanceConstructor<infer P>
|
|
66
|
+
? P
|
|
67
|
+
: never;
|
|
68
|
+
export type ObjectComponentProps<T extends Vue__ComponentPublicInstanceConstructor> = Writable<
|
|
69
|
+
Omit<ObjectComponentConfig<T>['$props'], keyof VNodeProps | keyof AllowedComponentProps>
|
|
70
|
+
>;
|
|
71
|
+
|
|
72
|
+
type ObjectOrDefault<T> = T extends object ? T : PropsWithCallback<{}>;
|
|
73
|
+
export type OverlayComponent = Vue__ComponentPublicInstanceConstructor | ((props: any) => any);
|
|
74
|
+
export type OverlayComponentConfig<T> = T extends Vue__ComponentPublicInstanceConstructor
|
|
75
|
+
? {
|
|
76
|
+
props: ObjectComponentProps<T>;
|
|
77
|
+
component: Raw<T>;
|
|
78
|
+
}
|
|
79
|
+
: T extends (props: infer P) => any
|
|
80
|
+
? {
|
|
81
|
+
props: Omit<ObjectOrDefault<P>, keyof VNodeProps | keyof AllowedComponentProps>;
|
|
82
|
+
component: T;
|
|
83
|
+
}
|
|
84
|
+
: never;
|
|
85
|
+
export type OverlayComponentUnwrapped<T extends OverlayComponent> = OverlayComponentConfig<T>['component'];
|
|
86
|
+
export type OverlayComponentProps<T extends OverlayComponent> = OverlayComponentConfig<T>['props'];
|
|
87
|
+
|
|
88
|
+
interface PropsWithCallback<T> {
|
|
89
|
+
callback?: (result: T) => void;
|
|
90
|
+
}
|
|
91
|
+
type ComponentReturn<M extends OverlayComponent> = OverlayComponentProps<M> extends PropsWithCallback<infer R> ? R : never;
|
|
92
|
+
|
|
93
|
+
export type AnyComponentPublicInstance = { $: ComponentInternalInstance };
|
|
94
|
+
|
|
95
|
+
export function createOverlayInjection<C extends OverlayComponent>(
|
|
96
|
+
component: C,
|
|
97
|
+
props: OverlayComponentProps<C>,
|
|
98
|
+
options?: OverlayOptions
|
|
99
|
+
): OverlayInjection<C> {
|
|
100
|
+
// create or reconfigure the existing overlay target
|
|
101
|
+
// re-injecting every time keeps the overlay container at the very end of the DOM
|
|
102
|
+
const targetEl = document.getElementById('vf-overlay-target') ?? document.createElement('div');
|
|
103
|
+
targetEl.id = 'vf-overlay-target';
|
|
104
|
+
targetEl.removeAttribute('inert');
|
|
105
|
+
document.body.appendChild(targetEl);
|
|
106
|
+
|
|
107
|
+
const overlayId = String(++overlayCount);
|
|
108
|
+
const rawComponent = markRaw(component);
|
|
109
|
+
const vnode = h(rawComponent, props);
|
|
110
|
+
const wrapperVnode = options?.anchor ? h(OverlayAnchor, { overlayId, anchor: options.anchor }, () => [vnode]) : undefined;
|
|
111
|
+
|
|
112
|
+
// todo: dunno what's going on with types here
|
|
113
|
+
const injection: OverlayInjection<C> = {
|
|
114
|
+
id: overlayId,
|
|
115
|
+
component: rawComponent as any,
|
|
116
|
+
props,
|
|
117
|
+
options: options ?? {},
|
|
118
|
+
vnode,
|
|
119
|
+
wrapperVnode
|
|
120
|
+
};
|
|
121
|
+
OverlayInjections.push(injection);
|
|
122
|
+
|
|
123
|
+
return injection;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function dismissOverlayInjectionByInstance(instance: AnyComponentPublicInstance) {
|
|
127
|
+
dismissOverlayInjectionByInternalInstance(instance.$);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function dismissOverlayInjectionByInternalInstance(instance: ComponentInternalInstance) {
|
|
131
|
+
let targetInstance: ComponentInternalInstance | null = instance;
|
|
132
|
+
while (targetInstance && !dismissOverlayInjectionByVnode(targetInstance.vnode)) {
|
|
133
|
+
targetInstance = targetInstance.parent;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function dismissOverlayInjectionByVnode(vnode: VNode) {
|
|
138
|
+
const injectionIdx = OverlayInjections.findIndex(i => i.vnode.component === vnode.component);
|
|
139
|
+
if (injectionIdx >= 0) {
|
|
140
|
+
OverlayInjections[injectionIdx].props.callback();
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function dismissOverlayInjectionById(id: string) {
|
|
147
|
+
const injectionIdx = OverlayInjections.findIndex(i => i.id === id);
|
|
148
|
+
if (injectionIdx >= 0) {
|
|
149
|
+
OverlayInjections[injectionIdx].props.callback();
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function removeOverlayInjection(injection: OverlayInjection<any>) {
|
|
156
|
+
const index = OverlayInjections.indexOf(injection);
|
|
157
|
+
if (index >= 0) {
|
|
158
|
+
OverlayInjections.splice(index, 1);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export async function presentOverlay<C extends OverlayComponent, R extends ComponentReturn<C>>(
|
|
163
|
+
component: C,
|
|
164
|
+
props: Omit<OverlayComponentProps<C>, 'callback'>,
|
|
165
|
+
options?: OverlayOptions
|
|
166
|
+
): Promise<R | undefined> {
|
|
167
|
+
return new Promise<R>(resolve => {
|
|
168
|
+
let overlayInjection: OverlayInjection<C> | null = null;
|
|
169
|
+
const callback = (result: R) => {
|
|
170
|
+
removeOverlayInjection(overlayInjection!);
|
|
171
|
+
resolve(result);
|
|
172
|
+
};
|
|
173
|
+
const resolvedProps = { ...props, callback } as OverlayComponentProps<C>;
|
|
174
|
+
overlayInjection = createOverlayInjection(component, resolvedProps, options);
|
|
175
|
+
});
|
|
176
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface OverlayAnchorOptionsObject {
|
|
2
|
+
el: HTMLElement;
|
|
3
|
+
class?: string | string[];
|
|
4
|
+
x?: 'auto' | 'left' | 'center' | 'right';
|
|
5
|
+
y?: 'auto' | 'above' | 'center' | 'below';
|
|
6
|
+
matchWidth?: boolean;
|
|
7
|
+
matchHeight?: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type OverlayAnchorOptions = HTMLElement | OverlayAnchorOptionsObject;
|
package/src/helpers/mask.ts
CHANGED
package/src/helpers/openapi.ts
CHANGED
|
@@ -53,6 +53,8 @@ export function isApiError(err: any): err is IApiError {
|
|
|
53
53
|
export function installApiClientInterceptors({ apiClient, onRequest, onError, CancelablePromise }: IWrappedApiClientOptions) {
|
|
54
54
|
const originalRequest = apiClient.request.request.bind(apiClient.request);
|
|
55
55
|
apiClient.request.request = (options: IRequestOptions) => {
|
|
56
|
+
options = rewriteOptionsForFileUpload(options);
|
|
57
|
+
|
|
56
58
|
if (onRequest) {
|
|
57
59
|
options = onRequest(options);
|
|
58
60
|
}
|
|
@@ -82,3 +84,39 @@ export function installApiClientInterceptors({ apiClient, onRequest, onError, Ca
|
|
|
82
84
|
});
|
|
83
85
|
};
|
|
84
86
|
}
|
|
87
|
+
|
|
88
|
+
export class FileUploadRequest {
|
|
89
|
+
constructor(blob: Blob) {
|
|
90
|
+
this.blob = blob;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
validator = null;
|
|
94
|
+
lastModifiedDate = null;
|
|
95
|
+
size = 0;
|
|
96
|
+
path = '';
|
|
97
|
+
name = '';
|
|
98
|
+
type = '';
|
|
99
|
+
blob!: Blob;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function rewriteOptionsForFileUpload(options: IRequestOptions): IRequestOptions {
|
|
103
|
+
const hasFileUpload = typeof options.body === 'object' && Object.values(options.body).some(v => v instanceof FileUploadRequest);
|
|
104
|
+
if (!hasFileUpload) return options;
|
|
105
|
+
|
|
106
|
+
const formData: Record<string, any> = {};
|
|
107
|
+
const jsonBody: Record<string, any> = {};
|
|
108
|
+
for (const [key, value] of Object.entries(options.body)) {
|
|
109
|
+
if (value instanceof FileUploadRequest) {
|
|
110
|
+
formData[key] = value.blob;
|
|
111
|
+
} else {
|
|
112
|
+
jsonBody[key] = value;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
formData._payload = new Blob([JSON.stringify(jsonBody)], { type: 'application/json' });
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
...options,
|
|
119
|
+
body: undefined,
|
|
120
|
+
formData
|
|
121
|
+
};
|
|
122
|
+
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import type { Writable } from 'type-fest';
|
|
2
|
-
import { type AllowedComponentProps, type ComponentInternalInstance, type ComponentPublicInstance, type ComputedOptions, type MethodOptions, type Raw, type VNode, type VNodeProps } from 'vue';
|
|
3
|
-
interface ModalInjection<C extends ModalComponent> {
|
|
4
|
-
id: string;
|
|
5
|
-
component: ModalComponentUnwrapped<C>;
|
|
6
|
-
props: ModalComponentProps<C>;
|
|
7
|
-
vnode: VNode;
|
|
8
|
-
}
|
|
9
|
-
export declare const ModalContainer: import("vue").DefineComponent<{}, () => VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
10
|
-
[key: string]: any;
|
|
11
|
-
}>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, VNodeProps & AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{}>>, {}, {}>;
|
|
12
|
-
export type Vue__ComponentPublicInstanceConstructor<T extends ComponentPublicInstance<Props, RawBindings, D, C, M> = ComponentPublicInstance<any>, Props = any, RawBindings = any, D = any, C extends ComputedOptions = ComputedOptions, M extends MethodOptions = MethodOptions> = {
|
|
13
|
-
__isFragment?: never;
|
|
14
|
-
__isTeleport?: never;
|
|
15
|
-
__isSuspense?: never;
|
|
16
|
-
new (...args: any[]): T;
|
|
17
|
-
};
|
|
18
|
-
export type ObjectComponentConfig<T extends Vue__ComponentPublicInstanceConstructor> = T extends Vue__ComponentPublicInstanceConstructor<infer P> ? P : never;
|
|
19
|
-
export type ObjectComponentProps<T extends Vue__ComponentPublicInstanceConstructor> = Writable<Omit<ObjectComponentConfig<T>['$props'], keyof VNodeProps | keyof AllowedComponentProps>>;
|
|
20
|
-
type ObjectOrDefault<T> = T extends object ? T : PropsWithCallback<{}>;
|
|
21
|
-
export type ModalComponent = Vue__ComponentPublicInstanceConstructor | ((props: any) => any);
|
|
22
|
-
export type ModalComponentConfig<T> = T extends Vue__ComponentPublicInstanceConstructor ? {
|
|
23
|
-
props: ObjectComponentProps<T>;
|
|
24
|
-
component: Raw<T>;
|
|
25
|
-
} : T extends (props: infer P) => any ? {
|
|
26
|
-
props: Omit<ObjectOrDefault<P>, keyof VNodeProps | keyof AllowedComponentProps>;
|
|
27
|
-
component: T;
|
|
28
|
-
} : never;
|
|
29
|
-
export type ModalComponentUnwrapped<T extends ModalComponent> = ModalComponentConfig<T>['component'];
|
|
30
|
-
export type ModalComponentProps<T extends ModalComponent> = ModalComponentConfig<T>['props'];
|
|
31
|
-
interface PropsWithCallback<T> {
|
|
32
|
-
callback?: (result: T) => void;
|
|
33
|
-
}
|
|
34
|
-
type ComponentReturn<M extends ModalComponent> = ModalComponentProps<M> extends PropsWithCallback<infer R> ? R : never;
|
|
35
|
-
export type AnyComponentPublicInstance = {
|
|
36
|
-
$: ComponentInternalInstance;
|
|
37
|
-
};
|
|
38
|
-
export declare function createModalInjection<C extends ModalComponent>(component: C, props: ModalComponentProps<C>): ModalInjection<C>;
|
|
39
|
-
export declare function removeModalInjection(injection: ModalInjection<any>): void;
|
|
40
|
-
export declare function removeModalInjectionByInstance(instance: AnyComponentPublicInstance): void;
|
|
41
|
-
export declare function removeModalInjectionByInternalInstance(instance: ComponentInternalInstance): void;
|
|
42
|
-
export declare function removeModalInjectionByVnode(vnode: VNode): boolean;
|
|
43
|
-
export declare function presentModal<C extends ModalComponent, R extends ComponentReturn<C>>(component: C, props: Omit<ModalComponentProps<C>, 'callback'>): Promise<R | undefined>;
|
|
44
|
-
export {};
|
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
import type { Writable } from 'type-fest';
|
|
2
|
-
import {
|
|
3
|
-
type AllowedComponentProps,
|
|
4
|
-
type ComponentInternalInstance,
|
|
5
|
-
type ComponentPublicInstance,
|
|
6
|
-
type ComputedOptions,
|
|
7
|
-
defineComponent,
|
|
8
|
-
h,
|
|
9
|
-
markRaw,
|
|
10
|
-
type MethodOptions,
|
|
11
|
-
type Raw,
|
|
12
|
-
reactive,
|
|
13
|
-
renderList,
|
|
14
|
-
type VNode,
|
|
15
|
-
type VNodeProps
|
|
16
|
-
} from 'vue';
|
|
17
|
-
|
|
18
|
-
interface ModalInjection<C extends ModalComponent> {
|
|
19
|
-
id: string;
|
|
20
|
-
component: ModalComponentUnwrapped<C>;
|
|
21
|
-
props: ModalComponentProps<C>;
|
|
22
|
-
vnode: VNode;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
let modalCount = 0;
|
|
26
|
-
const ModalInjections: ModalInjection<any>[] = reactive([]);
|
|
27
|
-
|
|
28
|
-
export const ModalContainer = defineComponent({
|
|
29
|
-
setup() {
|
|
30
|
-
return () =>
|
|
31
|
-
h('div', { id: 'modal-container' }, [
|
|
32
|
-
renderList(ModalInjections, injection => {
|
|
33
|
-
return injection.vnode;
|
|
34
|
-
})
|
|
35
|
-
]);
|
|
36
|
-
}
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
// copied in from Vue since it's not exported
|
|
40
|
-
export type Vue__ComponentPublicInstanceConstructor<
|
|
41
|
-
T extends ComponentPublicInstance<Props, RawBindings, D, C, M> = ComponentPublicInstance<any>,
|
|
42
|
-
Props = any,
|
|
43
|
-
RawBindings = any,
|
|
44
|
-
D = any,
|
|
45
|
-
C extends ComputedOptions = ComputedOptions,
|
|
46
|
-
M extends MethodOptions = MethodOptions
|
|
47
|
-
> = {
|
|
48
|
-
__isFragment?: never;
|
|
49
|
-
__isTeleport?: never;
|
|
50
|
-
__isSuspense?: never;
|
|
51
|
-
new (...args: any[]): T;
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
export type ObjectComponentConfig<T extends Vue__ComponentPublicInstanceConstructor> = T extends Vue__ComponentPublicInstanceConstructor<infer P>
|
|
55
|
-
? P
|
|
56
|
-
: never;
|
|
57
|
-
export type ObjectComponentProps<T extends Vue__ComponentPublicInstanceConstructor> = Writable<
|
|
58
|
-
Omit<ObjectComponentConfig<T>['$props'], keyof VNodeProps | keyof AllowedComponentProps>
|
|
59
|
-
>;
|
|
60
|
-
|
|
61
|
-
type ObjectOrDefault<T> = T extends object ? T : PropsWithCallback<{}>;
|
|
62
|
-
export type ModalComponent = Vue__ComponentPublicInstanceConstructor | ((props: any) => any);
|
|
63
|
-
export type ModalComponentConfig<T> = T extends Vue__ComponentPublicInstanceConstructor
|
|
64
|
-
? {
|
|
65
|
-
props: ObjectComponentProps<T>;
|
|
66
|
-
component: Raw<T>;
|
|
67
|
-
}
|
|
68
|
-
: T extends (props: infer P) => any
|
|
69
|
-
? {
|
|
70
|
-
props: Omit<ObjectOrDefault<P>, keyof VNodeProps | keyof AllowedComponentProps>;
|
|
71
|
-
component: T;
|
|
72
|
-
}
|
|
73
|
-
: never;
|
|
74
|
-
export type ModalComponentUnwrapped<T extends ModalComponent> = ModalComponentConfig<T>['component'];
|
|
75
|
-
export type ModalComponentProps<T extends ModalComponent> = ModalComponentConfig<T>['props'];
|
|
76
|
-
|
|
77
|
-
interface PropsWithCallback<T> {
|
|
78
|
-
callback?: (result: T) => void;
|
|
79
|
-
}
|
|
80
|
-
type ComponentReturn<M extends ModalComponent> = ModalComponentProps<M> extends PropsWithCallback<infer R> ? R : never;
|
|
81
|
-
|
|
82
|
-
export type AnyComponentPublicInstance = { $: ComponentInternalInstance };
|
|
83
|
-
|
|
84
|
-
export function createModalInjection<C extends ModalComponent>(component: C, props: ModalComponentProps<C>): ModalInjection<C> {
|
|
85
|
-
// create or reconfigure the existing modal target
|
|
86
|
-
// re-injecting every time keeps the modal container at the very end of the DOM
|
|
87
|
-
const targetEl = document.getElementById('vf-modal-target') ?? document.createElement('div');
|
|
88
|
-
targetEl.id = 'vf-modal-target';
|
|
89
|
-
targetEl.removeAttribute('inert');
|
|
90
|
-
document.body.appendChild(targetEl);
|
|
91
|
-
|
|
92
|
-
const rawComponent = markRaw(component);
|
|
93
|
-
|
|
94
|
-
// todo: dunno what's going on with types here
|
|
95
|
-
const injection: ModalInjection<C> = {
|
|
96
|
-
id: String(++modalCount),
|
|
97
|
-
component: rawComponent as any,
|
|
98
|
-
props,
|
|
99
|
-
vnode: h(rawComponent, props)
|
|
100
|
-
};
|
|
101
|
-
ModalInjections.push(injection);
|
|
102
|
-
|
|
103
|
-
return injection;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
export function removeModalInjection(injection: ModalInjection<any>) {
|
|
107
|
-
const index = ModalInjections.indexOf(injection);
|
|
108
|
-
if (index >= 0) {
|
|
109
|
-
ModalInjections.splice(index, 1);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
export function removeModalInjectionByInstance(instance: AnyComponentPublicInstance) {
|
|
114
|
-
removeModalInjectionByInternalInstance(instance.$);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
export function removeModalInjectionByInternalInstance(instance: ComponentInternalInstance) {
|
|
118
|
-
let targetInstance: ComponentInternalInstance | null = instance;
|
|
119
|
-
while (targetInstance && !removeModalInjectionByVnode(targetInstance.vnode)) {
|
|
120
|
-
targetInstance = targetInstance.parent;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
export function removeModalInjectionByVnode(vnode: VNode) {
|
|
125
|
-
const injectionIdx = ModalInjections.findIndex(i => i.vnode.component === vnode.component);
|
|
126
|
-
if (injectionIdx >= 0) {
|
|
127
|
-
ModalInjections[injectionIdx].props.callback?.(undefined);
|
|
128
|
-
ModalInjections.splice(injectionIdx, 1);
|
|
129
|
-
return true;
|
|
130
|
-
}
|
|
131
|
-
return false;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
export async function presentModal<C extends ModalComponent, R extends ComponentReturn<C>>(
|
|
135
|
-
component: C,
|
|
136
|
-
props: Omit<ModalComponentProps<C>, 'callback'>
|
|
137
|
-
): Promise<R | undefined> {
|
|
138
|
-
return new Promise<R>(resolve => {
|
|
139
|
-
let modalInjection: ModalInjection<C> | null = null;
|
|
140
|
-
const callback = (result: R) => {
|
|
141
|
-
removeModalInjection(modalInjection!);
|
|
142
|
-
resolve(result);
|
|
143
|
-
};
|
|
144
|
-
const resolvedProps = { ...props, callback } as ModalComponentProps<C>;
|
|
145
|
-
modalInjection = createModalInjection(component, resolvedProps);
|
|
146
|
-
});
|
|
147
|
-
}
|