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 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.4` - The core functionality is stable. Seeking community feedback before the final v1.0.0 release.
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.4`
45
+ **Version:** `1.0.0-rc.5`
46
46
 
47
47
 
48
48
 
@@ -1,4 +1,4 @@
1
- /*! Eleva Plugins v1.0.0-rc.4 | MIT License | https://elevajs.com */
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