@praxisui/table 8.0.0-beta.100 → 8.0.0-beta.102

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.
@@ -80,7 +80,7 @@ import { A11yModule } from '@angular/cdk/a11y';
80
80
  import * as i6$2 from '@angular/router';
81
81
  import * as i11$1 from '@angular/cdk/scrolling';
82
82
  import { ScrollingModule } from '@angular/cdk/scrolling';
83
- import { sanitizePraxisAssistantText, createPraxisAssistantViewportLayout, AiBackendApiService, PraxisAssistantSessionRegistryService, PraxisAssistantTurnOrchestratorService, PraxisAiAssistantShellComponent } from '@praxisui/ai';
83
+ import { sanitizePraxisAssistantText, createPraxisAssistantViewportLayout, AiBackendApiService, AgenticAuthoringTurnClientService, PraxisAssistantSessionRegistryService, PraxisAssistantTurnOrchestratorService, PraxisAiAssistantShellComponent } from '@praxisui/ai';
84
84
  import * as i6$3 from '@praxisui/dialog';
85
85
 
86
86
  const PRAXIS_TABLE_RUNTIME_I18N_NAMESPACE = 'praxisTableRuntime';
@@ -39318,6 +39318,7 @@ class PraxisTable {
39318
39318
  aiAssistantController = null;
39319
39319
  aiAssistantStateSubscription = null;
39320
39320
  aiApi = inject(AiBackendApiService);
39321
+ agenticTurnClient = inject(AgenticAuthoringTurnClientService);
39321
39322
  assistantSessions = inject(PraxisAssistantSessionRegistryService);
39322
39323
  aiTurnOrchestrator = inject(PraxisAssistantTurnOrchestratorService);
39323
39324
  aiAssistantSessionEffect = effect(() => {
@@ -39947,7 +39948,7 @@ class PraxisTable {
39947
39948
  if (this.aiAdapter || this.aiAdapterLoadStarted)
39948
39949
  return;
39949
39950
  this.aiAdapterLoadStarted = true;
39950
- import('./praxisui-table-table-ai.adapter-DFyMRt4g.mjs')
39951
+ import('./praxisui-table-table-ai.adapter-D1VAQO63.mjs')
39951
39952
  .then(({ TableAiAdapter }) => {
39952
39953
  this.aiAdapter = new TableAiAdapter(this);
39953
39954
  this.initializeAiAssistantController();
@@ -40087,20 +40088,32 @@ class PraxisTable {
40087
40088
  if (!controller)
40088
40089
  return;
40089
40090
  const state = controller.snapshot();
40090
- const contextHints = reply.contextHints ? { ...reply.contextHints } : undefined;
40091
- const actionValue = reply.value ?? reply.prompt;
40091
+ const visualPresentationPrompt = this.resolveAiAssistantVisualPresentationQuickReplyPrompt(reply, state);
40092
+ const visualPresentationContextHints = visualPresentationPrompt
40093
+ ? this.resolveAiAssistantVisualPresentationQuickReplyContextHints(reply, state)
40094
+ : null;
40095
+ const contextHints = this.mergeAiAssistantQuickReplyContextHints(reply.contextHints, visualPresentationContextHints);
40096
+ const semanticPrompt = typeof reply.prompt === 'string' && reply.prompt.trim()
40097
+ ? reply.prompt.trim()
40098
+ : '';
40099
+ const semanticValue = visualPresentationPrompt
40100
+ || semanticPrompt
40101
+ || (typeof reply.value === 'string' && reply.value.trim() ? reply.value.trim() : '')
40102
+ || reply.label;
40103
+ const actionValue = reply.value ?? semanticValue;
40092
40104
  const next$ = state.state === 'clarification'
40093
40105
  ? controller.answerClarification({
40094
40106
  id: reply.id,
40095
40107
  label: reply.label,
40096
- value: typeof reply.value === 'string' && reply.value.trim() ? reply.value.trim() : reply.prompt,
40108
+ value: semanticValue,
40109
+ displayPrompt: reply.label,
40097
40110
  description: reply.description ?? undefined,
40098
40111
  contextHints,
40099
40112
  })
40100
- : controller.submitPrompt(reply.prompt, {
40113
+ : controller.submitPrompt(semanticValue, {
40101
40114
  kind: reply.kind || 'quick-reply',
40102
40115
  id: reply.id,
40103
- value: actionValue,
40116
+ value: semanticValue || actionValue,
40104
40117
  displayPrompt: reply.label,
40105
40118
  contextHints,
40106
40119
  });
@@ -40111,6 +40124,105 @@ class PraxisTable {
40111
40124
  this.cdr.markForCheck();
40112
40125
  });
40113
40126
  }
40127
+ mergeAiAssistantQuickReplyContextHints(base, generated) {
40128
+ const baseRecord = this.asPlainRecord(base);
40129
+ if (!baseRecord && !generated)
40130
+ return undefined;
40131
+ return {
40132
+ ...(baseRecord ?? {}),
40133
+ ...(generated ?? {}),
40134
+ };
40135
+ }
40136
+ resolveAiAssistantVisualPresentationQuickReplyPrompt(reply, state) {
40137
+ const renderer = this.resolveAiAssistantVisualPresentationRenderer(reply);
40138
+ if (!renderer)
40139
+ return null;
40140
+ const field = this.resolveAiAssistantVisualPresentationTargetField(reply, state);
40141
+ return field
40142
+ ? `Aplique a apresentacao visual ${renderer} na coluna ${field}.`
40143
+ : `Aplique a apresentacao visual ${renderer}.`;
40144
+ }
40145
+ resolveAiAssistantVisualPresentationQuickReplyContextHints(reply, state) {
40146
+ const renderer = this.resolveAiAssistantVisualPresentationRenderer(reply);
40147
+ if (!renderer)
40148
+ return null;
40149
+ const field = this.resolveAiAssistantVisualPresentationTargetField(reply, state);
40150
+ const selected = {
40151
+ selection: {
40152
+ mode: 'renderer',
40153
+ ...(field ? { field } : {}),
40154
+ value: renderer,
40155
+ },
40156
+ };
40157
+ if (field) {
40158
+ selected['targetField'] = field;
40159
+ }
40160
+ return {
40161
+ optionSelected: selected,
40162
+ presentation: {
40163
+ kind: 'guided-option',
40164
+ ctaLabel: 'Aplicar opção',
40165
+ icon: renderer === 'two_lines' ? 'format_line_spacing' : 'check',
40166
+ tone: 'primary',
40167
+ },
40168
+ };
40169
+ }
40170
+ resolveAiAssistantVisualPresentationRenderer(reply) {
40171
+ const raw = [
40172
+ typeof reply.value === 'string' ? reply.value : '',
40173
+ reply.prompt,
40174
+ reply.label,
40175
+ ].filter(Boolean).join(' ');
40176
+ const normalized = this.normalizeFieldAlias(raw);
40177
+ if (!normalized)
40178
+ return null;
40179
+ if (normalized.includes('two lines')
40180
+ || normalized.includes('two line')
40181
+ || normalized.includes('two-line')
40182
+ || normalized.includes('twolines')
40183
+ || normalized.includes('twoline')
40184
+ || normalized.includes('duas linhas')
40185
+ || normalized.includes('duaslinhas')
40186
+ || normalized.includes('duas_linhas')
40187
+ || normalized.includes('segunda linha')
40188
+ || normalized.includes('segundalinha')) {
40189
+ return 'two_lines';
40190
+ }
40191
+ if (normalized.includes('badge'))
40192
+ return 'badge';
40193
+ if (normalized.includes('icon') || normalized.includes('icone'))
40194
+ return 'icon';
40195
+ if (normalized.includes('alignment') || normalized.includes('alinhamento'))
40196
+ return 'alignment';
40197
+ return null;
40198
+ }
40199
+ resolveAiAssistantVisualPresentationTargetField(reply, state) {
40200
+ const contextHints = this.asPlainRecord(reply.contextHints);
40201
+ const optionSelected = this.asPlainRecord(contextHints?.['optionSelected']);
40202
+ const selection = this.asPlainRecord(optionSelected?.['selection']);
40203
+ const hintedField = typeof selection?.['field'] === 'string' ? selection['field'].trim() : '';
40204
+ if (hintedField)
40205
+ return hintedField;
40206
+ const sourcePrompt = state.pendingClarification?.sourcePrompt ?? '';
40207
+ const normalizedSource = this.normalizeFieldAlias(sourcePrompt);
40208
+ const columns = this.config?.columns ?? this.visibleColumns ?? [];
40209
+ const matched = columns
40210
+ .map((column) => {
40211
+ const field = column?.field?.trim();
40212
+ if (!field)
40213
+ return null;
40214
+ const header = column.header?.trim() || field;
40215
+ const score = Math.max(this.aiAssistantVisualPresentationTargetScore(normalizedSource, field), this.aiAssistantVisualPresentationTargetScore(normalizedSource, header));
40216
+ return score > 0 ? { field, score } : null;
40217
+ })
40218
+ .filter((entry) => !!entry)
40219
+ .sort((a, b) => b.score - a.score)[0];
40220
+ return matched?.field || null;
40221
+ }
40222
+ aiAssistantVisualPresentationTargetScore(normalizedSource, value) {
40223
+ const normalized = this.normalizeFieldAlias(value);
40224
+ return normalized && normalizedSource.includes(normalized) ? normalized.length : 0;
40225
+ }
40114
40226
  getAiAssistantRecommendedIntents() {
40115
40227
  const locale = this.assistantContextRuntimeLocale();
40116
40228
  const intents = [];
@@ -40566,11 +40678,11 @@ class PraxisTable {
40566
40678
  initializeAiAssistantController() {
40567
40679
  if (!this.aiAdapter || this.aiAssistantController)
40568
40680
  return;
40569
- import('./praxisui-table-table-agentic-authoring-turn-flow-BJWah3jp.mjs')
40681
+ import('./praxisui-table-table-agentic-authoring-turn-flow-CC5bOKf5.mjs')
40570
40682
  .then(({ TableAgenticAuthoringTurnFlow }) => {
40571
40683
  if (this.aiAssistantController || !this.aiAdapter)
40572
40684
  return;
40573
- const flow = new TableAgenticAuthoringTurnFlow(this.aiAdapter, this.aiApi);
40685
+ const flow = new TableAgenticAuthoringTurnFlow(this.aiAdapter, this.aiApi, this.agenticTurnClient);
40574
40686
  const controller = this.aiTurnOrchestrator.createController(flow, {
40575
40687
  componentId: this.aiAdapter.componentId || 'praxis-table',
40576
40688
  componentType: this.aiAdapter.componentType || 'table',
@@ -49694,14 +49806,32 @@ class PraxisTable {
49694
49806
  const size = this.evaluateValueExpr(raw, _row, 'number');
49695
49807
  return size ? { fontSize: `${size}px`, width: `${size}px`, height: `${size}px` } : null;
49696
49808
  }
49697
- getIconAriaLabel(_row, column) {
49698
- return this.getEffectiveRenderer(_row, column)?.icon?.ariaLabel || null;
49809
+ getIconAriaLabel(row, column) {
49810
+ const cfg = this.getEffectiveRenderer(row, column)?.icon;
49811
+ if (!cfg)
49812
+ return null;
49813
+ const configured = cfg.ariaLabel;
49814
+ const evaluated = this.evaluateValueExpr(configured, row, 'string');
49815
+ if (evaluated != null && evaluated !== '')
49816
+ return String(evaluated);
49817
+ if (typeof configured === 'string' && configured.trim())
49818
+ return configured;
49819
+ return this.getIconText(row, column);
49820
+ }
49821
+ getIconText(row, column) {
49822
+ const cfg = this.getEffectiveRenderer(row, column)?.icon;
49823
+ if (!cfg)
49824
+ return null;
49825
+ const text = Object.prototype.hasOwnProperty.call(cfg, 'text')
49826
+ ? cfg.text
49827
+ : cfg.textField ? this.getRendererTextFieldValue(row, column, cfg.textField) : null;
49828
+ return text != null ? String(text) : null;
49699
49829
  }
49700
49830
  getIconRichContentNodes(row, column) {
49701
49831
  const icon = this.getIconName(row, column);
49702
49832
  if (!icon)
49703
49833
  return [];
49704
- return [
49834
+ const nodes = [
49705
49835
  {
49706
49836
  type: 'icon',
49707
49837
  icon,
@@ -49709,6 +49839,15 @@ class PraxisTable {
49709
49839
  className: 'pfx-icon-renderer__icon',
49710
49840
  },
49711
49841
  ];
49842
+ const text = this.getIconText(row, column);
49843
+ if (text) {
49844
+ nodes.push({
49845
+ type: 'text',
49846
+ text,
49847
+ className: 'pfx-icon-renderer__label',
49848
+ });
49849
+ }
49850
+ return nodes;
49712
49851
  }
49713
49852
  /**
49714
49853
  * Lightweight, controlled evaluator for string/number expressions using row context.
@@ -50304,7 +50443,18 @@ class PraxisTable {
50304
50443
  const field = this.getComposeItemField(item);
50305
50444
  if (!field)
50306
50445
  return this.getCellValue(row, base);
50307
- return this.getCellValue(row, { ...base, field });
50446
+ return this.getCellValue(row, this.asComposeValueItemColumn(base, field));
50447
+ }
50448
+ asComposeValueItemColumn(base, field) {
50449
+ const itemColumn = { ...base, field };
50450
+ if (field !== base.field) {
50451
+ delete itemColumn.computed;
50452
+ delete itemColumn._generatedValueGetter;
50453
+ delete itemColumn.expression;
50454
+ delete itemColumn.formula;
50455
+ delete itemColumn.calculated;
50456
+ }
50457
+ return itemColumn;
50308
50458
  }
50309
50459
  asItemColumn(base, item) {
50310
50460
  // Create a lightweight faux column reusing helpers by type
@@ -50335,9 +50485,9 @@ class PraxisTable {
50335
50485
  r.rating = item.rating;
50336
50486
  if (item?.compose)
50337
50487
  r.compose = item.compose;
50488
+ const field = this.getComposeItemField(item) || base.field;
50338
50489
  return {
50339
- ...base,
50340
- field: this.getComposeItemField(item) || base.field,
50490
+ ...this.asComposeValueItemColumn(base, field),
50341
50491
  header: base.header,
50342
50492
  renderer: r,
50343
50493
  conditionalRenderers: undefined,
@@ -55671,6 +55821,103 @@ const PRAXIS_TABLE_AUTHORING_MANIFEST = {
55671
55821
  'maintain-renderer-precedence',
55672
55822
  'preserve-meta-ids'
55673
55823
  ],
55824
+ presentationAffordances: {
55825
+ version: '0.1.0',
55826
+ componentId: 'praxis-table',
55827
+ defaultTargetKind: 'column',
55828
+ sourceRef: 'praxis-ui-angular:projects/praxis-table/src/lib/ai/praxis-table-authoring-manifest.ts#presentationAffordances',
55829
+ affordances: [
55830
+ {
55831
+ id: 'table.column.text-formatting',
55832
+ targetKind: 'column',
55833
+ category: 'format',
55834
+ description: 'Text transforms and masks for textual table columns.',
55835
+ options: [
55836
+ 'none',
55837
+ 'uppercase',
55838
+ 'lowercase',
55839
+ 'titlecase',
55840
+ 'capitalize',
55841
+ 'uppercase|truncate|50|...',
55842
+ '000.000.000-00'
55843
+ ],
55844
+ appliesToTypes: ['string', 'text', 'unknown'],
55845
+ unknownCompatible: true
55846
+ },
55847
+ {
55848
+ id: 'table.column.numeric-formatting',
55849
+ targetKind: 'column',
55850
+ category: 'format',
55851
+ description: 'Decimal, currency and percentage formats for numeric table columns.',
55852
+ options: [
55853
+ '1.0-0',
55854
+ '1.1-1',
55855
+ '1.2-2',
55856
+ '1.0-3',
55857
+ '1.0-0|nosep',
55858
+ 'BRL|symbol|2',
55859
+ 'USD|symbol|2',
55860
+ 'EUR|symbol|2',
55861
+ 'BRL|code|2',
55862
+ 'USD|code|0',
55863
+ 'BRL|symbol|2|nosep',
55864
+ 'EUR|symbol|2|nosep',
55865
+ '1.0-0|x100',
55866
+ '1.1-1|x100',
55867
+ '1.2-2|x100'
55868
+ ],
55869
+ appliesToTypes: ['number', 'currency', 'percentage'],
55870
+ unknownCompatible: false
55871
+ },
55872
+ {
55873
+ id: 'table.column.date-formatting',
55874
+ targetKind: 'column',
55875
+ category: 'format',
55876
+ description: 'Date and time formats for date-like table columns.',
55877
+ options: [
55878
+ 'shortDate',
55879
+ 'mediumDate',
55880
+ 'longDate',
55881
+ 'fullDate',
55882
+ 'MMM/yyyy',
55883
+ 'shortTime',
55884
+ 'short',
55885
+ 'yyyy-MM-dd',
55886
+ 'dd/MM/yyyy',
55887
+ 'yyyy-MM-dd HH:mm'
55888
+ ],
55889
+ appliesToTypes: ['date', 'datetime', 'time'],
55890
+ unknownCompatible: false
55891
+ },
55892
+ {
55893
+ id: 'table.column.boolean-formatting',
55894
+ targetKind: 'column',
55895
+ category: 'format',
55896
+ description: 'Boolean label formats for true/false table columns.',
55897
+ options: ['true-false', 'yes-no', 'active-inactive', 'on-off', 'enabled-disabled', 'custom|Sim|Nao'],
55898
+ appliesToTypes: ['boolean'],
55899
+ unknownCompatible: false
55900
+ },
55901
+ {
55902
+ id: 'table.column.semantic-renderers',
55903
+ targetKind: 'column',
55904
+ category: 'renderer',
55905
+ description: 'Visual renderers for categorical, status or action-oriented table columns.',
55906
+ options: ['badge', 'chip', 'icon', 'link', 'button', 'progress', 'avatar', 'toggle', 'menu', 'rating', 'compose'],
55907
+ appliesToTypes: ['string', 'text', 'boolean', 'number', 'currency', 'percentage', 'date', 'datetime', 'unknown'],
55908
+ unknownCompatible: true
55909
+ },
55910
+ {
55911
+ id: 'table.column.alignment-layout',
55912
+ targetKind: 'column',
55913
+ category: 'layout',
55914
+ description: 'Column alignment and composed cell layout options.',
55915
+ options: ['align:start', 'align:center', 'align:end', 'compose:row', 'compose:column', 'secondary-line', 'prefix', 'suffix'],
55916
+ appliesToTypes: ['string', 'text', 'boolean', 'number', 'currency', 'percentage', 'date', 'datetime', 'unknown'],
55917
+ unknownCompatible: true
55918
+ }
55919
+ ]
55920
+ },
55674
55921
  examples: [
55675
55922
  {
55676
55923
  id: 'add-schema-column',
@@ -57908,6 +58155,7 @@ const FORMAT_PRESETS = Array.from(new Set([
57908
58155
  ...FORMAT_BOOLEAN_PRESETS,
57909
58156
  ]));
57910
58157
  const RENDERER_COMPOSE_ITEM_TYPES = [
58158
+ 'value',
57911
58159
  'icon',
57912
58160
  'image',
57913
58161
  'badge',
@@ -60658,6 +60906,13 @@ function validateComposeRendererValue(rawCompose, originalValue) {
60658
60906
  if (!itemType || itemType === 'compose') {
60659
60907
  return { failureCodes: [`compose renderer item type is not allowed: ${itemType || '(empty)'}`] };
60660
60908
  }
60909
+ if (itemType === 'value') {
60910
+ const valueValidation = validateComposeValueItem(itemRecord);
60911
+ if (valueValidation.failureCodes.length > 0) {
60912
+ return valueValidation;
60913
+ }
60914
+ continue;
60915
+ }
60661
60916
  const itemValidation = validateRendererValue(itemRecord);
60662
60917
  if (itemValidation.failureCodes.length > 0) {
60663
60918
  return itemValidation;
@@ -60665,6 +60920,15 @@ function validateComposeRendererValue(rawCompose, originalValue) {
60665
60920
  }
60666
60921
  return { value: originalValue, failureCodes: [] };
60667
60922
  }
60923
+ function validateComposeValueItem(item) {
60924
+ for (const key of ['field', 'textField', 'valueField', 'text']) {
60925
+ const value = item[key];
60926
+ if (value !== undefined && typeof value !== 'string') {
60927
+ return { failureCodes: [`compose value item ${key} must be a string`] };
60928
+ }
60929
+ }
60930
+ return { value: item, failureCodes: [] };
60931
+ }
60668
60932
  function validateBadgeRendererValue(rawValue) {
60669
60933
  if (!rawValue || typeof rawValue !== 'object' || Array.isArray(rawValue)) {
60670
60934
  return { failureCodes: ['badge renderer value must be an object'] };