@fajarmaulana/komerce-lp-helper 0.1.1 → 0.2.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/README.md CHANGED
@@ -17,38 +17,39 @@ npm install komerce-lp-helper
17
17
  A wrapper around `fetch` with support for interceptors, caching, and retries.
18
18
 
19
19
  ```typescript
20
- import { http } from "komerce-lp-helper";
20
+ import { http } from 'komerce-lp-helper'
21
21
 
22
22
  // GET request
23
- const users = await http.get("/users");
23
+ const users = await http.get('/users')
24
24
 
25
25
  // POST request with body
26
- await http.post("/users", { name: "John Doe" });
26
+ await http.post('/users', { name: 'John Doe' })
27
27
 
28
28
  // Using cache
29
- const cachedData = http.getCache("my-key");
29
+ const cachedData = http.getCache('my-key')
30
30
  ```
31
31
 
32
32
  #### `createApi`
33
33
 
34
- Creates a new API instance with built-in React hooks (`fetch`, `mutation`, `infinite`) for performing typed data fetching and mutations with progress tracking.
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.
35
36
 
36
37
  ```tsx
37
- import { createApi } from "komerce-lp-helper";
38
+ import { createApi } from 'komerce-lp-helper'
38
39
 
39
- const api = createApi({ baseURL: "/api" });
40
+ const api = createApi({ baseURL: '/api' })
40
41
 
41
42
  // Fetch
42
- const { data, isLoading, refetch } = api.fetch<User[]>("/users");
43
+ const { data, isLoading, refetch } = api.fetch<User[]>('/users')
43
44
 
44
45
  // Mutation (POST, PUT, DELETE)
45
- const { mutate, isLoading, progress } = api.mutation<User, FormData>("/users", { method: "POST" });
46
+ const { mutate, isLoading, progress } = api.mutation<User, FormData>('/users', { method: 'POST' })
46
47
 
47
48
  // Infinite Fetch
48
- const { data, fetchNextPage } = api.infinite<User[]>("/users", {
49
+ const { data, fetchNextPage } = api.infinite<User[]>('/users', {
49
50
  initialOffset: 0,
50
51
  setOffset: (lastItems, allItems, lastOffset) => (lastItems.length ? lastOffset + 1 : null),
51
- });
52
+ })
52
53
  ```
53
54
 
54
55
  ### Hooks
@@ -58,8 +59,8 @@ const { data, fetchNextPage } = api.infinite<User[]>("/users", {
58
59
  Returns a debounced version of a value. Useful for delaying expensive operations.
59
60
 
60
61
  ```typescript
61
- import { useDebounce } from "komerce-lp-helper";
62
- const debouncedSearchTerm = useDebounce(searchTerm, 500);
62
+ import { useDebounce } from 'komerce-lp-helper'
63
+ const debouncedSearchTerm = useDebounce(searchTerm, 500)
63
64
  ```
64
65
 
65
66
  #### `useDebounceFunc`
@@ -67,8 +68,8 @@ const debouncedSearchTerm = useDebounce(searchTerm, 500);
67
68
  Returns a debounced version of a callback function.
68
69
 
69
70
  ```typescript
70
- import { useDebounceFunc } from "komerce-lp-helper";
71
- const debouncedSearch = useDebounceFunc((q) => fetchResults(q), 300);
71
+ import { useDebounceFunc } from 'komerce-lp-helper'
72
+ const debouncedSearch = useDebounceFunc(q => fetchResults(q), 300)
72
73
  ```
73
74
 
74
75
  #### `useConditionalDebounce`
@@ -80,12 +81,12 @@ Conditionally executes a callback function after a specified debounce delay.
80
81
  A custom router API built on top of React Router DOM that provides easier navigation methods and state management.
81
82
 
82
83
  ```typescript
83
- import { useRouter } from "komerce-lp-helper";
84
+ import { useRouter } from 'komerce-lp-helper'
84
85
 
85
- const { push, replace, back, query, params } = useRouter();
86
+ const { push, replace, back, query, params } = useRouter()
86
87
 
87
88
  // Navigate with query params
88
- push({ pathname: "/dashboard", query: { tab: "settings" } });
89
+ push({ pathname: '/dashboard', query: { tab: 'settings' } })
89
90
  ```
90
91
 
91
92
  #### `useQueryParams`
@@ -93,8 +94,8 @@ push({ pathname: "/dashboard", query: { tab: "settings" } });
93
94
  A hook for reading and updating query parameters in the URL locally.
94
95
 
95
96
  ```typescript
96
- import { useQueryParams } from "komerce-lp-helper";
97
- const [queryObj, updateQuery] = useQueryParams<{ page: string }>();
97
+ import { useQueryParams } from 'komerce-lp-helper'
98
+ const [queryObj, updateQuery] = useQueryParams<{ page: string }>()
98
99
  ```
99
100
 
100
101
  #### `useSlider`
@@ -102,8 +103,35 @@ const [queryObj, updateQuery] = useQueryParams<{ page: string }>();
102
103
  Manages logic for custom slider components, including touch/drag support and navigation.
103
104
 
104
105
  ```typescript
105
- import { useSlider } from "komerce-lp-helper";
106
- const slider = useSlider({ data: items });
106
+ import { useSlider } from 'komerce-lp-helper'
107
+ const slider = useSlider({ data: items })
108
+ ```
109
+
110
+ #### `useForm`
111
+
112
+ Manages form fields, retrieval of values, and error handling for both named inputs and standalone fields.
113
+
114
+ ```typescript
115
+ import { useForm } from 'komerce-lp-helper'
116
+
117
+ const { form, fields, fieldsWithoutName } = useForm<{ email: string }>(['custom-input-id'])
118
+
119
+ const handleSubmit = e => {
120
+ e.preventDefault()
121
+ const data = fields()
122
+ console.log(data.email.field_value)
123
+ }
124
+ ```
125
+
126
+ #### `useSectionObserver`
127
+
128
+ Trigger animations or state changes when a section comes into view.
129
+
130
+ ```typescript
131
+ import { useSectionObserver } from 'komerce-lp-helper'
132
+
133
+ const ref = useRef(null)
134
+ useSectionObserver({ triggerRef: ref, targetId: 'target-section' })
107
135
  ```
108
136
 
109
137
  ### Utilities
@@ -154,6 +182,19 @@ Common DOM and string utilities.
154
182
  - `acronym(name)`: Generates a 2-letter acronym from a name.
155
183
  - `isNotPrimitive(value)`: Checks if a value is an object/array.
156
184
 
185
+ #### Form Validation
186
+
187
+ Helpers for validating inputs and displaying error messages.
188
+
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).
192
+
193
+ ### Components
194
+
195
+ - `Form`: A wrapper component for HTML forms.
196
+ - `LazyBackground`: Component for lazy loading background images.
197
+
157
198
  ## License
158
199
 
159
200
  MIT
@@ -0,0 +1,18 @@
1
+ import type { TFormProps } from './types';
2
+ /**
3
+ * A reusable form component that simplifies form submission handling.
4
+ *
5
+ * ### Features:
6
+ * - Prevents the default browser submit behavior.
7
+ * - Collects form values into a `FormData` object.
8
+ * - Passes the `FormData` to the `action` callback.
9
+ * - Exposes the underlying `<form>` element via `ref`.
10
+ *
11
+ * Props for the `Form` component.
12
+ *
13
+ * @property {(formData: FormData) => void} action - Function called when the form is submitted.
14
+ * @property {ComponentPropsWithRef<'form'>} - All attribute of form element.
15
+ * @property {React.ReactNode} [children] - The form’s inner content (inputs, buttons, etc.).
16
+ */
17
+ declare const Form: ({ action, ref, children, ...props }: TFormProps) => import("react/jsx-runtime").JSX.Element;
18
+ export default Form;
@@ -0,0 +1,55 @@
1
+ var __assign = (this && this.__assign) || function () {
2
+ __assign = Object.assign || function(t) {
3
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
4
+ s = arguments[i];
5
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
6
+ t[p] = s[p];
7
+ }
8
+ return t;
9
+ };
10
+ return __assign.apply(this, arguments);
11
+ };
12
+ var __rest = (this && this.__rest) || function (s, e) {
13
+ var t = {};
14
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
15
+ t[p] = s[p];
16
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
17
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
18
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
19
+ t[p[i]] = s[p[i]];
20
+ }
21
+ return t;
22
+ };
23
+ import { jsx as _jsx } from "react/jsx-runtime";
24
+ import { useImperativeHandle, useRef } from 'react';
25
+ /**
26
+ * A reusable form component that simplifies form submission handling.
27
+ *
28
+ * ### Features:
29
+ * - Prevents the default browser submit behavior.
30
+ * - Collects form values into a `FormData` object.
31
+ * - Passes the `FormData` to the `action` callback.
32
+ * - Exposes the underlying `<form>` element via `ref`.
33
+ *
34
+ * Props for the `Form` component.
35
+ *
36
+ * @property {(formData: FormData) => void} action - Function called when the form is submitted.
37
+ * @property {ComponentPropsWithRef<'form'>} - All attribute of form element.
38
+ * @property {React.ReactNode} [children] - The form’s inner content (inputs, buttons, etc.).
39
+ */
40
+ var Form = function (_a) {
41
+ var action = _a.action, ref = _a.ref, children = _a.children, props = __rest(_a, ["action", "ref", "children"]);
42
+ var innerRef = useRef(null);
43
+ useImperativeHandle(ref, function () { return innerRef.current; });
44
+ var handleSubmit = function (e) {
45
+ e.preventDefault();
46
+ var activeEl = document.activeElement;
47
+ activeEl.blur();
48
+ if (!innerRef.current)
49
+ return;
50
+ var formData = new FormData(innerRef.current);
51
+ action(formData);
52
+ };
53
+ return (_jsx("form", __assign({}, props, { ref: innerRef, onSubmit: handleSubmit, children: children })));
54
+ };
55
+ export default Form;
@@ -0,0 +1,3 @@
1
+ import type { TLazyBackgroundProps } from './types';
2
+ declare const _default: import("react").MemoExoticComponent<({ url, children, className }: TLazyBackgroundProps) => import("react/jsx-runtime").JSX.Element>;
3
+ export default _default;
@@ -0,0 +1,26 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { memo } from 'react';
3
+ import { useLazyBackground } from './hooks';
4
+ /**
5
+ * A wrapper component that lazily loads a background image when it enters the viewport.
6
+ *
7
+ * Uses the `useLazyBackground` hook to observe the element via the Intersection Observer API
8
+ * and apply the background image only when visible.
9
+ * This helps improve performance by deferring image loading until needed.
10
+ *
11
+ * Props for the `LazyBackground` component.
12
+ *
13
+ * @property {string} url - The image URL to be lazily loaded as the background.
14
+ * @property {React.ReactNode} [children] - Optional elements or content rendered inside the container.
15
+ * @property {string} [className] - Additional CSS class names applied to the outer container.
16
+ *
17
+ * @remarks
18
+ * The component renders a `<div>` with a `ref` attached for the lazy observer.
19
+ * The `url` is passed to `useLazyBackground`, which handles loading behavior internally.
20
+ */
21
+ var LazyBackground = function (_a) {
22
+ var url = _a.url, children = _a.children, className = _a.className;
23
+ var ref = useLazyBackground({ url: url }).ref;
24
+ return (_jsx("div", { ref: ref, className: className || '', children: children }));
25
+ };
26
+ export default memo(LazyBackground);
@@ -0,0 +1 @@
1
+ export * from './useLazyBackground';
@@ -0,0 +1 @@
1
+ export * from './useLazyBackground';
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Props for the `useLazyBackground` hook.
3
+ *
4
+ * @property url - The URL of the background image to load lazily.
5
+ */
6
+ export type TLazyBackground = {
7
+ url: string;
8
+ };
9
+ /**
10
+ * `useLazyBackground` is a custom React hook that applies a background image
11
+ * to a `<div>` element only when it enters the viewport (lazy-loading).
12
+ *
13
+ * @param {TLazyBackground} props - The props object containing the image URL.
14
+ */
15
+ export declare const useLazyBackground: ({ url }: TLazyBackground) => {
16
+ ref: import("react").RefObject<HTMLDivElement | null>;
17
+ };
@@ -0,0 +1,44 @@
1
+ var __read = (this && this.__read) || function (o, n) {
2
+ var m = typeof Symbol === "function" && o[Symbol.iterator];
3
+ if (!m) return o;
4
+ var i = m.call(o), r, ar = [], e;
5
+ try {
6
+ while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
7
+ }
8
+ catch (error) { e = { error: error }; }
9
+ finally {
10
+ try {
11
+ if (r && !r.done && (m = i["return"])) m.call(i);
12
+ }
13
+ finally { if (e) throw e.error; }
14
+ }
15
+ return ar;
16
+ };
17
+ import { useEffect, useRef } from 'react';
18
+ /**
19
+ * `useLazyBackground` is a custom React hook that applies a background image
20
+ * to a `<div>` element only when it enters the viewport (lazy-loading).
21
+ *
22
+ * @param {TLazyBackground} props - The props object containing the image URL.
23
+ */
24
+ export var useLazyBackground = function (_a) {
25
+ var url = _a.url;
26
+ var ref = useRef(null);
27
+ useEffect(function () {
28
+ var el = ref.current;
29
+ if (!el)
30
+ return;
31
+ var observer = new IntersectionObserver(function (_a, obs) {
32
+ var _b = __read(_a, 1), entry = _b[0];
33
+ if (entry.isIntersecting) {
34
+ el.style.backgroundImage = "url(".concat(url, ")");
35
+ obs.unobserve(el);
36
+ }
37
+ });
38
+ observer.observe(el);
39
+ return function () { return observer.disconnect(); };
40
+ }, [url]);
41
+ return {
42
+ ref: ref,
43
+ };
44
+ };
@@ -0,0 +1,3 @@
1
+ export { default as Form } from './Form';
2
+ export { default as LazyBackground } from './LazyBackground';
3
+ export type * from './types';
@@ -0,0 +1,2 @@
1
+ export { default as Form } from './Form';
2
+ export { default as LazyBackground } from './LazyBackground';
@@ -0,0 +1,40 @@
1
+ export declare const VALID_EMAIL: {
2
+ re: RegExp;
3
+ text: string;
4
+ };
5
+ export declare const DOTALPHANUM: {
6
+ re: RegExp;
7
+ text: string;
8
+ };
9
+ export declare const REPEATED_DOT: {
10
+ re: RegExp;
11
+ text: string;
12
+ };
13
+ export declare const NUMBER_ONLY: {
14
+ re: RegExp;
15
+ text: string;
16
+ };
17
+ export declare const ALPHASPACE: {
18
+ re: RegExp;
19
+ text: string;
20
+ };
21
+ export declare const ANY_UPPERCASE: {
22
+ re: RegExp;
23
+ text: string;
24
+ };
25
+ export declare const ANY_LOWERCASE: {
26
+ re: RegExp;
27
+ text: string;
28
+ };
29
+ export declare const ANY_NUMBER: {
30
+ re: RegExp;
31
+ text: string;
32
+ };
33
+ export declare const ANY_SYMBOL: {
34
+ re: RegExp;
35
+ text: string;
36
+ };
37
+ export declare const NO_SPACE: {
38
+ re: RegExp;
39
+ text: string;
40
+ };
@@ -0,0 +1,40 @@
1
+ export var VALID_EMAIL = {
2
+ re: /^(?![.])[A-Za-z0-9._-]+(?<![.])@[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*\.[A-Za-z]{2,4}$/,
3
+ text: 'masukkan email yang valid',
4
+ };
5
+ export var DOTALPHANUM = {
6
+ re: /^[a-zA-Z0-9._-]+$/,
7
+ text: " hanya boleh berisi huruf, angka, '.', '_' atau '-'",
8
+ };
9
+ export var REPEATED_DOT = {
10
+ re: /(\.\.|__|--)/,
11
+ text: ' tidak boleh berisi simbol berulang',
12
+ };
13
+ export var NUMBER_ONLY = {
14
+ re: /[^\d]/,
15
+ text: ' hanya boleh berisi angka',
16
+ };
17
+ export var ALPHASPACE = {
18
+ re: /^[A-Za-z]+(?: [A-Za-z]+)*$/,
19
+ text: ' hanya boleh berisi huruf dan spasi antar kata',
20
+ };
21
+ export var ANY_UPPERCASE = {
22
+ re: /[A-Z]/,
23
+ text: ' harus berisi minimal 1 huruf kapital',
24
+ };
25
+ export var ANY_LOWERCASE = {
26
+ re: /[a-z]/,
27
+ text: ' harus berisi minimal 1 huruf kecil',
28
+ };
29
+ export var ANY_NUMBER = {
30
+ re: /\d/,
31
+ text: ' harus berisi minimal 1 angka',
32
+ };
33
+ export var ANY_SYMBOL = {
34
+ re: /[\W+_]/,
35
+ text: ' harus berisi minimal 1 simbol',
36
+ };
37
+ export var NO_SPACE = {
38
+ re: /^\S+$/,
39
+ text: ' tidak boleh berisi spasi',
40
+ };
@@ -0,0 +1,44 @@
1
+ import type { TPrimitive } from '@/types';
2
+ /**
3
+ * Represents a single form field item, containing its value, element ID, and related helper elements.
4
+ *
5
+ * @template T - The type of the field value.
6
+ * @property {T} field_value - The current value of the field (string, number, boolean, etc.).
7
+ * @property {string} field_id - The ID attribute of the input element.
8
+ * @property {HTMLElement | null} [field_error] - The element that displays the error message (e.g. `#field_error`).
9
+ * @property {HTMLElement | null} [field_info] - The element that displays extra info (e.g. `#field_info`).
10
+ */
11
+ export type TFieldItem<T> = {
12
+ field_value: T;
13
+ field_id: string;
14
+ field_error?: HTMLElement | null;
15
+ field_info?: HTMLElement | null;
16
+ };
17
+ /**
18
+ * Represents a collection of fields keyed by their input names, where each key maps to a `TFieldItem`.
19
+ *
20
+ * @template T - The record type mapping field names to primitive values.
21
+ */
22
+ type TFields<T extends Record<string, TPrimitive>> = {
23
+ [K in keyof T & string]: TFieldItem<T[K]>;
24
+ };
25
+ /**
26
+ * A custom React hook for managing form fields and their associated elements.
27
+ *
28
+ * This hook provides access to both named form inputs (retrieved from an HTML `<form>`)
29
+ * and standalone fields that do not belong to the form (`fieldsWithoutName`).
30
+ *
31
+ * It simplifies working with dynamic forms by returning structured data that includes
32
+ * field values, IDs, and linked error/info elements.
33
+ *
34
+ * @template T - The type for form fields inside the `<form>`.
35
+ * @template U - The type for standalone fields outside the `<form>`.
36
+ *
37
+ * @param {string[]} [fieldsWithoutNameIds=[]] - A list of field IDs that exist outside the form element.
38
+ */
39
+ export declare function useForm<T extends Record<string, TPrimitive>, U extends Record<string, TPrimitive> = Record<string, TPrimitive>>(fieldsWithoutNameIds?: (keyof U & string)[]): {
40
+ form: import("react").RefObject<HTMLFormElement | null>;
41
+ fields: () => TFields<T>;
42
+ fieldsWithoutName: () => TFields<U>;
43
+ };
44
+ export {};
@@ -0,0 +1,94 @@
1
+ var __values = (this && this.__values) || function(o) {
2
+ var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
3
+ if (m) return m.call(o);
4
+ if (o && typeof o.length === "number") return {
5
+ next: function () {
6
+ if (o && i >= o.length) o = void 0;
7
+ return { value: o && o[i++], done: !o };
8
+ }
9
+ };
10
+ throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
11
+ };
12
+ var __read = (this && this.__read) || function (o, n) {
13
+ var m = typeof Symbol === "function" && o[Symbol.iterator];
14
+ if (!m) return o;
15
+ var i = m.call(o), r, ar = [], e;
16
+ try {
17
+ while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
18
+ }
19
+ catch (error) { e = { error: error }; }
20
+ finally {
21
+ try {
22
+ if (r && !r.done && (m = i["return"])) m.call(i);
23
+ }
24
+ finally { if (e) throw e.error; }
25
+ }
26
+ return ar;
27
+ };
28
+ import { useRef } from 'react';
29
+ import { getById } from '@/utils/general';
30
+ /**
31
+ * A custom React hook for managing form fields and their associated elements.
32
+ *
33
+ * This hook provides access to both named form inputs (retrieved from an HTML `<form>`)
34
+ * and standalone fields that do not belong to the form (`fieldsWithoutName`).
35
+ *
36
+ * It simplifies working with dynamic forms by returning structured data that includes
37
+ * field values, IDs, and linked error/info elements.
38
+ *
39
+ * @template T - The type for form fields inside the `<form>`.
40
+ * @template U - The type for standalone fields outside the `<form>`.
41
+ *
42
+ * @param {string[]} [fieldsWithoutNameIds=[]] - A list of field IDs that exist outside the form element.
43
+ */
44
+ export function useForm(fieldsWithoutNameIds) {
45
+ if (fieldsWithoutNameIds === void 0) { fieldsWithoutNameIds = []; }
46
+ var form = useRef(null);
47
+ var fields = function () {
48
+ var e_1, _a;
49
+ var result = {};
50
+ if (!form.current)
51
+ return result;
52
+ var formData = new FormData(form.current);
53
+ try {
54
+ for (var _b = __values(formData.entries()), _c = _b.next(); !_c.done; _c = _b.next()) {
55
+ var _d = __read(_c.value, 2), name_1 = _d[0], value = _d[1];
56
+ var key = name_1;
57
+ var input = form.current.querySelector("[name=\"".concat(key, "\"]"));
58
+ var fieldValue = value;
59
+ if ((input === null || input === void 0 ? void 0 : input.type) === 'checkbox')
60
+ fieldValue = input.checked;
61
+ result[key] = {
62
+ field_value: fieldValue,
63
+ field_id: (input === null || input === void 0 ? void 0 : input.id) || '',
64
+ field_error: form.current.querySelector("#".concat(key, "_error")),
65
+ field_info: form.current.querySelector("#".concat(key, "_info")),
66
+ };
67
+ }
68
+ }
69
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
70
+ finally {
71
+ try {
72
+ if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
73
+ }
74
+ finally { if (e_1) throw e_1.error; }
75
+ }
76
+ return result;
77
+ };
78
+ var fieldsWithoutName = function () {
79
+ var result = {};
80
+ fieldsWithoutNameIds.forEach(function (id) {
81
+ var field = getById(id);
82
+ var key = id;
83
+ var value = field.type === 'radio' || field.type === 'checkbox' ? field.checked : field.value;
84
+ result[key] = {
85
+ field_value: value,
86
+ field_id: field.id,
87
+ field_error: getById("".concat(key, "_error")),
88
+ field_info: getById("".concat(key, "_info")),
89
+ };
90
+ });
91
+ return result;
92
+ };
93
+ return { form: form, fields: fields, fieldsWithoutName: fieldsWithoutName };
94
+ }
@@ -0,0 +1,8 @@
1
+ import { type RefObject } from 'react';
2
+ type TSectionObserverProps = {
3
+ triggerRef: RefObject<HTMLElement | null>;
4
+ targetId: string;
5
+ threshold?: number;
6
+ };
7
+ export declare const useSectionObserver: ({ triggerRef, targetId, threshold }: TSectionObserverProps) => void;
8
+ export {};
@@ -0,0 +1,41 @@
1
+ var __read = (this && this.__read) || function (o, n) {
2
+ var m = typeof Symbol === "function" && o[Symbol.iterator];
3
+ if (!m) return o;
4
+ var i = m.call(o), r, ar = [], e;
5
+ try {
6
+ while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
7
+ }
8
+ catch (error) { e = { error: error }; }
9
+ finally {
10
+ try {
11
+ if (r && !r.done && (m = i["return"])) m.call(i);
12
+ }
13
+ finally { if (e) throw e.error; }
14
+ }
15
+ return ar;
16
+ };
17
+ import { useEffect } from 'react';
18
+ import { getById } from '@/utils/general';
19
+ export var useSectionObserver = function (_a) {
20
+ var triggerRef = _a.triggerRef, targetId = _a.targetId, _b = _a.threshold, threshold = _b === void 0 ? 0.8 : _b;
21
+ useEffect(function () {
22
+ var container = getById(targetId);
23
+ var trigger = triggerRef.current;
24
+ if (!trigger || !container)
25
+ return;
26
+ var observer = new IntersectionObserver(function (_a) {
27
+ var _b = __read(_a, 1), entry = _b[0];
28
+ if (entry.isIntersecting) {
29
+ container.setAttribute('data-visible', 'true');
30
+ return;
31
+ }
32
+ if (entry.boundingClientRect.top > 0) {
33
+ container.setAttribute('data-visible', 'false');
34
+ }
35
+ }, { threshold: threshold });
36
+ observer.observe(trigger);
37
+ return function () {
38
+ observer.disconnect();
39
+ };
40
+ }, [triggerRef, targetId, threshold]);
41
+ };
package/dist/index.d.ts CHANGED
@@ -1,11 +1,15 @@
1
- export * from "./constants";
2
- export * from "./hooks/debounce";
3
- export * from "./hooks/router";
4
- export * from "./hooks/slider";
5
- export * from "./types";
6
- export * from "./utils/api";
7
- export * from "./utils/cookie";
8
- export * from "./utils/file";
9
- export * from "./utils/general";
10
- export * from "./utils/local";
11
- export { default as createApi } from "./utils/useApi";
1
+ export * from './components';
2
+ export * from './constants';
3
+ export * from './hooks/debounce';
4
+ export * from './hooks/form';
5
+ export * from './hooks/router';
6
+ export * from './hooks/sectionObserver';
7
+ export * from './hooks/slider';
8
+ export * from './types';
9
+ export * from './utils/api';
10
+ export * from './utils/cookie';
11
+ export * from './utils/error-provider';
12
+ export * from './utils/file';
13
+ export * from './utils/general';
14
+ export * from './utils/local';
15
+ export { default as createApi } from './utils/useApi';
package/dist/index.js CHANGED
@@ -1,11 +1,15 @@
1
- export * from "./constants";
2
- export * from "./hooks/debounce";
3
- export * from "./hooks/router";
4
- export * from "./hooks/slider";
5
- export * from "./types";
6
- export * from "./utils/api";
7
- export * from "./utils/cookie";
8
- export * from "./utils/file";
9
- export * from "./utils/general";
10
- export * from "./utils/local";
11
- export { default as createApi } from "./utils/useApi";
1
+ export * from './components';
2
+ export * from './constants';
3
+ export * from './hooks/debounce';
4
+ export * from './hooks/form';
5
+ export * from './hooks/router';
6
+ export * from './hooks/sectionObserver';
7
+ export * from './hooks/slider';
8
+ export * from './types';
9
+ export * from './utils/api';
10
+ export * from './utils/cookie';
11
+ export * from './utils/error-provider';
12
+ export * from './utils/file';
13
+ export * from './utils/general';
14
+ export * from './utils/local';
15
+ export { default as createApi } from './utils/useApi';
@@ -1 +1,2 @@
1
- export type * from "./meta.d.ts";
1
+ export type * from './general.d.ts';
2
+ export type * from './meta.d.ts';
@@ -1,4 +1,4 @@
1
- export type TPrimitive = string | number | boolean;
1
+ import { TPrimitive } from '@/types';
2
2
  export type TProgress = {
3
3
  loaded: number;
4
4
  total: number;
@@ -38,7 +38,7 @@ export type TApiConfig = {
38
38
  /** Endpoint URL (relative or absolute) */
39
39
  url: string;
40
40
  /** HTTP method for the request */
41
- method?: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
41
+ method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
42
42
  /** Request body content */
43
43
  body?: globalThis.BodyInit;
44
44
  /** Request mode (e.g., 'cors', 'same-origin') */
package/dist/utils/api.js CHANGED
@@ -83,13 +83,13 @@ export var buildURL = function (url, params) {
83
83
  .map(function (_a) {
84
84
  var _b = __read(_a, 2), key = _b[0], value = _b[1];
85
85
  if (Array.isArray(value)) {
86
- var joined = value.map(function (v) { return encodeURIComponent(v); }).join(",");
86
+ var joined = value.map(function (v) { return encodeURIComponent(v); }).join(',');
87
87
  return "".concat(encodeURIComponent(key), "=").concat(joined);
88
88
  }
89
89
  return "".concat(encodeURIComponent(key), "=").concat(encodeURIComponent(value));
90
90
  })
91
- .join("&");
92
- return url.includes("?") ? "".concat(url, "&").concat(queryString) : "".concat(url, "?").concat(queryString);
91
+ .join('&');
92
+ return url.includes('?') ? "".concat(url, "&").concat(queryString) : "".concat(url, "?").concat(queryString);
93
93
  };
94
94
  var ApiMeta = /** @class */ (function (_super) {
95
95
  __extends(ApiMeta, _super);
@@ -106,9 +106,9 @@ var ApiInstance = /** @class */ (function () {
106
106
  function ApiInstance(options) {
107
107
  if (options === void 0) { options = {}; }
108
108
  this.interceptors = {};
109
- this.baseURL = options.baseURL || "";
109
+ this.baseURL = options.baseURL || '';
110
110
  this.defaultHeaders = options.headers || {};
111
- this.source = "komcards-".concat(options.source || "default");
111
+ this.source = "komcards-".concat(options.source || 'default');
112
112
  this.cache = new Map();
113
113
  this.maxCacheSize = options.maxCacheSize || 100;
114
114
  this.cacheAccessOrder = [];
@@ -141,7 +141,7 @@ var ApiInstance = /** @class */ (function () {
141
141
  return __generator(this, function (_a) {
142
142
  return [2 /*return*/, new Promise(function (resolve, reject) {
143
143
  var xhr = new XMLHttpRequest();
144
- var method = (init.method || "GET").toUpperCase();
144
+ var method = (init.method || 'GET').toUpperCase();
145
145
  xhr.open(method, input);
146
146
  if (init.headers) {
147
147
  var headers = new Headers(init.headers);
@@ -150,7 +150,7 @@ var ApiInstance = /** @class */ (function () {
150
150
  });
151
151
  }
152
152
  if (onUpload && xhr.upload) {
153
- xhr.upload.addEventListener("progress", function (e) {
153
+ xhr.upload.addEventListener('progress', function (e) {
154
154
  var total = e.lengthComputable ? e.total : 0;
155
155
  var percentage = e.lengthComputable ? Math.round((e.loaded / e.total) * 100) : 0;
156
156
  onUpload({
@@ -161,8 +161,8 @@ var ApiInstance = /** @class */ (function () {
161
161
  });
162
162
  }
163
163
  if (onDownload) {
164
- xhr.responseType = "blob";
165
- xhr.addEventListener("progress", function (e) {
164
+ xhr.responseType = 'blob';
165
+ xhr.addEventListener('progress', function (e) {
166
166
  var total = e.lengthComputable ? e.total : 0;
167
167
  var percentage = e.lengthComputable ? Math.round((e.loaded / e.total) * 100) : 0;
168
168
  onDownload({
@@ -173,18 +173,18 @@ var ApiInstance = /** @class */ (function () {
173
173
  });
174
174
  }
175
175
  if (init.signal) {
176
- init.signal.addEventListener("abort", function () {
176
+ init.signal.addEventListener('abort', function () {
177
177
  xhr.abort();
178
- reject(new DOMException("The operation was aborted.", "AbortError"));
178
+ reject(new DOMException('The operation was aborted.', 'AbortError'));
179
179
  });
180
180
  }
181
181
  xhr.onload = function () {
182
182
  var headers = new Headers();
183
183
  xhr
184
184
  .getAllResponseHeaders()
185
- .split("\r\n")
185
+ .split('\r\n')
186
186
  .forEach(function (line) {
187
- var parts = line.split(": ");
187
+ var parts = line.split(': ');
188
188
  if (parts.length === 2) {
189
189
  headers.append(parts[0], parts[1]);
190
190
  }
@@ -196,8 +196,8 @@ var ApiInstance = /** @class */ (function () {
196
196
  });
197
197
  resolve(response);
198
198
  };
199
- xhr.onerror = function () { return reject(new TypeError("Network request failed")); };
200
- xhr.ontimeout = function () { return reject(new TypeError("Network request timeout")); };
199
+ xhr.onerror = function () { return reject(new TypeError('Network request failed')); };
200
+ xhr.ontimeout = function () { return reject(new TypeError('Network request timeout')); };
201
201
  xhr.send(init.body);
202
202
  })];
203
203
  });
@@ -242,7 +242,7 @@ var ApiInstance = /** @class */ (function () {
242
242
  case 6: return [2 /*return*/, { value: res }];
243
243
  case 7:
244
244
  err_1 = _b.sent();
245
- if (err_1 instanceof Error && err_1.name === "AbortError") {
245
+ if (err_1 instanceof Error && err_1.name === 'AbortError') {
246
246
  throw err_1;
247
247
  }
248
248
  lastErr = err_1;
@@ -356,7 +356,7 @@ var ApiInstance = /** @class */ (function () {
356
356
  case 1:
357
357
  finalConfig = _d.sent();
358
358
  cacheKey = this.buildcacheKey(finalConfig);
359
- if (finalConfig.cache && finalConfig.cache.enabled && finalConfig.method === "GET") {
359
+ if (finalConfig.cache && finalConfig.cache.enabled && finalConfig.method === 'GET') {
360
360
  cached = this.getCacheEntry(cacheKey);
361
361
  if (cached) {
362
362
  expired = cached.ttl && Date.now() - cached.timestamp > cached.ttl;
@@ -370,13 +370,9 @@ var ApiInstance = /** @class */ (function () {
370
370
  finalURL = buildURL(url, finalConfig.params);
371
371
  isFormData = finalConfig.body instanceof FormData;
372
372
  return [4 /*yield*/, this.fetchWithRetry(finalURL, {
373
- method: finalConfig.method || "GET",
374
- headers: __assign(__assign(__assign({}, (isFormData ? {} : { "Content-Type": "application/json" })), this.defaultHeaders), finalConfig.headers),
375
- body: isFormData
376
- ? finalConfig.body
377
- : finalConfig.body
378
- ? JSON.stringify(finalConfig.body)
379
- : undefined,
373
+ method: finalConfig.method || 'GET',
374
+ headers: __assign(__assign(__assign({}, (isFormData ? {} : { 'Content-Type': 'application/json' })), this.defaultHeaders), finalConfig.headers),
375
+ body: isFormData ? finalConfig.body : finalConfig.body ? JSON.stringify(finalConfig.body) : undefined,
380
376
  }, (_b = finalConfig.retry) !== null && _b !== void 0 ? _b : 0, finalConfig.signal, finalConfig.onUpload, finalConfig.onDownload)];
381
377
  case 2:
382
378
  response = _d.sent();
@@ -404,15 +400,15 @@ var ApiInstance = /** @class */ (function () {
404
400
  errorText = _d.sent();
405
401
  throw new Error(errorText || finalResponse.statusText);
406
402
  case 8:
407
- contentType = finalResponse.headers.get("content-type") || "";
403
+ contentType = finalResponse.headers.get('content-type') || '';
408
404
  data = void 0;
409
- if (!contentType.includes("application/json")) return [3 /*break*/, 10];
405
+ if (!contentType.includes('application/json')) return [3 /*break*/, 10];
410
406
  return [4 /*yield*/, finalResponse.json()];
411
407
  case 9:
412
408
  data = (_d.sent());
413
409
  return [3 /*break*/, 14];
414
410
  case 10:
415
- if (!contentType.includes("text/")) return [3 /*break*/, 12];
411
+ if (!contentType.includes('text/')) return [3 /*break*/, 12];
416
412
  return [4 /*yield*/, finalResponse.text()];
417
413
  case 11:
418
414
  data = (_d.sent());
@@ -422,7 +418,7 @@ var ApiInstance = /** @class */ (function () {
422
418
  data = (_d.sent());
423
419
  _d.label = 14;
424
420
  case 14:
425
- if (finalConfig.cache && finalConfig.cache.enabled && finalConfig.method === "GET") {
421
+ if (finalConfig.cache && finalConfig.cache.enabled && finalConfig.method === 'GET') {
426
422
  this.setCacheEntry(cacheKey, {
427
423
  data: data,
428
424
  timestamp: Date.now(),
@@ -440,23 +436,23 @@ var ApiInstance = /** @class */ (function () {
440
436
  };
441
437
  ApiInstance.prototype.get = function (url, config) {
442
438
  if (config === void 0) { config = {}; }
443
- return this.request(__assign(__assign({}, config), { url: url, method: "GET" }));
439
+ return this.request(__assign(__assign({}, config), { url: url, method: 'GET' }));
444
440
  };
445
441
  ApiInstance.prototype.post = function (url, body, config) {
446
442
  if (config === void 0) { config = {}; }
447
- return this.request(__assign(__assign({}, config), { url: url, method: "POST", body: body }));
443
+ return this.request(__assign(__assign({}, config), { url: url, method: 'POST', body: body }));
448
444
  };
449
445
  ApiInstance.prototype.put = function (url, body, config) {
450
446
  if (config === void 0) { config = {}; }
451
- return this.request(__assign(__assign({}, config), { url: url, method: "PUT", body: body }));
447
+ return this.request(__assign(__assign({}, config), { url: url, method: 'PUT', body: body }));
452
448
  };
453
449
  ApiInstance.prototype.patch = function (url, body, config) {
454
450
  if (config === void 0) { config = {}; }
455
- return this.request(__assign(__assign({}, config), { url: url, method: "PATCH", body: body }));
451
+ return this.request(__assign(__assign({}, config), { url: url, method: 'PATCH', body: body }));
456
452
  };
457
453
  ApiInstance.prototype.delete = function (url, body, config) {
458
454
  if (config === void 0) { config = {}; }
459
- return this.request(__assign(__assign({}, config), { url: url, method: "DELETE", body: body }));
455
+ return this.request(__assign(__assign({}, config), { url: url, method: 'DELETE', body: body }));
460
456
  };
461
457
  ApiInstance.prototype.getCache = function (key) {
462
458
  var entry = this.getCacheEntry(key);
@@ -478,18 +474,10 @@ export { ApiInstance };
478
474
  var instance = new ApiInstance();
479
475
  var http = {
480
476
  get: function (url, config) { return instance.get(url, config); },
481
- post: function (url, body, config) {
482
- return instance.post(url, body, config);
483
- },
484
- put: function (url, body, config) {
485
- return instance.put(url, body, config);
486
- },
487
- patch: function (url, body, config) {
488
- return instance.patch(url, body, config);
489
- },
490
- delete: function (url, body, config) {
491
- return instance.delete(url, body, config);
492
- },
477
+ post: function (url, body, config) { return instance.post(url, body, config); },
478
+ put: function (url, body, config) { return instance.put(url, body, config); },
479
+ patch: function (url, body, config) { return instance.patch(url, body, config); },
480
+ delete: function (url, body, config) { return instance.delete(url, body, config); },
493
481
  getCache: function (key) { return instance.getCache(key); },
494
482
  setCache: function (key, data, ttl) { return instance.setCache(key, data, ttl); },
495
483
  removeCache: function (key) { return instance.removeCache(key); },
@@ -0,0 +1,67 @@
1
+ import type { TFieldItem } from '@/hooks/form';
2
+ /**
3
+ * Validates a generic input field and updates its associated error element and label styling based on defined rules.
4
+ *
5
+ * @template T - The type of the input field value.
6
+ *
7
+ * @param {TFieldItem<T> & { rules: Record<string, boolean> }} params - The field metadata and validation rules.
8
+ * @param {string} params.field_id - The ID of the input element being validated.
9
+ * @param {T} params.field_value - The current value of the input field.
10
+ * @param {HTMLElement | null} params.field_error - The error message element associated with this field.
11
+ * @param {Record<string, boolean>} params.rules - A mapping of error messages to their boolean validation results (`true` = invalid).
12
+ *
13
+ *
14
+ * @example
15
+ * ```tsx
16
+ * provideFieldError({
17
+ * field_id: 'email',
18
+ * field_value: 'invalid-email',
19
+ * field_error: document.getElementById('email_error'),
20
+ * rules: {
21
+ * 'Email is required': !email,
22
+ * 'Email format is invalid': !email.includes('@')
23
+ * }
24
+ * })
25
+ * ```
26
+ *
27
+ * @remarks
28
+ * - If a rule evaluates to `true`, its message is displayed in the error element.
29
+ * - The function automatically applies a red border style (`!border-error-main`) to the associated `<label>`.
30
+ * - When all rules pass, the error text is cleared and the border style is removed.
31
+ */
32
+ export declare const provideFieldError: <T>({ field_id, field_value, field_error, rules, }: TFieldItem<T> & {
33
+ rules: Record<string, boolean>;
34
+ }) => void;
35
+ /**
36
+ * Performs advanced password validation and updates the error message and styling accordingly.
37
+ *
38
+ * This function enforces password strength requirements such as:
39
+ * - Minimum length (8 characters)
40
+ * - Presence of uppercase letters, lowercase letters, numbers, and symbols
41
+ * - No spaces allowed
42
+ *
43
+ * It dynamically generates an error message that lists missing requirements and adjusts the label border color to indicate errors.
44
+ *
45
+ * @template T - The type of the input field value.
46
+ *
47
+ * @param {TFieldItem<T>} params - The password field metadata.
48
+ * @param {string} params.field_id - The ID of the password input field.
49
+ * @param {T} params.field_value - The current value of the password field.
50
+ * @param {HTMLElement | null} params.field_error - The element where password validation messages will appear.
51
+ *
52
+ *
53
+ * @example
54
+ * ```tsx
55
+ * providePasswordFieldError({
56
+ * field_id: 'password',
57
+ * field_value: 'weakPass',
58
+ * field_error: document.getElementById('password_error')
59
+ * })
60
+ * ```
61
+ *
62
+ * @remarks
63
+ * - Displays context-aware feedback such as “add numbers or symbols to strengthen your password.”
64
+ * - Automatically applies or removes the red border class (`!border-error-main`) on the associated `<label>`.
65
+ * - Uses predefined regex constants from `@/constants/regex` for validation checks.
66
+ */
67
+ export declare const providePasswordFieldError: <T>({ field_id, field_value, field_error }: TFieldItem<T>) => void;
@@ -0,0 +1,158 @@
1
+ var __read = (this && this.__read) || function (o, n) {
2
+ var m = typeof Symbol === "function" && o[Symbol.iterator];
3
+ if (!m) return o;
4
+ var i = m.call(o), r, ar = [], e;
5
+ try {
6
+ while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
7
+ }
8
+ catch (error) { e = { error: error }; }
9
+ finally {
10
+ try {
11
+ if (r && !r.done && (m = i["return"])) m.call(i);
12
+ }
13
+ finally { if (e) throw e.error; }
14
+ }
15
+ return ar;
16
+ };
17
+ import { ANY_LOWERCASE, ANY_NUMBER, ANY_SYMBOL, ANY_UPPERCASE, NO_SPACE } from '@/constants/regex';
18
+ import { getByAny } from './general';
19
+ /**
20
+ * Validates a generic input field and updates its associated error element and label styling based on defined rules.
21
+ *
22
+ * @template T - The type of the input field value.
23
+ *
24
+ * @param {TFieldItem<T> & { rules: Record<string, boolean> }} params - The field metadata and validation rules.
25
+ * @param {string} params.field_id - The ID of the input element being validated.
26
+ * @param {T} params.field_value - The current value of the input field.
27
+ * @param {HTMLElement | null} params.field_error - The error message element associated with this field.
28
+ * @param {Record<string, boolean>} params.rules - A mapping of error messages to their boolean validation results (`true` = invalid).
29
+ *
30
+ *
31
+ * @example
32
+ * ```tsx
33
+ * provideFieldError({
34
+ * field_id: 'email',
35
+ * field_value: 'invalid-email',
36
+ * field_error: document.getElementById('email_error'),
37
+ * rules: {
38
+ * 'Email is required': !email,
39
+ * 'Email format is invalid': !email.includes('@')
40
+ * }
41
+ * })
42
+ * ```
43
+ *
44
+ * @remarks
45
+ * - If a rule evaluates to `true`, its message is displayed in the error element.
46
+ * - The function automatically applies a red border style (`!border-error-main`) to the associated `<label>`.
47
+ * - When all rules pass, the error text is cleared and the border style is removed.
48
+ */
49
+ export var provideFieldError = function (_a) {
50
+ var field_id = _a.field_id, field_value = _a.field_value, field_error = _a.field_error, rules = _a.rules;
51
+ var error = false;
52
+ var label = getByAny("label[for=\"".concat(field_id, "\"]"));
53
+ Object.entries(rules).forEach(function (_a) {
54
+ var _b = __read(_a, 2), message = _b[0], isError = _b[1];
55
+ if (isError && (!error || !!field_value)) {
56
+ field_error.textContent = message;
57
+ error = true;
58
+ label === null || label === void 0 ? void 0 : label.classList.add('!border-error-main');
59
+ }
60
+ });
61
+ if (!error) {
62
+ field_error.textContent = '';
63
+ label === null || label === void 0 ? void 0 : label.classList.remove('!border-error-main');
64
+ }
65
+ };
66
+ /**
67
+ * Performs advanced password validation and updates the error message and styling accordingly.
68
+ *
69
+ * This function enforces password strength requirements such as:
70
+ * - Minimum length (8 characters)
71
+ * - Presence of uppercase letters, lowercase letters, numbers, and symbols
72
+ * - No spaces allowed
73
+ *
74
+ * It dynamically generates an error message that lists missing requirements and adjusts the label border color to indicate errors.
75
+ *
76
+ * @template T - The type of the input field value.
77
+ *
78
+ * @param {TFieldItem<T>} params - The password field metadata.
79
+ * @param {string} params.field_id - The ID of the password input field.
80
+ * @param {T} params.field_value - The current value of the password field.
81
+ * @param {HTMLElement | null} params.field_error - The element where password validation messages will appear.
82
+ *
83
+ *
84
+ * @example
85
+ * ```tsx
86
+ * providePasswordFieldError({
87
+ * field_id: 'password',
88
+ * field_value: 'weakPass',
89
+ * field_error: document.getElementById('password_error')
90
+ * })
91
+ * ```
92
+ *
93
+ * @remarks
94
+ * - Displays context-aware feedback such as “add numbers or symbols to strengthen your password.”
95
+ * - Automatically applies or removes the red border class (`!border-error-main`) on the associated `<label>`.
96
+ * - Uses predefined regex constants from `@/constants/regex` for validation checks.
97
+ */
98
+ export var providePasswordFieldError = function (_a) {
99
+ var _b;
100
+ var field_id = _a.field_id, field_value = _a.field_value, field_error = _a.field_error;
101
+ var error = false;
102
+ var value = field_value;
103
+ var label = getByAny("label[for=\"".concat(field_id, "\"]"));
104
+ var firstRules = {
105
+ 'password harus diisi': !value,
106
+ 'panjang password minimal adalah 8 karakter': value.length < 8,
107
+ };
108
+ Object.entries(firstRules).forEach(function (_a) {
109
+ var _b = __read(_a, 2), message = _b[0], isError = _b[1];
110
+ if (isError && (!error || !!value)) {
111
+ field_error.textContent = message;
112
+ error = true;
113
+ label === null || label === void 0 ? void 0 : label.classList.add('!border-error-main');
114
+ }
115
+ });
116
+ var nextRules = (_b = {},
117
+ _b['huruf kapital'] = !ANY_UPPERCASE.re.test(value),
118
+ _b['huruf kecil'] = !ANY_LOWERCASE.re.test(value),
119
+ _b['angka'] = !ANY_NUMBER.re.test(value),
120
+ _b['simbol'] = !ANY_SYMBOL.re.test(value),
121
+ _b);
122
+ var nextError = Object.entries(nextRules).filter(function (_a) {
123
+ var _b = __read(_a, 2), _ = _b[0], rule = _b[1];
124
+ return rule;
125
+ });
126
+ var nextMessage = 'tambahkan ' +
127
+ nextError
128
+ .map(function (_a, idx) {
129
+ var _b = __read(_a, 1), message = _b[0];
130
+ var len = nextError.length;
131
+ if (len > 1 && idx >= len - 1) {
132
+ if (len <= 2)
133
+ return " dan ".concat(message);
134
+ return ", dan ".concat(message);
135
+ }
136
+ if (idx > 0)
137
+ return ", ".concat(message);
138
+ return message;
139
+ })
140
+ .join('');
141
+ if (nextError.length > 0 && (!error || !!value)) {
142
+ field_error.textContent = nextMessage + ' untuk memperkuat passwordmu.';
143
+ if (nextError.length >= Object.keys(nextError).length - 1) {
144
+ field_error.textContent = 'password terlalu lemah. ' + nextMessage + '.';
145
+ }
146
+ error = true;
147
+ label === null || label === void 0 ? void 0 : label.classList.add('!border-error-main');
148
+ }
149
+ if (!NO_SPACE.re.test(value) && (!error || !!value)) {
150
+ field_error.textContent = "password ".concat(NO_SPACE.text);
151
+ error = true;
152
+ label === null || label === void 0 ? void 0 : label.classList.add('!border-error-main');
153
+ }
154
+ if (!error) {
155
+ field_error.textContent = '';
156
+ label === null || label === void 0 ? void 0 : label.classList.remove('!border-error-main');
157
+ }
158
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@fajarmaulana/komerce-lp-helper",
3
- "version": "0.1.1",
4
- "description": "Helper functions, hooks and utils for Komerce LP",
3
+ "version": "0.2.0",
4
+ "description": "Helper functions, hooks, and utils for Komerce LP",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "files": [
@@ -11,14 +11,30 @@
11
11
  "build": "tsc"
12
12
  },
13
13
  "peerDependencies": {
14
- "react": ">=18",
15
- "react-dom": ">=18",
16
- "react-router-dom": ">=6"
14
+ "react": ">=19",
15
+ "react-dom": ">=19",
16
+ "react-router-dom": ">=7"
17
17
  },
18
18
  "devDependencies": {
19
19
  "@changesets/cli": "^2.29.8",
20
- "@types/react": "^18.2.0",
21
- "@types/react-dom": "^18.2.0",
20
+ "@eslint/js": "^9.33.0",
21
+ "@types/react": "^19.0.8",
22
+ "@types/react-dom": "^19.0.3",
23
+ "@typescript-eslint/eslint-plugin": "^8.39.1",
24
+ "@typescript-eslint/parser": "^8.39.1",
25
+ "eslint": "^9.18.0",
26
+ "eslint-config-prettier": "^10.1.8",
27
+ "eslint-plugin-import": "^2.32.0",
28
+ "eslint-plugin-jsx-a11y": "^6.10.2",
29
+ "eslint-plugin-prettier": "^5.5.4",
30
+ "eslint-plugin-react": "^7.37.5",
31
+ "eslint-plugin-react-hooks": "^5.2.0",
32
+ "eslint-plugin-react-refresh": "^0.4.20",
33
+ "eslint-plugin-simple-import-sort": "^12.1.1",
34
+ "eslint-plugin-unused-imports": "^4.1.4",
35
+ "globals": "^15.14.0",
36
+ "prettier": "^3.6.2",
37
+ "react-router-dom": "^7.11.0",
22
38
  "typescript": "^5.2.0"
23
39
  },
24
40
  "author": "Fajar Maulana",