react-better-html 1.1.10 → 1.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +28 -1
  2. package/dist/components/BetterHtmlProvider.d.ts +13 -3
  3. package/dist/components/BetterHtmlProvider.js +103 -24
  4. package/dist/components/Button.d.ts +63 -0
  5. package/dist/components/Button.js +157 -0
  6. package/dist/components/Chip.d.ts +20 -0
  7. package/dist/components/Chip.js +20 -0
  8. package/dist/components/Div.d.ts +9 -17
  9. package/dist/components/Div.js +2 -2
  10. package/dist/components/Divider.d.ts +21 -0
  11. package/dist/components/Divider.js +20 -0
  12. package/dist/components/Dropdown.d.ts +36 -0
  13. package/dist/components/Dropdown.js +157 -0
  14. package/dist/components/Icon.d.ts +13 -0
  15. package/dist/components/Icon.js +36 -0
  16. package/dist/components/Image.d.ts +18 -0
  17. package/dist/components/Image.js +44 -0
  18. package/dist/components/InputField.d.ts +33 -0
  19. package/dist/components/InputField.js +146 -0
  20. package/dist/components/Loader.d.ts +16 -1
  21. package/dist/components/Loader.js +11 -0
  22. package/dist/components/Modal.d.ts +41 -0
  23. package/dist/components/Modal.js +93 -0
  24. package/dist/components/PageHolder.d.ts +8 -0
  25. package/dist/components/PageHolder.js +15 -0
  26. package/dist/components/Text.d.ts +5 -11
  27. package/dist/components/ToggleInput.d.ts +19 -0
  28. package/dist/components/ToggleInput.js +122 -0
  29. package/dist/constants/app.d.ts +2 -0
  30. package/dist/constants/app.js +6 -0
  31. package/dist/constants/assets.d.ts +2 -0
  32. package/dist/constants/assets.js +10 -0
  33. package/dist/constants/icons.d.ts +2 -0
  34. package/dist/constants/icons.js +85 -0
  35. package/dist/constants/theme.d.ts +2 -0
  36. package/dist/constants/theme.js +46 -0
  37. package/dist/index.d.ts +13 -1
  38. package/dist/index.js +29 -1
  39. package/dist/types/app.d.ts +1 -0
  40. package/dist/types/asset.d.ts +4 -2
  41. package/dist/types/components.d.ts +3 -0
  42. package/dist/types/config.d.ts +8 -7
  43. package/dist/types/icon.d.ts +6 -3
  44. package/dist/types/loader.d.ts +3 -0
  45. package/dist/types/theme.d.ts +7 -5
  46. package/dist/utils/hooks.d.ts +34 -1
  47. package/dist/utils/hooks.js +107 -3
  48. package/package.json +1 -1
@@ -0,0 +1,157 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const jsx_runtime_1 = require("react/jsx-runtime");
7
+ const react_1 = require("react");
8
+ const hooks_1 = require("../utils/hooks");
9
+ const hooks_2 = require("../utils/hooks");
10
+ const Text_1 = __importDefault(require("./Text"));
11
+ const Div_1 = __importDefault(require("./Div"));
12
+ const InputField_1 = __importDefault(require("./InputField"));
13
+ const Icon_1 = __importDefault(require("./Icon"));
14
+ const Button_1 = __importDefault(require("./Button"));
15
+ const BetterHtmlProvider_1 = require("./BetterHtmlProvider");
16
+ const Loader_1 = __importDefault(require("./Loader"));
17
+ const DropdownComponent = (0, react_1.forwardRef)(function Dropdown({ label, errorText, infoText, required, disabled, options, value: controlledValue, placeholder = "Select an option", leftIcon, withSearch, withDebounce, debounceDelay = 0.5, debounceIsLoading, onChange, onChangeSearch, renderOption, ...props }, ref) {
18
+ const theme = (0, BetterHtmlProvider_1.useTheme)();
19
+ const dropdownHolderRef = (0, react_1.useRef)(null);
20
+ const inputRef = (0, react_1.useRef)(null);
21
+ const [isOpen, setIsOpen] = (0, hooks_1.useBooleanState)();
22
+ const [isOpenLate, setIsOpenLate] = (0, hooks_1.useBooleanState)();
23
+ const [isFocused, setIsFocused] = (0, hooks_1.useBooleanState)();
24
+ const [searchQuery, setSearchQuery] = (0, react_1.useState)("");
25
+ const [_, debouncedSearchQuery, setDebouncedSearchQuery, isLoadingDebouncedSearchQuery] = (0, hooks_2.useDebounceState)("", debounceDelay);
26
+ const [focusedOptionIndex, setFocusedOptionIndex] = (0, react_1.useState)();
27
+ const [internalValue, setInternalValue] = (0, react_1.useState)();
28
+ const value = controlledValue ?? internalValue;
29
+ const filteredOptions = (0, react_1.useMemo)(() => {
30
+ if (!searchQuery)
31
+ return options;
32
+ const query = searchQuery.toLowerCase();
33
+ return options.filter((option) => option.label.toLowerCase().includes(query) ||
34
+ option.searchValues?.some((value) => value.toLowerCase().includes(query)));
35
+ }, [options, searchQuery]);
36
+ const onKeyDownInputField = (0, react_1.useCallback)((event) => {
37
+ if (event.key === "Enter" || (!withSearch && event.key === " ")) {
38
+ event.preventDefault();
39
+ if (!disabled) {
40
+ setIsOpen.toggle();
41
+ if (isOpen && filteredOptions.length > 0 && focusedOptionIndex !== undefined) {
42
+ const option = filteredOptions[focusedOptionIndex];
43
+ if (!option.disabled) {
44
+ if (controlledValue === undefined)
45
+ setInternalValue(option.value);
46
+ onChange?.(option.value);
47
+ setIsOpen.setFalse();
48
+ inputRef.current?.blur();
49
+ setSearchQuery("");
50
+ setFocusedOptionIndex(undefined);
51
+ }
52
+ }
53
+ }
54
+ }
55
+ else if (event.key === "Escape") {
56
+ setIsOpen.setFalse();
57
+ setFocusedOptionIndex(undefined);
58
+ }
59
+ else if (event.key === "ArrowDown") {
60
+ event.preventDefault();
61
+ if (!isOpen)
62
+ setIsOpen.setTrue();
63
+ if (filteredOptions.length > 0) {
64
+ setFocusedOptionIndex((oldValue) => oldValue === undefined ? 0 : (oldValue + 1) % filteredOptions.length);
65
+ }
66
+ }
67
+ else if (event.key === "ArrowUp") {
68
+ event.preventDefault();
69
+ if (!isOpen)
70
+ setIsOpen.setTrue();
71
+ if (filteredOptions.length > 0) {
72
+ setFocusedOptionIndex((oldValue) => oldValue === undefined
73
+ ? filteredOptions.length - 1
74
+ : (oldValue - 1 + filteredOptions.length) % filteredOptions.length);
75
+ }
76
+ }
77
+ }, [disabled, withSearch, isOpen, filteredOptions, focusedOptionIndex, controlledValue, onChange]);
78
+ const onClickOption = (0, react_1.useCallback)((option) => {
79
+ if (!option.disabled) {
80
+ if (controlledValue === undefined)
81
+ setInternalValue(option.value);
82
+ onChange?.(option.value);
83
+ setIsOpen.setFalse();
84
+ inputRef.current?.blur();
85
+ setSearchQuery("");
86
+ setFocusedOptionIndex(undefined);
87
+ }
88
+ }, [onChange, controlledValue]);
89
+ const onClickClearButton = (0, react_1.useCallback)((event) => {
90
+ event.stopPropagation();
91
+ if (controlledValue === undefined)
92
+ setInternalValue(undefined);
93
+ onChange?.(undefined);
94
+ setIsOpen.setFalse();
95
+ inputRef.current?.blur();
96
+ setSearchQuery("");
97
+ setFocusedOptionIndex(undefined);
98
+ }, [controlledValue, onChange]);
99
+ const onChangeValue = (0, react_1.useCallback)((newValue) => {
100
+ setSearchQuery(newValue);
101
+ if (withDebounce)
102
+ setDebouncedSearchQuery(newValue);
103
+ else
104
+ onChangeSearch?.(newValue);
105
+ }, [withDebounce, onChangeSearch]);
106
+ const selectedOption = (0, react_1.useMemo)(() => options.find((option) => option.value === value), [options, value]);
107
+ (0, react_1.useEffect)(() => {
108
+ if (isOpen) {
109
+ setIsOpenLate.setTrue();
110
+ if (withSearch && inputRef.current)
111
+ inputRef.current.focus();
112
+ }
113
+ else {
114
+ const timeout = setTimeout(setIsOpenLate.setFalse, 0.2 * 1000);
115
+ return () => {
116
+ clearTimeout(timeout);
117
+ };
118
+ }
119
+ }, [isOpen, withSearch]);
120
+ (0, react_1.useEffect)(() => {
121
+ setFocusedOptionIndex(undefined);
122
+ }, [filteredOptions]);
123
+ (0, react_1.useEffect)(() => {
124
+ const handleClickOutside = (event) => {
125
+ if (dropdownHolderRef.current && !dropdownHolderRef.current.contains(event.target)) {
126
+ setIsOpen.setFalse();
127
+ setSearchQuery("");
128
+ setFocusedOptionIndex(undefined);
129
+ }
130
+ };
131
+ if (isOpen) {
132
+ document.addEventListener("mousedown", handleClickOutside);
133
+ }
134
+ return () => {
135
+ document.removeEventListener("mousedown", handleClickOutside);
136
+ };
137
+ }, [isOpen]);
138
+ (0, react_1.useEffect)(() => {
139
+ if (!withDebounce)
140
+ return;
141
+ onChangeSearch?.(debouncedSearchQuery);
142
+ }, [withDebounce, onChangeSearch, debouncedSearchQuery]);
143
+ const displayValue = withSearch && isFocused ? searchQuery : selectedOption?.label ?? "";
144
+ const withClearButton = isOpen && selectedOption;
145
+ return ((0, jsx_runtime_1.jsx)(Div_1.default.column, { position: "relative", userSelect: "none", ...props, ref: dropdownHolderRef, children: (0, jsx_runtime_1.jsxs)(Div_1.default.row, { position: "relative", width: "100%", children: [(0, jsx_runtime_1.jsx)(InputField_1.default, { label: label, errorText: errorText, infoText: infoText, required: required, disabled: disabled, readOnly: !withSearch, value: displayValue, placeholder: withSearch ? (selectedOption ? selectedOption.label : placeholder) : placeholder, leftIcon: leftIcon, className: `react-better-html-dropdown${isOpen ? " react-better-html-dropdown-open" : ""}`, zIndex: isOpen || isOpenLate ? 1001 : undefined, onClick: !disabled ? setIsOpen.toggle : undefined, onFocus: setIsFocused.setTrue, onBlur: setIsFocused.setFalse, onKeyDown: onKeyDownInputField, onChangeValue: withSearch ? onChangeValue : undefined, insideInputFieldComponent: (0, jsx_runtime_1.jsx)(Div_1.default, { position: "absolute", top: "100%", left: 0, width: "100%", maxHeight: 300, backgroundColor: theme.colors.backgroundContent, border: `1px solid ${isFocused ? theme.colors.primary : theme.colors.border}`, borderTop: "none", borderBottomLeftRadius: theme.styles.borderRadius, borderBottomRightRadius: theme.styles.borderRadius, boxShadow: "0px 10px 20px #00000020", zIndex: 1000, overflowY: "auto", opacity: !isOpen ? 0 : undefined, pointerEvents: !isOpen ? "none" : undefined, transform: `translateY(${!isOpen ? -10 : 0}px)`, transition: theme.styles.transition, role: "listbox", "aria-label": label, children: isLoadingDebouncedSearchQuery || debounceIsLoading ? ((0, jsx_runtime_1.jsx)(Div_1.default, { padding: `${theme.styles.space / 2}px ${theme.styles.space + theme.styles.gap}px`, children: (0, jsx_runtime_1.jsx)(Loader_1.default.text, {}) })) : filteredOptions.length ? (filteredOptions.map((option, index) => {
146
+ const isSelected = option.value === value;
147
+ const isDisabled = option.disabled;
148
+ const isFocused = index === focusedOptionIndex;
149
+ return ((0, jsx_runtime_1.jsx)(Div_1.default, { color: isDisabled
150
+ ? theme.colors.textSecondary + "80"
151
+ : isSelected
152
+ ? theme.colors.base
153
+ : theme.colors.textPrimary, backgroundColor: isSelected ? theme.colors.primary : theme.colors.backgroundContent, filter: isFocused ? (isDisabled ? "brightness(0.95)" : "brightness(0.9)") : undefined, filterHover: focusedOptionIndex === undefined && !isDisabled ? "brightness(0.9)" : undefined, cursor: isDisabled ? "not-allowed" : "pointer", padding: `${theme.styles.space / 2}px ${theme.styles.space + theme.styles.gap}px`, value: option, onClickWithValue: onClickOption, onMouseMove: () => setFocusedOptionIndex(undefined), role: "option", "aria-selected": isSelected, "aria-disabled": isDisabled, children: renderOption ? renderOption(option, index) : (0, jsx_runtime_1.jsx)(Text_1.default, { children: option.label }) }, JSON.stringify(option)));
154
+ })) : ((0, jsx_runtime_1.jsx)(Div_1.default, { padding: `${theme.styles.space / 2}px ${theme.styles.space + theme.styles.gap}px`, children: (0, jsx_runtime_1.jsx)(Text_1.default.unknown, { textAlign: "left", children: "No options" }) })) }), role: "combobox", "aria-expanded": isOpen, "aria-controls": "dropdown-list", "aria-haspopup": "listbox", "aria-label": label, ref: inputRef }), (0, jsx_runtime_1.jsxs)(Div_1.default.row, { position: "absolute", top: 46 / 2 + (label ? 16 + theme.styles.gap / 2 : 0), right: theme.styles.space + 1, alignItems: "center", gap: theme.styles.gap, transform: "translateY(-50%)", pointerEvents: "none", filter: disabled ? "brightness(0.9)" : undefined, opacity: disabled ? 0.6 : undefined, zIndex: isOpen || isOpenLate ? 1001 : undefined, children: [(0, jsx_runtime_1.jsx)(Button_1.default.icon, { icon: "XMark", position: "relative", size: 10, iconSize: 14, opacity: !withClearButton ? 0 : undefined, pointerEvents: withClearButton ? "all" : undefined, onClick: onClickClearButton, disabled: !withClearButton }), (0, jsx_runtime_1.jsx)(Icon_1.default, { name: "chevronDown", position: "relative", size: 16, color: theme.colors.textSecondary, transform: `rotate(${isOpen ? 180 : 0}deg)`, transition: theme.styles.transition, pointerEvents: "none" })] })] }) }));
155
+ });
156
+ const Dropdown = (0, react_1.memo)(DropdownComponent);
157
+ exports.default = Dropdown;
@@ -0,0 +1,13 @@
1
+ import { AnyOtherString, OmitProps } from "../types/app";
2
+ import { ComponentHoverStyle, ComponentPropWithRef, ComponentStyle } from "../types/components";
3
+ import { IconName } from "../types/icon";
4
+ type IconProps = {
5
+ name: IconName | AnyOtherString;
6
+ /** @default 16 */
7
+ size?: number;
8
+ } & OmitProps<React.ComponentProps<"svg">, "style" | "width" | "height" | "viewBox" | "fill" | "xmlns"> & ComponentStyle & ComponentHoverStyle;
9
+ type IconComponent = {
10
+ (props: ComponentPropWithRef<SVGSVGElement, IconProps>): React.ReactElement;
11
+ };
12
+ declare const _default: IconComponent;
13
+ export default _default;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const jsx_runtime_1 = require("react/jsx-runtime");
7
+ const react_1 = require("react");
8
+ const react_2 = require("react");
9
+ const styled_components_1 = __importDefault(require("styled-components"));
10
+ const hooks_1 = require("../utils/hooks");
11
+ const BetterHtmlProvider_1 = require("./BetterHtmlProvider");
12
+ const IconElement = styled_components_1.default.svg.withConfig({
13
+ shouldForwardProp: (prop) => !["normalStyle", "hoverStyle"].includes(prop),
14
+ }) `
15
+ ${(props) => props.normalStyle}
16
+
17
+ &:hover {
18
+ ${(props) => props.hoverStyle}
19
+ }
20
+ `;
21
+ const Icon = (0, react_2.forwardRef)(function Icon({ name, size = 16, ...props }, ref) {
22
+ const theme = (0, BetterHtmlProvider_1.useTheme)();
23
+ const { icons } = (0, BetterHtmlProvider_1.useBetterHtmlContext)();
24
+ const styledComponentStyles = (0, hooks_1.useStyledComponentStyles)(props, theme);
25
+ const dataProps = (0, hooks_1.useComponentPropsWithPrefix)(props, "data");
26
+ const ariaProps = (0, hooks_1.useComponentPropsWithPrefix)(props, "aria");
27
+ const restProps = (0, hooks_1.useComponentPropsWithoutStyle)(props);
28
+ const svgColor = props.color ?? theme.colors.textPrimary;
29
+ (0, react_2.useEffect)(() => {
30
+ if (!icons[name])
31
+ console.warn(`The icon \`${name}\` you are trying to use does not exist. Make sure to add it to the \`icons\` object in \`<BetterHtmlProvider>\` config value prop.`);
32
+ }, [icons, name]);
33
+ return ((0, jsx_runtime_1.jsx)(IconElement, { width: size, height: size, viewBox: `0 0 ${icons[name]?.width ?? 0} ${icons[name]?.height ?? 0}`, fill: "none", xmlns: "http://www.w3.org/2000/svg", ...styledComponentStyles, ...dataProps, ...ariaProps, ...restProps, ref: ref, children: icons[name]?.paths.map((path) => ((0, react_1.createElement)("path", { ...path, fill: path.type === "fill" ? svgColor : undefined, stroke: path.type === "stroke" ? svgColor : undefined, key: path.d }))) }));
34
+ });
35
+ const MemoizedIcon = (0, react_2.memo)(Icon);
36
+ exports.default = { Icon: MemoizedIcon }.Icon;
@@ -0,0 +1,18 @@
1
+ import { AnyOtherString, OmitProps } from "../types/app";
2
+ import { ComponentHoverStyle, ComponentPropWithRef, ComponentStyle } from "../types/components";
3
+ import { AssetName } from "../types/asset";
4
+ type ImageProps = {
5
+ name?: AssetName | AnyOtherString;
6
+ } & OmitProps<React.ComponentProps<"img">, "style"> & ComponentStyle & ComponentHoverStyle;
7
+ type ImageComponent = {
8
+ (props: ComponentPropWithRef<HTMLImageElement, ImageProps>): React.ReactElement;
9
+ profileImage: (props: ComponentPropWithRef<HTMLImageElement, OmitProps<ImageProps, "width" | "height"> & {
10
+ /** @default 40 */
11
+ size?: number;
12
+ }>) => React.ReactElement;
13
+ };
14
+ declare const Image: ImageComponent;
15
+ declare const _default: ImageComponent & {
16
+ profileImage: typeof Image.profileImage;
17
+ };
18
+ export default _default;
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const jsx_runtime_1 = require("react/jsx-runtime");
7
+ const react_1 = require("react");
8
+ const styled_components_1 = __importDefault(require("styled-components"));
9
+ const BetterHtmlProvider_1 = require("./BetterHtmlProvider");
10
+ const hooks_1 = require("../utils/hooks");
11
+ const ImageElement = styled_components_1.default.img.withConfig({
12
+ shouldForwardProp: (prop) => !["normalStyle", "hoverStyle"].includes(prop),
13
+ }) `
14
+ display: block;
15
+ user-select: none;
16
+ -webkit-user-drag: none;
17
+
18
+ ${(props) => props.normalStyle}
19
+
20
+ &:hover {
21
+ ${(props) => props.hoverStyle}
22
+ }
23
+ `;
24
+ const Image = (0, react_1.forwardRef)(function Image({ name, src, ...props }, ref) {
25
+ const theme = (0, BetterHtmlProvider_1.useTheme)();
26
+ const { assets } = (0, BetterHtmlProvider_1.useBetterHtmlContext)();
27
+ const styledComponentStyles = (0, hooks_1.useStyledComponentStyles)(props, theme);
28
+ const dataProps = (0, hooks_1.useComponentPropsWithPrefix)(props, "data");
29
+ const ariaProps = (0, hooks_1.useComponentPropsWithPrefix)(props, "aria");
30
+ const restProps = (0, hooks_1.useComponentPropsWithoutStyle)(props);
31
+ (0, react_1.useEffect)(() => {
32
+ if (!name)
33
+ return;
34
+ if (!assets[name.toString()])
35
+ console.warn(`The asset \`${name}\` you are trying to use does not exist. Make sure to add it to the \`assets\` object in \`<BetterHtmlProvider>\` config value prop.`);
36
+ }, [assets, name]);
37
+ return ((0, jsx_runtime_1.jsx)(ImageElement, { ...styledComponentStyles, src: name ? assets[name.toString()] : src, ...styledComponentStyles, ...dataProps, ...ariaProps, ...restProps, ref: ref }));
38
+ });
39
+ Image.profileImage = (0, react_1.forwardRef)(function ProfileImage({ size = 40, ...props }, ref) {
40
+ return (0, jsx_runtime_1.jsx)(Image, { width: size, height: size, borderRadius: "50%", objectFit: "cover", ref: ref, ...props });
41
+ });
42
+ const MemoizedImage = (0, react_1.memo)(Image);
43
+ MemoizedImage.profileImage = Image.profileImage;
44
+ exports.default = { Image: MemoizedImage }.Image;
@@ -0,0 +1,33 @@
1
+ import React from "react";
2
+ import { ComponentHoverStyle, ComponentPropWithRef, ComponentStyle } from "../types/components";
3
+ import { OmitProps } from "../types/app";
4
+ import { IconName } from "../types/icon";
5
+ type InputFieldProps = {
6
+ label?: string;
7
+ errorText?: string;
8
+ infoText?: string;
9
+ leftIcon?: IconName;
10
+ rightIcon?: IconName;
11
+ insideInputFieldComponent?: React.ReactNode;
12
+ withDebounce?: boolean;
13
+ /** @default 0.5s */
14
+ debounceDelay?: number;
15
+ onChangeValue?: (value: string) => void;
16
+ onClickRightIcon?: () => void;
17
+ } & OmitProps<React.ComponentProps<"input">, "style"> & ComponentStyle & ComponentHoverStyle;
18
+ type InputFieldComponentType = {
19
+ (props: ComponentPropWithRef<HTMLInputElement, InputFieldProps>): React.ReactElement;
20
+ multiline: (props: ComponentPropWithRef<HTMLTextAreaElement, TextareaFieldProps>) => React.ReactElement;
21
+ email: (props: ComponentPropWithRef<HTMLInputElement, InputFieldProps>) => React.ReactElement;
22
+ password: (props: ComponentPropWithRef<HTMLInputElement, InputFieldProps>) => React.ReactElement;
23
+ search: (props: ComponentPropWithRef<HTMLInputElement, InputFieldProps>) => React.ReactElement;
24
+ };
25
+ declare const InputFieldComponent: InputFieldComponentType;
26
+ type TextareaFieldProps = OmitProps<InputFieldProps, "type"> & OmitProps<React.ComponentProps<"textarea">, "style">;
27
+ declare const InputField: typeof InputFieldComponent & {
28
+ multiline: typeof InputFieldComponent.multiline;
29
+ email: typeof InputFieldComponent.email;
30
+ password: typeof InputFieldComponent.password;
31
+ search: typeof InputFieldComponent.search;
32
+ };
33
+ export default InputField;
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const jsx_runtime_1 = require("react/jsx-runtime");
7
+ const react_1 = require("react");
8
+ const styled_components_1 = __importDefault(require("styled-components"));
9
+ const hooks_1 = require("../utils/hooks");
10
+ const Text_1 = __importDefault(require("./Text"));
11
+ const Div_1 = __importDefault(require("./Div"));
12
+ const Icon_1 = __importDefault(require("./Icon"));
13
+ const Button_1 = __importDefault(require("./Button"));
14
+ const BetterHtmlProvider_1 = require("./BetterHtmlProvider");
15
+ const InputElement = styled_components_1.default.input.withConfig({
16
+ shouldForwardProp: (prop) => !["theme", "withLeftIcon", "withRightIcon", "normalStyle", "hoverStyle"].includes(prop),
17
+ }) `
18
+ position: relative;
19
+ width: 100%;
20
+ font-family: ${(props) => props.theme.styles.fontFamily};
21
+ font-size: 16px;
22
+ line-height: 20px;
23
+ color: ${(props) => props.theme.colors.textPrimary};
24
+ background: ${(props) => props.theme.colors.backgroundContent};
25
+ border: 1px solid ${(props) => props.theme.colors.border};
26
+ border-radius: ${(props) => props.theme.styles.borderRadius}px;
27
+ outline: none;
28
+ padding: ${(props) => `${(props.theme.styles.space + props.theme.styles.gap) / 2}px ${props.theme.styles.space + props.theme.styles.gap}px`};
29
+ padding-left: ${(props) => props.withLeftIcon ? `${props.theme.styles.space + 16 + props.theme.styles.space - 1}px` : undefined};
30
+ padding-right: ${(props) => props.withRightIcon ? `${props.theme.styles.space + 16 + props.theme.styles.space - 1}px` : undefined};
31
+ transition: ${(props) => props.theme.styles.transition};
32
+
33
+ &::placeholder {
34
+ color: ${(props) => props.theme.colors.textSecondary}80;
35
+ }
36
+
37
+ &:focus {
38
+ border-color: ${(props) => props.theme.colors.primary};
39
+ }
40
+
41
+ &:disabled {
42
+ filter: brightness(0.9);
43
+ cursor: not-allowed;
44
+ }
45
+
46
+ &.react-better-html-dropdown {
47
+ padding-right: ${(props) => props.theme.styles.space + 16 + props.theme.styles.space - 1}px;
48
+
49
+ &.react-better-html-dropdown-open {
50
+ border-bottom-left-radius: 0px;
51
+ border-bottom-right-radius: 0px;
52
+ }
53
+ }
54
+
55
+ ${(props) => props.normalStyle}
56
+
57
+ &:hover {
58
+ ${(props) => props.hoverStyle}
59
+ }
60
+ `;
61
+ const TextareaElement = styled_components_1.default.textarea.withConfig({
62
+ shouldForwardProp: (prop) => !["normalStyle", "hoverStyle"].includes(prop),
63
+ }) `
64
+ width: 100%;
65
+ min-height: 3lh;
66
+ max-height: 8lh;
67
+ font-family: ${(props) => props.theme.styles.fontFamily};
68
+ font-size: 16px;
69
+ line-height: 20px;
70
+ color: ${(props) => props.theme.colors.textPrimary};
71
+ background: ${(props) => props.theme.colors.backgroundContent};
72
+ border: 1px solid ${(props) => props.theme.colors.border};
73
+ border-radius: ${(props) => props.theme.styles.borderRadius}px;
74
+ outline: none;
75
+ padding: ${(props) => `${(props.theme.styles.gap + props.theme.styles.space) / 2}px ${props.theme.styles.space + props.theme.styles.gap}px`};
76
+ resize: vertical;
77
+ transition: border-color ${(props) => props.theme.styles.transition};
78
+
79
+ &::placeholder {
80
+ color: ${(props) => props.theme.colors.textSecondary}80;
81
+ }
82
+
83
+ &:focus {
84
+ border-color: ${(props) => props.theme.colors.primary};
85
+ }
86
+
87
+ ${(props) => props.normalStyle}
88
+
89
+ &:hover {
90
+ ${(props) => props.hoverStyle}
91
+ }
92
+ `;
93
+ const InputFieldComponent = (0, react_1.forwardRef)(function InputField({ label, errorText, infoText, leftIcon, rightIcon, insideInputFieldComponent, withDebounce, debounceDelay = 0.5, onChange, onChangeValue, onClickRightIcon, required, ...props }, ref) {
94
+ const theme = (0, BetterHtmlProvider_1.useTheme)();
95
+ const [_, debouncedValue, setDebouncedValue] = (0, hooks_1.useDebounceState)(props.value?.toString() ?? "", debounceDelay);
96
+ const styledComponentStylesWithoutExcluded = (0, hooks_1.useStyledComponentStyles)(props, theme, true);
97
+ const styledComponentStylesWithExcluded = (0, hooks_1.useComponentPropsWithExcludedStyle)(props);
98
+ const dataProps = (0, hooks_1.useComponentPropsWithPrefix)(props, "data");
99
+ const ariaProps = (0, hooks_1.useComponentPropsWithPrefix)(props, "aria");
100
+ const restProps = (0, hooks_1.useComponentPropsWithoutStyle)(props);
101
+ const onChangeElement = (0, react_1.useCallback)((event) => {
102
+ const newValue = event.target.value;
103
+ if (withDebounce) {
104
+ onChange?.(event);
105
+ setDebouncedValue(newValue);
106
+ }
107
+ else {
108
+ onChange?.(event);
109
+ onChangeValue?.(newValue);
110
+ }
111
+ }, [onChange, onChangeValue, withDebounce]);
112
+ (0, react_1.useEffect)(() => {
113
+ if (!withDebounce)
114
+ return;
115
+ onChangeValue?.(debouncedValue);
116
+ }, [withDebounce, onChangeValue, debouncedValue]);
117
+ return ((0, jsx_runtime_1.jsxs)(Div_1.default.column, { width: "100%", gap: theme.styles.gap / 2, ...styledComponentStylesWithExcluded, children: [label && ((0, jsx_runtime_1.jsxs)(Text_1.default, { as: "label", height: 16, fontSize: 14, color: errorText ? theme.colors.error : theme.colors.textSecondary, children: [label, required && ((0, jsx_runtime_1.jsxs)(Text_1.default, { as: "span", fontSize: 16, color: theme.colors.error, children: [" ", "*"] }))] })), (0, jsx_runtime_1.jsxs)(Div_1.default, { position: "relative", width: "100%", children: [leftIcon && ((0, jsx_runtime_1.jsx)(Icon_1.default, { name: leftIcon, position: "absolute", top: 46 / 2, left: theme.styles.space + 1, transform: "translateY(-50%)", pointerEvents: "none", zIndex: 1002 })), (0, jsx_runtime_1.jsx)(InputElement, { theme: theme, withLeftIcon: leftIcon !== undefined, withRightIcon: rightIcon !== undefined, onChange: onChangeElement, ...styledComponentStylesWithoutExcluded, ...dataProps, ...ariaProps, ...restProps, ref: ref }), rightIcon ? (onClickRightIcon ? ((0, jsx_runtime_1.jsx)(Button_1.default.icon, { icon: rightIcon, position: "absolute", top: 46 / 2, right: theme.styles.space + 1 - theme.styles.space / 2, transform: "translateY(-50%)", onClick: onClickRightIcon })) : ((0, jsx_runtime_1.jsx)(Icon_1.default, { name: rightIcon, position: "absolute", top: 46 / 2, right: theme.styles.space + 1, transform: "translateY(-50%)", pointerEvents: "none" }))) : undefined, insideInputFieldComponent] }), (errorText || infoText) && ((0, jsx_runtime_1.jsx)(Text_1.default, { as: "span", display: "block", fontSize: 14, color: errorText ? theme.colors.error : theme.colors.textSecondary, children: errorText || infoText }))] }));
118
+ });
119
+ InputFieldComponent.multiline = (0, react_1.forwardRef)(function Textarea({ label, errorText, infoText, onChange, onChangeValue, required, ...props }, ref) {
120
+ const theme = (0, BetterHtmlProvider_1.useTheme)();
121
+ const styledComponentStyles = (0, hooks_1.useStyledComponentStyles)(props, theme);
122
+ const dataProps = (0, hooks_1.useComponentPropsWithPrefix)(props, "data");
123
+ const ariaProps = (0, hooks_1.useComponentPropsWithPrefix)(props, "aria");
124
+ const restProps = (0, hooks_1.useComponentPropsWithoutStyle)(props);
125
+ const onChangeElement = (0, react_1.useCallback)((event) => {
126
+ onChange?.(event);
127
+ onChangeValue?.(event.target.value);
128
+ }, [onChange, onChangeValue]);
129
+ return ((0, jsx_runtime_1.jsxs)(Div_1.default.column, { gap: theme.styles.gap / 2, children: [label && ((0, jsx_runtime_1.jsxs)(Text_1.default, { as: "label", fontSize: 14, color: errorText ? theme.colors.error : theme.colors.textSecondary, children: [label, required && ((0, jsx_runtime_1.jsxs)(Text_1.default, { as: "span", fontSize: 16, color: theme.colors.error, children: [" ", "*"] }))] })), (0, jsx_runtime_1.jsx)(TextareaElement, { theme: theme, onChange: onChangeElement, ...styledComponentStyles, ...dataProps, ...ariaProps, ...restProps, ref: ref }), (errorText || infoText) && ((0, jsx_runtime_1.jsx)(Text_1.default, { as: "span", display: "block", marginTop: theme.styles.gap / 2, color: errorText ? theme.colors.error : theme.colors.textSecondary, fontSize: 14, children: errorText || infoText }))] }));
130
+ });
131
+ InputFieldComponent.email = (0, react_1.forwardRef)(function Email({ ...props }, ref) {
132
+ return ((0, jsx_runtime_1.jsx)(InputFieldComponent, { type: "email", placeholder: "your@email.here", autoComplete: "email", autoCorrect: "off", autoCapitalize: "off", ref: ref, ...props }));
133
+ });
134
+ InputFieldComponent.password = (0, react_1.forwardRef)(function Email({ ...props }, ref) {
135
+ const [isPassword, setIsPassword] = (0, hooks_1.useBooleanState)(true);
136
+ return ((0, jsx_runtime_1.jsx)(InputFieldComponent, { type: isPassword ? "password" : "text", placeholder: "******", rightIcon: isPassword ? "eye" : "eyeDashed", onClickRightIcon: setIsPassword.toggle, autoComplete: "current-password", autoCorrect: "off", autoCapitalize: "off", ref: ref, ...props }));
137
+ });
138
+ InputFieldComponent.search = (0, react_1.forwardRef)(function Email({ ...props }, ref) {
139
+ return (0, jsx_runtime_1.jsx)(InputFieldComponent, { leftIcon: "magnifyingGlass", placeholder: "Search...", ref: ref, ...props });
140
+ });
141
+ const InputField = (0, react_1.memo)(InputFieldComponent);
142
+ InputField.multiline = InputFieldComponent.multiline;
143
+ InputField.email = InputFieldComponent.email;
144
+ InputField.password = InputFieldComponent.password;
145
+ InputField.search = InputFieldComponent.search;
146
+ exports.default = InputField;
@@ -13,7 +13,22 @@ type LoaderProps = {
13
13
  } & OmitProps<DivProps<unknown>, "width" | "height" | "color" | "background" | "borderRadius" | "mask" | "WebkitMask" | "padding" | "animation" | "animationName">;
14
14
  type LoaderComponentType = {
15
15
  (props: LoaderProps): React.ReactElement;
16
+ box: (props: OmitProps<LoaderProps, "size"> & {
17
+ /** @default "Loading..." */
18
+ text?: string;
19
+ /** @default 20 */
20
+ size?: number;
21
+ }) => React.ReactElement;
22
+ text: (props: OmitProps<LoaderProps, "size"> & {
23
+ /** @default "Loading..." */
24
+ text?: string;
25
+ /** @default 14 */
26
+ size?: number;
27
+ }) => React.ReactElement;
16
28
  };
17
29
  declare const LoaderComponent: LoaderComponentType;
18
- declare const Loader: typeof LoaderComponent & {};
30
+ declare const Loader: typeof LoaderComponent & {
31
+ box: typeof LoaderComponent.box;
32
+ text: typeof LoaderComponent.text;
33
+ };
19
34
  export default Loader;
@@ -7,6 +7,7 @@ const jsx_runtime_1 = require("react/jsx-runtime");
7
7
  const react_1 = require("react");
8
8
  const styled_components_1 = __importDefault(require("styled-components"));
9
9
  const Div_1 = __importDefault(require("./Div"));
10
+ const Text_1 = __importDefault(require("./Text"));
10
11
  const BetterHtmlProvider_1 = require("./BetterHtmlProvider");
11
12
  const StyledDiv = (0, styled_components_1.default)(Div_1.default) `
12
13
  @keyframes loaderAnimation {
@@ -28,5 +29,15 @@ const LoaderComponent = function Loader({ color, size = 16, width, disabled, ...
28
29
  const mask = `radial-gradient(farthest-side, #0000 calc(100% - ${readyWidth}px), #000 0)`;
29
30
  return ((0, jsx_runtime_1.jsx)(StyledDiv, { width: size + readyWidth * 2, height: size + readyWidth * 2, background: `radial-gradient(farthest-side, ${readyColor} 94%, #0000) top/${readyWidth}px ${readyWidth}px no-repeat, conic-gradient(#0000 30%, ${readyColor})`, borderRadius: 999, mask: mask, WebkitMask: mask, padding: readyWidth, animation: !disabled ? "loaderAnimation .6s infinite linear" : undefined, ...props }));
30
31
  };
32
+ LoaderComponent.box = function Box({ text = "Loading...", size = 20, ...props }) {
33
+ const theme = (0, BetterHtmlProvider_1.useTheme)();
34
+ return ((0, jsx_runtime_1.jsxs)(Div_1.default.column, { width: "100%", alignItems: "center", gap: theme.styles.gap, children: [(0, jsx_runtime_1.jsx)(Loader, { size: size, ...props }), text && ((0, jsx_runtime_1.jsx)(Text_1.default, { textAlign: "center", color: props.color ?? theme.colors.textSecondary, children: text }))] }));
35
+ };
36
+ LoaderComponent.text = function LoaderText({ text = "Loading...", size = 14, ...props }) {
37
+ const theme = (0, BetterHtmlProvider_1.useTheme)();
38
+ return ((0, jsx_runtime_1.jsxs)(Div_1.default.row, { alignItems: "center", gap: theme.styles.gap, children: [(0, jsx_runtime_1.jsx)(Loader, { size: size, ...props }), text && ((0, jsx_runtime_1.jsx)(Text_1.default, { textAlign: "center", color: props.color ?? theme.colors.textSecondary, children: text }))] }));
39
+ };
31
40
  const Loader = (0, react_1.memo)(LoaderComponent);
41
+ Loader.box = LoaderComponent.box;
42
+ Loader.text = LoaderComponent.text;
32
43
  exports.default = Loader;
@@ -0,0 +1,41 @@
1
+ import { ComponentPropWithRef } from "../types/components";
2
+ import { OmitProps } from "../types/app";
3
+ type ModalProps = {
4
+ /**
5
+ * If you want to have a small modal, you can use 660px as it looks good on most screens.
6
+ *
7
+ * @default 30% smaller than app.contentMaxWidth
8
+ * */
9
+ maxWidth?: number;
10
+ title?: string;
11
+ titleColor?: string;
12
+ description?: string;
13
+ descriptionColor?: string;
14
+ headerBackgroundColor?: string;
15
+ overflow?: React.CSSProperties["overflow"];
16
+ onOpen?: () => void;
17
+ onClose?: () => void;
18
+ children?: React.ReactNode;
19
+ };
20
+ export type ModalRef = {
21
+ open: () => void;
22
+ close: () => void;
23
+ isOpened: boolean;
24
+ };
25
+ type ModalComponent = {
26
+ (props: ComponentPropWithRef<ModalRef, ModalProps>): React.ReactElement;
27
+ confirmation: (props: ComponentPropWithRef<ModalRef, OmitProps<ModalProps, "maxWidth" | "children" | "overflow"> & {
28
+ onConfirm?: () => void;
29
+ onCancel?: () => void;
30
+ }>) => React.ReactElement;
31
+ destructive: (props: ComponentPropWithRef<ModalRef, OmitProps<ModalProps, "maxWidth" | "children" | "overflow"> & {
32
+ onDelete?: () => void;
33
+ onCancel?: () => void;
34
+ }>) => React.ReactElement;
35
+ };
36
+ declare const ModalComponent: ModalComponent;
37
+ declare const Modal: typeof ModalComponent & {
38
+ confirmation: typeof ModalComponent.confirmation;
39
+ destructive: typeof ModalComponent.destructive;
40
+ };
41
+ export default Modal;
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const jsx_runtime_1 = require("react/jsx-runtime");
7
+ const react_1 = require("react");
8
+ const styled_components_1 = __importDefault(require("styled-components"));
9
+ const Div_1 = __importDefault(require("./Div"));
10
+ const Button_1 = __importDefault(require("./Button"));
11
+ const Text_1 = __importDefault(require("./Text"));
12
+ const Divider_1 = __importDefault(require("./Divider"));
13
+ const BetterHtmlProvider_1 = require("./BetterHtmlProvider");
14
+ const DialogStylesElement = styled_components_1.default.dialog.withConfig({
15
+ shouldForwardProp: (prop) => !["theme", "opacity"].includes(prop),
16
+ }) `
17
+ width: 100%;
18
+ max-width: none;
19
+ height: 100%;
20
+ max-height: none;
21
+ border: none;
22
+ outline: none;
23
+ background-color: transparent;
24
+ margin: auto auto;
25
+ padding-inline: ${(props) => props.theme.styles.gap}px;
26
+ opacity: ${(props) => props.opacity};
27
+ transition: ${(props) => props.theme.styles.transition};
28
+
29
+ &::backdrop {
30
+ background-color: #000000a0;
31
+ opacity: ${(props) => props.opacity};
32
+ transition: ${(props) => props.theme.styles.transition};
33
+ }
34
+
35
+ @keyframes fadeInAnimation {
36
+ from {
37
+ transform: translateY(${(props) => props.theme.styles.space}px);
38
+ }
39
+ to {
40
+ transform: translateY(0px);
41
+ }
42
+ }
43
+
44
+ @keyframes fadeOutAnimation {
45
+ from {
46
+ transform: translateY(0px);
47
+ }
48
+ to {
49
+ transform: translateY(${(props) => props.theme.styles.space}px);
50
+ }
51
+ }
52
+ `;
53
+ const ModalComponent = (0, react_1.forwardRef)(function Modal({ maxWidth, title, titleColor, description, descriptionColor, headerBackgroundColor, overflow, onOpen, onClose, children, }, ref) {
54
+ const theme = (0, BetterHtmlProvider_1.useTheme)();
55
+ const { app } = (0, BetterHtmlProvider_1.useBetterHtmlContext)();
56
+ const dialogRef = (0, react_1.useRef)(null);
57
+ const [isOpened, setIsOpened] = (0, react_1.useState)(false);
58
+ const [isOpenedLate, setIsOpenedLate] = (0, react_1.useState)(false);
59
+ const onClickOpen = (0, react_1.useCallback)(() => {
60
+ dialogRef.current?.showModal();
61
+ setIsOpened(true);
62
+ setIsOpenedLate(true);
63
+ onOpen?.();
64
+ }, [onOpen]);
65
+ const onClickClose = (0, react_1.useCallback)(() => {
66
+ setIsOpened(false);
67
+ onClose?.();
68
+ setTimeout(() => {
69
+ dialogRef.current?.close();
70
+ setIsOpenedLate(false);
71
+ }, 0.2 * 1000);
72
+ }, [onClose]);
73
+ (0, react_1.useImperativeHandle)(ref, () => {
74
+ return {
75
+ open: onClickOpen,
76
+ close: onClickClose,
77
+ isOpened,
78
+ };
79
+ }, [onClickOpen, onClickClose, isOpened]);
80
+ return ((0, jsx_runtime_1.jsx)(DialogStylesElement, { theme: theme, opacity: !isOpened ? 0 : 1, onClose: onClickClose, ref: dialogRef, children: isOpenedLate ? ((0, jsx_runtime_1.jsx)(Div_1.default.column, { position: "relative", width: "100%", maxWidth: maxWidth ?? app.contentMaxWidth / 1.3, minHeight: `calc(100% - ${theme.styles.space * 2}px)`, alignItems: "center", justifyContent: "center", marginBlock: theme.styles.space, marginInline: "auto", transform: `translateY(${theme.styles.space}px)`, transition: theme.styles.transition, animation: isOpened ? "fadeInAnimation 0.2s ease forwards" : "fadeOutAnimation 0.2s ease forwards", children: (0, jsx_runtime_1.jsxs)(Div_1.default, { position: "relative", width: "100%", minHeight: 32 + theme.styles.space * 2, backgroundColor: theme.colors.backgroundBase, borderRadius: theme.styles.borderRadius * 2, paddingInline: !title ? theme.styles.space : undefined, paddingBlock: !title ? theme.styles.gap : undefined, overflow: overflow, children: [title ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)(Div_1.default.row, { alignItems: "center", gap: theme.styles.gap, backgroundColor: headerBackgroundColor, borderTopLeftRadius: theme.styles.borderRadius * 2, borderTopRightRadius: theme.styles.borderRadius * 2, paddingInline: theme.styles.space, paddingBlock: theme.styles.gap, children: [(0, jsx_runtime_1.jsxs)(Div_1.default.column, { flex: 1, gap: theme.styles.gap / 2, children: [(0, jsx_runtime_1.jsx)(Text_1.default, { as: "h1", color: titleColor ?? theme.colors.textPrimary, children: title }), description && ((0, jsx_runtime_1.jsx)(Text_1.default, { color: descriptionColor ?? theme.colors.textSecondary, children: description }))] }), (0, jsx_runtime_1.jsx)(Button_1.default.icon, { icon: "XMark", marginTop: 1, iconColor: titleColor, onClick: onClickClose })] }), (0, jsx_runtime_1.jsx)(Divider_1.default.horizontal, {})] })) : ((0, jsx_runtime_1.jsx)(Div_1.default, { position: "absolute", top: theme.styles.space, right: theme.styles.space, children: (0, jsx_runtime_1.jsx)(Button_1.default.icon, { icon: "XMark", onClick: onClickClose }) })), (0, jsx_runtime_1.jsx)(Div_1.default, { paddingInline: title ? theme.styles.space : undefined, paddingBlock: title ? theme.styles.gap : undefined, children: children })] }) })) : undefined }));
81
+ });
82
+ ModalComponent.confirmation = (0, react_1.forwardRef)(function Confirmation({ onConfirm, onCancel, ...props }, ref) {
83
+ const theme = (0, BetterHtmlProvider_1.useTheme)();
84
+ return ((0, jsx_runtime_1.jsxs)(Modal, { title: "Are you sure?", maxWidth: 660, ...props, ref: ref, children: [(0, jsx_runtime_1.jsx)(Text_1.default, { color: theme.colors.textSecondary, children: "Do you really want to continue? This action may be irreversible." }), (0, jsx_runtime_1.jsxs)(Div_1.default.row, { alignItems: "center", justifyContent: "flex-end", gap: theme.styles.gap, marginTop: theme.styles.space * 2, children: [(0, jsx_runtime_1.jsx)(Button_1.default.secondary, { text: "Cancel", onClick: onCancel }), (0, jsx_runtime_1.jsx)(Button_1.default, { text: "Continue", onClick: onConfirm })] })] }));
85
+ });
86
+ ModalComponent.destructive = (0, react_1.forwardRef)(function Destructive({ onDelete, onCancel, ...props }, ref) {
87
+ const theme = (0, BetterHtmlProvider_1.useTheme)();
88
+ return ((0, jsx_runtime_1.jsxs)(Modal, { title: "Are you sure?", maxWidth: 660, ...props, ref: ref, children: [(0, jsx_runtime_1.jsx)(Text_1.default, { color: theme.colors.textSecondary, children: "Do you really want to continue with the deleting of the item? This action may be irreversible." }), (0, jsx_runtime_1.jsxs)(Div_1.default.row, { alignItems: "center", justifyContent: "flex-end", gap: theme.styles.gap, marginTop: theme.styles.space * 2, children: [(0, jsx_runtime_1.jsx)(Button_1.default.secondary, { text: "Cancel", onClick: onCancel }), (0, jsx_runtime_1.jsx)(Button_1.default.destructive, { icon: "trash", text: "Delete", onClick: onDelete })] })] }));
89
+ });
90
+ const Modal = (0, react_1.memo)(ModalComponent);
91
+ Modal.confirmation = ModalComponent.confirmation;
92
+ Modal.destructive = ModalComponent.destructive;
93
+ exports.default = Modal;