update model for contextMenu and render method

This commit is contained in:
srgooglo 2020-10-12 19:14:31 +02:00
parent c8d7809106
commit 4720ed240a
9 changed files with 309 additions and 166 deletions

View File

@ -5,6 +5,7 @@ export default [
key: "inspect_element",
title: "Inspect",
icon: <Icons.Command />,
require: "embedded",
params: {
onClick: (e) => {
window.inspectElement(e)

View File

@ -57,22 +57,6 @@ function resumeApp(){
}
}
function contextualMenu(payload){
if (!payload) {
return false
}
const menu = new Menu()
const menuItem = new MenuItem({
label: 'Inspect Element',
click: () => {
mainWindow.inspectElement(payload.x, payload.y)
}
})
menu.append(menuItem)
menu.popup(mainWindow)
}
function notify(params) {
if(!notifySupport || !params) return false
let options = {

View File

@ -1,20 +1,36 @@
import React from 'react'
import * as antd from 'antd'
import verbosity from 'core/libs/verbosity'
import * as Icons from 'components/Icons'
import styles from './index.less'
import ReactDOM from 'react-dom'
import * as antd from 'antd'
import React from 'react'
export interface ContextMenu_props {
visible: boolean;
fragment: object;
let onRend = false
const renderDiv = document.createElement('div')
export interface ContextMenuComponent_props {
renderList: any;
yPos: number;
xPos: number;
app: any;
dispatch: any;
}
export default class ContextMenu extends React.Component<ContextMenu_props>{
constructor(props){
export class ContextMenuComponent extends React.PureComponent<ContextMenuComponent_props>{
listening: boolean
wrapperRef: any
eventListener: () => void
renderDiv: HTMLDivElement
state: any
constructor(props:any){
super(props)
this.state = {
renderList: null,
loading: true
}
this.renderDiv = renderDiv
this.listening = false
this.setWrapperRef = this.setWrapperRef.bind(this)
this.handleClickOutside = this.handleClickOutside.bind(this)
@ -23,47 +39,107 @@ export default class ContextMenu extends React.Component<ContextMenu_props>{
this.listening = true
}
}
setWrapperRef(node){
this.wrapperRef = node
}
handleClickOutside(event) {
if ( this.props.visible && this.wrapperRef && !this.wrapperRef.contains(event.target)) {
if (this.wrapperRef && !this.wrapperRef.contains(event.target)) {
this.listening = false
window.contextMenu.toogle()
DestroyContextMenu()
document.removeEventListener('click', this.eventListener, false)
}
}
filterArray(data: any[]) {
let tmp: any = []
return new Promise(resolve => {
data.forEach(async (element: { require: string; }) => {
if (typeof(element.require) !== 'undefined') {
const validRequire = await window.requireQuery(element.require)
validRequire? tmp.push(element) : null
}else{
tmp.push(element)
}
})
resolve(tmp)
})
}
async queryMenu(data){
this.setState({ renderList: await this.filterArray(data), loading: false })
}
handle(e:any, props:any){
if(!e || typeof(e) == 'undefined') {
return false
}
typeof(e.onClick) !== 'undefined' && e.onClick ? e.onClick(props) : null
typeof(e.keepOnClick) !== 'undefined' && e.keepOnClick ? null : DestroyContextMenu()
}
renderElements(){
if (!Array.isArray(this.state.renderList)) {
return null
}
return this.state.renderList.map((e:any) => {
return(
<div {...e.params.itemProps} onClick={() => this.handle(e.params, this.props)} key={e.key}>
{e.icon}{e.title}
</div>
)
})
}
componentDidMount(){
if (this.props.renderList) {
this.queryMenu(this.props.renderList)
}
}
componentDidUpdate(){
!this.listening ? this.eventListener() : null
}
render(){
if (this.props.visible) {
return(
<div
id="contextualMenu"
ref={this.setWrapperRef}
className={styles.contextualMenu}
style={{
top: this.props.yPos,
left: this.props.xPos,
}}>
{this.props.fragment}
</div>
)
if (this.state.loading) {
return null
}
return null
return (
<div
id="contextualMenu"
ref={this.setWrapperRef}
className={styles.contextualMenu}
style={{
top: this.props.yPos,
left: this.props.xPos,
}}>
{this.renderElements()}
</div>
)
}
}
ContextMenu.defaultProps = {
visible: false,
fragment: null,
xPos: 0,
yPos: 0
export function DestroyContextMenu(){
verbosity('destroying')
const unmountResult = ReactDOM.unmountComponentAtNode(renderDiv)
if (unmountResult && renderDiv.parentNode) {
renderDiv.parentNode.removeChild(renderDiv)
onRend = false
}
}
export function OpenContextMenu(props){
verbosity([props])
const renderComponent = React.createElement(ContextMenuComponent, props)
if (onRend) {
DestroyContextMenu()
}
document.body.appendChild(renderDiv).setAttribute('id', 'contextMenu')
ReactDOM.render(renderComponent, renderDiv)
onRend = true
}
export default OpenContextMenu

View File

@ -31,7 +31,7 @@ const defaultPayload = {
ReportIgnore: false,
}
const contextMenuPost = [
const contextMenuList = [
{
key: "inspect_element",
title: "Copy URL",
@ -71,10 +71,12 @@ const contextMenuPost = [
@connect(({ app }) => ({ app }))
export default class PostCard extends React.Component {
state = {
visibleMoreMenu: false,
payload: this.props.payload,
}
state = {
visibleMoreMenu: false,
payload: this.props.payload,
}
elementRef = React.createRef()
handleDispatchInvoke(key, payload) {
this.props.dispatch({
@ -175,6 +177,17 @@ export default class PostCard extends React.Component {
)
}
componentDidMount(){
window.contextMenu.addEventListener(
{
priority: 100,
onEventRender: contextMenuList,
ref: this.elementRef.current,
props: { id: this.state.payload.id }
}
)
}
render() {
//
@ -187,12 +200,16 @@ export default class PostCard extends React.Component {
]
return (
<div key={this.state.payload.id} id={this.state.payload.id} className={styles.post_card_wrapper}>
<div ref={this.elementRef} key={this.state.payload.id} id={this.state.payload.id} className={styles.post_card_wrapper}>
<antd.Card
className={settings("post_hidebar") ? null : styles.showMode}
onDoubleClick={() => null}
onClick={() => this.goElementById(this.state.payload.id)}
onContextMenu={(e) => { window.contextMenu.open({ xPos: e.clientX, yPos: e.clientY, fragment: window.contextMenu.generate(contextMenuPost, this.state.payload) }) }}
// onContextMenu={(e) => {
// e.stopPropagation()
// e.preventDefault()
// window.contextMenu.open({ xPos: e.clientX, yPos: e.clientY, renderList: contextMenuList, id: this.state.payload.id })
// }}
actions={actions}
hoverable
>

View File

@ -17,23 +17,19 @@ import contextMenuList from 'globals/contextMenu'
import styles from './PrimaryLayout.less'
const { Content } = antd.Layout
const { Sider, Overlay, ContextMenu } = AppLayout
const { Sider, Overlay } = AppLayout
const isActive = (key) => { return key? key.active : false }
@withRouter
@connect(({ app, loading }) => ({ app, loading }))
@connect(({ app, contextMenu, loading }) => ({ app, contextMenu, loading }))
class PrimaryLayout extends React.Component {
constructor(props) {
super(props)
this.state = {
collapsed: app_config.default_collapse_sider ? true : false,
isMobile: false
},
this.handleContextMenu = document.getElementById("root").addEventListener("contextmenu", (e) => {
e.preventDefault()
window.contextMenu.open({ xPos: e.clientX, yPos: e.clientY, fragment: window.contextMenu.generate(contextMenuList, e) })
}, false)
}
// include API extensions
window.openLink = (e) => {
if(this.props.app.embedded){
@ -42,10 +38,11 @@ class PrimaryLayout extends React.Component {
window.open(e)
}
}
window.requireQuery = (require) =>{
return new Promise(resolve => {
this.props.dispatch({
type: 'app/isUser',
type: 'app/requireQuery',
payload: require,
callback: (e) => {
resolve(e)
@ -53,82 +50,35 @@ class PrimaryLayout extends React.Component {
})
})
}
window.inspectElement = (e) => this.props.dispatch({
type: "app/ipcInvoke",
payload: {
key: "inspectElement",
payload: { x: e.clientX, y: e.clientY }
payload: { x: e.xPos, y: e.yPos }
}
})
window.toogleSidebarCollapse = () => {
this.props.dispatch({
type: "app/handleCollapseSidebar",
payload: !this.props.app.sidebar_collapsed
})
}
window.contextMenu = this.props.app.contextMenu
window.contextMenu.open = (payload) => {
if (!payload) return false
this.props.dispatch({
type: "app/updateState",
payload: {contextMenu: {
...this.props.app.contextMenu,
xPos: payload.xPos,
yPos: payload.yPos,
fragment: payload.fragment,
visible: true
}}
})
}
window.contextMenu.handle = (e, ...rest) => {
if(!e || typeof(e) == 'undefined') {
return false
}
typeof(e.onClick) !== 'undefined' && e.onClick ? e.onClick(...rest) : null
typeof(e.keepOnClick) !== 'undefined' && e.keepOnClick ? null : window.contextMenu.toogle()
}
window.contextMenu.generate = (payload, ...rest) => {
if(!payload) return false
let tmp = []
payload.forEach(async(e) => {
if (typeof(e.params.require) !== 'undefined') {
if(await window.requireQuery(e.params.require)){
e.valid = true
tmp.push(e)
}else{
// bruh
}
}else{
tmp.push(e)
}
})
return tmp.map((e) => {
return(
<div {...e.params.itemProps} onClick={() => window.contextMenu.handle(e.params, ...rest)} key={e.key}>
{e.icon}{e.title}
</div>
)
})
}
window.contextMenu.toogle = () => {
this.props.dispatch({
type: "app/updateState",
payload: {contextMenu: {...this.props.app.contextMenu, visible: !this.props.app.contextMenu.visible} }
})
}
}
componentDidMount() {
this.handleContextMenu
if (this.props.app.embedded) {
window.contextMenu.addEventListener(
{
priority: 1,
onEventRender: contextMenuList,
ref: document.getElementById("root")
}
)
}
this.enquireHandler = enquireScreen(mobile => {
const { isMobile } = this.state
if (isMobile !== mobile) {
@ -140,8 +90,10 @@ class PrimaryLayout extends React.Component {
}
componentWillUnmount() {
window.removeEventListener("contextmenu", this.handleContextMenu)
unenquireScreen(this.enquireHandler)
if (this.props.contextMenu) {
window.contextMenu.destroy()
}
}
onCollapseChange = () => {
@ -154,7 +106,6 @@ class PrimaryLayout extends React.Component {
const { location, dispatch, children, app } = this.props
const { collapsed, isMobile } = this.state
const { onCollapseChange } = this
const { contextMenu } = app
const currentTheme = theme.get()
const SiderProps = { isMobile, collapsed, onCollapseChange }
@ -165,13 +116,6 @@ class PrimaryLayout extends React.Component {
return (
<React.Fragment>
<ContextMenu
visible={contextMenu.visible}
fragment={contextMenu.fragment}
xPos={contextMenu.xPos}
yPos={contextMenu.yPos}
onClose={this.handleCloseContextMenu}
/>
{isActive(currentTheme['backgroundImage'])
?<div style={{
backgroundImage: `url(${currentTheme.backgroundImage.src})`,

106
src/models/contextMenu.js Normal file
View File

@ -0,0 +1,106 @@
import { OpenContextMenu, DestroyContextMenu } from 'components/Layout/ContextMenu'
import verbosity from 'core/libs/verbosity'
const defaultState = {
xPos: 0,
yPos: 0,
renderList: null,
eventHandlers: [],
}
export default {
namespace: 'contextMenu',
state: defaultState,
subscriptions: {
setup({ dispatch }) {
window.contextMenu = {
open: (payload) => {
if (!payload) return false
dispatch({ type: "open", payload })
},
destroy: () => {
dispatch({ type: "destroy" })
},
addEventListener: (payload) => {
if (!payload) return false
dispatch({ type: "handleAddEvent", payload: payload })
}
}
document.getElementById("root").addEventListener("contextmenu", (e) => {
e.preventDefault()
dispatch({ type: "handleEvents", payload: e })
}, false)
dispatch({ type: "setup" })
}
},
effects: {
*setup({payload}, { select, put }){
const state = yield select(state => state.contextMenu)
window.contextMenu = { ...window.contextMenu, ...state }
},
*handleEvents({payload}, { select, put }){
const eventHandlers = yield select(state => state.contextMenu.eventHandlers)
verbosity(["New event recived =>", payload], { color: "magenta"})
if (Array.isArray(eventHandlers)) {
let byIndex = []
let prioritys = []
eventHandlers.forEach((e) => {
if (typeof(e.ref) !== "undefined") {
if (e.ref.contains(payload.srcElement)) {
byIndex[e.priority] = e
prioritys = Object.keys(byIndex).sort()
}
}
})
prioritys = prioritys.reverse()
const prioritaryEvent = byIndex[prioritys[0]]
if (prioritaryEvent != null && typeof(prioritaryEvent.onEventRender) !== "undefined") {
window.contextMenu.open({
renderList: prioritaryEvent.onEventRender,
...prioritaryEvent.props,
yPos: payload.clientY,
xPos: payload.clientX,
event: payload
})
}else{
verbosity('not valid events detected')
}
}else{
verbosity('eventHandlers is not an array, exiting')
}
},
*open({payload}, { select, put }){
OpenContextMenu(payload)
}
},
reducers: {
updateState(state, { payload }) {
return {
...state,
...payload,
};
},
handleAddEvent(state, { payload }){
let tmp = []
tmp.push(payload)
const concated = state.eventHandlers.concat(tmp)
state.eventHandlers = concated
},
open(state, { payload }){
state = {...state, ...payload}
},
close(state){
state = {...state, ...payload}
DestroyContextMenu()
}
},
}

View File

@ -0,0 +1,31 @@
import ContextMenu from 'components/Layout/ContextMenu'
import { connect } from 'umi'
import * as antd from 'antd'
import React from 'react'
@connect(({ app }) => ({ app }))
export default class ContextMenuDebug extends React.Component{
openContext(){
const list = [
{
key: "something",
title: "test",
params: {
onClick: (e) => {
console.log("yepe", e)
}
}
}
]
ContextMenu({ xPos: 0, yPos: 0, renderList: list })
}
render(){
return(
<div>
<antd.Button onClick={this.openContext}> openNew </antd.Button>
</div>
)
}
}

View File

@ -8,7 +8,7 @@ import ThemeDebug from './debuggers/theme'
import SocketDebug from './debuggers/socket'
import VerbosityDebug from './debuggers/verbosity'
import InternalDebug from './debuggers/internals'
import ContextMenuDebug from './debuggers/contextmenu.js'
const Debuggers = {
api: <ApiDebug />,
@ -16,7 +16,8 @@ const Debuggers = {
theme: <ThemeDebug />,
socket: <SocketDebug />,
verbosity: <VerbosityDebug />,
internals: <InternalDebug />
internals: <InternalDebug />,
contextMenu: <ContextMenuDebug />,
}
const menuList = [
@ -40,6 +41,11 @@ const menuList = [
title: "Socket",
icon: <Icons.Box />
},
{
key: "contextMenu",
title: "contextMenu",
icon: <Icons.Menu />
},
{
key: "verbosity",
title: "Verbosity",

View File

@ -2,8 +2,9 @@ import React from 'react';
import { connect } from 'umi';
import * as antd from 'antd'
import * as Icons from 'components/Icons'
import { objectToArray } from 'core'
@connect(({ app }) => ({ app }))
@connect(({ app, contextMenu }) => ({ app, contextMenu }))
class PageIndex extends React.PureComponent {
constructor(props){
super(props)
@ -22,26 +23,9 @@ class PageIndex extends React.PureComponent {
}
render() {
const AppState = () => {
if (!this.props.app) {
return false
}
let tmp = []
const keys = Object.keys(this.props.app)
const values = Object.values(this.props.app)
const lenght = keys.length
if(lenght > 0){
for (let i = 0; i < lenght; i++) {
let obj = {}
obj.key = keys[i]
obj.value = values[i]
tmp[i] = obj
}
const map = tmp.map(e => {
const modelToMap = (data) => {
if(!data) return false
return objectToArray(data).map(e => {
try {
const v = JSON.stringify(e.value)
if(!v) return false
@ -56,22 +40,16 @@ class PageIndex extends React.PureComponent {
}
})
return map
}
return null
}
const handleUpdateData = () => {
this.props.dispatch({
type: "app/handleUpdateData"
})
}
return (
<div>
<antd.Card style={{ wordBreak: 'break-all' }} title={<><Icons.Redux style={{ height: '19px', marginRight: '7px' }} /> Redux state</>}>
{AppState()}
<antd.Card style={{ wordBreak: 'break-all' }} title={<><Icons.Redux style={{ height: '19px', marginRight: '7px' }} /> App model</>}>
{modelToMap(this.props.app)}
</antd.Card>
<antd.Card style={{ wordBreak: 'break-all' }} title={<><Icons.Redux style={{ height: '19px', marginRight: '7px' }} /> ContextMenu model</>}>
{modelToMap(this.props.contextMenu)}
</antd.Card>
<antd.Button onClick={() => handleUpdateData()} > updateData </antd.Button>
</div>
);