frontend/src/components/trace/hat-graph-node.ts
2021-08-12 15:59:57 +02:00

150 lines
3.7 KiB
TypeScript

import {
css,
LitElement,
PropertyValues,
html,
TemplateResult,
svg,
} from "lit";
import { customElement, property } from "lit/decorators";
import { NODE_SIZE, SPACING } from "./hat-graph-const";
/**
* @attribute active
* @attribute track
*/
@customElement("hat-graph-node")
export class HatGraphNode extends LitElement {
@property() iconPath?: string;
@property({ reflect: true, type: Boolean }) disabled?: boolean;
@property({ reflect: true, type: Boolean }) graphStart?: boolean;
@property({ type: Boolean, attribute: "nofocus" }) noFocus = false;
@property({ reflect: true, type: Number }) badge?: number;
protected updated(changedProps: PropertyValues) {
if (changedProps.has("noFocus")) {
if (!this.hasAttribute("tabindex") && !this.noFocus) {
this.setAttribute("tabindex", "0");
} else if (changedProps.get("noFocus") !== undefined && this.noFocus) {
this.removeAttribute("tabindex");
}
}
}
protected render(): TemplateResult {
const height = NODE_SIZE + (this.graphStart ? 2 : SPACING + 1);
const width = SPACING + NODE_SIZE;
return html`
<svg
viewBox="-${Math.ceil(width / 2)} -${this.graphStart
? Math.ceil(height / 2)
: Math.ceil((NODE_SIZE + SPACING * 2) / 2)} ${width} ${height}"
>
${this.graphStart
? ``
: svg`
<path
class="connector"
d="
M 0 ${-SPACING - NODE_SIZE / 2}
L 0 0
"
line-caps="round"
/>
`}
<g class="node">
<circle cx="0" cy="0" r=${NODE_SIZE / 2} />
}
${this.badge
? svg`
<g class="number">
<circle
cx="8"
cy=${-NODE_SIZE / 2}
r="8"
></circle>
<text
x="8"
y=${-NODE_SIZE / 2}
text-anchor="middle"
alignment-baseline="middle"
>${this.badge > 9 ? "9+" : this.badge}</text>
</g>
`
: ""}
<g style="pointer-events: none" transform="translate(${-12} ${-12})">
${this.iconPath ? svg`<path class="icon" d=${this.iconPath}/>` : ""}
</g>
</g>
</svg>
`;
}
static get styles() {
return css`
:host {
display: flex;
flex-direction: column;
width: calc(var(--hat-graph-node-size) + var(--hat-graph-spacing));
height: calc(var(--hat-graph-node-size) + var(--hat-graph-spacing) + 1);
}
:host([graphStart]) {
height: calc(var(--hat-graph-node-size) + 2);
}
:host([track]) {
--stroke-clr: var(--track-clr);
--icon-clr: var(--default-icon-clr);
}
:host([active]) circle {
--stroke-clr: var(--active-clr);
--icon-clr: var(--default-icon-clr);
}
:host(:focus) {
outline: none;
}
:host(:hover) circle {
--stroke-clr: var(--hover-clr);
--icon-clr: var(--default-icon-clr);
}
:host([disabled]) circle {
stroke: var(--disabled-clr);
}
svg {
width: 100%;
height: 100%;
}
circle,
path.connector {
stroke: var(--stroke-clr);
stroke-width: 2;
fill: none;
}
circle {
fill: var(--background-clr);
stroke: var(--circle-clr, var(--stroke-clr));
}
.number circle {
fill: var(--track-clr);
stroke: none;
stroke-width: 0;
}
.number text {
font-size: smaller;
}
path.icon {
fill: var(--icon-clr);
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"hat-graph-node": HatGraphNode;
}
}