terra-draw icon indicating copy to clipboard operation
terra-draw copied to clipboard

[Feature Request] Custom Cursor support

Open j0hnm4r5 opened this issue 9 months ago • 1 comments

Is your feature request related to a problem? Please describe.

Terra Draw has support for selecting from preset cursors, but lacks support for real custom cursors and cursors aside from the presets ("unset", "grab", "grabbing", "crosshair", "pointer", "wait", "move").

Describe your proposed idea for the solution to this problem

Since you're not really doing anything to the cursor value with setCursor other than passing it directly to the map canvas context (at least for maplibre, which is the only one I checked), you could just allow any string to be set for the cursor value. I propose that validating CSS cursor formats is out of scope for this library.

Describe alternatives you've considered

At least with maplibre, you're removing the cursor property from the map canvas. Which means it gets steamrolled even if I've set it outside of Terra Draw, as defined here:

public setCursor(cursor: Parameters<SetCursor>[0]) {
    const canvas = this._map.getCanvas();
    if (cursor === "unset") {
        canvas.style.removeProperty("cursor");
    } else {
        canvas.style.cursor = cursor;
    }
}

Ideally "unset" (or maybe a new mode called "revert") would actually revert to the previous cursor value, not remove the property altogether.

Additional context

You can get around the problem right now with something like this, which just tricks TypeScript into thinking I'm passing "crosshair" as the value:

new TerraDrawPolygonMode({
    cursors: {
        start: cursorCrosshair as unknown as "crosshair",
        close: cursorCrosshair as unknown as "crosshair",
    },
})

Where cursorCrosshair is a string defined like this, as per the cursor spec:

import CrosshairSvg from './assets/cursors/crosshair.svg?raw'

const createCursor = ({
  svg,
  x = 0,
  y = 0,
  fallback = 'default',
}: {
  svg: string
  x?: number
  y?: number
  fallback?: string
}) => {
  const blob = new Blob([svg], { type: 'image/svg+xml' })
  const URL = window.URL.createObjectURL(blob)
  return `url(${URL}) ${x} ${y}, ${fallback}`
}

export const cursorCrosshair = createCursor({
  svg: CrosshairSvg,
  x: 12,
  y: 12,
  fallback: 'crosshair',
})

j0hnm4r5 avatar Jan 31 '25 15:01 j0hnm4r5