feat: double click on surface to add a point
parent
1539860da8
commit
c690992f93
|
@ -1,6 +1,6 @@
|
|||
<script lang="ts">
|
||||
import UpObject from "../components/display/UpObject.svelte";
|
||||
import { queryOnce } from "../lib/api";
|
||||
import { putEntityAttribute, putEntry, queryOnce } from "../lib/api";
|
||||
import Selector from "../components/utils/Selector.svelte";
|
||||
import { onMount } from "svelte";
|
||||
import type { ZoomTransform } from "d3";
|
||||
|
@ -8,6 +8,7 @@
|
|||
import UpObjectCard from "../components/display/UpObjectCard.svelte";
|
||||
import BlobPreview from "../components/display/BlobPreview.svelte";
|
||||
import { i18n } from "../i18n";
|
||||
import type { IValue } from "upend/types";
|
||||
|
||||
const urlSearch = window.location.href.substring(
|
||||
window.location.href.indexOf("?")
|
||||
|
@ -31,29 +32,34 @@
|
|||
y: number;
|
||||
}
|
||||
let points: IPoint[] = [];
|
||||
async function loadPoints() {
|
||||
points = [];
|
||||
const result = await queryOnce(`(matches ? (in "${x}" "${y}") ?)`);
|
||||
|
||||
points = Object.entries(result.objects)
|
||||
.map(([address, obj]) => {
|
||||
let objX = parseInt(String(obj.get(x)));
|
||||
let objY = parseInt(String(obj.get(y)));
|
||||
|
||||
if (objX && objY) {
|
||||
return {
|
||||
address,
|
||||
x: objX,
|
||||
y: objY,
|
||||
};
|
||||
}
|
||||
})
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
$: {
|
||||
if (x && y) {
|
||||
points = [];
|
||||
queryOnce(`(matches ? (in "${x}" "${y}") ?)`).then((result) => {
|
||||
points = Object.entries(result.objects)
|
||||
.map(([address, obj]) => {
|
||||
let objX = parseInt(String(obj.get(x)));
|
||||
let objY = parseInt(String(obj.get(y)));
|
||||
|
||||
if (objX && objY) {
|
||||
return {
|
||||
address,
|
||||
x: objX,
|
||||
y: objY,
|
||||
};
|
||||
}
|
||||
})
|
||||
.filter(Boolean);
|
||||
});
|
||||
loadPoints();
|
||||
}
|
||||
}
|
||||
|
||||
let selectorCoords: [number, number] | null = null;
|
||||
|
||||
onMount(async () => {
|
||||
const d3 = await import("d3");
|
||||
|
||||
|
@ -96,7 +102,7 @@
|
|||
.on("zoom", zoomed);
|
||||
|
||||
function zoomed({ transform }: { transform: ZoomTransform }) {
|
||||
const points = d3.select(".points");
|
||||
const points = d3.select(".content");
|
||||
points.style(
|
||||
"transform",
|
||||
`translate(${transform.x}px, ${transform.y}px) scale(${transform.k})`
|
||||
|
@ -116,15 +122,40 @@
|
|||
// not using offsetXY because `translate` transforms on .inner mess it up
|
||||
const viewBBox = (view.node() as HTMLElement).getBoundingClientRect();
|
||||
const [x, y] = d3
|
||||
.zoomTransform(d3.select(".points").node() as HTMLElement)
|
||||
.zoomTransform(d3.select(".content").node() as HTMLElement)
|
||||
.invert([ev.clientX - viewBBox.left, ev.clientY - viewBBox.top]);
|
||||
|
||||
currentX = xScale.invert(x);
|
||||
currentY = yScale.invert(y);
|
||||
});
|
||||
|
||||
d3.select(".view").call(zoom);
|
||||
d3.select(".view")
|
||||
.call(zoom)
|
||||
.on("dblclick.zoom", (ev: MouseEvent) => {
|
||||
selectorCoords = [currentX, currentY];
|
||||
});
|
||||
loaded = true;
|
||||
});
|
||||
|
||||
async function onSelectorInput(ev: CustomEvent<IValue>) {
|
||||
const [xValue, yValue] = selectorCoords;
|
||||
selectorCoords = null;
|
||||
console.log("HERE");
|
||||
await Promise.all(
|
||||
[
|
||||
[x, xValue],
|
||||
[y, yValue],
|
||||
].map(([axis, value]: [string, number]) =>
|
||||
putEntityAttribute(ev.detail.c as string, axis, {
|
||||
t: "Number",
|
||||
c: value,
|
||||
})
|
||||
)
|
||||
);
|
||||
console.log("THERE");
|
||||
await loadPoints();
|
||||
console.log("OVERHER");
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="surface">
|
||||
|
@ -147,14 +178,14 @@
|
|||
{$i18n.t("Current position")}:
|
||||
<div class="label">
|
||||
<em>X:</em>
|
||||
{x} = {(Math.round(currentX * 100) / 100).toLocaleString("en", {
|
||||
{x || "?"} = {(Math.round(currentX * 100) / 100).toLocaleString("en", {
|
||||
useGrouping: false,
|
||||
minimumFractionDigits: 2,
|
||||
})}
|
||||
</div>
|
||||
<div class="label">
|
||||
<em>Y:</em>
|
||||
{y} = {(Math.round(currentY * 100) / 100).toLocaleString("en", {
|
||||
{y || "?"} = {(Math.round(currentY * 100) / 100).toLocaleString("en", {
|
||||
useGrouping: false,
|
||||
minimumFractionDigits: 2,
|
||||
})}
|
||||
|
@ -172,7 +203,24 @@
|
|||
<Spinner centered="absolute" />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="points">
|
||||
<div class="content">
|
||||
{#if selectorCoords !== null}
|
||||
<div
|
||||
class="point selector"
|
||||
style="
|
||||
left: {selectorCoords[0]}px;
|
||||
top: {viewHeight - selectorCoords[1]}px"
|
||||
>
|
||||
<Selector
|
||||
type="value"
|
||||
valueTypes={["Address"]}
|
||||
on:input={onSelectorInput}
|
||||
on:focus={(ev) => {
|
||||
if (!ev.detail) selectorCoords = null;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{#each points as point}
|
||||
<div
|
||||
class="point"
|
||||
|
@ -180,7 +228,7 @@
|
|||
>
|
||||
<div class="inner">
|
||||
{#if viewMode == "link"}
|
||||
<UpObject point link address={point.address} />
|
||||
<UpObject link address={point.address} />
|
||||
{:else if viewMode == "card"}
|
||||
<UpObjectCard address={point.address} />
|
||||
{:else if viewMode == "preview"}
|
||||
|
@ -239,7 +287,7 @@
|
|||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.points {
|
||||
.content {
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue