fix: context menu selection outline

This commit is contained in:
Kevin
2025-06-17 08:12:19 -05:00
parent f0fb45404d
commit 48f68f6c08
5 changed files with 43 additions and 14 deletions

View File

@ -15,6 +15,7 @@ export interface ContextMenuProps
menuTargetElement?: RefObject<HTMLElement> menuTargetElement?: RefObject<HTMLElement>
guard?: (e: globalThis.MouseEvent) => boolean guard?: (e: globalThis.MouseEvent) => boolean
event?: 'contextmenu' | 'mouseup' event?: 'contextmenu' | 'mouseup'
callback?: (event: globalThis.MouseEvent)=> void
} }
const DefaultContextMenuItems = [ const DefaultContextMenuItems = [
@ -29,6 +30,7 @@ export function ContextMenu({
className, className,
guard, guard,
event = 'contextmenu', event = 'contextmenu',
callback,
...props ...props
}: ContextMenuProps) { }: ContextMenuProps) {
const dialogRef = useRef<HTMLDivElement>(null) const dialogRef = useRef<HTMLDivElement>(null)
@ -43,6 +45,9 @@ export function ContextMenu({
}) })
const handleContextMenu = useCallback( const handleContextMenu = useCallback(
(e: globalThis.MouseEvent) => { (e: globalThis.MouseEvent) => {
if (callback) {
callback(e)
}
if (guard && !guard(e)) return if (guard && !guard(e)) return
e.preventDefault() e.preventDefault()
// This stopPropagation is needed in case multiple nested items use a separate context menu each, // This stopPropagation is needed in case multiple nested items use a separate context menu each,

View File

@ -70,7 +70,7 @@ export const FileExplorer = ({
{rowsToRender.map((row, index, original) => { {rowsToRender.map((row, index, original) => {
const key = constructPath({ const key = constructPath({
parentPath: row.parentPath, parentPath: row.parentPath,
name: row.name name: row.name,
}) })
const renderRow: FileExplorerRender = { const renderRow: FileExplorerRender = {
...row, ...row,
@ -127,18 +127,18 @@ export const FileExplorerRowElement = ({
row.rowClicked(row.domIndex) row.rowClicked(row.domIndex)
}} }}
draggable="true" draggable="true"
onDragOver={(event)=>{ onDragOver={(event) => {
if (!row.isOpen && row.isFolder) { if (!row.isOpen && row.isFolder) {
// on drag over, open! // on drag over, open!
row.rowOpen() row.rowOpen()
} }
event.preventDefault() event.preventDefault()
}} }}
onDragStart={((event)=>{ onDragStart={(event) => {
console.log(event.target.innerText,'onDragStart') console.log(event.target.innerText, 'onDragStart')
})} }}
onDrop={(event)=>{ onDrop={(event) => {
console.log(event.target.innerText,'onDrop') console.log(event.target.innerText, 'onDrop')
}} }}
> >
<div style={{ width: '0.25rem' }}></div> <div style={{ width: '0.25rem' }}></div>
@ -158,6 +158,7 @@ export const FileExplorerRowElement = ({
onDelete={() => {}} onDelete={() => {}}
onClone={() => {}} onClone={() => {}}
onOpenInNewWindow={() => {}} onOpenInNewWindow={() => {}}
callback={row.rowContextMenu}
/> />
</div> </div>
) )
@ -169,12 +170,14 @@ function FileExplorerRowContextMenu({
onDelete, onDelete,
onClone, onClone,
onOpenInNewWindow, onOpenInNewWindow,
callback
}: FileExplorerRowContextMenuProps) { }: FileExplorerRowContextMenuProps) {
const platform = usePlatform() const platform = usePlatform()
const metaKey = platform === 'macos' ? '⌘' : 'Ctrl' const metaKey = platform === 'macos' ? '⌘' : 'Ctrl'
return ( return (
<ContextMenu <ContextMenu
menuTargetElement={itemRef} menuTargetElement={itemRef}
callback={callback}
items={[ items={[
<ContextMenuItem <ContextMenuItem
data-testid="context-menu-rename" data-testid="context-menu-rename"

View File

@ -9,8 +9,10 @@ import {
CONTAINER_IS_SELECTED, CONTAINER_IS_SELECTED,
STARTING_INDEX_TO_SELECT, STARTING_INDEX_TO_SELECT,
} from '@src/components/Explorer/utils' } from '@src/components/Explorer/utils'
import type { FileExplorerEntry , import type {
FileExplorerRow} from '@src/components/Explorer/utils' FileExplorerEntry,
FileExplorerRow,
} from '@src/components/Explorer/utils'
import { FileExplorerHeaderActions } from '@src/components/Explorer/FileExplorerHeaderActions' import { FileExplorerHeaderActions } from '@src/components/Explorer/FileExplorerHeaderActions'
import { useState, useRef, useEffect } from 'react' import { useState, useRef, useEffect } from 'react'
import { systemIOActor } from '@src/lib/singletons' import { systemIOActor } from '@src/lib/singletons'
@ -159,6 +161,9 @@ export const ProjectExplorer = ({
newOpenedRows[key] = true newOpenedRows[key] = true
setOpenedRows(newOpenedRows) setOpenedRows(newOpenedRows)
}, },
rowContextMenu: () => {
// NO OP
},
isFake: false, isFake: false,
activeIndex: activeIndex, activeIndex: activeIndex,
} }
@ -170,8 +175,18 @@ export const ProjectExplorer = ({
return row.render return row.render
}) })
// update the callback for rowContextMenu to be the index based on rendering
// Gotcha: you will see if you spam the context menu you will not be able to select a new one
// until closing
requestedRowsToRender.forEach((r, index)=>{
r.rowContextMenu = () => {
setActiveIndex(index)
}
})
setRowsToRender(requestedRowsToRender) setRowsToRender(requestedRowsToRender)
rowsToRenderRef.current = requestedRowsToRender rowsToRenderRef.current = requestedRowsToRender
console.log(activeIndex)
}, [project, openedRows, fakeRow, activeIndex]) }, [project, openedRows, fakeRow, activeIndex])
// Handle clicks and keyboard presses within the global DOM level // Handle clicks and keyboard presses within the global DOM level

View File

@ -23,7 +23,7 @@ export interface FileExplorerRow extends FileExplorerEntry {
* they are placed in the DOM as if they are real but not from the source of truth * they are placed in the DOM as if they are real but not from the source of truth
*/ */
isFake: boolean isFake: boolean
activeIndex: number, activeIndex: number
rowOpen: () => void rowOpen: () => void
} }

View File

@ -39,7 +39,11 @@ import {
enableMenu, enableMenu,
} from '@src/menu' } from '@src/menu'
import fs from 'fs' import fs from 'fs'
import { installExtension, REDUX_DEVTOOLS, REACT_DEVELOPER_TOOLS } from 'electron-devtools-installer'; import {
installExtension,
REDUX_DEVTOOLS,
REACT_DEVELOPER_TOOLS,
} from 'electron-devtools-installer'
// If we're on Windows, pull the local system TLS CAs in // If we're on Windows, pull the local system TLS CAs in
require('win-ca') require('win-ca')
@ -83,9 +87,11 @@ console.log('Parsed CLI args', args)
app.whenReady().then(() => { app.whenReady().then(() => {
installExtension([REDUX_DEVTOOLS, REACT_DEVELOPER_TOOLS]) installExtension([REDUX_DEVTOOLS, REACT_DEVELOPER_TOOLS])
.then(([redux, react]) => console.log(`Added Extensions: ${redux.name}, ${react.name}`)) .then(([redux, react]) =>
.catch((err) => console.log('An error occurred: ', err)); console.log(`Added Extensions: ${redux.name}, ${react.name}`)
}); )
.catch((err) => console.log('An error occurred: ', err))
})
/// Register our application to handle all "zoo-studio:" protocols. /// Register our application to handle all "zoo-studio:" protocols.
const singleInstanceLock = app.requestSingleInstanceLock() const singleInstanceLock = app.requestSingleInstanceLock()