diff --git a/docs/api-reference.md b/docs/api-reference.md index 3a2b3b97..ff0d41f0 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -243,11 +243,11 @@ const paddingClassGroup = [{ p: [validators.isLength] }] A brief summary for each validator: -- `isLength` checks whether a class part is a number (`3`, `1.5`), a fraction (`3/4`), a arbitrary length (`[3%]`, `[4px]`, `[length:var(--my-var)]`), or one of the strings `px`, `full` or `screen`. +- `isLength` checks whether a class part is a number (`3`, `1.5`), a fraction (`3/4`), or one of the strings `px`, `full` or `screen`. - `isArbitraryLength` checks for arbitrary length values (`[3%]`, `[4px]`, `[length:var(--my-var)]`). - `isNumber` checks for numbers (`3`, `1.5`) - `isArbitraryNumber` checks whether class part is an arbitrary value which starts with `number:` or is a number (`[number:var(--value)]`, `[450]`) which is necessary for font-weight and stroke-width classNames. -- `isInteger` checks for integer values (`3`) and arbitrary integer values (`[3]`). +- `isInteger` checks for integer values (`3`). - `isPercent` checks for percent values (`12.5%`) which is used for color stop positions. - `isArbitraryValue` checks whether the class part is enclosed in brackets (`[something]`) - `isTshirtSize`checks whether class part is a T-shirt size (`sm`, `xl`), optionally with a preceding number (`2xl`). diff --git a/src/lib/default-config.ts b/src/lib/default-config.ts index e8b357ff..a026dcaa 100644 --- a/src/lib/default-config.ts +++ b/src/lib/default-config.ts @@ -47,7 +47,7 @@ export function getDefaultConfig() { const getOverflow = () => ['auto', 'hidden', 'clip', 'visible', 'scroll'] as const const getSpacingWithAutoAndArbitrary = () => ['auto', isArbitraryValue, spacing] as const const getSpacingWithArbitrary = () => [isArbitraryValue, spacing] as const - const getLengthWithEmpty = () => ['', isLength] as const + const getLengthWithEmptyAndArbitrary = () => ['', isLength, isArbitraryLength] as const const getNumberWithAutoAndArbitrary = () => ['auto', isNumber, isArbitraryValue] as const const getPositions = () => [ @@ -95,13 +95,13 @@ export function getDefaultConfig() { separator: ':', theme: { colors: [isAny], - spacing: [isLength], + spacing: [isLength, isArbitraryLength], blur: ['none', '', isTshirtSize, isArbitraryValue], brightness: getNumber(), borderColor: [colors], borderRadius: ['none', '', 'full', isTshirtSize, isArbitraryValue], borderSpacing: getSpacingWithArbitrary(), - borderWidth: getLengthWithEmpty(), + borderWidth: getLengthWithEmptyAndArbitrary(), contrast: getNumber(), grayscale: getZeroAndEmpty(), hueRotate: getNumberAndArbitrary(), @@ -303,7 +303,7 @@ export function getDefaultConfig() { * Z-Index * @see https://tailwindcss.com/docs/z-index */ - z: [{ z: ['auto', isInteger] }], + z: [{ z: ['auto', isInteger, isArbitraryValue] }], // Flexbox and Grid /** * Flex Basis @@ -339,7 +339,7 @@ export function getDefaultConfig() { * Order * @see https://tailwindcss.com/docs/order */ - order: [{ order: ['first', 'last', 'none', isInteger] }], + order: [{ order: ['first', 'last', 'none', isInteger, isArbitraryValue] }], /** * Grid Template Columns * @see https://tailwindcss.com/docs/grid-template-columns @@ -349,7 +349,15 @@ export function getDefaultConfig() { * Grid Column Start / End * @see https://tailwindcss.com/docs/grid-column */ - 'col-start-end': [{ col: ['auto', { span: ['full', isInteger] }, isArbitraryValue] }], + 'col-start-end': [ + { + col: [ + 'auto', + { span: ['full', isInteger, isArbitraryValue] }, + isArbitraryValue, + ], + }, + ], /** * Grid Column Start * @see https://tailwindcss.com/docs/grid-column @@ -369,7 +377,9 @@ export function getDefaultConfig() { * Grid Row Start / End * @see https://tailwindcss.com/docs/grid-row */ - 'row-start-end': [{ row: ['auto', { span: [isInteger] }, isArbitraryValue] }], + 'row-start-end': [ + { row: ['auto', { span: [isInteger, isArbitraryValue] }, isArbitraryValue] }, + ], /** * Grid Row Start * @see https://tailwindcss.com/docs/grid-row @@ -606,7 +616,7 @@ export function getDefaultConfig() { * Min-Height * @see https://tailwindcss.com/docs/min-height */ - 'min-h': [{ 'min-h': ['min', 'max', 'fit', isArbitraryValue, isLength] }], + 'min-h': [{ 'min-h': ['min', 'max', 'fit', isLength, isArbitraryValue] }], /** * Max-Height * @see https://tailwindcss.com/docs/max-height @@ -718,8 +728,8 @@ export function getDefaultConfig() { 'normal', 'relaxed', 'loose', - isArbitraryValue, isLength, + isArbitraryValue, ], }, ], @@ -778,12 +788,14 @@ export function getDefaultConfig() { * Text Decoration Thickness * @see https://tailwindcss.com/docs/text-decoration-thickness */ - 'text-decoration-thickness': [{ decoration: ['auto', 'from-font', isLength] }], + 'text-decoration-thickness': [ + { decoration: ['auto', 'from-font', isLength, isArbitraryLength] }, + ], /** * Text Underline Offset * @see https://tailwindcss.com/docs/text-underline-offset */ - 'underline-offset': [{ 'underline-offset': ['auto', isArbitraryValue, isLength] }], + 'underline-offset': [{ 'underline-offset': ['auto', isLength, isArbitraryValue] }], /** * Text Decoration Color * @see https://tailwindcss.com/docs/text-decoration-color @@ -1140,12 +1152,12 @@ export function getDefaultConfig() { * Outline Offset * @see https://tailwindcss.com/docs/outline-offset */ - 'outline-offset': [{ 'outline-offset': [isArbitraryValue, isLength] }], + 'outline-offset': [{ 'outline-offset': [isLength, isArbitraryValue] }], /** * Outline Width * @see https://tailwindcss.com/docs/outline-width */ - 'outline-w': [{ outline: [isLength] }], + 'outline-w': [{ outline: [isLength, isArbitraryLength] }], /** * Outline Color * @see https://tailwindcss.com/docs/outline-color @@ -1155,7 +1167,7 @@ export function getDefaultConfig() { * Ring Width * @see https://tailwindcss.com/docs/ring-width */ - 'ring-w': [{ ring: getLengthWithEmpty() }], + 'ring-w': [{ ring: getLengthWithEmptyAndArbitrary() }], /** * Ring Width Inset * @see https://tailwindcss.com/docs/ring-width @@ -1175,7 +1187,7 @@ export function getDefaultConfig() { * Ring Offset Width * @see https://tailwindcss.com/docs/ring-offset-width */ - 'ring-offset-w': [{ 'ring-offset': [isLength] }], + 'ring-offset-w': [{ 'ring-offset': [isLength, isArbitraryLength] }], /** * Ring Offset Color * @see https://tailwindcss.com/docs/ring-offset-color @@ -1671,7 +1683,7 @@ export function getDefaultConfig() { * Stroke Width * @see https://tailwindcss.com/docs/stroke-width */ - 'stroke-w': [{ stroke: [isLength, isArbitraryNumber] }], + 'stroke-w': [{ stroke: [isLength, isArbitraryLength, isArbitraryNumber] }], /** * Stroke * @see https://tailwindcss.com/docs/stroke diff --git a/src/lib/validators.ts b/src/lib/validators.ts index f08aa01d..295ffdd4 100644 --- a/src/lib/validators.ts +++ b/src/lib/validators.ts @@ -8,62 +8,61 @@ const lengthUnitRegex = const shadowRegex = /^-?((\d+)?\.?(\d+)[a-z]+|0)_-?((\d+)?\.?(\d+)[a-z]+|0)/ export function isLength(value: string) { - return ( - isNumber(value) || - stringLengths.has(value) || - fractionRegex.test(value) || - isArbitraryLength(value) - ) + return isNumber(value) || stringLengths.has(value) || fractionRegex.test(value) } export function isArbitraryLength(value: string) { return getIsArbitraryValue(value, 'length', isLengthOnly) } -export function isArbitrarySize(value: string) { - return getIsArbitraryValue(value, 'size', isNever) -} - -export function isArbitraryPosition(value: string) { - return getIsArbitraryValue(value, 'position', isNever) -} - -export function isArbitraryUrl(value: string) { - return getIsArbitraryValue(value, 'url', isUrl) +export function isNumber(value: string) { + return !Number.isNaN(Number(value)) } export function isArbitraryNumber(value: string) { return getIsArbitraryValue(value, 'number', isNumber) } -export function isNumber(value: string) { - return !Number.isNaN(Number(value)) +export function isInteger(value: string) { + return Number.isInteger(Number(value)) } export function isPercent(value: string) { return value.endsWith('%') && isNumber(value.slice(0, -1)) } -export function isInteger(value: string) { - return isIntegerOnly(value) || getIsArbitraryValue(value, 'number', isIntegerOnly) -} - export function isArbitraryValue(value: string) { return arbitraryValueRegex.test(value) } -export function isAny() { - return true -} - export function isTshirtSize(value: string) { return tshirtUnitRegex.test(value) } +export function isArbitrarySize(value: string) { + return getIsArbitraryValue(value, 'size', isNever) +} + +export function isArbitraryPosition(value: string) { + return getIsArbitraryValue(value, 'position', isNever) +} + +export function isArbitraryUrl(value: string) { + return getIsArbitraryValue(value, 'url', isUrl) +} + export function isArbitraryShadow(value: string) { return getIsArbitraryValue(value, '', isShadow) } +export function isAny() { + return true +} + +function isLengthOnly(value: string) { + return lengthUnitRegex.test(value) +} + function getIsArbitraryValue(value: string, label: string, testValue: (value: string) => boolean) { const result = arbitraryValueRegex.exec(value) @@ -78,10 +77,6 @@ function getIsArbitraryValue(value: string, label: string, testValue: (value: st return false } -function isLengthOnly(value: string) { - return lengthUnitRegex.test(value) -} - function isNever() { return false } @@ -90,10 +85,6 @@ function isUrl(value: string) { return value.startsWith('url(') } -function isIntegerOnly(value: string) { - return Number.isInteger(Number(value)) -} - function isShadow(value: string) { return shadowRegex.test(value) } diff --git a/tests/validators.test.ts b/tests/validators.test.ts index a5405fc2..d45cbb60 100644 --- a/tests/validators.test.ts +++ b/tests/validators.test.ts @@ -25,13 +25,13 @@ test('isLength', () => { expect(isLength('screen')).toBe(true) expect(isLength('1/2')).toBe(true) expect(isLength('123/345')).toBe(true) - expect(isLength('[3.7%]')).toBe(true) - expect(isLength('[481px]')).toBe(true) - expect(isLength('[19.1rem]')).toBe(true) - expect(isLength('[50vw]')).toBe(true) - expect(isLength('[56vh]')).toBe(true) - expect(isLength('[length:var(--arbitrary)]')).toBe(true) + expect(isLength('[3.7%]')).toBe(false) + expect(isLength('[481px]')).toBe(false) + expect(isLength('[19.1rem]')).toBe(false) + expect(isLength('[50vw]')).toBe(false) + expect(isLength('[56vh]')).toBe(false) + expect(isLength('[length:var(--arbitrary)]')).toBe(false) expect(isLength('1d5')).toBe(false) expect(isLength('[1]')).toBe(false) expect(isLength('[12px')).toBe(false) @@ -60,9 +60,9 @@ test('isInteger', () => { expect(isInteger('1')).toBe(true) expect(isInteger('123')).toBe(true) expect(isInteger('8312')).toBe(true) - expect(isInteger('[8312]')).toBe(true) - expect(isInteger('[2]')).toBe(true) + expect(isInteger('[8312]')).toBe(false) + expect(isInteger('[2]')).toBe(false) expect(isInteger('[8312px]')).toBe(false) expect(isInteger('[8312%]')).toBe(false) expect(isInteger('[8312rem]')).toBe(false) @@ -178,6 +178,7 @@ test('isPercent', () => { expect(isPercent('100.001%')).toBe(true) expect(isPercent('.01%')).toBe(true) expect(isPercent('0%')).toBe(true) + expect(isPercent('0')).toBe(false) expect(isPercent('one%')).toBe(false) })