1 | import React, { memo, forwardRef, type CSSProperties } from "react";
|
2 | import { symToStr } from "tsafe/symToStr";
|
3 | import { assert } from "tsafe/assert";
|
4 | import { createComponentI18nApi } from "../i18n";
|
5 | import type { Equals } from "tsafe";
|
6 | import { cx } from "../tools/cx";
|
7 | import { fr } from "../fr";
|
8 | import { SearchButton } from "./SearchButton";
|
9 | import { useAnalyticsId } from "../tools/useAnalyticsId";
|
10 | import "../assets/search-bar.css";
|
11 |
|
12 | export type SearchBarProps = {
|
13 | className?: string;
|
14 | id?: string;
|
15 |
|
16 | label?: string;
|
17 |
|
18 | big?: boolean;
|
19 | classes?: Partial<Record<"root" | "label", string>>;
|
20 | style?: CSSProperties;
|
21 | renderInput?: (
|
22 | |
23 |
|
24 |
|
25 |
|
26 | params: {
|
27 | id: string;
|
28 | type: "search";
|
29 | className: string;
|
30 | placeholder: string;
|
31 | }
|
32 | ) => JSX.Element;
|
33 |
|
34 | clearInputOnSearch?: boolean;
|
35 |
|
36 | allowEmptySearch?: boolean;
|
37 | onButtonClick?: (text: string) => void;
|
38 | };
|
39 |
|
40 |
|
41 |
|
42 |
|
43 | export const SearchBar = memo(
|
44 | forwardRef<HTMLDivElement, SearchBarProps>((props, ref) => {
|
45 | const {
|
46 | className,
|
47 | id: id_props,
|
48 | label: label_props,
|
49 | big = false,
|
50 | classes = {},
|
51 | style,
|
52 | renderInput = ({ className, id, placeholder, type }) => (
|
53 | <input className={className} id={id} placeholder={placeholder} type={type} />
|
54 | ),
|
55 | clearInputOnSearch = false,
|
56 | allowEmptySearch = false,
|
57 | onButtonClick,
|
58 | ...rest
|
59 | } = props;
|
60 |
|
61 | assert<Equals<keyof typeof rest, never>>();
|
62 |
|
63 | const { t } = useTranslation();
|
64 |
|
65 | const label = label_props ?? t("label");
|
66 |
|
67 | const id = useAnalyticsId({
|
68 | "defaultIdPrefix": "fr-search-bar",
|
69 | "explicitlyProvidedId": id_props
|
70 | });
|
71 |
|
72 | const inputId = `search-${id}-input`;
|
73 |
|
74 | return (
|
75 | <div
|
76 | id={id}
|
77 | className={cx(
|
78 | fr.cx("fr-search-bar", big && "fr-search-bar--lg"),
|
79 | classes.root,
|
80 | className
|
81 | )}
|
82 | role="search"
|
83 | ref={ref}
|
84 | style={style}
|
85 | {...rest}
|
86 | >
|
87 | <label className={cx(fr.cx("fr-label"), classes.label)} htmlFor={inputId}>
|
88 | {label}
|
89 | </label>
|
90 | { |
91 | }
|
92 | {renderInput({
|
93 | "className": fr.cx("fr-input"),
|
94 | "placeholder": label,
|
95 | "type": "search",
|
96 | "id": inputId
|
97 | })}
|
98 | <SearchButton
|
99 | clearInputOnSearch={clearInputOnSearch}
|
100 | id={`${id}-button`}
|
101 | searchInputId={inputId}
|
102 | allowEmptySearch={allowEmptySearch}
|
103 | onClick={onButtonClick}
|
104 | />
|
105 | </div>
|
106 | );
|
107 | })
|
108 | );
|
109 |
|
110 | SearchBar.displayName = symToStr({ SearchBar });
|
111 |
|
112 | export default SearchBar;
|
113 |
|
114 | export const { useTranslation, addSearchBarTranslations } = createComponentI18nApi({
|
115 | "componentName": symToStr({ SearchBar }),
|
116 | "frMessages": {
|
117 |
|
118 | "label": "Rechercher"
|
119 |
|
120 | }
|
121 | });
|
122 |
|
123 | addSearchBarTranslations({
|
124 | "lang": "en",
|
125 | "messages": {
|
126 | "label": "Search"
|
127 | }
|
128 | });
|
129 |
|
\ | No newline at end of file |