@promptui-lib/figma-parser 0.1.19 → 0.1.20
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/parser/index.d.ts +2 -2
- package/dist/parser/index.d.ts.map +1 -1
- package/dist/parser/index.js +2 -2
- package/dist/parser/layout-parser.d.ts +8 -2
- package/dist/parser/layout-parser.d.ts.map +1 -1
- package/dist/parser/layout-parser.js +56 -9
- package/dist/parser/node-parser.d.ts.map +1 -1
- package/dist/parser/node-parser.js +65 -3
- package/dist/parser/position-parser.d.ts +5 -0
- package/dist/parser/position-parser.d.ts.map +1 -1
- package/dist/parser/position-parser.js +26 -6
- package/package.json +2 -2
package/dist/parser/index.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
export { parseLayout, parseSizing, parseLayoutAndSizing } from './layout-parser.js';
|
|
1
|
+
export { parseLayout, parseSizing, parseLayoutAndSizing, parsePadding } from './layout-parser.js';
|
|
2
2
|
export type { ILayoutProperties } from './layout-parser.js';
|
|
3
3
|
export { parseStyles, parseBackgroundColor, parseBorder, parseBorderRadius, parseBoxShadow } from './style-parser.js';
|
|
4
4
|
export type { IStyleProperties } from './style-parser.js';
|
|
5
5
|
export { parseTextStyles, extractTextContent } from './text-parser.js';
|
|
6
6
|
export type { ITextProperties } from './text-parser.js';
|
|
7
|
-
export { parsePosition, parseContainerPosition, hasAutoLayout } from './position-parser.js';
|
|
7
|
+
export { parsePosition, parseContainerPosition, hasAutoLayout, isAbsolutelyPositioned } from './position-parser.js';
|
|
8
8
|
export type { IPositionProperties } from './position-parser.js';
|
|
9
9
|
export { parseNode, parseNodes, parseFrameName } from './node-parser.js';
|
|
10
10
|
export type { IParseOptions } from './node-parser.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/parser/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/parser/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClG,YAAY,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE5D,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,WAAW,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACtH,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACvE,YAAY,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExD,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AACpH,YAAY,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAEhE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACzE,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC"}
|
package/dist/parser/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export { parseLayout, parseSizing, parseLayoutAndSizing } from './layout-parser.js';
|
|
1
|
+
export { parseLayout, parseSizing, parseLayoutAndSizing, parsePadding } from './layout-parser.js';
|
|
2
2
|
export { parseStyles, parseBackgroundColor, parseBorder, parseBorderRadius, parseBoxShadow } from './style-parser.js';
|
|
3
3
|
export { parseTextStyles, extractTextContent } from './text-parser.js';
|
|
4
|
-
export { parsePosition, parseContainerPosition, hasAutoLayout } from './position-parser.js';
|
|
4
|
+
export { parsePosition, parseContainerPosition, hasAutoLayout, isAbsolutelyPositioned } from './position-parser.js';
|
|
5
5
|
export { parseNode, parseNodes, parseFrameName } from './node-parser.js';
|
|
6
6
|
export { detectSemanticTag, normalizeName, getSemanticAttributes } from './semantic-detector.js';
|
|
@@ -15,16 +15,22 @@ export interface ILayoutProperties {
|
|
|
15
15
|
flex?: IStyleProperty;
|
|
16
16
|
flexWrap?: IStyleProperty;
|
|
17
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* Parseia padding de um node (funciona com ou sem Auto Layout)
|
|
20
|
+
*/
|
|
21
|
+
export declare function parsePadding(node: IFigmaNode): IStyleProperty | null;
|
|
18
22
|
/**
|
|
19
23
|
* Parseia AutoLayout de um node
|
|
20
24
|
*/
|
|
21
25
|
export declare function parseLayout(node: IFigmaNode): ILayoutProperties;
|
|
22
26
|
/**
|
|
23
27
|
* Parseia sizing de um node
|
|
28
|
+
* @param isRoot - indica se é o node raiz do componente
|
|
24
29
|
*/
|
|
25
|
-
export declare function parseSizing(node: IFigmaNode): ILayoutProperties;
|
|
30
|
+
export declare function parseSizing(node: IFigmaNode, isRoot?: boolean): ILayoutProperties;
|
|
26
31
|
/**
|
|
27
32
|
* Combina layout e sizing
|
|
33
|
+
* @param isRoot - indica se é o node raiz do componente
|
|
28
34
|
*/
|
|
29
|
-
export declare function parseLayoutAndSizing(node: IFigmaNode): IStyleProperty[];
|
|
35
|
+
export declare function parseLayoutAndSizing(node: IFigmaNode, isRoot?: boolean): IStyleProperty[];
|
|
30
36
|
//# sourceMappingURL=layout-parser.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"layout-parser.d.ts","sourceRoot":"","sources":["../../src/parser/layout-parser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AA4BrE,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,aAAa,CAAC,EAAE,cAAc,CAAC;IAC/B,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B;AAED;;GAEG;AACH,wBAAgB,
|
|
1
|
+
{"version":3,"file":"layout-parser.d.ts","sourceRoot":"","sources":["../../src/parser/layout-parser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AA4BrE,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,aAAa,CAAC,EAAE,cAAc,CAAC;IAC/B,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,UAAU,GAAG,cAAc,GAAG,IAAI,CAuBpE;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,UAAU,GAAG,iBAAiB,CA2F/D;AAwBD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,GAAE,OAAe,GAAG,iBAAiB,CA+ExF;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,GAAE,OAAe,GAAG,cAAc,EAAE,CAShG"}
|
|
@@ -26,6 +26,24 @@ function formatPixels(value) {
|
|
|
26
26
|
}
|
|
27
27
|
return `${rounded}px`;
|
|
28
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Parseia padding de um node (funciona com ou sem Auto Layout)
|
|
31
|
+
*/
|
|
32
|
+
export function parsePadding(node) {
|
|
33
|
+
const hasPadding = node.paddingTop ||
|
|
34
|
+
node.paddingRight ||
|
|
35
|
+
node.paddingBottom ||
|
|
36
|
+
node.paddingLeft;
|
|
37
|
+
if (!hasPadding) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
const paddingToken = generatePaddingToken(node.paddingTop ?? 0, node.paddingRight ?? 0, node.paddingBottom ?? 0, node.paddingLeft ?? 0);
|
|
41
|
+
return {
|
|
42
|
+
property: 'padding',
|
|
43
|
+
value: paddingToken,
|
|
44
|
+
token: paddingToken.startsWith('$') ? paddingToken : undefined,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
29
47
|
/**
|
|
30
48
|
* Parseia AutoLayout de um node
|
|
31
49
|
*/
|
|
@@ -102,11 +120,32 @@ export function parseLayout(node) {
|
|
|
102
120
|
}
|
|
103
121
|
return properties;
|
|
104
122
|
}
|
|
123
|
+
/**
|
|
124
|
+
* Detecta se o node é uma tela/página (root component que deve ocupar viewport)
|
|
125
|
+
*/
|
|
126
|
+
function isScreenNode(node) {
|
|
127
|
+
const name = node.name.toLowerCase().replace(/^#/, '');
|
|
128
|
+
const screenPatterns = /screen|page|view|layout|template|tela|pagina/i;
|
|
129
|
+
// Verifica se é uma tela pelo nome
|
|
130
|
+
if (screenPatterns.test(name)) {
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
// Verifica se tem dimensões típicas de tela (>= 320px width e >= 568px height)
|
|
134
|
+
const box = node.absoluteBoundingBox;
|
|
135
|
+
if (box && box.width >= 320 && box.height >= 568) {
|
|
136
|
+
// Se é um frame de nível superior com dimensões de tela
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
105
141
|
/**
|
|
106
142
|
* Parseia sizing de um node
|
|
143
|
+
* @param isRoot - indica se é o node raiz do componente
|
|
107
144
|
*/
|
|
108
|
-
export function parseSizing(node) {
|
|
145
|
+
export function parseSizing(node, isRoot = false) {
|
|
109
146
|
const properties = {};
|
|
147
|
+
// Se é o node raiz e é uma tela, usa viewport units
|
|
148
|
+
const isScreen = isRoot && isScreenNode(node);
|
|
110
149
|
// Width
|
|
111
150
|
if (node.layoutSizingHorizontal) {
|
|
112
151
|
switch (node.layoutSizingHorizontal) {
|
|
@@ -126,17 +165,17 @@ export function parseSizing(node) {
|
|
|
126
165
|
if (node.absoluteBoundingBox?.width) {
|
|
127
166
|
properties.width = {
|
|
128
167
|
property: 'width',
|
|
129
|
-
value: formatPixels(node.absoluteBoundingBox.width),
|
|
168
|
+
value: isScreen ? '100vw' : formatPixels(node.absoluteBoundingBox.width),
|
|
130
169
|
};
|
|
131
170
|
}
|
|
132
171
|
break;
|
|
133
172
|
}
|
|
134
173
|
}
|
|
135
174
|
else if (node.absoluteBoundingBox?.width) {
|
|
136
|
-
// Fallback: Se não tem layoutSizing mas tem dimensões
|
|
175
|
+
// Fallback: Se não tem layoutSizing mas tem dimensões
|
|
137
176
|
properties.width = {
|
|
138
177
|
property: 'width',
|
|
139
|
-
value: formatPixels(node.absoluteBoundingBox.width),
|
|
178
|
+
value: isScreen ? '100vw' : formatPixels(node.absoluteBoundingBox.width),
|
|
140
179
|
};
|
|
141
180
|
}
|
|
142
181
|
// Height
|
|
@@ -158,27 +197,35 @@ export function parseSizing(node) {
|
|
|
158
197
|
if (node.absoluteBoundingBox?.height) {
|
|
159
198
|
properties.height = {
|
|
160
199
|
property: 'height',
|
|
161
|
-
value: formatPixels(node.absoluteBoundingBox.height),
|
|
200
|
+
value: isScreen ? '100vh' : formatPixels(node.absoluteBoundingBox.height),
|
|
162
201
|
};
|
|
163
202
|
}
|
|
164
203
|
break;
|
|
165
204
|
}
|
|
166
205
|
}
|
|
167
206
|
else if (node.absoluteBoundingBox?.height) {
|
|
168
|
-
// Fallback: Se não tem layoutSizing mas tem dimensões
|
|
207
|
+
// Fallback: Se não tem layoutSizing mas tem dimensões
|
|
169
208
|
properties.height = {
|
|
170
209
|
property: 'height',
|
|
171
|
-
value: formatPixels(node.absoluteBoundingBox.height),
|
|
210
|
+
value: isScreen ? '100vh' : formatPixels(node.absoluteBoundingBox.height),
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
// Para screens, adiciona overflow hidden para evitar scroll
|
|
214
|
+
if (isScreen) {
|
|
215
|
+
properties.flex = {
|
|
216
|
+
property: 'overflow',
|
|
217
|
+
value: 'hidden',
|
|
172
218
|
};
|
|
173
219
|
}
|
|
174
220
|
return properties;
|
|
175
221
|
}
|
|
176
222
|
/**
|
|
177
223
|
* Combina layout e sizing
|
|
224
|
+
* @param isRoot - indica se é o node raiz do componente
|
|
178
225
|
*/
|
|
179
|
-
export function parseLayoutAndSizing(node) {
|
|
226
|
+
export function parseLayoutAndSizing(node, isRoot = false) {
|
|
180
227
|
const layout = parseLayout(node);
|
|
181
|
-
const sizing = parseSizing(node);
|
|
228
|
+
const sizing = parseSizing(node, isRoot);
|
|
182
229
|
const combined = { ...layout, ...sizing };
|
|
183
230
|
return Object.values(combined).filter((prop) => prop !== undefined);
|
|
184
231
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node-parser.d.ts","sourceRoot":"","sources":["../../src/parser/node-parser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,UAAU,EACV,aAAa,
|
|
1
|
+
{"version":3,"file":"node-parser.d.ts","sourceRoot":"","sources":["../../src/parser/node-parser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,UAAU,EACV,aAAa,EAOb,cAAc,EACd,gBAAgB,EACjB,MAAM,oBAAoB,CAAC;AAiB5B,MAAM,WAAW,aAAa;IAC5B,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,CAwB7D;AA+RD;;GAEG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,UAAU,EAChB,OAAO,GAAE,aAAkB,GAC1B,aAAa,CA6Cf;AAED;;GAEG;AACH,wBAAgB,UAAU,CACxB,KAAK,EAAE,UAAU,EAAE,EACnB,OAAO,GAAE,aAAkB,GAC1B,aAAa,EAAE,CAEjB"}
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
* Parser principal que converte node do Figma em AST do componente
|
|
4
4
|
*/
|
|
5
5
|
import { generateComponentName, generateFileName, generateBEMBlock, generateBEMElement, classifyComponent, extractBaseName, extractVariants, isComponentNode, } from '@promptui-lib/core';
|
|
6
|
-
import { parseLayoutAndSizing, parseSizing } from './layout-parser.js';
|
|
6
|
+
import { parseLayoutAndSizing, parseSizing, parsePadding } from './layout-parser.js';
|
|
7
7
|
import { parseStyles } from './style-parser.js';
|
|
8
8
|
import { parseTextStyles } from './text-parser.js';
|
|
9
9
|
import { detectSemanticTag, normalizeName, getSemanticAttributes } from './semantic-detector.js';
|
|
10
|
-
import { parsePosition, parseContainerPosition } from './position-parser.js';
|
|
10
|
+
import { parsePosition, parseContainerPosition, hasAutoLayout } from './position-parser.js';
|
|
11
11
|
/**
|
|
12
12
|
* Parseia o nome do frame para extrair informações
|
|
13
13
|
*/
|
|
@@ -32,6 +32,44 @@ export function parseFrameName(name) {
|
|
|
32
32
|
extractedProps,
|
|
33
33
|
};
|
|
34
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* Verifica se um node deve ser exportado como asset (SVG/imagem)
|
|
37
|
+
* Tipos que são gráficos vetoriais ou ícones devem ser exportados como SVG
|
|
38
|
+
*/
|
|
39
|
+
function isAssetNode(node) {
|
|
40
|
+
// Tipos de nodes que são gráficos vetoriais
|
|
41
|
+
const vectorTypes = ['VECTOR', 'BOOLEAN_OPERATION', 'STAR', 'LINE', 'ELLIPSE', 'REGULAR_POLYGON'];
|
|
42
|
+
if (vectorTypes.includes(node.type)) {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
// Verifica se o nome indica que é um ícone
|
|
46
|
+
const name = node.name.toLowerCase();
|
|
47
|
+
if (/^(icon|ico|icone|svg)[-_]?/i.test(name) || name.includes('-icon') || name.includes('_icon')) {
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
// Frames pequenos (< 64x64) com fills complexos podem ser ícones
|
|
51
|
+
if (node.type === 'FRAME' && node.absoluteBoundingBox) {
|
|
52
|
+
const { width, height } = node.absoluteBoundingBox;
|
|
53
|
+
if (width <= 64 && height <= 64) {
|
|
54
|
+
// Verifica se tem apenas elementos vetoriais como filhos
|
|
55
|
+
const hasOnlyVectors = node.children?.every((child) => vectorTypes.includes(child.type) || child.type === 'GROUP');
|
|
56
|
+
if (hasOnlyVectors && node.children && node.children.length > 0) {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Gera nome seguro para arquivo de asset
|
|
65
|
+
*/
|
|
66
|
+
function generateAssetFileName(nodeName, blockName) {
|
|
67
|
+
const cleanName = nodeName
|
|
68
|
+
.toLowerCase()
|
|
69
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
70
|
+
.replace(/^-+|-+$/g, '');
|
|
71
|
+
return `${blockName}-${cleanName || 'icon'}`;
|
|
72
|
+
}
|
|
35
73
|
/**
|
|
36
74
|
* Converte um node em elemento JSX
|
|
37
75
|
* Se o filho for um componente (marcado com #), retorna IJSXComponent em vez de JSX inline
|
|
@@ -74,6 +112,22 @@ function nodeToJSX(node, blockName, depth = 0, parentNode, usedTags) {
|
|
|
74
112
|
fileName: childFileName,
|
|
75
113
|
});
|
|
76
114
|
}
|
|
115
|
+
else if (isAssetNode(child)) {
|
|
116
|
+
// Se é um asset (ícone/vetor), cria referência para exportação
|
|
117
|
+
const childNormalizedName = normalizeName(child.name);
|
|
118
|
+
const childElementName = childNormalizedName.replace(/\s+/g, '-');
|
|
119
|
+
const childClassName = generateBEMElement(blockName, childElementName);
|
|
120
|
+
children.push({
|
|
121
|
+
type: 'asset',
|
|
122
|
+
nodeId: child.id,
|
|
123
|
+
fileName: generateAssetFileName(child.name, blockName),
|
|
124
|
+
format: 'svg',
|
|
125
|
+
className: childClassName,
|
|
126
|
+
alt: childNormalizedName,
|
|
127
|
+
width: child.absoluteBoundingBox?.width,
|
|
128
|
+
height: child.absoluteBoundingBox?.height,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
77
131
|
else {
|
|
78
132
|
children.push(nodeToJSX(child, blockName, depth + 1, node, tagsSet));
|
|
79
133
|
}
|
|
@@ -110,12 +164,20 @@ function collectStyles(node, blockName, depth = 0, parentNode) {
|
|
|
110
164
|
// Para GROUP: extrai sizing e position: relative (container sem flex)
|
|
111
165
|
// Para outros tipos (RECTANGLE, etc): extrai apenas sizing (width, height)
|
|
112
166
|
if (node.type === 'FRAME' || node.type === 'COMPONENT' || node.type === 'INSTANCE') {
|
|
113
|
-
properties.push(...parseLayoutAndSizing(node));
|
|
167
|
+
properties.push(...parseLayoutAndSizing(node, isRoot));
|
|
114
168
|
// Se é um container sem Auto Layout, adiciona position: relative
|
|
115
169
|
const containerPos = parseContainerPosition(node);
|
|
116
170
|
if (containerPos) {
|
|
117
171
|
properties.push(containerPos);
|
|
118
172
|
}
|
|
173
|
+
// Extrai padding mesmo se não tem Auto Layout (para elementos como inputs)
|
|
174
|
+
// parseLayoutAndSizing já extrai padding quando tem Auto Layout, mas não quando não tem
|
|
175
|
+
if (!hasAutoLayout(node)) {
|
|
176
|
+
const paddingProp = parsePadding(node);
|
|
177
|
+
if (paddingProp) {
|
|
178
|
+
properties.push(paddingProp);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
119
181
|
}
|
|
120
182
|
else if (node.type === 'GROUP') {
|
|
121
183
|
// GROUPs são containers visuais - precisam de position: relative e sizing
|
|
@@ -16,6 +16,11 @@ export interface IPositionProperties {
|
|
|
16
16
|
* Verifica se um node pai tem Auto Layout
|
|
17
17
|
*/
|
|
18
18
|
export declare function hasAutoLayout(node: IFigmaNode): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Verifica se um node está posicionado absolutamente
|
|
21
|
+
* (mesmo dentro de um container com Auto Layout)
|
|
22
|
+
*/
|
|
23
|
+
export declare function isAbsolutelyPositioned(node: IFigmaNode): boolean;
|
|
19
24
|
/**
|
|
20
25
|
* Parseia constraints e posição de um node
|
|
21
26
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"position-parser.d.ts","sourceRoot":"","sources":["../../src/parser/position-parser.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAgB,MAAM,oBAAoB,CAAC;AAEnF,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,SAAS,CAAC,EAAE,cAAc,CAAC;CAC5B;AAoBD;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAEvD;AAwBD;;GAEG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,UAAU,EAChB,UAAU,CAAC,EAAE,UAAU,GACtB,cAAc,EAAE,
|
|
1
|
+
{"version":3,"file":"position-parser.d.ts","sourceRoot":"","sources":["../../src/parser/position-parser.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAgB,MAAM,oBAAoB,CAAC;AAEnF,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,SAAS,CAAC,EAAE,cAAc,CAAC;CAC5B;AAoBD;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAEvD;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAEhE;AAwBD;;GAEG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,UAAU,EAChB,UAAU,CAAC,EAAE,UAAU,GACtB,cAAc,EAAE,CA6IlB;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAa/D;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,UAAU,GAAG,cAAc,GAAG,IAAI,CAQ9E"}
|
|
@@ -25,6 +25,13 @@ function formatPixels(value) {
|
|
|
25
25
|
export function hasAutoLayout(node) {
|
|
26
26
|
return node.layoutMode !== undefined && node.layoutMode !== 'NONE';
|
|
27
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Verifica se um node está posicionado absolutamente
|
|
30
|
+
* (mesmo dentro de um container com Auto Layout)
|
|
31
|
+
*/
|
|
32
|
+
export function isAbsolutelyPositioned(node) {
|
|
33
|
+
return node.layoutPositioning === 'ABSOLUTE';
|
|
34
|
+
}
|
|
28
35
|
/**
|
|
29
36
|
* Calcula a posição relativa de um filho em relação ao pai
|
|
30
37
|
*/
|
|
@@ -46,8 +53,16 @@ function calculateRelativePosition(child, parent) {
|
|
|
46
53
|
*/
|
|
47
54
|
export function parsePosition(node, parentNode) {
|
|
48
55
|
const properties = [];
|
|
49
|
-
|
|
50
|
-
|
|
56
|
+
if (!parentNode) {
|
|
57
|
+
return properties;
|
|
58
|
+
}
|
|
59
|
+
// Verifica se o node precisa de posicionamento absoluto:
|
|
60
|
+
// 1. O pai NÃO tem Auto Layout, OU
|
|
61
|
+
// 2. O node tem layoutPositioning: 'ABSOLUTE' (absoluto dentro de Auto Layout)
|
|
62
|
+
const parentHasAutoLayout = hasAutoLayout(parentNode);
|
|
63
|
+
const nodeIsAbsolute = isAbsolutelyPositioned(node);
|
|
64
|
+
if (parentHasAutoLayout && !nodeIsAbsolute) {
|
|
65
|
+
// Pai tem Auto Layout e o node não é absoluto - não precisa de posicionamento
|
|
51
66
|
return properties;
|
|
52
67
|
}
|
|
53
68
|
// Calcula posição relativa
|
|
@@ -56,7 +71,7 @@ export function parsePosition(node, parentNode) {
|
|
|
56
71
|
return properties;
|
|
57
72
|
}
|
|
58
73
|
const constraints = node.constraints;
|
|
59
|
-
//
|
|
74
|
+
// Elemento precisa de posição absoluta
|
|
60
75
|
properties.push({
|
|
61
76
|
property: 'position',
|
|
62
77
|
value: 'absolute',
|
|
@@ -176,11 +191,16 @@ export function parsePosition(node, parentNode) {
|
|
|
176
191
|
* (necessário quando tem filhos com position: absolute)
|
|
177
192
|
*/
|
|
178
193
|
export function needsRelativePosition(node) {
|
|
179
|
-
|
|
180
|
-
|
|
194
|
+
if (!node.children || node.children.length === 0) {
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
// Se não tem Auto Layout, precisa de relative (todos os filhos serão absolutos)
|
|
198
|
+
if (!hasAutoLayout(node)) {
|
|
181
199
|
return true;
|
|
182
200
|
}
|
|
183
|
-
|
|
201
|
+
// Se tem Auto Layout, verifica se algum filho tem layoutPositioning: 'ABSOLUTE'
|
|
202
|
+
const hasAbsoluteChild = node.children.some((child) => isAbsolutelyPositioned(child));
|
|
203
|
+
return hasAbsoluteChild;
|
|
184
204
|
}
|
|
185
205
|
/**
|
|
186
206
|
* Parseia position: relative para containers sem Auto Layout
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@promptui-lib/figma-parser",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.20",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Figma API client and parser for PromptUI",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"dist"
|
|
31
31
|
],
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@promptui-lib/core": "0.1.
|
|
33
|
+
"@promptui-lib/core": "0.1.20"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/node": "^20.0.0",
|