@explorer02/cfm-survey-sdk 0.3.2 → 0.3.4

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 (29) hide show
  1. package/dist/cli/index.js +55 -55
  2. package/dist/cli/index.mjs +61 -61
  3. package/package.json +1 -1
  4. package/templates/docs/00-integration/component-checklist.md +1 -0
  5. package/templates/docs/templates/CsatMatrixScale.tsx +86 -33
  6. package/templates/docs/templates/CustomSliderTrack.tsx +6 -4
  7. package/templates/docs/templates/Footer.tsx +69 -26
  8. package/templates/docs/templates/Header.tsx +102 -65
  9. package/templates/docs/templates/LikertMatrixScale.tsx +7 -6
  10. package/templates/docs/templates/Question.tsx +7 -1
  11. package/templates/docs/templates/RankOrderScale.tsx +8 -1
  12. package/templates/docs/templates/RatingScale.tsx +2 -1
  13. package/templates/docs/templates/SliderMatrixScale.tsx +5 -3
  14. package/templates/docs/templates/labelStyles.ts +59 -16
  15. package/templates/docs/templates/selectionStyles.ts +16 -1
  16. package/templates/docs/templates/surveyUiIcons.tsx +1 -1
  17. package/templates/preview-harness/vite-app/src/PreviewConfigContext.tsx +41 -1
  18. package/templates/preview-harness/vite-app/src/QuestionPreview.tsx +2 -1
  19. package/templates/preview-harness/vite-app/src/SurveyPagePreview.tsx +3 -2
  20. package/templates/preview-harness/vite-app/src/fixtures/questions.ts +18 -9
  21. package/templates/survey-theme.css +14 -2
  22. package/templates/wizard-dist/assets/{PreviewMock-DbbLpHdF.js → PreviewMock-CC1UlWe-.js} +1 -1
  23. package/templates/wizard-dist/assets/TypePanel-DISCXm0z.js +1 -0
  24. package/templates/wizard-dist/assets/index-Blpq4QjK.js +34 -0
  25. package/templates/wizard-dist/assets/index-CVBd54V0.css +1 -0
  26. package/templates/wizard-dist/index.html +2 -2
  27. package/templates/wizard-dist/assets/TypePanel-DQbV2iCf.js +0 -1
  28. package/templates/wizard-dist/assets/index-BhWM50Yu.css +0 -1
  29. package/templates/wizard-dist/assets/index-CY7WMJ93.js +0 -34
@@ -2,8 +2,8 @@ import React, { useState } from 'react';
2
2
  import type { CfmMatrixQuestion, MatrixRowAnswers } from '@explorer02/cfm-survey-sdk';
3
3
  import { columnSubmitValue } from './surveyUiScaleUtils';
4
4
  import MatrixDropdown from './MatrixDropdown';
5
- import { matrixColumnLabelStyle } from '@/lib/surveyUi/labelStyles';
6
- import { focusRingVar, matrixCarouselDotVar, matrixRadioDotStyle, matrixRadioRingStyle } from '@/lib/surveyUi/selectionStyles';
5
+ import { columnLabelPillStyle, matrixColumnLabelStyle } from '@/lib/surveyUi/labelStyles';
6
+ import { focusRingVar, matrixCarouselDotVar, matrixRadioDotStyle, matrixRadioRingStyle, matrixRowBackgroundStyle } from '@/lib/surveyUi/selectionStyles';
7
7
 
8
8
  const MATRIX_ROW_LABEL_WIDTH = 'var(--cfm-matrix-row-width, 180px)';
9
9
  const BIPOLAR_COL_WIDTH = 'var(--cfm-matrix-bipolar-width, 25%)';
@@ -219,7 +219,7 @@ function LikertMatrixGridLayout({
219
219
  position: 'absolute', left: leftPos, transform: 'translateX(-50%)',
220
220
  bottom: 0, display: 'flex', justifyContent: 'center', whiteSpace: 'nowrap'
221
221
  }}>
222
- <span style={{ fontSize: '13px', fontWeight: 600, color: '#111827' }}>{lbl}</span>
222
+ <span style={{ fontSize: '13px', fontWeight: 600, ...columnLabelPillStyle('matrix') }}>{lbl}</span>
223
223
  </div>
224
224
  );
225
225
  })}
@@ -234,7 +234,7 @@ function LikertMatrixGridLayout({
234
234
  <div style={{ flex: 1, display: 'grid', gridTemplateColumns: `repeat(${gridCols.length}, 1fr)` }}>
235
235
  {gridCols.map((col) => (
236
236
  <div key={col.id} style={{ display: 'flex', justifyContent: 'center', padding: '0 4px' }}>
237
- <span style={{ fontSize: '13px', fontWeight: 500, textAlign: 'center', lineHeight: 1.2, ...matrixColumnLabelStyle() }}
237
+ <span style={{ fontSize: '13px', fontWeight: 500, ...columnLabelPillStyle('matrix') }}
238
238
  dangerouslySetInnerHTML={{ __html: transposeTable ? ('statementText' in col ? col.statementText : col.label) : ('label' in col ? col.label : (col as { statementText: string }).statementText) }} />
239
239
  </div>
240
240
  ))}
@@ -258,8 +258,9 @@ function LikertMatrixGridLayout({
258
258
  {repeatColumnHeaders && renderHeader()}
259
259
  <div style={{
260
260
  display: 'flex', alignItems: 'center', borderRadius: '8px',
261
- padding: 'var(--cfm-matrix-cell-padding, 12px) 0', backgroundColor: !repeatColumnHeaders && rowIdx % 2 === 0 ? 'var(--cfm-zebra-row, rgba(249, 250, 251, 0.8))' : '#fff',
262
- boxSizing: 'border-box'
261
+ padding: 'var(--cfm-matrix-cell-padding, 12px) 0',
262
+ ...matrixRowBackgroundStyle(rowIdx),
263
+ boxSizing: 'border-box',
263
264
  }}>
264
265
  <div style={{ flex: isBipolar ? `0 0 ${BIPOLAR_COL_WIDTH}` : `0 0 ${MATRIX_ROW_LABEL_WIDTH}`, padding: '0 16px', boxSizing: 'border-box' }}>
265
266
  <span style={{ fontSize: '14px', fontWeight: 500, color: '#111827', lineHeight: 1.4, wordWrap: 'break-word', display: 'block' }}
@@ -121,6 +121,7 @@ export default function Question({
121
121
  boxShadow: 'var(--cfm-card-shadow, 0 1px 3px rgba(0,0,0,0.1))',
122
122
  borderRadius: 'var(--cfm-border-radius, 12px)',
123
123
  fontFamily: 'var(--cfm-font-family)',
124
+ marginBottom: 'var(--cfm-card-gap, 32px)',
124
125
  }}
125
126
  >
126
127
  <h2
@@ -149,15 +150,19 @@ export default function Question({
149
150
  {question.type === QUESTION_TYPE.NPS_SCALE && (
150
151
  <div className="space-y-3">
151
152
  {(question.minLabel || question.midLabel || question.maxLabel) && (
152
- <div className="relative w-full h-6 text-xs font-semibold" style={hintLabelStyle()}>
153
+ <div className="relative w-full min-h-[24px] text-xs font-semibold">
153
154
  <span
154
155
  className="absolute left-0 top-0 max-w-[30%] text-left leading-tight"
156
+ style={{ ...hintLabelStyle(), padding: '2px 6px', borderRadius: '4px' }}
155
157
  dangerouslySetInnerHTML={{ __html: question.minLabel ?? '' }}
156
158
  />
157
159
  {question.midLabel && (
158
160
  <span
159
161
  className="absolute top-0 -translate-x-1/2 text-center max-w-[40%] leading-tight"
160
162
  style={{
163
+ ...hintLabelStyle(),
164
+ padding: '2px 6px',
165
+ borderRadius: '4px',
161
166
  left: `${question.midLabelIndex !== undefined && question.options.length > 1
162
167
  ? (question.midLabelIndex / (question.options.length - 1)) * 100
163
168
  : 50}%`,
@@ -167,6 +172,7 @@ export default function Question({
167
172
  )}
168
173
  <span
169
174
  className="absolute right-0 top-0 max-w-[30%] text-right leading-tight"
175
+ style={{ ...hintLabelStyle(), padding: '2px 6px', borderRadius: '4px' }}
170
176
  dangerouslySetInnerHTML={{ __html: question.maxLabel ?? '' }}
171
177
  />
172
178
  </div>
@@ -121,12 +121,19 @@ function RankOrderOptionRow({
121
121
  }: RankOrderOptionRowProps) {
122
122
  return (
123
123
  <div
124
- className="flex items-center gap-3 rounded-lg border border-[#e5e5e5] transition-colors hover:bg-gray-50/50"
124
+ className="flex items-center gap-3 rounded-lg border border-[#e5e5e5] transition-colors"
125
125
  style={{
126
126
  backgroundColor: 'var(--cfm-rank-item-bg, #fff)',
127
127
  padding: 'var(--cfm-rank-item-padding, 12px 16px)',
128
128
  marginBottom: 'var(--cfm-rank-item-gap, 12px)',
129
129
  }}
130
+ onMouseEnter={(e) => {
131
+ e.currentTarget.style.backgroundColor =
132
+ 'var(--cfm-rank-dropdown-hover-bg, var(--cfm-rank-item-bg, #f9fafb))';
133
+ }}
134
+ onMouseLeave={(e) => {
135
+ e.currentTarget.style.backgroundColor = 'var(--cfm-rank-item-bg, #fff)';
136
+ }}
130
137
  >
131
138
  {rankSelect}
132
139
  {dragHandle}
@@ -112,7 +112,7 @@ export default function RatingScale({
112
112
  <div role="radiogroup" className="w-full overflow-x-auto" data-cfm-nps-area>
113
113
  {showNumberBadges && (
114
114
  <div className="mb-3 min-w-0" style={gridStyle}>
115
- {resolvedOptions.map((option) => {
115
+ {resolvedOptions.map((option, index) => {
116
116
  const isSelected = isScaleAnswerSelected(selectedValue, option.value);
117
117
  return (
118
118
  <div key={`${questionId}-badge-${option.value}`} className="flex justify-center">
@@ -125,6 +125,7 @@ export default function RatingScale({
125
125
  ? scaleCellSelectedStyle(true)
126
126
  : { backgroundColor: option.color }),
127
127
  }}
128
+ data-cfm-number-badge={index}
128
129
  title={option.label}
129
130
  >
130
131
  {option.label}
@@ -9,7 +9,7 @@ import React from 'react';
9
9
  import type { SliderMatrixQuestion, MatrixRowAnswers } from '@explorer02/cfm-survey-sdk';
10
10
  import { tickColorFromIndex } from './surveyUiScaleUtils';
11
11
  import { CustomSliderTrack } from './CustomSliderTrack';
12
- import { hintLabelStyle, sliderTickLabelStyle } from '@/lib/surveyUi/labelStyles';
12
+ import { columnLabelPillStyle, sliderTickLabelStyle } from '@/lib/surveyUi/labelStyles';
13
13
 
14
14
  const MATRIX_ROW_LABEL_WIDTH = 'var(--cfm-matrix-row-width, 180px)';
15
15
  const SLIDER_ROW_BAND = 'var(--cfm-slider-row-band, var(--cfm-row-band, #f3f4f6))';
@@ -84,12 +84,13 @@ export function SliderMatrixScale({ question, selectedValue = {}, onSelect }: Sl
84
84
  marginBottom: '16px',
85
85
  fontSize: '14px',
86
86
  fontWeight: 500,
87
- ...hintLabelStyle(),
88
87
  }}
89
88
  >
90
89
  {labels.map((lbl, idx) => (
91
90
  <div key={idx} style={{ width: 0, display: 'flex', justifyContent: 'center' }}>
92
- <span style={{ whiteSpace: 'nowrap' }}>{lbl}</span>
91
+ <span style={columnLabelPillStyle('hint')}>
92
+ {lbl}
93
+ </span>
93
94
  </div>
94
95
  ))}
95
96
  </div>
@@ -180,6 +181,7 @@ export function SliderMatrixScale({ question, selectedValue = {}, onSelect }: Sl
180
181
  <div key={row.id} style={{
181
182
  display: 'flex', width: '100%', alignItems: 'center',
182
183
  backgroundColor: SLIDER_ROW_BAND, borderRadius: '12px', padding: '16px', boxSizing: 'border-box',
184
+ borderBottom: '1px solid var(--cfm-card-border-color, #e5e7eb)',
183
185
  }}>
184
186
  <div style={{ flex: `0 0 ${MATRIX_ROW_LABEL_WIDTH}`, paddingRight: '16px', fontSize: '15px', color: '#111827', wordWrap: 'break-word', display: 'flex', alignItems: 'center' }}>
185
187
  {row.statementText !== 'Question' && row.statementText !== '' ? (
@@ -4,30 +4,73 @@
4
4
  */
5
5
  import type { CSSProperties } from 'react';
6
6
 
7
+ export type ColumnLabelScope = 'matrix' | 'csat' | 'rating' | 'hint' | 'sliderTick';
8
+
9
+ const PILL_BASE: CSSProperties = {
10
+ display: 'inline-flex',
11
+ alignItems: 'center',
12
+ justifyContent: 'center',
13
+ padding: '4px 10px',
14
+ borderRadius: 'var(--cfm-border-radius, 6px)',
15
+ textAlign: 'center',
16
+ lineHeight: 1.2,
17
+ whiteSpace: 'nowrap',
18
+ boxSizing: 'border-box',
19
+ };
20
+
21
+ function scopeColorVars(scope: ColumnLabelScope): { color: string; backgroundColor: string } {
22
+ switch (scope) {
23
+ case 'matrix':
24
+ return {
25
+ color: 'var(--cfm-matrix-column-label-color, #4b5563)',
26
+ backgroundColor: 'var(--cfm-matrix-column-label-bg, transparent)',
27
+ };
28
+ case 'csat':
29
+ return {
30
+ color: 'var(--cfm-csat-column-label-color, #4b5563)',
31
+ backgroundColor: 'var(--cfm-csat-column-label-bg, transparent)',
32
+ };
33
+ case 'rating':
34
+ return {
35
+ color: 'var(--cfm-rating-column-label-color, var(--cfm-matrix-column-label-color, #4b5563))',
36
+ backgroundColor: 'var(--cfm-rating-column-label-bg, var(--cfm-matrix-column-label-bg, transparent))',
37
+ };
38
+ case 'hint':
39
+ return {
40
+ color: 'var(--cfm-hint-label-color, #6b7280)',
41
+ backgroundColor: 'var(--cfm-hint-label-bg, transparent)',
42
+ };
43
+ case 'sliderTick':
44
+ return {
45
+ color: 'var(--cfm-slider-tick-label-color, #4b5563)',
46
+ backgroundColor: 'var(--cfm-slider-tick-label-bg, transparent)',
47
+ };
48
+ default:
49
+ return { color: '#4b5563', backgroundColor: 'transparent' };
50
+ }
51
+ }
52
+
53
+ /** Centered pill for column / anchor / tick labels — wizard Label Items customization. */
54
+ export function columnLabelPillStyle(scope: ColumnLabelScope): CSSProperties {
55
+ return { ...PILL_BASE, ...scopeColorVars(scope) };
56
+ }
57
+
7
58
  export function hintLabelStyle(): CSSProperties {
8
- return {
9
- color: 'var(--cfm-hint-label-color, #6b7280)',
10
- backgroundColor: 'var(--cfm-hint-label-bg, transparent)',
11
- };
59
+ return columnLabelPillStyle('hint');
12
60
  }
13
61
 
14
62
  export function matrixColumnLabelStyle(): CSSProperties {
15
- return {
16
- color: 'var(--cfm-matrix-column-label-color, #4b5563)',
17
- backgroundColor: 'var(--cfm-matrix-column-label-bg, transparent)',
18
- };
63
+ return scopeColorVars('matrix');
19
64
  }
20
65
 
21
66
  export function csatColumnLabelStyle(): CSSProperties {
22
- return {
23
- color: 'var(--cfm-csat-column-label-color, #4b5563)',
24
- backgroundColor: 'var(--cfm-csat-column-label-bg, transparent)',
25
- };
67
+ return scopeColorVars('csat');
68
+ }
69
+
70
+ export function ratingColumnLabelStyle(): CSSProperties {
71
+ return scopeColorVars('rating');
26
72
  }
27
73
 
28
74
  export function sliderTickLabelStyle(): CSSProperties {
29
- return {
30
- color: 'var(--cfm-slider-tick-label-color, #4b5563)',
31
- backgroundColor: 'var(--cfm-slider-tick-label-bg, transparent)',
32
- };
75
+ return scopeColorVars('sliderTick');
33
76
  }
@@ -41,7 +41,7 @@ export function mcqOptionCardStyle(isSelected: boolean): CSSProperties {
41
41
  if (optionStyle === 'filled') {
42
42
  return {
43
43
  ...base,
44
- background: isSelected ? mcqSelectedBgVar : 'var(--cfm-secondary, #f9fafb)',
44
+ background: isSelected ? mcqSelectedBgVar : 'var(--cfm-mcq-filled-bg, var(--cfm-secondary, #f9fafb))',
45
45
  };
46
46
  }
47
47
 
@@ -177,6 +177,21 @@ export function unselectedOpacityStyle(): CSSProperties {
177
177
  return { opacity: 'var(--cfm-csat-unselected-opacity, 0.5)' };
178
178
  }
179
179
 
180
+ /** Matrix statement row — zebra when --cfm-matrix-zebra-enabled is 1 (wizard multi-statement preview). */
181
+ export function matrixRowBackgroundStyle(rowIdx: number): CSSProperties {
182
+ let zebraOn = false;
183
+ if (typeof document !== 'undefined') {
184
+ zebraOn =
185
+ getComputedStyle(document.documentElement).getPropertyValue('--cfm-matrix-zebra-enabled').trim() ===
186
+ '1';
187
+ }
188
+ const useZebra = zebraOn && rowIdx % 2 === 0;
189
+ return {
190
+ backgroundColor: useZebra ? 'var(--cfm-zebra-row, rgba(249, 250, 251, 0.8))' : '#fff',
191
+ borderBottom: '1px solid var(--cfm-card-border-color, #e5e7eb)',
192
+ };
193
+ }
194
+
180
195
  export function emojiSizeStyle(): CSSProperties {
181
196
  return {
182
197
  fontSize: 'var(--cfm-csat-emoji-size, 28px)',
@@ -31,7 +31,7 @@ for (let i = 0; i < 11; i++) {
31
31
  const Icon = FACE_ICONS[i];
32
32
  CsatEmojiSet[i + 1] = React.createElement(Icon as React.ElementType, {
33
33
  className: 'transition-transform',
34
- style: { color: EMOJI_COLORS[i], fontSize: '28px' },
34
+ style: { color: EMOJI_COLORS[i], fontSize: 'var(--cfm-csat-emoji-size, 28px)' },
35
35
  });
36
36
  }
37
37
 
@@ -16,16 +16,54 @@ export type PreviewStatePatch = {
16
16
  export const PREVIEW_BRIDGE_SNAPSHOT = 'CFM_UI_CONFIG_SNAPSHOT';
17
17
  export const PREVIEW_BRIDGE_DELTA = 'CFM_UI_CONFIG_DELTA';
18
18
 
19
+ const TRAFFIC = [
20
+ '#ef4444', '#f97316', '#eab308', '#84cc16', '#22c55e', '#14b8a6',
21
+ '#06b6d4', '#3b82f6', '#6366f1', '#8b5cf6', '#a855f7',
22
+ ];
23
+
24
+ function readVar(name: string, fallback: string): string {
25
+ return getComputedStyle(document.documentElement).getPropertyValue(name).trim() || fallback;
26
+ }
27
+
28
+ function applyNumberBadgeColors(): void {
29
+ const mode = readVar('--cfm-number-label-mode', 'traffic');
30
+ const mono = readVar('--cfm-number-mono-color', '#9ca3af');
31
+ document.querySelectorAll('[data-cfm-number-badge]').forEach((el) => {
32
+ if (el.classList.contains('selected')) return;
33
+ const i = parseInt(el.getAttribute('data-cfm-number-badge') ?? '', 10);
34
+ if (Number.isNaN(i)) return;
35
+ let bg = TRAFFIC[i] ?? '#9ca3af';
36
+ if (mode === 'monochrome') bg = mono;
37
+ if (mode === 'individual') bg = readVar(`--cfm-number-color-${i}`, '#9ca3af');
38
+ (el as HTMLElement).style.backgroundColor = bg;
39
+ });
40
+ }
41
+
42
+ function applyDropzoneStyle(): void {
43
+ const style = readVar('--cfm-upload-border-style', 'dashed');
44
+ document.querySelectorAll('[data-cfm-dropzone], .cfm-dropzone').forEach((el) => {
45
+ (el as HTMLElement).style.borderStyle = style;
46
+ });
47
+ }
48
+
49
+ function applyThemeSideEffects(): void {
50
+ applyNumberBadgeColors();
51
+ applyDropzoneStyle();
52
+ }
53
+
19
54
  type PreviewConfigContextValue = {
20
55
  previewState: PreviewStatePatch;
56
+ configRevision: number;
21
57
  };
22
58
 
23
59
  const PreviewConfigContext = createContext<PreviewConfigContextValue>({
24
60
  previewState: {},
61
+ configRevision: 0,
25
62
  });
26
63
 
27
64
  export function PreviewConfigProvider({ children }: { children: ReactNode }) {
28
65
  const [previewState, setPreviewState] = useState<PreviewStatePatch>({});
66
+ const [configRevision, setConfigRevision] = useState(0);
29
67
 
30
68
  useEffect(() => {
31
69
  const handler = (event: MessageEvent) => {
@@ -47,6 +85,8 @@ export function PreviewConfigProvider({ children }: { children: ReactNode }) {
47
85
  root.style.setProperty(key, value as string);
48
86
  }
49
87
  }
88
+ setConfigRevision((r) => r + 1);
89
+ applyThemeSideEffects();
50
90
  };
51
91
 
52
92
  window.addEventListener('message', handler);
@@ -63,7 +103,7 @@ export function PreviewConfigProvider({ children }: { children: ReactNode }) {
63
103
  }, []);
64
104
 
65
105
  return (
66
- <PreviewConfigContext.Provider value={{ previewState }}>
106
+ <PreviewConfigContext.Provider value={{ previewState, configRevision }}>
67
107
  {children}
68
108
  </PreviewConfigContext.Provider>
69
109
  );
@@ -48,7 +48,7 @@ function applyMatrixRowCount<T extends SurveyQuestion & { statementRows?: { id:
48
48
  }
49
49
 
50
50
  function QuestionPreviewInner({ question, initialValue }: QuestionPreviewProps) {
51
- const { previewState } = usePreviewConfig();
51
+ const { previewState, configRevision } = usePreviewConfig();
52
52
  const [value, setValue] = useState<AnswerValue | undefined>(initialValue);
53
53
 
54
54
  const mergedQuestion = useMemo(() => {
@@ -143,6 +143,7 @@ function QuestionPreviewInner({ question, initialValue }: QuestionPreviewProps)
143
143
  data-cfm-question-area
144
144
  >
145
145
  <Question
146
+ key={configRevision}
146
147
  question={mergedQuestion}
147
148
  selectedValue={heatmapInitial ?? value}
148
149
  allAnswers={{}}
@@ -4,7 +4,7 @@ import SurveyStickyChrome from '@/components/SurveyStickyChrome';
4
4
  import LanguageSelector from '@/components/LanguageSelector';
5
5
  import Question from '@/components/Question';
6
6
  import Footer from '@/components/Footer';
7
- import { PreviewConfigProvider } from './PreviewConfigContext';
7
+ import { PreviewConfigProvider, usePreviewConfig } from './PreviewConfigContext';
8
8
  import {
9
9
  FIXTURE_SURVEY_PAGE_MCQ,
10
10
  FIXTURE_SURVEY_PAGE_NPS,
@@ -12,6 +12,7 @@ import {
12
12
  } from './fixtures/questions';
13
13
 
14
14
  function SurveyPagePreviewInner() {
15
+ const { configRevision } = usePreviewConfig();
15
16
  const [lang, setLang] = useState('en');
16
17
  const [mcqAnswer, setMcqAnswer] = useState<AnswerValue>('a');
17
18
  const [npsAnswer, setNpsAnswer] = useState<AnswerValue>(8);
@@ -42,7 +43,7 @@ function SurveyPagePreviewInner() {
42
43
  onChange={setLang}
43
44
  />
44
45
 
45
- <div className="space-y-8">
46
+ <div className="space-y-8" key={configRevision}>
46
47
  <Question
47
48
  question={FIXTURE_SURVEY_PAGE_MCQ}
48
49
  selectedValue={mcqAnswer}
@@ -209,8 +209,7 @@ export const FIXTURE_CSAT_EMOJI = base({
209
209
  displayStyle: 'emoji',
210
210
  gridLayout: 'standard',
211
211
  statementRows: MATRIX_ROW_SINGLE,
212
- scaleColumns: Array.from({ length: 10 }, (_, i) => ({ id: `c${i}`, label: String(i + 1) })),
213
- scaleAnchorLabels: ['Very unhappy', '', '', '', 'Very happy'],
212
+ scaleColumns: LIKERT_COLUMNS,
214
213
  });
215
214
 
216
215
  export const FIXTURE_CSAT_STAR = base({
@@ -218,7 +217,7 @@ export const FIXTURE_CSAT_STAR = base({
218
217
  displayStyle: 'star',
219
218
  gridLayout: 'standard',
220
219
  statementRows: MATRIX_ROW_SINGLE,
221
- scaleColumns: Array.from({ length: 10 }, (_, i) => ({ id: `c${i}`, label: String(i + 1) })),
220
+ scaleColumns: LIKERT_COLUMNS,
222
221
  });
223
222
 
224
223
  export const FIXTURE_CSAT_NUMBERED = base({
@@ -226,7 +225,7 @@ export const FIXTURE_CSAT_NUMBERED = base({
226
225
  displayStyle: 'numbered',
227
226
  gridLayout: 'standard',
228
227
  statementRows: MATRIX_ROWS_MULTI,
229
- scaleColumns: Array.from({ length: 10 }, (_, i) => ({ id: `c${i}`, label: String(i + 1) })),
228
+ scaleColumns: LIKERT_COLUMNS,
230
229
  });
231
230
 
232
231
  export const FIXTURE_CSAT_GRAPHICS = base({
@@ -234,8 +233,7 @@ export const FIXTURE_CSAT_GRAPHICS = base({
234
233
  displayStyle: 'graphics',
235
234
  gridLayout: 'standard',
236
235
  statementRows: MATRIX_ROW_SINGLE,
237
- scaleColumns: Array.from({ length: 10 }, (_, i) => ({ id: `c${i}`, label: String(i + 1) })),
238
- scaleAnchorLabels: ['Label 1', 'Label 2'],
236
+ scaleColumns: LIKERT_COLUMNS,
239
237
  });
240
238
 
241
239
  export const FIXTURE_CSAT_CAROUSEL = base({
@@ -243,7 +241,7 @@ export const FIXTURE_CSAT_CAROUSEL = base({
243
241
  displayStyle: 'star',
244
242
  gridLayout: 'carousel',
245
243
  statementRows: [{ id: 'r1', statementText: 'Overall experience' }],
246
- scaleColumns: Array.from({ length: 5 }, (_, i) => ({ id: `c${i}`, label: String(i + 1) })),
244
+ scaleColumns: LIKERT_COLUMNS,
247
245
  });
248
246
 
249
247
  export const FIXTURE_CSAT_DROPDOWN = base({
@@ -259,13 +257,22 @@ export const FIXTURE_CSAT_DROPDOWN = base({
259
257
  ],
260
258
  });
261
259
 
260
+ const RATING_ANCHOR_LABELS = [
261
+ 'Strongly Disagree',
262
+ 'Disagree',
263
+ 'Neutral',
264
+ 'Agree',
265
+ 'Strongly Agree',
266
+ ];
267
+
262
268
  export const FIXTURE_RATING_STAR = base({
263
269
  type: 'RATING_MATRIX',
264
270
  displayStyle: 'star',
265
271
  gridLayout: 'standard',
266
272
  showColumnHeaders: true,
267
273
  statementRows: MATRIX_ROWS_MULTI,
268
- scaleColumns: LIKERT_COLUMNS,
274
+ scaleColumns: Array.from({ length: 10 }, (_, i) => ({ id: `c${i}`, label: String(i + 1) })),
275
+ scaleAnchorLabels: RATING_ANCHOR_LABELS,
269
276
  });
270
277
 
271
278
  export const FIXTURE_RATING_NUMBERED = base({
@@ -274,6 +281,7 @@ export const FIXTURE_RATING_NUMBERED = base({
274
281
  gridLayout: 'standard',
275
282
  statementRows: MATRIX_ROWS_MULTI,
276
283
  scaleColumns: Array.from({ length: 10 }, (_, i) => ({ id: `c${i}`, label: String(i + 1) })),
284
+ scaleAnchorLabels: RATING_ANCHOR_LABELS,
277
285
  });
278
286
 
279
287
  export const FIXTURE_RATING_RADIO = base({
@@ -290,6 +298,7 @@ export const FIXTURE_RATING_EMOJI = base({
290
298
  gridLayout: 'standard',
291
299
  statementRows: MATRIX_ROW_SINGLE,
292
300
  scaleColumns: Array.from({ length: 10 }, (_, i) => ({ id: `c${i}`, label: String(i + 1) })),
301
+ scaleAnchorLabels: RATING_ANCHOR_LABELS,
293
302
  });
294
303
 
295
304
  export const FIXTURE_RATING_GRAPHICS = base({
@@ -298,7 +307,7 @@ export const FIXTURE_RATING_GRAPHICS = base({
298
307
  gridLayout: 'standard',
299
308
  statementRows: MATRIX_ROW_SINGLE,
300
309
  scaleColumns: Array.from({ length: 10 }, (_, i) => ({ id: `c${i}`, label: String(i + 1) })),
301
- scaleAnchorLabels: ['Label 1', 'Label 2', 'Label 3', 'Label 4', 'Label 5'],
310
+ scaleAnchorLabels: RATING_ANCHOR_LABELS,
302
311
  });
303
312
 
304
313
  export const FIXTURE_RATING_SLIDER = base({
@@ -25,6 +25,10 @@
25
25
  --cfm-header-company-weight: 600;
26
26
  --cfm-header-border: #e5e7eb;
27
27
  --cfm-header-padding-x: 40px;
28
+ --cfm-header-padding-y: 12px;
29
+ --cfm-header-brand-row-display: none;
30
+ --cfm-header-logo-slot-display: flex;
31
+ --cfm-header-company-slot-display: flex;
28
32
  --cfm-header-logo-col: 1;
29
33
  --cfm-header-logo-justify: start;
30
34
  --cfm-header-company-col: 1;
@@ -42,6 +46,8 @@
42
46
  --cfm-footer-logo-height: 32px;
43
47
  --cfm-footer-padding-y: 24px;
44
48
  --cfm-footer-padding-x: 40px;
49
+ --cfm-footer-logo-copyright-gap: 12px;
50
+ --cfm-footer-inner-gap: 24px;
45
51
  --cfm-footer-inner-display: grid;
46
52
  --cfm-footer-inner-direction: row;
47
53
  --cfm-footer-inner-align: end;
@@ -104,10 +110,16 @@
104
110
  --cfm-csat-unselected-opacity: 0.5;
105
111
  --cfm-csat-ring-color: var(--cfm-accent);
106
112
  --cfm-csat-cell-size: 40px;
113
+ --cfm-csat-track: var(--cfm-accent);
114
+ --cfm-csat-thumb: var(--cfm-primary);
107
115
 
108
116
  --cfm-star-color: #f59e0b;
109
117
  --cfm-badge-color: var(--cfm-primary);
110
118
  --cfm-star-unselected-opacity: 0.5;
119
+ --cfm-rating-column-label-color: #4b5563;
120
+ --cfm-rating-column-label-bg: transparent;
121
+ --cfm-rating-track: var(--cfm-accent);
122
+ --cfm-rating-thumb: var(--cfm-primary);
111
123
 
112
124
  --cfm-slider-track: var(--cfm-accent);
113
125
  --cfm-slider-thumb: var(--cfm-primary);
@@ -192,8 +204,8 @@ body {
192
204
  gap: var(--cfm-header-brand-gap, 16px);
193
205
  padding-left: var(--cfm-header-padding-x);
194
206
  padding-right: var(--cfm-header-padding-x);
195
- padding-top: 12px;
196
- padding-bottom: 12px;
207
+ padding-top: var(--cfm-header-padding-y, 12px);
208
+ padding-bottom: var(--cfm-header-padding-y, 12px);
197
209
  min-height: var(--cfm-header-height);
198
210
  box-sizing: border-box;
199
211
  }
@@ -1 +1 @@
1
- import{u as x,j as e}from"./index-CY7WMJ93.js";import{b as t}from"./vendor-BwkXDkd3.js";function p(d=150){const l=x(r=>r.config),[a,s]=t.useState(l);return t.useEffect(()=>{const r=window.setTimeout(()=>s(l),d);return()=>window.clearTimeout(r)},[l,d]),a}const h=t.memo(function(){const l=p(150),a=x(n=>n.logoPreviewDataUrl),{global:s}=l,{layout:r,colorScheme:o,buttons:c}=s,m=t.useMemo(()=>r.borderStyle==="sharp"?"rounded-none":r.borderStyle==="pill"?"rounded-[32px]":"rounded-2xl",[r.borderStyle]),i=t.useMemo(()=>r.fontStyle==="serif"?"Georgia, serif":r.fontStyle==="system"?"system-ui, sans-serif":"Inter, sans-serif",[r.fontStyle]),u=a??s.logo.url,f=t.useMemo(()=>({backgroundColor:o.background,color:o.text,borderColor:o.secondary,fontFamily:i}),[o,i]);return e.jsxs("div",{className:`overflow-hidden border shadow-md ${m}`,style:f,children:[r.showHeader&&e.jsxs("div",{className:"flex items-center gap-3 border-b px-5 py-4",style:{borderColor:o.secondary},children:[u&&e.jsx("img",{src:u,alt:"Logo",className:"max-h-6 w-auto object-contain",loading:"lazy"}),e.jsx("span",{className:"text-xs font-semibold tracking-wider",children:s.companyName})]}),e.jsxs("div",{className:"space-y-6 p-6",children:[r.showProgressBar&&e.jsxs("div",{className:"space-y-1.5",children:[e.jsxs("div",{className:"flex justify-between text-[10px] font-semibold text-slate-400",children:[e.jsx("span",{children:"Progress"}),e.jsx("span",{children:"35%"})]}),e.jsx("div",{className:"h-1.5 w-full overflow-hidden rounded-full bg-slate-100",children:e.jsx("div",{className:"h-full w-[35%] rounded-full",style:{backgroundColor:o.accent}})})]}),e.jsxs("div",{className:"space-y-3",children:[e.jsxs("h4",{className:"flex items-start gap-1.5 text-sm font-bold leading-snug",children:[r.showQuestionNumbers&&e.jsx("span",{className:"font-medium text-slate-400",children:"Q1."}),e.jsx("span",{children:s.surveyTitle}),r.showRequiredAsterisk&&e.jsx("span",{className:"text-red-500",children:"*"})]}),e.jsx("p",{className:"text-[11px] text-slate-400",children:"How would you rate your overall experience?"}),e.jsx("div",{className:"grid grid-cols-5 gap-2",children:[1,2,3,4,5].map(n=>e.jsx("div",{className:`flex h-9 items-center justify-center border text-xs font-semibold ${r.borderStyle==="pill"?"rounded-full":r.borderStyle==="sharp"?"rounded-none":"rounded-lg"}`,style:n===1?{backgroundColor:o.secondary,borderColor:o.primary,color:o.primary}:{backgroundColor:o.background,borderColor:o.secondary,color:o.text},children:n},n))})]})]}),e.jsxs("div",{className:"flex items-center justify-between border-t bg-slate-50/50 px-6 py-4",children:[r.showBackButton&&e.jsx("span",{className:`border px-3 py-1.5 text-[10px] font-semibold ${r.borderStyle==="pill"?"rounded-full":r.borderStyle==="sharp"?"rounded-none":"rounded-lg"}`,style:{borderColor:o.secondary,color:o.text},children:c.back}),e.jsx("div",{className:"flex-1"}),r.showNextButton&&e.jsx("span",{className:`px-4 py-1.5 text-[10px] font-bold ${r.borderStyle==="pill"?"rounded-full":r.borderStyle==="sharp"?"rounded-none":"rounded-lg"}`,style:{backgroundColor:o.primary,color:o.buttonText},children:c.next})]})]})});export{h as PreviewMock};
1
+ import{u as x,j as e}from"./index-Blpq4QjK.js";import{b as t}from"./vendor-BwkXDkd3.js";function p(d=150){const l=x(r=>r.config),[a,s]=t.useState(l);return t.useEffect(()=>{const r=window.setTimeout(()=>s(l),d);return()=>window.clearTimeout(r)},[l,d]),a}const h=t.memo(function(){const l=p(150),a=x(n=>n.logoPreviewDataUrl),{global:s}=l,{layout:r,colorScheme:o,buttons:c}=s,m=t.useMemo(()=>r.borderStyle==="sharp"?"rounded-none":r.borderStyle==="pill"?"rounded-[32px]":"rounded-2xl",[r.borderStyle]),i=t.useMemo(()=>r.fontStyle==="serif"?"Georgia, serif":r.fontStyle==="system"?"system-ui, sans-serif":"Inter, sans-serif",[r.fontStyle]),u=a??s.logo.url,f=t.useMemo(()=>({backgroundColor:o.background,color:o.text,borderColor:o.secondary,fontFamily:i}),[o,i]);return e.jsxs("div",{className:`overflow-hidden border shadow-md ${m}`,style:f,children:[r.showHeader&&e.jsxs("div",{className:"flex items-center gap-3 border-b px-5 py-4",style:{borderColor:o.secondary},children:[u&&e.jsx("img",{src:u,alt:"Logo",className:"max-h-6 w-auto object-contain",loading:"lazy"}),e.jsx("span",{className:"text-xs font-semibold tracking-wider",children:s.companyName})]}),e.jsxs("div",{className:"space-y-6 p-6",children:[r.showProgressBar&&e.jsxs("div",{className:"space-y-1.5",children:[e.jsxs("div",{className:"flex justify-between text-[10px] font-semibold text-slate-400",children:[e.jsx("span",{children:"Progress"}),e.jsx("span",{children:"35%"})]}),e.jsx("div",{className:"h-1.5 w-full overflow-hidden rounded-full bg-slate-100",children:e.jsx("div",{className:"h-full w-[35%] rounded-full",style:{backgroundColor:o.accent}})})]}),e.jsxs("div",{className:"space-y-3",children:[e.jsxs("h4",{className:"flex items-start gap-1.5 text-sm font-bold leading-snug",children:[r.showQuestionNumbers&&e.jsx("span",{className:"font-medium text-slate-400",children:"Q1."}),e.jsx("span",{children:s.surveyTitle}),r.showRequiredAsterisk&&e.jsx("span",{className:"text-red-500",children:"*"})]}),e.jsx("p",{className:"text-[11px] text-slate-400",children:"How would you rate your overall experience?"}),e.jsx("div",{className:"grid grid-cols-5 gap-2",children:[1,2,3,4,5].map(n=>e.jsx("div",{className:`flex h-9 items-center justify-center border text-xs font-semibold ${r.borderStyle==="pill"?"rounded-full":r.borderStyle==="sharp"?"rounded-none":"rounded-lg"}`,style:n===1?{backgroundColor:o.secondary,borderColor:o.primary,color:o.primary}:{backgroundColor:o.background,borderColor:o.secondary,color:o.text},children:n},n))})]})]}),e.jsxs("div",{className:"flex items-center justify-between border-t bg-slate-50/50 px-6 py-4",children:[r.showBackButton&&e.jsx("span",{className:`border px-3 py-1.5 text-[10px] font-semibold ${r.borderStyle==="pill"?"rounded-full":r.borderStyle==="sharp"?"rounded-none":"rounded-lg"}`,style:{borderColor:o.secondary,color:o.text},children:c.back}),e.jsx("div",{className:"flex-1"}),r.showNextButton&&e.jsx("span",{className:`px-4 py-1.5 text-[10px] font-bold ${r.borderStyle==="pill"?"rounded-full":r.borderStyle==="sharp"?"rounded-none":"rounded-lg"}`,style:{backgroundColor:o.primary,color:o.buttonText},children:c.next})]})]})});export{h as PreviewMock};
@@ -0,0 +1 @@
1
+ import{u as m,j as e,T as R,S as C,R as d,C as i,N as t,a as z,M as A}from"./index-Blpq4QjK.js";import"./vendor-BwkXDkd3.js";function b({values:o,onPatch:r}){return e.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[e.jsx(i,{label:"Label text color",value:o.columnLabelColor,onChange:a=>r({columnLabelColor:a})}),e.jsx(i,{label:"Label background",value:o.columnLabelBgColor,onChange:a=>r({columnLabelBgColor:a})})]})}function k({values:o,onPatch:r}){return e.jsxs("div",{className:"grid grid-cols-1 gap-3 sm:grid-cols-2",children:[e.jsxs("label",{className:"space-y-1 sm:col-span-2",children:[e.jsx("span",{className:"text-[10px] font-semibold uppercase tracking-wider text-slate-500",children:"Min hint label"}),e.jsx("input",{className:"w-full rounded-lg border border-white/10 bg-slate-950/50 px-3 py-2 text-sm text-white",value:o.hintMinText,onChange:a=>r({hintMinText:a.target.value})})]}),e.jsxs("label",{className:"space-y-1 sm:col-span-2",children:[e.jsx("span",{className:"text-[10px] font-semibold uppercase tracking-wider text-slate-500",children:"Max hint label"}),e.jsx("input",{className:"w-full rounded-lg border border-white/10 bg-slate-950/50 px-3 py-2 text-sm text-white",value:o.hintMaxText,onChange:a=>r({hintMaxText:a.target.value})})]}),e.jsx(i,{label:"Hint text color",value:o.hintLabelColor,onChange:a=>r({hintLabelColor:a})}),e.jsx(i,{label:"Hint label background",value:o.hintLabelBgColor,onChange:a=>r({hintLabelBgColor:a})})]})}function j({values:o,onPatch:r,count:a=11}){return e.jsxs("div",{className:"space-y-3",children:[e.jsx(C,{label:"Number label style",value:o.numberLabelMode,onChange:c=>r({numberLabelMode:c}),options:[{value:"traffic",label:"Traffic light (default)"},{value:"monochrome",label:"Monochrome (one color)"},{value:"individual",label:"Individual per number"}]}),o.numberLabelMode==="monochrome"&&e.jsx(i,{label:"Monochrome color",value:o.monochromeNumberColor,onChange:c=>r({monochromeNumberColor:c})}),o.numberLabelMode==="individual"&&e.jsx("div",{className:"grid grid-cols-2 gap-2 sm:grid-cols-3",children:Array.from({length:a},(c,u)=>e.jsx(i,{label:`#${u}`,value:o.individualNumberColors[u]??"#9CA3AF",onChange:v=>{const x=[...o.individualNumberColors];x[u]=v,r({individualNumberColors:x})}},u))})]})}function s({title:o,children:r}){return e.jsxs("div",{className:"space-y-3 rounded-xl border border-white/5 bg-slate-950/20 p-4",children:[e.jsx("h4",{className:"text-xs font-semibold uppercase tracking-wider text-slate-400",children:o}),r]})}function O({type:o,activeFormat:r}){const a=m(l=>l.config.questionTypes[o]),c=m(l=>l.updateQuestionType),u=m(l=>l.previewMultiStatementByType),v=m(l=>l.setPreviewMultiStatementForType),x=m(l=>l.heatmapPreviewPin);if(!a)return e.jsx("p",{className:"text-sm text-slate-500",children:"No configuration available for this type."});const n=l=>c(o,l),M=A.includes(o),y=!!u[o],h=r.includes("star"),g=r.includes("emoji"),S=r.includes("numbered"),N=r.includes("drag"),w=r.includes("dropdown"),f=r.includes("radio"),T=r.includes("graphics"),B=r==="CFM_bipolar",p=g||h||S||f,P=a.optionStyle??a.cardStyle??"outlined",L=a.buttonStyle??"numbered";return e.jsxs("div",{className:"space-y-4",children:[M&&e.jsx(R,{label:"Preview multiple statements",checked:y,onChange:l=>v(o,l)}),o==="MCQ"&&"optionGap"in a&&e.jsx(s,{title:"Option style",children:e.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[e.jsx(C,{label:"Option style",value:a.optionStyle??a.cardStyle??"outlined",onChange:l=>n({optionStyle:l}),options:[{value:"outlined",label:"Outlined"},{value:"filled",label:"Filled"},{value:"minimal",label:"Minimal"}]}),e.jsx(d,{label:"Option gap",value:a.optionGap,onChange:l=>n({optionGap:l}),min:0,max:32}),e.jsx(d,{label:"Border radius",value:a.borderRadius,onChange:l=>n({borderRadius:l}),min:0,max:24}),e.jsx(d,{label:"Option padding",value:a.optionPadding??12,onChange:l=>n({optionPadding:l}),min:4,max:24}),e.jsx(i,{label:"Hover border",value:a.hoverBorderColor,onChange:l=>n({hoverBorderColor:l})}),P==="filled"&&e.jsx(i,{label:"Unselected fill color",value:a.filledOptionBg??"#F9FAFB",onChange:l=>n({filledOptionBg:l})})]})}),o==="TEXTFIELD"&&"defaultPlaceholder"in a&&e.jsx(s,{title:"Text input",children:e.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[e.jsxs("label",{className:"space-y-1 sm:col-span-2",children:[e.jsx("span",{className:"text-[10px] font-semibold uppercase tracking-wider text-slate-500",children:"Default placeholder"}),e.jsx("input",{className:"w-full rounded-lg border border-white/10 bg-slate-950/50 px-3 py-2 text-sm text-white",value:a.defaultPlaceholder,onChange:l=>n({defaultPlaceholder:l.target.value})})]}),e.jsx(t,{label:"Input radius (px)",value:a.inputRadius,onChange:l=>n({inputRadius:l}),min:0,max:24}),e.jsx(i,{label:"Border color",value:a.borderColor,onChange:l=>n({borderColor:l})}),e.jsx(i,{label:"Placeholder color",value:a.placeholderColor,onChange:l=>n({placeholderColor:l})}),e.jsx(t,{label:"Input height (px)",value:a.inputHeight,onChange:l=>n({inputHeight:l}),min:32,max:64}),e.jsx(t,{label:"Input padding (px)",value:a.inputPadding,onChange:l=>n({inputPadding:l}),min:8,max:24})]})}),o==="NPS_SCALE"&&"buttonStyle"in a&&e.jsxs(e.Fragment,{children:[e.jsxs(s,{title:"NPS buttons",children:[e.jsx(C,{label:"Button style",value:a.buttonStyle,onChange:l=>n({buttonStyle:l}),options:[{value:"standard",label:"Radio"},{value:"numbered",label:"Numbered badges"}]}),e.jsx("p",{className:"text-xs text-slate-500",children:"Selected cell and focus ring come from Theme → Question card."})]}),e.jsx(s,{title:"Hint labels",children:e.jsx(k,{values:a,onPatch:n})}),L==="numbered"&&e.jsx(s,{title:"Number labels",children:e.jsx(j,{values:a,onPatch:n})}),e.jsx(s,{title:"Track & cells",children:e.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[e.jsx(t,{label:"Cell size (px)",value:a.cellSize,onChange:l=>n({cellSize:l}),min:24,max:56}),e.jsx(t,{label:"Cell gap (px)",value:a.cellGap,onChange:l=>n({cellGap:l}),min:0,max:16}),e.jsx(i,{label:"Track bar",value:a.trackBarColor,onChange:l=>n({trackBarColor:l})}),e.jsx(i,{label:"Track highlight",value:a.trackHighlightColor,onChange:l=>n({trackHighlightColor:l})})]})})]}),o==="CFM_MATRIX"&&"rowLabelWidth"in a&&e.jsxs(e.Fragment,{children:[e.jsx(s,{title:"Matrix layout",children:e.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[e.jsx(t,{label:"Row label width (px)",value:a.rowLabelWidth,onChange:l=>n({rowLabelWidth:l}),min:120,max:280}),B&&e.jsx(t,{label:"Bipolar column width (%)",value:a.bipolarColumnWidthPercent,onChange:l=>n({bipolarColumnWidthPercent:l}),min:15,max:40}),e.jsx(t,{label:"Cell padding (px)",value:a.cellPadding,onChange:l=>n({cellPadding:l}),min:4,max:24})]})}),e.jsx(s,{title:"Label items",children:e.jsx(b,{values:a,onPatch:n})})]}),o==="CSAT_MATRIX"&&"emojiSize"in a&&e.jsxs(e.Fragment,{children:[(g||p||h)&&e.jsxs(s,{title:`${z.CSAT_MATRIX} — ${r.replace("CSAT_","")}`,children:[e.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[g&&e.jsx(t,{label:"Emoji size (px)",value:a.emojiSize,onChange:l=>n({emojiSize:l}),min:16,max:40}),h&&e.jsx(i,{label:"Star color",value:a.accentColor,onChange:l=>n({accentColor:l})}),p&&e.jsx(d,{label:"Unselected opacity",value:Math.round(a.unselectedOpacity*100),onChange:l=>n({unselectedOpacity:l/100}),min:10,max:100})]}),e.jsx("p",{className:"text-xs text-slate-500",children:"Selected cell color and focus ring come from Theme → Question card. Column labels appear above stars in the star format preview."})]}),T&&e.jsx(s,{title:"CSAT graphics track",children:e.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[e.jsx(i,{label:"Track",value:a.trackColor,onChange:l=>n({trackColor:l})}),e.jsx(i,{label:"Thumb",value:a.thumbColor,onChange:l=>n({thumbColor:l})})]})}),e.jsx(s,{title:"Label items",children:e.jsx(b,{values:a,onPatch:n})}),S&&e.jsx(s,{title:"Number labels",children:e.jsx(j,{values:a,onPatch:n,count:10})})]}),o==="RATING_MATRIX"&&"unselectedStarOpacity"in a&&e.jsxs(e.Fragment,{children:[(g||p||h)&&e.jsxs(s,{title:`Rating — ${r.replace("RATING_","")}`,children:[e.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[g&&e.jsx(t,{label:"Emoji size (px)",value:a.emojiSize,onChange:l=>n({emojiSize:l}),min:16,max:40}),h&&e.jsx(i,{label:"Star color",value:a.starColor,onChange:l=>n({starColor:l})}),p&&e.jsx(d,{label:"Unselected opacity",value:Math.round(a.unselectedStarOpacity*100),onChange:l=>n({unselectedStarOpacity:l/100}),min:10,max:100})]}),e.jsx("p",{className:"text-xs text-slate-500",children:"Selected cell color and focus ring come from Theme → Question card."})]}),T&&e.jsx(s,{title:"Rating graphics track",children:e.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[e.jsx(i,{label:"Track",value:a.trackColor,onChange:l=>n({trackColor:l})}),e.jsx(i,{label:"Thumb",value:a.thumbColor,onChange:l=>n({thumbColor:l})})]})}),e.jsxs(s,{title:"Label items",children:[e.jsx("p",{className:"text-xs text-slate-500",children:"Label items styles the anchor label pills (max 5), not column numbers."}),e.jsx(b,{values:a,onPatch:n})]}),S&&e.jsx(s,{title:"Number labels",children:e.jsx(j,{values:a,onPatch:n,count:10})})]}),o==="SLIDER_MATRIX"&&"tickColor"in a&&e.jsxs(e.Fragment,{children:[e.jsx(s,{title:"Slider track",children:e.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[e.jsx(i,{label:"Track",value:a.trackColor,onChange:l=>n({trackColor:l})}),e.jsx(i,{label:"Thumb",value:a.thumbColor,onChange:l=>n({thumbColor:l})}),e.jsx(t,{label:"Row label width (px)",value:a.rowLabelWidth,onChange:l=>n({rowLabelWidth:l}),min:120,max:280}),e.jsx(i,{label:"Row band",value:a.rowBandColor,onChange:l=>n({rowBandColor:l})}),e.jsx(t,{label:"Tick badge size (px)",value:a.tickBadgeSize,onChange:l=>n({tickBadgeSize:l}),min:20,max:40})]})}),e.jsx(s,{title:"Anchor hint labels",children:e.jsx(k,{values:a,onPatch:n})}),e.jsx(s,{title:"Tick label items",children:e.jsx(b,{values:a,onPatch:n})}),e.jsx(s,{title:"Tick number labels",children:e.jsx(j,{values:a,onPatch:n,count:11})})]}),o==="FILE_UPLOAD"&&"dropzoneStyle"in a&&e.jsx(s,{title:"Dropzone",children:e.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[e.jsx(C,{label:"Dropzone border",value:a.dropzoneStyle,onChange:l=>n({dropzoneStyle:l}),options:[{value:"dashed",label:"Dashed"},{value:"solid",label:"Solid"}]}),e.jsx(i,{label:"Border color",value:a.borderColor??a.accentColor,onChange:l=>n({borderColor:l})}),e.jsx(i,{label:"Fill color",value:a.dropzoneFillColor,onChange:l=>n({dropzoneFillColor:l})}),e.jsx(t,{label:"Padding (px)",value:a.dropzonePadding,onChange:l=>n({dropzonePadding:l}),min:12,max:48})]})}),o==="TEXT_AND_MEDIA"&&"mediaMaxWidth"in a&&e.jsx(s,{title:"Text & media",children:e.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[e.jsx(d,{label:"Media max width (%)",value:a.mediaMaxWidth,onChange:l=>n({mediaMaxWidth:l}),min:50,max:100}),e.jsx(d,{label:"Media corner radius",value:a.mediaRadius,onChange:l=>n({mediaRadius:l}),min:0,max:24}),e.jsx(i,{label:"Caption color",value:a.captionColor,onChange:l=>n({captionColor:l})}),e.jsx(t,{label:"Caption size (px)",value:a.captionSize,onChange:l=>n({captionSize:l}),min:10,max:24})]})}),o==="HEATMAP"&&"pinColor"in a&&e.jsx(s,{title:"Heatmap pin",children:e.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[e.jsx(i,{label:"Pin color",value:a.pinColor,onChange:l=>n({pinColor:l})}),e.jsx(i,{label:"Pin border",value:a.pinBorderColor,onChange:l=>n({pinBorderColor:l})}),e.jsx(d,{label:"Pin size",value:a.pinSize,onChange:l=>n({pinSize:l}),min:8,max:32}),e.jsx(d,{label:"Overlay opacity",value:Math.round(a.overlayOpacity*100),onChange:l=>n({overlayOpacity:l/100}),min:0,max:80}),e.jsxs("p",{className:"text-xs text-slate-500 sm:col-span-2",children:["Click the heatmap image in the preview to place a pin and see styling update live. Pin preview position: ",Math.round(x.x*100),"%, ",Math.round(x.y*100),"%"]})]})}),o==="RANK_ORDER"&&"drag"in a&&e.jsx(s,{title:N?"Drag & drop":w?"Dropdown rank":"Rank order",children:e.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[N&&e.jsxs(e.Fragment,{children:[e.jsx(i,{label:"Item background",value:a.drag.itemBg,onChange:l=>n({drag:{...a.drag,itemBg:l}})}),e.jsx(i,{label:"Drag handle",value:a.drag.dragHandleColor,onChange:l=>n({drag:{...a.drag,dragHandleColor:l}})}),e.jsx(i,{label:"Rank badge",value:a.drag.rankBadgeColor,onChange:l=>n({drag:{...a.drag,rankBadgeColor:l}})})]}),w&&e.jsxs(e.Fragment,{children:[e.jsx(i,{label:"Item background",value:a.dropdown.itemBg,onChange:l=>n({dropdown:{...a.dropdown,itemBg:l}})}),e.jsx(i,{label:"List hover background",value:a.dropdown.itemHoverBg??"#F3F4F6",onChange:l=>n({dropdown:{...a.dropdown,itemHoverBg:l}})}),e.jsx(i,{label:"Select border",value:a.dropdown.selectBorderColor,onChange:l=>n({dropdown:{...a.dropdown,selectBorderColor:l}})}),e.jsx(i,{label:"Rank badge",value:a.dropdown.rankBadgeColor,onChange:l=>n({dropdown:{...a.dropdown,rankBadgeColor:l}})})]})]})})]})}export{O as TypePanel};