@praxisui/rich-content 9.0.0-beta.5 → 9.0.0-beta.7

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.
@@ -19,9 +19,16 @@ const PRAXIS_RICH_CONTENT_EN_US = {
19
19
  'praxis.richContent.editor.rootClass': 'Root class',
20
20
  'praxis.richContent.editor.rootClassPlaceholder': 'Example: employee-expansion-rich',
21
21
  'praxis.richContent.editor.document': 'Advanced JSON',
22
+ 'praxis.richContent.editor.documentJson': 'Canonical document JSON',
22
23
  'praxis.richContent.editor.documentHelp': 'Edit the canonical RichContentDocument. The inspector validates the shape and renders a preview before apply or save.',
24
+ 'praxis.richContent.editor.documentAdvancedHelp': 'Use JSON for diagnostics, migration and advanced fields that are not yet surfaced by guided controls.',
23
25
  'praxis.richContent.editor.blocks': 'Blocks',
24
26
  'praxis.richContent.editor.blocksHelp': 'Create and edit common top-level rich-content blocks. Use JSON for advanced nested structures.',
27
+ 'praxis.richContent.editor.structure': 'Document structure',
28
+ 'praxis.richContent.editor.structureHelp': 'Select a block to edit its properties.',
29
+ 'praxis.richContent.editor.properties': 'Properties',
30
+ 'praxis.richContent.editor.propertiesHelp': 'Edit the selected block without leaving the document structure.',
31
+ 'praxis.richContent.editor.hasIssues': 'Needs attention',
25
32
  'praxis.richContent.editor.block': 'Block',
26
33
  'praxis.richContent.editor.blockType': 'Block type',
27
34
  'praxis.richContent.editor.addBlock': 'Add block',
@@ -48,6 +55,7 @@ const PRAXIS_RICH_CONTENT_EN_US = {
48
55
  'praxis.richContent.editor.nodeType.text': 'Text',
49
56
  'praxis.richContent.editor.nodeType.badge': 'Badge',
50
57
  'praxis.richContent.editor.nodeType.icon': 'Icon',
58
+ 'praxis.richContent.editor.nodeType.avatar': 'Avatar',
51
59
  'praxis.richContent.editor.nodeType.image': 'Image',
52
60
  'praxis.richContent.editor.nodeType.link': 'Link',
53
61
  'praxis.richContent.editor.nodeType.metric': 'Metric',
@@ -85,9 +93,12 @@ const PRAXIS_RICH_CONTENT_EN_US = {
85
93
  'praxis.richContent.editor.field.icon': 'Icon',
86
94
  'praxis.richContent.editor.field.ariaLabel': 'Accessible label',
87
95
  'praxis.richContent.editor.field.src': 'Image URL',
96
+ 'praxis.richContent.editor.field.srcExpr': 'Image URL binding',
88
97
  'praxis.richContent.editor.field.href': 'Link URL',
89
98
  'praxis.richContent.editor.field.target': 'Target',
99
+ 'praxis.richContent.editor.field.rel': 'Rel',
90
100
  'praxis.richContent.editor.field.alt': 'Alternative text',
101
+ 'praxis.richContent.editor.field.altExpr': 'Alt text binding',
91
102
  'praxis.richContent.editor.field.testId': 'Test id',
92
103
  'praxis.richContent.editor.field.className': 'CSS class',
93
104
  'praxis.richContent.editor.field.visibleWhenPath': 'Context path',
@@ -97,15 +108,22 @@ const PRAXIS_RICH_CONTENT_EN_US = {
97
108
  'praxis.richContent.editor.field.valueExpr': 'Value binding',
98
109
  'praxis.richContent.editor.field.captionExpr': 'Caption binding',
99
110
  'praxis.richContent.editor.field.max': 'Maximum',
111
+ 'praxis.richContent.editor.field.showPercent': 'Show percent',
100
112
  'praxis.richContent.editor.field.direction': 'Direction',
101
113
  'praxis.richContent.editor.field.gap': 'Gap',
114
+ 'praxis.richContent.editor.field.wrap': 'Wrap items',
102
115
  'praxis.richContent.editor.field.avatarName': 'Avatar name',
103
116
  'praxis.richContent.editor.field.avatarImage': 'Avatar image',
117
+ 'praxis.richContent.editor.field.nameExpr': 'Name binding',
118
+ 'praxis.richContent.editor.field.imageSrcExpr': 'Image binding',
119
+ 'praxis.richContent.editor.field.initials': 'Initials',
120
+ 'praxis.richContent.editor.field.initialsExpr': 'Initials binding',
104
121
  'praxis.richContent.editor.field.emptyText': 'Empty text',
105
122
  'praxis.richContent.editor.field.errorText': 'Error text',
106
123
  'praxis.richContent.editor.field.meta': 'Meta',
107
124
  'praxis.richContent.editor.field.opposite': 'Opposite content',
108
125
  'praxis.richContent.editor.field.badge': 'Badge',
126
+ 'praxis.richContent.editor.field.badgeExpr': 'Badge binding',
109
127
  'praxis.richContent.editor.field.preset': 'Preset',
110
128
  'praxis.richContent.editor.field.title': 'Title',
111
129
  'praxis.richContent.editor.field.subtitle': 'Subtitle',
@@ -162,15 +180,37 @@ const PRAXIS_RICH_CONTENT_EN_US = {
162
180
  'praxis.richContent.editor.field.ctaLabel': 'CTA label',
163
181
  'praxis.richContent.editor.field.ctaIcon': 'CTA icon',
164
182
  'praxis.richContent.editor.field.size': 'Size',
183
+ 'praxis.richContent.editor.field.density': 'Density',
184
+ 'praxis.richContent.editor.field.loading': 'Loading',
185
+ 'praxis.richContent.editor.field.loadingExpr': 'Loading binding',
186
+ 'praxis.richContent.editor.field.active': 'Active',
187
+ 'praxis.richContent.editor.field.activeExpr': 'Active binding',
165
188
  'praxis.richContent.editor.field.mediaKind': 'Media',
166
189
  'praxis.richContent.editor.field.mediaSrc': 'Media source',
190
+ 'praxis.richContent.editor.field.mediaSrcExpr': 'Media source binding',
167
191
  'praxis.richContent.editor.field.mediaAlt': 'Media alt text',
192
+ 'praxis.richContent.editor.field.mediaAltExpr': 'Media alt binding',
168
193
  'praxis.richContent.editor.field.mediaIcon': 'Media icon',
194
+ 'praxis.richContent.editor.field.mediaLabel': 'Media label',
195
+ 'praxis.richContent.editor.field.mediaLabelExpr': 'Media label binding',
196
+ 'praxis.richContent.editor.field.aspectRatio': 'Aspect ratio',
169
197
  'praxis.richContent.editor.field.mediaPlacement': 'Media placement',
198
+ 'praxis.richContent.editor.field.interactionMode': 'Interaction mode',
199
+ 'praxis.richContent.editor.field.selected': 'Selected',
200
+ 'praxis.richContent.editor.field.selectedExpr': 'Selected binding',
201
+ 'praxis.richContent.editor.field.interactionActionId': 'Interaction action ID',
202
+ 'praxis.richContent.editor.field.accessibilityRole': 'Accessibility role',
203
+ 'praxis.richContent.editor.field.ariaLabelExpr': 'Accessible label binding',
204
+ 'praxis.richContent.editor.field.requiresCapabilities': 'Capabilities',
205
+ 'praxis.richContent.editor.field.capabilityMode': 'Capability mode',
170
206
  'praxis.richContent.editor.decisionGovernance': 'Decision governance',
171
207
  'praxis.richContent.editor.decisionEvidence': 'Evidence',
172
208
  'praxis.richContent.editor.decisionPrimaryAction': 'Primary action',
173
209
  'praxis.richContent.editor.decisionSecondaryActions': 'Secondary actions',
210
+ 'praxis.richContent.editor.addPrimaryAction': 'Add primary action',
211
+ 'praxis.richContent.editor.addSecondaryAction': 'Add secondary action',
212
+ 'praxis.richContent.editor.addAction': 'Add action',
213
+ 'praxis.richContent.editor.action': 'Action',
174
214
  'praxis.richContent.editor.addEvidence': 'Add evidence',
175
215
  'praxis.richContent.editor.evidence': 'Evidence',
176
216
  'praxis.richContent.editor.defaultEvidenceLabel': 'Evidence',
@@ -200,6 +240,20 @@ const PRAXIS_RICH_CONTENT_EN_US = {
200
240
  'praxis.richContent.editor.placeholder.supersedesExpr': '${decision.supersedes}',
201
241
  'praxis.richContent.editor.placeholder.evidenceLabelExpr': '${evidence.label}',
202
242
  'praxis.richContent.editor.placeholder.evidenceSourceExpr': '${evidence.source}',
243
+ 'praxis.richContent.editor.placeholder.tabLabelExpr': 'row.tabLabel',
244
+ 'praxis.richContent.editor.placeholder.badgeExpr': 'row.badge',
245
+ 'praxis.richContent.editor.placeholder.requiresCapabilities': 'form.mode.edit, employee.open',
246
+ 'praxis.richContent.editor.placeholder.srcExpr': 'row.imageUrl',
247
+ 'praxis.richContent.editor.placeholder.altExpr': 'row.imageAlt',
248
+ 'praxis.richContent.editor.placeholder.loadingExpr': 'row.loading',
249
+ 'praxis.richContent.editor.placeholder.activeExpr': 'row.active',
250
+ 'praxis.richContent.editor.placeholder.mediaSrcExpr': 'row.mediaUrl',
251
+ 'praxis.richContent.editor.placeholder.mediaAltExpr': 'row.mediaAlt',
252
+ 'praxis.richContent.editor.placeholder.mediaLabelExpr': 'row.mediaLabel',
253
+ 'praxis.richContent.editor.placeholder.aspectRatio': '16 / 9',
254
+ 'praxis.richContent.editor.placeholder.selectedExpr': 'row.selected',
255
+ 'praxis.richContent.editor.placeholder.actionId': 'host.action.open',
256
+ 'praxis.richContent.editor.placeholder.ariaLabelExpr': 'row.ariaLabel',
203
257
  'praxis.richContent.editor.cardVariant.outlined': 'Outlined',
204
258
  'praxis.richContent.editor.cardVariant.elevated': 'Elevated',
205
259
  'praxis.richContent.editor.cardVariant.filled': 'Filled',
@@ -208,8 +262,18 @@ const PRAXIS_RICH_CONTENT_EN_US = {
208
262
  'praxis.richContent.editor.size.sm': 'Small',
209
263
  'praxis.richContent.editor.size.md': 'Medium',
210
264
  'praxis.richContent.editor.size.lg': 'Large',
265
+ 'praxis.richContent.editor.density.compact': 'Compact',
266
+ 'praxis.richContent.editor.density.comfortable': 'Comfortable',
211
267
  'praxis.richContent.editor.orientation.vertical': 'Vertical',
212
268
  'praxis.richContent.editor.orientation.horizontal': 'Horizontal',
269
+ 'praxis.richContent.editor.interaction.none': 'None',
270
+ 'praxis.richContent.editor.interaction.action': 'Action',
271
+ 'praxis.richContent.editor.interaction.selectable': 'Selectable',
272
+ 'praxis.richContent.editor.accessibilityRole.article': 'Article',
273
+ 'praxis.richContent.editor.accessibilityRole.group': 'Group',
274
+ 'praxis.richContent.editor.accessibilityRole.button': 'Button',
275
+ 'praxis.richContent.editor.capabilityMode.all': 'All',
276
+ 'praxis.richContent.editor.capabilityMode.any': 'Any',
213
277
  'praxis.richContent.editor.mediaKind.image': 'Image',
214
278
  'praxis.richContent.editor.mediaKind.video': 'Video',
215
279
  'praxis.richContent.editor.mediaKind.icon': 'Icon',
@@ -287,6 +351,12 @@ const PRAXIS_RICH_CONTENT_EN_US = {
287
351
  'praxis.richContent.editor.placeholder.valueExpr': 'row.total',
288
352
  'praxis.richContent.editor.placeholder.captionExpr': 'row.caption',
289
353
  'praxis.richContent.editor.placeholder.progressExpr': 'row.progress',
354
+ 'praxis.richContent.editor.placeholder.nameExpr': 'row.name',
355
+ 'praxis.richContent.editor.placeholder.imageSrcExpr': 'row.avatarUrl',
356
+ 'praxis.richContent.editor.placeholder.initialsExpr': 'row.initials',
357
+ 'praxis.richContent.editor.placeholder.rel': 'noopener noreferrer',
358
+ 'praxis.richContent.editor.target.self': 'Same tab',
359
+ 'praxis.richContent.editor.target.blank': 'New tab',
290
360
  'praxis.richContent.editor.defaultText': 'Text',
291
361
  'praxis.richContent.editor.defaultBadge': 'Badge',
292
362
  'praxis.richContent.editor.defaultLink': 'Link',
@@ -327,13 +397,18 @@ const PRAXIS_RICH_CONTENT_EN_US = {
327
397
  'praxis.richContent.editor.defaultActionCardAction': 'Run action',
328
398
  'praxis.richContent.editor.defaultCollapsibleCardTitle': 'Advanced details',
329
399
  'praxis.richContent.editor.defaultCollapsibleCardText': 'Collapsible card content',
400
+ 'praxis.richContent.editor.cardActions': 'Card actions',
401
+ 'praxis.richContent.editor.calloutActions': 'Callout actions',
330
402
  'praxis.richContent.editor.ctaGroupActions': 'CTA actions',
403
+ 'praxis.richContent.editor.emptyStateActions': 'Empty state actions',
404
+ 'praxis.richContent.editor.recordSummaryActions': 'Summary actions',
331
405
  'praxis.richContent.editor.tabsItems': 'Tab items',
332
406
  'praxis.richContent.editor.lookupResultActions': 'Lookup actions',
333
407
  'praxis.richContent.editor.lookupCardSecondaryActions': 'Secondary actions',
334
408
  'praxis.richContent.editor.relatedRecordSecondaryActions': 'Secondary actions',
335
409
  'praxis.richContent.editor.actionCardSecondaryActions': 'Secondary actions',
336
410
  'praxis.richContent.editor.collapsibleCardActions': 'Card actions',
411
+ 'praxis.richContent.editor.accordionItemActions': 'Item actions',
337
412
  'praxis.richContent.editor.status.idle': 'Idle',
338
413
  'praxis.richContent.editor.status.resolved': 'Resolved',
339
414
  'praxis.richContent.editor.status.empty': 'Empty',
@@ -393,9 +468,16 @@ const PRAXIS_RICH_CONTENT_PT_BR = {
393
468
  'praxis.richContent.editor.rootClass': 'Classe raiz',
394
469
  'praxis.richContent.editor.rootClassPlaceholder': 'Exemplo: employee-expansion-rich',
395
470
  'praxis.richContent.editor.document': 'JSON avançado',
471
+ 'praxis.richContent.editor.documentJson': 'JSON do documento canônico',
396
472
  'praxis.richContent.editor.documentHelp': 'Edite o RichContentDocument canônico. O inspetor valida o formato e renderiza uma prévia antes de aplicar ou salvar.',
473
+ 'praxis.richContent.editor.documentAdvancedHelp': 'Use JSON para diagnóstico, migração e campos avançados que ainda não estão materializados nos controles guiados.',
397
474
  'praxis.richContent.editor.blocks': 'Blocos',
398
475
  'praxis.richContent.editor.blocksHelp': 'Crie e edite blocos comuns de nível superior de rich-content. Use JSON para estruturas aninhadas avançadas.',
476
+ 'praxis.richContent.editor.structure': 'Estrutura do documento',
477
+ 'praxis.richContent.editor.structureHelp': 'Selecione um bloco para editar suas propriedades.',
478
+ 'praxis.richContent.editor.properties': 'Propriedades',
479
+ 'praxis.richContent.editor.propertiesHelp': 'Edite o bloco selecionado sem sair da estrutura do documento.',
480
+ 'praxis.richContent.editor.hasIssues': 'Requer atenção',
399
481
  'praxis.richContent.editor.block': 'Bloco',
400
482
  'praxis.richContent.editor.blockType': 'Tipo de bloco',
401
483
  'praxis.richContent.editor.addBlock': 'Adicionar bloco',
@@ -422,6 +504,7 @@ const PRAXIS_RICH_CONTENT_PT_BR = {
422
504
  'praxis.richContent.editor.nodeType.text': 'Texto',
423
505
  'praxis.richContent.editor.nodeType.badge': 'Badge',
424
506
  'praxis.richContent.editor.nodeType.icon': 'Ícone',
507
+ 'praxis.richContent.editor.nodeType.avatar': 'Avatar',
425
508
  'praxis.richContent.editor.nodeType.image': 'Imagem',
426
509
  'praxis.richContent.editor.nodeType.link': 'Link',
427
510
  'praxis.richContent.editor.nodeType.metric': 'Métrica',
@@ -438,9 +521,12 @@ const PRAXIS_RICH_CONTENT_PT_BR = {
438
521
  'praxis.richContent.editor.field.icon': 'Ícone',
439
522
  'praxis.richContent.editor.field.ariaLabel': 'Label acessível',
440
523
  'praxis.richContent.editor.field.src': 'URL da imagem',
524
+ 'praxis.richContent.editor.field.srcExpr': 'Vínculo da URL da imagem',
441
525
  'praxis.richContent.editor.field.href': 'URL do link',
442
526
  'praxis.richContent.editor.field.target': 'Destino',
527
+ 'praxis.richContent.editor.field.rel': 'Rel',
443
528
  'praxis.richContent.editor.field.alt': 'Texto alternativo',
529
+ 'praxis.richContent.editor.field.altExpr': 'Vínculo do texto alternativo',
444
530
  'praxis.richContent.editor.field.testId': 'Id de teste',
445
531
  'praxis.richContent.editor.field.className': 'Classe CSS',
446
532
  'praxis.richContent.editor.field.visibleWhenPath': 'Path de contexto',
@@ -450,25 +536,50 @@ const PRAXIS_RICH_CONTENT_PT_BR = {
450
536
  'praxis.richContent.editor.field.valueExpr': 'Binding do valor',
451
537
  'praxis.richContent.editor.field.captionExpr': 'Binding da legenda',
452
538
  'praxis.richContent.editor.field.max': 'Máximo',
539
+ 'praxis.richContent.editor.field.showPercent': 'Mostrar percentual',
453
540
  'praxis.richContent.editor.field.direction': 'Direção',
454
541
  'praxis.richContent.editor.field.gap': 'Espaçamento',
542
+ 'praxis.richContent.editor.field.wrap': 'Quebrar itens',
455
543
  'praxis.richContent.editor.field.avatarName': 'Nome do avatar',
456
544
  'praxis.richContent.editor.field.avatarImage': 'Imagem do avatar',
545
+ 'praxis.richContent.editor.field.nameExpr': 'Vínculo do nome',
546
+ 'praxis.richContent.editor.field.imageSrcExpr': 'Vínculo da imagem',
547
+ 'praxis.richContent.editor.field.initials': 'Iniciais',
548
+ 'praxis.richContent.editor.field.initialsExpr': 'Vínculo das iniciais',
457
549
  'praxis.richContent.editor.field.emptyText': 'Texto vazio',
458
550
  'praxis.richContent.editor.field.errorText': 'Texto de erro',
459
551
  'praxis.richContent.editor.field.meta': 'Meta',
460
552
  'praxis.richContent.editor.field.opposite': 'Conteúdo oposto',
461
553
  'praxis.richContent.editor.field.badge': 'Badge',
554
+ 'praxis.richContent.editor.field.badgeExpr': 'Vínculo do badge',
462
555
  'praxis.richContent.editor.field.preset': 'Preset',
463
556
  'praxis.richContent.editor.field.title': 'Título',
464
557
  'praxis.richContent.editor.field.subtitle': 'Subtítulo',
465
558
  'praxis.richContent.editor.field.size': 'Tamanho',
559
+ 'praxis.richContent.editor.field.density': 'Densidade',
560
+ 'praxis.richContent.editor.field.loading': 'Carregando',
561
+ 'praxis.richContent.editor.field.loadingExpr': 'Vínculo de carregamento',
562
+ 'praxis.richContent.editor.field.active': 'Ativo',
563
+ 'praxis.richContent.editor.field.activeExpr': 'Vínculo de ativo',
466
564
  'praxis.richContent.editor.field.orientation': 'Orientação',
467
565
  'praxis.richContent.editor.field.mediaKind': 'Mídia',
468
566
  'praxis.richContent.editor.field.mediaSrc': 'Origem da mídia',
567
+ 'praxis.richContent.editor.field.mediaSrcExpr': 'Vínculo da origem da mídia',
469
568
  'praxis.richContent.editor.field.mediaAlt': 'Texto alternativo da mídia',
569
+ 'praxis.richContent.editor.field.mediaAltExpr': 'Vínculo do texto alternativo da mídia',
470
570
  'praxis.richContent.editor.field.mediaIcon': 'Ícone da mídia',
571
+ 'praxis.richContent.editor.field.mediaLabel': 'Label da mídia',
572
+ 'praxis.richContent.editor.field.mediaLabelExpr': 'Vínculo do label da mídia',
573
+ 'praxis.richContent.editor.field.aspectRatio': 'Proporção',
471
574
  'praxis.richContent.editor.field.mediaPlacement': 'Posição da mídia',
575
+ 'praxis.richContent.editor.field.interactionMode': 'Modo de interação',
576
+ 'praxis.richContent.editor.field.selected': 'Selecionado',
577
+ 'praxis.richContent.editor.field.selectedExpr': 'Vínculo de seleção',
578
+ 'praxis.richContent.editor.field.interactionActionId': 'Action ID da interação',
579
+ 'praxis.richContent.editor.field.accessibilityRole': 'Papel de acessibilidade',
580
+ 'praxis.richContent.editor.field.ariaLabelExpr': 'Vínculo do label acessível',
581
+ 'praxis.richContent.editor.field.requiresCapabilities': 'Capabilities',
582
+ 'praxis.richContent.editor.field.capabilityMode': 'Modo de capability',
472
583
  'praxis.richContent.editor.cardVariant.outlined': 'Com contorno',
473
584
  'praxis.richContent.editor.cardVariant.elevated': 'Elevado',
474
585
  'praxis.richContent.editor.cardVariant.filled': 'Preenchido',
@@ -477,8 +588,18 @@ const PRAXIS_RICH_CONTENT_PT_BR = {
477
588
  'praxis.richContent.editor.size.sm': 'Pequeno',
478
589
  'praxis.richContent.editor.size.md': 'Médio',
479
590
  'praxis.richContent.editor.size.lg': 'Grande',
591
+ 'praxis.richContent.editor.density.compact': 'Compacta',
592
+ 'praxis.richContent.editor.density.comfortable': 'Confortável',
480
593
  'praxis.richContent.editor.orientation.vertical': 'Vertical',
481
594
  'praxis.richContent.editor.orientation.horizontal': 'Horizontal',
595
+ 'praxis.richContent.editor.interaction.none': 'Nenhuma',
596
+ 'praxis.richContent.editor.interaction.action': 'Ação',
597
+ 'praxis.richContent.editor.interaction.selectable': 'Selecionável',
598
+ 'praxis.richContent.editor.accessibilityRole.article': 'Artigo',
599
+ 'praxis.richContent.editor.accessibilityRole.group': 'Grupo',
600
+ 'praxis.richContent.editor.accessibilityRole.button': 'Botão',
601
+ 'praxis.richContent.editor.capabilityMode.all': 'Todas',
602
+ 'praxis.richContent.editor.capabilityMode.any': 'Qualquer',
482
603
  'praxis.richContent.editor.mediaKind.image': 'Imagem',
483
604
  'praxis.richContent.editor.mediaKind.video': 'Vídeo',
484
605
  'praxis.richContent.editor.mediaKind.icon': 'Ícone',
@@ -501,6 +622,26 @@ const PRAXIS_RICH_CONTENT_PT_BR = {
501
622
  'praxis.richContent.editor.placeholder.valueExpr': 'row.total',
502
623
  'praxis.richContent.editor.placeholder.captionExpr': 'row.caption',
503
624
  'praxis.richContent.editor.placeholder.progressExpr': 'row.progress',
625
+ 'praxis.richContent.editor.placeholder.tabLabelExpr': 'row.tabLabel',
626
+ 'praxis.richContent.editor.placeholder.badgeExpr': 'row.badge',
627
+ 'praxis.richContent.editor.placeholder.requiresCapabilities': 'form.mode.edit, employee.open',
628
+ 'praxis.richContent.editor.placeholder.srcExpr': 'row.imageUrl',
629
+ 'praxis.richContent.editor.placeholder.altExpr': 'row.imageAlt',
630
+ 'praxis.richContent.editor.placeholder.loadingExpr': 'row.loading',
631
+ 'praxis.richContent.editor.placeholder.activeExpr': 'row.active',
632
+ 'praxis.richContent.editor.placeholder.mediaSrcExpr': 'row.mediaUrl',
633
+ 'praxis.richContent.editor.placeholder.mediaAltExpr': 'row.mediaAlt',
634
+ 'praxis.richContent.editor.placeholder.mediaLabelExpr': 'row.mediaLabel',
635
+ 'praxis.richContent.editor.placeholder.aspectRatio': '16 / 9',
636
+ 'praxis.richContent.editor.placeholder.selectedExpr': 'row.selected',
637
+ 'praxis.richContent.editor.placeholder.actionId': 'host.action.open',
638
+ 'praxis.richContent.editor.placeholder.ariaLabelExpr': 'row.ariaLabel',
639
+ 'praxis.richContent.editor.placeholder.nameExpr': 'row.name',
640
+ 'praxis.richContent.editor.placeholder.imageSrcExpr': 'row.avatarUrl',
641
+ 'praxis.richContent.editor.placeholder.initialsExpr': 'row.initials',
642
+ 'praxis.richContent.editor.placeholder.rel': 'noopener noreferrer',
643
+ 'praxis.richContent.editor.target.self': 'Mesma aba',
644
+ 'praxis.richContent.editor.target.blank': 'Nova aba',
504
645
  'praxis.richContent.editor.defaultText': 'Texto',
505
646
  'praxis.richContent.editor.defaultBadge': 'Badge',
506
647
  'praxis.richContent.editor.defaultLink': 'Link',
@@ -572,9 +713,9 @@ const PRAXIS_RICH_CONTENT_PT_BR = {
572
713
  'praxis.richContent.editor.nodeType.collapsibleCard': 'Card colapsável',
573
714
  'praxis.richContent.editor.nodeType.disclosure': 'Disclosure',
574
715
  'praxis.richContent.editor.nodeType.accordion': 'Accordion',
575
- 'praxis.richContent.editor.nodeType.formLauncher': 'Lançador de formulário',
716
+ 'praxis.richContent.editor.nodeType.formLauncher': 'Lançador de formulário',
576
717
  'praxis.richContent.editor.field.message': 'Mensagem',
577
- 'praxis.richContent.editor.field.description': 'Descrição',
718
+ 'praxis.richContent.editor.field.description': 'Descrição',
578
719
  'praxis.richContent.editor.field.status': 'Status',
579
720
  'praxis.richContent.editor.field.titleExpr': 'Vínculo do título',
580
721
  'praxis.richContent.editor.field.subtitleExpr': 'Vínculo do subtítulo',
@@ -617,11 +758,15 @@ const PRAXIS_RICH_CONTENT_PT_BR = {
617
758
  'praxis.richContent.editor.field.actionId': 'Action ID',
618
759
  'praxis.richContent.editor.field.payloadExpr': 'Binding do payload',
619
760
  'praxis.richContent.editor.field.availabilityExpr': 'Binding de disponibilidade',
620
- 'praxis.richContent.editor.field.confirmMessage': 'Mensagem de confirmação',
761
+ 'praxis.richContent.editor.field.confirmMessage': 'Mensagem de confirmação',
621
762
  'praxis.richContent.editor.decisionGovernance': 'Governança da decisão',
622
763
  'praxis.richContent.editor.decisionEvidence': 'Evidências',
623
764
  'praxis.richContent.editor.decisionPrimaryAction': 'Ação primária',
624
765
  'praxis.richContent.editor.decisionSecondaryActions': 'Ações secundárias',
766
+ 'praxis.richContent.editor.addPrimaryAction': 'Adicionar ação primária',
767
+ 'praxis.richContent.editor.addSecondaryAction': 'Adicionar ação secundária',
768
+ 'praxis.richContent.editor.addAction': 'Adicionar ação',
769
+ 'praxis.richContent.editor.action': 'Ação',
625
770
  'praxis.richContent.editor.addEvidence': 'Adicionar evidência',
626
771
  'praxis.richContent.editor.evidence': 'Evidência',
627
772
  'praxis.richContent.editor.defaultEvidenceLabel': 'Evidência',
@@ -653,7 +798,7 @@ const PRAXIS_RICH_CONTENT_PT_BR = {
653
798
  'praxis.richContent.editor.placeholder.evidenceSourceExpr': '${evidence.source}',
654
799
  'praxis.richContent.editor.field.formId': 'Form ID',
655
800
  'praxis.richContent.editor.field.ctaLabel': 'Label do CTA',
656
- 'praxis.richContent.editor.field.ctaIcon': 'Ícone do CTA',
801
+ 'praxis.richContent.editor.field.ctaIcon': 'Ícone do CTA',
657
802
  'praxis.richContent.editor.layout.stacked': 'Empilhado',
658
803
  'praxis.richContent.editor.layout.inlineShort': 'Inline',
659
804
  'praxis.richContent.editor.layout.split': 'Dividido',
@@ -695,25 +840,31 @@ const PRAXIS_RICH_CONTENT_PT_BR = {
695
840
  'praxis.richContent.editor.timeline.textAppearance.label': 'Rótulo',
696
841
  'praxis.richContent.editor.appearance.underline': 'Sublinhado',
697
842
  'praxis.richContent.editor.appearance.pills': 'Pílulas',
698
- 'praxis.richContent.editor.variant.basic': 'Básico',
843
+ 'praxis.richContent.editor.variant.basic': 'Básico',
699
844
  'praxis.richContent.editor.variant.raised': 'Elevado',
700
845
  'praxis.richContent.editor.variant.stroked': 'Contornado',
701
846
  'praxis.richContent.editor.variant.flat': 'Flat',
702
- 'praxis.richContent.editor.color.basic': 'Básica',
703
- 'praxis.richContent.editor.color.primary': 'Primária',
847
+ 'praxis.richContent.editor.color.basic': 'Básica',
848
+ 'praxis.richContent.editor.color.primary': 'Primária',
704
849
  'praxis.richContent.editor.color.accent': 'Accent',
705
850
  'praxis.richContent.editor.color.warn': 'Warn',
706
- 'praxis.richContent.editor.ctaGroupActions': 'Ações do CTA',
851
+ 'praxis.richContent.editor.cardActions': 'Ações do card',
852
+ 'praxis.richContent.editor.calloutActions': 'Ações do callout',
853
+ 'praxis.richContent.editor.ctaGroupActions': 'Ações do CTA',
854
+ 'praxis.richContent.editor.emptyStateActions': 'Ações do estado vazio',
855
+ 'praxis.richContent.editor.recordSummaryActions': 'Ações do resumo',
707
856
  'praxis.richContent.editor.tabsItems': 'Itens de aba',
708
- 'praxis.richContent.editor.lookupResultActions': 'Ações da consulta',
709
- 'praxis.richContent.editor.lookupCardSecondaryActions': 'Ações secundárias',
857
+ 'praxis.richContent.editor.lookupResultActions': 'Ações da consulta',
858
+ 'praxis.richContent.editor.lookupCardSecondaryActions': 'Ações secundárias',
859
+ 'praxis.richContent.editor.relatedRecordSecondaryActions': 'Ações secundárias',
710
860
  'praxis.richContent.editor.actionCardSecondaryActions': 'Ações secundárias',
711
- 'praxis.richContent.editor.collapsibleCardActions': 'Ações do card',
861
+ 'praxis.richContent.editor.collapsibleCardActions': 'Ações do card',
862
+ 'praxis.richContent.editor.accordionItemActions': 'Ações do item',
712
863
  'praxis.richContent.editor.status.idle': 'Aguardando',
713
864
  'praxis.richContent.editor.status.resolved': 'Resolvido',
714
865
  'praxis.richContent.editor.status.empty': 'Vazio',
715
866
  'praxis.richContent.editor.status.error': 'Erro',
716
- 'praxis.richContent.editor.formLauncherSecondaryActions': 'Ações secundárias',
867
+ 'praxis.richContent.editor.formLauncherSecondaryActions': 'Ações secundárias',
717
868
  'praxis.richContent.editor.defaultLookupResultTitle': 'Resultado de consulta',
718
869
  'praxis.richContent.editor.defaultLookupResultField': 'Departamento',
719
870
  'praxis.richContent.editor.defaultLookupResultHint': 'Resolvido a partir do registro selecionado',
@@ -5136,6 +5287,7 @@ const EDITABLE_TOP_LEVEL_NODE_TYPES = [
5136
5287
  'text',
5137
5288
  'badge',
5138
5289
  'icon',
5290
+ 'avatar',
5139
5291
  'image',
5140
5292
  'link',
5141
5293
  'metric',
@@ -5168,6 +5320,7 @@ const EDITABLE_PRESENTER_NODE_TYPES = [
5168
5320
  'text',
5169
5321
  'badge',
5170
5322
  'icon',
5323
+ 'avatar',
5171
5324
  'image',
5172
5325
  'link',
5173
5326
  'metric',
@@ -5184,6 +5337,12 @@ function createDefaultRichContentNode(type, tx, presetRef) {
5184
5337
  return { type: 'badge', label: tx('editor.defaultBadge', 'Badge') };
5185
5338
  case 'icon':
5186
5339
  return { type: 'icon', icon: 'check_circle' };
5340
+ case 'avatar':
5341
+ return {
5342
+ type: 'avatar',
5343
+ name: tx('editor.defaultAvatarName', 'Person'),
5344
+ initials: 'P',
5345
+ };
5187
5346
  case 'image':
5188
5347
  return { type: 'image', src: '/assets/placeholder.png', alt: '' };
5189
5348
  case 'link':
@@ -5539,6 +5698,12 @@ function createDefaultPresenterNode(type, tx) {
5539
5698
  return { type: 'badge', label: tx('editor.defaultBadge', 'Badge') };
5540
5699
  case 'icon':
5541
5700
  return { type: 'icon', icon: 'check_circle' };
5701
+ case 'avatar':
5702
+ return {
5703
+ type: 'avatar',
5704
+ name: tx('editor.defaultAvatarName', 'Person'),
5705
+ initials: 'P',
5706
+ };
5542
5707
  case 'image':
5543
5708
  return { type: 'image', src: '/assets/placeholder.png', alt: '' };
5544
5709
  case 'link':
@@ -5706,6 +5871,7 @@ class PraxisRichContentConfigEditor {
5706
5871
  validationIssues = [];
5707
5872
  nodeCount = 0;
5708
5873
  nodeTypeSummary = '';
5874
+ selectedNodeIndex = 0;
5709
5875
  initialInputs = this.normalizeInputs(this.inputs);
5710
5876
  pendingRemoval = null;
5711
5877
  ngOnChanges(changes) {
@@ -5758,14 +5924,19 @@ class PraxisRichContentConfigEditor {
5758
5924
  this.parseDocument();
5759
5925
  this.markDirty();
5760
5926
  }
5927
+ selectTopLevelNode(index) {
5928
+ this.selectedNodeIndex = index;
5929
+ }
5761
5930
  addTopLevelNode() {
5762
5931
  const document = this.ensureEditableDocument();
5763
5932
  document.nodes.push(this.createDefaultNode(this.newNodeType));
5933
+ this.selectedNodeIndex = document.nodes.length - 1;
5764
5934
  this.syncStructuredDocumentChange();
5765
5935
  }
5766
5936
  removeTopLevelNode(index) {
5767
5937
  const document = this.ensureEditableDocument();
5768
5938
  document.nodes.splice(index, 1);
5939
+ this.reconcileSelectedNodeIndex(document);
5769
5940
  this.syncStructuredDocumentChange();
5770
5941
  }
5771
5942
  moveTopLevelNode(index, delta) {
@@ -5776,6 +5947,17 @@ class PraxisRichContentConfigEditor {
5776
5947
  }
5777
5948
  const [node] = document.nodes.splice(index, 1);
5778
5949
  document.nodes.splice(nextIndex, 0, node);
5950
+ if (this.selectedNodeIndex === index) {
5951
+ this.selectedNodeIndex = nextIndex;
5952
+ }
5953
+ else if (delta < 0 &&
5954
+ this.selectedNodeIndex === nextIndex) {
5955
+ this.selectedNodeIndex = index;
5956
+ }
5957
+ else if (delta > 0 &&
5958
+ this.selectedNodeIndex === nextIndex) {
5959
+ this.selectedNodeIndex = index;
5960
+ }
5779
5961
  this.syncStructuredDocumentChange();
5780
5962
  }
5781
5963
  changeTopLevelNodeType(index, nextType) {
@@ -5888,6 +6070,93 @@ class PraxisRichContentConfigEditor {
5888
6070
  }
5889
6071
  this.syncStructuredDocumentChange();
5890
6072
  }
6073
+ getCardInteractionField(index, field) {
6074
+ const interaction = this.ensureCardNode(index).interaction;
6075
+ const value = interaction?.[field];
6076
+ return typeof value === 'string' ? value : '';
6077
+ }
6078
+ setCardInteractionField(index, field, value) {
6079
+ const card = this.ensureCardNode(index);
6080
+ const normalized = value.trim();
6081
+ if (field === 'mode' && (!normalized || normalized === 'none')) {
6082
+ if (card.interaction) {
6083
+ card.interaction.mode = 'none';
6084
+ }
6085
+ this.pruneEmptyCardInteraction(index);
6086
+ this.syncStructuredDocumentChange();
6087
+ return;
6088
+ }
6089
+ card.interaction ??= { mode: 'none' };
6090
+ const interaction = card.interaction;
6091
+ if (normalized) {
6092
+ interaction[field] = normalized;
6093
+ }
6094
+ else {
6095
+ delete interaction[field];
6096
+ }
6097
+ this.pruneEmptyCardInteraction(index);
6098
+ this.syncStructuredDocumentChange();
6099
+ }
6100
+ getCardInteractionBooleanField(index, field) {
6101
+ const interaction = this.ensureCardNode(index).interaction;
6102
+ return interaction?.[field] === true;
6103
+ }
6104
+ setCardInteractionBooleanField(index, field, value) {
6105
+ const card = this.ensureCardNode(index);
6106
+ card.interaction ??= { mode: 'none' };
6107
+ const interaction = card.interaction;
6108
+ if (value) {
6109
+ interaction[field] = true;
6110
+ }
6111
+ else {
6112
+ delete interaction[field];
6113
+ }
6114
+ this.pruneEmptyCardInteraction(index);
6115
+ this.syncStructuredDocumentChange();
6116
+ }
6117
+ getCardInteractionActionField(index, field) {
6118
+ const action = this.ensureCardNode(index).interaction?.action;
6119
+ return typeof action?.[field] === 'string' ? action[field] : '';
6120
+ }
6121
+ setCardInteractionActionField(index, field, value) {
6122
+ const card = this.ensureCardNode(index);
6123
+ card.interaction ??= { mode: 'action' };
6124
+ card.interaction.action ??= { actionId: '' };
6125
+ const action = card.interaction.action;
6126
+ const normalized = value.trim();
6127
+ if (normalized) {
6128
+ action[field] = value;
6129
+ }
6130
+ else {
6131
+ delete action[field];
6132
+ }
6133
+ if (!card.interaction.action.actionId) {
6134
+ delete card.interaction.action;
6135
+ }
6136
+ this.pruneEmptyCardInteraction(index);
6137
+ this.syncStructuredDocumentChange();
6138
+ }
6139
+ getCardAccessibilityField(index, field) {
6140
+ const accessibility = this.ensureCardNode(index).accessibility;
6141
+ const value = accessibility?.[field];
6142
+ return typeof value === 'string' ? value : '';
6143
+ }
6144
+ setCardAccessibilityField(index, field, value) {
6145
+ const card = this.ensureCardNode(index);
6146
+ const normalized = value.trim();
6147
+ card.accessibility ??= {};
6148
+ const accessibility = card.accessibility;
6149
+ if (normalized) {
6150
+ accessibility[field] = value;
6151
+ }
6152
+ else {
6153
+ delete accessibility[field];
6154
+ }
6155
+ if (!Object.keys(accessibility).length) {
6156
+ delete card.accessibility;
6157
+ }
6158
+ this.syncStructuredDocumentChange();
6159
+ }
5891
6160
  addCardSlotNode(index, slot) {
5892
6161
  this.ensureCardSlotNodes(index, slot).push(createDefaultPresenterNode('text', this.tx.bind(this)));
5893
6162
  this.syncStructuredDocumentChange();
@@ -6187,6 +6456,65 @@ class PraxisRichContentConfigEditor {
6187
6456
  };
6188
6457
  this.syncStructuredDocumentChange();
6189
6458
  }
6459
+ getTabsItemCapabilityList(index, itemIndex) {
6460
+ const item = this.ensureTabsNode(index).items[itemIndex];
6461
+ return Array.isArray(item?.requiresCapabilities)
6462
+ ? item.requiresCapabilities.join(', ')
6463
+ : '';
6464
+ }
6465
+ setTabsItemCapabilityList(index, itemIndex, value) {
6466
+ const node = this.ensureTabsNode(index);
6467
+ const current = node.items[itemIndex] ?? { content: [] };
6468
+ const capabilities = value
6469
+ .split(',')
6470
+ .map((item) => item.trim())
6471
+ .filter(Boolean);
6472
+ node.items[itemIndex] = {
6473
+ ...current,
6474
+ requiresCapabilities: capabilities.length ? capabilities : undefined,
6475
+ content: Array.isArray(current.content) && current.content.length
6476
+ ? current.content
6477
+ : [
6478
+ {
6479
+ type: 'text',
6480
+ text: this.tx('editor.defaultTabText', 'Tab content'),
6481
+ },
6482
+ ],
6483
+ };
6484
+ this.syncStructuredDocumentChange();
6485
+ }
6486
+ getTabsItemVisibleWhenPath(index, itemIndex) {
6487
+ const item = this.ensureTabsNode(index).items[itemIndex];
6488
+ return parseSimpleVisibleWhen(item?.visibleWhen)?.path ?? '';
6489
+ }
6490
+ getTabsItemVisibleWhenValue(index, itemIndex) {
6491
+ const item = this.ensureTabsNode(index).items[itemIndex];
6492
+ return parseSimpleVisibleWhen(item?.visibleWhen)?.value ?? '';
6493
+ }
6494
+ setTabsItemVisibleWhenPath(index, itemIndex, path) {
6495
+ const item = this.ensureTabsNode(index).items[itemIndex];
6496
+ if (!item)
6497
+ return;
6498
+ this.setTabsItemSimpleVisibleWhen(item, path, this.getTabsItemVisibleWhenValue(index, itemIndex));
6499
+ this.syncStructuredDocumentChange();
6500
+ }
6501
+ setTabsItemVisibleWhenValue(index, itemIndex, value) {
6502
+ const item = this.ensureTabsNode(index).items[itemIndex];
6503
+ if (!item)
6504
+ return;
6505
+ this.setTabsItemSimpleVisibleWhen(item, this.getTabsItemVisibleWhenPath(index, itemIndex), value);
6506
+ this.syncStructuredDocumentChange();
6507
+ }
6508
+ setTabsItemSimpleVisibleWhen(item, path, value) {
6509
+ const trimmedPath = path.trim();
6510
+ if (!trimmedPath) {
6511
+ delete item.visibleWhen;
6512
+ return;
6513
+ }
6514
+ item.visibleWhen = {
6515
+ '===': [{ var: trimmedPath }, parseScalar(value)],
6516
+ };
6517
+ }
6190
6518
  getActionCardSecondaryActions(index) {
6191
6519
  return this.ensureActionCardSecondaryActions(index);
6192
6520
  }
@@ -6610,6 +6938,16 @@ class PraxisRichContentConfigEditor {
6610
6938
  }
6611
6939
  return node;
6612
6940
  }
6941
+ pruneEmptyCardInteraction(index) {
6942
+ const card = this.ensureCardNode(index);
6943
+ const interaction = card.interaction;
6944
+ if (!interaction)
6945
+ return;
6946
+ const hasOnlyNoneMode = Object.keys(interaction).length === 1 && interaction['mode'] === 'none';
6947
+ if (!Object.keys(interaction).length || hasOnlyNoneMode) {
6948
+ delete card.interaction;
6949
+ }
6950
+ }
6613
6951
  ensureCardSlotNodes(index, slot) {
6614
6952
  const card = this.ensureCardNode(index);
6615
6953
  if (!Array.isArray(card[slot])) {
@@ -6915,6 +7253,7 @@ class PraxisRichContentConfigEditor {
6915
7253
  this.errorMessage = '';
6916
7254
  this.isValid$.next(true);
6917
7255
  this.parsedDocument = document;
7256
+ this.reconcileSelectedNodeIndex(document);
6918
7257
  this.updateDocumentOverview(document);
6919
7258
  return document;
6920
7259
  }
@@ -6934,6 +7273,20 @@ class PraxisRichContentConfigEditor {
6934
7273
  this.nodeCount = 0;
6935
7274
  this.nodeTypeSummary = '';
6936
7275
  }
7276
+ reconcileSelectedNodeIndex(document) {
7277
+ const nodeTotal = document.nodes.length;
7278
+ if (!nodeTotal) {
7279
+ this.selectedNodeIndex = 0;
7280
+ return;
7281
+ }
7282
+ if (this.selectedNodeIndex < 0) {
7283
+ this.selectedNodeIndex = 0;
7284
+ return;
7285
+ }
7286
+ if (this.selectedNodeIndex >= nodeTotal) {
7287
+ this.selectedNodeIndex = nodeTotal - 1;
7288
+ }
7289
+ }
6937
7290
  updateDocumentOverview(document) {
6938
7291
  const typeCounts = new Map();
6939
7292
  let count = 0;
@@ -7109,6 +7462,50 @@ class PraxisRichContentConfigEditor {
7109
7462
  />
7110
7463
  </label>
7111
7464
  }
7465
+ @case ('avatar') {
7466
+ <label>
7467
+ <span>{{ tx('editor.field.avatarName', 'Avatar name') }}</span>
7468
+ <input
7469
+ [ngModel]="getStringField(node, 'name')"
7470
+ (ngModelChange)="setString('name', $event)"
7471
+ />
7472
+ </label>
7473
+ <label>
7474
+ <span>{{ tx('editor.field.nameExpr', 'Name binding') }}</span>
7475
+ <input
7476
+ [ngModel]="getStringField(node, 'nameExpr')"
7477
+ (ngModelChange)="setString('nameExpr', $event)"
7478
+ />
7479
+ </label>
7480
+ <label>
7481
+ <span>{{ tx('editor.field.avatarImage', 'Avatar image') }}</span>
7482
+ <input
7483
+ [ngModel]="getStringField(node, 'imageSrc')"
7484
+ (ngModelChange)="setString('imageSrc', $event)"
7485
+ />
7486
+ </label>
7487
+ <label>
7488
+ <span>{{ tx('editor.field.imageSrcExpr', 'Image binding') }}</span>
7489
+ <input
7490
+ [ngModel]="getStringField(node, 'imageSrcExpr')"
7491
+ (ngModelChange)="setString('imageSrcExpr', $event)"
7492
+ />
7493
+ </label>
7494
+ <label>
7495
+ <span>{{ tx('editor.field.initials', 'Initials') }}</span>
7496
+ <input
7497
+ [ngModel]="getStringField(node, 'initials')"
7498
+ (ngModelChange)="setString('initials', $event)"
7499
+ />
7500
+ </label>
7501
+ <label>
7502
+ <span>{{ tx('editor.field.initialsExpr', 'Initials binding') }}</span>
7503
+ <input
7504
+ [ngModel]="getStringField(node, 'initialsExpr')"
7505
+ (ngModelChange)="setString('initialsExpr', $event)"
7506
+ />
7507
+ </label>
7508
+ }
7112
7509
  @case ('image') {
7113
7510
  <label>
7114
7511
  <span>{{ tx('editor.field.src', 'Image URL') }}</span>
@@ -7117,6 +7514,13 @@ class PraxisRichContentConfigEditor {
7117
7514
  (ngModelChange)="setString('src', $event)"
7118
7515
  />
7119
7516
  </label>
7517
+ <label>
7518
+ <span>{{ tx('editor.field.srcExpr', 'Image URL binding') }}</span>
7519
+ <input
7520
+ [ngModel]="getStringField(node, 'srcExpr')"
7521
+ (ngModelChange)="setString('srcExpr', $event)"
7522
+ />
7523
+ </label>
7120
7524
  <label>
7121
7525
  <span>{{ tx('editor.field.alt', 'Alternative text') }}</span>
7122
7526
  <input
@@ -7124,6 +7528,13 @@ class PraxisRichContentConfigEditor {
7124
7528
  (ngModelChange)="setString('alt', $event)"
7125
7529
  />
7126
7530
  </label>
7531
+ <label>
7532
+ <span>{{ tx('editor.field.altExpr', 'Alt text binding') }}</span>
7533
+ <input
7534
+ [ngModel]="getStringField(node, 'altExpr')"
7535
+ (ngModelChange)="setString('altExpr', $event)"
7536
+ />
7537
+ </label>
7127
7538
  }
7128
7539
  @case ('link') {
7129
7540
  <label>
@@ -7133,6 +7544,13 @@ class PraxisRichContentConfigEditor {
7133
7544
  (ngModelChange)="setString('label', $event)"
7134
7545
  />
7135
7546
  </label>
7547
+ <label>
7548
+ <span>{{ tx('editor.field.labelExpr', 'Label binding') }}</span>
7549
+ <input
7550
+ [ngModel]="getStringField(node, 'labelExpr')"
7551
+ (ngModelChange)="setString('labelExpr', $event)"
7552
+ />
7553
+ </label>
7136
7554
  <label>
7137
7555
  <span>{{ tx('editor.field.href', 'Link URL') }}</span>
7138
7556
  <input
@@ -7140,6 +7558,23 @@ class PraxisRichContentConfigEditor {
7140
7558
  (ngModelChange)="setString('href', $event)"
7141
7559
  />
7142
7560
  </label>
7561
+ <label>
7562
+ <span>{{ tx('editor.field.target', 'Target') }}</span>
7563
+ <select
7564
+ [ngModel]="getStringField(node, 'target') || '_self'"
7565
+ (ngModelChange)="setString('target', $event)"
7566
+ >
7567
+ <option value="_self">{{ tx('editor.target.self', 'Same tab') }}</option>
7568
+ <option value="_blank">{{ tx('editor.target.blank', 'New tab') }}</option>
7569
+ </select>
7570
+ </label>
7571
+ <label>
7572
+ <span>{{ tx('editor.field.rel', 'Rel') }}</span>
7573
+ <input
7574
+ [ngModel]="getStringField(node, 'rel')"
7575
+ (ngModelChange)="setString('rel', $event)"
7576
+ />
7577
+ </label>
7143
7578
  }
7144
7579
  @case ('metric') {
7145
7580
  <label>
@@ -7165,6 +7600,20 @@ class PraxisRichContentConfigEditor {
7165
7600
  (ngModelChange)="setString('label', $event)"
7166
7601
  />
7167
7602
  </label>
7603
+ <label>
7604
+ <span>{{ tx('editor.field.labelExpr', 'Label binding') }}</span>
7605
+ <input
7606
+ [ngModel]="getStringField(node, 'labelExpr')"
7607
+ (ngModelChange)="setString('labelExpr', $event)"
7608
+ />
7609
+ </label>
7610
+ <label>
7611
+ <span>{{ tx('editor.field.valueExpr', 'Value binding') }}</span>
7612
+ <input
7613
+ [ngModel]="getStringField(node, 'valueExpr')"
7614
+ (ngModelChange)="setString('valueExpr', $event)"
7615
+ />
7616
+ </label>
7168
7617
  <label>
7169
7618
  <span>{{ tx('editor.field.max', 'Maximum') }}</span>
7170
7619
  <input
@@ -7455,8 +7904,58 @@ class PraxisRichContentConfigEditor {
7455
7904
  </header>
7456
7905
 
7457
7906
  @if (parsedDocument?.nodes?.length) {
7907
+ <div class="prx-rich-editor__authoring-shell">
7908
+ <aside
7909
+ class="prx-rich-editor__structure"
7910
+ [attr.aria-label]="tx('editor.structure', 'Document structure')"
7911
+ >
7912
+ <div class="prx-rich-editor__structure-summary">
7913
+ <strong>{{ tx('editor.structure', 'Document structure') }}</strong>
7914
+ <span>
7915
+ {{
7916
+ tx('editor.structureHelp', 'Select a block to edit its properties.')
7917
+ }}
7918
+ </span>
7919
+ </div>
7920
+ <div class="prx-rich-editor__structure-list">
7921
+ @for (node of parsedDocument?.nodes ?? []; track node.id ?? $index; let nodeIndex = $index) {
7922
+ <button
7923
+ type="button"
7924
+ class="prx-rich-editor__structure-item"
7925
+ [class.prx-rich-editor__structure-item--selected]="selectedNodeIndex === nodeIndex"
7926
+ [attr.aria-current]="selectedNodeIndex === nodeIndex ? 'true' : null"
7927
+ (click)="selectTopLevelNode(nodeIndex)"
7928
+ [attr.data-testid]="'rich-content-node-selector-' + nodeIndex"
7929
+ >
7930
+ <span class="prx-rich-editor__node-eyebrow">
7931
+ {{ tx('editor.block', 'Block') }} {{ nodeIndex + 1 }}
7932
+ </span>
7933
+ <strong>{{ tx('editor.nodeType.' + node.type, node.type) }}</strong>
7934
+ @if (getIssueMessages('$.nodes[' + nodeIndex + ']').length) {
7935
+ <small>{{ tx('editor.hasIssues', 'Needs attention') }}</small>
7936
+ }
7937
+ </button>
7938
+ }
7939
+ </div>
7940
+ </aside>
7941
+
7942
+ <div class="prx-rich-editor__properties">
7943
+ <header class="prx-rich-editor__section-header">
7944
+ <div>
7945
+ <h3>{{ tx('editor.properties', 'Properties') }}</h3>
7946
+ <p>
7947
+ {{
7948
+ tx(
7949
+ 'editor.propertiesHelp',
7950
+ 'Edit the selected block without leaving the document structure.'
7951
+ )
7952
+ }}
7953
+ </p>
7954
+ </div>
7955
+ </header>
7458
7956
  <div class="prx-rich-editor__node-list">
7459
7957
  @for (node of parsedDocument?.nodes ?? []; track node.id ?? $index; let nodeIndex = $index) {
7958
+ @if (selectedNodeIndex === nodeIndex) {
7460
7959
  <article
7461
7960
  class="prx-rich-editor__node-card"
7462
7961
  [attr.data-rich-editor-node-type]="node.type"
@@ -7595,19 +8094,124 @@ class PraxisRichContentConfigEditor {
7595
8094
  />
7596
8095
  </label>
7597
8096
  }
7598
- @case ('image') {
8097
+ @case ('avatar') {
7599
8098
  <label>
7600
- <span>{{ tx('editor.field.src', 'Image URL') }}</span>
8099
+ <span>{{ tx('editor.field.avatarName', 'Avatar name') }}</span>
8100
+ <input
8101
+ [ngModel]="getStringField(node, 'name')"
8102
+ (ngModelChange)="setStringField($index, 'name', $event)"
8103
+ />
8104
+ </label>
8105
+ <label>
8106
+ <span>{{ tx('editor.field.nameExpr', 'Name binding') }}</span>
7601
8107
  <input
7602
- [ngModel]="getStringField(node, 'src')"
7603
- (ngModelChange)="setStringField($index, 'src', $event)"
8108
+ [ngModel]="getStringField(node, 'nameExpr')"
8109
+ (ngModelChange)="setStringField($index, 'nameExpr', $event)"
8110
+ [placeholder]="tx('editor.placeholder.nameExpr', 'row.name')"
7604
8111
  />
7605
8112
  </label>
7606
8113
  <label>
7607
- <span>{{ tx('editor.field.alt', 'Alternative text') }}</span>
8114
+ <span>{{ tx('editor.field.avatarImage', 'Avatar image') }}</span>
7608
8115
  <input
7609
- [ngModel]="getStringField(node, 'alt')"
7610
- (ngModelChange)="setStringField($index, 'alt', $event)"
8116
+ [ngModel]="getStringField(node, 'imageSrc')"
8117
+ (ngModelChange)="setStringField($index, 'imageSrc', $event)"
8118
+ />
8119
+ </label>
8120
+ <label>
8121
+ <span>{{ tx('editor.field.imageSrcExpr', 'Image binding') }}</span>
8122
+ <input
8123
+ [ngModel]="getStringField(node, 'imageSrcExpr')"
8124
+ (ngModelChange)="setStringField($index, 'imageSrcExpr', $event)"
8125
+ [placeholder]="tx('editor.placeholder.imageSrcExpr', 'row.avatarUrl')"
8126
+ />
8127
+ </label>
8128
+ <label>
8129
+ <span>{{ tx('editor.field.initials', 'Initials') }}</span>
8130
+ <input
8131
+ [ngModel]="getStringField(node, 'initials')"
8132
+ (ngModelChange)="setStringField($index, 'initials', $event)"
8133
+ />
8134
+ </label>
8135
+ <label>
8136
+ <span>{{ tx('editor.field.initialsExpr', 'Initials binding') }}</span>
8137
+ <input
8138
+ [ngModel]="getStringField(node, 'initialsExpr')"
8139
+ (ngModelChange)="setStringField($index, 'initialsExpr', $event)"
8140
+ [placeholder]="tx('editor.placeholder.initialsExpr', 'row.initials')"
8141
+ />
8142
+ </label>
8143
+ }
8144
+ @case ('image') {
8145
+ <label>
8146
+ <span>{{ tx('editor.field.src', 'Image URL') }}</span>
8147
+ <input
8148
+ [ngModel]="getStringField(node, 'src')"
8149
+ (ngModelChange)="setStringField($index, 'src', $event)"
8150
+ />
8151
+ </label>
8152
+ <label>
8153
+ <span>{{ tx('editor.field.srcExpr', 'Image URL binding') }}</span>
8154
+ <input
8155
+ [ngModel]="getStringField(node, 'srcExpr')"
8156
+ (ngModelChange)="setStringField($index, 'srcExpr', $event)"
8157
+ [placeholder]="tx('editor.placeholder.srcExpr', 'row.imageUrl')"
8158
+ />
8159
+ </label>
8160
+ <label>
8161
+ <span>{{ tx('editor.field.alt', 'Alternative text') }}</span>
8162
+ <input
8163
+ [ngModel]="getStringField(node, 'alt')"
8164
+ (ngModelChange)="setStringField($index, 'alt', $event)"
8165
+ />
8166
+ </label>
8167
+ <label>
8168
+ <span>{{ tx('editor.field.altExpr', 'Alt text binding') }}</span>
8169
+ <input
8170
+ [ngModel]="getStringField(node, 'altExpr')"
8171
+ (ngModelChange)="setStringField($index, 'altExpr', $event)"
8172
+ [placeholder]="tx('editor.placeholder.altExpr', 'row.imageAlt')"
8173
+ />
8174
+ </label>
8175
+ }
8176
+ @case ('link') {
8177
+ <label>
8178
+ <span>{{ tx('editor.field.label', 'Label') }}</span>
8179
+ <input
8180
+ [ngModel]="getStringField(node, 'label')"
8181
+ (ngModelChange)="setStringField($index, 'label', $event)"
8182
+ />
8183
+ </label>
8184
+ <label>
8185
+ <span>{{ tx('editor.field.labelExpr', 'Label binding') }}</span>
8186
+ <input
8187
+ [ngModel]="getStringField(node, 'labelExpr')"
8188
+ (ngModelChange)="setStringField($index, 'labelExpr', $event)"
8189
+ [placeholder]="tx('editor.placeholder.labelExpr', 'row.linkLabel')"
8190
+ />
8191
+ </label>
8192
+ <label class="prx-rich-editor__wide-field">
8193
+ <span>{{ tx('editor.field.href', 'Link URL') }}</span>
8194
+ <input
8195
+ [ngModel]="getStringField(node, 'href')"
8196
+ (ngModelChange)="setStringField($index, 'href', $event)"
8197
+ />
8198
+ </label>
8199
+ <label>
8200
+ <span>{{ tx('editor.field.target', 'Target') }}</span>
8201
+ <select
8202
+ [ngModel]="getStringField(node, 'target') || '_self'"
8203
+ (ngModelChange)="setStringField($index, 'target', $event)"
8204
+ >
8205
+ <option value="_self">{{ tx('editor.target.self', 'Same tab') }}</option>
8206
+ <option value="_blank">{{ tx('editor.target.blank', 'New tab') }}</option>
8207
+ </select>
8208
+ </label>
8209
+ <label>
8210
+ <span>{{ tx('editor.field.rel', 'Rel') }}</span>
8211
+ <input
8212
+ [ngModel]="getStringField(node, 'rel')"
8213
+ (ngModelChange)="setStringField($index, 'rel', $event)"
8214
+ [placeholder]="tx('editor.placeholder.rel', 'noopener noreferrer')"
7611
8215
  />
7612
8216
  </label>
7613
8217
  }
@@ -7619,6 +8223,14 @@ class PraxisRichContentConfigEditor {
7619
8223
  (ngModelChange)="setStringField($index, 'label', $event)"
7620
8224
  />
7621
8225
  </label>
8226
+ <label>
8227
+ <span>{{ tx('editor.field.labelExpr', 'Label binding') }}</span>
8228
+ <input
8229
+ [ngModel]="getStringField(node, 'labelExpr')"
8230
+ (ngModelChange)="setStringField($index, 'labelExpr', $event)"
8231
+ [placeholder]="tx('editor.placeholder.labelExpr', 'row.status')"
8232
+ />
8233
+ </label>
7622
8234
  <label>
7623
8235
  <span>{{ tx('editor.field.valueExpr', 'Value binding') }}</span>
7624
8236
  <input
@@ -7660,6 +8272,14 @@ class PraxisRichContentConfigEditor {
7660
8272
  (ngModelChange)="setNumberField($index, 'max', $event)"
7661
8273
  />
7662
8274
  </label>
8275
+ <label class="prx-rich-editor__checkbox-field">
8276
+ <input
8277
+ type="checkbox"
8278
+ [ngModel]="getBooleanField(node, 'showPercent')"
8279
+ (ngModelChange)="setBooleanField($index, 'showPercent', $event)"
8280
+ />
8281
+ <span>{{ tx('editor.field.showPercent', 'Show percent') }}</span>
8282
+ </label>
7663
8283
  }
7664
8284
  @case ('actionButton') {
7665
8285
  <label>
@@ -7755,6 +8375,14 @@ class PraxisRichContentConfigEditor {
7755
8375
  <option value="xl">xl</option>
7756
8376
  </select>
7757
8377
  </label>
8378
+ <label class="prx-rich-editor__checkbox-field">
8379
+ <input
8380
+ type="checkbox"
8381
+ [ngModel]="getBooleanField(node, 'wrap')"
8382
+ (ngModelChange)="setBooleanField($index, 'wrap', $event)"
8383
+ />
8384
+ <span>{{ tx('editor.field.wrap', 'Wrap items') }}</span>
8385
+ </label>
7758
8386
  <div class="prx-rich-editor__nested-editor">
7759
8387
  <header class="prx-rich-editor__nested-header">
7760
8388
  <h5>{{ tx('editor.composeItems', 'Compose items') }}</h5>
@@ -7812,6 +8440,14 @@ class PraxisRichContentConfigEditor {
7812
8440
  (ngModelChange)="setStringField($index, 'title', $event)"
7813
8441
  />
7814
8442
  </label>
8443
+ <label>
8444
+ <span>{{ tx('editor.field.titleExpr', 'Title binding') }}</span>
8445
+ <input
8446
+ [ngModel]="getStringField(node, 'titleExpr')"
8447
+ (ngModelChange)="setStringField($index, 'titleExpr', $event)"
8448
+ [placeholder]="tx('editor.placeholder.titleExpr', '\${decision.title}')"
8449
+ />
8450
+ </label>
7815
8451
  <label>
7816
8452
  <span>{{ tx('editor.field.subtitle', 'Subtitle') }}</span>
7817
8453
  <input
@@ -7819,6 +8455,14 @@ class PraxisRichContentConfigEditor {
7819
8455
  (ngModelChange)="setStringField($index, 'subtitle', $event)"
7820
8456
  />
7821
8457
  </label>
8458
+ <label>
8459
+ <span>{{ tx('editor.field.subtitleExpr', 'Subtitle binding') }}</span>
8460
+ <input
8461
+ [ngModel]="getStringField(node, 'subtitleExpr')"
8462
+ (ngModelChange)="setStringField($index, 'subtitleExpr', $event)"
8463
+ [placeholder]="tx('editor.placeholder.subtitleExpr', '\${decision.policySource}')"
8464
+ />
8465
+ </label>
7822
8466
  <label>
7823
8467
  <span>{{ tx('editor.field.variant', 'Variant') }}</span>
7824
8468
  <select
@@ -7856,6 +8500,16 @@ class PraxisRichContentConfigEditor {
7856
8500
  <option value="lg">{{ tx('editor.size.lg', 'Large') }}</option>
7857
8501
  </select>
7858
8502
  </label>
8503
+ <label>
8504
+ <span>{{ tx('editor.field.density', 'Density') }}</span>
8505
+ <select
8506
+ [ngModel]="getStringField(node, 'density') || 'comfortable'"
8507
+ (ngModelChange)="setStringField($index, 'density', $event)"
8508
+ >
8509
+ <option value="comfortable">{{ tx('editor.density.comfortable', 'Comfortable') }}</option>
8510
+ <option value="compact">{{ tx('editor.density.compact', 'Compact') }}</option>
8511
+ </select>
8512
+ </label>
7859
8513
  <label>
7860
8514
  <span>{{ tx('editor.field.orientation', 'Orientation') }}</span>
7861
8515
  <select
@@ -7866,6 +8520,38 @@ class PraxisRichContentConfigEditor {
7866
8520
  <option value="horizontal">{{ tx('editor.orientation.horizontal', 'Horizontal') }}</option>
7867
8521
  </select>
7868
8522
  </label>
8523
+ <label class="prx-rich-editor__checkbox-field">
8524
+ <input
8525
+ type="checkbox"
8526
+ [ngModel]="getBooleanField(node, 'loading')"
8527
+ (ngModelChange)="setBooleanField($index, 'loading', $event)"
8528
+ />
8529
+ <span>{{ tx('editor.field.loading', 'Loading') }}</span>
8530
+ </label>
8531
+ <label>
8532
+ <span>{{ tx('editor.field.loadingExpr', 'Loading binding') }}</span>
8533
+ <input
8534
+ [ngModel]="getStringField(node, 'loadingExpr')"
8535
+ (ngModelChange)="setStringField($index, 'loadingExpr', $event)"
8536
+ [placeholder]="tx('editor.placeholder.loadingExpr', 'row.loading')"
8537
+ />
8538
+ </label>
8539
+ <label class="prx-rich-editor__checkbox-field">
8540
+ <input
8541
+ type="checkbox"
8542
+ [ngModel]="getBooleanField(node, 'active')"
8543
+ (ngModelChange)="setBooleanField($index, 'active', $event)"
8544
+ />
8545
+ <span>{{ tx('editor.field.active', 'Active') }}</span>
8546
+ </label>
8547
+ <label>
8548
+ <span>{{ tx('editor.field.activeExpr', 'Active binding') }}</span>
8549
+ <input
8550
+ [ngModel]="getStringField(node, 'activeExpr')"
8551
+ (ngModelChange)="setStringField($index, 'activeExpr', $event)"
8552
+ [placeholder]="tx('editor.placeholder.activeExpr', 'row.active')"
8553
+ />
8554
+ </label>
7869
8555
  <label>
7870
8556
  <span>{{ tx('editor.field.mediaKind', 'Media') }}</span>
7871
8557
  <select
@@ -7887,6 +8573,14 @@ class PraxisRichContentConfigEditor {
7887
8573
  (ngModelChange)="setCardMediaField(nodeIndex, 'src', $event)"
7888
8574
  />
7889
8575
  </label>
8576
+ <label>
8577
+ <span>{{ tx('editor.field.mediaSrcExpr', 'Media source binding') }}</span>
8578
+ <input
8579
+ [ngModel]="getCardMediaField(nodeIndex, 'srcExpr')"
8580
+ (ngModelChange)="setCardMediaField(nodeIndex, 'srcExpr', $event)"
8581
+ [placeholder]="tx('editor.placeholder.mediaSrcExpr', 'row.mediaUrl')"
8582
+ />
8583
+ </label>
7890
8584
  <label>
7891
8585
  <span>{{ tx('editor.field.mediaAlt', 'Media alt text') }}</span>
7892
8586
  <input
@@ -7894,6 +8588,14 @@ class PraxisRichContentConfigEditor {
7894
8588
  (ngModelChange)="setCardMediaField(nodeIndex, 'alt', $event)"
7895
8589
  />
7896
8590
  </label>
8591
+ <label>
8592
+ <span>{{ tx('editor.field.mediaAltExpr', 'Media alt binding') }}</span>
8593
+ <input
8594
+ [ngModel]="getCardMediaField(nodeIndex, 'altExpr')"
8595
+ (ngModelChange)="setCardMediaField(nodeIndex, 'altExpr', $event)"
8596
+ [placeholder]="tx('editor.placeholder.mediaAltExpr', 'row.mediaAlt')"
8597
+ />
8598
+ </label>
7897
8599
  <label>
7898
8600
  <span>{{ tx('editor.field.mediaIcon', 'Media icon') }}</span>
7899
8601
  <input
@@ -7901,6 +8603,29 @@ class PraxisRichContentConfigEditor {
7901
8603
  (ngModelChange)="setCardMediaField(nodeIndex, 'icon', $event)"
7902
8604
  />
7903
8605
  </label>
8606
+ <label>
8607
+ <span>{{ tx('editor.field.mediaLabel', 'Media label') }}</span>
8608
+ <input
8609
+ [ngModel]="getCardMediaField(nodeIndex, 'label')"
8610
+ (ngModelChange)="setCardMediaField(nodeIndex, 'label', $event)"
8611
+ />
8612
+ </label>
8613
+ <label>
8614
+ <span>{{ tx('editor.field.mediaLabelExpr', 'Media label binding') }}</span>
8615
+ <input
8616
+ [ngModel]="getCardMediaField(nodeIndex, 'labelExpr')"
8617
+ (ngModelChange)="setCardMediaField(nodeIndex, 'labelExpr', $event)"
8618
+ [placeholder]="tx('editor.placeholder.mediaLabelExpr', 'row.mediaLabel')"
8619
+ />
8620
+ </label>
8621
+ <label>
8622
+ <span>{{ tx('editor.field.aspectRatio', 'Aspect ratio') }}</span>
8623
+ <input
8624
+ [ngModel]="getCardMediaField(nodeIndex, 'aspectRatio')"
8625
+ (ngModelChange)="setCardMediaField(nodeIndex, 'aspectRatio', $event)"
8626
+ [placeholder]="tx('editor.placeholder.aspectRatio', '16 / 9')"
8627
+ />
8628
+ </label>
7904
8629
  <label>
7905
8630
  <span>{{ tx('editor.field.mediaPlacement', 'Media placement') }}</span>
7906
8631
  <select
@@ -7914,6 +8639,76 @@ class PraxisRichContentConfigEditor {
7914
8639
  </select>
7915
8640
  </label>
7916
8641
  }
8642
+ <label>
8643
+ <span>{{ tx('editor.field.interactionMode', 'Interaction mode') }}</span>
8644
+ <select
8645
+ [ngModel]="getCardInteractionField(nodeIndex, 'mode') || 'none'"
8646
+ (ngModelChange)="setCardInteractionField(nodeIndex, 'mode', $event)"
8647
+ >
8648
+ <option value="none">{{ tx('editor.interaction.none', 'None') }}</option>
8649
+ <option value="action">{{ tx('editor.interaction.action', 'Action') }}</option>
8650
+ <option value="selectable">{{ tx('editor.interaction.selectable', 'Selectable') }}</option>
8651
+ </select>
8652
+ </label>
8653
+ <label class="prx-rich-editor__checkbox-field">
8654
+ <input
8655
+ type="checkbox"
8656
+ [ngModel]="getCardInteractionBooleanField(nodeIndex, 'selected')"
8657
+ (ngModelChange)="setCardInteractionBooleanField(nodeIndex, 'selected', $event)"
8658
+ />
8659
+ <span>{{ tx('editor.field.selected', 'Selected') }}</span>
8660
+ </label>
8661
+ <label>
8662
+ <span>{{ tx('editor.field.selectedExpr', 'Selected binding') }}</span>
8663
+ <input
8664
+ [ngModel]="getCardInteractionField(nodeIndex, 'selectedExpr')"
8665
+ (ngModelChange)="setCardInteractionField(nodeIndex, 'selectedExpr', $event)"
8666
+ [placeholder]="tx('editor.placeholder.selectedExpr', 'row.selected')"
8667
+ />
8668
+ </label>
8669
+ <label>
8670
+ <span>{{ tx('editor.field.interactionActionId', 'Interaction action ID') }}</span>
8671
+ <input
8672
+ [ngModel]="getCardInteractionActionField(nodeIndex, 'actionId')"
8673
+ (ngModelChange)="setCardInteractionActionField(nodeIndex, 'actionId', $event)"
8674
+ [placeholder]="tx('editor.placeholder.actionId', 'host.action.open')"
8675
+ />
8676
+ </label>
8677
+ <label>
8678
+ <span>{{ tx('editor.field.payloadExpr', 'Payload binding') }}</span>
8679
+ <input
8680
+ [ngModel]="getCardInteractionActionField(nodeIndex, 'payloadExpr')"
8681
+ (ngModelChange)="setCardInteractionActionField(nodeIndex, 'payloadExpr', $event)"
8682
+ [placeholder]="tx('editor.placeholder.payloadExpr', 'row')"
8683
+ />
8684
+ </label>
8685
+ <label>
8686
+ <span>{{ tx('editor.field.accessibilityRole', 'Accessibility role') }}</span>
8687
+ <select
8688
+ [ngModel]="getCardAccessibilityField(nodeIndex, 'role')"
8689
+ (ngModelChange)="setCardAccessibilityField(nodeIndex, 'role', $event)"
8690
+ >
8691
+ <option value="">{{ tx('editor.none', 'None') }}</option>
8692
+ <option value="article">{{ tx('editor.accessibilityRole.article', 'Article') }}</option>
8693
+ <option value="group">{{ tx('editor.accessibilityRole.group', 'Group') }}</option>
8694
+ <option value="button">{{ tx('editor.accessibilityRole.button', 'Button') }}</option>
8695
+ </select>
8696
+ </label>
8697
+ <label>
8698
+ <span>{{ tx('editor.field.ariaLabel', 'Accessible label') }}</span>
8699
+ <input
8700
+ [ngModel]="getCardAccessibilityField(nodeIndex, 'ariaLabel')"
8701
+ (ngModelChange)="setCardAccessibilityField(nodeIndex, 'ariaLabel', $event)"
8702
+ />
8703
+ </label>
8704
+ <label>
8705
+ <span>{{ tx('editor.field.ariaLabelExpr', 'Accessible label binding') }}</span>
8706
+ <input
8707
+ [ngModel]="getCardAccessibilityField(nodeIndex, 'ariaLabelExpr')"
8708
+ (ngModelChange)="setCardAccessibilityField(nodeIndex, 'ariaLabelExpr', $event)"
8709
+ [placeholder]="tx('editor.placeholder.ariaLabelExpr', 'row.ariaLabel')"
8710
+ />
8711
+ </label>
7917
8712
  <div class="prx-rich-editor__nested-editor">
7918
8713
  <header class="prx-rich-editor__nested-header">
7919
8714
  <h5>{{ tx('editor.cardContent', 'Card body') }}</h5>
@@ -8359,6 +9154,14 @@ class PraxisRichContentConfigEditor {
8359
9154
  (ngModelChange)="setTabsItemField(nodeIndex, $index, 'label', $event)"
8360
9155
  />
8361
9156
  </label>
9157
+ <label>
9158
+ <span>{{ tx('editor.field.labelExpr', 'Label binding') }}</span>
9159
+ <input
9160
+ [ngModel]="getTabsItemField(nodeIndex, $index, 'labelExpr')"
9161
+ (ngModelChange)="setTabsItemField(nodeIndex, $index, 'labelExpr', $event)"
9162
+ [placeholder]="tx('editor.placeholder.tabLabelExpr', 'row.tabLabel')"
9163
+ />
9164
+ </label>
8362
9165
  <label>
8363
9166
  <span>{{ tx('editor.field.icon', 'Icon') }}</span>
8364
9167
  <input
@@ -8373,6 +9176,48 @@ class PraxisRichContentConfigEditor {
8373
9176
  (ngModelChange)="setTabsItemField(nodeIndex, $index, 'badge', $event)"
8374
9177
  />
8375
9178
  </label>
9179
+ <label>
9180
+ <span>{{ tx('editor.field.badgeExpr', 'Badge binding') }}</span>
9181
+ <input
9182
+ [ngModel]="getTabsItemField(nodeIndex, $index, 'badgeExpr')"
9183
+ (ngModelChange)="setTabsItemField(nodeIndex, $index, 'badgeExpr', $event)"
9184
+ [placeholder]="tx('editor.placeholder.badgeExpr', 'row.badge')"
9185
+ />
9186
+ </label>
9187
+ <label>
9188
+ <span>{{ tx('editor.field.requiresCapabilities', 'Capabilities') }}</span>
9189
+ <input
9190
+ [ngModel]="getTabsItemCapabilityList(nodeIndex, $index)"
9191
+ (ngModelChange)="setTabsItemCapabilityList(nodeIndex, $index, $event)"
9192
+ [placeholder]="tx('editor.placeholder.requiresCapabilities', 'form.mode.edit, employee.open')"
9193
+ />
9194
+ </label>
9195
+ <label>
9196
+ <span>{{ tx('editor.field.capabilityMode', 'Capability mode') }}</span>
9197
+ <select
9198
+ [ngModel]="getTabsItemField(nodeIndex, $index, 'capabilityMode') || 'all'"
9199
+ (ngModelChange)="setTabsItemField(nodeIndex, $index, 'capabilityMode', $event)"
9200
+ >
9201
+ <option value="all">{{ tx('editor.capabilityMode.all', 'All') }}</option>
9202
+ <option value="any">{{ tx('editor.capabilityMode.any', 'Any') }}</option>
9203
+ </select>
9204
+ </label>
9205
+ <label>
9206
+ <span>{{ tx('editor.field.visibleWhenPath', 'Context path') }}</span>
9207
+ <input
9208
+ [ngModel]="getTabsItemVisibleWhenPath(nodeIndex, $index)"
9209
+ (ngModelChange)="setTabsItemVisibleWhenPath(nodeIndex, $index, $event)"
9210
+ [placeholder]="tx('editor.placeholder.visibleWhenPath', 'row.active')"
9211
+ />
9212
+ </label>
9213
+ <label>
9214
+ <span>{{ tx('editor.field.visibleWhenValue', 'Expected value') }}</span>
9215
+ <input
9216
+ [ngModel]="getTabsItemVisibleWhenValue(nodeIndex, $index)"
9217
+ (ngModelChange)="setTabsItemVisibleWhenValue(nodeIndex, $index, $event)"
9218
+ [placeholder]="tx('editor.placeholder.visibleWhenValue', 'true')"
9219
+ />
9220
+ </label>
8376
9221
  </div>
8377
9222
  }
8378
9223
  </div>
@@ -9035,24 +9880,24 @@ class PraxisRichContentConfigEditor {
9035
9880
  <header class="prx-rich-editor__nested-header">
9036
9881
  <h5>{{ tx('editor.decisionEvidence', 'Evidence') }}</h5>
9037
9882
  <div class="prx-rich-editor__quick-actions">
9038
- <button type="button" (click)="addDecisionPackageEvidence($index)">
9883
+ <button type="button" (click)="addDecisionPackageEvidence(nodeIndex)">
9039
9884
  {{ tx('editor.addEvidence', 'Add evidence') }}
9040
9885
  </button>
9041
9886
  </div>
9042
9887
  </header>
9043
- @for (evidence of getDecisionPackageEvidence($index); track evidence.id ?? $index; let evidenceIndex = $index) {
9888
+ @for (evidence of getDecisionPackageEvidence(nodeIndex); track evidence.id ?? $index; let evidenceIndex = $index) {
9044
9889
  <div class="prx-rich-editor__nested-node">
9045
9890
  <div class="prx-rich-editor__nested-actions">
9046
9891
  <strong>{{ tx('editor.evidence', 'Evidence') }} {{ evidenceIndex + 1 }}</strong>
9047
- @if (isRemovalPending('nodes.' + $index + '.evidence', evidenceIndex)) {
9048
- <button type="button" (click)="removeDecisionPackageEvidence($index, evidenceIndex)">
9892
+ @if (isRemovalPending('nodes.' + nodeIndex + '.evidence', evidenceIndex)) {
9893
+ <button type="button" (click)="removeDecisionPackageEvidence(nodeIndex, evidenceIndex)">
9049
9894
  {{ tx('editor.confirmRemove', 'Confirm remove') }}
9050
9895
  </button>
9051
9896
  <button type="button" (click)="cancelRemoval()">
9052
9897
  {{ tx('editor.cancelRemove', 'Cancel') }}
9053
9898
  </button>
9054
9899
  } @else {
9055
- <button type="button" (click)="requestNestedRemoval('nodes.' + $index + '.evidence', evidenceIndex)">
9900
+ <button type="button" (click)="requestNestedRemoval('nodes.' + nodeIndex + '.evidence', evidenceIndex)">
9056
9901
  {{ tx('editor.removeBlock', 'Remove') }}
9057
9902
  </button>
9058
9903
  }
@@ -9061,37 +9906,37 @@ class PraxisRichContentConfigEditor {
9061
9906
  <label>
9062
9907
  <span>{{ tx('editor.field.itemId', 'Item ID') }}</span>
9063
9908
  <input
9064
- [ngModel]="getDecisionPackageEvidenceField($index, evidenceIndex, 'id')"
9065
- (ngModelChange)="setDecisionPackageEvidenceField($index, evidenceIndex, 'id', $event)"
9909
+ [ngModel]="getDecisionPackageEvidenceField(nodeIndex, evidenceIndex, 'id')"
9910
+ (ngModelChange)="setDecisionPackageEvidenceField(nodeIndex, evidenceIndex, 'id', $event)"
9066
9911
  />
9067
9912
  </label>
9068
9913
  <label>
9069
9914
  <span>{{ tx('editor.field.label', 'Label') }}</span>
9070
9915
  <input
9071
- [ngModel]="getDecisionPackageEvidenceField($index, evidenceIndex, 'label')"
9072
- (ngModelChange)="setDecisionPackageEvidenceField($index, evidenceIndex, 'label', $event)"
9916
+ [ngModel]="getDecisionPackageEvidenceField(nodeIndex, evidenceIndex, 'label')"
9917
+ (ngModelChange)="setDecisionPackageEvidenceField(nodeIndex, evidenceIndex, 'label', $event)"
9073
9918
  />
9074
9919
  </label>
9075
9920
  <label>
9076
9921
  <span>{{ tx('editor.field.labelExpr', 'Label binding') }}</span>
9077
9922
  <input
9078
- [ngModel]="getDecisionPackageEvidenceField($index, evidenceIndex, 'labelExpr')"
9079
- (ngModelChange)="setDecisionPackageEvidenceField($index, evidenceIndex, 'labelExpr', $event)"
9923
+ [ngModel]="getDecisionPackageEvidenceField(nodeIndex, evidenceIndex, 'labelExpr')"
9924
+ (ngModelChange)="setDecisionPackageEvidenceField(nodeIndex, evidenceIndex, 'labelExpr', $event)"
9080
9925
  [placeholder]="tx('editor.placeholder.evidenceLabelExpr', '\${evidence.label}')"
9081
9926
  />
9082
9927
  </label>
9083
9928
  <label>
9084
9929
  <span>{{ tx('editor.field.source', 'Source') }}</span>
9085
9930
  <input
9086
- [ngModel]="getDecisionPackageEvidenceField($index, evidenceIndex, 'source')"
9087
- (ngModelChange)="setDecisionPackageEvidenceField($index, evidenceIndex, 'source', $event)"
9931
+ [ngModel]="getDecisionPackageEvidenceField(nodeIndex, evidenceIndex, 'source')"
9932
+ (ngModelChange)="setDecisionPackageEvidenceField(nodeIndex, evidenceIndex, 'source', $event)"
9088
9933
  />
9089
9934
  </label>
9090
9935
  <label>
9091
9936
  <span>{{ tx('editor.field.sourceExpr', 'Source binding') }}</span>
9092
9937
  <input
9093
- [ngModel]="getDecisionPackageEvidenceField($index, evidenceIndex, 'sourceExpr')"
9094
- (ngModelChange)="setDecisionPackageEvidenceField($index, evidenceIndex, 'sourceExpr', $event)"
9938
+ [ngModel]="getDecisionPackageEvidenceField(nodeIndex, evidenceIndex, 'sourceExpr')"
9939
+ (ngModelChange)="setDecisionPackageEvidenceField(nodeIndex, evidenceIndex, 'sourceExpr', $event)"
9095
9940
  [placeholder]="tx('editor.placeholder.evidenceSourceExpr', '\${evidence.source}')"
9096
9941
  />
9097
9942
  </label>
@@ -9104,27 +9949,27 @@ class PraxisRichContentConfigEditor {
9104
9949
  <header class="prx-rich-editor__nested-header">
9105
9950
  <h5>{{ tx('editor.decisionPrimaryAction', 'Primary action') }}</h5>
9106
9951
  <div class="prx-rich-editor__quick-actions">
9107
- @if (getDecisionPackagePrimaryAction($index)) {
9108
- <button type="button" (click)="removeDecisionPackagePrimaryAction($index)">
9952
+ @if (getDecisionPackagePrimaryAction(nodeIndex)) {
9953
+ <button type="button" (click)="removeDecisionPackagePrimaryAction(nodeIndex)">
9109
9954
  {{ tx('editor.removeBlock', 'Remove') }}
9110
9955
  </button>
9111
9956
  } @else {
9112
- <button type="button" (click)="addDecisionPackagePrimaryActionPreset($index, 'primary')">
9957
+ <button type="button" (click)="addDecisionPackagePrimaryActionPreset(nodeIndex, 'primary')">
9113
9958
  {{ tx('editor.addPrimaryAction', 'Add primary action') }}
9114
9959
  </button>
9115
- <button type="button" (click)="addDecisionPackagePrimaryAction($index)">
9960
+ <button type="button" (click)="addDecisionPackagePrimaryAction(nodeIndex)">
9116
9961
  {{ tx('editor.addAction', 'Add action') }}
9117
9962
  </button>
9118
9963
  }
9119
9964
  </div>
9120
9965
  </header>
9121
- @if (getDecisionPackagePrimaryAction($index); as primaryAction) {
9966
+ @if (getDecisionPackagePrimaryAction(nodeIndex); as primaryAction) {
9122
9967
  <div class="prx-rich-editor__node-grid">
9123
9968
  <ng-container
9124
9969
  *ngTemplateOutlet="actionFields; context: {
9125
9970
  action: primaryAction,
9126
- path: '$.nodes[' + $index + '].primaryAction',
9127
- setField: setDecisionPackagePrimaryActionField.bind(this, $index)
9971
+ path: '$.nodes[' + nodeIndex + '].primaryAction',
9972
+ setField: setDecisionPackagePrimaryActionField.bind(this, nodeIndex)
9128
9973
  }"
9129
9974
  ></ng-container>
9130
9975
  </div>
@@ -9842,8 +10687,11 @@ class PraxisRichContentConfigEditor {
9842
10687
  </fieldset>
9843
10688
  </div>
9844
10689
  </article>
10690
+ }
9845
10691
  }
9846
10692
  </div>
10693
+ </div>
10694
+ </div>
9847
10695
  } @else {
9848
10696
  <p class="prx-rich-editor__empty">
9849
10697
  {{ tx('editor.noBlocks', 'No blocks yet. Add a block to start authoring this document.') }}
@@ -9851,9 +10699,23 @@ class PraxisRichContentConfigEditor {
9851
10699
  }
9852
10700
  </section>
9853
10701
 
9854
- <label class="prx-rich-editor__document">
9855
- <span>{{ tx('editor.document', 'Advanced JSON') }}</span>
10702
+ <section class="prx-rich-editor__document-panel">
10703
+ <header class="prx-rich-editor__section-header">
10704
+ <div>
10705
+ <h3>{{ tx('editor.document', 'Advanced JSON') }}</h3>
10706
+ <p>
10707
+ {{
10708
+ tx(
10709
+ 'editor.documentAdvancedHelp',
10710
+ 'Use JSON for diagnostics, migration and advanced fields that are not yet surfaced by guided controls.'
10711
+ )
10712
+ }}
10713
+ </p>
10714
+ </div>
10715
+ </header>
9856
10716
  <div class="prx-rich-editor__workbench">
10717
+ <label class="prx-rich-editor__document">
10718
+ <span>{{ tx('editor.documentJson', 'Canonical document JSON') }}</span>
9857
10719
  <textarea
9858
10720
  name="rich-content-document"
9859
10721
  [(ngModel)]="documentJson"
@@ -9867,6 +10729,7 @@ class PraxisRichContentConfigEditor {
9867
10729
  spellcheck="false"
9868
10730
  data-testid="rich-content-document-input"
9869
10731
  ></textarea>
10732
+ </label>
9870
10733
 
9871
10734
  <aside
9872
10735
  class="prx-rich-editor__inspector"
@@ -9888,7 +10751,7 @@ class PraxisRichContentConfigEditor {
9888
10751
 
9889
10752
  @if (parsedDocument) {
9890
10753
  <section>
9891
- <h3>{{ tx('editor.preview', 'Preview') }}</h3>
10754
+ <h3>{{ tx('editor.preview', 'Preview') }}</h3>
9892
10755
  <div class="prx-rich-editor__preview">
9893
10756
  <praxis-rich-content
9894
10757
  [document]="parsedDocument"
@@ -9900,7 +10763,7 @@ class PraxisRichContentConfigEditor {
9900
10763
  }
9901
10764
  </aside>
9902
10765
  </div>
9903
- </label>
10766
+ </section>
9904
10767
 
9905
10768
  @if (errorMessage) {
9906
10769
  <div
@@ -9940,7 +10803,7 @@ class PraxisRichContentConfigEditor {
9940
10803
  </button>
9941
10804
  </div>
9942
10805
  </section>
9943
- `, isInline: true, styles: [":host{display:block;min-width:0}.prx-rich-editor{display:grid;gap:18px;color:var(--md-sys-color-on-surface, #1f2937)}.prx-rich-editor__header{display:flex;gap:16px;align-items:flex-start;justify-content:space-between}.prx-rich-editor__header h2{margin:0;font-size:1.25rem}.prx-rich-editor__header p{margin:6px 0 0;color:var(--md-sys-color-on-surface-variant, #5f6673)}.prx-rich-editor__eyebrow{margin:0 0 4px;text-transform:uppercase;letter-spacing:.08em;font-size:.72rem;color:var(--md-sys-color-primary, #3154e7)}.prx-rich-editor__status{flex:0 0 auto;padding:6px 10px;border-radius:999px;background:color-mix(in srgb,#16a34a 12%,transparent);color:#166534;font-size:.78rem;font-weight:700}.prx-rich-editor__status.invalid{background:color-mix(in srgb,#dc2626 12%,transparent);color:#991b1b}.prx-rich-editor__grid{display:grid;gap:14px;grid-template-columns:minmax(0,180px) minmax(0,1fr)}.prx-rich-editor__blocks{display:grid;gap:14px}.prx-rich-editor__section-header,.prx-rich-editor__node-header{display:flex;gap:16px;align-items:flex-start;justify-content:space-between}.prx-rich-editor__section-header h3,.prx-rich-editor__node-header h4{margin:0}.prx-rich-editor__section-header p,.prx-rich-editor__node-note,.prx-rich-editor__empty{margin:6px 0 0;color:var(--md-sys-color-on-surface-variant, #5f6673);font-size:.88rem}.prx-rich-editor__add-block,.prx-rich-editor__node-actions{display:flex;flex-wrap:wrap;gap:10px;align-items:flex-end}.prx-rich-editor__add-block label{min-width:180px}.prx-rich-editor__node-list{display:grid;gap:12px}.prx-rich-editor__node-card{display:grid;gap:14px;border:1px solid var(--md-sys-color-outline-variant, #d7dce5);border-radius:8px;padding:14px;background:var(--md-sys-color-surface-container-lowest, #fff)}.prx-rich-editor__node-eyebrow{margin:0 0 4px;color:var(--md-sys-color-on-surface-variant, #5f6673);font-size:.75rem;font-weight:700;text-transform:uppercase}.prx-rich-editor__node-grid{display:grid;gap:12px;grid-template-columns:repeat(2,minmax(0,1fr))}.prx-rich-editor__wide-field,.prx-rich-editor__node-note{grid-column:1 / -1}.prx-rich-editor__field-group,.prx-rich-editor__nested-editor,.prx-rich-editor__nested-node{grid-column:1 / -1;display:grid;gap:12px}.prx-rich-editor__field-group,.prx-rich-editor__nested-node{margin:0;border:1px solid var(--md-sys-color-outline-variant, #d7dce5);border-radius:8px;padding:12px}.prx-rich-editor__field-group{grid-template-columns:repeat(2,minmax(0,1fr))}.prx-rich-editor__field-group legend{padding:0 4px;font-weight:700}.prx-rich-editor__nested-header,.prx-rich-editor__nested-actions{display:flex;flex-wrap:wrap;gap:10px;align-items:center;justify-content:space-between}.prx-rich-editor__quick-actions{display:flex;flex-wrap:wrap;gap:8px}.prx-rich-editor__node-grid textarea{min-height:96px;font-family:inherit}label,.prx-rich-editor__document{display:grid;gap:7px;font-weight:700}.prx-rich-editor__checkbox-field{display:flex;gap:10px;align-items:center;align-self:end}.prx-rich-editor__checkbox-field input[type=checkbox]{width:auto}input,select,textarea{box-sizing:border-box;width:100%;border:1px solid var(--md-sys-color-outline-variant, #d7dce5);border-radius:12px;padding:10px 12px;font:inherit;color:inherit;background:var(--md-sys-color-surface, #fff)}textarea{min-height:360px;resize:vertical;font-family:Cascadia Code,Fira Code,Consolas,monospace;font-size:.86rem;line-height:1.45}.prx-rich-editor__workbench{display:grid;gap:14px;grid-template-columns:minmax(0,1.2fr) minmax(260px,.8fr)}.prx-rich-editor__inspector{display:grid;align-content:start;gap:14px;min-width:0;border:1px solid var(--md-sys-color-outline-variant, #d7dce5);border-radius:8px;padding:12px;background:var(--md-sys-color-surface-container-lowest, #fff)}.prx-rich-editor__inspector h3{margin:0 0 8px;font-size:.95rem}.prx-rich-editor__inspector dl{display:grid;gap:8px;margin:0}.prx-rich-editor__inspector dl div{display:grid;gap:2px}.prx-rich-editor__inspector dt{color:var(--md-sys-color-on-surface-variant, #5f6673);font-size:.78rem;font-weight:700}.prx-rich-editor__inspector dd{margin:0;overflow-wrap:anywhere;font-weight:600}.prx-rich-editor__preview{max-height:260px;overflow:auto;border:1px solid var(--md-sys-color-outline-variant, #d7dce5);border-radius:8px;padding:12px;background:var(--md-sys-color-surface, #fff)}.prx-rich-editor__help{margin:-8px 0 0;color:var(--md-sys-color-on-surface-variant, #5f6673);font-size:.85rem}.prx-rich-editor__error{margin:0;padding:10px 12px;border-radius:8px;color:#991b1b;background:color-mix(in srgb,#dc2626 9%,transparent)}.prx-rich-editor__error p{margin:0}.prx-rich-editor__error ul{display:grid;gap:6px;margin:8px 0 0;padding-inline-start:18px}.prx-rich-editor__error code{margin-right:4px;font-family:Cascadia Code,Fira Code,Consolas,monospace}.prx-rich-editor__actions{display:flex;flex-wrap:wrap;gap:10px}button{border:1px solid var(--md-sys-color-outline-variant, #d7dce5);border-radius:8px;padding:8px 14px;background:var(--md-sys-color-surface, #fff);color:var(--md-sys-color-primary, #3154e7);font-weight:700;cursor:pointer}button:disabled{cursor:not-allowed;opacity:.55}@media(max-width:760px){.prx-rich-editor__header,.prx-rich-editor__section-header,.prx-rich-editor__node-header,.prx-rich-editor__grid,.prx-rich-editor__field-group,.prx-rich-editor__node-grid,.prx-rich-editor__workbench{grid-template-columns:1fr;display:grid}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.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: i2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: PraxisRichContent, selector: "praxis-rich-content", inputs: ["document", "nodes", "context", "hostCapabilities", "layout", "rootClassName"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
10806
+ `, isInline: true, styles: [":host{display:block;min-width:0}.prx-rich-editor{display:grid;gap:18px;color:var(--md-sys-color-on-surface, #1f2937)}.prx-rich-editor__header{display:flex;gap:16px;align-items:flex-start;justify-content:space-between}.prx-rich-editor__header h2{margin:0;font-size:1.25rem}.prx-rich-editor__header p{margin:6px 0 0;color:var(--md-sys-color-on-surface-variant, #5f6673)}.prx-rich-editor__eyebrow{margin:0 0 4px;text-transform:uppercase;letter-spacing:.08em;font-size:.72rem;color:var(--md-sys-color-primary, #3154e7)}.prx-rich-editor__status{flex:0 0 auto;padding:6px 10px;border-radius:999px;background:color-mix(in srgb,#16a34a 12%,transparent);color:#166534;font-size:.78rem;font-weight:700}.prx-rich-editor__status.invalid{background:color-mix(in srgb,#dc2626 12%,transparent);color:#991b1b}.prx-rich-editor__grid{display:grid;gap:14px;grid-template-columns:minmax(0,180px) minmax(0,1fr)}.prx-rich-editor__blocks{display:grid;gap:14px}.prx-rich-editor__authoring-shell{display:grid;gap:14px;grid-template-columns:minmax(220px,.34fr) minmax(0,1fr);align-items:start}.prx-rich-editor__structure,.prx-rich-editor__properties,.prx-rich-editor__document-panel{min-width:0;display:grid;gap:12px}.prx-rich-editor__structure{position:sticky;top:12px;border:1px solid var(--md-sys-color-outline-variant, #d7dce5);border-radius:8px;padding:12px;background:var(--md-sys-color-surface-container-lowest, #fff)}.prx-rich-editor__structure-summary{display:grid;gap:4px}.prx-rich-editor__structure-summary span{color:var(--md-sys-color-on-surface-variant, #5f6673);font-size:.82rem;font-weight:500}.prx-rich-editor__structure-list{display:grid;gap:8px}.prx-rich-editor__structure-item{display:grid;gap:3px;width:100%;padding:10px;text-align:left;color:var(--md-sys-color-on-surface, #1f2937);background:var(--md-sys-color-surface, #fff)}.prx-rich-editor__structure-item--selected{border-color:var(--md-sys-color-primary, #3154e7);background:color-mix(in srgb,var(--md-sys-color-primary, #3154e7) 8%,var(--md-sys-color-surface, #fff))}.prx-rich-editor__structure-item small{color:#991b1b;font-weight:700}.prx-rich-editor__section-header,.prx-rich-editor__node-header{display:flex;gap:16px;align-items:flex-start;justify-content:space-between}.prx-rich-editor__section-header h3,.prx-rich-editor__node-header h4{margin:0}.prx-rich-editor__section-header p,.prx-rich-editor__node-note,.prx-rich-editor__empty{margin:6px 0 0;color:var(--md-sys-color-on-surface-variant, #5f6673);font-size:.88rem}.prx-rich-editor__add-block,.prx-rich-editor__node-actions{display:flex;flex-wrap:wrap;gap:10px;align-items:flex-end}.prx-rich-editor__add-block label{min-width:180px}.prx-rich-editor__node-list{display:grid;gap:12px}.prx-rich-editor__node-card{display:grid;gap:14px;border:1px solid var(--md-sys-color-outline-variant, #d7dce5);border-radius:8px;padding:14px;background:var(--md-sys-color-surface-container-lowest, #fff)}.prx-rich-editor__node-eyebrow{margin:0 0 4px;color:var(--md-sys-color-on-surface-variant, #5f6673);font-size:.75rem;font-weight:700;text-transform:uppercase}.prx-rich-editor__node-grid{display:grid;gap:12px;grid-template-columns:repeat(2,minmax(0,1fr))}.prx-rich-editor__wide-field,.prx-rich-editor__node-note{grid-column:1 / -1}.prx-rich-editor__field-group,.prx-rich-editor__nested-editor,.prx-rich-editor__nested-node{grid-column:1 / -1;display:grid;gap:12px}.prx-rich-editor__field-group,.prx-rich-editor__nested-node{margin:0;border:1px solid var(--md-sys-color-outline-variant, #d7dce5);border-radius:8px;padding:12px}.prx-rich-editor__field-group{grid-template-columns:repeat(2,minmax(0,1fr))}.prx-rich-editor__field-group legend{padding:0 4px;font-weight:700}.prx-rich-editor__nested-header,.prx-rich-editor__nested-actions{display:flex;flex-wrap:wrap;gap:10px;align-items:center;justify-content:space-between}.prx-rich-editor__quick-actions{display:flex;flex-wrap:wrap;gap:8px}.prx-rich-editor__node-grid textarea{min-height:96px;font-family:inherit}label,.prx-rich-editor__document{display:grid;gap:7px;font-weight:700}.prx-rich-editor__checkbox-field{display:flex;gap:10px;align-items:center;align-self:end}.prx-rich-editor__checkbox-field input[type=checkbox]{width:auto}input,select,textarea{box-sizing:border-box;width:100%;border:1px solid var(--md-sys-color-outline-variant, #d7dce5);border-radius:12px;padding:10px 12px;font:inherit;color:inherit;background:var(--md-sys-color-surface, #fff)}textarea{min-height:360px;resize:vertical;font-family:Cascadia Code,Fira Code,Consolas,monospace;font-size:.86rem;line-height:1.45}.prx-rich-editor__workbench{display:grid;gap:14px;grid-template-columns:minmax(0,1.2fr) minmax(260px,.8fr)}.prx-rich-editor__inspector{display:grid;align-content:start;gap:14px;min-width:0;border:1px solid var(--md-sys-color-outline-variant, #d7dce5);border-radius:8px;padding:12px;background:var(--md-sys-color-surface-container-lowest, #fff)}.prx-rich-editor__inspector h3{margin:0 0 8px;font-size:.95rem}.prx-rich-editor__inspector dl{display:grid;gap:8px;margin:0}.prx-rich-editor__inspector dl div{display:grid;gap:2px}.prx-rich-editor__inspector dt{color:var(--md-sys-color-on-surface-variant, #5f6673);font-size:.78rem;font-weight:700}.prx-rich-editor__inspector dd{margin:0;overflow-wrap:anywhere;font-weight:600}.prx-rich-editor__preview{max-height:260px;overflow:auto;border:1px solid var(--md-sys-color-outline-variant, #d7dce5);border-radius:8px;padding:12px;background:var(--md-sys-color-surface, #fff)}.prx-rich-editor__help{margin:-8px 0 0;color:var(--md-sys-color-on-surface-variant, #5f6673);font-size:.85rem}.prx-rich-editor__error{margin:0;padding:10px 12px;border-radius:8px;color:#991b1b;background:color-mix(in srgb,#dc2626 9%,transparent)}.prx-rich-editor__error p{margin:0}.prx-rich-editor__error ul{display:grid;gap:6px;margin:8px 0 0;padding-inline-start:18px}.prx-rich-editor__error code{margin-right:4px;font-family:Cascadia Code,Fira Code,Consolas,monospace}.prx-rich-editor__actions{display:flex;flex-wrap:wrap;gap:10px}button{border:1px solid var(--md-sys-color-outline-variant, #d7dce5);border-radius:8px;padding:8px 14px;background:var(--md-sys-color-surface, #fff);color:var(--md-sys-color-primary, #3154e7);font-weight:700;cursor:pointer}button:disabled{cursor:not-allowed;opacity:.55}@media(max-width:760px){.prx-rich-editor__header,.prx-rich-editor__section-header,.prx-rich-editor__node-header,.prx-rich-editor__authoring-shell,.prx-rich-editor__grid,.prx-rich-editor__field-group,.prx-rich-editor__node-grid,.prx-rich-editor__workbench{grid-template-columns:1fr;display:grid}.prx-rich-editor__structure{position:static}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.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: i2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: PraxisRichContent, selector: "praxis-rich-content", inputs: ["document", "nodes", "context", "hostCapabilities", "layout", "rootClassName"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
9944
10807
  }
9945
10808
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisRichContentConfigEditor, decorators: [{
9946
10809
  type: Component,
@@ -10025,19 +10888,63 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
10025
10888
  }
10026
10889
  @case ('badge') {
10027
10890
  <label>
10028
- <span>{{ tx('editor.field.label', 'Label') }}</span>
10891
+ <span>{{ tx('editor.field.label', 'Label') }}</span>
10892
+ <input
10893
+ [ngModel]="getStringField(node, 'label')"
10894
+ (ngModelChange)="setString('label', $event)"
10895
+ />
10896
+ </label>
10897
+ }
10898
+ @case ('icon') {
10899
+ <label>
10900
+ <span>{{ tx('editor.field.icon', 'Icon') }}</span>
10901
+ <input
10902
+ [ngModel]="getStringField(node, 'icon')"
10903
+ (ngModelChange)="setString('icon', $event)"
10904
+ />
10905
+ </label>
10906
+ }
10907
+ @case ('avatar') {
10908
+ <label>
10909
+ <span>{{ tx('editor.field.avatarName', 'Avatar name') }}</span>
10910
+ <input
10911
+ [ngModel]="getStringField(node, 'name')"
10912
+ (ngModelChange)="setString('name', $event)"
10913
+ />
10914
+ </label>
10915
+ <label>
10916
+ <span>{{ tx('editor.field.nameExpr', 'Name binding') }}</span>
10917
+ <input
10918
+ [ngModel]="getStringField(node, 'nameExpr')"
10919
+ (ngModelChange)="setString('nameExpr', $event)"
10920
+ />
10921
+ </label>
10922
+ <label>
10923
+ <span>{{ tx('editor.field.avatarImage', 'Avatar image') }}</span>
10924
+ <input
10925
+ [ngModel]="getStringField(node, 'imageSrc')"
10926
+ (ngModelChange)="setString('imageSrc', $event)"
10927
+ />
10928
+ </label>
10929
+ <label>
10930
+ <span>{{ tx('editor.field.imageSrcExpr', 'Image binding') }}</span>
10931
+ <input
10932
+ [ngModel]="getStringField(node, 'imageSrcExpr')"
10933
+ (ngModelChange)="setString('imageSrcExpr', $event)"
10934
+ />
10935
+ </label>
10936
+ <label>
10937
+ <span>{{ tx('editor.field.initials', 'Initials') }}</span>
10029
10938
  <input
10030
- [ngModel]="getStringField(node, 'label')"
10031
- (ngModelChange)="setString('label', $event)"
10939
+ [ngModel]="getStringField(node, 'initials')"
10940
+ (ngModelChange)="setString('initials', $event)"
10032
10941
  />
10033
10942
  </label>
10034
- }
10035
- @case ('icon') {
10036
10943
  <label>
10037
- <span>{{ tx('editor.field.icon', 'Icon') }}</span>
10944
+ <span>{{ tx('editor.field.initialsExpr', 'Initials binding') }}</span>
10038
10945
  <input
10039
- [ngModel]="getStringField(node, 'icon')"
10040
- (ngModelChange)="setString('icon', $event)"
10946
+ [ngModel]="getStringField(node, 'initialsExpr')"
10947
+ (ngModelChange)="setString('initialsExpr', $event)"
10041
10948
  />
10042
10949
  </label>
10043
10950
  }
@@ -10049,6 +10956,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
10049
10956
  (ngModelChange)="setString('src', $event)"
10050
10957
  />
10051
10958
  </label>
10959
+ <label>
10960
+ <span>{{ tx('editor.field.srcExpr', 'Image URL binding') }}</span>
10961
+ <input
10962
+ [ngModel]="getStringField(node, 'srcExpr')"
10963
+ (ngModelChange)="setString('srcExpr', $event)"
10964
+ />
10965
+ </label>
10052
10966
  <label>
10053
10967
  <span>{{ tx('editor.field.alt', 'Alternative text') }}</span>
10054
10968
  <input
@@ -10056,6 +10970,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
10056
10970
  (ngModelChange)="setString('alt', $event)"
10057
10971
  />
10058
10972
  </label>
10973
+ <label>
10974
+ <span>{{ tx('editor.field.altExpr', 'Alt text binding') }}</span>
10975
+ <input
10976
+ [ngModel]="getStringField(node, 'altExpr')"
10977
+ (ngModelChange)="setString('altExpr', $event)"
10978
+ />
10979
+ </label>
10059
10980
  }
10060
10981
  @case ('link') {
10061
10982
  <label>
@@ -10065,6 +10986,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
10065
10986
  (ngModelChange)="setString('label', $event)"
10066
10987
  />
10067
10988
  </label>
10989
+ <label>
10990
+ <span>{{ tx('editor.field.labelExpr', 'Label binding') }}</span>
10991
+ <input
10992
+ [ngModel]="getStringField(node, 'labelExpr')"
10993
+ (ngModelChange)="setString('labelExpr', $event)"
10994
+ />
10995
+ </label>
10068
10996
  <label>
10069
10997
  <span>{{ tx('editor.field.href', 'Link URL') }}</span>
10070
10998
  <input
@@ -10072,6 +11000,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
10072
11000
  (ngModelChange)="setString('href', $event)"
10073
11001
  />
10074
11002
  </label>
11003
+ <label>
11004
+ <span>{{ tx('editor.field.target', 'Target') }}</span>
11005
+ <select
11006
+ [ngModel]="getStringField(node, 'target') || '_self'"
11007
+ (ngModelChange)="setString('target', $event)"
11008
+ >
11009
+ <option value="_self">{{ tx('editor.target.self', 'Same tab') }}</option>
11010
+ <option value="_blank">{{ tx('editor.target.blank', 'New tab') }}</option>
11011
+ </select>
11012
+ </label>
11013
+ <label>
11014
+ <span>{{ tx('editor.field.rel', 'Rel') }}</span>
11015
+ <input
11016
+ [ngModel]="getStringField(node, 'rel')"
11017
+ (ngModelChange)="setString('rel', $event)"
11018
+ />
11019
+ </label>
10075
11020
  }
10076
11021
  @case ('metric') {
10077
11022
  <label>
@@ -10097,6 +11042,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
10097
11042
  (ngModelChange)="setString('label', $event)"
10098
11043
  />
10099
11044
  </label>
11045
+ <label>
11046
+ <span>{{ tx('editor.field.labelExpr', 'Label binding') }}</span>
11047
+ <input
11048
+ [ngModel]="getStringField(node, 'labelExpr')"
11049
+ (ngModelChange)="setString('labelExpr', $event)"
11050
+ />
11051
+ </label>
11052
+ <label>
11053
+ <span>{{ tx('editor.field.valueExpr', 'Value binding') }}</span>
11054
+ <input
11055
+ [ngModel]="getStringField(node, 'valueExpr')"
11056
+ (ngModelChange)="setString('valueExpr', $event)"
11057
+ />
11058
+ </label>
10100
11059
  <label>
10101
11060
  <span>{{ tx('editor.field.max', 'Maximum') }}</span>
10102
11061
  <input
@@ -10387,8 +11346,58 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
10387
11346
  </header>
10388
11347
 
10389
11348
  @if (parsedDocument?.nodes?.length) {
11349
+ <div class="prx-rich-editor__authoring-shell">
11350
+ <aside
11351
+ class="prx-rich-editor__structure"
11352
+ [attr.aria-label]="tx('editor.structure', 'Document structure')"
11353
+ >
11354
+ <div class="prx-rich-editor__structure-summary">
11355
+ <strong>{{ tx('editor.structure', 'Document structure') }}</strong>
11356
+ <span>
11357
+ {{
11358
+ tx('editor.structureHelp', 'Select a block to edit its properties.')
11359
+ }}
11360
+ </span>
11361
+ </div>
11362
+ <div class="prx-rich-editor__structure-list">
11363
+ @for (node of parsedDocument?.nodes ?? []; track node.id ?? $index; let nodeIndex = $index) {
11364
+ <button
11365
+ type="button"
11366
+ class="prx-rich-editor__structure-item"
11367
+ [class.prx-rich-editor__structure-item--selected]="selectedNodeIndex === nodeIndex"
11368
+ [attr.aria-current]="selectedNodeIndex === nodeIndex ? 'true' : null"
11369
+ (click)="selectTopLevelNode(nodeIndex)"
11370
+ [attr.data-testid]="'rich-content-node-selector-' + nodeIndex"
11371
+ >
11372
+ <span class="prx-rich-editor__node-eyebrow">
11373
+ {{ tx('editor.block', 'Block') }} {{ nodeIndex + 1 }}
11374
+ </span>
11375
+ <strong>{{ tx('editor.nodeType.' + node.type, node.type) }}</strong>
11376
+ @if (getIssueMessages('$.nodes[' + nodeIndex + ']').length) {
11377
+ <small>{{ tx('editor.hasIssues', 'Needs attention') }}</small>
11378
+ }
11379
+ </button>
11380
+ }
11381
+ </div>
11382
+ </aside>
11383
+
11384
+ <div class="prx-rich-editor__properties">
11385
+ <header class="prx-rich-editor__section-header">
11386
+ <div>
11387
+ <h3>{{ tx('editor.properties', 'Properties') }}</h3>
11388
+ <p>
11389
+ {{
11390
+ tx(
11391
+ 'editor.propertiesHelp',
11392
+ 'Edit the selected block without leaving the document structure.'
11393
+ )
11394
+ }}
11395
+ </p>
11396
+ </div>
11397
+ </header>
10390
11398
  <div class="prx-rich-editor__node-list">
10391
11399
  @for (node of parsedDocument?.nodes ?? []; track node.id ?? $index; let nodeIndex = $index) {
11400
+ @if (selectedNodeIndex === nodeIndex) {
10392
11401
  <article
10393
11402
  class="prx-rich-editor__node-card"
10394
11403
  [attr.data-rich-editor-node-type]="node.type"
@@ -10527,19 +11536,124 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
10527
11536
  />
10528
11537
  </label>
10529
11538
  }
10530
- @case ('image') {
11539
+ @case ('avatar') {
11540
+ <label>
11541
+ <span>{{ tx('editor.field.avatarName', 'Avatar name') }}</span>
11542
+ <input
11543
+ [ngModel]="getStringField(node, 'name')"
11544
+ (ngModelChange)="setStringField($index, 'name', $event)"
11545
+ />
11546
+ </label>
11547
+ <label>
11548
+ <span>{{ tx('editor.field.nameExpr', 'Name binding') }}</span>
11549
+ <input
11550
+ [ngModel]="getStringField(node, 'nameExpr')"
11551
+ (ngModelChange)="setStringField($index, 'nameExpr', $event)"
11552
+ [placeholder]="tx('editor.placeholder.nameExpr', 'row.name')"
11553
+ />
11554
+ </label>
11555
+ <label>
11556
+ <span>{{ tx('editor.field.avatarImage', 'Avatar image') }}</span>
11557
+ <input
11558
+ [ngModel]="getStringField(node, 'imageSrc')"
11559
+ (ngModelChange)="setStringField($index, 'imageSrc', $event)"
11560
+ />
11561
+ </label>
11562
+ <label>
11563
+ <span>{{ tx('editor.field.imageSrcExpr', 'Image binding') }}</span>
11564
+ <input
11565
+ [ngModel]="getStringField(node, 'imageSrcExpr')"
11566
+ (ngModelChange)="setStringField($index, 'imageSrcExpr', $event)"
11567
+ [placeholder]="tx('editor.placeholder.imageSrcExpr', 'row.avatarUrl')"
11568
+ />
11569
+ </label>
11570
+ <label>
11571
+ <span>{{ tx('editor.field.initials', 'Initials') }}</span>
11572
+ <input
11573
+ [ngModel]="getStringField(node, 'initials')"
11574
+ (ngModelChange)="setStringField($index, 'initials', $event)"
11575
+ />
11576
+ </label>
11577
+ <label>
11578
+ <span>{{ tx('editor.field.initialsExpr', 'Initials binding') }}</span>
11579
+ <input
11580
+ [ngModel]="getStringField(node, 'initialsExpr')"
11581
+ (ngModelChange)="setStringField($index, 'initialsExpr', $event)"
11582
+ [placeholder]="tx('editor.placeholder.initialsExpr', 'row.initials')"
11583
+ />
11584
+ </label>
11585
+ }
11586
+ @case ('image') {
11587
+ <label>
11588
+ <span>{{ tx('editor.field.src', 'Image URL') }}</span>
11589
+ <input
11590
+ [ngModel]="getStringField(node, 'src')"
11591
+ (ngModelChange)="setStringField($index, 'src', $event)"
11592
+ />
11593
+ </label>
11594
+ <label>
11595
+ <span>{{ tx('editor.field.srcExpr', 'Image URL binding') }}</span>
11596
+ <input
11597
+ [ngModel]="getStringField(node, 'srcExpr')"
11598
+ (ngModelChange)="setStringField($index, 'srcExpr', $event)"
11599
+ [placeholder]="tx('editor.placeholder.srcExpr', 'row.imageUrl')"
11600
+ />
11601
+ </label>
11602
+ <label>
11603
+ <span>{{ tx('editor.field.alt', 'Alternative text') }}</span>
11604
+ <input
11605
+ [ngModel]="getStringField(node, 'alt')"
11606
+ (ngModelChange)="setStringField($index, 'alt', $event)"
11607
+ />
11608
+ </label>
11609
+ <label>
11610
+ <span>{{ tx('editor.field.altExpr', 'Alt text binding') }}</span>
11611
+ <input
11612
+ [ngModel]="getStringField(node, 'altExpr')"
11613
+ (ngModelChange)="setStringField($index, 'altExpr', $event)"
11614
+ [placeholder]="tx('editor.placeholder.altExpr', 'row.imageAlt')"
11615
+ />
11616
+ </label>
11617
+ }
11618
+ @case ('link') {
11619
+ <label>
11620
+ <span>{{ tx('editor.field.label', 'Label') }}</span>
11621
+ <input
11622
+ [ngModel]="getStringField(node, 'label')"
11623
+ (ngModelChange)="setStringField($index, 'label', $event)"
11624
+ />
11625
+ </label>
10531
11626
  <label>
10532
- <span>{{ tx('editor.field.src', 'Image URL') }}</span>
11627
+ <span>{{ tx('editor.field.labelExpr', 'Label binding') }}</span>
11628
+ <input
11629
+ [ngModel]="getStringField(node, 'labelExpr')"
11630
+ (ngModelChange)="setStringField($index, 'labelExpr', $event)"
11631
+ [placeholder]="tx('editor.placeholder.labelExpr', 'row.linkLabel')"
11632
+ />
11633
+ </label>
11634
+ <label class="prx-rich-editor__wide-field">
11635
+ <span>{{ tx('editor.field.href', 'Link URL') }}</span>
10533
11636
  <input
10534
- [ngModel]="getStringField(node, 'src')"
10535
- (ngModelChange)="setStringField($index, 'src', $event)"
11637
+ [ngModel]="getStringField(node, 'href')"
11638
+ (ngModelChange)="setStringField($index, 'href', $event)"
10536
11639
  />
10537
11640
  </label>
10538
11641
  <label>
10539
- <span>{{ tx('editor.field.alt', 'Alternative text') }}</span>
11642
+ <span>{{ tx('editor.field.target', 'Target') }}</span>
11643
+ <select
11644
+ [ngModel]="getStringField(node, 'target') || '_self'"
11645
+ (ngModelChange)="setStringField($index, 'target', $event)"
11646
+ >
11647
+ <option value="_self">{{ tx('editor.target.self', 'Same tab') }}</option>
11648
+ <option value="_blank">{{ tx('editor.target.blank', 'New tab') }}</option>
11649
+ </select>
11650
+ </label>
11651
+ <label>
11652
+ <span>{{ tx('editor.field.rel', 'Rel') }}</span>
10540
11653
  <input
10541
- [ngModel]="getStringField(node, 'alt')"
10542
- (ngModelChange)="setStringField($index, 'alt', $event)"
11654
+ [ngModel]="getStringField(node, 'rel')"
11655
+ (ngModelChange)="setStringField($index, 'rel', $event)"
11656
+ [placeholder]="tx('editor.placeholder.rel', 'noopener noreferrer')"
10543
11657
  />
10544
11658
  </label>
10545
11659
  }
@@ -10551,6 +11665,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
10551
11665
  (ngModelChange)="setStringField($index, 'label', $event)"
10552
11666
  />
10553
11667
  </label>
11668
+ <label>
11669
+ <span>{{ tx('editor.field.labelExpr', 'Label binding') }}</span>
11670
+ <input
11671
+ [ngModel]="getStringField(node, 'labelExpr')"
11672
+ (ngModelChange)="setStringField($index, 'labelExpr', $event)"
11673
+ [placeholder]="tx('editor.placeholder.labelExpr', 'row.status')"
11674
+ />
11675
+ </label>
10554
11676
  <label>
10555
11677
  <span>{{ tx('editor.field.valueExpr', 'Value binding') }}</span>
10556
11678
  <input
@@ -10592,6 +11714,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
10592
11714
  (ngModelChange)="setNumberField($index, 'max', $event)"
10593
11715
  />
10594
11716
  </label>
11717
+ <label class="prx-rich-editor__checkbox-field">
11718
+ <input
11719
+ type="checkbox"
11720
+ [ngModel]="getBooleanField(node, 'showPercent')"
11721
+ (ngModelChange)="setBooleanField($index, 'showPercent', $event)"
11722
+ />
11723
+ <span>{{ tx('editor.field.showPercent', 'Show percent') }}</span>
11724
+ </label>
10595
11725
  }
10596
11726
  @case ('actionButton') {
10597
11727
  <label>
@@ -10687,6 +11817,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
10687
11817
  <option value="xl">xl</option>
10688
11818
  </select>
10689
11819
  </label>
11820
+ <label class="prx-rich-editor__checkbox-field">
11821
+ <input
11822
+ type="checkbox"
11823
+ [ngModel]="getBooleanField(node, 'wrap')"
11824
+ (ngModelChange)="setBooleanField($index, 'wrap', $event)"
11825
+ />
11826
+ <span>{{ tx('editor.field.wrap', 'Wrap items') }}</span>
11827
+ </label>
10690
11828
  <div class="prx-rich-editor__nested-editor">
10691
11829
  <header class="prx-rich-editor__nested-header">
10692
11830
  <h5>{{ tx('editor.composeItems', 'Compose items') }}</h5>
@@ -10744,6 +11882,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
10744
11882
  (ngModelChange)="setStringField($index, 'title', $event)"
10745
11883
  />
10746
11884
  </label>
11885
+ <label>
11886
+ <span>{{ tx('editor.field.titleExpr', 'Title binding') }}</span>
11887
+ <input
11888
+ [ngModel]="getStringField(node, 'titleExpr')"
11889
+ (ngModelChange)="setStringField($index, 'titleExpr', $event)"
11890
+ [placeholder]="tx('editor.placeholder.titleExpr', '\${decision.title}')"
11891
+ />
11892
+ </label>
10747
11893
  <label>
10748
11894
  <span>{{ tx('editor.field.subtitle', 'Subtitle') }}</span>
10749
11895
  <input
@@ -10751,6 +11897,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
10751
11897
  (ngModelChange)="setStringField($index, 'subtitle', $event)"
10752
11898
  />
10753
11899
  </label>
11900
+ <label>
11901
+ <span>{{ tx('editor.field.subtitleExpr', 'Subtitle binding') }}</span>
11902
+ <input
11903
+ [ngModel]="getStringField(node, 'subtitleExpr')"
11904
+ (ngModelChange)="setStringField($index, 'subtitleExpr', $event)"
11905
+ [placeholder]="tx('editor.placeholder.subtitleExpr', '\${decision.policySource}')"
11906
+ />
11907
+ </label>
10754
11908
  <label>
10755
11909
  <span>{{ tx('editor.field.variant', 'Variant') }}</span>
10756
11910
  <select
@@ -10788,6 +11942,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
10788
11942
  <option value="lg">{{ tx('editor.size.lg', 'Large') }}</option>
10789
11943
  </select>
10790
11944
  </label>
11945
+ <label>
11946
+ <span>{{ tx('editor.field.density', 'Density') }}</span>
11947
+ <select
11948
+ [ngModel]="getStringField(node, 'density') || 'comfortable'"
11949
+ (ngModelChange)="setStringField($index, 'density', $event)"
11950
+ >
11951
+ <option value="comfortable">{{ tx('editor.density.comfortable', 'Comfortable') }}</option>
11952
+ <option value="compact">{{ tx('editor.density.compact', 'Compact') }}</option>
11953
+ </select>
11954
+ </label>
10791
11955
  <label>
10792
11956
  <span>{{ tx('editor.field.orientation', 'Orientation') }}</span>
10793
11957
  <select
@@ -10798,6 +11962,38 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
10798
11962
  <option value="horizontal">{{ tx('editor.orientation.horizontal', 'Horizontal') }}</option>
10799
11963
  </select>
10800
11964
  </label>
11965
+ <label class="prx-rich-editor__checkbox-field">
11966
+ <input
11967
+ type="checkbox"
11968
+ [ngModel]="getBooleanField(node, 'loading')"
11969
+ (ngModelChange)="setBooleanField($index, 'loading', $event)"
11970
+ />
11971
+ <span>{{ tx('editor.field.loading', 'Loading') }}</span>
11972
+ </label>
11973
+ <label>
11974
+ <span>{{ tx('editor.field.loadingExpr', 'Loading binding') }}</span>
11975
+ <input
11976
+ [ngModel]="getStringField(node, 'loadingExpr')"
11977
+ (ngModelChange)="setStringField($index, 'loadingExpr', $event)"
11978
+ [placeholder]="tx('editor.placeholder.loadingExpr', 'row.loading')"
11979
+ />
11980
+ </label>
11981
+ <label class="prx-rich-editor__checkbox-field">
11982
+ <input
11983
+ type="checkbox"
11984
+ [ngModel]="getBooleanField(node, 'active')"
11985
+ (ngModelChange)="setBooleanField($index, 'active', $event)"
11986
+ />
11987
+ <span>{{ tx('editor.field.active', 'Active') }}</span>
11988
+ </label>
11989
+ <label>
11990
+ <span>{{ tx('editor.field.activeExpr', 'Active binding') }}</span>
11991
+ <input
11992
+ [ngModel]="getStringField(node, 'activeExpr')"
11993
+ (ngModelChange)="setStringField($index, 'activeExpr', $event)"
11994
+ [placeholder]="tx('editor.placeholder.activeExpr', 'row.active')"
11995
+ />
11996
+ </label>
10801
11997
  <label>
10802
11998
  <span>{{ tx('editor.field.mediaKind', 'Media') }}</span>
10803
11999
  <select
@@ -10819,6 +12015,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
10819
12015
  (ngModelChange)="setCardMediaField(nodeIndex, 'src', $event)"
10820
12016
  />
10821
12017
  </label>
12018
+ <label>
12019
+ <span>{{ tx('editor.field.mediaSrcExpr', 'Media source binding') }}</span>
12020
+ <input
12021
+ [ngModel]="getCardMediaField(nodeIndex, 'srcExpr')"
12022
+ (ngModelChange)="setCardMediaField(nodeIndex, 'srcExpr', $event)"
12023
+ [placeholder]="tx('editor.placeholder.mediaSrcExpr', 'row.mediaUrl')"
12024
+ />
12025
+ </label>
10822
12026
  <label>
10823
12027
  <span>{{ tx('editor.field.mediaAlt', 'Media alt text') }}</span>
10824
12028
  <input
@@ -10826,6 +12030,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
10826
12030
  (ngModelChange)="setCardMediaField(nodeIndex, 'alt', $event)"
10827
12031
  />
10828
12032
  </label>
12033
+ <label>
12034
+ <span>{{ tx('editor.field.mediaAltExpr', 'Media alt binding') }}</span>
12035
+ <input
12036
+ [ngModel]="getCardMediaField(nodeIndex, 'altExpr')"
12037
+ (ngModelChange)="setCardMediaField(nodeIndex, 'altExpr', $event)"
12038
+ [placeholder]="tx('editor.placeholder.mediaAltExpr', 'row.mediaAlt')"
12039
+ />
12040
+ </label>
10829
12041
  <label>
10830
12042
  <span>{{ tx('editor.field.mediaIcon', 'Media icon') }}</span>
10831
12043
  <input
@@ -10833,6 +12045,29 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
10833
12045
  (ngModelChange)="setCardMediaField(nodeIndex, 'icon', $event)"
10834
12046
  />
10835
12047
  </label>
12048
+ <label>
12049
+ <span>{{ tx('editor.field.mediaLabel', 'Media label') }}</span>
12050
+ <input
12051
+ [ngModel]="getCardMediaField(nodeIndex, 'label')"
12052
+ (ngModelChange)="setCardMediaField(nodeIndex, 'label', $event)"
12053
+ />
12054
+ </label>
12055
+ <label>
12056
+ <span>{{ tx('editor.field.mediaLabelExpr', 'Media label binding') }}</span>
12057
+ <input
12058
+ [ngModel]="getCardMediaField(nodeIndex, 'labelExpr')"
12059
+ (ngModelChange)="setCardMediaField(nodeIndex, 'labelExpr', $event)"
12060
+ [placeholder]="tx('editor.placeholder.mediaLabelExpr', 'row.mediaLabel')"
12061
+ />
12062
+ </label>
12063
+ <label>
12064
+ <span>{{ tx('editor.field.aspectRatio', 'Aspect ratio') }}</span>
12065
+ <input
12066
+ [ngModel]="getCardMediaField(nodeIndex, 'aspectRatio')"
12067
+ (ngModelChange)="setCardMediaField(nodeIndex, 'aspectRatio', $event)"
12068
+ [placeholder]="tx('editor.placeholder.aspectRatio', '16 / 9')"
12069
+ />
12070
+ </label>
10836
12071
  <label>
10837
12072
  <span>{{ tx('editor.field.mediaPlacement', 'Media placement') }}</span>
10838
12073
  <select
@@ -10846,6 +12081,76 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
10846
12081
  </select>
10847
12082
  </label>
10848
12083
  }
12084
+ <label>
12085
+ <span>{{ tx('editor.field.interactionMode', 'Interaction mode') }}</span>
12086
+ <select
12087
+ [ngModel]="getCardInteractionField(nodeIndex, 'mode') || 'none'"
12088
+ (ngModelChange)="setCardInteractionField(nodeIndex, 'mode', $event)"
12089
+ >
12090
+ <option value="none">{{ tx('editor.interaction.none', 'None') }}</option>
12091
+ <option value="action">{{ tx('editor.interaction.action', 'Action') }}</option>
12092
+ <option value="selectable">{{ tx('editor.interaction.selectable', 'Selectable') }}</option>
12093
+ </select>
12094
+ </label>
12095
+ <label class="prx-rich-editor__checkbox-field">
12096
+ <input
12097
+ type="checkbox"
12098
+ [ngModel]="getCardInteractionBooleanField(nodeIndex, 'selected')"
12099
+ (ngModelChange)="setCardInteractionBooleanField(nodeIndex, 'selected', $event)"
12100
+ />
12101
+ <span>{{ tx('editor.field.selected', 'Selected') }}</span>
12102
+ </label>
12103
+ <label>
12104
+ <span>{{ tx('editor.field.selectedExpr', 'Selected binding') }}</span>
12105
+ <input
12106
+ [ngModel]="getCardInteractionField(nodeIndex, 'selectedExpr')"
12107
+ (ngModelChange)="setCardInteractionField(nodeIndex, 'selectedExpr', $event)"
12108
+ [placeholder]="tx('editor.placeholder.selectedExpr', 'row.selected')"
12109
+ />
12110
+ </label>
12111
+ <label>
12112
+ <span>{{ tx('editor.field.interactionActionId', 'Interaction action ID') }}</span>
12113
+ <input
12114
+ [ngModel]="getCardInteractionActionField(nodeIndex, 'actionId')"
12115
+ (ngModelChange)="setCardInteractionActionField(nodeIndex, 'actionId', $event)"
12116
+ [placeholder]="tx('editor.placeholder.actionId', 'host.action.open')"
12117
+ />
12118
+ </label>
12119
+ <label>
12120
+ <span>{{ tx('editor.field.payloadExpr', 'Payload binding') }}</span>
12121
+ <input
12122
+ [ngModel]="getCardInteractionActionField(nodeIndex, 'payloadExpr')"
12123
+ (ngModelChange)="setCardInteractionActionField(nodeIndex, 'payloadExpr', $event)"
12124
+ [placeholder]="tx('editor.placeholder.payloadExpr', 'row')"
12125
+ />
12126
+ </label>
12127
+ <label>
12128
+ <span>{{ tx('editor.field.accessibilityRole', 'Accessibility role') }}</span>
12129
+ <select
12130
+ [ngModel]="getCardAccessibilityField(nodeIndex, 'role')"
12131
+ (ngModelChange)="setCardAccessibilityField(nodeIndex, 'role', $event)"
12132
+ >
12133
+ <option value="">{{ tx('editor.none', 'None') }}</option>
12134
+ <option value="article">{{ tx('editor.accessibilityRole.article', 'Article') }}</option>
12135
+ <option value="group">{{ tx('editor.accessibilityRole.group', 'Group') }}</option>
12136
+ <option value="button">{{ tx('editor.accessibilityRole.button', 'Button') }}</option>
12137
+ </select>
12138
+ </label>
12139
+ <label>
12140
+ <span>{{ tx('editor.field.ariaLabel', 'Accessible label') }}</span>
12141
+ <input
12142
+ [ngModel]="getCardAccessibilityField(nodeIndex, 'ariaLabel')"
12143
+ (ngModelChange)="setCardAccessibilityField(nodeIndex, 'ariaLabel', $event)"
12144
+ />
12145
+ </label>
12146
+ <label>
12147
+ <span>{{ tx('editor.field.ariaLabelExpr', 'Accessible label binding') }}</span>
12148
+ <input
12149
+ [ngModel]="getCardAccessibilityField(nodeIndex, 'ariaLabelExpr')"
12150
+ (ngModelChange)="setCardAccessibilityField(nodeIndex, 'ariaLabelExpr', $event)"
12151
+ [placeholder]="tx('editor.placeholder.ariaLabelExpr', 'row.ariaLabel')"
12152
+ />
12153
+ </label>
10849
12154
  <div class="prx-rich-editor__nested-editor">
10850
12155
  <header class="prx-rich-editor__nested-header">
10851
12156
  <h5>{{ tx('editor.cardContent', 'Card body') }}</h5>
@@ -11291,6 +12596,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
11291
12596
  (ngModelChange)="setTabsItemField(nodeIndex, $index, 'label', $event)"
11292
12597
  />
11293
12598
  </label>
12599
+ <label>
12600
+ <span>{{ tx('editor.field.labelExpr', 'Label binding') }}</span>
12601
+ <input
12602
+ [ngModel]="getTabsItemField(nodeIndex, $index, 'labelExpr')"
12603
+ (ngModelChange)="setTabsItemField(nodeIndex, $index, 'labelExpr', $event)"
12604
+ [placeholder]="tx('editor.placeholder.tabLabelExpr', 'row.tabLabel')"
12605
+ />
12606
+ </label>
11294
12607
  <label>
11295
12608
  <span>{{ tx('editor.field.icon', 'Icon') }}</span>
11296
12609
  <input
@@ -11305,6 +12618,48 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
11305
12618
  (ngModelChange)="setTabsItemField(nodeIndex, $index, 'badge', $event)"
11306
12619
  />
11307
12620
  </label>
12621
+ <label>
12622
+ <span>{{ tx('editor.field.badgeExpr', 'Badge binding') }}</span>
12623
+ <input
12624
+ [ngModel]="getTabsItemField(nodeIndex, $index, 'badgeExpr')"
12625
+ (ngModelChange)="setTabsItemField(nodeIndex, $index, 'badgeExpr', $event)"
12626
+ [placeholder]="tx('editor.placeholder.badgeExpr', 'row.badge')"
12627
+ />
12628
+ </label>
12629
+ <label>
12630
+ <span>{{ tx('editor.field.requiresCapabilities', 'Capabilities') }}</span>
12631
+ <input
12632
+ [ngModel]="getTabsItemCapabilityList(nodeIndex, $index)"
12633
+ (ngModelChange)="setTabsItemCapabilityList(nodeIndex, $index, $event)"
12634
+ [placeholder]="tx('editor.placeholder.requiresCapabilities', 'form.mode.edit, employee.open')"
12635
+ />
12636
+ </label>
12637
+ <label>
12638
+ <span>{{ tx('editor.field.capabilityMode', 'Capability mode') }}</span>
12639
+ <select
12640
+ [ngModel]="getTabsItemField(nodeIndex, $index, 'capabilityMode') || 'all'"
12641
+ (ngModelChange)="setTabsItemField(nodeIndex, $index, 'capabilityMode', $event)"
12642
+ >
12643
+ <option value="all">{{ tx('editor.capabilityMode.all', 'All') }}</option>
12644
+ <option value="any">{{ tx('editor.capabilityMode.any', 'Any') }}</option>
12645
+ </select>
12646
+ </label>
12647
+ <label>
12648
+ <span>{{ tx('editor.field.visibleWhenPath', 'Context path') }}</span>
12649
+ <input
12650
+ [ngModel]="getTabsItemVisibleWhenPath(nodeIndex, $index)"
12651
+ (ngModelChange)="setTabsItemVisibleWhenPath(nodeIndex, $index, $event)"
12652
+ [placeholder]="tx('editor.placeholder.visibleWhenPath', 'row.active')"
12653
+ />
12654
+ </label>
12655
+ <label>
12656
+ <span>{{ tx('editor.field.visibleWhenValue', 'Expected value') }}</span>
12657
+ <input
12658
+ [ngModel]="getTabsItemVisibleWhenValue(nodeIndex, $index)"
12659
+ (ngModelChange)="setTabsItemVisibleWhenValue(nodeIndex, $index, $event)"
12660
+ [placeholder]="tx('editor.placeholder.visibleWhenValue', 'true')"
12661
+ />
12662
+ </label>
11308
12663
  </div>
11309
12664
  }
11310
12665
  </div>
@@ -11967,24 +13322,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
11967
13322
  <header class="prx-rich-editor__nested-header">
11968
13323
  <h5>{{ tx('editor.decisionEvidence', 'Evidence') }}</h5>
11969
13324
  <div class="prx-rich-editor__quick-actions">
11970
- <button type="button" (click)="addDecisionPackageEvidence($index)">
13325
+ <button type="button" (click)="addDecisionPackageEvidence(nodeIndex)">
11971
13326
  {{ tx('editor.addEvidence', 'Add evidence') }}
11972
13327
  </button>
11973
13328
  </div>
11974
13329
  </header>
11975
- @for (evidence of getDecisionPackageEvidence($index); track evidence.id ?? $index; let evidenceIndex = $index) {
13330
+ @for (evidence of getDecisionPackageEvidence(nodeIndex); track evidence.id ?? $index; let evidenceIndex = $index) {
11976
13331
  <div class="prx-rich-editor__nested-node">
11977
13332
  <div class="prx-rich-editor__nested-actions">
11978
13333
  <strong>{{ tx('editor.evidence', 'Evidence') }} {{ evidenceIndex + 1 }}</strong>
11979
- @if (isRemovalPending('nodes.' + $index + '.evidence', evidenceIndex)) {
11980
- <button type="button" (click)="removeDecisionPackageEvidence($index, evidenceIndex)">
13334
+ @if (isRemovalPending('nodes.' + nodeIndex + '.evidence', evidenceIndex)) {
13335
+ <button type="button" (click)="removeDecisionPackageEvidence(nodeIndex, evidenceIndex)">
11981
13336
  {{ tx('editor.confirmRemove', 'Confirm remove') }}
11982
13337
  </button>
11983
13338
  <button type="button" (click)="cancelRemoval()">
11984
13339
  {{ tx('editor.cancelRemove', 'Cancel') }}
11985
13340
  </button>
11986
13341
  } @else {
11987
- <button type="button" (click)="requestNestedRemoval('nodes.' + $index + '.evidence', evidenceIndex)">
13342
+ <button type="button" (click)="requestNestedRemoval('nodes.' + nodeIndex + '.evidence', evidenceIndex)">
11988
13343
  {{ tx('editor.removeBlock', 'Remove') }}
11989
13344
  </button>
11990
13345
  }
@@ -11993,37 +13348,37 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
11993
13348
  <label>
11994
13349
  <span>{{ tx('editor.field.itemId', 'Item ID') }}</span>
11995
13350
  <input
11996
- [ngModel]="getDecisionPackageEvidenceField($index, evidenceIndex, 'id')"
11997
- (ngModelChange)="setDecisionPackageEvidenceField($index, evidenceIndex, 'id', $event)"
13351
+ [ngModel]="getDecisionPackageEvidenceField(nodeIndex, evidenceIndex, 'id')"
13352
+ (ngModelChange)="setDecisionPackageEvidenceField(nodeIndex, evidenceIndex, 'id', $event)"
11998
13353
  />
11999
13354
  </label>
12000
13355
  <label>
12001
13356
  <span>{{ tx('editor.field.label', 'Label') }}</span>
12002
13357
  <input
12003
- [ngModel]="getDecisionPackageEvidenceField($index, evidenceIndex, 'label')"
12004
- (ngModelChange)="setDecisionPackageEvidenceField($index, evidenceIndex, 'label', $event)"
13358
+ [ngModel]="getDecisionPackageEvidenceField(nodeIndex, evidenceIndex, 'label')"
13359
+ (ngModelChange)="setDecisionPackageEvidenceField(nodeIndex, evidenceIndex, 'label', $event)"
12005
13360
  />
12006
13361
  </label>
12007
13362
  <label>
12008
13363
  <span>{{ tx('editor.field.labelExpr', 'Label binding') }}</span>
12009
13364
  <input
12010
- [ngModel]="getDecisionPackageEvidenceField($index, evidenceIndex, 'labelExpr')"
12011
- (ngModelChange)="setDecisionPackageEvidenceField($index, evidenceIndex, 'labelExpr', $event)"
13365
+ [ngModel]="getDecisionPackageEvidenceField(nodeIndex, evidenceIndex, 'labelExpr')"
13366
+ (ngModelChange)="setDecisionPackageEvidenceField(nodeIndex, evidenceIndex, 'labelExpr', $event)"
12012
13367
  [placeholder]="tx('editor.placeholder.evidenceLabelExpr', '\${evidence.label}')"
12013
13368
  />
12014
13369
  </label>
12015
13370
  <label>
12016
13371
  <span>{{ tx('editor.field.source', 'Source') }}</span>
12017
13372
  <input
12018
- [ngModel]="getDecisionPackageEvidenceField($index, evidenceIndex, 'source')"
12019
- (ngModelChange)="setDecisionPackageEvidenceField($index, evidenceIndex, 'source', $event)"
13373
+ [ngModel]="getDecisionPackageEvidenceField(nodeIndex, evidenceIndex, 'source')"
13374
+ (ngModelChange)="setDecisionPackageEvidenceField(nodeIndex, evidenceIndex, 'source', $event)"
12020
13375
  />
12021
13376
  </label>
12022
13377
  <label>
12023
13378
  <span>{{ tx('editor.field.sourceExpr', 'Source binding') }}</span>
12024
13379
  <input
12025
- [ngModel]="getDecisionPackageEvidenceField($index, evidenceIndex, 'sourceExpr')"
12026
- (ngModelChange)="setDecisionPackageEvidenceField($index, evidenceIndex, 'sourceExpr', $event)"
13380
+ [ngModel]="getDecisionPackageEvidenceField(nodeIndex, evidenceIndex, 'sourceExpr')"
13381
+ (ngModelChange)="setDecisionPackageEvidenceField(nodeIndex, evidenceIndex, 'sourceExpr', $event)"
12027
13382
  [placeholder]="tx('editor.placeholder.evidenceSourceExpr', '\${evidence.source}')"
12028
13383
  />
12029
13384
  </label>
@@ -12036,27 +13391,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
12036
13391
  <header class="prx-rich-editor__nested-header">
12037
13392
  <h5>{{ tx('editor.decisionPrimaryAction', 'Primary action') }}</h5>
12038
13393
  <div class="prx-rich-editor__quick-actions">
12039
- @if (getDecisionPackagePrimaryAction($index)) {
12040
- <button type="button" (click)="removeDecisionPackagePrimaryAction($index)">
13394
+ @if (getDecisionPackagePrimaryAction(nodeIndex)) {
13395
+ <button type="button" (click)="removeDecisionPackagePrimaryAction(nodeIndex)">
12041
13396
  {{ tx('editor.removeBlock', 'Remove') }}
12042
13397
  </button>
12043
13398
  } @else {
12044
- <button type="button" (click)="addDecisionPackagePrimaryActionPreset($index, 'primary')">
13399
+ <button type="button" (click)="addDecisionPackagePrimaryActionPreset(nodeIndex, 'primary')">
12045
13400
  {{ tx('editor.addPrimaryAction', 'Add primary action') }}
12046
13401
  </button>
12047
- <button type="button" (click)="addDecisionPackagePrimaryAction($index)">
13402
+ <button type="button" (click)="addDecisionPackagePrimaryAction(nodeIndex)">
12048
13403
  {{ tx('editor.addAction', 'Add action') }}
12049
13404
  </button>
12050
13405
  }
12051
13406
  </div>
12052
13407
  </header>
12053
- @if (getDecisionPackagePrimaryAction($index); as primaryAction) {
13408
+ @if (getDecisionPackagePrimaryAction(nodeIndex); as primaryAction) {
12054
13409
  <div class="prx-rich-editor__node-grid">
12055
13410
  <ng-container
12056
13411
  *ngTemplateOutlet="actionFields; context: {
12057
13412
  action: primaryAction,
12058
- path: '$.nodes[' + $index + '].primaryAction',
12059
- setField: setDecisionPackagePrimaryActionField.bind(this, $index)
13413
+ path: '$.nodes[' + nodeIndex + '].primaryAction',
13414
+ setField: setDecisionPackagePrimaryActionField.bind(this, nodeIndex)
12060
13415
  }"
12061
13416
  ></ng-container>
12062
13417
  </div>
@@ -12774,8 +14129,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
12774
14129
  </fieldset>
12775
14130
  </div>
12776
14131
  </article>
14132
+ }
12777
14133
  }
12778
14134
  </div>
14135
+ </div>
14136
+ </div>
12779
14137
  } @else {
12780
14138
  <p class="prx-rich-editor__empty">
12781
14139
  {{ tx('editor.noBlocks', 'No blocks yet. Add a block to start authoring this document.') }}
@@ -12783,9 +14141,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
12783
14141
  }
12784
14142
  </section>
12785
14143
 
12786
- <label class="prx-rich-editor__document">
12787
- <span>{{ tx('editor.document', 'Advanced JSON') }}</span>
14144
+ <section class="prx-rich-editor__document-panel">
14145
+ <header class="prx-rich-editor__section-header">
14146
+ <div>
14147
+ <h3>{{ tx('editor.document', 'Advanced JSON') }}</h3>
14148
+ <p>
14149
+ {{
14150
+ tx(
14151
+ 'editor.documentAdvancedHelp',
14152
+ 'Use JSON for diagnostics, migration and advanced fields that are not yet surfaced by guided controls.'
14153
+ )
14154
+ }}
14155
+ </p>
14156
+ </div>
14157
+ </header>
12788
14158
  <div class="prx-rich-editor__workbench">
14159
+ <label class="prx-rich-editor__document">
14160
+ <span>{{ tx('editor.documentJson', 'Canonical document JSON') }}</span>
12789
14161
  <textarea
12790
14162
  name="rich-content-document"
12791
14163
  [(ngModel)]="documentJson"
@@ -12799,6 +14171,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
12799
14171
  spellcheck="false"
12800
14172
  data-testid="rich-content-document-input"
12801
14173
  ></textarea>
14174
+ </label>
12802
14175
 
12803
14176
  <aside
12804
14177
  class="prx-rich-editor__inspector"
@@ -12820,7 +14193,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
12820
14193
 
12821
14194
  @if (parsedDocument) {
12822
14195
  <section>
12823
- <h3>{{ tx('editor.preview', 'Preview') }}</h3>
14196
+ <h3>{{ tx('editor.preview', 'Preview') }}</h3>
12824
14197
  <div class="prx-rich-editor__preview">
12825
14198
  <praxis-rich-content
12826
14199
  [document]="parsedDocument"
@@ -12832,7 +14205,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
12832
14205
  }
12833
14206
  </aside>
12834
14207
  </div>
12835
- </label>
14208
+ </section>
12836
14209
 
12837
14210
  @if (errorMessage) {
12838
14211
  <div
@@ -12872,7 +14245,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
12872
14245
  </button>
12873
14246
  </div>
12874
14247
  </section>
12875
- `, styles: [":host{display:block;min-width:0}.prx-rich-editor{display:grid;gap:18px;color:var(--md-sys-color-on-surface, #1f2937)}.prx-rich-editor__header{display:flex;gap:16px;align-items:flex-start;justify-content:space-between}.prx-rich-editor__header h2{margin:0;font-size:1.25rem}.prx-rich-editor__header p{margin:6px 0 0;color:var(--md-sys-color-on-surface-variant, #5f6673)}.prx-rich-editor__eyebrow{margin:0 0 4px;text-transform:uppercase;letter-spacing:.08em;font-size:.72rem;color:var(--md-sys-color-primary, #3154e7)}.prx-rich-editor__status{flex:0 0 auto;padding:6px 10px;border-radius:999px;background:color-mix(in srgb,#16a34a 12%,transparent);color:#166534;font-size:.78rem;font-weight:700}.prx-rich-editor__status.invalid{background:color-mix(in srgb,#dc2626 12%,transparent);color:#991b1b}.prx-rich-editor__grid{display:grid;gap:14px;grid-template-columns:minmax(0,180px) minmax(0,1fr)}.prx-rich-editor__blocks{display:grid;gap:14px}.prx-rich-editor__section-header,.prx-rich-editor__node-header{display:flex;gap:16px;align-items:flex-start;justify-content:space-between}.prx-rich-editor__section-header h3,.prx-rich-editor__node-header h4{margin:0}.prx-rich-editor__section-header p,.prx-rich-editor__node-note,.prx-rich-editor__empty{margin:6px 0 0;color:var(--md-sys-color-on-surface-variant, #5f6673);font-size:.88rem}.prx-rich-editor__add-block,.prx-rich-editor__node-actions{display:flex;flex-wrap:wrap;gap:10px;align-items:flex-end}.prx-rich-editor__add-block label{min-width:180px}.prx-rich-editor__node-list{display:grid;gap:12px}.prx-rich-editor__node-card{display:grid;gap:14px;border:1px solid var(--md-sys-color-outline-variant, #d7dce5);border-radius:8px;padding:14px;background:var(--md-sys-color-surface-container-lowest, #fff)}.prx-rich-editor__node-eyebrow{margin:0 0 4px;color:var(--md-sys-color-on-surface-variant, #5f6673);font-size:.75rem;font-weight:700;text-transform:uppercase}.prx-rich-editor__node-grid{display:grid;gap:12px;grid-template-columns:repeat(2,minmax(0,1fr))}.prx-rich-editor__wide-field,.prx-rich-editor__node-note{grid-column:1 / -1}.prx-rich-editor__field-group,.prx-rich-editor__nested-editor,.prx-rich-editor__nested-node{grid-column:1 / -1;display:grid;gap:12px}.prx-rich-editor__field-group,.prx-rich-editor__nested-node{margin:0;border:1px solid var(--md-sys-color-outline-variant, #d7dce5);border-radius:8px;padding:12px}.prx-rich-editor__field-group{grid-template-columns:repeat(2,minmax(0,1fr))}.prx-rich-editor__field-group legend{padding:0 4px;font-weight:700}.prx-rich-editor__nested-header,.prx-rich-editor__nested-actions{display:flex;flex-wrap:wrap;gap:10px;align-items:center;justify-content:space-between}.prx-rich-editor__quick-actions{display:flex;flex-wrap:wrap;gap:8px}.prx-rich-editor__node-grid textarea{min-height:96px;font-family:inherit}label,.prx-rich-editor__document{display:grid;gap:7px;font-weight:700}.prx-rich-editor__checkbox-field{display:flex;gap:10px;align-items:center;align-self:end}.prx-rich-editor__checkbox-field input[type=checkbox]{width:auto}input,select,textarea{box-sizing:border-box;width:100%;border:1px solid var(--md-sys-color-outline-variant, #d7dce5);border-radius:12px;padding:10px 12px;font:inherit;color:inherit;background:var(--md-sys-color-surface, #fff)}textarea{min-height:360px;resize:vertical;font-family:Cascadia Code,Fira Code,Consolas,monospace;font-size:.86rem;line-height:1.45}.prx-rich-editor__workbench{display:grid;gap:14px;grid-template-columns:minmax(0,1.2fr) minmax(260px,.8fr)}.prx-rich-editor__inspector{display:grid;align-content:start;gap:14px;min-width:0;border:1px solid var(--md-sys-color-outline-variant, #d7dce5);border-radius:8px;padding:12px;background:var(--md-sys-color-surface-container-lowest, #fff)}.prx-rich-editor__inspector h3{margin:0 0 8px;font-size:.95rem}.prx-rich-editor__inspector dl{display:grid;gap:8px;margin:0}.prx-rich-editor__inspector dl div{display:grid;gap:2px}.prx-rich-editor__inspector dt{color:var(--md-sys-color-on-surface-variant, #5f6673);font-size:.78rem;font-weight:700}.prx-rich-editor__inspector dd{margin:0;overflow-wrap:anywhere;font-weight:600}.prx-rich-editor__preview{max-height:260px;overflow:auto;border:1px solid var(--md-sys-color-outline-variant, #d7dce5);border-radius:8px;padding:12px;background:var(--md-sys-color-surface, #fff)}.prx-rich-editor__help{margin:-8px 0 0;color:var(--md-sys-color-on-surface-variant, #5f6673);font-size:.85rem}.prx-rich-editor__error{margin:0;padding:10px 12px;border-radius:8px;color:#991b1b;background:color-mix(in srgb,#dc2626 9%,transparent)}.prx-rich-editor__error p{margin:0}.prx-rich-editor__error ul{display:grid;gap:6px;margin:8px 0 0;padding-inline-start:18px}.prx-rich-editor__error code{margin-right:4px;font-family:Cascadia Code,Fira Code,Consolas,monospace}.prx-rich-editor__actions{display:flex;flex-wrap:wrap;gap:10px}button{border:1px solid var(--md-sys-color-outline-variant, #d7dce5);border-radius:8px;padding:8px 14px;background:var(--md-sys-color-surface, #fff);color:var(--md-sys-color-primary, #3154e7);font-weight:700;cursor:pointer}button:disabled{cursor:not-allowed;opacity:.55}@media(max-width:760px){.prx-rich-editor__header,.prx-rich-editor__section-header,.prx-rich-editor__node-header,.prx-rich-editor__grid,.prx-rich-editor__field-group,.prx-rich-editor__node-grid,.prx-rich-editor__workbench{grid-template-columns:1fr;display:grid}}\n"] }]
14248
+ `, styles: [":host{display:block;min-width:0}.prx-rich-editor{display:grid;gap:18px;color:var(--md-sys-color-on-surface, #1f2937)}.prx-rich-editor__header{display:flex;gap:16px;align-items:flex-start;justify-content:space-between}.prx-rich-editor__header h2{margin:0;font-size:1.25rem}.prx-rich-editor__header p{margin:6px 0 0;color:var(--md-sys-color-on-surface-variant, #5f6673)}.prx-rich-editor__eyebrow{margin:0 0 4px;text-transform:uppercase;letter-spacing:.08em;font-size:.72rem;color:var(--md-sys-color-primary, #3154e7)}.prx-rich-editor__status{flex:0 0 auto;padding:6px 10px;border-radius:999px;background:color-mix(in srgb,#16a34a 12%,transparent);color:#166534;font-size:.78rem;font-weight:700}.prx-rich-editor__status.invalid{background:color-mix(in srgb,#dc2626 12%,transparent);color:#991b1b}.prx-rich-editor__grid{display:grid;gap:14px;grid-template-columns:minmax(0,180px) minmax(0,1fr)}.prx-rich-editor__blocks{display:grid;gap:14px}.prx-rich-editor__authoring-shell{display:grid;gap:14px;grid-template-columns:minmax(220px,.34fr) minmax(0,1fr);align-items:start}.prx-rich-editor__structure,.prx-rich-editor__properties,.prx-rich-editor__document-panel{min-width:0;display:grid;gap:12px}.prx-rich-editor__structure{position:sticky;top:12px;border:1px solid var(--md-sys-color-outline-variant, #d7dce5);border-radius:8px;padding:12px;background:var(--md-sys-color-surface-container-lowest, #fff)}.prx-rich-editor__structure-summary{display:grid;gap:4px}.prx-rich-editor__structure-summary span{color:var(--md-sys-color-on-surface-variant, #5f6673);font-size:.82rem;font-weight:500}.prx-rich-editor__structure-list{display:grid;gap:8px}.prx-rich-editor__structure-item{display:grid;gap:3px;width:100%;padding:10px;text-align:left;color:var(--md-sys-color-on-surface, #1f2937);background:var(--md-sys-color-surface, #fff)}.prx-rich-editor__structure-item--selected{border-color:var(--md-sys-color-primary, #3154e7);background:color-mix(in srgb,var(--md-sys-color-primary, #3154e7) 8%,var(--md-sys-color-surface, #fff))}.prx-rich-editor__structure-item small{color:#991b1b;font-weight:700}.prx-rich-editor__section-header,.prx-rich-editor__node-header{display:flex;gap:16px;align-items:flex-start;justify-content:space-between}.prx-rich-editor__section-header h3,.prx-rich-editor__node-header h4{margin:0}.prx-rich-editor__section-header p,.prx-rich-editor__node-note,.prx-rich-editor__empty{margin:6px 0 0;color:var(--md-sys-color-on-surface-variant, #5f6673);font-size:.88rem}.prx-rich-editor__add-block,.prx-rich-editor__node-actions{display:flex;flex-wrap:wrap;gap:10px;align-items:flex-end}.prx-rich-editor__add-block label{min-width:180px}.prx-rich-editor__node-list{display:grid;gap:12px}.prx-rich-editor__node-card{display:grid;gap:14px;border:1px solid var(--md-sys-color-outline-variant, #d7dce5);border-radius:8px;padding:14px;background:var(--md-sys-color-surface-container-lowest, #fff)}.prx-rich-editor__node-eyebrow{margin:0 0 4px;color:var(--md-sys-color-on-surface-variant, #5f6673);font-size:.75rem;font-weight:700;text-transform:uppercase}.prx-rich-editor__node-grid{display:grid;gap:12px;grid-template-columns:repeat(2,minmax(0,1fr))}.prx-rich-editor__wide-field,.prx-rich-editor__node-note{grid-column:1 / -1}.prx-rich-editor__field-group,.prx-rich-editor__nested-editor,.prx-rich-editor__nested-node{grid-column:1 / -1;display:grid;gap:12px}.prx-rich-editor__field-group,.prx-rich-editor__nested-node{margin:0;border:1px solid var(--md-sys-color-outline-variant, #d7dce5);border-radius:8px;padding:12px}.prx-rich-editor__field-group{grid-template-columns:repeat(2,minmax(0,1fr))}.prx-rich-editor__field-group legend{padding:0 4px;font-weight:700}.prx-rich-editor__nested-header,.prx-rich-editor__nested-actions{display:flex;flex-wrap:wrap;gap:10px;align-items:center;justify-content:space-between}.prx-rich-editor__quick-actions{display:flex;flex-wrap:wrap;gap:8px}.prx-rich-editor__node-grid textarea{min-height:96px;font-family:inherit}label,.prx-rich-editor__document{display:grid;gap:7px;font-weight:700}.prx-rich-editor__checkbox-field{display:flex;gap:10px;align-items:center;align-self:end}.prx-rich-editor__checkbox-field input[type=checkbox]{width:auto}input,select,textarea{box-sizing:border-box;width:100%;border:1px solid var(--md-sys-color-outline-variant, #d7dce5);border-radius:12px;padding:10px 12px;font:inherit;color:inherit;background:var(--md-sys-color-surface, #fff)}textarea{min-height:360px;resize:vertical;font-family:Cascadia Code,Fira Code,Consolas,monospace;font-size:.86rem;line-height:1.45}.prx-rich-editor__workbench{display:grid;gap:14px;grid-template-columns:minmax(0,1.2fr) minmax(260px,.8fr)}.prx-rich-editor__inspector{display:grid;align-content:start;gap:14px;min-width:0;border:1px solid var(--md-sys-color-outline-variant, #d7dce5);border-radius:8px;padding:12px;background:var(--md-sys-color-surface-container-lowest, #fff)}.prx-rich-editor__inspector h3{margin:0 0 8px;font-size:.95rem}.prx-rich-editor__inspector dl{display:grid;gap:8px;margin:0}.prx-rich-editor__inspector dl div{display:grid;gap:2px}.prx-rich-editor__inspector dt{color:var(--md-sys-color-on-surface-variant, #5f6673);font-size:.78rem;font-weight:700}.prx-rich-editor__inspector dd{margin:0;overflow-wrap:anywhere;font-weight:600}.prx-rich-editor__preview{max-height:260px;overflow:auto;border:1px solid var(--md-sys-color-outline-variant, #d7dce5);border-radius:8px;padding:12px;background:var(--md-sys-color-surface, #fff)}.prx-rich-editor__help{margin:-8px 0 0;color:var(--md-sys-color-on-surface-variant, #5f6673);font-size:.85rem}.prx-rich-editor__error{margin:0;padding:10px 12px;border-radius:8px;color:#991b1b;background:color-mix(in srgb,#dc2626 9%,transparent)}.prx-rich-editor__error p{margin:0}.prx-rich-editor__error ul{display:grid;gap:6px;margin:8px 0 0;padding-inline-start:18px}.prx-rich-editor__error code{margin-right:4px;font-family:Cascadia Code,Fira Code,Consolas,monospace}.prx-rich-editor__actions{display:flex;flex-wrap:wrap;gap:10px}button{border:1px solid var(--md-sys-color-outline-variant, #d7dce5);border-radius:8px;padding:8px 14px;background:var(--md-sys-color-surface, #fff);color:var(--md-sys-color-primary, #3154e7);font-weight:700;cursor:pointer}button:disabled{cursor:not-allowed;opacity:.55}@media(max-width:760px){.prx-rich-editor__header,.prx-rich-editor__section-header,.prx-rich-editor__node-header,.prx-rich-editor__authoring-shell,.prx-rich-editor__grid,.prx-rich-editor__field-group,.prx-rich-editor__node-grid,.prx-rich-editor__workbench{grid-template-columns:1fr;display:grid}.prx-rich-editor__structure{position:static}}\n"] }]
12876
14249
  }], propDecorators: { inputs: [{
12877
14250
  type: Input
12878
14251
  }] } });