Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 39 additions & 5 deletions app/assets/javascript/lexxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -9087,7 +9087,7 @@ class LexicalPromptElement extends HTMLElement {
#selectOption(listItem) {
this.#clearSelection();
listItem.toggleAttribute("aria-selected", true);
listItem.scrollIntoView({ block: "nearest", behavior: "smooth" });
this.#scrollToOption(listItem);
listItem.focus();

// Preserve selection to prevent cursor jump
Expand All @@ -9100,6 +9100,32 @@ class LexicalPromptElement extends HTMLElement {
this.#editorContentElement.setAttribute("aria-haspopup", "listbox");
}

#scrollToOption(listItem) {
const menu = this.popoverElement;
const menuRect = menu.getBoundingClientRect();
const itemRect = listItem.getBoundingClientRect();

// Calculate the item's position relative to the menu
const itemTop = listItem.offsetTop;
const itemBottom = itemTop + itemRect.height;
const menuScrollTop = menu.scrollTop;
const menuHeight = menuRect.height;

// Define padding: scroll when the item is within this distance from the edge
const scrollPadding = itemRect.height * 1.5;

// Check if we need to scroll down
if (itemBottom > menuScrollTop + menuHeight - scrollPadding) {
// Scroll so the item is visible with padding below
menu.scrollTop = itemBottom - menuHeight + scrollPadding;
}
// Check if we need to scroll up
else if (itemTop < menuScrollTop + scrollPadding) {
// Scroll so the item is visible with padding above
menu.scrollTop = itemTop - scrollPadding;
}
}

#clearSelection() {
this.#listItemElements.forEach((item) => { item.toggleAttribute("aria-selected", false); });
this.#editorContentElement.removeAttribute("aria-controls");
Expand Down Expand Up @@ -9208,13 +9234,21 @@ class LexicalPromptElement extends HTMLElement {
}

#moveSelectionDown() {
const nextIndex = this.#selectedIndex + 1;
if (nextIndex < this.#listItemElements.length) this.#selectOption(this.#listItemElements[nextIndex]);
const currentIndex = this.#selectedIndex;
const items = this.#listItemElements;

// Wrap around to the first item if we're at the last item
const nextIndex = currentIndex + 1 >= items.length ? 0 : currentIndex + 1;
this.#selectOption(items[nextIndex]);
}

#moveSelectionUp() {
const previousIndex = this.#selectedIndex - 1;
if (previousIndex >= 0) this.#selectOption(this.#listItemElements[previousIndex]);
const currentIndex = this.#selectedIndex;
const items = this.#listItemElements;

// Wrap around to the last item if we're at the first item
const previousIndex = currentIndex - 1 < 0 ? items.length - 1 : currentIndex - 1;
this.#selectOption(items[previousIndex]);
}

get #selectedIndex() {
Expand Down
Binary file modified app/assets/javascript/lexxy.js.br
Binary file not shown.
Binary file modified app/assets/javascript/lexxy.js.gz
Binary file not shown.
4 changes: 2 additions & 2 deletions app/assets/javascript/lexxy.min.js

Large diffs are not rendered by default.

Binary file modified app/assets/javascript/lexxy.min.js.br
Binary file not shown.
Binary file modified app/assets/javascript/lexxy.min.js.gz
Binary file not shown.
25 changes: 25 additions & 0 deletions app/assets/stylesheets/lexxy-editor.css
Original file line number Diff line number Diff line change
Expand Up @@ -279,8 +279,33 @@
min-inline-size: var(--lexxy-prompt-min-width);
overflow: auto;
padding: var(--lexxy-prompt-padding);
padding-inline-end: 0;
scrollbar-color: var(--lexxy-color-ink-lighter) transparent;
scrollbar-width: thin;
visibility: hidden;
z-index: var(--lexxy-z-popup);

@media (prefers-color-scheme: dark) {
scrollbar-color: oklch(40% 0 0) transparent;
}

/* Webkit browsers */
&::-webkit-scrollbar {
inline-size: 8px;
}

&::-webkit-scrollbar-track {
background: transparent;
}

&::-webkit-scrollbar-thumb {
background-color: var(--lexxy-color-ink-lighter);
border-radius: 4px;

@media (prefers-color-scheme: dark) {
background-color: oklch(40% 0 0);
}
}
}

:where(.lexxy-prompt-menu--visible) {
Expand Down
44 changes: 39 additions & 5 deletions src/elements/prompt.js
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ export default class LexicalPromptElement extends HTMLElement {
#selectOption(listItem) {
this.#clearSelection()
listItem.toggleAttribute("aria-selected", true)
listItem.scrollIntoView({ block: "nearest", behavior: "smooth" })
this.#scrollToOption(listItem)
listItem.focus()

// Preserve selection to prevent cursor jump
Expand All @@ -203,6 +203,32 @@ export default class LexicalPromptElement extends HTMLElement {
this.#editorContentElement.setAttribute("aria-haspopup", "listbox")
}

#scrollToOption(listItem) {
const menu = this.popoverElement
const menuRect = menu.getBoundingClientRect()
const itemRect = listItem.getBoundingClientRect()

// Calculate the item's position relative to the menu
const itemTop = listItem.offsetTop
const itemBottom = itemTop + itemRect.height
const menuScrollTop = menu.scrollTop
const menuHeight = menuRect.height

// Define padding: scroll when the item is within this distance from the edge
const scrollPadding = itemRect.height * 1.5

// Check if we need to scroll down
if (itemBottom > menuScrollTop + menuHeight - scrollPadding) {
// Scroll so the item is visible with padding below
menu.scrollTop = itemBottom - menuHeight + scrollPadding
}
// Check if we need to scroll up
else if (itemTop < menuScrollTop + scrollPadding) {
// Scroll so the item is visible with padding above
menu.scrollTop = itemTop - scrollPadding
}
}

#clearSelection() {
this.#listItemElements.forEach((item) => { item.toggleAttribute("aria-selected", false) })
this.#editorContentElement.removeAttribute("aria-controls")
Expand Down Expand Up @@ -311,13 +337,21 @@ export default class LexicalPromptElement extends HTMLElement {
}

#moveSelectionDown() {
const nextIndex = this.#selectedIndex + 1
if (nextIndex < this.#listItemElements.length) this.#selectOption(this.#listItemElements[nextIndex])
const currentIndex = this.#selectedIndex
const items = this.#listItemElements

// Wrap around to the first item if we're at the last item
const nextIndex = currentIndex + 1 >= items.length ? 0 : currentIndex + 1
this.#selectOption(items[nextIndex])
}

#moveSelectionUp() {
const previousIndex = this.#selectedIndex - 1
if (previousIndex >= 0) this.#selectOption(this.#listItemElements[previousIndex])
const currentIndex = this.#selectedIndex
const items = this.#listItemElements

// Wrap around to the last item if we're at the first item
const previousIndex = currentIndex - 1 < 0 ? items.length - 1 : currentIndex - 1
this.#selectOption(items[previousIndex])
}

get #selectedIndex() {
Expand Down