UNPKG

19.6 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 } 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});
81
82var defaultManagers = new Map();
83var getManager = function getManager(context, managerId) {
84 // If `managers` map is present in the context, we use it in order to
85 // let JssProvider reset them when new response has to render server-side.
86 var managers = context.managers;
87
88 if (managers) {
89 if (!managers[managerId]) {
90 managers[managerId] = new SheetsManager();
91 }
92
93 return managers[managerId];
94 }
95
96 var manager = defaultManagers.get(managerId);
97
98 if (!manager) {
99 manager = new SheetsManager();
100 defaultManagers.set(managerId, manager);
101 }
102
103 return manager;
104};
105var manageSheet = function manageSheet(options) {
106 var sheet = options.sheet,
107 context = options.context,
108 index = options.index,
109 theme = options.theme;
110
111 if (!sheet) {
112 return;
113 }
114
115 var manager = getManager(context, index);
116 manager.manage(theme);
117
118 if (context.registry) {
119 context.registry.add(sheet);
120 }
121};
122var unmanageSheet = function unmanageSheet(options) {
123 if (!options.sheet) {
124 return;
125 }
126
127 var manager = getManager(options.context, options.index);
128 manager.unmanage(options.theme);
129};
130
131var defaultJss = create$1(preset());
132
133var sheetsMeta = new WeakMap();
134var getMeta = function getMeta(sheet) {
135 return sheetsMeta.get(sheet);
136};
137var addMeta = function addMeta(sheet, meta) {
138 sheetsMeta.set(sheet, meta);
139};
140
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 && options.sheetOptions.generateId ? options.sheetOptions.generateId : options.context.generateId
174 });
175}
176
177var createStyleSheet = function createStyleSheet(options) {
178 if (options.context.disableStylesGeneration) {
179 return undefined;
180 }
181
182 var manager = getManager(options.context, options.index);
183 var existingSheet = manager.get(options.theme);
184
185 if (existingSheet) {
186 return existingSheet;
187 }
188
189 var jss = options.context.jss || defaultJss;
190 var styles = getStyles(options);
191 var dynamicStyles = getDynamicStyles(styles);
192 var sheet = jss.createStyleSheet(styles, getSheetOptions(options, dynamicStyles !== null));
193 addMeta(sheet, {
194 dynamicStyles: dynamicStyles,
195 styles: styles
196 });
197 manager.add(options.theme, sheet);
198 return sheet;
199};
200var removeDynamicRules = function removeDynamicRules(sheet, rules) {
201 // Loop over each dynamic rule and remove the dynamic rule
202 // We can't just remove the whole sheet as this has all of the rules for every component instance
203 for (var key in rules) {
204 sheet.deleteRule(rules[key]);
205 }
206};
207var updateDynamicRules = function updateDynamicRules(data, sheet, rules) {
208 // Loop over each dynamic rule and update it
209 // We can't just update the whole sheet as this has all of the rules for every component instance
210 for (var key in rules) {
211 sheet.updateOne(rules[key], data);
212 }
213};
214var addDynamicRules = function addDynamicRules(sheet, data) {
215 var meta = getMeta(sheet);
216
217 if (!meta) {
218 return undefined;
219 }
220
221 var rules = {}; // Loop over each dynamic rule and add it to the stylesheet
222
223 for (var key in meta.dynamicStyles) {
224 var initialRuleCount = sheet.rules.index.length;
225 var originalRule = sheet.addRule(key, meta.dynamicStyles[key]); // Loop through all created rules, fixes updating dynamic rules
226
227 for (var i = initialRuleCount; i < sheet.rules.index.length; i++) {
228 var rule = sheet.rules.index[i];
229 sheet.updateOne(rule, data); // If it's the original rule, we need to add it by the correct key so the hook and hoc
230 // can correctly concat the dynamic class with the static one
231
232 rules[originalRule === rule ? key : rule.key] = rule;
233 }
234 }
235
236 return rules;
237};
238
239var getSheetClasses = function getSheetClasses(sheet, dynamicRules) {
240 if (!dynamicRules) {
241 return sheet.classes;
242 }
243
244 var meta = getMeta(sheet);
245
246 if (!meta) {
247 return sheet.classes;
248 }
249
250 var classes = {};
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 var emptyObject = {};
289 return function useStyles(data) {
290 var isFirstMount = useRef(true);
291 var context = useContext(JssContext);
292 var theme = useTheme(data && data.theme);
293
294 var _React$useMemo = useMemo(function () {
295 var newSheet = createStyleSheet({
296 context: context,
297 styles: styles,
298 name: name,
299 theme: theme,
300 index: index,
301 sheetOptions: sheetOptions
302 });
303 var newDynamicRules = newSheet ? addDynamicRules(newSheet, data) : null;
304
305 if (newSheet) {
306 manageSheet({
307 index: index,
308 context: context,
309 sheet: newSheet,
310 theme: theme
311 });
312 }
313
314 return [newSheet, newDynamicRules];
315 }, [context, theme]),
316 sheet = _React$useMemo[0],
317 dynamicRules = _React$useMemo[1];
318
319 useEffectOrLayoutEffect(function () {
320 // We only need to update the rules on a subsequent update and not in the first mount
321 if (sheet && dynamicRules && !isFirstMount.current) {
322 updateDynamicRules(data, sheet, dynamicRules);
323 }
324 }, [data]);
325 useEffectOrLayoutEffect(function () {
326 return (// cleanup only
327 function () {
328 if (sheet) {
329 unmanageSheet({
330 index: index,
331 context: context,
332 sheet: sheet,
333 theme: theme
334 });
335 }
336
337 if (sheet && dynamicRules) {
338 removeDynamicRules(sheet, dynamicRules);
339 }
340 }
341 );
342 }, [sheet]);
343 var classes = useMemo(function () {
344 return sheet && dynamicRules ? getSheetClasses(sheet, dynamicRules) : emptyObject;
345 }, [sheet, dynamicRules]);
346 useDebugValue(classes);
347 useDebugValue(theme === noTheme ? 'No theme' : theme);
348 useEffect(function () {
349 isFirstMount.current = false;
350 });
351 return classes;
352 };
353};
354
355var NoRenderer = function NoRenderer(props) {
356 return props.children || null;
357};
358/**
359 * HOC creator function that wrapps the user component.
360 *
361 * `withStyles(styles, [options])(Component)`
362 */
363
364
365var createWithStyles = function createWithStyles(styles, options) {
366 if (options === void 0) {
367 options = {};
368 }
369
370 var _options = options,
371 _options$index = _options.index,
372 index = _options$index === void 0 ? getSheetIndex() : _options$index,
373 theming = _options.theming,
374 injectTheme = _options.injectTheme,
375 sheetOptions = _objectWithoutPropertiesLoose(_options, ["index", "theming", "injectTheme"]);
376
377 var ThemeContext$1 = theming ? theming.context : ThemeContext;
378 return function (InnerComponent) {
379 if (InnerComponent === void 0) {
380 InnerComponent = NoRenderer;
381 }
382
383 var displayName = getDisplayName(InnerComponent);
384 var mergeClassesProp = memoize(function (sheetClasses, classesProp) {
385 return classesProp ? mergeClasses(sheetClasses, classesProp) : sheetClasses;
386 });
387 var hookOptions = Object.assign(sheetOptions, {
388 theming: theming,
389 index: index,
390 name: displayName
391 });
392 var useStyles = createUseStyles(styles, hookOptions);
393 var WithStyles = forwardRef(function (props, ref) {
394 var theme = useContext(ThemeContext$1);
395
396 var newProps = _extends({}, props);
397
398 if (injectTheme && newProps.theme == null) {
399 newProps.theme = theme;
400 }
401
402 var sheetClasses = useStyles(newProps);
403 var classes = mergeClassesProp(sheetClasses, props.classes);
404 return createElement(InnerComponent, _extends({}, newProps, {
405 classes: classes,
406 ref: ref
407 }));
408 });
409 WithStyles.displayName = "WithStyles(" + displayName + ")";
410 WithStyles.defaultProps = _extends({}, InnerComponent.defaultProps);
411 WithStyles.InnerComponent = InnerComponent;
412 return hoistNonReactStatics(WithStyles, InnerComponent);
413 };
414};
415
416var initialContext = {};
417function JssProvider(props) {
418 var managersRef = useRef({});
419 var prevContextRef = useRef();
420 var registryRef = useRef(null);
421
422 var createContext = function createContext(parentContext, prevContext) {
423 if (prevContext === void 0) {
424 prevContext = initialContext;
425 }
426
427 var registry = props.registry,
428 classNamePrefix = props.classNamePrefix,
429 jss = props.jss,
430 generateId = props.generateId,
431 disableStylesGeneration = props.disableStylesGeneration,
432 media = props.media,
433 id = props.id;
434
435 var context = _extends({}, parentContext);
436
437 if (registry) {
438 context.registry = registry; // This way we identify a new request on the server, because user will create
439 // a new Registry instance for each.
440
441 if (registry !== registryRef.current) {
442 // We reset managers because we have to regenerate all sheets for the new request.
443 managersRef.current = {};
444 registryRef.current = registry;
445 }
446 }
447
448 context.managers = managersRef.current;
449
450 if (id !== undefined) {
451 context.id = id;
452 }
453
454 if (generateId !== undefined) {
455 context.generateId = generateId;
456 } else if (!context.generateId || !prevContext || context.id !== prevContext.id) {
457 context.generateId = createGenerateId(context.id);
458 }
459
460 if (classNamePrefix) {
461 context.classNamePrefix = (context.classNamePrefix || '') + classNamePrefix;
462 }
463
464 if (media !== undefined) {
465 context.media = media;
466 }
467
468 if (jss) {
469 context.jss = jss;
470 }
471
472 if (disableStylesGeneration !== undefined) {
473 context.disableStylesGeneration = disableStylesGeneration;
474 }
475
476 if (prevContext && shallowEqualObjects(prevContext, context)) {
477 return prevContext;
478 }
479
480 return context;
481 };
482
483 var renderProvider = function renderProvider(parentContext) {
484 var children = props.children;
485 var context = createContext(parentContext, prevContextRef.current);
486 prevContextRef.current = context;
487 return createElement(JssContext.Provider, {
488 value: context
489 }, children);
490 };
491
492 return createElement(JssContext.Consumer, null, renderProvider);
493}
494
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 var _staticStyle = staticStyle,
510 _label = _staticStyle.label;
511
512 if (_label) {
513 if (labels.indexOf(_label) === -1) labels.push(_label);
514 }
515 }
516 }
517
518 var styles = {};
519 var label = labels.length === 0 ? 'sc' : labels.join('-');
520
521 if (staticStyle) {
522 // Label should not leak to the core.
523 if ('label' in staticStyle) delete staticStyle.label;
524 styles[label] = staticStyle;
525 } // When there is only one function rule, we don't need to wrap it.
526
527
528 if (dynamicStyles.length === 1) {
529 styles.scd = dynamicStyles[0];
530 } // We create a new function rule which will call all other function rules
531 // and merge the styles they return.
532
533
534 if (dynamicStyles.length > 1) {
535 styles.scd = function (props) {
536 var merged = {};
537
538 for (var i = 0; i < dynamicStyles.length; i++) {
539 var dynamicStyle = dynamicStyles[i](props);
540 if (dynamicStyle) Object.assign(merged, dynamicStyle);
541 }
542
543 return merged;
544 };
545 }
546
547 return {
548 styles: styles,
549 label: label
550 };
551};
552
553var shouldForwardPropSymbol = Symbol('react-jss-styled');
554
555var getShouldForwardProp = function getShouldForwardProp(tagOrComponent, options) {
556 var shouldForwardProp = options.shouldForwardProp;
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}; // eslint-disable-next-line no-unused-vars
595
596
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 tagOrComponent[shouldForwardPropSymbol] = shouldForwardProp;
634 }
635
636 if (isTag && as) {
637 return createElement(as, childProps);
638 }
639
640 return createElement(tagOrComponent, childProps);
641 };
642
643 return Styled;
644 };
645};
646
647/* eslint-disable prefer-rest-params, prefer-spread */
648var create = function create(css) {
649 if (css === void 0) {
650 css = defaultCss;
651 }
652
653 return function createElement$1(type, props) {
654 var args = arguments;
655
656 if (props && props.css) {
657 var className = css(props.css);
658 var newProps = Object.assign({}, props);
659 newProps.className = props.className ? props.className + " " + className : className;
660 delete newProps.css;
661 args[1] = newProps;
662 }
663
664 return createElement.apply(undefined, args);
665 };
666};
667var jsx = create();
668
669export default createWithStyles;
670export { JssContext, JssProvider, create as createJsx, createUseStyles, defaultJss as jss, jsx, configureStyled as styled, createWithStyles as withStyles };