feat: double click on surface to add a point
This commit is contained in:
parent
1539860da8
commit
c690992f93
1 changed files with 74 additions and 26 deletions
|
@ -1,6 +1,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import UpObject from "../components/display/UpObject.svelte";
|
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 Selector from "../components/utils/Selector.svelte";
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
import type { ZoomTransform } from "d3";
|
import type { ZoomTransform } from "d3";
|
||||||
|
@ -8,6 +8,7 @@
|
||||||
import UpObjectCard from "../components/display/UpObjectCard.svelte";
|
import UpObjectCard from "../components/display/UpObjectCard.svelte";
|
||||||
import BlobPreview from "../components/display/BlobPreview.svelte";
|
import BlobPreview from "../components/display/BlobPreview.svelte";
|
||||||
import { i18n } from "../i18n";
|
import { i18n } from "../i18n";
|
||||||
|
import type { IValue } from "upend/types";
|
||||||
|
|
||||||
const urlSearch = window.location.href.substring(
|
const urlSearch = window.location.href.substring(
|
||||||
window.location.href.indexOf("?")
|
window.location.href.indexOf("?")
|
||||||
|
@ -31,29 +32,34 @@
|
||||||
y: number;
|
y: number;
|
||||||
}
|
}
|
||||||
let points: IPoint[] = [];
|
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) {
|
if (x && y) {
|
||||||
points = [];
|
loadPoints();
|
||||||
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);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let selectorCoords: [number, number] | null = null;
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
const d3 = await import("d3");
|
const d3 = await import("d3");
|
||||||
|
|
||||||
|
@ -96,7 +102,7 @@
|
||||||
.on("zoom", zoomed);
|
.on("zoom", zoomed);
|
||||||
|
|
||||||
function zoomed({ transform }: { transform: ZoomTransform }) {
|
function zoomed({ transform }: { transform: ZoomTransform }) {
|
||||||
const points = d3.select(".points");
|
const points = d3.select(".content");
|
||||||
points.style(
|
points.style(
|
||||||
"transform",
|
"transform",
|
||||||
`translate(${transform.x}px, ${transform.y}px) scale(${transform.k})`
|
`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
|
// not using offsetXY because `translate` transforms on .inner mess it up
|
||||||
const viewBBox = (view.node() as HTMLElement).getBoundingClientRect();
|
const viewBBox = (view.node() as HTMLElement).getBoundingClientRect();
|
||||||
const [x, y] = d3
|
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]);
|
.invert([ev.clientX - viewBBox.left, ev.clientY - viewBBox.top]);
|
||||||
|
|
||||||
currentX = xScale.invert(x);
|
currentX = xScale.invert(x);
|
||||||
currentY = yScale.invert(y);
|
currentY = yScale.invert(y);
|
||||||
});
|
});
|
||||||
|
|
||||||
d3.select(".view").call(zoom);
|
d3.select(".view")
|
||||||
|
.call(zoom)
|
||||||
|
.on("dblclick.zoom", (ev: MouseEvent) => {
|
||||||
|
selectorCoords = [currentX, currentY];
|
||||||
|
});
|
||||||
loaded = true;
|
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>
|
</script>
|
||||||
|
|
||||||
<div class="surface">
|
<div class="surface">
|
||||||
|
@ -147,14 +178,14 @@
|
||||||
{$i18n.t("Current position")}:
|
{$i18n.t("Current position")}:
|
||||||
<div class="label">
|
<div class="label">
|
||||||
<em>X:</em>
|
<em>X:</em>
|
||||||
{x} = {(Math.round(currentX * 100) / 100).toLocaleString("en", {
|
{x || "?"} = {(Math.round(currentX * 100) / 100).toLocaleString("en", {
|
||||||
useGrouping: false,
|
useGrouping: false,
|
||||||
minimumFractionDigits: 2,
|
minimumFractionDigits: 2,
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
<div class="label">
|
<div class="label">
|
||||||
<em>Y:</em>
|
<em>Y:</em>
|
||||||
{y} = {(Math.round(currentY * 100) / 100).toLocaleString("en", {
|
{y || "?"} = {(Math.round(currentY * 100) / 100).toLocaleString("en", {
|
||||||
useGrouping: false,
|
useGrouping: false,
|
||||||
minimumFractionDigits: 2,
|
minimumFractionDigits: 2,
|
||||||
})}
|
})}
|
||||||
|
@ -172,7 +203,24 @@
|
||||||
<Spinner centered="absolute" />
|
<Spinner centered="absolute" />
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/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}
|
{#each points as point}
|
||||||
<div
|
<div
|
||||||
class="point"
|
class="point"
|
||||||
|
@ -180,7 +228,7 @@
|
||||||
>
|
>
|
||||||
<div class="inner">
|
<div class="inner">
|
||||||
{#if viewMode == "link"}
|
{#if viewMode == "link"}
|
||||||
<UpObject point link address={point.address} />
|
<UpObject link address={point.address} />
|
||||||
{:else if viewMode == "card"}
|
{:else if viewMode == "card"}
|
||||||
<UpObjectCard address={point.address} />
|
<UpObjectCard address={point.address} />
|
||||||
{:else if viewMode == "preview"}
|
{:else if viewMode == "preview"}
|
||||||
|
@ -239,7 +287,7 @@
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.points {
|
.content {
|
||||||
transform-origin: 0 0;
|
transform-origin: 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue