11import type { Variant , VariantContext , VariantObject } from '@unocss/core'
2+ import type { PresetMiniOptions } from '..'
23import type { Theme } from '../theme'
4+ import { escapeRegExp , escapeSelector } from '@unocss/core'
35import { h , variantGetParameter } from '../utils'
46
57export const variantDataAttribute : VariantObject = {
@@ -20,28 +22,91 @@ export const variantDataAttribute: VariantObject = {
2022 multiPass : true ,
2123}
2224
23- function taggedData ( tagName : string ) : Variant {
25+ function taggedData ( tagName : string , combinator : string , options : PresetMiniOptions = { } ) : Variant {
2426 return {
2527 name : `${ tagName } -data` ,
2628 match ( matcher , ctx : VariantContext < Theme > ) {
2729 const variant = variantGetParameter ( `${ tagName } -data-` , matcher , ctx . generator . config . separators )
2830 if ( variant ) {
2931 const [ match , rest , label ] = variant
3032 const dataAttribute = h . bracket ( match ) ?? ctx . theme . data ?. [ match ] ?? ''
33+ if ( dataAttribute ) {
34+ const attributify = ! ! options ?. attributifyPseudo
35+ let firstPrefix = options ?. prefix ?? ''
36+ firstPrefix = ( Array . isArray ( firstPrefix ) ? firstPrefix : [ firstPrefix ] ) . filter ( Boolean ) [ 0 ] ?? ''
37+
38+ const parent = `${ attributify ? `[${ firstPrefix } ${ tagName } =""]` : `.${ firstPrefix } ${ tagName } ` } `
39+ const escapedLabel = escapeSelector ( label ? `/${ label } ` : '' )
40+
41+ return {
42+ matcher : rest ,
43+ handle : ( input , next ) => {
44+ const regexp = new RegExp ( `${ escapeRegExp ( parent ) } ${ escapeRegExp ( escapedLabel ) } (?:\\[.+?\\])+` )
45+ const match = input . prefix . match ( regexp )
46+
47+ let nextPrefix
48+ if ( match ) {
49+ const insertIndex = ( match . index ?? 0 ) + parent . length + escapedLabel . length
50+ nextPrefix = [
51+ input . prefix . slice ( 0 , insertIndex ) ,
52+ `[data-${ dataAttribute } ]` ,
53+ input . prefix . slice ( insertIndex ) ,
54+ ] . join ( '' )
55+ }
56+ else {
57+ const prefixGroupIndex = Math . max ( input . prefix . indexOf ( parent ) , 0 )
58+ nextPrefix = [
59+ input . prefix . slice ( 0 , prefixGroupIndex ) ,
60+ parent ,
61+ escapedLabel ,
62+ `[data-${ dataAttribute } ]` ,
63+ combinator ,
64+ input . prefix . slice ( prefixGroupIndex ) ,
65+ ] . join ( '' )
66+ }
67+
68+ return next ( {
69+ ...input ,
70+ prefix : nextPrefix ,
71+ } )
72+ } ,
73+ }
74+ }
75+ }
76+ } ,
77+ multiPass : true ,
78+ }
79+ }
80+
81+ function taggedHasData ( ) : Variant {
82+ return {
83+ name : 'has-data' ,
84+ match ( matcher , ctx : VariantContext < Theme > ) {
85+ const variant = variantGetParameter ( 'has-data-' , matcher , ctx . generator . config . separators )
86+ if ( variant ) {
87+ const [ match , rest ] = variant
88+ const dataAttribute = h . bracket ( match ) ?? ctx . theme . data ?. [ match ] ?? ''
3189 if ( dataAttribute ) {
3290 return {
33- matcher : `${ tagName } -[[data-${ dataAttribute } ]]${ label ? `/${ label } ` : '' } :${ rest } ` ,
91+ matcher : rest ,
92+ handle : ( input , next ) => next ( {
93+ ...input ,
94+ pseudo : `${ input . pseudo } :has([data-${ dataAttribute } ])` ,
95+ } ) ,
3496 }
3597 }
3698 }
3799 } ,
100+ multiPass : true ,
38101 }
39102}
40103
41- export const variantTaggedDataAttributes : Variant [ ] = [
42- taggedData ( 'group' ) ,
43- taggedData ( 'peer' ) ,
44- taggedData ( 'parent' ) ,
45- taggedData ( 'previous' ) ,
46- taggedData ( 'has' ) ,
47- ]
104+ export function variantTaggedDataAttributes ( options : PresetMiniOptions = { } ) : Variant [ ] {
105+ return [
106+ taggedData ( 'group' , ' ' , options ) ,
107+ taggedData ( 'peer' , '~' , options ) ,
108+ taggedData ( 'parent' , '>' , options ) ,
109+ taggedData ( 'previous' , '+' , options ) ,
110+ taggedHasData ( ) ,
111+ ]
112+ }
0 commit comments