Add confirmation for ejecting blocks and component target when using context menu
This commit is contained in:
parent
c1bd1dc2d0
commit
8a94ee620b
2 changed files with 108 additions and 64 deletions
|
@ -7,14 +7,15 @@
|
|||
$: noPaste = !$store.componentToPaste
|
||||
|
||||
const keyboardEvent = (key, ctrlKey = false) => {
|
||||
// Ensure this component is selected first
|
||||
if (component._id !== $store.selectedComponentId) {
|
||||
store.update(state => {
|
||||
state.selectedComponentId = component._id
|
||||
return state
|
||||
document.dispatchEvent(
|
||||
new CustomEvent("component-menu", {
|
||||
detail: {
|
||||
key,
|
||||
ctrlKey,
|
||||
id: component?._id,
|
||||
},
|
||||
})
|
||||
}
|
||||
document.dispatchEvent(new KeyboardEvent("keydown", { key, ctrlKey }))
|
||||
)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -7,14 +7,66 @@
|
|||
import NavItem from "components/common/NavItem.svelte"
|
||||
import ScreenslotDropdownMenu from "./ScreenslotDropdownMenu.svelte"
|
||||
import { setContext, onMount } from "svelte"
|
||||
import { get } from "svelte/store"
|
||||
import DNDPositionIndicator from "./DNDPositionIndicator.svelte"
|
||||
import { DropPosition } from "./dndStore"
|
||||
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
||||
import { notifications, Button } from "@budibase/bbui"
|
||||
import { findComponent } from "builderStore/componentUtils"
|
||||
|
||||
let scrollRef
|
||||
let confirmDeleteDialog
|
||||
let confirmEjectDialog
|
||||
let componentToDelete
|
||||
let componentToEject
|
||||
|
||||
const keyHandlers = {
|
||||
["^ArrowUp"]: async component => {
|
||||
await store.actions.components.moveUp(component)
|
||||
},
|
||||
["^ArrowDown"]: async component => {
|
||||
await store.actions.components.moveDown(component)
|
||||
},
|
||||
["^c"]: component => {
|
||||
store.actions.components.copy(component, false)
|
||||
},
|
||||
["^x"]: component => {
|
||||
store.actions.components.copy(component, true)
|
||||
},
|
||||
["^v"]: async component => {
|
||||
await store.actions.components.paste(component, "inside")
|
||||
},
|
||||
["^d"]: async component => {
|
||||
store.actions.components.copy(component)
|
||||
await store.actions.components.paste(component, "below")
|
||||
},
|
||||
["^e"]: component => {
|
||||
componentToEject = component
|
||||
confirmEjectDialog.show()
|
||||
},
|
||||
["^Enter"]: () => {
|
||||
$goto("./new")
|
||||
},
|
||||
["Delete"]: component => {
|
||||
// Don't show confirmation for the screen itself
|
||||
if (component?._id === $selectedScreen.props._id) {
|
||||
return false
|
||||
}
|
||||
componentToDelete = component
|
||||
confirmDeleteDialog.show()
|
||||
},
|
||||
["ArrowUp"]: () => {
|
||||
store.actions.components.selectPrevious()
|
||||
},
|
||||
["ArrowDown"]: () => {
|
||||
store.actions.components.selectNext()
|
||||
},
|
||||
["Escape"]: () => {
|
||||
if (!$isActive("/new")) {
|
||||
return false
|
||||
}
|
||||
$goto("./")
|
||||
},
|
||||
}
|
||||
|
||||
const scrollTo = bounds => {
|
||||
if (!bounds) {
|
||||
|
@ -59,6 +111,11 @@
|
|||
})
|
||||
}
|
||||
|
||||
// Set scroll context so components can invoke scrolling when selected
|
||||
setContext("scroll", {
|
||||
scrollTo,
|
||||
})
|
||||
|
||||
const onDrop = async () => {
|
||||
try {
|
||||
await dndStore.actions.drop()
|
||||
|
@ -68,13 +125,28 @@
|
|||
}
|
||||
}
|
||||
|
||||
// Set scroll context so components can invoke scrolling when selected
|
||||
setContext("scroll", {
|
||||
scrollTo,
|
||||
})
|
||||
|
||||
const deleteComponent = async () => {
|
||||
await store.actions.components.delete(get(selectedComponent))
|
||||
const handleKeyAction = async (component, key, ctrlKey = false) => {
|
||||
if (!component || !key) {
|
||||
return false
|
||||
}
|
||||
try {
|
||||
// Delete and backspace are the same
|
||||
if (key === "Backspace") {
|
||||
key = "Delete"
|
||||
}
|
||||
// Prefix key with a caret for ctrl modifier
|
||||
if (ctrlKey) {
|
||||
key = "^" + key
|
||||
}
|
||||
const handler = keyHandlers[key]
|
||||
if (!handler) {
|
||||
return false
|
||||
}
|
||||
return handler(component)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
notifications.error("Error handling key press")
|
||||
}
|
||||
}
|
||||
|
||||
const handleKeyPress = async e => {
|
||||
|
@ -87,59 +159,23 @@
|
|||
if (["input", "textarea"].indexOf(activeTag) !== -1 && e.key !== "Escape") {
|
||||
return
|
||||
}
|
||||
const component = get(selectedComponent)
|
||||
try {
|
||||
if (e.ctrlKey || e.metaKey) {
|
||||
if (e.key === "ArrowUp") {
|
||||
e.preventDefault()
|
||||
await store.actions.components.moveUp(component)
|
||||
} else if (e.key === "ArrowDown") {
|
||||
e.preventDefault()
|
||||
await store.actions.components.moveDown(component)
|
||||
} else if (e.key === "c") {
|
||||
e.preventDefault()
|
||||
await store.actions.components.copy(component, false)
|
||||
} else if (e.key === "x") {
|
||||
e.preventDefault()
|
||||
store.actions.components.copy(component, true)
|
||||
} else if (e.key === "v") {
|
||||
e.preventDefault()
|
||||
await store.actions.components.paste(component, "inside")
|
||||
} else if (e.key === "d") {
|
||||
e.preventDefault()
|
||||
await store.actions.components.copy(component)
|
||||
await store.actions.components.paste(component, "below")
|
||||
} else if (e.key === "Enter") {
|
||||
e.preventDefault()
|
||||
$goto("./new")
|
||||
}
|
||||
} else if (e.key === "Backspace" || e.key === "Delete") {
|
||||
// Don't show confirmation for the screen itself
|
||||
if (component._id === get(selectedScreen).props._id) {
|
||||
return
|
||||
}
|
||||
e.preventDefault()
|
||||
confirmDeleteDialog.show()
|
||||
} else if (e.key === "ArrowUp") {
|
||||
e.preventDefault()
|
||||
await store.actions.components.selectPrevious()
|
||||
} else if (e.key === "ArrowDown") {
|
||||
e.preventDefault()
|
||||
await store.actions.components.selectNext()
|
||||
} else if (e.key === "Escape" && $isActive("./new")) {
|
||||
e.preventDefault()
|
||||
$goto("./")
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
notifications.error("Error handling key press")
|
||||
}
|
||||
// Key events are always for the selected component
|
||||
return handleKeyAction($selectedComponent, e.key, e.ctrlKey || e.metaKey)
|
||||
}
|
||||
|
||||
const handleComponentMenu = async e => {
|
||||
// Menu events can be for any component
|
||||
const { id, key, ctrlKey } = e.detail
|
||||
const component = findComponent($selectedScreen.props, id)
|
||||
return await handleKeyAction(component, key, ctrlKey)
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
document.addEventListener("keydown", handleKeyPress)
|
||||
document.addEventListener("component-menu", handleComponentMenu)
|
||||
return () => {
|
||||
document.removeEventListener("keydown", handleKeyPress)
|
||||
document.removeEventListener("component-menu", handleComponentMenu)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
@ -192,9 +228,16 @@
|
|||
<ConfirmDialog
|
||||
bind:this={confirmDeleteDialog}
|
||||
title="Confirm Deletion"
|
||||
body={`Are you sure you want to delete "${$selectedComponent?._instanceName}"?`}
|
||||
body={`Are you sure you want to delete "${componentToDelete?._instanceName}"?`}
|
||||
okText="Delete Component"
|
||||
onOk={deleteComponent}
|
||||
onOk={() => store.actions.components.delete(componentToDelete)}
|
||||
/>
|
||||
<ConfirmDialog
|
||||
bind:this={confirmEjectDialog}
|
||||
title="Eject block"
|
||||
body={`Ejecting a block breaks it down into multiple components. Are you sure you want to eject "${componentToEject?._instanceName}"?`}
|
||||
onOk={() => store.actions.components.requestEjectBlock(componentToEject?._id)}
|
||||
okText="Eject block"
|
||||
/>
|
||||
|
||||
<style>
|
||||
|
|
Loading…
Reference in a new issue