@fajarmaulana/komerce-lp-helper 0.4.14 → 0.4.15

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 CHANGED
@@ -10,56 +10,72 @@ npm install @fajarmaulana/komerce-lp-helper
10
10
 
11
11
  ## Features
12
12
 
13
- ### HTTP Client & API Hooks
13
+ ### Components
14
14
 
15
- #### `http`
15
+ #### `Form`
16
16
 
17
- A wrapper around `fetch` with support for interceptors, caching, and retries.
17
+ A reusable form component that simplifies form submission handling by preventing default browser submit behavior,
18
+ collecting form values into a `FormData` object, and passing it to an action callback.
18
19
 
19
- ```typescript
20
- import { http } from 'komerce-lp-helper'
20
+ | Prop | Type | Description |
21
+ | ---------- | ------------------------------- | ------------------------------------------------- |
22
+ | `action` | `(formData: FormData) => void` | Function called when the form is submitted. |
23
+ | `ref` | `Ref<HTMLFormElement>` | Exposes the underlying `<form>` element. |
24
+ | `children` | `ReactNode` | The form’s inner content (inputs, buttons, etc.). |
25
+ | `...props` | `ComponentPropsWithRef<'form'>` | All standard HTML form attributes. |
21
26
 
22
- // GET request
23
- const users = await http.get('/users')
27
+ **Usage:**
24
28
 
25
- // POST request with body
26
- await http.post('/users', { name: 'John Doe' })
29
+ ```tsx
30
+ import { Form } from '@fajarmaulana/komerce-lp-helper'
27
31
 
28
- // Using cache
29
- const cachedData = http.getCache('my-key')
30
- ```
32
+ const handleSubmit = (formData: FormData) => {
33
+ console.log(formData.get('username'))
34
+ }
31
35
 
32
- #### `createApi`
36
+ ;<Form action={handleSubmit} className="my-form">
37
+ <input name="username" />
38
+ <button type="submit">Submit</button>
39
+ </Form>
40
+ ```
33
41
 
34
- Creates a new API instance with built-in React hooks (`fetch`, `mutation`, `infinite`) for performing typed data
35
- fetching and mutations with progress tracking.
42
+ #### `LazyBackground`
36
43
 
37
- ```tsx
38
- import { createApi } from 'komerce-lp-helper'
44
+ A wrapper component that lazily loads a background image when it enters the viewport using the Intersection Observer
45
+ API.
39
46
 
40
- const api = createApi({ baseURL: '/api' })
47
+ | Prop | Type | Description |
48
+ | ----------- | ----------- | ----------------------------------------------------------- |
49
+ | `url` | `string` | The image URL to be lazily loaded as the background. |
50
+ | `children` | `ReactNode` | Optional elements or content rendered inside the container. |
51
+ | `className` | `string` | Additional CSS class names applied to the outer container. |
41
52
 
42
- // Fetch
43
- const { data, isLoading, refetch } = api.fetch<User[]>('/users')
53
+ **Usage:**
44
54
 
45
- // Mutation (POST, PUT, DELETE)
46
- const { mutate, isLoading, progress } = api.mutation<User, FormData>('/users', { method: 'POST' })
47
-
48
- // Infinite Fetch
49
- const { data, fetchNextPage } = api.infinite<User[]>('/users', {
50
- initialOffset: 0,
51
- setOffset: (lastItems, allItems, lastOffset) => (lastItems.length ? lastOffset + 1 : null),
52
- })
55
+ ```tsx
56
+ import { LazyBackground } from '@fajarmaulana/komerce-lp-helper'
57
+ ;<LazyBackground url="https://example.com/bg.jpg" className="h-64 w-full">
58
+ <h1>Content inside lazy background</h1>
59
+ </LazyBackground>
53
60
  ```
54
61
 
55
62
  ### Hooks
56
63
 
57
64
  #### `useDebounce`
58
65
 
59
- Returns a debounced version of a value. Useful for delaying expensive operations.
66
+ Returns a debounced version of a value. It updates the returned value only after a specified delay has passed without
67
+ any changes to the input value.
60
68
 
61
- ```typescript
62
- import { useDebounce } from 'komerce-lp-helper'
69
+ | Parameter | Type | Default | Description |
70
+ | --------- | -------- | ---------- | ----------------------------------- |
71
+ | `value` | `T` | (Required) | The input value to debounce. |
72
+ | `delay` | `number` | `500` | The debounce delay in milliseconds. |
73
+
74
+ **Returns:** `T` - The debounced value.
75
+
76
+ **Usage:**
77
+
78
+ ```ts
63
79
  const debouncedSearchTerm = useDebounce(searchTerm, 500)
64
80
  ```
65
81
 
@@ -67,134 +83,430 @@ const debouncedSearchTerm = useDebounce(searchTerm, 500)
67
83
 
68
84
  Returns a debounced version of a callback function.
69
85
 
70
- ```typescript
71
- import { useDebounceFunc } from 'komerce-lp-helper'
86
+ | Parameter | Type | Default | Description |
87
+ | ---------- | -------------- | ---------- | ------------------------------------------------------ |
88
+ | `callback` | `T` (Function) | (Required) | The original function to debounce. |
89
+ | `delay` | `number` | (Required) | Delay in milliseconds before the callback is executed. |
90
+
91
+ **Returns:** `(...args: Parameters<T>) => void` - A debounced function.
92
+
93
+ **Usage:**
94
+
95
+ ```ts
72
96
  const debouncedSearch = useDebounceFunc(q => fetchResults(q), 300)
73
97
  ```
74
98
 
75
99
  #### `useConditionalDebounce`
76
100
 
77
- Conditionally executes a callback function after a specified debounce delay.
101
+ Conditionally executes a callback function after a specified debounce delay. Only triggers the callback if a given
102
+ condition is `true`.
78
103
 
79
- #### `useRouter`
104
+ | Parameter | Type | Default | Description |
105
+ | --------- | -------- | ------- | ----------------------------------- |
106
+ | `delay` | `number` | `500` | The debounce delay in milliseconds. |
80
107
 
81
- A custom router API built on top of React Router DOM that provides easier navigation methods and state management.
108
+ **Returns:** `(condition: boolean, callback: () => void) => void` - The function to execute.
82
109
 
83
- ```typescript
84
- import { useRouter } from 'komerce-lp-helper'
110
+ **Usage:**
111
+
112
+ ```ts
113
+ const run = useConditionalDebounce(300)
114
+ run(isValid, () => submitData())
115
+ ```
116
+
117
+ #### `useForm`
118
+
119
+ Manages form fields, retrieval of values, and error handling for both named inputs and standalone fields.
85
120
 
86
- const { push, replace, back, query, params } = useRouter()
121
+ | Parameter | Type | Default | Description |
122
+ | ---------------------- | ---------- | ------- | ------------------------------------------------------ |
123
+ | `fieldsWithoutNameIds` | `string[]` | `[]` | List of field IDs that exist outside the form element. |
87
124
 
88
- // Navigate with query params
89
- push({ pathname: '/dashboard', query: { tab: 'settings' } })
125
+ **Returns:**
126
+
127
+ | Property | Type | Description |
128
+ |---|---|---|
129
+ | `form` | `RefObject<HTMLFormElement>` | Ref to attach to the target `<form>`. |
130
+ | `fields` | `() => TFields<T>` | Function returning the values and elements of named form inputs. |
131
+ | `fieldsWithoutName` | `() => TFields<U>` | Function returning the values and elements of external inputs. |
132
+
133
+ _Note: `TFields` returns an object where keys are the field names and values are of type `TFieldItem<T>` containing
134
+ `field_value`, `field_id`, `field_error`, and `field_info`. Create a input component with small element with id
135
+ `{fieldName}_error` to show error message, and with id `{fieldName}_info` to show info message._
136
+
137
+ **Usage:**
138
+
139
+ ```tsx
140
+ const { form, fields, fieldsWithoutName } = useForm<{ email: string }>(['custom-input-id'])
141
+
142
+ const handleSubmit = e => {
143
+ e.preventDefault()
144
+ const data = fields()
145
+ console.log(data.email.field_value)
146
+ }
147
+
148
+ ;<form ref={form} onSubmit={handleSubmit}>
149
+ <input name="email" id="email" />
150
+ <span id="email_error"></span>
151
+ </form>
90
152
  ```
91
153
 
154
+ #### `useRouter`
155
+
156
+ Custom router API built on top of React Router DOM that provides easier navigation methods and state management.
157
+
158
+ **Returns:**
159
+
160
+ | Property | Type | Description |
161
+ |---|---|---|
162
+ | `push` | `IRouterPush` | Navigate to a new page (string or object with pathname, query, hash). |
163
+ | `route` | `(pathname, query?, options?) => void` | Navigate to a new page with query parameters. |
164
+ | `replace` | `IRouterPush` | Replace the current history entry. |
165
+ | `back` | `() => void` | Go back to previous page. |
166
+ | `next` | `() => void` | Go forward to next page. |
167
+ | `go` | `(num: number) => void` | Jump to specific history index. |
168
+ | `refresh` | `() => void` | Reload the current page. |
169
+ | `path` | `string` | The current pathname without query/hash. |
170
+ | `hash` | `string` | The hash portion of current URL. |
171
+ | `fullpath` | `string` | Full path including query and hash. |
172
+ | `origin` | `string` | The base URL. |
173
+ | `href` | `string` | Full URL including origin. |
174
+ | `query` | `TRouterQuery` | Parsed query parameters as object. |
175
+ | `params` | `Record<string, string>` | Current route parameters. |
176
+ | `beforePopState` | `(cb) => void` | Hook to cancel navigation if callback returns false. |
177
+
92
178
  #### `useQueryParams`
93
179
 
94
180
  A hook for reading and updating query parameters in the URL locally.
95
181
 
96
- ```typescript
97
- import { useQueryParams } from 'komerce-lp-helper'
98
- const [queryObj, updateQuery] = useQueryParams<{ page: string }>()
99
- ```
182
+ | Parameter | Type | Default | Description |
183
+ | --------------- | ------------ | ----------- | --------------------------------------------- |
184
+ | `defaultValues` | `Partial<T>` | `undefined` | Optional default values for query parameters. |
185
+
186
+ **Returns:** `[T, (updates: Partial<T>) => void]` - Tuple containing query object and updater function.
187
+
188
+ #### `useSectionObserver`
189
+
190
+ Uses the Intersection Observer API to detect when a trigger element comes into view and updates the `data-active`
191
+ attribute of a target element.
192
+
193
+ | Parameter | Type | Default | Description |
194
+ | ------------ | ------------------------ | ---------- | --------------------------------------------------- |
195
+ | `triggerRef` | `RefObject<HTMLElement>` | (Required) | Ref pointing to the element triggering observation. |
196
+ | `targetId` | `string` | (Required) | ID of the target element to toggle `data-active`. |
197
+ | `threshold` | `number` | `0.8` | Visibility threshold (0 to 1). |
100
198
 
101
199
  #### `useSlider`
102
200
 
103
201
  Manages logic for custom slider components, including touch/drag support and navigation.
104
202
 
105
- ```typescript
106
- import { useSlider } from 'komerce-lp-helper'
107
- const slider = useSlider({ data: items })
203
+ | Parameter | Type | Default | Description |
204
+ | --------------- | ------------ | ----------- | ----------------------------------------------------- |
205
+ | `data` | `any[]` | (Required) | Array of data to display. |
206
+ | `mobileOnly` | `boolean` | `false` | If true, slider only works below `mobileBound` width. |
207
+ | `infiniteSlide` | `boolean` | `false` | Allow infinite sliding. |
208
+ | `isLoading` | `boolean` | `false` | Is data still loading. |
209
+ | `mobileBound` | `number` | `640` | Screen width bound for mobile only mode. |
210
+ | `onNext` | `() => void` | `undefined` | Override the next function. |
211
+ | `onBack` | `() => void` | `undefined` | Override the back function. |
212
+
213
+ **Returns:**
214
+
215
+ | Property | Type | Description |
216
+ |---|---|---|
217
+ | `currentSlide` | `number` | The current active slide index. |
218
+ | `movement` | `number` | Current drag movement value. |
219
+ | `grab` | `boolean` | Whether the slider is currently being grabbed/dragged. |
220
+ | `disableLeftArrow` | `boolean` | Whether left arrow should be disabled. |
221
+ | `disableRightArrow`| `boolean` | Whether right arrow should be disabled. |
222
+ | `setCurrentSlide` | `(val) => void` | Manually set the slide. |
223
+ | `startSlide` | `(param) => void` | Call on mouse down / touch start. |
224
+ | `moveSlide` | `(param) => void` | Call on mouse move / touch move. |
225
+ | `endSlide` | `(param) => void` | Call on mouse up / touch end. |
226
+ | `next`, `back` | `() => void` | Navigate to next/prev slide. |
227
+
228
+ ### Utilities
229
+
230
+ #### HTTP Client & API
231
+
232
+ **`http`** A robust wrapper around `fetch` with caching, retry, and interceptors.
233
+
234
+ | Method | Parameters | Returns | Description |
235
+ | -------------------------------- | --------------------------------------------------- | -------------------------- | ----------------------------- |
236
+ | `get` | `url: string`, `config?: Omit<THttpConfig, 'body'>` | `Promise<TApiResponse<T>>` | Performs a GET request. |
237
+ | `post`, `put`, `patch`, `delete` | `url: string`, `body?: U`, `config?: THttpConfig` | `Promise<TApiResponse<T>>` | Performs a mutation request. |
238
+ | `request` | `config: TApiConfig` | `Promise<TApiResponse<T>>` | Full configuration request. |
239
+ | `getCache` | `key: string` | `T \| undefined` | Retrieves cached data. |
240
+ | `setCache` | `key: string, data: T, ttl?: number` | `void` | Manually store data in cache. |
241
+ | `removeCache` | `key: string` | `void` | Remove specific cache entry. |
242
+ | `clearCache` | | `void` | Clears all cache. |
243
+ | `create` | `options?: TApiInstanceOptions` | `IApiInstance` | Creates a new instance. |
244
+
245
+ **`createApi(options)`** Creates a new API instance with built-in React hooks for data fetching, mutation, and infinite
246
+ pagination. Returns an object with the following hooks:
247
+
248
+ **1. `fetch<T>(url, config?, enabled?)`**
249
+
250
+ | Parameter | Type | Default | Description |
251
+ |---|---|---|---|
252
+ | `url` | `string` | (Required) | The endpoint URL. Changing this automatically triggers a new request. |
253
+ | `config` | `Omit<THttpConfig, 'onUpload' \| 'onDownload'>` | `undefined` | Optional HTTP configuration (headers, params, cache, etc.). |
254
+ | `enabled` | `boolean` | `true` | Whether the fetch should run automatically on mount or when URL changes. |
255
+
256
+ **Returns:**
257
+ | Property | Type | Description |
258
+ |---|---|---|
259
+ | `data` | `T \| null` | Fetched data. |
260
+ | `error` | `unknown` | Any encountered error. |
261
+ | `isLoading`| `boolean` | Request state. |
262
+ | `cacheKey` | `string \| null` | Identifier for cache. |
263
+ | `refetch` | `() => Promise<TFetchState<T>>` | Manually trigger a fresh fetch. |
264
+ | `setData` | `(data) => void` | Manually update state data. |
265
+
266
+ **Example:**
267
+ ```tsx
268
+ const { data, isLoading, refetch } = api.fetch<User[]>('/users', { params: { status: 'active' } })
108
269
  ```
109
270
 
110
- #### `useForm`
271
+ **2. `mutation<TData, TRequest>(url, config?)`**
111
272
 
112
- Manages form fields, retrieval of values, and error handling for both named inputs and standalone fields.
273
+ | Parameter | Type | Default | Description |
274
+ |---|---|---|---|
275
+ | `url` | `string` | (Required) | Endpoint URL for the mutation. |
276
+ | `config` | `TMutationOptions` | `undefined` | Optional mutation config (method, headers, progress, etc.). |
113
277
 
114
- ```typescript
115
- import { useForm } from 'komerce-lp-helper'
278
+ **Returns:**
279
+ | Property | Type | Description |
280
+ |---|---|---|
281
+ | `mutate` | `(request?: TRequest) => Promise<TApiResponse<TData>>` | Trigger the mutation. |
282
+ | `isLoading`| `boolean` | In progress state. |
283
+ | `progress` | `TProgress \| null` | Upload/Download progress. |
116
284
 
117
- const { form, fields, fieldsWithoutName } = useForm<{ email: string }>(['custom-input-id'])
285
+ **Example:**
286
+ ```tsx
287
+ const { mutate, isLoading, progress } = api.mutation<User, FormData>('/users/upload', {
288
+ method: 'POST',
289
+ progress: 'upload'
290
+ })
118
291
 
119
- const handleSubmit = e => {
120
- e.preventDefault()
121
- const data = fields()
122
- console.log(data.email.field_value)
123
- }
292
+ // To trigger:
293
+ // await mutate(formData)
124
294
  ```
125
295
 
126
- #### `useSectionObserver`
296
+ **3. `infinite<T, TOffset>(url, options, config?, enabled?)`**
127
297
 
128
- Trigger animations or state changes when a section comes into view.
298
+ | Parameter | Type | Default | Description |
299
+ |---|---|---|---|
300
+ | `url` | `string` | (Required) | Endpoint URL. Changing this resets items and fetches from the beginning. |
301
+ | `options` | `TInfiniteFetchOptions` | (Required) | Options containing `initialOffset`, `offsetKey`, and `setOffset` logic. |
302
+ | `config` | `Omit<THttpConfig, 'onUpload' \| 'onDownload'>` | `undefined` | Optional HTTP configuration. |
303
+ | `enabled` | `boolean` | `true` | Whether the fetch should run automatically. |
129
304
 
130
- ```typescript
131
- import { useSectionObserver } from 'komerce-lp-helper'
305
+ **Returns:**
306
+ | Property | Type | Description |
307
+ |---|---|---|
308
+ | `data` | `T \| null` | Aggregated items. |
309
+ | `fetchNextPage`| `() => Promise<void>` | Trigger fetch for next offset. |
310
+ | `hasNextPage`| `boolean` | More pages available. |
311
+ | `isFetchingNextPage`| `boolean` | Fetching next page state. |
132
312
 
133
- const ref = useRef(null)
134
- useSectionObserver({ triggerRef: ref, targetId: 'target-section' })
313
+ **Example:**
314
+ ```tsx
315
+ const { data, fetchNextPage, hasNextPage } = api.infinite<User[], number>(
316
+ '/users',
317
+ {
318
+ initialOffset: 0,
319
+ offsetKey: 'page',
320
+ setOffset: (lastItems, allItems, lastOffset) => {
321
+ return lastItems.length > 0 ? lastOffset + 1 : null
322
+ }
323
+ }
324
+ )
135
325
  ```
136
326
 
137
- ### Utilities
327
+ **4. `batch<T>(initialRequests?)`**
328
+ Execute multiple API requests in parallel.
329
+
330
+ | Parameter | Type | Default | Description |
331
+ |---|---|---|---|
332
+ | `initialRequests` | `{ [K in keyof T]: string \| TApiConfig }` | `undefined` | Array of URLs or full request configurations. |
333
+
334
+ **Returns:**
335
+ | Property | Type | Description |
336
+ |---|---|---|
337
+ | `mutate` | `(overrideRequests?) => Promise<TBatchResponse<T>>` | Trigger the batch execution. |
338
+ | `isLoading`| `boolean` | Whether any request in the batch is in progress. |
339
+ | `error` | `unknown` | The first error encountered. |
340
+ | `cacheKeys`| `(string \| null)[]` | Array of cache keys for each request. |
341
+
342
+ **Example:**
343
+ ```tsx
344
+ const { mutate, isLoading } = api.batch<[User[], Post[]]>([
345
+ '/users',
346
+ { url: '/posts', method: 'GET' }
347
+ ])
348
+
349
+ // To trigger:
350
+ // const [usersResponse, postsResponse] = await mutate()
351
+ ```
352
+
353
+ #### Interceptor Setup Example
354
+
355
+ You can set up custom interceptors on an API instance to attach tokens globally, or handle specific error codes:
356
+
357
+ ```typescript
358
+ import { ApiMeta, createApi, getCookie, type TApiConfig } from '@fajarmaulana/komerce-lp-helper'
359
+
360
+ import { INTERNAL_API } from '@/constants/env'
361
+
362
+ import { COOKIE_KEY_JWT, forceLogout } from './auth'
363
+
364
+ const PUBLIC_ENDPOINTS: Record<string, string[]> = {
365
+ [INTERNAL_API.baseURL!]: ['/auth/api/v1/live-chat/verify-token'],
366
+ }
367
+
368
+ const isPublicEndpoint = (path: string, baseUrl: string) => {
369
+ const publicPaths = PUBLIC_ENDPOINTS[baseUrl]
370
+ return publicPaths && publicPaths.some(publicPath => path.startsWith(publicPath))
371
+ }
372
+
373
+ const interceptor = {
374
+ request: (config: TApiConfig) => {
375
+ if (!config.baseURL || isPublicEndpoint(config.url, config.baseURL)) return config
376
+
377
+ const token = getCookie<string>(COOKIE_KEY_JWT)
378
+ if (!token) {
379
+ forceLogout()
380
+ throw new Error('Token expired or missing')
381
+ }
382
+
383
+ return {
384
+ ...config,
385
+ headers: {
386
+ ...config.headers,
387
+ Authorization: `Bearer ${token}`,
388
+ },
389
+ }
390
+ },
391
+ error: async (error: unknown) => {
392
+ if (error instanceof ApiMeta && [401, 403].includes(error.code)) forceLogout()
393
+
394
+ const isNetworkError =
395
+ error instanceof Error &&
396
+ (error.message === 'Network Error' || (error as { code?: string }).code === 'ERR_NETWORK')
397
+
398
+ if (isNetworkError && window.location.pathname !== '/error-network') {
399
+ window.location.replace('/error-network')
400
+ }
401
+
402
+ return Promise.reject(error)
403
+ },
404
+ }
405
+
406
+ const internalApi = createApi({ ...INTERNAL_API })
407
+ internalApi.setInterceptors(interceptor)
408
+
409
+ export { internalApi }
410
+ ```
138
411
 
139
412
  #### Cookie
140
413
 
141
414
  Managed wrappers for `document.cookie`.
142
415
 
143
- - `setCookie({ key, value, maxAge })`: Sets a cookie.
144
- - `setCookies([ ... ])`: Sets multiple cookies.
145
- - `getCookie(key)`: Gets and parses a cookie value.
146
- - `getCookies([keys])`: Retrieves multiple cookies.
147
- - `removeCookie(key)`: Removes a cookie.
148
- - `removeCookies([keys])`: Removes multiple cookies.
149
- - `clearCookies()`: Clears all cookies.
416
+ | Function | Parameters | Description |
417
+ | --------------- | ------------------------------------ | ----------------------------------------- |
418
+ | `setCookie` | `{ key, value, maxAge?: number, sameSite?: Lax/Strict/None }` | Sets a cookie. Value is JSON stringified. |
419
+ | `setCookies` | `items: TCookie[]` | Sets multiple cookies. |
420
+ | `getCookie` | `key: string` | Gets and parses a cookie value. |
421
+ | `getCookies` | `keys: string[]` | Retrieves multiple cookies as an object. |
422
+ | `removeCookie` | `key: string` | Removes a cookie. |
423
+ | `removeCookies` | `keys: string[]` | Removes multiple cookies. |
424
+ | `clearCookies` | | Clears all cookies. |
150
425
 
151
426
  #### Local Storage
152
427
 
153
428
  Type-safe wrappers for `localStorage`.
154
429
 
155
- - `setLocal(key, value)`: Stores a value (JSON stringified).
156
- - `setLocals([ ... ])`: Stores multiple values.
157
- - `getLocal(key)`: Retrieves and parses a value.
158
- - `getLocals([keys])`: Retrieves multiple values.
159
- - `removeLocal(key)`: Removes an item.
160
- - `clearLocals()`: Clears local storage.
430
+ | Function | Parameters | Description |
431
+ | -------------- | ------------------------- | --------------------------------------- |
432
+ | `setLocal` | `key: string, value: T` | Stores a value (JSON stringified). |
433
+ | `setLocals` | `items: { key, value }[]` | Stores multiple values. |
434
+ | `getLocal` | `key: string` | Retrieves and parses a value. |
435
+ | `getLocals` | `keys: string[]` | Retrieves multiple values as an object. |
436
+ | `removeLocal` | `key: string` | Removes an item. |
437
+ | `removeLocals` | `keys: string[]` | Removes multiple items. |
438
+ | `clearLocals` | | Clears local storage. |
161
439
 
162
440
  #### File
163
441
 
164
442
  Helpers for file and blob manipulation.
165
443
 
166
- - `checkImage(url)`: Checks if an image URL is valid.
167
- - `convertBlob(blob)`: Converts a Blob to a Base64 string.
168
- - `extension(mimeType)`: Gets file extension from MIME type.
169
- - `filenameWithExtension(blob, prefix)`: Generates a filename.
170
- - `createDownloadAnchor(blob, options)`: Creates an anchor for downloading.
171
- - `downloadBlob(blob, filename)`: Triggers a file download.
444
+ | Function | Parameters | Returns | Description |
445
+ | ----------------------- | -------------------------------------------- | --------------------- | ------------------------------------------------------- |
446
+ | `checkImage` | `url: string` | `Promise<string>` | Checks if an image URL is valid (returns URL or empty). |
447
+ | `convertBlob` | `blob: Blob` | `Promise<string>` | Converts a Blob to a Base64 string. |
448
+ | `getExtension` | `mimeType: string` | `string` | Gets file extension from MIME type. |
449
+ | `filenameWithExtension` | `blob: Blob, prefix?: string` | `string` | Generates a filename. |
450
+ | `createDownloadAnchor` | `blob: Blob, options?: { filename, target }` | `{ anchor, blobUrl }` | Creates an anchor for downloading. |
451
+ | `downloadBlob` | `blob: Blob, filename: string` | `void` | Triggers a file download. |
172
452
 
173
453
  #### General
174
454
 
175
455
  Common DOM and string utilities.
176
456
 
177
- - `getById(id)`: Type-safe `document.getElementById`.
178
- - `getByAny(selector)`: Type-safe `document.querySelector`.
179
- - `clickById(id)`: Triggers a click on an element by ID.
180
- - `focusById(id)`: Focuses an element by ID.
181
- - `handleHashLink(hash, currentHash)`: Smooth scrolling for hash links.
182
- - `acronym(name)`: Generates a 2-letter acronym from a name.
183
- - `isNotPrimitive(value)`: Checks if a value is an object/array.
457
+ | Function | Parameters | Returns | Description |
458
+ | ---------------- | ------------------------------------------ | ---------------- | ------------------------------------------- |
459
+ | `getById` | `id: string` | `HTMLElement` | Type-safe `document.getElementById`. |
460
+ | `getByAny` | `selector: string` | `HTMLElement` | Type-safe `document.querySelector`. |
461
+ | `clickById` | `id: string` | `void` | Triggers a click on an element by ID. |
462
+ | `focusById` | `id: string` | `void` | Focuses an element by ID. |
463
+ | `handleHashLink` | `hash: string, currentHash: string, func?` | `void` | Smooth scrolling for hash links. |
464
+ | `acronym` | `name: string` | `string` | Generates a 1-2 letter acronym from a name. |
465
+ | `isNotPrimitive` | `value: unknown` | `boolean` | Checks if a value is an object/array. |
466
+ | `toWA` | `message: string, options?: { urlOnly?: boolean; phoneNumber?: string }` | `string \| void` | Get WhatsApp URL or open in new window. |
184
467
 
185
- #### Form Validation
468
+ #### Form Validation (Error Provider)
186
469
 
187
- Helpers for validating inputs and displaying error messages.
470
+ Helpers for validating inputs and displaying error messages dynamically.
188
471
 
189
- - `provideFieldError(params)`: Validates a field against a set of rules and updates the error element.
190
- - `providePasswordFieldError(params)`: Specialized validation for password strength (length, uppercase, lowercase,
191
- numbers, symbols).
472
+ **`provideFieldError(params)`** Validates a field against a set of rules, updates the target error element, and modifies
473
+ the target `<label>` border color.
192
474
 
193
- ### Components
475
+ - `params`: `{ field_id, field_value, field_error, rules }`
476
+ - `rules`: A mapping of error messages to their boolean validation results (`true` = invalid).
477
+
478
+ **Example Rules:**
479
+
480
+ ```typescript
481
+ export const VALID_EMAIL = {
482
+ re: /^(?![.])[A-Za-z0-9._-]+(?<![.])@[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*\.[A-Za-z]{2,4}$/,
483
+ text: 'masukkan email yang valid',
484
+ }
485
+
486
+ export const ALPHASPACE = {
487
+ re: /^[A-Za-z]+(?: [A-Za-z]+)*$/,
488
+ text: ' hanya boleh berisi huruf dan spasi antar kata',
489
+ }
490
+
491
+ export const REGISTER_RULES = {
492
+ emailRules: (email: string) => ({
493
+ 'email harus diisi': !email,
494
+ [VALID_EMAIL.text]: !VALID_EMAIL.re.test(email),
495
+ }),
496
+ fullnameRules: (fullname: string) => ({
497
+ 'nama harus diisi': !fullname,
498
+ 'panjang nama minimal adalah 3 karakter': fullname.length < 3,
499
+ [`nama ${ALPHASPACE.text}`]: !ALPHASPACE.re.test(fullname),
500
+ 'panjang nama maksimal adalah 40 karakter': fullname.length > 40,
501
+ }),
502
+ }
503
+ ```
504
+
505
+ **`providePasswordFieldError(params)`** Specialized validation for password strength (length, uppercase, lowercase,
506
+ numbers, symbols, no spaces).
194
507
 
195
- - `Form`: A wrapper component for HTML forms.
196
- - `LazyBackground`: Component for lazy loading background images.
508
+ - `params`: `{ field_id, field_value, field_error }`
197
509
 
198
510
  ## License
199
511
 
200
- MIT
512
+ MIT
@@ -61,9 +61,7 @@ export const removeCookie = (key) => {
61
61
  *
62
62
  * @param keys - Array of keys to remove.
63
63
  */
64
- export const removeCookies = (keys) => {
65
- keys.forEach(key => removeCookie(key));
66
- };
64
+ export const removeCookies = (keys) => keys.forEach(key => removeCookie(key));
67
65
  /**
68
66
  * Clears all cookies by setting their max age to 0.
69
67
  */
@@ -8,6 +8,12 @@ export declare const clearLocals: () => void;
8
8
  * @param key - The key of the item to remove.
9
9
  */
10
10
  export declare const removeLocal: (key: string) => void;
11
+ /**
12
+ * Removes multiple localStorage items by keys.
13
+ *
14
+ * @param keys - Array of keys to remove.
15
+ */
16
+ export declare const removeLocals: (keys: string[]) => void;
11
17
  /**
12
18
  * Sets an item in localStorage after serializing it to JSON.
13
19
  *
@@ -8,6 +8,12 @@ export const clearLocals = () => localStorage.clear();
8
8
  * @param key - The key of the item to remove.
9
9
  */
10
10
  export const removeLocal = (key) => localStorage.removeItem(key);
11
+ /**
12
+ * Removes multiple localStorage items by keys.
13
+ *
14
+ * @param keys - Array of keys to remove.
15
+ */
16
+ export const removeLocals = (keys) => keys.forEach(key => removeLocal(key));
11
17
  /**
12
18
  * Sets an item in localStorage after serializing it to JSON.
13
19
  *
@@ -193,7 +193,7 @@ export default function createApi(options = {}) {
193
193
  onDownload: shouldTrackDownload
194
194
  ? progress => {
195
195
  if (isMountedRef.current) {
196
- setState(s => ({ ...s, progress: progress }));
196
+ setState(s => ({ ...s, progress }));
197
197
  }
198
198
  }
199
199
  : undefined,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fajarmaulana/komerce-lp-helper",
3
- "version": "0.4.14",
3
+ "version": "0.4.15",
4
4
  "description": "Helper functions, hooks, and utils for Komerce LP",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",