@fieldnotes/svelte 0.1.0 → 0.1.1
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.
|
@@ -9,12 +9,6 @@
|
|
|
9
9
|
size?: { w: number; h: number };
|
|
10
10
|
children: Snippet;
|
|
11
11
|
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Marks every store mutation this component makes as host-owned: external origin, so it
|
|
15
|
-
* neither creates an undo step nor is re-broadcast to the sync hub.
|
|
16
|
-
*/
|
|
17
|
-
const HOST_ORIGIN = 'host';
|
|
18
12
|
</script>
|
|
19
13
|
|
|
20
14
|
<!--
|
|
@@ -26,6 +20,8 @@
|
|
|
26
20
|
-->
|
|
27
21
|
<script lang="ts">
|
|
28
22
|
import { onMount } from 'svelte';
|
|
23
|
+
import { attachHostElement } from '@fieldnotes/core';
|
|
24
|
+
import type { HostElementHandle } from '@fieldnotes/core';
|
|
29
25
|
import { useViewport } from './context.js';
|
|
30
26
|
|
|
31
27
|
const { position, size, children }: CanvasElementProps = $props();
|
|
@@ -42,77 +38,18 @@
|
|
|
42
38
|
});
|
|
43
39
|
|
|
44
40
|
let container: HTMLDivElement;
|
|
45
|
-
let
|
|
41
|
+
let host: HostElementHandle | undefined;
|
|
46
42
|
|
|
47
43
|
onMount(() => {
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
viewport.domLayer.appendChild(container);
|
|
52
|
-
const { position: current, size: currentSize } = geometry();
|
|
53
|
-
return viewport.addHtmlElement(container, current, currentSize, {
|
|
54
|
-
transient: true,
|
|
55
|
-
origin: HOST_ORIGIN,
|
|
56
|
-
});
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
elementId = register();
|
|
60
|
-
|
|
61
|
-
// A store clear detaches every canvas-owned DOM node and forgets the content registered
|
|
62
|
-
// for it, so this component has to hand its container back afterwards. The decision is
|
|
63
|
-
// deferred to a microtask because a remote clear is followed — synchronously, by the sync
|
|
64
|
-
// client — by re-adding the transient elements: reading the store inside the event would
|
|
65
|
-
// always report this element gone and add a duplicate.
|
|
66
|
-
const handleStoreReplacement = () => {
|
|
67
|
-
const clearedId = elementId;
|
|
68
|
-
if (clearedId === null) return;
|
|
69
|
-
queueMicrotask(() => {
|
|
70
|
-
if (elementId !== clearedId) return;
|
|
71
|
-
if (viewport.store.getById(clearedId)?.type === 'html') {
|
|
72
|
-
// Survived the clear (the sync client re-adds transient elements after a remote
|
|
73
|
-
// clear): restore the container as that element's content instead of adding a second.
|
|
74
|
-
viewport.updateHtmlElement(clearedId, container);
|
|
75
|
-
} else {
|
|
76
|
-
elementId = register();
|
|
77
|
-
}
|
|
78
|
-
viewport.requestRender();
|
|
79
|
-
});
|
|
80
|
-
};
|
|
81
|
-
// `clear` covers a clear-canvas gesture and a remote clear. A wholesale state replacement
|
|
82
|
-
// (`loadState`/`loadJSON`) runs inside `suspendNotifications`, so the store coalesces its
|
|
83
|
-
// events into a single `batch` instead: the same recovery applies.
|
|
84
|
-
const unsubscribeClear = viewport.store.on('clear', handleStoreReplacement);
|
|
85
|
-
const unsubscribeBatch = viewport.store.on('batch', handleStoreReplacement);
|
|
86
|
-
|
|
87
|
-
return () => {
|
|
88
|
-
unsubscribeClear();
|
|
89
|
-
unsubscribeBatch();
|
|
90
|
-
if (elementId) {
|
|
91
|
-
viewport.store.remove(elementId, { origin: HOST_ORIGIN });
|
|
92
|
-
viewport.requestRender();
|
|
93
|
-
elementId = null;
|
|
94
|
-
}
|
|
95
|
-
// Removing the element usually detaches the container; never leave it orphaned in the layer.
|
|
96
|
-
if (container.parentNode === viewport.domLayer) container.remove();
|
|
97
|
-
};
|
|
44
|
+
const handle = attachHostElement(viewport, container, geometry());
|
|
45
|
+
host = handle;
|
|
46
|
+
return () => handle.destroy();
|
|
98
47
|
});
|
|
99
48
|
|
|
100
|
-
let registered = false;
|
|
101
49
|
$effect(() => {
|
|
50
|
+
// Read before the optional call so tracking never depends on `host`.
|
|
102
51
|
const next = geometry();
|
|
103
|
-
|
|
104
|
-
if (!registered) {
|
|
105
|
-
registered = true;
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
if (!elementId) return;
|
|
109
|
-
const { position: nextPosition, size: nextSize } = next;
|
|
110
|
-
viewport.store.update(
|
|
111
|
-
elementId,
|
|
112
|
-
{ position: nextPosition, ...(nextSize ? { size: nextSize } : {}) },
|
|
113
|
-
{ origin: HOST_ORIGIN },
|
|
114
|
-
);
|
|
115
|
-
viewport.requestRender();
|
|
52
|
+
host?.setGeometry(next);
|
|
116
53
|
});
|
|
117
54
|
</script>
|
|
118
55
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CanvasElement.svelte.d.ts","sourceRoot":"","sources":["../src/CanvasElement.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEtC,uCAAuC;AACvC,MAAM,WAAW,kBAAkB;IACjC,8DAA8D;IAC9D,QAAQ,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACnC,wEAAwE;IACxE,IAAI,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAChC,QAAQ,EAAE,OAAO,CAAC;CACnB;
|
|
1
|
+
{"version":3,"file":"CanvasElement.svelte.d.ts","sourceRoot":"","sources":["../src/CanvasElement.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEtC,uCAAuC;AACvC,MAAM,WAAW,kBAAkB;IACjC,8DAA8D;IAC9D,QAAQ,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACnC,wEAAwE;IACxE,IAAI,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAChC,QAAQ,EAAE,OAAO,CAAC;CACnB;AAqDH;;;;;GAKG;AACH,QAAA,MAAM,aAAa,wDAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
<script module lang="ts">
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
|
-
import type {
|
|
4
|
-
|
|
3
|
+
import type {
|
|
4
|
+
ViewportOptions,
|
|
5
|
+
Tool,
|
|
6
|
+
CanvasState,
|
|
7
|
+
ImportableCanvasState,
|
|
8
|
+
Viewport,
|
|
9
|
+
} from '@fieldnotes/core';
|
|
5
10
|
|
|
6
11
|
export interface FieldNotesCanvasProps {
|
|
7
12
|
/** Constructor-only: changes after mount are ignored (the canvas is stateful). */
|
|
@@ -42,6 +47,8 @@
|
|
|
42
47
|
|
|
43
48
|
<script lang="ts">
|
|
44
49
|
import { onMount } from 'svelte';
|
|
50
|
+
import { mountViewport } from '@fieldnotes/core';
|
|
51
|
+
import type { MountedViewport } from '@fieldnotes/core';
|
|
45
52
|
import ViewportProvider from './ViewportProvider.svelte';
|
|
46
53
|
|
|
47
54
|
const {
|
|
@@ -63,82 +70,44 @@
|
|
|
63
70
|
let root: HTMLDivElement;
|
|
64
71
|
let viewport = $state.raw<Viewport | null>(null);
|
|
65
72
|
|
|
66
|
-
|
|
67
|
-
onMount(() => {
|
|
68
|
-
const vp = new Viewport(root, options);
|
|
69
|
-
|
|
70
|
-
const activate = (fallback?: string) => {
|
|
71
|
-
const name = tool ?? defaultTool ?? fallback;
|
|
72
|
-
if (name) vp.setTool(name);
|
|
73
|
-
};
|
|
74
|
-
// An explicit `options.tools` is the host's choice; core registers it and activates select.
|
|
75
|
-
const deferDefaults = tools === undefined && options?.tools === undefined;
|
|
76
|
-
for (const t of tools === 'default' ? createDefaultTools() : (tools ?? [])) {
|
|
77
|
-
vp.toolManager.register(t);
|
|
78
|
-
}
|
|
79
|
-
if (!deferDefaults) activate(tools === 'default' ? 'select' : undefined);
|
|
73
|
+
let mounted: MountedViewport | undefined;
|
|
80
74
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
75
|
+
// Getters keep the destructured props live for the helper's deferred reads.
|
|
76
|
+
onMount(() => {
|
|
77
|
+
const handle = mountViewport(root, {
|
|
78
|
+
options,
|
|
79
|
+
tools,
|
|
80
|
+
initialData,
|
|
81
|
+
get tool() {
|
|
82
|
+
return tool;
|
|
83
|
+
},
|
|
84
|
+
get defaultTool() {
|
|
85
|
+
return defaultTool;
|
|
86
|
+
},
|
|
87
|
+
onReady: onready,
|
|
88
|
+
get onChange() {
|
|
89
|
+
return onchange;
|
|
90
|
+
},
|
|
91
|
+
get changeDebounceMs() {
|
|
92
|
+
return changeDebounceMs;
|
|
93
|
+
},
|
|
94
|
+
get onToolChange() {
|
|
95
|
+
return ontoolchange;
|
|
96
|
+
},
|
|
98
97
|
});
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
onready?.(vp);
|
|
102
|
-
|
|
103
|
-
// Hosts that register their own tools in onready keep exactly those.
|
|
104
|
-
if (deferDefaults) {
|
|
105
|
-
const registerDefaults = vp.toolManager.toolNames.length === 0;
|
|
106
|
-
if (registerDefaults) {
|
|
107
|
-
for (const t of createDefaultTools()) vp.toolManager.register(t);
|
|
108
|
-
}
|
|
109
|
-
activate(registerDefaults ? 'select' : undefined);
|
|
110
|
-
}
|
|
111
|
-
} catch (error) {
|
|
112
|
-
clearTimeout(timer);
|
|
113
|
-
unsubscribe();
|
|
114
|
-
vp.destroy();
|
|
115
|
-
throw error;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const unsubscribeTool = vp.toolManager.onChange((name) => ontoolchange?.(name));
|
|
119
|
-
viewport = vp;
|
|
98
|
+
mounted = handle;
|
|
99
|
+
viewport = handle.viewport;
|
|
120
100
|
|
|
121
101
|
return () => {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
try {
|
|
125
|
-
emitChange();
|
|
126
|
-
} catch (error) {
|
|
127
|
-
// A failing host save must not leak the viewport or throw out of unmount.
|
|
128
|
-
if (typeof reportError === 'function') reportError(error);
|
|
129
|
-
else console.error('[fieldnotes] onChange failed during unmount', error);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
unsubscribeTool();
|
|
133
|
-
unsubscribe();
|
|
134
|
-
vp.destroy();
|
|
102
|
+
handle.destroy();
|
|
103
|
+
mounted = undefined;
|
|
135
104
|
viewport = null;
|
|
136
105
|
};
|
|
137
106
|
});
|
|
138
107
|
|
|
139
108
|
$effect(() => {
|
|
140
109
|
if (!viewport || tool === undefined) return;
|
|
141
|
-
|
|
110
|
+
mounted?.setTool(tool);
|
|
142
111
|
});
|
|
143
112
|
|
|
144
113
|
$effect(() => {
|
|
@@ -148,9 +117,7 @@
|
|
|
148
117
|
|
|
149
118
|
$effect(() => {
|
|
150
119
|
if (!viewport || !Array.isArray(tools)) return;
|
|
151
|
-
|
|
152
|
-
if (!viewport.toolManager.getTool(t.name)) viewport.toolManager.register(t);
|
|
153
|
-
}
|
|
120
|
+
mounted?.addTools(tools);
|
|
154
121
|
});
|
|
155
122
|
</script>
|
|
156
123
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { Snippet } from 'svelte';
|
|
2
|
-
import type { ViewportOptions, Tool, CanvasState, ImportableCanvasState } from '@fieldnotes/core';
|
|
3
|
-
import { Viewport } from '@fieldnotes/core';
|
|
2
|
+
import type { ViewportOptions, Tool, CanvasState, ImportableCanvasState, Viewport } from '@fieldnotes/core';
|
|
4
3
|
export interface FieldNotesCanvasProps {
|
|
5
4
|
/** Constructor-only: changes after mount are ignored (the canvas is stateful). */
|
|
6
5
|
options?: ViewportOptions;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FieldNotesCanvas.svelte.d.ts","sourceRoot":"","sources":["../src/FieldNotesCanvas.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"FieldNotesCanvas.svelte.d.ts","sourceRoot":"","sources":["../src/FieldNotesCanvas.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EACV,eAAe,EACf,IAAI,EACJ,WAAW,EACX,qBAAqB,EACrB,QAAQ,EACT,MAAM,kBAAkB,CAAC;AAE1B,MAAM,WAAW,qBAAqB;IACpC,kFAAkF;IAClF,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B;;;;;OAKG;IACH,KAAK,CAAC,EAAE,IAAI,EAAE,GAAG,SAAS,CAAC;IAC3B,iFAAiF;IACjF,WAAW,CAAC,EAAE,qBAAqB,GAAG,MAAM,CAAC;IAC7C,oEAAoE;IACpE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,6DAA6D;IAC7D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wDAAwD;IACxD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,sFAAsF;IACtF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAAC;IACvC,uFAAuF;IACvF,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;IACxC,8EAA8E;IAC9E,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wFAAwF;IACxF,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AA8FH,QAAA,MAAM,gBAAgB,2DAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fieldnotes/svelte",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Svelte wrapper for Field Notes canvas SDK",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"fieldnotes"
|
|
38
38
|
],
|
|
39
39
|
"peerDependencies": {
|
|
40
|
-
"@fieldnotes/core": ">=0.
|
|
40
|
+
"@fieldnotes/core": ">=0.98.0 <1.0.0",
|
|
41
41
|
"svelte": ">=5 <6"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"svelte": "^5.57.2",
|
|
50
50
|
"svelte-check": "^4.7.7",
|
|
51
51
|
"vitest": "^5.0.5",
|
|
52
|
-
"@fieldnotes/core": "0.
|
|
52
|
+
"@fieldnotes/core": "0.98.0"
|
|
53
53
|
},
|
|
54
54
|
"scripts": {
|
|
55
55
|
"build": "svelte-package -i src -o dist",
|