Compare commits
82 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
dffd83078b | |
|
|
787bb390c0 | |
|
|
54077cb530 | |
|
|
f0c6bd2fa5 | |
|
|
1b69cf9a17 | |
|
|
162b1c7a13 | |
|
|
f415b9c9e0 | |
|
|
c3173271a2 | |
|
|
768f4bd750 | |
|
|
d40fd27a94 | |
|
|
df4c69ecc1 | |
|
|
673410119f | |
|
|
f6366ab07c | |
|
|
ccff4647da | |
|
|
114d7d72f2 | |
|
|
e97a38c442 | |
|
|
bc6edb5951 | |
|
|
b7d3405708 | |
|
|
772a561b9e | |
|
|
21fa24cd3e | |
|
|
57fc20d331 | |
|
|
7978410f4a | |
|
|
1c3d4b8905 | |
|
|
17f7309463 | |
|
|
e6cbd41e3a | |
|
|
932bcfadb4 | |
|
|
1cc4ed63a9 | |
|
|
db9aa9ffd3 | |
|
|
dfc6fdd932 | |
|
|
6c783dff4b | |
|
|
5b72fd34d3 | |
|
|
902a8788cd | |
|
|
792397c033 | |
|
|
1a7a383944 | |
|
|
1783e6572d | |
|
|
277ca2b1a8 | |
|
|
a548f2f907 | |
|
|
fb9e96dcd9 | |
|
|
7393101214 | |
|
|
dd38c4dd45 | |
|
|
0ae460d6b7 | |
|
|
2e206c48e7 | |
|
|
27a8d2c8be | |
|
|
704eebccf9 | |
|
|
166b53546d | |
|
|
d2e1144c39 | |
|
|
5bab81b8cc | |
|
|
a31a652fbe | |
|
|
9b72bf9b30 | |
|
|
c01f3b5d29 | |
|
|
a5c16bc862 | |
|
|
82449124ed | |
|
|
ca11666be3 | |
|
|
61278b715d | |
|
|
007f705615 | |
|
|
3831fd24c4 | |
|
|
a31e596bc0 | |
|
|
6293f3f5d9 | |
|
|
c7ac4e4372 | |
|
|
b038d3452e | |
|
|
60fcc89df1 | |
|
|
37e3023cad | |
|
|
b9edb539b4 | |
|
|
ff888cbb0d | |
|
|
a51db4e906 | |
|
|
5643b53a3d | |
|
|
8269704dc9 | |
|
|
ecdbda2e0d | |
|
|
c93d227daa | |
|
|
5696cf112d | |
|
|
e5b13f1a0f | |
|
|
be9fb9a3bd | |
|
|
8ce6786020 | |
|
|
4ff1271086 | |
|
|
1ba7ddbdc9 | |
|
|
0ca2f12646 | |
|
|
62ecf66d5d | |
|
|
b3eb97a556 | |
|
|
3500881a62 | |
|
|
591ca51c3e | |
|
|
68f7ace62c | |
|
|
c0d3668e16 |
361
CHANGELOG.md
361
CHANGELOG.md
|
|
@ -1,3 +1,361 @@
|
|||
## 0.34.1
|
||||
|
||||
* Fix gap property break and remove redundant styles.
|
||||
|
||||
<br /> <br />
|
||||
|
||||
|
||||
|
||||
## 0.34.0
|
||||
|
||||
### Breaking
|
||||
|
||||
* The css-doodle element now is by default restrict the visible area to its dimension,
|
||||
which means it's overflow hidden. There are two ways to change this behavior:
|
||||
|
||||
1. Append the <code>noclip</code> keyword to the `@grid` property:
|
||||
|
||||
```css
|
||||
@grid: 1 / 400px noclip;
|
||||
```
|
||||
|
||||
2. Overide the `contain` property:
|
||||
|
||||
```css
|
||||
:doodle {
|
||||
@grid: 1 / 400px;
|
||||
contain: none;
|
||||
}
|
||||
```
|
||||
|
||||
### Features
|
||||
|
||||
* Extended `@grid` to support doodle background color, after the second `/` symbol.
|
||||
|
||||
```css
|
||||
@grid: 1 / 400px / #000;
|
||||
```
|
||||
|
||||
* Extended `@grid` and `@size` to support <code>aspect-ratio</code> if one of the `width` and `width` is `auto`.
|
||||
|
||||
```css
|
||||
@grid: 1 / 400px auto 2;
|
||||
@grid: 1 / 400px auto (4/3);
|
||||
|
||||
@size: 100px auto 1.5;
|
||||
```
|
||||
|
||||
* Extended `@grid` to support <code>rotate</code> and <code>scale</code> to <code>:container</code>.
|
||||
|
||||
```css
|
||||
@grid: 1 / 400px + 2; /* + means scale */
|
||||
@grid: 1 / 400px * 45deg; /* * means scale */
|
||||
|
||||
@grid: 1 / 400px + 2 * 45deg;
|
||||
```
|
||||
|
||||
* Extended `@grid` to change the layout to `Flex` layout.
|
||||
|
||||
```css
|
||||
@grid: - 8x2 / 400px; /* - means using flex */
|
||||
@grid: | 8x2 / 400px; /* | means using flex and vertical direction */
|
||||
```
|
||||
|
||||
|
||||
* Added support for 0 index value in `@m` function.
|
||||
|
||||
```css
|
||||
/* 0 1 2 3 4 */
|
||||
@content: @M0-4.n
|
||||
```
|
||||
|
||||
* Added `@gap` property.
|
||||
|
||||
```css
|
||||
@grid: 10 / 400px;
|
||||
@gap: 1px;
|
||||
```
|
||||
|
||||
* Added `@dx` and `@dy` functions.
|
||||
|
||||
```css
|
||||
/* -2,-2 -1,-2 0,-2 1,-2 2,-2
|
||||
-2,-1 -1,-1 0,-1 1,-1 2,-1
|
||||
-2,0 -1,0 0,0 1,0 2,0
|
||||
-2,1 -1,1 0,1 1,1 2,1
|
||||
-2,2 -1,2 0,2 1,2 2,2 */
|
||||
@grid: 5 / 400px;
|
||||
@content: @dx, @dy;
|
||||
```
|
||||
Adjust offset.
|
||||
|
||||
```css
|
||||
/* -3,-3 -2,-3 -1,-3 0,-3 1,-3
|
||||
-3,-2 -2,-2 -1,-2 0,-2 1,-2
|
||||
-3,-1 -2,-1 -1,-1 0,-1 1,-1
|
||||
-3,0 -2,0 -1,0 0,0 1,0
|
||||
-3,1 -2,1 -1,1 0,1 1,1 */
|
||||
@grid: 5 / 400px;
|
||||
@content: @dx(1), @dy(1);
|
||||
```
|
||||
|
||||
|
||||
### Fixes
|
||||
|
||||
* Fixed CSS parsing on function composition.
|
||||
* Fixed seed value at pre-compose phase.
|
||||
|
||||
|
||||
|
||||
<br /> <br />
|
||||
|
||||
|
||||
|
||||
## 0.33.1
|
||||
|
||||
* Fixed `@Svg` function detection for transforming the `multiplication` feature.
|
||||
* Fixed `@pn` index value inside `@m` function.
|
||||
|
||||
<br /> <br />
|
||||
|
||||
|
||||
|
||||
## 0.33.0
|
||||
|
||||
* Added new `@svg-pattern` function to reduce boilerplate code.
|
||||
|
||||
```css
|
||||
background: @svg-pattern(
|
||||
width, height: 10%;
|
||||
viewBox: 0 0 10 10;
|
||||
circle { }
|
||||
);
|
||||
```
|
||||
|
||||
* Added new `@Svg` function to return raw SVG code rather than encoded URI value.
|
||||
|
||||
```css
|
||||
@content: @Svg(
|
||||
viewBox: -1 -1 2 2;
|
||||
circle {
|
||||
r: 1;
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
* Added `padding` value for SVG `viewBox` property.
|
||||
|
||||
```css
|
||||
viewBox: -1 -1 2 2 padding 1;
|
||||
|
||||
/* equals to */
|
||||
|
||||
viewBox: -2 2 4 4;
|
||||
```
|
||||
<br /> <br />
|
||||
|
||||
|
||||
|
||||
## 0.32.2
|
||||
|
||||
* Do not use `??` operator at the moment.
|
||||
|
||||
<br /> <br />
|
||||
|
||||
|
||||
|
||||
## 0.32.1
|
||||
|
||||
* Build grid without DOM object.
|
||||
* Fix CSS parser for reading tail function names.
|
||||
|
||||
<br /> <br />
|
||||
|
||||
|
||||
|
||||
## 0.32.0
|
||||
|
||||
### Features
|
||||
|
||||
* Add `@unicode` function to insert Unicode and it can be used both in HTML and CSS.
|
||||
|
||||
```css
|
||||
/* in HTML */
|
||||
@content: @unicode(0x2500);
|
||||
|
||||
/* in CSS */
|
||||
:after {
|
||||
content: @unicode(0x2500);
|
||||
}
|
||||
```
|
||||
|
||||
A sequence of Unicode characters.
|
||||
|
||||
```css
|
||||
@content: @pn.unicode(0x2500, 0x257f, 0x2588);
|
||||
```
|
||||
|
||||
* Add `@mirror/@Mirror` function to transform input items, simlar to `@cycle` and `@reverse`.
|
||||
|
||||
```css
|
||||
/* 1, 2, 3, 4, 5, 5, 4, 3, 2, 1 */
|
||||
@mirror(1, 2, 3, 4, 5);
|
||||
|
||||
/* 1, 2, 3, 4, 5, 4, 3, 2, 1 */
|
||||
@Mirror(1, 2, 3, 4, 5);
|
||||
```
|
||||
|
||||
* All index functions, `@i/I`, `@x/X`, `@y/Y`, and `@n/N/nx/ny` accept extra arguments to do calulations.
|
||||
|
||||
```css
|
||||
@i5 === @calc(@i + 5)
|
||||
|
||||
@i(*10) === @calc(@i * 10)
|
||||
@i(-10) === @calc(@i - 10)
|
||||
@i(/10) === @calc(@i / 10)
|
||||
@i(%10) === @calc(@i % 10)
|
||||
|
||||
@i(10/) === @calc(10 / @i)
|
||||
@i(10-) === @calc(10 - @i)
|
||||
```
|
||||
|
||||
### Patches
|
||||
|
||||
* Reduce imports of the exported svg function.
|
||||
* Improve `@cycle` to support comma-separated values.
|
||||
* Fix grid build for `@content`.
|
||||
* Fix parsing quotes in content.
|
||||
|
||||
```css
|
||||
/* There used to be bugs` */
|
||||
content: '");';
|
||||
```
|
||||
|
||||
<br /> <br />
|
||||
|
||||
|
||||
|
||||
|
||||
## 0.31.2
|
||||
|
||||
* Fix svg-extended style tag generation.
|
||||
|
||||
<br /> <br />
|
||||
|
||||
|
||||
|
||||
## 0.31.1
|
||||
|
||||
* Fix bad reverse function implementation.
|
||||
|
||||
<br /> <br />
|
||||
|
||||
|
||||
|
||||
## 0.31.0
|
||||
|
||||
* Add `@content` property for text content so that they can be selectable.
|
||||
* The `@` symbol can be ommited if the functions are chained together.
|
||||
* Patch reverse function to support both SVG path commands and general arguments.
|
||||
* Skip variable properties for SVG generation.
|
||||
* Fix style tag parsing inside SVG for complex CSS selectors.
|
||||
|
||||
<br /> <br />
|
||||
|
||||
|
||||
|
||||
## 0.30.10
|
||||
|
||||
* Add `@pnr` alias for `@pr`.
|
||||
|
||||
<br /> <br />
|
||||
|
||||
|
||||
|
||||
## 0.30.9
|
||||
|
||||
* Support variables in standalone SVG export function.
|
||||
* Compute index instead of reversing the whole array.
|
||||
* Use single rotate for inner `@place` transformation.
|
||||
* Simplify image detection.
|
||||
* Use `@pn` and `@position` as aliases.
|
||||
|
||||
<br /> <br />
|
||||
|
||||
|
||||
|
||||
## 0.30.8
|
||||
|
||||
Experimenting the new added `pr` function.
|
||||
|
||||
<br /> <br />
|
||||
|
||||
|
||||
|
||||
## 0.30.7
|
||||
|
||||
Experimenting the new added `cycle()` function.
|
||||
|
||||
<br /> <br />
|
||||
|
||||
|
||||
|
||||
## 0.30.6
|
||||
|
||||
I was wrong. Only set `background-size:100%` to `pattern`, `canvas`, and `shader`.
|
||||
|
||||
<br /> <br />
|
||||
|
||||
|
||||
|
||||
## 0.30.5
|
||||
|
||||
* Always add `background-size:100%` to background properties.
|
||||
|
||||
<br /> <br />
|
||||
|
||||
|
||||
|
||||
## 0.30.4
|
||||
|
||||
* Normalize quoted SVG attribute values.
|
||||
|
||||
<br /> <br />
|
||||
|
||||
|
||||
|
||||
## 0.30.3
|
||||
|
||||
* Do not break for decodeURI.
|
||||
* Fix SVG generator on cutting values.
|
||||
|
||||
<br /> <br />
|
||||
|
||||
|
||||
|
||||
## 0.30.2
|
||||
|
||||
* Fix type error.
|
||||
|
||||
<br /> <br />
|
||||
|
||||
|
||||
|
||||
## 0.30.1
|
||||
|
||||
* Fix CSS and SVG parser on quotes and parens.
|
||||
* Add range support for `@m` function.
|
||||
```css
|
||||
::after {
|
||||
/* 5 4 3 2 1 */
|
||||
content: @M5-1.@n;
|
||||
}
|
||||
```
|
||||
|
||||
<br /> <br />
|
||||
|
||||
|
||||
|
||||
## 0.30.0
|
||||
|
||||
* Add exports.
|
||||
|
|
@ -30,6 +388,9 @@
|
|||
`);
|
||||
```
|
||||
|
||||
<br /> <br />
|
||||
|
||||
|
||||
|
||||
## 0.29.2
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ A web component for drawing patterns with CSS.
|
|||
|
||||
* [Tabbied](https://tabbied.com) -- Doodle with generated patterns
|
||||
* [Shapes](https://css-doodle.com/shapes) -- Discover new CSS polygon shapes
|
||||
* [SVG playground](https://css-doodle.com/svg) -- Generate SVG code with new syntax
|
||||
|
||||
|
||||
## Resources
|
||||
|
|
|
|||
760
css-doodle.js
760
css-doodle.js
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "css-doodle",
|
||||
"version": "0.30.0",
|
||||
"version": "0.34.1",
|
||||
"description": "A web component for drawing patterns with CSS",
|
||||
"main": "css-doodle.js",
|
||||
"scripts": {
|
||||
|
|
|
|||
|
|
@ -1,14 +1,24 @@
|
|||
import { generate_css } from '../../generator/css.js';
|
||||
import parse_css from './../../parser/parse-css.js';
|
||||
import parse_grid from './../../parser/parse-grid.js';
|
||||
import { make_tag_function } from '../../utils/index.js';
|
||||
import parse_svg from './../../parser/parse-svg.js';
|
||||
|
||||
export default function svg(rules) {
|
||||
let parsedSvg = parse_svg(rules);
|
||||
let inlineVariable = '';
|
||||
for (let item of parsedSvg.value) {
|
||||
if (item.variable) {
|
||||
inlineVariable += `${item.name}: ${item.value};`;
|
||||
}
|
||||
}
|
||||
let result = generate_css(
|
||||
parse_css(`background: @svg(${rules});`),
|
||||
parse_grid('1x1')
|
||||
parse_css(`${inlineVariable} background: @svg(${rules});`),
|
||||
{ x: 1, y: 1, z: 1, count: 1, ratio: 1 }
|
||||
);
|
||||
let raw = result && result.styles && result.styles.cells || '';
|
||||
let cut = raw.substring(52, raw.length - 5);
|
||||
return decodeURIComponent(cut);
|
||||
let cut = raw.substring(raw.indexOf('%3'), raw.lastIndexOf('");'));
|
||||
try {
|
||||
return decodeURIComponent(cut);
|
||||
} catch (e) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
205
src/function.js
205
src/function.js
|
|
@ -1,7 +1,7 @@
|
|||
import { create_svg_url, normalize_svg } from './utils/svg.js';
|
||||
import { generate_svg } from './generator/svg.js';
|
||||
|
||||
import { cell_id, is_letter, is_nil, add_alias, unique_id, lerp } from './utils/index.js';
|
||||
import { cell_id, is_letter, is_nil, is_empty, add_alias, unique_id, lerp } from './utils/index.js';
|
||||
import { lazy, clamp, sequence, get_value } from './utils/index.js';
|
||||
import { by_unit, by_charcode } from './utils/transform.js';
|
||||
import { last } from './utils/list.js';
|
||||
|
|
@ -26,7 +26,7 @@ function make_sequence(c) {
|
|||
if (!actions || !n) return '';
|
||||
let count = get_value(n());
|
||||
let evaluated = count;
|
||||
if (/\D/.test(count)){
|
||||
if (/\D/.test(count) && !/\d+[x-]\d+/.test(count)) {
|
||||
evaluated = calc(count);
|
||||
if (evaluated === 0) {
|
||||
evaluated = count;
|
||||
|
|
@ -61,62 +61,107 @@ function map2d(value, min, max, amp = 1) {
|
|||
return lerp((value - ma) / (mb - ma), min * amp, max * amp);
|
||||
}
|
||||
|
||||
function calc_with(base) {
|
||||
return v => {
|
||||
if (is_empty(v) || is_empty(base)) {
|
||||
return base;
|
||||
}
|
||||
if (/^[+*-\/%][.\d\s]/.test(v)) {
|
||||
let op = v[0];
|
||||
let num = Number(v.substr(1).trim()) || 0;
|
||||
switch (op) {
|
||||
case '+': return base + num;
|
||||
case '-': return base - num;
|
||||
case '*': return base * num;
|
||||
case '/': return base / num;
|
||||
case '%': return base % num;
|
||||
}
|
||||
}
|
||||
else if (/[+*-\/%]$/.test(v)) {
|
||||
let op = v.substr(-1);
|
||||
let num = Number(v.substr(0, v.length - 1).trim()) || 0;
|
||||
switch (op) {
|
||||
case '+': return num + base;
|
||||
case '-': return num - base;
|
||||
case '*': return num * base;
|
||||
case '/': return num / base;
|
||||
case '%': return num % base;
|
||||
}
|
||||
}
|
||||
return base + (Number(v) || 0);
|
||||
}
|
||||
}
|
||||
|
||||
const Expose = add_alias({
|
||||
|
||||
i({ count }) {
|
||||
return _ => count;
|
||||
return calc_with(count);
|
||||
},
|
||||
|
||||
y({ y }) {
|
||||
return _ => y;
|
||||
return calc_with(y);
|
||||
},
|
||||
|
||||
x({ x }) {
|
||||
return _ => x;
|
||||
return calc_with(x);
|
||||
},
|
||||
|
||||
z({ z }) {
|
||||
return _ => z;
|
||||
return calc_with(z);
|
||||
},
|
||||
|
||||
I({ grid }) {
|
||||
return _ => grid.count;
|
||||
return calc_with(grid.count);
|
||||
},
|
||||
|
||||
Y({ grid }) {
|
||||
return _ => grid.y;
|
||||
return calc_with(grid.y);
|
||||
},
|
||||
|
||||
X({ grid }) {
|
||||
return _ => grid.x;
|
||||
return calc_with(grid.x);
|
||||
},
|
||||
|
||||
Z({ grid }) {
|
||||
return _ => grid.z;
|
||||
return calc_with(grid.z);
|
||||
},
|
||||
|
||||
id({ x, y, z }) {
|
||||
return _ => cell_id(x, y, z);
|
||||
},
|
||||
|
||||
dx({ x, grid }) {
|
||||
return n => {
|
||||
n = Number(n) || 0;
|
||||
return x - .5 - n - grid.x / 2;
|
||||
}
|
||||
},
|
||||
|
||||
dy({ y, grid }) {
|
||||
return n => {
|
||||
n = Number(n) || 0;
|
||||
return y - .5 - n - grid.y / 2;
|
||||
}
|
||||
},
|
||||
|
||||
n({ extra }) {
|
||||
let lastExtra = last(extra);
|
||||
return n => lastExtra ? (lastExtra[0] + (Number(n) || 0)) : '@n';
|
||||
return lastExtra ? calc_with(lastExtra[0]) : '@n';
|
||||
},
|
||||
|
||||
nx({ extra }) {
|
||||
let lastExtra = last(extra);
|
||||
return n => lastExtra ? (lastExtra[1] + (Number(n) || 0)) : '@nx';
|
||||
return lastExtra ? calc_with(lastExtra[1]) : '@nx';
|
||||
},
|
||||
|
||||
ny({ extra }) {
|
||||
let lastExtra = last(extra);
|
||||
return n => lastExtra ? (lastExtra[2] + (Number(n) || 0)) : '@ny';
|
||||
return lastExtra ? calc_with(lastExtra[2]) : '@ny';
|
||||
},
|
||||
|
||||
N({ extra }) {
|
||||
let lastExtra = last(extra);
|
||||
return n => lastExtra ? (lastExtra[3] + (Number(n) || 0)) : '@N';
|
||||
return lastExtra ? calc_with(lastExtra[3]) : '@N';
|
||||
},
|
||||
|
||||
m: make_sequence(','),
|
||||
|
|
@ -167,21 +212,38 @@ const Expose = add_alias({
|
|||
});
|
||||
},
|
||||
|
||||
pn({ context, extra, position }) {
|
||||
pl({ context, extra, position }) {
|
||||
let lastExtra = last(extra);
|
||||
let sig = lastExtra ? last(lastExtra) : '';
|
||||
let counter = 'pn-counter' + position + sig;
|
||||
let counter = 'pl-counter' + position + sig;
|
||||
return expand((...args) => {
|
||||
if (!context[counter]) context[counter] = 0;
|
||||
context[counter] += 1;
|
||||
let max = args.length;
|
||||
let [idx = context[counter]] = lastExtra || [];
|
||||
let idx = lastExtra && lastExtra[6];
|
||||
if (is_nil(idx)) idx = context[counter];
|
||||
let pos = (idx - 1) % max;
|
||||
let value = args[pos];
|
||||
return push_stack(context, 'last_pick', value);
|
||||
});
|
||||
},
|
||||
|
||||
pr({ context, extra, position }) {
|
||||
let lastExtra = last(extra);
|
||||
let sig = lastExtra ? last(lastExtra) : '';
|
||||
let counter = 'pr-counter' + position + sig;
|
||||
return expand((...args) => {
|
||||
if (!context[counter]) context[counter] = 0;
|
||||
context[counter] += 1;
|
||||
let max = args.length;
|
||||
let idx = lastExtra && lastExtra[6];
|
||||
if (is_nil(idx)) idx = context[counter];
|
||||
let pos = (idx - 1) % max;
|
||||
let value = args[max - pos - 1];
|
||||
return push_stack(context, 'last_pick', value);
|
||||
});
|
||||
},
|
||||
|
||||
pd({ context, extra, position, shuffle }) {
|
||||
let lastExtra = last(extra);
|
||||
let sig = lastExtra ? last(lastExtra) : '';
|
||||
|
|
@ -194,7 +256,8 @@ const Expose = add_alias({
|
|||
context[values] = shuffle(args || []);
|
||||
}
|
||||
let max = args.length;
|
||||
let [idx = context[counter]] = lastExtra || [];
|
||||
let idx = lastExtra && lastExtra[6];
|
||||
if (is_nil(idx)) idx = context[counter];
|
||||
let pos = (idx - 1) % max;
|
||||
let value = context[values][pos];
|
||||
return push_stack(context, 'last_pick', value);
|
||||
|
|
@ -321,6 +384,15 @@ const Expose = add_alias({
|
|||
return create_svg_url(svg);
|
||||
}),
|
||||
|
||||
Svg: lazy((_, ...args) => {
|
||||
let value = args.map(input => get_value(input())).join(',');
|
||||
if (!value.startsWith('<')) {
|
||||
let parsed = parse_svg(value);
|
||||
value = generate_svg(parsed);
|
||||
}
|
||||
return normalize_svg(value);
|
||||
}),
|
||||
|
||||
filter: lazy((upstream, ...args) => {
|
||||
let values = args.map(input => get_value(input()));
|
||||
let value = values.join(',');
|
||||
|
|
@ -391,6 +463,20 @@ const Expose = add_alias({
|
|||
return create_svg_url(svg, id);
|
||||
}),
|
||||
|
||||
'svg-pattern': lazy((_, ...args) => {
|
||||
let value = args.map(input => get_value(input())).join(',');
|
||||
let parsed = parse_svg(`
|
||||
viewBox: 0 0 1 1;
|
||||
preserveAspectRatio: xMidYMid slice;
|
||||
rect {
|
||||
width, height: 100%;
|
||||
fill: defs pattern { ${ value } }
|
||||
}
|
||||
`);
|
||||
let svg = generate_svg(parsed);
|
||||
return create_svg_url(svg);
|
||||
}),
|
||||
|
||||
var() {
|
||||
return value => `var(${ get_value(value) })`;
|
||||
},
|
||||
|
|
@ -539,13 +625,76 @@ const Expose = add_alias({
|
|||
}
|
||||
},
|
||||
|
||||
reverse(...args) {
|
||||
return commands => {
|
||||
let parsed = parse_svg_path(commands);
|
||||
if (!parsed.valid) return commands;
|
||||
return parsed.commands.reverse().map(({ name, value }) => {
|
||||
return name + value.join(' ');
|
||||
}).join(' ');
|
||||
reverse() {
|
||||
return (...args) => {
|
||||
let commands = args.map(get_value);
|
||||
let parsed = parse_svg_path(commands.join(','));
|
||||
if (parsed.valid) {
|
||||
let result = [];
|
||||
for (let i = parsed.commands.length - 1; i >= 0; --i) {
|
||||
let { name, value } = parsed.commands[i];
|
||||
result.push(name + value.join(' '));
|
||||
}
|
||||
return result.join(' ');
|
||||
}
|
||||
return commands.reverse();
|
||||
}
|
||||
},
|
||||
|
||||
cycle() {
|
||||
return (...args) => {
|
||||
let list = [];
|
||||
let separator;
|
||||
if (args.length == 1) {
|
||||
separator = ' ';;
|
||||
list = parse_value_group(args[0], { symbol: separator });
|
||||
} else {
|
||||
separator = ',';
|
||||
list = parse_value_group(args.map(get_value).join(separator), { symbol: separator});
|
||||
}
|
||||
let size = list.length - 1;
|
||||
let result = [list.join(separator)];
|
||||
// Just ignore the performance
|
||||
for (let i = 0; i < size; ++i) {
|
||||
let item = list.shift();
|
||||
list.push(item);
|
||||
result.push(list.join(separator));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
},
|
||||
|
||||
mirror() {
|
||||
return (...args) => {
|
||||
for (let i = args.length - 1; i >= 0; --i) {
|
||||
args.push(args[i]);
|
||||
}
|
||||
return args;
|
||||
}
|
||||
},
|
||||
|
||||
Mirror() {
|
||||
return (...args) => {
|
||||
for (let i = args.length - 2; i >= 0; --i) {
|
||||
args.push(args[i]);
|
||||
}
|
||||
return args;
|
||||
}
|
||||
},
|
||||
|
||||
unicode() {
|
||||
return (...args) => {
|
||||
return args.map(code => String.fromCharCode(code));
|
||||
}
|
||||
},
|
||||
|
||||
once({ context, extra, position }) {
|
||||
let counter = 'once-counter' + position;
|
||||
return (...args) => {
|
||||
if (is_nil(context[counter])) {
|
||||
context[counter] = args;
|
||||
}
|
||||
return context[counter];
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -557,6 +706,8 @@ const Expose = add_alias({
|
|||
'depth': 'z',
|
||||
'rand': 'r',
|
||||
'pick': 'p',
|
||||
'pn': 'pl',
|
||||
'pnr': 'pr',
|
||||
|
||||
// error prone
|
||||
'stripes': 'stripe',
|
||||
|
|
@ -588,8 +739,8 @@ const Expose = add_alias({
|
|||
'sz': 'Z',
|
||||
'size-z': 'Z',
|
||||
'size-depth': 'Z',
|
||||
'pick-by-turn': 'pn',
|
||||
'pick-n': 'pn',
|
||||
'pick-by-turn': 'pl',
|
||||
'pick-n': 'pl',
|
||||
'pick-d': 'pd',
|
||||
'offset': 'plot',
|
||||
'Offset': 'Plot',
|
||||
|
|
|
|||
|
|
@ -2,11 +2,12 @@ import Func from '../function.js';
|
|||
import Property from '../property.js';
|
||||
import Selector from '../selector.js';
|
||||
import parse_value_group from '../parser/parse-value-group.js';
|
||||
import { uniform_time } from '../uniforms.js';
|
||||
import { prefixer } from '../utils/prefixer.js';
|
||||
|
||||
import calc from '../calc.js';
|
||||
import { uniform_time } from '../uniforms.js';
|
||||
import { seedrandom } from '../lib/seedrandom.js';
|
||||
|
||||
import { prefixer } from '../utils/prefixer.js';
|
||||
import { maybe, cell_id, is_nil, get_value, lerp, unique_id } from '../utils/index.js';
|
||||
import { join, make_array, remove_empty_values } from '../utils/list.js'
|
||||
|
||||
|
|
@ -22,6 +23,10 @@ function is_special_selector(s) {
|
|||
return is_host_selector(s) || is_parent_selector(s);
|
||||
}
|
||||
|
||||
function is_pseudo_selecotr(s) {
|
||||
return /\:before|\:after/.test(s);
|
||||
}
|
||||
|
||||
const MathFunc = {};
|
||||
for (let name of Object.getOwnPropertyNames(Math)) {
|
||||
MathFunc[name] = () => (...args) => {
|
||||
|
|
@ -43,6 +48,7 @@ class Rules {
|
|||
this.grid = null;
|
||||
this.seed = null;
|
||||
this.is_grid_defined = false;
|
||||
this.is_gap_defined = false;
|
||||
this.coords = [];
|
||||
this.doodles = {};
|
||||
this.canvas = {};
|
||||
|
|
@ -51,6 +57,7 @@ class Rules {
|
|||
this.reset();
|
||||
this.custom_properties = {};
|
||||
this.uniforms = {};
|
||||
this.content = {};
|
||||
}
|
||||
|
||||
reset() {
|
||||
|
|
@ -65,6 +72,7 @@ class Rules {
|
|||
this.canvas = {};
|
||||
this.pattern = {};
|
||||
this.shaders = {};
|
||||
this.content = {};
|
||||
for (let key in this.rules) {
|
||||
if (key.startsWith('#c')) {
|
||||
delete this.rules[key];
|
||||
|
|
@ -322,6 +330,29 @@ class Rules {
|
|||
}
|
||||
}
|
||||
|
||||
add_grid_style({ fill, clip, rotate, scale, flexRow, flexColumn }) {
|
||||
if (fill) {
|
||||
this.add_rule(':host', `background-color: ${fill};`);
|
||||
}
|
||||
if (!clip) {
|
||||
this.add_rule(':host', 'contain: none;');
|
||||
}
|
||||
if (rotate) {
|
||||
this.add_rule(':container', `rotate: ${rotate};`);
|
||||
}
|
||||
if (scale) {
|
||||
this.add_rule(':container', `scale: ${scale};`);
|
||||
}
|
||||
if (flexRow) {
|
||||
this.add_rule(':container', `display: flex`);
|
||||
this.add_rule('cell', `flex: 1`);
|
||||
}
|
||||
if (flexColumn) {
|
||||
this.add_rule(':container', `display: flex; flex-direction: column;`);
|
||||
this.add_rule('cell', `flex: 1`);
|
||||
}
|
||||
}
|
||||
|
||||
compose_rule(token, _coords, selector) {
|
||||
let coords = Object.assign({}, _coords);
|
||||
let prop = token.property;
|
||||
|
|
@ -400,7 +431,11 @@ class Rules {
|
|||
}
|
||||
}
|
||||
|
||||
if (prop === 'background' && (value.includes('shader') || value.includes('canvas') || value.includes('pattern'))) {
|
||||
let is_image = (
|
||||
/^(background|background\-image)$/.test(prop) &&
|
||||
/\$\{(canvas|shader|pattern)/.test(value)
|
||||
);
|
||||
if (is_image) {
|
||||
rule += 'background-size: 100% 100%;';
|
||||
}
|
||||
|
||||
|
|
@ -430,6 +465,7 @@ class Rules {
|
|||
case 'grid': {
|
||||
if (is_host_selector(selector)) {
|
||||
rule = transformed.size || '';
|
||||
this.add_grid_style(transformed);
|
||||
} else {
|
||||
rule = '';
|
||||
if (!this.is_grid_defined) {
|
||||
|
|
@ -439,12 +475,27 @@ class Rules {
|
|||
max_grid: coords.max_grid
|
||||
});
|
||||
this.add_rule(':host', transformed.size || '');
|
||||
this.add_grid_style(transformed);
|
||||
}
|
||||
}
|
||||
this.grid = coords.grid;
|
||||
this.is_grid_defined = true;
|
||||
break;
|
||||
}
|
||||
case 'gap': {
|
||||
rule = '';
|
||||
if (!this.is_gap_defined) {
|
||||
this.add_rule(':container', `gap: ${transformed};`);
|
||||
this.is_gap_defined = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'content': {
|
||||
rule = '';
|
||||
if (transformed !== undefined && !is_pseudo_selecotr(selector) && !is_parent_selector(selector)) {
|
||||
this.content[this.compose_selector(coords)] = transformed;
|
||||
}
|
||||
}
|
||||
case 'seed': {
|
||||
rule = '';
|
||||
break;
|
||||
|
|
@ -531,7 +582,9 @@ class Rules {
|
|||
}
|
||||
}
|
||||
});
|
||||
if (!is_nil(this.seed)) {
|
||||
if (is_nil(this.seed)) {
|
||||
this.seed = coords.seed_value;
|
||||
} else {
|
||||
coords.update_random(this.seed);
|
||||
}
|
||||
}
|
||||
|
|
@ -673,6 +726,7 @@ class Rules {
|
|||
canvas: this.canvas,
|
||||
pattern: this.pattern,
|
||||
uniforms: this.uniforms,
|
||||
content: this.content,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -716,6 +770,7 @@ function generate_css(tokens, grid_size, seed_value, max_grid, seed_random) {
|
|||
grid: { x: 1, y: 1, z: 1, count: 1 },
|
||||
random, rand, pick, shuffle,
|
||||
max_grid, update_random,
|
||||
seed_value,
|
||||
});
|
||||
|
||||
let { grid, seed } = rules.output();
|
||||
|
|
|
|||
|
|
@ -7,7 +7,10 @@ function generate(token, last) {
|
|||
} else {
|
||||
result += token.name + (isInline ? ' ' : '{');
|
||||
}
|
||||
if (Array.isArray(token.value) && token.value.length) {
|
||||
if (token.name === 'style') {
|
||||
result += token.value;
|
||||
}
|
||||
else if (Array.isArray(token.value) && token.value.length) {
|
||||
let lastGroup = '';
|
||||
for (let t of token.value) {
|
||||
result += generate(t, lastGroup);;
|
||||
|
|
|
|||
|
|
@ -49,11 +49,12 @@ class Tag {
|
|||
}
|
||||
toString() {
|
||||
if (this.isTextNode()) {
|
||||
return this.body;
|
||||
return removeQuotes(this.body);
|
||||
}
|
||||
let attrs = [''];
|
||||
let body = [];
|
||||
for (let [name, value] of Object.entries(this.attrs)) {
|
||||
value = removeQuotes(value);
|
||||
attrs.push(`${name}="${value}"`);
|
||||
}
|
||||
for (let tag of this.body) {
|
||||
|
|
@ -67,11 +68,27 @@ function composeStyleRule(name, value) {
|
|||
return `${name}:${value};`
|
||||
}
|
||||
|
||||
function composeStyle(block) {
|
||||
const style = block.value
|
||||
.map(n => (n.type === 'block') ? composeStyle(n) : composeStyleRule(n.name, n.value))
|
||||
.join('');
|
||||
return `${block.name}{${style}}`;
|
||||
function removeQuotes(text) {
|
||||
text = String(text);
|
||||
let double = text.startsWith('"') && text.endsWith('"');
|
||||
let single = text.startsWith("'") && text.endsWith("'");
|
||||
if (double || single) {
|
||||
return text.substring(1, text.length - 1);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
function transformViewBox(token) {
|
||||
let viewBox = token.detail.value;
|
||||
let p = token.detail.padding || token.detail.expand;
|
||||
if (!viewBox.length) {
|
||||
return '';
|
||||
}
|
||||
let [x, y, w, h] = viewBox;
|
||||
if (p) {
|
||||
[x, y, w, h] = [x-p, x-p, w+p*2, h+p*2];
|
||||
}
|
||||
return `${x} ${y} ${w} ${h}`;
|
||||
}
|
||||
|
||||
function generate(token, element, parent, root) {
|
||||
|
|
@ -82,17 +99,9 @@ function generate(token, element, parent, root) {
|
|||
if (token.type === 'block') {
|
||||
// style tag
|
||||
if (token.name === 'style') {
|
||||
if (Array.isArray(token.value)) {
|
||||
let el = new Tag('style');
|
||||
let styles = [];
|
||||
for (let block of token.value) {
|
||||
if (block.type === 'block') {
|
||||
styles.push(composeStyle(block));
|
||||
}
|
||||
}
|
||||
el.append(styles.join(''));
|
||||
element.append(el);
|
||||
}
|
||||
let el = new Tag('style');
|
||||
el.append(token.value);
|
||||
element.append(el);
|
||||
}
|
||||
// normal svg elements
|
||||
else {
|
||||
|
|
@ -123,9 +132,8 @@ function generate(token, element, parent, root) {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (token.type === 'statement') {
|
||||
let isTextNode = parent && /^(text|tspan|textPath|title|desc)$/i.test(parent.name);
|
||||
if (isTextNode && token.name === 'content') {
|
||||
if (token.type === 'statement' && !token.variable) {
|
||||
if (token.name === 'content') {
|
||||
let text = new Tag('text-node', token.value);
|
||||
element.append(text);
|
||||
}
|
||||
|
|
@ -147,7 +155,14 @@ function generate(token, element, parent, root) {
|
|||
value = `#${id}`;
|
||||
}
|
||||
}
|
||||
element.attr(token.name, value);
|
||||
if (/viewBox/i.test(token.name)) {
|
||||
value = transformViewBox(token, value);
|
||||
if (value) {
|
||||
element.attr(token.name, value);
|
||||
}
|
||||
} else {
|
||||
element.attr(token.name, value);
|
||||
}
|
||||
if (token.name.includes('xlink:')) {
|
||||
root.attr('xmlns:xlink', NSXLink);
|
||||
}
|
||||
|
|
|
|||
119
src/index.js
119
src/index.js
|
|
@ -58,44 +58,42 @@ if (typeof customElements !== 'undefined') {
|
|||
|
||||
update(styles) {
|
||||
this.cleanup();
|
||||
|
||||
let use = this.get_use();
|
||||
if (!styles) styles = un_entity(this.innerHTML);
|
||||
this.innerHTML = styles;
|
||||
|
||||
// Use old rules to update
|
||||
if (!styles) {
|
||||
styles = un_entity(this.innerHTML);
|
||||
}
|
||||
if (this.innerHTML !== styles) {
|
||||
this.innerHTML = styles;
|
||||
}
|
||||
if (!this.grid_size) {
|
||||
this.grid_size = this.get_grid();
|
||||
}
|
||||
|
||||
let { x: gx, y: gy, z: gz } = this.grid_size;
|
||||
const { x: gx, y: gy, z: gz } = this.grid_size;
|
||||
const use = this.get_use();
|
||||
|
||||
const compiled = this.generate(
|
||||
parse_css(use + styles, this.extra)
|
||||
let old_content = '';
|
||||
if (this.compiled) {
|
||||
old_content = this.compiled.content;
|
||||
}
|
||||
|
||||
const compiled = this.generate(parse_css(use + styles, this.extra));
|
||||
|
||||
let grid = compiled.grid || this.get_grid();
|
||||
let { x, y, z } = grid;
|
||||
|
||||
let should_rebuild = (
|
||||
!this.shadowRoot.innerHTML
|
||||
|| (gx !== x || gy !== y || gz !== z)
|
||||
|| (JSON.stringify(old_content) !== JSON.stringify(compiled.content))
|
||||
);
|
||||
|
||||
if (!this.shadowRoot.innerHTML) {
|
||||
Object.assign(this.grid_size, compiled.grid);
|
||||
return this.build_grid(compiled, compiled.grid);
|
||||
}
|
||||
Object.assign(this.grid_size, grid);
|
||||
|
||||
if (compiled.grid) {
|
||||
let { x, y, z } = compiled.grid;
|
||||
if (gx !== x || gy !== y || gz !== z) {
|
||||
Object.assign(this.grid_size, compiled.grid);
|
||||
return this.build_grid(compiled, compiled.grid);
|
||||
}
|
||||
Object.assign(this.grid_size, compiled.grid);
|
||||
}
|
||||
else {
|
||||
let grid = this.get_grid();
|
||||
let { x, y, z } = grid;
|
||||
if (gx !== x || gy !== y || gz !== z) {
|
||||
Object.assign(this.grid_size, grid);
|
||||
return this.build_grid(
|
||||
this.generate(parse_css(use + styles, this.extra)),
|
||||
grid
|
||||
);
|
||||
}
|
||||
if (should_rebuild) {
|
||||
return compiled.grid
|
||||
? this.build_grid(compiled, grid)
|
||||
: this.build_grid(this.generate(parse_css(use + styles, this.extra)), grid);
|
||||
}
|
||||
|
||||
let replace = this.replace(compiled);
|
||||
|
|
@ -196,7 +194,7 @@ if (typeof customElements !== 'undefined') {
|
|||
const { keyframes, host, container, cells } = compiled.styles;
|
||||
|
||||
let replace = this.replace(compiled);
|
||||
let grid_container = create_grid(grid);
|
||||
let grid_container = create_grid(grid, compiled.content);
|
||||
|
||||
let size = (options && options.width && options.height)
|
||||
? `width="${ options.width }" height="${ options.height }"`
|
||||
|
|
@ -391,7 +389,7 @@ if (typeof customElements !== 'undefined') {
|
|||
let style_container = get_grid_styles(grid) + host + container;
|
||||
let style_cells = has_delay ? '' : cells;
|
||||
|
||||
const { uniforms } = compiled;
|
||||
const { uniforms, content } = compiled;
|
||||
|
||||
let replace = this.replace(compiled);
|
||||
|
||||
|
|
@ -401,7 +399,7 @@ if (typeof customElements !== 'undefined') {
|
|||
<style class="style-container">${ style_container }</style>
|
||||
<style class="style-cells">${ style_cells }</style>
|
||||
<svg id="defs" xmlns="http://www.w3.org/2000/svg" style="width:0;height:0"></svg>
|
||||
${ create_grid(grid) }
|
||||
${ create_grid(grid, content) }
|
||||
`;
|
||||
|
||||
this.set_content('.style-container', replace(style_container));
|
||||
|
|
@ -588,13 +586,14 @@ function get_basic_styles() {
|
|||
return `
|
||||
*, *::after, *::before {
|
||||
box-sizing: border-box;
|
||||
animation-play-state: var(--cssd-animation-play-state) !important;
|
||||
animation-play-state: var(--cssd-animation-play-state) !important
|
||||
}
|
||||
:host, .host {
|
||||
display: block;
|
||||
visibility: visible;
|
||||
width: auto;
|
||||
height: auto;
|
||||
contain: content;
|
||||
--${ uniform_time.name }: 0
|
||||
}
|
||||
:host([hidden]), .host[hidden] {
|
||||
|
|
@ -607,20 +606,19 @@ function get_basic_styles() {
|
|||
display: grid;
|
||||
${ inherited_grid_props }
|
||||
}
|
||||
cell:empty {
|
||||
cell {
|
||||
position: relative;
|
||||
line-height: 1;
|
||||
display: grid;
|
||||
place-items: center
|
||||
}
|
||||
svg {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
height: 100%
|
||||
}
|
||||
:host([cssd-paused-animation]) {
|
||||
--cssd-animation-play-state: paused;
|
||||
animation-play-state: paused !important;
|
||||
animation-play-state: paused !important
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
|
@ -635,41 +633,44 @@ function get_grid_styles(grid_obj) {
|
|||
`;
|
||||
}
|
||||
|
||||
function create_cell(x, y, z) {
|
||||
let cell = document.createElement('cell');
|
||||
cell.id = cell_id(x, y, z);
|
||||
return cell;
|
||||
function get_content(input) {
|
||||
return is_nil(input) ? '' : input;
|
||||
}
|
||||
|
||||
function create_grid(grid_obj) {
|
||||
function create_cell(x, y, z, content, child = '') {
|
||||
let id = cell_id(x, y, z);
|
||||
let head = get_content(content['#' + id]);
|
||||
let tail = get_content(child);
|
||||
return `<cell id="${id}">${head}${tail}</cell>`;
|
||||
}
|
||||
|
||||
function create_grid(grid_obj, content) {
|
||||
let { x, y, z } = grid_obj || {};
|
||||
let grid = document.createElement('grid');
|
||||
let root = document.createDocumentFragment();
|
||||
let result = '';
|
||||
if (z == 1) {
|
||||
for (let j = 1; j <= y; ++j) {
|
||||
for (let i = 1; i <= x; ++i) {
|
||||
root.appendChild(create_cell(i, j, 1));
|
||||
result += create_cell(i, j, 1, content);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
let temp = null;
|
||||
for (let i = 1; i <= z; ++i) {
|
||||
let cell = create_cell(1, 1, i);
|
||||
(temp || root).appendChild(cell);
|
||||
temp = cell;
|
||||
let child = '';
|
||||
for (let i = z; i >= 1; i--) {
|
||||
let cell = create_cell(1, 1, i, content, child);
|
||||
child = cell;
|
||||
}
|
||||
temp = null;
|
||||
result = child;
|
||||
}
|
||||
grid.className = 'container';
|
||||
grid.appendChild(root);
|
||||
return grid.outerHTML;
|
||||
return `<grid class="container">${result}</grid>`;
|
||||
}
|
||||
|
||||
export default make_tag_function(rules => {
|
||||
let doodle = document.createElement('css-doodle');
|
||||
if (doodle.update) {
|
||||
doodle.update(rules);
|
||||
if (typeof document !== 'undefined') {
|
||||
let doodle = document.createElement('css-doodle');
|
||||
if (doodle.update) {
|
||||
doodle.update(rules);
|
||||
}
|
||||
return doodle;
|
||||
}
|
||||
return doodle;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -265,9 +265,14 @@ function read_arguments(it, composition, doodle) {
|
|||
let raw = '';
|
||||
while (!it.end()) {
|
||||
c = it.curr();
|
||||
let prev = it.curr(-1);
|
||||
let next = it.curr(1);
|
||||
let start = it.index();
|
||||
if ((/[\('"`]/.test(c) && it.curr(-1) !== '\\')) {
|
||||
if ((/[\('"`]/.test(c) && prev !== '\\')) {
|
||||
if (stack.length) {
|
||||
if ((c !== '(') && last(stack) === '(') {
|
||||
stack.pop();
|
||||
}
|
||||
if (c != '(' && c === last(stack)) {
|
||||
stack.pop();
|
||||
} else {
|
||||
|
|
@ -278,7 +283,7 @@ function read_arguments(it, composition, doodle) {
|
|||
}
|
||||
arg += c;
|
||||
}
|
||||
else if (c == '@' && !doodle) {
|
||||
else if ((c == '@' || (prev === '.' && composition)) && !doodle) {
|
||||
if (!group.length) {
|
||||
arg = arg.trimLeft();
|
||||
}
|
||||
|
|
@ -290,7 +295,7 @@ function read_arguments(it, composition, doodle) {
|
|||
}
|
||||
else if (doodle && /[)]/.test(c) || (!doodle && /[,)]/.test(c))) {
|
||||
if (stack.length) {
|
||||
if (c == ')') {
|
||||
if (c == ')' && last(stack) === '(') {
|
||||
stack.pop();
|
||||
}
|
||||
arg += c;
|
||||
|
|
@ -324,7 +329,7 @@ function read_arguments(it, composition, doodle) {
|
|||
}
|
||||
arg += c;
|
||||
}
|
||||
if (composition && (it.curr(1) == ')' || !/[0-9a-zA-Z_\-.]/.test(it.curr())) && !stack.length) {
|
||||
if (composition && ((next == ')' || next == ';') || !/[0-9a-zA-Z_\-.]/.test(it.curr())) && !stack.length) {
|
||||
if (group.length) {
|
||||
args.push(normalize_argument(group));
|
||||
}
|
||||
|
|
@ -377,14 +382,14 @@ function normalize_argument(group) {
|
|||
|
||||
function seperate_func_name(name) {
|
||||
let fname = '', extra = '';
|
||||
if ((/\D$/.test(name) && !/\d+x\d+/.test(name)) || Math[name.substr(1)]) {
|
||||
if ((/\D$/.test(name) && !/\d+[x-]\d+/.test(name)) || Math[name.substr(1)]) {
|
||||
return { fname: name, extra }
|
||||
}
|
||||
for (let i = name.length - 1; i >= 0; i--) {
|
||||
let c = name[i];
|
||||
let prev = name[i - 1];
|
||||
let next = name[i + 1];
|
||||
if (/[\d.]/.test(c) || ((c == 'x') && /\d/.test(prev) && /\d/.test(next))) {
|
||||
if (/[\d.]/.test(c) || ((c == 'x' || c == '-') && /\d/.test(prev) && /\d/.test(next))) {
|
||||
extra = c + extra;
|
||||
} else {
|
||||
fname = name.substring(0, i + 1);
|
||||
|
|
@ -399,21 +404,28 @@ function has_times_syntax(token) {
|
|||
return str.includes('pureName') && str.includes('times');
|
||||
}
|
||||
|
||||
function is_svg(name) {
|
||||
return /^@svg$/i.test(name);
|
||||
}
|
||||
|
||||
function read_func(it) {
|
||||
let func = Tokens.func();
|
||||
let name = '@', c;
|
||||
let name = it.curr(), c;
|
||||
let has_argument = false;
|
||||
it.next();
|
||||
|
||||
if (name === '@') {
|
||||
it.next();
|
||||
} else {
|
||||
name = '@';
|
||||
}
|
||||
while (!it.end()) {
|
||||
c = it.curr();
|
||||
let composition = (c == '.' && it.curr(1) == '@');
|
||||
let next = it.curr(1);
|
||||
let composition = (c == '.' && (next == '@' || /[a-zA-Z]/.test(next)));
|
||||
if (c == '(' || composition) {
|
||||
has_argument = true;
|
||||
it.next();
|
||||
let [args, raw_args] = read_arguments(it, composition, composible(name));
|
||||
if (name === '@svg' && /\d\s*{/.test(raw_args)) {
|
||||
if (is_svg(name) && /\d\s*{/.test(raw_args)) {
|
||||
let parsed_svg = parse_svg(raw_args);
|
||||
if (has_times_syntax(parsed_svg)) {
|
||||
let svg = generate_svg_extended(parsed_svg);
|
||||
|
|
@ -425,16 +437,14 @@ function read_func(it) {
|
|||
}
|
||||
func.arguments = args;
|
||||
break;
|
||||
} else if (!has_argument && next !== '(' && !/[0-9a-zA-Z_\-.]/.test(next)) {
|
||||
} else if (/[0-9a-zA-Z_\-.]/.test(c)) {
|
||||
name += c;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
name += c;
|
||||
if (!has_argument && next !== '(' && !/[0-9a-zA-Z_\-.]/.test(next)) {
|
||||
break;
|
||||
}
|
||||
it.next();
|
||||
}
|
||||
|
||||
let { fname, extra } = seperate_func_name(name);
|
||||
func.name = fname;
|
||||
|
||||
|
|
@ -451,8 +461,9 @@ function read_func(it) {
|
|||
|
||||
function read_value(it) {
|
||||
let text = Tokens.text(), idx = 0, skip = true, c;
|
||||
const value = [], stack = [];
|
||||
const value = [];
|
||||
value[idx] = [];
|
||||
let stack = [], quote_stack = [];
|
||||
|
||||
while (!it.end()) {
|
||||
c = it.curr();
|
||||
|
|
@ -475,20 +486,29 @@ function read_value(it) {
|
|||
value[++idx] = [];
|
||||
skip = true;
|
||||
}
|
||||
else if (/[;}<]/.test(c)) {
|
||||
else if (/[;}<]/.test(c) && !quote_stack.length) {
|
||||
if (text.value.length) {
|
||||
value[idx].push(text);
|
||||
text = Tokens.text();
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if (c == '@') {
|
||||
else if (c == '@' && /\w/.test(it.curr(1))) {
|
||||
if (text.value.length) {
|
||||
value[idx].push(text);
|
||||
text = Tokens.text();
|
||||
}
|
||||
value[idx].push(read_func(it));
|
||||
}
|
||||
else if (c === '"' || c === "'") {
|
||||
let quote = last(quote_stack);
|
||||
if (c === quote) {
|
||||
quote_stack.pop();
|
||||
} else if (!quote_stack.length) {
|
||||
quote_stack.push(c);
|
||||
}
|
||||
text.value += c;
|
||||
}
|
||||
else if (!is.white_space(c) || !is.white_space(it.curr(-1))) {
|
||||
if (c == '(') stack.push(c);
|
||||
if (c == ')') stack.pop();
|
||||
|
|
@ -496,9 +516,11 @@ function read_value(it) {
|
|||
if (symbols[c] && !/[0-9]/.test(it.curr(-1))) {
|
||||
c = symbols[c];
|
||||
}
|
||||
|
||||
text.value += c;
|
||||
}
|
||||
if (it.curr() === ';' || it.curr() == '}') {
|
||||
break;
|
||||
}
|
||||
it.next();
|
||||
}
|
||||
if (text.value.length) {
|
||||
|
|
|
|||
|
|
@ -8,19 +8,19 @@ function readStatement(iter, token) {
|
|||
let stackParen = [];
|
||||
while (iter.next()) {
|
||||
let { curr, next } = iter.get();
|
||||
let isStatementBreak = !stackQuote.length && !stackParen.length && (!next || curr.isSymbol(';') || next.isSymbol('}'));
|
||||
if (curr.isSymbol('(')) {
|
||||
if (curr.isSymbol('(') && !stackQuote.length) {
|
||||
stackParen.push(curr);
|
||||
} else if (curr.isSymbol(')')) {
|
||||
} else if (curr.isSymbol(')') && !stackQuote.length) {
|
||||
stackParen.pop();
|
||||
}
|
||||
let isStatementBreak = !stackQuote.length && !stackParen.length && (!next || curr.isSymbol(';') || next.isSymbol('}'));
|
||||
if (curr.isSymbol("'", '"')) {
|
||||
if (curr.status === 'open') {
|
||||
stackQuote.push(curr);
|
||||
} else {
|
||||
stackQuote.pop();
|
||||
}
|
||||
if (next.isSymbol('}') && !stackQuote.length) {
|
||||
if ((next && next.isSymbol('}')) && !stackQuote.length) {
|
||||
isStatementBreak = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -47,18 +47,13 @@ function readStatement(iter, token) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
// skip quotes
|
||||
let skip = (curr.status == 'open' && stackQuote.length == 1)
|
||||
|| (curr.status == 'close' && !stackQuote.length);
|
||||
|
||||
if (!skip) {
|
||||
fragment.push(curr);
|
||||
}
|
||||
fragment.push(curr);
|
||||
if (isStatementBreak) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (fragment.length && !inlineBlock) {
|
||||
token._valueTokens = fragment;
|
||||
token.value = joinToken(fragment);
|
||||
} else if (inlineBlock) {
|
||||
token.value = inlineBlock;
|
||||
|
|
@ -66,7 +61,26 @@ function readStatement(iter, token) {
|
|||
if (token.origin) {
|
||||
token.origin.value = token.value;
|
||||
}
|
||||
return token
|
||||
return token;
|
||||
}
|
||||
|
||||
function readStyle(iter) {
|
||||
let stack = [];
|
||||
let style = [];
|
||||
while (iter.next()) {
|
||||
let { curr } = iter.get();
|
||||
if (curr.isSymbol('{')) {
|
||||
stack.push(curr.value);
|
||||
} else if (curr.isSymbol('}')) {
|
||||
if (stack.length) {
|
||||
stack.pop();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
style.push(curr.value);
|
||||
}
|
||||
return style.join('');
|
||||
}
|
||||
|
||||
function walk(iter, parentToken) {
|
||||
|
|
@ -77,13 +91,13 @@ function walk(iter, parentToken) {
|
|||
|
||||
while (iter.next()) {
|
||||
let { prev, curr, next } = iter.get();
|
||||
let isBlockBreak = !next || curr.isSymbol('}');
|
||||
if (curr.isSymbol('(')) {
|
||||
stack.push(curr.value);
|
||||
}
|
||||
if (curr.isSymbol(')')) {
|
||||
stack.pop();
|
||||
}
|
||||
let isBlockBreak = !next || curr.isSymbol('}');
|
||||
if (isBlock(tokenType) && isBlockBreak) {
|
||||
if (!next && rules.length && !curr.isSymbol('}')) {
|
||||
let last = rules[rules.length - 1].value;
|
||||
|
|
@ -104,19 +118,29 @@ function walk(iter, parentToken) {
|
|||
}
|
||||
let tokenName = selectors.pop();
|
||||
let skip = isSkip(...selectors, parentToken.name, tokenName);
|
||||
let block = resolveId(walk(iter, splitTimes(tokenName, {
|
||||
type: 'block',
|
||||
name: tokenName,
|
||||
value: []
|
||||
})), skip);
|
||||
while (tokenName = selectors.pop()) {
|
||||
block = resolveId(splitTimes(tokenName, {
|
||||
|
||||
if (tokenName === 'style') {
|
||||
rules.push({
|
||||
type: 'block',
|
||||
name: tokenName,
|
||||
value: [block]
|
||||
}), skip);
|
||||
value: readStyle(iter)
|
||||
});
|
||||
} else {
|
||||
let block = resolveId(walk(iter, splitTimes(tokenName, {
|
||||
type: 'block',
|
||||
name: tokenName,
|
||||
value: []
|
||||
})), skip);
|
||||
|
||||
while (tokenName = selectors.pop()) {
|
||||
block = resolveId(splitTimes(tokenName, {
|
||||
type: 'block',
|
||||
name: tokenName,
|
||||
value: [block]
|
||||
}), skip);
|
||||
}
|
||||
rules.push(block);
|
||||
}
|
||||
rules.push(block);
|
||||
fragment = [];
|
||||
}
|
||||
else if (
|
||||
|
|
@ -137,15 +161,21 @@ function walk(iter, parentToken) {
|
|||
};
|
||||
}
|
||||
let statement = readStatement(iter, intial);
|
||||
|
||||
let groupdValue = parseValueGroup(statement.value);
|
||||
let expand = (props.length > 1 && groupdValue.length === props.length);
|
||||
|
||||
props.forEach((prop, i) => {
|
||||
let item = Object.assign({}, statement, { name: prop });
|
||||
if (/^\-\-/.test(prop)) {
|
||||
item.variable = true;
|
||||
}
|
||||
if (expand) {
|
||||
item.value = groupdValue[i];
|
||||
}
|
||||
if (/viewBox/i.test(prop)) {
|
||||
item.detail = parseViewBox(item.value, item._valueTokens);
|
||||
}
|
||||
delete item._valueTokens;
|
||||
rules.push(item);
|
||||
});
|
||||
if (isBlock(tokenType)) {
|
||||
|
|
@ -250,6 +280,30 @@ function getSelectors(tokens) {
|
|||
return result;
|
||||
}
|
||||
|
||||
function parseViewBox(value, tokens) {
|
||||
const viewBox = { value: [] };
|
||||
let temp;
|
||||
if (!Array.isArray(tokens)) {
|
||||
return viewBox;
|
||||
}
|
||||
for (let token of tokens) {
|
||||
if (token.isSpace() || token.isSymbol(',', ';')) {
|
||||
continue;
|
||||
}
|
||||
if (viewBox.value.length < 4 && token.isNumber()) {
|
||||
viewBox.value.push(Number(token.value));
|
||||
}
|
||||
else if (token.isNumber() && temp) {
|
||||
viewBox[temp] = Number(token.value);
|
||||
temp = null;
|
||||
}
|
||||
else if (token.isWord()) {
|
||||
temp = token.value;
|
||||
}
|
||||
}
|
||||
return viewBox;
|
||||
}
|
||||
|
||||
function splitTimes(name, object) {
|
||||
let target = Object.assign({}, object);
|
||||
if (/\*\s*[0-9]/.test(name)) {
|
||||
|
|
@ -271,12 +325,20 @@ function isBlock(type) {
|
|||
}
|
||||
|
||||
function skipHeadSVG(block) {
|
||||
let head = block && block.value && block.value[0];
|
||||
if (head && head.name === 'svg' && isBlock(head.type)) {
|
||||
return skipHeadSVG(head);
|
||||
} else {
|
||||
return block;
|
||||
let headSVG, headVariables = [];
|
||||
for (let item of block.value) {
|
||||
if (item.name === 'svg') {
|
||||
headSVG = item;
|
||||
}
|
||||
if (item.variable) {
|
||||
headVariables.push(item);
|
||||
}
|
||||
}
|
||||
if (headSVG) {
|
||||
headSVG.value.push(...headVariables);
|
||||
return headSVG;
|
||||
}
|
||||
return block;
|
||||
}
|
||||
|
||||
function parse(source, root) {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
import { is_empty } from '../utils/index.js';
|
||||
import { scan, iterator } from './tokenizer.js';
|
||||
|
||||
function parse(input, option = {symbol: ',', noSpace: false}) {
|
||||
function parse(input, option = {symbol: ',', noSpace: false, verbose: false }) {
|
||||
let group = [];
|
||||
let skip = false;
|
||||
let tokens = [];
|
||||
let parenStack = [];
|
||||
let quoteStack = [];
|
||||
let lastGroupName = '';
|
||||
|
||||
if (is_empty(input)) {
|
||||
return group;
|
||||
|
|
@ -15,11 +16,25 @@ function parse(input, option = {symbol: ',', noSpace: false}) {
|
|||
let iter = iterator(scan(input));
|
||||
|
||||
function isSeperator(token) {
|
||||
let symbol = option.symbol || ',';
|
||||
if (option.noSpace) {
|
||||
return token.isSymbol(symbol);
|
||||
let symbol = option.symbol || [','];
|
||||
if (!Array.isArray(symbol)) {
|
||||
symbol = [symbol];
|
||||
}
|
||||
if (option.noSpace) {
|
||||
return token.isSymbol(...symbol);
|
||||
}
|
||||
return token.isSymbol(...symbol) || token.isSpace();
|
||||
}
|
||||
|
||||
function addGroup(tokens) {
|
||||
let value = joinTokens(tokens);
|
||||
if (option.verbose) {
|
||||
if (lastGroupName.length || value.length) {
|
||||
group.push({ group: lastGroupName, value });
|
||||
}
|
||||
} else {
|
||||
group.push(value);
|
||||
}
|
||||
return token.isSymbol(symbol) || token.isSpace();
|
||||
}
|
||||
|
||||
while (iter.next()) {
|
||||
|
|
@ -43,7 +58,9 @@ function parse(input, option = {symbol: ',', noSpace: false}) {
|
|||
if (isNextSpace || isPrevSpace) continue;
|
||||
}
|
||||
if (emptyStack && isSeperator(curr)) {
|
||||
group.push(joinTokens(tokens));
|
||||
let groupName = lastGroupName;
|
||||
addGroup(tokens);
|
||||
lastGroupName = curr.value;
|
||||
tokens = [];
|
||||
} else {
|
||||
tokens.push(curr);
|
||||
|
|
@ -51,7 +68,7 @@ function parse(input, option = {symbol: ',', noSpace: false}) {
|
|||
}
|
||||
|
||||
if (tokens.length) {
|
||||
group.push(joinTokens(tokens));
|
||||
addGroup(tokens);
|
||||
}
|
||||
|
||||
return group;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ const symbols = [
|
|||
':', ';', ',', '(', ')', '[', ']',
|
||||
'{', '}', 'π', '±', '+', '-', '*',
|
||||
'/', '%', '"', "'", '`', '@', '=',
|
||||
'^',
|
||||
];
|
||||
|
||||
const is = {
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ const map_top_bottom = {
|
|||
export default add_alias({
|
||||
|
||||
size(value, { is_special_selector, grid }) {
|
||||
let [w, h = w] = parse_value_group(value);
|
||||
let [w, h = w, ratio] = parse_value_group(value);
|
||||
if (is_preset(w)) {
|
||||
[w, h] = get_preset(w, h);
|
||||
}
|
||||
|
|
@ -31,11 +31,22 @@ export default add_alias({
|
|||
width: ${ w };
|
||||
height: ${ h };
|
||||
`;
|
||||
if (is_special_selector) {
|
||||
if (w === 'auto' || h === 'auto') {
|
||||
styles += `aspect-ratio: ${ grid.ratio };`;
|
||||
if (w === 'auto' || h === 'auto') {
|
||||
if (ratio) {
|
||||
if (/^\(.+\)$/.test(ratio)) {
|
||||
ratio = ratio.substring(1, ratio.length - 1);
|
||||
} else if (!/^calc/.test(ratio)) {
|
||||
ratio = `calc(${ratio})`;
|
||||
}
|
||||
if (!is_special_selector) {
|
||||
styles += `aspect-ratio: ${ ratio };`;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (is_special_selector) {
|
||||
styles += `aspect-ratio: ${ ratio || grid.ratio };`;
|
||||
}
|
||||
}
|
||||
if (!is_special_selector) {
|
||||
styles += `
|
||||
--internal-cell-width: ${ w };
|
||||
--internal-cell-height: ${ h };
|
||||
|
|
@ -44,7 +55,7 @@ export default add_alias({
|
|||
return styles;
|
||||
},
|
||||
|
||||
position(value, { extra }) {
|
||||
place(value, { extra }) {
|
||||
let [left, top = '50%'] = parse_value_group(value);
|
||||
left = map_left_right[left] || left;
|
||||
top = map_top_bottom[top] || top;
|
||||
|
|
@ -60,16 +71,45 @@ export default add_alias({
|
|||
margin-top: calc(${ ch } / -2);
|
||||
grid-area: unset;
|
||||
--plot-angle: ${ extra || 0 };
|
||||
transform: rotate(${ extra || 0 }deg);
|
||||
rotate: ${ extra || 0 }deg;
|
||||
`;
|
||||
},
|
||||
|
||||
grid(value, options) {
|
||||
let [grid, size] = parse_value_group(value, { symbol: '/', noSpace: true });
|
||||
return {
|
||||
grid: parse_grid(grid, options.max_grid),
|
||||
size: size ? this.size(size, options) : ''
|
||||
let result = {
|
||||
clip: true,
|
||||
};
|
||||
if (/no\-*clip/i.test(value)) {
|
||||
result.clip = false;
|
||||
value = value.replace(/no\-*clip/i, '');
|
||||
}
|
||||
let groups = parse_value_group(value, {
|
||||
symbol: ['/', '+', '*', '|', '-'],
|
||||
noSpace: true,
|
||||
verbose: true
|
||||
});
|
||||
for (let { group, value } of groups) {
|
||||
if (group === '+') result.scale = value;
|
||||
if (group === '*') result.rotate = value;
|
||||
if (group === '/') {
|
||||
if (result.size === undefined) result.size = this.size(value, options);
|
||||
else result.fill = value;
|
||||
}
|
||||
if ((group === '|' || group == '-' || group == '') && !result.grid) {
|
||||
result.grid = parse_grid(value, options.max_grid);
|
||||
if (group === '|') {
|
||||
result.flexColumn = true;
|
||||
}
|
||||
if (group === '-') {
|
||||
result.flexRow = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
gap(value) {
|
||||
return value;
|
||||
},
|
||||
|
||||
seed(value) {
|
||||
|
|
@ -91,11 +131,13 @@ export default add_alias({
|
|||
}
|
||||
},
|
||||
|
||||
content(value) {
|
||||
return value;
|
||||
},
|
||||
|
||||
}, {
|
||||
'place': 'position',
|
||||
|
||||
// legacy names.
|
||||
'place-cell': 'position',
|
||||
'offset': 'position',
|
||||
|
||||
'place-cell': 'place',
|
||||
'offset': 'place',
|
||||
'position': 'place',
|
||||
});
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ function range(start, stop, step) {
|
|||
|| (step < 0 && start > stop)) {
|
||||
range.push(start);
|
||||
start += step;
|
||||
if (count++ >= 1000) break;
|
||||
if (count++ >= 65535) break;
|
||||
}
|
||||
if (!range.length) range.push(old);
|
||||
return range;
|
||||
|
|
@ -57,17 +57,43 @@ function lazy(fn) {
|
|||
}
|
||||
|
||||
function sequence(count, fn) {
|
||||
let [x, y = 1] = String(count).split('x');
|
||||
x = clamp(Math.ceil(x) || 1, 1, 65536);
|
||||
y = clamp(Math.ceil(y) || 1, 1, 65536);
|
||||
let [x, y = 1] = String(count).split(/[x-]/);
|
||||
let [cx, cy] = [Math.ceil(x), Math.ceil(y)];
|
||||
if (is_invalid_number(cx)) cx = 1;
|
||||
if (is_invalid_number(cy)) cy = 1;
|
||||
x = clamp(cx, 0, 65536);
|
||||
y = clamp(cy, 0, 65536);
|
||||
let max = x * y;
|
||||
let ret = [];
|
||||
let index = 1;
|
||||
for (let i = 1; i <= y; ++i) {
|
||||
for (let j = 1; j <= x; ++j) {
|
||||
ret.push(fn(index++, j, i, max, x, y));
|
||||
|
||||
if (/x/.test(count)) {
|
||||
for (let i = 1; i <= y; ++i) {
|
||||
for (let j = 1; j <= x; ++j) {
|
||||
ret.push(fn(index++, j, i, max, x, y, index));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (/-/.test(count)) {
|
||||
max = Math.abs(x - y) + 1;
|
||||
if (x <= y) {
|
||||
for (let i = x; i <= y; ++i) {
|
||||
ret.push(fn(i, i, 1, max, max, 1, index++));
|
||||
}
|
||||
} else {
|
||||
for (let i = x; i >= y; --i) {
|
||||
ret.push(fn(i, i, 1, max, max, 1, index++));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
for (let i = 1; i <= x; ++i) {
|
||||
ret.push(fn(i, i, 1, x, x, 1, index++));
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -130,6 +130,30 @@ test('multiple content', t => {
|
|||
);
|
||||
});
|
||||
|
||||
test('remove quotes around text', t => {
|
||||
compare(t,
|
||||
`text {
|
||||
content: "hello";
|
||||
}`,
|
||||
trim(`
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<text>hello</text>
|
||||
</svg>
|
||||
`)
|
||||
);
|
||||
|
||||
compare(t,
|
||||
`text {
|
||||
content: 'hello';
|
||||
}`,
|
||||
trim(`
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<text>hello</text>
|
||||
</svg>
|
||||
`)
|
||||
);
|
||||
});
|
||||
|
||||
test('style tag', t => {
|
||||
compare(t,
|
||||
`svg {
|
||||
|
|
@ -138,6 +162,8 @@ test('style tag', t => {
|
|||
circle {
|
||||
fill: red;
|
||||
}
|
||||
circle:nth-child(1) {
|
||||
}
|
||||
}
|
||||
}`,
|
||||
trim(`
|
||||
|
|
@ -145,6 +171,7 @@ test('style tag', t => {
|
|||
<circle></circle>
|
||||
<style>
|
||||
circle{fill:red;}
|
||||
circle:nth-child(1){}
|
||||
</style>
|
||||
</svg>
|
||||
`)
|
||||
|
|
@ -259,3 +286,28 @@ test('do not group elements for empty id', t => {
|
|||
`)
|
||||
);
|
||||
});
|
||||
|
||||
test('Normalize quoted attribute values', t => {
|
||||
compare(t,
|
||||
`circle {
|
||||
name: "hello";
|
||||
}
|
||||
`,
|
||||
trim(`
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<circle name="hello"></circle>
|
||||
</svg>
|
||||
`)
|
||||
);
|
||||
compare(t,
|
||||
`circle {
|
||||
name: 'hello';
|
||||
}
|
||||
`,
|
||||
trim(`
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<circle name="hello"></circle>
|
||||
</svg>
|
||||
`)
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -88,7 +88,6 @@ test('edge cases', t => {
|
|||
|
||||
});
|
||||
|
||||
|
||||
test('statement', t => {
|
||||
|
||||
compare(t, 'viewBox: 0 0 0 10', {
|
||||
|
|
@ -97,7 +96,24 @@ test('statement', t => {
|
|||
value: [{
|
||||
type: 'statement',
|
||||
name: 'viewBox',
|
||||
value: '0 0 0 10'
|
||||
value: '0 0 0 10',
|
||||
detail: {
|
||||
value: [0, 0, 0, 10]
|
||||
}
|
||||
}]
|
||||
});
|
||||
|
||||
compare(t, 'viewBox: 0 0 10 10 padding .2', {
|
||||
name: 'svg',
|
||||
type: 'block',
|
||||
value: [{
|
||||
type: 'statement',
|
||||
name: 'viewBox',
|
||||
value: '0 0 10 10 padding .2',
|
||||
detail: {
|
||||
value: [0, 0, 10, 10],
|
||||
padding: .2
|
||||
}
|
||||
}]
|
||||
});
|
||||
|
||||
|
|
@ -116,7 +132,6 @@ test('statement', t => {
|
|||
|
||||
});
|
||||
|
||||
|
||||
test('group property', t => {
|
||||
compare(t, 'cx, cy: 5', {
|
||||
name: 'svg',
|
||||
|
|
@ -135,10 +150,8 @@ test('group property', t => {
|
|||
{ type: 'statement', origin: { name: ['cx', 'cy'], value: '5 6' }, name: 'cy', value: '6'},
|
||||
]
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
test('semicolon separated values', t => {
|
||||
compare(t, 'values: 60; 100; 180', {
|
||||
name: 'svg',
|
||||
|
|
@ -158,7 +171,6 @@ test('semicolon separated values', t => {
|
|||
});
|
||||
});
|
||||
|
||||
|
||||
test('colon separated properties', t => {
|
||||
compare(t, 'xlink:href: url(#app)', {
|
||||
name: 'svg',
|
||||
|
|
@ -177,7 +189,6 @@ test('colon separated properties', t => {
|
|||
});
|
||||
});
|
||||
|
||||
|
||||
test('block names', t => {
|
||||
compare(t, 'g circle { } ', {
|
||||
name: 'svg',
|
||||
|
|
@ -230,7 +241,6 @@ test('block names', t => {
|
|||
}
|
||||
]
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
test('id expand', t => {
|
||||
|
|
@ -272,7 +282,6 @@ test('id expand', t => {
|
|||
]
|
||||
}]
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
test('empty id expand', t => {
|
||||
|
|
@ -312,7 +321,7 @@ test('statement end on quotes', t => {
|
|||
value: [{
|
||||
name: 'content',
|
||||
type: 'statement',
|
||||
value: ''
|
||||
value: '\'\''
|
||||
}]
|
||||
}, {
|
||||
name: 'g',
|
||||
|
|
@ -322,6 +331,24 @@ test('statement end on quotes', t => {
|
|||
});
|
||||
});
|
||||
|
||||
test('content values', t => {
|
||||
compare(t, `
|
||||
text { content: "world;}" }
|
||||
`, {
|
||||
type: 'block',
|
||||
name: 'svg',
|
||||
value: [{
|
||||
type: 'block',
|
||||
name: 'text',
|
||||
value: [{
|
||||
name: 'content',
|
||||
type: 'statement',
|
||||
value: '"world;}"'
|
||||
}]
|
||||
}]
|
||||
});
|
||||
});
|
||||
|
||||
test('times syntax', t => {
|
||||
compare(t, `
|
||||
circle*10 {}
|
||||
|
|
@ -350,8 +377,6 @@ test('times syntax', t => {
|
|||
value: []
|
||||
}]
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
test('complex values with parens', t => {
|
||||
|
|
@ -373,3 +398,27 @@ test('complex values with parens', t => {
|
|||
}]
|
||||
});
|
||||
});
|
||||
|
||||
test('svg variable', t => {
|
||||
compare(t, '--a: 1', {
|
||||
name: 'svg',
|
||||
type: 'block',
|
||||
value: [{
|
||||
type: 'statement',
|
||||
name: '--a',
|
||||
value: '1',
|
||||
variable: true
|
||||
}]
|
||||
});
|
||||
|
||||
compare(t, '--a: 1; svg {}', {
|
||||
name: 'svg',
|
||||
type: 'block',
|
||||
value: [{
|
||||
type: 'statement',
|
||||
name: '--a',
|
||||
value: '1',
|
||||
variable: true
|
||||
}]
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -71,3 +71,29 @@ test('grid value', t => {
|
|||
|
||||
});
|
||||
|
||||
test('space as separator', t => {
|
||||
|
||||
compare.use(input => {
|
||||
return parseValueGroup(input, { symbol: ' ' });
|
||||
});
|
||||
|
||||
compare(t, '5 100%', ['5', '100%']);
|
||||
compare(t, '5,100%', ['5,100%']);
|
||||
compare(t, '5, 100% 5', ['5,100%', '5']);
|
||||
compare(t, '5, 100% 5 8', ['5,100%', '5', '8']);
|
||||
|
||||
});
|
||||
|
||||
test('verbose option', t => {
|
||||
|
||||
compare.use(input => {
|
||||
return parseValueGroup(input, { symbol: ['v', 'h'], noSpace: true, verbose: true });
|
||||
});
|
||||
|
||||
compare(t, 'v 10 h -10 v 5', [
|
||||
{ group: 'v', value: '10' },
|
||||
{ group: 'h', value: '-10' },
|
||||
{ group: 'v', value: '5' },
|
||||
]);
|
||||
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue