@praxisui/settings-panel 1.0.0-beta.52 → 1.0.0-beta.53

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.
@@ -425,6 +425,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
425
425
  args: [{ providedIn: 'root' }]
426
426
  }] });
427
427
 
428
+ const TABLE_COMPACT_LENGTH_TOKEN$1 = '(?:0|(?:\\d+|\\d*\\.\\d+)(?:px|rem|em|%|vh|vw|svh|svw|lvh|lvw|dvh|dvw|ch|ex))';
429
+ const TABLE_COMPACT_SPACING_PATTERN = `^(?:var\\(.+\\)|calc\\(.+\\)|${TABLE_COMPACT_LENGTH_TOKEN$1}(?:\\s+${TABLE_COMPACT_LENGTH_TOKEN$1}){0,3})$`;
430
+ const TABLE_COMPACT_FONT_SIZE_PATTERN = `^(?:var\\(.+\\)|calc\\(.+\\)|${TABLE_COMPACT_LENGTH_TOKEN$1})$`;
428
431
  function safeName(path) {
429
432
  return path.replace(/[^a-zA-Z0-9_]/g, '_');
430
433
  }
@@ -732,12 +735,48 @@ function buildGlobalConfigFormConfig() {
732
735
  label: 'Aparência: densidade',
733
736
  controlType: FieldControlType.SELECT,
734
737
  selectOptions: [
735
- { text: 'comfortable', value: 'comfortable' },
736
- { text: 'cozy', value: 'cozy' },
737
738
  { text: 'compact', value: 'compact' },
739
+ { text: 'comfortable', value: 'comfortable' },
740
+ { text: 'spacious', value: 'spacious' },
738
741
  ],
739
742
  dataAttributes: { globalPath: 'table.appearance.density' },
740
743
  },
744
+ {
745
+ name: safeName('table.appearance.spacing.cellPadding'),
746
+ label: 'Aparência: padding das células',
747
+ controlType: FieldControlType.INPUT,
748
+ hint: 'Aceita 1 a 4 medidas CSS com unidade, var(...) ou calc(...). Ex.: 6px 12px',
749
+ pattern: TABLE_COMPACT_SPACING_PATTERN,
750
+ patternMessage: 'Use 1 a 4 medidas CSS válidas, como 6px 12px.',
751
+ dataAttributes: { globalPath: 'table.appearance.spacing.cellPadding' },
752
+ },
753
+ {
754
+ name: safeName('table.appearance.spacing.headerPadding'),
755
+ label: 'Aparência: padding do cabeçalho',
756
+ controlType: FieldControlType.INPUT,
757
+ hint: 'Aceita 1 a 4 medidas CSS com unidade, var(...) ou calc(...). Ex.: 8px 12px',
758
+ pattern: TABLE_COMPACT_SPACING_PATTERN,
759
+ patternMessage: 'Use 1 a 4 medidas CSS válidas, como 8px 12px.',
760
+ dataAttributes: { globalPath: 'table.appearance.spacing.headerPadding' },
761
+ },
762
+ {
763
+ name: safeName('table.appearance.typography.fontSize'),
764
+ label: 'Aparência: fonte das células',
765
+ controlType: FieldControlType.INPUT,
766
+ hint: 'Aceita uma medida CSS com unidade, var(...) ou calc(...). Ex.: 13px',
767
+ pattern: TABLE_COMPACT_FONT_SIZE_PATTERN,
768
+ patternMessage: 'Use uma medida CSS válida, como 13px.',
769
+ dataAttributes: { globalPath: 'table.appearance.typography.fontSize' },
770
+ },
771
+ {
772
+ name: safeName('table.appearance.typography.headerFontSize'),
773
+ label: 'Aparência: fonte do cabeçalho',
774
+ controlType: FieldControlType.INPUT,
775
+ hint: 'Aceita uma medida CSS com unidade, var(...) ou calc(...). Ex.: 13px',
776
+ pattern: TABLE_COMPACT_FONT_SIZE_PATTERN,
777
+ patternMessage: 'Use uma medida CSS válida, como 13px.',
778
+ dataAttributes: { globalPath: 'table.appearance.typography.headerFontSize' },
779
+ },
741
780
  {
742
781
  name: safeName('table.filteringUi.advancedOpenMode'),
743
782
  label: 'Filtro: modo de abertura',
@@ -1026,6 +1065,10 @@ function buildGlobalConfigFormConfig() {
1026
1065
  { id: 'tbl-col-1', fields: [
1027
1066
  safeName('table.toolbar.visible'),
1028
1067
  safeName('table.appearance.density'),
1068
+ safeName('table.appearance.spacing.cellPadding'),
1069
+ safeName('table.appearance.spacing.headerPadding'),
1070
+ safeName('table.appearance.typography.fontSize'),
1071
+ safeName('table.appearance.typography.headerFontSize'),
1029
1072
  safeName('table.filteringUi.advancedOpenMode'),
1030
1073
  safeName('table.filteringUi.overlayVariant'),
1031
1074
  safeName('table.filteringUi.overlayBackdrop'),
@@ -1219,6 +1262,23 @@ const GLOBAL_CONFIG_DYNAMIC_FORM_COMPONENT = new InjectionToken('GLOBAL_CONFIG_D
1219
1262
  factory: () => null,
1220
1263
  });
1221
1264
 
1265
+ const TABLE_COMPACT_SPACING_PATHS = new Set([
1266
+ 'table.appearance.spacing.cellPadding',
1267
+ 'table.appearance.spacing.headerPadding',
1268
+ ]);
1269
+ const TABLE_COMPACT_FONT_SIZE_PATHS = new Set([
1270
+ 'table.appearance.typography.fontSize',
1271
+ 'table.appearance.typography.headerFontSize',
1272
+ ]);
1273
+ const TABLE_COMPACT_FIELD_LABELS = {
1274
+ 'table.appearance.spacing.cellPadding': 'padding das células',
1275
+ 'table.appearance.spacing.headerPadding': 'padding do cabeçalho',
1276
+ 'table.appearance.typography.fontSize': 'fonte das células',
1277
+ 'table.appearance.typography.headerFontSize': 'fonte do cabeçalho',
1278
+ };
1279
+ const TABLE_COMPACT_LENGTH_TOKEN = '(?:0|(?:\\d+|\\d*\\.\\d+)(?:px|rem|em|%|vh|vw|svh|svw|lvh|lvw|dvh|dvw|ch|ex))';
1280
+ const TABLE_COMPACT_SPACING_REGEX = new RegExp(`^(?:var\\(.+\\)|calc\\(.+\\)|${TABLE_COMPACT_LENGTH_TOKEN}(?:\\s+${TABLE_COMPACT_LENGTH_TOKEN}){0,3})$`, 'i');
1281
+ const TABLE_COMPACT_FONT_SIZE_REGEX = new RegExp(`^(?:var\\(.+\\)|calc\\(.+\\)|${TABLE_COMPACT_LENGTH_TOKEN})$`, 'i');
1222
1282
  class GlobalConfigEditorComponent {
1223
1283
  admin;
1224
1284
  snack;
@@ -1670,14 +1730,14 @@ class GlobalConfigEditorComponent {
1670
1730
  const da = fm.dataAttributes || {};
1671
1731
  const path = da.globalPath || fm.name;
1672
1732
  this.pathMap[fm.name] = path;
1673
- const v = flat[path];
1733
+ const v = this.normalizeFieldValue(path, flat[path]);
1674
1734
  if (v !== undefined)
1675
1735
  fm.defaultValue = v;
1676
1736
  }
1677
1737
  this.initialValues = {};
1678
1738
  for (const safe of Object.keys(this.pathMap)) {
1679
1739
  const path = this.pathMap[safe];
1680
- const v = flat[path];
1740
+ const v = this.normalizeFieldValue(path, flat[path]);
1681
1741
  if (v !== undefined)
1682
1742
  this.initialValues[safe] = v;
1683
1743
  }
@@ -1910,6 +1970,14 @@ class GlobalConfigEditorComponent {
1910
1970
  }
1911
1971
  async onSave() {
1912
1972
  const partial = this.getSettingsValue();
1973
+ const validationMessage = this.validateCompactTableAppearancePayload(partial);
1974
+ if (validationMessage) {
1975
+ try {
1976
+ this.snack.open(validationMessage, undefined, { duration: 5000 });
1977
+ }
1978
+ catch { }
1979
+ return undefined;
1980
+ }
1913
1981
  this.isBusy$.next(true);
1914
1982
  try {
1915
1983
  await this.admin.save(partial);
@@ -2080,11 +2148,12 @@ class GlobalConfigEditorComponent {
2080
2148
  buildChangedValues() {
2081
2149
  const out = {};
2082
2150
  for (const [safe, value] of Object.entries(this.currentValues)) {
2083
- const initial = this.initialValues[safe];
2084
- if (!this.shouldIncludeField(initial, value))
2085
- continue;
2086
2151
  const path = this.pathMap[safe] || safe;
2087
- out[path] = value;
2152
+ const normalizedValue = this.normalizeFieldValue(path, value);
2153
+ const normalizedInitial = this.normalizeFieldValue(path, this.initialValues[safe]);
2154
+ if (!this.shouldIncludeField(normalizedInitial, normalizedValue))
2155
+ continue;
2156
+ out[path] = normalizedValue;
2088
2157
  }
2089
2158
  return out;
2090
2159
  }
@@ -2116,6 +2185,56 @@ class GlobalConfigEditorComponent {
2116
2185
  }
2117
2186
  return 'Falha ao salvar configurações.';
2118
2187
  }
2188
+ normalizeFieldValue(path, value) {
2189
+ if (typeof value !== 'string')
2190
+ return value;
2191
+ if (!TABLE_COMPACT_SPACING_PATHS.has(path) && !TABLE_COMPACT_FONT_SIZE_PATHS.has(path)) {
2192
+ return value;
2193
+ }
2194
+ return value.trim().replace(/\s+/g, ' ');
2195
+ }
2196
+ validateCompactTableAppearancePayload(partial) {
2197
+ const appearance = partial?.table?.appearance;
2198
+ if (!appearance || typeof appearance !== 'object')
2199
+ return null;
2200
+ const checks = [
2201
+ {
2202
+ path: 'table.appearance.spacing.cellPadding',
2203
+ value: appearance?.spacing?.cellPadding,
2204
+ regex: TABLE_COMPACT_SPACING_REGEX,
2205
+ example: '6px 12px',
2206
+ },
2207
+ {
2208
+ path: 'table.appearance.spacing.headerPadding',
2209
+ value: appearance?.spacing?.headerPadding,
2210
+ regex: TABLE_COMPACT_SPACING_REGEX,
2211
+ example: '8px 12px',
2212
+ },
2213
+ {
2214
+ path: 'table.appearance.typography.fontSize',
2215
+ value: appearance?.typography?.fontSize,
2216
+ regex: TABLE_COMPACT_FONT_SIZE_REGEX,
2217
+ example: '13px',
2218
+ },
2219
+ {
2220
+ path: 'table.appearance.typography.headerFontSize',
2221
+ value: appearance?.typography?.headerFontSize,
2222
+ regex: TABLE_COMPACT_FONT_SIZE_REGEX,
2223
+ example: '13px',
2224
+ },
2225
+ ];
2226
+ for (const check of checks) {
2227
+ if (typeof check.value !== 'string')
2228
+ continue;
2229
+ const normalized = this.normalizeFieldValue(check.path, check.value);
2230
+ if (!normalized)
2231
+ continue;
2232
+ if (!check.regex.test(normalized)) {
2233
+ return `Valor inválido em ${TABLE_COMPACT_FIELD_LABELS[check.path]}. Use uma string CSS simples, como ${check.example}.`;
2234
+ }
2235
+ }
2236
+ return null;
2237
+ }
2119
2238
  getVariantIcon(key) {
2120
2239
  const safe = this.safeName(`dialog.variants.${key}.icon`);
2121
2240
  return this.currentValues[safe];