Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 84 additions & 60 deletions site/components/InteractiveGraphics/InteractiveGraphics.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,15 @@ export const InteractiveGraphics = ({
onObjectClicked,
objectLimit,
height = 600,
stepMetadata,
alwaysShowToolbar = false,
}: {
graphics: GraphicsObject
onObjectClicked?: (event: GraphicsObjectClickEvent) => void
objectLimit?: number
height?: number
stepMetadata?: Array<{ title: string }>
alwaysShowToolbar?: boolean
}) => {
const [activeLayers, setActiveLayers] = useState<string[] | null>(null)
const [activeStep, setActiveStep] = useState<number | null>(null)
Expand Down Expand Up @@ -287,7 +291,13 @@ export const InteractiveGraphics = ({
onObjectClicked: onObjectClicked,
}

const showToolbar = availableLayers.length > 1 || maxStep > 0
const showToolbar =
alwaysShowToolbar || availableLayers.length > 1 || maxStep > 0

const stepTitle =
maxStep > 0
? stepMetadata?.[showLastStep ? maxStep : (activeStep ?? -1)]?.title
: undefined

// Use custom hooks for visibility checks and filtering
const isPointOnScreen = useIsPointOnScreen(realToScreen, size)
Expand Down Expand Up @@ -386,72 +396,86 @@ export const InteractiveGraphics = ({
return (
<div>
{showToolbar && (
<div style={{ margin: 8 }}>
{availableLayers.length > 1 && (
<select
value={activeLayers ? activeLayers[0] : ""}
onChange={(e) => {
const value = e.target.value
setActiveLayers(value === "" ? null : [value])
}}
style={{ marginRight: 8 }}
>
<option value="">All Layers</option>
{availableLayers.map((layer) => (
<option key={layer} value={layer}>
{layer}
</option>
))}
</select>
)}

{maxStep > 0 && (
<div
style={{ display: "inline-flex", alignItems: "center", gap: 8 }}
>
Step:
<input
type="number"
min={0}
max={maxStep}
value={activeStep ?? 0}
<div
style={{
margin: 8,
display: "flex",
alignItems: "center",
gap: 8,
}}
>
<div style={{ display: "inline-flex", alignItems: "center", gap: 8 }}>
{availableLayers.length > 1 && (
<select
value={activeLayers ? activeLayers[0] : ""}
onChange={(e) => {
const value = parseInt(e.target.value)
setShowLastStep(false)
setActiveStep(Number.isNaN(value) ? null : value)
const value = e.target.value
setActiveLayers(value === "" ? null : [value])
}}
disabled={activeStep === null}
/>
<label>
style={{ marginRight: 8 }}
>
<option value="">All Layers</option>
{availableLayers.map((layer) => (
<option key={layer} value={layer}>
{layer}
</option>
))}
</select>
)}

{maxStep > 0 && (
<div
style={{ display: "inline-flex", alignItems: "center", gap: 8 }}
>
Step:
<input
type="checkbox"
style={{ marginRight: 4 }}
checked={activeStep !== null}
type="number"
min={0}
max={maxStep}
value={activeStep ?? 0}
onChange={(e) => {
const value = parseInt(e.target.value)
setShowLastStep(false)
setActiveStep(e.target.checked ? 0 : null)
}}
/>
Filter by step
</label>
<label>
<input
type="checkbox"
style={{ marginRight: 4 }}
checked={showLastStep}
onChange={(e) => {
setShowLastStep(e.target.checked)
setActiveStep(null)
setActiveStep(Number.isNaN(value) ? null : value)
}}
disabled={activeStep === null}
/>
Show last step
</label>
{isLimitReached && (
<span style={{ color: "red", fontSize: "12px" }}>
Display limited to {objectLimit} objects. Received:{" "}
{totalFilteredObjects}.
</span>
)}
<label>
<input
type="checkbox"
style={{ marginRight: 4 }}
checked={activeStep !== null}
onChange={(e) => {
setShowLastStep(false)
setActiveStep(e.target.checked ? 0 : null)
}}
/>
Filter by step
</label>
<label>
<input
type="checkbox"
style={{ marginRight: 4 }}
checked={showLastStep}
onChange={(e) => {
setShowLastStep(e.target.checked)
setActiveStep(null)
}}
/>
Show last step
</label>
{isLimitReached && (
<span style={{ color: "red", fontSize: "12px" }}>
Display limited to {objectLimit} objects. Received:{" "}
{totalFilteredObjects}.
</span>
)}
</div>
)}
</div>
{maxStep > 0 && stepTitle && (
<div style={{ marginLeft: "auto", textAlign: "right" }}>
{stepTitle}
</div>
)}
</div>
Expand Down
135 changes: 80 additions & 55 deletions site/components/InteractiveGraphicsCanvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ interface InteractiveGraphicsCanvasProps {
showGrid?: boolean
height?: number | string
width?: number | string
stepMetadata?: Array<{ title: string }>
alwaysShowToolbar?: boolean
}

export function InteractiveGraphicsCanvas({
Expand All @@ -21,6 +23,8 @@ export function InteractiveGraphicsCanvas({
showGrid = true,
height = 500,
width = "100%",
stepMetadata,
alwaysShowToolbar = false,
}: InteractiveGraphicsCanvasProps) {
const canvasRef = useRef<HTMLCanvasElement>(null)
const containerRef = useRef<HTMLDivElement | null>(null)
Expand All @@ -31,6 +35,11 @@ export function InteractiveGraphicsCanvas({

// Calculate the maximum step value from all graphics objects
const maxStep = getMaxStep(graphics)
const showToolbar = alwaysShowToolbar || maxStep > 0 || showLabelsByDefault
const stepTitle =
maxStep > 0
? stepMetadata?.[showLastStep ? maxStep : (activeStep ?? -1)]?.title
: undefined

// Filter graphics objects based on step
const filteredGraphics = getGraphicsFilteredByStep(graphics, {
Expand Down Expand Up @@ -176,62 +185,78 @@ export function InteractiveGraphicsCanvas({

return (
<div style={{ display: "flex", flexDirection: "column", gap: "10px" }}>
<div style={{ display: "flex", gap: "12px", alignItems: "center" }}>
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
<label>
<input
type="checkbox"
style={{ marginRight: 4 }}
checked={activeStep !== null}
onChange={(e) => {
setActiveStep(e.target.checked ? 0 : null)
}}
/>
Filter by step
</label>

<input
type="number"
min={0}
max={maxStep}
value={activeStep ?? 0}
onChange={(e) => {
const value = parseInt(e.target.value)
setShowLastStep(false)
setActiveStep(Number.isNaN(value) ? 0 : Math.min(value, maxStep))
}}
disabled={activeStep === null}
style={{ width: "60px" }}
/>

<label>
<input
type="checkbox"
style={{ marginRight: 4 }}
checked={showLastStep}
onChange={(e) => {
setShowLastStep(e.target.checked)
setActiveStep(null)
}}
/>
Show last step
</label>
</div>

<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
<label>
<input
type="checkbox"
style={{ marginRight: 4 }}
checked={showLabels}
onChange={(e) => {
setShowLabels(e.target.checked)
}}
/>
Show labels
</label>
{showToolbar && (
<div
style={{
display: "flex",
gap: "12px",
alignItems: "center",
}}
>
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
{maxStep > 0 && (
<>
<label>
<input
type="checkbox"
style={{ marginRight: 4 }}
checked={activeStep !== null}
onChange={(e) => {
setActiveStep(e.target.checked ? 0 : null)
}}
/>
Filter by step
</label>

<input
type="number"
min={0}
max={maxStep}
value={activeStep ?? 0}
onChange={(e) => {
const value = parseInt(e.target.value)
setShowLastStep(false)
setActiveStep(
Number.isNaN(value) ? 0 : Math.min(value, maxStep),
)
}}
disabled={activeStep === null}
style={{ width: "60px" }}
/>

<label>
<input
type="checkbox"
style={{ marginRight: 4 }}
checked={showLastStep}
onChange={(e) => {
setShowLastStep(e.target.checked)
setActiveStep(null)
}}
/>
Show last step
</label>
</>
)}
<label>
<input
type="checkbox"
style={{ marginRight: 4 }}
checked={showLabels}
onChange={(e) => {
setShowLabels(e.target.checked)
}}
/>
Show labels
</label>
</div>
{maxStep > 0 && stepTitle && (
<div style={{ marginLeft: "auto", textAlign: "right" }}>
{stepTitle}
</div>
)}
</div>
</div>
)}

<div
ref={(node) => {
Expand Down