@praxisui/rich-content 9.0.0-beta.6 → 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',
@@ -86,10 +93,12 @@ const PRAXIS_RICH_CONTENT_EN_US = {
86
93
  'praxis.richContent.editor.field.icon': 'Icon',
87
94
  'praxis.richContent.editor.field.ariaLabel': 'Accessible label',
88
95
  'praxis.richContent.editor.field.src': 'Image URL',
96
+ 'praxis.richContent.editor.field.srcExpr': 'Image URL binding',
89
97
  'praxis.richContent.editor.field.href': 'Link URL',
90
98
  'praxis.richContent.editor.field.target': 'Target',
91
99
  'praxis.richContent.editor.field.rel': 'Rel',
92
100
  'praxis.richContent.editor.field.alt': 'Alternative text',
101
+ 'praxis.richContent.editor.field.altExpr': 'Alt text binding',
93
102
  'praxis.richContent.editor.field.testId': 'Test id',
94
103
  'praxis.richContent.editor.field.className': 'CSS class',
95
104
  'praxis.richContent.editor.field.visibleWhenPath': 'Context path',
@@ -99,8 +108,10 @@ const PRAXIS_RICH_CONTENT_EN_US = {
99
108
  'praxis.richContent.editor.field.valueExpr': 'Value binding',
100
109
  'praxis.richContent.editor.field.captionExpr': 'Caption binding',
101
110
  'praxis.richContent.editor.field.max': 'Maximum',
111
+ 'praxis.richContent.editor.field.showPercent': 'Show percent',
102
112
  'praxis.richContent.editor.field.direction': 'Direction',
103
113
  'praxis.richContent.editor.field.gap': 'Gap',
114
+ 'praxis.richContent.editor.field.wrap': 'Wrap items',
104
115
  'praxis.richContent.editor.field.avatarName': 'Avatar name',
105
116
  'praxis.richContent.editor.field.avatarImage': 'Avatar image',
106
117
  'praxis.richContent.editor.field.nameExpr': 'Name binding',
@@ -112,6 +123,7 @@ const PRAXIS_RICH_CONTENT_EN_US = {
112
123
  'praxis.richContent.editor.field.meta': 'Meta',
113
124
  'praxis.richContent.editor.field.opposite': 'Opposite content',
114
125
  'praxis.richContent.editor.field.badge': 'Badge',
126
+ 'praxis.richContent.editor.field.badgeExpr': 'Badge binding',
115
127
  'praxis.richContent.editor.field.preset': 'Preset',
116
128
  'praxis.richContent.editor.field.title': 'Title',
117
129
  'praxis.richContent.editor.field.subtitle': 'Subtitle',
@@ -168,11 +180,29 @@ const PRAXIS_RICH_CONTENT_EN_US = {
168
180
  'praxis.richContent.editor.field.ctaLabel': 'CTA label',
169
181
  'praxis.richContent.editor.field.ctaIcon': 'CTA icon',
170
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',
171
188
  'praxis.richContent.editor.field.mediaKind': 'Media',
172
189
  'praxis.richContent.editor.field.mediaSrc': 'Media source',
190
+ 'praxis.richContent.editor.field.mediaSrcExpr': 'Media source binding',
173
191
  'praxis.richContent.editor.field.mediaAlt': 'Media alt text',
192
+ 'praxis.richContent.editor.field.mediaAltExpr': 'Media alt binding',
174
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',
175
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',
176
206
  'praxis.richContent.editor.decisionGovernance': 'Decision governance',
177
207
  'praxis.richContent.editor.decisionEvidence': 'Evidence',
178
208
  'praxis.richContent.editor.decisionPrimaryAction': 'Primary action',
@@ -210,6 +240,20 @@ const PRAXIS_RICH_CONTENT_EN_US = {
210
240
  'praxis.richContent.editor.placeholder.supersedesExpr': '${decision.supersedes}',
211
241
  'praxis.richContent.editor.placeholder.evidenceLabelExpr': '${evidence.label}',
212
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',
213
257
  'praxis.richContent.editor.cardVariant.outlined': 'Outlined',
214
258
  'praxis.richContent.editor.cardVariant.elevated': 'Elevated',
215
259
  'praxis.richContent.editor.cardVariant.filled': 'Filled',
@@ -218,8 +262,18 @@ const PRAXIS_RICH_CONTENT_EN_US = {
218
262
  'praxis.richContent.editor.size.sm': 'Small',
219
263
  'praxis.richContent.editor.size.md': 'Medium',
220
264
  'praxis.richContent.editor.size.lg': 'Large',
265
+ 'praxis.richContent.editor.density.compact': 'Compact',
266
+ 'praxis.richContent.editor.density.comfortable': 'Comfortable',
221
267
  'praxis.richContent.editor.orientation.vertical': 'Vertical',
222
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',
223
277
  'praxis.richContent.editor.mediaKind.image': 'Image',
224
278
  'praxis.richContent.editor.mediaKind.video': 'Video',
225
279
  'praxis.richContent.editor.mediaKind.icon': 'Icon',
@@ -414,9 +468,16 @@ const PRAXIS_RICH_CONTENT_PT_BR = {
414
468
  'praxis.richContent.editor.rootClass': 'Classe raiz',
415
469
  'praxis.richContent.editor.rootClassPlaceholder': 'Exemplo: employee-expansion-rich',
416
470
  'praxis.richContent.editor.document': 'JSON avançado',
471
+ 'praxis.richContent.editor.documentJson': 'JSON do documento canônico',
417
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.',
418
474
  'praxis.richContent.editor.blocks': 'Blocos',
419
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',
420
481
  'praxis.richContent.editor.block': 'Bloco',
421
482
  'praxis.richContent.editor.blockType': 'Tipo de bloco',
422
483
  'praxis.richContent.editor.addBlock': 'Adicionar bloco',
@@ -460,10 +521,12 @@ const PRAXIS_RICH_CONTENT_PT_BR = {
460
521
  'praxis.richContent.editor.field.icon': 'Ícone',
461
522
  'praxis.richContent.editor.field.ariaLabel': 'Label acessível',
462
523
  'praxis.richContent.editor.field.src': 'URL da imagem',
524
+ 'praxis.richContent.editor.field.srcExpr': 'Vínculo da URL da imagem',
463
525
  'praxis.richContent.editor.field.href': 'URL do link',
464
526
  'praxis.richContent.editor.field.target': 'Destino',
465
527
  'praxis.richContent.editor.field.rel': 'Rel',
466
528
  'praxis.richContent.editor.field.alt': 'Texto alternativo',
529
+ 'praxis.richContent.editor.field.altExpr': 'Vínculo do texto alternativo',
467
530
  'praxis.richContent.editor.field.testId': 'Id de teste',
468
531
  'praxis.richContent.editor.field.className': 'Classe CSS',
469
532
  'praxis.richContent.editor.field.visibleWhenPath': 'Path de contexto',
@@ -473,8 +536,10 @@ const PRAXIS_RICH_CONTENT_PT_BR = {
473
536
  'praxis.richContent.editor.field.valueExpr': 'Binding do valor',
474
537
  'praxis.richContent.editor.field.captionExpr': 'Binding da legenda',
475
538
  'praxis.richContent.editor.field.max': 'Máximo',
539
+ 'praxis.richContent.editor.field.showPercent': 'Mostrar percentual',
476
540
  'praxis.richContent.editor.field.direction': 'Direção',
477
541
  'praxis.richContent.editor.field.gap': 'Espaçamento',
542
+ 'praxis.richContent.editor.field.wrap': 'Quebrar itens',
478
543
  'praxis.richContent.editor.field.avatarName': 'Nome do avatar',
479
544
  'praxis.richContent.editor.field.avatarImage': 'Imagem do avatar',
480
545
  'praxis.richContent.editor.field.nameExpr': 'Vínculo do nome',
@@ -486,16 +551,35 @@ const PRAXIS_RICH_CONTENT_PT_BR = {
486
551
  'praxis.richContent.editor.field.meta': 'Meta',
487
552
  'praxis.richContent.editor.field.opposite': 'Conteúdo oposto',
488
553
  'praxis.richContent.editor.field.badge': 'Badge',
554
+ 'praxis.richContent.editor.field.badgeExpr': 'Vínculo do badge',
489
555
  'praxis.richContent.editor.field.preset': 'Preset',
490
556
  'praxis.richContent.editor.field.title': 'Título',
491
557
  'praxis.richContent.editor.field.subtitle': 'Subtítulo',
492
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',
493
564
  'praxis.richContent.editor.field.orientation': 'Orientação',
494
565
  'praxis.richContent.editor.field.mediaKind': 'Mídia',
495
566
  'praxis.richContent.editor.field.mediaSrc': 'Origem da mídia',
567
+ 'praxis.richContent.editor.field.mediaSrcExpr': 'Vínculo da origem da mídia',
496
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',
497
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',
498
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',
499
583
  'praxis.richContent.editor.cardVariant.outlined': 'Com contorno',
500
584
  'praxis.richContent.editor.cardVariant.elevated': 'Elevado',
501
585
  'praxis.richContent.editor.cardVariant.filled': 'Preenchido',
@@ -504,8 +588,18 @@ const PRAXIS_RICH_CONTENT_PT_BR = {
504
588
  'praxis.richContent.editor.size.sm': 'Pequeno',
505
589
  'praxis.richContent.editor.size.md': 'Médio',
506
590
  'praxis.richContent.editor.size.lg': 'Grande',
591
+ 'praxis.richContent.editor.density.compact': 'Compacta',
592
+ 'praxis.richContent.editor.density.comfortable': 'Confortável',
507
593
  'praxis.richContent.editor.orientation.vertical': 'Vertical',
508
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',
509
603
  'praxis.richContent.editor.mediaKind.image': 'Imagem',
510
604
  'praxis.richContent.editor.mediaKind.video': 'Vídeo',
511
605
  'praxis.richContent.editor.mediaKind.icon': 'Ícone',
@@ -528,6 +622,20 @@ const PRAXIS_RICH_CONTENT_PT_BR = {
528
622
  'praxis.richContent.editor.placeholder.valueExpr': 'row.total',
529
623
  'praxis.richContent.editor.placeholder.captionExpr': 'row.caption',
530
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',
531
639
  'praxis.richContent.editor.placeholder.nameExpr': 'row.name',
532
640
  'praxis.richContent.editor.placeholder.imageSrcExpr': 'row.avatarUrl',
533
641
  'praxis.richContent.editor.placeholder.initialsExpr': 'row.initials',
@@ -5763,6 +5871,7 @@ class PraxisRichContentConfigEditor {
5763
5871
  validationIssues = [];
5764
5872
  nodeCount = 0;
5765
5873
  nodeTypeSummary = '';
5874
+ selectedNodeIndex = 0;
5766
5875
  initialInputs = this.normalizeInputs(this.inputs);
5767
5876
  pendingRemoval = null;
5768
5877
  ngOnChanges(changes) {
@@ -5815,14 +5924,19 @@ class PraxisRichContentConfigEditor {
5815
5924
  this.parseDocument();
5816
5925
  this.markDirty();
5817
5926
  }
5927
+ selectTopLevelNode(index) {
5928
+ this.selectedNodeIndex = index;
5929
+ }
5818
5930
  addTopLevelNode() {
5819
5931
  const document = this.ensureEditableDocument();
5820
5932
  document.nodes.push(this.createDefaultNode(this.newNodeType));
5933
+ this.selectedNodeIndex = document.nodes.length - 1;
5821
5934
  this.syncStructuredDocumentChange();
5822
5935
  }
5823
5936
  removeTopLevelNode(index) {
5824
5937
  const document = this.ensureEditableDocument();
5825
5938
  document.nodes.splice(index, 1);
5939
+ this.reconcileSelectedNodeIndex(document);
5826
5940
  this.syncStructuredDocumentChange();
5827
5941
  }
5828
5942
  moveTopLevelNode(index, delta) {
@@ -5833,6 +5947,17 @@ class PraxisRichContentConfigEditor {
5833
5947
  }
5834
5948
  const [node] = document.nodes.splice(index, 1);
5835
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
+ }
5836
5961
  this.syncStructuredDocumentChange();
5837
5962
  }
5838
5963
  changeTopLevelNodeType(index, nextType) {
@@ -5945,6 +6070,93 @@ class PraxisRichContentConfigEditor {
5945
6070
  }
5946
6071
  this.syncStructuredDocumentChange();
5947
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
+ }
5948
6160
  addCardSlotNode(index, slot) {
5949
6161
  this.ensureCardSlotNodes(index, slot).push(createDefaultPresenterNode('text', this.tx.bind(this)));
5950
6162
  this.syncStructuredDocumentChange();
@@ -6244,6 +6456,65 @@ class PraxisRichContentConfigEditor {
6244
6456
  };
6245
6457
  this.syncStructuredDocumentChange();
6246
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
+ }
6247
6518
  getActionCardSecondaryActions(index) {
6248
6519
  return this.ensureActionCardSecondaryActions(index);
6249
6520
  }
@@ -6667,6 +6938,16 @@ class PraxisRichContentConfigEditor {
6667
6938
  }
6668
6939
  return node;
6669
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
+ }
6670
6951
  ensureCardSlotNodes(index, slot) {
6671
6952
  const card = this.ensureCardNode(index);
6672
6953
  if (!Array.isArray(card[slot])) {
@@ -6972,6 +7253,7 @@ class PraxisRichContentConfigEditor {
6972
7253
  this.errorMessage = '';
6973
7254
  this.isValid$.next(true);
6974
7255
  this.parsedDocument = document;
7256
+ this.reconcileSelectedNodeIndex(document);
6975
7257
  this.updateDocumentOverview(document);
6976
7258
  return document;
6977
7259
  }
@@ -6991,6 +7273,20 @@ class PraxisRichContentConfigEditor {
6991
7273
  this.nodeCount = 0;
6992
7274
  this.nodeTypeSummary = '';
6993
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
+ }
6994
7290
  updateDocumentOverview(document) {
6995
7291
  const typeCounts = new Map();
6996
7292
  let count = 0;
@@ -7218,6 +7514,13 @@ class PraxisRichContentConfigEditor {
7218
7514
  (ngModelChange)="setString('src', $event)"
7219
7515
  />
7220
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>
7221
7524
  <label>
7222
7525
  <span>{{ tx('editor.field.alt', 'Alternative text') }}</span>
7223
7526
  <input
@@ -7225,6 +7528,13 @@ class PraxisRichContentConfigEditor {
7225
7528
  (ngModelChange)="setString('alt', $event)"
7226
7529
  />
7227
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>
7228
7538
  }
7229
7539
  @case ('link') {
7230
7540
  <label>
@@ -7234,6 +7544,13 @@ class PraxisRichContentConfigEditor {
7234
7544
  (ngModelChange)="setString('label', $event)"
7235
7545
  />
7236
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>
7237
7554
  <label>
7238
7555
  <span>{{ tx('editor.field.href', 'Link URL') }}</span>
7239
7556
  <input
@@ -7283,6 +7600,20 @@ class PraxisRichContentConfigEditor {
7283
7600
  (ngModelChange)="setString('label', $event)"
7284
7601
  />
7285
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>
7286
7617
  <label>
7287
7618
  <span>{{ tx('editor.field.max', 'Maximum') }}</span>
7288
7619
  <input
@@ -7573,8 +7904,58 @@ class PraxisRichContentConfigEditor {
7573
7904
  </header>
7574
7905
 
7575
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>
7576
7956
  <div class="prx-rich-editor__node-list">
7577
7957
  @for (node of parsedDocument?.nodes ?? []; track node.id ?? $index; let nodeIndex = $index) {
7958
+ @if (selectedNodeIndex === nodeIndex) {
7578
7959
  <article
7579
7960
  class="prx-rich-editor__node-card"
7580
7961
  [attr.data-rich-editor-node-type]="node.type"
@@ -7760,22 +8141,38 @@ class PraxisRichContentConfigEditor {
7760
8141
  />
7761
8142
  </label>
7762
8143
  }
7763
- @case ('image') {
7764
- <label>
7765
- <span>{{ tx('editor.field.src', 'Image URL') }}</span>
7766
- <input
7767
- [ngModel]="getStringField(node, 'src')"
7768
- (ngModelChange)="setStringField($index, 'src', $event)"
7769
- />
7770
- </label>
7771
- <label>
7772
- <span>{{ tx('editor.field.alt', 'Alternative text') }}</span>
7773
- <input
7774
- [ngModel]="getStringField(node, 'alt')"
7775
- (ngModelChange)="setStringField($index, 'alt', $event)"
7776
- />
7777
- </label>
7778
- }
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
+ }
7779
8176
  @case ('link') {
7780
8177
  <label>
7781
8178
  <span>{{ tx('editor.field.label', 'Label') }}</span>
@@ -7826,6 +8223,14 @@ class PraxisRichContentConfigEditor {
7826
8223
  (ngModelChange)="setStringField($index, 'label', $event)"
7827
8224
  />
7828
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>
7829
8234
  <label>
7830
8235
  <span>{{ tx('editor.field.valueExpr', 'Value binding') }}</span>
7831
8236
  <input
@@ -7867,6 +8272,14 @@ class PraxisRichContentConfigEditor {
7867
8272
  (ngModelChange)="setNumberField($index, 'max', $event)"
7868
8273
  />
7869
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>
7870
8283
  }
7871
8284
  @case ('actionButton') {
7872
8285
  <label>
@@ -7962,6 +8375,14 @@ class PraxisRichContentConfigEditor {
7962
8375
  <option value="xl">xl</option>
7963
8376
  </select>
7964
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>
7965
8386
  <div class="prx-rich-editor__nested-editor">
7966
8387
  <header class="prx-rich-editor__nested-header">
7967
8388
  <h5>{{ tx('editor.composeItems', 'Compose items') }}</h5>
@@ -8019,6 +8440,14 @@ class PraxisRichContentConfigEditor {
8019
8440
  (ngModelChange)="setStringField($index, 'title', $event)"
8020
8441
  />
8021
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>
8022
8451
  <label>
8023
8452
  <span>{{ tx('editor.field.subtitle', 'Subtitle') }}</span>
8024
8453
  <input
@@ -8026,6 +8455,14 @@ class PraxisRichContentConfigEditor {
8026
8455
  (ngModelChange)="setStringField($index, 'subtitle', $event)"
8027
8456
  />
8028
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>
8029
8466
  <label>
8030
8467
  <span>{{ tx('editor.field.variant', 'Variant') }}</span>
8031
8468
  <select
@@ -8063,6 +8500,16 @@ class PraxisRichContentConfigEditor {
8063
8500
  <option value="lg">{{ tx('editor.size.lg', 'Large') }}</option>
8064
8501
  </select>
8065
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>
8066
8513
  <label>
8067
8514
  <span>{{ tx('editor.field.orientation', 'Orientation') }}</span>
8068
8515
  <select
@@ -8073,6 +8520,38 @@ class PraxisRichContentConfigEditor {
8073
8520
  <option value="horizontal">{{ tx('editor.orientation.horizontal', 'Horizontal') }}</option>
8074
8521
  </select>
8075
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>
8076
8555
  <label>
8077
8556
  <span>{{ tx('editor.field.mediaKind', 'Media') }}</span>
8078
8557
  <select
@@ -8094,6 +8573,14 @@ class PraxisRichContentConfigEditor {
8094
8573
  (ngModelChange)="setCardMediaField(nodeIndex, 'src', $event)"
8095
8574
  />
8096
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>
8097
8584
  <label>
8098
8585
  <span>{{ tx('editor.field.mediaAlt', 'Media alt text') }}</span>
8099
8586
  <input
@@ -8101,6 +8588,14 @@ class PraxisRichContentConfigEditor {
8101
8588
  (ngModelChange)="setCardMediaField(nodeIndex, 'alt', $event)"
8102
8589
  />
8103
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>
8104
8599
  <label>
8105
8600
  <span>{{ tx('editor.field.mediaIcon', 'Media icon') }}</span>
8106
8601
  <input
@@ -8108,6 +8603,29 @@ class PraxisRichContentConfigEditor {
8108
8603
  (ngModelChange)="setCardMediaField(nodeIndex, 'icon', $event)"
8109
8604
  />
8110
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>
8111
8629
  <label>
8112
8630
  <span>{{ tx('editor.field.mediaPlacement', 'Media placement') }}</span>
8113
8631
  <select
@@ -8121,6 +8639,76 @@ class PraxisRichContentConfigEditor {
8121
8639
  </select>
8122
8640
  </label>
8123
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>
8124
8712
  <div class="prx-rich-editor__nested-editor">
8125
8713
  <header class="prx-rich-editor__nested-header">
8126
8714
  <h5>{{ tx('editor.cardContent', 'Card body') }}</h5>
@@ -8566,6 +9154,14 @@ class PraxisRichContentConfigEditor {
8566
9154
  (ngModelChange)="setTabsItemField(nodeIndex, $index, 'label', $event)"
8567
9155
  />
8568
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>
8569
9165
  <label>
8570
9166
  <span>{{ tx('editor.field.icon', 'Icon') }}</span>
8571
9167
  <input
@@ -8580,6 +9176,48 @@ class PraxisRichContentConfigEditor {
8580
9176
  (ngModelChange)="setTabsItemField(nodeIndex, $index, 'badge', $event)"
8581
9177
  />
8582
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>
8583
9221
  </div>
8584
9222
  }
8585
9223
  </div>
@@ -10049,8 +10687,11 @@ class PraxisRichContentConfigEditor {
10049
10687
  </fieldset>
10050
10688
  </div>
10051
10689
  </article>
10690
+ }
10052
10691
  }
10053
10692
  </div>
10693
+ </div>
10694
+ </div>
10054
10695
  } @else {
10055
10696
  <p class="prx-rich-editor__empty">
10056
10697
  {{ tx('editor.noBlocks', 'No blocks yet. Add a block to start authoring this document.') }}
@@ -10058,9 +10699,23 @@ class PraxisRichContentConfigEditor {
10058
10699
  }
10059
10700
  </section>
10060
10701
 
10061
- <label class="prx-rich-editor__document">
10062
- <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>
10063
10716
  <div class="prx-rich-editor__workbench">
10717
+ <label class="prx-rich-editor__document">
10718
+ <span>{{ tx('editor.documentJson', 'Canonical document JSON') }}</span>
10064
10719
  <textarea
10065
10720
  name="rich-content-document"
10066
10721
  [(ngModel)]="documentJson"
@@ -10074,6 +10729,7 @@ class PraxisRichContentConfigEditor {
10074
10729
  spellcheck="false"
10075
10730
  data-testid="rich-content-document-input"
10076
10731
  ></textarea>
10732
+ </label>
10077
10733
 
10078
10734
  <aside
10079
10735
  class="prx-rich-editor__inspector"
@@ -10095,7 +10751,7 @@ class PraxisRichContentConfigEditor {
10095
10751
 
10096
10752
  @if (parsedDocument) {
10097
10753
  <section>
10098
- <h3>{{ tx('editor.preview', 'Preview') }}</h3>
10754
+ <h3>{{ tx('editor.preview', 'Preview') }}</h3>
10099
10755
  <div class="prx-rich-editor__preview">
10100
10756
  <praxis-rich-content
10101
10757
  [document]="parsedDocument"
@@ -10107,7 +10763,7 @@ class PraxisRichContentConfigEditor {
10107
10763
  }
10108
10764
  </aside>
10109
10765
  </div>
10110
- </label>
10766
+ </section>
10111
10767
 
10112
10768
  @if (errorMessage) {
10113
10769
  <div
@@ -10147,7 +10803,7 @@ class PraxisRichContentConfigEditor {
10147
10803
  </button>
10148
10804
  </div>
10149
10805
  </section>
10150
- `, 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 });
10151
10807
  }
10152
10808
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisRichContentConfigEditor, decorators: [{
10153
10809
  type: Component,
@@ -10300,6 +10956,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
10300
10956
  (ngModelChange)="setString('src', $event)"
10301
10957
  />
10302
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>
10303
10966
  <label>
10304
10967
  <span>{{ tx('editor.field.alt', 'Alternative text') }}</span>
10305
10968
  <input
@@ -10307,6 +10970,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
10307
10970
  (ngModelChange)="setString('alt', $event)"
10308
10971
  />
10309
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>
10310
10980
  }
10311
10981
  @case ('link') {
10312
10982
  <label>
@@ -10316,6 +10986,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
10316
10986
  (ngModelChange)="setString('label', $event)"
10317
10987
  />
10318
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>
10319
10996
  <label>
10320
10997
  <span>{{ tx('editor.field.href', 'Link URL') }}</span>
10321
10998
  <input
@@ -10365,6 +11042,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
10365
11042
  (ngModelChange)="setString('label', $event)"
10366
11043
  />
10367
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>
10368
11059
  <label>
10369
11060
  <span>{{ tx('editor.field.max', 'Maximum') }}</span>
10370
11061
  <input
@@ -10655,8 +11346,58 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
10655
11346
  </header>
10656
11347
 
10657
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>
10658
11398
  <div class="prx-rich-editor__node-list">
10659
11399
  @for (node of parsedDocument?.nodes ?? []; track node.id ?? $index; let nodeIndex = $index) {
11400
+ @if (selectedNodeIndex === nodeIndex) {
10660
11401
  <article
10661
11402
  class="prx-rich-editor__node-card"
10662
11403
  [attr.data-rich-editor-node-type]="node.type"
@@ -10842,22 +11583,38 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
10842
11583
  />
10843
11584
  </label>
10844
11585
  }
10845
- @case ('image') {
10846
- <label>
10847
- <span>{{ tx('editor.field.src', 'Image URL') }}</span>
10848
- <input
10849
- [ngModel]="getStringField(node, 'src')"
10850
- (ngModelChange)="setStringField($index, 'src', $event)"
10851
- />
10852
- </label>
10853
- <label>
10854
- <span>{{ tx('editor.field.alt', 'Alternative text') }}</span>
10855
- <input
10856
- [ngModel]="getStringField(node, 'alt')"
10857
- (ngModelChange)="setStringField($index, 'alt', $event)"
10858
- />
10859
- </label>
10860
- }
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
+ }
10861
11618
  @case ('link') {
10862
11619
  <label>
10863
11620
  <span>{{ tx('editor.field.label', 'Label') }}</span>
@@ -10908,6 +11665,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
10908
11665
  (ngModelChange)="setStringField($index, 'label', $event)"
10909
11666
  />
10910
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>
10911
11676
  <label>
10912
11677
  <span>{{ tx('editor.field.valueExpr', 'Value binding') }}</span>
10913
11678
  <input
@@ -10949,6 +11714,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
10949
11714
  (ngModelChange)="setNumberField($index, 'max', $event)"
10950
11715
  />
10951
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>
10952
11725
  }
10953
11726
  @case ('actionButton') {
10954
11727
  <label>
@@ -11044,6 +11817,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
11044
11817
  <option value="xl">xl</option>
11045
11818
  </select>
11046
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>
11047
11828
  <div class="prx-rich-editor__nested-editor">
11048
11829
  <header class="prx-rich-editor__nested-header">
11049
11830
  <h5>{{ tx('editor.composeItems', 'Compose items') }}</h5>
@@ -11101,6 +11882,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
11101
11882
  (ngModelChange)="setStringField($index, 'title', $event)"
11102
11883
  />
11103
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>
11104
11893
  <label>
11105
11894
  <span>{{ tx('editor.field.subtitle', 'Subtitle') }}</span>
11106
11895
  <input
@@ -11108,6 +11897,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
11108
11897
  (ngModelChange)="setStringField($index, 'subtitle', $event)"
11109
11898
  />
11110
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>
11111
11908
  <label>
11112
11909
  <span>{{ tx('editor.field.variant', 'Variant') }}</span>
11113
11910
  <select
@@ -11145,6 +11942,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
11145
11942
  <option value="lg">{{ tx('editor.size.lg', 'Large') }}</option>
11146
11943
  </select>
11147
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>
11148
11955
  <label>
11149
11956
  <span>{{ tx('editor.field.orientation', 'Orientation') }}</span>
11150
11957
  <select
@@ -11155,6 +11962,38 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
11155
11962
  <option value="horizontal">{{ tx('editor.orientation.horizontal', 'Horizontal') }}</option>
11156
11963
  </select>
11157
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>
11158
11997
  <label>
11159
11998
  <span>{{ tx('editor.field.mediaKind', 'Media') }}</span>
11160
11999
  <select
@@ -11176,6 +12015,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
11176
12015
  (ngModelChange)="setCardMediaField(nodeIndex, 'src', $event)"
11177
12016
  />
11178
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>
11179
12026
  <label>
11180
12027
  <span>{{ tx('editor.field.mediaAlt', 'Media alt text') }}</span>
11181
12028
  <input
@@ -11183,6 +12030,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
11183
12030
  (ngModelChange)="setCardMediaField(nodeIndex, 'alt', $event)"
11184
12031
  />
11185
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>
11186
12041
  <label>
11187
12042
  <span>{{ tx('editor.field.mediaIcon', 'Media icon') }}</span>
11188
12043
  <input
@@ -11190,6 +12045,29 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
11190
12045
  (ngModelChange)="setCardMediaField(nodeIndex, 'icon', $event)"
11191
12046
  />
11192
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>
11193
12071
  <label>
11194
12072
  <span>{{ tx('editor.field.mediaPlacement', 'Media placement') }}</span>
11195
12073
  <select
@@ -11203,6 +12081,76 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
11203
12081
  </select>
11204
12082
  </label>
11205
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>
11206
12154
  <div class="prx-rich-editor__nested-editor">
11207
12155
  <header class="prx-rich-editor__nested-header">
11208
12156
  <h5>{{ tx('editor.cardContent', 'Card body') }}</h5>
@@ -11648,6 +12596,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
11648
12596
  (ngModelChange)="setTabsItemField(nodeIndex, $index, 'label', $event)"
11649
12597
  />
11650
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>
11651
12607
  <label>
11652
12608
  <span>{{ tx('editor.field.icon', 'Icon') }}</span>
11653
12609
  <input
@@ -11662,6 +12618,48 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
11662
12618
  (ngModelChange)="setTabsItemField(nodeIndex, $index, 'badge', $event)"
11663
12619
  />
11664
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>
11665
12663
  </div>
11666
12664
  }
11667
12665
  </div>
@@ -13131,8 +14129,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
13131
14129
  </fieldset>
13132
14130
  </div>
13133
14131
  </article>
14132
+ }
13134
14133
  }
13135
14134
  </div>
14135
+ </div>
14136
+ </div>
13136
14137
  } @else {
13137
14138
  <p class="prx-rich-editor__empty">
13138
14139
  {{ tx('editor.noBlocks', 'No blocks yet. Add a block to start authoring this document.') }}
@@ -13140,9 +14141,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
13140
14141
  }
13141
14142
  </section>
13142
14143
 
13143
- <label class="prx-rich-editor__document">
13144
- <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>
13145
14158
  <div class="prx-rich-editor__workbench">
14159
+ <label class="prx-rich-editor__document">
14160
+ <span>{{ tx('editor.documentJson', 'Canonical document JSON') }}</span>
13146
14161
  <textarea
13147
14162
  name="rich-content-document"
13148
14163
  [(ngModel)]="documentJson"
@@ -13156,6 +14171,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
13156
14171
  spellcheck="false"
13157
14172
  data-testid="rich-content-document-input"
13158
14173
  ></textarea>
14174
+ </label>
13159
14175
 
13160
14176
  <aside
13161
14177
  class="prx-rich-editor__inspector"
@@ -13177,7 +14193,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
13177
14193
 
13178
14194
  @if (parsedDocument) {
13179
14195
  <section>
13180
- <h3>{{ tx('editor.preview', 'Preview') }}</h3>
14196
+ <h3>{{ tx('editor.preview', 'Preview') }}</h3>
13181
14197
  <div class="prx-rich-editor__preview">
13182
14198
  <praxis-rich-content
13183
14199
  [document]="parsedDocument"
@@ -13189,7 +14205,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
13189
14205
  }
13190
14206
  </aside>
13191
14207
  </div>
13192
- </label>
14208
+ </section>
13193
14209
 
13194
14210
  @if (errorMessage) {
13195
14211
  <div
@@ -13229,7 +14245,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
13229
14245
  </button>
13230
14246
  </div>
13231
14247
  </section>
13232
- `, 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"] }]
13233
14249
  }], propDecorators: { inputs: [{
13234
14250
  type: Input
13235
14251
  }] } });