Implement nested media queries
Including double-nested queries which are used (not only) by bootstrap3. Triple-nested (or more) media queries are currently not supported. Added several tests. See http://www.lesscss.org/#-nested-media-queries
This commit is contained in:
parent
bba63d9df5
commit
3b74578cc9
@ -63,6 +63,14 @@ class Scope(list):
|
|||||||
self[-1]['__blocks__'].append(block)
|
self[-1]['__blocks__'].append(block)
|
||||||
self[-1]['__names__'].append(block.raw())
|
self[-1]['__names__'].append(block.raw())
|
||||||
|
|
||||||
|
def remove_block(self, block, index="-1"):
|
||||||
|
"""Remove block element from scope
|
||||||
|
Args:
|
||||||
|
block (Block): Block object
|
||||||
|
"""
|
||||||
|
self[index]["__blocks__"].remove(block)
|
||||||
|
self[index]["__names__"].remove(block.raw())
|
||||||
|
|
||||||
def add_mixin(self, mixin):
|
def add_mixin(self, mixin):
|
||||||
"""Add mixin to scope
|
"""Add mixin to scope
|
||||||
Args:
|
Args:
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
import re
|
import re
|
||||||
from .node import Node
|
from .node import Node
|
||||||
from lesscpy.lessc import utility
|
from lesscpy.lessc import utility
|
||||||
|
from lesscpy.plib.identifier import Identifier
|
||||||
|
|
||||||
|
|
||||||
class Block(Node):
|
class Block(Node):
|
||||||
@ -41,8 +42,77 @@ class Block(Node):
|
|||||||
if not inner:
|
if not inner:
|
||||||
inner = []
|
inner = []
|
||||||
inner = list(utility.flatten([p.parse(scope) for p in inner if p]))
|
inner = list(utility.flatten([p.parse(scope) for p in inner if p]))
|
||||||
self.parsed = [p for p in inner if p and not isinstance(p, Block)]
|
self.parsed = []
|
||||||
self.inner = [p for p in inner if p and isinstance(p, Block)]
|
self.inner = []
|
||||||
|
if not hasattr(self, "inner_media_queries"):
|
||||||
|
self.inner_media_queries = []
|
||||||
|
for p in inner:
|
||||||
|
if p is not None:
|
||||||
|
if isinstance(p, Block):
|
||||||
|
if p.name.tokens[0] == '@media' and not self.name.raw().startswith("@media"):
|
||||||
|
# Inner block @media ... { ... } is a nested media
|
||||||
|
# query. But double-nested media queries have to be
|
||||||
|
# removed and marked as well. While parsing ".foo",
|
||||||
|
# both nested "@media print" and double-nested
|
||||||
|
# "@media all" will be handled as we have to
|
||||||
|
# re-arrange the scope and block layout quite a bit:
|
||||||
|
#
|
||||||
|
# .foo {
|
||||||
|
# @media print {
|
||||||
|
# color: blue;
|
||||||
|
# @media screen { font-size: 12em; }
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# Expected result:
|
||||||
|
#
|
||||||
|
# @media print {
|
||||||
|
# .foo { color: blue; }
|
||||||
|
# }
|
||||||
|
# @media print and screen {
|
||||||
|
# .foo { font-size: 12 em; }
|
||||||
|
# }
|
||||||
|
append_list = []
|
||||||
|
reparse_p = False
|
||||||
|
for child in p.tokens[1]:
|
||||||
|
if isinstance(child, Block) and child.name.raw().startswith("@media"):
|
||||||
|
# Double-nested media query found. We remove it from 'p' and add
|
||||||
|
# it to this block with a new 'name'.
|
||||||
|
part_a = p.name.tokens[2:][0][0][0]
|
||||||
|
part_b = child.name.tokens[2:][0][0]
|
||||||
|
new_ident_tokens = ['@media', ' ', [part_a, (' ', 'and', ' '), part_b]]
|
||||||
|
# Remove child from the nested media query, it will be re-added to
|
||||||
|
# the parent with 'merged' media query (see above example).
|
||||||
|
p.tokens[1].remove(child)
|
||||||
|
reparse_p = True
|
||||||
|
# Parse child again with new @media $BLA {} part
|
||||||
|
child.tokens[0] = Identifier(new_ident_tokens)
|
||||||
|
child.parsed = None
|
||||||
|
child = child.parse(scope)
|
||||||
|
append_list.append(child)
|
||||||
|
if reparse_p:
|
||||||
|
p.parsed = None
|
||||||
|
p = p.parse(scope)
|
||||||
|
append_list.insert(0, p) # This media query should occur before it's children
|
||||||
|
for media_query in append_list:
|
||||||
|
self.inner_media_queries.append(media_query)
|
||||||
|
# NOTE(saschpe): The code is not recursive but we hope that people
|
||||||
|
# wont use triple-nested media queries.
|
||||||
|
else:
|
||||||
|
self.inner.append(p)
|
||||||
|
else:
|
||||||
|
self.parsed.append(p)
|
||||||
|
if len(self.inner_media_queries) > 0:
|
||||||
|
# Nested media queries, we have to remove self from scope and
|
||||||
|
# push all nested @media ... {} blocks.
|
||||||
|
scope.remove_block(self, index=-2)
|
||||||
|
for mb in self.inner_media_queries:
|
||||||
|
# New inner block with current name and media block contents
|
||||||
|
cb = Block([self.tokens[0], mb.tokens[1]]).parse(scope)
|
||||||
|
# Replace inner block contents with new block
|
||||||
|
new_mb = Block([mb.tokens[0], [cb]]).parse(scope)
|
||||||
|
self.inner.append(new_mb)
|
||||||
|
scope.add_block(new_mb)
|
||||||
scope.real.pop()
|
scope.real.pop()
|
||||||
scope.pop()
|
scope.pop()
|
||||||
return self
|
return self
|
||||||
|
@ -115,6 +115,8 @@ class Identifier(Node):
|
|||||||
parsed.extend(parent)
|
parsed.extend(parent)
|
||||||
else:
|
else:
|
||||||
parsed.append(n)
|
parsed.append(n)
|
||||||
|
elif name[0] == "@media":
|
||||||
|
parsed.extend(name)
|
||||||
else:
|
else:
|
||||||
parsed.extend(parent)
|
parsed.extend(parent)
|
||||||
if parent[-1] != ' ':
|
if parent[-1] != ' ':
|
||||||
|
@ -52,3 +52,31 @@
|
|||||||
@media only screen and (min--moz-device-pixel-ratio:2.5),only screen and (-o-min-device-pixel-ratio:2),only screen and (min-device-pixel-ratio:2),only screen and (min-resolution:192dpi),only screen and (min-resolution:2dppx) {
|
@media only screen and (min--moz-device-pixel-ratio:2.5),only screen and (-o-min-device-pixel-ratio:2),only screen and (min-device-pixel-ratio:2),only screen and (min-resolution:192dpi),only screen and (min-resolution:2dppx) {
|
||||||
background-size: 10px;
|
background-size: 10px;
|
||||||
}
|
}
|
||||||
|
@media (width:768px) {
|
||||||
|
.lead {
|
||||||
|
font-size: 21px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (width:400px) {
|
||||||
|
.one {
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (width:400px) and print and (color) {
|
||||||
|
.one {
|
||||||
|
color: blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.two {
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
@media (width:400px) {
|
||||||
|
.two {
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media print and (color) {
|
||||||
|
.two {
|
||||||
|
color: blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
6
lesscpy/test/css/media.min.css
vendored
6
lesscpy/test/css/media.min.css
vendored
@ -10,3 +10,9 @@ body{max-width:35em;margin:0 auto;}}
|
|||||||
@media (min-width:12px){body{margin:0 auto;}}
|
@media (min-width:12px){body{margin:0 auto;}}
|
||||||
@media (width:767px){.visible-xs{display:block;}}
|
@media (width:767px){.visible-xs{display:block;}}
|
||||||
@media only screen and (min--moz-device-pixel-ratio:2.5),only screen and (-o-min-device-pixel-ratio:2),only screen and (min-device-pixel-ratio:2),only screen and (min-resolution:192dpi),only screen and (min-resolution:2dppx){background-size:10px;}
|
@media only screen and (min--moz-device-pixel-ratio:2.5),only screen and (-o-min-device-pixel-ratio:2),only screen and (min-device-pixel-ratio:2),only screen and (min-resolution:192dpi),only screen and (min-resolution:2dppx){background-size:10px;}
|
||||||
|
@media (width:768px){.lead{font-size:21px;}}
|
||||||
|
@media (width:400px){.one{font-size:1.2em;}}
|
||||||
|
@media (width:400px) and print and (color){.one{color:blue;}}
|
||||||
|
.two{width:100px;}
|
||||||
|
@media (width:400px){.two{font-size:1.2em;}}
|
||||||
|
@media print and (color){.two{color:blue;}}
|
||||||
|
@ -70,3 +70,28 @@
|
|||||||
only screen and ( min-resolution: 2dppx) {
|
only screen and ( min-resolution: 2dppx) {
|
||||||
background-size: 10px;
|
background-size: 10px;
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
Nested media queries
|
||||||
|
*/
|
||||||
|
.lead {
|
||||||
|
@media (width: 768px) {
|
||||||
|
font-size: 21px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.one {
|
||||||
|
@media (width: 400px){
|
||||||
|
font-size: 1.2em;
|
||||||
|
@media print and (color) {
|
||||||
|
color: blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.two {
|
||||||
|
@media (width: 400px){
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
@media print and (color) {
|
||||||
|
color: blue;
|
||||||
|
}
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user