UNPKG

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