@classytic/fluid 0.1.2 → 0.2.1
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/README.md +63 -18
- package/animations.css +74 -0
- package/dist/chunk-GUHK2DTW.js +15 -0
- package/dist/chunk-GUHK2DTW.js.map +1 -0
- package/dist/chunk-H3NFL3GJ.js +57 -0
- package/dist/chunk-H3NFL3GJ.js.map +1 -0
- package/dist/chunk-J2YRTQE4.js +293 -0
- package/dist/chunk-J2YRTQE4.js.map +1 -0
- package/dist/compact.d.ts +217 -0
- package/dist/compact.js +986 -0
- package/dist/compact.js.map +1 -0
- package/dist/dashboard.d.ts +38 -1
- package/dist/dashboard.js +65 -25
- package/dist/dashboard.js.map +1 -1
- package/dist/index.d.ts +586 -507
- package/dist/index.js +1656 -2211
- package/dist/index.js.map +1 -1
- package/dist/layout.js +2 -60
- package/dist/layout.js.map +1 -1
- package/dist/search.d.ts +172 -0
- package/dist/search.js +341 -0
- package/dist/search.js.map +1 -0
- package/dist/use-base-search-AS5Z3SAy.d.ts +64 -0
- package/package.json +36 -28
- package/styles.css +3 -0
package/dist/layout.js
CHANGED
|
@@ -1,62 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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
|
package/dist/layout.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"layout.js"}
|
package/dist/search.d.ts
ADDED
|
@@ -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"]}
|