@ptx-showcase/utils 0.2.4 → 0.4.0-beta.1
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/README.md +48 -14
- package/dist/build-query-params/index.d.ts +21 -0
- package/dist/build-sort-param/index.d.ts +20 -0
- package/dist/chunks/handle-api-error-CMd_LxPS.esm +38 -0
- package/dist/chunks/handle-api-error-D714W9rp.cjs +1 -0
- package/dist/clean-object/index.d.ts +20 -0
- package/dist/cookies/index.cjs +1 -1
- package/dist/cookies/index.d.ts +30 -0
- package/dist/download-file/index.d.ts +22 -0
- package/dist/handle-api-error/constants/handle-api-error.constant.d.ts +2 -0
- package/dist/handle-api-error/index.cjs +1 -0
- package/dist/handle-api-error/index.d.ts +3 -0
- package/dist/handle-api-error/index.js +2 -0
- package/dist/handle-api-error/libs/handle-api-error.lib.d.ts +5 -0
- package/dist/handle-api-error/libs/init-handle-api-error.lib.d.ts +3 -0
- package/dist/handle-api-error/types/handle-api-error.type.d.ts +14 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -1
- package/dist/normalize-input/index.cjs +1 -0
- package/dist/normalize-input/index.d.ts +46 -0
- package/dist/normalize-input/index.js +14 -0
- package/package.json +25 -8
- package/dist/chunks/cookies-BayERMbH.cjs +0 -1
package/README.md
CHANGED
|
@@ -58,13 +58,13 @@ import { buildSortParam } from '@ptx-showcase/utils'
|
|
|
58
58
|
// from subpath
|
|
59
59
|
import { buildSortParam } from '@ptx-showcase/utils/build-sort-param'
|
|
60
60
|
|
|
61
|
-
buildSortParam('name')
|
|
62
|
-
buildSortParam('name', 'name')
|
|
63
|
-
buildSortParam('name', '-name')
|
|
61
|
+
buildSortParam('name') // → 'name'
|
|
62
|
+
buildSortParam('name', 'name') // → '-name'
|
|
63
|
+
buildSortParam('name', '-name') // → ''
|
|
64
64
|
|
|
65
65
|
// combining with existing sort
|
|
66
|
-
buildSortParam('age', 'name')
|
|
67
|
-
buildSortParam('age', 'age,name')
|
|
66
|
+
buildSortParam('age', 'name') // → 'age,name'
|
|
67
|
+
buildSortParam('age', 'age,name') // → '-age,name'
|
|
68
68
|
```
|
|
69
69
|
|
|
70
70
|
---
|
|
@@ -96,7 +96,7 @@ deleteCookie('token')
|
|
|
96
96
|
Default options:
|
|
97
97
|
|
|
98
98
|
| Option | Value |
|
|
99
|
-
|
|
99
|
+
| ---------- | ---------- |
|
|
100
100
|
| `expires` | 1 hour |
|
|
101
101
|
| `secure` | `true` |
|
|
102
102
|
| `sameSite` | `'Strict'` |
|
|
@@ -126,16 +126,50 @@ await downloadBlob(blob, { hash: 12345 })
|
|
|
126
126
|
|
|
127
127
|
---
|
|
128
128
|
|
|
129
|
+
### handleApiError
|
|
130
|
+
|
|
131
|
+
Parses backend error responses (including `ky`'s `HTTPError`) and reports human-readable messages through whatever notification system your project uses (`vue-sonner`, `react-hot-toast`, `console.error`, etc).
|
|
132
|
+
|
|
133
|
+
The utility is UI-agnostic: call `initHandleApiError` once at app startup with a `notify` function, then call `handleApiError` from anywhere without touching the notification library again. Without initialization, messages fall back to `console.error`.
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
// from root
|
|
137
|
+
import { handleApiError, initHandleApiError } from '@ptx-showcase/utils'
|
|
138
|
+
|
|
139
|
+
// from subpath
|
|
140
|
+
import { handleApiError, initHandleApiError } from '@ptx-showcase/utils/handle-api-error'
|
|
141
|
+
|
|
142
|
+
// Vue project (e.g. main.ts), using vue-sonner
|
|
143
|
+
import { toast } from 'vue-sonner'
|
|
144
|
+
|
|
145
|
+
initHandleApiError({ notify: (message) => toast.error(message) })
|
|
146
|
+
|
|
147
|
+
// React project (e.g. app entry), using react-hot-toast
|
|
148
|
+
import toast from 'react-hot-toast'
|
|
149
|
+
|
|
150
|
+
initHandleApiError({ notify: (message) => toast.error(message) })
|
|
151
|
+
|
|
152
|
+
// anywhere in either project
|
|
153
|
+
try {
|
|
154
|
+
await api.get('users')
|
|
155
|
+
} catch (error) {
|
|
156
|
+
handleApiError(error)
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
129
162
|
## Available modules
|
|
130
163
|
|
|
131
|
-
| Subpath
|
|
132
|
-
|
|
133
|
-
| `@ptx-showcase/utils`
|
|
134
|
-
| `@ptx-showcase/utils/build-query-params`
|
|
135
|
-
| `@ptx-showcase/utils/build-sort-param`
|
|
136
|
-
| `@ptx-showcase/utils/cookies`
|
|
137
|
-
| `@ptx-showcase/utils/download-file`
|
|
138
|
-
| `@ptx-showcase/utils/
|
|
164
|
+
| Subpath | Exports |
|
|
165
|
+
| ---------------------------------------- | ---------------------------------------------------------------- |
|
|
166
|
+
| `@ptx-showcase/utils` | all utilities |
|
|
167
|
+
| `@ptx-showcase/utils/build-query-params` | `buildQueryParams` |
|
|
168
|
+
| `@ptx-showcase/utils/build-sort-param` | `buildSortParam` |
|
|
169
|
+
| `@ptx-showcase/utils/cookies` | `setCookie`, `getCookie`, `deleteCookie`, `defaultCookieOptions` |
|
|
170
|
+
| `@ptx-showcase/utils/download-file` | `downloadBlob` |
|
|
171
|
+
| `@ptx-showcase/utils/handle-api-error` | `handleApiError`, `initHandleApiError` |
|
|
172
|
+
| `@ptx-showcase/utils/types` | `TableColumnConfig` |
|
|
139
173
|
|
|
140
174
|
---
|
|
141
175
|
|
|
@@ -1 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds a URL query string from an object, ready to be appended to a URL.
|
|
3
|
+
*
|
|
4
|
+
* - `undefined`, `null`, `''` (empty string) and empty arrays are skipped
|
|
5
|
+
* - Other values (including `0`, `false`, non-empty arrays) are stringified
|
|
6
|
+
* via `String(value)` and appended as-is — arrays become comma-separated
|
|
7
|
+
* (e.g. `[1, 2, 3]` → `h=1%2C2%2C3`), not repeated `key=value` pairs
|
|
8
|
+
* - Returns `''` (not `'?'`) when there are no params left after filtering
|
|
9
|
+
*
|
|
10
|
+
* @param params - Object whose entries become query string params
|
|
11
|
+
* @returns The query string including a leading `?`, or `''` if empty
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* buildQueryParams({ a: 1, b: '', c: null, d: [1, 2] })
|
|
16
|
+
* // => '?a=1&d=1%2C2'
|
|
17
|
+
*
|
|
18
|
+
* buildQueryParams({ a: undefined, b: '' })
|
|
19
|
+
* // => ''
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
1
22
|
export declare const buildQueryParams: <T extends object>(params: T) => string;
|
|
@@ -1 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds the next value of a comma-separated sort param string by cycling a
|
|
3
|
+
* field through ascending → descending → removed, e.g. for toggling sort on
|
|
4
|
+
* table column clicks: `"" → "name" → "-name" → ""`.
|
|
5
|
+
*
|
|
6
|
+
* Other fields already present in `prev` are left untouched and keep their
|
|
7
|
+
* relative order; a newly added field is placed at the front of the list.
|
|
8
|
+
*
|
|
9
|
+
* @param field - The field name to toggle (without the `-` prefix)
|
|
10
|
+
* @param prev - The current comma-separated sort string (e.g. `"id,-name"`), if any
|
|
11
|
+
* @returns The updated comma-separated sort string
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* buildSortParam('name') // => 'name'
|
|
16
|
+
* buildSortParam('name', 'name') // => '-name'
|
|
17
|
+
* buildSortParam('name', '-name') // => ''
|
|
18
|
+
* buildSortParam('name', 'id,-surname') // => 'name,id,-surname'
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
1
21
|
export declare const buildSortParam: (field: string, prev?: string) => string;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { isHTTPError as e } from "ky";
|
|
2
|
+
//#region src/handle-api-error/constants/handle-api-error.constant.ts
|
|
3
|
+
var t = "Something went wrong", n = /* @__PURE__ */ new Set([
|
|
4
|
+
"message",
|
|
5
|
+
"detail",
|
|
6
|
+
"error",
|
|
7
|
+
"msg"
|
|
8
|
+
]), r = (e) => typeof e == "object" && !!e && !Array.isArray(e), i = (e) => typeof e == "string" && e.trim() ? e : null, a = (e, t) => typeof e == "string" ? e.trim() ? [t ? `${t}: ${e}` : e] : [] : Array.isArray(e) ? e.flatMap((e) => a(e, t)) : r(e) ? Object.entries(e).flatMap(([e, t]) => a(t, e === "non_field_errors" ? void 0 : e)) : [], o = (e) => {
|
|
9
|
+
if (!e) return [];
|
|
10
|
+
if (typeof e == "string") return [e];
|
|
11
|
+
if (Array.isArray(e)) return e.filter((e) => typeof e == "string" && !!e.trim());
|
|
12
|
+
let t = i(e.detail) ?? i(e.message) ?? i(e.error) ?? i(e.msg);
|
|
13
|
+
return [...Object.entries(e).flatMap(([e, t]) => n.has(e) ? [] : e === "non_field_errors" || e === "errors" ? a(t) : a(t, e)), ...t ? [t] : []];
|
|
14
|
+
}, s = (e) => {
|
|
15
|
+
console.error(e);
|
|
16
|
+
}, c = (e) => {
|
|
17
|
+
s = e;
|
|
18
|
+
}, l = () => s, u = (e) => {
|
|
19
|
+
c(e.notify);
|
|
20
|
+
}, d = (n) => {
|
|
21
|
+
let r = l();
|
|
22
|
+
if (e(n)) {
|
|
23
|
+
let e = n.data, i = [...new Set(o(e))];
|
|
24
|
+
if (i.length > 0) {
|
|
25
|
+
i.forEach((e) => r(e));
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
r(t);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (n instanceof Error) {
|
|
32
|
+
r(n.message || "Something went wrong");
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
r(t);
|
|
36
|
+
};
|
|
37
|
+
//#endregion
|
|
38
|
+
export { u as n, d as t };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
let e=require("ky");var t=`Something went wrong`,n=new Set([`message`,`detail`,`error`,`msg`]),r=e=>typeof e==`object`&&!!e&&!Array.isArray(e),i=e=>typeof e==`string`&&e.trim()?e:null,a=(e,t)=>typeof e==`string`?e.trim()?[t?`${t}: ${e}`:e]:[]:Array.isArray(e)?e.flatMap(e=>a(e,t)):r(e)?Object.entries(e).flatMap(([e,t])=>a(t,e===`non_field_errors`?void 0:e)):[],o=e=>{if(!e)return[];if(typeof e==`string`)return[e];if(Array.isArray(e))return e.filter(e=>typeof e==`string`&&!!e.trim());let t=i(e.detail)??i(e.message)??i(e.error)??i(e.msg);return[...Object.entries(e).flatMap(([e,t])=>n.has(e)?[]:e===`non_field_errors`||e===`errors`?a(t):a(t,e)),...t?[t]:[]]},s=e=>{console.error(e)},c=e=>{s=e},l=()=>s,u=e=>{c(e.notify)},d=n=>{let r=l();if((0,e.isHTTPError)(n)){let e=n.data,i=[...new Set(o(e))];if(i.length>0){i.forEach(e=>r(e));return}r(t);return}if(n instanceof Error){r(n.message||`Something went wrong`);return}r(t)};Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return u}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return d}});
|
|
@@ -1,6 +1,26 @@
|
|
|
1
1
|
type EmptyValue = undefined | null | '';
|
|
2
|
+
/**
|
|
3
|
+
* Result type of {@link cleanObject} — same shape as `T`, but every property
|
|
4
|
+
* becomes optional and `undefined`/`null`/`''` are excluded from its value type,
|
|
5
|
+
* reflecting that empty values are stripped from the resulting object.
|
|
6
|
+
*/
|
|
2
7
|
export type CleanObjectResult<T extends Record<string, unknown>> = Partial<{
|
|
3
8
|
[K in keyof T]: Exclude<T[K], EmptyValue>;
|
|
4
9
|
}>;
|
|
10
|
+
/**
|
|
11
|
+
* Returns a shallow copy of `obj` with all `undefined`, `null` and `''` (empty string)
|
|
12
|
+
* properties removed. Other falsy values (`0`, `false`, empty arrays, etc.) are kept.
|
|
13
|
+
*
|
|
14
|
+
* Useful for trimming objects before sending them as query params or request payloads,
|
|
15
|
+
* where empty fields shouldn't be included.
|
|
16
|
+
*
|
|
17
|
+
* @param obj - The object to clean
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* cleanObject({ a: 1, b: '', c: null, d: false, e: undefined })
|
|
22
|
+
* // => { a: 1, d: false }
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
5
25
|
export declare const cleanObject: <T extends Record<string, unknown>>(obj: T) => CleanObjectResult<T>;
|
|
6
26
|
export {};
|
package/dist/cookies/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require("js-cookie");c=s(c,1);var l={expires:1/24,secure:!0,sameSite:`Strict`},u=(e,t,n)=>{c.default.set(e,t,{...l,...n})},d=e=>c.default.get(e),f=(e,t)=>{c.default.remove(e,t)};exports.defaultCookieOptions=l,exports.deleteCookie=f,exports.getCookie=d,exports.setCookie=u;
|
package/dist/cookies/index.d.ts
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
export type CookieOptions = Cookies.CookieAttributes;
|
|
2
|
+
/**
|
|
3
|
+
* Default options applied to every {@link setCookie} call: expires in 1 hour,
|
|
4
|
+
* `secure` and `sameSite: 'Strict'`. Can be overridden per-call via `options`.
|
|
5
|
+
*/
|
|
2
6
|
export declare const defaultCookieOptions: CookieOptions;
|
|
7
|
+
/**
|
|
8
|
+
* Sets a cookie, merging the given `options` over {@link defaultCookieOptions}.
|
|
9
|
+
*
|
|
10
|
+
* @param name - Cookie name
|
|
11
|
+
* @param value - Cookie value
|
|
12
|
+
* @param options - Overrides for the default cookie attributes (e.g. `expires`, `secure`, `sameSite`, `path`)
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* setCookie('token', '123')
|
|
17
|
+
* setCookie('token', '123', { expires: 7, secure: false })
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
3
20
|
export declare const setCookie: (name: string, value: string, options?: CookieOptions) => void;
|
|
21
|
+
/**
|
|
22
|
+
* Returns the value of the cookie with the given name, or `undefined` if it doesn't exist.
|
|
23
|
+
*
|
|
24
|
+
* @param name - Cookie name
|
|
25
|
+
*/
|
|
4
26
|
export declare const getCookie: (name: string) => string | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* Removes the cookie with the given name.
|
|
29
|
+
*
|
|
30
|
+
* @param name - Cookie name
|
|
31
|
+
* @param options - Attributes the cookie was set with (e.g. `path`, `domain`) —
|
|
32
|
+
* required if the cookie was set with non-default attributes, since removal
|
|
33
|
+
* must match them for the browser to find and delete the cookie
|
|
34
|
+
*/
|
|
5
35
|
export declare const deleteCookie: (name: string, options?: CookieOptions) => void;
|
|
@@ -1,7 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options for {@link downloadBlob} that determine the resulting filename.
|
|
3
|
+
*
|
|
4
|
+
* - `filename` — uses the given name as-is
|
|
5
|
+
* - `hash` — derives the filename from the hex-encoded SHA-1 hash of the given
|
|
6
|
+
* value, useful when the original filename is unknown or shouldn't be exposed
|
|
7
|
+
*/
|
|
1
8
|
type DownloadBlobOptions = {
|
|
2
9
|
filename: string;
|
|
3
10
|
} | {
|
|
4
11
|
hash: string | number;
|
|
5
12
|
};
|
|
13
|
+
/**
|
|
14
|
+
* Triggers a browser download of the given `Blob`, saving it under either an
|
|
15
|
+
* explicit filename or a name derived from the SHA-1 hash of a given value.
|
|
16
|
+
*
|
|
17
|
+
* @param blob - The `Blob` to download
|
|
18
|
+
* @param options - Either `{ filename }` to use an explicit name, or `{ hash }`
|
|
19
|
+
* to derive the filename from the SHA-1 hash of the given string/number
|
|
20
|
+
* @throws {Error} If `blob` is not a `Blob` instance
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* await downloadBlob(blob, { filename: 'report.pdf' })
|
|
25
|
+
* await downloadBlob(blob, { hash: userId })
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
6
28
|
export declare const downloadBlob: (blob: Blob, options: DownloadBlobOptions) => Promise<void>;
|
|
7
29
|
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../chunks/handle-api-error-D714W9rp.cjs");exports.handleApiError=e.t,exports.initHandleApiError=e.n;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { ApiErrorData, ApiErrorPrimitive } from '../types/handle-api-error.type';
|
|
2
|
+
export declare const isPlainObject: (value: unknown) => value is Record<string, unknown>;
|
|
3
|
+
export declare const toMessage: (value: unknown) => string | null;
|
|
4
|
+
export declare const extractMessages: (value: unknown, parentKey?: string) => string[];
|
|
5
|
+
export declare const getHttpErrorMessages: (errorData: ApiErrorData | ApiErrorPrimitive | undefined) => string[];
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type NotifyFn = (message: string) => void;
|
|
2
|
+
export interface HandleApiErrorConfig {
|
|
3
|
+
notify: NotifyFn;
|
|
4
|
+
}
|
|
5
|
+
export type ApiErrorPrimitive = string | string[];
|
|
6
|
+
export interface ApiErrorData {
|
|
7
|
+
detail?: string;
|
|
8
|
+
message?: string;
|
|
9
|
+
error?: string;
|
|
10
|
+
msg?: string;
|
|
11
|
+
non_field_errors?: string[];
|
|
12
|
+
errors?: unknown;
|
|
13
|
+
[key: string]: unknown;
|
|
14
|
+
}
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./build-query-params/index.cjs"),t=require("./clean-object/index.cjs"),n=require("./cookies/index.cjs"),r=require("./build-sort-param/index.cjs"),i=require("./download-file/index.cjs"),a=require("./chunks/handle-api-error-D714W9rp.cjs"),o=require("./normalize-input/index.cjs");exports.buildQueryParams=e.buildQueryParams,exports.buildSortParam=r.buildSortParam,exports.cleanObject=t.cleanObject,exports.defaultCookieOptions=n.defaultCookieOptions,exports.deleteCookie=n.deleteCookie,exports.downloadBlob=i.downloadBlob,exports.getCookie=n.getCookie,exports.handleApiError=a.t,exports.initHandleApiError=a.n,exports.normalizeDecimalInput=o.normalizeDecimalInput,exports.normalizePhoneNumberInput=o.normalizePhoneNumberInput,exports.setCookie=n.setCookie;
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -3,4 +3,6 @@ import { cleanObject as t } from "./clean-object/index.js";
|
|
|
3
3
|
import { defaultCookieOptions as n, deleteCookie as r, getCookie as i, setCookie as a } from "./cookies/index.js";
|
|
4
4
|
import { buildSortParam as o } from "./build-sort-param/index.js";
|
|
5
5
|
import { downloadBlob as s } from "./download-file/index.js";
|
|
6
|
-
|
|
6
|
+
import { n as c, t as l } from "./chunks/handle-api-error-CMd_LxPS.esm";
|
|
7
|
+
import { normalizeDecimalInput as u, normalizePhoneNumberInput as d } from "./normalize-input/index.js";
|
|
8
|
+
export { e as buildQueryParams, o as buildSortParam, t as cleanObject, n as defaultCookieOptions, r as deleteCookie, s as downloadBlob, i as getCookie, l as handleApiError, c as initHandleApiError, u as normalizeDecimalInput, d as normalizePhoneNumberInput, a as setCookie };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=(e,t=2)=>{let n=e.target,r=n.value;r=r.replace(`,`,`.`),r=r.replace(/[^0-9.]/g,``);let i=r.indexOf(`.`);i!==-1&&(r=r.slice(0,i+1)+r.slice(i+1).replace(/\./g,``)),r.startsWith(`.`)&&(r=`0`+r);let[a,o]=r.split(`.`);o!==void 0&&(r=t>0?`${a}.${o.slice(0,t)}`:a),n.value=r},t=(e,t=15)=>{let n=e.target,r=n.value.replace(/\D/g,``).slice(0,t);n.value=r?`+`+r:``};exports.normalizeDecimalInput=e,exports.normalizePhoneNumberInput=t;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sanitizes an input's value into a valid decimal number string in place,
|
|
3
|
+
* intended to be used as an `input`/`change` event handler.
|
|
4
|
+
*
|
|
5
|
+
* - Converts a comma decimal separator into a dot (`1,5` → `1.5`)
|
|
6
|
+
* - Strips any character that isn't a digit or a dot
|
|
7
|
+
* - Keeps only the first dot, removing subsequent ones (`1.2.3` → `1.23`)
|
|
8
|
+
* - Prefixes a leading dot with `0` (`.5` → `0.5`)
|
|
9
|
+
* - Truncates the decimal part to `decimals` digits (`1.2345` → `1.23`)
|
|
10
|
+
*
|
|
11
|
+
* @param e - The native `input`/`change` event from a text input
|
|
12
|
+
* @param decimals - Maximum number of digits allowed after the decimal point.
|
|
13
|
+
* Pass `0` to disallow decimals entirely (the dot is removed). Defaults to `2`.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* // Vue
|
|
18
|
+
* <input type="text" inputmode="decimal" @input="normalizeDecimalInput" />
|
|
19
|
+
*
|
|
20
|
+
* // React (wrap to access the native event)
|
|
21
|
+
* <input type="text" inputMode="decimal" onChange={(e) => normalizeDecimalInput(e.nativeEvent, 4)} />
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export declare const normalizeDecimalInput: (e: Event, decimals?: number) => void;
|
|
25
|
+
/**
|
|
26
|
+
* Sanitizes an input's value into a valid phone number string in place,
|
|
27
|
+
* intended to be used as an `input`/`change` event handler.
|
|
28
|
+
*
|
|
29
|
+
* - Strips any character that isn't a digit
|
|
30
|
+
* - Truncates to `maxDigits` digits (`898812345678` → `89881234567`)
|
|
31
|
+
* - Prefixes the result with a `+` (`89991234567` → `+89991234567`)
|
|
32
|
+
* - Leaves the value empty when there are no digits left, so the field can be cleared
|
|
33
|
+
*
|
|
34
|
+
* @param e - The native `input`/`change` event from a text input
|
|
35
|
+
* @param maxDigits - Maximum number of digits allowed. Defaults to `15` (E.164 limit).
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* // Vue
|
|
40
|
+
* <input type="text" inputmode="tel" @input="normalizePhoneNumberInput" />
|
|
41
|
+
*
|
|
42
|
+
* // React (wrap to access the native event)
|
|
43
|
+
* <input type="text" inputMode="tel" onChange={(e) => normalizePhoneNumberInput(e.nativeEvent, 11)} />
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export declare const normalizePhoneNumberInput: (e: Event, maxDigits?: number) => void;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
//#region src/normalize-input/index.ts
|
|
2
|
+
var e = (e, t = 2) => {
|
|
3
|
+
let n = e.target, r = n.value;
|
|
4
|
+
r = r.replace(",", "."), r = r.replace(/[^0-9.]/g, "");
|
|
5
|
+
let i = r.indexOf(".");
|
|
6
|
+
i !== -1 && (r = r.slice(0, i + 1) + r.slice(i + 1).replace(/\./g, "")), r.startsWith(".") && (r = "0" + r);
|
|
7
|
+
let [a, o] = r.split(".");
|
|
8
|
+
o !== void 0 && (r = t > 0 ? `${a}.${o.slice(0, t)}` : a), n.value = r;
|
|
9
|
+
}, t = (e, t = 15) => {
|
|
10
|
+
let n = e.target, r = n.value.replace(/\D/g, "").slice(0, t);
|
|
11
|
+
n.value = r ? "+" + r : "";
|
|
12
|
+
};
|
|
13
|
+
//#endregion
|
|
14
|
+
export { e as normalizeDecimalInput, t as normalizePhoneNumberInput };
|
package/package.json
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
"name": "@ptx-showcase/utils",
|
|
3
3
|
"author": "ptx-showcase",
|
|
4
4
|
"description": "Shared utilities for PTX Showcase projects",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.4.0-beta.1",
|
|
6
6
|
"private": false,
|
|
7
7
|
"type": "module",
|
|
8
8
|
"license": "MIT",
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
11
|
-
"url": "https://github.com/gibPerviy/ptx-showcase-utils"
|
|
11
|
+
"url": "git+https://github.com/gibPerviy/ptx-showcase-utils.git"
|
|
12
12
|
},
|
|
13
13
|
"homepage": "https://github.com/gibPerviy/ptx-showcase-utils#readme",
|
|
14
14
|
"bugs": {
|
|
@@ -56,6 +56,16 @@
|
|
|
56
56
|
"require": "./dist/download-file/index.cjs",
|
|
57
57
|
"types": "./dist/download-file/index.d.ts"
|
|
58
58
|
},
|
|
59
|
+
"./handle-api-error": {
|
|
60
|
+
"import": "./dist/handle-api-error/index.js",
|
|
61
|
+
"require": "./dist/handle-api-error/index.cjs",
|
|
62
|
+
"types": "./dist/handle-api-error/index.d.ts"
|
|
63
|
+
},
|
|
64
|
+
"./normalize-input": {
|
|
65
|
+
"import": "./dist/normalize-input/index.js",
|
|
66
|
+
"require": "./dist/normalize-input/index.cjs",
|
|
67
|
+
"types": "./dist/normalize-input/index.d.ts"
|
|
68
|
+
},
|
|
59
69
|
"./types": {
|
|
60
70
|
"import": "./dist/types/index.js",
|
|
61
71
|
"require": "./dist/types/index.cjs",
|
|
@@ -66,24 +76,31 @@
|
|
|
66
76
|
"dev": "vite",
|
|
67
77
|
"test": "vitest",
|
|
68
78
|
"build": "vite build",
|
|
79
|
+
"lint": "oxlint",
|
|
80
|
+
"lint:fix": "oxlint --fix",
|
|
81
|
+
"fmt": "oxfmt",
|
|
82
|
+
"fmt:check": "oxfmt --check",
|
|
83
|
+
"pre:commit": "oxfmt && oxlint && vite build",
|
|
69
84
|
"release": "bumpp"
|
|
70
85
|
},
|
|
71
86
|
"publishConfig": {
|
|
72
87
|
"access": "public"
|
|
73
88
|
},
|
|
74
89
|
"devDependencies": {
|
|
75
|
-
"@types/node": "^
|
|
90
|
+
"@types/node": "^26.1.1",
|
|
76
91
|
"bumpp": "^11.1.0",
|
|
77
|
-
"
|
|
92
|
+
"oxfmt": "^0.58.0",
|
|
93
|
+
"oxlint": "^1.73.0",
|
|
78
94
|
"typescript": "~6.0.3",
|
|
79
|
-
"vite": "^8.
|
|
80
|
-
"vite-plugin-dts": "^5.0.
|
|
81
|
-
"vitest": "^4.1.
|
|
95
|
+
"vite": "^8.1.4",
|
|
96
|
+
"vite-plugin-dts": "^5.0.3",
|
|
97
|
+
"vitest": "^4.1.10"
|
|
82
98
|
},
|
|
83
99
|
"dependencies": {
|
|
84
100
|
"@types/file-saver": "^2.0.7",
|
|
85
101
|
"@types/js-cookie": "^3.0.6",
|
|
86
102
|
"file-saver": "^2.0.5",
|
|
87
|
-
"js-cookie": "^3.0.8"
|
|
103
|
+
"js-cookie": "^3.0.8",
|
|
104
|
+
"ky": "^2.0.2"
|
|
88
105
|
}
|
|
89
106
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require("js-cookie");c=s(c,1);var l={expires:1/24,secure:!0,sameSite:`Strict`},u=(e,t,n)=>{c.default.set(e,t,{...l,...n})},d=e=>c.default.get(e),f=(e,t)=>{c.default.remove(e,t)};Object.defineProperty(exports,"i",{enumerable:!0,get:function(){return u}}),Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return f}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return d}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return l}});
|