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