@turtleclub/core 0.1.0-beta.16 → 0.1.0-beta.17
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/index.cjs +35 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +44 -17
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/wrappers/FiltersPopover.tsx +22 -6
- package/src/wrappers/FiltersWrapper.tsx +26 -11
package/dist/index.cjs
CHANGED
|
@@ -239,10 +239,24 @@ function FiltersPopover({
|
|
|
239
239
|
);
|
|
240
240
|
const sectionNames = Object.keys(sections);
|
|
241
241
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_ui4.Popover, { children: [
|
|
242
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
242
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
243
|
+
import_ui4.PopoverTrigger,
|
|
244
|
+
{
|
|
245
|
+
className: (0, import_ui4.cn)(
|
|
246
|
+
(0, import_ui4.buttonVariants)({
|
|
247
|
+
variant: "default",
|
|
248
|
+
size: "default",
|
|
249
|
+
border: "bordered"
|
|
250
|
+
}),
|
|
251
|
+
"!bg-neutral-alpha-2",
|
|
252
|
+
className
|
|
253
|
+
),
|
|
254
|
+
children: [
|
|
255
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react.SlidersHorizontal, { className: "mr-2 h-4 w-4" }),
|
|
256
|
+
triggerLabel
|
|
257
|
+
]
|
|
258
|
+
}
|
|
259
|
+
),
|
|
246
260
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
247
261
|
import_ui4.PopoverContent,
|
|
248
262
|
{
|
|
@@ -342,6 +356,9 @@ function FiltersWrapper({
|
|
|
342
356
|
layout,
|
|
343
357
|
className,
|
|
344
358
|
badgesClassName,
|
|
359
|
+
slotClassName,
|
|
360
|
+
leftSlot,
|
|
361
|
+
rightSlot,
|
|
345
362
|
columns = 3,
|
|
346
363
|
triggerLabel = "Filters",
|
|
347
364
|
popoverColumns = 2,
|
|
@@ -356,16 +373,20 @@ function FiltersWrapper({
|
|
|
356
373
|
}) {
|
|
357
374
|
const hasActiveFilters = activeFilters.some((filter) => filter.isActive && filter.value);
|
|
358
375
|
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: (0, import_ui6.cn)("space-y-3", className), children: [
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
{
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
376
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: (0, import_ui6.cn)("flex items-center gap-2", slotClassName), children: [
|
|
377
|
+
leftSlot,
|
|
378
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex-1", children: layout === "grid" ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(FiltersGrid, { filters, columns, className: popoverClassName }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
379
|
+
FiltersPopover,
|
|
380
|
+
{
|
|
381
|
+
filters,
|
|
382
|
+
triggerLabel,
|
|
383
|
+
columns: popoverColumns,
|
|
384
|
+
className: popoverClassName,
|
|
385
|
+
popoverClassName: popoverContentClassName
|
|
386
|
+
}
|
|
387
|
+
) }),
|
|
388
|
+
rightSlot
|
|
389
|
+
] }),
|
|
369
390
|
showActiveBadges && hasActiveFilters && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
370
391
|
ActiveFilterBadges,
|
|
371
392
|
{
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/filters/RangeSliderFilter.tsx","../src/filters/BooleanFilter.tsx","../src/filters/Filter.tsx","../src/filters/MultiSelectFilter.tsx","../src/wrappers/FiltersGrid.tsx","../src/wrappers/FiltersPopover.tsx","../src/wrappers/ActiveFilterBadges.tsx","../src/wrappers/FiltersWrapper.tsx","../src/helpers/usePagination.ts","../src/helpers/useSorting.ts","../src/selectors/ChainsSelector.tsx","../src/selectors/TokensSelector.tsx","../src/selectors/ProductsSelector.tsx","../src/selectors/OpportunitiesSelector.tsx","../src/selectors/ChainSelector.tsx","../src/selectors/TokenSelector.tsx","../src/selectors/ProductSelector.tsx","../src/selectors/OpportunitySelector.tsx"],"sourcesContent":["export * from \"./filters\";\nexport * from \"./wrappers\";\nexport * from \"./helpers\";\nexport * from \"./selectors\";\n","\"use client\";\n\nimport React, { useEffect, useState } from \"react\";\nimport { Slider, Label } from \"@turtleclub/ui\";\nimport { useQueryState, parseAsInteger } from \"nuqs\";\n\ninterface RangeSliderFilterProps {\n /** Query key for minimum value (default: 'min') */\n minQueryKey?: string;\n /** Query key for maximum value (default: 'max') */\n maxQueryKey?: string;\n /** Minimum value (default: 0) */\n min?: number;\n /** Maximum value (default: 100) */\n max?: number;\n /** Step value for slider (default: 1) */\n step?: number;\n /** Whether the filter is disabled */\n disabled?: boolean;\n /** Custom label for the filter */\n label: string;\n /** Whether to show values (default: true) */\n showValues?: boolean;\n /** Value formatter function */\n formatValue?: (value: number) => string;\n /** Custom className for the container */\n className?: string;\n}\n\nconst defaultFormatter = (value: number): string => {\n return value.toLocaleString();\n};\n\nexport function RangeSliderFilter({\n minQueryKey = \"min\",\n maxQueryKey = \"max\",\n min = 0,\n max = 100,\n step = 1,\n disabled = false,\n label = \"Range\",\n showValues = true,\n formatValue = defaultFormatter,\n className = \"\",\n}: RangeSliderFilterProps) {\n const [minValue, setMinValue] = useQueryState(minQueryKey, parseAsInteger.withDefault(min));\n\n const [maxValue, setMaxValue] = useQueryState(maxQueryKey, parseAsInteger.withDefault(max));\n\n // Local state for slider values (to avoid excessive URL updates)\n const [localRange, setLocalRange] = useState([minValue, maxValue]);\n\n // Update local state when query params change\n useEffect(() => {\n setLocalRange([minValue, maxValue]);\n }, [minValue, maxValue]);\n\n const handleRangeChange = (values: number[]) => {\n setLocalRange(values);\n };\n\n const handleRangeCommit = (values: number[]) => {\n const [newMin, newMax] = values;\n if (newMin !== minValue) {\n setMinValue(newMin === min ? null : newMin);\n }\n if (newMax !== maxValue) {\n setMaxValue(newMax === max ? null : newMax);\n }\n };\n\n const isDefaultRange = localRange[0] === min && localRange[1] === max;\n\n return (\n <div className={`space-y-3 ${className}`}>\n <div className=\"flex items-center justify-between\">\n <Label className=\"text-sm font-medium\">{label}</Label>\n {showValues && (\n <div className=\"flex items-center gap-1 text-xs text-muted-foreground\">\n <span>{formatValue(localRange[0])}</span>\n <span>-</span>\n <span>{formatValue(localRange[1])}</span>\n </div>\n )}\n </div>\n\n <div className=\"px-2\">\n <Slider\n value={localRange}\n onValueChange={handleRangeChange}\n onValueCommit={handleRangeCommit}\n min={min}\n max={max}\n step={step}\n disabled={disabled}\n className=\"w-full\"\n />\n </div>\n\n {showValues && (\n <div className=\"flex justify-between text-xs text-muted-foreground\">\n <span>{formatValue(min)}</span>\n <span>{formatValue(max)}</span>\n </div>\n )}\n\n {!isDefaultRange && (\n <div className=\"flex justify-end\">\n <button\n onClick={() => {\n setLocalRange([min, max]);\n handleRangeCommit([min, max]);\n }}\n className=\"text-xs text-muted-foreground hover:text-foreground transition-colors\"\n disabled={disabled}\n >\n Reset\n </button>\n </div>\n )}\n </div>\n );\n}\n","\"use client\";\n\nimport React from \"react\";\nimport { Switch, Label } from \"@turtleclub/ui\";\nimport { useQueryState, parseAsBoolean } from \"nuqs\";\n\ninterface BooleanSwitchFilterProps {\n /** Query key for the boolean value (default: 'enabled') */\n queryKey?: string;\n /** Default value when not set (default: false) */\n defaultValue?: boolean;\n /** Whether the switch is disabled */\n disabled?: boolean;\n /** Label for the switch */\n label: string;\n /** Description text (optional) */\n description?: string;\n /** Custom className for the container */\n className?: string;\n}\n\nexport function BooleanFilter({\n queryKey = \"enabled\",\n defaultValue = false,\n disabled = false,\n label,\n description,\n className = \"\",\n}: BooleanSwitchFilterProps) {\n const [value, setValue] = useQueryState(queryKey, parseAsBoolean.withDefault(defaultValue));\n\n const handleToggle = (checked: boolean) => {\n // If the value is the same as default, remove from URL\n setValue(checked === defaultValue ? null : checked);\n };\n\n return (\n <div className={`flex items-center justify-between space-x-2 ${className}`}>\n <div className=\"space-y-0.5\">\n <Label className=\"text-sm font-medium\">{label}</Label>\n {description && <p className=\"text-xs text-muted-foreground\">{description}</p>}\n </div>\n <Switch checked={value} onCheckedChange={handleToggle} disabled={disabled} />\n </div>\n );\n}\n","\"use client\";\n\nimport React from \"react\";\nimport { useQueryState, parseAsString } from \"nuqs\";\n\ninterface FilterProps {\n /** Query parameter key */\n queryKey: string;\n /** Child component to render (selector) */\n children: (props: { value: string; onValueChange: (value: string) => void }) => React.ReactNode;\n /** Callback when value changes */\n onValueChange?: (value: string) => void;\n}\n\n/**\n * Generic Filter component that handles nuqs URL state management for single values.\n *\n * Provides value and onValueChange props to child components,\n * decoupling selector logic from URL state management.\n *\n * @example Basic Single Selection\n * ```tsx\n * // Single chain selection - persists to URL as ?chainId=ethereum\n * <Filter queryKey=\"chainId\">\n * {({ value, onValueChange }) => (\n * <ChainSelector\n * value={value}\n * onValueChange={onValueChange}\n * placeholder=\"Select chain\"\n * />\n * )}\n * </Filter>\n * ```\n */\nexport function Filter({ queryKey, children, onValueChange }: FilterProps) {\n const [value, setValue] = useQueryState(queryKey, parseAsString.withDefault(\"\"));\n\n const handleValueChange = (newValue: string) => {\n setValue(newValue || null);\n onValueChange?.(newValue);\n };\n\n return <>{children({ value, onValueChange: handleValueChange })}</>;\n}\n","\"use client\";\n\nimport React from \"react\";\nimport { useQueryState, parseAsArrayOf, parseAsString } from \"nuqs\";\n\ninterface MultiSelectFilterProps {\n /** Query parameter key */\n queryKey: string;\n /** Child component to render (selector) */\n children: (props: {\n value: string[];\n onValueChange: (values: string[]) => void;\n }) => React.ReactNode;\n /** Callback when values change */\n onValueChange?: (values: string[]) => void;\n}\n\n/**\n * Generic MultiSelectFilter component that handles nuqs URL state management for array values.\n *\n * Provides value and onValueChange props to child components,\n * decoupling selector logic from URL state management.\n *\n * @example\n * ```tsx\n * <MultiSelectFilter queryKey=\"chainId\">\n * {({ value, onValueChange }) => (\n * <ChainsSelector\n * value={value}\n * onValueChange={onValueChange}\n * placeholder=\"Select chains\"\n * />\n * )}\n * </MultiSelectFilter>\n * ```\n */\nexport function MultiSelectFilter({ queryKey, children, onValueChange }: MultiSelectFilterProps) {\n const [value, setValue] = useQueryState(queryKey, parseAsArrayOf(parseAsString).withDefault([]));\n\n const handleValueChange = (newValue: string[]) => {\n setValue(newValue.length > 0 ? newValue : null);\n onValueChange?.(newValue);\n };\n\n return <>{children({ value, onValueChange: handleValueChange })}</>;\n}\n","\"use client\";\n\nimport React from \"react\";\nimport { cn, Label } from \"@turtleclub/ui\";\n\nexport interface FilterConfig {\n key: string;\n label: string;\n component: React.ReactNode;\n enabled: boolean;\n section?: string;\n}\n\nexport interface FiltersGridProps {\n filters: FilterConfig[];\n columns?: number;\n className?: string;\n sectionClassName?: string;\n}\n\nexport function FiltersGrid({\n filters,\n columns = 3,\n className,\n sectionClassName,\n}: FiltersGridProps) {\n const enabledFilters = filters.filter((filter) => filter.enabled);\n\n if (enabledFilters.length === 0) {\n return null;\n }\n\n // Group filters by section\n const sections = enabledFilters.reduce(\n (acc, filter) => {\n const sectionName = filter.section || \"default\";\n if (!acc[sectionName]) {\n acc[sectionName] = [];\n }\n acc[sectionName].push(filter);\n return acc;\n },\n {} as Record<string, typeof enabledFilters>\n );\n\n const sectionNames = Object.keys(sections);\n\n return (\n <div className={cn(\"space-y-4\", className)}>\n {sectionNames.map((sectionName, sectionIndex) => (\n <div key={sectionName}>\n {sectionIndex > 0 && <div className=\"border-t border-border mb-4\" />}\n <div\n className={cn(\"grid gap-2\", sectionClassName)}\n style={{\n gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`,\n }}\n >\n {sections[sectionName].map((filter) => (\n <div key={filter.key} className=\"flex flex-col gap-2\">\n {filter.component}\n </div>\n ))}\n </div>\n </div>\n ))}\n </div>\n );\n}\n","\"use client\";\n\nimport React from \"react\";\nimport { Popover, PopoverContent, PopoverTrigger, Button, cn, Label, Badge } from \"@turtleclub/ui\";\nimport { SlidersHorizontal } from \"lucide-react\";\nimport { FilterConfig } from \"./FiltersGrid\";\n\nexport interface FiltersPopoverProps {\n filters: FilterConfig[];\n triggerLabel?: string;\n columns?: number;\n className?: string;\n popoverClassName?: string;\n sectionClassName?: string;\n}\n\nexport function FiltersPopover({\n filters,\n triggerLabel = \"Filters\",\n columns = 2,\n className,\n popoverClassName,\n sectionClassName,\n}: FiltersPopoverProps) {\n const enabledFilters = filters.filter((filter) => filter.enabled);\n\n if (enabledFilters.length === 0) {\n return null;\n }\n\n // Group filters by section\n const sections = enabledFilters.reduce(\n (acc, filter) => {\n const sectionName = filter.section || \"default\";\n if (!acc[sectionName]) {\n acc[sectionName] = [];\n }\n acc[sectionName].push(filter);\n return acc;\n },\n {} as Record<string, typeof enabledFilters>\n );\n\n const sectionNames = Object.keys(sections);\n\n return (\n <Popover>\n <PopoverTrigger asChild>\n <Button variant=\"outline\" className={cn(className)}>\n <SlidersHorizontal className=\"mr-2 h-4 w-4\" />\n {triggerLabel}\n </Button>\n </PopoverTrigger>\n <PopoverContent\n className={cn(\"w-auto min-w-[400px] max-w-[600px]\", popoverClassName)}\n align=\"start\"\n >\n <div className=\"space-y-4\">\n <div className=\"flex items-center justify-between\">\n <h4 className=\"font-medium text-sm\">Filters</h4>\n </div>\n <div className=\"space-y-4\">\n {sectionNames.map((sectionName, sectionIndex) => (\n <div key={sectionName}>\n {sectionIndex > 0 && <div className=\"border-t border-border mb-4\" />}\n <div\n className={cn(\"grid gap-2\", sectionClassName)}\n style={{\n gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`,\n }}\n >\n {sections[sectionName].map((filter) => (\n <div key={filter.key} className=\"flex flex-col gap-2\">\n {filter.component}\n </div>\n ))}\n </div>\n </div>\n ))}\n </div>\n </div>\n </PopoverContent>\n </Popover>\n );\n}\n","\"use client\";\n\nimport React, { useMemo } from \"react\";\nimport { Badge, Button, cn } from \"@turtleclub/ui\";\nimport { X } from \"lucide-react\";\n\nexport interface ActiveFilterConfig {\n /** Unique key for the filter */\n key: string;\n /** Display label for the filter */\n label: string;\n /** Current filter value to display */\n value: string;\n /** Query parameter key(s) this filter uses */\n queryKeys: string | string[];\n /** Whether this filter is currently active */\n isActive: boolean;\n}\n\nexport interface ActiveFilterBadgesProps {\n /** Array of filter configurations */\n filters: ActiveFilterConfig[];\n /** Custom className for the container */\n className?: string;\n /** Label for clear all button (default: \"Clear All\") */\n clearAllLabel?: string;\n /** Whether to show clear all button (default: true) */\n showClearAll?: boolean;\n /** Custom render function for individual badges */\n renderBadge?: (filter: ActiveFilterConfig, clearFilter: () => void) => React.ReactNode;\n /** Callback function to clear individual filter */\n onClearFilter: (queryKeys: string | string[]) => void;\n /** Callback function to clear all filters */\n onClearAll: (allQueryKeys: (string | string[])[]) => void;\n}\n\n/**\n * Component that displays active filter badges with individual clear buttons\n * and an optional \"clear all\" button.\n *\n * @example\n * ```tsx\n * const activeFilters = [\n * {\n * key: 'chain',\n * label: 'Chain',\n * value: 'Ethereum',\n * queryKeys: 'chainId',\n * isActive: true,\n * },\n * {\n * key: 'sorting',\n * label: 'Sort',\n * value: 'Name (A-Z)',\n * queryKeys: ['sortBy', 'sortOrder'],\n * isActive: true,\n * },\n * ];\n *\n * <ActiveFilterBadges filters={activeFilters} />\n * ```\n */\nexport function ActiveFilterBadges({\n filters,\n className,\n clearAllLabel = \"Clear All\",\n showClearAll = true,\n renderBadge,\n onClearFilter,\n onClearAll,\n}: ActiveFilterBadgesProps) {\n // Get only active filters\n const activeFilters = useMemo(() => {\n return filters.filter((filter) => filter.isActive && filter.value);\n }, [filters]);\n\n // Clear all function\n const clearAll = () => {\n const allQueryKeys = activeFilters.map((filter) => filter.queryKeys);\n onClearAll(allQueryKeys);\n };\n\n // Don't render if no active filters\n if (activeFilters.length === 0) {\n return null;\n }\n\n return (\n <div className={cn(\"flex flex-wrap items-center gap-2\", className)}>\n {activeFilters.map((filter) => {\n return (\n <FilterBadge\n key={filter.key}\n filter={filter}\n renderBadge={renderBadge}\n onClearFilter={onClearFilter}\n />\n );\n })}\n\n {showClearAll && activeFilters.length > 1 && (\n <Button variant=\"ghost\" size=\"sm\" onClick={clearAll} className=\"h-7 px-2 text-xs\">\n {clearAllLabel}\n </Button>\n )}\n </div>\n );\n}\n\ninterface FilterBadgeProps {\n filter: ActiveFilterConfig;\n renderBadge?: (filter: ActiveFilterConfig, clearFilter: () => void) => React.ReactNode;\n onClearFilter: (queryKeys: string | string[]) => void;\n}\n\nfunction FilterBadge({ filter, renderBadge, onClearFilter }: FilterBadgeProps) {\n const clearFilter = () => {\n onClearFilter(filter.queryKeys);\n };\n\n if (renderBadge) {\n return <>{renderBadge(filter, clearFilter)}</>;\n }\n\n return (\n <Badge className=\"flex items-center gap-1 pr-1 border-border border\">\n <span className=\"text-xs\">\n {filter.label}: {filter.value}\n </span>\n <button\n onClick={clearFilter}\n className=\"ml-1 rounded-full p-0.5 hover:bg-black/10 focus:bg-black/10 focus:outline-none\"\n aria-label={`Clear ${filter.label} filter`}\n >\n <X className=\"h-3 w-3\" />\n </button>\n </Badge>\n );\n}\n","\"use client\";\n\nimport React from \"react\";\nimport { cn } from \"@turtleclub/ui\";\nimport { FiltersGrid } from \"./FiltersGrid\";\nimport { FiltersPopover } from \"./FiltersPopover\";\nimport { ActiveFilterBadges, ActiveFilterConfig } from \"./ActiveFilterBadges\";\nimport { FilterConfig } from \"./FiltersGrid\";\n\nexport interface FiltersWrapperProps {\n /** Array of filter configurations */\n filters: FilterConfig[];\n /** Array of active filter configurations for badges */\n activeFilters: ActiveFilterConfig[];\n /** Layout type: 'grid' or 'popover' */\n layout: \"grid\" | \"popover\";\n /** Custom className for the wrapper */\n className?: string;\n /** Custom className for the active badges section */\n badgesClassName?: string;\n\n // Grid-specific props\n /** Number of columns for grid layout (default: 3) */\n columns?: number;\n\n // Popover-specific props\n /** Button label for popover trigger (default: \"Filters\") */\n triggerLabel?: string;\n /** Number of columns in popover (default: 2) */\n popoverColumns?: number;\n /** Custom className for popover trigger button */\n popoverClassName?: string;\n /** Custom className for popover content */\n popoverContentClassName?: string;\n\n // Active badges props\n /** Label for clear all button (default: \"Clear All\") */\n clearAllLabel?: string;\n /** Whether to show clear all button (default: true) */\n showClearAll?: boolean;\n /** Custom render function for individual badges */\n renderBadge?: (filter: ActiveFilterConfig, clearFilter: () => void) => React.ReactNode;\n /** Whether to show active filter badges (default: true) */\n showActiveBadges?: boolean;\n /** Callback function to clear individual filter */\n onClearFilter: (queryKeys: string | string[]) => void;\n /** Callback function to clear all filters */\n onClearAll: (allQueryKeys: (string | string[])[]) => void;\n}\n\n/**\n * Unified wrapper component that combines filter controls with active filter badges.\n *\n * Supports both grid and popover layouts, automatically displays active filter badges\n * below the filter controls with individual and bulk clear functionality.\n *\n * @example\n * ```tsx\n * const filters = [\n * {\n * key: 'chain',\n * label: 'Chain',\n * component: <ChainFilter />,\n * enabled: true,\n * },\n * ];\n *\n * const activeFilters = [\n * {\n * key: 'chain',\n * label: 'Chain',\n * value: 'Ethereum',\n * queryKeys: 'chainId',\n * isActive: true,\n * },\n * ];\n *\n * <FiltersWrapper\n * layout=\"grid\"\n * filters={filters}\n * activeFilters={activeFilters}\n * />\n * ```\n */\nexport function FiltersWrapper({\n filters,\n activeFilters,\n layout,\n className,\n badgesClassName,\n columns = 3,\n triggerLabel = \"Filters\",\n popoverColumns = 2,\n popoverClassName,\n popoverContentClassName,\n clearAllLabel = \"Clear All\",\n showClearAll = true,\n renderBadge,\n showActiveBadges = true,\n onClearFilter,\n onClearAll,\n}: FiltersWrapperProps) {\n const hasActiveFilters = activeFilters.some((filter) => filter.isActive && filter.value);\n\n return (\n <div className={cn(\"space-y-3\", className)}>\n {/* Filter Controls */}\n {layout === \"grid\" ? (\n <FiltersGrid filters={filters} columns={columns} className={popoverClassName} />\n ) : (\n <FiltersPopover\n filters={filters}\n triggerLabel={triggerLabel}\n columns={popoverColumns}\n className={popoverClassName}\n popoverClassName={popoverContentClassName}\n />\n )}\n\n {/* Active Filter Badges */}\n {showActiveBadges && hasActiveFilters && (\n <ActiveFilterBadges\n filters={activeFilters}\n className={badgesClassName}\n clearAllLabel={clearAllLabel}\n showClearAll={showClearAll}\n renderBadge={renderBadge}\n onClearFilter={onClearFilter}\n onClearAll={onClearAll}\n />\n )}\n </div>\n );\n}\n","\"use client\";\n\nimport { useQueryState, parseAsInteger } from \"nuqs\";\nimport { useMemo, useCallback } from \"react\";\nimport type { PaginationState } from \"@tanstack/react-table\";\n\nexport interface UsePaginationOptions {\n /** Default page size (default: 10) */\n defaultPageSize?: number;\n /** Default page index (default: 0) */\n defaultPageIndex?: number;\n /** Query param key for page index (default: 'page') */\n pageIndexKey?: string;\n /** Query param key for page size (default: 'pageSize') */\n pageSizeKey?: string;\n}\n\nexport interface UsePaginationReturn {\n /** Current pagination state for TanStack Table */\n pagination: PaginationState;\n /** Function to update pagination state */\n setPagination: (updater: PaginationState | ((prev: PaginationState) => PaginationState)) => void;\n /** Current page index (0-based) */\n pageIndex: number;\n /** Current page size */\n pageSize: number;\n /** Set page index directly */\n setPageIndex: (pageIndex: number) => void;\n /** Set page size directly */\n setPageSize: (pageSize: number) => void;\n /** Go to next page */\n nextPage: (totalPages?: number) => void;\n /** Go to previous page */\n previousPage: () => void;\n /** Go to first page */\n firstPage: () => void;\n /** Go to last page */\n lastPage: (totalPages: number) => void;\n /** Reset pagination to defaults */\n reset: () => void;\n /** Check if can go to next page */\n canNextPage: (totalPages?: number) => boolean;\n /** Check if can go to previous page */\n canPreviousPage: boolean;\n}\n\n/**\n * Hook for managing server-side pagination state using nuqs.\n *\n * This hook synchronizes pagination state with URL query parameters,\n * making it perfect for server-side pagination where the URL should\n * reflect the current page and page size.\n *\n * @example\n * ```tsx\n * function MyTable() {\n * const {\n * pagination,\n * setPagination,\n * pageIndex,\n * pageSize,\n * setPageIndex,\n * setPageSize\n * } = usePagination({\n * defaultPageSize: 20,\n * pageIndexKey: 'page',\n * pageSizeKey: 'limit'\n * });\n *\n * // Use with TanStack Table\n * const table = useReactTable({\n * data,\n * columns,\n * manualPagination: true,\n * pagination,\n * onPaginationChange: setPagination,\n * pageCount: Math.ceil(totalCount / pageSize),\n * });\n *\n * return <DataTable table={table} />;\n * }\n * ```\n */\nexport function usePagination(options: UsePaginationOptions = {}): UsePaginationReturn {\n const {\n defaultPageSize = 10,\n defaultPageIndex = 0,\n pageIndexKey = \"page\",\n pageSizeKey = \"pageSize\",\n } = options;\n\n // Use nuqs to manage query state\n const [rawPageIndex, setRawPageIndex] = useQueryState(\n pageIndexKey,\n parseAsInteger.withDefault(defaultPageIndex)\n );\n\n const [rawPageSize, setRawPageSize] = useQueryState(\n pageSizeKey,\n parseAsInteger.withDefault(defaultPageSize)\n );\n\n // Ensure page index is never negative\n const pageIndex = useMemo(() => Math.max(0, rawPageIndex), [rawPageIndex]);\n\n // Use page size directly from query state\n const pageSize = rawPageSize;\n\n // Create pagination state for TanStack Table\n const pagination = useMemo<PaginationState>(\n () => ({\n pageIndex,\n pageSize,\n }),\n [pageIndex, pageSize]\n );\n\n // Set pagination function that handles both object and function updates\n const setPagination = useCallback(\n (updater: PaginationState | ((prev: PaginationState) => PaginationState)) => {\n const newPagination = typeof updater === \"function\" ? updater(pagination) : updater;\n\n if (newPagination.pageIndex !== pageIndex) {\n setRawPageIndex(Math.max(0, newPagination.pageIndex));\n }\n\n if (newPagination.pageSize !== pageSize) {\n setRawPageSize(newPagination.pageSize);\n }\n },\n [pagination, pageIndex, pageSize, setRawPageIndex, setRawPageSize]\n );\n\n // Individual setters\n const setPageIndex = useCallback(\n (newPageIndex: number) => {\n setRawPageIndex(Math.max(0, newPageIndex));\n },\n [setRawPageIndex]\n );\n\n const setPageSize = useCallback(\n (newPageSize: number) => {\n setRawPageSize(newPageSize);\n // Reset to first page when page size changes\n setRawPageIndex(0);\n },\n [setRawPageSize, setRawPageIndex]\n );\n\n // Navigation functions\n const nextPage = useCallback(\n (totalPages?: number) => {\n const nextPageIndex = pageIndex + 1;\n if (!totalPages || nextPageIndex < totalPages) {\n setPageIndex(nextPageIndex);\n }\n },\n [pageIndex, setPageIndex]\n );\n\n const previousPage = useCallback(() => {\n if (pageIndex > 0) {\n setPageIndex(pageIndex - 1);\n }\n }, [pageIndex, setPageIndex]);\n\n const firstPage = useCallback(() => {\n setPageIndex(0);\n }, [setPageIndex]);\n\n const lastPage = useCallback(\n (totalPages: number) => {\n setPageIndex(Math.max(0, totalPages - 1));\n },\n [setPageIndex]\n );\n\n // Reset function\n const reset = useCallback(() => {\n setRawPageIndex(defaultPageIndex);\n setRawPageSize(defaultPageSize);\n }, [setRawPageIndex, setRawPageSize, defaultPageIndex, defaultPageSize]);\n\n // Navigation state\n const canPreviousPage = pageIndex > 0;\n const canNextPage = useCallback(\n (totalPages?: number) => {\n if (!totalPages) return true; // Assume we can go next if total pages unknown\n return pageIndex < totalPages - 1;\n },\n [pageIndex]\n );\n\n return {\n pagination,\n setPagination,\n pageIndex,\n pageSize,\n setPageIndex,\n setPageSize,\n nextPage,\n previousPage,\n firstPage,\n lastPage,\n reset,\n canNextPage,\n canPreviousPage,\n };\n}\n","\"use client\";\n\nimport { useQueryState, parseAsString } from \"nuqs\";\nimport { useMemo, useCallback } from \"react\";\nimport type { SortingState } from \"@tanstack/react-table\";\n\nexport interface UseSortingOptions {\n /** Default sort column (default: undefined - no sorting) */\n defaultSortBy?: string;\n /** Default sort order (default: 'asc') */\n defaultSortOrder?: \"asc\" | \"desc\";\n /** Query param key for sort column (default: 'sortBy') */\n sortByKey?: string;\n /** Query param key for sort order (default: 'sortOrder') */\n sortOrderKey?: string;\n}\n\nexport interface UseSortingReturn {\n /** Current sorting state for TanStack Table */\n sorting: SortingState;\n /** Function to update sorting state */\n setSorting: (updater: SortingState | ((prev: SortingState) => SortingState)) => void;\n /** Current sort column (undefined if no sorting) */\n sortBy: string | undefined;\n /** Current sort order */\n sortOrder: \"asc\" | \"desc\";\n /** Set sort column and order directly */\n setSort: (column: string, order?: \"asc\" | \"desc\") => void;\n /** Clear all sorting */\n clearSort: () => void;\n /** Toggle sort order for a column */\n toggleSort: (column: string) => void;\n /** Check if a column is currently sorted */\n isSorted: (column: string) => boolean;\n /** Get sort order for a column */\n getSortOrder: (column: string) => \"asc\" | \"desc\" | undefined;\n}\n\n/**\n * Hook for managing server-side sorting state using nuqs.\n *\n * This hook synchronizes sorting state with URL query parameters,\n * making it perfect for server-side sorting where the URL should\n * reflect the current sort state.\n *\n * @example\n * ```tsx\n * function MyTable() {\n * const {\n * sorting,\n * setSorting,\n * sortBy,\n * sortOrder,\n * setSort,\n * toggleSort\n * } = useSorting({\n * defaultSortBy: 'name',\n * defaultSortOrder: 'asc'\n * });\n *\n * // Use with TanStack Table\n * const table = useReactTable({\n * data,\n * columns,\n * manualSorting: true,\n * sorting,\n * onSortingChange: setSorting,\n * });\n *\n * return <DataTable table={table} />;\n * }\n * ```\n */\nexport function useSorting(options: UseSortingOptions = {}): UseSortingReturn {\n const {\n defaultSortBy,\n defaultSortOrder = \"asc\",\n sortByKey = \"sortBy\",\n sortOrderKey = \"sortOrder\",\n } = options;\n\n // Use nuqs to manage query state\n const [sortBy, setSortBy] = useQueryState(\n sortByKey,\n parseAsString.withDefault(defaultSortBy || \"\")\n );\n\n const [sortOrder, setSortOrder] = useQueryState(\n sortOrderKey,\n parseAsString.withDefault(defaultSortOrder)\n );\n\n // Ensure sort order is valid\n const validSortOrder = useMemo(() => {\n return sortOrder === \"desc\" ? \"desc\" : \"asc\";\n }, [sortOrder]);\n\n // Create sorting state for TanStack Table\n const sorting = useMemo<SortingState>(() => {\n if (!sortBy || sortBy.trim() === \"\") {\n return [];\n }\n return [\n {\n id: sortBy,\n desc: validSortOrder === \"desc\",\n },\n ];\n }, [sortBy, validSortOrder]);\n\n // Set sorting function that handles both object and function updates\n const setSorting = useCallback(\n (updater: SortingState | ((prev: SortingState) => SortingState)) => {\n const newSorting = typeof updater === \"function\" ? updater(sorting) : updater;\n\n if (newSorting.length === 0) {\n // Clear sorting\n setSortBy(\"\");\n } else {\n // Set first sort (TanStack Table typically only has one sort at a time)\n const firstSort = newSorting[0];\n setSortBy(firstSort.id);\n setSortOrder(firstSort.desc ? \"desc\" : \"asc\");\n }\n },\n [sorting, setSortBy, setSortOrder]\n );\n\n // Set sort column and order directly\n const setSort = useCallback(\n (column: string, order: \"asc\" | \"desc\" = \"asc\") => {\n setSortBy(column);\n setSortOrder(order);\n },\n [setSortBy, setSortOrder]\n );\n\n // Clear all sorting\n const clearSort = useCallback(() => {\n setSortBy(null);\n }, [setSortBy]);\n\n // Toggle sort order for a column\n const toggleSort = useCallback(\n (column: string) => {\n if (sortBy === column) {\n // Same column, toggle order\n setSortOrder(validSortOrder === \"asc\" ? \"desc\" : \"asc\");\n } else {\n // Different column, set new column with asc\n setSortBy(column);\n setSortOrder(\"asc\");\n }\n },\n [sortBy, validSortOrder, setSortBy, setSortOrder]\n );\n\n // Check if a column is currently sorted\n const isSorted = useCallback(\n (column: string) => {\n return sortBy === column;\n },\n [sortBy]\n );\n\n // Get sort order for a column\n const getSortOrder = useCallback(\n (column: string) => {\n return sortBy === column ? validSortOrder : undefined;\n },\n [sortBy, validSortOrder]\n );\n\n return {\n sorting,\n setSorting,\n sortBy: sortBy || undefined,\n sortOrder: validSortOrder,\n setSort,\n clearSort,\n toggleSort,\n isSorted,\n getSortOrder,\n };\n}\n","\"use client\";\n\nimport { useMemo } from \"react\";\nimport { MultiSelect } from \"@turtleclub/ui\";\nimport { useSupportedChains } from \"@turtleclub/hooks\";\n\ninterface ChainSelectorProps {\n /** Selected chain IDs */\n value: string[];\n /** Callback when selection changes */\n onValueChange: (values: string[]) => void;\n /** Placeholder text */\n placeholder?: string;\n /** Whether the selector is disabled */\n disabled?: boolean;\n /** Custom className */\n className?: string;\n /** Maximum count to show in trigger */\n maxCount?: number;\n /** Whether to close on select */\n closeOnSelect?: boolean;\n /** Whether to show search */\n searchable?: boolean;\n}\n\nexport function ChainsSelector({\n value,\n onValueChange,\n placeholder = \"Select chains\",\n disabled = false,\n className,\n maxCount = 1,\n closeOnSelect = true,\n searchable = true,\n}: ChainSelectorProps) {\n const { chains, isLoading } = useSupportedChains();\n\n const chainOptions = useMemo(() => {\n if (!chains || chains.length === 0) return [];\n\n const filteredChains = chains.filter((chain) => chain.status === \"active\");\n\n return filteredChains.map((chain) => ({\n label: chain.name,\n value: chain?.id ?? \"\",\n icon: chain.logoUrl\n ? () => (\n <img\n src={chain.logoUrl}\n alt={chain.name}\n width={16}\n height={16}\n className=\"size-4 rounded-full\"\n />\n )\n : undefined,\n }));\n }, [chains]);\n\n return (\n <MultiSelect\n searchable={searchable}\n options={chainOptions}\n value={value}\n onValueChange={onValueChange}\n disabled={disabled || isLoading}\n placeholder={isLoading ? \"Loading chains...\" : placeholder}\n closeOnSelect={closeOnSelect}\n maxCount={maxCount}\n className={className}\n />\n );\n}\n","\"use client\";\n\nimport { useEffect, useMemo } from \"react\";\nimport { MultiSelect } from \"@turtleclub/ui\";\nimport { useSupportedTokens } from \"@turtleclub/hooks\";\n\ninterface TokenSelectorProps {\n /** Selected token IDs */\n value: string[];\n /** Callback when selection changes */\n onValueChange: (values: string[]) => void;\n /** Selected chain ID to filter tokens */\n chainId?: string;\n /** Placeholder text */\n placeholder?: string;\n /** Whether the selector is disabled */\n disabled?: boolean;\n /** Custom className */\n className?: string;\n /** Maximum count to show in trigger */\n maxCount?: number;\n /** Whether to close on select */\n closeOnSelect?: boolean;\n /** Whether to show search */\n searchable?: boolean;\n}\n\nexport function TokensSelector({\n value,\n onValueChange,\n chainId,\n placeholder = \"Select tokens\",\n disabled = false,\n className,\n maxCount = 1,\n closeOnSelect = true,\n searchable = true,\n}: TokenSelectorProps) {\n const isChainSelected = !!chainId && chainId.trim() !== \"\";\n\n const { tokens, isLoading } = useSupportedTokens({\n // chainId: isChainSelected ? chainId : undefined,\n limit: 100,\n enabled: isChainSelected,\n });\n\n const tokenOptions = useMemo(() => {\n if (!tokens) return [];\n\n return tokens.map((token) => ({\n label: `${token.symbol} - ${token.name}`,\n value: token?.id || \"\",\n icon: token.logoUrl\n ? () => (\n <img\n src={token.logoUrl}\n alt={token.name}\n width={16}\n height={16}\n className=\"size-4 rounded-full\"\n />\n )\n : undefined,\n }));\n }, [tokens]);\n\n // Clear selection when chain changes\n useEffect(() => {\n if (!isChainSelected && value.length > 0) {\n onValueChange([]);\n }\n }, [isChainSelected, value, onValueChange]);\n\n return (\n <MultiSelect\n searchable={searchable}\n options={tokenOptions}\n value={value}\n onValueChange={onValueChange}\n disabled={disabled || isLoading || !isChainSelected}\n placeholder={\n !isChainSelected ? \"Select a chain first\" : isLoading ? \"Loading tokens...\" : placeholder\n }\n closeOnSelect={closeOnSelect}\n maxCount={maxCount}\n className={className}\n />\n );\n}\n","\"use client\";\n\nimport { useMemo } from \"react\";\nimport { MultiSelect } from \"@turtleclub/ui\";\nimport { useProducts } from \"@turtleclub/hooks\";\n\ninterface ProductSelectorProps {\n /** Selected product IDs */\n value: string[];\n /** Callback when selection changes */\n onValueChange: (values: string[]) => void;\n /** Placeholder text */\n placeholder?: string;\n /** Whether the selector is disabled */\n disabled?: boolean;\n /** Custom className */\n className?: string;\n /** Maximum count to show in trigger */\n maxCount?: number;\n /** Whether to close on select */\n closeOnSelect?: boolean;\n /** Whether to show search */\n searchable?: boolean;\n}\n\nexport function ProductsSelector({\n value,\n onValueChange,\n placeholder = \"Select products\",\n disabled = false,\n className,\n maxCount = 1,\n closeOnSelect = true,\n searchable = true,\n}: ProductSelectorProps) {\n const { data: productsData, isLoading } = useProducts({});\n\n const products = useMemo(() => productsData?.products ?? [], [productsData?.products]);\n\n const productOptions = useMemo(() => {\n if (!products || products.length === 0) return [];\n\n return products.map((product) => ({\n label: product.name,\n value: product.id,\n icon: product.logoUrl\n ? () => (\n <img\n src={product.logoUrl}\n alt={product.name}\n width={16}\n height={16}\n className=\"size-4 rounded-full\"\n />\n )\n : undefined,\n }));\n }, [products]);\n\n return (\n <MultiSelect\n searchable={searchable}\n options={productOptions}\n value={value}\n onValueChange={onValueChange}\n disabled={disabled || isLoading}\n placeholder={isLoading ? \"Loading products...\" : placeholder}\n closeOnSelect={closeOnSelect}\n maxCount={maxCount}\n className={className}\n />\n );\n}\n","\"use client\";\n\nimport { useMemo } from \"react\";\nimport { MultiSelect } from \"@turtleclub/ui\";\nimport { useOpportunities } from \"@turtleclub/hooks\";\n\ninterface OpportunitySelectorProps {\n /** Selected opportunity IDs */\n value: string[];\n /** Callback when selection changes */\n onValueChange: (values: string[]) => void;\n /** Placeholder text */\n placeholder?: string;\n /** Whether the selector is disabled */\n disabled?: boolean;\n /** Custom className */\n className?: string;\n /** Maximum count to show in trigger */\n maxCount?: number;\n /** Whether to close on select */\n closeOnSelect?: boolean;\n /** Whether to show search */\n searchable?: boolean;\n}\n\nexport function OpportunitiesSelector({\n value,\n onValueChange,\n placeholder = \"Select opportunities\",\n disabled = false,\n className,\n maxCount = 1,\n closeOnSelect = true,\n searchable = true,\n}: OpportunitySelectorProps) {\n const { data: opportunitiesData, isLoading } = useOpportunities();\n\n const opportunities = useMemo(\n () => opportunitiesData?.opportunities ?? [],\n [opportunitiesData?.opportunities]\n );\n\n const opportunityOptions = useMemo(() => {\n if (!opportunities || opportunities.length === 0) return [];\n\n const filteredOpportunities = opportunities.filter((opp) => opp.status === \"active\");\n\n return filteredOpportunities.map((opportunity) => ({\n label: opportunity.name || opportunity.shortName,\n value: opportunity.id!,\n description: opportunity.shortName !== opportunity.name ? opportunity.shortName : undefined,\n icon: opportunity.depositTokens?.[0]?.logoUrl\n ? () => (\n <img\n src={opportunity.depositTokens[0].logoUrl}\n alt={opportunity.name}\n width={16}\n height={16}\n className=\"size-4 rounded-full\"\n />\n )\n : undefined,\n }));\n }, [opportunities]);\n\n return (\n <MultiSelect\n searchable={searchable}\n options={opportunityOptions}\n value={value}\n onValueChange={onValueChange}\n disabled={disabled || isLoading}\n placeholder={isLoading ? \"Loading opportunities...\" : placeholder}\n closeOnSelect={closeOnSelect}\n maxCount={maxCount}\n className={className}\n />\n );\n}\n","\"use client\";\n\nimport { useMemo } from \"react\";\nimport { Combobox } from \"@turtleclub/ui\";\nimport { useSupportedChains } from \"@turtleclub/hooks\";\n\ninterface ChainSelectorProps {\n /** Selected chain ID */\n value: string;\n /** Callback when selection changes */\n onValueChange: (value: string) => void;\n /** Placeholder text */\n placeholder?: string;\n /** Whether the selector is disabled */\n disabled?: boolean;\n /** Custom className */\n className?: string;\n /** Whether to close on select */\n closeOnSelect?: boolean;\n /** Whether to show search */\n searchable?: boolean;\n}\n\nexport function ChainSelector({\n value,\n onValueChange,\n placeholder = \"Select chain\",\n disabled = false,\n className,\n closeOnSelect = true,\n searchable = true,\n}: ChainSelectorProps) {\n const { chains, isLoading } = useSupportedChains();\n\n const chainOptions = useMemo(() => {\n if (!chains || chains.length === 0) return [];\n\n const filteredChains = chains.filter((chain) => chain.status === \"active\");\n\n return filteredChains.map((chain) => ({\n label: chain.name,\n value: chain?.id ?? \"\",\n icon: chain.logoUrl\n ? () => (\n <img\n src={chain.logoUrl}\n alt={chain.name}\n width={16}\n height={16}\n className=\"size-4 rounded-full\"\n />\n )\n : undefined,\n }));\n }, [chains]);\n\n return (\n <Combobox\n searchable={searchable}\n options={chainOptions}\n value={value}\n onValueChange={onValueChange}\n disabled={disabled || isLoading}\n placeholder={isLoading ? \"Loading chains...\" : placeholder}\n closeOnSelect={closeOnSelect}\n className={className}\n />\n );\n}\n","\"use client\";\n\nimport { useEffect, useMemo } from \"react\";\nimport { Combobox } from \"@turtleclub/ui\";\nimport { useSupportedTokens } from \"@turtleclub/hooks\";\n\ninterface TokenSelectorProps {\n /** Selected token ID */\n value: string;\n /** Callback when selection changes */\n onValueChange: (value: string) => void;\n /** Selected chain ID to filter tokens */\n chainId?: string;\n /** Placeholder text */\n placeholder?: string;\n /** Whether the selector is disabled */\n disabled?: boolean;\n /** Custom className */\n className?: string;\n /** Whether to close on select */\n closeOnSelect?: boolean;\n /** Whether to show search */\n searchable?: boolean;\n}\n\nexport function TokenSelector({\n value,\n onValueChange,\n chainId,\n placeholder = \"Select token\",\n disabled = false,\n className,\n closeOnSelect = true,\n searchable = true,\n}: TokenSelectorProps) {\n const isChainSelected = !!chainId && chainId.trim() !== \"\";\n\n const { tokens, isLoading } = useSupportedTokens({\n // chainId: isChainSelected ? chainId : undefined,\n limit: 100,\n enabled: isChainSelected,\n });\n\n const tokenOptions = useMemo(() => {\n if (!tokens) return [];\n\n return tokens.map((token) => ({\n label: `${token.symbol} - ${token.name}`,\n value: token?.id || \"\",\n icon: token.logoUrl\n ? () => (\n <img\n src={token.logoUrl}\n alt={token.name}\n width={16}\n height={16}\n className=\"size-4 rounded-full\"\n />\n )\n : undefined,\n }));\n }, [tokens]);\n\n // Clear selection when chain changes\n useEffect(() => {\n if (!isChainSelected && value) {\n onValueChange(\"\");\n }\n }, [isChainSelected, value, onValueChange]);\n\n return (\n <Combobox\n searchable={searchable}\n options={tokenOptions}\n value={value}\n onValueChange={onValueChange}\n disabled={disabled || isLoading || !isChainSelected}\n placeholder={\n !isChainSelected ? \"Select a chain first\" : isLoading ? \"Loading tokens...\" : placeholder\n }\n closeOnSelect={closeOnSelect}\n className={className}\n />\n );\n}\n","\"use client\";\n\nimport { useMemo } from \"react\";\nimport { Combobox } from \"@turtleclub/ui\";\nimport { useProducts } from \"@turtleclub/hooks\";\n\ninterface ProductSelectorProps {\n /** Selected product ID */\n value: string;\n /** Callback when selection changes */\n onValueChange: (value: string) => void;\n /** Placeholder text */\n placeholder?: string;\n /** Whether the selector is disabled */\n disabled?: boolean;\n /** Custom className */\n className?: string;\n /** Whether to close on select */\n closeOnSelect?: boolean;\n /** Whether to show search */\n searchable?: boolean;\n}\n\nexport function ProductSelector({\n value,\n onValueChange,\n placeholder = \"Select product\",\n disabled = false,\n className,\n closeOnSelect = true,\n searchable = true,\n}: ProductSelectorProps) {\n const { data: productsData, isLoading } = useProducts({});\n\n const products = useMemo(() => productsData?.products ?? [], [productsData?.products]);\n\n const productOptions = useMemo(() => {\n if (!products || products.length === 0) return [];\n\n return products.map((product) => ({\n label: product.name,\n value: product.id,\n icon: product.logoUrl\n ? () => (\n <img\n src={product.logoUrl}\n alt={product.name}\n width={16}\n height={16}\n className=\"size-4 rounded-full\"\n />\n )\n : undefined,\n }));\n }, [products]);\n\n return (\n <Combobox\n searchable={searchable}\n options={productOptions}\n value={value}\n onValueChange={onValueChange}\n disabled={disabled || isLoading}\n placeholder={isLoading ? \"Loading products...\" : placeholder}\n closeOnSelect={closeOnSelect}\n className={className}\n />\n );\n}\n","\"use client\";\n\nimport { useMemo } from \"react\";\nimport { Combobox } from \"@turtleclub/ui\";\nimport { useOpportunities } from \"@turtleclub/hooks\";\n\ninterface OpportunitySelectorProps {\n /** Selected opportunity ID */\n value: string;\n /** Callback when selection changes */\n onValueChange: (value: string) => void;\n /** Placeholder text */\n placeholder?: string;\n /** Whether the selector is disabled */\n disabled?: boolean;\n /** Custom className */\n className?: string;\n /** Whether to close on select */\n closeOnSelect?: boolean;\n /** Whether to show search */\n searchable?: boolean;\n}\n\nexport function OpportunitySelector({\n value,\n onValueChange,\n placeholder = \"Select opportunity\",\n disabled = false,\n className,\n closeOnSelect = true,\n searchable = true,\n}: OpportunitySelectorProps) {\n const { data: opportunitiesData, isLoading } = useOpportunities();\n\n const opportunities = useMemo(\n () => opportunitiesData?.opportunities ?? [],\n [opportunitiesData?.opportunities]\n );\n\n const opportunityOptions = useMemo(() => {\n if (!opportunities || opportunities.length === 0) return [];\n\n const filteredOpportunities = opportunities.filter((opp) => opp.status === \"active\");\n\n return filteredOpportunities.map((opportunity) => ({\n label: opportunity.name || opportunity.shortName,\n value: opportunity.id!,\n description: opportunity.shortName !== opportunity.name ? opportunity.shortName : undefined,\n icon: opportunity.depositTokens?.[0]?.logoUrl\n ? () => (\n <img\n src={opportunity.depositTokens[0].logoUrl}\n alt={opportunity.name}\n width={16}\n height={16}\n className=\"size-4 rounded-full\"\n />\n )\n : undefined,\n }));\n }, [opportunities]);\n\n return (\n <Combobox\n searchable={searchable}\n options={opportunityOptions}\n value={value}\n onValueChange={onValueChange}\n disabled={disabled || isLoading}\n placeholder={isLoading ? \"Loading opportunities...\" : placeholder}\n closeOnSelect={closeOnSelect}\n className={className}\n />\n );\n}\n"],"mappings":"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","names":["import_ui","import_nuqs","import_jsx_runtime","import_nuqs","import_jsx_runtime","import_nuqs","import_jsx_runtime","import_ui","import_jsx_runtime","import_ui","import_jsx_runtime","import_react","import_ui","import_lucide_react","import_jsx_runtime","import_ui","import_jsx_runtime","import_nuqs","import_react","import_nuqs","import_react","import_react","import_ui","import_jsx_runtime","import_react","import_ui","import_hooks","import_jsx_runtime","import_react","import_ui","import_hooks","import_jsx_runtime","import_react","import_ui","import_hooks","import_jsx_runtime","import_react","import_ui","import_hooks","import_jsx_runtime","import_react","import_ui","import_hooks","import_jsx_runtime","import_react","import_ui","import_hooks","import_jsx_runtime","import_react","import_ui","import_hooks","import_jsx_runtime"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/filters/RangeSliderFilter.tsx","../src/filters/BooleanFilter.tsx","../src/filters/Filter.tsx","../src/filters/MultiSelectFilter.tsx","../src/wrappers/FiltersGrid.tsx","../src/wrappers/FiltersPopover.tsx","../src/wrappers/ActiveFilterBadges.tsx","../src/wrappers/FiltersWrapper.tsx","../src/helpers/usePagination.ts","../src/helpers/useSorting.ts","../src/selectors/ChainsSelector.tsx","../src/selectors/TokensSelector.tsx","../src/selectors/ProductsSelector.tsx","../src/selectors/OpportunitiesSelector.tsx","../src/selectors/ChainSelector.tsx","../src/selectors/TokenSelector.tsx","../src/selectors/ProductSelector.tsx","../src/selectors/OpportunitySelector.tsx"],"sourcesContent":["export * from \"./filters\";\nexport * from \"./wrappers\";\nexport * from \"./helpers\";\nexport * from \"./selectors\";\n","\"use client\";\n\nimport React, { useEffect, useState } from \"react\";\nimport { Slider, Label } from \"@turtleclub/ui\";\nimport { useQueryState, parseAsInteger } from \"nuqs\";\n\ninterface RangeSliderFilterProps {\n /** Query key for minimum value (default: 'min') */\n minQueryKey?: string;\n /** Query key for maximum value (default: 'max') */\n maxQueryKey?: string;\n /** Minimum value (default: 0) */\n min?: number;\n /** Maximum value (default: 100) */\n max?: number;\n /** Step value for slider (default: 1) */\n step?: number;\n /** Whether the filter is disabled */\n disabled?: boolean;\n /** Custom label for the filter */\n label: string;\n /** Whether to show values (default: true) */\n showValues?: boolean;\n /** Value formatter function */\n formatValue?: (value: number) => string;\n /** Custom className for the container */\n className?: string;\n}\n\nconst defaultFormatter = (value: number): string => {\n return value.toLocaleString();\n};\n\nexport function RangeSliderFilter({\n minQueryKey = \"min\",\n maxQueryKey = \"max\",\n min = 0,\n max = 100,\n step = 1,\n disabled = false,\n label = \"Range\",\n showValues = true,\n formatValue = defaultFormatter,\n className = \"\",\n}: RangeSliderFilterProps) {\n const [minValue, setMinValue] = useQueryState(minQueryKey, parseAsInteger.withDefault(min));\n\n const [maxValue, setMaxValue] = useQueryState(maxQueryKey, parseAsInteger.withDefault(max));\n\n // Local state for slider values (to avoid excessive URL updates)\n const [localRange, setLocalRange] = useState([minValue, maxValue]);\n\n // Update local state when query params change\n useEffect(() => {\n setLocalRange([minValue, maxValue]);\n }, [minValue, maxValue]);\n\n const handleRangeChange = (values: number[]) => {\n setLocalRange(values);\n };\n\n const handleRangeCommit = (values: number[]) => {\n const [newMin, newMax] = values;\n if (newMin !== minValue) {\n setMinValue(newMin === min ? null : newMin);\n }\n if (newMax !== maxValue) {\n setMaxValue(newMax === max ? null : newMax);\n }\n };\n\n const isDefaultRange = localRange[0] === min && localRange[1] === max;\n\n return (\n <div className={`space-y-3 ${className}`}>\n <div className=\"flex items-center justify-between\">\n <Label className=\"text-sm font-medium\">{label}</Label>\n {showValues && (\n <div className=\"flex items-center gap-1 text-xs text-muted-foreground\">\n <span>{formatValue(localRange[0])}</span>\n <span>-</span>\n <span>{formatValue(localRange[1])}</span>\n </div>\n )}\n </div>\n\n <div className=\"px-2\">\n <Slider\n value={localRange}\n onValueChange={handleRangeChange}\n onValueCommit={handleRangeCommit}\n min={min}\n max={max}\n step={step}\n disabled={disabled}\n className=\"w-full\"\n />\n </div>\n\n {showValues && (\n <div className=\"flex justify-between text-xs text-muted-foreground\">\n <span>{formatValue(min)}</span>\n <span>{formatValue(max)}</span>\n </div>\n )}\n\n {!isDefaultRange && (\n <div className=\"flex justify-end\">\n <button\n onClick={() => {\n setLocalRange([min, max]);\n handleRangeCommit([min, max]);\n }}\n className=\"text-xs text-muted-foreground hover:text-foreground transition-colors\"\n disabled={disabled}\n >\n Reset\n </button>\n </div>\n )}\n </div>\n );\n}\n","\"use client\";\n\nimport React from \"react\";\nimport { Switch, Label } from \"@turtleclub/ui\";\nimport { useQueryState, parseAsBoolean } from \"nuqs\";\n\ninterface BooleanSwitchFilterProps {\n /** Query key for the boolean value (default: 'enabled') */\n queryKey?: string;\n /** Default value when not set (default: false) */\n defaultValue?: boolean;\n /** Whether the switch is disabled */\n disabled?: boolean;\n /** Label for the switch */\n label: string;\n /** Description text (optional) */\n description?: string;\n /** Custom className for the container */\n className?: string;\n}\n\nexport function BooleanFilter({\n queryKey = \"enabled\",\n defaultValue = false,\n disabled = false,\n label,\n description,\n className = \"\",\n}: BooleanSwitchFilterProps) {\n const [value, setValue] = useQueryState(queryKey, parseAsBoolean.withDefault(defaultValue));\n\n const handleToggle = (checked: boolean) => {\n // If the value is the same as default, remove from URL\n setValue(checked === defaultValue ? null : checked);\n };\n\n return (\n <div className={`flex items-center justify-between space-x-2 ${className}`}>\n <div className=\"space-y-0.5\">\n <Label className=\"text-sm font-medium\">{label}</Label>\n {description && <p className=\"text-xs text-muted-foreground\">{description}</p>}\n </div>\n <Switch checked={value} onCheckedChange={handleToggle} disabled={disabled} />\n </div>\n );\n}\n","\"use client\";\n\nimport React from \"react\";\nimport { useQueryState, parseAsString } from \"nuqs\";\n\ninterface FilterProps {\n /** Query parameter key */\n queryKey: string;\n /** Child component to render (selector) */\n children: (props: { value: string; onValueChange: (value: string) => void }) => React.ReactNode;\n /** Callback when value changes */\n onValueChange?: (value: string) => void;\n}\n\n/**\n * Generic Filter component that handles nuqs URL state management for single values.\n *\n * Provides value and onValueChange props to child components,\n * decoupling selector logic from URL state management.\n *\n * @example Basic Single Selection\n * ```tsx\n * // Single chain selection - persists to URL as ?chainId=ethereum\n * <Filter queryKey=\"chainId\">\n * {({ value, onValueChange }) => (\n * <ChainSelector\n * value={value}\n * onValueChange={onValueChange}\n * placeholder=\"Select chain\"\n * />\n * )}\n * </Filter>\n * ```\n */\nexport function Filter({ queryKey, children, onValueChange }: FilterProps) {\n const [value, setValue] = useQueryState(queryKey, parseAsString.withDefault(\"\"));\n\n const handleValueChange = (newValue: string) => {\n setValue(newValue || null);\n onValueChange?.(newValue);\n };\n\n return <>{children({ value, onValueChange: handleValueChange })}</>;\n}\n","\"use client\";\n\nimport React from \"react\";\nimport { useQueryState, parseAsArrayOf, parseAsString } from \"nuqs\";\n\ninterface MultiSelectFilterProps {\n /** Query parameter key */\n queryKey: string;\n /** Child component to render (selector) */\n children: (props: {\n value: string[];\n onValueChange: (values: string[]) => void;\n }) => React.ReactNode;\n /** Callback when values change */\n onValueChange?: (values: string[]) => void;\n}\n\n/**\n * Generic MultiSelectFilter component that handles nuqs URL state management for array values.\n *\n * Provides value and onValueChange props to child components,\n * decoupling selector logic from URL state management.\n *\n * @example\n * ```tsx\n * <MultiSelectFilter queryKey=\"chainId\">\n * {({ value, onValueChange }) => (\n * <ChainsSelector\n * value={value}\n * onValueChange={onValueChange}\n * placeholder=\"Select chains\"\n * />\n * )}\n * </MultiSelectFilter>\n * ```\n */\nexport function MultiSelectFilter({ queryKey, children, onValueChange }: MultiSelectFilterProps) {\n const [value, setValue] = useQueryState(queryKey, parseAsArrayOf(parseAsString).withDefault([]));\n\n const handleValueChange = (newValue: string[]) => {\n setValue(newValue.length > 0 ? newValue : null);\n onValueChange?.(newValue);\n };\n\n return <>{children({ value, onValueChange: handleValueChange })}</>;\n}\n","\"use client\";\n\nimport React from \"react\";\nimport { cn, Label } from \"@turtleclub/ui\";\n\nexport interface FilterConfig {\n key: string;\n label: string;\n component: React.ReactNode;\n enabled: boolean;\n section?: string;\n}\n\nexport interface FiltersGridProps {\n filters: FilterConfig[];\n columns?: number;\n className?: string;\n sectionClassName?: string;\n}\n\nexport function FiltersGrid({\n filters,\n columns = 3,\n className,\n sectionClassName,\n}: FiltersGridProps) {\n const enabledFilters = filters.filter((filter) => filter.enabled);\n\n if (enabledFilters.length === 0) {\n return null;\n }\n\n // Group filters by section\n const sections = enabledFilters.reduce(\n (acc, filter) => {\n const sectionName = filter.section || \"default\";\n if (!acc[sectionName]) {\n acc[sectionName] = [];\n }\n acc[sectionName].push(filter);\n return acc;\n },\n {} as Record<string, typeof enabledFilters>\n );\n\n const sectionNames = Object.keys(sections);\n\n return (\n <div className={cn(\"space-y-4\", className)}>\n {sectionNames.map((sectionName, sectionIndex) => (\n <div key={sectionName}>\n {sectionIndex > 0 && <div className=\"border-t border-border mb-4\" />}\n <div\n className={cn(\"grid gap-2\", sectionClassName)}\n style={{\n gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`,\n }}\n >\n {sections[sectionName].map((filter) => (\n <div key={filter.key} className=\"flex flex-col gap-2\">\n {filter.component}\n </div>\n ))}\n </div>\n </div>\n ))}\n </div>\n );\n}\n","\"use client\";\n\nimport React from \"react\";\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n buttonVariants,\n cn,\n Label,\n Badge,\n} from \"@turtleclub/ui\";\nimport { SlidersHorizontal } from \"lucide-react\";\nimport { FilterConfig } from \"./FiltersGrid\";\n\nexport interface FiltersPopoverProps {\n filters: FilterConfig[];\n triggerLabel?: string;\n columns?: number;\n className?: string;\n popoverClassName?: string;\n sectionClassName?: string;\n}\n\nexport function FiltersPopover({\n filters,\n triggerLabel = \"Filters\",\n columns = 2,\n className,\n popoverClassName,\n sectionClassName,\n}: FiltersPopoverProps) {\n const enabledFilters = filters.filter((filter) => filter.enabled);\n\n if (enabledFilters.length === 0) {\n return null;\n }\n\n // Group filters by section\n const sections = enabledFilters.reduce(\n (acc, filter) => {\n const sectionName = filter.section || \"default\";\n if (!acc[sectionName]) {\n acc[sectionName] = [];\n }\n acc[sectionName].push(filter);\n return acc;\n },\n {} as Record<string, typeof enabledFilters>\n );\n\n const sectionNames = Object.keys(sections);\n\n return (\n <Popover>\n <PopoverTrigger\n className={cn(\n buttonVariants({\n variant: \"default\",\n size: \"default\",\n border: \"bordered\",\n }),\n \"!bg-neutral-alpha-2\",\n className\n )}\n >\n <SlidersHorizontal className=\"mr-2 h-4 w-4\" />\n {triggerLabel}\n </PopoverTrigger>\n <PopoverContent\n className={cn(\"w-auto min-w-[400px] max-w-[600px]\", popoverClassName)}\n align=\"start\"\n >\n <div className=\"space-y-4\">\n <div className=\"flex items-center justify-between\">\n <h4 className=\"font-medium text-sm\">Filters</h4>\n </div>\n <div className=\"space-y-4\">\n {sectionNames.map((sectionName, sectionIndex) => (\n <div key={sectionName}>\n {sectionIndex > 0 && <div className=\"border-t border-border mb-4\" />}\n <div\n className={cn(\"grid gap-2\", sectionClassName)}\n style={{\n gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`,\n }}\n >\n {sections[sectionName].map((filter) => (\n <div key={filter.key} className=\"flex flex-col gap-2\">\n {filter.component}\n </div>\n ))}\n </div>\n </div>\n ))}\n </div>\n </div>\n </PopoverContent>\n </Popover>\n );\n}\n","\"use client\";\n\nimport React, { useMemo } from \"react\";\nimport { Badge, Button, cn } from \"@turtleclub/ui\";\nimport { X } from \"lucide-react\";\n\nexport interface ActiveFilterConfig {\n /** Unique key for the filter */\n key: string;\n /** Display label for the filter */\n label: string;\n /** Current filter value to display */\n value: string;\n /** Query parameter key(s) this filter uses */\n queryKeys: string | string[];\n /** Whether this filter is currently active */\n isActive: boolean;\n}\n\nexport interface ActiveFilterBadgesProps {\n /** Array of filter configurations */\n filters: ActiveFilterConfig[];\n /** Custom className for the container */\n className?: string;\n /** Label for clear all button (default: \"Clear All\") */\n clearAllLabel?: string;\n /** Whether to show clear all button (default: true) */\n showClearAll?: boolean;\n /** Custom render function for individual badges */\n renderBadge?: (filter: ActiveFilterConfig, clearFilter: () => void) => React.ReactNode;\n /** Callback function to clear individual filter */\n onClearFilter: (queryKeys: string | string[]) => void;\n /** Callback function to clear all filters */\n onClearAll: (allQueryKeys: (string | string[])[]) => void;\n}\n\n/**\n * Component that displays active filter badges with individual clear buttons\n * and an optional \"clear all\" button.\n *\n * @example\n * ```tsx\n * const activeFilters = [\n * {\n * key: 'chain',\n * label: 'Chain',\n * value: 'Ethereum',\n * queryKeys: 'chainId',\n * isActive: true,\n * },\n * {\n * key: 'sorting',\n * label: 'Sort',\n * value: 'Name (A-Z)',\n * queryKeys: ['sortBy', 'sortOrder'],\n * isActive: true,\n * },\n * ];\n *\n * <ActiveFilterBadges filters={activeFilters} />\n * ```\n */\nexport function ActiveFilterBadges({\n filters,\n className,\n clearAllLabel = \"Clear All\",\n showClearAll = true,\n renderBadge,\n onClearFilter,\n onClearAll,\n}: ActiveFilterBadgesProps) {\n // Get only active filters\n const activeFilters = useMemo(() => {\n return filters.filter((filter) => filter.isActive && filter.value);\n }, [filters]);\n\n // Clear all function\n const clearAll = () => {\n const allQueryKeys = activeFilters.map((filter) => filter.queryKeys);\n onClearAll(allQueryKeys);\n };\n\n // Don't render if no active filters\n if (activeFilters.length === 0) {\n return null;\n }\n\n return (\n <div className={cn(\"flex flex-wrap items-center gap-2\", className)}>\n {activeFilters.map((filter) => {\n return (\n <FilterBadge\n key={filter.key}\n filter={filter}\n renderBadge={renderBadge}\n onClearFilter={onClearFilter}\n />\n );\n })}\n\n {showClearAll && activeFilters.length > 1 && (\n <Button variant=\"ghost\" size=\"sm\" onClick={clearAll} className=\"h-7 px-2 text-xs\">\n {clearAllLabel}\n </Button>\n )}\n </div>\n );\n}\n\ninterface FilterBadgeProps {\n filter: ActiveFilterConfig;\n renderBadge?: (filter: ActiveFilterConfig, clearFilter: () => void) => React.ReactNode;\n onClearFilter: (queryKeys: string | string[]) => void;\n}\n\nfunction FilterBadge({ filter, renderBadge, onClearFilter }: FilterBadgeProps) {\n const clearFilter = () => {\n onClearFilter(filter.queryKeys);\n };\n\n if (renderBadge) {\n return <>{renderBadge(filter, clearFilter)}</>;\n }\n\n return (\n <Badge className=\"flex items-center gap-1 pr-1 border-border border\">\n <span className=\"text-xs\">\n {filter.label}: {filter.value}\n </span>\n <button\n onClick={clearFilter}\n className=\"ml-1 rounded-full p-0.5 hover:bg-black/10 focus:bg-black/10 focus:outline-none\"\n aria-label={`Clear ${filter.label} filter`}\n >\n <X className=\"h-3 w-3\" />\n </button>\n </Badge>\n );\n}\n","\"use client\";\n\nimport React from \"react\";\nimport { cn } from \"@turtleclub/ui\";\nimport { FiltersGrid } from \"./FiltersGrid\";\nimport { FiltersPopover } from \"./FiltersPopover\";\nimport { ActiveFilterBadges, ActiveFilterConfig } from \"./ActiveFilterBadges\";\nimport { FilterConfig } from \"./FiltersGrid\";\n\nexport interface FiltersWrapperProps {\n /** Array of filter configurations */\n filters: FilterConfig[];\n /** Array of active filter configurations for badges */\n activeFilters: ActiveFilterConfig[];\n /** Layout type: 'grid' or 'popover' */\n layout: \"grid\" | \"popover\";\n /** Custom className for the wrapper */\n className?: string;\n /** Custom className for the active badges section */\n badgesClassName?: string;\n /** Custom className for the slot container */\n slotClassName?: string;\n /** Optional content to render on the left side of filters */\n leftSlot?: React.ReactNode;\n /** Optional content to render on the right side of filters */\n rightSlot?: React.ReactNode;\n\n // Grid-specific props\n /** Number of columns for grid layout (default: 3) */\n columns?: number;\n\n // Popover-specific props\n /** Button label for popover trigger (default: \"Filters\") */\n triggerLabel?: string;\n /** Number of columns in popover (default: 2) */\n popoverColumns?: number;\n /** Custom className for popover trigger button */\n popoverClassName?: string;\n /** Custom className for popover content */\n popoverContentClassName?: string;\n\n // Active badges props\n /** Label for clear all button (default: \"Clear All\") */\n clearAllLabel?: string;\n /** Whether to show clear all button (default: true) */\n showClearAll?: boolean;\n /** Custom render function for individual badges */\n renderBadge?: (filter: ActiveFilterConfig, clearFilter: () => void) => React.ReactNode;\n /** Whether to show active filter badges (default: true) */\n showActiveBadges?: boolean;\n /** Callback function to clear individual filter */\n onClearFilter: (queryKeys: string | string[]) => void;\n /** Callback function to clear all filters */\n onClearAll: (allQueryKeys: (string | string[])[]) => void;\n}\n\n/**\n * Unified wrapper component that combines filter controls with active filter badges.\n *\n * Supports both grid and popover layouts, automatically displays active filter badges\n * below the filter controls with individual and bulk clear functionality.\n *\n * @example\n * ```tsx\n * const filters = [\n * {\n * key: 'chain',\n * label: 'Chain',\n * component: <ChainFilter />,\n * enabled: true,\n * },\n * ];\n *\n * const activeFilters = [\n * {\n * key: 'chain',\n * label: 'Chain',\n * value: 'Ethereum',\n * queryKeys: 'chainId',\n * isActive: true,\n * },\n * ];\n *\n * <FiltersWrapper\n * layout=\"grid\"\n * filters={filters}\n * activeFilters={activeFilters}\n * />\n * ```\n */\nexport function FiltersWrapper({\n filters,\n activeFilters,\n layout,\n className,\n badgesClassName,\n slotClassName,\n leftSlot,\n rightSlot,\n columns = 3,\n triggerLabel = \"Filters\",\n popoverColumns = 2,\n popoverClassName,\n popoverContentClassName,\n clearAllLabel = \"Clear All\",\n showClearAll = true,\n renderBadge,\n showActiveBadges = true,\n onClearFilter,\n onClearAll,\n}: FiltersWrapperProps) {\n const hasActiveFilters = activeFilters.some((filter) => filter.isActive && filter.value);\n\n return (\n <div className={cn(\"space-y-3\", className)}>\n {/* Filter Controls */}\n <div className={cn(\"flex items-center gap-2\", slotClassName)}>\n {leftSlot}\n <div className=\"flex-1\">\n {layout === \"grid\" ? (\n <FiltersGrid filters={filters} columns={columns} className={popoverClassName} />\n ) : (\n <FiltersPopover\n filters={filters}\n triggerLabel={triggerLabel}\n columns={popoverColumns}\n className={popoverClassName}\n popoverClassName={popoverContentClassName}\n />\n )}\n </div>\n {rightSlot}\n </div>\n\n {/* Active Filter Badges */}\n {showActiveBadges && hasActiveFilters && (\n <ActiveFilterBadges\n filters={activeFilters}\n className={badgesClassName}\n clearAllLabel={clearAllLabel}\n showClearAll={showClearAll}\n renderBadge={renderBadge}\n onClearFilter={onClearFilter}\n onClearAll={onClearAll}\n />\n )}\n </div>\n );\n}\n","\"use client\";\n\nimport { useQueryState, parseAsInteger } from \"nuqs\";\nimport { useMemo, useCallback } from \"react\";\nimport type { PaginationState } from \"@tanstack/react-table\";\n\nexport interface UsePaginationOptions {\n /** Default page size (default: 10) */\n defaultPageSize?: number;\n /** Default page index (default: 0) */\n defaultPageIndex?: number;\n /** Query param key for page index (default: 'page') */\n pageIndexKey?: string;\n /** Query param key for page size (default: 'pageSize') */\n pageSizeKey?: string;\n}\n\nexport interface UsePaginationReturn {\n /** Current pagination state for TanStack Table */\n pagination: PaginationState;\n /** Function to update pagination state */\n setPagination: (updater: PaginationState | ((prev: PaginationState) => PaginationState)) => void;\n /** Current page index (0-based) */\n pageIndex: number;\n /** Current page size */\n pageSize: number;\n /** Set page index directly */\n setPageIndex: (pageIndex: number) => void;\n /** Set page size directly */\n setPageSize: (pageSize: number) => void;\n /** Go to next page */\n nextPage: (totalPages?: number) => void;\n /** Go to previous page */\n previousPage: () => void;\n /** Go to first page */\n firstPage: () => void;\n /** Go to last page */\n lastPage: (totalPages: number) => void;\n /** Reset pagination to defaults */\n reset: () => void;\n /** Check if can go to next page */\n canNextPage: (totalPages?: number) => boolean;\n /** Check if can go to previous page */\n canPreviousPage: boolean;\n}\n\n/**\n * Hook for managing server-side pagination state using nuqs.\n *\n * This hook synchronizes pagination state with URL query parameters,\n * making it perfect for server-side pagination where the URL should\n * reflect the current page and page size.\n *\n * @example\n * ```tsx\n * function MyTable() {\n * const {\n * pagination,\n * setPagination,\n * pageIndex,\n * pageSize,\n * setPageIndex,\n * setPageSize\n * } = usePagination({\n * defaultPageSize: 20,\n * pageIndexKey: 'page',\n * pageSizeKey: 'limit'\n * });\n *\n * // Use with TanStack Table\n * const table = useReactTable({\n * data,\n * columns,\n * manualPagination: true,\n * pagination,\n * onPaginationChange: setPagination,\n * pageCount: Math.ceil(totalCount / pageSize),\n * });\n *\n * return <DataTable table={table} />;\n * }\n * ```\n */\nexport function usePagination(options: UsePaginationOptions = {}): UsePaginationReturn {\n const {\n defaultPageSize = 10,\n defaultPageIndex = 0,\n pageIndexKey = \"page\",\n pageSizeKey = \"pageSize\",\n } = options;\n\n // Use nuqs to manage query state\n const [rawPageIndex, setRawPageIndex] = useQueryState(\n pageIndexKey,\n parseAsInteger.withDefault(defaultPageIndex)\n );\n\n const [rawPageSize, setRawPageSize] = useQueryState(\n pageSizeKey,\n parseAsInteger.withDefault(defaultPageSize)\n );\n\n // Ensure page index is never negative\n const pageIndex = useMemo(() => Math.max(0, rawPageIndex), [rawPageIndex]);\n\n // Use page size directly from query state\n const pageSize = rawPageSize;\n\n // Create pagination state for TanStack Table\n const pagination = useMemo<PaginationState>(\n () => ({\n pageIndex,\n pageSize,\n }),\n [pageIndex, pageSize]\n );\n\n // Set pagination function that handles both object and function updates\n const setPagination = useCallback(\n (updater: PaginationState | ((prev: PaginationState) => PaginationState)) => {\n const newPagination = typeof updater === \"function\" ? updater(pagination) : updater;\n\n if (newPagination.pageIndex !== pageIndex) {\n setRawPageIndex(Math.max(0, newPagination.pageIndex));\n }\n\n if (newPagination.pageSize !== pageSize) {\n setRawPageSize(newPagination.pageSize);\n }\n },\n [pagination, pageIndex, pageSize, setRawPageIndex, setRawPageSize]\n );\n\n // Individual setters\n const setPageIndex = useCallback(\n (newPageIndex: number) => {\n setRawPageIndex(Math.max(0, newPageIndex));\n },\n [setRawPageIndex]\n );\n\n const setPageSize = useCallback(\n (newPageSize: number) => {\n setRawPageSize(newPageSize);\n // Reset to first page when page size changes\n setRawPageIndex(0);\n },\n [setRawPageSize, setRawPageIndex]\n );\n\n // Navigation functions\n const nextPage = useCallback(\n (totalPages?: number) => {\n const nextPageIndex = pageIndex + 1;\n if (!totalPages || nextPageIndex < totalPages) {\n setPageIndex(nextPageIndex);\n }\n },\n [pageIndex, setPageIndex]\n );\n\n const previousPage = useCallback(() => {\n if (pageIndex > 0) {\n setPageIndex(pageIndex - 1);\n }\n }, [pageIndex, setPageIndex]);\n\n const firstPage = useCallback(() => {\n setPageIndex(0);\n }, [setPageIndex]);\n\n const lastPage = useCallback(\n (totalPages: number) => {\n setPageIndex(Math.max(0, totalPages - 1));\n },\n [setPageIndex]\n );\n\n // Reset function\n const reset = useCallback(() => {\n setRawPageIndex(defaultPageIndex);\n setRawPageSize(defaultPageSize);\n }, [setRawPageIndex, setRawPageSize, defaultPageIndex, defaultPageSize]);\n\n // Navigation state\n const canPreviousPage = pageIndex > 0;\n const canNextPage = useCallback(\n (totalPages?: number) => {\n if (!totalPages) return true; // Assume we can go next if total pages unknown\n return pageIndex < totalPages - 1;\n },\n [pageIndex]\n );\n\n return {\n pagination,\n setPagination,\n pageIndex,\n pageSize,\n setPageIndex,\n setPageSize,\n nextPage,\n previousPage,\n firstPage,\n lastPage,\n reset,\n canNextPage,\n canPreviousPage,\n };\n}\n","\"use client\";\n\nimport { useQueryState, parseAsString } from \"nuqs\";\nimport { useMemo, useCallback } from \"react\";\nimport type { SortingState } from \"@tanstack/react-table\";\n\nexport interface UseSortingOptions {\n /** Default sort column (default: undefined - no sorting) */\n defaultSortBy?: string;\n /** Default sort order (default: 'asc') */\n defaultSortOrder?: \"asc\" | \"desc\";\n /** Query param key for sort column (default: 'sortBy') */\n sortByKey?: string;\n /** Query param key for sort order (default: 'sortOrder') */\n sortOrderKey?: string;\n}\n\nexport interface UseSortingReturn {\n /** Current sorting state for TanStack Table */\n sorting: SortingState;\n /** Function to update sorting state */\n setSorting: (updater: SortingState | ((prev: SortingState) => SortingState)) => void;\n /** Current sort column (undefined if no sorting) */\n sortBy: string | undefined;\n /** Current sort order */\n sortOrder: \"asc\" | \"desc\";\n /** Set sort column and order directly */\n setSort: (column: string, order?: \"asc\" | \"desc\") => void;\n /** Clear all sorting */\n clearSort: () => void;\n /** Toggle sort order for a column */\n toggleSort: (column: string) => void;\n /** Check if a column is currently sorted */\n isSorted: (column: string) => boolean;\n /** Get sort order for a column */\n getSortOrder: (column: string) => \"asc\" | \"desc\" | undefined;\n}\n\n/**\n * Hook for managing server-side sorting state using nuqs.\n *\n * This hook synchronizes sorting state with URL query parameters,\n * making it perfect for server-side sorting where the URL should\n * reflect the current sort state.\n *\n * @example\n * ```tsx\n * function MyTable() {\n * const {\n * sorting,\n * setSorting,\n * sortBy,\n * sortOrder,\n * setSort,\n * toggleSort\n * } = useSorting({\n * defaultSortBy: 'name',\n * defaultSortOrder: 'asc'\n * });\n *\n * // Use with TanStack Table\n * const table = useReactTable({\n * data,\n * columns,\n * manualSorting: true,\n * sorting,\n * onSortingChange: setSorting,\n * });\n *\n * return <DataTable table={table} />;\n * }\n * ```\n */\nexport function useSorting(options: UseSortingOptions = {}): UseSortingReturn {\n const {\n defaultSortBy,\n defaultSortOrder = \"asc\",\n sortByKey = \"sortBy\",\n sortOrderKey = \"sortOrder\",\n } = options;\n\n // Use nuqs to manage query state\n const [sortBy, setSortBy] = useQueryState(\n sortByKey,\n parseAsString.withDefault(defaultSortBy || \"\")\n );\n\n const [sortOrder, setSortOrder] = useQueryState(\n sortOrderKey,\n parseAsString.withDefault(defaultSortOrder)\n );\n\n // Ensure sort order is valid\n const validSortOrder = useMemo(() => {\n return sortOrder === \"desc\" ? \"desc\" : \"asc\";\n }, [sortOrder]);\n\n // Create sorting state for TanStack Table\n const sorting = useMemo<SortingState>(() => {\n if (!sortBy || sortBy.trim() === \"\") {\n return [];\n }\n return [\n {\n id: sortBy,\n desc: validSortOrder === \"desc\",\n },\n ];\n }, [sortBy, validSortOrder]);\n\n // Set sorting function that handles both object and function updates\n const setSorting = useCallback(\n (updater: SortingState | ((prev: SortingState) => SortingState)) => {\n const newSorting = typeof updater === \"function\" ? updater(sorting) : updater;\n\n if (newSorting.length === 0) {\n // Clear sorting\n setSortBy(\"\");\n } else {\n // Set first sort (TanStack Table typically only has one sort at a time)\n const firstSort = newSorting[0];\n setSortBy(firstSort.id);\n setSortOrder(firstSort.desc ? \"desc\" : \"asc\");\n }\n },\n [sorting, setSortBy, setSortOrder]\n );\n\n // Set sort column and order directly\n const setSort = useCallback(\n (column: string, order: \"asc\" | \"desc\" = \"asc\") => {\n setSortBy(column);\n setSortOrder(order);\n },\n [setSortBy, setSortOrder]\n );\n\n // Clear all sorting\n const clearSort = useCallback(() => {\n setSortBy(null);\n }, [setSortBy]);\n\n // Toggle sort order for a column\n const toggleSort = useCallback(\n (column: string) => {\n if (sortBy === column) {\n // Same column, toggle order\n setSortOrder(validSortOrder === \"asc\" ? \"desc\" : \"asc\");\n } else {\n // Different column, set new column with asc\n setSortBy(column);\n setSortOrder(\"asc\");\n }\n },\n [sortBy, validSortOrder, setSortBy, setSortOrder]\n );\n\n // Check if a column is currently sorted\n const isSorted = useCallback(\n (column: string) => {\n return sortBy === column;\n },\n [sortBy]\n );\n\n // Get sort order for a column\n const getSortOrder = useCallback(\n (column: string) => {\n return sortBy === column ? validSortOrder : undefined;\n },\n [sortBy, validSortOrder]\n );\n\n return {\n sorting,\n setSorting,\n sortBy: sortBy || undefined,\n sortOrder: validSortOrder,\n setSort,\n clearSort,\n toggleSort,\n isSorted,\n getSortOrder,\n };\n}\n","\"use client\";\n\nimport { useMemo } from \"react\";\nimport { MultiSelect } from \"@turtleclub/ui\";\nimport { useSupportedChains } from \"@turtleclub/hooks\";\n\ninterface ChainSelectorProps {\n /** Selected chain IDs */\n value: string[];\n /** Callback when selection changes */\n onValueChange: (values: string[]) => void;\n /** Placeholder text */\n placeholder?: string;\n /** Whether the selector is disabled */\n disabled?: boolean;\n /** Custom className */\n className?: string;\n /** Maximum count to show in trigger */\n maxCount?: number;\n /** Whether to close on select */\n closeOnSelect?: boolean;\n /** Whether to show search */\n searchable?: boolean;\n}\n\nexport function ChainsSelector({\n value,\n onValueChange,\n placeholder = \"Select chains\",\n disabled = false,\n className,\n maxCount = 1,\n closeOnSelect = true,\n searchable = true,\n}: ChainSelectorProps) {\n const { chains, isLoading } = useSupportedChains();\n\n const chainOptions = useMemo(() => {\n if (!chains || chains.length === 0) return [];\n\n const filteredChains = chains.filter((chain) => chain.status === \"active\");\n\n return filteredChains.map((chain) => ({\n label: chain.name,\n value: chain?.id ?? \"\",\n icon: chain.logoUrl\n ? () => (\n <img\n src={chain.logoUrl}\n alt={chain.name}\n width={16}\n height={16}\n className=\"size-4 rounded-full\"\n />\n )\n : undefined,\n }));\n }, [chains]);\n\n return (\n <MultiSelect\n searchable={searchable}\n options={chainOptions}\n value={value}\n onValueChange={onValueChange}\n disabled={disabled || isLoading}\n placeholder={isLoading ? \"Loading chains...\" : placeholder}\n closeOnSelect={closeOnSelect}\n maxCount={maxCount}\n className={className}\n />\n );\n}\n","\"use client\";\n\nimport { useEffect, useMemo } from \"react\";\nimport { MultiSelect } from \"@turtleclub/ui\";\nimport { useSupportedTokens } from \"@turtleclub/hooks\";\n\ninterface TokenSelectorProps {\n /** Selected token IDs */\n value: string[];\n /** Callback when selection changes */\n onValueChange: (values: string[]) => void;\n /** Selected chain ID to filter tokens */\n chainId?: string;\n /** Placeholder text */\n placeholder?: string;\n /** Whether the selector is disabled */\n disabled?: boolean;\n /** Custom className */\n className?: string;\n /** Maximum count to show in trigger */\n maxCount?: number;\n /** Whether to close on select */\n closeOnSelect?: boolean;\n /** Whether to show search */\n searchable?: boolean;\n}\n\nexport function TokensSelector({\n value,\n onValueChange,\n chainId,\n placeholder = \"Select tokens\",\n disabled = false,\n className,\n maxCount = 1,\n closeOnSelect = true,\n searchable = true,\n}: TokenSelectorProps) {\n const isChainSelected = !!chainId && chainId.trim() !== \"\";\n\n const { tokens, isLoading } = useSupportedTokens({\n // chainId: isChainSelected ? chainId : undefined,\n limit: 100,\n enabled: isChainSelected,\n });\n\n const tokenOptions = useMemo(() => {\n if (!tokens) return [];\n\n return tokens.map((token) => ({\n label: `${token.symbol} - ${token.name}`,\n value: token?.id || \"\",\n icon: token.logoUrl\n ? () => (\n <img\n src={token.logoUrl}\n alt={token.name}\n width={16}\n height={16}\n className=\"size-4 rounded-full\"\n />\n )\n : undefined,\n }));\n }, [tokens]);\n\n // Clear selection when chain changes\n useEffect(() => {\n if (!isChainSelected && value.length > 0) {\n onValueChange([]);\n }\n }, [isChainSelected, value, onValueChange]);\n\n return (\n <MultiSelect\n searchable={searchable}\n options={tokenOptions}\n value={value}\n onValueChange={onValueChange}\n disabled={disabled || isLoading || !isChainSelected}\n placeholder={\n !isChainSelected ? \"Select a chain first\" : isLoading ? \"Loading tokens...\" : placeholder\n }\n closeOnSelect={closeOnSelect}\n maxCount={maxCount}\n className={className}\n />\n );\n}\n","\"use client\";\n\nimport { useMemo } from \"react\";\nimport { MultiSelect } from \"@turtleclub/ui\";\nimport { useProducts } from \"@turtleclub/hooks\";\n\ninterface ProductSelectorProps {\n /** Selected product IDs */\n value: string[];\n /** Callback when selection changes */\n onValueChange: (values: string[]) => void;\n /** Placeholder text */\n placeholder?: string;\n /** Whether the selector is disabled */\n disabled?: boolean;\n /** Custom className */\n className?: string;\n /** Maximum count to show in trigger */\n maxCount?: number;\n /** Whether to close on select */\n closeOnSelect?: boolean;\n /** Whether to show search */\n searchable?: boolean;\n}\n\nexport function ProductsSelector({\n value,\n onValueChange,\n placeholder = \"Select products\",\n disabled = false,\n className,\n maxCount = 1,\n closeOnSelect = true,\n searchable = true,\n}: ProductSelectorProps) {\n const { data: productsData, isLoading } = useProducts({});\n\n const products = useMemo(() => productsData?.products ?? [], [productsData?.products]);\n\n const productOptions = useMemo(() => {\n if (!products || products.length === 0) return [];\n\n return products.map((product) => ({\n label: product.name,\n value: product.id,\n icon: product.logoUrl\n ? () => (\n <img\n src={product.logoUrl}\n alt={product.name}\n width={16}\n height={16}\n className=\"size-4 rounded-full\"\n />\n )\n : undefined,\n }));\n }, [products]);\n\n return (\n <MultiSelect\n searchable={searchable}\n options={productOptions}\n value={value}\n onValueChange={onValueChange}\n disabled={disabled || isLoading}\n placeholder={isLoading ? \"Loading products...\" : placeholder}\n closeOnSelect={closeOnSelect}\n maxCount={maxCount}\n className={className}\n />\n );\n}\n","\"use client\";\n\nimport { useMemo } from \"react\";\nimport { MultiSelect } from \"@turtleclub/ui\";\nimport { useOpportunities } from \"@turtleclub/hooks\";\n\ninterface OpportunitySelectorProps {\n /** Selected opportunity IDs */\n value: string[];\n /** Callback when selection changes */\n onValueChange: (values: string[]) => void;\n /** Placeholder text */\n placeholder?: string;\n /** Whether the selector is disabled */\n disabled?: boolean;\n /** Custom className */\n className?: string;\n /** Maximum count to show in trigger */\n maxCount?: number;\n /** Whether to close on select */\n closeOnSelect?: boolean;\n /** Whether to show search */\n searchable?: boolean;\n}\n\nexport function OpportunitiesSelector({\n value,\n onValueChange,\n placeholder = \"Select opportunities\",\n disabled = false,\n className,\n maxCount = 1,\n closeOnSelect = true,\n searchable = true,\n}: OpportunitySelectorProps) {\n const { data: opportunitiesData, isLoading } = useOpportunities();\n\n const opportunities = useMemo(\n () => opportunitiesData?.opportunities ?? [],\n [opportunitiesData?.opportunities]\n );\n\n const opportunityOptions = useMemo(() => {\n if (!opportunities || opportunities.length === 0) return [];\n\n const filteredOpportunities = opportunities.filter((opp) => opp.status === \"active\");\n\n return filteredOpportunities.map((opportunity) => ({\n label: opportunity.name || opportunity.shortName,\n value: opportunity.id!,\n description: opportunity.shortName !== opportunity.name ? opportunity.shortName : undefined,\n icon: opportunity.depositTokens?.[0]?.logoUrl\n ? () => (\n <img\n src={opportunity.depositTokens[0].logoUrl}\n alt={opportunity.name}\n width={16}\n height={16}\n className=\"size-4 rounded-full\"\n />\n )\n : undefined,\n }));\n }, [opportunities]);\n\n return (\n <MultiSelect\n searchable={searchable}\n options={opportunityOptions}\n value={value}\n onValueChange={onValueChange}\n disabled={disabled || isLoading}\n placeholder={isLoading ? \"Loading opportunities...\" : placeholder}\n closeOnSelect={closeOnSelect}\n maxCount={maxCount}\n className={className}\n />\n );\n}\n","\"use client\";\n\nimport { useMemo } from \"react\";\nimport { Combobox } from \"@turtleclub/ui\";\nimport { useSupportedChains } from \"@turtleclub/hooks\";\n\ninterface ChainSelectorProps {\n /** Selected chain ID */\n value: string;\n /** Callback when selection changes */\n onValueChange: (value: string) => void;\n /** Placeholder text */\n placeholder?: string;\n /** Whether the selector is disabled */\n disabled?: boolean;\n /** Custom className */\n className?: string;\n /** Whether to close on select */\n closeOnSelect?: boolean;\n /** Whether to show search */\n searchable?: boolean;\n}\n\nexport function ChainSelector({\n value,\n onValueChange,\n placeholder = \"Select chain\",\n disabled = false,\n className,\n closeOnSelect = true,\n searchable = true,\n}: ChainSelectorProps) {\n const { chains, isLoading } = useSupportedChains();\n\n const chainOptions = useMemo(() => {\n if (!chains || chains.length === 0) return [];\n\n const filteredChains = chains.filter((chain) => chain.status === \"active\");\n\n return filteredChains.map((chain) => ({\n label: chain.name,\n value: chain?.id ?? \"\",\n icon: chain.logoUrl\n ? () => (\n <img\n src={chain.logoUrl}\n alt={chain.name}\n width={16}\n height={16}\n className=\"size-4 rounded-full\"\n />\n )\n : undefined,\n }));\n }, [chains]);\n\n return (\n <Combobox\n searchable={searchable}\n options={chainOptions}\n value={value}\n onValueChange={onValueChange}\n disabled={disabled || isLoading}\n placeholder={isLoading ? \"Loading chains...\" : placeholder}\n closeOnSelect={closeOnSelect}\n className={className}\n />\n );\n}\n","\"use client\";\n\nimport { useEffect, useMemo } from \"react\";\nimport { Combobox } from \"@turtleclub/ui\";\nimport { useSupportedTokens } from \"@turtleclub/hooks\";\n\ninterface TokenSelectorProps {\n /** Selected token ID */\n value: string;\n /** Callback when selection changes */\n onValueChange: (value: string) => void;\n /** Selected chain ID to filter tokens */\n chainId?: string;\n /** Placeholder text */\n placeholder?: string;\n /** Whether the selector is disabled */\n disabled?: boolean;\n /** Custom className */\n className?: string;\n /** Whether to close on select */\n closeOnSelect?: boolean;\n /** Whether to show search */\n searchable?: boolean;\n}\n\nexport function TokenSelector({\n value,\n onValueChange,\n chainId,\n placeholder = \"Select token\",\n disabled = false,\n className,\n closeOnSelect = true,\n searchable = true,\n}: TokenSelectorProps) {\n const isChainSelected = !!chainId && chainId.trim() !== \"\";\n\n const { tokens, isLoading } = useSupportedTokens({\n // chainId: isChainSelected ? chainId : undefined,\n limit: 100,\n enabled: isChainSelected,\n });\n\n const tokenOptions = useMemo(() => {\n if (!tokens) return [];\n\n return tokens.map((token) => ({\n label: `${token.symbol} - ${token.name}`,\n value: token?.id || \"\",\n icon: token.logoUrl\n ? () => (\n <img\n src={token.logoUrl}\n alt={token.name}\n width={16}\n height={16}\n className=\"size-4 rounded-full\"\n />\n )\n : undefined,\n }));\n }, [tokens]);\n\n // Clear selection when chain changes\n useEffect(() => {\n if (!isChainSelected && value) {\n onValueChange(\"\");\n }\n }, [isChainSelected, value, onValueChange]);\n\n return (\n <Combobox\n searchable={searchable}\n options={tokenOptions}\n value={value}\n onValueChange={onValueChange}\n disabled={disabled || isLoading || !isChainSelected}\n placeholder={\n !isChainSelected ? \"Select a chain first\" : isLoading ? \"Loading tokens...\" : placeholder\n }\n closeOnSelect={closeOnSelect}\n className={className}\n />\n );\n}\n","\"use client\";\n\nimport { useMemo } from \"react\";\nimport { Combobox } from \"@turtleclub/ui\";\nimport { useProducts } from \"@turtleclub/hooks\";\n\ninterface ProductSelectorProps {\n /** Selected product ID */\n value: string;\n /** Callback when selection changes */\n onValueChange: (value: string) => void;\n /** Placeholder text */\n placeholder?: string;\n /** Whether the selector is disabled */\n disabled?: boolean;\n /** Custom className */\n className?: string;\n /** Whether to close on select */\n closeOnSelect?: boolean;\n /** Whether to show search */\n searchable?: boolean;\n}\n\nexport function ProductSelector({\n value,\n onValueChange,\n placeholder = \"Select product\",\n disabled = false,\n className,\n closeOnSelect = true,\n searchable = true,\n}: ProductSelectorProps) {\n const { data: productsData, isLoading } = useProducts({});\n\n const products = useMemo(() => productsData?.products ?? [], [productsData?.products]);\n\n const productOptions = useMemo(() => {\n if (!products || products.length === 0) return [];\n\n return products.map((product) => ({\n label: product.name,\n value: product.id,\n icon: product.logoUrl\n ? () => (\n <img\n src={product.logoUrl}\n alt={product.name}\n width={16}\n height={16}\n className=\"size-4 rounded-full\"\n />\n )\n : undefined,\n }));\n }, [products]);\n\n return (\n <Combobox\n searchable={searchable}\n options={productOptions}\n value={value}\n onValueChange={onValueChange}\n disabled={disabled || isLoading}\n placeholder={isLoading ? \"Loading products...\" : placeholder}\n closeOnSelect={closeOnSelect}\n className={className}\n />\n );\n}\n","\"use client\";\n\nimport { useMemo } from \"react\";\nimport { Combobox } from \"@turtleclub/ui\";\nimport { useOpportunities } from \"@turtleclub/hooks\";\n\ninterface OpportunitySelectorProps {\n /** Selected opportunity ID */\n value: string;\n /** Callback when selection changes */\n onValueChange: (value: string) => void;\n /** Placeholder text */\n placeholder?: string;\n /** Whether the selector is disabled */\n disabled?: boolean;\n /** Custom className */\n className?: string;\n /** Whether to close on select */\n closeOnSelect?: boolean;\n /** Whether to show search */\n searchable?: boolean;\n}\n\nexport function OpportunitySelector({\n value,\n onValueChange,\n placeholder = \"Select opportunity\",\n disabled = false,\n className,\n closeOnSelect = true,\n searchable = true,\n}: OpportunitySelectorProps) {\n const { data: opportunitiesData, isLoading } = useOpportunities();\n\n const opportunities = useMemo(\n () => opportunitiesData?.opportunities ?? [],\n [opportunitiesData?.opportunities]\n );\n\n const opportunityOptions = useMemo(() => {\n if (!opportunities || opportunities.length === 0) return [];\n\n const filteredOpportunities = opportunities.filter((opp) => opp.status === \"active\");\n\n return filteredOpportunities.map((opportunity) => ({\n label: opportunity.name || opportunity.shortName,\n value: opportunity.id!,\n description: opportunity.shortName !== opportunity.name ? opportunity.shortName : undefined,\n icon: opportunity.depositTokens?.[0]?.logoUrl\n ? () => (\n <img\n src={opportunity.depositTokens[0].logoUrl}\n alt={opportunity.name}\n width={16}\n height={16}\n className=\"size-4 rounded-full\"\n />\n )\n : undefined,\n }));\n }, [opportunities]);\n\n return (\n <Combobox\n searchable={searchable}\n options={opportunityOptions}\n value={value}\n onValueChange={onValueChange}\n disabled={disabled || isLoading}\n placeholder={isLoading ? \"Loading opportunities...\" : placeholder}\n closeOnSelect={closeOnSelect}\n className={className}\n />\n );\n}\n"],"mappings":"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","names":["import_ui","import_nuqs","import_jsx_runtime","import_nuqs","import_jsx_runtime","import_nuqs","import_jsx_runtime","import_ui","import_jsx_runtime","import_ui","import_jsx_runtime","import_react","import_ui","import_lucide_react","import_jsx_runtime","import_ui","import_jsx_runtime","import_nuqs","import_react","import_nuqs","import_react","import_react","import_ui","import_jsx_runtime","import_react","import_ui","import_hooks","import_jsx_runtime","import_react","import_ui","import_hooks","import_jsx_runtime","import_react","import_ui","import_hooks","import_jsx_runtime","import_react","import_ui","import_hooks","import_jsx_runtime","import_react","import_ui","import_hooks","import_jsx_runtime","import_react","import_ui","import_hooks","import_jsx_runtime","import_react","import_ui","import_hooks","import_jsx_runtime"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -199,6 +199,12 @@ interface FiltersWrapperProps {
|
|
|
199
199
|
className?: string;
|
|
200
200
|
/** Custom className for the active badges section */
|
|
201
201
|
badgesClassName?: string;
|
|
202
|
+
/** Custom className for the slot container */
|
|
203
|
+
slotClassName?: string;
|
|
204
|
+
/** Optional content to render on the left side of filters */
|
|
205
|
+
leftSlot?: React.ReactNode;
|
|
206
|
+
/** Optional content to render on the right side of filters */
|
|
207
|
+
rightSlot?: React.ReactNode;
|
|
202
208
|
/** Number of columns for grid layout (default: 3) */
|
|
203
209
|
columns?: number;
|
|
204
210
|
/** Button label for popover trigger (default: "Filters") */
|
|
@@ -256,7 +262,7 @@ interface FiltersWrapperProps {
|
|
|
256
262
|
* />
|
|
257
263
|
* ```
|
|
258
264
|
*/
|
|
259
|
-
declare function FiltersWrapper({ filters, activeFilters, layout, className, badgesClassName, columns, triggerLabel, popoverColumns, popoverClassName, popoverContentClassName, clearAllLabel, showClearAll, renderBadge, showActiveBadges, onClearFilter, onClearAll, }: FiltersWrapperProps): react_jsx_runtime.JSX.Element;
|
|
265
|
+
declare function FiltersWrapper({ filters, activeFilters, layout, className, badgesClassName, slotClassName, leftSlot, rightSlot, columns, triggerLabel, popoverColumns, popoverClassName, popoverContentClassName, clearAllLabel, showClearAll, renderBadge, showActiveBadges, onClearFilter, onClearAll, }: FiltersWrapperProps): react_jsx_runtime.JSX.Element;
|
|
260
266
|
|
|
261
267
|
interface UsePaginationOptions {
|
|
262
268
|
/** Default page size (default: 10) */
|
package/dist/index.d.ts
CHANGED
|
@@ -199,6 +199,12 @@ interface FiltersWrapperProps {
|
|
|
199
199
|
className?: string;
|
|
200
200
|
/** Custom className for the active badges section */
|
|
201
201
|
badgesClassName?: string;
|
|
202
|
+
/** Custom className for the slot container */
|
|
203
|
+
slotClassName?: string;
|
|
204
|
+
/** Optional content to render on the left side of filters */
|
|
205
|
+
leftSlot?: React.ReactNode;
|
|
206
|
+
/** Optional content to render on the right side of filters */
|
|
207
|
+
rightSlot?: React.ReactNode;
|
|
202
208
|
/** Number of columns for grid layout (default: 3) */
|
|
203
209
|
columns?: number;
|
|
204
210
|
/** Button label for popover trigger (default: "Filters") */
|
|
@@ -256,7 +262,7 @@ interface FiltersWrapperProps {
|
|
|
256
262
|
* />
|
|
257
263
|
* ```
|
|
258
264
|
*/
|
|
259
|
-
declare function FiltersWrapper({ filters, activeFilters, layout, className, badgesClassName, columns, triggerLabel, popoverColumns, popoverClassName, popoverContentClassName, clearAllLabel, showClearAll, renderBadge, showActiveBadges, onClearFilter, onClearAll, }: FiltersWrapperProps): react_jsx_runtime.JSX.Element;
|
|
265
|
+
declare function FiltersWrapper({ filters, activeFilters, layout, className, badgesClassName, slotClassName, leftSlot, rightSlot, columns, triggerLabel, popoverColumns, popoverClassName, popoverContentClassName, clearAllLabel, showClearAll, renderBadge, showActiveBadges, onClearFilter, onClearAll, }: FiltersWrapperProps): react_jsx_runtime.JSX.Element;
|
|
260
266
|
|
|
261
267
|
interface UsePaginationOptions {
|
|
262
268
|
/** Default page size (default: 10) */
|
package/dist/index.js
CHANGED
|
@@ -168,7 +168,13 @@ function FiltersGrid({
|
|
|
168
168
|
}
|
|
169
169
|
|
|
170
170
|
// src/wrappers/FiltersPopover.tsx
|
|
171
|
-
import {
|
|
171
|
+
import {
|
|
172
|
+
Popover,
|
|
173
|
+
PopoverContent,
|
|
174
|
+
PopoverTrigger,
|
|
175
|
+
buttonVariants,
|
|
176
|
+
cn as cn2
|
|
177
|
+
} from "@turtleclub/ui";
|
|
172
178
|
import { SlidersHorizontal } from "lucide-react";
|
|
173
179
|
import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
174
180
|
function FiltersPopover({
|
|
@@ -196,10 +202,24 @@ function FiltersPopover({
|
|
|
196
202
|
);
|
|
197
203
|
const sectionNames = Object.keys(sections);
|
|
198
204
|
return /* @__PURE__ */ jsxs4(Popover, { children: [
|
|
199
|
-
/* @__PURE__ */
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
205
|
+
/* @__PURE__ */ jsxs4(
|
|
206
|
+
PopoverTrigger,
|
|
207
|
+
{
|
|
208
|
+
className: cn2(
|
|
209
|
+
buttonVariants({
|
|
210
|
+
variant: "default",
|
|
211
|
+
size: "default",
|
|
212
|
+
border: "bordered"
|
|
213
|
+
}),
|
|
214
|
+
"!bg-neutral-alpha-2",
|
|
215
|
+
className
|
|
216
|
+
),
|
|
217
|
+
children: [
|
|
218
|
+
/* @__PURE__ */ jsx6(SlidersHorizontal, { className: "mr-2 h-4 w-4" }),
|
|
219
|
+
triggerLabel
|
|
220
|
+
]
|
|
221
|
+
}
|
|
222
|
+
),
|
|
203
223
|
/* @__PURE__ */ jsx6(
|
|
204
224
|
PopoverContent,
|
|
205
225
|
{
|
|
@@ -228,7 +248,7 @@ function FiltersPopover({
|
|
|
228
248
|
|
|
229
249
|
// src/wrappers/ActiveFilterBadges.tsx
|
|
230
250
|
import { useMemo } from "react";
|
|
231
|
-
import { Badge as Badge2, Button
|
|
251
|
+
import { Badge as Badge2, Button, cn as cn3 } from "@turtleclub/ui";
|
|
232
252
|
import { X } from "lucide-react";
|
|
233
253
|
import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
234
254
|
function ActiveFilterBadges({
|
|
@@ -262,7 +282,7 @@ function ActiveFilterBadges({
|
|
|
262
282
|
filter.key
|
|
263
283
|
);
|
|
264
284
|
}),
|
|
265
|
-
showClearAll && activeFilters.length > 1 && /* @__PURE__ */ jsx7(
|
|
285
|
+
showClearAll && activeFilters.length > 1 && /* @__PURE__ */ jsx7(Button, { variant: "ghost", size: "sm", onClick: clearAll, className: "h-7 px-2 text-xs", children: clearAllLabel })
|
|
266
286
|
] });
|
|
267
287
|
}
|
|
268
288
|
function FilterBadge({ filter, renderBadge, onClearFilter }) {
|
|
@@ -299,6 +319,9 @@ function FiltersWrapper({
|
|
|
299
319
|
layout,
|
|
300
320
|
className,
|
|
301
321
|
badgesClassName,
|
|
322
|
+
slotClassName,
|
|
323
|
+
leftSlot,
|
|
324
|
+
rightSlot,
|
|
302
325
|
columns = 3,
|
|
303
326
|
triggerLabel = "Filters",
|
|
304
327
|
popoverColumns = 2,
|
|
@@ -313,16 +336,20 @@ function FiltersWrapper({
|
|
|
313
336
|
}) {
|
|
314
337
|
const hasActiveFilters = activeFilters.some((filter) => filter.isActive && filter.value);
|
|
315
338
|
return /* @__PURE__ */ jsxs6("div", { className: cn4("space-y-3", className), children: [
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
{
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
339
|
+
/* @__PURE__ */ jsxs6("div", { className: cn4("flex items-center gap-2", slotClassName), children: [
|
|
340
|
+
leftSlot,
|
|
341
|
+
/* @__PURE__ */ jsx8("div", { className: "flex-1", children: layout === "grid" ? /* @__PURE__ */ jsx8(FiltersGrid, { filters, columns, className: popoverClassName }) : /* @__PURE__ */ jsx8(
|
|
342
|
+
FiltersPopover,
|
|
343
|
+
{
|
|
344
|
+
filters,
|
|
345
|
+
triggerLabel,
|
|
346
|
+
columns: popoverColumns,
|
|
347
|
+
className: popoverClassName,
|
|
348
|
+
popoverClassName: popoverContentClassName
|
|
349
|
+
}
|
|
350
|
+
) }),
|
|
351
|
+
rightSlot
|
|
352
|
+
] }),
|
|
326
353
|
showActiveBadges && hasActiveFilters && /* @__PURE__ */ jsx8(
|
|
327
354
|
ActiveFilterBadges,
|
|
328
355
|
{
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/filters/RangeSliderFilter.tsx","../src/filters/BooleanFilter.tsx","../src/filters/Filter.tsx","../src/filters/MultiSelectFilter.tsx","../src/wrappers/FiltersGrid.tsx","../src/wrappers/FiltersPopover.tsx","../src/wrappers/ActiveFilterBadges.tsx","../src/wrappers/FiltersWrapper.tsx","../src/helpers/usePagination.ts","../src/helpers/useSorting.ts","../src/selectors/ChainsSelector.tsx","../src/selectors/TokensSelector.tsx","../src/selectors/ProductsSelector.tsx","../src/selectors/OpportunitiesSelector.tsx","../src/selectors/ChainSelector.tsx","../src/selectors/TokenSelector.tsx","../src/selectors/ProductSelector.tsx","../src/selectors/OpportunitySelector.tsx"],"sourcesContent":["\"use client\";\n\nimport React, { useEffect, useState } from \"react\";\nimport { Slider, Label } from \"@turtleclub/ui\";\nimport { useQueryState, parseAsInteger } from \"nuqs\";\n\ninterface RangeSliderFilterProps {\n /** Query key for minimum value (default: 'min') */\n minQueryKey?: string;\n /** Query key for maximum value (default: 'max') */\n maxQueryKey?: string;\n /** Minimum value (default: 0) */\n min?: number;\n /** Maximum value (default: 100) */\n max?: number;\n /** Step value for slider (default: 1) */\n step?: number;\n /** Whether the filter is disabled */\n disabled?: boolean;\n /** Custom label for the filter */\n label: string;\n /** Whether to show values (default: true) */\n showValues?: boolean;\n /** Value formatter function */\n formatValue?: (value: number) => string;\n /** Custom className for the container */\n className?: string;\n}\n\nconst defaultFormatter = (value: number): string => {\n return value.toLocaleString();\n};\n\nexport function RangeSliderFilter({\n minQueryKey = \"min\",\n maxQueryKey = \"max\",\n min = 0,\n max = 100,\n step = 1,\n disabled = false,\n label = \"Range\",\n showValues = true,\n formatValue = defaultFormatter,\n className = \"\",\n}: RangeSliderFilterProps) {\n const [minValue, setMinValue] = useQueryState(minQueryKey, parseAsInteger.withDefault(min));\n\n const [maxValue, setMaxValue] = useQueryState(maxQueryKey, parseAsInteger.withDefault(max));\n\n // Local state for slider values (to avoid excessive URL updates)\n const [localRange, setLocalRange] = useState([minValue, maxValue]);\n\n // Update local state when query params change\n useEffect(() => {\n setLocalRange([minValue, maxValue]);\n }, [minValue, maxValue]);\n\n const handleRangeChange = (values: number[]) => {\n setLocalRange(values);\n };\n\n const handleRangeCommit = (values: number[]) => {\n const [newMin, newMax] = values;\n if (newMin !== minValue) {\n setMinValue(newMin === min ? null : newMin);\n }\n if (newMax !== maxValue) {\n setMaxValue(newMax === max ? null : newMax);\n }\n };\n\n const isDefaultRange = localRange[0] === min && localRange[1] === max;\n\n return (\n <div className={`space-y-3 ${className}`}>\n <div className=\"flex items-center justify-between\">\n <Label className=\"text-sm font-medium\">{label}</Label>\n {showValues && (\n <div className=\"flex items-center gap-1 text-xs text-muted-foreground\">\n <span>{formatValue(localRange[0])}</span>\n <span>-</span>\n <span>{formatValue(localRange[1])}</span>\n </div>\n )}\n </div>\n\n <div className=\"px-2\">\n <Slider\n value={localRange}\n onValueChange={handleRangeChange}\n onValueCommit={handleRangeCommit}\n min={min}\n max={max}\n step={step}\n disabled={disabled}\n className=\"w-full\"\n />\n </div>\n\n {showValues && (\n <div className=\"flex justify-between text-xs text-muted-foreground\">\n <span>{formatValue(min)}</span>\n <span>{formatValue(max)}</span>\n </div>\n )}\n\n {!isDefaultRange && (\n <div className=\"flex justify-end\">\n <button\n onClick={() => {\n setLocalRange([min, max]);\n handleRangeCommit([min, max]);\n }}\n className=\"text-xs text-muted-foreground hover:text-foreground transition-colors\"\n disabled={disabled}\n >\n Reset\n </button>\n </div>\n )}\n </div>\n );\n}\n","\"use client\";\n\nimport React from \"react\";\nimport { Switch, Label } from \"@turtleclub/ui\";\nimport { useQueryState, parseAsBoolean } from \"nuqs\";\n\ninterface BooleanSwitchFilterProps {\n /** Query key for the boolean value (default: 'enabled') */\n queryKey?: string;\n /** Default value when not set (default: false) */\n defaultValue?: boolean;\n /** Whether the switch is disabled */\n disabled?: boolean;\n /** Label for the switch */\n label: string;\n /** Description text (optional) */\n description?: string;\n /** Custom className for the container */\n className?: string;\n}\n\nexport function BooleanFilter({\n queryKey = \"enabled\",\n defaultValue = false,\n disabled = false,\n label,\n description,\n className = \"\",\n}: BooleanSwitchFilterProps) {\n const [value, setValue] = useQueryState(queryKey, parseAsBoolean.withDefault(defaultValue));\n\n const handleToggle = (checked: boolean) => {\n // If the value is the same as default, remove from URL\n setValue(checked === defaultValue ? null : checked);\n };\n\n return (\n <div className={`flex items-center justify-between space-x-2 ${className}`}>\n <div className=\"space-y-0.5\">\n <Label className=\"text-sm font-medium\">{label}</Label>\n {description && <p className=\"text-xs text-muted-foreground\">{description}</p>}\n </div>\n <Switch checked={value} onCheckedChange={handleToggle} disabled={disabled} />\n </div>\n );\n}\n","\"use client\";\n\nimport React from \"react\";\nimport { useQueryState, parseAsString } from \"nuqs\";\n\ninterface FilterProps {\n /** Query parameter key */\n queryKey: string;\n /** Child component to render (selector) */\n children: (props: { value: string; onValueChange: (value: string) => void }) => React.ReactNode;\n /** Callback when value changes */\n onValueChange?: (value: string) => void;\n}\n\n/**\n * Generic Filter component that handles nuqs URL state management for single values.\n *\n * Provides value and onValueChange props to child components,\n * decoupling selector logic from URL state management.\n *\n * @example Basic Single Selection\n * ```tsx\n * // Single chain selection - persists to URL as ?chainId=ethereum\n * <Filter queryKey=\"chainId\">\n * {({ value, onValueChange }) => (\n * <ChainSelector\n * value={value}\n * onValueChange={onValueChange}\n * placeholder=\"Select chain\"\n * />\n * )}\n * </Filter>\n * ```\n */\nexport function Filter({ queryKey, children, onValueChange }: FilterProps) {\n const [value, setValue] = useQueryState(queryKey, parseAsString.withDefault(\"\"));\n\n const handleValueChange = (newValue: string) => {\n setValue(newValue || null);\n onValueChange?.(newValue);\n };\n\n return <>{children({ value, onValueChange: handleValueChange })}</>;\n}\n","\"use client\";\n\nimport React from \"react\";\nimport { useQueryState, parseAsArrayOf, parseAsString } from \"nuqs\";\n\ninterface MultiSelectFilterProps {\n /** Query parameter key */\n queryKey: string;\n /** Child component to render (selector) */\n children: (props: {\n value: string[];\n onValueChange: (values: string[]) => void;\n }) => React.ReactNode;\n /** Callback when values change */\n onValueChange?: (values: string[]) => void;\n}\n\n/**\n * Generic MultiSelectFilter component that handles nuqs URL state management for array values.\n *\n * Provides value and onValueChange props to child components,\n * decoupling selector logic from URL state management.\n *\n * @example\n * ```tsx\n * <MultiSelectFilter queryKey=\"chainId\">\n * {({ value, onValueChange }) => (\n * <ChainsSelector\n * value={value}\n * onValueChange={onValueChange}\n * placeholder=\"Select chains\"\n * />\n * )}\n * </MultiSelectFilter>\n * ```\n */\nexport function MultiSelectFilter({ queryKey, children, onValueChange }: MultiSelectFilterProps) {\n const [value, setValue] = useQueryState(queryKey, parseAsArrayOf(parseAsString).withDefault([]));\n\n const handleValueChange = (newValue: string[]) => {\n setValue(newValue.length > 0 ? newValue : null);\n onValueChange?.(newValue);\n };\n\n return <>{children({ value, onValueChange: handleValueChange })}</>;\n}\n","\"use client\";\n\nimport React from \"react\";\nimport { cn, Label } from \"@turtleclub/ui\";\n\nexport interface FilterConfig {\n key: string;\n label: string;\n component: React.ReactNode;\n enabled: boolean;\n section?: string;\n}\n\nexport interface FiltersGridProps {\n filters: FilterConfig[];\n columns?: number;\n className?: string;\n sectionClassName?: string;\n}\n\nexport function FiltersGrid({\n filters,\n columns = 3,\n className,\n sectionClassName,\n}: FiltersGridProps) {\n const enabledFilters = filters.filter((filter) => filter.enabled);\n\n if (enabledFilters.length === 0) {\n return null;\n }\n\n // Group filters by section\n const sections = enabledFilters.reduce(\n (acc, filter) => {\n const sectionName = filter.section || \"default\";\n if (!acc[sectionName]) {\n acc[sectionName] = [];\n }\n acc[sectionName].push(filter);\n return acc;\n },\n {} as Record<string, typeof enabledFilters>\n );\n\n const sectionNames = Object.keys(sections);\n\n return (\n <div className={cn(\"space-y-4\", className)}>\n {sectionNames.map((sectionName, sectionIndex) => (\n <div key={sectionName}>\n {sectionIndex > 0 && <div className=\"border-t border-border mb-4\" />}\n <div\n className={cn(\"grid gap-2\", sectionClassName)}\n style={{\n gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`,\n }}\n >\n {sections[sectionName].map((filter) => (\n <div key={filter.key} className=\"flex flex-col gap-2\">\n {filter.component}\n </div>\n ))}\n </div>\n </div>\n ))}\n </div>\n );\n}\n","\"use client\";\n\nimport React from \"react\";\nimport { Popover, PopoverContent, PopoverTrigger, Button, cn, Label, Badge } from \"@turtleclub/ui\";\nimport { SlidersHorizontal } from \"lucide-react\";\nimport { FilterConfig } from \"./FiltersGrid\";\n\nexport interface FiltersPopoverProps {\n filters: FilterConfig[];\n triggerLabel?: string;\n columns?: number;\n className?: string;\n popoverClassName?: string;\n sectionClassName?: string;\n}\n\nexport function FiltersPopover({\n filters,\n triggerLabel = \"Filters\",\n columns = 2,\n className,\n popoverClassName,\n sectionClassName,\n}: FiltersPopoverProps) {\n const enabledFilters = filters.filter((filter) => filter.enabled);\n\n if (enabledFilters.length === 0) {\n return null;\n }\n\n // Group filters by section\n const sections = enabledFilters.reduce(\n (acc, filter) => {\n const sectionName = filter.section || \"default\";\n if (!acc[sectionName]) {\n acc[sectionName] = [];\n }\n acc[sectionName].push(filter);\n return acc;\n },\n {} as Record<string, typeof enabledFilters>\n );\n\n const sectionNames = Object.keys(sections);\n\n return (\n <Popover>\n <PopoverTrigger asChild>\n <Button variant=\"outline\" className={cn(className)}>\n <SlidersHorizontal className=\"mr-2 h-4 w-4\" />\n {triggerLabel}\n </Button>\n </PopoverTrigger>\n <PopoverContent\n className={cn(\"w-auto min-w-[400px] max-w-[600px]\", popoverClassName)}\n align=\"start\"\n >\n <div className=\"space-y-4\">\n <div className=\"flex items-center justify-between\">\n <h4 className=\"font-medium text-sm\">Filters</h4>\n </div>\n <div className=\"space-y-4\">\n {sectionNames.map((sectionName, sectionIndex) => (\n <div key={sectionName}>\n {sectionIndex > 0 && <div className=\"border-t border-border mb-4\" />}\n <div\n className={cn(\"grid gap-2\", sectionClassName)}\n style={{\n gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`,\n }}\n >\n {sections[sectionName].map((filter) => (\n <div key={filter.key} className=\"flex flex-col gap-2\">\n {filter.component}\n </div>\n ))}\n </div>\n </div>\n ))}\n </div>\n </div>\n </PopoverContent>\n </Popover>\n );\n}\n","\"use client\";\n\nimport React, { useMemo } from \"react\";\nimport { Badge, Button, cn } from \"@turtleclub/ui\";\nimport { X } from \"lucide-react\";\n\nexport interface ActiveFilterConfig {\n /** Unique key for the filter */\n key: string;\n /** Display label for the filter */\n label: string;\n /** Current filter value to display */\n value: string;\n /** Query parameter key(s) this filter uses */\n queryKeys: string | string[];\n /** Whether this filter is currently active */\n isActive: boolean;\n}\n\nexport interface ActiveFilterBadgesProps {\n /** Array of filter configurations */\n filters: ActiveFilterConfig[];\n /** Custom className for the container */\n className?: string;\n /** Label for clear all button (default: \"Clear All\") */\n clearAllLabel?: string;\n /** Whether to show clear all button (default: true) */\n showClearAll?: boolean;\n /** Custom render function for individual badges */\n renderBadge?: (filter: ActiveFilterConfig, clearFilter: () => void) => React.ReactNode;\n /** Callback function to clear individual filter */\n onClearFilter: (queryKeys: string | string[]) => void;\n /** Callback function to clear all filters */\n onClearAll: (allQueryKeys: (string | string[])[]) => void;\n}\n\n/**\n * Component that displays active filter badges with individual clear buttons\n * and an optional \"clear all\" button.\n *\n * @example\n * ```tsx\n * const activeFilters = [\n * {\n * key: 'chain',\n * label: 'Chain',\n * value: 'Ethereum',\n * queryKeys: 'chainId',\n * isActive: true,\n * },\n * {\n * key: 'sorting',\n * label: 'Sort',\n * value: 'Name (A-Z)',\n * queryKeys: ['sortBy', 'sortOrder'],\n * isActive: true,\n * },\n * ];\n *\n * <ActiveFilterBadges filters={activeFilters} />\n * ```\n */\nexport function ActiveFilterBadges({\n filters,\n className,\n clearAllLabel = \"Clear All\",\n showClearAll = true,\n renderBadge,\n onClearFilter,\n onClearAll,\n}: ActiveFilterBadgesProps) {\n // Get only active filters\n const activeFilters = useMemo(() => {\n return filters.filter((filter) => filter.isActive && filter.value);\n }, [filters]);\n\n // Clear all function\n const clearAll = () => {\n const allQueryKeys = activeFilters.map((filter) => filter.queryKeys);\n onClearAll(allQueryKeys);\n };\n\n // Don't render if no active filters\n if (activeFilters.length === 0) {\n return null;\n }\n\n return (\n <div className={cn(\"flex flex-wrap items-center gap-2\", className)}>\n {activeFilters.map((filter) => {\n return (\n <FilterBadge\n key={filter.key}\n filter={filter}\n renderBadge={renderBadge}\n onClearFilter={onClearFilter}\n />\n );\n })}\n\n {showClearAll && activeFilters.length > 1 && (\n <Button variant=\"ghost\" size=\"sm\" onClick={clearAll} className=\"h-7 px-2 text-xs\">\n {clearAllLabel}\n </Button>\n )}\n </div>\n );\n}\n\ninterface FilterBadgeProps {\n filter: ActiveFilterConfig;\n renderBadge?: (filter: ActiveFilterConfig, clearFilter: () => void) => React.ReactNode;\n onClearFilter: (queryKeys: string | string[]) => void;\n}\n\nfunction FilterBadge({ filter, renderBadge, onClearFilter }: FilterBadgeProps) {\n const clearFilter = () => {\n onClearFilter(filter.queryKeys);\n };\n\n if (renderBadge) {\n return <>{renderBadge(filter, clearFilter)}</>;\n }\n\n return (\n <Badge className=\"flex items-center gap-1 pr-1 border-border border\">\n <span className=\"text-xs\">\n {filter.label}: {filter.value}\n </span>\n <button\n onClick={clearFilter}\n className=\"ml-1 rounded-full p-0.5 hover:bg-black/10 focus:bg-black/10 focus:outline-none\"\n aria-label={`Clear ${filter.label} filter`}\n >\n <X className=\"h-3 w-3\" />\n </button>\n </Badge>\n );\n}\n","\"use client\";\n\nimport React from \"react\";\nimport { cn } from \"@turtleclub/ui\";\nimport { FiltersGrid } from \"./FiltersGrid\";\nimport { FiltersPopover } from \"./FiltersPopover\";\nimport { ActiveFilterBadges, ActiveFilterConfig } from \"./ActiveFilterBadges\";\nimport { FilterConfig } from \"./FiltersGrid\";\n\nexport interface FiltersWrapperProps {\n /** Array of filter configurations */\n filters: FilterConfig[];\n /** Array of active filter configurations for badges */\n activeFilters: ActiveFilterConfig[];\n /** Layout type: 'grid' or 'popover' */\n layout: \"grid\" | \"popover\";\n /** Custom className for the wrapper */\n className?: string;\n /** Custom className for the active badges section */\n badgesClassName?: string;\n\n // Grid-specific props\n /** Number of columns for grid layout (default: 3) */\n columns?: number;\n\n // Popover-specific props\n /** Button label for popover trigger (default: \"Filters\") */\n triggerLabel?: string;\n /** Number of columns in popover (default: 2) */\n popoverColumns?: number;\n /** Custom className for popover trigger button */\n popoverClassName?: string;\n /** Custom className for popover content */\n popoverContentClassName?: string;\n\n // Active badges props\n /** Label for clear all button (default: \"Clear All\") */\n clearAllLabel?: string;\n /** Whether to show clear all button (default: true) */\n showClearAll?: boolean;\n /** Custom render function for individual badges */\n renderBadge?: (filter: ActiveFilterConfig, clearFilter: () => void) => React.ReactNode;\n /** Whether to show active filter badges (default: true) */\n showActiveBadges?: boolean;\n /** Callback function to clear individual filter */\n onClearFilter: (queryKeys: string | string[]) => void;\n /** Callback function to clear all filters */\n onClearAll: (allQueryKeys: (string | string[])[]) => void;\n}\n\n/**\n * Unified wrapper component that combines filter controls with active filter badges.\n *\n * Supports both grid and popover layouts, automatically displays active filter badges\n * below the filter controls with individual and bulk clear functionality.\n *\n * @example\n * ```tsx\n * const filters = [\n * {\n * key: 'chain',\n * label: 'Chain',\n * component: <ChainFilter />,\n * enabled: true,\n * },\n * ];\n *\n * const activeFilters = [\n * {\n * key: 'chain',\n * label: 'Chain',\n * value: 'Ethereum',\n * queryKeys: 'chainId',\n * isActive: true,\n * },\n * ];\n *\n * <FiltersWrapper\n * layout=\"grid\"\n * filters={filters}\n * activeFilters={activeFilters}\n * />\n * ```\n */\nexport function FiltersWrapper({\n filters,\n activeFilters,\n layout,\n className,\n badgesClassName,\n columns = 3,\n triggerLabel = \"Filters\",\n popoverColumns = 2,\n popoverClassName,\n popoverContentClassName,\n clearAllLabel = \"Clear All\",\n showClearAll = true,\n renderBadge,\n showActiveBadges = true,\n onClearFilter,\n onClearAll,\n}: FiltersWrapperProps) {\n const hasActiveFilters = activeFilters.some((filter) => filter.isActive && filter.value);\n\n return (\n <div className={cn(\"space-y-3\", className)}>\n {/* Filter Controls */}\n {layout === \"grid\" ? (\n <FiltersGrid filters={filters} columns={columns} className={popoverClassName} />\n ) : (\n <FiltersPopover\n filters={filters}\n triggerLabel={triggerLabel}\n columns={popoverColumns}\n className={popoverClassName}\n popoverClassName={popoverContentClassName}\n />\n )}\n\n {/* Active Filter Badges */}\n {showActiveBadges && hasActiveFilters && (\n <ActiveFilterBadges\n filters={activeFilters}\n className={badgesClassName}\n clearAllLabel={clearAllLabel}\n showClearAll={showClearAll}\n renderBadge={renderBadge}\n onClearFilter={onClearFilter}\n onClearAll={onClearAll}\n />\n )}\n </div>\n );\n}\n","\"use client\";\n\nimport { useQueryState, parseAsInteger } from \"nuqs\";\nimport { useMemo, useCallback } from \"react\";\nimport type { PaginationState } from \"@tanstack/react-table\";\n\nexport interface UsePaginationOptions {\n /** Default page size (default: 10) */\n defaultPageSize?: number;\n /** Default page index (default: 0) */\n defaultPageIndex?: number;\n /** Query param key for page index (default: 'page') */\n pageIndexKey?: string;\n /** Query param key for page size (default: 'pageSize') */\n pageSizeKey?: string;\n}\n\nexport interface UsePaginationReturn {\n /** Current pagination state for TanStack Table */\n pagination: PaginationState;\n /** Function to update pagination state */\n setPagination: (updater: PaginationState | ((prev: PaginationState) => PaginationState)) => void;\n /** Current page index (0-based) */\n pageIndex: number;\n /** Current page size */\n pageSize: number;\n /** Set page index directly */\n setPageIndex: (pageIndex: number) => void;\n /** Set page size directly */\n setPageSize: (pageSize: number) => void;\n /** Go to next page */\n nextPage: (totalPages?: number) => void;\n /** Go to previous page */\n previousPage: () => void;\n /** Go to first page */\n firstPage: () => void;\n /** Go to last page */\n lastPage: (totalPages: number) => void;\n /** Reset pagination to defaults */\n reset: () => void;\n /** Check if can go to next page */\n canNextPage: (totalPages?: number) => boolean;\n /** Check if can go to previous page */\n canPreviousPage: boolean;\n}\n\n/**\n * Hook for managing server-side pagination state using nuqs.\n *\n * This hook synchronizes pagination state with URL query parameters,\n * making it perfect for server-side pagination where the URL should\n * reflect the current page and page size.\n *\n * @example\n * ```tsx\n * function MyTable() {\n * const {\n * pagination,\n * setPagination,\n * pageIndex,\n * pageSize,\n * setPageIndex,\n * setPageSize\n * } = usePagination({\n * defaultPageSize: 20,\n * pageIndexKey: 'page',\n * pageSizeKey: 'limit'\n * });\n *\n * // Use with TanStack Table\n * const table = useReactTable({\n * data,\n * columns,\n * manualPagination: true,\n * pagination,\n * onPaginationChange: setPagination,\n * pageCount: Math.ceil(totalCount / pageSize),\n * });\n *\n * return <DataTable table={table} />;\n * }\n * ```\n */\nexport function usePagination(options: UsePaginationOptions = {}): UsePaginationReturn {\n const {\n defaultPageSize = 10,\n defaultPageIndex = 0,\n pageIndexKey = \"page\",\n pageSizeKey = \"pageSize\",\n } = options;\n\n // Use nuqs to manage query state\n const [rawPageIndex, setRawPageIndex] = useQueryState(\n pageIndexKey,\n parseAsInteger.withDefault(defaultPageIndex)\n );\n\n const [rawPageSize, setRawPageSize] = useQueryState(\n pageSizeKey,\n parseAsInteger.withDefault(defaultPageSize)\n );\n\n // Ensure page index is never negative\n const pageIndex = useMemo(() => Math.max(0, rawPageIndex), [rawPageIndex]);\n\n // Use page size directly from query state\n const pageSize = rawPageSize;\n\n // Create pagination state for TanStack Table\n const pagination = useMemo<PaginationState>(\n () => ({\n pageIndex,\n pageSize,\n }),\n [pageIndex, pageSize]\n );\n\n // Set pagination function that handles both object and function updates\n const setPagination = useCallback(\n (updater: PaginationState | ((prev: PaginationState) => PaginationState)) => {\n const newPagination = typeof updater === \"function\" ? updater(pagination) : updater;\n\n if (newPagination.pageIndex !== pageIndex) {\n setRawPageIndex(Math.max(0, newPagination.pageIndex));\n }\n\n if (newPagination.pageSize !== pageSize) {\n setRawPageSize(newPagination.pageSize);\n }\n },\n [pagination, pageIndex, pageSize, setRawPageIndex, setRawPageSize]\n );\n\n // Individual setters\n const setPageIndex = useCallback(\n (newPageIndex: number) => {\n setRawPageIndex(Math.max(0, newPageIndex));\n },\n [setRawPageIndex]\n );\n\n const setPageSize = useCallback(\n (newPageSize: number) => {\n setRawPageSize(newPageSize);\n // Reset to first page when page size changes\n setRawPageIndex(0);\n },\n [setRawPageSize, setRawPageIndex]\n );\n\n // Navigation functions\n const nextPage = useCallback(\n (totalPages?: number) => {\n const nextPageIndex = pageIndex + 1;\n if (!totalPages || nextPageIndex < totalPages) {\n setPageIndex(nextPageIndex);\n }\n },\n [pageIndex, setPageIndex]\n );\n\n const previousPage = useCallback(() => {\n if (pageIndex > 0) {\n setPageIndex(pageIndex - 1);\n }\n }, [pageIndex, setPageIndex]);\n\n const firstPage = useCallback(() => {\n setPageIndex(0);\n }, [setPageIndex]);\n\n const lastPage = useCallback(\n (totalPages: number) => {\n setPageIndex(Math.max(0, totalPages - 1));\n },\n [setPageIndex]\n );\n\n // Reset function\n const reset = useCallback(() => {\n setRawPageIndex(defaultPageIndex);\n setRawPageSize(defaultPageSize);\n }, [setRawPageIndex, setRawPageSize, defaultPageIndex, defaultPageSize]);\n\n // Navigation state\n const canPreviousPage = pageIndex > 0;\n const canNextPage = useCallback(\n (totalPages?: number) => {\n if (!totalPages) return true; // Assume we can go next if total pages unknown\n return pageIndex < totalPages - 1;\n },\n [pageIndex]\n );\n\n return {\n pagination,\n setPagination,\n pageIndex,\n pageSize,\n setPageIndex,\n setPageSize,\n nextPage,\n previousPage,\n firstPage,\n lastPage,\n reset,\n canNextPage,\n canPreviousPage,\n };\n}\n","\"use client\";\n\nimport { useQueryState, parseAsString } from \"nuqs\";\nimport { useMemo, useCallback } from \"react\";\nimport type { SortingState } from \"@tanstack/react-table\";\n\nexport interface UseSortingOptions {\n /** Default sort column (default: undefined - no sorting) */\n defaultSortBy?: string;\n /** Default sort order (default: 'asc') */\n defaultSortOrder?: \"asc\" | \"desc\";\n /** Query param key for sort column (default: 'sortBy') */\n sortByKey?: string;\n /** Query param key for sort order (default: 'sortOrder') */\n sortOrderKey?: string;\n}\n\nexport interface UseSortingReturn {\n /** Current sorting state for TanStack Table */\n sorting: SortingState;\n /** Function to update sorting state */\n setSorting: (updater: SortingState | ((prev: SortingState) => SortingState)) => void;\n /** Current sort column (undefined if no sorting) */\n sortBy: string | undefined;\n /** Current sort order */\n sortOrder: \"asc\" | \"desc\";\n /** Set sort column and order directly */\n setSort: (column: string, order?: \"asc\" | \"desc\") => void;\n /** Clear all sorting */\n clearSort: () => void;\n /** Toggle sort order for a column */\n toggleSort: (column: string) => void;\n /** Check if a column is currently sorted */\n isSorted: (column: string) => boolean;\n /** Get sort order for a column */\n getSortOrder: (column: string) => \"asc\" | \"desc\" | undefined;\n}\n\n/**\n * Hook for managing server-side sorting state using nuqs.\n *\n * This hook synchronizes sorting state with URL query parameters,\n * making it perfect for server-side sorting where the URL should\n * reflect the current sort state.\n *\n * @example\n * ```tsx\n * function MyTable() {\n * const {\n * sorting,\n * setSorting,\n * sortBy,\n * sortOrder,\n * setSort,\n * toggleSort\n * } = useSorting({\n * defaultSortBy: 'name',\n * defaultSortOrder: 'asc'\n * });\n *\n * // Use with TanStack Table\n * const table = useReactTable({\n * data,\n * columns,\n * manualSorting: true,\n * sorting,\n * onSortingChange: setSorting,\n * });\n *\n * return <DataTable table={table} />;\n * }\n * ```\n */\nexport function useSorting(options: UseSortingOptions = {}): UseSortingReturn {\n const {\n defaultSortBy,\n defaultSortOrder = \"asc\",\n sortByKey = \"sortBy\",\n sortOrderKey = \"sortOrder\",\n } = options;\n\n // Use nuqs to manage query state\n const [sortBy, setSortBy] = useQueryState(\n sortByKey,\n parseAsString.withDefault(defaultSortBy || \"\")\n );\n\n const [sortOrder, setSortOrder] = useQueryState(\n sortOrderKey,\n parseAsString.withDefault(defaultSortOrder)\n );\n\n // Ensure sort order is valid\n const validSortOrder = useMemo(() => {\n return sortOrder === \"desc\" ? \"desc\" : \"asc\";\n }, [sortOrder]);\n\n // Create sorting state for TanStack Table\n const sorting = useMemo<SortingState>(() => {\n if (!sortBy || sortBy.trim() === \"\") {\n return [];\n }\n return [\n {\n id: sortBy,\n desc: validSortOrder === \"desc\",\n },\n ];\n }, [sortBy, validSortOrder]);\n\n // Set sorting function that handles both object and function updates\n const setSorting = useCallback(\n (updater: SortingState | ((prev: SortingState) => SortingState)) => {\n const newSorting = typeof updater === \"function\" ? updater(sorting) : updater;\n\n if (newSorting.length === 0) {\n // Clear sorting\n setSortBy(\"\");\n } else {\n // Set first sort (TanStack Table typically only has one sort at a time)\n const firstSort = newSorting[0];\n setSortBy(firstSort.id);\n setSortOrder(firstSort.desc ? \"desc\" : \"asc\");\n }\n },\n [sorting, setSortBy, setSortOrder]\n );\n\n // Set sort column and order directly\n const setSort = useCallback(\n (column: string, order: \"asc\" | \"desc\" = \"asc\") => {\n setSortBy(column);\n setSortOrder(order);\n },\n [setSortBy, setSortOrder]\n );\n\n // Clear all sorting\n const clearSort = useCallback(() => {\n setSortBy(null);\n }, [setSortBy]);\n\n // Toggle sort order for a column\n const toggleSort = useCallback(\n (column: string) => {\n if (sortBy === column) {\n // Same column, toggle order\n setSortOrder(validSortOrder === \"asc\" ? \"desc\" : \"asc\");\n } else {\n // Different column, set new column with asc\n setSortBy(column);\n setSortOrder(\"asc\");\n }\n },\n [sortBy, validSortOrder, setSortBy, setSortOrder]\n );\n\n // Check if a column is currently sorted\n const isSorted = useCallback(\n (column: string) => {\n return sortBy === column;\n },\n [sortBy]\n );\n\n // Get sort order for a column\n const getSortOrder = useCallback(\n (column: string) => {\n return sortBy === column ? validSortOrder : undefined;\n },\n [sortBy, validSortOrder]\n );\n\n return {\n sorting,\n setSorting,\n sortBy: sortBy || undefined,\n sortOrder: validSortOrder,\n setSort,\n clearSort,\n toggleSort,\n isSorted,\n getSortOrder,\n };\n}\n","\"use client\";\n\nimport { useMemo } from \"react\";\nimport { MultiSelect } from \"@turtleclub/ui\";\nimport { useSupportedChains } from \"@turtleclub/hooks\";\n\ninterface ChainSelectorProps {\n /** Selected chain IDs */\n value: string[];\n /** Callback when selection changes */\n onValueChange: (values: string[]) => void;\n /** Placeholder text */\n placeholder?: string;\n /** Whether the selector is disabled */\n disabled?: boolean;\n /** Custom className */\n className?: string;\n /** Maximum count to show in trigger */\n maxCount?: number;\n /** Whether to close on select */\n closeOnSelect?: boolean;\n /** Whether to show search */\n searchable?: boolean;\n}\n\nexport function ChainsSelector({\n value,\n onValueChange,\n placeholder = \"Select chains\",\n disabled = false,\n className,\n maxCount = 1,\n closeOnSelect = true,\n searchable = true,\n}: ChainSelectorProps) {\n const { chains, isLoading } = useSupportedChains();\n\n const chainOptions = useMemo(() => {\n if (!chains || chains.length === 0) return [];\n\n const filteredChains = chains.filter((chain) => chain.status === \"active\");\n\n return filteredChains.map((chain) => ({\n label: chain.name,\n value: chain?.id ?? \"\",\n icon: chain.logoUrl\n ? () => (\n <img\n src={chain.logoUrl}\n alt={chain.name}\n width={16}\n height={16}\n className=\"size-4 rounded-full\"\n />\n )\n : undefined,\n }));\n }, [chains]);\n\n return (\n <MultiSelect\n searchable={searchable}\n options={chainOptions}\n value={value}\n onValueChange={onValueChange}\n disabled={disabled || isLoading}\n placeholder={isLoading ? \"Loading chains...\" : placeholder}\n closeOnSelect={closeOnSelect}\n maxCount={maxCount}\n className={className}\n />\n );\n}\n","\"use client\";\n\nimport { useEffect, useMemo } from \"react\";\nimport { MultiSelect } from \"@turtleclub/ui\";\nimport { useSupportedTokens } from \"@turtleclub/hooks\";\n\ninterface TokenSelectorProps {\n /** Selected token IDs */\n value: string[];\n /** Callback when selection changes */\n onValueChange: (values: string[]) => void;\n /** Selected chain ID to filter tokens */\n chainId?: string;\n /** Placeholder text */\n placeholder?: string;\n /** Whether the selector is disabled */\n disabled?: boolean;\n /** Custom className */\n className?: string;\n /** Maximum count to show in trigger */\n maxCount?: number;\n /** Whether to close on select */\n closeOnSelect?: boolean;\n /** Whether to show search */\n searchable?: boolean;\n}\n\nexport function TokensSelector({\n value,\n onValueChange,\n chainId,\n placeholder = \"Select tokens\",\n disabled = false,\n className,\n maxCount = 1,\n closeOnSelect = true,\n searchable = true,\n}: TokenSelectorProps) {\n const isChainSelected = !!chainId && chainId.trim() !== \"\";\n\n const { tokens, isLoading } = useSupportedTokens({\n // chainId: isChainSelected ? chainId : undefined,\n limit: 100,\n enabled: isChainSelected,\n });\n\n const tokenOptions = useMemo(() => {\n if (!tokens) return [];\n\n return tokens.map((token) => ({\n label: `${token.symbol} - ${token.name}`,\n value: token?.id || \"\",\n icon: token.logoUrl\n ? () => (\n <img\n src={token.logoUrl}\n alt={token.name}\n width={16}\n height={16}\n className=\"size-4 rounded-full\"\n />\n )\n : undefined,\n }));\n }, [tokens]);\n\n // Clear selection when chain changes\n useEffect(() => {\n if (!isChainSelected && value.length > 0) {\n onValueChange([]);\n }\n }, [isChainSelected, value, onValueChange]);\n\n return (\n <MultiSelect\n searchable={searchable}\n options={tokenOptions}\n value={value}\n onValueChange={onValueChange}\n disabled={disabled || isLoading || !isChainSelected}\n placeholder={\n !isChainSelected ? \"Select a chain first\" : isLoading ? \"Loading tokens...\" : placeholder\n }\n closeOnSelect={closeOnSelect}\n maxCount={maxCount}\n className={className}\n />\n );\n}\n","\"use client\";\n\nimport { useMemo } from \"react\";\nimport { MultiSelect } from \"@turtleclub/ui\";\nimport { useProducts } from \"@turtleclub/hooks\";\n\ninterface ProductSelectorProps {\n /** Selected product IDs */\n value: string[];\n /** Callback when selection changes */\n onValueChange: (values: string[]) => void;\n /** Placeholder text */\n placeholder?: string;\n /** Whether the selector is disabled */\n disabled?: boolean;\n /** Custom className */\n className?: string;\n /** Maximum count to show in trigger */\n maxCount?: number;\n /** Whether to close on select */\n closeOnSelect?: boolean;\n /** Whether to show search */\n searchable?: boolean;\n}\n\nexport function ProductsSelector({\n value,\n onValueChange,\n placeholder = \"Select products\",\n disabled = false,\n className,\n maxCount = 1,\n closeOnSelect = true,\n searchable = true,\n}: ProductSelectorProps) {\n const { data: productsData, isLoading } = useProducts({});\n\n const products = useMemo(() => productsData?.products ?? [], [productsData?.products]);\n\n const productOptions = useMemo(() => {\n if (!products || products.length === 0) return [];\n\n return products.map((product) => ({\n label: product.name,\n value: product.id,\n icon: product.logoUrl\n ? () => (\n <img\n src={product.logoUrl}\n alt={product.name}\n width={16}\n height={16}\n className=\"size-4 rounded-full\"\n />\n )\n : undefined,\n }));\n }, [products]);\n\n return (\n <MultiSelect\n searchable={searchable}\n options={productOptions}\n value={value}\n onValueChange={onValueChange}\n disabled={disabled || isLoading}\n placeholder={isLoading ? \"Loading products...\" : placeholder}\n closeOnSelect={closeOnSelect}\n maxCount={maxCount}\n className={className}\n />\n );\n}\n","\"use client\";\n\nimport { useMemo } from \"react\";\nimport { MultiSelect } from \"@turtleclub/ui\";\nimport { useOpportunities } from \"@turtleclub/hooks\";\n\ninterface OpportunitySelectorProps {\n /** Selected opportunity IDs */\n value: string[];\n /** Callback when selection changes */\n onValueChange: (values: string[]) => void;\n /** Placeholder text */\n placeholder?: string;\n /** Whether the selector is disabled */\n disabled?: boolean;\n /** Custom className */\n className?: string;\n /** Maximum count to show in trigger */\n maxCount?: number;\n /** Whether to close on select */\n closeOnSelect?: boolean;\n /** Whether to show search */\n searchable?: boolean;\n}\n\nexport function OpportunitiesSelector({\n value,\n onValueChange,\n placeholder = \"Select opportunities\",\n disabled = false,\n className,\n maxCount = 1,\n closeOnSelect = true,\n searchable = true,\n}: OpportunitySelectorProps) {\n const { data: opportunitiesData, isLoading } = useOpportunities();\n\n const opportunities = useMemo(\n () => opportunitiesData?.opportunities ?? [],\n [opportunitiesData?.opportunities]\n );\n\n const opportunityOptions = useMemo(() => {\n if (!opportunities || opportunities.length === 0) return [];\n\n const filteredOpportunities = opportunities.filter((opp) => opp.status === \"active\");\n\n return filteredOpportunities.map((opportunity) => ({\n label: opportunity.name || opportunity.shortName,\n value: opportunity.id!,\n description: opportunity.shortName !== opportunity.name ? opportunity.shortName : undefined,\n icon: opportunity.depositTokens?.[0]?.logoUrl\n ? () => (\n <img\n src={opportunity.depositTokens[0].logoUrl}\n alt={opportunity.name}\n width={16}\n height={16}\n className=\"size-4 rounded-full\"\n />\n )\n : undefined,\n }));\n }, [opportunities]);\n\n return (\n <MultiSelect\n searchable={searchable}\n options={opportunityOptions}\n value={value}\n onValueChange={onValueChange}\n disabled={disabled || isLoading}\n placeholder={isLoading ? \"Loading opportunities...\" : placeholder}\n closeOnSelect={closeOnSelect}\n maxCount={maxCount}\n className={className}\n />\n );\n}\n","\"use client\";\n\nimport { useMemo } from \"react\";\nimport { Combobox } from \"@turtleclub/ui\";\nimport { useSupportedChains } from \"@turtleclub/hooks\";\n\ninterface ChainSelectorProps {\n /** Selected chain ID */\n value: string;\n /** Callback when selection changes */\n onValueChange: (value: string) => void;\n /** Placeholder text */\n placeholder?: string;\n /** Whether the selector is disabled */\n disabled?: boolean;\n /** Custom className */\n className?: string;\n /** Whether to close on select */\n closeOnSelect?: boolean;\n /** Whether to show search */\n searchable?: boolean;\n}\n\nexport function ChainSelector({\n value,\n onValueChange,\n placeholder = \"Select chain\",\n disabled = false,\n className,\n closeOnSelect = true,\n searchable = true,\n}: ChainSelectorProps) {\n const { chains, isLoading } = useSupportedChains();\n\n const chainOptions = useMemo(() => {\n if (!chains || chains.length === 0) return [];\n\n const filteredChains = chains.filter((chain) => chain.status === \"active\");\n\n return filteredChains.map((chain) => ({\n label: chain.name,\n value: chain?.id ?? \"\",\n icon: chain.logoUrl\n ? () => (\n <img\n src={chain.logoUrl}\n alt={chain.name}\n width={16}\n height={16}\n className=\"size-4 rounded-full\"\n />\n )\n : undefined,\n }));\n }, [chains]);\n\n return (\n <Combobox\n searchable={searchable}\n options={chainOptions}\n value={value}\n onValueChange={onValueChange}\n disabled={disabled || isLoading}\n placeholder={isLoading ? \"Loading chains...\" : placeholder}\n closeOnSelect={closeOnSelect}\n className={className}\n />\n );\n}\n","\"use client\";\n\nimport { useEffect, useMemo } from \"react\";\nimport { Combobox } from \"@turtleclub/ui\";\nimport { useSupportedTokens } from \"@turtleclub/hooks\";\n\ninterface TokenSelectorProps {\n /** Selected token ID */\n value: string;\n /** Callback when selection changes */\n onValueChange: (value: string) => void;\n /** Selected chain ID to filter tokens */\n chainId?: string;\n /** Placeholder text */\n placeholder?: string;\n /** Whether the selector is disabled */\n disabled?: boolean;\n /** Custom className */\n className?: string;\n /** Whether to close on select */\n closeOnSelect?: boolean;\n /** Whether to show search */\n searchable?: boolean;\n}\n\nexport function TokenSelector({\n value,\n onValueChange,\n chainId,\n placeholder = \"Select token\",\n disabled = false,\n className,\n closeOnSelect = true,\n searchable = true,\n}: TokenSelectorProps) {\n const isChainSelected = !!chainId && chainId.trim() !== \"\";\n\n const { tokens, isLoading } = useSupportedTokens({\n // chainId: isChainSelected ? chainId : undefined,\n limit: 100,\n enabled: isChainSelected,\n });\n\n const tokenOptions = useMemo(() => {\n if (!tokens) return [];\n\n return tokens.map((token) => ({\n label: `${token.symbol} - ${token.name}`,\n value: token?.id || \"\",\n icon: token.logoUrl\n ? () => (\n <img\n src={token.logoUrl}\n alt={token.name}\n width={16}\n height={16}\n className=\"size-4 rounded-full\"\n />\n )\n : undefined,\n }));\n }, [tokens]);\n\n // Clear selection when chain changes\n useEffect(() => {\n if (!isChainSelected && value) {\n onValueChange(\"\");\n }\n }, [isChainSelected, value, onValueChange]);\n\n return (\n <Combobox\n searchable={searchable}\n options={tokenOptions}\n value={value}\n onValueChange={onValueChange}\n disabled={disabled || isLoading || !isChainSelected}\n placeholder={\n !isChainSelected ? \"Select a chain first\" : isLoading ? \"Loading tokens...\" : placeholder\n }\n closeOnSelect={closeOnSelect}\n className={className}\n />\n );\n}\n","\"use client\";\n\nimport { useMemo } from \"react\";\nimport { Combobox } from \"@turtleclub/ui\";\nimport { useProducts } from \"@turtleclub/hooks\";\n\ninterface ProductSelectorProps {\n /** Selected product ID */\n value: string;\n /** Callback when selection changes */\n onValueChange: (value: string) => void;\n /** Placeholder text */\n placeholder?: string;\n /** Whether the selector is disabled */\n disabled?: boolean;\n /** Custom className */\n className?: string;\n /** Whether to close on select */\n closeOnSelect?: boolean;\n /** Whether to show search */\n searchable?: boolean;\n}\n\nexport function ProductSelector({\n value,\n onValueChange,\n placeholder = \"Select product\",\n disabled = false,\n className,\n closeOnSelect = true,\n searchable = true,\n}: ProductSelectorProps) {\n const { data: productsData, isLoading } = useProducts({});\n\n const products = useMemo(() => productsData?.products ?? [], [productsData?.products]);\n\n const productOptions = useMemo(() => {\n if (!products || products.length === 0) return [];\n\n return products.map((product) => ({\n label: product.name,\n value: product.id,\n icon: product.logoUrl\n ? () => (\n <img\n src={product.logoUrl}\n alt={product.name}\n width={16}\n height={16}\n className=\"size-4 rounded-full\"\n />\n )\n : undefined,\n }));\n }, [products]);\n\n return (\n <Combobox\n searchable={searchable}\n options={productOptions}\n value={value}\n onValueChange={onValueChange}\n disabled={disabled || isLoading}\n placeholder={isLoading ? \"Loading products...\" : placeholder}\n closeOnSelect={closeOnSelect}\n className={className}\n />\n );\n}\n","\"use client\";\n\nimport { useMemo } from \"react\";\nimport { Combobox } from \"@turtleclub/ui\";\nimport { useOpportunities } from \"@turtleclub/hooks\";\n\ninterface OpportunitySelectorProps {\n /** Selected opportunity ID */\n value: string;\n /** Callback when selection changes */\n onValueChange: (value: string) => void;\n /** Placeholder text */\n placeholder?: string;\n /** Whether the selector is disabled */\n disabled?: boolean;\n /** Custom className */\n className?: string;\n /** Whether to close on select */\n closeOnSelect?: boolean;\n /** Whether to show search */\n searchable?: boolean;\n}\n\nexport function OpportunitySelector({\n value,\n onValueChange,\n placeholder = \"Select opportunity\",\n disabled = false,\n className,\n closeOnSelect = true,\n searchable = true,\n}: OpportunitySelectorProps) {\n const { data: opportunitiesData, isLoading } = useOpportunities();\n\n const opportunities = useMemo(\n () => opportunitiesData?.opportunities ?? [],\n [opportunitiesData?.opportunities]\n );\n\n const opportunityOptions = useMemo(() => {\n if (!opportunities || opportunities.length === 0) return [];\n\n const filteredOpportunities = opportunities.filter((opp) => opp.status === \"active\");\n\n return filteredOpportunities.map((opportunity) => ({\n label: opportunity.name || opportunity.shortName,\n value: opportunity.id!,\n description: opportunity.shortName !== opportunity.name ? opportunity.shortName : undefined,\n icon: opportunity.depositTokens?.[0]?.logoUrl\n ? () => (\n <img\n src={opportunity.depositTokens[0].logoUrl}\n alt={opportunity.name}\n width={16}\n height={16}\n className=\"size-4 rounded-full\"\n />\n )\n : undefined,\n }));\n }, [opportunities]);\n\n return (\n <Combobox\n searchable={searchable}\n options={opportunityOptions}\n value={value}\n onValueChange={onValueChange}\n disabled={disabled || isLoading}\n placeholder={isLoading ? \"Loading opportunities...\" : placeholder}\n closeOnSelect={closeOnSelect}\n className={className}\n />\n );\n}\n"],"mappings":"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","names":["Label","useQueryState","jsx","jsxs","useQueryState","jsx","useQueryState","parseAsString","Fragment","jsx","jsx","jsxs","cn","jsx","jsxs","Badge","Button","cn","Fragment","jsx","jsxs","cn","jsx","jsxs","cn","useQueryState","parseAsInteger","useMemo","useQueryState","parseAsString","useMemo","useCallback","useMemo","jsx","useEffect","useMemo","MultiSelect","jsx","useMemo","MultiSelect","jsx","useMemo","MultiSelect","jsx","useMemo","useSupportedChains","jsx","useEffect","useMemo","Combobox","useSupportedTokens","jsx","useMemo","Combobox","useProducts","jsx","useMemo","Combobox","useOpportunities","jsx"]}
|
|
1
|
+
{"version":3,"sources":["../src/filters/RangeSliderFilter.tsx","../src/filters/BooleanFilter.tsx","../src/filters/Filter.tsx","../src/filters/MultiSelectFilter.tsx","../src/wrappers/FiltersGrid.tsx","../src/wrappers/FiltersPopover.tsx","../src/wrappers/ActiveFilterBadges.tsx","../src/wrappers/FiltersWrapper.tsx","../src/helpers/usePagination.ts","../src/helpers/useSorting.ts","../src/selectors/ChainsSelector.tsx","../src/selectors/TokensSelector.tsx","../src/selectors/ProductsSelector.tsx","../src/selectors/OpportunitiesSelector.tsx","../src/selectors/ChainSelector.tsx","../src/selectors/TokenSelector.tsx","../src/selectors/ProductSelector.tsx","../src/selectors/OpportunitySelector.tsx"],"sourcesContent":["\"use client\";\n\nimport React, { useEffect, useState } from \"react\";\nimport { Slider, Label } from \"@turtleclub/ui\";\nimport { useQueryState, parseAsInteger } from \"nuqs\";\n\ninterface RangeSliderFilterProps {\n /** Query key for minimum value (default: 'min') */\n minQueryKey?: string;\n /** Query key for maximum value (default: 'max') */\n maxQueryKey?: string;\n /** Minimum value (default: 0) */\n min?: number;\n /** Maximum value (default: 100) */\n max?: number;\n /** Step value for slider (default: 1) */\n step?: number;\n /** Whether the filter is disabled */\n disabled?: boolean;\n /** Custom label for the filter */\n label: string;\n /** Whether to show values (default: true) */\n showValues?: boolean;\n /** Value formatter function */\n formatValue?: (value: number) => string;\n /** Custom className for the container */\n className?: string;\n}\n\nconst defaultFormatter = (value: number): string => {\n return value.toLocaleString();\n};\n\nexport function RangeSliderFilter({\n minQueryKey = \"min\",\n maxQueryKey = \"max\",\n min = 0,\n max = 100,\n step = 1,\n disabled = false,\n label = \"Range\",\n showValues = true,\n formatValue = defaultFormatter,\n className = \"\",\n}: RangeSliderFilterProps) {\n const [minValue, setMinValue] = useQueryState(minQueryKey, parseAsInteger.withDefault(min));\n\n const [maxValue, setMaxValue] = useQueryState(maxQueryKey, parseAsInteger.withDefault(max));\n\n // Local state for slider values (to avoid excessive URL updates)\n const [localRange, setLocalRange] = useState([minValue, maxValue]);\n\n // Update local state when query params change\n useEffect(() => {\n setLocalRange([minValue, maxValue]);\n }, [minValue, maxValue]);\n\n const handleRangeChange = (values: number[]) => {\n setLocalRange(values);\n };\n\n const handleRangeCommit = (values: number[]) => {\n const [newMin, newMax] = values;\n if (newMin !== minValue) {\n setMinValue(newMin === min ? null : newMin);\n }\n if (newMax !== maxValue) {\n setMaxValue(newMax === max ? null : newMax);\n }\n };\n\n const isDefaultRange = localRange[0] === min && localRange[1] === max;\n\n return (\n <div className={`space-y-3 ${className}`}>\n <div className=\"flex items-center justify-between\">\n <Label className=\"text-sm font-medium\">{label}</Label>\n {showValues && (\n <div className=\"flex items-center gap-1 text-xs text-muted-foreground\">\n <span>{formatValue(localRange[0])}</span>\n <span>-</span>\n <span>{formatValue(localRange[1])}</span>\n </div>\n )}\n </div>\n\n <div className=\"px-2\">\n <Slider\n value={localRange}\n onValueChange={handleRangeChange}\n onValueCommit={handleRangeCommit}\n min={min}\n max={max}\n step={step}\n disabled={disabled}\n className=\"w-full\"\n />\n </div>\n\n {showValues && (\n <div className=\"flex justify-between text-xs text-muted-foreground\">\n <span>{formatValue(min)}</span>\n <span>{formatValue(max)}</span>\n </div>\n )}\n\n {!isDefaultRange && (\n <div className=\"flex justify-end\">\n <button\n onClick={() => {\n setLocalRange([min, max]);\n handleRangeCommit([min, max]);\n }}\n className=\"text-xs text-muted-foreground hover:text-foreground transition-colors\"\n disabled={disabled}\n >\n Reset\n </button>\n </div>\n )}\n </div>\n );\n}\n","\"use client\";\n\nimport React from \"react\";\nimport { Switch, Label } from \"@turtleclub/ui\";\nimport { useQueryState, parseAsBoolean } from \"nuqs\";\n\ninterface BooleanSwitchFilterProps {\n /** Query key for the boolean value (default: 'enabled') */\n queryKey?: string;\n /** Default value when not set (default: false) */\n defaultValue?: boolean;\n /** Whether the switch is disabled */\n disabled?: boolean;\n /** Label for the switch */\n label: string;\n /** Description text (optional) */\n description?: string;\n /** Custom className for the container */\n className?: string;\n}\n\nexport function BooleanFilter({\n queryKey = \"enabled\",\n defaultValue = false,\n disabled = false,\n label,\n description,\n className = \"\",\n}: BooleanSwitchFilterProps) {\n const [value, setValue] = useQueryState(queryKey, parseAsBoolean.withDefault(defaultValue));\n\n const handleToggle = (checked: boolean) => {\n // If the value is the same as default, remove from URL\n setValue(checked === defaultValue ? null : checked);\n };\n\n return (\n <div className={`flex items-center justify-between space-x-2 ${className}`}>\n <div className=\"space-y-0.5\">\n <Label className=\"text-sm font-medium\">{label}</Label>\n {description && <p className=\"text-xs text-muted-foreground\">{description}</p>}\n </div>\n <Switch checked={value} onCheckedChange={handleToggle} disabled={disabled} />\n </div>\n );\n}\n","\"use client\";\n\nimport React from \"react\";\nimport { useQueryState, parseAsString } from \"nuqs\";\n\ninterface FilterProps {\n /** Query parameter key */\n queryKey: string;\n /** Child component to render (selector) */\n children: (props: { value: string; onValueChange: (value: string) => void }) => React.ReactNode;\n /** Callback when value changes */\n onValueChange?: (value: string) => void;\n}\n\n/**\n * Generic Filter component that handles nuqs URL state management for single values.\n *\n * Provides value and onValueChange props to child components,\n * decoupling selector logic from URL state management.\n *\n * @example Basic Single Selection\n * ```tsx\n * // Single chain selection - persists to URL as ?chainId=ethereum\n * <Filter queryKey=\"chainId\">\n * {({ value, onValueChange }) => (\n * <ChainSelector\n * value={value}\n * onValueChange={onValueChange}\n * placeholder=\"Select chain\"\n * />\n * )}\n * </Filter>\n * ```\n */\nexport function Filter({ queryKey, children, onValueChange }: FilterProps) {\n const [value, setValue] = useQueryState(queryKey, parseAsString.withDefault(\"\"));\n\n const handleValueChange = (newValue: string) => {\n setValue(newValue || null);\n onValueChange?.(newValue);\n };\n\n return <>{children({ value, onValueChange: handleValueChange })}</>;\n}\n","\"use client\";\n\nimport React from \"react\";\nimport { useQueryState, parseAsArrayOf, parseAsString } from \"nuqs\";\n\ninterface MultiSelectFilterProps {\n /** Query parameter key */\n queryKey: string;\n /** Child component to render (selector) */\n children: (props: {\n value: string[];\n onValueChange: (values: string[]) => void;\n }) => React.ReactNode;\n /** Callback when values change */\n onValueChange?: (values: string[]) => void;\n}\n\n/**\n * Generic MultiSelectFilter component that handles nuqs URL state management for array values.\n *\n * Provides value and onValueChange props to child components,\n * decoupling selector logic from URL state management.\n *\n * @example\n * ```tsx\n * <MultiSelectFilter queryKey=\"chainId\">\n * {({ value, onValueChange }) => (\n * <ChainsSelector\n * value={value}\n * onValueChange={onValueChange}\n * placeholder=\"Select chains\"\n * />\n * )}\n * </MultiSelectFilter>\n * ```\n */\nexport function MultiSelectFilter({ queryKey, children, onValueChange }: MultiSelectFilterProps) {\n const [value, setValue] = useQueryState(queryKey, parseAsArrayOf(parseAsString).withDefault([]));\n\n const handleValueChange = (newValue: string[]) => {\n setValue(newValue.length > 0 ? newValue : null);\n onValueChange?.(newValue);\n };\n\n return <>{children({ value, onValueChange: handleValueChange })}</>;\n}\n","\"use client\";\n\nimport React from \"react\";\nimport { cn, Label } from \"@turtleclub/ui\";\n\nexport interface FilterConfig {\n key: string;\n label: string;\n component: React.ReactNode;\n enabled: boolean;\n section?: string;\n}\n\nexport interface FiltersGridProps {\n filters: FilterConfig[];\n columns?: number;\n className?: string;\n sectionClassName?: string;\n}\n\nexport function FiltersGrid({\n filters,\n columns = 3,\n className,\n sectionClassName,\n}: FiltersGridProps) {\n const enabledFilters = filters.filter((filter) => filter.enabled);\n\n if (enabledFilters.length === 0) {\n return null;\n }\n\n // Group filters by section\n const sections = enabledFilters.reduce(\n (acc, filter) => {\n const sectionName = filter.section || \"default\";\n if (!acc[sectionName]) {\n acc[sectionName] = [];\n }\n acc[sectionName].push(filter);\n return acc;\n },\n {} as Record<string, typeof enabledFilters>\n );\n\n const sectionNames = Object.keys(sections);\n\n return (\n <div className={cn(\"space-y-4\", className)}>\n {sectionNames.map((sectionName, sectionIndex) => (\n <div key={sectionName}>\n {sectionIndex > 0 && <div className=\"border-t border-border mb-4\" />}\n <div\n className={cn(\"grid gap-2\", sectionClassName)}\n style={{\n gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`,\n }}\n >\n {sections[sectionName].map((filter) => (\n <div key={filter.key} className=\"flex flex-col gap-2\">\n {filter.component}\n </div>\n ))}\n </div>\n </div>\n ))}\n </div>\n );\n}\n","\"use client\";\n\nimport React from \"react\";\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n buttonVariants,\n cn,\n Label,\n Badge,\n} from \"@turtleclub/ui\";\nimport { SlidersHorizontal } from \"lucide-react\";\nimport { FilterConfig } from \"./FiltersGrid\";\n\nexport interface FiltersPopoverProps {\n filters: FilterConfig[];\n triggerLabel?: string;\n columns?: number;\n className?: string;\n popoverClassName?: string;\n sectionClassName?: string;\n}\n\nexport function FiltersPopover({\n filters,\n triggerLabel = \"Filters\",\n columns = 2,\n className,\n popoverClassName,\n sectionClassName,\n}: FiltersPopoverProps) {\n const enabledFilters = filters.filter((filter) => filter.enabled);\n\n if (enabledFilters.length === 0) {\n return null;\n }\n\n // Group filters by section\n const sections = enabledFilters.reduce(\n (acc, filter) => {\n const sectionName = filter.section || \"default\";\n if (!acc[sectionName]) {\n acc[sectionName] = [];\n }\n acc[sectionName].push(filter);\n return acc;\n },\n {} as Record<string, typeof enabledFilters>\n );\n\n const sectionNames = Object.keys(sections);\n\n return (\n <Popover>\n <PopoverTrigger\n className={cn(\n buttonVariants({\n variant: \"default\",\n size: \"default\",\n border: \"bordered\",\n }),\n \"!bg-neutral-alpha-2\",\n className\n )}\n >\n <SlidersHorizontal className=\"mr-2 h-4 w-4\" />\n {triggerLabel}\n </PopoverTrigger>\n <PopoverContent\n className={cn(\"w-auto min-w-[400px] max-w-[600px]\", popoverClassName)}\n align=\"start\"\n >\n <div className=\"space-y-4\">\n <div className=\"flex items-center justify-between\">\n <h4 className=\"font-medium text-sm\">Filters</h4>\n </div>\n <div className=\"space-y-4\">\n {sectionNames.map((sectionName, sectionIndex) => (\n <div key={sectionName}>\n {sectionIndex > 0 && <div className=\"border-t border-border mb-4\" />}\n <div\n className={cn(\"grid gap-2\", sectionClassName)}\n style={{\n gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`,\n }}\n >\n {sections[sectionName].map((filter) => (\n <div key={filter.key} className=\"flex flex-col gap-2\">\n {filter.component}\n </div>\n ))}\n </div>\n </div>\n ))}\n </div>\n </div>\n </PopoverContent>\n </Popover>\n );\n}\n","\"use client\";\n\nimport React, { useMemo } from \"react\";\nimport { Badge, Button, cn } from \"@turtleclub/ui\";\nimport { X } from \"lucide-react\";\n\nexport interface ActiveFilterConfig {\n /** Unique key for the filter */\n key: string;\n /** Display label for the filter */\n label: string;\n /** Current filter value to display */\n value: string;\n /** Query parameter key(s) this filter uses */\n queryKeys: string | string[];\n /** Whether this filter is currently active */\n isActive: boolean;\n}\n\nexport interface ActiveFilterBadgesProps {\n /** Array of filter configurations */\n filters: ActiveFilterConfig[];\n /** Custom className for the container */\n className?: string;\n /** Label for clear all button (default: \"Clear All\") */\n clearAllLabel?: string;\n /** Whether to show clear all button (default: true) */\n showClearAll?: boolean;\n /** Custom render function for individual badges */\n renderBadge?: (filter: ActiveFilterConfig, clearFilter: () => void) => React.ReactNode;\n /** Callback function to clear individual filter */\n onClearFilter: (queryKeys: string | string[]) => void;\n /** Callback function to clear all filters */\n onClearAll: (allQueryKeys: (string | string[])[]) => void;\n}\n\n/**\n * Component that displays active filter badges with individual clear buttons\n * and an optional \"clear all\" button.\n *\n * @example\n * ```tsx\n * const activeFilters = [\n * {\n * key: 'chain',\n * label: 'Chain',\n * value: 'Ethereum',\n * queryKeys: 'chainId',\n * isActive: true,\n * },\n * {\n * key: 'sorting',\n * label: 'Sort',\n * value: 'Name (A-Z)',\n * queryKeys: ['sortBy', 'sortOrder'],\n * isActive: true,\n * },\n * ];\n *\n * <ActiveFilterBadges filters={activeFilters} />\n * ```\n */\nexport function ActiveFilterBadges({\n filters,\n className,\n clearAllLabel = \"Clear All\",\n showClearAll = true,\n renderBadge,\n onClearFilter,\n onClearAll,\n}: ActiveFilterBadgesProps) {\n // Get only active filters\n const activeFilters = useMemo(() => {\n return filters.filter((filter) => filter.isActive && filter.value);\n }, [filters]);\n\n // Clear all function\n const clearAll = () => {\n const allQueryKeys = activeFilters.map((filter) => filter.queryKeys);\n onClearAll(allQueryKeys);\n };\n\n // Don't render if no active filters\n if (activeFilters.length === 0) {\n return null;\n }\n\n return (\n <div className={cn(\"flex flex-wrap items-center gap-2\", className)}>\n {activeFilters.map((filter) => {\n return (\n <FilterBadge\n key={filter.key}\n filter={filter}\n renderBadge={renderBadge}\n onClearFilter={onClearFilter}\n />\n );\n })}\n\n {showClearAll && activeFilters.length > 1 && (\n <Button variant=\"ghost\" size=\"sm\" onClick={clearAll} className=\"h-7 px-2 text-xs\">\n {clearAllLabel}\n </Button>\n )}\n </div>\n );\n}\n\ninterface FilterBadgeProps {\n filter: ActiveFilterConfig;\n renderBadge?: (filter: ActiveFilterConfig, clearFilter: () => void) => React.ReactNode;\n onClearFilter: (queryKeys: string | string[]) => void;\n}\n\nfunction FilterBadge({ filter, renderBadge, onClearFilter }: FilterBadgeProps) {\n const clearFilter = () => {\n onClearFilter(filter.queryKeys);\n };\n\n if (renderBadge) {\n return <>{renderBadge(filter, clearFilter)}</>;\n }\n\n return (\n <Badge className=\"flex items-center gap-1 pr-1 border-border border\">\n <span className=\"text-xs\">\n {filter.label}: {filter.value}\n </span>\n <button\n onClick={clearFilter}\n className=\"ml-1 rounded-full p-0.5 hover:bg-black/10 focus:bg-black/10 focus:outline-none\"\n aria-label={`Clear ${filter.label} filter`}\n >\n <X className=\"h-3 w-3\" />\n </button>\n </Badge>\n );\n}\n","\"use client\";\n\nimport React from \"react\";\nimport { cn } from \"@turtleclub/ui\";\nimport { FiltersGrid } from \"./FiltersGrid\";\nimport { FiltersPopover } from \"./FiltersPopover\";\nimport { ActiveFilterBadges, ActiveFilterConfig } from \"./ActiveFilterBadges\";\nimport { FilterConfig } from \"./FiltersGrid\";\n\nexport interface FiltersWrapperProps {\n /** Array of filter configurations */\n filters: FilterConfig[];\n /** Array of active filter configurations for badges */\n activeFilters: ActiveFilterConfig[];\n /** Layout type: 'grid' or 'popover' */\n layout: \"grid\" | \"popover\";\n /** Custom className for the wrapper */\n className?: string;\n /** Custom className for the active badges section */\n badgesClassName?: string;\n /** Custom className for the slot container */\n slotClassName?: string;\n /** Optional content to render on the left side of filters */\n leftSlot?: React.ReactNode;\n /** Optional content to render on the right side of filters */\n rightSlot?: React.ReactNode;\n\n // Grid-specific props\n /** Number of columns for grid layout (default: 3) */\n columns?: number;\n\n // Popover-specific props\n /** Button label for popover trigger (default: \"Filters\") */\n triggerLabel?: string;\n /** Number of columns in popover (default: 2) */\n popoverColumns?: number;\n /** Custom className for popover trigger button */\n popoverClassName?: string;\n /** Custom className for popover content */\n popoverContentClassName?: string;\n\n // Active badges props\n /** Label for clear all button (default: \"Clear All\") */\n clearAllLabel?: string;\n /** Whether to show clear all button (default: true) */\n showClearAll?: boolean;\n /** Custom render function for individual badges */\n renderBadge?: (filter: ActiveFilterConfig, clearFilter: () => void) => React.ReactNode;\n /** Whether to show active filter badges (default: true) */\n showActiveBadges?: boolean;\n /** Callback function to clear individual filter */\n onClearFilter: (queryKeys: string | string[]) => void;\n /** Callback function to clear all filters */\n onClearAll: (allQueryKeys: (string | string[])[]) => void;\n}\n\n/**\n * Unified wrapper component that combines filter controls with active filter badges.\n *\n * Supports both grid and popover layouts, automatically displays active filter badges\n * below the filter controls with individual and bulk clear functionality.\n *\n * @example\n * ```tsx\n * const filters = [\n * {\n * key: 'chain',\n * label: 'Chain',\n * component: <ChainFilter />,\n * enabled: true,\n * },\n * ];\n *\n * const activeFilters = [\n * {\n * key: 'chain',\n * label: 'Chain',\n * value: 'Ethereum',\n * queryKeys: 'chainId',\n * isActive: true,\n * },\n * ];\n *\n * <FiltersWrapper\n * layout=\"grid\"\n * filters={filters}\n * activeFilters={activeFilters}\n * />\n * ```\n */\nexport function FiltersWrapper({\n filters,\n activeFilters,\n layout,\n className,\n badgesClassName,\n slotClassName,\n leftSlot,\n rightSlot,\n columns = 3,\n triggerLabel = \"Filters\",\n popoverColumns = 2,\n popoverClassName,\n popoverContentClassName,\n clearAllLabel = \"Clear All\",\n showClearAll = true,\n renderBadge,\n showActiveBadges = true,\n onClearFilter,\n onClearAll,\n}: FiltersWrapperProps) {\n const hasActiveFilters = activeFilters.some((filter) => filter.isActive && filter.value);\n\n return (\n <div className={cn(\"space-y-3\", className)}>\n {/* Filter Controls */}\n <div className={cn(\"flex items-center gap-2\", slotClassName)}>\n {leftSlot}\n <div className=\"flex-1\">\n {layout === \"grid\" ? (\n <FiltersGrid filters={filters} columns={columns} className={popoverClassName} />\n ) : (\n <FiltersPopover\n filters={filters}\n triggerLabel={triggerLabel}\n columns={popoverColumns}\n className={popoverClassName}\n popoverClassName={popoverContentClassName}\n />\n )}\n </div>\n {rightSlot}\n </div>\n\n {/* Active Filter Badges */}\n {showActiveBadges && hasActiveFilters && (\n <ActiveFilterBadges\n filters={activeFilters}\n className={badgesClassName}\n clearAllLabel={clearAllLabel}\n showClearAll={showClearAll}\n renderBadge={renderBadge}\n onClearFilter={onClearFilter}\n onClearAll={onClearAll}\n />\n )}\n </div>\n );\n}\n","\"use client\";\n\nimport { useQueryState, parseAsInteger } from \"nuqs\";\nimport { useMemo, useCallback } from \"react\";\nimport type { PaginationState } from \"@tanstack/react-table\";\n\nexport interface UsePaginationOptions {\n /** Default page size (default: 10) */\n defaultPageSize?: number;\n /** Default page index (default: 0) */\n defaultPageIndex?: number;\n /** Query param key for page index (default: 'page') */\n pageIndexKey?: string;\n /** Query param key for page size (default: 'pageSize') */\n pageSizeKey?: string;\n}\n\nexport interface UsePaginationReturn {\n /** Current pagination state for TanStack Table */\n pagination: PaginationState;\n /** Function to update pagination state */\n setPagination: (updater: PaginationState | ((prev: PaginationState) => PaginationState)) => void;\n /** Current page index (0-based) */\n pageIndex: number;\n /** Current page size */\n pageSize: number;\n /** Set page index directly */\n setPageIndex: (pageIndex: number) => void;\n /** Set page size directly */\n setPageSize: (pageSize: number) => void;\n /** Go to next page */\n nextPage: (totalPages?: number) => void;\n /** Go to previous page */\n previousPage: () => void;\n /** Go to first page */\n firstPage: () => void;\n /** Go to last page */\n lastPage: (totalPages: number) => void;\n /** Reset pagination to defaults */\n reset: () => void;\n /** Check if can go to next page */\n canNextPage: (totalPages?: number) => boolean;\n /** Check if can go to previous page */\n canPreviousPage: boolean;\n}\n\n/**\n * Hook for managing server-side pagination state using nuqs.\n *\n * This hook synchronizes pagination state with URL query parameters,\n * making it perfect for server-side pagination where the URL should\n * reflect the current page and page size.\n *\n * @example\n * ```tsx\n * function MyTable() {\n * const {\n * pagination,\n * setPagination,\n * pageIndex,\n * pageSize,\n * setPageIndex,\n * setPageSize\n * } = usePagination({\n * defaultPageSize: 20,\n * pageIndexKey: 'page',\n * pageSizeKey: 'limit'\n * });\n *\n * // Use with TanStack Table\n * const table = useReactTable({\n * data,\n * columns,\n * manualPagination: true,\n * pagination,\n * onPaginationChange: setPagination,\n * pageCount: Math.ceil(totalCount / pageSize),\n * });\n *\n * return <DataTable table={table} />;\n * }\n * ```\n */\nexport function usePagination(options: UsePaginationOptions = {}): UsePaginationReturn {\n const {\n defaultPageSize = 10,\n defaultPageIndex = 0,\n pageIndexKey = \"page\",\n pageSizeKey = \"pageSize\",\n } = options;\n\n // Use nuqs to manage query state\n const [rawPageIndex, setRawPageIndex] = useQueryState(\n pageIndexKey,\n parseAsInteger.withDefault(defaultPageIndex)\n );\n\n const [rawPageSize, setRawPageSize] = useQueryState(\n pageSizeKey,\n parseAsInteger.withDefault(defaultPageSize)\n );\n\n // Ensure page index is never negative\n const pageIndex = useMemo(() => Math.max(0, rawPageIndex), [rawPageIndex]);\n\n // Use page size directly from query state\n const pageSize = rawPageSize;\n\n // Create pagination state for TanStack Table\n const pagination = useMemo<PaginationState>(\n () => ({\n pageIndex,\n pageSize,\n }),\n [pageIndex, pageSize]\n );\n\n // Set pagination function that handles both object and function updates\n const setPagination = useCallback(\n (updater: PaginationState | ((prev: PaginationState) => PaginationState)) => {\n const newPagination = typeof updater === \"function\" ? updater(pagination) : updater;\n\n if (newPagination.pageIndex !== pageIndex) {\n setRawPageIndex(Math.max(0, newPagination.pageIndex));\n }\n\n if (newPagination.pageSize !== pageSize) {\n setRawPageSize(newPagination.pageSize);\n }\n },\n [pagination, pageIndex, pageSize, setRawPageIndex, setRawPageSize]\n );\n\n // Individual setters\n const setPageIndex = useCallback(\n (newPageIndex: number) => {\n setRawPageIndex(Math.max(0, newPageIndex));\n },\n [setRawPageIndex]\n );\n\n const setPageSize = useCallback(\n (newPageSize: number) => {\n setRawPageSize(newPageSize);\n // Reset to first page when page size changes\n setRawPageIndex(0);\n },\n [setRawPageSize, setRawPageIndex]\n );\n\n // Navigation functions\n const nextPage = useCallback(\n (totalPages?: number) => {\n const nextPageIndex = pageIndex + 1;\n if (!totalPages || nextPageIndex < totalPages) {\n setPageIndex(nextPageIndex);\n }\n },\n [pageIndex, setPageIndex]\n );\n\n const previousPage = useCallback(() => {\n if (pageIndex > 0) {\n setPageIndex(pageIndex - 1);\n }\n }, [pageIndex, setPageIndex]);\n\n const firstPage = useCallback(() => {\n setPageIndex(0);\n }, [setPageIndex]);\n\n const lastPage = useCallback(\n (totalPages: number) => {\n setPageIndex(Math.max(0, totalPages - 1));\n },\n [setPageIndex]\n );\n\n // Reset function\n const reset = useCallback(() => {\n setRawPageIndex(defaultPageIndex);\n setRawPageSize(defaultPageSize);\n }, [setRawPageIndex, setRawPageSize, defaultPageIndex, defaultPageSize]);\n\n // Navigation state\n const canPreviousPage = pageIndex > 0;\n const canNextPage = useCallback(\n (totalPages?: number) => {\n if (!totalPages) return true; // Assume we can go next if total pages unknown\n return pageIndex < totalPages - 1;\n },\n [pageIndex]\n );\n\n return {\n pagination,\n setPagination,\n pageIndex,\n pageSize,\n setPageIndex,\n setPageSize,\n nextPage,\n previousPage,\n firstPage,\n lastPage,\n reset,\n canNextPage,\n canPreviousPage,\n };\n}\n","\"use client\";\n\nimport { useQueryState, parseAsString } from \"nuqs\";\nimport { useMemo, useCallback } from \"react\";\nimport type { SortingState } from \"@tanstack/react-table\";\n\nexport interface UseSortingOptions {\n /** Default sort column (default: undefined - no sorting) */\n defaultSortBy?: string;\n /** Default sort order (default: 'asc') */\n defaultSortOrder?: \"asc\" | \"desc\";\n /** Query param key for sort column (default: 'sortBy') */\n sortByKey?: string;\n /** Query param key for sort order (default: 'sortOrder') */\n sortOrderKey?: string;\n}\n\nexport interface UseSortingReturn {\n /** Current sorting state for TanStack Table */\n sorting: SortingState;\n /** Function to update sorting state */\n setSorting: (updater: SortingState | ((prev: SortingState) => SortingState)) => void;\n /** Current sort column (undefined if no sorting) */\n sortBy: string | undefined;\n /** Current sort order */\n sortOrder: \"asc\" | \"desc\";\n /** Set sort column and order directly */\n setSort: (column: string, order?: \"asc\" | \"desc\") => void;\n /** Clear all sorting */\n clearSort: () => void;\n /** Toggle sort order for a column */\n toggleSort: (column: string) => void;\n /** Check if a column is currently sorted */\n isSorted: (column: string) => boolean;\n /** Get sort order for a column */\n getSortOrder: (column: string) => \"asc\" | \"desc\" | undefined;\n}\n\n/**\n * Hook for managing server-side sorting state using nuqs.\n *\n * This hook synchronizes sorting state with URL query parameters,\n * making it perfect for server-side sorting where the URL should\n * reflect the current sort state.\n *\n * @example\n * ```tsx\n * function MyTable() {\n * const {\n * sorting,\n * setSorting,\n * sortBy,\n * sortOrder,\n * setSort,\n * toggleSort\n * } = useSorting({\n * defaultSortBy: 'name',\n * defaultSortOrder: 'asc'\n * });\n *\n * // Use with TanStack Table\n * const table = useReactTable({\n * data,\n * columns,\n * manualSorting: true,\n * sorting,\n * onSortingChange: setSorting,\n * });\n *\n * return <DataTable table={table} />;\n * }\n * ```\n */\nexport function useSorting(options: UseSortingOptions = {}): UseSortingReturn {\n const {\n defaultSortBy,\n defaultSortOrder = \"asc\",\n sortByKey = \"sortBy\",\n sortOrderKey = \"sortOrder\",\n } = options;\n\n // Use nuqs to manage query state\n const [sortBy, setSortBy] = useQueryState(\n sortByKey,\n parseAsString.withDefault(defaultSortBy || \"\")\n );\n\n const [sortOrder, setSortOrder] = useQueryState(\n sortOrderKey,\n parseAsString.withDefault(defaultSortOrder)\n );\n\n // Ensure sort order is valid\n const validSortOrder = useMemo(() => {\n return sortOrder === \"desc\" ? \"desc\" : \"asc\";\n }, [sortOrder]);\n\n // Create sorting state for TanStack Table\n const sorting = useMemo<SortingState>(() => {\n if (!sortBy || sortBy.trim() === \"\") {\n return [];\n }\n return [\n {\n id: sortBy,\n desc: validSortOrder === \"desc\",\n },\n ];\n }, [sortBy, validSortOrder]);\n\n // Set sorting function that handles both object and function updates\n const setSorting = useCallback(\n (updater: SortingState | ((prev: SortingState) => SortingState)) => {\n const newSorting = typeof updater === \"function\" ? updater(sorting) : updater;\n\n if (newSorting.length === 0) {\n // Clear sorting\n setSortBy(\"\");\n } else {\n // Set first sort (TanStack Table typically only has one sort at a time)\n const firstSort = newSorting[0];\n setSortBy(firstSort.id);\n setSortOrder(firstSort.desc ? \"desc\" : \"asc\");\n }\n },\n [sorting, setSortBy, setSortOrder]\n );\n\n // Set sort column and order directly\n const setSort = useCallback(\n (column: string, order: \"asc\" | \"desc\" = \"asc\") => {\n setSortBy(column);\n setSortOrder(order);\n },\n [setSortBy, setSortOrder]\n );\n\n // Clear all sorting\n const clearSort = useCallback(() => {\n setSortBy(null);\n }, [setSortBy]);\n\n // Toggle sort order for a column\n const toggleSort = useCallback(\n (column: string) => {\n if (sortBy === column) {\n // Same column, toggle order\n setSortOrder(validSortOrder === \"asc\" ? \"desc\" : \"asc\");\n } else {\n // Different column, set new column with asc\n setSortBy(column);\n setSortOrder(\"asc\");\n }\n },\n [sortBy, validSortOrder, setSortBy, setSortOrder]\n );\n\n // Check if a column is currently sorted\n const isSorted = useCallback(\n (column: string) => {\n return sortBy === column;\n },\n [sortBy]\n );\n\n // Get sort order for a column\n const getSortOrder = useCallback(\n (column: string) => {\n return sortBy === column ? validSortOrder : undefined;\n },\n [sortBy, validSortOrder]\n );\n\n return {\n sorting,\n setSorting,\n sortBy: sortBy || undefined,\n sortOrder: validSortOrder,\n setSort,\n clearSort,\n toggleSort,\n isSorted,\n getSortOrder,\n };\n}\n","\"use client\";\n\nimport { useMemo } from \"react\";\nimport { MultiSelect } from \"@turtleclub/ui\";\nimport { useSupportedChains } from \"@turtleclub/hooks\";\n\ninterface ChainSelectorProps {\n /** Selected chain IDs */\n value: string[];\n /** Callback when selection changes */\n onValueChange: (values: string[]) => void;\n /** Placeholder text */\n placeholder?: string;\n /** Whether the selector is disabled */\n disabled?: boolean;\n /** Custom className */\n className?: string;\n /** Maximum count to show in trigger */\n maxCount?: number;\n /** Whether to close on select */\n closeOnSelect?: boolean;\n /** Whether to show search */\n searchable?: boolean;\n}\n\nexport function ChainsSelector({\n value,\n onValueChange,\n placeholder = \"Select chains\",\n disabled = false,\n className,\n maxCount = 1,\n closeOnSelect = true,\n searchable = true,\n}: ChainSelectorProps) {\n const { chains, isLoading } = useSupportedChains();\n\n const chainOptions = useMemo(() => {\n if (!chains || chains.length === 0) return [];\n\n const filteredChains = chains.filter((chain) => chain.status === \"active\");\n\n return filteredChains.map((chain) => ({\n label: chain.name,\n value: chain?.id ?? \"\",\n icon: chain.logoUrl\n ? () => (\n <img\n src={chain.logoUrl}\n alt={chain.name}\n width={16}\n height={16}\n className=\"size-4 rounded-full\"\n />\n )\n : undefined,\n }));\n }, [chains]);\n\n return (\n <MultiSelect\n searchable={searchable}\n options={chainOptions}\n value={value}\n onValueChange={onValueChange}\n disabled={disabled || isLoading}\n placeholder={isLoading ? \"Loading chains...\" : placeholder}\n closeOnSelect={closeOnSelect}\n maxCount={maxCount}\n className={className}\n />\n );\n}\n","\"use client\";\n\nimport { useEffect, useMemo } from \"react\";\nimport { MultiSelect } from \"@turtleclub/ui\";\nimport { useSupportedTokens } from \"@turtleclub/hooks\";\n\ninterface TokenSelectorProps {\n /** Selected token IDs */\n value: string[];\n /** Callback when selection changes */\n onValueChange: (values: string[]) => void;\n /** Selected chain ID to filter tokens */\n chainId?: string;\n /** Placeholder text */\n placeholder?: string;\n /** Whether the selector is disabled */\n disabled?: boolean;\n /** Custom className */\n className?: string;\n /** Maximum count to show in trigger */\n maxCount?: number;\n /** Whether to close on select */\n closeOnSelect?: boolean;\n /** Whether to show search */\n searchable?: boolean;\n}\n\nexport function TokensSelector({\n value,\n onValueChange,\n chainId,\n placeholder = \"Select tokens\",\n disabled = false,\n className,\n maxCount = 1,\n closeOnSelect = true,\n searchable = true,\n}: TokenSelectorProps) {\n const isChainSelected = !!chainId && chainId.trim() !== \"\";\n\n const { tokens, isLoading } = useSupportedTokens({\n // chainId: isChainSelected ? chainId : undefined,\n limit: 100,\n enabled: isChainSelected,\n });\n\n const tokenOptions = useMemo(() => {\n if (!tokens) return [];\n\n return tokens.map((token) => ({\n label: `${token.symbol} - ${token.name}`,\n value: token?.id || \"\",\n icon: token.logoUrl\n ? () => (\n <img\n src={token.logoUrl}\n alt={token.name}\n width={16}\n height={16}\n className=\"size-4 rounded-full\"\n />\n )\n : undefined,\n }));\n }, [tokens]);\n\n // Clear selection when chain changes\n useEffect(() => {\n if (!isChainSelected && value.length > 0) {\n onValueChange([]);\n }\n }, [isChainSelected, value, onValueChange]);\n\n return (\n <MultiSelect\n searchable={searchable}\n options={tokenOptions}\n value={value}\n onValueChange={onValueChange}\n disabled={disabled || isLoading || !isChainSelected}\n placeholder={\n !isChainSelected ? \"Select a chain first\" : isLoading ? \"Loading tokens...\" : placeholder\n }\n closeOnSelect={closeOnSelect}\n maxCount={maxCount}\n className={className}\n />\n );\n}\n","\"use client\";\n\nimport { useMemo } from \"react\";\nimport { MultiSelect } from \"@turtleclub/ui\";\nimport { useProducts } from \"@turtleclub/hooks\";\n\ninterface ProductSelectorProps {\n /** Selected product IDs */\n value: string[];\n /** Callback when selection changes */\n onValueChange: (values: string[]) => void;\n /** Placeholder text */\n placeholder?: string;\n /** Whether the selector is disabled */\n disabled?: boolean;\n /** Custom className */\n className?: string;\n /** Maximum count to show in trigger */\n maxCount?: number;\n /** Whether to close on select */\n closeOnSelect?: boolean;\n /** Whether to show search */\n searchable?: boolean;\n}\n\nexport function ProductsSelector({\n value,\n onValueChange,\n placeholder = \"Select products\",\n disabled = false,\n className,\n maxCount = 1,\n closeOnSelect = true,\n searchable = true,\n}: ProductSelectorProps) {\n const { data: productsData, isLoading } = useProducts({});\n\n const products = useMemo(() => productsData?.products ?? [], [productsData?.products]);\n\n const productOptions = useMemo(() => {\n if (!products || products.length === 0) return [];\n\n return products.map((product) => ({\n label: product.name,\n value: product.id,\n icon: product.logoUrl\n ? () => (\n <img\n src={product.logoUrl}\n alt={product.name}\n width={16}\n height={16}\n className=\"size-4 rounded-full\"\n />\n )\n : undefined,\n }));\n }, [products]);\n\n return (\n <MultiSelect\n searchable={searchable}\n options={productOptions}\n value={value}\n onValueChange={onValueChange}\n disabled={disabled || isLoading}\n placeholder={isLoading ? \"Loading products...\" : placeholder}\n closeOnSelect={closeOnSelect}\n maxCount={maxCount}\n className={className}\n />\n );\n}\n","\"use client\";\n\nimport { useMemo } from \"react\";\nimport { MultiSelect } from \"@turtleclub/ui\";\nimport { useOpportunities } from \"@turtleclub/hooks\";\n\ninterface OpportunitySelectorProps {\n /** Selected opportunity IDs */\n value: string[];\n /** Callback when selection changes */\n onValueChange: (values: string[]) => void;\n /** Placeholder text */\n placeholder?: string;\n /** Whether the selector is disabled */\n disabled?: boolean;\n /** Custom className */\n className?: string;\n /** Maximum count to show in trigger */\n maxCount?: number;\n /** Whether to close on select */\n closeOnSelect?: boolean;\n /** Whether to show search */\n searchable?: boolean;\n}\n\nexport function OpportunitiesSelector({\n value,\n onValueChange,\n placeholder = \"Select opportunities\",\n disabled = false,\n className,\n maxCount = 1,\n closeOnSelect = true,\n searchable = true,\n}: OpportunitySelectorProps) {\n const { data: opportunitiesData, isLoading } = useOpportunities();\n\n const opportunities = useMemo(\n () => opportunitiesData?.opportunities ?? [],\n [opportunitiesData?.opportunities]\n );\n\n const opportunityOptions = useMemo(() => {\n if (!opportunities || opportunities.length === 0) return [];\n\n const filteredOpportunities = opportunities.filter((opp) => opp.status === \"active\");\n\n return filteredOpportunities.map((opportunity) => ({\n label: opportunity.name || opportunity.shortName,\n value: opportunity.id!,\n description: opportunity.shortName !== opportunity.name ? opportunity.shortName : undefined,\n icon: opportunity.depositTokens?.[0]?.logoUrl\n ? () => (\n <img\n src={opportunity.depositTokens[0].logoUrl}\n alt={opportunity.name}\n width={16}\n height={16}\n className=\"size-4 rounded-full\"\n />\n )\n : undefined,\n }));\n }, [opportunities]);\n\n return (\n <MultiSelect\n searchable={searchable}\n options={opportunityOptions}\n value={value}\n onValueChange={onValueChange}\n disabled={disabled || isLoading}\n placeholder={isLoading ? \"Loading opportunities...\" : placeholder}\n closeOnSelect={closeOnSelect}\n maxCount={maxCount}\n className={className}\n />\n );\n}\n","\"use client\";\n\nimport { useMemo } from \"react\";\nimport { Combobox } from \"@turtleclub/ui\";\nimport { useSupportedChains } from \"@turtleclub/hooks\";\n\ninterface ChainSelectorProps {\n /** Selected chain ID */\n value: string;\n /** Callback when selection changes */\n onValueChange: (value: string) => void;\n /** Placeholder text */\n placeholder?: string;\n /** Whether the selector is disabled */\n disabled?: boolean;\n /** Custom className */\n className?: string;\n /** Whether to close on select */\n closeOnSelect?: boolean;\n /** Whether to show search */\n searchable?: boolean;\n}\n\nexport function ChainSelector({\n value,\n onValueChange,\n placeholder = \"Select chain\",\n disabled = false,\n className,\n closeOnSelect = true,\n searchable = true,\n}: ChainSelectorProps) {\n const { chains, isLoading } = useSupportedChains();\n\n const chainOptions = useMemo(() => {\n if (!chains || chains.length === 0) return [];\n\n const filteredChains = chains.filter((chain) => chain.status === \"active\");\n\n return filteredChains.map((chain) => ({\n label: chain.name,\n value: chain?.id ?? \"\",\n icon: chain.logoUrl\n ? () => (\n <img\n src={chain.logoUrl}\n alt={chain.name}\n width={16}\n height={16}\n className=\"size-4 rounded-full\"\n />\n )\n : undefined,\n }));\n }, [chains]);\n\n return (\n <Combobox\n searchable={searchable}\n options={chainOptions}\n value={value}\n onValueChange={onValueChange}\n disabled={disabled || isLoading}\n placeholder={isLoading ? \"Loading chains...\" : placeholder}\n closeOnSelect={closeOnSelect}\n className={className}\n />\n );\n}\n","\"use client\";\n\nimport { useEffect, useMemo } from \"react\";\nimport { Combobox } from \"@turtleclub/ui\";\nimport { useSupportedTokens } from \"@turtleclub/hooks\";\n\ninterface TokenSelectorProps {\n /** Selected token ID */\n value: string;\n /** Callback when selection changes */\n onValueChange: (value: string) => void;\n /** Selected chain ID to filter tokens */\n chainId?: string;\n /** Placeholder text */\n placeholder?: string;\n /** Whether the selector is disabled */\n disabled?: boolean;\n /** Custom className */\n className?: string;\n /** Whether to close on select */\n closeOnSelect?: boolean;\n /** Whether to show search */\n searchable?: boolean;\n}\n\nexport function TokenSelector({\n value,\n onValueChange,\n chainId,\n placeholder = \"Select token\",\n disabled = false,\n className,\n closeOnSelect = true,\n searchable = true,\n}: TokenSelectorProps) {\n const isChainSelected = !!chainId && chainId.trim() !== \"\";\n\n const { tokens, isLoading } = useSupportedTokens({\n // chainId: isChainSelected ? chainId : undefined,\n limit: 100,\n enabled: isChainSelected,\n });\n\n const tokenOptions = useMemo(() => {\n if (!tokens) return [];\n\n return tokens.map((token) => ({\n label: `${token.symbol} - ${token.name}`,\n value: token?.id || \"\",\n icon: token.logoUrl\n ? () => (\n <img\n src={token.logoUrl}\n alt={token.name}\n width={16}\n height={16}\n className=\"size-4 rounded-full\"\n />\n )\n : undefined,\n }));\n }, [tokens]);\n\n // Clear selection when chain changes\n useEffect(() => {\n if (!isChainSelected && value) {\n onValueChange(\"\");\n }\n }, [isChainSelected, value, onValueChange]);\n\n return (\n <Combobox\n searchable={searchable}\n options={tokenOptions}\n value={value}\n onValueChange={onValueChange}\n disabled={disabled || isLoading || !isChainSelected}\n placeholder={\n !isChainSelected ? \"Select a chain first\" : isLoading ? \"Loading tokens...\" : placeholder\n }\n closeOnSelect={closeOnSelect}\n className={className}\n />\n );\n}\n","\"use client\";\n\nimport { useMemo } from \"react\";\nimport { Combobox } from \"@turtleclub/ui\";\nimport { useProducts } from \"@turtleclub/hooks\";\n\ninterface ProductSelectorProps {\n /** Selected product ID */\n value: string;\n /** Callback when selection changes */\n onValueChange: (value: string) => void;\n /** Placeholder text */\n placeholder?: string;\n /** Whether the selector is disabled */\n disabled?: boolean;\n /** Custom className */\n className?: string;\n /** Whether to close on select */\n closeOnSelect?: boolean;\n /** Whether to show search */\n searchable?: boolean;\n}\n\nexport function ProductSelector({\n value,\n onValueChange,\n placeholder = \"Select product\",\n disabled = false,\n className,\n closeOnSelect = true,\n searchable = true,\n}: ProductSelectorProps) {\n const { data: productsData, isLoading } = useProducts({});\n\n const products = useMemo(() => productsData?.products ?? [], [productsData?.products]);\n\n const productOptions = useMemo(() => {\n if (!products || products.length === 0) return [];\n\n return products.map((product) => ({\n label: product.name,\n value: product.id,\n icon: product.logoUrl\n ? () => (\n <img\n src={product.logoUrl}\n alt={product.name}\n width={16}\n height={16}\n className=\"size-4 rounded-full\"\n />\n )\n : undefined,\n }));\n }, [products]);\n\n return (\n <Combobox\n searchable={searchable}\n options={productOptions}\n value={value}\n onValueChange={onValueChange}\n disabled={disabled || isLoading}\n placeholder={isLoading ? \"Loading products...\" : placeholder}\n closeOnSelect={closeOnSelect}\n className={className}\n />\n );\n}\n","\"use client\";\n\nimport { useMemo } from \"react\";\nimport { Combobox } from \"@turtleclub/ui\";\nimport { useOpportunities } from \"@turtleclub/hooks\";\n\ninterface OpportunitySelectorProps {\n /** Selected opportunity ID */\n value: string;\n /** Callback when selection changes */\n onValueChange: (value: string) => void;\n /** Placeholder text */\n placeholder?: string;\n /** Whether the selector is disabled */\n disabled?: boolean;\n /** Custom className */\n className?: string;\n /** Whether to close on select */\n closeOnSelect?: boolean;\n /** Whether to show search */\n searchable?: boolean;\n}\n\nexport function OpportunitySelector({\n value,\n onValueChange,\n placeholder = \"Select opportunity\",\n disabled = false,\n className,\n closeOnSelect = true,\n searchable = true,\n}: OpportunitySelectorProps) {\n const { data: opportunitiesData, isLoading } = useOpportunities();\n\n const opportunities = useMemo(\n () => opportunitiesData?.opportunities ?? [],\n [opportunitiesData?.opportunities]\n );\n\n const opportunityOptions = useMemo(() => {\n if (!opportunities || opportunities.length === 0) return [];\n\n const filteredOpportunities = opportunities.filter((opp) => opp.status === \"active\");\n\n return filteredOpportunities.map((opportunity) => ({\n label: opportunity.name || opportunity.shortName,\n value: opportunity.id!,\n description: opportunity.shortName !== opportunity.name ? opportunity.shortName : undefined,\n icon: opportunity.depositTokens?.[0]?.logoUrl\n ? () => (\n <img\n src={opportunity.depositTokens[0].logoUrl}\n alt={opportunity.name}\n width={16}\n height={16}\n className=\"size-4 rounded-full\"\n />\n )\n : undefined,\n }));\n }, [opportunities]);\n\n return (\n <Combobox\n searchable={searchable}\n options={opportunityOptions}\n value={value}\n onValueChange={onValueChange}\n disabled={disabled || isLoading}\n placeholder={isLoading ? \"Loading opportunities...\" : placeholder}\n closeOnSelect={closeOnSelect}\n className={className}\n />\n );\n}\n"],"mappings":"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","names":["Label","useQueryState","jsx","jsxs","useQueryState","jsx","useQueryState","parseAsString","Fragment","jsx","jsx","jsxs","cn","jsx","jsxs","Badge","cn","Fragment","jsx","jsxs","cn","jsx","jsxs","cn","useQueryState","parseAsInteger","useMemo","useQueryState","parseAsString","useMemo","useCallback","useMemo","jsx","useEffect","useMemo","MultiSelect","jsx","useMemo","MultiSelect","jsx","useMemo","MultiSelect","jsx","useMemo","useSupportedChains","jsx","useEffect","useMemo","Combobox","useSupportedTokens","jsx","useMemo","Combobox","useProducts","jsx","useMemo","Combobox","useOpportunities","jsx"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@turtleclub/core",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.17",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@tanstack/react-table": "^8.21.3",
|
|
27
27
|
"@turtleclub/hooks": "0.5.0-beta.14",
|
|
28
|
-
"@turtleclub/ui": "0.7.0-beta.
|
|
28
|
+
"@turtleclub/ui": "0.7.0-beta.12",
|
|
29
29
|
"lucide-react": "^0.454.0",
|
|
30
30
|
"nuqs": "^2.8.5"
|
|
31
31
|
},
|
|
@@ -42,5 +42,5 @@
|
|
|
42
42
|
"publishConfig": {
|
|
43
43
|
"access": "public"
|
|
44
44
|
},
|
|
45
|
-
"gitHead": "
|
|
45
|
+
"gitHead": "ac6e44f7d256ff72bec59a93f91474df91b4b0b5"
|
|
46
46
|
}
|
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import React from "react";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
Popover,
|
|
6
|
+
PopoverContent,
|
|
7
|
+
PopoverTrigger,
|
|
8
|
+
buttonVariants,
|
|
9
|
+
cn,
|
|
10
|
+
Label,
|
|
11
|
+
Badge,
|
|
12
|
+
} from "@turtleclub/ui";
|
|
5
13
|
import { SlidersHorizontal } from "lucide-react";
|
|
6
14
|
import { FilterConfig } from "./FiltersGrid";
|
|
7
15
|
|
|
@@ -45,11 +53,19 @@ export function FiltersPopover({
|
|
|
45
53
|
|
|
46
54
|
return (
|
|
47
55
|
<Popover>
|
|
48
|
-
<PopoverTrigger
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
56
|
+
<PopoverTrigger
|
|
57
|
+
className={cn(
|
|
58
|
+
buttonVariants({
|
|
59
|
+
variant: "default",
|
|
60
|
+
size: "default",
|
|
61
|
+
border: "bordered",
|
|
62
|
+
}),
|
|
63
|
+
"!bg-neutral-alpha-2",
|
|
64
|
+
className
|
|
65
|
+
)}
|
|
66
|
+
>
|
|
67
|
+
<SlidersHorizontal className="mr-2 h-4 w-4" />
|
|
68
|
+
{triggerLabel}
|
|
53
69
|
</PopoverTrigger>
|
|
54
70
|
<PopoverContent
|
|
55
71
|
className={cn("w-auto min-w-[400px] max-w-[600px]", popoverClassName)}
|
|
@@ -18,6 +18,12 @@ export interface FiltersWrapperProps {
|
|
|
18
18
|
className?: string;
|
|
19
19
|
/** Custom className for the active badges section */
|
|
20
20
|
badgesClassName?: string;
|
|
21
|
+
/** Custom className for the slot container */
|
|
22
|
+
slotClassName?: string;
|
|
23
|
+
/** Optional content to render on the left side of filters */
|
|
24
|
+
leftSlot?: React.ReactNode;
|
|
25
|
+
/** Optional content to render on the right side of filters */
|
|
26
|
+
rightSlot?: React.ReactNode;
|
|
21
27
|
|
|
22
28
|
// Grid-specific props
|
|
23
29
|
/** Number of columns for grid layout (default: 3) */
|
|
@@ -88,6 +94,9 @@ export function FiltersWrapper({
|
|
|
88
94
|
layout,
|
|
89
95
|
className,
|
|
90
96
|
badgesClassName,
|
|
97
|
+
slotClassName,
|
|
98
|
+
leftSlot,
|
|
99
|
+
rightSlot,
|
|
91
100
|
columns = 3,
|
|
92
101
|
triggerLabel = "Filters",
|
|
93
102
|
popoverColumns = 2,
|
|
@@ -105,17 +114,23 @@ export function FiltersWrapper({
|
|
|
105
114
|
return (
|
|
106
115
|
<div className={cn("space-y-3", className)}>
|
|
107
116
|
{/* Filter Controls */}
|
|
108
|
-
{
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
117
|
+
<div className={cn("flex items-center gap-2", slotClassName)}>
|
|
118
|
+
{leftSlot}
|
|
119
|
+
<div className="flex-1">
|
|
120
|
+
{layout === "grid" ? (
|
|
121
|
+
<FiltersGrid filters={filters} columns={columns} className={popoverClassName} />
|
|
122
|
+
) : (
|
|
123
|
+
<FiltersPopover
|
|
124
|
+
filters={filters}
|
|
125
|
+
triggerLabel={triggerLabel}
|
|
126
|
+
columns={popoverColumns}
|
|
127
|
+
className={popoverClassName}
|
|
128
|
+
popoverClassName={popoverContentClassName}
|
|
129
|
+
/>
|
|
130
|
+
)}
|
|
131
|
+
</div>
|
|
132
|
+
{rightSlot}
|
|
133
|
+
</div>
|
|
119
134
|
|
|
120
135
|
{/* Active Filter Badges */}
|
|
121
136
|
{showActiveBadges && hasActiveFilters && (
|