Fix lint issues

This commit is contained in:
Eric Clemmons
2024-11-03 12:09:03 -06:00
parent 6a2fe0cca3
commit bd4b288a1d
3 changed files with 247 additions and 254 deletions

View File

@@ -5,7 +5,7 @@
"build": "turbo run build --filter=click-to-*-component",
"env": "env",
"dev": "turbo run dev --parallel",
"lint": "turbo run lint",
"lint": "turbo run lint --filter=click-to-*-component",
"prerelease": "turbo run build",
"release": "changeset publish"
},

View File

@@ -29,282 +29,276 @@ import { getPropsForInstance } from './getPropsForInstance.js'
import { getReactInstancesForElement } from './getReactInstancesForElement.js'
import { getSourceForInstance } from './getSourceForInstance.js'
export const ContextMenu = React.forwardRef(
(
/** @type {Props} */
props,
ref
) => {
const { onClose, pathModifier } = props
// @ts-expect-error
export const ContextMenu = React.forwardRef((props, ref) => {
// @ts-expect-error
const { onClose, pathModifier } = props
const [target, setTarget] = React.useState(
/** @type {HTMLElement | null} */
(null)
)
const [target, setTarget] = React.useState(
/** @type {HTMLElement | null} */
(null)
)
const arrowRef = React.useRef(
/** @type {HTMLElement | null} */
(null)
)
const arrowRef = React.useRef(
/** @type {HTMLElement | null} */
(null)
)
const [activeIndex, setActiveIndex] = React.useState(
/** @type {number | null} */
(null)
)
const [activeIndex, setActiveIndex] = React.useState(
/** @type {number | null} */
(null)
)
const [open, setOpen] = React.useState(false)
const [open, setOpen] = React.useState(false)
const listItemsRef = React.useRef(
/** @type {Array<HTMLButtonElement | null>} */
([])
)
const listItemsRef = React.useRef(
/** @type {Array<HTMLButtonElement | null>} */
([])
)
const {
x,
y,
reference,
floating,
strategy,
refs,
update,
context,
placement,
middlewareData: { arrow: { x: arrowX, y: arrowY } = {} },
} = useFloating({
open,
onOpenChange(open) {
setOpen(open)
const {
x,
y,
reference,
floating,
strategy,
refs,
update,
context,
placement,
middlewareData: { arrow: { x: arrowX, y: arrowY } = {} },
} = useFloating({
open,
onOpenChange(open) {
setOpen(open)
if (!open) onClose?.()
},
middleware: [
offset({ mainAxis: 5, alignmentAxis: 4 }),
flip(),
shift(),
arrow({ element: arrowRef }),
],
placement: 'right',
})
if (!open) onClose?.()
},
middleware: [
offset({ mainAxis: 5, alignmentAxis: 4 }),
flip(),
shift(),
arrow({ element: arrowRef }),
],
placement: 'right',
})
const { getFloatingProps, getItemProps } = useInteractions([
useRole(context, { role: 'menu' }),
useDismiss(context),
useListNavigation(context, {
listRef: listItemsRef,
activeIndex,
onNavigate: setActiveIndex,
focusItemOnOpen: false,
}),
])
const { getFloatingProps, getItemProps } = useInteractions([
useRole(context, { role: 'menu' }),
useDismiss(context),
useListNavigation(context, {
listRef: listItemsRef,
activeIndex,
onNavigate: setActiveIndex,
focusItemOnOpen: false,
}),
])
React.useEffect(() => {
if (open && refs.reference.current && refs.floating.current) {
return autoUpdate(refs.reference.current, refs.floating.current, update)
}
}, [open, update, refs.reference, refs.floating])
React.useEffect(() => {
if (open && refs.reference.current && refs.floating.current) {
return autoUpdate(refs.reference.current, refs.floating.current, update)
}
}, [open, update, refs.reference, refs.floating])
const mergedReferenceRef = React.useMemo(
() => mergeRefs([ref, reference]),
[reference, ref]
)
const mergedReferenceRef = React.useMemo(
() => mergeRefs([ref, reference]),
[reference, ref]
)
React.useEffect(() => {
function onContextMenu(
/** @type {MouseEvent} */
e
) {
if (!e.altKey) {
return
}
e.preventDefault()
mergedReferenceRef({
getBoundingClientRect() {
return {
x: e.clientX,
y: e.clientY,
width: 0,
height: 0,
top: e.clientY,
right: e.clientX,
bottom: e.clientY,
left: e.clientX,
}
},
})
setOpen(true)
if (e.target instanceof HTMLElement) setTarget(e.target)
React.useEffect(() => {
function onContextMenu(
/** @type {MouseEvent} */
e
) {
if (!e.altKey) {
return
}
document.addEventListener('contextmenu', onContextMenu)
return () => {
document.removeEventListener('contextmenu', onContextMenu)
}
}, [mergedReferenceRef])
React.useLayoutEffect(() => {
if (open) {
refs.floating.current?.focus()
}
}, [open, refs.floating])
React.useLayoutEffect(() => {
if (!arrowRef.current) return
const staticSide = {
top: 'bottom',
right: 'left',
bottom: 'top',
left: 'right',
}[placement.split('-')[0]]
Object.assign(arrowRef.current.style, {
display: 'block',
left: arrowX != null ? `${arrowX}px` : '',
top: arrowY != null ? `${arrowY}px` : '',
right: '',
bottom: '',
[staticSide]: '-4px',
e.preventDefault()
mergedReferenceRef({
getBoundingClientRect() {
return {
x: e.clientX,
y: e.clientY,
width: 0,
height: 0,
top: e.clientY,
right: e.clientX,
bottom: e.clientY,
left: e.clientX,
}
},
})
}, [arrowX, arrowY, placement])
if (!target) {
return null
setOpen(true)
if (e.target instanceof HTMLElement) setTarget(e.target)
}
const instances = getReactInstancesForElement(target).filter((instance) =>
getSourceForInstance(instance)
)
document.addEventListener('contextmenu', onContextMenu)
return () => {
document.removeEventListener('contextmenu', onContextMenu)
}
}, [mergedReferenceRef])
return html`
<style key="click-to-component-contextmenu-style">
#floating-ui-root > div {
z-index: 2147483647;
}
React.useLayoutEffect(() => {
if (open) {
refs.floating.current?.focus()
}
}, [open, refs.floating])
[data-click-to-component-contextmenu],
[data-click-to-component-contextmenu] * {
box-sizing: border-box !important;
}
React.useLayoutEffect(() => {
if (!arrowRef.current) return
[data-click-to-component-contextmenu] {
all: unset;
outline: 0;
background: white;
color: black;
font-weight: bold;
overflow: visible;
padding: 5px;
font-size: 13px;
border-radius: 6px;
border: none;
const staticSide = {
top: 'bottom',
right: 'left',
bottom: 'top',
left: 'right',
}[placement.split('-')[0]]
--shadow-color: 0deg 0% 0%;
--shadow-elevation-low: 0px -1px 0.8px hsl(var(--shadow-color) / 0.1),
0px -1.2px 0.9px -2.5px hsl(var(--shadow-color) / 0.07),
0px -3px 2.3px -5px hsl(var(--shadow-color) / 0.03);
Object.assign(arrowRef.current.style, {
display: 'block',
left: arrowX != null ? `${arrowX}px` : '',
top: arrowY != null ? `${arrowY}px` : '',
right: '',
bottom: '',
[staticSide]: '-4px',
})
}, [arrowX, arrowY, placement])
--shadow-elevation-medium: 0px 1px 0.8px
hsl(var(--shadow-color) / 0.11),
0px 1.5px 1.1px -1.7px hsl(var(--shadow-color) / 0.08),
0px 5.1px 3.8px -3.3px hsl(var(--shadow-color) / 0.05),
0px 15px 11.3px -5px hsl(var(--shadow-color) / 0.03);
--shadow-elevation-high: 0px 1px 0.8px hsl(var(--shadow-color) / 0.1),
0px 1.1px 0.8px -0.7px hsl(var(--shadow-color) / 0.09),
0px 2.1px 1.6px -1.4px hsl(var(--shadow-color) / 0.07),
0px 4.9px 3.7px -2.1px hsl(var(--shadow-color) / 0.06),
0px 10.1px 7.6px -2.9px hsl(var(--shadow-color) / 0.05),
0px 18.9px 14.2px -3.6px hsl(var(--shadow-color) / 0.04),
0px 31.9px 23.9px -4.3px hsl(var(--shadow-color) / 0.02),
0px 50px 37.5px -5px hsl(var(--shadow-color) / 0.01);
if (!target) {
return null
}
box-shadow: var(--shadow-elevation-high);
filter: drop-shadow(0px 0px 0.5px rgba(0 0 0 / 50%));
}
const instances = getReactInstancesForElement(target).filter((instance) =>
getSourceForInstance(instance)
)
[data-click-to-component-contextmenu] button {
all: unset;
outline: 0;
display: flex;
flex-direction: column;
width: 100%;
padding: 5px;
border-radius: 4px;
font-size: 13px;
max-width: 50vw;
}
return html`
<style key="click-to-component-contextmenu-style">
#floating-ui-root > div {
z-index: 2147483647;
}
[data-click-to-component-contextmenu] button:focus,
[data-click-to-component-contextmenu] button:not([disabled]):active {
cursor: pointer;
background: royalblue;
color: white;
box-shadow: var(--shadow-elevation-medium);
}
[data-click-to-component-contextmenu],
[data-click-to-component-contextmenu] * {
box-sizing: border-box !important;
}
[data-click-to-component-contextmenu] button:focus code,
[data-click-to-component-contextmenu]
button:not([disabled]):active
code {
color: white;
}
[data-click-to-component-contextmenu] {
all: unset;
outline: 0;
background: white;
color: black;
font-weight: bold;
overflow: visible;
padding: 5px;
font-size: 13px;
border-radius: 6px;
border: none;
[data-click-to-component-contextmenu] button > * + * {
margin-top: 3px;
}
--shadow-color: 0deg 0% 0%;
--shadow-elevation-low: 0px -1px 0.8px hsl(var(--shadow-color) / 0.1),
0px -1.2px 0.9px -2.5px hsl(var(--shadow-color) / 0.07),
0px -3px 2.3px -5px hsl(var(--shadow-color) / 0.03);
[data-click-to-component-contextmenu] button code {
color: royalblue;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
'Liberation Mono', 'Courier New', monospace;
}
--shadow-elevation-medium: 0px 1px 0.8px hsl(var(--shadow-color) / 0.11),
0px 1.5px 1.1px -1.7px hsl(var(--shadow-color) / 0.08),
0px 5.1px 3.8px -3.3px hsl(var(--shadow-color) / 0.05),
0px 15px 11.3px -5px hsl(var(--shadow-color) / 0.03);
--shadow-elevation-high: 0px 1px 0.8px hsl(var(--shadow-color) / 0.1),
0px 1.1px 0.8px -0.7px hsl(var(--shadow-color) / 0.09),
0px 2.1px 1.6px -1.4px hsl(var(--shadow-color) / 0.07),
0px 4.9px 3.7px -2.1px hsl(var(--shadow-color) / 0.06),
0px 10.1px 7.6px -2.9px hsl(var(--shadow-color) / 0.05),
0px 18.9px 14.2px -3.6px hsl(var(--shadow-color) / 0.04),
0px 31.9px 23.9px -4.3px hsl(var(--shadow-color) / 0.02),
0px 50px 37.5px -5px hsl(var(--shadow-color) / 0.01);
[data-click-to-component-contextmenu] button code var {
background: rgba(0 0 0 / 5%);
cursor: help;
border-radius: 3px;
padding: 3px 6px;
font-style: normal;
font-weight: normal;
font-family: ui-sans-serif, system-ui, -apple-system,
BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,
'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
'Segoe UI Symbol', 'Noto Color Emoji';
}
box-shadow: var(--shadow-elevation-high);
filter: drop-shadow(0px 0px 0.5px rgba(0 0 0 / 50%));
}
[data-click-to-component-contextmenu] button cite {
font-weight: normal;
font-style: normal;
font-size: 11px;
opacity: 0.5;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
'Liberation Mono', 'Courier New', monospace;
}
[data-click-to-component-contextmenu] button {
all: unset;
outline: 0;
display: flex;
flex-direction: column;
width: 100%;
padding: 5px;
border-radius: 4px;
font-size: 13px;
max-width: 50vw;
}
[data-click-to-component-contextmenu] button cite data::after {
content: attr(value);
float: right;
padding-left: 15px;
font-family: ui-sans-serif, system-ui, -apple-system,
BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,
'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
'Segoe UI Symbol', 'Noto Color Emoji';
}
[data-click-to-component-contextmenu] button:focus,
[data-click-to-component-contextmenu] button:not([disabled]):active {
cursor: pointer;
background: royalblue;
color: white;
box-shadow: var(--shadow-elevation-medium);
}
[data-click-to-component-contextmenu-arrow] {
display: none;
position: absolute;
background: inherit;
width: 8px;
height: 8px;
transform: rotate(45deg);
}
</style>
[data-click-to-component-contextmenu] button:focus code,
[data-click-to-component-contextmenu] button:not([disabled]):active code {
color: white;
}
${open &&
html`
[data-click-to-component-contextmenu] button > * + * {
margin-top: 3px;
}
[data-click-to-component-contextmenu] button code {
color: royalblue;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
'Liberation Mono', 'Courier New', monospace;
}
[data-click-to-component-contextmenu] button code var {
background: rgba(0 0 0 / 5%);
cursor: help;
border-radius: 3px;
padding: 3px 6px;
font-style: normal;
font-weight: normal;
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif,
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
'Noto Color Emoji';
}
[data-click-to-component-contextmenu] button cite {
font-weight: normal;
font-style: normal;
font-size: 11px;
opacity: 0.5;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
'Liberation Mono', 'Courier New', monospace;
}
[data-click-to-component-contextmenu] button cite data::after {
content: attr(value);
float: right;
padding-left: 15px;
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif,
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
'Noto Color Emoji';
}
[data-click-to-component-contextmenu-arrow] {
display: none;
position: absolute;
background: inherit;
width: 8px;
height: 8px;
transform: rotate(45deg);
}
</style>
${open &&
html`
<${FloatingOverlay} key="click-to-component-overlay" lockScroll>
<${FloatingFocusManager} context=${context}>
<dialog
@@ -377,6 +371,5 @@ export const ContextMenu = React.forwardRef(
</${FloatingFocusManager}>
</${FloatingOverlay}>
`}
`
}
)
`
})

View File

@@ -1,12 +1,12 @@
export { ClickToComponent } from './ClickToComponent';
export { ClickToComponent } from './ClickToComponent'
export type Editor = 'vscode' | 'vscode-insiders' | 'cursor';
export type Editor = 'vscode' | 'vscode-insiders' | 'cursor' | string
export type PathModifier = (path: string) => string;
export type PathModifier = (path: string) => string
export type ClickToComponentProps = {
editor?: Editor;
pathModifier?: PathModifier;
editor?: Editor
pathModifier?: PathModifier
}
export type Coords = [MouseEvent['pageX'], MouseEvent['pageY']]
@@ -14,6 +14,6 @@ export type Coords = [MouseEvent['pageX'], MouseEvent['pageY']]
export type Target = HTMLElement
export type ContextMenuProps = {
onClose?: () => void;
pathModifier?: PathModifier;
onClose?: () => void
pathModifier?: PathModifier
}