Skip to content

Commit

Permalink
feat(hiccup-css): add package @thi.ng/hiccup-css
Browse files Browse the repository at this point in the history
  • Loading branch information
postspectacular committed Mar 3, 2018
1 parent 92fb7b0 commit 3a4cf1e
Show file tree
Hide file tree
Showing 7 changed files with 267 additions and 0 deletions.
3 changes: 3 additions & 0 deletions packages/hiccup-css/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
yarn.lock
*.js
48 changes: 48 additions & 0 deletions packages/hiccup-css/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# @thi.ng/hiccup-css

[![npm (scoped)](https://img.shields.io/npm/v/@thi.ng/hiccup-css.svg)](https://www.npmjs.com/package/@thi.ng/hiccup-css)

## About

CSS as nested data structures.

## Installation

```
yarn add @thi.ng/hiccup-css
```

## Usage examples

```typescript
import { css, FORMATS } from "@thi.ng/hiccup-css";

css(["#foo",
{ background: "#eee" },
["h1", { "font-size": "2em" }, ["small", { color: "#999"}]],
["h2", "h3", [".title", { "font-weight": 700 }]]],
FORMATS.pretty);
```

```css
#foo h1 small {
color:#999;
}
#foo h1 {
font-size:2em;
}
#foo h2 .title, #foo h3 .title {
font-weight:700;
}
#foo {
background:#eee;
}
```

## Authors

- Karsten Schmidt

## License

© 2018 Karsten Schmidt // Apache Software License 2.0
49 changes: 49 additions & 0 deletions packages/hiccup-css/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"name": "@thi.ng/hiccup-css",
"version": "0.0.1",
"description": "CSS as nested data structures",
"main": "./index.js",
"typings": "./index.d.ts",
"repository": "https://github.com/thi-ng/umbrella",
"author": "Karsten Schmidt <k+npm@thi.ng>",
"license": "Apache-2.0",
"scripts": {
"build": "yarn clean && tsc --declaration",
"clean": "rm -rf *.js *.d.ts .nyc_output build coverage doc",
"cover": "yarn test && nyc report --reporter=lcov",
"doc": "node_modules/.bin/typedoc --mode modules --out doc src",
"pub": "yarn build && yarn publish --access public",
"test": "rm -rf build && tsc -p test && nyc mocha build/test/*.js"
},
"devDependencies": {
"@types/mocha": "^2.2.48",
"@types/node": "^9.4.6",
"mocha": "^5.0.0",
"nyc": "^11.4.1",
"ts-loader": "^3.5.0",
"typedoc": "^0.10.0",
"typescript": "^2.7.2",
"webpack": "^3.11.0"
},
"dependencies": {
"@thi.ng/api": "^2.0.3",
"@thi.ng/transducers": "^1.6.1"
},
"keywords": [
"clojure",
"components",
"ES6",
"hiccup",
"html",
"iterators",
"json",
"serialization",
"svg",
"template",
"typescript",
"xml"
],
"publishConfig": {
"access": "public"
}
}
82 changes: 82 additions & 0 deletions packages/hiccup-css/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { IObjectOf } from "@thi.ng/api/api";
import { isArray } from "@thi.ng/checks/is-array";
import { isPlainObject } from "@thi.ng/checks/is-plain-object";
import { transduce } from "@thi.ng/transducers/transduce";
import { permutations } from "@thi.ng/transducers/iter/permutations";
import { str } from "@thi.ng/transducers/rfn/str";
import { flatten } from "@thi.ng/transducers/xform/flatten";
import { map } from "@thi.ng/transducers/xform/map";

export interface Format {
rules: string;
ruleSep: string;
decls: string;
declsStart: string;
declEnd: string;
indent: string;
}

const NO_SPACES = ":[";

export const FORMATS: IObjectOf<Format> = {
min: { rules: "", ruleSep: ",", decls: "", declsStart: "{", declEnd: "}", indent: "" },
pretty: { rules: "\n", ruleSep: ", ", decls: "\n", declsStart: " {\n", declEnd: "\n}", indent: " " },
};

const xfSel = ((a, b) => (x) => a(b(x)))(
flatten(),
map((x: string) => NO_SPACES.indexOf(x.charAt(0)) >= 0 ? x : " " + x)
);

export function css(rules: any, fmt = FORMATS.min) {
if (isArray(rules)) {
return _css([], [], rules, fmt).join(fmt.rules);
}
if (isPlainObject(rules)) {
return format(rules, fmt);
}
}

function _css(acc: string[], parent: any[], rules: any[], fmt: Format) {
const n = rules.length;
const sel: string[] = [];
let curr: any;
for (let i = 0; i < n; i++) {
const r = rules[i];
if (isArray(r)) {
_css(acc, makeSelector(parent, sel), r, fmt);
} else if (isPlainObject(r)) {
curr = Object.assign(curr || {}, r);
} else {
sel.push(r);
}
}
if (curr) {
acc.push(formatRule(parent, sel, curr, fmt));
}
return acc;
}

function format(rules: any, fmt: Format) {
const acc = [];
for (let r in rules) {
if (rules.hasOwnProperty(r)) {
acc.push(`${fmt.indent}${r}:${rules[r]};`);
}
}
return acc.join(fmt.decls);
}

function makeSelector(parent: any[], curr: any[]) {
return parent.length ? [...permutations(parent, curr)] : curr;
}

function formatRule(parent: any[], sel: any[], curr: any, fmt: Format) {
return transduce(
map((sel: any[]) => transduce(xfSel, str(), isArray(sel) ? sel : [sel]).trim()),
str(fmt.ruleSep),
makeSelector(parent, sel))
+ fmt.declsStart
+ format(curr, fmt)
+ fmt.declEnd;
}
66 changes: 66 additions & 0 deletions packages/hiccup-css/test/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import * as assert from "assert";

import { css, FORMATS } from "../src";

const rules = {
a: { color: "red" },
b: { border: 0 },
};

describe("hiccup-css", () => {

it("rules only", () => {
assert.equal(css("a"), undefined);
assert.equal(css({}), "");
assert.equal(css(rules.a), "color:red;");
});

it("simple", () => {
assert.equal(css(["a"]), "");
assert.equal(css(["a", rules.a]), "a{color:red;}");
assert.equal(
css([["a", rules.a], ["b", rules.b]]),
"a{color:red;}b{border:0;}"
);
assert.equal(
css(["a", "b", rules.a, rules.b]),
"a,b{color:red;border:0;}"
);
});

it("nested", () => {
assert.equal(
css(["a", [":link", rules.a], [":visited", rules.b]]),
"a:link{color:red;}a:visited{border:0;}"
);
assert.equal(
css(["p", ["a", [":link", rules.a], [":visited", rules.b]]]),
"p a:link{color:red;}p a:visited{border:0;}"
);
assert.equal(
css(
["#id",
["h1", {}, {}],
["h2", "h3",
["div", {}],
["[attr]",
["span", rules.a]]]]
),
"#id h1{}#id h2 div,#id h3 div{}#id h2[attr] span,#id h3[attr] span{color:red;}"
);
});

it("pretty", () => {
assert.equal(
css(
["#id",
["h1", {}, {}],
["h2", "h3",
["div", {}],
["[attr]",
["span", rules.a]]]],
FORMATS.pretty),
"#id h1 {\n\n}\n#id h2 div, #id h3 div {\n\n}\n#id h2[attr] span, #id h3[attr] span {\n color:red;\n}"
);
});
});
10 changes: 10 additions & 0 deletions packages/hiccup-css/test/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"outDir": "../build"
},
"include": [
"./**/*.ts",
"../src/**/*.ts"
]
}
9 changes: 9 additions & 0 deletions packages/hiccup-css/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "."
},
"include": [
"./src/**/*.ts"
]
}

0 comments on commit 3a4cf1e

Please sign in to comment.