Skip to content

Commit

Permalink
Merge pull request #39 from DHBern/32-devilstable-make-detail-scrollable
Browse files Browse the repository at this point in the history
32 Devilstable: make Detail scrollable
  • Loading branch information
vvvyyynet authored Jan 7, 2025
2 parents eaaa740 + 25f1b4f commit b71f9c2
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 49 deletions.
30 changes: 17 additions & 13 deletions src/routes/Brush.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
<script>
import * as d3 from 'd3';
import {
DATA_MIN,
DATA_MAX,
BRUSH_WINDOW_DEFAULT_START,
BRUSH_WINDOW_DEFAULT_END
} from './Devilstable_DEFAULTS.json';
let marginTop = 20;
let marginRight = 0;
Expand All @@ -20,12 +26,10 @@
*/
let gBrush = $state();
/** @type {{width?: number, height?: number, DATA_MIN?: number, DATA_MAX?: number, data?: {values: boolean[], label: string}[], brushE: function}} */
/** @type {{width?: number, height?: number, data?: {values: boolean[], label: string}[], selection: {start: number, end: number}}} */
let {
width = 400,
height = 150,
DATA_MIN = 1,
DATA_MAX = 827,
data = [
{
label: 'D',
Expand All @@ -36,7 +40,7 @@
values: []
}
],
brushE
selection = $bindable()
} = $props();
let mobile = $derived(width > height);
Expand Down Expand Up @@ -95,28 +99,28 @@
.on('brush', (/** @type {{ selection: [number, number]; }} */ e) => {
const from = e.selection[0];
const to = e.selection[1];
if (Math.abs(from - to) <= 180) {
const start = Math.round(valuesDim.invert(from));
const end = Math.round(valuesDim.invert(to));
brushE({ start, end });
// Update range in Details
if (Math.abs(from - to) <= DATA_MAX - DATA_MIN) {
selection.start = Math.round(valuesDim.invert(from));
selection.end = Math.round(valuesDim.invert(to));
}
})
.on('end', (/** @type {{ selection: [number, number]; }} */ e) => {
const from = e.selection[0];
const to = e.selection[1];
if (Math.abs(from - to) > 180) {
const start = Math.round(valuesDim.invert(from));
const end = Math.round(valuesDim.invert(to));
brushE({ start, end });
// Update range in Details
if (Math.abs(from - to) > DATA_MAX - DATA_MIN) {
selection.start = Math.round(valuesDim.invert(from));
selection.end = Math.round(valuesDim.invert(to));
}
});
});
$effect(() => {
d3.select(gBrush)
.call(brush)
.call(brush.move, [valuesDim(DATA_MIN), valuesDim(100)]);
.call(brush.move, [valuesDim(selection.start), valuesDim(selection.end)]);
});
let chunkedData = $derived(
data.map((dataObject) => {
Expand Down
92 changes: 69 additions & 23 deletions src/routes/Detail.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,16 @@
import { computePosition, shift, flip, offset } from '@floating-ui/dom';
import { base } from '$app/paths';
import { summaryLabel } from '$lib/constants';
import {
DATA_MIN,
DATA_MAX,
SCROLL_SPEED,
ZOOM_INCREMENT,
ZOOM_MINIMUM_WINDOW_SIZE
} from './Devilstable_DEFAULTS.json';
/** @type {{codices: any, width?: number, height?: number, data_start?: number, data?: {values: boolean[], label: string}[]}} */
let { codices, width = 400, height = 400, data_start = 1, data = [] } = $props();
/** @type {{codices: any, width?: number, height?: number,data?: {values: boolean[], label: string}[], selection: {start: number, end: number}}} */
let { codices, width = 400, height = 400, data = [], selection = $bindable() } = $props();
let marginTop = 30;
let marginRight = 0;
let marginBottom = 20;
Expand Down Expand Up @@ -38,6 +45,44 @@
*/
let svgElement = $state();
const handleWheel = (/** @type {{ deltaY: any; }} */ event) => {
event.preventDefault();
// Check if the CTRL key is held down during the scroll
const isZooming = event.ctrlKey;
let delta = selection.end - selection.start;
if (isZooming) {
// Zoom in
if (event.deltaY > 0) {
// Zoom out
selection.start = Math.max(DATA_MIN, selection.start - ZOOM_INCREMENT);
selection.end = Math.min(DATA_MAX, selection.end + ZOOM_INCREMENT);
} else {
// Zoom in
if (delta > ZOOM_MINIMUM_WINDOW_SIZE) {
selection.start = Math.max(DATA_MIN, selection.start + ZOOM_INCREMENT);
selection.end = Math.max(
selection.start + ZOOM_MINIMUM_WINDOW_SIZE,
Math.min(DATA_MAX, selection.end - ZOOM_INCREMENT)
);
}
}
} else {
// Scroll
if (event.deltaY > 0) {
// Scroll down
selection.end = Math.min(DATA_MAX, selection.end + SCROLL_SPEED);
selection.start = selection.end - delta;
} else {
// Scroll up
selection.start = Math.max(DATA_MIN, selection.start - SCROLL_SPEED);
selection.end = selection.start + delta;
}
}
};
const handleMouseMove = (/** @type {{ clientX: any; clientY: any; }} */ event) => {
mousePos = d3.pointer(event, svgElement);
if (mousePos[0] >= marginLeft && mousePos[1] >= marginTop) {
Expand Down Expand Up @@ -117,27 +162,26 @@
};
let contigousData = $derived(
data.map((d) => {
if (d.label === 'fr') {
return d;
}
// skip for label 'fr'
if (d.label === 'fr') return d;
// init
let contiguousRanges = [];
let start = 0;
let start = null;
//
for (let i = 0; i < d.values.length; i++) {
if (d.values[i]) {
if (start === 0) {
// console.log(d.label, i, data_start);
start = i + data_start;
}
} else {
if (start !== 0) {
contiguousRanges.push([start, i + data_start - 1]);
start = 0;
}
if (d.values[i] && start === null) {
// start a new range
start = i + selection.start;
} else if (!d.values[i] && start !== null) {
contiguousRanges.push([start, i + selection.start - 1]);
start = null; // reset start
}
}
if (start !== 0) {
// console.log(d.label, start, d.values.length - 1 + data_start);
contiguousRanges.push([start, d.values.length - 1 + data_start]);
// if range is still open at the end
if (start !== null) {
contiguousRanges.push([start, d.values.length + selection.start - 1]);
}
return {
label: d.label,
Expand All @@ -158,14 +202,14 @@
let y = $derived(
d3.scaleLinear(
// Domain: from bottom to top: at the bottom is the last selected verse, at the top is the first selected verse
[data_start + data[0]?.values.length, data_start],
[selection.start + data[0]?.values.length, selection.start],
[height - marginBottom, marginTop]
)
);
let verse = $derived(Math.floor(y.invert(mousePos[1])));
let manuscript = $derived(
scaleBandInvert(x)(mousePos[0]) === 'fr'
? data.find((d) => d.label === 'fr')?.values[verse - data_start] || 'fr'
? data.find((d) => d.label === 'fr')?.values[verse - selection.start] || 'fr'
: scaleBandInvert(x)(mousePos[0])
);
// $inspect(contigousData);
Expand Down Expand Up @@ -220,6 +264,7 @@
});
}
})
.on('blur', (e) => {
const reference = e.currentTarget;
const popup =
Expand Down Expand Up @@ -263,7 +308,7 @@
{/each}
{#each data.find((d) => d.label === 'fr')?.values || [] as fraction, i}
{#if Array.isArray(fraction)}
{@const verse = i + data_start}
{@const verse = i + selection.start}
<div
class="card p-1 variant-filled-primary top-0 left-0 w-max absolute opacity-0"
bind:this={popupFractions[verse]}
Expand All @@ -282,6 +327,7 @@
{/if}
{/each}
<div
onwheel={handleWheel}
onmousemove={handleMouseMove}
onmouseleave={(_e) => {
floating.style.display = 'none';
Expand All @@ -300,7 +346,7 @@
{#each sigla.values as values, i}
{#if values}
{#if isNaN(values[1])}
{@const verseNumber = i + data_start}
{@const verseNumber = i + selection.start}
{#if values.length === 1}
<a
href={`${base}/textzeugen/${values[0]}/${verseNumber}`}
Expand Down
26 changes: 13 additions & 13 deletions src/routes/Devilstable.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
import Brush from './Brush.svelte';
import Detail from './Detail.svelte';
import { summaryLabel } from '$lib/constants';
const DATA_MAX = 827;
import {
DATA_MAX,
BRUSH_WINDOW_DEFAULT_START,
BRUSH_WINDOW_DEFAULT_END
} from './Devilstable_DEFAULTS.json';
const brushDimension = 200;
const brushDimensionWithSafetyPixel = brushDimension + 1; // fixes a glitch, where Brush and Detail don't fit next to each other on PageResize.
Expand Down Expand Up @@ -71,13 +73,14 @@
})
);
let selection = $state({
start: 1,
end: 100
start: BRUSH_WINDOW_DEFAULT_START,
end: BRUSH_WINDOW_DEFAULT_END
});
let detailData = $derived(
boolData.map((d) => {
return { label: d.label, values: d.values.slice(selection.start - 1, selection.end) };
})
boolData.map(({ values, ...rest }) => ({
values: values.slice(selection.start - 1, selection.end),
...rest
}))
);
</script>
Expand Down Expand Up @@ -139,17 +142,14 @@
width={mobile ? width : brushDimension}
height={mobile ? brushDimension : height}
data={boolData.filter((d) => d.label !== summaryLabel)}
brushE={(/** @type {{ start: number; end: number; }} */ e) => {
selection.start = e.start;
selection.end = e.end;
}}
bind:selection
/>
<Detail
{codices}
width={mobile ? width : width - brushDimensionWithSafetyPixel}
height={mobile ? height - brushDimension : height}
data={detailData}
data_start={selection.start}
bind:selection
/>
<style>
Expand Down
9 changes: 9 additions & 0 deletions src/routes/Devilstable_DEFAULTS.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"DATA_MIN": 1,
"DATA_MAX": 827,
"BRUSH_WINDOW_DEFAULT_START": 1,
"BRUSH_WINDOW_DEFAULT_END": 100,
"SCROLL_SPEED": 50,
"ZOOM_INCREMENT": 40,
"ZOOM_MINIMUM_WINDOW_SIZE": 50
}

0 comments on commit b71f9c2

Please sign in to comment.