snice 1.14.3 → 2.1.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.
Files changed (185) hide show
  1. package/bin/templates/base/tsconfig.json +5 -4
  2. package/components/accordion/demo.html +403 -0
  3. package/components/accordion/snice-accordion-item.css +85 -0
  4. package/components/accordion/snice-accordion-item.ts +226 -0
  5. package/components/accordion/snice-accordion.css +31 -0
  6. package/components/accordion/snice-accordion.ts +182 -0
  7. package/components/accordion/snice-accordion.types.ts +32 -0
  8. package/components/alert/demo.html +445 -0
  9. package/components/alert/snice-alert.css +195 -0
  10. package/components/alert/snice-alert.ts +141 -0
  11. package/components/alert/snice-alert.types.ts +12 -0
  12. package/components/avatar/demo.html +598 -0
  13. package/components/avatar/snice-avatar.css +131 -0
  14. package/components/avatar/snice-avatar.ts +136 -0
  15. package/components/avatar/snice-avatar.types.ts +13 -0
  16. package/components/badge/demo.html +523 -0
  17. package/components/badge/snice-badge.css +161 -0
  18. package/components/badge/snice-badge.ts +117 -0
  19. package/components/badge/snice-badge.types.ts +16 -0
  20. package/components/breadcrumbs/demo.html +404 -0
  21. package/components/breadcrumbs/snice-breadcrumbs.css +133 -0
  22. package/components/breadcrumbs/snice-breadcrumbs.ts +191 -0
  23. package/components/breadcrumbs/snice-breadcrumbs.types.ts +26 -0
  24. package/components/breadcrumbs/snice-crumb.ts +26 -0
  25. package/components/button/demo.html +42 -0
  26. package/components/button/snice-button.css +230 -0
  27. package/components/button/snice-button.ts +169 -0
  28. package/components/button/snice-button.types.ts +25 -0
  29. package/components/card/demo.html +525 -0
  30. package/components/card/snice-card.css +140 -0
  31. package/components/card/snice-card.ts +102 -0
  32. package/components/card/snice-card.types.ts +10 -0
  33. package/components/checkbox/demo.html +253 -0
  34. package/components/checkbox/snice-checkbox.css +164 -0
  35. package/components/checkbox/snice-checkbox.ts +223 -0
  36. package/components/checkbox/snice-checkbox.types.ts +22 -0
  37. package/components/chip/demo.html +383 -0
  38. package/components/chip/snice-chip.css +195 -0
  39. package/components/chip/snice-chip.ts +139 -0
  40. package/components/chip/snice-chip.types.ts +15 -0
  41. package/components/date-picker/README.md +233 -0
  42. package/components/date-picker/demo.html +191 -0
  43. package/components/date-picker/snice-date-picker.css +330 -0
  44. package/components/date-picker/snice-date-picker.ts +777 -0
  45. package/components/date-picker/snice-date-picker.types.ts +83 -0
  46. package/components/divider/demo.html +233 -0
  47. package/components/divider/snice-divider.css +155 -0
  48. package/components/divider/snice-divider.ts +69 -0
  49. package/components/divider/snice-divider.types.ts +15 -0
  50. package/components/drawer/demo.html +328 -0
  51. package/components/drawer/snice-drawer.css +476 -0
  52. package/components/drawer/snice-drawer.ts +287 -0
  53. package/components/drawer/snice-drawer.types.ts +17 -0
  54. package/components/global.d.ts +14 -0
  55. package/components/input/demo.html +303 -0
  56. package/components/input/snice-input.css +257 -0
  57. package/components/input/snice-input.ts +442 -0
  58. package/components/input/snice-input.types.ts +59 -0
  59. package/components/input/test.html +77 -0
  60. package/components/layout/README.md +260 -0
  61. package/components/layout/demo.html +538 -0
  62. package/components/layout/snice-layout-blog.css +129 -0
  63. package/components/layout/snice-layout-blog.ts +48 -0
  64. package/components/layout/snice-layout-card.css +104 -0
  65. package/components/layout/snice-layout-card.ts +35 -0
  66. package/components/layout/snice-layout-centered.css +51 -0
  67. package/components/layout/snice-layout-centered.ts +22 -0
  68. package/components/layout/snice-layout-dashboard.css +98 -0
  69. package/components/layout/snice-layout-dashboard.ts +45 -0
  70. package/components/layout/snice-layout-fullscreen.css +72 -0
  71. package/components/layout/snice-layout-fullscreen.ts +34 -0
  72. package/components/layout/snice-layout-landing.css +92 -0
  73. package/components/layout/snice-layout-landing.ts +47 -0
  74. package/components/layout/snice-layout-minimal.css +16 -0
  75. package/components/layout/snice-layout-minimal.ts +19 -0
  76. package/components/layout/snice-layout-sidebar.css +117 -0
  77. package/components/layout/snice-layout-sidebar.ts +48 -0
  78. package/components/layout/snice-layout-split.css +103 -0
  79. package/components/layout/snice-layout-split.ts +29 -0
  80. package/components/layout/snice-layout.css +72 -0
  81. package/components/layout/snice-layout.ts +35 -0
  82. package/components/layout/snice-layout.types.ts +5 -0
  83. package/components/login/demo-auth-controller.ts +185 -0
  84. package/components/login/demo.html +470 -0
  85. package/components/login/snice-login.css +204 -0
  86. package/components/login/snice-login.ts +337 -0
  87. package/components/login/snice-login.types.ts +34 -0
  88. package/components/modal/demo.html +291 -0
  89. package/components/modal/snice-modal.css +203 -0
  90. package/components/modal/snice-modal.ts +233 -0
  91. package/components/modal/snice-modal.types.ts +21 -0
  92. package/components/pagination/demo.html +395 -0
  93. package/components/pagination/snice-pagination.ts +333 -0
  94. package/components/pagination/snice-pagination.types.ts +21 -0
  95. package/components/progress/demo.html +510 -0
  96. package/components/progress/snice-progress.css +267 -0
  97. package/components/progress/snice-progress.ts +247 -0
  98. package/components/progress/snice-progress.types.ts +19 -0
  99. package/components/radio/demo.html +287 -0
  100. package/components/radio/snice-radio.css +171 -0
  101. package/components/radio/snice-radio.ts +218 -0
  102. package/components/radio/snice-radio.types.ts +21 -0
  103. package/components/select/demo.html +511 -0
  104. package/components/select/snice-option.ts +52 -0
  105. package/components/select/snice-option.types.ts +14 -0
  106. package/components/select/snice-select.css +392 -0
  107. package/components/select/snice-select.ts +796 -0
  108. package/components/select/snice-select.types.ts +55 -0
  109. package/components/skeleton/demo.html +514 -0
  110. package/components/skeleton/snice-skeleton.css +109 -0
  111. package/components/skeleton/snice-skeleton.ts +126 -0
  112. package/components/skeleton/snice-skeleton.types.ts +11 -0
  113. package/components/switch/demo.html +284 -0
  114. package/components/switch/snice-switch.css +221 -0
  115. package/components/switch/snice-switch.ts +229 -0
  116. package/components/switch/snice-switch.types.ts +23 -0
  117. package/components/symbols.ts +23 -0
  118. package/components/table/demo-table-controller.ts +100 -0
  119. package/components/table/demo.html +480 -0
  120. package/components/table/snice-cell-boolean.ts +112 -0
  121. package/components/table/snice-cell-date.ts +210 -0
  122. package/components/table/snice-cell-duration.ts +91 -0
  123. package/components/table/snice-cell-filesize.ts +90 -0
  124. package/components/table/snice-cell-number.ts +165 -0
  125. package/components/table/snice-cell-progress.ts +83 -0
  126. package/components/table/snice-cell-rating.ts +82 -0
  127. package/components/table/snice-cell-sparkline.ts +253 -0
  128. package/components/table/snice-cell-text.ts +125 -0
  129. package/components/table/snice-cell.css +296 -0
  130. package/components/table/snice-cell.ts +473 -0
  131. package/components/table/snice-column.ts +353 -0
  132. package/components/table/snice-header.css +243 -0
  133. package/components/table/snice-header.ts +261 -0
  134. package/components/table/snice-progress.ts +66 -0
  135. package/components/table/snice-rating.ts +45 -0
  136. package/components/table/snice-row.css +255 -0
  137. package/components/table/snice-row.ts +331 -0
  138. package/components/table/snice-table.css +241 -0
  139. package/components/table/snice-table.ts +737 -0
  140. package/components/table/snice-table.types.ts +158 -0
  141. package/components/tabs/demo.html +487 -0
  142. package/components/tabs/snice-tab-panel.css +264 -0
  143. package/components/tabs/snice-tab-panel.ts +47 -0
  144. package/components/tabs/snice-tab.css +96 -0
  145. package/components/tabs/snice-tab.ts +65 -0
  146. package/components/tabs/snice-tabs.css +189 -0
  147. package/components/tabs/snice-tabs.ts +332 -0
  148. package/components/tabs/snice-tabs.types.ts +28 -0
  149. package/components/theme/theme.css +234 -0
  150. package/components/toast/demo.html +329 -0
  151. package/components/toast/snice-toast-container.ts +256 -0
  152. package/components/toast/snice-toast.css +213 -0
  153. package/components/toast/snice-toast.ts +276 -0
  154. package/components/toast/snice-toast.types.ts +35 -0
  155. package/components/tooltip/demo.html +350 -0
  156. package/components/tooltip/snice-tooltip-portal.css +79 -0
  157. package/components/tooltip/snice-tooltip.css +117 -0
  158. package/components/tooltip/snice-tooltip.ts +612 -0
  159. package/components/tooltip/snice-tooltip.types.ts +32 -0
  160. package/components/transitions.ts +94 -0
  161. package/components/tsconfig.json +18 -0
  162. package/dist/index.cjs +441 -329
  163. package/dist/index.cjs.map +1 -1
  164. package/dist/index.cjs.min.map +1 -1
  165. package/dist/index.esm.js +441 -329
  166. package/dist/index.esm.js.map +1 -1
  167. package/dist/index.esm.min.js +3 -3
  168. package/dist/index.esm.min.js.map +1 -1
  169. package/dist/index.iife.js +441 -329
  170. package/dist/index.iife.js.map +1 -1
  171. package/dist/index.iife.min.js +3 -3
  172. package/dist/index.iife.min.js.map +1 -1
  173. package/dist/symbols.esm.js +1 -1
  174. package/dist/transitions.esm.js +1 -1
  175. package/dist/types/controller.d.ts +1 -1
  176. package/dist/types/element.d.ts +10 -10
  177. package/dist/types/events.d.ts +2 -2
  178. package/dist/types/index.d.ts +1 -1
  179. package/dist/types/observe.d.ts +1 -1
  180. package/dist/types/request-response.d.ts +2 -3
  181. package/dist/types/router.d.ts +1 -1
  182. package/package.json +9 -3
  183. package/dist/index.cjs.min +0 -15
  184. package/dist/symbols.cjs +0 -103
  185. package/dist/transitions.cjs +0 -219
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * snice v1.14.2
2
+ * snice v1.14.3
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.
@@ -83,24 +83,27 @@ var Snice = (function (exports) {
83
83
  selector = undefined;
84
84
  opts = selectorOrOptions;
85
85
  }
86
- return function (target, propertyKey, descriptor) {
87
- // Store event handler metadata
88
- if (!target[ON_HANDLERS]) {
89
- target[ON_HANDLERS] = [];
90
- }
91
- // Normalize to array and expand at decoration time
92
- const eventNames = Array.isArray(eventName) ? eventName : [eventName];
93
- // Create a handler entry for each event
94
- for (const event of eventNames) {
95
- target[ON_HANDLERS].push({
96
- eventName: event,
97
- selector,
98
- methodName: propertyKey,
99
- method: descriptor.value,
100
- options: opts
101
- });
102
- }
103
- return descriptor;
86
+ return function (target, context) {
87
+ const propertyKey = context.name;
88
+ context.addInitializer(function () {
89
+ const constructor = this.constructor;
90
+ // Store event handler metadata
91
+ if (!constructor.prototype[ON_HANDLERS]) {
92
+ constructor.prototype[ON_HANDLERS] = [];
93
+ }
94
+ // Normalize to array and expand at decoration time
95
+ const eventNames = Array.isArray(eventName) ? eventName : [eventName];
96
+ // Create a handler entry for each event
97
+ for (const event of eventNames) {
98
+ constructor.prototype[ON_HANDLERS].push({
99
+ eventName: event,
100
+ selector,
101
+ methodName: propertyKey,
102
+ method: target,
103
+ options: opts
104
+ });
105
+ }
106
+ });
104
107
  };
105
108
  }
106
109
  // Helper to setup event handlers for elements
@@ -294,13 +297,12 @@ var Snice = (function (exports) {
294
297
  * @param options Optional configuration extending EventInit
295
298
  */
296
299
  function dispatch(eventName, options) {
297
- return function (_target, _propertyKey, descriptor) {
298
- const originalMethod = descriptor.value;
300
+ return function (originalMethod, _context) {
299
301
  // Create timing wrappers for dispatch
300
302
  let debounceTimeout;
301
303
  let throttleLastCall = 0;
302
304
  let throttleTimeout;
303
- descriptor.value = function (...args) {
305
+ return function (...args) {
304
306
  // Call the original method
305
307
  const result = originalMethod.apply(this, args);
306
308
  // Helper to dispatch the event
@@ -355,7 +357,6 @@ var Snice = (function (exports) {
355
357
  timedDispatch(result);
356
358
  return result;
357
359
  };
358
- return descriptor;
359
360
  };
360
361
  }
361
362
 
@@ -382,27 +383,30 @@ var Snice = (function (exports) {
382
383
  selector = undefined;
383
384
  opts = selectorOrOptions;
384
385
  }
385
- return function (target, propertyKey, descriptor) {
386
- // Store observer metadata
387
- if (!target[OBSERVERS]) {
388
- target[OBSERVERS] = [];
389
- }
390
- // Normalize to array
391
- const observeTargets = Array.isArray(observeTarget) ? observeTarget : [observeTarget];
392
- // Create an observer entry for each target
393
- for (const targetString of observeTargets) {
394
- // Parse the observation type from the observeTarget string
395
- const [type, ...modifiers] = targetString.split(':');
396
- target[OBSERVERS].push({
397
- type,
398
- target: modifiers.join(':'), // Rejoin for media queries or mutation types
399
- selector,
400
- methodName: propertyKey,
401
- method: descriptor.value,
402
- options: opts
403
- });
404
- }
405
- return descriptor;
386
+ return function (target, context) {
387
+ const propertyKey = context.name;
388
+ context.addInitializer(function () {
389
+ const constructor = this.constructor;
390
+ // Store observer metadata
391
+ if (!constructor.prototype[OBSERVERS]) {
392
+ constructor.prototype[OBSERVERS] = [];
393
+ }
394
+ // Normalize to array
395
+ const observeTargets = Array.isArray(observeTarget) ? observeTarget : [observeTarget];
396
+ // Create an observer entry for each target
397
+ for (const targetString of observeTargets) {
398
+ // Parse the observation type from the observeTarget string
399
+ const [type, ...modifiers] = targetString.split(':');
400
+ constructor.prototype[OBSERVERS].push({
401
+ type,
402
+ target: modifiers.join(':'), // Rejoin for media queries or mutation types
403
+ selector,
404
+ methodName: propertyKey,
405
+ method: target,
406
+ options: opts
407
+ });
408
+ }
409
+ });
406
410
  };
407
411
  }
408
412
  // Helper to setup observers for elements
@@ -695,13 +699,12 @@ var Snice = (function (exports) {
695
699
  * @param options Optional configuration
696
700
  */
697
701
  function request(requestName, options) {
698
- return function (_target, _propertyKey, descriptor) {
699
- const originalMethod = descriptor.value;
702
+ return function (originalMethod, _context) {
700
703
  // Create timing variables for debounce/throttle
701
704
  let debounceTimeout;
702
705
  let throttleLastCall = 0;
703
706
  let throttleTimeout;
704
- descriptor.value = async function (...args) {
707
+ return async function (...args) {
705
708
  const actualRequest = async () => {
706
709
  // @request always acts as requester (client side)
707
710
  const responseTimeout = options?.timeout ?? 120000; // Default 2 minute timeout
@@ -825,7 +828,6 @@ var Snice = (function (exports) {
825
828
  // No timing applied, execute immediately
826
829
  return actualRequest();
827
830
  };
828
- return descriptor;
829
831
  };
830
832
  }
831
833
  /**
@@ -835,20 +837,22 @@ var Snice = (function (exports) {
835
837
  * @param options Optional configuration
836
838
  */
837
839
  function respond(requestName, options) {
838
- return function (target, propertyKey, descriptor) {
839
- const originalMethod = descriptor.value;
840
- // Store response metadata on the prototype
841
- // This will be picked up by setupResponseHandlers
842
- if (!target[CHANNEL_HANDLERS]) {
843
- target[CHANNEL_HANDLERS] = [];
844
- }
845
- target[CHANNEL_HANDLERS].push({
846
- channelName: requestName,
847
- methodName: propertyKey,
848
- method: originalMethod,
849
- options: options
840
+ return function (target, context) {
841
+ const propertyKey = context.name;
842
+ context.addInitializer(function () {
843
+ const constructor = this.constructor;
844
+ // Store response metadata on the prototype
845
+ // This will be picked up by setupResponseHandlers
846
+ if (!constructor.prototype[CHANNEL_HANDLERS]) {
847
+ constructor.prototype[CHANNEL_HANDLERS] = [];
848
+ }
849
+ constructor.prototype[CHANNEL_HANDLERS].push({
850
+ channelName: requestName,
851
+ methodName: propertyKey,
852
+ method: target,
853
+ options: options
854
+ });
850
855
  });
851
- return descriptor;
852
856
  };
853
857
  }
854
858
  // Helper to setup response handlers for elements and controllers
@@ -999,7 +1003,7 @@ var Snice = (function (exports) {
999
1003
  * @param name The name to register the controller under
1000
1004
  */
1001
1005
  function controller(name) {
1002
- return function (constructor) {
1006
+ return function (constructor, _context) {
1003
1007
  snice.controllerRegistry.set(name, constructor);
1004
1008
  // Mark as controller class for channel decorator detection
1005
1009
  constructor.prototype[IS_CONTROLLER_CLASS] = true;
@@ -1346,10 +1350,12 @@ var Snice = (function (exports) {
1346
1350
  // Mark that properties have been initialized
1347
1351
  this[PROPERTIES_INITIALIZED] = true;
1348
1352
  // Reflect properties that were explicitly set before connection
1349
- // but skip default values that were never explicitly set
1350
- if (properties && this[EXPLICITLY_SET_PROPERTIES]) {
1353
+ // AND also reflect initial values that have reflect: true
1354
+ if (properties) {
1351
1355
  for (const [propName, propOptions] of properties) {
1352
- if (propOptions.reflect && this[EXPLICITLY_SET_PROPERTIES].has(propName) && propName in this[PROPERTY_VALUES]) {
1356
+ const wasExplicitlySet = this[EXPLICITLY_SET_PROPERTIES] && this[EXPLICITLY_SET_PROPERTIES].has(propName);
1357
+ const hasInitialValue = propName in this[PROPERTY_VALUES];
1358
+ if (propOptions.reflect && hasInitialValue && (wasExplicitlySet || this[PROPERTY_VALUES][propName] !== undefined)) {
1353
1359
  const value = this[PROPERTY_VALUES][propName];
1354
1360
  const attributeName = typeof propOptions.attribute === 'string' ? propOptions.attribute : propName.toLowerCase();
1355
1361
  if (value !== null && value !== undefined && value !== false &&
@@ -1598,176 +1604,224 @@ var Snice = (function (exports) {
1598
1604
  };
1599
1605
  }
1600
1606
  function element(tagName) {
1601
- return function (constructor) {
1607
+ return function (constructor, context) {
1608
+ // Transfer metadata from context to constructor
1609
+ if (context.metadata && context.metadata[PROPERTIES]) {
1610
+ if (!constructor[PROPERTIES]) {
1611
+ constructor[PROPERTIES] = new Map();
1612
+ }
1613
+ for (const [key, value] of context.metadata[PROPERTIES]) {
1614
+ constructor[PROPERTIES].set(key, value);
1615
+ }
1616
+ }
1602
1617
  applyElementFunctionality(constructor);
1603
1618
  customElements.define(tagName, constructor);
1619
+ return constructor;
1604
1620
  };
1605
1621
  }
1606
1622
  function layout(tagName) {
1607
- return function (constructor) {
1623
+ return function (constructor, context) {
1624
+ // Transfer metadata from context to constructor
1625
+ if (context.metadata && context.metadata[PROPERTIES]) {
1626
+ if (!constructor[PROPERTIES]) {
1627
+ constructor[PROPERTIES] = new Map();
1628
+ }
1629
+ for (const [key, value] of context.metadata[PROPERTIES]) {
1630
+ constructor[PROPERTIES].set(key, value);
1631
+ }
1632
+ }
1608
1633
  applyElementFunctionality(constructor);
1609
1634
  customElements.define(tagName, constructor);
1635
+ return constructor;
1610
1636
  };
1611
1637
  }
1612
1638
  function property(options) {
1613
- return function (target, propertyKey) {
1614
- const constructor = target.constructor;
1615
- // Warn about problematic reflection usage
1639
+ return function (_value, context) {
1640
+ const propertyKey = context.name;
1641
+ // Use metadata to store property information at decoration time
1642
+ if (!context.metadata) {
1643
+ context.metadata = {};
1644
+ }
1645
+ if (!context.metadata[PROPERTIES]) {
1646
+ context.metadata[PROPERTIES] = new Map();
1647
+ }
1648
+ context.metadata[PROPERTIES].set(propertyKey, options || {});
1649
+ // Warn about problematic reflection usage at decoration time
1616
1650
  if (options?.reflect && options?.type === Array) {
1617
1651
  console.warn(`⚠️ Property '${propertyKey}' uses reflect:true with Array type.`);
1618
1652
  }
1619
1653
  if (options?.reflect && options?.type === Object) {
1620
1654
  console.warn(`⚠️ Property '${propertyKey}' uses reflect:true with Object type.`);
1621
1655
  }
1622
- if (!constructor[PROPERTIES]) {
1623
- constructor[PROPERTIES] = new Map();
1624
- }
1625
- constructor[PROPERTIES].set(propertyKey, options || {});
1626
- const descriptor = {
1627
- get() {
1628
- if (!this[PROPERTY_VALUES]) {
1629
- this[PROPERTY_VALUES] = {};
1630
- }
1631
- return this[PROPERTY_VALUES][propertyKey];
1632
- },
1633
- set(value) {
1634
- if (!this[PROPERTY_VALUES]) {
1635
- this[PROPERTY_VALUES] = {};
1636
- }
1637
- if (!this[EXPLICITLY_SET_PROPERTIES]) {
1638
- this[EXPLICITLY_SET_PROPERTIES] = new Set();
1639
- }
1640
- const oldValue = this[PROPERTY_VALUES][propertyKey];
1641
- // Don't update if value hasn't changed
1642
- if (oldValue === value)
1643
- return;
1644
- // Mark as explicitly set in these cases:
1645
- // 1. There was a previous value (normal property update)
1646
- // 2. This is during element construction and we have a non-null/non-undefined value
1647
- // (this handles default values declared in class properties)
1648
- const isInitialDefaultValue = oldValue === undefined && !this[PROPERTIES_INITIALIZED];
1649
- if (oldValue !== undefined || (isInitialDefaultValue && value !== null && value !== undefined)) {
1650
- this[EXPLICITLY_SET_PROPERTIES].add(propertyKey);
1651
- }
1652
- this[PROPERTY_VALUES][propertyKey] = value;
1653
- // Only reflect to attributes if:
1654
- // 1. Properties have been initialized from attributes
1655
- // 2. The property was explicitly set (not just default value)
1656
- // This prevents default values from creating attributes
1657
- if (options?.reflect && this.setAttribute && this[PROPERTIES_INITIALIZED] && this[EXPLICITLY_SET_PROPERTIES].has(propertyKey)) {
1658
- const attributeName = typeof options.attribute === 'string' ? options.attribute : propertyKey.toLowerCase();
1659
- if (value === null || value === undefined || value === false ||
1660
- (options?.type === SimpleArray && Array.isArray(value) && value.length === 0)) {
1661
- this.removeAttribute(attributeName);
1662
- }
1663
- else {
1664
- // Handle special types for reflection
1665
- let attributeValue;
1666
- if (value instanceof Date) {
1667
- attributeValue = value.toISOString();
1656
+ context.addInitializer(function () {
1657
+ // No longer need warnings here since they're at decoration time
1658
+ });
1659
+ // Return a field initializer function for new decorators
1660
+ return function (initialValue) {
1661
+ // Set up the property descriptor on first access
1662
+ if (!Object.hasOwnProperty.call(this.constructor.prototype, propertyKey)) {
1663
+ const descriptor = {
1664
+ get() {
1665
+ if (!this[PROPERTY_VALUES]) {
1666
+ this[PROPERTY_VALUES] = {};
1668
1667
  }
1669
- else if (typeof value === 'bigint') {
1670
- attributeValue = value.toString() + 'n';
1668
+ return this[PROPERTY_VALUES][propertyKey];
1669
+ },
1670
+ set(newValue) {
1671
+ if (!this[PROPERTY_VALUES]) {
1672
+ this[PROPERTY_VALUES] = {};
1671
1673
  }
1672
- else if (options?.type === SimpleArray && Array.isArray(value)) {
1673
- attributeValue = SimpleArray.serialize(value);
1674
+ if (!this[EXPLICITLY_SET_PROPERTIES]) {
1675
+ this[EXPLICITLY_SET_PROPERTIES] = new Set();
1674
1676
  }
1675
- else {
1676
- attributeValue = String(value);
1677
+ const oldValue = this[PROPERTY_VALUES][propertyKey];
1678
+ if (oldValue === newValue)
1679
+ return;
1680
+ const isInitialDefaultValue = oldValue === undefined && !this[PROPERTIES_INITIALIZED];
1681
+ if (oldValue !== undefined || (isInitialDefaultValue && newValue !== null && newValue !== undefined)) {
1682
+ this[EXPLICITLY_SET_PROPERTIES].add(propertyKey);
1677
1683
  }
1678
- this.setAttribute(attributeName, attributeValue);
1679
- }
1680
- }
1681
- // Call watchers for this property
1682
- const watchers = constructor[PROPERTY_WATCHERS];
1683
- if (watchers) {
1684
- // Call specific property watchers
1685
- if (watchers.has(propertyKey)) {
1686
- const propertyWatchers = watchers.get(propertyKey);
1687
- for (const watcher of propertyWatchers) {
1688
- try {
1689
- // Always pass oldValue, newValue, and propertyName
1690
- watcher.method.call(this, oldValue, value, propertyKey);
1684
+ this[PROPERTY_VALUES][propertyKey] = newValue;
1685
+ if (options?.reflect && this.setAttribute && this[PROPERTIES_INITIALIZED] && this[EXPLICITLY_SET_PROPERTIES].has(propertyKey)) {
1686
+ const attributeName = typeof options.attribute === 'string' ? options.attribute : propertyKey.toLowerCase();
1687
+ if (newValue === null || newValue === undefined || newValue === false ||
1688
+ (options?.type === SimpleArray && Array.isArray(newValue) && newValue.length === 0)) {
1689
+ this.removeAttribute(attributeName);
1691
1690
  }
1692
- catch (error) {
1693
- console.error(`Error in @watch('${propertyKey}') method ${watcher.methodName}:`, error);
1691
+ else {
1692
+ let attributeValue;
1693
+ if (newValue instanceof Date) {
1694
+ attributeValue = newValue.toISOString();
1695
+ }
1696
+ else if (typeof newValue === 'bigint') {
1697
+ attributeValue = newValue.toString() + 'n';
1698
+ }
1699
+ else if (options?.type === SimpleArray && Array.isArray(newValue)) {
1700
+ attributeValue = SimpleArray.serialize(newValue);
1701
+ }
1702
+ else {
1703
+ attributeValue = String(newValue);
1704
+ }
1705
+ this.setAttribute(attributeName, attributeValue);
1694
1706
  }
1695
1707
  }
1696
- }
1697
- // Call wildcard watchers (watching "*")
1698
- if (watchers.has('*')) {
1699
- const wildcardWatchers = watchers.get('*');
1700
- for (const watcher of wildcardWatchers) {
1701
- try {
1702
- // Same signature for consistency
1703
- watcher.method.call(this, oldValue, value, propertyKey);
1708
+ const constructor = this.constructor;
1709
+ const watchers = constructor[PROPERTY_WATCHERS];
1710
+ if (watchers) {
1711
+ if (watchers.has(propertyKey)) {
1712
+ const propertyWatchers = watchers.get(propertyKey);
1713
+ for (const watcher of propertyWatchers) {
1714
+ try {
1715
+ watcher.method.call(this, oldValue, newValue, propertyKey);
1716
+ }
1717
+ catch (error) {
1718
+ console.error(`Error in @watch('${propertyKey}') method ${watcher.methodName}:`, error);
1719
+ }
1720
+ }
1704
1721
  }
1705
- catch (error) {
1706
- console.error(`Error in @watch('*') method ${watcher.methodName}:`, error);
1722
+ if (watchers.has('*')) {
1723
+ const wildcardWatchers = watchers.get('*');
1724
+ for (const watcher of wildcardWatchers) {
1725
+ try {
1726
+ watcher.method.call(this, oldValue, newValue, propertyKey);
1727
+ }
1728
+ catch (error) {
1729
+ console.error(`Error in @watch('*') method ${watcher.methodName}:`, error);
1730
+ }
1731
+ }
1707
1732
  }
1708
1733
  }
1709
- }
1710
- }
1711
- // Call requestUpdate if available and value changed
1712
- if (this.requestUpdate) {
1713
- this.requestUpdate(propertyKey, oldValue);
1714
- }
1715
- },
1716
- enumerable: true,
1717
- configurable: true,
1734
+ if (this.requestUpdate) {
1735
+ this.requestUpdate(propertyKey, oldValue);
1736
+ }
1737
+ },
1738
+ configurable: true,
1739
+ enumerable: true
1740
+ };
1741
+ Object.defineProperty(this.constructor.prototype, propertyKey, descriptor);
1742
+ }
1743
+ // Initialize the property value
1744
+ if (!this[PROPERTY_VALUES]) {
1745
+ this[PROPERTY_VALUES] = {};
1746
+ }
1747
+ this[PROPERTY_VALUES][propertyKey] = initialValue;
1748
+ return initialValue;
1718
1749
  };
1719
- Object.defineProperty(target, propertyKey, descriptor);
1720
1750
  };
1721
1751
  }
1722
1752
  function query(selector, options = {}) {
1723
- return function (target, propertyKey) {
1753
+ return function (_value, context) {
1724
1754
  // Default to shadow DOM only
1725
1755
  const { light = false, shadow = true } = options;
1726
- Object.defineProperty(target, propertyKey, {
1727
- get() {
1728
- // Check if this is a controller using the symbol
1729
- const isController = this[IS_CONTROLLER_INSTANCE] === true;
1730
- const root = isController && this.element ? this.element : this;
1731
- // Query in specified contexts
1732
- let result = null;
1733
- if (shadow && root.shadowRoot) {
1734
- result = root.shadowRoot.querySelector(selector);
1735
- }
1736
- if (!result && light) {
1737
- result = root.querySelector(selector);
1738
- }
1739
- return result || null;
1740
- },
1741
- enumerable: true,
1742
- configurable: true,
1743
- });
1756
+ const propertyKey = context.name;
1757
+ // Return a field initializer function for new decorators
1758
+ return function (initialValue) {
1759
+ // Set up the property descriptor on first access
1760
+ if (!Object.hasOwnProperty.call(this.constructor.prototype, propertyKey)) {
1761
+ const descriptor = {
1762
+ get() {
1763
+ // Check if this is a controller using the symbol
1764
+ const isController = this[IS_CONTROLLER_INSTANCE] === true;
1765
+ const root = isController && this.element ? this.element : this;
1766
+ // Query in specified contexts
1767
+ let result = null;
1768
+ if (shadow && root.shadowRoot) {
1769
+ result = root.shadowRoot.querySelector(selector);
1770
+ }
1771
+ if (!result && light) {
1772
+ result = root.querySelector(selector);
1773
+ }
1774
+ return result || null;
1775
+ },
1776
+ set() {
1777
+ // Query results are read-only
1778
+ },
1779
+ configurable: true,
1780
+ enumerable: true
1781
+ };
1782
+ Object.defineProperty(this.constructor.prototype, propertyKey, descriptor);
1783
+ }
1784
+ return initialValue;
1785
+ };
1744
1786
  };
1745
1787
  }
1746
1788
  function queryAll(selector, options = {}) {
1747
- return function (target, propertyKey) {
1789
+ return function (_value, context) {
1748
1790
  // Default to shadow DOM only
1749
1791
  const { light = false, shadow = true } = options;
1750
- Object.defineProperty(target, propertyKey, {
1751
- get() {
1752
- // Check if this is a controller using the symbol
1753
- const isController = this[IS_CONTROLLER_INSTANCE] === true;
1754
- const root = isController && this.element ? this.element : this;
1755
- // Query in specified contexts and combine results
1756
- const results = [];
1757
- if (shadow && root.shadowRoot) {
1758
- const shadowResults = root.shadowRoot.querySelectorAll(selector);
1759
- results.push(...shadowResults);
1760
- }
1761
- if (light) {
1762
- const lightResults = root.querySelectorAll(selector);
1763
- results.push(...lightResults);
1764
- }
1765
- // Return a static NodeList-like object
1766
- return results;
1767
- },
1768
- enumerable: true,
1769
- configurable: true,
1770
- });
1792
+ const propertyKey = context.name;
1793
+ // Return a field initializer function for new decorators
1794
+ return function (initialValue) {
1795
+ // Set up the property descriptor on first access
1796
+ if (!Object.hasOwnProperty.call(this.constructor.prototype, propertyKey)) {
1797
+ const descriptor = {
1798
+ get() {
1799
+ // Check if this is a controller using the symbol
1800
+ const isController = this[IS_CONTROLLER_INSTANCE] === true;
1801
+ const root = isController && this.element ? this.element : this;
1802
+ // Query in specified contexts and combine results
1803
+ const results = [];
1804
+ if (shadow && root.shadowRoot) {
1805
+ const shadowResults = root.shadowRoot.querySelectorAll(selector);
1806
+ results.push(...shadowResults);
1807
+ }
1808
+ if (light) {
1809
+ const lightResults = root.querySelectorAll(selector);
1810
+ results.push(...lightResults);
1811
+ }
1812
+ // Return a static NodeList-like object
1813
+ return results;
1814
+ },
1815
+ set() {
1816
+ // Query results are read-only
1817
+ },
1818
+ configurable: true,
1819
+ enumerable: true
1820
+ };
1821
+ Object.defineProperty(this.constructor.prototype, propertyKey, descriptor);
1822
+ }
1823
+ return initialValue;
1824
+ };
1771
1825
  };
1772
1826
  }
1773
1827
  /**
@@ -1777,6 +1831,7 @@ var Snice = (function (exports) {
1777
1831
  * Strings cannot contain the full-width comma character
1778
1832
  */
1779
1833
  class SimpleArray {
1834
+ static { this.SEPARATOR = ','; } // U+FF0C Full-width comma
1780
1835
  /**
1781
1836
  * Serialize array to string for attribute storage
1782
1837
  */
@@ -1826,24 +1881,25 @@ var Snice = (function (exports) {
1826
1881
  });
1827
1882
  }
1828
1883
  }
1829
- SimpleArray.SEPARATOR = ','; // U+FF0C Full-width comma
1830
1884
  function watch(...propertyNames) {
1831
- return function (target, methodName, descriptor) {
1832
- const constructor = target.constructor;
1833
- if (!constructor[PROPERTY_WATCHERS]) {
1834
- constructor[PROPERTY_WATCHERS] = new Map();
1835
- }
1836
- // Store the watcher method for each property
1837
- for (const propertyName of propertyNames) {
1838
- if (!constructor[PROPERTY_WATCHERS].has(propertyName)) {
1839
- constructor[PROPERTY_WATCHERS].set(propertyName, []);
1885
+ return function (target, context) {
1886
+ const methodName = context.name;
1887
+ context.addInitializer(function () {
1888
+ const constructor = this.constructor;
1889
+ if (!constructor[PROPERTY_WATCHERS]) {
1890
+ constructor[PROPERTY_WATCHERS] = new Map();
1891
+ }
1892
+ // Store the watcher method for each property
1893
+ for (const propertyName of propertyNames) {
1894
+ if (!constructor[PROPERTY_WATCHERS].has(propertyName)) {
1895
+ constructor[PROPERTY_WATCHERS].set(propertyName, []);
1896
+ }
1897
+ constructor[PROPERTY_WATCHERS].get(propertyName).push({
1898
+ methodName,
1899
+ method: target
1900
+ });
1840
1901
  }
1841
- constructor[PROPERTY_WATCHERS].get(propertyName).push({
1842
- methodName,
1843
- method: descriptor.value
1844
- });
1845
- }
1846
- return descriptor;
1902
+ });
1847
1903
  };
1848
1904
  }
1849
1905
  /**
@@ -1851,46 +1907,57 @@ var Snice = (function (exports) {
1851
1907
  * The context is automatically provided to page components by the router
1852
1908
  */
1853
1909
  function context() {
1854
- return function (target, propertyKey) {
1855
- // Define property getter that returns the context
1856
- Object.defineProperty(target, propertyKey, {
1857
- get() {
1858
- // First check if context is stored directly on this element
1859
- if (this[ROUTER_CONTEXT] !== undefined) {
1860
- return this[ROUTER_CONTEXT];
1861
- }
1862
- // Otherwise, request context from parent page via event
1863
- const detail = { target: this };
1864
- const event = new CustomEvent('@context/request', {
1865
- bubbles: true,
1866
- cancelable: true,
1867
- detail
1868
- });
1869
- // Dispatch event and wait for response
1870
- // Check if this is a controller using the symbol
1871
- const isController = this[IS_CONTROLLER_INSTANCE] === true;
1872
- let targetElement = isController && this.element ? this.element : this;
1873
- // If element is null (e.g., controller was detached), can't get context
1874
- if (!targetElement || !targetElement.dispatchEvent) {
1875
- return undefined;
1876
- }
1877
- // If we're in shadow DOM, dispatch on the host element to ensure proper bubbling
1878
- if (targetElement.getRootNode && targetElement.getRootNode() instanceof ShadowRoot) {
1879
- const shadowRoot = targetElement.getRootNode();
1880
- targetElement = shadowRoot.host;
1881
- }
1882
- targetElement.dispatchEvent(event);
1883
- // Check if context was provided via the event
1884
- if (detail.context !== undefined) {
1885
- // Cache it for future use
1886
- this[ROUTER_CONTEXT] = detail.context;
1887
- return detail.context;
1888
- }
1889
- return undefined;
1890
- },
1891
- enumerable: true,
1892
- configurable: true
1893
- });
1910
+ return function (_value, context) {
1911
+ const propertyKey = context.name;
1912
+ // Return a field initializer function for new decorators
1913
+ return function (initialValue) {
1914
+ // Set up the property descriptor on first access
1915
+ if (!Object.hasOwnProperty.call(this.constructor.prototype, propertyKey)) {
1916
+ const descriptor = {
1917
+ get() {
1918
+ // First check if context is stored directly on this element
1919
+ if (this[ROUTER_CONTEXT] !== undefined) {
1920
+ return this[ROUTER_CONTEXT];
1921
+ }
1922
+ // Otherwise, request context from parent page via event
1923
+ const detail = { target: this };
1924
+ const event = new CustomEvent('@context/request', {
1925
+ bubbles: true,
1926
+ cancelable: true,
1927
+ detail
1928
+ });
1929
+ // Dispatch event and wait for response
1930
+ // Check if this is a controller using the symbol
1931
+ const isController = this[IS_CONTROLLER_INSTANCE] === true;
1932
+ let targetElement = isController && this.element ? this.element : this;
1933
+ // If element is null (e.g., controller was detached), can't get context
1934
+ if (!targetElement || !targetElement.dispatchEvent) {
1935
+ return undefined;
1936
+ }
1937
+ // If we're in shadow DOM, dispatch on the host element to ensure proper bubbling
1938
+ if (targetElement.getRootNode && targetElement.getRootNode() instanceof ShadowRoot) {
1939
+ const shadowRoot = targetElement.getRootNode();
1940
+ targetElement = shadowRoot.host;
1941
+ }
1942
+ targetElement.dispatchEvent(event);
1943
+ // Check if context was provided via the event
1944
+ if (detail.context !== undefined) {
1945
+ // Cache it for future use
1946
+ this[ROUTER_CONTEXT] = detail.context;
1947
+ return detail.context;
1948
+ }
1949
+ return undefined;
1950
+ },
1951
+ set() {
1952
+ // Context is read-only
1953
+ },
1954
+ configurable: true,
1955
+ enumerable: true
1956
+ };
1957
+ Object.defineProperty(this.constructor.prototype, propertyKey, descriptor);
1958
+ }
1959
+ return initialValue;
1960
+ };
1894
1961
  };
1895
1962
  }
1896
1963
  /**
@@ -1899,16 +1966,18 @@ var Snice = (function (exports) {
1899
1966
  * Supports async methods
1900
1967
  */
1901
1968
  function ready() {
1902
- return function (target, methodName, descriptor) {
1903
- const constructor = target.constructor;
1904
- if (!constructor[READY_HANDLERS]) {
1905
- constructor[READY_HANDLERS] = [];
1906
- }
1907
- constructor[READY_HANDLERS].push({
1908
- methodName,
1909
- method: descriptor.value
1969
+ return function (target, context) {
1970
+ const methodName = context.name;
1971
+ context.addInitializer(function () {
1972
+ const constructor = this.constructor;
1973
+ if (!constructor[READY_HANDLERS]) {
1974
+ constructor[READY_HANDLERS] = [];
1975
+ }
1976
+ constructor[READY_HANDLERS].push({
1977
+ methodName,
1978
+ method: target
1979
+ });
1910
1980
  });
1911
- return descriptor;
1912
1981
  };
1913
1982
  }
1914
1983
  /**
@@ -1916,16 +1985,18 @@ var Snice = (function (exports) {
1916
1985
  * Used for cleanup tasks when element is removed from DOM
1917
1986
  */
1918
1987
  function dispose() {
1919
- return function (target, methodName, descriptor) {
1920
- const constructor = target.constructor;
1921
- if (!constructor[DISPOSE_HANDLERS]) {
1922
- constructor[DISPOSE_HANDLERS] = [];
1923
- }
1924
- constructor[DISPOSE_HANDLERS].push({
1925
- methodName,
1926
- method: descriptor.value
1988
+ return function (target, context) {
1989
+ const methodName = context.name;
1990
+ context.addInitializer(function () {
1991
+ const constructor = this.constructor;
1992
+ if (!constructor[DISPOSE_HANDLERS]) {
1993
+ constructor[DISPOSE_HANDLERS] = [];
1994
+ }
1995
+ constructor[DISPOSE_HANDLERS].push({
1996
+ methodName,
1997
+ method: target
1998
+ });
1927
1999
  });
1928
- return descriptor;
1929
2000
  };
1930
2001
  }
1931
2002
  /**
@@ -1934,27 +2005,20 @@ var Snice = (function (exports) {
1934
2005
  * When the decorated method is called, it automatically re-renders its part
1935
2006
  */
1936
2007
  function part(partName, options = {}) {
1937
- return function (target, methodName, descriptor) {
1938
- const constructor = target.constructor;
1939
- // Handle case where descriptor might be undefined (TypeScript experimental decorators)
1940
- if (!descriptor) {
1941
- descriptor = Object.getOwnPropertyDescriptor(target, methodName) || {
1942
- value: target[methodName],
1943
- writable: true,
1944
- enumerable: true,
1945
- configurable: true
1946
- };
1947
- }
1948
- const originalMethod = descriptor.value;
1949
- if (!constructor[PARTS]) {
1950
- constructor[PARTS] = new Map();
1951
- }
1952
- constructor[PARTS].set(partName, {
1953
- methodName,
1954
- method: originalMethod
2008
+ return function (originalMethod, context) {
2009
+ const methodName = context.name;
2010
+ context.addInitializer(function () {
2011
+ const constructor = this.constructor;
2012
+ if (!constructor[PARTS]) {
2013
+ constructor[PARTS] = new Map();
2014
+ }
2015
+ constructor[PARTS].set(partName, {
2016
+ methodName,
2017
+ method: originalMethod
2018
+ });
1955
2019
  });
1956
- // Wrap the original method to automatically re-render the part when called
1957
- descriptor.value = async function (...args) {
2020
+ // Return wrapped method that automatically re-renders the part when called
2021
+ return function (...args) {
1958
2022
  // Initialize timers storage if not present
1959
2023
  if (!this[PART_TIMERS]) {
1960
2024
  this[PART_TIMERS] = new Map();
@@ -1968,58 +2032,96 @@ var Snice = (function (exports) {
1968
2032
  });
1969
2033
  }
1970
2034
  const timers = this[PART_TIMERS].get(partName);
1971
- // Create the render function
1972
- const renderPart = async () => {
1973
- // Call the original method to get the content
1974
- const result = originalMethod.apply(this, args);
1975
- const content = result instanceof Promise ? await result : result;
1976
- // Re-render the part if shadow DOM exists and content is defined
2035
+ // Call the original method first to get its result
2036
+ const result = originalMethod.apply(this, args);
2037
+ // Helper function to update DOM
2038
+ const updateDOM = (content) => {
1977
2039
  if (this.shadowRoot && content !== undefined) {
1978
2040
  const partElement = this.shadowRoot.querySelector(`[part="${partName}"]`);
1979
2041
  if (partElement) {
1980
2042
  partElement.innerHTML = content;
1981
2043
  }
1982
2044
  }
1983
- return content;
1984
2045
  };
1985
- // Handle debounce (only if positive value)
1986
- if (options.debounce !== undefined && options.debounce > 0) {
1987
- if (timers.debounceTimer) {
1988
- clearTimeout(timers.debounceTimer);
1989
- }
1990
- return new Promise((resolve) => {
2046
+ // Check if result is a Promise (async method)
2047
+ if (result instanceof Promise) {
2048
+ // Handle async method
2049
+ if (options.debounce !== undefined && options.debounce > 0) {
2050
+ // Debounce: defer DOM update, return original Promise
2051
+ if (timers.debounceTimer) {
2052
+ clearTimeout(timers.debounceTimer);
2053
+ }
1991
2054
  timers.debounceTimer = setTimeout(async () => {
1992
- const result = await renderPart();
1993
- resolve(result);
2055
+ const content = await result;
2056
+ updateDOM(content);
1994
2057
  }, options.debounce);
2058
+ return result;
2059
+ }
2060
+ if (options.throttle !== undefined && options.throttle > 0) {
2061
+ // Throttle: handle timing but return original Promise
2062
+ const now = Date.now();
2063
+ if (timers.lastThrottleCall === 0 || now - timers.lastThrottleCall >= options.throttle) {
2064
+ timers.lastThrottleCall = now;
2065
+ return result.then(content => {
2066
+ updateDOM(content);
2067
+ return content;
2068
+ });
2069
+ }
2070
+ else {
2071
+ if (!timers.throttleTimer) {
2072
+ const remainingTime = options.throttle - (now - timers.lastThrottleCall);
2073
+ timers.throttleTimer = setTimeout(async () => {
2074
+ timers.throttleTimer = null;
2075
+ timers.lastThrottleCall = Date.now();
2076
+ const content = await result;
2077
+ updateDOM(content);
2078
+ }, remainingTime);
2079
+ }
2080
+ return result;
2081
+ }
2082
+ }
2083
+ // No timing: update DOM after Promise resolves
2084
+ return result.then(content => {
2085
+ updateDOM(content);
2086
+ return content;
1995
2087
  });
1996
2088
  }
1997
- // Handle throttle (only if positive value)
1998
- if (options.throttle !== undefined && options.throttle > 0) {
1999
- const now = Date.now();
2000
- if (timers.lastThrottleCall === 0 || now - timers.lastThrottleCall >= options.throttle) {
2001
- timers.lastThrottleCall = now;
2002
- return await renderPart();
2089
+ else {
2090
+ // Handle sync method
2091
+ if (options.debounce !== undefined && options.debounce > 0) {
2092
+ // Debounce: defer DOM update, return result immediately
2093
+ if (timers.debounceTimer) {
2094
+ clearTimeout(timers.debounceTimer);
2095
+ }
2096
+ timers.debounceTimer = setTimeout(() => {
2097
+ updateDOM(result);
2098
+ }, options.debounce);
2099
+ return result;
2003
2100
  }
2004
- else {
2005
- // If throttled, schedule the next call if not already scheduled
2006
- if (!timers.throttleTimer) {
2007
- const remainingTime = options.throttle - (now - timers.lastThrottleCall);
2008
- timers.throttleTimer = setTimeout(async () => {
2009
- timers.throttleTimer = null;
2010
- timers.lastThrottleCall = Date.now();
2011
- await renderPart();
2012
- }, remainingTime);
2101
+ if (options.throttle !== undefined && options.throttle > 0) {
2102
+ // Throttle: handle timing for DOM updates
2103
+ const now = Date.now();
2104
+ if (timers.lastThrottleCall === 0 || now - timers.lastThrottleCall >= options.throttle) {
2105
+ timers.lastThrottleCall = now;
2106
+ updateDOM(result);
2107
+ }
2108
+ else {
2109
+ if (!timers.throttleTimer) {
2110
+ const remainingTime = options.throttle - (now - timers.lastThrottleCall);
2111
+ timers.throttleTimer = setTimeout(() => {
2112
+ timers.throttleTimer = null;
2113
+ timers.lastThrottleCall = Date.now();
2114
+ updateDOM(result);
2115
+ }, remainingTime);
2116
+ }
2013
2117
  }
2014
- // For throttled calls, don't execute the original method, just return undefined
2015
- // The actual render will happen in the scheduled timeout
2016
- return undefined;
2118
+ return result;
2017
2119
  }
2120
+ // No timing: update DOM immediately
2121
+ updateDOM(result);
2122
+ return result;
2018
2123
  }
2019
- // No throttle/debounce - render immediately
2020
- return await renderPart();
2021
2124
  };
2022
- return descriptor;
2023
2125
  };
2024
2126
  }
2025
2127
 
@@ -2791,7 +2893,16 @@ var Snice = (function (exports) {
2791
2893
  * @returns A decorator function to apply to a custom element class.
2792
2894
  */
2793
2895
  function page(pageOptions) {
2794
- return function (constructor) {
2896
+ return function (constructor, context) {
2897
+ // Transfer metadata from context to constructor (for new decorators)
2898
+ if (context.metadata && context.metadata[PROPERTIES]) {
2899
+ if (!constructor[PROPERTIES]) {
2900
+ constructor[PROPERTIES] = new Map();
2901
+ }
2902
+ for (const [key, value] of context.metadata[PROPERTIES]) {
2903
+ constructor[PROPERTIES].set(key, value);
2904
+ }
2905
+ }
2795
2906
  // Apply all element functionality (properties, queries, watchers, controllers, etc.)
2796
2907
  applyElementFunctionality(constructor);
2797
2908
  // Store transition config on constructor for later use
@@ -2831,6 +2942,7 @@ var Snice = (function (exports) {
2831
2942
  customElements.define(pageOptions.tag, constructor);
2832
2943
  // Register the routes with guards and layout
2833
2944
  pageOptions.routes.forEach(route => register(route, pageOptions.tag, pageOptions.transition, pageOptions.guards, pageOptions.layout));
2945
+ return constructor;
2834
2946
  };
2835
2947
  }
2836
2948
  /**