@fulate/ui 1.0.11 → 1.0.13

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.
@@ -10,14 +10,14 @@ export declare function disconnectEndpoint(root: Root, source: EndpointRef, opti
10
10
  /** @internal 从调用时 live scene 按当前连接规则生成一次 owner change。 */
11
11
  export declare function resolveConnectEndpointChange(root: Root, source: EndpointRef, target: ConnectionPortRef): OwnerPropertyChange | null;
12
12
  /** @internal 按 source owner 合并多个 detach 为每 owner 一个 final change。 */
13
- export declare function resolveDisconnectEndpointChanges(root: Root, sources: readonly EndpointRef[], localPositions?: ReadonlyMap<AnchorId, PointView>): OwnerPropertyChange[];
13
+ export declare function resolveDisconnectEndpointChanges(root: Root, sources: readonly EndpointRef[], localPositions?: ReadonlyMap<AnchorId, PointView>, patches?: ReadonlyMap<Element, Readonly<Record<string, unknown>>>): OwnerPropertyChange[];
14
14
  /** 当前 Port direction 是否接受 source endpoint 的连接角色。 */
15
15
  export declare function connectionPortAccepts(definition: ConnectionPortDefinition | undefined, role: EndpointConnectionRole): boolean;
16
16
  /** 当前 Port 对指定 source endpoint 是否仍有容量。 */
17
17
  export declare function hasConnectionPortCapacity(root: Root, target: ConnectionPortRef, definition: ConnectionPortDefinition | undefined, role: EndpointConnectionRole, excludeSource?: EndpointRef): boolean;
18
18
  /**
19
19
  * @internal 为一组 configured owner changes 补齐被删除 Port 的 source-owned
20
- * detach changes;无 detach 时借用原 changes 引用。
20
+ * detach changes。依赖解释使用同 owner 的最终 patch;无 ports 字段时借用原 changes
21
21
  */
22
22
  export declare function resolveConnectionPortChanges(root: Root, changes: readonly OwnerPropertyChange[]): readonly OwnerPropertyChange[];
23
23
  /**
@@ -29,7 +29,7 @@ export interface EndpointRelationSource {
29
29
  getEndpointRelation(anchorId: AnchorId): EndpointRelation | undefined;
30
30
  getEndpointRelationBindings(): readonly EndpointRelationBinding[];
31
31
  resolveConnectEndpointChange(anchorId: AnchorId, target: ConnectionPortRef): OwnerPropertyChange | null;
32
- resolveDisconnectEndpointChange(anchorIds: readonly AnchorId[], localPositions?: ReadonlyMap<AnchorId, PointView>): OwnerPropertyChange | null;
32
+ resolveDisconnectEndpointChange(anchorIds: readonly AnchorId[], localPositions?: ReadonlyMap<AnchorId, PointView>, patch?: Readonly<Record<string, unknown>>): OwnerPropertyChange | null;
33
33
  }
34
34
  /** 判断 Element 是否直接拥有 endpoint relation 语义。 */
35
35
  export declare function isEndpointRelationSource(element: Element): element is Element & EndpointRelationSource;
@@ -1,8 +1,8 @@
1
1
  import { Element, type ElementOptionPatch, type ElementTransformBaseline, type OwnerPropertyChange, type ReparentTransformIntent, type TransformIntent } from "@fulate/core";
2
- import { type Rect } from "@fulate/share";
2
+ import type { Rect } from "@fulate/share";
3
3
  /** @internal frame-authored provider 共用的纯 resolver。 */
4
4
  export declare function resolveFrameTransformChange(owner: Element, baseline: Readonly<ElementTransformBaseline>, intent: Readonly<TransformIntent | ReparentTransformIntent>): readonly OwnerPropertyChange[];
5
5
  /** @internal 捕获一次 owner reference;structured ownedState 直接保留原引用。 */
6
6
  export declare function captureElementTransformBaseline<Owned>(owner: Element, writeMode: "frame-authored" | "authored-geometry" | "pose-authored", ownedState: Owned): ElementTransformBaseline<Owned>;
7
7
  /** @internal Geometry reference 变化时保持当前 world pose 的最小 frame patch。 */
8
- export declare function resolveReferenceBoundsPosePatch(owner: Element, referenceBounds: Readonly<Rect>): Readonly<ElementOptionPatch>;
8
+ export declare function resolveReferenceBoundsPosePatch(owner: Element, readBounds: () => readonly [Readonly<Rect>, Readonly<Rect>], patch?: Readonly<ElementOptionPatch>): Readonly<ElementOptionPatch>;
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { clearCache, layoutNextLineRange, layoutWithLines, materializeLineRange, measureLineStats, measureNaturalWidth, prepareWithSegments } from "@chenglou/pretext";
2
- import { ChangeImpact, CustomEvent, Element, ElementProperties, ElementTransform, SKIN_DEFAULTS, Shape, ShapeProperties, ShapeTransform, createAuthoredLineSubpath, createGeometrySnapshot, createLineSubpath, createPathLocalId, evaluatePathSegmentPoint, getElementConnectionPort, getElementConnectionPortPoint, isReparentTransformIntent, resolveAuthoredGeometryFramePatch, resolveAuthoredSubpath, resolveReferenceTransform, resolveReferenceTransformTarget, splitPathSegment } from "@fulate/core";
2
+ import { ChangeImpact, CustomEvent, Element, ElementProperties, ElementTransform, SKIN_DEFAULTS, Shape, ShapeProperties, ShapeTransform, createAuthoredLineSubpath, createGeometrySnapshot, createLineSubpath, createPathLocalId, evaluatePathSegmentPoint, getElementConnectionPort, getElementConnectionPortPoint, isReparentTransformIntent, mergeOwnerPropertyChanges, resolveAuthoredGeometryFramePatch, resolveAuthoredSubpath, resolveReferenceBoundsTranslation, resolveReferenceTransform, resolveReferenceTransformTarget, splitPathSegment } from "@fulate/core";
3
3
  import { clamp, has, isFunction, isNil, isNull, isString, isUndefined, last } from "lodash-es";
4
4
  import { Bound, Intersection, createCanvasGradient, createPointView, isGradient, isIdentityMatrix, isPointWithinDistance, rectEdgePosition, transformPoint } from "@fulate/share";
5
5
  import { computePosition as computePosition$1, flip, offset, shift } from "@floating-ui/core";
@@ -1551,7 +1551,7 @@ function resolveConnectEndpointChange(root, source, target) {
1551
1551
  return currentPort && connectionPortAccepts(currentPort.definition, role) && hasConnectionPortCapacity(root, target, currentPort.definition, role, source) ? change : null;
1552
1552
  }
1553
1553
  /** @internal 按 source owner 合并多个 detach 为每 owner 一个 final change。 */
1554
- function resolveDisconnectEndpointChanges(root, sources, localPositions) {
1554
+ function resolveDisconnectEndpointChanges(root, sources, localPositions, patches) {
1555
1555
  const anchorsByOwner = /* @__PURE__ */ new Map();
1556
1556
  for (const source of sources) {
1557
1557
  const owner = root.getByKey(source.elementKey);
@@ -1564,7 +1564,7 @@ function resolveDisconnectEndpointChanges(root, sources, localPositions) {
1564
1564
  for (const [owner, anchors] of anchorsByOwner) {
1565
1565
  const parent = owner.parent;
1566
1566
  const relations = anchors.map((anchorId) => owner.getEndpointRelation(anchorId));
1567
- const change = owner.resolveDisconnectEndpointChange(anchors, localPositions);
1567
+ const change = owner.resolveDisconnectEndpointChange(anchors, localPositions, patches?.get(owner));
1568
1568
  if (change && root.getByKey(owner.key) === owner && owner.parent === parent && isEndpointRelationSource(owner) && anchors.every((anchorId, index) => owner.getEndpointRelation(anchorId) === relations[index])) changes.push(change);
1569
1569
  }
1570
1570
  return changes;
@@ -1583,18 +1583,21 @@ function resolveConfiguredPorts(value) {
1583
1583
  }
1584
1584
  /**
1585
1585
  * @internal 为一组 configured owner changes 补齐被删除 Port 的 source-owned
1586
- * detach changes;无 detach 时借用原 changes 引用。
1586
+ * detach changes。依赖解释使用同 owner 的最终 patch;无 ports 字段时借用原 changes
1587
1587
  */
1588
1588
  function resolveConnectionPortChanges(root, changes) {
1589
+ if (!changes.some(({ patch }) => has(patch, "ports"))) return changes;
1590
+ const patches = mergeOwnerPropertyChanges(changes);
1589
1591
  let sources;
1590
1592
  let index;
1591
- for (const { owner, patch } of changes) {
1593
+ for (const [owner, patch] of patches) {
1592
1594
  if (owner.activeRoot !== root || !has(patch, "ports")) continue;
1595
+ const previous = Element.getConfiguredPropertyValue(owner, "ports");
1593
1596
  const next = resolveConfiguredPorts(patch.ports);
1594
- if (Object.is(owner.ports, next)) continue;
1595
- const nextIds = new Set(next === false ? [] : next === true ? owner.resolveConnectionPorts(true).map(({ ref }) => ref.portId) : next.map(({ id }) => id));
1597
+ if (Object.is(previous, next)) continue;
1598
+ const nextIds = new Set(owner.resolveConnectionPortIds(next, patch));
1596
1599
  index ??= getEndpointRelationIndex(root);
1597
- const previousPortIds = owner.ports === false ? [] : owner.ports === true ? owner.resolveConnectionPorts(true).map(({ ref }) => ref.portId) : owner.ports.map(({ id }) => id);
1600
+ const previousPortIds = owner.resolveConnectionPortIds(previous);
1598
1601
  for (const portId of previousPortIds) {
1599
1602
  if (nextIds.has(portId)) continue;
1600
1603
  const targeting = index.relationsTargetingPort({
@@ -1604,7 +1607,12 @@ function resolveConnectionPortChanges(root, changes) {
1604
1607
  if (targeting.length > 0) (sources ??= []).push(...targeting);
1605
1608
  }
1606
1609
  }
1607
- return sources ? [...resolveDisconnectEndpointChanges(root, sources), ...changes] : changes;
1610
+ if (sources) for (const { owner, patch } of resolveDisconnectEndpointChanges(root, sources, void 0, patches)) patches.set(owner, patch);
1611
+ else if (patches.size === changes.length) return changes;
1612
+ return Array.from(patches, ([owner, patch]) => ({
1613
+ owner,
1614
+ patch
1615
+ }));
1608
1616
  }
1609
1617
  /**
1610
1618
  * @internal active Port owner 的 whole-value patch 与被删除 Port 的 detach
@@ -1613,7 +1621,7 @@ function resolveConnectionPortChanges(root, changes) {
1613
1621
  function commitConnectionPortPatch(owner, patch) {
1614
1622
  const root = owner.activeRoot;
1615
1623
  if (!root || !has(patch, "ports")) return false;
1616
- if (Object.is(owner.ports, resolveConfiguredPorts(patch.ports))) return false;
1624
+ if (Object.is(Element.getConfiguredPropertyValue(owner, "ports"), resolveConfiguredPorts(patch.ports))) return false;
1617
1625
  root.applyChangeBatch(resolveConnectionPortChanges(root, [{
1618
1626
  owner,
1619
1627
  patch
@@ -1712,20 +1720,22 @@ function captureElementTransformBaseline(owner, writeMode, ownedState) {
1712
1720
  return readElementTransformBaseline(owner, writeMode, ownedState);
1713
1721
  }
1714
1722
  /** @internal Geometry reference 变化时保持当前 world pose 的最小 frame patch。 */
1715
- function resolveReferenceBoundsPosePatch(owner, referenceBounds) {
1716
- const reference = readElementTransformBaseline(owner, "pose-authored", void 0).reference;
1717
- const center = {
1718
- x: referenceBounds.left + referenceBounds.width / 2,
1719
- y: referenceBounds.top + referenceBounds.height / 2
1720
- };
1721
- return resolveReferenceTransformTarget({
1722
- ...reference,
1723
- referenceBounds
1724
- }, {
1725
- worldMatrix: reference.startWorldMatrix,
1726
- worldCenter: transformPoint(reference.startWorldMatrix, center),
1727
- parentInverseMatrix: reference.startParentWorldInverseMatrix
1728
- }).framePatch;
1723
+ function resolveReferenceBoundsPosePatch(owner, readBounds, patch) {
1724
+ const frame = patch ? {
1725
+ left: owner.left,
1726
+ top: owner.top,
1727
+ angle: owner.angle,
1728
+ scaleX: owner.scaleX,
1729
+ scaleY: owner.scaleY,
1730
+ skewX: owner.skewX,
1731
+ skewY: owner.skewY,
1732
+ originX: owner.originX,
1733
+ originY: owner.originY,
1734
+ ...patch
1735
+ } : owner;
1736
+ if ((frame.angle ?? 0) === 0 && (frame.skewX ?? 0) === 0 && (frame.skewY ?? 0) === 0 && (frame.scaleX ?? 1) === 1 && (frame.scaleY ?? 1) === 1) return {};
1737
+ const [before, after] = readBounds();
1738
+ return resolveReferenceBoundsTranslation(frame, before, after);
1729
1739
  }
1730
1740
  //#endregion
1731
1741
  //#region packages/ui/src/editor/shape.ts
@@ -2650,18 +2660,20 @@ var Polygon = class extends EditorShape {
2650
2660
  end: createPointView(b.x, b.y)
2651
2661
  };
2652
2662
  }
2653
- getDefaultConnectionPortSchema() {
2654
- return this.contour.anchors.map((_, index) => {
2655
- const next = (index + 1) % this.contour.anchors.length;
2656
- return {
2657
- id: `edge_${index}`,
2658
- localPosition: () => {
2659
- const a = this.contour.anchors[index].point;
2660
- const b = this.contour.anchors[next].point;
2661
- return createPointView((a.x + b.x) / 2, (a.y + b.y) / 2);
2662
- }
2663
- };
2664
- });
2663
+ getDefaultConnectionPortSchema(patch) {
2664
+ const contour = patch && has(patch, "contour") ? patch.contour : this.contour;
2665
+ const points = Array.isArray(contour) ? contour : void 0;
2666
+ const anchors = points ? void 0 : contour?.anchors;
2667
+ const count = points?.length ?? anchors?.length ?? 0;
2668
+ return Array.from({ length: count }, (_, index) => ({
2669
+ id: "edge_" + index,
2670
+ localPosition: () => {
2671
+ const next = (index + 1) % count;
2672
+ const a = points ? points[index] : anchors[index].point;
2673
+ const b = points ? points[next] : anchors[next].point;
2674
+ return createPointView((a.x + b.x) / 2, (a.y + b.y) / 2);
2675
+ }
2676
+ }));
2665
2677
  }
2666
2678
  };
2667
2679
  //#endregion
@@ -4891,7 +4903,7 @@ var BaseLine = class extends Element {
4891
4903
  if (this.angle === 0 && this.scaleX === 1 && this.scaleY === 1 && this.skewX === 0 && this.skewY === 0) return { path };
4892
4904
  const bounds = getAuthoredSubpathBounds(path);
4893
4905
  return bounds ? {
4894
- ...resolveReferenceBoundsPosePatch(this, bounds),
4906
+ ...resolveReferenceBoundsPosePatch(this, () => [this.transform.getLayoutReferenceBoundsView(), bounds]),
4895
4907
  path
4896
4908
  } : { path };
4897
4909
  }
@@ -4996,25 +5008,35 @@ var BaseLine = class extends Element {
4996
5008
  patch: { relations: head ? { head: relation } : { tail: relation } }
4997
5009
  };
4998
5010
  }
4999
- resolveDisconnectEndpointChange(anchorIds, localPositions) {
5011
+ resolveDisconnectEndpointChange(anchorIds, localPositions, patch) {
5012
+ const finalPatch = patch ? resolveLinePatch(patch) : void 0;
5013
+ const beforePath = finalPatch && has(finalPatch, "path") ? finalPatch.path ?? createEmptyLinePath() : this.path;
5014
+ const head = beforePath.anchors[0];
5015
+ const tail = last(beforePath.anchors);
5016
+ const headRelation = finalPatch && has(finalPatch, "headRelation") ? finalPatch.headRelation : this.headRelation;
5017
+ const tailRelation = finalPatch && has(finalPatch, "tailRelation") ? finalPatch.tailRelation : this.tailRelation;
5000
5018
  const removing = new Set(anchorIds);
5001
- const head = this.headAnchor;
5002
- const tail = this.tailAnchor;
5003
- const detachHead = !!head && !!this.headRelation && removing.has(head.anchorId);
5004
- const detachTail = !!tail && !!this.tailRelation && removing.has(tail.anchorId);
5005
- if (!detachHead && !detachTail) return null;
5019
+ const detachHead = !!head && !!headRelation && removing.has(head.anchorId) && sameEndpointRelation(headRelation, this.getEndpointRelation(head.anchorId));
5020
+ const detachTail = !!tail && !!tailRelation && removing.has(tail.anchorId) && sameEndpointRelation(tailRelation, this.getEndpointRelation(tail.anchorId));
5021
+ if (!detachHead && !detachTail) return finalPatch ? {
5022
+ owner: this,
5023
+ patch: finalPatch
5024
+ } : null;
5006
5025
  const vertices = this.getEffectiveLineVerticesView();
5007
- let path = this.path;
5026
+ let path = beforePath;
5008
5027
  if (detachHead) path = replaceAuthoredAnchorPoint(path, head.anchorId, localPositions?.get(head.anchorId) ?? vertices[0].point);
5009
5028
  if (detachTail && tail.anchorId !== head?.anchorId) path = replaceAuthoredAnchorPoint(path, tail.anchorId, localPositions?.get(tail.anchorId) ?? last(vertices).point);
5029
+ const geometryPatch = finalPatch ? {
5030
+ ...resolveReferenceBoundsPosePatch(this, () => [getAuthoredSubpathBounds(beforePath), getAuthoredSubpathBounds(path)], finalPatch),
5031
+ path
5032
+ } : this.resolvePointPathPatch(path);
5010
5033
  return {
5011
5034
  owner: this,
5012
5035
  patch: {
5013
- ...this.resolvePointPathPatch(path),
5014
- relations: {
5015
- ...detachHead ? { head: void 0 } : {},
5016
- ...detachTail ? { tail: void 0 } : {}
5017
- }
5036
+ ...finalPatch,
5037
+ ...geometryPatch,
5038
+ ...detachHead ? { headRelation: void 0 } : {},
5039
+ ...detachTail ? { tailRelation: void 0 } : {}
5018
5040
  }
5019
5041
  };
5020
5042
  }
@@ -5946,15 +5968,37 @@ var LineTree = class extends Element {
5946
5968
  patch: { topology: moveLineTreePoint(this.topology, anchorId, point.point, relation) }
5947
5969
  };
5948
5970
  }
5949
- resolveDisconnectEndpointChange(anchorIds, localPositions) {
5950
- let topology = this.topology;
5951
- for (const anchorId of new Set(anchorIds)) {
5952
- if (!this.transform.getPoint(anchorId)?.relation) continue;
5953
- topology = moveLineTreePoint(topology, anchorId, localPositions?.get(anchorId) ?? this.transform.getResolvedPoint(anchorId).point, void 0);
5971
+ resolveDisconnectEndpointChange(anchorIds, localPositions, patch) {
5972
+ const finalPatch = patch ? prepareTopologyOption(patch) : void 0;
5973
+ const beforeTopology = finalPatch && has(finalPatch, "topology") ? finalPatch.topology : this.topology;
5974
+ const removing = new Set(anchorIds);
5975
+ let points;
5976
+ for (let index = 0; index < beforeTopology.points.length; index++) {
5977
+ const point = beforeTopology.points[index];
5978
+ const { anchorId } = point;
5979
+ if (!removing.has(anchorId)) continue;
5980
+ if (!point.relation || !sameEndpointRelation(point.relation, this.getEndpointRelation(anchorId))) continue;
5981
+ points ??= [...beforeTopology.points];
5982
+ points[index] = {
5983
+ anchorId,
5984
+ point: localPositions?.get(anchorId) ?? this.transform.getResolvedPoint(anchorId).point
5985
+ };
5954
5986
  }
5955
- return topology === this.topology ? null : {
5987
+ if (!points) return finalPatch ? {
5956
5988
  owner: this,
5957
- patch: { topology }
5989
+ patch: finalPatch
5990
+ } : null;
5991
+ const topology = {
5992
+ ...beforeTopology,
5993
+ points
5994
+ };
5995
+ return {
5996
+ owner: this,
5997
+ patch: {
5998
+ ...finalPatch,
5999
+ ...resolveReferenceBoundsPosePatch(this, () => [getLineTreeTopologyBounds(beforeTopology), getLineTreeTopologyBounds(topology)], finalPatch),
6000
+ topology
6001
+ }
5958
6002
  };
5959
6003
  }
5960
6004
  resolvePointScope(point, current) {
@@ -120,7 +120,7 @@ export declare abstract class BaseLine extends Element<LineProperties, LineOptio
120
120
  getSegmentIndex(segmentId: SegmentId): number;
121
121
  getEndpointRelation(anchorId: AnchorId): EndpointRelation;
122
122
  resolveConnectEndpointChange(anchorId: AnchorId, target: ConnectionPortRef): OwnerPropertyChange | null;
123
- resolveDisconnectEndpointChange(anchorIds: readonly AnchorId[], localPositions?: ReadonlyMap<AnchorId, PointView>): OwnerPropertyChange | null;
123
+ resolveDisconnectEndpointChange(anchorIds: readonly AnchorId[], localPositions?: ReadonlyMap<AnchorId, PointView>, patch?: Readonly<Record<string, unknown>>): OwnerPropertyChange | null;
124
124
  worldToLocal(x: number, y: number): PointView | null;
125
125
  /** 返回可独立修改的 world point 结果。 */
126
126
  getWorldLinePoints(): PointView[];
@@ -50,7 +50,7 @@ export declare class LineTree extends Element<LineTreeProperties, LineTreeOption
50
50
  getEndpointRole(anchorId: AnchorId): "input" | "output";
51
51
  getEndpointRelation(anchorId: AnchorId): EndpointRelation;
52
52
  resolveConnectEndpointChange(anchorId: AnchorId, target: ConnectionPortRef): OwnerPropertyChange | null;
53
- resolveDisconnectEndpointChange(anchorIds: readonly AnchorId[], localPositions?: ReadonlyMap<AnchorId, PointView>): OwnerPropertyChange | null;
53
+ resolveDisconnectEndpointChange(anchorIds: readonly AnchorId[], localPositions?: ReadonlyMap<AnchorId, PointView>, patch?: Readonly<Record<string, unknown>>): OwnerPropertyChange | null;
54
54
  resolvePointScope(point: PointView, current?: PointEditingScope): PointEditingScope | null;
55
55
  hasPointScope(scope: PointEditingScope): boolean;
56
56
  getPointControls(scope: PointEditingScope): import("..").PointControlDescriptor[];
package/dist/polygon.d.ts CHANGED
@@ -44,7 +44,7 @@ export declare class Polygon extends EditorShape<PolygonProperties, PolygonOptio
44
44
  start: PointView;
45
45
  end: PointView;
46
46
  };
47
- protected getDefaultConnectionPortSchema(): {
47
+ protected getDefaultConnectionPortSchema(patch?: Readonly<Record<string, unknown>>): {
48
48
  id: string;
49
49
  localPosition: () => PointView;
50
50
  }[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fulate/ui",
3
- "version": "1.0.11",
3
+ "version": "1.0.13",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",