Skip to content

Commit d71ac00

Browse files
committed
Merge branch 'main' into demo
2 parents 066bf7c + 77504a7 commit d71ac00

55 files changed

Lines changed: 1417 additions & 636 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

index.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
import { createApp } from 'vue'
1515
import App from './web-docs/index.vue'
1616
import * as components from '#base/components'
17+
import PreHighlightElement from './web-docs/script/pre-highlight.js'
18+
customElements.define('pre-highlight', PreHighlightElement)
1719
let app = createApp(App)
1820
for (let c in components) {
1921
app.component(c, components[c])

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "vvplot",
3-
"version": "0.1.2",
3+
"version": "0.1.3",
44
"license": "MIT",
55
"files": [
66
"dist"

src/components/Plot.vue

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ const components = {
1717
VVAction, VVSelection,
1818
}
1919

20+
const opponents = {
21+
x: 'y', y: 'x', h: 'v', v: 'h',
22+
top: 'bottom', bottom: 'top', left: 'right', right: 'left'
23+
}
24+
2025
function _isPropTruthy(v) {
2126
if (v == null) return v
2227
return v === "" || Boolean(v)
@@ -288,7 +293,7 @@ watch(coordRangePreview, (newRange, oldRange) => {
288293

289294
watch(() => range, (newRange, oldRange) => {
290295
for (let key in rangeUpdate) {
291-
rangeUpdate[key]?.(newRange[key])
296+
rangeUpdate[key]?.(newRange[key] + (coordLevels.value[key.charAt(0)] ? 0.5 : 0))
292297
}
293298
emit('rangechange', { ...newRange }, { ...oldRange })
294299
emit('update:range', { ...newRange })
@@ -324,7 +329,7 @@ const expandMult = computed(() => {
324329
else if (typeof y == 'number') y = { min: y, max: y }
325330
return { x, y }
326331
})
327-
const reverse = computed(() => ({
332+
const reverse = reactiveComputed(() => ({
328333
x: _isPropTruthy(primaryAxisConfig.reverse.x) ?? $reverse?.x ?? false,
329334
y: _isPropTruthy(primaryAxisConfig.reverse.y) ?? $reverse?.y ?? false,
330335
}))
@@ -346,7 +351,14 @@ const axes = computed(() => {
346351
$_children, ...etc
347352
}) => {
348353
let orientation = ori[coord]
349-
position = position ?? defaultPos[coord]
354+
position = position ?? "start"
355+
if (position == "start") {
356+
position = { h: "bottom", v: "left" }[orientation]
357+
if (reverse[opponents[coord]]) position = opponents[position]
358+
} else if (position == "end") {
359+
position = { h: "top", v: "right" }[orientation]
360+
if (reverse[opponents[coord]]) position = opponents[position]
361+
}
350362
let action = Object.values($_children ?? {})
351363
.filter(s => typeof s == "function")
352364
.flatMap(s => expandFragment(s()))
@@ -444,7 +456,10 @@ const selections = computed(() => {
444456
resize: resize !== false,
445457
x: xy || Boolean(_isPropTruthy(x)),
446458
y: xy || Boolean(_isPropTruthy(y)),
447-
xmin, xmax, ymin, ymax,
459+
xmin: xmin ?? actionBoundary?.x?.min,
460+
xmax: xmax ?? actionBoundary?.x?.max,
461+
ymin: ymin ?? actionBoundary?.y?.min,
462+
ymax: ymax ?? actionBoundary?.y?.max,
448463
ctrlKey: Boolean(_isPropTruthy(ctrl)),
449464
shiftKey: Boolean(_isPropTruthy(shift)),
450465
altKey: Boolean(_isPropTruthy(alt)),
@@ -464,10 +479,10 @@ const width = defineModel('width')
464479
const height = defineModel('height')
465480
onMounted(() => {
466481
watch(width, (v) => {
467-
wrapperRef.value.style.width = str_c(v, 'px')
482+
wrapperRef.value.style.width = str_c(v, 'px') ?? null
468483
}, { immediate: true })
469484
watch(height, (v) => {
470-
wrapperRef.value.style.height = str_c(v, 'px')
485+
wrapperRef.value.style.height = str_c(v, 'px') ?? null
471486
}, { immediate: true })
472487
})
473488
const { width: w, height: h } = useElementSize(plotRef)
@@ -500,7 +515,8 @@ const wrapperStyle = computed(() => {
500515
})
501516

502517
defineExpose({
503-
serialize() { return serializeSVG(plotRef.value.svg) }
518+
get svg() { return plotRef.value.svg },
519+
serialize() { return serializeSVG(plotRef.value.svg) },
504520
})
505521
</script>
506522
<template>

src/components/core/CoreLegend.vue

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ const { scales, theme } = defineProps({
77
scales: Map,
88
})
99
const guides = computed(() => {
10-
let scale_fns = Array.from(scales.keys())
10+
let guide_scales = new Map(
11+
Array.from(scales).filter(([fn]) => fn.legend !== false)
12+
)
13+
let scale_fns = Array.from(guide_scales.keys())
1114
let keys = scale_fns.map(s => s.key)
1215
let j = 1
1316
for (let i in keys) {
@@ -16,7 +19,7 @@ const guides = computed(() => {
1619
}
1720
let types = scale_fns.map(s => getGuideType(s))
1821
let groups = interaction(keys, types)
19-
return Map.groupBy(scales, (s, i) => groups.categories[groups[i]])
22+
return Map.groupBy(guide_scales, (s, i) => groups.categories[groups[i]])
2023
})
2124
2225
function getGuideType(scale) {

src/components/core/CorePlot.vue

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -310,10 +310,36 @@ function isInPlot(event) {
310310
event.clientY < rect.bottom - panel.b
311311
}
312312
313+
function dispatchPointerEvent(e) {
314+
if (!layers.value || e._vhandled || !isInPlot(e)) return e
315+
let canvasLayers = Array.from(layers.value).filter(l => l.render == 'canvas')
316+
if (!canvasLayers.length) return e
317+
// clone event
318+
let options = {}
319+
for (let key in e) options[key] = e[key]
320+
options.bubbles = false
321+
let event = new PointerEvent(e.type, options)
322+
for (let key of Object.keys(e)) {
323+
let prop = Object.getOwnPropertyDescriptor(event, key)
324+
if (prop && !prop.writable && !prop.set) continue
325+
event[key] = e[key]
326+
}
327+
// dispatch to canvas layers
328+
for (let i = layers.value.length - 1; i >= 0; i--)
329+
if (layers.value[i].dispatchEvent(event) === false) e.preventDefault()
330+
for (let key of Object.keys(event)) {
331+
let prop = Object.getOwnPropertyDescriptor(event, key)
332+
if (prop && !prop.writable && !prop.set) continue
333+
e[key] = event[key]
334+
}
335+
return event._vhandled ? event : e
336+
}
337+
313338
let moveTimer, movementX = 0, movementY = 0
314339
function svgPointerdown(e) {
315340
let coord = getCoord(e)
316-
emit('pointerdown', e, coord)
341+
emit('pointerdown', dispatchPointerEvent(e), coord)
342+
if (props.clip && !isInPlot(e)) return
317343
let svg = e.currentTarget
318344
let pointerMoved = false
319345
function detectMove(ev) {
@@ -326,16 +352,10 @@ function svgPointerdown(e) {
326352
e.target.addEventListener('pointerup', function (ev) {
327353
e.target.removeEventListener('pointermove', detectMove)
328354
if (!pointerMoved) {
329-
let coord = getCoord(ev)
330-
emit('singleclick', new PointerEvent("singleclick", ev), coord)
331-
if (isInPlot(e) && layers.value) {
332-
if (ev.button == 0) {
333-
layers.value.forEach(layer => layer.dispatchEvent(new PointerEvent("click", ev)))
334-
}
335-
}
355+
let event = new PointerEvent("singleclick", ev)
356+
ev.target.dispatchEvent(event)
336357
}
337358
}, { once: true })
338-
if (props.clip && !isInPlot(e)) return
339359
let sel = props.selections.find(s => ["buttons", "ctrlKey", "shiftKey", "altKey", "metaKey"].every(k => s[k] == e[k]))
340360
if (sel) {
341361
let { x = false, y = false, "min-range-x": mrx = 0, "min-range-y": mry = 0 } = sel
@@ -414,7 +434,7 @@ function svgPointerdown(e) {
414434
if (sel.dismissible !== true && ["xmin", "xmax", "ymin", "ymax"].every(k => model?.[k] == null)) return
415435
let res = {}, event = new PointerEvent("select", e)
416436
sel["onUpdate:modelValue"]?.(res)
417-
if (!emitEvent(sel["onCancel"], dropNull(res), event)) {
437+
if (!emitEvent(sel["onDismiss"], dropNull(res), event)) {
418438
emit('select', dropNull(res), event)
419439
}
420440
}
@@ -463,7 +483,7 @@ function svgPointerdown(e) {
463483
let wheelDelta = 0, wheelTimer
464484
function svgWheel(e) {
465485
let coord = getCoord(e)
466-
emit('wheel', e, coord)
486+
emit('wheel', dispatchPointerEvent(e), coord)
467487
if (props.clip && !isInPlot(e)) return
468488
let act = props.action.find(a => ["zoom", "nudge"].includes(a.action) && ["ctrlKey", "shiftKey", "altKey", "metaKey"].every(k => a[k] == e[k]))
469489
if (!act || !act.x && !act.y) return
@@ -551,15 +571,16 @@ function applyTransform(act, event) {
551571
}
552572
const svgVOn = {
553573
pointerdown: svgPointerdown,
554-
pointerup(e) { emit('pointerup', e, getCoord(e)) },
574+
pointerup(e) { emit('pointerup', dispatchPointerEvent(e), getCoord(e)) },
555575
pointerover(e) { emit('pointerover', e, getCoord(e)) },
556576
pointerout(e) { emit('pointerout', e, getCoord(e)) },
557577
pointerenter(e) { emit('pointerenter', e, getCoord(e)) },
558578
pointerleave(e) { emit('pointerleave', e, getCoord(e)) },
559-
dblclick(e) { emit('dblclick', e, getCoord(e)) },
560-
click(e) { emit('click', e, getCoord(e)) },
561-
contextmenu(e) { emit('contextmenu', e, getCoord(e)) },
562-
pointermove(e) { emit('pointermove', e, getCoord(e)) },
579+
click(e) { emit('click', dispatchPointerEvent(e), getCoord(e)) },
580+
contextmenu(e) { emit('contextmenu', dispatchPointerEvent(e), getCoord(e)) },
581+
singleclick(e) { emit('singleclick', dispatchPointerEvent(e), getCoord(e)) },
582+
dblclick(e) { emit('dblclick', dispatchPointerEvent(e), getCoord(e)) },
583+
pointermove(e) { emit('pointermove', dispatchPointerEvent(e), getCoord(e)) },
563584
wheel: svgWheel,
564585
}
565586
@@ -653,7 +674,7 @@ const axes = computed(() => {
653674
</defs>
654675
<rect :transform="`translate(${panel.left}, ${panel.top})`" :width="panel.width" :height="panel.height"
655676
:fill="theme.plot.background" v-if="theme.plot.background"></rect>
656-
<g :transform="`translate(${panel.left}, ${panel.top})`">
677+
<g :transform="`translate(${panel.left}, ${panel.top})`" style="pointer-events: none;">
657678
<CoreGridH v-if="theme.grid.h" v-bind="gridBreaks.h" :layout="innerRect" :theme="theme.grid.h"
658679
:activeTransform="activeTransform" :coord2pos="coord2pos" :transition="transition" />
659680
<CoreGridV v-if="theme.grid.v" v-bind="gridBreaks.v" :layout="innerRect" :theme="theme.grid.v"

src/components/core/axis/CoreAxisH.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ const tickLines = computed(() => {
7171
style: { transition },
7272
})
7373
}
74-
return result.filter(t => t.stroke != null)
74+
return result.filter(t => t.stroke != null).sort((a, b) => a.x1 - b.x1)
7575
})
7676
const tickTexts = computed(() => {
7777
let isTop = theme.tick_position == "top"
@@ -109,7 +109,7 @@ const tickTexts = computed(() => {
109109
},
110110
})
111111
}
112-
return result.filter(t => t.text.fill != null)
112+
return result.filter(t => t.text.fill != null).sort((a, b) => a.text.x - b.text.x)
113113
})
114114
115115
const iRef = useTemplateRef("i")

src/components/core/axis/CoreAxisV.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ const tickLines = computed(() => {
7171
style: { transition },
7272
})
7373
}
74-
return result.filter(t => t.stroke != null)
74+
return result.filter(t => t.stroke != null).sort((a, b) => a.y1 - b.y1)
7575
})
7676
const tickTexts = computed(() => {
7777
let isRight = theme.tick_position == "right"
@@ -109,7 +109,7 @@ const tickTexts = computed(() => {
109109
}
110110
})
111111
}
112-
return result.filter(t => t.text.fill != null)
112+
return result.filter(t => t.text.fill != null).sort((a, b) => a.text.y - b.text.y)
113113
})
114114
115115
const iRef = useTemplateRef("i")

src/components/core/element/CoreCurve.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ const binds = computed(() => {
1616
let interpolatorFn = interpolators[interpolate] ?? d3.curveNatural
1717
return {
1818
d: d3.line().curve(interpolatorFn)(points.map(p => [p.x, p.y])),
19-
fill: fill,
20-
stroke: color,
19+
fill: fill || null,
20+
stroke: color || null,
2121
'stroke-width': linewidth,
22-
'stroke-opacity': alpha,
22+
'stroke-opacity': alpha == 1 ? null : alpha,
2323
'stroke-dasharray': parseLineType(linetype),
2424
transform: (translateX || translateY) ? `translate(${translateX}, ${translateY})` : null,
2525
}

src/components/core/element/CoreLine.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ const {
1515
const binds = computed(() => {
1616
return {
1717
x1, x2, y1, y2,
18-
stroke: color,
18+
stroke: color || null,
1919
'stroke-width': linewidth,
20-
'stroke-opacity': alpha,
20+
'stroke-opacity': alpha == 1 ? null : alpha,
2121
'stroke-dasharray': parseLineType(linetype),
2222
transform: (translateX || translateY) ? `translate(${translateX}, ${translateY})` : null,
2323
}

src/components/core/element/CorePoint.vue

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ const paths = {
2222
const binds = computed(() => {
2323
let d = shape?.startsWith?.("path:") ? shape?.slice?.(5) : paths[shape]
2424
let result = {
25-
fill: color,
26-
'fill-opacity': alpha,
27-
stroke,
25+
fill: color || null,
26+
'fill-opacity': alpha == 1 ? null : alpha,
27+
stroke: stroke || null,
2828
'stroke-width': linewidth,
29-
'stroke-opacity': alpha,
29+
'stroke-opacity': alpha == 1 ? null : alpha,
3030
'stroke-dasharray': parseLineType(linetype),
3131
}
3232
if (d != null) {

0 commit comments

Comments
 (0)