dialkit 1.4.0 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1038,6 +1038,10 @@ var ICON_PAUSE = [
1038
1038
  "M16.75 3C15.2312 3 14 4.23122 14 5.75V18.25C14 19.7688 15.2312 21 16.75 21H17.25C18.7688 21 20 19.7688 20 18.25V5.75C20 4.23122 18.7688 3 17.25 3H16.75Z"
1039
1039
  ];
1040
1040
  var ICON_PLAY = "M9.24394 2.36758C7.41419 1.18362 5 2.49701 5 4.67639V19.3238C5 21.5032 7.41419 22.8166 9.24394 21.6326L20.5624 14.3089C22.2371 13.2253 22.2372 10.775 20.5624 9.69129L9.24394 2.36758Z";
1041
+ var ICON_REPLAY = [
1042
+ "M12 2.5C17.2466 2.50016 21.5 6.7534 21.5 12C21.5 17.2466 17.2466 21.4998 12 21.5C7.52191 21.5 3.76987 18.4025 2.76465 14.2344C2.63517 13.6975 2.96508 13.1578 3.50195 13.0283C4.03883 12.8988 4.57851 13.2288 4.70801 13.7656C5.5016 17.0563 8.46701 19.5 12 19.5C16.142 19.4998 19.5 16.142 19.5 12C19.5 7.85796 16.142 4.50016 12 4.5C9.32981 4.5 6.98389 5.89541 5.6543 8H7.5C8.05228 8 8.5 8.44772 8.5 9C8.5 9.55228 8.05228 10 7.5 10H3.5C2.94772 10 2.5 9.55228 2.5 9V5C2.5 4.44772 2.94772 4 3.5 4C4.05228 4 4.5 4.44772 4.5 5V6.16797C6.2376 3.93677 8.95063 2.5 12 2.5Z",
1043
+ "M10 9.94043C10 9.33379 10.6826 8.97849 11.1797 9.32617L14.1221 11.3857C14.5486 11.6843 14.5486 12.3157 14.1221 12.6143L11.1797 14.6738C10.6826 15.0215 10 14.6662 10 14.0596V9.94043Z"
1044
+ ];
1041
1045
  var ICON_TIMELINE = [
1042
1046
  "M18.868 10C20.8517 10.0003 22.2886 11.8914 21.7577 13.8027L20.369 18.8027C20.0083 20.1012 18.826 20.9999 17.4784 21H6.51941C5.17179 21 3.98948 20.1012 3.62878 18.8027L2.24011 13.8027C1.7092 11.8913 3.14603 10.0003 5.12976 10H18.868Z",
1043
1047
  "M18.9989 6.5C19.5511 6.50007 19.9989 6.94776 19.9989 7.5C19.9989 8.05224 19.5511 8.49993 18.9989 8.5H4.9989C4.44661 8.5 3.9989 8.05228 3.9989 7.5C3.9989 6.94772 4.44661 6.5 4.9989 6.5H18.9989Z",
@@ -4235,8 +4239,11 @@ var SECOND_TICK_STEPS = [
4235
4239
  ];
4236
4240
  var MIN_TIMELINE_MAX_ZOOM = 8;
4237
4241
  var PLAYHEAD_FLAG_WIDTH = 52;
4242
+ var PLAYHEAD_FLAG_EDGE_OVERHANG = 1;
4238
4243
  var POPOVER_WIDTH = 280;
4239
4244
  var ZOOM_DRAG_DISTANCE = 180;
4245
+ var DEFAULT_DOCK_MAX_HEIGHT = 400;
4246
+ var MIN_DOCK_MAX_HEIGHT = 120;
4240
4247
  var subscribeGlobalTimelines = (callback) => TimelineStore.subscribeGlobal(callback);
4241
4248
  var getTimelines = () => TimelineStore.getTimelines();
4242
4249
  var subscribeTimelineVisibility = (callback) => TimelineUiStore.subscribe(callback);
@@ -4269,7 +4276,10 @@ function DialTimelineDock({
4269
4276
  defaultOpen
4270
4277
  }) {
4271
4278
  const [mounted, setMounted] = useState11(false);
4279
+ const [dockMaxHeight, setDockMaxHeight] = useState11(DEFAULT_DOCK_MAX_HEIGHT);
4272
4280
  const visibilityControllerId = useRef14(/* @__PURE__ */ Symbol("dialkit-timeline-visibility"));
4281
+ const dockRef = useRef14(null);
4282
+ const resizeCleanupRef = useRef14(null);
4273
4283
  useEffect11(() => TimelineUiStore.registerController(visibilityControllerId.current, {
4274
4284
  visible,
4275
4285
  defaultVisible,
@@ -4285,6 +4295,31 @@ function DialTimelineDock({
4285
4295
  useEffect11(() => {
4286
4296
  setMounted(true);
4287
4297
  }, []);
4298
+ useEffect11(() => () => resizeCleanupRef.current?.(), []);
4299
+ const handleResizePointerDown = useCallback14((e) => {
4300
+ const dock = dockRef.current;
4301
+ if (!dock) return;
4302
+ e.preventDefault();
4303
+ e.stopPropagation();
4304
+ resizeCleanupRef.current?.();
4305
+ const pointerY = e.clientY;
4306
+ const startHeight = dock.getBoundingClientRect().height;
4307
+ const handlePointerMove = (event) => {
4308
+ event.preventDefault();
4309
+ const viewportMax = Math.max(MIN_DOCK_MAX_HEIGHT, window.innerHeight - 24);
4310
+ setDockMaxHeight(clamp(startHeight + pointerY - event.clientY, MIN_DOCK_MAX_HEIGHT, viewportMax));
4311
+ };
4312
+ const finishResize = () => {
4313
+ window.removeEventListener("pointermove", handlePointerMove);
4314
+ window.removeEventListener("pointerup", finishResize);
4315
+ window.removeEventListener("pointercancel", finishResize);
4316
+ resizeCleanupRef.current = null;
4317
+ };
4318
+ window.addEventListener("pointermove", handlePointerMove, { passive: false });
4319
+ window.addEventListener("pointerup", finishResize);
4320
+ window.addEventListener("pointercancel", finishResize);
4321
+ resizeCleanupRef.current = finishResize;
4322
+ }, []);
4288
4323
  const timelines = useSyncExternalStore7(subscribeGlobalTimelines, getTimelines, getTimelines);
4289
4324
  const dockVisible = useSyncExternalStore7(
4290
4325
  subscribeTimelineVisibility,
@@ -4295,16 +4330,37 @@ function DialTimelineDock({
4295
4330
  return null;
4296
4331
  }
4297
4332
  return createPortal4(
4298
- /* @__PURE__ */ jsx18("div", { className: "dialkit-root dialkit-timeline", "data-theme": theme, hidden: !dockVisible, children: /* @__PURE__ */ jsx18("div", { className: "dialkit-timeline-dock", children: timelines.map((timeline) => /* @__PURE__ */ jsx18(
4299
- TimelineSection,
4300
- {
4301
- meta: timeline,
4302
- defaultOpen,
4303
- theme,
4304
- dockVisible
4305
- },
4306
- timeline.id
4307
- )) }) }),
4333
+ /* @__PURE__ */ jsxs14("div", { className: "dialkit-root dialkit-timeline", "data-theme": theme, hidden: !dockVisible, children: [
4334
+ /* @__PURE__ */ jsx18(
4335
+ "div",
4336
+ {
4337
+ className: "dialkit-timeline-resize-handle",
4338
+ onPointerDown: handleResizePointerDown,
4339
+ role: "separator",
4340
+ "aria-label": "Resize timeline height",
4341
+ "aria-orientation": "horizontal",
4342
+ title: "Drag to resize timeline"
4343
+ }
4344
+ ),
4345
+ /* @__PURE__ */ jsx18(
4346
+ "div",
4347
+ {
4348
+ ref: dockRef,
4349
+ className: "dialkit-timeline-dock",
4350
+ style: { maxHeight: `min(${dockMaxHeight}px, calc(100vh - 24px))` },
4351
+ children: timelines.map((timeline) => /* @__PURE__ */ jsx18(
4352
+ TimelineSection,
4353
+ {
4354
+ meta: timeline,
4355
+ defaultOpen,
4356
+ theme,
4357
+ dockVisible
4358
+ },
4359
+ timeline.id
4360
+ ))
4361
+ }
4362
+ )
4363
+ ] }),
4308
4364
  document.body
4309
4365
  );
4310
4366
  }
@@ -4356,6 +4412,20 @@ function PlayPauseButton({ id }) {
4356
4412
  }
4357
4413
  );
4358
4414
  }
4415
+ function ReplayButton({ onReplay }) {
4416
+ return /* @__PURE__ */ jsx18(
4417
+ motion7.button,
4418
+ {
4419
+ className: "dialkit-toolbar-add",
4420
+ onClick: onReplay,
4421
+ title: "Replay",
4422
+ "aria-label": "Replay",
4423
+ whileTap: { scale: 0.9 },
4424
+ transition: { type: "spring", visualDuration: 0.15, bounce: 0.3 },
4425
+ children: /* @__PURE__ */ jsx18("svg", { viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: ICON_REPLAY.map((d, i) => /* @__PURE__ */ jsx18("path", { d, fill: "currentColor" }, i)) })
4426
+ }
4427
+ );
4428
+ }
4359
4429
  function TimelinePlayheadFlag({
4360
4430
  id,
4361
4431
  duration,
@@ -4364,8 +4434,6 @@ function TimelinePlayheadFlag({
4364
4434
  viewEnd,
4365
4435
  laneWidth,
4366
4436
  rulerRef,
4367
- headerClearStart,
4368
- headerClearEnd,
4369
4437
  onResetView
4370
4438
  }) {
4371
4439
  const subscribe = useTransportSubscribe(id);
@@ -4425,16 +4493,22 @@ function TimelinePlayheadFlag({
4425
4493
  0,
4426
4494
  laneWidth
4427
4495
  );
4428
- const flagLeft = x - PLAYHEAD_FLAG_WIDTH / 2;
4429
- const flagRight = flagLeft + PLAYHEAD_FLAG_WIDTH;
4430
- const placement = flagLeft >= headerClearStart && flagRight <= headerClearEnd ? "raised" : "lowered";
4496
+ const flagCenter = clamp(
4497
+ x,
4498
+ PLAYHEAD_FLAG_WIDTH / 2 - PLAYHEAD_FLAG_EDGE_OVERHANG,
4499
+ laneWidth - PLAYHEAD_FLAG_WIDTH / 2 + PLAYHEAD_FLAG_EDGE_OVERHANG
4500
+ );
4501
+ const flagOffset = flagCenter - x;
4502
+ const edge = flagOffset > 0.5 ? "start" : flagOffset < -0.5 ? "end" : "center";
4431
4503
  return /* @__PURE__ */ jsxs14(
4432
4504
  "div",
4433
4505
  {
4434
4506
  className: "dialkit-timeline-playhead-control",
4435
- "data-edge": "center",
4436
- "data-placement": placement,
4437
- style: { left: `calc(var(--dial-timeline-label-w) + ${x}px)` },
4507
+ "data-edge": edge,
4508
+ style: {
4509
+ left: `calc(var(--dial-timeline-label-w) + ${x}px)`,
4510
+ "--dial-timeline-playhead-flag-offset": `${flagOffset}px`
4511
+ },
4438
4512
  onPointerDown: handlePointerDown,
4439
4513
  role: "slider",
4440
4514
  "aria-label": "Timeline current time",
@@ -4443,8 +4517,8 @@ function TimelinePlayheadFlag({
4443
4517
  "aria-valuenow": time,
4444
4518
  title: "Drag to scrub the timeline",
4445
4519
  children: [
4446
- /* @__PURE__ */ jsx18("div", { className: "dialkit-timeline-playhead-flag", children: time.toFixed(2) }),
4447
- /* @__PURE__ */ jsx18("div", { className: "dialkit-timeline-playhead-stem" })
4520
+ /* @__PURE__ */ jsx18("div", { className: "dialkit-timeline-playhead-stem" }),
4521
+ /* @__PURE__ */ jsx18("div", { className: "dialkit-timeline-playhead-anchor", children: /* @__PURE__ */ jsx18("div", { className: "dialkit-timeline-playhead-flag", children: time.toFixed(2) }) })
4448
4522
  ]
4449
4523
  }
4450
4524
  );
@@ -4542,30 +4616,18 @@ var TimelineSection = memo(function TimelineSection2({
4542
4616
  const presets = DialStore.getPresets(meta.id);
4543
4617
  const activePresetId = DialStore.getActivePresetId(meta.id);
4544
4618
  const laneAreaRef = useRef14(null);
4545
- const titleRef = useRef14(null);
4546
- const actionsRef = useRef14(null);
4619
+ const horizontalScrollRef = useRef14(null);
4547
4620
  const [laneWidth, setLaneWidth] = useState11(0);
4548
- const [flagClearRange, setFlagClearRange] = useState11({ start: 0, end: 0 });
4549
4621
  useLayoutEffect2(() => {
4550
4622
  if (!open) return;
4551
4623
  const ruler = laneAreaRef.current;
4552
- const title = titleRef.current;
4553
- const actions = actionsRef.current;
4554
- if (!ruler || !title || !actions) return;
4624
+ if (!ruler) return;
4555
4625
  const measure = () => {
4556
- const rulerRect = ruler.getBoundingClientRect();
4557
- const titleRect = title.getBoundingClientRect();
4558
- const actionsRect = actions.getBoundingClientRect();
4559
- setLaneWidth(rulerRect.width);
4560
- const start = Math.round(titleRect.right + 10 - rulerRect.left);
4561
- const end = Math.round(actionsRect.left - 10 - rulerRect.left);
4562
- setFlagClearRange((current) => current.start === start && current.end === end ? current : { start, end });
4626
+ setLaneWidth(ruler.getBoundingClientRect().width);
4563
4627
  };
4564
4628
  measure();
4565
4629
  const observer = new ResizeObserver(measure);
4566
4630
  observer.observe(ruler);
4567
- observer.observe(title);
4568
- observer.observe(actions);
4569
4631
  return () => observer.disconnect();
4570
4632
  }, [open]);
4571
4633
  const visibleDuration = meta.duration > 0 ? meta.duration / zoom : meta.duration;
@@ -4580,6 +4642,14 @@ var TimelineSection = memo(function TimelineSection2({
4580
4642
  useEffect11(() => {
4581
4643
  setViewStart((current) => clampViewStart(current, meta.duration, meta.duration / zoom));
4582
4644
  }, [meta.duration, zoom]);
4645
+ useLayoutEffect2(() => {
4646
+ const scroller = horizontalScrollRef.current;
4647
+ if (!scroller || pxPerSecond <= 0) return;
4648
+ const nextScrollLeft = safeViewStart * pxPerSecond;
4649
+ if (Math.abs(scroller.scrollLeft - nextScrollLeft) > 0.5) {
4650
+ scroller.scrollLeft = nextScrollLeft;
4651
+ }
4652
+ }, [open, pxPerSecond, safeViewStart]);
4583
4653
  useEffect11(() => {
4584
4654
  if (!dockVisible) setPopover(null);
4585
4655
  }, [dockVisible]);
@@ -4592,6 +4662,26 @@ var TimelineSection = memo(function TimelineSection2({
4592
4662
  setZoom(1);
4593
4663
  setViewStart(0);
4594
4664
  }, []);
4665
+ const handleReplay = useCallback14(() => {
4666
+ setViewStart(0);
4667
+ TimelineStore.replay(meta.id);
4668
+ }, [meta.id]);
4669
+ const handleHorizontalScroll = useCallback14((e) => {
4670
+ if (pxPerSecond <= 0) return;
4671
+ setViewStart(clampViewStart(
4672
+ e.currentTarget.scrollLeft / pxPerSecond,
4673
+ meta.duration,
4674
+ visibleDuration
4675
+ ));
4676
+ }, [meta.duration, pxPerSecond, visibleDuration]);
4677
+ const handleTimelineWheel = useCallback14((e) => {
4678
+ const scroller = horizontalScrollRef.current;
4679
+ if (!scroller || zoom <= 1) return;
4680
+ const horizontalDelta = Math.abs(e.deltaX) > Math.abs(e.deltaY) ? e.deltaX : e.shiftKey ? e.deltaY : 0;
4681
+ if (horizontalDelta === 0) return;
4682
+ e.preventDefault();
4683
+ scroller.scrollLeft += horizontalDelta;
4684
+ }, [zoom]);
4595
4685
  const zoomDragRef = useRef14(null);
4596
4686
  const rulerScrubRef = useRef14(null);
4597
4687
  const seekRulerFromClientX = useCallback14((clientX) => {
@@ -4914,7 +5004,7 @@ var TimelineSection = memo(function TimelineSection2({
4914
5004
  }
4915
5005
  return /* @__PURE__ */ jsxs14("div", { className: "dialkit-timeline-section", children: [
4916
5006
  /* @__PURE__ */ jsxs14("div", { className: "dialkit-timeline-header", "data-open": open || void 0, children: [
4917
- /* @__PURE__ */ jsx18("div", { className: "dialkit-timeline-identity", children: /* @__PURE__ */ jsx18("span", { ref: titleRef, className: "dialkit-timeline-title", children: meta.name }) }),
5007
+ /* @__PURE__ */ jsx18("div", { className: "dialkit-timeline-identity", children: /* @__PURE__ */ jsx18("span", { className: "dialkit-timeline-title", children: meta.name }) }),
4918
5008
  !open && /* @__PURE__ */ jsx18(
4919
5009
  TimelineOverview,
4920
5010
  {
@@ -4925,8 +5015,9 @@ var TimelineSection = memo(function TimelineSection2({
4925
5015
  onNavigate: centerViewAt
4926
5016
  }
4927
5017
  ),
4928
- /* @__PURE__ */ jsxs14("div", { ref: actionsRef, className: "dialkit-timeline-actions", children: [
5018
+ /* @__PURE__ */ jsxs14("div", { className: "dialkit-timeline-actions", children: [
4929
5019
  /* @__PURE__ */ jsx18(PlayPauseButton, { id: meta.id }),
5020
+ /* @__PURE__ */ jsx18(ReplayButton, { onReplay: handleReplay }),
4930
5021
  /* @__PURE__ */ jsx18(
4931
5022
  motion7.button,
4932
5023
  {
@@ -5009,54 +5100,68 @@ var TimelineSection = memo(function TimelineSection2({
5009
5100
  )
5010
5101
  ] })
5011
5102
  ] }),
5012
- open && /* @__PURE__ */ jsx18(
5103
+ open && /* @__PURE__ */ jsxs14(
5013
5104
  "div",
5014
5105
  {
5015
5106
  className: "dialkit-timeline-body",
5107
+ onWheel: handleTimelineWheel,
5016
5108
  onPointerDown: handleTrackPointerDown,
5017
5109
  onPointerMove: handleTrackPointerMove,
5018
5110
  onPointerUp: finishTrackScrub,
5019
5111
  onPointerCancel: finishTrackScrub,
5020
5112
  onLostPointerCapture: finishTrackScrub,
5021
- children: /* @__PURE__ */ jsxs14("div", { className: "dialkit-timeline-grid", children: [
5022
- /* @__PURE__ */ jsxs14("div", { className: "dialkit-timeline-row dialkit-timeline-ruler-row", children: [
5113
+ children: [
5114
+ /* @__PURE__ */ jsxs14("div", { className: "dialkit-timeline-grid", children: [
5115
+ /* @__PURE__ */ jsxs14("div", { className: "dialkit-timeline-row dialkit-timeline-ruler-row", children: [
5116
+ /* @__PURE__ */ jsx18("div", { className: "dialkit-timeline-label" }),
5117
+ /* @__PURE__ */ jsxs14(
5118
+ "div",
5119
+ {
5120
+ ref: laneAreaRef,
5121
+ className: "dialkit-timeline-ruler",
5122
+ onPointerDown: handleRulerPointerDown,
5123
+ onPointerMove: handleRulerPointerMove,
5124
+ onPointerUp: handleRulerPointerUp,
5125
+ onPointerCancel: handleRulerPointerCancel,
5126
+ onLostPointerCapture: handleRulerPointerCancel,
5127
+ title: "Drag to seek \xB7 Option-drag to zoom \xB7 Shift-drag to reset zoom",
5128
+ children: [
5129
+ fineTicks.map((t) => /* @__PURE__ */ jsx18("div", { className: "dialkit-timeline-tick dialkit-timeline-tick-fine", style: { left: (t - safeViewStart) * pxPerSecond } }, `fine:${t}`)),
5130
+ mediumTicks.map((t) => /* @__PURE__ */ jsx18("div", { className: "dialkit-timeline-tick dialkit-timeline-tick-medium", style: { left: (t - safeViewStart) * pxPerSecond } }, `medium:${t}`)),
5131
+ majorTicks.map((t) => /* @__PURE__ */ jsx18("div", { className: "dialkit-timeline-tick", style: { left: (t - safeViewStart) * pxPerSecond }, children: /* @__PURE__ */ jsx18("span", { className: "dialkit-timeline-tick-label", children: formatRulerSeconds(t, majorStep) }) }, t))
5132
+ ]
5133
+ }
5134
+ )
5135
+ ] }),
5136
+ rows,
5137
+ pxPerSecond > 0 && /* @__PURE__ */ jsx18(
5138
+ TimelinePlayheadFlag,
5139
+ {
5140
+ id: meta.id,
5141
+ duration: meta.duration,
5142
+ pxPerSecond,
5143
+ viewStart: safeViewStart,
5144
+ viewEnd,
5145
+ laneWidth,
5146
+ rulerRef: laneAreaRef,
5147
+ onResetView: resetView
5148
+ }
5149
+ )
5150
+ ] }),
5151
+ zoom > 1 && /* @__PURE__ */ jsxs14("div", { className: "dialkit-timeline-scroll-row", children: [
5023
5152
  /* @__PURE__ */ jsx18("div", { className: "dialkit-timeline-label" }),
5024
- /* @__PURE__ */ jsxs14(
5153
+ /* @__PURE__ */ jsx18(
5025
5154
  "div",
5026
5155
  {
5027
- ref: laneAreaRef,
5028
- className: "dialkit-timeline-ruler",
5029
- onPointerDown: handleRulerPointerDown,
5030
- onPointerMove: handleRulerPointerMove,
5031
- onPointerUp: handleRulerPointerUp,
5032
- onPointerCancel: handleRulerPointerCancel,
5033
- onLostPointerCapture: handleRulerPointerCancel,
5034
- title: "Drag to seek \xB7 Option-drag to zoom \xB7 Shift-drag to reset zoom",
5035
- children: [
5036
- fineTicks.map((t) => /* @__PURE__ */ jsx18("div", { className: "dialkit-timeline-tick dialkit-timeline-tick-fine", style: { left: (t - safeViewStart) * pxPerSecond } }, `fine:${t}`)),
5037
- mediumTicks.map((t) => /* @__PURE__ */ jsx18("div", { className: "dialkit-timeline-tick dialkit-timeline-tick-medium", style: { left: (t - safeViewStart) * pxPerSecond } }, `medium:${t}`)),
5038
- majorTicks.map((t) => /* @__PURE__ */ jsx18("div", { className: "dialkit-timeline-tick", style: { left: (t - safeViewStart) * pxPerSecond }, children: /* @__PURE__ */ jsx18("span", { className: "dialkit-timeline-tick-label", children: formatRulerSeconds(t, majorStep) }) }, t))
5039
- ]
5156
+ ref: horizontalScrollRef,
5157
+ className: "dialkit-timeline-horizontal-scroll",
5158
+ onScroll: handleHorizontalScroll,
5159
+ "aria-label": "Timeline horizontal scroll",
5160
+ children: /* @__PURE__ */ jsx18("div", { style: { width: laneWidth * zoom } })
5040
5161
  }
5041
5162
  )
5042
- ] }),
5043
- rows,
5044
- pxPerSecond > 0 && /* @__PURE__ */ jsx18(
5045
- TimelinePlayheadFlag,
5046
- {
5047
- id: meta.id,
5048
- duration: meta.duration,
5049
- pxPerSecond,
5050
- viewStart: safeViewStart,
5051
- viewEnd,
5052
- laneWidth,
5053
- rulerRef: laneAreaRef,
5054
- headerClearStart: flagClearRange.start,
5055
- headerClearEnd: flagClearRange.end,
5056
- onResetView: resetView
5057
- }
5058
- )
5059
- ] })
5163
+ ] })
5164
+ ]
5060
5165
  }
5061
5166
  ),
5062
5167
  popover && /* @__PURE__ */ jsx18(