@lightningtv/solid 2.12.3 → 2.12.5

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.
@@ -20,7 +20,6 @@ type RenderableNode = ElementNode | ElementText | TextNode;
20
20
  interface MouseStateOptions {
21
21
  hoverState: CustomState;
22
22
  pressedState: CustomState;
23
- pressedStateDuration?: number;
24
23
  }
25
24
 
26
25
  type UseMouseOptions =
@@ -29,7 +28,6 @@ type UseMouseOptions =
29
28
 
30
29
  declare module '@lightningtv/core' {
31
30
  interface ElementNode {
32
- onEnter?: () => void;
33
31
  /** function to be called on mouse click */
34
32
  onMouseClick?: (
35
33
  this: ElementNode,
@@ -39,8 +37,6 @@ declare module '@lightningtv/core' {
39
37
  }
40
38
  }
41
39
 
42
- const DEFAULT_PRESSED_STATE_DURATION = 150;
43
-
44
40
  export function addCustomStateToElement(
45
41
  element: RenderableNode,
46
42
  state: CustomState,
@@ -168,25 +164,22 @@ function findElementByActiveElement(e: MouseEvent): ElementNode | null {
168
164
  function applyPressedState(
169
165
  element: ElementNode,
170
166
  pressedState: CustomState,
171
- pressedStateDuration: number = DEFAULT_PRESSED_STATE_DURATION,
172
167
  ): void {
173
168
  addCustomStateToElement(element, pressedState);
174
- setTimeout(() => {
175
- removeCustomStateFromElement(element, pressedState);
176
- }, pressedStateDuration);
177
169
  }
178
170
 
179
171
  function handleElementClick(
180
172
  clickedElement: ElementNode,
181
173
  e: MouseEvent,
182
174
  customStates?: MouseStateOptions,
175
+ pressedElementRef?: { current: ElementNode | null },
183
176
  ): void {
184
- if (customStates?.pressedState) {
185
- applyPressedState(
186
- clickedElement,
177
+ if (customStates?.pressedState && pressedElementRef?.current) {
178
+ removeCustomStateFromElement(
179
+ pressedElementRef.current,
187
180
  customStates.pressedState,
188
- customStates.pressedStateDuration,
189
181
  );
182
+ pressedElementRef.current = null;
190
183
  }
191
184
 
192
185
  if (isFunc(clickedElement.onMouseClick)) {
@@ -211,6 +204,7 @@ function handleElementClick(
211
204
  function createHandleClick<TApp extends ElementNode>(
212
205
  myApp: TApp,
213
206
  customStates?: MouseStateOptions,
207
+ pressedElementRef?: { current: ElementNode | null },
214
208
  ) {
215
209
  return (e: MouseEvent): void => {
216
210
  const clickedElement = customStates
@@ -226,7 +220,35 @@ function createHandleClick<TApp extends ElementNode>(
226
220
  return;
227
221
  }
228
222
 
229
- handleElementClick(clickedElement, e, customStates);
223
+ handleElementClick(clickedElement, e, customStates, pressedElementRef);
224
+ };
225
+ }
226
+
227
+ function createHandleMouseDown<TApp extends ElementNode>(
228
+ myApp: TApp,
229
+ customStates?: MouseStateOptions,
230
+ pressedElementRef?: { current: ElementNode | null },
231
+ ) {
232
+ return (e: MouseEvent): void => {
233
+ if (!customStates) {
234
+ return;
235
+ }
236
+
237
+ const pressedElement = findElementWithCustomState(
238
+ myApp,
239
+ e.clientX,
240
+ e.clientY,
241
+ customStates.hoverState,
242
+ );
243
+
244
+ if (!pressedElement) {
245
+ return;
246
+ }
247
+
248
+ applyPressedState(pressedElement, customStates.pressedState);
249
+ if (pressedElementRef) {
250
+ pressedElementRef.current = pressedElement;
251
+ }
230
252
  };
231
253
  }
232
254
 
@@ -330,16 +352,26 @@ export function useMouse<TApp extends ElementNode = ElementNode>(
330
352
  const pos = useMousePosition();
331
353
  const scheduled = createScheduled((fn) => throttle(fn, throttleBy));
332
354
  let previousElement: ElementNode | null = null;
355
+ const pressedElementRef: { current: ElementNode | null } = { current: null };
333
356
  const customStates = options?.customStates;
334
357
  const hoverState = customStates?.hoverState;
335
- const handleClick = createHandleClick(myApp, customStates);
358
+ const handleClick = createHandleClick(myApp, customStates, pressedElementRef);
359
+ const handleMouseDown = createHandleMouseDown(
360
+ myApp,
361
+ customStates,
362
+ pressedElementRef,
363
+ );
336
364
  const owner = getOwner();
337
365
  const handleClickContext = (e: MouseEvent) => {
338
366
  runWithOwner(owner, () => handleClick(e));
339
367
  };
368
+ const handleMouseDownContext = (e: MouseEvent) => {
369
+ runWithOwner(owner, () => handleMouseDown(e));
370
+ };
340
371
 
341
372
  makeEventListener(window, 'wheel', handleScroll);
342
373
  makeEventListener(window, 'click', handleClickContext);
374
+ makeEventListener(window, 'mousedown', handleMouseDownContext);
343
375
  createEffect(() => {
344
376
  if (scheduled()) {
345
377
  const result = getChildrenByPosition(myApp, pos.x, pos.y).filter(
@@ -138,7 +138,7 @@ export function moveSelection(
138
138
  if (selected === -1) {
139
139
  if (
140
140
  !idxInArray(el.selected, el.children) ||
141
- el.children[el.selected]!.skipFocus ||
141
+ el.children[el.selected]?.skipFocus ||
142
142
  lng.isFocused(el.children[el.selected]!)
143
143
  ) {
144
144
  return false;
@@ -15,10 +15,11 @@ export type Scroller = (
15
15
  // Adds properties expected by withScrolling
16
16
  export interface ScrollableElement extends ElementNode {
17
17
  scrollIndex?: number;
18
- scroll?: 'always' | 'none' | 'edge' | 'auto' | 'center';
18
+ scroll?: 'always' | 'none' | 'edge' | 'auto' | 'center' | 'bounded';
19
19
  selected: number;
20
20
  offset?: number;
21
21
  endOffset?: number;
22
+ upCount?: number;
22
23
  onScrolled?: (
23
24
  elm: ScrollableElement,
24
25
  offset: number,
@@ -40,6 +41,19 @@ const isNotShown = (node: ElementNode | ElementText) => {
40
41
  Always scroll moves the list every time
41
42
  */
42
43
 
44
+ /**
45
+ * Checks if the selected index is in the non-scrollable zone (last upCount items).
46
+ */
47
+ export function checkIsInNonScrollableZone(
48
+ componentRef: ScrollableElement,
49
+ ): boolean {
50
+ const totalItems = componentRef.children.length;
51
+ const upCount = componentRef.upCount || 6;
52
+ const selected = componentRef.selected || 0;
53
+ const nonScrollableZoneStart = Math.max(0, totalItems - upCount);
54
+ return selected >= nonScrollableZoneStart;
55
+ }
56
+
43
57
  /** @deprecated Use {@link scrollRow} or {@link scrollColumn} */
44
58
  export function withScrolling(isRow: boolean): Scroller {
45
59
  const dimension = isRow ? 'width' : 'height';
@@ -135,6 +149,35 @@ export function withScrolling(isRow: boolean): Scroller {
135
149
  nextPosition = -selectedPosition + (screenSize - selectedSizeScaled) / 2;
136
150
  } else if (scroll === 'always') {
137
151
  nextPosition = -selectedPosition + offset;
152
+ } else if (scroll === 'bounded') {
153
+ const totalItems = componentRef.children.length;
154
+ const upCount = componentRef.upCount || 6;
155
+ const nonScrollableZoneStart = Math.max(0, totalItems - upCount);
156
+ const isInNonScrollableZone = selected >= nonScrollableZoneStart;
157
+ const isFirstOfNonScrollableZone = selected === nonScrollableZoneStart;
158
+ const isEnteringZone =
159
+ isFirstOfNonScrollableZone &&
160
+ lastSelected !== undefined &&
161
+ lastSelected < nonScrollableZoneStart;
162
+
163
+ if (!isInNonScrollableZone) {
164
+ nextPosition = -selectedPosition + offset;
165
+ } else if (isIncrementing) {
166
+ if (isEnteringZone) {
167
+ const firstOfZoneElement =
168
+ componentRef.children[nonScrollableZoneStart];
169
+ const firstOfZonePosition = firstOfZoneElement?.[axis] ?? 0;
170
+ nextPosition = firstOfZoneElement
171
+ ? -firstOfZonePosition + offset
172
+ : rootPosition;
173
+ } else {
174
+ nextPosition = rootPosition;
175
+ }
176
+ } else if (isFirstOfNonScrollableZone) {
177
+ nextPosition = -selectedPosition + offset;
178
+ } else {
179
+ nextPosition = rootPosition;
180
+ }
138
181
  } else if (scroll === 'center') {
139
182
  const centerPosition =
140
183
  -selectedPosition +
@@ -160,7 +203,6 @@ export function withScrolling(isRow: boolean): Scroller {
160
203
  nextPosition = rootPosition + selectedSize + gap;
161
204
  }
162
205
  } else if (isIncrementing) {
163
- //nextPosition = -selectedPosition + offset;
164
206
  nextPosition = rootPosition - selectedSize - gap;
165
207
  } else {
166
208
  nextPosition = rootPosition + selectedSize + gap;
@@ -174,7 +216,7 @@ export function withScrolling(isRow: boolean): Scroller {
174
216
 
175
217
  // Prevent container from moving beyond bounds
176
218
  nextPosition =
177
- isIncrementing && scroll !== 'always'
219
+ isIncrementing && scroll !== 'always' && scroll !== 'bounded'
178
220
  ? Math.max(nextPosition, maxOffset)
179
221
  : Math.min(nextPosition, offset);
180
222