tailwindcss/src/util/parseBoxShadowValue.js
Robin Malfait be51739337
Arbitrary variants (#8299)
* register arbitrary variants

With the new `addVariant` API, we have a beautiful way of creating new
variants.

You can use it as:
```js
addVariant('children', '& > *')
```

Now you can use the `children:` variant. The API uses a `&` as a
reference for the candidate, which means that:
```html
children:pl-4
```

Will result in:
```css
.children\:pl-4 > * { .. }
```

Notice that the `&` was replaced by `.children\:pl-4`.

We can leverage this API to implement arbitrary variants, this means
that you can write those `&>*` (Notice that we don't have spaces) inside
a variant directly. An example of this can be:
```html
<ul class="[&>*]:underline">
  <li>A</li>
  <li>B</li>
  <li>C</li>
</ul>
```
Which generates the following css:
```css
.\[\&\>\*\]\:underline > * {
  text-decoration-line: underline;
}
```

Now all the children of the `ul` will have an `underline`. The selector
itself is a bit crazy since it contains the candidate which is the
selector itself, it is just escaped.

* add tests for arbitrary variants

This still requires some work to the `defaultExtractor` to make sure it
all works with existing code.

* update changelog

* Fix candidate detection for arbitrary variants

* Refactor

* Add support for at rules

* Add test for attribute selectors

* Fix test

* Add attribute selector support

* Split top-level comma parsing into a generalized splitting routine

We can now split on any character at the top level with any nesting. We don’t balance brackets directly here but this is probably “enough”

* Split variants by separator at the top-level only

This means that the separator has to be ouside of balanced brackets

* Fix extraction when using custom variant separators

* Support custom separators when top-level splitting variants

* Add a second multi-character separator test

* Split tests for at-rule and at-rule with selector changes

* Add nested at-rule tests

* Fix space-less at-rule parsing in addVariant

* Add test for using with `@apply`

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2022-05-08 12:24:59 -04:00

73 lines
1.8 KiB
JavaScript

import { splitAtTopLevelOnly } from './splitAtTopLevelOnly'
let KEYWORDS = new Set(['inset', 'inherit', 'initial', 'revert', 'unset'])
let SPACE = /\ +(?![^(]*\))/g // Similar to the one above, but with spaces instead.
let LENGTH = /^-?(\d+|\.\d+)(.*?)$/g
export function parseBoxShadowValue(input) {
let shadows = Array.from(splitAtTopLevelOnly(input, ','))
return shadows.map((shadow) => {
let value = shadow.trim()
let result = { raw: value }
let parts = value.split(SPACE)
let seen = new Set()
for (let part of parts) {
// Reset index, since the regex is stateful.
LENGTH.lastIndex = 0
// Keyword
if (!seen.has('KEYWORD') && KEYWORDS.has(part)) {
result.keyword = part
seen.add('KEYWORD')
}
// Length value
else if (LENGTH.test(part)) {
if (!seen.has('X')) {
result.x = part
seen.add('X')
} else if (!seen.has('Y')) {
result.y = part
seen.add('Y')
} else if (!seen.has('BLUR')) {
result.blur = part
seen.add('BLUR')
} else if (!seen.has('SPREAD')) {
result.spread = part
seen.add('SPREAD')
}
}
// Color or unknown
else {
if (!result.color) {
result.color = part
} else {
if (!result.unknown) result.unknown = []
result.unknown.push(part)
}
}
}
// Check if valid
result.valid = result.x !== undefined && result.y !== undefined
return result
})
}
export function formatBoxShadowValue(shadows) {
return shadows
.map((shadow) => {
if (!shadow.valid) {
return shadow.raw
}
return [shadow.keyword, shadow.x, shadow.y, shadow.blur, shadow.spread, shadow.color]
.filter(Boolean)
.join(' ')
})
.join(', ')
}