Skip to content

Commit 2a98c9f

Browse files
authored
Add missing variants to Tailwind plugin (#5027)
1 parent 50b1e19 commit 2a98c9f

File tree

4 files changed

+176
-13
lines changed

4 files changed

+176
-13
lines changed

packages/react-aria-components/src/DateField.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ function DateSegment({segment, ...otherProps}: DateSegmentProps, ref: ForwardedR
274274
{...mergeProps(filterDOMProps(otherProps as any), segmentProps)}
275275
{...renderProps}
276276
ref={domRef}
277+
data-placeholder={segment.isPlaceholder || undefined}
277278
data-invalid={state.isInvalid || undefined}
278279
data-readonly={!segment.isEditable || undefined}
279280
data-type={segment.type} />

packages/react-aria-components/src/ListBox.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ export interface ListBoxRenderProps {
4242
* @selector [data-drop-target]
4343
*/
4444
isDropTarget: boolean,
45+
/**
46+
* Whether the items are arranged in a stack or grid.
47+
* @selector [data-layout="stack | grid"]
48+
*/
49+
layout: 'stack' | 'grid',
4550
/**
4651
* State of the listbox.
4752
*/
@@ -231,6 +236,7 @@ function ListBoxInner<T>({state, props, listBoxRef}: ListBoxInnerProps<T>) {
231236
isEmpty: state.collection.size === 0,
232237
isFocused,
233238
isFocusVisible,
239+
layout: props.layout || 'stack',
234240
state
235241
}
236242
});

packages/tailwindcss-react-aria-components/src/index.js

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,37 +5,60 @@ const attributes = {
55
['hover', 'hovered'],
66
['focus', 'focused'],
77
'focus-visible',
8+
'focus-within',
89
'pressed',
910
'disabled',
1011
'drop-target',
1112
'dragging',
1213
'empty',
14+
'allows-dragging',
1315
'allows-removing',
1416
'allows-sorting',
15-
'placeholder',
17+
['placeholder-shown', 'placeholder'],
1618
'selected',
1719
'indeterminate',
18-
'readonly',
20+
['read-only', 'readonly'],
1921
'required',
2022
'entering',
2123
'exiting',
2224
'open',
2325
'unavailable',
26+
'outside-month',
27+
'outside-visible-range',
28+
'selection-start',
29+
'selection-end',
2430
'current',
25-
'invalid'
31+
'invalid',
32+
'resizing'
2633
],
2734
enum: {
2835
placement: ['left', 'right', 'top', 'bottom'],
2936
type: ['literal', 'year', 'month', 'day'],
3037
layout: ['grid', 'stack'],
31-
orientation: ['horizontal', 'vertical']
38+
orientation: ['horizontal', 'vertical'],
39+
'selection-mode': ['single', 'multiple'],
40+
'resizable-direction': ['right', 'left', 'both'],
41+
'sort-direction': ['ascending', 'descending']
3242
}
3343
};
3444

45+
const shortNames = {
46+
'selection-mode': 'selection',
47+
'resizable-direction': 'resizable',
48+
'sort-direction': 'sort'
49+
};
50+
3551
// Variants we use that are already defined by Tailwind:
3652
// https://github.com/tailwindlabs/tailwindcss/blob/a2fa6932767ab328515f743d6188c2164ad2a5de/src/corePlugins.js#L84
37-
const nativeVariants = ['indeterminate', 'required', 'invalid', 'empty', 'focus-visible', 'disabled'];
38-
const nativeVariantSelectors = new Map([...nativeVariants.map((variant) => [variant, `:${variant}`]), ['hovered', ':hover'], ['focused', ':focus'], ['open', '[open]']]);
53+
const nativeVariants = ['indeterminate', 'required', 'invalid', 'empty', 'focus-visible', 'focus-within', 'disabled'];
54+
const nativeVariantSelectors = new Map([
55+
...nativeVariants.map((variant) => [variant, `:${variant}`]),
56+
['hovered', ':hover'],
57+
['focused', ':focus'],
58+
['readonly', ':read-only'],
59+
['open', '[open]'],
60+
['placeholder', ':placeholder-shown']
61+
]);
3962

4063
// If no prefix is specified, we want to avoid overriding native variants on non-RAC components, so we only target elements with the data-rac attribute for those variants.
4164
let getSelector = (prefix, attributeName, attributeValue) => {
@@ -55,13 +78,18 @@ module.exports = plugin.withOptions((options) => (({addVariant}) => {
5578
let attributeName = Array.isArray(attribute) ? attribute[1] : attribute;
5679
let selector = getSelector(prefix, attributeName);
5780
addVariant(variantName, selector);
81+
addVariant(`group-${variantName}`, `:merge(.group)${selector.slice(1)} &`);
82+
addVariant(`peer-${variantName}`, `:merge(.peer)${selector.slice(1)} ~ &`);
5883
});
5984
Object.keys(attributes.enum).forEach((attributeName) => {
6085
attributes.enum[attributeName].forEach(
6186
(attributeValue) => {
62-
let variantName = `${prefix}${attributeName}-${attributeValue}`;
87+
let name = shortNames[attributeName] || attributeName;
88+
let variantName = `${prefix}${name}-${attributeValue}`;
6389
let selector = getSelector(prefix, attributeName, attributeValue);
6490
addVariant(variantName, selector);
91+
addVariant(`group-${variantName}`, `:merge(.group)${selector.slice(1)} &`);
92+
addVariant(`peer-${variantName}`, `:merge(.peer)${selector.slice(1)} ~ &`);
6593
}
6694
);
6795
});

packages/tailwindcss-react-aria-components/src/index.test.js

Lines changed: 134 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ function run({options, content}) {
2323
}
2424

2525
test('variants', async () => {
26-
let content = html`<div data-rac className="hover:bg-red focus:bg-red focus-visible:bg-red pressed:bg-red disabled:bg-red drop-target:bg-red dragging:bg-red empty:bg-red allows-removing:bg-red allows-sorting:bg-red placeholder:bg-red selected:bg-red indeterminate:bg-red readonly:bg-red required:bg-red entering:bg-red exiting:bg-red open:bg-red unavailable:bg-red current:bg-red invalid:bg-red placement-left:bg-red placement-right:bg-red placement-top:bg-red placement-bottom:bg-red type-literal:bg-red type-year:bg-red type-month:bg-red type-day:bg-red layout-grid:bg-red layout-stack:bg-red orientation-horizontal:bg-red orientation-vertical:bg-red"></div>`;
26+
let content = html`<div data-rac className="hover:bg-red focus:bg-red focus-visible:bg-red focus-within:bg-red pressed:bg-red disabled:bg-red drop-target:bg-red dragging:bg-red empty:bg-red allows-dragging:bg-red allows-removing:bg-red allows-sorting:bg-red placeholder-shown:bg-red selected:bg-red indeterminate:bg-red read-only:bg-red required:bg-red entering:bg-red exiting:bg-red open:bg-red unavailable:bg-red outside-month:bg-red outside-visible-range:bg-red selection-start:bg-red selection-end:bg-red current:bg-red invalid:bg-red resizing:bg-red placement-left:bg-red placement-right:bg-red placement-top:bg-red placement-bottom:bg-red type-literal:bg-red type-year:bg-red type-month:bg-red type-day:bg-red layout-grid:bg-red layout-stack:bg-red orientation-horizontal:bg-red orientation-vertical:bg-red selection-single:bg-red selection-multiple:bg-red resizable-right:bg-red resizable-left:bg-red resizable-both:bg-red sort-ascending:bg-red sort-descending:bg-red group-pressed:bg-red peer-pressed:bg-red"></div>`;
2727
return run({content}).then((result) => {
2828
expect(result.css).toContain(css`
2929
.hover\:bg-red:is([data-rac][data-hovered], :not([data-rac]):hover) {
@@ -38,10 +38,22 @@ test('variants', async () => {
3838
--tw-bg-opacity: 1;
3939
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
4040
}
41+
.focus-within\:bg-red:is([data-rac][data-focus-within], :not([data-rac]):focus-within) {
42+
--tw-bg-opacity: 1;
43+
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
44+
}
4145
.pressed\:bg-red[data-pressed] {
4246
--tw-bg-opacity: 1;
4347
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
4448
}
49+
.group[data-pressed] .group-pressed\:bg-red {
50+
--tw-bg-opacity: 1;
51+
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
52+
}
53+
.peer[data-pressed] ~ .peer-pressed\:bg-red {
54+
--tw-bg-opacity: 1;
55+
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
56+
}
4557
.disabled\:bg-red:is([data-rac][data-disabled], :not([data-rac]):disabled) {
4658
--tw-bg-opacity: 1;
4759
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
@@ -58,6 +70,10 @@ test('variants', async () => {
5870
--tw-bg-opacity: 1;
5971
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
6072
}
73+
.allows-dragging\:bg-red[data-allows-dragging] {
74+
--tw-bg-opacity: 1;
75+
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
76+
}
6177
.allows-removing\:bg-red[data-allows-removing] {
6278
--tw-bg-opacity: 1;
6379
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
@@ -66,7 +82,7 @@ test('variants', async () => {
6682
--tw-bg-opacity: 1;
6783
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
6884
}
69-
.placeholder\:bg-red[data-placeholder] {
85+
.placeholder-shown\:bg-red:is([data-rac][data-placeholder], :not([data-rac]):placeholder-shown) {
7086
--tw-bg-opacity: 1;
7187
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
7288
}
@@ -78,7 +94,7 @@ test('variants', async () => {
7894
--tw-bg-opacity: 1;
7995
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
8096
}
81-
.readonly\:bg-red[data-readonly] {
97+
.read-only\:bg-red:is([data-rac][data-readonly], :not([data-rac]):read-only) {
8298
--tw-bg-opacity: 1;
8399
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
84100
}
@@ -102,6 +118,22 @@ test('variants', async () => {
102118
--tw-bg-opacity: 1;
103119
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
104120
}
121+
.outside-month\:bg-red[data-outside-month] {
122+
--tw-bg-opacity: 1;
123+
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
124+
}
125+
.outside-visible-range\:bg-red[data-outside-visible-range] {
126+
--tw-bg-opacity: 1;
127+
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
128+
}
129+
.selection-start\:bg-red[data-selection-start] {
130+
--tw-bg-opacity: 1;
131+
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
132+
}
133+
.selection-end\:bg-red[data-selection-end] {
134+
--tw-bg-opacity: 1;
135+
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
136+
}
105137
.current\:bg-red[data-current] {
106138
--tw-bg-opacity: 1;
107139
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
@@ -110,6 +142,10 @@ test('variants', async () => {
110142
--tw-bg-opacity: 1;
111143
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
112144
}
145+
.resizing\:bg-red[data-resizing] {
146+
--tw-bg-opacity: 1;
147+
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
148+
}
113149
.placement-left\:bg-red[data-placement="left"] {
114150
--tw-bg-opacity: 1;
115151
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
@@ -157,13 +193,41 @@ test('variants', async () => {
157193
.orientation-vertical\:bg-red[data-orientation="vertical"] {
158194
--tw-bg-opacity: 1;
159195
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
196+
}
197+
.selection-single\:bg-red[data-selection-mode="single"] {
198+
--tw-bg-opacity: 1;
199+
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
200+
}
201+
.selection-multiple\:bg-red[data-selection-mode="multiple"] {
202+
--tw-bg-opacity: 1;
203+
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
204+
}
205+
.resizable-right\:bg-red[data-resizable-direction="right"] {
206+
--tw-bg-opacity: 1;
207+
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
208+
}
209+
.resizable-left\:bg-red[data-resizable-direction="left"] {
210+
--tw-bg-opacity: 1;
211+
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
212+
}
213+
.resizable-both\:bg-red[data-resizable-direction="both"] {
214+
--tw-bg-opacity: 1;
215+
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
216+
}
217+
.sort-ascending\:bg-red[data-sort-direction="ascending"] {
218+
--tw-bg-opacity: 1;
219+
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
220+
}
221+
.sort-descending\:bg-red[data-sort-direction="descending"] {
222+
--tw-bg-opacity: 1;
223+
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
160224
}`
161225
);
162226
});
163227
});
164228

165229
test('variants with prefix', async () => {
166-
let content = html`<div data-rac className="rac-hover:bg-red rac-focus:bg-red rac-focus-visible:bg-red rac-pressed:bg-red rac-disabled:bg-red rac-drop-target:bg-red rac-dragging:bg-red rac-empty:bg-red rac-allows-removing:bg-red rac-allows-sorting:bg-red rac-placeholder:bg-red rac-selected:bg-red rac-indeterminate:bg-red rac-readonly:bg-red rac-required:bg-red rac-entering:bg-red rac-exiting:bg-red rac-open:bg-red rac-unavailable:bg-red rac-current:bg-red rac-invalid:bg-red rac-placement-left:bg-red rac-placement-right:bg-red rac-placement-top:bg-red rac-placement-bottom:bg-red rac-type-literal:bg-red rac-type-year:bg-red rac-type-month:bg-red rac-type-day:bg-red rac-layout-grid:bg-red rac-layout-stack:bg-red rac-orientation-horizontal:bg-red rac-orientation-vertical:bg-red"></div>`;
230+
let content = html`<div data-rac className="rac-hover:bg-red rac-focus:bg-red rac-focus-visible:bg-red rac-focus-within:bg-red rac-pressed:bg-red rac-disabled:bg-red rac-drop-target:bg-red rac-dragging:bg-red rac-empty:bg-red rac-allows-dragging:bg-red rac-allows-removing:bg-red rac-allows-sorting:bg-red rac-placeholder-shown:bg-red rac-selected:bg-red rac-indeterminate:bg-red rac-read-only:bg-red rac-required:bg-red rac-entering:bg-red rac-exiting:bg-red rac-open:bg-red rac-unavailable:bg-red rac-outside-month:bg-red rac-outside-visible-range:bg-red rac-selection-start:bg-red rac-selection-end:bg-red rac-current:bg-red rac-invalid:bg-red rac-resizing:bg-red rac-placement-left:bg-red rac-placement-right:bg-red rac-placement-top:bg-red rac-placement-bottom:bg-red rac-type-literal:bg-red rac-type-year:bg-red rac-type-month:bg-red rac-type-day:bg-red rac-layout-grid:bg-red rac-layout-stack:bg-red rac-orientation-horizontal:bg-red rac-orientation-vertical:bg-red rac-selection-single:bg-red rac-selection-multiple:bg-red rac-resizable-right:bg-red rac-resizable-left:bg-red rac-resizable-both:bg-red rac-sort-ascending:bg-red rac-sort-descending:bg-red group-rac-pressed:bg-red peer-rac-pressed:bg-red"></div>`;
167231
return run({content, options: {prefix: 'rac'}}).then((result) => {
168232
expect(result.css).toContain(css`
169233
.rac-hover\:bg-red[data-hovered] {
@@ -178,10 +242,22 @@ test('variants with prefix', async () => {
178242
--tw-bg-opacity: 1;
179243
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
180244
}
245+
.rac-focus-within\:bg-red[data-focus-within] {
246+
--tw-bg-opacity: 1;
247+
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
248+
}
181249
.rac-pressed\:bg-red[data-pressed] {
182250
--tw-bg-opacity: 1;
183251
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
184252
}
253+
.group[data-pressed] .group-rac-pressed\:bg-red {
254+
--tw-bg-opacity: 1;
255+
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
256+
}
257+
.peer[data-pressed] ~ .peer-rac-pressed\:bg-red {
258+
--tw-bg-opacity: 1;
259+
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
260+
}
185261
.rac-disabled\:bg-red[data-disabled] {
186262
--tw-bg-opacity: 1;
187263
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
@@ -198,6 +274,10 @@ test('variants with prefix', async () => {
198274
--tw-bg-opacity: 1;
199275
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
200276
}
277+
.rac-allows-dragging\:bg-red[data-allows-dragging] {
278+
--tw-bg-opacity: 1;
279+
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
280+
}
201281
.rac-allows-removing\:bg-red[data-allows-removing] {
202282
--tw-bg-opacity: 1;
203283
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
@@ -206,7 +286,7 @@ test('variants with prefix', async () => {
206286
--tw-bg-opacity: 1;
207287
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
208288
}
209-
.rac-placeholder\:bg-red[data-placeholder] {
289+
.rac-placeholder-shown\:bg-red[data-placeholder] {
210290
--tw-bg-opacity: 1;
211291
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
212292
}
@@ -218,7 +298,7 @@ test('variants with prefix', async () => {
218298
--tw-bg-opacity: 1;
219299
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
220300
}
221-
.rac-readonly\:bg-red[data-readonly] {
301+
.rac-read-only\:bg-red[data-readonly] {
222302
--tw-bg-opacity: 1;
223303
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
224304
}
@@ -242,6 +322,22 @@ test('variants with prefix', async () => {
242322
--tw-bg-opacity: 1;
243323
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
244324
}
325+
.rac-outside-month\:bg-red[data-outside-month] {
326+
--tw-bg-opacity: 1;
327+
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
328+
}
329+
.rac-outside-visible-range\:bg-red[data-outside-visible-range] {
330+
--tw-bg-opacity: 1;
331+
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
332+
}
333+
.rac-selection-start\:bg-red[data-selection-start] {
334+
--tw-bg-opacity: 1;
335+
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
336+
}
337+
.rac-selection-end\:bg-red[data-selection-end] {
338+
--tw-bg-opacity: 1;
339+
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
340+
}
245341
.rac-current\:bg-red[data-current] {
246342
--tw-bg-opacity: 1;
247343
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
@@ -250,6 +346,10 @@ test('variants with prefix', async () => {
250346
--tw-bg-opacity: 1;
251347
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
252348
}
349+
.rac-resizing\:bg-red[data-resizing] {
350+
--tw-bg-opacity: 1;
351+
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
352+
}
253353
.rac-placement-left\:bg-red[data-placement="left"] {
254354
--tw-bg-opacity: 1;
255355
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
@@ -297,6 +397,34 @@ test('variants with prefix', async () => {
297397
.rac-orientation-vertical\:bg-red[data-orientation="vertical"] {
298398
--tw-bg-opacity: 1;
299399
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
400+
}
401+
.rac-selection-single\:bg-red[data-selection-mode="single"] {
402+
--tw-bg-opacity: 1;
403+
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
404+
}
405+
.rac-selection-multiple\:bg-red[data-selection-mode="multiple"] {
406+
--tw-bg-opacity: 1;
407+
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
408+
}
409+
.rac-resizable-right\:bg-red[data-resizable-direction="right"] {
410+
--tw-bg-opacity: 1;
411+
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
412+
}
413+
.rac-resizable-left\:bg-red[data-resizable-direction="left"] {
414+
--tw-bg-opacity: 1;
415+
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
416+
}
417+
.rac-resizable-both\:bg-red[data-resizable-direction="both"] {
418+
--tw-bg-opacity: 1;
419+
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
420+
}
421+
.rac-sort-ascending\:bg-red[data-sort-direction="ascending"] {
422+
--tw-bg-opacity: 1;
423+
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
424+
}
425+
.rac-sort-descending\:bg-red[data-sort-direction="descending"] {
426+
--tw-bg-opacity: 1;
427+
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
300428
}`
301429
);
302430
});

0 commit comments

Comments
 (0)