@principal-ai/subsystems-react 0.24.24 → 0.24.26
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/dist/subsystem/ConstructsCatalog.d.ts.map +1 -1
- package/dist/subsystem/ConstructsCatalog.js +182 -12
- package/dist/subsystem/ConstructsCatalog.js.map +1 -1
- package/dist/subsystem/model.d.ts +3 -0
- package/dist/subsystem/model.d.ts.map +1 -1
- package/dist/subsystem/model.js +18 -0
- package/dist/subsystem/model.js.map +1 -1
- package/dist/subsystem/nodes.d.ts.map +1 -1
- package/dist/subsystem/nodes.js +21 -2
- package/dist/subsystem/nodes.js.map +1 -1
- package/dist/utils/elkLayout.d.ts +1 -0
- package/dist/utils/elkLayout.d.ts.map +1 -1
- package/dist/utils/elkLayout.js +5 -1
- package/dist/utils/elkLayout.js.map +1 -1
- package/package.json +1 -1
- package/src/subsystem/ConstructsCatalog.tsx +280 -11
- package/src/subsystem/model.test.ts +2 -0
- package/src/subsystem/model.ts +22 -0
- package/src/subsystem/nodes.test.ts +6 -2
- package/src/subsystem/nodes.tsx +28 -0
- package/src/utils/elkLayout.ts +6 -2
package/src/subsystem/model.ts
CHANGED
|
@@ -502,6 +502,27 @@ export function moduleGroupNodeId(moduleKey: string): string {
|
|
|
502
502
|
return `module:${moduleKey}`;
|
|
503
503
|
}
|
|
504
504
|
|
|
505
|
+
export const MODULE_BADGE_INSET = 12;
|
|
506
|
+
const MODULE_BADGE_CHAR_WIDTH = 12.5;
|
|
507
|
+
const MODULE_BADGE_CHROME = 16;
|
|
508
|
+
const MODULE_FRAME_BORDER = 4;
|
|
509
|
+
|
|
510
|
+
export function moduleBadgeLabel(path: string, availableWidth = 0): string {
|
|
511
|
+
if (path.length * MODULE_BADGE_CHAR_WIDTH + MODULE_BADGE_CHROME <= availableWidth) {
|
|
512
|
+
return path;
|
|
513
|
+
}
|
|
514
|
+
const parts = path.split(/[\\/]/).filter((part) => part !== '' && part !== '.');
|
|
515
|
+
if (parts.length <= 2) return path;
|
|
516
|
+
return `${parts[0]}/…/${parts[parts.length - 1]}`;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
export function moduleMinWidthForBadge(path: string): number {
|
|
520
|
+
return Math.ceil(
|
|
521
|
+
moduleBadgeLabel(path).length * MODULE_BADGE_CHAR_WIDTH +
|
|
522
|
+
MODULE_BADGE_CHROME + MODULE_BADGE_INSET * 2 + MODULE_FRAME_BORDER,
|
|
523
|
+
);
|
|
524
|
+
}
|
|
525
|
+
|
|
505
526
|
/** React Flow id for a package (repo) boundary group node. */
|
|
506
527
|
export function packageGroupNodeId(packageKey: string): string {
|
|
507
528
|
return `package:${packageKey}`;
|
|
@@ -1363,6 +1384,7 @@ export async function buildSubsystemGraph(
|
|
|
1363
1384
|
id: g.id,
|
|
1364
1385
|
memberIds: g.memberIds,
|
|
1365
1386
|
parentId: g.parentId,
|
|
1387
|
+
minWidth: g.region.kind === 'module' ? moduleMinWidthForBadge(g.region.label) : undefined,
|
|
1366
1388
|
})),
|
|
1367
1389
|
});
|
|
1368
1390
|
const builtGroupIds = new Set(result.groupBounds.keys());
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import { describe, expect, test } from 'bun:test';
|
|
2
|
-
import {
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, mock, test } from 'bun:test';
|
|
2
|
+
import { createElement, type ComponentProps } from 'react';
|
|
3
|
+
import { cleanup, fireEvent, render } from '@testing-library/react/pure';
|
|
4
|
+
import { ThemeProvider } from '@principal-ade/industry-theme';
|
|
5
|
+
import { Window } from 'happy-dom';
|
|
6
|
+
import { EDGE_DIM_ALPHA, fileMatchForNode, flowElementVisibility, hexWithAlpha, SubsystemGroupNode } from './nodes';
|
|
3
7
|
|
|
4
8
|
describe('hexWithAlpha', () => {
|
|
5
9
|
test('appends a two-digit alpha to #rrggbb', () => {
|
package/src/subsystem/nodes.tsx
CHANGED
|
@@ -29,6 +29,8 @@ import {
|
|
|
29
29
|
deriveNameFromSymbol,
|
|
30
30
|
BADGE_EDGE_INSET,
|
|
31
31
|
nodeMinWidthForBadges,
|
|
32
|
+
MODULE_BADGE_INSET,
|
|
33
|
+
moduleBadgeLabel,
|
|
32
34
|
packageColor,
|
|
33
35
|
type SubsystemGraphNodeData,
|
|
34
36
|
type SubsystemGroupNodeData,
|
|
@@ -352,6 +354,12 @@ export function SubsystemGroupNode(props: NodeProps<Node<SubsystemGroupNodeData,
|
|
|
352
354
|
// Kind is carried by frame chrome (square = process; rounded = module/package)
|
|
353
355
|
// — don't prefix `module ·` / `package ·` or the path reads twice.
|
|
354
356
|
const label = region?.label ?? '';
|
|
357
|
+
const [expandedLabel, setExpandedLabel] = useState<string | null>(null);
|
|
358
|
+
const isModule = region?.kind === 'module';
|
|
359
|
+
const expanded = isModule && expandedLabel === label;
|
|
360
|
+
const availableBadgeWidth = Math.max(0, (width ?? 400) - MODULE_BADGE_INSET * 2 - 4);
|
|
361
|
+
const collapsedLabel = isModule ? moduleBadgeLabel(label, availableBadgeWidth) : label;
|
|
362
|
+
const canExpand = isModule && collapsedLabel !== label;
|
|
355
363
|
// Process frames match external nodes — square corners (outside the soft
|
|
356
364
|
// component language). Module / package frames keep the rounded look.
|
|
357
365
|
const isProcess = region?.kind === 'process';
|
|
@@ -382,6 +390,26 @@ export function SubsystemGroupNode(props: NodeProps<Node<SubsystemGroupNodeData,
|
|
|
382
390
|
}}
|
|
383
391
|
>
|
|
384
392
|
<div
|
|
393
|
+
className={isModule ? 'nodrag nopan' : undefined}
|
|
394
|
+
role={canExpand ? 'button' : undefined}
|
|
395
|
+
tabIndex={canExpand && !hidden ? 0 : undefined}
|
|
396
|
+
aria-expanded={canExpand ? expanded : undefined}
|
|
397
|
+
aria-label={canExpand ? `${expanded ? 'Collapse' : 'Expand'} module path: ${label}` : undefined}
|
|
398
|
+
title={isModule ? label : undefined}
|
|
399
|
+
onClick={canExpand ? (event) => {
|
|
400
|
+
event.stopPropagation();
|
|
401
|
+
setExpandedLabel(expanded ? null : label);
|
|
402
|
+
} : undefined}
|
|
403
|
+
onKeyDown={canExpand ? (event) => {
|
|
404
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
405
|
+
event.preventDefault();
|
|
406
|
+
event.stopPropagation();
|
|
407
|
+
setExpandedLabel(expanded ? null : label);
|
|
408
|
+
} else if (event.key === 'Escape' && expanded) {
|
|
409
|
+
event.stopPropagation();
|
|
410
|
+
setExpandedLabel(null);
|
|
411
|
+
}
|
|
412
|
+
} : undefined}
|
|
385
413
|
style={{
|
|
386
414
|
position: 'absolute',
|
|
387
415
|
// Process: badge fill starts with the process fill (inside the
|
package/src/utils/elkLayout.ts
CHANGED
|
@@ -79,7 +79,7 @@ export interface ElkLayoutOptions {
|
|
|
79
79
|
* nests this group under another group; omit for a root-level frame.
|
|
80
80
|
* Members reference their immediate parent via React Flow `parentId`.
|
|
81
81
|
*/
|
|
82
|
-
groups?: Array<{ id: string; memberIds: string[]; parentId?: string }>;
|
|
82
|
+
groups?: Array<{ id: string; memberIds: string[]; parentId?: string; minWidth?: number }>;
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
/** Result of ELK layout computation */
|
|
@@ -587,7 +587,11 @@ export async function computeElkLayout(
|
|
|
587
587
|
builtGroups.set(g.id, {
|
|
588
588
|
id: g.id,
|
|
589
589
|
children: childNodes,
|
|
590
|
-
layoutOptions: compoundLayoutOptions
|
|
590
|
+
layoutOptions: g.minWidth == null ? compoundLayoutOptions : {
|
|
591
|
+
...compoundLayoutOptions,
|
|
592
|
+
'elk.nodeSize.constraints': 'MINIMUM_SIZE',
|
|
593
|
+
'elk.nodeSize.minimum': `(${g.minWidth},0)`,
|
|
594
|
+
},
|
|
591
595
|
});
|
|
592
596
|
}
|
|
593
597
|
if (!progress) {
|