UEA-PRODEM

This commit is contained in:
2026-06-10 12:14:46 -03:00
parent f54126b9d8
commit 9947565694
5319 changed files with 148520 additions and 129332 deletions
+15 -1
View File
@@ -2,7 +2,7 @@ import { createClassGroupUtils } from './class-group-utils'
import { createLruCache } from './lru-cache'
import { createParseClassName } from './parse-class-name'
import { createSortModifiers } from './sort-modifiers'
import { AnyConfig } from './types'
import { AnyClassGroupIds, AnyConfig } from './types'
export type ConfigUtils = ReturnType<typeof createConfigUtils>
@@ -10,5 +10,19 @@ export const createConfigUtils = (config: AnyConfig) => ({
cache: createLruCache<string, string>(config.cacheSize),
parseClassName: createParseClassName(config),
sortModifiers: createSortModifiers(config),
postfixLookupClassGroupIds: createPostfixLookupClassGroupIds(config),
...createClassGroupUtils(config),
})
const createPostfixLookupClassGroupIds = (config: AnyConfig) => {
const lookup: Partial<Record<AnyClassGroupIds, true>> = Object.create(null)
const classGroupIds = config.postfixLookupClassGroups
if (classGroupIds) {
for (let i = 0; i < classGroupIds.length; i++) {
lookup[classGroupIds[i]!] = true
}
}
return lookup
}
+261 -36
View File
@@ -3,6 +3,7 @@ import { Config, DefaultClassGroupIds, DefaultThemeGroupIds } from './types'
import {
isAny,
isAnyNonArbitrary,
isArbitraryFamilyName,
isArbitraryImage,
isArbitraryLength,
isArbitraryNumber,
@@ -17,8 +18,11 @@ import {
isArbitraryVariablePosition,
isArbitraryVariableShadow,
isArbitraryVariableSize,
isArbitraryVariableWeight,
isArbitraryWeight,
isFraction,
isInteger,
isNamedContainerQuery,
isNumber,
isPercent,
isTshirtSize,
@@ -134,6 +138,33 @@ export const getDefaultConfig = () => {
'fit',
...scaleUnambiguousSpacing(),
] as const
const scaleSizingInline = () =>
[
isFraction,
'screen',
'full',
'dvw',
'lvw',
'svw',
'min',
'max',
'fit',
...scaleUnambiguousSpacing(),
] as const
const scaleSizingBlock = () =>
[
isFraction,
'screen',
'full',
'lh',
'dvh',
'lvh',
'svh',
'min',
'max',
'fit',
...scaleUnambiguousSpacing(),
] as const
const scaleColor = () => [themeColor, isArbitraryVariable, isArbitraryValue] as const
const scaleBgPosition = () =>
[
@@ -262,6 +293,18 @@ export const getDefaultConfig = () => {
* @deprecated since Tailwind CSS v4.0.0
*/
container: ['container'],
/**
* Container Type
* @see https://tailwindcss.com/docs/responsive-design#container-queries
*/
'container-type': [
{ '@container': ['', 'normal', 'size', isArbitraryVariable, isArbitraryValue] },
],
/**
* Container Name
* @see https://tailwindcss.com/docs/responsive-design#named-containers
*/
'container-named': [isNamedContainerQuery],
/**
* Columns
* @see https://tailwindcss.com/docs/columns
@@ -387,30 +430,60 @@ export const getDefaultConfig = () => {
*/
position: ['static', 'fixed', 'absolute', 'relative', 'sticky'],
/**
* Top / Right / Bottom / Left
* Inset
* @see https://tailwindcss.com/docs/top-right-bottom-left
*/
inset: [{ inset: scaleInset() }],
/**
* Right / Left
* Inset Inline
* @see https://tailwindcss.com/docs/top-right-bottom-left
*/
'inset-x': [{ 'inset-x': scaleInset() }],
/**
* Top / Bottom
* Inset Block
* @see https://tailwindcss.com/docs/top-right-bottom-left
*/
'inset-y': [{ 'inset-y': scaleInset() }],
/**
* Start
* Inset Inline Start
* @see https://tailwindcss.com/docs/top-right-bottom-left
* @todo class group will be renamed to `inset-s` in next major release
*/
start: [{ start: scaleInset() }],
start: [
{
'inset-s': scaleInset(),
/**
* @deprecated since Tailwind CSS v4.2.0 in favor of `inset-s-*` utilities.
* @see https://github.com/tailwindlabs/tailwindcss/pull/19613
*/
start: scaleInset(),
},
],
/**
* End
* Inset Inline End
* @see https://tailwindcss.com/docs/top-right-bottom-left
* @todo class group will be renamed to `inset-e` in next major release
*/
end: [
{
'inset-e': scaleInset(),
/**
* @deprecated since Tailwind CSS v4.2.0 in favor of `inset-e-*` utilities.
* @see https://github.com/tailwindlabs/tailwindcss/pull/19613
*/
end: scaleInset(),
},
],
/**
* Inset Block Start
* @see https://tailwindcss.com/docs/top-right-bottom-left
*/
end: [{ end: scaleInset() }],
'inset-bs': [{ 'inset-bs': scaleInset() }],
/**
* Inset Block End
* @see https://tailwindcss.com/docs/top-right-bottom-left
*/
'inset-be': [{ 'inset-be': scaleInset() }],
/**
* Top
* @see https://tailwindcss.com/docs/top-right-bottom-left
@@ -626,25 +699,35 @@ export const getDefaultConfig = () => {
*/
p: [{ p: scaleUnambiguousSpacing() }],
/**
* Padding X
* Padding Inline
* @see https://tailwindcss.com/docs/padding
*/
px: [{ px: scaleUnambiguousSpacing() }],
/**
* Padding Y
* Padding Block
* @see https://tailwindcss.com/docs/padding
*/
py: [{ py: scaleUnambiguousSpacing() }],
/**
* Padding Start
* Padding Inline Start
* @see https://tailwindcss.com/docs/padding
*/
ps: [{ ps: scaleUnambiguousSpacing() }],
/**
* Padding End
* Padding Inline End
* @see https://tailwindcss.com/docs/padding
*/
pe: [{ pe: scaleUnambiguousSpacing() }],
/**
* Padding Block Start
* @see https://tailwindcss.com/docs/padding
*/
pbs: [{ pbs: scaleUnambiguousSpacing() }],
/**
* Padding Block End
* @see https://tailwindcss.com/docs/padding
*/
pbe: [{ pbe: scaleUnambiguousSpacing() }],
/**
* Padding Top
* @see https://tailwindcss.com/docs/padding
@@ -671,25 +754,35 @@ export const getDefaultConfig = () => {
*/
m: [{ m: scaleMargin() }],
/**
* Margin X
* Margin Inline
* @see https://tailwindcss.com/docs/margin
*/
mx: [{ mx: scaleMargin() }],
/**
* Margin Y
* Margin Block
* @see https://tailwindcss.com/docs/margin
*/
my: [{ my: scaleMargin() }],
/**
* Margin Start
* Margin Inline Start
* @see https://tailwindcss.com/docs/margin
*/
ms: [{ ms: scaleMargin() }],
/**
* Margin End
* Margin Inline End
* @see https://tailwindcss.com/docs/margin
*/
me: [{ me: scaleMargin() }],
/**
* Margin Block Start
* @see https://tailwindcss.com/docs/margin
*/
mbs: [{ mbs: scaleMargin() }],
/**
* Margin Block End
* @see https://tailwindcss.com/docs/margin
*/
mbe: [{ mbe: scaleMargin() }],
/**
* Margin Top
* @see https://tailwindcss.com/docs/margin
@@ -740,6 +833,36 @@ export const getDefaultConfig = () => {
* @see https://tailwindcss.com/docs/width#setting-both-width-and-height
*/
size: [{ size: scaleSizing() }],
/**
* Inline Size
* @see https://tailwindcss.com/docs/width
*/
'inline-size': [{ inline: ['auto', ...scaleSizingInline()] }],
/**
* Min-Inline Size
* @see https://tailwindcss.com/docs/min-width
*/
'min-inline-size': [{ 'min-inline': ['auto', ...scaleSizingInline()] }],
/**
* Max-Inline Size
* @see https://tailwindcss.com/docs/max-width
*/
'max-inline-size': [{ 'max-inline': ['none', ...scaleSizingInline()] }],
/**
* Block Size
* @see https://tailwindcss.com/docs/height
*/
'block-size': [{ block: ['auto', ...scaleSizingBlock()] }],
/**
* Min-Block Size
* @see https://tailwindcss.com/docs/min-height
*/
'min-block-size': [{ 'min-block': ['auto', ...scaleSizingBlock()] }],
/**
* Max-Block Size
* @see https://tailwindcss.com/docs/max-height
*/
'max-block-size': [{ 'max-block': ['none', ...scaleSizingBlock()] }],
/**
* Width
* @see https://tailwindcss.com/docs/width
@@ -819,7 +942,11 @@ export const getDefaultConfig = () => {
* Font Weight
* @see https://tailwindcss.com/docs/font-weight
*/
'font-weight': [{ font: [themeFontWeight, isArbitraryVariable, isArbitraryNumber] }],
'font-weight': [
{
font: [themeFontWeight, isArbitraryVariableWeight, isArbitraryWeight],
},
],
/**
* Font Stretch
* @see https://tailwindcss.com/docs/font-stretch
@@ -845,7 +972,14 @@ export const getDefaultConfig = () => {
* Font Family
* @see https://tailwindcss.com/docs/font-family
*/
'font-family': [{ font: [isArbitraryVariableFamilyName, isArbitraryValue, themeFont] }],
'font-family': [
{ font: [isArbitraryVariableFamilyName, isArbitraryFamilyName, themeFont] },
],
/**
* Font Feature Settings
* @see https://tailwindcss.com/docs/font-feature-settings
*/
'font-features': [{ 'font-features': [isArbitraryValue] }],
/**
* Font Variant Numeric
* @see https://tailwindcss.com/docs/font-variant-numeric
@@ -991,6 +1125,11 @@ export const getDefaultConfig = () => {
* @see https://tailwindcss.com/docs/text-indent
*/
indent: [{ indent: scaleUnambiguousSpacing() }],
/**
* Tab Size
* @see https://tailwindcss.com/docs/tab-size
*/
'tab-size': [{ tab: [isInteger, isArbitraryVariable, isArbitraryValue] }],
/**
* Vertical Alignment
* @see https://tailwindcss.com/docs/vertical-align
@@ -1217,25 +1356,35 @@ export const getDefaultConfig = () => {
*/
'border-w': [{ border: scaleBorderWidth() }],
/**
* Border Width X
* Border Width Inline
* @see https://tailwindcss.com/docs/border-width
*/
'border-w-x': [{ 'border-x': scaleBorderWidth() }],
/**
* Border Width Y
* Border Width Block
* @see https://tailwindcss.com/docs/border-width
*/
'border-w-y': [{ 'border-y': scaleBorderWidth() }],
/**
* Border Width Start
* Border Width Inline Start
* @see https://tailwindcss.com/docs/border-width
*/
'border-w-s': [{ 'border-s': scaleBorderWidth() }],
/**
* Border Width End
* Border Width Inline End
* @see https://tailwindcss.com/docs/border-width
*/
'border-w-e': [{ 'border-e': scaleBorderWidth() }],
/**
* Border Width Block Start
* @see https://tailwindcss.com/docs/border-width
*/
'border-w-bs': [{ 'border-bs': scaleBorderWidth() }],
/**
* Border Width Block End
* @see https://tailwindcss.com/docs/border-width
*/
'border-w-be': [{ 'border-be': scaleBorderWidth() }],
/**
* Border Width Top
* @see https://tailwindcss.com/docs/border-width
@@ -1292,25 +1441,35 @@ export const getDefaultConfig = () => {
*/
'border-color': [{ border: scaleColor() }],
/**
* Border Color X
* Border Color Inline
* @see https://tailwindcss.com/docs/border-color
*/
'border-color-x': [{ 'border-x': scaleColor() }],
/**
* Border Color Y
* Border Color Block
* @see https://tailwindcss.com/docs/border-color
*/
'border-color-y': [{ 'border-y': scaleColor() }],
/**
* Border Color S
* Border Color Inline Start
* @see https://tailwindcss.com/docs/border-color
*/
'border-color-s': [{ 'border-s': scaleColor() }],
/**
* Border Color E
* Border Color Inline End
* @see https://tailwindcss.com/docs/border-color
*/
'border-color-e': [{ 'border-e': scaleColor() }],
/**
* Border Color Block Start
* @see https://tailwindcss.com/docs/border-color
*/
'border-color-bs': [{ 'border-bs': scaleColor() }],
/**
* Border Color Block End
* @see https://tailwindcss.com/docs/border-color
*/
'border-color-be': [{ 'border-be': scaleColor() }],
/**
* Border Color Top
* @see https://tailwindcss.com/docs/border-color
@@ -1945,6 +2104,11 @@ export const getDefaultConfig = () => {
* @see https://tailwindcss.com/docs/translate
*/
'translate-none': ['translate-none'],
/**
* Zoom
* @see https://tailwindcss.com/docs/zoom
*/
zoom: [{ zoom: [isInteger, isArbitraryVariable, isArbitraryValue] }],
// ---------------------
// --- Interactivity ---
@@ -2040,31 +2204,61 @@ export const getDefaultConfig = () => {
* @see https://tailwindcss.com/docs/scroll-behavior
*/
'scroll-behavior': [{ scroll: ['auto', 'smooth'] }],
/**
* Scrollbar Thumb Color
* @see https://tailwindcss.com/docs/scrollbar-color
*/
'scrollbar-thumb-color': [{ 'scrollbar-thumb': scaleColor() }],
/**
* Scrollbar Track Color
* @see https://tailwindcss.com/docs/scrollbar-color
*/
'scrollbar-track-color': [{ 'scrollbar-track': scaleColor() }],
/**
* Scrollbar Gutter
* @see https://tailwindcss.com/docs/scrollbar-gutter
*/
'scrollbar-gutter': [{ 'scrollbar-gutter': ['auto', 'stable', 'both'] }],
/**
* Scrollbar Width
* @see https://tailwindcss.com/docs/scrollbar-width
*/
'scrollbar-w': [{ scrollbar: ['auto', 'thin', 'none'] }],
/**
* Scroll Margin
* @see https://tailwindcss.com/docs/scroll-margin
*/
'scroll-m': [{ 'scroll-m': scaleUnambiguousSpacing() }],
/**
* Scroll Margin X
* Scroll Margin Inline
* @see https://tailwindcss.com/docs/scroll-margin
*/
'scroll-mx': [{ 'scroll-mx': scaleUnambiguousSpacing() }],
/**
* Scroll Margin Y
* Scroll Margin Block
* @see https://tailwindcss.com/docs/scroll-margin
*/
'scroll-my': [{ 'scroll-my': scaleUnambiguousSpacing() }],
/**
* Scroll Margin Start
* Scroll Margin Inline Start
* @see https://tailwindcss.com/docs/scroll-margin
*/
'scroll-ms': [{ 'scroll-ms': scaleUnambiguousSpacing() }],
/**
* Scroll Margin End
* Scroll Margin Inline End
* @see https://tailwindcss.com/docs/scroll-margin
*/
'scroll-me': [{ 'scroll-me': scaleUnambiguousSpacing() }],
/**
* Scroll Margin Block Start
* @see https://tailwindcss.com/docs/scroll-margin
*/
'scroll-mbs': [{ 'scroll-mbs': scaleUnambiguousSpacing() }],
/**
* Scroll Margin Block End
* @see https://tailwindcss.com/docs/scroll-margin
*/
'scroll-mbe': [{ 'scroll-mbe': scaleUnambiguousSpacing() }],
/**
* Scroll Margin Top
* @see https://tailwindcss.com/docs/scroll-margin
@@ -2091,25 +2285,35 @@ export const getDefaultConfig = () => {
*/
'scroll-p': [{ 'scroll-p': scaleUnambiguousSpacing() }],
/**
* Scroll Padding X
* Scroll Padding Inline
* @see https://tailwindcss.com/docs/scroll-padding
*/
'scroll-px': [{ 'scroll-px': scaleUnambiguousSpacing() }],
/**
* Scroll Padding Y
* Scroll Padding Block
* @see https://tailwindcss.com/docs/scroll-padding
*/
'scroll-py': [{ 'scroll-py': scaleUnambiguousSpacing() }],
/**
* Scroll Padding Start
* Scroll Padding Inline Start
* @see https://tailwindcss.com/docs/scroll-padding
*/
'scroll-ps': [{ 'scroll-ps': scaleUnambiguousSpacing() }],
/**
* Scroll Padding End
* Scroll Padding Inline End
* @see https://tailwindcss.com/docs/scroll-padding
*/
'scroll-pe': [{ 'scroll-pe': scaleUnambiguousSpacing() }],
/**
* Scroll Padding Block Start
* @see https://tailwindcss.com/docs/scroll-padding
*/
'scroll-pbs': [{ 'scroll-pbs': scaleUnambiguousSpacing() }],
/**
* Scroll Padding Block End
* @see https://tailwindcss.com/docs/scroll-padding
*/
'scroll-pbe': [{ 'scroll-pbe': scaleUnambiguousSpacing() }],
/**
* Scroll Padding Top
* @see https://tailwindcss.com/docs/scroll-padding
@@ -2232,17 +2436,29 @@ export const getDefaultConfig = () => {
'forced-color-adjust': [{ 'forced-color-adjust': ['auto', 'none'] }],
},
conflictingClassGroups: {
'container-named': ['container-type'],
overflow: ['overflow-x', 'overflow-y'],
overscroll: ['overscroll-x', 'overscroll-y'],
inset: ['inset-x', 'inset-y', 'start', 'end', 'top', 'right', 'bottom', 'left'],
inset: [
'inset-x',
'inset-y',
'inset-bs',
'inset-be',
'start',
'end',
'top',
'right',
'bottom',
'left',
],
'inset-x': ['right', 'left'],
'inset-y': ['top', 'bottom'],
flex: ['basis', 'grow', 'shrink'],
gap: ['gap-x', 'gap-y'],
p: ['px', 'py', 'ps', 'pe', 'pt', 'pr', 'pb', 'pl'],
p: ['px', 'py', 'ps', 'pe', 'pbs', 'pbe', 'pt', 'pr', 'pb', 'pl'],
px: ['pr', 'pl'],
py: ['pt', 'pb'],
m: ['mx', 'my', 'ms', 'me', 'mt', 'mr', 'mb', 'ml'],
m: ['mx', 'my', 'ms', 'me', 'mbs', 'mbe', 'mt', 'mr', 'mb', 'ml'],
mx: ['mr', 'ml'],
my: ['mt', 'mb'],
size: ['w', 'h'],
@@ -2288,6 +2504,8 @@ export const getDefaultConfig = () => {
'border-w-y',
'border-w-s',
'border-w-e',
'border-w-bs',
'border-w-be',
'border-w-t',
'border-w-r',
'border-w-b',
@@ -2300,6 +2518,8 @@ export const getDefaultConfig = () => {
'border-color-y',
'border-color-s',
'border-color-e',
'border-color-bs',
'border-color-be',
'border-color-t',
'border-color-r',
'border-color-b',
@@ -2314,6 +2534,8 @@ export const getDefaultConfig = () => {
'scroll-my',
'scroll-ms',
'scroll-me',
'scroll-mbs',
'scroll-mbe',
'scroll-mt',
'scroll-mr',
'scroll-mb',
@@ -2326,6 +2548,8 @@ export const getDefaultConfig = () => {
'scroll-py',
'scroll-ps',
'scroll-pe',
'scroll-pbs',
'scroll-pbe',
'scroll-pt',
'scroll-pr',
'scroll-pb',
@@ -2341,6 +2565,7 @@ export const getDefaultConfig = () => {
conflictingClassGroupModifiers: {
'font-size': ['leading'],
},
postfixLookupClassGroups: ['container-type'],
orderSensitiveModifiers: [
'*',
'**',
+27 -7
View File
@@ -4,8 +4,13 @@ import { IMPORTANT_MODIFIER } from './parse-class-name'
const SPLIT_CLASSES_REGEX = /\s+/
export const mergeClassList = (classList: string, configUtils: ConfigUtils) => {
const { parseClassName, getClassGroupId, getConflictingClassGroupIds, sortModifiers } =
configUtils
const {
parseClassName,
getClassGroupId,
getConflictingClassGroupIds,
sortModifiers,
postfixLookupClassGroupIds,
} = configUtils
/**
* Set of classGroupIds in following format:
@@ -36,11 +41,26 @@ export const mergeClassList = (classList: string, configUtils: ConfigUtils) => {
}
let hasPostfixModifier = !!maybePostfixModifierPosition
let classGroupId = getClassGroupId(
hasPostfixModifier
? baseClassName.substring(0, maybePostfixModifierPosition)
: baseClassName,
)
let classGroupId: ReturnType<typeof getClassGroupId>
if (hasPostfixModifier) {
const baseClassNameWithoutPostfix = baseClassName.substring(
0,
maybePostfixModifierPosition,
)
classGroupId = getClassGroupId(baseClassNameWithoutPostfix)
const classGroupIdWithPostfix =
classGroupId && postfixLookupClassGroupIds[classGroupId]
? getClassGroupId(baseClassName)
: undefined
if (classGroupIdWithPostfix && classGroupIdWithPostfix !== classGroupId) {
classGroupId = classGroupIdWithPostfix
hasPostfixModifier = false
}
} else {
classGroupId = getClassGroupId(baseClassName)
}
if (!classGroupId) {
if (!hasPostfixModifier) {
+2
View File
@@ -25,6 +25,7 @@ export const mergeConfigs = <ClassGroupIds extends string, ThemeGroupIds extends
baseConfig.conflictingClassGroupModifiers,
override.conflictingClassGroupModifiers,
)
overrideProperty(baseConfig, 'postfixLookupClassGroups', override.postfixLookupClassGroups)
overrideProperty(baseConfig, 'orderSensitiveModifiers', override.orderSensitiveModifiers)
mergeConfigProperties(baseConfig.theme, extend.theme)
@@ -34,6 +35,7 @@ export const mergeConfigs = <ClassGroupIds extends string, ThemeGroupIds extends
baseConfig.conflictingClassGroupModifiers,
extend.conflictingClassGroupModifiers,
)
mergeArrayProperties(baseConfig, extend, 'postfixLookupClassGroups')
mergeArrayProperties(baseConfig, extend, 'orderSensitiveModifiers')
return baseConfig
+1 -1
View File
@@ -9,7 +9,7 @@
*/
export type ClassNameValue = ClassNameArray | string | null | undefined | 0 | 0n | false
type ClassNameArray = ClassNameValue[]
type ClassNameArray = readonly ClassNameValue[]
export const twJoin = (...classLists: ClassNameValue[]): string => {
let index = 0
+61 -9
View File
@@ -2,8 +2,7 @@
* Type the tailwind-merge configuration adheres to.
*/
export interface Config<ClassGroupIds extends string, ThemeGroupIds extends string>
extends ConfigStaticPart,
ConfigGroupsPart<ClassGroupIds, ThemeGroupIds> {}
extends ConfigStaticPart, ConfigGroupsPart<ClassGroupIds, ThemeGroupIds> {}
/**
* The static part of the tailwind-merge configuration. When merging multiple configurations, the properties of this interface are always overridden.
@@ -75,7 +74,7 @@ export interface ParsedClassName {
*
* This property is prefixed with "maybe" because tailwind-merge does not know whether something is a postfix modifier or part of the base class since it's possible to configure Tailwind CSS classes which include a `/` in the base class name.
*
* If a `maybePostfixModifierPosition` is present, tailwind-merge first tries to match the `baseClassName` without the possible postfix modifier to a class group. If that fails, it tries again with the possible postfix modifier.
* If a `maybePostfixModifierPosition` is present, tailwind-merge first tries to match the `baseClassName` without the possible postfix modifier to a class group. If that fails or the matched class group is configured in `postfixLookupClassGroups`, it tries again with the possible postfix modifier.
*
* @example 11 // for `bg-gray-100/50`
*/
@@ -124,6 +123,14 @@ interface ConfigGroupsPart<ClassGroupIds extends string, ThemeGroupIds extends s
conflictingClassGroupModifiers: NoInfer<
Partial<Record<ClassGroupIds, readonly ClassGroupIds[]>>
>
/**
* Class group IDs which should be resolved again with their postfix modifier attached.
*
* This is needed when a slash can make the full class name belong to a different class group than the part before the slash.
*
* @example ['container-type'] // `@container-size/sidebar` should resolve differently from `@container-size`
*/
postfixLookupClassGroups?: readonly NoInferString<ClassGroupIds>[]
/**
* Modifiers whose order among multiple modifiers should be preserved because their order changes which element gets targeted.
*
@@ -135,14 +142,23 @@ interface ConfigGroupsPart<ClassGroupIds extends string, ThemeGroupIds extends s
/**
* Type of the configuration object that can be passed to `extendTailwindMerge`.
*/
export interface ConfigExtension<ClassGroupIds extends string, ThemeGroupIds extends string>
extends Partial<ConfigStaticPart> {
override?: PartialPartial<ConfigGroupsPart<ClassGroupIds, ThemeGroupIds>>
extend?: PartialPartial<ConfigGroupsPart<ClassGroupIds, ThemeGroupIds>>
export interface ConfigExtension<
ClassGroupIds extends string,
ThemeGroupIds extends string,
> extends Partial<ConfigStaticPart> {
override?: PartialConfigGroupsPart<ClassGroupIds, ThemeGroupIds>
extend?: PartialConfigGroupsPart<ClassGroupIds, ThemeGroupIds>
}
type PartialPartial<T> = {
[P in keyof T]?: T[P] extends any[] ? T[P] : Partial<T[P]>
interface PartialConfigGroupsPart<ClassGroupIds extends string, ThemeGroupIds extends string> {
theme?: NoInfer<Partial<ThemeObject<ThemeGroupIds>>>
classGroups?: NoInfer<Partial<Record<ClassGroupIds, ClassGroup<ThemeGroupIds>>>>
conflictingClassGroups?: NoInfer<Partial<Record<ClassGroupIds, readonly ClassGroupIds[]>>>
conflictingClassGroupModifiers?: NoInfer<
Partial<Record<ClassGroupIds, readonly ClassGroupIds[]>>
>
postfixLookupClassGroups?: readonly NoInferString<ClassGroupIds>[]
orderSensitiveModifiers?: string[]
}
export type ThemeObject<ThemeGroupIds extends string> = Record<
@@ -172,6 +188,13 @@ type ClassObject<ThemeGroupIds extends string> = Record<
*/
export type NoInfer<T> = [T][T extends any ? 0 : never]
/**
* Special-purpose NoInfer variant for string unions used in array item positions.
*
* The NoInfer helper above doesn't prevent inference from array items in all cases, so this keeps config arrays like `postfixLookupClassGroups` from defining or narrowing class group IDs. Once tailwind-merge only supports TypeScript 5.4 and newer, this can be replaced with TypeScript's built-in NoInfer utility type.
*/
type NoInferString<T extends string> = T extends infer S ? S & string : never
/**
* Theme group IDs included in the default configuration of tailwind-merge.
*
@@ -236,9 +259,12 @@ export type DefaultClassGroupIds =
| 'bg-position'
| 'bg-repeat'
| 'bg-size'
| 'block-size'
| 'blur'
| 'border-collapse'
| 'border-color-b'
| 'border-color-be'
| 'border-color-bs'
| 'border-color-e'
| 'border-color-l'
| 'border-color-r'
@@ -252,6 +278,8 @@ export type DefaultClassGroupIds =
| 'border-spacing'
| 'border-style'
| 'border-w-b'
| 'border-w-be'
| 'border-w-bs'
| 'border-w-e'
| 'border-w-l'
| 'border-w-r'
@@ -277,6 +305,8 @@ export type DefaultClassGroupIds =
| 'color-scheme'
| 'columns'
| 'container'
| 'container-named'
| 'container-type'
| 'content'
| 'contrast'
| 'cursor'
@@ -301,6 +331,7 @@ export type DefaultClassGroupIds =
| 'flex'
| 'float'
| 'font-family'
| 'font-features'
| 'font-size'
| 'font-smoothing'
| 'font-stretch'
@@ -331,10 +362,13 @@ export type DefaultClassGroupIds =
| 'hue-rotate'
| 'hyphens'
| 'indent'
| 'inline-size'
| 'inset-ring-color'
| 'inset-ring-w'
| 'inset-shadow-color'
| 'inset-shadow'
| 'inset-be'
| 'inset-bs'
| 'inset-x'
| 'inset-y'
| 'inset'
@@ -401,11 +435,17 @@ export type DefaultClassGroupIds =
| 'mask-repeat'
| 'mask-size'
| 'mask-type'
| 'max-block-size'
| 'max-h'
| 'max-inline-size'
| 'max-w'
| 'mb'
| 'mbe'
| 'mbs'
| 'me'
| 'min-block-size'
| 'min-h'
| 'min-inline-size'
| 'min-w'
| 'mix-blend'
| 'ml'
@@ -430,6 +470,7 @@ export type DefaultClassGroupIds =
| 'overscroll'
| 'p'
| 'pb'
| 'pbe'
| 'pe'
| 'perspective-origin'
| 'perspective'
@@ -442,6 +483,7 @@ export type DefaultClassGroupIds =
| 'position'
| 'pr'
| 'ps'
| 'pbs'
| 'pt'
| 'px'
| 'py'
@@ -480,21 +522,29 @@ export type DefaultClassGroupIds =
| 'scale-y'
| 'scale-z'
| 'scale'
| 'scrollbar-gutter'
| 'scrollbar-thumb-color'
| 'scrollbar-track-color'
| 'scrollbar-w'
| 'scroll-behavior'
| 'scroll-m'
| 'scroll-mb'
| 'scroll-mbe'
| 'scroll-me'
| 'scroll-ml'
| 'scroll-mr'
| 'scroll-mbs'
| 'scroll-ms'
| 'scroll-mt'
| 'scroll-mx'
| 'scroll-my'
| 'scroll-p'
| 'scroll-pb'
| 'scroll-pbe'
| 'scroll-pe'
| 'scroll-pl'
| 'scroll-pr'
| 'scroll-pbs'
| 'scroll-ps'
| 'scroll-pt'
| 'scroll-px'
@@ -521,6 +571,7 @@ export type DefaultClassGroupIds =
| 'stroke-w'
| 'stroke'
| 'table-layout'
| 'tab-size'
| 'text-alignment'
| 'text-color'
| 'text-decoration-color'
@@ -555,6 +606,7 @@ export type DefaultClassGroupIds =
| 'whitespace'
| 'will-change'
| 'wrap'
| 'zoom'
| 'z'
export type AnyClassGroupIds = string
+17 -1
View File
@@ -1,6 +1,6 @@
const arbitraryValueRegex = /^\[(?:(\w[\w-]*):)?(.+)\]$/i
const arbitraryVariableRegex = /^\((?:(\w[\w-]*):)?(.+)\)$/i
const fractionRegex = /^\d+\/\d+$/
const fractionRegex = /^\d+(?:\.\d+)?\/\d+(?:\.\d+)?$/
const tshirtUnitRegex = /^(\d+(\.\d+)?)?(xs|sm|md|lg|xl)$/
const lengthUnitRegex =
/\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh|cq(w|h|i|b|min|max))|\b(calc|min|max|clamp)\(.+\)|^0$/
@@ -37,6 +37,12 @@ const isImage = (value: string) => imageRegex.test(value)
export const isAnyNonArbitrary = (value: string) =>
!isArbitraryValue(value) && !isArbitraryVariable(value)
export const isNamedContainerQuery = (value: string) =>
value.startsWith('@container') &&
((value[10] === '/' && value[11] !== undefined) ||
(value[11] === 's' && value[16] !== undefined && value.startsWith('-size/', 10)) ||
(value[11] === 'n' && value[18] !== undefined && value.startsWith('-normal/', 10)))
export const isArbitrarySize = (value: string) => getIsArbitraryValue(value, isLabelSize, isNever)
export const isArbitraryValue = (value: string) => arbitraryValueRegex.test(value)
@@ -47,6 +53,11 @@ export const isArbitraryLength = (value: string) =>
export const isArbitraryNumber = (value: string) =>
getIsArbitraryValue(value, isLabelNumber, isNumber)
export const isArbitraryWeight = (value: string) => getIsArbitraryValue(value, isLabelWeight, isAny)
export const isArbitraryFamilyName = (value: string) =>
getIsArbitraryValue(value, isLabelFamilyName, isNever)
export const isArbitraryPosition = (value: string) =>
getIsArbitraryValue(value, isLabelPosition, isNever)
@@ -74,6 +85,9 @@ export const isArbitraryVariableImage = (value: string) =>
export const isArbitraryVariableShadow = (value: string) =>
getIsArbitraryVariable(value, isLabelShadow, true)
export const isArbitraryVariableWeight = (value: string) =>
getIsArbitraryVariable(value, isLabelWeight, true)
// Helpers
const getIsArbitraryValue = (
@@ -125,4 +139,6 @@ const isLabelNumber = (label: string) => label === 'number'
const isLabelFamilyName = (label: string) => label === 'family-name'
const isLabelWeight = (label: string) => label === 'number' || label === 'weight'
const isLabelShadow = (label: string) => label === 'shadow'