# TimeBox

A Jetpack Compose component for rendering time-based content within a scrollable and zoomable viewport.

## Overview

TimeBox is a layout component that positions its children based on their time positions. It provides a timeline-like interface where composables can be placed at specific time points or span time ranges, with support for panning and zooming interactions.

## Key Features

- **Time-based positioning**: Place composables at specific timestamps or time ranges
- **Interactive viewport**: Pan to scroll through time and zoom to change resolution
- **Efficient rendering**: Only visible content is rendered for performance
- **Flexible layout**: Support for both point-in-time and time-spanning elements
- **Gesture support**: Built-in pan and zoom gestures with customizable callbacks

## Basic Usage

### Creating a TimeBox

```kotlin
@Composable
fun MyTimelineView() {
    val timeBoxState = rememberTimeBoxState(
        resolution = 10.dp.perSecond,
        startTimeOffset = 0.seconds
    )
    
    TimeBox(
        state = timeBoxState,
        modifier = Modifier
            .size(300.dp, 80.dp)
            .timeBoxGestures(timeBoxState) // Add pan/zoom support
    ) {
        // Content goes here
    }
}
```

### Positioning Content

#### At a Specific Time Point

Place content at an exact timestamp:

```kotlin
TimeBox(state = timeBoxState) {
    Text(
        text = "Event at 5s",
        modifier = Modifier.atTime(5.seconds)
    )
    Text(
        text = "Event at 15s",
        modifier = Modifier.atTime(15.seconds)
    )
}
```

#### Spanning a Time Range

Place content that spans a duration:

```kotlin
TimeBox(state = timeBoxState) {
    // Using ClosedRange
    Box(
        modifier = Modifier
            .atTime(2.seconds..4.seconds)
            .background(Color.Blue)
            .fillMaxHeight()
    )
    
    // Using Pair syntax
    Box(
        modifier = Modifier
            .atTime(6.seconds to 8.seconds)
            .background(Color.Red)
            .fillMaxHeight()
    )
}
```

#### Self-Rendering Content

Some content may need to handle its own positioning within the TimeBox, such as:

- Complex waveform visualizations that calculate their own layout based on audio data
- Custom timeline markers or grids that need precise control over positioning
- Interactive elements that need to respond to the current viewport state
- Content that needs to span the entire viewport width regardless of time range

For these cases, use `timeBoxIgnorePositioning()` to opt out of TimeBox's automatic positioning. The content will then be responsible for:

1. Reading the TimeBox state (resolution and startTimeOffset)
2. Calculating its own position and dimensions
3. Handling viewport changes appropriately

```kotlin
TimeBox(state = timeBoxState) {
    CustomWaveform(
        modifier = Modifier.timeBoxIgnorePositioning()
    )
}
```

## State Management

### TimeBoxState

The `TimeBoxState` manages the viewport's time range and resolution:

```kotlin
class TimeBoxState(
    initialResolution: DpPerSecond,
    initialStartTimeOffset: Duration,
)
```

**Properties:**

- `resolution`: Current zoom level (pixels per second)
- `startTimeOffset`: Starting time of the visible viewport

### Creating State

#### Remember State

Creates state that persists across recompositions:

```kotlin
val state = rememberTimeBoxState(
    resolution = 20.dp.perSecond,
    startTimeOffset = 10.seconds
)
```

#### Remember State Of

Creates state that updates when parameters change:

```kotlin
val state = rememberTimeBoxStateOf(
    resolution = currentResolution,
    startTimeOffset = currentOffset
)
```

## Gesture Support

### Basic Gestures

Add pan and zoom support:

```kotlin
TimeBox(
    state = timeBoxState,
    modifier = Modifier.timeBoxGestures(timeBoxState)
) {
    // Content
}
```

### Pan-Only Gestures

Disable zooming and only allow panning:

```kotlin
TimeBox(
    state = timeBoxState,
    modifier = Modifier.timeBoxGestures(
        state = timeBoxState,
        zoomEnabled = false
    )
) {
    // Content
}
```

### Custom Gesture Handling

Handle gestures with custom callbacks:

```kotlin
TimeBox(
    state = timeBoxState,
    modifier = Modifier.timeBoxGestures(
        state = timeBoxState,
        onGestureStart = { /* Handle gesture start */ },
        onGestureEnd = { /* Handle gesture end */ },
        onOffsetUpdate = { newOffset ->
            // Transform offset before applying
            newOffset.coerceIn(0.seconds, maxDuration)
        }
    )
) {
    // Content
}
```

## Advanced Usage

### Multiple Time Elements

```kotlin
TimeBox(state = timeBoxState) {
    // Point markers
    repeat(10) { index ->
        Circle(
            modifier = Modifier
                .atTime((index * 2).seconds)
                .size(8.dp),
            color = Color.Blue
        )
    }
    
    // Time spans
    Box(
        modifier = Modifier
            .atTime(5.seconds..15.seconds)
            .background(Color.Red.copy(alpha = 0.3f))
            .fillMaxHeight()
    )
    
    // Text annotations
    Text(
        text = "Important Event",
        modifier = Modifier
            .atTime(10.seconds)
            .background(Color.White)
            .padding(4.dp)
    )
}
```

### Responsive Timeline

Adjust content based on zoom level:

```kotlin
TimeBox(state = timeBoxState) {
    val showDetailedMarkers = resolution > 50.dp.perSecond
    
    if (showDetailedMarkers) {
        // Show detailed markers when zoomed in
        repeat(100) { index ->
            Divider(
                modifier = Modifier
                    .atTime((index * 0.1).seconds)
                    .width(1.dp)
                    .fillMaxHeight(),
                color = Color.Gray
            )
        }
    } else {
        // Show only major markers when zoomed out
        repeat(10) { index ->
            Divider(
                modifier = Modifier
                    .atTime(index.seconds)
                    .width(2.dp)
                    .fillMaxHeight(),
                color = Color.Black
            )
        }
    }
}
```

## API Reference

### TimeBox

```kotlin
@Composable
fun TimeBox(
    state: TimeBoxState,
    modifier: Modifier = Modifier,
    content: @Composable TimeBoxPlaceableScope.() -> Unit,
)
```

### TimeBoxPlaceableScope

Available modifiers within TimeBox content:

```kotlin
// Position at specific time
fun Modifier.atTime(time: Duration): Modifier

// Position spanning time range
fun Modifier.atTime(time: ClosedRange<Duration>): Modifier
fun Modifier.atTime(time: Pair<Duration, Duration>): Modifier

// Handle own positioning
fun Modifier.timeBoxIgnorePositioning(ignorePositioning: Boolean = true): Modifier
```

### Gesture Modifiers

```kotlin
// Full gesture support
@Composable
fun Modifier.timeBoxGestures(
    state: TimeBoxState,
    zoomEnabled: Boolean = true,
    onGestureStart: () -> Unit = {},
    onGestureEnd: () -> Unit = {},
    onOffsetUpdate: (Duration) -> Duration = { it },
): Modifier

// Pan-only support
@Composable
fun Modifier.panOnlyTimeBoxGestures(
    startOffset: Duration,
    resolution: DpPerSecond,
    onOffsetChanged: (Duration) -> Unit,
    onGestureStart: () -> Unit = {},
    onGestureEnd: () -> Unit = {},
): Modifier
```
