-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #28 from redstonekasi/contextMenu
Context menu library
- Loading branch information
Showing
8 changed files
with
307 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { ExtensionWebpackModule, Patch } from "@moonlight-mod/types"; | ||
|
||
export const patches: Patch[] = [ | ||
{ | ||
find: "Menu API only allows Items and groups of Items as children.", | ||
replace: [ | ||
{ | ||
match: | ||
/(?<=let{navId[^}]+?}=(.),(.)=function .\(.\){.+(?=,.=function))/, | ||
replacement: (_, props, items) => | ||
`,__contextMenu=!${props}.__contextMenu_evilMenu&&require("contextMenu_contextMenu")._patchMenu(${props}, ${items})` | ||
} | ||
] | ||
}, | ||
{ | ||
find: ".getContextMenu(", | ||
replace: [ | ||
{ | ||
match: /(?<=let\{[^}]+?\}=.;return ).\({[^}]+?}\)/, | ||
replacement: (render) => | ||
`require("contextMenu_contextMenu")._saveProps(${render})` | ||
} | ||
] | ||
} | ||
]; | ||
|
||
export const webpackModules: Record<string, ExtensionWebpackModule> = { | ||
contextMenu: { | ||
dependencies: [{ ext: "spacepack", id: "spacepack" }, "MenuGroup:"] | ||
}, | ||
evilMenu: { | ||
dependencies: [ | ||
{ ext: "spacepack", id: "spacepack" }, | ||
"Menu API only allows Items and groups of Items as children." | ||
] | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{ | ||
"id": "contextMenu", | ||
"meta": { | ||
"name": "Context Menu", | ||
"tagline": "A library for patching and creating context menus", | ||
"authors": ["redstonekasi"], | ||
"tags": ["library"] | ||
} | ||
} |
66 changes: 66 additions & 0 deletions
66
packages/core-extensions/src/contextMenu/webpackModules/contextMenu.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import { | ||
InternalItem, | ||
MenuElement, | ||
MenuProps | ||
} from "@moonlight-mod/types/coreExtensions/contextMenu"; | ||
import spacepack from "@moonlight-mod/wp/spacepack_spacepack"; | ||
import parser from "@moonlight-mod/wp/contextMenu_evilMenu"; | ||
|
||
type Patch = { | ||
navId: string; | ||
item: ( | ||
props: any | ||
) => | ||
| React.ReactComponentElement<MenuElement> | ||
| React.ReactComponentElement<MenuElement>[]; | ||
anchorId: string; | ||
before: boolean; | ||
}; | ||
|
||
export function addItem<T>( | ||
navId: string, | ||
item: ( | ||
props: T | ||
) => | ||
| React.ReactComponentElement<MenuElement> | ||
| React.ReactComponentElement<MenuElement>[], | ||
anchorId: string, | ||
before = false | ||
) { | ||
patches.push({ navId, item, anchorId, before }); | ||
} | ||
|
||
export const patches: Patch[] = []; | ||
function _patchMenu(props: MenuProps, items: InternalItem[]) { | ||
const matches = patches.filter((p) => p.navId === props.navId); | ||
if (!matches.length) return; | ||
|
||
for (const patch of matches) { | ||
const idx = items.findIndex((i) => i.key === patch.anchorId); | ||
if (idx === -1) continue; | ||
items.splice(idx + 1 - +patch.before, 0, ...parser(patch.item(menuProps))); | ||
} | ||
} | ||
|
||
let menuProps: any; | ||
function _saveProps(el: any) { | ||
menuProps = el.props; | ||
|
||
const original = el.props.config.onClose; | ||
el.props.config.onClose = function (...args: any[]) { | ||
menuProps = undefined; | ||
return original?.apply(this, args); | ||
}; | ||
|
||
return el; | ||
} | ||
|
||
const MenuElements = spacepack.findByCode("return null", "MenuGroup:")[0] | ||
.exports; | ||
|
||
module.exports = { | ||
...MenuElements, | ||
addItem, | ||
_patchMenu, | ||
_saveProps | ||
}; |
24 changes: 24 additions & 0 deletions
24
packages/core-extensions/src/contextMenu/webpackModules/evilMenu.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import spacepack from "@moonlight-mod/wp/spacepack_spacepack"; | ||
|
||
let code = | ||
spacepack.require.m[ | ||
spacepack.findByCode( | ||
"Menu API only allows Items and groups of Items as children." | ||
)[0].id | ||
].toString(); | ||
code = code.replace(/,.=(?=function .\(.\){.+?,.=function)/, ";return "); | ||
code = code.replace(/,(?=__contextMenu)/, ";let "); | ||
const mod = new Function( | ||
"module", | ||
"exports", | ||
"require", | ||
`(${code}).apply(this, arguments)` | ||
); | ||
const exp: any = {}; | ||
mod({}, exp, require); | ||
module.exports = (el: any) => { | ||
return exp.Menu({ | ||
children: el, | ||
__contextMenu_evilMenu: true | ||
}); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
// TODO: Deduplicate common props | ||
|
||
export type Menu = React.FunctionComponent<{ | ||
navId: string; | ||
variant?: string; | ||
hideScrollbar?: boolean; | ||
className?: string; | ||
children: React.ReactComponentElement<MenuElement>[]; | ||
onClose?: () => void; | ||
onSelect?: () => void; | ||
}>; | ||
export type MenuProps = React.ComponentProps<Menu>; | ||
|
||
export type MenuElement = | ||
| MenuSeparator | ||
| MenuGroup | ||
| MenuItem | ||
| MenuCheckboxItem | ||
| MenuRadioItem | ||
| MenuControlItem; | ||
|
||
/* eslint-disable prettier/prettier */ | ||
export type MenuSeparator = React.FunctionComponent; | ||
export type MenuGroup = React.FunctionComponent<{ | ||
label?: string; | ||
className?: string; | ||
color?: string; | ||
children: React.ReactComponentElement<MenuElement>[]; | ||
}>; | ||
export type MenuItem = React.FunctionComponent<{ | ||
id: any; | ||
dontCloseOnActionIfHoldingShiftKey?: boolean; | ||
} & ({ | ||
label: string; | ||
subtext?: string; | ||
color?: string; | ||
hint?: string; | ||
disabled?: boolean; | ||
icon?: any; | ||
showIconFirst?: boolean; | ||
imageUrl?: string; | ||
|
||
className?: string; | ||
focusedClassName?: string; | ||
subMenuIconClassName?: string; | ||
|
||
action?: () => void; | ||
onFocus?: () => void; | ||
|
||
iconProps?: any; | ||
sparkle?: any; | ||
|
||
children?: React.ReactComponentElement<MenuElement>[]; | ||
onChildrenScroll?: any; | ||
childRowHeight?: any; | ||
listClassName?: string; | ||
subMenuClassName?: string; | ||
} | { | ||
color?: string; | ||
disabled?: boolean; | ||
keepItemStyles?: boolean; | ||
|
||
action?: () => void; | ||
|
||
render: any; | ||
navigable?: boolean; | ||
})>; | ||
export type MenuCheckboxItem = React.FunctionComponent<{ | ||
id: any; | ||
label: string; | ||
subtext?: string; | ||
color?: string; | ||
className?: string; | ||
focusedClassName?: string; | ||
disabled?: boolean; | ||
checked: boolean; | ||
action?: () => void; | ||
}>; | ||
export type MenuRadioItem = React.FunctionComponent<{ | ||
id: any; | ||
label: string; | ||
subtext?: string; | ||
color?: string; | ||
disabled?: boolean; | ||
action?: () => void; | ||
}>; | ||
export type MenuControlItem = React.FunctionComponent<{ | ||
id: any; | ||
label: string; | ||
color?: string; | ||
disabled?: boolean; | ||
showDefaultFocus?: boolean; | ||
} & ({ | ||
control: any; | ||
} | { | ||
control?: undefined; | ||
interactive?: boolean; | ||
children?: React.ReactComponentElement<MenuElement>[]; | ||
})>; | ||
/* eslint-disable prettier/prettier */ | ||
|
||
export type ContextMenu = { | ||
addItem: ( | ||
navId: string, | ||
item: (props: any) => React.ReactComponentElement<MenuElement>, | ||
anchorId: string, | ||
before?: boolean | ||
) => void; | ||
|
||
MenuCheckboxItem: MenuCheckboxItem; | ||
MenuControlItem: MenuControlItem; | ||
MenuGroup: MenuGroup; | ||
MenuItem: MenuItem; | ||
MenuRadioItem: MenuRadioItem; | ||
MenuSeparator: MenuSeparator; | ||
}; | ||
|
||
export type InternalItem = { | ||
type: string; | ||
key?: string; | ||
}; | ||
|
||
export type InternalSeparator = { | ||
type: "separator"; | ||
navigable: false; | ||
}; | ||
export type InternalGroupStart = { | ||
type: "groupstart"; | ||
length: number; | ||
navigable: false; | ||
props: React.ComponentProps<MenuGroup>; | ||
}; | ||
export type InternalGroupEnd = { | ||
type: "groupend"; | ||
} & Omit<InternalGroupStart, "type">; | ||
export type InternalCustomItem = { | ||
type: "customitem"; | ||
key: any; | ||
navigable?: boolean; | ||
render: any; | ||
props: Extract<React.ComponentProps<MenuItem>, { render: any }>; | ||
}; | ||
export type InternalItem_ = { | ||
type: "item"; | ||
key: any; | ||
navigable: true; | ||
label: string; | ||
}; | ||
|
||
export type EvilItemParser = ( | ||
el: | ||
| React.ReactComponentElement<MenuElement> | ||
| React.ReactComponentElement<MenuElement>[] | ||
) => InternalItem[]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters