Skip to content

Commit

Permalink
modified: src/App.vue
Browse files Browse the repository at this point in the history
	modified:   src/Icon.vue
	modified:   src/initial-elements.js
	modified:   src/main.css
	modified:   src/nodes/TextAreaNode.vue
  • Loading branch information
SkyAphid committed Sep 4, 2024
1 parent d6c4eea commit c4382dd
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 21 deletions.
128 changes: 115 additions & 13 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Uses the following APIs:
*/

<script setup>
import { computed, ref } from 'vue'
import { computed, ref, onMounted, onBeforeUnmount, provide } from 'vue'
import { useVueFlow, VueFlow } from '@vue-flow/core'
import { Background } from '@vue-flow/background'
import { ControlButton, Controls } from '@vue-flow/controls'
Expand Down Expand Up @@ -38,39 +38,138 @@ const { onInit, screenToFlowCoordinate,
onPaneContextMenu,
toObject, fromObject,
getNodes, getEdges, vueFlowRef, } = useVueFlow();
//If enabled, a dialog will ask the user to confirm the deletion of elements
let confirmDelete = true;
//Delete nodes and edges
const deleteKey = 'Delete';
const confirmDeleteDialog = useConfirmDeleteDialog();
//Node intersecting
const panelEl = ref()
const isIntersectingWithPanel = ref(false)
const componentUtil = useComponentUtil();
//Edge renaming
const editingEdge = ref(null);
const edgeLabelFocused = false;
let mouseX = 0;
let mouseY = 0;
//Undo/Redo Stacks
const undoStack = [];
const redoStack = [];
const maxStackSize = 50;
/* Functions Start */
onInit((vueFlowInstance) => {
vueFlowInstance.fitView();
});
onConnect(addEdges)
//Delete nodes and edges
const deleteKey = 'Delete';
const confirmDeleteDialog = useConfirmDeleteDialog();
//Key event listener
onMounted(() => {
window.addEventListener('keydown', handleHotkeys);
});
onBeforeUnmount(() => {
window.removeEventListener('keydown', handleHotkeys);
});
const handleHotkeys = (event) => {
handleUndoRedoHotkeys(event);
}
/* Undo / Redo */
function pushToUndoStack() {
const state = JSON.stringify(toObject());
undoStack.push(state);
if (undoStack.length > maxStackSize) {
undoStack.shift(); // Maintain stack size
}
clearRedoStack();
}
//Allow other classes to inject this function so that they can notify the state of changes
provide('pushToUndoStack', pushToUndoStack);
function pushToRedoStack() {
const state = JSON.stringify(toObject());
redoStack.push(state);
if (redoStack.length > maxStackSize) {
redoStack.shift(); // Maintain stack size
}
}
function clearRedoStack() {
redoStack.value = [];
}
function undo() {
console.log(undoStack.length)
if (undoStack.length > 0) {
pushToRedoStack();
const lastState = undoStack.pop();
fromObject(JSON.parse(lastState));
return true;
}
return false;
}
function redo() {
if (redoStack.length > 0) {
pushToUndoStack();
const nextState = redoStack.pop();
fromObject(JSON.parse(nextState));
return true;
}
return false;
}
//Node Deleting
//CTRL-Z and CTRL-Y/CTRL_SHIFT_Z to undo and redo
const handleUndoRedoHotkeys = (event) => {
if (event.ctrlKey && event.key === 'z') {
if (undo()){
event.preventDefault();
}
} else if ((event.ctrlKey && event.key === 'y') || (event.ctrlKey && event.shiftKey && event.key === 'z')) {
if (redo()){
event.preventDefault();
}
}
}
/* onNodesChange/onEdgesChange - allow for deleting/editing and recording program states */
//Node deletion & state recording
onNodesChange(async (changes) => {
//Deleting Nodes
const nextChanges = []
for (const change of changes) {
if (change.type === 'remove') {
if (await canDelete(change)) {
pushToUndoStack();
componentUtil.deleteComponents(getNodes, change.id);
nextChanges.push(change)
}
Expand All @@ -83,17 +182,21 @@ onNodesChange(async (changes) => {
applyNodeChanges(nextChanges)
})
//Edge Deleting
//Edge Deleting & state recording
onEdgesChange(async (changes) => {
//Editing & Deleting Edges
const nextChanges = []
for (const change of changes) {
if (change.type === 'select') {
pushToUndoStack(JSON.stringify(toObject()));
endEdgeEditing();
}
if (change.type === 'remove') {
if (await canDelete(change)) {
pushToUndoStack(JSON.stringify(toObject()));
nextChanges.push(change)
}
} else {
Expand Down Expand Up @@ -395,9 +498,9 @@ async function onLoad() {

<!-- Toolbar -->
<Controls position="top-left">
<ControlButton title="Reset to New Project" @click="onReset">
<Icon name="reset" />

<ControlButton title="New Project" @click="onReset">
<Icon name="new-file" />
</ControlButton>

<ControlButton title="Save Project" @click="onSave">
Expand All @@ -418,8 +521,7 @@ async function onLoad() {

<!-- Label Renaming Dialog -->
<div v-if="editingEdge" class="label-renaming-field">
<input :id="'label-editor'" type="text" v-model="editingEdge.label" @keydown.enter="endEdgeEditing"
@keydown.escape="endEdgeEditing" />
<input :id="'label-editor'" type="text" v-model="editingEdge.label" @focusout="endEdgeEditing" @keydown.enter="endEdgeEditing" @keydown.escape="endEdgeEditing" />
</div>

</template>
Expand Down
8 changes: 7 additions & 1 deletion src/Icon.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,18 @@ defineProps({
d="M20 19V7H4v12h16m0-16a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16m-7 14v-2h5v2h-5m-3.42-4L5.57 9H8.4l3.3 3.3c.39.39.39 1.03 0 1.42L8.42 17H5.59l3.99-4Z" />
</svg>

<svg v-if="name === 'new-file'" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-file-earmark" viewBox="0 0 16 16">
<path
d="M14 4.5V14a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2h5.5zm-3 0A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V4.5z" />
</svg>

<svg v-if="name === 'save'" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path fill="currentColor"
d="M21 7v12q0 .825-.587 1.413T19 21H5q-.825 0-1.412-.587T3 19V5q0-.825.588-1.412T5 3h12zm-9 11q1.25 0 2.125-.875T15 15t-.875-2.125T12 12t-2.125.875T9 15t.875 2.125T12 18m-6-8h9V6H6z" />
</svg>

<svg v-if="name === 'load'" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-folder2-open" viewBox="0 0 16 16">
<svg v-if="name === 'load'" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
class="bi bi-folder2-open" viewBox="0 0 16 16">
<path
d="M1 3.5A1.5 1.5 0 0 1 2.5 2h2.764c.958 0 1.76.56 2.311 1.184C7.985 3.648 8.48 4 9 4h4.5A1.5 1.5 0 0 1 15 5.5v.64c.57.265.94.876.856 1.546l-.64 5.124A2.5 2.5 0 0 1 12.733 15H3.266a2.5 2.5 0 0 1-2.481-2.19l-.64-5.124A1.5 1.5 0 0 1 1 6.14zM2 6h12v-.5a.5.5 0 0 0-.5-.5H9c-.964 0-1.71-.629-2.174-1.154C6.374 3.334 5.82 3 5.264 3H2.5a.5.5 0 0 0-.5.5zm-.367 1a.5.5 0 0 0-.496.562l.64 5.124A1.5 1.5 0 0 0 3.266 14h9.468a1.5 1.5 0 0 0 1.489-1.314l.64-5.124A.5.5 0 0 0 14.367 7z" />
</svg>
Expand Down
6 changes: 3 additions & 3 deletions src/initial-elements.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,23 @@ export const initialNodes = reactive([
{
id: uuidv4(),
type: 'text-field',
position: { x: 500, y: 0 },
position: { x: 600, y: 0 },
data: { label: 'Node 2' },
},

// An output node, specified by using `type: 'output'`
{
id: uuidv4(),
type: 'text-area',
position: { x: 900, y: 0 },
position: { x: 1100, y: 0 },
data: { label: 'Node 3', body: '' },
},

// A note node, for making notes
{
id: uuidv4(),
type: 'note',
position: { x: 375, y: 175 },
position: { x: 485, y: 175 },
data: {
label: 'Welcome to Corkboard!',
body: '<b><i>Getting Started:</i></b><ol><li>Right click the background to bring up the context menu</li><li>Double click nodes to delete and modify them</li><li>Drag attribute nodes over text area nodes to combine them</li><li>Save & load projects using the toolbar in the upper-left</li></ol>'
Expand Down
18 changes: 15 additions & 3 deletions src/main.css
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/* import the necessary styles for Vue Flow to work */
@import '@vue-flow/core/dist/style.css';

/* import the default theme, this is optional but generally recommended */
@import '@vue-flow/core/dist/theme-default.css';
/* import the default theme, this is optional but generally recommended
@import '@vue-flow/core/dist/theme-default.css';*/

/* import themeing for controls and node-resizing*/
@import './css/controls.css';
Expand All @@ -23,6 +23,12 @@
color: #2c3e50;
}

/* Edge Labels */

.vue-flow__edge-labels {
font-size: 1.0em;
}

/* Node Defaults */

.node-background {
Expand All @@ -34,10 +40,12 @@
border-radius:0px;
}

/* If intersecting a node, it's settings can be adjusted while doing so */
/*.intersecting {
background-color:#ec4899;
}*/

/* Quill & Textfields/areas */
.content-wrapper {
display: flex;
flex-direction: column;
Expand Down Expand Up @@ -78,20 +86,24 @@ input:focus {
align-items: left;
}

/* */
/* Minimap */

.vue-flow__minimap {
transform: scale(75%);
transform-origin: bottom right;
}

/* Handles */

.vue-flow__handle {
height:50%;
width:5px;
background:#3367d9;
border-radius:8px
}

/* Node Settings */

.vue-flow__node-value {
display:flex;
align-items:center;
Expand Down
2 changes: 1 addition & 1 deletion src/nodes/TextAreaNode.vue
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ watch(components, (newComponents) => {
}
}, { deep: true });
// Optionally, you might want to watch for changes in textAreaNodeProps.data.components as well
//Watch this node's data and push changes to the undoStack, and also watch for changes in components
watch(() => textAreaNodeProps.data.components, (newComponents) => {
components.value = newComponents || [];
}, { deep: true });
Expand Down

0 comments on commit c4382dd

Please sign in to comment.