Initial commit - Todo app frontend

This commit is contained in:
2026-01-28 16:46:44 +00:00
commit 95b816a2e6
15978 changed files with 2514406 additions and 0 deletions

21
node_modules/tailwind-merge/LICENSE.md generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Dany Castillo
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

38
node_modules/tailwind-merge/README.md generated vendored Normal file
View File

@@ -0,0 +1,38 @@
<!-- This file is autogenerated. If you want to change this content, please do the changes in `./docs/README.md` instead. -->
<div align="center">
<br />
<a href="https://github.com/dcastil/tailwind-merge">
<img src="https://github.com/dcastil/tailwind-merge/raw/v3.4.0/assets/logo.svg" alt="tailwind-merge" height="150px" />
</a>
</div>
# tailwind-merge
Utility function to efficiently merge [Tailwind CSS](https://tailwindcss.com) classes in JS without style conflicts.
```ts
import { twMerge } from 'tailwind-merge'
twMerge('px-2 py-1 bg-red hover:bg-dark-red', 'p-3 bg-[#B91C1C]')
// → 'hover:bg-dark-red p-3 bg-[#B91C1C]'
```
- Supports Tailwind v4.0 up to v4.1 (if you use Tailwind v3, use [tailwind-merge v2.6.0](https://github.com/dcastil/tailwind-merge/tree/v2.6.0))
- Works in all modern browsers and maintained Node versions
- Fully typed
- [Check bundle size on Bundlephobia](https://bundlephobia.com/package/tailwind-merge)
## Get started
- [What is it for](https://github.com/dcastil/tailwind-merge/tree/v3.4.0/docs/what-is-it-for.md)
- [When and how to use it](https://github.com/dcastil/tailwind-merge/tree/v3.4.0/docs/when-and-how-to-use-it.md)
- [Features](https://github.com/dcastil/tailwind-merge/tree/v3.4.0/docs/features.md)
- [Limitations](https://github.com/dcastil/tailwind-merge/tree/v3.4.0/docs/limitations.md)
- [Configuration](https://github.com/dcastil/tailwind-merge/tree/v3.4.0/docs/configuration.md)
- [Recipes](https://github.com/dcastil/tailwind-merge/tree/v3.4.0/docs/recipes.md)
- [API reference](https://github.com/dcastil/tailwind-merge/tree/v3.4.0/docs/api-reference.md)
- [Writing plugins](https://github.com/dcastil/tailwind-merge/tree/v3.4.0/docs/writing-plugins.md)
- [Versioning](https://github.com/dcastil/tailwind-merge/tree/v3.4.0/docs/versioning.md)
- [Contributing](https://github.com/dcastil/tailwind-merge/tree/v3.4.0/docs/contributing.md)
- [Similar packages](https://github.com/dcastil/tailwind-merge/tree/v3.4.0/docs/similar-packages.md)

3127
node_modules/tailwind-merge/dist/bundle-cjs.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

1
node_modules/tailwind-merge/dist/bundle-cjs.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

3114
node_modules/tailwind-merge/dist/bundle-mjs.mjs generated vendored Normal file

File diff suppressed because it is too large Load Diff

1
node_modules/tailwind-merge/dist/bundle-mjs.mjs.map generated vendored Normal file

File diff suppressed because one or more lines are too long

3290
node_modules/tailwind-merge/dist/es5/bundle-cjs.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

3277
node_modules/tailwind-merge/dist/es5/bundle-mjs.mjs generated vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

2630
node_modules/tailwind-merge/dist/types.d.ts generated vendored Normal file

File diff suppressed because one or more lines are too long

93
node_modules/tailwind-merge/package.json generated vendored Normal file
View File

@@ -0,0 +1,93 @@
{
"name": "tailwind-merge",
"version": "3.4.0",
"description": "Merge Tailwind CSS classes without style conflicts",
"keywords": [
"tailwindcss",
"tailwind",
"css",
"classes",
"className",
"classList",
"merge",
"conflict",
"override"
],
"homepage": "https://github.com/dcastil/tailwind-merge",
"bugs": {
"url": "https://github.com/dcastil/tailwind-merge/issues"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/dcastil"
},
"license": "MIT",
"author": "Dany Castillo",
"files": [
"dist",
"src"
],
"source": "src/index.ts",
"exports": {
".": {
"types": "./dist/types.d.ts",
"require": "./dist/bundle-cjs.js",
"import": "./dist/bundle-mjs.mjs",
"default": "./dist/bundle-mjs.mjs"
},
"./es5": {
"types": "./dist/types.d.ts",
"require": "./dist/es5/bundle-cjs.js",
"import": "./dist/es5/bundle-mjs.mjs",
"default": "./dist/es5/bundle-mjs.mjs"
}
},
"main": "./dist/bundle-cjs.js",
"types": "./dist/types.d.ts",
"repository": {
"type": "git",
"url": "https://github.com/dcastil/tailwind-merge.git"
},
"sideEffects": false,
"scripts": {
"build": "rollup --config scripts/rollup.config.mjs",
"bench": "vitest bench --config scripts/vitest.config.mts --no-watch",
"test": "vitest --config scripts/vitest.config.mts --no-watch --coverage",
"test:watch": "vitest --config scripts/vitest.config.mts",
"test:exports": "node scripts/test-built-package-exports.cjs && node scripts/test-built-package-exports.mjs",
"lint": "eslint --max-warnings 0 '**'",
"preversion": "if [ -n \"$DANYS_MACHINE\" ]; then git checkout main && git pull; fi",
"version": "zx scripts/update-readme.mjs",
"postversion": "if [ -n \"$DANYS_MACHINE\" ]; then git push --follow-tags && open https://github.com/dcastil/tailwind-merge/releases; fi"
},
"dependencies": {},
"devDependencies": {
"@babel/core": "^7.28.4",
"@babel/preset-env": "^7.28.3",
"@codspeed/vitest-plugin": "^5.0.1",
"@rollup/plugin-babel": "^6.0.4",
"@rollup/plugin-node-resolve": "^16.0.1",
"@rollup/plugin-typescript": "^12.1.4",
"@types/node": "^24.9.2",
"@vitest/coverage-v8": "^3.2.4",
"@vitest/eslint-plugin": "^1.3.15",
"babel-plugin-annotate-pure-calls": "^0.5.0",
"babel-plugin-polyfill-regenerator": "^0.6.5",
"eslint": "^9.37.0",
"eslint-plugin-import": "^2.32.0",
"globby": "^11.1.0",
"prettier": "^3.6.2",
"rollup": "^4.52.4",
"rollup-plugin-delete": "^3.0.1",
"rollup-plugin-dts": "^6.2.3",
"tslib": "^2.8.1",
"typescript": "^5.9.3",
"typescript-eslint": "^8.45.0",
"vitest": "^3.2.4",
"zx": "^8.8.4"
},
"publishConfig": {
"provenance": true
},
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
}

17
node_modules/tailwind-merge/src/index.ts generated vendored Normal file
View File

@@ -0,0 +1,17 @@
export { createTailwindMerge } from './lib/create-tailwind-merge'
export { getDefaultConfig } from './lib/default-config'
export { extendTailwindMerge } from './lib/extend-tailwind-merge'
export { fromTheme } from './lib/from-theme'
export { mergeConfigs } from './lib/merge-configs'
export { twJoin, type ClassNameValue } from './lib/tw-join'
export { twMerge } from './lib/tw-merge'
export {
type ClassValidator,
type Config,
type ConfigExtension,
type DefaultClassGroupIds,
type DefaultThemeGroupIds,
type ExperimentalParseClassNameParam,
type ParsedClassName as ExperimentalParsedClassName,
} from './lib/types'
export * as validators from './lib/validators'

View File

@@ -0,0 +1,274 @@
import {
AnyClassGroupIds,
AnyConfig,
AnyThemeGroupIds,
ClassGroup,
ClassValidator,
Config,
ThemeGetter,
ThemeObject,
} from './types'
import { concatArrays } from './utils'
export interface ClassPartObject {
nextPart: Map<string, ClassPartObject>
validators: ClassValidatorObject[] | null
classGroupId: AnyClassGroupIds | undefined // Always define optional props for consistent shape
}
interface ClassValidatorObject {
classGroupId: AnyClassGroupIds
validator: ClassValidator
}
// Factory function ensures consistent object shapes
const createClassValidatorObject = (
classGroupId: AnyClassGroupIds,
validator: ClassValidator,
): ClassValidatorObject => ({
classGroupId,
validator,
})
// Factory ensures consistent ClassPartObject shape
const createClassPartObject = (
nextPart: Map<string, ClassPartObject> = new Map(),
validators: ClassValidatorObject[] | null = null,
classGroupId?: AnyClassGroupIds,
): ClassPartObject => ({
nextPart,
validators,
classGroupId,
})
const CLASS_PART_SEPARATOR = '-'
const EMPTY_CONFLICTS: readonly AnyClassGroupIds[] = []
// I use two dots here because one dot is used as prefix for class groups in plugins
const ARBITRARY_PROPERTY_PREFIX = 'arbitrary..'
export const createClassGroupUtils = (config: AnyConfig) => {
const classMap = createClassMap(config)
const { conflictingClassGroups, conflictingClassGroupModifiers } = config
const getClassGroupId = (className: string) => {
if (className.startsWith('[') && className.endsWith(']')) {
return getGroupIdForArbitraryProperty(className)
}
const classParts = className.split(CLASS_PART_SEPARATOR)
// Classes like `-inset-1` produce an empty string as first classPart. We assume that classes for negative values are used correctly and skip it.
const startIndex = classParts[0] === '' && classParts.length > 1 ? 1 : 0
return getGroupRecursive(classParts, startIndex, classMap)
}
const getConflictingClassGroupIds = (
classGroupId: AnyClassGroupIds,
hasPostfixModifier: boolean,
): readonly AnyClassGroupIds[] => {
if (hasPostfixModifier) {
const modifierConflicts = conflictingClassGroupModifiers[classGroupId]
const baseConflicts = conflictingClassGroups[classGroupId]
if (modifierConflicts) {
if (baseConflicts) {
// Merge base conflicts with modifier conflicts
return concatArrays(baseConflicts, modifierConflicts)
}
// Only modifier conflicts
return modifierConflicts
}
// Fall back to without postfix if no modifier conflicts
return baseConflicts || EMPTY_CONFLICTS
}
return conflictingClassGroups[classGroupId] || EMPTY_CONFLICTS
}
return {
getClassGroupId,
getConflictingClassGroupIds,
}
}
const getGroupRecursive = (
classParts: string[],
startIndex: number,
classPartObject: ClassPartObject,
): AnyClassGroupIds | undefined => {
const classPathsLength = classParts.length - startIndex
if (classPathsLength === 0) {
return classPartObject.classGroupId
}
const currentClassPart = classParts[startIndex]!
const nextClassPartObject = classPartObject.nextPart.get(currentClassPart)
if (nextClassPartObject) {
const result = getGroupRecursive(classParts, startIndex + 1, nextClassPartObject)
if (result) return result
}
const validators = classPartObject.validators
if (validators === null) {
return undefined
}
// Build classRest string efficiently by joining from startIndex onwards
const classRest =
startIndex === 0
? classParts.join(CLASS_PART_SEPARATOR)
: classParts.slice(startIndex).join(CLASS_PART_SEPARATOR)
const validatorsLength = validators.length
for (let i = 0; i < validatorsLength; i++) {
const validatorObj = validators[i]!
if (validatorObj.validator(classRest)) {
return validatorObj.classGroupId
}
}
return undefined
}
/**
* Get the class group ID for an arbitrary property.
*
* @param className - The class name to get the group ID for. Is expected to be string starting with `[` and ending with `]`.
*/
const getGroupIdForArbitraryProperty = (className: string): AnyClassGroupIds | undefined =>
className.slice(1, -1).indexOf(':') === -1
? undefined
: (() => {
const content = className.slice(1, -1)
const colonIndex = content.indexOf(':')
const property = content.slice(0, colonIndex)
return property ? ARBITRARY_PROPERTY_PREFIX + property : undefined
})()
/**
* Exported for testing only
*/
export const createClassMap = (config: Config<AnyClassGroupIds, AnyThemeGroupIds>) => {
const { theme, classGroups } = config
return processClassGroups(classGroups, theme)
}
// Split into separate functions to maintain monomorphic call sites
const processClassGroups = (
classGroups: Record<AnyClassGroupIds, ClassGroup<AnyThemeGroupIds>>,
theme: ThemeObject<AnyThemeGroupIds>,
): ClassPartObject => {
const classMap = createClassPartObject()
for (const classGroupId in classGroups) {
const group = classGroups[classGroupId]!
processClassesRecursively(group, classMap, classGroupId, theme)
}
return classMap
}
const processClassesRecursively = (
classGroup: ClassGroup<AnyThemeGroupIds>,
classPartObject: ClassPartObject,
classGroupId: AnyClassGroupIds,
theme: ThemeObject<AnyThemeGroupIds>,
) => {
const len = classGroup.length
for (let i = 0; i < len; i++) {
const classDefinition = classGroup[i]!
processClassDefinition(classDefinition, classPartObject, classGroupId, theme)
}
}
// Split into separate functions for each type to maintain monomorphic call sites
const processClassDefinition = (
classDefinition: ClassGroup<AnyThemeGroupIds>[number],
classPartObject: ClassPartObject,
classGroupId: AnyClassGroupIds,
theme: ThemeObject<AnyThemeGroupIds>,
) => {
if (typeof classDefinition === 'string') {
processStringDefinition(classDefinition, classPartObject, classGroupId)
return
}
if (typeof classDefinition === 'function') {
processFunctionDefinition(classDefinition, classPartObject, classGroupId, theme)
return
}
processObjectDefinition(
classDefinition as Record<string, ClassGroup<AnyThemeGroupIds>>,
classPartObject,
classGroupId,
theme,
)
}
const processStringDefinition = (
classDefinition: string,
classPartObject: ClassPartObject,
classGroupId: AnyClassGroupIds,
) => {
const classPartObjectToEdit =
classDefinition === '' ? classPartObject : getPart(classPartObject, classDefinition)
classPartObjectToEdit.classGroupId = classGroupId
}
const processFunctionDefinition = (
classDefinition: Function,
classPartObject: ClassPartObject,
classGroupId: AnyClassGroupIds,
theme: ThemeObject<AnyThemeGroupIds>,
) => {
if (isThemeGetter(classDefinition)) {
processClassesRecursively(classDefinition(theme), classPartObject, classGroupId, theme)
return
}
if (classPartObject.validators === null) {
classPartObject.validators = []
}
classPartObject.validators.push(
createClassValidatorObject(classGroupId, classDefinition as ClassValidator),
)
}
const processObjectDefinition = (
classDefinition: Record<string, ClassGroup<AnyThemeGroupIds>>,
classPartObject: ClassPartObject,
classGroupId: AnyClassGroupIds,
theme: ThemeObject<AnyThemeGroupIds>,
) => {
const entries = Object.entries(classDefinition)
const len = entries.length
for (let i = 0; i < len; i++) {
const [key, value] = entries[i]!
processClassesRecursively(value, getPart(classPartObject, key), classGroupId, theme)
}
}
const getPart = (classPartObject: ClassPartObject, path: string): ClassPartObject => {
let current = classPartObject
const parts = path.split(CLASS_PART_SEPARATOR)
const len = parts.length
for (let i = 0; i < len; i++) {
const part = parts[i]!
let next = current.nextPart.get(part)
if (!next) {
next = createClassPartObject()
current.nextPart.set(part, next)
}
current = next
}
return current
}
// Type guard maintains monomorphic check
const isThemeGetter = (func: Function): func is ThemeGetter =>
'isThemeGetter' in func && (func as ThemeGetter).isThemeGetter === true

14
node_modules/tailwind-merge/src/lib/config-utils.ts generated vendored Normal file
View File

@@ -0,0 +1,14 @@
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'
export type ConfigUtils = ReturnType<typeof createConfigUtils>
export const createConfigUtils = (config: AnyConfig) => ({
cache: createLruCache<string, string>(config.cacheSize),
parseClassName: createParseClassName(config),
sortModifiers: createSortModifiers(config),
...createClassGroupUtils(config),
})

View File

@@ -0,0 +1,50 @@
import { createConfigUtils } from './config-utils'
import { mergeClassList } from './merge-classlist'
import { ClassNameValue, twJoin } from './tw-join'
import { AnyConfig } from './types'
type CreateConfigFirst = () => AnyConfig
type CreateConfigSubsequent = (config: AnyConfig) => AnyConfig
type TailwindMerge = (...classLists: ClassNameValue[]) => string
type ConfigUtils = ReturnType<typeof createConfigUtils>
export const createTailwindMerge = (
createConfigFirst: CreateConfigFirst,
...createConfigRest: CreateConfigSubsequent[]
): TailwindMerge => {
let configUtils: ConfigUtils
let cacheGet: ConfigUtils['cache']['get']
let cacheSet: ConfigUtils['cache']['set']
let functionToCall: (classList: string) => string
const initTailwindMerge = (classList: string) => {
const config = createConfigRest.reduce(
(previousConfig, createConfigCurrent) => createConfigCurrent(previousConfig),
createConfigFirst() as AnyConfig,
)
configUtils = createConfigUtils(config)
cacheGet = configUtils.cache.get
cacheSet = configUtils.cache.set
functionToCall = tailwindMerge
return tailwindMerge(classList)
}
const tailwindMerge = (classList: string) => {
const cachedResult = cacheGet(classList)
if (cachedResult) {
return cachedResult
}
const result = mergeClassList(classList, configUtils)
cacheSet(classList, result)
return result
}
functionToCall = initTailwindMerge
return (...args: ClassNameValue[]) => functionToCall(twJoin(...args))
}

2359
node_modules/tailwind-merge/src/lib/default-config.ts generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,25 @@
import { createTailwindMerge } from './create-tailwind-merge'
import { getDefaultConfig } from './default-config'
import { mergeConfigs } from './merge-configs'
import { AnyConfig, ConfigExtension, DefaultClassGroupIds, DefaultThemeGroupIds } from './types'
type CreateConfigSubsequent = (config: AnyConfig) => AnyConfig
export const extendTailwindMerge = <
AdditionalClassGroupIds extends string = never,
AdditionalThemeGroupIds extends string = never,
>(
configExtension:
| ConfigExtension<
DefaultClassGroupIds | AdditionalClassGroupIds,
DefaultThemeGroupIds | AdditionalThemeGroupIds
>
| CreateConfigSubsequent,
...createConfig: CreateConfigSubsequent[]
) =>
typeof configExtension === 'function'
? createTailwindMerge(getDefaultConfig, configExtension, ...createConfig)
: createTailwindMerge(
() => mergeConfigs(getDefaultConfig(), configExtension),
...createConfig,
)

17
node_modules/tailwind-merge/src/lib/from-theme.ts generated vendored Normal file
View File

@@ -0,0 +1,17 @@
import { DefaultThemeGroupIds, NoInfer, ThemeGetter, ThemeObject } from './types'
const fallbackThemeArr: ThemeObject<DefaultThemeGroupIds>[DefaultThemeGroupIds] = []
export const fromTheme = <
AdditionalThemeGroupIds extends string = never,
DefaultThemeGroupIdsInner extends string = DefaultThemeGroupIds,
>(
key: NoInfer<DefaultThemeGroupIdsInner | AdditionalThemeGroupIds>,
): ThemeGetter => {
const themeGetter = (theme: ThemeObject<DefaultThemeGroupIdsInner | AdditionalThemeGroupIds>) =>
theme[key] || fallbackThemeArr
themeGetter.isThemeGetter = true as const
return themeGetter
}

54
node_modules/tailwind-merge/src/lib/lru-cache.ts generated vendored Normal file
View File

@@ -0,0 +1,54 @@
// Export is needed because TypeScript complains about an error otherwise:
// Error: …/tailwind-merge/src/config-utils.ts(8,17): semantic error TS4058: Return type of exported function has or is using name 'LruCache' from external module "…/tailwind-merge/src/lru-cache" but cannot be named.
export interface LruCache<Key extends string, Value> {
get(key: Key): Value | undefined
set(key: Key, value: Value): void
}
// LRU cache implementation using plain objects for simplicity
export const createLruCache = <Key extends string, Value>(
maxCacheSize: number,
): LruCache<Key, Value> => {
if (maxCacheSize < 1) {
return {
get: () => undefined,
set: () => {},
}
}
let cacheSize = 0
let cache: Record<Key, Value> = Object.create(null)
let previousCache: Record<Key, Value> = Object.create(null)
const update = (key: Key, value: Value) => {
cache[key] = value
cacheSize++
if (cacheSize > maxCacheSize) {
cacheSize = 0
previousCache = cache
cache = Object.create(null)
}
}
return {
get(key) {
let value = cache[key]
if (value !== undefined) {
return value
}
if ((value = previousCache[key]) !== undefined) {
update(key, value)
return value
}
},
set(key, value) {
if (key in cache) {
cache[key] = value
} else {
update(key, value)
}
},
}
}

95
node_modules/tailwind-merge/src/lib/merge-classlist.ts generated vendored Normal file
View File

@@ -0,0 +1,95 @@
import { ConfigUtils } from './config-utils'
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
/**
* Set of classGroupIds in following format:
* `{importantModifier}{variantModifiers}{classGroupId}`
* @example 'float'
* @example 'hover:focus:bg-color'
* @example 'md:!pr'
*/
const classGroupsInConflict: string[] = []
const classNames = classList.trim().split(SPLIT_CLASSES_REGEX)
let result = ''
for (let index = classNames.length - 1; index >= 0; index -= 1) {
const originalClassName = classNames[index]!
const {
isExternal,
modifiers,
hasImportantModifier,
baseClassName,
maybePostfixModifierPosition,
} = parseClassName(originalClassName)
if (isExternal) {
result = originalClassName + (result.length > 0 ? ' ' + result : result)
continue
}
let hasPostfixModifier = !!maybePostfixModifierPosition
let classGroupId = getClassGroupId(
hasPostfixModifier
? baseClassName.substring(0, maybePostfixModifierPosition)
: baseClassName,
)
if (!classGroupId) {
if (!hasPostfixModifier) {
// Not a Tailwind class
result = originalClassName + (result.length > 0 ? ' ' + result : result)
continue
}
classGroupId = getClassGroupId(baseClassName)
if (!classGroupId) {
// Not a Tailwind class
result = originalClassName + (result.length > 0 ? ' ' + result : result)
continue
}
hasPostfixModifier = false
}
// Fast path: skip sorting for empty or single modifier
const variantModifier =
modifiers.length === 0
? ''
: modifiers.length === 1
? modifiers[0]!
: sortModifiers(modifiers).join(':')
const modifierId = hasImportantModifier
? variantModifier + IMPORTANT_MODIFIER
: variantModifier
const classId = modifierId + classGroupId
if (classGroupsInConflict.indexOf(classId) > -1) {
// Tailwind class omitted due to conflict
continue
}
classGroupsInConflict.push(classId)
const conflictGroups = getConflictingClassGroupIds(classGroupId, hasPostfixModifier)
for (let i = 0; i < conflictGroups.length; ++i) {
const group = conflictGroups[i]!
classGroupsInConflict.push(modifierId + group)
}
// Tailwind class not in conflict
result = originalClassName + (result.length > 0 ? ' ' + result : result)
}
return result
}

84
node_modules/tailwind-merge/src/lib/merge-configs.ts generated vendored Normal file
View File

@@ -0,0 +1,84 @@
import { AnyConfig, ConfigExtension, NoInfer } from './types'
/**
* @param baseConfig Config where other config will be merged into. This object will be mutated.
* @param configExtension Partial config to merge into the `baseConfig`.
*/
export const mergeConfigs = <ClassGroupIds extends string, ThemeGroupIds extends string = never>(
baseConfig: AnyConfig,
{
cacheSize,
prefix,
experimentalParseClassName,
extend = {},
override = {},
}: ConfigExtension<ClassGroupIds, ThemeGroupIds>,
) => {
overrideProperty(baseConfig, 'cacheSize', cacheSize)
overrideProperty(baseConfig, 'prefix', prefix)
overrideProperty(baseConfig, 'experimentalParseClassName', experimentalParseClassName)
overrideConfigProperties(baseConfig.theme, override.theme)
overrideConfigProperties(baseConfig.classGroups, override.classGroups)
overrideConfigProperties(baseConfig.conflictingClassGroups, override.conflictingClassGroups)
overrideConfigProperties(
baseConfig.conflictingClassGroupModifiers,
override.conflictingClassGroupModifiers,
)
overrideProperty(baseConfig, 'orderSensitiveModifiers', override.orderSensitiveModifiers)
mergeConfigProperties(baseConfig.theme, extend.theme)
mergeConfigProperties(baseConfig.classGroups, extend.classGroups)
mergeConfigProperties(baseConfig.conflictingClassGroups, extend.conflictingClassGroups)
mergeConfigProperties(
baseConfig.conflictingClassGroupModifiers,
extend.conflictingClassGroupModifiers,
)
mergeArrayProperties(baseConfig, extend, 'orderSensitiveModifiers')
return baseConfig
}
const overrideProperty = <T extends object, K extends keyof T>(
baseObject: T,
overrideKey: K,
overrideValue: T[K] | undefined,
) => {
if (overrideValue !== undefined) {
baseObject[overrideKey] = overrideValue
}
}
const overrideConfigProperties = (
baseObject: Partial<Record<string, readonly unknown[]>>,
overrideObject: Partial<Record<string, readonly unknown[]>> | undefined,
) => {
if (overrideObject) {
for (const key in overrideObject) {
overrideProperty(baseObject, key, overrideObject[key])
}
}
}
const mergeConfigProperties = (
baseObject: Partial<Record<string, readonly unknown[]>>,
mergeObject: Partial<Record<string, readonly unknown[]>> | undefined,
) => {
if (mergeObject) {
for (const key in mergeObject) {
mergeArrayProperties(baseObject, mergeObject, key)
}
}
}
const mergeArrayProperties = <Key extends string>(
baseObject: Partial<Record<NoInfer<Key>, readonly unknown[]>>,
mergeObject: Partial<Record<NoInfer<Key>, readonly unknown[]>>,
key: Key,
) => {
const mergeValue = mergeObject[key]
if (mergeValue !== undefined) {
baseObject[key] = baseObject[key] ? baseObject[key].concat(mergeValue) : mergeValue
}
}

114
node_modules/tailwind-merge/src/lib/parse-class-name.ts generated vendored Normal file
View File

@@ -0,0 +1,114 @@
import { AnyConfig, ParsedClassName } from './types'
export const IMPORTANT_MODIFIER = '!'
const MODIFIER_SEPARATOR = ':'
const EMPTY_MODIFIERS: string[] = []
// Pre-allocated result object shape for consistency
const createResultObject = (
modifiers: string[],
hasImportantModifier: boolean,
baseClassName: string,
maybePostfixModifierPosition?: number,
isExternal?: boolean,
): ParsedClassName => ({
modifiers,
hasImportantModifier,
baseClassName,
maybePostfixModifierPosition,
isExternal,
})
export const createParseClassName = (config: AnyConfig) => {
const { prefix, experimentalParseClassName } = config
/**
* Parse class name into parts.
*
* Inspired by `splitAtTopLevelOnly` used in Tailwind CSS
* @see https://github.com/tailwindlabs/tailwindcss/blob/v3.2.2/src/util/splitAtTopLevelOnly.js
*/
let parseClassName = (className: string): ParsedClassName => {
// Use simple array with push for better performance
const modifiers: string[] = []
let bracketDepth = 0
let parenDepth = 0
let modifierStart = 0
let postfixModifierPosition: number | undefined
const len = className.length
for (let index = 0; index < len; index++) {
const currentCharacter = className[index]!
if (bracketDepth === 0 && parenDepth === 0) {
if (currentCharacter === MODIFIER_SEPARATOR) {
modifiers.push(className.slice(modifierStart, index))
modifierStart = index + 1
continue
}
if (currentCharacter === '/') {
postfixModifierPosition = index
continue
}
}
if (currentCharacter === '[') bracketDepth++
else if (currentCharacter === ']') bracketDepth--
else if (currentCharacter === '(') parenDepth++
else if (currentCharacter === ')') parenDepth--
}
const baseClassNameWithImportantModifier =
modifiers.length === 0 ? className : className.slice(modifierStart)
// Inline important modifier check
let baseClassName = baseClassNameWithImportantModifier
let hasImportantModifier = false
if (baseClassNameWithImportantModifier.endsWith(IMPORTANT_MODIFIER)) {
baseClassName = baseClassNameWithImportantModifier.slice(0, -1)
hasImportantModifier = true
} else if (
/**
* In Tailwind CSS v3 the important modifier was at the start of the base class name. This is still supported for legacy reasons.
* @see https://github.com/dcastil/tailwind-merge/issues/513#issuecomment-2614029864
*/
baseClassNameWithImportantModifier.startsWith(IMPORTANT_MODIFIER)
) {
baseClassName = baseClassNameWithImportantModifier.slice(1)
hasImportantModifier = true
}
const maybePostfixModifierPosition =
postfixModifierPosition && postfixModifierPosition > modifierStart
? postfixModifierPosition - modifierStart
: undefined
return createResultObject(
modifiers,
hasImportantModifier,
baseClassName,
maybePostfixModifierPosition,
)
}
if (prefix) {
const fullPrefix = prefix + MODIFIER_SEPARATOR
const parseClassNameOriginal = parseClassName
parseClassName = (className: string) =>
className.startsWith(fullPrefix)
? parseClassNameOriginal(className.slice(fullPrefix.length))
: createResultObject(EMPTY_MODIFIERS, false, className, undefined, true)
}
if (experimentalParseClassName) {
const parseClassNameOriginal = parseClassName
parseClassName = (className: string) =>
experimentalParseClassName({ className, parseClassName: parseClassNameOriginal })
}
return parseClassName
}

51
node_modules/tailwind-merge/src/lib/sort-modifiers.ts generated vendored Normal file
View File

@@ -0,0 +1,51 @@
import { AnyConfig } from './types'
/**
* Sorts modifiers according to following schema:
* - Predefined modifiers are sorted alphabetically
* - When an arbitrary variant appears, it must be preserved which modifiers are before and after it
*/
export const createSortModifiers = (config: AnyConfig) => {
// Pre-compute weights for all known modifiers for O(1) comparison
const modifierWeights = new Map<string, number>()
// Assign weights to sensitive modifiers (highest priority, but preserve order)
config.orderSensitiveModifiers.forEach((mod, index) => {
modifierWeights.set(mod, 1000000 + index) // High weights for sensitive mods
})
return (modifiers: readonly string[]): string[] => {
const result: string[] = []
let currentSegment: string[] = []
// Process modifiers in one pass
for (let i = 0; i < modifiers.length; i++) {
const modifier = modifiers[i]!
// Check if modifier is sensitive (starts with '[' or in orderSensitiveModifiers)
const isArbitrary = modifier[0] === '['
const isOrderSensitive = modifierWeights.has(modifier)
if (isArbitrary || isOrderSensitive) {
// Sort and flush current segment alphabetically
if (currentSegment.length > 0) {
currentSegment.sort()
result.push(...currentSegment)
currentSegment = []
}
result.push(modifier)
} else {
// Regular modifier - add to current segment for batch sorting
currentSegment.push(modifier)
}
}
// Sort and add any remaining segment items
if (currentSegment.length > 0) {
currentSegment.sort()
result.push(...currentSegment)
}
return result
}
}

50
node_modules/tailwind-merge/src/lib/tw-join.ts generated vendored Normal file
View File

@@ -0,0 +1,50 @@
/**
* The code in this file is copied from https://github.com/lukeed/clsx and modified to suit the needs of tailwind-merge better.
*
* Specifically:
* - Runtime code from https://github.com/lukeed/clsx/blob/v1.2.1/src/index.js
* - TypeScript types from https://github.com/lukeed/clsx/blob/v1.2.1/clsx.d.ts
*
* Original code has MIT license: Copyright (c) Luke Edwards <luke.edwards05@gmail.com> (lukeed.com)
*/
export type ClassNameValue = ClassNameArray | string | null | undefined | 0 | 0n | false
type ClassNameArray = ClassNameValue[]
export const twJoin = (...classLists: ClassNameValue[]): string => {
let index = 0
let argument: ClassNameValue
let resolvedValue: string
let string = ''
while (index < classLists.length) {
if ((argument = classLists[index++])) {
if ((resolvedValue = toValue(argument))) {
string && (string += ' ')
string += resolvedValue
}
}
}
return string
}
const toValue = (mix: ClassNameArray | string): string => {
// Fast path for strings
if (typeof mix === 'string') {
return mix
}
let resolvedValue: string
let string = ''
for (let k = 0; k < mix.length; k++) {
if (mix[k]) {
if ((resolvedValue = toValue(mix[k] as ClassNameArray | string))) {
string && (string += ' ')
string += resolvedValue
}
}
}
return string
}

4
node_modules/tailwind-merge/src/lib/tw-merge.ts generated vendored Normal file
View File

@@ -0,0 +1,4 @@
import { createTailwindMerge } from './create-tailwind-merge'
import { getDefaultConfig } from './default-config'
export const twMerge = createTailwindMerge(getDefaultConfig)

566
node_modules/tailwind-merge/src/lib/types.ts generated vendored Normal file
View File

@@ -0,0 +1,566 @@
/**
* Type the tailwind-merge configuration adheres to.
*/
export interface Config<ClassGroupIds extends string, ThemeGroupIds extends string>
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.
*/
interface ConfigStaticPart {
/**
* Integer indicating size of LRU cache used for memoizing results.
* - Cache might be up to twice as big as `cacheSize`
* - No cache is used for values <= 0
*/
cacheSize: number
/**
* Prefix added to Tailwind-generated classes
* @see https://tailwindcss.com/docs/configuration#prefix
*/
prefix?: string
/**
* Allows to customize parsing of individual classes passed to `twMerge`.
* All classes passed to `twMerge` outside of cache hits are passed to this function before it is determined whether the class is a valid Tailwind CSS class.
*
* This is an experimental feature and may introduce breaking changes in any minor version update.
*/
experimentalParseClassName?(param: ExperimentalParseClassNameParam): ParsedClassName
}
/**
* Type of param passed to the `experimentalParseClassName` function.
*
* This is an experimental feature and may introduce breaking changes in any minor version update.
*/
export interface ExperimentalParseClassNameParam {
className: string
parseClassName(className: string): ParsedClassName
}
/**
* Type of the result returned by the `experimentalParseClassName` function.
*
* This is an experimental feature and may introduce breaking changes in any minor version update.
*/
export interface ParsedClassName {
/**
* Whether the class is external and merging logic should be sipped.
*
* If this is `true`, the class will be treated as if it wasn't a Tailwind class and will be passed through as is.
*/
isExternal?: boolean
/**
* Modifiers of the class in the order they appear in the class.
*
* @example ['hover', 'dark'] // for `hover:dark:bg-gray-100`
*/
modifiers: string[]
/**
* Whether the class has an `!important` modifier.
*
* @example true // for `hover:dark:!bg-gray-100`
*/
hasImportantModifier: boolean
/**
* Base class without preceding modifiers.
*
* @example 'bg-gray-100' // for `hover:dark:bg-gray-100`
*/
baseClassName: string
/**
* Index position of a possible postfix modifier in the class.
* If the class has no postfix modifier, this is `undefined`.
*
* 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.
*
* @example 11 // for `bg-gray-100/50`
*/
maybePostfixModifierPosition: number | undefined
}
/**
* The dynamic part of the tailwind-merge configuration. When merging multiple configurations, the user can choose to either override or extend the properties of this interface.
*/
interface ConfigGroupsPart<ClassGroupIds extends string, ThemeGroupIds extends string> {
/**
* Theme scales used in classGroups.
*
* The keys are the same as in the Tailwind config but the values are sometimes defined more broadly.
*/
theme: NoInfer<ThemeObject<ThemeGroupIds>>
/**
* Object with groups of classes.
*
* @example
* {
* // Creates group of classes `group`, `of` and `classes`
* 'group-id': ['group', 'of', 'classes'],
* // Creates group of classes `look-at-me-other` and `look-at-me-group`.
* 'other-group': [{ 'look-at-me': ['other', 'group']}]
* }
*/
classGroups: NoInfer<Record<ClassGroupIds, ClassGroup<ThemeGroupIds>>>
/**
* Conflicting classes across groups.
*
* The key is the ID of a class group which creates a conflict, values are IDs of class groups which receive a conflict. That means if a class from from the key ID is present, all preceding classes from the values are removed.
*
* A class group ID is the key of a class group in the classGroups object.
*
* @example { gap: ['gap-x', 'gap-y'] }
*/
conflictingClassGroups: NoInfer<Partial<Record<ClassGroupIds, readonly ClassGroupIds[]>>>
/**
* Postfix modifiers conflicting with other class groups.
*
* A class group ID is the key of a class group in classGroups object.
*
* @example { 'font-size': ['leading'] }
*/
conflictingClassGroupModifiers: NoInfer<
Partial<Record<ClassGroupIds, readonly ClassGroupIds[]>>
>
/**
* Modifiers whose order among multiple modifiers should be preserved because their order changes which element gets targeted.
*
* tailwind-merge makes sure that classes with these modifiers are not overwritten by classes with the same modifiers with order-sensitive modifiers being in a different position.
*/
orderSensitiveModifiers: string[]
}
/**
* 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>>
}
type PartialPartial<T> = {
[P in keyof T]?: T[P] extends any[] ? T[P] : Partial<T[P]>
}
export type ThemeObject<ThemeGroupIds extends string> = Record<
ThemeGroupIds,
ClassGroup<ThemeGroupIds>
>
export type ClassGroup<ThemeGroupIds extends string> = readonly ClassDefinition<ThemeGroupIds>[]
type ClassDefinition<ThemeGroupIds extends string> =
| string
| ClassValidator
| ThemeGetter
| ClassObject<ThemeGroupIds>
export type ClassValidator = (classPart: string) => boolean
export interface ThemeGetter {
(theme: ThemeObject<AnyThemeGroupIds>): ClassGroup<AnyClassGroupIds>
isThemeGetter: true
}
type ClassObject<ThemeGroupIds extends string> = Record<
string,
readonly ClassDefinition<ThemeGroupIds>[]
>
/**
* Hack from https://stackoverflow.com/questions/56687668/a-way-to-disable-type-argument-inference-in-generics/56688073#56688073
*
* Could be replaced with NoInfer utility type from TypeScript (https://www.typescriptlang.org/docs/handbook/utility-types.html#noinfertype), but that is only supported in TypeScript 5.4 or higher, so I should wait some time before using it.
*/
export type NoInfer<T> = [T][T extends any ? 0 : never]
/**
* Theme group IDs included in the default configuration of tailwind-merge.
*
* If you want to use a scale that is not supported in the `ThemeObject` type,
* consider using `classGroups` instead of `theme`.
*
* @see https://github.com/dcastil/tailwind-merge/blob/main/docs/configuration.md#theme
* (the list of supported keys may vary between `tailwind-merge` versions)
*/
export type DefaultThemeGroupIds =
| 'animate'
| 'aspect'
| 'blur'
| 'breakpoint'
| 'color'
| 'container'
| 'drop-shadow'
| 'ease'
| 'font-weight'
| 'font'
| 'inset-shadow'
| 'leading'
| 'perspective'
| 'radius'
| 'shadow'
| 'spacing'
| 'text'
| 'text-shadow'
| 'tracking'
/**
* Class group IDs included in the default configuration of tailwind-merge.
*/
export type DefaultClassGroupIds =
| 'accent'
| 'align-content'
| 'align-items'
| 'align-self'
| 'animate'
| 'appearance'
| 'aspect'
| 'auto-cols'
| 'auto-rows'
| 'backdrop-blur'
| 'backdrop-brightness'
| 'backdrop-contrast'
| 'backdrop-filter'
| 'backdrop-grayscale'
| 'backdrop-hue-rotate'
| 'backdrop-invert'
| 'backdrop-opacity'
| 'backdrop-saturate'
| 'backdrop-sepia'
| 'backface'
| 'basis'
| 'bg-attachment'
| 'bg-blend'
| 'bg-clip'
| 'bg-color'
| 'bg-image'
| 'bg-origin'
| 'bg-position'
| 'bg-repeat'
| 'bg-size'
| 'blur'
| 'border-collapse'
| 'border-color-b'
| 'border-color-e'
| 'border-color-l'
| 'border-color-r'
| 'border-color-s'
| 'border-color-t'
| 'border-color-x'
| 'border-color-y'
| 'border-color'
| 'border-spacing-x'
| 'border-spacing-y'
| 'border-spacing'
| 'border-style'
| 'border-w-b'
| 'border-w-e'
| 'border-w-l'
| 'border-w-r'
| 'border-w-s'
| 'border-w-t'
| 'border-w-x'
| 'border-w-y'
| 'border-w'
| 'bottom'
| 'box-decoration'
| 'box'
| 'break-after'
| 'break-before'
| 'break-inside'
| 'break'
| 'brightness'
| 'caption'
| 'caret-color'
| 'clear'
| 'col-end'
| 'col-start-end'
| 'col-start'
| 'color-scheme'
| 'columns'
| 'container'
| 'content'
| 'contrast'
| 'cursor'
| 'delay'
| 'display'
| 'divide-color'
| 'divide-style'
| 'divide-x-reverse'
| 'divide-x'
| 'divide-y-reverse'
| 'divide-y'
| 'drop-shadow'
| 'drop-shadow-color'
| 'duration'
| 'ease'
| 'end'
| 'field-sizing'
| 'fill'
| 'filter'
| 'flex-direction'
| 'flex-wrap'
| 'flex'
| 'float'
| 'font-family'
| 'font-size'
| 'font-smoothing'
| 'font-stretch'
| 'font-style'
| 'font-weight'
| 'forced-color-adjust'
| 'fvn-figure'
| 'fvn-fraction'
| 'fvn-normal'
| 'fvn-ordinal'
| 'fvn-slashed-zero'
| 'fvn-spacing'
| 'gap-x'
| 'gap-y'
| 'gap'
| 'gradient-from-pos'
| 'gradient-from'
| 'gradient-to-pos'
| 'gradient-to'
| 'gradient-via-pos'
| 'gradient-via'
| 'grayscale'
| 'grid-cols'
| 'grid-flow'
| 'grid-rows'
| 'grow'
| 'h'
| 'hue-rotate'
| 'hyphens'
| 'indent'
| 'inset-ring-color'
| 'inset-ring-w'
| 'inset-shadow-color'
| 'inset-shadow'
| 'inset-x'
| 'inset-y'
| 'inset'
| 'invert'
| 'isolation'
| 'justify-content'
| 'justify-items'
| 'justify-self'
| 'leading'
| 'left'
| 'line-clamp'
| 'list-image'
| 'list-style-position'
| 'list-style-type'
| 'm'
| 'mask-clip'
| 'mask-composite'
| 'mask-image-b-from-color'
| 'mask-image-b-from-pos'
| 'mask-image-b-to-color'
| 'mask-image-b-to-pos'
| 'mask-image-conic-from-color'
| 'mask-image-conic-from-pos'
| 'mask-image-conic-pos'
| 'mask-image-conic-to-color'
| 'mask-image-conic-to-pos'
| 'mask-image-l-from-color'
| 'mask-image-l-from-pos'
| 'mask-image-l-to-color'
| 'mask-image-l-to-pos'
| 'mask-image-linear-from-color'
| 'mask-image-linear-from-pos'
| 'mask-image-linear-pos'
| 'mask-image-linear-to-color'
| 'mask-image-linear-to-pos'
| 'mask-image-r-from-color'
| 'mask-image-r-from-pos'
| 'mask-image-r-to-color'
| 'mask-image-r-to-pos'
| 'mask-image-radial-from-color'
| 'mask-image-radial-from-pos'
| 'mask-image-radial-pos'
| 'mask-image-radial-shape'
| 'mask-image-radial-size'
| 'mask-image-radial-to-color'
| 'mask-image-radial-to-pos'
| 'mask-image-radial'
| 'mask-image-t-from-color'
| 'mask-image-t-from-pos'
| 'mask-image-t-to-color'
| 'mask-image-t-to-pos'
| 'mask-image-x-from-color'
| 'mask-image-x-from-pos'
| 'mask-image-x-to-color'
| 'mask-image-x-to-pos'
| 'mask-image-y-from-color'
| 'mask-image-y-from-pos'
| 'mask-image-y-to-color'
| 'mask-image-y-to-pos'
| 'mask-image'
| 'mask-mode'
| 'mask-origin'
| 'mask-position'
| 'mask-repeat'
| 'mask-size'
| 'mask-type'
| 'max-h'
| 'max-w'
| 'mb'
| 'me'
| 'min-h'
| 'min-w'
| 'mix-blend'
| 'ml'
| 'mr'
| 'ms'
| 'mt'
| 'mx'
| 'my'
| 'object-fit'
| 'object-position'
| 'opacity'
| 'order'
| 'outline-color'
| 'outline-offset'
| 'outline-style'
| 'outline-w'
| 'overflow-x'
| 'overflow-y'
| 'overflow'
| 'overscroll-x'
| 'overscroll-y'
| 'overscroll'
| 'p'
| 'pb'
| 'pe'
| 'perspective-origin'
| 'perspective'
| 'pl'
| 'place-content'
| 'place-items'
| 'place-self'
| 'placeholder-color'
| 'pointer-events'
| 'position'
| 'pr'
| 'ps'
| 'pt'
| 'px'
| 'py'
| 'resize'
| 'right'
| 'ring-color'
| 'ring-offset-color'
| 'ring-offset-w'
| 'ring-w-inset'
| 'ring-w'
| 'rotate-x'
| 'rotate-y'
| 'rotate-z'
| 'rotate'
| 'rounded-b'
| 'rounded-bl'
| 'rounded-br'
| 'rounded-e'
| 'rounded-ee'
| 'rounded-es'
| 'rounded-l'
| 'rounded-r'
| 'rounded-s'
| 'rounded-se'
| 'rounded-ss'
| 'rounded-t'
| 'rounded-tl'
| 'rounded-tr'
| 'rounded'
| 'row-end'
| 'row-start-end'
| 'row-start'
| 'saturate'
| 'scale-3d'
| 'scale-x'
| 'scale-y'
| 'scale-z'
| 'scale'
| 'scroll-behavior'
| 'scroll-m'
| 'scroll-mb'
| 'scroll-me'
| 'scroll-ml'
| 'scroll-mr'
| 'scroll-ms'
| 'scroll-mt'
| 'scroll-mx'
| 'scroll-my'
| 'scroll-p'
| 'scroll-pb'
| 'scroll-pe'
| 'scroll-pl'
| 'scroll-pr'
| 'scroll-ps'
| 'scroll-pt'
| 'scroll-px'
| 'scroll-py'
| 'select'
| 'sepia'
| 'shadow-color'
| 'shadow'
| 'shrink'
| 'size'
| 'skew-x'
| 'skew-y'
| 'skew'
| 'snap-align'
| 'snap-stop'
| 'snap-strictness'
| 'snap-type'
| 'space-x-reverse'
| 'space-x'
| 'space-y-reverse'
| 'space-y'
| 'sr'
| 'start'
| 'stroke-w'
| 'stroke'
| 'table-layout'
| 'text-alignment'
| 'text-color'
| 'text-decoration-color'
| 'text-decoration-style'
| 'text-decoration-thickness'
| 'text-decoration'
| 'text-overflow'
| 'text-shadow'
| 'text-shadow-color'
| 'text-transform'
| 'text-wrap'
| 'top'
| 'touch-pz'
| 'touch-x'
| 'touch-y'
| 'touch'
| 'tracking'
| 'transform-origin'
| 'transform-style'
| 'transform'
| 'transition-behavior'
| 'transition'
| 'translate-none'
| 'translate-x'
| 'translate-y'
| 'translate-z'
| 'translate'
| 'underline-offset'
| 'vertical-align'
| 'visibility'
| 'w'
| 'whitespace'
| 'will-change'
| 'wrap'
| 'z'
export type AnyClassGroupIds = string
export type AnyThemeGroupIds = string
/**
* type of the tailwind-merge configuration that allows for any possible configuration.
*/
export type AnyConfig = Config<AnyClassGroupIds, AnyThemeGroupIds>

17
node_modules/tailwind-merge/src/lib/utils.ts generated vendored Normal file
View File

@@ -0,0 +1,17 @@
/**
* Concatenates two arrays faster than the array spread operator.
*/
export const concatArrays = <T, U>(
array1: readonly T[],
array2: readonly U[],
): readonly (T | U)[] => {
// Pre-allocate for better V8 optimization
const combinedArray: (T | U)[] = new Array(array1.length + array2.length)
for (let i = 0; i < array1.length; i++) {
combinedArray[i] = array1[i]!
}
for (let i = 0; i < array2.length; i++) {
combinedArray[array1.length + i] = array2[i]!
}
return combinedArray
}

128
node_modules/tailwind-merge/src/lib/validators.ts generated vendored Normal file
View File

@@ -0,0 +1,128 @@
const arbitraryValueRegex = /^\[(?:(\w[\w-]*):)?(.+)\]$/i
const arbitraryVariableRegex = /^\((?:(\w[\w-]*):)?(.+)\)$/i
const fractionRegex = /^\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$/
const colorFunctionRegex = /^(rgba?|hsla?|hwb|(ok)?(lab|lch)|color-mix)\(.+\)$/
// Shadow always begins with x and y offset separated by underscore optionally prepended by inset
const shadowRegex = /^(inset_)?-?((\d+)?\.?(\d+)[a-z]+|0)_-?((\d+)?\.?(\d+)[a-z]+|0)/
const imageRegex =
/^(url|image|image-set|cross-fade|element|(repeating-)?(linear|radial|conic)-gradient)\(.+\)$/
export const isFraction = (value: string) => fractionRegex.test(value)
export const isNumber = (value: string) => !!value && !Number.isNaN(Number(value))
export const isInteger = (value: string) => !!value && Number.isInteger(Number(value))
export const isPercent = (value: string) => value.endsWith('%') && isNumber(value.slice(0, -1))
export const isTshirtSize = (value: string) => tshirtUnitRegex.test(value)
export const isAny = () => true
const isLengthOnly = (value: string) =>
// `colorFunctionRegex` check is necessary because color functions can have percentages in them which which would be incorrectly classified as lengths.
// For example, `hsl(0 0% 0%)` would be classified as a length without this check.
// I could also use lookbehind assertion in `lengthUnitRegex` but that isn't supported widely enough.
lengthUnitRegex.test(value) && !colorFunctionRegex.test(value)
const isNever = () => false
const isShadow = (value: string) => shadowRegex.test(value)
const isImage = (value: string) => imageRegex.test(value)
export const isAnyNonArbitrary = (value: string) =>
!isArbitraryValue(value) && !isArbitraryVariable(value)
export const isArbitrarySize = (value: string) => getIsArbitraryValue(value, isLabelSize, isNever)
export const isArbitraryValue = (value: string) => arbitraryValueRegex.test(value)
export const isArbitraryLength = (value: string) =>
getIsArbitraryValue(value, isLabelLength, isLengthOnly)
export const isArbitraryNumber = (value: string) =>
getIsArbitraryValue(value, isLabelNumber, isNumber)
export const isArbitraryPosition = (value: string) =>
getIsArbitraryValue(value, isLabelPosition, isNever)
export const isArbitraryImage = (value: string) => getIsArbitraryValue(value, isLabelImage, isImage)
export const isArbitraryShadow = (value: string) =>
getIsArbitraryValue(value, isLabelShadow, isShadow)
export const isArbitraryVariable = (value: string) => arbitraryVariableRegex.test(value)
export const isArbitraryVariableLength = (value: string) =>
getIsArbitraryVariable(value, isLabelLength)
export const isArbitraryVariableFamilyName = (value: string) =>
getIsArbitraryVariable(value, isLabelFamilyName)
export const isArbitraryVariablePosition = (value: string) =>
getIsArbitraryVariable(value, isLabelPosition)
export const isArbitraryVariableSize = (value: string) => getIsArbitraryVariable(value, isLabelSize)
export const isArbitraryVariableImage = (value: string) =>
getIsArbitraryVariable(value, isLabelImage)
export const isArbitraryVariableShadow = (value: string) =>
getIsArbitraryVariable(value, isLabelShadow, true)
// Helpers
const getIsArbitraryValue = (
value: string,
testLabel: (label: string) => boolean,
testValue: (value: string) => boolean,
) => {
const result = arbitraryValueRegex.exec(value)
if (result) {
if (result[1]) {
return testLabel(result[1])
}
return testValue(result[2]!)
}
return false
}
const getIsArbitraryVariable = (
value: string,
testLabel: (label: string) => boolean,
shouldMatchNoLabel = false,
) => {
const result = arbitraryVariableRegex.exec(value)
if (result) {
if (result[1]) {
return testLabel(result[1])
}
return shouldMatchNoLabel
}
return false
}
// Labels
const isLabelPosition = (label: string) => label === 'position' || label === 'percentage'
const isLabelImage = (label: string) => label === 'image' || label === 'url'
const isLabelSize = (label: string) => label === 'length' || label === 'size' || label === 'bg-size'
const isLabelLength = (label: string) => label === 'length'
const isLabelNumber = (label: string) => label === 'number'
const isLabelFamilyName = (label: string) => label === 'family-name'
const isLabelShadow = (label: string) => label === 'shadow'