@classytic/fluid 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/layout.js CHANGED
@@ -1,62 +1,4 @@
1
- import { clsx } from 'clsx';
2
- import { twMerge } from 'tailwind-merge';
3
- import { jsx } from 'react/jsx-runtime';
4
-
5
- // src/utils.ts
6
- function cn(...inputs) {
7
- return twMerge(clsx(inputs));
8
- }
9
- var backgrounds = {
10
- default: "bg-background",
11
- muted: "bg-muted",
12
- primary: "bg-primary text-primary-foreground",
13
- transparent: "bg-transparent"
14
- };
15
- var paddings = {
16
- none: "",
17
- sm: "py-8 md:py-12",
18
- md: "py-12 md:py-16",
19
- lg: "py-16 md:py-24",
20
- xl: "py-24 md:py-32"
21
- };
22
- function Section({
23
- id,
24
- children,
25
- className,
26
- background = "default",
27
- padding = "sm"
28
- }) {
29
- return /* @__PURE__ */ jsx(
30
- "section",
31
- {
32
- id,
33
- className: cn(backgrounds[background], paddings[padding], className),
34
- children
35
- }
36
- );
37
- }
38
- var maxWidthClasses = {
39
- sm: "max-w-screen-sm",
40
- md: "max-w-screen-md",
41
- lg: "max-w-screen-lg",
42
- xl: "max-w-screen-xl",
43
- "2xl": "max-w-screen-2xl",
44
- "4xl": "max-w-4xl",
45
- "5xl": "max-w-5xl",
46
- "6xl": "max-w-6xl",
47
- "7xl": "max-w-7xl",
48
- full: "max-w-full"
49
- };
50
- function Container({
51
- children,
52
- className,
53
- maxWidth = "7xl"
54
- }) {
55
- const isFullWidth = maxWidth === "full";
56
- const baseClass = isFullWidth ? "w-full px-4 sm:px-6 lg:px-10" : "mx-auto px-4 sm:px-6 lg:px-8";
57
- return /* @__PURE__ */ jsx("div", { className: cn(baseClass, !isFullWidth && maxWidthClasses[maxWidth], className), children });
58
- }
59
-
60
- export { Container, Section, cn };
1
+ export { Container, Section } from './chunk-H3NFL3GJ.js';
2
+ export { cn } from './chunk-GUHK2DTW.js';
61
3
  //# sourceMappingURL=layout.js.map
62
4
  //# sourceMappingURL=layout.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils.ts","../src/layout/section.tsx","../src/layout/container.tsx"],"names":["jsx"],"mappings":";;;;;AAGO,SAAS,MAAM,MAAA,EAAsB;AAC1C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC7B;ACSA,IAAM,WAAA,GAA0C;AAAA,EAC9C,OAAA,EAAS,eAAA;AAAA,EACT,KAAA,EAAO,UAAA;AAAA,EACP,OAAA,EAAS,oCAAA;AAAA,EACT,WAAA,EAAa;AACf,CAAA;AAEA,IAAM,QAAA,GAAoC;AAAA,EACxC,IAAA,EAAM,EAAA;AAAA,EACN,EAAA,EAAI,eAAA;AAAA,EACJ,EAAA,EAAI,gBAAA;AAAA,EACJ,EAAA,EAAI,gBAAA;AAAA,EACJ,EAAA,EAAI;AACN,CAAA;AAEO,SAAS,OAAA,CAAQ;AAAA,EACtB,EAAA;AAAA,EACA,QAAA;AAAA,EACA,SAAA;AAAA,EACA,UAAA,GAAa,SAAA;AAAA,EACb,OAAA,GAAU;AACZ,CAAA,EAAiB;AACf,EAAA,uBACE,GAAA;AAAA,IAAC,SAAA;AAAA,IAAA;AAAA,MACC,EAAA;AAAA,MACA,SAAA,EAAW,GAAG,WAAA,CAAY,UAAU,GAAG,QAAA,CAAS,OAAO,GAAG,SAAS,CAAA;AAAA,MAElE;AAAA;AAAA,GACH;AAEJ;ACjCA,IAAM,eAAA,GAA4C;AAAA,EAChD,EAAA,EAAI,iBAAA;AAAA,EACJ,EAAA,EAAI,iBAAA;AAAA,EACJ,EAAA,EAAI,iBAAA;AAAA,EACJ,EAAA,EAAI,iBAAA;AAAA,EACJ,KAAA,EAAO,kBAAA;AAAA,EACP,KAAA,EAAO,WAAA;AAAA,EACP,KAAA,EAAO,WAAA;AAAA,EACP,KAAA,EAAO,WAAA;AAAA,EACP,KAAA,EAAO,WAAA;AAAA,EACP,IAAA,EAAM;AACR,CAAA;AAEO,SAAS,SAAA,CAAU;AAAA,EACxB,QAAA;AAAA,EACA,SAAA;AAAA,EACA,QAAA,GAAW;AACb,CAAA,EAAmB;AACjB,EAAA,MAAM,cAAc,QAAA,KAAa,MAAA;AACjC,EAAA,MAAM,SAAA,GAAY,cACd,8BAAA,GACA,8BAAA;AAEJ,EAAA,uBACEA,GAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,EAAA,CAAG,SAAA,EAAW,CAAC,WAAA,IAAe,eAAA,CAAgB,QAAQ,CAAA,EAAG,SAAS,GAC/E,QAAA,EACH,CAAA;AAEJ","file":"layout.js","sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\r\nimport { twMerge } from \"tailwind-merge\";\r\n\r\nexport function cn(...inputs: ClassValue[]) {\r\n return twMerge(clsx(inputs));\r\n}\r\n","import { cn } from \"../utils\";\r\nimport type { ReactNode } from \"react\";\r\n\r\ntype Background = \"default\" | \"muted\" | \"primary\" | \"transparent\";\r\ntype Padding = \"none\" | \"sm\" | \"md\" | \"lg\" | \"xl\";\r\n\r\nexport interface SectionProps {\r\n id?: string;\r\n children: ReactNode;\r\n className?: string;\r\n background?: Background;\r\n padding?: Padding;\r\n}\r\n\r\nconst backgrounds: Record<Background, string> = {\r\n default: \"bg-background\",\r\n muted: \"bg-muted\",\r\n primary: \"bg-primary text-primary-foreground\",\r\n transparent: \"bg-transparent\",\r\n};\r\n\r\nconst paddings: Record<Padding, string> = {\r\n none: \"\",\r\n sm: \"py-8 md:py-12\",\r\n md: \"py-12 md:py-16\",\r\n lg: \"py-16 md:py-24\",\r\n xl: \"py-24 md:py-32\",\r\n};\r\n\r\nexport function Section({\r\n id,\r\n children,\r\n className,\r\n background = \"default\",\r\n padding = \"sm\",\r\n}: SectionProps) {\r\n return (\r\n <section\r\n id={id}\r\n className={cn(backgrounds[background], paddings[padding], className)}\r\n >\r\n {children}\r\n </section>\r\n );\r\n}\r\n","import { cn } from \"../utils\";\r\nimport type { ReactNode } from \"react\";\r\n\r\ntype MaxWidth = \"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\" | \"4xl\" | \"5xl\" | \"6xl\" | \"7xl\" | \"full\";\r\n\r\nexport interface ContainerProps {\r\n children: ReactNode;\r\n className?: string;\r\n maxWidth?: MaxWidth;\r\n}\r\n\r\nconst maxWidthClasses: Record<MaxWidth, string> = {\r\n sm: \"max-w-screen-sm\",\r\n md: \"max-w-screen-md\",\r\n lg: \"max-w-screen-lg\",\r\n xl: \"max-w-screen-xl\",\r\n \"2xl\": \"max-w-screen-2xl\",\r\n \"4xl\": \"max-w-4xl\",\r\n \"5xl\": \"max-w-5xl\",\r\n \"6xl\": \"max-w-6xl\",\r\n \"7xl\": \"max-w-7xl\",\r\n full: \"max-w-full\",\r\n};\r\n\r\nexport function Container({\r\n children,\r\n className,\r\n maxWidth = \"7xl\",\r\n}: ContainerProps) {\r\n const isFullWidth = maxWidth === \"full\";\r\n const baseClass = isFullWidth\r\n ? \"w-full px-4 sm:px-6 lg:px-10\"\r\n : \"mx-auto px-4 sm:px-6 lg:px-8\";\r\n\r\n return (\r\n <div className={cn(baseClass, !isFullWidth && maxWidthClasses[maxWidth], className)}>\r\n {children}\r\n </div>\r\n );\r\n}\r\n"]}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"layout.js"}
@@ -0,0 +1,172 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode, KeyboardEvent } from 'react';
3
+ import { U as UseBaseSearchReturn } from './use-base-search-AS5Z3SAy.js';
4
+
5
+ interface SearchRootProps {
6
+ children: ReactNode;
7
+ hook: UseBaseSearchReturn;
8
+ className?: string;
9
+ }
10
+ /**
11
+ * Root search component that provides context to all child components
12
+ *
13
+ * @example
14
+ * <Search.Root hook={useMySearch()}>
15
+ * <Search.Input />
16
+ * <Search.Filters />
17
+ * <Search.Actions />
18
+ * </Search.Root>
19
+ */
20
+ declare function SearchRoot({ children, hook, className }: SearchRootProps): react_jsx_runtime.JSX.Element;
21
+
22
+ interface SearchInputProps {
23
+ placeholder?: string;
24
+ className?: string;
25
+ disabled?: boolean;
26
+ showIcon?: boolean;
27
+ showClearButton?: boolean;
28
+ onKeyDown?: (event: KeyboardEvent<HTMLInputElement>) => void;
29
+ }
30
+ /**
31
+ * Search input component using modern shadcn InputGroup pattern
32
+ *
33
+ * @example
34
+ * <Search.Input placeholder="Search..." />
35
+ */
36
+ declare function SearchInput({ placeholder, className, disabled, showIcon, showClearButton, onKeyDown, ...props }: SearchInputProps): react_jsx_runtime.JSX.Element;
37
+
38
+ interface SearchTypeOption {
39
+ value: string;
40
+ label: string;
41
+ }
42
+ interface SearchTypeInputProps {
43
+ placeholder?: string;
44
+ className?: string;
45
+ disabled?: boolean;
46
+ showIcon?: boolean;
47
+ showClearButton?: boolean;
48
+ searchTypeOptions?: SearchTypeOption[];
49
+ onKeyDown?: (event: KeyboardEvent<HTMLInputElement>) => void;
50
+ }
51
+ /**
52
+ * Search input component with type selector using InputGroup pattern
53
+ *
54
+ * @example
55
+ * <Search.TypeInput
56
+ * placeholder="Search..."
57
+ * searchTypeOptions={[
58
+ * { value: "_id", label: "ID" },
59
+ * { value: "customerPhone", label: "Phone" },
60
+ * { value: "customerEmail", label: "Email" },
61
+ * ]}
62
+ * />
63
+ */
64
+ declare function SearchTypeInput({ placeholder, className, disabled, showIcon, showClearButton, searchTypeOptions, onKeyDown, ...props }: SearchTypeInputProps): react_jsx_runtime.JSX.Element;
65
+
66
+ interface SearchFiltersProps {
67
+ children: ReactNode;
68
+ title?: string;
69
+ description?: string;
70
+ disabled?: boolean;
71
+ className?: string;
72
+ }
73
+ /**
74
+ * Search filters component with mobile sheet support
75
+ *
76
+ * @example
77
+ * <Search.Filters>
78
+ * <TagChoiceInput label="Category" ... />
79
+ * <SelectInput label="Status" ... />
80
+ * </Search.Filters>
81
+ */
82
+ declare function SearchFilters({ children, title, description, disabled, className, }: SearchFiltersProps): react_jsx_runtime.JSX.Element;
83
+
84
+ interface SearchActionsProps {
85
+ showSearchButton?: boolean;
86
+ showClearButton?: boolean;
87
+ searchButtonText?: string;
88
+ clearButtonText?: string;
89
+ disabled?: boolean;
90
+ className?: string;
91
+ }
92
+ /**
93
+ * Search action buttons
94
+ */
95
+ declare function SearchActions({ showSearchButton, showClearButton, searchButtonText, clearButtonText, disabled, className, }: SearchActionsProps): react_jsx_runtime.JSX.Element;
96
+
97
+ interface SearchContainerProps {
98
+ children: ReactNode;
99
+ className?: string;
100
+ }
101
+ /**
102
+ * Container for search input and action buttons
103
+ * Provides responsive layout
104
+ *
105
+ * @example
106
+ * <Search.Container>
107
+ * <Search.Input />
108
+ * <Search.Filters />
109
+ * <Search.Actions />
110
+ * </Search.Container>
111
+ */
112
+ declare function SearchContainer({ children, className }: SearchContainerProps): react_jsx_runtime.JSX.Element;
113
+
114
+ interface SearchFilterActionsProps {
115
+ onClose?: () => void;
116
+ disabled?: boolean;
117
+ }
118
+ /**
119
+ * Filter actions component (Reset/Apply buttons)
120
+ * Used inside filter popovers/sheets
121
+ */
122
+ declare function SearchFilterActions({ onClose, disabled }: SearchFilterActionsProps): react_jsx_runtime.JSX.Element;
123
+
124
+ interface SearchProviderProps {
125
+ children: ReactNode;
126
+ value: UseBaseSearchReturn;
127
+ }
128
+ declare function SearchProvider({ children, value }: SearchProviderProps): react_jsx_runtime.JSX.Element;
129
+ declare function useSearch(): UseBaseSearchReturn;
130
+
131
+ /**
132
+ * Composable Search Component System
133
+ *
134
+ * @example
135
+ * ```tsx
136
+ * import { Search } from "@classytic/fluid";
137
+ *
138
+ * function MySearch() {
139
+ * const searchHook = useMySearch();
140
+ *
141
+ * return (
142
+ * <Search.Root hook={searchHook}>
143
+ * <Search.Container>
144
+ * <Search.Input placeholder="Search..." />
145
+ * <Search.Filters>
146
+ * <SelectInput ... />
147
+ * <TagChoiceInput ... />
148
+ * </Search.Filters>
149
+ * <Search.Actions />
150
+ * </Search.Container>
151
+ * </Search.Root>
152
+ * );
153
+ * }
154
+ * ```
155
+ */
156
+
157
+ type index_SearchActionsProps = SearchActionsProps;
158
+ type index_SearchContainerProps = SearchContainerProps;
159
+ type index_SearchFilterActionsProps = SearchFilterActionsProps;
160
+ type index_SearchFiltersProps = SearchFiltersProps;
161
+ type index_SearchInputProps = SearchInputProps;
162
+ declare const index_SearchProvider: typeof SearchProvider;
163
+ type index_SearchProviderProps = SearchProviderProps;
164
+ type index_SearchRootProps = SearchRootProps;
165
+ type index_SearchTypeInputProps = SearchTypeInputProps;
166
+ type index_SearchTypeOption = SearchTypeOption;
167
+ declare const index_useSearch: typeof useSearch;
168
+ declare namespace index {
169
+ export { SearchActions as Actions, SearchContainer as Container, SearchFilterActions as FilterActions, SearchFilters as Filters, SearchInput as Input, SearchRoot as Root, type index_SearchActionsProps as SearchActionsProps, type index_SearchContainerProps as SearchContainerProps, type index_SearchFilterActionsProps as SearchFilterActionsProps, type index_SearchFiltersProps as SearchFiltersProps, type index_SearchInputProps as SearchInputProps, index_SearchProvider as SearchProvider, type index_SearchProviderProps as SearchProviderProps, type index_SearchRootProps as SearchRootProps, type index_SearchTypeInputProps as SearchTypeInputProps, type index_SearchTypeOption as SearchTypeOption, SearchTypeInput as TypeInput, index_useSearch as useSearch };
170
+ }
171
+
172
+ export { index as Search, type SearchActionsProps, type SearchContainerProps, type SearchFilterActionsProps, type SearchFiltersProps, type SearchInputProps, SearchProvider, type SearchProviderProps, type SearchRootProps, type SearchTypeInputProps, type SearchTypeOption, useSearch };
package/dist/search.js ADDED
@@ -0,0 +1,341 @@
1
+ import { useIsMobile, SheetWrapper } from './chunk-J2YRTQE4.js';
2
+ import { __export, cn } from './chunk-GUHK2DTW.js';
3
+ import { createContext, useContext, useState } from 'react';
4
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
+ import { Search, X, Filter, RotateCcw } from 'lucide-react';
6
+ import { InputGroup, InputGroupAddon, InputGroupInput, InputGroupButton } from '@/components/ui/input-group';
7
+ import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from '@/components/ui/select';
8
+ import { Button } from '@/components/ui/button';
9
+ import { Badge } from '@/components/ui/badge';
10
+ import { Popover, PopoverTrigger, PopoverContent } from '@/components/ui/popover';
11
+
12
+ // src/components/search/index.ts
13
+ var search_exports = {};
14
+ __export(search_exports, {
15
+ Actions: () => SearchActions,
16
+ Container: () => SearchContainer,
17
+ FilterActions: () => SearchFilterActions,
18
+ Filters: () => SearchFilters,
19
+ Input: () => SearchInput,
20
+ Root: () => SearchRoot,
21
+ SearchProvider: () => SearchProvider,
22
+ TypeInput: () => SearchTypeInput,
23
+ useSearch: () => useSearch
24
+ });
25
+ var SearchContext = createContext(void 0);
26
+ function SearchProvider({ children, value }) {
27
+ return /* @__PURE__ */ jsx(SearchContext.Provider, { value, children });
28
+ }
29
+ function useSearch() {
30
+ const context = useContext(SearchContext);
31
+ if (!context) {
32
+ throw new Error("useSearch must be used within SearchProvider");
33
+ }
34
+ return context;
35
+ }
36
+ function SearchRoot({ children, hook, className }) {
37
+ return /* @__PURE__ */ jsx(SearchProvider, { value: hook, children: /* @__PURE__ */ jsx("div", { className: cn("w-full", className), children }) });
38
+ }
39
+ function SearchInput({
40
+ placeholder = "Search...",
41
+ className,
42
+ disabled,
43
+ showIcon = true,
44
+ showClearButton = false,
45
+ onKeyDown,
46
+ ...props
47
+ }) {
48
+ const { searchValue, setSearchValue, handleSearch } = useSearch();
49
+ const handleKeyDown = (event) => {
50
+ onKeyDown?.(event);
51
+ if (!event.defaultPrevented && event.key === "Enter" && !disabled) {
52
+ handleSearch?.();
53
+ }
54
+ };
55
+ return /* @__PURE__ */ jsxs(
56
+ InputGroup,
57
+ {
58
+ className: cn("h-10 flex-1 shadow-sm", className),
59
+ "data-disabled": disabled || void 0,
60
+ children: [
61
+ showIcon && /* @__PURE__ */ jsx(InputGroupAddon, { align: "inline-start", children: /* @__PURE__ */ jsx(Search, { className: "size-4" }) }),
62
+ /* @__PURE__ */ jsx(
63
+ InputGroupInput,
64
+ {
65
+ ...props,
66
+ type: "text",
67
+ placeholder,
68
+ value: searchValue || "",
69
+ onChange: (e) => setSearchValue?.(e.target.value),
70
+ onKeyDown: handleKeyDown,
71
+ disabled,
72
+ className: "text-base"
73
+ }
74
+ ),
75
+ showClearButton && searchValue && /* @__PURE__ */ jsx(InputGroupAddon, { align: "inline-end", children: /* @__PURE__ */ jsx(
76
+ InputGroupButton,
77
+ {
78
+ variant: "ghost",
79
+ size: "icon-sm",
80
+ onClick: () => setSearchValue?.(""),
81
+ disabled,
82
+ children: /* @__PURE__ */ jsx(X, { className: "size-4" })
83
+ }
84
+ ) })
85
+ ]
86
+ }
87
+ );
88
+ }
89
+ function SearchTypeInput({
90
+ placeholder = "Search...",
91
+ className,
92
+ disabled,
93
+ showIcon = true,
94
+ showClearButton = false,
95
+ searchTypeOptions = [],
96
+ onKeyDown,
97
+ ...props
98
+ }) {
99
+ const { searchValue, setSearchValue, searchType, setSearchType, handleSearch } = useSearch();
100
+ const handleKeyDown = (event) => {
101
+ onKeyDown?.(event);
102
+ if (!event.defaultPrevented && event.key === "Enter" && !disabled) {
103
+ handleSearch?.();
104
+ }
105
+ };
106
+ const selectedOption = searchTypeOptions.find((opt) => opt.value === searchType);
107
+ return /* @__PURE__ */ jsxs(
108
+ InputGroup,
109
+ {
110
+ className: cn("h-10 flex-1 shadow-sm", className),
111
+ "data-disabled": disabled || void 0,
112
+ children: [
113
+ showIcon && /* @__PURE__ */ jsx(InputGroupAddon, { align: "inline-start", children: /* @__PURE__ */ jsx(Search, { className: "size-4" }) }),
114
+ searchTypeOptions.length > 0 && /* @__PURE__ */ jsxs(InputGroupAddon, { align: "inline-start", className: "pr-0", children: [
115
+ /* @__PURE__ */ jsxs(
116
+ Select,
117
+ {
118
+ value: searchType,
119
+ onValueChange: setSearchType,
120
+ disabled,
121
+ children: [
122
+ /* @__PURE__ */ jsx(SelectTrigger, { className: "h-7 w-auto border-0 bg-transparent px-2 text-sm font-medium shadow-none focus:ring-0", children: /* @__PURE__ */ jsx(SelectValue, { children: selectedOption?.label || searchTypeOptions[0]?.label }) }),
123
+ /* @__PURE__ */ jsx(SelectContent, { children: searchTypeOptions.map((option) => /* @__PURE__ */ jsx(SelectItem, { value: option.value, children: option.label }, option.value)) })
124
+ ]
125
+ }
126
+ ),
127
+ /* @__PURE__ */ jsx("div", { className: "h-6 w-px bg-border ml-1" })
128
+ ] }),
129
+ /* @__PURE__ */ jsx(
130
+ InputGroupInput,
131
+ {
132
+ ...props,
133
+ type: "text",
134
+ placeholder,
135
+ value: searchValue || "",
136
+ onChange: (e) => setSearchValue?.(e.target.value),
137
+ onKeyDown: handleKeyDown,
138
+ disabled,
139
+ className: "text-base"
140
+ }
141
+ ),
142
+ showClearButton && searchValue && /* @__PURE__ */ jsx(InputGroupAddon, { align: "inline-end", children: /* @__PURE__ */ jsx(
143
+ InputGroupButton,
144
+ {
145
+ variant: "ghost",
146
+ size: "icon-sm",
147
+ onClick: () => setSearchValue?.(""),
148
+ disabled,
149
+ children: /* @__PURE__ */ jsx(X, { className: "size-4" })
150
+ }
151
+ ) })
152
+ ]
153
+ }
154
+ );
155
+ }
156
+ function SearchFilterActions({ onClose, disabled }) {
157
+ const { handleSearch, clearSearch, hasActiveFilters } = useSearch();
158
+ const handleApply = () => {
159
+ handleSearch?.();
160
+ onClose?.();
161
+ };
162
+ return /* @__PURE__ */ jsxs("div", { className: "flex gap-2 border-t bg-muted/30 p-4 pt-3 -mx-4 -mb-4", children: [
163
+ /* @__PURE__ */ jsx(
164
+ Button,
165
+ {
166
+ variant: "outline",
167
+ size: "sm",
168
+ onClick: clearSearch,
169
+ className: "flex-1",
170
+ disabled: disabled || !hasActiveFilters,
171
+ children: "Reset"
172
+ }
173
+ ),
174
+ /* @__PURE__ */ jsxs(Button, { size: "sm", onClick: handleApply, className: "flex-1", disabled, children: [
175
+ /* @__PURE__ */ jsx(Search, { size: 16, className: "mr-2" }),
176
+ "Apply"
177
+ ] })
178
+ ] });
179
+ }
180
+ function SearchFilters({
181
+ children,
182
+ title = "Filters",
183
+ description = "Refine your search results",
184
+ disabled,
185
+ className
186
+ }) {
187
+ const { hasActiveFilters, filters = {} } = useSearch();
188
+ const isMobile = useIsMobile();
189
+ const [isOpen, setIsOpen] = useState(false);
190
+ const activeFilterCount = Object.values(filters).filter((value) => {
191
+ if (Array.isArray(value)) {
192
+ return value.filter(Boolean).length > 0;
193
+ }
194
+ if (typeof value === "string") {
195
+ return value.trim().length > 0 && value !== "all";
196
+ }
197
+ if (typeof value === "number") {
198
+ return value !== void 0 && value !== null && !Number.isNaN(value);
199
+ }
200
+ if (typeof value === "boolean") {
201
+ return value;
202
+ }
203
+ return Boolean(value);
204
+ }).length;
205
+ const FilterContent = () => /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
206
+ /* @__PURE__ */ jsx("div", { className: "space-y-4", children }),
207
+ /* @__PURE__ */ jsx(SearchFilterActions, { onClose: () => setIsOpen(false) })
208
+ ] });
209
+ const triggerButton = /* @__PURE__ */ jsxs(
210
+ Button,
211
+ {
212
+ variant: "outline",
213
+ size: "default",
214
+ className: cn(
215
+ "relative h-10 w-10 shrink-0 px-0 sm:w-auto sm:px-4",
216
+ hasActiveFilters && "bg-primary/10 border-primary hover:bg-primary/15"
217
+ ),
218
+ disabled,
219
+ "aria-label": "Open filters",
220
+ children: [
221
+ /* @__PURE__ */ jsx(Filter, { className: "size-4" }),
222
+ /* @__PURE__ */ jsx("span", { className: "hidden sm:inline ml-2", children: "Filters" }),
223
+ activeFilterCount > 0 && /* @__PURE__ */ jsx(
224
+ Badge,
225
+ {
226
+ variant: "secondary",
227
+ className: "absolute -right-1 -top-1 flex h-5 min-w-5 items-center justify-center rounded-full bg-primary px-1 text-xs font-medium text-primary-foreground",
228
+ children: activeFilterCount
229
+ }
230
+ )
231
+ ]
232
+ }
233
+ );
234
+ if (isMobile) {
235
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
236
+ /* @__PURE__ */ jsxs(
237
+ Button,
238
+ {
239
+ variant: "outline",
240
+ size: "default",
241
+ className: cn(
242
+ "relative h-10 w-10 shrink-0 px-0",
243
+ hasActiveFilters && "bg-primary/10 border-primary hover:bg-primary/15"
244
+ ),
245
+ disabled,
246
+ "aria-label": "Open filters",
247
+ onClick: () => setIsOpen(true),
248
+ children: [
249
+ /* @__PURE__ */ jsx(Filter, { className: "size-4" }),
250
+ activeFilterCount > 0 && /* @__PURE__ */ jsx(
251
+ Badge,
252
+ {
253
+ variant: "secondary",
254
+ className: "absolute -right-1 -top-1 flex h-5 min-w-5 items-center justify-center rounded-full bg-primary px-1 text-xs font-medium text-primary-foreground",
255
+ children: activeFilterCount
256
+ }
257
+ )
258
+ ]
259
+ }
260
+ ),
261
+ /* @__PURE__ */ jsx(
262
+ SheetWrapper,
263
+ {
264
+ open: isOpen,
265
+ onOpenChange: setIsOpen,
266
+ title,
267
+ description,
268
+ side: "bottom",
269
+ size: "default",
270
+ children: /* @__PURE__ */ jsx(FilterContent, {})
271
+ }
272
+ )
273
+ ] });
274
+ }
275
+ return /* @__PURE__ */ jsxs(Popover, { open: isOpen, onOpenChange: setIsOpen, children: [
276
+ /* @__PURE__ */ jsx(PopoverTrigger, { render: triggerButton }),
277
+ /* @__PURE__ */ jsx(PopoverContent, { className: "w-80 p-4 sm:w-96", align: "end", sideOffset: 8, children: /* @__PURE__ */ jsx(FilterContent, {}) })
278
+ ] });
279
+ }
280
+ function SearchActions({
281
+ showSearchButton = true,
282
+ showClearButton = true,
283
+ searchButtonText,
284
+ clearButtonText = "Clear",
285
+ disabled,
286
+ className
287
+ }) {
288
+ const { handleSearch, clearSearch, searchValue, hasActiveFilters, hasActiveSearch } = useSearch();
289
+ const isMobile = useIsMobile();
290
+ const canSearch = Boolean(searchValue?.trim() || hasActiveFilters);
291
+ const canClear = Boolean(searchValue?.trim() || hasActiveFilters || hasActiveSearch);
292
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex items-center gap-2", className), children: [
293
+ showClearButton && canClear && (isMobile ? /* @__PURE__ */ jsx(
294
+ Button,
295
+ {
296
+ variant: "outline",
297
+ size: "icon",
298
+ onClick: clearSearch,
299
+ "aria-label": clearButtonText,
300
+ title: clearButtonText,
301
+ className: "shrink-0",
302
+ children: /* @__PURE__ */ jsx(RotateCcw, {})
303
+ }
304
+ ) : /* @__PURE__ */ jsx(
305
+ Button,
306
+ {
307
+ variant: "outline",
308
+ size: "default",
309
+ onClick: clearSearch,
310
+ className: "h-10 shrink-0",
311
+ children: clearButtonText
312
+ }
313
+ )),
314
+ showSearchButton && /* @__PURE__ */ jsxs(
315
+ Button,
316
+ {
317
+ size: "default",
318
+ onClick: handleSearch,
319
+ disabled: disabled || !canSearch,
320
+ className: "h-10 shrink-0 px-3 sm:px-4",
321
+ children: [
322
+ /* @__PURE__ */ jsx(
323
+ Search,
324
+ {
325
+ size: 16,
326
+ className: cn(searchButtonText ? "mr-2" : "sm:mr-2")
327
+ }
328
+ ),
329
+ /* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: searchButtonText || "Search" })
330
+ ]
331
+ }
332
+ )
333
+ ] });
334
+ }
335
+ function SearchContainer({ children, className }) {
336
+ return /* @__PURE__ */ jsx("div", { className: cn("flex items-center gap-2 flex-1", className), children });
337
+ }
338
+
339
+ export { search_exports as Search, SearchProvider, useSearch };
340
+ //# sourceMappingURL=search.js.map
341
+ //# sourceMappingURL=search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/search/index.ts","../src/components/search/search-context.tsx","../src/components/search/search-root.tsx","../src/components/search/search-input.tsx","../src/components/search/search-type-input.tsx","../src/components/search/search-filter-actions.tsx","../src/components/search/search-filters.tsx","../src/components/search/search-actions.tsx","../src/components/search/search-container.tsx"],"names":["jsx","jsxs","InputGroup","InputGroupAddon","Search","InputGroupInput","InputGroupButton","X","Button"],"mappings":"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","file":"search.js","sourcesContent":["/**\r\n * Composable Search Component System\r\n *\r\n * @example\r\n * ```tsx\r\n * import { Search } from \"@classytic/fluid\";\r\n *\r\n * function MySearch() {\r\n * const searchHook = useMySearch();\r\n *\r\n * return (\r\n * <Search.Root hook={searchHook}>\r\n * <Search.Container>\r\n * <Search.Input placeholder=\"Search...\" />\r\n * <Search.Filters>\r\n * <SelectInput ... />\r\n * <TagChoiceInput ... />\r\n * </Search.Filters>\r\n * <Search.Actions />\r\n * </Search.Container>\r\n * </Search.Root>\r\n * );\r\n * }\r\n * ```\r\n */\r\n\r\nexport { SearchRoot as Root, type SearchRootProps } from \"./search-root\";\r\nexport { SearchInput as Input, type SearchInputProps } from \"./search-input\";\r\nexport {\r\n SearchTypeInput as TypeInput,\r\n type SearchTypeInputProps,\r\n type SearchTypeOption,\r\n} from \"./search-type-input\";\r\nexport { SearchFilters as Filters, type SearchFiltersProps } from \"./search-filters\";\r\nexport { SearchActions as Actions, type SearchActionsProps } from \"./search-actions\";\r\nexport { SearchContainer as Container, type SearchContainerProps } from \"./search-container\";\r\nexport {\r\n SearchFilterActions as FilterActions,\r\n type SearchFilterActionsProps,\r\n} from \"./search-filter-actions\";\r\nexport { useSearch, SearchProvider, type SearchProviderProps } from \"./search-context\";\r\n","\"use client\";\r\n\r\nimport { createContext, useContext, type ReactNode } from \"react\";\r\nimport type { UseBaseSearchReturn } from \"../../hooks/use-base-search\";\r\n\r\nconst SearchContext = createContext<UseBaseSearchReturn | undefined>(undefined);\r\n\r\nexport interface SearchProviderProps {\r\n children: ReactNode;\r\n value: UseBaseSearchReturn;\r\n}\r\n\r\nexport function SearchProvider({ children, value }: SearchProviderProps) {\r\n return (\r\n <SearchContext.Provider value={value}>{children}</SearchContext.Provider>\r\n );\r\n}\r\n\r\nexport function useSearch(): UseBaseSearchReturn {\r\n const context = useContext(SearchContext);\r\n if (!context) {\r\n throw new Error(\"useSearch must be used within SearchProvider\");\r\n }\r\n return context;\r\n}\r\n","\"use client\";\r\n\r\nimport type { ReactNode } from \"react\";\r\nimport { cn } from \"../../utils\";\r\nimport { SearchProvider } from \"./search-context\";\r\nimport type { UseBaseSearchReturn } from \"../../hooks/use-base-search\";\r\n\r\nexport interface SearchRootProps {\r\n children: ReactNode;\r\n hook: UseBaseSearchReturn;\r\n className?: string;\r\n}\r\n\r\n/**\r\n * Root search component that provides context to all child components\r\n *\r\n * @example\r\n * <Search.Root hook={useMySearch()}>\r\n * <Search.Input />\r\n * <Search.Filters />\r\n * <Search.Actions />\r\n * </Search.Root>\r\n */\r\nexport function SearchRoot({ children, hook, className }: SearchRootProps) {\r\n return (\r\n <SearchProvider value={hook}>\r\n <div className={cn(\"w-full\", className)}>{children}</div>\r\n </SearchProvider>\r\n );\r\n}\r\n","\"use client\";\n\nimport type { KeyboardEvent, ChangeEvent } from \"react\";\nimport { Search, X } from \"lucide-react\";\nimport { cn } from \"../../utils\";\nimport {\n InputGroup,\n InputGroupAddon,\n InputGroupInput,\n InputGroupButton,\n} from \"@/components/ui/input-group\";\nimport { useSearch } from \"./search-context\";\n\nexport interface SearchInputProps {\n placeholder?: string;\n className?: string;\n disabled?: boolean;\n showIcon?: boolean;\n showClearButton?: boolean;\n onKeyDown?: (event: KeyboardEvent<HTMLInputElement>) => void;\n}\n\n/**\n * Search input component using modern shadcn InputGroup pattern\n *\n * @example\n * <Search.Input placeholder=\"Search...\" />\n */\nexport function SearchInput({\n placeholder = \"Search...\",\n className,\n disabled,\n showIcon = true,\n showClearButton = false,\n onKeyDown,\n ...props\n}: SearchInputProps) {\n const { searchValue, setSearchValue, handleSearch } = useSearch();\n\n const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {\n onKeyDown?.(event);\n if (!event.defaultPrevented && event.key === \"Enter\" && !disabled) {\n handleSearch?.();\n }\n };\n\n return (\n <InputGroup\n className={cn(\"h-10 flex-1 shadow-sm\", className)}\n data-disabled={disabled || undefined}\n >\n {showIcon && (\n <InputGroupAddon align=\"inline-start\">\n <Search className=\"size-4\" />\n </InputGroupAddon>\n )}\n\n <InputGroupInput\n {...props}\n type=\"text\"\n placeholder={placeholder}\n value={searchValue || \"\"}\n onChange={(e: ChangeEvent<HTMLInputElement>) => setSearchValue?.(e.target.value)}\n onKeyDown={handleKeyDown}\n disabled={disabled}\n className=\"text-base\"\n />\n\n {showClearButton && searchValue && (\n <InputGroupAddon align=\"inline-end\">\n <InputGroupButton\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={() => setSearchValue?.(\"\")}\n disabled={disabled}\n >\n <X className=\"size-4\" />\n </InputGroupButton>\n </InputGroupAddon>\n )}\n </InputGroup>\n );\n}\n","\"use client\";\n\nimport type { KeyboardEvent, ChangeEvent } from \"react\";\nimport { Search, X } from \"lucide-react\";\nimport { cn } from \"../../utils\";\nimport {\n InputGroup,\n InputGroupAddon,\n InputGroupInput,\n InputGroupButton,\n} from \"@/components/ui/input-group\";\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"@/components/ui/select\";\nimport { useSearch } from \"./search-context\";\n\nexport interface SearchTypeOption {\n value: string;\n label: string;\n}\n\nexport interface SearchTypeInputProps {\n placeholder?: string;\n className?: string;\n disabled?: boolean;\n showIcon?: boolean;\n showClearButton?: boolean;\n searchTypeOptions?: SearchTypeOption[];\n onKeyDown?: (event: KeyboardEvent<HTMLInputElement>) => void;\n}\n\n/**\n * Search input component with type selector using InputGroup pattern\n *\n * @example\n * <Search.TypeInput\n * placeholder=\"Search...\"\n * searchTypeOptions={[\n * { value: \"_id\", label: \"ID\" },\n * { value: \"customerPhone\", label: \"Phone\" },\n * { value: \"customerEmail\", label: \"Email\" },\n * ]}\n * />\n */\nexport function SearchTypeInput({\n placeholder = \"Search...\",\n className,\n disabled,\n showIcon = true,\n showClearButton = false,\n searchTypeOptions = [],\n onKeyDown,\n ...props\n}: SearchTypeInputProps) {\n const { searchValue, setSearchValue, searchType, setSearchType, handleSearch } =\n useSearch();\n\n const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {\n onKeyDown?.(event);\n if (!event.defaultPrevented && event.key === \"Enter\" && !disabled) {\n handleSearch?.();\n }\n };\n\n const selectedOption = searchTypeOptions.find((opt) => opt.value === searchType);\n\n return (\n <InputGroup\n className={cn(\"h-10 flex-1 shadow-sm\", className)}\n data-disabled={disabled || undefined}\n >\n {showIcon && (\n <InputGroupAddon align=\"inline-start\">\n <Search className=\"size-4\" />\n </InputGroupAddon>\n )}\n\n {/* Search Type Selector */}\n {searchTypeOptions.length > 0 && (\n <InputGroupAddon align=\"inline-start\" className=\"pr-0\">\n <Select\n value={searchType}\n onValueChange={setSearchType}\n disabled={disabled}\n >\n <SelectTrigger className=\"h-7 w-auto border-0 bg-transparent px-2 text-sm font-medium shadow-none focus:ring-0\">\n <SelectValue>\n {selectedOption?.label || searchTypeOptions[0]?.label}\n </SelectValue>\n </SelectTrigger>\n <SelectContent>\n {searchTypeOptions.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <div className=\"h-6 w-px bg-border ml-1\" />\n </InputGroupAddon>\n )}\n\n <InputGroupInput\n {...props}\n type=\"text\"\n placeholder={placeholder}\n value={searchValue || \"\"}\n onChange={(e: ChangeEvent<HTMLInputElement>) => setSearchValue?.(e.target.value)}\n onKeyDown={handleKeyDown}\n disabled={disabled}\n className=\"text-base\"\n />\n\n {showClearButton && searchValue && (\n <InputGroupAddon align=\"inline-end\">\n <InputGroupButton\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={() => setSearchValue?.(\"\")}\n disabled={disabled}\n >\n <X className=\"size-4\" />\n </InputGroupButton>\n </InputGroupAddon>\n )}\n </InputGroup>\n );\n}\n","\"use client\";\r\n\r\nimport { Search } from \"lucide-react\";\r\nimport { Button } from \"@/components/ui/button\";\r\nimport { useSearch } from \"./search-context\";\r\n\r\nexport interface SearchFilterActionsProps {\r\n onClose?: () => void;\r\n disabled?: boolean;\r\n}\r\n\r\n/**\r\n * Filter actions component (Reset/Apply buttons)\r\n * Used inside filter popovers/sheets\r\n */\r\nexport function SearchFilterActions({ onClose, disabled }: SearchFilterActionsProps) {\r\n const { handleSearch, clearSearch, hasActiveFilters } = useSearch();\r\n\r\n const handleApply = () => {\r\n handleSearch?.();\r\n onClose?.();\r\n };\r\n\r\n return (\r\n <div className=\"flex gap-2 border-t bg-muted/30 p-4 pt-3 -mx-4 -mb-4\">\r\n <Button\r\n variant=\"outline\"\r\n size=\"sm\"\r\n onClick={clearSearch}\r\n className=\"flex-1\"\r\n disabled={disabled || !hasActiveFilters}\r\n >\r\n Reset\r\n </Button>\r\n <Button size=\"sm\" onClick={handleApply} className=\"flex-1\" disabled={disabled}>\r\n <Search size={16} className=\"mr-2\" />\r\n Apply\r\n </Button>\r\n </div>\r\n );\r\n}\r\n","\"use client\";\r\n\r\nimport { useState, type ReactNode } from \"react\";\r\nimport { Filter } from \"lucide-react\";\r\nimport { cn } from \"../../utils\";\r\nimport { Button } from \"@/components/ui/button\";\r\nimport { Badge } from \"@/components/ui/badge\";\r\nimport {\r\n Popover,\r\n PopoverContent,\r\n PopoverTrigger,\r\n} from \"@/components/ui/popover\";\r\nimport { SheetWrapper } from \"../sheet-wrapper\";\r\nimport { useIsMobile } from \"../../hooks/use-mobile\";\r\nimport { useSearch } from \"./search-context\";\r\nimport { SearchFilterActions } from \"./search-filter-actions\";\r\n\r\nexport interface SearchFiltersProps {\r\n children: ReactNode;\r\n title?: string;\r\n description?: string;\r\n disabled?: boolean;\r\n className?: string;\r\n}\r\n\r\n/**\r\n * Search filters component with mobile sheet support\r\n *\r\n * @example\r\n * <Search.Filters>\r\n * <TagChoiceInput label=\"Category\" ... />\r\n * <SelectInput label=\"Status\" ... />\r\n * </Search.Filters>\r\n */\r\nexport function SearchFilters({\r\n children,\r\n title = \"Filters\",\r\n description = \"Refine your search results\",\r\n disabled,\r\n className,\r\n}: SearchFiltersProps) {\r\n const { hasActiveFilters, filters = {} } = useSearch();\r\n const isMobile = useIsMobile();\r\n const [isOpen, setIsOpen] = useState(false);\r\n\r\n // Count active filters (excluding default \"all\" values)\r\n const activeFilterCount = Object.values(filters).filter((value) => {\r\n if (Array.isArray(value)) {\r\n return value.filter(Boolean).length > 0;\r\n }\r\n if (typeof value === \"string\") {\r\n // Exclude \"all\" as it's the default/neutral state\r\n return value.trim().length > 0 && value !== \"all\";\r\n }\r\n if (typeof value === \"number\") {\r\n return value !== undefined && value !== null && !Number.isNaN(value);\r\n }\r\n if (typeof value === \"boolean\") {\r\n return value;\r\n }\r\n return Boolean(value);\r\n }).length;\r\n\r\n const FilterContent = () => (\r\n <div className=\"space-y-4\">\r\n <div className=\"space-y-4\">{children}</div>\r\n <SearchFilterActions onClose={() => setIsOpen(false)} />\r\n </div>\r\n );\r\n\r\n const triggerButton = (\r\n <Button\r\n variant=\"outline\"\r\n size=\"default\"\r\n className={cn(\r\n \"relative h-10 w-10 shrink-0 px-0 sm:w-auto sm:px-4\",\r\n hasActiveFilters && \"bg-primary/10 border-primary hover:bg-primary/15\"\r\n )}\r\n disabled={disabled}\r\n aria-label=\"Open filters\"\r\n >\r\n <Filter className=\"size-4\" />\r\n <span className=\"hidden sm:inline ml-2\">Filters</span>\r\n {activeFilterCount > 0 && (\r\n <Badge\r\n variant=\"secondary\"\r\n className=\"absolute -right-1 -top-1 flex h-5 min-w-5 items-center justify-center rounded-full bg-primary px-1 text-xs font-medium text-primary-foreground\"\r\n >\r\n {activeFilterCount}\r\n </Badge>\r\n )}\r\n </Button>\r\n );\r\n\r\n if (isMobile) {\r\n return (\r\n <>\r\n <Button\r\n variant=\"outline\"\r\n size=\"default\"\r\n className={cn(\r\n \"relative h-10 w-10 shrink-0 px-0\",\r\n hasActiveFilters && \"bg-primary/10 border-primary hover:bg-primary/15\"\r\n )}\r\n disabled={disabled}\r\n aria-label=\"Open filters\"\r\n onClick={() => setIsOpen(true)}\r\n >\r\n <Filter className=\"size-4\" />\r\n {activeFilterCount > 0 && (\r\n <Badge\r\n variant=\"secondary\"\r\n className=\"absolute -right-1 -top-1 flex h-5 min-w-5 items-center justify-center rounded-full bg-primary px-1 text-xs font-medium text-primary-foreground\"\r\n >\r\n {activeFilterCount}\r\n </Badge>\r\n )}\r\n </Button>\r\n <SheetWrapper\r\n open={isOpen}\r\n onOpenChange={setIsOpen}\r\n title={title}\r\n description={description}\r\n side=\"bottom\"\r\n size=\"default\"\r\n >\r\n <FilterContent />\r\n </SheetWrapper>\r\n </>\r\n );\r\n }\r\n\r\n return (\r\n <Popover open={isOpen} onOpenChange={setIsOpen}>\r\n <PopoverTrigger render={triggerButton} />\r\n <PopoverContent className=\"w-80 p-4 sm:w-96\" align=\"end\" sideOffset={8}>\r\n <FilterContent />\r\n </PopoverContent>\r\n </Popover>\r\n );\r\n}\r\n","\"use client\";\r\n\r\nimport { Search, RotateCcw } from \"lucide-react\";\r\nimport { cn } from \"../../utils\";\r\nimport { Button } from \"@/components/ui/button\";\r\nimport { useSearch } from \"./search-context\";\r\nimport { useIsMobile } from \"../../hooks/use-mobile\";\r\n\r\nexport interface SearchActionsProps {\r\n showSearchButton?: boolean;\r\n showClearButton?: boolean;\r\n searchButtonText?: string;\r\n clearButtonText?: string;\r\n disabled?: boolean;\r\n className?: string;\r\n}\r\n\r\n/**\r\n * Search action buttons\r\n */\r\nexport function SearchActions({\r\n showSearchButton = true,\r\n showClearButton = true,\r\n searchButtonText,\r\n clearButtonText = \"Clear\",\r\n disabled,\r\n className,\r\n}: SearchActionsProps) {\r\n const { handleSearch, clearSearch, searchValue, hasActiveFilters, hasActiveSearch } =\r\n useSearch();\r\n const isMobile = useIsMobile();\r\n\r\n const canSearch = Boolean(searchValue?.trim() || hasActiveFilters);\r\n const canClear = Boolean(searchValue?.trim() || hasActiveFilters || hasActiveSearch);\r\n\r\n return (\r\n <div className={cn(\"flex items-center gap-2\", className)}>\r\n {showClearButton &&\r\n canClear &&\r\n (isMobile ? (\r\n <Button\r\n variant=\"outline\"\r\n size=\"icon\"\r\n onClick={clearSearch}\r\n aria-label={clearButtonText}\r\n title={clearButtonText}\r\n className=\"shrink-0\"\r\n >\r\n <RotateCcw />\r\n </Button>\r\n ) : (\r\n <Button\r\n variant=\"outline\"\r\n size=\"default\"\r\n onClick={clearSearch}\r\n className=\"h-10 shrink-0\"\r\n >\r\n {clearButtonText}\r\n </Button>\r\n ))}\r\n {showSearchButton && (\r\n <Button\r\n size=\"default\"\r\n onClick={handleSearch}\r\n disabled={disabled || !canSearch}\r\n className=\"h-10 shrink-0 px-3 sm:px-4\"\r\n >\r\n <Search\r\n size={16}\r\n className={cn(searchButtonText ? \"mr-2\" : \"sm:mr-2\")}\r\n />\r\n <span className=\"hidden sm:inline\">{searchButtonText || \"Search\"}</span>\r\n </Button>\r\n )}\r\n </div>\r\n );\r\n}\r\n","\"use client\";\r\n\r\nimport type { ReactNode } from \"react\";\r\nimport { cn } from \"../../utils\";\r\n\r\nexport interface SearchContainerProps {\r\n children: ReactNode;\r\n className?: string;\r\n}\r\n\r\n/**\r\n * Container for search input and action buttons\r\n * Provides responsive layout\r\n *\r\n * @example\r\n * <Search.Container>\r\n * <Search.Input />\r\n * <Search.Filters />\r\n * <Search.Actions />\r\n * </Search.Container>\r\n */\r\nexport function SearchContainer({ children, className }: SearchContainerProps) {\r\n return (\r\n <div className={cn(\"flex items-center gap-2 flex-1\", className)}>\r\n {children}\r\n </div>\r\n );\r\n}\r\n"]}