Skip to content

Commit 2f2a1a2

Browse files
committed
feat: support outline* style props
1 parent 7a60aff commit 2f2a1a2

File tree

8 files changed

+134
-1
lines changed

8 files changed

+134
-1
lines changed

src/UtilityParser.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {
2727
import pointerEvents from './resolve/pointer-events';
2828
import userSelect from './resolve/user-select';
2929
import textDecorationStyle from './resolve/text-decoration-style';
30+
import { outlineOffset, outlineStyle, outlineWidth } from './resolve/outline';
3031

3132
export default class UtilityParser {
3233
private position = 0;
@@ -359,6 +360,22 @@ export default class UtilityParser {
359360
if (style) return style;
360361
}
361362

363+
if (this.consumePeeked(`outline-`)) {
364+
if (this.consumePeeked(`offset-`)) {
365+
style = outlineOffset(this.rest, this.isNegative, theme?.outlineOffset);
366+
if (style) return style;
367+
}
368+
369+
style = outlineWidth(this.rest, theme?.outlineWidth);
370+
if (style) return style;
371+
372+
style = outlineStyle(this.rest);
373+
if (style) return style;
374+
375+
style = color(`outline`, this.rest, theme?.colors);
376+
if (style) return style;
377+
}
378+
362379
h.warn(`\`${this.isNegative ? `-` : ``}${this.rest}\` unknown or invalid utility`);
363380
return null;
364381
}

src/__tests__/outline.spec.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { describe, test, expect } from '@jest/globals';
2+
import { create } from '../';
3+
4+
describe(`text-decoration utilities`, () => {
5+
let tw = create();
6+
beforeEach(() => (tw = create()));
7+
8+
const cases: Array<[string, Record<string, string | number>]> = [
9+
// outline style
10+
[`outline`, { outlineStyle: `solid` }],
11+
[`outline-dotted`, { outlineStyle: `dotted` }],
12+
[`outline-dashed`, { outlineStyle: `dashed` }],
13+
// outline width
14+
[`outline-4`, { outlineWidth: 4 }],
15+
[`outline-[5px]`, { outlineWidth: 5 }],
16+
// outline offset
17+
[`outline-offset-0`, { outlineOffset: 0 }],
18+
[`outline-offset-[-3px]`, { outlineOffset: -3 }],
19+
// all values mix
20+
[
21+
`outline outline-1 outline-[#58c4dc] outline-offset-2`,
22+
{
23+
outlineWidth: 1,
24+
outlineStyle: `solid`,
25+
outlineOffset: 2,
26+
outlineColor: `#58c4dc`,
27+
},
28+
],
29+
[
30+
`outline-dashed outline-[6px] outline-offset-[-2px] outline-black/50`,
31+
{
32+
outlineWidth: 6,
33+
outlineStyle: `dashed`,
34+
outlineOffset: -2,
35+
outlineColor: `rgba(0, 0, 0, 0.5)`,
36+
},
37+
],
38+
];
39+
40+
test.each(cases)(`tw\`%s\` -> %s`, (utility, expected) => {
41+
expect(tw.style(utility)).toEqual(expected);
42+
});
43+
});

src/__tests__/simple-mappings.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ describe(`simple style mappings`, () => {
9898
[`box-border`, { boxSizing: `border-box` }],
9999
[`box-content`, { boxSizing: `content-box` }],
100100

101+
[`outline`, { outlineStyle: `solid` }],
102+
101103
// default box-shadow implementations
102104
[
103105
`shadow-sm`,

src/resolve/color.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ const STYLE_PROPS = {
107107
borderRight: { opacity: `__opacity_border`, color: `borderRightColor` },
108108
shadow: { opacity: `__opacity_shadow`, color: `shadowColor` },
109109
decoration: { opacity: `__opacity_decoration`, color: `textDecorationColor` },
110+
outline: { opacity: `__opacity_decoration`, color: `outlineColor` },
110111
tint: { opacity: `__opacity_tint`, color: `tintColor` },
111112
};
112113

src/resolve/outline.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import type { StyleIR } from '../types';
2+
import type { TwTheme } from '../tw-config';
3+
import { complete, parseStyleVal, parseUnconfigged } from '../helpers';
4+
5+
const ALLOWED_STYLE_VALUES = [`dotted`, `dashed`];
6+
export function outlineStyle(value: string): StyleIR | null {
7+
if (!ALLOWED_STYLE_VALUES.includes(value)) return null;
8+
9+
return complete({
10+
outlineStyle: value,
11+
});
12+
}
13+
14+
export function outlineWidth(
15+
value: string,
16+
config?: TwTheme['outlineWidth'],
17+
): StyleIR | null {
18+
const configValue = config?.[value];
19+
20+
if (configValue) {
21+
const parsedConfigValue = parseStyleVal(configValue);
22+
if (parsedConfigValue !== null) {
23+
return complete({
24+
outlineWidth: parsedConfigValue,
25+
});
26+
}
27+
}
28+
29+
const parsedValue = parseUnconfigged(value);
30+
if (parsedValue !== null) {
31+
return complete({
32+
outlineWidth: parsedValue,
33+
});
34+
}
35+
36+
return null;
37+
}
38+
39+
export function outlineOffset(
40+
value: string,
41+
isNegative: boolean,
42+
config?: TwTheme['outlineOffset'],
43+
): StyleIR | null {
44+
const configValue = config?.[value];
45+
46+
if (configValue) {
47+
const parsedConfigValue = parseStyleVal(configValue);
48+
if (parsedConfigValue !== null) {
49+
return complete({
50+
outlineOffset: isNegative ? -parsedConfigValue : parsedConfigValue,
51+
});
52+
}
53+
}
54+
55+
const parsedValue = parseUnconfigged(value);
56+
if (parsedValue !== null) {
57+
return complete({
58+
outlineOffset: isNegative ? -parsedValue : parsedValue,
59+
});
60+
}
61+
62+
return null;
63+
}

src/styles.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,8 @@ const defaultStyles: Array<[string, StyleIR]> = [
121121
[`box-border`, complete({ boxSizing: `border-box` })],
122122
[`box-content`, complete({ boxSizing: `content-box` })],
123123

124+
[`outline`, complete({ outlineStyle: `solid` })],
125+
124126
// default box-shadow implementations
125127
[
126128
`shadow-sm`,

src/tw-config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,20 +43,24 @@ export interface TwTheme {
4343
skew?: Record<string, string>;
4444
translate?: Record<string, string>;
4545
transformOrigin?: Record<string, string>;
46+
outlineOffset?: Record<string, string>;
47+
outlineWidth?: Record<string, string>;
4648
extend?: Omit<TwTheme, 'extend'>;
4749
//
4850
colors?: TwColors;
4951
backgroundColor?: TwColors; // bg-
5052
borderColor?: TwColors; // border-
5153
textColor?: TwColors; // text-
5254
textDecorationColor?: TwColors; // decoration-
55+
outlineColor?: TwColors; // outline-
5356
}
5457

5558
export const PREFIX_COLOR_PROP_MAP = {
5659
'bg-': `backgroundColor`,
5760
'border-': `borderColor`,
5861
'text-': `textColor`,
5962
'decoration-': `textDecorationColor`,
63+
'outline-': `outlineColor`,
6064
} as const;
6165

6266
export interface TwConfig {

src/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@ export type ColorStyleType =
9595
| 'borderBottom'
9696
| 'shadow'
9797
| 'tint'
98-
| 'decoration';
98+
| 'decoration'
99+
| 'outline';
99100

100101
export type Direction =
101102
| 'All'

0 commit comments

Comments
 (0)