Skip to content

Commit

Permalink
Support sort function in matchVariant (#9423)
Browse files Browse the repository at this point in the history
* support `sort` function in `matchVariant`

This will ensure that we can sort arbitrary variant values (and
hardcoded values) to ensure the order.

* update changelog
  • Loading branch information
RobinMalfait committed Sep 26, 2022
1 parent 19b86e6 commit 26cab53
Show file tree
Hide file tree
Showing 5 changed files with 471 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -39,6 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Don't emit generated utilities with invalid uses of theme functions ([#9319](https://github.com/tailwindlabs/tailwindcss/pull/9319))
- Revert change that only listened for stdin close on TTYs ([#9331](https://github.com/tailwindlabs/tailwindcss/pull/9331))
- Ignore unset values (like `null` or `undefined`) when resolving the classList for intellisense ([#9385](https://github.com/tailwindlabs/tailwindcss/pull/9385))
- Support `sort` function in `matchVariant` ([#9423](https://github.com/tailwindlabs/tailwindcss/pull/9423))

## [3.1.8] - 2022-08-05

Expand Down
6 changes: 5 additions & 1 deletion src/lib/generateRules.js
Expand Up @@ -294,7 +294,11 @@ function applyVariant(variant, matches, context) {
let withOffset = [
{
...meta,
sort: context.offsets.applyVariantOffset(meta.sort, variantSort),
sort: context.offsets.applyVariantOffset(
meta.sort,
variantSort,
Object.assign({ value: args }, context.variantOptions.get(variant))
),
collectedFormats: (meta.collectedFormats ?? []).concat(collectedFormats),
isArbitraryVariant: isArbitraryValue(variant),
},
Expand Down
25 changes: 23 additions & 2 deletions src/lib/offsets.js
Expand Up @@ -6,6 +6,13 @@ import bigSign from '../util/bigSign'
* @typedef {'base' | 'defaults' | 'components' | 'utilities' | 'variants' | 'user'} Layer
*/

/**
* @typedef {object} VariantOption
* @property {number} id An unique identifier to identify `matchVariant`
* @property {function | undefined} sort The sort function
* @property {string} value The value we want to compare
*/

/**
* @typedef {object} RuleOffset
* @property {Layer} layer The layer that this rule belongs to
Expand All @@ -14,6 +21,7 @@ import bigSign from '../util/bigSign'
* @property {bigint} variants Dynamic size. 1 bit per registered variant. 0n means no variants
* @property {bigint} parallelIndex Rule index for the parallel variant. 0 if not applicable.
* @property {bigint} index Index of the rule / utility in it's given *parent* layer. Monotonically increasing.
* @property {VariantOption[]} options Some information on how we can sort arbitrary variants
*/

export class Offsets {
Expand Down Expand Up @@ -77,6 +85,7 @@ export class Offsets {
variants: 0n,
parallelIndex: 0n,
index: this.offsets[layer]++,
options: [],
}
}

Expand Down Expand Up @@ -112,14 +121,16 @@ export class Offsets {
/**
* @param {RuleOffset} rule
* @param {RuleOffset} variant
* @param {VariantOption} options
* @returns {RuleOffset}
*/
applyVariantOffset(rule, variant) {
applyVariantOffset(rule, variant, options) {
return {
...rule,
layer: 'variants',
parentLayer: rule.layer === 'variants' ? rule.parentLayer : rule.layer,
variants: rule.variants | variant.variants,
options: options.sort ? [].concat(options, rule.options) : rule.options,

// TODO: Technically this is wrong. We should be handling parallel index on a per variant basis.
// We'll take the max of all the parallel indexes for now.
Expand Down Expand Up @@ -151,7 +162,7 @@ export class Offsets {
* @param {(name: string) => number} getLength
*/
recordVariants(variants, getLength) {
for (const variant of variants) {
for (let variant of variants) {
this.recordVariant(variant, getLength(variant))
}
}
Expand Down Expand Up @@ -193,6 +204,16 @@ export class Offsets {
return this.layerPositions[a.layer] - this.layerPositions[b.layer]
}

// Sort based on the sorting function
for (let aOptions of a.options) {
for (let bOptions of b.options) {
if (aOptions.id !== bOptions.id) continue
if (!aOptions.sort || !bOptions.sort) continue
let result = aOptions.sort(aOptions.value, bOptions.value)
if (result !== 0) return result
}
}

// Sort variants in the order they were registered
if (a.variants !== b.variants) {
return a.variants - b.variants
Expand Down
9 changes: 7 additions & 2 deletions src/lib/setupContextUtils.js
Expand Up @@ -496,19 +496,23 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs

insertInto(variantList, variantName, options)
variantMap.set(variantName, variantFunctions)
context.variantOptions.set(variantName, options)
},
}

if (flagEnabled(tailwindConfig, 'matchVariant')) {
let variantIdentifier = 0
api.matchVariant = function (variant, variantFn, options) {
let id = ++variantIdentifier // A unique identifier that "groups" these variables together.

for (let [key, value] of Object.entries(options?.values ?? {})) {
api.addVariant(`${variant}-${key}`, variantFn({ value }))
api.addVariant(`${variant}-${key}`, variantFn({ value }), { ...options, value, id })
}

api.addVariant(
variant,
Object.assign(({ args }) => variantFn({ value: args }), { [MATCH_VARIANT]: true }),
options
{ ...options, id }
)
}
}
Expand Down Expand Up @@ -919,6 +923,7 @@ export function createContext(tailwindConfig, changedContent = [], root = postcs
changedContent: changedContent,
variantMap: new Map(),
stylesheetCache: null,
variantOptions: new Map(),

markInvalidUtilityCandidate: (candidate) => markInvalidUtilityCandidate(context, candidate),
markInvalidUtilityNode: (node) => markInvalidUtilityNode(context, node),
Expand Down

0 comments on commit 26cab53

Please sign in to comment.