grommet
Version:
focus on the essential experience
236 lines (233 loc) • 9.51 kB
JavaScript
import React, { useCallback, useContext, useEffect, useRef, useState } from 'react';
import styled from 'styled-components';
import { Add } from 'grommet-icons/icons/Add';
import { Subtract } from 'grommet-icons/icons/Subtract';
import { Box } from '../Box';
import { Button } from '../Button';
import { DropButton } from '../DropButton';
import { Keyboard } from '../Keyboard';
import { useThemeValue } from '../../utils/useThemeValue';
import { MessageContext } from '../../contexts/MessageContext';
import { edgeStyle } from '../../utils/styles';
// We determined 12 empirically as being wide enough to hit but
// not too wide to cause false hits.
var STEP = 12; // Used to determine the width change on resize
var resizerWidth = 24; // minimum width required for WCAG compliance
var StyledResizer = styled(DropButton).withConfig({
displayName: "Resizer__StyledResizer",
componentId: "sc-8l808w-0"
})(["display:flex;justify-content:center;", ";", ";", " ", " ", " ", " position:absolute;width:", "px;height:100%;top:0;cursor:col-resize;z-index:1;"], function (props) {
var _props$theme$dataTabl;
return edgeStyle('padding-top', (_props$theme$dataTabl = props.theme.dataTable) == null || (_props$theme$dataTabl = _props$theme$dataTabl.resize) == null || (_props$theme$dataTabl = _props$theme$dataTabl.padding) == null ? void 0 : _props$theme$dataTabl.vertical, false, props.theme.global.edgeSize.responsiveBreakpoint, props.theme);
}, function (props) {
var _props$theme$dataTabl2;
return edgeStyle('padding-bottom', (_props$theme$dataTabl2 = props.theme.dataTable) == null || (_props$theme$dataTabl2 = _props$theme$dataTabl2.resize) == null || (_props$theme$dataTabl2 = _props$theme$dataTabl2.padding) == null ? void 0 : _props$theme$dataTabl2.vertical, false, props.theme.global.edgeSize.responsiveBreakpoint, props.theme);
}, function (props) {
return props.side === 'start' && "margin-left: -" + (resizerWidth / 2 - props.theme.global.borderSize.xsmall.replace('px', '')) + "px;";
}, function (props) {
return props.side === 'start' && "left: 0;";
}, function (props) {
return props.side === 'end' && "margin-right: -" + (resizerWidth / 2 - props.theme.global.borderSize.xsmall.replace('px', '')) + "px;";
}, function (props) {
return props.side === 'end' && "right: 0;";
}, resizerWidth);
var Resizer = function Resizer(_ref) {
var _theme$dataTable$icon, _theme$dataTable$icon2, _border;
var onResize = _ref.onResize,
property = _ref.property,
headerText = _ref.headerText,
messages = _ref.messages,
headerId = _ref.headerId;
var _useThemeValue = useThemeValue(),
theme = _useThemeValue.theme;
var _useState = useState(false),
active = _useState[0],
setActive = _useState[1];
var _useState2 = useState(),
start = _useState2[0],
setStart = _useState2[1];
var _useState3 = useState(0),
width = _useState3[0],
setWidth = _useState3[1];
var ref = useRef();
var thRef = useRef();
var _useContext = useContext(MessageContext),
format = _useContext.format;
var ResizeIncreaseIcon = ((_theme$dataTable$icon = theme.dataTable.icons) == null ? void 0 : _theme$dataTable$icon.resizeIncrease) || Add;
var ResizeDecreaseIcon = ((_theme$dataTable$icon2 = theme.dataTable.icons) == null ? void 0 : _theme$dataTable$icon2.resizeDecrease) || Subtract;
// Set the initial width based on the TH element's width and
// store th element ref
useEffect(function () {
if (ref.current) {
var element = ref.current;
// find TH parent
while (element && element.nodeName !== 'TH') element = element.parentNode;
thRef.current = element;
var rect = element.getBoundingClientRect();
// Set initial width based on the TH element's width
setWidth(rect.width);
}
}, []);
var onResizeStart = useCallback(function (event) {
var clientX = event.touches ? event.touches[0].clientX : event.clientX;
if (thRef.current) {
var element = thRef.current;
var rect = element.getBoundingClientRect();
setStart(clientX);
setWidth(rect.width);
setActive(true);
}
}, []);
var onResizeMove = useCallback(function (event) {
var clientX = event.touches ? event.touches[0].clientX : event.clientX;
var nextWidth = Math.max(STEP, width + (clientX - start));
onResize(property, nextWidth);
}, [onResize, property, start, width]);
var onResizeEnd = useCallback(function () {
setActive(false);
setStart(undefined);
setWidth(undefined);
}, []);
useEffect(function () {
var remove = function remove() {
document.removeEventListener('mouseup', onResizeEnd);
document.removeEventListener('mousemove', onResizeMove);
document.removeEventListener('touchend', onResizeEnd);
document.removeEventListener('touchmove', onResizeMove);
};
if (active) {
document.addEventListener('mouseup', onResizeEnd);
document.addEventListener('mousemove', onResizeMove);
document.addEventListener('touchend', onResizeEnd);
document.addEventListener('touchmove', onResizeMove);
return remove;
}
remove();
return undefined;
}, [active, onResizeMove, onResizeEnd]);
var border;
if (theme.dataTable.resize.border.color && theme.dataTable.resize.border.side) {
var _theme$dataTable$resi = theme.dataTable.resize.border,
color = _theme$dataTable$resi.color,
_theme$dataTable$resi2 = _theme$dataTable$resi.side,
side = _theme$dataTable$resi2 === void 0 ? 'end' : _theme$dataTable$resi2;
border = {
color: color,
side: side
};
}
var hoverBorder = border;
if (theme.dataTable.resize.hover && theme.dataTable.resize.hover.border) {
var _theme$dataTable$resi3 = theme.dataTable.resize.hover.border,
_color = _theme$dataTable$resi3.color,
_theme$dataTable$resi4 = _theme$dataTable$resi3.side,
_side = _theme$dataTable$resi4 === void 0 ? 'end' : _theme$dataTable$resi4,
size = _theme$dataTable$resi3.size;
hoverBorder = {
color: _color,
side: _side,
size: size
};
}
var onKeyDown = useCallback(function (event) {
event.preventDefault();
if (!ref.current) return;
if (thRef.current) {
var element = thRef.current;
var currentWidth = element.getBoundingClientRect().width;
// Used STEP here to align with the value set in onMouseMove
var delta = event.key === 'ArrowLeft' ? -STEP : STEP;
onResize(property, currentWidth + delta);
setWidth(currentWidth + delta);
}
}, [onResize, property]);
var onDecrease = useCallback(function () {
if (thRef.current) {
var element = thRef.current;
var rect = element.getBoundingClientRect();
var currentWidth = rect.width;
var nextWidth = Math.max(STEP, currentWidth - STEP);
setWidth(nextWidth);
onResize(property, nextWidth);
}
}, [onResize, property]);
var onIncrease = useCallback(function () {
if (thRef.current) {
var element = thRef.current;
var rect = element.getBoundingClientRect();
var currentWidth = rect.width;
var nextWidth = Math.max(STEP, currentWidth + STEP);
setWidth(nextWidth);
onResize(property, nextWidth);
}
}, [onResize, property]);
var _useState4 = useState(false),
hover = _useState4[0],
setHover = _useState4[1];
var ariaLabel = format({
id: 'dataTable.resizerAria',
values: {
headerText: headerText
},
messages: messages
});
return /*#__PURE__*/React.createElement(Keyboard, {
onLeft: onKeyDown,
onRight: onKeyDown
}, /*#__PURE__*/React.createElement(StyledResizer, {
"aria-label": width ? ariaLabel + " " + Math.trunc(width) + " pixels" : ariaLabel,
onMouseEnter: function onMouseEnter() {
return setHover(true);
},
onMouseLeave: function onMouseLeave() {
return setHover(false);
},
ref: ref,
role: "separator",
"aria-valuenow": width,
"aria-valuetext": width ? ariaLabel + " " + Math.trunc(width) + " pixels" : ariaLabel,
"aria-controls": headerId,
"aria-orientation": "vertical",
onMouseDown: onResizeStart,
onMouseMove: start !== undefined ? onResizeMove : undefined,
onMouseUp: start !== undefined ? onResizeEnd : undefined,
onTouchStart: onResizeStart,
onTouchMove: start !== undefined ? onResizeMove : undefined,
onTouchEnd: start !== undefined ? onResizeEnd : undefined,
dropContent: /*#__PURE__*/React.createElement(Box, {
direction: "row",
pad: "xsmall"
}, /*#__PURE__*/React.createElement(Button, {
"aria-label": format({
id: 'dataTable.decrease',
values: {
headerText: headerText
},
messages: messages
}),
icon: /*#__PURE__*/React.createElement(ResizeDecreaseIcon, null),
onClick: onDecrease,
autoFocus: true
}), /*#__PURE__*/React.createElement(Button, {
"aria-label": format({
id: 'dataTable.increase',
values: {
headerText: headerText
},
messages: messages
}),
icon: /*#__PURE__*/React.createElement(ResizeIncreaseIcon, null),
onClick: onIncrease
})),
dropAlign: {
top: 'bottom'
},
side: (_border = border) == null ? void 0 : _border.side
}, /*#__PURE__*/React.createElement(Box, {
border: hover || active ? hoverBorder : border,
height: "100%",
alignSelf: "center"
})));
};
Resizer.displayName = 'Resizer';
export { Resizer };