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.
- package/README.md +13 -2
- package/dist/{lilact.development.js → lilact.development.min.js} +690 -315
- package/dist/lilact.development.min.js.LICENSE.txt +77 -0
- package/dist/lilact.development.min.js.map +1 -0
- package/dist/lilact.production.min.js +1 -1
- package/dist/lilact.production.min.js.map +1 -1
- package/docs/assets/navigation.js +1 -1
- package/docs/assets/search.js +1 -1
- package/docs/classes/accessories.ErrorBoundary.html +11 -10
- package/docs/classes/accessories.Suspense.html +12 -22
- package/docs/classes/components.Component.html +11 -10
- package/docs/classes/components.HTMLComponent.html +11 -10
- package/docs/classes/components.RootComponent.html +11 -10
- package/docs/functions/accessories.Spinner.html +1 -1
- package/docs/functions/components.createComponent.html +1 -1
- package/docs/functions/components.createRoot.html +1 -1
- package/docs/functions/components.render.html +1 -1
- package/docs/functions/hooks.createContext.html +1 -1
- package/docs/functions/hooks.forwardRef.html +4 -0
- package/docs/functions/hooks.useActionState.html +1 -1
- package/docs/functions/hooks.useImperativeHandle.html +7 -0
- package/docs/functions/hooks.useReducer.html +1 -1
- package/docs/functions/hooks.useState.html +2 -2
- package/docs/functions/hooks.useTransition.html +1 -1
- package/docs/functions/jsx.transpileJSX.html +1 -1
- package/docs/functions/run.lazy.html +7 -0
- package/docs/functions/run.require.html +11 -11
- package/docs/functions/run.run.html +3 -5
- package/docs/functions/run.runScripts.html +1 -1
- package/docs/functions/run.traceError.html +1 -1
- package/docs/functions/transition.CSSTransition.html +1 -1
- package/docs/functions/transition.Transition.html +15 -0
- package/docs/functions/transition.TransitionGroup.html +1 -1
- package/docs/index.html +8 -2
- package/docs/modules/hooks.html +1 -1
- package/docs/modules/jsx.html +1 -1
- package/docs/modules/run.html +1 -1
- package/docs/modules/transition.html +1 -1
- package/docs/static/demos/actionstate.jsx +5 -7
- package/docs/static/demos/context.jsx +2 -4
- package/docs/static/demos/css-transition.jsx +1 -1
- package/docs/static/demos/lazy.jsx +14 -0
- package/docs/static/demos/modal.jsx +14 -16
- package/docs/static/demos/proptypes.jsx +6 -9
- package/docs/static/demos/reducer.jsx +22 -24
- package/docs/static/demos/redux.jsx +1 -1
- package/docs/static/demos/router.jsx +1 -8
- package/docs/static/demos/stopwatch.jsx +1 -1
- package/docs/static/demos/suspense.jsx +5 -7
- package/docs/static/demos/transition.jsx +25 -25
- package/{root/demos/use-differed.jsx → docs/static/demos/use-deffered.jsx} +1 -1
- package/docs/static/demos/usetransition.jsx +2 -4
- package/docs/static/index 2.html +95 -0
- package/docs/static/index.html +27 -29
- package/docs/static/{lilact.development.js → lilact.development.min.js} +690 -315
- package/docs/static/lilact.production.min.js +1 -1
- package/package.json +2 -2
- package/root/demos/actionstate.jsx +5 -7
- package/root/demos/context.jsx +2 -4
- package/root/demos/css-transition.jsx +1 -1
- package/root/demos/lazy.jsx +14 -0
- package/root/demos/modal.jsx +14 -16
- package/root/demos/proptypes.jsx +6 -9
- package/root/demos/reducer.jsx +22 -24
- package/root/demos/redux.jsx +1 -1
- package/root/demos/router.jsx +1 -8
- package/root/demos/stopwatch.jsx +1 -1
- package/root/demos/suspense.jsx +5 -7
- package/root/demos/transition.jsx +25 -25
- package/{docs/static/demos/use-differed.jsx → root/demos/use-deffered.jsx} +1 -1
- package/root/demos/usetransition.jsx +2 -4
- package/root/index 2.html +95 -0
- package/root/index.html +27 -29
- package/root/{lilact.development.js → lilact.development.min.js} +690 -315
- package/root/lilact.production.min.js +1 -1
- package/src/accessories.jsx +12 -11
- package/src/components.jsx +11 -5
- package/src/hooks.jsx +47 -6
- package/src/jsx.js +1 -0
- package/src/lilact.jsx +5 -7
- package/src/pane.jsx +287 -0
- package/src/run.jsx +91 -43
- package/src/transition.jsx +32 -21
- package/webpack.config.js +2 -12
- package/dist/lilact.development.js.map +0 -1
- package/docs/classes/transition.Transition.html +0 -31
- 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
|
-
|
|
13
|
+
notice, this list of conditions and the following disclaimer.
|
|
14
14
|
* Redistributions in binary form must reproduce the above copyright
|
|
15
|
-
|
|
16
|
-
|
|
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
|
|
40
|
-
*
|
|
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
|
|
124
|
-
*
|
|
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.
|
|
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
|
|
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
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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.
|
package/src/transition.jsx
CHANGED
|
@@ -32,10 +32,10 @@
|
|
|
32
32
|
|
|
33
33
|
/* States */
|
|
34
34
|
const UNMOUNTED = "unmounted";
|
|
35
|
-
const EXITED =
|
|
36
|
-
const ENTERING =
|
|
37
|
-
const ENTERED =
|
|
38
|
-
const 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
|
-
|
|
85
|
-
|
|
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.
|
|
91
|
+
this[CORE].mount_state = appear && !this[CORE].is_appeared ? ENTERING : ENTERED;
|
|
88
92
|
}
|
|
89
|
-
this.
|
|
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.
|
|
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.
|
|
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.
|
|
120
|
+
if (this[CORE].mount_state === ENTERING || this[CORE].mount_state === ENTERED) return;
|
|
116
121
|
|
|
117
122
|
onEnter?.(!this[CORE].is_appeared);
|
|
118
|
-
this.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
163
|
-
else if (this.
|
|
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
|
},
|