snice 2.5.4 → 3.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 (323) hide show
  1. package/README.md +501 -882
  2. package/bin/templates/base/src/components/counter-button.ts +13 -26
  3. package/bin/templates/base/src/controllers/counter-controller.ts +3 -3
  4. package/dist/components/accordion/snice-accordion-item.d.ts +4 -5
  5. package/dist/components/accordion/snice-accordion-item.js +37 -39
  6. package/dist/components/accordion/snice-accordion-item.js.map +1 -1
  7. package/dist/components/accordion/snice-accordion.d.ts +5 -11
  8. package/dist/components/accordion/snice-accordion.js +51 -52
  9. package/dist/components/accordion/snice-accordion.js.map +1 -1
  10. package/dist/components/alert/snice-alert.d.ts +2 -6
  11. package/dist/components/alert/snice-alert.js +41 -56
  12. package/dist/components/alert/snice-alert.js.map +1 -1
  13. package/dist/components/avatar/snice-avatar.d.ts +2 -6
  14. package/dist/components/avatar/snice-avatar.js +64 -71
  15. package/dist/components/avatar/snice-avatar.js.map +1 -1
  16. package/dist/components/badge/snice-badge.d.ts +2 -3
  17. package/dist/components/badge/snice-badge.js +22 -23
  18. package/dist/components/badge/snice-badge.js.map +1 -1
  19. package/dist/components/breadcrumbs/snice-breadcrumbs.d.ts +5 -12
  20. package/dist/components/breadcrumbs/snice-breadcrumbs.js +88 -89
  21. package/dist/components/breadcrumbs/snice-breadcrumbs.js.map +1 -1
  22. package/dist/components/button/snice-button.d.ts +3 -7
  23. package/dist/components/button/snice-button.js +37 -58
  24. package/dist/components/button/snice-button.js.map +1 -1
  25. package/dist/components/card/snice-card.d.ts +5 -8
  26. package/dist/components/card/snice-card.js +71 -56
  27. package/dist/components/card/snice-card.js.map +1 -1
  28. package/dist/components/checkbox/snice-checkbox.d.ts +4 -13
  29. package/dist/components/checkbox/snice-checkbox.js +66 -137
  30. package/dist/components/checkbox/snice-checkbox.js.map +1 -1
  31. package/dist/components/chip/snice-chip.d.ts +5 -11
  32. package/dist/components/chip/snice-chip.js +44 -47
  33. package/dist/components/chip/snice-chip.js.map +1 -1
  34. package/dist/components/date-picker/snice-date-picker.d.ts +11 -11
  35. package/dist/components/date-picker/snice-date-picker.js +134 -133
  36. package/dist/components/date-picker/snice-date-picker.js.map +1 -1
  37. package/dist/components/divider/snice-divider.d.ts +2 -4
  38. package/dist/components/divider/snice-divider.js +14 -22
  39. package/dist/components/divider/snice-divider.js.map +1 -1
  40. package/dist/components/drawer/snice-drawer.d.ts +4 -4
  41. package/dist/components/drawer/snice-drawer.js +25 -19
  42. package/dist/components/drawer/snice-drawer.js.map +1 -1
  43. package/dist/components/input/snice-input.d.ts +8 -6
  44. package/dist/components/input/snice-input.js +122 -105
  45. package/dist/components/input/snice-input.js.map +1 -1
  46. package/dist/components/layout/snice-layout-blog.d.ts +4 -4
  47. package/dist/components/layout/snice-layout-blog.js +21 -19
  48. package/dist/components/layout/snice-layout-blog.js.map +1 -1
  49. package/dist/components/layout/snice-layout-card.d.ts +2 -2
  50. package/dist/components/layout/snice-layout-card.js +16 -9
  51. package/dist/components/layout/snice-layout-card.js.map +1 -1
  52. package/dist/components/layout/snice-layout-centered.d.ts +2 -2
  53. package/dist/components/layout/snice-layout-centered.js +14 -7
  54. package/dist/components/layout/snice-layout-centered.js.map +1 -1
  55. package/dist/components/layout/snice-layout-dashboard.d.ts +5 -5
  56. package/dist/components/layout/snice-layout-dashboard.js +38 -30
  57. package/dist/components/layout/snice-layout-dashboard.js.map +1 -1
  58. package/dist/components/layout/snice-layout-fullscreen.d.ts +2 -2
  59. package/dist/components/layout/snice-layout-fullscreen.js +17 -10
  60. package/dist/components/layout/snice-layout-fullscreen.js.map +1 -1
  61. package/dist/components/layout/snice-layout-landing.d.ts +4 -4
  62. package/dist/components/layout/snice-layout-landing.js +21 -19
  63. package/dist/components/layout/snice-layout-landing.js.map +1 -1
  64. package/dist/components/layout/snice-layout-minimal.d.ts +2 -2
  65. package/dist/components/layout/snice-layout-minimal.js +17 -6
  66. package/dist/components/layout/snice-layout-minimal.js.map +1 -1
  67. package/dist/components/layout/snice-layout-sidebar.d.ts +5 -4
  68. package/dist/components/layout/snice-layout-sidebar.js +42 -20
  69. package/dist/components/layout/snice-layout-sidebar.js.map +1 -1
  70. package/dist/components/layout/snice-layout-split.d.ts +2 -2
  71. package/dist/components/layout/snice-layout-split.js +14 -7
  72. package/dist/components/layout/snice-layout-split.js.map +1 -1
  73. package/dist/components/layout/snice-layout.d.ts +4 -4
  74. package/dist/components/layout/snice-layout.js +16 -10
  75. package/dist/components/layout/snice-layout.js.map +1 -1
  76. package/dist/components/login/snice-login.d.ts +6 -11
  77. package/dist/components/login/snice-login.js +97 -71
  78. package/dist/components/login/snice-login.js.map +1 -1
  79. package/dist/components/modal/snice-modal.d.ts +5 -9
  80. package/dist/components/modal/snice-modal.js +47 -78
  81. package/dist/components/modal/snice-modal.js.map +1 -1
  82. package/dist/components/nav/snice-nav.d.ts +13 -7
  83. package/dist/components/nav/snice-nav.js +191 -100
  84. package/dist/components/nav/snice-nav.js.map +1 -1
  85. package/dist/components/nav/snice-nav.types.d.ts +3 -3
  86. package/dist/components/pagination/snice-pagination.d.ts +6 -7
  87. package/dist/components/pagination/snice-pagination.js +94 -81
  88. package/dist/components/pagination/snice-pagination.js.map +1 -1
  89. package/dist/components/progress/snice-progress.d.ts +2 -7
  90. package/dist/components/progress/snice-progress.js +41 -98
  91. package/dist/components/progress/snice-progress.js.map +1 -1
  92. package/dist/components/radio/snice-radio.d.ts +4 -4
  93. package/dist/components/radio/snice-radio.js +52 -44
  94. package/dist/components/radio/snice-radio.js.map +1 -1
  95. package/dist/components/select/snice-option.d.ts +2 -1
  96. package/dist/components/select/snice-option.js +12 -5
  97. package/dist/components/select/snice-option.js.map +1 -1
  98. package/dist/components/select/snice-select.d.ts +9 -21
  99. package/dist/components/select/snice-select.js +98 -170
  100. package/dist/components/select/snice-select.js.map +1 -1
  101. package/dist/components/skeleton/snice-skeleton.d.ts +2 -6
  102. package/dist/components/skeleton/snice-skeleton.js +18 -49
  103. package/dist/components/skeleton/snice-skeleton.js.map +1 -1
  104. package/dist/components/snice-cell-BLFVdxPp.js +4 -0
  105. package/dist/components/snice-cell-BLFVdxPp.js.map +1 -0
  106. package/dist/components/switch/snice-switch.d.ts +2 -2
  107. package/dist/components/switch/snice-switch.js +38 -26
  108. package/dist/components/switch/snice-switch.js.map +1 -1
  109. package/dist/components/table/snice-cell-actions.d.ts +24 -0
  110. package/dist/components/table/snice-cell-actions.js +149 -0
  111. package/dist/components/table/snice-cell-actions.js.map +1 -0
  112. package/dist/components/table/snice-cell-boolean.d.ts +2 -2
  113. package/dist/components/table/snice-cell-boolean.js +13 -7
  114. package/dist/components/table/snice-cell-boolean.js.map +1 -1
  115. package/dist/components/table/snice-cell-color.d.ts +18 -0
  116. package/dist/components/table/snice-cell-color.js +149 -0
  117. package/dist/components/table/snice-cell-color.js.map +1 -0
  118. package/dist/components/table/snice-cell-currency.d.ts +24 -0
  119. package/dist/components/table/snice-cell-currency.js +235 -0
  120. package/dist/components/table/snice-cell-currency.js.map +1 -0
  121. package/dist/components/table/snice-cell-date.d.ts +2 -2
  122. package/dist/components/table/snice-cell-date.js +14 -8
  123. package/dist/components/table/snice-cell-date.js.map +1 -1
  124. package/dist/components/table/snice-cell-duration.d.ts +2 -2
  125. package/dist/components/table/snice-cell-duration.js +12 -6
  126. package/dist/components/table/snice-cell-duration.js.map +1 -1
  127. package/dist/components/table/snice-cell-email.d.ts +15 -0
  128. package/dist/components/table/snice-cell-email.js +125 -0
  129. package/dist/components/table/snice-cell-email.js.map +1 -0
  130. package/dist/components/table/snice-cell-filesize.d.ts +2 -2
  131. package/dist/components/table/snice-cell-filesize.js +12 -6
  132. package/dist/components/table/snice-cell-filesize.js.map +1 -1
  133. package/dist/components/table/snice-cell-image.d.ts +20 -0
  134. package/dist/components/table/snice-cell-image.js +162 -0
  135. package/dist/components/table/snice-cell-image.js.map +1 -0
  136. package/dist/components/table/snice-cell-json.d.ts +20 -0
  137. package/dist/components/table/snice-cell-json.js +186 -0
  138. package/dist/components/table/snice-cell-json.js.map +1 -0
  139. package/dist/components/table/snice-cell-link.d.ts +17 -0
  140. package/dist/components/table/snice-cell-link.js +142 -0
  141. package/dist/components/table/snice-cell-link.js.map +1 -0
  142. package/dist/components/table/snice-cell-location.d.ts +19 -0
  143. package/dist/components/table/snice-cell-location.js +185 -0
  144. package/dist/components/table/snice-cell-location.js.map +1 -0
  145. package/dist/components/table/snice-cell-number.d.ts +2 -2
  146. package/dist/components/table/snice-cell-number.js +12 -6
  147. package/dist/components/table/snice-cell-number.js.map +1 -1
  148. package/dist/components/table/snice-cell-percentage.d.ts +22 -0
  149. package/dist/components/table/snice-cell-percentage.js +208 -0
  150. package/dist/components/table/snice-cell-percentage.js.map +1 -0
  151. package/dist/components/table/snice-cell-phone.d.ts +18 -0
  152. package/dist/components/table/snice-cell-phone.js +153 -0
  153. package/dist/components/table/snice-cell-phone.js.map +1 -0
  154. package/dist/components/table/snice-cell-progress.d.ts +2 -2
  155. package/dist/components/table/snice-cell-progress.js +12 -6
  156. package/dist/components/table/snice-cell-progress.js.map +1 -1
  157. package/dist/components/table/snice-cell-rating.d.ts +2 -2
  158. package/dist/components/table/snice-cell-rating.js +12 -6
  159. package/dist/components/table/snice-cell-rating.js.map +1 -1
  160. package/dist/components/table/snice-cell-sparkline.d.ts +2 -2
  161. package/dist/components/table/snice-cell-sparkline.js +13 -7
  162. package/dist/components/table/snice-cell-sparkline.js.map +1 -1
  163. package/dist/components/table/snice-cell-status.d.ts +17 -0
  164. package/dist/components/table/snice-cell-status.js +144 -0
  165. package/dist/components/table/snice-cell-status.js.map +1 -0
  166. package/dist/components/table/snice-cell-tag.d.ts +16 -0
  167. package/dist/components/table/snice-cell-tag.js +131 -0
  168. package/dist/components/table/snice-cell-tag.js.map +1 -0
  169. package/dist/components/table/snice-cell-text.d.ts +2 -2
  170. package/dist/components/table/snice-cell-text.js +14 -8
  171. package/dist/components/table/snice-cell-text.js.map +1 -1
  172. package/dist/components/table/snice-cell.d.ts +2 -2
  173. package/dist/components/table/snice-cell.js +12 -6
  174. package/dist/components/table/snice-cell.js.map +1 -1
  175. package/dist/components/table/snice-column.d.ts +1 -1
  176. package/dist/components/table/snice-column.js +6 -3
  177. package/dist/components/table/snice-column.js.map +1 -1
  178. package/dist/components/table/snice-header.d.ts +5 -5
  179. package/dist/components/table/snice-header.js +60 -50
  180. package/dist/components/table/snice-header.js.map +1 -1
  181. package/dist/components/table/snice-progress.d.ts +2 -2
  182. package/dist/components/table/snice-progress.js +18 -11
  183. package/dist/components/table/snice-progress.js.map +1 -1
  184. package/dist/components/table/snice-rating.d.ts +2 -2
  185. package/dist/components/table/snice-rating.js +15 -8
  186. package/dist/components/table/snice-rating.js.map +1 -1
  187. package/dist/components/table/snice-row.d.ts +17 -6
  188. package/dist/components/table/snice-row.js +95 -44
  189. package/dist/components/table/snice-row.js.map +1 -1
  190. package/dist/components/table/snice-table.d.ts +18 -10
  191. package/dist/components/table/snice-table.js +355 -173
  192. package/dist/components/table/snice-table.js.map +1 -1
  193. package/dist/components/table/snice-table.types.d.ts +101 -2
  194. package/dist/components/tabs/snice-tab-panel.d.ts +2 -2
  195. package/dist/components/tabs/snice-tab-panel.js +12 -6
  196. package/dist/components/tabs/snice-tab-panel.js.map +1 -1
  197. package/dist/components/tabs/snice-tab.d.ts +6 -5
  198. package/dist/components/tabs/snice-tab.js +36 -19
  199. package/dist/components/tabs/snice-tab.js.map +1 -1
  200. package/dist/components/tabs/snice-tabs.d.ts +5 -5
  201. package/dist/components/tabs/snice-tabs.js +38 -28
  202. package/dist/components/tabs/snice-tabs.js.map +1 -1
  203. package/dist/components/toast/snice-toast-container.d.ts +7 -7
  204. package/dist/components/toast/snice-toast-container.js +19 -12
  205. package/dist/components/toast/snice-toast-container.js.map +1 -1
  206. package/dist/components/toast/snice-toast.d.ts +3 -15
  207. package/dist/components/toast/snice-toast.js +49 -108
  208. package/dist/components/toast/snice-toast.js.map +1 -1
  209. package/dist/components/tooltip/snice-tooltip.d.ts +2 -2
  210. package/dist/components/tooltip/snice-tooltip.js +14 -7
  211. package/dist/components/tooltip/snice-tooltip.js.map +1 -1
  212. package/dist/context.d.ts +44 -0
  213. package/dist/element-ready.d.ts +40 -0
  214. package/dist/{types/element.d.ts → element.d.ts} +2 -8
  215. package/dist/{types/events.d.ts → events.d.ts} +0 -4
  216. package/dist/index.cjs +2589 -605
  217. package/dist/index.cjs.map +1 -1
  218. package/dist/index.d.ts +21 -0
  219. package/dist/index.esm.js +2568 -604
  220. package/dist/index.esm.js.map +1 -1
  221. package/dist/index.iife.js +2589 -605
  222. package/dist/index.iife.js.map +1 -1
  223. package/dist/method-decorators.d.ts +121 -0
  224. package/dist/on.d.ts +59 -0
  225. package/dist/parts.d.ts +159 -0
  226. package/dist/render-debug.d.ts +27 -0
  227. package/dist/render-tracker.d.ts +14 -0
  228. package/dist/render.d.ts +96 -0
  229. package/dist/symbols.cjs +163 -0
  230. package/dist/symbols.cjs.map +1 -1
  231. package/dist/{types/symbols.d.ts → symbols.d.ts} +22 -0
  232. package/dist/symbols.esm.js +27 -3
  233. package/dist/symbols.esm.js.map +1 -1
  234. package/dist/template.d.ts +100 -0
  235. package/dist/transitions.cjs +219 -0
  236. package/dist/transitions.esm.js +2 -2
  237. package/dist/types/context.d.ts +48 -0
  238. package/dist/types/element-options.d.ts +26 -0
  239. package/dist/types/index.d.ts +25 -9
  240. package/dist/types/nav-context.d.ts +19 -0
  241. package/dist/types/{types/on-options.d.ts → on-options.d.ts} +2 -0
  242. package/dist/types/{types/placard.d.ts → placard.d.ts} +0 -1
  243. package/docs/ai/README.md +17 -0
  244. package/docs/ai/api.md +175 -0
  245. package/docs/ai/architecture.md +160 -0
  246. package/docs/ai/components/accordion.md +174 -0
  247. package/docs/ai/components/alert.md +77 -0
  248. package/docs/ai/components/avatar.md +61 -0
  249. package/docs/ai/components/badge.md +69 -0
  250. package/docs/ai/components/breadcrumbs.md +74 -0
  251. package/docs/ai/components/button.md +75 -0
  252. package/docs/ai/components/card.md +61 -0
  253. package/docs/ai/components/checkbox.md +74 -0
  254. package/docs/ai/components/chip.md +73 -0
  255. package/docs/ai/components/date-picker.md +75 -0
  256. package/docs/ai/components/divider.md +66 -0
  257. package/docs/ai/components/drawer.md +80 -0
  258. package/docs/ai/components/input.md +111 -0
  259. package/docs/ai/components/login.md +109 -0
  260. package/docs/ai/components/modal.md +67 -0
  261. package/docs/ai/components/nav.md +76 -0
  262. package/docs/ai/components/pagination.md +55 -0
  263. package/docs/ai/components/progress.md +72 -0
  264. package/docs/ai/components/radio.md +79 -0
  265. package/docs/ai/components/select.md +92 -0
  266. package/docs/ai/components/skeleton.md +57 -0
  267. package/docs/ai/components/switch.md +53 -0
  268. package/docs/ai/components/table.md +227 -0
  269. package/docs/ai/components/tabs.md +83 -0
  270. package/docs/ai/components/toast.md +140 -0
  271. package/docs/ai/components/tooltip.md +146 -0
  272. package/docs/ai/patterns.md +244 -0
  273. package/docs/components/accordion.md +558 -0
  274. package/docs/components/drawer.md +602 -0
  275. package/docs/components/modal.md +558 -0
  276. package/docs/components/nav.md +239 -0
  277. package/docs/components/pagination.md +289 -0
  278. package/docs/components/select.md +599 -0
  279. package/docs/components/switch.md +354 -0
  280. package/docs/components/tabs.md +546 -0
  281. package/docs/components/toast.md +506 -0
  282. package/docs/components/tooltip.md +523 -0
  283. package/docs/controllers.md +744 -0
  284. package/docs/elements.md +855 -0
  285. package/docs/events.md +807 -0
  286. package/docs/migration-v2-to-v3.md +569 -0
  287. package/docs/observe.md +588 -0
  288. package/docs/placards.md +401 -0
  289. package/docs/request-response.md +852 -0
  290. package/docs/routing.md +1186 -0
  291. package/package.json +10 -11
  292. package/dist/components/snice-cell-C9N6yGxQ.js +0 -4
  293. package/dist/components/snice-cell-C9N6yGxQ.js.map +0 -1
  294. package/dist/types/types/index.d.ts +0 -23
  295. /package/dist/{types/controller.d.ts → controller.d.ts} +0 -0
  296. /package/dist/{types/global.d.ts → global.d.ts} +0 -0
  297. /package/dist/{types/observe.d.ts → observe.d.ts} +0 -0
  298. /package/dist/{types/request-response.d.ts → request-response.d.ts} +0 -0
  299. /package/dist/{types/router.d.ts → router.d.ts} +0 -0
  300. /package/dist/{types/testing.d.ts → testing.d.ts} +0 -0
  301. /package/dist/{types/transitions.d.ts → transitions.d.ts} +0 -0
  302. /package/dist/types/{types/adopted-options.d.ts → adopted-options.d.ts} +0 -0
  303. /package/dist/types/{types/app-context.d.ts → app-context.d.ts} +0 -0
  304. /package/dist/types/{types/dispatch-options.d.ts → dispatch-options.d.ts} +0 -0
  305. /package/dist/types/{types/guard.d.ts → guard.d.ts} +0 -0
  306. /package/dist/types/{types/i-controller.d.ts → i-controller.d.ts} +0 -0
  307. /package/dist/types/{types/moved-options.d.ts → moved-options.d.ts} +0 -0
  308. /package/dist/types/{types/observe-options.d.ts → observe-options.d.ts} +0 -0
  309. /package/dist/types/{types/page-options.d.ts → page-options.d.ts} +0 -0
  310. /package/dist/types/{types/part-options.d.ts → part-options.d.ts} +0 -0
  311. /package/dist/types/{types/property-converter.d.ts → property-converter.d.ts} +0 -0
  312. /package/dist/types/{types/property-options.d.ts → property-options.d.ts} +0 -0
  313. /package/dist/types/{types/query-options.d.ts → query-options.d.ts} +0 -0
  314. /package/dist/types/{types/request-options.d.ts → request-options.d.ts} +0 -0
  315. /package/dist/types/{types/respond-options.d.ts → respond-options.d.ts} +0 -0
  316. /package/dist/types/{types/route-params.d.ts → route-params.d.ts} +0 -0
  317. /package/dist/types/{types/router-instance.d.ts → router-instance.d.ts} +0 -0
  318. /package/dist/types/{types/router-options.d.ts → router-options.d.ts} +0 -0
  319. /package/dist/types/{types/simple-array.d.ts → simple-array.d.ts} +0 -0
  320. /package/dist/types/{types/snice-element.d.ts → snice-element.d.ts} +0 -0
  321. /package/dist/types/{types/snice-global.d.ts → snice-global.d.ts} +0 -0
  322. /package/dist/types/{types/transition.d.ts → transition.d.ts} +0 -0
  323. /package/dist/{types/utils.d.ts → utils.d.ts} +0 -0
@@ -0,0 +1,855 @@
1
+ # Elements API Documentation
2
+
3
+ Elements are the core building blocks of Snice components. They define custom HTML elements with encapsulated styling and behavior using web components.
4
+
5
+ ## Table of Contents
6
+ - [Basic Usage](#basic-usage)
7
+ - [Lifecycle Methods](#lifecycle-methods)
8
+ - [Shadow DOM](#shadow-dom)
9
+ - [Properties](#properties)
10
+ - [Queries](#queries)
11
+ - [Styling](#styling)
12
+ - [Template Events](#template-events)
13
+ - [Advanced Examples](#advanced-examples)
14
+
15
+ ## Basic Usage
16
+
17
+ ### Creating an Element
18
+
19
+ ```typescript
20
+ import { element, render, html } from 'snice';
21
+
22
+ @element('my-button')
23
+ class MyButton extends HTMLElement {
24
+ @render()
25
+ renderContent() {
26
+ return html`<button>Click me</button>`;
27
+ }
28
+ }
29
+ ```
30
+
31
+ ### Element Decorator Options
32
+
33
+ The `@element` decorator accepts a single parameter:
34
+ - `tagName: string` - The custom element tag name (must contain a hyphen)
35
+
36
+ ## Lifecycle Methods
37
+
38
+ ### @render() Decorator
39
+
40
+ Returns a template using the `html` tagged template. Automatically re-renders when properties change due to differential rendering.
41
+
42
+ ```typescript
43
+ import { element, render, html, property } from 'snice';
44
+
45
+ @element('user-card')
46
+ class UserCard extends HTMLElement {
47
+ @property()
48
+ name = 'Anonymous';
49
+
50
+ @render()
51
+ renderContent() {
52
+ return html`
53
+ <div class="card">
54
+ <h3>${this.name}</h3>
55
+ <p>User details...</p>
56
+ </div>
57
+ `;
58
+ }
59
+ }
60
+ ```
61
+
62
+ **Auto-Rendering:**
63
+ - Template automatically re-renders when `@property()` decorated properties change
64
+ - Only changed parts of the DOM update (differential rendering)
65
+ - No manual re-render calls needed
66
+
67
+ **Render Options:**
68
+
69
+ The `@render()` decorator accepts an optional configuration object:
70
+
71
+ ```typescript
72
+ @render({
73
+ debounce?: number, // Delay re-render (ms)
74
+ throttle?: number, // Limit re-render frequency (ms)
75
+ once?: boolean, // Render once, disable auto-rendering
76
+ sync?: boolean, // Synchronous rendering (skip batching)
77
+ differential?: boolean // Disable differential rendering (default: true)
78
+ })
79
+ ```
80
+
81
+ **Differential Rendering:**
82
+
83
+ By default, Snice uses differential rendering which only updates changed parts of the DOM. To disable this and re-render from scratch each time:
84
+
85
+ ```typescript
86
+ @element('simple-list')
87
+ class SimpleList extends HTMLElement {
88
+ @property({ type: Array })
89
+ items = [];
90
+
91
+ @render({ differential: false })
92
+ renderContent() {
93
+ // Must return a string when differential: false
94
+ return `
95
+ <ul>
96
+ ${this.items.map(item => `<li>${item}</li>`).join('')}
97
+ </ul>
98
+ `;
99
+ }
100
+ }
101
+ ```
102
+
103
+ **When to use `differential: false`:**
104
+ - Component has complex dynamic structure that changes between renders
105
+ - Template structure changes based on data (e.g., empty state vs. populated)
106
+ - Avoiding differential rendering issues with dynamic attributes
107
+ - Simple components where full re-render is acceptable
108
+
109
+ **Note:** When `differential: false`, the render method must return a string (not `html\`...\``) and still honors `<if>` and `<switch>/<case>` meta elements.
110
+
111
+ ### @styles() Decorator
112
+
113
+ Returns CSS using the `css` tagged template, scoped to the element's shadow DOM.
114
+
115
+ ```typescript
116
+ import { element, render, styles, html, css } from 'snice';
117
+
118
+ @element('styled-card')
119
+ class StyledCard extends HTMLElement {
120
+ @render()
121
+ renderContent() {
122
+ return html`<div class="card">Content</div>`;
123
+ }
124
+
125
+ @styles()
126
+ cardStyles() {
127
+ return css`
128
+ .card {
129
+ padding: 20px;
130
+ border: 1px solid #ddd;
131
+ border-radius: 8px;
132
+ }
133
+ `;
134
+ }
135
+ }
136
+ ```
137
+
138
+ **Multiple Style Methods:**
139
+ ```typescript
140
+ @styles()
141
+ baseStyles() {
142
+ return css`
143
+ :host { display: block; }
144
+ // ...
145
+ `;
146
+ }
147
+
148
+ @styles()
149
+ themeStyles() {
150
+ return css`
151
+ .card { background: var(--bg-color); }
152
+ // ...
153
+ `;
154
+ }
155
+ ```
156
+
157
+ ### Lifecycle Decorators
158
+
159
+ **@ready()** - Called after shadow DOM is ready and initial render completes:
160
+
161
+ ```typescript
162
+ import { element, ready, render, html } from 'snice';
163
+
164
+ @element('data-loader')
165
+ class DataLoader extends HTMLElement {
166
+ @ready()
167
+ async loadData() {
168
+ // Called after element is fully initialized
169
+ const data = await fetch('/api/data').then(r => r.json());
170
+ this.data = data;
171
+ }
172
+
173
+ @render()
174
+ renderContent() {
175
+ return html`<div>Loading...</div>`;
176
+ }
177
+ }
178
+ ```
179
+
180
+ **@dispose()** - Called when element is removed from DOM:
181
+
182
+ ```typescript
183
+ @element('polling-element')
184
+ class PollingElement extends HTMLElement {
185
+ private intervalId?: number;
186
+
187
+ @ready()
188
+ startPolling() {
189
+ this.intervalId = setInterval(() => {
190
+ this.updateData();
191
+ }, 5000);
192
+ }
193
+
194
+ @dispose()
195
+ stopPolling() {
196
+ if (this.intervalId) {
197
+ clearInterval(this.intervalId);
198
+ }
199
+ }
200
+
201
+ @render()
202
+ renderContent() {
203
+ return html`<div>Polling...</div>`;
204
+ }
205
+ }
206
+ ```
207
+
208
+ ### ready Promise
209
+
210
+ Every element has a `ready` promise that resolves when fully initialized:
211
+
212
+ ```typescript
213
+ const el = document.createElement('my-element') as MyElement;
214
+ document.body.appendChild(el);
215
+ await (el as any).ready; // Wait for element to be ready
216
+ ```
217
+
218
+ ## Shadow DOM
219
+
220
+ All elements automatically use Shadow DOM for style encapsulation.
221
+
222
+ ### Accessing Shadow Root
223
+
224
+ ```typescript
225
+ @element('shadow-demo')
226
+ class ShadowDemo extends HTMLElement {
227
+ @render()
228
+ renderContent() {
229
+ return html`<div id="content">Hello</div>`;
230
+ }
231
+
232
+ updateContent(text: string) {
233
+ const content = this.shadowRoot?.getElementById('content');
234
+ if (content) {
235
+ content.textContent = text;
236
+ }
237
+ }
238
+ }
239
+ ```
240
+
241
+ ## Properties
242
+
243
+ ### Basic Properties
244
+
245
+ Properties automatically sync with DOM attributes and trigger re-renders:
246
+
247
+ ```typescript
248
+ import { element, property, render, html } from 'snice';
249
+
250
+ @element('user-profile')
251
+ class UserProfile extends HTMLElement {
252
+ @property()
253
+ name = 'Anonymous';
254
+
255
+ @property({ type: Number })
256
+ age = 0;
257
+
258
+ @property({ type: Boolean })
259
+ verified = false;
260
+
261
+ @render()
262
+ renderContent() {
263
+ return html`
264
+ <div>
265
+ <h3>${this.name}</h3>
266
+ <p>Age: ${this.age}</p>
267
+ ${this.verified ? html`<span>✓ Verified</span>` : ''}
268
+ </div>
269
+ `;
270
+ }
271
+ }
272
+ ```
273
+
274
+ Usage:
275
+ ```html
276
+ <user-profile name="John Doe" age="30" verified></user-profile>
277
+ ```
278
+
279
+ ### Property Options
280
+
281
+ ```typescript
282
+ interface PropertyOptions {
283
+ type?: String | Number | Boolean | Array | Object | Date | BigInt | SimpleArray;
284
+ attribute?: string; // Custom attribute name
285
+ converter?: PropertyConverter; // Custom converter
286
+ hasChanged?: (value: any, oldValue: any) => boolean;
287
+ }
288
+ ```
289
+
290
+ ### Property Behavior
291
+
292
+ All properties automatically:
293
+ - Read from DOM attributes when present
294
+ - Reflect changes to corresponding attributes
295
+ - Convert between string attributes and typed properties
296
+ - Trigger re-renders when changed
297
+
298
+ ```typescript
299
+ @element('reflected-props')
300
+ class ReflectedProps extends HTMLElement {
301
+ @property()
302
+ theme = 'light';
303
+
304
+ @property({ attribute: 'user-id' })
305
+ userId = '';
306
+
307
+ @render()
308
+ renderContent() {
309
+ return html`<div class="${this.theme}">User: ${this.userId}</div>`;
310
+ }
311
+ }
312
+ ```
313
+
314
+ **Boolean Properties:**
315
+
316
+ ```typescript
317
+ @property({ type: Boolean })
318
+ enabled = false;
319
+ ```
320
+
321
+ - `<element>` or `<element enabled="">` → `true`
322
+ - `<element enabled="true">` → `true`
323
+ - `<element enabled="false">` → `false`
324
+ - No attribute → `false`
325
+
326
+ ### Custom Converters
327
+
328
+ ```typescript
329
+ const dateConverter: PropertyConverter = {
330
+ fromAttribute(value: string | null): Date | null {
331
+ return value ? new Date(value) : null;
332
+ },
333
+ toAttribute(value: Date | null): string | null {
334
+ return value ? value.toISOString() : null;
335
+ }
336
+ };
337
+
338
+ @element('date-display')
339
+ class DateDisplay extends HTMLElement {
340
+ @property({ converter: dateConverter })
341
+ date: Date | null = null;
342
+
343
+ @render()
344
+ renderContent() {
345
+ return html`<time>${this.date?.toLocaleDateString() || 'No date'}</time>`;
346
+ }
347
+ }
348
+ ```
349
+
350
+ ### SimpleArray Type
351
+
352
+ The `SimpleArray` type enables safe reflection of arrays containing basic types:
353
+
354
+ ```typescript
355
+ import { element, property, SimpleArray, render, html } from 'snice';
356
+
357
+ @element('tag-list')
358
+ class TagList extends HTMLElement {
359
+ @property({ type: SimpleArray })
360
+ tags = ['javascript', 'typescript', 'web'];
361
+
362
+ @render()
363
+ renderContent() {
364
+ return html`
365
+ <ul>
366
+ ${this.tags.map(tag => html`<li>${tag}</li>`)}
367
+ </ul>
368
+ `;
369
+ }
370
+ }
371
+ ```
372
+
373
+ Usage:
374
+ ```html
375
+ <tag-list tags="react,vue,angular"></tag-list>
376
+ ```
377
+
378
+ - Uses full-width comma (,) as separator
379
+ - Supports string, number, and boolean types
380
+ - Type-safe serialization
381
+
382
+ ## Queries
383
+
384
+ ### Single Element Query
385
+
386
+ ```typescript
387
+ import { element, query, render, html } from 'snice';
388
+
389
+ @element('form-component')
390
+ class FormComponent extends HTMLElement {
391
+ @query('input[type="text"]')
392
+ textInput?: HTMLInputElement;
393
+
394
+ @query('button[type="submit"]')
395
+ submitButton?: HTMLButtonElement;
396
+
397
+ @render()
398
+ renderContent() {
399
+ return html`
400
+ <form>
401
+ <input type="text" placeholder="Enter text">
402
+ <button type="submit">Submit</button>
403
+ </form>
404
+ `;
405
+ }
406
+
407
+ getValue(): string {
408
+ return this.textInput?.value || '';
409
+ }
410
+ }
411
+ ```
412
+
413
+ ### Multiple Elements Query
414
+
415
+ ```typescript
416
+ @element('todo-list')
417
+ class TodoList extends HTMLElement {
418
+ @queryAll('.todo-item')
419
+ todoItems?: NodeListOf<HTMLElement>;
420
+
421
+ @queryAll('input[type="checkbox"]')
422
+ checkboxes?: NodeListOf<HTMLInputElement>;
423
+
424
+ @render()
425
+ renderContent() {
426
+ return html`
427
+ <ul>
428
+ <li class="todo-item"><input type="checkbox"> Task 1</li>
429
+ <li class="todo-item"><input type="checkbox"> Task 2</li>
430
+ </ul>
431
+ `;
432
+ }
433
+
434
+ getCompletedCount(): number {
435
+ if (!this.checkboxes) return 0;
436
+ return Array.from(this.checkboxes).filter(cb => cb.checked).length;
437
+ }
438
+ }
439
+ ```
440
+
441
+ ### Query Options
442
+
443
+ Control where queries search using `light` and `shadow` options:
444
+
445
+ ```typescript
446
+ @element('query-options')
447
+ class QueryOptions extends HTMLElement {
448
+ // Query only in shadow DOM (default)
449
+ @query('.shadow-only')
450
+ shadowElement?: HTMLElement;
451
+
452
+ // Query only in light DOM (slotted content)
453
+ @query('.light-only', { light: true, shadow: false })
454
+ lightElement?: HTMLElement;
455
+
456
+ // Query in both light and shadow DOM
457
+ @query('.anywhere', { light: true, shadow: true })
458
+ anyElement?: HTMLElement;
459
+
460
+ @render()
461
+ renderContent() {
462
+ return html`
463
+ <div class="shadow-only">Shadow Content</div>
464
+ <slot></slot>
465
+ `;
466
+ }
467
+ }
468
+ ```
469
+
470
+ ## Styling
471
+
472
+ ### Scoped Styles
473
+
474
+ Styles are automatically scoped to the component's shadow DOM:
475
+
476
+ ```typescript
477
+ @element('scoped-styles')
478
+ class ScopedStyles extends HTMLElement {
479
+ @render()
480
+ renderContent() {
481
+ return html`
482
+ <div class="container">
483
+ <h1>Title</h1>
484
+ <p class="content">Content</p>
485
+ </div>
486
+ `;
487
+ }
488
+
489
+ @styles()
490
+ componentStyles() {
491
+ return css`
492
+ :host {
493
+ display: block;
494
+ padding: 20px;
495
+ }
496
+
497
+ .container {
498
+ border: 1px solid #ccc;
499
+ }
500
+
501
+ h1 {
502
+ color: blue; /* Only affects h1 inside this component */
503
+ }
504
+ `;
505
+ }
506
+ }
507
+ ```
508
+
509
+ ### Dynamic Styles
510
+
511
+ ```typescript
512
+ @element('theme-component')
513
+ class ThemeComponent extends HTMLElement {
514
+ @property()
515
+ primaryColor = '#007bff';
516
+
517
+ @property({ type: Number })
518
+ fontSize = 16;
519
+
520
+ @render()
521
+ renderContent() {
522
+ return html`<div class="themed">Themed content</div>`;
523
+ }
524
+
525
+ @styles()
526
+ themeStyles() {
527
+ return css`
528
+ .themed {
529
+ color: ${this.primaryColor};
530
+ font-size: ${this.fontSize}px;
531
+ }
532
+ `;
533
+ }
534
+ }
535
+ ```
536
+
537
+ ### Host Styling
538
+
539
+ ```typescript
540
+ @element('host-styled')
541
+ class HostStyled extends HTMLElement {
542
+ @styles()
543
+ hostStyles() {
544
+ return css`
545
+ :host {
546
+ display: block;
547
+ width: 100%;
548
+ max-width: 600px;
549
+ margin: 0 auto;
550
+ }
551
+
552
+ :host([disabled]) {
553
+ opacity: 0.5;
554
+ pointer-events: none;
555
+ }
556
+
557
+ :host(:hover) {
558
+ background: #f0f0f0;
559
+ }
560
+ `;
561
+ }
562
+
563
+ @render()
564
+ renderContent() {
565
+ return html`<div>Content</div>`;
566
+ }
567
+ }
568
+ ```
569
+
570
+ ## Template Events
571
+
572
+ Handle events directly in templates using `@event=${handler}` syntax:
573
+
574
+ ### Basic Event Handling
575
+
576
+ ```typescript
577
+ @element('click-counter')
578
+ class ClickCounter extends HTMLElement {
579
+ @property({ type: Number })
580
+ count = 0;
581
+
582
+ @render()
583
+ renderContent() {
584
+ return html`
585
+ <button @click=${this.increment}>Click me</button>
586
+ <span>Count: ${this.count}</span>
587
+ `;
588
+ }
589
+
590
+ increment() {
591
+ this.count++;
592
+ // Auto re-renders due to property change
593
+ }
594
+ }
595
+ ```
596
+
597
+ ### Keyboard Shortcuts
598
+
599
+ Use dot notation for keyboard shortcuts:
600
+
601
+ ```typescript
602
+ @element('keyboard-handler')
603
+ class KeyboardHandler extends HTMLElement {
604
+ @render()
605
+ renderContent() {
606
+ return html`
607
+ <input @keydown.enter=${this.handleEnter} placeholder="Press Enter">
608
+ <input @keydown.ctrl+s=${this.handleSave} placeholder="Press Ctrl+S">
609
+ <input @keydown.escape=${this.handleCancel} placeholder="Press Escape">
610
+ `;
611
+ }
612
+
613
+ handleEnter(e: KeyboardEvent) {
614
+ console.log('Enter pressed');
615
+ }
616
+
617
+ handleSave(e: KeyboardEvent) {
618
+ e.preventDefault();
619
+ console.log('Saving...');
620
+ }
621
+
622
+ handleCancel() {
623
+ console.log('Cancelled');
624
+ }
625
+ }
626
+ ```
627
+
628
+ Keyboard syntax:
629
+ - `@keydown.enter` - Plain Enter (no modifiers)
630
+ - `@keydown.ctrl+s` - Ctrl+S combination
631
+ - `@keydown.~enter` - Enter with any modifiers
632
+ - `@keydown.escape`, `@keydown.down`, etc.
633
+
634
+ ### Event Object Access
635
+
636
+ ```typescript
637
+ @element('form-handler')
638
+ class FormHandler extends HTMLElement {
639
+ @render()
640
+ renderContent() {
641
+ return html`
642
+ <form @submit=${this.handleSubmit}>
643
+ <input type="text" name="username" @input=${this.handleInput}>
644
+ <button type="submit">Submit</button>
645
+ </form>
646
+ `;
647
+ }
648
+
649
+ handleSubmit(event: Event) {
650
+ event.preventDefault();
651
+ const form = event.target as HTMLFormElement;
652
+ const formData = new FormData(form);
653
+ console.log('Form data:', Object.fromEntries(formData));
654
+ }
655
+
656
+ handleInput(event: Event) {
657
+ const input = event.target as HTMLInputElement;
658
+ console.log('Input changed:', input.value);
659
+ }
660
+ }
661
+ ```
662
+
663
+ ## Advanced Examples
664
+
665
+ ### Complex Form Component
666
+
667
+ ```typescript
668
+ import { element, property, query, watch, render, styles, html, css } from 'snice';
669
+
670
+ @element('registration-form')
671
+ class RegistrationForm extends HTMLElement {
672
+ @property({ type: Boolean })
673
+ loading = false;
674
+
675
+ @query('form')
676
+ form?: HTMLFormElement;
677
+
678
+ @render()
679
+ renderContent() {
680
+ return html`
681
+ <form @submit=${this.handleSubmit}>
682
+ <h2>Register</h2>
683
+
684
+ <div class="field">
685
+ <label>Username</label>
686
+ <input type="text" name="username" required>
687
+ </div>
688
+
689
+ <div class="field">
690
+ <label>Email</label>
691
+ <input type="email" name="email" required>
692
+ </div>
693
+
694
+ <button type="submit" ?disabled=${this.loading}>
695
+ ${this.loading ? 'Registering...' : 'Register'}
696
+ </button>
697
+ </form>
698
+ `;
699
+ }
700
+
701
+ @styles()
702
+ formStyles() {
703
+ return css`
704
+ :host {
705
+ display: block;
706
+ max-width: 400px;
707
+ margin: 0 auto;
708
+ }
709
+
710
+ form {
711
+ padding: 20px;
712
+ background: white;
713
+ border-radius: 8px;
714
+ }
715
+
716
+ .field {
717
+ margin-bottom: 20px;
718
+ }
719
+
720
+ label {
721
+ display: block;
722
+ margin-bottom: 5px;
723
+ font-weight: bold;
724
+ }
725
+
726
+ input {
727
+ width: 100%;
728
+ padding: 8px 12px;
729
+ border: 1px solid #ddd;
730
+ border-radius: 4px;
731
+ }
732
+
733
+ button {
734
+ width: 100%;
735
+ padding: 10px;
736
+ background: #007bff;
737
+ color: white;
738
+ border: none;
739
+ border-radius: 4px;
740
+ cursor: pointer;
741
+ }
742
+
743
+ button:disabled {
744
+ opacity: 0.6;
745
+ cursor: not-allowed;
746
+ }
747
+ `;
748
+ }
749
+
750
+ async handleSubmit(event: Event) {
751
+ event.preventDefault();
752
+
753
+ this.loading = true;
754
+
755
+ try {
756
+ const formData = new FormData(this.form!);
757
+
758
+ // Simulate API call
759
+ await new Promise(resolve => setTimeout(resolve, 2000));
760
+
761
+ this.dispatchEvent(new CustomEvent('registration-success', {
762
+ detail: Object.fromEntries(formData),
763
+ bubbles: true
764
+ }));
765
+
766
+ this.form?.reset();
767
+ } catch (error) {
768
+ console.error('Registration failed:', error);
769
+ } finally {
770
+ this.loading = false;
771
+ }
772
+ }
773
+ }
774
+ ```
775
+
776
+ ### Watch Decorator
777
+
778
+ Use `@watch` to react to property changes:
779
+
780
+ ```typescript
781
+ @element('reactive-component')
782
+ class ReactiveComponent extends HTMLElement {
783
+ @property()
784
+ userName = '';
785
+
786
+ @property({ type: Number })
787
+ score = 0;
788
+
789
+ @watch('userName')
790
+ onUserNameChange(oldVal: string, newVal: string) {
791
+ console.log(`Name changed from ${oldVal} to ${newVal}`);
792
+ }
793
+
794
+ @watch('score')
795
+ onScoreChange(oldVal: number, newVal: number) {
796
+ if (newVal > 100) {
797
+ console.log('High score achieved!');
798
+ }
799
+ }
800
+
801
+ @render()
802
+ renderContent() {
803
+ return html`
804
+ <div>
805
+ <h1>${this.userName}</h1>
806
+ <p>Score: ${this.score}</p>
807
+ </div>
808
+ `;
809
+ }
810
+ }
811
+ ```
812
+
813
+ ### Conditional Rendering
814
+
815
+ ```typescript
816
+ @element('conditional-content')
817
+ class ConditionalContent extends HTMLElement {
818
+ @property({ type: Boolean })
819
+ isLoggedIn = false;
820
+
821
+ @render()
822
+ renderContent() {
823
+ return html`
824
+ <if ${this.isLoggedIn}>
825
+ <div>Welcome back!</div>
826
+ <button @click=${this.logout}>Logout</button>
827
+ </if>
828
+
829
+ <if ${!this.isLoggedIn}>
830
+ <a href="/login">Please login</a>
831
+ </if>
832
+ `;
833
+ }
834
+
835
+ logout() {
836
+ this.isLoggedIn = false;
837
+ }
838
+ }
839
+ ```
840
+
841
+ ## Best Practices
842
+
843
+ 1. **Use @render() for templates**: Always return `html\`...\`` tagged templates
844
+ 2. **Use @styles() for CSS**: Always return `css\`...\`` tagged templates
845
+ 3. **Leverage auto-rendering**: Properties automatically trigger re-renders
846
+ 4. **Use template events**: Handle events with `@event=${handler}` in templates
847
+ 5. **Clean up resources**: Use @dispose() for cleanup tasks
848
+ 6. **Type your queries**: Use proper TypeScript types for queried elements
849
+ 7. **Handle errors**: Wrap async operations in try-catch blocks
850
+
851
+ ## Removed in v3.0.0
852
+
853
+ - **@part decorator**: Removed in favor of differential rendering. Use `@render()` with templates instead.
854
+ - **html() method**: Replaced with `@render()` decorator
855
+ - **css() method**: Replaced with `@styles()` decorator