@tinya-code/gas-admin-ui 0.2.0-alpha.15 → 0.2.0-alpha.17

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 (368) hide show
  1. package/fesm2022/tinya-code-gas-admin-ui-badge-EeCA_l-o.mjs +84 -0
  2. package/fesm2022/tinya-code-gas-admin-ui-badge-EeCA_l-o.mjs.map +1 -0
  3. package/fesm2022/tinya-code-gas-admin-ui-bar-chart-G7sEMSBY.mjs +140 -0
  4. package/fesm2022/tinya-code-gas-admin-ui-bar-chart-G7sEMSBY.mjs.map +1 -0
  5. package/fesm2022/tinya-code-gas-admin-ui-boolean-B2ASKL0E.mjs +113 -0
  6. package/fesm2022/tinya-code-gas-admin-ui-boolean-B2ASKL0E.mjs.map +1 -0
  7. package/fesm2022/tinya-code-gas-admin-ui-button-BQjNW8KB.mjs +163 -0
  8. package/fesm2022/tinya-code-gas-admin-ui-button-BQjNW8KB.mjs.map +1 -0
  9. package/fesm2022/tinya-code-gas-admin-ui-calendar-view-DCY8bj2V.mjs +1027 -0
  10. package/fesm2022/tinya-code-gas-admin-ui-calendar-view-DCY8bj2V.mjs.map +1 -0
  11. package/fesm2022/tinya-code-gas-admin-ui-chart-line-BjdlKHoH.mjs +278 -0
  12. package/fesm2022/tinya-code-gas-admin-ui-chart-line-BjdlKHoH.mjs.map +1 -0
  13. package/fesm2022/tinya-code-gas-admin-ui-color-Dljh_By3.mjs +107 -0
  14. package/fesm2022/tinya-code-gas-admin-ui-color-Dljh_By3.mjs.map +1 -0
  15. package/fesm2022/tinya-code-gas-admin-ui-condition-evaluator-DY2k1M3C.mjs +101 -0
  16. package/fesm2022/tinya-code-gas-admin-ui-condition-evaluator-DY2k1M3C.mjs.map +1 -0
  17. package/fesm2022/tinya-code-gas-admin-ui-currency-BvuF3Z63.mjs +141 -0
  18. package/fesm2022/tinya-code-gas-admin-ui-currency-BvuF3Z63.mjs.map +1 -0
  19. package/fesm2022/tinya-code-gas-admin-ui-data-source.service-96FSbN72.mjs +88 -0
  20. package/fesm2022/tinya-code-gas-admin-ui-data-source.service-96FSbN72.mjs.map +1 -0
  21. package/fesm2022/tinya-code-gas-admin-ui-date-1SJGXZrd.mjs +107 -0
  22. package/fesm2022/tinya-code-gas-admin-ui-date-1SJGXZrd.mjs.map +1 -0
  23. package/fesm2022/tinya-code-gas-admin-ui-display-badge-fwk6sDU7.mjs +140 -0
  24. package/fesm2022/tinya-code-gas-admin-ui-display-badge-fwk6sDU7.mjs.map +1 -0
  25. package/fesm2022/tinya-code-gas-admin-ui-display-date-D7zLiDiG.mjs +119 -0
  26. package/fesm2022/tinya-code-gas-admin-ui-display-date-D7zLiDiG.mjs.map +1 -0
  27. package/fesm2022/tinya-code-gas-admin-ui-display-file-BiIhqvQC.mjs +125 -0
  28. package/fesm2022/tinya-code-gas-admin-ui-display-file-BiIhqvQC.mjs.map +1 -0
  29. package/fesm2022/tinya-code-gas-admin-ui-display-image-C1dhnZi1.mjs +152 -0
  30. package/fesm2022/tinya-code-gas-admin-ui-display-image-C1dhnZi1.mjs.map +1 -0
  31. package/fesm2022/tinya-code-gas-admin-ui-display-list-DOEuUbGe.mjs +211 -0
  32. package/fesm2022/tinya-code-gas-admin-ui-display-list-DOEuUbGe.mjs.map +1 -0
  33. package/fesm2022/tinya-code-gas-admin-ui-display-number-DDMzr4Hn.mjs +116 -0
  34. package/fesm2022/tinya-code-gas-admin-ui-display-number-DDMzr4Hn.mjs.map +1 -0
  35. package/fesm2022/tinya-code-gas-admin-ui-display-text-B8CdVNpv.mjs +111 -0
  36. package/fesm2022/tinya-code-gas-admin-ui-display-text-B8CdVNpv.mjs.map +1 -0
  37. package/fesm2022/tinya-code-gas-admin-ui-dropdown-C3blvmE1.mjs +122 -0
  38. package/fesm2022/tinya-code-gas-admin-ui-dropdown-C3blvmE1.mjs.map +1 -0
  39. package/fesm2022/tinya-code-gas-admin-ui-email-CT_O5ihA.mjs +107 -0
  40. package/fesm2022/tinya-code-gas-admin-ui-email-CT_O5ihA.mjs.map +1 -0
  41. package/fesm2022/tinya-code-gas-admin-ui-empty-state-BMc0QtoT.mjs +109 -0
  42. package/fesm2022/tinya-code-gas-admin-ui-empty-state-BMc0QtoT.mjs.map +1 -0
  43. package/fesm2022/tinya-code-gas-admin-ui-error-mapper.service-DY5y1FjE.mjs +45 -0
  44. package/fesm2022/tinya-code-gas-admin-ui-error-mapper.service-DY5y1FjE.mjs.map +1 -0
  45. package/fesm2022/tinya-code-gas-admin-ui-field-aria-BD1Wiggc.mjs +50 -0
  46. package/fesm2022/tinya-code-gas-admin-ui-field-aria-BD1Wiggc.mjs.map +1 -0
  47. package/fesm2022/tinya-code-gas-admin-ui-field-node-CIK6KNN_.mjs +24 -0
  48. package/fesm2022/tinya-code-gas-admin-ui-field-node-CIK6KNN_.mjs.map +1 -0
  49. package/fesm2022/tinya-code-gas-admin-ui-field-path-B4-FTBGZ.mjs +27 -0
  50. package/fesm2022/tinya-code-gas-admin-ui-field-path-B4-FTBGZ.mjs.map +1 -0
  51. package/fesm2022/tinya-code-gas-admin-ui-file-Dmxioog7.mjs +408 -0
  52. package/fesm2022/tinya-code-gas-admin-ui-file-Dmxioog7.mjs.map +1 -0
  53. package/fesm2022/tinya-code-gas-admin-ui-form-view-CEe7aZ4m.mjs +4516 -0
  54. package/fesm2022/tinya-code-gas-admin-ui-form-view-CEe7aZ4m.mjs.map +1 -0
  55. package/fesm2022/tinya-code-gas-admin-ui-format-badge-CK8ct5Wk.mjs +82 -0
  56. package/fesm2022/tinya-code-gas-admin-ui-format-badge-CK8ct5Wk.mjs.map +1 -0
  57. package/fesm2022/tinya-code-gas-admin-ui-format-boolean-ClzezZz-.mjs +105 -0
  58. package/fesm2022/tinya-code-gas-admin-ui-format-boolean-ClzezZz-.mjs.map +1 -0
  59. package/fesm2022/tinya-code-gas-admin-ui-format-currency-CVmniKml.mjs +65 -0
  60. package/fesm2022/tinya-code-gas-admin-ui-format-currency-CVmniKml.mjs.map +1 -0
  61. package/fesm2022/tinya-code-gas-admin-ui-format-date-Dr1YsDd_.mjs +65 -0
  62. package/fesm2022/tinya-code-gas-admin-ui-format-date-Dr1YsDd_.mjs.map +1 -0
  63. package/fesm2022/tinya-code-gas-admin-ui-format-number-CWCjpI4n.mjs +65 -0
  64. package/fesm2022/tinya-code-gas-admin-ui-format-number-CWCjpI4n.mjs.map +1 -0
  65. package/fesm2022/tinya-code-gas-admin-ui-format-text-DBOgJkH0.mjs +59 -0
  66. package/fesm2022/tinya-code-gas-admin-ui-format-text-DBOgJkH0.mjs.map +1 -0
  67. package/fesm2022/tinya-code-gas-admin-ui-format-thumbnail-PCRvxWKt.mjs +94 -0
  68. package/fesm2022/tinya-code-gas-admin-ui-format-thumbnail-PCRvxWKt.mjs.map +1 -0
  69. package/fesm2022/tinya-code-gas-admin-ui-gap-sorting-iTdy_DYG.mjs +72 -0
  70. package/fesm2022/tinya-code-gas-admin-ui-gap-sorting-iTdy_DYG.mjs.map +1 -0
  71. package/fesm2022/tinya-code-gas-admin-ui-group-h7-O8tt4.mjs +238 -0
  72. package/fesm2022/tinya-code-gas-admin-ui-group-h7-O8tt4.mjs.map +1 -0
  73. package/fesm2022/tinya-code-gas-admin-ui-image-7UhJ7nqI.mjs +416 -0
  74. package/fesm2022/tinya-code-gas-admin-ui-image-7UhJ7nqI.mjs.map +1 -0
  75. package/fesm2022/tinya-code-gas-admin-ui-kanban-view-Dl0Ol5Kb.mjs +439 -0
  76. package/fesm2022/tinya-code-gas-admin-ui-kanban-view-Dl0Ol5Kb.mjs.map +1 -0
  77. package/fesm2022/tinya-code-gas-admin-ui-key-value-nVdgg5at.mjs +513 -0
  78. package/fesm2022/tinya-code-gas-admin-ui-key-value-nVdgg5at.mjs.map +1 -0
  79. package/fesm2022/tinya-code-gas-admin-ui-list-DTtCYS4d.mjs +637 -0
  80. package/fesm2022/tinya-code-gas-admin-ui-list-DTtCYS4d.mjs.map +1 -0
  81. package/fesm2022/tinya-code-gas-admin-ui-metric-card-DnGMQL8l.mjs +127 -0
  82. package/fesm2022/tinya-code-gas-admin-ui-metric-card-DnGMQL8l.mjs.map +1 -0
  83. package/fesm2022/tinya-code-gas-admin-ui-multiselect-PB28e-jl.mjs +251 -0
  84. package/fesm2022/tinya-code-gas-admin-ui-multiselect-PB28e-jl.mjs.map +1 -0
  85. package/fesm2022/tinya-code-gas-admin-ui-notification.service-LjSULER6.mjs +66 -0
  86. package/fesm2022/tinya-code-gas-admin-ui-notification.service-LjSULER6.mjs.map +1 -0
  87. package/fesm2022/tinya-code-gas-admin-ui-number-JNr7glc8.mjs +114 -0
  88. package/fesm2022/tinya-code-gas-admin-ui-number-JNr7glc8.mjs.map +1 -0
  89. package/fesm2022/tinya-code-gas-admin-ui-phone-jR417hPf.mjs +116 -0
  90. package/fesm2022/tinya-code-gas-admin-ui-phone-jR417hPf.mjs.map +1 -0
  91. package/fesm2022/tinya-code-gas-admin-ui-quick-actions-DCQ6emmF.mjs +235 -0
  92. package/fesm2022/tinya-code-gas-admin-ui-quick-actions-DCQ6emmF.mjs.map +1 -0
  93. package/fesm2022/tinya-code-gas-admin-ui-radio-mode-DxvN07DY.mjs +8 -0
  94. package/fesm2022/tinya-code-gas-admin-ui-radio-mode-DxvN07DY.mjs.map +1 -0
  95. package/fesm2022/tinya-code-gas-admin-ui-readonly-text-BL7-4qie.mjs +124 -0
  96. package/fesm2022/tinya-code-gas-admin-ui-readonly-text-BL7-4qie.mjs.map +1 -0
  97. package/fesm2022/tinya-code-gas-admin-ui-record-list-BiZfnd_7.mjs +249 -0
  98. package/fesm2022/tinya-code-gas-admin-ui-record-list-BiZfnd_7.mjs.map +1 -0
  99. package/fesm2022/tinya-code-gas-admin-ui-relation-drawer.service-B0kT8EiW.mjs +39 -0
  100. package/fesm2022/tinya-code-gas-admin-ui-relation-drawer.service-B0kT8EiW.mjs.map +1 -0
  101. package/fesm2022/tinya-code-gas-admin-ui-relation-ykpJ3PYl.mjs +309 -0
  102. package/fesm2022/tinya-code-gas-admin-ui-relation-ykpJ3PYl.mjs.map +1 -0
  103. package/fesm2022/tinya-code-gas-admin-ui-render-registry-B88Pme-y.mjs +576 -0
  104. package/fesm2022/tinya-code-gas-admin-ui-render-registry-B88Pme-y.mjs.map +1 -0
  105. package/fesm2022/tinya-code-gas-admin-ui-rich-select-CIHaOrbU.mjs +161 -0
  106. package/fesm2022/tinya-code-gas-admin-ui-rich-select-CIHaOrbU.mjs.map +1 -0
  107. package/fesm2022/tinya-code-gas-admin-ui-rolldown-runtime-DpJrqANO.mjs +11 -0
  108. package/fesm2022/tinya-code-gas-admin-ui-schema-catalog-D0Pk0wAN.mjs +685 -0
  109. package/fesm2022/tinya-code-gas-admin-ui-schema-catalog-D0Pk0wAN.mjs.map +1 -0
  110. package/fesm2022/tinya-code-gas-admin-ui-seed-DkVZbxPP.mjs +80 -0
  111. package/fesm2022/tinya-code-gas-admin-ui-seed-DkVZbxPP.mjs.map +1 -0
  112. package/fesm2022/tinya-code-gas-admin-ui-select-CDwX9yNO.mjs +296 -0
  113. package/fesm2022/tinya-code-gas-admin-ui-select-CDwX9yNO.mjs.map +1 -0
  114. package/fesm2022/tinya-code-gas-admin-ui-skeleton-DnWK0tcZ.mjs +119 -0
  115. package/fesm2022/tinya-code-gas-admin-ui-skeleton-DnWK0tcZ.mjs.map +1 -0
  116. package/fesm2022/tinya-code-gas-admin-ui-slug-DU58cbiY.mjs +158 -0
  117. package/fesm2022/tinya-code-gas-admin-ui-slug-DU58cbiY.mjs.map +1 -0
  118. package/fesm2022/tinya-code-gas-admin-ui-source-options-B9R4mw8-.mjs +26 -0
  119. package/fesm2022/tinya-code-gas-admin-ui-source-options-B9R4mw8-.mjs.map +1 -0
  120. package/fesm2022/tinya-code-gas-admin-ui-status-progress-DmeG8htE.mjs +205 -0
  121. package/fesm2022/tinya-code-gas-admin-ui-status-progress-DmeG8htE.mjs.map +1 -0
  122. package/fesm2022/tinya-code-gas-admin-ui-string-list-gleasqzD.mjs +432 -0
  123. package/fesm2022/tinya-code-gas-admin-ui-string-list-gleasqzD.mjs.map +1 -0
  124. package/fesm2022/tinya-code-gas-admin-ui-template-interpolator-ewv7CkNX.mjs +36 -0
  125. package/fesm2022/tinya-code-gas-admin-ui-template-interpolator-ewv7CkNX.mjs.map +1 -0
  126. package/fesm2022/tinya-code-gas-admin-ui-text-CUPmL7W_.mjs +107 -0
  127. package/fesm2022/tinya-code-gas-admin-ui-text-CUPmL7W_.mjs.map +1 -0
  128. package/fesm2022/tinya-code-gas-admin-ui-textarea-CGLSIW13.mjs +223 -0
  129. package/fesm2022/tinya-code-gas-admin-ui-textarea-CGLSIW13.mjs.map +1 -0
  130. package/fesm2022/tinya-code-gas-admin-ui-time-BcHzsTWf.mjs +107 -0
  131. package/fesm2022/tinya-code-gas-admin-ui-time-BcHzsTWf.mjs.map +1 -0
  132. package/fesm2022/tinya-code-gas-admin-ui-upload.service-CZPEPCeN.mjs +60 -0
  133. package/fesm2022/tinya-code-gas-admin-ui-upload.service-CZPEPCeN.mjs.map +1 -0
  134. package/fesm2022/tinya-code-gas-admin-ui-url-GO4LZi__.mjs +141 -0
  135. package/fesm2022/tinya-code-gas-admin-ui-url-GO4LZi__.mjs.map +1 -0
  136. package/fesm2022/tinya-code-gas-admin-ui.mjs +4738 -0
  137. package/fesm2022/tinya-code-gas-admin-ui.mjs.map +1 -0
  138. package/package.json +15 -3
  139. package/types/tinya-code-gas-admin-ui.d.ts +3634 -0
  140. package/ng-package.json +0 -7
  141. package/src/lib/actions/action-registry.spec.ts +0 -52
  142. package/src/lib/actions/action-registry.ts +0 -126
  143. package/src/lib/actions/action-service.spec.ts +0 -215
  144. package/src/lib/actions/action-service.ts +0 -87
  145. package/src/lib/actions/descriptors.spec.ts +0 -155
  146. package/src/lib/actions/descriptors.ts +0 -154
  147. package/src/lib/columns/column-cell.ts +0 -33
  148. package/src/lib/columns/column-defaults.spec.ts +0 -99
  149. package/src/lib/columns/column-defaults.ts +0 -84
  150. package/src/lib/columns/column-format.spec.ts +0 -274
  151. package/src/lib/columns/descriptors.ts +0 -113
  152. package/src/lib/columns/format-badge.ts +0 -51
  153. package/src/lib/columns/format-boolean.ts +0 -58
  154. package/src/lib/columns/format-currency.ts +0 -30
  155. package/src/lib/columns/format-date.ts +0 -35
  156. package/src/lib/columns/format-number.ts +0 -32
  157. package/src/lib/columns/format-text.ts +0 -23
  158. package/src/lib/columns/format-thumbnail.ts +0 -63
  159. package/src/lib/core/adapters/firebase-auth.adapter.ts +0 -6
  160. package/src/lib/core/guards/auth.guard.spec.ts +0 -88
  161. package/src/lib/core/guards/auth.guard.ts +0 -48
  162. package/src/lib/core/guards/unsaved-changes.guard.ts +0 -16
  163. package/src/lib/core/interceptors/api-error.interceptor.spec.ts +0 -182
  164. package/src/lib/core/interceptors/api-error.interceptor.ts +0 -87
  165. package/src/lib/core/models/api.model.ts +0 -104
  166. package/src/lib/core/models/schema-descriptions.ts +0 -118
  167. package/src/lib/core/models/schema.model.ts +0 -1097
  168. package/src/lib/core/registry/composition-validator.spec.ts +0 -358
  169. package/src/lib/core/registry/composition-validator.ts +0 -270
  170. package/src/lib/core/registry/field-validators.spec.ts +0 -199
  171. package/src/lib/core/registry/field-validators.ts +0 -440
  172. package/src/lib/core/registry/render-host.spec.ts +0 -208
  173. package/src/lib/core/registry/render-host.ts +0 -646
  174. package/src/lib/core/registry/render-registry.spec.ts +0 -96
  175. package/src/lib/core/registry/render-registry.ts +0 -312
  176. package/src/lib/core/registry/schema-catalog.ts +0 -156
  177. package/src/lib/core/services/api.service.spec.ts +0 -1142
  178. package/src/lib/core/services/api.service.ts +0 -371
  179. package/src/lib/core/services/auth.service.spec.ts +0 -653
  180. package/src/lib/core/services/auth.service.ts +0 -503
  181. package/src/lib/core/services/config.service.spec.ts +0 -121
  182. package/src/lib/core/services/config.service.ts +0 -119
  183. package/src/lib/core/services/data-source.service.spec.ts +0 -179
  184. package/src/lib/core/services/data-source.service.ts +0 -142
  185. package/src/lib/core/services/error-mapper.service.spec.ts +0 -50
  186. package/src/lib/core/services/error-mapper.service.ts +0 -49
  187. package/src/lib/core/services/notification.service.ts +0 -73
  188. package/src/lib/core/services/relation-drawer.service.ts +0 -41
  189. package/src/lib/core/services/theme.service.spec.ts +0 -106
  190. package/src/lib/core/services/theme.service.ts +0 -86
  191. package/src/lib/core/services/transport.spec.ts +0 -198
  192. package/src/lib/core/services/transport.ts +0 -164
  193. package/src/lib/core/services/upload.service.ts +0 -81
  194. package/src/lib/core/testing/categories.schema.ts +0 -125
  195. package/src/lib/core/testing/orders.schema.ts +0 -372
  196. package/src/lib/core/testing/products.schema.ts +0 -258
  197. package/src/lib/core/testing/site.schema.ts +0 -195
  198. package/src/lib/core/testing/test-catalog.ts +0 -21
  199. package/src/lib/core/tokens.ts +0 -112
  200. package/src/lib/core/utils/clipboard.spec.ts +0 -55
  201. package/src/lib/core/utils/clipboard.ts +0 -90
  202. package/src/lib/core/utils/context-resolver.spec.ts +0 -95
  203. package/src/lib/core/utils/context-resolver.ts +0 -101
  204. package/src/lib/core/utils/response-unwrapper.spec.ts +0 -70
  205. package/src/lib/core/utils/response-unwrapper.ts +0 -103
  206. package/src/lib/core/utils/source-options.spec.ts +0 -96
  207. package/src/lib/core/utils/source-options.ts +0 -54
  208. package/src/lib/core/utils/template-interpolator.spec.ts +0 -59
  209. package/src/lib/core/utils/template-interpolator.ts +0 -56
  210. package/src/lib/core/view-navigation/view-navigation.service.spec.ts +0 -207
  211. package/src/lib/core/view-navigation/view-navigation.service.ts +0 -132
  212. package/src/lib/fields/boolean/boolean.descriptor.ts +0 -11
  213. package/src/lib/fields/boolean/boolean.ts +0 -40
  214. package/src/lib/fields/color/color.descriptor.ts +0 -12
  215. package/src/lib/fields/color/color.ts +0 -34
  216. package/src/lib/fields/currency/currency.descriptor.ts +0 -12
  217. package/src/lib/fields/currency/currency.ts +0 -67
  218. package/src/lib/fields/date/date.descriptor.ts +0 -12
  219. package/src/lib/fields/date/date.ts +0 -36
  220. package/src/lib/fields/descriptors.ts +0 -122
  221. package/src/lib/fields/display/display-badge.ts +0 -65
  222. package/src/lib/fields/display/display-date.ts +0 -39
  223. package/src/lib/fields/display/display-defaults.ts +0 -84
  224. package/src/lib/fields/display/display-file.ts +0 -41
  225. package/src/lib/fields/display/display-image.ts +0 -54
  226. package/src/lib/fields/display/display-list.ts +0 -107
  227. package/src/lib/fields/display/display-number.ts +0 -41
  228. package/src/lib/fields/display/display-text.ts +0 -42
  229. package/src/lib/fields/display/display.spec.ts +0 -242
  230. package/src/lib/fields/dropdown/dropdown.descriptor.ts +0 -12
  231. package/src/lib/fields/dropdown/dropdown.ts +0 -45
  232. package/src/lib/fields/email/email.descriptor.ts +0 -12
  233. package/src/lib/fields/email/email.ts +0 -28
  234. package/src/lib/fields/field-aria.ts +0 -60
  235. package/src/lib/fields/field-host/field-host.spec.ts +0 -118
  236. package/src/lib/fields/field-host/field-host.ts +0 -47
  237. package/src/lib/fields/field-node.ts +0 -88
  238. package/src/lib/fields/file/file.descriptor.ts +0 -14
  239. package/src/lib/fields/file/file.spec.ts +0 -134
  240. package/src/lib/fields/file/file.ts +0 -281
  241. package/src/lib/fields/group/group.descriptor.ts +0 -21
  242. package/src/lib/fields/group/group.ts +0 -110
  243. package/src/lib/fields/image/image.descriptor.ts +0 -12
  244. package/src/lib/fields/image/image.spec.ts +0 -117
  245. package/src/lib/fields/image/image.ts +0 -281
  246. package/src/lib/fields/key-value/key-value.descriptor.ts +0 -11
  247. package/src/lib/fields/key-value/key-value.ts +0 -283
  248. package/src/lib/fields/list/list.descriptor.ts +0 -21
  249. package/src/lib/fields/list/list.ts +0 -376
  250. package/src/lib/fields/multiselect/multiselect.descriptor.ts +0 -11
  251. package/src/lib/fields/multiselect/multiselect.spec.ts +0 -142
  252. package/src/lib/fields/multiselect/multiselect.ts +0 -152
  253. package/src/lib/fields/number/number.descriptor.ts +0 -12
  254. package/src/lib/fields/number/number.ts +0 -40
  255. package/src/lib/fields/phone/phone.descriptor.ts +0 -12
  256. package/src/lib/fields/phone/phone.ts +0 -46
  257. package/src/lib/fields/readonly-text/readonly-text.descriptor.ts +0 -12
  258. package/src/lib/fields/readonly-text/readonly-text.ts +0 -58
  259. package/src/lib/fields/relation/relation.descriptor.ts +0 -12
  260. package/src/lib/fields/relation/relation.spec.ts +0 -193
  261. package/src/lib/fields/relation/relation.ts +0 -224
  262. package/src/lib/fields/rich-select/rich-select.descriptor.ts +0 -12
  263. package/src/lib/fields/rich-select/rich-select.ts +0 -175
  264. package/src/lib/fields/select/radio-mode.ts +0 -25
  265. package/src/lib/fields/select/select.descriptor.ts +0 -12
  266. package/src/lib/fields/select/select.spec.ts +0 -162
  267. package/src/lib/fields/select/select.ts +0 -210
  268. package/src/lib/fields/slug/slug.descriptor.ts +0 -19
  269. package/src/lib/fields/slug/slug.ts +0 -67
  270. package/src/lib/fields/string-list/string-list.descriptor.ts +0 -11
  271. package/src/lib/fields/string-list/string-list.ts +0 -222
  272. package/src/lib/fields/text/text.descriptor.ts +0 -12
  273. package/src/lib/fields/text/text.ts +0 -35
  274. package/src/lib/fields/textarea/textarea.descriptor.ts +0 -12
  275. package/src/lib/fields/textarea/textarea.spec.ts +0 -117
  276. package/src/lib/fields/textarea/textarea.ts +0 -126
  277. package/src/lib/fields/time/time.descriptor.ts +0 -12
  278. package/src/lib/fields/time/time.ts +0 -28
  279. package/src/lib/fields/unknown-render/unknown-render.ts +0 -49
  280. package/src/lib/fields/url/url.descriptor.ts +0 -12
  281. package/src/lib/fields/url/url.ts +0 -53
  282. package/src/lib/gas-admin-ui.spec.ts +0 -21
  283. package/src/lib/gas-admin-ui.ts +0 -9
  284. package/src/lib/layouts/descriptors.ts +0 -9
  285. package/src/lib/pages/dashboard/dashboard.ts +0 -167
  286. package/src/lib/pages/detail-view/detail-view.spec.ts +0 -333
  287. package/src/lib/pages/detail-view/detail-view.ts +0 -570
  288. package/src/lib/pages/drawer-form/drawer-form.ts +0 -228
  289. package/src/lib/pages/form-view/form-model.ts +0 -88
  290. package/src/lib/pages/form-view/form-schema.ts +0 -255
  291. package/src/lib/pages/form-view/form-view.ts +0 -1622
  292. package/src/lib/pages/list-view/list-view.formats.spec.ts +0 -214
  293. package/src/lib/pages/list-view/list-view.ts +0 -882
  294. package/src/lib/pages/login/login.spec.ts +0 -109
  295. package/src/lib/pages/login/login.ts +0 -227
  296. package/src/lib/pages/resource-page/resource-page.ts +0 -234
  297. package/src/lib/shared/components/badge/badge.ts +0 -35
  298. package/src/lib/shared/components/button/button.ts +0 -65
  299. package/src/lib/shared/components/confirm-dialog/confirm-dialog.ts +0 -48
  300. package/src/lib/shared/components/copy-button/copy-button.ts +0 -56
  301. package/src/lib/shared/components/copy-share/copy-share.spec.ts +0 -122
  302. package/src/lib/shared/components/copy-share/copy-share.ts +0 -107
  303. package/src/lib/shared/components/drawer/drawer.spec.ts +0 -127
  304. package/src/lib/shared/components/drawer/drawer.ts +0 -172
  305. package/src/lib/shared/components/empty-state/empty-state.ts +0 -30
  306. package/src/lib/shared/components/modal/modal.spec.ts +0 -149
  307. package/src/lib/shared/components/modal/modal.ts +0 -145
  308. package/src/lib/shared/components/post-create-modal/post-create-modal.spec.ts +0 -165
  309. package/src/lib/shared/components/post-create-modal/post-create-modal.ts +0 -159
  310. package/src/lib/shared/components/skeleton/skeleton.ts +0 -31
  311. package/src/lib/shared/components/toast/toast.ts +0 -58
  312. package/src/lib/shared/utils/col-span.ts +0 -25
  313. package/src/lib/shared/utils/condition-evaluator.ts +0 -53
  314. package/src/lib/shared/utils/depends-on.ts +0 -48
  315. package/src/lib/shared/utils/dialog-focus.ts +0 -28
  316. package/src/lib/shared/utils/field-path.ts +0 -49
  317. package/src/lib/shared/utils/gap-sorting.ts +0 -144
  318. package/src/lib/shared/utils/seed.spec.ts +0 -64
  319. package/src/lib/shared/utils/seed.ts +0 -149
  320. package/src/lib/shared/utils/slugify.ts +0 -13
  321. package/src/lib/shared/utils/validators.ts +0 -41
  322. package/src/lib/shell/layout/layout.ts +0 -63
  323. package/src/lib/shell/shell.routes.spec.ts +0 -59
  324. package/src/lib/shell/shell.routes.ts +0 -122
  325. package/src/lib/shell/sidebar/sidebar.ts +0 -139
  326. package/src/lib/shell/theme-toggle/theme-toggle.ts +0 -74
  327. package/src/lib/views/calendar-view/calendar-model.ts +0 -426
  328. package/src/lib/views/calendar-view/calendar-view.spec.ts +0 -557
  329. package/src/lib/views/calendar-view/calendar-view.ts +0 -568
  330. package/src/lib/views/composer/composer-host.spec.ts +0 -301
  331. package/src/lib/views/composer/composer-host.ts +0 -248
  332. package/src/lib/views/composer/composer-imports.spec.ts +0 -54
  333. package/src/lib/views/composer/view-context.spec.ts +0 -117
  334. package/src/lib/views/composer/view-context.ts +0 -109
  335. package/src/lib/views/composer/view-slot.spec.ts +0 -130
  336. package/src/lib/views/composer/view-slot.ts +0 -178
  337. package/src/lib/views/descriptors.ts +0 -46
  338. package/src/lib/views/kanban-view/kanban-model.ts +0 -173
  339. package/src/lib/views/kanban-view/kanban-view.spec.ts +0 -540
  340. package/src/lib/views/kanban-view/kanban-view.ts +0 -327
  341. package/src/lib/widgets/bar-chart/bar-chart.descriptor.ts +0 -17
  342. package/src/lib/widgets/bar-chart/bar-chart.spec.ts +0 -66
  343. package/src/lib/widgets/bar-chart/bar-chart.ts +0 -76
  344. package/src/lib/widgets/chart-line/chart-line.descriptor.ts +0 -17
  345. package/src/lib/widgets/chart-line/chart-line.spec.ts +0 -113
  346. package/src/lib/widgets/chart-line/chart-line.ts +0 -219
  347. package/src/lib/widgets/descriptors.ts +0 -29
  348. package/src/lib/widgets/metric-card/metric-card.descriptor.ts +0 -41
  349. package/src/lib/widgets/metric-card/metric-card.spec.ts +0 -52
  350. package/src/lib/widgets/metric-card/metric-card.ts +0 -48
  351. package/src/lib/widgets/quick-actions/quick-actions.descriptor.ts +0 -13
  352. package/src/lib/widgets/quick-actions/quick-actions.spec.ts +0 -61
  353. package/src/lib/widgets/quick-actions/quick-actions.ts +0 -100
  354. package/src/lib/widgets/record-list/record-list.descriptor.ts +0 -16
  355. package/src/lib/widgets/record-list/record-list.spec.ts +0 -79
  356. package/src/lib/widgets/record-list/record-list.ts +0 -115
  357. package/src/lib/widgets/series.spec.ts +0 -42
  358. package/src/lib/widgets/series.ts +0 -56
  359. package/src/lib/widgets/status-progress/status-progress.descriptor.ts +0 -19
  360. package/src/lib/widgets/status-progress/status-progress.spec.ts +0 -63
  361. package/src/lib/widgets/status-progress/status-progress.ts +0 -104
  362. package/src/lib/widgets/widget-host/widget-host.spec.ts +0 -179
  363. package/src/lib/widgets/widget-host/widget-host.ts +0 -99
  364. package/src/public-api.ts +0 -104
  365. package/tinya-code-gas-admin-ui-0.2.0-alpha.15.tgz +0 -0
  366. package/tsconfig.lib.json +0 -13
  367. package/tsconfig.lib.prod.json +0 -11
  368. package/tsconfig.spec.json +0 -10
@@ -1,301 +0,0 @@
1
- import { Component } from '@angular/core';
2
- import { TestBed } from '@angular/core/testing';
3
- import { ActivatedRoute } from '@angular/router';
4
- import { afterEach, describe, expect, it } from 'vitest';
5
- import { of } from 'rxjs';
6
-
7
- import type { ResourceSchema, UiViewDescriptor, UiViewTabs } from '../../core/models/schema.model';
8
- import { register, unregister } from '../../core/registry/render-registry';
9
- import { ComposerHost } from './composer-host';
10
-
11
- /** Hoja ligera para los tests (se registra y se desregistra por test). */
12
- @Component({
13
- standalone: true,
14
- selector: 'app-hoja-h',
15
- template: `<p data-hoja>{{ etiqueta }}</p>`,
16
- })
17
- class HojaH {
18
- readonly etiqueta: string;
19
- constructor(route: ActivatedRoute) {
20
- let v: Record<string, unknown> = {};
21
- route.data.subscribe((d) => (v = d as Record<string, unknown>));
22
- this.etiqueta = String(v['uiView'] ?? '');
23
- }
24
- }
25
-
26
- function tabs(children: UiViewTabs['config']['children'], initial?: string): UiViewTabs {
27
- return { type: 'tabs', config: { children, ...(initial ? { initial } : {}) } };
28
- }
29
-
30
- function base(): ResourceSchema {
31
- return {
32
- id: 'demo',
33
- label: 'Demo',
34
- labelPlural: 'Demos',
35
- kind: 'collection',
36
- endpoint: {},
37
- keyField: 'id',
38
- titleField: 'name',
39
- listColumns: [],
40
- fields: [],
41
- uiViews: {},
42
- };
43
- }
44
-
45
- function esquemaCon(vista: UiViewDescriptor): ResourceSchema {
46
- return { ...base(), uiViews: { panel: vista } };
47
- }
48
-
49
- type Fixture = ReturnType<TestBed['createComponent']>;
50
-
51
- /**
52
- * Dos ciclos estables: el primero resuelve el `load()` del slot activo, el
53
- * segundo monta su componente; el tercero cubre un nivel de anidación.
54
- */
55
- async function flush(fixture: Fixture, cond?: () => boolean): Promise<Fixture> {
56
- const ciclosMax = cond === undefined ? 4 : 30;
57
- for (let ciclo = 0; ciclo < ciclosMax; ciclo += 1) {
58
- if (cond?.()) break;
59
- // Ceder la macrotarea: el `import()` dinámico de un descriptor no es
60
- // microtask puro y `whenStable` en TestBed no lo espera. Con `cond`
61
- // además se espera hasta que el DOM realmente avance (la suite completa
62
- // satura el worker y el import tarda más ciclos).
63
- await new Promise((resolve) => setTimeout(resolve, 0));
64
- await fixture.whenStable();
65
- fixture.detectChanges();
66
- }
67
- return fixture;
68
- }
69
-
70
- async function montar(schema: ResourceSchema, uiView = 'panel'): Promise<Fixture> {
71
- const datos = { schema, resourceId: schema.id, uiView, viewType: 'tabs' };
72
- TestBed.configureTestingModule({
73
- providers: [{ provide: ActivatedRoute, useValue: { data: of(datos) } }],
74
- });
75
- const fixture = TestBed.createComponent(ComposerHost);
76
- fixture.detectChanges();
77
- await flush(fixture);
78
- return fixture;
79
- }
80
-
81
- describe('ComposerHost', () => {
82
- afterEach(() => unregister('view', 'hoja-h'));
83
-
84
- it('renderiza los hijos en orden declarado con tablist accesible', async () => {
85
- register('view', { kind: 'view', type: 'hoja-h', since: '9.9', load: async () => HojaH });
86
- const fixture = await montar(
87
- esquemaCon(
88
- tabs([
89
- { id: 'a', label: 'Uno', type: 'hoja-h' },
90
- { id: 'b', label: 'Dos', type: 'hoja-h' },
91
- { id: 'c', label: 'Tres', type: 'hoja-h' },
92
- ]),
93
- ),
94
- );
95
- const tabsDom = [...fixture.nativeElement.querySelectorAll('[role="tab"]')];
96
- expect(tabsDom.map((t) => t.textContent?.trim())).toEqual(['Uno', 'Dos', 'Tres']);
97
- expect(fixture.nativeElement.querySelector('[role="tablist"]')).toBeTruthy();
98
- // el primer hijo está activo por defecto: sólo su panel está montado
99
- expect(fixture.nativeElement.querySelectorAll('[role="tabpanel"]').length).toBe(1);
100
- expect(tabsDom[0].getAttribute('aria-selected')).toBe('true');
101
- expect(tabsDom[0].getAttribute('tabindex')).toBe('0');
102
- expect(tabsDom[1].getAttribute('tabindex')).toBe('-1');
103
- });
104
-
105
- it('respeta `initial` para la pestaña activa al montar', async () => {
106
- register('view', { kind: 'view', type: 'hoja-h', since: '9.9', load: async () => HojaH });
107
- const fixture = await montar(
108
- esquemaCon(
109
- tabs(
110
- [
111
- { id: 'a', label: 'Uno', type: 'hoja-h' },
112
- { id: 'b', label: 'Dos', type: 'hoja-h' },
113
- ],
114
- 'b',
115
- ),
116
- ),
117
- );
118
- const selected = [...fixture.nativeElement.querySelectorAll('[role="tab"]')].filter(
119
- (t) => t.getAttribute('aria-selected') === 'true',
120
- );
121
- expect(selected.map((t) => t.textContent?.trim())).toEqual(['Dos']);
122
- expect(fixture.nativeElement.querySelector('[role="tabpanel"]')?.textContent).toContain(
123
- 'panel/b',
124
- );
125
- });
126
-
127
- it('clic en pestaña activa el panel y destruye el no retenido', async () => {
128
- register('view', { kind: 'view', type: 'hoja-h', since: '9.9', load: async () => HojaH });
129
- const fixture = await montar(
130
- esquemaCon(
131
- tabs([
132
- { id: 'a', label: 'Uno', type: 'hoja-h' },
133
- { id: 'b', label: 'Dos', type: 'hoja-h' },
134
- ]),
135
- ),
136
- );
137
- const panels = () => fixture.nativeElement.querySelectorAll('[role="tabpanel"]');
138
- expect(panels().length).toBe(1);
139
- expect(panels()[0].textContent).toContain('panel/a');
140
- [...fixture.nativeElement.querySelectorAll('[role="tab"]')][1].click();
141
- await flush(fixture);
142
- expect(panels().length).toBe(1);
143
- expect(panels()[0].textContent).toContain('panel/b');
144
- // no retenido: el panel de 'a' ya no existe en el DOM
145
- expect(panels()[0].textContent).not.toContain('panel/a');
146
- });
147
-
148
- it('retain:true conserva el panel montado pero oculto tras cambiar de pestaña', async () => {
149
- register('view', { kind: 'view', type: 'hoja-h', since: '9.9', load: async () => HojaH });
150
- const fixture = await montar(
151
- esquemaCon(
152
- tabs([
153
- { id: 'a', label: 'Uno', type: 'hoja-h', retain: true },
154
- { id: 'b', label: 'Dos', type: 'hoja-h' },
155
- ]),
156
- ),
157
- );
158
- [...fixture.nativeElement.querySelectorAll('[role="tab"]')][1].click();
159
- await flush(fixture);
160
- const panels = [...fixture.nativeElement.querySelectorAll('[role="tabpanel"]')];
161
- expect(panels.length).toBe(2);
162
- const oculto = panels.find((p) => p.textContent?.includes('panel/a'));
163
- expect(oculto).toBeTruthy();
164
- expect(oculto!.classList.contains('hidden')).toBe(true);
165
- const visible = panels.find((p) => p.textContent?.includes('panel/b'));
166
- expect(visible!.classList.contains('hidden')).toBe(false);
167
- });
168
-
169
- it('flechas/Home/End mueven la selección y el tabindex (roving)', async () => {
170
- register('view', { kind: 'view', type: 'hoja-h', since: '9.9', load: async () => HojaH });
171
- const fixture = await montar(
172
- esquemaCon(
173
- tabs([
174
- { id: 'a', label: 'Uno', type: 'hoja-h' },
175
- { id: 'b', label: 'Dos', type: 'hoja-h' },
176
- { id: 'c', label: 'Tres', type: 'hoja-h' },
177
- ]),
178
- ),
179
- );
180
- const tablist = fixture.nativeElement.querySelector('[role="tablist"]');
181
- const tabsDom = [...tablist.querySelectorAll('[role="tab"]')];
182
- const tecla = (key: string) => {
183
- tablist.dispatchEvent(new KeyboardEvent('keydown', { key, bubbles: true }));
184
- fixture.detectChanges();
185
- };
186
- tecla('ArrowRight');
187
- expect(tabsDom[1].getAttribute('aria-selected')).toBe('true');
188
- expect(tabsDom[1].getAttribute('tabindex')).toBe('0');
189
- expect(tabsDom[0].getAttribute('tabindex')).toBe('-1');
190
- tecla('End');
191
- expect(tabsDom[2].getAttribute('aria-selected')).toBe('true');
192
- tecla('Home');
193
- expect(tabsDom[0].getAttribute('aria-selected')).toBe('true');
194
- tecla('ArrowLeft'); // desde el primero ⇒ envuelve al último
195
- expect(tabsDom[2].getAttribute('aria-selected')).toBe('true');
196
- });
197
-
198
- it('config inválida (no-tabs) ⇒ estado de error, sin montar hijos', async () => {
199
- const fixture = await montar(esquemaCon({ type: 'list' }));
200
- expect(fixture.nativeElement.textContent).toContain('inválida');
201
- expect(fixture.nativeElement.querySelector('[role="tablist"]')).toBeFalsy();
202
- });
203
-
204
- it('ciclo de composición ⇒ error validado antes del render (D2)', async () => {
205
- const conRef: UiViewTabs = {
206
- type: 'tabs',
207
- config: { children: [{ id: 'loop', label: 'Loop', ref: 'panel' }] },
208
- };
209
- const fixture = await montar(esquemaCon(conRef));
210
- expect(fixture.nativeElement.textContent).toContain('ciclo');
211
- expect(fixture.nativeElement.querySelector('[role="tablist"]')).toBeFalsy();
212
- });
213
-
214
- it('profundidad sobre el límite ⇒ error validado antes del render (D2)', async () => {
215
- register('view', { kind: 'view', type: 'hoja-h', since: '9.9', load: async () => HojaH });
216
- const schema: ResourceSchema = {
217
- ...base(),
218
- uiViews: {
219
- a: tabs([{ id: 'n1', label: 'N1', ref: 'b' }]),
220
- b: tabs([{ id: 'n2', label: 'N2', ref: 'c' }]),
221
- c: tabs([{ id: 'n3', label: 'N3', ref: 'd' }]),
222
- d: tabs([{ id: 'n4', label: 'N4', type: 'hoja-h' }]),
223
- },
224
- };
225
- const fixture = await montar(schema, 'a');
226
- expect(fixture.nativeElement.textContent).toContain('profundidad');
227
- expect(fixture.nativeElement.querySelector('[role="tablist"]')).toBeFalsy();
228
- });
229
-
230
- it('visibleWhen sin record evalúa contra vacío: empty pasa, eq no', async () => {
231
- register('view', { kind: 'view', type: 'hoja-h', since: '9.9', load: async () => HojaH });
232
- const fixture = await montar(
233
- esquemaCon(
234
- tabs([
235
- { id: 'a', label: 'Siempre', type: 'hoja-h' },
236
- {
237
- id: 'b',
238
- label: 'Condicional',
239
- type: 'hoja-h',
240
- visibleWhen: [{ field: 'estado', operator: 'eq', value: 'activo' }],
241
- },
242
- {
243
- id: 'c',
244
- label: 'Sin filtro',
245
- type: 'hoja-h',
246
- visibleWhen: [{ field: 'estado', operator: 'empty' }],
247
- },
248
- ]),
249
- ),
250
- );
251
- const labels = [...fixture.nativeElement.querySelectorAll('[role="tab"]')].map((t) =>
252
- t.textContent?.trim(),
253
- );
254
- expect(labels).toEqual(['Siempre', 'Sin filtro']);
255
- });
256
-
257
- it('si la pestaña activa queda oculta, cae visiblemente a la primera visible', async () => {
258
- register('view', { kind: 'view', type: 'hoja-h', since: '9.9', load: async () => HojaH });
259
- const fixture = await montar(
260
- esquemaCon(
261
- tabs([
262
- {
263
- id: 'oculta',
264
- label: 'Oculta',
265
- type: 'hoja-h',
266
- visibleWhen: [{ field: 'estado', operator: 'eq', value: 'activo' }],
267
- },
268
- { id: 'visible', label: 'Visible', type: 'hoja-h' },
269
- ]),
270
- ),
271
- );
272
- const tabsDom = [...fixture.nativeElement.querySelectorAll('[role="tab"]')];
273
- expect(tabsDom.map((t) => t.textContent?.trim())).toEqual(['Visible']);
274
- expect(tabsDom[0].getAttribute('aria-selected')).toBe('true');
275
- expect(fixture.nativeElement.querySelector('[role="tabpanel"]')?.textContent).toContain(
276
- 'panel/visible',
277
- );
278
- });
279
-
280
- it('anida tabs inline: el hijo contenedor monta sus propios hijos', async () => {
281
- register('view', { kind: 'view', type: 'hoja-h', since: '9.9', load: async () => HojaH });
282
- const anidado: UiViewTabs = tabs([
283
- {
284
- id: 'hijo',
285
- label: 'Hijo',
286
- type: 'tabs',
287
- config: { children: [{ id: 'nieto', label: 'Nieto', type: 'hoja-h' }] },
288
- },
289
- ]);
290
- const fixture = await montar(esquemaCon(anidado));
291
- await flush(fixture, () => fixture.nativeElement.querySelectorAll('[role="tab"]').length >= 2);
292
- const labels = [...fixture.nativeElement.querySelectorAll('[role="tab"]')].map((t) =>
293
- t.textContent?.trim(),
294
- );
295
- expect(labels).toEqual(['Hijo', 'Nieto']);
296
- // el nieto vive bajo instanceId de cadena: panel/hijo (parentInstanceId)
297
- expect(fixture.nativeElement.querySelector('[role="tabpanel"]')?.textContent).toContain(
298
- 'panel/hijo',
299
- );
300
- });
301
- });
@@ -1,248 +0,0 @@
1
- import { ChangeDetectionStrategy, Component, computed, inject, signal } from '@angular/core';
2
- import { ActivatedRoute } from '@angular/router';
3
- import { toSignal } from '@angular/core/rxjs-interop';
4
-
5
- import type { ResourceSchema, UiViewTabs, ViewChildNode } from '../../core/models/schema.model';
6
- import { validateComposition } from '../../core/registry/composition-validator';
7
- import { getSchema } from '../../core/registry/schema-catalog';
8
- import { EmptyState } from '../../shared/components/empty-state/empty-state';
9
- import { evaluateCondition } from '../../shared/utils/condition-evaluator';
10
- import { ViewSlot } from './view-slot';
11
-
12
- /**
13
- * R9.9 · slice 3 — Host genérico de composición (`uiView` tipo `tabs`).
14
- *
15
- * Un SOLO componente recursivo renderiza cualquier composición declarada:
16
- * resuelve cada hijo por `(kind:'view', type)` dentro del `ViewSlot` (D2,
17
- * sin `switch` de dominio y sin importar hojas), monta la validación del
18
- * schema ANTES del primer render (fail-loud) y maneja el tablist accesible
19
- * con roving tabindex (R9.9.5).
20
- *
21
- * Decisiones R9.9.5:
22
- * - Retención: un hijo `retain:true` visitado queda MONTADO pero oculto
23
- * (`hidden`); sin `retain` se destruye al cambiar de pestaña.
24
- * - Pestaña activa LOCAL por componente: no hay estado compartido entre
25
- * instancias de tabs anidadas (documentado en los docs de slice 5).
26
- * - `visibleWhen` se evalúa sin record (el contenedor no es un formulario):
27
- * contra registro vacío, TODAS las condiciones deben pasar (AND).
28
- * Si la pestaña activa queda oculta, la selección cae visiblemente a la
29
- * primera visible — nunca se pierde en silencio.
30
- */
31
- @Component({
32
- selector: 'app-composer-host',
33
- standalone: true,
34
- imports: [ViewSlot, EmptyState],
35
- changeDetection: ChangeDetectionStrategy.OnPush,
36
- template: `
37
- @if (error() !== null) {
38
- <app-empty-state title="Composición no disponible" [message]="error()!" />
39
- } @else if (visibles().length === 0) {
40
- <app-empty-state
41
- title="Sin secciones disponibles"
42
- message="La vista no declara secciones visibles."
43
- />
44
- } @else {
45
- <div
46
- role="tablist"
47
- class="flex flex-wrap gap-1 border-b border-neutral/20 px-1"
48
- [attr.aria-label]="etiqueta()"
49
- (keydown)="onKeydown($event)"
50
- >
51
- @for (h of visibles(); track h.id) {
52
- <button
53
- type="button"
54
- role="tab"
55
- [id]="'tab-' + h.id"
56
- [attr.aria-selected]="h.id === activo()"
57
- [attr.aria-controls]="montado(h.id) ? 'panel-' + h.id : null"
58
- [attr.tabindex]="h.id === activo() ? 0 : -1"
59
- [class]="claseTab(h.id)"
60
- (click)="seleccionar(h.id, true)"
61
- >
62
- {{ h.label }}
63
- </button>
64
- }
65
- </div>
66
-
67
- @for (h of montados(); track h.id) {
68
- <div
69
- role="tabpanel"
70
- [id]="'panel-' + h.id"
71
- [attr.aria-labelledby]="'tab-' + h.id"
72
- [class.hidden]="h.id !== activo()"
73
- class="py-3 focus:outline-none"
74
- tabindex="0"
75
- >
76
- <app-view-slot
77
- [schema]="schema()!"
78
- [node]="h"
79
- [parentInstanceId]="uiViewName()"
80
- [depth]="profundidad() + 1"
81
- />
82
- </div>
83
- }
84
- }
85
- `,
86
- })
87
- export class ComposerHost {
88
- private readonly route = inject(ActivatedRoute);
89
- private readonly routeData = toSignal(this.route.data, { requireSync: true });
90
-
91
- /** Schema: explícito en `data` (fixtures/ruta embebida) o vía `resourceId`. */
92
- readonly schema = computed<ResourceSchema | undefined>(() => {
93
- const data = this.routeData();
94
- const explicit = data['schema'];
95
- if (explicit !== undefined) return explicit as ResourceSchema;
96
- const resourceId = data['resourceId'];
97
- return typeof resourceId === 'string' ? getSchema(resourceId) : undefined;
98
- });
99
-
100
- /** instanceId de ESTE contenedor (raíz ⇒ nombre de la uiView de ruta). */
101
- readonly uiViewName = computed(() => {
102
- const v = this.routeData()['uiView'];
103
- return typeof v === 'string' ? v : '';
104
- });
105
-
106
- /** Profundidad de ESTE contenedor (raíz ⇒ 0; embebido ⇒ payload.depth). */
107
- readonly profundidad = computed(() => {
108
- const d = this.routeData()['depth'];
109
- return typeof d === 'number' ? d : 0;
110
- });
111
-
112
- /** Config `tabs` declarada para esta instancia. */
113
- readonly vista = computed<UiViewTabs | undefined>(() => {
114
- const schema = this.schema();
115
- const nombre = this.uiViewName();
116
- if (schema === undefined || nombre === '') return undefined;
117
- const vista = schema.uiViews?.[nombre];
118
- // `'config' in` descarta UiViewBase; el literal descarta calendar/kanban.
119
- return vista !== undefined && 'config' in vista && vista.type === 'tabs' ? vista : undefined;
120
- });
121
-
122
- /** Error determinista de configuración (schema/vista ausente o no-tabs). */
123
- private readonly errorConfig = computed<string | null>(() => {
124
- const data = this.routeData();
125
- const schema = this.schema();
126
- const nombre = this.uiViewName();
127
- if (schema === undefined) {
128
- return `Schema no encontrado para '${String(data['resourceId'] ?? '')}'`;
129
- }
130
- if (nombre === '') return 'Ruta de composición sin uiView declarada';
131
- const vista = schema.uiViews?.[nombre];
132
- if (vista === undefined) return `Vista '${nombre}' no declarada en ${schema.id}`;
133
- if (vista.type !== 'tabs') {
134
- return `Configuración inválida: ${schema.id}.${nombre} declara '${vista.type}', no es una vista tabs`;
135
- }
136
- return null;
137
- });
138
-
139
- /** Error de validación D2 (se recalcula una sola vez, en el constructor). */
140
- private readonly errorValidacion = signal<string | null>(null);
141
-
142
- /** Error mostrado: validación del schema primero, luego configuración. */
143
- readonly error = computed<string | null>(() => this.errorValidacion() ?? this.errorConfig());
144
-
145
- /** Todos los hijos declarados (orden de declaración). */
146
- private readonly hijos = computed<readonly ViewChildNode[]>(
147
- () => this.vista()?.config.children ?? [],
148
- );
149
-
150
- /** Hijos visibles: TODAS sus condiciones `visibleWhen` pasan sin record. */
151
- readonly visibles = computed<readonly ViewChildNode[]>(() =>
152
- this.hijos().filter(
153
- (h) =>
154
- h.visibleWhen === undefined ||
155
- h.visibleWhen.every((cond) => evaluateCondition(cond, {}, {})),
156
- ),
157
- );
158
-
159
- /** Selección del usuario (intención; vacío ⇒ usar `initial`/primera). */
160
- private readonly seleccion = signal('');
161
-
162
- /**
163
- * Pestaña activa EFECTIVA: derivada pura de la selección y de las
164
- * visibles — si la selección dejó de existir (u oculta), cae a `initial`
165
- * o a la primera visible de forma SINCRÓNICA y visible (R9.9.5: nunca
166
- * perder la vista elegida en silencio). Estado LOCAL por instancia.
167
- */
168
- readonly activo = computed<string>(() => {
169
- const vis = this.visibles();
170
- if (vis.length === 0) return '';
171
- const sel = this.seleccion();
172
- if (vis.some((h) => h.id === sel)) return sel;
173
- const inicial = this.vista()?.config.initial;
174
- return inicial !== undefined && vis.some((h) => h.id === inicial) ? inicial : vis[0].id;
175
- });
176
-
177
- /** Ids ya seleccionados alguna vez (sustento de la retención). */
178
- private readonly visitados = signal<ReadonlySet<string>>(new Set());
179
-
180
- /** Paneles en DOM: activo + retenidos visitados que siguen visibles. */
181
- readonly montados = computed<readonly ViewChildNode[]>(() => {
182
- const visibles = new Set(this.visibles().map((h) => h.id));
183
- const activo = this.activo();
184
- const visitados = this.visitados();
185
- return this.hijos().filter(
186
- (h) => visibles.has(h.id) && (h.id === activo || (h.retain === true && visitados.has(h.id))),
187
- );
188
- });
189
-
190
- constructor() {
191
- // D2 — validar TODO el schema antes del primer render (fail-loud).
192
- const schema = this.schema();
193
- if (schema !== undefined) {
194
- try {
195
- validateComposition(schema);
196
- } catch (e) {
197
- this.errorValidacion.set(e instanceof Error ? e.message : String(e));
198
- }
199
- }
200
- // La pestaña activa al montar cuenta como visitada: al cambiar de
201
- // pestaña, un hijo `retain:true` que estaba activo conserva su panel.
202
- const inicial = this.activo();
203
- if (inicial !== '') this.visitados.set(new Set([inicial]));
204
- }
205
-
206
- /** Selecciona una pestaña y, opcionalmente, mueve el foco a su botón. */
207
- seleccionar(id: string, conFoco: boolean): void {
208
- this.seleccion.set(id);
209
- this.visitados.update((v) => new Set(v).add(id));
210
- if (conFoco) {
211
- setTimeout(() => document.getElementById(`tab-${id}`)?.focus(), 0);
212
- }
213
- }
214
-
215
- /** Navegación por teclado del tablist: flechas con wrap, Home/End. */
216
- onKeydown(event: KeyboardEvent): void {
217
- const vis = this.visibles();
218
- if (vis.length === 0) return;
219
- const i = vis.findIndex((h) => h.id === this.activo());
220
- let next = -1;
221
- if (event.key === 'ArrowRight') next = (i + 1) % vis.length;
222
- else if (event.key === 'ArrowLeft') next = (i - 1 + vis.length) % vis.length;
223
- else if (event.key === 'Home') next = 0;
224
- else if (event.key === 'End') next = vis.length - 1;
225
- if (next < 0) return;
226
- event.preventDefault();
227
- this.seleccionar(vis[next].id, true);
228
- }
229
-
230
- /** El panel de este hijo está montado (⇒ existe su `aria-controls`). */
231
- montado(id: string): boolean {
232
- return this.montados().some((h) => h.id === id);
233
- }
234
-
235
- /** Clases del botón de pestaña: activo resaltado con token `primary`. */
236
- claseTab(id: string): string {
237
- const base =
238
- 'rounded-t-lg px-3 py-2 text-sm transition-colors focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-primary';
239
- return id === this.activo()
240
- ? `${base} bg-primary/10 font-medium text-primary`
241
- : `${base} text-neutral hover:text-primary`;
242
- }
243
-
244
- /** Etiqueta a11y del tablist (la del recurso). */
245
- etiqueta(): string {
246
- return this.schema()?.labelPlural ?? 'Secciones';
247
- }
248
- }
@@ -1,54 +0,0 @@
1
- // @ts-ignore — `node:fs` no tiene tipos en este proyecto (sin @types/node);
2
- // el runtime de tests corre en Node y el builtin resuelve igual.
3
- import { readFileSync } from 'node:fs';
4
- // @ts-ignore — idem para `node:url`.
5
- import { fileURLToPath } from 'node:url';
6
-
7
- import { describe, expect, it } from 'vitest';
8
-
9
- /**
10
- * R9.9 · slice 3 — Guardia arquitectónica sobre el compositor (D2).
11
- *
12
- * El host debe ser GENÉRICO: sin imports de hojas, sin strings de dominio y
13
- * sin `switch` de tipos — cualquier página nueva declarada en un schema se
14
- * compone sin tocar el compositor. Este test lee el fuente REAL desde disco
15
- * (no el bundle) y falla si aparece un acoplamiento al dominio.
16
- */
17
- function fuente(nombre: string): string {
18
- return readFileSync(fileURLToPath(new URL(nombre, import.meta.url)), 'utf8');
19
- }
20
-
21
- const COMPOSITOR = ['composer-host.ts', 'view-slot.ts', 'view-context.ts'];
22
-
23
- describe('composer source-scan: el compositor no conoce el dominio', () => {
24
- it('no importa hojas, páginas ni vistas de dominio', () => {
25
- for (const nombre of COMPOSITOR) {
26
- const codigo = fuente(nombre);
27
- expect(codigo, `${nombre} importa una hoja/página de dominio`).not.toMatch(
28
- /from ['"](\.\.\/)+(pages|views\/(calendar-view|kanban-view))/,
29
- );
30
- expect(codigo, `${nombre} referencia una hoja por import dinámico`).not.toMatch(
31
- /import\(['"](\.\.\/)+(pages|views\/(calendar-view|kanban-view))/,
32
- );
33
- expect(codigo, `${nombre} menciona un tipo de dominio`).not.toMatch(
34
- /'calendar'|'kanban'|'resource-page'|'form-view'|'detail-view'/,
35
- );
36
- }
37
- });
38
-
39
- it('no contiene un switch de tipos (dispatch exclusivamente por registro)', () => {
40
- for (const nombre of COMPOSITOR) {
41
- expect(fuente(nombre), `${nombre} contiene un switch`).not.toMatch(/@?switch\s*\(/);
42
- }
43
- });
44
-
45
- it('resuelve hijos sólo vía get(kind, type) del registro, nunca resolve()', () => {
46
- for (const nombre of ['view-slot.ts']) {
47
- const codigo = fuente(nombre);
48
- expect(codigo, `${nombre} usa resolve() (fallback silencioso prohibido)`).not.toMatch(
49
- /\bresolve\(\s*'view'/,
50
- );
51
- expect(codigo, `${nombre} no usa get('view', type)`).toMatch(/get\(\s*'view'\s*,/);
52
- }
53
- });
54
- });