mirror of
https://github.com/harvester/harvester-ui-extension.git
synced 2026-02-04 15:01:46 +00:00
feat: add topology
Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
This commit is contained in:
parent
6cca297775
commit
f214d69ed3
@ -8,6 +8,10 @@
|
||||
"dependencies": {
|
||||
"@babel/plugin-transform-class-static-block": "7.28.3",
|
||||
"@rancher/shell": "3.0.5-rc.8",
|
||||
"@vue-flow/background": "^1.3.0",
|
||||
"@vue-flow/controls": "^1.1.1",
|
||||
"@vue-flow/core": "^1.33.5",
|
||||
"@vue-flow/minimap": "^1.4.0",
|
||||
"cache-loader": "^4.1.0",
|
||||
"color": "4.2.3",
|
||||
"ip": "2.0.1",
|
||||
|
||||
485
pkg/harvester/detail/kubeovn.io.vpc.vue
Normal file
485
pkg/harvester/detail/kubeovn.io.vpc.vue
Normal file
@ -0,0 +1,485 @@
|
||||
<script>
|
||||
import { VueFlow } from '@vue-flow/core';
|
||||
import { Background } from '@vue-flow/background';
|
||||
import { Controls } from '@vue-flow/controls';
|
||||
import { MiniMap } from '@vue-flow/minimap';
|
||||
import { allHash } from '@shell/utils/promise';
|
||||
import { HCI } from '../types';
|
||||
|
||||
export default {
|
||||
name: 'VPCDetail',
|
||||
|
||||
components: {
|
||||
VueFlow,
|
||||
Background,
|
||||
Controls,
|
||||
MiniMap,
|
||||
},
|
||||
|
||||
props: {
|
||||
value: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
nodes: [],
|
||||
edges: [],
|
||||
loading: true,
|
||||
};
|
||||
},
|
||||
|
||||
// Layout constants
|
||||
LAYOUT: {
|
||||
PODS_PER_ROW: 5,
|
||||
POD_WIDTH: 150,
|
||||
HORIZONTAL_SPACING: 160,
|
||||
VERTICAL_SPACING: 95,
|
||||
SUBNET_SPACING: 550,
|
||||
VPC_Y_POSITION: 50,
|
||||
SUBNET_Y_POSITION: 200,
|
||||
POD_START_Y: 380,
|
||||
},
|
||||
|
||||
async fetch() {
|
||||
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||
|
||||
await allHash({
|
||||
subnets: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.SUBNET }),
|
||||
ips: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.IP }),
|
||||
});
|
||||
|
||||
await this.loadTopology();
|
||||
},
|
||||
|
||||
computed: {
|
||||
vpcName() {
|
||||
return this.value?.metadata?.name || '';
|
||||
},
|
||||
|
||||
subnetCount() {
|
||||
const subnets = this.nodes.filter((n) => n.data?.type === 'subnet');
|
||||
|
||||
return subnets.length;
|
||||
},
|
||||
|
||||
podCount() {
|
||||
const pods = this.nodes.filter((n) => n.data?.type === 'pod');
|
||||
|
||||
return pods.length;
|
||||
},
|
||||
|
||||
inStore() {
|
||||
return this.$store.getters['currentProduct']?.inStore || 'cluster';
|
||||
},
|
||||
|
||||
allSubnets() {
|
||||
return this.$store.getters[`${ this.inStore }/all`](HCI.SUBNET) || [];
|
||||
},
|
||||
|
||||
allIps() {
|
||||
return this.$store.getters[`${ this.inStore }/all`](HCI.IP) || [];
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
allSubnets: {
|
||||
handler() {
|
||||
if (!this.loading) {
|
||||
this.loadTopology();
|
||||
}
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
|
||||
allIps: {
|
||||
handler() {
|
||||
if (!this.loading) {
|
||||
this.loadTopology();
|
||||
}
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
|
||||
'value.metadata.resourceVersion'() {
|
||||
if (!this.loading) {
|
||||
this.loadTopology();
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
async loadTopology() {
|
||||
if (this._loadingTopology) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._loadingTopology = true;
|
||||
const wasLoading = this.loading;
|
||||
|
||||
if (!wasLoading) {
|
||||
this.loading = true;
|
||||
}
|
||||
|
||||
try {
|
||||
const vpc = this.value;
|
||||
|
||||
if (!vpc || !vpc.metadata) {
|
||||
this.loading = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const subnets = this.allSubnets;
|
||||
const ips = this.allIps;
|
||||
|
||||
const vpcSubnets = (Array.isArray(subnets) ? subnets : []).filter(
|
||||
(subnet) => subnet.spec?.vpc === vpc.metadata.name,
|
||||
);
|
||||
|
||||
const vpcPods = (Array.isArray(ips) ? ips : []).filter((ip) => {
|
||||
const ipSubnet = ip.spec?.subnet;
|
||||
const hasPodName = ip.spec?.podName;
|
||||
|
||||
return (
|
||||
hasPodName &&
|
||||
vpcSubnets.some((subnet) => subnet.metadata.name === ipSubnet)
|
||||
);
|
||||
});
|
||||
|
||||
const nodes = [];
|
||||
const edges = [];
|
||||
|
||||
// Add VPC node (top layer)
|
||||
nodes.push({
|
||||
id: 'vpc',
|
||||
type: 'default',
|
||||
position: { x: 400, y: this.LAYOUT.VPC_Y_POSITION },
|
||||
data: {
|
||||
label: `VPC: ${ vpc.metadata.name }`,
|
||||
type: 'vpc',
|
||||
},
|
||||
style: {
|
||||
background: '#f0f9ff',
|
||||
color: '#0c4a6e',
|
||||
border: '2px solid #38bdf8',
|
||||
borderRadius: '12px',
|
||||
padding: '10px',
|
||||
width: '200px',
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
});
|
||||
|
||||
// Add subnet nodes (middle layer)
|
||||
vpcSubnets.forEach((subnet, index) => {
|
||||
const subnetId = `subnet-${ subnet.metadata.name }`;
|
||||
|
||||
nodes.push({
|
||||
id: subnetId,
|
||||
type: 'default',
|
||||
position: {
|
||||
x: index * this.LAYOUT.SUBNET_SPACING + 50,
|
||||
y: this.LAYOUT.SUBNET_Y_POSITION,
|
||||
},
|
||||
data: {
|
||||
label: `Subnet:${ subnet.metadata.name }\nCIDR: ${
|
||||
subnet.spec?.cidrBlock || 'N/A'
|
||||
}`,
|
||||
type: 'subnet',
|
||||
},
|
||||
style: {
|
||||
background: '#fefce8',
|
||||
color: '#713f12',
|
||||
border: '2px solid #facc15',
|
||||
borderRadius: '12px',
|
||||
padding: '10px',
|
||||
width: '180px',
|
||||
whiteSpace: 'pre-wrap',
|
||||
},
|
||||
});
|
||||
|
||||
// Connect subnet to VPC
|
||||
edges.push({
|
||||
id: `edge-vpc-${ subnet.metadata.name }`,
|
||||
source: 'vpc',
|
||||
target: subnetId,
|
||||
animated: true,
|
||||
style: { stroke: '#38bdf8', strokeWidth: 2 },
|
||||
});
|
||||
});
|
||||
|
||||
// Add pod nodes (bottom layer) - grouped by subnet
|
||||
vpcSubnets.forEach((subnet, subnetIndex) => {
|
||||
const subnetPods = vpcPods.filter(
|
||||
(ip) => ip.spec?.subnet === subnet.metadata.name,
|
||||
);
|
||||
|
||||
subnetPods.forEach((pod, podIndex) => {
|
||||
const podName = pod.spec?.podName || pod.metadata.name;
|
||||
const podIp = pod.spec?.ipAddress || 'N/A';
|
||||
const podNamespace = pod.spec?.namespace || 'default';
|
||||
const podId = `pod-${ podName }-${ podIndex }`;
|
||||
|
||||
// Calculate pod position
|
||||
const subnetOffset = subnetIndex * this.LAYOUT.SUBNET_SPACING;
|
||||
const columnIndex = podIndex % this.LAYOUT.PODS_PER_ROW;
|
||||
const rowIndex = Math.floor(podIndex / this.LAYOUT.PODS_PER_ROW);
|
||||
|
||||
nodes.push({
|
||||
id: podId,
|
||||
type: 'default',
|
||||
position: {
|
||||
x: subnetOffset + columnIndex * this.LAYOUT.HORIZONTAL_SPACING + 10,
|
||||
y: this.LAYOUT.POD_START_Y + rowIndex * this.LAYOUT.VERTICAL_SPACING,
|
||||
},
|
||||
data: {
|
||||
label: `${ podName.substring(
|
||||
0,
|
||||
13,
|
||||
) }\nIP: ${ podIp }\nNS: ${ podNamespace }`,
|
||||
type: 'pod',
|
||||
},
|
||||
style: {
|
||||
background: '#f0fdf4',
|
||||
color: '#14532d',
|
||||
border: '2px solid #22c55e',
|
||||
borderRadius: '8px',
|
||||
padding: '8px',
|
||||
width: `${ this.LAYOUT.POD_WIDTH }px`,
|
||||
fontSize: '11px',
|
||||
whiteSpace: 'pre-wrap',
|
||||
},
|
||||
});
|
||||
|
||||
// Connect pod to its subnet
|
||||
edges.push({
|
||||
id: `edge-${ subnet.metadata.name }-${ podId }`,
|
||||
source: `subnet-${ subnet.metadata.name }`,
|
||||
target: podId,
|
||||
animated: false,
|
||||
style: {
|
||||
stroke: '#22c55e',
|
||||
strokeWidth: 1,
|
||||
strokeDasharray: '2,2',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
this.nodes = nodes;
|
||||
this.edges = edges;
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Failed to load VPC topology:', error);
|
||||
this.$store.dispatch(
|
||||
'growl/error',
|
||||
{
|
||||
title: 'Topology Error',
|
||||
message: `Failed to load VPC topology: ${ error.message }`,
|
||||
},
|
||||
{ root: true },
|
||||
);
|
||||
} finally {
|
||||
this.loading = false;
|
||||
this._loadingTopology = false;
|
||||
}
|
||||
},
|
||||
|
||||
onNodeClick(event) {
|
||||
// Handle node click
|
||||
},
|
||||
|
||||
onEdgeClick(event) {
|
||||
// Handle edge click
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="vpc-topology">
|
||||
<div class="topology-header">
|
||||
<div class="header-title">
|
||||
<div class="stats">
|
||||
<span class="stat-badge">{{ subnetCount }} subnet(s)</span>
|
||||
<span class="stat-badge">{{ podCount }} pod(s)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="legend">
|
||||
<div class="legend-item">
|
||||
<div class="legend-color vpc" />
|
||||
<span>VPC</span>
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<div class="legend-color subnet" />
|
||||
<span>Subnet</span>
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<div class="legend-color pod" />
|
||||
<span>Pod</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="loading"
|
||||
class="loading"
|
||||
>
|
||||
<i class="icon icon-spinner icon-spin" />
|
||||
Loading topology...
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else-if="nodes.length === 0"
|
||||
class="empty-state"
|
||||
>
|
||||
<i class="icon icon-info" />
|
||||
<p>No resources found in this VPC</p>
|
||||
</div>
|
||||
|
||||
<VueFlow
|
||||
v-else
|
||||
v-model:nodes="nodes"
|
||||
v-model:edges="edges"
|
||||
class="vpc-flow"
|
||||
:default-zoom="0.8"
|
||||
:min-zoom="0.2"
|
||||
:max-zoom="2"
|
||||
fit-view-on-init
|
||||
@node-click="onNodeClick"
|
||||
@edge-click="onEdgeClick"
|
||||
>
|
||||
<Background
|
||||
pattern-color="#f1f1f1"
|
||||
:gap="12"
|
||||
size="1"
|
||||
/>
|
||||
<Controls />
|
||||
<MiniMap />
|
||||
</VueFlow>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../styles/vue-flow.scss';
|
||||
|
||||
.vpc-topology {
|
||||
height: 800px;
|
||||
width: 100%;
|
||||
background: #f5f5f5;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
|
||||
.topology-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20px 0;
|
||||
background: white;
|
||||
border-bottom: 1px solid #ddd;
|
||||
|
||||
.header-title {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
|
||||
h2 {
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.stats {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
|
||||
.stat-badge {
|
||||
display: inline-block;
|
||||
padding: 4px 12px;
|
||||
background: #e5e7eb;
|
||||
border-radius: 12px;
|
||||
font-size: 12px;
|
||||
color: #374151;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.legend {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
|
||||
.legend-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
.legend-color {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 4px;
|
||||
|
||||
&.vpc {
|
||||
background: #f0f9ff;
|
||||
border: 2px solid #38bdf8;
|
||||
}
|
||||
|
||||
&.subnet {
|
||||
background: #fefce8;
|
||||
border: 2px solid #facc15;
|
||||
}
|
||||
|
||||
&.pod {
|
||||
background: #f0fdf4;
|
||||
border: 2px solid #22c55e;
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.loading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: calc(100% - 100px);
|
||||
font-size: 16px;
|
||||
color: #666;
|
||||
|
||||
i {
|
||||
margin-right: 10px;
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: calc(100% - 100px);
|
||||
color: #9ca3af;
|
||||
|
||||
i {
|
||||
font-size: 48px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 16px;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.vpc-flow {
|
||||
height: calc(100% - 100px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -1056,6 +1056,7 @@ harvester:
|
||||
tooltip: The IP address list to reserve from automatic assignment. The gateway IP address is always excluded and will be automatically added to the list.
|
||||
|
||||
vpc:
|
||||
viewTopology: Topology
|
||||
noAddonEnabled:
|
||||
prefix: The kubeovn-operator add-on is not enabled, click
|
||||
middle: here
|
||||
|
||||
@ -155,6 +155,15 @@ export default {
|
||||
return location;
|
||||
},
|
||||
|
||||
viewTopology(group) {
|
||||
const vpc = group.key;
|
||||
const resource = this.$store.getters[`harvester/byId`](HCI.VPC, vpc);
|
||||
|
||||
if (resource && resource.goToDetail) {
|
||||
resource.goToDetail();
|
||||
}
|
||||
},
|
||||
|
||||
showVpcAction(event, group) {
|
||||
const vpc = group.key;
|
||||
|
||||
@ -218,6 +227,14 @@ export default {
|
||||
>
|
||||
{{ t('harvester.vpc.createSubnet') }}
|
||||
</router-link>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm role-secondary mr-5"
|
||||
@click="viewTopology(group)"
|
||||
>
|
||||
<i class="icon icon-globe mr-5" />
|
||||
{{ t('harvester.vpc.viewTopology') }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm role-multi-action actions mr-10"
|
||||
|
||||
4
pkg/harvester/styles/vue-flow.scss
Normal file
4
pkg/harvester/styles/vue-flow.scss
Normal file
@ -0,0 +1,4 @@
|
||||
@import '@vue-flow/core/dist/style.css';
|
||||
@import '@vue-flow/core/dist/theme-default.css';
|
||||
@import '@vue-flow/controls/dist/style.css';
|
||||
@import '@vue-flow/minimap/dist/style.css';
|
||||
@ -18,6 +18,7 @@ export const HCI = {
|
||||
CLUSTER_NETWORK: 'network.harvesterhci.io.clusternetwork',
|
||||
SUBNET: 'kubeovn.io.subnet',
|
||||
VPC: 'kubeovn.io.vpc',
|
||||
IP: 'kubeovn.io.ip',
|
||||
VM_IMAGE_DOWNLOADER: 'harvesterhci.io.virtualmachineimagedownloader',
|
||||
SUPPORT_BUNDLE: 'harvesterhci.io.supportbundle',
|
||||
NETWORK_ATTACHMENT: 'harvesterhci.io.networkattachmentdefinition',
|
||||
|
||||
69
yarn.lock
69
yarn.lock
@ -3221,6 +3221,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.8.tgz#7545ba4fc3c003d6c756f651f3bf163d8f0f29ba"
|
||||
integrity sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==
|
||||
|
||||
"@types/web-bluetooth@^0.0.20":
|
||||
version "0.0.20"
|
||||
resolved "https://registry.yarnpkg.com/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz#f066abfcd1cbe66267cdbbf0de010d8a41b41597"
|
||||
integrity sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==
|
||||
|
||||
"@types/webpack-env@^1.15.2":
|
||||
version "1.18.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.18.5.tgz#eccda0b04fe024bed505881e2e532f9c119169bf"
|
||||
@ -3382,6 +3387,35 @@
|
||||
"@typescript-eslint/types" "5.62.0"
|
||||
eslint-visitor-keys "^3.3.0"
|
||||
|
||||
"@vue-flow/background@^1.3.0":
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@vue-flow/background/-/background-1.3.2.tgz#0c90cd05e5d60da017bbaf5a1c3eb6af7ed9b778"
|
||||
integrity sha512-eJPhDcLj1wEo45bBoqTXw1uhl0yK2RaQGnEINqvvBsAFKh/camHJd5NPmOdS1w+M9lggc9igUewxaEd3iCQX2w==
|
||||
|
||||
"@vue-flow/controls@^1.1.1":
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@vue-flow/controls/-/controls-1.1.3.tgz#40866b553101fbef22d2b9a043965ed76fca4b2c"
|
||||
integrity sha512-XCf+G+jCvaWURdFlZmOjifZGw3XMhN5hHlfMGkWh9xot+9nH9gdTZtn+ldIJKtarg3B21iyHU8JjKDhYcB6JMw==
|
||||
|
||||
"@vue-flow/core@^1.33.5":
|
||||
version "1.48.2"
|
||||
resolved "https://registry.yarnpkg.com/@vue-flow/core/-/core-1.48.2.tgz#cef8641b17f6220c257d4208bdb2082cee882225"
|
||||
integrity sha512-raxhgKWE+G/mcEvXJjGFUDYW9rAI3GOtiHR3ZkNpwBWuIaCC1EYiBmKGwJOoNzVFgwO7COgErnK7i08i287AFA==
|
||||
dependencies:
|
||||
"@vueuse/core" "^10.5.0"
|
||||
d3-drag "^3.0.0"
|
||||
d3-interpolate "^3.0.1"
|
||||
d3-selection "^3.0.0"
|
||||
d3-zoom "^3.0.0"
|
||||
|
||||
"@vue-flow/minimap@^1.4.0":
|
||||
version "1.5.4"
|
||||
resolved "https://registry.yarnpkg.com/@vue-flow/minimap/-/minimap-1.5.4.tgz#c9c3badea49d4166aa9cdc713017397d9df7574c"
|
||||
integrity sha512-l4C+XTAXnRxsRpUdN7cAVFBennC1sVRzq4bDSpVK+ag7tdMczAnhFYGgbLkUw3v3sY6gokyWwMl8CDonp8eB2g==
|
||||
dependencies:
|
||||
d3-selection "^3.0.0"
|
||||
d3-zoom "^3.0.0"
|
||||
|
||||
"@vue/babel-helper-vue-jsx-merge-props@^1.4.0":
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@vue/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-1.4.0.tgz#8d53a1e21347db8edbe54d339902583176de09f2"
|
||||
@ -3777,6 +3811,28 @@
|
||||
resolved "https://registry.yarnpkg.com/@vue/web-component-wrapper/-/web-component-wrapper-1.3.0.tgz#b6b40a7625429d2bd7c2281ddba601ed05dc7f1a"
|
||||
integrity sha512-Iu8Tbg3f+emIIMmI2ycSI8QcEuAUgPTgHwesDU1eKMLE4YC/c/sFbGc70QgMq31ijRftV0R7vCm9co6rldCeOA==
|
||||
|
||||
"@vueuse/core@^10.5.0":
|
||||
version "10.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@vueuse/core/-/core-10.11.1.tgz#15d2c0b6448d2212235b23a7ba29c27173e0c2c6"
|
||||
integrity sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww==
|
||||
dependencies:
|
||||
"@types/web-bluetooth" "^0.0.20"
|
||||
"@vueuse/metadata" "10.11.1"
|
||||
"@vueuse/shared" "10.11.1"
|
||||
vue-demi ">=0.14.8"
|
||||
|
||||
"@vueuse/metadata@10.11.1":
|
||||
version "10.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@vueuse/metadata/-/metadata-10.11.1.tgz#209db7bb5915aa172a87510b6de2ca01cadbd2a7"
|
||||
integrity sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw==
|
||||
|
||||
"@vueuse/shared@10.11.1":
|
||||
version "10.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@vueuse/shared/-/shared-10.11.1.tgz#62b84e3118ae6e1f3ff38f4fbe71b0c5d0f10938"
|
||||
integrity sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA==
|
||||
dependencies:
|
||||
vue-demi ">=0.14.8"
|
||||
|
||||
"@webassemblyjs/ast@1.12.1", "@webassemblyjs/ast@^1.12.1":
|
||||
version "1.12.1"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.12.1.tgz#bb16a0e8b1914f979f45864c23819cc3e3f0d4bb"
|
||||
@ -5799,7 +5855,7 @@ d3-delaunay@6:
|
||||
resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-3.0.1.tgz#5fc75284e9c2375c36c839411a0cf550cbfc4d5e"
|
||||
integrity sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==
|
||||
|
||||
"d3-drag@2 - 3", d3-drag@3:
|
||||
"d3-drag@2 - 3", d3-drag@3, d3-drag@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/d3-drag/-/d3-drag-3.0.0.tgz#994aae9cd23c719f53b5e10e3a0a6108c69607ba"
|
||||
integrity sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==
|
||||
@ -5854,7 +5910,7 @@ d3-hierarchy@3:
|
||||
resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz#b01cd42c1eed3d46db77a5966cf726f8c09160c6"
|
||||
integrity sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==
|
||||
|
||||
"d3-interpolate@1 - 3", "d3-interpolate@1.2.0 - 3", d3-interpolate@3:
|
||||
"d3-interpolate@1 - 3", "d3-interpolate@1.2.0 - 3", d3-interpolate@3, d3-interpolate@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz#3c47aa5b32c5b3dfb56ef3fd4342078a632b400d"
|
||||
integrity sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==
|
||||
@ -5900,7 +5956,7 @@ d3-scale@4:
|
||||
d3-time "2.1.1 - 3"
|
||||
d3-time-format "2 - 4"
|
||||
|
||||
"d3-selection@2 - 3", d3-selection@3, d3-selection@3.0.0:
|
||||
"d3-selection@2 - 3", d3-selection@3, d3-selection@3.0.0, d3-selection@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-3.0.0.tgz#c25338207efa72cc5b9bd1458a1a41901f1e1b31"
|
||||
integrity sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==
|
||||
@ -5942,7 +5998,7 @@ d3-shape@3:
|
||||
d3-interpolate "1 - 3"
|
||||
d3-timer "1 - 3"
|
||||
|
||||
d3-zoom@3:
|
||||
d3-zoom@3, d3-zoom@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/d3-zoom/-/d3-zoom-3.0.0.tgz#d13f4165c73217ffeaa54295cd6969b3e7aee8f3"
|
||||
integrity sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==
|
||||
@ -12797,6 +12853,11 @@ vue-component-type-helpers@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/vue-component-type-helpers/-/vue-component-type-helpers-2.2.0.tgz#de5fa802b6beae7125595ec0d3d5195a22691623"
|
||||
integrity sha512-cYrAnv2me7bPDcg9kIcGwjJiSB6Qyi08+jLDo9yuvoFQjzHiPTzML7RnkJB1+3P6KMsX/KbCD4QE3Tv/knEllw==
|
||||
|
||||
vue-demi@>=0.14.8:
|
||||
version "0.14.10"
|
||||
resolved "https://registry.yarnpkg.com/vue-demi/-/vue-demi-0.14.10.tgz#afc78de3d6f9e11bf78c55e8510ee12814522f04"
|
||||
integrity sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==
|
||||
|
||||
vue-draggable-next@^2.2.1:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/vue-draggable-next/-/vue-draggable-next-2.3.0.tgz#ba83154f60b8a3c24059c18b8060b72200a4c673"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user