@mackin.com/styleguide 9.0.0-beta.1 → 9.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +60 -78
- package/index.js +855 -974
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -6,10 +6,10 @@ var proRegularSvgIcons = require('@fortawesome/pro-regular-svg-icons');
|
|
|
6
6
|
var proSolidSvgIcons = require('@fortawesome/pro-solid-svg-icons');
|
|
7
7
|
var proLightSvgIcons = require('@fortawesome/pro-light-svg-icons');
|
|
8
8
|
var reactFontawesome = require('@fortawesome/react-fontawesome');
|
|
9
|
-
var lodash = require('lodash');
|
|
10
|
-
var dateFns = require('date-fns');
|
|
11
9
|
var nanoid = require('nanoid');
|
|
10
|
+
var dateFns = require('date-fns');
|
|
12
11
|
var reactDom = require('react-dom');
|
|
12
|
+
var lodash = require('lodash');
|
|
13
13
|
var reactTinyPopover = require('react-tiny-popover');
|
|
14
14
|
var reactRouterDom = require('react-router-dom');
|
|
15
15
|
var ReactSlider = require('react-slider');
|
|
@@ -602,51 +602,22 @@ const useAccordianState = (count, openIndex) => {
|
|
|
602
602
|
];
|
|
603
603
|
};
|
|
604
604
|
|
|
605
|
-
const
|
|
606
|
-
const
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
return newValue;
|
|
620
|
-
};
|
|
621
|
-
/** @deprecated Use DateInput, NumberInput, or TextInput instead. */
|
|
622
|
-
const Input = React__namespace.forwardRef((props, ref) => {
|
|
623
|
-
var _a, _b, _c;
|
|
624
|
-
const [localValue, setLocalValue] = React__namespace.useState(formatLocalValue(props.value, props.type));
|
|
625
|
-
const debounceMs = (_a = props.debounceMs) !== null && _a !== void 0 ? _a : DEFAULT_DEBOUNCE_MS;
|
|
626
|
-
const autoComplete = (_b = props.autoComplete) !== null && _b !== void 0 ? _b : 'off';
|
|
627
|
-
const vars = React__namespace.useRef({
|
|
628
|
-
wrappedOnChange: (props.onChange && debounceMs) ? lodash.debounce((value, name) => {
|
|
629
|
-
var _a;
|
|
630
|
-
(_a = props.onChange) === null || _a === void 0 ? void 0 : _a.call(props, value, name);
|
|
631
|
-
}, debounceMs) : undefined,
|
|
632
|
-
focused: false
|
|
633
|
-
});
|
|
634
|
-
const outerOnChange = (_c = vars.current.wrappedOnChange) !== null && _c !== void 0 ? _c : props.onChange;
|
|
635
|
-
const trySyncLocalValue = () => {
|
|
636
|
-
if (vars.current.focused) {
|
|
637
|
-
return;
|
|
638
|
-
}
|
|
639
|
-
if (props.value === localValue) {
|
|
640
|
-
return;
|
|
641
|
-
}
|
|
642
|
-
const newValue = formatLocalValue(props.value, props.type);
|
|
643
|
-
setLocalValue(newValue);
|
|
644
|
-
};
|
|
645
|
-
React__namespace.useEffect(() => {
|
|
646
|
-
trySyncLocalValue();
|
|
647
|
-
}, [props.value]);
|
|
605
|
+
const InputErrorDisplay = (props) => {
|
|
606
|
+
const theme = useThemeSafely();
|
|
607
|
+
return (React__namespace.createElement(Text, { className: css.css({
|
|
608
|
+
minHeight: theme.controls.inputErrorMinHeight,
|
|
609
|
+
lineHeight: theme.controls.inputErrorMinHeight,
|
|
610
|
+
color: theme.colors.negative
|
|
611
|
+
}), smaller: true, noPad: true }, props.error));
|
|
612
|
+
};
|
|
613
|
+
|
|
614
|
+
const defaultMaxLength$1 = 100;
|
|
615
|
+
const BaseInput = React__namespace.forwardRef((props, ref) => {
|
|
616
|
+
var _a, _b;
|
|
648
617
|
const theme = useThemeSafely();
|
|
618
|
+
const { rightControl, round, wrapperClassName, showErrorDisplay } = props, nativeProps = __rest(props, ["rightControl", "round", "wrapperClassName", "showErrorDisplay"]);
|
|
649
619
|
const inputStyles = css.css({
|
|
620
|
+
backgroundColor: theme.colors.bg,
|
|
650
621
|
fontFamily: theme.fonts.family,
|
|
651
622
|
fontSize: theme.fonts.size,
|
|
652
623
|
width: '100%',
|
|
@@ -671,7 +642,7 @@ const Input = React__namespace.forwardRef((props, ref) => {
|
|
|
671
642
|
boxShadow: theme.controls.focusOutlineRequiredShadow
|
|
672
643
|
}
|
|
673
644
|
},
|
|
674
|
-
}, props.round &&
|
|
645
|
+
}, props.round && {
|
|
675
646
|
borderRadius: theme.controls.roundRadius,
|
|
676
647
|
paddingLeft: `calc(${theme.controls.padding} * 2)`,
|
|
677
648
|
paddingRight: `calc(${theme.controls.padding} * 2)`
|
|
@@ -682,100 +653,19 @@ const Input = React__namespace.forwardRef((props, ref) => {
|
|
|
682
653
|
':focus': {
|
|
683
654
|
outline: 'none',
|
|
684
655
|
boxShadow: 'none'
|
|
656
|
+
},
|
|
657
|
+
// FF fix to hide spinner on number elements
|
|
658
|
+
appearance: props.type === 'number' ? 'none' : undefined,
|
|
659
|
+
'::-webkit-outer-spin-button': {
|
|
660
|
+
appearance: 'none'
|
|
661
|
+
},
|
|
662
|
+
'::-webkit-inner-spin-button': {
|
|
663
|
+
appearance: 'none'
|
|
685
664
|
}
|
|
686
|
-
}, props.rightControl &&
|
|
665
|
+
}, props.rightControl && {
|
|
687
666
|
paddingRight: theme.controls.height
|
|
688
667
|
});
|
|
689
|
-
|
|
690
|
-
const onFocus = (e) => {
|
|
691
|
-
var _a;
|
|
692
|
-
vars.current.focused = true;
|
|
693
|
-
(_a = props.onFocus) === null || _a === void 0 ? void 0 : _a.call(props, e);
|
|
694
|
-
};
|
|
695
|
-
const onBlur = (e) => {
|
|
696
|
-
var _a;
|
|
697
|
-
vars.current.focused = false;
|
|
698
|
-
trySyncLocalValue();
|
|
699
|
-
(_a = props.onBlur) === null || _a === void 0 ? void 0 : _a.call(props, e);
|
|
700
|
-
};
|
|
701
|
-
let localOnChange;
|
|
702
|
-
if (props.type === 'number') {
|
|
703
|
-
localOnChange = e => {
|
|
704
|
-
const value = e.target.value;
|
|
705
|
-
if (NUMBER_REGEX.test(value)) {
|
|
706
|
-
let numValue = parseFloat(value);
|
|
707
|
-
if (props.min && numValue < props.min) {
|
|
708
|
-
numValue = props.min;
|
|
709
|
-
}
|
|
710
|
-
if (props.max && numValue > props.max) {
|
|
711
|
-
numValue = props.max;
|
|
712
|
-
}
|
|
713
|
-
setLocalValue(numValue);
|
|
714
|
-
outerOnChange === null || outerOnChange === void 0 ? void 0 : outerOnChange(numValue, props.name, e);
|
|
715
|
-
}
|
|
716
|
-
else if (!value) {
|
|
717
|
-
setLocalValue(value);
|
|
718
|
-
outerOnChange === null || outerOnChange === void 0 ? void 0 : outerOnChange(undefined, props.name, e);
|
|
719
|
-
}
|
|
720
|
-
};
|
|
721
|
-
}
|
|
722
|
-
else if (props.type === 'date') {
|
|
723
|
-
localOnChange = e => {
|
|
724
|
-
const value = e.target.value;
|
|
725
|
-
setLocalValue(value);
|
|
726
|
-
if (outerOnChange) {
|
|
727
|
-
const dateParts = DATE_REGEX.exec(value);
|
|
728
|
-
if (!dateParts) {
|
|
729
|
-
outerOnChange(undefined, props.name, e);
|
|
730
|
-
}
|
|
731
|
-
else {
|
|
732
|
-
const year = parseInt(dateParts[3], 10);
|
|
733
|
-
const month = parseInt(dateParts[1], 10);
|
|
734
|
-
const day = parseInt(dateParts[2], 10);
|
|
735
|
-
if (dateFns.isExists(year, month, day)) {
|
|
736
|
-
let ms = new Date(year, month - 1, day).valueOf();
|
|
737
|
-
if (props.min && ms < props.min) {
|
|
738
|
-
ms = props.min;
|
|
739
|
-
}
|
|
740
|
-
if (props.max && ms > props.max) {
|
|
741
|
-
ms = props.max;
|
|
742
|
-
}
|
|
743
|
-
outerOnChange(ms, props.name, e);
|
|
744
|
-
}
|
|
745
|
-
else {
|
|
746
|
-
outerOnChange(undefined, props.name, e);
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
};
|
|
751
|
-
}
|
|
752
|
-
else {
|
|
753
|
-
localOnChange = e => {
|
|
754
|
-
const value = e.target.value;
|
|
755
|
-
setLocalValue(value);
|
|
756
|
-
outerOnChange === null || outerOnChange === void 0 ? void 0 : outerOnChange(value, props.name, e);
|
|
757
|
-
};
|
|
758
|
-
}
|
|
759
|
-
if (props.type === 'number') {
|
|
760
|
-
inputElement = React__namespace.createElement("input", Object.assign({}, props.inputAriaAttributes, { autoFocus: props.autoFocus, ref: ref, name: props.name, pattern: props.pattern, style: props.style, autoComplete: autoComplete, tabIndex: props.readOnly ? -1 : undefined, readOnly: props.readOnly,
|
|
761
|
-
// set fixed default to defeat pasting stupid numbers
|
|
762
|
-
maxLength: 50, min: props.min, max: props.max, required: props.required, disabled: props.disabled, id: props.id, className: css.cx(inputStyles, props.inputClassName), placeholder: props.placeholder, type: "number", value: localValue, onFocus: onFocus, onBlur: onBlur, onChange: localOnChange, onKeyDown: props.onKeyDown, onKeyUp: props.onKeyUp, onKeyPress: props.onKeyPress }));
|
|
763
|
-
}
|
|
764
|
-
else if (props.type === 'date') {
|
|
765
|
-
inputElement = React__namespace.createElement("input", Object.assign({}, props.inputAriaAttributes, { autoFocus: props.autoFocus, ref: ref, name: props.name, pattern: props.pattern, style: props.style, autoComplete: autoComplete, tabIndex: props.readOnly ? -1 : undefined, readOnly: props.readOnly, maxLength: 10, required: props.required, disabled: props.disabled, id: props.id, className: css.cx(inputStyles, props.inputClassName), placeholder: props.placeholder, type: "text", value: localValue, onFocus: onFocus, onBlur: onBlur, onChange: localOnChange, onKeyDown: props.onKeyDown, onKeyUp: props.onKeyUp, onKeyPress: props.onKeyPress }));
|
|
766
|
-
}
|
|
767
|
-
else if (props.type === 'textarea') {
|
|
768
|
-
inputElement = React__namespace.createElement("textarea", Object.assign({}, props.inputAriaAttributes, { autoFocus: props.autoFocus, ref: ref, name: props.name, style: props.style, rows: props.rows || 10, autoComplete: autoComplete, tabIndex: props.readOnly ? -1 : undefined, readOnly: props.readOnly, maxLength: props.maxLength || DEFAULT_MAX_LENGTH, required: props.required, disabled: props.disabled, id: props.id, className: css.cx(css.css `
|
|
769
|
-
${inputStyles}
|
|
770
|
-
max-width: 100%;
|
|
771
|
-
min-height: ${theme.controls.height};
|
|
772
|
-
padding-top: 0.75rem;
|
|
773
|
-
height:auto;`, props.inputClassName), placeholder: props.placeholder, value: localValue, onFocus: onFocus, onBlur: onBlur, onChange: localOnChange, onKeyDown: props.onKeyDown, onKeyUp: props.onKeyUp, onKeyPress: props.onKeyPress }));
|
|
774
|
-
}
|
|
775
|
-
else {
|
|
776
|
-
// text, password, email, and url
|
|
777
|
-
inputElement = React__namespace.createElement("input", Object.assign({}, props.inputAriaAttributes, { autoFocus: props.autoFocus, ref: ref, name: props.name, pattern: props.pattern, style: props.style, autoComplete: autoComplete, tabIndex: props.readOnly ? -1 : undefined, readOnly: props.readOnly, maxLength: props.maxLength || DEFAULT_MAX_LENGTH, required: props.required, disabled: props.disabled, id: props.id, className: css.cx(inputStyles, props.inputClassName), placeholder: props.placeholder, type: props.type, value: localValue, onFocus: onFocus, onBlur: onBlur, onChange: localOnChange, onKeyDown: props.onKeyDown, onKeyUp: props.onKeyUp, onKeyPress: props.onKeyPress }));
|
|
778
|
-
}
|
|
668
|
+
const inputElement = React__namespace.createElement("input", Object.assign({}, nativeProps, { ref: ref, autoComplete: (_a = nativeProps.autoComplete) !== null && _a !== void 0 ? _a : 'off', tabIndex: nativeProps.readOnly ? -1 : nativeProps.tabIndex, maxLength: nativeProps.maxLength || defaultMaxLength$1, className: css.cx(inputStyles, props.className) }));
|
|
779
669
|
const inputWrapperStyles = css.css `
|
|
780
670
|
width:100%;
|
|
781
671
|
${props.rightControl && `
|
|
@@ -793,187 +683,362 @@ const Input = React__namespace.forwardRef((props, ref) => {
|
|
|
793
683
|
right: calc(${theme.controls.padding} * 2);
|
|
794
684
|
`}
|
|
795
685
|
`;
|
|
796
|
-
return (React__namespace.createElement("div", { className: css.
|
|
797
|
-
|
|
798
|
-
|
|
686
|
+
return (React__namespace.createElement("div", { className: css.css({
|
|
687
|
+
width: '100%',
|
|
688
|
+
label: 'BaseInput'
|
|
689
|
+
}) },
|
|
690
|
+
React__namespace.createElement("div", { className: css.cx('input', inputWrapperStyles, wrapperClassName) },
|
|
691
|
+
inputElement,
|
|
692
|
+
props.rightControl && (React__namespace.createElement("div", { className: rightControlStyles }, props.rightControl))),
|
|
693
|
+
((_b = props.showErrorDisplay) !== null && _b !== void 0 ? _b : true) && React__namespace.createElement(InputErrorDisplay, { error: props.readOnly ? undefined : props.error })));
|
|
799
694
|
});
|
|
800
695
|
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
const
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
padding: 0;
|
|
808
|
-
list-style-type: none;
|
|
809
|
-
${props.altRowColor && `
|
|
810
|
-
> .listItem:nth-of-type(even) {
|
|
811
|
-
background-color: ${theme.colors.lightBg};
|
|
812
|
-
}
|
|
813
|
-
`}
|
|
814
|
-
${props.noLines && `
|
|
815
|
-
> .listItem {
|
|
816
|
-
border-bottom: none;
|
|
817
|
-
}
|
|
818
|
-
`}
|
|
819
|
-
`;
|
|
820
|
-
return (React__namespace.createElement("ul", Object.assign({}, listProps, { ref: ref, className: css.cx('list', listStyles, props.className) }), children));
|
|
821
|
-
});
|
|
822
|
-
const ListItem = (props) => {
|
|
823
|
-
const liProps = __rest(props, ["variant", "noContentStyling"]);
|
|
824
|
-
const theme = useThemeSafely();
|
|
825
|
-
const itemStyles = css.css `
|
|
826
|
-
border-bottom: ${theme.controls.border};
|
|
827
|
-
&:last-child {
|
|
828
|
-
border-bottom: none;
|
|
696
|
+
/** useEffect but ignores the first call on component mount. */
|
|
697
|
+
const useIgnoreMount = (effect, deps) => {
|
|
698
|
+
const mounted = React__default['default'].useRef(false);
|
|
699
|
+
React__default['default'].useEffect(() => {
|
|
700
|
+
if (!mounted.current) {
|
|
701
|
+
mounted.current = true;
|
|
829
702
|
}
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
padding: `calc(${theme.controls.padding} * 1.5)`
|
|
833
|
-
}, props.variant === 'full' && {
|
|
834
|
-
padding: 0
|
|
835
|
-
}, !props.noContentStyling && {
|
|
836
|
-
'>.button': {
|
|
837
|
-
padding: `calc(${theme.controls.padding} * 1.5)`,
|
|
838
|
-
width: '100%',
|
|
839
|
-
border: 'none'
|
|
840
|
-
},
|
|
841
|
-
'>.omniLink': {
|
|
842
|
-
paddingLeft: `calc(${theme.controls.padding} * 1.5)`,
|
|
843
|
-
paddingRight: `calc(${theme.controls.padding} * 1.5)`,
|
|
844
|
-
width: '100%',
|
|
845
|
-
border: 'none',
|
|
846
|
-
lineHeight: theme.controls.height
|
|
847
|
-
},
|
|
848
|
-
'>.button:not(:focus), >.omniLink:not(:focus)': {
|
|
849
|
-
boxShadow: 'none',
|
|
850
|
-
},
|
|
851
|
-
'>.omniLink:not(.omniLink--iconBlock)': {
|
|
852
|
-
display: 'block'
|
|
703
|
+
else {
|
|
704
|
+
effect();
|
|
853
705
|
}
|
|
854
|
-
});
|
|
855
|
-
return (React__namespace.createElement("li", Object.assign({}, liProps, { className: css.cx('listItem', itemStyles, props.className) }),
|
|
856
|
-
React__namespace.createElement("div", { className: css.css(contentStyle) }, props.children)));
|
|
857
|
-
};
|
|
858
|
-
|
|
859
|
-
const TabLocker = (props) => {
|
|
860
|
-
const tabLocker = React__namespace.useRef(null);
|
|
861
|
-
return (React__namespace.createElement("div", { className: "tabLocker", style: props.style, ref: tabLocker, onKeyDown: e => {
|
|
862
|
-
var _a, _b;
|
|
863
|
-
if (props.disabled) {
|
|
864
|
-
return;
|
|
865
|
-
}
|
|
866
|
-
if (e.key === 'Tab') {
|
|
867
|
-
e.preventDefault();
|
|
868
|
-
e.stopPropagation();
|
|
869
|
-
const tabElements = Array.from((_b = (_a = tabLocker.current) === null || _a === void 0 ? void 0 : _a.querySelectorAll('a, button, input, textarea, select, details,[tabindex]:not([tabindex="-1"])')) !== null && _b !== void 0 ? _b : []).filter(el => !el.hasAttribute('disabled'));
|
|
870
|
-
if (tabElements.length) {
|
|
871
|
-
const direction = e.shiftKey ? -1 : 1;
|
|
872
|
-
const index = tabElements.findIndex(x => x === document.activeElement);
|
|
873
|
-
if (index === undefined) {
|
|
874
|
-
tabElements[0].focus();
|
|
875
|
-
}
|
|
876
|
-
else if (index === tabElements.length - 1 && direction === 1) {
|
|
877
|
-
tabElements[0].focus();
|
|
878
|
-
}
|
|
879
|
-
else if (index === 0 && direction === -1) {
|
|
880
|
-
tabElements[tabElements.length - 1].focus();
|
|
881
|
-
}
|
|
882
|
-
else {
|
|
883
|
-
tabElements[index + direction].focus();
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
}
|
|
887
|
-
} }, props.children));
|
|
706
|
+
}, deps);
|
|
888
707
|
};
|
|
889
708
|
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
709
|
+
const tryClampRange = (value, min, max) => {
|
|
710
|
+
if (value === undefined) {
|
|
711
|
+
return value;
|
|
712
|
+
}
|
|
713
|
+
if (isNaN(value)) {
|
|
714
|
+
return undefined;
|
|
715
|
+
}
|
|
716
|
+
if (min !== undefined && value < min) {
|
|
717
|
+
return min;
|
|
718
|
+
}
|
|
719
|
+
if (max !== undefined && value > max) {
|
|
720
|
+
return max;
|
|
721
|
+
}
|
|
722
|
+
return value;
|
|
723
|
+
};
|
|
724
|
+
const isOutOfRange = (value, min, max) => {
|
|
725
|
+
if (min !== undefined && value < min) {
|
|
726
|
+
return true;
|
|
727
|
+
}
|
|
728
|
+
if (max !== undefined && value > max) {
|
|
729
|
+
return true;
|
|
730
|
+
}
|
|
731
|
+
return false;
|
|
732
|
+
};
|
|
733
|
+
const getStepDecimalPlaces = (step) => {
|
|
894
734
|
var _a, _b;
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
const input = React__namespace.useRef(null);
|
|
898
|
-
const list = React__namespace.useRef(null);
|
|
899
|
-
const [values, setValues] = React__namespace.useState([]);
|
|
900
|
-
const showValues = React__namespace.useMemo(() => values.length > 0, [values]);
|
|
901
|
-
const maxShowValues = (_a = p.maxShownValues) !== null && _a !== void 0 ? _a : defaultMaxShownValues;
|
|
902
|
-
const shownValues = React__namespace.useMemo(() => {
|
|
903
|
-
if (!p.allowScroll) {
|
|
904
|
-
return values.slice(0, maxShowValues);
|
|
905
|
-
}
|
|
906
|
-
return values.slice();
|
|
907
|
-
}, [values]);
|
|
908
|
-
const onChangeForOptions = React__namespace.useRef(lodash.debounce((value) => {
|
|
909
|
-
if (!p.minChars || value.length >= p.minChars) {
|
|
910
|
-
p.getOptions(value)
|
|
911
|
-
.then(vals => {
|
|
912
|
-
setValues(vals);
|
|
913
|
-
}).catch(err => {
|
|
914
|
-
// ignore it
|
|
915
|
-
});
|
|
916
|
-
}
|
|
917
|
-
else {
|
|
918
|
-
setValues([]);
|
|
919
|
-
}
|
|
920
|
-
}, (_b = p.getOptionsDebounceMs) !== null && _b !== void 0 ? _b : 0, { leading: false, trailing: true }));
|
|
921
|
-
const getNextTabElement = (fromIndex, direction) => {
|
|
922
|
-
var _a, _b, _c;
|
|
923
|
-
if (fromIndex === -1) {
|
|
924
|
-
let buttonIndex = 0;
|
|
925
|
-
if (direction === -1) {
|
|
926
|
-
buttonIndex = shownValues.length - 1;
|
|
927
|
-
}
|
|
928
|
-
return (_a = list.current) === null || _a === void 0 ? void 0 : _a.querySelector(`.${buttonMarkerClass}${buttonIndex}`);
|
|
929
|
-
}
|
|
930
|
-
else {
|
|
931
|
-
const nextIndex = fromIndex + direction;
|
|
932
|
-
if (nextIndex >= shownValues.length || nextIndex < 0) {
|
|
933
|
-
return (_b = input.current) !== null && _b !== void 0 ? _b : undefined;
|
|
934
|
-
}
|
|
935
|
-
else {
|
|
936
|
-
return (_c = list.current) === null || _c === void 0 ? void 0 : _c.querySelector(`.${buttonMarkerClass}${nextIndex}`);
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
};
|
|
940
|
-
React__namespace.useEffect(() => {
|
|
941
|
-
const clearItems = () => {
|
|
942
|
-
if (values.length > 0) {
|
|
943
|
-
setValues([]);
|
|
944
|
-
}
|
|
945
|
-
};
|
|
946
|
-
document.addEventListener('click', clearItems);
|
|
947
|
-
return () => {
|
|
948
|
-
document.removeEventListener('click', clearItems);
|
|
949
|
-
};
|
|
950
|
-
}, [values]);
|
|
951
|
-
let listBorderRadius = '';
|
|
952
|
-
if (p.round || theme.controls.borderRadius) {
|
|
953
|
-
listBorderRadius = theme.controls.borderRadius || '0.5rem';
|
|
735
|
+
if (!step) {
|
|
736
|
+
return 0;
|
|
954
737
|
}
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
738
|
+
const strStep = typeof step === 'number' ? step.toString() : step;
|
|
739
|
+
return (_b = (_a = strStep.split('.')[1]) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0;
|
|
740
|
+
};
|
|
741
|
+
const tryClampDecimals = (value, step = 0) => {
|
|
742
|
+
if (value === undefined) {
|
|
743
|
+
return value;
|
|
744
|
+
}
|
|
745
|
+
if (isNaN(value)) {
|
|
746
|
+
return undefined;
|
|
747
|
+
}
|
|
748
|
+
const decimals = getStepDecimalPlaces(step);
|
|
749
|
+
if (decimals === 0) {
|
|
750
|
+
return Math.floor(value);
|
|
751
|
+
}
|
|
752
|
+
return parseFloat(value.toFixed(decimals));
|
|
753
|
+
};
|
|
754
|
+
|
|
755
|
+
/** Common state handling for displaying validation messages with inputs. */
|
|
756
|
+
const useInputValidationMessage = (ref, props) => {
|
|
757
|
+
const [validationError, setValidationError] = React__default['default'].useState('');
|
|
758
|
+
const updateErrorMessage = (customErrorOverride) => {
|
|
759
|
+
var _a;
|
|
760
|
+
const customError = customErrorOverride || props.customError || '';
|
|
761
|
+
// set it OR clear it. either way, update it.
|
|
762
|
+
(_a = ref.current) === null || _a === void 0 ? void 0 : _a.setCustomValidity(customError);
|
|
763
|
+
setValidationError(customError || getValidationMessage(ref.current, props.patternErrorMessage));
|
|
764
|
+
};
|
|
765
|
+
React.useEffect(() => {
|
|
766
|
+
updateErrorMessage();
|
|
767
|
+
}, [props.customError]);
|
|
768
|
+
React__default['default'].useEffect(() => {
|
|
769
|
+
updateErrorMessage();
|
|
770
|
+
}, []);
|
|
771
|
+
return [validationError, updateErrorMessage];
|
|
772
|
+
};
|
|
773
|
+
const getValidationMessage = (element, patternErrorMessage) => {
|
|
774
|
+
var _a;
|
|
775
|
+
if (!element) {
|
|
776
|
+
return '';
|
|
777
|
+
}
|
|
778
|
+
const validity = element.validity;
|
|
779
|
+
if (validity.valid) {
|
|
780
|
+
return '';
|
|
781
|
+
}
|
|
782
|
+
if (validity.customError) {
|
|
783
|
+
return element.validationMessage;
|
|
784
|
+
}
|
|
785
|
+
if (validity.typeMismatch) {
|
|
786
|
+
switch (element.type) {
|
|
787
|
+
case 'url':
|
|
788
|
+
return `Invalid URL.`;
|
|
789
|
+
case 'email':
|
|
790
|
+
return `Invalid email.`;
|
|
791
|
+
default:
|
|
792
|
+
return element.validationMessage;
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
if (element instanceof HTMLInputElement) {
|
|
796
|
+
if (validity.rangeOverflow) {
|
|
797
|
+
return `Must be less than or equal to ${element.max}.`;
|
|
798
|
+
}
|
|
799
|
+
if (validity.rangeUnderflow) {
|
|
800
|
+
return `Must be greater than or equal to ${element.min}.`;
|
|
801
|
+
}
|
|
802
|
+
if (validity.stepMismatch) {
|
|
803
|
+
const decimalPlaces = getStepDecimalPlaces(element.step);
|
|
804
|
+
if (decimalPlaces > 0) {
|
|
805
|
+
const place = decimalPlaces === 1 ? 'place' : 'places';
|
|
806
|
+
return `Limited to ${getStepDecimalPlaces(element.step)} decimal ${place}.`;
|
|
807
|
+
}
|
|
808
|
+
else {
|
|
809
|
+
/*
|
|
810
|
+
step is buggy!
|
|
811
|
+
|
|
812
|
+
at least in Chrome, setting step=5 will cause the browser to mark the field as invalid if the number is not divisible
|
|
813
|
+
by 5. 55 is ok. 50 is ok. 59 is not ok.
|
|
814
|
+
|
|
815
|
+
to make things worse, if you enter an invalid number like 59 with step=5 and then clear the input, Chrome will tell
|
|
816
|
+
you the number 5 is now invalid and should be 4 or 9.
|
|
817
|
+
*/
|
|
818
|
+
return `Must be an integer.`;
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
if (validity.tooShort) {
|
|
823
|
+
return `Must be at least ${((_a = element.minLength) !== null && _a !== void 0 ? _a : 0).toLocaleString()} characters in length.`;
|
|
824
|
+
}
|
|
825
|
+
if (validity.valueMissing) {
|
|
826
|
+
return 'Required.';
|
|
827
|
+
}
|
|
828
|
+
if (validity.patternMismatch && patternErrorMessage) {
|
|
829
|
+
return patternErrorMessage;
|
|
830
|
+
}
|
|
831
|
+
// unhandled. let the browser decide.
|
|
832
|
+
return element.validationMessage;
|
|
833
|
+
};
|
|
834
|
+
|
|
835
|
+
const TextInput = React__namespace.forwardRef((props, ref) => {
|
|
836
|
+
var _a;
|
|
837
|
+
const [localValue, setLocalValue] = React__namespace.useState(props.value);
|
|
838
|
+
const inputRef = (ref !== null && ref !== void 0 ? ref : React__namespace.useRef(null));
|
|
839
|
+
const [validationError, updateErrorMessage] = useInputValidationMessage(inputRef, props);
|
|
840
|
+
const nativeProps = __rest(props, ["emptyString", "onValueChange", "customError", "patternErrorMessage"]);
|
|
841
|
+
useIgnoreMount(() => {
|
|
842
|
+
var _a;
|
|
843
|
+
if ((_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.checkValidity()) {
|
|
844
|
+
props.onValueChange(localValue);
|
|
845
|
+
}
|
|
846
|
+
else {
|
|
847
|
+
props.onValueChange(undefined);
|
|
848
|
+
}
|
|
849
|
+
updateErrorMessage();
|
|
850
|
+
}, [localValue]);
|
|
851
|
+
useIgnoreMount(() => {
|
|
852
|
+
if (document.activeElement !== inputRef.current) {
|
|
853
|
+
setLocalValue(props.value);
|
|
854
|
+
}
|
|
855
|
+
updateErrorMessage();
|
|
856
|
+
}, [props.value]);
|
|
857
|
+
return (React__namespace.createElement(BaseInput, Object.assign({}, nativeProps, { error: validationError, type: (_a = props.type) !== null && _a !== void 0 ? _a : 'text', ref: inputRef, value: localValue !== null && localValue !== void 0 ? localValue : '', onChange: e => {
|
|
858
|
+
var _a;
|
|
859
|
+
setLocalValue(props.emptyString ? e.target.value : e.target.value || undefined);
|
|
860
|
+
(_a = props.onChange) === null || _a === void 0 ? void 0 : _a.call(props, e);
|
|
861
|
+
}, onBlur: e => {
|
|
862
|
+
var _a, _b;
|
|
863
|
+
if (!e.target.checkValidity()) {
|
|
864
|
+
setLocalValue(undefined);
|
|
865
|
+
}
|
|
866
|
+
else if ((_a = props.trim) !== null && _a !== void 0 ? _a : true) {
|
|
867
|
+
setLocalValue(currentValue => {
|
|
868
|
+
return currentValue === null || currentValue === void 0 ? void 0 : currentValue.trim();
|
|
869
|
+
});
|
|
870
|
+
}
|
|
871
|
+
(_b = props.onBlur) === null || _b === void 0 ? void 0 : _b.call(props, e);
|
|
872
|
+
} })));
|
|
873
|
+
});
|
|
874
|
+
|
|
875
|
+
const List = React__namespace.forwardRef((props, ref) => {
|
|
876
|
+
const children = props.items ? props.items.map((item, i) => React__namespace.createElement(ListItem, { key: i }, item)) : props.children;
|
|
877
|
+
const theme = useThemeSafely();
|
|
878
|
+
const listProps = __rest(props, ["altRowColor", "noLines", "items"]);
|
|
879
|
+
const listStyles = css.css `
|
|
880
|
+
margin: 0;
|
|
881
|
+
padding: 0;
|
|
882
|
+
list-style-type: none;
|
|
883
|
+
${props.altRowColor && `
|
|
884
|
+
> .listItem:nth-of-type(even) {
|
|
885
|
+
background-color: ${theme.colors.lightBg};
|
|
886
|
+
}
|
|
887
|
+
`}
|
|
888
|
+
${props.noLines && `
|
|
889
|
+
> .listItem {
|
|
890
|
+
border-bottom: none;
|
|
891
|
+
}
|
|
892
|
+
`}
|
|
893
|
+
`;
|
|
894
|
+
return (React__namespace.createElement("ul", Object.assign({}, listProps, { ref: ref, className: css.cx('list', listStyles, props.className) }), children));
|
|
895
|
+
});
|
|
896
|
+
const ListItem = (props) => {
|
|
897
|
+
const liProps = __rest(props, ["variant", "noContentStyling"]);
|
|
898
|
+
const theme = useThemeSafely();
|
|
899
|
+
const itemStyles = css.css `
|
|
900
|
+
border-bottom: ${theme.controls.border};
|
|
901
|
+
&:last-child {
|
|
902
|
+
border-bottom: none;
|
|
903
|
+
}
|
|
904
|
+
`;
|
|
905
|
+
const contentStyle = css.css({
|
|
906
|
+
padding: `calc(${theme.controls.padding} * 1.5)`
|
|
907
|
+
}, props.variant === 'full' && {
|
|
908
|
+
padding: 0
|
|
909
|
+
}, !props.noContentStyling && {
|
|
910
|
+
'>.button': {
|
|
911
|
+
padding: `calc(${theme.controls.padding} * 1.5)`,
|
|
912
|
+
width: '100%',
|
|
913
|
+
border: 'none'
|
|
914
|
+
},
|
|
915
|
+
'>.omniLink': {
|
|
916
|
+
paddingLeft: `calc(${theme.controls.padding} * 1.5)`,
|
|
917
|
+
paddingRight: `calc(${theme.controls.padding} * 1.5)`,
|
|
918
|
+
width: '100%',
|
|
919
|
+
border: 'none',
|
|
920
|
+
lineHeight: theme.controls.height
|
|
921
|
+
},
|
|
922
|
+
'>.button:not(:focus), >.omniLink:not(:focus)': {
|
|
923
|
+
boxShadow: 'none',
|
|
924
|
+
},
|
|
925
|
+
'>.omniLink:not(.omniLink--iconBlock)': {
|
|
926
|
+
display: 'block'
|
|
927
|
+
}
|
|
928
|
+
});
|
|
929
|
+
return (React__namespace.createElement("li", Object.assign({}, liProps, { className: css.cx('listItem', itemStyles, props.className) }),
|
|
930
|
+
React__namespace.createElement("div", { className: css.css(contentStyle) }, props.children)));
|
|
931
|
+
};
|
|
932
|
+
|
|
933
|
+
const TabLocker = (props) => {
|
|
934
|
+
const tabLocker = React__namespace.useRef(null);
|
|
935
|
+
return (React__namespace.createElement("div", { className: "tabLocker", style: props.style, ref: tabLocker, onKeyDown: e => {
|
|
936
|
+
var _a, _b;
|
|
937
|
+
if (props.disabled) {
|
|
938
|
+
return;
|
|
939
|
+
}
|
|
940
|
+
if (e.key === 'Tab') {
|
|
941
|
+
e.preventDefault();
|
|
942
|
+
e.stopPropagation();
|
|
943
|
+
const tabElements = Array.from((_b = (_a = tabLocker.current) === null || _a === void 0 ? void 0 : _a.querySelectorAll('a, button, input, textarea, select, details,[tabindex]:not([tabindex="-1"])')) !== null && _b !== void 0 ? _b : []).filter(el => !el.hasAttribute('disabled'));
|
|
944
|
+
if (tabElements.length) {
|
|
945
|
+
const direction = e.shiftKey ? -1 : 1;
|
|
946
|
+
const index = tabElements.findIndex(x => x === document.activeElement);
|
|
947
|
+
if (index === undefined) {
|
|
948
|
+
tabElements[0].focus();
|
|
949
|
+
}
|
|
950
|
+
else if (index === tabElements.length - 1 && direction === 1) {
|
|
951
|
+
tabElements[0].focus();
|
|
952
|
+
}
|
|
953
|
+
else if (index === 0 && direction === -1) {
|
|
954
|
+
tabElements[tabElements.length - 1].focus();
|
|
955
|
+
}
|
|
956
|
+
else {
|
|
957
|
+
tabElements[index + direction].focus();
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
} }, props.children));
|
|
962
|
+
};
|
|
963
|
+
|
|
964
|
+
const defaultMaxShownValues = 7;
|
|
965
|
+
const buttonMarkerClass = 'ListItem__button';
|
|
966
|
+
const Autocomplete = (p) => {
|
|
967
|
+
var _a;
|
|
968
|
+
const inputProps = __rest(p, ["value", "className", "inputWrapperClassName", "inputClassName", "maxShownValues", "allowScroll", "options", "onPick"]);
|
|
969
|
+
const theme = useThemeSafely();
|
|
970
|
+
const element = React__namespace.useRef(null);
|
|
971
|
+
const input = React__namespace.useRef(null);
|
|
972
|
+
const list = React__namespace.useRef(null);
|
|
973
|
+
const maxShowValues = (_a = p.maxShownValues) !== null && _a !== void 0 ? _a : defaultMaxShownValues;
|
|
974
|
+
const displayOptions = React__namespace.useMemo(() => {
|
|
975
|
+
if (!p.allowScroll) {
|
|
976
|
+
return p.options.slice(0, maxShowValues);
|
|
977
|
+
}
|
|
978
|
+
return p.options.slice();
|
|
979
|
+
}, [p.options]);
|
|
980
|
+
const getNextTabElement = (fromIndex, direction) => {
|
|
981
|
+
var _a, _b, _c;
|
|
982
|
+
if (fromIndex === -1) {
|
|
983
|
+
let buttonIndex = 0;
|
|
984
|
+
if (direction === -1) {
|
|
985
|
+
buttonIndex = displayOptions.length - 1;
|
|
986
|
+
}
|
|
987
|
+
return (_a = list.current) === null || _a === void 0 ? void 0 : _a.querySelector(`.${buttonMarkerClass}${buttonIndex}`);
|
|
988
|
+
}
|
|
989
|
+
else {
|
|
990
|
+
const nextIndex = fromIndex + direction;
|
|
991
|
+
if (nextIndex >= displayOptions.length || nextIndex < 0) {
|
|
992
|
+
return (_b = input.current) !== null && _b !== void 0 ? _b : undefined;
|
|
993
|
+
}
|
|
994
|
+
else {
|
|
995
|
+
return (_c = list.current) === null || _c === void 0 ? void 0 : _c.querySelector(`.${buttonMarkerClass}${nextIndex}`);
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
};
|
|
999
|
+
React__namespace.useEffect(() => {
|
|
1000
|
+
const clearItems = () => {
|
|
1001
|
+
if (p.options.length) {
|
|
1002
|
+
p.onPick(undefined);
|
|
1003
|
+
}
|
|
1004
|
+
};
|
|
1005
|
+
document.addEventListener('click', clearItems);
|
|
1006
|
+
return () => {
|
|
1007
|
+
document.removeEventListener('click', clearItems);
|
|
1008
|
+
};
|
|
1009
|
+
}, [p.options]);
|
|
1010
|
+
let listBorderRadius = '';
|
|
1011
|
+
if (p.round || theme.controls.borderRadius) {
|
|
1012
|
+
listBorderRadius = theme.controls.borderRadius || '0.5rem';
|
|
1013
|
+
}
|
|
1014
|
+
const onPickValue = (v) => {
|
|
1015
|
+
p.onPick(v);
|
|
1016
|
+
// wait for the re-render. the value will not update if the control has focus
|
|
1017
|
+
setTimeout(() => {
|
|
1018
|
+
var _a;
|
|
1019
|
+
(_a = input.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
1020
|
+
}, 0);
|
|
1021
|
+
};
|
|
1022
|
+
return (React__namespace.createElement("div", { onClick: e => {
|
|
1023
|
+
e.stopPropagation();
|
|
1024
|
+
}, onKeyDown: e => {
|
|
1025
|
+
var _a;
|
|
1026
|
+
if (e.key === 'Escape') {
|
|
1027
|
+
// the TextInput will not respond to outer value changes if it has focus.
|
|
1028
|
+
// here we clear first and then onPickValue will re-focus after all updates.
|
|
1029
|
+
(_a = input.current) === null || _a === void 0 ? void 0 : _a.blur();
|
|
1030
|
+
onPickValue(undefined);
|
|
1031
|
+
}
|
|
1032
|
+
}, ref: element, className: css.cx(css.css({
|
|
1033
|
+
position: 'relative',
|
|
1034
|
+
width: '100%',
|
|
1035
|
+
label: 'Autocomplete'
|
|
1036
|
+
}), p.className, 'autocomplete') },
|
|
1037
|
+
React__namespace.createElement(TabLocker, { disabled: !displayOptions.length, style: { position: 'relative' } },
|
|
1038
|
+
React__namespace.createElement(TextInput, Object.assign({}, inputProps, { showErrorDisplay: false, ref: input, value: p.value, className: p.inputClassName, wrapperClassName: p.inputWrapperClassName, onKeyDown: e => {
|
|
1039
|
+
var _a, _b, _c;
|
|
1040
|
+
if (displayOptions.length) {
|
|
1041
|
+
if (e.key === 'ArrowDown') {
|
|
977
1042
|
e.preventDefault();
|
|
978
1043
|
e.stopPropagation();
|
|
979
1044
|
(_a = getNextTabElement(-1, 1)) === null || _a === void 0 ? void 0 : _a.focus();
|
|
@@ -985,8 +1050,8 @@ const Autocomplete = (p) => {
|
|
|
985
1050
|
}
|
|
986
1051
|
}
|
|
987
1052
|
(_c = p.onKeyDown) === null || _c === void 0 ? void 0 : _c.call(p, e);
|
|
988
|
-
}
|
|
989
|
-
|
|
1053
|
+
} })),
|
|
1054
|
+
!!displayOptions.length && (React__namespace.createElement(List, { ref: list, className: css.cx(css.css({
|
|
990
1055
|
position: 'absolute',
|
|
991
1056
|
width: '100%',
|
|
992
1057
|
border: theme.controls.border,
|
|
@@ -1003,12 +1068,12 @@ const Autocomplete = (p) => {
|
|
|
1003
1068
|
borderBottomRightRadius: listBorderRadius,
|
|
1004
1069
|
borderBottomLeftRadius: listBorderRadius,
|
|
1005
1070
|
}
|
|
1006
|
-
}), p.allowScroll &&
|
|
1071
|
+
}), p.allowScroll && displayOptions.length > maxShowValues && css.css({
|
|
1007
1072
|
overflowY: 'scroll',
|
|
1008
1073
|
maxHeight: `calc(${theme.controls.height} * ${maxShowValues})`
|
|
1009
|
-
})) },
|
|
1010
|
-
|
|
1011
|
-
return (React__namespace.createElement(ListItem, { key:
|
|
1074
|
+
}), p.listClassName) },
|
|
1075
|
+
displayOptions.map((v, listItemIndex) => {
|
|
1076
|
+
return (React__namespace.createElement(ListItem, { key: v, variant: "full", className: p.listItemClassName },
|
|
1012
1077
|
React__namespace.createElement(Button, { onKeyDown: e => {
|
|
1013
1078
|
var _a, _b;
|
|
1014
1079
|
if (e.key === 'ArrowDown') {
|
|
@@ -1021,42 +1086,23 @@ const Autocomplete = (p) => {
|
|
|
1021
1086
|
e.preventDefault();
|
|
1022
1087
|
(_b = getNextTabElement(listItemIndex, -1)) === null || _b === void 0 ? void 0 : _b.focus();
|
|
1023
1088
|
}
|
|
1089
|
+
else if (e.key === 'Enter') {
|
|
1090
|
+
onPickValue(v);
|
|
1091
|
+
}
|
|
1024
1092
|
}, className: css.cx(buttonMarkerClass + listItemIndex, css.css({
|
|
1025
1093
|
borderRadius: 0,
|
|
1026
|
-
})), onClick: () => {
|
|
1027
|
-
|
|
1028
|
-
setValues([]);
|
|
1029
|
-
setTimeout(() => {
|
|
1030
|
-
var _a;
|
|
1031
|
-
// we need to wait until the component is re-rendered.
|
|
1032
|
-
// outside changes to Inputs will be ignored if the component has focus.
|
|
1033
|
-
(_a = input.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
1034
|
-
}, 0);
|
|
1094
|
+
}), p.listItemButtonClassName), onClick: () => {
|
|
1095
|
+
onPickValue(v);
|
|
1035
1096
|
} },
|
|
1036
|
-
React__namespace.createElement(Text, { tag: "div", ellipsis: true, align: "left" },
|
|
1097
|
+
React__namespace.createElement(Text, { tag: "div", ellipsis: true, align: "left" }, v))));
|
|
1037
1098
|
}),
|
|
1038
|
-
!p.allowScroll &&
|
|
1099
|
+
!p.allowScroll && displayOptions.length < p.options.length && (React__namespace.createElement(ListItem, { className: p.listItemClassName },
|
|
1039
1100
|
React__namespace.createElement(Text, { tag: "div", italics: true, align: "center" },
|
|
1040
1101
|
"Showing ",
|
|
1041
|
-
|
|
1102
|
+
displayOptions.length.toLocaleString(),
|
|
1042
1103
|
" of ",
|
|
1043
|
-
|
|
1104
|
+
p.options.length.toLocaleString(),
|
|
1044
1105
|
" results."))))))));
|
|
1045
|
-
};
|
|
1046
|
-
const getAutocompleteValueText = (v) => {
|
|
1047
|
-
if (!v) {
|
|
1048
|
-
return '';
|
|
1049
|
-
}
|
|
1050
|
-
if (typeof v === 'string') {
|
|
1051
|
-
return v;
|
|
1052
|
-
}
|
|
1053
|
-
return v.name;
|
|
1054
|
-
};
|
|
1055
|
-
const getAutocompleteValueId = (v) => {
|
|
1056
|
-
if (typeof v === 'string') {
|
|
1057
|
-
return v;
|
|
1058
|
-
}
|
|
1059
|
-
return v.id;
|
|
1060
1106
|
};
|
|
1061
1107
|
|
|
1062
1108
|
/** @deprecated Use Backdrop2 going forward. */
|
|
@@ -1160,266 +1206,31 @@ const Backdrop$1 = (props) => {
|
|
|
1160
1206
|
}
|
|
1161
1207
|
return () => {
|
|
1162
1208
|
if (backdrop && backdrop.current && !props.allowScroll) {
|
|
1163
|
-
document.body.classList.remove(bodyStyles);
|
|
1164
|
-
}
|
|
1165
|
-
};
|
|
1166
|
-
}, [props.show]);
|
|
1167
|
-
return (React__namespace.createElement("div", { onMouseDown: e => {
|
|
1168
|
-
var _a;
|
|
1169
|
-
e.stopPropagation();
|
|
1170
|
-
e.preventDefault();
|
|
1171
|
-
(_a = props.onClick) === null || _a === void 0 ? void 0 : _a.call(props);
|
|
1172
|
-
}, onClick: e => {
|
|
1173
|
-
e.stopPropagation();
|
|
1174
|
-
e.preventDefault();
|
|
1175
|
-
}, ref: backdrop, className: css.cx('backdrop', styles, props.className) }, props.children));
|
|
1176
|
-
};
|
|
1177
|
-
|
|
1178
|
-
/** useEffect but it will only fire when the actual truthiness of the value changes.
|
|
1179
|
-
* Use for comparing previous states to next states without all the bullshit around useEffect and component mounting.
|
|
1180
|
-
*/
|
|
1181
|
-
const useBooleanChanged = (effect, dep) => {
|
|
1182
|
-
/*
|
|
1183
|
-
Why?
|
|
1184
|
-
useEffect with a dependency array will fire once on mount even though the dependency list doesn't change.
|
|
1185
|
-
Components like Modal need to communicate when their show status changes.
|
|
1186
|
-
useIgnoreMount is not enough because it only ignores the first render and is therefore a kludge.
|
|
1187
|
-
This is what we want regardless of mount status:
|
|
1188
|
-
true > false = Change
|
|
1189
|
-
false > true = Change
|
|
1190
|
-
true > true = No Change
|
|
1191
|
-
false > false = No Change
|
|
1192
|
-
undefined > false = No Change
|
|
1193
|
-
undefined > true = Change
|
|
1194
|
-
*/
|
|
1195
|
-
const lastValue = React.useRef(undefined);
|
|
1196
|
-
React.useEffect(() => {
|
|
1197
|
-
//console.log('[useBooleanChanged] useEffect called with', dep, 'was', lastValue.current ?? 'undefined')
|
|
1198
|
-
if (!!lastValue.current !== !!dep) {
|
|
1199
|
-
const previous = lastValue.current;
|
|
1200
|
-
lastValue.current = dep;
|
|
1201
|
-
effect(!!lastValue.current, !!previous);
|
|
1202
|
-
//console.log('[useBooleanChanged] change called')
|
|
1203
|
-
}
|
|
1204
|
-
}, [dep]);
|
|
1205
|
-
};
|
|
1206
|
-
|
|
1207
|
-
const useLogger = (componentName, enabled) => {
|
|
1208
|
-
return (...messages) => {
|
|
1209
|
-
if (enabled) {
|
|
1210
|
-
// tslint:disable-next-line
|
|
1211
|
-
console.log(`[${componentName}]`, ...messages);
|
|
1212
|
-
}
|
|
1213
|
-
};
|
|
1214
|
-
};
|
|
1215
|
-
|
|
1216
|
-
// Taken from: https://github.com/react-bootstrap/dom-helpers/blob/master/src/scrollbarSize.ts
|
|
1217
|
-
const canUseDom = !!(typeof window !== 'undefined' &&
|
|
1218
|
-
window.document &&
|
|
1219
|
-
window.document.createElement);
|
|
1220
|
-
let size;
|
|
1221
|
-
/** Tells you actual width of the scroll bar. This can vary by browser. */
|
|
1222
|
-
const useScrollbarSize = (recalc) => {
|
|
1223
|
-
if ((!size && size !== 0) || recalc) {
|
|
1224
|
-
if (canUseDom) {
|
|
1225
|
-
const scrollDiv = document.createElement('div');
|
|
1226
|
-
scrollDiv.style.position = 'absolute';
|
|
1227
|
-
scrollDiv.style.top = '-9999px';
|
|
1228
|
-
scrollDiv.style.width = '50px';
|
|
1229
|
-
scrollDiv.style.height = '50px';
|
|
1230
|
-
scrollDiv.style.overflow = 'scroll';
|
|
1231
|
-
document.body.appendChild(scrollDiv);
|
|
1232
|
-
size = scrollDiv.offsetWidth - scrollDiv.clientWidth;
|
|
1233
|
-
document.body.removeChild(scrollDiv);
|
|
1234
|
-
}
|
|
1235
|
-
}
|
|
1236
|
-
return size;
|
|
1237
|
-
};
|
|
1238
|
-
|
|
1239
|
-
/** Add to fixed positioned elements so their contents do not jump around when scrolling is disabled. */
|
|
1240
|
-
const modalScrollFixClassName = 'modal-scroll-fix';
|
|
1241
|
-
const Modal = (p) => {
|
|
1242
|
-
var _a, _b, _c, _d;
|
|
1243
|
-
const backdrop = React.useContext(BackdropContext);
|
|
1244
|
-
const mouseDownElement = React.useRef(undefined);
|
|
1245
|
-
const theme = useThemeSafely();
|
|
1246
|
-
const hasHeader = p.closeButton || p.heading;
|
|
1247
|
-
const contentRef = React__default['default'].useRef(null);
|
|
1248
|
-
const log = useLogger((_a = p.id) !== null && _a !== void 0 ? _a : 'Modal', (_b = p.__debug) !== null && _b !== void 0 ? _b : false);
|
|
1249
|
-
const showing = React.useRef(p.show);
|
|
1250
|
-
const bodyStyles = React.useRef('');
|
|
1251
|
-
const fixedElementStyles = React.useRef('');
|
|
1252
|
-
const addScrollStyles = () => {
|
|
1253
|
-
var _a, _b, _c, _d;
|
|
1254
|
-
if (!bodyStyles.current) {
|
|
1255
|
-
bodyStyles.current = css.css({
|
|
1256
|
-
label: 'ModalBodyOverrides_' + ((_b = (_a = p.id) === null || _a === void 0 ? void 0 : _a.replace(/\s+/, '')) !== null && _b !== void 0 ? _b : nanoid.nanoid()),
|
|
1257
|
-
overflow: 'hidden',
|
|
1258
|
-
paddingRight: `${useScrollbarSize()}px`
|
|
1259
|
-
});
|
|
1260
|
-
log('creating singleton bodyStyles', bodyStyles.current);
|
|
1261
|
-
}
|
|
1262
|
-
if (!fixedElementStyles.current) {
|
|
1263
|
-
fixedElementStyles.current = css.css({
|
|
1264
|
-
label: 'ModalElementOverrides_' + ((_d = (_c = p.id) === null || _c === void 0 ? void 0 : _c.replace(/\s+/, '')) !== null && _d !== void 0 ? _d : nanoid.nanoid()),
|
|
1265
|
-
paddingRight: `${useScrollbarSize()}px`
|
|
1266
|
-
});
|
|
1267
|
-
}
|
|
1268
|
-
document.body.classList.add(bodyStyles.current);
|
|
1269
|
-
Array.from(document.querySelectorAll(`.${modalScrollFixClassName}`)).forEach(e => {
|
|
1270
|
-
e.classList.add(fixedElementStyles.current);
|
|
1271
|
-
});
|
|
1272
|
-
};
|
|
1273
|
-
const tryRemoveScrollStyles = () => {
|
|
1274
|
-
if (bodyStyles.current) {
|
|
1275
|
-
log('removing singleton', bodyStyles.current);
|
|
1276
|
-
document.body.classList.remove(bodyStyles.current);
|
|
1277
|
-
}
|
|
1278
|
-
if (fixedElementStyles.current) {
|
|
1279
|
-
Array.from(document.querySelectorAll(`.${modalScrollFixClassName}`)).forEach(e => {
|
|
1280
|
-
e.classList.remove(fixedElementStyles.current);
|
|
1281
|
-
});
|
|
1282
|
-
}
|
|
1283
|
-
};
|
|
1284
|
-
React.useEffect(() => {
|
|
1285
|
-
log('mounted');
|
|
1286
|
-
return () => {
|
|
1287
|
-
var _a;
|
|
1288
|
-
if (showing.current) {
|
|
1289
|
-
log(`un-mount in progress and this modal is showing. decrement the backdrop and try to remove singleton body styles.`);
|
|
1290
|
-
backdrop.setShow(false, (_a = p.id) !== null && _a !== void 0 ? _a : 'Modal');
|
|
1291
|
-
log('backdrop.setShow', false);
|
|
1292
|
-
tryRemoveScrollStyles();
|
|
1293
|
-
}
|
|
1294
|
-
else {
|
|
1295
|
-
log(`un-mount in progress but this modal is not showing. do nothing with the backdrop.`);
|
|
1296
|
-
}
|
|
1297
|
-
log('un-mounted');
|
|
1298
|
-
};
|
|
1299
|
-
}, []);
|
|
1300
|
-
useBooleanChanged((show, previousShow) => {
|
|
1301
|
-
var _a;
|
|
1302
|
-
log('show changed', `${previousShow !== null && previousShow !== void 0 ? previousShow : 'undefined'} > ${show}`);
|
|
1303
|
-
backdrop.setShow(show, (_a = p.id) !== null && _a !== void 0 ? _a : 'Modal');
|
|
1304
|
-
showing.current = show;
|
|
1305
|
-
log('backdrop.setShow', show);
|
|
1306
|
-
if (show) {
|
|
1307
|
-
log('this modal is showing. adding singleton bodyStyles', bodyStyles.current);
|
|
1308
|
-
addScrollStyles();
|
|
1309
|
-
}
|
|
1310
|
-
else {
|
|
1311
|
-
log('this modal is hiding. try removing singleton bodyStyles');
|
|
1312
|
-
tryRemoveScrollStyles();
|
|
1313
|
-
}
|
|
1314
|
-
}, p.show);
|
|
1315
|
-
React__default['default'].useLayoutEffect(() => {
|
|
1316
|
-
var _a;
|
|
1317
|
-
if (p.show === true) {
|
|
1318
|
-
const focusSelector = (_a = p.focusSelector) !== null && _a !== void 0 ? _a : '.modalCloseButton';
|
|
1319
|
-
// still need to wait for the next tick so the children are all rendered.
|
|
1320
|
-
setTimeout(() => {
|
|
1321
|
-
var _a;
|
|
1322
|
-
const element = (_a = contentRef.current) === null || _a === void 0 ? void 0 : _a.querySelector(focusSelector);
|
|
1323
|
-
element === null || element === void 0 ? void 0 : element.focus();
|
|
1324
|
-
log('set focus', focusSelector);
|
|
1325
|
-
});
|
|
1326
|
-
}
|
|
1327
|
-
}, [p.show]);
|
|
1328
|
-
const modalBodyStyles = css.css({
|
|
1329
|
-
maxHeight: p.scrollable ? undefined : '99vh',
|
|
1330
|
-
overflow: 'hidden',
|
|
1331
|
-
zIndex: theme.zIndexes.modal,
|
|
1332
|
-
cursor: 'default',
|
|
1333
|
-
margin: '1rem',
|
|
1334
|
-
backgroundColor: p.noBackground ? undefined : theme.colors.modalBg,
|
|
1335
|
-
border: p.noBackground ? undefined : theme.controls.border,
|
|
1336
|
-
boxShadow: p.noBackground ? undefined : theme.controls.boxShadow,
|
|
1337
|
-
maxWidth: (_c = p.maxWidth) !== null && _c !== void 0 ? _c : theme.breakpoints.tablet,
|
|
1338
|
-
minWidth: (_d = p.minWidth) !== null && _d !== void 0 ? _d : (hasHeader ? '250px' : undefined),
|
|
1339
|
-
opacity: p.show ? 1 : 0,
|
|
1340
|
-
fontSize: theme.fonts.size,
|
|
1341
|
-
fontFamily: theme.fonts.family,
|
|
1342
|
-
fontWeight: 'normal',
|
|
1343
|
-
'&:focus': {
|
|
1344
|
-
outline: 'none'
|
|
1345
|
-
}
|
|
1346
|
-
});
|
|
1347
|
-
const modalHeaderStyles = css.cx(css.css({
|
|
1348
|
-
display: 'flex',
|
|
1349
|
-
justifyContent: 'space-between',
|
|
1350
|
-
alignItems: 'center',
|
|
1351
|
-
backgroundColor: theme.colors.header,
|
|
1352
|
-
padding: '1rem',
|
|
1353
|
-
color: theme.colors.headerFont
|
|
1354
|
-
}), p.headerClassName);
|
|
1355
|
-
const modalContainerStyles = css.css([{
|
|
1356
|
-
position: 'fixed',
|
|
1357
|
-
height: '100%',
|
|
1358
|
-
width: '100%',
|
|
1359
|
-
backgroundColor: "transparent",
|
|
1360
|
-
display: 'flex',
|
|
1361
|
-
justifyContent: 'center',
|
|
1362
|
-
alignItems: 'center',
|
|
1363
|
-
cursor: p.onClick ? 'pointer' : 'default'
|
|
1364
|
-
}, p.scrollable && {
|
|
1365
|
-
overflowY: 'auto',
|
|
1366
|
-
overflowX: 'hidden',
|
|
1367
|
-
alignItems: 'flex-start'
|
|
1368
|
-
}]);
|
|
1369
|
-
if (p.show) {
|
|
1370
|
-
const backdropContainer = document.getElementById(backdrop.portalId);
|
|
1371
|
-
if (backdropContainer) {
|
|
1372
|
-
return reactDom.createPortal((React__default['default'].createElement("div", { onClick: e => {
|
|
1373
|
-
e.stopPropagation();
|
|
1374
|
-
if (!mouseDownElement.current) {
|
|
1375
|
-
if (p.onClick) {
|
|
1376
|
-
log('backdropContainer onClick');
|
|
1377
|
-
p.onClick();
|
|
1378
|
-
}
|
|
1379
|
-
}
|
|
1380
|
-
mouseDownElement.current = undefined;
|
|
1381
|
-
}, className: css.cx('modalContainer', modalContainerStyles) },
|
|
1382
|
-
React__default['default'].createElement("div", { id: p.id, ref: contentRef, onClick: e => e.stopPropagation(), onMouseDown: e => {
|
|
1383
|
-
mouseDownElement.current = e.target;
|
|
1384
|
-
e.stopPropagation();
|
|
1385
|
-
}, onMouseUp: e => {
|
|
1386
|
-
mouseDownElement.current = undefined;
|
|
1387
|
-
/*
|
|
1388
|
-
MouseDown and MouseUp stopPropagation was added to fix bugs while clicking within the modal.
|
|
1389
|
-
At least in the case of MouseUp, this breaks sliders and the handle grab logic appears to live on window.
|
|
1390
|
-
Turning this off now. Should not cause any issues for MouseUp unlike MouseDown.
|
|
1391
|
-
*/
|
|
1392
|
-
// e.stopPropagation()
|
|
1393
|
-
}, className: css.cx('modalBody', modalBodyStyles, p.className) },
|
|
1394
|
-
React__default['default'].createElement(TabLocker, null,
|
|
1395
|
-
hasHeader && (React__default['default'].createElement("header", { className: css.cx('modalHeader', modalHeaderStyles) },
|
|
1396
|
-
p.heading ? React__default['default'].createElement(Text, { className: css.css({
|
|
1397
|
-
margin: 0,
|
|
1398
|
-
flexGrow: 1
|
|
1399
|
-
}), tag: "h1", bold: true }, p.heading) : React__default['default'].createElement("span", null),
|
|
1400
|
-
p.closeButton && p.onClick ? React__default['default'].createElement(Button, { className: css.cx('modalCloseButton', css.css({
|
|
1401
|
-
color: theme.colors.headerFont,
|
|
1402
|
-
marginLeft: '1rem',
|
|
1403
|
-
backgroundColor: 'transparent'
|
|
1404
|
-
})), variant: "icon", onClick: p.onClick },
|
|
1405
|
-
React__default['default'].createElement(Icon, { id: "close" })) : React__default['default'].createElement("span", null))),
|
|
1406
|
-
p.children)))), backdropContainer);
|
|
1407
|
-
}
|
|
1408
|
-
}
|
|
1409
|
-
return null;
|
|
1209
|
+
document.body.classList.remove(bodyStyles);
|
|
1210
|
+
}
|
|
1211
|
+
};
|
|
1212
|
+
}, [props.show]);
|
|
1213
|
+
return (React__namespace.createElement("div", { onMouseDown: e => {
|
|
1214
|
+
var _a;
|
|
1215
|
+
e.stopPropagation();
|
|
1216
|
+
e.preventDefault();
|
|
1217
|
+
(_a = props.onClick) === null || _a === void 0 ? void 0 : _a.call(props);
|
|
1218
|
+
}, onClick: e => {
|
|
1219
|
+
e.stopPropagation();
|
|
1220
|
+
e.preventDefault();
|
|
1221
|
+
}, ref: backdrop, className: css.cx('backdrop', styles, props.className) }, props.children));
|
|
1410
1222
|
};
|
|
1411
1223
|
|
|
1412
|
-
/**
|
|
1413
|
-
const
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
effect();
|
|
1224
|
+
/** Add to fixed positioned elements so their contents do not jump around when scrolling is disabled. */
|
|
1225
|
+
const modalScrollFixClassName = 'modal-scroll-fix';
|
|
1226
|
+
|
|
1227
|
+
const useLogger = (componentName, enabled) => {
|
|
1228
|
+
return (...messages) => {
|
|
1229
|
+
if (enabled) {
|
|
1230
|
+
// tslint:disable-next-line
|
|
1231
|
+
console.log(`[${componentName}]`, ...messages);
|
|
1421
1232
|
}
|
|
1422
|
-
}
|
|
1233
|
+
};
|
|
1423
1234
|
};
|
|
1424
1235
|
|
|
1425
1236
|
const portalId = 'backdrop';
|
|
@@ -1691,6 +1502,227 @@ const Checkbox = (props) => {
|
|
|
1691
1502
|
props.children)));
|
|
1692
1503
|
};
|
|
1693
1504
|
|
|
1505
|
+
/** useEffect but it will only fire when the actual truthiness of the value changes.
|
|
1506
|
+
* Use for comparing previous states to next states without all the bullshit around useEffect and component mounting.
|
|
1507
|
+
*/
|
|
1508
|
+
const useBooleanChanged = (effect, dep) => {
|
|
1509
|
+
/*
|
|
1510
|
+
Why?
|
|
1511
|
+
useEffect with a dependency array will fire once on mount even though the dependency list doesn't change.
|
|
1512
|
+
Components like Modal need to communicate when their show status changes.
|
|
1513
|
+
useIgnoreMount is not enough because it only ignores the first render and is therefore a kludge.
|
|
1514
|
+
This is what we want regardless of mount status:
|
|
1515
|
+
true > false = Change
|
|
1516
|
+
false > true = Change
|
|
1517
|
+
true > true = No Change
|
|
1518
|
+
false > false = No Change
|
|
1519
|
+
undefined > false = No Change
|
|
1520
|
+
undefined > true = Change
|
|
1521
|
+
*/
|
|
1522
|
+
const lastValue = React.useRef(undefined);
|
|
1523
|
+
React.useEffect(() => {
|
|
1524
|
+
if (!!lastValue.current !== !!dep) {
|
|
1525
|
+
const previous = lastValue.current;
|
|
1526
|
+
lastValue.current = dep;
|
|
1527
|
+
effect(!!lastValue.current, !!previous);
|
|
1528
|
+
}
|
|
1529
|
+
}, [dep]);
|
|
1530
|
+
};
|
|
1531
|
+
|
|
1532
|
+
// Taken from: https://github.com/react-bootstrap/dom-helpers/blob/master/src/scrollbarSize.ts
|
|
1533
|
+
const canUseDom = !!(typeof window !== 'undefined' &&
|
|
1534
|
+
window.document &&
|
|
1535
|
+
window.document.createElement);
|
|
1536
|
+
let size;
|
|
1537
|
+
/** Tells you actual width of the scroll bar. This can vary by browser. */
|
|
1538
|
+
const useScrollbarSize = (recalc) => {
|
|
1539
|
+
if ((!size && size !== 0) || recalc) {
|
|
1540
|
+
if (canUseDom) {
|
|
1541
|
+
const scrollDiv = document.createElement('div');
|
|
1542
|
+
scrollDiv.style.position = 'absolute';
|
|
1543
|
+
scrollDiv.style.top = '-9999px';
|
|
1544
|
+
scrollDiv.style.width = '50px';
|
|
1545
|
+
scrollDiv.style.height = '50px';
|
|
1546
|
+
scrollDiv.style.overflow = 'scroll';
|
|
1547
|
+
document.body.appendChild(scrollDiv);
|
|
1548
|
+
size = scrollDiv.offsetWidth - scrollDiv.clientWidth;
|
|
1549
|
+
document.body.removeChild(scrollDiv);
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
return size;
|
|
1553
|
+
};
|
|
1554
|
+
|
|
1555
|
+
const Modal = (p) => {
|
|
1556
|
+
var _a, _b, _c, _d;
|
|
1557
|
+
const backdrop = React.useContext(BackdropContext);
|
|
1558
|
+
const mouseDownElement = React.useRef(undefined);
|
|
1559
|
+
const theme = useThemeSafely();
|
|
1560
|
+
const hasHeader = p.closeButton || p.heading;
|
|
1561
|
+
const contentRef = React__default['default'].useRef(null);
|
|
1562
|
+
const log = useLogger((_a = p.id) !== null && _a !== void 0 ? _a : 'Modal', (_b = p.__debug) !== null && _b !== void 0 ? _b : false);
|
|
1563
|
+
const showing = React.useRef(p.show);
|
|
1564
|
+
const bodyStyles = React.useRef('');
|
|
1565
|
+
const fixedElementStyles = React.useRef('');
|
|
1566
|
+
const addScrollStyles = () => {
|
|
1567
|
+
var _a, _b, _c, _d;
|
|
1568
|
+
if (!bodyStyles.current) {
|
|
1569
|
+
bodyStyles.current = css.css({
|
|
1570
|
+
label: 'ModalBodyOverrides_' + ((_b = (_a = p.id) === null || _a === void 0 ? void 0 : _a.replace(/\s+/, '')) !== null && _b !== void 0 ? _b : nanoid.nanoid()),
|
|
1571
|
+
overflow: 'hidden',
|
|
1572
|
+
paddingRight: `${useScrollbarSize()}px`
|
|
1573
|
+
});
|
|
1574
|
+
log('creating singleton bodyStyles', bodyStyles.current);
|
|
1575
|
+
}
|
|
1576
|
+
if (!fixedElementStyles.current) {
|
|
1577
|
+
fixedElementStyles.current = css.css({
|
|
1578
|
+
label: 'ModalElementOverrides_' + ((_d = (_c = p.id) === null || _c === void 0 ? void 0 : _c.replace(/\s+/, '')) !== null && _d !== void 0 ? _d : nanoid.nanoid()),
|
|
1579
|
+
paddingRight: `${useScrollbarSize()}px`
|
|
1580
|
+
});
|
|
1581
|
+
}
|
|
1582
|
+
document.body.classList.add(bodyStyles.current);
|
|
1583
|
+
Array.from(document.querySelectorAll(`.${modalScrollFixClassName}`)).forEach(e => {
|
|
1584
|
+
e.classList.add(fixedElementStyles.current);
|
|
1585
|
+
});
|
|
1586
|
+
};
|
|
1587
|
+
const tryRemoveScrollStyles = () => {
|
|
1588
|
+
if (bodyStyles.current) {
|
|
1589
|
+
log('removing singleton', bodyStyles.current);
|
|
1590
|
+
document.body.classList.remove(bodyStyles.current);
|
|
1591
|
+
}
|
|
1592
|
+
if (fixedElementStyles.current) {
|
|
1593
|
+
Array.from(document.querySelectorAll(`.${modalScrollFixClassName}`)).forEach(e => {
|
|
1594
|
+
e.classList.remove(fixedElementStyles.current);
|
|
1595
|
+
});
|
|
1596
|
+
}
|
|
1597
|
+
};
|
|
1598
|
+
React.useEffect(() => {
|
|
1599
|
+
log('mounted');
|
|
1600
|
+
return () => {
|
|
1601
|
+
var _a;
|
|
1602
|
+
if (showing.current) {
|
|
1603
|
+
log(`un-mount in progress and this modal is showing. decrement the backdrop and try to remove singleton body styles.`);
|
|
1604
|
+
backdrop.setShow(false, (_a = p.id) !== null && _a !== void 0 ? _a : 'Modal');
|
|
1605
|
+
log('backdrop.setShow', false);
|
|
1606
|
+
tryRemoveScrollStyles();
|
|
1607
|
+
}
|
|
1608
|
+
else {
|
|
1609
|
+
log(`un-mount in progress but this modal is not showing. do nothing with the backdrop.`);
|
|
1610
|
+
}
|
|
1611
|
+
log('un-mounted');
|
|
1612
|
+
};
|
|
1613
|
+
}, []);
|
|
1614
|
+
useBooleanChanged((show, previousShow) => {
|
|
1615
|
+
var _a;
|
|
1616
|
+
log('show changed', `${previousShow !== null && previousShow !== void 0 ? previousShow : 'undefined'} > ${show}`);
|
|
1617
|
+
backdrop.setShow(show, (_a = p.id) !== null && _a !== void 0 ? _a : 'Modal');
|
|
1618
|
+
showing.current = show;
|
|
1619
|
+
log('backdrop.setShow', show);
|
|
1620
|
+
if (show) {
|
|
1621
|
+
log('this modal is showing. adding singleton bodyStyles', bodyStyles.current);
|
|
1622
|
+
addScrollStyles();
|
|
1623
|
+
}
|
|
1624
|
+
else {
|
|
1625
|
+
log('this modal is hiding. try removing singleton bodyStyles');
|
|
1626
|
+
tryRemoveScrollStyles();
|
|
1627
|
+
}
|
|
1628
|
+
}, p.show);
|
|
1629
|
+
React__default['default'].useLayoutEffect(() => {
|
|
1630
|
+
var _a;
|
|
1631
|
+
if (p.show === true) {
|
|
1632
|
+
const focusSelector = (_a = p.focusSelector) !== null && _a !== void 0 ? _a : '.modalCloseButton';
|
|
1633
|
+
// still need to wait for the next tick so the children are all rendered.
|
|
1634
|
+
setTimeout(() => {
|
|
1635
|
+
var _a;
|
|
1636
|
+
const element = (_a = contentRef.current) === null || _a === void 0 ? void 0 : _a.querySelector(focusSelector);
|
|
1637
|
+
element === null || element === void 0 ? void 0 : element.focus();
|
|
1638
|
+
log('set focus', focusSelector);
|
|
1639
|
+
});
|
|
1640
|
+
}
|
|
1641
|
+
}, [p.show]);
|
|
1642
|
+
const modalBodyStyles = css.css({
|
|
1643
|
+
maxHeight: p.scrollable ? undefined : '99vh',
|
|
1644
|
+
overflow: 'hidden',
|
|
1645
|
+
zIndex: theme.zIndexes.modal,
|
|
1646
|
+
cursor: 'default',
|
|
1647
|
+
margin: '1rem',
|
|
1648
|
+
backgroundColor: p.noBackground ? undefined : theme.colors.modalBg,
|
|
1649
|
+
border: p.noBackground ? undefined : theme.controls.border,
|
|
1650
|
+
boxShadow: p.noBackground ? undefined : theme.controls.boxShadow,
|
|
1651
|
+
maxWidth: (_c = p.maxWidth) !== null && _c !== void 0 ? _c : theme.breakpoints.tablet,
|
|
1652
|
+
minWidth: (_d = p.minWidth) !== null && _d !== void 0 ? _d : (hasHeader ? '250px' : undefined),
|
|
1653
|
+
opacity: p.show ? 1 : 0,
|
|
1654
|
+
fontSize: theme.fonts.size,
|
|
1655
|
+
fontFamily: theme.fonts.family,
|
|
1656
|
+
fontWeight: 'normal',
|
|
1657
|
+
'&:focus': {
|
|
1658
|
+
outline: 'none'
|
|
1659
|
+
}
|
|
1660
|
+
});
|
|
1661
|
+
const modalHeaderStyles = css.cx(css.css({
|
|
1662
|
+
display: 'flex',
|
|
1663
|
+
justifyContent: 'space-between',
|
|
1664
|
+
alignItems: 'center',
|
|
1665
|
+
backgroundColor: theme.colors.header,
|
|
1666
|
+
padding: '1rem',
|
|
1667
|
+
color: theme.colors.headerFont
|
|
1668
|
+
}), p.headerClassName);
|
|
1669
|
+
const modalContainerStyles = css.css([{
|
|
1670
|
+
position: 'fixed',
|
|
1671
|
+
height: '100%',
|
|
1672
|
+
width: '100%',
|
|
1673
|
+
backgroundColor: "transparent",
|
|
1674
|
+
display: 'flex',
|
|
1675
|
+
justifyContent: 'center',
|
|
1676
|
+
alignItems: 'center',
|
|
1677
|
+
cursor: p.onClick ? 'pointer' : 'default'
|
|
1678
|
+
}, p.scrollable && {
|
|
1679
|
+
overflowY: 'auto',
|
|
1680
|
+
overflowX: 'hidden',
|
|
1681
|
+
alignItems: 'flex-start'
|
|
1682
|
+
}]);
|
|
1683
|
+
if (p.show) {
|
|
1684
|
+
const backdropContainer = document.getElementById(backdrop.portalId);
|
|
1685
|
+
if (backdropContainer) {
|
|
1686
|
+
return reactDom.createPortal((React__default['default'].createElement("div", { onClick: e => {
|
|
1687
|
+
e.stopPropagation();
|
|
1688
|
+
if (!mouseDownElement.current) {
|
|
1689
|
+
if (p.onClick) {
|
|
1690
|
+
log('backdropContainer onClick');
|
|
1691
|
+
p.onClick();
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
mouseDownElement.current = undefined;
|
|
1695
|
+
}, className: css.cx('modalContainer', modalContainerStyles) },
|
|
1696
|
+
React__default['default'].createElement("div", { id: p.id, ref: contentRef, onClick: e => e.stopPropagation(), onMouseDown: e => {
|
|
1697
|
+
mouseDownElement.current = e.target;
|
|
1698
|
+
e.stopPropagation();
|
|
1699
|
+
}, onMouseUp: e => {
|
|
1700
|
+
mouseDownElement.current = undefined;
|
|
1701
|
+
/*
|
|
1702
|
+
MouseDown and MouseUp stopPropagation was added to fix bugs while clicking within the modal.
|
|
1703
|
+
At least in the case of MouseUp, this breaks sliders and the handle grab logic appears to live on window.
|
|
1704
|
+
Turning this off now. Should not cause any issues for MouseUp unlike MouseDown.
|
|
1705
|
+
*/
|
|
1706
|
+
// e.stopPropagation()
|
|
1707
|
+
}, className: css.cx('modalBody', modalBodyStyles, p.className) },
|
|
1708
|
+
React__default['default'].createElement(TabLocker, null,
|
|
1709
|
+
hasHeader && (React__default['default'].createElement("header", { className: css.cx('modalHeader', modalHeaderStyles) },
|
|
1710
|
+
p.heading ? React__default['default'].createElement(Text, { className: css.css({
|
|
1711
|
+
margin: 0,
|
|
1712
|
+
flexGrow: 1
|
|
1713
|
+
}), tag: "h1", bold: true }, p.heading) : React__default['default'].createElement("span", null),
|
|
1714
|
+
p.closeButton && p.onClick ? React__default['default'].createElement(Button, { className: css.cx('modalCloseButton', css.css({
|
|
1715
|
+
color: theme.colors.headerFont,
|
|
1716
|
+
marginLeft: '1rem',
|
|
1717
|
+
backgroundColor: 'transparent'
|
|
1718
|
+
})), variant: "icon", onClick: p.onClick },
|
|
1719
|
+
React__default['default'].createElement(Icon, { id: "close" })) : React__default['default'].createElement("span", null))),
|
|
1720
|
+
p.children)))), backdropContainer);
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1723
|
+
return null;
|
|
1724
|
+
};
|
|
1725
|
+
|
|
1694
1726
|
const ConfirmModal = (props) => {
|
|
1695
1727
|
const theme = useThemeSafely();
|
|
1696
1728
|
const modalStyle = css.css `
|
|
@@ -1709,11 +1741,15 @@ const ConfirmModal = (props) => {
|
|
|
1709
1741
|
React__namespace.createElement(Button, { className: css.css({ margin: '0 0.5rem' }), enforceMinWidth: true, onClick: props.onCancel }, props.cancelText || 'Cancel')))));
|
|
1710
1742
|
};
|
|
1711
1743
|
|
|
1712
|
-
const CopyButton = (props) => {
|
|
1744
|
+
const CopyButton = React__namespace.forwardRef((props, ref) => {
|
|
1745
|
+
const buttonProps = __rest(props, ["selector"]);
|
|
1713
1746
|
const [copied, setCopied] = React__namespace.useState(false);
|
|
1714
|
-
return (React__namespace.createElement(Button, { title: copied ? 'Copied!' : (props.title || 'Copy to clipboard'), variant: "icon", onBlur:
|
|
1747
|
+
return (React__namespace.createElement(Button, Object.assign({}, buttonProps, { ref: ref, title: copied ? 'Copied!' : (props.title || 'Copy to clipboard'), variant: "icon", onBlur: e => {
|
|
1748
|
+
var _a;
|
|
1715
1749
|
setCopied(false);
|
|
1750
|
+
(_a = props.onBlur) === null || _a === void 0 ? void 0 : _a.call(props, e);
|
|
1716
1751
|
}, onClick: e => {
|
|
1752
|
+
var _a;
|
|
1717
1753
|
const button = e.currentTarget;
|
|
1718
1754
|
let copySuccess = false;
|
|
1719
1755
|
try {
|
|
@@ -1727,9 +1763,10 @@ const CopyButton = (props) => {
|
|
|
1727
1763
|
// You done wrong.
|
|
1728
1764
|
}
|
|
1729
1765
|
setCopied(copySuccess);
|
|
1730
|
-
|
|
1766
|
+
(_a = props.onClick) === null || _a === void 0 ? void 0 : _a.call(props, e);
|
|
1767
|
+
} }),
|
|
1731
1768
|
React__namespace.createElement(Icon, { id: copied ? 'paste' : 'copy' })));
|
|
1732
|
-
};
|
|
1769
|
+
});
|
|
1733
1770
|
|
|
1734
1771
|
const Divider = (p) => {
|
|
1735
1772
|
const theme = useThemeSafely();
|
|
@@ -2172,343 +2209,126 @@ const Header = (props) => {
|
|
|
2172
2209
|
React__namespace.createElement("div", null, props.centerOffsetElements)))));
|
|
2173
2210
|
};
|
|
2174
2211
|
|
|
2175
|
-
const Highlight = (props) => {
|
|
2176
|
-
const theme = useThemeSafely();
|
|
2177
|
-
const highlightStyles = css.css `
|
|
2178
|
-
> mark {
|
|
2179
|
-
background-color: ${theme.colors.textHighlight};
|
|
2180
|
-
}
|
|
2181
|
-
`;
|
|
2182
|
-
let text = props.text;
|
|
2183
|
-
if (props.text && props.highlightText) {
|
|
2184
|
-
const replaceText = props.highlightText.trim();
|
|
2185
|
-
if (replaceText) {
|
|
2186
|
-
text = props.text.replace(new RegExp(`(${replaceText})`, 'gi'), `<mark>$1</mark>`);
|
|
2187
|
-
}
|
|
2188
|
-
}
|
|
2189
|
-
return (React__namespace.createElement("span", { className: css.cx('highlight', highlightStyles), dangerouslySetInnerHTML: { __html: text } }));
|
|
2190
|
-
};
|
|
2191
|
-
|
|
2192
|
-
const Image = React__namespace.forwardRef((p, ref) => {
|
|
2193
|
-
return (React__namespace.createElement("img", Object.assign({}, p, { ref: ref, className: css.cx('image', css.css({
|
|
2194
|
-
label: 'Image',
|
|
2195
|
-
maxWidth: '100%',
|
|
2196
|
-
maxHeight: '100%',
|
|
2197
|
-
}), p.className) })));
|
|
2198
|
-
});
|
|
2199
|
-
|
|
2200
|
-
const Popover = (p) => {
|
|
2201
|
-
var _a, _b;
|
|
2202
|
-
const theme = useThemeSafely();
|
|
2203
|
-
const resposition = (_a = p.reposition) !== null && _a !== void 0 ? _a : true;
|
|
2204
|
-
return (React__namespace.createElement(reactTinyPopover.Popover, { containerClassName: css.css({
|
|
2205
|
-
zIndex: theme.zIndexes.tooltip
|
|
2206
|
-
}), reposition: resposition, isOpen: p.isOpen, positions: (_b = p.positions) !== null && _b !== void 0 ? _b : ['right', 'top', 'left', 'bottom'], onClickOutside: p.onClickOutside, content: ({ position, childRect, popoverRect }) => {
|
|
2207
|
-
var _a, _b, _c, _d;
|
|
2208
|
-
return (React__namespace.createElement(reactTinyPopover.ArrowContainer, { position: position, childRect: childRect, popoverRect: popoverRect, arrowColor: (_a = p.arrorColor) !== null && _a !== void 0 ? _a : theme.colors.border, arrowSize: 10 },
|
|
2209
|
-
React__namespace.createElement(TabLocker, null,
|
|
2210
|
-
React__namespace.createElement("div", { className: css.css({
|
|
2211
|
-
border: (_b = p.border) !== null && _b !== void 0 ? _b : theme.controls.border,
|
|
2212
|
-
borderRadius: (_c = p.border) !== null && _c !== void 0 ? _c : theme.controls.borderRadius,
|
|
2213
|
-
boxShadow: theme.controls.boxShadow,
|
|
2214
|
-
backgroundColor: (_d = p.backgroundColor) !== null && _d !== void 0 ? _d : theme.colors.bg,
|
|
2215
|
-
}) }, p.content))));
|
|
2216
|
-
} },
|
|
2217
|
-
React__namespace.createElement("span", null, p.parent)));
|
|
2218
|
-
};
|
|
2219
|
-
|
|
2220
|
-
const InfoTip = (props) => {
|
|
2221
|
-
var _a, _b, _c;
|
|
2222
|
-
const [showTip, setShowTip] = React__namespace.useState(false);
|
|
2223
|
-
const theme = useThemeSafely();
|
|
2224
|
-
const bgColor = (_a = props.bgColor) !== null && _a !== void 0 ? _a : theme.colors.nav;
|
|
2225
|
-
const fontColor = (_b = props.fontColor) !== null && _b !== void 0 ? _b : theme.colors.navFont;
|
|
2226
|
-
const onClick = () => {
|
|
2227
|
-
if (props.onClick) {
|
|
2228
|
-
props.onClick();
|
|
2229
|
-
}
|
|
2230
|
-
else if (props.content) {
|
|
2231
|
-
openTip();
|
|
2232
|
-
}
|
|
2233
|
-
};
|
|
2234
|
-
const onMouseOver = () => {
|
|
2235
|
-
if (props.variant === 'modal') {
|
|
2236
|
-
return;
|
|
2237
|
-
}
|
|
2238
|
-
if (props.loadOnHover) {
|
|
2239
|
-
props.loadOnHover().then(() => {
|
|
2240
|
-
openTip();
|
|
2241
|
-
}).catch(err => {
|
|
2242
|
-
/* Not my responsiblity. */
|
|
2243
|
-
});
|
|
2244
|
-
}
|
|
2245
|
-
else {
|
|
2246
|
-
openTip();
|
|
2247
|
-
}
|
|
2248
|
-
};
|
|
2249
|
-
const onMouseOut = () => {
|
|
2250
|
-
if (props.variant === 'modal') {
|
|
2251
|
-
return;
|
|
2252
|
-
}
|
|
2253
|
-
closeTip();
|
|
2254
|
-
};
|
|
2255
|
-
const openTip = () => {
|
|
2256
|
-
if (!props.content) {
|
|
2257
|
-
return;
|
|
2258
|
-
}
|
|
2259
|
-
setShowTip(props.disabled ? false : true);
|
|
2260
|
-
};
|
|
2261
|
-
const closeTip = () => {
|
|
2262
|
-
if (!props.content) {
|
|
2263
|
-
return;
|
|
2264
|
-
}
|
|
2265
|
-
setShowTip(false);
|
|
2266
|
-
if (props.onClose) {
|
|
2267
|
-
props.onClose();
|
|
2268
|
-
}
|
|
2269
|
-
};
|
|
2270
|
-
const buttonStyles = css.css `
|
|
2271
|
-
font-weight: bold;
|
|
2272
|
-
width: 1.5rem;
|
|
2273
|
-
min-width:1.5rem;
|
|
2274
|
-
height: 1.5rem;
|
|
2275
|
-
padding: 0 !important;
|
|
2276
|
-
font-family: serif;
|
|
2277
|
-
display:inline-block;
|
|
2278
|
-
`;
|
|
2279
|
-
const button = React__namespace.createElement(Button, { className: buttonStyles, disabled: props.disabled, variant: 'circle', tabIndex: props.tabIndex, onClick: onClick, onMouseEnter: onMouseOver, onMouseLeave: onMouseOut }, "i");
|
|
2280
|
-
if (props.variant === 'modal') {
|
|
2281
|
-
return (React__namespace.createElement(React__namespace.Fragment, null,
|
|
2282
|
-
button,
|
|
2283
|
-
React__namespace.createElement(Modal, { id: props.modalId, __debug: props.__modalDebug, show: showTip, heading: props.modalHeader, onClick: closeTip, className: css.css({
|
|
2284
|
-
whiteSpace: 'normal'
|
|
2285
|
-
}), closeButton: true },
|
|
2286
|
-
React__namespace.createElement("div", { className: css.css({ padding: '1rem' }) }, props.content))));
|
|
2287
|
-
}
|
|
2288
|
-
else {
|
|
2289
|
-
return (React__namespace.createElement(Popover, { positions: props.positions, reposition: (_c = props.reposition) !== null && _c !== void 0 ? _c : false, isOpen: showTip, onClickOutside: closeTip, arrorColor: bgColor, border: '', backgroundColor: bgColor, parent: button, content: (React__namespace.createElement("div", { className: css.css({
|
|
2290
|
-
padding: '0.5rem',
|
|
2291
|
-
fontSize: '0.75rem',
|
|
2292
|
-
maxWidth: '22rem'
|
|
2293
|
-
}), style: { backgroundColor: bgColor, color: fontColor }, onMouseOut: closeTip }, props.content)) }));
|
|
2294
|
-
}
|
|
2295
|
-
};
|
|
2296
|
-
|
|
2297
|
-
const InputErrorDisplay = (props) => {
|
|
2298
|
-
const theme = useThemeSafely();
|
|
2299
|
-
return (React__namespace.createElement(Text, { className: css.css({
|
|
2300
|
-
minHeight: theme.controls.inputErrorMinHeight,
|
|
2301
|
-
lineHeight: theme.controls.inputErrorMinHeight,
|
|
2302
|
-
color: theme.colors.negative
|
|
2303
|
-
}), smaller: true, noPad: true }, props.error));
|
|
2304
|
-
};
|
|
2305
|
-
|
|
2306
|
-
const defaultMaxLength$1 = 100;
|
|
2307
|
-
const BaseInput = React__namespace.forwardRef((props, ref) => {
|
|
2308
|
-
var _a, _b;
|
|
2309
|
-
const theme = useThemeSafely();
|
|
2310
|
-
const { rightControl, round, wrapperClassName, showErrorDisplay } = props, nativeProps = __rest(props, ["rightControl", "round", "wrapperClassName", "showErrorDisplay"]);
|
|
2311
|
-
const inputStyles = css.css({
|
|
2312
|
-
backgroundColor: theme.colors.bg,
|
|
2313
|
-
fontFamily: theme.fonts.family,
|
|
2314
|
-
fontSize: theme.fonts.size,
|
|
2315
|
-
width: '100%',
|
|
2316
|
-
border: theme.controls.border,
|
|
2317
|
-
borderRadius: theme.controls.borderRadius,
|
|
2318
|
-
color: theme.colors.font,
|
|
2319
|
-
paddingLeft: theme.controls.padding,
|
|
2320
|
-
paddingRight: theme.controls.padding,
|
|
2321
|
-
height: theme.controls.height,
|
|
2322
|
-
transition: theme.controls.transition,
|
|
2323
|
-
':focus': {
|
|
2324
|
-
outline: 'none',
|
|
2325
|
-
boxShadow: theme.controls.focusOutlineShadow
|
|
2326
|
-
},
|
|
2327
|
-
':disabled': {
|
|
2328
|
-
backgroundColor: theme.colors.disabled,
|
|
2329
|
-
cursor: 'not-allowed'
|
|
2330
|
-
},
|
|
2331
|
-
':invalid': {
|
|
2332
|
-
borderColor: theme.colors.required,
|
|
2333
|
-
':focus': {
|
|
2334
|
-
boxShadow: theme.controls.focusOutlineRequiredShadow
|
|
2335
|
-
}
|
|
2336
|
-
},
|
|
2337
|
-
}, props.round && {
|
|
2338
|
-
borderRadius: theme.controls.roundRadius,
|
|
2339
|
-
paddingLeft: `calc(${theme.controls.padding} * 2)`,
|
|
2340
|
-
paddingRight: `calc(${theme.controls.padding} * 2)`
|
|
2341
|
-
}, props.readOnly && {
|
|
2342
|
-
backgroundColor: 'transparent',
|
|
2343
|
-
cursor: 'default',
|
|
2344
|
-
border: 'none',
|
|
2345
|
-
':focus': {
|
|
2346
|
-
outline: 'none',
|
|
2347
|
-
boxShadow: 'none'
|
|
2348
|
-
},
|
|
2349
|
-
// FF fix to hide spinner on number elements
|
|
2350
|
-
appearance: props.type === 'number' ? 'none' : undefined,
|
|
2351
|
-
'::-webkit-outer-spin-button': {
|
|
2352
|
-
appearance: 'none'
|
|
2353
|
-
},
|
|
2354
|
-
'::-webkit-inner-spin-button': {
|
|
2355
|
-
appearance: 'none'
|
|
2356
|
-
}
|
|
2357
|
-
}, props.rightControl && {
|
|
2358
|
-
paddingRight: theme.controls.height
|
|
2359
|
-
});
|
|
2360
|
-
const inputElement = React__namespace.createElement("input", Object.assign({}, nativeProps, { ref: ref, autoComplete: (_a = nativeProps.autoComplete) !== null && _a !== void 0 ? _a : 'off', tabIndex: nativeProps.readOnly ? -1 : nativeProps.tabIndex, maxLength: nativeProps.maxLength || defaultMaxLength$1, className: css.cx(inputStyles, props.className) }));
|
|
2361
|
-
const inputWrapperStyles = css.css `
|
|
2362
|
-
width:100%;
|
|
2363
|
-
${props.rightControl && `
|
|
2364
|
-
position: relative;
|
|
2365
|
-
`}
|
|
2366
|
-
`;
|
|
2367
|
-
const rightControlStyles = props.rightControl && css.css `
|
|
2368
|
-
position: absolute;
|
|
2369
|
-
right: ${theme.controls.padding};
|
|
2370
|
-
top: 0;
|
|
2371
|
-
bottom: 0;
|
|
2372
|
-
display: flex;
|
|
2373
|
-
align-items: center;
|
|
2374
|
-
${props.round && `
|
|
2375
|
-
right: calc(${theme.controls.padding} * 2);
|
|
2376
|
-
`}
|
|
2377
|
-
`;
|
|
2378
|
-
return (React__namespace.createElement("div", { className: css.css({
|
|
2379
|
-
width: '100%',
|
|
2380
|
-
label: 'BaseInput'
|
|
2381
|
-
}) },
|
|
2382
|
-
React__namespace.createElement("div", { className: css.cx('input', inputWrapperStyles, wrapperClassName) },
|
|
2383
|
-
inputElement,
|
|
2384
|
-
props.rightControl && (React__namespace.createElement("div", { className: rightControlStyles }, props.rightControl))),
|
|
2385
|
-
((_b = props.showErrorDisplay) !== null && _b !== void 0 ? _b : true) && React__namespace.createElement(InputErrorDisplay, { error: props.readOnly ? undefined : props.error })));
|
|
2386
|
-
});
|
|
2387
|
-
|
|
2388
|
-
const tryClampRange = (value, min, max) => {
|
|
2389
|
-
if (value === undefined) {
|
|
2390
|
-
return value;
|
|
2391
|
-
}
|
|
2392
|
-
if (isNaN(value)) {
|
|
2393
|
-
return undefined;
|
|
2394
|
-
}
|
|
2395
|
-
if (min !== undefined && value < min) {
|
|
2396
|
-
return min;
|
|
2397
|
-
}
|
|
2398
|
-
if (max !== undefined && value > max) {
|
|
2399
|
-
return max;
|
|
2400
|
-
}
|
|
2401
|
-
return value;
|
|
2402
|
-
};
|
|
2403
|
-
const isOutOfRange = (value, min, max) => {
|
|
2404
|
-
if (min !== undefined && value < min) {
|
|
2405
|
-
return true;
|
|
2406
|
-
}
|
|
2407
|
-
if (max !== undefined && value > max) {
|
|
2408
|
-
return true;
|
|
2409
|
-
}
|
|
2410
|
-
return false;
|
|
2411
|
-
};
|
|
2412
|
-
const getStepDecimalPlaces = (step) => {
|
|
2413
|
-
var _a, _b;
|
|
2414
|
-
if (!step) {
|
|
2415
|
-
return 0;
|
|
2416
|
-
}
|
|
2417
|
-
const strStep = typeof step === 'number' ? step.toString() : step;
|
|
2418
|
-
return (_b = (_a = strStep.split('.')[1]) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0;
|
|
2419
|
-
};
|
|
2420
|
-
const tryClampDecimals = (value, step = 0) => {
|
|
2421
|
-
if (value === undefined) {
|
|
2422
|
-
return value;
|
|
2423
|
-
}
|
|
2424
|
-
if (isNaN(value)) {
|
|
2425
|
-
return undefined;
|
|
2426
|
-
}
|
|
2427
|
-
const decimals = getStepDecimalPlaces(step);
|
|
2428
|
-
if (decimals === 0) {
|
|
2429
|
-
return Math.floor(value);
|
|
2212
|
+
const Highlight = (props) => {
|
|
2213
|
+
const theme = useThemeSafely();
|
|
2214
|
+
const highlightStyles = css.css `
|
|
2215
|
+
> mark {
|
|
2216
|
+
background-color: ${theme.colors.textHighlight};
|
|
2217
|
+
}
|
|
2218
|
+
`;
|
|
2219
|
+
let text = props.text;
|
|
2220
|
+
if (props.text && props.highlightText) {
|
|
2221
|
+
const replaceText = props.highlightText.trim();
|
|
2222
|
+
if (replaceText) {
|
|
2223
|
+
text = props.text.replace(new RegExp(`(${replaceText})`, 'gi'), `<mark>$1</mark>`);
|
|
2224
|
+
}
|
|
2430
2225
|
}
|
|
2431
|
-
return
|
|
2226
|
+
return (React__namespace.createElement("span", { className: css.cx('highlight', highlightStyles), dangerouslySetInnerHTML: { __html: text } }));
|
|
2432
2227
|
};
|
|
2433
2228
|
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2229
|
+
const Image = React__namespace.forwardRef((p, ref) => {
|
|
2230
|
+
return (React__namespace.createElement("img", Object.assign({}, p, { ref: ref, className: css.cx('image', css.css({
|
|
2231
|
+
label: 'Image',
|
|
2232
|
+
maxWidth: '100%',
|
|
2233
|
+
maxHeight: '100%',
|
|
2234
|
+
}), p.className) })));
|
|
2235
|
+
});
|
|
2236
|
+
|
|
2237
|
+
const Popover = (p) => {
|
|
2238
|
+
var _a, _b;
|
|
2239
|
+
const theme = useThemeSafely();
|
|
2240
|
+
const resposition = (_a = p.reposition) !== null && _a !== void 0 ? _a : true;
|
|
2241
|
+
return (React__namespace.createElement(reactTinyPopover.Popover, { containerClassName: css.css({
|
|
2242
|
+
zIndex: theme.zIndexes.tooltip
|
|
2243
|
+
}), reposition: resposition, isOpen: p.isOpen, positions: (_b = p.positions) !== null && _b !== void 0 ? _b : ['right', 'top', 'left', 'bottom'], onClickOutside: p.onClickOutside, content: ({ position, childRect, popoverRect }) => {
|
|
2244
|
+
var _a, _b, _c, _d;
|
|
2245
|
+
return (React__namespace.createElement(reactTinyPopover.ArrowContainer, { position: position, childRect: childRect, popoverRect: popoverRect, arrowColor: (_a = p.arrorColor) !== null && _a !== void 0 ? _a : theme.colors.border, arrowSize: 10 },
|
|
2246
|
+
React__namespace.createElement(TabLocker, null,
|
|
2247
|
+
React__namespace.createElement("div", { className: css.css({
|
|
2248
|
+
border: (_b = p.border) !== null && _b !== void 0 ? _b : theme.controls.border,
|
|
2249
|
+
borderRadius: (_c = p.border) !== null && _c !== void 0 ? _c : theme.controls.borderRadius,
|
|
2250
|
+
boxShadow: theme.controls.boxShadow,
|
|
2251
|
+
backgroundColor: (_d = p.backgroundColor) !== null && _d !== void 0 ? _d : theme.colors.bg,
|
|
2252
|
+
}) }, p.content))));
|
|
2253
|
+
} },
|
|
2254
|
+
React__namespace.createElement("span", null, p.parent)));
|
|
2255
|
+
};
|
|
2256
|
+
|
|
2257
|
+
const InfoTip = (props) => {
|
|
2258
|
+
var _a, _b, _c;
|
|
2259
|
+
const [showTip, setShowTip] = React__namespace.useState(false);
|
|
2260
|
+
const theme = useThemeSafely();
|
|
2261
|
+
const bgColor = (_a = props.bgColor) !== null && _a !== void 0 ? _a : theme.colors.nav;
|
|
2262
|
+
const fontColor = (_b = props.fontColor) !== null && _b !== void 0 ? _b : theme.colors.navFont;
|
|
2263
|
+
const onClick = () => {
|
|
2264
|
+
if (props.onClick) {
|
|
2265
|
+
props.onClick();
|
|
2266
|
+
}
|
|
2267
|
+
else if (props.content) {
|
|
2268
|
+
openTip();
|
|
2269
|
+
}
|
|
2443
2270
|
};
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
React__default['default'].useEffect(() => {
|
|
2448
|
-
updateErrorMessage();
|
|
2449
|
-
}, []);
|
|
2450
|
-
return [validationError, updateErrorMessage];
|
|
2451
|
-
};
|
|
2452
|
-
const getValidationMessage = (element, patternErrorMessage) => {
|
|
2453
|
-
var _a;
|
|
2454
|
-
if (!element) {
|
|
2455
|
-
return '';
|
|
2456
|
-
}
|
|
2457
|
-
const validity = element.validity;
|
|
2458
|
-
if (validity.valid) {
|
|
2459
|
-
return '';
|
|
2460
|
-
}
|
|
2461
|
-
if (validity.customError) {
|
|
2462
|
-
return element.validationMessage;
|
|
2463
|
-
}
|
|
2464
|
-
if (validity.typeMismatch) {
|
|
2465
|
-
switch (element.type) {
|
|
2466
|
-
case 'url':
|
|
2467
|
-
return `Invalid URL.`;
|
|
2468
|
-
case 'email':
|
|
2469
|
-
return `Invalid email.`;
|
|
2470
|
-
default:
|
|
2471
|
-
return element.validationMessage;
|
|
2271
|
+
const onMouseOver = () => {
|
|
2272
|
+
if (props.variant === 'modal') {
|
|
2273
|
+
return;
|
|
2472
2274
|
}
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2275
|
+
if (props.loadOnHover) {
|
|
2276
|
+
props.loadOnHover().then(() => {
|
|
2277
|
+
openTip();
|
|
2278
|
+
}).catch(err => {
|
|
2279
|
+
/* Not my responsiblity. */
|
|
2280
|
+
});
|
|
2477
2281
|
}
|
|
2478
|
-
|
|
2479
|
-
|
|
2282
|
+
else {
|
|
2283
|
+
openTip();
|
|
2480
2284
|
}
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
return `Limited to ${getStepDecimalPlaces(element.step)} decimal ${place}.`;
|
|
2486
|
-
}
|
|
2487
|
-
else {
|
|
2488
|
-
/*
|
|
2489
|
-
step is buggy!
|
|
2490
|
-
|
|
2491
|
-
at least in Chrome, setting step=5 will cause the browser to mark the field as invalid if the number is not divisible
|
|
2492
|
-
by 5. 55 is ok. 50 is ok. 59 is not ok.
|
|
2493
|
-
|
|
2494
|
-
to make things worse, if you enter an invalid number like 59 with step=5 and then clear the input, Chrome will tell
|
|
2495
|
-
you the number 5 is now invalid and should be 4 or 9.
|
|
2496
|
-
*/
|
|
2497
|
-
return `Must be an integer.`;
|
|
2498
|
-
}
|
|
2285
|
+
};
|
|
2286
|
+
const onMouseOut = () => {
|
|
2287
|
+
if (props.variant === 'modal') {
|
|
2288
|
+
return;
|
|
2499
2289
|
}
|
|
2290
|
+
closeTip();
|
|
2291
|
+
};
|
|
2292
|
+
const openTip = () => {
|
|
2293
|
+
if (!props.content) {
|
|
2294
|
+
return;
|
|
2295
|
+
}
|
|
2296
|
+
setShowTip(props.disabled ? false : true);
|
|
2297
|
+
};
|
|
2298
|
+
const closeTip = () => {
|
|
2299
|
+
if (!props.content) {
|
|
2300
|
+
return;
|
|
2301
|
+
}
|
|
2302
|
+
setShowTip(false);
|
|
2303
|
+
if (props.onClose) {
|
|
2304
|
+
props.onClose();
|
|
2305
|
+
}
|
|
2306
|
+
};
|
|
2307
|
+
const buttonStyles = css.css `
|
|
2308
|
+
font-weight: bold;
|
|
2309
|
+
width: 1.5rem;
|
|
2310
|
+
min-width:1.5rem;
|
|
2311
|
+
height: 1.5rem;
|
|
2312
|
+
padding: 0 !important;
|
|
2313
|
+
font-family: serif;
|
|
2314
|
+
display:inline-block;
|
|
2315
|
+
`;
|
|
2316
|
+
const button = React__namespace.createElement(Button, { className: buttonStyles, disabled: props.disabled, variant: 'circle', tabIndex: props.tabIndex, onClick: onClick, onMouseEnter: onMouseOver, onMouseLeave: onMouseOut }, "i");
|
|
2317
|
+
if (props.variant === 'modal') {
|
|
2318
|
+
return (React__namespace.createElement(React__namespace.Fragment, null,
|
|
2319
|
+
button,
|
|
2320
|
+
React__namespace.createElement(Modal, { id: props.modalId, __debug: props.__modalDebug, show: showTip, heading: props.modalHeader, onClick: closeTip, className: css.css({
|
|
2321
|
+
whiteSpace: 'normal'
|
|
2322
|
+
}), closeButton: true },
|
|
2323
|
+
React__namespace.createElement("div", { className: css.css({ padding: '1rem' }) }, props.content))));
|
|
2500
2324
|
}
|
|
2501
|
-
|
|
2502
|
-
return
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
if (validity.patternMismatch && patternErrorMessage) {
|
|
2508
|
-
return patternErrorMessage;
|
|
2325
|
+
else {
|
|
2326
|
+
return (React__namespace.createElement(Popover, { positions: props.positions, reposition: (_c = props.reposition) !== null && _c !== void 0 ? _c : false, isOpen: showTip, onClickOutside: closeTip, arrorColor: bgColor, border: '', backgroundColor: bgColor, parent: button, content: (React__namespace.createElement("div", { className: css.css({
|
|
2327
|
+
padding: '0.5rem',
|
|
2328
|
+
fontSize: '0.75rem',
|
|
2329
|
+
maxWidth: '22rem'
|
|
2330
|
+
}), style: { backgroundColor: bgColor, color: fontColor }, onMouseOut: closeTip }, props.content)) }));
|
|
2509
2331
|
}
|
|
2510
|
-
// unhandled. let the browser decide.
|
|
2511
|
-
return element.validationMessage;
|
|
2512
2332
|
};
|
|
2513
2333
|
|
|
2514
2334
|
const dateRegex = /(\d{1,2})(?:\/|-)(\d{1,2})(?:\/|-)(\d{4})/;
|
|
@@ -2751,46 +2571,6 @@ const parseNumber = (rawValue) => {
|
|
|
2751
2571
|
return value;
|
|
2752
2572
|
};
|
|
2753
2573
|
|
|
2754
|
-
const TextInput = React__namespace.forwardRef((props, ref) => {
|
|
2755
|
-
var _a;
|
|
2756
|
-
const [localValue, setLocalValue] = React__namespace.useState(props.value);
|
|
2757
|
-
const inputRef = (ref !== null && ref !== void 0 ? ref : React__namespace.useRef(null));
|
|
2758
|
-
const [validationError, updateErrorMessage] = useInputValidationMessage(inputRef, props);
|
|
2759
|
-
const nativeProps = __rest(props, ["emptyString", "onValueChange", "customError", "patternErrorMessage"]);
|
|
2760
|
-
useIgnoreMount(() => {
|
|
2761
|
-
var _a;
|
|
2762
|
-
if ((_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.checkValidity()) {
|
|
2763
|
-
props.onValueChange(localValue);
|
|
2764
|
-
}
|
|
2765
|
-
else {
|
|
2766
|
-
props.onValueChange(undefined);
|
|
2767
|
-
}
|
|
2768
|
-
updateErrorMessage();
|
|
2769
|
-
}, [localValue]);
|
|
2770
|
-
useIgnoreMount(() => {
|
|
2771
|
-
if (document.activeElement !== inputRef.current) {
|
|
2772
|
-
setLocalValue(props.value);
|
|
2773
|
-
}
|
|
2774
|
-
updateErrorMessage();
|
|
2775
|
-
}, [props.value]);
|
|
2776
|
-
return (React__namespace.createElement(BaseInput, Object.assign({}, nativeProps, { error: validationError, type: (_a = props.type) !== null && _a !== void 0 ? _a : 'text', ref: inputRef, value: localValue !== null && localValue !== void 0 ? localValue : '', onChange: e => {
|
|
2777
|
-
var _a;
|
|
2778
|
-
setLocalValue(props.emptyString ? e.target.value : e.target.value || undefined);
|
|
2779
|
-
(_a = props.onChange) === null || _a === void 0 ? void 0 : _a.call(props, e);
|
|
2780
|
-
}, onBlur: e => {
|
|
2781
|
-
var _a, _b;
|
|
2782
|
-
if (!e.target.checkValidity()) {
|
|
2783
|
-
setLocalValue(undefined);
|
|
2784
|
-
}
|
|
2785
|
-
else if ((_a = props.trim) !== null && _a !== void 0 ? _a : true) {
|
|
2786
|
-
setLocalValue(currentValue => {
|
|
2787
|
-
return currentValue === null || currentValue === void 0 ? void 0 : currentValue.trim();
|
|
2788
|
-
});
|
|
2789
|
-
}
|
|
2790
|
-
(_b = props.onBlur) === null || _b === void 0 ? void 0 : _b.call(props, e);
|
|
2791
|
-
} })));
|
|
2792
|
-
});
|
|
2793
|
-
|
|
2794
2574
|
const Label = (props) => {
|
|
2795
2575
|
var _a, _b, _c;
|
|
2796
2576
|
const labelProps = __rest(props, ["text", "static", "orientation", "align", "noWrap", "subText", "optional", "controlAlign"]);
|
|
@@ -4392,8 +4172,12 @@ const ToggleButtonGroup = (props) => {
|
|
|
4392
4172
|
};
|
|
4393
4173
|
|
|
4394
4174
|
const TogglePasswordInput = React__namespace.forwardRef((props, ref) => {
|
|
4175
|
+
const { onVisibilityChanged } = props, inputProps = __rest(props, ["onVisibilityChanged"]);
|
|
4395
4176
|
const [show, setShow] = React__namespace.useState(false);
|
|
4396
|
-
|
|
4177
|
+
useIgnoreMount(() => {
|
|
4178
|
+
onVisibilityChanged === null || onVisibilityChanged === void 0 ? void 0 : onVisibilityChanged(show);
|
|
4179
|
+
}, [show]);
|
|
4180
|
+
return (React__namespace.createElement(TextInput, Object.assign({}, inputProps, { ref: ref, type: show ? 'text' : 'password', rightControl: (React__namespace.createElement(Button, { small: true, style: {
|
|
4397
4181
|
// small button is required here due to the icon pushing outside the boundries of the
|
|
4398
4182
|
// parent textbox. increasing the font size here to fill the small button.
|
|
4399
4183
|
fontSize: '1rem'
|
|
@@ -4871,8 +4655,106 @@ const TabContainer = (p) => {
|
|
|
4871
4655
|
}), p.contentClassName) }, p.tabs[tabIndex].getContent())));
|
|
4872
4656
|
};
|
|
4873
4657
|
|
|
4658
|
+
const defaultMinChars = 3;
|
|
4659
|
+
/** Extracted logic around autocomplete functionality for Autocomplete.tsx. */
|
|
4660
|
+
class AutocompleteController {
|
|
4661
|
+
constructor(getOptions, config) {
|
|
4662
|
+
var _a;
|
|
4663
|
+
this._value = undefined;
|
|
4664
|
+
this._options = [];
|
|
4665
|
+
this._minChars = (_a = config === null || config === void 0 ? void 0 : config.minChars) !== null && _a !== void 0 ? _a : defaultMinChars;
|
|
4666
|
+
if (config === null || config === void 0 ? void 0 : config.debounceMs) {
|
|
4667
|
+
this.getOptions = lodash.debounce(getOptions, config.debounceMs, {
|
|
4668
|
+
leading: false,
|
|
4669
|
+
trailing: true
|
|
4670
|
+
});
|
|
4671
|
+
}
|
|
4672
|
+
else {
|
|
4673
|
+
this.getOptions = getOptions;
|
|
4674
|
+
}
|
|
4675
|
+
}
|
|
4676
|
+
get value() {
|
|
4677
|
+
return this._value;
|
|
4678
|
+
}
|
|
4679
|
+
get options() {
|
|
4680
|
+
return this._options;
|
|
4681
|
+
}
|
|
4682
|
+
async onChange(newValue) {
|
|
4683
|
+
var _a;
|
|
4684
|
+
// don't make getOptions calls if the value hasn't changed.
|
|
4685
|
+
if (newValue === this.value) {
|
|
4686
|
+
return;
|
|
4687
|
+
}
|
|
4688
|
+
// nullish should not make the getOptions call and instead clear everything.
|
|
4689
|
+
if (!newValue) {
|
|
4690
|
+
this._value = newValue;
|
|
4691
|
+
this._options = [];
|
|
4692
|
+
return;
|
|
4693
|
+
}
|
|
4694
|
+
// sub min chars should clear everything and not attempt the getOptions call.
|
|
4695
|
+
if (newValue.length < this._minChars) {
|
|
4696
|
+
this._value = newValue;
|
|
4697
|
+
this._options = [];
|
|
4698
|
+
return;
|
|
4699
|
+
}
|
|
4700
|
+
try {
|
|
4701
|
+
this._value = newValue;
|
|
4702
|
+
// debounce (if used) will return undefined until the actual function is executed.
|
|
4703
|
+
// after that it will return the result of the last execution.
|
|
4704
|
+
this._options = (_a = (await this.getOptions(newValue))) !== null && _a !== void 0 ? _a : [];
|
|
4705
|
+
}
|
|
4706
|
+
catch (err) {
|
|
4707
|
+
// the getOptions method needs to handle it's own errors
|
|
4708
|
+
}
|
|
4709
|
+
}
|
|
4710
|
+
onPick(newValue) {
|
|
4711
|
+
this._value = newValue;
|
|
4712
|
+
this._options = [];
|
|
4713
|
+
}
|
|
4714
|
+
}
|
|
4715
|
+
|
|
4716
|
+
/** Extracted logic around autocomplete functionality for Autocomplete.tsx that supports Entity (id/name) mapping. */
|
|
4717
|
+
class AutocompleteEntityController {
|
|
4718
|
+
constructor(getOptions, config) {
|
|
4719
|
+
this._options = [];
|
|
4720
|
+
const getStringOptions = async (value) => {
|
|
4721
|
+
this._options = await getOptions(value);
|
|
4722
|
+
return this._options.map(o => o.name);
|
|
4723
|
+
};
|
|
4724
|
+
this._ctrl = new AutocompleteController(getStringOptions, config);
|
|
4725
|
+
}
|
|
4726
|
+
get entity() {
|
|
4727
|
+
return this._pickedEntity;
|
|
4728
|
+
}
|
|
4729
|
+
get entities() {
|
|
4730
|
+
return this._options;
|
|
4731
|
+
}
|
|
4732
|
+
get value() {
|
|
4733
|
+
return this._ctrl.value;
|
|
4734
|
+
}
|
|
4735
|
+
get options() {
|
|
4736
|
+
return this._options.map(o => o.name);
|
|
4737
|
+
}
|
|
4738
|
+
async onChange(newValue) {
|
|
4739
|
+
await this._ctrl.onChange(newValue);
|
|
4740
|
+
this.trySyncCtrlOptions();
|
|
4741
|
+
}
|
|
4742
|
+
onPick(newValue) {
|
|
4743
|
+
this._ctrl.onPick(newValue);
|
|
4744
|
+
this._pickedEntity = this._options.find(o => o.name === this._ctrl.value);
|
|
4745
|
+
this.trySyncCtrlOptions();
|
|
4746
|
+
}
|
|
4747
|
+
trySyncCtrlOptions() {
|
|
4748
|
+
if (!this._ctrl.options.length) {
|
|
4749
|
+
this._options = [];
|
|
4750
|
+
}
|
|
4751
|
+
}
|
|
4752
|
+
}
|
|
4753
|
+
|
|
4874
4754
|
exports.Accordian = Accordian;
|
|
4875
4755
|
exports.Autocomplete = Autocomplete;
|
|
4756
|
+
exports.AutocompleteController = AutocompleteController;
|
|
4757
|
+
exports.AutocompleteEntityController = AutocompleteEntityController;
|
|
4876
4758
|
exports.Backdrop = Backdrop$1;
|
|
4877
4759
|
exports.Backdrop2 = Backdrop;
|
|
4878
4760
|
exports.BoundMemoryPager = BoundMemoryPager;
|
|
@@ -4897,7 +4779,6 @@ exports.Icon = Icon;
|
|
|
4897
4779
|
exports.Image = Image;
|
|
4898
4780
|
exports.InfoPanel = InfoPanel;
|
|
4899
4781
|
exports.InfoTip = InfoTip;
|
|
4900
|
-
exports.Input = Input;
|
|
4901
4782
|
exports.ItemPager = ItemPager;
|
|
4902
4783
|
exports.Label = Label;
|
|
4903
4784
|
exports.Link = Link;
|