mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-03 21:03:08 +00:00
add use ts hooks
This commit is contained in:
@@ -74,10 +74,11 @@
|
|||||||
"react": "19.2.3",
|
"react": "19.2.3",
|
||||||
"react-dom": "19.2.3",
|
"react-dom": "19.2.3",
|
||||||
"react-markdown": "^10.1.0",
|
"react-markdown": "^10.1.0",
|
||||||
"rehype-raw": "^7.0.0",
|
|
||||||
"react-resizable-panels": "^3.0.6",
|
"react-resizable-panels": "^3.0.6",
|
||||||
|
"rehype-raw": "^7.0.0",
|
||||||
"sonner": "^2.0.7",
|
"sonner": "^2.0.7",
|
||||||
"tailwind-merge": "^3.4.0",
|
"tailwind-merge": "^3.4.0",
|
||||||
|
"usehooks-ts": "^3.1.1",
|
||||||
"zustand": "^5.0.9"
|
"zustand": "^5.0.9"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ interface GraphFilterControlsProps {
|
|||||||
filterState: GraphFilterState;
|
filterState: GraphFilterState;
|
||||||
availableCategories: string[];
|
availableCategories: string[];
|
||||||
hasActiveFilter: boolean;
|
hasActiveFilter: boolean;
|
||||||
onSearchQueryChange: (query: string) => void;
|
|
||||||
onCategoriesChange: (categories: string[]) => void;
|
onCategoriesChange: (categories: string[]) => void;
|
||||||
onStatusesChange: (statuses: string[]) => void;
|
onStatusesChange: (statuses: string[]) => void;
|
||||||
onNegativeFilterChange: (isNegative: boolean) => void;
|
onNegativeFilterChange: (isNegative: boolean) => void;
|
||||||
@@ -54,7 +53,6 @@ export function GraphFilterControls({
|
|||||||
filterState,
|
filterState,
|
||||||
availableCategories,
|
availableCategories,
|
||||||
hasActiveFilter,
|
hasActiveFilter,
|
||||||
onSearchQueryChange,
|
|
||||||
onCategoriesChange,
|
onCategoriesChange,
|
||||||
onStatusesChange,
|
onStatusesChange,
|
||||||
onNegativeFilterChange,
|
onNegativeFilterChange,
|
||||||
@@ -62,9 +60,6 @@ export function GraphFilterControls({
|
|||||||
}: GraphFilterControlsProps) {
|
}: GraphFilterControlsProps) {
|
||||||
const { selectedCategories, selectedStatuses, isNegativeFilter } = filterState;
|
const { selectedCategories, selectedStatuses, isNegativeFilter } = filterState;
|
||||||
|
|
||||||
// Suppress unused variable warning - onSearchQueryChange is used by parent for search input
|
|
||||||
void onSearchQueryChange;
|
|
||||||
|
|
||||||
const handleCategoryToggle = (category: string) => {
|
const handleCategoryToggle = (category: string) => {
|
||||||
if (selectedCategories.includes(category)) {
|
if (selectedCategories.includes(category)) {
|
||||||
onCategoriesChange(selectedCategories.filter((c) => c !== category));
|
onCategoriesChange(selectedCategories.filter((c) => c !== category));
|
||||||
@@ -267,6 +262,12 @@ export function GraphFilterControls({
|
|||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<button
|
<button
|
||||||
onClick={() => onNegativeFilterChange(!isNegativeFilter)}
|
onClick={() => onNegativeFilterChange(!isNegativeFilter)}
|
||||||
|
aria-label={
|
||||||
|
isNegativeFilter
|
||||||
|
? 'Switch to show matching nodes'
|
||||||
|
: 'Switch to hide matching nodes'
|
||||||
|
}
|
||||||
|
aria-pressed={isNegativeFilter}
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex items-center gap-1.5 px-2 py-1 rounded text-xs transition-colors',
|
'flex items-center gap-1.5 px-2 py-1 rounded text-xs transition-colors',
|
||||||
isNegativeFilter
|
isNegativeFilter
|
||||||
@@ -289,6 +290,7 @@ export function GraphFilterControls({
|
|||||||
<Switch
|
<Switch
|
||||||
checked={isNegativeFilter}
|
checked={isNegativeFilter}
|
||||||
onCheckedChange={onNegativeFilterChange}
|
onCheckedChange={onNegativeFilterChange}
|
||||||
|
aria-label="Toggle between show and hide filter modes"
|
||||||
className="h-5 w-9 data-[state=checked]:bg-orange-500"
|
className="h-5 w-9 data-[state=checked]:bg-orange-500"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -311,6 +313,7 @@ export function GraphFilterControls({
|
|||||||
size="sm"
|
size="sm"
|
||||||
className="h-8 w-8 p-0 text-muted-foreground hover:text-destructive"
|
className="h-8 w-8 p-0 text-muted-foreground hover:text-destructive"
|
||||||
onClick={onClearFilters}
|
onClick={onClearFilters}
|
||||||
|
aria-label="Clear all filters"
|
||||||
>
|
>
|
||||||
<X className="w-4 h-4" />
|
<X className="w-4 h-4" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
Background,
|
Background,
|
||||||
BackgroundVariant,
|
BackgroundVariant,
|
||||||
MiniMap,
|
MiniMap,
|
||||||
|
Panel,
|
||||||
useNodesState,
|
useNodesState,
|
||||||
useEdgesState,
|
useEdgesState,
|
||||||
ReactFlowProvider,
|
ReactFlowProvider,
|
||||||
@@ -30,6 +31,9 @@ import {
|
|||||||
type NodeActionCallbacks,
|
type NodeActionCallbacks,
|
||||||
} from './hooks';
|
} from './hooks';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
import { useDebounceValue } from 'usehooks-ts';
|
||||||
|
import { SearchX } from 'lucide-react';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
|
||||||
// Define custom node and edge types - using any to avoid React Flow's strict typing
|
// Define custom node and edge types - using any to avoid React Flow's strict typing
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
@@ -71,9 +75,12 @@ function GraphCanvasInner({
|
|||||||
const [selectedStatuses, setSelectedStatuses] = useState<string[]>([]);
|
const [selectedStatuses, setSelectedStatuses] = useState<string[]>([]);
|
||||||
const [isNegativeFilter, setIsNegativeFilter] = useState(false);
|
const [isNegativeFilter, setIsNegativeFilter] = useState(false);
|
||||||
|
|
||||||
|
// Debounce search query for performance with large graphs
|
||||||
|
const [debouncedSearchQuery] = useDebounceValue(searchQuery, 200);
|
||||||
|
|
||||||
// Combined filter state
|
// Combined filter state
|
||||||
const filterState: GraphFilterState = {
|
const filterState: GraphFilterState = {
|
||||||
searchQuery,
|
searchQuery: debouncedSearchQuery,
|
||||||
selectedCategories,
|
selectedCategories,
|
||||||
selectedStatuses,
|
selectedStatuses,
|
||||||
isNegativeFilter,
|
isNegativeFilter,
|
||||||
@@ -196,7 +203,6 @@ function GraphCanvasInner({
|
|||||||
filterState={filterState}
|
filterState={filterState}
|
||||||
availableCategories={filterResult.availableCategories}
|
availableCategories={filterResult.availableCategories}
|
||||||
hasActiveFilter={filterResult.hasActiveFilter}
|
hasActiveFilter={filterResult.hasActiveFilter}
|
||||||
onSearchQueryChange={onSearchQueryChange}
|
|
||||||
onCategoriesChange={setSelectedCategories}
|
onCategoriesChange={setSelectedCategories}
|
||||||
onStatusesChange={setSelectedStatuses}
|
onStatusesChange={setSelectedStatuses}
|
||||||
onNegativeFilterChange={setIsNegativeFilter}
|
onNegativeFilterChange={setIsNegativeFilter}
|
||||||
@@ -204,6 +210,24 @@ function GraphCanvasInner({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<GraphLegend />
|
<GraphLegend />
|
||||||
|
|
||||||
|
{/* Empty state when all nodes are filtered out */}
|
||||||
|
{filterResult.hasActiveFilter && filterResult.matchedNodeIds.size === 0 && (
|
||||||
|
<Panel position="top-center" className="mt-20">
|
||||||
|
<div className="flex flex-col items-center gap-3 p-6 rounded-lg bg-popover/95 backdrop-blur-sm border border-border shadow-lg text-popover-foreground">
|
||||||
|
<SearchX className="w-10 h-10 text-muted-foreground" />
|
||||||
|
<div className="text-center">
|
||||||
|
<p className="text-sm font-medium">No matching tasks</p>
|
||||||
|
<p className="text-xs text-muted-foreground mt-1">
|
||||||
|
Try adjusting your filters or search query
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<Button variant="outline" size="sm" onClick={handleClearFilters} className="mt-1">
|
||||||
|
Clear Filters
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Panel>
|
||||||
|
)}
|
||||||
</ReactFlow>
|
</ReactFlow>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
24
package-lock.json
generated
24
package-lock.json
generated
@@ -115,6 +115,7 @@
|
|||||||
"rehype-raw": "^7.0.0",
|
"rehype-raw": "^7.0.0",
|
||||||
"sonner": "^2.0.7",
|
"sonner": "^2.0.7",
|
||||||
"tailwind-merge": "^3.4.0",
|
"tailwind-merge": "^3.4.0",
|
||||||
|
"usehooks-ts": "^3.1.1",
|
||||||
"zustand": "^5.0.9"
|
"zustand": "^5.0.9"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -1215,7 +1216,7 @@
|
|||||||
},
|
},
|
||||||
"node_modules/@electron/node-gyp": {
|
"node_modules/@electron/node-gyp": {
|
||||||
"version": "10.2.0-electron.1",
|
"version": "10.2.0-electron.1",
|
||||||
"resolved": "git+https://github.com/electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2",
|
"resolved": "git+ssh://git@github.com/electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2",
|
||||||
"integrity": "sha512-4MSBTT8y07YUDqf69/vSh80Hh791epYqGtWHO3zSKhYFwQg+gx9wi1PqbqP6YqC4WMsNxZ5l9oDmnWdK5pfCKQ==",
|
"integrity": "sha512-4MSBTT8y07YUDqf69/vSh80Hh791epYqGtWHO3zSKhYFwQg+gx9wi1PqbqP6YqC4WMsNxZ5l9oDmnWdK5pfCKQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -11446,6 +11447,12 @@
|
|||||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/lodash.debounce": {
|
||||||
|
"version": "4.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
|
||||||
|
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/lodash.merge": {
|
"node_modules/lodash.merge": {
|
||||||
"version": "4.6.2",
|
"version": "4.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||||
@@ -15889,6 +15896,21 @@
|
|||||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/usehooks-ts": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/usehooks-ts/-/usehooks-ts-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-I4diPp9Cq6ieSUH2wu+fDAVQO43xwtulo+fKEidHUwZPnYImbtkTjzIJYcDcJqxgmX31GVqNFURodvcgHcW0pA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"lodash.debounce": "^4.0.8"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.15.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17 || ^18 || ^19 || ^19.0.0-rc"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/utf8-byte-length": {
|
"node_modules/utf8-byte-length": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz",
|
||||||
|
|||||||
Reference in New Issue
Block a user