@signal24/vue-foundation 4.30.5 → 4.30.8
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/overlay-container.d.ts +26 -34
- package/dist/src/components/vf-ajax-select.vue.d.ts +9 -5
- package/dist/src/components/vf-ez-smart-select.vue.d.ts +9 -5
- package/dist/src/components/vf-smart-select.vue.d.ts +9 -5
- package/dist/src/helpers/object.d.ts +11 -0
- package/dist/tsconfig.app.tsbuildinfo +1 -1
- package/dist/vue-foundation.es.js +894 -853
- package/package.json +21 -18
- package/src/components/overlay-container.ts +54 -66
- package/src/components/toast-helpers.ts +1 -1
- package/src/helpers/object.ts +64 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@signal24/vue-foundation",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "4.30.
|
|
4
|
+
"version": "4.30.8",
|
|
5
5
|
"description": "Common components, directives, and helpers for Vue 3 apps",
|
|
6
6
|
"module": "./dist/vue-foundation.es.js",
|
|
7
7
|
"exports": {
|
|
@@ -31,6 +31,9 @@
|
|
|
31
31
|
"format": "prettier --write ."
|
|
32
32
|
},
|
|
33
33
|
"license": "MIT",
|
|
34
|
+
"repository": {
|
|
35
|
+
"url": "https://github.com/signal24/vue-foundation"
|
|
36
|
+
},
|
|
34
37
|
"dependencies": {
|
|
35
38
|
"currency.js": "^2.0.4",
|
|
36
39
|
"mark.js": "^8.11.1",
|
|
@@ -43,37 +46,37 @@
|
|
|
43
46
|
"vue": "^3.4.0"
|
|
44
47
|
},
|
|
45
48
|
"devDependencies": {
|
|
46
|
-
"@eslint/js": "9.39.
|
|
49
|
+
"@eslint/js": "9.39.2",
|
|
47
50
|
"@nabla/vite-plugin-eslint": "^2.0.6",
|
|
48
51
|
"@signal24/openapi-client-codegen": "^2.6.2",
|
|
49
|
-
"@tsconfig/node22": "^22.0.
|
|
52
|
+
"@tsconfig/node22": "^22.0.5",
|
|
50
53
|
"@types/jsdom": "^27.0.0",
|
|
51
|
-
"@types/lodash": "^4.17.
|
|
54
|
+
"@types/lodash": "^4.17.21",
|
|
52
55
|
"@types/mark.js": "^8",
|
|
53
|
-
"@types/node": "^
|
|
56
|
+
"@types/node": "^25.0.3",
|
|
54
57
|
"@types/uuid": "^11.0.0",
|
|
55
|
-
"@vitejs/plugin-vue": "^6.0.
|
|
58
|
+
"@vitejs/plugin-vue": "^6.0.3",
|
|
56
59
|
"@vue/eslint-config-prettier": "^10.2.0",
|
|
57
60
|
"@vue/eslint-config-typescript": "^14.6.0",
|
|
58
61
|
"@vue/test-utils": "^2.4.6",
|
|
59
62
|
"@vue/tsconfig": "^0.8.1",
|
|
60
63
|
"date-fns": "^4.1.0",
|
|
61
|
-
"eslint": "9.39.
|
|
64
|
+
"eslint": "9.39.2",
|
|
62
65
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|
|
63
66
|
"eslint-plugin-unused-imports": "^4.3.0",
|
|
64
|
-
"eslint-plugin-vue": "^10.
|
|
65
|
-
"jsdom": "^27.
|
|
67
|
+
"eslint-plugin-vue": "^10.6.2",
|
|
68
|
+
"jsdom": "^27.4.0",
|
|
66
69
|
"lodash": "^4.17.21",
|
|
67
|
-
"prettier": "^3.
|
|
68
|
-
"sass": "^1.
|
|
69
|
-
"start-server-and-test": "^2.1.
|
|
70
|
-
"type-fest": "^5.
|
|
70
|
+
"prettier": "^3.7.4",
|
|
71
|
+
"sass": "^1.97.2",
|
|
72
|
+
"start-server-and-test": "^2.1.3",
|
|
73
|
+
"type-fest": "^5.3.1",
|
|
71
74
|
"typescript": "~5.9",
|
|
72
|
-
"typescript-eslint": "^8.
|
|
73
|
-
"vite": "^7.
|
|
74
|
-
"vitest": "^4.0.
|
|
75
|
-
"vue": "^3.5.
|
|
76
|
-
"vue-tsc": "^3.
|
|
75
|
+
"typescript-eslint": "^8.52.0",
|
|
76
|
+
"vite": "^7.3.1",
|
|
77
|
+
"vitest": "^4.0.16",
|
|
78
|
+
"vue": "^3.5.26",
|
|
79
|
+
"vue-tsc": "^3.2.2"
|
|
77
80
|
},
|
|
78
81
|
"packageManager": "yarn@4.9.2"
|
|
79
82
|
}
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import type { Writable } from 'type-fest';
|
|
3
2
|
import {
|
|
4
3
|
type AllowedComponentProps,
|
|
4
|
+
type Component,
|
|
5
|
+
type ComponentCustomProps,
|
|
5
6
|
type ComponentInternalInstance,
|
|
6
|
-
type ComponentPublicInstance,
|
|
7
|
-
type ComputedOptions,
|
|
8
7
|
defineComponent,
|
|
9
8
|
h,
|
|
10
9
|
markRaw,
|
|
11
|
-
type MethodOptions,
|
|
12
10
|
type Raw,
|
|
13
11
|
reactive,
|
|
14
12
|
renderList,
|
|
@@ -23,23 +21,23 @@ import { VfOptions } from '@/config';
|
|
|
23
21
|
import OverlayAnchor from './overlay-anchor.vue';
|
|
24
22
|
import type { OverlayAnchorOptions } from './overlay-types';
|
|
25
23
|
|
|
26
|
-
interface OverlayOptions<C extends
|
|
24
|
+
interface OverlayOptions<C extends Component, R extends ComponentReturn<C>> {
|
|
27
25
|
anchor?: OverlayAnchorOptions;
|
|
28
26
|
onCallback?: (result: R) => void | Promise<boolean>;
|
|
29
27
|
}
|
|
30
28
|
|
|
31
|
-
export interface OverlayInjection<C extends
|
|
29
|
+
export interface OverlayInjection<C extends Component> {
|
|
32
30
|
id: string;
|
|
33
|
-
component:
|
|
34
|
-
props: OverlayComponentProps<C>;
|
|
35
|
-
options: OverlayOptions<C,
|
|
31
|
+
component: Raw<C>;
|
|
32
|
+
props: { callback?: () => void } & OverlayComponentProps<C>;
|
|
33
|
+
options: OverlayOptions<C, any>;
|
|
36
34
|
vnode: VNode;
|
|
37
35
|
wrapperVnode?: VNode;
|
|
38
36
|
}
|
|
39
37
|
|
|
40
38
|
let overlayCount = 0;
|
|
41
39
|
|
|
42
|
-
const OverlayInjections: OverlayInjection<any
|
|
40
|
+
const OverlayInjections: OverlayInjection<any>[] = reactive([]);
|
|
43
41
|
watch(OverlayInjections, () => {
|
|
44
42
|
VfOptions.onOverlaysChanged?.(OverlayInjections.length);
|
|
45
43
|
});
|
|
@@ -55,57 +53,47 @@ export const OverlayContainer = defineComponent({
|
|
|
55
53
|
}
|
|
56
54
|
});
|
|
57
55
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
56
|
+
export type AnyComponentPublicInstance = { $?: ComponentInternalInstance };
|
|
57
|
+
|
|
58
|
+
// Handle both regular and generic components
|
|
59
|
+
type ExtractComponentProps<C> = C extends new (...args: any) => any
|
|
60
|
+
? InstanceType<C>['$props']
|
|
61
|
+
: C extends (props: infer P, ...args: any) => any
|
|
62
|
+
? P
|
|
63
|
+
: C extends { __props?: infer P }
|
|
64
|
+
? P
|
|
65
|
+
: never;
|
|
66
|
+
|
|
67
|
+
// Remove Vue's internal prop types and the Record<string, unknown> index signature
|
|
68
|
+
type CleanProps<P> = {
|
|
69
|
+
[K in keyof P as K extends keyof (VNodeProps & AllowedComponentProps & ComponentCustomProps) ? never : string extends K ? never : K]: P[K];
|
|
72
70
|
};
|
|
73
71
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
type ObjectOrDefault<T> = T extends object ? T : PropsWithCallback<{}>;
|
|
82
|
-
export type OverlayComponent = Vue__ComponentPublicInstanceConstructor | ((props: any) => any);
|
|
83
|
-
export type OverlayComponentConfig<T> = T extends Vue__ComponentPublicInstanceConstructor
|
|
84
|
-
? {
|
|
85
|
-
props: ObjectComponentProps<T>;
|
|
86
|
-
component: Raw<T>;
|
|
87
|
-
}
|
|
88
|
-
: T extends (props: infer P) => any
|
|
89
|
-
? {
|
|
90
|
-
props: Omit<ObjectOrDefault<P>, keyof VNodeProps | keyof AllowedComponentProps>;
|
|
91
|
-
component: T;
|
|
92
|
-
}
|
|
93
|
-
: never;
|
|
94
|
-
export type OverlayComponentUnwrapped<T extends OverlayComponent> = OverlayComponentConfig<T>['component'];
|
|
95
|
-
export type OverlayComponentProps<T extends OverlayComponent> = OverlayComponentConfig<T>['props'];
|
|
96
|
-
|
|
97
|
-
interface PropsWithCallback<T> {
|
|
98
|
-
callback?: (result: T) => void;
|
|
99
|
-
}
|
|
100
|
-
type ComponentReturn<M extends OverlayComponent> = OverlayComponentProps<M> extends PropsWithCallback<infer R> ? R : never;
|
|
72
|
+
// Check if component has a callback prop with correct signature
|
|
73
|
+
type HasCallbackProp<C> =
|
|
74
|
+
CleanProps<ExtractComponentProps<C>> extends {
|
|
75
|
+
callback: (result: any) => void;
|
|
76
|
+
}
|
|
77
|
+
? true
|
|
78
|
+
: false;
|
|
101
79
|
|
|
102
|
-
|
|
80
|
+
// Constraint type - resolves to C if valid, never if not
|
|
81
|
+
type OverlayComponent<C extends Component> = HasCallbackProp<C> extends true ? C : never;
|
|
82
|
+
|
|
83
|
+
type ComponentReturn<C> =
|
|
84
|
+
CleanProps<ExtractComponentProps<C>> extends {
|
|
85
|
+
callback: (result: infer R) => void;
|
|
86
|
+
}
|
|
87
|
+
? R
|
|
88
|
+
: never;
|
|
89
|
+
|
|
90
|
+
type OverlayComponentProps<C> = CleanProps<ExtractComponentProps<C>>;
|
|
103
91
|
|
|
104
|
-
export function createOverlayInjection<C extends
|
|
105
|
-
component: C
|
|
92
|
+
export function createOverlayInjection<C extends Component, R extends ComponentReturn<C>>(
|
|
93
|
+
component: OverlayComponent<C>,
|
|
106
94
|
props: OverlayComponentProps<C>,
|
|
107
95
|
options?: OverlayOptions<C, R>
|
|
108
|
-
): OverlayInjection<C
|
|
96
|
+
): OverlayInjection<C> {
|
|
109
97
|
// create or reconfigure the existing overlay target
|
|
110
98
|
// re-injecting every time keeps the overlay container at the very end of the DOM
|
|
111
99
|
const targetEl = document.getElementById('vf-overlay-target') ?? document.createElement('div');
|
|
@@ -119,9 +107,9 @@ export function createOverlayInjection<C extends OverlayComponent, R extends Com
|
|
|
119
107
|
const wrapperVnode = options?.anchor ? h(OverlayAnchor, { overlayId, anchor: options.anchor }, () => [vnode]) : undefined;
|
|
120
108
|
|
|
121
109
|
// todo: dunno what's going on with types here
|
|
122
|
-
const injection: OverlayInjection<C
|
|
110
|
+
const injection: OverlayInjection<C> = {
|
|
123
111
|
id: overlayId,
|
|
124
|
-
component: rawComponent
|
|
112
|
+
component: rawComponent,
|
|
125
113
|
props,
|
|
126
114
|
options: options ?? {},
|
|
127
115
|
vnode,
|
|
@@ -148,7 +136,7 @@ export function dismissOverlayInjectionByInternalInstance(instance: ComponentInt
|
|
|
148
136
|
export function dismissOverlayInjectionByVnode(vnode: VNode) {
|
|
149
137
|
const injectionIdx = OverlayInjections.findIndex(i => i.vnode.component === vnode.component);
|
|
150
138
|
if (injectionIdx >= 0) {
|
|
151
|
-
OverlayInjections[injectionIdx]!.props.callback();
|
|
139
|
+
OverlayInjections[injectionIdx]!.props.callback?.();
|
|
152
140
|
return true;
|
|
153
141
|
}
|
|
154
142
|
return false;
|
|
@@ -157,26 +145,26 @@ export function dismissOverlayInjectionByVnode(vnode: VNode) {
|
|
|
157
145
|
export function dismissOverlayInjectionById(id: string) {
|
|
158
146
|
const injectionIdx = OverlayInjections.findIndex(i => i.id === id);
|
|
159
147
|
if (injectionIdx >= 0) {
|
|
160
|
-
OverlayInjections[injectionIdx]!.props.callback();
|
|
148
|
+
OverlayInjections[injectionIdx]!.props.callback?.();
|
|
161
149
|
return true;
|
|
162
150
|
}
|
|
163
151
|
return false;
|
|
164
152
|
}
|
|
165
153
|
|
|
166
|
-
export function removeOverlayInjection(injection: OverlayInjection<any
|
|
154
|
+
export function removeOverlayInjection(injection: OverlayInjection<any>) {
|
|
167
155
|
const index = OverlayInjections.indexOf(injection);
|
|
168
156
|
if (index >= 0) {
|
|
169
157
|
OverlayInjections.splice(index, 1);
|
|
170
158
|
}
|
|
171
159
|
}
|
|
172
160
|
|
|
173
|
-
export async function presentOverlay<C extends
|
|
174
|
-
component: C
|
|
161
|
+
export async function presentOverlay<C extends Component, R extends ComponentReturn<C>>(
|
|
162
|
+
component: OverlayComponent<C>,
|
|
175
163
|
props: Omit<OverlayComponentProps<C>, 'callback'>,
|
|
176
164
|
options?: OverlayOptions<C, R>
|
|
177
165
|
): Promise<R | undefined> {
|
|
178
166
|
return new Promise<R>(resolve => {
|
|
179
|
-
let overlayInjection: OverlayInjection<C
|
|
167
|
+
let overlayInjection: OverlayInjection<C> | null = null;
|
|
180
168
|
const callback = async (result: R) => {
|
|
181
169
|
if (options?.onCallback) {
|
|
182
170
|
const hookResult = options.onCallback(result);
|
|
@@ -197,12 +185,12 @@ export async function presentOverlay<C extends OverlayComponent, R extends Compo
|
|
|
197
185
|
});
|
|
198
186
|
}
|
|
199
187
|
|
|
200
|
-
export async function updateOverlayProps<C extends
|
|
201
|
-
injection: OverlayInjection<C
|
|
188
|
+
export async function updateOverlayProps<C extends Component>(
|
|
189
|
+
injection: OverlayInjection<C>,
|
|
202
190
|
props: Partial<Omit<OverlayComponentProps<C>, 'callback'>>
|
|
203
191
|
) {
|
|
204
192
|
const targetProps = injection.vnode.component!.props;
|
|
205
193
|
for (const key in props) {
|
|
206
|
-
targetProps[key] = props[key];
|
|
194
|
+
targetProps[key] = (props as any)[key];
|
|
207
195
|
}
|
|
208
196
|
}
|
|
@@ -2,7 +2,7 @@ import { createOverlayInjection, type OverlayInjection, removeOverlayInjection }
|
|
|
2
2
|
import Toast, { type IToastOptions } from './vf-toast.vue';
|
|
3
3
|
|
|
4
4
|
export function showToast(options: IToastOptions) {
|
|
5
|
-
const injection: OverlayInjection<typeof Toast
|
|
5
|
+
const injection: OverlayInjection<typeof Toast> = createOverlayInjection(Toast, {
|
|
6
6
|
...options,
|
|
7
7
|
callback: () => removeOverlayInjection(injection)
|
|
8
8
|
});
|
package/src/helpers/object.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { cloneDeep } from 'lodash';
|
|
1
|
+
import { cloneDeep, isEqual, isMatch } from 'lodash';
|
|
2
2
|
|
|
3
3
|
export function cloneProp<T>(prop: T | undefined | null, fallback: T): T {
|
|
4
4
|
if (prop !== undefined && prop !== null) {
|
|
@@ -22,3 +22,66 @@ export function nullifyEmptyInputs<T extends Record<string, unknown>, K extends
|
|
|
22
22
|
export function isNotNullOrUndefined<T>(value: T | null | undefined): value is T {
|
|
23
23
|
return value !== null && value !== undefined;
|
|
24
24
|
}
|
|
25
|
+
|
|
26
|
+
export function objectKeys<T extends object>(object: T): (keyof T)[] {
|
|
27
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
28
|
+
return Object.keys(object) as any[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function objectAssign<T extends object>(object: T, ...values: Partial<T>[]): T {
|
|
32
|
+
return Object.assign(object, ...values);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
type Entries<T> = {
|
|
36
|
+
[K in keyof T]: [K, T[K]];
|
|
37
|
+
}[keyof T][];
|
|
38
|
+
export function objectEntries<T extends object>(object: T): Entries<T> {
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
40
|
+
return Object.entries(object) as any;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function extractValues<T extends object, K extends readonly (keyof T)[]>(state: T, fields: K): Pick<T, K[number]> {
|
|
44
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
45
|
+
const result: Pick<T, K[number]> = {} as any;
|
|
46
|
+
for (const key of fields) {
|
|
47
|
+
if (state[key] !== undefined) {
|
|
48
|
+
result[key] = state[key];
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
55
|
+
function doesMatch(original: any, updated: any, method?: 'equals' | 'matches'): boolean {
|
|
56
|
+
if (method === 'matches' && typeof original === 'object' && typeof updated === 'object') {
|
|
57
|
+
return isMatch(original, updated);
|
|
58
|
+
}
|
|
59
|
+
return isEqual(original, updated);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function extractUpdates<T extends object>(state: T, updates: Partial<T>, fields?: Array<keyof T>, method?: 'equals' | 'matches'): Partial<T> {
|
|
63
|
+
const result: Partial<T> = {};
|
|
64
|
+
const updateFields = fields ?? objectKeys(updates);
|
|
65
|
+
for (const key of updateFields) {
|
|
66
|
+
if (updates[key] !== undefined && !doesMatch(state[key], updates[key], method)) {
|
|
67
|
+
result[key] = updates[key];
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function patchObject<T extends object>(state: T, updates: Partial<T>, fields?: Array<keyof T>, method?: 'equals' | 'matches'): T {
|
|
74
|
+
const effectiveUpdates = extractUpdates(state, updates, fields, method);
|
|
75
|
+
objectAssign(state, effectiveUpdates);
|
|
76
|
+
return state;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function extractKV<T, K extends keyof T, V extends keyof T>(arr: T[], keyCol: K, valCol: V) {
|
|
80
|
+
return arr.reduce(
|
|
81
|
+
(acc, cur) => {
|
|
82
|
+
acc[cur[keyCol] as string] = cur[valCol];
|
|
83
|
+
return acc;
|
|
84
|
+
},
|
|
85
|
+
{} as Record<string, T[V]>
|
|
86
|
+
);
|
|
87
|
+
}
|