diff --git a/.changeset/spotty-suits-own.md b/.changeset/spotty-suits-own.md
new file mode 100644
index 000000000..65e20d100
--- /dev/null
+++ b/.changeset/spotty-suits-own.md
@@ -0,0 +1,5 @@
+---
+"@hyperdx/app": minor
+---
+
+Feat: add highlight animation for recently moved filter checkboxes
diff --git a/packages/app/src/components/DBSearchPageFilters.tsx b/packages/app/src/components/DBSearchPageFilters.tsx
index 2c6489e5e..6482fc28e 100644
--- a/packages/app/src/components/DBSearchPageFilters.tsx
+++ b/packages/app/src/components/DBSearchPageFilters.tsx
@@ -1,4 +1,13 @@
-import { memo, useCallback, useEffect, useId, useMemo, useState } from 'react';
+import {
+ memo,
+ useCallback,
+ useEffect,
+ useId,
+ useMemo,
+ useRef,
+ useState,
+} from 'react';
+import cx from 'classnames';
import {
TableMetadata,
tcFromSource,
@@ -65,6 +74,7 @@ type FilterCheckboxProps = {
onClickOnly?: VoidFunction;
onClickExclude?: VoidFunction;
onClickPin: VoidFunction;
+ className?: string;
};
export const TextButton = ({
@@ -103,6 +113,7 @@ export const FilterCheckbox = ({
onClickOnly,
onClickExclude,
onClickPin,
+ className,
}: FilterCheckboxProps) => {
return (
>(new Set());
useEffect(() => {
if (isDefaultExpanded) {
@@ -298,6 +311,27 @@ export const FilterGroup = ({
totalFiltersSize,
]);
+ // Simple highlight animation when checkbox is checked
+ const handleChange = useCallback(
+ (value: string) => {
+ const wasIncluded = selectedValues.included.has(value);
+
+ // If checking (not unchecking), trigger highlight animation
+ if (!wasIncluded) {
+ setRecentlyMoved(prev => new Set(prev).add(value));
+ setTimeout(() => {
+ setRecentlyMoved(prev => {
+ const newSet = new Set(prev);
+ newSet.delete(value);
+ return newSet;
+ });
+ }, 600);
+ }
+
+ onChange(value);
+ },
+ [onChange, selectedValues],
+ );
const showShowMoreButton =
!search &&
augmentedOptions.length > MAX_FILTER_GROUP_ITEMS &&
@@ -403,6 +437,9 @@ export const FilterGroup = ({
key={option.value}
label={option.label}
pinned={isPinned(option.value)}
+ className={
+ recentlyMoved.has(option.value) ? classes.recentlyMoved : ''
+ }
value={
selectedValues.included.has(option.value)
? 'included'
@@ -410,7 +447,7 @@ export const FilterGroup = ({
? 'excluded'
: false
}
- onChange={() => onChange(option.value)}
+ onChange={() => handleChange(option.value)}
onClickOnly={() => onOnlyClick(option.value)}
onClickExclude={() => onExcludeClick(option.value)}
onClickPin={() => onPinClick(option.value)}
diff --git a/packages/app/styles/SearchPage.module.scss b/packages/app/styles/SearchPage.module.scss
index 9854b17d8..cb664da39 100644
--- a/packages/app/styles/SearchPage.module.scss
+++ b/packages/app/styles/SearchPage.module.scss
@@ -130,3 +130,24 @@
transform: rotate(0deg);
}
}
+
+.recentlyMoved {
+ animation: highlight 0.6s ease-out;
+}
+
+@keyframes highlight {
+ 0% {
+ background-color: $slate-950;
+ transform: scale(1.02);
+ }
+
+ 50% {
+ background-color: $slate-900;
+ transform: scale(1.01);
+ }
+
+ 100% {
+ background-color: transparent;
+ transform: scale(1);
+ }
+}