gloomberb 0.7.1 → 0.7.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gloomberb",
3
- "version": "0.7.1",
3
+ "version": "0.7.2",
4
4
  "description": "Bloomberg-style stock portfolio tracker for the terminal",
5
5
  "module": "src/index.tsx",
6
6
  "type": "module",
@@ -8,6 +8,22 @@ import {
8
8
  } from "react";
9
9
  import { Box, type ScrollBoxRenderable } from "../ui";
10
10
 
11
+ function listenToScrollBarChange(
12
+ scrollBar: ScrollBoxRenderable["verticalScrollBar"],
13
+ handler: () => void,
14
+ ): (() => void) | null {
15
+ if (
16
+ !scrollBar
17
+ || typeof scrollBar.on !== "function"
18
+ || typeof scrollBar.off !== "function"
19
+ ) {
20
+ return null;
21
+ }
22
+
23
+ scrollBar.on("change", handler);
24
+ return () => scrollBar.off?.("change", handler);
25
+ }
26
+
11
27
  export interface TableViewKeyEvent {
12
28
  name?: string;
13
29
  ctrl?: boolean;
@@ -172,13 +188,19 @@ export function useScrollBoxScrollActivity({
172
188
  });
173
189
  };
174
190
 
175
- scrollBox.verticalScrollBar.on("change", handleVerticalChange);
176
- scrollBox.horizontalScrollBar.on("change", handleHorizontalChange);
191
+ const removeVerticalListener = listenToScrollBarChange(
192
+ scrollBox.verticalScrollBar,
193
+ handleVerticalChange,
194
+ );
195
+ const removeHorizontalListener = listenToScrollBarChange(
196
+ scrollBox.horizontalScrollBar,
197
+ handleHorizontalChange,
198
+ );
177
199
 
178
200
  return () => {
179
201
  active = false;
180
- scrollBox.verticalScrollBar.off("change", handleVerticalChange);
181
- scrollBox.horizontalScrollBar.off("change", handleHorizontalChange);
202
+ removeVerticalListener?.();
203
+ removeHorizontalListener?.();
182
204
  };
183
205
  }, [scrollRef]);
184
206
  }
@@ -886,13 +886,29 @@ const WebScrollBox = forwardRef<ScrollBoxRenderable, Record<string, unknown> & {
886
886
  );
887
887
  const horizontalScrollBarVisibleRef = useRef(horizontalScrollBarVisible);
888
888
  const verticalScrollBarVisibleRef = useRef(verticalScrollBarVisible);
889
+ const horizontalScrollChangeHandlersRef = useRef(new Set<() => void>());
890
+ const verticalScrollChangeHandlersRef = useRef(new Set<() => void>());
889
891
  const scrollFrameRef = useRef<number | null>(null);
890
892
  const lastWheelAtRef = useRef(0);
891
893
  const [scrollbarActive, markScrollbarActive] = useScrollbarActivity();
892
894
 
893
- const getElement = () => elementRef.current;
895
+ const getElement = useCallback(() => elementRef.current, []);
894
896
  const toCellY = (pixels: number) => Math.max(0, Math.round(pixels / WEB_CELL_HEIGHT));
895
897
  const toCellX = (pixels: number) => Math.max(0, Math.round(pixels / WEB_CELL_WIDTH));
898
+ const emitHorizontalScrollChange = useCallback(() => {
899
+ for (const handler of horizontalScrollChangeHandlersRef.current) {
900
+ handler();
901
+ }
902
+ }, []);
903
+ const emitVerticalScrollChange = useCallback(() => {
904
+ for (const handler of verticalScrollChangeHandlersRef.current) {
905
+ handler();
906
+ }
907
+ }, []);
908
+ const emitScrollPositionChanges = useCallback(() => {
909
+ emitVerticalScrollChange();
910
+ emitHorizontalScrollChange();
911
+ }, [emitHorizontalScrollChange, emitVerticalScrollChange]);
896
912
  const horizontalScrollBar = useMemo(() => ({
897
913
  get visible() {
898
914
  return horizontalScrollBarVisibleRef.current;
@@ -902,6 +918,12 @@ const WebScrollBox = forwardRef<ScrollBoxRenderable, Record<string, unknown> & {
902
918
  horizontalScrollBarVisibleRef.current = normalized;
903
919
  setHorizontalScrollBarVisible(normalized);
904
920
  },
921
+ on(event: "change", handler: () => void) {
922
+ if (event === "change") horizontalScrollChangeHandlersRef.current.add(handler);
923
+ },
924
+ off(event: "change", handler: () => void) {
925
+ if (event === "change") horizontalScrollChangeHandlersRef.current.delete(handler);
926
+ },
905
927
  }), []);
906
928
  const verticalScrollBar = useMemo(() => ({
907
929
  get visible() {
@@ -912,6 +934,12 @@ const WebScrollBox = forwardRef<ScrollBoxRenderable, Record<string, unknown> & {
912
934
  verticalScrollBarVisibleRef.current = normalized;
913
935
  setVerticalScrollBarVisible(normalized);
914
936
  },
937
+ on(event: "change", handler: () => void) {
938
+ if (event === "change") verticalScrollChangeHandlersRef.current.add(handler);
939
+ },
940
+ off(event: "change", handler: () => void) {
941
+ if (event === "change") verticalScrollChangeHandlersRef.current.delete(handler);
942
+ },
915
943
  }), []);
916
944
 
917
945
  useEffect(() => {
@@ -938,28 +966,40 @@ const WebScrollBox = forwardRef<ScrollBoxRenderable, Record<string, unknown> & {
938
966
  },
939
967
  set scrollTop(value: number) {
940
968
  const element = getElement();
941
- if (element) element.scrollTop = Math.max(0, value) * WEB_CELL_HEIGHT;
969
+ if (element) {
970
+ element.scrollTop = Math.max(0, value) * WEB_CELL_HEIGHT;
971
+ emitVerticalScrollChange();
972
+ }
942
973
  },
943
974
  get scrollTopPx() {
944
975
  return Math.max(0, getElement()?.scrollTop ?? 0);
945
976
  },
946
977
  set scrollTopPx(value: number) {
947
978
  const element = getElement();
948
- if (element) element.scrollTop = Math.max(0, value);
979
+ if (element) {
980
+ element.scrollTop = Math.max(0, value);
981
+ emitVerticalScrollChange();
982
+ }
949
983
  },
950
984
  get scrollLeft() {
951
985
  return toCellX(getElement()?.scrollLeft ?? 0);
952
986
  },
953
987
  set scrollLeft(value: number) {
954
988
  const element = getElement();
955
- if (element) element.scrollLeft = Math.max(0, value) * WEB_CELL_WIDTH;
989
+ if (element) {
990
+ element.scrollLeft = Math.max(0, value) * WEB_CELL_WIDTH;
991
+ emitHorizontalScrollChange();
992
+ }
956
993
  },
957
994
  get scrollLeftPx() {
958
995
  return Math.max(0, getElement()?.scrollLeft ?? 0);
959
996
  },
960
997
  set scrollLeftPx(value: number) {
961
998
  const element = getElement();
962
- if (element) element.scrollLeft = Math.max(0, value);
999
+ if (element) {
1000
+ element.scrollLeft = Math.max(0, value);
1001
+ emitHorizontalScrollChange();
1002
+ }
963
1003
  },
964
1004
  get scrollHeight() {
965
1005
  return toCellY(getElement()?.scrollHeight ?? 0);
@@ -1003,8 +1043,10 @@ const WebScrollBox = forwardRef<ScrollBoxRenderable, Record<string, unknown> & {
1003
1043
  if (!element) return;
1004
1044
  if (typeof target === "number") {
1005
1045
  element.scrollTop = Math.max(0, target) * WEB_CELL_HEIGHT;
1046
+ emitVerticalScrollChange();
1006
1047
  if (typeof y === "number") {
1007
1048
  element.scrollLeft = Math.max(0, y) * WEB_CELL_WIDTH;
1049
+ emitHorizontalScrollChange();
1008
1050
  }
1009
1051
  return;
1010
1052
  }
@@ -1012,14 +1054,17 @@ const WebScrollBox = forwardRef<ScrollBoxRenderable, Record<string, unknown> & {
1012
1054
  left: Math.max(0, target.x ?? toCellX(element.scrollLeft)) * WEB_CELL_WIDTH,
1013
1055
  top: Math.max(0, target.y ?? toCellY(element.scrollTop)) * WEB_CELL_HEIGHT,
1014
1056
  });
1057
+ emitScrollPositionChanges();
1015
1058
  },
1016
1059
  scrollToPixels(target: number | { x?: number; y?: number }, y?: number) {
1017
1060
  const element = getElement();
1018
1061
  if (!element) return;
1019
1062
  if (typeof target === "number") {
1020
1063
  element.scrollTop = Math.max(0, target);
1064
+ emitVerticalScrollChange();
1021
1065
  if (typeof y === "number") {
1022
1066
  element.scrollLeft = Math.max(0, y);
1067
+ emitHorizontalScrollChange();
1023
1068
  }
1024
1069
  return;
1025
1070
  }
@@ -1027,13 +1072,22 @@ const WebScrollBox = forwardRef<ScrollBoxRenderable, Record<string, unknown> & {
1027
1072
  left: Math.max(0, target.x ?? element.scrollLeft),
1028
1073
  top: Math.max(0, target.y ?? element.scrollTop),
1029
1074
  });
1075
+ emitScrollPositionChanges();
1030
1076
  },
1031
- }), [horizontalScrollBar, verticalScrollBar]);
1077
+ }), [
1078
+ emitHorizontalScrollChange,
1079
+ emitScrollPositionChanges,
1080
+ emitVerticalScrollChange,
1081
+ getElement,
1082
+ horizontalScrollBar,
1083
+ verticalScrollBar,
1084
+ ]);
1032
1085
 
1033
1086
  const scrollable = props.scrollX === true || props.scrollY === true;
1034
1087
  const overflowX = props.scrollX === true ? "auto" : "hidden";
1035
1088
  const overflowY = props.scrollY === true ? "auto" : "hidden";
1036
1089
  const handleScroll = useCallback(() => {
1090
+ emitScrollPositionChanges();
1037
1091
  markScrollbarActive();
1038
1092
  if (typeof props.onMouseScroll !== "function") return;
1039
1093
  if (Date.now() - lastWheelAtRef.current < 32) return;
@@ -1042,7 +1096,7 @@ const WebScrollBox = forwardRef<ScrollBoxRenderable, Record<string, unknown> & {
1042
1096
  scrollFrameRef.current = null;
1043
1097
  (props.onMouseScroll as () => void)();
1044
1098
  });
1045
- }, [markScrollbarActive, props.onMouseScroll]);
1099
+ }, [emitScrollPositionChanges, markScrollbarActive, props.onMouseScroll]);
1046
1100
  const handleWheel = useCallback((event: ReactWheelEvent<HTMLDivElement>) => {
1047
1101
  markScrollbarActive();
1048
1102
  lastWheelAtRef.current = Date.now();
package/src/ui/host.tsx CHANGED
@@ -94,6 +94,12 @@ export interface BoxRenderable {
94
94
  [key: string]: unknown;
95
95
  }
96
96
 
97
+ export interface ScrollBarRenderable {
98
+ visible: boolean;
99
+ on?(event: "change", handler: () => void): void;
100
+ off?(event: "change", handler: () => void): void;
101
+ }
102
+
97
103
  export interface ScrollBoxRenderable {
98
104
  scrollTop: number;
99
105
  scrollLeft?: number;
@@ -107,8 +113,8 @@ export interface ScrollBoxRenderable {
107
113
  visible?: boolean;
108
114
  parent?: unknown;
109
115
  getBoundingClientRect?: () => { x: number; y: number; width: number; height: number };
110
- horizontalScrollBar?: { visible: boolean };
111
- verticalScrollBar?: { visible: boolean };
116
+ horizontalScrollBar?: ScrollBarRenderable;
117
+ verticalScrollBar?: ScrollBarRenderable;
112
118
  scrollTo(target: number | { x?: number; y?: number }, y?: number): void;
113
119
  scrollToPixels?(target: number | { x?: number; y?: number }, y?: number): void;
114
120
  }
package/src/ui/index.tsx CHANGED
@@ -39,6 +39,7 @@ export type {
39
39
  PixelResolution,
40
40
  RGBA as RGBAColor,
41
41
  RendererHost,
42
+ ScrollBarRenderable,
42
43
  ScrollBoxRenderable,
43
44
  ScrollBoxProps,
44
45
  SyntaxStyleLike,
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const VERSION = "0.7.1";
1
+ export const VERSION = "0.7.2";