feat: Filter zurücksetzen button
All checks were successful
Deploy Nyla Frontend INT / build-and-deploy (push) Successful in 10m33s
All checks were successful
Deploy Nyla Frontend INT / build-and-deploy (push) Successful in 10m33s
This commit is contained in:
parent
654a400313
commit
3c34a068fc
6 changed files with 132 additions and 21 deletions
|
|
@ -0,0 +1,48 @@
|
||||||
|
.wrapper {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 3px 30px 3px 6px;
|
||||||
|
font-size: 12px;
|
||||||
|
border: 1px solid var(--border-color, #ccc);
|
||||||
|
border-radius: 3px;
|
||||||
|
outline: none;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: var(--color-bg, #fff);
|
||||||
|
color: var(--color-text, #334155);
|
||||||
|
}
|
||||||
|
|
||||||
|
.input:focus {
|
||||||
|
border-color: var(--primary-color, #F25843);
|
||||||
|
}
|
||||||
|
|
||||||
|
.input::placeholder {
|
||||||
|
color: var(--color-text-muted, #94a3b8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.clearBtn {
|
||||||
|
position: absolute;
|
||||||
|
right: 5px;
|
||||||
|
top: 3px;
|
||||||
|
bottom: 3px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 25px;
|
||||||
|
padding: 0;
|
||||||
|
border: none;
|
||||||
|
border-radius: 3px;
|
||||||
|
background: none;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 25px;
|
||||||
|
line-height: 1;
|
||||||
|
color: var(--color-text-secondary, #94a3b8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.clearBtn:hover {
|
||||||
|
background: none;
|
||||||
|
color: var(--color-text-secondary, #94a3b8);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
import React, { type Ref } from 'react';
|
||||||
|
import styles from './FilterSearchInput.module.css';
|
||||||
|
|
||||||
|
export interface FilterSearchInputProps {
|
||||||
|
value: string;
|
||||||
|
onChange: (value: string) => void;
|
||||||
|
placeholder?: string;
|
||||||
|
inputRef?: Ref<HTMLInputElement>;
|
||||||
|
onInputClick?: (e: React.MouseEvent<HTMLInputElement>) => void;
|
||||||
|
onFocus?: () => void;
|
||||||
|
onBlur?: () => void;
|
||||||
|
/** When set, only `inputClassName` styles the input (for floating-label toolbar search). */
|
||||||
|
variant?: 'compact' | 'inherit';
|
||||||
|
inputClassName?: string;
|
||||||
|
wrapperClassName?: string;
|
||||||
|
clearTitle?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function FilterSearchInput({
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
placeholder = 'Filter...',
|
||||||
|
inputRef,
|
||||||
|
onInputClick,
|
||||||
|
onFocus,
|
||||||
|
onBlur,
|
||||||
|
variant = 'compact',
|
||||||
|
inputClassName,
|
||||||
|
wrapperClassName,
|
||||||
|
clearTitle = 'Eingabe löschen',
|
||||||
|
}: FilterSearchInputProps) {
|
||||||
|
const inputClass = variant === 'inherit'
|
||||||
|
? inputClassName
|
||||||
|
: inputClassName
|
||||||
|
? `${styles.input} ${inputClassName}`
|
||||||
|
: styles.input;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={wrapperClassName ? `${styles.wrapper} ${wrapperClassName}` : styles.wrapper}>
|
||||||
|
<input
|
||||||
|
ref={inputRef}
|
||||||
|
type="text"
|
||||||
|
value={value}
|
||||||
|
onChange={(e) => onChange(e.target.value)}
|
||||||
|
placeholder={placeholder}
|
||||||
|
className={inputClass}
|
||||||
|
onClick={onInputClick}
|
||||||
|
onFocus={onFocus}
|
||||||
|
onBlur={onBlur}
|
||||||
|
/>
|
||||||
|
{value && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={styles.clearBtn}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
onChange('');
|
||||||
|
}}
|
||||||
|
onMouseDown={(e) => e.preventDefault()}
|
||||||
|
title={clearTitle}
|
||||||
|
tabIndex={-1}
|
||||||
|
aria-label={clearTitle}
|
||||||
|
>
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
2
src/components/FormGenerator/FilterSearchInput/index.ts
Normal file
2
src/components/FormGenerator/FilterSearchInput/index.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
export { FilterSearchInput } from './FilterSearchInput';
|
||||||
|
export type { FilterSearchInputProps } from './FilterSearchInput';
|
||||||
|
|
@ -168,7 +168,7 @@
|
||||||
.searchInput {
|
.searchInput {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
padding: 8px 12px;
|
padding: 8px 28px 8px 12px;
|
||||||
border: 1px solid var(--color-border, #E2E8F0);
|
border: 1px solid var(--color-border, #E2E8F0);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import React from 'react';
|
||||||
import type { IconType } from 'react-icons';
|
import type { IconType } from 'react-icons';
|
||||||
import { useLanguage } from '../../../providers/language/LanguageContext';
|
import { useLanguage } from '../../../providers/language/LanguageContext';
|
||||||
import styles from './FormGeneratorControls.module.css';
|
import styles from './FormGeneratorControls.module.css';
|
||||||
|
import { FilterSearchInput } from '../FilterSearchInput';
|
||||||
import { Button } from '../../UiComponents/Button';
|
import { Button } from '../../UiComponents/Button';
|
||||||
import { IoIosRefresh } from "react-icons/io";
|
import { IoIosRefresh } from "react-icons/io";
|
||||||
import { FaTrash, FaDownload } from "react-icons/fa";
|
import { FaTrash, FaDownload } from "react-icons/fa";
|
||||||
|
|
@ -189,14 +190,15 @@ export function FormGeneratorControls({
|
||||||
<div className={styles.searchContainer}>
|
<div className={styles.searchContainer}>
|
||||||
{searchable && (
|
{searchable && (
|
||||||
<div className={styles.floatingLabelInput}>
|
<div className={styles.floatingLabelInput}>
|
||||||
<input
|
<FilterSearchInput
|
||||||
type="text"
|
variant="inherit"
|
||||||
placeholder=" "
|
|
||||||
value={searchTerm}
|
value={searchTerm}
|
||||||
onChange={(e) => onSearchChange(e.target.value)}
|
onChange={onSearchChange}
|
||||||
|
placeholder=" "
|
||||||
onFocus={() => onSearchFocus(true)}
|
onFocus={() => onSearchFocus(true)}
|
||||||
onBlur={() => onSearchFocus(false)}
|
onBlur={() => onSearchFocus(false)}
|
||||||
className={`${styles.searchInput} ${searchFocused || searchTerm ? styles.focused : ''}`}
|
inputClassName={`${styles.searchInput} ${searchFocused || searchTerm ? styles.focused : ''}`}
|
||||||
|
clearTitle={t('Suche löschen')}
|
||||||
/>
|
/>
|
||||||
<label className={searchFocused || searchTerm ? styles.focusedLabel : styles.label}>
|
<label className={searchFocused || searchTerm ? styles.focusedLabel : styles.label}>
|
||||||
{t('Suchen...')}
|
{t('Suchen...')}
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,7 @@ import {
|
||||||
import { formatUnixTimestamp } from '../../../utils/time';
|
import { formatUnixTimestamp } from '../../../utils/time';
|
||||||
import { applyFrontendFormat } from '../../../utils/applyFrontendFormat';
|
import { applyFrontendFormat } from '../../../utils/applyFrontendFormat';
|
||||||
import { FormGeneratorControls } from '../FormGeneratorControls';
|
import { FormGeneratorControls } from '../FormGeneratorControls';
|
||||||
|
import { FilterSearchInput } from '../FilterSearchInput';
|
||||||
import { CopyableTruncatedValue } from '../../UiComponents/CopyableTruncatedValue';
|
import { CopyableTruncatedValue } from '../../UiComponents/CopyableTruncatedValue';
|
||||||
import {
|
import {
|
||||||
isDateTimeType,
|
isDateTimeType,
|
||||||
|
|
@ -446,22 +447,11 @@ function FilterValuesList({
|
||||||
<>
|
<>
|
||||||
{showSearch && (
|
{showSearch && (
|
||||||
<div style={{ padding: '4px 6px', borderBottom: '1px solid var(--border-color, #ddd)' }}>
|
<div style={{ padding: '4px 6px', borderBottom: '1px solid var(--border-color, #ddd)' }}>
|
||||||
<input
|
<FilterSearchInput
|
||||||
ref={searchInputRef}
|
inputRef={searchInputRef}
|
||||||
type="text"
|
|
||||||
value={searchTerm}
|
value={searchTerm}
|
||||||
onChange={(e) => { setSearchTerm(e.target.value); setDisplayCount(_FILTER_PAGE_SIZE); }}
|
onChange={(value) => { setSearchTerm(value); setDisplayCount(_FILTER_PAGE_SIZE); }}
|
||||||
placeholder="Filter..."
|
onInputClick={(e) => e.stopPropagation()}
|
||||||
style={{
|
|
||||||
width: '100%',
|
|
||||||
padding: '3px 6px',
|
|
||||||
fontSize: '12px',
|
|
||||||
border: '1px solid var(--border-color, #ccc)',
|
|
||||||
borderRadius: '3px',
|
|
||||||
outline: 'none',
|
|
||||||
boxSizing: 'border-box',
|
|
||||||
}}
|
|
||||||
onClick={(e) => e.stopPropagation()}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue