feat: refine notes workspace and add canvas zoom
Cross-platform packages / Validate source (push) Canceled after 0s
Cross-platform packages / Authorize desktop packaging (push) Canceled after 0s
Cross-platform packages / linux arm64 (push) Canceled after 0s
Cross-platform packages / macos arm64 (push) Canceled after 0s
Cross-platform packages / windows arm64 (push) Canceled after 0s
Cross-platform packages / linux x64 (push) Canceled after 0s
Cross-platform packages / macos x64 (push) Canceled after 0s
Cross-platform packages / windows x64 (push) Canceled after 0s
Cross-platform packages / Publish GitHub and OSS release (push) Canceled after 0s
Cross-platform packages / Validate source (push) Canceled after 0s
Cross-platform packages / Authorize desktop packaging (push) Canceled after 0s
Cross-platform packages / linux arm64 (push) Canceled after 0s
Cross-platform packages / macos arm64 (push) Canceled after 0s
Cross-platform packages / windows arm64 (push) Canceled after 0s
Cross-platform packages / linux x64 (push) Canceled after 0s
Cross-platform packages / macos x64 (push) Canceled after 0s
Cross-platform packages / windows x64 (push) Canceled after 0s
Cross-platform packages / Publish GitHub and OSS release (push) Canceled after 0s
Simplify sidebar selection and note record framing, compact the empty composer, and emphasize note titles. Replace redundant AI comment headings with an accessible panel toggle. Add view-only canvas zoom and fit width, correct PDF import/export icons, and allow image files up to 20 MiB. Include regression coverage and updated design documentation. Release note: Notes have clearer navigation and a more compact writing area. Canvases support zoom and fit width, and image imports accept files up to 20 MiB.
This commit is contained in:
@@ -68,6 +68,11 @@ it('preserves annotations when flow removes the active page and renders scoped t
|
||||
expect(result.flowTransform).toBe('translateX(0px)')
|
||||
expect(result.thumbnailStyles).toEqual(result.editorStyles)
|
||||
expect(result.thumbnailMatchesEditor).toBe(true)
|
||||
expect(result.imageImports).toEqual([
|
||||
{ size: 2 * 1024 * 1024 + 1, imported: 1, restored: 1, errors: [] },
|
||||
{ size: 20 * 1024 * 1024, imported: 1, restored: 1, errors: [] },
|
||||
{ size: 20 * 1024 * 1024 + 1, imported: 0, restored: 0, errors: ['单张图片不能超过 20 MB'] }
|
||||
])
|
||||
} finally {
|
||||
await server.close()
|
||||
await rm(directory, { recursive: true, force: true })
|
||||
|
||||
@@ -44,6 +44,8 @@ if (!process.env.CANVAS_WHEEL_DRIVER) {
|
||||
import '/node_modules/quill/dist/quill.snow.css';
|
||||
import '/src/renderer/src/magic-canvas.css';
|
||||
import { mountCanvasNote } from '/src/renderer/src/magic-canvas/canvas-note.mjs';
|
||||
import { setupZoom } from '/tests/support/canvas-zoom-regression.mjs';
|
||||
window.setupZoom = setupZoom;
|
||||
window.setupLayout = async (width, saved) => {
|
||||
await window.editor?.destroy();
|
||||
document.querySelector('#outer').style.display = 'none';
|
||||
@@ -180,13 +182,15 @@ if (!process.env.CANVAS_WHEEL_DRIVER) {
|
||||
const diagonal = await wheel(70, 60);
|
||||
assert.ok(diagonal.x > 0 && diagonal.y > vertical.y, JSON.stringify(diagonal));
|
||||
});
|
||||
await check(`${mode}: Ctrl wheel is not consumed`, async () => {
|
||||
await check(`${mode}: Ctrl wheel zooms without scrolling or editing`, async () => {
|
||||
const before = await run('snapshot()');
|
||||
const state = await wheel(0, 60, ['control']);
|
||||
assert.equal(state.wheels.at(-1).ctrl, true);
|
||||
assert.equal(state.wheels.at(-1).prevented, false);
|
||||
assert.equal(state.wheels.at(-1).prevented, true);
|
||||
assert.equal(state.x, before.x);
|
||||
assert.equal(state.y, before.y);
|
||||
assert.ok(await run('parseFloat(document.querySelector(".canvas-note-zoom-reset").textContent) < 100'));
|
||||
await run('document.querySelector(".canvas-note-zoom-reset").click()');
|
||||
});
|
||||
await check(`${mode}: right edge chains to outer page`, async () => {
|
||||
await run('viewport.scrollLeft = viewport.scrollWidth');
|
||||
@@ -245,6 +249,79 @@ if (!process.env.CANVAS_WHEEL_DRIVER) {
|
||||
});
|
||||
}
|
||||
}
|
||||
for (const scale of [0.5, 2]) {
|
||||
await run(`setupZoom(${scale})`);
|
||||
await check(`zoom ${scale}: native pen coordinates and selection transforms`, async () => {
|
||||
const point = async (x, y) => run(`zoomFixture.point(${x}, ${y})`);
|
||||
const drag = async (from, to) => {
|
||||
win.webContents.sendInputEvent({ type: 'mouseMove', ...from });
|
||||
win.webContents.sendInputEvent({ type: 'mouseDown', ...from, button: 'left', clickCount: 1 });
|
||||
for (let step = 1; step <= 5; step++) {
|
||||
win.webContents.sendInputEvent({ type: 'mouseMove', x: Math.round(from.x + (to.x - from.x) * step / 5),
|
||||
y: Math.round(from.y + (to.y - from.y) * step / 5), button: 'left' });
|
||||
await sleep(20);
|
||||
}
|
||||
win.webContents.sendInputEvent({ type: 'mouseUp', ...to, button: 'left', clickCount: 1 });
|
||||
await sleep(80);
|
||||
};
|
||||
await drag(await point(50, 50), await point(100, 80));
|
||||
const pen = await run('editor.flush().then(content => content.pages[0].objects.at(-1))');
|
||||
assert.equal(pen.canvasKind, 'pen');
|
||||
assert.ok(Math.abs(pen.path[0][1] - 50) < 2 && Math.abs(pen.path[0][2] - 50) < 2, JSON.stringify(pen));
|
||||
assert.ok(Math.abs(pen.width - 50) < 2 && Math.abs(pen.height - 30) < 2, JSON.stringify(pen));
|
||||
await run('zoomFixture.tool("select")');
|
||||
await drag(await point(200, 130), await point(220, 150));
|
||||
const moved = await run('editor.flush().then(content => content.pages[0].objects[0])');
|
||||
assert.ok(Math.abs(moved.left - 180) < 2 && Math.abs(moved.top - 120) < 2, JSON.stringify(moved));
|
||||
await drag(await point(261, 181), await point(281, 201));
|
||||
const resized = await run('editor.flush().then(content => content.pages[0].objects[0])');
|
||||
assert.ok(resized.scaleX > moved.scaleX && resized.scaleY > moved.scaleY, JSON.stringify(resized));
|
||||
});
|
||||
await check(`zoom ${scale}: PDF, flow, export, pagination and no dirty`, async () => {
|
||||
const result = await run('zoomFixture.verify()');
|
||||
assert.deepEqual(result.errors, []);
|
||||
assert.equal(result.sameContent, true, JSON.stringify(result));
|
||||
assert.equal(result.changes, 0);
|
||||
assert.equal(result.samePng, true, JSON.stringify(result));
|
||||
assert.deepEqual(result.pngSize, [794, 1123]);
|
||||
assert.equal(result.pdfInk, true, JSON.stringify(result));
|
||||
assert.equal(result.maxY, 0);
|
||||
assert.ok(Math.abs(result.pageHeight - 1123 * scale) < 1);
|
||||
assert.equal(result.pageCount, 2);
|
||||
assert.equal(result.followedPage, '2 / 2');
|
||||
assert.equal(result.retainedZoom, true, JSON.stringify(result));
|
||||
});
|
||||
await check(`zoom ${scale}: native flow click and typing on page two`, async () => {
|
||||
const point = await run('zoomFixture.flowPoint()');
|
||||
win.webContents.sendInputEvent({ type: 'mouseMove', ...point });
|
||||
win.webContents.sendInputEvent({ type: 'mouseDown', ...point, button: 'left', clickCount: 1 });
|
||||
win.webContents.sendInputEvent({ type: 'mouseUp', ...point, button: 'left', clickCount: 1 });
|
||||
await win.webContents.insertText('Typed zoom ');
|
||||
await run('editor.flush()');
|
||||
await sleep(100);
|
||||
assert.equal(await run('editor.content().flow.ops.some(op => typeof op.insert === "string" && op.insert.includes("Typed zoom "))'), true);
|
||||
assert.equal(await run('document.querySelector("#layout .canvas-note-page-counter").textContent'), '2 / 2');
|
||||
});
|
||||
}
|
||||
await check('zoom: readonly, fit resize, different page widths, narrow dark and full height', async () => {
|
||||
const result = await run('zoomFixture.verifyLayout()');
|
||||
assert.deepEqual(result.errors, []);
|
||||
for (const state of result.states) {
|
||||
assert.equal(state.maxY, 0, JSON.stringify(state));
|
||||
assert.equal(state.maxX, 0, JSON.stringify(state));
|
||||
assert.equal(state.outerX, 0, JSON.stringify(state));
|
||||
assert.ok(Math.abs(state.pageWidth - state.available) < 1, JSON.stringify(state));
|
||||
assert.ok(Math.abs(state.pageHeight - state.logicalHeight * state.scale) < 1, JSON.stringify(state));
|
||||
assert.equal(state.enabled, true);
|
||||
assert.equal(state.visible, true);
|
||||
}
|
||||
assert.equal(result.sameContent, true);
|
||||
assert.equal(result.changes, 0);
|
||||
win.setSize(560, 800);
|
||||
await sleep(220);
|
||||
assert.equal(await run('zoomFixture.resizeCheck()'), true, 'Fit must follow responsive padding even at an unchanged host width');
|
||||
await run('editor.destroy()');
|
||||
});
|
||||
console.log(JSON.stringify({ electron: process.versions.electron, passed, failures }));
|
||||
win.destroy();
|
||||
app.exit(failures.length ? 1 : 0);
|
||||
|
||||
@@ -35,6 +35,11 @@ it('resizes record columns with native Electron input and preserves desktop widt
|
||||
webPreferences: { sandbox: true, contextIsolation: true, nodeIntegration: false, backgroundThrottling: false } });
|
||||
const js = code => win.webContents.executeJavaScript(code).catch(error => { throw new Error(code + ': ' + error.message); });
|
||||
const settle = () => js('new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)))');
|
||||
const screenshot = async name => {
|
||||
if (process.env.GOODBUDDY_NOTES_SCREENSHOT_DIR) {
|
||||
fs.writeFileSync(require('node:path').join(process.env.GOODBUDDY_NOTES_SCREENSHOT_DIR, name + '.png'), (await win.webContents.capturePage()).toPNG());
|
||||
}
|
||||
};
|
||||
const wait = async code => { for (let i = 0; i < 400; i++) {
|
||||
if (await js(code)) return;
|
||||
await new Promise(resolve => setTimeout(resolve, 25));
|
||||
@@ -61,9 +66,19 @@ it('resizes record columns with native Electron input and preserves desktop widt
|
||||
await click('[id^=magic-note-select-]'); await wait('!!document.querySelector(".magic-note-record")'); await settle(); };
|
||||
await win.loadURL(${JSON.stringify(server.resolvedUrls!.local[0] + 'layout.html')});
|
||||
await open(); await js('document.fonts.ready'); win.focus(); win.webContents.focus();
|
||||
const editor = '.magic-note-composer .magic-note-editor__content .ql-editor';
|
||||
const emptyHeight = (await rect(editor)).height;
|
||||
assert(emptyHeight >= 110 && emptyHeight <= 125, 'Empty composer height: ' + emptyHeight);
|
||||
assert.equal(await js('document.querySelector("#magic-notes-title").textContent'), 'Layout note');
|
||||
assert.equal(await js('document.querySelector(".page-header__description")'), null);
|
||||
await screenshot('magic-notes-light');
|
||||
await js('document.documentElement.dataset.theme = "dark"'); await settle();
|
||||
assert.equal((await rect(editor)).height, emptyHeight);
|
||||
await screenshot('magic-notes-dark');
|
||||
await js('document.documentElement.dataset.theme = "light"'); await settle();
|
||||
const index = '.magic-notes-index-pane', handle = '.magic-notes-index-resize-handle', stream = '.magic-notes-stream-pane';
|
||||
assert.equal((await rect(index)).width, 168);
|
||||
await drag(handle, 80); const dragged = (await rect(index)).width; assert(dragged > 240 && dragged < 260);
|
||||
await drag(handle, 80); const dragged = (await rect(index)).width; assert(dragged > 240 && dragged < 260, 'Dragged index width: ' + dragged);
|
||||
await key(handle, 'Home'); assert.equal((await rect(index)).width, 140);
|
||||
await key(handle, 'Right'); assert.equal((await rect(index)).width, 156);
|
||||
await key(handle, 'End'); assert.equal((await rect(index)).width, 320);
|
||||
@@ -89,9 +104,23 @@ it('resizes record columns with native Electron input and preserves desktop widt
|
||||
assert(await js('document.documentElement.scrollWidth <= innerWidth'));
|
||||
const narrowStream = (await rect(stream)).width;
|
||||
await click('#magic-notes-index-toggle'); assert.equal((await rect(stream)).width, narrowStream);
|
||||
await screenshot('magic-notes-narrow');
|
||||
win.setContentSize(1280, 800); await settle(); await settle();
|
||||
assert.equal((await rect(index)).width, withoutAi);
|
||||
fs.writeFileSync(${JSON.stringify(join(directory, 'result.json'))}, JSON.stringify({ dragged, withoutAi, constrained, narrowStream, persisted: true }));
|
||||
await js('document.querySelector(' + JSON.stringify(editor) + ').focus()');
|
||||
await win.webContents.insertText('Composer growth check\\n'.repeat(30)); await settle();
|
||||
const grownHeight = (await rect(editor)).height;
|
||||
assert(grownHeight > emptyHeight + 400, 'Composer must grow with content');
|
||||
assert(await js('(() => { const el = document.querySelector(' + JSON.stringify(editor) + '); return el.scrollHeight <= el.clientHeight + 1; })()'), 'Quill content must not be clipped');
|
||||
assert(await js('(() => { const el = document.querySelector(".magic-notes-stream-pane"); el.scrollTop = el.scrollHeight; return el.scrollTop > 0; })()'), 'Long draft must remain reachable through the note stream');
|
||||
await settle();
|
||||
await screenshot('magic-notes-long-draft');
|
||||
win.webContents.debugger.attach('1.3');
|
||||
await win.webContents.debugger.sendCommand('Emulation.setEmulatedMedia', { features: [{ name: 'forced-colors', value: 'active' }] });
|
||||
assert.equal(await js('getComputedStyle(document.querySelector(".magic-notes-stream-pane")).scrollbarWidth'), 'auto');
|
||||
assert.equal(await js('getComputedStyle(document.querySelector(".magic-notes-stream-pane")).scrollbarColor'), 'auto');
|
||||
win.webContents.debugger.detach();
|
||||
fs.writeFileSync(${JSON.stringify(join(directory, 'result.json'))}, JSON.stringify({ emptyHeight, grownHeight, dragged, withoutAi, constrained, narrowStream, persisted: true }));
|
||||
app.exit(0);
|
||||
}).catch(error => { console.error(error); app.exit(1); });
|
||||
`)
|
||||
|
||||
@@ -2,7 +2,7 @@ import Quill from 'quill'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import { mountCanvasNote } from '../../src/renderer/src/magic-canvas/canvas-note.mjs'
|
||||
import { MagicCanvasThumbnail } from '../../src/renderer/src/MagicCanvasThumbnail'
|
||||
import { fromCoreContent } from '../../src/renderer/src/magic-canvas/model'
|
||||
import { fromCoreContent, toCoreContent } from '../../src/renderer/src/magic-canvas/model'
|
||||
import '../../src/renderer/src/i18n'
|
||||
import 'quill/dist/quill.snow.css'
|
||||
import '../../src/renderer/src/styles.css'
|
||||
@@ -65,11 +65,33 @@ async function canvasRegression() {
|
||||
root.unmount()
|
||||
observer.disconnect()
|
||||
thumbnailHost.remove()
|
||||
const imageImports = []
|
||||
const pixel = document.createElement('canvas')
|
||||
pixel.width = pixel.height = 1
|
||||
const png = await new Promise<Blob>((resolve) => pixel.toBlob(blob => resolve(blob!), 'image/png'))
|
||||
for (const size of [2 * 1024 * 1024 + 1, 20 * 1024 * 1024, 20 * 1024 * 1024 + 1]) {
|
||||
const importErrors: string[] = []
|
||||
editor = mountCanvasNote(host, undefined, { onError: error => importErrors.push(error) })
|
||||
await editor.flush()
|
||||
const input = host.querySelector<HTMLInputElement>('.canvas-note-image-input')!
|
||||
const transfer = new DataTransfer()
|
||||
transfer.items.add(new File([png, new Uint8Array(size - png.size)], 'boundary.png', { type: 'image/png' }))
|
||||
input.files = transfer.files
|
||||
input.dispatchEvent(new Event('change'))
|
||||
const imported = await editor.flush()
|
||||
const persisted = fromCoreContent(imported, [])
|
||||
await editor.destroy()
|
||||
editor = mountCanvasNote(host, toCoreContent(persisted), { onError: error => importErrors.push(error) })
|
||||
const restored = await editor.flush()
|
||||
imageImports.push({ size, imported: imported.pages[0].objects.length,
|
||||
restored: restored.pages[0].objects.length, errors: importErrors })
|
||||
await editor.destroy()
|
||||
}
|
||||
host.remove()
|
||||
return { errors, initialPages: initial.pages.length, initialObjects: initial.pages[0].objects,
|
||||
beforeShrink, afterShrink, savedPages: saved.pages.length, savedObjects: saved.pages[0].objects,
|
||||
reopenedObjects: reopened.pages[0].objects, visibleInkBefore, visibleInkAfter, reopenedInk, flowTransform,
|
||||
editorStyles, thumbnailStyles, thumbnailMatchesEditor }
|
||||
editorStyles, thumbnailStyles, thumbnailMatchesEditor, imageImports }
|
||||
}
|
||||
|
||||
Object.assign(window, { canvasRegression })
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
/* global window, document, getComputedStyle, requestAnimationFrame, Image */
|
||||
import Quill from 'quill';
|
||||
import { jsPDF as JsPdf } from 'jspdf';
|
||||
import { mountCanvasNote } from '../../src/renderer/src/magic-canvas/canvas-note.mjs';
|
||||
|
||||
const settle = () => new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)));
|
||||
|
||||
export async function setupZoom(scale) {
|
||||
await window.editor?.destroy();
|
||||
document.querySelector('#outer').style.display = 'none';
|
||||
const layout = document.querySelector('#layout');
|
||||
layout.style.cssText = 'width:900px;height:650px;overflow:auto';
|
||||
layout.innerHTML = '<div class="magic-canvas-editor"></div>';
|
||||
const host = layout.firstElementChild;
|
||||
const errors = [];
|
||||
let changes = 0;
|
||||
const pdf = new JsPdf({ unit: 'px', format: [794, 1123], hotfixes: ['px_scaling'] });
|
||||
pdf.setFillColor(255, 0, 0);
|
||||
pdf.rect(30, 30, 100, 40, 'F');
|
||||
const bytes = new Uint8Array(pdf.output('arraybuffer'));
|
||||
const editor = mountCanvasNote(host, { version: 2, pages: [{
|
||||
id: 'pdf-page', width: 794, height: 1123,
|
||||
background: { type: 'pdf', assetId: 'pdf-zoom', page: 1 },
|
||||
objects: [{ type: 'Rect', canvasKind: 'rectangle', left: 160, top: 100,
|
||||
width: 80, height: 60, fill: '#0000ff', strokeWidth: 1, originX: 'left', originY: 'top' }]
|
||||
}], flow: { version: 1, ops: [{ insert: 'Flow zoom body\n'.repeat(55) }] } }, {
|
||||
readPdf: async () => bytes, onError: message => errors.push(message), onChange: () => changes++
|
||||
});
|
||||
window.editor = editor;
|
||||
await editor.flush();
|
||||
const click = suffix => host.querySelector(`.canvas-note-${suffix}`).click();
|
||||
const setScale = value => {
|
||||
click('zoom-reset');
|
||||
for (let i = 0; i < Math.abs(value - 1) * 4; i++) click(value < 1 ? 'zoom-out' : 'zoom-in');
|
||||
};
|
||||
setScale(scale);
|
||||
const tool = name => host.querySelector(`[data-tool="${name}"]`).click();
|
||||
tool('pen');
|
||||
window.zoomFixture = {
|
||||
tool,
|
||||
async flowPoint() {
|
||||
click('next-page');
|
||||
await editor.flush();
|
||||
tool('flow-text');
|
||||
layout.scrollTop = 0;
|
||||
const layer = host.querySelector('.canvas-flow-layer').getBoundingClientRect();
|
||||
const surface = host.querySelector('.canvas-flow-surface');
|
||||
const rect = [...surface.children].flatMap(child => [...child.getClientRects()])
|
||||
.find(rect => rect.left >= layer.left - 1 && rect.left < layer.right && rect.top >= layer.top && rect.top < layer.top + 50);
|
||||
return { x: Math.round(rect.left + 8 * scale), y: Math.round(rect.top + 10 * scale) };
|
||||
},
|
||||
point(x, y) {
|
||||
const rect = host.querySelector('.upper-canvas').getBoundingClientRect();
|
||||
return { x: Math.round(rect.left + x * scale), y: Math.round(rect.top + y * scale) };
|
||||
},
|
||||
async verify() {
|
||||
tool('select');
|
||||
setScale(1);
|
||||
const before = JSON.stringify(await editor.flush());
|
||||
const captures = await editor.capturePages();
|
||||
changes = 0;
|
||||
setScale(scale);
|
||||
await editor.flush();
|
||||
const scaled = await editor.capturePages();
|
||||
const image = new Image();
|
||||
image.src = scaled[0].dataUrl;
|
||||
await image.decode();
|
||||
const background = host.querySelector('.canvas-note-background');
|
||||
const pixel = background.getContext('2d').getImageData(40, 40, 1, 1).data;
|
||||
const paper = background.getContext('2d').getImageData(200, 40, 1, 1).data;
|
||||
const viewport = host.querySelector('.canvas-note-viewport');
|
||||
const pageHeight = host.querySelector('.canvas-note-page').getBoundingClientRect().height;
|
||||
const maxY = viewport.scrollHeight - viewport.clientHeight;
|
||||
click('next-page');
|
||||
await editor.flush();
|
||||
const retainedZoom = host.querySelector('.canvas-note-zoom-reset').textContent === `${scale * 100}%`;
|
||||
tool('flow-text');
|
||||
const quill = Quill.find(host.querySelector('.canvas-flow-quill'));
|
||||
quill.setSelection(quill.getLength() - 2, 0, 'user');
|
||||
await settle();
|
||||
await editor.flush();
|
||||
const followedPage = host.querySelector('.canvas-note-page-counter').textContent;
|
||||
click('previous-page');
|
||||
await editor.flush();
|
||||
return { errors, changes, sameContent: JSON.stringify(editor.content()) === before,
|
||||
samePng: JSON.stringify(captures) === JSON.stringify(scaled), pngSize: [image.width, image.height],
|
||||
pdfInk: pixel[0] > 240 && pixel[1] < 10 && pixel[2] < 10 && paper[0] > 240,
|
||||
pageHeight, maxY,
|
||||
pageCount: editor.content().pages.length, retainedZoom, followedPage };
|
||||
},
|
||||
async verifyLayout() {
|
||||
await editor.destroy();
|
||||
host.classList.add('magic-canvas-content');
|
||||
document.documentElement.dataset.theme = 'dark';
|
||||
const viewer = mountCanvasNote(host, { version: 2, pages: [
|
||||
{ id: 'portrait', width: 794, height: 1123, objects: [] },
|
||||
{ id: 'landscape', width: 1123, height: 794, objects: [] }
|
||||
] }, { disabled: true, onChange: () => changes++, onError: message => errors.push(message) });
|
||||
window.editor = viewer;
|
||||
const before = JSON.stringify(await viewer.flush());
|
||||
changes = 0;
|
||||
click('zoom-fit');
|
||||
const states = [];
|
||||
for (const width of [900, 360, 600]) {
|
||||
layout.style.width = `${width}px`;
|
||||
await settle();
|
||||
for (let page = 0; page < 2; page++) {
|
||||
if (page) click('next-page');
|
||||
else click('previous-page');
|
||||
await viewer.flush();
|
||||
await settle();
|
||||
const viewport = host.querySelector('.canvas-note-viewport');
|
||||
const css = getComputedStyle(viewport);
|
||||
const rect = host.querySelector('.canvas-note-page').getBoundingClientRect();
|
||||
const logical = viewer.content().pages[page];
|
||||
const controls = host.querySelector('.canvas-note-zoom-controls');
|
||||
states.push({ pageWidth: rect.width, pageHeight: rect.height, logicalHeight: logical.height,
|
||||
scale: rect.width / logical.width,
|
||||
available: viewport.clientWidth - parseFloat(css.paddingLeft) - parseFloat(css.paddingRight),
|
||||
maxX: viewport.scrollWidth - viewport.clientWidth, maxY: viewport.scrollHeight - viewport.clientHeight,
|
||||
outerX: layout.scrollWidth - layout.clientWidth,
|
||||
enabled: !host.querySelector('.canvas-note-zoom-in').disabled,
|
||||
visible: controls.getBoundingClientRect().width > 0 && controls.getBoundingClientRect().right <= layout.getBoundingClientRect().right });
|
||||
}
|
||||
}
|
||||
const sameContent = JSON.stringify(await viewer.flush()) === before;
|
||||
layout.style.width = '360px';
|
||||
await settle();
|
||||
window.zoomFixture.resizeCheck = async () => {
|
||||
await settle();
|
||||
const viewport = host.querySelector('.canvas-note-viewport');
|
||||
const css = getComputedStyle(viewport);
|
||||
const available = viewport.clientWidth - parseFloat(css.paddingLeft) - parseFloat(css.paddingRight);
|
||||
return Math.abs(host.querySelector('.canvas-note-page').getBoundingClientRect().width - available) < 1
|
||||
&& JSON.stringify(await viewer.flush()) === before && changes === 0;
|
||||
};
|
||||
return { errors, states, changes, sameContent };
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user