@wix/zero-config-implementation 1.72.0 → 1.74.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{index-CSi4SZpQ.js → index-DNi5gVDn.js} +1 -1
- package/dist/{index-CdHu4-o-.js → index-FEhWzHzO.js} +11997 -11772
- package/dist/index.d.ts +8 -0
- package/dist/index.js +1 -1
- package/package.json +2 -2
- package/src/converters/to-editor-component.test.ts +88 -0
- package/src/converters/to-editor-component.ts +11 -0
- package/src/information-extractors/css/parse.test.ts +49 -1
- package/src/information-extractors/css/parse.ts +167 -3
- package/src/information-extractors/css/selector-matcher.ts +3 -1
- package/src/information-extractors/css/types.ts +11 -0
- package/src/information-extractors/react/extractors/css-properties.test.ts +328 -2
- package/src/information-extractors/react/extractors/css-properties.ts +266 -98
- package/src/information-extractors/react/extractors/index.ts +1 -0
- package/src/information-extractors/react/index.ts +1 -0
- package/src/manifest-pipeline.ts +3 -1
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
* CSS Properties Extractor
|
|
3
3
|
*
|
|
4
4
|
* Determines which CSS properties are relevant to each element
|
|
5
|
-
* based on
|
|
5
|
+
* based on semantic traits and effective display behavior.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { CSS_PROPERTIES } from '@wix/react-component-schema'
|
|
9
|
+
import { resolveCssPropertyValue } from '../../css/parse'
|
|
9
10
|
import type { MatchedCssData } from '../../css/types'
|
|
10
11
|
import type { ExtractedElement } from './core/tree-builder'
|
|
11
12
|
import type { CreateElementEvent, ReactExtractor } from './core/types'
|
|
@@ -19,14 +20,9 @@ export interface CssPropertiesData {
|
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
22
|
-
//
|
|
23
|
+
// Semantic Traits
|
|
23
24
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
24
25
|
|
|
25
|
-
/**
|
|
26
|
-
* Layout Containers - structural boxes focused on spacing and positioning
|
|
27
|
-
*/
|
|
28
|
-
const LAYOUT_CONTAINER_TAGS = new Set(['div', 'section', 'article', 'header', 'footer', 'main', 'nav', 'aside'])
|
|
29
|
-
|
|
30
26
|
/**
|
|
31
27
|
* Text & Typography - elements where readability and hierarchy are primary
|
|
32
28
|
*/
|
|
@@ -48,26 +44,24 @@ const TEXT_TAGS = new Set([
|
|
|
48
44
|
'i',
|
|
49
45
|
])
|
|
50
46
|
|
|
47
|
+
const DEFAULT_NON_BOX_TEXT_TAGS = new Set(['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'])
|
|
48
|
+
|
|
51
49
|
/**
|
|
52
50
|
* Media & Visuals - elements requiring aspect ratio control (excluding svg)
|
|
53
51
|
*/
|
|
54
52
|
const MEDIA_TAGS = new Set(['img', 'video', 'canvas', 'picture'])
|
|
55
53
|
|
|
56
|
-
/**
|
|
57
|
-
* Form Inputs & Interactive Elements - require state-based styling
|
|
58
|
-
*/
|
|
59
|
-
const INTERACTIVE_TAGS = new Set(['button', 'input', 'textarea', 'select'])
|
|
60
|
-
|
|
61
54
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
62
55
|
// CSS Property Sets (using logical properties)
|
|
63
56
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
64
57
|
|
|
65
58
|
/**
|
|
66
|
-
* CSS properties for
|
|
59
|
+
* CSS properties for box-generating elements. These are visual box styles,
|
|
60
|
+
* not child-layout controls such as `gap`.
|
|
67
61
|
*/
|
|
68
62
|
const { CSS_PROPERTY_TYPE } = CSS_PROPERTIES
|
|
69
63
|
|
|
70
|
-
const
|
|
64
|
+
const BOX_CSS_PROPERTIES = [
|
|
71
65
|
CSS_PROPERTY_TYPE.background,
|
|
72
66
|
CSS_PROPERTY_TYPE.borderTop,
|
|
73
67
|
CSS_PROPERTY_TYPE.borderBottom,
|
|
@@ -113,81 +107,52 @@ const MEDIA_CSS_PROPERTIES = [
|
|
|
113
107
|
CSS_PROPERTY_TYPE.boxShadow,
|
|
114
108
|
]
|
|
115
109
|
|
|
116
|
-
|
|
117
|
-
// Category Detection
|
|
118
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
119
|
-
|
|
120
|
-
type ElementCategory = 'layout' | 'text' | 'media' | 'interactive' | 'unknown'
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Determines the category of an element based on its tag and role
|
|
124
|
-
*/
|
|
125
|
-
function getElementCategory(tag: string, role?: string): ElementCategory {
|
|
110
|
+
function hasTextSemantics(tag: string, role?: string): boolean {
|
|
126
111
|
const normalizedTag = tag.toLowerCase()
|
|
127
|
-
|
|
128
|
-
// Check role-based overrides first
|
|
129
112
|
if (role) {
|
|
130
113
|
const normalizedRole = role.toLowerCase()
|
|
131
|
-
if (normalizedRole === 'button' || normalizedRole === 'textbox') {
|
|
132
|
-
return 'interactive'
|
|
133
|
-
}
|
|
134
114
|
if (normalizedRole === 'heading' || normalizedRole === 'paragraph') {
|
|
135
|
-
return
|
|
136
|
-
}
|
|
137
|
-
if (normalizedRole === 'img' || normalizedRole === 'figure') {
|
|
138
|
-
return 'media'
|
|
115
|
+
return true
|
|
139
116
|
}
|
|
140
117
|
}
|
|
118
|
+
return TEXT_TAGS.has(normalizedTag)
|
|
119
|
+
}
|
|
141
120
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
if (MEDIA_TAGS.has(normalizedTag)) {
|
|
150
|
-
return 'media'
|
|
151
|
-
}
|
|
152
|
-
if (LAYOUT_CONTAINER_TAGS.has(normalizedTag)) {
|
|
153
|
-
return 'layout'
|
|
121
|
+
function hasMediaSemantics(tag: string, role?: string): boolean {
|
|
122
|
+
const normalizedTag = tag.toLowerCase()
|
|
123
|
+
if (role) {
|
|
124
|
+
const normalizedRole = role.toLowerCase()
|
|
125
|
+
if (normalizedRole === 'img' || normalizedRole === 'figure') {
|
|
126
|
+
return true
|
|
127
|
+
}
|
|
154
128
|
}
|
|
155
|
-
|
|
156
|
-
return 'unknown'
|
|
129
|
+
return MEDIA_TAGS.has(normalizedTag)
|
|
157
130
|
}
|
|
158
131
|
|
|
159
132
|
/**
|
|
160
|
-
* Gets CSS properties relevant to an element
|
|
133
|
+
* Gets CSS properties relevant to an element from composable traits:
|
|
134
|
+
* text semantics, media semantics, and box-generating display.
|
|
161
135
|
* Note: hasTextContent determination is deferred to tree building since
|
|
162
136
|
* we can't know during createElement if a child will have text content.
|
|
163
137
|
*/
|
|
164
138
|
export function getCssPropertiesForTag(tag: string, role?: string): string[] {
|
|
165
|
-
const category = getElementCategory(tag, role)
|
|
166
139
|
const properties: string[] = []
|
|
140
|
+
const hasTextProperties = hasTextSemantics(tag, role)
|
|
141
|
+
const hasMediaProperties = hasMediaSemantics(tag, role)
|
|
142
|
+
const hasBoxProperties = hasDefaultBoxProperties(tag, role) && !hasMediaProperties
|
|
167
143
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
break
|
|
177
|
-
|
|
178
|
-
case 'media':
|
|
179
|
-
properties.push(...MEDIA_CSS_PROPERTIES)
|
|
180
|
-
break
|
|
181
|
-
|
|
182
|
-
case 'unknown':
|
|
183
|
-
// For unknown elements, default to container properties
|
|
184
|
-
// (can be refined during tree building based on children)
|
|
185
|
-
properties.push(...CONTAINER_CSS_PROPERTIES)
|
|
186
|
-
break
|
|
144
|
+
if (hasTextProperties) {
|
|
145
|
+
properties.push(...TEXT_CSS_PROPERTIES)
|
|
146
|
+
}
|
|
147
|
+
if (hasMediaProperties) {
|
|
148
|
+
properties.push(...MEDIA_CSS_PROPERTIES)
|
|
149
|
+
}
|
|
150
|
+
if (hasBoxProperties) {
|
|
151
|
+
properties.push(...BOX_CSS_PROPERTIES)
|
|
187
152
|
}
|
|
188
153
|
|
|
189
154
|
properties.push(CSS_PROPERTY_TYPE.display)
|
|
190
|
-
return properties
|
|
155
|
+
return [...new Set(properties)]
|
|
191
156
|
}
|
|
192
157
|
|
|
193
158
|
/**
|
|
@@ -204,6 +169,19 @@ export function addTextProperties(existing: string[]): string[] {
|
|
|
204
169
|
return result
|
|
205
170
|
}
|
|
206
171
|
|
|
172
|
+
/**
|
|
173
|
+
* Adds box CSS properties to an existing property list without duplicates.
|
|
174
|
+
*/
|
|
175
|
+
export function addBoxProperties(existing: string[]): string[] {
|
|
176
|
+
const result = [...existing]
|
|
177
|
+
for (const property of BOX_CSS_PROPERTIES) {
|
|
178
|
+
if (!result.includes(property)) {
|
|
179
|
+
result.push(property)
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return result
|
|
183
|
+
}
|
|
184
|
+
|
|
207
185
|
/**
|
|
208
186
|
* Adds gap CSS property to an existing property list if not already present.
|
|
209
187
|
*/
|
|
@@ -216,21 +194,91 @@ export function addGapProperty(existing: string[]): string[] {
|
|
|
216
194
|
// Display Value Resolution
|
|
217
195
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
218
196
|
|
|
197
|
+
// Based on WebKit and Blink UA stylesheets. The map is intentionally limited
|
|
198
|
+
// to element defaults rather than pseudo-elements or internal control parts.
|
|
219
199
|
const TAG_DEFAULT_DISPLAY: Record<string, string> = {
|
|
220
|
-
span: 'inline',
|
|
221
200
|
a: 'inline',
|
|
222
|
-
|
|
223
|
-
|
|
201
|
+
address: 'block',
|
|
202
|
+
area: 'none',
|
|
203
|
+
article: 'block',
|
|
204
|
+
aside: 'block',
|
|
205
|
+
audio: 'inline',
|
|
224
206
|
b: 'inline',
|
|
207
|
+
base: 'none',
|
|
208
|
+
basefont: 'none',
|
|
209
|
+
blockquote: 'block',
|
|
210
|
+
body: 'block',
|
|
211
|
+
button: 'inline-block',
|
|
212
|
+
canvas: 'inline',
|
|
213
|
+
caption: 'table-caption',
|
|
214
|
+
center: 'block',
|
|
215
|
+
col: 'table-column',
|
|
216
|
+
colgroup: 'table-column-group',
|
|
217
|
+
datalist: 'none',
|
|
218
|
+
dd: 'block',
|
|
219
|
+
dir: 'block',
|
|
220
|
+
div: 'block',
|
|
221
|
+
dl: 'block',
|
|
222
|
+
dt: 'block',
|
|
223
|
+
em: 'inline',
|
|
224
|
+
embed: 'inline',
|
|
225
|
+
fieldset: 'block',
|
|
226
|
+
figcaption: 'block',
|
|
227
|
+
figure: 'block',
|
|
228
|
+
footer: 'block',
|
|
229
|
+
form: 'block',
|
|
230
|
+
h1: 'block',
|
|
231
|
+
h2: 'block',
|
|
232
|
+
h3: 'block',
|
|
233
|
+
h4: 'block',
|
|
234
|
+
h5: 'block',
|
|
235
|
+
h6: 'block',
|
|
236
|
+
head: 'none',
|
|
237
|
+
header: 'block',
|
|
238
|
+
hgroup: 'block',
|
|
239
|
+
hr: 'block',
|
|
240
|
+
html: 'block',
|
|
225
241
|
i: 'inline',
|
|
226
|
-
|
|
242
|
+
iframe: 'inline',
|
|
227
243
|
img: 'inline',
|
|
228
|
-
input: 'inline',
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
button: 'inline-block',
|
|
244
|
+
input: 'inline-block',
|
|
245
|
+
label: 'inline',
|
|
246
|
+
legend: 'block',
|
|
232
247
|
li: 'list-item',
|
|
248
|
+
link: 'none',
|
|
249
|
+
main: 'block',
|
|
250
|
+
map: 'inline',
|
|
251
|
+
marquee: 'inline-block',
|
|
252
|
+
menu: 'block',
|
|
253
|
+
meta: 'none',
|
|
254
|
+
nav: 'block',
|
|
255
|
+
noembed: 'none',
|
|
256
|
+
noframes: 'none',
|
|
257
|
+
object: 'inline',
|
|
258
|
+
ol: 'block',
|
|
259
|
+
p: 'block',
|
|
260
|
+
param: 'none',
|
|
261
|
+
q: 'inline',
|
|
262
|
+
rp: 'none',
|
|
263
|
+
script: 'none',
|
|
264
|
+
search: 'block',
|
|
265
|
+
section: 'block',
|
|
266
|
+
select: 'inline-block',
|
|
267
|
+
span: 'inline',
|
|
268
|
+
strong: 'inline',
|
|
269
|
+
style: 'none',
|
|
233
270
|
table: 'table',
|
|
271
|
+
tbody: 'table-row-group',
|
|
272
|
+
td: 'table-cell',
|
|
273
|
+
template: 'none',
|
|
274
|
+
textarea: 'inline-block',
|
|
275
|
+
tfoot: 'table-footer-group',
|
|
276
|
+
th: 'table-cell',
|
|
277
|
+
thead: 'table-header-group',
|
|
278
|
+
title: 'none',
|
|
279
|
+
tr: 'table-row',
|
|
280
|
+
ul: 'block',
|
|
281
|
+
video: 'inline',
|
|
234
282
|
}
|
|
235
283
|
|
|
236
284
|
export function getDefaultDisplayForTag(tag: string): string {
|
|
@@ -238,27 +286,44 @@ export function getDefaultDisplayForTag(tag: string): string {
|
|
|
238
286
|
}
|
|
239
287
|
|
|
240
288
|
/**
|
|
241
|
-
* Resolves the current `display` value from matched CSS data
|
|
242
|
-
*
|
|
243
|
-
* Returns the resolved value, or `undefined` if `display` is not declared
|
|
244
|
-
* or uses a variable that cannot be resolved.
|
|
289
|
+
* Resolves the current `display` value from matched CSS data using the
|
|
290
|
+
* CSS-layer value resolver, including var() fallback handling.
|
|
245
291
|
*/
|
|
246
|
-
|
|
292
|
+
function getDisplayPropertyMatch(matcherData: MatchedCssData) {
|
|
293
|
+
let matchedDisplayProperty: (typeof matcherData.matches)[number]['properties'][number] | undefined
|
|
294
|
+
let matchedDisplaySpecificity: [number, number, number] | undefined
|
|
295
|
+
|
|
247
296
|
for (const match of matcherData.matches) {
|
|
248
297
|
for (const property of match.properties) {
|
|
249
|
-
if (property.name
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
298
|
+
if (property.name === 'display') {
|
|
299
|
+
if (!matchedDisplaySpecificity || compareSpecificity(match.specificity, matchedDisplaySpecificity) >= 0) {
|
|
300
|
+
matchedDisplayProperty = property
|
|
301
|
+
matchedDisplaySpecificity = match.specificity
|
|
302
|
+
}
|
|
253
303
|
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
return matchedDisplayProperty
|
|
307
|
+
}
|
|
254
308
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
309
|
+
function compareSpecificity(
|
|
310
|
+
leftSpecificity: [number, number, number],
|
|
311
|
+
rightSpecificity: [number, number, number],
|
|
312
|
+
): number {
|
|
313
|
+
for (let specificityIndex = 0; specificityIndex < leftSpecificity.length; specificityIndex += 1) {
|
|
314
|
+
const difference = leftSpecificity[specificityIndex] - rightSpecificity[specificityIndex]
|
|
315
|
+
if (difference !== 0) {
|
|
316
|
+
return difference
|
|
259
317
|
}
|
|
260
318
|
}
|
|
261
|
-
|
|
319
|
+
|
|
320
|
+
return 0
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
export function resolveDisplayValue(matcherData: MatchedCssData): string | undefined {
|
|
324
|
+
const displayProperty = getDisplayPropertyMatch(matcherData)
|
|
325
|
+
if (!displayProperty) return undefined
|
|
326
|
+
return resolveCssPropertyValue(displayProperty, matcherData.customProperties)
|
|
262
327
|
}
|
|
263
328
|
|
|
264
329
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -266,20 +331,62 @@ export function resolveDisplayValue(matcherData: MatchedCssData): string | undef
|
|
|
266
331
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
267
332
|
|
|
268
333
|
const FLEX_GRID_DISPLAY_VALUES = new Set(['flex', 'grid', 'inline-flex', 'inline-grid'])
|
|
334
|
+
const NON_CONTAINER_DISPLAY_VALUES = new Set(['inline', 'contents', 'none'])
|
|
335
|
+
const BOX_GENERATING_DISPLAY_VALUES = new Set([
|
|
336
|
+
'block',
|
|
337
|
+
'flex',
|
|
338
|
+
'grid',
|
|
339
|
+
'flow-root',
|
|
340
|
+
'inline-block',
|
|
341
|
+
'inline-flex',
|
|
342
|
+
'inline-grid',
|
|
343
|
+
'inline-table',
|
|
344
|
+
'table',
|
|
345
|
+
'list-item',
|
|
346
|
+
'table-caption',
|
|
347
|
+
'table-cell',
|
|
348
|
+
'table-column',
|
|
349
|
+
'table-column-group',
|
|
350
|
+
'table-footer-group',
|
|
351
|
+
'table-header-group',
|
|
352
|
+
'table-row',
|
|
353
|
+
'table-row-group',
|
|
354
|
+
])
|
|
355
|
+
|
|
356
|
+
function normalizeDisplayValue(displayValue: string): string {
|
|
357
|
+
return displayValue.trim().toLowerCase().replace(/\s+/g, ' ')
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function displayCreatesContainerBox(displayValue: string): boolean {
|
|
361
|
+
const normalizedDisplayValue = normalizeDisplayValue(displayValue)
|
|
362
|
+
if (NON_CONTAINER_DISPLAY_VALUES.has(normalizedDisplayValue)) return false
|
|
363
|
+
|
|
364
|
+
const displayTokens = normalizedDisplayValue.split(' ')
|
|
365
|
+
|
|
366
|
+
return displayTokens.some((displayToken) => BOX_GENERATING_DISPLAY_VALUES.has(displayToken))
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function displayUsesFlexOrGridLayout(displayValue: string): boolean {
|
|
370
|
+
const normalizedDisplayValue = normalizeDisplayValue(displayValue)
|
|
371
|
+
const displayTokens = normalizedDisplayValue.split(' ')
|
|
372
|
+
|
|
373
|
+
return displayTokens.some((displayToken) => FLEX_GRID_DISPLAY_VALUES.has(displayToken))
|
|
374
|
+
}
|
|
269
375
|
|
|
270
376
|
/**
|
|
271
377
|
* Returns true if display uses a CSS variable that cannot be resolved --
|
|
272
378
|
* we optimistically assume it could be flex/grid.
|
|
273
379
|
*/
|
|
274
380
|
function hasUnresolvableDisplayVar(matcherData: MatchedCssData): boolean {
|
|
381
|
+
const displayProperty = getDisplayPropertyMatch(matcherData)
|
|
382
|
+
if (!displayProperty?.varRefs || displayProperty.varRefs.length === 0) return false
|
|
383
|
+
return resolveCssPropertyValue(displayProperty, matcherData.customProperties) === undefined
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function hasDisplayDeclaration(matcherData: MatchedCssData): boolean {
|
|
275
387
|
for (const match of matcherData.matches) {
|
|
276
388
|
for (const property of match.properties) {
|
|
277
|
-
if (property.name
|
|
278
|
-
if (property.varRefs && property.varRefs.length > 0) {
|
|
279
|
-
for (const varName of property.varRefs) {
|
|
280
|
-
if (matcherData.customProperties[varName] === undefined) return true
|
|
281
|
-
}
|
|
282
|
-
}
|
|
389
|
+
if (property.name === 'display') return true
|
|
283
390
|
}
|
|
284
391
|
}
|
|
285
392
|
return false
|
|
@@ -287,10 +394,46 @@ function hasUnresolvableDisplayVar(matcherData: MatchedCssData): boolean {
|
|
|
287
394
|
|
|
288
395
|
export function hasFlexOrGridDisplay(matcherData: MatchedCssData): boolean {
|
|
289
396
|
const displayValue = resolveDisplayValue(matcherData)
|
|
290
|
-
if (displayValue !== undefined) return
|
|
397
|
+
if (displayValue !== undefined) return displayUsesFlexOrGridLayout(displayValue)
|
|
291
398
|
return hasUnresolvableDisplayVar(matcherData)
|
|
292
399
|
}
|
|
293
400
|
|
|
401
|
+
export function hasContainerLikeDisplay(matcherData: MatchedCssData): boolean {
|
|
402
|
+
const displayValue = resolveDisplayValue(matcherData)
|
|
403
|
+
if (displayValue !== undefined) return displayCreatesContainerBox(displayValue)
|
|
404
|
+
return hasUnresolvableDisplayVar(matcherData)
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
function hasContainerLikeDefaultDisplay(tag: string): boolean {
|
|
408
|
+
return displayCreatesContainerBox(getDefaultDisplayForTag(tag))
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
function suppressesDefaultBoxProperties(tag: string, role?: string): boolean {
|
|
412
|
+
const normalizedTag = tag.toLowerCase()
|
|
413
|
+
if (DEFAULT_NON_BOX_TEXT_TAGS.has(normalizedTag)) {
|
|
414
|
+
return true
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
const normalizedRole = role?.toLowerCase()
|
|
418
|
+
return normalizedRole === 'heading' || normalizedRole === 'paragraph'
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
function hasDefaultBoxProperties(tag: string, role?: string): boolean {
|
|
422
|
+
if (suppressesDefaultBoxProperties(tag, role)) {
|
|
423
|
+
return false
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
return hasContainerLikeDefaultDisplay(tag)
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function hasEffectiveContainerDisplay(tag: string, role?: string, matcherData?: MatchedCssData): boolean {
|
|
430
|
+
if (matcherData && hasDisplayDeclaration(matcherData)) {
|
|
431
|
+
return hasContainerLikeDisplay(matcherData)
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
return hasDefaultBoxProperties(tag, role)
|
|
435
|
+
}
|
|
436
|
+
|
|
294
437
|
/**
|
|
295
438
|
* Walks the element tree and adds `gap` to relevant CSS properties
|
|
296
439
|
* for elements that have display: flex|grid and more than 1 child.
|
|
@@ -316,6 +459,31 @@ export function enrichGapProperties(elements: ExtractedElement[]): ExtractedElem
|
|
|
316
459
|
})
|
|
317
460
|
}
|
|
318
461
|
|
|
462
|
+
/**
|
|
463
|
+
* Walks the element tree and adds box properties to selected text tags
|
|
464
|
+
* when CSS overrides `display` or default UA display creates a box.
|
|
465
|
+
*/
|
|
466
|
+
export function enrichContainerProperties(elements: ExtractedElement[]): ExtractedElement[] {
|
|
467
|
+
return elements.map((element) => {
|
|
468
|
+
const role = element.attributes.role
|
|
469
|
+
if (hasTextSemantics(element.tag, role)) {
|
|
470
|
+
const matcherData = element.extractorData.get('css-matcher') as MatchedCssData | undefined
|
|
471
|
+
const cssData = element.extractorData.get('css-properties') as CssPropertiesData | undefined
|
|
472
|
+
|
|
473
|
+
if (cssData && hasEffectiveContainerDisplay(element.tag, role, matcherData)) {
|
|
474
|
+
element.extractorData.set('css-properties', {
|
|
475
|
+
relevant: addBoxProperties(cssData.relevant),
|
|
476
|
+
})
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
return {
|
|
481
|
+
...element,
|
|
482
|
+
children: enrichContainerProperties(element.children),
|
|
483
|
+
}
|
|
484
|
+
})
|
|
485
|
+
}
|
|
486
|
+
|
|
319
487
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
320
488
|
// Factory
|
|
321
489
|
// ─────────────────────────────────────────────────────────────────────────────
|
package/src/manifest-pipeline.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
type RunExtractorsOptions,
|
|
10
10
|
createCssPropertiesExtractor,
|
|
11
11
|
createPropTrackerExtractor,
|
|
12
|
+
enrichContainerProperties,
|
|
12
13
|
enrichGapProperties,
|
|
13
14
|
runExtractors,
|
|
14
15
|
} from './information-extractors/react'
|
|
@@ -163,7 +164,8 @@ export function processComponent(
|
|
|
163
164
|
if (html && extractedElements.length > 0 && css.length > 0) {
|
|
164
165
|
try {
|
|
165
166
|
const matchResult = matchCssSelectors(html, extractedElements, css)
|
|
166
|
-
const
|
|
167
|
+
const containerEnrichedElements = enrichContainerProperties(matchResult.elements)
|
|
168
|
+
const gapEnrichedElements = enrichGapProperties(containerEnrichedElements)
|
|
167
169
|
enhancedInfo = {
|
|
168
170
|
...enhancedInfo,
|
|
169
171
|
elements: convertElements(gapEnrichedElements),
|