@gpa-gemstone/react-forms
Version:
React Form modules for gpa webapps
273 lines (272 loc) • 13.6 kB
JavaScript
;
// ******************************************************************************************************
// 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;
var _c;
setPickerRecord(arg);
if (allowNull && arg == null && props.Record[props.Field] != null)
props.Setter(__assign(__assign({}, props.Record), (_a = {}, _a[props.Field] = null, _a)));
else if ((arg != null && validateDate(arg)) &&
((_c = props.Record[props.Field]) === null || _c === void 0 ? void 0 : _c.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));
}
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: "top" }, 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;