@mackin.com/styleguide 8.11.0 → 8.12.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 +11 -2
- package/index.js +242 -239
- package/package.json +1 -1
package/index.d.ts
CHANGED
|
@@ -54,6 +54,11 @@ interface AutocompleteProps<T extends AutocompleteValue> {
|
|
|
54
54
|
/** Defaults to 0ms. */
|
|
55
55
|
getOptionsDebounceMs?: number;
|
|
56
56
|
autoFocus?: boolean;
|
|
57
|
+
/** Options passed to the lodash debounce function. */
|
|
58
|
+
debounceOptions?: {
|
|
59
|
+
leading: boolean;
|
|
60
|
+
trailing: boolean;
|
|
61
|
+
};
|
|
57
62
|
/** Will enable scrolling in the results list. */
|
|
58
63
|
allowScroll?: boolean;
|
|
59
64
|
onChange: (value: string) => void;
|
|
@@ -467,8 +472,6 @@ interface ModalProps {
|
|
|
467
472
|
__debug?: boolean;
|
|
468
473
|
onClick?: () => void;
|
|
469
474
|
}
|
|
470
|
-
/** Add to fixed positioned elements so their contents do not jump around when scrolling is disabled. */
|
|
471
|
-
declare const modalScrollFixClassName = "modal-scroll-fix";
|
|
472
475
|
declare const Modal: (p: ModalProps) => React__default.ReactPortal | null;
|
|
473
476
|
|
|
474
477
|
declare const Nav: (props: {
|
|
@@ -930,9 +933,12 @@ declare const Table: (props: {
|
|
|
930
933
|
caption?: string | JSX.Element;
|
|
931
934
|
children?: any;
|
|
932
935
|
noCellBorder?: boolean;
|
|
936
|
+
/** Styles applied to the table element. */
|
|
933
937
|
className?: string;
|
|
934
938
|
/** Colors alternate rows. */
|
|
935
939
|
altRows?: boolean;
|
|
940
|
+
/** Styles applied to the table wrapper scroll container. */
|
|
941
|
+
wrapperClassName?: string;
|
|
936
942
|
}) => JSX.Element;
|
|
937
943
|
declare const Tr: (props: {
|
|
938
944
|
className?: string;
|
|
@@ -1146,4 +1152,7 @@ interface TabContainerProps {
|
|
|
1146
1152
|
}
|
|
1147
1153
|
declare const TabContainer: (p: TabContainerProps) => JSX.Element;
|
|
1148
1154
|
|
|
1155
|
+
/** Add to fixed positioned elements so their contents do not jump around when scrolling is disabled. */
|
|
1156
|
+
declare const modalScrollFixClassName = "modal-scroll-fix";
|
|
1157
|
+
|
|
1149
1158
|
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, SliderProps, SliderValue, 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
|
@@ -891,17 +891,17 @@ const TabLocker = (props) => {
|
|
|
891
891
|
const defaultMaxShownValues = 7;
|
|
892
892
|
const buttonMarkerClass = 'ListItem__button';
|
|
893
893
|
const Autocomplete = (p) => {
|
|
894
|
-
var _a;
|
|
894
|
+
var _a, _b;
|
|
895
895
|
const theme = useThemeSafely();
|
|
896
896
|
const element = React__namespace.useRef(null);
|
|
897
897
|
const input = React__namespace.useRef(null);
|
|
898
898
|
const list = React__namespace.useRef(null);
|
|
899
899
|
const [values, setValues] = React__namespace.useState([]);
|
|
900
900
|
const showValues = React__namespace.useMemo(() => values.length > 0, [values]);
|
|
901
|
+
const maxShowValues = (_a = p.maxShownValues) !== null && _a !== void 0 ? _a : defaultMaxShownValues;
|
|
901
902
|
const shownValues = React__namespace.useMemo(() => {
|
|
902
|
-
var _a;
|
|
903
903
|
if (!p.allowScroll) {
|
|
904
|
-
return values.slice(0,
|
|
904
|
+
return values.slice(0, maxShowValues);
|
|
905
905
|
}
|
|
906
906
|
return values.slice();
|
|
907
907
|
}, [values]);
|
|
@@ -917,7 +917,7 @@ const Autocomplete = (p) => {
|
|
|
917
917
|
else {
|
|
918
918
|
setValues([]);
|
|
919
919
|
}
|
|
920
|
-
}, (
|
|
920
|
+
}, (_b = p.getOptionsDebounceMs) !== null && _b !== void 0 ? _b : 0, { leading: false, trailing: true }));
|
|
921
921
|
const getNextTabElement = (fromIndex, direction) => {
|
|
922
922
|
var _a, _b, _c;
|
|
923
923
|
if (fromIndex === -1) {
|
|
@@ -1003,9 +1003,9 @@ const Autocomplete = (p) => {
|
|
|
1003
1003
|
borderBottomRightRadius: listBorderRadius,
|
|
1004
1004
|
borderBottomLeftRadius: listBorderRadius,
|
|
1005
1005
|
}
|
|
1006
|
-
}), p.allowScroll && css.css({
|
|
1006
|
+
}), p.allowScroll && shownValues.length > maxShowValues && css.css({
|
|
1007
1007
|
overflowY: 'scroll',
|
|
1008
|
-
maxHeight: `calc(${theme.controls.height} * ${
|
|
1008
|
+
maxHeight: `calc(${theme.controls.height} * ${maxShowValues})`
|
|
1009
1009
|
})) },
|
|
1010
1010
|
shownValues.map((value, listItemIndex) => {
|
|
1011
1011
|
return (React__namespace.createElement(ListItem, { key: getAutocompleteValueId(value), variant: "full" },
|
|
@@ -1175,239 +1175,8 @@ const Backdrop$1 = (props) => {
|
|
|
1175
1175
|
}, ref: backdrop, className: css.cx('backdrop', styles, props.className) }, props.children));
|
|
1176
1176
|
};
|
|
1177
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
1178
|
/** 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;
|
|
1410
|
-
};
|
|
1179
|
+
const modalScrollFixClassName = 'modal-scroll-fix';
|
|
1411
1180
|
|
|
1412
1181
|
/** useEffect but ignores the first call on component mount. */
|
|
1413
1182
|
const useIgnoreMount = (effect, deps) => {
|
|
@@ -1422,6 +1191,15 @@ const useIgnoreMount = (effect, deps) => {
|
|
|
1422
1191
|
}, deps);
|
|
1423
1192
|
};
|
|
1424
1193
|
|
|
1194
|
+
const useLogger = (componentName, enabled) => {
|
|
1195
|
+
return (...messages) => {
|
|
1196
|
+
if (enabled) {
|
|
1197
|
+
// tslint:disable-next-line
|
|
1198
|
+
console.log(`[${componentName}]`, ...messages);
|
|
1199
|
+
}
|
|
1200
|
+
};
|
|
1201
|
+
};
|
|
1202
|
+
|
|
1425
1203
|
const portalId = 'backdrop';
|
|
1426
1204
|
const BackdropContext = React__default['default'].createContext({
|
|
1427
1205
|
showing: false,
|
|
@@ -1691,6 +1469,229 @@ const Checkbox = (props) => {
|
|
|
1691
1469
|
props.children)));
|
|
1692
1470
|
};
|
|
1693
1471
|
|
|
1472
|
+
/** useEffect but it will only fire when the actual truthiness of the value changes.
|
|
1473
|
+
* Use for comparing previous states to next states without all the bullshit around useEffect and component mounting.
|
|
1474
|
+
*/
|
|
1475
|
+
const useBooleanChanged = (effect, dep) => {
|
|
1476
|
+
/*
|
|
1477
|
+
Why?
|
|
1478
|
+
useEffect with a dependency array will fire once on mount even though the dependency list doesn't change.
|
|
1479
|
+
Components like Modal need to communicate when their show status changes.
|
|
1480
|
+
useIgnoreMount is not enough because it only ignores the first render and is therefore a kludge.
|
|
1481
|
+
This is what we want regardless of mount status:
|
|
1482
|
+
true > false = Change
|
|
1483
|
+
false > true = Change
|
|
1484
|
+
true > true = No Change
|
|
1485
|
+
false > false = No Change
|
|
1486
|
+
undefined > false = No Change
|
|
1487
|
+
undefined > true = Change
|
|
1488
|
+
*/
|
|
1489
|
+
const lastValue = React.useRef(undefined);
|
|
1490
|
+
React.useEffect(() => {
|
|
1491
|
+
//console.log('[useBooleanChanged] useEffect called with', dep, 'was', lastValue.current ?? 'undefined')
|
|
1492
|
+
if (!!lastValue.current !== !!dep) {
|
|
1493
|
+
const previous = lastValue.current;
|
|
1494
|
+
lastValue.current = dep;
|
|
1495
|
+
effect(!!lastValue.current, !!previous);
|
|
1496
|
+
//console.log('[useBooleanChanged] change called')
|
|
1497
|
+
}
|
|
1498
|
+
}, [dep]);
|
|
1499
|
+
};
|
|
1500
|
+
|
|
1501
|
+
// Taken from: https://github.com/react-bootstrap/dom-helpers/blob/master/src/scrollbarSize.ts
|
|
1502
|
+
const canUseDom = !!(typeof window !== 'undefined' &&
|
|
1503
|
+
window.document &&
|
|
1504
|
+
window.document.createElement);
|
|
1505
|
+
let size;
|
|
1506
|
+
/** Tells you actual width of the scroll bar. This can vary by browser. */
|
|
1507
|
+
const useScrollbarSize = (recalc) => {
|
|
1508
|
+
if ((!size && size !== 0) || recalc) {
|
|
1509
|
+
if (canUseDom) {
|
|
1510
|
+
const scrollDiv = document.createElement('div');
|
|
1511
|
+
scrollDiv.style.position = 'absolute';
|
|
1512
|
+
scrollDiv.style.top = '-9999px';
|
|
1513
|
+
scrollDiv.style.width = '50px';
|
|
1514
|
+
scrollDiv.style.height = '50px';
|
|
1515
|
+
scrollDiv.style.overflow = 'scroll';
|
|
1516
|
+
document.body.appendChild(scrollDiv);
|
|
1517
|
+
size = scrollDiv.offsetWidth - scrollDiv.clientWidth;
|
|
1518
|
+
document.body.removeChild(scrollDiv);
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
return size;
|
|
1522
|
+
};
|
|
1523
|
+
|
|
1524
|
+
const Modal = (p) => {
|
|
1525
|
+
var _a, _b, _c, _d;
|
|
1526
|
+
const backdrop = React.useContext(BackdropContext);
|
|
1527
|
+
const mouseDownElement = React.useRef(undefined);
|
|
1528
|
+
const theme = useThemeSafely();
|
|
1529
|
+
const hasHeader = p.closeButton || p.heading;
|
|
1530
|
+
const contentRef = React__default['default'].useRef(null);
|
|
1531
|
+
const log = useLogger((_a = p.id) !== null && _a !== void 0 ? _a : 'Modal', (_b = p.__debug) !== null && _b !== void 0 ? _b : false);
|
|
1532
|
+
const showing = React.useRef(p.show);
|
|
1533
|
+
const bodyStyles = React.useRef('');
|
|
1534
|
+
const fixedElementStyles = React.useRef('');
|
|
1535
|
+
const addScrollStyles = () => {
|
|
1536
|
+
var _a, _b, _c, _d;
|
|
1537
|
+
if (!bodyStyles.current) {
|
|
1538
|
+
bodyStyles.current = css.css({
|
|
1539
|
+
label: 'ModalBodyOverrides_' + ((_b = (_a = p.id) === null || _a === void 0 ? void 0 : _a.replace(/\s+/, '')) !== null && _b !== void 0 ? _b : nanoid.nanoid()),
|
|
1540
|
+
overflow: 'hidden',
|
|
1541
|
+
paddingRight: `${useScrollbarSize()}px`
|
|
1542
|
+
});
|
|
1543
|
+
log('creating singleton bodyStyles', bodyStyles.current);
|
|
1544
|
+
}
|
|
1545
|
+
if (!fixedElementStyles.current) {
|
|
1546
|
+
fixedElementStyles.current = css.css({
|
|
1547
|
+
label: 'ModalElementOverrides_' + ((_d = (_c = p.id) === null || _c === void 0 ? void 0 : _c.replace(/\s+/, '')) !== null && _d !== void 0 ? _d : nanoid.nanoid()),
|
|
1548
|
+
paddingRight: `${useScrollbarSize()}px`
|
|
1549
|
+
});
|
|
1550
|
+
}
|
|
1551
|
+
document.body.classList.add(bodyStyles.current);
|
|
1552
|
+
Array.from(document.querySelectorAll(`.${modalScrollFixClassName}`)).forEach(e => {
|
|
1553
|
+
e.classList.add(fixedElementStyles.current);
|
|
1554
|
+
});
|
|
1555
|
+
};
|
|
1556
|
+
const tryRemoveScrollStyles = () => {
|
|
1557
|
+
if (bodyStyles.current) {
|
|
1558
|
+
log('removing singleton', bodyStyles.current);
|
|
1559
|
+
document.body.classList.remove(bodyStyles.current);
|
|
1560
|
+
}
|
|
1561
|
+
if (fixedElementStyles.current) {
|
|
1562
|
+
Array.from(document.querySelectorAll(`.${modalScrollFixClassName}`)).forEach(e => {
|
|
1563
|
+
e.classList.remove(fixedElementStyles.current);
|
|
1564
|
+
});
|
|
1565
|
+
}
|
|
1566
|
+
};
|
|
1567
|
+
React.useEffect(() => {
|
|
1568
|
+
log('mounted');
|
|
1569
|
+
return () => {
|
|
1570
|
+
var _a;
|
|
1571
|
+
if (showing.current) {
|
|
1572
|
+
log(`un-mount in progress and this modal is showing. decrement the backdrop and try to remove singleton body styles.`);
|
|
1573
|
+
backdrop.setShow(false, (_a = p.id) !== null && _a !== void 0 ? _a : 'Modal');
|
|
1574
|
+
log('backdrop.setShow', false);
|
|
1575
|
+
tryRemoveScrollStyles();
|
|
1576
|
+
}
|
|
1577
|
+
else {
|
|
1578
|
+
log(`un-mount in progress but this modal is not showing. do nothing with the backdrop.`);
|
|
1579
|
+
}
|
|
1580
|
+
log('un-mounted');
|
|
1581
|
+
};
|
|
1582
|
+
}, []);
|
|
1583
|
+
useBooleanChanged((show, previousShow) => {
|
|
1584
|
+
var _a;
|
|
1585
|
+
log('show changed', `${previousShow !== null && previousShow !== void 0 ? previousShow : 'undefined'} > ${show}`);
|
|
1586
|
+
backdrop.setShow(show, (_a = p.id) !== null && _a !== void 0 ? _a : 'Modal');
|
|
1587
|
+
showing.current = show;
|
|
1588
|
+
log('backdrop.setShow', show);
|
|
1589
|
+
if (show) {
|
|
1590
|
+
log('this modal is showing. adding singleton bodyStyles', bodyStyles.current);
|
|
1591
|
+
addScrollStyles();
|
|
1592
|
+
}
|
|
1593
|
+
else {
|
|
1594
|
+
log('this modal is hiding. try removing singleton bodyStyles');
|
|
1595
|
+
tryRemoveScrollStyles();
|
|
1596
|
+
}
|
|
1597
|
+
}, p.show);
|
|
1598
|
+
React__default['default'].useLayoutEffect(() => {
|
|
1599
|
+
var _a;
|
|
1600
|
+
if (p.show === true) {
|
|
1601
|
+
const focusSelector = (_a = p.focusSelector) !== null && _a !== void 0 ? _a : '.modalCloseButton';
|
|
1602
|
+
// still need to wait for the next tick so the children are all rendered.
|
|
1603
|
+
setTimeout(() => {
|
|
1604
|
+
var _a;
|
|
1605
|
+
const element = (_a = contentRef.current) === null || _a === void 0 ? void 0 : _a.querySelector(focusSelector);
|
|
1606
|
+
element === null || element === void 0 ? void 0 : element.focus();
|
|
1607
|
+
log('set focus', focusSelector);
|
|
1608
|
+
});
|
|
1609
|
+
}
|
|
1610
|
+
}, [p.show]);
|
|
1611
|
+
const modalBodyStyles = css.css({
|
|
1612
|
+
maxHeight: p.scrollable ? undefined : '99vh',
|
|
1613
|
+
overflow: 'hidden',
|
|
1614
|
+
zIndex: theme.zIndexes.modal,
|
|
1615
|
+
cursor: 'default',
|
|
1616
|
+
margin: '1rem',
|
|
1617
|
+
backgroundColor: p.noBackground ? undefined : theme.colors.modalBg,
|
|
1618
|
+
border: p.noBackground ? undefined : theme.controls.border,
|
|
1619
|
+
boxShadow: p.noBackground ? undefined : theme.controls.boxShadow,
|
|
1620
|
+
maxWidth: (_c = p.maxWidth) !== null && _c !== void 0 ? _c : theme.breakpoints.tablet,
|
|
1621
|
+
minWidth: (_d = p.minWidth) !== null && _d !== void 0 ? _d : (hasHeader ? '250px' : undefined),
|
|
1622
|
+
opacity: p.show ? 1 : 0,
|
|
1623
|
+
fontSize: theme.fonts.size,
|
|
1624
|
+
fontFamily: theme.fonts.family,
|
|
1625
|
+
fontWeight: 'normal',
|
|
1626
|
+
'&:focus': {
|
|
1627
|
+
outline: 'none'
|
|
1628
|
+
}
|
|
1629
|
+
});
|
|
1630
|
+
const modalHeaderStyles = css.cx(css.css({
|
|
1631
|
+
display: 'flex',
|
|
1632
|
+
justifyContent: 'space-between',
|
|
1633
|
+
alignItems: 'center',
|
|
1634
|
+
backgroundColor: theme.colors.header,
|
|
1635
|
+
padding: '1rem',
|
|
1636
|
+
color: theme.colors.headerFont
|
|
1637
|
+
}), p.headerClassName);
|
|
1638
|
+
const modalContainerStyles = css.css([{
|
|
1639
|
+
position: 'fixed',
|
|
1640
|
+
height: '100%',
|
|
1641
|
+
width: '100%',
|
|
1642
|
+
backgroundColor: "transparent",
|
|
1643
|
+
display: 'flex',
|
|
1644
|
+
justifyContent: 'center',
|
|
1645
|
+
alignItems: 'center',
|
|
1646
|
+
cursor: p.onClick ? 'pointer' : 'default'
|
|
1647
|
+
}, p.scrollable && {
|
|
1648
|
+
overflowY: 'auto',
|
|
1649
|
+
overflowX: 'hidden',
|
|
1650
|
+
alignItems: 'flex-start'
|
|
1651
|
+
}]);
|
|
1652
|
+
if (p.show) {
|
|
1653
|
+
const backdropContainer = document.getElementById(backdrop.portalId);
|
|
1654
|
+
if (backdropContainer) {
|
|
1655
|
+
return reactDom.createPortal((React__default['default'].createElement("div", { onClick: e => {
|
|
1656
|
+
e.stopPropagation();
|
|
1657
|
+
if (!mouseDownElement.current) {
|
|
1658
|
+
if (p.onClick) {
|
|
1659
|
+
log('backdropContainer onClick');
|
|
1660
|
+
p.onClick();
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
mouseDownElement.current = undefined;
|
|
1664
|
+
}, className: css.cx('modalContainer', modalContainerStyles) },
|
|
1665
|
+
React__default['default'].createElement("div", { id: p.id, ref: contentRef, onClick: e => e.stopPropagation(), onMouseDown: e => {
|
|
1666
|
+
mouseDownElement.current = e.target;
|
|
1667
|
+
e.stopPropagation();
|
|
1668
|
+
}, onMouseUp: e => {
|
|
1669
|
+
mouseDownElement.current = undefined;
|
|
1670
|
+
/*
|
|
1671
|
+
MouseDown and MouseUp stopPropagation was added to fix bugs while clicking within the modal.
|
|
1672
|
+
At least in the case of MouseUp, this breaks sliders and the handle grab logic appears to live on window.
|
|
1673
|
+
Turning this off now. Should not cause any issues for MouseUp unlike MouseDown.
|
|
1674
|
+
*/
|
|
1675
|
+
// e.stopPropagation()
|
|
1676
|
+
}, className: css.cx('modalBody', modalBodyStyles, p.className) },
|
|
1677
|
+
React__default['default'].createElement(TabLocker, null,
|
|
1678
|
+
hasHeader && (React__default['default'].createElement("header", { className: css.cx('modalHeader', modalHeaderStyles) },
|
|
1679
|
+
p.heading ? React__default['default'].createElement(Text, { className: css.css({
|
|
1680
|
+
margin: 0,
|
|
1681
|
+
flexGrow: 1
|
|
1682
|
+
}), tag: "h1", bold: true }, p.heading) : React__default['default'].createElement("span", null),
|
|
1683
|
+
p.closeButton && p.onClick ? React__default['default'].createElement(Button, { className: css.cx('modalCloseButton', css.css({
|
|
1684
|
+
color: theme.colors.headerFont,
|
|
1685
|
+
marginLeft: '1rem',
|
|
1686
|
+
backgroundColor: 'transparent'
|
|
1687
|
+
})), variant: "icon", onClick: p.onClick },
|
|
1688
|
+
React__default['default'].createElement(Icon, { id: "close" })) : React__default['default'].createElement("span", null))),
|
|
1689
|
+
p.children)))), backdropContainer);
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
return null;
|
|
1693
|
+
};
|
|
1694
|
+
|
|
1694
1695
|
const ConfirmModal = (props) => {
|
|
1695
1696
|
const theme = useThemeSafely();
|
|
1696
1697
|
const modalStyle = css.css `
|
|
@@ -4089,6 +4090,7 @@ const TabHeader = (p) => {
|
|
|
4089
4090
|
const Table = (props) => {
|
|
4090
4091
|
const theme = useThemeSafely();
|
|
4091
4092
|
const tableStyles = css.css `
|
|
4093
|
+
label: Table;
|
|
4092
4094
|
width: 100%;
|
|
4093
4095
|
border-collapse: collapse;
|
|
4094
4096
|
${props.noCellBorder && `
|
|
@@ -4104,11 +4106,12 @@ const Table = (props) => {
|
|
|
4104
4106
|
`}
|
|
4105
4107
|
`;
|
|
4106
4108
|
const wrapperStyles = css.css `
|
|
4109
|
+
label: TableScrollWrapper;
|
|
4107
4110
|
width:100%;
|
|
4108
4111
|
overflow-y: auto;
|
|
4109
4112
|
padding:0 1px; //fixes always showing of the table scroller
|
|
4110
4113
|
`;
|
|
4111
|
-
return (React__namespace.createElement("div", { className: wrapperStyles },
|
|
4114
|
+
return (React__namespace.createElement("div", { className: css.cx(wrapperStyles, props.wrapperClassName) },
|
|
4112
4115
|
React__namespace.createElement("table", { className: css.cx(tableStyles, props.className) },
|
|
4113
4116
|
props.caption && React__namespace.createElement("caption", { className: css.css({
|
|
4114
4117
|
fontWeight: 'bold',
|