Skip to content

Commit

Permalink
inherit @media
Browse files Browse the repository at this point in the history
inherit @media

docs

0.0.2
  • Loading branch information
jonathanong committed Mar 15, 2013
1 parent f686f36 commit e6b1927
Show file tree
Hide file tree
Showing 12 changed files with 402 additions and 32 deletions.
155 changes: 153 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
## Inherit

Inherit mixin for [rework](https://github.com/visionmedia/rework).
Like the extend mixin, but does more.
Like the extend mixin, but does so much more.
If you inherit a selector,
it will inherit __all__ rules associated with that selector.

### API

Expand All @@ -11,9 +13,158 @@ Like the extend mixin, but does more.
.use(inherit())
.toString()

### Examples

#### Regular inherit

.gray {
color: gray;
}

.text {
inherit: .gray;
}

yields:

.gray,
.text {
color: gray;
}

#### Multiple inherit

Inherit multiple selectors at the same time.

.gray {
color: gray;
}

.black {
color: black;
}

.button {
inherit: .gray, .black;
}

yields:

.gray,
.button {
color: gray;
}

.black,
.button {
color: black;
}

#### Placeholders

Any selector that includes a `%` is considered a placeholder.
Placeholders will not be output in the final CSS.

%gray {
color: gray;
}

.text {
inherit: %gray;
}

yields:

.text {
color: gray;
}

#### Partial selectors

If you inherit a selector,
all rules that include that selector will be included as well.

div button span {
color: red;
}

div button {
color: green;
}

button span {
color: pink;
}

.button {
inherit: button;
}

.link {
inherit: div button;
}

yields:

div button span,
div .button span,
.link span {
color: red;
}

div button,
div .button,
.link {
color: green;
}

button span,
.button span {
color: pink;
}

#### Media Queries

Inheriting from inside a media query will create a copy of the declarations.
It will act like a "mixin".
Thus, with `%`placeholders, you won't have to use mixins at all.
Each type of media query will need its own declaration,
so there will be some inevitable repetition.

.gray {
color: gray
}

@media (min-width: 320px) {
.button {
inherit: .gray;
}
}

@media (min-width: 320px) {
.link {
inherit: .gray;
}
}

yields:

.gray {
color: gray;
}

@media (min-width: 320px) {
.button,
.link {
color: gray;
}
}

### Limitations

- You can not inherit something from inside a media query
- You can not inherit a rule that is inside a media query.
In other words, rules must be defined outside media queries.
This wouldn't make sense anyways; how would you type it?

### License

Expand Down
178 changes: 152 additions & 26 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,36 +1,141 @@
module.exports = function () {
return function (style) {
var rules = style.rules
var util = require('util')

findRules(rules)
removePlaceholders(rules)
exports = module.exports = function () {
return Inherit
}

exports.Inherit = Inherit

function Inherit(style) {
if (!(this instanceof Inherit)) return new Inherit(style);

var rules = this.rules = style.rules
this.matches = {}

for (var i = 0; i < rules.length; i++) {
var rule = rules[i]
if (rule.media) {
this.inheritMedia(rule)
if (!rule.rules.length) rules.splice(i--, 1);
} else if (rule.selectors) {
this.inheritRules(rule)
if (!rule.declarations.length) rules.splice(i--, 1);
}
}

this.removePlaceholders()
}

function findRules(rules) {
Inherit.prototype.inheritMedia = function (mediaRule) {
var rules = mediaRule.rules
var query = mediaRule.media

for (var i = 0; i < rules.length; i++) {
var rule = rules[i]
var selectors = rule.selectors
if (!selectors) continue;
if (!rule.selectors) continue;

var declarations = rule.declarations
var additionalRules = this.inheritMediaRules(rule, query)

for (var j = 0; j < declarations.length; j++) {
var decl = declarations[j]
var key = decl.property
var val = decl.value
if (!/^inherits?$/.test(key)) continue;
if (!rule.declarations.length) rules.splice(i--, 1);

inheritRules(rules, val, selectors)
declarations.splice(j--, 1)
var len = additionalRules.length
if (len) {
;[].splice.apply(rules, [i, 0].concat(additionalRules))
i += len
}
}
}

Inherit.prototype.inheritMediaRules = function (rule, query) {
var declarations = rule.declarations
var appendRules = []

if (!declarations.length) rules.splice(i--, 1);
for (var i = 0; i < declarations.length; i++) {
var decl = declarations[i]
var key = decl.property
if (!/^inherits?$/.test(key)) continue;

decl.value.split(',').map(trim).forEach(function (val) {
var rules = this.inheritMediaRule(val, rule.selectors, query)
if (rules) [].push.apply(appendRules, rules);
}, this)

declarations.splice(i--, 1)
}

return appendRules
}

function inheritRules(rules, val, selectors) {
rules.forEach(function (rule) {
Inherit.prototype.inheritMediaRule = function (val, selectors, query) {
var matchedRules = this.matches[val] || this.matchRules(val)
var alreadyMatched = matchedRules.media[query]
var matchedQueryRules = alreadyMatched || this.matchQueryRule(val, query)

if (!matchedQueryRules.rules.length) {
throw new Error('Failed to extend as media query from ' + val + '.')
}

this.appendSelectors(matchedQueryRules, val, selectors)

if (!alreadyMatched) {
// If not already matched, return rules to insert
return matchedQueryRules.rules.map(function (rule) {
return rule.rule
})
}
}

Inherit.prototype.inheritRules = function (rule) {
var declarations = rule.declarations

for (var i = 0; i < declarations.length; i++) {
var decl = declarations[i]
var key = decl.property
if (!/^inherits?$/.test(key)) continue;

decl.value.split(',').map(trim).forEach(function (val) {
this.inheritRule(val, rule.selectors)
}, this)

declarations.splice(i--, 1)
}
}

Inherit.prototype.inheritRule = function (val, selectors) {
var matchedRules = this.matches[val] || this.matchRules(val)

if (!matchedRules.rules.length) {
throw new Error('Failed to extend from ' + val + '.')
}

this.appendSelectors(matchedRules, val, selectors)
}

Inherit.prototype.matchQueryRule = function (val, query) {
var matchedRules = this.matches[val] || this.matchRules(val)

return matchedRules.media[query] = {
media: query,
rules: matchedRules.rules.map(function (rule) {
return {
selectors: rule.selectors,
declarations: rule.declarations,
rule: {
selectors: [],
declarations: rule.declarations
}
}
})
}
}

Inherit.prototype.matchRules = function (val) {
var matchedRules = this.matches[val] = {
rules: [],
media: {}
}

this.rules.forEach(function (rule) {
if (!rule.selectors) return;

var matchedSelectors = rule.selectors.filter(function (selector) {
Expand All @@ -39,18 +144,35 @@ function inheritRules(rules, val, selectors) {

if (!matchedSelectors.length) return;

selectors.forEach(function (selector) {
matchedSelectors.forEach(function (matchedSelector) {
rule.selectors.push(replaceSelector(matchedSelector, val, selector))
})
matchedRules.rules.push({
selectors: matchedSelectors,
declarations: rule.declarations,
rule: rule
})
})

return matchedRules
}

function removePlaceholders(rules) {
Inherit.prototype.appendSelectors = function (matchedRules, val, selectors) {
matchedRules.rules.forEach(function (matchedRule) {
// Selector to actually inherit
var selectorReference = matchedRule.rule.selectors

matchedRule.selectors.forEach(function (matchedSelector) {
;[].push.apply(selectorReference, selectors.map(function (selector) {
return replaceSelector(matchedSelector, val, selector)
}))
})
})
}

// Placeholders are not allowed in media queries
Inherit.prototype.removePlaceholders = function (rules) {
rules = rules || this.rules

for (var i = 0; i < rules.length; i++) {
var rule = rules[i]
var selectors = rule.selectors
var selectors = rules[i].selectors
if (!selectors) continue;

for (var j = 0; j < selectors.length; j++) {
Expand All @@ -68,4 +190,8 @@ function replaceSelector(matchedSelector, val, selector) {

function escapeRegExp(str) {
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&")
}

function trim(x) {
return x.trim()
}
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"name": "rework-inherit",
"description": "Inherit rules from other selectors",
"version": "0.0.1",
"version": "0.0.2",
"peerDependencies": {
"rework": "*"
}
}
}
Loading

0 comments on commit e6b1927

Please sign in to comment.