@fluentui/react
Version:
Reusable React components for building web experiences.
221 lines (220 loc) • 12.6 kB
JavaScript
define(["require", "exports", "tslib", "react", "../../Utilities", "../../utilities/ButtonGrid/ButtonGrid", "./ColorPickerGridCell", "@fluentui/react-hooks", "../../utilities/dom"], function (require, exports, tslib_1, React, Utilities_1, ButtonGrid_1, ColorPickerGridCell_1, react_hooks_1, dom_1) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SwatchColorPickerBase = void 0;
var getClassNames = (0, Utilities_1.classNamesFunction)();
var COMPONENT_NAME = 'SwatchColorPicker';
function useDebugWarnings(props) {
}
exports.SwatchColorPickerBase = React.forwardRef(function (props, ref) {
var defaultId = (0, react_hooks_1.useId)('swatchColorPicker');
var id = props.id || defaultId;
var doc = (0, dom_1.useDocumentEx)();
var internalState = (0, react_hooks_1.useConst)({
isNavigationIdle: true,
cellFocused: false,
navigationIdleTimeoutId: undefined,
navigationIdleDelay: 250,
});
var _a = (0, react_hooks_1.useSetTimeout)(), setTimeout = _a.setTimeout, clearTimeout = _a.clearTimeout;
useDebugWarnings(props);
var colorCells = props.colorCells, _b = props.cellShape, cellShape = _b === void 0 ? 'circle' : _b, columnCount = props.columnCount, _c = props.shouldFocusCircularNavigate, shouldFocusCircularNavigate = _c === void 0 ? true : _c, className = props.className, _d = props.disabled, disabled = _d === void 0 ? false : _d, doNotContainWithinFocusZone = props.doNotContainWithinFocusZone, styles = props.styles, _e = props.cellMargin, cellMargin = _e === void 0 ? 10 : _e, defaultSelectedId = props.defaultSelectedId, focusOnHover = props.focusOnHover, mouseLeaveParentSelector = props.mouseLeaveParentSelector, onChange = props.onChange,
// eslint-disable-next-line @typescript-eslint/no-deprecated
onColorChanged = props.onColorChanged, onCellHovered = props.onCellHovered, onCellFocused = props.onCellFocused, getColorGridCellStyles = props.getColorGridCellStyles, cellHeight = props.cellHeight, cellWidth = props.cellWidth, cellBorderWidth = props.cellBorderWidth, onRenderColorCellContent = props.onRenderColorCellContent;
/**
* Add an index to each color cells. Memoizes this so that color cells do not re-render on every update.
*/
var itemsWithIndex = React.useMemo(function () {
return colorCells.map(function (item, index) {
return tslib_1.__assign(tslib_1.__assign({}, item), { index: index });
});
}, [colorCells]);
var mergedOnChange = React.useCallback(function (ev, newSelectedId) {
var _a;
// Call both new and old change handlers, and add the extra `color` parameter
var newColor = (_a = colorCells.filter(function (c) { return c.id === newSelectedId; })[0]) === null || _a === void 0 ? void 0 : _a.color;
onChange === null || onChange === void 0 ? void 0 : onChange(ev, newSelectedId, newColor);
onColorChanged === null || onColorChanged === void 0 ? void 0 : onColorChanged(newSelectedId, newColor);
}, [onChange, onColorChanged, colorCells]);
var _f = (0, react_hooks_1.useControllableValue)(props.selectedId, defaultSelectedId, mergedOnChange), selectedId = _f[0], setSelectedId = _f[1];
var classNames = getClassNames(styles, {
theme: props.theme,
className: className,
cellMargin: cellMargin,
});
var gridStyles = {
root: classNames.root,
tableCell: classNames.tableCell,
focusedContainer: classNames.focusedContainer,
};
/**
* If there is only one row of cells, they should use radio semantics,
* multi-row swatch cells should use grid semantics.
* There are two reasons for this:
* 1. Radios are a more simple and understandable control, and a better fit for a single-dimensional picker.
* 2. Multiple browsers use heuristics to strip table and grid roles from single-row tables with no column headers.
*/
var isSemanticRadio = colorCells.length <= columnCount;
/**
* When the whole swatchColorPicker is blurred,
* make sure to clear the pending focused stated
*/
var onSwatchColorPickerBlur = React.useCallback(function (event) {
if (onCellFocused) {
internalState.cellFocused = false;
onCellFocused(undefined, undefined, event);
}
}, [internalState, onCellFocused]);
/**
* Callback passed to the GridCell that will manage triggering the onCellHovered callback for mouseEnter
*/
var onMouseEnter = React.useCallback(function (ev) {
if (!focusOnHover) {
return !internalState.isNavigationIdle || !!disabled;
}
if (internalState.isNavigationIdle && !disabled) {
ev.currentTarget.focus();
}
return true;
}, [focusOnHover, internalState, disabled]);
/**
* Callback passed to the GridCell that will manage Hover/Focus updates
*/
var onMouseMove = React.useCallback(function (ev) {
if (!focusOnHover) {
return !internalState.isNavigationIdle || !!disabled;
}
var targetElement = ev.currentTarget;
// If navigation is idle and the targetElement is the focused element bail out
if (internalState.isNavigationIdle && !(doc && targetElement === doc.activeElement)) {
targetElement.focus();
}
return true;
}, [focusOnHover, internalState, disabled, doc]);
/**
* Callback passed to the GridCell that will manage Hover/Focus updates
*/
var onMouseLeave = React.useCallback(function (ev) {
var _a;
var parentSelector = mouseLeaveParentSelector;
if (!focusOnHover || !parentSelector || !internalState.isNavigationIdle || disabled) {
return;
}
// Get the elements that math the given selector
var elements = (_a = doc === null || doc === void 0 ? void 0 : doc.querySelectorAll(parentSelector)) !== null && _a !== void 0 ? _a : [];
// iterate over the elements return to make sure it is a parent of the target and focus it
for (var index = 0; index < elements.length; index += 1) {
if (elements[index].contains(ev.currentTarget)) {
/**
* IE11 focus() method forces parents to scroll to top of element.
* Edge and IE expose a setActive() function for focusable divs that
* sets the page focus but does not scroll the parent element.
*/
if (elements[index].setActive) {
try {
elements[index].setActive();
}
catch (e) {
/* no-op */
}
}
else {
elements[index].focus();
}
break;
}
}
}, [disabled, focusOnHover, internalState, mouseLeaveParentSelector, doc]);
/**
* Callback passed to the GridCell class that will trigger the onCellHovered callback of the SwatchColorPicker
* NOTE: This will not be triggered if shouldFocusOnHover === true
*/
var onGridCellHovered = React.useCallback(function (item, event) {
if (onCellHovered) {
item ? onCellHovered(item.id, item.color, event) : onCellHovered(undefined, undefined, event);
}
}, [onCellHovered]);
/**
* Callback passed to the GridCell class that will trigger the onCellFocus callback of the SwatchColorPicker
*/
var onGridCellFocused = React.useCallback(function (item, event) {
if (onCellFocused) {
if (item) {
internalState.cellFocused = true;
return onCellFocused(item.id, item.color, event);
}
else {
internalState.cellFocused = false;
return onCellFocused(undefined, undefined, event);
}
}
}, [internalState, onCellFocused]);
/**
* Handle the click on a cell
*/
var onCellClick = React.useCallback(function (item, event) {
if (disabled || item.disabled) {
return;
}
if (item.id !== selectedId) {
if (onCellFocused && internalState.cellFocused) {
internalState.cellFocused = false;
onCellFocused(undefined, undefined, event);
}
setSelectedId(item.id, event);
}
}, [disabled, internalState, onCellFocused, selectedId, setSelectedId]);
/**
* Sets a timeout so we won't process any mouse "hover" events
* while navigating (via mouseWheel or arrowKeys)
*/
var setNavigationTimeout = React.useCallback(function () {
if (!internalState.isNavigationIdle && internalState.navigationIdleTimeoutId !== undefined) {
clearTimeout(internalState.navigationIdleTimeoutId);
internalState.navigationIdleTimeoutId = undefined;
}
else {
internalState.isNavigationIdle = false;
}
internalState.navigationIdleTimeoutId = setTimeout(function () {
internalState.isNavigationIdle = true;
}, internalState.navigationIdleDelay);
}, [clearTimeout, internalState, setTimeout]);
/**
* Callback used to handle KeyCode events
*/
var onKeyDown = React.useCallback(function (ev) {
if (
// eslint-disable-next-line @typescript-eslint/no-deprecated
ev.which === Utilities_1.KeyCodes.up ||
// eslint-disable-next-line @typescript-eslint/no-deprecated
ev.which === Utilities_1.KeyCodes.down ||
// eslint-disable-next-line @typescript-eslint/no-deprecated
ev.which === Utilities_1.KeyCodes.left ||
// eslint-disable-next-line @typescript-eslint/no-deprecated
ev.which === Utilities_1.KeyCodes.right) {
setNavigationTimeout();
}
}, [setNavigationTimeout]);
/**
* Render a color cell
* @param item - The item to render
* @returns - Element representing the item
*/
var renderOption = function (item) {
return (React.createElement(ColorPickerGridCell_1.ColorPickerGridCell, { item: item, idPrefix: id, color: item.color, styles: getColorGridCellStyles, disabled: disabled || item.disabled, onClick: onCellClick, onHover: onGridCellHovered, onFocus: onGridCellFocused, selected: selectedId === item.id, circle: cellShape === 'circle', label: item.label, onMouseEnter: onMouseEnter, onMouseMove: onMouseMove, onMouseLeave: onMouseLeave, onWheel: setNavigationTimeout, onKeyDown: onKeyDown, onRenderColorCellContent: onRenderColorCellContent, height: cellHeight, width: cellWidth, borderWidth: cellBorderWidth, isRadio: isSemanticRadio }));
};
if (colorCells.length < 1 || columnCount < 1) {
return null;
}
var onRenderItem = function (item, index) {
var _a = props.onRenderColorCell, onRenderColorCell = _a === void 0 ? renderOption : _a;
return onRenderColorCell(item, renderOption);
};
return (React.createElement(ButtonGrid_1.ButtonGrid, tslib_1.__assign({}, props, { ref: ref, id: id, items: itemsWithIndex, columnCount: columnCount, isSemanticRadio: isSemanticRadio,
// eslint-disable-next-line react/jsx-no-bind
onRenderItem: onRenderItem, shouldFocusCircularNavigate: shouldFocusCircularNavigate, doNotContainWithinFocusZone: doNotContainWithinFocusZone, onBlur: onSwatchColorPickerBlur, theme: props.theme, styles: gridStyles })));
});
exports.SwatchColorPickerBase.displayName = COMPONENT_NAME;
});
//# sourceMappingURL=SwatchColorPicker.base.js.map