elementdrawing 1.0.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/LICENSE +21 -0
- package/dist/elementdrawing.min.js +3 -0
- package/dist/elementdrawing.min.js.LICENSE.txt +8 -0
- package/dist/elementdrawing.min.js.map +1 -0
- package/dist/index.html +1 -0
- package/package.json +127 -0
- package/src/core/bridge.h +855 -0
- package/src/core/diff.c +900 -0
- package/src/core/element.c +1078 -0
- package/src/core/event.c +813 -0
- package/src/core/fiber.c +1027 -0
- package/src/core/hooks.c +919 -0
- package/src/core/renderer.c +963 -0
- package/src/core/scheduler.c +702 -0
- package/src/core/state.c +803 -0
- package/src/css/animations.css +779 -0
- package/src/css/base.css +615 -0
- package/src/css/components.css +1311 -0
- package/src/css/tailwind.css +370 -0
- package/src/css/themes.css +517 -0
- package/src/css/utilities.css +475 -0
- package/src/index.js +746 -0
- package/src/js/animation.js +655 -0
- package/src/js/dom.js +665 -0
- package/src/js/events.js +585 -0
- package/src/js/http.js +446 -0
- package/src/js/index.js +26 -0
- package/src/js/router.js +483 -0
- package/src/js/store.js +539 -0
- package/src/js/utils.js +593 -0
- package/src/js/validator.js +529 -0
- package/src/jsx/components/Accordion.jsx +210 -0
- package/src/jsx/components/Alert.jsx +169 -0
- package/src/jsx/components/Avatar.jsx +214 -0
- package/src/jsx/components/Badge.jsx +136 -0
- package/src/jsx/components/Breadcrumb.jsx +200 -0
- package/src/jsx/components/Button.jsx +188 -0
- package/src/jsx/components/Card.jsx +192 -0
- package/src/jsx/components/Carousel.jsx +278 -0
- package/src/jsx/components/Checkbox.jsx +215 -0
- package/src/jsx/components/Dialog.jsx +242 -0
- package/src/jsx/components/Drawer.jsx +190 -0
- package/src/jsx/components/Dropdown.jsx +268 -0
- package/src/jsx/components/Form.jsx +274 -0
- package/src/jsx/components/Input.jsx +285 -0
- package/src/jsx/components/Menu.jsx +276 -0
- package/src/jsx/components/Modal.jsx +274 -0
- package/src/jsx/components/Navbar.jsx +292 -0
- package/src/jsx/components/Pagination.jsx +268 -0
- package/src/jsx/components/Progress.jsx +252 -0
- package/src/jsx/components/Radio.jsx +208 -0
- package/src/jsx/components/Select.jsx +397 -0
- package/src/jsx/components/Sidebar.jsx +250 -0
- package/src/jsx/components/Slider.jsx +310 -0
- package/src/jsx/components/Spinner.jsx +198 -0
- package/src/jsx/components/Switch.jsx +201 -0
- package/src/jsx/components/Table.jsx +332 -0
- package/src/jsx/components/Tabs.jsx +227 -0
- package/src/jsx/components/Textarea.jsx +212 -0
- package/src/jsx/components/Toast.jsx +270 -0
- package/src/jsx/components/Tooltip.jsx +178 -0
- package/src/jsx/components/Typography.jsx +299 -0
- package/src/jsx/components/index.jsx +70 -0
- package/src/jsx/core/element.js +3 -0
- package/src/jsx/hooks/index.js +356 -0
- package/src/jsx/hooks/useCallback.js +472 -0
- package/src/jsx/hooks/useContext.js +586 -0
- package/src/jsx/hooks/useEffect.js +704 -0
- package/src/jsx/hooks/useLayoutEffect.js +508 -0
- package/src/jsx/hooks/useMemo.js +689 -0
- package/src/jsx/hooks/useReducer.js +729 -0
- package/src/jsx/hooks/useRef.js +542 -0
- package/src/jsx/hooks/useState.js +854 -0
- package/src/jsx/runtime/commit.js +903 -0
- package/src/jsx/runtime/createElement.js +860 -0
- package/src/jsx/runtime/index.js +356 -0
- package/src/jsx/runtime/reconcile.js +687 -0
- package/src/jsx/runtime/render.js +914 -0
|
@@ -0,0 +1,860 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* createElement - Core Virtual DOM Element Creation
|
|
3
|
+
* ElementDrawing Framework - Creates virtual DOM nodes with comprehensive
|
|
4
|
+
* props processing, children handling, fragments, keys, refs, Suspense,
|
|
5
|
+
* StrictMode, error boundaries, lazy loading, and profiling.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
'use strict';
|
|
9
|
+
|
|
10
|
+
// ─── Constants ────────────────────────────────────────────────────────────────
|
|
11
|
+
|
|
12
|
+
const REACT_ELEMENT_TYPE = Symbol.for('elementdrawing.element');
|
|
13
|
+
const REACT_FRAGMENT_TYPE = Symbol.for('elementdrawing.fragment');
|
|
14
|
+
const REACT_PORTAL_TYPE = Symbol.for('elementdrawing.portal');
|
|
15
|
+
const REACT_SUSPENSE_TYPE = Symbol.for('elementdrawing.suspense');
|
|
16
|
+
const REACT_LAZY_TYPE = Symbol.for('elementdrawing.lazy');
|
|
17
|
+
const REACT_STRICT_MODE_TYPE = Symbol.for('elementdrawing.strict_mode');
|
|
18
|
+
const REACT_ERROR_BOUNDARY_TYPE = Symbol.for('elementdrawing.error_boundary');
|
|
19
|
+
const REACT_PROVIDER_TYPE = Symbol.for('elementdrawing.provider');
|
|
20
|
+
const REACT_CONSUMER_TYPE = Symbol.for('elementdrawing.consumer');
|
|
21
|
+
|
|
22
|
+
const RESERVED_PROPS = {
|
|
23
|
+
key: true,
|
|
24
|
+
ref: true,
|
|
25
|
+
__self: true,
|
|
26
|
+
__source: true,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// ─── Element Type Detection ───────────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Check if a value is a valid element type.
|
|
33
|
+
* @param {*} type
|
|
34
|
+
* @returns {boolean}
|
|
35
|
+
*/
|
|
36
|
+
function isValidElementType(type) {
|
|
37
|
+
if (typeof type === 'string') return true;
|
|
38
|
+
if (typeof type === 'function') return true;
|
|
39
|
+
if (typeof type === 'symbol') return true;
|
|
40
|
+
if (type === REACT_FRAGMENT_TYPE) return true;
|
|
41
|
+
if (type === REACT_PORTAL_TYPE) return true;
|
|
42
|
+
if (type === REACT_SUSPENSE_TYPE) return true;
|
|
43
|
+
if (type === REACT_STRICT_MODE_TYPE) return true;
|
|
44
|
+
if (type === REACT_ERROR_BOUNDARY_TYPE) return true;
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Check if a value is a virtual DOM element.
|
|
50
|
+
* @param {*} element
|
|
51
|
+
* @returns {boolean}
|
|
52
|
+
*/
|
|
53
|
+
function isValidElement(element) {
|
|
54
|
+
return (
|
|
55
|
+
element !== null &&
|
|
56
|
+
typeof element === 'object' &&
|
|
57
|
+
element.$$typeof === REACT_ELEMENT_TYPE
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Check if an element type is a function component.
|
|
63
|
+
* @param {*} type
|
|
64
|
+
* @returns {boolean}
|
|
65
|
+
*/
|
|
66
|
+
function isFunctionComponent(type) {
|
|
67
|
+
return typeof type === 'function' && !type._isClassComponent;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Check if an element type is a class component.
|
|
72
|
+
* @param {*} type
|
|
73
|
+
* @returns {boolean}
|
|
74
|
+
*/
|
|
75
|
+
function isClassComponent(type) {
|
|
76
|
+
return typeof type === 'function' && type._isClassComponent === true;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Check if an element type is a host (HTML) element.
|
|
81
|
+
* @param {*} type
|
|
82
|
+
* @returns {boolean}
|
|
83
|
+
*/
|
|
84
|
+
function isHostElement(type) {
|
|
85
|
+
return typeof type === 'string';
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Check if an element is a Suspense boundary.
|
|
90
|
+
* @param {Object} element
|
|
91
|
+
* @returns {boolean}
|
|
92
|
+
*/
|
|
93
|
+
function isSuspenseElement(element) {
|
|
94
|
+
return isValidElement(element) && element.type === REACT_SUSPENSE_TYPE;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Check if an element is a StrictMode wrapper.
|
|
99
|
+
* @param {Object} element
|
|
100
|
+
* @returns {boolean}
|
|
101
|
+
*/
|
|
102
|
+
function isStrictModeElement(element) {
|
|
103
|
+
return isValidElement(element) && element.type === REACT_STRICT_MODE_TYPE;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Check if an element is an error boundary.
|
|
108
|
+
* @param {Object} element
|
|
109
|
+
* @returns {boolean}
|
|
110
|
+
*/
|
|
111
|
+
function isErrorBoundaryElement(element) {
|
|
112
|
+
return isValidElement(element) && element.type === REACT_ERROR_BOUNDARY_TYPE;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ─── Key Extraction & Validation ──────────────────────────────────────────────
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Extract and validate the key from props.
|
|
119
|
+
* @param {*} key - The key value
|
|
120
|
+
* @param {*} parentKey - Parent key for nested elements
|
|
121
|
+
* @returns {string|null} Validated key string
|
|
122
|
+
*/
|
|
123
|
+
function extractKey(key, parentKey) {
|
|
124
|
+
if (key === undefined || key === null) return null;
|
|
125
|
+
|
|
126
|
+
const keyString = String(key);
|
|
127
|
+
|
|
128
|
+
if (keyString === '') {
|
|
129
|
+
console.warn('[createElement] Empty string key detected. This may cause reconciliation issues.');
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Validate key format
|
|
134
|
+
if (/^\./.test(keyString) || /\$/.test(keyString) || /\.\./.test(keyString)) {
|
|
135
|
+
console.warn(
|
|
136
|
+
'[createElement] Key contains invalid characters (., $, ..): ' +
|
|
137
|
+
`"${keyString}". Keys should be simple strings or numbers.`
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return parentKey ? parentKey + '/' + keyString : keyString;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Validate that keys are unique among siblings.
|
|
146
|
+
* @param {Array} children - Array of child elements
|
|
147
|
+
*/
|
|
148
|
+
function validateChildKeys(children) {
|
|
149
|
+
if (!Array.isArray(children)) return;
|
|
150
|
+
|
|
151
|
+
const seenKeys = new Set();
|
|
152
|
+
let hasIndexKey = false;
|
|
153
|
+
|
|
154
|
+
for (let i = 0; i < children.length; i++) {
|
|
155
|
+
const child = children[i];
|
|
156
|
+
if (!isValidElement(child)) continue;
|
|
157
|
+
|
|
158
|
+
const key = child.key;
|
|
159
|
+
if (key === null) {
|
|
160
|
+
hasIndexKey = true;
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (seenKeys.has(key)) {
|
|
165
|
+
console.warn(
|
|
166
|
+
'[createElement] Duplicate key "' + key + '" found in children. ' +
|
|
167
|
+
'This may cause components to be duplicated or lost during reconciliation.'
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
seenKeys.add(key);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (hasIndexKey && seenKeys.size > 0) {
|
|
174
|
+
console.warn(
|
|
175
|
+
'[createElement] Mixed explicit and implicit keys detected. ' +
|
|
176
|
+
'When using keys, all siblings should have explicit keys for optimal reconciliation.'
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// ─── Ref Forwarding ───────────────────────────────────────────────────────────
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Process the ref prop and handle ref forwarding.
|
|
185
|
+
* @param {*} ref - Ref value
|
|
186
|
+
* @param {Object} element - The element being created
|
|
187
|
+
* @returns {*} Processed ref
|
|
188
|
+
*/
|
|
189
|
+
function processRef(ref, element) {
|
|
190
|
+
if (ref === undefined || ref === null) return null;
|
|
191
|
+
|
|
192
|
+
// Object ref (useRef)
|
|
193
|
+
if (typeof ref === 'object' && ref._isRef) {
|
|
194
|
+
return ref;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Callback ref
|
|
198
|
+
if (typeof ref === 'function') {
|
|
199
|
+
return ref;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// String ref (legacy)
|
|
203
|
+
if (typeof ref === 'string') {
|
|
204
|
+
console.warn(
|
|
205
|
+
'[createElement] String refs are deprecated. Use callback refs or useRef instead. ' +
|
|
206
|
+
`Received ref: "${ref}"`
|
|
207
|
+
);
|
|
208
|
+
return ref;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Forwarded ref
|
|
212
|
+
if (ref._isForwardRef) {
|
|
213
|
+
return ref;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
console.warn('[createElement] Invalid ref type: ' + typeof ref);
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// ─── Props Processing ─────────────────────────────────────────────────────────
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Process and clean props, removing reserved props and applying defaults.
|
|
224
|
+
* @param {*} type - Element type
|
|
225
|
+
* @param {Object} props - Raw props
|
|
226
|
+
* @param {*} key - Element key
|
|
227
|
+
* @param {*} ref - Element ref
|
|
228
|
+
* @returns {Object} Cleaned props
|
|
229
|
+
*/
|
|
230
|
+
function processProps(type, props, key, ref) {
|
|
231
|
+
const cleanedProps = {};
|
|
232
|
+
|
|
233
|
+
for (const propName in props) {
|
|
234
|
+
if (props.hasOwnProperty(propName)) {
|
|
235
|
+
if (RESERVED_PROPS[propName]) continue;
|
|
236
|
+
|
|
237
|
+
const propValue = props[propName];
|
|
238
|
+
|
|
239
|
+
// Process event handlers
|
|
240
|
+
if (propName.startsWith('on') && typeof propValue === 'function') {
|
|
241
|
+
cleanedProps[propName] = propValue;
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Process style prop
|
|
246
|
+
if (propName === 'style') {
|
|
247
|
+
cleanedProps[propName] = processStyleProp(propValue);
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Process className / class
|
|
252
|
+
if (propName === 'className' || propName === 'class') {
|
|
253
|
+
cleanedProps.className = processClassName(propValue);
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Process dangerouslySetInnerHTML
|
|
258
|
+
if (propName === 'dangerouslySetInnerHTML') {
|
|
259
|
+
if (propValue && typeof propValue.__html === 'string') {
|
|
260
|
+
cleanedProps[propName] = propValue;
|
|
261
|
+
} else {
|
|
262
|
+
console.warn('[createElement] dangerouslySetInnerHTML must have a __html property');
|
|
263
|
+
}
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Process suppressHydrationWarning
|
|
268
|
+
if (propName === 'suppressHydrationWarning') {
|
|
269
|
+
cleanedProps[propName] = !!propValue;
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
cleanedProps[propName] = propValue;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Apply default props
|
|
278
|
+
if (type && type.defaultProps) {
|
|
279
|
+
for (const defaultProp in type.defaultProps) {
|
|
280
|
+
if (type.defaultProps.hasOwnProperty(defaultProp) && cleanedProps[defaultProp] === undefined) {
|
|
281
|
+
cleanedProps[defaultProp] = type.defaultProps[defaultProp];
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return cleanedProps;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Process the style prop into a consistent format.
|
|
291
|
+
* @param {*} style
|
|
292
|
+
* @returns {Object|string|undefined}
|
|
293
|
+
*/
|
|
294
|
+
function processStyleProp(style) {
|
|
295
|
+
if (style === null || style === undefined) return undefined;
|
|
296
|
+
|
|
297
|
+
if (typeof style === 'string') return style;
|
|
298
|
+
|
|
299
|
+
if (typeof style === 'object') {
|
|
300
|
+
const processed = {};
|
|
301
|
+
for (const prop in style) {
|
|
302
|
+
if (style.hasOwnProperty(prop)) {
|
|
303
|
+
const value = style[prop];
|
|
304
|
+
const cssProp = prop.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
305
|
+
|
|
306
|
+
if (typeof value === 'number' && !isUnitlessProperty(cssProp)) {
|
|
307
|
+
processed[cssProp] = value + 'px';
|
|
308
|
+
} else {
|
|
309
|
+
processed[cssProp] = value;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return processed;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return undefined;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Check if a CSS property is unitless.
|
|
321
|
+
* @param {string} prop
|
|
322
|
+
* @returns {boolean}
|
|
323
|
+
*/
|
|
324
|
+
function isUnitlessProperty(prop) {
|
|
325
|
+
const unitlessProps = new Set([
|
|
326
|
+
'animation-iteration-count', 'border-image-outset', 'border-image-slice',
|
|
327
|
+
'border-image-width', 'box-flex', 'box-flex-group', 'box-ordinal-group',
|
|
328
|
+
'column-count', 'columns', 'flex', 'flex-grow', 'flex-positive',
|
|
329
|
+
'flex-shrink', 'flex-negative', 'flex-order', 'grid-row', 'grid-row-end',
|
|
330
|
+
'grid-row-span', 'grid-row-start', 'grid-column', 'grid-column-end',
|
|
331
|
+
'grid-column-span', 'grid-column-start', 'font-weight', 'line-clamp',
|
|
332
|
+
'line-height', 'opacity', 'order', 'orphans', 'tab-size', 'widows',
|
|
333
|
+
'z-index', 'zoom', 'fill-opacity', 'flood-opacity', 'stop-opacity',
|
|
334
|
+
'stroke-dasharray', 'stroke-dashoffset', 'stroke-miterlimit',
|
|
335
|
+
'stroke-opacity', 'stroke-width',
|
|
336
|
+
]);
|
|
337
|
+
return unitlessProps.has(prop);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Process className prop.
|
|
342
|
+
* @param {*} className
|
|
343
|
+
* @returns {string}
|
|
344
|
+
*/
|
|
345
|
+
function processClassName(className) {
|
|
346
|
+
if (className === null || className === undefined) return '';
|
|
347
|
+
|
|
348
|
+
if (typeof className === 'string') return className;
|
|
349
|
+
|
|
350
|
+
if (Array.isArray(className)) {
|
|
351
|
+
return className.filter(Boolean).join(' ');
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (typeof className === 'object') {
|
|
355
|
+
return Object.keys(className)
|
|
356
|
+
.filter((key) => className[key])
|
|
357
|
+
.join(' ');
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
return String(className);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// ─── Children Processing ──────────────────────────────────────────────────────
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Flatten and normalize children, handling arrays, fragments, and nested structures.
|
|
367
|
+
* @param {Array} children - Raw children arguments
|
|
368
|
+
* @returns {Array} Normalized flat array of children
|
|
369
|
+
*/
|
|
370
|
+
function flattenChildren(children) {
|
|
371
|
+
const result = [];
|
|
372
|
+
|
|
373
|
+
for (let i = 0; i < children.length; i++) {
|
|
374
|
+
const child = children[i];
|
|
375
|
+
|
|
376
|
+
if (child === null || child === undefined || typeof child === 'boolean') {
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (Array.isArray(child)) {
|
|
381
|
+
const flat = flattenChildren(child);
|
|
382
|
+
for (let j = 0; j < flat.length; j++) {
|
|
383
|
+
result.push(flat[j]);
|
|
384
|
+
}
|
|
385
|
+
} else if (isValidElement(child) && child.type === REACT_FRAGMENT_TYPE) {
|
|
386
|
+
if (child.props && child.props.children) {
|
|
387
|
+
const fragmentChildren = Array.isArray(child.props.children)
|
|
388
|
+
? child.props.children
|
|
389
|
+
: [child.props.children];
|
|
390
|
+
const flat = flattenChildren(fragmentChildren);
|
|
391
|
+
for (let j = 0; j < flat.length; j++) {
|
|
392
|
+
result.push(flat[j]);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
} else {
|
|
396
|
+
result.push(child);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
return result;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Normalize children into a consistent format.
|
|
405
|
+
* @param {Array} children
|
|
406
|
+
* @param {string|null} parentKey
|
|
407
|
+
* @returns {Array}
|
|
408
|
+
*/
|
|
409
|
+
function normalizeChildren(children, parentKey) {
|
|
410
|
+
if (children.length === 0) return [];
|
|
411
|
+
|
|
412
|
+
const normalized = [];
|
|
413
|
+
|
|
414
|
+
for (let i = 0; i < children.length; i++) {
|
|
415
|
+
let child = children[i];
|
|
416
|
+
|
|
417
|
+
if (typeof child === 'string' || typeof child === 'number') {
|
|
418
|
+
child = createTextElement(String(child));
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if (isValidElement(child)) {
|
|
422
|
+
if (child.key === null) {
|
|
423
|
+
child = assignKey(child, parentKey, i);
|
|
424
|
+
}
|
|
425
|
+
normalized.push(child);
|
|
426
|
+
} else if (child !== null && child !== undefined) {
|
|
427
|
+
console.warn('[createElement] Invalid child type: ' + typeof child);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
return normalized;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Create a text element for primitive children.
|
|
436
|
+
* @param {string} text
|
|
437
|
+
* @returns {Object}
|
|
438
|
+
*/
|
|
439
|
+
function createTextElement(text) {
|
|
440
|
+
return {
|
|
441
|
+
$$typeof: REACT_ELEMENT_TYPE,
|
|
442
|
+
type: '#text',
|
|
443
|
+
props: { nodeValue: text, children: [] },
|
|
444
|
+
key: null,
|
|
445
|
+
ref: null,
|
|
446
|
+
_owner: null,
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Assign an implicit key based on the parent key and child index.
|
|
452
|
+
*/
|
|
453
|
+
function assignKey(element, parentKey, index) {
|
|
454
|
+
const key = parentKey ? parentKey + '.' + index : '.' + index;
|
|
455
|
+
return Object.assign({}, element, { key });
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// ─── Props Cloning & Merging ──────────────────────────────────────────────────
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Clone an element with new props merged.
|
|
462
|
+
* @param {Object} element
|
|
463
|
+
* @param {Object} newProps
|
|
464
|
+
* @param {...*} newChildren
|
|
465
|
+
* @returns {Object}
|
|
466
|
+
*/
|
|
467
|
+
function cloneElement(element, newProps) {
|
|
468
|
+
if (!isValidElement(element)) {
|
|
469
|
+
throw new Error('[cloneElement] Argument must be a valid element');
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
const children = Array.prototype.slice.call(arguments, 2);
|
|
473
|
+
const mergedProps = Object.assign({}, element.props, newProps);
|
|
474
|
+
|
|
475
|
+
if (children.length > 0) {
|
|
476
|
+
mergedProps.children = children.length === 1 ? children[0] : children;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
const key = newProps && newProps.key !== undefined ? newProps.key : element.key;
|
|
480
|
+
const ref = newProps && newProps.ref !== undefined ? newProps.ref : element.ref;
|
|
481
|
+
|
|
482
|
+
if (newProps && newProps.key !== undefined) delete mergedProps.key;
|
|
483
|
+
if (newProps && newProps.ref !== undefined) delete mergedProps.ref;
|
|
484
|
+
|
|
485
|
+
return createElement(element.type, Object.assign({ key, ref }, mergedProps));
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Merge props from multiple sources.
|
|
490
|
+
* Event handlers are chained rather than overwritten.
|
|
491
|
+
* @returns {Object}
|
|
492
|
+
*/
|
|
493
|
+
function mergeProps() {
|
|
494
|
+
const result = {};
|
|
495
|
+
|
|
496
|
+
for (let i = 0; i < arguments.length; i++) {
|
|
497
|
+
const props = arguments[i];
|
|
498
|
+
if (!props) continue;
|
|
499
|
+
|
|
500
|
+
for (const key in props) {
|
|
501
|
+
if (!props.hasOwnProperty(key)) continue;
|
|
502
|
+
|
|
503
|
+
if (key.startsWith('on') && result[key] && typeof result[key] === 'function') {
|
|
504
|
+
const existingHandler = result[key];
|
|
505
|
+
const newHandler = props[key];
|
|
506
|
+
result[key] = function chainedHandler() {
|
|
507
|
+
existingHandler.apply(this, arguments);
|
|
508
|
+
newHandler.apply(this, arguments);
|
|
509
|
+
};
|
|
510
|
+
} else if (key === 'style' && result.style && typeof result.style === 'object' && typeof props.style === 'object') {
|
|
511
|
+
result.style = Object.assign({}, result.style, props.style);
|
|
512
|
+
} else if (key === 'className' && result.className) {
|
|
513
|
+
result.className = result.className + ' ' + props.className;
|
|
514
|
+
} else {
|
|
515
|
+
result[key] = props[key];
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
return result;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// ─── Core createElement ───────────────────────────────────────────────────────
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* createElement - Create a virtual DOM element.
|
|
527
|
+
*
|
|
528
|
+
* @param {*} type - Element type
|
|
529
|
+
* @param {Object|null} config - Element configuration
|
|
530
|
+
* @param {...*} children - Child elements
|
|
531
|
+
* @returns {Object} Virtual DOM element
|
|
532
|
+
*/
|
|
533
|
+
function createElement(type, config) {
|
|
534
|
+
const children = Array.prototype.slice.call(arguments, 2);
|
|
535
|
+
|
|
536
|
+
// Validate type
|
|
537
|
+
if (!isValidElementType(type)) {
|
|
538
|
+
throw new Error(
|
|
539
|
+
'[createElement] Invalid element type: ' + typeof type +
|
|
540
|
+
'. Expected a string, function, or symbol.'
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// Extract Key and Ref from Config
|
|
545
|
+
let key = null;
|
|
546
|
+
let ref = null;
|
|
547
|
+
let props = {};
|
|
548
|
+
|
|
549
|
+
if (config !== null && config !== undefined) {
|
|
550
|
+
if (typeof config !== 'object') {
|
|
551
|
+
throw new Error(
|
|
552
|
+
'[createElement] Config must be an object or null. ' +
|
|
553
|
+
'Received: ' + typeof config
|
|
554
|
+
);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
if (config.key !== undefined) {
|
|
558
|
+
key = extractKey(config.key, null);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
if (config.ref !== undefined) {
|
|
562
|
+
ref = processRef(config.ref, null);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
for (const propName in config) {
|
|
566
|
+
if (config.hasOwnProperty(propName) && !RESERVED_PROPS[propName]) {
|
|
567
|
+
props[propName] = config[propName];
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// Process Children
|
|
573
|
+
const flattenedChildren = flattenChildren(children);
|
|
574
|
+
const normalizedChildren = normalizeChildren(flattenedChildren, key);
|
|
575
|
+
|
|
576
|
+
if (normalizedChildren.length === 0) {
|
|
577
|
+
props.children = [];
|
|
578
|
+
} else if (normalizedChildren.length === 1) {
|
|
579
|
+
props.children = normalizedChildren[0];
|
|
580
|
+
} else {
|
|
581
|
+
props.children = normalizedChildren;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// Process Props
|
|
585
|
+
props = processProps(type, props, key, ref);
|
|
586
|
+
|
|
587
|
+
// Prop Type Validation
|
|
588
|
+
if (type && type.propTypes && typeof type.propTypes === 'object') {
|
|
589
|
+
for (const propName in type.propTypes) {
|
|
590
|
+
if (type.propTypes.hasOwnProperty(propName)) {
|
|
591
|
+
const validator = type.propTypes[propName];
|
|
592
|
+
if (typeof validator === 'function') {
|
|
593
|
+
const result = validator(props, propName, type.displayName || type.name || 'Component', 'prop');
|
|
594
|
+
if (result instanceof Error) {
|
|
595
|
+
console.warn('[createElement] Prop validation: ' + result.message);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// Validate Child Keys
|
|
603
|
+
if (Array.isArray(normalizedChildren) && normalizedChildren.length > 1) {
|
|
604
|
+
validateChildKeys(normalizedChildren);
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// Create Element Object
|
|
608
|
+
const element = {
|
|
609
|
+
$$typeof: REACT_ELEMENT_TYPE,
|
|
610
|
+
type,
|
|
611
|
+
key,
|
|
612
|
+
ref,
|
|
613
|
+
props,
|
|
614
|
+
_owner: null,
|
|
615
|
+
_store: {},
|
|
616
|
+
};
|
|
617
|
+
|
|
618
|
+
// Make element immutable in development
|
|
619
|
+
if (typeof process !== 'undefined' && process.env && process.env.NODE_ENV !== 'production') {
|
|
620
|
+
Object.freeze(element.props);
|
|
621
|
+
Object.freeze(element);
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
return element;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
// ─── Fragment ─────────────────────────────────────────────────────────────────
|
|
628
|
+
|
|
629
|
+
const Fragment = REACT_FRAGMENT_TYPE;
|
|
630
|
+
|
|
631
|
+
// ─── Suspense ─────────────────────────────────────────────────────────────────
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Suspense component for handling async rendering.
|
|
635
|
+
* @param {Object} props
|
|
636
|
+
* @param {*} props.fallback - Fallback UI while suspended
|
|
637
|
+
* @param {*} props.children - Children to suspend
|
|
638
|
+
*/
|
|
639
|
+
const Suspense = REACT_SUSPENSE_TYPE;
|
|
640
|
+
|
|
641
|
+
/**
|
|
642
|
+
* Create a Suspense element.
|
|
643
|
+
* @param {Object} props
|
|
644
|
+
* @returns {Object} Suspense element
|
|
645
|
+
*/
|
|
646
|
+
function createSuspense(props) {
|
|
647
|
+
return createElement(Suspense, props, props.children);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
// ─── StrictMode ───────────────────────────────────────────────────────────────
|
|
651
|
+
|
|
652
|
+
const StrictMode = REACT_STRICT_MODE_TYPE;
|
|
653
|
+
|
|
654
|
+
/**
|
|
655
|
+
* Create a StrictMode wrapper element.
|
|
656
|
+
* @param {Object} props
|
|
657
|
+
* @returns {Object} StrictMode element
|
|
658
|
+
*/
|
|
659
|
+
function createStrictMode(props) {
|
|
660
|
+
return createElement(StrictMode, null, props.children);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// ─── Error Boundary ───────────────────────────────────────────────────────────
|
|
664
|
+
|
|
665
|
+
const ErrorBoundary = REACT_ERROR_BOUNDARY_TYPE;
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* Create an error boundary element.
|
|
669
|
+
* @param {Object} props
|
|
670
|
+
* @param {Function} props.onError - Error handler
|
|
671
|
+
* @param {*} props.fallback - Fallback UI
|
|
672
|
+
* @param {*} props.children - Children to protect
|
|
673
|
+
* @returns {Object} Error boundary element
|
|
674
|
+
*/
|
|
675
|
+
function createErrorBoundary(props) {
|
|
676
|
+
return createElement(ErrorBoundary, props, props.children);
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// ─── Portal ───────────────────────────────────────────────────────────────────
|
|
680
|
+
|
|
681
|
+
/**
|
|
682
|
+
* Create a portal element.
|
|
683
|
+
* @param {*} children
|
|
684
|
+
* @param {HTMLElement} container
|
|
685
|
+
* @param {string|null} key
|
|
686
|
+
* @returns {Object}
|
|
687
|
+
*/
|
|
688
|
+
function createPortal(children, container, key) {
|
|
689
|
+
return {
|
|
690
|
+
$$typeof: REACT_PORTAL_TYPE,
|
|
691
|
+
key: key === null ? null : String(key),
|
|
692
|
+
children,
|
|
693
|
+
containerInfo: container,
|
|
694
|
+
implementation: null,
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
// ─── Lazy Component ───────────────────────────────────────────────────────────
|
|
699
|
+
|
|
700
|
+
/**
|
|
701
|
+
* Create a lazy-loaded component.
|
|
702
|
+
* @param {Function} factory
|
|
703
|
+
* @returns {Object}
|
|
704
|
+
*/
|
|
705
|
+
function lazy(factory) {
|
|
706
|
+
const lazyComponent = {
|
|
707
|
+
$$typeof: REACT_LAZY_TYPE,
|
|
708
|
+
_payload: {
|
|
709
|
+
_status: -1, // -1: pending, 0: resolved, 1: rejected
|
|
710
|
+
_result: null,
|
|
711
|
+
},
|
|
712
|
+
_init: function init(payload) {
|
|
713
|
+
if (payload._status === -1) {
|
|
714
|
+
const thenable = factory();
|
|
715
|
+
thenable.then(
|
|
716
|
+
function resolve(module) {
|
|
717
|
+
if (payload._status === -1) {
|
|
718
|
+
payload._status = 0;
|
|
719
|
+
payload._result = module.default || module;
|
|
720
|
+
}
|
|
721
|
+
},
|
|
722
|
+
function reject(error) {
|
|
723
|
+
if (payload._status === -1) {
|
|
724
|
+
payload._status = 1;
|
|
725
|
+
payload._result = error;
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
);
|
|
729
|
+
payload._status = -1;
|
|
730
|
+
payload._result = thenable;
|
|
731
|
+
}
|
|
732
|
+
},
|
|
733
|
+
};
|
|
734
|
+
return lazyComponent;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
// ─── Children Utilities ───────────────────────────────────────────────────────
|
|
738
|
+
|
|
739
|
+
/**
|
|
740
|
+
* Count the number of children.
|
|
741
|
+
* @param {*} children
|
|
742
|
+
* @returns {number}
|
|
743
|
+
*/
|
|
744
|
+
function countChildren(children) {
|
|
745
|
+
if (children === null || children === undefined) return 0;
|
|
746
|
+
if (Array.isArray(children)) return children.length;
|
|
747
|
+
return 1;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
/**
|
|
751
|
+
* Map over children.
|
|
752
|
+
* @param {*} children
|
|
753
|
+
* @param {Function} fn
|
|
754
|
+
* @returns {Array}
|
|
755
|
+
*/
|
|
756
|
+
function mapChildren(children, fn) {
|
|
757
|
+
if (children === null || children === undefined) return [];
|
|
758
|
+
|
|
759
|
+
if (Array.isArray(children)) {
|
|
760
|
+
return children.map((child, index) => fn(child, index));
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
return [fn(children, 0)];
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
/**
|
|
767
|
+
* ForEach over children (no return value).
|
|
768
|
+
* @param {*} children
|
|
769
|
+
* @param {Function} fn
|
|
770
|
+
*/
|
|
771
|
+
function forEachChild(children, fn) {
|
|
772
|
+
if (children === null || children === undefined) return;
|
|
773
|
+
|
|
774
|
+
if (Array.isArray(children)) {
|
|
775
|
+
children.forEach((child, index) => fn(child, index));
|
|
776
|
+
} else {
|
|
777
|
+
fn(children, 0);
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
/**
|
|
782
|
+
* Check if children is an array with more than one element.
|
|
783
|
+
* @param {*} children
|
|
784
|
+
* @returns {boolean}
|
|
785
|
+
*/
|
|
786
|
+
function isChildrenArray(children) {
|
|
787
|
+
return Array.isArray(children) && children.length > 1;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
/**
|
|
791
|
+
* Get only child from children.
|
|
792
|
+
* @param {*} children
|
|
793
|
+
* @returns {*}
|
|
794
|
+
*/
|
|
795
|
+
function onlyChild(children) {
|
|
796
|
+
if (!isValidElement(children)) {
|
|
797
|
+
throw new Error('[onlyChild] Expected a single ElementDrawing element');
|
|
798
|
+
}
|
|
799
|
+
return children;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
// ─── Element Creation with Render Prop ────────────────────────────────────────
|
|
803
|
+
|
|
804
|
+
function createElementWithRenderProp(type, props, renderFn) {
|
|
805
|
+
if (typeof renderFn !== 'function') {
|
|
806
|
+
throw new Error('[createElementWithRenderProp] Third argument must be a function');
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
return createElement(type, Object.assign({}, props, { children: renderFn }));
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
// ─── Exports ──────────────────────────────────────────────────────────────────
|
|
813
|
+
|
|
814
|
+
module.exports = {
|
|
815
|
+
createElement,
|
|
816
|
+
cloneElement,
|
|
817
|
+
mergeProps,
|
|
818
|
+
createTextElement,
|
|
819
|
+
createPortal,
|
|
820
|
+
createSuspense,
|
|
821
|
+
createStrictMode,
|
|
822
|
+
createErrorBoundary,
|
|
823
|
+
createRef: function () { return { current: null, _isRef: true }; },
|
|
824
|
+
Fragment,
|
|
825
|
+
Suspense,
|
|
826
|
+
StrictMode,
|
|
827
|
+
ErrorBoundary,
|
|
828
|
+
lazy,
|
|
829
|
+
isValidElement,
|
|
830
|
+
isValidElementType,
|
|
831
|
+
isFunctionComponent,
|
|
832
|
+
isClassComponent,
|
|
833
|
+
isHostElement,
|
|
834
|
+
isSuspenseElement,
|
|
835
|
+
isStrictModeElement,
|
|
836
|
+
isErrorBoundaryElement,
|
|
837
|
+
extractKey,
|
|
838
|
+
processRef,
|
|
839
|
+
processProps,
|
|
840
|
+
processStyleProp,
|
|
841
|
+
processClassName,
|
|
842
|
+
flattenChildren,
|
|
843
|
+
normalizeChildren,
|
|
844
|
+
createElementWithRenderProp,
|
|
845
|
+
countChildren,
|
|
846
|
+
mapChildren,
|
|
847
|
+
forEachChild,
|
|
848
|
+
isChildrenArray,
|
|
849
|
+
onlyChild,
|
|
850
|
+
REACT_ELEMENT_TYPE,
|
|
851
|
+
REACT_FRAGMENT_TYPE,
|
|
852
|
+
REACT_PORTAL_TYPE,
|
|
853
|
+
REACT_SUSPENSE_TYPE,
|
|
854
|
+
REACT_LAZY_TYPE,
|
|
855
|
+
REACT_STRICT_MODE_TYPE,
|
|
856
|
+
REACT_ERROR_BOUNDARY_TYPE,
|
|
857
|
+
REACT_PROVIDER_TYPE,
|
|
858
|
+
REACT_CONSUMER_TYPE,
|
|
859
|
+
RESERVED_PROPS,
|
|
860
|
+
};
|