@prefect9/ui
Version:
UI React components
548 lines (543 loc) • 23.9 kB
JavaScript
;
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;