@mackin.com/styleguide 8.7.0 → 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 +3 -1
- package/index.js +246 -217
- 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: {
|
|
@@ -1107,4 +1109,4 @@ interface TabContainerProps {
|
|
|
1107
1109
|
}
|
|
1108
1110
|
declare const TabContainer: (p: TabContainerProps) => JSX.Element;
|
|
1109
1111
|
|
|
1110
|
-
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) {
|
|
@@ -4830,6 +4858,7 @@ exports.defaultTheme = defaultTheme;
|
|
|
4830
4858
|
exports.enumToEntities = enumToEntities;
|
|
4831
4859
|
exports.getCurrencyDisplay = getCurrencyDisplay;
|
|
4832
4860
|
exports.getFileSizeDisplay = getFileSizeDisplay;
|
|
4861
|
+
exports.modalScrollFixClassName = modalScrollFixClassName;
|
|
4833
4862
|
exports.useAccordianState = useAccordianState;
|
|
4834
4863
|
exports.useBooleanChanged = useBooleanChanged;
|
|
4835
4864
|
exports.useIgnoreMount = useIgnoreMount;
|