diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx index 3bec9f7..1b46283 100644 --- a/src/components/SideBar.tsx +++ b/src/components/SideBar.tsx @@ -7,16 +7,68 @@ import PencilSquare from 'heroicons/24/solid/pencil-square.svg'; import Config from '@/components/modal/Config'; import NotePostForm from '@/components/NotePostForm'; +import Popup, { PopupRef } from '@/components/utils/Popup'; +import { createSearchColumn } from '@/core/column'; import useConfig from '@/core/useConfig'; -import { useHandleCommand } from '@/hooks/useCommandBus'; +import { useHandleCommand, useRequestCommand } from '@/hooks/useCommandBus'; import useModalState from '@/hooks/useModalState'; import resolveAsset from '@/utils/resolveAsset'; +const SearchButton = () => { + let popupRef: PopupRef | undefined; + let inputRef: HTMLInputElement | undefined; + + const { saveColumn } = useConfig(); + const request = useRequestCommand(); + + const [query, setQuery] = createSignal(''); + + const handleSearchSubmit: JSX.EventHandler = (ev) => { + ev.preventDefault(); + + saveColumn(createSearchColumn({ query: query() })); + request({ command: 'moveToLastColumn' }).catch((err) => console.log(err)); + popupRef?.close(); + }; + + return ( + { + popupRef = r; + }} + position="right" + button={ + + + + } + onOpen={() => inputRef?.focus()} + > +
+ setQuery(ev.currentTarget.value)} + /> + +
+
+ ); +}; + const SideBar: Component = () => { let textAreaRef: HTMLTextAreaElement | undefined; const { showAddColumn, showAbout } = useModalState(); const { config } = useConfig(); + const [formOpened, setFormOpened] = createSignal(false); const [configOpened, setConfigOpened] = createSignal(false); @@ -48,11 +100,7 @@ const SideBar: Component = () => { > - {/* - - */} +
diff --git a/src/components/column/SearchColumn.tsx b/src/components/column/SearchColumn.tsx index 72dbf30..2a735cd 100644 --- a/src/components/column/SearchColumn.tsx +++ b/src/components/column/SearchColumn.tsx @@ -13,7 +13,6 @@ import useConfig from '@/core/useConfig'; import useSubscription from '@/nostr/useSubscription'; export type SearchColumnHeaderProps = { - name: string; column: SearchColumnType; settings: () => JSX.Element; onClose?: () => void; @@ -27,15 +26,24 @@ const SearchColumnHeader: Component = (props) => { const toggleSettingsOpened = () => setIsSettingOpened((current) => !current); - const handleBlur: JSX.EventHandler = () => { + const updateQuery = () => { if (query() === props.column.query) return; saveColumn({ ...props.column, query: query() }); }; + const handleBlur: JSX.EventHandler = () => { + updateQuery(); + }; + const handleChange: JSX.EventHandler = (ev) => { setQuery(ev.currentTarget.value); }; + const handleSubmit: JSX.EventHandler = (ev) => { + ev.preventDefault(); + updateQuery(); + }; + onMount(() => { setQuery(props.column.query); }); @@ -48,14 +56,16 @@ const SearchColumnHeader: Component = (props) => { - +
+ +
@@ -99,7 +109,6 @@ const SearchColumn: Component = (props) => { } onClose={() => removeColumn(props.column.id)} diff --git a/src/components/utils/Popup.tsx b/src/components/utils/Popup.tsx index 1975534..c085ea7 100644 --- a/src/components/utils/Popup.tsx +++ b/src/components/utils/Popup.tsx @@ -1,20 +1,90 @@ -import { createSignal, createEffect, type Component, type JSX } from 'solid-js'; +import { createSignal, createEffect, type Component, type JSX, onCleanup, onMount } from 'solid-js'; + +export type PopupRef = { + close: () => void; +}; export type PopupProps = { - baseElemRef: HTMLElement | null; children: JSX.Element; + button: JSX.Element; + position?: 'left' | 'bottom' | 'right' | 'top'; + onOpen?: () => void; + ref?: (ref: PopupRef) => void; }; const Popup: Component = (props) => { + let buttonRef: HTMLButtonElement | undefined; let popupRef: HTMLDivElement | undefined; - const [style, setStyle] = createSignal({}); + const [isOpen, setIsOpen] = createSignal(false); + + const handleClickOutside = (ev: MouseEvent) => { + const target = ev.target as HTMLElement; + if (target != null && !popupRef?.contains(target)) { + setIsOpen(false); + } + }; + + const addClickOutsideHandler = () => { + document.addEventListener('mousedown', handleClickOutside); + }; + const removeClickOutsideHandler = () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + + const close = () => setIsOpen(false); + const toggle = () => setIsOpen((current) => !current); + + const handleClick: JSX.EventHandler = () => toggle(); createEffect(() => { - if (props.baseElemRef == null || popupRef == null) return; - - const baseElemRect = props.baseElemRef; + if (isOpen()) { + addClickOutsideHandler(); + } else { + removeClickOutsideHandler(); + } }); - return null; + createEffect(() => { + if (isOpen()) props.onOpen?.(); + }); + + createEffect(() => { + if (buttonRef == null || popupRef == null) return; + + const buttonRect = buttonRef?.getBoundingClientRect(); + + if (props.position === 'left') { + popupRef.style.left = `${buttonRect.left - buttonRect.width}px`; + popupRef.style.top = `${buttonRect.top}px`; + } else if (props.position === 'right') { + popupRef.style.left = `${buttonRect.left + buttonRect.width}px`; + popupRef.style.top = `${buttonRect.top}px`; + } else if (props.position === 'top') { + popupRef.style.left = `${buttonRect.left + buttonRect.width}px`; + popupRef.style.top = `${buttonRect.top - buttonRect.height}px`; + } else { + popupRef.style.left = `${buttonRect.left + buttonRect.width / 2}px`; + popupRef.style.top = `${buttonRect.top + buttonRect.height}px`; + } + }); + + onMount(() => { + props.ref?.({ close }); + }); + + onCleanup(() => removeClickOutsideHandler()); + + return ( +
+ +
+ {props.children} +
+
+ ); }; + +export default Popup;