@vortexm/vjt 0.1.17 → 0.1.18

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
@@ -7910,6 +7910,7 @@ function samePath(left, right) {
7910
7910
  // src/lib/render.ts
7911
7911
  var DEFAULT_DATE_FORMAT = "dd.MM.yyyy";
7912
7912
  var MOBILE_RATIO_THRESHOLD = 1.15;
7913
+ var SCROLL_END_DEBOUNCE_MS = 140;
7913
7914
  var globalErrorLoggingAttached = false;
7914
7915
  function escapeHtml(value) {
7915
7916
  return String(value).replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
@@ -8088,6 +8089,7 @@ var RuntimeRenderer = class {
8088
8089
  activeScrollAnimationFrameId = null;
8089
8090
  pendingCursorReference = null;
8090
8091
  pendingCursorFrameId = null;
8092
+ scrollEndTimeoutIds = /* @__PURE__ */ new Map();
8091
8093
  constructor(description, options = {}) {
8092
8094
  this.description = deepClone(description);
8093
8095
  this.resourceManager = options.resourceManager ?? null;
@@ -8327,6 +8329,10 @@ var RuntimeRenderer = class {
8327
8329
  window.clearTimeout(timeoutId);
8328
8330
  }
8329
8331
  this.toastTimeoutIds.clear();
8332
+ for (const timeoutId of this.scrollEndTimeoutIds.values()) {
8333
+ window.clearTimeout(timeoutId);
8334
+ }
8335
+ this.scrollEndTimeoutIds.clear();
8330
8336
  this.voiceRuntime.dispose();
8331
8337
  };
8332
8338
  }
@@ -9516,6 +9522,7 @@ var RuntimeRenderer = class {
9516
9522
  }
9517
9523
  element.addEventListener("scroll", () => {
9518
9524
  this.syncScrollStateForElement(element, key);
9525
+ this.scheduleScrollEndEvent(element, node, key);
9519
9526
  }, { passive: true });
9520
9527
  }
9521
9528
  }
@@ -9837,6 +9844,7 @@ var RuntimeRenderer = class {
9837
9844
  if (typeof window === "undefined") {
9838
9845
  initialElement.scrollTop = Math.max(0, Math.min(targetTop, getMaxScrollTop(initialElement)));
9839
9846
  this.syncScrollStateForReference(reference, initialElement);
9847
+ this.scheduleProgrammaticScrollEndForReference(reference);
9840
9848
  return;
9841
9849
  }
9842
9850
  if (this.activeScrollAnimationFrameId !== null) {
@@ -9849,6 +9857,7 @@ var RuntimeRenderer = class {
9849
9857
  if (Math.abs(delta) < 1) {
9850
9858
  initialElement.scrollTop = clampedTargetTop;
9851
9859
  this.syncScrollStateForReference(reference, initialElement);
9860
+ this.scheduleProgrammaticScrollEndForReference(reference);
9852
9861
  return;
9853
9862
  }
9854
9863
  const durationMs = 300;
@@ -9870,6 +9879,7 @@ var RuntimeRenderer = class {
9870
9879
  } else {
9871
9880
  liveElement.scrollTop = liveTargetTop;
9872
9881
  this.syncScrollStateForReference(reference, liveElement);
9882
+ this.scheduleProgrammaticScrollEndForReference(reference);
9873
9883
  this.activeScrollAnimationFrameId = null;
9874
9884
  }
9875
9885
  };
@@ -9965,6 +9975,41 @@ var RuntimeRenderer = class {
9965
9975
  state.isScrolledToTop = element.scrollTop <= 1;
9966
9976
  state.isScrolledToBottom = element.scrollTop >= maxScrollTop - 1;
9967
9977
  }
9978
+ scheduleScrollEndEvent(element, node, key) {
9979
+ if (!node.events?.onScroll?.length || typeof window === "undefined") {
9980
+ return;
9981
+ }
9982
+ const existingTimeoutId = this.scrollEndTimeoutIds.get(key);
9983
+ if (existingTimeoutId !== void 0) {
9984
+ window.clearTimeout(existingTimeoutId);
9985
+ }
9986
+ const timeoutId = window.setTimeout(() => {
9987
+ this.scrollEndTimeoutIds.delete(key);
9988
+ void this.actionRuntime.dispatchWidgetEvent(
9989
+ node,
9990
+ "onScroll",
9991
+ key,
9992
+ getActionInputValueForElement(element, node, this.stateByKey),
9993
+ null
9994
+ );
9995
+ }, SCROLL_END_DEBOUNCE_MS);
9996
+ this.scrollEndTimeoutIds.set(key, timeoutId);
9997
+ }
9998
+ scheduleProgrammaticScrollEndForReference(reference) {
9999
+ const element = this.findScrollableWidgetElement(reference);
10000
+ if (!(element instanceof HTMLElement)) {
10001
+ return;
10002
+ }
10003
+ const key = element.dataset.widgetKey ?? reference.split(".")[0]?.trim() ?? "";
10004
+ if (!key) {
10005
+ return;
10006
+ }
10007
+ const node = this.nodeByKey.get(key) ?? this.nodeById.get(key);
10008
+ if (!node || !isScrollableWidgetNode(node)) {
10009
+ return;
10010
+ }
10011
+ this.scheduleScrollEndEvent(element, node, key);
10012
+ }
9968
10013
  toScrollableIndex(value) {
9969
10014
  if (typeof value === "number" && Number.isFinite(value)) {
9970
10015
  return Math.max(0, Math.trunc(value));
@@ -3,7 +3,7 @@ export type LayoutType = 'vertical' | 'horizontal' | 'grid';
3
3
  export type TextAlign = 'left' | 'center' | 'right';
4
4
  export type VerticalAlign = 'top' | 'center' | 'bottom';
5
5
  export type HeadingTag = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
6
- export type WidgetEventName = 'onClick' | 'onUserValueChange' | 'onRefresh' | 'onEnter' | 'onShiftEnter' | 'onControlEnter' | 'onPaste';
6
+ export type WidgetEventName = 'onClick' | 'onUserValueChange' | 'onRefresh' | 'onEnter' | 'onShiftEnter' | 'onControlEnter' | 'onPaste' | 'onScroll';
7
7
  export type SystemEventName = 'onBeforeRender' | 'onAfterRender' | 'onBeforeNavigate' | 'onAfterNavigate' | 'onLayoutSwitchToMobile' | 'onLayoutSwitchToDesktop' | 'onSpeechDetected' | 'onRecordingStarted' | 'onRecordingStopped' | 'onRecordingError' | 'onListeningError' | 'onListeringError' | 'onPlayFinished' | 'onPlayingStopped';
8
8
  export type PrimitiveRequestType = 'int' | 'float' | 'boolean' | 'string';
9
9
  export type ClosableRuntimeResource = {
@@ -12,7 +12,7 @@ type BindingEnvironment = {
12
12
  }) => void;
13
13
  updatePointerFromEvent: (event: MouseEvent) => void;
14
14
  updateOwningListElementDescriptor: (element: HTMLElement, mutate: (descriptor: DescriptionNode) => void) => void;
15
- dispatchWidgetEvent: (node: DescriptionNode, eventName: 'onClick' | 'onUserValueChange' | 'onEnter' | 'onShiftEnter' | 'onControlEnter' | 'onPaste', key: string, inputValue?: unknown, pointer?: {
15
+ dispatchWidgetEvent: (node: DescriptionNode, eventName: 'onClick' | 'onUserValueChange' | 'onEnter' | 'onShiftEnter' | 'onControlEnter' | 'onPaste' | 'onScroll', key: string, inputValue?: unknown, pointer?: {
16
16
  x: number;
17
17
  y: number;
18
18
  } | null) => Promise<void>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vortexm/vjt",
3
- "version": "0.1.17",
3
+ "version": "0.1.18",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",