Skip to content

Commit

Permalink
Add dynamic negative value opt-in (#5713)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
bradlc committed Oct 6, 2021
1 parent e2ad6bc commit 8ec9497
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 80 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
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
34 changes: 17 additions & 17 deletions src/util/pluginUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ function resolveArbitraryValue(modifier, validate) {
return normalize(value)
}

function asNegativeValue(modifier, lookup, validate) {
function asNegativeValue(modifier, lookup = {}, validate) {
let positiveValue = lookup[modifier]

if (positiveValue !== undefined) {
Expand All @@ -200,15 +200,15 @@ function asNegativeValue(modifier, lookup, validate) {
}
}

export function asValue(modifier, lookup = {}, { validate = () => true } = {}) {
let value = lookup[modifier]
export function asValue(modifier, options = {}, { validate = () => true } = {}) {
let value = options.values?.[modifier]

if (value !== undefined) {
return value
}

if (modifier.startsWith('-')) {
return asNegativeValue(modifier.slice(1), lookup, validate)
if (options.supportsNegativeValues && modifier.startsWith('-')) {
return asNegativeValue(modifier.slice(1), options.values, validate)
}

return resolveArbitraryValue(modifier, validate)
Expand All @@ -228,16 +228,16 @@ function splitAlpha(modifier) {
return [modifier.slice(0, slashIdx), modifier.slice(slashIdx + 1)]
}

export function asColor(modifier, lookup = {}, tailwindConfig = {}) {
if (lookup[modifier] !== undefined) {
return lookup[modifier]
export function asColor(modifier, options = {}, { tailwindConfig = {} } = {}) {
if (options.values?.[modifier] !== undefined) {
return options.values?.[modifier]
}

let [color, alpha] = splitAlpha(modifier)

if (alpha !== undefined) {
let normalizedColor =
lookup[color] ?? (isArbitraryValue(color) ? color.slice(1, -1) : undefined)
options.values?.[color] ?? (isArbitraryValue(color) ? color.slice(1, -1) : undefined)

if (normalizedColor === undefined) {
return undefined
Expand All @@ -254,16 +254,16 @@ export function asColor(modifier, lookup = {}, tailwindConfig = {}) {
return withAlphaValue(normalizedColor, tailwindConfig.theme.opacity[alpha])
}

return asValue(modifier, lookup, { validate: validateColor })
return asValue(modifier, options, { validate: validateColor })
}

export function asLookupValue(modifier, lookup = {}) {
return lookup[modifier]
export function asLookupValue(modifier, options = {}) {
return options.values?.[modifier]
}

function guess(validate) {
return (modifier, lookup) => {
return asValue(modifier, lookup, { validate })
return (modifier, options) => {
return asValue(modifier, options, { validate })
}
}

Expand Down Expand Up @@ -292,7 +292,7 @@ function splitAtFirst(input, delim) {
return [input.slice(0, idx), input.slice(idx + 1)]
}

export function coerceValue(types, modifier, values, tailwindConfig) {
export function coerceValue(types, modifier, options, tailwindConfig) {
if (isArbitraryValue(modifier)) {
let [explicitType, value] = splitAtFirst(modifier.slice(1, -1), ':')

Expand All @@ -301,13 +301,13 @@ export function coerceValue(types, modifier, values, tailwindConfig) {
}

if (value.length > 0 && supportedTypes.includes(explicitType)) {
return [asValue(`[${value}]`, values, tailwindConfig), explicitType]
return [asValue(`[${value}]`, options), explicitType]
}
}

// Find first matching type
for (let type of [].concat(types)) {
let result = typeMap[type](modifier, values, tailwindConfig)
let result = typeMap[type](modifier, options, { tailwindConfig })
if (result) return [result, type]
}

Expand Down
Loading

0 comments on commit 8ec9497

Please sign in to comment.