@meshmakers/octo-ui 3.4.290 → 3.4.310
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/fesm2022/meshmakers-octo-ui-tree-navigation-settings.mjs +77 -4
- package/fesm2022/meshmakers-octo-ui-tree-navigation-settings.mjs.map +1 -1
- package/fesm2022/meshmakers-octo-ui.mjs +495 -18
- package/fesm2022/meshmakers-octo-ui.mjs.map +1 -1
- package/package.json +1 -1
- package/types/meshmakers-octo-ui-tree-navigation-settings.d.ts +25 -0
- package/types/meshmakers-octo-ui.d.ts +224 -95
|
@@ -6062,6 +6062,46 @@ const CONFIG_QUERY = gql `
|
|
|
6062
6062
|
}
|
|
6063
6063
|
}
|
|
6064
6064
|
`;
|
|
6065
|
+
/**
|
|
6066
|
+
* Loads the singleton's `perspectives` array (AB#4263). Kept SEPARATE from
|
|
6067
|
+
* CONFIG_QUERY on purpose: the `perspectives` field only exists in the tenant
|
|
6068
|
+
* schema when System.UI >= 2.3.0. On a tenant still on System.UI 2.2.0 the type
|
|
6069
|
+
* IS present (so the type-exists probe passes) but this field is not, which would
|
|
6070
|
+
* raise a GraphQL validation error for the whole operation. Isolating it here
|
|
6071
|
+
* lets `fetchPerspectives` swallow that error so the 2.2.0 roles feature keeps
|
|
6072
|
+
* working and perspectives simply come back empty.
|
|
6073
|
+
*/
|
|
6074
|
+
const CONFIG_PERSPECTIVES_QUERY = gql `
|
|
6075
|
+
query getTreeNavigationPerspectives {
|
|
6076
|
+
runtime {
|
|
6077
|
+
systemUITreeNavigationConfiguration(
|
|
6078
|
+
first: 1
|
|
6079
|
+
fieldFilter: [
|
|
6080
|
+
{
|
|
6081
|
+
attributePath: "rtWellKnownName"
|
|
6082
|
+
operator: EQUALS
|
|
6083
|
+
comparisonValue: "TreeNavigation"
|
|
6084
|
+
}
|
|
6085
|
+
]
|
|
6086
|
+
) {
|
|
6087
|
+
items {
|
|
6088
|
+
rtId
|
|
6089
|
+
perspectives {
|
|
6090
|
+
key
|
|
6091
|
+
displayName
|
|
6092
|
+
sortIndex
|
|
6093
|
+
icon
|
|
6094
|
+
rootMode
|
|
6095
|
+
rootCkTypeId
|
|
6096
|
+
primaryRoleId
|
|
6097
|
+
primaryDirection
|
|
6098
|
+
secondaryRoleIds
|
|
6099
|
+
}
|
|
6100
|
+
}
|
|
6101
|
+
}
|
|
6102
|
+
}
|
|
6103
|
+
}
|
|
6104
|
+
`;
|
|
6065
6105
|
const ROLE_SUGGESTIONS_QUERY = gql `
|
|
6066
6106
|
query treeNavigationRoleSuggestions($ckTypeId: String!) {
|
|
6067
6107
|
constructionKit {
|
|
@@ -6126,6 +6166,8 @@ class TreeNavigationConfigService {
|
|
|
6126
6166
|
apollo = inject(Apollo);
|
|
6127
6167
|
/** key `${sourceCkTypeId}::${roleId}` -> override; cached for the session. */
|
|
6128
6168
|
overridesPromise;
|
|
6169
|
+
/** Configured extra perspectives; cached for the session. */
|
|
6170
|
+
perspectivesPromise;
|
|
6129
6171
|
/**
|
|
6130
6172
|
* Resolves the override for one (source type, role) pair, preferring an exact
|
|
6131
6173
|
* source-type match over a wildcard (`*`) rule. Returns undefined when nothing
|
|
@@ -6139,6 +6181,58 @@ class TreeNavigationConfigService {
|
|
|
6139
6181
|
/** Forces a reload on next access (e.g. after a tenant switch). */
|
|
6140
6182
|
reset() {
|
|
6141
6183
|
this.overridesPromise = undefined;
|
|
6184
|
+
this.perspectivesPromise = undefined;
|
|
6185
|
+
}
|
|
6186
|
+
/**
|
|
6187
|
+
* Returns the configured extra tree perspectives (AB#4263), sorted by
|
|
6188
|
+
* sortIndex then displayName. Returns [] when the CK type is absent
|
|
6189
|
+
* (System.UI < 2.3.0) or none are configured. The built-in spatial
|
|
6190
|
+
* perspective is synthesized by the data source and is NOT included here.
|
|
6191
|
+
*/
|
|
6192
|
+
perspectives() {
|
|
6193
|
+
if (!this.perspectivesPromise) {
|
|
6194
|
+
this.perspectivesPromise = this.loadPerspectives().catch((error) => {
|
|
6195
|
+
console.error('Error loading tree navigation perspectives', error);
|
|
6196
|
+
this.perspectivesPromise = undefined;
|
|
6197
|
+
return [];
|
|
6198
|
+
});
|
|
6199
|
+
}
|
|
6200
|
+
return this.perspectivesPromise;
|
|
6201
|
+
}
|
|
6202
|
+
async loadPerspectives() {
|
|
6203
|
+
// Skip the singleton entirely when the CK type is not installed at all.
|
|
6204
|
+
if (!(await this.probeTypePresent())) {
|
|
6205
|
+
return [];
|
|
6206
|
+
}
|
|
6207
|
+
const rawPerspectives = await this.fetchPerspectivesRaw();
|
|
6208
|
+
return rawPerspectives
|
|
6209
|
+
.map((raw) => this.toPerspectiveDefinition(raw))
|
|
6210
|
+
.filter((p) => p !== null)
|
|
6211
|
+
.sort((a, b) => (a.sortIndex ?? Number.MAX_SAFE_INTEGER) -
|
|
6212
|
+
(b.sortIndex ?? Number.MAX_SAFE_INTEGER) ||
|
|
6213
|
+
a.displayName.localeCompare(b.displayName));
|
|
6214
|
+
}
|
|
6215
|
+
/** Maps a raw perspective record to a definition, dropping invalid rows. */
|
|
6216
|
+
toPerspectiveDefinition(raw) {
|
|
6217
|
+
const key = raw?.key?.trim();
|
|
6218
|
+
if (!key) {
|
|
6219
|
+
return null;
|
|
6220
|
+
}
|
|
6221
|
+
const rootMode = raw?.rootMode === 'Type' ? 'Type' : 'Spatial';
|
|
6222
|
+
const secondary = (raw?.secondaryRoleIds ?? [])
|
|
6223
|
+
.map((r) => (typeof r === 'string' ? r : ''))
|
|
6224
|
+
.filter((r) => r.length > 0);
|
|
6225
|
+
return {
|
|
6226
|
+
key,
|
|
6227
|
+
displayName: raw?.displayName?.trim() || key,
|
|
6228
|
+
sortIndex: raw?.sortIndex ?? undefined,
|
|
6229
|
+
icon: raw?.icon ?? undefined,
|
|
6230
|
+
rootMode,
|
|
6231
|
+
rootCkTypeId: raw?.rootCkTypeId ?? undefined,
|
|
6232
|
+
primaryRoleId: raw?.primaryRoleId ?? undefined,
|
|
6233
|
+
primaryDirection: raw?.primaryDirection === 'Outbound' ? 'Outbound' : undefined,
|
|
6234
|
+
secondaryRoleIds: secondary.length > 0 ? secondary : undefined,
|
|
6235
|
+
};
|
|
6142
6236
|
}
|
|
6143
6237
|
/**
|
|
6144
6238
|
* Returns the inbound association roles declared on a CK type, for the role
|
|
@@ -6209,6 +6303,7 @@ class TreeNavigationConfigService {
|
|
|
6209
6303
|
*/
|
|
6210
6304
|
async loadConfig() {
|
|
6211
6305
|
const { typePresent, rtId, rawRoles } = await this.fetchSingleton();
|
|
6306
|
+
const rawPerspectives = typePresent ? await this.fetchPerspectivesRaw() : [];
|
|
6212
6307
|
const roles = [];
|
|
6213
6308
|
for (const raw of rawRoles) {
|
|
6214
6309
|
if (!raw?.sourceCkTypeId || !raw?.roleId) {
|
|
@@ -6224,22 +6319,38 @@ class TreeNavigationConfigService {
|
|
|
6224
6319
|
icon: raw.icon ?? undefined,
|
|
6225
6320
|
});
|
|
6226
6321
|
}
|
|
6227
|
-
|
|
6322
|
+
const perspectives = rawPerspectives
|
|
6323
|
+
.map((raw) => this.toPerspectiveDefinition(raw))
|
|
6324
|
+
.filter((p) => p !== null);
|
|
6325
|
+
return { typePresent, rtId, roles, perspectives };
|
|
6228
6326
|
}
|
|
6229
6327
|
/**
|
|
6230
|
-
* Creates or updates the singleton with the given rules, then
|
|
6231
|
-
* resolve cache so the trees pick up the change on
|
|
6232
|
-
* singleton rtId.
|
|
6328
|
+
* Creates or updates the singleton with the given rules and perspectives, then
|
|
6329
|
+
* invalidates the resolve/perspective cache so the trees pick up the change on
|
|
6330
|
+
* the next expand. Returns the singleton rtId.
|
|
6233
6331
|
*/
|
|
6234
|
-
async saveConfig(rtId, roles) {
|
|
6332
|
+
async saveConfig(rtId, roles, perspectives = []) {
|
|
6235
6333
|
const cleanRoles = roles
|
|
6236
6334
|
.filter((r) => r.sourceCkTypeId && r.roleId)
|
|
6237
6335
|
.map((r) => this.toRoleInput(r));
|
|
6336
|
+
const cleanPerspectives = perspectives
|
|
6337
|
+
.filter((p) => p.key)
|
|
6338
|
+
.map((p) => this.toPerspectiveInput(p));
|
|
6339
|
+
// Only send `perspectives` when non-empty: on a System.UI 2.2.0 tenant the
|
|
6340
|
+
// input type has no `perspectives` field, so including it (even as []) would
|
|
6341
|
+
// fail validation and break roles saving. Trade-off: clearing the last
|
|
6342
|
+
// perspective on 2.3.0 is not persisted via save (edge case).
|
|
6238
6343
|
let savedRtId;
|
|
6239
6344
|
if (rtId) {
|
|
6345
|
+
const item = { roles: cleanRoles };
|
|
6346
|
+
if (cleanPerspectives.length > 0) {
|
|
6347
|
+
item['perspectives'] = cleanPerspectives;
|
|
6348
|
+
}
|
|
6240
6349
|
const result = await firstValueFrom(this.apollo.mutate({
|
|
6241
6350
|
mutation: UPDATE_CONFIG_MUTATION,
|
|
6242
|
-
variables: {
|
|
6351
|
+
variables: {
|
|
6352
|
+
entities: [{ rtId, item }],
|
|
6353
|
+
},
|
|
6243
6354
|
fetchPolicy: 'no-cache',
|
|
6244
6355
|
}));
|
|
6245
6356
|
savedRtId =
|
|
@@ -6255,6 +6366,9 @@ class TreeNavigationConfigService {
|
|
|
6255
6366
|
rtWellKnownName: CONFIG_WELL_KNOWN_NAME,
|
|
6256
6367
|
name: 'Tree Navigation',
|
|
6257
6368
|
roles: cleanRoles,
|
|
6369
|
+
...(cleanPerspectives.length > 0
|
|
6370
|
+
? { perspectives: cleanPerspectives }
|
|
6371
|
+
: {}),
|
|
6258
6372
|
},
|
|
6259
6373
|
],
|
|
6260
6374
|
},
|
|
@@ -6290,11 +6404,45 @@ class TreeNavigationConfigService {
|
|
|
6290
6404
|
input['icon'] = role.icon;
|
|
6291
6405
|
return input;
|
|
6292
6406
|
}
|
|
6293
|
-
/**
|
|
6294
|
-
|
|
6407
|
+
/** Drops undefined/empty fields so the perspective input only carries set values. */
|
|
6408
|
+
toPerspectiveInput(p) {
|
|
6409
|
+
const input = {
|
|
6410
|
+
key: p.key,
|
|
6411
|
+
displayName: p.displayName || p.key,
|
|
6412
|
+
rootMode: p.rootMode,
|
|
6413
|
+
};
|
|
6414
|
+
if (p.sortIndex !== undefined && p.sortIndex !== null) {
|
|
6415
|
+
input['sortIndex'] = p.sortIndex;
|
|
6416
|
+
}
|
|
6417
|
+
if (p.icon)
|
|
6418
|
+
input['icon'] = p.icon;
|
|
6419
|
+
if (p.rootCkTypeId)
|
|
6420
|
+
input['rootCkTypeId'] = p.rootCkTypeId;
|
|
6421
|
+
if (p.primaryRoleId)
|
|
6422
|
+
input['primaryRoleId'] = p.primaryRoleId;
|
|
6423
|
+
if (p.primaryDirection)
|
|
6424
|
+
input['primaryDirection'] = p.primaryDirection;
|
|
6425
|
+
if (p.secondaryRoleIds && p.secondaryRoleIds.length > 0) {
|
|
6426
|
+
input['secondaryRoleIds'] = p.secondaryRoleIds;
|
|
6427
|
+
}
|
|
6428
|
+
return input;
|
|
6429
|
+
}
|
|
6430
|
+
/**
|
|
6431
|
+
* Probes whether the `System.UI/TreeNavigationConfiguration` CK type is
|
|
6432
|
+
* installed on the tenant (System.UI >= 2.2.0). Uses the always-valid
|
|
6433
|
+
* `constructionKit.types` query so it never raises a schema validation error.
|
|
6434
|
+
*/
|
|
6435
|
+
async probeTypePresent() {
|
|
6295
6436
|
const exists = await firstValueFrom(this.apollo.query({ query: CONFIG_TYPE_EXISTS_QUERY, fetchPolicy: 'network-only' }));
|
|
6296
|
-
|
|
6297
|
-
|
|
6437
|
+
return (exists.data?.constructionKit?.types?.items?.length ?? 0) > 0;
|
|
6438
|
+
}
|
|
6439
|
+
/**
|
|
6440
|
+
* Probes the CK schema and (when present) loads the singleton's raw roles.
|
|
6441
|
+
* Perspectives are loaded separately (see fetchPerspectivesRaw) so a System.UI
|
|
6442
|
+
* 2.2.0 tenant (type present but no `perspectives` field yet) keeps its roles.
|
|
6443
|
+
*/
|
|
6444
|
+
async fetchSingleton() {
|
|
6445
|
+
if (!(await this.probeTypePresent())) {
|
|
6298
6446
|
return { typePresent: false, rtId: null, rawRoles: [] };
|
|
6299
6447
|
}
|
|
6300
6448
|
const result = await firstValueFrom(this.apollo.query({ query: CONFIG_QUERY, fetchPolicy: 'network-only' }));
|
|
@@ -6305,6 +6453,23 @@ class TreeNavigationConfigService {
|
|
|
6305
6453
|
rawRoles: item?.roles ?? [],
|
|
6306
6454
|
};
|
|
6307
6455
|
}
|
|
6456
|
+
/**
|
|
6457
|
+
* Loads the singleton's raw perspectives, tolerating the System.UI 2.2.0 case
|
|
6458
|
+
* where the `perspectives` field does not yet exist (GraphQL validation error).
|
|
6459
|
+
* Any failure yields an empty list so roles loading is never affected.
|
|
6460
|
+
*/
|
|
6461
|
+
async fetchPerspectivesRaw() {
|
|
6462
|
+
try {
|
|
6463
|
+
const result = await firstValueFrom(this.apollo.query({ query: CONFIG_PERSPECTIVES_QUERY, fetchPolicy: 'network-only' }));
|
|
6464
|
+
return (result.data?.runtime?.systemUITreeNavigationConfiguration?.items?.[0]
|
|
6465
|
+
?.perspectives ?? []);
|
|
6466
|
+
}
|
|
6467
|
+
catch {
|
|
6468
|
+
// System.UI 2.2.0: `perspectives` field absent → validation error. Degrade
|
|
6469
|
+
// to no perspectives; the roles query above is unaffected.
|
|
6470
|
+
return [];
|
|
6471
|
+
}
|
|
6472
|
+
}
|
|
6308
6473
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: TreeNavigationConfigService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
6309
6474
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: TreeNavigationConfigService, providedIn: 'root' });
|
|
6310
6475
|
}
|
|
@@ -6356,6 +6521,44 @@ const PARENT_CHILD_ROLE_ID = 'System/ParentChild';
|
|
|
6356
6521
|
* tree from firing a guaranteed-failing query for every entity.
|
|
6357
6522
|
*/
|
|
6358
6523
|
const NON_NAVIGABLE_TARGET_CK_TYPES = new Set(['System/Entity']);
|
|
6524
|
+
/** Key of the built-in spatial perspective (all Basic/Tree roots, as before). */
|
|
6525
|
+
const SPATIAL_PERSPECTIVE_KEY = 'Spatial';
|
|
6526
|
+
/**
|
|
6527
|
+
* The always-available built-in perspective. It reproduces the pre-AB#4263
|
|
6528
|
+
* behaviour (roots = all Basic/Tree entities) and is synthesized rather than
|
|
6529
|
+
* stored, so a zero-config tenant still has exactly one perspective.
|
|
6530
|
+
*/
|
|
6531
|
+
const BUILT_IN_SPATIAL_PERSPECTIVE = {
|
|
6532
|
+
key: SPATIAL_PERSPECTIVE_KEY,
|
|
6533
|
+
displayName: 'Spatial',
|
|
6534
|
+
rootMode: 'Spatial',
|
|
6535
|
+
sortIndex: 0,
|
|
6536
|
+
};
|
|
6537
|
+
/**
|
|
6538
|
+
* Loads all runtime instances of a CK type as the roots of a `Type` perspective
|
|
6539
|
+
* (AB#4263). Inline gql (like TreeNavigationConfigService) to stay decoupled
|
|
6540
|
+
* from a schema re-introspection; the selection mirrors getTrees so the existing
|
|
6541
|
+
* root-node rendering keeps working.
|
|
6542
|
+
*/
|
|
6543
|
+
const GET_ROOT_ENTITIES_BY_CK_TYPE = gql `
|
|
6544
|
+
query getRuntimeEntitiesByCkType($ckTypeId: String!, $first: Int!) {
|
|
6545
|
+
runtime {
|
|
6546
|
+
runtimeEntities(ckId: $ckTypeId, first: $first) {
|
|
6547
|
+
items {
|
|
6548
|
+
rtId
|
|
6549
|
+
ckTypeId
|
|
6550
|
+
rtWellKnownName
|
|
6551
|
+
attributes(attributeNames: ["name", "displayName", "description"]) {
|
|
6552
|
+
items {
|
|
6553
|
+
attributeName
|
|
6554
|
+
value
|
|
6555
|
+
}
|
|
6556
|
+
}
|
|
6557
|
+
}
|
|
6558
|
+
}
|
|
6559
|
+
}
|
|
6560
|
+
}
|
|
6561
|
+
`;
|
|
6359
6562
|
class RuntimeBrowserDataSource extends OctoGraphQlHierarchyDataSource {
|
|
6360
6563
|
getTreesDtoGQL = inject(GetTreesDtoGQL);
|
|
6361
6564
|
getCkTypeAssociationRolesDtoGQL = inject(GetCkTypeAssociationRolesDtoGQL);
|
|
@@ -6369,6 +6572,93 @@ class RuntimeBrowserDataSource extends OctoGraphQlHierarchyDataSource {
|
|
|
6369
6572
|
updateTreeNodesDtoGQL = inject(UpdateTreeNodesDtoGQL);
|
|
6370
6573
|
typeHelperService = inject(TypeHelperService);
|
|
6371
6574
|
treeNavConfig = inject(TreeNavigationConfigService);
|
|
6575
|
+
apollo = inject(Apollo);
|
|
6576
|
+
/** Key of the currently active tree perspective (AB#4263). */
|
|
6577
|
+
activePerspectiveKey = SPATIAL_PERSPECTIVE_KEY;
|
|
6578
|
+
/**
|
|
6579
|
+
* The perspective resolved during the last `fetchRootNodes()`. Used by
|
|
6580
|
+
* `fetchChildren()` to apply the root-level whitelist for `Type` perspectives.
|
|
6581
|
+
*/
|
|
6582
|
+
activePerspective = BUILT_IN_SPATIAL_PERSPECTIVE;
|
|
6583
|
+
/**
|
|
6584
|
+
* rtIds of the current perspective's root nodes. The primary/secondary role
|
|
6585
|
+
* whitelist is applied ONLY to the direct children of these roots — deeper
|
|
6586
|
+
* nodes keep full auto-discovery (whitelist-at-root-only).
|
|
6587
|
+
*/
|
|
6588
|
+
perspectiveRootRtIds = new Set();
|
|
6589
|
+
/**
|
|
6590
|
+
* Selects the active perspective by key. The host is responsible for reloading
|
|
6591
|
+
* the tree afterwards (e.g. `treeDetail.refreshTree()`), which re-runs
|
|
6592
|
+
* `fetchRootNodes()` and re-resolves the perspective.
|
|
6593
|
+
*/
|
|
6594
|
+
setActivePerspective(key) {
|
|
6595
|
+
this.activePerspectiveKey = key || SPATIAL_PERSPECTIVE_KEY;
|
|
6596
|
+
}
|
|
6597
|
+
/** The currently selected perspective key. */
|
|
6598
|
+
getActivePerspectiveKey() {
|
|
6599
|
+
return this.activePerspectiveKey;
|
|
6600
|
+
}
|
|
6601
|
+
/**
|
|
6602
|
+
* Returns all selectable perspectives: the built-in spatial one first, then the
|
|
6603
|
+
* per-tenant configured perspectives, de-duplicated by key (a configured
|
|
6604
|
+
* `Spatial` overrides the built-in). With no configuration this is a single
|
|
6605
|
+
* entry, so the host can hide the switcher.
|
|
6606
|
+
*/
|
|
6607
|
+
async getPerspectives() {
|
|
6608
|
+
let configured = [];
|
|
6609
|
+
try {
|
|
6610
|
+
configured = await this.treeNavConfig.perspectives();
|
|
6611
|
+
}
|
|
6612
|
+
catch (error) {
|
|
6613
|
+
console.error('Error loading perspectives', error);
|
|
6614
|
+
}
|
|
6615
|
+
const byKey = new Map();
|
|
6616
|
+
byKey.set(BUILT_IN_SPATIAL_PERSPECTIVE.key, BUILT_IN_SPATIAL_PERSPECTIVE);
|
|
6617
|
+
for (const p of configured) {
|
|
6618
|
+
byKey.set(p.key, p);
|
|
6619
|
+
}
|
|
6620
|
+
return [...byKey.values()].sort((a, b) => (a.sortIndex ?? Number.MAX_SAFE_INTEGER) -
|
|
6621
|
+
(b.sortIndex ?? Number.MAX_SAFE_INTEGER) ||
|
|
6622
|
+
a.displayName.localeCompare(b.displayName));
|
|
6623
|
+
}
|
|
6624
|
+
/**
|
|
6625
|
+
* The role-id whitelist of the active perspective (primary + secondary), or
|
|
6626
|
+
* null when the perspective is not a `Type` perspective or declares no roles.
|
|
6627
|
+
*/
|
|
6628
|
+
activePerspectiveWhitelist() {
|
|
6629
|
+
const p = this.activePerspective;
|
|
6630
|
+
if (p.rootMode !== 'Type') {
|
|
6631
|
+
return null;
|
|
6632
|
+
}
|
|
6633
|
+
const allowed = new Set();
|
|
6634
|
+
if (p.primaryRoleId) {
|
|
6635
|
+
allowed.add(p.primaryRoleId);
|
|
6636
|
+
}
|
|
6637
|
+
for (const roleId of p.secondaryRoleIds ?? []) {
|
|
6638
|
+
allowed.add(roleId);
|
|
6639
|
+
}
|
|
6640
|
+
return allowed.size > 0 ? allowed : null;
|
|
6641
|
+
}
|
|
6642
|
+
/**
|
|
6643
|
+
* The set of role ids the given entity's direct children are restricted to,
|
|
6644
|
+
* or null for full auto-discovery. Non-null only when the entity is a root of
|
|
6645
|
+
* the active `Type` perspective (whitelist-at-root-only).
|
|
6646
|
+
*/
|
|
6647
|
+
rootWhitelistFor(rtId) {
|
|
6648
|
+
return this.perspectiveRootRtIds.has(rtId)
|
|
6649
|
+
? this.activePerspectiveWhitelist()
|
|
6650
|
+
: null;
|
|
6651
|
+
}
|
|
6652
|
+
/**
|
|
6653
|
+
* Resolves the active perspective from the configured list, falling back to a
|
|
6654
|
+
* configured/built-in spatial one when the selected key is unknown.
|
|
6655
|
+
*/
|
|
6656
|
+
async resolveActivePerspective() {
|
|
6657
|
+
const perspectives = await this.getPerspectives();
|
|
6658
|
+
return (perspectives.find((p) => p.key === this.activePerspectiveKey) ??
|
|
6659
|
+
perspectives.find((p) => p.key === SPATIAL_PERSPECTIVE_KEY) ??
|
|
6660
|
+
BUILT_IN_SPATIAL_PERSPECTIVE);
|
|
6661
|
+
}
|
|
6372
6662
|
isCkModelsRoot(item) {
|
|
6373
6663
|
return !!item && 'isCkModelsRoot' in item;
|
|
6374
6664
|
}
|
|
@@ -6426,17 +6716,30 @@ class RuntimeBrowserDataSource extends OctoGraphQlHierarchyDataSource {
|
|
|
6426
6716
|
// runtime edges but are no longer declared on the type in the installed CK
|
|
6427
6717
|
// model (orphan roles after model evolution). Each group carries its concrete
|
|
6428
6718
|
// origin CK type (the ckId used to load its targets) and an exact count.
|
|
6429
|
-
const
|
|
6719
|
+
const discoveredGroups = await this.discoverEntityInboundRoleGroups(rtEntity.rtId, rtEntity.ckTypeId);
|
|
6720
|
+
// AB#4263: when this entity is a root of the active `Type` perspective,
|
|
6721
|
+
// restrict its direct children to the perspective's primary + secondary
|
|
6722
|
+
// roles (whitelist-at-root-only). Deeper nodes are not in
|
|
6723
|
+
// perspectiveRootRtIds, so they keep full auto-discovery.
|
|
6724
|
+
const whitelist = this.rootWhitelistFor(rtEntity.rtId);
|
|
6725
|
+
const primaryRoleId = whitelist ? this.activePerspective.primaryRoleId : undefined;
|
|
6726
|
+
const roleGroups = whitelist
|
|
6727
|
+
? discoveredGroups.filter((g) => whitelist.has(g.roleId))
|
|
6728
|
+
: discoveredGroups;
|
|
6430
6729
|
// Apply the optional per-tenant TreeNavigationConfiguration overrides on top
|
|
6431
6730
|
// of auto-discovery: hide (visible), relabel (displayName), reorder
|
|
6432
6731
|
// (sortIndex), flatten vs group (grouped), icon. Defaults reproduce Phase 1:
|
|
6433
|
-
// System/ParentChild flat, every other role grouped.
|
|
6732
|
+
// System/ParentChild flat, every other role grouped. In a perspective root
|
|
6733
|
+
// the perspective's primary role is flattened at the top instead.
|
|
6434
6734
|
const annotated = await Promise.all(roleGroups.map(async (group) => {
|
|
6435
6735
|
const override = await this.treeNavConfig.resolve(rtEntity.ckTypeId, group.roleId);
|
|
6736
|
+
const defaultGrouped = primaryRoleId
|
|
6737
|
+
? group.roleId !== primaryRoleId
|
|
6738
|
+
: group.roleId !== PARENT_CHILD_ROLE_ID;
|
|
6436
6739
|
return {
|
|
6437
6740
|
group,
|
|
6438
6741
|
visible: override?.visible !== false,
|
|
6439
|
-
grouped: override?.grouped ??
|
|
6742
|
+
grouped: override?.grouped ?? defaultGrouped,
|
|
6440
6743
|
displayName: override?.displayName,
|
|
6441
6744
|
sortIndex: override?.sortIndex,
|
|
6442
6745
|
icon: override?.icon,
|
|
@@ -6697,6 +7000,17 @@ class RuntimeBrowserDataSource extends OctoGraphQlHierarchyDataSource {
|
|
|
6697
7000
|
// Add CK Models root node
|
|
6698
7001
|
const ckModelsRoot = { isCkModelsRoot: true };
|
|
6699
7002
|
result.push(new TreeItemDataTyped('ck-models-root', 'CK Models', 'Construction Kit Models', ckModelsRoot, folderOpenIcon, true));
|
|
7003
|
+
// AB#4263: resolve the active perspective and reset the root-id set that
|
|
7004
|
+
// scopes the whitelist to the direct children of these roots.
|
|
7005
|
+
this.perspectiveRootRtIds.clear();
|
|
7006
|
+
this.activePerspective = await this.resolveActivePerspective();
|
|
7007
|
+
// `Type` perspective: roots are all instances of the configured CK type.
|
|
7008
|
+
if (this.activePerspective.rootMode === 'Type' &&
|
|
7009
|
+
this.activePerspective.rootCkTypeId) {
|
|
7010
|
+
result.push(...(await this.fetchTypePerspectiveRoots(this.activePerspective.rootCkTypeId)));
|
|
7011
|
+
return result;
|
|
7012
|
+
}
|
|
7013
|
+
// Spatial perspective (default): all Basic/Tree entities, exactly as before.
|
|
6700
7014
|
// Check if Basic construction kit is available before trying to fetch Tree entities
|
|
6701
7015
|
const isBasicCkAvailable = await this.checkBasicConstructionKitAvailable();
|
|
6702
7016
|
if (isBasicCkAvailable) {
|
|
@@ -6743,6 +7057,39 @@ class RuntimeBrowserDataSource extends OctoGraphQlHierarchyDataSource {
|
|
|
6743
7057
|
return [];
|
|
6744
7058
|
}
|
|
6745
7059
|
}
|
|
7060
|
+
/**
|
|
7061
|
+
* Loads all runtime instances of a CK type as the roots of a `Type`
|
|
7062
|
+
* perspective (AB#4263), registering their rtIds so the whitelist applies to
|
|
7063
|
+
* their direct children only. Expandability is schema-based (does the type
|
|
7064
|
+
* declare an inbound role, restricted to the whitelist when set).
|
|
7065
|
+
*/
|
|
7066
|
+
async fetchTypePerspectiveRoots(ckTypeId) {
|
|
7067
|
+
try {
|
|
7068
|
+
const response = await firstValueFrom(this.apollo.query({
|
|
7069
|
+
query: GET_ROOT_ENTITIES_BY_CK_TYPE,
|
|
7070
|
+
variables: { ckTypeId, first: 2000 },
|
|
7071
|
+
fetchPolicy: 'network-only',
|
|
7072
|
+
}));
|
|
7073
|
+
const items = (response.data?.runtime?.runtimeEntities?.items ?? []).filter((i) => !!i && !!i.rtId && !!i.ckTypeId);
|
|
7074
|
+
// Expandable when the root type declares at least one inbound role that
|
|
7075
|
+
// survives the whitelist (avoids always-empty expand arrows).
|
|
7076
|
+
const whitelist = this.activePerspectiveWhitelist();
|
|
7077
|
+
const roles = items.length > 0 ? await this.getInboundRoles(items[0].ckTypeId) : [];
|
|
7078
|
+
const expandable = whitelist
|
|
7079
|
+
? roles.some((r) => whitelist.has(r.roleId))
|
|
7080
|
+
: roles.length > 0;
|
|
7081
|
+
const result = [];
|
|
7082
|
+
for (const item of items) {
|
|
7083
|
+
this.perspectiveRootRtIds.add(item.rtId);
|
|
7084
|
+
result.push(new TreeItemDataTyped(`${item.ckTypeId}@${item.rtId}`, this.extractDisplayName(item), this.extractTooltip(item), item, this.resolveIcon(item.ckTypeId), expandable));
|
|
7085
|
+
}
|
|
7086
|
+
return result;
|
|
7087
|
+
}
|
|
7088
|
+
catch (error) {
|
|
7089
|
+
console.error('Error fetching type-perspective roots for', ckTypeId, error);
|
|
7090
|
+
return [];
|
|
7091
|
+
}
|
|
7092
|
+
}
|
|
6746
7093
|
/**
|
|
6747
7094
|
* Gets ParentChild association of given Runtime Entity.
|
|
6748
7095
|
*
|
|
@@ -7036,11 +7383,86 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
|
|
|
7036
7383
|
}]
|
|
7037
7384
|
}] });
|
|
7038
7385
|
|
|
7386
|
+
/**
|
|
7387
|
+
* Small toolbar control that lets the user switch the active tree perspective
|
|
7388
|
+
* (AB#4263). Theme-neutral (Kendo CSS variables only). Renders nothing when
|
|
7389
|
+
* there is a single perspective, so a zero-config tenant sees no switcher.
|
|
7390
|
+
*/
|
|
7391
|
+
class PerspectiveSwitcherComponent {
|
|
7392
|
+
/** All selectable perspectives (built-in spatial first). */
|
|
7393
|
+
perspectives = input([], /* @ts-ignore */
|
|
7394
|
+
...(ngDevMode ? [{ debugName: "perspectives" }] : /* istanbul ignore next */ []));
|
|
7395
|
+
/** Currently active perspective key. */
|
|
7396
|
+
activeKey = input('Spatial', /* @ts-ignore */
|
|
7397
|
+
...(ngDevMode ? [{ debugName: "activeKey" }] : /* istanbul ignore next */ []));
|
|
7398
|
+
/** Label shown before the dropdown. */
|
|
7399
|
+
label = input('Perspective', /* @ts-ignore */
|
|
7400
|
+
...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
7401
|
+
/** Emits the selected perspective key. */
|
|
7402
|
+
perspectiveChange = output();
|
|
7403
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: PerspectiveSwitcherComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
7404
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: PerspectiveSwitcherComponent, isStandalone: true, selector: "mm-perspective-switcher", inputs: { perspectives: { classPropertyName: "perspectives", publicName: "perspectives", isSignal: true, isRequired: false, transformFunction: null }, activeKey: { classPropertyName: "activeKey", publicName: "activeKey", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { perspectiveChange: "perspectiveChange" }, ngImport: i0, template: `
|
|
7405
|
+
@if (perspectives().length > 1) {
|
|
7406
|
+
<div class="perspective-switcher">
|
|
7407
|
+
<span class="switcher-label">{{ label() }}</span>
|
|
7408
|
+
<kendo-dropdownlist
|
|
7409
|
+
[data]="perspectives()"
|
|
7410
|
+
[textField]="'displayName'"
|
|
7411
|
+
[valueField]="'key'"
|
|
7412
|
+
[valuePrimitive]="true"
|
|
7413
|
+
[value]="activeKey()"
|
|
7414
|
+
(valueChange)="perspectiveChange.emit($event)"
|
|
7415
|
+
></kendo-dropdownlist>
|
|
7416
|
+
</div>
|
|
7417
|
+
}
|
|
7418
|
+
`, isInline: true, styles: [".perspective-switcher{display:flex;align-items:center;gap:8px;padding:4px 8px}.switcher-label{font-size:.85rem;font-weight:600;color:var(--kendo-color-subtle, #6c757d);white-space:nowrap}\n"], dependencies: [{ kind: "ngmodule", type: DropDownListModule }, { kind: "component", type: i3$1.DropDownListComponent, selector: "kendo-dropdownlist", inputs: ["customIconClass", "showStickyHeader", "icon", "svgIcon", "loading", "data", "value", "textField", "valueField", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "defaultItem", "disabled", "itemDisabled", "readonly", "filterable", "virtual", "ignoreCase", "delay", "valuePrimitive", "tabindex", "tabIndex", "size", "rounded", "fillMode", "leftRightArrowsNavigation", "id"], outputs: ["valueChange", "filterChange", "selectionChange", "open", "opened", "close", "closed", "focus", "blur"], exportAs: ["kendoDropDownList"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
7419
|
+
}
|
|
7420
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: PerspectiveSwitcherComponent, decorators: [{
|
|
7421
|
+
type: Component,
|
|
7422
|
+
args: [{ selector: 'mm-perspective-switcher', standalone: true, imports: [DropDownListModule], template: `
|
|
7423
|
+
@if (perspectives().length > 1) {
|
|
7424
|
+
<div class="perspective-switcher">
|
|
7425
|
+
<span class="switcher-label">{{ label() }}</span>
|
|
7426
|
+
<kendo-dropdownlist
|
|
7427
|
+
[data]="perspectives()"
|
|
7428
|
+
[textField]="'displayName'"
|
|
7429
|
+
[valueField]="'key'"
|
|
7430
|
+
[valuePrimitive]="true"
|
|
7431
|
+
[value]="activeKey()"
|
|
7432
|
+
(valueChange)="perspectiveChange.emit($event)"
|
|
7433
|
+
></kendo-dropdownlist>
|
|
7434
|
+
</div>
|
|
7435
|
+
}
|
|
7436
|
+
`, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".perspective-switcher{display:flex;align-items:center;gap:8px;padding:4px 8px}.switcher-label{font-size:.85rem;font-weight:600;color:var(--kendo-color-subtle, #6c757d);white-space:nowrap}\n"] }]
|
|
7437
|
+
}], propDecorators: { perspectives: [{ type: i0.Input, args: [{ isSignal: true, alias: "perspectives", required: false }] }], activeKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeKey", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], perspectiveChange: [{ type: i0.Output, args: ["perspectiveChange"] }] } });
|
|
7438
|
+
|
|
7039
7439
|
class EntitySelectorDialogComponent {
|
|
7040
7440
|
windowRef = inject(WindowRef);
|
|
7041
7441
|
treeDataSource = inject(RuntimeBrowserDataSource);
|
|
7442
|
+
tree;
|
|
7443
|
+
/** Selectable tree perspectives (AB#4263); switcher hides itself when <= 1. */
|
|
7444
|
+
perspectives = signal([], /* @ts-ignore */
|
|
7445
|
+
...(ngDevMode ? [{ debugName: "perspectives" }] : /* istanbul ignore next */ []));
|
|
7446
|
+
activePerspectiveKey = signal(this.treeDataSource.getActivePerspectiveKey(), /* @ts-ignore */
|
|
7447
|
+
...(ngDevMode ? [{ debugName: "activePerspectiveKey" }] : /* istanbul ignore next */ []));
|
|
7042
7448
|
data = {};
|
|
7043
7449
|
selectedEntity = null;
|
|
7450
|
+
async ngOnInit() {
|
|
7451
|
+
try {
|
|
7452
|
+
this.perspectives.set(await this.treeDataSource.getPerspectives());
|
|
7453
|
+
this.activePerspectiveKey.set(this.treeDataSource.getActivePerspectiveKey());
|
|
7454
|
+
}
|
|
7455
|
+
catch (error) {
|
|
7456
|
+
console.error('Error loading perspectives', error);
|
|
7457
|
+
}
|
|
7458
|
+
}
|
|
7459
|
+
/** Switches the active perspective and reloads the picker tree. */
|
|
7460
|
+
async onPerspectiveChange(key) {
|
|
7461
|
+
this.treeDataSource.setActivePerspective(key);
|
|
7462
|
+
this.activePerspectiveKey.set(key);
|
|
7463
|
+
this.selectedEntity = null;
|
|
7464
|
+
await this.tree?.refreshTree();
|
|
7465
|
+
}
|
|
7044
7466
|
onNodeSelected(node) {
|
|
7045
7467
|
const item = node.item;
|
|
7046
7468
|
if (item && typeof item === 'object' && 'rtId' in item && 'ckTypeId' in item) {
|
|
@@ -7070,11 +7492,17 @@ class EntitySelectorDialogComponent {
|
|
|
7070
7492
|
this.windowRef.close();
|
|
7071
7493
|
}
|
|
7072
7494
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: EntitySelectorDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
7073
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: EntitySelectorDialogComponent, isStandalone: true, selector: "mm-entity-selector-dialog", providers: [RuntimeBrowserDataSource], ngImport: i0, template: `
|
|
7495
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: EntitySelectorDialogComponent, isStandalone: true, selector: "mm-entity-selector-dialog", providers: [RuntimeBrowserDataSource], viewQueries: [{ propertyName: "tree", first: true, predicate: ["tree"], descendants: true }], ngImport: i0, template: `
|
|
7074
7496
|
<div class="entity-selector">
|
|
7075
7497
|
<div class="entity-selector-body">
|
|
7498
|
+
<mm-perspective-switcher
|
|
7499
|
+
[perspectives]="perspectives()"
|
|
7500
|
+
[activeKey]="activePerspectiveKey()"
|
|
7501
|
+
(perspectiveChange)="onPerspectiveChange($event)"
|
|
7502
|
+
></mm-perspective-switcher>
|
|
7076
7503
|
<div class="tree-section">
|
|
7077
7504
|
<mm-tree-view
|
|
7505
|
+
#tree
|
|
7078
7506
|
[dataSource]="treeDataSource"
|
|
7079
7507
|
(nodeSelected)="onNodeSelected($event)"
|
|
7080
7508
|
></mm-tree-view>
|
|
@@ -7109,7 +7537,7 @@ class EntitySelectorDialogComponent {
|
|
|
7109
7537
|
</button>
|
|
7110
7538
|
</div>
|
|
7111
7539
|
</div>
|
|
7112
|
-
`, isInline: true, styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0}.entity-selector{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;box-sizing:border-box}.entity-selector-body{flex:1 1 auto;min-height:0;overflow-y:auto;padding:12px;display:flex;flex-direction:column;gap:12px}.tree-section{flex:1;min-height:0;overflow:auto;border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px;background:var(--kendo-color-surface, #ffffff)}.selection-preview{padding:10px 12px;border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px;background:var(--kendo-color-surface-alt, #f8f9fa);display:flex;flex-direction:column;gap:4px;flex-shrink:0}.preview-row{display:flex;gap:8px}.preview-row .preview-label{font-weight:600;min-width:45px;color:var(--kendo-color-subtle, #6c757d);font-size:.85rem}.preview-row .preview-value{font-size:.85rem}.preview-row .monospace{font-family:monospace}.selection-hint{text-align:center;padding:8px;color:var(--kendo-color-subtle, #6c757d);font-size:.85rem;flex-shrink:0}.dialog-actions{flex:0 0 auto;display:flex;gap:8px;justify-content:flex-end;padding:10px 14px;border-top:1px solid var(--kendo-color-border, #dee2e6);background:var(--kendo-color-surface, transparent)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconPosition", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "component", type: TreeComponent, selector: "mm-tree-view", inputs: ["dataSource"], outputs: ["nodeSelected", "nodeClick", "nodeDoubleClick", "nodeDrop", "expand", "collapse"] }], changeDetection: i0.ChangeDetectionStrategy.Eager });
|
|
7540
|
+
`, isInline: true, styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0}.entity-selector{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;box-sizing:border-box}.entity-selector-body{flex:1 1 auto;min-height:0;overflow-y:auto;padding:12px;display:flex;flex-direction:column;gap:12px}.tree-section{flex:1;min-height:0;overflow:auto;border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px;background:var(--kendo-color-surface, #ffffff)}.selection-preview{padding:10px 12px;border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px;background:var(--kendo-color-surface-alt, #f8f9fa);display:flex;flex-direction:column;gap:4px;flex-shrink:0}.preview-row{display:flex;gap:8px}.preview-row .preview-label{font-weight:600;min-width:45px;color:var(--kendo-color-subtle, #6c757d);font-size:.85rem}.preview-row .preview-value{font-size:.85rem}.preview-row .monospace{font-family:monospace}.selection-hint{text-align:center;padding:8px;color:var(--kendo-color-subtle, #6c757d);font-size:.85rem;flex-shrink:0}.dialog-actions{flex:0 0 auto;display:flex;gap:8px;justify-content:flex-end;padding:10px 14px;border-top:1px solid var(--kendo-color-border, #dee2e6);background:var(--kendo-color-surface, transparent)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconPosition", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "component", type: TreeComponent, selector: "mm-tree-view", inputs: ["dataSource"], outputs: ["nodeSelected", "nodeClick", "nodeDoubleClick", "nodeDrop", "expand", "collapse"] }, { kind: "component", type: PerspectiveSwitcherComponent, selector: "mm-perspective-switcher", inputs: ["perspectives", "activeKey", "label"], outputs: ["perspectiveChange"] }], changeDetection: i0.ChangeDetectionStrategy.Eager });
|
|
7113
7541
|
}
|
|
7114
7542
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: EntitySelectorDialogComponent, decorators: [{
|
|
7115
7543
|
type: Component,
|
|
@@ -7117,11 +7545,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
|
|
|
7117
7545
|
CommonModule,
|
|
7118
7546
|
ButtonModule,
|
|
7119
7547
|
TreeComponent,
|
|
7548
|
+
PerspectiveSwitcherComponent,
|
|
7120
7549
|
], providers: [RuntimeBrowserDataSource], template: `
|
|
7121
7550
|
<div class="entity-selector">
|
|
7122
7551
|
<div class="entity-selector-body">
|
|
7552
|
+
<mm-perspective-switcher
|
|
7553
|
+
[perspectives]="perspectives()"
|
|
7554
|
+
[activeKey]="activePerspectiveKey()"
|
|
7555
|
+
(perspectiveChange)="onPerspectiveChange($event)"
|
|
7556
|
+
></mm-perspective-switcher>
|
|
7123
7557
|
<div class="tree-section">
|
|
7124
7558
|
<mm-tree-view
|
|
7559
|
+
#tree
|
|
7125
7560
|
[dataSource]="treeDataSource"
|
|
7126
7561
|
(nodeSelected)="onNodeSelected($event)"
|
|
7127
7562
|
></mm-tree-view>
|
|
@@ -7157,7 +7592,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
|
|
|
7157
7592
|
</div>
|
|
7158
7593
|
</div>
|
|
7159
7594
|
`, changeDetection: ChangeDetectionStrategy.Eager, styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0}.entity-selector{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;box-sizing:border-box}.entity-selector-body{flex:1 1 auto;min-height:0;overflow-y:auto;padding:12px;display:flex;flex-direction:column;gap:12px}.tree-section{flex:1;min-height:0;overflow:auto;border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px;background:var(--kendo-color-surface, #ffffff)}.selection-preview{padding:10px 12px;border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px;background:var(--kendo-color-surface-alt, #f8f9fa);display:flex;flex-direction:column;gap:4px;flex-shrink:0}.preview-row{display:flex;gap:8px}.preview-row .preview-label{font-weight:600;min-width:45px;color:var(--kendo-color-subtle, #6c757d);font-size:.85rem}.preview-row .preview-value{font-size:.85rem}.preview-row .monospace{font-family:monospace}.selection-hint{text-align:center;padding:8px;color:var(--kendo-color-subtle, #6c757d);font-size:.85rem;flex-shrink:0}.dialog-actions{flex:0 0 auto;display:flex;gap:8px;justify-content:flex-end;padding:10px 14px;border-top:1px solid var(--kendo-color-border, #dee2e6);background:var(--kendo-color-surface, transparent)}\n"] }]
|
|
7160
|
-
}]
|
|
7595
|
+
}], propDecorators: { tree: [{
|
|
7596
|
+
type: ViewChild,
|
|
7597
|
+
args: ['tree']
|
|
7598
|
+
}] } });
|
|
7161
7599
|
|
|
7162
7600
|
const DIALOG_KEY$1 = 'entity-selector';
|
|
7163
7601
|
const DEFAULT_SIZE$1 = { width: 600, height: 680 };
|
|
@@ -8817,6 +9255,7 @@ const DEFAULT_RUNTIME_BROWSER_MESSAGES = {
|
|
|
8817
9255
|
mappingNotSet: '(not set)',
|
|
8818
9256
|
mappingSelect: 'Select...',
|
|
8819
9257
|
mappingNoneConfigured: 'No data point mappings configured yet.',
|
|
9258
|
+
perspective: 'Perspective',
|
|
8820
9259
|
};
|
|
8821
9260
|
|
|
8822
9261
|
class DataMappingListComponent {
|
|
@@ -14754,6 +15193,26 @@ class RuntimeBrowserComponent {
|
|
|
14754
15193
|
...this.messages(),
|
|
14755
15194
|
}), /* @ts-ignore */
|
|
14756
15195
|
...(ngDevMode ? [{ debugName: "resolvedMessages" }] : /* istanbul ignore next */ []));
|
|
15196
|
+
/** Selectable tree perspectives (AB#4263); switcher hides itself when <= 1. */
|
|
15197
|
+
perspectives = signal([], /* @ts-ignore */
|
|
15198
|
+
...(ngDevMode ? [{ debugName: "perspectives" }] : /* istanbul ignore next */ []));
|
|
15199
|
+
activePerspectiveKey = signal(this.dataSource.getActivePerspectiveKey(), /* @ts-ignore */
|
|
15200
|
+
...(ngDevMode ? [{ debugName: "activePerspectiveKey" }] : /* istanbul ignore next */ []));
|
|
15201
|
+
/** Switches the active perspective and reloads the tree from its new roots. */
|
|
15202
|
+
async onPerspectiveChange(key) {
|
|
15203
|
+
this.dataSource.setActivePerspective(key);
|
|
15204
|
+
this.activePerspectiveKey.set(key);
|
|
15205
|
+
await this.treeDetail?.refreshTree();
|
|
15206
|
+
}
|
|
15207
|
+
async loadPerspectives() {
|
|
15208
|
+
try {
|
|
15209
|
+
this.perspectives.set(await this.dataSource.getPerspectives());
|
|
15210
|
+
this.activePerspectiveKey.set(this.dataSource.getActivePerspectiveKey());
|
|
15211
|
+
}
|
|
15212
|
+
catch (error) {
|
|
15213
|
+
console.error('Error loading perspectives', error);
|
|
15214
|
+
}
|
|
15215
|
+
}
|
|
14757
15216
|
treeDetail;
|
|
14758
15217
|
detailsPanel;
|
|
14759
15218
|
// Define toolbar actions
|
|
@@ -14833,6 +15292,8 @@ class RuntimeBrowserComponent {
|
|
|
14833
15292
|
return !this.isLoading && this.isSelectedItemAnRtEntity && !this.isEditing;
|
|
14834
15293
|
}
|
|
14835
15294
|
ngAfterViewInit() {
|
|
15295
|
+
// Load the selectable perspectives for the switcher (AB#4263).
|
|
15296
|
+
void this.loadPerspectives();
|
|
14836
15297
|
// Restore tree state after the tree has been initialized
|
|
14837
15298
|
this.waitForTreeComponent().then((isReady) => {
|
|
14838
15299
|
if (isReady) {
|
|
@@ -15338,6 +15799,12 @@ class RuntimeBrowserComponent {
|
|
|
15338
15799
|
<!-- Main Content -->
|
|
15339
15800
|
<div class="lcars-content-panel">
|
|
15340
15801
|
<div class="panel-accent-top"></div>
|
|
15802
|
+
<mm-perspective-switcher
|
|
15803
|
+
[perspectives]="perspectives()"
|
|
15804
|
+
[activeKey]="activePerspectiveKey()"
|
|
15805
|
+
[label]="resolvedMessages().perspective ?? 'Perspective'"
|
|
15806
|
+
(perspectiveChange)="onPerspectiveChange($event)"
|
|
15807
|
+
></mm-perspective-switcher>
|
|
15341
15808
|
<mm-base-tree-detail
|
|
15342
15809
|
#treeDetail
|
|
15343
15810
|
[treeDataSource]="dataSource"
|
|
@@ -15373,11 +15840,15 @@ class RuntimeBrowserComponent {
|
|
|
15373
15840
|
</div>
|
|
15374
15841
|
</div>
|
|
15375
15842
|
</div>
|
|
15376
|
-
`, isInline: true, styles: [":host{display:flex;flex-direction:column;height:100%;width:100%}.runtime-browser-container{display:flex;flex-direction:column;height:100%;padding:16px;gap:16px}::ng-deep mm-base-tree-detail .k-splitter{background:transparent;border:none}::ng-deep mm-base-tree-detail .k-splitter .k-splitbar{background:linear-gradient(180deg,var(--octo-mint-30),transparent);width:4px!important}::ng-deep mm-base-tree-detail .k-splitter .k-splitbar:hover{background:linear-gradient(180deg,var(--octo-mint-50),var(--octo-mint-20))}::ng-deep mm-base-tree-detail kendo-treeview .k-treeview-item .k-treeview-leaf{font-family:Roboto,sans-serif;transition:all .2s ease;border-radius:4px}::ng-deep mm-base-tree-detail kendo-treeview .k-treeview-item .k-treeview-leaf:hover{background:var(--octo-mint-10);color:var(--octo-mint)}::ng-deep mm-base-tree-detail kendo-treeview .k-treeview-item .k-treeview-leaf.k-selected{background:linear-gradient(90deg,var(--octo-mint-20),transparent);color:var(--octo-mint);border-left:3px solid var(--octo-mint)}::ng-deep mm-base-tree-detail kendo-treeview .k-treeview-item .k-treeview-leaf.k-selected:hover{background:linear-gradient(90deg,var(--octo-mint-25),transparent)}::ng-deep mm-base-tree-detail .toolbar{background:linear-gradient(90deg,var(--octo-mint-05),transparent);border-bottom:1px solid var(--octo-mint-20)}::ng-deep mm-base-tree-detail .base-tree-detail-container{height:100%;display:flex;flex-direction:column}::ng-deep mm-base-tree-detail .base-tree-detail-container .k-splitter{flex:1;min-height:0}::ng-deep mm-base-tree-detail kendo-splitter-pane.detail-pane{display:flex!important;flex-direction:column!important;height:100%!important}::ng-deep mm-base-tree-detail kendo-splitter-pane.detail-pane>mm-runtime-browser-details{display:flex!important;flex:1!important;min-height:0!important;height:100%!important}::ng-deep mm-base-tree-detail kendo-splitter-pane.detail-pane>mm-runtime-browser-details>.runtime-browser-details{flex:1;display:flex;flex-direction:column;min-height:0}@media(max-width:1024px){.runtime-browser-container{padding:12px;gap:12px}}@media(max-width:768px){.runtime-browser-container{padding:8px;gap:10px}}\n"], dependencies: [{ kind: "component", type: BaseTreeDetailComponent, selector: "mm-base-tree-detail", inputs: ["treeDataSource", "leftPaneSize", "leftToolbarActions", "rightToolbarActions"], outputs: ["nodeSelected", "nodeDropped"] }, { kind: "component", type: RuntimeBrowserDetailsComponent, selector: "mm-runtime-browser-details", inputs: ["selectedItem", "showDataMapping", "messages", "expressionValidator"], outputs: ["entitySaved"] }], changeDetection: i0.ChangeDetectionStrategy.Eager });
|
|
15843
|
+
`, isInline: true, styles: [":host{display:flex;flex-direction:column;height:100%;width:100%}.runtime-browser-container{display:flex;flex-direction:column;height:100%;padding:16px;gap:16px}::ng-deep mm-base-tree-detail .k-splitter{background:transparent;border:none}::ng-deep mm-base-tree-detail .k-splitter .k-splitbar{background:linear-gradient(180deg,var(--octo-mint-30),transparent);width:4px!important}::ng-deep mm-base-tree-detail .k-splitter .k-splitbar:hover{background:linear-gradient(180deg,var(--octo-mint-50),var(--octo-mint-20))}::ng-deep mm-base-tree-detail kendo-treeview .k-treeview-item .k-treeview-leaf{font-family:Roboto,sans-serif;transition:all .2s ease;border-radius:4px}::ng-deep mm-base-tree-detail kendo-treeview .k-treeview-item .k-treeview-leaf:hover{background:var(--octo-mint-10);color:var(--octo-mint)}::ng-deep mm-base-tree-detail kendo-treeview .k-treeview-item .k-treeview-leaf.k-selected{background:linear-gradient(90deg,var(--octo-mint-20),transparent);color:var(--octo-mint);border-left:3px solid var(--octo-mint)}::ng-deep mm-base-tree-detail kendo-treeview .k-treeview-item .k-treeview-leaf.k-selected:hover{background:linear-gradient(90deg,var(--octo-mint-25),transparent)}::ng-deep mm-base-tree-detail .toolbar{background:linear-gradient(90deg,var(--octo-mint-05),transparent);border-bottom:1px solid var(--octo-mint-20)}::ng-deep mm-base-tree-detail .base-tree-detail-container{height:100%;display:flex;flex-direction:column}::ng-deep mm-base-tree-detail .base-tree-detail-container .k-splitter{flex:1;min-height:0}::ng-deep mm-base-tree-detail kendo-splitter-pane.detail-pane{display:flex!important;flex-direction:column!important;height:100%!important}::ng-deep mm-base-tree-detail kendo-splitter-pane.detail-pane>mm-runtime-browser-details{display:flex!important;flex:1!important;min-height:0!important;height:100%!important}::ng-deep mm-base-tree-detail kendo-splitter-pane.detail-pane>mm-runtime-browser-details>.runtime-browser-details{flex:1;display:flex;flex-direction:column;min-height:0}@media(max-width:1024px){.runtime-browser-container{padding:12px;gap:12px}}@media(max-width:768px){.runtime-browser-container{padding:8px;gap:10px}}\n"], dependencies: [{ kind: "component", type: BaseTreeDetailComponent, selector: "mm-base-tree-detail", inputs: ["treeDataSource", "leftPaneSize", "leftToolbarActions", "rightToolbarActions"], outputs: ["nodeSelected", "nodeDropped"] }, { kind: "component", type: RuntimeBrowserDetailsComponent, selector: "mm-runtime-browser-details", inputs: ["selectedItem", "showDataMapping", "messages", "expressionValidator"], outputs: ["entitySaved"] }, { kind: "component", type: PerspectiveSwitcherComponent, selector: "mm-perspective-switcher", inputs: ["perspectives", "activeKey", "label"], outputs: ["perspectiveChange"] }], changeDetection: i0.ChangeDetectionStrategy.Eager });
|
|
15377
15844
|
}
|
|
15378
15845
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RuntimeBrowserComponent, decorators: [{
|
|
15379
15846
|
type: Component,
|
|
15380
|
-
args: [{ selector: 'mm-runtime-browser', imports: [
|
|
15847
|
+
args: [{ selector: 'mm-runtime-browser', imports: [
|
|
15848
|
+
BaseTreeDetailComponent,
|
|
15849
|
+
RuntimeBrowserDetailsComponent,
|
|
15850
|
+
PerspectiveSwitcherComponent,
|
|
15851
|
+
], template: `
|
|
15381
15852
|
<div class="runtime-browser-container kendo-theme-provider">
|
|
15382
15853
|
<!-- LCARS Header -->
|
|
15383
15854
|
<div class="lcars-page-header">
|
|
@@ -15404,6 +15875,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
|
|
|
15404
15875
|
<!-- Main Content -->
|
|
15405
15876
|
<div class="lcars-content-panel">
|
|
15406
15877
|
<div class="panel-accent-top"></div>
|
|
15878
|
+
<mm-perspective-switcher
|
|
15879
|
+
[perspectives]="perspectives()"
|
|
15880
|
+
[activeKey]="activePerspectiveKey()"
|
|
15881
|
+
[label]="resolvedMessages().perspective ?? 'Perspective'"
|
|
15882
|
+
(perspectiveChange)="onPerspectiveChange($event)"
|
|
15883
|
+
></mm-perspective-switcher>
|
|
15407
15884
|
<mm-base-tree-detail
|
|
15408
15885
|
#treeDetail
|
|
15409
15886
|
[treeDataSource]="dataSource"
|