@fluentui/react-northstar
Version:
A themable React component library.
217 lines (212 loc) • 8.57 kB
JavaScript
import _extends from "@babel/runtime/helpers/esm/extends";
import _noop from "lodash/noop";
import _isBoolean from "lodash/isBoolean";
import _isFunction from "lodash/isFunction";
import _forEach from "lodash/forEach";
import _isPlainObject from "lodash/isPlainObject";
import { defaultContextValue, getElementType, useUnhandledProps, RendererContext, Telemetry, useFluentContext, unstable_getStyles, useIsomorphicLayoutEffect, Unstable_FluentContextProvider } from '@fluentui/react-bindings';
import { mergeSiteVariables } from '@fluentui/styles';
import * as PropTypes from 'prop-types';
import * as React from 'react';
import { setUpWhatInput, tryCleanupWhatInput } from '../../utils';
import { mergeProviderContexts } from '../../utils/mergeProviderContexts';
import { ProviderConsumer } from './ProviderConsumer';
import { PortalContext } from './portalContext';
var renderFontFaces = function renderFontFaces(renderer, theme) {
if (!theme.fontFaces) {
return;
}
var renderFontObject = function renderFontObject(font) {
if (!_isPlainObject(font)) {
throw new Error("fontFaces must be objects, got: " + typeof font);
}
renderer.renderFont(font);
};
theme.fontFaces.forEach(function (font) {
renderFontObject(font);
});
};
var renderStaticStyles = function renderStaticStyles(renderer, theme, siteVariables) {
if (!theme.staticStyles) {
return;
}
var renderObject = function renderObject(object) {
_forEach(object, function (style, selector) {
renderer.renderGlobal(style, selector);
});
};
theme.staticStyles.forEach(function (staticStyle) {
if (typeof staticStyle === 'string') {
renderer.renderGlobal(staticStyle);
} else if (_isPlainObject(staticStyle)) {
renderObject(staticStyle);
} else if (_isFunction(staticStyle)) {
var preparedSiteVariables = mergeSiteVariables(undefined, siteVariables);
renderObject(staticStyle(preparedSiteVariables));
} else {
throw new Error("staticStyles array must contain CSS strings, style objects, or style functions, got: " + typeof staticStyle);
}
});
};
export var providerClassName = 'ui-provider';
/**
* The Provider passes the CSS-in-JS renderer, theme styles and other settings to Fluent UI components.
*/
export var Provider = /*#__PURE__*/function () {
var Provider = function Provider(props) {
var children = props.children,
className = props.className,
design = props.design,
overwrite = props.overwrite,
styles = props.styles,
variables = props.variables,
telemetryRef = props.telemetryRef;
var ElementType = getElementType(props);
var unhandledProps = useUnhandledProps(Provider.handledProps, props);
var rendersReactFragment = ElementType === React.Fragment;
var telemetry = React.useMemo(function () {
if (!telemetryRef) {
return undefined;
}
if (!telemetryRef.current) {
telemetryRef.current = new Telemetry();
}
return telemetryRef.current;
}, [telemetryRef]);
var consumedContext = useFluentContext();
var incomingContext = overwrite ? defaultContextValue : consumedContext;
var createRenderer = React.useContext(RendererContext);
// Memoization of `inputContext` & `outgoingContext` is required to avoid useless notifications of components that
// consume `useFluentContext()` on each render
// @see https://reactjs.org/docs/context.html#caveats
var inputContext = React.useMemo(function () {
return {
disableAnimations: props.disableAnimations,
performance: props.performance,
rtl: props.rtl,
target: props.target,
telemetry: telemetry,
theme: props.theme
};
}, [props.disableAnimations, props.performance, props.rtl, props.target, telemetry, props.theme]);
var outgoingContext = React.useMemo(function () {
return mergeProviderContexts(createRenderer, incomingContext, inputContext);
}, [createRenderer, incomingContext, inputContext]);
var rtlProps = {};
// only add dir attribute for top level provider or when direction changes from parent to child
if (consumedContext.rtl !== outgoingContext.rtl && _isBoolean(outgoingContext.rtl)) {
rtlProps.dir = outgoingContext.rtl ? 'rtl' : 'ltr';
}
// Perf optimisation
// Do not invoke styling layer is there is no need
var _ref = rendersReactFragment ? {
classes: {
root: ''
}
} : unstable_getStyles({
allDisplayNames: [Provider.displayName],
className: providerClassName,
primaryDisplayName: Provider.displayName,
componentProps: {},
inlineStylesProps: {
className: className,
design: design,
styles: styles,
variables: variables
},
disableAnimations: outgoingContext.disableAnimations,
performance: outgoingContext.performance,
renderer: outgoingContext.renderer,
rtl: outgoingContext.rtl,
theme: outgoingContext.theme,
saveDebug: _noop,
telemetry: undefined
}),
classes = _ref.classes;
var portalContextValue = React.useMemo(function () {
return {
className: classes.root
};
}, [classes.root]);
var RenderProvider = outgoingContext.renderer.Provider;
useIsomorphicLayoutEffect(function () {
renderFontFaces(outgoingContext.renderer, props.theme);
renderStaticStyles(outgoingContext.renderer, props.theme, outgoingContext.theme.siteVariables);
if (props.target) {
setUpWhatInput(props.target);
}
outgoingContext.renderer.registerUsage();
return function () {
if (props.target) {
tryCleanupWhatInput(props.target);
}
outgoingContext.renderer.unregisterUsage();
};
}, []);
// If a Fragment is rendered:
// - do not spread anything to an element - React.Fragment can only have `key` and `children` props
// - as we don't apply styles "PortalContext.Provider" should not be rendered
if (rendersReactFragment) {
return /*#__PURE__*/React.createElement(RenderProvider, {
target: outgoingContext.target
}, /*#__PURE__*/React.createElement(Unstable_FluentContextProvider, {
value: outgoingContext
}, /*#__PURE__*/React.createElement(React.Fragment, null, children)));
}
return /*#__PURE__*/React.createElement(RenderProvider, {
target: outgoingContext.target
}, /*#__PURE__*/React.createElement(Unstable_FluentContextProvider, {
value: outgoingContext
}, /*#__PURE__*/React.createElement(PortalContext.Provider, {
value: portalContextValue
}, /*#__PURE__*/React.createElement(ElementType, _extends({
className: classes.root
}, rtlProps, unhandledProps), children))));
};
Provider.displayName = 'Provider';
Provider.defaultProps = {
theme: {}
};
Provider.propTypes = {
as: PropTypes.elementType,
design: PropTypes.object,
variables: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
styles: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
theme: PropTypes.shape({
siteVariables: PropTypes.object,
componentVariables: PropTypes.object,
componentStyles: PropTypes.objectOf(PropTypes.any),
fontFaces: PropTypes.arrayOf(PropTypes.exact({
name: PropTypes.string.isRequired,
paths: PropTypes.arrayOf(PropTypes.string),
props: PropTypes.shape({
fontStretch: PropTypes.string,
fontStyle: PropTypes.string,
fontVariant: PropTypes.string,
fontWeight: PropTypes.number,
localAlias: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]),
unicodeRange: PropTypes.string
})
})),
staticStyles: PropTypes.array,
animations: PropTypes.objectOf(PropTypes.any)
}),
rtl: PropTypes.bool,
disableAnimations: PropTypes.bool,
// Heads Up!
// Keep in sync with packages/react-bindings/src/styles/types.ts
performance: PropTypes.shape({
enableSanitizeCssPlugin: PropTypes.bool,
enableStylesCaching: PropTypes.bool,
enableVariablesCaching: PropTypes.bool
}),
children: PropTypes.node.isRequired,
overwrite: PropTypes.bool,
target: PropTypes.object,
telemetryRef: PropTypes.object
};
Provider.handledProps = Object.keys(Provider.propTypes);
Provider.Consumer = ProviderConsumer;
return Provider;
}();
//# sourceMappingURL=Provider.js.map