Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion packages/core/src/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,17 @@ const bendMidpoint = (pr) => {
}

export class Editor {
constructor({ container, store, theme = 'light', grid = 'lines', readonly = false, camera, styles, geoKind } = {}) {
constructor({ container, store, theme = 'light', grid = 'lines', readonly = false, camera, styles, geoKind, disablePanScroll = false } = {}) {
this.container = container
this.store = store || new Store()
this.theme = themeOf(theme)
this.grid = GRID_IDS.includes(grid) ? grid : 'lines'
this.readonly = !!readonly
// Opt-in embed mode: plain wheel events pass through to the page instead
// of panning, while pinch-zoom (Ctrl/Meta+wheel) zooms the board instead
// of the page. Pointer tools (including the hand tool's drag-pan) are
// unaffected either way.
this.disablePanScroll = !!disablePanScroll
this.camera = camera || { x: 0, y: 0, z: 1 }
this.styles = { ...DEFAULT_STYLES, ...(styles || {}) }
this.geoKind = geoKind || 'rectangle'
Expand Down Expand Up @@ -1463,6 +1468,9 @@ export class Editor {

_wheel(e) {
if (this.readonly) return
// Embed mode: plain scroll belongs to the page — but pinch-zoom still
// zooms the board, since the browser would page-zoom without the capture.
if (this.disablePanScroll && !(e.ctrlKey || e.metaKey)) return
e.preventDefault()
const s = this._evPoint(e)
if (e.ctrlKey || e.metaKey) {
Expand Down
20 changes: 20 additions & 0 deletions packages/core/test/editor.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,26 @@ describe('setup', () => {
expect(TOOLS).toContain('draw')
})

it('disablePanScroll passes plain scroll through but keeps pinch-zoom', () => {
const embed = new Editor({ container: document.createElement('div'), disablePanScroll: true })
const before = { ...embed.camera }
let prevented = false
embed._wheel({ deltaX: 0, deltaY: 100, ctrlKey: false, metaKey: false, preventDefault: () => { prevented = true } })
expect(embed.camera).toEqual(before)
expect(prevented).toBe(false)
// ...but pinch-zoom still captures and zooms
const zBefore = embed.camera.z
embed._wheel({ clientX: 50, clientY: 50, deltaX: 0, deltaY: -100, ctrlKey: true, metaKey: false, preventDefault: () => { prevented = true } })
expect(embed.camera.z).toBeGreaterThan(zBefore)
expect(prevented).toBe(true)
embed.destroy()
// default captures: pans and prevents default
const before2 = { ...editor.camera }
editor._wheel({ clientX: 50, clientY: 50, deltaX: 10, deltaY: 0, ctrlKey: false, metaKey: false, preventDefault: () => { prevented = true } })
expect(editor.camera).not.toEqual(before2)
expect(prevented).toBe(true)
})

it('camera math round-trips', () => {
editor.setCamera({ x: 50, y: -20, z: 2 })
const p = editor.screenToPage(100, 100)
Expand Down
4 changes: 4 additions & 0 deletions packages/core/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,9 @@ export interface EditorOptions {
camera?: Camera
styles?: Partial<Styles>
geoKind?: GeoId
/** Opt in to embed scrolling: plain wheel passes to the page, Ctrl/Meta+wheel zooms the board.
* Off by default (wheel pans as before); pointer tools keep working either way. */
disablePanScroll?: boolean
}

export type EditorEvent =
Expand All @@ -250,6 +253,7 @@ export class Editor {
theme: Theme
grid: GridId
readonly: boolean
disablePanScroll: boolean
camera: Camera
styles: Styles
geoKind: GeoId
Expand Down
6 changes: 6 additions & 0 deletions packages/react/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export const Quickdraw = forwardRef(function Quickdraw(props, ref) {
grid = 'lines',
readonly = false,
hideUi = false,
disablePanScroll = false,
themeToggle = true,
gridControl = true,
watermark = true,
Expand Down Expand Up @@ -77,6 +78,7 @@ export const Quickdraw = forwardRef(function Quickdraw(props, ref) {
theme,
grid,
readonly,
disablePanScroll,
camera,
styles,
})
Expand Down Expand Up @@ -166,6 +168,10 @@ export const Quickdraw = forwardRef(function Quickdraw(props, ref) {
uiRef.current?.setHidden(hideUi || readonly)
}, [readonly, hideUi])

useEffect(() => {
if (editorRef.current) editorRef.current.disablePanScroll = !!disablePanScroll
}, [disablePanScroll])

useEffect(() => {
uiRef.current?.setOptions({ themeToggle, gridControl })
}, [themeToggle, gridControl])
Expand Down
2 changes: 2 additions & 0 deletions packages/react/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ export interface QuickdrawProps {
grid?: GridId
/** Lock input (also hides the toolbar). */
readonly?: boolean
/** Opt in to embed scrolling: plain wheel passes to the page, Ctrl/Meta+wheel zooms the board. Live-switchable. */
disablePanScroll?: boolean
/** Hide the stock toolbar (bring your own chrome). */
hideUi?: boolean
/** Show the theme switch in the board menu (default true). */
Expand Down