commons-shared-web-ui 0.0.53 → 0.0.55

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 (26) hide show
  1. package/documentation/smart-form.md +236 -3
  2. package/documentation/time-picker.md +274 -0
  3. package/ng-package.json +3 -1
  4. package/package.json +1 -1
  5. package/src/lib/modules/form-components/form-components.module.ts +2 -1
  6. package/src/lib/modules/form-field-configuration/services/config-schema-tree.service.ts +13 -2
  7. package/src/lib/modules/side-nav/components/side-nav/side-nav.component.scss +1 -1
  8. package/src/lib/modules/smart-form/components/form-field/form-field.component.html +145 -11
  9. package/src/lib/modules/smart-form/components/form-field/form-field.component.scss +98 -0
  10. package/src/lib/modules/smart-form/components/form-field/form-field.component.ts +225 -1
  11. package/src/lib/modules/smart-form/components/form-section/form-section.component.scss +2 -2
  12. package/src/lib/modules/smart-form/components/smart-form/smart-form.component.scss +2 -2
  13. package/src/lib/modules/smart-form/models/form-schema.model.ts +44 -0
  14. package/src/lib/modules/smart-form/smart-form.module.ts +3 -1
  15. package/src/lib/modules/time-picker/components/time-picker/time-picker.component.html +37 -0
  16. package/src/lib/modules/time-picker/components/time-picker/time-picker.component.scss +102 -0
  17. package/src/lib/modules/time-picker/components/time-picker/time-picker.component.ts +178 -0
  18. package/src/lib/modules/time-picker/components/time-wheel-panel/time-wheel-panel.component.html +78 -0
  19. package/src/lib/modules/time-picker/components/time-wheel-panel/time-wheel-panel.component.scss +226 -0
  20. package/src/lib/modules/time-picker/components/time-wheel-panel/time-wheel-panel.component.ts +595 -0
  21. package/src/lib/modules/time-picker/models/time-picker.models.ts +49 -0
  22. package/src/lib/modules/time-picker/time-picker.module.ts +23 -0
  23. package/src/lib/modules/time-picker/time-picker.theme.scss +87 -0
  24. package/src/lib/shared-ui.module.ts +5 -0
  25. package/src/lib/styles/global.scss +10 -0
  26. package/src/public-api.ts +6 -0
@@ -396,6 +396,10 @@ The End Date picker automatically sets its minimum to whatever value `startDate`
396
396
  | `maxTime` | string | Explicit maximum time in 24-hour `"HH:mm"` format (e.g. `"18:00"`). |
397
397
  | `inputReadonly` | boolean | When true, the input is readonly. |
398
398
  | `minTimeField` | string | Name of a sibling TIME field whose value is used as the dynamic minimum. When it changes, this field's minimum updates and any now-invalid value (earlier than the new minimum) is cleared. Mirrors `dateConfig.minTimeField`. |
399
+ | `variant` | string | Rendering variant: `'default'` (native picker input, default) or `'wheel'` (custom drum-roll slide picker). |
400
+ | `mode` | string | Hour display mode for `'wheel'` variant: `'12'` (12-hour, AM/PM column, default) or `'24'` (24-hour, no AM/PM). |
401
+ | `minuteStep` | number | Minute column increment step for `'wheel'` variant. Omit (or `1`) for every minute `00–59`; a value greater than `0` adds the break (e.g. `5`, `10`, `15`, `30`). |
402
+ | `placeholder` | string | Overridden placeholder text for the `'wheel'` variant trigger field. |
399
403
 
400
404
  **Start / End time pair** — the end time cannot be earlier than the start time (same pattern as `dateConfig.minDateField`):
401
405
 
@@ -419,6 +423,154 @@ The End Date picker automatically sets its minimum to whatever value `startDate`
419
423
  }
420
424
  ```
421
425
 
426
+ **Custom Wheel Time Picker Example** — using the custom drum-roll picker with 12-hour mode and 15-minute increments:
427
+
428
+ ```json
429
+ {
430
+ "name": "preferredSlot",
431
+ "label": "Preferred Meeting Slot",
432
+ "type": "TIME",
433
+ "subType": "SINGLE",
434
+ "required": true,
435
+ "timeConfig": {
436
+ "variant": "wheel",
437
+ "mode": "12",
438
+ "minuteStep": 15,
439
+ "minTime": "09:00",
440
+ "maxTime": "17:00",
441
+ "placeholder": "Choose a 15-minute slot"
442
+ }
443
+ }
444
+ ```
445
+
446
+ > **Note:** `minuteStep` omitted (or `0`/`1`) shows every minute `00–59`; any value greater than `0` adds the step break (e.g. `15` → `00, 15, 30, 45`). The wheel opens as a compact, centered modal.
447
+
448
+ **Styling & Variants (SCSS)**:
449
+ By default, the time picker uses the black/grey theme (Image 2 style). To use a blue accent variant instead, include the theme mixin with overrides inside your global stylesheet or MFE container styles:
450
+
451
+ ```scss
452
+ @use 'commons-shared-web-ui/lib/modules/time-picker/time-picker.theme' as tp;
453
+
454
+ // Default variant (Black/Grey Color Scheme)
455
+ :root {
456
+ @include tp.time-picker-theme();
457
+ }
458
+
459
+ // Blue Accent Variant
460
+ :root {
461
+ @include tp.time-picker-theme((
462
+ accent: #4D89EB,
463
+ accent-light: rgba(77, 137, 235, 0.08),
464
+ accent-border: rgba(77, 137, 235, 0.15),
465
+ btn-confirm-bg: #4D89EB,
466
+ btn-confirm-hover: #3b74d1,
467
+ btn-cancel-bg: transparent,
468
+ btn-cancel-border: #e5e7eb,
469
+ btn-cancel-hover: #e5e7eb
470
+ ));
471
+ }
472
+ ```
473
+
474
+ **Time Picker i18n labels** — the `'wheel'` variant reads these keys from the form-level `[labels]` map (all optional; each falls back to English):
475
+
476
+ | Label key | Applies to | Default |
477
+ | --- | --- | --- |
478
+ | `TIME_PICKER.SELECT_TIME` | Popup title | `Select Time` |
479
+ | `TIME_PICKER.HOUR` | Hour column header | `Hour` |
480
+ | `TIME_PICKER.MINUTE` | Minute column header | `Minute` |
481
+ | `TIME_PICKER.PERIOD` | AM/PM column header | `AM/PM` |
482
+ | `TIME_PICKER.AM` / `TIME_PICKER.PM` | Period values | `AM` / `PM` |
483
+ | `COMMON.ACTIONS.CONFIRM` | Confirm button | `Confirm` |
484
+ | `COMMON.ACTIONS.CANCEL` | Cancel button | `Cancel` |
485
+
486
+ #### Complete Example (TIME wheel field in a Smart Form)
487
+
488
+ A full form schema with a start/end wheel time-picker pair, plus the parent-component wiring and picker i18n labels.
489
+
490
+ ```typescript
491
+ import { Component } from '@angular/core';
492
+
493
+ @Component({
494
+ selector: 'app-booking-form',
495
+ template: `
496
+ <lib-smart-form
497
+ [formJson]="formJson"
498
+ [labels]="labels"
499
+ (submit)="onSubmit($event)">
500
+ </lib-smart-form>
501
+ `
502
+ })
503
+ export class BookingFormComponent {
504
+ formJson = JSON.stringify({
505
+ entityType: 'BOOKING',
506
+ label: 'FORM.BOOKING_TITLE',
507
+ formType: 'SECTION',
508
+ sectionConfig: {
509
+ children: [
510
+ {
511
+ type: 'ROW',
512
+ subType: 'HORIZONTAL',
513
+ children: [
514
+ {
515
+ name: 'startTime',
516
+ label: 'FIELD.START_TIME',
517
+ type: 'TIME',
518
+ subType: 'SINGLE',
519
+ required: true,
520
+ colSpan: 6,
521
+ timeConfig: {
522
+ variant: 'wheel',
523
+ mode: '12',
524
+ minuteStep: 15,
525
+ minTime: '09:00',
526
+ maxTime: '18:00'
527
+ }
528
+ },
529
+ {
530
+ name: 'endTime',
531
+ label: 'FIELD.END_TIME',
532
+ type: 'TIME',
533
+ subType: 'SINGLE',
534
+ required: true,
535
+ colSpan: 6,
536
+ timeConfig: {
537
+ variant: 'wheel',
538
+ mode: '12',
539
+ minuteStep: 15,
540
+ minTimeField: 'startTime' // end cannot precede start
541
+ }
542
+ }
543
+ ]
544
+ }
545
+ ]
546
+ },
547
+ submitConfig: {
548
+ apiUrl: 'https://api.example.com/bookings',
549
+ method: 'POST',
550
+ successMessage: 'Booking created!'
551
+ }
552
+ });
553
+
554
+ labels = {
555
+ 'FORM.BOOKING_TITLE': 'New Booking',
556
+ 'FIELD.START_TIME': 'Start Time',
557
+ 'FIELD.END_TIME': 'End Time',
558
+ // Time picker popup i18n
559
+ 'TIME_PICKER.SELECT_TIME': 'Pick a time',
560
+ 'TIME_PICKER.HOUR': 'Hour',
561
+ 'TIME_PICKER.MINUTE': 'Min',
562
+ 'TIME_PICKER.PERIOD': 'AM/PM',
563
+ 'COMMON.ACTIONS.CONFIRM': 'Confirm',
564
+ 'COMMON.ACTIONS.CANCEL': 'Cancel'
565
+ };
566
+
567
+ onSubmit(payload: any): void {
568
+ // { startTime: "09:15 AM", endTime: "10:00 AM" }
569
+ console.log('Booking:', payload);
570
+ }
571
+ }
572
+ ```
573
+
422
574
  ### 5. Autocomplete (AUTOCOMPLETE)
423
575
 
424
576
  A searchable input backed by Angular Material's `mat-autocomplete`. As the user types, the option list is filtered by label or code. The form control stores the **code** value, while the input shows the human-readable **label**.
@@ -989,6 +1141,87 @@ Provides a WYSIWYG rich text editor using Quill.
989
1141
 
990
1142
  Supported tokens: `bold`, `italic`, `underline`, `strike`, `blockquote`, `code`, `font`, `size`, `color`, `background`, `align`, `header1`, `header2`, `orderedList`, `bulletList`, `subscript`, `superscript`, `indentMinus`, `indentPlus`, `link`, `image`, `video`, `clean`.
991
1143
 
1144
+ ### 14. Link List (LINK_LIST)
1145
+
1146
+ Allows users to manage a list of URLs dynamically. Provides a text input box with a trailing plus icon to add links, displaying them as a list above or below the input field.
1147
+
1148
+ **Configuration Properties (`linkListConfig`):**
1149
+
1150
+ | Property | Type | Description |
1151
+ |---|---|---|
1152
+ | `listPosition` | `'top' \| 'bottom'` | Renders the link list above or below the input field (default: `'bottom'`). |
1153
+ | `editable` | `boolean` | Allows inline editing of existing links in the list (default: `true`). |
1154
+ | `deleteable` | `boolean` | Shows a delete action button on list items (default: `true`). |
1155
+ | `valueFormat` | `'string' \| 'array' \| 'object'` | Format in which data is stored in the form control: `'string'` (separated by `separator`), `'array'` (array of strings), or `'object'` (array of objects) (default: `'string'`). |
1156
+ | `separator` | `string` | Character used to separate links when `valueFormat` is `'string'` (default: `','`). |
1157
+ | `urlKey` | `string` | The object key used to store/load the link URL when `valueFormat` is `'object'` (default: `'linkUrl'`). |
1158
+ | `idField` | `string` | The object key representing the link identifier when resolving API placeholders (default: `'id'`). |
1159
+ | `deleteApiUrl` | `string` | Optional API endpoint to execute an HTTP DELETE call upon removing a saved item (e.g. `/api/v1/links/:id`). |
1160
+ | `editApiUrl` | `string` | Optional API endpoint to execute an HTTP PUT call upon editing a saved item (e.g. `/api/v1/links/:id`). |
1161
+ | `pattern` | `string` | Regular expression pattern for validating each link (default URL validator regex: `^https?://([a-zA-Z0-9\-]+\.)+[a-zA-Z]{2,}(/\S*)?$`). |
1162
+ | `patternMessage` | `string` | Custom error message displayed if pattern validation fails. |
1163
+ | `colSpan` | `number` | Grid column span (1-12) for each list item in the 12-column grid. E.g., `6` for two columns side-by-side (default: `12`). |
1164
+
1165
+ **Example Config (Storing as an Array of Objects):**
1166
+
1167
+ ```json
1168
+ {
1169
+ "name": "externalLinks",
1170
+ "label": "External Links",
1171
+ "type": "LINK_LIST",
1172
+ "placeholder": "Enter link...",
1173
+ "colSpan": 12,
1174
+ "linkListConfig": {
1175
+ "listPosition": "bottom",
1176
+ "editable": true,
1177
+ "deleteable": true,
1178
+ "valueFormat": "object",
1179
+ "urlKey": "linkUrl"
1180
+ }
1181
+ }
1182
+ ```
1183
+
1184
+ **Example Config (Storing as a Comma-Separated String):**
1185
+
1186
+ ```json
1187
+ {
1188
+ "name": "externalLinks",
1189
+ "label": "External Links",
1190
+ "type": "LINK_LIST",
1191
+ "placeholder": "Enter link...",
1192
+ "colSpan": 12,
1193
+ "linkListConfig": {
1194
+ "listPosition": "top",
1195
+ "editable": true,
1196
+ "deleteable": true,
1197
+ "valueFormat": "string",
1198
+ "separator": ","
1199
+ }
1200
+ }
1201
+ ```
1202
+
1203
+ **Example Config (Two-Column Layout with Regex Validation):**
1204
+
1205
+ ```json
1206
+ {
1207
+ "name": "externalLinks",
1208
+ "label": "External Links",
1209
+ "type": "LINK_LIST",
1210
+ "placeholder": "Enter link...",
1211
+ "colSpan": 12,
1212
+ "linkListConfig": {
1213
+ "listPosition": "bottom",
1214
+ "editable": true,
1215
+ "deleteable": true,
1216
+ "valueFormat": "object",
1217
+ "urlKey": "linkUrl",
1218
+ "colSpan": 6,
1219
+ "pattern": "^https?://([a-zA-Z0-9\\-]+\\.)+com(/\\S*)?$",
1220
+ "patternMessage": "Only .com domain links are allowed"
1221
+ }
1222
+ }
1223
+ ```
1224
+
992
1225
  ---
993
1226
 
994
1227
  ## Layout System
@@ -2104,6 +2337,7 @@ All variables are prefixed `--cc-sf-`. They are generated by the mixin and casca
2104
2337
 
2105
2338
  ### Contact Form
2106
2339
 
2340
+ ```json
2107
2341
  {
2108
2342
  "entityType": "CONTACT",
2109
2343
  "label": "Contact Us",
@@ -2147,8 +2381,7 @@ All variables are prefixed `--cc-sf-`. They are generated by the mixin and casca
2147
2381
  ]
2148
2382
  }
2149
2383
  }
2150
-
2151
- ````
2384
+ ```
2152
2385
 
2153
2386
  ### User Registration with Password Validation
2154
2387
 
@@ -2203,7 +2436,7 @@ All variables are prefixed `--cc-sf-`. They are generated by the mixin and casca
2203
2436
  ]
2204
2437
  }
2205
2438
  }
2206
- ````
2439
+ ```
2207
2440
 
2208
2441
  ### Section Stepper with `showOnLastStepOnly`
2209
2442
 
@@ -0,0 +1,274 @@
1
+ # Time Picker Component (`lib-time-picker`)
2
+
3
+ A highly customizable, slot-machine / drum-roll style wheel picker component that supports 12-hour or 24-hour modes, custom minute steps, disabled time range limits (`minTime` and `maxTime`), full touch interaction, and accessibility attributes.
4
+
5
+ ---
6
+
7
+ ## 1. Importing the Module
8
+
9
+ To use the time picker in your Angular application (or within a Micro Frontend Remote MFE), import `TimePickerModule` into your Angular module:
10
+
11
+ ```typescript
12
+ import { NgModule } from '@angular/core';
13
+ import { CommonModule } from '@angular/common';
14
+ import { TimePickerModule } from 'commons-shared-web-ui';
15
+
16
+ @NgModule({
17
+ imports: [
18
+ CommonModule,
19
+ TimePickerModule
20
+ ]
21
+ })
22
+ export class MyFeatureModule {}
23
+ ```
24
+
25
+ ---
26
+
27
+ ## 2. Basic Component Usage
28
+
29
+ ### Template-Driven Form / Two-Way Binding (`ngModel`)
30
+ ```html
31
+ <lib-time-picker
32
+ [(ngModel)]="selectedTime"
33
+ [label]="'Meeting Time'"
34
+ [placeholder]="'Choose a time'"
35
+ [required]="true"
36
+ [mode]="'12'"
37
+ [minuteStep]="15">
38
+ </lib-time-picker>
39
+ ```
40
+
41
+ ### Reactive Forms (`formControlName`)
42
+ ```html
43
+ <form [formGroup]="form">
44
+ <lib-time-picker
45
+ [formControlName]="'startTime'"
46
+ [label]="'Start Time'"
47
+ [mode]="'24'"
48
+ [minTime]="'08:00'"
49
+ [maxTime]="'18:00'">
50
+ </lib-time-picker>
51
+ </form>
52
+ ```
53
+
54
+ ### Complete Example (Reactive Form + Events)
55
+
56
+ A full working component wiring up two pickers with a time range, a minute step, and the `(timeChange)` output.
57
+
58
+ ```typescript
59
+ import { Component } from '@angular/core';
60
+ import { FormBuilder, FormGroup, Validators } from '@angular/forms';
61
+
62
+ @Component({
63
+ selector: 'app-schedule-form',
64
+ templateUrl: './schedule-form.component.html'
65
+ })
66
+ export class ScheduleFormComponent {
67
+ form: FormGroup = this.fb.group({
68
+ startTime: ['09:00 AM', Validators.required],
69
+ endTime: ['05:00 PM', Validators.required]
70
+ });
71
+
72
+ constructor(private fb: FormBuilder) {}
73
+
74
+ onStartChange(value: string): void {
75
+ // Emitted on Confirm — "hh:mm AM|PM" (12h) or "HH:mm" (24h)
76
+ console.log('Start time selected:', value);
77
+ }
78
+
79
+ submit(): void {
80
+ if (this.form.valid) {
81
+ console.log('Schedule:', this.form.value);
82
+ }
83
+ }
84
+ }
85
+ ```
86
+
87
+ ```html
88
+ <form [formGroup]="form" (ngSubmit)="submit()">
89
+ <lib-time-picker
90
+ formControlName="startTime"
91
+ [label]="'Start Time'"
92
+ [placeholder]="'Select start'"
93
+ [required]="true"
94
+ [mode]="'12'"
95
+ [minuteStep]="15"
96
+ [errorMessage]="form.get('startTime')?.touched && form.get('startTime')?.invalid ? 'Start time is required' : ''"
97
+ (timeChange)="onStartChange($event)">
98
+ </lib-time-picker>
99
+
100
+ <lib-time-picker
101
+ formControlName="endTime"
102
+ [label]="'End Time'"
103
+ [mode]="'12'"
104
+ [minuteStep]="15"
105
+ [minTime]="'09:00'"
106
+ [maxTime]="'21:00'">
107
+ </lib-time-picker>
108
+
109
+ <button type="submit" [disabled]="form.invalid">Save</button>
110
+ </form>
111
+ ```
112
+
113
+ ### Complete Example (Single `config` object)
114
+
115
+ Every input can also be supplied through one `TimePickerConfig` object — handy when the config comes from JSON/i18n.
116
+
117
+ ```typescript
118
+ import { Component } from '@angular/core';
119
+ import { TimePickerConfig } from 'commons-shared-web-ui';
120
+
121
+ @Component({
122
+ selector: 'app-reminder',
123
+ template: `
124
+ <lib-time-picker
125
+ [(ngModel)]="reminderTime"
126
+ [config]="pickerConfig"
127
+ (timeChange)="reminderTime = $event">
128
+ </lib-time-picker>
129
+ `
130
+ })
131
+ export class ReminderComponent {
132
+ reminderTime = '';
133
+
134
+ pickerConfig: TimePickerConfig = {
135
+ label: 'Daily Reminder',
136
+ placeholder: 'Pick a time',
137
+ mode: '24',
138
+ minuteStep: 5, // 0/1 = every minute; > 0 adds the break
139
+ required: true,
140
+ minTime: '06:00',
141
+ maxTime: '23:30'
142
+ };
143
+ }
144
+ ```
145
+
146
+ ---
147
+
148
+ ## 3. Configuration Properties
149
+
150
+ ### Inputs
151
+ | Input | Type | Default | Description |
152
+ | --- | --- | --- | --- |
153
+ | `config` | `TimePickerConfig` | `undefined` | Unified configuration object that mirrors individual inputs. |
154
+ | `labels` | `TimePickerLabels` | `undefined` | i18n label map keys. |
155
+ | `label` | `string` | `""` | The label text displayed above the trigger input. |
156
+ | `placeholder` | `string` | `""` | Placeholder text shown inside the input when no time is selected. |
157
+ | `mode` | `'12' \| '24'` | `'12'` | Hour display format. `'12'` shows a AM/PM column, while `'24'` runs from 00-23. |
158
+ | `minuteStep` | `number` | `0` | Increment for the minutes column. `0` (or `1`) shows every minute `00–59` with no break; a value greater than `0` adds the step break (e.g. `15` → `00, 15, 30, 45`). |
159
+ | `disabled` | `boolean` | `false` | Disables the trigger field, blocking popup opening. |
160
+ | `required` | `boolean` | `false` | Appends a red asterisk to the field label. |
161
+ | `errorMessage` | `string` | `""` | Validation error message text. |
162
+ | `minTime` | `string` | `undefined` | Minimum allowed limit in 24hr `"HH:mm"` string format (e.g., `"09:30"`). |
163
+ | `maxTime` | `string` | `undefined` | Maximum allowed limit in 24hr `"HH:mm"` string format (e.g., `"17:00"`). |
164
+
165
+ ### `TimePickerLabels` Properties
166
+ When passing the `[labels]` input, you can provide any of the following fields (all optional, falling back to English defaults):
167
+
168
+ | Property | Type | Default | Description |
169
+ | --- | --- | --- | --- |
170
+ | `selectTimeTitle` | `string` | `'Select Time'` | The subtle header title printed at the top of the dialog. |
171
+ | `hourLabel` | `string` | `'Hour'` | Header label shown above the hour column. |
172
+ | `minuteLabel` | `string` | `'Minute'` | Header label shown above the minute column. |
173
+ | `periodLabel` | `string` | `'AM/PM'` | Header label shown above the AM/PM column (12h mode only). |
174
+ | `confirmLabel` | `string` | `'Confirm'` | Confirm button text. |
175
+ | `cancelLabel` | `string` | `'Cancel'` | Cancel button text. |
176
+
177
+ ### Outputs
178
+ | Output | Type | Description |
179
+ | --- | --- | --- |
180
+ | `timeChange` | `EventEmitter<string>` | Emitted when a time is confirmed and closed. Emits `"hh:mm AM\|PM"` for `'12'` mode, or `"HH:mm"` for `'24'` mode. |
181
+
182
+ ---
183
+
184
+ ## 4. Styling & Customization in MFEs
185
+
186
+ The time-picker has two main styling options: the **Default (Black/Grey)** scheme (matching Image 2) and the **Blue Accent** scheme. These are easily set via the theme mixin or global CSS variables.
187
+
188
+ ### Variant Options (SCSS)
189
+
190
+ #### Option 1: Default (Black/Grey Theme — Image 2)
191
+ The default theme features black text active indicators, borderless wheels, a light-grey center highlight bar, a black Confirm button, and a light-grey Cancel button.
192
+
193
+ ```scss
194
+ @use 'commons-shared-web-ui/lib/modules/time-picker/time-picker.theme' as tp;
195
+
196
+ :root {
197
+ @include tp.time-picker-theme();
198
+ }
199
+ ```
200
+
201
+ #### Option 2: Blue Accent Variant
202
+ The blue accent theme uses the same layout and headers, but changes highlights, preview text, active wheel items, and buttons to use the blue accent color.
203
+
204
+ ```scss
205
+ @use 'commons-shared-web-ui/lib/modules/time-picker/time-picker.theme' as tp;
206
+
207
+ :root {
208
+ @include tp.time-picker-theme((
209
+ accent: #4D89EB,
210
+ accent-light: rgba(77, 137, 235, 0.08),
211
+ accent-border: rgba(77, 137, 235, 0.15),
212
+ btn-confirm-bg: #4D89EB,
213
+ btn-confirm-hover: #3b74d1,
214
+ btn-cancel-bg: transparent,
215
+ btn-cancel-border: #e5e7eb,
216
+ btn-cancel-hover: #e5e7eb
217
+ ));
218
+ }
219
+ ```
220
+
221
+ You can style the trigger inputs and picker wheels using two different methods: using Scss Mixins or CSS Custom Properties.
222
+
223
+ ### Method A: Including Theme Mixin inside MFE Component SCSS
224
+
225
+ To scope the theme changes inside a specific feature component or the main MFE root stylesheet, use the `@use` rule to import the SCSS file and `@include` the mixin:
226
+
227
+ ```scss
228
+ // remote-mfe/src/app/feature/feature.component.scss
229
+
230
+ @use 'commons-shared-web-ui/lib/modules/time-picker/time-picker.theme' as tp;
231
+
232
+ // Apply default theme to component scope
233
+ :host {
234
+ @include tp.time-picker-theme();
235
+ }
236
+
237
+ // OR customize theme properties:
238
+ .custom-theme-picker {
239
+ @include tp.time-picker-theme((
240
+ accent: #C21E25, // Crimson Red brand color
241
+ accent-light: rgba(194, 30, 37, 0.08),
242
+ accent-border: rgba(194, 30, 37, 0.15),
243
+ btn-confirm-bg: #C21E25,
244
+ btn-confirm-hover: #a1141a,
245
+ border-radius: 8px
246
+ ));
247
+ }
248
+ ```
249
+
250
+ ### Method B: Overriding CSS Custom Properties directly
251
+
252
+ If you prefer not to include mixins, you can set the CSS variables directly in your MFE stylesheet:
253
+
254
+ ```css
255
+ /* remote-mfe/src/styles.scss */
256
+ :root {
257
+ --cc-timepicker-accent: #C21E25;
258
+ --cc-timepicker-accent-light: rgba(194, 30, 37, 0.08);
259
+ --cc-timepicker-accent-border: rgba(194, 30, 37, 0.15);
260
+ --cc-timepicker-btn-confirm-bg: #C21E25;
261
+ --cc-timepicker-btn-confirm-hover: #a1141a;
262
+ --cc-timepicker-border-radius: 8px;
263
+ }
264
+ ```
265
+
266
+ ---
267
+
268
+ ## 5. Constraint Engine Enforcement
269
+
270
+ When `minTime` and/or `maxTime` is specified:
271
+ 1. Slices of time that violate the boundaries are **automatically greyed out** (`opacity: 0.25`) and have pointer events disabled.
272
+ 2. Clicking on a disabled item will not trigger selection.
273
+ 3. Flicking or scrolling past a disabled value snaps to the **closest valid item** automatically.
274
+ 4. Changing the AM/PM column to a period where the current hour is disabled triggers an automatic visual snap transition to a valid hour.
package/ng-package.json CHANGED
@@ -23,6 +23,8 @@
23
23
  "src/lib/modules/side-nav/side-nav.theme.scss",
24
24
  "src/lib/modules/form-builder/form-builder.theme.scss",
25
25
  "src/lib/modules/filter-table-selector/filter-table-selector.theme.scss",
26
- "src/lib/modules/snackbar/snackbar.theme.scss"
26
+ "src/lib/modules/snackbar/snackbar.theme.scss",
27
+ "src/lib/modules/button-dropdown/button-dropdown.theme.scss",
28
+ "src/lib/modules/time-picker/time-picker.theme.scss"
27
29
  ]
28
30
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "commons-shared-web-ui",
3
- "version": "0.0.53",
3
+ "version": "0.0.55",
4
4
  "peerDependencies": {
5
5
  "@angular/animations": "20.3.15",
6
6
  "@angular/cdk": "20.2.14",
@@ -35,7 +35,8 @@ import { ClickOutsideDirective } from './directives/click-outside.directive';
35
35
  RadioComponent,
36
36
  ToggleComponent,
37
37
  DatepickerComponent,
38
- SearchComponent
38
+ SearchComponent,
39
+ ClickOutsideDirective
39
40
  ]
40
41
  })
41
42
  export class FormComponentsModule { }
@@ -83,13 +83,24 @@ export class ConfigSchemaTreeService {
83
83
  }
84
84
  }
85
85
 
86
- const visible = (field.isEnabled ?? field.visible ?? true) && field.disabled !== true;
86
+ // A section is locked (toggle replaced by a lock, can never be hidden) when its
87
+ // own field is locked OR any descendant field/subsection is locked — hiding the
88
+ // section would otherwise hide a field that must always stay visible. Subsection
89
+ // locks are already transitive, so checking direct children is enough.
90
+ const hasLockedDescendant =
91
+ fields.some(f => f.lockVisibility) || subsections.some(s => s.lockVisibility);
92
+ const lockVisibility = field.lockVisibility === true || hasLockedDescendant;
93
+
94
+ // Locked sections are always visible; otherwise honour the schema's flags.
95
+ const visible = lockVisibility
96
+ ? true
97
+ : (field.isEnabled ?? field.visible ?? true) && field.disabled !== true;
87
98
  return {
88
99
  label: field.sectionConfig?.label || field.label || field.name || 'Section',
89
100
  fieldConfig: field,
90
101
  sectionConfig: field.sectionConfig ?? null,
91
102
  visible,
92
- lockVisibility: field.lockVisibility === true,
103
+ lockVisibility,
93
104
  expanded: true,
94
105
  fields,
95
106
  subsections,
@@ -268,7 +268,7 @@
268
268
 
269
269
  .cc-side-nav-label {
270
270
  flex: 1;
271
- white-space: nowrap;
271
+ white-space: wrap;
272
272
  overflow: hidden;
273
273
  max-width: 200px;
274
274
  opacity: 1;