feat(node-toolbar): add node-toolbar component

Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com>
This commit is contained in:
braks
2022-12-09 20:52:01 +01:00
committed by Braks
parent 2829f8a92d
commit f6acbd8dc6
12 changed files with 166 additions and 34 deletions
+8
View File
@@ -0,0 +1,8 @@
module.exports = {
rules: {
'no-use-before-define': 0,
'vue/no-setup-props-destructure': 0,
},
extends: ['../../.eslintrc.js'],
ignorePatterns: ['!**/*'],
}
+3
View File
@@ -0,0 +1,3 @@
# Vue Flow: Toolbar Component
This is a toolbar component for Vue Flow.
+48
View File
@@ -0,0 +1,48 @@
{
"name": "@vue-flow/node-toolbar",
"version": "0.0.1",
"private": false,
"license": "MIT",
"author": "Burak Cakmakoglu<78412429+bcakmakoglu@users.noreply.github.com>",
"repository": {
"type": "git",
"url": "git+https://github.com/bcakmakoglu/vue-flow/packages/plugins/node-toolbar"
},
"homepage": "https://github.com/bcakmakoglu/vue-flow#readme",
"bugs": {
"url": "https://github.com/bcakmakoglu/vue-flow/issues"
},
"main": "./dist/vue-flow-plugin-node-toolbar.js",
"module": "./dist/vue-flow-node-toolbar.mjs",
"types": "./dist/index.d.ts",
"unpkg": "./dist/vue-flow-node-toolbar.iife.js",
"jsdelivr": "./dist/vue-flow-node-toolbar.iife.js",
"files": [
"dist"
],
"sideEffects": false,
"scripts": {
"build": "vite build",
"types": "vue-tsc --declaration --emitDeclarationOnly && pnpm lint:dist",
"lint": "eslint --ext \".js,.jsx,.ts,.tsx\" --fix --ignore-path ../../.gitignore .",
"lint:dist": "eslint --ext \".ts,.tsx\" -c .eslintrc.js --fix ./dist",
"test": "exit 0"
},
"peerDependencies": {
"@vue-flow/core": "^1.0.0",
"vue": "^3.2.37"
},
"dependencies": {},
"devDependencies": {
"@vue-flow/core": "workspace:*",
"@vitejs/plugin-vue": "^3.2.0",
"unplugin-auto-import": "^0.12.0",
"vite": "^3.2.5",
"vite-plugin-vue-type-imports": "0.2.0",
"vue-tsc": "^1.0.11"
},
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org/"
}
}
+91
View File
@@ -0,0 +1,91 @@
<script lang="ts" setup>
import { computed, inject } from 'vue'
import type { GraphNode, Rect, ViewpaneTransform } from '@vue-flow/core'
import { NodeIdInjection, Position, getRectOfNodes, useVueFlow } from '@vue-flow/core'
import type { CSSProperties } from 'vue'
import type { NodeToolbarProps } from './types'
const props = withDefaults(defineProps<NodeToolbarProps>(), {
position: Position.Top,
offset: 10,
})
const contextNodeId = inject(NodeIdInjection, null)
const { viewportRef, viewport, getSelectedNodes, findNode } = useVueFlow()
function getTransform(nodeRect: Rect, transform: ViewpaneTransform, position: Position, offset: number): string {
// position === Position.Top
let xPos = (nodeRect.x + nodeRect.width / 2) * transform.zoom + transform.x
let yPos = nodeRect.y * transform.zoom + transform.y - offset
let xShift = -50
let yShift = -100
switch (position) {
case Position.Right:
xPos = (nodeRect.x + nodeRect.width) * transform.zoom + transform.x + offset
yPos = (nodeRect.y + nodeRect.height / 2) * transform.zoom + transform.y
xShift = 0
yShift = -50
break
case Position.Bottom:
yPos = (nodeRect.y + nodeRect.height) * transform.zoom + transform.y + offset
yShift = 0
break
case Position.Left:
xPos = nodeRect.x * transform.zoom + transform.x - offset
yPos = (nodeRect.y + nodeRect.height / 2) * transform.zoom + transform.y
xShift = -100
yShift = -50
break
}
return `translate(${xPos}px, ${yPos}px) translate(${xShift}%, ${yShift}%)`
}
const nodes = computed(() => {
const nodeIds = Array.isArray(props.nodeId) ? props.nodeId : [props.nodeId || contextNodeId || '']
return nodeIds.reduce<GraphNode[]>((acc, id) => {
const node = findNode(id)
if (node) {
acc.push(node)
}
return acc
}, [] as GraphNode[])
})
const isActive = computed(() =>
typeof props.isVisible === 'boolean'
? props.isVisible
: nodes.value.length === 1 && nodes.value[0].selected && getSelectedNodes.value.length === 1,
)
const nodeRect = computed<Rect>(() => getRectOfNodes(nodes.value))
const zIndex = computed<number>(() => Math.max(...nodes.value.map((node) => (node.computedPosition.z || 1) + 1)))
const wrapperStyle = computed<CSSProperties>(() => ({
position: 'absolute',
transform: getTransform(nodeRect.value, viewport.value, props.position, props.offset),
zIndex: zIndex.value,
}))
</script>
<script lang="ts">
export default {
name: 'NodeToolbar',
inheritAttrs: false,
}
</script>
<template>
<Teleport :to="viewportRef">
<div v-if="isActive && nodes.length" v-bind="$attrs" :style="wrapperStyle" class="vue-flow__node-toolbar">
<slot />
</div>
</Teleport>
</template>
+2
View File
@@ -0,0 +1,2 @@
export * from './types'
export { default as NodeToolbar } from './NodeToolbar.vue'
+8
View File
@@ -0,0 +1,8 @@
import type { Position } from '@vue-flow/core'
export interface NodeToolbarProps {
nodeId?: string | string[]
isVisible?: boolean
position?: Position
offset?: number
}
+31
View File
@@ -0,0 +1,31 @@
{
"compilerOptions": {
"baseUrl": ".",
"module": "ESNext",
"target": "es2017",
"lib": [
"DOM",
"ESNext"
],
"strict": true,
"esModuleInterop": true,
"incremental": false,
"skipLibCheck": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"noUnusedLocals": false,
"strictNullChecks": true,
"forceConsistentCasingInFileNames": true,
"declaration": true,
"declarationDir": "./dist",
"emitDeclarationOnly": true,
"types": [],
},
"include": [
"./src",
],
"exclude": [
"node_modules",
"dist"
]
}
+45
View File
@@ -0,0 +1,45 @@
import { resolve } from 'path'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import vueTypes from 'vite-plugin-vue-type-imports'
// https://vitejs.dev/config/
export default defineConfig({
resolve: {
extensions: ['.ts', '.vue'],
},
build: {
emptyOutDir: false,
lib: {
formats: ['es', 'cjs', 'iife'],
entry: resolve(__dirname, 'src/index.ts'),
fileName: 'vue-flow-node-toolbar',
name: 'vueFlowNodeToolbar',
},
rollupOptions: {
// make sure to externalize deps that shouldn't be bundled
// into your library
external: ['vue', '@vue-flow/core'],
output: {
dir: './dist',
// Provide global variables to use in the UMD build
// for externalized deps
globals: {
'vue': 'Vue',
'@vue-flow/core': 'VueFlow',
},
},
},
},
plugins: [
vue({
reactivityTransform: true,
}),
vueTypes(),
AutoImport({
imports: ['vue', 'vue/macros'],
dts: 'src/auto-imports.d.ts',
}),
],
})