mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-10 02:54:15 +00:00
implement ContextMenu
core
This commit is contained in:
parent
f39952baa6
commit
8e81116ba1
@ -0,0 +1,53 @@
|
||||
import React from "react"
|
||||
|
||||
import { createIconRender } from "components/Icons"
|
||||
|
||||
import "./index.less"
|
||||
|
||||
export default (props) => {
|
||||
const { items = [], cords, clickedComponent } = props
|
||||
|
||||
const handleItemClick = async (item) => {
|
||||
if (typeof item.action === "function") {
|
||||
await item.action(clickedComponent)
|
||||
}
|
||||
}
|
||||
|
||||
const renderItems = () => {
|
||||
if (items.length === 0) {
|
||||
return <div>
|
||||
<p>No items</p>
|
||||
</div>
|
||||
}
|
||||
|
||||
return items.map((item, index) => {
|
||||
if (item.type === "separator") {
|
||||
return <div key={index} className="context-menu-separator" />
|
||||
}
|
||||
|
||||
return <div
|
||||
key={index}
|
||||
onClick={() => handleItemClick(item)}
|
||||
className="item"
|
||||
>
|
||||
<p className="label">
|
||||
{item.label}
|
||||
</p>
|
||||
{item.description && <p className="description">
|
||||
{item.description}
|
||||
</p>}
|
||||
{createIconRender(item.icon)}
|
||||
</div>
|
||||
})
|
||||
}
|
||||
|
||||
return <div
|
||||
className="contextMenu"
|
||||
style={{
|
||||
top: cords.y,
|
||||
left: cords.x,
|
||||
}}
|
||||
>
|
||||
{renderItems()}
|
||||
</div>
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
.contextMenu {
|
||||
position: absolute;
|
||||
z-index: 100000;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
width: 200px;
|
||||
height: fit-content;
|
||||
|
||||
background-color: var(--background-color-primary);
|
||||
border: 1px solid var(--border-color);
|
||||
|
||||
border-radius: 8px;
|
||||
|
||||
padding: 10px;
|
||||
|
||||
font-family: "Recursive", sans-serif;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 500;
|
||||
color: var(--text-color);
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
p,
|
||||
span {
|
||||
margin: 0;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.item {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
width: 100%;
|
||||
|
||||
padding: 5px 10px 5px 20px;
|
||||
|
||||
transition: all 50ms ease-in-out;
|
||||
|
||||
border-radius: 8px;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--background-color-accent);
|
||||
padding-left: 25px;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: var(--background-color-primary2);
|
||||
transform: scale(0.95);
|
||||
}
|
||||
}
|
||||
|
||||
.context-menu-separator {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
|
||||
margin: 10px 0;
|
||||
|
||||
background-color: var(--border-color);
|
||||
}
|
||||
}
|
124
packages/app/src/cores/contextMenu/index.js
Normal file
124
packages/app/src/cores/contextMenu/index.js
Normal file
@ -0,0 +1,124 @@
|
||||
import React from "react"
|
||||
import Core from "evite/src/core"
|
||||
|
||||
import { DOMWindow } from "components/RenderWindow"
|
||||
import ContextMenu from "./components/contextMenu"
|
||||
|
||||
import Contexts from "schemas/menu-contexts"
|
||||
|
||||
export default class ContextMenuCore extends Core {
|
||||
static namespace = "ContextMenu"
|
||||
static public = ["show", "hide", "registerContext"]
|
||||
|
||||
contexts = Object()
|
||||
defaultContext = [
|
||||
{
|
||||
label: "Report a bug",
|
||||
icon: "AlertTriangle",
|
||||
action: (parent, element) => {
|
||||
app.eventBus.emit("app.reportBug", {
|
||||
parent,
|
||||
element
|
||||
})
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
DOMWindow = new DOMWindow({
|
||||
id: "contextMenu",
|
||||
className: "contextMenuWrapper",
|
||||
clickOutsideToClose: true,
|
||||
})
|
||||
|
||||
async initialize() {
|
||||
document.addEventListener("contextmenu", this.handleEvent)
|
||||
}
|
||||
|
||||
registerContext = (element, context) => {
|
||||
this.contexts[element] = context
|
||||
}
|
||||
|
||||
generateItems = (element) => {
|
||||
let items = []
|
||||
|
||||
// find the closest context with attribute (context-menu)
|
||||
// if not found, use default context
|
||||
const parentElement = element.closest("[context-menu]")
|
||||
|
||||
if (parentElement) {
|
||||
const context = parentElement.getAttribute("context-menu")
|
||||
|
||||
// if context is not registered, try to fetch it from the constants contexts object
|
||||
if (!this.contexts[context]) {
|
||||
items = Contexts[context] || []
|
||||
} else {
|
||||
items = this.contexts[context] ?? []
|
||||
}
|
||||
|
||||
if (typeof items === "function") {
|
||||
items = items(
|
||||
parentElement,
|
||||
element,
|
||||
{
|
||||
close: this.hide
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// fullfill each item with a correspondent index if missing declared
|
||||
items = items.map((item, index) => {
|
||||
if (!item.index) {
|
||||
item.index = index
|
||||
}
|
||||
|
||||
return item
|
||||
})
|
||||
|
||||
// short items (if has declared index)
|
||||
items = items.sort((a, b) => a.index - b.index)
|
||||
|
||||
if (items.length > 0) {
|
||||
items.push({
|
||||
type: "separator"
|
||||
})
|
||||
}
|
||||
|
||||
items.push(...this.defaultContext)
|
||||
|
||||
return items
|
||||
}
|
||||
|
||||
handleEvent = (event) => {
|
||||
event.preventDefault()
|
||||
|
||||
// get the cords of the mouse
|
||||
const x = event.clientX
|
||||
const y = event.clientY
|
||||
|
||||
// get the component that was clicked
|
||||
const component = document.elementFromPoint(x, y)
|
||||
|
||||
// check if is clicking inside a context menu or a children inside a context menu
|
||||
if (component.classList.contains("contextMenu") || component.closest(".contextMenu")) {
|
||||
return
|
||||
}
|
||||
|
||||
this.show({
|
||||
cords: {
|
||||
x,
|
||||
y,
|
||||
},
|
||||
clickedComponent: component,
|
||||
items: this.generateItems(component),
|
||||
})
|
||||
}
|
||||
|
||||
show = (payload) => {
|
||||
this.DOMWindow.render(React.createElement(ContextMenu, payload))
|
||||
}
|
||||
|
||||
hide = () => {
|
||||
this.DOMWindow.remove()
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ import APICore from "./api"
|
||||
import StyleCore from "./style"
|
||||
import PermissionsCore from "./permissions"
|
||||
import SearchCore from "./search"
|
||||
import ContextMenuCore from "./contextMenu"
|
||||
|
||||
import I18nCore from "./i18n"
|
||||
import NotificationsCore from "./notifications"
|
||||
@ -24,4 +25,5 @@ export default [
|
||||
ShortcutsCore,
|
||||
|
||||
AudioPlayer,
|
||||
ContextMenuCore,
|
||||
]
|
Loading…
x
Reference in New Issue
Block a user