@fieldnotes/core 0.33.0 → 0.34.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.
- package/README.md +16 -0
- package/dist/index.cjs +84 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +84 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -588,6 +588,22 @@ viewport.setSmartGuides(true); // enable
|
|
|
588
588
|
viewport.setSmartGuides(false); // disable (default)
|
|
589
589
|
```
|
|
590
590
|
|
|
591
|
+
## Grouping
|
|
592
|
+
|
|
593
|
+
Group elements so they select, move, delete, z-order, and align as a single unit.
|
|
594
|
+
|
|
595
|
+
- **`viewport.groupSelection()`** — groups the current selection under a new id.
|
|
596
|
+
- **`viewport.ungroupSelection()`** — dissolves any groups in the current selection.
|
|
597
|
+
|
|
598
|
+
Each is one undo step. Selecting any member selects its whole group, so to edit a single member individually, ungroup first. Pasting or duplicating a group keeps the copies grouped under a fresh id.
|
|
599
|
+
|
|
600
|
+
```typescript
|
|
601
|
+
viewport.groupSelection(); // Ctrl/Cmd+G
|
|
602
|
+
viewport.ungroupSelection(); // Ctrl/Cmd+Shift+G
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
The shortcuts are rebindable as `group` and `ungroup`.
|
|
606
|
+
|
|
591
607
|
## Built-in Interactions
|
|
592
608
|
|
|
593
609
|
| Input | Action |
|
package/dist/index.cjs
CHANGED
|
@@ -1021,6 +1021,14 @@ var KeyboardActions = class {
|
|
|
1021
1021
|
if (this.deps.isToolActive()) return;
|
|
1022
1022
|
this.deps.fitToContent?.();
|
|
1023
1023
|
}
|
|
1024
|
+
group() {
|
|
1025
|
+
if (this.deps.isToolActive()) return;
|
|
1026
|
+
this.deps.group?.();
|
|
1027
|
+
}
|
|
1028
|
+
ungroup() {
|
|
1029
|
+
if (this.deps.isToolActive()) return;
|
|
1030
|
+
this.deps.ungroup?.();
|
|
1031
|
+
}
|
|
1024
1032
|
zOrder(operation) {
|
|
1025
1033
|
if (this.deps.isToolActive()) return;
|
|
1026
1034
|
this.flushPendingNudge();
|
|
@@ -1054,6 +1062,10 @@ var KeyboardActions = class {
|
|
|
1054
1062
|
for (const el of source) {
|
|
1055
1063
|
idMap.set(el.id, createId(el.type));
|
|
1056
1064
|
}
|
|
1065
|
+
const groupIdMap = /* @__PURE__ */ new Map();
|
|
1066
|
+
for (const el of source) {
|
|
1067
|
+
if (el.groupId && !groupIdMap.has(el.groupId)) groupIdMap.set(el.groupId, createId("group"));
|
|
1068
|
+
}
|
|
1057
1069
|
const newIds = [];
|
|
1058
1070
|
const recorder = this.deps.getHistoryRecorder();
|
|
1059
1071
|
recorder?.begin();
|
|
@@ -1062,6 +1074,7 @@ var KeyboardActions = class {
|
|
|
1062
1074
|
const newId = idMap.get(el.id);
|
|
1063
1075
|
if (!newId) continue;
|
|
1064
1076
|
clone.id = newId;
|
|
1077
|
+
if (clone.groupId) clone.groupId = groupIdMap.get(clone.groupId) ?? clone.groupId;
|
|
1065
1078
|
clone.position = { x: clone.position.x + offset.x, y: clone.position.y + offset.y };
|
|
1066
1079
|
if (clone.type === "arrow") {
|
|
1067
1080
|
const arrow = clone;
|
|
@@ -1115,6 +1128,8 @@ var DEFAULT_BINDINGS = [
|
|
|
1115
1128
|
["zoom-in", ["mod+="]],
|
|
1116
1129
|
["zoom-out", ["mod+-"]],
|
|
1117
1130
|
["zoom-reset", ["mod+0"]],
|
|
1131
|
+
["group", ["mod+g"]],
|
|
1132
|
+
["ungroup", ["mod+shift+g"]],
|
|
1118
1133
|
["nudge-left", ["arrowleft"]],
|
|
1119
1134
|
["nudge-right", ["arrowright"]],
|
|
1120
1135
|
["nudge-up", ["arrowup"]],
|
|
@@ -1274,6 +1289,8 @@ var InputHandler = class {
|
|
|
1274
1289
|
getHistoryStack: () => this.historyStack,
|
|
1275
1290
|
isToolActive: () => this.isToolActive,
|
|
1276
1291
|
fitToContent: options.fitToContent,
|
|
1292
|
+
group: options.group,
|
|
1293
|
+
ungroup: options.ungroup,
|
|
1277
1294
|
getLastPointerWorld: () => this.lastPointerWorld()
|
|
1278
1295
|
});
|
|
1279
1296
|
this.shortcutMap = new ShortcutMap(options.shortcuts?.bindings);
|
|
@@ -1513,6 +1530,14 @@ var InputHandler = class {
|
|
|
1513
1530
|
e.preventDefault();
|
|
1514
1531
|
this.actions.zoomToFit();
|
|
1515
1532
|
return;
|
|
1533
|
+
case "group":
|
|
1534
|
+
e.preventDefault();
|
|
1535
|
+
this.actions.group();
|
|
1536
|
+
return;
|
|
1537
|
+
case "ungroup":
|
|
1538
|
+
e.preventDefault();
|
|
1539
|
+
this.actions.ungroup();
|
|
1540
|
+
return;
|
|
1516
1541
|
case "zoom-in":
|
|
1517
1542
|
e.preventDefault();
|
|
1518
1543
|
this.zoomByFactor(ZOOM_STEP);
|
|
@@ -4018,12 +4043,19 @@ var UpdateElementCommand = class {
|
|
|
4018
4043
|
this.current = current;
|
|
4019
4044
|
}
|
|
4020
4045
|
execute(store) {
|
|
4021
|
-
store.update(this.id,
|
|
4046
|
+
store.update(this.id, diffPatch(this.previous, this.current));
|
|
4022
4047
|
}
|
|
4023
4048
|
undo(store) {
|
|
4024
|
-
store.update(this.id,
|
|
4049
|
+
store.update(this.id, diffPatch(this.current, this.previous));
|
|
4025
4050
|
}
|
|
4026
4051
|
};
|
|
4052
|
+
function diffPatch(from, to) {
|
|
4053
|
+
const patch = { ...to };
|
|
4054
|
+
for (const key of Object.keys(from)) {
|
|
4055
|
+
if (!(key in to)) patch[key] = void 0;
|
|
4056
|
+
}
|
|
4057
|
+
return patch;
|
|
4058
|
+
}
|
|
4027
4059
|
var BatchCommand = class {
|
|
4028
4060
|
commands;
|
|
4029
4061
|
constructor(commands) {
|
|
@@ -5586,6 +5618,8 @@ var Viewport = class {
|
|
|
5586
5618
|
historyRecorder: this.historyRecorder,
|
|
5587
5619
|
historyStack: this.history,
|
|
5588
5620
|
fitToContent: () => this.fitToContent(),
|
|
5621
|
+
group: () => this.groupSelection(),
|
|
5622
|
+
ungroup: () => this.ungroupSelection(),
|
|
5589
5623
|
shortcuts: options.shortcuts
|
|
5590
5624
|
});
|
|
5591
5625
|
this.domNodeManager = new DomNodeManager({
|
|
@@ -5923,6 +5957,26 @@ var Viewport = class {
|
|
|
5923
5957
|
}
|
|
5924
5958
|
this.historyRecorder.commit();
|
|
5925
5959
|
}
|
|
5960
|
+
groupSelection() {
|
|
5961
|
+
const ids = this.getSelectedIds();
|
|
5962
|
+
if (ids.length < 2) return;
|
|
5963
|
+
const groupId = createId("group");
|
|
5964
|
+
this.historyRecorder.begin();
|
|
5965
|
+
for (const id of ids) {
|
|
5966
|
+
if (this.store.getById(id)) this.store.update(id, { groupId });
|
|
5967
|
+
}
|
|
5968
|
+
this.historyRecorder.commit();
|
|
5969
|
+
}
|
|
5970
|
+
ungroupSelection() {
|
|
5971
|
+
const ids = this.getSelectedIds();
|
|
5972
|
+
if (ids.length === 0) return;
|
|
5973
|
+
this.historyRecorder.begin();
|
|
5974
|
+
for (const id of ids) {
|
|
5975
|
+
const el = this.store.getById(id);
|
|
5976
|
+
if (el && el.groupId !== void 0) this.store.update(id, { groupId: void 0 });
|
|
5977
|
+
}
|
|
5978
|
+
this.historyRecorder.commit();
|
|
5979
|
+
}
|
|
5926
5980
|
alignSelection(edge) {
|
|
5927
5981
|
const bounded = this.boundedSelection();
|
|
5928
5982
|
if (bounded.length < 2) return;
|
|
@@ -6632,6 +6686,26 @@ var EraserTool = class {
|
|
|
6632
6686
|
}
|
|
6633
6687
|
};
|
|
6634
6688
|
|
|
6689
|
+
// src/elements/group.ts
|
|
6690
|
+
function expandToGroups(ids, elements) {
|
|
6691
|
+
const byId = new Map(elements.map((e) => [e.id, e]));
|
|
6692
|
+
const groupIds = /* @__PURE__ */ new Set();
|
|
6693
|
+
for (const id of ids) {
|
|
6694
|
+
const g = byId.get(id)?.groupId;
|
|
6695
|
+
if (g) groupIds.add(g);
|
|
6696
|
+
}
|
|
6697
|
+
if (groupIds.size === 0) return ids;
|
|
6698
|
+
const idSet = new Set(ids);
|
|
6699
|
+
const result = [...ids];
|
|
6700
|
+
for (const el of elements) {
|
|
6701
|
+
if (el.groupId && groupIds.has(el.groupId) && !idSet.has(el.id)) {
|
|
6702
|
+
result.push(el.id);
|
|
6703
|
+
idSet.add(el.id);
|
|
6704
|
+
}
|
|
6705
|
+
}
|
|
6706
|
+
return result;
|
|
6707
|
+
}
|
|
6708
|
+
|
|
6635
6709
|
// src/tools/arrow-handles.ts
|
|
6636
6710
|
var BIND_THRESHOLD = 20;
|
|
6637
6711
|
var HANDLE_RADIUS = 5;
|
|
@@ -6897,18 +6971,20 @@ var SelectTool = class {
|
|
|
6897
6971
|
this.hasDragged = false;
|
|
6898
6972
|
const hit = this.hitTest(world, ctx);
|
|
6899
6973
|
if (hit) {
|
|
6974
|
+
const all = ctx.store.getAll();
|
|
6900
6975
|
const alreadySelected = this._selectedIds.includes(hit.id);
|
|
6901
6976
|
if (state.shiftKey) {
|
|
6902
6977
|
if (alreadySelected) {
|
|
6903
|
-
|
|
6978
|
+
const grp = new Set(expandToGroups([hit.id], all));
|
|
6979
|
+
this.setSelectedIds(this._selectedIds.filter((id) => !grp.has(id)));
|
|
6904
6980
|
this.mode = { type: "idle" };
|
|
6905
6981
|
} else {
|
|
6906
|
-
this.setSelectedIds([...this._selectedIds, hit.id]);
|
|
6982
|
+
this.setSelectedIds(expandToGroups([...this._selectedIds, hit.id], all));
|
|
6907
6983
|
this.mode = hit.locked ? { type: "idle" } : { type: "dragging" };
|
|
6908
6984
|
}
|
|
6909
6985
|
} else {
|
|
6910
6986
|
if (!alreadySelected) {
|
|
6911
|
-
this.setSelectedIds([hit.id]);
|
|
6987
|
+
this.setSelectedIds(expandToGroups([hit.id], all));
|
|
6912
6988
|
} else if (this._selectedIds.length > 1) {
|
|
6913
6989
|
this.pendingSingleSelectId = hit.id;
|
|
6914
6990
|
}
|
|
@@ -7022,12 +7098,12 @@ var SelectTool = class {
|
|
|
7022
7098
|
if (this.mode.type === "marquee") {
|
|
7023
7099
|
const rect = this.getMarqueeRect();
|
|
7024
7100
|
if (rect) {
|
|
7025
|
-
this.setSelectedIds(this.findElementsInRect(rect, ctx));
|
|
7101
|
+
this.setSelectedIds(expandToGroups(this.findElementsInRect(rect, ctx), ctx.store.getAll()));
|
|
7026
7102
|
}
|
|
7027
7103
|
ctx.requestRender();
|
|
7028
7104
|
}
|
|
7029
7105
|
if (!this.hasDragged && this.pendingSingleSelectId !== null) {
|
|
7030
|
-
this.setSelectedIds([this.pendingSingleSelectId]);
|
|
7106
|
+
this.setSelectedIds(expandToGroups([this.pendingSingleSelectId], ctx.store.getAll()));
|
|
7031
7107
|
}
|
|
7032
7108
|
this.pendingSingleSelectId = null;
|
|
7033
7109
|
this.hasDragged = false;
|
|
@@ -8312,7 +8388,7 @@ var TemplateTool = class {
|
|
|
8312
8388
|
};
|
|
8313
8389
|
|
|
8314
8390
|
// src/index.ts
|
|
8315
|
-
var VERSION = "0.
|
|
8391
|
+
var VERSION = "0.34.0";
|
|
8316
8392
|
// Annotate the CommonJS export names for ESM import in node:
|
|
8317
8393
|
0 && (module.exports = {
|
|
8318
8394
|
ArrowTool,
|