eleva 1.0.0-rc.4 → 1.0.0-rc.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/eleva-plugins.cjs.js +364 -1
- package/dist/eleva-plugins.cjs.js.map +1 -1
- package/dist/eleva-plugins.esm.js +364 -2
- package/dist/eleva-plugins.esm.js.map +1 -1
- package/dist/eleva-plugins.umd.js +364 -1
- package/dist/eleva-plugins.umd.js.map +1 -1
- package/dist/eleva-plugins.umd.min.js +2 -2
- package/dist/eleva-plugins.umd.min.js.map +1 -1
- package/dist/eleva.cjs.js +1 -1
- package/dist/eleva.esm.js +1 -1
- package/dist/eleva.umd.js +1 -1
- package/dist/eleva.umd.min.js +1 -1
- package/dist/plugins/props.umd.js +373 -0
- package/dist/plugins/props.umd.js.map +1 -0
- package/dist/plugins/props.umd.min.js +3 -0
- package/dist/plugins/props.umd.min.js.map +1 -0
- package/package.json +17 -17
- package/src/plugins/Props.js +385 -0
- package/src/plugins/index.js +1 -0
- package/types/plugins/Props.d.ts +48 -0
- package/types/plugins/Props.d.ts.map +1 -0
- package/types/plugins/index.d.ts +1 -0
package/README.md
CHANGED
|
@@ -40,9 +40,9 @@ Pure JavaScript, Pure Performance, Simply Elegant.
|
|
|
40
40
|
**A minimalist, lightweight, pure vanilla JavaScript frontend runtime framework.**
|
|
41
41
|
_Built with love for native JavaScript and designed with a minimal core that can be extended through a powerful plugin system-because sometimes, less really is more!_ 😊
|
|
42
42
|
|
|
43
|
-
> **Stability Notice**: This is `v1.0.0-rc.
|
|
43
|
+
> **Stability Notice**: This is `v1.0.0-rc.5` - The core functionality is stable. Seeking community feedback before the final v1.0.0 release.
|
|
44
44
|
|
|
45
|
-
**Version:** `1.0.0-rc.
|
|
45
|
+
**Version:** `1.0.0-rc.5`
|
|
46
46
|
|
|
47
47
|
|
|
48
48
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! Eleva Plugins v1.0.0-rc.
|
|
1
|
+
/*! Eleva Plugins v1.0.0-rc.5 | MIT License | https://elevajs.com */
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -1325,6 +1325,369 @@ const RouterPlugin = {
|
|
|
1325
1325
|
}
|
|
1326
1326
|
};
|
|
1327
1327
|
|
|
1328
|
+
/**
|
|
1329
|
+
* @class 🎯 PropsPlugin
|
|
1330
|
+
* @classdesc A plugin that extends Eleva's props data handling to support any type of data structure
|
|
1331
|
+
* with automatic type detection, parsing, and reactive prop updates. This plugin enables seamless
|
|
1332
|
+
* passing of complex data types from parent to child components without manual parsing.
|
|
1333
|
+
*
|
|
1334
|
+
* Core Features:
|
|
1335
|
+
* - Automatic type detection and parsing (strings, numbers, booleans, objects, arrays, dates, etc.)
|
|
1336
|
+
* - Support for complex data structures including nested objects and arrays
|
|
1337
|
+
* - Reactive props that automatically update when parent data changes
|
|
1338
|
+
* - Comprehensive error handling with custom error callbacks
|
|
1339
|
+
* - Simple configuration with minimal setup required
|
|
1340
|
+
*
|
|
1341
|
+
* @example
|
|
1342
|
+
* // Install the plugin
|
|
1343
|
+
* const app = new Eleva("myApp");
|
|
1344
|
+
* app.use(PropsPlugin, {
|
|
1345
|
+
* enableAutoParsing: true,
|
|
1346
|
+
* enableReactivity: true,
|
|
1347
|
+
* onError: (error, value) => {
|
|
1348
|
+
* console.error('Props parsing error:', error, value);
|
|
1349
|
+
* }
|
|
1350
|
+
* });
|
|
1351
|
+
*
|
|
1352
|
+
* // Use complex props in components
|
|
1353
|
+
* app.component("UserCard", {
|
|
1354
|
+
* template: (ctx) => `
|
|
1355
|
+
* <div class="user-info-container"
|
|
1356
|
+
* :user='${JSON.stringify(ctx.user.value)}'
|
|
1357
|
+
* :permissions='${JSON.stringify(ctx.permissions.value)}'
|
|
1358
|
+
* :settings='${JSON.stringify(ctx.settings.value)}'>
|
|
1359
|
+
* </div>
|
|
1360
|
+
* `,
|
|
1361
|
+
* children: {
|
|
1362
|
+
* '.user-info-container': 'UserInfo'
|
|
1363
|
+
* }
|
|
1364
|
+
* });
|
|
1365
|
+
*
|
|
1366
|
+
* app.component("UserInfo", {
|
|
1367
|
+
* setup({ props }) {
|
|
1368
|
+
* return {
|
|
1369
|
+
* user: props.user, // Automatically parsed object
|
|
1370
|
+
* permissions: props.permissions, // Automatically parsed array
|
|
1371
|
+
* settings: props.settings // Automatically parsed object
|
|
1372
|
+
* };
|
|
1373
|
+
* }
|
|
1374
|
+
* });
|
|
1375
|
+
*/
|
|
1376
|
+
const PropsPlugin = {
|
|
1377
|
+
/**
|
|
1378
|
+
* Unique identifier for the plugin
|
|
1379
|
+
* @type {string}
|
|
1380
|
+
*/
|
|
1381
|
+
name: "props",
|
|
1382
|
+
/**
|
|
1383
|
+
* Plugin version
|
|
1384
|
+
* @type {string}
|
|
1385
|
+
*/
|
|
1386
|
+
version: "1.0.0-rc.1",
|
|
1387
|
+
/**
|
|
1388
|
+
* Plugin description
|
|
1389
|
+
* @type {string}
|
|
1390
|
+
*/
|
|
1391
|
+
description: "Advanced props data handling for complex data structures with automatic type detection and reactivity",
|
|
1392
|
+
/**
|
|
1393
|
+
* Installs the plugin into the Eleva instance
|
|
1394
|
+
*
|
|
1395
|
+
* @param {Object} eleva - The Eleva instance
|
|
1396
|
+
* @param {Object} options - Plugin configuration options
|
|
1397
|
+
* @param {boolean} [options.enableAutoParsing=true] - Enable automatic type detection and parsing
|
|
1398
|
+
* @param {boolean} [options.enableReactivity=true] - Enable reactive prop updates using Eleva's signal system
|
|
1399
|
+
* @param {Function} [options.onError=null] - Error handler function called when parsing fails
|
|
1400
|
+
*
|
|
1401
|
+
* @example
|
|
1402
|
+
* // Basic installation
|
|
1403
|
+
* app.use(PropsPlugin);
|
|
1404
|
+
*
|
|
1405
|
+
* // Installation with custom options
|
|
1406
|
+
* app.use(PropsPlugin, {
|
|
1407
|
+
* enableAutoParsing: true,
|
|
1408
|
+
* enableReactivity: false,
|
|
1409
|
+
* onError: (error, value) => {
|
|
1410
|
+
* console.error('Props parsing error:', error, value);
|
|
1411
|
+
* }
|
|
1412
|
+
* });
|
|
1413
|
+
*/
|
|
1414
|
+
install(eleva, options = {}) {
|
|
1415
|
+
const {
|
|
1416
|
+
enableAutoParsing = true,
|
|
1417
|
+
enableReactivity = true,
|
|
1418
|
+
onError = null
|
|
1419
|
+
} = options;
|
|
1420
|
+
|
|
1421
|
+
/**
|
|
1422
|
+
* Detects the type of a given value
|
|
1423
|
+
* @private
|
|
1424
|
+
* @param {any} value - The value to detect type for
|
|
1425
|
+
* @returns {string} The detected type ('string', 'number', 'boolean', 'object', 'array', 'date', 'map', 'set', 'function', 'null', 'undefined', 'unknown')
|
|
1426
|
+
*
|
|
1427
|
+
* @example
|
|
1428
|
+
* detectType("hello") // → "string"
|
|
1429
|
+
* detectType(42) // → "number"
|
|
1430
|
+
* detectType(true) // → "boolean"
|
|
1431
|
+
* detectType([1, 2, 3]) // → "array"
|
|
1432
|
+
* detectType({}) // → "object"
|
|
1433
|
+
* detectType(new Date()) // → "date"
|
|
1434
|
+
* detectType(null) // → "null"
|
|
1435
|
+
*/
|
|
1436
|
+
const detectType = value => {
|
|
1437
|
+
if (value === null) return "null";
|
|
1438
|
+
if (value === undefined) return "undefined";
|
|
1439
|
+
if (typeof value === "boolean") return "boolean";
|
|
1440
|
+
if (typeof value === "number") return "number";
|
|
1441
|
+
if (typeof value === "string") return "string";
|
|
1442
|
+
if (typeof value === "function") return "function";
|
|
1443
|
+
if (value instanceof Date) return "date";
|
|
1444
|
+
if (value instanceof Map) return "map";
|
|
1445
|
+
if (value instanceof Set) return "set";
|
|
1446
|
+
if (Array.isArray(value)) return "array";
|
|
1447
|
+
if (typeof value === "object") return "object";
|
|
1448
|
+
return "unknown";
|
|
1449
|
+
};
|
|
1450
|
+
|
|
1451
|
+
/**
|
|
1452
|
+
* Parses a prop value with automatic type detection
|
|
1453
|
+
* @private
|
|
1454
|
+
* @param {any} value - The value to parse
|
|
1455
|
+
* @returns {any} The parsed value with appropriate type
|
|
1456
|
+
*
|
|
1457
|
+
* @description
|
|
1458
|
+
* This function automatically detects and parses different data types from string values:
|
|
1459
|
+
* - Special strings: "true" → true, "false" → false, "null" → null, "undefined" → undefined
|
|
1460
|
+
* - JSON objects/arrays: '{"key": "value"}' → {key: "value"}, '[1, 2, 3]' → [1, 2, 3]
|
|
1461
|
+
* - Boolean-like strings: "1" → true, "0" → false, "" → true
|
|
1462
|
+
* - Numeric strings: "42" → 42, "3.14" → 3.14
|
|
1463
|
+
* - Date strings: "2023-01-01T00:00:00.000Z" → Date object
|
|
1464
|
+
* - Other strings: returned as-is
|
|
1465
|
+
*
|
|
1466
|
+
* @example
|
|
1467
|
+
* parsePropValue("true") // → true
|
|
1468
|
+
* parsePropValue("42") // → 42
|
|
1469
|
+
* parsePropValue('{"key": "val"}') // → {key: "val"}
|
|
1470
|
+
* parsePropValue('[1, 2, 3]') // → [1, 2, 3]
|
|
1471
|
+
* parsePropValue("hello") // → "hello"
|
|
1472
|
+
*/
|
|
1473
|
+
const parsePropValue = value => {
|
|
1474
|
+
try {
|
|
1475
|
+
// Handle non-string values - return as-is
|
|
1476
|
+
if (typeof value !== "string") {
|
|
1477
|
+
return value;
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
// Handle special string patterns first
|
|
1481
|
+
if (value === "true") return true;
|
|
1482
|
+
if (value === "false") return false;
|
|
1483
|
+
if (value === "null") return null;
|
|
1484
|
+
if (value === "undefined") return undefined;
|
|
1485
|
+
|
|
1486
|
+
// Try to parse as JSON (for objects and arrays)
|
|
1487
|
+
// This handles complex data structures like objects and arrays
|
|
1488
|
+
if (value.startsWith("{") || value.startsWith("[")) {
|
|
1489
|
+
try {
|
|
1490
|
+
return JSON.parse(value);
|
|
1491
|
+
} catch (e) {
|
|
1492
|
+
// Not valid JSON, throw error to trigger error handler
|
|
1493
|
+
throw new Error(`Invalid JSON: ${value}`);
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
// Handle boolean-like strings (including "1" and "0")
|
|
1498
|
+
// These are common in HTML attributes and should be treated as booleans
|
|
1499
|
+
if (value === "1") return true;
|
|
1500
|
+
if (value === "0") return false;
|
|
1501
|
+
if (value === "") return true; // Empty string is truthy in HTML attributes
|
|
1502
|
+
|
|
1503
|
+
// Handle numeric strings (after boolean check to avoid conflicts)
|
|
1504
|
+
// This ensures "0" is treated as boolean false, not number 0
|
|
1505
|
+
if (!isNaN(value) && value !== "" && !isNaN(parseFloat(value))) {
|
|
1506
|
+
return Number(value);
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
// Handle date strings (ISO format)
|
|
1510
|
+
// Recognizes standard ISO date format and converts to Date object
|
|
1511
|
+
if (value.match(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/)) {
|
|
1512
|
+
const date = new Date(value);
|
|
1513
|
+
if (!isNaN(date.getTime())) {
|
|
1514
|
+
return date;
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
// Return as string if no other parsing applies
|
|
1519
|
+
// This is the fallback for regular text strings
|
|
1520
|
+
return value;
|
|
1521
|
+
} catch (error) {
|
|
1522
|
+
// Call error handler if provided
|
|
1523
|
+
if (onError) {
|
|
1524
|
+
onError(error, value);
|
|
1525
|
+
}
|
|
1526
|
+
// Fallback to original value to prevent breaking the application
|
|
1527
|
+
return value;
|
|
1528
|
+
}
|
|
1529
|
+
};
|
|
1530
|
+
|
|
1531
|
+
/**
|
|
1532
|
+
* Enhanced props extraction with automatic type detection
|
|
1533
|
+
* @private
|
|
1534
|
+
* @param {HTMLElement} element - The DOM element to extract props from
|
|
1535
|
+
* @returns {Object} Object containing parsed props with appropriate types
|
|
1536
|
+
*
|
|
1537
|
+
* @description
|
|
1538
|
+
* Extracts props from DOM element attributes that start with ":" and automatically
|
|
1539
|
+
* parses them to their appropriate types. Removes the attributes from the element
|
|
1540
|
+
* after extraction.
|
|
1541
|
+
*
|
|
1542
|
+
* @example
|
|
1543
|
+
* // HTML: <div :name="John" :age="30" :active="true" :data='{"key": "value"}'></div>
|
|
1544
|
+
* const props = extractProps(element);
|
|
1545
|
+
* // Result: { name: "John", age: 30, active: true, data: {key: "value"} }
|
|
1546
|
+
*/
|
|
1547
|
+
const extractProps = element => {
|
|
1548
|
+
const props = {};
|
|
1549
|
+
const attrs = element.attributes;
|
|
1550
|
+
|
|
1551
|
+
// Iterate through attributes in reverse order to handle removal correctly
|
|
1552
|
+
for (let i = attrs.length - 1; i >= 0; i--) {
|
|
1553
|
+
const attr = attrs[i];
|
|
1554
|
+
// Only process attributes that start with ":" (prop attributes)
|
|
1555
|
+
if (attr.name.startsWith(":")) {
|
|
1556
|
+
const propName = attr.name.slice(1); // Remove the ":" prefix
|
|
1557
|
+
// Parse the value if auto-parsing is enabled, otherwise use as-is
|
|
1558
|
+
const parsedValue = enableAutoParsing ? parsePropValue(attr.value) : attr.value;
|
|
1559
|
+
props[propName] = parsedValue;
|
|
1560
|
+
// Remove the attribute from the DOM element after extraction
|
|
1561
|
+
element.removeAttribute(attr.name);
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
return props;
|
|
1565
|
+
};
|
|
1566
|
+
|
|
1567
|
+
/**
|
|
1568
|
+
* Creates reactive props using Eleva's signal system
|
|
1569
|
+
* @private
|
|
1570
|
+
* @param {Object} props - The props object to make reactive
|
|
1571
|
+
* @returns {Object} Object containing reactive props (Eleva signals)
|
|
1572
|
+
*
|
|
1573
|
+
* @description
|
|
1574
|
+
* Converts regular prop values into Eleva signals for reactive updates.
|
|
1575
|
+
* If a value is already a signal, it's passed through unchanged.
|
|
1576
|
+
*
|
|
1577
|
+
* @example
|
|
1578
|
+
* const props = { name: "John", age: 30, active: true };
|
|
1579
|
+
* const reactiveProps = createReactiveProps(props);
|
|
1580
|
+
* // Result: {
|
|
1581
|
+
* // name: Signal("John"),
|
|
1582
|
+
* // age: Signal(30),
|
|
1583
|
+
* // active: Signal(true)
|
|
1584
|
+
* // }
|
|
1585
|
+
*/
|
|
1586
|
+
const createReactiveProps = props => {
|
|
1587
|
+
const reactiveProps = {};
|
|
1588
|
+
|
|
1589
|
+
// Convert each prop value to a reactive signal
|
|
1590
|
+
Object.entries(props).forEach(([key, value]) => {
|
|
1591
|
+
// Check if value is already a signal (has 'value' and 'watch' properties)
|
|
1592
|
+
if (value && typeof value === "object" && "value" in value && "watch" in value) {
|
|
1593
|
+
// Value is already a signal, use it as-is
|
|
1594
|
+
reactiveProps[key] = value;
|
|
1595
|
+
} else {
|
|
1596
|
+
// Create new signal for the prop value to make it reactive
|
|
1597
|
+
reactiveProps[key] = new eleva.signal(value);
|
|
1598
|
+
}
|
|
1599
|
+
});
|
|
1600
|
+
return reactiveProps;
|
|
1601
|
+
};
|
|
1602
|
+
|
|
1603
|
+
// Override Eleva's internal _extractProps method with our enhanced version
|
|
1604
|
+
eleva._extractProps = extractProps;
|
|
1605
|
+
|
|
1606
|
+
// Override Eleva's mount method to apply enhanced prop handling
|
|
1607
|
+
const originalMount = eleva.mount;
|
|
1608
|
+
eleva.mount = async (container, compName, props = {}) => {
|
|
1609
|
+
// Create reactive props if reactivity is enabled
|
|
1610
|
+
const enhancedProps = enableReactivity ? createReactiveProps(props) : props;
|
|
1611
|
+
|
|
1612
|
+
// Call the original mount method with enhanced props
|
|
1613
|
+
return await originalMount.call(eleva, container, compName, enhancedProps);
|
|
1614
|
+
};
|
|
1615
|
+
|
|
1616
|
+
/**
|
|
1617
|
+
* Expose utility methods on the Eleva instance
|
|
1618
|
+
* @namespace eleva.props
|
|
1619
|
+
*/
|
|
1620
|
+
eleva.props = {
|
|
1621
|
+
/**
|
|
1622
|
+
* Parse a single value with automatic type detection
|
|
1623
|
+
* @param {any} value - The value to parse
|
|
1624
|
+
* @returns {any} The parsed value with appropriate type
|
|
1625
|
+
*
|
|
1626
|
+
* @example
|
|
1627
|
+
* app.props.parse("42") // → 42
|
|
1628
|
+
* app.props.parse("true") // → true
|
|
1629
|
+
* app.props.parse('{"key": "val"}') // → {key: "val"}
|
|
1630
|
+
*/
|
|
1631
|
+
parse: value => {
|
|
1632
|
+
// Return value as-is if auto parsing is disabled
|
|
1633
|
+
if (!enableAutoParsing) {
|
|
1634
|
+
return value;
|
|
1635
|
+
}
|
|
1636
|
+
// Use our enhanced parsing function
|
|
1637
|
+
return parsePropValue(value);
|
|
1638
|
+
},
|
|
1639
|
+
/**
|
|
1640
|
+
* Detect the type of a value
|
|
1641
|
+
* @param {any} value - The value to detect type for
|
|
1642
|
+
* @returns {string} The detected type
|
|
1643
|
+
*
|
|
1644
|
+
* @example
|
|
1645
|
+
* app.props.detectType("hello") // → "string"
|
|
1646
|
+
* app.props.detectType(42) // → "number"
|
|
1647
|
+
* app.props.detectType([1, 2, 3]) // → "array"
|
|
1648
|
+
*/
|
|
1649
|
+
detectType
|
|
1650
|
+
};
|
|
1651
|
+
|
|
1652
|
+
// Store original methods for uninstall
|
|
1653
|
+
eleva._originalExtractProps = eleva._extractProps;
|
|
1654
|
+
eleva._originalMount = originalMount;
|
|
1655
|
+
},
|
|
1656
|
+
/**
|
|
1657
|
+
* Uninstalls the plugin from the Eleva instance
|
|
1658
|
+
*
|
|
1659
|
+
* @param {Object} eleva - The Eleva instance
|
|
1660
|
+
*
|
|
1661
|
+
* @description
|
|
1662
|
+
* Restores the original Eleva methods and removes all plugin-specific
|
|
1663
|
+
* functionality. This method should be called when the plugin is no
|
|
1664
|
+
* longer needed.
|
|
1665
|
+
*
|
|
1666
|
+
* @example
|
|
1667
|
+
* // Uninstall the plugin
|
|
1668
|
+
* PropsPlugin.uninstall(app);
|
|
1669
|
+
*/
|
|
1670
|
+
uninstall(eleva) {
|
|
1671
|
+
// Restore original _extractProps method
|
|
1672
|
+
if (eleva._originalExtractProps) {
|
|
1673
|
+
eleva._extractProps = eleva._originalExtractProps;
|
|
1674
|
+
delete eleva._originalExtractProps;
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
// Restore original mount method
|
|
1678
|
+
if (eleva._originalMount) {
|
|
1679
|
+
eleva.mount = eleva._originalMount;
|
|
1680
|
+
delete eleva._originalMount;
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1683
|
+
// Remove plugin utility methods
|
|
1684
|
+
if (eleva.props) {
|
|
1685
|
+
delete eleva.props;
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
};
|
|
1689
|
+
|
|
1328
1690
|
exports.Attr = AttrPlugin;
|
|
1691
|
+
exports.Props = PropsPlugin;
|
|
1329
1692
|
exports.Router = RouterPlugin;
|
|
1330
1693
|
//# sourceMappingURL=eleva-plugins.cjs.js.map
|