@fluentui/react
Version:
Reusable React components for building web experiences.
197 lines (196 loc) • 10.8 kB
JavaScript
define(["require", "exports", "tslib", "@fluentui/react-portal-compat-context", "react", "react-dom", "../../Fabric", "../../Utilities", "./Layer.notification", "@fluentui/react-hooks"], function (require, exports, tslib_1, react_portal_compat_context_1, React, ReactDOM, Fabric_1, Utilities_1, Layer_notification_1, react_hooks_1) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.LayerBase = void 0;
var getClassNames = (0, Utilities_1.classNamesFunction)();
var getFocusVisibility = function (providerRef) {
if (providerRef === null || providerRef === void 0 ? void 0 : providerRef.current) {
return providerRef.current.classList.contains(Utilities_1.IsFocusVisibleClassName);
}
return false;
};
exports.LayerBase = React.forwardRef(function (props, ref) {
var registerPortalEl = (0, react_portal_compat_context_1.usePortalCompat)();
var rootRef = React.useRef(null);
var mergedRef = (0, react_hooks_1.useMergedRefs)(rootRef, ref);
var layerRef = React.useRef();
var fabricElementRef = React.useRef(null);
var focusContext = React.useContext(Utilities_1.FocusRectsContext);
// Tracks if the layer mount events need to be raised.
// Required to allow the DOM to render after the layer element is added.
var _a = React.useState(false), needRaiseLayerMount = _a[0], setNeedRaiseLayerMount = _a[1];
// Sets the focus visible className when the FocusRectsProvider for the layer is rendered
// This allows the current focus visibility style to be carried over to the layer content
var focusRectsRef = React.useCallback(function (el) {
var isFocusVisible = getFocusVisibility(focusContext === null || focusContext === void 0 ? void 0 : focusContext.providerRef);
if (el && isFocusVisible) {
el.classList.add(Utilities_1.IsFocusVisibleClassName);
}
}, [focusContext]);
var children = props.children, className = props.className, eventBubblingEnabled = props.eventBubblingEnabled, fabricProps = props.fabricProps, hostId = props.hostId, insertFirst = props.insertFirst, _b = props.onLayerDidMount, onLayerDidMount = _b === void 0 ? function () { return undefined; } : _b,
// eslint-disable-next-line deprecation/deprecation
_c = props.onLayerMounted,
// eslint-disable-next-line deprecation/deprecation
onLayerMounted = _c === void 0 ? function () { return undefined; } : _c, onLayerWillUnmount = props.onLayerWillUnmount, styles = props.styles, theme = props.theme;
var fabricRef = (0, react_hooks_1.useMergedRefs)(fabricElementRef, fabricProps === null || fabricProps === void 0 ? void 0 : fabricProps.ref, focusRectsRef);
var classNames = getClassNames(styles, {
theme: theme,
className: className,
isNotHost: !hostId,
});
// Returns the user provided hostId props element, the default target selector,
// or undefined if document doesn't exist.
var getHost = function (doc, shadowRoot) {
var _a, _b;
if (shadowRoot === void 0) { shadowRoot = null; }
var root = shadowRoot !== null && shadowRoot !== void 0 ? shadowRoot : doc;
if (hostId) {
var layerHost = (0, Layer_notification_1.getLayerHost)(hostId);
if (layerHost) {
return (_a = layerHost.rootRef.current) !== null && _a !== void 0 ? _a : null;
}
return (_b = root.getElementById(hostId)) !== null && _b !== void 0 ? _b : null;
}
else {
var defaultHostSelector = (0, Layer_notification_1.getDefaultTarget)();
// Find the host.
var host = defaultHostSelector ? root.querySelector(defaultHostSelector) : null;
// If no host is available, create a container for injecting layers in.
// Having a container scopes layout computation.
if (!host) {
host = (0, Layer_notification_1.createDefaultLayerHost)(doc, shadowRoot);
}
return host;
}
};
// Removes the current layer element's parentNode and runs onLayerWillUnmount prop if provided.
var removeLayerElement = function () {
onLayerWillUnmount === null || onLayerWillUnmount === void 0 ? void 0 : onLayerWillUnmount();
var elem = layerRef.current;
// Clear ref before removing from the DOM
layerRef.current = undefined;
if (elem && elem.parentNode) {
elem.parentNode.removeChild(elem);
}
};
// If a doc or host exists, it will remove and update layer parentNodes.
var createLayerElement = function () {
var _a, _b, _c, _d;
var doc = (0, Utilities_1.getDocument)(rootRef.current);
var shadowRoot = ((_b = (_a = rootRef.current) === null || _a === void 0 ? void 0 : _a.getRootNode()) === null || _b === void 0 ? void 0 : _b.host)
? (_c = rootRef === null || rootRef === void 0 ? void 0 : rootRef.current) === null || _c === void 0 ? void 0 : _c.getRootNode()
: undefined;
if (!doc || (!doc && !shadowRoot)) {
return;
}
var host = getHost(doc, shadowRoot);
if (!host) {
return;
}
// Tabster in V9 sets aria-hidden on the elements outside of the modal dialog. And it doesn't set aria-hidden
// on the virtual children of the dialog. But the host element itself is not a virtual child of a dialog, it
// might contain virtual children. noDirectAriaHidden flag makes Tabster to poke inside the element and set
// aria-hidden on the children (if they are not virtual children of the active V9 dialog) not on the host element.
// To avoid importing Tabster as a dependency here, we just set a flag on the host element which is checked by
// Tabster.
if (!host.__tabsterElementFlags) {
host.__tabsterElementFlags = {};
}
host.__tabsterElementFlags.noDirectAriaHidden = true;
// Remove and re-create any previous existing layer elements.
removeLayerElement();
var el = ((_d = host.ownerDocument) !== null && _d !== void 0 ? _d : doc).createElement('div');
el.className = classNames.root;
(0, Utilities_1.setPortalAttribute)(el);
(0, Utilities_1.setVirtualParent)(el, rootRef.current);
insertFirst ? host.insertBefore(el, host.firstChild) : host.appendChild(el);
layerRef.current = el;
setNeedRaiseLayerMount(true);
};
(0, react_hooks_1.useIsomorphicLayoutEffect)(function () {
createLayerElement();
// Check if the user provided a hostId prop and register the layer with the ID.
if (hostId) {
(0, Layer_notification_1.registerLayer)(hostId, createLayerElement);
}
var unregisterPortalEl = layerRef.current ? registerPortalEl(layerRef.current) : undefined;
return function () {
if (unregisterPortalEl) {
unregisterPortalEl();
}
removeLayerElement();
if (hostId) {
(0, Layer_notification_1.unregisterLayer)(hostId, createLayerElement);
}
};
// eslint-disable-next-line react-hooks/exhaustive-deps -- should run if the hostId updates.
}, [hostId]);
React.useEffect(function () {
if (layerRef.current && needRaiseLayerMount) {
onLayerMounted === null || onLayerMounted === void 0 ? void 0 : onLayerMounted();
onLayerDidMount === null || onLayerDidMount === void 0 ? void 0 : onLayerDidMount();
setNeedRaiseLayerMount(false);
}
}, [needRaiseLayerMount, onLayerMounted, onLayerDidMount]);
useDebugWarnings(props);
return (React.createElement("span", { className: "ms-layer", ref: mergedRef }, layerRef.current &&
ReactDOM.createPortal(React.createElement(Utilities_1.FocusRectsProvider, { layerRoot: true, providerRef: fabricRef },
React.createElement(Fabric_1.Fabric, tslib_1.__assign({}, (!eventBubblingEnabled && getFilteredEvents()), fabricProps, { className: (0, Utilities_1.css)(classNames.content, fabricProps === null || fabricProps === void 0 ? void 0 : fabricProps.className), ref: fabricRef }), children)), layerRef.current)));
});
exports.LayerBase.displayName = 'LayerBase';
var filteredEventProps;
var onFilterEvent = function (ev) {
// We should just be able to check ev.bubble here and only stop events that are bubbling up. However, even though
// mouseenter and mouseleave do NOT bubble up, they are showing up as bubbling. Therefore we stop events based on
// event name rather than ev.bubble.
if (ev.eventPhase === Event.BUBBLING_PHASE &&
ev.type !== 'mouseenter' &&
ev.type !== 'mouseleave' &&
ev.type !== 'touchstart' &&
ev.type !== 'touchend') {
ev.stopPropagation();
}
};
function getFilteredEvents() {
if (!filteredEventProps) {
filteredEventProps = {};
[
'onClick',
'onContextMenu',
'onDoubleClick',
'onDrag',
'onDragEnd',
'onDragEnter',
'onDragExit',
'onDragLeave',
'onDragOver',
'onDragStart',
'onDrop',
'onMouseDown',
'onMouseEnter',
'onMouseLeave',
'onMouseMove',
'onMouseOver',
'onMouseOut',
'onMouseUp',
'onTouchMove',
'onTouchStart',
'onTouchCancel',
'onTouchEnd',
'onKeyDown',
'onKeyPress',
'onKeyUp',
'onFocus',
'onBlur',
'onChange',
'onInput',
'onInvalid',
'onSubmit',
].forEach(function (name) { return (filteredEventProps[name] = onFilterEvent); });
}
return filteredEventProps;
}
function useDebugWarnings(props) {
}
});
//# sourceMappingURL=Layer.base.js.map