Skip to content

Commit 52dab63

Browse files
authored
feat: nested media queries (#48)
1 parent ac89ce8 commit 52dab63

File tree

3 files changed

+153
-11
lines changed

3 files changed

+153
-11
lines changed

src/compiler/__tests__/compiler.test.tsx

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,3 +280,96 @@ test("light-dark()", () => {
280280
],
281281
});
282282
});
283+
284+
test("media query nested in rules", () => {
285+
const compiled = compile(`
286+
.my-class {
287+
color: red;
288+
@media (min-width: 600px) {
289+
color: blue;
290+
291+
@media (min-width: 400px) {
292+
background-color: green;
293+
}
294+
}
295+
296+
@media (min-width: 100px) {
297+
background-color: yellow;
298+
}
299+
}`);
300+
301+
expect(compiled).toStrictEqual({
302+
s: [
303+
[
304+
"my-class",
305+
[
306+
{
307+
d: [
308+
{
309+
color: "#f00",
310+
},
311+
],
312+
s: [1, 1],
313+
v: [["__rn-css-color", "#f00"]],
314+
},
315+
{
316+
d: [
317+
{
318+
color: "#00f",
319+
},
320+
],
321+
m: [[">=", "width", 600]],
322+
s: [2, 1],
323+
v: [["__rn-css-color", "#00f"]],
324+
},
325+
{
326+
d: [
327+
{
328+
backgroundColor: "#008000",
329+
},
330+
],
331+
m: [[">=", "width", 400]],
332+
s: [3, 1],
333+
},
334+
{
335+
d: [
336+
{
337+
backgroundColor: "#ff0",
338+
},
339+
],
340+
m: [[">=", "width", 100]],
341+
s: [4, 1],
342+
},
343+
{
344+
d: [
345+
{
346+
color: "#00f",
347+
},
348+
],
349+
m: [[">=", "width", 600]],
350+
s: [2, 1, 1],
351+
v: [["__rn-css-color", "#00f"]],
352+
},
353+
{
354+
d: [
355+
{
356+
backgroundColor: "#008000",
357+
},
358+
],
359+
m: [[">=", "width", 400]],
360+
s: [3, 1, 1],
361+
},
362+
{
363+
d: [
364+
{
365+
backgroundColor: "#ff0",
366+
},
367+
],
368+
m: [[">=", "width", 100]],
369+
s: [4, 1, 1],
370+
},
371+
],
372+
],
373+
],
374+
});
375+
});

src/compiler/compiler.ts

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -132,35 +132,67 @@ function extractRule(rule: Rule, builder: StylesheetBuilder) {
132132
extractMedia(rule.value, builder);
133133
break;
134134
}
135+
case "nested-declarations": {
136+
const value = rule.value;
137+
138+
const declarationBlock = value.declarations;
139+
if (declarationBlock) {
140+
if (declarationBlock.declarations) {
141+
builder.newRuleFork();
142+
for (const declaration of declarationBlock.declarations) {
143+
parseDeclaration(declaration, builder);
144+
}
145+
builder.applyRuleToSelectors();
146+
}
147+
148+
if (declarationBlock.importantDeclarations) {
149+
builder.newRuleFork({ important: true });
150+
for (const declaration of declarationBlock.importantDeclarations) {
151+
parseDeclaration(declaration, builder);
152+
}
153+
builder.applyRuleToSelectors();
154+
}
155+
}
156+
break;
157+
}
135158
case "style": {
136-
// If the rule is a style declaration, extract it with the `getExtractedStyle` function and store it in the `declarations` map
137-
builder = builder.fork("style");
159+
const value = rule.value;
138160

139-
const declarationBlock = rule.value.declarations;
140-
const mapping = parsePropAtRule(rule.value.rules);
161+
const declarationBlock = value.declarations;
162+
const mapping = parsePropAtRule(value.rules);
141163
const selectors = getSelectors(
142-
rule.value.selectors,
164+
value.selectors,
143165
false,
144166
builder.getOptions(),
145167
);
146168

169+
// If the rule is a style declaration, extract it with the `getExtractedStyle` function and store it in the `declarations` map
170+
builder = builder.fork("style", selectors);
171+
147172
if (declarationBlock) {
148173
if (declarationBlock.declarations) {
149174
builder.newRule(mapping);
150175
for (const declaration of declarationBlock.declarations) {
151176
parseDeclaration(declaration, builder);
152177
}
153-
builder.applyRuleToSelectors(selectors);
178+
builder.applyRuleToSelectors();
154179
}
155180

156181
if (declarationBlock.importantDeclarations) {
157182
builder.newRule(mapping, { important: true });
158183
for (const declaration of declarationBlock.importantDeclarations) {
159184
parseDeclaration(declaration, builder);
160185
}
161-
builder.applyRuleToSelectors(selectors);
186+
builder.applyRuleToSelectors();
162187
}
163188
}
189+
190+
if (value.rules) {
191+
for (const nestedRule of value.rules) {
192+
extractRule(nestedRule, builder);
193+
}
194+
}
195+
164196
break;
165197
}
166198
case "layer-block":
@@ -184,7 +216,6 @@ function extractRule(rule: Rule, builder: StylesheetBuilder) {
184216
case "counter-style":
185217
case "moz-document":
186218
case "nesting":
187-
case "nested-declarations":
188219
case "viewport":
189220
case "custom-media":
190221
case "scope":

src/compiler/stylesheet.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,17 +57,22 @@ export class StylesheetBuilder {
5757
rem: number;
5858
ruleOrder: number;
5959
} = { ruleSets: {}, rem: 14, ruleOrder: 0 },
60+
private selectors?: NormalizeSelector[],
6061
) {}
6162

62-
fork(mode = this.mode, rule?: Partial<StyleRule>): StylesheetBuilder {
63+
fork(
64+
mode = this.mode,
65+
selectors: NormalizeSelector[] | undefined = this.selectors,
66+
): StylesheetBuilder {
6367
this.shared.ruleOrder++;
6468
return new StylesheetBuilder(
6569
this.options,
6670
mode,
67-
this.cloneRule(rule ? { ...this.rule, ...rule } : undefined),
71+
this.cloneRule(),
6872
{ ...this.mapping },
6973
this.descriptorProperty,
7074
this.shared,
75+
selectors,
7176
);
7277
}
7378

@@ -166,6 +171,14 @@ export class StylesheetBuilder {
166171
}
167172
}
168173

174+
newRuleFork({ important = false } = {}) {
175+
this.rule = this.cloneRule(this.rule);
176+
this.rule.s[Specificity.Order] = this.shared.ruleOrder;
177+
if (important) {
178+
this.rule.s[Specificity.Important] = 1;
179+
}
180+
}
181+
169182
addExtraRule(rule: Partial<StyleRule>) {
170183
let extraRuleArray = extraRules.get(this.rule);
171184
if (!extraRuleArray) {
@@ -331,7 +344,12 @@ export class StylesheetBuilder {
331344
}
332345
}
333346

334-
applyRuleToSelectors(selectorList: NormalizeSelector[]): void {
347+
applyRuleToSelectors(selectorList = this.selectors): void {
348+
if (!selectorList?.length) {
349+
// If there are no selectors, we cannot apply the rule
350+
return;
351+
}
352+
335353
if (!this.rule.d && !this.rule.v) {
336354
return;
337355
}

0 commit comments

Comments
 (0)