styled-hook-form
Version:
React form library for styled-components based on grommet and react-hook-form
251 lines (250 loc) • 13.9 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DropDown = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = __importStar(require("react"));
const react_2 = require("react");
const react_hook_form_1 = require("react-hook-form");
const grommet_1 = require("grommet");
const grommet_2 = require("grommet");
const paged_data_source_1 = require("../../../utils/paged-data-source");
const context_1 = require("../../../../context");
const styled_components_1 = __importDefault(require("styled-components"));
// @ts-ignore
const styles_1 = require("grommet/utils/styles");
const use_debounce_1 = require("use-debounce");
const grommet_icons_1 = require("grommet-icons");
const remeda_1 = require("remeda");
const MultipleOption = react_2.memo((props) => {
let { label, selected } = props;
return (jsx_runtime_1.jsxs(grommet_1.Box, Object.assign({ direction: "row", gap: "small", align: "center", pad: "xsmall" }, { children: [jsx_runtime_1.jsx(grommet_1.CheckBox, { tabIndex: -1, checked: selected, onChange: () => { } }, void 0), label] }), void 0));
});
const SingleOption = react_2.memo((props) => {
let { label, selected } = props;
return (jsx_runtime_1.jsx(grommet_1.Box, Object.assign({ direction: "row", gap: "small", align: "center", pad: "xsmall", background: selected ? "brand" : "light-1" }, { children: label }), void 0));
});
const LabelBox = styled_components_1.default(grommet_1.Box).attrs({
direction: "row",
}) `
${({ plainLabel }) => !plainLabel && styles_1.inputStyle}
border:none;
display: flex;
flex-direction: row;
flex-wrap: wrap;
`;
const DefaultOptionLabel = ({ content }) => (jsx_runtime_1.jsxs("span", { children: [" ", content, " "] }, void 0));
const DropDown = react_1.default.memo(react_2.forwardRef((props, ref) => {
let vrules = props.validationRules || {};
const { translate: T } = context_1.useFormBuilderContext();
let { name, label, options, methods, required, multiple, onSearch, itemLabelKey, itemValueKey, placeholder, labelWrap, renderItem, renderItemLabel, plainLabel, selectProps, shouldUnregister, defaultValue: initialValue, searchDebounce = 500, } = props;
let [localValue, setLocalValue] = react_2.useState(null);
let [localOptions, setLocalOptions] = react_2.useState([]);
let [remoteOptions, setRemoteOptions] = react_2.useState([]);
let [remoteSearchKey, setRemoteSearchKey] = react_2.useState("");
let dataSourceOptions = react_1.useMemo(() => {
return options.hasOwnProperty("request")
? options
: null;
}, [options]);
const params = react_1.useMemo(() => {
var _a;
return Object.assign(Object.assign({}, (typeof (dataSourceOptions === null || dataSourceOptions === void 0 ? void 0 : dataSourceOptions.request) !== "string"
? (_a = dataSourceOptions === null || dataSourceOptions === void 0 ? void 0 : dataSourceOptions.request) === null || _a === void 0 ? void 0 : _a.params
: {})), dataSourceOptions === null || dataSourceOptions === void 0 ? void 0 : dataSourceOptions.extraParams);
}, [dataSourceOptions === null || dataSourceOptions === void 0 ? void 0 : dataSourceOptions.extraParams, dataSourceOptions === null || dataSourceOptions === void 0 ? void 0 : dataSourceOptions.request]);
let { loading = false, nextPage = null, hasMore = null } = paged_data_source_1.usePagedData({
request: dataSourceOptions === null || dataSourceOptions === void 0 ? void 0 : dataSourceOptions.request,
pageParamName: dataSourceOptions === null || dataSourceOptions === void 0 ? void 0 : dataSourceOptions.pageKey,
pageSizeParamName: dataSourceOptions === null || dataSourceOptions === void 0 ? void 0 : dataSourceOptions.pageSizeKey,
pageSize: dataSourceOptions === null || dataSourceOptions === void 0 ? void 0 : dataSourceOptions.pageSize,
searchParamName: dataSourceOptions === null || dataSourceOptions === void 0 ? void 0 : dataSourceOptions.searchKey,
searchParam: remoteSearchKey,
listPropName: dataSourceOptions === null || dataSourceOptions === void 0 ? void 0 : dataSourceOptions.listKey,
totalPropName: dataSourceOptions === null || dataSourceOptions === void 0 ? void 0 : dataSourceOptions.totalKey,
params: params,
mockResponse: dataSourceOptions === null || dataSourceOptions === void 0 ? void 0 : dataSourceOptions.mockResponse,
onRequest: (data, headers) => {
if (dataSourceOptions === null || dataSourceOptions === void 0 ? void 0 : dataSourceOptions.onRequest) {
data = dataSourceOptions.onRequest(data, headers);
}
return data;
},
onResponse: (data, page, headers) => {
var _a;
if (dataSourceOptions === null || dataSourceOptions === void 0 ? void 0 : dataSourceOptions.onResponse) {
data = dataSourceOptions.onResponse(data, headers);
}
if (!Array.isArray(data)) {
data = data[(_a = dataSourceOptions === null || dataSourceOptions === void 0 ? void 0 : dataSourceOptions.listKey) !== null && _a !== void 0 ? _a : "list"];
}
setRemoteOptions((oldOptions) => {
let newOptions = [...(page > 1 ? oldOptions : []), ...data];
return newOptions;
});
return data;
},
});
let control = methods === null || methods === void 0 ? void 0 : methods.control;
if (required) {
vrules.required = {
value: required,
message: T("required-msg", { name: label }),
};
}
react_2.useEffect(() => {
setLocalOptions(options instanceof Array ? options : []);
}, [options]);
let liveValue = react_hook_form_1.useWatch({
name: name,
control: methods.control,
defaultValue: initialValue,
});
let actualOptions = react_1.useMemo(() => (dataSourceOptions ? remoteOptions !== null && remoteOptions !== void 0 ? remoteOptions : [] : localOptions), [dataSourceOptions, remoteOptions, localOptions]);
const getOptionsByValue = react_2.useCallback((options, value) => {
return options.filter((o) => Array.isArray(value)
? value.some((v) => v === o[itemValueKey] || v[itemValueKey] === o[itemValueKey])
: value === o[itemValueKey] || value === o);
}, [itemValueKey]);
const liveValueRef = react_1.useRef(null);
const updateLocalValue = react_2.useCallback(() => {
setLocalValue(getOptionsByValue(actualOptions, liveValue));
liveValueRef.current = liveValue;
}, [actualOptions, getOptionsByValue, liveValue]);
react_2.useEffect(() => {
if (actualOptions &&
(!liveValueRef.current ||
JSON.stringify(liveValueRef.current) !== JSON.stringify(liveValue))) {
updateLocalValue();
}
}, [actualOptions, liveValue, updateLocalValue]);
react_2.useEffect(() => {
if (actualOptions) {
updateLocalValue();
}
}, [actualOptions, updateLocalValue]);
react_2.useEffect(() => {
if (actualOptions && initialValue) {
setLocalValue(getOptionsByValue(actualOptions, initialValue));
}
}, [actualOptions, getOptionsByValue, initialValue]);
const handleSearch = use_debounce_1.useDebouncedCallback((text) => {
if (!dataSourceOptions && text.length === 0) {
setLocalOptions(options instanceof Array ? options : []);
return;
}
if (dataSourceOptions) {
setRemoteSearchKey(text);
}
else {
let filteredOptions;
if (onSearch) {
filteredOptions = onSearch(text, localOptions);
}
const escapedText = text.replace(/[-\\^$*+?.()|[\]{}]/g, "\\$&");
filteredOptions = localOptions.filter((o) => o[itemLabelKey].match(new RegExp(escapedText, "gi")));
setLocalOptions(filteredOptions);
}
}, searchDebounce);
const handleMore = () => {
if (hasMore) {
nextPage();
}
};
const selectDynamicProps = {};
if (dataSourceOptions && loading) {
selectDynamicProps.icon = jsx_runtime_1.jsx(grommet_2.Spinner, {}, void 0);
}
const handleChange = react_2.useCallback((field) => (e) => {
setLocalValue(e.value);
field.onChange(multiple
? e.value.map((v) => v[itemValueKey])
: e.value[itemValueKey]);
}, [itemValueKey, multiple]);
let selectContent = (option) => {
let selectedValues = localValue;
let opt_value = option[itemValueKey], opt_label = option[itemLabelKey];
const selected = selectedValues
? Array.isArray(selectedValues)
? selectedValues.findIndex((v) => v[itemValueKey] === opt_value) !==
-1
: selectedValues[itemValueKey] === opt_value
: false;
return multiple ? (jsx_runtime_1.jsx(MultipleOption, { label: renderItem ? (renderItem(option, selected)) : (jsx_runtime_1.jsx(DefaultOptionLabel, { content: opt_label }, void 0)), selected: selected }, void 0)) : renderItem ? (renderItem(option, selected)) : (jsx_runtime_1.jsx(SingleOption, { selected: selected, label: opt_label }, void 0));
};
const valueLabel = () => {
const handleClear = (e) => {
e.stopPropagation();
setLocalValue(null);
if (methods) {
methods.setValue(name, null);
}
};
const isEmpty = (value) => Array.isArray(value) ? !value.length : !value;
let _value = localValue
? Array.isArray(localValue)
? localValue
: [localValue]
: [];
let labels = (jsx_runtime_1.jsxs(grommet_1.Box, Object.assign({ direction: "row", justify: "between", align: "center", fill: "horizontal" }, { children: [jsx_runtime_1.jsx(LabelBox, Object.assign({ plainLabel: plainLabel, fill: "horizontal" }, { children: _value && _value.length ? (_value.map((val, idx) => (jsx_runtime_1.jsx(react_1.Fragment, { children: renderItemLabel ? (renderItemLabel(val, {
setValue: (setter) => {
let transformedValue = setter(_value);
methods.setValue(name, transformedValue);
setLocalValue(transformedValue);
},
}, idx)) : (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [jsx_runtime_1.jsx(grommet_1.Text, { children: val[itemLabelKey] }, void 0),
localValue.length - 1 > idx && jsx_runtime_1.jsx("span", { children: "," }, void 0)] }, void 0)) }, val[itemValueKey])))) : (jsx_runtime_1.jsx(grommet_1.Text, { children: placeholder !== null && placeholder !== void 0 ? placeholder : T("drop-down-plcaholder") }, void 0)) }), void 0),
(selectProps === null || selectProps === void 0 ? void 0 : selectProps.clear) && !isEmpty(localValue) && (jsx_runtime_1.jsx(grommet_1.Button, { focusIndicator: false, icon: jsx_runtime_1.jsx(grommet_icons_1.Close, { size: "small", onClick: handleClear }, void 0) }, void 0))] }), void 0));
let wrapElement = labelWrap
? react_1.default.cloneElement(labelWrap, {}, labels)
: react_1.default.createElement(grommet_1.Box, {
direction: "row",
overflow: "hidden",
fill: "horizontal",
wrap: true,
}, labels);
return wrapElement;
};
return (jsx_runtime_1.jsx(react_hook_form_1.Controller, { name: name, defaultValue: initialValue, shouldUnregister: shouldUnregister, rules: vrules, control: control, render: ({ field }) => (jsx_runtime_1.jsx(grommet_1.Select, Object.assign({}, selectProps, { closeOnChange: !multiple, ref: ref, multiple: multiple, valueLabel: valueLabel(), options: actualOptions, option: true, labelKey: itemLabelKey, valueKey: itemValueKey, onSearch: handleSearch, onMore: handleMore, onChange: handleChange(field), value: localValue !== null && localValue !== void 0 ? localValue : [], emptySearchMessage: T("drop-down-search-empty-msg"), clear: false }, selectDynamicProps, { children: selectContent }), void 0)) }, void 0));
}), (prev, next) => {
const eq = remeda_1.equals(remeda_1.omit(prev, [
"methods",
"children",
"onChange",
"onSearch",
"renderItem",
"renderItemLabel",
"render",
]), remeda_1.omit(next, [
"methods",
"children",
"onChange",
"onSearch",
"renderItem",
"renderItemLabel",
"render",
]));
return eq;
});
exports.DropDown = DropDown;