@tumaet/apollon 4.2.22 → 4.3.0

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.
@@ -14,6 +14,7 @@ export type DiagramStore = {
14
14
  assessments: Record<string, Assessment>;
15
15
  interactiveElements: Record<string, boolean>;
16
16
  interactiveRelationships: Record<string, boolean>;
17
+ interactiveSelectionInitialized: boolean;
17
18
  canUndo: boolean;
18
19
  canRedo: boolean;
19
20
  undoManager: Y.UndoManager | null;
@@ -13,5 +13,6 @@ export * from './bpmnConstraints';
13
13
  export * from './versionConverter';
14
14
  export * from './labelUtils';
15
15
  export * from './alignmentUtils';
16
- export * from './flatSvgExporter';
17
16
  export * from './requiredInterfaceUtils';
17
+ export { wrapTextInRect, layoutTextInEllipse, layoutTextInDiamond, maxLinesForHeight, } from './svgTextLayout';
18
+ export type { WrappedText, ShapeLayout, SvgFontSpec, WhiteSpaceMode, } from './svgTextLayout';
@@ -1,4 +1,7 @@
1
1
  import { ApollonEdge, ApollonNode, InteractiveElements } from '../typings';
2
- export declare function pruneInteractiveElements(interactive: InteractiveElements | undefined, nodes: Array<Pick<ApollonNode, "id">>, edges: Array<Pick<ApollonEdge, "id">>): InteractiveElements | undefined;
2
+ type InteractivePruneNode = Pick<ApollonNode, "id"> & Partial<Pick<ApollonNode, "data">>;
3
+ export declare function getNestedNodeElementIds(nodes: InteractivePruneNode[]): Set<string>;
4
+ export declare function pruneInteractiveElements(interactive: InteractiveElements | undefined, nodes: InteractivePruneNode[], edges: Array<Pick<ApollonEdge, "id">>): InteractiveElements | undefined;
3
5
  export declare function toggleInteractiveRecord(record: Record<string, boolean>, id: string): Record<string, boolean>;
4
6
  export declare function hasInteractiveSelections(interactive: InteractiveElements | undefined): boolean;
7
+ export {};
@@ -2,4 +2,14 @@ import { XYPosition, Node } from '@xyflow/react';
2
2
  export declare const getPositionOnCanvas: (node: Node, allNodes: Node[]) => XYPosition;
3
3
  export declare const resizeAllParents: (node: Node, allNodes: Node[]) => Node[];
4
4
  export declare function sortNodesTopologically(nodes: Node[]): Node[];
5
+ /**
6
+ * Does this node's SVG renderer wrap its label? Drives whether the rename
7
+ * popover accepts multiline input — see `NODE_LABEL_CAPABILITIES`.
8
+ */
9
+ export declare const supportsMultilineName: (nodeType?: string) => boolean;
10
+ /**
11
+ * Does this node render a user-editable text label at all? False for
12
+ * pure-symbol nodes so the rename popover hides its input for them.
13
+ */
14
+ export declare const rendersNameLabel: (nodeType?: string) => boolean;
5
15
  export declare const isParentNodeType: (nodeType?: string) => boolean;
@@ -0,0 +1,86 @@
1
+ /** Compact spec for a text style. Converted into a canvas `font` shorthand. */
2
+ export type SvgFontSpec = {
3
+ fontSize: number;
4
+ fontWeight?: string | number;
5
+ fontFamily?: string;
6
+ fontStyle?: string;
7
+ };
8
+ export declare const toCanvasFont: (spec: SvgFontSpec) => string;
9
+ /** How to treat whitespace during layout. Mirrors a CSS `white-space` subset. */
10
+ export type WhiteSpaceMode = "normal" | "pre-wrap";
11
+ /**
12
+ * Invalidate every cached preparation. Call this when the set of available
13
+ * fonts changes (e.g. a webfont just finished loading) so that subsequent
14
+ * measurements use the real font metrics rather than the fallback's.
15
+ */
16
+ export declare const clearPrepareCache: () => void;
17
+ /**
18
+ * Convenience: the most lines of `lineHeight` that can stack vertically in
19
+ * `availableHeight` pixels. Always returns at least 1. Useful for SVG nodes
20
+ * whose inner drawable area has a known vertical budget (rectangle height
21
+ * minus top/bottom padding, for instance) and who want to cap
22
+ * `MultilineText`'s `maxLines` so wrapping never overflows the shape.
23
+ */
24
+ export declare const maxLinesForHeight: (availableHeight: number, lineHeight: number) => number;
25
+ export type WrappedText = {
26
+ lines: string[];
27
+ /**
28
+ * Measured width of the widest laid-out line, in CSS px. Does not account
29
+ * for any ellipsis a caller may append after truncation.
30
+ */
31
+ maxLineWidth: number;
32
+ /** Whether every grapheme of the input was consumed by the layout. */
33
+ overflow: boolean;
34
+ };
35
+ /**
36
+ * Wrap text inside a rectangle of `maxWidth`. Word-break is CSS `normal`.
37
+ * When `whiteSpace` is `"pre-wrap"` (the default), literal `\n` characters in
38
+ * the input become hard line breaks; when it is `"normal"` whitespace runs
39
+ * collapse the way CSS normally collapses them.
40
+ *
41
+ * Returns the resulting lines, the widest line's measured width, and whether
42
+ * any content was dropped (i.e. could not fit within `maxLines`).
43
+ */
44
+ export declare const wrapTextInRect: (text: string, maxWidth: number, font: SvgFontSpec | string, options?: {
45
+ maxLines?: number;
46
+ lineHeight?: number;
47
+ whiteSpace?: WhiteSpaceMode;
48
+ }) => WrappedText;
49
+ export type ShapeLayout = {
50
+ /** Materialized lines (from pretext). `text` already includes any ellipsis. */
51
+ lines: {
52
+ text: string;
53
+ width: number;
54
+ }[];
55
+ lineHeight: number;
56
+ /** Signed y-offsets (relative to shape center) of each line's visual center. */
57
+ lineOffsets: number[];
58
+ /** Total vertical span of the laid-out block. */
59
+ blockHeight: number;
60
+ /** True when input text exceeded the shape's capacity and was truncated. */
61
+ overflow: boolean;
62
+ };
63
+ type ShapeOptions = {
64
+ paddingX?: number;
65
+ paddingY?: number;
66
+ /** Cap on how many lines we will try to fit. Defaults to a generous value. */
67
+ maxLines?: number;
68
+ /** Same semantics as `wrapTextInRect`'s `whiteSpace`. */
69
+ whiteSpace?: WhiteSpaceMode;
70
+ };
71
+ /**
72
+ * Lay out `text` inside an ellipse of size `width`×`height`, respecting the
73
+ * ellipse's curvature so lines near the top and bottom get a narrower max
74
+ * width than lines near the middle. When the text does not fit, the final
75
+ * visible line is suffixed with a horizontal ellipsis.
76
+ */
77
+ export declare const layoutTextInEllipse: (text: string, width: number, height: number, font: SvgFontSpec | string, lineHeight: number, options?: ShapeOptions) => ShapeLayout;
78
+ /**
79
+ * Lay out `text` inside a rhombus / diamond of size `width`×`height`. The
80
+ * inner width varies linearly with the distance from the vertical center
81
+ * (full width at the middle, zero at the top and bottom vertices). Lines
82
+ * near the vertices therefore get narrower line boxes, and overflow is
83
+ * marked with a horizontal ellipsis on the last visible line.
84
+ */
85
+ export declare const layoutTextInDiamond: (text: string, width: number, height: number, font: SvgFontSpec | string, lineHeight: number, options?: ShapeOptions) => ShapeLayout;
86
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tumaet/apollon",
3
- "version": "4.2.22",
3
+ "version": "4.3.0",
4
4
  "description": "An embeddable UML modeling editor for React. 13 diagram types, SVG/PNG/PDF/JSON export, optional real-time collaboration via Yjs.",
5
5
  "keywords": [
6
6
  "apollon",
@@ -62,6 +62,7 @@
62
62
  "test:coverage": "vitest run --coverage"
63
63
  },
64
64
  "dependencies": {
65
+ "@chenglou/pretext": "0.0.6",
65
66
  "@emotion/react": "11.11.1",
66
67
  "@emotion/styled": "11.11.0",
67
68
  "@mui/material": "6.4.2",
@@ -74,20 +75,20 @@
74
75
  },
75
76
  "devDependencies": {
76
77
  "@eslint/js": "9.17.0",
77
- "@testing-library/jest-dom": "^6.9.1",
78
- "@testing-library/react": "^16.3.2",
79
- "@testing-library/user-event": "^14.6.1",
78
+ "@testing-library/jest-dom": "6.9.1",
79
+ "@testing-library/react": "16.3.2",
80
+ "@testing-library/user-event": "14.6.1",
80
81
  "@types/node": "22.13.8",
81
82
  "@vitejs/plugin-react": "4.3.4",
82
- "@vitest/coverage-v8": "^4.0.18",
83
+ "@vitest/coverage-v8": "4.1.2",
83
84
  "eslint": "9.17.0",
84
85
  "eslint-plugin-react": "7.37.2",
85
86
  "globals": "15.13.0",
86
- "jsdom": "^28.1.0",
87
+ "jsdom": "28.1.0",
87
88
  "typescript": "5.7.2",
88
89
  "typescript-eslint": "8.18.1",
89
- "vite": "6.4.1",
90
+ "vite": "6.4.2",
90
91
  "vite-plugin-dts": "4.3.0",
91
- "vitest": "^4.0.18"
92
+ "vitest": "4.1.2"
92
93
  }
93
94
  }
@@ -1,8 +0,0 @@
1
- import { SVG, UMLModel } from '../typings';
2
- export type FlatSvgExportOptions = {
3
- margin?: number;
4
- background?: "white" | "transparent";
5
- fontFamily?: string;
6
- fontSize?: number;
7
- };
8
- export declare const exportClassDiagramAsFlatSvg: (model: UMLModel, options?: FlatSvgExportOptions) => SVG;