@seed-design/figma 0.0.24 → 0.0.27

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.
Files changed (49) hide show
  1. package/lib/codegen/index.cjs +32 -9
  2. package/lib/codegen/index.d.ts +19 -7
  3. package/lib/codegen/index.js +32 -9
  4. package/lib/codegen/targets/react/index.cjs +237 -129
  5. package/lib/codegen/targets/react/index.d.ts +9 -3
  6. package/lib/codegen/targets/react/index.js +237 -129
  7. package/package.json +3 -3
  8. package/src/codegen/core/codegen.ts +6 -3
  9. package/src/codegen/core/jsx.ts +38 -7
  10. package/src/codegen/targets/figma/shape.ts +3 -1
  11. package/src/codegen/targets/figma/text.ts +4 -7
  12. package/src/codegen/targets/react/component/handlers/action-button.ts +8 -5
  13. package/src/codegen/targets/react/component/handlers/action-chip.ts +9 -8
  14. package/src/codegen/targets/react/component/handlers/action-sheet.ts +17 -15
  15. package/src/codegen/targets/react/component/handlers/app-bar.ts +31 -24
  16. package/src/codegen/targets/react/component/handlers/avatar-stack.ts +6 -5
  17. package/src/codegen/targets/react/component/handlers/avatar.ts +12 -7
  18. package/src/codegen/targets/react/component/handlers/badge.ts +4 -3
  19. package/src/codegen/targets/react/component/handlers/callout.ts +9 -4
  20. package/src/codegen/targets/react/component/handlers/checkbox.ts +6 -3
  21. package/src/codegen/targets/react/component/handlers/chip-tabs.ts +8 -5
  22. package/src/codegen/targets/react/component/handlers/control-chip.ts +14 -8
  23. package/src/codegen/targets/react/component/handlers/error-state.ts +6 -3
  24. package/src/codegen/targets/react/component/handlers/extended-action-sheet.ts +22 -20
  25. package/src/codegen/targets/react/component/handlers/extended-fab.ts +5 -4
  26. package/src/codegen/targets/react/component/handlers/fab.ts +9 -5
  27. package/src/codegen/targets/react/component/handlers/help-bubble.ts +9 -5
  28. package/src/codegen/targets/react/component/handlers/identity-placeholder.ts +4 -4
  29. package/src/codegen/targets/react/component/handlers/inline-banner.ts +8 -3
  30. package/src/codegen/targets/react/component/handlers/manner-temp-badge.ts +6 -3
  31. package/src/codegen/targets/react/component/handlers/multiline-text-field.ts +7 -4
  32. package/src/codegen/targets/react/component/handlers/progress-circle.ts +6 -3
  33. package/src/codegen/targets/react/component/handlers/reaction-button.ts +7 -4
  34. package/src/codegen/targets/react/component/handlers/segmented-control.ts +15 -11
  35. package/src/codegen/targets/react/component/handlers/select-box.ts +17 -21
  36. package/src/codegen/targets/react/component/handlers/skeleton.ts +5 -4
  37. package/src/codegen/targets/react/component/handlers/snackbar.ts +6 -3
  38. package/src/codegen/targets/react/component/handlers/switch.ts +6 -3
  39. package/src/codegen/targets/react/component/handlers/tabs.ts +13 -10
  40. package/src/codegen/targets/react/component/handlers/text-button.ts +3 -2
  41. package/src/codegen/targets/react/component/handlers/text-field.ts +7 -4
  42. package/src/codegen/targets/react/component/handlers/toggle-button.ts +8 -5
  43. package/src/codegen/targets/react/element-factories.ts +59 -0
  44. package/src/codegen/targets/react/frame.ts +5 -9
  45. package/src/codegen/targets/react/icon.ts +7 -2
  46. package/src/codegen/targets/react/instance.ts +2 -2
  47. package/src/codegen/targets/react/props.ts +13 -13
  48. package/src/codegen/targets/react/shape.ts +11 -4
  49. package/src/codegen/targets/react/text.ts +6 -8
@@ -40,13 +40,13 @@ function objectEntries(obj) {
40
40
  return Object.entries(obj);
41
41
  }
42
42
 
43
- function createElement(tag, props = {}, children, comment) {
43
+ function createElement(tag, props = {}, children, meta) {
44
44
  return {
45
45
  __IS_JSX_ELEMENT_NODE: true,
46
46
  tag,
47
47
  props,
48
48
  children: ensureArray(children).filter(exists),
49
- comment
49
+ meta: meta ?? {}
50
50
  };
51
51
  }
52
52
  function cloneElement(element, props = {}, children) {
@@ -69,14 +69,25 @@ function isElement(node) {
69
69
  return typeof node === "object" && node != null && "__IS_JSX_ELEMENT_NODE" in node && node.__IS_JSX_ELEMENT_NODE === true;
70
70
  }
71
71
  function stringifyElement(element, options = {}) {
72
+ const importMap = new Map();
72
73
  function recursive(node, depth) {
73
74
  if (typeof node === "string") {
74
75
  return node;
75
76
  }
76
- const { tag, props, children, comment } = node;
77
+ const { tag, props, children, meta: { comment, source, importPath } } = node;
78
+ if (importPath) {
79
+ const existing = importMap.get(importPath);
80
+ if (existing) {
81
+ existing.add(tag);
82
+ } else {
83
+ importMap.set(importPath, new Set([
84
+ tag
85
+ ]));
86
+ }
87
+ }
77
88
  const propEntries = Object.entries(options.printSource ? {
78
89
  ...props,
79
- "data-figma-node-id": node.source
90
+ "data-figma-node-id": source
80
91
  } : props);
81
92
  const propFragments = propEntries.map(([key, value])=>{
82
93
  if (typeof value === "string") {
@@ -113,7 +124,12 @@ function stringifyElement(element, options = {}) {
113
124
  ].join("\n");
114
125
  return result;
115
126
  }
116
- return recursive(element, 0);
127
+ const jsx = recursive(element, 0);
128
+ const imports = Array.from(importMap.entries()).sort((a, b)=>a[0].localeCompare(b[0])).map(([importPath, tags])=>`import { ${Array.from(tags).join(", ")} } from "${importPath}";`).join("\n");
129
+ return {
130
+ imports,
131
+ jsx
132
+ };
117
133
  }
118
134
 
119
135
  // --- Helper Functions ---
@@ -456,9 +472,12 @@ function createCodeGenerator({ frameTransformer, textTransformer, rectangleTrans
456
472
  }
457
473
  function generateCode(node, options) {
458
474
  const jsxTree = generateJsxTree(node);
459
- return jsxTree ? stringifyElement(jsxTree, {
475
+ if (!jsxTree) {
476
+ return undefined;
477
+ }
478
+ return stringifyElement(jsxTree, {
460
479
  printSource: options.shouldPrintSource
461
- }) : undefined;
480
+ });
462
481
  }
463
482
  return {
464
483
  generateJsxTree,
@@ -7879,7 +7898,9 @@ function createVectorTransformer({ propsConverters }) {
7879
7898
  ...propsConverters.radius(node),
7880
7899
  ...propsConverters.stroke(node),
7881
7900
  ...propsConverters.shapeFill(node)
7882
- }, [], "Vector Node Placeholder");
7901
+ }, [], {
7902
+ comment: "Vector Node Placeholder"
7903
+ });
7883
7904
  });
7884
7905
  }
7885
7906
  function createBooleanOperationTransformer({ propsConverters }) {
@@ -7902,7 +7923,9 @@ function createTextTransformer({ propsConverters }) {
7902
7923
  ...typeStyleProps,
7903
7924
  ...fillProps
7904
7925
  });
7905
- return createElement("Text", props, node.characters, hasMultipleFills ? "Multiple fills in Text node encountered, only the first fill is used." : "");
7926
+ return createElement("Text", props, node.characters, {
7927
+ comment: hasMultipleFills ? "Multiple fills in Text node encountered, only the first fill is used." : undefined
7928
+ });
7906
7929
  });
7907
7930
  }
7908
7931
 
@@ -77,10 +77,13 @@ interface ElementNode {
77
77
  tag: string;
78
78
  props: Record<string, string | number | boolean | ElementNode | object | undefined>;
79
79
  children: (ElementNode | string)[];
80
- comment?: string;
81
- source?: string;
80
+ meta: {
81
+ comment?: string;
82
+ source?: string;
83
+ importPath?: string;
84
+ };
82
85
  }
83
- declare function createElement(tag: string, props?: Record<string, string | number | boolean | object | undefined>, children?: ElementNode | string | undefined | (ElementNode | string | undefined)[], comment?: string): ElementNode;
86
+ declare function createElement(tag: string, props?: Record<string, string | number | boolean | object | undefined>, children?: ElementNode | string | undefined | (ElementNode | string | undefined)[], meta?: ElementNode["meta"]): ElementNode;
84
87
  declare function cloneElement(element: ElementNode, props?: Record<string, string | number | boolean | object | undefined>, children?: ElementNode | string | undefined | (ElementNode | string | undefined)[]): {
85
88
  props: {
86
89
  [x: string]: string | number | boolean | object | ElementNode | undefined;
@@ -88,8 +91,11 @@ declare function cloneElement(element: ElementNode, props?: Record<string, strin
88
91
  children: (string | ElementNode)[];
89
92
  __IS_JSX_ELEMENT_NODE: true;
90
93
  tag: string;
91
- comment?: string;
92
- source?: string;
94
+ meta: {
95
+ comment?: string;
96
+ source?: string;
97
+ importPath?: string;
98
+ };
93
99
  };
94
100
 
95
101
  type ElementTransformer<T extends NormalizedSceneNode> = (node: T, traverse: (node: NormalizedSceneNode) => ElementNode | undefined) => ElementNode | undefined;
@@ -108,7 +114,10 @@ declare function createCodeGenerator({ frameTransformer, textTransformer, rectan
108
114
  generateJsxTree: (node: NormalizedSceneNode) => ElementNode | undefined;
109
115
  generateCode: (node: NormalizedSceneNode, options: {
110
116
  shouldPrintSource: boolean;
111
- }) => string | undefined;
117
+ }) => {
118
+ imports: string;
119
+ jsx: string;
120
+ } | undefined;
112
121
  };
113
122
 
114
123
  interface ComponentHandler<T extends NormalizedInstanceNode["componentProperties"] = NormalizedInstanceNode["componentProperties"]> {
@@ -1680,7 +1689,10 @@ declare function createPipeline(options?: CreatePipelineConfig): {
1680
1689
  generateJsxTree: (node: ________.NormalizedSceneNode) => ElementNode | undefined;
1681
1690
  generateCode: (node: ________.NormalizedSceneNode, options: {
1682
1691
  shouldPrintSource: boolean;
1683
- }) => string | undefined;
1692
+ }) => {
1693
+ imports: string;
1694
+ jsx: string;
1695
+ } | undefined;
1684
1696
  };
1685
1697
 
1686
1698
  type FigmaValueResolver = ValueResolver<string, number, number, number>;
@@ -21,13 +21,13 @@ function objectEntries(obj) {
21
21
  return Object.entries(obj);
22
22
  }
23
23
 
24
- function createElement(tag, props = {}, children, comment) {
24
+ function createElement(tag, props = {}, children, meta) {
25
25
  return {
26
26
  __IS_JSX_ELEMENT_NODE: true,
27
27
  tag,
28
28
  props,
29
29
  children: ensureArray(children).filter(exists),
30
- comment
30
+ meta: meta ?? {}
31
31
  };
32
32
  }
33
33
  function cloneElement(element, props = {}, children) {
@@ -50,14 +50,25 @@ function isElement(node) {
50
50
  return typeof node === "object" && node != null && "__IS_JSX_ELEMENT_NODE" in node && node.__IS_JSX_ELEMENT_NODE === true;
51
51
  }
52
52
  function stringifyElement(element, options = {}) {
53
+ const importMap = new Map();
53
54
  function recursive(node, depth) {
54
55
  if (typeof node === "string") {
55
56
  return node;
56
57
  }
57
- const { tag, props, children, comment } = node;
58
+ const { tag, props, children, meta: { comment, source, importPath } } = node;
59
+ if (importPath) {
60
+ const existing = importMap.get(importPath);
61
+ if (existing) {
62
+ existing.add(tag);
63
+ } else {
64
+ importMap.set(importPath, new Set([
65
+ tag
66
+ ]));
67
+ }
68
+ }
58
69
  const propEntries = Object.entries(options.printSource ? {
59
70
  ...props,
60
- "data-figma-node-id": node.source
71
+ "data-figma-node-id": source
61
72
  } : props);
62
73
  const propFragments = propEntries.map(([key, value])=>{
63
74
  if (typeof value === "string") {
@@ -94,7 +105,12 @@ function stringifyElement(element, options = {}) {
94
105
  ].join("\n");
95
106
  return result;
96
107
  }
97
- return recursive(element, 0);
108
+ const jsx = recursive(element, 0);
109
+ const imports = Array.from(importMap.entries()).sort((a, b)=>a[0].localeCompare(b[0])).map(([importPath, tags])=>`import { ${Array.from(tags).join(", ")} } from "${importPath}";`).join("\n");
110
+ return {
111
+ imports,
112
+ jsx
113
+ };
98
114
  }
99
115
 
100
116
  // --- Helper Functions ---
@@ -437,9 +453,12 @@ function createCodeGenerator({ frameTransformer, textTransformer, rectangleTrans
437
453
  }
438
454
  function generateCode(node, options) {
439
455
  const jsxTree = generateJsxTree(node);
440
- return jsxTree ? stringifyElement(jsxTree, {
456
+ if (!jsxTree) {
457
+ return undefined;
458
+ }
459
+ return stringifyElement(jsxTree, {
441
460
  printSource: options.shouldPrintSource
442
- }) : undefined;
461
+ });
443
462
  }
444
463
  return {
445
464
  generateJsxTree,
@@ -7860,7 +7879,9 @@ function createVectorTransformer({ propsConverters }) {
7860
7879
  ...propsConverters.radius(node),
7861
7880
  ...propsConverters.stroke(node),
7862
7881
  ...propsConverters.shapeFill(node)
7863
- }, [], "Vector Node Placeholder");
7882
+ }, [], {
7883
+ comment: "Vector Node Placeholder"
7884
+ });
7864
7885
  });
7865
7886
  }
7866
7887
  function createBooleanOperationTransformer({ propsConverters }) {
@@ -7883,7 +7904,9 @@ function createTextTransformer({ propsConverters }) {
7883
7904
  ...typeStyleProps,
7884
7905
  ...fillProps
7885
7906
  });
7886
- return createElement("Text", props, node.characters, hasMultipleFills ? "Multiple fills in Text node encountered, only the first fill is used." : "");
7907
+ return createElement("Text", props, node.characters, {
7908
+ comment: hasMultipleFills ? "Multiple fills in Text node encountered, only the first fill is used." : undefined
7909
+ });
7887
7910
  });
7888
7911
  }
7889
7912