@praxisui/table 8.0.0-beta.19 → 8.0.0-beta.20
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.
- package/README.md +39 -5
- package/docs/DSL-Extensions-Guide.md +23 -0
- package/docs/adr/2026-03-dynamic-filter-cross-lib-coupling.md +107 -0
- package/docs/adr/2026-03-filter-drawer-adapter-light-entrypoint.md +105 -0
- package/docs/adr/2026-03-table-editor-idfield-decision.md +85 -0
- package/docs/column-resize-reorder-implementation-plan.md +338 -0
- package/docs/column-resize-reorder-review-prompt.md +34 -0
- package/docs/dynamic-filter-architecture-overview.md +207 -0
- package/docs/dynamic-filter-backend-contract-cheatsheet.md +167 -0
- package/docs/dynamic-filter-editor-settings-guide.md +229 -0
- package/docs/dynamic-filter-host-integration-guide.md +217 -0
- package/docs/dynamic-filter-payload-contract.md +331 -0
- package/docs/dynamic-filter-range-filters-guide.md +289 -0
- package/docs/dynamic-filter-troubleshooting-guide.md +220 -0
- package/docs/dynamic-inline-filter-catalog.md +147 -0
- package/docs/e2e-column-drag-playwright.md +62 -0
- package/docs/expandable-rows-enterprise-big-leagues-plan.md +1080 -0
- package/docs/json-logic-operators-and-helpers.md +57 -0
- package/docs/local-data-mode-precedence.md +12 -0
- package/docs/local-data-pre-implementation-baseline.md +22 -0
- package/docs/local-data-preimplementation-go-no-go.md +39 -0
- package/docs/local-data-support-implementation-plan.md +524 -0
- package/docs/local-data-support-pr-package.md +66 -0
- package/docs/localization-persistence-merge.md +22 -0
- package/docs/performance-hardening-v2-implementation-plan.md +479 -0
- package/docs/playground-scenario-curation-plan.md +482 -0
- package/docs/playground-scenario-second-opinion-prompt.md +121 -0
- package/docs/playground-scenario-second-opinion-review.md +234 -0
- package/docs/release-notes-p1-hardening.md +76 -0
- package/docs/table-authoring-document-completeness-checklist.md +120 -0
- package/docs/table-editor-capability-review-prompt.md +349 -0
- package/docs/visual-rules-editor-transition.md +29 -0
- package/fesm2022/praxisui-table.mjs +301 -14
- package/index.d.ts +13 -0
- package/package.json +15 -9
- package/src/lib/praxis-table.json-api.md +1315 -0
|
@@ -33,7 +33,7 @@ import * as i12 from '@angular/cdk/drag-drop';
|
|
|
33
33
|
import { moveItemInArray, DragDropModule } from '@angular/cdk/drag-drop';
|
|
34
34
|
import { Subject, debounceTime, takeUntil, of, firstValueFrom, BehaviorSubject, take as take$1 } from 'rxjs';
|
|
35
35
|
import * as i1 from '@praxisui/core';
|
|
36
|
-
import { LoggerService, createCorporateLoggerConfig, ConsoleLoggerSink, PraxisJsonLogicService, PraxisIconDirective, PraxisI18nService, isTableConfigV2, providePraxisI18nConfig, PRAXIS_GLOBAL_ACTION_CATALOG, SURFACE_OPEN_I18N_NAMESPACE, isRequiredGlobalActionPayloadMissing, getGlobalActionUiSchema, getRequiredGlobalActionPayloadKeys, hasMeaningfulGlobalActionPayloadValue, SurfaceOpenActionEditorComponent, SURFACE_OPEN_I18N_CONFIG, INLINE_FILTER_CONTROL_TYPES, INLINE_FILTER_CONTROL_TYPE_VALUES, FieldControlType, normalizeControlTypeToken, ASYNC_CONFIG_STORAGE, resolveControlTypeAlias, mapFieldDefinitionsToMetadata, PraxisJsonLogicError, GLOBAL_ACTION_CATALOG, GenericCrudService, validateGlobalActionRefs, getGlobalActionCatalog, TableConfigService, createDefaultTableConfig, fillUndefined, deepMerge, INLINE_FILTER_ALIAS_TOKENS, GlobalConfigService, buildSchemaId, MemoryCacheAdapter, LocalStorageCacheAdapter, SchemaMetadataClient, resolveInlineFilterControlType, fetchWithETag, ComponentMetadataRegistry, PraxisCollectionExportService, ResourceQuickConnectComponent, translateResourceAvailabilityReason, assertPraxisCollectionExportArtifact, PRAXIS_LOADING_CTX, translateResourceDiscoveryText, supportsImplicitValuePresentation, resolveValuePresentation, translateUnavailableWorkflowMessage, CONNECTION_STORAGE, PRAXIS_LOADING_RENDERER, EmptyStateCardComponent, RESOURCE_DISCOVERY_I18N_CONFIG, AnalyticsStatsRequestBuilderService, buildApiUrl, normalizePraxisDataQueryContext, API_URL } from '@praxisui/core';
|
|
36
|
+
import { LoggerService, createCorporateLoggerConfig, ConsoleLoggerSink, PraxisJsonLogicService, PraxisIconDirective, PraxisI18nService, isTableConfigV2, providePraxisI18nConfig, PRAXIS_GLOBAL_ACTION_CATALOG, SURFACE_OPEN_I18N_NAMESPACE, isRequiredGlobalActionPayloadMissing, getGlobalActionUiSchema, getRequiredGlobalActionPayloadKeys, hasMeaningfulGlobalActionPayloadValue, SurfaceOpenActionEditorComponent, SURFACE_OPEN_I18N_CONFIG, INLINE_FILTER_CONTROL_TYPES, INLINE_FILTER_CONTROL_TYPE_VALUES, FieldControlType, normalizeControlTypeToken, ASYNC_CONFIG_STORAGE, resolveControlTypeAlias, mapFieldDefinitionsToMetadata, PraxisJsonLogicError, GLOBAL_ACTION_CATALOG, GenericCrudService, validateGlobalActionRefs, getGlobalActionCatalog, TableConfigService, createDefaultTableConfig, fillUndefined, deepMerge, INLINE_FILTER_ALIAS_TOKENS, GlobalConfigService, buildSchemaId, MemoryCacheAdapter, LocalStorageCacheAdapter, SchemaMetadataClient, resolveInlineFilterControlType, fetchWithETag, serializeEntityLookupValueForPayload, ComponentMetadataRegistry, PraxisCollectionExportService, ResourceQuickConnectComponent, translateResourceAvailabilityReason, assertPraxisCollectionExportArtifact, PRAXIS_LOADING_CTX, translateResourceDiscoveryText, supportsImplicitValuePresentation, resolveValuePresentation, translateUnavailableWorkflowMessage, CONNECTION_STORAGE, PRAXIS_LOADING_RENDERER, EmptyStateCardComponent, RESOURCE_DISCOVERY_I18N_CONFIG, AnalyticsStatsRequestBuilderService, buildApiUrl, normalizePraxisDataQueryContext, API_URL } from '@praxisui/core';
|
|
37
37
|
import { PraxisRichContent } from '@praxisui/rich-content';
|
|
38
38
|
import * as i2 from '@angular/material/toolbar';
|
|
39
39
|
import { MatToolbarModule } from '@angular/material/toolbar';
|
|
@@ -35857,7 +35857,7 @@ class PraxisFilter {
|
|
|
35857
35857
|
cleanFilterPayload(source) {
|
|
35858
35858
|
const cleaned = {};
|
|
35859
35859
|
Object.keys(source || {}).forEach((key) => {
|
|
35860
|
-
const value = source[key];
|
|
35860
|
+
const value = this.normalizeFilterFieldValue(key, source[key]);
|
|
35861
35861
|
if (!this.isEffectivelyEmptyFilterValue(value)) {
|
|
35862
35862
|
cleaned[key] = value;
|
|
35863
35863
|
}
|
|
@@ -35865,11 +35865,43 @@ class PraxisFilter {
|
|
|
35865
35865
|
return cleaned;
|
|
35866
35866
|
}
|
|
35867
35867
|
upsertDtoValue(key, value) {
|
|
35868
|
-
|
|
35868
|
+
const normalizedValue = this.normalizeFilterFieldValue(key, value);
|
|
35869
|
+
if (this.isEffectivelyEmptyFilterValue(normalizedValue)) {
|
|
35869
35870
|
delete this.dto[key];
|
|
35870
35871
|
return;
|
|
35871
35872
|
}
|
|
35872
|
-
this.dto[key] =
|
|
35873
|
+
this.dto[key] = normalizedValue;
|
|
35874
|
+
}
|
|
35875
|
+
normalizeFilterFieldValue(key, value) {
|
|
35876
|
+
const meta = this.getFilterFieldMetaByName(key);
|
|
35877
|
+
if (!this.isEntityLookupFilterField(meta)) {
|
|
35878
|
+
return value;
|
|
35879
|
+
}
|
|
35880
|
+
return serializeEntityLookupValueForPayload(value, {
|
|
35881
|
+
payloadMode: meta?.payloadMode,
|
|
35882
|
+
multiple: meta?.multiple === true,
|
|
35883
|
+
entityType: String(meta?.optionSource?.entityKey || '').trim() || undefined,
|
|
35884
|
+
});
|
|
35885
|
+
}
|
|
35886
|
+
getFilterFieldMetaByName(fieldName) {
|
|
35887
|
+
const normalizedFieldName = String(fieldName || '').trim();
|
|
35888
|
+
if (!normalizedFieldName)
|
|
35889
|
+
return null;
|
|
35890
|
+
return (this.schemaMetas?.find((meta) => meta.name === normalizedFieldName) ||
|
|
35891
|
+
(this.fieldMetadata || []).find((meta) => meta.name === normalizedFieldName) ||
|
|
35892
|
+
this.getEditableFieldMeta(normalizedFieldName)) ?? null;
|
|
35893
|
+
}
|
|
35894
|
+
isEntityLookupFilterField(meta) {
|
|
35895
|
+
if (!meta)
|
|
35896
|
+
return false;
|
|
35897
|
+
const controlType = meta.controlType;
|
|
35898
|
+
const normalizedControlType = String(controlType || '').toLowerCase();
|
|
35899
|
+
const controlTypeToken = normalizeControlTypeToken(controlType);
|
|
35900
|
+
return (controlType === FieldControlType.ENTITY_LOOKUP ||
|
|
35901
|
+
normalizedControlType === 'entitylookup' ||
|
|
35902
|
+
controlTypeToken === 'entitylookup' ||
|
|
35903
|
+
controlTypeToken === normalizeControlTypeToken(INLINE_ENTITY_LOOKUP_CONTROL_TYPE) ||
|
|
35904
|
+
meta?.entityLookup === true);
|
|
35873
35905
|
}
|
|
35874
35906
|
isEffectivelyEmptyFilterValue(value) {
|
|
35875
35907
|
if (value === null || value === undefined)
|
|
@@ -41638,6 +41670,13 @@ class PraxisTable {
|
|
|
41638
41670
|
const payload = Object.prototype.hasOwnProperty.call(runtimeOptions || {}, 'payload')
|
|
41639
41671
|
? runtimeOptions?.payload
|
|
41640
41672
|
: { row };
|
|
41673
|
+
const resolvedGlobalAction = this.resolveConfiguredGlobalActionRef(actionConfig.globalAction, {
|
|
41674
|
+
sourceId: this.tableId || this.componentInstanceId || 'praxis-table',
|
|
41675
|
+
output: 'rowAction',
|
|
41676
|
+
payload,
|
|
41677
|
+
runtime: { row },
|
|
41678
|
+
meta: { actionId: action, actionConfig },
|
|
41679
|
+
});
|
|
41641
41680
|
await this.executeConfiguredGlobalAction(actionConfig, {
|
|
41642
41681
|
sourceId: this.tableId || this.componentInstanceId || 'praxis-table',
|
|
41643
41682
|
output: 'rowAction',
|
|
@@ -41796,7 +41835,7 @@ class PraxisTable {
|
|
|
41796
41835
|
const ref = actionConfig?.globalAction;
|
|
41797
41836
|
if (!ref)
|
|
41798
41837
|
return false;
|
|
41799
|
-
const result = await this.globalActions.executeRef(ref, context);
|
|
41838
|
+
const result = await this.globalActions.executeRef(this.resolveConfiguredGlobalActionRef(ref, context) || ref, context);
|
|
41800
41839
|
if (!result.success) {
|
|
41801
41840
|
this.warnLog('[PraxisTable] global action execution failed', { actionId: ref.actionId, error: result.error }, { actionId: ref.actionId });
|
|
41802
41841
|
this.showActionFeedbackMessage('errors', ref.actionId, {
|
|
@@ -41808,6 +41847,70 @@ class PraxisTable {
|
|
|
41808
41847
|
}
|
|
41809
41848
|
return true;
|
|
41810
41849
|
}
|
|
41850
|
+
resolveConfiguredGlobalActionRef(ref, context) {
|
|
41851
|
+
if (!ref || ref.payloadExpr !== undefined || !Object.prototype.hasOwnProperty.call(ref, 'payload')) {
|
|
41852
|
+
return ref;
|
|
41853
|
+
}
|
|
41854
|
+
return {
|
|
41855
|
+
...ref,
|
|
41856
|
+
payload: this.resolveGlobalActionPayload(ref.payload, context),
|
|
41857
|
+
};
|
|
41858
|
+
}
|
|
41859
|
+
resolveGlobalActionPayload(raw, context) {
|
|
41860
|
+
const root = this.buildGlobalActionTemplateContext(context);
|
|
41861
|
+
return this.resolveGlobalActionTemplateValue(raw, root);
|
|
41862
|
+
}
|
|
41863
|
+
buildGlobalActionTemplateContext(context) {
|
|
41864
|
+
return {
|
|
41865
|
+
context,
|
|
41866
|
+
payload: context?.payload,
|
|
41867
|
+
runtime: context?.runtime,
|
|
41868
|
+
meta: context?.meta,
|
|
41869
|
+
pageContext: context?.pageContext,
|
|
41870
|
+
row: context?.runtime?.row,
|
|
41871
|
+
item: context?.runtime?.item,
|
|
41872
|
+
selection: context?.runtime?.selection,
|
|
41873
|
+
formData: context?.runtime?.formData,
|
|
41874
|
+
value: context?.runtime?.value,
|
|
41875
|
+
state: context?.runtime?.state,
|
|
41876
|
+
actionConfig: context?.meta?.actionConfig,
|
|
41877
|
+
actionId: context?.meta?.actionId,
|
|
41878
|
+
};
|
|
41879
|
+
}
|
|
41880
|
+
resolveGlobalActionTemplateValue(raw, ctx, key) {
|
|
41881
|
+
if (this.isDeferredTemplateExpressionKey(key) && typeof raw === 'string') {
|
|
41882
|
+
return raw;
|
|
41883
|
+
}
|
|
41884
|
+
if (typeof raw === 'string') {
|
|
41885
|
+
return this.interpolateGlobalActionTemplate(raw, ctx);
|
|
41886
|
+
}
|
|
41887
|
+
if (Array.isArray(raw)) {
|
|
41888
|
+
return raw.map((value) => this.resolveGlobalActionTemplateValue(value, ctx, key));
|
|
41889
|
+
}
|
|
41890
|
+
if (raw && typeof raw === 'object') {
|
|
41891
|
+
const out = {};
|
|
41892
|
+
for (const [childKey, value] of Object.entries(raw)) {
|
|
41893
|
+
out[childKey] = this.resolveGlobalActionTemplateValue(value, ctx, childKey);
|
|
41894
|
+
}
|
|
41895
|
+
return out;
|
|
41896
|
+
}
|
|
41897
|
+
return raw;
|
|
41898
|
+
}
|
|
41899
|
+
isDeferredTemplateExpressionKey(key) {
|
|
41900
|
+
if (!key)
|
|
41901
|
+
return false;
|
|
41902
|
+
return key === 'expr' || key.endsWith('Expr');
|
|
41903
|
+
}
|
|
41904
|
+
interpolateGlobalActionTemplate(template, ctx) {
|
|
41905
|
+
const single = String(template || '').match(/^\$\{([^}]+)\}$/);
|
|
41906
|
+
if (single) {
|
|
41907
|
+
return this.getNestedPropertyValue(ctx, single[1].trim());
|
|
41908
|
+
}
|
|
41909
|
+
return String(template || '').replace(/\$\{([^}]+)\}/g, (_match, path) => {
|
|
41910
|
+
const value = this.getNestedPropertyValue(ctx, String(path).trim());
|
|
41911
|
+
return value == null ? '' : String(value);
|
|
41912
|
+
});
|
|
41913
|
+
}
|
|
41811
41914
|
shouldEmitLocalForGlobalAction(actionConfig) {
|
|
41812
41915
|
return actionConfig?.emitLocal === true || actionConfig?.globalAction?.meta?.emitLocal === true;
|
|
41813
41916
|
}
|
|
@@ -43808,7 +43911,7 @@ class PraxisTable {
|
|
|
43808
43911
|
// Only verify when we already have columns configured (bootstrap handled by loadSchema)
|
|
43809
43912
|
if ((this.config?.columns?.length ?? 0) === 0)
|
|
43810
43913
|
return;
|
|
43811
|
-
// Build request to /schemas/filtered for grid
|
|
43914
|
+
// Build request to /schemas/filtered for grid using the governed search/list surface.
|
|
43812
43915
|
const baseUrl = this.crudService.getSchemasFilteredBaseUrl();
|
|
43813
43916
|
let derived = '';
|
|
43814
43917
|
try {
|
|
@@ -43818,8 +43921,8 @@ class PraxisTable {
|
|
|
43818
43921
|
return;
|
|
43819
43922
|
}
|
|
43820
43923
|
const u = new URL(baseUrl);
|
|
43821
|
-
u.searchParams.set('path', `${derived}/
|
|
43822
|
-
u.searchParams.set('operation', '
|
|
43924
|
+
u.searchParams.set('path', `${derived}/filter`);
|
|
43925
|
+
u.searchParams.set('operation', 'post');
|
|
43823
43926
|
u.searchParams.set('schemaType', 'response');
|
|
43824
43927
|
u.searchParams.set('includeInternalSchemas', 'false');
|
|
43825
43928
|
const serverHash = this.runtimeSchemaMeta.serverHash;
|
|
@@ -44850,7 +44953,68 @@ class PraxisTable {
|
|
|
44850
44953
|
this.errorLog('PTABLE:format:error', { error, value, column });
|
|
44851
44954
|
}
|
|
44852
44955
|
}
|
|
44853
|
-
return value;
|
|
44956
|
+
return this.normalizeReadonlyEntityLookupCellValue(value);
|
|
44957
|
+
}
|
|
44958
|
+
normalizeReadonlyEntityLookupCellValue(value) {
|
|
44959
|
+
if (Array.isArray(value)) {
|
|
44960
|
+
const normalizedItems = value
|
|
44961
|
+
.map((item) => this.toEntityLookupDisplayToken(item))
|
|
44962
|
+
.filter((item) => !!item);
|
|
44963
|
+
const hasStructuredEntries = value.some((item) => this.isEntityLookupDisplayCandidate(item));
|
|
44964
|
+
if (hasStructuredEntries && normalizedItems.length > 0) {
|
|
44965
|
+
return normalizedItems.join(', ');
|
|
44966
|
+
}
|
|
44967
|
+
return value;
|
|
44968
|
+
}
|
|
44969
|
+
const normalized = this.toEntityLookupDisplayToken(value);
|
|
44970
|
+
return normalized ?? value;
|
|
44971
|
+
}
|
|
44972
|
+
toEntityLookupDisplayToken(value) {
|
|
44973
|
+
if (!this.isEntityLookupDisplayCandidate(value)) {
|
|
44974
|
+
return null;
|
|
44975
|
+
}
|
|
44976
|
+
const record = value;
|
|
44977
|
+
const extra = record['extra'] && typeof record['extra'] === 'object' && !Array.isArray(record['extra'])
|
|
44978
|
+
? record['extra']
|
|
44979
|
+
: null;
|
|
44980
|
+
const code = this.toEntityLookupDisplayPrimitive(record['code'] ?? extra?.['code'] ?? record['resourceKey'] ?? record['externalId']);
|
|
44981
|
+
const label = this.toEntityLookupDisplayPrimitive(record['label']
|
|
44982
|
+
?? record['displayName']
|
|
44983
|
+
?? record['name']
|
|
44984
|
+
?? record['title']
|
|
44985
|
+
?? extra?.['label']);
|
|
44986
|
+
const id = this.toEntityLookupDisplayPrimitive(record['id']);
|
|
44987
|
+
if (code && label && code !== label) {
|
|
44988
|
+
return `${code} · ${label}`;
|
|
44989
|
+
}
|
|
44990
|
+
return label || code || id || null;
|
|
44991
|
+
}
|
|
44992
|
+
isEntityLookupDisplayCandidate(value) {
|
|
44993
|
+
if (!value || typeof value !== 'object' || Array.isArray(value) || value instanceof Date) {
|
|
44994
|
+
return false;
|
|
44995
|
+
}
|
|
44996
|
+
const record = value;
|
|
44997
|
+
return (Object.prototype.hasOwnProperty.call(record, 'label') ||
|
|
44998
|
+
Object.prototype.hasOwnProperty.call(record, 'displayName') ||
|
|
44999
|
+
Object.prototype.hasOwnProperty.call(record, 'name') ||
|
|
45000
|
+
Object.prototype.hasOwnProperty.call(record, 'title') ||
|
|
45001
|
+
Object.prototype.hasOwnProperty.call(record, 'id') ||
|
|
45002
|
+
Object.prototype.hasOwnProperty.call(record, 'type') ||
|
|
45003
|
+
Object.prototype.hasOwnProperty.call(record, 'code') ||
|
|
45004
|
+
Object.prototype.hasOwnProperty.call(record, 'extra'));
|
|
45005
|
+
}
|
|
45006
|
+
toEntityLookupDisplayPrimitive(value) {
|
|
45007
|
+
if (value === null || value === undefined) {
|
|
45008
|
+
return null;
|
|
45009
|
+
}
|
|
45010
|
+
if (typeof value === 'string') {
|
|
45011
|
+
const trimmed = value.trim();
|
|
45012
|
+
return trimmed ? trimmed : null;
|
|
45013
|
+
}
|
|
45014
|
+
if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {
|
|
45015
|
+
return String(value);
|
|
45016
|
+
}
|
|
45017
|
+
return null;
|
|
44854
45018
|
}
|
|
44855
45019
|
resolveImplicitFormat(column) {
|
|
44856
45020
|
if (!column?.type || !supportsImplicitValuePresentation(column.type)) {
|
|
@@ -49336,7 +49500,11 @@ const tableFilteringSchema = {
|
|
|
49336
49500
|
enabled: { type: 'boolean' },
|
|
49337
49501
|
settings: {
|
|
49338
49502
|
type: 'object',
|
|
49339
|
-
properties: {
|
|
49503
|
+
properties: {
|
|
49504
|
+
mode: { enum: ['filter'] },
|
|
49505
|
+
showAdvanced: { type: 'boolean' },
|
|
49506
|
+
allowSaveTags: { type: 'boolean' }
|
|
49507
|
+
}
|
|
49340
49508
|
}
|
|
49341
49509
|
}
|
|
49342
49510
|
},
|
|
@@ -50200,7 +50368,26 @@ const PRAXIS_TABLE_AUTHORING_MANIFEST = {
|
|
|
50200
50368
|
label: { type: 'string' },
|
|
50201
50369
|
action: { type: 'string' },
|
|
50202
50370
|
icon: { type: 'string' },
|
|
50203
|
-
type: { enum: ['button', 'icon', 'menu'], default: 'button' }
|
|
50371
|
+
type: { enum: ['button', 'icon', 'menu'], default: 'button' },
|
|
50372
|
+
globalAction: {
|
|
50373
|
+
type: 'object',
|
|
50374
|
+
properties: {
|
|
50375
|
+
actionId: {
|
|
50376
|
+
type: 'string',
|
|
50377
|
+
enum: [
|
|
50378
|
+
'toast.success',
|
|
50379
|
+
'toast.error',
|
|
50380
|
+
'dialog.alert',
|
|
50381
|
+
'dialog.open',
|
|
50382
|
+
'navigation.openExternal',
|
|
50383
|
+
'navigation.openRoute',
|
|
50384
|
+
'api.post',
|
|
50385
|
+
'api.patch',
|
|
50386
|
+
],
|
|
50387
|
+
},
|
|
50388
|
+
payload: { type: 'object' },
|
|
50389
|
+
},
|
|
50390
|
+
},
|
|
50204
50391
|
}
|
|
50205
50392
|
},
|
|
50206
50393
|
effects: [{ kind: 'append-unique', path: 'toolbar.actions[]', key: 'id' }],
|
|
@@ -50223,7 +50410,26 @@ const PRAXIS_TABLE_AUTHORING_MANIFEST = {
|
|
|
50223
50410
|
label: { type: 'string' },
|
|
50224
50411
|
action: { type: 'string' },
|
|
50225
50412
|
icon: { type: 'string' },
|
|
50226
|
-
color: { type: 'string' }
|
|
50413
|
+
color: { type: 'string' },
|
|
50414
|
+
globalAction: {
|
|
50415
|
+
type: 'object',
|
|
50416
|
+
properties: {
|
|
50417
|
+
actionId: {
|
|
50418
|
+
type: 'string',
|
|
50419
|
+
enum: [
|
|
50420
|
+
'toast.success',
|
|
50421
|
+
'toast.error',
|
|
50422
|
+
'dialog.alert',
|
|
50423
|
+
'dialog.open',
|
|
50424
|
+
'navigation.openExternal',
|
|
50425
|
+
'navigation.openRoute',
|
|
50426
|
+
'api.post',
|
|
50427
|
+
'api.patch',
|
|
50428
|
+
],
|
|
50429
|
+
},
|
|
50430
|
+
payload: { type: 'object' },
|
|
50431
|
+
},
|
|
50432
|
+
},
|
|
50227
50433
|
}
|
|
50228
50434
|
},
|
|
50229
50435
|
effects: [{ kind: 'append-unique', path: 'actions.row.actions[]', key: 'id' }],
|
|
@@ -50232,6 +50438,48 @@ const PRAXIS_TABLE_AUTHORING_MANIFEST = {
|
|
|
50232
50438
|
submissionImpact: 'config-only',
|
|
50233
50439
|
preconditions: ['config-initialized']
|
|
50234
50440
|
},
|
|
50441
|
+
{
|
|
50442
|
+
operationId: 'bulkAction.add',
|
|
50443
|
+
title: 'Adicionar ação em lote',
|
|
50444
|
+
scope: 'global',
|
|
50445
|
+
targetKind: 'bulkAction',
|
|
50446
|
+
target: { kind: 'bulkAction', resolver: 'action-in-bulk-config', ambiguityPolicy: 'fail', required: false },
|
|
50447
|
+
inputSchema: {
|
|
50448
|
+
type: 'object',
|
|
50449
|
+
required: ['id', 'label', 'action'],
|
|
50450
|
+
properties: {
|
|
50451
|
+
id: { type: 'string' },
|
|
50452
|
+
label: { type: 'string' },
|
|
50453
|
+
action: { type: 'string' },
|
|
50454
|
+
icon: { type: 'string' },
|
|
50455
|
+
color: { type: 'string' },
|
|
50456
|
+
globalAction: {
|
|
50457
|
+
type: 'object',
|
|
50458
|
+
properties: {
|
|
50459
|
+
actionId: {
|
|
50460
|
+
type: 'string',
|
|
50461
|
+
enum: [
|
|
50462
|
+
'toast.success',
|
|
50463
|
+
'toast.error',
|
|
50464
|
+
'dialog.alert',
|
|
50465
|
+
'dialog.open',
|
|
50466
|
+
'navigation.openExternal',
|
|
50467
|
+
'navigation.openRoute',
|
|
50468
|
+
'api.post',
|
|
50469
|
+
'api.patch',
|
|
50470
|
+
],
|
|
50471
|
+
},
|
|
50472
|
+
payload: { type: 'object' },
|
|
50473
|
+
},
|
|
50474
|
+
},
|
|
50475
|
+
}
|
|
50476
|
+
},
|
|
50477
|
+
effects: [{ kind: 'append-unique', path: 'actions.bulk.actions[]', key: 'id' }],
|
|
50478
|
+
validators: ['bulk-action-id-unique'],
|
|
50479
|
+
affectedPaths: ['actions.bulk.actions[]'],
|
|
50480
|
+
submissionImpact: 'config-only',
|
|
50481
|
+
preconditions: ['config-initialized']
|
|
50482
|
+
},
|
|
50235
50483
|
// --- BEHAVIOR & PERFORMANCE ---
|
|
50236
50484
|
{
|
|
50237
50485
|
operationId: 'export.enabled.set',
|
|
@@ -50264,8 +50512,16 @@ const PRAXIS_TABLE_AUTHORING_MANIFEST = {
|
|
|
50264
50512
|
required: ['enabled'],
|
|
50265
50513
|
properties: {
|
|
50266
50514
|
enabled: { type: 'boolean' },
|
|
50267
|
-
|
|
50268
|
-
|
|
50515
|
+
queryBuilder: { type: 'boolean' },
|
|
50516
|
+
savePresets: { type: 'boolean' },
|
|
50517
|
+
settings: {
|
|
50518
|
+
type: 'object',
|
|
50519
|
+
properties: {
|
|
50520
|
+
mode: { enum: ['filter'] },
|
|
50521
|
+
showAdvanced: { type: 'boolean' },
|
|
50522
|
+
allowSaveTags: { type: 'boolean' }
|
|
50523
|
+
}
|
|
50524
|
+
}
|
|
50269
50525
|
}
|
|
50270
50526
|
},
|
|
50271
50527
|
effects: [
|
|
@@ -51001,6 +51257,12 @@ const PRAXIS_TABLE_AUTHORING_MANIFEST = {
|
|
|
51001
51257
|
code: 'TB011',
|
|
51002
51258
|
description: 'O id da row action deve ser único em actions.row.actions[]. O efeito append-unique usa este id como chave de deduplicação.'
|
|
51003
51259
|
},
|
|
51260
|
+
{
|
|
51261
|
+
validatorId: 'bulk-action-id-unique',
|
|
51262
|
+
level: 'error',
|
|
51263
|
+
code: 'TB017',
|
|
51264
|
+
description: 'O id da ação em lote deve ser único em actions.bulk.actions[]. O efeito append-unique usa este id como chave de deduplicação.'
|
|
51265
|
+
},
|
|
51004
51266
|
{
|
|
51005
51267
|
validatorId: 'format-preset-supported',
|
|
51006
51268
|
level: 'error',
|
|
@@ -51084,6 +51346,25 @@ const PRAXIS_TABLE_AUTHORING_MANIFEST = {
|
|
|
51084
51346
|
params: { id: 'delete', label: 'Excluir', action: 'delete', icon: 'delete', color: 'warn' },
|
|
51085
51347
|
isPositive: true
|
|
51086
51348
|
},
|
|
51349
|
+
{
|
|
51350
|
+
id: 'add-row-action-open-route',
|
|
51351
|
+
request: 'Adicionar botão de linha para abrir detalhe com navegação interna',
|
|
51352
|
+
operationId: 'rowAction.add',
|
|
51353
|
+
params: {
|
|
51354
|
+
id: 'details',
|
|
51355
|
+
label: 'Detalhes',
|
|
51356
|
+
action: 'navigation.openRoute',
|
|
51357
|
+
icon: 'open_in_new',
|
|
51358
|
+
globalAction: {
|
|
51359
|
+
actionId: 'navigation.openRoute',
|
|
51360
|
+
payload: {
|
|
51361
|
+
path: '/clientes/detalhe',
|
|
51362
|
+
query: { id: '${row.id}' }
|
|
51363
|
+
}
|
|
51364
|
+
}
|
|
51365
|
+
},
|
|
51366
|
+
isPositive: true
|
|
51367
|
+
},
|
|
51087
51368
|
{
|
|
51088
51369
|
id: 'group-by-field',
|
|
51089
51370
|
request: 'Agrupar a tabela por departamento',
|
|
@@ -53404,11 +53685,17 @@ const TABLE_AI_CAPABILITIES = {
|
|
|
53404
53685
|
{ path: 'actions.row.display', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.rowActionDisplay, description: 'Modo de exibição das ações (menu/buttons/icons).', dependsOn: 'actions.row.enabled' },
|
|
53405
53686
|
{ path: 'actions.row.trigger', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.rowActionTrigger, description: 'Como as ações aparecem (hover/always/click).', dependsOn: 'actions.row.enabled' },
|
|
53406
53687
|
{ path: 'actions.row.discovery.enabled', category: 'actions', valueKind: 'boolean', description: 'Controla se a tabela enriquece ações por linha com discovery contextual de HATEOAS/capabilities.', dependsOn: 'actions.row.enabled', safetyNotes: 'Use false quando a experiência precisar mostrar apenas ações curadas pelo host.' },
|
|
53688
|
+
{ path: 'actions.row.actions[].globalAction.[actionId]', category: 'actions', valueKind: 'string', description: 'Identificador canônico de global action para a ação por linha (ex.: "navigation.openRoute", "surface.open", "toast.success").', dependsOn: 'actions.row.enabled', intentExamples: ['navegar para detalhe interno ao clicar na linha', 'abrir drawer com surface.open', 'mostrar toast de sucesso'] },
|
|
53689
|
+
{ path: 'actions.row.actions[].globalAction.payload', category: 'actions', valueKind: 'object', description: 'Payload estruturado da global action por linha. Para `navigation.openRoute`, templates como `${row.id}` são resolvidos antes da navegação; para ações como `surface.open`, `payload.*` continua sendo o envelope canônico do evento consumido pelo destino.', dependsOn: 'actions.row.actions[].globalAction.[actionId]', intentExamples: ['abrir detalhe com query.id da linha', 'passar state.selectedId usando a linha atual'], example: '{ "path": "/clientes/detalhe", "query": { "id": "${row.id}" }, "state": { "selectedId": "${row.id}" } }' },
|
|
53407
53690
|
{ path: 'actions.bulk.enabled', category: 'actions', valueKind: 'boolean', description: 'Habilita ações em lote.', critical: true, intentExamples: ['ativar ações em lote', 'permitir excluir em massa'], safetyNotes: 'Combine com selection.persistSelection e limites de maxSelections.' },
|
|
53408
53691
|
{ path: 'actions.bulk.position', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.bulkActionPosition, description: 'Posição das ações em lote.', dependsOn: 'actions.bulk.enabled' },
|
|
53692
|
+
{ path: 'actions.bulk.actions[].globalAction.[actionId]', category: 'actions', valueKind: 'string', description: 'Identificador canônico de global action para a ação em lote.', dependsOn: 'actions.bulk.enabled', intentExamples: ['disparar ação global com a seleção atual', 'abrir surface corporativa a partir do bulk action'] },
|
|
53693
|
+
{ path: 'actions.bulk.actions[].globalAction.payload', category: 'actions', valueKind: 'object', description: 'Payload estruturado da global action em lote. Deve permanecer alinhado ao schema da action escolhida e ao contexto da seleção atual.', dependsOn: 'actions.bulk.actions[].globalAction.[actionId]' },
|
|
53409
53694
|
{ path: 'actions.context.enabled', category: 'actions', valueKind: 'boolean', description: 'Habilita menu de contexto (clique direito/long press).', critical: true, intentExamples: ['ativar menu de contexto'] },
|
|
53410
53695
|
{ path: 'actions.context.trigger', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.contextTrigger, description: 'Trigger para abrir o menu de contexto.', dependsOn: 'actions.context.enabled' },
|
|
53411
53696
|
{ path: 'actions.confirmations.default', category: 'actions', valueKind: 'object', description: 'Texto padrão de confirmação para ações perigosas.', critical: true, example: '{ title: "Confirmar exclusão", message: "Deseja remover o item?", confirmText: "Remover" }' },
|
|
53697
|
+
{ path: 'toolbar.actions[].globalAction.[actionId]', category: 'toolbar', valueKind: 'string', description: 'Identificador canônico de global action para ações da toolbar (ex.: "navigation.openRoute", "surface.open", "dialog.open").', dependsOn: 'toolbar.visible', intentExamples: ['abrir rota interna pela toolbar', 'abrir surface pela toolbar'] },
|
|
53698
|
+
{ path: 'toolbar.actions[].globalAction.payload', category: 'toolbar', valueKind: 'object', description: 'Payload estruturado da global action da toolbar. Para `surface.open`, preserve a semântica canônica do catálogo compartilhado: `payload.*` representa o envelope do evento e `runtime.*` referencia estado exposto diretamente pelo host.', dependsOn: 'toolbar.actions[].globalAction.[actionId]' },
|
|
53412
53699
|
// Export
|
|
53413
53700
|
{ path: 'export.enabled', category: 'export', valueKind: 'boolean', description: 'Habilita funcionalidades de exportação.', critical: true, intentExamples: ['permitir exportar', 'desativar exportação'], safetyNotes: 'Avaliar políticas de dados sensíveis antes de habilitar.' },
|
|
53414
53701
|
{ path: 'export.formats', category: 'export', valueKind: 'array', description: 'Formatos permitidos.', allowedValues: ENUMS.exportFormat, example: '["csv","excel"]', intentExamples: ['exportar para excel', 'download em csv'] },
|
package/index.d.ts
CHANGED
|
@@ -744,6 +744,9 @@ declare class PraxisFilter implements OnInit, OnChanges, AfterViewInit, OnDestro
|
|
|
744
744
|
private persistTags;
|
|
745
745
|
private cleanFilterPayload;
|
|
746
746
|
private upsertDtoValue;
|
|
747
|
+
private normalizeFilterFieldValue;
|
|
748
|
+
private getFilterFieldMetaByName;
|
|
749
|
+
private isEntityLookupFilterField;
|
|
747
750
|
private isEffectivelyEmptyFilterValue;
|
|
748
751
|
private syncFormsToDto;
|
|
749
752
|
private updateDisplayedTags;
|
|
@@ -1380,6 +1383,12 @@ declare class PraxisTable implements OnInit, OnChanges, AfterViewInit, AfterCont
|
|
|
1380
1383
|
private showActionFeedbackMessage;
|
|
1381
1384
|
private emitEventWithActionFeedback;
|
|
1382
1385
|
private executeConfiguredGlobalAction;
|
|
1386
|
+
private resolveConfiguredGlobalActionRef;
|
|
1387
|
+
private resolveGlobalActionPayload;
|
|
1388
|
+
private buildGlobalActionTemplateContext;
|
|
1389
|
+
private resolveGlobalActionTemplateValue;
|
|
1390
|
+
private isDeferredTemplateExpressionKey;
|
|
1391
|
+
private interpolateGlobalActionTemplate;
|
|
1383
1392
|
private shouldEmitLocalForGlobalAction;
|
|
1384
1393
|
private resolveBulkValidationMessage;
|
|
1385
1394
|
private hasValidBulkSelectionCount;
|
|
@@ -1554,6 +1563,10 @@ declare class PraxisTable implements OnInit, OnChanges, AfterViewInit, AfterCont
|
|
|
1554
1563
|
* 3. format (data formatting like dates, numbers, currency)
|
|
1555
1564
|
*/
|
|
1556
1565
|
getCellValue(rowData: any, column: ColumnDefinition): any;
|
|
1566
|
+
private normalizeReadonlyEntityLookupCellValue;
|
|
1567
|
+
private toEntityLookupDisplayToken;
|
|
1568
|
+
private isEntityLookupDisplayCandidate;
|
|
1569
|
+
private toEntityLookupDisplayPrimitive;
|
|
1557
1570
|
private resolveImplicitFormat;
|
|
1558
1571
|
getCellClasses(rowData: any, column: ColumnDefinition): string[] | undefined;
|
|
1559
1572
|
getCellNgStyle(rowData: any, column: ColumnDefinition): Record<string, string> | undefined;
|
package/package.json
CHANGED
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@praxisui/table",
|
|
3
|
-
"version": "8.0.0-beta.
|
|
3
|
+
"version": "8.0.0-beta.20",
|
|
4
4
|
"description": "Advanced data table for Angular (Praxis UI) with editing, filtering, sorting, virtualization, and settings panel integration.",
|
|
5
5
|
"peerDependencies": {
|
|
6
6
|
"@angular/common": "^20.0.0",
|
|
7
7
|
"@angular/core": "^20.0.0",
|
|
8
|
-
"@praxisui/ai": "^8.0.0-beta.
|
|
9
|
-
"@praxisui/core": "^8.0.0-beta.
|
|
10
|
-
"@praxisui/dynamic-fields": "^8.0.0-beta.
|
|
11
|
-
"@praxisui/dynamic-form": "^8.0.0-beta.
|
|
12
|
-
"@praxisui/metadata-editor": "^8.0.0-beta.
|
|
13
|
-
"@praxisui/rich-content": "^8.0.0-beta.
|
|
14
|
-
"@praxisui/settings-panel": "^8.0.0-beta.
|
|
15
|
-
"@praxisui/table-rule-builder": "^8.0.0-beta.
|
|
8
|
+
"@praxisui/ai": "^8.0.0-beta.20",
|
|
9
|
+
"@praxisui/core": "^8.0.0-beta.20",
|
|
10
|
+
"@praxisui/dynamic-fields": "^8.0.0-beta.20",
|
|
11
|
+
"@praxisui/dynamic-form": "^8.0.0-beta.20",
|
|
12
|
+
"@praxisui/metadata-editor": "^8.0.0-beta.20",
|
|
13
|
+
"@praxisui/rich-content": "^8.0.0-beta.20",
|
|
14
|
+
"@praxisui/settings-panel": "^8.0.0-beta.20",
|
|
15
|
+
"@praxisui/table-rule-builder": "^8.0.0-beta.20",
|
|
16
|
+
"@angular/cdk": "^20.0.0",
|
|
17
|
+
"@angular/forms": "^20.0.0",
|
|
18
|
+
"@angular/material": "^20.0.0",
|
|
19
|
+
"@angular/router": "^20.0.0",
|
|
20
|
+
"@praxisui/dialog": "^8.0.0-beta.20",
|
|
21
|
+
"rxjs": "~7.8.0"
|
|
16
22
|
},
|
|
17
23
|
"dependencies": {
|
|
18
24
|
"tslib": "^2.3.0"
|