snice 2.2.2 → 2.2.3
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/dist/index.cjs +163 -129
- package/dist/index.cjs.map +1 -1
- package/dist/index.esm.js +163 -129
- package/dist/index.esm.js.map +1 -1
- package/dist/index.iife.js +163 -129
- package/dist/index.iife.js.map +1 -1
- package/dist/symbols.cjs.map +1 -1
- package/dist/symbols.esm.js +1 -1
- package/dist/symbols.esm.js.map +1 -1
- package/dist/transitions.cjs.map +1 -1
- package/dist/transitions.esm.js +1 -1
- package/dist/transitions.esm.js.map +1 -1
- package/dist/types/controller.d.ts +1 -7
- package/dist/types/element.d.ts +3 -44
- package/dist/types/events.d.ts +2 -26
- package/dist/types/global.d.ts +1 -5
- package/dist/types/index.d.ts +2 -8
- package/dist/types/observe.d.ts +1 -16
- package/dist/types/request-response.d.ts +2 -28
- package/dist/types/router.d.ts +2 -81
- package/dist/types/transitions.d.ts +2 -30
- package/dist/types/types/DispatchOptions.d.ts +10 -0
- package/dist/types/types/IController.d.ts +8 -0
- package/dist/types/types/ObserveOptions.d.ts +16 -0
- package/dist/types/types/OnOptions.d.ts +16 -0
- package/dist/types/types/PageOptions.d.ts +30 -0
- package/dist/types/types/PartOptions.d.ts +4 -0
- package/dist/types/types/PropertyConverter.d.ts +4 -0
- package/dist/types/types/PropertyOptions.d.ts +9 -0
- package/dist/types/types/QueryOptions.d.ts +4 -0
- package/dist/types/types/RequestOptions.d.ts +18 -0
- package/dist/types/types/RespondOptions.d.ts +10 -0
- package/dist/types/types/RouterInstance.d.ts +10 -0
- package/dist/types/types/RouterOptions.d.ts +32 -0
- package/dist/types/types/SimpleArray.d.ts +17 -0
- package/dist/types/types/SniceElement.d.ts +8 -0
- package/dist/types/types/SniceGlobal.d.ts +5 -0
- package/dist/types/types/Transition.d.ts +33 -0
- package/dist/types/types/index.d.ts +17 -0
- package/dist/types/utils.d.ts +16 -0
- package/package.json +2 -3
package/dist/index.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* snice v2.2.
|
|
2
|
+
* snice v2.2.2
|
|
3
3
|
* Imperative TypeScript framework for building vanilla web components with decorators, routing, and controllers. No virtual DOM, no build complexity.
|
|
4
4
|
* (c) 2024
|
|
5
5
|
* Released under the MIT License.
|
|
@@ -701,6 +701,7 @@ function cleanupObservers(instance) {
|
|
|
701
701
|
}
|
|
702
702
|
}
|
|
703
703
|
|
|
704
|
+
// @request decorator transforms methods to return Promise<T>
|
|
704
705
|
/**
|
|
705
706
|
* Decorator for making requests from elements or controllers.
|
|
706
707
|
* Uses async generator pattern for bidirectional communication.
|
|
@@ -1237,6 +1238,133 @@ function cleanupNativeElementControllers() {
|
|
|
1237
1238
|
}
|
|
1238
1239
|
}
|
|
1239
1240
|
|
|
1241
|
+
/**
|
|
1242
|
+
* SimpleArray type for arrays that can be safely reflected to attributes
|
|
1243
|
+
* Supports arrays of: string, number, boolean
|
|
1244
|
+
* Uses full-width comma (,) as separator to avoid conflicts
|
|
1245
|
+
* Strings cannot contain the full-width comma character
|
|
1246
|
+
*/
|
|
1247
|
+
class SimpleArray {
|
|
1248
|
+
static { this.SEPARATOR = ','; } // U+FF0C Full-width comma
|
|
1249
|
+
/**
|
|
1250
|
+
* Serialize array to string for attribute storage
|
|
1251
|
+
*/
|
|
1252
|
+
static serialize(arr) {
|
|
1253
|
+
if (!Array.isArray(arr))
|
|
1254
|
+
return '';
|
|
1255
|
+
return arr.map(item => {
|
|
1256
|
+
if (typeof item === 'string') {
|
|
1257
|
+
// Validate string doesn't contain our separator
|
|
1258
|
+
if (item.includes(SimpleArray.SEPARATOR)) {
|
|
1259
|
+
throw new Error(`SimpleArray strings cannot contain the character "${SimpleArray.SEPARATOR}" (U+FF0C)`);
|
|
1260
|
+
}
|
|
1261
|
+
return item;
|
|
1262
|
+
}
|
|
1263
|
+
else if (typeof item === 'number' || typeof item === 'boolean') {
|
|
1264
|
+
return String(item);
|
|
1265
|
+
}
|
|
1266
|
+
else {
|
|
1267
|
+
throw new Error(`SimpleArray only supports string, number, and boolean types. Got: ${typeof item}`);
|
|
1268
|
+
}
|
|
1269
|
+
}).join(SimpleArray.SEPARATOR);
|
|
1270
|
+
}
|
|
1271
|
+
/**
|
|
1272
|
+
* Parse string from attribute back to array
|
|
1273
|
+
*/
|
|
1274
|
+
static parse(str) {
|
|
1275
|
+
if (str === null || str === undefined)
|
|
1276
|
+
return [];
|
|
1277
|
+
// Empty string should not be parsed as containing an empty string
|
|
1278
|
+
// since empty arrays don't get reflected (handled by the reflection logic)
|
|
1279
|
+
if (str === '')
|
|
1280
|
+
return [];
|
|
1281
|
+
return str.split(SimpleArray.SEPARATOR).map(item => {
|
|
1282
|
+
// Try to parse as number
|
|
1283
|
+
if (/^-?\d+\.?\d*$/.test(item)) {
|
|
1284
|
+
const num = Number(item);
|
|
1285
|
+
if (!isNaN(num))
|
|
1286
|
+
return num;
|
|
1287
|
+
}
|
|
1288
|
+
// Parse as boolean
|
|
1289
|
+
if (item === 'true')
|
|
1290
|
+
return true;
|
|
1291
|
+
if (item === 'false')
|
|
1292
|
+
return false;
|
|
1293
|
+
// Default to string
|
|
1294
|
+
return item;
|
|
1295
|
+
});
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
/**
|
|
1300
|
+
* Detects the type constructor from an initial value
|
|
1301
|
+
* @param initialValue - The default value assigned to a property field (e.g., `name = "default"` -> "default")
|
|
1302
|
+
* @returns The constructor function (String, Number, Boolean, etc.) or undefined if type can't be determined
|
|
1303
|
+
*/
|
|
1304
|
+
function detectType(initialValue) {
|
|
1305
|
+
if (initialValue === null || initialValue === undefined) {
|
|
1306
|
+
return undefined;
|
|
1307
|
+
}
|
|
1308
|
+
if (typeof initialValue === 'string') {
|
|
1309
|
+
return String;
|
|
1310
|
+
}
|
|
1311
|
+
if (typeof initialValue === 'number') {
|
|
1312
|
+
return Number;
|
|
1313
|
+
}
|
|
1314
|
+
if (typeof initialValue === 'boolean') {
|
|
1315
|
+
return Boolean;
|
|
1316
|
+
}
|
|
1317
|
+
if (initialValue instanceof Date) {
|
|
1318
|
+
return Date;
|
|
1319
|
+
}
|
|
1320
|
+
if (typeof initialValue === 'bigint') {
|
|
1321
|
+
return BigInt;
|
|
1322
|
+
}
|
|
1323
|
+
if (Array.isArray(initialValue)) {
|
|
1324
|
+
return Array;
|
|
1325
|
+
}
|
|
1326
|
+
return undefined;
|
|
1327
|
+
}
|
|
1328
|
+
/**
|
|
1329
|
+
* Parses an attribute value based on the property type
|
|
1330
|
+
* @param attributeValue - The raw string value from the HTML attribute (e.g., "123", "true", "hello")
|
|
1331
|
+
* @param propertyOptions - The options from @property decorator (contains explicit type, attribute name, etc.)
|
|
1332
|
+
* @param currentValue - The current stored value of the property (used for type inference when no explicit type)
|
|
1333
|
+
* @param initialValue - The default value assigned to the property field (used for type detection as fallback)
|
|
1334
|
+
* @returns The parsed value in the correct JavaScript type
|
|
1335
|
+
*/
|
|
1336
|
+
function parseAttributeValue(attributeValue, propertyOptions, currentValue, initialValue) {
|
|
1337
|
+
// Use explicit type or detect from initial value
|
|
1338
|
+
const typeToUse = propertyOptions.type || detectType(initialValue);
|
|
1339
|
+
switch (typeToUse) {
|
|
1340
|
+
case Boolean:
|
|
1341
|
+
return attributeValue !== null && attributeValue !== 'false';
|
|
1342
|
+
case Number:
|
|
1343
|
+
return attributeValue !== null ? Number(attributeValue) : null;
|
|
1344
|
+
case String:
|
|
1345
|
+
return attributeValue;
|
|
1346
|
+
case Date:
|
|
1347
|
+
return attributeValue ? new Date(attributeValue) : null;
|
|
1348
|
+
case BigInt:
|
|
1349
|
+
if (attributeValue && attributeValue.endsWith('n')) {
|
|
1350
|
+
return BigInt(attributeValue.slice(0, -1));
|
|
1351
|
+
}
|
|
1352
|
+
else {
|
|
1353
|
+
return attributeValue ? BigInt(attributeValue) : null;
|
|
1354
|
+
}
|
|
1355
|
+
case SimpleArray:
|
|
1356
|
+
return SimpleArray.parse(attributeValue);
|
|
1357
|
+
default:
|
|
1358
|
+
// If no type specified and can't detect, try to infer from current value type
|
|
1359
|
+
if (typeof currentValue === 'number' && attributeValue !== null) {
|
|
1360
|
+
return Number(attributeValue);
|
|
1361
|
+
}
|
|
1362
|
+
else {
|
|
1363
|
+
return attributeValue;
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1240
1368
|
/**
|
|
1241
1369
|
* Applies core element functionality to a constructor
|
|
1242
1370
|
* This is shared between @element and @page decorators
|
|
@@ -1318,42 +1446,17 @@ function applyElementFunctionality(constructor) {
|
|
|
1318
1446
|
const properties = constructor[PROPERTIES];
|
|
1319
1447
|
if (properties) {
|
|
1320
1448
|
for (const [propName, propOptions] of properties) {
|
|
1321
|
-
//
|
|
1322
|
-
|
|
1449
|
+
// Check for attribute using proper attribute name
|
|
1450
|
+
const attributeName = typeof propOptions.attribute === 'string' ? propOptions.attribute : propName.toLowerCase();
|
|
1451
|
+
if (this.hasAttribute(attributeName)) {
|
|
1323
1452
|
// Attribute exists, parse and set the property value
|
|
1324
|
-
const attrValue = this.getAttribute(
|
|
1453
|
+
const attrValue = this.getAttribute(attributeName);
|
|
1325
1454
|
// Mark as explicitly set since it came from an attribute
|
|
1326
1455
|
if (!this[EXPLICITLY_SET_PROPERTIES]) {
|
|
1327
1456
|
this[EXPLICITLY_SET_PROPERTIES] = new Set();
|
|
1328
1457
|
}
|
|
1329
1458
|
this[EXPLICITLY_SET_PROPERTIES].add(propName);
|
|
1330
|
-
|
|
1331
|
-
case Boolean:
|
|
1332
|
-
this[propName] = attrValue !== null && attrValue !== 'false';
|
|
1333
|
-
break;
|
|
1334
|
-
case Number:
|
|
1335
|
-
this[propName] = Number(attrValue);
|
|
1336
|
-
break;
|
|
1337
|
-
case String:
|
|
1338
|
-
this[propName] = attrValue;
|
|
1339
|
-
break;
|
|
1340
|
-
case Date:
|
|
1341
|
-
this[propName] = attrValue ? new Date(attrValue) : null;
|
|
1342
|
-
break;
|
|
1343
|
-
case BigInt:
|
|
1344
|
-
if (attrValue && attrValue.endsWith('n')) {
|
|
1345
|
-
this[propName] = BigInt(attrValue.slice(0, -1));
|
|
1346
|
-
}
|
|
1347
|
-
else {
|
|
1348
|
-
this[propName] = attrValue ? BigInt(attrValue) : null;
|
|
1349
|
-
}
|
|
1350
|
-
break;
|
|
1351
|
-
case SimpleArray:
|
|
1352
|
-
this[propName] = SimpleArray.parse(attrValue);
|
|
1353
|
-
break;
|
|
1354
|
-
default:
|
|
1355
|
-
this[propName] = attrValue;
|
|
1356
|
-
}
|
|
1459
|
+
this[propName] = parseAttributeValue(attrValue, propOptions);
|
|
1357
1460
|
}
|
|
1358
1461
|
}
|
|
1359
1462
|
}
|
|
@@ -1528,40 +1631,11 @@ function applyElementFunctionality(constructor) {
|
|
|
1528
1631
|
if (properties) {
|
|
1529
1632
|
for (const [propName, propOptions] of properties) {
|
|
1530
1633
|
const attributeName = typeof propOptions.attribute === 'string' ? propOptions.attribute : propName.toLowerCase();
|
|
1531
|
-
if (attributeName === name) {
|
|
1634
|
+
if (attributeName.toLowerCase() === name.toLowerCase()) {
|
|
1532
1635
|
// Check if the current property value already matches to avoid feedback loops
|
|
1533
1636
|
const currentValue = this[PROPERTY_VALUES]?.[propName];
|
|
1534
1637
|
// Parse the new value based on type
|
|
1535
|
-
|
|
1536
|
-
if (propOptions.type === Boolean) {
|
|
1537
|
-
parsedValue = newValue !== null && newValue !== 'false';
|
|
1538
|
-
}
|
|
1539
|
-
else if (propOptions.type === Number) {
|
|
1540
|
-
parsedValue = Number(newValue);
|
|
1541
|
-
}
|
|
1542
|
-
else if (propOptions.type === Date) {
|
|
1543
|
-
parsedValue = newValue ? new Date(newValue) : null;
|
|
1544
|
-
}
|
|
1545
|
-
else if (propOptions.type === BigInt) {
|
|
1546
|
-
if (newValue && newValue.endsWith('n')) {
|
|
1547
|
-
parsedValue = BigInt(newValue.slice(0, -1));
|
|
1548
|
-
}
|
|
1549
|
-
else {
|
|
1550
|
-
parsedValue = newValue ? BigInt(newValue) : null;
|
|
1551
|
-
}
|
|
1552
|
-
}
|
|
1553
|
-
else if (propOptions.type === SimpleArray) {
|
|
1554
|
-
parsedValue = SimpleArray.parse(newValue);
|
|
1555
|
-
}
|
|
1556
|
-
else {
|
|
1557
|
-
// If no type specified, try to infer from current value type
|
|
1558
|
-
if (typeof currentValue === 'number' && newValue !== null) {
|
|
1559
|
-
parsedValue = Number(newValue);
|
|
1560
|
-
}
|
|
1561
|
-
else {
|
|
1562
|
-
parsedValue = newValue;
|
|
1563
|
-
}
|
|
1564
|
-
}
|
|
1638
|
+
const parsedValue = parseAttributeValue(newValue, propOptions, currentValue, undefined);
|
|
1565
1639
|
// Only update if the value actually changed and avoid infinite loops
|
|
1566
1640
|
if (currentValue !== parsedValue) {
|
|
1567
1641
|
// Mark as explicitly set since it came from an attribute change
|
|
@@ -1660,11 +1734,18 @@ function property(options) {
|
|
|
1660
1734
|
if (options?.reflect && options?.type === Object) {
|
|
1661
1735
|
console.warn(`⚠️ Property '${propertyKey}' uses reflect:true with Object type.`);
|
|
1662
1736
|
}
|
|
1663
|
-
context.addInitializer(function () {
|
|
1664
|
-
// No longer need warnings here since they're at decoration time
|
|
1665
|
-
});
|
|
1666
1737
|
// Return a field initializer function for new decorators
|
|
1667
1738
|
return function (initialValue) {
|
|
1739
|
+
// Detect type from initial value if not explicitly provided
|
|
1740
|
+
const finalOptions = { ...options };
|
|
1741
|
+
if (!finalOptions.type && initialValue !== undefined) {
|
|
1742
|
+
finalOptions.type = detectType(initialValue);
|
|
1743
|
+
// Update the metadata with the detected type
|
|
1744
|
+
const constructor = this.constructor;
|
|
1745
|
+
if (constructor[PROPERTIES]) {
|
|
1746
|
+
constructor[PROPERTIES].set(propertyKey, finalOptions);
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1668
1749
|
// Set up the property descriptor on first access
|
|
1669
1750
|
if (!Object.hasOwnProperty.call(this.constructor.prototype, propertyKey)) {
|
|
1670
1751
|
const descriptor = {
|
|
@@ -1672,7 +1753,18 @@ function property(options) {
|
|
|
1672
1753
|
if (!this[PROPERTY_VALUES]) {
|
|
1673
1754
|
this[PROPERTY_VALUES] = {};
|
|
1674
1755
|
}
|
|
1675
|
-
return
|
|
1756
|
+
// If we have a stored value, return it
|
|
1757
|
+
if (this[PROPERTY_VALUES][propertyKey] !== undefined) {
|
|
1758
|
+
return this[PROPERTY_VALUES][propertyKey];
|
|
1759
|
+
}
|
|
1760
|
+
// Otherwise check attribute and parse it, or return initial value
|
|
1761
|
+
const attributeName = typeof finalOptions?.attribute === 'string' ? finalOptions?.attribute : propertyKey.toLowerCase();
|
|
1762
|
+
const attrValue = this.getAttribute?.(attributeName);
|
|
1763
|
+
// If attribute exists or we have a type that needs special handling for null (like Boolean)
|
|
1764
|
+
if (attrValue !== null || finalOptions?.type === Boolean) {
|
|
1765
|
+
return parseAttributeValue(attrValue, finalOptions || {}, undefined, initialValue);
|
|
1766
|
+
}
|
|
1767
|
+
return initialValue;
|
|
1676
1768
|
},
|
|
1677
1769
|
set(newValue) {
|
|
1678
1770
|
if (!this[PROPERTY_VALUES]) {
|
|
@@ -1689,10 +1781,10 @@ function property(options) {
|
|
|
1689
1781
|
this[EXPLICITLY_SET_PROPERTIES].add(propertyKey);
|
|
1690
1782
|
}
|
|
1691
1783
|
this[PROPERTY_VALUES][propertyKey] = newValue;
|
|
1692
|
-
if (
|
|
1693
|
-
const attributeName = typeof
|
|
1784
|
+
if (finalOptions?.reflect && this.setAttribute && this[PROPERTIES_INITIALIZED] && this[EXPLICITLY_SET_PROPERTIES].has(propertyKey)) {
|
|
1785
|
+
const attributeName = typeof finalOptions.attribute === 'string' ? finalOptions.attribute : propertyKey.toLowerCase();
|
|
1694
1786
|
if (newValue === null || newValue === undefined || newValue === false ||
|
|
1695
|
-
(
|
|
1787
|
+
(finalOptions?.type === SimpleArray && Array.isArray(newValue) && newValue.length === 0)) {
|
|
1696
1788
|
this.removeAttribute(attributeName);
|
|
1697
1789
|
}
|
|
1698
1790
|
else {
|
|
@@ -1703,7 +1795,7 @@ function property(options) {
|
|
|
1703
1795
|
else if (typeof newValue === 'bigint') {
|
|
1704
1796
|
attributeValue = newValue.toString() + 'n';
|
|
1705
1797
|
}
|
|
1706
|
-
else if (
|
|
1798
|
+
else if (finalOptions?.type === SimpleArray && Array.isArray(newValue)) {
|
|
1707
1799
|
attributeValue = SimpleArray.serialize(newValue);
|
|
1708
1800
|
}
|
|
1709
1801
|
else {
|
|
@@ -1831,63 +1923,6 @@ function queryAll(selector, options = {}) {
|
|
|
1831
1923
|
};
|
|
1832
1924
|
};
|
|
1833
1925
|
}
|
|
1834
|
-
/**
|
|
1835
|
-
* SimpleArray type for arrays that can be safely reflected to attributes
|
|
1836
|
-
* Supports arrays of: string, number, boolean
|
|
1837
|
-
* Uses full-width comma (,) as separator to avoid conflicts
|
|
1838
|
-
* Strings cannot contain the full-width comma character
|
|
1839
|
-
*/
|
|
1840
|
-
class SimpleArray {
|
|
1841
|
-
static { this.SEPARATOR = ','; } // U+FF0C Full-width comma
|
|
1842
|
-
/**
|
|
1843
|
-
* Serialize array to string for attribute storage
|
|
1844
|
-
*/
|
|
1845
|
-
static serialize(arr) {
|
|
1846
|
-
if (!Array.isArray(arr))
|
|
1847
|
-
return '';
|
|
1848
|
-
return arr.map(item => {
|
|
1849
|
-
if (typeof item === 'string') {
|
|
1850
|
-
// Validate string doesn't contain our separator
|
|
1851
|
-
if (item.includes(SimpleArray.SEPARATOR)) {
|
|
1852
|
-
throw new Error(`SimpleArray strings cannot contain the character "${SimpleArray.SEPARATOR}" (U+FF0C)`);
|
|
1853
|
-
}
|
|
1854
|
-
return item;
|
|
1855
|
-
}
|
|
1856
|
-
else if (typeof item === 'number' || typeof item === 'boolean') {
|
|
1857
|
-
return String(item);
|
|
1858
|
-
}
|
|
1859
|
-
else {
|
|
1860
|
-
throw new Error(`SimpleArray only supports string, number, and boolean types. Got: ${typeof item}`);
|
|
1861
|
-
}
|
|
1862
|
-
}).join(SimpleArray.SEPARATOR);
|
|
1863
|
-
}
|
|
1864
|
-
/**
|
|
1865
|
-
* Parse string from attribute back to array
|
|
1866
|
-
*/
|
|
1867
|
-
static parse(str) {
|
|
1868
|
-
if (str === null || str === undefined)
|
|
1869
|
-
return [];
|
|
1870
|
-
// Empty string should not be parsed as containing an empty string
|
|
1871
|
-
// since empty arrays don't get reflected (handled by the reflection logic)
|
|
1872
|
-
if (str === '')
|
|
1873
|
-
return [];
|
|
1874
|
-
return str.split(SimpleArray.SEPARATOR).map(item => {
|
|
1875
|
-
// Try to parse as number
|
|
1876
|
-
if (/^-?\d+\.?\d*$/.test(item)) {
|
|
1877
|
-
const num = Number(item);
|
|
1878
|
-
if (!isNaN(num))
|
|
1879
|
-
return num;
|
|
1880
|
-
}
|
|
1881
|
-
// Parse as boolean
|
|
1882
|
-
if (item === 'true')
|
|
1883
|
-
return true;
|
|
1884
|
-
if (item === 'false')
|
|
1885
|
-
return false;
|
|
1886
|
-
// Default to string
|
|
1887
|
-
return item;
|
|
1888
|
-
});
|
|
1889
|
-
}
|
|
1890
|
-
}
|
|
1891
1926
|
function watch(...propertyNames) {
|
|
1892
1927
|
return function (target, context) {
|
|
1893
1928
|
const methodName = context.name;
|
|
@@ -3190,8 +3225,7 @@ function Router(options) {
|
|
|
3190
3225
|
page,
|
|
3191
3226
|
initialize,
|
|
3192
3227
|
navigate,
|
|
3193
|
-
register
|
|
3194
|
-
context,
|
|
3228
|
+
register
|
|
3195
3229
|
};
|
|
3196
3230
|
}
|
|
3197
3231
|
|