feat: scaffold blueprints SPA with List functional-blueprint illustration
This commit is contained in:
120
src/components/CompositionMap.vue
Normal file
120
src/components/CompositionMap.vue
Normal file
@@ -0,0 +1,120 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue"
|
||||
import { COMP_CAPS, COMP_FUNCS, COMP_LINKS } from "@/data/listBlueprint"
|
||||
|
||||
const PAL = {
|
||||
boxFill: "#103763",
|
||||
boxStroke: "#3d5f85",
|
||||
ink: "#dbe9f7",
|
||||
inkFaint: "#4f7099",
|
||||
cyan: "#8fd0ff",
|
||||
amber: "#ffb84d",
|
||||
}
|
||||
|
||||
const W = 640
|
||||
const lx = 24
|
||||
const lw = 170
|
||||
const rx = 446
|
||||
const rw = 170
|
||||
const bh = 34
|
||||
const top = 16
|
||||
const lstep = 40
|
||||
const rstep = 52
|
||||
const H = top + COMP_CAPS.length * lstep + 16
|
||||
|
||||
const capY = (i: number) => top + i * lstep + bh / 2
|
||||
const funcY = (i: number) => top + i * rstep + bh / 2
|
||||
|
||||
const capIndex = computed(
|
||||
() => Object.fromEntries(COMP_CAPS.map((c, i) => [c.id, i])) as Record<string, number>,
|
||||
)
|
||||
const funcIndex = computed(
|
||||
() => Object.fromEntries(COMP_FUNCS.map((f, i) => [f, i])) as Record<string, number>,
|
||||
)
|
||||
|
||||
const leftBoxes = computed(() =>
|
||||
COMP_CAPS.map((c, i) => ({ ...c, x: lx, y: capY(i) - bh / 2, w: lw, h: bh })),
|
||||
)
|
||||
const rightBoxes = computed(() =>
|
||||
COMP_FUNCS.map((f, i) => ({ label: f, x: rx, y: funcY(i) - bh / 2, w: rw, h: bh })),
|
||||
)
|
||||
|
||||
const edges = computed(() =>
|
||||
COMP_LINKS.map(([a, b]) => {
|
||||
const y1 = capY(capIndex.value[a])
|
||||
const y2 = funcY(funcIndex.value[b])
|
||||
const x1 = lx + lw
|
||||
const x2 = rx
|
||||
const mx = (x1 + x2) / 2
|
||||
const color = a === "core" ? PAL.inkFaint : PAL.cyan
|
||||
return {
|
||||
key: `${a}-${b}`,
|
||||
d: `M${x1} ${y1} C ${mx} ${y1}, ${mx} ${y2}, ${x2} ${y2}`,
|
||||
color,
|
||||
x2,
|
||||
y2,
|
||||
}
|
||||
}),
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<svg :viewBox="`0 0 ${W} ${H}`" role="img" aria-label="Composition map" class="schematic">
|
||||
<path
|
||||
v-for="e in edges"
|
||||
:key="e.key"
|
||||
:d="e.d"
|
||||
fill="none"
|
||||
stroke-width="1.4"
|
||||
:stroke="e.color"
|
||||
opacity="0.55"
|
||||
/>
|
||||
<circle v-for="e in edges" :key="`c-${e.key}`" :cx="e.x2" :cy="e.y2" r="2.4" :fill="e.color" />
|
||||
|
||||
<g v-for="b in leftBoxes" :key="b.id">
|
||||
<rect
|
||||
:x="b.x"
|
||||
:y="b.y"
|
||||
:width="b.w"
|
||||
:height="b.h"
|
||||
rx="2"
|
||||
:fill="PAL.boxFill"
|
||||
:stroke="b.core ? PAL.amber : PAL.boxStroke"
|
||||
stroke-width="1.5"
|
||||
/>
|
||||
<text :x="b.x + 12" :y="b.y + 15" :fill="PAL.ink" font-size="12">{{ b.label }}</text>
|
||||
<text :x="b.x + 12" :y="b.y + 27" :fill="PAL.inkFaint" font-size="9.5">{{ b.sub }}</text>
|
||||
</g>
|
||||
|
||||
<g v-for="b in rightBoxes" :key="b.label">
|
||||
<rect
|
||||
:x="b.x"
|
||||
:y="b.y"
|
||||
:width="b.w"
|
||||
:height="b.h"
|
||||
rx="2"
|
||||
:fill="PAL.boxFill"
|
||||
:stroke="PAL.amber"
|
||||
stroke-width="1.5"
|
||||
/>
|
||||
<text :x="b.x + 12" :y="b.y + 21" :fill="PAL.amber" font-size="12">{{ b.label }}</text>
|
||||
</g>
|
||||
|
||||
<text :x="lx" :y="H - 4" :fill="PAL.inkFaint" font-size="10" letter-spacing="1.5">
|
||||
CAPABILITIES (compose onto host)
|
||||
</text>
|
||||
<text :x="rx" :y="H - 4" :fill="PAL.inkFaint" font-size="10" letter-spacing="1.5">
|
||||
FUNCTIONS
|
||||
</text>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.schematic {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
margin: 0 auto;
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user