@basmilius/common 3.0.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.
Files changed (72) hide show
  1. package/README.md +19 -0
  2. package/dist/composable/index.d.ts +16 -0
  3. package/dist/composable/useCopy.d.ts +2 -0
  4. package/dist/composable/useDebounced.d.ts +1 -0
  5. package/dist/composable/useDtoForm.d.ts +2 -0
  6. package/dist/composable/useInterval.d.ts +2 -0
  7. package/dist/composable/useLoaded.d.ts +6 -0
  8. package/dist/composable/useLoadedAction.d.ts +2 -0
  9. package/dist/composable/useLocalFile.d.ts +1 -0
  10. package/dist/composable/useMounted.d.ts +2 -0
  11. package/dist/composable/useNamedRoute.d.ts +2 -0
  12. package/dist/composable/useNavigate.d.ts +5 -0
  13. package/dist/composable/usePagination.d.ts +1 -0
  14. package/dist/composable/usePasswordStrength.d.ts +2 -0
  15. package/dist/composable/useRouteMeta.d.ts +5 -0
  16. package/dist/composable/useRouteNames.d.ts +1 -0
  17. package/dist/composable/useRouteParam.d.ts +2 -0
  18. package/dist/composable/useService.d.ts +6 -0
  19. package/dist/error/ForbiddenException.d.ts +1 -0
  20. package/dist/error/HandledException.d.ts +1 -0
  21. package/dist/error/UnauthorizedException.d.ts +1 -0
  22. package/dist/error/index.d.ts +3 -0
  23. package/dist/index.d.ts +4 -0
  24. package/dist/index.js +7 -0
  25. package/dist/index.js.map +267 -0
  26. package/dist/store/defineStore.d.ts +8 -0
  27. package/dist/store/index.d.ts +2 -0
  28. package/dist/util/emptyNull.d.ts +1 -0
  29. package/dist/util/generateColorPalette.d.ts +1 -0
  30. package/dist/util/guarded.d.ts +3 -0
  31. package/dist/util/index.d.ts +9 -0
  32. package/dist/util/onError.d.ts +1 -0
  33. package/dist/util/onErrorSnackbar.d.ts +2 -0
  34. package/dist/util/persistentRef.d.ts +4 -0
  35. package/dist/util/persistentStringRef.d.ts +2 -0
  36. package/dist/util/runBefore.d.ts +1 -0
  37. package/dist/util/unrefAll.d.ts +3 -0
  38. package/package.json +66 -0
  39. package/src/composable/index.ts +16 -0
  40. package/src/composable/useCopy.ts +8 -0
  41. package/src/composable/useDebounced.ts +5 -0
  42. package/src/composable/useDtoForm.ts +17 -0
  43. package/src/composable/useInterval.ts +22 -0
  44. package/src/composable/useLoaded.ts +38 -0
  45. package/src/composable/useLoadedAction.ts +13 -0
  46. package/src/composable/useLocalFile.ts +40 -0
  47. package/src/composable/useMounted.ts +9 -0
  48. package/src/composable/useNamedRoute.ts +17 -0
  49. package/src/composable/useNavigate.ts +28 -0
  50. package/src/composable/usePagination.ts +35 -0
  51. package/src/composable/usePasswordStrength.ts +67 -0
  52. package/src/composable/useRouteMeta.ts +34 -0
  53. package/src/composable/useRouteNames.ts +14 -0
  54. package/src/composable/useRouteParam.ts +13 -0
  55. package/src/composable/useService.ts +20 -0
  56. package/src/error/ForbiddenException.ts +2 -0
  57. package/src/error/HandledException.ts +2 -0
  58. package/src/error/UnauthorizedException.ts +2 -0
  59. package/src/error/index.ts +3 -0
  60. package/src/index.ts +4 -0
  61. package/src/store/defineStore.ts +31 -0
  62. package/src/store/index.ts +7 -0
  63. package/src/util/emptyNull.ts +3 -0
  64. package/src/util/generateColorPalette.ts +132 -0
  65. package/src/util/guarded.ts +73 -0
  66. package/src/util/index.ts +9 -0
  67. package/src/util/onError.ts +5 -0
  68. package/src/util/onErrorSnackbar.ts +15 -0
  69. package/src/util/persistentRef.ts +21 -0
  70. package/src/util/persistentStringRef.ts +6 -0
  71. package/src/util/runBefore.ts +8 -0
  72. package/src/util/unrefAll.ts +19 -0
@@ -0,0 +1,132 @@
1
+ import { converter, formatHex } from 'culori';
2
+
3
+ type Shade =
4
+ | 25
5
+ | 50
6
+ | 100
7
+ | 200
8
+ | 300
9
+ | 400
10
+ | 500
11
+ | 600
12
+ | 700
13
+ | 800
14
+ | 900
15
+ | 950;
16
+
17
+ const toOklch = converter('oklch');
18
+
19
+ const SHADES: Shade[] = [25, 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950];
20
+
21
+ const POSITION: Record<Shade, number> = {
22
+ 25: 0.00,
23
+ 50: 0.05,
24
+ 100: 0.10,
25
+ 200: 0.20,
26
+ 300: 0.35,
27
+ 400: 0.48,
28
+ 500: 0.55,
29
+ 600: 0.65,
30
+ 700: 0.75,
31
+ 800: 0.85,
32
+ 900: 0.93,
33
+ 950: 0.98
34
+ };
35
+
36
+ function clamp(v: number, min = 0, max = 1) {
37
+ return Math.min(max, Math.max(min, v));
38
+ }
39
+
40
+ export default function (baseHex: string, prefix = 'color') {
41
+ const base = toOklch(baseHex);
42
+
43
+ if (!base || base.mode !== 'oklch') {
44
+ throw new Error('Invalid color');
45
+ }
46
+
47
+ const palette: Record<string, string> = {};
48
+
49
+ for (const shade of SHADES) {
50
+ const p = POSITION[shade];
51
+
52
+ let l =
53
+ shade < 500
54
+ ? base.l + (1 - base.l) * (1 - p / POSITION[500])
55
+ : base.l * (1 - (p - POSITION[500]) / (1 - POSITION[500]));
56
+
57
+ l = clamp(l);
58
+
59
+ const distanceFrom500 = Math.abs(p - POSITION[500]);
60
+ const chromaFalloff = 1 - distanceFrom500 * 1.4;
61
+
62
+ const c = (base.c ?? 0) * clamp(chromaFalloff, 0.2, 1);
63
+
64
+ palette[`--${prefix}-${shade}`] = formatHex({
65
+ mode: 'oklch',
66
+ l,
67
+ c,
68
+ h: base.h
69
+ });
70
+ }
71
+
72
+ return palette;
73
+ }
74
+
75
+ // Tailwind Like
76
+ // import { converter, formatHex } from 'culori';
77
+ // import { clamp } from 'lodash-es';
78
+ //
79
+ // type Shade =
80
+ // | 25 | 50 | 100 | 200 | 300 | 400
81
+ // | 500 | 600 | 700 | 800 | 900 | 950;
82
+ //
83
+ // const toOklch = converter('oklch');
84
+ //
85
+ // const SHADES: Shade[] = [
86
+ // 25, 50, 100, 200, 300, 400,
87
+ // 500,
88
+ // 600, 700, 800, 900, 950
89
+ // ];
90
+ //
91
+ // // Perceptual lightness curve (similar to Tailwind gray)
92
+ // const LIGHTNESS_CURVE: Record<Shade, number> = {
93
+ // 25: 0.99,
94
+ // 50: 0.96,
95
+ // 100: 0.92,
96
+ // 200: 0.86,
97
+ // 300: 0.78,
98
+ // 400: 0.66,
99
+ // 500: 0.55,
100
+ // 600: 0.44,
101
+ // 700: 0.33,
102
+ // 800: 0.23,
103
+ // 900: 0.15,
104
+ // 950: 0.08
105
+ // };
106
+ //
107
+ // export default function (baseHex: string, prefix = 'color', maxChroma = 0.08) {
108
+ // const base = toOklch(baseHex);
109
+ //
110
+ // if (!base || base.mode !== 'oklch') {
111
+ // throw new Error('Invalid color');
112
+ // }
113
+ //
114
+ // const palette: Record<string, string> = {};
115
+ //
116
+ // for (const shade of SHADES) {
117
+ // let l = LIGHTNESS_CURVE[shade];
118
+ //
119
+ // const distanceFromMiddle = Math.abs(shade === 500 ? 0 : (shade - 500) / 450);
120
+ // const c = clamp((base.c ?? 0) * maxChroma * (1 - distanceFromMiddle), 0, 1);
121
+ //
122
+ // palette[`--${prefix}-${shade}`] = formatHex({
123
+ // mode: 'oklch',
124
+ // l,
125
+ // c,
126
+ // h: base.h
127
+ // });
128
+ // }
129
+ //
130
+ // return palette;
131
+ // }
132
+
@@ -0,0 +1,73 @@
1
+ import { isRequestError, isUnsanctionedRequest } from '@basmilius/http-client';
2
+ import { showSnackbar } from '@flux-ui/components';
3
+ import { ForbiddenException, HandledException, UnauthorizedException } from '../error';
4
+
5
+ const ORIGINAL = Symbol();
6
+
7
+ export default function <T extends Function>(fn: T): T;
8
+ export default function <T extends Function>(fn: T, onError: (err: Error) => void): T;
9
+ export default function <T extends Function>(fn: T, snackbarTitle: string, snackbarMessage: string): T;
10
+
11
+ /**
12
+ * Adds basic global error checking on the provided function. Should
13
+ * be used when fetching remote data. Don't use try-catch with this
14
+ * function, because it throws exceptions that our global error handler
15
+ * will catch for you.
16
+ *
17
+ * ```typescript
18
+ * const {getOrder} = useService(OrderService);
19
+ * const _getOrder = guarded(getOrder);
20
+ * const _getOrder = guarded(getOrder, err => console.error(err));
21
+ * const _getOrder = guarded(getOrder, 'Error snackbar title', 'Error snackbar message');
22
+ * ```
23
+ */
24
+ export default function <T extends Function>(fn: T, onErrorOrSnackbarTitle?: ((err: Error) => void) | string, snackbarMessage?: string): T {
25
+ if (isGuarded(fn)) {
26
+ // note(Bas): Always wrap the original function, so we don't get
27
+ // double error handling.
28
+ fn = fn[ORIGINAL];
29
+ }
30
+
31
+ const wrapped = async (...args: any[]): Promise<T> => {
32
+ try {
33
+ return await fn(...args);
34
+ } catch (err) {
35
+ if (isRequestError(err) && err.statusCode === 403) {
36
+ throw new ForbiddenException();
37
+ }
38
+
39
+ if (isUnsanctionedRequest(err)) {
40
+ throw new UnauthorizedException();
41
+ }
42
+
43
+ if (onErrorOrSnackbarTitle) {
44
+ if (typeof onErrorOrSnackbarTitle === 'function') {
45
+ onErrorOrSnackbarTitle(err as Error);
46
+ } else {
47
+ showSnackbar({
48
+ color: 'danger',
49
+ icon: 'circle-exclamation',
50
+ message: snackbarMessage,
51
+ title: onErrorOrSnackbarTitle
52
+ });
53
+ }
54
+
55
+ throw new HandledException();
56
+ }
57
+
58
+ throw err;
59
+ }
60
+ };
61
+
62
+ wrapped[ORIGINAL] = fn;
63
+
64
+ return wrapped as unknown as T;
65
+ }
66
+
67
+ function isGuarded<T extends Function>(fn: T | Guarded<T>): fn is Guarded<T> {
68
+ return ORIGINAL in fn;
69
+ }
70
+
71
+ type Guarded<T extends Function> = Function & {
72
+ readonly [ORIGINAL]: T;
73
+ };
@@ -0,0 +1,9 @@
1
+ export { default as emptyNull } from './emptyNull';
2
+ export { default as generateColorPalette } from './generateColorPalette';
3
+ export { default as guarded } from './guarded';
4
+ export { default as onError } from './onError';
5
+ export { default as onErrorSnackbar } from './onErrorSnackbar';
6
+ export { default as persistentRef } from './persistentRef';
7
+ export { default as persistentStringRef } from './persistentStringRef';
8
+ export { default as runBefore } from './runBefore';
9
+ export { default as unrefAll } from './unrefAll';
@@ -0,0 +1,5 @@
1
+ import guarded from './guarded';
2
+
3
+ export default function <T extends Function>(onError: (err: Error) => void): T {
4
+ return ((fn: T) => guarded(fn, onError)) as unknown as T;
5
+ }
@@ -0,0 +1,15 @@
1
+ import { isRequestError } from '@basmilius/http-client';
2
+ import { showSnackbar } from '@flux-ui/components';
3
+ import onError from './onError';
4
+
5
+ export default <T extends Function>() => onError<T>(err => {
6
+ if (!isRequestError(err)) {
7
+ throw err;
8
+ }
9
+
10
+ showSnackbar({
11
+ color: 'danger',
12
+ icon: 'circle-exclamation',
13
+ message: err.errorDescription
14
+ });
15
+ });
@@ -0,0 +1,21 @@
1
+ import { ref, type Ref, watch } from 'vue';
2
+
3
+ type Deserializer<T> = (value: string) => T;
4
+ type Serializer<T> = (value: T) => string;
5
+
6
+ export default function <T>(key: string, defaultValue: T, serialize: Serializer<T> = JSON.stringify, deserialize: Deserializer<T> = JSON.parse): Ref<T | null> {
7
+ const storedValue = localStorage.getItem(key);
8
+ const initialValue = storedValue ? deserialize(storedValue) : defaultValue;
9
+
10
+ const persistentRef: Ref<T | null> = ref<T>(initialValue) as Ref<T | null>;
11
+
12
+ watch(persistentRef, value => {
13
+ if (value === null || value === undefined) {
14
+ localStorage.removeItem(key);
15
+ } else {
16
+ localStorage.setItem(key, serialize(value));
17
+ }
18
+ }, {deep: true, immediate: true});
19
+
20
+ return persistentRef;
21
+ }
@@ -0,0 +1,6 @@
1
+ import type { Ref } from 'vue';
2
+ import persistentRef from './persistentRef';
3
+
4
+ export default function (key: string, defaultValue: string | null): Ref<string | null> {
5
+ return persistentRef(key, defaultValue, value => value ?? '', value => value === '' ? null : value);
6
+ }
@@ -0,0 +1,8 @@
1
+ export default function <T extends Function>(before: () => void): T {
2
+ return ((fn: T) => {
3
+ return (...args: any[]) => {
4
+ before();
5
+ return fn(...args);
6
+ };
7
+ }) as unknown as T;
8
+ }
@@ -0,0 +1,19 @@
1
+ import { type Ref, unref } from 'vue';
2
+
3
+ type UnrefAll<T extends readonly unknown[]> = {
4
+ [K in keyof T]: T[K] extends Ref<infer U> ? NonNullable<U> : T[K];
5
+ };
6
+
7
+ export default function <T extends readonly unknown[]>(...deps: T): UnrefAll<T> {
8
+ return deps
9
+ .map(unref)
10
+ .map(ensure) as UnrefAll<T>;
11
+ }
12
+
13
+ function ensure<T>(dep: T): T {
14
+ if (!dep) {
15
+ throw new Error('Dep is null or undefined.');
16
+ }
17
+
18
+ return dep;
19
+ }