UNPKG

@prefect9/ui

Version:

UI React components

334 lines (333 loc) 14.7 kB
"use strict"; 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;