Skip to content

Instantly share code, notes, and snippets.

@klyngen
Created February 19, 2021 23:52
Show Gist options
  • Save klyngen/fec8ca91599ac11939a071791c1b90fc to your computer and use it in GitHub Desktop.
Save klyngen/fec8ca91599ac11939a071791c1b90fc to your computer and use it in GitHub Desktop.
import {query, css, html, customElement, LitElement, property, TemplateResult} from 'lit-element';
@customElement("overtime-visualizer")
export class OvertimeVisualizer extends LitElement {
@query("#wrapper")
wrapper: HTMLDivElement;
@query("#test")
test: HTMLParagraphElement;
@property()
overtimeData: OvertimeData[] = [];
@property({type: Number})
subtract = 0;
loaded = false;
private textWillOverflow(data: OvertimeData): boolean {
this.test.innerText = data.name;
const pixels: number = this.getRelativeWidth(data.value);
if (pixels < this.test.clientWidth)
return true;
return false;
}
get filteredData(): OvertimeData[] {
const sorted = this.overtimeData.sort((a, b) => a.priority - b.priority);
if (this.overtimeData) {
let remaining = this.subtract;
const clones: OvertimeData[] = [];
sorted.forEach(item => {
const clone = {...item};
if (clone.value >= remaining) {
clone.value -= remaining;
remaining = 0;
}
if (clone.value < remaining) {
remaining -= item.value;
clone.value = 0;
}
if (this.textWillOverflow(clone)) {
clone.name = clone.name[0];
}
if (clone.value > 0)
clones.push(clone);
});
return clones;
}
return [];
}
static get styles() {
return css`
.overtime {
display: flex;
}
.overtime-component-color-bar {
height: 30px;
display: flex;
justify-content: center;
align-items: center;
font-weight: 600;
}
.overtime-component p {
text-align: center;
width: 100%;
margin-bottom: 5px;
}
.overtime-component {
transition: width 0.5s;
}
.overtime-component--delete {
transform: translateX(0);
}
`;
}
private createOvertimeBars(): TemplateResult[] | TemplateResult {
if (this.overtimeData && this.wrapper) {
return this.filteredData
.map(item =>
html`<div class="overtime-component" style="width: ${this.getRelativeWidth(item.value)}px">
<p id="${item.name}">${item.name}</p>
<div style="background: ${item.color}" class="overtime-component-color-bar">
${item.value}
</div>
</div>`);
} else {
// TODO apply some sort of loading animation
return html`
<span>There is no current overtime data</span>
`;
}
}
render(): TemplateResult {
return html`
<p style="position: absolute; visibility: hidden; height: auto; width: auto: white-space: nowrap" id="test"></p>
<div id="wrapper" class="overtime">
${this.createOvertimeBars()}
</div>
`;
}
firstUpdated(_changedProperties: Map<string, string | number | unknown>) {
if (this.wrapper) {
this.requestUpdate();
}
}
private getRelativeWidth(value: number): number {
if (this.wrapper) {
return this.wrapper.clientWidth * (value / this.valueSum);
}
return 0;
}
private get valueSum(): number {
if (this.overtimeData) {
return this.overtimeData
.map(item => item.value)
.reduce((previous: number, current: number, _) => previous+current);
}
return 0;
}
}
export interface OvertimeData {
name: string;
color: string;
value: number;
priority: number;
}
<template>
<div ref="cl" class="color-list">
<div
v-for="color in filteredColors"
:key="color.priority"
:style="createWidthString(color)"
class="color-bar"
>
<template v-if="color.value > 0">
<div class="color-bar-content">
<div class="color-bar-name">{{ color.name }}</div>
<div :style="createColorString(color)" class="color-bar-value">
{{ color.value }}
</div>
</div>
</template>
</div>
</div>
</template>
<script lang="ts">
import Vue from "vue";
// eslint-disable-next-line
import { State } from "../store/index";
import { mapState, Store } from "vuex";
import { CategorizedFlexHours } from "../store/overtime";
export default Vue.extend({
props: {
subtract: {
default: "",
type: String,
},
},
data() {
return {
colors: [],
unsubscribe: () => {},
};
},
computed: {
filteredColors(): CategorizedFlexHours[] {
return this.withDrawFromList(
this.colors as CategorizedFlexHours[],
Number.parseInt(this.subtract, 10)
);
},
},
async created() {
await this.$store.dispatch("FETCH_AVAILABLE_HOURS");
this.colors = (this.$store as Store<State>).getters.getCategorizedFlexHours;
this.unsubscribe = (this.$store as Store<State>).subscribe(
(mutation, state) => {
if (mutation.type === "SET_AVAILABLEHOURS") {
this.colors = (this.$store as Store<
State
>).getters.getCategorizedFlexHours;
}
}
);
},
beforeDestroy() {
this.unsubscribe();
},
methods: {
createColorString(colorConfig: CategorizedFlexHours): string {
return `background: ${colorConfig.colorValue};`;
},
createWidthString(colorConfig: CategorizedFlexHours): string {
return this.createCalculatedWidthString(colorConfig.value);
},
createFlexWidthString(value: number): string {
return `flex-grow: ${Math.floor(
(value / this.getValuePercentage()) * 100
)};`;
},
createCalculatedWidthString(value: number): string {
const width: number = this.getElementWidth();
const pixelWidth: number = Math.floor(
(value / this.getValuePercentage()) * width
);
return `width: ${pixelWidth}px;`;
},
getValuePercentage(): number {
let valueSum = 0;
(this.colors as CategorizedFlexHours[]).forEach(item => {
valueSum += item.value;
});
return valueSum;
},
getElementWidth(): number {
if (this.$refs.cl) return (this.$refs.cl as HTMLDivElement).clientWidth;
else return window.innerWidth;
},
withDrawFromList(
colorConfigs: CategorizedFlexHours[],
value: number
): CategorizedFlexHours[] {
const newItems: CategorizedFlexHours[] = [];
var sortedConfig = colorConfigs.sort((a, b) => b.priority - a.priority);
for (var item of sortedConfig) {
const clone = { ...item };
if (value > 0) {
if (clone.value < value) {
value = value - clone.value;
clone.value = 0;
} else {
clone.value = clone.value - value;
value = 0;
}
}
newItems.push(clone);
}
return newItems;
},
},
});
</script>
<style scoped>
.color-list {
display: flex;
align-content: stretch;
}
.color-bar {
transition: width 0.5s;
}
.color-bar-value {
height: 30px;
display: flex;
justify-content: center;
align-items: center;
font-weight: 600;
}
.color-bar-name {
text-align: center;
font-weight: 600;
}
</style>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment