From bbfc671de068eab8982e07f780c95030b37cd216 Mon Sep 17 00:00:00 2001 From: Caleb Evans Date: Thu, 30 May 2024 09:54:30 -0700 Subject: [PATCH] Replace string types with proper string unions Specifically for event types and drawing types. --- src/jcanvas.d.ts | 92 +++++++++++++++++++++++++++++++++++++++++++++--- src/jcanvas.ts | 55 ++++++++++++++++------------- 2 files changed, 119 insertions(+), 28 deletions(-) diff --git a/src/jcanvas.d.ts b/src/jcanvas.d.ts index 684628d..2c947ce 100644 --- a/src/jcanvas.d.ts +++ b/src/jcanvas.d.ts @@ -19,7 +19,7 @@ declare module "jcanvas/dist/esm/jcanvas-handles.min.js"; interface JCanvasPluginParams { name: string; props?: Record; - type?: keyofJCanvasMapsdrawings; + type?: string; fn: ( this: HTMLCanvasElement, ctx: CanvasRenderingContext2D, @@ -27,10 +27,94 @@ interface JCanvasPluginParams { ) => void; } +type JCanvasLayerType = + | "arc" + | "bezier" + | "ellipse" + | "function" + | "image" + | "line" + | "path" + | "polygon" + | "slice" + | "quadratic" + | "rectangle" + | "text" + | "vector" + | "save" + | "restore" + | "rotate" + | "scale" + | "translate"; + +type JCanvasDrawingMethodName = + | "drawArc" + | "drawBezier" + | "drawEllipse" + | "draw" + | "drawImage" + | "drawLine" + | "drawPath" + | "drawPolygon" + | "drawSlice" + | "drawQuadratic" + | "drawRect" + | "drawText" + | "drawVector" + | "saveCanvas" + | "restoreCanvas" + | "rotateCanvas" + | "scaleCanvas" + | "translateCanvas"; + +type JCanvasMouseEventName = + | "click" + | "dblclick" + | "mousedown" + | "mouseup" + | "mousemove" + | "mouseover" + | "mouseout" + | "contextmenu"; + +type JCanvasTouchEventName = "touchstart" | "touchmove" | "touchend"; + +type JCanvasPointerEventName = "pointerdown" | "pointermove" | "pointerup"; + +type JCanvasDragEventName = "dragstart" | "drag" | "dragstop" | "dragcancel"; + +type JCanvasInteractionEventName = + | JCanvasMouseEventName + | JCanvasTouchEventName + | JCanvasPointerEventName + | JCanvasDragEventName; + +type JCanvasMechanicalEventName = + | "add" + | "remove" + | "change" + | "move" + | "animatestart" + | "animate" + | "animateend" + | "stop" + | "delay"; + +type JCanvasImageEventName = "load"; + +type JCanvasEventName = + | JCanvasInteractionEventName + | JCanvasMechanicalEventName + | JCanvasImageEventName; + interface JCanvasMaps { - drawings: Record; - touchEvents: Record; - mouseEvents: Record; + drawings: Record; + touchEvents: Partial< + Record + >; + mouseEvents: Partial< + Record + >; } interface JCanvasCache { diff --git a/src/jcanvas.ts b/src/jcanvas.ts index 0ddfc2c..72a6656 100644 --- a/src/jcanvas.ts +++ b/src/jcanvas.ts @@ -81,7 +81,7 @@ const extendObject = Object.assign, cursors: ["grab", "grabbing", "zoom-in", "zoom-out"], propsObj: {} as Record, }, - tangibleEvents = [ + tangibleEvents: (JCanvasMouseEventName | JCanvasTouchEventName)[] = [ "mousedown", "mousemove", "mouseup", @@ -569,7 +569,11 @@ jCanvas.extend = function extend(plugin) { }; // Add drawing type to drawing map if (plugin.type) { - maps.drawings[String(plugin.type)] = plugin.name; + // We need to type-assert because the nature of $.jCanvas.extend + // implies that plugin.type is always a custom type which is not + // inherently part of JCanvasLayerType + maps.drawings[plugin.type as JCanvasLayerType] = + plugin.name as JCanvasDrawingMethodName; } } // @ts-expect-error TODO: fix this @@ -604,7 +608,7 @@ class JCanvasInternalData { }; // Data for the current event event: { - type: string | null; + type: JCanvasInteractionEventName | null; x: number | null; y: number | null; event?: Event | null; @@ -1365,7 +1369,7 @@ function _drawLayer( function _handleLayerDrag( $canvas: JQuery, data: JCanvasInternalData, - eventType: string + eventType: JCanvasInteractionEventName ) { const drag = data.drag; const layer = drag.layer as JCanvasLayer; @@ -1470,7 +1474,7 @@ function _handleLayerDrag( function _setCursor( $canvas: JQuery, layer: JCanvasLayer, - eventType: string + eventType: JCanvasInteractionEventName ) { let cursor; if (layer.cursors) { @@ -1505,11 +1509,7 @@ function _runEventCallback( arg: any ) { // Prevent callback from firing recursively - if ( - callbacks[eventType as any] && - layer._running && - !layer._running[eventType] - ) { + if (callbacks[eventType] && layer._running && !layer._running[eventType]) { // Signify the start of callback execution for this event layer._running[eventType] = true; // Run event callback with the given arguments @@ -1520,7 +1520,10 @@ function _runEventCallback( } // Determine if the given layer can "legally" fire the given event -function _layerCanFireEvent(layer: JCanvasLayer, eventType: string) { +function _layerCanFireEvent( + layer: JCanvasLayer, + eventType: string +): eventType is JCanvasMouseEventName | JCanvasTouchEventName { // If events are disable and if // layer is tangible or event is not tangible return ( @@ -1534,7 +1537,7 @@ function _triggerLayerEvent( $canvas: JQuery, data: JCanvasInternalData, layer: JCanvasLayer, - eventType: string, + eventType: JCanvasEventName, arg?: any ) { // If layer can legally fire this event type @@ -1567,7 +1570,10 @@ $.fn.triggerLayerEvent = function (layerId, eventType) { const data = _getCanvasData(canvas); const layer = $canvas.getLayer(layerId); if (layer) { - _triggerLayerEvent($canvas, data, layer, eventType); + // We need to type assert here because the developer may trigger a + // custom event that is not inherently part of JCanvasEventName; + // this is a use case we want to allow + _triggerLayerEvent($canvas, data, layer, eventType as JCanvasEventName); } } return $canvases; @@ -1769,7 +1775,7 @@ function _addLayer( canvas: HTMLCanvasElement, params: JCanvasObject, args?: Partial, - method?: (args: JCanvasObject) => JQuery + method?: (args: JCanvasObject) => JQuery ): JCanvasLayer | null { const layer: JCanvasObject | JCanvasLayer = params._layer ? (args as JCanvasLayer) @@ -1791,7 +1797,7 @@ function _addLayer( } else if (params.method) { params._method = $.fn[params.method]; } else if (params.type) { - params._method = $.fn[maps.drawings[params.type] as keyof JQuery]; + params._method = $.fn[maps.drawings[params.type]]; } } @@ -2340,23 +2346,24 @@ _supportColorProps([ /* Event API */ // Convert mouse event name to a corresponding touch event name (if possible) -function _getTouchEventName(eventName: keyof typeof maps.touchEvents) { - // Detect touch event support - if (maps.touchEvents[eventName]) { - return maps.touchEvents[eventName]; +function _getTouchEventName(eventName: JCanvasInteractionEventName) { + const touchEventName = maps.touchEvents[eventName]; + if (touchEventName) { + return touchEventName; } return eventName; } // Convert touch event name to a corresponding mouse event name -function _getMouseEventName(eventName: string) { - if (maps.mouseEvents[eventName]) { - eventName = maps.mouseEvents[eventName]; +function _getMouseEventName(eventName: JCanvasInteractionEventName) { + const mouseEventName = maps.mouseEvents[eventName]; + if (mouseEventName) { + return mouseEventName; } return eventName; } // Bind event to jCanvas layer using standard jQuery events -function _createEvent(eventName: string) { +function _createEvent(eventName: JCanvasInteractionEventName) { jCanvas.events[eventName] = function ($canvas, data) { // Retrieve canvas's event cache const eventCache = data.event; @@ -2409,7 +2416,7 @@ function _createEvent(eventName: string) { } }; } -function _createEvents(eventNames: string[]) { +function _createEvents(eventNames: JCanvasInteractionEventName[]) { for (let n = 0; n < eventNames.length; n += 1) { _createEvent(eventNames[n]); }