Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
new-api
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Unverified
Commit
de0d6ac9
authored
Jun 24, 2026
by
yyhhyyyyyy
Committed by
GitHub
Jun 24, 2026
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix(web): sync channel card selection state (#5700)
parent
f4473d96
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
30 additions
and
13 deletions
+30
-13
web/default/src/components/data-table/index.ts
+2
-2
web/default/src/components/data-table/layout/card-grid.tsx
+11
-5
web/default/src/features/channels/components/channel-card.tsx
+14
-5
web/default/src/features/channels/components/channels-table.tsx
+3
-1
No files found.
web/default/src/components/data-table/index.ts
View file @
de0d6ac9
...
...
@@ -61,7 +61,7 @@ export {
export
{
useDebouncedColumnFilter
}
from
'./hooks/use-debounced-column-filter'
export
const
DISABLED_ROW_DESKTOP
=
'[--data-table-card-bg:var(--table-disabled)] hover:[--data-table-card-bg:var(--table-disabled-hover)] [background-color:var(--table-disabled)] hover:[background-color:var(--table-disabled-hover)] [&>td:first-child]:[border-left-color:var(--table-disabled-border)] [&>td:first-child]:border-l-4 [&>td:first-child]:pl-1'
'[--data-table-card-bg:var(--table-disabled)] hover:[--data-table-card-bg:var(--table-disabled-hover)]
data-[state=selected]:![--data-table-card-bg:var(--table-disabled)] data-[state=selected]:hover:![--data-table-card-bg:var(--table-disabled-hover)]
[background-color:var(--table-disabled)] hover:[background-color:var(--table-disabled-hover)] [&>td:first-child]:[border-left-color:var(--table-disabled-border)] [&>td:first-child]:border-l-4 [&>td:first-child]:pl-1'
export
const
DISABLED_ROW_MOBILE
=
'[--data-table-card-bg:var(--table-disabled)] [background-color:var(--table-disabled)]'
'[--data-table-card-bg:var(--table-disabled)]
data-[state=selected]:![--data-table-card-bg:var(--table-disabled)]
[background-color:var(--table-disabled)]'
web/default/src/components/data-table/layout/card-grid.tsx
View file @
de0d6ac9
...
...
@@ -41,6 +41,10 @@ export type DataTableCardHelpers = {
* Provided so custom renderers can match the default layout decision.
*/
compact
:
boolean
/**
* Row selection state captured before entering memoized custom card renderers.
*/
isSelected
:
boolean
}
export
interface
DataTableCardGridProps
<
TData
>
{
...
...
@@ -80,7 +84,7 @@ function CardGridSkeleton(props: {
{
[
1
,
2
,
3
,
4
,
5
,
6
].
map
((
i
)
=>
(
<
div
key=
{
`${prefix}-${i}`
}
className=
'space-y-3 rounded-lg border
[background-color:var(--table-row)]
p-3'
className=
'space-y-3 rounded-lg border
bg-(--table-row)
p-3'
>
<
div
className=
'flex items-center justify-between gap-2'
>
<
Skeleton
className=
'h-4 w-32'
/>
...
...
@@ -108,8 +112,8 @@ function CardGridSkeleton(props: {
* the card view reusable across any table with zero per-feature work while
* still allowing a bespoke card design when desired.
*
*
Selection (the `select` column) is intentionally not rendered in card mode;
*
bulk selection remains a table-mode capability
.
*
The default generic card omits the `select` column. Custom `renderCard`
*
implementations can use `helpers.isSelected` to keep selection UI in sync
.
*/
export
function
DataTableCardGrid
<
TData
>
(
props
:
DataTableCardGridProps
<
TData
>
)
{
const
{
t
}
=
useTranslation
()
...
...
@@ -156,17 +160,19 @@ export function DataTableCardGrid<TData>(props: DataTableCardGridProps<TData>) {
<
div
className=
{
props
.
gridClassName
??
DEFAULT_GRID_CLASSNAME
}
>
{
rows
.
map
((
row
)
=>
{
const
key
=
props
.
getRowKey
?
props
.
getRowKey
(
row
)
:
row
.
id
const
isSelected
=
row
.
getIsSelected
()
return
(
<
div
key=
{
key
}
data
-
slot=
'data-table-card'
data
-
state=
{
isSelected
?
'selected'
:
undefined
}
className=
{
cn
(
'rounded-lg border
[background-color:var(--data-table-card-bg,var(--table-row))] px-3 py-2.5
'
,
'rounded-lg border
bg-(--data-table-card-bg,var(--table-row)) px-3 py-2.5 transition-[background-color,border-color] duration-150 data-[state=selected]:[--data-table-card-bg:color-mix(in_oklch,var(--primary)_7%,var(--table-row))] data-[state=selected]:border-primary/40
'
,
props
.
getRowClassName
?.(
row
)
)
}
>
{
props
.
renderCard
?
(
props
.
renderCard
(
row
,
{
compact
})
props
.
renderCard
(
row
,
{
compact
,
isSelected
})
)
:
(
<
CardRowContent
row=
{
row
}
compact=
{
compact
}
/>
)
}
...
...
web/default/src/features/channels/components/channel-card.tsx
View file @
de0d6ac9
...
...
@@ -37,7 +37,13 @@ const SENSITIVE_MASK = '••••'
* priority/weight spinners, balance refresh, response/test times, tag
* expand-collapse, and the per-row (or per-tag) actions menu.
*/
function
ChannelCardComponent
({
row
}:
{
row
:
Row
<
Channel
>
})
{
function
ChannelCardComponent
({
row
,
isSelected
,
}:
{
row
:
Row
<
Channel
>
isSelected
:
boolean
})
{
const
{
t
}
=
useTranslation
()
const
{
sensitiveVisible
}
=
useChannels
()
const
isTagRow
=
isTagAggregateRow
(
row
.
original
)
...
...
@@ -81,16 +87,19 @@ function ChannelCardComponent({ row }: { row: Row<Channel> }) {
row
.
original
.
status
!==
CHANNEL_STATUS
.
MANUAL_DISABLED
)
return
(
<
div
className=
'flex flex-col gap-3'
>
<
div
data
-
state=
{
isSelected
?
'selected'
:
undefined
}
className=
'flex flex-col gap-3'
>
{
/* Row 1: selection + type, with status badge + actions menu */
}
<
div
className=
'flex items-center justify-between gap-2'
>
<
div
className=
'flex min-w-0 flex-1 items-center gap-2'
>
{
!
isTagRow
&&
selectCell
&&
(
<
span
className=
'
flex-
shrink-0'
>
{
selectCell
}
</
span
>
<
span
className=
'shrink-0'
>
{
selectCell
}
</
span
>
)
}
<
div
className=
'min-w-0 overflow-hidden'
>
{
typeCell
}
</
div
>
</
div
>
<
div
className=
'flex
flex-
shrink-0 items-center gap-1.5'
>
<
div
className=
'flex shrink-0 items-center gap-1.5'
>
{
showStatusBadge
&&
statusCell
}
<
ChannelRowActionsLayoutContext
.
Provider
value=
'card'
>
{
actionsCell
}
...
...
@@ -122,7 +131,7 @@ function ChannelCardComponent({ row }: { row: Row<Channel> }) {
{
/* Right column (sits on the right, content left-aligned). A single
grid with content-sized columns keeps Priority/Weight and
Response/Last Tested aligned without wasting horizontal space. */
}
<
div
className=
'grid
flex-
shrink-0 grid-cols-[auto_auto] items-center gap-x-3 gap-y-1'
>
<
div
className=
'grid shrink-0 grid-cols-[auto_auto] items-center gap-x-3 gap-y-1'
>
<
span
className=
{
labelClass
}
>
{
t
(
'Priority'
)
}
</
span
>
<
span
className=
{
labelClass
}
>
{
t
(
'Weight'
)
}
</
span
>
<
div
className=
'flex justify-start'
>
{
priorityCell
}
</
div
>
...
...
web/default/src/features/channels/components/channels-table.tsx
View file @
de0d6ac9
...
...
@@ -370,7 +370,9 @@ export function ChannelsTable() {
skeletonKeyPrefix=
'channel-skeleton'
enableCardView
viewModeStorageKey=
{
CHANNELS_VIEW_MODE_STORAGE_KEY
}
renderCard=
{
(
row
)
=>
<
ChannelCard
row=
{
row
}
/>
}
renderCard=
{
(
row
,
{
isSelected
})
=>
(
<
ChannelCard
row=
{
row
}
isSelected=
{
isSelected
}
/>
)
}
cardGridClassName=
'grid grid-cols-1 gap-3 sm:gap-4 lg:grid-cols-3'
applyHeaderSize
toolbarProps=
{
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment