UNPKG

20.1 kBJavaScriptView Raw
1import _extends from '@babel/runtime/helpers/esm/extends';
2import _objectWithoutPropertiesLoose from '@babel/runtime/helpers/esm/objectWithoutPropertiesLoose';
3import React, { createContext, useRef, useContext, useMemo, useEffect, useLayoutEffect, useDebugValue, forwardRef, createElement } from 'react';
4import hoistNonReactStatics from 'hoist-non-react-statics';
5import { ThemeContext } from 'theming';
6export { ThemeProvider, createTheming, useTheme, withTheme } from 'theming';
7import isInBrowser from 'is-in-browser';
8import warning from 'tiny-warning';
9import { SheetsManager, create as create$1, getDynamicStyles, createGenerateId } from 'jss';
10export { SheetsRegistry, createGenerateId } from 'jss';
11import preset from 'jss-preset-default';
12import { shallowEqualObjects } from 'shallow-equal';
13import isPropValid from '@emotion/is-prop-valid';
14import defaultCss from 'css-jss';
15
16var getDisplayName = function getDisplayName(Component) {
17 return Component.displayName || Component.name || 'Component';
18};
19
20var memoize = function memoize(fn) {
21 var lastArgs;
22 var lastResult;
23 return function () {
24 for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
25 args[_key] = arguments[_key];
26 }
27
28 if (Array.isArray(lastArgs) && args.length === lastArgs.length) {
29 var isSame = true;
30
31 for (var i = 0; i < args.length; i++) {
32 if (args[i] !== lastArgs[i]) {
33 isSame = false;
34 }
35 }
36
37 if (isSame) {
38 return lastResult;
39 }
40 }
41
42 lastArgs = args;
43 lastResult = fn.apply(void 0, args);
44 return lastResult;
45 };
46};
47
48var mergeClasses = function mergeClasses(baseClasses, additionalClasses) {
49 var combinedClasses = _extends({}, baseClasses);
50
51 for (var name in additionalClasses) {
52 combinedClasses[name] = name in combinedClasses ? combinedClasses[name] + " " + additionalClasses[name] : additionalClasses[name];
53 }
54
55 return combinedClasses;
56};
57
58/**
59 * Global index counter to preserve source order.
60 * As we create the style sheet during componentWillMount lifecycle,
61 * children are handled after the parents, so the order of style elements would
62 * be parent->child. It is a problem though when a parent passes a className
63 * which needs to override any childs styles. StyleSheet of the child has a higher
64 * specificity, because of the source order.
65 * So our solution is to render sheets them in the reverse order child->sheet, so
66 * that parent has a higher specificity.
67 *
68 * We start at [Number.MIN_SAFE_INTEGER] to always insert sheets from react-jss first before any
69 * sheet which might be inserted manually by the user.
70 */
71var index = Number.MIN_SAFE_INTEGER || -1e9;
72
73var getSheetIndex = function getSheetIndex() {
74 return index++;
75};
76
77var JssContext = createContext({
78 classNamePrefix: '',
79 disableStylesGeneration: false,
80 isSSR: !isInBrowser
81});
82
83var defaultManagers = new Map();
84var getManager = function getManager(context, managerId) {
85 // If `managers` map is present in the context, we use it in order to
86 // let JssProvider reset them when new response has to render server-side.
87 var managers = context.managers;
88
89 if (managers) {
90 if (!managers[managerId]) {
91 managers[managerId] = new SheetsManager();
92 }
93
94 return managers[managerId];
95 }
96
97 var manager = defaultManagers.get(managerId);
98
99 if (!manager) {
100 manager = new SheetsManager();
101 defaultManagers.set(managerId, manager);
102 }
103
104 return manager;
105};
106var manageSheet = function manageSheet(options) {
107 var sheet = options.sheet,
108 context = options.context,
109 index = options.index,
110 theme = options.theme;
111
112 if (!sheet) {
113 return;
114 }
115
116 var manager = getManager(context, index);
117 manager.manage(theme);
118
119 if (context.registry) {
120 context.registry.add(sheet);
121 }
122};
123var unmanageSheet = function unmanageSheet(options) {
124 if (!options.sheet) {
125 return;
126 }
127
128 var manager = getManager(options.context, options.index);
129 manager.unmanage(options.theme);
130};
131
132var defaultJss = create$1(preset());
133
134var sheetsMeta = new WeakMap();
135var getMeta = function getMeta(sheet) {
136 return sheetsMeta.get(sheet);
137};
138var addMeta = function addMeta(sheet, meta) {
139 sheetsMeta.set(sheet, meta);
140};
141
142var getStyles = function getStyles(options) {
143 var styles = options.styles;
144
145 if (typeof styles !== 'function') {
146 return styles;
147 }
148
149 process.env.NODE_ENV !== "production" ? warning(styles.length !== 0, "[JSS] <" + (options.name || 'Hook') + " />'s styles function doesn't rely on the \"theme\" argument. We recommend declaring styles as an object instead.") : void 0;
150 return styles(options.theme);
151};
152
153function getSheetOptions(options, link) {
154 var minify;
155
156 if (options.context.id && options.context.id.minify != null) {
157 minify = options.context.id.minify;
158 }
159
160 var classNamePrefix = options.context.classNamePrefix || '';
161
162 if (options.name && !minify) {
163 classNamePrefix += options.name.replace(/\s/g, '-') + "-";
164 }
165
166 var meta = '';
167 if (options.name) meta = options.name + ", ";
168 meta += typeof options.styles === 'function' ? 'Themed' : 'Unthemed';
169 return _extends({}, options.sheetOptions, {
170 index: options.index,
171 meta: meta,
172 classNamePrefix: classNamePrefix,
173 link: link,
174 generateId: options.sheetOptions && options.sheetOptions.generateId ? options.sheetOptions.generateId : options.context.generateId
175 });
176}
177
178var createStyleSheet = function createStyleSheet(options) {
179 if (options.context.disableStylesGeneration) {
180 return undefined;
181 }
182
183 var manager = getManager(options.context, options.index);
184 var existingSheet = manager.get(options.theme);
185
186 if (existingSheet) {
187 return existingSheet;
188 }
189
190 var jss = options.context.jss || defaultJss;
191 var styles = getStyles(options);
192 var dynamicStyles = getDynamicStyles(styles);
193 var sheet = jss.createStyleSheet(styles, getSheetOptions(options, dynamicStyles !== null));
194 addMeta(sheet, {
195 dynamicStyles: dynamicStyles,
196 styles: styles
197 });
198 manager.add(options.theme, sheet);
199 return sheet;
200};
201var removeDynamicRules = function removeDynamicRules(sheet, rules) {
202 // Loop over each dynamic rule and remove the dynamic rule
203 // We can't just remove the whole sheet as this has all of the rules for every component instance
204 for (var key in rules) {
205 sheet.deleteRule(rules[key]);
206 }
207};
208var updateDynamicRules = function updateDynamicRules(data, sheet, rules) {
209 // Loop over each dynamic rule and update it
210 // We can't just update the whole sheet as this has all of the rules for every component instance
211 for (var key in rules) {
212 sheet.updateOne(rules[key], data);
213 }
214};
215var addDynamicRules = function addDynamicRules(sheet, data) {
216 var meta = getMeta(sheet);
217
218 if (!meta) {
219 return undefined;
220 }
221
222 var rules = {}; // Loop over each dynamic rule and add it to the stylesheet
223
224 for (var key in meta.dynamicStyles) {
225 var initialRuleCount = sheet.rules.index.length;
226 var originalRule = sheet.addRule(key, meta.dynamicStyles[key]); // Loop through all created rules, fixes updating dynamic rules
227
228 for (var i = initialRuleCount; i < sheet.rules.index.length; i++) {
229 var rule = sheet.rules.index[i];
230 sheet.updateOne(rule, data); // If it's the original rule, we need to add it by the correct key so the hook and hoc
231 // can correctly concat the dynamic class with the static one
232
233 rules[originalRule === rule ? key : rule.key] = rule;
234 }
235 }
236
237 return rules;
238};
239
240var getSheetClasses = function getSheetClasses(sheet, dynamicRules) {
241 if (!dynamicRules) {
242 return sheet.classes;
243 }
244
245 var meta = getMeta(sheet);
246
247 if (!meta) {
248 return sheet.classes;
249 }
250
251 var classes = {};
252
253 for (var key in meta.styles) {
254 classes[key] = sheet.classes[key];
255
256 if (key in dynamicRules) {
257 classes[key] += " " + sheet.classes[dynamicRules[key].key];
258 }
259 }
260
261 return classes;
262};
263
264function getUseInsertionEffect(isSSR) {
265 return isSSR ? useEffect : React.useInsertionEffect || // React 18+ (https://github.com/reactwg/react-18/discussions/110)
266 useLayoutEffect;
267}
268
269var noTheme = {};
270
271var createUseStyles = function createUseStyles(styles, options) {
272 if (options === void 0) {
273 options = {};
274 }
275
276 var _options = options,
277 _options$index = _options.index,
278 index = _options$index === void 0 ? getSheetIndex() : _options$index,
279 theming = _options.theming,
280 name = _options.name,
281 sheetOptions = _objectWithoutPropertiesLoose(_options, ["index", "theming", "name"]);
282
283 var ThemeContext$1 = theming && theming.context || ThemeContext;
284
285 var useTheme = function useTheme(theme) {
286 if (typeof styles === 'function') {
287 return theme || useContext(ThemeContext$1) || noTheme;
288 }
289
290 return noTheme;
291 };
292
293 var emptyObject = {};
294 return function useStyles(data) {
295 var isFirstMount = useRef(true);
296 var context = useContext(JssContext);
297 var theme = useTheme(data && data.theme);
298
299 var _useMemo = useMemo(function () {
300 var newSheet = createStyleSheet({
301 context: context,
302 styles: styles,
303 name: name,
304 theme: theme,
305 index: index,
306 sheetOptions: sheetOptions
307 });
308
309 if (newSheet && context.isSSR) {
310 // manage immediately during SSRs. browsers will manage the sheet through useInsertionEffect below
311 manageSheet({
312 index: index,
313 context: context,
314 sheet: newSheet,
315 theme: theme
316 });
317 }
318
319 return [newSheet, newSheet ? addDynamicRules(newSheet, data) : null];
320 }, [context, theme]),
321 sheet = _useMemo[0],
322 dynamicRules = _useMemo[1];
323
324 getUseInsertionEffect(context.isSSR)(function () {
325 // We only need to update the rules on a subsequent update and not in the first mount
326 if (sheet && dynamicRules && !isFirstMount.current) {
327 updateDynamicRules(data, sheet, dynamicRules);
328 }
329 }, [data]);
330 getUseInsertionEffect(context.isSSR)(function () {
331 if (sheet) {
332 manageSheet({
333 index: index,
334 context: context,
335 sheet: sheet,
336 theme: theme
337 });
338 }
339
340 return function () {
341 if (sheet) {
342 unmanageSheet({
343 index: index,
344 context: context,
345 sheet: sheet,
346 theme: theme
347 }); // when sheet changes, remove related dynamic rules
348
349 if (dynamicRules) {
350 removeDynamicRules(sheet, dynamicRules);
351 }
352 }
353 };
354 }, [sheet]);
355 var classes = useMemo(function () {
356 return sheet && dynamicRules ? getSheetClasses(sheet, dynamicRules) : emptyObject;
357 }, [sheet, dynamicRules]);
358 useDebugValue(classes);
359 useDebugValue(theme === noTheme ? 'No theme' : theme);
360 useEffect(function () {
361 isFirstMount.current = false;
362 });
363 return classes;
364 };
365};
366
367var NoRenderer = function NoRenderer(props) {
368 return props.children || null;
369};
370/**
371 * HOC creator function that wrapps the user component.
372 *
373 * `withStyles(styles, [options])(Component)`
374 */
375
376
377var createWithStyles = function createWithStyles(styles, options) {
378 if (options === void 0) {
379 options = {};
380 }
381
382 var _options = options,
383 _options$index = _options.index,
384 index = _options$index === void 0 ? getSheetIndex() : _options$index,
385 theming = _options.theming,
386 injectTheme = _options.injectTheme,
387 sheetOptions = _objectWithoutPropertiesLoose(_options, ["index", "theming", "injectTheme"]);
388
389 var ThemeContext$1 = theming ? theming.context : ThemeContext;
390 return function (InnerComponent) {
391 if (InnerComponent === void 0) {
392 InnerComponent = NoRenderer;
393 }
394
395 var displayName = getDisplayName(InnerComponent);
396 var mergeClassesProp = memoize(function (sheetClasses, classesProp) {
397 return classesProp ? mergeClasses(sheetClasses, classesProp) : sheetClasses;
398 });
399 var hookOptions = Object.assign(sheetOptions, {
400 theming: theming,
401 index: index,
402 name: displayName
403 });
404 var useStyles = createUseStyles(styles, hookOptions);
405 var WithStyles = forwardRef(function (props, ref) {
406 var theme = useContext(ThemeContext$1);
407
408 var newProps = _extends({}, props);
409
410 if (injectTheme && newProps.theme == null) {
411 newProps.theme = theme;
412 }
413
414 var sheetClasses = useStyles(newProps);
415 var classes = mergeClassesProp(sheetClasses, props.classes);
416 return createElement(InnerComponent, _extends({}, newProps, {
417 classes: classes,
418 ref: ref
419 }));
420 });
421 WithStyles.displayName = "WithStyles(" + displayName + ")";
422 WithStyles.defaultProps = _extends({}, InnerComponent.defaultProps);
423 WithStyles.InnerComponent = InnerComponent;
424 return hoistNonReactStatics(WithStyles, InnerComponent);
425 };
426};
427
428var initialContext = {};
429function JssProvider(props) {
430 var managersRef = useRef({});
431 var prevContextRef = useRef();
432 var registryRef = useRef(null);
433
434 var createContext = function createContext(parentContext, prevContext) {
435 if (prevContext === void 0) {
436 prevContext = initialContext;
437 }
438
439 var registry = props.registry,
440 classNamePrefix = props.classNamePrefix,
441 jss = props.jss,
442 generateId = props.generateId,
443 disableStylesGeneration = props.disableStylesGeneration,
444 media = props.media,
445 id = props.id,
446 isSSR = props.isSSR;
447
448 var context = _extends({}, parentContext);
449
450 if (registry) {
451 context.registry = registry; // This way we identify a new request on the server, because user will create
452 // a new Registry instance for each.
453
454 if (registry !== registryRef.current) {
455 // We reset managers because we have to regenerate all sheets for the new request.
456 managersRef.current = {};
457 registryRef.current = registry;
458 }
459 }
460
461 context.managers = managersRef.current;
462
463 if (id !== undefined) {
464 context.id = id;
465 }
466
467 if (generateId !== undefined) {
468 context.generateId = generateId;
469 } else if (!context.generateId || !prevContext || context.id !== prevContext.id) {
470 context.generateId = createGenerateId(context.id);
471 }
472
473 if (classNamePrefix) {
474 context.classNamePrefix = (context.classNamePrefix || '') + classNamePrefix;
475 }
476
477 if (media !== undefined) {
478 context.media = media;
479 }
480
481 if (jss) {
482 context.jss = jss;
483 }
484
485 if (disableStylesGeneration !== undefined) {
486 context.disableStylesGeneration = disableStylesGeneration;
487 }
488
489 if (isSSR !== undefined) {
490 context.isSSR = isSSR;
491 }
492
493 if (prevContext && shallowEqualObjects(prevContext, context)) {
494 return prevContext;
495 }
496
497 return context;
498 };
499
500 var renderProvider = function renderProvider(parentContext) {
501 var children = props.children;
502 var context = createContext(parentContext, prevContextRef.current);
503 prevContextRef.current = context;
504 return createElement(JssContext.Provider, {
505 value: context
506 }, children);
507 };
508
509 return createElement(JssContext.Consumer, null, renderProvider);
510}
511
512var parseStyles = function parseStyles(args) {
513 var dynamicStyles = [];
514 var staticStyle;
515 var labels = []; // Not using ...rest to optimize perf.
516
517 for (var key in args) {
518 var style = args[key];
519 if (!style) continue;
520
521 if (typeof style === 'function') {
522 dynamicStyles.push(style);
523 } else {
524 if (!staticStyle) staticStyle = {};
525 Object.assign(staticStyle, style);
526 var _staticStyle = staticStyle,
527 _label = _staticStyle.label;
528
529 if (_label) {
530 if (labels.indexOf(_label) === -1) labels.push(_label);
531 }
532 }
533 }
534
535 var styles = {};
536 var label = labels.length === 0 ? 'sc' : labels.join('-');
537
538 if (staticStyle) {
539 // Label should not leak to the core.
540 if ('label' in staticStyle) delete staticStyle.label;
541 styles[label] = staticStyle;
542 } // When there is only one function rule, we don't need to wrap it.
543
544
545 if (dynamicStyles.length === 1) {
546 styles.scd = dynamicStyles[0];
547 } // We create a new function rule which will call all other function rules
548 // and merge the styles they return.
549
550
551 if (dynamicStyles.length > 1) {
552 styles.scd = function (props) {
553 var merged = {};
554
555 for (var i = 0; i < dynamicStyles.length; i++) {
556 var dynamicStyle = dynamicStyles[i](props);
557 if (dynamicStyle) Object.assign(merged, dynamicStyle);
558 }
559
560 return merged;
561 };
562 }
563
564 return {
565 styles: styles,
566 label: label
567 };
568};
569
570var shouldForwardPropSymbol = Symbol('react-jss-styled');
571
572var getShouldForwardProp = function getShouldForwardProp(tagOrComponent, options) {
573 var shouldForwardProp = options.shouldForwardProp;
574 var childShouldForwardProp = tagOrComponent[shouldForwardPropSymbol];
575 var finalShouldForwardProp = shouldForwardProp || childShouldForwardProp;
576
577 if (shouldForwardProp && childShouldForwardProp) {
578 finalShouldForwardProp = function finalShouldForwardProp(prop) {
579 return childShouldForwardProp(prop) && shouldForwardProp(prop);
580 };
581 }
582
583 return finalShouldForwardProp;
584};
585
586var getChildProps = function getChildProps(props, shouldForwardProp, isTag) {
587 var childProps = {};
588
589 for (var prop in props) {
590 if (shouldForwardProp) {
591 if (shouldForwardProp(prop) === true) {
592 childProps[prop] = props[prop];
593 }
594
595 continue;
596 } // We don't want to pass non-dom props to the DOM.
597
598
599 if (isTag) {
600 if (isPropValid(prop)) {
601 childProps[prop] = props[prop];
602 }
603
604 continue;
605 }
606
607 childProps[prop] = props[prop];
608 }
609
610 return childProps;
611}; // eslint-disable-next-line no-unused-vars
612
613
614var configureStyled = function configureStyled(tagOrComponent, options) {
615 if (options === void 0) {
616 options = {};
617 }
618
619 var _options = options,
620 theming = _options.theming;
621 var isTag = typeof tagOrComponent === 'string';
622 var ThemeContext$1 = theming ? theming.context : ThemeContext;
623 var shouldForwardProp = getShouldForwardProp(tagOrComponent, options);
624
625 var _options2 = options,
626 _ = _options2.shouldForwardProp,
627 hookOptions = _objectWithoutPropertiesLoose(_options2, ["shouldForwardProp"]);
628
629 return function createStyledComponent() {
630 // eslint-disable-next-line prefer-rest-params
631 var _parseStyles = parseStyles(arguments),
632 styles = _parseStyles.styles,
633 label = _parseStyles.label;
634
635 var useStyles = createUseStyles(styles, hookOptions);
636
637 var Styled = function Styled(props) {
638 var as = props.as,
639 className = props.className;
640 var theme = useContext(ThemeContext$1);
641 var propsWithTheme = Object.assign({
642 theme: theme
643 }, props);
644 var classes = useStyles(propsWithTheme);
645 var childProps = getChildProps(props, shouldForwardProp, isTag);
646 var classNames = ((classes[label] || classes.sc || '') + " " + (classes.scd || '')).trim();
647 childProps.className = className ? className + " " + classNames : classNames;
648
649 if (!isTag && shouldForwardProp) {
650 tagOrComponent[shouldForwardPropSymbol] = shouldForwardProp;
651 }
652
653 if (isTag && as) {
654 return createElement(as, childProps);
655 }
656
657 return createElement(tagOrComponent, childProps);
658 };
659
660 return Styled;
661 };
662};
663
664/* eslint-disable prefer-rest-params, prefer-spread */
665var create = function create(css) {
666 if (css === void 0) {
667 css = defaultCss;
668 }
669
670 return function createElement$1(type, props) {
671 var args = arguments;
672
673 if (props && props.css) {
674 var className = css(props.css);
675 var newProps = Object.assign({}, props);
676 newProps.className = props.className ? props.className + " " + className : className;
677 delete newProps.css;
678 args[1] = newProps;
679 }
680
681 return createElement.apply(undefined, args);
682 };
683};
684var jsx = create();
685
686export default createWithStyles;
687export { JssContext, JssProvider, create as createJsx, createUseStyles, defaultJss as jss, jsx, configureStyled as styled, createWithStyles as withStyles };