@data-fair/lib-vue 1.21.1 → 1.23.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/async-action.js +43 -40
- package/computed-deep-diff.js +9 -0
- package/concept-filters.d.ts +1 -0
- package/concept-filters.js +25 -19
- package/deep-diff.d.ts +3 -0
- package/deep-diff.js +14 -0
- package/fetch.js +57 -50
- package/locale-dayjs-global.js +4 -4
- package/locale-dayjs.js +33 -32
- package/package.json +2 -1
- package/reactive-search-params-global.js +3 -3
- package/reactive-search-params.js +140 -124
- package/session.js +369 -325
- package/ui-notif.js +80 -63
- package/vite.d.ts +1 -0
- package/vite.js +15 -14
- package/ws.js +62 -58
package/async-action.js
CHANGED
|
@@ -1,44 +1,47 @@
|
|
|
1
1
|
// similar to withUiNotif but more powerful
|
|
2
|
-
import { ref, readonly, shallowReadonly, shallowRef } from 'vue'
|
|
3
|
-
import { useUiNotif, getFullNotif, getErrorMsg } from './ui-notif.js'
|
|
4
|
-
export function useAsyncAction
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
2
|
+
import { ref, readonly, shallowReadonly, shallowRef } from 'vue';
|
|
3
|
+
import { useUiNotif, getFullNotif, getErrorMsg } from './ui-notif.js';
|
|
4
|
+
export function useAsyncAction(fn, options) {
|
|
5
|
+
if (typeof options === 'function') {
|
|
6
|
+
options = { finally: options };
|
|
7
|
+
}
|
|
8
|
+
const { sendUiNotif } = useUiNotif();
|
|
9
|
+
const notif = shallowRef();
|
|
10
|
+
const loading = ref(false);
|
|
11
|
+
const error = ref();
|
|
12
|
+
const execute = async function (...args) {
|
|
13
|
+
loading.value = true;
|
|
14
|
+
notif.value = undefined;
|
|
15
|
+
error.value = undefined;
|
|
16
|
+
try {
|
|
17
|
+
const result = await fn(...args);
|
|
18
|
+
if (options?.success) {
|
|
19
|
+
const successNotif = getFullNotif(options?.success, 'success');
|
|
20
|
+
notif.value = successNotif;
|
|
21
|
+
if (options?.catch !== 'success' && options?.catch !== 'all') {
|
|
22
|
+
sendUiNotif(successNotif);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
loading.value = false;
|
|
26
|
+
if (options?.finally)
|
|
27
|
+
await options?.finally();
|
|
28
|
+
return result;
|
|
23
29
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
catch (err) {
|
|
31
|
+
const abortError = err.name === 'AbortError' || err.cause?.name === 'AbortError';
|
|
32
|
+
if (!abortError) {
|
|
33
|
+
error.value = getErrorMsg(err);
|
|
34
|
+
const errorNotif = getFullNotif({ msg: options?.error ?? '', error: err });
|
|
35
|
+
notif.value = errorNotif;
|
|
36
|
+
if (options?.catch !== 'error' && options?.catch !== 'all') {
|
|
37
|
+
sendUiNotif(errorNotif);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
loading.value = false;
|
|
41
|
+
if (options?.finally && !abortError)
|
|
42
|
+
await options?.finally();
|
|
36
43
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if (options?.finally && !abortError) { await options?.finally() }
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
return { execute, notif: shallowReadonly(notif), loading: readonly(loading), error: readonly(error) }
|
|
44
|
+
};
|
|
45
|
+
return { execute, notif: shallowReadonly(notif), loading: readonly(loading), error: readonly(error) };
|
|
43
46
|
}
|
|
44
|
-
export default useAsyncAction
|
|
47
|
+
export default useAsyncAction;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { computed } from 'vue';
|
|
2
|
+
import equal from 'fast-deep-equal';
|
|
3
|
+
export const computedDeepDiff = (getter, options) => {
|
|
4
|
+
return computed((oldValue) => {
|
|
5
|
+
const newValue = getter();
|
|
6
|
+
return (oldValue !== undefined && equal(newValue, oldValue)) ? oldValue : newValue;
|
|
7
|
+
}, options);
|
|
8
|
+
};
|
|
9
|
+
export default computedDeepDiff;
|
package/concept-filters.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
+
export declare function getConceptFilters(searchParams: Record<string, string>, datasetId?: string): Record<string, string>;
|
|
1
2
|
export declare function useConceptFilters(reactiveSearchParams: Record<string, string>, datasetId?: string): Record<string, string>;
|
|
2
3
|
export default useConceptFilters;
|
package/concept-filters.js
CHANGED
|
@@ -1,22 +1,28 @@
|
|
|
1
1
|
// filter reactiveSearchParams to conceptFilters (params prefixed by _c_)
|
|
2
|
-
import { reactive, watch } from 'vue'
|
|
3
|
-
export function
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
2
|
+
import { reactive, watch } from 'vue';
|
|
3
|
+
export function getConceptFilters(searchParams, datasetId) {
|
|
4
|
+
const conceptFilters = {};
|
|
5
|
+
const datasetFiltersPrefix = datasetId && `_d_${datasetId}_`;
|
|
6
|
+
for (const key of Object.keys(searchParams)) {
|
|
7
|
+
if (key.startsWith('_c_'))
|
|
8
|
+
conceptFilters[key] = searchParams[key];
|
|
9
|
+
if (datasetFiltersPrefix && key.startsWith(datasetFiltersPrefix)) {
|
|
10
|
+
conceptFilters[key.replace(datasetFiltersPrefix, '')] = searchParams[key];
|
|
11
|
+
}
|
|
12
12
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
13
|
+
return conceptFilters;
|
|
14
|
+
}
|
|
15
|
+
export function useConceptFilters(reactiveSearchParams, datasetId) {
|
|
16
|
+
const conceptFilters = reactive({});
|
|
17
|
+
// we use a watch and mutations on a reactive to prevent triggering reactivity on unrelated changes in reactive search params
|
|
18
|
+
watch(reactiveSearchParams, () => {
|
|
19
|
+
const newConceptFilters = getConceptFilters(reactiveSearchParams, datasetId);
|
|
20
|
+
Object.assign(conceptFilters, newConceptFilters);
|
|
21
|
+
for (const key of Object.keys(conceptFilters)) {
|
|
22
|
+
if (!(key in newConceptFilters))
|
|
23
|
+
delete conceptFilters[key];
|
|
24
|
+
}
|
|
25
|
+
}, { immediate: true });
|
|
26
|
+
return conceptFilters;
|
|
21
27
|
}
|
|
22
|
-
export default useConceptFilters
|
|
28
|
+
export default useConceptFilters;
|
package/deep-diff.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { type ComputedGetter, type ComputedOptions, type WatchSource, type WatchCallback, type WatchOptions } from 'vue';
|
|
2
|
+
export declare const computedDeepDiff: <Type>(getter: ComputedGetter<Type>, options: ComputedOptions) => import("vue").ComputedRef<Type>;
|
|
3
|
+
export declare const watchDeepDiff: (source: WatchSource, callback: WatchCallback, options: WatchOptions) => import("vue").WatchHandle;
|
package/deep-diff.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { computed, watch } from 'vue';
|
|
2
|
+
import equal from 'fast-deep-equal';
|
|
3
|
+
export const computedDeepDiff = (getter, options) => {
|
|
4
|
+
return computed((oldValue) => {
|
|
5
|
+
const newValue = getter();
|
|
6
|
+
return (oldValue !== undefined && equal(newValue, oldValue)) ? oldValue : newValue;
|
|
7
|
+
}, options);
|
|
8
|
+
};
|
|
9
|
+
export const watchDeepDiff = (source, callback, options) => {
|
|
10
|
+
return watch(source, (newValue, oldValue, onCleanup) => {
|
|
11
|
+
if (!equal(newValue, oldValue))
|
|
12
|
+
callback(newValue, oldValue, onCleanup);
|
|
13
|
+
}, options);
|
|
14
|
+
};
|
package/fetch.js
CHANGED
|
@@ -1,53 +1,60 @@
|
|
|
1
|
-
import { ofetch } from 'ofetch'
|
|
2
|
-
import { withQuery } from 'ufo'
|
|
3
|
-
import { computed, isRef, watch, ref, shallowRef, readonly, shallowReadonly } from 'vue'
|
|
4
|
-
import { useUiNotif } from '@data-fair/lib-vue/ui-notif.js'
|
|
5
|
-
export function useFetch
|
|
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
|
-
|
|
1
|
+
import { ofetch } from 'ofetch';
|
|
2
|
+
import { withQuery } from 'ufo';
|
|
3
|
+
import { computed, isRef, watch, ref, shallowRef, readonly, shallowReadonly } from 'vue';
|
|
4
|
+
import { useUiNotif } from '@data-fair/lib-vue/ui-notif.js';
|
|
5
|
+
export function useFetch(url, options = {}) {
|
|
6
|
+
const { sendUiNotif } = useUiNotif();
|
|
7
|
+
if (typeof url === 'function')
|
|
8
|
+
url = computed(url);
|
|
9
|
+
const fullUrl = computed(() => {
|
|
10
|
+
let fullUrl = isRef(url) ? url.value : url;
|
|
11
|
+
if (!fullUrl)
|
|
12
|
+
return null;
|
|
13
|
+
const query = isRef(options.query) ? options.query.value : options.query;
|
|
14
|
+
if (query)
|
|
15
|
+
fullUrl = withQuery(fullUrl, query);
|
|
16
|
+
return fullUrl;
|
|
17
|
+
});
|
|
18
|
+
const data = shallowRef(null);
|
|
19
|
+
const loading = ref(false);
|
|
20
|
+
const initialized = ref(false);
|
|
21
|
+
const error = shallowRef(null);
|
|
22
|
+
let abortController;
|
|
23
|
+
const refresh = async () => {
|
|
24
|
+
initialized.value = true;
|
|
25
|
+
if (!fullUrl.value)
|
|
26
|
+
return null;
|
|
27
|
+
error.value = null;
|
|
28
|
+
if (abortController)
|
|
29
|
+
abortController.abort();
|
|
30
|
+
loading.value = true;
|
|
31
|
+
abortController = new AbortController();
|
|
32
|
+
try {
|
|
33
|
+
data.value = await ofetch(fullUrl.value, { signal: abortController.signal });
|
|
34
34
|
}
|
|
35
|
-
|
|
35
|
+
catch (err) {
|
|
36
|
+
if (err.name !== 'AbortError' && err.cause?.name !== 'AbortError') {
|
|
37
|
+
error.value = err;
|
|
38
|
+
if (options.notifError !== false) {
|
|
39
|
+
sendUiNotif({ msg: '', error: err });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
loading.value = false;
|
|
44
|
+
return data.value;
|
|
45
|
+
};
|
|
46
|
+
if (options.watch !== false) {
|
|
47
|
+
watch(fullUrl, () => {
|
|
48
|
+
if (options.immediate !== false || initialized.value)
|
|
49
|
+
refresh();
|
|
50
|
+
}, { immediate: true });
|
|
36
51
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
return {
|
|
46
|
-
initialized: readonly(initialized),
|
|
47
|
-
data: shallowReadonly(data),
|
|
48
|
-
loading: readonly(loading),
|
|
49
|
-
error: shallowReadonly(error),
|
|
50
|
-
refresh,
|
|
51
|
-
}
|
|
52
|
+
return {
|
|
53
|
+
initialized: readonly(initialized),
|
|
54
|
+
data: shallowReadonly(data),
|
|
55
|
+
loading: readonly(loading),
|
|
56
|
+
error: shallowReadonly(error),
|
|
57
|
+
refresh,
|
|
58
|
+
};
|
|
52
59
|
}
|
|
53
|
-
export default useFetch
|
|
60
|
+
export default useFetch;
|
package/locale-dayjs-global.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// same as locale-dayjs but in a module level singleton for convenience when not using SSR
|
|
2
|
-
import { getLocaleDayjs } from './locale-dayjs.js'
|
|
2
|
+
import { getLocaleDayjs } from './locale-dayjs.js';
|
|
3
3
|
// @ts-ignore
|
|
4
4
|
if (import.meta.env?.SSR) {
|
|
5
|
-
|
|
5
|
+
throw new Error('this module uses a module level singleton, it cannot be used in SSR mode');
|
|
6
6
|
}
|
|
7
|
-
console.error('locale-dayjs-global is deprecated, please use create + use')
|
|
8
|
-
export const { locale, dayjs } = getLocaleDayjs()
|
|
7
|
+
console.error('locale-dayjs-global is deprecated, please use create + use');
|
|
8
|
+
export const { locale, dayjs } = getLocaleDayjs();
|
package/locale-dayjs.js
CHANGED
|
@@ -1,38 +1,39 @@
|
|
|
1
|
-
import { inject } from 'vue'
|
|
2
|
-
import dayjs from 'dayjs'
|
|
3
|
-
import 'dayjs/locale/fr'
|
|
4
|
-
import 'dayjs/locale/en'
|
|
5
|
-
import localizedFormat from 'dayjs/plugin/localizedFormat.js'
|
|
6
|
-
import relativeTime from 'dayjs/plugin/relativeTime.js'
|
|
7
|
-
import duration from 'dayjs/plugin/duration.js'
|
|
8
|
-
dayjs.extend(localizedFormat)
|
|
9
|
-
dayjs.extend(relativeTime)
|
|
10
|
-
dayjs.extend(duration)
|
|
1
|
+
import { inject } from 'vue';
|
|
2
|
+
import dayjs from 'dayjs';
|
|
3
|
+
import 'dayjs/locale/fr';
|
|
4
|
+
import 'dayjs/locale/en';
|
|
5
|
+
import localizedFormat from 'dayjs/plugin/localizedFormat.js';
|
|
6
|
+
import relativeTime from 'dayjs/plugin/relativeTime.js';
|
|
7
|
+
import duration from 'dayjs/plugin/duration.js';
|
|
8
|
+
dayjs.extend(localizedFormat);
|
|
9
|
+
dayjs.extend(relativeTime);
|
|
10
|
+
dayjs.extend(duration);
|
|
11
11
|
// main functionality, use through the createLocaleDayjs plugin and useLocaleDayjs composable
|
|
12
12
|
// or as a global singleton through ./locale-dayjs-global.js
|
|
13
|
-
export function getLocaleDayjs
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
13
|
+
export function getLocaleDayjs(locale) {
|
|
14
|
+
locale = locale ?? 'fr';
|
|
15
|
+
return {
|
|
16
|
+
locale,
|
|
17
|
+
duration: ((...args) => {
|
|
18
|
+
// @ts-ignore
|
|
19
|
+
return dayjs.duration(...args).locale(locale);
|
|
20
|
+
}),
|
|
21
|
+
dayjs: ((...args) => {
|
|
22
|
+
// @ts-ignore
|
|
23
|
+
return dayjs(...args).locale(locale);
|
|
24
|
+
})
|
|
25
|
+
};
|
|
26
26
|
}
|
|
27
27
|
// uses pattern for SSR friendly plugin/composable, cf https://antfu.me/posts/composable-vue-vueday-2021#shared-state-ssr-friendly
|
|
28
|
-
export const localeDayjsKey = Symbol('localeDayjs')
|
|
29
|
-
export function createLocaleDayjs
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
export const localeDayjsKey = Symbol('localeDayjs');
|
|
29
|
+
export function createLocaleDayjs(locale) {
|
|
30
|
+
const localeDayjs = getLocaleDayjs(locale);
|
|
31
|
+
return { install(app) { app.provide(localeDayjsKey, localeDayjs); } };
|
|
32
32
|
}
|
|
33
|
-
export function useLocaleDayjs
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
export function useLocaleDayjs() {
|
|
34
|
+
const localeDayjs = inject(localeDayjsKey);
|
|
35
|
+
if (!localeDayjs)
|
|
36
|
+
throw new Error('useLocaleDayjs requires using the plugin createLocaleDayjs');
|
|
37
|
+
return localeDayjs;
|
|
37
38
|
}
|
|
38
|
-
export default useLocaleDayjs
|
|
39
|
+
export default useLocaleDayjs;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@data-fair/lib-vue",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.23.0",
|
|
4
4
|
"description": "Composables and other utilities for Vue applications in the data-fair stack.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"files": [
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@data-fair/lib-common-types": "^1.7.1",
|
|
32
32
|
"@data-fair/lib-utils": "^1.0.0",
|
|
33
|
+
"fast-deep-equal": "^3.1.3",
|
|
33
34
|
"jwt-decode": "^4.0.0",
|
|
34
35
|
"universal-cookie": "^7.2.0"
|
|
35
36
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// same as use-reactive-search-params.js but in a module level singleton for convenience when not using SSR
|
|
2
|
-
import { getReactiveSearchParams } from './reactive-search-params.js'
|
|
2
|
+
import { getReactiveSearchParams } from './reactive-search-params.js';
|
|
3
3
|
// @ts-ignore
|
|
4
4
|
if (import.meta.env?.SSR) {
|
|
5
|
-
|
|
5
|
+
throw new Error('this module uses a module level singleton, it cannot be used in SSR mode');
|
|
6
6
|
}
|
|
7
|
-
export default getReactiveSearchParams()
|
|
7
|
+
export default getReactiveSearchParams();
|