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

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 (369) hide show
  1. package/README.md +222 -0
  2. package/ng-package.json +7 -0
  3. package/package.json +3 -15
  4. package/src/lib/actions/action-registry.spec.ts +52 -0
  5. package/src/lib/actions/action-registry.ts +126 -0
  6. package/src/lib/actions/action-service.spec.ts +215 -0
  7. package/src/lib/actions/action-service.ts +87 -0
  8. package/src/lib/actions/descriptors.spec.ts +155 -0
  9. package/src/lib/actions/descriptors.ts +154 -0
  10. package/src/lib/columns/column-cell.ts +33 -0
  11. package/src/lib/columns/column-defaults.spec.ts +99 -0
  12. package/src/lib/columns/column-defaults.ts +84 -0
  13. package/src/lib/columns/column-format.spec.ts +274 -0
  14. package/src/lib/columns/descriptors.ts +113 -0
  15. package/src/lib/columns/format-badge.ts +51 -0
  16. package/src/lib/columns/format-boolean.ts +58 -0
  17. package/src/lib/columns/format-currency.ts +30 -0
  18. package/src/lib/columns/format-date.ts +35 -0
  19. package/src/lib/columns/format-number.ts +32 -0
  20. package/src/lib/columns/format-text.ts +23 -0
  21. package/src/lib/columns/format-thumbnail.ts +63 -0
  22. package/src/lib/core/adapters/firebase-auth.adapter.ts +6 -0
  23. package/src/lib/core/guards/auth.guard.spec.ts +88 -0
  24. package/src/lib/core/guards/auth.guard.ts +48 -0
  25. package/src/lib/core/guards/unsaved-changes.guard.ts +16 -0
  26. package/src/lib/core/interceptors/api-error.interceptor.spec.ts +182 -0
  27. package/src/lib/core/interceptors/api-error.interceptor.ts +87 -0
  28. package/src/lib/core/models/api.model.ts +104 -0
  29. package/src/lib/core/models/schema-descriptions.ts +118 -0
  30. package/src/lib/core/models/schema.model.ts +1097 -0
  31. package/src/lib/core/registry/composition-validator.spec.ts +358 -0
  32. package/src/lib/core/registry/composition-validator.ts +270 -0
  33. package/src/lib/core/registry/field-validators.spec.ts +199 -0
  34. package/src/lib/core/registry/field-validators.ts +440 -0
  35. package/src/lib/core/registry/render-host.spec.ts +208 -0
  36. package/src/lib/core/registry/render-host.ts +646 -0
  37. package/src/lib/core/registry/render-registry.spec.ts +96 -0
  38. package/src/lib/core/registry/render-registry.ts +312 -0
  39. package/src/lib/core/registry/schema-catalog.ts +156 -0
  40. package/src/lib/core/services/api.service.spec.ts +1142 -0
  41. package/src/lib/core/services/api.service.ts +371 -0
  42. package/src/lib/core/services/auth.service.spec.ts +653 -0
  43. package/src/lib/core/services/auth.service.ts +503 -0
  44. package/src/lib/core/services/config.service.spec.ts +121 -0
  45. package/src/lib/core/services/config.service.ts +119 -0
  46. package/src/lib/core/services/data-source.service.spec.ts +179 -0
  47. package/src/lib/core/services/data-source.service.ts +142 -0
  48. package/src/lib/core/services/error-mapper.service.spec.ts +50 -0
  49. package/src/lib/core/services/error-mapper.service.ts +49 -0
  50. package/src/lib/core/services/notification.service.ts +73 -0
  51. package/src/lib/core/services/relation-drawer.service.ts +41 -0
  52. package/src/lib/core/services/theme.service.spec.ts +106 -0
  53. package/src/lib/core/services/theme.service.ts +86 -0
  54. package/src/lib/core/services/transport.spec.ts +198 -0
  55. package/src/lib/core/services/transport.ts +164 -0
  56. package/src/lib/core/services/upload.service.ts +81 -0
  57. package/src/lib/core/testing/categories.schema.ts +125 -0
  58. package/src/lib/core/testing/orders.schema.ts +372 -0
  59. package/src/lib/core/testing/products.schema.ts +258 -0
  60. package/src/lib/core/testing/site.schema.ts +195 -0
  61. package/src/lib/core/testing/test-catalog.ts +21 -0
  62. package/src/lib/core/tokens.ts +112 -0
  63. package/src/lib/core/utils/clipboard.spec.ts +55 -0
  64. package/src/lib/core/utils/clipboard.ts +90 -0
  65. package/src/lib/core/utils/context-resolver.spec.ts +95 -0
  66. package/src/lib/core/utils/context-resolver.ts +101 -0
  67. package/src/lib/core/utils/response-unwrapper.spec.ts +70 -0
  68. package/src/lib/core/utils/response-unwrapper.ts +103 -0
  69. package/src/lib/core/utils/source-options.spec.ts +96 -0
  70. package/src/lib/core/utils/source-options.ts +54 -0
  71. package/src/lib/core/utils/template-interpolator.spec.ts +59 -0
  72. package/src/lib/core/utils/template-interpolator.ts +56 -0
  73. package/src/lib/core/view-navigation/view-navigation.service.spec.ts +207 -0
  74. package/src/lib/core/view-navigation/view-navigation.service.ts +132 -0
  75. package/src/lib/fields/boolean/boolean.descriptor.ts +11 -0
  76. package/src/lib/fields/boolean/boolean.ts +40 -0
  77. package/src/lib/fields/color/color.descriptor.ts +12 -0
  78. package/src/lib/fields/color/color.ts +34 -0
  79. package/src/lib/fields/currency/currency.descriptor.ts +12 -0
  80. package/src/lib/fields/currency/currency.ts +67 -0
  81. package/src/lib/fields/date/date.descriptor.ts +12 -0
  82. package/src/lib/fields/date/date.ts +36 -0
  83. package/src/lib/fields/descriptors.ts +122 -0
  84. package/src/lib/fields/display/display-badge.ts +65 -0
  85. package/src/lib/fields/display/display-date.ts +39 -0
  86. package/src/lib/fields/display/display-defaults.ts +84 -0
  87. package/src/lib/fields/display/display-file.ts +41 -0
  88. package/src/lib/fields/display/display-image.ts +54 -0
  89. package/src/lib/fields/display/display-list.ts +107 -0
  90. package/src/lib/fields/display/display-number.ts +41 -0
  91. package/src/lib/fields/display/display-text.ts +42 -0
  92. package/src/lib/fields/display/display.spec.ts +242 -0
  93. package/src/lib/fields/dropdown/dropdown.descriptor.ts +12 -0
  94. package/src/lib/fields/dropdown/dropdown.ts +45 -0
  95. package/src/lib/fields/email/email.descriptor.ts +12 -0
  96. package/src/lib/fields/email/email.ts +28 -0
  97. package/src/lib/fields/field-aria.ts +60 -0
  98. package/src/lib/fields/field-host/field-host.spec.ts +118 -0
  99. package/src/lib/fields/field-host/field-host.ts +47 -0
  100. package/src/lib/fields/field-node.ts +88 -0
  101. package/src/lib/fields/file/file.descriptor.ts +14 -0
  102. package/src/lib/fields/file/file.spec.ts +134 -0
  103. package/src/lib/fields/file/file.ts +281 -0
  104. package/src/lib/fields/group/group.descriptor.ts +21 -0
  105. package/src/lib/fields/group/group.ts +110 -0
  106. package/src/lib/fields/image/image.descriptor.ts +12 -0
  107. package/src/lib/fields/image/image.spec.ts +117 -0
  108. package/src/lib/fields/image/image.ts +281 -0
  109. package/src/lib/fields/key-value/key-value.descriptor.ts +11 -0
  110. package/src/lib/fields/key-value/key-value.ts +283 -0
  111. package/src/lib/fields/list/list.descriptor.ts +21 -0
  112. package/src/lib/fields/list/list.ts +376 -0
  113. package/src/lib/fields/multiselect/multiselect.descriptor.ts +11 -0
  114. package/src/lib/fields/multiselect/multiselect.spec.ts +142 -0
  115. package/src/lib/fields/multiselect/multiselect.ts +152 -0
  116. package/src/lib/fields/number/number.descriptor.ts +12 -0
  117. package/src/lib/fields/number/number.ts +40 -0
  118. package/src/lib/fields/phone/phone.descriptor.ts +12 -0
  119. package/src/lib/fields/phone/phone.ts +46 -0
  120. package/src/lib/fields/readonly-text/readonly-text.descriptor.ts +12 -0
  121. package/src/lib/fields/readonly-text/readonly-text.ts +58 -0
  122. package/src/lib/fields/relation/relation.descriptor.ts +12 -0
  123. package/src/lib/fields/relation/relation.spec.ts +193 -0
  124. package/src/lib/fields/relation/relation.ts +224 -0
  125. package/src/lib/fields/rich-select/rich-select.descriptor.ts +12 -0
  126. package/src/lib/fields/rich-select/rich-select.ts +175 -0
  127. package/src/lib/fields/select/radio-mode.ts +25 -0
  128. package/src/lib/fields/select/select.descriptor.ts +12 -0
  129. package/src/lib/fields/select/select.spec.ts +162 -0
  130. package/src/lib/fields/select/select.ts +210 -0
  131. package/src/lib/fields/slug/slug.descriptor.ts +19 -0
  132. package/src/lib/fields/slug/slug.ts +67 -0
  133. package/src/lib/fields/string-list/string-list.descriptor.ts +11 -0
  134. package/src/lib/fields/string-list/string-list.ts +222 -0
  135. package/src/lib/fields/text/text.descriptor.ts +12 -0
  136. package/src/lib/fields/text/text.ts +35 -0
  137. package/src/lib/fields/textarea/textarea.descriptor.ts +12 -0
  138. package/src/lib/fields/textarea/textarea.spec.ts +117 -0
  139. package/src/lib/fields/textarea/textarea.ts +126 -0
  140. package/src/lib/fields/time/time.descriptor.ts +12 -0
  141. package/src/lib/fields/time/time.ts +28 -0
  142. package/src/lib/fields/unknown-render/unknown-render.ts +49 -0
  143. package/src/lib/fields/url/url.descriptor.ts +12 -0
  144. package/src/lib/fields/url/url.ts +53 -0
  145. package/src/lib/gas-admin-ui.spec.ts +21 -0
  146. package/src/lib/gas-admin-ui.ts +9 -0
  147. package/src/lib/layouts/descriptors.ts +9 -0
  148. package/src/lib/pages/dashboard/dashboard.ts +167 -0
  149. package/src/lib/pages/detail-view/detail-view.spec.ts +333 -0
  150. package/src/lib/pages/detail-view/detail-view.ts +570 -0
  151. package/src/lib/pages/drawer-form/drawer-form.ts +228 -0
  152. package/src/lib/pages/form-view/form-model.ts +88 -0
  153. package/src/lib/pages/form-view/form-schema.ts +255 -0
  154. package/src/lib/pages/form-view/form-view.ts +1622 -0
  155. package/src/lib/pages/list-view/list-view.formats.spec.ts +214 -0
  156. package/src/lib/pages/list-view/list-view.ts +882 -0
  157. package/src/lib/pages/login/login.spec.ts +109 -0
  158. package/src/lib/pages/login/login.ts +227 -0
  159. package/src/lib/pages/resource-page/resource-page.ts +234 -0
  160. package/src/lib/shared/components/badge/badge.ts +35 -0
  161. package/src/lib/shared/components/button/button.ts +65 -0
  162. package/src/lib/shared/components/confirm-dialog/confirm-dialog.ts +48 -0
  163. package/src/lib/shared/components/copy-button/copy-button.ts +56 -0
  164. package/src/lib/shared/components/copy-share/copy-share.spec.ts +122 -0
  165. package/src/lib/shared/components/copy-share/copy-share.ts +107 -0
  166. package/src/lib/shared/components/drawer/drawer.spec.ts +127 -0
  167. package/src/lib/shared/components/drawer/drawer.ts +172 -0
  168. package/src/lib/shared/components/empty-state/empty-state.ts +30 -0
  169. package/src/lib/shared/components/modal/modal.spec.ts +149 -0
  170. package/src/lib/shared/components/modal/modal.ts +145 -0
  171. package/src/lib/shared/components/post-create-modal/post-create-modal.spec.ts +165 -0
  172. package/src/lib/shared/components/post-create-modal/post-create-modal.ts +159 -0
  173. package/src/lib/shared/components/skeleton/skeleton.ts +31 -0
  174. package/src/lib/shared/components/toast/toast.ts +58 -0
  175. package/src/lib/shared/utils/col-span.ts +25 -0
  176. package/src/lib/shared/utils/condition-evaluator.ts +53 -0
  177. package/src/lib/shared/utils/depends-on.ts +48 -0
  178. package/src/lib/shared/utils/dialog-focus.ts +28 -0
  179. package/src/lib/shared/utils/field-path.ts +49 -0
  180. package/src/lib/shared/utils/gap-sorting.ts +144 -0
  181. package/src/lib/shared/utils/seed.spec.ts +64 -0
  182. package/src/lib/shared/utils/seed.ts +149 -0
  183. package/src/lib/shared/utils/slugify.ts +13 -0
  184. package/src/lib/shared/utils/validators.ts +41 -0
  185. package/src/lib/shell/layout/layout.ts +63 -0
  186. package/src/lib/shell/shell.routes.spec.ts +59 -0
  187. package/src/lib/shell/shell.routes.ts +122 -0
  188. package/src/lib/shell/sidebar/sidebar.ts +139 -0
  189. package/src/lib/shell/theme-toggle/theme-toggle.ts +74 -0
  190. package/src/lib/views/calendar-view/calendar-model.ts +426 -0
  191. package/src/lib/views/calendar-view/calendar-view.spec.ts +557 -0
  192. package/src/lib/views/calendar-view/calendar-view.ts +568 -0
  193. package/src/lib/views/composer/composer-host.spec.ts +301 -0
  194. package/src/lib/views/composer/composer-host.ts +248 -0
  195. package/src/lib/views/composer/composer-imports.spec.ts +54 -0
  196. package/src/lib/views/composer/view-context.spec.ts +117 -0
  197. package/src/lib/views/composer/view-context.ts +109 -0
  198. package/src/lib/views/composer/view-slot.spec.ts +130 -0
  199. package/src/lib/views/composer/view-slot.ts +178 -0
  200. package/src/lib/views/descriptors.ts +46 -0
  201. package/src/lib/views/kanban-view/kanban-model.ts +173 -0
  202. package/src/lib/views/kanban-view/kanban-view.spec.ts +540 -0
  203. package/src/lib/views/kanban-view/kanban-view.ts +327 -0
  204. package/src/lib/widgets/bar-chart/bar-chart.descriptor.ts +17 -0
  205. package/src/lib/widgets/bar-chart/bar-chart.spec.ts +66 -0
  206. package/src/lib/widgets/bar-chart/bar-chart.ts +76 -0
  207. package/src/lib/widgets/chart-line/chart-line.descriptor.ts +17 -0
  208. package/src/lib/widgets/chart-line/chart-line.spec.ts +113 -0
  209. package/src/lib/widgets/chart-line/chart-line.ts +219 -0
  210. package/src/lib/widgets/descriptors.ts +29 -0
  211. package/src/lib/widgets/metric-card/metric-card.descriptor.ts +41 -0
  212. package/src/lib/widgets/metric-card/metric-card.spec.ts +52 -0
  213. package/src/lib/widgets/metric-card/metric-card.ts +48 -0
  214. package/src/lib/widgets/quick-actions/quick-actions.descriptor.ts +13 -0
  215. package/src/lib/widgets/quick-actions/quick-actions.spec.ts +61 -0
  216. package/src/lib/widgets/quick-actions/quick-actions.ts +100 -0
  217. package/src/lib/widgets/record-list/record-list.descriptor.ts +16 -0
  218. package/src/lib/widgets/record-list/record-list.spec.ts +79 -0
  219. package/src/lib/widgets/record-list/record-list.ts +115 -0
  220. package/src/lib/widgets/series.spec.ts +42 -0
  221. package/src/lib/widgets/series.ts +56 -0
  222. package/src/lib/widgets/status-progress/status-progress.descriptor.ts +19 -0
  223. package/src/lib/widgets/status-progress/status-progress.spec.ts +63 -0
  224. package/src/lib/widgets/status-progress/status-progress.ts +104 -0
  225. package/src/lib/widgets/widget-host/widget-host.spec.ts +179 -0
  226. package/src/lib/widgets/widget-host/widget-host.ts +99 -0
  227. package/src/public-api.ts +104 -0
  228. package/tinya-code-gas-admin-ui-0.2.0-alpha.15.tgz +0 -0
  229. package/tsconfig.lib.json +13 -0
  230. package/tsconfig.lib.prod.json +11 -0
  231. package/tsconfig.spec.json +10 -0
  232. package/fesm2022/tinya-code-gas-admin-ui-badge-EeCA_l-o.mjs +0 -84
  233. package/fesm2022/tinya-code-gas-admin-ui-badge-EeCA_l-o.mjs.map +0 -1
  234. package/fesm2022/tinya-code-gas-admin-ui-bar-chart-G7sEMSBY.mjs +0 -140
  235. package/fesm2022/tinya-code-gas-admin-ui-bar-chart-G7sEMSBY.mjs.map +0 -1
  236. package/fesm2022/tinya-code-gas-admin-ui-boolean-B2ASKL0E.mjs +0 -113
  237. package/fesm2022/tinya-code-gas-admin-ui-boolean-B2ASKL0E.mjs.map +0 -1
  238. package/fesm2022/tinya-code-gas-admin-ui-button-BQjNW8KB.mjs +0 -163
  239. package/fesm2022/tinya-code-gas-admin-ui-button-BQjNW8KB.mjs.map +0 -1
  240. package/fesm2022/tinya-code-gas-admin-ui-calendar-view-DCY8bj2V.mjs +0 -1027
  241. package/fesm2022/tinya-code-gas-admin-ui-calendar-view-DCY8bj2V.mjs.map +0 -1
  242. package/fesm2022/tinya-code-gas-admin-ui-chart-line-BjdlKHoH.mjs +0 -278
  243. package/fesm2022/tinya-code-gas-admin-ui-chart-line-BjdlKHoH.mjs.map +0 -1
  244. package/fesm2022/tinya-code-gas-admin-ui-color-Dljh_By3.mjs +0 -107
  245. package/fesm2022/tinya-code-gas-admin-ui-color-Dljh_By3.mjs.map +0 -1
  246. package/fesm2022/tinya-code-gas-admin-ui-condition-evaluator-DY2k1M3C.mjs +0 -101
  247. package/fesm2022/tinya-code-gas-admin-ui-condition-evaluator-DY2k1M3C.mjs.map +0 -1
  248. package/fesm2022/tinya-code-gas-admin-ui-currency-BvuF3Z63.mjs +0 -141
  249. package/fesm2022/tinya-code-gas-admin-ui-currency-BvuF3Z63.mjs.map +0 -1
  250. package/fesm2022/tinya-code-gas-admin-ui-data-source.service-96FSbN72.mjs +0 -88
  251. package/fesm2022/tinya-code-gas-admin-ui-data-source.service-96FSbN72.mjs.map +0 -1
  252. package/fesm2022/tinya-code-gas-admin-ui-date-1SJGXZrd.mjs +0 -107
  253. package/fesm2022/tinya-code-gas-admin-ui-date-1SJGXZrd.mjs.map +0 -1
  254. package/fesm2022/tinya-code-gas-admin-ui-display-badge-fwk6sDU7.mjs +0 -140
  255. package/fesm2022/tinya-code-gas-admin-ui-display-badge-fwk6sDU7.mjs.map +0 -1
  256. package/fesm2022/tinya-code-gas-admin-ui-display-date-D7zLiDiG.mjs +0 -119
  257. package/fesm2022/tinya-code-gas-admin-ui-display-date-D7zLiDiG.mjs.map +0 -1
  258. package/fesm2022/tinya-code-gas-admin-ui-display-file-BiIhqvQC.mjs +0 -125
  259. package/fesm2022/tinya-code-gas-admin-ui-display-file-BiIhqvQC.mjs.map +0 -1
  260. package/fesm2022/tinya-code-gas-admin-ui-display-image-C1dhnZi1.mjs +0 -152
  261. package/fesm2022/tinya-code-gas-admin-ui-display-image-C1dhnZi1.mjs.map +0 -1
  262. package/fesm2022/tinya-code-gas-admin-ui-display-list-DOEuUbGe.mjs +0 -211
  263. package/fesm2022/tinya-code-gas-admin-ui-display-list-DOEuUbGe.mjs.map +0 -1
  264. package/fesm2022/tinya-code-gas-admin-ui-display-number-DDMzr4Hn.mjs +0 -116
  265. package/fesm2022/tinya-code-gas-admin-ui-display-number-DDMzr4Hn.mjs.map +0 -1
  266. package/fesm2022/tinya-code-gas-admin-ui-display-text-B8CdVNpv.mjs +0 -111
  267. package/fesm2022/tinya-code-gas-admin-ui-display-text-B8CdVNpv.mjs.map +0 -1
  268. package/fesm2022/tinya-code-gas-admin-ui-dropdown-C3blvmE1.mjs +0 -122
  269. package/fesm2022/tinya-code-gas-admin-ui-dropdown-C3blvmE1.mjs.map +0 -1
  270. package/fesm2022/tinya-code-gas-admin-ui-email-CT_O5ihA.mjs +0 -107
  271. package/fesm2022/tinya-code-gas-admin-ui-email-CT_O5ihA.mjs.map +0 -1
  272. package/fesm2022/tinya-code-gas-admin-ui-empty-state-BMc0QtoT.mjs +0 -109
  273. package/fesm2022/tinya-code-gas-admin-ui-empty-state-BMc0QtoT.mjs.map +0 -1
  274. package/fesm2022/tinya-code-gas-admin-ui-error-mapper.service-DY5y1FjE.mjs +0 -45
  275. package/fesm2022/tinya-code-gas-admin-ui-error-mapper.service-DY5y1FjE.mjs.map +0 -1
  276. package/fesm2022/tinya-code-gas-admin-ui-field-aria-BD1Wiggc.mjs +0 -50
  277. package/fesm2022/tinya-code-gas-admin-ui-field-aria-BD1Wiggc.mjs.map +0 -1
  278. package/fesm2022/tinya-code-gas-admin-ui-field-node-CIK6KNN_.mjs +0 -24
  279. package/fesm2022/tinya-code-gas-admin-ui-field-node-CIK6KNN_.mjs.map +0 -1
  280. package/fesm2022/tinya-code-gas-admin-ui-field-path-B4-FTBGZ.mjs +0 -27
  281. package/fesm2022/tinya-code-gas-admin-ui-field-path-B4-FTBGZ.mjs.map +0 -1
  282. package/fesm2022/tinya-code-gas-admin-ui-file-Dmxioog7.mjs +0 -408
  283. package/fesm2022/tinya-code-gas-admin-ui-file-Dmxioog7.mjs.map +0 -1
  284. package/fesm2022/tinya-code-gas-admin-ui-form-view-C709rc57.mjs +0 -4516
  285. package/fesm2022/tinya-code-gas-admin-ui-form-view-C709rc57.mjs.map +0 -1
  286. package/fesm2022/tinya-code-gas-admin-ui-format-badge-CK8ct5Wk.mjs +0 -82
  287. package/fesm2022/tinya-code-gas-admin-ui-format-badge-CK8ct5Wk.mjs.map +0 -1
  288. package/fesm2022/tinya-code-gas-admin-ui-format-boolean-ClzezZz-.mjs +0 -105
  289. package/fesm2022/tinya-code-gas-admin-ui-format-boolean-ClzezZz-.mjs.map +0 -1
  290. package/fesm2022/tinya-code-gas-admin-ui-format-currency-CVmniKml.mjs +0 -65
  291. package/fesm2022/tinya-code-gas-admin-ui-format-currency-CVmniKml.mjs.map +0 -1
  292. package/fesm2022/tinya-code-gas-admin-ui-format-date-Dr1YsDd_.mjs +0 -65
  293. package/fesm2022/tinya-code-gas-admin-ui-format-date-Dr1YsDd_.mjs.map +0 -1
  294. package/fesm2022/tinya-code-gas-admin-ui-format-number-CWCjpI4n.mjs +0 -65
  295. package/fesm2022/tinya-code-gas-admin-ui-format-number-CWCjpI4n.mjs.map +0 -1
  296. package/fesm2022/tinya-code-gas-admin-ui-format-text-DBOgJkH0.mjs +0 -59
  297. package/fesm2022/tinya-code-gas-admin-ui-format-text-DBOgJkH0.mjs.map +0 -1
  298. package/fesm2022/tinya-code-gas-admin-ui-format-thumbnail-PCRvxWKt.mjs +0 -94
  299. package/fesm2022/tinya-code-gas-admin-ui-format-thumbnail-PCRvxWKt.mjs.map +0 -1
  300. package/fesm2022/tinya-code-gas-admin-ui-gap-sorting-iTdy_DYG.mjs +0 -72
  301. package/fesm2022/tinya-code-gas-admin-ui-gap-sorting-iTdy_DYG.mjs.map +0 -1
  302. package/fesm2022/tinya-code-gas-admin-ui-group-h7-O8tt4.mjs +0 -238
  303. package/fesm2022/tinya-code-gas-admin-ui-group-h7-O8tt4.mjs.map +0 -1
  304. package/fesm2022/tinya-code-gas-admin-ui-image-7UhJ7nqI.mjs +0 -416
  305. package/fesm2022/tinya-code-gas-admin-ui-image-7UhJ7nqI.mjs.map +0 -1
  306. package/fesm2022/tinya-code-gas-admin-ui-kanban-view-Dl0Ol5Kb.mjs +0 -439
  307. package/fesm2022/tinya-code-gas-admin-ui-kanban-view-Dl0Ol5Kb.mjs.map +0 -1
  308. package/fesm2022/tinya-code-gas-admin-ui-key-value-nVdgg5at.mjs +0 -513
  309. package/fesm2022/tinya-code-gas-admin-ui-key-value-nVdgg5at.mjs.map +0 -1
  310. package/fesm2022/tinya-code-gas-admin-ui-list-C8ijJHAx.mjs +0 -637
  311. package/fesm2022/tinya-code-gas-admin-ui-list-C8ijJHAx.mjs.map +0 -1
  312. package/fesm2022/tinya-code-gas-admin-ui-metric-card-DnGMQL8l.mjs +0 -127
  313. package/fesm2022/tinya-code-gas-admin-ui-metric-card-DnGMQL8l.mjs.map +0 -1
  314. package/fesm2022/tinya-code-gas-admin-ui-multiselect-PB28e-jl.mjs +0 -251
  315. package/fesm2022/tinya-code-gas-admin-ui-multiselect-PB28e-jl.mjs.map +0 -1
  316. package/fesm2022/tinya-code-gas-admin-ui-notification.service-LjSULER6.mjs +0 -66
  317. package/fesm2022/tinya-code-gas-admin-ui-notification.service-LjSULER6.mjs.map +0 -1
  318. package/fesm2022/tinya-code-gas-admin-ui-number-JNr7glc8.mjs +0 -114
  319. package/fesm2022/tinya-code-gas-admin-ui-number-JNr7glc8.mjs.map +0 -1
  320. package/fesm2022/tinya-code-gas-admin-ui-phone-jR417hPf.mjs +0 -116
  321. package/fesm2022/tinya-code-gas-admin-ui-phone-jR417hPf.mjs.map +0 -1
  322. package/fesm2022/tinya-code-gas-admin-ui-quick-actions-DCQ6emmF.mjs +0 -235
  323. package/fesm2022/tinya-code-gas-admin-ui-quick-actions-DCQ6emmF.mjs.map +0 -1
  324. package/fesm2022/tinya-code-gas-admin-ui-radio-mode-DxvN07DY.mjs +0 -8
  325. package/fesm2022/tinya-code-gas-admin-ui-radio-mode-DxvN07DY.mjs.map +0 -1
  326. package/fesm2022/tinya-code-gas-admin-ui-readonly-text-BL7-4qie.mjs +0 -124
  327. package/fesm2022/tinya-code-gas-admin-ui-readonly-text-BL7-4qie.mjs.map +0 -1
  328. package/fesm2022/tinya-code-gas-admin-ui-record-list-BiZfnd_7.mjs +0 -249
  329. package/fesm2022/tinya-code-gas-admin-ui-record-list-BiZfnd_7.mjs.map +0 -1
  330. package/fesm2022/tinya-code-gas-admin-ui-relation-drawer.service-B0kT8EiW.mjs +0 -39
  331. package/fesm2022/tinya-code-gas-admin-ui-relation-drawer.service-B0kT8EiW.mjs.map +0 -1
  332. package/fesm2022/tinya-code-gas-admin-ui-relation-ykpJ3PYl.mjs +0 -309
  333. package/fesm2022/tinya-code-gas-admin-ui-relation-ykpJ3PYl.mjs.map +0 -1
  334. package/fesm2022/tinya-code-gas-admin-ui-render-registry-CULkxrSt.mjs +0 -576
  335. package/fesm2022/tinya-code-gas-admin-ui-render-registry-CULkxrSt.mjs.map +0 -1
  336. package/fesm2022/tinya-code-gas-admin-ui-rich-select-CIHaOrbU.mjs +0 -161
  337. package/fesm2022/tinya-code-gas-admin-ui-rich-select-CIHaOrbU.mjs.map +0 -1
  338. package/fesm2022/tinya-code-gas-admin-ui-rolldown-runtime-DpJrqANO.mjs +0 -11
  339. package/fesm2022/tinya-code-gas-admin-ui-schema-catalog-D0Pk0wAN.mjs +0 -685
  340. package/fesm2022/tinya-code-gas-admin-ui-schema-catalog-D0Pk0wAN.mjs.map +0 -1
  341. package/fesm2022/tinya-code-gas-admin-ui-seed-CcCA3FdQ.mjs +0 -80
  342. package/fesm2022/tinya-code-gas-admin-ui-seed-CcCA3FdQ.mjs.map +0 -1
  343. package/fesm2022/tinya-code-gas-admin-ui-select-CDwX9yNO.mjs +0 -296
  344. package/fesm2022/tinya-code-gas-admin-ui-select-CDwX9yNO.mjs.map +0 -1
  345. package/fesm2022/tinya-code-gas-admin-ui-skeleton-DnWK0tcZ.mjs +0 -119
  346. package/fesm2022/tinya-code-gas-admin-ui-skeleton-DnWK0tcZ.mjs.map +0 -1
  347. package/fesm2022/tinya-code-gas-admin-ui-slug-DU58cbiY.mjs +0 -158
  348. package/fesm2022/tinya-code-gas-admin-ui-slug-DU58cbiY.mjs.map +0 -1
  349. package/fesm2022/tinya-code-gas-admin-ui-source-options-B9R4mw8-.mjs +0 -26
  350. package/fesm2022/tinya-code-gas-admin-ui-source-options-B9R4mw8-.mjs.map +0 -1
  351. package/fesm2022/tinya-code-gas-admin-ui-status-progress-DmeG8htE.mjs +0 -205
  352. package/fesm2022/tinya-code-gas-admin-ui-status-progress-DmeG8htE.mjs.map +0 -1
  353. package/fesm2022/tinya-code-gas-admin-ui-string-list-gleasqzD.mjs +0 -432
  354. package/fesm2022/tinya-code-gas-admin-ui-string-list-gleasqzD.mjs.map +0 -1
  355. package/fesm2022/tinya-code-gas-admin-ui-template-interpolator-ewv7CkNX.mjs +0 -36
  356. package/fesm2022/tinya-code-gas-admin-ui-template-interpolator-ewv7CkNX.mjs.map +0 -1
  357. package/fesm2022/tinya-code-gas-admin-ui-text-CUPmL7W_.mjs +0 -107
  358. package/fesm2022/tinya-code-gas-admin-ui-text-CUPmL7W_.mjs.map +0 -1
  359. package/fesm2022/tinya-code-gas-admin-ui-textarea-CGLSIW13.mjs +0 -223
  360. package/fesm2022/tinya-code-gas-admin-ui-textarea-CGLSIW13.mjs.map +0 -1
  361. package/fesm2022/tinya-code-gas-admin-ui-time-BcHzsTWf.mjs +0 -107
  362. package/fesm2022/tinya-code-gas-admin-ui-time-BcHzsTWf.mjs.map +0 -1
  363. package/fesm2022/tinya-code-gas-admin-ui-upload.service-CZPEPCeN.mjs +0 -60
  364. package/fesm2022/tinya-code-gas-admin-ui-upload.service-CZPEPCeN.mjs.map +0 -1
  365. package/fesm2022/tinya-code-gas-admin-ui-url-GO4LZi__.mjs +0 -141
  366. package/fesm2022/tinya-code-gas-admin-ui-url-GO4LZi__.mjs.map +0 -1
  367. package/fesm2022/tinya-code-gas-admin-ui.mjs +0 -4599
  368. package/fesm2022/tinya-code-gas-admin-ui.mjs.map +0 -1
  369. package/types/tinya-code-gas-admin-ui.d.ts +0 -3632
@@ -0,0 +1,646 @@
1
+ import { NgComponentOutlet } from '@angular/common';
2
+ import {
3
+ Component,
4
+ computed,
5
+ effect,
6
+ forwardRef,
7
+ inject,
8
+ InjectionToken,
9
+ input,
10
+ isDevMode,
11
+ PendingTasks,
12
+ signal,
13
+ viewChild,
14
+ type TemplateRef,
15
+ type Type,
16
+ } from '@angular/core';
17
+ import { FormField } from '@angular/forms/signals';
18
+ import { LucideCircleAlert } from '@lucide/angular';
19
+
20
+ import { FIELD_ARIA, type FieldAriaContext } from '../../fields/field-aria';
21
+ import {
22
+ childTree,
23
+ groupTree,
24
+ type ListChildContext,
25
+ type RootTree,
26
+ } from '../../fields/field-node';
27
+ import { FieldFile } from '../../fields/file/file';
28
+ import { FieldImage } from '../../fields/image/image';
29
+ import { FieldMultiselect } from '../../fields/multiselect/multiselect';
30
+ import { esGrupoRadio } from '../../fields/select/radio-mode';
31
+ import { displayLoader } from '../../fields/display/display-defaults';
32
+ import { UnknownRender } from '../../fields/unknown-render/unknown-render';
33
+ import type {
34
+ FieldSchema,
35
+ FileField,
36
+ GroupField,
37
+ ImageField,
38
+ ListField,
39
+ MultiselectField,
40
+ SelectField,
41
+ WidgetSchema,
42
+ } from '../models/schema.model';
43
+ import { Skeleton } from '../../shared/components/skeleton/skeleton';
44
+ import { colSpanClass } from '../../shared/utils/col-span';
45
+ import { getSchema } from './schema-catalog';
46
+ import {
47
+ get,
48
+ isSupported,
49
+ RENDER_CONTEXT_VACIO,
50
+ resolve,
51
+ type RenderConfig,
52
+ type RenderContext,
53
+ type RenderExtras,
54
+ type ComponentRenderKind,
55
+ } from './render-registry';
56
+
57
+ /**
58
+ * El contexto vive en el registro (R3.6): el contrato de `RenderDescriptor.
59
+ * inputs` necesita nombrarlo. Se re-exporta acá para que `field-host` y sus
60
+ * tests no cambien de domicilio.
61
+ */
62
+ export type { RenderContext } from './render-registry';
63
+
64
+ /** Token con el que los controles inyectan el `RenderContext` del host. */
65
+ export const RENDER_CONTEXT = new InjectionToken<RenderContext>('RENDER_CONTEXT');
66
+
67
+ /**
68
+ * Host genérico de render (R3.4).
69
+ *
70
+ * Sustituye el `@switch` de 21 ramas de `FieldHost` por una resolución contra
71
+ * `core/registry/render-registry.ts`: el tipo llega por `[type]`, el registro
72
+ * devuelve el componente y `NgComponentOutlet` lo monta. Añadir un tipo deja
73
+ * de tocar este archivo.
74
+ *
75
+ * Conserva el envoltorio que `FieldHost` dibujaba — `<label>`, marca
76
+ * required/opcional, `data-field`, `colSpanClass`, mensaje de error y ayuda —
77
+ * porque eso es lo que el operador ve y no depende del tipo de control.
78
+ *
79
+ * **Sin imports de los 20 controles**: sólo llegan por `load()` perezoso del
80
+ * descriptor. Mientras el `import()` resuelve se pinta `Skeleton`, y un tipo
81
+ * fuera del catálogo degrada a `UnknownRender` en dev (nunca pantalla rota).
82
+ *
83
+ * **Modo `display` (R4.1 · R4.2).** El mismo host pinta la ficha de detalle:
84
+ * con `mode="display"` no hay árbol, ni etiqueta, ni error — sólo el valor
85
+ * crudo que llega por `[value]` y el componente que elige la tabla
86
+ * `fields/display/display-defaults.ts`. El descriptor declara la superficie
87
+ * (`surfaces: ['form', 'display']`) y, si no la declara o el tipo no está en
88
+ * la tabla, corre la misma degradación controlada de R1.10.
89
+ */
90
+ @Component({
91
+ selector: 'app-render-host',
92
+ imports: [
93
+ FieldFile,
94
+ FieldImage,
95
+ FieldMultiselect,
96
+ FormField,
97
+ LucideCircleAlert,
98
+ NgComponentOutlet,
99
+ RenderHost,
100
+ Skeleton,
101
+ UnknownRender,
102
+ ],
103
+ // El envoltorio de render es quien conoce la ayuda y el error —ambos viven
104
+ // en SU plantilla—, así que de aquí sale el contexto de accesibilidad y el
105
+ // de render, igual que salían de `FieldHost`.
106
+ providers: [
107
+ { provide: FIELD_ARIA, useExisting: forwardRef(() => RenderHost) },
108
+ { provide: RENDER_CONTEXT, useExisting: forwardRef(() => RenderHost) },
109
+ ],
110
+ template: `
111
+ <ng-template #groupContent>
112
+ @for (child of group().fields; track child.key) {
113
+ <div [class]="colSpan(child.width)">
114
+ <app-render-host
115
+ [kind]="kind()"
116
+ [type]="child.type"
117
+ [config]="child"
118
+ [ctx]="groupChildCtx()"
119
+ />
120
+ </div>
121
+ }
122
+ </ng-template>
123
+
124
+ <ng-template #childTpl let-child>
125
+ @for (sub of child.fields; track sub.key) {
126
+ <div [class]="child.row ? 'min-w-0 grow' : colSpan(sub.width)">
127
+ <app-render-host
128
+ [kind]="kind()"
129
+ [type]="sub.type"
130
+ [config]="sub"
131
+ [ctx]="listChildCtx(child)"
132
+ />
133
+ </div>
134
+ }
135
+ </ng-template>
136
+
137
+ @if (esDisplay()) {
138
+ <!-- Rama de display (R4.1 · R4.2): sólo el valor crudo. El host no
139
+ lee el árbol, no dibuja etiqueta ni error y no conoce la
140
+ sección: detail-view agrupa y este bloque pinta. -->
141
+ @if (vacio()) {
142
+ <span aria-hidden="true">—</span>
143
+ } @else if (soportaDisplay()) {
144
+ @if (control()) {
145
+ <ng-container
146
+ [ngComponentOutlet]="control()"
147
+ [ngComponentOutletInputs]="displayInputs()"
148
+ />
149
+ } @else {
150
+ <app-skeleton [lines]="1" [height]="20" />
151
+ }
152
+ } @else if (showUnknown()) {
153
+ <app-unknown-render [type]="type()" [label]="config().label" />
154
+ }
155
+ } @else if (supported()) {
156
+ @if (esCampo()) {
157
+ <div class="mb-4" [class.field-enter]="cfgCampo().visibleWhen" [attr.data-field]="name()">
158
+ @if (type() !== 'group') {
159
+ @if (composite()) {
160
+ <!-- Sin control único no existe ancla para el atributo for del
161
+ label: apuntaría a un id inexistente. Se dibuja como texto
162
+ (en listas/key-value los hijos reciben su etiqueta por
163
+ campo anidado; en un select en modo radio cada opción
164
+ trae su label implícito y el grupo va con aria-label). -->
165
+ <div class="mb-1 text-label font-medium">
166
+ {{ config().label }}
167
+ @if (showRequiredMark()) {
168
+ <span aria-hidden="true" class="text-danger">*</span>
169
+ }
170
+ @if (showOptionalMark()) {
171
+ <span class="font-normal text-neutral">(opcional)</span>
172
+ }
173
+ </div>
174
+ } @else {
175
+ <label [for]="name()" class="mb-1 block text-label font-medium">
176
+ {{ config().label }}
177
+ @if (showRequiredMark()) {
178
+ <span aria-hidden="true" class="text-danger">*</span>
179
+ }
180
+ @if (showOptionalMark()) {
181
+ <span class="font-normal text-neutral">(opcional)</span>
182
+ }
183
+ </label>
184
+ }
185
+ }
186
+
187
+ @if (type() === 'multiselect') {
188
+ <!-- Tres de los veintiuno no pueden pasar por "NgComponentOutlet": su
189
+ sincronización con Signal Forms vive en ɵɵcontrol, una
190
+ instrucción que el compilador emite únicamente junto al binding
191
+ formField de la propia plantilla. Sin ese binding escrito en el
192
+ HTML el model() de value nunca se inicializa y el control lanza
193
+ NG0952. Se declaran a mano (D27) y el resto de tipos siguen
194
+ saliendo del registro. -->
195
+ <app-field-multiselect
196
+ [id]="name()"
197
+ [field]="multiselectCfg()"
198
+ [formField]="multiselectNode()"
199
+ [tree]="ctx().tree"
200
+ />
201
+ } @else if (type() === 'image') {
202
+ <app-field-image
203
+ [id]="name()"
204
+ [field]="imageCfg()"
205
+ [formField]="imageNode()"
206
+ [resource]="ctx().resource"
207
+ />
208
+ } @else if (type() === 'file') {
209
+ <app-field-file
210
+ [id]="name()"
211
+ [field]="fileCfg()"
212
+ [formField]="fileNode()"
213
+ [resource]="ctx().resource"
214
+ />
215
+ } @else {
216
+ @if (control()) {
217
+ <ng-container
218
+ [ngComponentOutlet]="control()"
219
+ [ngComponentOutletInputs]="controlInputs()"
220
+ />
221
+ } @else {
222
+ <app-skeleton [lines]="2" [height]="32" />
223
+ }
224
+ }
225
+
226
+ @if (errorMessage(); as message) {
227
+ <p
228
+ [id]="errorId()"
229
+ class="mt-1 flex items-center gap-1 text-help text-danger"
230
+ role="alert"
231
+ >
232
+ <svg lucideCircleAlert size="14" class="shrink-0" />
233
+ {{ message }}
234
+ </p>
235
+ } @else if (cfgCampo().help; as help) {
236
+ <p [id]="helpId()" class="mt-1 text-help text-neutral">{{ help }}</p>
237
+ }
238
+ </div>
239
+ } @else {
240
+ <!-- Rama de widget (R3.6): el mismo outlet y el mismo cableado que
241
+ un campo, pero sin el envoltorio — un widget no tiene etiqueta
242
+ asociada, ni marca required, ni error, ni ayuda. El ancho en la
243
+ grilla lo pone su host (app-widget-host), no el motor. -->
244
+ @if (control()) {
245
+ <ng-container
246
+ [ngComponentOutlet]="control()"
247
+ [ngComponentOutletInputs]="controlInputs()"
248
+ />
249
+ } @else {
250
+ <app-skeleton [lines]="2" [height]="32" />
251
+ }
252
+ }
253
+ } @else if (showUnknown()) {
254
+ <!-- R1.10: degradación controlada. Un tipo fuera del catálogo no
255
+ revienta; en dev se avisa y en producción sólo se omite. -->
256
+ <app-unknown-render [type]="type()" [label]="config().label" />
257
+ }
258
+ `,
259
+ })
260
+ export class RenderHost implements FieldAriaContext, RenderContext {
261
+ /** Categoría de render. Resuelve campos y widgets (R3.6). */
262
+ readonly kind = input<ComponentRenderKind>('field');
263
+ /** Identificador del tipo dentro de su `kind`. */
264
+ readonly type = input.required<string>();
265
+ /**
266
+ * Configuración del descriptor: el `FieldSchema` del campo, el
267
+ * `WidgetSchema` del widget o la `ListColumn` de la columna (R4.3).
268
+ */
269
+ readonly config = input.required<RenderConfig>();
270
+ /**
271
+ * Contexto de render del nivel de este campo.
272
+ *
273
+ * Con valor por defecto y no requerido: los widgets no pertenecen a un
274
+ * formulario y llegan sin árbol (R3.6). El default es un contexto vacío
275
+ * que nunca se lee en esa rama — un campo montado sin contexto real ya
276
+ * era un error antes y `childTree` lo sigue siendo.
277
+ */
278
+ readonly ctx = input<RenderContext>(RENDER_CONTEXT_VACIO);
279
+ /** Payload del widget (`app-widget-host [data]`). `null` en campos. */
280
+ readonly data = input<unknown>(null);
281
+ /**
282
+ * Superficie de render (R4.1).
283
+ *
284
+ * `'form'` (por defecto) es el formulario de siempre: árbol, etiqueta,
285
+ * marca required, error y ayuda. `'display'` es la ficha de detalle:
286
+ * valor crudo, sin envoltorio — es el `mode="display"` con el que
287
+ * `detail-view` deja de dibujar sus propias celdas (R4.2).
288
+ */
289
+ readonly mode = input<'form' | 'display'>('form');
290
+ /**
291
+ * Valor crudo del registro, **sólo para el modo `display`**.
292
+ *
293
+ * En el formulario el valor vive en el árbol de Signal Forms y este
294
+ * input se ignora: un campo de detalle no tiene árbol que consultar.
295
+ */
296
+ readonly value = input<unknown>(null);
297
+ /**
298
+ * Presentación en chip, **sólo para el modo `display`** (R4.2).
299
+ *
300
+ * La cabecera de la ficha siempre mostró `headerFields` como etiquetas
301
+ * compactas, aunque el valor fuera texto plano. Sin este matiz el host
302
+ * pintaría «ORD-42» como texto suelto y la cabecera cambiaría de cara en
303
+ * los 7 detalles. El aviso va en el contrato de entrada de los display —
304
+ * uniforme para los 6 — y sólo lo aprovechan los que dibujan texto: los
305
+ * que ya son chip (`badge`) o no son un valor escalar (`image`, `list`)
306
+ * lo ignoran a propósito.
307
+ */
308
+ readonly chip = input(false);
309
+
310
+ private readonly dev = isDevMode();
311
+
312
+ /** Mantiene `whenStable()` bloqueado mientras el descriptor carga su chunk. */
313
+ private readonly pendientes = inject(PendingTasks);
314
+
315
+ /** `true` cuando se dibuja un campo, `false` para cualquier otro `kind`. */
316
+ protected readonly esCampo = computed(() => this.kind() === 'field');
317
+
318
+ /** `true` en la rama de detalle (R4.1): sin árbol, etiqueta ni error. */
319
+ protected readonly esDisplay = computed(() => this.mode() === 'display');
320
+
321
+ /**
322
+ * ¿El descriptor declara la superficie `display`? (R4.1)
323
+ *
324
+ * Es la respuesta a «cada descriptor de campo declara si sabe pintarse en
325
+ * `display`»: sin esa declaración el host no pinta el valor en la ficha,
326
+ * y `contract.spec.ts` vigila que los 20 la traigan.
327
+ */
328
+ private readonly declaraDisplay = computed(
329
+ () => get(this.kind(), this.type())?.surfaces?.includes('display') ?? false,
330
+ );
331
+
332
+ /**
333
+ * Componente que pinta este tipo en `display`.
334
+ *
335
+ * El descriptor sólo dice **si** sabe pintarse; **qué** componente usa lo
336
+ * decide la tabla por tipo (`fields/display/display-defaults.ts`), que es
337
+ * la que garantiza que el mismo `field.type` se pinte igual en todas las
338
+ * fichas.
339
+ */
340
+ private readonly loaderDisplay = computed(() =>
341
+ this.declaraDisplay() ? displayLoader(this.type()) : undefined,
342
+ );
343
+
344
+ /** El modo display tiene componente para este tipo. */
345
+ protected readonly soportaDisplay = computed(() => this.loaderDisplay() !== undefined);
346
+
347
+ /**
348
+ * Valor vacío, con el mismo criterio que `formatValue` de `detail-view`
349
+ * antes de R4.2 (`null`, `undefined` o `''`): se pinta un `—` y el
350
+ * componente ni se monta. `false`, `0` y `[]` **no** están vacíos.
351
+ */
352
+ protected readonly vacio = computed(() => {
353
+ const valor = this.value();
354
+ return valor === null || valor === undefined || valor === '';
355
+ });
356
+
357
+ /** Configuración leída como campo. Sólo válida en la rama de campo. */
358
+ private readonly cfgCampo = computed(() => this.config() as FieldSchema);
359
+
360
+ private readonly node = computed(() => childTree<unknown>(this.ctx().tree, this.cfgCampo().key));
361
+
362
+ protected readonly state = computed(() => this.node()());
363
+
364
+ /** Nombre del control: ancla del `<label for>` y base de `*-help`/`*-error`. */
365
+ protected readonly name = computed(() => this.state().name());
366
+
367
+ /**
368
+ * Fuera del catálogo o oculto por `visibleWhen`: no se dibuja.
369
+ *
370
+ * El `isSupported` va primero y corta antes de leer `state()`: un tipo
371
+ * desconocido se siembra como `undefined`, Signal Forms no le crea nodo y
372
+ * `childTree` lanzaría «El formulario no contiene el campo».
373
+ *
374
+ * El `esCampo()` corta lo mismo para los widgets: no hay árbol que ocultar
375
+ * ni nodo que consultar, así que el widget se dibuja si está en el
376
+ * catálogo y ya.
377
+ */
378
+ protected readonly supported = computed(() => {
379
+ if (!isSupported(this.kind(), this.type())) {
380
+ return false;
381
+ }
382
+ return this.esCampo() ? !this.state().hidden() : true;
383
+ });
384
+
385
+ /**
386
+ * Sin renderizador para este tipo **en la superficie actual**.
387
+ *
388
+ * En `display`: el descriptor no declara la superficie o el tipo no está
389
+ * en la tabla de display. En `form`: sigue siendo «fuera del catálogo».
390
+ * Ninguna de las dos ramas toca `state()`, que en display no existe.
391
+ */
392
+ private readonly unsupported = computed(() =>
393
+ this.esDisplay() ? !this.soportaDisplay() : !isSupported(this.kind(), this.type()),
394
+ );
395
+
396
+ protected readonly showUnknown = computed(() => this.unsupported() && this.dev);
397
+
398
+ private readonly _trazaTipoDesconocido = effect(() => {
399
+ if (!this.unsupported() || this.dev) return;
400
+ const superficie = this.esDisplay() ? 'display' : 'form';
401
+ console.warn(
402
+ `[render-host] tipo sin renderizador (${superficie}): "${this.type()}" — se omite el control.`,
403
+ );
404
+ });
405
+
406
+ // ── Resolución del componente ────────────────────────────────────────────
407
+
408
+ /** Componente montado, o `null` mientras el `load()` perezoso resuelve. */
409
+ private readonly control = signal<Type<unknown> | null>(null);
410
+
411
+ /** Descarta cargas obsoletas cuando el tipo cambia antes de que resuelva. */
412
+ private carga = 0;
413
+
414
+ private readonly _cargar = effect(() => {
415
+ const type = this.type();
416
+ const carga = ++this.carga;
417
+ this.control.set(null);
418
+
419
+ // Modo display (R4.1): el componente sale de la tabla por tipo, no del
420
+ // `load()` del descriptor — la tabla es la que garantiza que un mismo
421
+ // `field.type` se pinte igual en todas las fichas.
422
+ if (this.esDisplay()) {
423
+ const loader = this.loaderDisplay();
424
+ if (!loader) return;
425
+ this.pendientes.run(async () => {
426
+ const componente = await loader();
427
+ if (this.carga === carga) this.control.set(componente);
428
+ });
429
+ return;
430
+ }
431
+
432
+ if (!isSupported(this.kind(), type)) return;
433
+ // El `load()` del descriptor es un `import()` de chunk, que `whenStable()`
434
+ // NO espera por sí solo (queda fuera del ciclo de estabilidad y el control
435
+ // llegaría tarde a un `detectChanges()` posterior). Registrándolo como
436
+ // tarea pendiente la estabilidad cubre la carga: el skeleton se pinta
437
+ // mientras baja y el control aparece en el siguiente ciclo ya montado.
438
+ this.pendientes.run(async () => {
439
+ const componente = await resolve(this.kind(), type, UnknownRender);
440
+ if (this.carga === carga) this.control.set(componente);
441
+ });
442
+ });
443
+
444
+ /**
445
+ * Inputs del componente de display.
446
+ *
447
+ * Forma fija y uniforme para los 6 componentes de
448
+ * `fields/display/`: `field` (el `FieldSchema` completo, para los que
449
+ * necesitan `options`), `value` (el valor crudo) y `chip` (la
450
+ * presentación de la cabecera). El descriptor no interviene acá — en
451
+ * display no hay ni control ni árbol que cablear.
452
+ */
453
+ private readonly displayInputs = computed<Record<string, unknown>>(() => ({
454
+ field: this.config() as FieldSchema,
455
+ value: this.value(),
456
+ chip: this.chip(),
457
+ }));
458
+
459
+ /**
460
+ * Inputs del componente montado.
461
+ *
462
+ * El motor no sabe qué recibe cada tipo: pregunta al descriptor (`inputs`)
463
+ * y sólo aplica la forma estándar del `kind` cuando el descriptor no la
464
+ * sobreescribe — `{field, tree}` en campos, `{widget, data}` en widgets.
465
+ * Ésa es la pieza que hace que «añadir un tipo» no toque este archivo
466
+ * (R3.8). `multiselect` e `image` no pasan por aquí: se declaran a mano
467
+ * por D27.
468
+ */
469
+ private readonly controlInputs = computed<Record<string, unknown>>(() => {
470
+ const cfg = this.config();
471
+ const extras: RenderExtras = {
472
+ ctx: this.ctx(),
473
+ data: this.data(),
474
+ groupContent: this.groupTpl(),
475
+ childTemplate: this.childTpl(),
476
+ };
477
+
478
+ const descriptor = get(this.kind(), this.type());
479
+ if (descriptor?.inputs) {
480
+ return descriptor.inputs(cfg, extras);
481
+ }
482
+
483
+ return this.esCampo()
484
+ ? { field: cfg as FieldSchema, tree: this.ctx().tree }
485
+ : { widget: cfg, data: this.data() };
486
+ });
487
+
488
+ // ── Recursión de los tipos compuestos ────────────────────────────────────
489
+
490
+ protected readonly group = computed(() => this.config() as GroupField);
491
+ protected readonly list = computed(() => this.config() as ListField);
492
+
493
+ private readonly groupTpl = viewChild<TemplateRef<unknown>>('groupContent');
494
+ private readonly childTpl = viewChild<TemplateRef<unknown>>('childTpl');
495
+
496
+ /** Subárbol y prefijo que heredan los hijos del grupo. */
497
+ protected readonly groupChildCtx = computed<RenderContext>(() => ({
498
+ ...this.ctx(),
499
+ tree: groupTree(this.ctx().tree, this.config().key),
500
+ pathPrefix: `${this.ctx().pathPrefix}${this.config().key}.`,
501
+ }));
502
+
503
+ protected listChildCtx(child: ListChildContext): RenderContext {
504
+ return {
505
+ tree: child.tree,
506
+ serverErrors: child.errors,
507
+ pathPrefix: child.prefix,
508
+ exists: child.exists,
509
+ resource: this.ctx().resource,
510
+ };
511
+ }
512
+
513
+ /** `sm:col-span-*` compartido con el grupo (literales: Tailwind v4 es JIT). */
514
+ colSpan(width?: number): string {
515
+ return colSpanClass(width);
516
+ }
517
+
518
+ // ── Envoltorio: label, marcas y estado ───────────────────────────────────
519
+
520
+ /**
521
+ * Los tres `FormValueControl` se declaran en la plantilla (D27), así que
522
+ * su campo necesita el tipo estrecho que exige cada input — el mismo
523
+ * estrechado que hacía `FieldHost`.
524
+ */
525
+ protected readonly multiselectCfg = computed(() => this.config() as MultiselectField);
526
+
527
+ protected readonly imageCfg = computed(() => this.config() as ImageField);
528
+
529
+ protected readonly fileCfg = computed(() => this.config() as FileField);
530
+
531
+ private readonly multiselectNode = computed(() =>
532
+ childTree<string[]>(this.ctx().tree, this.config().key),
533
+ );
534
+
535
+ private readonly imageNode = computed(() =>
536
+ childTree<string>(this.ctx().tree, this.config().key),
537
+ );
538
+
539
+ private readonly fileNode = computed(() => childTree<string>(this.ctx().tree, this.config().key));
540
+
541
+ /**
542
+ * Error visible: primero el del backend (si existe para esta ruta) y
543
+ * después el propio, solo cuando el campo ya se tocó (validación al
544
+ * perder el foco).
545
+ */
546
+ protected readonly errorMessage = computed<string | null>(() => {
547
+ const serverError = this.ctx().serverErrors[`${this.ctx().pathPrefix}${this.config().key}`];
548
+ if (serverError) {
549
+ return serverError;
550
+ }
551
+ const state = this.state();
552
+ if (!state.invalid() || !state.touched()) {
553
+ return null;
554
+ }
555
+ return state.errors()[0]?.message ?? 'Revisa este campo.';
556
+ });
557
+
558
+ /**
559
+ * Compuestos: varios controles internos, ninguno único. Para ellos
560
+ * `<label for>` no tendría ancla, porque ningún elemento lleva el id del
561
+ * campo padre (los hijos tienen los suyos propios).
562
+ */
563
+ protected readonly composite = computed(() => {
564
+ const cfg = this.cfgCampo();
565
+ if (new Set(['list', 'key-value', 'string-list', 'multiselect']).has(cfg.type)) {
566
+ return true;
567
+ }
568
+ // Un select en modo radio (2–4 opciones estáticas) tampoco tiene ancla
569
+ // única: el grupo se nombra con `aria-label`, así que su rótulo va como
570
+ // texto. El cálculo vive en `radio-mode.ts` y lo comparte con el campo
571
+ // para que las dos ramas nunca se desincronicen.
572
+ return cfg.type === 'select' && esGrupoRadio(cfg as SelectField);
573
+ });
574
+
575
+ // Contexto de accesibilidad consumido por `FieldAria` en los controles.
576
+
577
+ /**
578
+ * Ancla de la ayuda, sólo mientras no hay error: §2 dice que el error
579
+ * **reemplaza** la ayuda, de modo que el id no puede apuntar a un nodo
580
+ * que ya no está en el DOM.
581
+ */
582
+ readonly helpId = computed(() =>
583
+ this.cfgCampo().help && !this.errorMessage() ? `${this.name()}-help` : null,
584
+ );
585
+
586
+ readonly errorId = computed(() => (this.errorMessage() ? `${this.name()}-error` : null));
587
+
588
+ readonly describedBy = computed(
589
+ () => [this.helpId(), this.errorId()].filter(Boolean).join(' ') || null,
590
+ );
591
+
592
+ /** Mismo criterio que `errorMessage()` — el estado del control no es público. */
593
+ readonly invalid = computed(() => this.errorMessage() !== null);
594
+
595
+ readonly required = computed(() => this.cfgCampo().required ?? false);
596
+
597
+ // §2: "si casi todos son obligatorios, marcar los opcionales con
598
+ // «(opcional)»" en vez de sembrar `*` por todo el formulario.
599
+
600
+ private readonly schema = computed(() => getSchema(this.ctx().resource));
601
+
602
+ /**
603
+ * Se decide una vez por recurso y se aplica a todos los niveles: es un
604
+ * juicio de diseño sobre el formulario, no sobre un campo suelto. Contado
605
+ * por nivel (sólo top-level) — un conteo plano mezcla los subcampos de
606
+ * `group`/`list` y da ratios imposibles.
607
+ */
608
+ protected readonly markOptionals = computed(() => {
609
+ const fields = this.schema()?.fields ?? [];
610
+ if (fields.length === 0) {
611
+ return false;
612
+ }
613
+ const required = fields.filter((field) => field.required).length;
614
+ return required / fields.length >= 0.75;
615
+ });
616
+
617
+ protected readonly showRequiredMark = computed(
618
+ () => !this.markOptionals() && this.cfgCampo().required,
619
+ );
620
+
621
+ protected readonly showOptionalMark = computed(
622
+ () => this.markOptionals() && !this.cfgCampo().required,
623
+ );
624
+
625
+ // ── RenderContext (por DI) ───────────────────────────────────────────────
626
+
627
+ get tree(): RootTree {
628
+ return this.ctx().tree;
629
+ }
630
+
631
+ get serverErrors(): Record<string, string> {
632
+ return this.ctx().serverErrors;
633
+ }
634
+
635
+ get pathPrefix(): string {
636
+ return this.ctx().pathPrefix;
637
+ }
638
+
639
+ get exists(): boolean {
640
+ return this.ctx().exists;
641
+ }
642
+
643
+ get resource(): string {
644
+ return this.ctx().resource;
645
+ }
646
+ }