@explorer02/cfm-survey-sdk 0.2.9 → 0.3.1

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 (47) hide show
  1. package/dist/cli/index.js +41 -38
  2. package/dist/cli/index.mjs +39 -36
  3. package/package.json +1 -1
  4. package/templates/docs/00-integration/agent-execution-flow.md +6 -6
  5. package/templates/docs/00-integration/agent-operating-contract.md +1 -2
  6. package/templates/docs/00-integration/client-lib-folder.md +1 -0
  7. package/templates/docs/00-integration/component-checklist.md +0 -1
  8. package/templates/docs/00-integration/ui-customization-wizard.md +38 -29
  9. package/templates/docs/00-integration/wizard-chrome-contract.md +3 -12
  10. package/templates/docs/00-integration/wizard-config-handoff.md +19 -11
  11. package/templates/docs/00-integration/wizard-post-customize-build.md +0 -6
  12. package/templates/docs/00-integration/wizard-preview-build-guide.md +17 -0
  13. package/templates/docs/00-integration/wizard-question-type-styling.md +3 -3
  14. package/templates/docs/00-integration/wizard-troubleshooting.md +4 -20
  15. package/templates/docs/01-components/03-rating-scale.md +8 -3
  16. package/templates/docs/02-reference/value-derivation.md +2 -0
  17. package/templates/docs/03-ui-specs/01-rating.md +15 -1
  18. package/templates/docs/index.md +1 -1
  19. package/templates/docs/templates/CsatMatrixScale.tsx +59 -53
  20. package/templates/docs/templates/CustomSliderTrack.tsx +8 -17
  21. package/templates/docs/templates/FileUploadScale.tsx +4 -12
  22. package/templates/docs/templates/Header.tsx +49 -32
  23. package/templates/docs/templates/HeatmapScale.tsx +2 -5
  24. package/templates/docs/templates/LikertMatrixScale.tsx +45 -57
  25. package/templates/docs/templates/MatrixDropdown.tsx +11 -12
  26. package/templates/docs/templates/Question.tsx +24 -4
  27. package/templates/docs/templates/RankOrderScale.tsx +9 -42
  28. package/templates/docs/templates/RatingScale.tsx +73 -75
  29. package/templates/docs/templates/SliderMatrixScale.tsx +3 -4
  30. package/templates/docs/templates/selectionStyles.ts +3 -2
  31. package/templates/docs/templates/surveyUiScaleUtils.ts +15 -1
  32. package/templates/docs/templates/verify-agent-build.sh +0 -17
  33. package/templates/preview-harness/preview-bridge.inline.js +61 -14
  34. package/templates/preview-harness/previewPages.js +84 -48
  35. package/templates/preview-harness/previewPages.ts +7 -5
  36. package/templates/preview-harness/vite-app/src/QuestionPreview.tsx +22 -14
  37. package/templates/preview-harness/vite-app/src/fixtures/questions.ts +13 -2
  38. package/templates/preview-harness/vite-app/src/preview-live-overrides.css +6 -11
  39. package/templates/previewBridge.ts +38 -5
  40. package/templates/survey-theme.css +17 -17
  41. package/templates/wizard-dist/assets/{PreviewMock-CysRws9G.js → PreviewMock-Bax7oRAL.js} +1 -1
  42. package/templates/wizard-dist/assets/{TypePanel-DWX1LL3q.js → TypePanel-D2t4FPSd.js} +1 -1
  43. package/templates/wizard-dist/assets/index-BhWM50Yu.css +1 -0
  44. package/templates/wizard-dist/assets/index-c5lka74l.js +34 -0
  45. package/templates/wizard-dist/index.html +2 -2
  46. package/templates/wizard-dist/assets/index-DIuc7AU2.js +0 -34
  47. package/templates/wizard-dist/assets/index-pCM8n0FT.css +0 -1
@@ -1,19 +1,21 @@
1
1
  import React, { useState } from 'react';
2
2
  import type { CsatQuestion, RatingMatrixQuestion, MatrixRowAnswers, ScaleColumn } from '@explorer02/cfm-survey-sdk';
3
- import { csatColumnLabelStyle } from '@/lib/surveyUi/labelStyles';
3
+ import { getEmojiForIndex, CsatStarIcons } from './surveyUiIcons';
4
+ import { columnSubmitValue, nonNaScaleColumns } from './surveyUiScaleUtils';
5
+ import { CustomSliderTrack } from './CustomSliderTrack';
6
+ import MatrixDropdown from './MatrixDropdown';
4
7
  import {
5
- cellSelectedVar,
6
- focusRingVar,
7
- matrixRadioDotStyle,
8
8
  matrixRadioRingStyle,
9
+ matrixRadioDotStyle,
9
10
  scaleCellSelectedStyle,
10
- emojiSizeStyle,
11
11
  unselectedOpacityStyle,
12
+ emojiSizeStyle,
13
+ cellSelectedVar,
14
+ focusRingVar,
12
15
  } from '@/lib/surveyUi/selectionStyles';
13
- import { getEmojiForIndex, CsatStarIcons } from './surveyUiIcons';
14
- import { columnSubmitValue, nonNaScaleColumns } from './surveyUiScaleUtils';
15
- import { CustomSliderTrack } from './CustomSliderTrack';
16
- import MatrixDropdown from './MatrixDropdown';
16
+ import { csatColumnLabelStyle } from '@/lib/surveyUi/labelStyles';
17
+
18
+ const MATRIX_ROW_LABEL_WIDTH = 'var(--cfm-matrix-row-width, 180px)';
17
19
 
18
20
  type CsatMatrixQuestion = CsatQuestion | RatingMatrixQuestion;
19
21
 
@@ -36,10 +38,10 @@ function GraphicsColumnLabelsRow({ scaleColumns }: { scaleColumns: ScaleColumn[]
36
38
  textAlign: 'center',
37
39
  fontSize: '12px',
38
40
  fontWeight: 500,
41
+ color: '#4b5563',
39
42
  lineHeight: 1.2,
40
43
  wordBreak: 'break-word',
41
44
  width: '64px',
42
- ...csatColumnLabelStyle(),
43
45
  }}
44
46
  >
45
47
  <span dangerouslySetInnerHTML={{ __html: col.label }} />
@@ -104,10 +106,10 @@ function CsatMatrixVerticalList({
104
106
  onClick={() => onCellSelect(row.id, submitValue)}
105
107
  style={{
106
108
  width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'flex-start',
107
- borderRadius: '8px', border: `1px solid ${isSelected ? focusRingVar : '#d1d5db'}`,
109
+ borderRadius: '8px', border: `1px solid ${isSelected ? '#e20074' : '#d1d5db'}`,
108
110
  padding: '14px 18px', cursor: 'pointer', transition: 'all 0.15s',
109
- backgroundColor: isSelected ? 'var(--cfm-mcq-selected-bg)' : '#fff',
110
- color: isSelected ? focusRingVar : '#374151',
111
+ backgroundColor: isSelected ? '#fdf2f8' : '#fff',
112
+ color: isSelected ? '#e20074' : '#374151',
111
113
  fontWeight: isSelected ? 600 : 400, fontSize: '15px', textAlign: 'left',
112
114
  }}
113
115
  >
@@ -126,9 +128,9 @@ function CsatMatrixVerticalList({
126
128
  key={col.id}
127
129
  style={{
128
130
  display: 'flex', alignItems: 'center', gap: '16px',
129
- borderRadius: '8px', border: `1px solid ${isSelected ? focusRingVar : '#e5e5e5'}`,
131
+ borderRadius: '8px', border: `1px solid ${isSelected ? '#e20074' : '#e5e5e5'}`,
130
132
  padding: '16px 20px', cursor: 'pointer', transition: 'all 0.15s',
131
- backgroundColor: isSelected ? 'var(--cfm-mcq-selected-bg)' : '#fff',
133
+ backgroundColor: isSelected ? '#fdf2f8' : '#fff',
132
134
  }}
133
135
  >
134
136
  <input type="radio" name={`${question.id}-${row.id}`}
@@ -136,8 +138,13 @@ function CsatMatrixVerticalList({
136
138
  onChange={() => onCellSelect(row.id, submitValue)}
137
139
  style={{ position: 'absolute', width: 0, height: 0, opacity: 0 }}
138
140
  />
139
- <div style={matrixRadioRingStyle(isSelected, '20px')}>
140
- {isSelected && <div style={matrixRadioDotStyle(isSelected, '10px')} />}
141
+ <div style={{
142
+ display: 'flex', width: '20px', height: '20px', flexShrink: 0,
143
+ alignItems: 'center', justifyContent: 'center', borderRadius: '50%',
144
+ border: isSelected ? '2px solid #e20074' : '1.5px solid #9ca3af',
145
+ backgroundColor: '#fff',
146
+ }}>
147
+ {isSelected && <div style={{ width: '10px', height: '10px', borderRadius: '50%', backgroundColor: '#e20074' }} />}
141
148
  </div>
142
149
  <span style={{ fontSize: '15px', fontWeight: 500, color: '#111827' }}
143
150
  dangerouslySetInnerHTML={{ __html: col.label }} />
@@ -151,7 +158,7 @@ function CsatMatrixVerticalList({
151
158
  <label style={{ display: 'flex', alignItems: 'center', gap: '12px', cursor: 'pointer', userSelect: 'none' }}>
152
159
  <input type="checkbox" checked={selectedValue[row.id] === null}
153
160
  onChange={() => onCellSelect(row.id, selectedValue[row.id] === null ? undefined : null)}
154
- style={{ width: '16px', height: '16px', accentColor: focusRingVar, cursor: 'pointer' }} />
161
+ style={{ width: '16px', height: '16px', accentColor: '#e20074', cursor: 'pointer' }} />
155
162
  <span style={{ fontSize: '14px', fontWeight: 500, color: '#4b5563' }}>Not Applicable</span>
156
163
  </label>
157
164
  )}
@@ -195,7 +202,7 @@ function CsatMatrixGrid({
195
202
  {/* Labels row */}
196
203
  {showLabels && m > 0 && (
197
204
  <div style={{ display: 'flex', alignItems: 'flex-end', marginBottom: '12px' }}>
198
- <div style={{ width: 'var(--cfm-matrix-row-width, 180px)', flexShrink: 0 }} />
205
+ <div style={{ width: MATRIX_ROW_LABEL_WIDTH, flexShrink: 0 }} />
199
206
  <div style={{ flex: 1, display: 'flex' }}>
200
207
  <div style={{ flex: 1, position: 'relative', height: '24px', ...(isGraphics ? { margin: '0 40px' } : {}) }}>
201
208
  {labels!.map((label, i) => {
@@ -209,8 +216,7 @@ function CsatMatrixGrid({
209
216
  left: `${percent}%`,
210
217
  transform: 'translateX(-50%)',
211
218
  textAlign: 'center', fontSize: '13px', fontWeight: 600,
212
- lineHeight: 1.3, whiteSpace: 'nowrap',
213
- ...csatColumnLabelStyle(),
219
+ color: '#4b5563', lineHeight: 1.3, whiteSpace: 'nowrap'
214
220
  }}>
215
221
  {label}
216
222
  </div>
@@ -225,7 +231,7 @@ function CsatMatrixGrid({
225
231
  {/* Column tick labels for graphics sliders (shown with or without anchor labels) */}
226
232
  {isGraphics && m > 0 && (
227
233
  <div style={{ display: 'flex', alignItems: 'flex-end', marginBottom: showLabels ? '8px' : '16px', minHeight: '24px' }}>
228
- <div style={{ width: 'var(--cfm-matrix-row-width, 180px)', flexShrink: 0 }} />
234
+ <div style={{ width: MATRIX_ROW_LABEL_WIDTH, flexShrink: 0 }} />
229
235
  <div style={{ flex: 1, display: 'flex' }}>
230
236
  <GraphicsColumnLabelsRow scaleColumns={scaleColumns} />
231
237
  {hasNotApplicable && (
@@ -238,7 +244,7 @@ function CsatMatrixGrid({
238
244
  {/* Column Headers for non-graphics */}
239
245
  {!isGraphics && !showLabels && m > 0 && (
240
246
  <div style={{ display: 'flex', alignItems: 'flex-end', marginBottom: '16px', minHeight: '24px' }}>
241
- <div style={{ width: 'var(--cfm-matrix-row-width, 180px)', flexShrink: 0 }} />
247
+ <div style={{ width: MATRIX_ROW_LABEL_WIDTH, flexShrink: 0 }} />
242
248
  <div style={{ flex: 1, display: 'flex' }}>
243
249
  <div style={{ flex: 1, display: 'grid', gridTemplateColumns: `repeat(${scaleColumns.length}, 1fr)` }}>
244
250
  {scaleColumns.map(col => (
@@ -264,9 +270,9 @@ function CsatMatrixGrid({
264
270
  {statementRows.map((row, rowIdx) => (
265
271
  <div key={row.id} style={{
266
272
  display: 'flex', alignItems: 'center', borderRadius: '8px',
267
- padding: '12px 0', backgroundColor: rowIdx % 2 === 0 ? 'var(--cfm-zebra-row)' : '#fff',
273
+ padding: '12px 0', backgroundColor: rowIdx % 2 === 0 ? 'rgba(249, 250, 251, 0.8)' : '#fff',
268
274
  }}>
269
- <div style={{ width: 'var(--cfm-matrix-row-width, 180px)', flexShrink: 0, padding: '0 16px' }}>
275
+ <div style={{ width: MATRIX_ROW_LABEL_WIDTH, flexShrink: 0, padding: '0 16px' }}>
270
276
  <span style={{ fontSize: '14px', fontWeight: 500, color: '#111827', lineHeight: 1.4 }}
271
277
  dangerouslySetInnerHTML={{ __html: row.statementText }} />
272
278
  </div>
@@ -305,7 +311,7 @@ function CsatMatrixGrid({
305
311
 
306
312
  const RadioNode = (
307
313
  <div style={matrixRadioRingStyle(isSelected)}>
308
- {isSelected && <div style={matrixRadioDotStyle(isSelected)} />}
314
+ <div style={matrixRadioDotStyle(isSelected)} />
309
315
  </div>
310
316
  );
311
317
 
@@ -328,23 +334,22 @@ function CsatMatrixGrid({
328
334
  ...emojiSizeStyle(),
329
335
  transform: isSelected ? 'scale(1.1)' : isHovered ? 'scale(1.25)' : 'scale(1)',
330
336
  ...(isSelected ? {
331
- backgroundColor: 'var(--cfm-mcq-selected-bg)',
332
- boxShadow: `0 0 0 2px ${focusRingVar}`,
337
+ outline: `2px solid ${focusRingVar}`,
338
+ backgroundColor: cellSelectedVar,
333
339
  borderRadius: '50%',
334
- } : {}),
340
+ } : unselectedOpacityStyle()),
335
341
  } : isStar ? {
336
342
  padding: '4px',
337
343
  transform: isHovered ? 'scale(1.1)' : 'scale(1)',
344
+ color: isSelected ? cellSelectedVar : focusRingVar,
338
345
  ...(isSelected ? {} : unselectedOpacityStyle()),
339
- opacity: isSelected ? 1 : isHovered ? 1 : undefined,
340
346
  } : isNumbered ? {
341
- width: 'var(--cfm-csat-cell-size, 40px)', height: 'var(--cfm-csat-cell-size, 40px)', borderRadius: '8px',
347
+ width: '40px', height: '40px', borderRadius: '8px',
342
348
  fontSize: '14px', fontWeight: 500,
343
- border: `1px solid ${isSelected ? focusRingVar : '#d1d5db'}`,
344
- backgroundColor: isSelected ? cellSelectedVar : '#fff',
345
- color: isSelected ? '#fff' : '#111827',
349
+ ...(isSelected
350
+ ? scaleCellSelectedStyle(true)
351
+ : { border: '1px solid #d1d5db', backgroundColor: '#fff', color: '#111827' }),
346
352
  transition: 'all 0.15s',
347
- ...(isSelected ? scaleCellSelectedStyle(true) : {}),
348
353
  } : { padding: '8px' }),
349
354
  }}
350
355
  >
@@ -382,7 +387,7 @@ function CsatMatrixGrid({
382
387
  <label style={{ cursor: 'pointer' }}>
383
388
  <input type="checkbox" checked={selectedValue[row.id] === null}
384
389
  onChange={() => onCellSelect(row.id, selectedValue[row.id] === null ? undefined : null)}
385
- style={{ width: '16px', height: '16px', accentColor: focusRingVar, cursor: 'pointer' }} />
390
+ style={{ width: '16px', height: '16px', accentColor: '#e20074', cursor: 'pointer' }} />
386
391
  </label>
387
392
  </div>
388
393
  )}
@@ -441,8 +446,7 @@ function CsatMatrixCarousel({
441
446
  <div key={i} style={{
442
447
  position: 'absolute', left: `${percent}%`, transform: 'translateX(-50%)',
443
448
  textAlign: 'center', fontSize: '13px', fontWeight: 600,
444
- lineHeight: 1.3, whiteSpace: 'nowrap',
445
- ...csatColumnLabelStyle(),
449
+ color: '#4b5563', lineHeight: 1.3, whiteSpace: 'nowrap'
446
450
  }}>
447
451
  {label}
448
452
  </div>
@@ -486,8 +490,13 @@ function CsatMatrixCarousel({
486
490
  const EmojiNode = isEmoji ? getEmojiForIndex(displayIdx, scaleColumns.length) : null;
487
491
  const StarNode = isStar ? (isSelected ? (CsatStarIcons.filled as any) : (CsatStarIcons.empty as any)) : null;
488
492
  const RadioNode = (
489
- <div style={matrixRadioRingStyle(isSelected)}>
490
- {isSelected && <div style={matrixRadioDotStyle(isSelected)} />}
493
+ <div style={{
494
+ display: 'flex', width: '18px', height: '18px',
495
+ alignItems: 'center', justifyContent: 'center', borderRadius: '50%',
496
+ margin: '0 auto', border: isSelected ? '2px solid #e20074' : '2px solid #d1d5db',
497
+ backgroundColor: '#fff',
498
+ }}>
499
+ {isSelected && <div style={{ width: '8px', height: '8px', borderRadius: '50%', backgroundColor: '#e20074' }} />}
491
500
  </div>
492
501
  );
493
502
 
@@ -503,23 +512,19 @@ function CsatMatrixCarousel({
503
512
  transition: 'all 0.15s', border: 'none', backgroundColor: 'transparent',
504
513
  cursor: 'pointer', borderRadius: '6px', pointerEvents: 'none',
505
514
  ...(isEmoji ? {
506
- padding: '6px', ...emojiSizeStyle(),
507
- transform: isSelected ? 'scale(1.1)' : isHovered ? 'scale(1.25)' : 'scale(1)',
508
- ...(isSelected ? { backgroundColor: 'var(--cfm-mcq-selected-bg)', boxShadow: `0 0 0 2px ${focusRingVar}`, borderRadius: '50%' } : {}),
515
+ padding: '6px', transform: isSelected ? 'scale(1.1)' : isHovered ? 'scale(1.25)' : 'scale(1)',
516
+ ...(isSelected ? { backgroundColor: 'rgba(252, 231, 243, 0.6)', boxShadow: '0 0 0 2px rgba(226, 0, 116, 0.3)', borderRadius: '50%' } : {}),
509
517
  } : isStar ? {
510
- padding: '4px', transform: isHovered ? 'scale(1.1)' : 'scale(1)',
511
- ...(isSelected ? {} : unselectedOpacityStyle()),
512
- opacity: isSelected ? 1 : isHovered ? 1 : undefined,
518
+ padding: '4px', transform: isHovered ? 'scale(1.1)' : 'scale(1)', opacity: isSelected ? 1 : isHovered ? 1 : 0.5,
513
519
  } : isNumbered ? {
514
- width: 'var(--cfm-csat-cell-size, 40px)', height: 'var(--cfm-csat-cell-size, 40px)', borderRadius: '8px', fontSize: '14px', fontWeight: 500,
515
- border: `1px solid ${isSelected ? focusRingVar : '#d1d5db'}`, backgroundColor: isSelected ? cellSelectedVar : '#fff', color: isSelected ? '#fff' : '#111827',
516
- ...(isSelected ? scaleCellSelectedStyle(true) : {}),
520
+ width: '40px', height: '40px', borderRadius: '8px', fontSize: '14px', fontWeight: 500,
521
+ border: `1px solid ${isSelected ? '#e20074' : '#d1d5db'}`, backgroundColor: isSelected ? '#e20074' : '#fff', color: isSelected ? '#fff' : '#111827',
517
522
  } : { padding: '8px' }),
518
523
  }}
519
524
  >
520
525
  {(EmojiNode ?? StarNode ?? (isNumbered ? (displayIdx + 1) : RadioNode)) as React.ReactNode}
521
526
  </button>
522
- <span style={{ fontSize: '13px', fontWeight: 500, textAlign: 'center', lineHeight: 1.2, ...csatColumnLabelStyle() }}
527
+ <span style={{ fontSize: '13px', fontWeight: 500, color: '#4b5563', textAlign: 'center', lineHeight: 1.2 }}
523
528
  dangerouslySetInnerHTML={{ __html: col.label }} />
524
529
  </div>
525
530
  );
@@ -532,7 +537,7 @@ function CsatMatrixCarousel({
532
537
  <label style={{ display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer' }}>
533
538
  <input type="checkbox" checked={selectedValue[row.id] === null}
534
539
  onChange={() => onCellSelect(row.id, selectedValue[row.id] === null ? undefined : null)}
535
- style={{ width: '16px', height: '16px', accentColor: focusRingVar, cursor: 'pointer' }} />
540
+ style={{ width: '16px', height: '16px', accentColor: '#e20074', cursor: 'pointer' }} />
536
541
  <span style={{ fontSize: '14px', fontWeight: 500, color: '#4b5563' }}>N/A</span>
537
542
  </label>
538
543
  </div>
@@ -554,7 +559,7 @@ function CsatMatrixCarousel({
554
559
  <button key={i} type="button" onClick={() => setActiveCarouselIndex(i)}
555
560
  style={{
556
561
  width: i === activeCarouselIndex ? '20px' : '8px', height: '8px', borderRadius: '4px',
557
- backgroundColor: i === activeCarouselIndex ? cellSelectedVar : '#d1d5db', border: 'none', padding: 0,
562
+ backgroundColor: i === activeCarouselIndex ? '#e20074' : '#d1d5db', border: 'none', padding: 0,
558
563
  cursor: 'pointer', transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)'
559
564
  }} />
560
565
  ))}
@@ -594,6 +599,7 @@ export function CsatMatrixScale({ question, selectedValue = {}, onSelect }: Csat
594
599
  const isStar = displayStyle === 'star';
595
600
  const isNumbered = displayStyle === 'numbered';
596
601
  const isGraphics = displayStyle === 'graphics';
602
+ const isStandardRadio = displayStyle === 'standard';
597
603
 
598
604
  const scaleColumns = nonNaScaleColumns(question.scaleColumns);
599
605
 
@@ -612,7 +618,7 @@ export function CsatMatrixScale({ question, selectedValue = {}, onSelect }: Csat
612
618
  );
613
619
  }
614
620
 
615
- if (isDropdown || isSelectBox) {
621
+ if (isDropdown || isSelectBox || (isStandardRadio && question.type !== 'RATING_MATRIX')) {
616
622
  return (
617
623
  <CsatMatrixVerticalList
618
624
  question={question}
@@ -7,7 +7,6 @@
7
7
  */
8
8
 
9
9
  import React from 'react';
10
- import { focusRingVar, cellSelectedVar } from '@/lib/surveyUi/selectionStyles';
11
10
  import { getEmojiForIndex } from './surveyUiIcons';
12
11
 
13
12
  type CustomSliderTrackProps = {
@@ -51,8 +50,7 @@ export function CustomSliderTrack({
51
50
  ? tooltipLabel.replace(/<[^>]*>/g, '')
52
51
  : String(Math.round(value));
53
52
 
54
- const trackColor = disabled ? 'var(--cfm-slider-tick, #9ca3af)' : 'var(--cfm-slider-track, var(--cfm-primary))';
55
- const thumbBorderColor = disabled ? 'var(--cfm-slider-tick, #9ca3af)' : focusRingVar;
53
+ const themeColor = disabled ? '#9ca3af' : 'var(--cfm-input-focus-ring, var(--cfm-primary))';
56
54
 
57
55
  return (
58
56
  <div
@@ -62,12 +60,12 @@ export function CustomSliderTrack({
62
60
  >
63
61
  <div style={{
64
62
  position: 'absolute', top: '50%', left: 0, right: 0, transform: 'translateY(-50%)',
65
- height: '4px', borderRadius: '2px', backgroundColor: 'var(--cfm-slider-tick, #e5e7eb)',
63
+ height: '4px', borderRadius: '2px', backgroundColor: 'var(--cfm-slider-track, #e5e7eb)',
66
64
  }} />
67
65
 
68
66
  <div style={{
69
67
  position: 'absolute', top: '50%', left: 0, width: `${percentage}%`, transform: 'translateY(-50%)',
70
- height: '4px', borderRadius: '2px', backgroundColor: trackColor,
68
+ height: '4px', borderRadius: '2px', backgroundColor: themeColor,
71
69
  transition: isDragging ? 'none' : 'width 0.15s ease-in-out',
72
70
  }} />
73
71
 
@@ -78,7 +76,7 @@ export function CustomSliderTrack({
78
76
  key={idx}
79
77
  style={{
80
78
  position: 'absolute', top: '50%', left: `${tickPct}%`, transform: 'translate(-50%, -50%)',
81
- width: '1px', height: '8px', backgroundColor: 'var(--cfm-slider-tick, #d1d5db)', pointerEvents: 'none',
79
+ width: '1px', height: '8px', backgroundColor: '#d1d5db', pointerEvents: 'none',
82
80
  }}
83
81
  />
84
82
  );
@@ -115,22 +113,15 @@ export function CustomSliderTrack({
115
113
  ) : (
116
114
  <div style={{
117
115
  position: 'absolute', top: '50%', left: `${percentage}%`, transform: 'translate(-50%, -50%)',
118
- width: '18px', height: '18px', borderRadius: '50%',
119
- border: `2.5px solid ${thumbBorderColor}`,
120
- backgroundColor: cellSelectedVar,
121
- display: 'flex', alignItems: 'center', justifyContent: 'center',
116
+ width: '18px', height: '18px', borderRadius: '50%', backgroundColor: '#ffffff',
117
+ border: `2.5px solid ${themeColor}`,
122
118
  boxShadow: (isHovered || isDragging) && !disabled
123
- ? '0 0 0 6px var(--cfm-mcq-selected-bg, rgba(226, 0, 116, 0.2)), 0 1px 3px rgba(0,0,0,0.2)'
119
+ ? '0 0 0 6px rgba(226, 0, 116, 0.2), 0 1px 3px rgba(0,0,0,0.2)'
124
120
  : '0 1px 3px rgba(0,0,0,0.2)',
125
121
  pointerEvents: 'none',
126
122
  transition: isDragging ? 'none' : 'left 0.15s ease-in-out, box-shadow 0.15s, border-color 0.2s',
127
123
  boxSizing: 'border-box',
128
- }}>
129
- <div style={{
130
- width: '33%', height: '33%', borderRadius: '50%',
131
- backgroundColor: 'var(--cfm-slider-thumb, var(--cfm-primary))',
132
- }} />
133
- </div>
124
+ }} />
134
125
  )}
135
126
 
136
127
  {(isHovered || isDragging) && !disabled && (
@@ -8,7 +8,6 @@
8
8
 
9
9
  import React, { useState, useRef } from 'react';
10
10
  import type { FileUploadQuestion } from '@explorer02/cfm-survey-sdk';
11
- import { focusRingVar } from '@/lib/surveyUi/selectionStyles';
12
11
 
13
12
  type UploadedFileAnswer = {
14
13
  id: string;
@@ -87,15 +86,8 @@ function FileUploadDropzone({
87
86
  onDrag: (e: React.DragEvent) => void;
88
87
  onDrop: (e: React.DragEvent) => void;
89
88
  }) {
90
- const dropzoneStyleAttr =
91
- typeof document !== 'undefined'
92
- ? document.documentElement.getAttribute('data-cfm-dropzone-style') ?? undefined
93
- : undefined;
94
-
95
89
  return (
96
90
  <div
97
- className="cfm-dropzone"
98
- {...(dropzoneStyleAttr ? { 'data-cfm-dropzone-style': dropzoneStyleAttr } : {})}
99
91
  onDragEnter={onDrag}
100
92
  onDragOver={onDrag}
101
93
  onDragLeave={onDrag}
@@ -106,10 +98,10 @@ function FileUploadDropzone({
106
98
  flexDirection: 'column',
107
99
  alignItems: 'center',
108
100
  justifyContent: 'center',
109
- border: `2px var(--cfm-upload-border-style, dashed) ${isDragActive ? focusRingVar : 'var(--cfm-upload-border-color)'}`,
110
- borderRadius: '12px',
111
- backgroundColor: isDragActive ? 'var(--cfm-mcq-selected-bg)' : 'var(--cfm-upload-fill)',
112
- padding: 'var(--cfm-upload-padding)',
101
+ border: `2px ${isDragActive ? 'solid' : 'var(--cfm-upload-border-style, dashed)'} var(--cfm-upload-border-color, #d1d5db)`,
102
+ borderRadius: 'var(--cfm-border-radius, 12px)',
103
+ backgroundColor: isDragActive ? 'var(--cfm-mcq-selected-bg, #fdf2f8)' : 'var(--cfm-upload-fill, #f9fafb)',
104
+ padding: 'var(--cfm-upload-padding, 40px 24px)',
113
105
  cursor: disabled ? 'not-allowed' : 'pointer',
114
106
  opacity: disabled ? 0.6 : 1,
115
107
  textAlign: 'center',
@@ -2,7 +2,7 @@
2
2
  * Wizard-ready Header — copy to src/components/Header.tsx
3
3
  *
4
4
  * Brand row: [logo image] [company name] — company sits after logo when both exist.
5
- * Layout locked: logo left, name after (flex row only).
5
+ * When no logo file: text fallback via data-cfm-logo-text (company name or empty).
6
6
  *
7
7
  * After wizard save, logo src MUST come from survey-ui-config.json global.logo.fileName
8
8
  * via uiConfig.ts — never hardcode telekom-logo.svg or change fileName manually.
@@ -33,30 +33,43 @@ export default function Header({ embedded = false }: HeaderProps) {
33
33
  }}
34
34
  >
35
35
  <div
36
- className="cfm-header-inner flex h-full w-full items-center"
36
+ className="cfm-header-inner grid h-full w-full items-center"
37
37
  style={{
38
+ gridTemplateColumns: '1fr 1fr 1fr',
38
39
  padding: `0 var(--cfm-header-padding-x, 40px)`,
39
40
  }}
40
41
  >
41
42
  <div
42
- className="cfm-header-brand flex min-w-0 items-center"
43
+ className="cfm-header-brand flex items-center"
43
44
  style={{
45
+ gridColumn: 'var(--cfm-header-logo-col, 1)',
46
+ justifySelf: 'var(--cfm-header-logo-justify, start)',
44
47
  gap: 'var(--cfm-header-brand-gap, 16px)',
45
48
  }}
46
49
  >
47
- <img
48
- data-cfm-logo
49
- src={hasLogoFile ? seedLogoSrc! : ''}
50
- alt={seedCompany || 'Logo'}
51
- className="shrink-0 object-contain"
52
- style={{
53
- display: hasLogoFile ? '' : 'none',
54
- width: 'var(--cfm-header-logo-width, 120px)',
55
- height: 'var(--cfm-header-logo-height, 120px)',
56
- padding: 'var(--cfm-header-logo-padding, 0px)',
57
- background: 'var(--cfm-header-logo-bg, transparent)',
58
- }}
59
- />
50
+ {hasLogoFile ? (
51
+ <img
52
+ data-cfm-logo
53
+ src={seedLogoSrc!}
54
+ alt={seedCompany || 'Logo'}
55
+ className="shrink-0 object-contain"
56
+ style={{
57
+ width: 'var(--cfm-header-logo-width, 120px)',
58
+ height: 'var(--cfm-header-logo-height, 120px)',
59
+ maxHeight: 'calc(var(--cfm-header-height, 120px) - 8px)',
60
+ padding: 'var(--cfm-header-logo-padding, 10px)',
61
+ background: 'transparent',
62
+ }}
63
+ />
64
+ ) : (
65
+ <img
66
+ data-cfm-logo
67
+ src=""
68
+ alt="Logo"
69
+ className="shrink-0 object-contain"
70
+ style={{ display: 'none' }}
71
+ />
72
+ )}
60
73
 
61
74
  <span
62
75
  data-cfm-logo-text
@@ -71,22 +84,26 @@ export default function Header({ embedded = false }: HeaderProps) {
71
84
  {seedCompany}
72
85
  </span>
73
86
 
74
- {seedShowCompany && seedCompany ? (
75
- <span
76
- data-cfm-company
77
- className="truncate font-semibold"
78
- style={{
79
- display: hasLogoFile ? '' : 'none',
80
- color: 'var(--cfm-header-company-color, var(--cfm-text))',
81
- fontSize: 'var(--cfm-header-company-size, 18px)',
82
- fontWeight: 'var(--cfm-header-company-weight, 600)',
83
- }}
84
- >
85
- {seedCompany}
86
- </span>
87
- ) : (
88
- <span data-cfm-company className="hidden" aria-hidden />
89
- )}
87
+ <span
88
+ data-cfm-company
89
+ className="font-semibold"
90
+ style={{
91
+ display:
92
+ seedShowCompany && (hasLogoFile || seedCompany)
93
+ ? hasLogoFile
94
+ ? ''
95
+ : 'none'
96
+ : 'none',
97
+ marginTop: 'var(--cfm-header-company-margin-top, 0)',
98
+ marginLeft: 'var(--cfm-header-company-margin-left, 0)',
99
+ marginRight: 'var(--cfm-header-company-margin-right, 0)',
100
+ color: 'var(--cfm-header-company-color, var(--cfm-text))',
101
+ fontSize: 'var(--cfm-header-company-size, 18px)',
102
+ fontWeight: 'var(--cfm-header-company-weight, 600)',
103
+ }}
104
+ >
105
+ {seedCompany}
106
+ </span>
90
107
  </div>
91
108
  </div>
92
109
  </header>
@@ -29,8 +29,8 @@ function HeatmapSpot({ spot }: { spot: { id: string; x: number; y: number } }) {
29
29
  width: 'var(--cfm-heatmap-pin-size, 16px)',
30
30
  height: 'var(--cfm-heatmap-pin-size, 16px)',
31
31
  transform: 'translate(-50%, -50%)',
32
- backgroundColor: 'var(--cfm-heatmap-pin)',
33
- border: '2px solid var(--cfm-heatmap-pin-border, white)',
32
+ backgroundColor: 'var(--cfm-heatmap-pin, #F97316)',
33
+ border: '2px solid var(--cfm-heatmap-pin-border, #fff)',
34
34
  boxShadow: '0 1px 3px rgba(0,0,0,0.25)',
35
35
  }}
36
36
  />
@@ -63,9 +63,6 @@ export function HeatmapScale({ question, selectedValue = [], onSelect }: Heatmap
63
63
  }
64
64
 
65
65
  onSelect([...spots, createNewSpot(x, y)]);
66
- if (typeof window !== 'undefined' && window.parent !== window) {
67
- window.parent.postMessage({ type: 'CFM_HEATMAP_PIN', pin: { x, y } }, '*');
68
- }
69
66
  }
70
67
 
71
68
  function handleImageLoad() {