/* * Copyright (C) 2026 Fluxer Contributors * * This file is part of Fluxer. * * Fluxer is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Fluxer is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with Fluxer. If not, see . */ /** @jsxRuntime automatic */ /** @jsxImportSource hono/jsx */ import {Button} from '@fluxer/ui/src/components/Button'; import {Card} from '@fluxer/ui/src/components/Card'; import { FORM_CONTROL_INPUT_CLASS, FORM_CONTROL_SELECT_CLASS, FORM_SELECT_ICON_CLASS, } from '@fluxer/ui/src/styles/FormControls'; import {cn} from '@fluxer/ui/src/utils/ClassNames'; import type {FC} from 'hono/jsx'; export type SearchFieldType = 'text' | 'select' | 'number'; export interface SearchFieldOption { value: string; label: string; } export interface SearchField { name: string; type: SearchFieldType; label?: string; placeholder?: string; value?: string | number | undefined; options?: Array; autocomplete?: string; } export interface SearchFormProps { action: string; method?: 'get' | 'post'; fields: Array; submitLabel?: string; showClear?: boolean; clearHref?: string; clearLabel?: string; helperText?: string; layout?: 'vertical' | 'horizontal'; padding?: 'none' | 'sm' | 'md' | 'lg' | 'xl'; basePath?: string; } function withBasePath(basePath: string, path: string): string { return `${basePath}${path}`; } function getSearchInputClass(): string { return cn(FORM_CONTROL_INPUT_CLASS, 'h-10'); } function getSearchSelectClass(): string { return cn(FORM_CONTROL_SELECT_CLASS, 'h-10'); } export const SearchForm: FC = ({ action, method = 'get', fields, submitLabel = 'Search', showClear = true, clearHref, clearLabel = 'Clear', helperText, layout = 'vertical', padding = 'sm', basePath = '', }) => { const isHorizontal = layout === 'horizontal'; const actionUrl = withBasePath(basePath, action); const formClass = isHorizontal ? 'flex flex-col gap-3 sm:flex-row sm:items-center' : 'space-y-4'; const fieldGroupClass = isHorizontal ? 'flex flex-1 flex-col gap-2 sm:flex-row' : 'space-y-4'; const actionGroupClass = isHorizontal ? 'flex flex-col gap-2 sm:shrink-0 sm:flex-row' : 'flex flex-wrap gap-2'; const clearUrl = clearHref ? withBasePath(basePath, clearHref) : undefined; return (
{fields.map((field) => ( ))}
{showClear && clearUrl && ( )}
{helperText &&

{helperText}

}
); }; interface SearchFieldInputProps { field: SearchField; layout: 'vertical' | 'horizontal'; } function SearchFieldInput({field, layout}: SearchFieldInputProps) { const controlId = `search-${field.name}`; const isVertical = layout === 'vertical'; const containerClass = isVertical ? 'w-full' : 'flex-1'; const labelClass = 'mb-2 block font-medium text-neutral-700 text-sm'; if (field.type === 'select') { return (
{isVertical && field.label && ( )}
); } return (
{isVertical && field.label && ( )}
); }