|
@@ -15,15 +15,40 @@ export const defaultActivationSignal = {
|
|
|
order: "a[checked]",
|
|
order: "a[checked]",
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+const MIN_TICK_DISTANCE_PX = 100;
|
|
|
|
|
+
|
|
|
export default function ClockTimeline({}: Props) {
|
|
export default function ClockTimeline({}: Props) {
|
|
|
const { duration, pixelsPerTick, rowOrder, alignmentTick, addRow } = useClockStore();
|
|
const { duration, pixelsPerTick, rowOrder, alignmentTick, addRow } = useClockStore();
|
|
|
const timelineWidth = duration * pixelsPerTick;
|
|
const timelineWidth = duration * pixelsPerTick;
|
|
|
- const majorTicks = useMemo(() => {
|
|
|
|
|
- const step = Math.max(1, Math.round(duration / 10));
|
|
|
|
|
|
|
+ const { majorTicks, step } = useMemo(() => {
|
|
|
|
|
+ const rawStep = MIN_TICK_DISTANCE_PX / pixelsPerTick;
|
|
|
|
|
+ let step = 1;
|
|
|
|
|
+ if (rawStep > 1) {
|
|
|
|
|
+ // Find the order of magnitude (e.g., 10, 100, 1000)
|
|
|
|
|
+ const magnitude = Math.pow(10, Math.floor(Math.log10(rawStep)));
|
|
|
|
|
+
|
|
|
|
|
+ // Normalize the step to a number between 1 and 10
|
|
|
|
|
+ const normalized = rawStep / magnitude;
|
|
|
|
|
+
|
|
|
|
|
+ // Snap to 1, 2, 5, or 10 based on proximity
|
|
|
|
|
+ if (normalized < 1.5) step = 1 * magnitude;
|
|
|
|
|
+ else if (normalized < 3.5) step = 2 * magnitude;
|
|
|
|
|
+ else if (normalized < 7.5) step = 5 * magnitude;
|
|
|
|
|
+ else step = 10 * magnitude;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Generate the ticks
|
|
|
const out: number[] = [];
|
|
const out: number[] = [];
|
|
|
- for (let t = 0; t <= duration; t += step) out.push(t);
|
|
|
|
|
- return out;
|
|
|
|
|
- }, [duration]);
|
|
|
|
|
|
|
+ for (let t = step; t <= duration; t += step) out.push(t);
|
|
|
|
|
+
|
|
|
|
|
+ // Ensure the final duration tick is always drawn (optional but looks nice)
|
|
|
|
|
+ if (out[out.length - 1] === duration) {
|
|
|
|
|
+ out.pop();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return { step: step, majorTicks: out };
|
|
|
|
|
+ }, [duration, pixelsPerTick]);
|
|
|
|
|
+
|
|
|
const onPaletteDragStart = (e: React.DragEvent, presetId: string) => {
|
|
const onPaletteDragStart = (e: React.DragEvent, presetId: string) => {
|
|
|
e.dataTransfer.setData("text/plain", presetId);
|
|
e.dataTransfer.setData("text/plain", presetId);
|
|
|
e.dataTransfer.effectAllowed = "copy";
|
|
e.dataTransfer.effectAllowed = "copy";
|
|
@@ -53,7 +78,7 @@ export default function ClockTimeline({}: Props) {
|
|
|
</div>
|
|
</div>
|
|
|
<div className={styles.scrollWrapper}>
|
|
<div className={styles.scrollWrapper}>
|
|
|
<div className={styles.scrollContent}>
|
|
<div className={styles.scrollContent}>
|
|
|
- <div className={styles.ruler} style={{ width: timelineWidth }}>
|
|
|
|
|
|
|
+ <div className={styles.ruler} style={{ width: timelineWidth, minWidth: "100cqw" }}>
|
|
|
{majorTicks.map((t) => (
|
|
{majorTicks.map((t) => (
|
|
|
<div key={t} className={styles.rulerTick} style={{ left: `${(t / duration) * 100}%` }}>
|
|
<div key={t} className={styles.rulerTick} style={{ left: `${(t / duration) * 100}%` }}>
|
|
|
<span>{t}</span>
|
|
<span>{t}</span>
|
|
@@ -67,7 +92,7 @@ export default function ClockTimeline({}: Props) {
|
|
|
)}
|
|
)}
|
|
|
|
|
|
|
|
{rowOrder.map((rowId) => (
|
|
{rowOrder.map((rowId) => (
|
|
|
- <TimelineRow key={rowId} rowId={rowId} />
|
|
|
|
|
|
|
+ <TimelineRow key={rowId} rowId={rowId} step={step} />
|
|
|
))}
|
|
))}
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|