@developer_tribe/react-builder 1.0.2 → 1.0.3

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 (124) hide show
  1. package/dist/AttributesEditor.d.ts +3 -1
  2. package/dist/RenderPage.d.ts +2 -1
  3. package/dist/attributes-editor/Field.d.ts +2 -1
  4. package/dist/attributes-editor/SpecialCategorySection.d.ts +2 -1
  5. package/dist/build-components/BackgroundImage/BackgroundImage.d.ts +5 -0
  6. package/dist/build-components/BackgroundImage/BackgroundImageProps.generated.d.ts +44 -0
  7. package/dist/build-components/Button/ButtonProps.generated.d.ts +7 -0
  8. package/dist/build-components/Carousel/CarouselProps.generated.d.ts +7 -0
  9. package/dist/build-components/CarouselButtons/CarouselButtonsProps.generated.d.ts +7 -0
  10. package/dist/build-components/CarouselDots/CarouselDotsProps.generated.d.ts +7 -0
  11. package/dist/build-components/CarouselItem/CarouselItemProps.generated.d.ts +7 -0
  12. package/dist/build-components/CarouselProvider/CarouselProviderProps.generated.d.ts +7 -0
  13. package/dist/build-components/Image/ImageProps.generated.d.ts +7 -0
  14. package/dist/build-components/Onboard/OnboardProps.generated.d.ts +7 -0
  15. package/dist/build-components/OnboardButton/OnboardButtonProps.generated.d.ts +7 -0
  16. package/dist/build-components/OnboardButtons/OnboardButtonsProps.generated.d.ts +7 -0
  17. package/dist/build-components/OnboardDot/OnboardDotProps.generated.d.ts +7 -0
  18. package/dist/build-components/OnboardFooter/OnboardFooterProps.generated.d.ts +7 -0
  19. package/dist/build-components/OnboardImage/OnboardImageProps.generated.d.ts +7 -0
  20. package/dist/build-components/OnboardItem/OnboardItemProps.generated.d.ts +7 -0
  21. package/dist/build-components/OnboardProvider/OnboardProviderProps.generated.d.ts +7 -1
  22. package/dist/build-components/OnboardSubtitle/OnboardSubtitleProps.generated.d.ts +7 -0
  23. package/dist/build-components/OnboardTitle/OnboardTitleProps.generated.d.ts +7 -0
  24. package/dist/build-components/Text/TextProps.generated.d.ts +7 -0
  25. package/dist/build-components/View/ViewProps.generated.d.ts +7 -0
  26. package/dist/build-components/index.d.ts +2 -1
  27. package/dist/build-components/patterns.generated.d.ts +1444 -15
  28. package/dist/components/AttributesEditorPanel.d.ts +3 -4
  29. package/dist/components/Builder.d.ts +2 -1
  30. package/dist/components/BuilderButton.d.ts +9 -0
  31. package/dist/index.cjs.js +5 -5
  32. package/dist/index.cjs.js.map +1 -1
  33. package/dist/index.d.ts +2 -2
  34. package/dist/index.esm.js +5 -5
  35. package/dist/index.esm.js.map +1 -1
  36. package/dist/modals/ColorModal.d.ts +3 -1
  37. package/dist/pages/ProjectPage.d.ts +3 -3
  38. package/dist/pages/tabs/BuilderPanel.d.ts +8 -0
  39. package/dist/pages/tabs/{DebugTab.d.ts → SideTool.d.ts} +2 -2
  40. package/dist/store.d.ts +7 -1
  41. package/dist/styles.css +1 -1
  42. package/dist/types/Project.d.ts +11 -0
  43. package/dist/utils/analyseNode.d.ts +1 -0
  44. package/dist/utils/extractTextStyle.d.ts +8 -1
  45. package/dist/utils/extractViewStyle.d.ts +8 -1
  46. package/dist/utils/parseColor.d.ts +7 -0
  47. package/package.json +1 -1
  48. package/src/AttributesEditor.tsx +76 -14
  49. package/src/RenderPage.tsx +82 -4
  50. package/src/attributes-editor/Field.tsx +12 -5
  51. package/src/attributes-editor/SpecialCategorySection.tsx +2 -1
  52. package/src/build-components/BackgroundImage/BackgroundImage.tsx +87 -0
  53. package/src/build-components/BackgroundImage/BackgroundImageProps.generated.ts +60 -0
  54. package/src/build-components/BackgroundImage/pattern.json +45 -0
  55. package/src/build-components/Button/Button.tsx +31 -4
  56. package/src/build-components/Button/ButtonProps.generated.ts +7 -0
  57. package/src/build-components/Carousel/Carousel.tsx +27 -3
  58. package/src/build-components/Carousel/CarouselProps.generated.ts +7 -0
  59. package/src/build-components/CarouselButtons/CarouselButtons.tsx +19 -4
  60. package/src/build-components/CarouselButtons/CarouselButtonsProps.generated.ts +7 -0
  61. package/src/build-components/CarouselDots/CarouselDots.tsx +13 -4
  62. package/src/build-components/CarouselDots/CarouselDotsProps.generated.ts +7 -0
  63. package/src/build-components/CarouselItem/CarouselItem.tsx +20 -4
  64. package/src/build-components/CarouselItem/CarouselItemProps.generated.ts +7 -0
  65. package/src/build-components/CarouselProvider/CarouselProvider.tsx +14 -3
  66. package/src/build-components/CarouselProvider/CarouselProviderProps.generated.ts +7 -0
  67. package/src/build-components/Image/Image.tsx +29 -4
  68. package/src/build-components/Image/ImageProps.generated.ts +7 -0
  69. package/src/build-components/Onboard/Onboard.tsx +2 -2
  70. package/src/build-components/Onboard/OnboardProps.generated.ts +7 -0
  71. package/src/build-components/OnboardButton/OnboardButton.tsx +11 -4
  72. package/src/build-components/OnboardButton/OnboardButtonProps.generated.ts +7 -0
  73. package/src/build-components/OnboardButtons/OnboardButtons.tsx +17 -5
  74. package/src/build-components/OnboardButtons/OnboardButtonsProps.generated.ts +7 -0
  75. package/src/build-components/OnboardDot/OnboardDot.tsx +15 -6
  76. package/src/build-components/OnboardDot/OnboardDotProps.generated.ts +7 -0
  77. package/src/build-components/OnboardDot/pattern.json +1 -1
  78. package/src/build-components/OnboardFooter/OnboardFooter.tsx +15 -5
  79. package/src/build-components/OnboardFooter/OnboardFooterProps.generated.ts +7 -0
  80. package/src/build-components/OnboardImage/OnboardImage.tsx +28 -6
  81. package/src/build-components/OnboardImage/OnboardImageProps.generated.ts +7 -0
  82. package/src/build-components/OnboardItem/OnboardItem.tsx +14 -3
  83. package/src/build-components/OnboardItem/OnboardItemProps.generated.ts +7 -0
  84. package/src/build-components/OnboardProvider/OnboardProvider.tsx +34 -12
  85. package/src/build-components/OnboardProvider/OnboardProviderProps.generated.ts +7 -1
  86. package/src/build-components/OnboardProvider/pattern.json +0 -8
  87. package/src/build-components/OnboardSubtitle/OnboardSubtitleProps.generated.ts +7 -0
  88. package/src/build-components/OnboardSubtitle/pattern.json +1 -1
  89. package/src/build-components/OnboardTitle/OnboardTitleProps.generated.ts +7 -0
  90. package/src/build-components/OnboardTitle/pattern.json +1 -1
  91. package/src/build-components/RenderNode.generated.tsx +3 -0
  92. package/src/build-components/Text/Text.tsx +33 -9
  93. package/src/build-components/Text/TextProps.generated.ts +7 -0
  94. package/src/build-components/View/View.tsx +27 -3
  95. package/src/build-components/View/ViewProps.generated.ts +7 -0
  96. package/src/build-components/View/pattern.json +59 -1
  97. package/src/build-components/index.ts +5 -0
  98. package/src/build-components/patterns.generated.ts +1452 -15
  99. package/src/components/AttributesEditorPanel.tsx +13 -6
  100. package/src/components/Builder.tsx +140 -40
  101. package/src/components/BuilderButton.tsx +127 -0
  102. package/src/index.ts +2 -2
  103. package/src/mockOS/components/MockOSRouter.tsx +11 -3
  104. package/src/modals/ColorModal.tsx +212 -55
  105. package/src/pages/ProjectPage.tsx +293 -55
  106. package/src/pages/tabs/{BuilderTab.tsx → BuilderPanel.tsx} +13 -9
  107. package/src/pages/tabs/SideTool.tsx +259 -0
  108. package/src/size-matters/index.ts +6 -0
  109. package/src/store.ts +13 -1
  110. package/src/styles/base/_global.scss +158 -7
  111. package/src/styles/components/_attributes-editor.scss +12 -0
  112. package/src/styles/components/_editor-shell.scss +23 -0
  113. package/src/styles/foundation/_sizes.scss +1 -1
  114. package/src/styles/layout/_builder.scss +66 -10
  115. package/src/styles/modals/_color-modal.scss +29 -0
  116. package/src/types/Project.ts +14 -0
  117. package/src/utils/analyseNode.ts +98 -0
  118. package/src/utils/extractTextStyle.ts +24 -8
  119. package/src/utils/extractViewStyle.ts +27 -3
  120. package/src/utils/parseColor.ts +43 -0
  121. package/dist/pages/tabs/BuilderTab.d.ts +0 -9
  122. package/dist/pages/tabs/PreviewTab.d.ts +0 -3
  123. package/src/pages/tabs/DebugTab.tsx +0 -64
  124. package/src/pages/tabs/PreviewTab.tsx +0 -206
@@ -1,32 +1,64 @@
1
- import React, { useMemo, useRef, useState } from 'react';
1
+ import React, { useId, useMemo, useRef, useState } from 'react';
2
+ import type { ProjectColorTokenMap, ProjectColors } from '../types/Project';
2
3
  import Modal from './Modal';
3
4
 
4
5
  const SAVED_COLORS_KEY = 'attributes-editor-saved-colors';
5
6
 
6
- const POPULAR_COLORS: Array<{ label: string; value: string }> = [
7
- { label: 'White', value: '#FFFFFF' },
8
- { label: 'Black', value: '#000000' },
9
- { label: 'Text Gray', value: '#1F1F1F' },
10
- { label: 'Muted Text', value: '#4A4A4A' },
11
- { label: 'Background', value: '#F5F5F5' },
12
- { label: 'Primary Blue', value: '#0A84FF' },
13
- { label: 'Success Green', value: '#34C759' },
14
- { label: 'Accent Purple', value: '#AF52DE' },
15
- ];
16
-
17
7
  type ColorOption = {
8
+ id: string;
18
9
  label?: string;
19
10
  value: string;
11
+ tokenLabel?: string;
12
+ };
13
+
14
+ type ColorGroup = {
15
+ title: string;
16
+ options: ColorOption[];
17
+ emptyMessage?: string;
20
18
  };
21
19
 
22
20
  type ColorModalProps = {
23
21
  value?: string;
24
- projectColors?: string[];
22
+ projectColors?: ProjectColors;
25
23
  onSelect: (color: string) => void;
26
24
  onClose: () => void;
27
25
  onClear: () => void;
28
26
  };
29
27
 
28
+ const formatTokenLabel = (token: string) =>
29
+ token
30
+ .split(/[_-]/g)
31
+ .filter(Boolean)
32
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())
33
+ .join(' ');
34
+
35
+ type MapTokensOptions = {
36
+ groupKey?: string;
37
+ tokenLabelPrefix?: string;
38
+ };
39
+
40
+ const mapTokensToOptions = (
41
+ tokenMap?: ProjectColorTokenMap,
42
+ options?: MapTokensOptions,
43
+ ): ColorOption[] => {
44
+ if (!tokenMap) return [];
45
+ const prefix = options?.groupKey ?? 'group';
46
+ return Object.entries(tokenMap)
47
+ .filter(([, value]) => typeof value === 'string' && value.trim().length > 0)
48
+ .map(([token, value]) => {
49
+ const trimmedValue = value.trim();
50
+ const tokenLabel = options?.tokenLabelPrefix
51
+ ? `${options.tokenLabelPrefix}.${token}`
52
+ : undefined;
53
+ return {
54
+ id: `${prefix}-${token}`,
55
+ label: formatTokenLabel(token),
56
+ value: trimmedValue,
57
+ tokenLabel,
58
+ };
59
+ });
60
+ };
61
+
30
62
  const readSavedColors = (): string[] => {
31
63
  if (typeof window === 'undefined') return [];
32
64
  try {
@@ -48,9 +80,59 @@ const persistSavedColors = (colors: string[]) => {
48
80
  }
49
81
  };
50
82
 
83
+ const STATIC_PREFIX = 'STATIC_COLORS.';
84
+ const THEME_PREFIX = 'THEME_COLORS.';
85
+
86
+ export const resolveProjectColorValue = (
87
+ candidate?: string,
88
+ projectColors?: ProjectColors,
89
+ ): string | undefined => {
90
+ if (!candidate || typeof candidate !== 'string' || !projectColors) {
91
+ return undefined;
92
+ }
93
+ const trimmedCandidate = candidate.trim();
94
+ if (!trimmedCandidate) return undefined;
95
+
96
+ if (trimmedCandidate.startsWith(STATIC_PREFIX)) {
97
+ const token = trimmedCandidate.slice(STATIC_PREFIX.length);
98
+ const resolved = projectColors.STATIC_COLORS?.[token];
99
+ return typeof resolved === 'string' && resolved.trim()
100
+ ? resolved.trim()
101
+ : undefined;
102
+ }
103
+
104
+ if (trimmedCandidate.startsWith(THEME_PREFIX)) {
105
+ const remainder = trimmedCandidate.slice(THEME_PREFIX.length);
106
+ if (!remainder) return undefined;
107
+ const themeParts = remainder.split('.');
108
+ if (themeParts.length === 1) {
109
+ const token = themeParts[0];
110
+ const themes = projectColors.THEME_COLORS;
111
+ if (!themes) return undefined;
112
+ for (const themeTokens of Object.values(themes)) {
113
+ const resolved = themeTokens?.[token];
114
+ if (typeof resolved === 'string' && resolved.trim()) {
115
+ return resolved.trim();
116
+ }
117
+ }
118
+ return undefined;
119
+ }
120
+ const [themeName, ...tokenSegments] = themeParts;
121
+ const token = tokenSegments.join('.');
122
+ if (!themeName || !token) return undefined;
123
+ const themeTokens = projectColors.THEME_COLORS?.[themeName];
124
+ const resolved = themeTokens?.[token];
125
+ return typeof resolved === 'string' && resolved.trim()
126
+ ? resolved.trim()
127
+ : undefined;
128
+ }
129
+
130
+ return undefined;
131
+ };
132
+
51
133
  export function ColorModal({
52
134
  value,
53
- projectColors = [],
135
+ projectColors,
54
136
  onSelect,
55
137
  onClose,
56
138
  onClear,
@@ -58,29 +140,58 @@ export function ColorModal({
58
140
  const [savedColors, setSavedColors] = useState<string[]>(() =>
59
141
  readSavedColors(),
60
142
  );
143
+ const [useColorNames, setUseColorNames] = useState(true);
61
144
  const colorInputRef = useRef<HTMLInputElement | null>(null);
145
+ const colorNameToggleId = useId();
146
+
147
+ const selectedColorPreview = useMemo(
148
+ () => resolveProjectColorValue(value, projectColors) ?? value,
149
+ [value, projectColors],
150
+ );
62
151
 
63
- const uniqueProjectColors = useMemo(() => {
64
- const seen = new Set<string>();
65
- return projectColors
66
- .map((color) => color?.trim())
67
- .filter(
68
- (color): color is string =>
69
- Boolean(color) && !seen.has(color!.toLowerCase()),
70
- )
71
- .map((color) => {
72
- seen.add(color.toLowerCase());
73
- return color;
152
+ const projectColorGroups = useMemo<ColorGroup[]>(() => {
153
+ if (!projectColors) return [];
154
+ const groups: ColorGroup[] = [];
155
+
156
+ const staticOptions = mapTokensToOptions(projectColors.STATIC_COLORS, {
157
+ groupKey: 'static',
158
+ tokenLabelPrefix: 'STATIC_COLORS',
159
+ });
160
+ if (staticOptions.length) {
161
+ groups.push({
162
+ title: 'Static colors',
163
+ options: staticOptions,
164
+ emptyMessage: 'No static colors defined.',
74
165
  });
75
- }, [projectColors]);
166
+ }
76
167
 
77
- const projectColorOptions = useMemo<ColorOption[]>(
78
- () => uniqueProjectColors.map((color) => ({ value: color })),
79
- [uniqueProjectColors],
80
- );
168
+ const themeColors = projectColors.THEME_COLORS ?? {};
169
+ Object.entries(themeColors).forEach(([themeName, themeTokens]) => {
170
+ const options = mapTokensToOptions(themeTokens, {
171
+ groupKey: `theme-${themeName || 'default'}`,
172
+ tokenLabelPrefix: 'THEME_COLORS',
173
+ });
174
+ if (!options.length) return;
175
+ const normalizedThemeName = themeName?.trim()
176
+ ? formatTokenLabel(themeName)
177
+ : 'Theme';
178
+ groups.push({
179
+ title: `Theme: ${normalizedThemeName}`,
180
+ options,
181
+ emptyMessage: `No colors defined for ${normalizedThemeName}.`,
182
+ });
183
+ });
184
+
185
+ return groups;
186
+ }, [projectColors]);
81
187
 
82
188
  const savedColorOptions = useMemo<ColorOption[]>(
83
- () => savedColors.map((color) => ({ value: color })),
189
+ () =>
190
+ savedColors.map((color, index) => ({
191
+ id: `saved-${index}-${color}`,
192
+ label: color,
193
+ value: color,
194
+ })),
84
195
  [savedColors],
85
196
  );
86
197
 
@@ -88,6 +199,14 @@ export function ColorModal({
88
199
  colorInputRef.current?.click();
89
200
  };
90
201
 
202
+ const handlePreviewKeyDown = (
203
+ event: React.KeyboardEvent<HTMLSpanElement>,
204
+ ) => {
205
+ if (event.key !== 'Enter' && event.key !== ' ') return;
206
+ event.preventDefault();
207
+ handleAddColorClick();
208
+ };
209
+
91
210
  const handleColorPicked = (event: React.ChangeEvent<HTMLInputElement>) => {
92
211
  const picked = event.target.value;
93
212
  if (!picked) return;
@@ -102,8 +221,10 @@ export function ColorModal({
102
221
  event.target.value = '';
103
222
  };
104
223
 
105
- const handleSelectColor = (hex: string) => {
106
- onSelect(hex);
224
+ const handleSelectColor = (option: ColorOption) => {
225
+ const nextValue =
226
+ useColorNames && option.tokenLabel ? option.tokenLabel : option.value;
227
+ onSelect(nextValue);
107
228
  onClose();
108
229
  };
109
230
 
@@ -125,9 +246,14 @@ export function ColorModal({
125
246
  <div className="color-modal__selected">
126
247
  <div className="color-modal__selected-info">
127
248
  <span
128
- aria-hidden
249
+ role="button"
250
+ aria-label="Open color picker"
251
+ tabIndex={0}
252
+ title="Pick a color"
129
253
  className="color-modal__selected-preview"
130
- style={{ background: value ?? 'transparent' }}
254
+ style={{ background: selectedColorPreview ?? 'transparent' }}
255
+ onClick={handleAddColorClick}
256
+ onKeyDown={handlePreviewKeyDown}
131
257
  />
132
258
  <div>
133
259
  <p className="color-modal__selected-label">Selected color</p>
@@ -145,13 +271,45 @@ export function ColorModal({
145
271
  ) : null}
146
272
  </div>
147
273
 
148
- <ColorSection
149
- title="Project colors"
150
- options={projectColorOptions}
151
- emptyMessage="No project colors detected yet."
152
- activeValue={value}
153
- onSelect={handleSelectColor}
154
- />
274
+ <div className="color-modal__toggle">
275
+ <label
276
+ htmlFor={colorNameToggleId}
277
+ className="color-modal__toggle-label"
278
+ >
279
+ <input
280
+ id={colorNameToggleId}
281
+ type="checkbox"
282
+ className="color-modal__toggle-input"
283
+ checked={useColorNames}
284
+ onChange={(event) => setUseColorNames(event.target.checked)}
285
+ />
286
+ Use color names
287
+ </label>
288
+ <p className="color-modal__toggle-description">
289
+ Output tokens like STATIC_COLORS.PRIMARY instead of raw values.
290
+ </p>
291
+ </div>
292
+
293
+ {projectColorGroups.length ? (
294
+ projectColorGroups.map((group) => (
295
+ <ColorSection
296
+ key={group.title}
297
+ title={group.title}
298
+ options={group.options}
299
+ emptyMessage={group.emptyMessage}
300
+ activeValue={value}
301
+ onSelect={handleSelectColor}
302
+ />
303
+ ))
304
+ ) : (
305
+ <ColorSection
306
+ title="Project colors"
307
+ options={[]}
308
+ emptyMessage="No project colors detected yet."
309
+ activeValue={value}
310
+ onSelect={handleSelectColor}
311
+ />
312
+ )}
155
313
 
156
314
  <ColorSection
157
315
  title="Saved colors"
@@ -170,14 +328,6 @@ export function ColorModal({
170
328
  onSelect={handleSelectColor}
171
329
  />
172
330
 
173
- <ColorSection
174
- title="Popular colors"
175
- options={POPULAR_COLORS}
176
- emptyMessage="Popular palettes unavailable."
177
- activeValue={value}
178
- onSelect={handleSelectColor}
179
- />
180
-
181
331
  <input
182
332
  ref={colorInputRef}
183
333
  type="color"
@@ -194,7 +344,7 @@ type ColorSectionProps = {
194
344
  emptyMessage?: string;
195
345
  action?: React.ReactNode;
196
346
  activeValue?: string;
197
- onSelect: (value: string) => void;
347
+ onSelect: (option: ColorOption) => void;
198
348
  };
199
349
 
200
350
  function ColorSection({
@@ -215,10 +365,13 @@ function ColorSection({
215
365
  <div className="color-section__swatches">
216
366
  {options.map((option) => (
217
367
  <ColorSwatch
218
- key={`${title}-${option.value}`}
368
+ key={option.id}
219
369
  option={option}
220
- isActive={option.value === activeValue}
221
- onSelect={() => onSelect(option.value)}
370
+ isActive={
371
+ (option.tokenLabel && option.tokenLabel === activeValue) ||
372
+ option.value === activeValue
373
+ }
374
+ onSelect={() => onSelect(option)}
222
375
  />
223
376
  ))}
224
377
  </div>
@@ -260,7 +413,11 @@ function ColorSwatch({ option, isActive, onSelect }: ColorSwatchProps) {
260
413
  <span className="color-modal__swatch-label">
261
414
  {option.label ?? option.value}
262
415
  </span>
263
- <span className="color-modal__swatch-value">{option.value}</span>
416
+ <span className="color-modal__swatch-value">
417
+ {option.tokenLabel
418
+ ? `${option.tokenLabel} - ${option.value}`
419
+ : option.value}
420
+ </span>
264
421
  </button>
265
422
  );
266
423
  }