UNPKG

@gpa-gemstone/react-forms

Version:
275 lines (274 loc) 13.7 kB
"use strict"; // ****************************************************************************************************** // DateTimePickerBase.tsx - Gbtc // // Copyright © 2020, Grid Protection Alliance. All Rights Reserved. // // Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See // the NOTICE file distributed with this work for additional information regarding copyright ownership. // The GPA licenses this file to you under the MIT License (MIT), the "License"; you may not use this // file except in compliance with the License. You may obtain a copy of the License at: // // http://opensource.org/licenses/MIT // // Unless agreed to in writing, the subject software distributed under the License is distributed on an // "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the // License for the specific language governing permissions and limitations. // // Code Modification History: // ---------------------------------------------------------------------------------------------------- // 07/02/2025 - Preston Crawford // Generated original version of source code. // // ****************************************************************************************************** var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getInputWidth = void 0; exports.default = DateTimePickerBase; exports.getBoxFormat = getBoxFormat; var React = require("react"); var moment = require("moment"); var DateTimePopup_1 = require("./DateTimePopup"); var helper_functions_1 = require("@gpa-gemstone/helper-functions"); var ToolTip_1 = require("../ToolTip"); var gpa_symbols_1 = require("@gpa-gemstone/gpa-symbols"); /** * Component that allows a user to pick a date or datetime. */ function DateTimePickerBase(props) { var inputRef = React.useRef(null); var popupRef = React.useRef(null); var divRef = React.useRef(null); var width = (0, helper_functions_1.useGetContainerPosition)(inputRef).width; var _a = React.useState(false), isTextOverflowing = _a[0], setIsTextOverflowing = _a[1]; var _b = React.useState(false), isInputHovered = _b[0], setIsInputHovered = _b[1]; var overflowGUID = React.useState((0, helper_functions_1.CreateGuid)())[0]; var _c = React.useState(false), showOverlay = _c[0], setShowOverlay = _c[1]; // Formats for displaying dates in the input box and storing in the record. var boxFormat = getBoxFormat(props.Type, props.Accuracy); var recordFormat = props.Format !== undefined ? props.Format : "YYYY-MM-DD" + (props.Type === undefined || props.Type === 'date' ? "" : "[T]HH:mm:ss.SSS[Z]"); // Parse the date from the record. var parse = function (r) { return moment(props.Record[props.Field], recordFormat); }; var helpGuid = React.useState((0, helper_functions_1.CreateGuid)())[0]; var _d = React.useState(false), showHelp = _d[0], setShowHelp = _d[1]; // Adds a buffer between the outside props and what the box is reading to prevent box overwriting every render with a keystroke var _e = React.useState(parse(props.Record).format(boxFormat)), boxRecord = _e[0], setBoxRecord = _e[1]; var _f = React.useState(parse(props.Record)), pickerRecord = _f[0], setPickerRecord = _f[1]; var _g = React.useState(""), feedbackMessage = _g[0], setFeedbackMessage = _g[1]; var _h = React.useState(0), top = _h[0], setTop = _h[1]; var _j = React.useState(0), left = _j[0], setLeft = _j[1]; var allowNull = React.useMemo(function () { var _a; return (_a = props.AllowEmpty) !== null && _a !== void 0 ? _a : false; }, [props.AllowEmpty]); React.useEffect(function () { if (props.Record[props.Field] !== null) { setPickerRecord(parse(props.Record)); setBoxRecord(parse(props.Record).format(boxFormat)); } else { setPickerRecord(undefined); setBoxRecord(''); } }, [props.Record]); React.useLayoutEffect(function () { var node = (divRef.current !== null ? (0, helper_functions_1.GetNodeSize)(divRef.current) : { top: top, left: left, height: 0, width: 0 }); if (node.height === 0 && node.width === 0) { setLeft(0); setTop(-9999); return; } setLeft(node.left + 0.5 * node.width); setTop(node.top + node.height + 10); }); //Effect to set top and left on a scroll event React.useEffect(function () { function updatePosition() { if (divRef.current != null) { var node = (0, helper_functions_1.GetNodeSize)(divRef.current); setLeft(node.left + 0.5 * node.width); setTop(node.top + node.height + 10); } } document.addEventListener('scroll', updatePosition, true); window.addEventListener('resize', updatePosition); updatePosition(); // Initial update return function () { document.removeEventListener('scroll', updatePosition, true); window.removeEventListener('resize', updatePosition); }; }, []); //Effect to handle click events on the window React.useEffect(function () { if (showOverlay) { window.addEventListener('click', onWindowClick); return function () { window.removeEventListener('click', onWindowClick); }; } }, [props.Record, props.Field, boxFormat, showOverlay]); function setPickerAndRecord(arg) { var _a, _b; setPickerRecord(arg); if (allowNull && arg === undefined && props.Record[props.Field] !== null) props.Setter(__assign(__assign({}, props.Record), (_a = {}, _a[props.Field] = null, _a))); else if ((arg != undefined && validateDate(arg)) && props.Record[props.Field].toString() !== arg.format(recordFormat)) props.Setter(__assign(__assign({}, props.Record), (_b = {}, _b[props.Field] = arg.format(recordFormat), _b))); } // Handle clicks outside the component. function onWindowClick(evt) { var _a, _b; // if the click is inside the input-container or inside the popup, bail if (divRef.current == null || popupRef.current == null || ((_a = divRef.current) === null || _a === void 0 ? void 0 : _a.contains(evt.target)) || ((_b = popupRef.current) === null || _b === void 0 ? void 0 : _b.contains(evt.target))) return; setShowOverlay(false); if (props.Record[props.Field] !== null) { setPickerAndRecord(parse(props.Record)); setBoxRecord(parse(props.Record).format(boxFormat)); } else { setPickerAndRecord(undefined); setBoxRecord(''); } } function getFeedbackMessage() { if (feedbackMessage.length != 0) { return feedbackMessage; } else if (props.Feedback == null || props.Feedback.length == 0) { return "".concat(props.Field.toString(), " is a required field."); } else { return props.Feedback; } } function validateDate(date) { var minStartDate = props.MinDate != null ? props.MinDate.startOf('day') : moment("1753-01-01", "YYYY-MM-DD").startOf('day'); if (allowNull && date == null) { setFeedbackMessage(""); return true; } else if (date == null || !date.isValid()) { setFeedbackMessage("Please enter a valid date."); return false; } else if (date.clone().startOf('day').isBefore(minStartDate)) { setFeedbackMessage("Date must be on or after ".concat(minStartDate.format("MM-DD-YYYY"))); return false; } else { setFeedbackMessage(""); return true; } } // Variables for rendering labels and help icons. var showLabel = props.Label !== ""; var showHelpIcon = props.Help !== undefined; var label = props.Label === undefined ? props.Field : props.Label; var step = props.Accuracy === 'millisecond' ? '0.001' : (props.Accuracy === 'minute' ? '60' : '1'); var IsValid = function () { if (feedbackMessage.length > 0) return false; return props.Valid(props.Field); }; function valueChange(value) { var _a, _b; var date = (value === '') ? undefined : moment(value, boxFormat); var validDate = validateDate(date); if (allowNull && value === '') { props.Setter(__assign(__assign({}, props.Record), (_a = {}, _a[props.Field] = null, _a))); setPickerAndRecord(undefined); } else if (validDate) { props.Setter(__assign(__assign({}, props.Record), (_b = {}, _b[props.Field] = moment(value, boxFormat).format(recordFormat), _b))); setPickerAndRecord(moment(value, boxFormat)); } else { setPickerAndRecord(undefined); } setBoxRecord(value); } React.useEffect(function () { if (inputRef.current == null) return; var inputWidth = (0, exports.getInputWidth)(inputRef.current, boxRecord, step); setIsTextOverflowing(inputWidth > width); }, [width, boxRecord, step]); return (React.createElement("div", { className: "form-group", ref: divRef }, showHelpIcon || showLabel ? React.createElement("label", { className: "d-flex align-items-center" }, React.createElement("span", null, showLabel ? label : ''), showHelpIcon && (React.createElement("span", { className: "ml-2 d-flex align-items-center", onMouseEnter: function () { return setShowHelp(true); }, onMouseLeave: function () { return setShowHelp(false); }, "data-tooltip": helpGuid }, React.createElement(gpa_symbols_1.ReactIcons.QuestionMark, { Color: "var(--info)", Size: 20 })))) : null, showHelpIcon ? React.createElement(ToolTip_1.default, { Show: showHelp, Target: helpGuid, Class: "info", Position: "bottom" }, props.Help) : null, React.createElement("input", { className: "gpa-gemstone-datetime form-control ".concat(IsValid() ? '' : 'is-invalid'), type: props.Type === undefined ? 'date' : props.Type, onChange: function (evt) { valueChange(evt.target.value); }, onFocus: function () { return setShowOverlay(true); }, value: boxRecord, disabled: props.Disabled === undefined ? false : props.Disabled, onClick: function (e) { return e.preventDefault(); }, step: step, ref: inputRef, "data-tooltip": overflowGUID, onMouseOver: function () { return setIsInputHovered(true); }, onMouseOut: function () { return setIsInputHovered(false); } }), React.createElement("div", { className: "invalid-feedback" }, getFeedbackMessage()), React.createElement(ToolTip_1.default, { Show: isTextOverflowing && isInputHovered, Target: overflowGUID }, props.Format != null ? moment(boxRecord).format(props.Format) : boxRecord), React.createElement(DateTimePopup_1.default, { ref: popupRef, Setter: function (d) { setPickerAndRecord(d); if (props.Type === 'date') setShowOverlay(false); }, Show: showOverlay, DateTime: pickerRecord, Top: top, Center: left, Type: props.Type === undefined ? 'date' : props.Type, Accuracy: props.Accuracy }))); } function getBoxFormat(type, accuracy) { var dateTime = type !== null && type !== void 0 ? type : 'date'; var timeUnit = accuracy !== null && accuracy !== void 0 ? accuracy : 'second'; if (dateTime === 'time') { if (timeUnit === 'minute') { return "HH:mm"; } else if (timeUnit === 'second') { return "HH:mm:ss"; } else { return "HH:mm:ss.SSS"; } } else if (dateTime === 'datetime-local') { if (timeUnit === 'minute') { return "YYYY-MM-DD[T]HH:mm"; } else if (timeUnit === 'second') { return "YYYY-MM-DD[T]HH:mm:ss"; } else { return "YYYY-MM-DD[T]HH:mm:ss.SSS"; } } else { return "YYYY-MM-DD"; } } var getInputWidth = function (inputRef, currentValue, currentStep) { var computedStyle = window.getComputedStyle(inputRef); var dummyInput = document.createElement('input'); dummyInput.type = inputRef.type; dummyInput.value = currentValue; dummyInput.step = currentStep; dummyInput.style.font = computedStyle.font; dummyInput.style.fontSize = computedStyle.fontSize; dummyInput.style.fontFamily = computedStyle.fontFamily; dummyInput.style.fontWeight = computedStyle.fontWeight; dummyInput.style.letterSpacing = computedStyle.letterSpacing; dummyInput.style.whiteSpace = 'nowrap'; // Position it off-screen and hide it so it doesn't affect layout dummyInput.style.position = 'absolute'; dummyInput.style.visibility = 'hidden'; document.body.appendChild(dummyInput); var width = dummyInput.getBoundingClientRect().width; // Clean up document.body.removeChild(dummyInput); return width; }; exports.getInputWidth = getInputWidth;