UNPKG

@prefect9/ui

Version:

UI React components

548 lines (543 loc) 23.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; require("core-js/modules/es.array.includes.js"); require("core-js/modules/es.parse-int.js"); require("core-js/modules/es.regexp.exec.js"); require("core-js/modules/es.string.includes.js"); require("core-js/modules/web.dom-collections.iterator.js"); var _react = require("react"); var _server = require("react-dom/server"); var _isType = require("@prefect9/is-type"); var _datetime = _interopRequireDefault(require("@prefect9/datetime")); var _icons = _interopRequireDefault(require("../../icons")); var _jsxRuntime = require("react/jsx-runtime"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function Modal(_ref) { let { resolve, reject, from, to } = _ref; const ref = (0, _react.useRef)(null); (0, _react.useEffect)(() => { if (!ref || !ref.current) return; const modal = ref.current; let nowSelected = { from, to }; const title = document.createElement('div'); title.classList.add('prefect9-modal-calendar__modal-title'); title.innerText = 'Date'; const iconContainer = document.createElement('div'); const iconData = (0, _server.renderToStaticMarkup)( /*#__PURE__*/(0, _jsxRuntime.jsx)(_icons.default.Close, { className: "prefect9-modal-calendar__modal-close" })); iconContainer.innerHTML = iconData; const icon = iconContainer.children[0]; icon.addEventListener('click', () => resolve('close')); const fieldsContainer = document.createElement('div'); fieldsContainer.classList.add('prefect9-modal-calendar__modal-fields'); let fields = {}; const renderField = (id, labelText, date) => { const result = {}; const container = document.createElement('div'); const label = document.createElement('label'); label.setAttribute('for', id); label.innerText = labelText; const input = document.createElement('input'); input.setAttribute('id', id); input.setAttribute('autocomplete', 'off'); input.value = date.toDateInterfaceStr(); container.append(label); container.append(input); let previousDate = input.value; const setDate = date => { input.value = date.toDateInterfaceStr(); if (previousDate !== input.value) { input.classList.remove('prefect9-modal-calendar__modal-field__updated'); setTimeout(() => input.classList.add('prefect9-modal-calendar__modal-field__updated'), 50); } previousDate = input.value; }; let scheme = { day: [], month: [], year: [] }; const digitsKeys = ['Digit0', 'Digit1', 'Digit2', 'Digit3', 'Digit4', 'Digit5', 'Digit6', 'Digit7', 'Digit8', 'Digit9']; const renderDay = () => { const day = scheme.day; let value = ''; if (day.length === 1 && [4, 5, 6, 7, 8, 9].includes(day[0])) value = "0".concat(day[0]);else value = day.join(''); const length = value.length; for (let i = 0; i < 2 - length; i++) value += '_'; let keys = []; if (length === 0) keys = [...digitsKeys, 'Tab', 'Space'];else if (length === 1 && day[0] === 3) keys = ['Digit0', 'Digit1', 'Tab', 'Space', 'Backspace'];else if (length === 1 && [0, 1, 2].includes(day[0])) keys = [...digitsKeys, 'Tab', 'Space', 'Backspace']; return { length, value, filled: length === 2, keys }; }; const renderMonth = () => { const month = scheme.month; let value = ''; if (month.length === 1 && [2, 3, 4, 5, 6, 7, 8, 9].includes(month[0])) value = "0".concat(month[0]);else value = month.join(''); const length = value.length; for (let i = 0; i < 2 - length; i++) value += '_'; let keys = []; if (length === 0) keys = [...digitsKeys, 'Tab', 'Space', 'Backspace'];else if (length === 1 && month[0] === 1) keys = ['Digit0', 'Digit1', 'Digit2', 'Tab', 'Space', 'Backspace'];else if (length === 1 && month[0] === 0) keys = [...digitsKeys, 'Tab', 'Space', 'Backspace']; return { length, value, filled: length === 2, keys }; }; const renderYear = () => { const year = scheme.year; let value = year.join(''); const length = value.length; for (let i = 0; i < 4 - length; i++) value += '_'; let keys = []; if (length === 0) keys = ['Digit1', 'Digit2', 'Backspace'];else if (length === 1 && year[0] === 1) keys = ['Digit8', 'Digit9', 'Backspace'];else if (length === 1 && year[0] === 2) keys = ['Digit0', 'Digit1', 'Backspace'];else if (length === 2 || length === 3) keys = [...digitsKeys, 'Backspace'];else keys = ['Backspace']; return { length, value, filled: length === 4, keys }; }; const renderScheme = () => { const startedPositions = { day: 0, month: 3, year: 6 }; const day = renderDay(); const month = renderMonth(); const year = renderYear(); input.value = "".concat(day.value, ".").concat(month.value, ".").concat(year.value); let cursorPosition = 0; if (!day.filled) cursorPosition = startedPositions.day + day.length;else if (!month.filled) cursorPosition = startedPositions.month + month.length;else cursorPosition = startedPositions.year + year.length; input.setSelectionRange(cursorPosition, cursorPosition); if (day.filled && month.filled && year.filled) { if (_datetime.default.isDate(year.value, month.value, day.value)) { let fieldName = 'from'; if (result === fields.to) fieldName = 'to'; const newDay = new _datetime.default(year.value, month.value, day.value); let newFrom, newTo; if (fieldName === 'from') { if (newDay.smallThen(nowSelected.to)) { newFrom = newDay; newTo = nowSelected.to; } else { newFrom = nowSelected.to; newTo = newDay; } } else if (fieldName === 'to') { if (newDay.bigThen(nowSelected.from)) { newFrom = nowSelected.from; newTo = newDay; } else { newFrom = newDay; newTo = nowSelected.from; } } nowSelected = { from: newFrom, to: newTo }; scheme = { day: [], month: [], year: [] }; input.blur(); updateFieldsDate(); updateSelectedCalendar(); } else { scheme = { day: [], month: [], year: [] }; renderScheme(); } } }; const focus = e => { if (e && (0, _isType.isFunc)(e.preventDefault)) e.preventDefault(); input.focus(); renderScheme(); }; input.addEventListener('focus', focus); input.addEventListener('mousedown', focus, { passive: false }); input.addEventListener('contextmenu', focus, { passive: false }); input.addEventListener('blur', () => { scheme = { day: [], month: [], year: [] }; input.value = previousDate; }); input.addEventListener('keydown', e => { e.preventDefault(); const keyCode = e.code; let keysCodes; let nowSection; const day = renderDay(); const month = renderMonth(); const year = renderYear(); if (!day.filled) { keysCodes = day.keys; nowSection = 'day'; } else if (!month.filled) { keysCodes = month.keys; nowSection = 'month'; } else { keysCodes = year.keys; nowSection = 'year'; } if (!keysCodes.includes(keyCode)) return; if (digitsKeys.includes(keyCode)) { const parseDigit = /^Digit([0-9]{1})$/.exec(keyCode); if (!parseDigit) return; const digit = parseInt(parseDigit[1]); scheme[nowSection].push(digit); renderScheme(); } else if (keyCode === 'Backspace') { if (year.length) scheme.year.splice(-1);else if (month.length) scheme.month.splice(-1);else if (day.length) scheme.day.splice(-1); renderScheme(); } }); result.setValue = value => input.value = value; result.focus = focus; result.container = container; result.setDate = setDate; return result; }; fields = { from: renderField('prefect9_modal_calendar_from', 'From', nowSelected.from), to: renderField('prefect9_modal_calendar_to', 'To', nowSelected.to) }; const fieldsSeparator = document.createElement('div'); fieldsSeparator.innerText = '-'; fieldsContainer.append(fields.from.container); fieldsContainer.append(fieldsSeparator); fieldsContainer.append(fields.to.container); const updateFieldsDate = () => { fields.from.setDate(nowSelected.from); fields.to.setDate(nowSelected.to); }; const weekDays = { scheme: [1, 2, 3, 4, 5, 6, 0], names: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'], weekends: [0, 6] }; const weekDaysContainer = document.createElement('div'); weekDaysContainer.classList.add('prefect9-modal-calendar__modal-days-names'); for (let schemeIndex in weekDays.scheme) { const weekDayNumber = weekDays.scheme[schemeIndex]; const weekDay = document.createElement('div'); weekDay.innerText = weekDays.names[weekDayNumber]; if (weekDays.weekends.includes(weekDayNumber)) weekDay.classList.add('prefect9-modal-calendar__modal-day-weekend'); weekDaysContainer.append(weekDay); } const calendarContainer = document.createElement('div'); calendarContainer.classList.add('prefect9-modal-calendar__modal-days'); let calendarHeight = 0; const applyBtn = document.createElement('div'); applyBtn.classList.add('prefect9-modal-calendar__modal-btn'); applyBtn.innerText = 'Apply'; applyBtn.addEventListener('click', () => resolve(nowSelected)); const fixedMonth = {}; fixedMonth.current = null; const drawMonth = (month, previous, next) => { const result = {}; const container = document.createElement('div'); container.classList.add('prefect9-modal-calendar__modal-month'); result.top = 0; result.fixedType = null; result.previous = previous; if (result.previous) result.previous.setNext(result); result.next = next; if (result.next) result.next.setPrevious(result); result.setNext = next => { if (next && result.next) throw new Error('element already have next month'); result.next = next; return result; }; result.setPrevious = previous => { if (previous && result.previous) throw new Error('element already have prevoius month'); result.previous = previous; return result; }; const name = document.createElement('div'); name.classList.add('prefect9-modal-calendar__modal-month__name'); name.innerText = month.getMonthName('different'); const daysContainer = document.createElement('div'); daysContainer.classList.add('prefect9-modal-calendar__modal-month__days'); const days = []; const firstMonthDate = month.getFirstDayOfMonth(); const { weekDay: firstWeekDay, month: monthNumber, year } = firstMonthDate.getDate(); const lastMonthDate = month.getLastDayOfMonth(); const { day: daysInMonth } = lastMonthDate.getDate(); // Отрисовка отступов перед первым днем месяца for (let weekDayNumber of weekDays.scheme) { if (weekDayNumber === firstWeekDay) break; const dayPadding = document.createElement('div'); daysContainer.append(dayPadding); } // Отрисовка дней месяца for (let i = 1; i <= daysInMonth; i++) { const dayObj = {}; const day = new _datetime.default(year, monthNumber, i); const { weekDay } = day.getDate(); dayObj.date = day; const dayContainer = document.createElement('div'); dayContainer.classList.add('prefect9-modal-calendar__modal-month__day'); if (weekDays.weekends.includes(weekDay)) dayContainer.classList.add('prefect9-modal-calendar__modal-day-weekend'); dayContainer.innerHTML = "<div>".concat(i, "</div>"); dayObj.container = dayContainer; dayContainer.addEventListener('click', e => { e.preventDefault(); clickOnDay(day); }); dayContainer.addEventListener('mouseenter', () => hoverOnDay(day), { passive: false }); dayContainer.addEventListener('mouseleave', () => blurDay(), { passive: false }); daysContainer.append(dayContainer); days.push(dayObj); dayObj.setRange = (from, to) => { let inRange = false; if (day.isEqualDate(from)) inRange = dayContainer.classList.add('prefect9-modal-calendar__modal-month__day__start-range') || true;else dayContainer.classList.remove('prefect9-modal-calendar__modal-month__day__start-range'); if (day.isEqualDate(to)) inRange = dayContainer.classList.add('prefect9-modal-calendar__modal-month__day__end-range') || true;else dayContainer.classList.remove('prefect9-modal-calendar__modal-month__day__end-range'); if (day.bigThen(from) && day.smallThen(to)) inRange = dayContainer.classList.add('prefect9-modal-calendar__modal-month__day__range') || true; if (!inRange) dayObj.unSelect(); }; dayObj.setActive = date => { if (day.isEqualDate(date)) { dayContainer.classList.add('prefect9-modal-calendar__modal-month__day__start-range'); dayContainer.classList.add('prefect9-modal-calendar__modal-month__day__end-range'); } else dayObj.unSelect(); }; dayObj.unSelect = () => { dayContainer.classList.remove('prefect9-modal-calendar__modal-month__day__start-range'); dayContainer.classList.remove('prefect9-modal-calendar__modal-month__day__end-range'); dayContainer.classList.remove('prefect9-modal-calendar__modal-month__day__range'); }; } container.append(name); container.append(daysContainer); calendarContainer.append(container); const setTopRelative = top => { if (!(0, _isType.isNum)(top)) throw new Error('top must be number'); if (result.top === top) return result; result.top = top; container.style.top = "".concat(top, "px"); return result; }; result.updateTopChilds = function () { let direction = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; //console.log(`> update top childs ${result.month}_${result.year} (d: ${direction})`) if ((result.fixedType === 'fixed' || result.fixedType === 'afterFixed') && result.next && (direction === null || direction === 'fromPrevious')) result.next.updateTop('fromPrevious'); if ((result.fixedType === 'fixed' || result.fixedType === 'beforeFixed') && result.previous && (direction === null || direction === 'fromNext')) result.previous.updateTop('fromNext'); return result; }; result.updateHeight = function () { let direction = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; //console.log(`> update height ${result.month}_${result.year} (d: ${direction})`) const newHeight = container.getBoundingClientRect().height; if (newHeight === result.height) return result; result.height = newHeight; return result.updateTop(direction); }; result.updateTop = function () { let direction = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; //console.log(`> update top ${result.month}_${result.year} (d: ${direction}, ft: ${result.fixedType}, pt: ${result.previous ? result.previous.top : 'NF'}, ph: ${result.previous ? result.previous.height : 'NF'})`) if (result.fixedType === 'afterFixed' && result.previous) setTopRelative(result.previous.top + result.previous.height); if (result.fixedType === 'beforeFixed' && result.next) setTopRelative(result.next.top - result.height); return result.updateTopChilds(direction).updateHidden(); }; result.updateHidden = () => { result.hiddenTop = result.top + result.height <= 0; result.hiddenBottom = result.top >= calendarHeight; //console.log(`> update hidden ${result.month}_${result.year} (ft: ${result.fixedType}, ht: ${result.hiddenTop}, hb: ${result.hiddenBottom}, t: ${result.top})`) if (result.hiddenBottom && result.next) result.next.destroy(); if (result.hiddenTop && result.previous) result.previous.destroy(); if ((result.fixedType === 'fixed' || result.fixedType === 'beforeFixed') && !result.hiddenTop && !result.previous) drawMonth(month.getPreviousMonth(), null, result).setFixed('beforeFixed').updateHeight('fromNext').setSelected(result.selected, 'fromNext'); if ((result.fixedType === 'fixed' || result.fixedType === 'afterFixed') && !result.hiddenBottom && !result.next) drawMonth(month.getNextMonth(), result).setFixed('afterFixed').updateHeight('fromPrevious').setSelected(result.selected, 'fromPrevious'); return result; }; result.setFixed = value => { result.fixedType = value; if (value === 'fixed') fixedMonth.current = result; return result; }; result.setTop = top => { if (result.fixedType !== 'fixed') throw new Error('You can set absolute top only for fixed month'); //console.log(`> set top ${result.month}_${result.year}`) return setTopRelative(top).updateTopChilds().updateHidden(); }; result.destroy = () => { if (result.fixedType === 'afterFixed' && result.next) result.next.destroy(); if (result.fixedType === 'beforeFixed' && result.previous) result.previous.destroy(); if (result.fixedType === 'fixed' && result.previous && result.previous.hiddenBottom) { result.setFixed('afterFixed'); result.previous.setFixed('fixed'); if (result.next) result.next.destroy(); } else if (result.fixedType === 'fixed' && result.next && result.next.hiddenTop) { result.setFixed('beforeFixed'); result.next.setFixed('fixed'); if (result.previous) result.previous.destroy(); } container.remove(); if (result.previous) result.previous.setNext(null); if (result.next) result.next.setPrevious(null); }; result.selected = null; result.setSelected = function (data) { let direction = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; //console.log(`> set selected ${result.month}_${result.year}`) if (data !== null && !(0, _isType.isObj)(data)) throw new Error('invalid setSelected data'); result.selected = data; if (!data) for (let day of days) day.unSelect();else if (data.type === 'range') for (let day of days) day.setRange(data.from, data.to);else if (data.type === 'selecting') for (let day of days) day.setActive(data.date);else if (data.type === 'selecting_hover') { for (let day of days) { if (data.date.smallThen(data.hover)) day.setRange(data.date, data.hover);else day.setRange(data.hover, data.date); } } if (result.next && (direction === null || direction === 'fromPrevious')) result.next.setSelected(data, 'fromPrevious'); if (result.previous && (direction === null || direction === 'fromNext')) result.previous.setSelected(data, 'fromNext'); }; result.container = container; result.date = month; result.month = monthNumber; result.year = year; result.height = 0; result.hiddenTop = false; result.hiddenBottom = false; //console.log(`> drawed month ${result.month}_${result.year}`) return result; }; let selectedFirstDate = null; let hoverDate = null; const updateSelectedCalendar = () => { if (hoverDate && selectedFirstDate) fixedMonth.current.setSelected({ type: 'selecting_hover', date: selectedFirstDate, hover: hoverDate });else if (selectedFirstDate) fixedMonth.current.setSelected({ type: 'selecting', date: selectedFirstDate });else fixedMonth.current.setSelected({ type: 'range', from: nowSelected.from, to: nowSelected.to }); }; const clickOnDay = day => { if (!selectedFirstDate) selectedFirstDate = day;else { let newDateFrom = selectedFirstDate; let newDateTo = day; if (newDateFrom.bigThen(newDateTo)) { let temp = newDateTo; newDateTo = newDateFrom; newDateFrom = temp; } selectedFirstDate = null; hoverDate = null; nowSelected = { from: newDateFrom, to: newDateTo }; updateFieldsDate(); } updateSelectedCalendar(); }; const hoverOnDay = day => { hoverDate = day; updateSelectedCalendar(); }; const blurDay = () => { hoverDate = null; updateSelectedCalendar(); }; const clickOnDocument = e => { if (!selectedFirstDate) return; const target = e.target; const abortRange = () => { selectedFirstDate = null; updateSelectedCalendar(); }; const modalParent = target.closest('.prefect9-modal-calendar__modal'); const isModal = target.classList.contains('prefect9-modal-calendar__modal') && target === ref.current || modalParent && modalParent === ref.current; if (!isModal) return abortRange(); const dayParent = target.closest('.prefect9-modal-calendar__modal-month__day'); const isDay = target.classList.contains('prefect9-modal-calendar__modal-month__day') || dayParent; if (!isDay) return abortRange(); }; window.addEventListener('click', clickOnDocument); drawMonth(nowSelected.from).setFixed('fixed'); updateSelectedCalendar(); modal.innerHTML = ''; modal.append(title); modal.append(iconContainer); modal.append(fieldsContainer); modal.append(weekDaysContainer); modal.append(calendarContainer); modal.append(applyBtn); const windowResize = e => { calendarHeight = calendarContainer.getBoundingClientRect().height; fixedMonth.current.updateHeight(); }; windowResize(); window.addEventListener('resize', windowResize, { passive: true }); const mouseWheel = e => { e.preventDefault(); const deltaY = e.deltaY; fixedMonth.current.setTop(fixedMonth.current.top + -1 * deltaY); }; calendarContainer.addEventListener('wheel', mouseWheel, { passive: false }); return () => { window.removeEventListener('click', clickOnDocument); window.removeEventListener('resize', windowResize, { passive: true }); calendarContainer.removeEventListener('wheel', mouseWheel, { passive: false }); }; }, [ref, resolve, reject, from, to]); return /*#__PURE__*/(0, _jsxRuntime.jsx)("div", { ref: ref, className: "prefect9-modal-calendar__modal" }); } var _default = exports.default = Modal;