+
+
{
spellCheck="false"
className="cpInput"
id="cpInput"
+ ref={inputRef}
+ onChange={changeHandler}
/>
+
+ {filteredResults.length === 0 ? (
+
+
{"(。>﹏<)"}
+
no matching commands
+
+ try searching for a different term
+
+
+ ) : null}
+ {filteredResults.map((Command, index) => {
+ return (
+
+ );
+ })}
+
diff --git a/lib/navigation/handleEvents.js b/lib/navigation/handleEvents.js
new file mode 100644
index 0000000..0f1dd34
--- /dev/null
+++ b/lib/navigation/handleEvents.js
@@ -0,0 +1,72 @@
+"use babel";
+
+function handleEnter({ event, currentIndex, availableElements }) {
+ let clickElement;
+ if (currentIndex === 0) {
+ clickElement = availableElements[currentIndex + 1];
+ } else {
+ clickElement = availableElements[currentIndex];
+ }
+ clickElement.click();
+ event.preventDefault();
+}
+
+function handleArrowKey({ event, currentIndex, availableElements }) {
+ // If the focus isn't in the container, focus on the first thing
+ if (currentIndex === -1) availableElements[0].focus();
+
+ // Move the focus up or down
+ let nextElement;
+ if (event.key === "ArrowDown") {
+ nextElement = availableElements[currentIndex + 1];
+ }
+
+ if (event.key === "ArrowUp") {
+ nextElement = availableElements[currentIndex - 1];
+ }
+
+ console.log(nextElement);
+ nextElement && nextElement.focus();
+ event.preventDefault();
+}
+
+/**
+ * Implement arrow key navigation for the given parentNode
+ * @param {object} options
+ * @param {Event} options.e Keydown event
+ * @param {DOMNode} options.parentNode The parent node to operate on. Arrow keys won't navigate outside of this node
+ * @param {String} options.selectors Selectors for elements we want to be able to key through
+ */
+export default function handleEvents({
+ event,
+ parentNode,
+ selectors = "a,button,input",
+}) {
+ if (!parentNode) return;
+
+ const key = event.key;
+ if (!["ArrowUp", "ArrowDown", "Enter"].includes(key)) {
+ return;
+ }
+
+ const activeElement = document.activeElement;
+
+ // If we're not inside the container, don't do anything
+ if (!parentNode.contains(activeElement)) return;
+
+ // Get the list of elements we're allowed to scroll through
+ const availableElements = parentNode.querySelectorAll(selectors);
+
+ // No elements are available to loop through.
+ if (!availableElements.length) return;
+
+ // Which index is currently selected
+ const currentIndex = Array.from(availableElements).findIndex(
+ (availableElement) => availableElement === activeElement
+ );
+
+ if (key === "Enter") {
+ handleEnter({ event, currentIndex, availableElements });
+ }
+ handleArrowKey({ event, currentIndex, availableElements });
+}
diff --git a/lib/navigation/hook.js b/lib/navigation/hook.js
new file mode 100644
index 0000000..a10deff
--- /dev/null
+++ b/lib/navigation/hook.js
@@ -0,0 +1,24 @@
+"use babel";
+
+import handleEvents from "./handleEvents";
+import { useRef, useEffect } from "react";
+
+/**
+ * A react hook to enable arrow key navigation on a component.
+ * @param {*} param0
+ * @returns a useRef, which can be applied to a component
+ */
+export default function useArrowKeyNavigation(props) {
+ const { selectors } = props || {};
+ const parentNode = useRef();
+
+ useEffect(() => {
+ const eventHandler = (event) => {
+ handleEvents({ event, parentNode: parentNode.current, selectors });
+ };
+ document.addEventListener("keydown", eventHandler);
+ return () => document.removeEventListener("keydown", eventHandler);
+ }, []);
+
+ return parentNode;
+}
diff --git a/styles/palettemodal.less b/styles/palettemodal.less
index c5726fd..770e58e 100644
--- a/styles/palettemodal.less
+++ b/styles/palettemodal.less
@@ -1,9 +1,94 @@
.commandpalette {
- padding: 0rem !important ;
- background-color: red;
+ padding: 5px !important ;
}
-.cpInput {
- width: 400px;
- background-color: var(--page-background) !important ;
+.commandpalettewrapper {
+ height: 255px;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+}
+
+.cpInput,
+.cpContents {
+ width: 600px;
+ background-color: var(--page-background) !important ;
+ margin: 4px 0px;
+}
+
+.contents {
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+}
+
+.cpContents {
+ overflow: hidden scroll;
+ height: 210px;
+}
+
+.option {
+ padding: 4px 13px;
+ width: 600px;
+ height: 25px;
+ color: var(--text-color);
+ margin-top: 5px;
+ cursor: pointer;
+ border: none !important;
+}
+
+.option:hover,
+.option:focus {
+ padding: 4px 13px;
+ width: 600px;
+ height: 25px;
+ background-color: var(--highlight-background);
+ color: var(--selected-text-color);
+ margin-top: 5px;
+ border: none;
+ outline: 0;
+}
+
+.flex-col {
+ display: flex;
+ flex-direction: column;
+ align-items: top;
+ justify-content: center;
+ margin-bottom: auto;
+}
+
+.flex-row {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.nomargin {
+ margin: 0 !important;
+}
+
+.topresult {
+ color: var(--disabled-text-color);
+ margin-right: 10px;
+}
+
+.nomatch {
+ height: 100%;
+ text-align: center;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+}
+
+.nomatchemoji {
+ font-size: 50px;
+ color: var(--disabled-text-color);
+ margin: 15px;
+}
+
+.nomatchtext {
+ color: var(--disabled-text-color);
+ margin: 0;
}