@praxisui/metadata-editor 9.0.0-beta.9 → 9.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -0
- package/ai/component-registry.json +2067 -0
- package/fesm2022/praxisui-metadata-editor.mjs +1887 -186
- package/package.json +10 -6
- package/types/praxisui-metadata-editor.d.ts +22 -4
|
@@ -1,40 +1,42 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Injectable, inject, Component, Input, ViewEncapsulation, EventEmitter, DestroyRef, Output, signal, ENVIRONMENT_INITIALIZER } from '@angular/core';
|
|
2
|
+
import { Injectable, inject, Component, forwardRef, ElementRef, Input, Optional, ViewEncapsulation, EventEmitter, DestroyRef, Output, signal, ENVIRONMENT_INITIALIZER } from '@angular/core';
|
|
3
3
|
import * as i1 from '@angular/forms';
|
|
4
|
-
import { FormControl, FormGroup, ReactiveFormsModule, FormArray, FormBuilder
|
|
5
|
-
import * as i3 from '@angular/common';
|
|
4
|
+
import { FormControl, FormGroup, FormsModule, NG_VALUE_ACCESSOR, NG_VALIDATORS, ReactiveFormsModule, FormArray, FormBuilder } from '@angular/forms';
|
|
5
|
+
import * as i3$1 from '@angular/common';
|
|
6
6
|
import { CommonModule } from '@angular/common';
|
|
7
7
|
import { DynamicFieldLoaderDirective } from '@praxisui/dynamic-fields';
|
|
8
|
-
import * as i2$
|
|
9
|
-
import {
|
|
10
|
-
import * as i5 from '@angular/material/expansion';
|
|
8
|
+
import * as i2$2 from '@praxisui/core';
|
|
9
|
+
import { providePraxisI18n, PraxisI18nService, PraxisIconDirective, FieldControlType, INLINE_FILTER_CONTROL_TYPES, resolveInlineFilterControlType, normalizeControlTypeToken, ComponentMetadataRegistry } from '@praxisui/core';
|
|
10
|
+
import * as i5$1 from '@angular/material/expansion';
|
|
11
11
|
import { MatExpansionModule } from '@angular/material/expansion';
|
|
12
|
-
import * as
|
|
12
|
+
import * as i4 from '@angular/material/icon';
|
|
13
13
|
import { MatIconModule } from '@angular/material/icon';
|
|
14
|
-
import * as
|
|
14
|
+
import * as i2$1 from '@angular/material/button';
|
|
15
15
|
import { MatButtonModule } from '@angular/material/button';
|
|
16
16
|
import * as i1$1 from '@angular/material/dialog';
|
|
17
17
|
import { MatDialogRef, MatDialogModule } from '@angular/material/dialog';
|
|
18
|
-
import * as
|
|
18
|
+
import * as i7 from '@angular/material/tooltip';
|
|
19
19
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
20
20
|
import * as i2 from '@angular/material/list';
|
|
21
21
|
import { MatListModule } from '@angular/material/list';
|
|
22
|
+
import * as i3 from '@angular/material/form-field';
|
|
23
|
+
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
24
|
+
import * as i5 from '@angular/material/input';
|
|
25
|
+
import { MatInputModule } from '@angular/material/input';
|
|
26
|
+
import * as i6 from '@angular/material/select';
|
|
27
|
+
import { MatSelectModule } from '@angular/material/select';
|
|
22
28
|
import { SETTINGS_PANEL_DATA } from '@praxisui/settings-panel';
|
|
23
29
|
import { BehaviorSubject } from 'rxjs';
|
|
24
30
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
25
|
-
import * as i6$1 from '@angular/material/input';
|
|
26
|
-
import { MatInputModule } from '@angular/material/input';
|
|
27
31
|
import { MatChipsModule } from '@angular/material/chips';
|
|
28
32
|
import { MatDividerModule } from '@angular/material/divider';
|
|
29
|
-
import * as
|
|
30
|
-
import { MatSelectModule } from '@angular/material/select';
|
|
31
|
-
import * as i8$1 from '@angular/material/checkbox';
|
|
33
|
+
import * as i9 from '@angular/material/checkbox';
|
|
32
34
|
import { MatCheckboxModule } from '@angular/material/checkbox';
|
|
33
|
-
import * as
|
|
35
|
+
import * as i11 from '@angular/material/menu';
|
|
34
36
|
import { MatMenuModule } from '@angular/material/menu';
|
|
35
|
-
import * as
|
|
37
|
+
import * as i12 from '@angular/material/autocomplete';
|
|
36
38
|
import { MatAutocompleteModule } from '@angular/material/autocomplete';
|
|
37
|
-
import * as
|
|
39
|
+
import * as i13 from '@angular/cdk/scrolling';
|
|
38
40
|
import { ScrollingModule } from '@angular/cdk/scrolling';
|
|
39
41
|
|
|
40
42
|
class ConfigRegistryService {
|
|
@@ -390,23 +392,1425 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
|
|
|
390
392
|
<span matListItemLine>{{ r.label }}</span>
|
|
391
393
|
</a>
|
|
392
394
|
}
|
|
393
|
-
</mat-nav-list>
|
|
394
|
-
<p style="margin-top: 12px;">Recursos úteis:</p>
|
|
395
|
-
<ul>
|
|
396
|
-
<li><a href="https://regex101.com/" target="_blank" rel="noreferrer noopener">regex101.com</a> — Teste e depure regex</li>
|
|
397
|
-
<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions" target="_blank" rel="noreferrer noopener">MDN: Regular expressions</a></li>
|
|
398
|
-
</ul>
|
|
399
|
-
</div>
|
|
400
|
-
<div mat-dialog-actions>
|
|
401
|
-
<button class="mat-mdc-button mdc-button" mat-dialog-close>Fechar</button>
|
|
402
|
-
</div>
|
|
403
|
-
`,
|
|
404
|
-
}]
|
|
405
|
-
}] });
|
|
395
|
+
</mat-nav-list>
|
|
396
|
+
<p style="margin-top: 12px;">Recursos úteis:</p>
|
|
397
|
+
<ul>
|
|
398
|
+
<li><a href="https://regex101.com/" target="_blank" rel="noreferrer noopener">regex101.com</a> — Teste e depure regex</li>
|
|
399
|
+
<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions" target="_blank" rel="noreferrer noopener">MDN: Regular expressions</a></li>
|
|
400
|
+
</ul>
|
|
401
|
+
</div>
|
|
402
|
+
<div mat-dialog-actions>
|
|
403
|
+
<button class="mat-mdc-button mdc-button" mat-dialog-close>Fechar</button>
|
|
404
|
+
</div>
|
|
405
|
+
`,
|
|
406
|
+
}]
|
|
407
|
+
}] });
|
|
408
|
+
|
|
409
|
+
const METADATA_EDITOR_EN_US = {
|
|
410
|
+
'praxis.metadataEditor.arrayFields.section': 'Collection subfields',
|
|
411
|
+
'praxis.metadataEditor.arrayFields.title': 'Subfields',
|
|
412
|
+
'praxis.metadataEditor.arrayFields.add': 'Add subfield',
|
|
413
|
+
'praxis.metadataEditor.arrayFields.duplicateNames': 'There are duplicate names: {{names}}',
|
|
414
|
+
'praxis.metadataEditor.arrayFields.empty': 'No local subfields configured.',
|
|
415
|
+
'praxis.metadataEditor.arrayFields.name': 'Name',
|
|
416
|
+
'praxis.metadataEditor.arrayFields.nameRequired': 'Enter the subfield name.',
|
|
417
|
+
'praxis.metadataEditor.arrayFields.nameDuplicated': 'This name is already used in the collection.',
|
|
418
|
+
'praxis.metadataEditor.arrayFields.label': 'Label',
|
|
419
|
+
'praxis.metadataEditor.arrayFields.controlType': 'Control',
|
|
420
|
+
'praxis.metadataEditor.arrayFields.required': 'Required',
|
|
421
|
+
'praxis.metadataEditor.arrayFields.moveUp': 'Move up',
|
|
422
|
+
'praxis.metadataEditor.arrayFields.moveDown': 'Move down',
|
|
423
|
+
'praxis.metadataEditor.arrayFields.duplicate': 'Duplicate',
|
|
424
|
+
'praxis.metadataEditor.arrayFields.remove': 'Remove',
|
|
425
|
+
'praxis.metadataEditor.arrayFields.removeBlocked': 'Remove references before deleting: {{references}}',
|
|
426
|
+
'praxis.metadataEditor.arrayFields.refOnly': 'The item schema comes from itemSchemaRef; create local subfields only when you need an override.',
|
|
427
|
+
'praxis.metadataEditor.arrayFields.overrideLocal': 'There is an itemSchemaRef and local subfields; local subfields work as an explicit override.',
|
|
428
|
+
'praxis.metadataEditor.arrayFields.imported': 'Subfields imported. Review them before applying or saving.',
|
|
429
|
+
'praxis.metadataEditor.arrayFields.localOnly': 'Local subfields will be saved in array.itemSchema.fields.',
|
|
430
|
+
'praxis.metadataEditor.arrayFields.control.input': 'Text',
|
|
431
|
+
'praxis.metadataEditor.arrayFields.control.textarea': 'Long text',
|
|
432
|
+
'praxis.metadataEditor.arrayFields.control.numeric': 'Number',
|
|
433
|
+
'praxis.metadataEditor.arrayFields.control.currency': 'Currency',
|
|
434
|
+
'praxis.metadataEditor.arrayFields.control.email': 'Email',
|
|
435
|
+
'praxis.metadataEditor.arrayFields.control.phone': 'Phone',
|
|
436
|
+
'praxis.metadataEditor.arrayFields.control.select': 'Select',
|
|
437
|
+
'praxis.metadataEditor.arrayFields.control.entityLookup': 'Entity lookup',
|
|
438
|
+
'praxis.metadataEditor.arrayFields.control.toggle': 'Toggle',
|
|
439
|
+
'praxis.metadataEditor.arrayFields.control.date': 'Date',
|
|
440
|
+
'praxis.metadataEditor.fieldResource.label': 'Governed resource',
|
|
441
|
+
'praxis.metadataEditor.fieldResource.searchPlaceholder': 'Search by name, domain, or path',
|
|
442
|
+
'praxis.metadataEditor.fieldResource.hint': 'Select from the host governed catalog. The saved value remains resourcePath.',
|
|
443
|
+
'praxis.metadataEditor.common.help': 'Help',
|
|
444
|
+
'praxis.metadataEditor.common.iconMaterialSymbolsHint': 'Use the icon name (Material Symbols)',
|
|
445
|
+
'praxis.metadataEditor.presentationFields.group': 'Presentation',
|
|
446
|
+
'praxis.metadataEditor.presentationFields.valuePresentation.label': 'Value format',
|
|
447
|
+
'praxis.metadataEditor.presentationFields.valuePresentation.hint': 'Canonical formatter used on readonly/presentation surfaces. Use a JSON object.',
|
|
448
|
+
'praxis.metadataEditor.presentationFields.presenter.label': 'Presenter',
|
|
449
|
+
'praxis.metadataEditor.presentationFields.presenter.hint': 'Semantic rendering strategy for presentationMode.',
|
|
450
|
+
'praxis.metadataEditor.presentationFields.presenter.option.text': 'Text',
|
|
451
|
+
'praxis.metadataEditor.presentationFields.presenter.option.status': 'Status',
|
|
452
|
+
'praxis.metadataEditor.presentationFields.presenter.option.chip': 'Chip',
|
|
453
|
+
'praxis.metadataEditor.presentationFields.presenter.option.badge': 'Badge',
|
|
454
|
+
'praxis.metadataEditor.presentationFields.presenter.option.iconValue': 'Icon + value',
|
|
455
|
+
'praxis.metadataEditor.presentationFields.presenter.option.progress': 'Progress',
|
|
456
|
+
'praxis.metadataEditor.presentationFields.presenter.option.rating': 'Rating',
|
|
457
|
+
'praxis.metadataEditor.presentationFields.tone.label': 'Tone',
|
|
458
|
+
'praxis.metadataEditor.presentationFields.tone.hint': 'Semantic tone mapped by the runtime to theme tokens.',
|
|
459
|
+
'praxis.metadataEditor.presentationFields.tone.option.neutral': 'Neutral',
|
|
460
|
+
'praxis.metadataEditor.presentationFields.tone.option.info': 'Info',
|
|
461
|
+
'praxis.metadataEditor.presentationFields.tone.option.success': 'Success',
|
|
462
|
+
'praxis.metadataEditor.presentationFields.tone.option.warning': 'Warning',
|
|
463
|
+
'praxis.metadataEditor.presentationFields.tone.option.danger': 'Critical',
|
|
464
|
+
'praxis.metadataEditor.presentationFields.appearance.label': 'Emphasis',
|
|
465
|
+
'praxis.metadataEditor.presentationFields.appearance.hint': 'Visual emphasis level for the presenter.',
|
|
466
|
+
'praxis.metadataEditor.presentationFields.appearance.option.plain': 'Plain',
|
|
467
|
+
'praxis.metadataEditor.presentationFields.appearance.option.soft': 'Soft',
|
|
468
|
+
'praxis.metadataEditor.presentationFields.appearance.option.outlined': 'Outlined',
|
|
469
|
+
'praxis.metadataEditor.presentationFields.appearance.option.filled': 'Filled',
|
|
470
|
+
'praxis.metadataEditor.presentationFields.icon.label': 'Icon',
|
|
471
|
+
'praxis.metadataEditor.presentationFields.icon.hint': 'Material Symbols icon name, without HTML.',
|
|
472
|
+
'praxis.metadataEditor.presentationFields.label.label': 'Label',
|
|
473
|
+
'praxis.metadataEditor.presentationFields.label.placeholder': 'Pending',
|
|
474
|
+
'praxis.metadataEditor.presentationFields.label.hint': 'Semantic text shown by status, chip or badge presenters.',
|
|
475
|
+
'praxis.metadataEditor.presentationFields.badge.label': 'Badge',
|
|
476
|
+
'praxis.metadataEditor.presentationFields.badge.hint': 'Optional secondary marker shown with the value.',
|
|
477
|
+
'praxis.metadataEditor.presentationFields.tooltip.label': 'Tooltip',
|
|
478
|
+
'praxis.metadataEditor.presentationFields.tooltip.placeholder': 'Awaiting fiscal approval',
|
|
479
|
+
'praxis.metadataEditor.presentationFields.tooltip.hint': 'Short explanation shown as a tooltip.',
|
|
480
|
+
'praxis.metadataEditor.presentationFields.presentationRules.label': 'Presentation rules',
|
|
481
|
+
'praxis.metadataEditor.presentationFields.presentationRules.hint': 'Guided builder for ordered Json Logic rules that only change presentation. Later rules win.',
|
|
482
|
+
'praxis.metadataEditor.presentationChoice.help': 'Help',
|
|
483
|
+
'praxis.metadataEditor.presentationChoice.clear': 'Clear selection',
|
|
484
|
+
'praxis.metadataEditor.presentationRulesBuilder.title': 'Presentation rules',
|
|
485
|
+
'praxis.metadataEditor.presentationRulesBuilder.description': 'Change tone, icon, and labels without changing the submitted value.',
|
|
486
|
+
'praxis.metadataEditor.presentationRulesBuilder.add': 'Add rule',
|
|
487
|
+
'praxis.metadataEditor.presentationRulesBuilder.empty': 'No rules configured. Add rules for states such as delay, block, or approval.',
|
|
488
|
+
'praxis.metadataEditor.presentationRulesBuilder.rule': 'Rule',
|
|
489
|
+
'praxis.metadataEditor.presentationRulesBuilder.mode': 'Rule mode',
|
|
490
|
+
'praxis.metadataEditor.presentationRulesBuilder.guided': 'Guided',
|
|
491
|
+
'praxis.metadataEditor.presentationRulesBuilder.advanced': 'Advanced JSON',
|
|
492
|
+
'praxis.metadataEditor.presentationRulesBuilder.advancedRule': 'JSON rule',
|
|
493
|
+
'praxis.metadataEditor.presentationRulesBuilder.moveUp': 'Move up',
|
|
494
|
+
'praxis.metadataEditor.presentationRulesBuilder.moveDown': 'Move down',
|
|
495
|
+
'praxis.metadataEditor.presentationRulesBuilder.remove': 'Remove',
|
|
496
|
+
'praxis.metadataEditor.presentationRulesBuilder.sourcePath': 'Variable',
|
|
497
|
+
'praxis.metadataEditor.presentationRulesBuilder.operator': 'Condition',
|
|
498
|
+
'praxis.metadataEditor.presentationRulesBuilder.compareValue': 'Value',
|
|
499
|
+
'praxis.metadataEditor.presentationRulesBuilder.keep': 'Keep',
|
|
500
|
+
'praxis.metadataEditor.presentationRulesBuilder.tone': 'Tone',
|
|
501
|
+
'praxis.metadataEditor.presentationRulesBuilder.appearance': 'Emphasis',
|
|
502
|
+
'praxis.metadataEditor.presentationRulesBuilder.icon': 'Icon',
|
|
503
|
+
'praxis.metadataEditor.presentationRulesBuilder.label': 'Label',
|
|
504
|
+
'praxis.metadataEditor.presentationRulesBuilder.badge': 'Badge',
|
|
505
|
+
'praxis.metadataEditor.presentationRulesBuilder.tooltip': 'Tooltip',
|
|
506
|
+
'praxis.metadataEditor.presentationRulesBuilder.invalidJson': 'Invalid JSON. Fix the rule before applying.',
|
|
507
|
+
'praxis.metadataEditor.presentationRulesBuilder.invalidRuleShape': 'The rule must be an object with when and set.',
|
|
508
|
+
'praxis.metadataEditor.presentationRulesBuilder.unsupportedEffects': 'Use only semantic presentation effects: presenter, tone, appearance, icon, label, badge, tooltip, valuePresentation and interactions.',
|
|
509
|
+
'praxis.metadataEditor.presentationRulesBuilder.summaryAdvanced': 'Advanced rule preserved as JSON.',
|
|
510
|
+
'praxis.metadataEditor.presentationRulesBuilder.summaryEffects': '{{count}} effect(s)',
|
|
511
|
+
'praxis.metadataEditor.presentationRulesBuilder.summaryNoEffects': 'no effects defined',
|
|
512
|
+
'praxis.metadataEditor.presentationRulesBuilder.operator.equals': 'equals',
|
|
513
|
+
'praxis.metadataEditor.presentationRulesBuilder.operator.notEquals': 'does not equal',
|
|
514
|
+
'praxis.metadataEditor.presentationRulesBuilder.operator.greaterThan': 'greater than',
|
|
515
|
+
'praxis.metadataEditor.presentationRulesBuilder.operator.greaterOrEqual': 'greater than or equal to',
|
|
516
|
+
'praxis.metadataEditor.presentationRulesBuilder.operator.lessThan': 'less than',
|
|
517
|
+
'praxis.metadataEditor.presentationRulesBuilder.operator.lessOrEqual': 'less than or equal to',
|
|
518
|
+
'praxis.metadataEditor.presentationRulesBuilder.operator.contains': 'contains',
|
|
519
|
+
'praxis.metadataEditor.presentationRulesBuilder.operator.exists': 'exists',
|
|
520
|
+
'praxis.metadataEditor.presentationRulesBuilder.tone.neutral': 'Neutral',
|
|
521
|
+
'praxis.metadataEditor.presentationRulesBuilder.tone.info': 'Info',
|
|
522
|
+
'praxis.metadataEditor.presentationRulesBuilder.tone.success': 'Success',
|
|
523
|
+
'praxis.metadataEditor.presentationRulesBuilder.tone.warning': 'Warning',
|
|
524
|
+
'praxis.metadataEditor.presentationRulesBuilder.tone.danger': 'Critical',
|
|
525
|
+
'praxis.metadataEditor.presentationRulesBuilder.appearance.plain': 'Plain',
|
|
526
|
+
'praxis.metadataEditor.presentationRulesBuilder.appearance.soft': 'Soft',
|
|
527
|
+
'praxis.metadataEditor.presentationRulesBuilder.appearance.outlined': 'Outlined',
|
|
528
|
+
'praxis.metadataEditor.presentationRulesBuilder.appearance.filled': 'Filled',
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
const METADATA_EDITOR_PT_BR = {
|
|
532
|
+
'praxis.metadataEditor.arrayFields.section': 'Subcampos da cole\u00e7\u00e3o',
|
|
533
|
+
'praxis.metadataEditor.arrayFields.title': 'Subcampos',
|
|
534
|
+
'praxis.metadataEditor.arrayFields.add': 'Adicionar subcampo',
|
|
535
|
+
'praxis.metadataEditor.arrayFields.duplicateNames': 'Existem nomes duplicados: {{names}}',
|
|
536
|
+
'praxis.metadataEditor.arrayFields.empty': 'Nenhum subcampo local configurado.',
|
|
537
|
+
'praxis.metadataEditor.arrayFields.name': 'Nome',
|
|
538
|
+
'praxis.metadataEditor.arrayFields.nameRequired': 'Informe o nome do subcampo.',
|
|
539
|
+
'praxis.metadataEditor.arrayFields.nameDuplicated': 'Este nome j\u00e1 est\u00e1 em uso na cole\u00e7\u00e3o.',
|
|
540
|
+
'praxis.metadataEditor.arrayFields.label': 'Label',
|
|
541
|
+
'praxis.metadataEditor.arrayFields.controlType': 'Controle',
|
|
542
|
+
'praxis.metadataEditor.arrayFields.required': 'Obrigat\u00f3rio',
|
|
543
|
+
'praxis.metadataEditor.arrayFields.moveUp': 'Subir',
|
|
544
|
+
'praxis.metadataEditor.arrayFields.moveDown': 'Descer',
|
|
545
|
+
'praxis.metadataEditor.arrayFields.duplicate': 'Duplicar',
|
|
546
|
+
'praxis.metadataEditor.arrayFields.remove': 'Remover',
|
|
547
|
+
'praxis.metadataEditor.arrayFields.removeBlocked': 'Remova as refer\u00eancias antes de excluir: {{references}}',
|
|
548
|
+
'praxis.metadataEditor.arrayFields.refOnly': 'O schema do item vem do itemSchemaRef; crie subcampos locais apenas quando precisar de override.',
|
|
549
|
+
'praxis.metadataEditor.arrayFields.overrideLocal': 'H\u00e1 um itemSchemaRef e subcampos locais; os subcampos locais funcionam como override expl\u00edcito.',
|
|
550
|
+
'praxis.metadataEditor.arrayFields.imported': 'Subcampos importados. Revise antes de aplicar ou salvar.',
|
|
551
|
+
'praxis.metadataEditor.arrayFields.localOnly': 'Subcampos locais ser\u00e3o salvos em array.itemSchema.fields.',
|
|
552
|
+
'praxis.metadataEditor.arrayFields.control.input': 'Texto',
|
|
553
|
+
'praxis.metadataEditor.arrayFields.control.textarea': 'Texto longo',
|
|
554
|
+
'praxis.metadataEditor.arrayFields.control.numeric': 'N\u00famero',
|
|
555
|
+
'praxis.metadataEditor.arrayFields.control.currency': 'Moeda',
|
|
556
|
+
'praxis.metadataEditor.arrayFields.control.email': 'E-mail',
|
|
557
|
+
'praxis.metadataEditor.arrayFields.control.phone': 'Telefone',
|
|
558
|
+
'praxis.metadataEditor.arrayFields.control.select': 'Sele\u00e7\u00e3o',
|
|
559
|
+
'praxis.metadataEditor.arrayFields.control.entityLookup': 'Busca de entidade',
|
|
560
|
+
'praxis.metadataEditor.arrayFields.control.toggle': 'Liga/desliga',
|
|
561
|
+
'praxis.metadataEditor.arrayFields.control.date': 'Data',
|
|
562
|
+
'praxis.metadataEditor.fieldResource.label': 'Recurso governado',
|
|
563
|
+
'praxis.metadataEditor.fieldResource.searchPlaceholder': 'Buscar por nome, dom\u00ednio ou caminho',
|
|
564
|
+
'praxis.metadataEditor.fieldResource.hint': 'Selecione no cat\u00e1logo governado do host. O valor gravado continua sendo resourcePath.',
|
|
565
|
+
'praxis.metadataEditor.common.help': 'Ajuda',
|
|
566
|
+
'praxis.metadataEditor.common.iconMaterialSymbolsHint': 'Use o nome do \u00edcone (Material Symbols)',
|
|
567
|
+
'praxis.metadataEditor.presentationFields.group': 'Apresenta\u00e7\u00e3o',
|
|
568
|
+
'praxis.metadataEditor.presentationFields.valuePresentation.label': 'Formato do valor',
|
|
569
|
+
'praxis.metadataEditor.presentationFields.valuePresentation.hint': 'Formatador can\u00f4nico usado em superf\u00edcies readonly/presentation. Use um objeto JSON.',
|
|
570
|
+
'praxis.metadataEditor.presentationFields.presenter.label': 'Apresentador',
|
|
571
|
+
'praxis.metadataEditor.presentationFields.presenter.hint': 'Estrat\u00e9gia sem\u00e2ntica para renderiza\u00e7\u00e3o em presentationMode.',
|
|
572
|
+
'praxis.metadataEditor.presentationFields.presenter.option.text': 'Texto',
|
|
573
|
+
'praxis.metadataEditor.presentationFields.presenter.option.status': 'Status',
|
|
574
|
+
'praxis.metadataEditor.presentationFields.presenter.option.chip': 'Chip',
|
|
575
|
+
'praxis.metadataEditor.presentationFields.presenter.option.badge': 'Badge',
|
|
576
|
+
'praxis.metadataEditor.presentationFields.presenter.option.iconValue': '\u00cdcone + valor',
|
|
577
|
+
'praxis.metadataEditor.presentationFields.presenter.option.progress': 'Progresso',
|
|
578
|
+
'praxis.metadataEditor.presentationFields.presenter.option.rating': 'Avalia\u00e7\u00e3o',
|
|
579
|
+
'praxis.metadataEditor.presentationFields.tone.label': 'Tom',
|
|
580
|
+
'praxis.metadataEditor.presentationFields.tone.hint': 'Tom sem\u00e2ntico mapeado pelo runtime para tokens de tema.',
|
|
581
|
+
'praxis.metadataEditor.presentationFields.tone.option.neutral': 'Neutro',
|
|
582
|
+
'praxis.metadataEditor.presentationFields.tone.option.info': 'Informa\u00e7\u00e3o',
|
|
583
|
+
'praxis.metadataEditor.presentationFields.tone.option.success': 'Sucesso',
|
|
584
|
+
'praxis.metadataEditor.presentationFields.tone.option.warning': 'Aten\u00e7\u00e3o',
|
|
585
|
+
'praxis.metadataEditor.presentationFields.tone.option.danger': 'Cr\u00edtico',
|
|
586
|
+
'praxis.metadataEditor.presentationFields.appearance.label': '\u00canfase',
|
|
587
|
+
'praxis.metadataEditor.presentationFields.appearance.hint': 'N\u00edvel de \u00eanfase visual do apresentador.',
|
|
588
|
+
'praxis.metadataEditor.presentationFields.appearance.option.plain': 'Simples',
|
|
589
|
+
'praxis.metadataEditor.presentationFields.appearance.option.soft': 'Suave',
|
|
590
|
+
'praxis.metadataEditor.presentationFields.appearance.option.outlined': 'Contornado',
|
|
591
|
+
'praxis.metadataEditor.presentationFields.appearance.option.filled': 'Preenchido',
|
|
592
|
+
'praxis.metadataEditor.presentationFields.icon.label': '\u00cdcone',
|
|
593
|
+
'praxis.metadataEditor.presentationFields.icon.hint': 'Nome do \u00edcone Material Symbols, sem HTML.',
|
|
594
|
+
'praxis.metadataEditor.presentationFields.label.label': 'R\u00f3tulo',
|
|
595
|
+
'praxis.metadataEditor.presentationFields.label.placeholder': 'Pendente',
|
|
596
|
+
'praxis.metadataEditor.presentationFields.label.hint': 'Texto sem\u00e2ntico exibido por status, chip ou badge.',
|
|
597
|
+
'praxis.metadataEditor.presentationFields.badge.label': 'Badge',
|
|
598
|
+
'praxis.metadataEditor.presentationFields.badge.hint': 'Marcador secund\u00e1rio opcional junto ao valor.',
|
|
599
|
+
'praxis.metadataEditor.presentationFields.tooltip.label': 'Tooltip',
|
|
600
|
+
'praxis.metadataEditor.presentationFields.tooltip.placeholder': 'Aguardando aprova\u00e7\u00e3o fiscal',
|
|
601
|
+
'praxis.metadataEditor.presentationFields.tooltip.hint': 'Explica\u00e7\u00e3o curta exibida como tooltip.',
|
|
602
|
+
'praxis.metadataEditor.presentationFields.presentationRules.label': 'Regras de apresenta\u00e7\u00e3o',
|
|
603
|
+
'praxis.metadataEditor.presentationFields.presentationRules.hint': 'Builder guiado para regras Json Logic ordenadas que alteram somente a apresenta\u00e7\u00e3o. Regras posteriores vencem.',
|
|
604
|
+
'praxis.metadataEditor.presentationChoice.help': 'Ajuda',
|
|
605
|
+
'praxis.metadataEditor.presentationChoice.clear': 'Limpar sele\u00e7\u00e3o',
|
|
606
|
+
'praxis.metadataEditor.presentationRulesBuilder.title': 'Regras de apresenta\u00e7\u00e3o',
|
|
607
|
+
'praxis.metadataEditor.presentationRulesBuilder.description': 'Altere tom, \u00edcone e r\u00f3tulos sem modificar o valor enviado.',
|
|
608
|
+
'praxis.metadataEditor.presentationRulesBuilder.add': 'Adicionar regra',
|
|
609
|
+
'praxis.metadataEditor.presentationRulesBuilder.empty': 'Nenhuma regra configurada. Adicione regras para estados como atraso, bloqueio ou aprova\u00e7\u00e3o.',
|
|
610
|
+
'praxis.metadataEditor.presentationRulesBuilder.rule': 'Regra',
|
|
611
|
+
'praxis.metadataEditor.presentationRulesBuilder.mode': 'Modo da regra',
|
|
612
|
+
'praxis.metadataEditor.presentationRulesBuilder.guided': 'Guiado',
|
|
613
|
+
'praxis.metadataEditor.presentationRulesBuilder.advanced': 'JSON avan\u00e7ado',
|
|
614
|
+
'praxis.metadataEditor.presentationRulesBuilder.advancedRule': 'Regra JSON',
|
|
615
|
+
'praxis.metadataEditor.presentationRulesBuilder.moveUp': 'Subir',
|
|
616
|
+
'praxis.metadataEditor.presentationRulesBuilder.moveDown': 'Descer',
|
|
617
|
+
'praxis.metadataEditor.presentationRulesBuilder.remove': 'Remover',
|
|
618
|
+
'praxis.metadataEditor.presentationRulesBuilder.sourcePath': 'Vari\u00e1vel',
|
|
619
|
+
'praxis.metadataEditor.presentationRulesBuilder.operator': 'Condi\u00e7\u00e3o',
|
|
620
|
+
'praxis.metadataEditor.presentationRulesBuilder.compareValue': 'Valor',
|
|
621
|
+
'praxis.metadataEditor.presentationRulesBuilder.keep': 'Manter',
|
|
622
|
+
'praxis.metadataEditor.presentationRulesBuilder.tone': 'Tom',
|
|
623
|
+
'praxis.metadataEditor.presentationRulesBuilder.appearance': '\u00canfase',
|
|
624
|
+
'praxis.metadataEditor.presentationRulesBuilder.icon': '\u00cdcone',
|
|
625
|
+
'praxis.metadataEditor.presentationRulesBuilder.label': 'R\u00f3tulo',
|
|
626
|
+
'praxis.metadataEditor.presentationRulesBuilder.badge': 'Badge',
|
|
627
|
+
'praxis.metadataEditor.presentationRulesBuilder.tooltip': 'Tooltip',
|
|
628
|
+
'praxis.metadataEditor.presentationRulesBuilder.invalidJson': 'JSON inv\u00e1lido. Corrija a regra antes de aplicar.',
|
|
629
|
+
'praxis.metadataEditor.presentationRulesBuilder.invalidRuleShape': 'A regra deve ser um objeto com when e set.',
|
|
630
|
+
'praxis.metadataEditor.presentationRulesBuilder.unsupportedEffects': 'Use apenas efeitos sem\u00e2nticos de apresenta\u00e7\u00e3o: presenter, tone, appearance, icon, label, badge, tooltip, valuePresentation e interactions.',
|
|
631
|
+
'praxis.metadataEditor.presentationRulesBuilder.summaryAdvanced': 'Regra avan\u00e7ada preservada como JSON.',
|
|
632
|
+
'praxis.metadataEditor.presentationRulesBuilder.summaryEffects': '{{count}} efeito(s)',
|
|
633
|
+
'praxis.metadataEditor.presentationRulesBuilder.summaryNoEffects': 'sem efeitos definidos',
|
|
634
|
+
'praxis.metadataEditor.presentationRulesBuilder.operator.equals': 'igual a',
|
|
635
|
+
'praxis.metadataEditor.presentationRulesBuilder.operator.notEquals': 'diferente de',
|
|
636
|
+
'praxis.metadataEditor.presentationRulesBuilder.operator.greaterThan': 'maior que',
|
|
637
|
+
'praxis.metadataEditor.presentationRulesBuilder.operator.greaterOrEqual': 'maior ou igual a',
|
|
638
|
+
'praxis.metadataEditor.presentationRulesBuilder.operator.lessThan': 'menor que',
|
|
639
|
+
'praxis.metadataEditor.presentationRulesBuilder.operator.lessOrEqual': 'menor ou igual a',
|
|
640
|
+
'praxis.metadataEditor.presentationRulesBuilder.operator.contains': 'cont\u00e9m',
|
|
641
|
+
'praxis.metadataEditor.presentationRulesBuilder.operator.exists': 'existe',
|
|
642
|
+
'praxis.metadataEditor.presentationRulesBuilder.tone.neutral': 'Neutro',
|
|
643
|
+
'praxis.metadataEditor.presentationRulesBuilder.tone.info': 'Informa\u00e7\u00e3o',
|
|
644
|
+
'praxis.metadataEditor.presentationRulesBuilder.tone.success': 'Sucesso',
|
|
645
|
+
'praxis.metadataEditor.presentationRulesBuilder.tone.warning': 'Aten\u00e7\u00e3o',
|
|
646
|
+
'praxis.metadataEditor.presentationRulesBuilder.tone.danger': 'Cr\u00edtico',
|
|
647
|
+
'praxis.metadataEditor.presentationRulesBuilder.appearance.plain': 'Simples',
|
|
648
|
+
'praxis.metadataEditor.presentationRulesBuilder.appearance.soft': 'Suave',
|
|
649
|
+
'praxis.metadataEditor.presentationRulesBuilder.appearance.outlined': 'Contornado',
|
|
650
|
+
'praxis.metadataEditor.presentationRulesBuilder.appearance.filled': 'Preenchido',
|
|
651
|
+
};
|
|
652
|
+
|
|
653
|
+
function createPraxisMetadataEditorI18nConfig(options = {}) {
|
|
654
|
+
const dictionaries = {
|
|
655
|
+
'pt-BR': {
|
|
656
|
+
...METADATA_EDITOR_PT_BR,
|
|
657
|
+
...(options.dictionaries?.['pt-BR'] ?? {}),
|
|
658
|
+
},
|
|
659
|
+
'en-US': {
|
|
660
|
+
...METADATA_EDITOR_EN_US,
|
|
661
|
+
...(options.dictionaries?.['en-US'] ?? {}),
|
|
662
|
+
},
|
|
663
|
+
};
|
|
664
|
+
for (const [locale, dictionary] of Object.entries(options.dictionaries ?? {})) {
|
|
665
|
+
if (locale === 'pt-BR' || locale === 'en-US') {
|
|
666
|
+
continue;
|
|
667
|
+
}
|
|
668
|
+
dictionaries[locale] = {
|
|
669
|
+
...(dictionaries[locale] ?? {}),
|
|
670
|
+
...dictionary,
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
return {
|
|
674
|
+
locale: options.locale,
|
|
675
|
+
fallbackLocale: options.fallbackLocale ?? 'pt-BR',
|
|
676
|
+
dictionaries,
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
const METADATA_EDITOR_I18N_CONFIG = createPraxisMetadataEditorI18nConfig();
|
|
680
|
+
function providePraxisMetadataEditorI18n(options = {}) {
|
|
681
|
+
return providePraxisI18n(createPraxisMetadataEditorI18nConfig(options));
|
|
682
|
+
}
|
|
683
|
+
function resolvePraxisMetadataEditorText(i18n, key, fallback) {
|
|
684
|
+
const namespacedKey = key.startsWith('praxis.metadataEditor.')
|
|
685
|
+
? key
|
|
686
|
+
: `praxis.metadataEditor.${key}`;
|
|
687
|
+
return i18n.t(namespacedKey, undefined, fallback) || fallback;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
const OPERATOR_TO_JSON_LOGIC = {
|
|
691
|
+
equals: '===',
|
|
692
|
+
notEquals: '!==',
|
|
693
|
+
greaterThan: '>',
|
|
694
|
+
greaterOrEqual: '>=',
|
|
695
|
+
lessThan: '<',
|
|
696
|
+
lessOrEqual: '<=',
|
|
697
|
+
};
|
|
698
|
+
const PRESENTATION_RULE_EFFECT_KEYS = [
|
|
699
|
+
'presenter',
|
|
700
|
+
'tone',
|
|
701
|
+
'appearance',
|
|
702
|
+
'icon',
|
|
703
|
+
'label',
|
|
704
|
+
'badge',
|
|
705
|
+
'tooltip',
|
|
706
|
+
'valuePresentation',
|
|
707
|
+
'interactions',
|
|
708
|
+
];
|
|
709
|
+
const PRESENTATION_RULE_EFFECT_KEY_SET = new Set(PRESENTATION_RULE_EFFECT_KEYS);
|
|
710
|
+
const GUIDED_PRESENTATION_RULE_EFFECT_KEYS = [
|
|
711
|
+
'tone',
|
|
712
|
+
'appearance',
|
|
713
|
+
'icon',
|
|
714
|
+
'label',
|
|
715
|
+
'badge',
|
|
716
|
+
'tooltip',
|
|
717
|
+
];
|
|
718
|
+
const GUIDED_PRESENTATION_RULE_EFFECT_KEY_SET = new Set(GUIDED_PRESENTATION_RULE_EFFECT_KEYS);
|
|
719
|
+
class PresentationRulesBuilderComponent {
|
|
720
|
+
i18n = inject(PraxisI18nService);
|
|
721
|
+
rules = [];
|
|
722
|
+
disabled = false;
|
|
723
|
+
nextId = 1;
|
|
724
|
+
operatorOptions = [
|
|
725
|
+
{ value: 'equals', label: this.text('presentationRulesBuilder.operator.equals', 'igual a') },
|
|
726
|
+
{ value: 'notEquals', label: this.text('presentationRulesBuilder.operator.notEquals', 'diferente de') },
|
|
727
|
+
{ value: 'greaterThan', label: this.text('presentationRulesBuilder.operator.greaterThan', 'maior que') },
|
|
728
|
+
{ value: 'greaterOrEqual', label: this.text('presentationRulesBuilder.operator.greaterOrEqual', 'maior ou igual a') },
|
|
729
|
+
{ value: 'lessThan', label: this.text('presentationRulesBuilder.operator.lessThan', 'menor que') },
|
|
730
|
+
{ value: 'lessOrEqual', label: this.text('presentationRulesBuilder.operator.lessOrEqual', 'menor ou igual a') },
|
|
731
|
+
{ value: 'contains', label: this.text('presentationRulesBuilder.operator.contains', 'contém') },
|
|
732
|
+
{ value: 'exists', label: this.text('presentationRulesBuilder.operator.exists', 'existe') },
|
|
733
|
+
];
|
|
734
|
+
toneOptions = [
|
|
735
|
+
{ value: 'neutral', label: this.text('presentationRulesBuilder.tone.neutral', 'Neutro') },
|
|
736
|
+
{ value: 'info', label: this.text('presentationRulesBuilder.tone.info', 'Informação') },
|
|
737
|
+
{ value: 'success', label: this.text('presentationRulesBuilder.tone.success', 'Sucesso') },
|
|
738
|
+
{ value: 'warning', label: this.text('presentationRulesBuilder.tone.warning', 'Atenção') },
|
|
739
|
+
{ value: 'danger', label: this.text('presentationRulesBuilder.tone.danger', 'Crítico') },
|
|
740
|
+
];
|
|
741
|
+
appearanceOptions = [
|
|
742
|
+
{ value: 'plain', label: this.text('presentationRulesBuilder.appearance.plain', 'Simples') },
|
|
743
|
+
{ value: 'soft', label: this.text('presentationRulesBuilder.appearance.soft', 'Suave') },
|
|
744
|
+
{ value: 'outlined', label: this.text('presentationRulesBuilder.appearance.outlined', 'Contornado') },
|
|
745
|
+
{ value: 'filled', label: this.text('presentationRulesBuilder.appearance.filled', 'Preenchido') },
|
|
746
|
+
];
|
|
747
|
+
onChange = () => { };
|
|
748
|
+
onTouched = () => { };
|
|
749
|
+
onValidatorChange = () => { };
|
|
750
|
+
writeValue(value) {
|
|
751
|
+
const parsed = this.coerceRules(value);
|
|
752
|
+
this.rules = parsed.map((rule) => this.hydrateRule(rule));
|
|
753
|
+
}
|
|
754
|
+
registerOnChange(fn) {
|
|
755
|
+
this.onChange = fn;
|
|
756
|
+
}
|
|
757
|
+
registerOnTouched(fn) {
|
|
758
|
+
this.onTouched = fn;
|
|
759
|
+
}
|
|
760
|
+
registerOnValidatorChange(fn) {
|
|
761
|
+
this.onValidatorChange = fn;
|
|
762
|
+
}
|
|
763
|
+
validate(_control) {
|
|
764
|
+
return this.hasInvalidAdvancedJson()
|
|
765
|
+
? { presentationRulesInvalidJson: true }
|
|
766
|
+
: null;
|
|
767
|
+
}
|
|
768
|
+
setDisabledState(isDisabled) {
|
|
769
|
+
this.disabled = isDisabled;
|
|
770
|
+
}
|
|
771
|
+
text(key, fallback) {
|
|
772
|
+
return resolvePraxisMetadataEditorText(this.i18n, key, fallback);
|
|
773
|
+
}
|
|
774
|
+
addRule() {
|
|
775
|
+
this.rules = [...this.rules, this.createDraft()];
|
|
776
|
+
this.emit();
|
|
777
|
+
}
|
|
778
|
+
removeRule(index) {
|
|
779
|
+
this.rules = this.rules.filter((_, candidateIndex) => candidateIndex !== index);
|
|
780
|
+
this.emit();
|
|
781
|
+
}
|
|
782
|
+
moveRule(index, delta) {
|
|
783
|
+
const target = index + delta;
|
|
784
|
+
if (target < 0 || target >= this.rules.length) {
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
787
|
+
const next = [...this.rules];
|
|
788
|
+
const [rule] = next.splice(index, 1);
|
|
789
|
+
next.splice(target, 0, rule);
|
|
790
|
+
this.rules = next;
|
|
791
|
+
this.emit();
|
|
792
|
+
}
|
|
793
|
+
setMode(rule, mode) {
|
|
794
|
+
if (rule.mode === mode) {
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
rule.mode = mode;
|
|
798
|
+
if (mode === 'advanced' && !rule.advancedJson) {
|
|
799
|
+
rule.advancedJson = JSON.stringify(this.buildGuidedRule(rule), null, 2);
|
|
800
|
+
}
|
|
801
|
+
this.emit();
|
|
802
|
+
}
|
|
803
|
+
updateRule(rule, key, value) {
|
|
804
|
+
rule[key] = value;
|
|
805
|
+
this.emit();
|
|
806
|
+
}
|
|
807
|
+
updateAdvanced(rule, value) {
|
|
808
|
+
rule.advancedJson = value;
|
|
809
|
+
rule.parseError = this.validateAdvancedJson(value);
|
|
810
|
+
this.emit();
|
|
811
|
+
}
|
|
812
|
+
summarizeRule(rule) {
|
|
813
|
+
if (rule.mode === 'advanced') {
|
|
814
|
+
return this.text('presentationRulesBuilder.summaryAdvanced', 'Regra avançada preservada como JSON.');
|
|
815
|
+
}
|
|
816
|
+
const variable = rule.sourcePath || 'value';
|
|
817
|
+
const operator = this.operatorOptions.find((option) => option.value === rule.operator)?.label ?? rule.operator;
|
|
818
|
+
const value = rule.operator === 'exists' ? '' : ` ${rule.compareValue || '?'}`;
|
|
819
|
+
const effects = this.buildSet(rule);
|
|
820
|
+
const effectCount = Object.keys(effects).length;
|
|
821
|
+
const effectText = effectCount
|
|
822
|
+
? this.text('presentationRulesBuilder.summaryEffects', '{{count}} efeito(s)').replace('{{count}}', String(effectCount))
|
|
823
|
+
: this.text('presentationRulesBuilder.summaryNoEffects', 'sem efeitos definidos');
|
|
824
|
+
return `${variable} ${operator}${value} -> ${effectText}`;
|
|
825
|
+
}
|
|
826
|
+
emit() {
|
|
827
|
+
this.onTouched();
|
|
828
|
+
this.onValidatorChange();
|
|
829
|
+
if (this.hasInvalidAdvancedJson()) {
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
this.onChange(this.rules.map((rule) => this.toRule(rule)).filter(Boolean));
|
|
833
|
+
}
|
|
834
|
+
hasInvalidAdvancedJson() {
|
|
835
|
+
return this.rules.some((rule) => rule.mode === 'advanced' && !!rule.parseError);
|
|
836
|
+
}
|
|
837
|
+
toRule(rule) {
|
|
838
|
+
if (rule.mode === 'advanced') {
|
|
839
|
+
const text = rule.advancedJson.trim();
|
|
840
|
+
if (!text || rule.parseError) {
|
|
841
|
+
return null;
|
|
842
|
+
}
|
|
843
|
+
const parsed = JSON.parse(text);
|
|
844
|
+
return this.isRecord(parsed) ? parsed : null;
|
|
845
|
+
}
|
|
846
|
+
const set = this.buildSet(rule);
|
|
847
|
+
if (!Object.keys(set).length) {
|
|
848
|
+
return null;
|
|
849
|
+
}
|
|
850
|
+
return this.buildGuidedRule(rule);
|
|
851
|
+
}
|
|
852
|
+
buildGuidedRule(rule) {
|
|
853
|
+
return {
|
|
854
|
+
when: this.buildCondition(rule),
|
|
855
|
+
set: this.buildSet(rule),
|
|
856
|
+
};
|
|
857
|
+
}
|
|
858
|
+
buildCondition(rule) {
|
|
859
|
+
const sourcePath = rule.sourcePath.trim() || 'value';
|
|
860
|
+
const variable = { var: sourcePath };
|
|
861
|
+
if (rule.operator === 'exists') {
|
|
862
|
+
return { '!=': [variable, null] };
|
|
863
|
+
}
|
|
864
|
+
const compareValue = this.parseScalar(rule.compareValue);
|
|
865
|
+
if (rule.operator === 'contains') {
|
|
866
|
+
return { in: [compareValue, variable] };
|
|
867
|
+
}
|
|
868
|
+
return {
|
|
869
|
+
[OPERATOR_TO_JSON_LOGIC[rule.operator]]: [variable, compareValue],
|
|
870
|
+
};
|
|
871
|
+
}
|
|
872
|
+
buildSet(rule) {
|
|
873
|
+
const set = {};
|
|
874
|
+
for (const key of GUIDED_PRESENTATION_RULE_EFFECT_KEYS) {
|
|
875
|
+
const value = String(rule[key] ?? '').trim();
|
|
876
|
+
if (value) {
|
|
877
|
+
set[key] = value;
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
return set;
|
|
881
|
+
}
|
|
882
|
+
hydrateRule(rule) {
|
|
883
|
+
const draft = this.createDraft();
|
|
884
|
+
const parsedCondition = this.parseCondition(rule.when);
|
|
885
|
+
const effect = this.resolveRuleEffect(rule);
|
|
886
|
+
if (!parsedCondition ||
|
|
887
|
+
!this.isSupportedGuidedEffectSet(effect) ||
|
|
888
|
+
!this.isRecord(rule.set)) {
|
|
889
|
+
return this.createAdvancedDraft(rule, draft);
|
|
890
|
+
}
|
|
891
|
+
return {
|
|
892
|
+
...draft,
|
|
893
|
+
sourcePath: parsedCondition.sourcePath,
|
|
894
|
+
operator: parsedCondition.operator,
|
|
895
|
+
compareValue: parsedCondition.compareValue,
|
|
896
|
+
tone: this.stringValue(effect?.['tone']),
|
|
897
|
+
appearance: this.stringValue(effect?.['appearance']),
|
|
898
|
+
icon: this.stringValue(effect?.['icon']),
|
|
899
|
+
label: this.stringValue(effect?.['label']),
|
|
900
|
+
badge: this.stringValue(effect?.['badge']),
|
|
901
|
+
tooltip: this.stringValue(effect?.['tooltip']),
|
|
902
|
+
advancedJson: JSON.stringify(rule, null, 2),
|
|
903
|
+
};
|
|
904
|
+
}
|
|
905
|
+
createAdvancedDraft(rule, draft = this.createDraft()) {
|
|
906
|
+
const advancedJson = JSON.stringify(rule, null, 2);
|
|
907
|
+
return {
|
|
908
|
+
...draft,
|
|
909
|
+
mode: 'advanced',
|
|
910
|
+
advancedJson,
|
|
911
|
+
parseError: this.validateAdvancedJson(advancedJson),
|
|
912
|
+
};
|
|
913
|
+
}
|
|
914
|
+
validateAdvancedJson(value) {
|
|
915
|
+
const text = value.trim();
|
|
916
|
+
if (!text) {
|
|
917
|
+
return '';
|
|
918
|
+
}
|
|
919
|
+
let parsed;
|
|
920
|
+
try {
|
|
921
|
+
parsed = JSON.parse(text);
|
|
922
|
+
}
|
|
923
|
+
catch {
|
|
924
|
+
return this.text('presentationRulesBuilder.invalidJson', 'JSON inválido. Corrija a regra antes de aplicar.');
|
|
925
|
+
}
|
|
926
|
+
if (!this.isRecord(parsed)) {
|
|
927
|
+
return this.text('presentationRulesBuilder.invalidRuleShape', 'A regra deve ser um objeto com when e set.');
|
|
928
|
+
}
|
|
929
|
+
const unsupportedTopLevelKeys = Object.keys(parsed).filter((key) => key !== 'when' && key !== 'set' && key !== 'effect');
|
|
930
|
+
if (unsupportedTopLevelKeys.length) {
|
|
931
|
+
return this.text('presentationRulesBuilder.invalidRuleShape', 'A regra deve ser um objeto com when e set.');
|
|
932
|
+
}
|
|
933
|
+
const effect = this.resolveRuleEffect(parsed);
|
|
934
|
+
if (!this.isRecord(effect)) {
|
|
935
|
+
return this.text('presentationRulesBuilder.invalidRuleShape', 'A regra deve ser um objeto com when e set.');
|
|
936
|
+
}
|
|
937
|
+
const unsupportedEffectKeys = Object.keys(effect).filter((key) => !PRESENTATION_RULE_EFFECT_KEY_SET.has(key));
|
|
938
|
+
if (unsupportedEffectKeys.length) {
|
|
939
|
+
return this.text('presentationRulesBuilder.unsupportedEffects', 'Use apenas efeitos semânticos de apresentação: presenter, tone, appearance, icon, label, badge, tooltip, valuePresentation e interactions.');
|
|
940
|
+
}
|
|
941
|
+
return '';
|
|
942
|
+
}
|
|
943
|
+
isSupportedEffectSet(set) {
|
|
944
|
+
return this.isRecord(set) && Object.keys(set).every((key) => PRESENTATION_RULE_EFFECT_KEY_SET.has(key));
|
|
945
|
+
}
|
|
946
|
+
isSupportedGuidedEffectSet(set) {
|
|
947
|
+
return this.isRecord(set) && Object.keys(set).every((key) => GUIDED_PRESENTATION_RULE_EFFECT_KEY_SET.has(key));
|
|
948
|
+
}
|
|
949
|
+
resolveRuleEffect(rule) {
|
|
950
|
+
return this.isRecord(rule.set)
|
|
951
|
+
? rule.set
|
|
952
|
+
: this.isRecord(rule.effect)
|
|
953
|
+
? rule.effect
|
|
954
|
+
: undefined;
|
|
955
|
+
}
|
|
956
|
+
parseCondition(when) {
|
|
957
|
+
if (!this.isRecord(when)) {
|
|
958
|
+
return null;
|
|
959
|
+
}
|
|
960
|
+
const [operator, operands] = Object.entries(when)[0] ?? [];
|
|
961
|
+
if (!operator || !Array.isArray(operands)) {
|
|
962
|
+
return null;
|
|
963
|
+
}
|
|
964
|
+
if (operator === '!=' && operands.length === 2 && operands[1] === null) {
|
|
965
|
+
const sourcePath = this.parseVar(operands[0]);
|
|
966
|
+
return sourcePath ? { sourcePath, operator: 'exists', compareValue: '' } : null;
|
|
967
|
+
}
|
|
968
|
+
if (operator === 'in' && operands.length === 2) {
|
|
969
|
+
const sourcePath = this.parseVar(operands[1]);
|
|
970
|
+
return sourcePath
|
|
971
|
+
? { sourcePath, operator: 'contains', compareValue: this.formatScalar(operands[0]) }
|
|
972
|
+
: null;
|
|
973
|
+
}
|
|
974
|
+
const entry = Object.entries(OPERATOR_TO_JSON_LOGIC).find(([, symbol]) => symbol === operator);
|
|
975
|
+
if (!entry || operands.length !== 2) {
|
|
976
|
+
return null;
|
|
977
|
+
}
|
|
978
|
+
const sourcePath = this.parseVar(operands[0]);
|
|
979
|
+
return sourcePath
|
|
980
|
+
? {
|
|
981
|
+
sourcePath,
|
|
982
|
+
operator: entry[0],
|
|
983
|
+
compareValue: this.formatScalar(operands[1]),
|
|
984
|
+
}
|
|
985
|
+
: null;
|
|
986
|
+
}
|
|
987
|
+
parseVar(value) {
|
|
988
|
+
return this.isRecord(value) && typeof value['var'] === 'string'
|
|
989
|
+
? value['var']
|
|
990
|
+
: null;
|
|
991
|
+
}
|
|
992
|
+
coerceRules(value) {
|
|
993
|
+
if (Array.isArray(value)) {
|
|
994
|
+
return value.filter((item) => this.isRecord(item));
|
|
995
|
+
}
|
|
996
|
+
if (typeof value === 'string' && value.trim()) {
|
|
997
|
+
try {
|
|
998
|
+
const parsed = JSON.parse(value);
|
|
999
|
+
return Array.isArray(parsed)
|
|
1000
|
+
? parsed.filter((item) => this.isRecord(item))
|
|
1001
|
+
: [];
|
|
1002
|
+
}
|
|
1003
|
+
catch {
|
|
1004
|
+
return [];
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
return [];
|
|
1008
|
+
}
|
|
1009
|
+
createDraft() {
|
|
1010
|
+
return {
|
|
1011
|
+
id: this.nextId++,
|
|
1012
|
+
mode: 'guided',
|
|
1013
|
+
sourcePath: 'value',
|
|
1014
|
+
operator: 'equals',
|
|
1015
|
+
compareValue: '',
|
|
1016
|
+
tone: '',
|
|
1017
|
+
appearance: '',
|
|
1018
|
+
icon: '',
|
|
1019
|
+
label: '',
|
|
1020
|
+
badge: '',
|
|
1021
|
+
tooltip: '',
|
|
1022
|
+
advancedJson: '',
|
|
1023
|
+
parseError: '',
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
1026
|
+
parseScalar(value) {
|
|
1027
|
+
const trimmed = value.trim();
|
|
1028
|
+
if (trimmed === 'true')
|
|
1029
|
+
return true;
|
|
1030
|
+
if (trimmed === 'false')
|
|
1031
|
+
return false;
|
|
1032
|
+
if (trimmed === 'null')
|
|
1033
|
+
return null;
|
|
1034
|
+
if (trimmed !== '' && !Number.isNaN(Number(trimmed))) {
|
|
1035
|
+
return Number(trimmed);
|
|
1036
|
+
}
|
|
1037
|
+
return trimmed;
|
|
1038
|
+
}
|
|
1039
|
+
formatScalar(value) {
|
|
1040
|
+
if (value === null)
|
|
1041
|
+
return 'null';
|
|
1042
|
+
if (typeof value === 'string')
|
|
1043
|
+
return value;
|
|
1044
|
+
return JSON.stringify(value);
|
|
1045
|
+
}
|
|
1046
|
+
stringValue(value) {
|
|
1047
|
+
return typeof value === 'string' ? value : '';
|
|
1048
|
+
}
|
|
1049
|
+
isRecord(value) {
|
|
1050
|
+
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
1051
|
+
}
|
|
1052
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PresentationRulesBuilderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1053
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PresentationRulesBuilderComponent, isStandalone: true, selector: "praxis-presentation-rules-builder", providers: [
|
|
1054
|
+
{
|
|
1055
|
+
provide: NG_VALUE_ACCESSOR,
|
|
1056
|
+
useExisting: forwardRef(() => PresentationRulesBuilderComponent),
|
|
1057
|
+
multi: true,
|
|
1058
|
+
},
|
|
1059
|
+
{
|
|
1060
|
+
provide: NG_VALIDATORS,
|
|
1061
|
+
useExisting: forwardRef(() => PresentationRulesBuilderComponent),
|
|
1062
|
+
multi: true,
|
|
1063
|
+
},
|
|
1064
|
+
], ngImport: i0, template: `
|
|
1065
|
+
<section class="pme-rules" [class.is-disabled]="disabled">
|
|
1066
|
+
<div class="pme-rules__header">
|
|
1067
|
+
<div>
|
|
1068
|
+
<h3>{{ text('presentationRulesBuilder.title', 'Regras de apresentação') }}</h3>
|
|
1069
|
+
<p>{{ text('presentationRulesBuilder.description', 'Altere tom, ícone e rótulos sem modificar o valor enviado.') }}</p>
|
|
1070
|
+
</div>
|
|
1071
|
+
<button
|
|
1072
|
+
type="button"
|
|
1073
|
+
mat-stroked-button
|
|
1074
|
+
color="primary"
|
|
1075
|
+
(click)="addRule()"
|
|
1076
|
+
[disabled]="disabled"
|
|
1077
|
+
>
|
|
1078
|
+
<mat-icon [praxisIcon]="'add'"></mat-icon>
|
|
1079
|
+
{{ text('presentationRulesBuilder.add', 'Adicionar regra') }}
|
|
1080
|
+
</button>
|
|
1081
|
+
</div>
|
|
1082
|
+
|
|
1083
|
+
@if (!rules.length) {
|
|
1084
|
+
<div class="pme-rules__empty">
|
|
1085
|
+
{{ text('presentationRulesBuilder.empty', 'Nenhuma regra configurada. Adicione regras para estados como atraso, bloqueio ou aprovação.') }}
|
|
1086
|
+
</div>
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
<div class="pme-rules__list">
|
|
1090
|
+
@for (rule of rules; track rule.id; let index = $index) {
|
|
1091
|
+
<article class="pme-rule">
|
|
1092
|
+
<div class="pme-rule__toolbar">
|
|
1093
|
+
<strong>{{ text('presentationRulesBuilder.rule', 'Regra') }} {{ index + 1 }}</strong>
|
|
1094
|
+
<span class="pme-rule__summary">{{ summarizeRule(rule) }}</span>
|
|
1095
|
+
<span class="pme-rule__spacer"></span>
|
|
1096
|
+
<button
|
|
1097
|
+
type="button"
|
|
1098
|
+
mat-icon-button
|
|
1099
|
+
[matTooltip]="text('presentationRulesBuilder.moveUp', 'Subir')"
|
|
1100
|
+
[attr.aria-label]="text('presentationRulesBuilder.moveUp', 'Subir')"
|
|
1101
|
+
(click)="moveRule(index, -1)"
|
|
1102
|
+
[disabled]="disabled || index === 0"
|
|
1103
|
+
>
|
|
1104
|
+
<mat-icon [praxisIcon]="'arrow_upward'"></mat-icon>
|
|
1105
|
+
</button>
|
|
1106
|
+
<button
|
|
1107
|
+
type="button"
|
|
1108
|
+
mat-icon-button
|
|
1109
|
+
[matTooltip]="text('presentationRulesBuilder.moveDown', 'Descer')"
|
|
1110
|
+
[attr.aria-label]="text('presentationRulesBuilder.moveDown', 'Descer')"
|
|
1111
|
+
(click)="moveRule(index, 1)"
|
|
1112
|
+
[disabled]="disabled || index === rules.length - 1"
|
|
1113
|
+
>
|
|
1114
|
+
<mat-icon [praxisIcon]="'arrow_downward'"></mat-icon>
|
|
1115
|
+
</button>
|
|
1116
|
+
<button
|
|
1117
|
+
type="button"
|
|
1118
|
+
mat-icon-button
|
|
1119
|
+
color="warn"
|
|
1120
|
+
[matTooltip]="text('presentationRulesBuilder.remove', 'Remover')"
|
|
1121
|
+
[attr.aria-label]="text('presentationRulesBuilder.remove', 'Remover')"
|
|
1122
|
+
(click)="removeRule(index)"
|
|
1123
|
+
[disabled]="disabled"
|
|
1124
|
+
>
|
|
1125
|
+
<mat-icon [praxisIcon]="'delete'"></mat-icon>
|
|
1126
|
+
</button>
|
|
1127
|
+
</div>
|
|
1128
|
+
|
|
1129
|
+
<div class="pme-rule__mode" role="group" [attr.aria-label]="text('presentationRulesBuilder.mode', 'Modo da regra')">
|
|
1130
|
+
<button
|
|
1131
|
+
type="button"
|
|
1132
|
+
mat-button
|
|
1133
|
+
[class.is-active]="rule.mode === 'guided'"
|
|
1134
|
+
[attr.aria-pressed]="rule.mode === 'guided'"
|
|
1135
|
+
(click)="setMode(rule, 'guided')"
|
|
1136
|
+
[disabled]="disabled"
|
|
1137
|
+
>
|
|
1138
|
+
{{ text('presentationRulesBuilder.guided', 'Guiado') }}
|
|
1139
|
+
</button>
|
|
1140
|
+
<button
|
|
1141
|
+
type="button"
|
|
1142
|
+
mat-button
|
|
1143
|
+
[class.is-active]="rule.mode === 'advanced'"
|
|
1144
|
+
[attr.aria-pressed]="rule.mode === 'advanced'"
|
|
1145
|
+
(click)="setMode(rule, 'advanced')"
|
|
1146
|
+
[disabled]="disabled"
|
|
1147
|
+
>
|
|
1148
|
+
{{ text('presentationRulesBuilder.advanced', 'JSON avançado') }}
|
|
1149
|
+
</button>
|
|
1150
|
+
</div>
|
|
1151
|
+
|
|
1152
|
+
@if (rule.mode === 'guided') {
|
|
1153
|
+
<div class="pme-rule__grid">
|
|
1154
|
+
<mat-form-field appearance="outline">
|
|
1155
|
+
<mat-label>{{ text('presentationRulesBuilder.sourcePath', 'Variável') }}</mat-label>
|
|
1156
|
+
<input
|
|
1157
|
+
matInput
|
|
1158
|
+
[ngModel]="rule.sourcePath"
|
|
1159
|
+
(ngModelChange)="updateRule(rule, 'sourcePath', $event)"
|
|
1160
|
+
[disabled]="disabled"
|
|
1161
|
+
placeholder="value"
|
|
1162
|
+
/>
|
|
1163
|
+
</mat-form-field>
|
|
1164
|
+
|
|
1165
|
+
<mat-form-field appearance="outline">
|
|
1166
|
+
<mat-label>{{ text('presentationRulesBuilder.operator', 'Condição') }}</mat-label>
|
|
1167
|
+
<mat-select
|
|
1168
|
+
[ngModel]="rule.operator"
|
|
1169
|
+
(ngModelChange)="updateRule(rule, 'operator', $event)"
|
|
1170
|
+
[disabled]="disabled"
|
|
1171
|
+
>
|
|
1172
|
+
@for (option of operatorOptions; track option.value) {
|
|
1173
|
+
<mat-option [value]="option.value">{{ option.label }}</mat-option>
|
|
1174
|
+
}
|
|
1175
|
+
</mat-select>
|
|
1176
|
+
</mat-form-field>
|
|
1177
|
+
|
|
1178
|
+
@if (rule.operator !== 'exists') {
|
|
1179
|
+
<mat-form-field appearance="outline">
|
|
1180
|
+
<mat-label>{{ text('presentationRulesBuilder.compareValue', 'Valor') }}</mat-label>
|
|
1181
|
+
<input
|
|
1182
|
+
matInput
|
|
1183
|
+
[ngModel]="rule.compareValue"
|
|
1184
|
+
(ngModelChange)="updateRule(rule, 'compareValue', $event)"
|
|
1185
|
+
[disabled]="disabled"
|
|
1186
|
+
placeholder="BLOQUEADO"
|
|
1187
|
+
/>
|
|
1188
|
+
</mat-form-field>
|
|
1189
|
+
}
|
|
1190
|
+
</div>
|
|
1191
|
+
|
|
1192
|
+
<div class="pme-rule__grid pme-rule__grid--effects">
|
|
1193
|
+
<mat-form-field appearance="outline">
|
|
1194
|
+
<mat-label>{{ text('presentationRulesBuilder.tone', 'Tom') }}</mat-label>
|
|
1195
|
+
<mat-select
|
|
1196
|
+
[ngModel]="rule.tone"
|
|
1197
|
+
(ngModelChange)="updateRule(rule, 'tone', $event)"
|
|
1198
|
+
[disabled]="disabled"
|
|
1199
|
+
>
|
|
1200
|
+
<mat-option value="">{{ text('presentationRulesBuilder.keep', 'Manter') }}</mat-option>
|
|
1201
|
+
@for (option of toneOptions; track option.value) {
|
|
1202
|
+
<mat-option [value]="option.value">{{ option.label }}</mat-option>
|
|
1203
|
+
}
|
|
1204
|
+
</mat-select>
|
|
1205
|
+
</mat-form-field>
|
|
1206
|
+
|
|
1207
|
+
<mat-form-field appearance="outline">
|
|
1208
|
+
<mat-label>{{ text('presentationRulesBuilder.appearance', 'Ênfase') }}</mat-label>
|
|
1209
|
+
<mat-select
|
|
1210
|
+
[ngModel]="rule.appearance"
|
|
1211
|
+
(ngModelChange)="updateRule(rule, 'appearance', $event)"
|
|
1212
|
+
[disabled]="disabled"
|
|
1213
|
+
>
|
|
1214
|
+
<mat-option value="">{{ text('presentationRulesBuilder.keep', 'Manter') }}</mat-option>
|
|
1215
|
+
@for (option of appearanceOptions; track option.value) {
|
|
1216
|
+
<mat-option [value]="option.value">{{ option.label }}</mat-option>
|
|
1217
|
+
}
|
|
1218
|
+
</mat-select>
|
|
1219
|
+
</mat-form-field>
|
|
1220
|
+
|
|
1221
|
+
<mat-form-field appearance="outline">
|
|
1222
|
+
<mat-label>{{ text('presentationRulesBuilder.icon', 'Ícone') }}</mat-label>
|
|
1223
|
+
<input
|
|
1224
|
+
matInput
|
|
1225
|
+
[ngModel]="rule.icon"
|
|
1226
|
+
(ngModelChange)="updateRule(rule, 'icon', $event)"
|
|
1227
|
+
[disabled]="disabled"
|
|
1228
|
+
placeholder="lock"
|
|
1229
|
+
/>
|
|
1230
|
+
</mat-form-field>
|
|
1231
|
+
|
|
1232
|
+
<mat-form-field appearance="outline">
|
|
1233
|
+
<mat-label>{{ text('presentationRulesBuilder.label', 'Rótulo') }}</mat-label>
|
|
1234
|
+
<input
|
|
1235
|
+
matInput
|
|
1236
|
+
[ngModel]="rule.label"
|
|
1237
|
+
(ngModelChange)="updateRule(rule, 'label', $event)"
|
|
1238
|
+
[disabled]="disabled"
|
|
1239
|
+
placeholder="Bloqueado"
|
|
1240
|
+
/>
|
|
1241
|
+
</mat-form-field>
|
|
1242
|
+
|
|
1243
|
+
<mat-form-field appearance="outline">
|
|
1244
|
+
<mat-label>{{ text('presentationRulesBuilder.badge', 'Badge') }}</mat-label>
|
|
1245
|
+
<input
|
|
1246
|
+
matInput
|
|
1247
|
+
[ngModel]="rule.badge"
|
|
1248
|
+
(ngModelChange)="updateRule(rule, 'badge', $event)"
|
|
1249
|
+
[disabled]="disabled"
|
|
1250
|
+
placeholder="SLA"
|
|
1251
|
+
/>
|
|
1252
|
+
</mat-form-field>
|
|
1253
|
+
|
|
1254
|
+
<mat-form-field appearance="outline">
|
|
1255
|
+
<mat-label>{{ text('presentationRulesBuilder.tooltip', 'Tooltip') }}</mat-label>
|
|
1256
|
+
<input
|
|
1257
|
+
matInput
|
|
1258
|
+
[ngModel]="rule.tooltip"
|
|
1259
|
+
(ngModelChange)="updateRule(rule, 'tooltip', $event)"
|
|
1260
|
+
[disabled]="disabled"
|
|
1261
|
+
placeholder="Aguardando aprovação fiscal"
|
|
1262
|
+
/>
|
|
1263
|
+
</mat-form-field>
|
|
1264
|
+
</div>
|
|
1265
|
+
} @else {
|
|
1266
|
+
<mat-form-field appearance="outline" class="pme-rule__advanced">
|
|
1267
|
+
<mat-label>{{ text('presentationRulesBuilder.advancedRule', 'Regra JSON') }}</mat-label>
|
|
1268
|
+
<textarea
|
|
1269
|
+
matInput
|
|
1270
|
+
rows="8"
|
|
1271
|
+
[ngModel]="rule.advancedJson"
|
|
1272
|
+
(ngModelChange)="updateAdvanced(rule, $event)"
|
|
1273
|
+
[disabled]="disabled"
|
|
1274
|
+
></textarea>
|
|
1275
|
+
@if (rule.parseError) {
|
|
1276
|
+
<mat-error>{{ rule.parseError }}</mat-error>
|
|
1277
|
+
}
|
|
1278
|
+
</mat-form-field>
|
|
1279
|
+
}
|
|
1280
|
+
</article>
|
|
1281
|
+
}
|
|
1282
|
+
</div>
|
|
1283
|
+
</section>
|
|
1284
|
+
`, isInline: true, styles: [":host{display:block;width:100%}.pme-rules{display:grid;gap:12px;width:100%}.pme-rules.is-disabled{opacity:.72}.pme-rules__header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}.pme-rules__header h3{margin:0;color:var(--md-sys-color-on-surface);font-size:.95rem;font-weight:650;line-height:1.3}.pme-rules__header p{margin:3px 0 0;color:var(--md-sys-color-on-surface-variant);font-size:.82rem;line-height:1.35}.pme-rules__empty{border:1px dashed var(--md-sys-color-outline-variant);border-radius:8px;color:var(--md-sys-color-on-surface-variant);padding:12px;font-size:.86rem;line-height:1.4}.pme-rules__list{display:grid;gap:10px}.pme-rule{border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;background:var(--md-sys-color-surface-container-low);padding:10px}.pme-rule__toolbar{display:flex;align-items:center;gap:6px;min-height:32px}.pme-rule__toolbar strong{color:var(--md-sys-color-on-surface);font-size:.88rem;font-weight:650}.pme-rule__summary{color:var(--md-sys-color-on-surface-variant);font-size:.8rem;line-height:1.3}.pme-rule__spacer{flex:1 1 auto}.pme-rule__mode{display:inline-flex;gap:2px;margin:8px 0 10px;padding:2px;border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;background:var(--md-sys-color-surface-container)}.pme-rule__mode button{min-height:30px;line-height:30px}.pme-rule__mode button.is-active{background:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container)}.pme-rule__grid{display:grid;grid-template-columns:repeat(3,minmax(160px,1fr));gap:10px}.pme-rule__grid--effects{grid-template-columns:repeat(3,minmax(150px,1fr))}.pme-rule__advanced{width:100%}@media(max-width:720px){.pme-rules__header{display:grid}.pme-rules__header button{justify-self:start}.pme-rule__toolbar{flex-wrap:wrap}.pme-rule__summary{flex:1 1 100%}.pme-rule__grid,.pme-rule__grid--effects{grid-template-columns:1fr}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i2$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }] });
|
|
1285
|
+
}
|
|
1286
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PresentationRulesBuilderComponent, decorators: [{
|
|
1287
|
+
type: Component,
|
|
1288
|
+
args: [{ selector: 'praxis-presentation-rules-builder', standalone: true, imports: [
|
|
1289
|
+
CommonModule,
|
|
1290
|
+
FormsModule,
|
|
1291
|
+
MatButtonModule,
|
|
1292
|
+
MatFormFieldModule,
|
|
1293
|
+
MatIconModule,
|
|
1294
|
+
MatInputModule,
|
|
1295
|
+
MatSelectModule,
|
|
1296
|
+
MatTooltipModule,
|
|
1297
|
+
PraxisIconDirective,
|
|
1298
|
+
], providers: [
|
|
1299
|
+
{
|
|
1300
|
+
provide: NG_VALUE_ACCESSOR,
|
|
1301
|
+
useExisting: forwardRef(() => PresentationRulesBuilderComponent),
|
|
1302
|
+
multi: true,
|
|
1303
|
+
},
|
|
1304
|
+
{
|
|
1305
|
+
provide: NG_VALIDATORS,
|
|
1306
|
+
useExisting: forwardRef(() => PresentationRulesBuilderComponent),
|
|
1307
|
+
multi: true,
|
|
1308
|
+
},
|
|
1309
|
+
], template: `
|
|
1310
|
+
<section class="pme-rules" [class.is-disabled]="disabled">
|
|
1311
|
+
<div class="pme-rules__header">
|
|
1312
|
+
<div>
|
|
1313
|
+
<h3>{{ text('presentationRulesBuilder.title', 'Regras de apresentação') }}</h3>
|
|
1314
|
+
<p>{{ text('presentationRulesBuilder.description', 'Altere tom, ícone e rótulos sem modificar o valor enviado.') }}</p>
|
|
1315
|
+
</div>
|
|
1316
|
+
<button
|
|
1317
|
+
type="button"
|
|
1318
|
+
mat-stroked-button
|
|
1319
|
+
color="primary"
|
|
1320
|
+
(click)="addRule()"
|
|
1321
|
+
[disabled]="disabled"
|
|
1322
|
+
>
|
|
1323
|
+
<mat-icon [praxisIcon]="'add'"></mat-icon>
|
|
1324
|
+
{{ text('presentationRulesBuilder.add', 'Adicionar regra') }}
|
|
1325
|
+
</button>
|
|
1326
|
+
</div>
|
|
1327
|
+
|
|
1328
|
+
@if (!rules.length) {
|
|
1329
|
+
<div class="pme-rules__empty">
|
|
1330
|
+
{{ text('presentationRulesBuilder.empty', 'Nenhuma regra configurada. Adicione regras para estados como atraso, bloqueio ou aprovação.') }}
|
|
1331
|
+
</div>
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
<div class="pme-rules__list">
|
|
1335
|
+
@for (rule of rules; track rule.id; let index = $index) {
|
|
1336
|
+
<article class="pme-rule">
|
|
1337
|
+
<div class="pme-rule__toolbar">
|
|
1338
|
+
<strong>{{ text('presentationRulesBuilder.rule', 'Regra') }} {{ index + 1 }}</strong>
|
|
1339
|
+
<span class="pme-rule__summary">{{ summarizeRule(rule) }}</span>
|
|
1340
|
+
<span class="pme-rule__spacer"></span>
|
|
1341
|
+
<button
|
|
1342
|
+
type="button"
|
|
1343
|
+
mat-icon-button
|
|
1344
|
+
[matTooltip]="text('presentationRulesBuilder.moveUp', 'Subir')"
|
|
1345
|
+
[attr.aria-label]="text('presentationRulesBuilder.moveUp', 'Subir')"
|
|
1346
|
+
(click)="moveRule(index, -1)"
|
|
1347
|
+
[disabled]="disabled || index === 0"
|
|
1348
|
+
>
|
|
1349
|
+
<mat-icon [praxisIcon]="'arrow_upward'"></mat-icon>
|
|
1350
|
+
</button>
|
|
1351
|
+
<button
|
|
1352
|
+
type="button"
|
|
1353
|
+
mat-icon-button
|
|
1354
|
+
[matTooltip]="text('presentationRulesBuilder.moveDown', 'Descer')"
|
|
1355
|
+
[attr.aria-label]="text('presentationRulesBuilder.moveDown', 'Descer')"
|
|
1356
|
+
(click)="moveRule(index, 1)"
|
|
1357
|
+
[disabled]="disabled || index === rules.length - 1"
|
|
1358
|
+
>
|
|
1359
|
+
<mat-icon [praxisIcon]="'arrow_downward'"></mat-icon>
|
|
1360
|
+
</button>
|
|
1361
|
+
<button
|
|
1362
|
+
type="button"
|
|
1363
|
+
mat-icon-button
|
|
1364
|
+
color="warn"
|
|
1365
|
+
[matTooltip]="text('presentationRulesBuilder.remove', 'Remover')"
|
|
1366
|
+
[attr.aria-label]="text('presentationRulesBuilder.remove', 'Remover')"
|
|
1367
|
+
(click)="removeRule(index)"
|
|
1368
|
+
[disabled]="disabled"
|
|
1369
|
+
>
|
|
1370
|
+
<mat-icon [praxisIcon]="'delete'"></mat-icon>
|
|
1371
|
+
</button>
|
|
1372
|
+
</div>
|
|
1373
|
+
|
|
1374
|
+
<div class="pme-rule__mode" role="group" [attr.aria-label]="text('presentationRulesBuilder.mode', 'Modo da regra')">
|
|
1375
|
+
<button
|
|
1376
|
+
type="button"
|
|
1377
|
+
mat-button
|
|
1378
|
+
[class.is-active]="rule.mode === 'guided'"
|
|
1379
|
+
[attr.aria-pressed]="rule.mode === 'guided'"
|
|
1380
|
+
(click)="setMode(rule, 'guided')"
|
|
1381
|
+
[disabled]="disabled"
|
|
1382
|
+
>
|
|
1383
|
+
{{ text('presentationRulesBuilder.guided', 'Guiado') }}
|
|
1384
|
+
</button>
|
|
1385
|
+
<button
|
|
1386
|
+
type="button"
|
|
1387
|
+
mat-button
|
|
1388
|
+
[class.is-active]="rule.mode === 'advanced'"
|
|
1389
|
+
[attr.aria-pressed]="rule.mode === 'advanced'"
|
|
1390
|
+
(click)="setMode(rule, 'advanced')"
|
|
1391
|
+
[disabled]="disabled"
|
|
1392
|
+
>
|
|
1393
|
+
{{ text('presentationRulesBuilder.advanced', 'JSON avançado') }}
|
|
1394
|
+
</button>
|
|
1395
|
+
</div>
|
|
1396
|
+
|
|
1397
|
+
@if (rule.mode === 'guided') {
|
|
1398
|
+
<div class="pme-rule__grid">
|
|
1399
|
+
<mat-form-field appearance="outline">
|
|
1400
|
+
<mat-label>{{ text('presentationRulesBuilder.sourcePath', 'Variável') }}</mat-label>
|
|
1401
|
+
<input
|
|
1402
|
+
matInput
|
|
1403
|
+
[ngModel]="rule.sourcePath"
|
|
1404
|
+
(ngModelChange)="updateRule(rule, 'sourcePath', $event)"
|
|
1405
|
+
[disabled]="disabled"
|
|
1406
|
+
placeholder="value"
|
|
1407
|
+
/>
|
|
1408
|
+
</mat-form-field>
|
|
1409
|
+
|
|
1410
|
+
<mat-form-field appearance="outline">
|
|
1411
|
+
<mat-label>{{ text('presentationRulesBuilder.operator', 'Condição') }}</mat-label>
|
|
1412
|
+
<mat-select
|
|
1413
|
+
[ngModel]="rule.operator"
|
|
1414
|
+
(ngModelChange)="updateRule(rule, 'operator', $event)"
|
|
1415
|
+
[disabled]="disabled"
|
|
1416
|
+
>
|
|
1417
|
+
@for (option of operatorOptions; track option.value) {
|
|
1418
|
+
<mat-option [value]="option.value">{{ option.label }}</mat-option>
|
|
1419
|
+
}
|
|
1420
|
+
</mat-select>
|
|
1421
|
+
</mat-form-field>
|
|
1422
|
+
|
|
1423
|
+
@if (rule.operator !== 'exists') {
|
|
1424
|
+
<mat-form-field appearance="outline">
|
|
1425
|
+
<mat-label>{{ text('presentationRulesBuilder.compareValue', 'Valor') }}</mat-label>
|
|
1426
|
+
<input
|
|
1427
|
+
matInput
|
|
1428
|
+
[ngModel]="rule.compareValue"
|
|
1429
|
+
(ngModelChange)="updateRule(rule, 'compareValue', $event)"
|
|
1430
|
+
[disabled]="disabled"
|
|
1431
|
+
placeholder="BLOQUEADO"
|
|
1432
|
+
/>
|
|
1433
|
+
</mat-form-field>
|
|
1434
|
+
}
|
|
1435
|
+
</div>
|
|
1436
|
+
|
|
1437
|
+
<div class="pme-rule__grid pme-rule__grid--effects">
|
|
1438
|
+
<mat-form-field appearance="outline">
|
|
1439
|
+
<mat-label>{{ text('presentationRulesBuilder.tone', 'Tom') }}</mat-label>
|
|
1440
|
+
<mat-select
|
|
1441
|
+
[ngModel]="rule.tone"
|
|
1442
|
+
(ngModelChange)="updateRule(rule, 'tone', $event)"
|
|
1443
|
+
[disabled]="disabled"
|
|
1444
|
+
>
|
|
1445
|
+
<mat-option value="">{{ text('presentationRulesBuilder.keep', 'Manter') }}</mat-option>
|
|
1446
|
+
@for (option of toneOptions; track option.value) {
|
|
1447
|
+
<mat-option [value]="option.value">{{ option.label }}</mat-option>
|
|
1448
|
+
}
|
|
1449
|
+
</mat-select>
|
|
1450
|
+
</mat-form-field>
|
|
1451
|
+
|
|
1452
|
+
<mat-form-field appearance="outline">
|
|
1453
|
+
<mat-label>{{ text('presentationRulesBuilder.appearance', 'Ênfase') }}</mat-label>
|
|
1454
|
+
<mat-select
|
|
1455
|
+
[ngModel]="rule.appearance"
|
|
1456
|
+
(ngModelChange)="updateRule(rule, 'appearance', $event)"
|
|
1457
|
+
[disabled]="disabled"
|
|
1458
|
+
>
|
|
1459
|
+
<mat-option value="">{{ text('presentationRulesBuilder.keep', 'Manter') }}</mat-option>
|
|
1460
|
+
@for (option of appearanceOptions; track option.value) {
|
|
1461
|
+
<mat-option [value]="option.value">{{ option.label }}</mat-option>
|
|
1462
|
+
}
|
|
1463
|
+
</mat-select>
|
|
1464
|
+
</mat-form-field>
|
|
1465
|
+
|
|
1466
|
+
<mat-form-field appearance="outline">
|
|
1467
|
+
<mat-label>{{ text('presentationRulesBuilder.icon', 'Ícone') }}</mat-label>
|
|
1468
|
+
<input
|
|
1469
|
+
matInput
|
|
1470
|
+
[ngModel]="rule.icon"
|
|
1471
|
+
(ngModelChange)="updateRule(rule, 'icon', $event)"
|
|
1472
|
+
[disabled]="disabled"
|
|
1473
|
+
placeholder="lock"
|
|
1474
|
+
/>
|
|
1475
|
+
</mat-form-field>
|
|
1476
|
+
|
|
1477
|
+
<mat-form-field appearance="outline">
|
|
1478
|
+
<mat-label>{{ text('presentationRulesBuilder.label', 'Rótulo') }}</mat-label>
|
|
1479
|
+
<input
|
|
1480
|
+
matInput
|
|
1481
|
+
[ngModel]="rule.label"
|
|
1482
|
+
(ngModelChange)="updateRule(rule, 'label', $event)"
|
|
1483
|
+
[disabled]="disabled"
|
|
1484
|
+
placeholder="Bloqueado"
|
|
1485
|
+
/>
|
|
1486
|
+
</mat-form-field>
|
|
1487
|
+
|
|
1488
|
+
<mat-form-field appearance="outline">
|
|
1489
|
+
<mat-label>{{ text('presentationRulesBuilder.badge', 'Badge') }}</mat-label>
|
|
1490
|
+
<input
|
|
1491
|
+
matInput
|
|
1492
|
+
[ngModel]="rule.badge"
|
|
1493
|
+
(ngModelChange)="updateRule(rule, 'badge', $event)"
|
|
1494
|
+
[disabled]="disabled"
|
|
1495
|
+
placeholder="SLA"
|
|
1496
|
+
/>
|
|
1497
|
+
</mat-form-field>
|
|
1498
|
+
|
|
1499
|
+
<mat-form-field appearance="outline">
|
|
1500
|
+
<mat-label>{{ text('presentationRulesBuilder.tooltip', 'Tooltip') }}</mat-label>
|
|
1501
|
+
<input
|
|
1502
|
+
matInput
|
|
1503
|
+
[ngModel]="rule.tooltip"
|
|
1504
|
+
(ngModelChange)="updateRule(rule, 'tooltip', $event)"
|
|
1505
|
+
[disabled]="disabled"
|
|
1506
|
+
placeholder="Aguardando aprovação fiscal"
|
|
1507
|
+
/>
|
|
1508
|
+
</mat-form-field>
|
|
1509
|
+
</div>
|
|
1510
|
+
} @else {
|
|
1511
|
+
<mat-form-field appearance="outline" class="pme-rule__advanced">
|
|
1512
|
+
<mat-label>{{ text('presentationRulesBuilder.advancedRule', 'Regra JSON') }}</mat-label>
|
|
1513
|
+
<textarea
|
|
1514
|
+
matInput
|
|
1515
|
+
rows="8"
|
|
1516
|
+
[ngModel]="rule.advancedJson"
|
|
1517
|
+
(ngModelChange)="updateAdvanced(rule, $event)"
|
|
1518
|
+
[disabled]="disabled"
|
|
1519
|
+
></textarea>
|
|
1520
|
+
@if (rule.parseError) {
|
|
1521
|
+
<mat-error>{{ rule.parseError }}</mat-error>
|
|
1522
|
+
}
|
|
1523
|
+
</mat-form-field>
|
|
1524
|
+
}
|
|
1525
|
+
</article>
|
|
1526
|
+
}
|
|
1527
|
+
</div>
|
|
1528
|
+
</section>
|
|
1529
|
+
`, styles: [":host{display:block;width:100%}.pme-rules{display:grid;gap:12px;width:100%}.pme-rules.is-disabled{opacity:.72}.pme-rules__header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}.pme-rules__header h3{margin:0;color:var(--md-sys-color-on-surface);font-size:.95rem;font-weight:650;line-height:1.3}.pme-rules__header p{margin:3px 0 0;color:var(--md-sys-color-on-surface-variant);font-size:.82rem;line-height:1.35}.pme-rules__empty{border:1px dashed var(--md-sys-color-outline-variant);border-radius:8px;color:var(--md-sys-color-on-surface-variant);padding:12px;font-size:.86rem;line-height:1.4}.pme-rules__list{display:grid;gap:10px}.pme-rule{border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;background:var(--md-sys-color-surface-container-low);padding:10px}.pme-rule__toolbar{display:flex;align-items:center;gap:6px;min-height:32px}.pme-rule__toolbar strong{color:var(--md-sys-color-on-surface);font-size:.88rem;font-weight:650}.pme-rule__summary{color:var(--md-sys-color-on-surface-variant);font-size:.8rem;line-height:1.3}.pme-rule__spacer{flex:1 1 auto}.pme-rule__mode{display:inline-flex;gap:2px;margin:8px 0 10px;padding:2px;border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;background:var(--md-sys-color-surface-container)}.pme-rule__mode button{min-height:30px;line-height:30px}.pme-rule__mode button.is-active{background:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container)}.pme-rule__grid{display:grid;grid-template-columns:repeat(3,minmax(160px,1fr));gap:10px}.pme-rule__grid--effects{grid-template-columns:repeat(3,minmax(150px,1fr))}.pme-rule__advanced{width:100%}@media(max-width:720px){.pme-rules__header{display:grid}.pme-rules__header button{justify-self:start}.pme-rule__toolbar{flex-wrap:wrap}.pme-rule__summary{flex:1 1 100%}.pme-rule__grid,.pme-rule__grid--effects{grid-template-columns:1fr}}\n"] }]
|
|
1530
|
+
}] });
|
|
1531
|
+
|
|
1532
|
+
const PRESENTER_ICONS = {
|
|
1533
|
+
text: 'text_fields',
|
|
1534
|
+
status: 'radio_button_checked',
|
|
1535
|
+
chip: 'label',
|
|
1536
|
+
badge: 'new_releases',
|
|
1537
|
+
iconValue: 'add_reaction',
|
|
1538
|
+
progress: 'progress_activity',
|
|
1539
|
+
rating: 'star',
|
|
1540
|
+
};
|
|
1541
|
+
const APPEARANCE_ICONS = {
|
|
1542
|
+
plain: 'horizontal_rule',
|
|
1543
|
+
soft: 'tonality',
|
|
1544
|
+
outlined: 'crop_square',
|
|
1545
|
+
filled: 'stop',
|
|
1546
|
+
};
|
|
1547
|
+
class PresentationChoiceControlComponent {
|
|
1548
|
+
i18n = inject(PraxisI18nService);
|
|
1549
|
+
elementRef = inject(ElementRef);
|
|
1550
|
+
field;
|
|
1551
|
+
value = '';
|
|
1552
|
+
disabled = false;
|
|
1553
|
+
onChange = () => { };
|
|
1554
|
+
onTouched = () => { };
|
|
1555
|
+
get kind() {
|
|
1556
|
+
const name = this.field?.name ?? '';
|
|
1557
|
+
if (name.endsWith('.tone'))
|
|
1558
|
+
return 'tone';
|
|
1559
|
+
if (name.endsWith('.appearance'))
|
|
1560
|
+
return 'appearance';
|
|
1561
|
+
return 'presenter';
|
|
1562
|
+
}
|
|
1563
|
+
get options() {
|
|
1564
|
+
return Array.isArray(this.field?.options)
|
|
1565
|
+
? this.field?.options
|
|
1566
|
+
: [];
|
|
1567
|
+
}
|
|
1568
|
+
writeValue(value) {
|
|
1569
|
+
this.value = typeof value === 'string' ? value : '';
|
|
1570
|
+
}
|
|
1571
|
+
registerOnChange(fn) {
|
|
1572
|
+
this.onChange = fn;
|
|
1573
|
+
}
|
|
1574
|
+
registerOnTouched(fn) {
|
|
1575
|
+
this.onTouched = fn;
|
|
1576
|
+
}
|
|
1577
|
+
setDisabledState(isDisabled) {
|
|
1578
|
+
this.disabled = isDisabled;
|
|
1579
|
+
}
|
|
1580
|
+
select(nextValue) {
|
|
1581
|
+
if (this.disabled) {
|
|
1582
|
+
return;
|
|
1583
|
+
}
|
|
1584
|
+
this.value = nextValue;
|
|
1585
|
+
this.onTouched();
|
|
1586
|
+
this.onChange(nextValue);
|
|
1587
|
+
}
|
|
1588
|
+
clear() {
|
|
1589
|
+
if (this.disabled) {
|
|
1590
|
+
return;
|
|
1591
|
+
}
|
|
1592
|
+
this.value = '';
|
|
1593
|
+
this.onTouched();
|
|
1594
|
+
this.onChange(null);
|
|
1595
|
+
}
|
|
1596
|
+
tabIndexFor(optionValue) {
|
|
1597
|
+
if (this.disabled) {
|
|
1598
|
+
return -1;
|
|
1599
|
+
}
|
|
1600
|
+
if (this.value) {
|
|
1601
|
+
return this.value === optionValue ? 0 : -1;
|
|
1602
|
+
}
|
|
1603
|
+
return this.options[0]?.value === optionValue ? 0 : -1;
|
|
1604
|
+
}
|
|
1605
|
+
onOptionKeydown(event, optionValue) {
|
|
1606
|
+
if (this.disabled) {
|
|
1607
|
+
return;
|
|
1608
|
+
}
|
|
1609
|
+
const key = event.key;
|
|
1610
|
+
if (key === ' ' || key === 'Enter') {
|
|
1611
|
+
event.preventDefault();
|
|
1612
|
+
this.select(optionValue);
|
|
1613
|
+
return;
|
|
1614
|
+
}
|
|
1615
|
+
const direction = key === 'ArrowRight' || key === 'ArrowDown'
|
|
1616
|
+
? 1
|
|
1617
|
+
: key === 'ArrowLeft' || key === 'ArrowUp'
|
|
1618
|
+
? -1
|
|
1619
|
+
: 0;
|
|
1620
|
+
if (direction) {
|
|
1621
|
+
event.preventDefault();
|
|
1622
|
+
this.focusAndSelectRelative(optionValue, direction);
|
|
1623
|
+
return;
|
|
1624
|
+
}
|
|
1625
|
+
if (key === 'Home' || key === 'End') {
|
|
1626
|
+
event.preventDefault();
|
|
1627
|
+
this.focusAndSelectIndex(key === 'Home' ? 0 : this.options.length - 1);
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
focusAndSelectRelative(currentValue, delta) {
|
|
1631
|
+
const options = this.options;
|
|
1632
|
+
if (!options.length) {
|
|
1633
|
+
return;
|
|
1634
|
+
}
|
|
1635
|
+
const currentIndex = Math.max(0, options.findIndex((option) => option.value === currentValue));
|
|
1636
|
+
const nextIndex = (currentIndex + delta + options.length) % options.length;
|
|
1637
|
+
this.focusAndSelectIndex(nextIndex);
|
|
1638
|
+
}
|
|
1639
|
+
focusAndSelectIndex(index) {
|
|
1640
|
+
const option = this.options[index];
|
|
1641
|
+
if (!option) {
|
|
1642
|
+
return;
|
|
1643
|
+
}
|
|
1644
|
+
this.select(option.value);
|
|
1645
|
+
queueMicrotask(() => {
|
|
1646
|
+
const selector = `.pme-choice__option[role="radio"][data-choice-value="${CSS.escape(option.value)}"]`;
|
|
1647
|
+
this.elementRef.nativeElement.querySelector(selector)?.focus();
|
|
1648
|
+
});
|
|
1649
|
+
}
|
|
1650
|
+
iconFor(optionValue) {
|
|
1651
|
+
return this.kind === 'appearance'
|
|
1652
|
+
? APPEARANCE_ICONS[optionValue] ?? 'radio_button_unchecked'
|
|
1653
|
+
: PRESENTER_ICONS[optionValue] ?? 'radio_button_unchecked';
|
|
1654
|
+
}
|
|
1655
|
+
text(key, fallback) {
|
|
1656
|
+
return resolvePraxisMetadataEditorText(this.i18n, key, fallback);
|
|
1657
|
+
}
|
|
1658
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PresentationChoiceControlComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1659
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PresentationChoiceControlComponent, isStandalone: true, selector: "praxis-presentation-choice-control", inputs: { field: "field" }, providers: [
|
|
1660
|
+
{
|
|
1661
|
+
provide: NG_VALUE_ACCESSOR,
|
|
1662
|
+
useExisting: forwardRef(() => PresentationChoiceControlComponent),
|
|
1663
|
+
multi: true,
|
|
1664
|
+
},
|
|
1665
|
+
], ngImport: i0, template: `
|
|
1666
|
+
<section class="pme-choice" [class.is-disabled]="disabled">
|
|
1667
|
+
<div class="pme-choice__header">
|
|
1668
|
+
<span class="pme-choice__label">{{ field?.label }}</span>
|
|
1669
|
+
@if (field?.hint) {
|
|
1670
|
+
<button
|
|
1671
|
+
type="button"
|
|
1672
|
+
mat-icon-button
|
|
1673
|
+
class="pme-choice__help"
|
|
1674
|
+
[matTooltip]="field?.hint || ''"
|
|
1675
|
+
[attr.aria-label]="text('presentationChoice.help', 'Ajuda')"
|
|
1676
|
+
[disabled]="disabled"
|
|
1677
|
+
>
|
|
1678
|
+
<mat-icon [praxisIcon]="'help_outline'"></mat-icon>
|
|
1679
|
+
</button>
|
|
1680
|
+
}
|
|
1681
|
+
</div>
|
|
1682
|
+
|
|
1683
|
+
<div
|
|
1684
|
+
class="pme-choice__options"
|
|
1685
|
+
[class.pme-choice__options--tone]="kind === 'tone'"
|
|
1686
|
+
role="radiogroup"
|
|
1687
|
+
[attr.aria-label]="field?.label || ''"
|
|
1688
|
+
>
|
|
1689
|
+
@for (option of options; track option.value) {
|
|
1690
|
+
<button
|
|
1691
|
+
type="button"
|
|
1692
|
+
class="pme-choice__option"
|
|
1693
|
+
[class.is-selected]="value === option.value"
|
|
1694
|
+
[class.is-tone]="kind === 'tone'"
|
|
1695
|
+
[attr.data-tone]="kind === 'tone' ? option.value : null"
|
|
1696
|
+
[attr.data-choice-value]="option.value"
|
|
1697
|
+
[attr.aria-checked]="value === option.value"
|
|
1698
|
+
[attr.tabindex]="tabIndexFor(option.value)"
|
|
1699
|
+
role="radio"
|
|
1700
|
+
[disabled]="disabled"
|
|
1701
|
+
(click)="select(option.value)"
|
|
1702
|
+
(keydown)="onOptionKeydown($event, option.value)"
|
|
1703
|
+
>
|
|
1704
|
+
@if (kind === 'tone') {
|
|
1705
|
+
<span class="pme-choice__swatch" aria-hidden="true"></span>
|
|
1706
|
+
} @else {
|
|
1707
|
+
<mat-icon [praxisIcon]="iconFor(option.value)"></mat-icon>
|
|
1708
|
+
}
|
|
1709
|
+
<span>{{ option.text }}</span>
|
|
1710
|
+
</button>
|
|
1711
|
+
}
|
|
1712
|
+
</div>
|
|
1713
|
+
|
|
1714
|
+
@if (value) {
|
|
1715
|
+
<button
|
|
1716
|
+
type="button"
|
|
1717
|
+
mat-button
|
|
1718
|
+
class="pme-choice__clear"
|
|
1719
|
+
[disabled]="disabled"
|
|
1720
|
+
(click)="clear()"
|
|
1721
|
+
>
|
|
1722
|
+
<mat-icon [praxisIcon]="'close'"></mat-icon>
|
|
1723
|
+
{{ text('presentationChoice.clear', 'Limpar seleção') }}
|
|
1724
|
+
</button>
|
|
1725
|
+
}
|
|
1726
|
+
</section>
|
|
1727
|
+
`, isInline: true, styles: [":host{display:block;min-width:0;width:100%}.pme-choice{display:grid;gap:8px}.pme-choice.is-disabled{opacity:.7}.pme-choice__header{display:flex;align-items:center;gap:4px;min-height:28px}.pme-choice__label{color:var(--md-sys-color-on-surface);font-size:.88rem;font-weight:650;line-height:1.25}.pme-choice__help{width:28px;height:28px;min-width:28px;padding:0;color:var(--md-sys-color-primary)}.pme-choice__options{display:flex;flex-wrap:wrap;gap:6px}.pme-choice__option{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:34px;padding:0 10px;border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;background:var(--md-sys-color-surface-container);color:var(--md-sys-color-on-surface-variant);cursor:pointer;font:inherit;line-height:1.2}.pme-choice__option:hover:not(:disabled){border-color:var(--md-sys-color-primary);color:var(--md-sys-color-on-surface)}.pme-choice__option:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.pme-choice__option.is-selected{border-color:var(--md-sys-color-primary);background:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container)}.pme-choice__option mat-icon{font-size:18px;width:18px;height:18px}.pme-choice__option.is-tone{padding-left:8px}.pme-choice__swatch{width:14px;height:14px;border-radius:999px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-highest)}.pme-choice__option[data-tone=info] .pme-choice__swatch{background:var(--md-sys-color-primary-container);border-color:var(--md-sys-color-primary)}.pme-choice__option[data-tone=success] .pme-choice__swatch{background:var(--md-sys-color-tertiary-container);border-color:var(--md-sys-color-tertiary)}.pme-choice__option[data-tone=warning] .pme-choice__swatch{background:var(--md-sys-color-secondary-container);border-color:var(--md-sys-color-secondary)}.pme-choice__option[data-tone=danger] .pme-choice__swatch{background:var(--md-sys-color-error-container);border-color:var(--md-sys-color-error)}.pme-choice__clear{justify-self:start;min-height:30px;padding:0 8px;color:var(--md-sys-color-on-surface-variant)}.pme-choice__clear mat-icon{font-size:16px;width:16px;height:16px;margin-right:4px}@media(max-width:520px){.pme-choice__options{display:grid;grid-template-columns:1fr}.pme-choice__option{justify-content:flex-start;width:100%}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i2$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }] });
|
|
1728
|
+
}
|
|
1729
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PresentationChoiceControlComponent, decorators: [{
|
|
1730
|
+
type: Component,
|
|
1731
|
+
args: [{ selector: 'praxis-presentation-choice-control', standalone: true, imports: [
|
|
1732
|
+
CommonModule,
|
|
1733
|
+
MatButtonModule,
|
|
1734
|
+
MatIconModule,
|
|
1735
|
+
MatTooltipModule,
|
|
1736
|
+
PraxisIconDirective,
|
|
1737
|
+
], providers: [
|
|
1738
|
+
{
|
|
1739
|
+
provide: NG_VALUE_ACCESSOR,
|
|
1740
|
+
useExisting: forwardRef(() => PresentationChoiceControlComponent),
|
|
1741
|
+
multi: true,
|
|
1742
|
+
},
|
|
1743
|
+
], template: `
|
|
1744
|
+
<section class="pme-choice" [class.is-disabled]="disabled">
|
|
1745
|
+
<div class="pme-choice__header">
|
|
1746
|
+
<span class="pme-choice__label">{{ field?.label }}</span>
|
|
1747
|
+
@if (field?.hint) {
|
|
1748
|
+
<button
|
|
1749
|
+
type="button"
|
|
1750
|
+
mat-icon-button
|
|
1751
|
+
class="pme-choice__help"
|
|
1752
|
+
[matTooltip]="field?.hint || ''"
|
|
1753
|
+
[attr.aria-label]="text('presentationChoice.help', 'Ajuda')"
|
|
1754
|
+
[disabled]="disabled"
|
|
1755
|
+
>
|
|
1756
|
+
<mat-icon [praxisIcon]="'help_outline'"></mat-icon>
|
|
1757
|
+
</button>
|
|
1758
|
+
}
|
|
1759
|
+
</div>
|
|
1760
|
+
|
|
1761
|
+
<div
|
|
1762
|
+
class="pme-choice__options"
|
|
1763
|
+
[class.pme-choice__options--tone]="kind === 'tone'"
|
|
1764
|
+
role="radiogroup"
|
|
1765
|
+
[attr.aria-label]="field?.label || ''"
|
|
1766
|
+
>
|
|
1767
|
+
@for (option of options; track option.value) {
|
|
1768
|
+
<button
|
|
1769
|
+
type="button"
|
|
1770
|
+
class="pme-choice__option"
|
|
1771
|
+
[class.is-selected]="value === option.value"
|
|
1772
|
+
[class.is-tone]="kind === 'tone'"
|
|
1773
|
+
[attr.data-tone]="kind === 'tone' ? option.value : null"
|
|
1774
|
+
[attr.data-choice-value]="option.value"
|
|
1775
|
+
[attr.aria-checked]="value === option.value"
|
|
1776
|
+
[attr.tabindex]="tabIndexFor(option.value)"
|
|
1777
|
+
role="radio"
|
|
1778
|
+
[disabled]="disabled"
|
|
1779
|
+
(click)="select(option.value)"
|
|
1780
|
+
(keydown)="onOptionKeydown($event, option.value)"
|
|
1781
|
+
>
|
|
1782
|
+
@if (kind === 'tone') {
|
|
1783
|
+
<span class="pme-choice__swatch" aria-hidden="true"></span>
|
|
1784
|
+
} @else {
|
|
1785
|
+
<mat-icon [praxisIcon]="iconFor(option.value)"></mat-icon>
|
|
1786
|
+
}
|
|
1787
|
+
<span>{{ option.text }}</span>
|
|
1788
|
+
</button>
|
|
1789
|
+
}
|
|
1790
|
+
</div>
|
|
1791
|
+
|
|
1792
|
+
@if (value) {
|
|
1793
|
+
<button
|
|
1794
|
+
type="button"
|
|
1795
|
+
mat-button
|
|
1796
|
+
class="pme-choice__clear"
|
|
1797
|
+
[disabled]="disabled"
|
|
1798
|
+
(click)="clear()"
|
|
1799
|
+
>
|
|
1800
|
+
<mat-icon [praxisIcon]="'close'"></mat-icon>
|
|
1801
|
+
{{ text('presentationChoice.clear', 'Limpar seleção') }}
|
|
1802
|
+
</button>
|
|
1803
|
+
}
|
|
1804
|
+
</section>
|
|
1805
|
+
`, styles: [":host{display:block;min-width:0;width:100%}.pme-choice{display:grid;gap:8px}.pme-choice.is-disabled{opacity:.7}.pme-choice__header{display:flex;align-items:center;gap:4px;min-height:28px}.pme-choice__label{color:var(--md-sys-color-on-surface);font-size:.88rem;font-weight:650;line-height:1.25}.pme-choice__help{width:28px;height:28px;min-width:28px;padding:0;color:var(--md-sys-color-primary)}.pme-choice__options{display:flex;flex-wrap:wrap;gap:6px}.pme-choice__option{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:34px;padding:0 10px;border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;background:var(--md-sys-color-surface-container);color:var(--md-sys-color-on-surface-variant);cursor:pointer;font:inherit;line-height:1.2}.pme-choice__option:hover:not(:disabled){border-color:var(--md-sys-color-primary);color:var(--md-sys-color-on-surface)}.pme-choice__option:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.pme-choice__option.is-selected{border-color:var(--md-sys-color-primary);background:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container)}.pme-choice__option mat-icon{font-size:18px;width:18px;height:18px}.pme-choice__option.is-tone{padding-left:8px}.pme-choice__swatch{width:14px;height:14px;border-radius:999px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-highest)}.pme-choice__option[data-tone=info] .pme-choice__swatch{background:var(--md-sys-color-primary-container);border-color:var(--md-sys-color-primary)}.pme-choice__option[data-tone=success] .pme-choice__swatch{background:var(--md-sys-color-tertiary-container);border-color:var(--md-sys-color-tertiary)}.pme-choice__option[data-tone=warning] .pme-choice__swatch{background:var(--md-sys-color-secondary-container);border-color:var(--md-sys-color-secondary)}.pme-choice__option[data-tone=danger] .pme-choice__swatch{background:var(--md-sys-color-error-container);border-color:var(--md-sys-color-error)}.pme-choice__clear{justify-self:start;min-height:30px;padding:0 8px;color:var(--md-sys-color-on-surface-variant)}.pme-choice__clear mat-icon{font-size:16px;width:16px;height:16px;margin-right:4px}@media(max-width:520px){.pme-choice__options{display:grid;grid-template-columns:1fr}.pme-choice__option{justify-content:flex-start;width:100%}}\n"] }]
|
|
1806
|
+
}], propDecorators: { field: [{
|
|
1807
|
+
type: Input
|
|
1808
|
+
}] } });
|
|
406
1809
|
|
|
407
1810
|
class DynamicEditorRendererComponent {
|
|
408
1811
|
dialog;
|
|
409
1812
|
iconPicker;
|
|
1813
|
+
i18n;
|
|
410
1814
|
properties = [];
|
|
411
1815
|
form;
|
|
412
1816
|
fields = [];
|
|
@@ -448,29 +1852,32 @@ class DynamicEditorRendererComponent {
|
|
|
448
1852
|
}
|
|
449
1853
|
toFieldMetadata(p) {
|
|
450
1854
|
const controlType = this.mapEditorType(p.editorType);
|
|
1855
|
+
const label = this.resolveEditorText(p.labelKey, p.label);
|
|
1856
|
+
const placeholder = this.resolveEditorText(p.placeholderKey, p.placeholder);
|
|
1857
|
+
const hint = this.resolveEditorText(p.hintKey, p.hint);
|
|
451
1858
|
const meta = {
|
|
452
1859
|
name: p.name,
|
|
453
|
-
label: p.label,
|
|
1860
|
+
label: label ?? p.label,
|
|
454
1861
|
controlType,
|
|
455
|
-
placeholder
|
|
1862
|
+
placeholder,
|
|
456
1863
|
// Ajuda contextual padrão quando ausente
|
|
457
|
-
hint
|
|
1864
|
+
hint,
|
|
458
1865
|
// Help suffix dentro do campo (padrao) - somente para campos compatíveis
|
|
459
|
-
suffixIcon:
|
|
1866
|
+
suffixIcon: hint && !this.isIconField(p.name) && this.isSuffixCapable(p.editorType)
|
|
460
1867
|
? 'help_outline'
|
|
461
1868
|
: undefined,
|
|
462
|
-
suffixIconTooltip:
|
|
463
|
-
?
|
|
1869
|
+
suffixIconTooltip: hint && !this.isIconField(p.name) && this.isSuffixCapable(p.editorType)
|
|
1870
|
+
? hint
|
|
464
1871
|
: undefined,
|
|
465
|
-
suffixIconAriaLabel:
|
|
466
|
-
? 'Ajuda'
|
|
1872
|
+
suffixIconAriaLabel: hint && !this.isIconField(p.name) && this.isSuffixCapable(p.editorType)
|
|
1873
|
+
? this.resolveEditorText('common.help', 'Ajuda')
|
|
467
1874
|
: undefined,
|
|
468
|
-
options:
|
|
1875
|
+
options: this.resolveOptions(p.options),
|
|
469
1876
|
required: p.required,
|
|
470
1877
|
searchable: p.searchable,
|
|
471
|
-
searchPlaceholder: p.searchPlaceholder,
|
|
1878
|
+
searchPlaceholder: this.resolveEditorText(p.searchPlaceholderKey, p.searchPlaceholder),
|
|
472
1879
|
// meta auxiliar de agrupamento (não exportado)
|
|
473
|
-
_group: p.group || 'Geral',
|
|
1880
|
+
_group: this.resolveEditorText(p.groupKey, p.group || 'Geral'),
|
|
474
1881
|
_inline: p.inline || false,
|
|
475
1882
|
_row: p.row || undefined,
|
|
476
1883
|
_editorType: p.editorType,
|
|
@@ -495,7 +1902,7 @@ class DynamicEditorRendererComponent {
|
|
|
495
1902
|
}
|
|
496
1903
|
// Apenas campos de ícone devem receber o hint de Material Symbols
|
|
497
1904
|
if (this.isIconField(p.name) && !meta.hint) {
|
|
498
|
-
meta.hint = 'Use o nome do ícone (Material Symbols)';
|
|
1905
|
+
meta.hint = this.resolveEditorText('common.iconMaterialSymbolsHint', 'Use o nome do ícone (Material Symbols)');
|
|
499
1906
|
}
|
|
500
1907
|
// Booleans em linha (switches) para melhor densidade
|
|
501
1908
|
const booleanLike = new Set([
|
|
@@ -549,6 +1956,10 @@ class DynamicEditorRendererComponent {
|
|
|
549
1956
|
}
|
|
550
1957
|
// (preview removed by request)
|
|
551
1958
|
rowClass(row) {
|
|
1959
|
+
if (this.isPresentationRulesRow(row))
|
|
1960
|
+
return 'row-presentation-rules';
|
|
1961
|
+
if (this.isPresentationChoiceRow(row))
|
|
1962
|
+
return 'row-presentation-choice';
|
|
552
1963
|
// Linha compacta (toggles) quando todos são inline
|
|
553
1964
|
if (row.every((f) => !!f._inline))
|
|
554
1965
|
return 'row-inline';
|
|
@@ -560,6 +1971,28 @@ class DynamicEditorRendererComponent {
|
|
|
560
1971
|
return 'row-3';
|
|
561
1972
|
return '';
|
|
562
1973
|
}
|
|
1974
|
+
isPresentationRulesRow(row) {
|
|
1975
|
+
return row.length > 0 && row.every((field) => field._editorType === 'presentation-rules-builder');
|
|
1976
|
+
}
|
|
1977
|
+
isPresentationChoiceRow(row) {
|
|
1978
|
+
return row.length > 0 && row.every((field) => field._editorType === 'presentation-choice');
|
|
1979
|
+
}
|
|
1980
|
+
canRenderPresentationRulesRow(row) {
|
|
1981
|
+
return this.ready && this.isPresentationRulesRow(row) && this.hasControlsForRow(row);
|
|
1982
|
+
}
|
|
1983
|
+
canRenderPresentationChoiceRow(row) {
|
|
1984
|
+
return this.ready && this.isPresentationChoiceRow(row) && this.hasControlsForRow(row);
|
|
1985
|
+
}
|
|
1986
|
+
hasControlsForRow(row) {
|
|
1987
|
+
return row.every((field) => !!this.form?.get(field.name));
|
|
1988
|
+
}
|
|
1989
|
+
controlFor(path) {
|
|
1990
|
+
const control = this.form.get(path);
|
|
1991
|
+
if (!control) {
|
|
1992
|
+
throw new Error(`Missing editor control for ${path}`);
|
|
1993
|
+
}
|
|
1994
|
+
return control;
|
|
1995
|
+
}
|
|
563
1996
|
mapEditorType(t) {
|
|
564
1997
|
switch (t) {
|
|
565
1998
|
case 'text':
|
|
@@ -635,9 +2068,10 @@ class DynamicEditorRendererComponent {
|
|
|
635
2068
|
return 'visibility';
|
|
636
2069
|
return 'settings';
|
|
637
2070
|
}
|
|
638
|
-
constructor(dialog, iconPicker) {
|
|
2071
|
+
constructor(dialog, iconPicker, i18n) {
|
|
639
2072
|
this.dialog = dialog;
|
|
640
2073
|
this.iconPicker = iconPicker;
|
|
2074
|
+
this.i18n = i18n;
|
|
641
2075
|
}
|
|
642
2076
|
isIconField(path) {
|
|
643
2077
|
if (!path)
|
|
@@ -686,7 +2120,22 @@ class DynamicEditorRendererComponent {
|
|
|
686
2120
|
this.form?.markAsDirty();
|
|
687
2121
|
}
|
|
688
2122
|
}
|
|
689
|
-
|
|
2123
|
+
resolveEditorText(key, fallback) {
|
|
2124
|
+
if (!key || !this.i18n) {
|
|
2125
|
+
return fallback;
|
|
2126
|
+
}
|
|
2127
|
+
return resolvePraxisMetadataEditorText(this.i18n, key, fallback ?? key);
|
|
2128
|
+
}
|
|
2129
|
+
resolveOptions(options) {
|
|
2130
|
+
if (!Array.isArray(options)) {
|
|
2131
|
+
return undefined;
|
|
2132
|
+
}
|
|
2133
|
+
return options.map((option) => ({
|
|
2134
|
+
...option,
|
|
2135
|
+
text: this.resolveEditorText(option.textKey, option.text) ?? option.text,
|
|
2136
|
+
}));
|
|
2137
|
+
}
|
|
2138
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DynamicEditorRendererComponent, deps: [{ token: i1$1.MatDialog }, { token: i2$2.IconPickerService }, { token: i2$2.PraxisI18nService, optional: true }], target: i0.ɵɵFactoryTarget.Component });
|
|
690
2139
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: DynamicEditorRendererComponent, isStandalone: true, selector: "praxis-dynamic-editor-renderer", inputs: { properties: "properties", form: "form" }, usesOnChanges: true, ngImport: i0, template: `
|
|
691
2140
|
<ng-template #item let-field="field" let-index="index" let-content="content">
|
|
692
2141
|
<div class="editor-item" [attr.data-control-type]="field.controlType">
|
|
@@ -750,7 +2199,22 @@ class DynamicEditorRendererComponent {
|
|
|
750
2199
|
</mat-expansion-panel-header>
|
|
751
2200
|
@for (row of groupRows[g]; track row) {
|
|
752
2201
|
<div class="editor-row" [ngClass]="rowClass(row)">
|
|
753
|
-
@if (
|
|
2202
|
+
@if (canRenderPresentationRulesRow(row)) {
|
|
2203
|
+
@for (field of row; track field.name) {
|
|
2204
|
+
<praxis-presentation-rules-builder
|
|
2205
|
+
class="presentation-rules-field"
|
|
2206
|
+
[formControl]="controlFor(field.name)">
|
|
2207
|
+
</praxis-presentation-rules-builder>
|
|
2208
|
+
}
|
|
2209
|
+
} @else if (canRenderPresentationChoiceRow(row)) {
|
|
2210
|
+
@for (field of row; track field.name) {
|
|
2211
|
+
<praxis-presentation-choice-control
|
|
2212
|
+
class="presentation-choice-field"
|
|
2213
|
+
[field]="field"
|
|
2214
|
+
[formControl]="controlFor(field.name)">
|
|
2215
|
+
</praxis-presentation-choice-control>
|
|
2216
|
+
}
|
|
2217
|
+
} @else if (ready && hasControlsForRow(row)) {
|
|
754
2218
|
<ng-container
|
|
755
2219
|
dynamicFieldLoader
|
|
756
2220
|
[fields]="row"
|
|
@@ -764,7 +2228,7 @@ class DynamicEditorRendererComponent {
|
|
|
764
2228
|
}
|
|
765
2229
|
</mat-accordion>
|
|
766
2230
|
</div>
|
|
767
|
-
`, isInline: true, styles: [":host{display:block}.editor-surface{padding:8px 0 2px;border-radius:12px;background:transparent}.section-accordion{display:block}.section-card{margin:6px 0 8px;border-radius:12px;border:1px solid var(--md-sys-color-outline-variant);overflow:hidden}.section-card.mat-expanded{border-color:var(--md-sys-color-primary);box-shadow:0 2px 10px #0000000d}.section-card .mat-expansion-panel-body{background:var(--md-sys-color-surface-container-low)}.section-header{background:var(--md-sys-color-surface-container-low);border-bottom:1px solid var(--md-sys-color-outline-variant, var(--sicoob-stroke-medium));padding:2px 8px;min-height:32px;display:flex;align-items:center;color:var(--md-sys-color-on-surface)}.section-header .mat-expansion-panel-header-title,.section-header .mat-expansion-panel-header-description{display:flex;align-items:center;color:var(--md-sys-color-on-surface)}.section-title{display:inline-flex;align-items:center;gap:6px;font-weight:600;letter-spacing:.2px;color:var(--md-sys-color-on-surface);font-size:.9rem;line-height:1.2}.section-icon{color:var(--md-sys-color-primary, var(--md-sys-color-primary, #3f51b5));opacity:.9}.editor-row{display:flex;gap:12px;align-items:flex-start;flex-wrap:wrap;margin-bottom:8px}.editor-row>praxis-field-shell{flex:1 1 100%;min-width:280px}.editor-row praxis-field-shell .mat-mdc-form-field{width:100%;margin-bottom:8px}.editor-row.row-inline>praxis-field-shell{flex:0 0 auto}.editor-row.row-2>praxis-field-shell{flex:1 1 calc(50% - 12px);min-width:260px}.editor-row.row-3>praxis-field-shell{flex:1 1 calc(33.333% - 12px);min-width:220px}.editor-row>praxis-field-shell[data-field-name=controlType],.editor-row>praxis-field-shell[data-field-name=label],.editor-row>praxis-field-shell[data-field-name=placeholder]{flex:1 1 calc(50% - 12px);min-width:300px}praxis-field-shell[data-field-name=label] .mdc-floating-label,praxis-field-shell[data-field-name=placeholder] .mdc-floating-label{font-weight:600}.editor-row>praxis-field-shell[data-field-name=prefix],.editor-row>praxis-field-shell[data-field-name=suffix]{flex:0 0 200px;min-width:160px}.editor-row>praxis-field-shell[data-field-name=prefixIcon],.editor-row>praxis-field-shell[data-field-name=suffixIcon],.editor-row>praxis-field-shell[data-field-name=prefixIconColor],.editor-row>praxis-field-shell[data-field-name=suffixIconColor]{flex:1 1 auto;min-width:160px}.editor-item{display:flex;align-items:flex-start;gap:4px}.editor-item>ng-container,.editor-item>praxis-field-shell{flex:1;min-width:0}.actions-container{display:flex;align-items:center;gap:0;margin-top:12px;flex-shrink:0}.decorator-btn{position:static;color:var(--md-sys-color-primary, #3f51b5);opacity:.6;transform:scale(.8);width:32px;height:32px;line-height:32px;padding:0}.decorator-btn:hover{opacity:1}.icon-picker-btn,.hint-btn,.hint-btn.has-icon-picker{right:auto}.editor-row .mat-mdc-form-field-subscript-wrapper mat-hint{display:none!important}.editor-row .mat-mdc-form-field-subscript-wrapper mat-error{display:block!important}praxis-field-shell[data-field-name=showCharCount] .mdc-switch{transform:scale(1.05)}praxis-field-shell[data-field-name=showCharCount] .mat-mdc-slide-toggle{font-weight:600}praxis-field-shell[data-field-name=controlType] .mat-mdc-form-field-hint{display:flex;align-items:center;gap:8px;background:var(--md-sys-color-tertiary-container);border:1px solid var(--md-sys-color-tertiary);color:var(--md-sys-color-on-tertiary-container);padding:8px 10px;border-radius:6px;line-height:1.3;white-space:normal}praxis-field-shell[data-field-name=controlType] .mat-mdc-form-field-hint:before{content:\"\\26a0\\fe0f\";display:inline-block}praxis-field-shell[data-field-name=controlType] .mat-mdc-form-field-subscript-wrapper{margin-top:6px;margin-bottom:10px;min-height:auto}praxis-field-shell[data-field-name=controlType] .mat-mdc-form-field{margin-bottom:16px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: DynamicFieldLoaderDirective, selector: "[dynamicFieldLoader]", inputs: ["fields", "formGroup", "enableExternalControlBinding", "itemTemplate", "debugTrace", "debugTraceLabel", "readonlyMode", "disabledMode", "presentationMode", "visible", "canvasMode"], outputs: ["componentsCreated", "fieldCreated", "fieldDestroyed", "renderError", "canvasMouseEnter", "canvasMouseLeave", "canvasClick", "canvasFocus"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "directive", type: i5.MatAccordion, selector: "mat-accordion", inputs: ["hideToggle", "displayMode", "togglePosition"], exportAs: ["matAccordion"] }, { kind: "component", type: i5.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i5.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i5.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type:
|
|
2231
|
+
`, isInline: true, styles: [":host{display:block}.editor-surface{padding:8px 0 2px;border-radius:12px;background:transparent}.section-accordion{display:block}.section-card{margin:6px 0 8px;border-radius:12px;border:1px solid var(--md-sys-color-outline-variant);overflow:hidden}.section-card.mat-expanded{border-color:var(--md-sys-color-primary);box-shadow:0 2px 10px #0000000d}.section-card .mat-expansion-panel-body{background:var(--md-sys-color-surface-container-low)}.section-header{background:var(--md-sys-color-surface-container-low);border-bottom:1px solid var(--md-sys-color-outline-variant, var(--sicoob-stroke-medium));padding:2px 8px;min-height:32px;display:flex;align-items:center;color:var(--md-sys-color-on-surface)}.section-header .mat-expansion-panel-header-title,.section-header .mat-expansion-panel-header-description{display:flex;align-items:center;color:var(--md-sys-color-on-surface)}.section-title{display:inline-flex;align-items:center;gap:6px;font-weight:600;letter-spacing:.2px;color:var(--md-sys-color-on-surface);font-size:.9rem;line-height:1.2}.section-icon{color:var(--md-sys-color-primary, var(--md-sys-color-primary, #3f51b5));opacity:.9}.editor-row{display:flex;gap:12px;align-items:flex-start;flex-wrap:wrap;margin-bottom:8px}.editor-row>praxis-field-shell{flex:1 1 100%;min-width:280px}.editor-row praxis-field-shell .mat-mdc-form-field{width:100%;margin-bottom:8px}.editor-row.row-inline>praxis-field-shell{flex:0 0 auto}.editor-row.row-2>praxis-field-shell{flex:1 1 calc(50% - 12px);min-width:260px}.editor-row.row-3>praxis-field-shell{flex:1 1 calc(33.333% - 12px);min-width:220px}.editor-row>praxis-field-shell[data-field-name=controlType],.editor-row>praxis-field-shell[data-field-name=label],.editor-row>praxis-field-shell[data-field-name=placeholder]{flex:1 1 calc(50% - 12px);min-width:300px}.editor-row>praxis-presentation-rules-builder.presentation-rules-field{flex:1 1 100%;min-width:0}.editor-row>praxis-presentation-choice-control.presentation-choice-field{flex:1 1 100%;min-width:0}praxis-field-shell[data-field-name=label] .mdc-floating-label,praxis-field-shell[data-field-name=placeholder] .mdc-floating-label{font-weight:600}.editor-row>praxis-field-shell[data-field-name=prefix],.editor-row>praxis-field-shell[data-field-name=suffix]{flex:0 0 200px;min-width:160px}.editor-row>praxis-field-shell[data-field-name=prefixIcon],.editor-row>praxis-field-shell[data-field-name=suffixIcon],.editor-row>praxis-field-shell[data-field-name=prefixIconColor],.editor-row>praxis-field-shell[data-field-name=suffixIconColor]{flex:1 1 auto;min-width:160px}.editor-item{display:flex;align-items:flex-start;gap:4px;min-width:0;width:100%}.editor-item>ng-container,.editor-item>praxis-field-shell{flex:1;min-width:0}.actions-container{display:flex;align-items:center;gap:0;margin-top:12px;flex-shrink:0}.decorator-btn{position:static;color:var(--md-sys-color-primary, #3f51b5);opacity:.6;transform:scale(.8);width:32px;height:32px;line-height:32px;padding:0}.decorator-btn:hover{opacity:1}.icon-picker-btn,.hint-btn,.hint-btn.has-icon-picker{right:auto}.editor-row .mat-mdc-form-field-subscript-wrapper mat-hint{display:none!important}.editor-row .mat-mdc-form-field-subscript-wrapper mat-error{display:block!important}praxis-field-shell[data-field-name=showCharCount] .mdc-switch{transform:scale(1.05)}praxis-field-shell[data-field-name=showCharCount] .mat-mdc-slide-toggle{font-weight:600}praxis-field-shell[data-field-name=controlType] .mat-mdc-form-field-hint{display:flex;align-items:center;gap:8px;background:var(--md-sys-color-tertiary-container);border:1px solid var(--md-sys-color-tertiary);color:var(--md-sys-color-on-tertiary-container);padding:8px 10px;border-radius:6px;line-height:1.3;white-space:normal}praxis-field-shell[data-field-name=controlType] .mat-mdc-form-field-hint:before{content:\"\\26a0\\fe0f\";display:inline-block}praxis-field-shell[data-field-name=controlType] .mat-mdc-form-field-subscript-wrapper{margin-top:6px;margin-bottom:10px;min-height:auto}praxis-field-shell[data-field-name=controlType] .mat-mdc-form-field{margin-bottom:16px}@media(max-width:520px){.editor-row,.editor-row.row-inline{gap:8px}.editor-item{flex-direction:column;gap:2px}.editor-row.row-inline>praxis-field-shell,.editor-row.row-2>praxis-field-shell,.editor-row.row-3>praxis-field-shell{flex:1 1 100%;min-width:0}.actions-container{width:100%;justify-content:flex-end;margin-top:0}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i3$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i3$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: DynamicFieldLoaderDirective, selector: "[dynamicFieldLoader]", inputs: ["fields", "formGroup", "enableExternalControlBinding", "itemTemplate", "debugTrace", "debugTraceLabel", "readonlyMode", "disabledMode", "presentationMode", "visible", "canvasMode"], outputs: ["componentsCreated", "fieldCreated", "fieldDestroyed", "renderError", "canvasMouseEnter", "canvasMouseLeave", "canvasClick", "canvasFocus"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "directive", type: i5$1.MatAccordion, selector: "mat-accordion", inputs: ["hideToggle", "displayMode", "togglePosition"], exportAs: ["matAccordion"] }, { kind: "component", type: i5$1.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i5$1.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i5$1.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatDialogModule }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: PresentationRulesBuilderComponent, selector: "praxis-presentation-rules-builder" }, { kind: "component", type: PresentationChoiceControlComponent, selector: "praxis-presentation-choice-control", inputs: ["field"] }], encapsulation: i0.ViewEncapsulation.None });
|
|
768
2232
|
}
|
|
769
2233
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DynamicEditorRendererComponent, decorators: [{
|
|
770
2234
|
type: Component,
|
|
@@ -778,6 +2242,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
|
|
|
778
2242
|
MatDialogModule,
|
|
779
2243
|
MatTooltipModule,
|
|
780
2244
|
PraxisIconDirective,
|
|
2245
|
+
PresentationRulesBuilderComponent,
|
|
2246
|
+
PresentationChoiceControlComponent,
|
|
781
2247
|
], encapsulation: ViewEncapsulation.None, template: `
|
|
782
2248
|
<ng-template #item let-field="field" let-index="index" let-content="content">
|
|
783
2249
|
<div class="editor-item" [attr.data-control-type]="field.controlType">
|
|
@@ -841,7 +2307,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
|
|
|
841
2307
|
</mat-expansion-panel-header>
|
|
842
2308
|
@for (row of groupRows[g]; track row) {
|
|
843
2309
|
<div class="editor-row" [ngClass]="rowClass(row)">
|
|
844
|
-
@if (
|
|
2310
|
+
@if (canRenderPresentationRulesRow(row)) {
|
|
2311
|
+
@for (field of row; track field.name) {
|
|
2312
|
+
<praxis-presentation-rules-builder
|
|
2313
|
+
class="presentation-rules-field"
|
|
2314
|
+
[formControl]="controlFor(field.name)">
|
|
2315
|
+
</praxis-presentation-rules-builder>
|
|
2316
|
+
}
|
|
2317
|
+
} @else if (canRenderPresentationChoiceRow(row)) {
|
|
2318
|
+
@for (field of row; track field.name) {
|
|
2319
|
+
<praxis-presentation-choice-control
|
|
2320
|
+
class="presentation-choice-field"
|
|
2321
|
+
[field]="field"
|
|
2322
|
+
[formControl]="controlFor(field.name)">
|
|
2323
|
+
</praxis-presentation-choice-control>
|
|
2324
|
+
}
|
|
2325
|
+
} @else if (ready && hasControlsForRow(row)) {
|
|
845
2326
|
<ng-container
|
|
846
2327
|
dynamicFieldLoader
|
|
847
2328
|
[fields]="row"
|
|
@@ -855,122 +2336,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
|
|
|
855
2336
|
}
|
|
856
2337
|
</mat-accordion>
|
|
857
2338
|
</div>
|
|
858
|
-
`, styles: [":host{display:block}.editor-surface{padding:8px 0 2px;border-radius:12px;background:transparent}.section-accordion{display:block}.section-card{margin:6px 0 8px;border-radius:12px;border:1px solid var(--md-sys-color-outline-variant);overflow:hidden}.section-card.mat-expanded{border-color:var(--md-sys-color-primary);box-shadow:0 2px 10px #0000000d}.section-card .mat-expansion-panel-body{background:var(--md-sys-color-surface-container-low)}.section-header{background:var(--md-sys-color-surface-container-low);border-bottom:1px solid var(--md-sys-color-outline-variant, var(--sicoob-stroke-medium));padding:2px 8px;min-height:32px;display:flex;align-items:center;color:var(--md-sys-color-on-surface)}.section-header .mat-expansion-panel-header-title,.section-header .mat-expansion-panel-header-description{display:flex;align-items:center;color:var(--md-sys-color-on-surface)}.section-title{display:inline-flex;align-items:center;gap:6px;font-weight:600;letter-spacing:.2px;color:var(--md-sys-color-on-surface);font-size:.9rem;line-height:1.2}.section-icon{color:var(--md-sys-color-primary, var(--md-sys-color-primary, #3f51b5));opacity:.9}.editor-row{display:flex;gap:12px;align-items:flex-start;flex-wrap:wrap;margin-bottom:8px}.editor-row>praxis-field-shell{flex:1 1 100%;min-width:280px}.editor-row praxis-field-shell .mat-mdc-form-field{width:100%;margin-bottom:8px}.editor-row.row-inline>praxis-field-shell{flex:0 0 auto}.editor-row.row-2>praxis-field-shell{flex:1 1 calc(50% - 12px);min-width:260px}.editor-row.row-3>praxis-field-shell{flex:1 1 calc(33.333% - 12px);min-width:220px}.editor-row>praxis-field-shell[data-field-name=controlType],.editor-row>praxis-field-shell[data-field-name=label],.editor-row>praxis-field-shell[data-field-name=placeholder]{flex:1 1 calc(50% - 12px);min-width:300px}praxis-field-shell[data-field-name=label] .mdc-floating-label,praxis-field-shell[data-field-name=placeholder] .mdc-floating-label{font-weight:600}.editor-row>praxis-field-shell[data-field-name=prefix],.editor-row>praxis-field-shell[data-field-name=suffix]{flex:0 0 200px;min-width:160px}.editor-row>praxis-field-shell[data-field-name=prefixIcon],.editor-row>praxis-field-shell[data-field-name=suffixIcon],.editor-row>praxis-field-shell[data-field-name=prefixIconColor],.editor-row>praxis-field-shell[data-field-name=suffixIconColor]{flex:1 1 auto;min-width:160px}.editor-item{display:flex;align-items:flex-start;gap:4px}.editor-item>ng-container,.editor-item>praxis-field-shell{flex:1;min-width:0}.actions-container{display:flex;align-items:center;gap:0;margin-top:12px;flex-shrink:0}.decorator-btn{position:static;color:var(--md-sys-color-primary, #3f51b5);opacity:.6;transform:scale(.8);width:32px;height:32px;line-height:32px;padding:0}.decorator-btn:hover{opacity:1}.icon-picker-btn,.hint-btn,.hint-btn.has-icon-picker{right:auto}.editor-row .mat-mdc-form-field-subscript-wrapper mat-hint{display:none!important}.editor-row .mat-mdc-form-field-subscript-wrapper mat-error{display:block!important}praxis-field-shell[data-field-name=showCharCount] .mdc-switch{transform:scale(1.05)}praxis-field-shell[data-field-name=showCharCount] .mat-mdc-slide-toggle{font-weight:600}praxis-field-shell[data-field-name=controlType] .mat-mdc-form-field-hint{display:flex;align-items:center;gap:8px;background:var(--md-sys-color-tertiary-container);border:1px solid var(--md-sys-color-tertiary);color:var(--md-sys-color-on-tertiary-container);padding:8px 10px;border-radius:6px;line-height:1.3;white-space:normal}praxis-field-shell[data-field-name=controlType] .mat-mdc-form-field-hint:before{content:\"\\26a0\\fe0f\";display:inline-block}praxis-field-shell[data-field-name=controlType] .mat-mdc-form-field-subscript-wrapper{margin-top:6px;margin-bottom:10px;min-height:auto}praxis-field-shell[data-field-name=controlType] .mat-mdc-form-field{margin-bottom:16px}\n"] }]
|
|
859
|
-
}], ctorParameters: () => [{ type: i1$1.MatDialog }, { type: i2$
|
|
2339
|
+
`, styles: [":host{display:block}.editor-surface{padding:8px 0 2px;border-radius:12px;background:transparent}.section-accordion{display:block}.section-card{margin:6px 0 8px;border-radius:12px;border:1px solid var(--md-sys-color-outline-variant);overflow:hidden}.section-card.mat-expanded{border-color:var(--md-sys-color-primary);box-shadow:0 2px 10px #0000000d}.section-card .mat-expansion-panel-body{background:var(--md-sys-color-surface-container-low)}.section-header{background:var(--md-sys-color-surface-container-low);border-bottom:1px solid var(--md-sys-color-outline-variant, var(--sicoob-stroke-medium));padding:2px 8px;min-height:32px;display:flex;align-items:center;color:var(--md-sys-color-on-surface)}.section-header .mat-expansion-panel-header-title,.section-header .mat-expansion-panel-header-description{display:flex;align-items:center;color:var(--md-sys-color-on-surface)}.section-title{display:inline-flex;align-items:center;gap:6px;font-weight:600;letter-spacing:.2px;color:var(--md-sys-color-on-surface);font-size:.9rem;line-height:1.2}.section-icon{color:var(--md-sys-color-primary, var(--md-sys-color-primary, #3f51b5));opacity:.9}.editor-row{display:flex;gap:12px;align-items:flex-start;flex-wrap:wrap;margin-bottom:8px}.editor-row>praxis-field-shell{flex:1 1 100%;min-width:280px}.editor-row praxis-field-shell .mat-mdc-form-field{width:100%;margin-bottom:8px}.editor-row.row-inline>praxis-field-shell{flex:0 0 auto}.editor-row.row-2>praxis-field-shell{flex:1 1 calc(50% - 12px);min-width:260px}.editor-row.row-3>praxis-field-shell{flex:1 1 calc(33.333% - 12px);min-width:220px}.editor-row>praxis-field-shell[data-field-name=controlType],.editor-row>praxis-field-shell[data-field-name=label],.editor-row>praxis-field-shell[data-field-name=placeholder]{flex:1 1 calc(50% - 12px);min-width:300px}.editor-row>praxis-presentation-rules-builder.presentation-rules-field{flex:1 1 100%;min-width:0}.editor-row>praxis-presentation-choice-control.presentation-choice-field{flex:1 1 100%;min-width:0}praxis-field-shell[data-field-name=label] .mdc-floating-label,praxis-field-shell[data-field-name=placeholder] .mdc-floating-label{font-weight:600}.editor-row>praxis-field-shell[data-field-name=prefix],.editor-row>praxis-field-shell[data-field-name=suffix]{flex:0 0 200px;min-width:160px}.editor-row>praxis-field-shell[data-field-name=prefixIcon],.editor-row>praxis-field-shell[data-field-name=suffixIcon],.editor-row>praxis-field-shell[data-field-name=prefixIconColor],.editor-row>praxis-field-shell[data-field-name=suffixIconColor]{flex:1 1 auto;min-width:160px}.editor-item{display:flex;align-items:flex-start;gap:4px;min-width:0;width:100%}.editor-item>ng-container,.editor-item>praxis-field-shell{flex:1;min-width:0}.actions-container{display:flex;align-items:center;gap:0;margin-top:12px;flex-shrink:0}.decorator-btn{position:static;color:var(--md-sys-color-primary, #3f51b5);opacity:.6;transform:scale(.8);width:32px;height:32px;line-height:32px;padding:0}.decorator-btn:hover{opacity:1}.icon-picker-btn,.hint-btn,.hint-btn.has-icon-picker{right:auto}.editor-row .mat-mdc-form-field-subscript-wrapper mat-hint{display:none!important}.editor-row .mat-mdc-form-field-subscript-wrapper mat-error{display:block!important}praxis-field-shell[data-field-name=showCharCount] .mdc-switch{transform:scale(1.05)}praxis-field-shell[data-field-name=showCharCount] .mat-mdc-slide-toggle{font-weight:600}praxis-field-shell[data-field-name=controlType] .mat-mdc-form-field-hint{display:flex;align-items:center;gap:8px;background:var(--md-sys-color-tertiary-container);border:1px solid var(--md-sys-color-tertiary);color:var(--md-sys-color-on-tertiary-container);padding:8px 10px;border-radius:6px;line-height:1.3;white-space:normal}praxis-field-shell[data-field-name=controlType] .mat-mdc-form-field-hint:before{content:\"\\26a0\\fe0f\";display:inline-block}praxis-field-shell[data-field-name=controlType] .mat-mdc-form-field-subscript-wrapper{margin-top:6px;margin-bottom:10px;min-height:auto}praxis-field-shell[data-field-name=controlType] .mat-mdc-form-field{margin-bottom:16px}@media(max-width:520px){.editor-row,.editor-row.row-inline{gap:8px}.editor-item{flex-direction:column;gap:2px}.editor-row.row-inline>praxis-field-shell,.editor-row.row-2>praxis-field-shell,.editor-row.row-3>praxis-field-shell{flex:1 1 100%;min-width:0}.actions-container{width:100%;justify-content:flex-end;margin-top:0}}\n"] }]
|
|
2340
|
+
}], ctorParameters: () => [{ type: i1$1.MatDialog }, { type: i2$2.IconPickerService }, { type: i2$2.PraxisI18nService, decorators: [{
|
|
2341
|
+
type: Optional
|
|
2342
|
+
}] }], propDecorators: { properties: [{
|
|
860
2343
|
type: Input
|
|
861
2344
|
}], form: [{
|
|
862
2345
|
type: Input
|
|
863
2346
|
}] } });
|
|
864
2347
|
|
|
865
|
-
const METADATA_EDITOR_EN_US = {
|
|
866
|
-
'praxis.metadataEditor.arrayFields.section': 'Collection subfields',
|
|
867
|
-
'praxis.metadataEditor.arrayFields.title': 'Subfields',
|
|
868
|
-
'praxis.metadataEditor.arrayFields.add': 'Add subfield',
|
|
869
|
-
'praxis.metadataEditor.arrayFields.duplicateNames': 'There are duplicate names: {{names}}',
|
|
870
|
-
'praxis.metadataEditor.arrayFields.empty': 'No local subfields configured.',
|
|
871
|
-
'praxis.metadataEditor.arrayFields.name': 'Name',
|
|
872
|
-
'praxis.metadataEditor.arrayFields.nameRequired': 'Enter the subfield name.',
|
|
873
|
-
'praxis.metadataEditor.arrayFields.nameDuplicated': 'This name is already used in the collection.',
|
|
874
|
-
'praxis.metadataEditor.arrayFields.label': 'Label',
|
|
875
|
-
'praxis.metadataEditor.arrayFields.controlType': 'Control',
|
|
876
|
-
'praxis.metadataEditor.arrayFields.required': 'Required',
|
|
877
|
-
'praxis.metadataEditor.arrayFields.moveUp': 'Move up',
|
|
878
|
-
'praxis.metadataEditor.arrayFields.moveDown': 'Move down',
|
|
879
|
-
'praxis.metadataEditor.arrayFields.duplicate': 'Duplicate',
|
|
880
|
-
'praxis.metadataEditor.arrayFields.remove': 'Remove',
|
|
881
|
-
'praxis.metadataEditor.arrayFields.removeBlocked': 'Remove references before deleting: {{references}}',
|
|
882
|
-
'praxis.metadataEditor.arrayFields.refOnly': 'The item schema comes from itemSchemaRef; create local subfields only when you need an override.',
|
|
883
|
-
'praxis.metadataEditor.arrayFields.overrideLocal': 'There is an itemSchemaRef and local subfields; local subfields work as an explicit override.',
|
|
884
|
-
'praxis.metadataEditor.arrayFields.imported': 'Subfields imported. Review them before applying or saving.',
|
|
885
|
-
'praxis.metadataEditor.arrayFields.localOnly': 'Local subfields will be saved in array.itemSchema.fields.',
|
|
886
|
-
'praxis.metadataEditor.arrayFields.control.input': 'Text',
|
|
887
|
-
'praxis.metadataEditor.arrayFields.control.textarea': 'Long text',
|
|
888
|
-
'praxis.metadataEditor.arrayFields.control.numeric': 'Number',
|
|
889
|
-
'praxis.metadataEditor.arrayFields.control.currency': 'Currency',
|
|
890
|
-
'praxis.metadataEditor.arrayFields.control.email': 'Email',
|
|
891
|
-
'praxis.metadataEditor.arrayFields.control.phone': 'Phone',
|
|
892
|
-
'praxis.metadataEditor.arrayFields.control.select': 'Select',
|
|
893
|
-
'praxis.metadataEditor.arrayFields.control.entityLookup': 'Entity lookup',
|
|
894
|
-
'praxis.metadataEditor.arrayFields.control.toggle': 'Toggle',
|
|
895
|
-
'praxis.metadataEditor.arrayFields.control.date': 'Date',
|
|
896
|
-
'praxis.metadataEditor.fieldResource.label': 'Governed resource',
|
|
897
|
-
'praxis.metadataEditor.fieldResource.searchPlaceholder': 'Search by name, domain, or path',
|
|
898
|
-
'praxis.metadataEditor.fieldResource.hint': 'Select from the host governed catalog. The saved value remains resourcePath.',
|
|
899
|
-
};
|
|
900
|
-
|
|
901
|
-
const METADATA_EDITOR_PT_BR = {
|
|
902
|
-
'praxis.metadataEditor.arrayFields.section': 'Subcampos da cole\u00e7\u00e3o',
|
|
903
|
-
'praxis.metadataEditor.arrayFields.title': 'Subcampos',
|
|
904
|
-
'praxis.metadataEditor.arrayFields.add': 'Adicionar subcampo',
|
|
905
|
-
'praxis.metadataEditor.arrayFields.duplicateNames': 'Existem nomes duplicados: {{names}}',
|
|
906
|
-
'praxis.metadataEditor.arrayFields.empty': 'Nenhum subcampo local configurado.',
|
|
907
|
-
'praxis.metadataEditor.arrayFields.name': 'Nome',
|
|
908
|
-
'praxis.metadataEditor.arrayFields.nameRequired': 'Informe o nome do subcampo.',
|
|
909
|
-
'praxis.metadataEditor.arrayFields.nameDuplicated': 'Este nome j\u00e1 est\u00e1 em uso na cole\u00e7\u00e3o.',
|
|
910
|
-
'praxis.metadataEditor.arrayFields.label': 'Label',
|
|
911
|
-
'praxis.metadataEditor.arrayFields.controlType': 'Controle',
|
|
912
|
-
'praxis.metadataEditor.arrayFields.required': 'Obrigat\u00f3rio',
|
|
913
|
-
'praxis.metadataEditor.arrayFields.moveUp': 'Subir',
|
|
914
|
-
'praxis.metadataEditor.arrayFields.moveDown': 'Descer',
|
|
915
|
-
'praxis.metadataEditor.arrayFields.duplicate': 'Duplicar',
|
|
916
|
-
'praxis.metadataEditor.arrayFields.remove': 'Remover',
|
|
917
|
-
'praxis.metadataEditor.arrayFields.removeBlocked': 'Remova as refer\u00eancias antes de excluir: {{references}}',
|
|
918
|
-
'praxis.metadataEditor.arrayFields.refOnly': 'O schema do item vem do itemSchemaRef; crie subcampos locais apenas quando precisar de override.',
|
|
919
|
-
'praxis.metadataEditor.arrayFields.overrideLocal': 'H\u00e1 um itemSchemaRef e subcampos locais; os subcampos locais funcionam como override expl\u00edcito.',
|
|
920
|
-
'praxis.metadataEditor.arrayFields.imported': 'Subcampos importados. Revise antes de aplicar ou salvar.',
|
|
921
|
-
'praxis.metadataEditor.arrayFields.localOnly': 'Subcampos locais ser\u00e3o salvos em array.itemSchema.fields.',
|
|
922
|
-
'praxis.metadataEditor.arrayFields.control.input': 'Texto',
|
|
923
|
-
'praxis.metadataEditor.arrayFields.control.textarea': 'Texto longo',
|
|
924
|
-
'praxis.metadataEditor.arrayFields.control.numeric': 'N\u00famero',
|
|
925
|
-
'praxis.metadataEditor.arrayFields.control.currency': 'Moeda',
|
|
926
|
-
'praxis.metadataEditor.arrayFields.control.email': 'E-mail',
|
|
927
|
-
'praxis.metadataEditor.arrayFields.control.phone': 'Telefone',
|
|
928
|
-
'praxis.metadataEditor.arrayFields.control.select': 'Sele\u00e7\u00e3o',
|
|
929
|
-
'praxis.metadataEditor.arrayFields.control.entityLookup': 'Busca de entidade',
|
|
930
|
-
'praxis.metadataEditor.arrayFields.control.toggle': 'Liga/desliga',
|
|
931
|
-
'praxis.metadataEditor.arrayFields.control.date': 'Data',
|
|
932
|
-
'praxis.metadataEditor.fieldResource.label': 'Recurso governado',
|
|
933
|
-
'praxis.metadataEditor.fieldResource.searchPlaceholder': 'Buscar por nome, dom\u00ednio ou caminho',
|
|
934
|
-
'praxis.metadataEditor.fieldResource.hint': 'Selecione no cat\u00e1logo governado do host. O valor gravado continua sendo resourcePath.',
|
|
935
|
-
};
|
|
936
|
-
|
|
937
|
-
function createPraxisMetadataEditorI18nConfig(options = {}) {
|
|
938
|
-
const dictionaries = {
|
|
939
|
-
'pt-BR': {
|
|
940
|
-
...METADATA_EDITOR_PT_BR,
|
|
941
|
-
...(options.dictionaries?.['pt-BR'] ?? {}),
|
|
942
|
-
},
|
|
943
|
-
'en-US': {
|
|
944
|
-
...METADATA_EDITOR_EN_US,
|
|
945
|
-
...(options.dictionaries?.['en-US'] ?? {}),
|
|
946
|
-
},
|
|
947
|
-
};
|
|
948
|
-
for (const [locale, dictionary] of Object.entries(options.dictionaries ?? {})) {
|
|
949
|
-
if (locale === 'pt-BR' || locale === 'en-US') {
|
|
950
|
-
continue;
|
|
951
|
-
}
|
|
952
|
-
dictionaries[locale] = {
|
|
953
|
-
...(dictionaries[locale] ?? {}),
|
|
954
|
-
...dictionary,
|
|
955
|
-
};
|
|
956
|
-
}
|
|
957
|
-
return {
|
|
958
|
-
locale: options.locale,
|
|
959
|
-
fallbackLocale: options.fallbackLocale ?? 'pt-BR',
|
|
960
|
-
dictionaries,
|
|
961
|
-
};
|
|
962
|
-
}
|
|
963
|
-
const METADATA_EDITOR_I18N_CONFIG = createPraxisMetadataEditorI18nConfig();
|
|
964
|
-
function providePraxisMetadataEditorI18n(options = {}) {
|
|
965
|
-
return providePraxisI18n(createPraxisMetadataEditorI18nConfig(options));
|
|
966
|
-
}
|
|
967
|
-
function resolvePraxisMetadataEditorText(i18n, key, fallback) {
|
|
968
|
-
const namespacedKey = key.startsWith('praxis.metadataEditor.')
|
|
969
|
-
? key
|
|
970
|
-
: `praxis.metadataEditor.${key}`;
|
|
971
|
-
return i18n.t(namespacedKey, undefined, fallback) || fallback;
|
|
972
|
-
}
|
|
973
|
-
|
|
974
2348
|
class ArrayItemSchemaFieldsEditorComponent {
|
|
975
2349
|
static nextInstanceId = 0;
|
|
976
2350
|
control = null;
|
|
@@ -3798,25 +5172,30 @@ const dateRangeProperties = [
|
|
|
3798
5172
|
{ name: 'showShortcuts', label: 'Habilitar atalhos', editorType: 'checkbox', group: 'Atalhos', hint: 'Exibe painel lateral com presets de datas.' },
|
|
3799
5173
|
{
|
|
3800
5174
|
name: 'shortcutsPosition', label: 'Posição do painel', editorType: 'select', group: 'Atalhos', options: [
|
|
5175
|
+
{ value: 'auto', text: 'Automática' },
|
|
5176
|
+
{ value: 'below', text: 'Abaixo' },
|
|
3801
5177
|
{ value: 'left', text: 'Esquerda' },
|
|
3802
5178
|
{ value: 'right', text: 'Direita' },
|
|
3803
|
-
], hint: 'Escolhe
|
|
5179
|
+
], hint: 'Escolhe a posição preferencial do overlay externo de atalhos do dateRange.'
|
|
3804
5180
|
},
|
|
3805
5181
|
{ name: 'applyOnShortcutClick', label: 'Aplicar ao clicar', editorType: 'checkbox', group: 'Atalhos', hint: 'Compatibilidade legada para atalhos do date-range base. No inlineDateRange, prefira inlineQuickPresetsApplyMode.' },
|
|
3806
5182
|
{
|
|
3807
|
-
name: 'shortcuts',
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
{ value: 'thisMonth', text: 'Este mês' },
|
|
3813
|
-
{ value: 'lastMonth', text: 'Mês passado' },
|
|
3814
|
-
{ value: 'thisYear', text: 'Este ano' },
|
|
3815
|
-
{ value: 'lastYear', text: 'Ano passado' },
|
|
3816
|
-
], hint: 'Selecione quais presets mostrar no painel de atalhos.'
|
|
5183
|
+
name: 'shortcuts',
|
|
5184
|
+
label: 'Atalhos visíveis (JSON)',
|
|
5185
|
+
editorType: 'textarea',
|
|
5186
|
+
group: 'Atalhos',
|
|
5187
|
+
hint: 'Array canônico com ids built-in e presets estáticos resolvidos pelo backend. Ex.: ["today", {"id":"periodo-votacao-2026","label":"Período de votação","startDate":"2026-07-06","endDate":"2026-10-04","timeZone":"America/Sao_Paulo","tone":"info"}]. Não informe calculateRange em JSON.'
|
|
3817
5188
|
},
|
|
3818
5189
|
{ name: 'inlineQuickPresets.enabled', label: 'Atalhos inline', editorType: 'checkbox', group: 'Atalhos', hint: 'Exibe chips rápidos ao lado do campo no modo inlineDateRange.' },
|
|
3819
5190
|
{ name: 'inlineQuickPresets.maxVisible', label: 'Qtd. chips inline', editorType: 'number', group: 'Atalhos', hint: 'Define o número máximo de atalhos inline visíveis (1 a 8).' },
|
|
5191
|
+
{
|
|
5192
|
+
name: 'inlineQuickPresets.position', label: 'Posição inline', editorType: 'select', group: 'Atalhos', options: [
|
|
5193
|
+
{ value: 'auto', text: 'Automática' },
|
|
5194
|
+
{ value: 'footer', text: 'Rodapé' },
|
|
5195
|
+
{ value: 'start', text: 'Início lógico' },
|
|
5196
|
+
{ value: 'end', text: 'Fim lógico' },
|
|
5197
|
+
], hint: 'Define se atalhos inline aparecem no rodapé, início lógico, fim lógico ou em fallback automático.'
|
|
5198
|
+
},
|
|
3820
5199
|
{ name: 'inlineQuickPresetsAriaLabel', label: 'ARIA atalhos inline', editorType: 'text', group: 'Atalhos', hint: 'Rótulo de acessibilidade do grupo de atalhos rápidos inline.' },
|
|
3821
5200
|
{
|
|
3822
5201
|
name: 'inlineOverlay.applyMode',
|
|
@@ -6038,6 +7417,140 @@ const fieldSubmitProperties = [
|
|
|
6038
7417
|
},
|
|
6039
7418
|
];
|
|
6040
7419
|
|
|
7420
|
+
const fieldPresentationProperties = [
|
|
7421
|
+
{
|
|
7422
|
+
name: 'valuePresentation',
|
|
7423
|
+
label: 'Formato do valor',
|
|
7424
|
+
labelKey: 'presentationFields.valuePresentation.label',
|
|
7425
|
+
editorType: 'textarea',
|
|
7426
|
+
group: 'Apresentação',
|
|
7427
|
+
groupKey: 'presentationFields.group',
|
|
7428
|
+
placeholder: '{\n "type": "currency",\n "currency": { "code": "BRL" }\n}',
|
|
7429
|
+
hint: 'Formatador canônico usado em superfícies readonly/presentation. Use um objeto JSON.',
|
|
7430
|
+
hintKey: 'presentationFields.valuePresentation.hint',
|
|
7431
|
+
},
|
|
7432
|
+
{
|
|
7433
|
+
name: 'presentation.presenter',
|
|
7434
|
+
label: 'Apresentador',
|
|
7435
|
+
labelKey: 'presentationFields.presenter.label',
|
|
7436
|
+
editorType: 'presentation-choice',
|
|
7437
|
+
group: 'Apresentação',
|
|
7438
|
+
groupKey: 'presentationFields.group',
|
|
7439
|
+
row: 'presentation.semantic',
|
|
7440
|
+
inline: true,
|
|
7441
|
+
options: [
|
|
7442
|
+
{ value: 'text', text: 'Texto', textKey: 'presentationFields.presenter.option.text' },
|
|
7443
|
+
{ value: 'status', text: 'Status', textKey: 'presentationFields.presenter.option.status' },
|
|
7444
|
+
{ value: 'chip', text: 'Chip', textKey: 'presentationFields.presenter.option.chip' },
|
|
7445
|
+
{ value: 'badge', text: 'Badge', textKey: 'presentationFields.presenter.option.badge' },
|
|
7446
|
+
{ value: 'iconValue', text: 'Ícone + valor', textKey: 'presentationFields.presenter.option.iconValue' },
|
|
7447
|
+
{ value: 'progress', text: 'Progresso', textKey: 'presentationFields.presenter.option.progress' },
|
|
7448
|
+
{ value: 'rating', text: 'Avaliação', textKey: 'presentationFields.presenter.option.rating' },
|
|
7449
|
+
],
|
|
7450
|
+
hint: 'Estratégia semântica para renderização em presentationMode.',
|
|
7451
|
+
hintKey: 'presentationFields.presenter.hint',
|
|
7452
|
+
},
|
|
7453
|
+
{
|
|
7454
|
+
name: 'presentation.tone',
|
|
7455
|
+
label: 'Tom',
|
|
7456
|
+
labelKey: 'presentationFields.tone.label',
|
|
7457
|
+
editorType: 'presentation-choice',
|
|
7458
|
+
group: 'Apresentação',
|
|
7459
|
+
groupKey: 'presentationFields.group',
|
|
7460
|
+
row: 'presentation.semantic',
|
|
7461
|
+
inline: true,
|
|
7462
|
+
options: [
|
|
7463
|
+
{ value: 'neutral', text: 'Neutro', textKey: 'presentationFields.tone.option.neutral' },
|
|
7464
|
+
{ value: 'info', text: 'Informação', textKey: 'presentationFields.tone.option.info' },
|
|
7465
|
+
{ value: 'success', text: 'Sucesso', textKey: 'presentationFields.tone.option.success' },
|
|
7466
|
+
{ value: 'warning', text: 'Atenção', textKey: 'presentationFields.tone.option.warning' },
|
|
7467
|
+
{ value: 'danger', text: 'Crítico', textKey: 'presentationFields.tone.option.danger' },
|
|
7468
|
+
],
|
|
7469
|
+
hint: 'Tom semântico mapeado pelo runtime para tokens de tema.',
|
|
7470
|
+
hintKey: 'presentationFields.tone.hint',
|
|
7471
|
+
},
|
|
7472
|
+
{
|
|
7473
|
+
name: 'presentation.appearance',
|
|
7474
|
+
label: 'Ênfase',
|
|
7475
|
+
labelKey: 'presentationFields.appearance.label',
|
|
7476
|
+
editorType: 'presentation-choice',
|
|
7477
|
+
group: 'Apresentação',
|
|
7478
|
+
groupKey: 'presentationFields.group',
|
|
7479
|
+
row: 'presentation.semantic',
|
|
7480
|
+
inline: true,
|
|
7481
|
+
options: [
|
|
7482
|
+
{ value: 'plain', text: 'Simples', textKey: 'presentationFields.appearance.option.plain' },
|
|
7483
|
+
{ value: 'soft', text: 'Suave', textKey: 'presentationFields.appearance.option.soft' },
|
|
7484
|
+
{ value: 'outlined', text: 'Contornado', textKey: 'presentationFields.appearance.option.outlined' },
|
|
7485
|
+
{ value: 'filled', text: 'Preenchido', textKey: 'presentationFields.appearance.option.filled' },
|
|
7486
|
+
],
|
|
7487
|
+
hint: 'Nível de ênfase visual do apresentador.',
|
|
7488
|
+
hintKey: 'presentationFields.appearance.hint',
|
|
7489
|
+
},
|
|
7490
|
+
{
|
|
7491
|
+
name: 'presentation.icon',
|
|
7492
|
+
label: 'Ícone',
|
|
7493
|
+
labelKey: 'presentationFields.icon.label',
|
|
7494
|
+
editorType: 'text',
|
|
7495
|
+
group: 'Apresentação',
|
|
7496
|
+
groupKey: 'presentationFields.group',
|
|
7497
|
+
row: 'presentation.content',
|
|
7498
|
+
inline: true,
|
|
7499
|
+
placeholder: 'schedule',
|
|
7500
|
+
hint: 'Nome do ícone Material Symbols, sem HTML.',
|
|
7501
|
+
hintKey: 'presentationFields.icon.hint',
|
|
7502
|
+
},
|
|
7503
|
+
{
|
|
7504
|
+
name: 'presentation.label',
|
|
7505
|
+
label: 'Rótulo',
|
|
7506
|
+
labelKey: 'presentationFields.label.label',
|
|
7507
|
+
editorType: 'text',
|
|
7508
|
+
group: 'Apresentação',
|
|
7509
|
+
groupKey: 'presentationFields.group',
|
|
7510
|
+
row: 'presentation.content',
|
|
7511
|
+
placeholder: 'Pendente',
|
|
7512
|
+
placeholderKey: 'presentationFields.label.placeholder',
|
|
7513
|
+
hint: 'Texto semântico exibido por status, chip ou badge.',
|
|
7514
|
+
hintKey: 'presentationFields.label.hint',
|
|
7515
|
+
},
|
|
7516
|
+
{
|
|
7517
|
+
name: 'presentation.badge',
|
|
7518
|
+
label: 'Badge',
|
|
7519
|
+
labelKey: 'presentationFields.badge.label',
|
|
7520
|
+
editorType: 'text',
|
|
7521
|
+
group: 'Apresentação',
|
|
7522
|
+
groupKey: 'presentationFields.group',
|
|
7523
|
+
row: 'presentation.content',
|
|
7524
|
+
inline: true,
|
|
7525
|
+
placeholder: 'SLA',
|
|
7526
|
+
hint: 'Marcador secundário opcional junto ao valor.',
|
|
7527
|
+
hintKey: 'presentationFields.badge.hint',
|
|
7528
|
+
},
|
|
7529
|
+
{
|
|
7530
|
+
name: 'presentation.tooltip',
|
|
7531
|
+
label: 'Tooltip',
|
|
7532
|
+
labelKey: 'presentationFields.tooltip.label',
|
|
7533
|
+
editorType: 'text',
|
|
7534
|
+
group: 'Apresentação',
|
|
7535
|
+
groupKey: 'presentationFields.group',
|
|
7536
|
+
placeholder: 'Aguardando aprovação fiscal',
|
|
7537
|
+
placeholderKey: 'presentationFields.tooltip.placeholder',
|
|
7538
|
+
hint: 'Explicação curta exibida como tooltip.',
|
|
7539
|
+
hintKey: 'presentationFields.tooltip.hint',
|
|
7540
|
+
},
|
|
7541
|
+
{
|
|
7542
|
+
name: 'presentationRules',
|
|
7543
|
+
label: 'Regras de apresentação',
|
|
7544
|
+
labelKey: 'presentationFields.presentationRules.label',
|
|
7545
|
+
editorType: 'presentation-rules-builder',
|
|
7546
|
+
group: 'Apresentação',
|
|
7547
|
+
groupKey: 'presentationFields.group',
|
|
7548
|
+
placeholder: '[\n {\n "when": { "===": [{ "var": "value" }, "BLOQUEADO"] },\n "set": { "tone": "danger", "icon": "lock", "label": "Bloqueado" }\n }\n]',
|
|
7549
|
+
hint: 'Builder guiado para regras Json Logic ordenadas que alteram somente a apresentação. Regras posteriores vencem.',
|
|
7550
|
+
hintKey: 'presentationFields.presentationRules.hint',
|
|
7551
|
+
},
|
|
7552
|
+
];
|
|
7553
|
+
|
|
6041
7554
|
/** Garante que minLength <= maxLength quando ambos definidos. */
|
|
6042
7555
|
function minLenLeMaxLen(group) {
|
|
6043
7556
|
const v = group.value || {};
|
|
@@ -8840,8 +10353,20 @@ class FieldMetadataEditorComponent {
|
|
|
8840
10353
|
patch.links = parsedLinks;
|
|
8841
10354
|
}
|
|
8842
10355
|
}
|
|
10356
|
+
if (typeof patch.shortcuts === 'string') {
|
|
10357
|
+
const parsedShortcuts = this.parseArrayTextarea(patch.shortcuts);
|
|
10358
|
+
if (parsedShortcuts) {
|
|
10359
|
+
patch.shortcuts = parsedShortcuts;
|
|
10360
|
+
}
|
|
10361
|
+
}
|
|
8843
10362
|
this.normalizeEntityLookupPatchValues(patch);
|
|
8844
10363
|
this.normalizeArrayPatchValues(patch);
|
|
10364
|
+
this.normalizePresentationPatchValues(patch);
|
|
10365
|
+
}
|
|
10366
|
+
normalizePresentationPatchValues(patch) {
|
|
10367
|
+
this.normalizeJsonObjectProperty(patch, 'valuePresentation');
|
|
10368
|
+
this.normalizeJsonObjectProperty(patch, 'presentation');
|
|
10369
|
+
this.normalizeJsonArrayProperty(patch, 'presentationRules');
|
|
8845
10370
|
}
|
|
8846
10371
|
normalizeEntityLookupPatchValues(patch) {
|
|
8847
10372
|
const optionSource = patch?.optionSource;
|
|
@@ -9033,7 +10558,8 @@ class FieldMetadataEditorComponent {
|
|
|
9033
10558
|
}
|
|
9034
10559
|
withSharedFieldMetadataProperties(props) {
|
|
9035
10560
|
const propNames = new Set(props.map((prop) => prop.name));
|
|
9036
|
-
const
|
|
10561
|
+
const sharedProps = [...fieldPresentationProperties, ...fieldSubmitProperties];
|
|
10562
|
+
const missingSharedProps = sharedProps.filter((prop) => !propNames.has(prop.name));
|
|
9037
10563
|
return missingSharedProps.length ? [...props, ...missingSharedProps] : props;
|
|
9038
10564
|
}
|
|
9039
10565
|
get isArrayEditor() {
|
|
@@ -9215,7 +10741,7 @@ class CascadeRulesService {
|
|
|
9215
10741
|
return map;
|
|
9216
10742
|
}
|
|
9217
10743
|
hydrateRule(field) {
|
|
9218
|
-
const deps = (field.dependencyFields ??
|
|
10744
|
+
const deps = this.normalizeDependencyFields(field.dependencyFields ??
|
|
9219
10745
|
field.optionSource?.dependsOn);
|
|
9220
10746
|
if (!deps || deps.length === 0)
|
|
9221
10747
|
return null;
|
|
@@ -9224,7 +10750,7 @@ class CascadeRulesService {
|
|
|
9224
10750
|
null;
|
|
9225
10751
|
const vm = {
|
|
9226
10752
|
targetField: field.name,
|
|
9227
|
-
dependencyFields:
|
|
10753
|
+
dependencyFields: deps,
|
|
9228
10754
|
enableDependencyCascade: field.enableDependencyCascade === false ? false : true,
|
|
9229
10755
|
resetOnDependentChange: !!field.resetOnDependentChange,
|
|
9230
10756
|
dependencyFilterMap,
|
|
@@ -9253,6 +10779,18 @@ class CascadeRulesService {
|
|
|
9253
10779
|
patch.dependencyDebounceMs = Number(rule.dependencyDebounceMs);
|
|
9254
10780
|
return patch;
|
|
9255
10781
|
}
|
|
10782
|
+
clearPatch() {
|
|
10783
|
+
return {
|
|
10784
|
+
dependencyFields: [],
|
|
10785
|
+
enableDependencyCascade: false,
|
|
10786
|
+
resetOnDependentChange: false,
|
|
10787
|
+
dependencyFilterMap: null,
|
|
10788
|
+
dependencyValuePath: null,
|
|
10789
|
+
dependencyMergeStrategy: null,
|
|
10790
|
+
dependencyDebounceMs: null,
|
|
10791
|
+
dependencyLoadOnChange: null,
|
|
10792
|
+
};
|
|
10793
|
+
}
|
|
9256
10794
|
/** Suggest a basic mapping dep->dep and a default valuePath */
|
|
9257
10795
|
suggest(rule, inferredValueKey) {
|
|
9258
10796
|
const map = {};
|
|
@@ -9263,6 +10801,20 @@ class CascadeRulesService {
|
|
|
9263
10801
|
dependencyValuePath: rule.dependencyValuePath || inferredValueKey || 'id',
|
|
9264
10802
|
};
|
|
9265
10803
|
}
|
|
10804
|
+
normalizeDependencyFields(value) {
|
|
10805
|
+
if (Array.isArray(value)) {
|
|
10806
|
+
const deps = value
|
|
10807
|
+
.filter((item) => typeof item === 'string')
|
|
10808
|
+
.map((item) => item.trim())
|
|
10809
|
+
.filter(Boolean);
|
|
10810
|
+
return deps.length ? deps : null;
|
|
10811
|
+
}
|
|
10812
|
+
if (typeof value === 'string') {
|
|
10813
|
+
const trimmed = value.trim();
|
|
10814
|
+
return trimmed ? [trimmed] : null;
|
|
10815
|
+
}
|
|
10816
|
+
return null;
|
|
10817
|
+
}
|
|
9266
10818
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: CascadeRulesService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
9267
10819
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: CascadeRulesService, providedIn: 'root' });
|
|
9268
10820
|
}
|
|
@@ -9299,7 +10851,6 @@ class CascadeManagerTabComponent {
|
|
|
9299
10851
|
this.svc = svc;
|
|
9300
10852
|
}
|
|
9301
10853
|
ngOnInit() {
|
|
9302
|
-
console.log('CascadeManagerTabComponent ngOnInit', this.fields);
|
|
9303
10854
|
// hydrate from input fields
|
|
9304
10855
|
const map = new Map();
|
|
9305
10856
|
for (const f of this.fields) {
|
|
@@ -9314,7 +10865,6 @@ class CascadeManagerTabComponent {
|
|
|
9314
10865
|
}
|
|
9315
10866
|
}
|
|
9316
10867
|
ngOnChanges(changes) {
|
|
9317
|
-
console.log('CascadeManagerTabComponent ngOnChanges', changes);
|
|
9318
10868
|
if (changes['fields'] && !changes['fields'].firstChange) {
|
|
9319
10869
|
// Re-hydrate rules from incoming fields while preserving existing ones when possible
|
|
9320
10870
|
const incoming = this.fields || [];
|
|
@@ -9446,18 +10996,8 @@ class CascadeManagerTabComponent {
|
|
|
9446
10996
|
const next = new Map(this.rules());
|
|
9447
10997
|
next.delete(name);
|
|
9448
10998
|
this.rules.set(next);
|
|
9449
|
-
// Removing means clearing the cascade-related keys; send empty patch
|
|
9450
10999
|
const patch = {
|
|
9451
|
-
[name]:
|
|
9452
|
-
dependencyFields: [],
|
|
9453
|
-
enableDependencyCascade: false,
|
|
9454
|
-
resetOnDependentChange: false,
|
|
9455
|
-
dependencyFilterMap: undefined,
|
|
9456
|
-
dependencyValuePath: undefined,
|
|
9457
|
-
dependencyMergeStrategy: undefined,
|
|
9458
|
-
dependencyDebounceMs: undefined,
|
|
9459
|
-
dependencyLoadOnChange: undefined,
|
|
9460
|
-
},
|
|
11000
|
+
[name]: this.svc.clearPatch(),
|
|
9461
11001
|
};
|
|
9462
11002
|
this.apply.emit(patch);
|
|
9463
11003
|
}
|
|
@@ -9934,7 +11474,7 @@ class CascadeManagerTabComponent {
|
|
|
9934
11474
|
</div>
|
|
9935
11475
|
}
|
|
9936
11476
|
</div>
|
|
9937
|
-
`, isInline: true, styles: [":host{display:block}.cm-root{display:grid;grid-template-columns:320px 1fr;gap:16px;min-height:420px}.cm-left{border-right:1px solid var(--md-sys-color-outline-variant);padding-right:8px}.cm-search{width:100%;margin-bottom:8px}.cm-right{padding-left:8px}.field-item{display:flex;align-items:center;justify-content:space-between;padding:8px 6px;cursor:pointer;border-radius:10px;transition:background .12s ease,border .12s ease;border:1px solid transparent}.field-item:hover{background:var(--md-sys-color-surface-container-high)}.field-item.selected{background:var(--md-sys-color-surface-container-highest);border-color:var(--md-sys-color-outline-variant)}.rule-badges{display:inline-flex;gap:6px}.rule-badge{font-size:11px;padding:2px 8px;border-radius:999px;font-weight:600}.b-active{background:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container)}.b-manual{background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-on-surface-variant)}.b-immediate{background:var(--md-sys-color-secondary-container);color:var(--md-sys-color-on-secondary-container)}.b-conflict{background:var(--md-sys-color-error-container);color:var(--md-sys-color-on-error-container)}.cm-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}.rules-empty{opacity:.8;font-style:italic;padding:8px 0;color:var(--md-sys-color-on-surface-variant)}.rule-row{display:grid;grid-template-columns:1fr auto;align-items:center;padding:10px 0;border-bottom:1px dashed var(--md-sys-color-outline-variant)}.rule-actions button{margin-left:4px}.banner{background:var(--md-sys-color-tertiary-container);color:var(--md-sys-color-on-tertiary-container);padding:10px 12px;border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;margin-bottom:10px;display:flex;gap:8px;align-items:center}.import-panel{background:var(--md-sys-color-surface-container-high);border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;padding:10px;margin-bottom:10px}.full{width:100%}.form-row{display:flex;gap:12px;align-items:center;flex-wrap:wrap}.form-grid{display:grid;gap:12px;padding:8px 0}.map-table{width:100%;border-collapse:collapse}.map-table th,.map-table td{padding:8px;border-bottom:1px dashed var(--md-sys-color-outline-variant)}.kbd{font-family:monospace;background:var(--md-sys-color-surface-container-highest);padding:0 6px;border-radius:4px;color:var(--md-sys-color-on-surface-variant)}.panel-surface{background:var(--md-sys-color-surface-container-high);border:1px solid var(--md-sys-color-outline-variant);border-radius:10px;box-shadow:var(--md-sys-elevation-level1, none);margin-bottom:12px}.panel-stack{display:grid;gap:12px;margin:12px 0}.stack-panel{background:var(--md-sys-color-surface-container-high);border:1px solid var(--md-sys-color-outline-variant);border-radius:10px;box-shadow:var(--md-sys-elevation-level1, none)}.stack-panel .mat-expansion-panel-content{padding:0 16px 12px}.panel-header{display:flex;align-items:center;gap:8px}.preview-box{background:var(--md-sys-color-surface-container-low);border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;padding:8px;color:var(--md-sys-color-on-surface)}.help-icon-button{--mdc-icon-button-state-layer-size: 28px;--mdc-icon-button-icon-size: 18px;width:28px;height:28px;padding:0;display:inline-flex;align-items:center;justify-content:center;margin-right:-4px}.help-icon-button mat-icon{font-size:18px;width:18px;height:18px}.mat-mdc-form-field-icon-suffix{align-self:center}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i6.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i4.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i8.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i6$1.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i6$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i6$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i6$1.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatChipsModule }, { kind: "ngmodule", type: MatListModule }, { kind: "ngmodule", type: MatDividerModule }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i7.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i7.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i8$1.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "component", type: i5.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i5.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i5.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i10.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i10.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i10.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatAutocompleteModule }, { kind: "component", type: i11.MatAutocomplete, selector: "mat-autocomplete", inputs: ["aria-label", "aria-labelledby", "displayWith", "autoActiveFirstOption", "autoSelectActiveOption", "requireSelection", "panelWidth", "disableRipple", "class", "hideSingleSelectionIndicator"], outputs: ["optionSelected", "opened", "closed", "optionActivated"], exportAs: ["matAutocomplete"] }, { kind: "directive", type: i11.MatAutocompleteTrigger, selector: "input[matAutocomplete], textarea[matAutocomplete]", inputs: ["matAutocomplete", "matAutocompletePosition", "matAutocompleteConnectedTo", "autocomplete", "matAutocompleteDisabled"], exportAs: ["matAutocompleteTrigger"] }, { kind: "ngmodule", type: ScrollingModule }, { kind: "directive", type: i12.CdkFixedSizeVirtualScroll, selector: "cdk-virtual-scroll-viewport[itemSize]", inputs: ["itemSize", "minBufferPx", "maxBufferPx"] }, { kind: "directive", type: i12.CdkVirtualForOf, selector: "[cdkVirtualFor][cdkVirtualForOf]", inputs: ["cdkVirtualForOf", "cdkVirtualForTrackBy", "cdkVirtualForTemplate", "cdkVirtualForTemplateCacheSize"] }, { kind: "component", type: i12.CdkVirtualScrollViewport, selector: "cdk-virtual-scroll-viewport", inputs: ["orientation", "appendOnly"], outputs: ["scrolledIndexChange"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }] });
|
|
11477
|
+
`, isInline: true, styles: [":host{display:block}.cm-root{display:grid;grid-template-columns:320px 1fr;gap:16px;min-height:420px}.cm-left{border-right:1px solid var(--md-sys-color-outline-variant);padding-right:8px}.cm-search{width:100%;margin-bottom:8px}.cm-right{padding-left:8px}.field-item{display:flex;align-items:center;justify-content:space-between;padding:8px 6px;cursor:pointer;border-radius:10px;transition:background .12s ease,border .12s ease;border:1px solid transparent}.field-item:hover{background:var(--md-sys-color-surface-container-high)}.field-item.selected{background:var(--md-sys-color-surface-container-highest);border-color:var(--md-sys-color-outline-variant)}.rule-badges{display:inline-flex;gap:6px}.rule-badge{font-size:11px;padding:2px 8px;border-radius:999px;font-weight:600}.b-active{background:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container)}.b-manual{background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-on-surface-variant)}.b-immediate{background:var(--md-sys-color-secondary-container);color:var(--md-sys-color-on-secondary-container)}.b-conflict{background:var(--md-sys-color-error-container);color:var(--md-sys-color-on-error-container)}.cm-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}.rules-empty{opacity:.8;font-style:italic;padding:8px 0;color:var(--md-sys-color-on-surface-variant)}.rule-row{display:grid;grid-template-columns:1fr auto;align-items:center;padding:10px 0;border-bottom:1px dashed var(--md-sys-color-outline-variant)}.rule-actions button{margin-left:4px}.banner{background:var(--md-sys-color-tertiary-container);color:var(--md-sys-color-on-tertiary-container);padding:10px 12px;border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;margin-bottom:10px;display:flex;gap:8px;align-items:center}.import-panel{background:var(--md-sys-color-surface-container-high);border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;padding:10px;margin-bottom:10px}.full{width:100%}.form-row{display:flex;gap:12px;align-items:center;flex-wrap:wrap}.form-grid{display:grid;gap:12px;padding:8px 0}.map-table{width:100%;border-collapse:collapse}.map-table th,.map-table td{padding:8px;border-bottom:1px dashed var(--md-sys-color-outline-variant)}.kbd{font-family:monospace;background:var(--md-sys-color-surface-container-highest);padding:0 6px;border-radius:4px;color:var(--md-sys-color-on-surface-variant)}.panel-surface{background:var(--md-sys-color-surface-container-high);border:1px solid var(--md-sys-color-outline-variant);border-radius:10px;box-shadow:var(--md-sys-elevation-level1, none);margin-bottom:12px}.panel-stack{display:grid;gap:12px;margin:12px 0}.stack-panel{background:var(--md-sys-color-surface-container-high);border:1px solid var(--md-sys-color-outline-variant);border-radius:10px;box-shadow:var(--md-sys-elevation-level1, none)}.stack-panel .mat-expansion-panel-content{padding:0 16px 12px}.panel-header{display:flex;align-items:center;gap:8px}.preview-box{background:var(--md-sys-color-surface-container-low);border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;padding:8px;color:var(--md-sys-color-on-surface)}.help-icon-button{--mdc-icon-button-state-layer-size: 28px;--mdc-icon-button-icon-size: 18px;width:28px;height:28px;padding:0;display:inline-flex;align-items:center;justify-content:center;margin-right:-4px}.help-icon-button mat-icon{font-size:18px;width:18px;height:18px}.mat-mdc-form-field-icon-suffix{align-self:center}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i2$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatChipsModule }, { kind: "ngmodule", type: MatListModule }, { kind: "ngmodule", type: MatDividerModule }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i9.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "component", type: i5$1.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i5$1.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i5$1.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i11.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i11.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i11.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatAutocompleteModule }, { kind: "component", type: i12.MatAutocomplete, selector: "mat-autocomplete", inputs: ["aria-label", "aria-labelledby", "displayWith", "autoActiveFirstOption", "autoSelectActiveOption", "requireSelection", "panelWidth", "disableRipple", "class", "hideSingleSelectionIndicator"], outputs: ["optionSelected", "opened", "closed", "optionActivated"], exportAs: ["matAutocomplete"] }, { kind: "directive", type: i12.MatAutocompleteTrigger, selector: "input[matAutocomplete], textarea[matAutocomplete]", inputs: ["matAutocomplete", "matAutocompletePosition", "matAutocompleteConnectedTo", "autocomplete", "matAutocompleteDisabled"], exportAs: ["matAutocompleteTrigger"] }, { kind: "ngmodule", type: ScrollingModule }, { kind: "directive", type: i13.CdkFixedSizeVirtualScroll, selector: "cdk-virtual-scroll-viewport[itemSize]", inputs: ["itemSize", "minBufferPx", "maxBufferPx"] }, { kind: "directive", type: i13.CdkVirtualForOf, selector: "[cdkVirtualFor][cdkVirtualForOf]", inputs: ["cdkVirtualForOf", "cdkVirtualForTrackBy", "cdkVirtualForTemplate", "cdkVirtualForTemplateCacheSize"] }, { kind: "component", type: i13.CdkVirtualScrollViewport, selector: "cdk-virtual-scroll-viewport", inputs: ["orientation", "appendOnly"], outputs: ["scrolledIndexChange"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }] });
|
|
9938
11478
|
}
|
|
9939
11479
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: CascadeManagerTabComponent, decorators: [{
|
|
9940
11480
|
type: Component,
|
|
@@ -10288,6 +11828,86 @@ const BASE_CAPABILITIES = [
|
|
|
10288
11828
|
valueKind: 'object',
|
|
10289
11829
|
description: 'Root configuration payload for the metadata editor.',
|
|
10290
11830
|
},
|
|
11831
|
+
{
|
|
11832
|
+
path: 'seed.valuePresentation',
|
|
11833
|
+
category: 'transform',
|
|
11834
|
+
valueKind: 'object',
|
|
11835
|
+
description: 'Canonical readonly value formatter metadata edited on FieldMetadata.',
|
|
11836
|
+
safetyNotes: 'Use canonical valuePresentation objects only; do not encode CSS, HTML, commands, or display-only aliases.',
|
|
11837
|
+
},
|
|
11838
|
+
{
|
|
11839
|
+
path: 'seed.presentation',
|
|
11840
|
+
category: 'appearance',
|
|
11841
|
+
valueKind: 'object',
|
|
11842
|
+
description: 'Semantic presentationMode state for FieldMetadata, including presenter, tone, icon, label and badge intent.',
|
|
11843
|
+
safetyNotes: 'Presentation metadata must remain semantic and renderer-safe; do not include raw HTML or imperative behavior.',
|
|
11844
|
+
},
|
|
11845
|
+
{
|
|
11846
|
+
path: 'seed.presentation.presenter',
|
|
11847
|
+
category: 'appearance',
|
|
11848
|
+
valueKind: 'enum',
|
|
11849
|
+
allowedValues: ['text', 'badge', 'chip', 'status', 'iconValue', 'progress', 'rating'],
|
|
11850
|
+
description: 'Guided semantic presenter for presentationMode.',
|
|
11851
|
+
},
|
|
11852
|
+
{
|
|
11853
|
+
path: 'seed.presentation.tone',
|
|
11854
|
+
category: 'appearance',
|
|
11855
|
+
valueKind: 'enum',
|
|
11856
|
+
allowedValues: ['neutral', 'info', 'success', 'warning', 'danger'],
|
|
11857
|
+
description: 'Guided semantic tone for presentationMode.',
|
|
11858
|
+
},
|
|
11859
|
+
{
|
|
11860
|
+
path: 'seed.presentation.appearance',
|
|
11861
|
+
category: 'appearance',
|
|
11862
|
+
valueKind: 'enum',
|
|
11863
|
+
allowedValues: ['plain', 'soft', 'outlined', 'filled'],
|
|
11864
|
+
description: 'Guided visual emphasis for presentationMode.',
|
|
11865
|
+
},
|
|
11866
|
+
{
|
|
11867
|
+
path: 'seed.presentation.icon',
|
|
11868
|
+
category: 'appearance',
|
|
11869
|
+
valueKind: 'string',
|
|
11870
|
+
description: 'Material Symbols icon name used by semantic presentation.',
|
|
11871
|
+
},
|
|
11872
|
+
{
|
|
11873
|
+
path: 'seed.presentation.label',
|
|
11874
|
+
category: 'identity',
|
|
11875
|
+
valueKind: 'string',
|
|
11876
|
+
description: 'Semantic label shown by status, chip and badge presenters.',
|
|
11877
|
+
},
|
|
11878
|
+
{
|
|
11879
|
+
path: 'seed.presentation.badge',
|
|
11880
|
+
category: 'appearance',
|
|
11881
|
+
valueKind: 'string',
|
|
11882
|
+
description: 'Secondary badge marker shown with the presentation value.',
|
|
11883
|
+
},
|
|
11884
|
+
{
|
|
11885
|
+
path: 'seed.presentation.tooltip',
|
|
11886
|
+
category: 'accessibility',
|
|
11887
|
+
valueKind: 'string',
|
|
11888
|
+
description: 'Tooltip text for the presentation wrapper.',
|
|
11889
|
+
},
|
|
11890
|
+
{
|
|
11891
|
+
path: 'seed.presentationRules',
|
|
11892
|
+
category: 'behavior',
|
|
11893
|
+
valueKind: 'array',
|
|
11894
|
+
description: 'Ordered Json Logic presentation rules edited by a guided builder for common conditions and semantic visual effects.',
|
|
11895
|
+
safetyNotes: 'Rules may only affect tone, icon, label, badge, appearance and tooltip; they must not mutate submitted values or execute commands.',
|
|
11896
|
+
},
|
|
11897
|
+
{
|
|
11898
|
+
path: 'seed.shortcuts',
|
|
11899
|
+
category: 'behavior',
|
|
11900
|
+
valueKind: 'array',
|
|
11901
|
+
description: 'Canonical dateRange/inlineDateRange shortcut catalog with built-in ids and backend-resolved static business periods.',
|
|
11902
|
+
safetyNotes: 'Use dateRangeShortcuts.configure; JSON metadata must not contain calculateRange, executable expressions, fiscal/legal/electoral rules, holiday rules, or business-day counting logic.',
|
|
11903
|
+
},
|
|
11904
|
+
{
|
|
11905
|
+
path: 'seed.inlineQuickPresets.position',
|
|
11906
|
+
category: 'layout',
|
|
11907
|
+
valueKind: 'enum',
|
|
11908
|
+
allowedValues: ['auto', 'footer', 'start', 'end'],
|
|
11909
|
+
description: 'Logical placement for inline date range quick presets, with runtime fallback for narrow/touch layouts.',
|
|
11910
|
+
},
|
|
10291
11911
|
];
|
|
10292
11912
|
const METADATA_EDITOR_AI_CAPABILITY_CATALOG = {
|
|
10293
11913
|
version: '1.0.0',
|
|
@@ -10320,6 +11940,16 @@ const fieldMetadataPropertySchema = {
|
|
|
10320
11940
|
'hint',
|
|
10321
11941
|
'helpText',
|
|
10322
11942
|
'tooltip',
|
|
11943
|
+
'valuePresentation',
|
|
11944
|
+
'presentation',
|
|
11945
|
+
'presentation.presenter',
|
|
11946
|
+
'presentation.tone',
|
|
11947
|
+
'presentation.appearance',
|
|
11948
|
+
'presentation.icon',
|
|
11949
|
+
'presentation.label',
|
|
11950
|
+
'presentation.badge',
|
|
11951
|
+
'presentation.tooltip',
|
|
11952
|
+
'presentationRules',
|
|
10323
11953
|
'options',
|
|
10324
11954
|
'optionSource',
|
|
10325
11955
|
'validators',
|
|
@@ -10395,6 +12025,48 @@ const cascadeSchema = {
|
|
|
10395
12025
|
},
|
|
10396
12026
|
},
|
|
10397
12027
|
};
|
|
12028
|
+
const staticDateRangeShortcutSchema = {
|
|
12029
|
+
type: 'object',
|
|
12030
|
+
required: ['id', 'label', 'startDate', 'endDate'],
|
|
12031
|
+
additionalProperties: false,
|
|
12032
|
+
properties: {
|
|
12033
|
+
id: { type: 'string', minLength: 1 },
|
|
12034
|
+
label: { type: 'string', minLength: 1 },
|
|
12035
|
+
startDate: { type: 'string', pattern: '^\\d{4}-\\d{2}-\\d{2}$' },
|
|
12036
|
+
endDate: { type: 'string', pattern: '^\\d{4}-\\d{2}-\\d{2}$' },
|
|
12037
|
+
timeZone: { type: 'string' },
|
|
12038
|
+
icon: { type: 'string' },
|
|
12039
|
+
description: { type: 'string' },
|
|
12040
|
+
tone: { enum: ['neutral', 'info', 'success', 'warning'] },
|
|
12041
|
+
effectiveFrom: { type: 'string', pattern: '^\\d{4}-\\d{2}-\\d{2}$' },
|
|
12042
|
+
effectiveTo: { type: 'string', pattern: '^\\d{4}-\\d{2}-\\d{2}$' },
|
|
12043
|
+
order: { type: 'number' },
|
|
12044
|
+
},
|
|
12045
|
+
};
|
|
12046
|
+
const dateRangeShortcutsSchema = {
|
|
12047
|
+
type: 'object',
|
|
12048
|
+
required: ['shortcuts'],
|
|
12049
|
+
properties: {
|
|
12050
|
+
shortcuts: {
|
|
12051
|
+
type: 'array',
|
|
12052
|
+
items: {
|
|
12053
|
+
anyOf: [
|
|
12054
|
+
{ enum: ['today', 'yesterday', 'thisWeek', 'lastWeek', 'thisMonth', 'lastMonth', 'thisYear', 'lastYear'] },
|
|
12055
|
+
staticDateRangeShortcutSchema,
|
|
12056
|
+
],
|
|
12057
|
+
},
|
|
12058
|
+
},
|
|
12059
|
+
inlineQuickPresets: {
|
|
12060
|
+
type: 'object',
|
|
12061
|
+
properties: {
|
|
12062
|
+
enabled: { type: 'boolean' },
|
|
12063
|
+
maxVisible: { type: 'number' },
|
|
12064
|
+
position: { enum: ['auto', 'footer', 'start', 'end'] },
|
|
12065
|
+
},
|
|
12066
|
+
},
|
|
12067
|
+
applyMode: { enum: ['auto', 'explicit'] },
|
|
12068
|
+
},
|
|
12069
|
+
};
|
|
10398
12070
|
const PRAXIS_METADATA_EDITOR_AUTHORING_MANIFEST = {
|
|
10399
12071
|
schemaVersion: '1.0.0',
|
|
10400
12072
|
componentId: 'praxis-metadata-editor',
|
|
@@ -10415,6 +12087,7 @@ const PRAXIS_METADATA_EDITOR_AUTHORING_MANIFEST = {
|
|
|
10415
12087
|
{ kind: 'cascade', resolver: 'metadata-editor-cascade-rules', description: 'Cascade dependencies and dependency filter mappings edited through CascadeManagerTab.' },
|
|
10416
12088
|
{ kind: 'renderer', resolver: 'metadata-editor-renderer-property', description: 'Dynamic editor renderer property coverage and editor component mapping.' },
|
|
10417
12089
|
{ kind: 'validation', resolver: 'field-metadata-validation-rules', description: 'Field validation rules and contextual validators.' },
|
|
12090
|
+
{ kind: 'dateRangeShortcuts', resolver: 'metadata-editor-date-range-shortcut-catalog', description: 'Governed static date range shortcut catalog for dateRange and inlineDateRange metadata.' },
|
|
10418
12091
|
{ kind: 'contextHint', resolver: 'metadata-editor-context-hints', description: 'Authoring hints, help text and contextual guidance for metadata editors.' },
|
|
10419
12092
|
{ kind: 'normalization', resolver: 'metadata-editor-schema-normalizer', description: 'Schema normalization and seed hydration rules.' },
|
|
10420
12093
|
],
|
|
@@ -10606,6 +12279,28 @@ const PRAXIS_METADATA_EDITOR_AUTHORING_MANIFEST = {
|
|
|
10606
12279
|
submissionImpact: 'visual-only',
|
|
10607
12280
|
preconditions: ['field-metadata-loaded'],
|
|
10608
12281
|
},
|
|
12282
|
+
{
|
|
12283
|
+
operationId: 'dateRangeShortcuts.configure',
|
|
12284
|
+
title: 'Configure governed date range shortcuts',
|
|
12285
|
+
scope: 'fieldMetadataPath',
|
|
12286
|
+
targetKind: 'dateRangeShortcuts',
|
|
12287
|
+
target: { kind: 'dateRangeShortcuts', resolver: 'metadata-editor-date-range-shortcut-catalog', ambiguityPolicy: 'fail', required: false },
|
|
12288
|
+
inputSchema: dateRangeShortcutsSchema,
|
|
12289
|
+
effects: [{ kind: 'compile-domain-patch', handler: 'metadata-date-range-shortcuts-configure', handlerContract: {
|
|
12290
|
+
reads: ['fieldMetadata.controlType', 'fieldMetadata.shortcuts', 'fieldMetadata.inlineQuickPresets', 'fieldMetadata.inlineOverlay', 'ConfigRegistryService'],
|
|
12291
|
+
writes: ['fieldMetadata.shortcuts', 'fieldMetadata.inlineQuickPresets', 'fieldMetadata.inlineOverlay.applyMode'],
|
|
12292
|
+
identityKeys: ['field.name', 'shortcuts[].id'],
|
|
12293
|
+
inputSchema: dateRangeShortcutsSchema,
|
|
12294
|
+
failureModes: ['control-type-not-date-range', 'duplicate-shortcut-id', 'invalid-static-date-range-shortcut', 'unsupported-shortcut-tone', 'executable-shortcut-metadata'],
|
|
12295
|
+
description: 'Configures built-in and backend-resolved static date range shortcuts through the canonical shortcuts array; JSON metadata may not contain calculateRange or executable expressions.',
|
|
12296
|
+
} }],
|
|
12297
|
+
destructive: false,
|
|
12298
|
+
requiresConfirmation: false,
|
|
12299
|
+
validators: ['date-range-shortcuts-static-canonical', 'date-range-shortcuts-no-executable-metadata', 'date-range-shortcuts-unique-ids', 'metadata-round-trip'],
|
|
12300
|
+
affectedPaths: ['fieldMetadata.shortcuts', 'fieldMetadata.inlineQuickPresets', 'fieldMetadata.inlineOverlay.applyMode'],
|
|
12301
|
+
submissionImpact: 'affects-schema-backed-data',
|
|
12302
|
+
preconditions: ['field-metadata-loaded', 'editor-properties-loaded'],
|
|
12303
|
+
},
|
|
10609
12304
|
{
|
|
10610
12305
|
operationId: 'normalization.apply',
|
|
10611
12306
|
title: 'Apply schema normalization',
|
|
@@ -10651,6 +12346,9 @@ const PRAXIS_METADATA_EDITOR_AUTHORING_MANIFEST = {
|
|
|
10651
12346
|
{ validatorId: 'context-validator-registered', level: 'error', code: 'METADATA_CONTEXT_VALIDATOR_REGISTERED', description: 'Contextual validator must be registered before use.' },
|
|
10652
12347
|
{ validatorId: 'context-hint-shape-canonical', level: 'error', code: 'METADATA_CONTEXT_HINT_CANONICAL', description: 'Context hints must remain structured metadata, not free-form host-only strings.' },
|
|
10653
12348
|
{ validatorId: 'context-hint-i18n-compatible', level: 'warning', code: 'METADATA_CONTEXT_HINT_I18N_COMPATIBLE', description: 'Authoring hint text must remain compatible with metadata-editor i18n.' },
|
|
12349
|
+
{ validatorId: 'date-range-shortcuts-static-canonical', level: 'error', code: 'METADATA_DATE_RANGE_SHORTCUTS_STATIC_CANONICAL', description: 'Date range shortcuts must be built-in ids or static backend-resolved ranges with id, label, startDate and endDate.' },
|
|
12350
|
+
{ validatorId: 'date-range-shortcuts-no-executable-metadata', level: 'error', code: 'METADATA_DATE_RANGE_SHORTCUTS_NO_EXECUTABLE_METADATA', description: 'Metadata shortcuts must not contain calculateRange, executable expressions, local calendar-law rules, or frontend business-day logic.' },
|
|
12351
|
+
{ validatorId: 'date-range-shortcuts-unique-ids', level: 'error', code: 'METADATA_DATE_RANGE_SHORTCUTS_UNIQUE_IDS', description: 'Static business shortcuts must use unique ids inside the date range field.' },
|
|
10654
12352
|
{ validatorId: 'normalization-preserves-canonical-fields', level: 'error', code: 'METADATA_NORMALIZATION_PRESERVES_FIELDS', description: 'Normalization must not drop canonical advanced FieldMetadata properties.' },
|
|
10655
12353
|
{ validatorId: 'runtime-editor-round-trip', level: 'error', code: 'METADATA_RUNTIME_EDITOR_ROUND_TRIP', description: 'Runtime and editor must consume the same normalized metadata shape.' },
|
|
10656
12354
|
{ validatorId: 'metadata-round-trip', level: 'error', code: 'METADATA_ROUND_TRIP', description: 'Open, edit, apply/save, reopen and runtime consume must preserve metadata.' },
|
|
@@ -10659,6 +12357,7 @@ const PRAXIS_METADATA_EDITOR_AUTHORING_MANIFEST = {
|
|
|
10659
12357
|
'FieldMetadata is the canonical edited document shape; backend x-ui-derived metadata must not be rewritten into host-only aliases.',
|
|
10660
12358
|
'Control type changes require both dynamic-fields discovery and metadata-editor visual coverage.',
|
|
10661
12359
|
'Option source cascades preserve x-ui optionSource.dependsOn and optionSource.dependencyFilterMap semantics.',
|
|
12360
|
+
'Date range business shortcuts are authored as built-in ids or backend-resolved static ranges; browser metadata never carries calculateRange code or business calendar rules.',
|
|
10662
12361
|
'JSON-only support is incomplete when visual editor coverage is required.',
|
|
10663
12362
|
'Schema normalization must preserve advanced canonical properties through open, edit, apply/save, reopen and runtime consumption.',
|
|
10664
12363
|
],
|
|
@@ -10670,6 +12369,8 @@ const PRAXIS_METADATA_EDITOR_AUTHORING_MANIFEST = {
|
|
|
10670
12369
|
{ id: 'configure-renderer', request: 'Expose placeholder in the visual editor as a text field.', operationId: 'renderer.configure', params: { propertyName: 'placeholder', editorType: 'text', group: 'Presentation' }, isPositive: true },
|
|
10671
12370
|
{ id: 'add-required-validation', request: 'Make this field required with a validation message.', operationId: 'validationRule.add', params: { rule: { type: 'required' }, message: 'Required field' }, isPositive: true },
|
|
10672
12371
|
{ id: 'set-context-hint', request: 'Add a contextual hint for sales users.', operationId: 'contextHint.set', params: { hintPath: 'helpText', value: 'Use the legal customer name.' }, isPositive: true },
|
|
12372
|
+
{ id: 'configure-date-range-business-shortcut', request: 'Add a fiscal competence period resolved by the backend to this inline date range.', operationId: 'dateRangeShortcuts.configure', params: { shortcuts: ['thisMonth', { id: 'competencia-fiscal-marco', label: 'Fiscal competence March', startDate: '2026-03-01', endDate: '2026-03-31', timeZone: 'America/Sao_Paulo', tone: 'success' }], inlineQuickPresets: { enabled: true, position: 'start', maxVisible: 4 }, applyMode: 'explicit' }, isPositive: true },
|
|
12373
|
+
{ id: 'reject-date-range-executable-shortcut', request: 'Add a date range shortcut that calculates fiscal holidays in calculateRange from JSON.', operationId: 'dateRangeShortcuts.configure', params: { shortcuts: [{ id: 'legal-days', label: 'Legal days', startDate: '2026-03-01', endDate: '2026-03-31', calculateRange: '() => legalCalendar()' }] }, isPositive: false },
|
|
10673
12374
|
{ id: 'normalize-seed', request: 'Normalize this imported field metadata while preserving advanced properties.', operationId: 'normalization.apply', params: { mode: 'preserve-advanced-properties', preserveUnknownCanonicalFields: true }, isPositive: true },
|
|
10674
12375
|
{ id: 'reject-unknown-control', request: 'Use an unknown control type named magic-picker.', operationId: 'controlType.set', params: { controlType: 'magic-picker' }, isPositive: false },
|
|
10675
12376
|
{ id: 'reject-json-only-coverage', request: 'Support this field only by editing raw JSON.', operationId: 'renderer.configure', params: { propertyName: 'optionSource', editorType: 'json' }, isPositive: false },
|