diff --git a/app/web/src/components/AssetFuncListPanel.vue b/app/web/src/components/AssetFuncListPanel.vue index 7bb1ad851e..f32f88a31a 100644 --- a/app/web/src/components/AssetFuncListPanel.vue +++ b/app/web/src/components/AssetFuncListPanel.vue @@ -10,7 +10,7 @@ diff --git a/app/web/src/components/AssetPalette.vue b/app/web/src/components/AssetPalette.vue index 8ceb81929a..018eaab54f 100644 --- a/app/web/src/components/AssetPalette.vue +++ b/app/web/src/components/AssetPalette.vue @@ -14,6 +14,7 @@ v-tooltip=" 'Drag the assets that you wish to include in your application into the canvas to the right.' " + class="cursor-pointer" name="question-circle" /> diff --git a/app/web/src/components/AttributeDebugView.vue b/app/web/src/components/AttributeDebugView.vue index 4dcbfdad7b..518d7704dd 100644 --- a/app/web/src/components/AttributeDebugView.vue +++ b/app/web/src/components/AttributeDebugView.vue @@ -1,60 +1,39 @@ @@ -66,6 +66,7 @@ import { useComponentsStore } from "@/store/components.store"; import CodeViewer from "./CodeViewer.vue"; import FixDetails from "./FixDetails.vue"; import StatusIndicatorIcon from "./StatusIndicatorIcon.vue"; +import EmptyStateIcon from "./EmptyStateIcon.vue"; const componentsStore = useComponentsStore(); const selectedComponent = computed(() => componentsStore.selectedComponent); diff --git a/app/web/src/components/DebugViewItem.vue b/app/web/src/components/DebugViewItem.vue new file mode 100644 index 0000000000..5498f195f1 --- /dev/null +++ b/app/web/src/components/DebugViewItem.vue @@ -0,0 +1,24 @@ + + + diff --git a/app/web/src/components/ModelingDiagram/ModelingDiagram.vue b/app/web/src/components/ModelingDiagram/ModelingDiagram.vue index 9bfaefc3c2..9fcac5d520 100644 --- a/app/web/src/components/ModelingDiagram/ModelingDiagram.vue +++ b/app/web/src/components/ModelingDiagram/ModelingDiagram.vue @@ -7,7 +7,7 @@ overflow hidden */ class="absolute inset-0 overflow-hidden" :style="{ cursor }" > - +
) { e.evt.preventDefault(); if (props.controlsDisabled) return; + // is it a mouse wheel or a trackpad pinch to zoom? + const isTrackpadPinch = !_.isInteger(e.evt.deltaY); // if CMD key, treat wheel as zoom, otherwise pan - if (e.evt.metaKey) { - // e.evt.metaKey - // zoom - let newZoomLevel = zoomLevel.value - e.evt.deltaY * ZOOM_SCROLL_FACTOR; - if (newZoomLevel < MIN_ZOOM) newZoomLevel = MIN_ZOOM; - if (newZoomLevel > MAX_ZOOM) newZoomLevel = MAX_ZOOM; - + if (e.evt.metaKey || (e.evt.ctrlKey && isTrackpadPinch)) { // need to move origin to zoom centered on pointer position if (containerPointerPos.value && gridPointerPos.value) { // this a little confusing, but we're recreating the same calculations as above, but but at the new zoom level // so we know where the pointer _would_ move and then offset the pointer position stays constant - const newGridWidth = containerWidth.value / newZoomLevel; - const newMinX = gridOrigin.value.x - newGridWidth / 2; - const newGridHeight = containerHeight.value / newZoomLevel; - const newMinY = gridOrigin.value.y - newGridHeight / 2; - const pointerXAtNewZoom = - newMinX + containerPointerPos.value.x / newZoomLevel; - const pointerYAtNewZoom = - newMinY + containerPointerPos.value.y / newZoomLevel; - - gridOrigin.value = { - x: gridOrigin.value.x - (pointerXAtNewZoom - gridPointerPos.value.x), - y: gridOrigin.value.y - (pointerYAtNewZoom - gridPointerPos.value.y), - }; + zoomAtPoint(e.evt.deltaY, containerPointerPos.value, isTrackpadPinch); } - zoomLevel.value = newZoomLevel; } else { // pan - const panFactor = zoomLevel.value * ZOOM_PAN_FACTOR; + const panFactor = ZOOM_PAN_FACTOR / zoomLevel.value; gridOrigin.value = { x: gridOrigin.value.x + e.evt.deltaX * panFactor, y: gridOrigin.value.y + e.evt.deltaY * panFactor, @@ -417,6 +398,32 @@ function onMouseWheel(e: KonvaEventObject) { } } +function zoomAtPoint(delta: number, zoomPos: Vector2d, isPinchToZoom = false) { + // e.evt.metaKey + // zoom + if (!gridPointerPos.value) return; + + const panSpeed = 0.001 * (isPinchToZoom ? 20 : 1) * zoomLevel.value; + + let newZoomLevel = zoomLevel.value - delta * panSpeed; + if (newZoomLevel < MIN_ZOOM) newZoomLevel = MIN_ZOOM; + if (newZoomLevel > MAX_ZOOM) newZoomLevel = MAX_ZOOM; + + const newGridWidth = containerWidth.value / newZoomLevel; + const newMinX = gridOrigin.value.x - newGridWidth / 2; + const newGridHeight = containerHeight.value / newZoomLevel; + const newMinY = gridOrigin.value.y - newGridHeight / 2; + const pointerXAtNewZoom = newMinX + zoomPos.x / newZoomLevel; + const pointerYAtNewZoom = newMinY + zoomPos.y / newZoomLevel; + + gridOrigin.value = { + x: gridOrigin.value.x - (pointerXAtNewZoom - gridPointerPos.value.x), + y: gridOrigin.value.y - (pointerYAtNewZoom - gridPointerPos.value.y), + }; + + zoomLevel.value = newZoomLevel; +} + // not sure why but TS couldnt quite find the ResizeObserverCallback type... type ResizeObserverCallback = ConstructorParameters[0]; const onResize: ResizeObserverCallback = (entries) => { @@ -468,6 +475,12 @@ function onMountedAndReady() { window.addEventListener("mouseup", onMouseUp); window.addEventListener("keydown", onKeyDown); window.addEventListener("keyup", onKeyUp); + // window.addEventListener("pointerdown", onPointerDown); + // window.addEventListener("pointermove", onPointerMove); + // window.addEventListener("pointerup", onPointerUp); + // window.addEventListener("pointercancel", onPointerUp); + // window.addEventListener("pointerout", onPointerUp); + // window.addEventListener("pointerleave", onPointerUp); } onBeforeUnmount(() => { @@ -476,6 +489,12 @@ onBeforeUnmount(() => { window.removeEventListener("mouseup", onMouseUp); window.removeEventListener("keydown", onKeyDown); window.removeEventListener("keyup", onKeyUp); + // window.removeEventListener("pointerdown", onPointerDown); + // window.removeEventListener("pointermove", onPointerMove); + // window.removeEventListener("pointerup", onPointerUp); + // window.removeEventListener("pointercancel", onPointerUp); + // window.removeEventListener("pointerout", onPointerUp); + // window.removeEventListener("pointerleave", onPointerUp); resizeObserver.unobserve(containerRef.value!); }); @@ -658,7 +677,7 @@ function checkIfDragStarted(_e: MouseEvent) { } else if (lastMouseDownElement.value instanceof DiagramEdgeData) { // not sure what dragging an edge means... maybe nothing? /* eslint-disable-next-line no-console */ - console.log("dragging edge ?"); + // console.log("dragging edge ?"); } else if ( lastMouseDownElement.value instanceof DiagramNodeData || lastMouseDownElement.value instanceof DiagramGroupData @@ -678,6 +697,41 @@ function checkIfDragStarted(_e: MouseEvent) { } } +// Pointer events (for touch screens) +// const pointerEventCache = {} as Record; +// let previousPointerDiff: number | undefined; + +// function onPointerDown(e: PointerEvent) { +// pointerEventCache[e.pointerId] = e; +// } + +// function onPointerMove(e: PointerEvent) { +// const events = _.values(pointerEventCache); +// if (events.length === 2 && events[0] && events[1]) { +// // time to zoom! +// const point1 = { x: events[0].clientX, y: events[0].clientY }; +// const point2 = { x: events[1].clientX, y: events[1].clientY }; +// const zoomCenter = pointAlongLinePct(point1, point2, 0.5); +// const newPointerDiff = vectorDistance(point1, point2); + +// if (!previousPointerDiff) { +// previousPointerDiff = newPointerDiff; +// } else { +// const delta = newPointerDiff - previousPointerDiff; +// zoomAtPoint(delta, zoomCenter); +// } +// previousPointerDiff = newPointerDiff; +// } +// } + +// function onPointerUp(e: PointerEvent) { +// delete pointerEventCache[e.pointerId]; + +// if (_.values(pointerEventCache).length < 2) { +// previousPointerDiff = undefined; +// } +// } + // Mode and cursor const cursor = computed(() => { if (dragToPanActive.value) return "grabbing"; diff --git a/app/web/src/components/Workspace/WorkspaceModelAndView.vue b/app/web/src/components/Workspace/WorkspaceModelAndView.vue index 8bdd553976..12331b830f 100644 --- a/app/web/src/components/Workspace/WorkspaceModelAndView.vue +++ b/app/web/src/components/Workspace/WorkspaceModelAndView.vue @@ -48,8 +48,8 @@
diff --git a/app/web/src/components/layout/navbar/ChangeSetPanel.vue b/app/web/src/components/layout/navbar/ChangeSetPanel.vue index 43586cd3bc..ae9423604f 100644 --- a/app/web/src/components/layout/navbar/ChangeSetPanel.vue +++ b/app/web/src/components/layout/navbar/ChangeSetPanel.vue @@ -31,9 +31,7 @@ icon="trash" size="sm" :disabled="fixesStore.fixesAreInProgress || !selectedChangeSetName" - :requestStatus="abandonChangeSetReqStatus" - loadingText="Abandoning Changeset..." - @click="abandonChangesetHandler" + @click="abandonConfirmationModalRef.open" />
@@ -73,6 +71,37 @@ + +
+
+ Are you sure that you want to abandon change set + "{{ selectedChangeSetName }}" + and return to HEAD? +
+
+ Once abandoned, a change set cannot be recovered. +
+
+ + +
+
+
+