diff --git a/packages/jss-plugin-extend/src/jss-plugin-extend.tsx b/packages/jss-plugin-extend/src/jss-plugin-extend.tsx new file mode 100644 index 000000000..596562a32 --- /dev/null +++ b/packages/jss-plugin-extend/src/jss-plugin-extend.tsx @@ -0,0 +1,172 @@ +// jss (builds css using javascript): +import type { + Plugin, + JssStyle, + + Rule, + StyleSheet, +} from 'jss' // base technology of our nodestrap components + +// others: +import warning from 'tiny-warning' + + + +// utilities: +type LiteralObject = { [key: string]: any } +const isLiteralObject = (object: any): object is LiteralObject => object && (typeof(object) === 'object') && !Array.isArray(object); + +// upgrade `JssStyle` definition: +type Optional = T|null|undefined +type ExtendableObject = JssStyle|string // extend using a JssStyle object or using a rule name +type SingleExtend = Optional +type Extend = SingleExtend|SingleExtend[] +type Style = JssStyle & { extend?: Optional } // add `extend` prop into `JssStyle` +// export the upgraded `JssStyle`: +export type { Style, Style as ExtendableStyle, Style as JssStyle } +const isStyle = (object: any): object is Style => isLiteralObject(object); + + + +const mergeExtend = (style: Style, rule?: Rule, sheet?: StyleSheet): void => { + const extend = style.extend; + if (!extend) return; // nothing to extend + + + + // if `extend` is an `Array` => loop it + // otherwise => convert to single `Array` => loop it + for (const singleExtend of (Array.isArray(extend) ? extend : [extend])) { + if (!singleExtend) continue; // null & undefined => skip + + + + //#region extend using a `Style` + if (isStyle(singleExtend)) { + mergeStyle(style, singleExtend, rule, sheet); + } // if + //#endregion extend using a `Style` + + + + //#region extend using a rule name + else if (typeof(singleExtend) === 'string') { + if (sheet) { + const refRule = sheet.getRule(singleExtend) as Optional; + if (refRule) { + if (refRule === rule) { + warning(false, `[JSS] A rule tries to extend itself \n${rule.toString()}`); + + // TODO: detect circular ref, causing infinite recursive + } + else { + // now it seems the `refRule` is not `rule` nor circular ref + // warning: calling `mergeStyle` might causing infinite recursive if the `refRule` is `rule` or circular ref + + const ruleStyle = (refRule.options?.parent as any)?.rules?.raw?.[singleExtend] as Optional