UNPKG

@elastic/eui

Version:

Elastic UI Component Library

481 lines (477 loc) 21.5 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.useContainerCallbacks = exports.sizesOnly = exports.pxToPercent = exports.isTouchEvent = exports.getPosition = exports.getPanelMinSize = void 0; var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof")); var _react = require("react"); var _common = require("../common"); 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) { (0, _defineProperty2.default)(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; } /* * 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. */ var isTouchEvent = exports.isTouchEvent = function isTouchEvent(event) { return (0, _typeof2.default)(event) === 'object' && 'targetTouches' in event; }; var pxToPercent = exports.pxToPercent = function pxToPercent(proportion, whole) { if (whole < 1 || proportion < 0) return 0; return proportion / whole * 100; }; var sizesOnly = exports.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; }; var getPanelMinSize = exports.getPanelMinSize = function getPanelMinSize(panelMinSize, containerSize) { var paddingMin = _getPanelMinSize(panelMinSize[1], containerSize); var configMin = _getPanelMinSize(panelMinSize[0], containerSize); return Math.max(configMin, paddingMin); }; var getPosition = exports.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; }; var useContainerCallbacks = exports.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), {}, (0, _defineProperty2.default)({}, 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), {}, (0, _defineProperty2.default)({}, 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), {}, (0, _defineProperty2.default)((0, _defineProperty2.default)({}, _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), {}, (0, _defineProperty2.default)((0, _defineProperty2.default)({}, _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), {}, (0, _defineProperty2.default)({}, 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: (0, _common.assertNever)(action); return state; } } var _useReducer = (0, _react.useReducer)(reducer, initialState, init), _useReducer2 = (0, _slicedToArray2.default)(_useReducer, 2), reducerState = _useReducer2[0], dispatch = _useReducer2[1]; var actions = (0, _react.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]; };