fix: align route priority drag preview (#375)
This commit is contained in:
@@ -213,5 +213,20 @@ describe('RouteCard', () => {
|
|||||||
expect(text).toContain('P1 · 1');
|
expect(text).toContain('P1 · 1');
|
||||||
expect(text).toContain('user_a');
|
expect(text).toContain('user_a');
|
||||||
expect(text).toContain('user_b');
|
expect(text).toContain('user_b');
|
||||||
|
|
||||||
|
const p0RailNode = root.root.find((node) => (
|
||||||
|
node.type === 'div'
|
||||||
|
&& collectText(node) === 'P0 · 1'
|
||||||
|
&& node.props?.style?.borderRadius === 999
|
||||||
|
));
|
||||||
|
const p1RailNode = root.root.find((node) => (
|
||||||
|
node.type === 'div'
|
||||||
|
&& collectText(node) === 'P1 · 1'
|
||||||
|
&& node.props?.style?.borderRadius === 999
|
||||||
|
));
|
||||||
|
|
||||||
|
expect(p0RailNode.props.style.background).not.toBe('var(--color-bg)');
|
||||||
|
expect(p1RailNode.props.style.background).not.toBe('var(--color-bg)');
|
||||||
|
expect(p0RailNode.props.style.color).not.toBe(p1RailNode.props.style.color);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -42,11 +42,13 @@ import {
|
|||||||
isExplicitGroupRoute,
|
isExplicitGroupRoute,
|
||||||
resolveRouteTitle,
|
resolveRouteTitle,
|
||||||
resolveRouteIcon,
|
resolveRouteIcon,
|
||||||
|
getPriorityTagStyle,
|
||||||
} from './utils.js';
|
} from './utils.js';
|
||||||
import {
|
import {
|
||||||
buildPriorityBuckets,
|
buildPriorityBuckets,
|
||||||
} from './priorityBuckets.js';
|
} from './priorityBuckets.js';
|
||||||
import {
|
import {
|
||||||
|
buildPriorityDragPreviewStyle,
|
||||||
buildPriorityRailSections,
|
buildPriorityRailSections,
|
||||||
createPriorityRailNewLayerId,
|
createPriorityRailNewLayerId,
|
||||||
isPriorityRailNewLayerId,
|
isPriorityRailNewLayerId,
|
||||||
@@ -160,15 +162,18 @@ function PriorityRailNewLayerRow({
|
|||||||
function PriorityDragPreview({
|
function PriorityDragPreview({
|
||||||
channel,
|
channel,
|
||||||
displayPriority,
|
displayPriority,
|
||||||
|
activeRowWidth,
|
||||||
}: {
|
}: {
|
||||||
channel: RouteChannel;
|
channel: RouteChannel;
|
||||||
displayPriority: number;
|
displayPriority: number;
|
||||||
|
activeRowWidth?: number | null;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: 'inline-flex',
|
display: 'inline-flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
justifyContent: 'space-between',
|
||||||
gap: 8,
|
gap: 8,
|
||||||
padding: '8px 12px',
|
padding: '8px 12px',
|
||||||
borderRadius: 'var(--radius-md)',
|
borderRadius: 'var(--radius-md)',
|
||||||
@@ -177,6 +182,7 @@ function PriorityDragPreview({
|
|||||||
boxShadow: 'var(--shadow-sm)',
|
boxShadow: 'var(--shadow-sm)',
|
||||||
color: 'var(--color-text-primary)',
|
color: 'var(--color-text-primary)',
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
|
...buildPriorityDragPreviewStyle(activeRowWidth),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@@ -275,6 +281,7 @@ function RouteCardInner({
|
|||||||
const priorityBuckets = buildPriorityBuckets(channels || []);
|
const priorityBuckets = buildPriorityBuckets(channels || []);
|
||||||
const priorityRailSections = buildPriorityRailSections(channels || []);
|
const priorityRailSections = buildPriorityRailSections(channels || []);
|
||||||
const [activeDragChannelId, setActiveDragChannelId] = useState<number | null>(null);
|
const [activeDragChannelId, setActiveDragChannelId] = useState<number | null>(null);
|
||||||
|
const [activeDragRowWidth, setActiveDragRowWidth] = useState<number | null>(null);
|
||||||
const [dragOverId, setDragOverId] = useState<string | number | null>(null);
|
const [dragOverId, setDragOverId] = useState<string | number | null>(null);
|
||||||
const activeDragChannel = activeDragChannelId == null
|
const activeDragChannel = activeDragChannelId == null
|
||||||
? null
|
? null
|
||||||
@@ -291,12 +298,14 @@ function RouteCardInner({
|
|||||||
|
|
||||||
const clearDragState = () => {
|
const clearDragState = () => {
|
||||||
setActiveDragChannelId(null);
|
setActiveDragChannelId(null);
|
||||||
|
setActiveDragRowWidth(null);
|
||||||
setDragOverId(null);
|
setDragOverId(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDragStart = (event: DragStartEvent) => {
|
const handleDragStart = (event: DragStartEvent) => {
|
||||||
const nextId = Number(event.active.id);
|
const nextId = Number(event.active.id);
|
||||||
setActiveDragChannelId(Number.isFinite(nextId) ? nextId : null);
|
setActiveDragChannelId(Number.isFinite(nextId) ? nextId : null);
|
||||||
|
setActiveDragRowWidth(event.active.rect.current.initial?.width ?? null);
|
||||||
setDragOverId(event.active.id);
|
setDragOverId(event.active.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -670,6 +679,7 @@ function RouteCardInner({
|
|||||||
const hoveredExistingLayer = activeDragChannelId != null && hoveredBucketIndex === bucketIndex;
|
const hoveredExistingLayer = activeDragChannelId != null && hoveredBucketIndex === bucketIndex;
|
||||||
const hoveredCrossLayer = hoveredExistingLayer && activeDragBucketIndex !== bucketIndex;
|
const hoveredCrossLayer = hoveredExistingLayer && activeDragBucketIndex !== bucketIndex;
|
||||||
const hoveredNewLayer = activeDragChannelId != null && hoveredNewLayerBucketIndex === bucketIndex;
|
const hoveredNewLayer = activeDragChannelId != null && hoveredNewLayerBucketIndex === bucketIndex;
|
||||||
|
const railTone = getPriorityTagStyle(bucketIndex);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -729,11 +739,11 @@ function RouteCardInner({
|
|||||||
minWidth: 72,
|
minWidth: 72,
|
||||||
padding: '6px 10px',
|
padding: '6px 10px',
|
||||||
borderRadius: 999,
|
borderRadius: 999,
|
||||||
border: `1px solid ${hoveredExistingLayer ? 'var(--color-primary)' : 'var(--color-border)'}`,
|
border: `1px solid ${hoveredExistingLayer ? 'var(--color-primary)' : 'color-mix(in srgb, currentColor 24%, transparent)'}`,
|
||||||
background: hoveredExistingLayer
|
background: hoveredExistingLayer
|
||||||
? 'color-mix(in srgb, var(--color-primary) 10%, var(--color-bg))'
|
? 'color-mix(in srgb, var(--color-primary) 10%, var(--color-bg))'
|
||||||
: 'var(--color-bg)',
|
: railTone.background,
|
||||||
color: hoveredExistingLayer ? 'var(--color-primary)' : 'var(--color-text-secondary)',
|
color: hoveredExistingLayer ? 'var(--color-primary)' : railTone.color,
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
@@ -801,6 +811,7 @@ function RouteCardInner({
|
|||||||
<PriorityDragPreview
|
<PriorityDragPreview
|
||||||
channel={activeDragChannel}
|
channel={activeDragChannel}
|
||||||
displayPriority={Math.max(0, activeDragBucketIndex)}
|
displayPriority={Math.max(0, activeDragBucketIndex)}
|
||||||
|
activeRowWidth={activeDragRowWidth}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</DragOverlay>
|
</DragOverlay>
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ export function SortableChannelRow({
|
|||||||
const rowStyle: CSSProperties = {
|
const rowStyle: CSSProperties = {
|
||||||
transform: CSS.Transform.toString(transform),
|
transform: CSS.Transform.toString(transform),
|
||||||
transition,
|
transition,
|
||||||
opacity: isDragging ? 0.72 : channel.enabled === false ? 0.5 : 1,
|
opacity: isDragging ? (mobile ? 0.72 : 0) : channel.enabled === false ? 0.5 : 1,
|
||||||
zIndex: isDragging ? 10 : 1,
|
zIndex: isDragging ? 10 : 1,
|
||||||
display: 'grid',
|
display: 'grid',
|
||||||
gridTemplateColumns: managementLocked || mobile ? 'minmax(0, 1fr)' : 'minmax(0, 1fr) auto auto auto',
|
gridTemplateColumns: managementLocked || mobile ? 'minmax(0, 1fr)' : 'minmax(0, 1fr) auto auto auto',
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { describe, expect, it } from 'vitest';
|
import { describe, expect, it } from 'vitest';
|
||||||
import {
|
import {
|
||||||
applyPriorityRailDrop,
|
applyPriorityRailDrop,
|
||||||
|
buildPriorityDragPreviewStyle,
|
||||||
buildPriorityRailDragTargets,
|
buildPriorityRailDragTargets,
|
||||||
buildPriorityRailSections,
|
buildPriorityRailSections,
|
||||||
createPriorityRailNewLayerId,
|
createPriorityRailNewLayerId,
|
||||||
@@ -74,4 +75,11 @@ describe('priorityRail helpers', () => {
|
|||||||
{ id: 21, priority: 2 },
|
{ id: 21, priority: 2 },
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('uses the active row width for drag preview alignment when available', () => {
|
||||||
|
expect(buildPriorityDragPreviewStyle(640)).toMatchObject({
|
||||||
|
width: 640,
|
||||||
|
maxWidth: 'calc(100vw - 32px)',
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { CSSProperties } from 'react';
|
||||||
import type { PriorityRailDragTarget, PriorityRailSection } from './types.js';
|
import type { PriorityRailDragTarget, PriorityRailSection } from './types.js';
|
||||||
|
|
||||||
type PriorityRailChannelLike = {
|
type PriorityRailChannelLike = {
|
||||||
@@ -117,3 +118,11 @@ export function applyPriorityRailDrop<T extends PriorityRailChannelLike>(
|
|||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function buildPriorityDragPreviewStyle(activeRowWidth?: number | null): CSSProperties {
|
||||||
|
const resolvedWidth = Number.isFinite(activeRowWidth ?? Number.NaN) ? activeRowWidth ?? undefined : undefined;
|
||||||
|
return {
|
||||||
|
width: resolvedWidth,
|
||||||
|
maxWidth: 'calc(100vw - 32px)',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user