Merge ticket/024-popout-tab-independent-rolling-range-and-controls
Resolves conflicts with tickets 006 (header-click collapse) and 009 (all-dashboards print view) in CollapsibleSection.tsx, Dashboards.tsx, and Metrics.tsx: dropped a spurious onToggle addition from the worktree's stale base that duplicated already-merged header-collapse behavior, keeping only the activePreset plumbing; wired activePreset through Dashboards' non-empty-state header too, which the source branch (based on a pre-print-view commit) never had to touch. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XFUNZwCvz3kcod3352ZR2o
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { useState } from 'react'
|
||||
import type { TimeRange } from './LogTimeline'
|
||||
import type { TimeRange, TimeRangePresetId } from './LogTimeline'
|
||||
|
||||
/// Shared look for a primary section's title bar: shaded so it reads as a
|
||||
/// distinct band rather than blending into the section body below it.
|
||||
@@ -98,23 +98,34 @@ export function handleSectionHeaderKeyDown(onToggle: () => void) {
|
||||
/// The four primary sections poppable into their own tab; matches the `?section=` query param a standalone page reads.
|
||||
export type PopOutSectionId = 'log' | 'params' | 'metrics' | 'dashboards'
|
||||
|
||||
function popOutUrl(section: PopOutSectionId, range?: TimeRange | null): string {
|
||||
/// A rolling preset (anything but a manually dragged/fixed range) is carried over by id so the popped-out tab can resolve and keep re-resolving its own independent window of the same size, instead of freezing whatever absolute bounds happened to be active at click time. A fixed/dragged range has no such id, so it's carried over as absolute bounds, same as before.
|
||||
function popOutUrl(section: PopOutSectionId, options?: { range?: TimeRange | null; presetId?: TimeRangePresetId | null }): string {
|
||||
const query = new URLSearchParams({ section })
|
||||
if (range) {
|
||||
query.set('startMs', String(range.startMs))
|
||||
query.set('endMs', String(range.endMs))
|
||||
if (options?.presetId != null) {
|
||||
query.set('preset', options.presetId)
|
||||
} else if (options?.range) {
|
||||
query.set('startMs', String(options.range.startMs))
|
||||
query.set('endMs', String(options.range.endMs))
|
||||
}
|
||||
return `?${query.toString()}`
|
||||
}
|
||||
|
||||
/// `range` is only needed for sections without their own LogTimeline — the popped-out page can't pick a range itself, so the current one is carried over via query params.
|
||||
export function PopOutButton({ section, range }: { section: PopOutSectionId; range?: TimeRange | null }) {
|
||||
/// `range`/`activePreset` are only needed for sections without their own LogTimeline — the popped-out page can't pick a range itself until it mounts, so the current one is carried over via query params.
|
||||
export function PopOutButton({
|
||||
section,
|
||||
range,
|
||||
activePreset,
|
||||
}: {
|
||||
section: PopOutSectionId
|
||||
range?: TimeRange | null
|
||||
activePreset?: TimeRangePresetId | null
|
||||
}) {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
window.open(popOutUrl(section, range), '_blank')
|
||||
window.open(popOutUrl(section, { range, presetId: activePreset }), '_blank')
|
||||
}}
|
||||
title="Open in new tab"
|
||||
aria-label="Open in new tab"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import type { TimeRange } from './LogTimeline'
|
||||
import type { TimeRange, TimeRangePresetId } from './LogTimeline'
|
||||
import { PanelChart } from './PanelChart'
|
||||
import {
|
||||
ApiError,
|
||||
@@ -142,7 +142,7 @@ function AllDashboardsView({ dashboards, range }: { dashboards: Dashboard[]; ran
|
||||
)
|
||||
}
|
||||
|
||||
export function Dashboards({ range }: { range: TimeRange | null }) {
|
||||
export function Dashboards({ range, activePreset }: { range: TimeRange | null; activePreset?: TimeRangePresetId | null }) {
|
||||
const [activeProfile, setActiveProfile] = useState<string | null>(null)
|
||||
const [dashboards, setDashboards] = useState<Dashboard[]>([])
|
||||
const [selectedId, setSelectedId] = useState<string | null>(null)
|
||||
@@ -278,7 +278,7 @@ export function Dashboards({ range }: { range: TimeRange | null }) {
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<CollapseToggle collapsed={collapsed} />
|
||||
<PopOutButton section="dashboards" range={range} />
|
||||
<PopOutButton section="dashboards" range={range} activePreset={activePreset} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-3 text-xs text-text-faint">
|
||||
@@ -304,7 +304,7 @@ export function Dashboards({ range }: { range: TimeRange | null }) {
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<CollapseToggle collapsed={collapsed} />
|
||||
<PopOutButton section="dashboards" range={range} />
|
||||
<PopOutButton section="dashboards" range={range} activePreset={activePreset} />
|
||||
</div>
|
||||
</div>
|
||||
{error && !collapsed && (
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import type { TimeRange } from './LogTimeline'
|
||||
import type { TimeRange, TimeRangePresetId } from './LogTimeline'
|
||||
import {
|
||||
ApiError,
|
||||
declareMetric,
|
||||
@@ -78,7 +78,7 @@ function sparkline(values: BucketValue[], range: TimeRange | null): string {
|
||||
return slots.map((count) => SPARK_CHARS[Math.round((count / max) * (SPARK_CHARS.length - 1))]).join('')
|
||||
}
|
||||
|
||||
export function Metrics({ range }: { range: TimeRange | null }) {
|
||||
export function Metrics({ range, activePreset }: { range: TimeRange | null; activePreset?: TimeRangePresetId | null }) {
|
||||
const [metrics, setMetrics] = useState<MetricDeclaration[]>([])
|
||||
const [selected, setSelected] = useState<string | null>(null)
|
||||
const [bucketValues, setBucketValues] = useState<BucketValue[]>([])
|
||||
@@ -198,7 +198,7 @@ export function Metrics({ range }: { range: TimeRange | null }) {
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<CollapseToggle collapsed={collapsed} />
|
||||
<PopOutButton section="metrics" range={range} />
|
||||
<PopOutButton section="metrics" range={range} activePreset={activePreset} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-4 p-3" hidden={collapsed}>
|
||||
|
||||
Reference in New Issue
Block a user