@seed-design/figma 0.0.21 → 0.0.22
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/lib/codegen/index.cjs +8019 -0
- package/lib/codegen/index.d.ts +1827 -0
- package/lib/codegen/index.js +7989 -0
- package/lib/codegen/targets/react/index.cjs +12205 -0
- package/lib/codegen/targets/react/index.d.ts +270 -0
- package/lib/codegen/targets/react/index.js +12187 -0
- package/lib/index.cjs +79 -2741
- package/lib/index.d.ts +17 -1937
- package/lib/index.js +61 -2715
- package/package.json +11 -1
- package/src/codegen/{targets/react/component/properties.type.ts → component-properties.ts} +43 -43
- package/src/codegen/core/codegen.ts +23 -8
- package/src/codegen/core/{component.ts → component-handler.ts} +3 -3
- package/src/codegen/core/component-type-helper.ts +35 -0
- package/src/codegen/core/context.ts +20 -0
- package/src/codegen/core/index.ts +16 -14
- package/src/codegen/core/{props.ts → props-converter.ts} +10 -13
- package/src/codegen/core/{value.ts → value-resolver.ts} +90 -52
- package/src/codegen/default-services.ts +44 -0
- package/src/codegen/index.ts +1 -44
- package/src/codegen/targets/figma/frame.ts +8 -8
- package/src/codegen/targets/figma/index.ts +1 -1
- package/src/codegen/targets/figma/pipeline.ts +71 -0
- package/src/codegen/targets/figma/props.ts +59 -70
- package/src/codegen/targets/figma/shape.ts +18 -18
- package/src/codegen/targets/figma/text.ts +6 -6
- package/src/codegen/targets/figma/value-resolver.ts +22 -0
- package/src/codegen/targets/react/component/deps.interface.ts +5 -4
- package/src/codegen/targets/react/component/{transformers → handlers}/action-button.ts +8 -14
- package/src/codegen/targets/react/component/{transformers → handlers}/action-chip.ts +10 -20
- package/src/codegen/targets/react/component/{transformers → handlers}/action-sheet.ts +13 -10
- package/src/codegen/targets/react/component/{transformers → handlers}/app-bar.ts +28 -36
- package/src/codegen/targets/react/component/handlers/avatar-stack.ts +29 -0
- package/src/codegen/targets/react/component/{transformers → handlers}/avatar.ts +12 -9
- package/src/codegen/targets/react/component/handlers/badge.ts +18 -0
- package/src/codegen/targets/react/component/{transformers → handlers}/callout.ts +6 -8
- package/src/codegen/targets/react/component/{transformers → handlers}/checkbox.ts +5 -5
- package/src/codegen/targets/react/component/{transformers → handlers}/chip-tabs.ts +10 -10
- package/src/codegen/targets/react/component/{transformers → handlers}/control-chip.ts +10 -20
- package/src/codegen/targets/react/component/{transformers → handlers}/error-state.ts +9 -9
- package/src/codegen/targets/react/component/{transformers → handlers}/extended-action-sheet.ts +16 -18
- package/src/codegen/targets/react/component/{transformers → handlers}/extended-fab.ts +6 -6
- package/src/codegen/targets/react/component/handlers/fab.ts +18 -0
- package/src/codegen/targets/react/component/{transformers → handlers}/help-bubble.ts +5 -5
- package/src/codegen/targets/react/component/{transformers → handlers}/identity-placeholder.ts +5 -5
- package/src/codegen/targets/react/component/{transformers → handlers}/inline-banner.ts +7 -10
- package/src/codegen/targets/react/component/{transformers → handlers}/manner-temp-badge.ts +5 -5
- package/src/codegen/targets/react/component/{transformers → handlers}/multiline-text-field.ts +5 -5
- package/src/codegen/targets/react/component/{transformers → handlers}/progress-circle.ts +5 -5
- package/src/codegen/targets/react/component/{transformers → handlers}/reaction-button.ts +6 -6
- package/src/codegen/targets/react/component/{transformers → handlers}/segmented-control.ts +10 -10
- package/src/codegen/targets/react/component/{transformers → handlers}/select-box.ts +10 -10
- package/src/codegen/targets/react/component/handlers/skeleton.ts +25 -0
- package/src/codegen/targets/react/component/{transformers → handlers}/snackbar.ts +5 -5
- package/src/codegen/targets/react/component/{transformers → handlers}/switch.ts +5 -5
- package/src/codegen/targets/react/component/{transformers → handlers}/tabs.ts +15 -15
- package/src/codegen/targets/react/component/{transformers → handlers}/text-button.ts +7 -13
- package/src/codegen/targets/react/component/{transformers → handlers}/text-field.ts +9 -9
- package/src/codegen/targets/react/component/{transformers → handlers}/toggle-button.ts +7 -11
- package/src/codegen/targets/react/component/index.ts +79 -75
- package/src/codegen/targets/react/frame.ts +8 -8
- package/src/codegen/targets/react/icon.ts +50 -0
- package/src/codegen/targets/react/index.ts +1 -1
- package/src/codegen/targets/react/instance.ts +19 -50
- package/src/codegen/targets/react/pipeline.ts +108 -0
- package/src/codegen/targets/react/props.ts +95 -73
- package/src/codegen/targets/react/shape.ts +5 -5
- package/src/codegen/targets/react/text.ts +6 -6
- package/src/codegen/targets/react/value-resolver.ts +35 -0
- package/src/entities/icon.repository.ts +2 -2
- package/src/entities/icon.service.ts +9 -20
- package/src/entities/style.service.ts +5 -17
- package/src/entities/variable.service.ts +36 -68
- package/src/utils/figma-variable.ts +39 -3
- package/src/codegen/core/component.types.ts +0 -29
- package/src/codegen/targets/figma/context.ts +0 -139
- package/src/codegen/targets/react/component/transformers/avatar-stack.ts +0 -29
- package/src/codegen/targets/react/component/transformers/badge.ts +0 -21
- package/src/codegen/targets/react/component/transformers/fab.ts +0 -18
- package/src/codegen/targets/react/component/transformers/skeleton.ts +0 -51
- package/src/codegen/targets/react/context.ts +0 -176
- /package/src/codegen/core/{element.ts → element-transformer.ts} +0 -0
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createPropsTransformer,
|
|
3
|
-
definePropsTransformer,
|
|
4
|
-
type PropsTransformer,
|
|
5
|
-
type ValueTransformer,
|
|
6
|
-
} from "@/codegen/core";
|
|
7
|
-
import type { StyleService } from "@/entities";
|
|
1
|
+
import { createPropsConverter, definePropsConverter, type PropsConverter } from "@/codegen/core";
|
|
8
2
|
import type {
|
|
9
3
|
NormalizedCornerTrait,
|
|
10
4
|
NormalizedHasChildrenTrait,
|
|
@@ -15,17 +9,19 @@ import type {
|
|
|
15
9
|
NormalizedTypePropertiesTrait,
|
|
16
10
|
} from "@/normalizer";
|
|
17
11
|
import { match } from "ts-pattern";
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
12
|
+
import type { ReactValueResolver } from "./value-resolver";
|
|
13
|
+
|
|
14
|
+
export interface PropsConverters {
|
|
15
|
+
containerLayout: PropsConverter<ContainerLayoutTrait, ContainerLayoutProps>;
|
|
16
|
+
selfLayout: PropsConverter<SelfLayoutTrait, SelfLayoutProps>;
|
|
17
|
+
iconSelfLayout: PropsConverter<SelfLayoutTrait, IconSelfLayoutProps>;
|
|
18
|
+
radius: PropsConverter<RadiusTrait, RadiusProps>;
|
|
19
|
+
frameFill: PropsConverter<FillTrait, FrameFillProps>;
|
|
20
|
+
shapeFill: PropsConverter<FillTrait, ShapeFillProps>;
|
|
21
|
+
textFill: PropsConverter<FillTrait, TextFillProps>;
|
|
22
|
+
vectorChildrenFill: PropsConverter<ContainerLayoutTrait, VectorChildrenFillProps>;
|
|
23
|
+
stroke: PropsConverter<StrokeTrait, StrokeProps>;
|
|
24
|
+
typeStyle: PropsConverter<TypeStyleTrait, TypeStyleProps>;
|
|
29
25
|
}
|
|
30
26
|
|
|
31
27
|
export type ContainerLayoutTrait = NormalizedHasFramePropertiesTrait &
|
|
@@ -58,12 +54,10 @@ export interface ContainerLayoutProps {
|
|
|
58
54
|
p?: string | 0;
|
|
59
55
|
}
|
|
60
56
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
): PropsTransformer<ContainerLayoutTrait, ContainerLayoutProps> {
|
|
66
|
-
return createPropsTransformer({
|
|
57
|
+
export function createContainerLayoutPropsConverter(
|
|
58
|
+
valueResolver: ReactValueResolver,
|
|
59
|
+
): PropsConverter<ContainerLayoutTrait, ContainerLayoutProps> {
|
|
60
|
+
return createPropsConverter({
|
|
67
61
|
_types: {
|
|
68
62
|
trait: {} as ContainerLayoutTrait,
|
|
69
63
|
props: {} as ContainerLayoutProps,
|
|
@@ -120,12 +114,12 @@ export function createContainerLayoutPropsTransformer(
|
|
|
120
114
|
return undefined;
|
|
121
115
|
}
|
|
122
116
|
|
|
123
|
-
return
|
|
117
|
+
return valueResolver.getFormattedValue.itemSpacing(node);
|
|
124
118
|
},
|
|
125
|
-
pt: (node) =>
|
|
126
|
-
pb: (node) =>
|
|
127
|
-
pl: (node) =>
|
|
128
|
-
pr: (node) =>
|
|
119
|
+
pt: (node) => valueResolver.getFormattedValue.paddingTop(node),
|
|
120
|
+
pb: (node) => valueResolver.getFormattedValue.paddingBottom(node),
|
|
121
|
+
pl: (node) => valueResolver.getFormattedValue.paddingLeft(node),
|
|
122
|
+
pr: (node) => valueResolver.getFormattedValue.paddingRight(node),
|
|
129
123
|
},
|
|
130
124
|
shorthands: {
|
|
131
125
|
p: ["pt", "pb", "pl", "pr"],
|
|
@@ -159,10 +153,10 @@ export interface SelfLayoutProps {
|
|
|
159
153
|
maxHeight?: string | number;
|
|
160
154
|
}
|
|
161
155
|
|
|
162
|
-
export function
|
|
163
|
-
|
|
164
|
-
):
|
|
165
|
-
return
|
|
156
|
+
export function createSelfLayoutPropsConverter(
|
|
157
|
+
valueResolver: ReactValueResolver,
|
|
158
|
+
): PropsConverter<SelfLayoutTrait, SelfLayoutProps> {
|
|
159
|
+
return createPropsConverter({
|
|
166
160
|
_types: {
|
|
167
161
|
trait: {} as SelfLayoutTrait,
|
|
168
162
|
props: {} as SelfLayoutProps,
|
|
@@ -180,27 +174,27 @@ export function createSelfLayoutPropsTransformer(
|
|
|
180
174
|
.exhaustive(),
|
|
181
175
|
height: (node) =>
|
|
182
176
|
node.layoutSizingVertical === "FIXED"
|
|
183
|
-
?
|
|
177
|
+
? valueResolver.getFormattedValue.height(node)
|
|
184
178
|
: undefined,
|
|
185
179
|
width: (node) =>
|
|
186
180
|
node.layoutSizingHorizontal === "FIXED"
|
|
187
|
-
?
|
|
181
|
+
? valueResolver.getFormattedValue.width(node)
|
|
188
182
|
: undefined,
|
|
189
183
|
minHeight: (node) =>
|
|
190
184
|
node.layoutSizingVertical === "HUG"
|
|
191
|
-
?
|
|
185
|
+
? valueResolver.getFormattedValue.minHeight(node)
|
|
192
186
|
: undefined,
|
|
193
187
|
maxHeight: (node) =>
|
|
194
188
|
node.layoutSizingVertical === "HUG"
|
|
195
|
-
?
|
|
189
|
+
? valueResolver.getFormattedValue.maxHeight(node)
|
|
196
190
|
: undefined,
|
|
197
191
|
minWidth: (node) =>
|
|
198
192
|
node.layoutSizingHorizontal === "HUG"
|
|
199
|
-
?
|
|
193
|
+
? valueResolver.getFormattedValue.minWidth(node)
|
|
200
194
|
: undefined,
|
|
201
195
|
maxWidth: (node) =>
|
|
202
196
|
node.layoutSizingHorizontal === "HUG"
|
|
203
|
-
?
|
|
197
|
+
? valueResolver.getFormattedValue.maxWidth(node)
|
|
204
198
|
: undefined,
|
|
205
199
|
},
|
|
206
200
|
defaults: {
|
|
@@ -213,14 +207,14 @@ export interface IconSelfLayoutProps {
|
|
|
213
207
|
size?: string | number;
|
|
214
208
|
}
|
|
215
209
|
|
|
216
|
-
export function
|
|
217
|
-
return
|
|
210
|
+
export function createIconSelfLayoutPropsConverter(valueResolver: ReactValueResolver) {
|
|
211
|
+
return createPropsConverter({
|
|
218
212
|
_types: {
|
|
219
213
|
trait: {} as SelfLayoutTrait,
|
|
220
214
|
props: {} as IconSelfLayoutProps,
|
|
221
215
|
},
|
|
222
216
|
handlers: {
|
|
223
|
-
size: (node) =>
|
|
217
|
+
size: (node) => valueResolver.getFormattedValue.width(node),
|
|
224
218
|
},
|
|
225
219
|
});
|
|
226
220
|
}
|
|
@@ -233,17 +227,17 @@ export interface RadiusProps {
|
|
|
233
227
|
borderBottomRightRadius?: string | 0;
|
|
234
228
|
}
|
|
235
229
|
|
|
236
|
-
export function
|
|
237
|
-
return
|
|
230
|
+
export function createRadiusPropsConverter(valueResolver: ReactValueResolver) {
|
|
231
|
+
return createPropsConverter({
|
|
238
232
|
_types: {
|
|
239
233
|
trait: {} as RadiusTrait,
|
|
240
234
|
props: {} as RadiusProps,
|
|
241
235
|
},
|
|
242
236
|
handlers: {
|
|
243
|
-
borderTopLeftRadius: (node) =>
|
|
244
|
-
borderTopRightRadius: (node) =>
|
|
245
|
-
borderBottomLeftRadius: (node) =>
|
|
246
|
-
borderBottomRightRadius: (node) =>
|
|
237
|
+
borderTopLeftRadius: (node) => valueResolver.getFormattedValue.topLeftRadius(node),
|
|
238
|
+
borderTopRightRadius: (node) => valueResolver.getFormattedValue.topRightRadius(node),
|
|
239
|
+
borderBottomLeftRadius: (node) => valueResolver.getFormattedValue.bottomLeftRadius(node),
|
|
240
|
+
borderBottomRightRadius: (node) => valueResolver.getFormattedValue.bottomRightRadius(node),
|
|
247
241
|
},
|
|
248
242
|
shorthands: {
|
|
249
243
|
borderRadius: [
|
|
@@ -271,15 +265,13 @@ export interface TypeStyleProps {
|
|
|
271
265
|
maxLines?: number;
|
|
272
266
|
}
|
|
273
267
|
|
|
274
|
-
export function
|
|
275
|
-
|
|
276
|
-
styleService,
|
|
268
|
+
export function createTypeStylePropsConverter({
|
|
269
|
+
valueResolver,
|
|
277
270
|
}: {
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
const styleName = node.textStyleKey ? styleService.getStyleName(node.textStyleKey) : undefined;
|
|
271
|
+
valueResolver: ReactValueResolver;
|
|
272
|
+
}): PropsConverter<TypeStyleTrait, TypeStyleProps> {
|
|
273
|
+
return definePropsConverter((node) => {
|
|
274
|
+
const styleName = valueResolver.getTextStyleValue(node);
|
|
283
275
|
const maxLines =
|
|
284
276
|
node.style.textTruncation === "ENDING" ? (node.style.maxLines ?? undefined) : undefined;
|
|
285
277
|
|
|
@@ -291,9 +283,9 @@ export function createTypeStylePropsTransformer({
|
|
|
291
283
|
}
|
|
292
284
|
|
|
293
285
|
return {
|
|
294
|
-
fontSize:
|
|
295
|
-
fontWeight:
|
|
296
|
-
lineHeight:
|
|
286
|
+
fontSize: valueResolver.getFormattedValue.fontSize(node),
|
|
287
|
+
fontWeight: valueResolver.getFormattedValue.fontWeight(node),
|
|
288
|
+
lineHeight: valueResolver.getFormattedValue.lineHeight(node),
|
|
297
289
|
maxLines,
|
|
298
290
|
};
|
|
299
291
|
});
|
|
@@ -303,9 +295,9 @@ export interface FrameFillProps {
|
|
|
303
295
|
bg?: string;
|
|
304
296
|
}
|
|
305
297
|
|
|
306
|
-
export function
|
|
307
|
-
return
|
|
308
|
-
const bg =
|
|
298
|
+
export function createFrameFillPropsConverter(valueResolver: ReactValueResolver) {
|
|
299
|
+
return definePropsConverter<FillTrait, FrameFillProps>((node) => {
|
|
300
|
+
const bg = valueResolver.getFormattedValue.frameFill(node);
|
|
309
301
|
|
|
310
302
|
return {
|
|
311
303
|
bg,
|
|
@@ -317,9 +309,9 @@ export interface ShapeFillProps {
|
|
|
317
309
|
color?: string;
|
|
318
310
|
}
|
|
319
311
|
|
|
320
|
-
export function
|
|
321
|
-
return
|
|
322
|
-
const color =
|
|
312
|
+
export function createShapeFillPropsConverter(valueResolver: ReactValueResolver) {
|
|
313
|
+
return definePropsConverter<FillTrait, ShapeFillProps>((node) => {
|
|
314
|
+
const color = valueResolver.getFormattedValue.shapeFill(node);
|
|
323
315
|
|
|
324
316
|
return {
|
|
325
317
|
color,
|
|
@@ -331,9 +323,9 @@ export interface TextFillProps {
|
|
|
331
323
|
color?: string;
|
|
332
324
|
}
|
|
333
325
|
|
|
334
|
-
export function
|
|
335
|
-
return
|
|
336
|
-
const color =
|
|
326
|
+
export function createTextFillPropsConverter(valueResolver: ReactValueResolver) {
|
|
327
|
+
return definePropsConverter<FillTrait, TextFillProps>((node) => {
|
|
328
|
+
const color = valueResolver.getFormattedValue.textFill(node);
|
|
337
329
|
|
|
338
330
|
return {
|
|
339
331
|
color,
|
|
@@ -341,16 +333,46 @@ export function createTextFillPropsTransformer(valueTransformer: ReactValueTrans
|
|
|
341
333
|
});
|
|
342
334
|
}
|
|
343
335
|
|
|
336
|
+
export interface VectorChildrenFillProps {
|
|
337
|
+
color?: string;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
export function createVectorChildrenFillPropsConverter(valueResolver: ReactValueResolver) {
|
|
341
|
+
return definePropsConverter<ContainerLayoutTrait, VectorChildrenFillProps>((node) => {
|
|
342
|
+
if (node.children.length === 0) {
|
|
343
|
+
console.warn(
|
|
344
|
+
`createVectorChildrenFillPropsConverter: Node has no children. Name:${node.name}, ID:${node.id}`,
|
|
345
|
+
);
|
|
346
|
+
return {};
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const vectors = node.children.filter(
|
|
350
|
+
(child) => child.type === "VECTOR" || child.type === "BOOLEAN_OPERATION",
|
|
351
|
+
);
|
|
352
|
+
|
|
353
|
+
const colors = vectors.map((vector) => valueResolver.getFormattedValue.shapeFill(vector));
|
|
354
|
+
|
|
355
|
+
const fills = new Set(colors.filter((color) => color !== undefined));
|
|
356
|
+
|
|
357
|
+
// If there are more than 1 color, colors are likely pre-defined in the icon component; we should ignore the color prop.
|
|
358
|
+
if (fills.size > 1) {
|
|
359
|
+
return {};
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
return { color: fills.values().next().value };
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
|
|
344
366
|
export interface StrokeProps {
|
|
345
367
|
borderWidth?: number;
|
|
346
368
|
borderColor?: string;
|
|
347
369
|
}
|
|
348
370
|
|
|
349
|
-
export function
|
|
350
|
-
|
|
351
|
-
):
|
|
352
|
-
return
|
|
353
|
-
const borderColor =
|
|
371
|
+
export function createStrokePropsConverter(
|
|
372
|
+
valueResolver: ReactValueResolver,
|
|
373
|
+
): PropsConverter<StrokeTrait, StrokeProps> {
|
|
374
|
+
return definePropsConverter((node) => {
|
|
375
|
+
const borderColor = valueResolver.getFormattedValue.stroke(node);
|
|
354
376
|
const borderWidth = borderColor ? node.strokeWeight : undefined;
|
|
355
377
|
|
|
356
378
|
return {
|
|
@@ -4,19 +4,19 @@ import type {
|
|
|
4
4
|
NormalizedVectorNode,
|
|
5
5
|
} from "@/normalizer";
|
|
6
6
|
import { createElement, defineElementTransformer, type ElementTransformer } from "../../core";
|
|
7
|
-
import type {
|
|
7
|
+
import type { PropsConverters } from "./props";
|
|
8
8
|
|
|
9
9
|
export interface RectangleTransformerDeps {
|
|
10
|
-
|
|
10
|
+
propsConverters: PropsConverters;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export function createRectangleTransformer({
|
|
14
|
-
|
|
14
|
+
propsConverters,
|
|
15
15
|
}: RectangleTransformerDeps): ElementTransformer<NormalizedRectangleNode> {
|
|
16
|
-
return defineElementTransformer((node: NormalizedRectangleNode
|
|
16
|
+
return defineElementTransformer((node: NormalizedRectangleNode) => {
|
|
17
17
|
return createElement(
|
|
18
18
|
"Box",
|
|
19
|
-
{ ...
|
|
19
|
+
{ ...propsConverters.selfLayout(node), background: "palette.gray200" },
|
|
20
20
|
undefined,
|
|
21
21
|
"Rectangle Node Placeholder",
|
|
22
22
|
);
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import type { NormalizedTextNode } from "@/normalizer";
|
|
2
2
|
import { compactObject } from "@/utils/common";
|
|
3
3
|
import { createElement, defineElementTransformer, type ElementTransformer } from "../../core";
|
|
4
|
-
import type {
|
|
4
|
+
import type { PropsConverters } from "./props";
|
|
5
5
|
|
|
6
6
|
export interface TextTransformerDeps {
|
|
7
|
-
|
|
7
|
+
propsConverters: PropsConverters;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export function createTextTransformer({
|
|
11
|
-
|
|
11
|
+
propsConverters,
|
|
12
12
|
}: TextTransformerDeps): ElementTransformer<NormalizedTextNode> {
|
|
13
|
-
return defineElementTransformer((node: NormalizedTextNode
|
|
13
|
+
return defineElementTransformer((node: NormalizedTextNode) => {
|
|
14
14
|
const hasMultipleFills = node.fills.length > 1;
|
|
15
15
|
|
|
16
|
-
const fillProps =
|
|
17
|
-
const typeStyleProps =
|
|
16
|
+
const fillProps = propsConverters.textFill(node);
|
|
17
|
+
const typeStyleProps = propsConverters.typeStyle(node);
|
|
18
18
|
|
|
19
19
|
const props = compactObject({
|
|
20
20
|
...typeStyleProps,
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { createValueResolver, type ValueResolver } from "@/codegen/core";
|
|
2
|
+
import { styleService, variableService } from "@/codegen/default-services";
|
|
3
|
+
import { camelCasePreserveUnderscoreBetweenNumbers } from "@/utils/common";
|
|
4
|
+
import { toCssPixel, toCssRgba } from "@/utils/css";
|
|
5
|
+
import { camelCase } from "change-case";
|
|
6
|
+
|
|
7
|
+
export type ReactValueResolver = ValueResolver<string, string, string, number>;
|
|
8
|
+
|
|
9
|
+
export const valueResolver = createValueResolver({
|
|
10
|
+
variableService,
|
|
11
|
+
variableNameFormatter: ({ slug }) =>
|
|
12
|
+
slug
|
|
13
|
+
.filter(
|
|
14
|
+
(s) =>
|
|
15
|
+
!(
|
|
16
|
+
s === "dimension" ||
|
|
17
|
+
s === "radius" ||
|
|
18
|
+
s === "font-size" ||
|
|
19
|
+
s === "font-weight" ||
|
|
20
|
+
s === "line-height"
|
|
21
|
+
),
|
|
22
|
+
)
|
|
23
|
+
.map((s) => s.replaceAll(",", "_"))
|
|
24
|
+
.map(camelCasePreserveUnderscoreBetweenNumbers)
|
|
25
|
+
.join("."),
|
|
26
|
+
styleService,
|
|
27
|
+
styleNameFormatter: ({ slug }) =>
|
|
28
|
+
camelCase(slug[slug.length - 1]!, { mergeAmbiguousCharacters: true }),
|
|
29
|
+
rawValueFormatters: {
|
|
30
|
+
color: (value: RGBA) => toCssRgba(value),
|
|
31
|
+
dimension: (value: number) => toCssPixel(value),
|
|
32
|
+
fontDimension: (value: number) => toCssPixel(value),
|
|
33
|
+
fontWeight: (value: number) => value,
|
|
34
|
+
},
|
|
35
|
+
});
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { IconData } from "./icon.interface";
|
|
2
2
|
|
|
3
3
|
export interface IconRepository {
|
|
4
|
-
|
|
4
|
+
getOne(key: string): IconData;
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
export function createStaticIconRepository(iconRecord: Record<string, IconData>) {
|
|
8
8
|
return {
|
|
9
|
-
|
|
9
|
+
getOne: (key: string) => iconRecord[key],
|
|
10
10
|
};
|
|
11
11
|
}
|
|
@@ -1,35 +1,24 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { IconData } from "./icon.interface";
|
|
2
2
|
import type { IconRepository } from "./icon.repository";
|
|
3
3
|
|
|
4
4
|
export interface IconService {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
isAvailable: (componentKey: string) => boolean;
|
|
6
|
+
getOne: (componentKey: string) => IconData;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
export function createIconService({
|
|
10
10
|
iconRepository,
|
|
11
11
|
}: { iconRepository: IconRepository }): IconService {
|
|
12
|
-
function
|
|
13
|
-
return iconRepository.
|
|
12
|
+
function isAvailable(componentKey: string) {
|
|
13
|
+
return iconRepository.getOne(componentKey) !== undefined;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
function
|
|
17
|
-
|
|
18
|
-
return "UnknownIcon";
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const iconData = iconRepository.getIconData(key);
|
|
22
|
-
if (!iconData) {
|
|
23
|
-
return "UnknownIcon";
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const { name, weight } = iconData;
|
|
27
|
-
|
|
28
|
-
return pascalCase(`${name}${weight ? weight : ""}`);
|
|
16
|
+
function getOne(componentKey: string) {
|
|
17
|
+
return iconRepository.getOne(componentKey);
|
|
29
18
|
}
|
|
30
19
|
|
|
31
20
|
return {
|
|
32
|
-
|
|
33
|
-
|
|
21
|
+
isAvailable,
|
|
22
|
+
getOne,
|
|
34
23
|
};
|
|
35
24
|
}
|
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
import type { StyleRepository } from "./style.repository";
|
|
2
2
|
|
|
3
3
|
export interface StyleService {
|
|
4
|
-
|
|
4
|
+
getSlug: (id: string) => string[] | undefined;
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
// TODO: inferStyleName 추가해야 함, rest api에서 style value가 제공되지 않고 있어 보류
|
|
8
8
|
export function createStyleService({
|
|
9
9
|
styleRepository,
|
|
10
|
-
styleNameTransformer,
|
|
11
10
|
}: {
|
|
12
11
|
styleRepository: StyleRepository;
|
|
13
|
-
styleNameTransformer: ({ slug }: { slug: string[] }) => string;
|
|
14
12
|
}): StyleService {
|
|
15
|
-
function
|
|
13
|
+
function getName(id: string) {
|
|
16
14
|
const style = styleRepository.findOneByKey(id);
|
|
17
15
|
|
|
18
16
|
if (!style) {
|
|
@@ -22,8 +20,8 @@ export function createStyleService({
|
|
|
22
20
|
return style.name;
|
|
23
21
|
}
|
|
24
22
|
|
|
25
|
-
function
|
|
26
|
-
const name =
|
|
23
|
+
function getSlug(id: string): string[] | undefined {
|
|
24
|
+
const name = getName(id);
|
|
27
25
|
|
|
28
26
|
if (!name) {
|
|
29
27
|
return undefined;
|
|
@@ -32,17 +30,7 @@ export function createStyleService({
|
|
|
32
30
|
return name.split("/");
|
|
33
31
|
}
|
|
34
32
|
|
|
35
|
-
function getStyleName(id: string) {
|
|
36
|
-
const slug = getFigmaStyleSlug(id);
|
|
37
|
-
|
|
38
|
-
if (!slug) {
|
|
39
|
-
return undefined;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return styleNameTransformer({ slug });
|
|
43
|
-
}
|
|
44
|
-
|
|
45
33
|
return {
|
|
46
|
-
|
|
34
|
+
getSlug,
|
|
47
35
|
};
|
|
48
36
|
}
|
|
@@ -1,42 +1,41 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
isIdenticalVariableValue,
|
|
3
|
+
isInsideScope,
|
|
4
|
+
isVariableAlias,
|
|
5
|
+
sanitizeVariableId,
|
|
6
|
+
} from "@/utils/figma-variable";
|
|
2
7
|
import type { Variable, VariableScope, VariableValueResolved } from "./variable.interface";
|
|
3
8
|
import type { VariableRepository } from "./variable.repository";
|
|
4
9
|
|
|
5
10
|
export interface VariableService {
|
|
6
|
-
|
|
7
|
-
|
|
11
|
+
getSlug: (id: string) => string[] | undefined;
|
|
12
|
+
resolveValue: (variable: Variable, mode: string) => VariableValueResolved;
|
|
13
|
+
infer: (value: VariableValueResolved, scope: VariableScope) => Variable | undefined;
|
|
8
14
|
}
|
|
9
15
|
|
|
10
16
|
export interface VariableServiceDeps {
|
|
11
17
|
variableRepository: VariableRepository;
|
|
12
|
-
|
|
13
|
-
inferCompareFunction: (name1: string, name2: string) => number;
|
|
18
|
+
inferCompareFunction: (a: Variable, b: Variable) => number;
|
|
14
19
|
}
|
|
15
20
|
|
|
16
21
|
export function createVariableService({
|
|
17
22
|
variableRepository,
|
|
18
|
-
variableNameTransformer,
|
|
19
23
|
inferCompareFunction,
|
|
20
24
|
}: VariableServiceDeps): VariableService {
|
|
21
25
|
const variables = variableRepository.getVariableList();
|
|
22
26
|
|
|
23
27
|
// private
|
|
24
|
-
function
|
|
28
|
+
function getName(key: string) {
|
|
25
29
|
const sanitizedId = sanitizeVariableId(key);
|
|
26
30
|
const variable = variableRepository.findVariableByKey(sanitizedId);
|
|
27
31
|
|
|
28
32
|
if (!variable) {
|
|
29
|
-
return
|
|
33
|
+
return undefined;
|
|
30
34
|
}
|
|
31
35
|
|
|
32
36
|
return variable.name;
|
|
33
37
|
}
|
|
34
38
|
|
|
35
|
-
function getFigmaVariableSlug(key: string): string[] {
|
|
36
|
-
const name = getFigmaVariableName(key);
|
|
37
|
-
return name.split("/");
|
|
38
|
-
}
|
|
39
|
-
|
|
40
39
|
function getDefaultModeId(variable: Variable) {
|
|
41
40
|
const variableCollection = variableRepository.findVariableCollectionById(
|
|
42
41
|
variable.variableCollectionId,
|
|
@@ -50,84 +49,53 @@ export function createVariableService({
|
|
|
50
49
|
return variableCollection.defaultModeId;
|
|
51
50
|
}
|
|
52
51
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
throw new Error(`Variable not found: ${id}`);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const value = variable.valuesByMode[mode];
|
|
60
|
-
|
|
61
|
-
if (value === undefined) {
|
|
62
|
-
throw new Error(`Variable value not found: ${id} ${mode}`);
|
|
63
|
-
}
|
|
52
|
+
// public
|
|
53
|
+
function getSlug(key: string): string[] | undefined {
|
|
54
|
+
const name = getName(key);
|
|
64
55
|
|
|
65
|
-
if (
|
|
66
|
-
return
|
|
56
|
+
if (!name) {
|
|
57
|
+
return undefined;
|
|
67
58
|
}
|
|
68
59
|
|
|
69
|
-
return
|
|
60
|
+
return name.split("/");
|
|
70
61
|
}
|
|
71
62
|
|
|
72
|
-
function
|
|
73
|
-
|
|
74
|
-
return false;
|
|
75
|
-
}
|
|
63
|
+
function resolveValue(variable: Variable, mode: string): VariableValueResolved {
|
|
64
|
+
const value = variable.valuesByMode[mode];
|
|
76
65
|
|
|
77
|
-
if (
|
|
78
|
-
|
|
66
|
+
if (value === undefined) {
|
|
67
|
+
throw new Error(`Variable value not found: ${variable.id} ${mode}`);
|
|
79
68
|
}
|
|
80
69
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
value1.g === (value2 as RGBA).g &&
|
|
84
|
-
value1.b === (value2 as RGBA).b &&
|
|
85
|
-
value1.a === (value2 as RGBA).a
|
|
86
|
-
);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function isInsideScope(variable: Variable, scope: VariableScope) {
|
|
90
|
-
if (variable.scopes.includes("ALL_SCOPES")) {
|
|
91
|
-
return true;
|
|
92
|
-
}
|
|
70
|
+
if (isVariableAlias(value)) {
|
|
71
|
+
const resolvedVariable = variableRepository.findVariableById(value.id);
|
|
93
72
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
return true;
|
|
73
|
+
if (!resolvedVariable) {
|
|
74
|
+
throw new Error(`Variable not found: ${value.id}`);
|
|
97
75
|
}
|
|
98
|
-
}
|
|
99
76
|
|
|
100
|
-
|
|
101
|
-
|
|
77
|
+
return resolveValue(resolvedVariable, mode);
|
|
78
|
+
}
|
|
102
79
|
|
|
103
|
-
|
|
104
|
-
function getVariableName(key: string) {
|
|
105
|
-
const slug = getFigmaVariableSlug(key);
|
|
106
|
-
return variableNameTransformer({ slug });
|
|
80
|
+
return value;
|
|
107
81
|
}
|
|
108
82
|
|
|
109
|
-
function
|
|
83
|
+
function infer(value: VariableValueResolved, scope: VariableScope) {
|
|
110
84
|
// NOTE: We assume that the variable is in the default mode or value is equal between all modes for simplicity.
|
|
111
85
|
const inferredVariables = variables.filter(
|
|
112
86
|
(variable) =>
|
|
113
87
|
isInsideScope(variable, scope) &&
|
|
114
|
-
isIdenticalVariableValue(
|
|
115
|
-
resolveVariableValue(variable.id, getDefaultModeId(variable)),
|
|
116
|
-
value,
|
|
117
|
-
),
|
|
118
|
-
);
|
|
119
|
-
|
|
120
|
-
const inferredVariableNames = inferredVariables.map((variable) =>
|
|
121
|
-
getVariableName(variable.key),
|
|
88
|
+
isIdenticalVariableValue(resolveValue(variable, getDefaultModeId(variable)), value),
|
|
122
89
|
);
|
|
123
90
|
|
|
124
|
-
const
|
|
91
|
+
const sortedVariables = inferredVariables.sort(inferCompareFunction);
|
|
125
92
|
|
|
126
|
-
return
|
|
93
|
+
return sortedVariables[0];
|
|
127
94
|
}
|
|
128
95
|
|
|
129
96
|
return {
|
|
130
|
-
|
|
131
|
-
|
|
97
|
+
getSlug,
|
|
98
|
+
resolveValue,
|
|
99
|
+
infer,
|
|
132
100
|
};
|
|
133
101
|
}
|