@linzjs/step-ag-grid
Version:
[](https://github.com/semantic-release/semantic-release) > Reusable [ag-grid](https://www.ag-grid.com/) component for LINZ / Toitū te whenua.
1,413 lines (1,329 loc) • 300 kB
JavaScript
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
import { LuiMiniSpinner, LuiIcon, useShowLUIMessage, LuiStatusSpinner, LuiButtonGroup, LuiButton, LuiCheckboxInput } from '@linzjs/lui';
import { ModuleRegistry, AllCommunityModule } from 'ag-grid-community';
import { AgGridReact } from 'ag-grid-react';
import { negate, isEmpty, findIndex, defer, debounce as debounce$1, compact, xorBy, last, difference, delay, omit, sortBy, partition, pick, groupBy, fromPairs, toPairs, isEqual, pull, filter, sumBy, remove, flatten, castArray } from 'lodash-es';
import React, { useRef, useLayoutEffect, useEffect, createContext, useContext, useState, useMemo, memo, forwardRef, useCallback, useReducer, cloneElement, useImperativeHandle, Fragment as Fragment$1, useId } from 'react';
import { unstable_batchedUpdates, flushSync, createPortal } from 'react-dom';
import { createRoot } from 'react-dom/client';
/**
* If loading is true this returns a loading spinner, otherwise it returns its children.
*/
const ComponentLoadingWrapper = (props) => {
return props.loading ? (jsx(LuiMiniSpinner, { size: 22, divProps: { role: 'status', ['aria-label']: 'Loading', style: { padding: 16 } } })) : (jsxs("div", { style: { pointerEvents: props.saving ? 'none' : 'inherit' }, className: props.className, children: [props.saving && jsx("div", { className: 'ComponentLoadingWrapper-saveOverlay' }), props.children] }));
};
function r(e){var t,f,n="";if("string"==typeof e||"number"==typeof e)n+=e;else if("object"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=" "),n+=f);}else for(f in e)e[f]&&(n&&(n+=" "),n+=f);return n}function clsx(){for(var e,t,f=0,n="",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=" "),n+=t);return n}
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
function getDefaultExportFromCjs (x) {
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
}
/**
* lodash (Custom Build) <https://lodash.com/>
* Build: `lodash modularize exports="npm" -o ./`
* Copyright jQuery Foundation and other contributors <https://jquery.org/>
* Released under MIT license <https://lodash.com/license>
* Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
* Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
*/
var lodash_debounce;
var hasRequiredLodash_debounce;
function requireLodash_debounce () {
if (hasRequiredLodash_debounce) return lodash_debounce;
hasRequiredLodash_debounce = 1;
/** Used as the `TypeError` message for "Functions" methods. */
var FUNC_ERROR_TEXT = 'Expected a function';
/** Used as references for various `Number` constants. */
var NAN = 0 / 0;
/** `Object#toString` result references. */
var symbolTag = '[object Symbol]';
/** Used to match leading and trailing whitespace. */
var reTrim = /^\s+|\s+$/g;
/** Used to detect bad signed hexadecimal string values. */
var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
/** Used to detect binary string values. */
var reIsBinary = /^0b[01]+$/i;
/** Used to detect octal string values. */
var reIsOctal = /^0o[0-7]+$/i;
/** Built-in method references without a dependency on `root`. */
var freeParseInt = parseInt;
/** Detect free variable `global` from Node.js. */
var freeGlobal = typeof commonjsGlobal == 'object' && commonjsGlobal && commonjsGlobal.Object === Object && commonjsGlobal;
/** Detect free variable `self`. */
var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
/** Used as a reference to the global object. */
var root = freeGlobal || freeSelf || Function('return this')();
/** Used for built-in method references. */
var objectProto = Object.prototype;
/**
* Used to resolve the
* [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
* of values.
*/
var objectToString = objectProto.toString;
/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeMax = Math.max,
nativeMin = Math.min;
/**
* Gets the timestamp of the number of milliseconds that have elapsed since
* the Unix epoch (1 January 1970 00:00:00 UTC).
*
* @static
* @memberOf _
* @since 2.4.0
* @category Date
* @returns {number} Returns the timestamp.
* @example
*
* _.defer(function(stamp) {
* console.log(_.now() - stamp);
* }, _.now());
* // => Logs the number of milliseconds it took for the deferred invocation.
*/
var now = function() {
return root.Date.now();
};
/**
* Creates a debounced function that delays invoking `func` until after `wait`
* milliseconds have elapsed since the last time the debounced function was
* invoked. The debounced function comes with a `cancel` method to cancel
* delayed `func` invocations and a `flush` method to immediately invoke them.
* Provide `options` to indicate whether `func` should be invoked on the
* leading and/or trailing edge of the `wait` timeout. The `func` is invoked
* with the last arguments provided to the debounced function. Subsequent
* calls to the debounced function return the result of the last `func`
* invocation.
*
* **Note:** If `leading` and `trailing` options are `true`, `func` is
* invoked on the trailing edge of the timeout only if the debounced function
* is invoked more than once during the `wait` timeout.
*
* If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
* until to the next tick, similar to `setTimeout` with a timeout of `0`.
*
* See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
* for details over the differences between `_.debounce` and `_.throttle`.
*
* @static
* @memberOf _
* @since 0.1.0
* @category Function
* @param {Function} func The function to debounce.
* @param {number} [wait=0] The number of milliseconds to delay.
* @param {Object} [options={}] The options object.
* @param {boolean} [options.leading=false]
* Specify invoking on the leading edge of the timeout.
* @param {number} [options.maxWait]
* The maximum time `func` is allowed to be delayed before it's invoked.
* @param {boolean} [options.trailing=true]
* Specify invoking on the trailing edge of the timeout.
* @returns {Function} Returns the new debounced function.
* @example
*
* // Avoid costly calculations while the window size is in flux.
* jQuery(window).on('resize', _.debounce(calculateLayout, 150));
*
* // Invoke `sendMail` when clicked, debouncing subsequent calls.
* jQuery(element).on('click', _.debounce(sendMail, 300, {
* 'leading': true,
* 'trailing': false
* }));
*
* // Ensure `batchLog` is invoked once after 1 second of debounced calls.
* var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
* var source = new EventSource('/stream');
* jQuery(source).on('message', debounced);
*
* // Cancel the trailing debounced invocation.
* jQuery(window).on('popstate', debounced.cancel);
*/
function debounce(func, wait, options) {
var lastArgs,
lastThis,
maxWait,
result,
timerId,
lastCallTime,
lastInvokeTime = 0,
leading = false,
maxing = false,
trailing = true;
if (typeof func != 'function') {
throw new TypeError(FUNC_ERROR_TEXT);
}
wait = toNumber(wait) || 0;
if (isObject(options)) {
leading = !!options.leading;
maxing = 'maxWait' in options;
maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;
trailing = 'trailing' in options ? !!options.trailing : trailing;
}
function invokeFunc(time) {
var args = lastArgs,
thisArg = lastThis;
lastArgs = lastThis = undefined;
lastInvokeTime = time;
result = func.apply(thisArg, args);
return result;
}
function leadingEdge(time) {
// Reset any `maxWait` timer.
lastInvokeTime = time;
// Start the timer for the trailing edge.
timerId = setTimeout(timerExpired, wait);
// Invoke the leading edge.
return leading ? invokeFunc(time) : result;
}
function remainingWait(time) {
var timeSinceLastCall = time - lastCallTime,
timeSinceLastInvoke = time - lastInvokeTime,
result = wait - timeSinceLastCall;
return maxing ? nativeMin(result, maxWait - timeSinceLastInvoke) : result;
}
function shouldInvoke(time) {
var timeSinceLastCall = time - lastCallTime,
timeSinceLastInvoke = time - lastInvokeTime;
// Either this is the first call, activity has stopped and we're at the
// trailing edge, the system time has gone backwards and we're treating
// it as the trailing edge, or we've hit the `maxWait` limit.
return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||
(timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));
}
function timerExpired() {
var time = now();
if (shouldInvoke(time)) {
return trailingEdge(time);
}
// Restart the timer.
timerId = setTimeout(timerExpired, remainingWait(time));
}
function trailingEdge(time) {
timerId = undefined;
// Only invoke if we have `lastArgs` which means `func` has been
// debounced at least once.
if (trailing && lastArgs) {
return invokeFunc(time);
}
lastArgs = lastThis = undefined;
return result;
}
function cancel() {
if (timerId !== undefined) {
clearTimeout(timerId);
}
lastInvokeTime = 0;
lastArgs = lastCallTime = lastThis = timerId = undefined;
}
function flush() {
return timerId === undefined ? result : trailingEdge(now());
}
function debounced() {
var time = now(),
isInvoking = shouldInvoke(time);
lastArgs = arguments;
lastThis = this;
lastCallTime = time;
if (isInvoking) {
if (timerId === undefined) {
return leadingEdge(lastCallTime);
}
if (maxing) {
// Handle invocations in a tight loop.
timerId = setTimeout(timerExpired, wait);
return invokeFunc(lastCallTime);
}
}
if (timerId === undefined) {
timerId = setTimeout(timerExpired, wait);
}
return result;
}
debounced.cancel = cancel;
debounced.flush = flush;
return debounced;
}
/**
* Checks if `value` is the
* [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
* of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
*
* @static
* @memberOf _
* @since 0.1.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is an object, else `false`.
* @example
*
* _.isObject({});
* // => true
*
* _.isObject([1, 2, 3]);
* // => true
*
* _.isObject(_.noop);
* // => true
*
* _.isObject(null);
* // => false
*/
function isObject(value) {
var type = typeof value;
return !!value && (type == 'object' || type == 'function');
}
/**
* Checks if `value` is object-like. A value is object-like if it's not `null`
* and has a `typeof` result of "object".
*
* @static
* @memberOf _
* @since 4.0.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is object-like, else `false`.
* @example
*
* _.isObjectLike({});
* // => true
*
* _.isObjectLike([1, 2, 3]);
* // => true
*
* _.isObjectLike(_.noop);
* // => false
*
* _.isObjectLike(null);
* // => false
*/
function isObjectLike(value) {
return !!value && typeof value == 'object';
}
/**
* Checks if `value` is classified as a `Symbol` primitive or object.
*
* @static
* @memberOf _
* @since 4.0.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
* @example
*
* _.isSymbol(Symbol.iterator);
* // => true
*
* _.isSymbol('abc');
* // => false
*/
function isSymbol(value) {
return typeof value == 'symbol' ||
(isObjectLike(value) && objectToString.call(value) == symbolTag);
}
/**
* Converts `value` to a number.
*
* @static
* @memberOf _
* @since 4.0.0
* @category Lang
* @param {*} value The value to process.
* @returns {number} Returns the number.
* @example
*
* _.toNumber(3.2);
* // => 3.2
*
* _.toNumber(Number.MIN_VALUE);
* // => 5e-324
*
* _.toNumber(Infinity);
* // => Infinity
*
* _.toNumber('3.2');
* // => 3.2
*/
function toNumber(value) {
if (typeof value == 'number') {
return value;
}
if (isSymbol(value)) {
return NAN;
}
if (isObject(value)) {
var other = typeof value.valueOf == 'function' ? value.valueOf() : value;
value = isObject(other) ? (other + '') : other;
}
if (typeof value != 'string') {
return value === 0 ? value : +value;
}
value = value.replace(reTrim, '');
var isBinary = reIsBinary.test(value);
return (isBinary || reIsOctal.test(value))
? freeParseInt(value.slice(2), isBinary ? 2 : 8)
: (reIsBadHex.test(value) ? NAN : +value);
}
lodash_debounce = debounce;
return lodash_debounce;
}
requireLodash_debounce();
var useIsomorphicLayoutEffect$1 = typeof window !== "undefined" ? useLayoutEffect : useEffect;
function useInterval(callback, delay) {
const savedCallback = useRef(callback);
useIsomorphicLayoutEffect$1(() => {
savedCallback.current = callback;
}, [callback]);
useEffect(() => {
if (delay === null) {
return;
}
const id = setInterval(() => {
savedCallback.current();
}, delay);
return () => {
clearInterval(id);
};
}, [delay]);
}
const NoContext = () => {
console.error('Missing GridContextProvider');
return null;
};
const GridContext = createContext({
gridReady: false,
gridRenderState: () => null,
getCellValue: NoContext,
getColDef: NoContext,
getColumns: NoContext,
getColumnIds: NoContext,
invisibleColumnIds: undefined,
setInvisibleColumnIds: NoContext,
prePopupOps: NoContext,
externallySelectedItemsAreInSync: false,
setApis: NoContext,
setQuickFilter: NoContext,
selectRowsById: NoContext,
getSelectedRows: NoContext,
getFilteredSelectedRows: NoContext,
getSelectedRowIds: NoContext,
getFilteredSelectedRowIds: NoContext,
selectRowsDiff: NoContext,
selectRowsByIdWithFlash: NoContext,
selectRowsWithFlashDiff: NoContext,
flashRows: NoContext,
flashRowsDiff: NoContext,
focusByRowById: NoContext,
ensureRowVisible: NoContext,
ensureSelectedRowIsVisible: NoContext,
getFirstRowId: NoContext,
autoSizeColumns: NoContext,
sizeColumnsToFit: NoContext,
editingCells: NoContext,
startCellEditing: NoContext,
resetFocusedCellAfterCellEditing: NoContext,
updatingCells: NoContext,
redrawRows: NoContext,
setExternallySelectedItemsAreInSync: NoContext,
waitForExternallySelectedItemsToBeInSync: NoContext,
addExternalFilter: NoContext,
removeExternalFilter: NoContext,
isExternalFilterPresent: NoContext,
doesExternalFilterPass: NoContext,
downloadCsv: NoContext,
onBulkEditingComplete: NoContext,
setOnBulkEditingComplete: NoContext,
showNoRowsOverlay: NoContext,
});
const useGridContext = () => useContext(GridContext);
const GridUpdatingContext = createContext({
anyUpdating: () => {
console.error('Missing GridUpdatingContext');
return false;
},
checkUpdating: () => {
console.error('Missing GridUpdatingContext');
return false;
},
updatingCols: () => {
console.error('Missing GridUpdatingContext');
return [];
},
// eslint-disable-next-line @typescript-eslint/require-await
modifyUpdating: async () => {
console.error('Missing GridUpdatingContext');
},
updatedDep: 0,
});
var lib = {};
var hasRequiredLib;
function requireLib () {
if (hasRequiredLib) return lib;
hasRequiredLib = 1;
Object.defineProperty(lib, "__esModule", { value: true });
function natsort(options) {
if (options === void 0) { options = {}; }
var ore = /^0/;
var sre = /\s+/g;
var tre = /^\s+|\s+$/g;
// unicode
var ure = /[^\x00-\x80]/;
// hex
var hre = /^0x[0-9a-f]+$/i;
// numeric
var nre = /(0x[\da-fA-F]+|(^[\+\-]?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?(?=\D|\s|$))|\d+)/g;
// datetime
var dre = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/; // tslint:disable-line
var toLowerCase = String.prototype.toLocaleLowerCase || String.prototype.toLowerCase;
var GREATER = options.desc ? -1 : 1;
var SMALLER = -GREATER;
var normalize = options.insensitive
? function (s) { return toLowerCase.call("" + s).replace(tre, ''); }
: function (s) { return ("" + s).replace(tre, ''); };
function tokenize(s) {
return s.replace(nre, '\0$1\0')
.replace(/\0$/, '')
.replace(/^\0/, '')
.split('\0');
}
function parse(s, l) {
// normalize spaces; find floats not starting with '0',
// string or 0 if not defined (Clint Priest)
return (!s.match(ore) || l === 1)
&& parseFloat(s)
|| s.replace(sre, ' ').replace(tre, '')
|| 0;
}
return function (a, b) {
// trim pre-post whitespace
var aa = normalize(a);
var bb = normalize(b);
// return immediately if at least one of the values is empty.
// empty string < any others
if (!aa && !bb) {
return 0;
}
if (!aa && bb) {
return SMALLER;
}
if (aa && !bb) {
return GREATER;
}
// tokenize: split numeric strings and default strings
var aArr = tokenize(aa);
var bArr = tokenize(bb);
// hex or date detection
var aHex = aa.match(hre);
var bHex = bb.match(hre);
var av = (aHex && bHex) ? parseInt(aHex[0], 16) : (aArr.length !== 1 && Date.parse(aa));
var bv = (aHex && bHex)
? parseInt(bHex[0], 16)
: av && bb.match(dre) && Date.parse(bb) || null;
// try and sort Hex codes or Dates
if (bv) {
if (av === bv) {
return 0;
}
if (av < bv) {
return SMALLER;
}
if (av > bv) {
return GREATER;
}
}
var al = aArr.length;
var bl = bArr.length;
// handle numeric strings and default strings
for (var i = 0, l = Math.max(al, bl); i < l; i += 1) {
var af = parse(aArr[i] || '', al);
var bf = parse(bArr[i] || '', bl);
// handle numeric vs string comparison.
// numeric < string
if (isNaN(af) !== isNaN(bf)) {
return isNaN(af) ? GREATER : SMALLER;
}
// if unicode use locale comparison
if (ure.test(af + bf) && af.localeCompare) {
var comp = af.localeCompare(bf);
if (comp > 0) {
return GREATER;
}
if (comp < 0) {
return SMALLER;
}
if (i === l - 1) {
return 0;
}
}
if (af < bf) {
return SMALLER;
}
if (af > bf) {
return GREATER;
}
if ("" + af < "" + bf) {
return SMALLER;
}
if ("" + af > "" + bf) {
return GREATER;
}
}
return 0;
};
}
lib.default = natsort;
return lib;
}
var libExports = requireLib();
var natsort = /*@__PURE__*/getDefaultExportFromCjs(libExports);
const isNotEmpty = negate(isEmpty);
const wait = (timeoutMs) => new Promise((resolve) => {
setTimeout(resolve, timeoutMs);
});
// This regexp only works if you parseFloat first, it won't validate a float on its own
// It prevents scientific 1e10, or trailing decimal 1.2.3, or trailing garbage 1.2xyz
const isFloatRegExp = /^-?\d*\.?\d*$/;
const isFloat = (value) => {
try {
// Just checking it's not scientific notation or "NaN" here.
// Also parse float will parse up to the first invalid character,
// so we need to check there's no remaining invalids e.g. "1.2xyz" would parse as 1.2
return !Number.isNaN(parseFloat(value)) && isFloatRegExp.test(value);
}
catch {
return false;
}
};
const findParentWithClass = function (className, child) {
for (let node = child; node; node = node.parentNode) {
// When nodes are in portals they aren't type node anymore hence treating it as any here
if (node.classList && node.classList.contains(className)) {
return node;
}
}
return null;
};
const hasParentClass = function (className, child) {
for (let node = child; node; node = node.parentNode) {
// When nodes are in portals they aren't type node anymore hence treating it as any here
if (node.classList && node.classList.contains(className)) {
return true;
}
}
return false;
};
const stringByteLengthIsInvalid = (str, maxBytes) => new TextEncoder().encode(str).length > maxBytes;
const fnOrVar = (fn, param) => (typeof fn === 'function' ? fn(param) : fn);
const sanitiseFileName = (filename) => {
const valid = filename
.trim()
.replaceAll(/(\/|\\)+/g, '-')
.replaceAll(/\s+/g, '_')
.replaceAll(/[^\w\-āēīōūĀĒĪŌŪ.]/g, '');
const parts = valid.split('.');
const fileExt = parts.length > 1 ? parts.pop() : undefined;
// Arbitrary max filename length of 64 chars + extension
if (!fileExt)
return valid.slice().slice(0, 64);
return valid.slice(0, -fileExt.length - 1).slice(0, 64) + '.' + fileExt;
};
const naturalSortInsensitive = natsort({ insensitive: true });
const santitizeNaturalValue = (v) => {
if (v === '–' || v === '-' || v == null) {
return '';
}
return String(v);
};
const compareNaturalInsensitive = (a, b) => {
if ((a !== null && typeof a === 'object') || (b !== null && typeof b === 'object')) {
// We can't compare objects
return 0;
}
return naturalSortInsensitive(santitizeNaturalValue(a), santitizeNaturalValue(b));
};
/**
* AgGrid checkbox select does not pass clicks within cell but not on the checkbox to checkbox.
* This passes the event to the checkbox when you click anywhere in the cell.
*/
const clickInputWhenContainingCellClicked = (params) => {
const { data, event, colDef } = params;
if (!data || !event)
return;
const element = event.target;
// Already handled
if (element.closest('.GridCell-readonly') ||
(['BUTTON', 'INPUT'].includes(element?.tagName) && element.closest('.ag-cell-inline-editing'))) {
return;
}
const row = element.closest('[row-id]');
if (!row)
return;
const colId = colDef.colId;
if (!colId)
return;
const clickInput = () => {
const cell = row.querySelector(`[col-id='${colId}']`);
if (!cell)
return;
const input = cell.querySelector('input, button');
if (!input) {
return;
}
input.dispatchEvent(event);
};
setTimeout(clickInput, 20);
};
/**
* AgGrid's existing select header doesn't work the way we want.
* If you have partial select then clicking the header checkbox will select all,
* but we want to deselect all on partial select.
*/
const GridHeaderSelect = ({ api }) => {
// This is used to force an update on selection change
const [updateCounter, setUpdateCounter] = useState(0);
const selectedNodeCount = api.getSelectedRows().length;
useEffect(() => {
const clickHandler = () => {
setUpdateCounter(updateCounter + 1);
};
api.addEventListener('selectionChanged', clickHandler);
return () => {
!api.isDestroyed() && api.removeEventListener('selectionChanged', clickHandler);
};
}, [api, updateCounter]);
const handleMultiSelect = () => {
if (selectedNodeCount == 0) {
api.selectAll('filtered');
}
else {
api.deselectAll();
}
};
const totalNodeCount = api.getDisplayedRowCount();
const partialSelect = selectedNodeCount != 0 && selectedNodeCount != totalNodeCount;
const allSelected = selectedNodeCount != 0 && selectedNodeCount == totalNodeCount;
return (jsx("div", { className: clsx('ag-wrapper ag-input-wrapper ag-checkbox-input-wrapper', partialSelect && 'ag-indeterminate', allSelected && 'ag-checked'), onClick: handleMultiSelect, children: jsx("input", { type: "checkbox", className: 'ag-checkbox-input-wrapper', onChange: () => {
/* do nothing */
} }) }));
};
function styleInject(css, ref) {
if ( ref === void 0 ) ref = {};
var insertAt = ref.insertAt;
if (!css || typeof document === 'undefined') { return; }
var head = document.head || document.getElementsByTagName('head')[0];
var style = document.createElement('style');
style.type = 'text/css';
if (insertAt === 'top') {
if (head.firstChild) {
head.insertBefore(style, head.firstChild);
} else {
head.appendChild(style);
}
} else {
head.appendChild(style);
}
if (style.styleSheet) {
style.styleSheet.cssText = css;
} else {
style.appendChild(document.createTextNode(css));
}
}
var css_248z$4 = ".GridContextMenu li.szh-menu__header{color:#6b6966;font-size:14px;font-weight:600;padding-left:16px;padding-right:16px;text-transform:none}.GridContextMenu li.szh-menu__item{padding-left:16px;padding-right:16px}.GridContextMenu li.szh-menu__item .copyMenuMenuItem{align-items:center;display:flex;gap:8px}.GridContextMenu li.szh-menu__item .copyMenuMenuItem__text{color:#2a292c;font-size:16px;font-weight:400;width:80px}.GridContextMenu li.szh-menu__item .copyMenuMenuItem__buttonDefault{background:none;border:2px solid #0000;display:flex;padding:6px}.GridContextMenu li.szh-menu__item .copyMenuMenuItem__buttonDefault.copyMenuMenuItem__buttonDefault--hidden:hover{border:2px solid #2a292c}.GridContextMenu li.szh-menu__item .copyMenuMenuItem__buttonDefault--hidden .LuiIcon{fill:#0000}.GridContextMenu li.szh-menu__item .LuiIcon,.GridContextMenu li.szh-menu__item .copyMenuMenuItem__buttonDefault--hidden:hover .LuiIcon{fill:#6b6966}.GridContextMenu li.szh-menu__item .LuiIcon[data-icon=ic_tick]{padding-left:4px;padding-right:4px}.GridContextMenu li.szh-menu__item--hover{background-color:#e2f3f7}";
styleInject(css_248z$4);
const EventHandlersContext = createContext({
handleClick: () => { },
});
const ItemSettingsContext = createContext({
submenuOpenDelay: 0,
submenuCloseDelay: 0,
});
// FIXME hacking a default context in here is probably bad, but the context is mess
const SettingsContext = createContext({});
// Generate className following BEM methodology: http://getbem.com/naming/
// Modifier value can be one of the following types: boolean, string, undefined
const useBEM = ({ block, element, modifiers, className }) => useMemo(() => {
const blockElement = element ? `${block}__${element}` : block;
let classString = blockElement;
modifiers &&
Object.keys(modifiers).forEach((name) => {
const value = modifiers[name];
if (value)
classString += ` ${blockElement}--${value === true ? name : `${name}-${value}`}`;
});
let expandedClassName = typeof className === 'function' ? className(modifiers) : className;
if (typeof expandedClassName === 'string') {
expandedClassName = expandedClassName.trim();
if (expandedClassName)
classString += ` ${expandedClassName}`;
}
return classString;
}, [block, element, modifiers, className]);
// Adapted from material-ui
// https://github.com/mui-org/material-ui/blob/f996027d00e7e4bff3fc040786c1706f9c6c3f82/packages/material-ui-utils/src/useForkRef.ts
const setRef = (ref, instance) => {
if (typeof ref === 'function') {
ref(instance);
}
else if (ref) {
ref.current = instance;
}
};
const useCombinedRef = (refA, refB) => {
return useMemo(() => {
return (instance) => {
setRef(refA, instance);
setRef(refB, instance);
};
}, [refA, refB]);
};
// Get around a warning when using useLayoutEffect on the server.
// https://github.com/reduxjs/react-redux/blob/b48d087d76f666e1c6c5a9713bbec112a1631841/src/utils/useIsomorphicLayoutEffect.js#L12
// https://gist.github.com/gaearon/e7d97cdf38a2907924ea12e4ebdf3c85
// https://github.com/facebook/react/issues/14927#issuecomment-549457471
const useIsomorphicLayoutEffect = typeof window !== 'undefined' &&
typeof window.document !== 'undefined' &&
typeof window.document.createElement !== 'undefined'
? useLayoutEffect
: useEffect;
const useItemEffect = (isDisabled, menuItemRef, updateItems) => {
useIsomorphicLayoutEffect(() => {
if (!menuItemRef)
return;
if (process.env.NODE_ENV !== 'production' && !updateItems) {
throw new Error(`[React-Menu] This menu item or submenu should be rendered under a menu: ${menuItemRef.current.outerHTML}`);
}
if (isDisabled)
return;
const item = menuItemRef.current;
updateItems(item, true);
return () => {
updateItems(item);
};
}, [isDisabled, menuItemRef, updateItems]);
};
const menuContainerClass = 'szh-menu-container';
const menuClass = 'szh-menu';
const menuButtonClass = 'szh-menu-button';
const menuArrowClass = 'arrow';
const menuItemClass = 'item';
const menuDividerClass = 'divider';
const menuHeaderClass = 'header';
const menuGroupClass = 'group';
const subMenuClass = 'submenu';
const radioGroupClass = 'radio-group';
const Keys = Object.freeze({
ENTER: 'Enter',
TAB: 'Tab',
ESC: 'Escape',
SPACE: ' ',
HOME: 'Home',
END: 'End',
LEFT: 'ArrowLeft',
RIGHT: 'ArrowRight',
UP: 'ArrowUp',
DOWN: 'ArrowDown',
});
const HoverActionTypes = Object.freeze({
RESET: 0,
SET: 1,
UNSET: 2,
INCREASE: 3,
DECREASE: 4,
FIRST: 5,
LAST: 6,
SET_INDEX: 7,
});
const CloseReason = Object.freeze({
CLICK: 'click',
CANCEL: 'cancel',
BLUR: 'blur',
SCROLL: 'scroll',
TAB_FORWARD: 'tab_forward',
TAB_BACKWARD: 'tab_backward',
});
const FocusPositions = Object.freeze({
FIRST: 'first',
LAST: 'last',
});
const MenuStateMap = Object.freeze({
entering: 'opening',
entered: 'open',
exiting: 'closing',
exited: 'closed',
});
const isMenuOpen = (state) => !!state && state[0] === 'o';
const batchedUpdates = unstable_batchedUpdates || ((callback) => callback());
const floatEqual = (a, b, diff = 0.0001) => Math.abs(a - b) < diff;
const getTransition = (transition, name) => transition === true || !!(transition && transition[name]);
function safeCall(fn, arg) {
return typeof fn === 'function' ? fn(arg) : fn;
}
const internalKey = '_szhsinMenu';
const getName = (component) => component[internalKey];
const mergeProps = (target, source) => {
source &&
Object.keys(source).forEach((key) => {
const targetProp = target[key];
const sourceProp = source[key];
if (typeof sourceProp === 'function' && targetProp) {
target[key] = (...arg) => {
sourceProp(...arg);
targetProp(...arg);
};
}
else {
target[key] = sourceProp;
}
});
return target;
};
const parsePadding = (paddingStr) => {
if (paddingStr == null)
return { top: 0, right: 0, bottom: 0, left: 0 };
const padding = paddingStr.trim().split(/\s+/, 4).map(parseFloat);
const top = !isNaN(padding[0]) ? padding[0] : 0;
const right = !isNaN(padding[1]) ? padding[1] : top;
return {
top,
right,
bottom: !isNaN(padding[2]) ? padding[2] : top,
left: !isNaN(padding[3]) ? padding[3] : right,
};
};
// Adapted from https://github.com/popperjs/popper-core/tree/v2.9.1/src/dom-utils
const getScrollAncestor = (node) => {
const thisWindow = (node?.ownerDocument ?? document).defaultView ?? window;
while (node) {
node = node.parentNode;
if (!node || node === thisWindow?.document?.body)
return null;
if (node instanceof Element) {
const { overflow, overflowX, overflowY } = thisWindow.getComputedStyle(node);
if (/auto|scroll|overlay|hidden/.test(overflow + overflowY + overflowX))
return node;
}
}
return null;
};
function commonProps(isDisabled, isHovering) {
return {
'aria-disabled': isDisabled || undefined,
tabIndex: isHovering ? 0 : -1,
};
}
const indexOfNode = (nodeList, node) => findIndex(nodeList, node);
const focusFirstInput = (container) => {
// We can't use instanceof Element in portals, so I use querySelectorAll as a proxy here
if (!container || !('querySelectorAll' in container))
return false;
const inputs = container.querySelectorAll("input[type='text'],input:not([type]),textarea");
const input = inputs[0];
// Using focus as proxy for HTMLElement
if (!input || !('focus' in input))
return false;
input.focus();
// Text areas should start at end
// this is a proxy for instanceof HTMLTextAreaElement
if (['textarea', 'text'].includes(input.type)) {
input.setSelectionRange(0, input.value.length);
}
return true;
};
const HoverItemContext = createContext(undefined);
const withHovering = (name, WrappedComponent) => {
const Component = memo(WrappedComponent);
const WithHovering = forwardRef((props, ref) => {
const menuItemRef = useRef(null);
return (jsx(Fragment, { children: jsx(Component, { ...props, menuItemRef: menuItemRef, externalRef: ref, isHovering: useContext(HoverItemContext) === menuItemRef.current }) }));
});
WithHovering.displayName = `WithHovering(${name})`;
return WithHovering;
};
const useItems = (menuRef, focusRef) => {
const [hoverItem, setHoverItem] = useState();
const stateRef = useRef({
items: [],
hoverIndex: -1,
sorted: false,
});
const mutableState = stateRef.current;
const updateItems = useCallback((item, isMounted) => {
const { items } = mutableState;
if (!item) {
mutableState.items = [];
}
else if (isMounted) {
items.push(item);
}
else {
const index = items.indexOf(item);
if (index > -1) {
items.splice(index, 1);
if (item.contains(document.activeElement)) {
focusRef?.current?.focus();
setHoverItem(undefined);
}
}
}
mutableState.hoverIndex = -1;
mutableState.sorted = false;
}, [mutableState, focusRef]);
const dispatch = useCallback((actionType, item, nextIndex) => {
const { items, hoverIndex } = mutableState;
const sortItems = () => {
if (mutableState.sorted)
return;
const orderedNodes = menuRef.current.querySelectorAll('.szh-menu__item');
items.sort((a, b) => indexOfNode(orderedNodes, a) - indexOfNode(orderedNodes, b));
mutableState.sorted = true;
};
let index = -1;
let newItem = undefined;
let newItemFn = undefined;
switch (actionType) {
case HoverActionTypes.RESET:
break;
case HoverActionTypes.SET:
newItem = item;
break;
case HoverActionTypes.UNSET:
newItemFn = (prevItem) => (prevItem === item ? undefined : prevItem);
break;
case HoverActionTypes.FIRST:
sortItems();
index = 0;
newItem = items[index];
break;
case HoverActionTypes.LAST:
sortItems();
index = items.length - 1;
newItem = items[index];
break;
case HoverActionTypes.SET_INDEX:
if (typeof nextIndex !== 'number')
break;
sortItems();
index = nextIndex;
newItem = items[index];
defer(() => newItem.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' }));
break;
case HoverActionTypes.INCREASE:
sortItems();
index = hoverIndex;
if (index < 0)
index = items.indexOf(item);
index++;
if (index >= items.length)
index = 0;
newItem = items[index];
focusFirstInput(newItem);
break;
case HoverActionTypes.DECREASE: {
sortItems();
index = hoverIndex;
if (index < 0)
index = items.indexOf(item);
index--;
if (index < 0)
index = items.length - 1;
newItem = items[index];
focusFirstInput(newItem);
break;
}
default:
if (process.env.NODE_ENV !== 'production')
throw new Error(`[React-Menu] Unknown hover action type: ${actionType}`);
}
if (!newItem && !newItemFn)
index = -1;
setHoverItem(newItem ?? newItemFn);
mutableState.hoverIndex = index;
}, [menuRef, mutableState]);
return { hoverItem, dispatch, updateItems };
};
const MenuListItemContext = createContext({
dispatch: () => { },
updateItems: () => { },
setOpenSubmenuCount: () => 0,
});
// This hook includes some common stateful logic in MenuItem and FocusableItem
const useItemState = (menuItemRef, focusRef, isHovering, isDisabled) => {
const { submenuCloseDelay } = useContext(ItemSettingsContext);
const { isParentOpen, isSubmenuOpen, dispatch, updateItems } = useContext(MenuListItemContext);
const timeoutId = useRef();
const setHover = () => {
!isHovering && !isDisabled && dispatch(HoverActionTypes.SET, menuItemRef?.current, 0);
};
const unsetHover = () => {
!isDisabled && dispatch(HoverActionTypes.UNSET, menuItemRef?.current, 0);
};
const onBlur = (e) => {
// Focus has moved out of the entire item
// It handles situation such as clicking on a sibling disabled menu item
if (isHovering && !e.currentTarget.contains(e.relatedTarget))
unsetHover();
};
const onPointerMove = () => {
if (isSubmenuOpen) {
if (!timeoutId.current)
timeoutId.current = setTimeout(() => {
timeoutId.current = undefined;
setHover();
}, submenuCloseDelay);
}
else {
setHover();
}
};
const onPointerLeave = (_, keepHover) => {
if (timeoutId.current) {
clearTimeout(timeoutId.current);
timeoutId.current = undefined;
}
!keepHover && unsetHover();
};
useItemEffect(isDisabled, menuItemRef, updateItems);
useEffect(() => () => clearTimeout(timeoutId.current), []);
useEffect(() => {
// Don't set focus when parent menu is closed, otherwise focus will be lost
// and onBlur event will be fired with relatedTarget setting as null.
if (isHovering && isParentOpen) {
focusRef?.current && focusRef.current.focus();
}
}, [focusRef, isHovering, isParentOpen]);
return {
setHover,
onBlur,
onPointerMove,
onPointerLeave,
};
};
const useMenuChange = (onMenuChange, isOpen) => {
const prevOpen = useRef(isOpen);
useEffect(() => {
if (onMenuChange && prevOpen.current !== isOpen)
safeCall(onMenuChange, { open: !!isOpen });
prevOpen.current = isOpen;
}, [onMenuChange, isOpen]);
};
const PRE_ENTER = 0;
const ENTERING = 1;
const ENTERED = 2;
const PRE_EXIT = 3;
const EXITING = 4;
const EXITED = 5;
const UNMOUNTED = 6;
const STATUS = ['preEnter', 'entering', 'entered', 'preExit', 'exiting', 'exited', 'unmounted'];
const getState = status => ({
_s: status,
status: STATUS[status],
isEnter: status < PRE_EXIT,
isMounted: status !== UNMOUNTED,
isResolved: status === ENTERED || status > EXITING
});
const startOrEnd = unmounted => unmounted ? UNMOUNTED : EXITED;
const getEndStatus = (status, unmountOnExit) => {
switch (status) {
case ENTERING:
case PRE_ENTER:
return ENTERED;
case EXITING:
case PRE_EXIT:
return startOrEnd(unmountOnExit);
}
};
const getTimeout = timeout => typeof timeout === 'object' ? [timeout.enter, timeout.exit] : [timeout, timeout];
const nextTick = (transitState, status) => setTimeout(() => {
// Reading document.body.offsetTop can force browser to repaint before transition to the next state
isNaN(document.body.offsetTop) || transitState(status + 1);
}, 0);
const updateState = (status, setState, latestState, timeoutId, onChange) => {
clearTimeout(timeoutId.current);
const state = getState(status);
setState(state);
latestState.current = state;
onChange && onChange({
current: state
});
};
const useTransitionState = ({
enter = true,
exit = true,
preEnter,
preExit,
timeout,
initialEntered,
mountOnEnter,
unmountOnExit,
onStateChange: onChange
} = {}) => {
const [state, setState] = useState(() => getState(initialEntered ? ENTERED : startOrEnd(mountOnEnter)));
const latestState = useRef(state);
const timeoutId = useRef();
const [enterTimeout, exitTimeout] = getTimeout(timeout);
const endTransition = useCallback(() => {
const status = getEndStatus(latestState.current._s, unmountOnExit);
status && updateState(status, setState, latestState, timeoutId, onChange);
}, [onChange, unmountOnExit]);
const toggle = useCallback(toEnter => {
const transitState = status => {
updateState(status, setState, latestState, timeoutId, onChange);
switch (status) {
case ENTERING:
if (enterTimeout >= 0) timeoutId.current = setTimeout(endTransition, enterTimeout);
break;
case EXITING:
if (exitTimeout >= 0) timeoutId.current = setTimeout(endTransition, exitTimeout);
break;
case PRE_ENTER:
case PRE_EXIT:
timeoutId.current = nextTick(transitState, status);
break;
}
};
const enterStage = latestState.current.isEnter;
if (typeof toEnter !== 'boolean') toEnter = !enterStage;
if (toEnter) {
!enterStage && transitState(enter ? preEnter ? PRE_ENTER : ENTERING : ENTERED);
} else {
enterStage && transitState(exit ? preExit ? PRE_EXIT : EXITING : startOrEnd(unmountOnExit));
}
}, [endTransition, onChange, enter, exit, preEnter, preExit, enterTimeout, exitTimeout, unmountOnExit]);
return [state, toggle, endTransition];
};
/**
* A custom Hook which helps manage the states of `ControlledMenu`.
*/
const useMenuState = (props) => {
const { initialMounted, unmountOnClose, transition, transitionTimeout } = props ?? {
transition: false,
};
const [state, toggleMenu, endTransition] = useTransitionState({
mountOnEnter: !initialMounted,
unmountOnExit: unmountOnClose,
timeout: transitionTimeout ?? 500,
enter: getTransition(transition, 'open'),
exit: getTransition(transition, 'close'),
});
return [{ state: MenuStateMap[state.status], endTransition }, toggleMenu];
};
const useMenuStateAndFocus = (options) => {
const [menuProps, toggleMenu] = useMenuState(options);
const [menuItemFocus, setMenuItemFocus] = useState();
const openMenu = (position, alwaysUpdate) => {
setMenuItemFocus({ position, alwaysUpdate });
toggleMenu(true);
};
return [{ menuItemFocus, ...menuProps }, toggleMenu, openMenu];
};
const MenuListContext = createContext({});
const getPositionHelpers = (containerRef, menuRef, menuScroll, boundingBoxPadding) => {
const menuRect = menuRef.current.getBoundingClientRect();
const thisWindow = containerRef.current?.ownerDocument.defaultView ?? window;
const containerRect = (containerRef.current ?? thisWindow.document.body).getBoundingClientRect();
const boundingRect = menuScroll === window || menuScroll === thisWindow
? {
left: 0,
top: 0,
right: thisWindow.document.documentElement.clientWidth,
bottom: thisWindow.innerHeight,
}
: menuScroll.getBoundingClientRect();
const padding = parsePadding(boundingBoxPadding);
// For left and top, overflows are negative value.
// For right and bottom, overflows are positive value.
const getLeftOverflow = (x) => x + containerRect.left - boundingRect.left - padding.left;
const getRightOverflow = (x) => x + containerRect.left + menuRect.width - boundingRect.right + padding.right;
const getTopOverflow = (y) => y + containerRect.top - boundingRect.top - padding.top;
const getBottomOverflow = (y) => y + containerRect.top + menuRect.height - boundingRect.bottom + padding.bottom;
const confineHorizontally = (x) => {
// If menu overflows to the left side, adjust x to have the menu contained within the viewport
// and there is no need to check the right side;
// if it doesn't overflow to the left, then check the right side
let leftOverflow = getLeftOverflow(x);
if (leftOverflow < 0) {
x -= leftOverflow;
}
else {
const rightOverflow = getRightOverflow(x);
if (rightOverflow > 0) {
x -= rightOverflow;
// Check again to make sure menu doesn't overflow to the left
// because it may go off-screen and cannot be scrolled into view.
leftOverflow = getLeftOverflow(x);
if (leftOverflow < 0)
x -= leftOverflow;
}
}
return x;
};
const confineVertically = (y) => {
// Similar logic to confineHorizontally above
let topOverflow = getTopOverflow(y);
if (topOverflow < 0) {
y -= topOverflow;
}
else {
const bottomOverflow = getBottomOverflow(y);
if (bottomOverflow > 0) {
y -= bottomOverflow;
// Check again to make sure menu doesn't overflow to the bottom
// because it may go off screen and cannot be scroll into view.
topOverflow = getTopOverflow(y);
if (topOverflow < 0)
y -= topOverflow;
}
}
return y;
};
return {
menuRect,
containerRect,
getLeftOverflow,
getRightOverflow,
getTopOverflow,
getBottomOverflow,
confineHorizontally,
confineVertically,
};
};
const positionContextMenu = ({ positionHelpers, anchorPoint, }) => {
const { menuRect, containerRect, getLeftOverflow, getRightOverflow, getTopOverflow, getBottomOverflow, confineHorizontally, confineVertically, } = positionHelpers;
let x, y;
// position the menu with cursor pointing to its top-left corner
x = anchorPoint.x - containerRect.left;
y = anchorPoint.y - containerRect.top;
// If menu overflows to the right of viewport,
// try to reposition it on the left side of cursor.
// If menu overflows to the left of viewport after repositioning,
// choose a side which has less overflow
// and adjust x to have it contained within the viewport.
const rightOverflow = getRightOverflow(x);
if (rightOverflow > 0) {
const adjustedX = x - menuRect.width;
const leftOverflow = getLeftOverflow(adjustedX);
if (leftOverflow >= 0 || -leftOverflow < rightOverflow) {
x = adjustedX;
}
x = confineHorizontally(x);
}
// Similar logic to the left and right side above.
let computedDirection = 'bottom';
const bottomOverflow = getBottomOverflow(y);
if (bottomOverflow > 0) {
const adjustedY = y - menuRect.height;
const topOverflow = getTopOverflow(adjustedY);
if (topOverflow >= 0 || -topOverflow < bottomOverflow) {
y = adjustedY;
computedDirection = 'top';
}
y = confineVertically(y);
}
return { x, y, computedDirection };
};
const placeArrowVertical = (p) => {
let y = p.anchorRect.top - p.containerRect.top - p.menuY + p.anchorRect.height / 2;
const offset = p.arrowRef.current ? p.arrowRef.current.offsetHeight * 1.25 : 0;
y = Math.max(offset, y);
y = Math.min(y, p.menuRect.height - offset);
return y;
};
const placeLeftorRight = (props) => {
const { anchorRect, containerRect, menuRect, placeLeftorRightY, placeLeftX, placeRightX, getLeftO