Skip to content

Commit

Permalink
Fix input context menu focus issue on safari (#2804)
Browse files Browse the repository at this point in the history
  • Loading branch information
fonsp authored Feb 7, 2024
1 parent 7f85e89 commit 7566d35
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 66 deletions.
153 changes: 96 additions & 57 deletions frontend/components/CellInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -931,18 +931,23 @@ const InputContextMenu = ({ on_delete, cell_id, run_cell, skip_as_script, runnin
const timeout = useRef(null)
let pluto_actions = useContext(PlutoActionsContext)
const [open, setOpenState] = useState(false)
const element_ref = useRef(/** @type {HTMLButtonElement?} */ (null))
const button_ref = useRef(/** @type {HTMLButtonElement?} */ (null))
const list_ref = useRef(/** @type {HTMLButtonElement?} */ (null))

const prevously_focused_element_ref = useRef(/** @type {Element?} */ (null))
const setOpen = (val) => {
console.error("setOpen", val)
if (val) {
prevously_focused_element_ref.current = document.activeElement
}
setOpenState(val)
}
useLayoutEffect(() => {
if (open) {
element_ref.current?.querySelector("li")?.focus()
list_ref.current?.querySelector("button")?.focus()
console.log("forcing focus", list_ref.current?.querySelector("button"))
} else {
console.log("restoring focus", prevously_focused_element_ref.current)
if (prevously_focused_element_ref.current instanceof HTMLElement) prevously_focused_element_ref.current?.focus()
}
}, [open])
Expand Down Expand Up @@ -985,59 +990,93 @@ const InputContextMenu = ({ on_delete, cell_id, run_cell, skip_as_script, runnin
}
})

return html` <button
onClick=${() => setOpen(!open)}
onfocusout=${(e) => {
if (
// the focus is not one of the <li>
!element_ref.current?.matches(":focus-within") ||
// or the focus is on the button itself
e.relatedTarget === element_ref.current
)
setOpen(false)
}}
class=${cl({
input_context_menu: true,
open,
})}
title="Actions"
ref=${element_ref}
>
<span class="icon"></span>
${open
? html`<ul onMouseenter=${mouseenter}>
<li tabindex="0" onClick=${on_delete} title="Delete"><span class="delete ctx_icon" />Delete cell</li>
<li
tabindex="0"
onClick=${toggle_running_disabled}
title=${running_disabled ? "Enable and run the cell" : "Disable this cell, and all cells that depend on it"}
>
${running_disabled ? html`<span class="enable_cell ctx_icon" />` : html`<span class="disable_cell ctx_icon" />`}
${running_disabled ? html`<b>Enable cell</b>` : html`Disable cell`}
</li>
${any_logs
? html`<li tabindex="0" title="" onClick=${toggle_logs}>
${show_logs
? html`<span class="hide_logs ctx_icon" /><span>Hide logs</span>`
: html`<span class="show_logs ctx_icon" /><span>Show logs</span>`}
</li>`
: null}
${is_copy_output_supported()
? html`<li tabindex="0" title="Copy the output of this cell to the clipboard." onClick=${copy_output}>
<span class="copy_output ctx_icon" />Copy output
</li>`
: null}
<li
tabindex="0"
onClick=${toggle_skip_as_script}
title=${skip_as_script
? "This cell is currently stored in the notebook file as a Julia comment. Click here to disable."
: "Store this code in the notebook file as a Julia comment. This way, it will not run when the notebook runs as a script outside of Pluto."}
>
${skip_as_script ? html`<span class="skip_as_script ctx_icon" />` : html`<span class="run_as_script ctx_icon" />`}
${skip_as_script ? html`<b>Enable in file</b>` : html`Disable in file`}
</li>
</ul>`
: html``}
</button>`
return html`
<button
onClick=${(e) => {
setOpen(!open)
}}
class=${cl({
input_context_menu: true,
open,
})}
title="Actions"
ref=${button_ref}
>
<span class="icon"></span>
</button>
<div
class=${cl({
input_context_menu: true,
open,
})}
ref=${list_ref}
onfocusout=${(e) => {
const li_focused = list_ref.current?.matches(":focus-within") || list_ref.current?.contains(e.relatedTarget)
if (
!li_focused ||
// or the focus is on the list itself
e.relatedTarget === list_ref.current
)
setOpen(false)
}}
>
${open
? html`<ul onMouseenter=${mouseenter}>
<${InputContextMenuItem} tag="delete" contents="Delete cell" title="Delete cell" onClick=${on_delete} setOpen=${setOpen} />
<${InputContextMenuItem}
title=${running_disabled ? "Enable and run the cell" : "Disable this cell, and all cells that depend on it"}
tag=${running_disabled ? "enable_cell" : "disable_cell"}
contents=${running_disabled ? html`<b>Enable cell</b>` : html`Disable cell`}
onClick=${toggle_running_disabled}
setOpen=${setOpen}
/>
${any_logs
? html`<${InputContextMenuItem}
title=${show_logs ? "Show cell logs" : "Hide cell logs"}
tag=${show_logs ? "hide_logs" : "show_logs"}
contents=${show_logs ? "Hide logs" : "Show logs"}
onClick=${toggle_logs}
setOpen=${setOpen}
/>`
: null}
${is_copy_output_supported()
? html`<${InputContextMenuItem}
tag="copy_output"
contents="Copy output"
title="Copy the output of this cell to the clipboard."
onClick=${copy_output}
setOpen=${setOpen}
/>`
: null}
<${InputContextMenuItem}
title=${skip_as_script
? "This cell is currently stored in the notebook file as a Julia comment. Click here to disable."
: "Store this code in the notebook file as a Julia comment. This way, it will not run when the notebook runs as a script outside of Pluto."}
tag=${skip_as_script ? "run_as_script" : "skip_as_script"}
contents=${skip_as_script ? html`<b>Enable in file</b>` : html`Disable in file`}
onClick=${toggle_skip_as_script}
setOpen=${setOpen}
/>
</ul>`
: html``}
</div>
`
}

const InputContextMenuItem = ({ contents, title, onClick, setOpen, tag }) =>
html`<li>
<button
tabindex="0"
title=${title}
onClick=${(e) => {
setOpen(false)
onClick(e)
}}
class=${tag}
>
<span class=${`${tag} ctx_icon`} />${contents}
</button>
</li>`
31 changes: 22 additions & 9 deletions frontend/editor.css
Original file line number Diff line number Diff line change
Expand Up @@ -1687,7 +1687,8 @@ pluto-cell > button.add_cell > span::after {
background-image: url("https://cdn.jsdelivr.net/gh/ionic-team/[email protected]/src/svg/add-outline.svg");
filter: var(--image-filters);
}
pluto-input > button.input_context_menu ul {

pluto-input > .input_context_menu ul {
left: 100%;
top: -8px;
margin: 0;
Expand All @@ -1700,33 +1701,45 @@ pluto-input > button.input_context_menu ul {
background-color: var(--input-context-menu-bg-color);
}
@media screen and (min-width: 921px) {
pluto-input > button.input_context_menu ul {
pluto-input > .input_context_menu ul {
left: calc(100% - 3px);
}
}
@media screen and (max-width: 920px) {
pluto-input > button.input_context_menu {
z-index: 31;
pluto-input > .input_context_menu {
z-index: 1400;
}
pluto-input > button.input_context_menu ul {
pluto-input > .input_context_menu ul {
right: 0px;
left: unset;
z-index: 1401;
}
}
.input_context_menu li {

pluto-input > .input_context_menu li {
list-style: none;
margin-block-end: 0px;
display: flex;
align-items: stretch;
flex-direction: column;
}
pluto-input > .input_context_menu li button {
font-family: "Roboto Mono", system-ui;
font-size: 0.8rem;
margin-block-end: 0px;
letter-spacing: -0.02em;
color: var(--input-context-menu-li-color);
position: relative;
list-style: none;
padding: 8px;
height: 32px;
display: flex;
align-items: center;
justify-content: flex-start;
border-radius: 2px;
cursor: pointer;

background: none;
border: none;
}
.input_context_menu li:last-child {
border-bottom-left-radius: 6px;
Expand Down Expand Up @@ -1802,11 +1815,11 @@ pluto-input > button.input_context_menu {
padding: 5px;
}

pluto-input > button.input_context_menu.open {
pluto-input > .input_context_menu.open {
opacity: 1;
}

pluto-input > button.input_context_menu > span.icon::after {
pluto-input > .input_context_menu span.icon::after {
background-image: url("https://cdn.jsdelivr.net/gh/ionic-team/[email protected]/src/svg/ellipsis-horizontal-circle-outline.svg");
filter: var(--image-filters);
}
Expand Down

0 comments on commit 7566d35

Please sign in to comment.