@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 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 (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)
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
- loading.value = false
26
- if (options?.finally) { await options?.finally() }
27
- return result
28
- } catch (err) {
29
- const abortError = err.name === 'AbortError' || err.cause?.name === 'AbortError'
30
- if (!abortError) {
31
- error.value = getErrorMsg(err)
32
- const errorNotif = getFullNotif({ msg: options?.error ?? '', error: err })
33
- notif.value = errorNotif
34
- if (options?.catch !== 'error' && options?.catch !== 'all') {
35
- sendUiNotif(errorNotif)
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
- loading.value = false
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;
@@ -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;
@@ -1,22 +1,28 @@
1
1
  // filter reactiveSearchParams to conceptFilters (params prefixed by _c_)
2
- import { reactive, watch } from 'vue'
3
- export function useConceptFilters (reactiveSearchParams, datasetId) {
4
- const conceptFilters = reactive({})
5
- const datasetFiltersPrefix = datasetId && `_d_${datasetId}_`
6
- watch(reactiveSearchParams, () => {
7
- for (const key of Object.keys(reactiveSearchParams)) {
8
- if (key.startsWith('_c_')) { conceptFilters[key] = reactiveSearchParams[key] }
9
- if (datasetFiltersPrefix && key.startsWith(datasetFiltersPrefix)) {
10
- conceptFilters[key.replace(datasetFiltersPrefix, '')] = reactiveSearchParams[key]
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
- for (const key of Object.keys(conceptFilters)) {
14
- if (key.startsWith('_c_') && reactiveSearchParams[key] === undefined) { delete conceptFilters[key] }
15
- if (datasetFiltersPrefix && !key.startsWith('_c_') && reactiveSearchParams[datasetFiltersPrefix + key] === undefined) {
16
- delete conceptFilters[key]
17
- }
18
- }
19
- }, { immediate: true })
20
- return conceptFilters
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 (url, options = {}) {
6
- const { sendUiNotif } = useUiNotif()
7
- if (typeof url === 'function') { url = computed(url) }
8
- const fullUrl = computed(() => {
9
- let fullUrl = isRef(url) ? url.value : url
10
- if (!fullUrl) { return null }
11
- const query = isRef(options.query) ? options.query.value : options.query
12
- if (query) { fullUrl = withQuery(fullUrl, query) }
13
- return fullUrl
14
- })
15
- const data = shallowRef(null)
16
- const loading = ref(false)
17
- const initialized = ref(false)
18
- const error = shallowRef(null)
19
- let abortController
20
- const refresh = async () => {
21
- initialized.value = true
22
- if (!fullUrl.value) { return null }
23
- error.value = null
24
- if (abortController) { abortController.abort() }
25
- loading.value = true
26
- abortController = new AbortController()
27
- try {
28
- data.value = await ofetch(fullUrl.value, { signal: abortController.signal })
29
- } catch (err) {
30
- if (err.name !== 'AbortError' && err.cause?.name !== 'AbortError') {
31
- error.value = err
32
- if (options.notifError !== false) {
33
- sendUiNotif({ msg: '', error: err })
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
- loading.value = false
38
- return data.value
39
- }
40
- if (options.watch !== false) {
41
- watch(fullUrl, () => {
42
- if (options.immediate !== false || initialized.value) { refresh() }
43
- }, { immediate: true })
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;
@@ -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
- throw new Error('this module uses a module level singleton, it cannot be used in SSR mode')
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 (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
- }
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 (locale) {
30
- const localeDayjs = getLocaleDayjs(locale)
31
- return { install (app) { app.provide(localeDayjsKey, localeDayjs) } }
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
- const localeDayjs = inject(localeDayjsKey)
35
- if (!localeDayjs) { throw new Error('useLocaleDayjs requires using the plugin createLocaleDayjs') }
36
- return localeDayjs
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.21.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
- throw new Error('this module uses a module level singleton, it cannot be used in SSR mode')
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();