@mackin.com/styleguide 8.6.1 → 8.7.1
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 +10 -3
- package/index.js +260 -222
- package/package.json +1 -1
package/index.d.ts
CHANGED
|
@@ -461,6 +461,8 @@ interface ModalProps {
|
|
|
461
461
|
__debug?: boolean;
|
|
462
462
|
onClick?: () => void;
|
|
463
463
|
}
|
|
464
|
+
/** Add to fixed positioned elements so their contents do not jump around when scrolling is disabled. */
|
|
465
|
+
declare const modalScrollFixClassName = "modal-scroll-fix";
|
|
464
466
|
declare const Modal: (p: ModalProps) => React__default.ReactPortal | null;
|
|
465
467
|
|
|
466
468
|
declare const Nav: (props: {
|
|
@@ -709,11 +711,14 @@ interface SearchBoxProps {
|
|
|
709
711
|
id?: string;
|
|
710
712
|
placeholder?: string;
|
|
711
713
|
round?: boolean;
|
|
712
|
-
className?: string;
|
|
713
714
|
onSubmit?: () => Promise<void>;
|
|
714
715
|
/** Defaults to 250ms */
|
|
715
716
|
debounceMs?: number;
|
|
716
717
|
autoFocus?: boolean;
|
|
718
|
+
/** This will be applied to the containing element (Form). */
|
|
719
|
+
className?: string;
|
|
720
|
+
buttonClassName?: string;
|
|
721
|
+
inputClassName?: string;
|
|
717
722
|
}
|
|
718
723
|
declare const SearchBox: (props: SearchBoxProps) => JSX.Element;
|
|
719
724
|
|
|
@@ -863,7 +868,9 @@ interface SliderProps<T extends SliderValue> {
|
|
|
863
868
|
declare const Slider: <T extends SliderValue>(p: SliderProps<T>) => JSX.Element;
|
|
864
869
|
|
|
865
870
|
interface TabHeaderTabProps {
|
|
866
|
-
name: string;
|
|
871
|
+
name: string | JSX.Element;
|
|
872
|
+
/** The HTML title of the tab button. Defaults to 'name' prop. */
|
|
873
|
+
title?: string;
|
|
867
874
|
}
|
|
868
875
|
interface TabHeaderProps {
|
|
869
876
|
tabs: TabHeaderTabProps[];
|
|
@@ -1102,4 +1109,4 @@ interface TabContainerProps {
|
|
|
1102
1109
|
}
|
|
1103
1110
|
declare const TabContainer: (p: TabContainerProps) => JSX.Element;
|
|
1104
1111
|
|
|
1105
|
-
export { Accordian, AccordianProps, Alignment, Autocomplete, AutocompleteProps, AutocompleteValue, Backdrop$1 as Backdrop, Backdrop as Backdrop2, BoundMemoryPager, BoundStaticPager, Button, ButtonProps, Calendar, CalendarProps, Checkbox, CheckboxProps, ConfirmModal, ConfirmModalProps, CopyButton, DateInput, DateInputProps, Divider, ErrorModal, FileUploader, Form, FormColumnRow, FormFlexRow, FormProps, GlobalStyles, Header, Highlight, ICONS, Icon, Image, ImageProps, InfoPanel, InfoTip, InfoTipProps, Input, InputProps, ItemPager, Label, LabelProps, Link, LinkProps, List, ListItem, ListItemProps, ListProps, MackinTheme, Modal, ModalProps, Nav, NormalizeCss, NumberInput, NumberInputProps, OmniLink, OmniLinkProps, PagedResult, Pager, PagerProps, Picker, PickerOption, PickerProps, PickerValue, Popover, ProgressBar, ProgressBarProps, SearchBox, SearchBoxProps, Slider, TabContainer, TabContainerProps, TabHeader, TabHeaderProps, TabLocker, Table, Td, TdCurrency, TdNumber, Text, TextArea, TextAreaProps, TextInput, TextInputProps, TextProps, Th, ThSort, ThemeProvider, ThemeRenderer, ToggleButton, ToggleButtonGroup, ToggleButtonGroupProps, ToggleButtonProps, TogglePasswordInput, Tr, WaitingIndicator, calcDynamicThemeProps, defaultTheme, enumToEntities, getCurrencyDisplay, getFileSizeDisplay, useAccordianState, useBooleanChanged, useIgnoreMount, useMediaQuery, useScrollbarSize, useThemeSafely, useWaiting };
|
|
1112
|
+
export { Accordian, AccordianProps, Alignment, Autocomplete, AutocompleteProps, AutocompleteValue, Backdrop$1 as Backdrop, Backdrop as Backdrop2, BoundMemoryPager, BoundStaticPager, Button, ButtonProps, Calendar, CalendarProps, Checkbox, CheckboxProps, ConfirmModal, ConfirmModalProps, CopyButton, DateInput, DateInputProps, Divider, ErrorModal, FileUploader, Form, FormColumnRow, FormFlexRow, FormProps, GlobalStyles, Header, Highlight, ICONS, Icon, Image, ImageProps, InfoPanel, InfoTip, InfoTipProps, Input, InputProps, ItemPager, Label, LabelProps, Link, LinkProps, List, ListItem, ListItemProps, ListProps, MackinTheme, Modal, ModalProps, Nav, NormalizeCss, NumberInput, NumberInputProps, OmniLink, OmniLinkProps, PagedResult, Pager, PagerProps, Picker, PickerOption, PickerProps, PickerValue, Popover, ProgressBar, ProgressBarProps, SearchBox, SearchBoxProps, Slider, TabContainer, TabContainerProps, TabHeader, TabHeaderProps, TabLocker, Table, Td, TdCurrency, TdNumber, Text, TextArea, TextAreaProps, TextInput, TextInputProps, TextProps, Th, ThSort, ThemeProvider, ThemeRenderer, ToggleButton, ToggleButtonGroup, ToggleButtonGroupProps, ToggleButtonProps, TogglePasswordInput, Tr, WaitingIndicator, calcDynamicThemeProps, defaultTheme, enumToEntities, getCurrencyDisplay, getFileSizeDisplay, modalScrollFixClassName, useAccordianState, useBooleanChanged, useIgnoreMount, useMediaQuery, useScrollbarSize, useThemeSafely, useWaiting };
|
package/index.js
CHANGED
|
@@ -282,6 +282,13 @@ const Text = (props) => {
|
|
|
282
282
|
if (props.leftPad) {
|
|
283
283
|
style.paddingLeft = props.leftPad;
|
|
284
284
|
}
|
|
285
|
+
let fontWeight;
|
|
286
|
+
if (props.bold) {
|
|
287
|
+
fontWeight = 'bold';
|
|
288
|
+
}
|
|
289
|
+
else if (props.bold === false) {
|
|
290
|
+
fontWeight = 'normal';
|
|
291
|
+
}
|
|
285
292
|
const styles = css.css(getTagStyles(theme, tagChoice), {
|
|
286
293
|
userSelect: 'text',
|
|
287
294
|
label: 'Text',
|
|
@@ -295,7 +302,7 @@ const Text = (props) => {
|
|
|
295
302
|
overflow: 'hidden',
|
|
296
303
|
textOverflow: 'ellipsis',
|
|
297
304
|
display: '-webkit-box'
|
|
298
|
-
}, props.spacedOut && { lineHeight: '1.5rem' },
|
|
305
|
+
}, props.spacedOut && { lineHeight: '1.5rem' }, { fontWeight }, props.noPad && { margin: 0, padding: 0 }, headerRegex.test((_b = props.tag) !== null && _b !== void 0 ? _b : '') && {
|
|
299
306
|
fontFamily: theme.fonts.headerFamily
|
|
300
307
|
});
|
|
301
308
|
return React__namespace.createElement(tagChoice, {
|
|
@@ -1154,6 +1161,240 @@ const Backdrop$1 = (props) => {
|
|
|
1154
1161
|
}, ref: backdrop, className: css.cx('backdrop', styles, props.className) }, props.children));
|
|
1155
1162
|
};
|
|
1156
1163
|
|
|
1164
|
+
/** useEffect but it will only fire when the actual truthiness of the value changes.
|
|
1165
|
+
* Use for comparing previous states to next states without all the bullshit around useEffect and component mounting.
|
|
1166
|
+
*/
|
|
1167
|
+
const useBooleanChanged = (effect, dep) => {
|
|
1168
|
+
/*
|
|
1169
|
+
Why?
|
|
1170
|
+
useEffect with a dependency array will fire once on mount even though the dependency list doesn't change.
|
|
1171
|
+
Components like Modal need to communicate when their show status changes.
|
|
1172
|
+
useIgnoreMount is not enough because it only ignores the first render and is therefore a kludge.
|
|
1173
|
+
This is what we want regardless of mount status:
|
|
1174
|
+
true > false = Change
|
|
1175
|
+
false > true = Change
|
|
1176
|
+
true > true = No Change
|
|
1177
|
+
false > false = No Change
|
|
1178
|
+
undefined > false = No Change
|
|
1179
|
+
undefined > true = Change
|
|
1180
|
+
*/
|
|
1181
|
+
const lastValue = React.useRef(undefined);
|
|
1182
|
+
React.useEffect(() => {
|
|
1183
|
+
//console.log('[useBooleanChanged] useEffect called with', dep, 'was', lastValue.current ?? 'undefined')
|
|
1184
|
+
if (!!lastValue.current !== !!dep) {
|
|
1185
|
+
const previous = lastValue.current;
|
|
1186
|
+
lastValue.current = dep;
|
|
1187
|
+
effect(!!lastValue.current, !!previous);
|
|
1188
|
+
//console.log('[useBooleanChanged] change called')
|
|
1189
|
+
}
|
|
1190
|
+
}, [dep]);
|
|
1191
|
+
};
|
|
1192
|
+
|
|
1193
|
+
const useLogger = (componentName, enabled) => {
|
|
1194
|
+
return (...messages) => {
|
|
1195
|
+
if (enabled) {
|
|
1196
|
+
// tslint:disable-next-line
|
|
1197
|
+
console.log(`[${componentName}]`, ...messages);
|
|
1198
|
+
}
|
|
1199
|
+
};
|
|
1200
|
+
};
|
|
1201
|
+
|
|
1202
|
+
// Taken from: https://github.com/react-bootstrap/dom-helpers/blob/master/src/scrollbarSize.ts
|
|
1203
|
+
const canUseDom = !!(typeof window !== 'undefined' &&
|
|
1204
|
+
window.document &&
|
|
1205
|
+
window.document.createElement);
|
|
1206
|
+
let size;
|
|
1207
|
+
/** Tells you actual width of the scroll bar. This can vary by browser. */
|
|
1208
|
+
const useScrollbarSize = (recalc) => {
|
|
1209
|
+
if ((!size && size !== 0) || recalc) {
|
|
1210
|
+
if (canUseDom) {
|
|
1211
|
+
const scrollDiv = document.createElement('div');
|
|
1212
|
+
scrollDiv.style.position = 'absolute';
|
|
1213
|
+
scrollDiv.style.top = '-9999px';
|
|
1214
|
+
scrollDiv.style.width = '50px';
|
|
1215
|
+
scrollDiv.style.height = '50px';
|
|
1216
|
+
scrollDiv.style.overflow = 'scroll';
|
|
1217
|
+
document.body.appendChild(scrollDiv);
|
|
1218
|
+
size = scrollDiv.offsetWidth - scrollDiv.clientWidth;
|
|
1219
|
+
document.body.removeChild(scrollDiv);
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
return size;
|
|
1223
|
+
};
|
|
1224
|
+
|
|
1225
|
+
/** Add to fixed positioned elements so their contents do not jump around when scrolling is disabled. */
|
|
1226
|
+
const modalScrollFixClassName = 'modal-scroll-fix';
|
|
1227
|
+
const Modal = (p) => {
|
|
1228
|
+
var _a, _b, _c, _d;
|
|
1229
|
+
const backdrop = React.useContext(BackdropContext);
|
|
1230
|
+
const mouseDownElement = React.useRef(undefined);
|
|
1231
|
+
const theme = useThemeSafely();
|
|
1232
|
+
const hasHeader = p.closeButton || p.heading;
|
|
1233
|
+
const contentRef = React__default['default'].useRef(null);
|
|
1234
|
+
const log = useLogger((_a = p.id) !== null && _a !== void 0 ? _a : 'Modal', (_b = p.__debug) !== null && _b !== void 0 ? _b : false);
|
|
1235
|
+
const showing = React.useRef(p.show);
|
|
1236
|
+
const bodyStyles = React.useRef('');
|
|
1237
|
+
const fixedElementStyles = React.useRef('');
|
|
1238
|
+
const addScrollStyles = () => {
|
|
1239
|
+
var _a, _b, _c, _d;
|
|
1240
|
+
if (!bodyStyles.current) {
|
|
1241
|
+
bodyStyles.current = css.css({
|
|
1242
|
+
label: 'ModalBodyOverrides_' + ((_b = (_a = p.id) === null || _a === void 0 ? void 0 : _a.replace(/\s+/, '')) !== null && _b !== void 0 ? _b : nanoid.nanoid()),
|
|
1243
|
+
overflow: 'hidden',
|
|
1244
|
+
paddingRight: `${useScrollbarSize()}px`
|
|
1245
|
+
});
|
|
1246
|
+
log('creating singleton bodyStyles', bodyStyles.current);
|
|
1247
|
+
}
|
|
1248
|
+
if (!fixedElementStyles.current) {
|
|
1249
|
+
fixedElementStyles.current = css.css({
|
|
1250
|
+
label: 'ModalElementOverrides_' + ((_d = (_c = p.id) === null || _c === void 0 ? void 0 : _c.replace(/\s+/, '')) !== null && _d !== void 0 ? _d : nanoid.nanoid()),
|
|
1251
|
+
paddingRight: `${useScrollbarSize()}px`
|
|
1252
|
+
});
|
|
1253
|
+
}
|
|
1254
|
+
document.body.classList.add(bodyStyles.current);
|
|
1255
|
+
Array.from(document.querySelectorAll(`.${modalScrollFixClassName}`)).forEach(e => {
|
|
1256
|
+
e.classList.add(fixedElementStyles.current);
|
|
1257
|
+
});
|
|
1258
|
+
};
|
|
1259
|
+
const tryRemoveScrollStyles = () => {
|
|
1260
|
+
if (bodyStyles.current) {
|
|
1261
|
+
log('removing singleton', bodyStyles.current);
|
|
1262
|
+
document.body.classList.remove(bodyStyles.current);
|
|
1263
|
+
}
|
|
1264
|
+
if (fixedElementStyles.current) {
|
|
1265
|
+
Array.from(document.querySelectorAll(`.${modalScrollFixClassName}`)).forEach(e => {
|
|
1266
|
+
e.classList.remove(fixedElementStyles.current);
|
|
1267
|
+
});
|
|
1268
|
+
}
|
|
1269
|
+
};
|
|
1270
|
+
React.useEffect(() => {
|
|
1271
|
+
log('mounted');
|
|
1272
|
+
return () => {
|
|
1273
|
+
var _a;
|
|
1274
|
+
if (showing.current) {
|
|
1275
|
+
log(`un-mount in progress and this modal is showing. decrement the backdrop and try to remove singleton body styles.`);
|
|
1276
|
+
backdrop.setShow(false, (_a = p.id) !== null && _a !== void 0 ? _a : 'Modal');
|
|
1277
|
+
log('backdrop.setShow', false);
|
|
1278
|
+
tryRemoveScrollStyles();
|
|
1279
|
+
}
|
|
1280
|
+
else {
|
|
1281
|
+
log(`un-mount in progress but this modal is not showing. do nothing with the backdrop.`);
|
|
1282
|
+
}
|
|
1283
|
+
log('un-mounted');
|
|
1284
|
+
};
|
|
1285
|
+
}, []);
|
|
1286
|
+
useBooleanChanged((show, previousShow) => {
|
|
1287
|
+
var _a;
|
|
1288
|
+
log('show changed', `${previousShow !== null && previousShow !== void 0 ? previousShow : 'undefined'} > ${show}`);
|
|
1289
|
+
backdrop.setShow(show, (_a = p.id) !== null && _a !== void 0 ? _a : 'Modal');
|
|
1290
|
+
showing.current = show;
|
|
1291
|
+
log('backdrop.setShow', show);
|
|
1292
|
+
if (show) {
|
|
1293
|
+
log('this modal is showing. adding singleton bodyStyles', bodyStyles.current);
|
|
1294
|
+
addScrollStyles();
|
|
1295
|
+
}
|
|
1296
|
+
else {
|
|
1297
|
+
log('this modal is hiding. try removing singleton bodyStyles');
|
|
1298
|
+
tryRemoveScrollStyles();
|
|
1299
|
+
}
|
|
1300
|
+
}, p.show);
|
|
1301
|
+
React__default['default'].useLayoutEffect(() => {
|
|
1302
|
+
var _a;
|
|
1303
|
+
if (p.show === true) {
|
|
1304
|
+
const focusSelector = (_a = p.focusSelector) !== null && _a !== void 0 ? _a : '.modalCloseButton';
|
|
1305
|
+
// still need to wait for the next tick so the children are all rendered.
|
|
1306
|
+
setTimeout(() => {
|
|
1307
|
+
var _a;
|
|
1308
|
+
const element = (_a = contentRef.current) === null || _a === void 0 ? void 0 : _a.querySelector(focusSelector);
|
|
1309
|
+
element === null || element === void 0 ? void 0 : element.focus();
|
|
1310
|
+
log('set focus', focusSelector);
|
|
1311
|
+
});
|
|
1312
|
+
}
|
|
1313
|
+
}, [p.show]);
|
|
1314
|
+
const modalBodyStyles = css.css({
|
|
1315
|
+
maxHeight: p.scrollable ? undefined : '99vh',
|
|
1316
|
+
overflow: 'hidden',
|
|
1317
|
+
zIndex: theme.zIndexes.modal,
|
|
1318
|
+
cursor: 'default',
|
|
1319
|
+
margin: '1rem',
|
|
1320
|
+
backgroundColor: p.noBackground ? undefined : theme.colors.modalBg,
|
|
1321
|
+
border: p.noBackground ? undefined : theme.controls.border,
|
|
1322
|
+
boxShadow: p.noBackground ? undefined : theme.controls.boxShadow,
|
|
1323
|
+
maxWidth: (_c = p.maxWidth) !== null && _c !== void 0 ? _c : theme.breakpoints.tablet,
|
|
1324
|
+
minWidth: (_d = p.minWidth) !== null && _d !== void 0 ? _d : (hasHeader ? '250px' : undefined),
|
|
1325
|
+
opacity: p.show ? 1 : 0,
|
|
1326
|
+
fontSize: theme.fonts.size,
|
|
1327
|
+
fontFamily: theme.fonts.family,
|
|
1328
|
+
fontWeight: 'normal',
|
|
1329
|
+
'&:focus': {
|
|
1330
|
+
outline: 'none'
|
|
1331
|
+
}
|
|
1332
|
+
});
|
|
1333
|
+
const modalHeaderStyles = css.cx(css.css({
|
|
1334
|
+
display: 'flex',
|
|
1335
|
+
justifyContent: 'space-between',
|
|
1336
|
+
alignItems: 'center',
|
|
1337
|
+
backgroundColor: theme.colors.header,
|
|
1338
|
+
padding: '1rem',
|
|
1339
|
+
color: theme.colors.headerFont
|
|
1340
|
+
}), p.headerClassName);
|
|
1341
|
+
const modalContainerStyles = css.css([{
|
|
1342
|
+
position: 'fixed',
|
|
1343
|
+
height: '100%',
|
|
1344
|
+
width: '100%',
|
|
1345
|
+
backgroundColor: "transparent",
|
|
1346
|
+
display: 'flex',
|
|
1347
|
+
justifyContent: 'center',
|
|
1348
|
+
alignItems: 'center',
|
|
1349
|
+
cursor: p.onClick ? 'pointer' : 'default'
|
|
1350
|
+
}, p.scrollable && {
|
|
1351
|
+
overflowY: 'auto',
|
|
1352
|
+
overflowX: 'hidden',
|
|
1353
|
+
alignItems: 'flex-start'
|
|
1354
|
+
}]);
|
|
1355
|
+
if (p.show) {
|
|
1356
|
+
const backdropContainer = document.getElementById(backdrop.portalId);
|
|
1357
|
+
if (backdropContainer) {
|
|
1358
|
+
return reactDom.createPortal((React__default['default'].createElement("div", { onClick: e => {
|
|
1359
|
+
e.stopPropagation();
|
|
1360
|
+
if (!mouseDownElement.current) {
|
|
1361
|
+
if (p.onClick) {
|
|
1362
|
+
log('backdropContainer onClick');
|
|
1363
|
+
p.onClick();
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
mouseDownElement.current = undefined;
|
|
1367
|
+
}, className: css.cx('modalContainer', modalContainerStyles) },
|
|
1368
|
+
React__default['default'].createElement("div", { id: p.id, ref: contentRef, onClick: e => e.stopPropagation(), onMouseDown: e => {
|
|
1369
|
+
mouseDownElement.current = e.target;
|
|
1370
|
+
e.stopPropagation();
|
|
1371
|
+
}, onMouseUp: e => {
|
|
1372
|
+
mouseDownElement.current = undefined;
|
|
1373
|
+
/*
|
|
1374
|
+
MouseDown and MouseUp stopPropagation was added to fix bugs while clicking within the modal.
|
|
1375
|
+
At least in the case of MouseUp, this breaks sliders and the handle grab logic appears to live on window.
|
|
1376
|
+
Turning this off now. Should not cause any issues for MouseUp unlike MouseDown.
|
|
1377
|
+
*/
|
|
1378
|
+
// e.stopPropagation()
|
|
1379
|
+
}, className: css.cx('modalBody', modalBodyStyles, p.className) },
|
|
1380
|
+
React__default['default'].createElement(TabLocker, null,
|
|
1381
|
+
hasHeader && (React__default['default'].createElement("header", { className: css.cx('modalHeader', modalHeaderStyles) },
|
|
1382
|
+
p.heading ? React__default['default'].createElement(Text, { className: css.css({
|
|
1383
|
+
margin: 0,
|
|
1384
|
+
flexGrow: 1
|
|
1385
|
+
}), tag: "h1", bold: true }, p.heading) : React__default['default'].createElement("span", null),
|
|
1386
|
+
p.closeButton && p.onClick ? React__default['default'].createElement(Button, { className: css.cx('modalCloseButton', css.css({
|
|
1387
|
+
color: theme.colors.headerFont,
|
|
1388
|
+
marginLeft: '1rem',
|
|
1389
|
+
backgroundColor: 'transparent'
|
|
1390
|
+
})), variant: "icon", onClick: p.onClick },
|
|
1391
|
+
React__default['default'].createElement(Icon, { id: "close" })) : React__default['default'].createElement("span", null))),
|
|
1392
|
+
p.children)))), backdropContainer);
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
return null;
|
|
1396
|
+
};
|
|
1397
|
+
|
|
1157
1398
|
/** useEffect but ignores the first call on component mount. */
|
|
1158
1399
|
const useIgnoreMount = (effect, deps) => {
|
|
1159
1400
|
const mounted = React__default['default'].useRef(false);
|
|
@@ -1167,15 +1408,6 @@ const useIgnoreMount = (effect, deps) => {
|
|
|
1167
1408
|
}, deps);
|
|
1168
1409
|
};
|
|
1169
1410
|
|
|
1170
|
-
const useLogger = (componentName, enabled) => {
|
|
1171
|
-
return (...messages) => {
|
|
1172
|
-
if (enabled) {
|
|
1173
|
-
// tslint:disable-next-line
|
|
1174
|
-
console.log(`[${componentName}]`, ...messages);
|
|
1175
|
-
}
|
|
1176
|
-
};
|
|
1177
|
-
};
|
|
1178
|
-
|
|
1179
1411
|
const portalId = 'backdrop';
|
|
1180
1412
|
const BackdropContext = React__default['default'].createContext({
|
|
1181
1413
|
showing: false,
|
|
@@ -1222,7 +1454,7 @@ const BackdropContextProvider = (p) => {
|
|
|
1222
1454
|
}
|
|
1223
1455
|
} },
|
|
1224
1456
|
p.children,
|
|
1225
|
-
p.__debug && (React__default['default'].createElement("p", { className: css.css({
|
|
1457
|
+
p.__debug && (React__default['default'].createElement("p", { className: css.cx(modalScrollFixClassName, css.css({
|
|
1226
1458
|
position: 'fixed',
|
|
1227
1459
|
top: 0, right: 0,
|
|
1228
1460
|
backgroundColor: '#ff00004f',
|
|
@@ -1230,7 +1462,7 @@ const BackdropContextProvider = (p) => {
|
|
|
1230
1462
|
padding: '0.5rem',
|
|
1231
1463
|
margin: 0,
|
|
1232
1464
|
zIndex: 9999
|
|
1233
|
-
}) },
|
|
1465
|
+
})) },
|
|
1234
1466
|
"Backdrop showCount: ",
|
|
1235
1467
|
showCount))));
|
|
1236
1468
|
};
|
|
@@ -1445,210 +1677,6 @@ const Checkbox = (props) => {
|
|
|
1445
1677
|
props.children)));
|
|
1446
1678
|
};
|
|
1447
1679
|
|
|
1448
|
-
/** useEffect but it will only fire when the actual truthiness of the value changes.
|
|
1449
|
-
* Use for comparing previous states to next states without all the bullshit around useEffect and component mounting.
|
|
1450
|
-
*/
|
|
1451
|
-
const useBooleanChanged = (effect, dep) => {
|
|
1452
|
-
/*
|
|
1453
|
-
Why?
|
|
1454
|
-
useEffect with a dependency array will fire once on mount even though the dependency list doesn't change.
|
|
1455
|
-
Components like Modal need to communicate when their show status changes.
|
|
1456
|
-
useIgnoreMount is not enough because it only ignores the first render and is therefore a kludge.
|
|
1457
|
-
This is what we want regardless of mount status:
|
|
1458
|
-
true > false = Change
|
|
1459
|
-
false > true = Change
|
|
1460
|
-
true > true = No Change
|
|
1461
|
-
false > false = No Change
|
|
1462
|
-
undefined > false = No Change
|
|
1463
|
-
undefined > true = Change
|
|
1464
|
-
*/
|
|
1465
|
-
const lastValue = React.useRef(undefined);
|
|
1466
|
-
React.useEffect(() => {
|
|
1467
|
-
//console.log('[useBooleanChanged] useEffect called with', dep, 'was', lastValue.current ?? 'undefined')
|
|
1468
|
-
if (!!lastValue.current !== !!dep) {
|
|
1469
|
-
const previous = lastValue.current;
|
|
1470
|
-
lastValue.current = dep;
|
|
1471
|
-
effect(!!lastValue.current, !!previous);
|
|
1472
|
-
//console.log('[useBooleanChanged] change called')
|
|
1473
|
-
}
|
|
1474
|
-
}, [dep]);
|
|
1475
|
-
};
|
|
1476
|
-
|
|
1477
|
-
// Taken from: https://github.com/react-bootstrap/dom-helpers/blob/master/src/scrollbarSize.ts
|
|
1478
|
-
const canUseDom = !!(typeof window !== 'undefined' &&
|
|
1479
|
-
window.document &&
|
|
1480
|
-
window.document.createElement);
|
|
1481
|
-
let size;
|
|
1482
|
-
/** Tells you actual width of the scroll bar. This can vary by browser. */
|
|
1483
|
-
const useScrollbarSize = (recalc) => {
|
|
1484
|
-
if ((!size && size !== 0) || recalc) {
|
|
1485
|
-
if (canUseDom) {
|
|
1486
|
-
const scrollDiv = document.createElement('div');
|
|
1487
|
-
scrollDiv.style.position = 'absolute';
|
|
1488
|
-
scrollDiv.style.top = '-9999px';
|
|
1489
|
-
scrollDiv.style.width = '50px';
|
|
1490
|
-
scrollDiv.style.height = '50px';
|
|
1491
|
-
scrollDiv.style.overflow = 'scroll';
|
|
1492
|
-
document.body.appendChild(scrollDiv);
|
|
1493
|
-
size = scrollDiv.offsetWidth - scrollDiv.clientWidth;
|
|
1494
|
-
document.body.removeChild(scrollDiv);
|
|
1495
|
-
}
|
|
1496
|
-
}
|
|
1497
|
-
return size;
|
|
1498
|
-
};
|
|
1499
|
-
|
|
1500
|
-
const Modal = (p) => {
|
|
1501
|
-
var _a, _b, _c, _d;
|
|
1502
|
-
const backdrop = React.useContext(BackdropContext);
|
|
1503
|
-
const mouseDownElement = React.useRef(undefined);
|
|
1504
|
-
const theme = useThemeSafely();
|
|
1505
|
-
const hasHeader = p.closeButton || p.heading;
|
|
1506
|
-
const contentRef = React__default['default'].useRef(null);
|
|
1507
|
-
const log = useLogger((_a = p.id) !== null && _a !== void 0 ? _a : 'Modal', (_b = p.__debug) !== null && _b !== void 0 ? _b : false);
|
|
1508
|
-
const showing = React.useRef(p.show);
|
|
1509
|
-
const bodyStyles = React.useRef('');
|
|
1510
|
-
const tryRemoveBodyStyles = () => {
|
|
1511
|
-
if (bodyStyles.current) {
|
|
1512
|
-
log('removing singleton', bodyStyles.current);
|
|
1513
|
-
document.body.classList.remove(bodyStyles.current);
|
|
1514
|
-
}
|
|
1515
|
-
};
|
|
1516
|
-
React.useEffect(() => {
|
|
1517
|
-
log('mounted');
|
|
1518
|
-
return () => {
|
|
1519
|
-
var _a;
|
|
1520
|
-
if (showing.current) {
|
|
1521
|
-
log(`un-mount in progress and this modal is showing. decrement the backdrop and try to remove singleton body styles.`);
|
|
1522
|
-
backdrop.setShow(false, (_a = p.id) !== null && _a !== void 0 ? _a : 'Modal');
|
|
1523
|
-
log('backdrop.setShow', false);
|
|
1524
|
-
tryRemoveBodyStyles();
|
|
1525
|
-
}
|
|
1526
|
-
else {
|
|
1527
|
-
log(`un-mount in progress but this modal is not showing. do nothing with the backdrop.`);
|
|
1528
|
-
}
|
|
1529
|
-
log('un-mounted');
|
|
1530
|
-
};
|
|
1531
|
-
}, []);
|
|
1532
|
-
useBooleanChanged((show, previousShow) => {
|
|
1533
|
-
var _a, _b, _c;
|
|
1534
|
-
log('show changed', `${previousShow !== null && previousShow !== void 0 ? previousShow : 'undefined'} > ${show}`);
|
|
1535
|
-
backdrop.setShow(show, (_a = p.id) !== null && _a !== void 0 ? _a : 'Modal');
|
|
1536
|
-
showing.current = show;
|
|
1537
|
-
log('backdrop.setShow', show);
|
|
1538
|
-
if (!bodyStyles.current) {
|
|
1539
|
-
bodyStyles.current = css.css({
|
|
1540
|
-
label: 'ModalBodyOverrides_' + ((_c = (_b = p.id) === null || _b === void 0 ? void 0 : _b.replace(/\s+/, '')) !== null && _c !== void 0 ? _c : nanoid.nanoid()),
|
|
1541
|
-
overflow: 'hidden',
|
|
1542
|
-
paddingRight: `${useScrollbarSize()}px`
|
|
1543
|
-
});
|
|
1544
|
-
log('creating singleton bodyStyles', bodyStyles.current);
|
|
1545
|
-
}
|
|
1546
|
-
if (show) {
|
|
1547
|
-
log('this modal is showing. adding singleton bodyStyles', bodyStyles.current);
|
|
1548
|
-
document.body.classList.add(bodyStyles.current);
|
|
1549
|
-
}
|
|
1550
|
-
else {
|
|
1551
|
-
log('this modal is hiding. try removing singleton bodyStyles');
|
|
1552
|
-
tryRemoveBodyStyles();
|
|
1553
|
-
}
|
|
1554
|
-
}, p.show);
|
|
1555
|
-
React__default['default'].useLayoutEffect(() => {
|
|
1556
|
-
var _a;
|
|
1557
|
-
if (p.show === true) {
|
|
1558
|
-
const focusSelector = (_a = p.focusSelector) !== null && _a !== void 0 ? _a : '.modalCloseButton';
|
|
1559
|
-
// still need to wait for the next tick so the children are all rendered.
|
|
1560
|
-
setTimeout(() => {
|
|
1561
|
-
var _a;
|
|
1562
|
-
const element = (_a = contentRef.current) === null || _a === void 0 ? void 0 : _a.querySelector(focusSelector);
|
|
1563
|
-
element === null || element === void 0 ? void 0 : element.focus();
|
|
1564
|
-
log('set focus', focusSelector);
|
|
1565
|
-
});
|
|
1566
|
-
}
|
|
1567
|
-
}, [p.show]);
|
|
1568
|
-
const modalBodyStyles = css.css({
|
|
1569
|
-
maxHeight: p.scrollable ? undefined : '99vh',
|
|
1570
|
-
overflow: 'hidden',
|
|
1571
|
-
zIndex: theme.zIndexes.modal,
|
|
1572
|
-
cursor: 'default',
|
|
1573
|
-
margin: '1rem',
|
|
1574
|
-
backgroundColor: p.noBackground ? undefined : theme.colors.modalBg,
|
|
1575
|
-
border: p.noBackground ? undefined : theme.controls.border,
|
|
1576
|
-
boxShadow: p.noBackground ? undefined : theme.controls.boxShadow,
|
|
1577
|
-
maxWidth: (_c = p.maxWidth) !== null && _c !== void 0 ? _c : theme.breakpoints.tablet,
|
|
1578
|
-
minWidth: (_d = p.minWidth) !== null && _d !== void 0 ? _d : (hasHeader ? '250px' : undefined),
|
|
1579
|
-
opacity: p.show ? 1 : 0,
|
|
1580
|
-
fontSize: theme.fonts.size,
|
|
1581
|
-
fontFamily: theme.fonts.family,
|
|
1582
|
-
fontWeight: 'normal',
|
|
1583
|
-
'&:focus': {
|
|
1584
|
-
outline: 'none'
|
|
1585
|
-
}
|
|
1586
|
-
});
|
|
1587
|
-
const modalHeaderStyles = css.cx(css.css({
|
|
1588
|
-
display: 'flex',
|
|
1589
|
-
justifyContent: 'space-between',
|
|
1590
|
-
alignItems: 'center',
|
|
1591
|
-
backgroundColor: theme.colors.header,
|
|
1592
|
-
padding: '1rem',
|
|
1593
|
-
color: theme.colors.headerFont
|
|
1594
|
-
}), p.headerClassName);
|
|
1595
|
-
const modalContainerStyles = css.css([{
|
|
1596
|
-
position: 'fixed',
|
|
1597
|
-
height: '100%',
|
|
1598
|
-
width: '100%',
|
|
1599
|
-
backgroundColor: "transparent",
|
|
1600
|
-
display: 'flex',
|
|
1601
|
-
justifyContent: 'center',
|
|
1602
|
-
alignItems: 'center',
|
|
1603
|
-
cursor: p.onClick ? 'pointer' : 'default'
|
|
1604
|
-
}, p.scrollable && {
|
|
1605
|
-
overflowY: 'auto',
|
|
1606
|
-
overflowX: 'hidden',
|
|
1607
|
-
alignItems: 'flex-start'
|
|
1608
|
-
}]);
|
|
1609
|
-
if (p.show) {
|
|
1610
|
-
const backdropContainer = document.getElementById(backdrop.portalId);
|
|
1611
|
-
if (backdropContainer) {
|
|
1612
|
-
return reactDom.createPortal((React__default['default'].createElement("div", { onClick: e => {
|
|
1613
|
-
e.stopPropagation();
|
|
1614
|
-
if (!mouseDownElement.current) {
|
|
1615
|
-
if (p.onClick) {
|
|
1616
|
-
log('backdropContainer onClick');
|
|
1617
|
-
p.onClick();
|
|
1618
|
-
}
|
|
1619
|
-
}
|
|
1620
|
-
mouseDownElement.current = undefined;
|
|
1621
|
-
}, className: css.cx('modalContainer', modalContainerStyles) },
|
|
1622
|
-
React__default['default'].createElement("div", { id: p.id, ref: contentRef, onClick: e => e.stopPropagation(), onMouseDown: e => {
|
|
1623
|
-
mouseDownElement.current = e.target;
|
|
1624
|
-
e.stopPropagation();
|
|
1625
|
-
}, onMouseUp: e => {
|
|
1626
|
-
mouseDownElement.current = undefined;
|
|
1627
|
-
/*
|
|
1628
|
-
MouseDown and MouseUp stopPropagation was added to fix bugs while clicking within the modal.
|
|
1629
|
-
At least in the case of MouseUp, this breaks sliders and the handle grab logic appears to live on window.
|
|
1630
|
-
Turning this off now. Should not cause any issues for MouseUp unlike MouseDown.
|
|
1631
|
-
*/
|
|
1632
|
-
// e.stopPropagation()
|
|
1633
|
-
}, className: css.cx('modalBody', modalBodyStyles, p.className) },
|
|
1634
|
-
React__default['default'].createElement(TabLocker, null,
|
|
1635
|
-
hasHeader && (React__default['default'].createElement("header", { className: css.cx('modalHeader', modalHeaderStyles) },
|
|
1636
|
-
p.heading ? React__default['default'].createElement(Text, { className: css.css({
|
|
1637
|
-
margin: 0,
|
|
1638
|
-
flexGrow: 1
|
|
1639
|
-
}), tag: "h1", bold: true }, p.heading) : React__default['default'].createElement("span", null),
|
|
1640
|
-
p.closeButton && p.onClick ? React__default['default'].createElement(Button, { className: css.cx('modalCloseButton', css.css({
|
|
1641
|
-
color: theme.colors.headerFont,
|
|
1642
|
-
marginLeft: '1rem',
|
|
1643
|
-
backgroundColor: 'transparent'
|
|
1644
|
-
})), variant: "icon", onClick: p.onClick },
|
|
1645
|
-
React__default['default'].createElement(Icon, { id: "close" })) : React__default['default'].createElement("span", null))),
|
|
1646
|
-
p.children)))), backdropContainer);
|
|
1647
|
-
}
|
|
1648
|
-
}
|
|
1649
|
-
return null;
|
|
1650
|
-
};
|
|
1651
|
-
|
|
1652
1680
|
const ConfirmModal = (props) => {
|
|
1653
1681
|
const theme = useThemeSafely();
|
|
1654
1682
|
const modalStyle = css.css `
|
|
@@ -2840,7 +2868,7 @@ const Label = (props) => {
|
|
|
2840
2868
|
* https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia
|
|
2841
2869
|
*/
|
|
2842
2870
|
const useMediaQuery = (query) => {
|
|
2843
|
-
const [matches, setMatches] = React.useState(
|
|
2871
|
+
const [matches, setMatches] = React.useState(window.matchMedia(query).matches);
|
|
2844
2872
|
React.useEffect(() => {
|
|
2845
2873
|
const media = window.matchMedia(query);
|
|
2846
2874
|
if (media.matches !== matches) {
|
|
@@ -3772,14 +3800,14 @@ const SearchBox = (props) => {
|
|
|
3772
3800
|
return (_c = props.onSubmit) === null || _c === void 0 ? void 0 : _c.call(props);
|
|
3773
3801
|
});
|
|
3774
3802
|
const theme = useThemeSafely();
|
|
3775
|
-
const submitButton = (React__namespace.createElement(Button, { tabIndex: -1, disabled: waiting, readOnly: !props.onSubmit, type: "submit", className: css.css({
|
|
3803
|
+
const submitButton = (React__namespace.createElement(Button, { tabIndex: -1, disabled: waiting, readOnly: !props.onSubmit, type: "submit", className: css.cx(css.css({
|
|
3776
3804
|
color: `${theme.colors.font} !important;`,
|
|
3777
3805
|
fontSize: '1rem'
|
|
3778
|
-
}), variant: "icon", small: true },
|
|
3806
|
+
}), props.buttonClassName), variant: "icon", small: true },
|
|
3779
3807
|
React__namespace.createElement(Icon, { id: waiting ? 'waiting' : 'search', spin: waiting })));
|
|
3780
3808
|
//TB: FUTURE replace with new inputs
|
|
3781
3809
|
return (React__namespace.createElement(Form, { role: "search", className: css.cx('searchBox', props.className), onSubmit: onSubmit },
|
|
3782
|
-
React__namespace.createElement(Input, { autoFocus: props.autoFocus, id: props.id, debounceMs: props.debounceMs, disabled: waiting, type: "text", value: props.value, placeholder: props.placeholder, round: props.round, onChange: props.onChange, rightControl: submitButton })));
|
|
3810
|
+
React__namespace.createElement(Input, { inputClassName: props.inputClassName, autoFocus: props.autoFocus, id: props.id, debounceMs: props.debounceMs, disabled: waiting, type: "text", value: props.value, placeholder: props.placeholder, round: props.round, onChange: props.onChange, rightControl: submitButton })));
|
|
3783
3811
|
};
|
|
3784
3812
|
|
|
3785
3813
|
const GlobalStyles = (p) => {
|
|
@@ -3955,8 +3983,17 @@ const TabHeader = (p) => {
|
|
|
3955
3983
|
overflow: 'hidden',
|
|
3956
3984
|
});
|
|
3957
3985
|
}
|
|
3986
|
+
let title = tab.title;
|
|
3987
|
+
let buttonContent;
|
|
3988
|
+
if (typeof tab.name === 'string') {
|
|
3989
|
+
title !== null && title !== void 0 ? title : (title = tab.name);
|
|
3990
|
+
buttonContent = React__namespace.createElement(Text, { tag: "div", align: "center", ellipsis: true }, tab.name);
|
|
3991
|
+
}
|
|
3992
|
+
else {
|
|
3993
|
+
buttonContent = tab.name;
|
|
3994
|
+
}
|
|
3958
3995
|
return (React__namespace.createElement("li", { key: index, className: css.cx(tabStyles, p.tabClassName) },
|
|
3959
|
-
React__namespace.createElement(Button, { disabled: tabsChanging, className: buttonStyles, variant: buttonVariant, title:
|
|
3996
|
+
React__namespace.createElement(Button, { disabled: tabsChanging, className: buttonStyles, variant: buttonVariant, title: title, readOnly: active, onClick: () => {
|
|
3960
3997
|
const onChange = () => {
|
|
3961
3998
|
var _a;
|
|
3962
3999
|
setTabIndex(index);
|
|
@@ -3978,7 +4015,7 @@ const TabHeader = (p) => {
|
|
|
3978
4015
|
else {
|
|
3979
4016
|
onChange();
|
|
3980
4017
|
}
|
|
3981
|
-
} },
|
|
4018
|
+
} }, buttonContent)));
|
|
3982
4019
|
})),
|
|
3983
4020
|
variant === 'tab' && (React__namespace.createElement("div", { className: css.cx(css.css({
|
|
3984
4021
|
label: 'TabHeaderDivider',
|
|
@@ -4821,6 +4858,7 @@ exports.defaultTheme = defaultTheme;
|
|
|
4821
4858
|
exports.enumToEntities = enumToEntities;
|
|
4822
4859
|
exports.getCurrencyDisplay = getCurrencyDisplay;
|
|
4823
4860
|
exports.getFileSizeDisplay = getFileSizeDisplay;
|
|
4861
|
+
exports.modalScrollFixClassName = modalScrollFixClassName;
|
|
4824
4862
|
exports.useAccordianState = useAccordianState;
|
|
4825
4863
|
exports.useBooleanChanged = useBooleanChanged;
|
|
4826
4864
|
exports.useIgnoreMount = useIgnoreMount;
|