@elastic/eui
Version:
Elastic UI Component Library
483 lines (479 loc) • 23.1 kB
JavaScript
function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { useMemo, useReducer } from 'react';
import { assertNever } from '../common';
export var isTouchEvent = function isTouchEvent(event) {
return _typeof(event) === 'object' && 'targetTouches' in event;
};
export var pxToPercent = function pxToPercent(proportion, whole) {
if (whole < 1 || proportion < 0) return 0;
return proportion / whole * 100;
};
export var sizesOnly = function sizesOnly(panelObject) {
return Object.values(panelObject).reduce(function (out, panel) {
out[panel.id] = panel.size;
return out;
}, {});
};
var _getPanelMinSize = function _getPanelMinSize(panelMinSize, containerSize) {
var panelMinSizePercent = 0;
var panelMinSizeInt = parseFloat(panelMinSize);
if (panelMinSize.indexOf('px') > -1) {
panelMinSizePercent = pxToPercent(panelMinSizeInt, containerSize);
} else if (panelMinSize.indexOf('%') > -1) {
panelMinSizePercent = pxToPercent(containerSize * (panelMinSizeInt / 100), containerSize);
}
return panelMinSizePercent;
};
export var getPanelMinSize = function getPanelMinSize(panelMinSize, containerSize) {
var paddingMin = _getPanelMinSize(panelMinSize[1], containerSize);
var configMin = _getPanelMinSize(panelMinSize[0], containerSize);
return Math.max(configMin, paddingMin);
};
export var getPosition = function getPosition(event, isHorizontal) {
var direction = isHorizontal ? 'clientX' : 'clientY';
return isTouchEvent(event) ? event.targetTouches[0][direction] : event[direction];
};
var getSiblingPanel = function getSiblingPanel(element, adjacency) {
if (!element) return null;
var method = adjacency === 'prev' ? 'previousElementSibling' : 'nextElementSibling';
var sibling = element[method];
while (sibling) {
if (sibling.matches('.euiResizablePanel:not([data-collapsed])')) {
return sibling;
}
sibling = sibling[method];
}
};
// lazy initialization to prevent rerender on initial interaction
var init = function init(state) {
return state;
};
export var useContainerCallbacks = function useContainerCallbacks(_ref) {
var initialState = _ref.initialState,
containerRef = _ref.containerRef,
onPanelWidthChange = _ref.onPanelWidthChange;
function reducer(state, action) {
var getContainerSize = function getContainerSize(isHorizontal) {
return isHorizontal ? containerRef.current.getBoundingClientRect().width : containerRef.current.getBoundingClientRect().height;
};
var runSideEffect = function runSideEffect(panels) {
if (onPanelWidthChange) {
onPanelWidthChange(sizesOnly(panels));
}
};
var withSideEffect = function withSideEffect(newState) {
runSideEffect(newState.panels);
return newState;
};
switch (action.type) {
case 'EUI_RESIZABLE_CONTAINER_INIT':
{
var isHorizontal = action.payload.isHorizontal;
return _objectSpread(_objectSpread({}, state), {}, {
isHorizontal: isHorizontal,
containerSize: getContainerSize(isHorizontal)
});
}
case 'EUI_RESIZABLE_PANEL_REGISTER':
{
var panel = action.payload.panel;
return _objectSpread(_objectSpread({}, state), {}, {
panels: _objectSpread(_objectSpread({}, state.panels), {}, _defineProperty({}, panel.id, panel))
});
}
case 'EUI_RESIZABLE_PANEL_DEREGISTER':
{
var panelId = action.payload.panelId;
return _objectSpread(_objectSpread({}, state), {}, {
panels: Object.values(state.panels).reduce(function (out, panel) {
if (panel.id !== panelId) {
out[panel.id] = panel;
}
return out;
}, {})
});
}
case 'EUI_RESIZABLE_BUTTON_REGISTER':
{
var resizer = action.payload.resizer;
return _objectSpread(_objectSpread({}, state), {}, {
resizers: _objectSpread(_objectSpread({}, state.resizers), {}, _defineProperty({}, resizer.id, resizer))
});
}
case 'EUI_RESIZABLE_BUTTON_DEREGISTER':
{
var resizerId = action.payload.resizerId;
return _objectSpread(_objectSpread({}, state), {}, {
resizers: Object.values(state.resizers).reduce(function (out, panel) {
if (panel.id !== resizerId) {
out[panel.id] = panel;
}
return out;
}, {})
});
}
case 'EUI_RESIZABLE_DRAG_START':
{
var _action$payload = action.payload,
position = _action$payload.position,
prevPanelId = _action$payload.prevPanelId,
nextPanelId = _action$payload.nextPanelId;
return _objectSpread(_objectSpread({}, state), {}, {
isDragging: true,
currentResizerPos: position,
prevPanelId: prevPanelId,
nextPanelId: nextPanelId
});
}
case 'EUI_RESIZABLE_DRAG_MOVE':
{
if (!state.isDragging) {
return state;
}
var _action$payload2 = action.payload,
_position = _action$payload2.position,
_prevPanelId = _action$payload2.prevPanelId,
_nextPanelId = _action$payload2.nextPanelId;
var prevPanel = state.panels[_prevPanelId];
var nextPanel = state.panels[_nextPanelId];
var delta = _position - state.currentResizerPos;
var prevPanelMin = getPanelMinSize(prevPanel.minSize, state.containerSize);
var nextPanelMin = getPanelMinSize(nextPanel.minSize, state.containerSize);
var prevPanelSize = pxToPercent(prevPanel.getSizePx() + delta, state.containerSize);
var nextPanelSize = pxToPercent(nextPanel.getSizePx() - delta, state.containerSize);
if (prevPanelSize >= prevPanelMin && nextPanelSize >= nextPanelMin) {
return withSideEffect(_objectSpread(_objectSpread({}, state), {}, {
currentResizerPos: _position,
panels: _objectSpread(_objectSpread({}, state.panels), {}, _defineProperty(_defineProperty({}, _prevPanelId, _objectSpread(_objectSpread({}, state.panels[_prevPanelId]), {}, {
size: prevPanelSize
})), _nextPanelId, _objectSpread(_objectSpread({}, state.panels[_nextPanelId]), {}, {
size: nextPanelSize
})))
}));
}
return state;
}
case 'EUI_RESIZABLE_KEY_MOVE':
{
var _action$payload3 = action.payload,
_prevPanelId2 = _action$payload3.prevPanelId,
_nextPanelId2 = _action$payload3.nextPanelId,
direction = _action$payload3.direction;
var _prevPanel = state.panels[_prevPanelId2];
var _nextPanel = state.panels[_nextPanelId2];
var _prevPanelMin = getPanelMinSize(_prevPanel.minSize, state.containerSize);
var _nextPanelMin = getPanelMinSize(_nextPanel.minSize, state.containerSize);
var _prevPanelSize = pxToPercent(_prevPanel.getSizePx() - (direction === 'backward' ? 10 : -10), state.containerSize);
var _nextPanelSize = pxToPercent(_nextPanel.getSizePx() - (direction === 'forward' ? 10 : -10), state.containerSize);
if (_prevPanelSize >= _prevPanelMin && _nextPanelSize >= _nextPanelMin) {
return withSideEffect(_objectSpread(_objectSpread({}, state), {}, {
isDragging: false,
panels: _objectSpread(_objectSpread({}, state.panels), {}, _defineProperty(_defineProperty({}, _prevPanelId2, _objectSpread(_objectSpread({}, state.panels[_prevPanelId2]), {}, {
size: _prevPanelSize
})), _nextPanelId2, _objectSpread(_objectSpread({}, state.panels[_nextPanelId2]), {}, {
size: _nextPanelSize
})))
}));
}
return state;
}
case 'EUI_RESIZABLE_TOGGLE':
{
var _action$payload4 = action.payload,
options = _action$payload4.options,
currentPanelId = _action$payload4.panelId;
var currentPanel = state.panels[currentPanelId];
var shouldCollapse = !currentPanel.isCollapsed;
var panelElement = document.getElementById(currentPanelId);
var prevResizer = panelElement.previousElementSibling;
var _prevPanel2 = prevResizer ? prevResizer.previousElementSibling : null;
var nextResizer = panelElement.nextElementSibling;
var _nextPanel2 = nextResizer ? nextResizer.nextElementSibling : null;
var resizersToDisable = {};
if (prevResizer && _prevPanel2) {
resizersToDisable[prevResizer.id] = state.panels[_prevPanel2.id].isCollapsed ? true : shouldCollapse;
}
if (nextResizer && _nextPanel2) {
resizersToDisable[nextResizer.id] = state.panels[_nextPanel2.id].isCollapsed ? true : shouldCollapse;
}
var otherPanels = {};
if (_prevPanel2 && !state.panels[_prevPanel2.id].isCollapsed && options.direction === 'right') {
otherPanels[_prevPanel2.id] = state.panels[_prevPanel2.id];
}
if (_nextPanel2 && !state.panels[_nextPanel2.id].isCollapsed && options.direction === 'left') {
otherPanels[_nextPanel2.id] = state.panels[_nextPanel2.id];
}
var siblings = Object.keys(otherPanels).length;
// A toggling sequence has occurred where an immediate sibling panel
// has not been found. We need to move more broadly through the DOM
// to find the next most suitable panel or space affordance.
// Can only occur when multiple immediate sibling panels are collapsed.
if (!siblings) {
var maybePrevPanel = getSiblingPanel(panelElement, 'prev');
var maybeNextPanel = getSiblingPanel(panelElement, 'next');
var validPrevPanel = maybePrevPanel ? state.panels[maybePrevPanel.id] : null;
var validNextPanel = maybeNextPanel ? state.panels[maybeNextPanel.id] : null;
// Intentional, preferential redistribution order
if (validPrevPanel && options.direction === 'right') {
otherPanels[validPrevPanel.id] = validPrevPanel;
} else if (validNextPanel && options.direction === 'left') {
otherPanels[validNextPanel.id] = validNextPanel;
} else {
if (validPrevPanel) otherPanels[validPrevPanel.id] = validPrevPanel;
if (validNextPanel) otherPanels[validNextPanel.id] = validNextPanel;
}
siblings = Object.keys(otherPanels).length;
}
var newPanelSize = shouldCollapse ? pxToPercent(!currentPanel.mode ? 0 : 24,
// size of the default toggle button
state.containerSize) : currentPanel.prevSize;
var _delta = shouldCollapse ? (currentPanel.size - newPanelSize) / siblings : (newPanelSize - currentPanel.size) / siblings * -1;
var collapsedPanelsSize = Object.values(state.panels).reduce(function (sum, panel) {
if (panel.id !== currentPanelId && panel.isCollapsed) {
sum += panel.size;
}
return sum;
}, 0);
// A toggling sequence has occurred where a to-be-opened panel will
// become the only open panel. Rather than reopen to its previous
// size, give it the full width, less size occupied by collapsed panels.
// Can only occur with external toggling.
if (!shouldCollapse && !siblings) {
newPanelSize = 100 - collapsedPanelsSize;
}
var updatedPanels = {};
if (_delta < 0 && Object.values(otherPanels).some(function (panel) {
return panel.size + _delta < getPanelMinSize(panel.minSize, state.containerSize);
})) {
// A toggling sequence has occurred where a to-be-opened panel is
// requesting more space than its logical sibling panel can afford.
// Rather than choose another single panel to sacrifice space,
// or try to pull proportionally from all availble panels
// (neither of which is guaranteed to prevent negative resulting widths),
// or attempt something even more complex,
// we redistribute _all_ space evenly to non-collapsed panels
// as something of a reset.
// This situation can only occur when (n-1) panels are collapsed at once
// and the most recently collapsed panel gains significant width
// during the previously occurring collapse.
// That is (largely), external toggling where the default logic has
// been negated by the lack of panel mode distinction.
otherPanels = Object.values(state.panels).reduce(function (out, panel) {
if (panel.id !== currentPanelId && !panel.isCollapsed) {
out[panel.id] = _objectSpread({}, panel);
}
return out;
}, {});
newPanelSize = (100 - collapsedPanelsSize) / (Object.keys(otherPanels).length + 1);
updatedPanels = Object.values(otherPanels).reduce(function (out, panel) {
out[panel.id] = _objectSpread(_objectSpread({}, panel), {}, {
size: newPanelSize
});
return out;
}, {});
} else {
// A toggling sequence has occurred that is standard and predictable
updatedPanels = Object.values(otherPanels).reduce(function (out, panel) {
out[panel.id] = _objectSpread(_objectSpread({}, panel), {}, {
size: panel.size + _delta
});
return out;
}, {});
}
return withSideEffect(_objectSpread(_objectSpread({}, state), {}, {
panels: _objectSpread(_objectSpread(_objectSpread({}, state.panels), updatedPanels), {}, _defineProperty({}, currentPanelId, _objectSpread(_objectSpread({}, state.panels[currentPanelId]), {}, {
size: newPanelSize,
isCollapsed: shouldCollapse,
prevSize: shouldCollapse ? currentPanel.size : newPanelSize
}))),
resizers: Object.values(state.resizers).reduce(function (out, resizer) {
var _resizersToDisable$re;
out[resizer.id] = _objectSpread(_objectSpread({}, resizer), {}, {
isFocused: false,
isDisabled: (_resizersToDisable$re = resizersToDisable[resizer.id]) !== null && _resizersToDisable$re !== void 0 ? _resizersToDisable$re : resizer.isDisabled
});
return out;
}, {})
}));
}
case 'EUI_RESIZABLE_BUTTON_FOCUS':
{
var _resizerId = action.payload.resizerId;
return _objectSpread(_objectSpread({}, state), {}, {
resizers: Object.values(state.resizers).reduce(function (out, resizer) {
out[resizer.id] = _objectSpread(_objectSpread({}, resizer), {}, {
isFocused: resizer.id === _resizerId
});
return out;
}, {})
});
}
case 'EUI_RESIZABLE_BUTTON_BLUR':
{
return _objectSpread(_objectSpread({}, state), {}, {
resizers: Object.values(state.resizers).reduce(function (out, resizer) {
out[resizer.id] = _objectSpread(_objectSpread({}, resizer), {}, {
isFocused: false
});
return out;
}, {})
});
}
case 'EUI_RESIZABLE_RESET':
{
return _objectSpread(_objectSpread({}, initialState), {}, {
panels: state.panels,
resizers: state.resizers,
containerSize: state.containerSize
});
}
case 'EUI_RESIZABLE_ONCHANGE':
{
onPanelWidthChange(sizesOnly(state.panels));
return state;
}
// TODO: Implement more generic version of
// 'EUI_RESIZABLE_DRAG_MOVE' to expose to consumers
case 'EUI_RESIZABLE_RESIZE':
{
return state;
}
default:
assertNever(action);
return state;
}
}
var _useReducer = useReducer(reducer, initialState, init),
_useReducer2 = _slicedToArray(_useReducer, 2),
reducerState = _useReducer2[0],
dispatch = _useReducer2[1];
var actions = useMemo(function () {
return {
reset: function reset() {
return dispatch({
type: 'EUI_RESIZABLE_RESET'
});
},
initContainer: function initContainer(isHorizontal) {
return dispatch({
type: 'EUI_RESIZABLE_CONTAINER_INIT',
payload: {
isHorizontal: isHorizontal
}
});
},
registerPanel: function registerPanel(panel) {
return dispatch({
type: 'EUI_RESIZABLE_PANEL_REGISTER',
payload: {
panel: panel
}
});
},
deregisterPanel: function deregisterPanel(panelId) {
return dispatch({
type: 'EUI_RESIZABLE_PANEL_DEREGISTER',
payload: {
panelId: panelId
}
});
},
registerResizer: function registerResizer(resizer) {
return dispatch({
type: 'EUI_RESIZABLE_BUTTON_REGISTER',
payload: {
resizer: resizer
}
});
},
deregisterResizer: function deregisterResizer(resizerId) {
return dispatch({
type: 'EUI_RESIZABLE_BUTTON_DEREGISTER',
payload: {
resizerId: resizerId
}
});
},
dragStart: function dragStart(_ref2) {
var prevPanelId = _ref2.prevPanelId,
nextPanelId = _ref2.nextPanelId,
position = _ref2.position;
return dispatch({
type: 'EUI_RESIZABLE_DRAG_START',
payload: {
position: position,
prevPanelId: prevPanelId,
nextPanelId: nextPanelId
}
});
},
dragMove: function dragMove(_ref3) {
var prevPanelId = _ref3.prevPanelId,
nextPanelId = _ref3.nextPanelId,
position = _ref3.position;
return dispatch({
type: 'EUI_RESIZABLE_DRAG_MOVE',
payload: {
position: position,
prevPanelId: prevPanelId,
nextPanelId: nextPanelId
}
});
},
keyMove: function keyMove(_ref4) {
var prevPanelId = _ref4.prevPanelId,
nextPanelId = _ref4.nextPanelId,
direction = _ref4.direction;
return dispatch({
type: 'EUI_RESIZABLE_KEY_MOVE',
payload: {
prevPanelId: prevPanelId,
nextPanelId: nextPanelId,
direction: direction
}
});
},
togglePanel: function togglePanel(panelId, options) {
return dispatch({
type: 'EUI_RESIZABLE_TOGGLE',
payload: {
panelId: panelId,
options: options
}
});
},
resizerFocus: function resizerFocus(resizerId) {
return dispatch({
type: 'EUI_RESIZABLE_BUTTON_FOCUS',
payload: {
resizerId: resizerId
}
});
},
resizerBlur: function resizerBlur() {
return dispatch({
type: 'EUI_RESIZABLE_BUTTON_BLUR'
});
}
};
}, []);
return [actions, reducerState];
};