Skip to content

Commit

Permalink
Make negative values a first-class feature, rather than theme-driven (#…
Browse files Browse the repository at this point in the history
…5709)

* WIP

* Add failing test for negating default values

* Add dynamic negative value opt-in (#5713)

* Add `supportsNegativeValues` plugin option

* Update `getClassList` to support dynamic negative values

* Add test for using a negative scale value with a plugin that does not support dynamic negative values

* Support dynamic negation of `DEFAULT` values (#5718)

* Add test case

Co-authored-by: Brad Cornes <[email protected]>
  • Loading branch information
adamwathan and bradlc authored Oct 6, 2021
1 parent b661614 commit 97062c3
Show file tree
Hide file tree
Showing 13 changed files with 491 additions and 137 deletions.
145 changes: 88 additions & 57 deletions src/corePlugins.js
Original file line number Diff line number Diff line change
Expand Up @@ -529,19 +529,23 @@ export let corePlugins = {
})
},

inset: createUtilityPlugin('inset', [
['inset', ['top', 'right', 'bottom', 'left']],
inset: createUtilityPlugin(
'inset',
[
['inset-x', ['left', 'right']],
['inset-y', ['top', 'bottom']],
],
[
['top', ['top']],
['right', ['right']],
['bottom', ['bottom']],
['left', ['left']],
['inset', ['top', 'right', 'bottom', 'left']],
[
['inset-x', ['left', 'right']],
['inset-y', ['top', 'bottom']],
],
[
['top', ['top']],
['right', ['right']],
['bottom', ['bottom']],
['left', ['left']],
],
],
]),
{ supportsNegativeValues: true }
),

isolation: ({ addUtilities }) => {
addUtilities({
Expand All @@ -550,8 +554,8 @@ export let corePlugins = {
})
},

zIndex: createUtilityPlugin('zIndex', [['z', ['zIndex']]]),
order: createUtilityPlugin('order'),
zIndex: createUtilityPlugin('zIndex', [['z', ['zIndex']]], { supportsNegativeValues: true }),
order: createUtilityPlugin('order', undefined, { supportsNegativeValues: true }),
gridColumn: createUtilityPlugin('gridColumn', [['col', ['gridColumn']]]),
gridColumnStart: createUtilityPlugin('gridColumnStart', [['col-start', ['gridColumnStart']]]),
gridColumnEnd: createUtilityPlugin('gridColumnEnd', [['col-end', ['gridColumnEnd']]]),
Expand All @@ -576,19 +580,23 @@ export let corePlugins = {
})
},

margin: createUtilityPlugin('margin', [
['m', ['margin']],
[
['mx', ['margin-left', 'margin-right']],
['my', ['margin-top', 'margin-bottom']],
],
margin: createUtilityPlugin(
'margin',
[
['mt', ['margin-top']],
['mr', ['margin-right']],
['mb', ['margin-bottom']],
['ml', ['margin-left']],
['m', ['margin']],
[
['mx', ['margin-left', 'margin-right']],
['my', ['margin-top', 'margin-bottom']],
],
[
['mt', ['margin-top']],
['mr', ['margin-right']],
['mb', ['margin-bottom']],
['ml', ['margin-left']],
],
],
]),
{ supportsNegativeValues: true }
),

boxSizing: ({ addUtilities }) => {
addUtilities({
Expand Down Expand Up @@ -653,33 +661,48 @@ export let corePlugins = {
},

transformOrigin: createUtilityPlugin('transformOrigin', [['origin', ['transformOrigin']]]),
translate: createUtilityPlugin('translate', [
translate: createUtilityPlugin(
'translate',
[
[
'translate-x',
[['@defaults transform', {}], '--tw-translate-x', ['transform', 'var(--tw-transform)']],
],
[
'translate-y',
[['@defaults transform', {}], '--tw-translate-y', ['transform', 'var(--tw-transform)']],
[
'translate-x',
[['@defaults transform', {}], '--tw-translate-x', ['transform', 'var(--tw-transform)']],
],
[
'translate-y',
[['@defaults transform', {}], '--tw-translate-y', ['transform', 'var(--tw-transform)']],
],
],
],
]),
rotate: createUtilityPlugin('rotate', [
['rotate', [['@defaults transform', {}], '--tw-rotate', ['transform', 'var(--tw-transform)']]],
]),
skew: createUtilityPlugin('skew', [
{ supportsNegativeValues: true }
),
rotate: createUtilityPlugin(
'rotate',
[
[
'skew-x',
[['@defaults transform', {}], '--tw-skew-x', ['transform', 'var(--tw-transform)']],
'rotate',
[['@defaults transform', {}], '--tw-rotate', ['transform', 'var(--tw-transform)']],
],
],
{ supportsNegativeValues: true }
),
skew: createUtilityPlugin(
'skew',
[
[
'skew-y',
[['@defaults transform', {}], '--tw-skew-y', ['transform', 'var(--tw-transform)']],
[
'skew-x',
[['@defaults transform', {}], '--tw-skew-x', ['transform', 'var(--tw-transform)']],
],
[
'skew-y',
[['@defaults transform', {}], '--tw-skew-y', ['transform', 'var(--tw-transform)']],
],
],
],
]),
{ supportsNegativeValues: true }
),
scale: createUtilityPlugin('scale', [
[
'scale',
Expand Down Expand Up @@ -859,19 +882,23 @@ export let corePlugins = {
})
},

scrollMargin: createUtilityPlugin('scrollMargin', [
['scroll-m', ['scroll-margin']],
[
['scroll-mx', ['scroll-margin-left', 'scroll-margin-right']],
['scroll-my', ['scroll-margin-top', 'scroll-margin-bottom']],
],
scrollMargin: createUtilityPlugin(
'scrollMargin',
[
['scroll-mt', ['scroll-margin-top']],
['scroll-mr', ['scroll-margin-right']],
['scroll-mb', ['scroll-margin-bottom']],
['scroll-ml', ['scroll-margin-left']],
['scroll-m', ['scroll-margin']],
[
['scroll-mx', ['scroll-margin-left', 'scroll-margin-right']],
['scroll-my', ['scroll-margin-top', 'scroll-margin-bottom']],
],
[
['scroll-mt', ['scroll-margin-top']],
['scroll-mr', ['scroll-margin-right']],
['scroll-mb', ['scroll-margin-bottom']],
['scroll-ml', ['scroll-margin-left']],
],
],
]),
{ supportsNegativeValues: true }
),

scrollPadding: createUtilityPlugin('scrollPadding', [
['scroll-p', ['scroll-padding']],
Expand Down Expand Up @@ -1069,7 +1096,7 @@ export let corePlugins = {
}
},
},
{ values: theme('space') }
{ values: theme('space'), supportsNegativeValues: true }
)

addUtilities({
Expand Down Expand Up @@ -1641,7 +1668,9 @@ export let corePlugins = {
})
},

textIndent: createUtilityPlugin('textIndent', [['indent', ['text-indent']]]),
textIndent: createUtilityPlugin('textIndent', [['indent', ['text-indent']]], {
supportsNegativeValues: true,
}),

verticalAlign: ({ addUtilities, matchUtilities }) => {
addUtilities({
Expand Down Expand Up @@ -1730,7 +1759,9 @@ export let corePlugins = {
},

lineHeight: createUtilityPlugin('lineHeight', [['leading', ['lineHeight']]]),
letterSpacing: createUtilityPlugin('letterSpacing', [['tracking', ['letterSpacing']]]),
letterSpacing: createUtilityPlugin('letterSpacing', [['tracking', ['letterSpacing']]], {
supportsNegativeValues: true,
}),

textColor: ({ matchUtilities, theme, corePlugins }) => {
matchUtilities(
Expand Down Expand Up @@ -2099,7 +2130,7 @@ export let corePlugins = {
}
},
},
{ values: theme('hueRotate') }
{ values: theme('hueRotate'), supportsNegativeValues: true }
)
},

Expand Down Expand Up @@ -2250,7 +2281,7 @@ export let corePlugins = {
}
},
},
{ values: theme('backdropHueRotate') }
{ values: theme('backdropHueRotate'), supportsNegativeValues: true }
)
},

Expand Down
6 changes: 5 additions & 1 deletion src/lib/generateRules.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,10 @@ function* resolveMatchedPlugins(classCandidate, context) {
candidatePrefix = twConfigPrefix + candidatePrefix.slice(twConfigPrefixLen + 1)
}

if (negative && context.candidateRuleMap.has(candidatePrefix)) {
yield [context.candidateRuleMap.get(candidatePrefix), '-DEFAULT']
}

for (let [prefix, modifier] of candidatePermutations(candidatePrefix)) {
if (context.candidateRuleMap.has(prefix)) {
yield [context.candidateRuleMap.get(prefix), negative ? `-${modifier}` : modifier]
Expand Down Expand Up @@ -238,7 +242,7 @@ function* resolveMatches(candidate, context) {
}
}
// Only process static plugins on exact matches
else if (modifier === 'DEFAULT') {
else if (modifier === 'DEFAULT' || modifier === '-DEFAULT') {
let ruleSet = plugin
let [rules, options] = parseRules(ruleSet, context.postCssNodeCache)
for (let rule of rules) {
Expand Down
15 changes: 11 additions & 4 deletions src/lib/setupContextUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import * as sharedState from './sharedState'
import { env } from './sharedState'
import { toPath } from '../util/toPath'
import log from '../util/log'
import negateValue from '../util/negateValue'

function insertInto(list, value, { before = [] } = {}) {
before = [].concat(before)
Expand Down Expand Up @@ -300,7 +301,7 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
function wrapped(modifier, { isOnlyPlugin }) {
let { type = 'any' } = options
type = [].concat(type)
let [value, coercedType] = coerceValue(type, modifier, options.values, tailwindConfig)
let [value, coercedType] = coerceValue(type, modifier, options, tailwindConfig)

if (value === undefined) {
return []
Expand Down Expand Up @@ -352,7 +353,7 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
function wrapped(modifier, { isOnlyPlugin }) {
let { type = 'any' } = options
type = [].concat(type)
let [value, coercedType] = coerceValue(type, modifier, options.values, tailwindConfig)
let [value, coercedType] = coerceValue(type, modifier, options, tailwindConfig)

if (value === undefined) {
return []
Expand Down Expand Up @@ -670,10 +671,16 @@ function registerPlugins(plugins, context) {
for (let util of classList) {
if (Array.isArray(util)) {
let [utilName, options] = util
let negativeClasses = []

for (let value of Object.keys(options?.values ?? {})) {
output.push(formatClass(utilName, value))
for (let [key, value] of Object.entries(options?.values ?? {})) {
output.push(formatClass(utilName, key))
if (options?.supportsNegativeValues && negateValue(value)) {
negativeClasses.push(formatClass(utilName, `-${key}`))
}
}

output.push(...negativeClasses)
} else {
output.push(util)
}
Expand Down
4 changes: 2 additions & 2 deletions src/util/createUtilityPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import transformThemeValue from './transformThemeValue'
export default function createUtilityPlugin(
themeKey,
utilityVariations = [[themeKey, [themeKey]]],
{ filterDefault = false, type = 'any' } = {}
{ filterDefault = false, ...options } = {}
) {
let transformValue = transformThemeValue(themeKey)
return function ({ matchUtilities, theme }) {
Expand All @@ -24,12 +24,12 @@ export default function createUtilityPlugin(
})
}, {}),
{
...options,
values: filterDefault
? Object.fromEntries(
Object.entries(theme(themeKey) ?? {}).filter(([modifier]) => modifier !== 'DEFAULT')
)
: theme(themeKey),
type,
}
)
}
Expand Down
2 changes: 1 addition & 1 deletion src/util/nameClass.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export function formatClass(classPrefix, key) {
return classPrefix
}

if (key === '-') {
if (key === '-' || key === '-DEFAULT') {
return `-${classPrefix}`
}

Expand Down
6 changes: 4 additions & 2 deletions src/util/negateValue.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
export default function (value) {
value = `${value}`

if (value === '0') {
return '0'
}

// Flip sign of numbers
if (/^[+-]?(\d+|\d*\.\d+)(e[+-]?\d+)?(%|\w+)?$/.test(value)) {
return value.replace(/^[+-]?/, (sign) => (sign === '-' ? '' : '-'))
Expand All @@ -9,6 +13,4 @@ export default function (value) {
if (value.includes('var(') || value.includes('calc(')) {
return `calc(${value} * -1)`
}

return value
}
Loading

0 comments on commit 97062c3

Please sign in to comment.