@praxisui/dynamic-fields 3.0.0-beta.6 → 3.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.
@@ -1,9 +1,9 @@
1
1
  import * as i1$3 from '@angular/forms';
2
2
  import { NgControl, FormControl, Validators, ReactiveFormsModule, NG_VALUE_ACCESSOR, FormGroup, FormsModule } from '@angular/forms';
3
3
  import * as i0 from '@angular/core';
4
- import { InjectionToken, inject, DestroyRef, ElementRef, ChangeDetectorRef, Injector, signal, output, computed, Input, Directive, viewChild, effect, Injectable, EventEmitter, ViewContainerRef, ViewChild, HostListener, HostBinding, Output, ViewEncapsulation, ChangeDetectionStrategy, Component, Inject, forwardRef, LOCALE_ID, ViewChildren, TemplateRef, ContentChild, ENVIRONMENT_INITIALIZER, APP_INITIALIZER, Optional } from '@angular/core';
4
+ import { InjectionToken, inject, DestroyRef, ElementRef, ChangeDetectorRef, Injector, signal, output, computed, Input, Directive, viewChild, effect, Injectable, LOCALE_ID, EventEmitter, ViewContainerRef, ViewChild, HostListener, HostBinding, Output, ViewEncapsulation, ChangeDetectionStrategy, Component, Inject, forwardRef, ViewChildren, TemplateRef, ContentChild, ENVIRONMENT_INITIALIZER, APP_INITIALIZER, Optional } from '@angular/core';
5
5
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
6
- import { providePraxisI18n, PraxisI18nService, isCssTextTransform, getTextTransformer, GenericCrudService, GlobalConfigService, INLINE_FILTER_CONTROL_TYPES, FieldControlType, FieldSelectorRegistry, FIELD_SELECTOR_REGISTRY_BASE, FIELD_SELECTOR_REGISTRY_DISABLE_DEFAULTS, LoggerService, resolveInlineFilterControlType, DEFAULT_FIELD_SELECTOR_CONTROL_TYPE_MAP, PraxisIconDirective, resolveBuiltinPresets, createCpfCnpjValidator, NumericFormat, interpolatePraxisTranslation, ComponentMetadataRegistry, FIELD_METADATA_CAPABILITIES } from '@praxisui/core';
6
+ import { providePraxisI18n, PraxisI18nService, isCssTextTransform, getTextTransformer, GenericCrudService, GlobalConfigService, INLINE_FILTER_CONTROL_TYPES, FieldControlType, FieldSelectorRegistry, FIELD_SELECTOR_REGISTRY_BASE, FIELD_SELECTOR_REGISTRY_DISABLE_DEFAULTS, LoggerService, resolveInlineFilterControlType, DEFAULT_FIELD_SELECTOR_CONTROL_TYPE_MAP, resolveValuePresentation, FieldDataType, PraxisIconDirective, resolveBuiltinPresets, createCpfCnpjValidator, NumericFormat, interpolatePraxisTranslation, ComponentMetadataRegistry, FIELD_METADATA_CAPABILITIES } from '@praxisui/core';
7
7
  import { BehaviorSubject, combineLatest, Subscription, firstValueFrom, fromEvent, take as take$1, of, EMPTY } from 'rxjs';
8
8
  import { Router } from '@angular/router';
9
9
  import * as i1$2 from '@angular/material/dialog';
@@ -12,7 +12,7 @@ import { take, startWith, debounceTime, filter, distinctUntilChanged, switchMap,
12
12
  import * as i4$2 from '@angular/material/select';
13
13
  import { MatSelect, MatSelectModule } from '@angular/material/select';
14
14
  import * as i1 from '@angular/common';
15
- import { CommonModule, registerLocaleData, CurrencyPipe } from '@angular/common';
15
+ import { formatDate, CommonModule, registerLocaleData, CurrencyPipe } from '@angular/common';
16
16
  import * as i1$1 from '@angular/material/button';
17
17
  import { MatButtonModule } from '@angular/material/button';
18
18
  import * as i3 from '@angular/material/icon';
@@ -4147,47 +4147,33 @@ const LoggerPresets = {
4147
4147
  }
4148
4148
  };
4149
4149
 
4150
+ const EMPTY_DISPLAY = '-- Nao informado';
4150
4151
  function isISODate(v) {
4151
4152
  return /^\d{4}-\d{2}-\d{2}$/.test(v);
4152
4153
  }
4153
4154
  function isISODateTimeLocal(v) {
4154
4155
  return /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2})?$/.test(v);
4155
4156
  }
4156
- function tryFormatDate(v) {
4157
- try {
4158
- const d = new Date(v);
4159
- if (!isNaN(d.getTime()))
4160
- return d.toLocaleDateString();
4161
- }
4162
- catch { }
4163
- return v;
4164
- }
4165
- function tryFormatDateTime(v) {
4166
- try {
4167
- const d = new Date(v.replace('T', ' '));
4168
- if (!isNaN(d.getTime()))
4169
- return d.toLocaleString();
4170
- }
4171
- catch { }
4172
- return v.replace('T', ' ');
4173
- }
4174
- function formatDisplayValue(field, value) {
4157
+ function formatDisplayValue(field, value, options) {
4175
4158
  if (value == null)
4176
- return '— Não informado';
4177
- if (typeof value === 'boolean') {
4178
- return value ? 'Sim' : 'Não';
4179
- }
4180
- // Arrays → join labels or values
4159
+ return EMPTY_DISPLAY;
4181
4160
  if (Array.isArray(value)) {
4182
4161
  const parts = value.map((item) => {
4183
4162
  if (item && typeof item === 'object') {
4184
- return item.label ?? item.name ?? String(item.id ?? '');
4163
+ return (item.label ??
4164
+ item.name ??
4165
+ String(item.id ?? ''));
4185
4166
  }
4186
4167
  return String(item);
4187
4168
  });
4188
4169
  return parts.filter(Boolean).join(', ');
4189
4170
  }
4190
- // Option-like objects
4171
+ if (value instanceof Date) {
4172
+ const explicit = resolveFieldPresentation(field, value, options);
4173
+ return explicit
4174
+ ? formatTypedValue(field, value, explicit)
4175
+ : formatHeuristicDateTime(value, options);
4176
+ }
4191
4177
  if (value && typeof value === 'object') {
4192
4178
  const obj = value;
4193
4179
  if (obj.label)
@@ -4205,29 +4191,30 @@ function formatDisplayValue(field, value) {
4205
4191
  return String(obj);
4206
4192
  }
4207
4193
  }
4194
+ const presentation = resolveFieldPresentation(field, value, options);
4208
4195
  if (typeof value === 'string') {
4209
4196
  const trimmed = value.trim();
4210
4197
  if (!trimmed)
4211
- return '— Não informado';
4198
+ return EMPTY_DISPLAY;
4199
+ if (presentation) {
4200
+ return formatTypedValue(field, trimmed, presentation);
4201
+ }
4212
4202
  if (isISODate(trimmed))
4213
- return tryFormatDate(trimmed);
4214
- if (isISODateTimeLocal(trimmed))
4215
- return tryFormatDateTime(trimmed);
4203
+ return formatHeuristicDate(trimmed, options);
4204
+ if (isISODateTimeLocal(trimmed)) {
4205
+ return formatHeuristicDateTime(trimmed, options);
4206
+ }
4216
4207
  return trimmed;
4217
4208
  }
4218
4209
  if (typeof value === 'number') {
4219
- const ct = String(field?.controlType || '').toLowerCase();
4220
- if (ct.includes('currency')) {
4221
- try {
4222
- const currency = field?.currency || 'BRL';
4223
- return new Intl.NumberFormat(undefined, {
4224
- style: 'currency',
4225
- currency,
4226
- }).format(value);
4227
- }
4228
- catch {
4229
- return String(value);
4230
- }
4210
+ if (presentation) {
4211
+ return formatTypedValue(field, value, presentation);
4212
+ }
4213
+ return String(value);
4214
+ }
4215
+ if (typeof value === 'boolean') {
4216
+ if (presentation) {
4217
+ return formatTypedValue(field, value, presentation);
4231
4218
  }
4232
4219
  return String(value);
4233
4220
  }
@@ -4237,22 +4224,437 @@ function formatDisplayValue(field, value) {
4237
4224
  * Formats a value for presentation and linkifies emails/URLs.
4238
4225
  * Rely on Angular's built-in sanitization when binding via [innerHTML].
4239
4226
  */
4240
- function formatDisplayHtml(field, value) {
4241
- const text = formatDisplayValue(field, value);
4242
- // Email
4227
+ function formatDisplayHtml(field, value, options) {
4228
+ const text = formatDisplayValue(field, value, options);
4243
4229
  const emailRe = /^([A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,})$/i;
4244
4230
  if (emailRe.test(text)) {
4245
4231
  const mailto = `mailto:${text}`;
4246
4232
  return `<a href="${mailto}">${text}</a>`;
4247
4233
  }
4248
- // URL (http/https only)
4249
4234
  const urlRe = /^(https?:\/\/[^\s]+)$/i;
4250
4235
  if (urlRe.test(text)) {
4251
- const url = text;
4252
- return `<a href="${url}" target="_blank" rel="noopener noreferrer">${text}</a>`;
4236
+ return `<a href="${text}" target="_blank" rel="noopener noreferrer">${text}</a>`;
4253
4237
  }
4254
4238
  return text;
4255
4239
  }
4240
+ function resolveFieldPresentation(field, value, options) {
4241
+ const config = buildValuePresentationConfig(field, value);
4242
+ if (!config)
4243
+ return null;
4244
+ return resolveValuePresentation(config, {
4245
+ valueLocale: resolveFieldLocale(field),
4246
+ localization: mergeLocalization(options?.localization, buildLegacyFieldLocalization(field)),
4247
+ surfaceLocale: options?.surfaceLocale || undefined,
4248
+ appLocale: options?.appLocale || undefined,
4249
+ });
4250
+ }
4251
+ function buildValuePresentationConfig(field, value) {
4252
+ const explicit = normalizeExplicitValuePresentation(field?.valuePresentation);
4253
+ if (explicit) {
4254
+ return {
4255
+ ...explicit,
4256
+ currency: {
4257
+ ...resolveLegacyCurrencyOverrides(field),
4258
+ ...(explicit.currency || {}),
4259
+ },
4260
+ number: {
4261
+ ...resolveLegacyNumberOverrides(field),
4262
+ ...(explicit.number || {}),
4263
+ },
4264
+ };
4265
+ }
4266
+ const inferredType = inferValuePresentationType(field, value);
4267
+ if (!inferredType)
4268
+ return null;
4269
+ return {
4270
+ type: inferredType,
4271
+ style: inferLegacyPresentationStyle(field),
4272
+ format: normalizeString(field?.format) || undefined,
4273
+ currency: resolveLegacyCurrencyOverrides(field),
4274
+ number: resolveLegacyNumberOverrides(field),
4275
+ };
4276
+ }
4277
+ function normalizeExplicitValuePresentation(value) {
4278
+ if (!value || typeof value !== 'object')
4279
+ return null;
4280
+ const config = value;
4281
+ if (!config.type)
4282
+ return null;
4283
+ return config;
4284
+ }
4285
+ function inferValuePresentationType(field, value) {
4286
+ const controlType = String(field?.controlType || '').trim().toLowerCase();
4287
+ const dataType = String(field?.dataType || '').trim().toLowerCase();
4288
+ const numericFormat = String(field?.numericFormat || '').trim().toLowerCase();
4289
+ const format = normalizeString(field?.format)?.toLowerCase();
4290
+ if (field?.currency || field?.numberFormat?.currency) {
4291
+ return 'currency';
4292
+ }
4293
+ if (numericFormat === 'percent' || format === 'percent') {
4294
+ return 'percentage';
4295
+ }
4296
+ if (controlType === 'currency' ||
4297
+ controlType === 'inlinecurrency') {
4298
+ return 'currency';
4299
+ }
4300
+ if (controlType === 'date' ||
4301
+ controlType === 'dateinput' ||
4302
+ controlType === 'inlinedate') {
4303
+ return 'date';
4304
+ }
4305
+ if (controlType === 'datetime' ||
4306
+ controlType === 'datetimelocal' ||
4307
+ controlType === 'datetimepicker') {
4308
+ return 'datetime';
4309
+ }
4310
+ if (controlType === 'time' ||
4311
+ controlType === 'timepicker' ||
4312
+ controlType === 'inlinetime') {
4313
+ return 'time';
4314
+ }
4315
+ if (controlType === 'numerictextbox' ||
4316
+ controlType === 'inlinenumber' ||
4317
+ controlType === 'year' ||
4318
+ controlType === 'month') {
4319
+ return 'number';
4320
+ }
4321
+ if (dataType === FieldDataType.DATE) {
4322
+ return 'date';
4323
+ }
4324
+ if (dataType === FieldDataType.NUMBER) {
4325
+ return 'number';
4326
+ }
4327
+ if (typeof value === 'boolean') {
4328
+ return 'boolean';
4329
+ }
4330
+ if (typeof value === 'string') {
4331
+ if (isISODate(value.trim()))
4332
+ return 'date';
4333
+ if (isISODateTimeLocal(value.trim()))
4334
+ return 'datetime';
4335
+ }
4336
+ return null;
4337
+ }
4338
+ function inferLegacyPresentationStyle(field) {
4339
+ const style = normalizeString(field?.displayStyle || field?.styleVariant);
4340
+ switch ((style || '').toLowerCase()) {
4341
+ case 'short':
4342
+ return 'short';
4343
+ case 'medium':
4344
+ return 'medium';
4345
+ case 'long':
4346
+ return 'long';
4347
+ case 'full':
4348
+ return 'full';
4349
+ case 'compact':
4350
+ return 'compact';
4351
+ default:
4352
+ return undefined;
4353
+ }
4354
+ }
4355
+ function resolveFieldLocale(field) {
4356
+ return (normalizeString(field?.locale) ||
4357
+ normalizeString(field?.numberFormat?.locale) ||
4358
+ undefined);
4359
+ }
4360
+ function buildLegacyFieldLocalization(field) {
4361
+ const locale = resolveFieldLocale(field);
4362
+ const currency = resolveLegacyCurrencyOverrides(field);
4363
+ const number = resolveLegacyNumberOverrides(field);
4364
+ if (!locale && !currency && !number) {
4365
+ return undefined;
4366
+ }
4367
+ return {
4368
+ ...(locale ? { locale } : {}),
4369
+ direction: 'ltr',
4370
+ ...(currency ? { currency } : {}),
4371
+ ...(number ? { number } : {}),
4372
+ };
4373
+ }
4374
+ function resolveLegacyCurrencyOverrides(field) {
4375
+ const code = normalizeString(field?.currency) ||
4376
+ normalizeString(field?.numberFormat?.currency);
4377
+ const position = normalizeCurrencyPosition(field?.currencyPosition) ||
4378
+ normalizeCurrencyPosition(field?.numberFormat?.currencyPosition);
4379
+ const precision = resolveLegacyPrecision(field);
4380
+ if (!code && !position && precision == null) {
4381
+ return undefined;
4382
+ }
4383
+ return {
4384
+ code: code || 'USD',
4385
+ symbol: code || 'USD',
4386
+ position: position || 'before',
4387
+ spacing: true,
4388
+ precision: precision ?? 2,
4389
+ };
4390
+ }
4391
+ function resolveLegacyNumberOverrides(field) {
4392
+ const precision = resolveLegacyPrecision(field);
4393
+ const decimalSeparator = normalizeString(field?.decimalSeparator);
4394
+ const thousandsSeparator = normalizeString(field?.thousandsSeparator);
4395
+ if (precision == null &&
4396
+ !decimalSeparator &&
4397
+ !thousandsSeparator) {
4398
+ return undefined;
4399
+ }
4400
+ return {
4401
+ defaultPrecision: precision ?? 2,
4402
+ decimalSeparator: decimalSeparator || '.',
4403
+ thousandsSeparator: thousandsSeparator || ',',
4404
+ negativeSign: '-',
4405
+ negativeSignPosition: 'before',
4406
+ };
4407
+ }
4408
+ function resolveLegacyPrecision(field) {
4409
+ const direct = toFiniteNumber(field?.decimalPlaces);
4410
+ if (direct != null)
4411
+ return direct;
4412
+ const nested = toFiniteNumber(field?.numberFormat?.decimalPlaces);
4413
+ if (nested != null)
4414
+ return nested;
4415
+ return undefined;
4416
+ }
4417
+ function normalizeCurrencyPosition(value) {
4418
+ return value === 'before' || value === 'after' ? value : undefined;
4419
+ }
4420
+ function mergeLocalization(base, override) {
4421
+ if (!base && !override)
4422
+ return undefined;
4423
+ return {
4424
+ ...(base || {}),
4425
+ ...(override || {}),
4426
+ ...((base?.dateTime || override?.dateTime)
4427
+ ? {
4428
+ dateTime: {
4429
+ ...(base?.dateTime || {}),
4430
+ ...(override?.dateTime || {}),
4431
+ },
4432
+ }
4433
+ : {}),
4434
+ ...((base?.number || override?.number)
4435
+ ? {
4436
+ number: {
4437
+ ...(base?.number || {}),
4438
+ ...(override?.number || {}),
4439
+ },
4440
+ }
4441
+ : {}),
4442
+ ...((base?.currency || override?.currency)
4443
+ ? {
4444
+ currency: {
4445
+ ...(base?.currency || {}),
4446
+ ...(override?.currency || {}),
4447
+ },
4448
+ }
4449
+ : {}),
4450
+ ...((base?.formatting || override?.formatting)
4451
+ ? {
4452
+ formatting: {
4453
+ ...(base?.formatting || {}),
4454
+ ...(override?.formatting || {}),
4455
+ },
4456
+ }
4457
+ : {}),
4458
+ };
4459
+ }
4460
+ function formatTypedValue(field, value, presentation) {
4461
+ switch (presentation.type) {
4462
+ case 'boolean':
4463
+ return formatBooleanPresentation(value, presentation.locale);
4464
+ case 'date':
4465
+ case 'datetime':
4466
+ case 'time':
4467
+ return formatDatePresentation(value, presentation);
4468
+ case 'currency':
4469
+ return formatNumberPresentation(value, presentation, {
4470
+ style: 'currency',
4471
+ currency: presentation.currency?.code || 'USD',
4472
+ currencyPosition: presentation.currency?.position ||
4473
+ normalizeCurrencyPosition(field?.currencyPosition),
4474
+ });
4475
+ case 'percentage':
4476
+ return formatNumberPresentation(value, presentation, {
4477
+ style: 'percent',
4478
+ });
4479
+ case 'number':
4480
+ return formatNumberPresentation(value, presentation, {
4481
+ style: 'decimal',
4482
+ });
4483
+ default:
4484
+ return String(value);
4485
+ }
4486
+ }
4487
+ function formatBooleanPresentation(value, locale) {
4488
+ const [trueLabel, falseLabel] = resolveBooleanLabels(locale);
4489
+ if (typeof value === 'string') {
4490
+ const normalized = value.trim().toLowerCase();
4491
+ if (normalized === 'true')
4492
+ return trueLabel;
4493
+ if (normalized === 'false')
4494
+ return falseLabel;
4495
+ }
4496
+ return value ? trueLabel : falseLabel;
4497
+ }
4498
+ function resolveBooleanLabels(locale) {
4499
+ const normalized = String(locale || '').trim().toLowerCase();
4500
+ if (normalized.startsWith('pt')) {
4501
+ return ['Sim', 'Nao'];
4502
+ }
4503
+ if (normalized.startsWith('es')) {
4504
+ return ['Si', 'No'];
4505
+ }
4506
+ if (normalized.startsWith('fr')) {
4507
+ return ['Oui', 'Non'];
4508
+ }
4509
+ if (normalized.startsWith('de')) {
4510
+ return ['Ja', 'Nein'];
4511
+ }
4512
+ return ['Yes', 'No'];
4513
+ }
4514
+ function formatDatePresentation(value, presentation) {
4515
+ const dt = presentation.type === 'date'
4516
+ ? coerceDateOnlyCalendarValue(value)
4517
+ : coerceDateValue(value, presentation.type);
4518
+ if (Number.isNaN(dt.getTime())) {
4519
+ return String(value);
4520
+ }
4521
+ try {
4522
+ return formatDate(dt, presentation.format || 'shortDate', presentation.locale);
4523
+ }
4524
+ catch {
4525
+ return new Intl.DateTimeFormat(presentation.locale || undefined).format(dt);
4526
+ }
4527
+ }
4528
+ function formatNumberPresentation(value, presentation, kind) {
4529
+ const numericValue = typeof value === 'number'
4530
+ ? value
4531
+ : value instanceof Date
4532
+ ? value.getTime()
4533
+ : Number(value);
4534
+ if (!Number.isFinite(numericValue)) {
4535
+ return String(value);
4536
+ }
4537
+ const digits = parseDigitsFormat(presentation.format);
4538
+ const options = {
4539
+ style: kind.style,
4540
+ };
4541
+ if (digits) {
4542
+ options.minimumIntegerDigits = digits.minimumIntegerDigits;
4543
+ options.minimumFractionDigits = digits.minimumFractionDigits;
4544
+ options.maximumFractionDigits = digits.maximumFractionDigits;
4545
+ }
4546
+ if (kind.style === 'currency') {
4547
+ options.currency = kind.currency || 'USD';
4548
+ const parts = (presentation.format || '').split('|').filter(Boolean);
4549
+ if ((parts[1] || '').trim() === 'code') {
4550
+ options.currencyDisplay = 'code';
4551
+ }
4552
+ }
4553
+ const formatter = new Intl.NumberFormat(presentation.locale || undefined, options);
4554
+ const formatted = formatter.format(numericValue);
4555
+ if (kind.style !== 'currency' || !kind.currencyPosition) {
4556
+ return formatted;
4557
+ }
4558
+ return applyCurrencyPosition(formatter, numericValue, formatted, kind.currencyPosition);
4559
+ }
4560
+ function applyCurrencyPosition(formatter, value, formatted, position) {
4561
+ const parts = formatter.formatToParts(value);
4562
+ const currency = parts
4563
+ .filter((part) => part.type === 'currency')
4564
+ .map((part) => part.value)
4565
+ .join('');
4566
+ if (!currency)
4567
+ return formatted;
4568
+ const withoutCurrency = parts
4569
+ .filter((part) => part.type !== 'currency')
4570
+ .map((part) => part.value)
4571
+ .join('')
4572
+ .trim();
4573
+ if (!withoutCurrency)
4574
+ return formatted;
4575
+ return position === 'after'
4576
+ ? `${withoutCurrency} ${currency}`
4577
+ : `${currency} ${withoutCurrency}`;
4578
+ }
4579
+ function parseDigitsFormat(format) {
4580
+ if (!format)
4581
+ return null;
4582
+ const match = /^(\d+)\.(\d+)-(\d+)$/.exec(format.trim());
4583
+ if (!match)
4584
+ return null;
4585
+ return {
4586
+ minimumIntegerDigits: Number(match[1]),
4587
+ minimumFractionDigits: Number(match[2]),
4588
+ maximumFractionDigits: Number(match[3]),
4589
+ };
4590
+ }
4591
+ function formatHeuristicDate(value, options) {
4592
+ try {
4593
+ return formatDate(coerceDateValue(value, 'date'), 'shortDate', options?.surfaceLocale || options?.appLocale || 'en-US');
4594
+ }
4595
+ catch {
4596
+ return value;
4597
+ }
4598
+ }
4599
+ function formatHeuristicDateTime(value, options) {
4600
+ try {
4601
+ const source = coerceDateValue(value, 'datetime');
4602
+ return formatDate(source, 'short', options?.surfaceLocale || options?.appLocale || 'en-US');
4603
+ }
4604
+ catch {
4605
+ return value instanceof Date ? String(value) : value.replace('T', ' ');
4606
+ }
4607
+ }
4608
+ function toFiniteNumber(value) {
4609
+ if (typeof value !== 'number' || Number.isNaN(value)) {
4610
+ return undefined;
4611
+ }
4612
+ return Math.floor(value);
4613
+ }
4614
+ function normalizeString(value) {
4615
+ if (typeof value !== 'string')
4616
+ return undefined;
4617
+ const trimmed = value.trim();
4618
+ return trimmed ? trimmed : undefined;
4619
+ }
4620
+ function coerceDateValue(value, type) {
4621
+ if (value instanceof Date) {
4622
+ return value;
4623
+ }
4624
+ if (typeof value === 'string') {
4625
+ const trimmed = value.trim();
4626
+ if (type === 'date' && isISODate(trimmed)) {
4627
+ const [year, month, day] = trimmed.split('-').map(Number);
4628
+ return new Date(year, month - 1, day);
4629
+ }
4630
+ if (type === 'datetime' && isISODateTimeLocal(trimmed)) {
4631
+ return new Date(trimmed);
4632
+ }
4633
+ }
4634
+ return new Date(value);
4635
+ }
4636
+ function coerceDateOnlyCalendarValue(value) {
4637
+ if (value instanceof Date) {
4638
+ if (Number.isNaN(value.getTime())) {
4639
+ return value;
4640
+ }
4641
+ const localMidnight = value.getHours() === 0 &&
4642
+ value.getMinutes() === 0 &&
4643
+ value.getSeconds() === 0 &&
4644
+ value.getMilliseconds() === 0;
4645
+ if (localMidnight) {
4646
+ return new Date(value.getFullYear(), value.getMonth(), value.getDate());
4647
+ }
4648
+ const utcMidnight = value.getUTCHours() === 0 &&
4649
+ value.getUTCMinutes() === 0 &&
4650
+ value.getUTCSeconds() === 0 &&
4651
+ value.getUTCMilliseconds() === 0;
4652
+ if (utcMidnight) {
4653
+ return new Date(value.getUTCFullYear(), value.getUTCMonth(), value.getUTCDate());
4654
+ }
4655
+ }
4656
+ return coerceDateValue(value, 'date');
4657
+ }
4256
4658
 
4257
4659
  /**
4258
4660
  * Internal wrapper used by {@link DynamicFieldLoaderDirective}.
@@ -4262,6 +4664,7 @@ function formatDisplayHtml(field, value) {
4262
4664
  * project additional chrome around the field.
4263
4665
  */
4264
4666
  class FieldShellComponent {
4667
+ hostLocale = inject(LOCALE_ID, { optional: true }) ?? 'en-US';
4265
4668
  /** Metadata for the dynamic field being rendered. */
4266
4669
  field;
4267
4670
  /** Index position of the field within the directive's list. */
@@ -4375,11 +4778,21 @@ class FieldShellComponent {
4375
4778
  const raw = this.control?.value;
4376
4779
  try {
4377
4780
  const fn = this.field?.transformDisplayValue;
4378
- const formatted = typeof fn === 'function' ? fn(raw) : formatDisplayValue(this.field, raw);
4781
+ const formatted = typeof fn === 'function'
4782
+ ? fn(raw)
4783
+ : formatDisplayValue(this.field, raw, {
4784
+ appLocale: this.hostLocale,
4785
+ surfaceLocale: this.field?.localization?.locale ?? this.field?.locale ?? undefined,
4786
+ localization: this.field?.localization ?? undefined,
4787
+ });
4379
4788
  return formatted == null ? '' : String(formatted);
4380
4789
  }
4381
4790
  catch {
4382
- return formatDisplayValue(this.field, raw);
4791
+ return formatDisplayValue(this.field, raw, {
4792
+ appLocale: this.hostLocale,
4793
+ surfaceLocale: this.field?.localization?.locale ?? this.field?.locale ?? undefined,
4794
+ localization: this.field?.localization ?? undefined,
4795
+ });
4383
4796
  }
4384
4797
  }
4385
4798
  getPresentationHtml() {
@@ -4388,10 +4801,18 @@ class FieldShellComponent {
4388
4801
  const fn = this.field?.transformDisplayValue;
4389
4802
  const formatted = typeof fn === 'function' ? fn(raw) : null;
4390
4803
  const val = formatted == null ? raw : formatted;
4391
- return formatDisplayHtml(this.field, val);
4804
+ return formatDisplayHtml(this.field, val, {
4805
+ appLocale: this.hostLocale,
4806
+ surfaceLocale: this.field?.localization?.locale ?? this.field?.locale ?? undefined,
4807
+ localization: this.field?.localization ?? undefined,
4808
+ });
4392
4809
  }
4393
4810
  catch {
4394
- return formatDisplayHtml(this.field, raw);
4811
+ return formatDisplayHtml(this.field, raw, {
4812
+ appLocale: this.hostLocale,
4813
+ surfaceLocale: this.field?.localization?.locale ?? this.field?.locale ?? undefined,
4814
+ localization: this.field?.localization ?? undefined,
4815
+ });
4395
4816
  }
4396
4817
  }
4397
4818
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: FieldShellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
@@ -5004,9 +5425,12 @@ class DynamicFieldLoaderDirective {
5004
5425
  });
5005
5426
  this.renderFields();
5006
5427
  }
5007
- if (changes['readonlyMode'] ||
5428
+ if (changes['presentationMode']) {
5429
+ this.dbg('[DFL] ngOnChanges: presentationMode changed -> re-rendering fields');
5430
+ this.renderFields();
5431
+ }
5432
+ else if (changes['readonlyMode'] ||
5008
5433
  changes['disabledMode'] ||
5009
- changes['presentationMode'] ||
5010
5434
  changes['visible']) {
5011
5435
  this.applyGlobalStates();
5012
5436
  }
@@ -5497,6 +5921,15 @@ class DynamicFieldLoaderDirective {
5497
5921
  }));
5498
5922
  }
5499
5923
  this.shellSubscriptions.set(field.name, subscriptions);
5924
+ const shouldSkipComponentCreation = shellRef.instance.effectivePresentationMode &&
5925
+ !shellRef.instance.renderContentInPresentation();
5926
+ if (shouldSkipComponentCreation) {
5927
+ this.dbg('[DFL] step: skip field component creation in presentation mode', {
5928
+ name: field.name,
5929
+ });
5930
+ this.shellRefs.set(field.name, shellRef);
5931
+ return null;
5932
+ }
5500
5933
  // Criar o componente dentro do shell
5501
5934
  this.dbg('[DFL] step: create field component start', { name: field.name });
5502
5935
  const componentRef = shellRef.instance.vc.createComponent(componentType);
@@ -9022,15 +9455,15 @@ class MaterialDatepickerComponent extends SimpleBaseInputComponent {
9022
9455
  presentationMode = false;
9023
9456
  minDate = computed(() => {
9024
9457
  const md = this.metadata()?.minDate;
9025
- return typeof md === 'string' ? new Date(md) : md;
9458
+ return typeof md === 'string' ? this.parseDateOnlyString(md) : md;
9026
9459
  }, ...(ngDevMode ? [{ debugName: "minDate" }] : []));
9027
9460
  maxDate = computed(() => {
9028
9461
  const md = this.metadata()?.maxDate;
9029
- return typeof md === 'string' ? new Date(md) : md;
9462
+ return typeof md === 'string' ? this.parseDateOnlyString(md) : md;
9030
9463
  }, ...(ngDevMode ? [{ debugName: "maxDate" }] : []));
9031
9464
  startAt = computed(() => {
9032
9465
  const sa = this.metadata()?.startAt;
9033
- return typeof sa === 'string' ? new Date(sa) : (sa ?? null);
9466
+ return typeof sa === 'string' ? this.parseDateOnlyString(sa) : (sa ?? null);
9034
9467
  }, ...(ngDevMode ? [{ debugName: "startAt" }] : []));
9035
9468
  onDateChange(event) {
9036
9469
  this.setValue(event.value ?? null);
@@ -9101,6 +9534,14 @@ class MaterialDatepickerComponent extends SimpleBaseInputComponent {
9101
9534
  }
9102
9535
  return metadata;
9103
9536
  }
9537
+ parseDateOnlyString(value) {
9538
+ const trimmed = value.trim();
9539
+ if (/^\d{4}-\d{2}-\d{2}$/.test(trimmed)) {
9540
+ const [year, month, day] = trimmed.split('-').map(Number);
9541
+ return new Date(year, month - 1, day);
9542
+ }
9543
+ return new Date(trimmed);
9544
+ }
9104
9545
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: MaterialDatepickerComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
9105
9546
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: MaterialDatepickerComponent, isStandalone: true, selector: "pdx-material-datepicker", inputs: { readonlyMode: "readonlyMode", disabledMode: "disabledMode", visible: "visible", presentationMode: "presentationMode" }, outputs: { validationChange: "validationChange" }, host: { properties: { "class": "componentCssClasses()", "class.praxis-disabled": "disabledMode", "style.display": "visible ? null : \"none\"", "attr.aria-hidden": "visible ? null : \"true\"", "attr.data-field-type": "\"date\"", "attr.data-field-name": "metadata()?.name", "attr.data-component-id": "componentId()" } }, providers: [
9106
9547
  {
@@ -48061,6 +48502,14 @@ class DateUtilsService {
48061
48502
  const date = new Date(y, m - 1, d, h, min, s, ms);
48062
48503
  return isNaN(date.getTime()) ? null : date;
48063
48504
  }
48505
+ if (typeof value === 'string') {
48506
+ const trimmed = value.trim();
48507
+ if (/^\d{4}-\d{2}-\d{2}$/.test(trimmed)) {
48508
+ const [year, month, day] = trimmed.split('-').map(Number);
48509
+ const date = new Date(year, month - 1, day);
48510
+ return isNaN(date.getTime()) ? null : date;
48511
+ }
48512
+ }
48064
48513
  try {
48065
48514
  const parsed = new Date(value);
48066
48515
  return isNaN(parsed.getTime()) ? null : parsed;
@@ -48323,11 +48772,11 @@ const OVERVIEW_SLUG = 'dynamic-fields-overview';
48323
48772
  const SELECTION_GUIDE_SLUG = 'dynamic-fields-field-selection-guide';
48324
48773
  const INLINE_SELECTION_GUIDE_SLUG = 'dynamic-fields-inline-filter-selection-guide';
48325
48774
  const DEFAULT_A11Y_NOTES = [
48326
- 'Use labels claros e nao dependa apenas de placeholder.',
48327
- 'Garanta foco visivel, contraste AA e mensagens de erro textuais.',
48775
+ 'Use clear labels and do not rely on placeholders alone.',
48776
+ 'Ensure visible focus, AA contrast, and textual error messages.',
48328
48777
  ];
48329
48778
  const DEFAULT_THEMING_NOTES = [
48330
- 'O preview deve respeitar tokens do host e nao introduzir skin isolada da landing.',
48779
+ 'The preview must respect host tokens and avoid introducing a landing-only skin.',
48331
48780
  ];
48332
48781
  function detailFragment(controlType) {
48333
48782
  return controlType.toLowerCase().replace(/[^a-z0-9-]+/g, '-');
@@ -48335,6 +48784,95 @@ function detailFragment(controlType) {
48335
48784
  function jsonApiPath(relativePath) {
48336
48785
  return `projects/praxis-dynamic-fields/src/lib/components/${relativePath}`;
48337
48786
  }
48787
+ function defaultIconSemantic(input) {
48788
+ const controlType = input.controlType.toLowerCase();
48789
+ let key;
48790
+ let tone;
48791
+ if (controlType.includes('button')) {
48792
+ key = 'action';
48793
+ tone = 'amber';
48794
+ }
48795
+ else if (controlType.includes('cron')) {
48796
+ key = 'automation';
48797
+ tone = 'violet';
48798
+ }
48799
+ else if (controlType.includes('upload') ||
48800
+ controlType.includes('avatar') ||
48801
+ controlType.includes('color')) {
48802
+ key = 'visual';
48803
+ tone = 'teal';
48804
+ }
48805
+ else {
48806
+ switch (input.family) {
48807
+ case 'text':
48808
+ key = 'text';
48809
+ tone = 'blue';
48810
+ break;
48811
+ case 'numbers-range':
48812
+ key = 'numeric';
48813
+ tone = 'amber';
48814
+ break;
48815
+ case 'date-time':
48816
+ key = 'date-time';
48817
+ tone = 'violet';
48818
+ break;
48819
+ case 'selection':
48820
+ key = 'selection';
48821
+ tone = 'green';
48822
+ break;
48823
+ case 'trees-lists':
48824
+ key = 'tree-list';
48825
+ tone = 'slate';
48826
+ break;
48827
+ case 'toggle-choice':
48828
+ key = 'toggle';
48829
+ tone = 'rose';
48830
+ break;
48831
+ case 'upload-color-visual':
48832
+ key = 'visual';
48833
+ tone = 'teal';
48834
+ break;
48835
+ default:
48836
+ key = 'text';
48837
+ tone = 'blue';
48838
+ break;
48839
+ }
48840
+ }
48841
+ return { key, tone };
48842
+ }
48843
+ function defaultInteractionPattern(input) {
48844
+ const controlType = input.controlType.toLowerCase();
48845
+ if (controlType.includes('textarea'))
48846
+ return 'multi-line';
48847
+ if (controlType.includes('button') || controlType.includes('cron'))
48848
+ return 'trigger';
48849
+ if (controlType.includes('toggle') || controlType.includes('checkbox') || controlType.includes('radio'))
48850
+ return 'toggle';
48851
+ if (controlType.includes('autocomplete') ||
48852
+ controlType.includes('searchable') ||
48853
+ controlType.includes('async') ||
48854
+ controlType.includes('entity-lookup')) {
48855
+ return 'lookup';
48856
+ }
48857
+ if (controlType.includes('multi') || controlType.includes('chips') || controlType.includes('transfer')) {
48858
+ return 'multi-choice';
48859
+ }
48860
+ if (controlType.includes('range') || controlType.includes('period') || controlType.includes('distance')) {
48861
+ return 'range';
48862
+ }
48863
+ if (controlType.includes('avatar') ||
48864
+ controlType.includes('color') ||
48865
+ controlType.includes('rating') ||
48866
+ controlType.includes('pipeline') ||
48867
+ controlType.includes('sentiment') ||
48868
+ controlType.includes('score')) {
48869
+ return 'visual';
48870
+ }
48871
+ if (input.family === 'selection' || input.family === 'trees-lists') {
48872
+ return 'single-choice';
48873
+ }
48874
+ return 'single-value';
48875
+ }
48338
48876
  function createEntry(input) {
48339
48877
  const states = input.states ?? ['default', 'filled', 'disabled', 'readonly', 'presentation', 'error'];
48340
48878
  return {
@@ -48375,6 +48913,8 @@ function createEntry(input) {
48375
48913
  `{ "name": "${input.id}", "label": "${input.friendlyName}", "controlType": "${input.controlType}" }`,
48376
48914
  note: input.snippetNote,
48377
48915
  },
48916
+ icon: input.icon ?? defaultIconSemantic({ controlType: input.controlType, family: input.family }),
48917
+ interactionPattern: input.interactionPattern ?? defaultInteractionPattern({ controlType: input.controlType, family: input.family }),
48378
48918
  a11yNotes: input.a11yNotes ?? DEFAULT_A11Y_NOTES,
48379
48919
  themingNotes: input.themingNotes ?? DEFAULT_THEMING_NOTES,
48380
48920
  };
@@ -48395,11 +48935,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
48395
48935
  track: 'primary-form',
48396
48936
  family: 'text',
48397
48937
  friendlyName: 'Text input',
48398
- description: 'Entrada textual curta e canonica para formularios metadata-driven.',
48938
+ description: 'Short canonical text input for metadata-driven forms.',
48399
48939
  tags: ['text', 'input', 'form'],
48400
48940
  valueShape: 'string',
48401
- recommendedWhen: ['texto livre curto', 'nome', 'descricao curta'],
48402
- avoidWhen: ['lista governada de opcoes', 'texto longo multi-linha'],
48941
+ recommendedWhen: ['short free text', 'name', 'short description'],
48942
+ avoidWhen: ['governed option list', 'long multi-line text'],
48403
48943
  dataSourceKind: 'local',
48404
48944
  apiPath: jsonApiPath('text-input/pdx-text-input.json-api.md'),
48405
48945
  metadata: { placeholder: 'Type here' },
@@ -48413,11 +48953,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
48413
48953
  track: 'primary-form',
48414
48954
  family: 'text',
48415
48955
  friendlyName: 'Email input',
48416
- description: 'Campo textual especializado para emails.',
48956
+ description: 'Specialized text field for email addresses.',
48417
48957
  tags: ['text', 'email', 'validation'],
48418
48958
  valueShape: 'string',
48419
- recommendedWhen: ['email validado', 'contato institucional'],
48420
- avoidWhen: ['texto livre generico'],
48959
+ recommendedWhen: ['validated email', 'institutional contact'],
48960
+ avoidWhen: ['generic free text'],
48421
48961
  dataSourceKind: 'local',
48422
48962
  apiPath: jsonApiPath('email-input/pdx-email-input.json-api.md'),
48423
48963
  metadata: { placeholder: 'name@company.com' },
@@ -48428,11 +48968,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
48428
48968
  track: 'primary-form',
48429
48969
  family: 'text',
48430
48970
  friendlyName: 'Password input',
48431
- description: 'Campo de segredo com mascaramento e interacao apropriada.',
48971
+ description: 'Secret field with masking and appropriate interaction behavior.',
48432
48972
  tags: ['text', 'password', 'security'],
48433
48973
  valueShape: 'string',
48434
- recommendedWhen: ['segredos', 'credenciais'],
48435
- avoidWhen: ['valor que precisa ficar legivel em tela'],
48974
+ recommendedWhen: ['secrets', 'credentials'],
48975
+ avoidWhen: ['value that must remain visible on screen'],
48436
48976
  dataSourceKind: 'local',
48437
48977
  apiPath: jsonApiPath('password-input/pdx-password-input.json-api.md'),
48438
48978
  }),
@@ -48442,11 +48982,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
48442
48982
  track: 'primary-form',
48443
48983
  family: 'text',
48444
48984
  friendlyName: 'Textarea',
48445
- description: 'Campo multi-linha para observacoes e conteudo textual maior.',
48985
+ description: 'Multi-line field for notes and longer text content.',
48446
48986
  tags: ['text', 'textarea', 'multiline'],
48447
48987
  valueShape: 'string',
48448
- recommendedWhen: ['descricao longa', 'observacoes', 'justificativa'],
48449
- avoidWhen: ['texto curto de uma linha'],
48988
+ recommendedWhen: ['long description', 'notes', 'justification'],
48989
+ avoidWhen: ['short single-line text'],
48450
48990
  dataSourceKind: 'local',
48451
48991
  apiPath: jsonApiPath('material-textarea/pdx-material-textarea.json-api.md'),
48452
48992
  metadata: { rows: 4, placeholder: 'Describe the situation' },
@@ -48457,11 +48997,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
48457
48997
  track: 'primary-form',
48458
48998
  family: 'text',
48459
48999
  friendlyName: 'Search input',
48460
- description: 'Entrada textual dedicada a busca.',
49000
+ description: 'Text input dedicated to search.',
48461
49001
  tags: ['text', 'search', 'query'],
48462
49002
  valueShape: 'string',
48463
- recommendedWhen: ['busca textual', 'query curta'],
48464
- avoidWhen: ['campo de negocio comum sem semantica de busca'],
49003
+ recommendedWhen: ['text search', 'short query'],
49004
+ avoidWhen: ['common business field without search semantics'],
48465
49005
  dataSourceKind: 'local',
48466
49006
  apiPath: jsonApiPath('search-input/pdx-search-input.json-api.md'),
48467
49007
  metadata: { placeholder: 'Search records' },
@@ -48472,11 +49012,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
48472
49012
  track: 'primary-form',
48473
49013
  family: 'text',
48474
49014
  friendlyName: 'Phone input',
48475
- description: 'Campo especializado para telefone.',
49015
+ description: 'Specialized phone field.',
48476
49016
  tags: ['text', 'phone', 'formatted'],
48477
49017
  valueShape: 'string',
48478
- recommendedWhen: ['telefone', 'contato'],
48479
- avoidWhen: ['dominio sem telefone'],
49018
+ recommendedWhen: ['phone', 'contact'],
49019
+ avoidWhen: ['domain without phone data'],
48480
49020
  dataSourceKind: 'local',
48481
49021
  status: 'partial-docs',
48482
49022
  apiPath: jsonApiPath('phone-input/pdx-phone-input.json-api.md'),
@@ -48487,11 +49027,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
48487
49027
  track: 'primary-form',
48488
49028
  family: 'text',
48489
49029
  friendlyName: 'URL input',
48490
- description: 'Campo para links e URLs.',
49030
+ description: 'Field for links and URLs.',
48491
49031
  tags: ['text', 'url', 'link'],
48492
49032
  valueShape: 'string',
48493
- recommendedWhen: ['website', 'link institucional'],
48494
- avoidWhen: ['texto generico'],
49033
+ recommendedWhen: ['website', 'institutional link'],
49034
+ avoidWhen: ['generic text'],
48495
49035
  dataSourceKind: 'local',
48496
49036
  status: 'partial-docs',
48497
49037
  apiPath: jsonApiPath('url-input/pdx-url-input.json-api.md'),
@@ -48502,11 +49042,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
48502
49042
  track: 'primary-form',
48503
49043
  family: 'text',
48504
49044
  friendlyName: 'CPF/CNPJ input',
48505
- description: 'Campo regionalizado para documento brasileiro.',
49045
+ description: 'Regionalized field for Brazilian documents.',
48506
49046
  tags: ['text', 'brazil', 'document'],
48507
49047
  valueShape: 'string',
48508
- recommendedWhen: ['CPF', 'CNPJ', 'documento BR'],
48509
- avoidWhen: ['dominio internacional ou sem documento nacional'],
49048
+ recommendedWhen: ['CPF', 'CNPJ', 'Brazilian document'],
49049
+ avoidWhen: ['international domain or domain without a national document'],
48510
49050
  dataSourceKind: 'local',
48511
49051
  status: 'partial-docs',
48512
49052
  apiPath: jsonApiPath('material-cpf-cnpj-input/pdx-material-cpf-cnpj-input.json-api.md'),
@@ -48517,11 +49057,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
48517
49057
  track: 'primary-form',
48518
49058
  family: 'numbers-range',
48519
49059
  friendlyName: 'Number input',
48520
- description: 'Campo numerico canonico para valores pontuais.',
49060
+ description: 'Canonical numeric field for single values.',
48521
49061
  tags: ['number', 'numeric', 'form'],
48522
49062
  valueShape: 'number',
48523
- recommendedWhen: ['idade', 'quantidade', 'score unico'],
48524
- avoidWhen: ['range', 'moeda'],
49063
+ recommendedWhen: ['age', 'quantity', 'single score'],
49064
+ avoidWhen: ['range', 'currency'],
48525
49065
  dataSourceKind: 'local',
48526
49066
  status: 'partial-docs',
48527
49067
  apiPath: jsonApiPath('number-input/pdx-number-input.json-api.md'),
@@ -48532,12 +49072,13 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
48532
49072
  track: 'primary-form',
48533
49073
  family: 'numbers-range',
48534
49074
  friendlyName: 'Currency input',
48535
- description: 'Campo monetario para valor unico.',
49075
+ description: 'Currency field for a single monetary value.',
48536
49076
  tags: ['number', 'currency', 'money'],
48537
49077
  valueShape: 'number',
48538
- recommendedWhen: ['orcamento', 'ticket medio', 'valor monetario unico'],
48539
- avoidWhen: ['faixa monetaria'],
49078
+ recommendedWhen: ['budget', 'average ticket', 'single monetary value'],
49079
+ avoidWhen: ['currency range'],
48540
49080
  dataSourceKind: 'local',
49081
+ interactionPattern: 'single-value',
48541
49082
  apiPath: jsonApiPath('material-currency/pdx-material-currency.json-api.md'),
48542
49083
  metadata: { currency: 'BRL', locale: 'pt-BR' },
48543
49084
  stateRecipes: [
@@ -48550,11 +49091,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
48550
49091
  track: 'primary-form',
48551
49092
  family: 'numbers-range',
48552
49093
  friendlyName: 'Slider',
48553
- description: 'Campo de faixa continua para valor unico.',
49094
+ description: 'Continuous range control for a single value.',
48554
49095
  tags: ['number', 'slider', 'range'],
48555
49096
  valueShape: 'number',
48556
- recommendedWhen: ['nota', 'nivel', 'escala continua'],
48557
- avoidWhen: ['valor financeiro complexo', 'range duplo'],
49097
+ recommendedWhen: ['rating', 'level', 'continuous scale'],
49098
+ avoidWhen: ['complex financial value', 'dual range'],
48558
49099
  dataSourceKind: 'local',
48559
49100
  apiPath: jsonApiPath('material-slider/pdx-material-slider.json-api.md'),
48560
49101
  metadata: { min: 0, max: 10 },
@@ -48565,11 +49106,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
48565
49106
  track: 'primary-form',
48566
49107
  family: 'numbers-range',
48567
49108
  friendlyName: 'Range slider',
48568
- description: 'Campo para faixa numerica com um ou dois bounds.',
49109
+ description: 'Field for a numeric range with one or two bounds.',
48569
49110
  tags: ['number', 'range', 'slider'],
48570
49111
  valueShape: 'number | { start, end }',
48571
- recommendedWhen: ['faixa numerica', 'limite minimo e maximo'],
48572
- avoidWhen: ['datas ou horarios'],
49112
+ recommendedWhen: ['numeric range', 'minimum and maximum bound'],
49113
+ avoidWhen: ['dates or times'],
48573
49114
  dataSourceKind: 'local',
48574
49115
  status: 'partial-docs',
48575
49116
  apiPath: jsonApiPath('material-range-slider/pdx-material-range-slider.json-api.md'),
@@ -48580,12 +49121,13 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
48580
49121
  track: 'primary-form',
48581
49122
  family: 'numbers-range',
48582
49123
  friendlyName: 'Price range',
48583
- description: 'Faixa monetaria com semantica propria.',
49124
+ description: 'Currency range with dedicated semantics.',
48584
49125
  tags: ['number', 'currency', 'range'],
48585
49126
  valueShape: '{ min, max }',
48586
- recommendedWhen: ['faixa de preco', 'banda salarial', 'orcamento minimo/maximo'],
48587
- avoidWhen: ['valor monetario unico'],
49127
+ recommendedWhen: ['price range', 'salary band', 'minimum and maximum budget'],
49128
+ avoidWhen: ['single monetary value'],
48588
49129
  dataSourceKind: 'local',
49130
+ interactionPattern: 'range',
48589
49131
  status: 'partial-docs',
48590
49132
  apiPath: jsonApiPath('material-price-range/pdx-material-price-range.json-api.md'),
48591
49133
  metadata: { currency: 'BRL', locale: 'pt-BR' },
@@ -48596,12 +49138,14 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
48596
49138
  track: 'primary-form',
48597
49139
  family: 'numbers-range',
48598
49140
  friendlyName: 'Rating',
48599
- description: 'Controle visual de nota.',
49141
+ description: 'Visual rating control.',
48600
49142
  tags: ['number', 'rating', 'visual'],
48601
49143
  valueShape: 'number',
48602
- recommendedWhen: ['avaliacao visual', 'satisfacao', 'score legivel'],
48603
- avoidWhen: ['quando numero bruto ja basta'],
49144
+ recommendedWhen: ['visual rating', 'satisfaction', 'readable score'],
49145
+ avoidWhen: ['when a raw number is enough'],
48604
49146
  dataSourceKind: 'local',
49147
+ icon: { key: 'visual', tone: 'amber' },
49148
+ interactionPattern: 'visual',
48605
49149
  apiPath: jsonApiPath('material-rating/pdx-material-rating.json-api.md'),
48606
49150
  metadata: { max: 5 },
48607
49151
  }),
@@ -48611,11 +49155,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
48611
49155
  track: 'primary-form',
48612
49156
  family: 'date-time',
48613
49157
  friendlyName: 'Date input',
48614
- description: 'Input nativo de data.',
49158
+ description: 'Native date input.',
48615
49159
  tags: ['date', 'native', 'time'],
48616
49160
  valueShape: 'string | Date',
48617
- recommendedWhen: ['integracao simples com input nativo'],
48618
- avoidWhen: ['quando a UX Material padronizada e obrigatoria'],
49161
+ recommendedWhen: ['simple integration with a native input'],
49162
+ avoidWhen: ['when standardized Material UX is required'],
48619
49163
  dataSourceKind: 'local',
48620
49164
  status: 'partial-docs',
48621
49165
  apiPath: jsonApiPath('date-input/pdx-date-input.json-api.md'),
@@ -48626,11 +49170,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
48626
49170
  track: 'primary-form',
48627
49171
  family: 'date-time',
48628
49172
  friendlyName: 'Date picker',
48629
- description: 'Datepicker Material canonico.',
49173
+ description: 'Canonical Material datepicker.',
48630
49174
  tags: ['date', 'picker', 'material'],
48631
49175
  valueShape: 'Date | string',
48632
- recommendedWhen: ['data unica', 'consistencia de design system'],
48633
- avoidWhen: ['range temporal'],
49176
+ recommendedWhen: ['single date', 'design system consistency'],
49177
+ avoidWhen: ['time range'],
48634
49178
  dataSourceKind: 'local',
48635
49179
  apiPath: jsonApiPath('material-datepicker/pdx-material-datepicker.json-api.md'),
48636
49180
  }),
@@ -48640,11 +49184,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
48640
49184
  track: 'primary-form',
48641
49185
  family: 'date-time',
48642
49186
  friendlyName: 'Date range',
48643
- description: 'Intervalo de datas.',
49187
+ description: 'Date interval.',
48644
49188
  tags: ['date', 'range', 'time'],
48645
49189
  valueShape: '{ startDate, endDate }',
48646
- recommendedWhen: ['periodo', 'janela temporal'],
48647
- avoidWhen: ['data unica'],
49190
+ recommendedWhen: ['period', 'time window'],
49191
+ avoidWhen: ['single date'],
48648
49192
  dataSourceKind: 'local',
48649
49193
  apiPath: jsonApiPath('material-date-range/pdx-material-date-range.json-api.md'),
48650
49194
  stateRecipes: [
@@ -48657,11 +49201,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
48657
49201
  track: 'primary-form',
48658
49202
  family: 'date-time',
48659
49203
  friendlyName: 'Datetime local',
48660
- description: 'Campo unificado para data e hora local.',
49204
+ description: 'Unified field for local date and time.',
48661
49205
  tags: ['date', 'time', 'datetime'],
48662
49206
  valueShape: 'string',
48663
- recommendedWhen: ['agendamento local', 'data + hora no mesmo campo'],
48664
- avoidWhen: ['quando data e hora precisam de UX separada'],
49207
+ recommendedWhen: ['local scheduling', 'date and time in the same field'],
49208
+ avoidWhen: ['when date and time need separate UX'],
48665
49209
  dataSourceKind: 'local',
48666
49210
  status: 'partial-docs',
48667
49211
  apiPath: jsonApiPath('datetime-local-input/pdx-datetime-local-input.json-api.md'),
@@ -48672,11 +49216,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
48672
49216
  track: 'primary-form',
48673
49217
  family: 'date-time',
48674
49218
  friendlyName: 'Month input',
48675
- description: 'Campo especializado para mes e ano.',
49219
+ description: 'Specialized month and year field.',
48676
49220
  tags: ['date', 'month', 'competence'],
48677
49221
  valueShape: 'string',
48678
- recommendedWhen: ['competencia', 'mes/ano'],
48679
- avoidWhen: ['data completa'],
49222
+ recommendedWhen: ['accounting period', 'month/year'],
49223
+ avoidWhen: ['full date'],
48680
49224
  dataSourceKind: 'local',
48681
49225
  status: 'partial-docs',
48682
49226
  apiPath: jsonApiPath('month-input/pdx-month-input.json-api.md'),
@@ -48687,11 +49231,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
48687
49231
  track: 'primary-form',
48688
49232
  family: 'date-time',
48689
49233
  friendlyName: 'Time input',
48690
- description: 'Campo simples para horario.',
49234
+ description: 'Simple time field.',
48691
49235
  tags: ['time', 'input', 'hour'],
48692
49236
  valueShape: 'string',
48693
- recommendedWhen: ['hora simples', 'slot horario'],
48694
- avoidWhen: ['range horario'],
49237
+ recommendedWhen: ['single time', 'time slot'],
49238
+ avoidWhen: ['time range'],
48695
49239
  dataSourceKind: 'local',
48696
49240
  apiPath: jsonApiPath('time-input/pdx-time-input.json-api.md'),
48697
49241
  }),
@@ -48701,11 +49245,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
48701
49245
  track: 'primary-form',
48702
49246
  family: 'date-time',
48703
49247
  friendlyName: 'Time picker',
48704
- description: 'Seletor Material de horario.',
49248
+ description: 'Material time picker.',
48705
49249
  tags: ['time', 'picker', 'hour'],
48706
49250
  valueShape: 'string',
48707
- recommendedWhen: ['escolha guiada de horario'],
48708
- avoidWhen: ['texto/mascara simples e suficiente'],
49251
+ recommendedWhen: ['guided time selection'],
49252
+ avoidWhen: ['simple text or mask is enough'],
48709
49253
  dataSourceKind: 'local',
48710
49254
  apiPath: jsonApiPath('material-timepicker/pdx-material-timepicker.json-api.md'),
48711
49255
  }),
@@ -48715,11 +49259,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
48715
49259
  track: 'primary-form',
48716
49260
  family: 'date-time',
48717
49261
  friendlyName: 'Time range',
48718
- description: 'Intervalo de horario.',
49262
+ description: 'Time interval.',
48719
49263
  tags: ['time', 'range', 'hour'],
48720
49264
  valueShape: '{ start, end }',
48721
- recommendedWhen: ['janela de atendimento', 'turno'],
48722
- avoidWhen: ['horario unico'],
49265
+ recommendedWhen: ['service window', 'shift'],
49266
+ avoidWhen: ['single time'],
48723
49267
  dataSourceKind: 'local',
48724
49268
  status: 'partial-docs',
48725
49269
  apiPath: jsonApiPath('pdx-material-time-range/pdx-material-time-range.json-api.md'),
@@ -48730,11 +49274,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
48730
49274
  track: 'primary-form',
48731
49275
  family: 'date-time',
48732
49276
  friendlyName: 'Week input',
48733
- description: 'Campo de semana ISO.',
49277
+ description: 'ISO week field.',
48734
49278
  tags: ['date', 'week', 'iso'],
48735
49279
  valueShape: 'string',
48736
- recommendedWhen: ['semana de referencia'],
48737
- avoidWhen: ['negocio centrado em datas convencionais'],
49280
+ recommendedWhen: ['reference week'],
49281
+ avoidWhen: ['domain centered on conventional calendar dates'],
48738
49282
  dataSourceKind: 'local',
48739
49283
  status: 'partial-docs',
48740
49284
  apiPath: jsonApiPath('week-input/pdx-week-input.json-api.md'),
@@ -48745,11 +49289,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
48745
49289
  track: 'primary-form',
48746
49290
  family: 'date-time',
48747
49291
  friendlyName: 'Year input',
48748
- description: 'Campo para ano isolado.',
49292
+ description: 'Field for a standalone year.',
48749
49293
  tags: ['date', 'year', 'fiscal'],
48750
49294
  valueShape: 'number | string',
48751
- recommendedWhen: ['ano fiscal', 'ano civil isolado'],
48752
- avoidWhen: ['mes/ano ou data completa'],
49295
+ recommendedWhen: ['fiscal year', 'standalone calendar year'],
49296
+ avoidWhen: ['month/year or full date'],
48753
49297
  dataSourceKind: 'local',
48754
49298
  status: 'partial-docs',
48755
49299
  apiPath: jsonApiPath('pdx-year-input/pdx-year-input.json-api.md'),
@@ -48760,11 +49304,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
48760
49304
  track: 'primary-form',
48761
49305
  family: 'selection',
48762
49306
  friendlyName: 'Select',
48763
- description: 'Lista fechada simples para escolha unica.',
49307
+ description: 'Simple closed list for a single selection.',
48764
49308
  tags: ['selection', 'select', 'options'],
48765
49309
  valueShape: 'string | number | object',
48766
- recommendedWhen: ['lista pequena estavel', 'escolha unica'],
48767
- avoidWhen: ['lista grande ou remota'],
49310
+ recommendedWhen: ['small stable list', 'single selection'],
49311
+ avoidWhen: ['large or remote list'],
48768
49312
  dataSourceKind: 'mixed',
48769
49313
  apiPath: jsonApiPath('material-select/pdx-material-select.json-api.md'),
48770
49314
  metadata: {
@@ -48783,14 +49327,28 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
48783
49327
  track: 'primary-form',
48784
49328
  family: 'selection',
48785
49329
  friendlyName: 'Searchable select',
48786
- description: 'Select com busca local para listas medias ou grandes.',
49330
+ description: 'Select with local search for medium or large lists.',
48787
49331
  tags: ['selection', 'select', 'search'],
48788
49332
  valueShape: 'string | number | object',
48789
- recommendedWhen: ['lista maior com busca', 'descoberta de opcao'],
48790
- avoidWhen: ['lista remota sob demanda'],
49333
+ recommendedWhen: ['larger list with search', 'option discovery'],
49334
+ avoidWhen: ['remote on-demand list'],
48791
49335
  dataSourceKind: 'mixed',
49336
+ interactionPattern: 'lookup',
48792
49337
  status: 'partial-docs',
48793
49338
  apiPath: jsonApiPath('material-searchable-select/pdx-material-searchable-select.json-api.md'),
49339
+ metadata: {
49340
+ options: [
49341
+ { label: 'Finance', value: 'FINANCE' },
49342
+ { label: 'Operations', value: 'OPERATIONS' },
49343
+ { label: 'People', value: 'PEOPLE' },
49344
+ { label: 'Risk', value: 'RISK' },
49345
+ ],
49346
+ searchable: true,
49347
+ placeholder: 'Search and select',
49348
+ },
49349
+ stateRecipes: [
49350
+ createDefaultSeedRecipe('OPERATIONS', 'Seeded default preview to reveal local search and selected-label rendering.'),
49351
+ ],
48794
49352
  }),
48795
49353
  createEntry({
48796
49354
  id: 'async-select',
@@ -48798,12 +49356,13 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
48798
49356
  track: 'primary-form',
48799
49357
  family: 'selection',
48800
49358
  friendlyName: 'Async select',
48801
- description: 'Select remoto para opcoes sob demanda.',
49359
+ description: 'Remote select for on-demand options.',
48802
49360
  tags: ['selection', 'remote', 'async'],
48803
49361
  valueShape: 'string | number | object',
48804
- recommendedWhen: ['lookup remoto', 'lista paginada', 'catalogo grande'],
48805
- avoidWhen: ['opcoes locais pequenas'],
49362
+ recommendedWhen: ['remote lookup', 'paginated list', 'large catalog'],
49363
+ avoidWhen: ['small local options'],
48806
49364
  dataSourceKind: 'remote',
49365
+ interactionPattern: 'lookup',
48807
49366
  status: 'partial-docs',
48808
49367
  apiPath: jsonApiPath('material-async-select/pdx-material-async-select.json-api.md'),
48809
49368
  metadata: { resourcePath: 'cities', optionLabelKey: 'name', optionValueKey: 'id' },
@@ -48814,13 +49373,25 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
48814
49373
  track: 'primary-form',
48815
49374
  family: 'selection',
48816
49375
  friendlyName: 'Autocomplete',
48817
- description: 'Busca incremental com sugestoes.',
49376
+ description: 'Incremental search with suggestions.',
48818
49377
  tags: ['selection', 'autocomplete', 'search'],
48819
49378
  valueShape: 'string | number | object',
48820
- recommendedWhen: ['sugestoes incrementais', 'descoberta textual'],
48821
- avoidWhen: ['lista pequena e fechada'],
49379
+ recommendedWhen: ['incremental suggestions', 'textual discovery'],
49380
+ avoidWhen: ['small closed list'],
48822
49381
  dataSourceKind: 'mixed',
49382
+ interactionPattern: 'lookup',
48823
49383
  apiPath: jsonApiPath('material-autocomplete/pdx-material-autocomplete.json-api.md'),
49384
+ metadata: {
49385
+ options: [
49386
+ { label: 'Analyst', value: 'ANALYST' },
49387
+ { label: 'Architect', value: 'ARCHITECT' },
49388
+ { label: 'Manager', value: 'MANAGER' },
49389
+ ],
49390
+ placeholder: 'Start typing a role',
49391
+ },
49392
+ stateRecipes: [
49393
+ createDefaultSeedRecipe('ARCHITECT', 'Seeded default preview to show suggestion-driven discovery instead of an empty shell.'),
49394
+ ],
48824
49395
  }),
48825
49396
  createEntry({
48826
49397
  id: 'multi-select',
@@ -48828,11 +49399,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
48828
49399
  track: 'primary-form',
48829
49400
  family: 'selection',
48830
49401
  friendlyName: 'Multi select',
48831
- description: 'Selecao multipla de opcoes.',
49402
+ description: 'Multi-select options.',
48832
49403
  tags: ['selection', 'multiple', 'options'],
48833
49404
  valueShape: 'unknown[]',
48834
- recommendedWhen: ['multiplas escolhas discretas'],
48835
- avoidWhen: ['escolha unica'],
49405
+ recommendedWhen: ['multiple discrete choices'],
49406
+ avoidWhen: ['single selection'],
48836
49407
  dataSourceKind: 'mixed',
48837
49408
  status: 'partial-docs',
48838
49409
  apiPath: jsonApiPath('material-multi-select/pdx-material-multi-select.json-api.md'),
@@ -48843,12 +49414,14 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
48843
49414
  track: 'primary-form',
48844
49415
  family: 'trees-lists',
48845
49416
  friendlyName: 'Multi select tree',
48846
- description: 'Arvore hierarquica com selecao multipla.',
49417
+ description: 'Hierarchical tree with multi-selection.',
48847
49418
  tags: ['tree', 'selection', 'hierarchy'],
48848
49419
  valueShape: 'unknown[]',
48849
- recommendedWhen: ['taxonomia hierarquica com multiplas escolhas'],
48850
- avoidWhen: ['lista plana pequena'],
49420
+ recommendedWhen: ['hierarchical taxonomy with multiple selections'],
49421
+ avoidWhen: ['small flat list'],
48851
49422
  dataSourceKind: 'mixed',
49423
+ icon: { key: 'tree-list', tone: 'slate' },
49424
+ interactionPattern: 'multi-choice',
48852
49425
  status: 'partial-docs',
48853
49426
  apiPath: jsonApiPath('material-multi-select-tree/pdx-material-multi-select-tree.json-api.md'),
48854
49427
  }),
@@ -48858,12 +49431,14 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
48858
49431
  track: 'primary-form',
48859
49432
  family: 'trees-lists',
48860
49433
  friendlyName: 'Tree select',
48861
- description: 'Arvore hierarquica com selecao simples.',
49434
+ description: 'Hierarchical tree with single selection.',
48862
49435
  tags: ['tree', 'selection', 'hierarchy'],
48863
49436
  valueShape: 'string | number | object',
48864
- recommendedWhen: ['estrutura hierarquica com escolha unica'],
48865
- avoidWhen: ['multiplas escolhas'],
49437
+ recommendedWhen: ['hierarchical structure with a single selection'],
49438
+ avoidWhen: ['multiple selections'],
48866
49439
  dataSourceKind: 'mixed',
49440
+ icon: { key: 'tree-list', tone: 'slate' },
49441
+ interactionPattern: 'single-choice',
48867
49442
  status: 'partial-docs',
48868
49443
  apiPath: jsonApiPath('material-tree-select/pdx-material-tree-select.json-api.md'),
48869
49444
  }),
@@ -48873,11 +49448,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
48873
49448
  track: 'primary-form',
48874
49449
  family: 'trees-lists',
48875
49450
  friendlyName: 'Selection list',
48876
- description: 'Lista explicita de opcoes selecionaveis.',
49451
+ description: 'Explicit list of selectable options.',
48877
49452
  tags: ['list', 'selection', 'explicit'],
48878
49453
  valueShape: 'unknown[]',
48879
- recommendedWhen: ['lista visivel de opcoes', 'affordance forte de lista'],
48880
- avoidWhen: ['dropdown compacto resolve melhor'],
49454
+ recommendedWhen: ['visible list of options', 'strong list affordance'],
49455
+ avoidWhen: ['a compact dropdown is a better fit'],
48881
49456
  dataSourceKind: 'mixed',
48882
49457
  status: 'partial-docs',
48883
49458
  apiPath: jsonApiPath('material-selection-list/pdx-material-selection-list.json-api.md'),
@@ -48888,12 +49463,14 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
48888
49463
  track: 'primary-form',
48889
49464
  family: 'trees-lists',
48890
49465
  friendlyName: 'Transfer list',
48891
- description: 'Move itens entre listas disponivel e selecionado.',
49466
+ description: 'Moves items between available and selected lists.',
48892
49467
  tags: ['list', 'transfer', 'selection'],
48893
49468
  valueShape: 'unknown[]',
48894
- recommendedWhen: ['alocacao entre disponiveis e selecionados'],
48895
- avoidWhen: ['selecao simples ou pequena'],
49469
+ recommendedWhen: ['allocation between available and selected items'],
49470
+ avoidWhen: ['simple or small selection'],
48896
49471
  dataSourceKind: 'mixed',
49472
+ icon: { key: 'tree-list', tone: 'slate' },
49473
+ interactionPattern: 'multi-choice',
48897
49474
  status: 'partial-docs',
48898
49475
  apiPath: jsonApiPath('material-transfer-list/pdx-material-transfer-list.json-api.md'),
48899
49476
  }),
@@ -48903,11 +49480,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
48903
49480
  track: 'primary-form',
48904
49481
  family: 'trees-lists',
48905
49482
  friendlyName: 'Chip input',
48906
- description: 'Entrada de itens textuais em formato de chips.',
49483
+ description: 'Text item input in chip format.',
48907
49484
  tags: ['chips', 'list', 'text'],
48908
49485
  valueShape: 'string[]',
48909
- recommendedWhen: ['emails', 'labels editaveis', 'lista textual'],
48910
- avoidWhen: ['valor unico'],
49486
+ recommendedWhen: ['email addresses', 'editable labels', 'text list'],
49487
+ avoidWhen: ['single value'],
48911
49488
  dataSourceKind: 'local',
48912
49489
  status: 'partial-docs',
48913
49490
  apiPath: jsonApiPath('material-chips/pdx-material-chips.json-api.md'),
@@ -48918,11 +49495,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
48918
49495
  track: 'primary-form',
48919
49496
  family: 'trees-lists',
48920
49497
  friendlyName: 'Chip list',
48921
- description: 'Lista textual em chips.',
49498
+ description: 'Text list in chips.',
48922
49499
  tags: ['chips', 'list', 'display'],
48923
49500
  valueShape: 'string[]',
48924
- recommendedWhen: ['lista curta em chips', 'edicao leve'],
48925
- avoidWhen: ['valor unico'],
49501
+ recommendedWhen: ['short list in chips', 'light editing'],
49502
+ avoidWhen: ['single value'],
48926
49503
  dataSourceKind: 'local',
48927
49504
  status: 'partial-docs',
48928
49505
  apiPath: jsonApiPath('material-chips/pdx-material-chips.json-api.md'),
@@ -48933,13 +49510,23 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
48933
49510
  track: 'primary-form',
48934
49511
  family: 'toggle-choice',
48935
49512
  friendlyName: 'Checkbox group',
48936
- description: 'Escolhas discretas booleanas ou multiplas.',
49513
+ description: 'Discrete boolean or multiple choices.',
48937
49514
  tags: ['choice', 'checkbox', 'boolean'],
48938
49515
  valueShape: 'boolean | unknown[]',
48939
- recommendedWhen: ['multiplas escolhas discretas', 'booleano com selectionMode apropriado'],
48940
- avoidWhen: ['escolha unica'],
49516
+ recommendedWhen: ['multiple discrete choices', 'boolean with the appropriate selectionMode'],
49517
+ avoidWhen: ['single selection'],
48941
49518
  dataSourceKind: 'mixed',
48942
49519
  apiPath: jsonApiPath('material-checkbox-group/pdx-material-checkbox-group.json-api.md'),
49520
+ metadata: {
49521
+ selectionMode: 'boolean',
49522
+ description: 'Enable publishing notifications for this record.',
49523
+ links: [{ label: 'Privacy terms', href: '/privacy' }],
49524
+ },
49525
+ snippet: `{ "name": "privacyConsent", "controlType": "checkbox", "selectionMode": "boolean", "description": "Enable publishing notifications for this record." }`,
49526
+ snippetNote: 'Checkbox is only didactic when selectionMode is explicit. Use `boolean` for a single consent/flag or `multiple` for option groups.',
49527
+ stateRecipes: [
49528
+ createDefaultSeedRecipe(true, 'Seeded default preview to make the boolean contract visible immediately.'),
49529
+ ],
48943
49530
  }),
48944
49531
  createEntry({
48945
49532
  id: 'radio-group',
@@ -48947,13 +49534,25 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
48947
49534
  track: 'primary-form',
48948
49535
  family: 'toggle-choice',
48949
49536
  friendlyName: 'Radio group',
48950
- description: 'Escolha unica explicita.',
49537
+ description: 'Explicit single choice.',
48951
49538
  tags: ['choice', 'radio', 'single'],
48952
49539
  valueShape: 'string | number | boolean',
48953
- recommendedWhen: ['escolha unica com poucas opcoes e alta legibilidade'],
48954
- avoidWhen: ['muitas opcoes'],
49540
+ recommendedWhen: ['single choice with few options and high readability'],
49541
+ avoidWhen: ['many options'],
48955
49542
  dataSourceKind: 'mixed',
48956
49543
  apiPath: jsonApiPath('material-radio-group/pdx-material-radio-group.json-api.md'),
49544
+ metadata: {
49545
+ selectionMode: 'single',
49546
+ options: [
49547
+ { label: 'Low', value: 'LOW' },
49548
+ { label: 'Medium', value: 'MEDIUM' },
49549
+ { label: 'High', value: 'HIGH' },
49550
+ ],
49551
+ },
49552
+ snippet: `{ "name": "priority", "controlType": "radio", "selectionMode": "single", "options": [{ "label": "Low", "value": "LOW" }, { "label": "Medium", "value": "MEDIUM" }, { "label": "High", "value": "HIGH" }] }`,
49553
+ stateRecipes: [
49554
+ createDefaultSeedRecipe('MEDIUM', 'Seeded default preview to expose the explicit single-choice contract.'),
49555
+ ],
48957
49556
  }),
48958
49557
  createEntry({
48959
49558
  id: 'toggle',
@@ -48961,11 +49560,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
48961
49560
  track: 'primary-form',
48962
49561
  family: 'toggle-choice',
48963
49562
  friendlyName: 'Toggle',
48964
- description: 'Booleano binario on/off.',
49563
+ description: 'Binary on/off boolean.',
48965
49564
  tags: ['choice', 'toggle', 'boolean'],
48966
49565
  valueShape: 'boolean',
48967
- recommendedWhen: ['booleano simples'],
48968
- avoidWhen: ['mais de dois estados'],
49566
+ recommendedWhen: ['simple boolean'],
49567
+ avoidWhen: ['more than two states'],
48969
49568
  dataSourceKind: 'local',
48970
49569
  apiPath: jsonApiPath('material-slide-toggle/pdx-material-slide-toggle.json-api.md'),
48971
49570
  stateRecipes: [
@@ -48978,11 +49577,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
48978
49577
  track: 'primary-form',
48979
49578
  family: 'toggle-choice',
48980
49579
  friendlyName: 'Button toggle',
48981
- description: 'Grupo segmentado para poucas opcoes.',
49580
+ description: 'Segmented group for a small number of options.',
48982
49581
  tags: ['choice', 'toggle', 'segmented'],
48983
49582
  valueShape: 'string | number',
48984
- recommendedWhen: ['poucas opcoes curtas', 'segmentacao visivel'],
48985
- avoidWhen: ['lista longa'],
49583
+ recommendedWhen: ['few short options', 'visible segmentation'],
49584
+ avoidWhen: ['long list'],
48986
49585
  dataSourceKind: 'local',
48987
49586
  status: 'partial-docs',
48988
49587
  apiPath: jsonApiPath('material-button-toggle/pdx-material-button-toggle.json-api.md'),
@@ -48993,13 +49592,21 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
48993
49592
  track: 'primary-form',
48994
49593
  family: 'upload-color-visual',
48995
49594
  friendlyName: 'File upload',
48996
- description: 'Campo para anexos e documentos.',
49595
+ description: 'Field for attachments and documents.',
48997
49596
  tags: ['upload', 'files', 'attachments'],
48998
49597
  valueShape: 'File | File[] | metadata',
48999
- recommendedWhen: ['documentos', 'anexos', 'comprovantes'],
49000
- avoidWhen: ['fluxo sem arquivos'],
49598
+ recommendedWhen: ['documents', 'attachments', 'supporting documents'],
49599
+ avoidWhen: ['flow without files'],
49001
49600
  dataSourceKind: 'specialized',
49601
+ icon: { key: 'visual', tone: 'teal' },
49602
+ interactionPattern: 'trigger',
49002
49603
  apiPath: jsonApiPath('material-file-upload/pdx-material-file-upload.json-api.md'),
49604
+ metadata: {
49605
+ accept: '.pdf,.png,.jpg',
49606
+ hint: 'Current runtime is a lightweight placeholder based on native file input.',
49607
+ },
49608
+ snippet: `{ "name": "attachment", "controlType": "upload", "accept": ".pdf,.png,.jpg", "hint": "Current runtime is a lightweight placeholder based on native file input." }`,
49609
+ snippetNote: 'This preview intentionally demonstrates the current placeholder runtime, not a full asset-management experience.',
49003
49610
  }),
49004
49611
  createEntry({
49005
49612
  id: 'color-input',
@@ -49007,11 +49614,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
49007
49614
  track: 'primary-form',
49008
49615
  family: 'upload-color-visual',
49009
49616
  friendlyName: 'Color input',
49010
- description: 'Entrada simples de cor.',
49617
+ description: 'Simple color input.',
49011
49618
  tags: ['color', 'input', 'visual'],
49012
49619
  valueShape: 'string',
49013
- recommendedWhen: ['cor por valor simples'],
49014
- avoidWhen: ['paleta rica ou presets'],
49620
+ recommendedWhen: ['color by simple value'],
49621
+ avoidWhen: ['rich palette or presets'],
49015
49622
  dataSourceKind: 'local',
49016
49623
  apiPath: jsonApiPath('color-input/pdx-color-input.json-api.md'),
49017
49624
  }),
@@ -49021,11 +49628,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
49021
49628
  track: 'primary-form',
49022
49629
  family: 'upload-color-visual',
49023
49630
  friendlyName: 'Color picker',
49024
- description: 'Picker avancado de cor com paleta.',
49631
+ description: 'Advanced color picker with palette.',
49025
49632
  tags: ['color', 'picker', 'visual'],
49026
49633
  valueShape: 'string',
49027
- recommendedWhen: ['paleta rica', 'preset visual', 'tema'],
49028
- avoidWhen: ['valor simples textual basta'],
49634
+ recommendedWhen: ['rich palette', 'visual preset', 'theme'],
49635
+ avoidWhen: ['a simple textual value is enough'],
49029
49636
  dataSourceKind: 'local',
49030
49637
  apiPath: jsonApiPath('color-picker/pdx-color-picker.json-api.md'),
49031
49638
  }),
@@ -49035,13 +49642,26 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
49035
49642
  track: 'primary-form',
49036
49643
  family: 'upload-color-visual',
49037
49644
  friendlyName: 'Avatar',
49038
- description: 'Representacao visual de entidade/usuario.',
49645
+ description: 'Visual representation of an entity or user.',
49039
49646
  tags: ['visual', 'avatar', 'display'],
49040
49647
  valueShape: 'string | object',
49041
- recommendedWhen: ['representacao visual de usuario ou entidade'],
49042
- avoidWhen: ['captura de dado textual'],
49648
+ recommendedWhen: ['visual representation of a user or entity'],
49649
+ avoidWhen: ['text data capture'],
49043
49650
  dataSourceKind: 'specialized',
49651
+ icon: { key: 'visual', tone: 'teal' },
49652
+ interactionPattern: 'visual',
49044
49653
  apiPath: jsonApiPath('material-avatar/pdx-material-avatar.json-api.md'),
49654
+ metadata: {
49655
+ imageSrc: 'https://cdn.corp.example/avatar/u123.png',
49656
+ extra: {
49657
+ initials: 'AS',
49658
+ name: 'Ana Souza',
49659
+ ariaLabel: 'Avatar do responsavel',
49660
+ },
49661
+ },
49662
+ stateRecipes: [
49663
+ createDefaultSeedRecipe({ imageSrc: 'https://cdn.corp.example/avatar/u123.png', name: 'Ana Souza', initials: 'AS' }, 'Seeded default preview to show the visual contract instead of an empty shell.'),
49664
+ ],
49045
49665
  }),
49046
49666
  createEntry({
49047
49667
  id: 'button',
@@ -49049,14 +49669,23 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
49049
49669
  track: 'primary-form',
49050
49670
  family: 'upload-color-visual',
49051
49671
  friendlyName: 'Button',
49052
- description: 'Acao auxiliar embutida no layout do formulario.',
49672
+ description: 'Auxiliary action embedded in the form layout.',
49053
49673
  tags: ['action', 'button', 'visual'],
49054
49674
  valueShape: 'n/a',
49055
- recommendedWhen: ['acao auxiliar no layout'],
49056
- avoidWhen: ['submit/cancel padrao do form'],
49675
+ recommendedWhen: ['auxiliary action in the layout'],
49676
+ avoidWhen: ['default form submit or cancel action'],
49057
49677
  dataSourceKind: 'specialized',
49678
+ icon: { key: 'action', tone: 'amber' },
49679
+ interactionPattern: 'trigger',
49058
49680
  apiPath: jsonApiPath('material-button/pdx-material-button.json-api.md'),
49059
49681
  states: ['default', 'disabled', 'readonly'],
49682
+ metadata: {
49683
+ label: 'Validate data',
49684
+ action: 'validateForm',
49685
+ variant: 'stroked',
49686
+ },
49687
+ snippet: `{ "name": "validateBtn", "controlType": "button", "label": "Validate data", "action": "validateForm", "variant": "stroked" }`,
49688
+ snippetNote: 'Button previews are only meaningful when the action contract is explicit. Treat this as an embedded auxiliary action, not the form submit surface.',
49060
49689
  }),
49061
49690
  createEntry({
49062
49691
  id: 'cron-builder',
@@ -49064,13 +49693,22 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
49064
49693
  track: 'primary-form',
49065
49694
  family: 'upload-color-visual',
49066
49695
  friendlyName: 'Cron builder',
49067
- description: 'Controle especializado para expressoes CRON.',
49696
+ description: 'Specialized control for CRON expressions.',
49068
49697
  tags: ['cron', 'schedule', 'specialized'],
49069
49698
  valueShape: 'string',
49070
- recommendedWhen: ['agenda cron', 'frequencia recorrente'],
49071
- avoidWhen: ['datas ou ranges simples'],
49699
+ recommendedWhen: ['cron schedule', 'recurring frequency'],
49700
+ avoidWhen: ['dates or simple ranges'],
49072
49701
  dataSourceKind: 'specialized',
49702
+ icon: { key: 'automation', tone: 'violet' },
49703
+ interactionPattern: 'trigger',
49073
49704
  status: 'partial-docs',
49705
+ metadata: {
49706
+ placeholder: '0 9 * * 1-5',
49707
+ hint: 'Use when the business contract is truly cron-based, not just date or time range based.',
49708
+ },
49709
+ stateRecipes: [
49710
+ createDefaultSeedRecipe('0 9 * * 1-5', 'Seeded default preview to expose the recurring-expression contract immediately.'),
49711
+ ],
49074
49712
  snippetNote: 'A documentacao detalhada continua vivendo na lib especializada @praxisui/cron-builder.',
49075
49713
  }),
49076
49714
  createEntry({
@@ -49079,11 +49717,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
49079
49717
  track: 'inline-filter',
49080
49718
  family: 'text',
49081
49719
  friendlyName: 'Inline input',
49082
- description: 'Filtro textual compacto para toolbar.',
49720
+ description: 'Compact text filter for the toolbar.',
49083
49721
  tags: ['inline', 'filter', 'text'],
49084
49722
  valueShape: 'string',
49085
- recommendedWhen: ['busca curta em toolbar', 'identificador', 'nome'],
49086
- avoidWhen: ['texto longo', 'validacao complexa'],
49723
+ recommendedWhen: ['short search in the toolbar', 'identifier', 'name'],
49724
+ avoidWhen: ['long text', 'complex validation'],
49087
49725
  dataSourceKind: 'local',
49088
49726
  apiPath: jsonApiPath('inline-input/pdx-inline-input.json-api.md'),
49089
49727
  stateRecipes: [
@@ -49096,11 +49734,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
49096
49734
  track: 'inline-filter',
49097
49735
  family: 'selection',
49098
49736
  friendlyName: 'Inline select',
49099
- description: 'Selecao compacta em pill para filtros.',
49737
+ description: 'Compact pill selection for filters.',
49100
49738
  tags: ['inline', 'filter', 'selection'],
49101
49739
  valueShape: 'simple value',
49102
- recommendedWhen: ['lista pequena ou media em toolbar'],
49103
- avoidWhen: ['lookup remoto grande'],
49740
+ recommendedWhen: ['small or medium list in the toolbar'],
49741
+ avoidWhen: ['large remote lookup'],
49104
49742
  dataSourceKind: 'mixed',
49105
49743
  apiPath: jsonApiPath('inline-select/pdx-inline-select.json-api.md'),
49106
49744
  stateRecipes: [
@@ -49113,11 +49751,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
49113
49751
  track: 'inline-filter',
49114
49752
  family: 'selection',
49115
49753
  friendlyName: 'Inline searchable select',
49116
- description: 'Selecao compacta com busca.',
49754
+ description: 'Compact selection with search.',
49117
49755
  tags: ['inline', 'filter', 'selection', 'search'],
49118
49756
  valueShape: 'simple value | option object',
49119
- recommendedWhen: ['lista media/grande com busca em toolbar'],
49120
- avoidWhen: ['select simples ja resolve'],
49757
+ recommendedWhen: ['medium or large list with search in the toolbar'],
49758
+ avoidWhen: ['a simple select is enough'],
49121
49759
  dataSourceKind: 'mixed',
49122
49760
  apiPath: jsonApiPath('inline-searchable-select/pdx-inline-searchable-select.json-api.md'),
49123
49761
  }),
@@ -49127,11 +49765,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
49127
49765
  track: 'inline-filter',
49128
49766
  family: 'selection',
49129
49767
  friendlyName: 'Inline async select',
49130
- description: 'Selecao remota compacta para toolbar.',
49768
+ description: 'Compact remote selection for the toolbar.',
49131
49769
  tags: ['inline', 'filter', 'selection', 'remote'],
49132
49770
  valueShape: 'simple value | option object',
49133
- recommendedWhen: ['lookup remoto em toolbar', 'lista sob demanda'],
49134
- avoidWhen: ['lista local pequena'],
49771
+ recommendedWhen: ['remote lookup in the toolbar', 'on-demand list'],
49772
+ avoidWhen: ['small local list'],
49135
49773
  dataSourceKind: 'remote',
49136
49774
  apiPath: jsonApiPath('inline-async-select/pdx-inline-async-select.json-api.md'),
49137
49775
  }),
@@ -49141,11 +49779,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
49141
49779
  track: 'inline-filter',
49142
49780
  family: 'selection',
49143
49781
  friendlyName: 'Inline entity lookup',
49144
- description: 'Lookup compacto de entidade com semantica corporativa.',
49782
+ description: 'Compact entity lookup with enterprise semantics.',
49145
49783
  tags: ['inline', 'filter', 'lookup', 'remote'],
49146
49784
  valueShape: 'object | id',
49147
- recommendedWhen: ['id + descricao', 'identidade de entidade'],
49148
- avoidWhen: ['select local simples'],
49785
+ recommendedWhen: ['id and description', 'entity identity'],
49786
+ avoidWhen: ['simple local select'],
49149
49787
  dataSourceKind: 'remote',
49150
49788
  apiPath: jsonApiPath('inline-entity-lookup/pdx-inline-entity-lookup.json-api.md'),
49151
49789
  }),
@@ -49155,11 +49793,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
49155
49793
  track: 'inline-filter',
49156
49794
  family: 'selection',
49157
49795
  friendlyName: 'Inline autocomplete',
49158
- description: 'Autocomplete compacto para filtros.',
49796
+ description: 'Compact autocomplete for filters.',
49159
49797
  tags: ['inline', 'filter', 'autocomplete'],
49160
49798
  valueShape: 'simple value | option object',
49161
- recommendedWhen: ['sugestao incremental em toolbar'],
49162
- avoidWhen: ['select simples basta'],
49799
+ recommendedWhen: ['incremental suggestion in the toolbar'],
49800
+ avoidWhen: ['a simple select is enough'],
49163
49801
  dataSourceKind: 'mixed',
49164
49802
  apiPath: jsonApiPath('inline-autocomplete/pdx-inline-autocomplete.json-api.md'),
49165
49803
  }),
@@ -49169,11 +49807,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
49169
49807
  track: 'inline-filter',
49170
49808
  family: 'selection',
49171
49809
  friendlyName: 'Inline multi select',
49172
- description: 'Selecao multipla compacta com resumo em pill.',
49810
+ description: 'Compact multi-selection with pill summary.',
49173
49811
  tags: ['inline', 'filter', 'multiple'],
49174
49812
  valueShape: 'unknown[]',
49175
- recommendedWhen: ['poucas selecoes simultaneas com resumo curto'],
49176
- avoidWhen: ['necessidade de leitura integral de todos os itens'],
49813
+ recommendedWhen: ['few simultaneous selections with a short summary'],
49814
+ avoidWhen: ['need to read every selected item in full'],
49177
49815
  dataSourceKind: 'mixed',
49178
49816
  apiPath: jsonApiPath('inline-multi-select/pdx-inline-multi-select.json-api.md'),
49179
49817
  }),
@@ -49183,10 +49821,10 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
49183
49821
  track: 'inline-filter',
49184
49822
  family: 'numbers-range',
49185
49823
  friendlyName: 'Inline number',
49186
- description: 'Filtro numerico compacto.',
49824
+ description: 'Compact numeric filter.',
49187
49825
  tags: ['inline', 'filter', 'number'],
49188
49826
  valueShape: 'number',
49189
- recommendedWhen: ['valor numerico pontual em toolbar'],
49827
+ recommendedWhen: ['single numeric value in the toolbar'],
49190
49828
  avoidWhen: ['range'],
49191
49829
  dataSourceKind: 'local',
49192
49830
  apiPath: jsonApiPath('inline-number/pdx-inline-number.json-api.md'),
@@ -49197,11 +49835,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
49197
49835
  track: 'inline-filter',
49198
49836
  family: 'numbers-range',
49199
49837
  friendlyName: 'Inline currency',
49200
- description: 'Filtro monetario compacto.',
49838
+ description: 'Compact currency filter.',
49201
49839
  tags: ['inline', 'filter', 'currency'],
49202
49840
  valueShape: 'number',
49203
- recommendedWhen: ['valor monetario unico em toolbar'],
49204
- avoidWhen: ['faixa monetaria'],
49841
+ recommendedWhen: ['single monetary value in the toolbar'],
49842
+ avoidWhen: ['currency range'],
49205
49843
  dataSourceKind: 'local',
49206
49844
  apiPath: jsonApiPath('inline-currency/pdx-inline-currency.json-api.md'),
49207
49845
  }),
@@ -49211,11 +49849,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
49211
49849
  track: 'inline-filter',
49212
49850
  family: 'numbers-range',
49213
49851
  friendlyName: 'Inline currency range',
49214
- description: 'Faixa monetaria compacta para toolbar.',
49852
+ description: 'Compact currency range for the toolbar.',
49215
49853
  tags: ['inline', 'filter', 'currency', 'range'],
49216
49854
  valueShape: '{ minPrice?, maxPrice?, currency? }',
49217
- recommendedWhen: ['faixa monetaria em toolbar'],
49218
- avoidWhen: ['valor unico'],
49855
+ recommendedWhen: ['currency range in the toolbar'],
49856
+ avoidWhen: ['single value'],
49219
49857
  dataSourceKind: 'specialized',
49220
49858
  apiPath: jsonApiPath('inline-currency-range/pdx-inline-currency-range.json-api.md'),
49221
49859
  }),
@@ -49225,11 +49863,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
49225
49863
  track: 'inline-filter',
49226
49864
  family: 'numbers-range',
49227
49865
  friendlyName: 'Inline range',
49228
- description: 'Range numerico compacto para toolbar.',
49866
+ description: 'Compact numeric range for the toolbar.',
49229
49867
  tags: ['inline', 'filter', 'range', 'slider'],
49230
49868
  valueShape: 'number | { start?, end? }',
49231
- recommendedWhen: ['faixa numerica compacta'],
49232
- avoidWhen: ['datas ou horarios'],
49869
+ recommendedWhen: ['compact numeric range'],
49870
+ avoidWhen: ['dates or times'],
49233
49871
  dataSourceKind: 'specialized',
49234
49872
  apiPath: jsonApiPath('inline-currency-range/pdx-inline-currency-range.json-api.md'),
49235
49873
  detailFragment: 'inline-range',
@@ -49240,11 +49878,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
49240
49878
  track: 'inline-filter',
49241
49879
  family: 'numbers-range',
49242
49880
  friendlyName: 'Inline rating',
49243
- description: 'Filtro visual de rating para toolbar.',
49881
+ description: 'Visual rating filter for the toolbar.',
49244
49882
  tags: ['inline', 'filter', 'rating'],
49245
49883
  valueShape: 'number | { start?, end? }',
49246
- recommendedWhen: ['score visual', 'avaliacao em estrelas'],
49247
- avoidWhen: ['quando numero bruto ja basta'],
49884
+ recommendedWhen: ['visual score', 'star rating'],
49885
+ avoidWhen: ['when a raw number is enough'],
49248
49886
  dataSourceKind: 'specialized',
49249
49887
  apiPath: jsonApiPath('inline-rating/pdx-inline-rating.json-api.md'),
49250
49888
  }),
@@ -49254,12 +49892,13 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
49254
49892
  track: 'inline-filter',
49255
49893
  family: 'numbers-range',
49256
49894
  friendlyName: 'Inline distance radius',
49257
- description: 'Filtro semantico de distancia e raio.',
49895
+ description: 'Semantic distance and radius filter.',
49258
49896
  tags: ['inline', 'filter', 'distance', 'radius'],
49259
49897
  valueShape: 'number | { start?, end? }',
49260
- recommendedWhen: ['raio de busca', 'proximidade'],
49261
- avoidWhen: ['range numerico generico'],
49898
+ recommendedWhen: ['search radius', 'proximity'],
49899
+ avoidWhen: ['generic numeric range'],
49262
49900
  dataSourceKind: 'specialized',
49901
+ interactionPattern: 'range',
49263
49902
  apiPath: jsonApiPath('inline-distance-radius/pdx-inline-distance-radius.json-api.md'),
49264
49903
  }),
49265
49904
  createEntry({
@@ -49268,12 +49907,14 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
49268
49907
  track: 'inline-filter',
49269
49908
  family: 'numbers-range',
49270
49909
  friendlyName: 'Inline score priority',
49271
- description: 'Filtro semantico de score/prioridade.',
49910
+ description: 'Semantic score or priority filter.',
49272
49911
  tags: ['inline', 'filter', 'score', 'priority'],
49273
49912
  valueShape: 'number | { start?, end? }',
49274
- recommendedWhen: ['prioridade', 'criticidade', 'banda de score'],
49275
- avoidWhen: ['numero simples sem ganho semantico'],
49913
+ recommendedWhen: ['priority', 'criticality', 'score band'],
49914
+ avoidWhen: ['simple number with no semantic gain'],
49276
49915
  dataSourceKind: 'specialized',
49916
+ icon: { key: 'visual', tone: 'amber' },
49917
+ interactionPattern: 'visual',
49277
49918
  apiPath: jsonApiPath('inline-score-priority/pdx-inline-score-priority.json-api.md'),
49278
49919
  }),
49279
49920
  createEntry({
@@ -49282,11 +49923,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
49282
49923
  track: 'inline-filter',
49283
49924
  family: 'date-time',
49284
49925
  friendlyName: 'Inline date',
49285
- description: 'Filtro de data compacta.',
49926
+ description: 'Compact date filter.',
49286
49927
  tags: ['inline', 'filter', 'date'],
49287
49928
  valueShape: 'Date',
49288
- recommendedWhen: ['data unica em toolbar'],
49289
- avoidWhen: ['periodo completo'],
49929
+ recommendedWhen: ['single date in the toolbar'],
49930
+ avoidWhen: ['full period'],
49290
49931
  dataSourceKind: 'specialized',
49291
49932
  apiPath: jsonApiPath('inline-date/pdx-inline-date.json-api.md'),
49292
49933
  }),
@@ -49296,11 +49937,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
49296
49937
  track: 'inline-filter',
49297
49938
  family: 'date-time',
49298
49939
  friendlyName: 'Inline date range',
49299
- description: 'Range de data compacta com semantica de toolbar.',
49940
+ description: 'Compact date range with toolbar semantics.',
49300
49941
  tags: ['inline', 'filter', 'date', 'range'],
49301
49942
  valueShape: '{ startDate?, endDate? }',
49302
- recommendedWhen: ['janela temporal em toolbar'],
49303
- avoidWhen: ['toolbar saturada ou data unica'],
49943
+ recommendedWhen: ['time window in the toolbar'],
49944
+ avoidWhen: ['dense toolbar or a single date'],
49304
49945
  dataSourceKind: 'specialized',
49305
49946
  apiPath: jsonApiPath('inline-date-range/pdx-inline-date-range.json-api.md'),
49306
49947
  stateRecipes: [
@@ -49313,11 +49954,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
49313
49954
  track: 'inline-filter',
49314
49955
  family: 'date-time',
49315
49956
  friendlyName: 'Inline time',
49316
- description: 'Filtro compacto de horario.',
49957
+ description: 'Compact time filter.',
49317
49958
  tags: ['inline', 'filter', 'time'],
49318
49959
  valueShape: 'string HH:mm',
49319
- recommendedWhen: ['horario unico em toolbar'],
49320
- avoidWhen: ['periodo relativo ou range'],
49960
+ recommendedWhen: ['single time in the toolbar'],
49961
+ avoidWhen: ['relative period or range'],
49321
49962
  dataSourceKind: 'specialized',
49322
49963
  apiPath: jsonApiPath('inline-time/pdx-inline-time.json-api.md'),
49323
49964
  }),
@@ -49327,11 +49968,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
49327
49968
  track: 'inline-filter',
49328
49969
  family: 'date-time',
49329
49970
  friendlyName: 'Inline time range',
49330
- description: 'Faixa horaria compacta para toolbar.',
49971
+ description: 'Compact time range for the toolbar.',
49331
49972
  tags: ['inline', 'filter', 'time', 'range'],
49332
49973
  valueShape: '{ start?, end? }',
49333
- recommendedWhen: ['turno', 'janela operacional'],
49334
- avoidWhen: ['horario unico'],
49974
+ recommendedWhen: ['shift', 'operational window'],
49975
+ avoidWhen: ['single time'],
49335
49976
  dataSourceKind: 'specialized',
49336
49977
  apiPath: jsonApiPath('inline-time-range/pdx-inline-time-range.json-api.md'),
49337
49978
  }),
@@ -49341,11 +49982,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
49341
49982
  track: 'inline-filter',
49342
49983
  family: 'trees-lists',
49343
49984
  friendlyName: 'Inline tree select',
49344
- description: 'Arvore compacta para filtro em toolbar.',
49985
+ description: 'Compact tree filter for the toolbar.',
49345
49986
  tags: ['inline', 'filter', 'tree', 'hierarchy'],
49346
49987
  valueShape: 'simple node value',
49347
- recommendedWhen: ['taxonomia ou unidade organizacional em toolbar'],
49348
- avoidWhen: ['arvore profunda demais'],
49988
+ recommendedWhen: ['taxonomy or organizational unit in the toolbar'],
49989
+ avoidWhen: ['tree that is too deep'],
49349
49990
  dataSourceKind: 'mixed',
49350
49991
  apiPath: jsonApiPath('inline-tree-select/pdx-inline-tree-select.json-api.md'),
49351
49992
  }),
@@ -49355,12 +49996,14 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
49355
49996
  track: 'inline-filter',
49356
49997
  family: 'selection',
49357
49998
  friendlyName: 'Inline pipeline status',
49358
- description: 'Filtro semantico de status de pipeline.',
49999
+ description: 'Semantic pipeline status filter.',
49359
50000
  tags: ['inline', 'filter', 'pipeline', 'status'],
49360
50001
  valueShape: 'simple value | unknown[]',
49361
- recommendedWhen: ['esteira operacional', 'pipeline comercial'],
49362
- avoidWhen: ['status simples sem ganho visual'],
50002
+ recommendedWhen: ['operational pipeline', 'sales pipeline'],
50003
+ avoidWhen: ['simple status with no visual gain'],
49363
50004
  dataSourceKind: 'specialized',
50005
+ icon: { key: 'visual', tone: 'violet' },
50006
+ interactionPattern: 'visual',
49364
50007
  apiPath: jsonApiPath('inline-pipeline-status/pdx-inline-pipeline-status.json-api.md'),
49365
50008
  }),
49366
50009
  createEntry({
@@ -49369,11 +50012,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
49369
50012
  track: 'inline-filter',
49370
50013
  family: 'date-time',
49371
50014
  friendlyName: 'Inline relative period',
49372
- description: 'Preset semantico de periodo relativo.',
50015
+ description: 'Semantic relative period preset.',
49373
50016
  tags: ['inline', 'filter', 'date', 'relative'],
49374
50017
  valueShape: 'string',
49375
- recommendedWhen: ['hoje', 'ontem', 'ultimos 7 dias', 'este mes'],
49376
- avoidWhen: ['API fora da trilha canonica que nao normaliza presets'],
50018
+ recommendedWhen: ['today', 'yesterday', 'last 7 days', 'this month'],
50019
+ avoidWhen: ['API outside the canonical track that does not normalize presets'],
49377
50020
  dataSourceKind: 'specialized',
49378
50021
  apiPath: jsonApiPath('inline-relative-period/pdx-inline-relative-period.json-api.md'),
49379
50022
  }),
@@ -49383,12 +50026,14 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
49383
50026
  track: 'inline-filter',
49384
50027
  family: 'selection',
49385
50028
  friendlyName: 'Inline sentiment',
49386
- description: 'Filtro semantico de sentimento/polaridade.',
50029
+ description: 'Semantic sentiment or polarity filter.',
49387
50030
  tags: ['inline', 'filter', 'sentiment'],
49388
50031
  valueShape: 'string',
49389
- recommendedWhen: ['sentimento', 'polaridade', 'humor'],
49390
- avoidWhen: ['enum simples sem ganho visual'],
50032
+ recommendedWhen: ['sentiment', 'polarity', 'mood'],
50033
+ avoidWhen: ['simple enum with no visual gain'],
49391
50034
  dataSourceKind: 'specialized',
50035
+ icon: { key: 'visual', tone: 'rose' },
50036
+ interactionPattern: 'visual',
49392
50037
  apiPath: jsonApiPath('inline-sentiment/pdx-inline-sentiment.json-api.md'),
49393
50038
  }),
49394
50039
  createEntry({
@@ -49397,12 +50042,14 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
49397
50042
  track: 'inline-filter',
49398
50043
  family: 'selection',
49399
50044
  friendlyName: 'Inline color label',
49400
- description: 'Filtro de labels coloridas semanticas.',
50045
+ description: 'Semantic colored-label filter.',
49401
50046
  tags: ['inline', 'filter', 'color', 'labels'],
49402
50047
  valueShape: 'simple value | unknown[]',
49403
- recommendedWhen: ['tags coloridas', 'categorias visuais'],
49404
- avoidWhen: ['quando cor e o unico canal de significado'],
50048
+ recommendedWhen: ['colored tags', 'visual categories'],
50049
+ avoidWhen: ['when color is the only channel of meaning'],
49405
50050
  dataSourceKind: 'specialized',
50051
+ icon: { key: 'visual', tone: 'teal' },
50052
+ interactionPattern: 'visual',
49406
50053
  apiPath: jsonApiPath('inline-color-label/pdx-inline-color-label.json-api.md'),
49407
50054
  a11yNotes: [
49408
50055
  'Nunca dependa apenas de cor para comunicar estado ou significado.',
@@ -49415,11 +50062,11 @@ const DYNAMIC_FIELDS_PLAYGROUND_CATALOG = [
49415
50062
  track: 'inline-filter',
49416
50063
  family: 'toggle-choice',
49417
50064
  friendlyName: 'Inline toggle',
49418
- description: 'Booleano compacto com estado neutro para toolbar.',
50065
+ description: 'Compact boolean with neutral state for the toolbar.',
49419
50066
  tags: ['inline', 'filter', 'toggle', 'boolean'],
49420
50067
  valueShape: 'boolean | null',
49421
- recommendedWhen: ['booleano simples em toolbar'],
49422
- avoidWhen: ['false e null nao estao bem documentados no fluxo'],
50068
+ recommendedWhen: ['simple boolean in the toolbar'],
50069
+ avoidWhen: ['false and null are not well documented in the flow'],
49423
50070
  dataSourceKind: 'local',
49424
50071
  apiPath: jsonApiPath('inline-toggle/pdx-inline-toggle.json-api.md'),
49425
50072
  stateRecipes: [
@@ -50826,6 +51473,24 @@ function getControlTypeCatalog(controlType) {
50826
51473
  * Lida com campos que não possuem controlType definido,
50827
51474
  * inferindo tipos apropriados baseado no schema JSON.
50828
51475
  */
51476
+ const VALUE_PRESENTATION_TYPES = new Set([
51477
+ 'string',
51478
+ 'number',
51479
+ 'currency',
51480
+ 'percentage',
51481
+ 'date',
51482
+ 'datetime',
51483
+ 'time',
51484
+ 'boolean',
51485
+ ]);
51486
+ const VALUE_PRESENTATION_STYLES = new Set([
51487
+ 'default',
51488
+ 'short',
51489
+ 'medium',
51490
+ 'long',
51491
+ 'full',
51492
+ 'compact',
51493
+ ]);
50829
51494
  /**
50830
51495
  * Mapeia um JSON Schema completo para array de FieldMetadata
50831
51496
  *
@@ -50925,6 +51590,10 @@ function mapPropertyToFieldMetadata(fieldName, property, requiredFields = []) {
50925
51590
  // Propriedades específicas baseadas no tipo
50926
51591
  ...extractTypeSpecificProperties(property, uiConfig),
50927
51592
  };
51593
+ const valuePresentation = mapValuePresentation(uiConfig.valuePresentation);
51594
+ if (valuePresentation) {
51595
+ fieldMetadata.valuePresentation = valuePresentation;
51596
+ }
50928
51597
  // Keep a lightweight trace for downstream editors/runtimes to distinguish
50929
51598
  // explicit schema text from convenience-generated labels.
50930
51599
  if (!uiConfig.label && !property.title) {
@@ -50942,6 +51611,41 @@ function mapPropertyToFieldMetadata(fieldName, property, requiredFields = []) {
50942
51611
  }
50943
51612
  return fieldMetadata;
50944
51613
  }
51614
+ function mapValuePresentation(value) {
51615
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
51616
+ return undefined;
51617
+ }
51618
+ const typeToken = String(value.type ?? '').trim();
51619
+ if (!typeToken || !VALUE_PRESENTATION_TYPES.has(typeToken)) {
51620
+ if (typeToken) {
51621
+ logger.warn(`[JsonSchemaMapper] Ignorando x-ui.valuePresentation.type invalido: ${typeToken}`);
51622
+ }
51623
+ return undefined;
51624
+ }
51625
+ const presentation = {
51626
+ type: typeToken,
51627
+ };
51628
+ const styleToken = String(value.style ?? '').trim();
51629
+ if (styleToken) {
51630
+ if (VALUE_PRESENTATION_STYLES.has(styleToken)) {
51631
+ presentation.style = styleToken;
51632
+ }
51633
+ else {
51634
+ logger.warn(`[JsonSchemaMapper] Ignorando x-ui.valuePresentation.style invalido: ${styleToken}`);
51635
+ }
51636
+ }
51637
+ const formatToken = String(value.format ?? '').trim();
51638
+ if (formatToken) {
51639
+ presentation.format = formatToken;
51640
+ }
51641
+ if (value.currency && typeof value.currency === 'object') {
51642
+ presentation.currency = { ...value.currency };
51643
+ }
51644
+ if (value.number && typeof value.number === 'object') {
51645
+ presentation.number = { ...value.number };
51646
+ }
51647
+ return presentation;
51648
+ }
50945
51649
  /**
50946
51650
  * Infere controlType baseado no schema JSON quando x-ui não está disponível
50947
51651
  *