feat(client): add tag cloud component
This commit is contained in:
parent
f997b7dff2
commit
c9b3ab80ca
3 changed files with 133 additions and 1 deletions
21
packages/client/assets/tagcanvas.min.js
vendored
Normal file
21
packages/client/assets/tagcanvas.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
80
packages/client/src/components/tag-cloud.vue
Normal file
80
packages/client/src/components/tag-cloud.vue
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
<template>
|
||||||
|
<div class="root">
|
||||||
|
<canvas :id="idForCanvas" ref="canvasEl" class="canvas" width="300" height="300"></canvas>
|
||||||
|
<div :id="idForTags" ref="tagsEl" class="tags">
|
||||||
|
<ul>
|
||||||
|
<slot></slot>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { onMounted, ref, watch, PropType, onBeforeUnmount } from 'vue';
|
||||||
|
import tinycolor from 'tinycolor2';
|
||||||
|
|
||||||
|
const props = defineProps<{}>();
|
||||||
|
|
||||||
|
const loaded = !!window.TagCanvas;
|
||||||
|
const SAFE_FOR_HTML_ID = 'abcdefghijklmnopqrstuvwxyz';
|
||||||
|
const computedStyle = getComputedStyle(document.documentElement);
|
||||||
|
const idForCanvas = Array.from(Array(16)).map(() => SAFE_FOR_HTML_ID[Math.floor(Math.random() * SAFE_FOR_HTML_ID.length)]).join('');
|
||||||
|
const idForTags = Array.from(Array(16)).map(() => SAFE_FOR_HTML_ID[Math.floor(Math.random() * SAFE_FOR_HTML_ID.length)]).join('');
|
||||||
|
let available = $ref(false);
|
||||||
|
let canvasEl = $ref<HTMLCanvasElement | null>(null);
|
||||||
|
let tagsEl = $ref<HTMLElement | null>(null);
|
||||||
|
|
||||||
|
watch($$(available), () => {
|
||||||
|
window.TagCanvas.Start(idForCanvas, idForTags, {
|
||||||
|
textColour: '#ffffff',
|
||||||
|
outlineColour: tinycolor(computedStyle.getPropertyValue('--accent')).toHexString(),
|
||||||
|
outlineRadius: 10,
|
||||||
|
initial: [-0.030, -0.010],
|
||||||
|
frontSelect: true,
|
||||||
|
imageRadius: 8,
|
||||||
|
//dragControl: true,
|
||||||
|
dragThreshold: 3,
|
||||||
|
wheelZoom: false,
|
||||||
|
reverse: true,
|
||||||
|
depth: 0.5,
|
||||||
|
maxSpeed: 0.2,
|
||||||
|
minSpeed: 0.003,
|
||||||
|
stretchX: 0.8,
|
||||||
|
stretchY: 0.8,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (loaded) {
|
||||||
|
available = true;
|
||||||
|
} else {
|
||||||
|
document.head.appendChild(Object.assign(document.createElement('script'), {
|
||||||
|
async: true,
|
||||||
|
src: '/client-assets/tagcanvas.min.js',
|
||||||
|
})).addEventListener('load', () => available = true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
window.TagCanvas.Delete(idForCanvas);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.root {
|
||||||
|
position: relative;
|
||||||
|
overflow: clip;
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
|
||||||
|
> .canvas {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .tags {
|
||||||
|
position: absolute;
|
||||||
|
top: 999px;
|
||||||
|
left: 999px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -108,6 +108,17 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="container tagCloud">
|
||||||
|
<div class="body">
|
||||||
|
<MkTagCloud v-if="activeInstances">
|
||||||
|
<li v-for="instance in activeInstances">
|
||||||
|
<a @click.prevent="onInstanceClick(instance)">
|
||||||
|
<img style="width: 32px;" :src="instance.iconUrl">
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</MkTagCloud>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div v-if="fedStats" class="container federationPies">
|
<div v-if="fedStats" class="container federationPies">
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<div class="chart deliver">
|
<div class="chart deliver">
|
||||||
|
@ -154,8 +165,8 @@ import XFederation from './overview.federation.vue';
|
||||||
import XQueueChart from './overview.queue-chart.vue';
|
import XQueueChart from './overview.queue-chart.vue';
|
||||||
import XUser from './overview.user.vue';
|
import XUser from './overview.user.vue';
|
||||||
import XPie from './overview.pie.vue';
|
import XPie from './overview.pie.vue';
|
||||||
import MkInstanceStats from '@/components/instance-stats.vue';
|
|
||||||
import MkNumberDiff from '@/components/number-diff.vue';
|
import MkNumberDiff from '@/components/number-diff.vue';
|
||||||
|
import MkTagCloud from '@/components/tag-cloud.vue';
|
||||||
import { version, url } from '@/config';
|
import { version, url } from '@/config';
|
||||||
import number from '@/filters/number';
|
import number from '@/filters/number';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
@ -197,6 +208,7 @@ let federationPubActiveDiff = $ref<number | null>(null);
|
||||||
let federationSubActive = $ref<number | null>(null);
|
let federationSubActive = $ref<number | null>(null);
|
||||||
let federationSubActiveDiff = $ref<number | null>(null);
|
let federationSubActiveDiff = $ref<number | null>(null);
|
||||||
let newUsers = $ref(null);
|
let newUsers = $ref(null);
|
||||||
|
let activeInstances = $shallowRef(null);
|
||||||
const queueStatsConnection = markRaw(stream.useChannel('queueStats'));
|
const queueStatsConnection = markRaw(stream.useChannel('queueStats'));
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
let chartInstance: Chart = null;
|
let chartInstance: Chart = null;
|
||||||
|
@ -363,6 +375,10 @@ async function renderChart() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onInstanceClick(i) {
|
||||||
|
os.pageWindow(`/instance-info/${i.host}`);
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
/*
|
/*
|
||||||
const magicGrid = new MagicGrid({
|
const magicGrid = new MagicGrid({
|
||||||
|
@ -410,6 +426,13 @@ onMounted(async () => {
|
||||||
newUsers = res;
|
newUsers = res;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
os.api('federation/instances', {
|
||||||
|
sort: '+lastCommunicatedAt',
|
||||||
|
limit: 25,
|
||||||
|
}).then(res => {
|
||||||
|
activeInstances = res;
|
||||||
|
});
|
||||||
|
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
queueStatsConnection.send('requestLog', {
|
queueStatsConnection.send('requestLog', {
|
||||||
id: Math.random().toString().substr(2, 8),
|
id: Math.random().toString().substr(2, 8),
|
||||||
|
@ -577,6 +600,14 @@ definePageMetadata({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.tagCloud {
|
||||||
|
> .body {
|
||||||
|
background: var(--panel);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
overflow: clip;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue