react-better-html 1.1.17 → 1.1.19

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/dist/index.d.ts CHANGED
@@ -11,8 +11,9 @@ import Chip from "./components/Chip";
11
11
  import InputField from "./components/InputField";
12
12
  import Dropdown, { type DropdownOption } from "./components/Dropdown";
13
13
  import ToggleInput from "./components/ToggleInput";
14
+ import Form from "./components/Form";
14
15
  import BetterHtmlProvider, { useBetterHtmlContext, useTheme, useLoader, useLoaderControls } from "./components/BetterHtmlProvider";
15
- import { usePageResize, useMediaQuery, useBooleanState, useDebounceState } from "./utils/hooks";
16
+ import { usePageResize, useMediaQuery, useBooleanState, useDebounceState, useForm } from "./utils/hooks";
16
17
  import { type OmitProps, type ExcludeOptions, type PickValue, type PartialRecord, type DeepPartialRecord, type PickAllRequired } from "./types/app";
17
18
  import { type AppConfig, type BetterHtmlConfig } from "./types/config";
18
19
  import { type AssetName, type AssetsConfig } from "./types/asset";
@@ -20,4 +21,4 @@ import { type IconName, type IconsConfig } from "./types/icon";
20
21
  import { type LoaderName, type LoaderConfig } from "./types/loader";
21
22
  import { type Color, type ColorName, type ColorTheme, type Colors, type Styles, type Theme, type ThemeConfig } from "./types/theme";
22
23
  import { isMobileDevice } from "./constants";
23
- export { BetterHtmlProvider, Div, Text, Loader, Icon, Image, Button, Divider, Modal, ModalRef, PageHolder, Chip, InputField, Dropdown, DropdownOption, ToggleInput, useBetterHtmlContext, useTheme, useLoader, useLoaderControls, usePageResize, useMediaQuery, useBooleanState, useDebounceState, OmitProps, ExcludeOptions, PickValue, PartialRecord, DeepPartialRecord, PickAllRequired, AppConfig, BetterHtmlConfig, AssetName, AssetsConfig, IconName, IconsConfig, LoaderName, LoaderConfig, Color, ColorName, ColorTheme, Colors, Styles, Theme, ThemeConfig, isMobileDevice, };
24
+ export { BetterHtmlProvider, Div, Text, Loader, Icon, Image, Button, Divider, Modal, ModalRef, PageHolder, Chip, InputField, Dropdown, DropdownOption, ToggleInput, Form, useBetterHtmlContext, useTheme, useLoader, useLoaderControls, usePageResize, useMediaQuery, useBooleanState, useDebounceState, useForm, OmitProps, ExcludeOptions, PickValue, PartialRecord, DeepPartialRecord, PickAllRequired, AppConfig, BetterHtmlConfig, AssetName, AssetsConfig, IconName, IconsConfig, LoaderName, LoaderConfig, Color, ColorName, ColorTheme, Colors, Styles, Theme, ThemeConfig, isMobileDevice, };
package/dist/index.js CHANGED
@@ -36,7 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.isMobileDevice = exports.useDebounceState = exports.useBooleanState = exports.useMediaQuery = exports.usePageResize = exports.useLoaderControls = exports.useLoader = exports.useTheme = exports.useBetterHtmlContext = exports.ToggleInput = exports.Dropdown = exports.InputField = exports.Chip = exports.PageHolder = exports.Modal = exports.Divider = exports.Button = exports.Image = exports.Icon = exports.Loader = exports.Text = exports.Div = exports.BetterHtmlProvider = void 0;
39
+ exports.isMobileDevice = exports.useForm = exports.useDebounceState = exports.useBooleanState = exports.useMediaQuery = exports.usePageResize = exports.useLoaderControls = exports.useLoader = exports.useTheme = exports.useBetterHtmlContext = exports.Form = exports.ToggleInput = exports.Dropdown = exports.InputField = exports.Chip = exports.PageHolder = exports.Modal = exports.Divider = exports.Button = exports.Image = exports.Icon = exports.Loader = exports.Text = exports.Div = exports.BetterHtmlProvider = void 0;
40
40
  const Div_1 = __importDefault(require("./components/Div"));
41
41
  exports.Div = Div_1.default;
42
42
  const Text_1 = __importDefault(require("./components/Text"));
@@ -63,6 +63,8 @@ const Dropdown_1 = __importDefault(require("./components/Dropdown"));
63
63
  exports.Dropdown = Dropdown_1.default;
64
64
  const ToggleInput_1 = __importDefault(require("./components/ToggleInput"));
65
65
  exports.ToggleInput = ToggleInput_1.default;
66
+ const Form_1 = __importDefault(require("./components/Form"));
67
+ exports.Form = Form_1.default;
66
68
  const BetterHtmlProvider_1 = __importStar(require("./components/BetterHtmlProvider"));
67
69
  exports.BetterHtmlProvider = BetterHtmlProvider_1.default;
68
70
  Object.defineProperty(exports, "useBetterHtmlContext", { enumerable: true, get: function () { return BetterHtmlProvider_1.useBetterHtmlContext; } });
@@ -74,5 +76,6 @@ Object.defineProperty(exports, "usePageResize", { enumerable: true, get: functio
74
76
  Object.defineProperty(exports, "useMediaQuery", { enumerable: true, get: function () { return hooks_1.useMediaQuery; } });
75
77
  Object.defineProperty(exports, "useBooleanState", { enumerable: true, get: function () { return hooks_1.useBooleanState; } });
76
78
  Object.defineProperty(exports, "useDebounceState", { enumerable: true, get: function () { return hooks_1.useDebounceState; } });
79
+ Object.defineProperty(exports, "useForm", { enumerable: true, get: function () { return hooks_1.useForm; } });
77
80
  const constants_1 = require("./constants");
78
81
  Object.defineProperty(exports, "isMobileDevice", { enumerable: true, get: function () { return constants_1.isMobileDevice; } });
@@ -1,5 +1,8 @@
1
- import { ComponentHoverStyle, ComponentStyle } from "../types/components";
1
+ import { ComponentHoverStyle, ComponentPropWithRef, ComponentStyle } from "../types/components";
2
2
  import { Theme } from "../types/theme";
3
+ import { OmitProps, PartialRecord } from "../types/app";
4
+ import { InputFieldProps } from "../components/InputField";
5
+ import { DropdownProps } from "../components/Dropdown";
3
6
  export declare function useStyledComponentStyles(props: ComponentStyle & ComponentHoverStyle, theme?: Theme,
4
7
  /** @default false */
5
8
  excludeProps?: boolean): {
@@ -39,3 +42,18 @@ export declare function useBooleanState(initialValue?: boolean): [
39
42
  }
40
43
  ];
41
44
  export declare function useDebounceState<Value>(initialValue: Value, delay?: number): [value: Value, debouncedValue: Value, setValue: React.Dispatch<React.SetStateAction<Value>>, isLoading: boolean];
45
+ export declare function useForm<FormFields extends Record<string, string | number | boolean | undefined>>({ defaultValues, onSubmit, validate, }: {
46
+ defaultValues: FormFields;
47
+ onSubmit?: (values: FormFields) => void | Promise<void>;
48
+ validate?: (values: FormFields) => PartialRecord<keyof FormFields, string>;
49
+ }): {
50
+ values: FormFields;
51
+ errors: Partial<Record<keyof FormFields, string>>;
52
+ isSubmitting: boolean;
53
+ setFieldValue: <FieldName extends keyof FormFields>(field: FieldName, value: FormFields[FieldName] | undefined) => void;
54
+ getInputFieldProps: <FieldName extends keyof FormFields>(field: FieldName) => ComponentPropWithRef<HTMLInputElement, InputFieldProps>;
55
+ getDropdownFieldProps: <FieldName extends keyof FormFields>(field: FieldName) => OmitProps<ComponentPropWithRef<HTMLDivElement, DropdownProps<FormFields[FieldName], unknown>>, "options">;
56
+ focusField: (field: keyof FormFields) => void;
57
+ onSubmit: (event: React.FormEvent<HTMLFormElement>) => Promise<void>;
58
+ reset: () => void;
59
+ };
@@ -8,12 +8,9 @@ exports.usePageResize = usePageResize;
8
8
  exports.useMediaQuery = useMediaQuery;
9
9
  exports.useBooleanState = useBooleanState;
10
10
  exports.useDebounceState = useDebounceState;
11
+ exports.useForm = useForm;
11
12
  const react_1 = require("react");
12
- const cssProps = Object.keys(document.documentElement.style).reduce((previousValue, currentValue) => {
13
- previousValue[currentValue.toLowerCase()] = true;
14
- previousValue[`${currentValue}Hover`.toLowerCase()] = true;
15
- return previousValue;
16
- }, {});
13
+ const css_1 = require("../constants/css");
17
14
  const cssPropsToExclude = [
18
15
  "position",
19
16
  "top",
@@ -56,7 +53,7 @@ excludeProps) {
56
53
  hoverStyle[normalKey] = props[key];
57
54
  }
58
55
  else {
59
- if (!cssProps[key.toLowerCase()])
56
+ if (!css_1.cssProps[key.toLowerCase()])
60
57
  continue;
61
58
  normalStyle[key] = props[key];
62
59
  }
@@ -91,7 +88,7 @@ function useComponentPropsWithExcludedStyle(props) {
91
88
  }
92
89
  function useComponentPropsWithoutStyle(props) {
93
90
  return (0, react_1.useMemo)(() => Object.keys(props).reduce((previousValue, currentValue) => {
94
- if (!cssProps[currentValue.toLowerCase()])
91
+ if (!css_1.cssProps[currentValue.toLowerCase()])
95
92
  previousValue[currentValue] = props[currentValue];
96
93
  return previousValue;
97
94
  }, {}), [props]);
@@ -156,3 +153,85 @@ function useDebounceState(initialValue, delay = 0.5) {
156
153
  }, [value, delay]);
157
154
  return [value, debouncedValue, setValue, isLoading];
158
155
  }
156
+ function useForm({ defaultValues, onSubmit, validate, }) {
157
+ const inputFieldRefs = (0, react_1.useRef)({});
158
+ const [inputTypes, setInputTypes] = (0, react_1.useState)({});
159
+ const [values, setValues] = (0, react_1.useState)(defaultValues);
160
+ const [errors, setErrors] = (0, react_1.useState)({});
161
+ const [isSubmitting, setIsSubmitting] = useBooleanState();
162
+ const setFieldValue = (0, react_1.useCallback)((field, value) => {
163
+ setValues((oldValue) => ({
164
+ ...oldValue,
165
+ [field]: value,
166
+ }));
167
+ setErrors((oldValue) => ({
168
+ ...oldValue,
169
+ [field]: undefined,
170
+ }));
171
+ }, []);
172
+ const getInputFieldProps = (0, react_1.useCallback)((field) => {
173
+ const type = inputTypes[field] ?? "text";
174
+ return {
175
+ value: values[field]?.toString() ?? "",
176
+ onChangeValue: (newValue) => {
177
+ const readyValue = type === "number" ? (newValue ? Number(newValue) : undefined) : newValue;
178
+ setFieldValue(field, readyValue);
179
+ },
180
+ ref: (element) => {
181
+ if (!element)
182
+ return;
183
+ inputFieldRefs.current[field] = element;
184
+ if (inputTypes[field] === undefined)
185
+ setInputTypes((oldValue) => ({
186
+ ...oldValue,
187
+ [field]: element.getAttribute("type"),
188
+ }));
189
+ },
190
+ };
191
+ }, [values, setFieldValue, inputTypes]);
192
+ const getDropdownFieldProps = (0, react_1.useCallback)((field) => {
193
+ return {
194
+ value: values[field],
195
+ onChange: (value) => {
196
+ setFieldValue(field, value);
197
+ },
198
+ errorText: errors[field],
199
+ };
200
+ }, [values, errors, setFieldValue]);
201
+ const focusField = (0, react_1.useCallback)((field) => {
202
+ inputFieldRefs.current[field]?.focus();
203
+ }, []);
204
+ const onSubmitFunction = (0, react_1.useCallback)(async (event) => {
205
+ event.preventDefault();
206
+ setIsSubmitting.setTrue();
207
+ try {
208
+ const validationErrors = validate?.(values) || {};
209
+ setErrors(validationErrors);
210
+ if (Object.keys(validationErrors).length === 0) {
211
+ await onSubmit?.(values);
212
+ }
213
+ else {
214
+ const firstErrorField = Object.keys(validationErrors)[0];
215
+ focusField(firstErrorField);
216
+ }
217
+ }
218
+ finally {
219
+ setIsSubmitting.setFalse();
220
+ }
221
+ }, [values, validate, onSubmit, focusField]);
222
+ const reset = (0, react_1.useCallback)(() => {
223
+ setValues(defaultValues);
224
+ setErrors({});
225
+ }, [defaultValues]);
226
+ return {
227
+ values,
228
+ errors,
229
+ isSubmitting,
230
+ setFieldValue,
231
+ getInputFieldProps,
232
+ getDropdownFieldProps,
233
+ focusField,
234
+ onSubmit: onSubmitFunction,
235
+ reset,
236
+ };
237
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-better-html",
3
- "version": "1.1.17",
3
+ "version": "1.1.19",
4
4
  "description": "A component library for react that is as close to plane html as possible",
5
5
  "main": "dist/index.js",
6
6
  "files": [