@gramps-connect/story 0.5.0 → 0.6.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.
- package/README.md +1 -1
- package/dist/editor.js +11 -7
- package/dist/editor.js.map +3 -3
- package/dist/types/editor/model.d.ts +12 -11
- package/package.json +1 -1
- package/src/editor/MapEditor.tsx +19 -7
- package/src/editor/model.ts +12 -11
|
@@ -79,18 +79,19 @@ export declare function pruneMedia(doc: StoryDoc, files: Map<string, Uint8Array>
|
|
|
79
79
|
/** "Josef Meyer" -> "Josef Meyer.story.zip", minus characters some file
|
|
80
80
|
* systems refuse. */
|
|
81
81
|
export declare function storyFileName(title: string): string;
|
|
82
|
-
/** Undo and redo over whole documents
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
82
|
+
/** Undo and redo over whole documents (a StoryDoc here; any value that's
|
|
83
|
+
* replaced, never changed in place, works). Typing in one field makes one
|
|
84
|
+
* step, not one per keystroke: a change with the same `key` within
|
|
85
|
+
* COALESCE_MS of the last replaces it. */
|
|
86
|
+
export interface History<T = StoryDoc> {
|
|
87
|
+
past: T[];
|
|
88
|
+
present: T;
|
|
89
|
+
future: T[];
|
|
89
90
|
lastKey?: string;
|
|
90
91
|
lastAt?: number;
|
|
91
92
|
}
|
|
92
93
|
export declare const COALESCE_MS = 1500;
|
|
93
|
-
export declare function startHistory(doc:
|
|
94
|
-
export declare function record(history: History
|
|
95
|
-
export declare function undo(history: History): History
|
|
96
|
-
export declare function redo(history: History): History
|
|
94
|
+
export declare function startHistory<T = StoryDoc>(doc: T): History<T>;
|
|
95
|
+
export declare function record<T>(history: History<T>, next: T, key?: string, now?: number): History<T>;
|
|
96
|
+
export declare function undo<T>(history: History<T>): History<T>;
|
|
97
|
+
export declare function redo<T>(history: History<T>): History<T>;
|
package/package.json
CHANGED
package/src/editor/MapEditor.tsx
CHANGED
|
@@ -54,6 +54,9 @@ const COLOR_SWATCHES = [
|
|
|
54
54
|
"#2a78d6", "#d64545", "#3aa657", "#e0a325", "#8654c9", "#2aa7a0", "#e0678a", "#555555",
|
|
55
55
|
];
|
|
56
56
|
|
|
57
|
+
/** A region's fill with no opacity of its own, as the maps and the story
|
|
58
|
+
* player draw it; also a new region's starting opacity. */
|
|
59
|
+
const UNSET_OPACITY = 0.25;
|
|
57
60
|
const EMPTY_LABEL_PREVIEW: FeatureCollection = { type: "FeatureCollection", features: [] };
|
|
58
61
|
|
|
59
62
|
/** An image overlay as the host gives and gets it: `image` is the host's
|
|
@@ -356,9 +359,14 @@ export const MapEditor = forwardRef<MapEditorHandle, MapEditorProps>(function Ma
|
|
|
356
359
|
// both a newly-finished region's own opacity and, while one is selected,
|
|
357
360
|
// what changing this edits in place. 0.25 matches Gramps Connect's map
|
|
358
361
|
// view default, so an untouched region renders the same there.
|
|
359
|
-
const [opacity, setOpacity] = useState(
|
|
362
|
+
const [opacity, setOpacity] = useState(UNSET_OPACITY);
|
|
360
363
|
const opacityRef = useRef(opacity);
|
|
361
364
|
opacityRef.current = opacity;
|
|
365
|
+
// How a loaded shape with no color of its own is drawn: as the maps draw
|
|
366
|
+
// it (their mark color), not in whatever the toolbar's color is now --
|
|
367
|
+
// and it's saved still without one.
|
|
368
|
+
const unsetColorRef = useRef(defaultColor ?? defaultColors(dark).mark);
|
|
369
|
+
unsetColorRef.current = defaultColor ?? defaultColors(dark).mark;
|
|
362
370
|
// The selected point/region's own `name` (a point's label text, or a
|
|
363
371
|
// region's purely-descriptive name), mirroring `color`'s own pattern --
|
|
364
372
|
// NOT derived from getSnapshot() on every render, since
|
|
@@ -483,12 +491,12 @@ export const MapEditor = forwardRef<MapEditorHandle, MapEditorProps>(function Ma
|
|
|
483
491
|
// and terra-draw calls a styling function fresh on every render, not
|
|
484
492
|
// just at construction time.
|
|
485
493
|
const readColor = (feature: GeoJSONStoreFeatures): HexColor =>
|
|
486
|
-
(feature.properties.color as HexColor | undefined) ?? (
|
|
494
|
+
(feature.properties.color as HexColor | undefined) ?? (unsetColorRef.current as HexColor);
|
|
487
495
|
// Same pattern as readColor, for a region's own `opacity` property --
|
|
488
496
|
// only polygon/rectangle modes take a `fillOpacity` style at all (point/
|
|
489
497
|
// line have nothing to fill).
|
|
490
498
|
const readOpacity = (feature: GeoJSONStoreFeatures): number =>
|
|
491
|
-
(feature.properties.opacity as number | undefined) ??
|
|
499
|
+
(feature.properties.opacity as number | undefined) ?? UNSET_OPACITY;
|
|
492
500
|
|
|
493
501
|
const draw = new TerraDraw({
|
|
494
502
|
adapter: new TerraDrawMapLibreGLAdapter({ map }),
|
|
@@ -585,8 +593,9 @@ export const MapEditor = forwardRef<MapEditorHandle, MapEditorProps>(function Ma
|
|
|
585
593
|
const feature = id ? draw.getSnapshot().find((f) => String(f.id) === id) : undefined;
|
|
586
594
|
setLabelText((feature?.properties?.name as string | undefined) ?? "");
|
|
587
595
|
if (feature) {
|
|
588
|
-
|
|
589
|
-
|
|
596
|
+
// An uncolored shape shows the color it's drawn in.
|
|
597
|
+
setColor((feature.properties?.color as string | undefined) ?? unsetColorRef.current);
|
|
598
|
+
setOpacity((feature.properties?.opacity as number | undefined) ?? UNSET_OPACITY);
|
|
590
599
|
// Mirrors the overlay mousedown handler's own deselectFeature call
|
|
591
600
|
// -- an image overlay and a drawn shape are mutually exclusive
|
|
592
601
|
// selections, so a shape becoming selected clears any overlay
|
|
@@ -1227,14 +1236,17 @@ export const MapEditor = forwardRef<MapEditorHandle, MapEditorProps>(function Ma
|
|
|
1227
1236
|
const name = (f.properties?.name as string | undefined)?.trim();
|
|
1228
1237
|
// Opacity only for a region (Polygon) -- meaningless on a point/
|
|
1229
1238
|
// line, which have nothing to fill (see readOpacity/opacityRef).
|
|
1239
|
+
// Color and opacity only as the shape has them: a drawn shape got
|
|
1240
|
+
// both when finished, and one loaded without them stays that way.
|
|
1230
1241
|
const regionOpacity = f.geometry.type === "Polygon"
|
|
1231
|
-
?
|
|
1242
|
+
? f.properties?.opacity as number | undefined
|
|
1232
1243
|
: undefined;
|
|
1244
|
+
const color = f.properties?.color as string | undefined;
|
|
1233
1245
|
const sourceKey = f.properties?.sourceKey;
|
|
1234
1246
|
return {
|
|
1235
1247
|
...(typeof sourceKey === "string" ? { sourceKey } : {}),
|
|
1236
1248
|
geometry: f.geometry as EditableGeometry,
|
|
1237
|
-
|
|
1249
|
+
...(color !== undefined ? { color } : {}),
|
|
1238
1250
|
...(name ? { name } : {}),
|
|
1239
1251
|
...(regionOpacity !== undefined ? { opacity: regionOpacity } : {}),
|
|
1240
1252
|
};
|
package/src/editor/model.ts
CHANGED
|
@@ -228,13 +228,14 @@ export function storyFileName(title: string): string {
|
|
|
228
228
|
return `${safe || "story"}.story.zip`;
|
|
229
229
|
}
|
|
230
230
|
|
|
231
|
-
/** Undo and redo over whole documents
|
|
232
|
-
*
|
|
233
|
-
*
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
231
|
+
/** Undo and redo over whole documents (a StoryDoc here; any value that's
|
|
232
|
+
* replaced, never changed in place, works). Typing in one field makes one
|
|
233
|
+
* step, not one per keystroke: a change with the same `key` within
|
|
234
|
+
* COALESCE_MS of the last replaces it. */
|
|
235
|
+
export interface History<T = StoryDoc> {
|
|
236
|
+
past: T[];
|
|
237
|
+
present: T;
|
|
238
|
+
future: T[];
|
|
238
239
|
lastKey?: string;
|
|
239
240
|
lastAt?: number;
|
|
240
241
|
}
|
|
@@ -242,24 +243,24 @@ export interface History {
|
|
|
242
243
|
export const COALESCE_MS = 1500;
|
|
243
244
|
const MAX_STEPS = 200;
|
|
244
245
|
|
|
245
|
-
export function startHistory(doc:
|
|
246
|
+
export function startHistory<T = StoryDoc>(doc: T): History<T> {
|
|
246
247
|
return { past: [], present: doc, future: [] };
|
|
247
248
|
}
|
|
248
249
|
|
|
249
|
-
export function record(history: History
|
|
250
|
+
export function record<T>(history: History<T>, next: T, key?: string, now = Date.now()): History<T> {
|
|
250
251
|
if (next === history.present) return history;
|
|
251
252
|
const coalesce = key !== undefined && key === history.lastKey && history.lastAt !== undefined && now - history.lastAt < COALESCE_MS;
|
|
252
253
|
const past = coalesce ? history.past : [...history.past, history.present].slice(-MAX_STEPS);
|
|
253
254
|
return { past, present: next, future: [], lastKey: key, lastAt: now };
|
|
254
255
|
}
|
|
255
256
|
|
|
256
|
-
export function undo(history: History): History {
|
|
257
|
+
export function undo<T>(history: History<T>): History<T> {
|
|
257
258
|
const previous = history.past[history.past.length - 1];
|
|
258
259
|
if (!previous) return history;
|
|
259
260
|
return { past: history.past.slice(0, -1), present: previous, future: [history.present, ...history.future] };
|
|
260
261
|
}
|
|
261
262
|
|
|
262
|
-
export function redo(history: History): History {
|
|
263
|
+
export function redo<T>(history: History<T>): History<T> {
|
|
263
264
|
const [next, ...future] = history.future;
|
|
264
265
|
if (!next) return history;
|
|
265
266
|
return { past: [...history.past, history.present], present: next, future };
|