mirror of
https://github.com/aljazceru/rabbit.git
synced 2025-12-17 22:14:26 +01:00
feat: add search button
This commit is contained in:
@@ -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<HTMLFormElement, SubmitEvent> = (ev) => {
|
||||
ev.preventDefault();
|
||||
|
||||
saveColumn(createSearchColumn({ query: query() }));
|
||||
request({ command: 'moveToLastColumn' }).catch((err) => console.log(err));
|
||||
popupRef?.close();
|
||||
};
|
||||
|
||||
return (
|
||||
<Popup
|
||||
ref={(r) => {
|
||||
popupRef = r;
|
||||
}}
|
||||
position="right"
|
||||
button={
|
||||
<span class="inline-block h-9 w-9 rounded-full border border-primary p-2 text-2xl font-bold text-primary">
|
||||
<MagnifyingGlass />
|
||||
</span>
|
||||
}
|
||||
onOpen={() => inputRef?.focus()}
|
||||
>
|
||||
<form
|
||||
class="flex w-72 items-center gap-1 rounded-md bg-white p-4 shadow-md"
|
||||
onSubmit={handleSearchSubmit}
|
||||
>
|
||||
<input
|
||||
ref={inputRef}
|
||||
class="h-8 w-full rounded border border-stone-300 focus:border-rose-100 focus:ring-rose-300"
|
||||
type="text"
|
||||
value={query()}
|
||||
onChange={(ev) => setQuery(ev.currentTarget.value)}
|
||||
/>
|
||||
<button class="h-8 w-8 rounded bg-primary p-1 text-white" type="submit">
|
||||
<MagnifyingGlass />
|
||||
</button>
|
||||
</form>
|
||||
</Popup>
|
||||
);
|
||||
};
|
||||
|
||||
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 = () => {
|
||||
>
|
||||
<PencilSquare />
|
||||
</button>
|
||||
{/*
|
||||
<button class="h-9 w-9 rounded-full border border-primary p-2 text-2xl font-bold text-primary">
|
||||
<MagnifyingGlass />
|
||||
</button>
|
||||
*/}
|
||||
<SearchButton />
|
||||
</div>
|
||||
<div class="grow" />
|
||||
<div class="flex flex-col items-center pb-2">
|
||||
|
||||
@@ -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<SearchColumnHeaderProps> = (props) => {
|
||||
|
||||
const toggleSettingsOpened = () => setIsSettingOpened((current) => !current);
|
||||
|
||||
const handleBlur: JSX.EventHandler<HTMLInputElement, Event> = () => {
|
||||
const updateQuery = () => {
|
||||
if (query() === props.column.query) return;
|
||||
saveColumn({ ...props.column, query: query() });
|
||||
};
|
||||
|
||||
const handleBlur: JSX.EventHandler<HTMLInputElement, Event> = () => {
|
||||
updateQuery();
|
||||
};
|
||||
|
||||
const handleChange: JSX.EventHandler<HTMLInputElement, Event> = (ev) => {
|
||||
setQuery(ev.currentTarget.value);
|
||||
};
|
||||
|
||||
const handleSubmit: JSX.EventHandler<HTMLFormElement, SubmitEvent> = (ev) => {
|
||||
ev.preventDefault();
|
||||
updateQuery();
|
||||
};
|
||||
|
||||
onMount(() => {
|
||||
setQuery(props.column.query);
|
||||
});
|
||||
@@ -48,14 +56,16 @@ const SearchColumnHeader: Component<SearchColumnHeaderProps> = (props) => {
|
||||
<MagnifyingGlass />
|
||||
</span>
|
||||
</h2>
|
||||
<input
|
||||
class="flex-1 rounded border border-stone-300 px-1 py-0 focus:border-rose-100 focus:ring-rose-300"
|
||||
type="text"
|
||||
name="query"
|
||||
value={query()}
|
||||
onChange={handleChange}
|
||||
onBlur={handleBlur}
|
||||
/>
|
||||
<form class="flex-1" onSubmit={handleSubmit}>
|
||||
<input
|
||||
class="w-full rounded border border-stone-300 px-1 py-0 focus:border-rose-100 focus:ring-rose-300"
|
||||
type="text"
|
||||
name="query"
|
||||
value={query()}
|
||||
onChange={handleChange}
|
||||
onBlur={handleBlur}
|
||||
/>
|
||||
</form>
|
||||
<button class="h-4 w-4" onClick={() => toggleSettingsOpened()}>
|
||||
<EllipsisVertical />
|
||||
</button>
|
||||
@@ -99,7 +109,6 @@ const SearchColumn: Component<SearchColumnDisplayProps> = (props) => {
|
||||
<Column
|
||||
header={
|
||||
<SearchColumnHeader
|
||||
name={props.column.name ?? '検索'}
|
||||
column={props.column}
|
||||
settings={() => <ColumnSettings column={props.column} columnIndex={props.columnIndex} />}
|
||||
onClose={() => removeColumn(props.column.id)}
|
||||
|
||||
@@ -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<PopupProps> = (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<HTMLButtonElement, MouseEvent> = () => 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 (
|
||||
<div>
|
||||
<button ref={buttonRef} onClick={handleClick}>
|
||||
{props.button}
|
||||
</button>
|
||||
<div ref={popupRef} class="absolute z-20" classList={{ hidden: !isOpen(), block: isOpen() }}>
|
||||
{props.children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Popup;
|
||||
|
||||
Reference in New Issue
Block a user