@prefect9/ui
Version:
UI React components
334 lines (333 loc) • 14.7 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "Option", {
enumerable: true,
get: function get() {
return _Option.default;
}
});
exports.default = void 0;
require("core-js/modules/es.array.includes.js");
require("core-js/modules/es.parse-float.js");
require("core-js/modules/es.promise.js");
require("core-js/modules/es.string.includes.js");
require("core-js/modules/web.dom-collections.iterator.js");
var _react = require("react");
var _isType = require("@prefect9/is-type");
var _Option = _interopRequireDefault(require("./Option"));
var _Scroll = _interopRequireDefault(require("../Scroll"));
var _styles = _interopRequireDefault(require("../../styles.css"));
var _icons = _interopRequireDefault(require("../../icons"));
var _jsxRuntime = require("react/jsx-runtime");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
const Select = /*#__PURE__*/(0, _react.forwardRef)((_ref, apiRef) => {
let {
children,
className: fieldClassName,
label,
onChange,
multi,
modalPaddingTop,
disabled
} = _ref;
const ref = (0, _react.useRef)(null);
const childrenArr = (0, _react.useMemo)(() => _react.Children.toArray(children), [children]);
const modal = (0, _react.useRef)({
showed: false,
disabled: false
});
const [value, setValue] = (0, _react.useState)(multi ? [] : null);
const [options, setOptions] = (0, _react.useState)([]);
const changeHandler = (0, _react.useCallback)(newValue => {
if (multi) return changeMultiHandler(newValue);
if (!(0, _isType.isStr)(newValue)) throw new Error('Value must be a string');
const isValue = options.find(option => option.value === newValue);
if (isValue === undefined) newValue = null;
setValue(newValue);
if ((0, _isType.isFunc)(onChange)) onChange(newValue);
}, [options, multi]);
const changeMultiHandler = (0, _react.useCallback)(newValues => {
if (!(0, _isType.isArr)(newValues)) throw new Error('Value must be array for multi select');
const newValidValues = [];
for (let newValue of newValues) {
const isNewValue = options.find(option => option.value === newValue);
if (isNewValue && !newValidValues.includes(newValue)) newValidValues.push(newValue);
}
setValue(newValidValues);
if ((0, _isType.isFunc)(onChange)) onChange(newValidValues);
}, [changeHandler]);
(0, _react.useEffect)(() => {
if (apiRef && apiRef.current !== undefined) apiRef.current = {
setValue: changeHandler
};
if (modal && modal.current !== undefined) modal.current.change = changeHandler;
}, [apiRef, modal, changeHandler]);
(0, _react.useEffect)(() => {
if (!modal || !modal.current || !(0, _isType.isObj)(modal.current)) return;
modal.current.value = value;
}, [modal, value]);
const [mountedOptions, setMountedOptions] = (0, _react.useState)(false);
(0, _react.useLayoutEffect)(() => {
const options = [];
_react.Children.map(children, child => {
const value = child.props.value;
if (!(0, _isType.isStr)(value)) throw new Error('value of child element must be a string');
for (let option of options) {
if (option.value === value) throw new Error("duplicate value: ".concat(value));
}
options.push({
value,
element: child
});
});
setOptions(options);
setMountedOptions(true);
}, [children]);
const [initedDefaultValue, setInitedDefaultValue] = (0, _react.useState)(false);
const initDefaultValue = (0, _react.useCallback)(() => {
if (initedDefaultValue) return;
let newValue = null;
for (let child of childrenArr) {
if (child.props.selected) {
newValue = child.props.value;
break;
}
}
if (newValue === null && options.length) newValue = options[0].value;
setInitedDefaultValue(true);
changeHandler(newValue);
}, [initedDefaultValue, childrenArr, options, changeHandler]);
const initMultiDefaultValue = (0, _react.useCallback)(() => {
if (initedDefaultValue) return;
let selected = [];
for (let child of childrenArr) {
if (child.props.selected) selected.push(child.props.value);
}
setInitedDefaultValue(true);
changeHandler(selected);
}, [initedDefaultValue, childrenArr, changeHandler]);
(0, _react.useLayoutEffect)(() => {
if (!mountedOptions) return;
if (multi) initMultiDefaultValue();else initDefaultValue();
}, [options, mountedOptions, multi]);
const bodyContent = (0, _react.useMemo)(() => {
const getMultiBody = () => {
const selectedOptions = [];
let i = 0;
for (let option of options) {
if (!value.includes(option.value)) continue;
selectedOptions.push( /*#__PURE__*/(0, _react.cloneElement)(option.element, _objectSpread(_objectSpread({}, option.element.props), {}, {
key: i++,
place: 'multi-body'
})));
}
return selectedOptions.length ? /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
className: "prefect9-select__body-multi",
children: selectedOptions
}) : /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
className: "prefect9-select__body-multi__empty",
children: "Nothing is selected"
});
};
const getBody = () => {
let result = null;
for (let option of options) {
if (option.value !== value) continue;
result = /*#__PURE__*/(0, _react.cloneElement)(option.element, _objectSpread(_objectSpread({}, option.element.props), {}, {
place: 'body'
}));
break;
}
return result;
};
return multi ? getMultiBody() : getBody();
}, [value, options, multi]);
const modalContent = (0, _react.useMemo)(() => {
const result = [];
let i = 0;
for (let option of options) {
let optionSelected = false;
if (multi && value.includes(option.value)) optionSelected = true;
if (!multi && value === option.value) optionSelected = true;
result.push( /*#__PURE__*/(0, _react.cloneElement)(option.element, _objectSpread(_objectSpread({}, option.element.props), {}, {
key: i++,
place: 'modal',
selected: optionSelected
})));
}
return result;
}, [value, options]);
const [showed, setShowed] = (0, _react.useState)(false);
(0, _react.useEffect)(() => {
if (!ref || !ref.current) return;
const container = ref.current.querySelector('.prefect9-select__container');
const show = () => {
if (modal.current.showed) return;
modal.current.showed = true;
setShowed(true);
};
modal.current.show = show;
const hide = () => {
if (!modal.current.showed) return;
modal.current.showed = false;
setShowed(false);
};
modal.current.hide = hide;
const clickModalOptionHandler = optionValue => {
if (multi) {
const active = modal.current.value.includes(optionValue);
let newValue;
if (active) newValue = modal.current.value.filter(selectedValue => selectedValue !== optionValue);else {
newValue = [];
for (let selectedValue of modal.current.value) newValue.push(selectedValue);
newValue.push(optionValue);
}
modal.current.change(newValue);
} else modal.current.change(optionValue);
hide();
};
const clickHandler = e => {
const target = e.target;
const nowShowed = modal.current.showed;
const disabled = modal.current.disabled;
const selectParent = target.closest('.prefect9-select__container');
const isSelect = target.classList.contains('prefect9-select__container') && target === container || selectParent && selectParent === container;
if (isSelect) {
e.preventDefault();
if (disabled) return;
const bodyParent = target.closest('.prefect9-select__body');
const isBody = target.classList.contains('prefect9-select__body') || bodyParent;
const labelParent = target.closest('.prefect9-field__label');
const isLabel = target.classList.contains('prefect9-field__label') || labelParent;
const optionMultiParent = target.closest('.prefect9-select__option-multi');
const isOptionMultiParent = target.classList.contains('prefect9-select__option-multi') || optionMultiParent;
if (isOptionMultiParent) {
let option = optionMultiParent;
if (!option) option = target;
clickModalOptionHandler(option.getAttribute('data-value'));
hide();
} else if ((isBody || isLabel) && !nowShowed) show();else if ((isBody || isLabel) && nowShowed) hide();
const modalOptionParent = target.closest('.prefect9-select__option-modal');
const isModalOption = target.classList.contains('prefect9-select__option-modal') || modalOptionParent;
if (isModalOption) {
let option = modalOptionParent;
if (!option) option = target;
const optionValue = option.getAttribute('data-value');
clickModalOptionHandler(optionValue);
}
} else if (nowShowed) hide();
};
window.addEventListener('click', clickHandler);
return () => window.removeEventListener('click', clickHandler);
}, [ref, modal]);
(0, _react.useEffect)(() => {
if (!ref || !ref.current) return;
if (!(0, _isType.isObj)(modal) || !(0, _isType.isObj)(modal.current)) return;
if ((0, _isType.isTrue)(disabled)) {
modal.current.disabled = true;
if (modal.current.showed) modal.current.hide();
} else modal.current.disabled = false;
}, [ref, modal, disabled]);
const modalApi = (0, _react.useRef)({
paddingTop: 0
});
const [modalPosition, setModalPosition] = (0, _react.useState)({
top: 0,
left: 0,
width: 0,
paddingTop: 0
});
(0, _react.useEffect)(() => {
if (!modalApi || !modalApi.current) return;
let paddingTop = parseFloat(modalPaddingTop);
if (!isFinite(paddingTop)) paddingTop = 4;
modalApi.current.paddingTop = paddingTop;
if ((0, _isType.isFunc)(modalApi.current.resize)) modalApi.current.resize();
}, [modalApi, modalPaddingTop]);
(0, _react.useEffect)(() => {
if (!ref || !ref.current) return;
if (!modalApi || !modalApi.current) return;
const body = ref.current.querySelector('.prefect9-select__body');
if (!body) throw new Error('Body of select not found');
const resize = () => {
const bodySize = body.getBoundingClientRect();
const scrollTop = document.body.scrollTop;
if (!isFinite(scrollTop)) throw new Error('Scroll top is not number');
const modalLeft = bodySize.x;
const modalTop = bodySize.y + bodySize.height + modalApi.current.paddingTop;
const modalWidth = bodySize.width;
setModalPosition(state => _objectSpread(_objectSpread({}, state), {}, {
left: modalLeft,
top: modalTop,
width: modalWidth
}));
};
resize();
window.addEventListener('resize', resize, {
passive: true
});
window.addEventListener('scroll', resize, {
passive: true
});
modalApi.current.resize = resize;
return () => {
if (window) window.removeEventListener('resize', resize, {
passive: true
});
if (window) window.removeEventListener('scroll', resize, {
passive: true
});
};
}, [ref, modalApi]);
(0, _react.useEffect)(() => {
if (!modalApi || !modalApi.current || !(0, _isType.isFunc)(modalApi.current.resize)) return;
if (!showed) return;
modalApi.current.resize();
}, [showed, modalApi]);
const className = (0, _react.useMemo)(() => {
const result = ['prefect9-field', 'prefect9-select'];
if (multi) result.push('prefect9-select__multi');
if (showed) result.push('prefect9-select__active');
if ((0, _isType.isStr)(fieldClassName)) result.push(fieldClassName);
if ((0, _isType.isTrue)(disabled)) result.push('prefect9-select__disabled');
return result.join(' ');
}, [showed, fieldClassName]);
return /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
ref: ref,
className: className,
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
className: "prefect9-select__container",
children: [label ? /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
className: "prefect9-field__label",
children: label
}) : null, /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
className: "prefect9-field__container prefect9-field-ico__right prefect9-select__body",
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
className: "prefect9-field__input",
children: bodyContent
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_icons.default.ArrowBottom, {
className: "prefect9-ico"
})]
}), /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
className: "prefect9-field__modal",
style: {
left: "".concat(modalPosition.left, "px"),
top: "".concat(modalPosition.top, "px"),
width: "".concat(modalPosition.width, "px")
},
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Scroll.default, {
className: "prefect9-select__modal-options",
children: modalContent
})
})]
})
});
});
var _default = exports.default = Select;