@openpresentation/opf-editor 0.11.3 → 0.14.2
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 +82 -58
- package/dist/annotations.d.ts +16 -16
- package/dist/annotations.js +71 -79
- package/dist/assets.d.ts +7 -7
- package/dist/assets.js +15 -15
- package/dist/background-options.d.ts +2 -2
- package/dist/background-options.js +6 -6
- package/dist/block-controls.js +7 -7
- package/dist/block-convert.d.ts +7 -7
- package/dist/block-convert.js +23 -21
- package/dist/blocks.d.ts +8 -8
- package/dist/blocks.js +41 -40
- package/dist/canvas-fields.js +35 -12
- package/dist/canvas.d.ts +22 -30
- package/dist/canvas.js +57 -64
- package/dist/chart-data.d.ts +12 -10
- package/dist/chart-data.js +22 -21
- package/dist/chart-options-panel.d.ts +2 -2
- package/dist/chart-options-panel.js +82 -8
- package/dist/chart-options.d.ts +35 -9
- package/dist/chart-options.js +133 -40
- package/dist/checks.js +21 -0
- package/dist/color-authoring.js +1 -1
- package/dist/content-actions.d.ts +15 -15
- package/dist/content-actions.js +71 -70
- package/dist/content-controls.js +12 -12
- package/dist/data-grid.js +179 -10
- package/dist/data.d.ts +23 -2
- package/dist/data.js +40 -1
- package/dist/dataset-refs.d.ts +17 -0
- package/dist/dataset-refs.js +45 -0
- package/dist/design-controls.js +66 -47
- package/dist/design-options.d.ts +16 -14
- package/dist/design-options.js +86 -74
- package/dist/edit-helpers.js +8 -7
- package/dist/exact-source.js +5 -5
- package/dist/export.d.ts +5 -7
- package/dist/export.js +40 -14
- package/dist/find-panel.js +5 -5
- package/dist/find-replace.d.ts +3 -3
- package/dist/find-replace.js +35 -20
- package/dist/font-gate.js +49 -58
- package/dist/galleries.js +36 -52
- package/dist/grid-model.d.ts +99 -24
- package/dist/grid-model.js +577 -101
- package/dist/image-crop.d.ts +4 -4
- package/dist/image-crop.js +20 -20
- package/dist/image-cropper.js +3 -3
- package/dist/index.d.ts +28 -28
- package/dist/index.js +74 -90
- package/dist/json-options.js +17 -16
- package/dist/layout-handles.js +5 -5
- package/dist/layout-placeholders.js +4 -4
- package/dist/layout.d.ts +2 -2
- package/dist/layout.js +8 -7
- package/dist/numbering-panel.js +3 -5
- package/dist/numbering.d.ts +3 -5
- package/dist/numbering.js +14 -26
- package/dist/outline-view.js +24 -24
- package/dist/outline.d.ts +12 -12
- package/dist/outline.js +52 -50
- package/dist/persistence.d.ts +1 -1
- package/dist/persistence.js +19 -18
- package/dist/presentation-ids.js +2 -2
- package/dist/react.js +3 -3
- package/dist/review-panel.d.ts +30 -13
- package/dist/review-panel.js +154 -53
- package/dist/review.d.ts +36 -43
- package/dist/review.js +92 -53
- package/dist/rich-text-input.js +4 -3
- package/dist/rich-text-toolbar.js +5 -3
- package/dist/rich-text.js +5 -2
- package/dist/schema-inspector.d.ts +2 -2
- package/dist/schema-inspector.js +16 -11
- package/dist/schema.d.ts +1 -1
- package/dist/schema.js +25 -5
- package/dist/slide-manager.d.ts +2 -2
- package/dist/slide-manager.js +18 -18
- package/dist/slides.d.ts +20 -20
- package/dist/slides.js +99 -97
- package/dist/svelte.js +1 -1
- package/dist/switches.d.ts +10 -10
- package/dist/switches.js +136 -109
- package/dist/table-options.d.ts +8 -8
- package/dist/table-options.js +41 -30
- package/dist/table-structure.d.ts +10 -10
- package/dist/table-structure.js +21 -21
- package/dist/template-panel.d.ts +3 -3
- package/dist/template-panel.js +37 -13
- package/dist/templates.d.ts +22 -11
- package/dist/templates.js +48 -55
- package/dist/transfer.d.ts +4 -4
- package/dist/transfer.js +74 -54
- package/package.json +10 -6
- package/dist/font-defaults.js +0 -25
package/dist/content-actions.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// split and merge, as editor transactions. The pure transforms live in core (`@openpresentation/opf/convert`);
|
|
3
3
|
// each action here finds its target in the document, calls the transform, turns the result into a guarded patch
|
|
4
4
|
// (`test` of what was read, then `replace`, or per-slide `remove` and `add`), validates the document and, in the
|
|
5
|
-
// apply form, applies it as ONE undoable step. Every prepare function returns `{
|
|
5
|
+
// apply form, applies it as ONE undoable step. Every prepare function returns `{ presentation, patches, changed,
|
|
6
6
|
// lossless, loss, path }` without touching a session; a refusal throws `content-action-refused`.
|
|
7
7
|
import {
|
|
8
8
|
OPFConversionError,
|
|
@@ -19,9 +19,10 @@ import {
|
|
|
19
19
|
unwrapGroup,
|
|
20
20
|
wrapBlocks,
|
|
21
21
|
} from "@openpresentation/opf/convert";
|
|
22
|
-
import { applyJsonPatch, getValueAtPath, opfPathToJsonPointer, splitOpfPath
|
|
22
|
+
import { applyJsonPatch, getValueAtPath, opfPathToJsonPointer, splitOpfPath } from "./index.js";
|
|
23
23
|
import { readBlockContent } from "./block-convert.js";
|
|
24
24
|
import { checkedDocument, fail } from "./edit-helpers.js";
|
|
25
|
+
import { checkFormat } from "./checks.js";
|
|
25
26
|
|
|
26
27
|
const refuse = (message, details) => fail("content-action-refused", message, details);
|
|
27
28
|
|
|
@@ -39,14 +40,14 @@ function requireEditor(editor) {
|
|
|
39
40
|
if (!editor || typeof editor.applyPatch !== "function") throw fail("invalid-editor", "Expected an editor session created by createEditorSession.");
|
|
40
41
|
}
|
|
41
42
|
|
|
42
|
-
function slideParts(
|
|
43
|
+
function slideParts(presentation, path, what) {
|
|
43
44
|
let parts;
|
|
44
45
|
try {
|
|
45
46
|
parts = splitOpfPath(path);
|
|
46
47
|
} catch {
|
|
47
48
|
parts = undefined;
|
|
48
49
|
}
|
|
49
|
-
if (!parts || parts[0] !== "slides" || !/^(0|[1-9][0-9]*)$/.test(parts[1] ?? "") || !
|
|
50
|
+
if (!parts || parts[0] !== "slides" || !/^(0|[1-9][0-9]*)$/.test(parts[1] ?? "") || !presentation?.slides?.[Number(parts[1])]) throw refuse(`Choose ${what} on a slide.`, { path });
|
|
50
51
|
return { slideIndex: Number(parts[1]), relative: parts.slice(2), parts };
|
|
51
52
|
}
|
|
52
53
|
|
|
@@ -54,27 +55,27 @@ const asNumbers = (path) => path.map((part) => (/^(0|[1-9][0-9]*)$/.test(String(
|
|
|
54
55
|
const toDocumentPath = (slideIndex, relative) => ["slides", String(slideIndex), ...(relative ?? []).map(String)].join(".");
|
|
55
56
|
|
|
56
57
|
/** Replace one value (a slide, a block, a list) as `test` + `replace`, validated like every other edit. */
|
|
57
|
-
function replacePatches(
|
|
58
|
+
function replacePatches(presentation, parts, next) {
|
|
58
59
|
const pointer = opfPathToJsonPointer(parts);
|
|
59
60
|
return [
|
|
60
|
-
{ op: "test", path: pointer, value: structuredClone(getValueAtPath(
|
|
61
|
+
{ op: "test", path: pointer, value: structuredClone(getValueAtPath(presentation, parts)) },
|
|
61
62
|
{ op: "replace", path: pointer, value: next },
|
|
62
63
|
];
|
|
63
64
|
}
|
|
64
65
|
// `validate: false` skips the whole-document check, for a dry run that only needs the loss report (core has already
|
|
65
66
|
// validated the changed slide); applying always validates.
|
|
66
|
-
function finish(
|
|
67
|
-
if (!validate) return {
|
|
68
|
-
const before =
|
|
69
|
-
return {
|
|
67
|
+
function finish(presentation, patches, extra, validate = true) {
|
|
68
|
+
if (!validate) return { presentation: applyJsonPatch(presentation, patches), patches, ...extra };
|
|
69
|
+
const before = checkFormat(presentation);
|
|
70
|
+
return { presentation: checkedDocument(presentation, patches, before), patches, ...extra };
|
|
70
71
|
}
|
|
71
72
|
function apply(editor, prepared, meta, source) {
|
|
72
|
-
if (!prepared.changed) return { ...prepared,
|
|
73
|
+
if (!prepared.changed) return { ...prepared, presentation: editor.presentation, patches: [], inversePatches: [], validation: editor.validation };
|
|
73
74
|
const change = editor.applyPatch(prepared.patches, { ...meta, source: meta.source ?? source, path: prepared.path });
|
|
74
75
|
const extra = Object.fromEntries(["levels", "range", "slideCount", "pages"].filter((key) => prepared[key] !== undefined).map((key) => [key, prepared[key]]));
|
|
75
76
|
return { ...change, changed: true, lossless: prepared.lossless, loss: prepared.loss, path: prepared.path, ...extra };
|
|
76
77
|
}
|
|
77
|
-
const unchanged = (
|
|
78
|
+
const unchanged = (presentation, path, reason) => ({ presentation: structuredClone(presentation), patches: [], path, changed: false, lossless: true, loss: [], ...(reason ? { reason } : {}) });
|
|
78
79
|
|
|
79
80
|
// --- list levels -------------------------------------------------------------------------------
|
|
80
81
|
|
|
@@ -91,75 +92,75 @@ export function listItemIndexForSelection(selectedPath, blockPath) {
|
|
|
91
92
|
* item cannot be nested, and items under a moved item move with it (`options.withChildren`, default true).
|
|
92
93
|
* `changed` is false, with a `reason`, when nothing can move.
|
|
93
94
|
*/
|
|
94
|
-
export function prepareListShift(
|
|
95
|
-
const found = readBlockContent(
|
|
95
|
+
export function prepareListShift(presentation, blockPath, indices, delta, options = {}) {
|
|
96
|
+
const found = readBlockContent(presentation, blockPath);
|
|
96
97
|
if (!found || found.kind !== "list") throw refuse("Choose a list block.", { path: blockPath });
|
|
97
98
|
const result = core(() => shiftListLevels(found.owner, indices, delta, options));
|
|
98
99
|
const path = found.path.join(".");
|
|
99
|
-
if (!result.changed) return unchanged(
|
|
100
|
-
return finish(
|
|
100
|
+
if (!result.changed) return unchanged(presentation, path, result.reason);
|
|
101
|
+
return finish(presentation, replacePatches(presentation, found.path, result.payload), { path, changed: true, lossless: result.lossless, loss: result.loss, levels: result.levels }, options.validate !== false);
|
|
101
102
|
}
|
|
102
103
|
/** Nest or un-nest list items as one undoable step. Returns the session change plus `changed`, `reason`, `levels`, `lossless` and `loss`. */
|
|
103
104
|
export function shiftListItems(editor, blockPath, indices, delta, options = {}, meta = {}) {
|
|
104
105
|
requireEditor(editor);
|
|
105
|
-
return apply(editor, prepareListShift(editor.
|
|
106
|
+
return apply(editor, prepareListShift(editor.presentation, blockPath, indices, delta, options), meta, "list-level");
|
|
106
107
|
}
|
|
107
108
|
|
|
108
109
|
// --- structure inside one slide ----------------------------------------------------------------
|
|
109
110
|
|
|
110
111
|
/** One slide-level transform as a guarded replace of that slide. */
|
|
111
|
-
function slideAction(
|
|
112
|
-
const slide =
|
|
112
|
+
function slideAction(presentation, slideIndex, transform, targetPath, validate = true) {
|
|
113
|
+
const slide = presentation.slides[slideIndex];
|
|
113
114
|
const result = core(() => transform(slide));
|
|
114
115
|
const path = result.path ? toDocumentPath(slideIndex, result.path) : (targetPath ?? `slides.${slideIndex}`);
|
|
115
|
-
if (!result.changed) return unchanged(
|
|
116
|
-
const patches = replacePatches(
|
|
117
|
-
return finish(
|
|
116
|
+
if (!result.changed) return unchanged(presentation, path);
|
|
117
|
+
const patches = replacePatches(presentation, ["slides", String(slideIndex)], result.slide);
|
|
118
|
+
return finish(presentation, patches, { path, changed: true, lossless: result.lossless, loss: result.loss }, validate);
|
|
118
119
|
}
|
|
119
120
|
|
|
120
121
|
/** Wrap the blocks at `indices` of the container at `containerPath` (a slide `slides.0`, a group or a region group) in a new group. */
|
|
121
|
-
export function prepareGroupBlocks(
|
|
122
|
-
const { slideIndex, relative } = slideParts(
|
|
123
|
-
return slideAction(
|
|
122
|
+
export function prepareGroupBlocks(presentation, containerPath, indices, options = {}) {
|
|
123
|
+
const { slideIndex, relative } = slideParts(presentation, containerPath, "a container");
|
|
124
|
+
return slideAction(presentation, slideIndex, (slide) => wrapBlocks(slide, indices, { container: asNumbers(relative), ...(options.composition ? { composition: options.composition } : {}) }), undefined, options.validate !== false);
|
|
124
125
|
}
|
|
125
126
|
/** Replace the group at `groupPath` by its blocks. What the group carried (composition, id) is reported in `loss`. */
|
|
126
|
-
export function prepareUngroupBlock(
|
|
127
|
-
const { slideIndex, relative } = slideParts(
|
|
128
|
-
return slideAction(
|
|
127
|
+
export function prepareUngroupBlock(presentation, groupPath, options = {}) {
|
|
128
|
+
const { slideIndex, relative } = slideParts(presentation, groupPath, "a group");
|
|
129
|
+
return slideAction(presentation, slideIndex, (slide) => unwrapGroup(slide, asNumbers(relative)), undefined, options.validate !== false);
|
|
129
130
|
}
|
|
130
131
|
/** Place every block of a slide in its own named region: `regions[i]` is block i's region (`left`, `right`, `top:left`, ...). */
|
|
131
|
-
export function prepareBlocksToRegions(
|
|
132
|
-
slideParts(
|
|
133
|
-
return slideAction(
|
|
132
|
+
export function prepareBlocksToRegions(presentation, slideIndex, regions, options = {}) {
|
|
133
|
+
slideParts(presentation, `slides.${slideIndex}`, "a slide");
|
|
134
|
+
return slideAction(presentation, slideIndex, (slide) => blocksToRegions(slide, regions), undefined, options.validate !== false);
|
|
134
135
|
}
|
|
135
136
|
/** Turn the named regions of a slide into ordinary blocks in reading order. The regions' placement is reported in `loss`. */
|
|
136
|
-
export function prepareRegionsToBlocks(
|
|
137
|
-
slideParts(
|
|
138
|
-
return slideAction(
|
|
137
|
+
export function prepareRegionsToBlocks(presentation, slideIndex, options = {}) {
|
|
138
|
+
slideParts(presentation, `slides.${slideIndex}`, "a slide");
|
|
139
|
+
return slideAction(presentation, slideIndex, (slide) => regionsToBlocks(slide), undefined, options.validate !== false);
|
|
139
140
|
}
|
|
140
141
|
/** Move the content of region `from` to region `to`, or swap them when `to` is taken and `options.swap` is true. */
|
|
141
|
-
export function prepareMoveRegion(
|
|
142
|
-
slideParts(
|
|
143
|
-
return slideAction(
|
|
142
|
+
export function prepareMoveRegion(presentation, slideIndex, from, to, options = {}) {
|
|
143
|
+
slideParts(presentation, `slides.${slideIndex}`, "a slide");
|
|
144
|
+
return slideAction(presentation, slideIndex, (slide) => moveRegion(slide, from, to, options), undefined, options.validate !== false);
|
|
144
145
|
}
|
|
145
146
|
/** Move the image block at `blockPath` into the slide's design as `slideImage`, `background` or `watermark`. */
|
|
146
|
-
export function prepareImageToDesign(
|
|
147
|
-
const { slideIndex, relative } = slideParts(
|
|
148
|
-
return slideAction(
|
|
147
|
+
export function prepareImageToDesign(presentation, blockPath, target, options = {}) {
|
|
148
|
+
const { slideIndex, relative } = slideParts(presentation, blockPath, "an image block");
|
|
149
|
+
return slideAction(presentation, slideIndex, (slide) => promoteImage(slide, asNumbers(relative), target, options), `slides.${slideIndex}`, options.validate !== false);
|
|
149
150
|
}
|
|
150
151
|
/** Move a slide's own `slideImage`, `background` image or `watermark` back into its content as an image block. */
|
|
151
|
-
export function prepareImageToContent(
|
|
152
|
-
slideParts(
|
|
153
|
-
return slideAction(
|
|
152
|
+
export function prepareImageToContent(presentation, slideIndex, source, options = {}) {
|
|
153
|
+
slideParts(presentation, `slides.${slideIndex}`, "a slide");
|
|
154
|
+
return slideAction(presentation, slideIndex, (slide) => demoteImage(slide, source, options), undefined, options.validate !== false);
|
|
154
155
|
}
|
|
155
156
|
|
|
156
|
-
export const groupBlocks = (editor, containerPath, indices, options = {}, meta = {}) => (requireEditor(editor), apply(editor, prepareGroupBlocks(editor.
|
|
157
|
-
export const ungroupBlock = (editor, groupPath, meta = {}) => (requireEditor(editor), apply(editor, prepareUngroupBlock(editor.
|
|
158
|
-
export const placeBlocksInRegions = (editor, slideIndex, regions, meta = {}) => (requireEditor(editor), apply(editor, prepareBlocksToRegions(editor.
|
|
159
|
-
export const regionsAsBlocks = (editor, slideIndex, meta = {}) => (requireEditor(editor), apply(editor, prepareRegionsToBlocks(editor.
|
|
160
|
-
export const moveSlideRegion = (editor, slideIndex, from, to, options = {}, meta = {}) => (requireEditor(editor), apply(editor, prepareMoveRegion(editor.
|
|
161
|
-
export const moveImageToDesign = (editor, blockPath, target, options = {}, meta = {}) => (requireEditor(editor), apply(editor, prepareImageToDesign(editor.
|
|
162
|
-
export const moveImageToContent = (editor, slideIndex, source, options = {}, meta = {}) => (requireEditor(editor), apply(editor, prepareImageToContent(editor.
|
|
157
|
+
export const groupBlocks = (editor, containerPath, indices, options = {}, meta = {}) => (requireEditor(editor), apply(editor, prepareGroupBlocks(editor.presentation, containerPath, indices, options), meta, "group-blocks"));
|
|
158
|
+
export const ungroupBlock = (editor, groupPath, meta = {}) => (requireEditor(editor), apply(editor, prepareUngroupBlock(editor.presentation, groupPath), meta, "ungroup-block"));
|
|
159
|
+
export const placeBlocksInRegions = (editor, slideIndex, regions, meta = {}) => (requireEditor(editor), apply(editor, prepareBlocksToRegions(editor.presentation, slideIndex, regions), meta, "blocks-to-regions"));
|
|
160
|
+
export const regionsAsBlocks = (editor, slideIndex, meta = {}) => (requireEditor(editor), apply(editor, prepareRegionsToBlocks(editor.presentation, slideIndex), meta, "regions-to-blocks"));
|
|
161
|
+
export const moveSlideRegion = (editor, slideIndex, from, to, options = {}, meta = {}) => (requireEditor(editor), apply(editor, prepareMoveRegion(editor.presentation, slideIndex, from, to, options), meta, "move-region"));
|
|
162
|
+
export const moveImageToDesign = (editor, blockPath, target, options = {}, meta = {}) => (requireEditor(editor), apply(editor, prepareImageToDesign(editor.presentation, blockPath, target, options), meta, "image-to-design"));
|
|
163
|
+
export const moveImageToContent = (editor, slideIndex, source, options = {}, meta = {}) => (requireEditor(editor), apply(editor, prepareImageToContent(editor.presentation, slideIndex, source, options), meta, "image-to-content"));
|
|
163
164
|
|
|
164
165
|
// --- slides ------------------------------------------------------------------------------------
|
|
165
166
|
|
|
@@ -168,8 +169,8 @@ export const moveImageToContent = (editor, slideIndex, source, options = {}, met
|
|
|
168
169
|
* replaced slide, a `replace` for each one that changed, `remove` for the surplus old slides and `add` for the
|
|
169
170
|
* surplus new ones, so a split or merge stays one guarded, undoable transaction without rewriting the other slides.
|
|
170
171
|
*/
|
|
171
|
-
function slidePatches(
|
|
172
|
-
const old =
|
|
172
|
+
function slidePatches(presentation, range, slides) {
|
|
173
|
+
const old = presentation.slides.slice(range.start, range.start + range.deleteCount);
|
|
173
174
|
const patches = old.map((slide, offset) => ({ op: "test", path: `/slides/${range.start + offset}`, value: structuredClone(slide) }));
|
|
174
175
|
const common = Math.min(old.length, slides.length);
|
|
175
176
|
for (let offset = 0; offset < common; offset++) if (JSON.stringify(old[offset]) !== JSON.stringify(slides[offset])) patches.push({ op: "replace", path: `/slides/${range.start + offset}`, value: structuredClone(slides[offset]) });
|
|
@@ -177,31 +178,31 @@ function slidePatches(document, range, slides) {
|
|
|
177
178
|
for (let offset = common; offset < slides.length; offset++) patches.push({ op: "add", path: `/slides/${range.start + offset}`, value: structuredClone(slides[offset]) });
|
|
178
179
|
return patches;
|
|
179
180
|
}
|
|
180
|
-
function slidesAction(
|
|
181
|
+
function slidesAction(presentation, result, extra = {}) {
|
|
181
182
|
const path = `slides.${result.range.start}`;
|
|
182
|
-
if (!result.changed) return unchanged(
|
|
183
|
-
return finish(
|
|
183
|
+
if (!result.changed) return unchanged(presentation, path, "Nothing to change.");
|
|
184
|
+
return finish(presentation, slidePatches(presentation, result.range, result.slides), { path, changed: true, lossless: result.lossless, loss: result.loss, range: result.range, slideCount: result.slides.length, ...extra });
|
|
184
185
|
}
|
|
185
186
|
|
|
186
187
|
/** Split a slide by its blocks (`options.at`: block indexes where a new slide starts, or `options.each`). See core's `splitSlide`. */
|
|
187
|
-
export function prepareSplitSlide(
|
|
188
|
-
return slidesAction(
|
|
188
|
+
export function prepareSplitSlide(presentation, slideIndex, options = {}) {
|
|
189
|
+
return slidesAction(presentation, core(() => splitSlide(presentation, slideIndex, options)));
|
|
189
190
|
}
|
|
190
|
-
/** Split a slide that overflows into pages with the existing pagination (`options`: text measurement, fonts, as for `
|
|
191
|
-
export function prepareSplitSlideOnOverflow(
|
|
192
|
-
const result = core(() => splitSlideOnOverflowCore(
|
|
193
|
-
return slidesAction(
|
|
191
|
+
/** Split a slide that overflows into pages with the existing pagination (`options`: text measurement, fonts, as for core `paginate`). `pages` maps the new slides back for `prepareUnpaginate`. */
|
|
192
|
+
export function prepareSplitSlideOnOverflow(presentation, slideIndex, options = {}) {
|
|
193
|
+
const result = core(() => splitSlideOnOverflowCore(presentation, slideIndex, options));
|
|
194
|
+
return slidesAction(presentation, result, { pages: result.pages });
|
|
194
195
|
}
|
|
195
196
|
/** Merge `count` consecutive slides from `start`. The first slide's id, headings and design win; what is dropped is reported in `loss`. */
|
|
196
|
-
export function prepareMergeSlides(
|
|
197
|
-
return slidesAction(
|
|
197
|
+
export function prepareMergeSlides(presentation, start, count = 2) {
|
|
198
|
+
return slidesAction(presentation, core(() => mergeSlidesCore(presentation, start, count)));
|
|
198
199
|
}
|
|
199
|
-
/** Put the continuation slides of a pagination back into one slide each. `pages` is the mapping `splitSlideOnOverflow` or `
|
|
200
|
-
export function prepareUnpaginate(
|
|
201
|
-
return slidesAction(
|
|
200
|
+
/** Put the continuation slides of a pagination back into one slide each. `pages` is the mapping `splitSlideOnOverflow` or core `paginate` returned. */
|
|
201
|
+
export function prepareUnpaginate(presentation, pages, options = {}) {
|
|
202
|
+
return slidesAction(presentation, core(() => unpaginate(presentation, pages, options)));
|
|
202
203
|
}
|
|
203
204
|
|
|
204
|
-
export const splitSlideByBlocks = (editor, slideIndex, options = {}, meta = {}) => (requireEditor(editor), apply(editor, prepareSplitSlide(editor.
|
|
205
|
-
export const splitSlideOnOverflow = (editor, slideIndex, options = {}, meta = {}) => (requireEditor(editor), apply(editor, prepareSplitSlideOnOverflow(editor.
|
|
206
|
-
export const mergeSlides = (editor, start, count = 2, meta = {}) => (requireEditor(editor), apply(editor, prepareMergeSlides(editor.
|
|
207
|
-
export const unpaginateSlides = (editor, pages, options = {}, meta = {}) => (requireEditor(editor), apply(editor, prepareUnpaginate(editor.
|
|
205
|
+
export const splitSlideByBlocks = (editor, slideIndex, options = {}, meta = {}) => (requireEditor(editor), apply(editor, prepareSplitSlide(editor.presentation, slideIndex, options), meta, "split-slide"));
|
|
206
|
+
export const splitSlideOnOverflow = (editor, slideIndex, options = {}, meta = {}) => (requireEditor(editor), apply(editor, prepareSplitSlideOnOverflow(editor.presentation, slideIndex, options), meta, "split-slide-overflow"));
|
|
207
|
+
export const mergeSlides = (editor, start, count = 2, meta = {}) => (requireEditor(editor), apply(editor, prepareMergeSlides(editor.presentation, start, count), meta, "merge-slides"));
|
|
208
|
+
export const unpaginateSlides = (editor, pages, options = {}, meta = {}) => (requireEditor(editor), apply(editor, prepareUnpaginate(editor.presentation, pages, options), meta, "unpaginate-slides"));
|
package/dist/content-controls.js
CHANGED
|
@@ -104,7 +104,7 @@ export function createContentControls(ctx) {
|
|
|
104
104
|
const prepare = (delta) => (state) => {
|
|
105
105
|
const item = index(state);
|
|
106
106
|
if (item === undefined) return { changed: false, loss: [], reason: "Select a list item to change its level." };
|
|
107
|
-
return prepareListShift(editor.
|
|
107
|
+
return prepareListShift(editor.presentation, state.blockPath, [item], delta, { validate: false });
|
|
108
108
|
};
|
|
109
109
|
const apply = (delta) => () => {
|
|
110
110
|
const state = ctxState.current;
|
|
@@ -140,7 +140,7 @@ export function createContentControls(ctx) {
|
|
|
140
140
|
updaters.push((state) => {
|
|
141
141
|
metrics.wrap.hidden = !state.metricGroupPath;
|
|
142
142
|
if (!state.metricGroupPath) return;
|
|
143
|
-
const targets = blockConversionTargets(editor.
|
|
143
|
+
const targets = blockConversionTargets(editor.presentation, state.metricGroupPath);
|
|
144
144
|
metrics.setOptions(
|
|
145
145
|
targets.map((target) => ({
|
|
146
146
|
value: target.kind,
|
|
@@ -172,7 +172,7 @@ export function createContentControls(ctx) {
|
|
|
172
172
|
prepare: (state) => {
|
|
173
173
|
const indices = next(state);
|
|
174
174
|
if (!indices) return { changed: false, loss: [], reason: "There is no block after this one in its container." };
|
|
175
|
-
return prepareGroupBlocks(editor.
|
|
175
|
+
return prepareGroupBlocks(editor.presentation, containerPath(state), indices, { validate: false });
|
|
176
176
|
},
|
|
177
177
|
apply: () => {
|
|
178
178
|
const state = ctxState.current;
|
|
@@ -190,7 +190,7 @@ export function createContentControls(ctx) {
|
|
|
190
190
|
prepare: (state) => {
|
|
191
191
|
const path = parentGroup(state);
|
|
192
192
|
if (!path) return { changed: false, loss: [], reason: "This block is not inside a group." };
|
|
193
|
-
return prepareUngroupBlock(editor.
|
|
193
|
+
return prepareUngroupBlock(editor.presentation, path, { validate: false });
|
|
194
194
|
},
|
|
195
195
|
apply: () => ungroupBlock(editor, parentGroup(ctxState.current)),
|
|
196
196
|
selectAfter: (change) => change.path,
|
|
@@ -222,7 +222,7 @@ export function createContentControls(ctx) {
|
|
|
222
222
|
image.setOptions(
|
|
223
223
|
IMAGE_DESTINATIONS.map((entry) => {
|
|
224
224
|
try {
|
|
225
|
-
const prepared = prepareImageToDesign(editor.
|
|
225
|
+
const prepared = prepareImageToDesign(editor.presentation, state.blockPath, entry.target, { ...entry.options, validate: false });
|
|
226
226
|
return { value: entry.value, label: `${entry.label}${prepared.loss.length ? ` (loses ${prepared.loss.join(", ")})` : ""}` };
|
|
227
227
|
} catch (error) {
|
|
228
228
|
return { value: entry.value, label: `${entry.label} (unavailable)`, disabled: true, title: messageOf(error) };
|
|
@@ -251,21 +251,21 @@ export function createContentControls(ctx) {
|
|
|
251
251
|
});
|
|
252
252
|
const toBlocks = action("Turn regions into blocks", {
|
|
253
253
|
role: "regions-to-blocks",
|
|
254
|
-
prepare: () => prepareRegionsToBlocks(editor.
|
|
254
|
+
prepare: () => prepareRegionsToBlocks(editor.presentation, getSlide(), { validate: false }),
|
|
255
255
|
apply: () => regionsAsBlocks(editor, getSlide()),
|
|
256
256
|
done: "Regions are now blocks, in reading order.",
|
|
257
257
|
});
|
|
258
258
|
const designImages = Object.keys(DESIGN_IMAGE_LABELS).map((source) =>
|
|
259
259
|
action(`Move the ${DESIGN_IMAGE_LABELS[source]} into the content`, {
|
|
260
260
|
role: `back-${source}`,
|
|
261
|
-
prepare: () => prepareImageToContent(editor.
|
|
261
|
+
prepare: () => prepareImageToContent(editor.presentation, getSlide(), source, { validate: false }),
|
|
262
262
|
apply: () => moveImageToContent(editor, getSlide(), source),
|
|
263
263
|
done: `The ${DESIGN_IMAGE_LABELS[source]} is now an image block.`,
|
|
264
264
|
}),
|
|
265
265
|
);
|
|
266
266
|
slideBox.append(layouts.wrap, toBlocks.wrap, ...designImages.map((entry) => entry.wrap));
|
|
267
267
|
updaters.push((state) => {
|
|
268
|
-
const slide = editor.
|
|
268
|
+
const slide = editor.presentation.slides?.[state.slideIndex];
|
|
269
269
|
if (!slide) return;
|
|
270
270
|
const regions = regionKeysOfSlide(slide);
|
|
271
271
|
const count = blockCount(state.slideIndex);
|
|
@@ -275,7 +275,7 @@ export function createContentControls(ctx) {
|
|
|
275
275
|
layouts.setOptions(
|
|
276
276
|
choices.map((choice) => {
|
|
277
277
|
try {
|
|
278
|
-
const prepared = prepareBlocksToRegions(editor.
|
|
278
|
+
const prepared = prepareBlocksToRegions(editor.presentation, state.slideIndex, choice.regions, { validate: false });
|
|
279
279
|
return { value: choice.id, label: `${choice.label}${prepared.loss.length ? ` (loses ${prepared.loss.join(", ")})` : ""}` };
|
|
280
280
|
} catch (error) {
|
|
281
281
|
return { value: choice.id, label: `${choice.label} (unavailable)`, disabled: true, title: messageOf(error) };
|
|
@@ -296,7 +296,7 @@ export function createContentControls(ctx) {
|
|
|
296
296
|
|
|
297
297
|
/** The number of blocks a slide can place in regions: its `blocks`, or the content fields it holds inline. */
|
|
298
298
|
function blockCount(slideIndex) {
|
|
299
|
-
const slide = editor.
|
|
299
|
+
const slide = editor.presentation.slides?.[slideIndex];
|
|
300
300
|
if (!slide) return 0;
|
|
301
301
|
if (Array.isArray(slide.blocks)) return slide.blocks.length;
|
|
302
302
|
return ["text", "items", "bullets", "image", "video", "chart", "table", "code", "metric", "quote", "timeline"].filter((key) => slide[key] !== undefined).length;
|
|
@@ -309,9 +309,9 @@ export function createContentControls(ctx) {
|
|
|
309
309
|
slideNode: slideBox,
|
|
310
310
|
/** Update every control from the session and the current selection. */
|
|
311
311
|
sync(state) {
|
|
312
|
-
const slide = editor.
|
|
312
|
+
const slide = editor.presentation.slides?.[state.slideIndex];
|
|
313
313
|
const imageBlock = state.blockPath ? isImageBlock(editor, state.blockPath) : false;
|
|
314
|
-
const full = { ...state, imageBlock, metricGroupPath: state.selectedPath ? metricGroupForSelection(editor.
|
|
314
|
+
const full = { ...state, imageBlock, metricGroupPath: state.selectedPath ? metricGroupForSelection(editor.presentation, state.selectedPath) : undefined, slide };
|
|
315
315
|
ctxState.current = full;
|
|
316
316
|
for (const update of updaters) update(full);
|
|
317
317
|
},
|