lilact 0.0.0 → 0.2.0

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.
Files changed (87) hide show
  1. package/README.md +13 -2
  2. package/dist/{lilact.development.js → lilact.development.min.js} +690 -315
  3. package/dist/lilact.development.min.js.LICENSE.txt +77 -0
  4. package/dist/lilact.development.min.js.map +1 -0
  5. package/dist/lilact.production.min.js +1 -1
  6. package/dist/lilact.production.min.js.map +1 -1
  7. package/docs/assets/navigation.js +1 -1
  8. package/docs/assets/search.js +1 -1
  9. package/docs/classes/accessories.ErrorBoundary.html +11 -10
  10. package/docs/classes/accessories.Suspense.html +12 -22
  11. package/docs/classes/components.Component.html +11 -10
  12. package/docs/classes/components.HTMLComponent.html +11 -10
  13. package/docs/classes/components.RootComponent.html +11 -10
  14. package/docs/functions/accessories.Spinner.html +1 -1
  15. package/docs/functions/components.createComponent.html +1 -1
  16. package/docs/functions/components.createRoot.html +1 -1
  17. package/docs/functions/components.render.html +1 -1
  18. package/docs/functions/hooks.createContext.html +1 -1
  19. package/docs/functions/hooks.forwardRef.html +4 -0
  20. package/docs/functions/hooks.useActionState.html +1 -1
  21. package/docs/functions/hooks.useImperativeHandle.html +7 -0
  22. package/docs/functions/hooks.useReducer.html +1 -1
  23. package/docs/functions/hooks.useState.html +2 -2
  24. package/docs/functions/hooks.useTransition.html +1 -1
  25. package/docs/functions/jsx.transpileJSX.html +1 -1
  26. package/docs/functions/run.lazy.html +7 -0
  27. package/docs/functions/run.require.html +11 -11
  28. package/docs/functions/run.run.html +3 -5
  29. package/docs/functions/run.runScripts.html +1 -1
  30. package/docs/functions/run.traceError.html +1 -1
  31. package/docs/functions/transition.CSSTransition.html +1 -1
  32. package/docs/functions/transition.Transition.html +15 -0
  33. package/docs/functions/transition.TransitionGroup.html +1 -1
  34. package/docs/index.html +8 -2
  35. package/docs/modules/hooks.html +1 -1
  36. package/docs/modules/jsx.html +1 -1
  37. package/docs/modules/run.html +1 -1
  38. package/docs/modules/transition.html +1 -1
  39. package/docs/static/demos/actionstate.jsx +5 -7
  40. package/docs/static/demos/context.jsx +2 -4
  41. package/docs/static/demos/css-transition.jsx +1 -1
  42. package/docs/static/demos/lazy.jsx +14 -0
  43. package/docs/static/demos/modal.jsx +14 -16
  44. package/docs/static/demos/proptypes.jsx +6 -9
  45. package/docs/static/demos/reducer.jsx +22 -24
  46. package/docs/static/demos/redux.jsx +1 -1
  47. package/docs/static/demos/router.jsx +1 -8
  48. package/docs/static/demos/stopwatch.jsx +1 -1
  49. package/docs/static/demos/suspense.jsx +5 -7
  50. package/docs/static/demos/transition.jsx +25 -25
  51. package/{root/demos/use-differed.jsx → docs/static/demos/use-deffered.jsx} +1 -1
  52. package/docs/static/demos/usetransition.jsx +2 -4
  53. package/docs/static/index 2.html +95 -0
  54. package/docs/static/index.html +27 -29
  55. package/docs/static/{lilact.development.js → lilact.development.min.js} +690 -315
  56. package/docs/static/lilact.production.min.js +1 -1
  57. package/package.json +2 -2
  58. package/root/demos/actionstate.jsx +5 -7
  59. package/root/demos/context.jsx +2 -4
  60. package/root/demos/css-transition.jsx +1 -1
  61. package/root/demos/lazy.jsx +14 -0
  62. package/root/demos/modal.jsx +14 -16
  63. package/root/demos/proptypes.jsx +6 -9
  64. package/root/demos/reducer.jsx +22 -24
  65. package/root/demos/redux.jsx +1 -1
  66. package/root/demos/router.jsx +1 -8
  67. package/root/demos/stopwatch.jsx +1 -1
  68. package/root/demos/suspense.jsx +5 -7
  69. package/root/demos/transition.jsx +25 -25
  70. package/{docs/static/demos/use-differed.jsx → root/demos/use-deffered.jsx} +1 -1
  71. package/root/demos/usetransition.jsx +2 -4
  72. package/root/index 2.html +95 -0
  73. package/root/index.html +27 -29
  74. package/root/{lilact.development.js → lilact.development.min.js} +690 -315
  75. package/root/lilact.production.min.js +1 -1
  76. package/src/accessories.jsx +12 -11
  77. package/src/components.jsx +11 -5
  78. package/src/hooks.jsx +47 -6
  79. package/src/jsx.js +1 -0
  80. package/src/lilact.jsx +5 -7
  81. package/src/pane.jsx +287 -0
  82. package/src/run.jsx +91 -43
  83. package/src/transition.jsx +32 -21
  84. package/webpack.config.js +2 -12
  85. package/dist/lilact.development.js.map +0 -1
  86. package/docs/classes/transition.Transition.html +0 -31
  87. package/docs/variables/jsx.transpilerConfig.html +0 -1
package/src/lilact.jsx CHANGED
@@ -47,6 +47,7 @@ import * as misc from './misc.jsx';
47
47
 
48
48
  import * as router from './router.jsx';
49
49
  import * as accessories from './accessories.jsx';
50
+ import {ResizablePane} from './pane.jsx';
50
51
 
51
52
  import {transpileJSX, transpilerConfig} from "./jsx";
52
53
 
@@ -61,7 +62,7 @@ import {transpileJSX, transpilerConfig} from "./jsx";
61
62
  * @property emotion - Handle to Emotion’s CSS-in-JS package for styling (https://github.com/emotion-js/emotion).
62
63
  */
63
64
  export const Lilact =
64
- {
65
+ {
65
66
 
66
67
  VERSION: "beta.0",
67
68
 
@@ -86,27 +87,24 @@ export const Lilact =
86
87
  ...router,
87
88
  ...accessories,
88
89
 
90
+ ResizablePane,
91
+
89
92
  transpileJSX,
90
93
  transpilerConfig,
91
94
 
92
95
  // Dependencies ()
93
96
 
94
97
  PropTypes,
98
+ //redux: {...redux, createSlice, createAsyncThunk},
95
99
  redux,
96
100
  emotion,
97
101
 
98
102
  }
99
103
 
100
-
101
- // or, if you prefer DOMContentLoaded:
102
104
  document.addEventListener('DOMContentLoaded', () => {
103
105
  Lilact.runScripts();
104
106
  });
105
107
 
106
- function yourFunctionHere() {
107
- // ...
108
- }
109
-
110
108
  ʔ if(DEBUG) {
111
109
  console.log(`Lilact (Version: ${Lilact.VERSION}) - Debug Mode`);
112
110
  console.log(`Copyright(C) 2024-2026 Arash Kazemi <contact.arash.kazemi@gmail.com>`);
package/src/pane.jsx ADDED
@@ -0,0 +1,287 @@
1
+ import { useEffect, useLayoutEffect, useMemo, useRef, useState, forwardRef, useImperativeHandle } from "./hooks.jsx";
2
+ import { Children } from "./misc.jsx";
3
+
4
+ function clamp(n, min, max) {
5
+ return Math.max(min, Math.min(max, n));
6
+ }
7
+
8
+ /**
9
+ * ResizablePane
10
+ *
11
+ * Features:
12
+ * - Supports "horizontal" (left/top size affects width) and "vertical" (affects height)
13
+ * - Initial position via props.position
14
+ * - Methods to set mode and position via ref:
15
+ * ref.current.setMode(mode)
16
+ * ref.current.setPosition(position)
17
+ * - Callback when size changes via onSizeChange
18
+ * - Children are rendered in two separate containers (no portals)
19
+ */
20
+ export const ResizablePane = forwardRef(function Pane(
21
+ {
22
+ mode = "horizontal", // "horizontal" | "vertical"
23
+ initialPosition, // optional (alias-ish to position)
24
+ position, // controlled position (number)
25
+ defaultPosition = 0.5, // used if position/initialPosition not provided
26
+ min = 0.1, // clamp in "percent of container" units: 0..1
27
+ max = 0.9, // clamp in "percent of container" units: 0..1
28
+ splitterSize = 8, // px thickness of the drag handle
29
+ onSizeChange, // (newPosition: number) => void
30
+ style,
31
+ className,
32
+ leftPaneStyle,
33
+ rightPaneStyle,
34
+ splitterStyle,
35
+ children, // expects two children: [A, B]
36
+ },
37
+ ref
38
+ ) {
39
+ const [internalMode, setInternalMode] = useState(mode);
40
+ const [pos, setPos] = useState(() => {
41
+ const start =
42
+ position ?? initialPosition ?? (defaultPosition != null ? defaultPosition : 0.5);
43
+ return clamp(start, min, max);
44
+ });
45
+
46
+ const containerRef = useRef(null);
47
+ const draggingRef = useRef(false);
48
+
49
+ const panes = Children.toArray(children);
50
+ const leftChild = panes[0] ?? null;
51
+ const rightChild = panes[1] ?? null;
52
+
53
+ const modeResolved = mode != null ? mode : internalMode;
54
+
55
+ // Keep internalMode aligned if mode is provided as a prop
56
+ useEffect(() => {
57
+ if (mode != null) setInternalMode(mode);
58
+ }, [mode]);
59
+
60
+ // Controlled position: update state when prop changes
61
+ useEffect(() => {
62
+ if (position == null) return;
63
+ setPos(clamp(position, min, max));
64
+ }, [position, min, max]);
65
+
66
+ const setPosition = (newPos) => {
67
+ const next = clamp(newPos, min, max);
68
+ if (position == null) setPos(next); // only update internal state if uncontrolled
69
+ onSizeChange?.(next);
70
+ };
71
+
72
+ const setMode = (nextMode) => {
73
+ const m = nextMode === "vertical" ? "vertical" : "horizontal";
74
+ if (mode == null) setInternalMode(m);
75
+ // If mode is controlled via prop, consumer should re-render with new prop.
76
+ };
77
+
78
+ useImperativeHandle(
79
+ ref,
80
+ () => ({
81
+ setPosition,
82
+ setMode,
83
+ getPosition: () => (position == null ? pos : clamp(position, min, max)),
84
+ getMode: () => modeResolved,
85
+ }),
86
+ [pos, position, min, max, modeResolved, onSizeChange, mode]
87
+ );
88
+
89
+ const updateFromClientXorY = (clientX, clientY) => {
90
+ const el = containerRef.current;
91
+ if (!el) return;
92
+
93
+ const rect = el.getBoundingClientRect();
94
+ let next;
95
+
96
+ if (modeResolved === "horizontal") {
97
+ const usable = rect.width;
98
+ if (usable <= 0) return;
99
+ next = (clientX - rect.left) / usable;
100
+ } else {
101
+ const usable = rect.height;
102
+ if (usable <= 0) return;
103
+ next = (clientY - rect.top) / usable;
104
+ }
105
+ setPosition(next);
106
+ };
107
+
108
+ const onPointerDown = (e) => {
109
+ e.preventDefault();
110
+ draggingRef.current = true;
111
+
112
+ // Capture pointer so we still get events outside the handle area.
113
+ try {
114
+ e.currentTarget.setPointerCapture?.(e.pointerId);
115
+ } catch {
116
+ // ignore
117
+ }
118
+
119
+ updateFromClientXorY(e.clientX, e.clientY);
120
+ };
121
+
122
+ const onPointerMove = (e) => {
123
+ if (!draggingRef.current) return;
124
+ updateFromClientXorY(e.clientX, e.clientY);
125
+ };
126
+
127
+ const stopDragging = () => {
128
+ draggingRef.current = false;
129
+ };
130
+
131
+ // Attach global listeners to ensure smooth dragging even if pointer capture fails
132
+ useEffect(() => {
133
+ const handleMove = (e) => {
134
+ if (!draggingRef.current) return;
135
+ updateFromClientXorY(e.clientX, e.clientY);
136
+ };
137
+ const handleUp = () => stopDragging();
138
+
139
+ window.addEventListener("pointermove", handleMove, { passive: false });
140
+ window.addEventListener("pointerup", handleUp, { passive: true });
141
+ window.addEventListener("pointercancel", handleUp, { passive: true });
142
+
143
+ return () => {
144
+ window.removeEventListener("pointermove", handleMove);
145
+ window.removeEventListener("pointerup", handleUp);
146
+ window.removeEventListener("pointercancel", handleUp);
147
+ };
148
+ }, [modeResolved, min, max, position, onSizeChange]);
149
+
150
+ const posResolved = position == null ? pos : clamp(position, min, max);
151
+
152
+ const sizes = useMemo(() => {
153
+ if (modeResolved === "horizontal") {
154
+ // left width = pos, splitter width = splitterSize, right flexes
155
+ // We set left/right as percentages and keep splitter fixed in px.
156
+ return {
157
+ left: `${posResolved * 100}%`,
158
+ right: `calc(${100 - posResolved * 100}% - ${splitterSize}px)`,
159
+ };
160
+ }
161
+ return {
162
+ left: `${posResolved * 100}%`,
163
+ right: `calc(${100 - posResolved * 100}% - ${splitterSize}px)`,
164
+ };
165
+ }, [modeResolved, posResolved, splitterSize]);
166
+
167
+ // Improve keyboard accessibility (arrow keys when handle is focused)
168
+ const onSplitterKeyDown = (e) => {
169
+ const step = 0.02;
170
+ let delta = 0;
171
+ if (modeResolved === "horizontal") {
172
+ if (e.key === "ArrowLeft") delta = -step;
173
+ if (e.key === "ArrowRight") delta = step;
174
+ } else {
175
+ if (e.key === "ArrowUp") delta = -step;
176
+ if (e.key === "ArrowDown") delta = step;
177
+ }
178
+ if (delta !== 0) {
179
+ e.preventDefault();
180
+ setPosition(posResolved + delta);
181
+ }
182
+ };
183
+
184
+ // Ensure position clamps correctly if min/max change
185
+ useLayoutEffect(() => {
186
+ setPos((p) => clamp(p, min, max));
187
+ }, [min, max]);
188
+
189
+ const rootStyle = {
190
+ display: "flex",
191
+ width: "100%",
192
+ height: "100%",
193
+ overflow: "hidden",
194
+ touchAction: "none",
195
+ ...(style || {}),
196
+ flexDirection: modeResolved === "horizontal" ? "row" : "column",
197
+ };
198
+
199
+ const leftPaneComputed =
200
+ modeResolved === "horizontal"
201
+ ? {
202
+ width: sizes.left,
203
+ flex: `0 0 ${sizes.left}`,
204
+ overflow: "auto",
205
+ ...(leftPaneStyle || {}),
206
+ }
207
+ : {
208
+ height: sizes.left,
209
+ flex: `0 0 ${sizes.left}`,
210
+ overflow: "auto",
211
+ ...(leftPaneStyle || {}),
212
+ };
213
+
214
+ const rightPaneComputed =
215
+ modeResolved === "horizontal"
216
+ ? {
217
+ width: `calc(${100 - posResolved * 100}% - ${splitterSize}px)`,
218
+ flex: `1 1 auto`,
219
+ overflow: "auto",
220
+ ...(rightPaneStyle || {}),
221
+ }
222
+ : {
223
+ height: `calc(${100 - posResolved * 100}% - ${splitterSize}px)`,
224
+ flex: `1 1 auto`,
225
+ overflow: "auto",
226
+ ...(rightPaneStyle || {}),
227
+ };
228
+
229
+ const splitterComputed =
230
+ modeResolved === "horizontal"
231
+ ? {
232
+ width: `${splitterSize}px`,
233
+ flex: `0 0 ${splitterSize}px`,
234
+ cursor: "col-resize",
235
+ background: "transparent",
236
+ ...(splitterStyle || {}),
237
+ }
238
+ : {
239
+ height: `${splitterSize}px`,
240
+ flex: `0 0 ${splitterSize}px`,
241
+ cursor: "row-resize",
242
+ background: "transparent",
243
+ ...(splitterStyle || {}),
244
+ };
245
+
246
+ const dividerVisualStyle =
247
+ modeResolved === "horizontal"
248
+ ? {
249
+ height: "100%",
250
+ width: "100%",
251
+ background: "rgba(0,0,0,0.08)",
252
+ }
253
+ : {
254
+ width: "100%",
255
+ height: "100%",
256
+ background: "rgba(0,0,0,0.08)",
257
+ };
258
+
259
+ return (
260
+ <div
261
+ ref={containerRef}
262
+ className={className}
263
+ style={rootStyle}
264
+ onPointerMove={onPointerMove}
265
+ >
266
+ <div style={leftPaneComputed}>{leftChild}</div>
267
+
268
+ <div
269
+ role="separator"
270
+ aria-orientation={modeResolved === "horizontal" ? "vertical" : "horizontal"}
271
+ aria-valuemin={min}
272
+ aria-valuemax={max}
273
+ aria-valuenow={posResolved}
274
+ tabIndex={0}
275
+ onPointerDown={onPointerDown}
276
+ onPointerUp={stopDragging}
277
+ onPointerCancel={stopDragging}
278
+ onKeyDown={onSplitterKeyDown}
279
+ style={splitterComputed}
280
+ >
281
+ <div style={dividerVisualStyle} />
282
+ </div>
283
+
284
+ <div style={rightPaneComputed}>{rightChild}</div>
285
+ </div>
286
+ );
287
+ });
package/src/run.jsx CHANGED
@@ -10,10 +10,10 @@
10
10
  modification, are permitted provided that the following conditions are met:
11
11
 
12
12
  * Redistributions of source code must retain the above copyright
13
- notice, this list of conditions and the following disclaimer.
13
+ notice, this list of conditions and the following disclaimer.
14
14
  * Redistributions in binary form must reproduce the above copyright
15
- notice, this list of conditions and the following disclaimer in the
16
- documentation and/or other materials provided with the distribution.
15
+ notice, this list of conditions and the following disclaimer in the
16
+ documentation and/or other materials provided with the distribution.
17
17
 
18
18
  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
19
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
@@ -28,18 +28,17 @@
28
28
 
29
29
  */
30
30
 
31
- ʔ defineSymbols ( "LILACT", [ "CORE", "COMPONENT" ] ) ʔ
31
+ ʔ defineSymbols ( "LILACT", [ "CORE", "COMPONENT", "LAZY" ] ) ʔ
32
32
 
33
33
  import Lilact from './lilact.jsx';
34
34
 
35
+
35
36
  /**
36
37
  * Runs a jsx script. All scripts can access Lilact namespace as a global object.
37
38
  *
38
39
  * Notice that `run` uses eval internally. There is no standard way of getting the error location when
39
- * running it with eval. Lilact has its own experimental workarounds for this, but generally speaking it
40
- * works better when an script is loaded from a file (using <script type='text/jsx' src=''.../> and is not
41
- * inline, because that way the generated sourcemap will be used by the browser and the exceptions will be
42
- * located automatically. When an inline script raises exception, the original exception does not report
40
+ * running it with eval. Lilact has its own experimental workarounds for this.
41
+ * When an inline script raises an exception, the original exception does not report
43
42
  * which eval has caused the error, so Lilact should guess it from some labels, and for the sake of
44
43
  * efficiency, it is block based and not exact.
45
44
  *
@@ -109,50 +108,62 @@ export function run(jsx, path=`<string input ${++Lilact.eval_num}>`, is_inline=t
109
108
  }
110
109
  }
111
110
 
112
- // export async function importAsync(path)
113
- // {
114
- // const res = await fetch(path);
115
- // const text = await res.text();
116
-
117
- // return Lilact.run(text, path, false);
118
- // }
119
111
 
120
112
  /**
121
113
  * Loads a jsx script from a path. All scripts can access Lilact namespace as a global object.
122
114
  *
123
- * Lilact require loads synchronously, as it is expected to be loaded on the next instruction.
124
- * It is better to load existing scripts using <script type="text/jsx" src+"..."/> if possible.
115
+ * `Lilact.require` loads synchronously, as it is expected to be loaded on the next instruction.
116
+ *
117
+ * As running the transpiled scripts rely on `eval`, it is not possible to use module imports
118
+ * and exports. So to import you should use `const {useState} = Lilact` convention. And to
119
+ * export, you should use `module.exports = ...`. `Lilact.require` returns `module.exports` value
120
+ * so you can import different modules using the convention above.
121
+ *
122
+ * If the path is in the format #id, it will query the document for a script element with the given
123
+ * id and run its contents.
124
+ *
125
+ * If require is called inside the function given to lazy, it will run async. See `lazy`.
126
+ *
127
+ * @param path - The path to the required file. Must be either absolute path or relative to the current
128
+ * document’s URL (the page/location that initiated the request).
125
129
  *
126
- * Notice that run uses eval internally. There is no standard way of getting the error location when
127
- * running it with eval. Lilact has its own experimental workarounds for this, but generally speaking it
128
- * works better when an script is loaded from a file (using <script type='text/jsx' src=''.../> and is not
129
- * inline, because that way the generated sourcemap will be used by the browser and the exceptions will be
130
- * located automatically. When an inline script raises exception, the original exception does not report
131
- * which eval has caused the error, so Lilact should guess it from some labels, and for the sake of
132
- * efficiency, it is block based and not exact.
133
- *
134
- * @param path - The path to the required file.
135
130
  * @param force_update - To treat the code as inline. The main difference at the moment is that inline code doesn't include sourcemap.
136
131
  *
137
132
  * @returns An array representation of the children.
138
133
  */
139
134
  export function require(path, force_update)
140
135
  {
141
- if(Lilact.transpilerConfig.required[path]!==undefined && !force_update) return Lilact.transpilerConfig.required[path].module;
136
+ //if(Lilact.transpilerConfig.required[path]!==undefined && !force_update) return Lilact.transpilerConfig.required[path].module;
142
137
 
143
138
  if(path[0]==='#') {
144
139
 
145
- const el = document.querySelector(path);
140
+ const el = document.getElementById(path);
146
141
 
147
142
  if(el) {
148
143
  return Lilact.run(el.innerText, path);
149
144
  }
150
145
 
151
146
  }
147
+ else if(Lilact?.[LAZY]) {
148
+ Lilact[LAZY]=false;
149
+
150
+ return fetch(path)
151
+ .then(res => {
152
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
153
+ return res.text();
154
+ })
155
+ .then(res => {
156
+ res = Lilact.run(res, path, false);
157
+ // todo: this is for the lazy, so we detect default, should we do it in sync mode too?
158
+ return res?.default ?? res;
159
+ })
160
+ .catch(err => {
161
+ throw err;
162
+ });
163
+ }
152
164
  else {
153
-
154
165
  // note: this makes a sync request. this is not advised,
155
- // but import should be sync. for an async solution use importAsync.
166
+ // but import should be sync. for an async solution use lazy and suspense.
156
167
 
157
168
  const request = new XMLHttpRequest();
158
169
  request.open("GET", path, false);
@@ -167,6 +178,48 @@ export function require(path, force_update)
167
178
  }
168
179
 
169
180
 
181
+ /**
182
+ * Wrapper that enables async, code-split component loading. `lazy` should be used
183
+ * outside the component definintion or it will produce new components on each rerender.
184
+ *
185
+ * @param factory - A function with **no arguments** that returns a `Promise`.
186
+ * The promise must resolve to a module whose module.exports.default is a Lilact component
187
+ * or otherwise it will be whatever the module.exports is set to.
188
+ *
189
+ * @returns A Lilact component that should be rendered inside a {@link Suspense} boundary.
190
+ */
191
+ export function lazy(factory) {
192
+ let status = "pending"; // pending | success | error
193
+ let result; // component | error
194
+
195
+ Lilact[LAZY] = true;
196
+ result = factory();
197
+
198
+ if(Lilact.isThenable(result)) {
199
+ result.then(
200
+ (mod) => {
201
+ status = "success";
202
+ result = mod;
203
+ return result;
204
+ },
205
+ (err) => {
206
+ status = "error";
207
+ result = err;
208
+ throw err;
209
+ }
210
+ );
211
+ }
212
+
213
+ function LazyComponent(props) {
214
+ if (status === "pending") throw result;
215
+ if (status === "error") throw result;
216
+ const Component = result;
217
+ return <Component {...props} />;
218
+ }
219
+
220
+ return LazyComponent;
221
+ }
222
+
170
223
  /**
171
224
  * Debug tool to get the Lilact traced location of an error. It can also produce some block based stack trace
172
225
  * if `Lilact.transpilerConfig.enableLabelStack` is set to `true` before loading the script. This is `false`
@@ -236,21 +289,16 @@ export function traceError(err)
236
289
  }
237
290
 
238
291
  function scanScriptTagsWithType() {
239
- const scripts = Array.from(
240
- document.querySelectorAll('script[type="text/jsx"]')
241
- );
242
-
243
- return scripts.map((el) => ({
244
- src: el.getAttribute("src") ?? null,
245
- content: el.textContent ?? ""
246
- }));
292
+ const scripts = Array.from(
293
+ document.querySelectorAll('script[type="text/jsx"]')
294
+ );
295
+
296
+ return scripts.map((el) => ({
297
+ src: el.getAttribute("src") ?? null,
298
+ content: el.textContent ?? ""
299
+ }));
247
300
  }
248
301
 
249
- // usage (in browser):
250
- // const result = scanScriptTagsWithType();
251
- // console.log(result);
252
-
253
-
254
302
  /**
255
303
  * Scans the whole documents and runs all the script elements with type `text/jsx`.
256
304
  * It is automatically attached to document.onload when Lilact is loaded.
@@ -32,10 +32,10 @@
32
32
 
33
33
  /* States */
34
34
  const UNMOUNTED = "unmounted";
35
- const EXITED = "exited";
36
- const ENTERING = "entering";
37
- const ENTERED = "entered";
38
- const EXITING = "exiting";
35
+ const EXITED = "exited";
36
+ const ENTERING = "entering";
37
+ const ENTERED = "entered";
38
+ const EXITING = "exiting";
39
39
 
40
40
  import {setTimeout, clearTimeout} from "./timers.jsx"
41
41
  import {Children} from "./misc.jsx"
@@ -81,12 +81,16 @@ export function Transition({
81
81
  this[CORE].is_appeared ??= inProp;
82
82
  this[CORE].timer ??= null;
83
83
 
84
- if(!this.state) {
85
- if (!this[CORE].is_mounted) this.state = UNMOUNTED;
84
+ this[CORE].childFunctionHandler = (func)=>{
85
+ return func(this[CORE].mount_state);
86
+ }
87
+
88
+ if(!this[CORE].mount_state) {
89
+ if (!this[CORE].is_mounted) this[CORE].mount_state = UNMOUNTED;
86
90
  if (inProp) {
87
- this.state = appear && !this[CORE].is_appeared ? ENTERING : ENTERED;
91
+ this[CORE].mount_state = appear && !this[CORE].is_appeared ? ENTERING : ENTERED;
88
92
  }
89
- this.state = EXITED;
93
+ else this[CORE].mount_state = EXITED;
90
94
  }
91
95
 
92
96
  useEffect(() => {
@@ -94,13 +98,14 @@ export function Transition({
94
98
  }, []);
95
99
 
96
100
  useEffect(() => {
97
- if (!this[CORE].is_appeared && appear && this.state === ENTERING && inProp) {
101
+ if (!this[CORE].is_appeared && appear && this[CORE].mount_state === ENTERING && inProp) {
98
102
  onEnter?.();
99
103
  requestAnimationFrame(() => {
100
104
  onEntering?.(!this[CORE].is_appeared);
101
105
  clearTimeout(this[CORE].timer);
102
106
  this[CORE].timer = setTimeout(() => {
103
- this.setState(ENTERED);
107
+ this[CORE].mount_state = ENTERED;
108
+ this.forceUpdate();
104
109
  this[CORE].is_appeared = true;
105
110
  onEntered?.(!this[CORE].is_appeared);
106
111
  }, timeout);
@@ -112,32 +117,38 @@ export function Transition({
112
117
  if (inProp) {
113
118
  this[CORE].is_mounted = true;
114
119
  // If we are already entering/entered, no-op
115
- if (this.state === ENTERING || this.state === ENTERED) return;
120
+ if (this[CORE].mount_state === ENTERING || this[CORE].mount_state === ENTERED) return;
116
121
 
117
122
  onEnter?.(!this[CORE].is_appeared);
118
- this.setState(ENTERING, () => {
123
+ this[CORE].mount_state = ENTERING;
124
+ this.forceUpdate(() => {
119
125
  onEntering?.(!this[CORE].is_appeared);
120
126
  clearTimeout(this[CORE].timer);
127
+
121
128
  this[CORE].timer = setTimeout(() => {
122
- this.setState(ENTERED);
129
+ this[CORE].mount_state = ENTERED;
130
+ this.forceUpdate();
123
131
  this[CORE].is_appeared = true;
124
132
  onEntered?.();
125
133
  }, timeout);
126
134
  });
127
135
  }
128
136
  else {
129
- if (this.state === UNMOUNTED || this.state === EXITING || this.state === EXITED) return;
137
+ if (this[CORE].mount_state === UNMOUNTED || this[CORE].mount_state === EXITING || this[CORE].mount_state === EXITED) return;
130
138
 
131
139
  onExit?.();
132
- this.setState(EXITING, () => {
140
+ this[CORE].mount_state = EXITING;
141
+ this.forceUpdate( () => {
133
142
  onExiting?.();
134
143
  clearTimeout(this[CORE].timer);
135
144
  this[CORE].timer = setTimeout(() => {
136
- this.setState(EXITED);
145
+ this[CORE].mount_state = EXITED;
146
+ this.forceUpdate();
137
147
  onExited?.();
138
148
  if (unmountOnExit) {
139
149
  this[CORE].is_mounted = false;
140
- this.setState(UNMOUNTED);
150
+ this[CORE].mount_state = UNMOUNTED;
151
+ this.forceUpdate();
141
152
  }
142
153
  }, timeout);
143
154
  });
@@ -147,20 +158,20 @@ export function Transition({
147
158
  if (!this[CORE].is_mounted) return null;
148
159
 
149
160
  if(classNames) {
150
- if (this.state === ENTERING) {
161
+ if (this[CORE].mount_state === ENTERING) {
151
162
  if(this[CORE].is_appeared)
152
163
  this[CORE][CHILD_CLASS_ADDENDUM] = classNames.appearActive;
153
164
  else
154
165
  this[CORE][CHILD_CLASS_ADDENDUM] = classNames.enterActive;
155
166
  }
156
- else if (this.state === ENTERED) {
167
+ else if (this[CORE].mount_state === ENTERED) {
157
168
  if(this[CORE].is_appeared)
158
169
  this[CORE][CHILD_CLASS_ADDENDUM] = classNames.appearDone;
159
170
  else
160
171
  this[CORE][CHILD_CLASS_ADDENDUM] = classNames.enterDone;
161
172
  }
162
- else if (this.state === EXITING) this[CORE][CHILD_CLASS_ADDENDUM] = classNames.exitActive;
163
- else if (this.state === EXITED) this[CORE][CHILD_CLASS_ADDENDUM] = classNames.exitDone;
173
+ else if (this[CORE].mount_state === EXITING) this[CORE][CHILD_CLASS_ADDENDUM] = classNames.exitActive;
174
+ else if (this[CORE].mount_state === EXITED) this[CORE][CHILD_CLASS_ADDENDUM] = classNames.exitDone;
164
175
  }
165
176
  return children;
166
177
  }
package/webpack.config.js CHANGED
@@ -45,7 +45,8 @@ export default (env, argv) => {
45
45
  const __filename = fileURLToPath(import.meta.url);
46
46
  const __dirname = dirname(__filename);
47
47
 
48
- const filename = `lilact.${mode}${mode==="development"?"":".min"}.js`;
48
+ //const filename = `lilact.${mode}${mode==="development"?"":".min"}.js`;
49
+ const filename = `lilact.${mode}.min.js`;
49
50
 
50
51
  return {
51
52
  entry: './src/lilact.jsx',
@@ -54,17 +55,6 @@ export default (env, argv) => {
54
55
 
55
56
  optimization: {
56
57
  concatenateModules: true, // scope hoisting
57
- // runtimeChunk: 'single', // centralize runtime helpers
58
- // splitChunks: {
59
- // chunks: 'all',
60
- // cacheGroups: {
61
- // vendors: {
62
- // test: /[\\/]node_modules[\\/]/,
63
- // name: 'vendors',
64
- // chunks: 'all',
65
- // },
66
- // },
67
- // },
68
58
  moduleIds: 'deterministic', // smaller stable ids (or 'hashed')
69
59
  minimize: mode === 'production',
70
60
  },