UNPKG

@fluentui/react

Version:

Reusable React components for building web experiences.

172 lines 7.45 kB
import { __assign } from "tslib"; import * as React from 'react'; import * as ReactDOM from 'react-dom'; import { Fabric } from '../../Fabric'; import { classNamesFunction, setPortalAttribute, setVirtualParent } from '../../Utilities'; import { registerLayer, getDefaultTarget, unregisterLayer, getLayerHost, createDefaultLayerHost, } from './Layer.notification'; import { useIsomorphicLayoutEffect, useMergedRefs, useWarnings } from '@fluentui/react-hooks'; import { useDocument } from '../../WindowProvider'; var getClassNames = classNamesFunction(); export var LayerBase = React.forwardRef(function (props, ref) { var rootRef = React.useRef(null); var mergedRef = useMergedRefs(rootRef, ref); var layerRef = React.useRef(); // 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]; var doc = useDocument(); var eventBubblingEnabled = props.eventBubblingEnabled, styles = props.styles, theme = props.theme, className = props.className, children = props.children, hostId = props.hostId, _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, insertFirst = props.insertFirst; 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 () { var _a, _b; if (hostId) { var layerHost = getLayerHost(hostId); if (layerHost) { return (_a = layerHost.rootRef.current) !== null && _a !== void 0 ? _a : null; } return (_b = doc === null || doc === void 0 ? void 0 : doc.getElementById(hostId)) !== null && _b !== void 0 ? _b : null; } else { var defaultHostSelector = getDefaultTarget(); // Find the host. var host = defaultHostSelector ? doc === null || doc === void 0 ? void 0 : doc.querySelector(defaultHostSelector) : null; // If no host is available, create a container for injecting layers in. // Having a container scopes layout computation. if (!host && doc) { host = createDefaultLayerHost(doc); } 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; var host = getHost(); if (!host) { return; } // Remove and re-create any previous existing layer elements. removeLayerElement(); var el = (_b = ((_a = host.ownerDocument) !== null && _a !== void 0 ? _a : doc)) === null || _b === void 0 ? void 0 : _b.createElement('div'); if (el) { el.className = classNames.root; setPortalAttribute(el); setVirtualParent(el, rootRef.current); insertFirst ? host.insertBefore(el, host.firstChild) : host.appendChild(el); layerRef.current = el; setNeedRaiseLayerMount(true); } }; useIsomorphicLayoutEffect(function () { createLayerElement(); // Check if the user provided a hostId prop and register the layer with the ID. if (hostId) { registerLayer(hostId, createLayerElement); } return function () { removeLayerElement(); if (hostId) { 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( /* eslint-disable deprecation/deprecation */ React.createElement(Fabric, __assign({}, (!eventBubblingEnabled && getFilteredEvents()), { className: classNames.content }), children), /* eslint-enable deprecation/deprecation */ layerRef.current))); }); 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) { if (process.env.NODE_ENV !== 'production') { // eslint-disable-next-line react-hooks/rules-of-hooks -- build-time conditional useWarnings({ name: 'Layer', props: props, deprecations: { onLayerMounted: 'onLayerDidMount' }, }); } } //# sourceMappingURL=Layer.base.js.map