commons-shared-web-ui 0.0.27 → 0.0.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.d.ts CHANGED
@@ -58,6 +58,12 @@ interface FormSchema {
58
58
  metadata?: {
59
59
  [key: string]: any;
60
60
  };
61
+ /**
62
+ * When true, top-level GROUP children of sectionConfig are rendered as
63
+ * a horizontal stepper at the top, showing one section at a time.
64
+ * Navigate between sections via the Next/Previous buttons in the host app.
65
+ */
66
+ sectionStepper?: boolean;
61
67
  sectionConfig?: SectionConfig;
62
68
  stepperConfig?: StepperConfig;
63
69
  submitConfig?: SubmitConfig;
@@ -1087,6 +1093,15 @@ declare class SmartFormComponent implements OnInit, OnChanges, OnDestroy {
1087
1093
  fileAdded: EventEmitter<any>;
1088
1094
  fileUploadFinished: EventEmitter<any>;
1089
1095
  fileRemoved: EventEmitter<any>;
1096
+ /** Emitted whenever the active section step changes. Carries current state so the
1097
+ * host can show/hide Previous/Next/Submit buttons in its own footer. */
1098
+ stepChange: EventEmitter<{
1099
+ currentStep: number;
1100
+ totalSteps: number;
1101
+ isFirst: boolean;
1102
+ isLast: boolean;
1103
+ stepLabel: string;
1104
+ }>;
1090
1105
  formSchema: FormSchema;
1091
1106
  formGroup: FormGroup;
1092
1107
  fieldList: FieldConfig[];
@@ -1094,9 +1109,27 @@ declare class SmartFormComponent implements OnInit, OnChanges, OnDestroy {
1094
1109
  currentStep: number;
1095
1110
  isLoading: boolean;
1096
1111
  isDraftLoading: boolean;
1112
+ /** True when sectionStepper mode is active (SECTION form with top-level GROUPs as steps). */
1113
+ isSectionStepper: boolean;
1114
+ /** Index of the currently visible section step. */
1115
+ currentSectionStep: number;
1116
+ /** Flat list of top-level GROUP FieldConfigs that become the stepper steps. */
1117
+ sectionSteps: FieldConfig[];
1118
+ /** Validation state per section step — drives badge colour/icon. */
1119
+ stepValidationStates: ('untouched' | 'valid' | 'warning')[];
1120
+ /** Flat field-name lists per step used for targeted validation. */
1121
+ private stepFieldNames;
1122
+ /** Controls skeleton visibility. Stays false until schema is parsed AND
1123
+ * any EDIT-mode data fetch completes, but always shows for at least
1124
+ * SKELETON_MIN_MS so the animation is visible even on fast loads. */
1125
+ isFormReady: boolean;
1126
+ private readonly SKELETON_MIN_MS;
1127
+ private _skeletonStart;
1097
1128
  constructor(fb: FormBuilder, controller: SmartFormController, expressionService: ExpressionService, http: HttpClient, snackbarService: SnackbarService, router: Router);
1098
1129
  ngOnInit(): void;
1099
1130
  loadEditData(): void;
1131
+ /** Flips isFormReady=true after the skeleton has been visible for at least SKELETON_MIN_MS. */
1132
+ private _markReady;
1100
1133
  ngOnChanges(changes: SimpleChanges): void;
1101
1134
  ngOnDestroy(): void;
1102
1135
  parseFormJson(): void;
@@ -1133,6 +1166,25 @@ declare class SmartFormComponent implements OnInit, OnChanges, OnDestroy {
1133
1166
  get canGoNext(): boolean;
1134
1167
  get canGoPrevious(): boolean;
1135
1168
  get currentStepConfig(): FieldConfig | undefined;
1169
+ /** Advance to the next section step. Called by the host footer "Next" button.
1170
+ * Validates the current step first — marks it valid (green) or warning (orange). */
1171
+ navigateToNext(): void;
1172
+ /** Go back to the previous section step. Called by the host footer "Previous" button. */
1173
+ navigateToPrevious(): void;
1174
+ /** Jump directly to a specific section step by index.
1175
+ * Validates the step being left so the badge state updates correctly. */
1176
+ goToSectionStep(index: number): void;
1177
+ get isSectionStepFirst(): boolean;
1178
+ get isSectionStepLast(): boolean;
1179
+ /** Returns the SectionConfig for a given step — passed to lib-form-section.
1180
+ * The outer label is intentionally omitted because the stepper nav already
1181
+ * displays it; showing it again inside the content would be redundant. */
1182
+ getSectionStepConfig(step: FieldConfig): any;
1183
+ private _emitStepChange;
1184
+ /** Marks all controls in the given step as touched and records valid/warning state. */
1185
+ private _validateStep;
1186
+ /** Recursively collects all leaf field names from a set of FieldConfigs. */
1187
+ private _collectFieldNames;
1136
1188
  get nextLabel(): string;
1137
1189
  get submitLabel(): string;
1138
1190
  get previousLabel(): string;
@@ -1146,7 +1198,7 @@ declare class SmartFormComponent implements OnInit, OnChanges, OnDestroy {
1146
1198
  private getButtonByActionKind;
1147
1199
  private navigateTo;
1148
1200
  static ɵfac: i0.ɵɵFactoryDeclaration<SmartFormComponent, never>;
1149
- static ɵcmp: i0.ɵɵComponentDeclaration<SmartFormComponent, "lib-smart-form", never, { "formJson": { "alias": "formJson"; "required": false; }; "initialValues": { "alias": "initialValues"; "required": false; }; "enableDraftAutoSave": { "alias": "enableDraftAutoSave"; "required": false; }; "labels": { "alias": "labels"; "required": false; }; "mode": { "alias": "mode"; "required": false; }; "readOnly": { "alias": "readOnly"; "required": false; }; }, { "submit": "submit"; "draftSave": "draftSave"; "actionClick": "actionClick"; "valueChange": "valueChange"; "fileAdded": "fileAdded"; "fileUploadFinished": "fileUploadFinished"; "fileRemoved": "fileRemoved"; }, never, never, false, never>;
1201
+ static ɵcmp: i0.ɵɵComponentDeclaration<SmartFormComponent, "lib-smart-form", never, { "formJson": { "alias": "formJson"; "required": false; }; "initialValues": { "alias": "initialValues"; "required": false; }; "enableDraftAutoSave": { "alias": "enableDraftAutoSave"; "required": false; }; "labels": { "alias": "labels"; "required": false; }; "mode": { "alias": "mode"; "required": false; }; "readOnly": { "alias": "readOnly"; "required": false; }; }, { "submit": "submit"; "draftSave": "draftSave"; "actionClick": "actionClick"; "valueChange": "valueChange"; "fileAdded": "fileAdded"; "fileUploadFinished": "fileUploadFinished"; "fileRemoved": "fileRemoved"; "stepChange": "stepChange"; }, never, never, false, never>;
1150
1202
  }
1151
1203
 
1152
1204
  declare class FormSectionComponent implements OnInit, OnDestroy {
@@ -1159,6 +1211,8 @@ declare class FormSectionComponent implements OnInit, OnDestroy {
1159
1211
  * Each element is a FormGroup representing one repeater instance.
1160
1212
  */
1161
1213
  repeaterFormArray: FormArray;
1214
+ /** Tracks which accordion panels are open (by index). New instances start expanded. */
1215
+ expandedInstances: Set<number>;
1162
1216
  /**
1163
1217
  * The key under which the FormArray is registered in the root formGroup.
1164
1218
  * Falls back to config.name or a generated key.
@@ -1171,6 +1225,8 @@ declare class FormSectionComponent implements OnInit, OnDestroy {
1171
1225
  private createInstanceGroup;
1172
1226
  addInstance(): void;
1173
1227
  removeInstance(index: number): void;
1228
+ toggleInstance(index: number): void;
1229
+ isExpanded(index: number): boolean;
1174
1230
  getInstanceGroup(index: number): FormGroup;
1175
1231
  get instanceGroups(): FormGroup[];
1176
1232
  /** For non-allowMulti sections we simply pass the root formGroup down */
@@ -1262,6 +1318,8 @@ declare class FormFieldComponent implements OnInit, OnDestroy {
1262
1318
  isExpanded?: boolean;
1263
1319
  }[];
1264
1320
  private _nextInstanceId;
1321
+ /** Tracks open accordion panels for standard (non-multiSave) GROUP repeaters. */
1322
+ expandedGroupInstances: Set<number>;
1265
1323
  /**
1266
1324
  * Key used to register the GROUP control on the parent formGroup.
1267
1325
  * Priority: sectionConfig.name > field.name > camelCase(label) > '__group__'
@@ -1283,6 +1341,8 @@ declare class FormFieldComponent implements OnInit, OnDestroy {
1283
1341
  editGroupInstance(index: number): void;
1284
1342
  toggleExpandGroupInstance(index: number): void;
1285
1343
  removeGroupInstance(index: number, force?: boolean): void;
1344
+ toggleGroupAccordion(index: number): void;
1345
+ isGroupExpanded(index: number): boolean;
1286
1346
  trackByInstanceId(_: number, item: {
1287
1347
  id: number;
1288
1348
  fg: FormGroup;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "commons-shared-web-ui",
3
- "version": "0.0.27",
3
+ "version": "0.0.28",
4
4
  "peerDependencies": {
5
5
  "@angular/animations": "20.3.15",
6
6
  "@angular/cdk": "20.2.14",
@@ -14,6 +14,9 @@ $default-smart-form-config: (
14
14
  font-family: ('Inter', 'Segoe UI', sans-serif),
15
15
  font-size-base: 0.875rem,
16
16
 
17
+ // ── Accent colour (flows from app primary; replaces hard-coded blue) ────────
18
+ accent-color: #3B82F6,
19
+
17
20
  // ── Form container ──────────────────────────────────────────────────────────
18
21
  form-bg: #ffffff,
19
22
  form-padding: 24px,
@@ -21,6 +24,7 @@ $default-smart-form-config: (
21
24
  form-border: none,
22
25
  form-shadow: 0 1px 3px rgba(0, 0, 0, 0.06),
23
26
  form-max-width: 1200px,
27
+ form-section-gap: 24px,
24
28
 
25
29
  // ── Form header (title + description) ──────────────────────────────────────
26
30
  form-title-size: 1.5rem,
@@ -29,7 +33,7 @@ $default-smart-form-config: (
29
33
  form-desc-size: 0.875rem,
30
34
  form-desc-color: #6B7280,
31
35
 
32
- // ── Section / GROUP card ────────────────────────────────────────────────────
36
+ // ── Section / GROUP — flat layout (no card shell) ───────────────────────────
33
37
  section-bg: #ffffff,
34
38
  section-border: 1px solid #E5E7EB,
35
39
  section-radius: 10px,
@@ -42,6 +46,19 @@ $default-smart-form-config: (
42
46
  section-label-color: #1F2937,
43
47
  section-label-border: 2px solid #E5E7EB,
44
48
 
49
+ // ── Section heading left-accent bar ─────────────────────────────────────────
50
+ section-header-accent-width: 4px,
51
+ section-header-accent-color: #3B82F6,
52
+ section-header-bg: #F9FAFB,
53
+
54
+ // ── Repeater accordion ──────────────────────────────────────────────────────
55
+ repeater-accordion-header-bg: #F9FAFB,
56
+ repeater-accordion-header-color: #1F2937,
57
+ repeater-accordion-active-bg: #EFF6FF,
58
+ repeater-badge-bg: #E5E7EB,
59
+ repeater-badge-color: #374151,
60
+ section-border-radius-inner: 8px,
61
+
45
62
  // ── GROUP repeater instance card ────────────────────────────────────────────
46
63
  instance-bg: #F9FAFB,
47
64
  instance-border: 1px solid #E5E7EB,
@@ -51,8 +68,13 @@ $default-smart-form-config: (
51
68
  instance-num-color: #4B5563,
52
69
  instance-num-size: 0.8125rem,
53
70
 
54
- // ── Grid gap ────────────────────────────────────────────────────────────────
71
+ // ── Grid gap + field gap ─────────────────────────────────────────────────────
55
72
  grid-gap: 16px,
73
+ field-gap: 6px,
74
+
75
+ // ── Focus ring ──────────────────────────────────────────────────────────────
76
+ focus-ring-width: 3px,
77
+ focus-ring-offset: 0,
56
78
 
57
79
  // ── Field label ─────────────────────────────────────────────────────────────
58
80
  label-size: 0.875rem,
@@ -119,7 +141,7 @@ $default-smart-form-config: (
119
141
  generated-padding: 0.625rem 0.875rem,
120
142
 
121
143
  // ── File upload drop-zone ───────────────────────────────────────────────────
122
- dropzone-bg: #FFFAF1,
144
+ dropzone-bg: #F8FAFC,
123
145
  dropzone-border: 1.5px dashed #CBD5E1,
124
146
  dropzone-radius: 12px,
125
147
  dropzone-hover-bg: #EFF6FF,
@@ -127,6 +149,7 @@ $default-smart-form-config: (
127
149
  dropzone-over-border: #3B82F6,
128
150
  dropzone-over-shadow: 0 0 0 4px rgba(59, 130, 246, 0.12),
129
151
  dropzone-icon-color: #94A3B8,
152
+ dropzone-icon-bg: rgba(59, 130, 246, 0.10),
130
153
  dropzone-link-color: #3B82F6,
131
154
  dropzone-hint-color: #64748B,
132
155
 
@@ -194,6 +217,31 @@ $default-smart-form-config: (
194
217
  btn-font-weight: 600,
195
218
  btn-transition: all 0.2s ease,
196
219
  btn-disabled-opacity: 0.55,
220
+
221
+ // ── Repeater button sizing ──────────────────────────────────────────────────
222
+ btn-remove-padding: 0.375rem 0.75rem,
223
+ btn-remove-font-size: 0.875rem,
224
+ btn-remove-font-weight: 500,
225
+
226
+ btn-add-padding: 0.625rem 1.5rem,
227
+ btn-add-font-size: 0.875rem,
228
+ btn-add-font-weight: 600,
229
+
230
+ // ── Instance card polish ────────────────────────────────────────────────────
231
+ stepper-connector-display: block,
232
+ instance-shadow: none,
233
+ instance-hover-shadow: none,
234
+ instance-transition: none,
235
+ instance-header-divider: none,
236
+ instance-header-pb: 0px,
237
+ instance-header-mb: 0px,
238
+ instance-num-weight: 600,
239
+
240
+ // ── Section stepper accent (top stripe + active badge) ──────────────────────
241
+ section-stepper-accent: #6366F1,
242
+ section-stepper-accent-end: #8B5CF6,
243
+ section-stepper-active-label: #4F46E5,
244
+ section-stepper-active-glow: rgba(99, 102, 241, 0.25),
197
245
  );
198
246
 
199
247
  // ── Theme 2 — Vibrant & Modern (dark) ────────────────────────────────────────
@@ -202,6 +250,9 @@ $theme-2-smart-form-config: (
202
250
  font-family: ('Poppins', 'Segoe UI', sans-serif),
203
251
  font-size-base: 0.875rem,
204
252
 
253
+ // ── Accent colour ───────────────────────────────────────────────────────────
254
+ accent-color: #818CF8,
255
+
205
256
  // ── Form container ──────────────────────────────────────────────────────────
206
257
  form-bg: #0F172A,
207
258
  form-padding: 28px,
@@ -209,6 +260,7 @@ $theme-2-smart-form-config: (
209
260
  form-border: 1px solid rgba(255, 255, 255, 0.06),
210
261
  form-shadow: 0 8px 32px rgba(0, 0, 0, 0.4),
211
262
  form-max-width: 1200px,
263
+ form-section-gap: 24px,
212
264
 
213
265
  // ── Form header ─────────────────────────────────────────────────────────────
214
266
  form-title-size: 1.75rem,
@@ -217,7 +269,7 @@ $theme-2-smart-form-config: (
217
269
  form-desc-size: 0.9rem,
218
270
  form-desc-color: #94A3B8,
219
271
 
220
- // ── Section / GROUP card ────────────────────────────────────────────────────
272
+ // ── Section / GROUP flat layout ───────────────────────────────────────────
221
273
  section-bg: #1E293B,
222
274
  section-border: 1px solid rgba(255, 255, 255, 0.08),
223
275
  section-radius: 12px,
@@ -230,6 +282,19 @@ $theme-2-smart-form-config: (
230
282
  section-label-color: #E2E8F0,
231
283
  section-label-border: 2px solid #334155,
232
284
 
285
+ // ── Section heading left-accent bar ─────────────────────────────────────────
286
+ section-header-accent-width: 4px,
287
+ section-header-accent-color: #818CF8,
288
+ section-header-bg: #162032,
289
+
290
+ // ── Repeater accordion ──────────────────────────────────────────────────────
291
+ repeater-accordion-header-bg: #162032,
292
+ repeater-accordion-header-color: #E2E8F0,
293
+ repeater-accordion-active-bg: rgba(129, 140, 248, 0.10),
294
+ repeater-badge-bg: #334155,
295
+ repeater-badge-color: #CBD5E1,
296
+ section-border-radius-inner: 8px,
297
+
233
298
  // ── GROUP repeater instance card ────────────────────────────────────────────
234
299
  instance-bg: #162032,
235
300
  instance-border: 1px solid rgba(255, 255, 255, 0.07),
@@ -239,8 +304,13 @@ $theme-2-smart-form-config: (
239
304
  instance-num-color: #94A3B8,
240
305
  instance-num-size: 0.8125rem,
241
306
 
242
- // ── Grid gap ────────────────────────────────────────────────────────────────
307
+ // ── Grid gap + field gap ─────────────────────────────────────────────────────
243
308
  grid-gap: 16px,
309
+ field-gap: 6px,
310
+
311
+ // ── Focus ring ──────────────────────────────────────────────────────────────
312
+ focus-ring-width: 3px,
313
+ focus-ring-offset: 0,
244
314
 
245
315
  // ── Field label ─────────────────────────────────────────────────────────────
246
316
  label-size: 0.875rem,
@@ -315,6 +385,7 @@ $theme-2-smart-form-config: (
315
385
  dropzone-over-border: #818CF8,
316
386
  dropzone-over-shadow: 0 0 0 4px rgba(129, 140, 248, 0.2),
317
387
  dropzone-icon-color: #475569,
388
+ dropzone-icon-bg: rgba(129, 140, 248, 0.12),
318
389
  dropzone-link-color: #818CF8,
319
390
  dropzone-hint-color: #64748B,
320
391
 
@@ -382,8 +453,90 @@ $theme-2-smart-form-config: (
382
453
  btn-font-weight: 600,
383
454
  btn-transition: all 0.2s ease,
384
455
  btn-disabled-opacity: 0.45,
456
+
457
+ // ── Repeater button sizing ──────────────────────────────────────────────────
458
+ btn-remove-padding: 0.375rem 0.75rem,
459
+ btn-remove-font-size: 0.875rem,
460
+ btn-remove-font-weight: 600,
461
+
462
+ btn-add-padding: 0.625rem 1.5rem,
463
+ btn-add-font-size: 0.875rem,
464
+ btn-add-font-weight: 600,
465
+
466
+ // ── Instance card polish ────────────────────────────────────────────────────
467
+ stepper-connector-display: block,
468
+ instance-shadow: 0 2px 8px rgba(0, 0, 0, 0.3),
469
+ instance-hover-shadow: 0 4px 16px rgba(0, 0, 0, 0.4),
470
+ instance-transition: box-shadow 0.15s ease,
471
+ instance-header-divider: 1px solid #1a253a,
472
+ instance-header-pb: 12px,
473
+ instance-header-mb: 16px,
474
+ instance-num-weight: 600,
475
+
476
+ // ── Section stepper accent ──────────────────────────────────────────────────
477
+ section-stepper-accent: #818CF8,
478
+ section-stepper-accent-end: #A5B4FC,
479
+ section-stepper-active-label: #A5B4FC,
480
+ section-stepper-active-glow: rgba(129, 140, 248, 0.25),
385
481
  );
386
482
 
483
+ // ── Theme 3 — Campus Students (campus red accent) ─────────────────────────────
484
+ $type1-config: map.merge($default-smart-form-config, (
485
+ input-focus-border: #E63E30,
486
+ input-focus-shadow: 0 0 0 3px rgba(230, 62, 48, 0.08),
487
+ chip-selected-bg: #E63E30,
488
+ chip-selected-border: #E63E30,
489
+ switch-track-on: #E63E30,
490
+
491
+ section-bg: transparent,
492
+ section-border: none,
493
+ section-shadow: none,
494
+ section-header-accent-color: #E63E30,
495
+
496
+ step-active-bg: #E63E30,
497
+ step-active-label: #C13320,
498
+ stepper-connector-display: none,
499
+
500
+ btn-add-color: #E63E30,
501
+ btn-add-border: 1.5px dashed rgba(230, 62, 48, 0.3),
502
+ btn-add-hover-bg: rgba(230, 62, 48, 0.04),
503
+ btn-add-hover-border: rgba(230, 62, 48, 0.6),
504
+
505
+ btn-remove-bg: transparent,
506
+ btn-remove-color: #EF4444,
507
+ btn-remove-border: 1px solid #FECACA,
508
+ btn-remove-hover-bg: #FEF2F2,
509
+ btn-remove-radius: 6px,
510
+ btn-remove-padding: 5px 12px,
511
+ btn-remove-font-size: 0.75rem,
512
+ btn-remove-font-weight: 600,
513
+
514
+ btn-add-padding: 10px 20px,
515
+ btn-add-font-size: 0.875rem,
516
+ btn-add-font-weight: 600,
517
+
518
+ btn-primary-bg: #E63E30,
519
+ btn-primary-hover-bg: #C13320,
520
+ btn-primary-color: #ffffff,
521
+
522
+ instance-bg: #ffffff,
523
+ instance-border: 1px solid #E5E7EB,
524
+ instance-radius: 12px,
525
+ instance-shadow: 0 1px 4px rgba(0, 0, 0, 0.04),
526
+ instance-hover-shadow: 0 2px 8px rgba(0, 0, 0, 0.07),
527
+ instance-transition: box-shadow 0.15s ease,
528
+ instance-header-divider: 1px solid #F5F5F5,
529
+ instance-header-pb: 12px,
530
+ instance-header-mb: 16px,
531
+ instance-num-color: #9CA3AF,
532
+ instance-num-weight: 600,
533
+
534
+ section-stepper-accent: #E63E30,
535
+ section-stepper-accent-end: #C13320,
536
+ section-stepper-active-label: #C13320,
537
+ section-stepper-active-glow: rgba(230, 62, 48, 0.2),
538
+ ));
539
+
387
540
  // ─────────────────────────────────────────────────────────────────────────────
388
541
  // Mixin — apply theme by emitting CSS custom properties
389
542
  // Usage:
@@ -398,6 +551,9 @@ $theme-2-smart-form-config: (
398
551
  --cc-sf-font-family: #{map.get($config, font-family)};
399
552
  --cc-sf-font-size-base: #{map.get($config, font-size-base)};
400
553
 
554
+ // Accent colour
555
+ --cc-sf-accent-color: #{map.get($config, accent-color)};
556
+
401
557
  // Form container
402
558
  --cc-sf-form-bg: #{map.get($config, form-bg)};
403
559
  --cc-sf-form-padding: #{map.get($config, form-padding)};
@@ -405,6 +561,7 @@ $theme-2-smart-form-config: (
405
561
  --cc-sf-form-border: #{map.get($config, form-border)};
406
562
  --cc-sf-form-shadow: #{map.get($config, form-shadow)};
407
563
  --cc-sf-form-max-width: #{map.get($config, form-max-width)};
564
+ --cc-sf-form-section-gap: #{map.get($config, form-section-gap)};
408
565
 
409
566
  // Header
410
567
  --cc-sf-form-title-size: #{map.get($config, form-title-size)};
@@ -413,7 +570,7 @@ $theme-2-smart-form-config: (
413
570
  --cc-sf-form-desc-size: #{map.get($config, form-desc-size)};
414
571
  --cc-sf-form-desc-color: #{map.get($config, form-desc-color)};
415
572
 
416
- // Section card
573
+ // Section — flat layout tokens
417
574
  --cc-sf-section-bg: #{map.get($config, section-bg)};
418
575
  --cc-sf-section-border: #{map.get($config, section-border)};
419
576
  --cc-sf-section-radius: #{map.get($config, section-radius)};
@@ -425,6 +582,19 @@ $theme-2-smart-form-config: (
425
582
  --cc-sf-section-label-color: #{map.get($config, section-label-color)};
426
583
  --cc-sf-section-label-border: #{map.get($config, section-label-border)};
427
584
 
585
+ // Section heading accent bar
586
+ --cc-sf-section-header-accent-width: #{map.get($config, section-header-accent-width)};
587
+ --cc-sf-section-header-accent-color: #{map.get($config, section-header-accent-color)};
588
+ --cc-sf-section-header-bg: #{map.get($config, section-header-bg)};
589
+
590
+ // Repeater accordion
591
+ --cc-sf-repeater-accordion-header-bg: #{map.get($config, repeater-accordion-header-bg)};
592
+ --cc-sf-repeater-accordion-header-color: #{map.get($config, repeater-accordion-header-color)};
593
+ --cc-sf-repeater-accordion-active-bg: #{map.get($config, repeater-accordion-active-bg)};
594
+ --cc-sf-repeater-badge-bg: #{map.get($config, repeater-badge-bg)};
595
+ --cc-sf-repeater-badge-color: #{map.get($config, repeater-badge-color)};
596
+ --cc-sf-section-border-radius-inner: #{map.get($config, section-border-radius-inner)};
597
+
428
598
  // Repeater instance
429
599
  --cc-sf-instance-bg: #{map.get($config, instance-bg)};
430
600
  --cc-sf-instance-border: #{map.get($config, instance-border)};
@@ -434,8 +604,13 @@ $theme-2-smart-form-config: (
434
604
  --cc-sf-instance-num-color:#{map.get($config, instance-num-color)};
435
605
  --cc-sf-instance-num-size: #{map.get($config, instance-num-size)};
436
606
 
437
- // Grid
607
+ // Grid + field gap
438
608
  --cc-sf-grid-gap: #{map.get($config, grid-gap)};
609
+ --cc-sf-field-gap: #{map.get($config, field-gap)};
610
+
611
+ // Focus ring
612
+ --cc-sf-focus-ring-width: #{map.get($config, focus-ring-width)};
613
+ --cc-sf-focus-ring-offset: #{map.get($config, focus-ring-offset)};
439
614
 
440
615
  // Label
441
616
  --cc-sf-label-size: #{map.get($config, label-size)};
@@ -508,6 +683,7 @@ $theme-2-smart-form-config: (
508
683
  --cc-sf-dropzone-over-border: #{map.get($config, dropzone-over-border)};
509
684
  --cc-sf-dropzone-over-shadow: #{map.get($config, dropzone-over-shadow)};
510
685
  --cc-sf-dropzone-icon-color: #{map.get($config, dropzone-icon-color)};
686
+ --cc-sf-dropzone-icon-bg: #{map.get($config, dropzone-icon-bg)};
511
687
  --cc-sf-dropzone-link-color: #{map.get($config, dropzone-link-color)};
512
688
  --cc-sf-dropzone-hint-color: #{map.get($config, dropzone-hint-color)};
513
689
 
@@ -531,6 +707,24 @@ $theme-2-smart-form-config: (
531
707
  --cc-sf-btn-remove-border: #{map.get($config, btn-remove-border)};
532
708
  --cc-sf-btn-remove-radius: #{map.get($config, btn-remove-radius)};
533
709
  --cc-sf-btn-remove-hover-bg: #{map.get($config, btn-remove-hover-bg)};
710
+ --cc-sf-btn-remove-padding: #{map.get($config, btn-remove-padding)};
711
+ --cc-sf-btn-remove-font-size: #{map.get($config, btn-remove-font-size)};
712
+ --cc-sf-btn-remove-font-weight: #{map.get($config, btn-remove-font-weight)};
713
+ --cc-sf-btn-add-padding: #{map.get($config, btn-add-padding)};
714
+ --cc-sf-btn-add-font-size: #{map.get($config, btn-add-font-size)};
715
+ --cc-sf-btn-add-font-weight: #{map.get($config, btn-add-font-weight)};
716
+ --cc-sf-stepper-connector-display: #{map.get($config, stepper-connector-display)};
717
+ --cc-sf-instance-shadow: #{map.get($config, instance-shadow)};
718
+ --cc-sf-instance-hover-shadow: #{map.get($config, instance-hover-shadow)};
719
+ --cc-sf-instance-transition: #{map.get($config, instance-transition)};
720
+ --cc-sf-instance-header-divider: #{map.get($config, instance-header-divider)};
721
+ --cc-sf-instance-header-pb: #{map.get($config, instance-header-pb)};
722
+ --cc-sf-instance-header-mb: #{map.get($config, instance-header-mb)};
723
+ --cc-sf-instance-num-weight: #{map.get($config, instance-num-weight)};
724
+ --cc-sf-section-stepper-accent: #{map.get($config, section-stepper-accent)};
725
+ --cc-sf-section-stepper-accent-end: #{map.get($config, section-stepper-accent-end)};
726
+ --cc-sf-section-stepper-active-label: #{map.get($config, section-stepper-active-label)};
727
+ --cc-sf-section-stepper-active-glow: #{map.get($config, section-stepper-active-glow)};
534
728
 
535
729
  // Stepper
536
730
  --cc-sf-step-connector-color: #{map.get($config, step-connector-color)};
@@ -571,4 +765,120 @@ $theme-2-smart-form-config: (
571
765
  --cc-sf-btn-font-weight: #{map.get($config, btn-font-weight)};
572
766
  --cc-sf-btn-transition: #{map.get($config, btn-transition)};
573
767
  --cc-sf-btn-disabled-opacity: #{map.get($config, btn-disabled-opacity)};
768
+
769
+ // ── Structural rules using the emitted tokens ─────────────────────────────
770
+ ::ng-deep {
771
+ .stepper-nav .stepper-step:not(:last-child)::after {
772
+ display: var(--cc-sf-stepper-connector-display) !important;
773
+ }
774
+
775
+ .section-instance {
776
+ background: var(--cc-sf-instance-bg) !important;
777
+ border: var(--cc-sf-instance-border) !important;
778
+ border-radius: var(--cc-sf-instance-radius) !important;
779
+ box-shadow: var(--cc-sf-instance-shadow);
780
+ transition: var(--cc-sf-instance-transition);
781
+
782
+ &:hover {
783
+ box-shadow: var(--cc-sf-instance-hover-shadow);
784
+ }
785
+ }
786
+
787
+ .section-instance-header {
788
+ display: flex;
789
+ align-items: center;
790
+ justify-content: space-between;
791
+ padding-bottom: var(--cc-sf-instance-header-pb);
792
+ margin-bottom: var(--cc-sf-instance-header-mb);
793
+ border-bottom: var(--cc-sf-instance-header-divider);
794
+ }
795
+
796
+ .section-instance-num {
797
+ font-size: var(--cc-sf-instance-num-size);
798
+ font-weight: var(--cc-sf-instance-num-weight);
799
+ color: var(--cc-sf-instance-num-color);
800
+ }
801
+
802
+ .btn-instance-remove {
803
+ display: inline-flex;
804
+ align-items: center;
805
+ gap: 6px;
806
+ padding: var(--cc-sf-btn-remove-padding);
807
+ border-radius: var(--cc-sf-btn-remove-radius);
808
+ border: var(--cc-sf-btn-remove-border) !important;
809
+ background: var(--cc-sf-btn-remove-bg);
810
+ color: var(--cc-sf-btn-remove-color) !important;
811
+ font-size: var(--cc-sf-btn-remove-font-size);
812
+ font-weight: var(--cc-sf-btn-remove-font-weight);
813
+ cursor: pointer;
814
+ transition: all 0.15s ease;
815
+
816
+ &:hover {
817
+ background: var(--cc-sf-btn-remove-hover-bg);
818
+ }
819
+ }
820
+
821
+ .btn-add-section {
822
+ display: inline-flex;
823
+ align-items: center;
824
+ gap: 8px;
825
+ padding: var(--cc-sf-btn-add-padding);
826
+ border-radius: var(--cc-sf-btn-add-radius);
827
+ border: var(--cc-sf-btn-add-border) !important;
828
+ background: var(--cc-sf-btn-add-bg);
829
+ color: var(--cc-sf-btn-add-color);
830
+ font-size: var(--cc-sf-btn-add-font-size);
831
+ font-weight: var(--cc-sf-btn-add-font-weight);
832
+ cursor: pointer;
833
+ transition: all 0.18s ease;
834
+
835
+ &:hover {
836
+ background: var(--cc-sf-btn-add-hover-bg);
837
+ border-color: var(--cc-sf-btn-add-hover-border) !important;
838
+ }
839
+ }
840
+
841
+ // ── Section stepper — accent colour override ──────────────────────────
842
+ .section-stepper-nav::before {
843
+ background: linear-gradient(
844
+ 90deg,
845
+ var(--cc-sf-section-stepper-accent) 0%,
846
+ var(--cc-sf-section-stepper-accent-end) 100%
847
+ ) !important;
848
+ }
849
+
850
+ .section-stepper-step {
851
+ &.ss-active {
852
+ .ss-badge {
853
+ background: linear-gradient(
854
+ 135deg,
855
+ var(--cc-sf-section-stepper-accent) 0%,
856
+ var(--cc-sf-section-stepper-accent-end) 100%
857
+ ) !important;
858
+ box-shadow:
859
+ 0 0 0 4px var(--cc-sf-section-stepper-active-glow),
860
+ 0 4px 12px var(--cc-sf-section-stepper-active-glow) !important;
861
+ }
862
+
863
+ .ss-label {
864
+ color: var(--cc-sf-section-stepper-active-label) !important;
865
+ }
866
+ }
867
+
868
+ &:not(.ss-active):not(.ss-completed):not(.ss-warning):hover {
869
+ .ss-badge {
870
+ color: var(--cc-sf-section-stepper-accent) !important;
871
+ border-color: var(--cc-sf-section-stepper-accent-end) !important;
872
+ }
873
+ }
874
+
875
+ .ss-connector::after {
876
+ background: linear-gradient(
877
+ 90deg,
878
+ var(--cc-sf-section-stepper-accent),
879
+ var(--cc-sf-section-stepper-accent-end)
880
+ ) !important;
881
+ }
882
+ }
883
+ }
574
884
  }