@lightningtv/solid 3.1.14 → 3.1.16

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.
@@ -1,6 +1,7 @@
1
- import { Route, RoutePreloadFuncArgs, RouteProps } from "@solidjs/router";
1
+ import { Route, RoutePreloadFuncArgs, RouteProps } from '@solidjs/router';
2
2
  import * as s from 'solid-js';
3
- import { ElementNode } from "@lightningtv/solid";
3
+ import { ElementNode, activeElement } from '@lightningtv/solid';
4
+ import { chainFunctions } from './utils/chainFunctions.js';
4
5
 
5
6
  export interface KeepAliveElement {
6
7
  id: string;
@@ -11,26 +12,56 @@ export interface KeepAliveElement {
11
12
  }
12
13
 
13
14
  const keepAliveElements = new Map<string, KeepAliveElement>();
15
+ export const keepAliveRouteElements = new Map<string, KeepAliveElement>();
14
16
 
15
- export const storeKeepAlive = (
16
- element: KeepAliveElement
17
+ const _storeKeepAlive = (
18
+ map: Map<string, KeepAliveElement>,
19
+ element: KeepAliveElement,
17
20
  ): KeepAliveElement | undefined => {
18
- if (keepAliveElements.has(element.id)) {
19
- console.warn(`[KeepAlive] Element with id "${element.id}" already in cache. Recreating.`);
21
+ if (map.has(element.id)) {
22
+ console.warn(
23
+ `[KeepAlive] Element with id "${element.id}" already in cache.`,
24
+ );
20
25
  return element;
21
26
  }
22
- keepAliveElements.set(element.id, element);
27
+ map.set(element.id, element);
23
28
  return element;
24
29
  };
25
30
 
26
- export const removeKeepAlive = (id: string): void => {
27
- const element = keepAliveElements.get(id);
31
+ export const storeKeepAlive = (element: KeepAliveElement) =>
32
+ _storeKeepAlive(keepAliveElements, element);
33
+ export const storeKeepAliveRoute = (element: KeepAliveElement) =>
34
+ _storeKeepAlive(keepAliveRouteElements, element);
35
+
36
+ const _removeKeepAlive = (
37
+ map: Map<string, KeepAliveElement>,
38
+ id: string,
39
+ ): void => {
40
+ const element = map.get(id);
28
41
  if (element) {
42
+ (element.children as unknown as ElementNode).destroy();
29
43
  element.dispose();
30
- keepAliveElements.delete(id);
44
+ map.delete(id);
31
45
  }
32
46
  };
33
47
 
48
+ export const removeKeepAlive = (id: string): void =>
49
+ _removeKeepAlive(keepAliveElements, id);
50
+ export const removeKeepAliveRoute = (id: string): void =>
51
+ _removeKeepAlive(keepAliveRouteElements, id);
52
+
53
+ const _clearKeepAlive = (map: Map<string, KeepAliveElement>): void => {
54
+ map.forEach((element) => {
55
+ (element.children as unknown as ElementNode).destroy();
56
+ element.dispose();
57
+ });
58
+ map.clear();
59
+ };
60
+
61
+ export const clearKeepAlive = (): void => _clearKeepAlive(keepAliveElements);
62
+ export const clearKeepAliveRoute = (): void =>
63
+ _clearKeepAlive(keepAliveRouteElements);
64
+
34
65
  interface KeepAliveProps {
35
66
  id: string;
36
67
  shouldDispose?: (key: string) => boolean;
@@ -40,8 +71,16 @@ interface KeepAliveProps {
40
71
  }
41
72
 
42
73
  function wrapChildren(props: s.ParentProps<KeepAliveProps>) {
43
- const onRemove = props.onRemove || ((elm: ElementNode) => { elm.alpha = 0; });
44
- const onRender = props.onRender || ((elm: ElementNode) => { elm.alpha = 1; });
74
+ const onRemove =
75
+ props.onRemove ||
76
+ ((elm: ElementNode) => {
77
+ elm.alpha = 0;
78
+ });
79
+ const onRender =
80
+ props.onRender ||
81
+ ((elm: ElementNode) => {
82
+ elm.alpha = 1;
83
+ });
45
84
  const transition = props.transition || { alpha: true };
46
85
 
47
86
  return (
@@ -52,73 +91,140 @@ function wrapChildren(props: s.ParentProps<KeepAliveProps>) {
52
91
  forwardFocus={0}
53
92
  transition={transition}
54
93
  {...props}
55
- />)
94
+ />
95
+ );
56
96
  }
57
97
 
58
- export const KeepAlive = (props: s.ParentProps<KeepAliveProps>) => {
59
- let existing = keepAliveElements.get(props.id)
60
-
61
- if (existing && props.shouldDispose?.(props.id)) {
62
- existing.dispose();
63
- keepAliveElements.delete(props.id);
64
- existing = undefined;
65
- }
98
+ const createKeepAliveComponent = (
99
+ map: Map<string, KeepAliveElement>,
100
+ storeFn: (element: KeepAliveElement) => KeepAliveElement | undefined,
101
+ ) => {
102
+ return (props: s.ParentProps<KeepAliveProps>) => {
103
+ let existing = map.get(props.id);
66
104
 
67
- if (!existing) {
68
- return s.createRoot((dispose) => {
69
- const children = wrapChildren(props);
70
- storeKeepAlive({
71
- id: props.id,
72
- owner: s.getOwner(),
73
- children,
74
- dispose,
75
- });
76
- return children;
77
- });
78
- } else if (existing && !existing.children) {
79
- existing.children = s.runWithOwner(existing.owner, () => wrapChildren(props));
80
- }
81
- return existing.children;
82
- };
83
-
84
- export const KeepAliveRoute = <S extends string>(props: RouteProps<S> & {
85
- id?: string,
86
- path: string,
87
- component: s.Component<RouteProps<S>>,
88
- shouldDispose?: (key: string) => boolean,
89
- onRemove?: ElementNode['onRemove'];
90
- onRender?: ElementNode['onRender'];
91
- transition?: ElementNode['transition'];
92
- }) => {
93
- const key = props.id || props.path;
94
-
95
- const preload = props.preload ? (preloadProps: RoutePreloadFuncArgs) => {
96
- let existing = keepAliveElements.get(key)
97
-
98
- if (existing && props.shouldDispose?.(key)) {
105
+ if (
106
+ existing &&
107
+ (props.shouldDispose?.(props.id) ||
108
+ (existing.children as unknown as ElementNode)?.destroyed)
109
+ ) {
110
+ (existing.children as unknown as ElementNode).destroy();
99
111
  existing.dispose();
100
- keepAliveElements.delete(key);
112
+ map.delete(props.id);
101
113
  existing = undefined;
102
114
  }
103
115
 
104
116
  if (!existing) {
105
117
  return s.createRoot((dispose) => {
106
- storeKeepAlive({
107
- id: key,
118
+ const children = wrapChildren(props);
119
+ storeFn({
120
+ id: props.id,
108
121
  owner: s.getOwner(),
122
+ children,
109
123
  dispose,
110
- children: null,
111
124
  });
112
- return props.preload!(preloadProps);
125
+ return children;
113
126
  });
114
- } else if (existing.children) {
115
- (existing.children as unknown as ElementNode)?.setFocus();
127
+ } else if (existing && !existing.children) {
128
+ existing.children = s.runWithOwner(existing.owner, () =>
129
+ wrapChildren(props),
130
+ );
116
131
  }
117
- } : undefined;
132
+ return existing.children;
133
+ };
134
+ };
135
+
136
+ export const KeepAlive = createKeepAliveComponent(
137
+ keepAliveElements,
138
+ storeKeepAlive,
139
+ );
140
+ const KeepAliveRouteInternal = createKeepAliveComponent(
141
+ keepAliveRouteElements,
142
+ storeKeepAliveRoute,
143
+ );
118
144
 
119
- return (<Route {...props} preload={preload} component={(childProps) =>
120
- <KeepAlive id={key} onRemove={props.onRemove} onRender={props.onRender} transition={props.transition}>
121
- {props.component(childProps)}
122
- </KeepAlive>
123
- }/>);
145
+ export const KeepAliveRoute = <S extends string>(
146
+ props: RouteProps<S> & {
147
+ id?: string;
148
+ path: string;
149
+ component: s.Component<RouteProps<S>>;
150
+ shouldDispose?: (key: string) => boolean;
151
+ onRemove?: ElementNode['onRemove'];
152
+ onRender?: ElementNode['onRender'];
153
+ transition?: ElementNode['transition'];
154
+ },
155
+ ) => {
156
+ const key = props.id || props.path;
157
+ let savedFocusedElement: ElementNode | undefined;
158
+
159
+ const onRemove = chainFunctions(props.onRemove, (elm: ElementNode) => {
160
+ savedFocusedElement = activeElement() as ElementNode;
161
+ elm.alpha = 0;
162
+ });
163
+
164
+ const onRender = chainFunctions(props.onRender, (elm: ElementNode) => {
165
+ let isChild = false;
166
+ let current = savedFocusedElement;
167
+ while (current) {
168
+ if (current === elm) {
169
+ isChild = true;
170
+ break;
171
+ }
172
+ current = current.parent as ElementNode | undefined;
173
+ }
174
+
175
+ if (isChild && savedFocusedElement) {
176
+ savedFocusedElement.setFocus();
177
+ } else {
178
+ elm.setFocus();
179
+ }
180
+ elm.alpha = 1;
181
+ });
182
+
183
+ const preload = props.preload
184
+ ? (preloadProps: RoutePreloadFuncArgs) => {
185
+ let existing = keepAliveRouteElements.get(key);
186
+
187
+ if (
188
+ existing &&
189
+ (props.shouldDispose?.(key) ||
190
+ (existing.children as unknown as ElementNode)?.destroyed)
191
+ ) {
192
+ (existing.children as unknown as ElementNode).destroy();
193
+ existing.dispose();
194
+ keepAliveRouteElements.delete(key);
195
+ existing = undefined;
196
+ }
197
+
198
+ if (!existing) {
199
+ return s.createRoot((dispose) => {
200
+ storeKeepAliveRoute({
201
+ id: key,
202
+ owner: s.getOwner(),
203
+ dispose,
204
+ children: null,
205
+ });
206
+ return props.preload!(preloadProps);
207
+ });
208
+ } else if (existing.children) {
209
+ (existing.children as unknown as ElementNode)?.setFocus();
210
+ }
211
+ }
212
+ : undefined;
213
+
214
+ return (
215
+ <Route
216
+ {...props}
217
+ preload={preload}
218
+ component={(childProps) => (
219
+ <KeepAliveRouteInternal
220
+ id={key}
221
+ onRemove={onRemove}
222
+ onRender={onRender}
223
+ transition={props.transition}
224
+ >
225
+ {props.component(childProps)}
226
+ </KeepAliveRouteInternal>
227
+ )}
228
+ />
229
+ );
124
230
  };