@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.
- package/README.md +222 -0
- package/ng-package.json +7 -0
- package/package.json +3 -15
- package/src/lib/actions/action-registry.spec.ts +52 -0
- package/src/lib/actions/action-registry.ts +126 -0
- package/src/lib/actions/action-service.spec.ts +215 -0
- package/src/lib/actions/action-service.ts +87 -0
- package/src/lib/actions/descriptors.spec.ts +155 -0
- package/src/lib/actions/descriptors.ts +154 -0
- package/src/lib/columns/column-cell.ts +33 -0
- package/src/lib/columns/column-defaults.spec.ts +99 -0
- package/src/lib/columns/column-defaults.ts +84 -0
- package/src/lib/columns/column-format.spec.ts +274 -0
- package/src/lib/columns/descriptors.ts +113 -0
- package/src/lib/columns/format-badge.ts +51 -0
- package/src/lib/columns/format-boolean.ts +58 -0
- package/src/lib/columns/format-currency.ts +30 -0
- package/src/lib/columns/format-date.ts +35 -0
- package/src/lib/columns/format-number.ts +32 -0
- package/src/lib/columns/format-text.ts +23 -0
- package/src/lib/columns/format-thumbnail.ts +63 -0
- package/src/lib/core/adapters/firebase-auth.adapter.ts +6 -0
- package/src/lib/core/guards/auth.guard.spec.ts +88 -0
- package/src/lib/core/guards/auth.guard.ts +48 -0
- package/src/lib/core/guards/unsaved-changes.guard.ts +16 -0
- package/src/lib/core/interceptors/api-error.interceptor.spec.ts +182 -0
- package/src/lib/core/interceptors/api-error.interceptor.ts +87 -0
- package/src/lib/core/models/api.model.ts +104 -0
- package/src/lib/core/models/schema-descriptions.ts +118 -0
- package/src/lib/core/models/schema.model.ts +1097 -0
- package/src/lib/core/registry/composition-validator.spec.ts +358 -0
- package/src/lib/core/registry/composition-validator.ts +270 -0
- package/src/lib/core/registry/field-validators.spec.ts +199 -0
- package/src/lib/core/registry/field-validators.ts +440 -0
- package/src/lib/core/registry/render-host.spec.ts +208 -0
- package/src/lib/core/registry/render-host.ts +646 -0
- package/src/lib/core/registry/render-registry.spec.ts +96 -0
- package/src/lib/core/registry/render-registry.ts +312 -0
- package/src/lib/core/registry/schema-catalog.ts +156 -0
- package/src/lib/core/services/api.service.spec.ts +1142 -0
- package/src/lib/core/services/api.service.ts +371 -0
- package/src/lib/core/services/auth.service.spec.ts +653 -0
- package/src/lib/core/services/auth.service.ts +503 -0
- package/src/lib/core/services/config.service.spec.ts +121 -0
- package/src/lib/core/services/config.service.ts +119 -0
- package/src/lib/core/services/data-source.service.spec.ts +179 -0
- package/src/lib/core/services/data-source.service.ts +142 -0
- package/src/lib/core/services/error-mapper.service.spec.ts +50 -0
- package/src/lib/core/services/error-mapper.service.ts +49 -0
- package/src/lib/core/services/notification.service.ts +73 -0
- package/src/lib/core/services/relation-drawer.service.ts +41 -0
- package/src/lib/core/services/theme.service.spec.ts +106 -0
- package/src/lib/core/services/theme.service.ts +86 -0
- package/src/lib/core/services/transport.spec.ts +198 -0
- package/src/lib/core/services/transport.ts +164 -0
- package/src/lib/core/services/upload.service.ts +81 -0
- package/src/lib/core/testing/categories.schema.ts +125 -0
- package/src/lib/core/testing/orders.schema.ts +372 -0
- package/src/lib/core/testing/products.schema.ts +258 -0
- package/src/lib/core/testing/site.schema.ts +195 -0
- package/src/lib/core/testing/test-catalog.ts +21 -0
- package/src/lib/core/tokens.ts +112 -0
- package/src/lib/core/utils/clipboard.spec.ts +55 -0
- package/src/lib/core/utils/clipboard.ts +90 -0
- package/src/lib/core/utils/context-resolver.spec.ts +95 -0
- package/src/lib/core/utils/context-resolver.ts +101 -0
- package/src/lib/core/utils/response-unwrapper.spec.ts +70 -0
- package/src/lib/core/utils/response-unwrapper.ts +103 -0
- package/src/lib/core/utils/source-options.spec.ts +96 -0
- package/src/lib/core/utils/source-options.ts +54 -0
- package/src/lib/core/utils/template-interpolator.spec.ts +59 -0
- package/src/lib/core/utils/template-interpolator.ts +56 -0
- package/src/lib/core/view-navigation/view-navigation.service.spec.ts +207 -0
- package/src/lib/core/view-navigation/view-navigation.service.ts +132 -0
- package/src/lib/fields/boolean/boolean.descriptor.ts +11 -0
- package/src/lib/fields/boolean/boolean.ts +40 -0
- package/src/lib/fields/color/color.descriptor.ts +12 -0
- package/src/lib/fields/color/color.ts +34 -0
- package/src/lib/fields/currency/currency.descriptor.ts +12 -0
- package/src/lib/fields/currency/currency.ts +67 -0
- package/src/lib/fields/date/date.descriptor.ts +12 -0
- package/src/lib/fields/date/date.ts +36 -0
- package/src/lib/fields/descriptors.ts +122 -0
- package/src/lib/fields/display/display-badge.ts +65 -0
- package/src/lib/fields/display/display-date.ts +39 -0
- package/src/lib/fields/display/display-defaults.ts +84 -0
- package/src/lib/fields/display/display-file.ts +41 -0
- package/src/lib/fields/display/display-image.ts +54 -0
- package/src/lib/fields/display/display-list.ts +107 -0
- package/src/lib/fields/display/display-number.ts +41 -0
- package/src/lib/fields/display/display-text.ts +42 -0
- package/src/lib/fields/display/display.spec.ts +242 -0
- package/src/lib/fields/dropdown/dropdown.descriptor.ts +12 -0
- package/src/lib/fields/dropdown/dropdown.ts +45 -0
- package/src/lib/fields/email/email.descriptor.ts +12 -0
- package/src/lib/fields/email/email.ts +28 -0
- package/src/lib/fields/field-aria.ts +60 -0
- package/src/lib/fields/field-host/field-host.spec.ts +118 -0
- package/src/lib/fields/field-host/field-host.ts +47 -0
- package/src/lib/fields/field-node.ts +88 -0
- package/src/lib/fields/file/file.descriptor.ts +14 -0
- package/src/lib/fields/file/file.spec.ts +134 -0
- package/src/lib/fields/file/file.ts +281 -0
- package/src/lib/fields/group/group.descriptor.ts +21 -0
- package/src/lib/fields/group/group.ts +110 -0
- package/src/lib/fields/image/image.descriptor.ts +12 -0
- package/src/lib/fields/image/image.spec.ts +117 -0
- package/src/lib/fields/image/image.ts +281 -0
- package/src/lib/fields/key-value/key-value.descriptor.ts +11 -0
- package/src/lib/fields/key-value/key-value.ts +283 -0
- package/src/lib/fields/list/list.descriptor.ts +21 -0
- package/src/lib/fields/list/list.ts +376 -0
- package/src/lib/fields/multiselect/multiselect.descriptor.ts +11 -0
- package/src/lib/fields/multiselect/multiselect.spec.ts +142 -0
- package/src/lib/fields/multiselect/multiselect.ts +152 -0
- package/src/lib/fields/number/number.descriptor.ts +12 -0
- package/src/lib/fields/number/number.ts +40 -0
- package/src/lib/fields/phone/phone.descriptor.ts +12 -0
- package/src/lib/fields/phone/phone.ts +46 -0
- package/src/lib/fields/readonly-text/readonly-text.descriptor.ts +12 -0
- package/src/lib/fields/readonly-text/readonly-text.ts +58 -0
- package/src/lib/fields/relation/relation.descriptor.ts +12 -0
- package/src/lib/fields/relation/relation.spec.ts +193 -0
- package/src/lib/fields/relation/relation.ts +224 -0
- package/src/lib/fields/rich-select/rich-select.descriptor.ts +12 -0
- package/src/lib/fields/rich-select/rich-select.ts +175 -0
- package/src/lib/fields/select/radio-mode.ts +25 -0
- package/src/lib/fields/select/select.descriptor.ts +12 -0
- package/src/lib/fields/select/select.spec.ts +162 -0
- package/src/lib/fields/select/select.ts +210 -0
- package/src/lib/fields/slug/slug.descriptor.ts +19 -0
- package/src/lib/fields/slug/slug.ts +67 -0
- package/src/lib/fields/string-list/string-list.descriptor.ts +11 -0
- package/src/lib/fields/string-list/string-list.ts +222 -0
- package/src/lib/fields/text/text.descriptor.ts +12 -0
- package/src/lib/fields/text/text.ts +35 -0
- package/src/lib/fields/textarea/textarea.descriptor.ts +12 -0
- package/src/lib/fields/textarea/textarea.spec.ts +117 -0
- package/src/lib/fields/textarea/textarea.ts +126 -0
- package/src/lib/fields/time/time.descriptor.ts +12 -0
- package/src/lib/fields/time/time.ts +28 -0
- package/src/lib/fields/unknown-render/unknown-render.ts +49 -0
- package/src/lib/fields/url/url.descriptor.ts +12 -0
- package/src/lib/fields/url/url.ts +53 -0
- package/src/lib/gas-admin-ui.spec.ts +21 -0
- package/src/lib/gas-admin-ui.ts +9 -0
- package/src/lib/layouts/descriptors.ts +9 -0
- package/src/lib/pages/dashboard/dashboard.ts +167 -0
- package/src/lib/pages/detail-view/detail-view.spec.ts +333 -0
- package/src/lib/pages/detail-view/detail-view.ts +570 -0
- package/src/lib/pages/drawer-form/drawer-form.ts +228 -0
- package/src/lib/pages/form-view/form-model.ts +88 -0
- package/src/lib/pages/form-view/form-schema.ts +255 -0
- package/src/lib/pages/form-view/form-view.ts +1622 -0
- package/src/lib/pages/list-view/list-view.formats.spec.ts +214 -0
- package/src/lib/pages/list-view/list-view.ts +882 -0
- package/src/lib/pages/login/login.spec.ts +109 -0
- package/src/lib/pages/login/login.ts +227 -0
- package/src/lib/pages/resource-page/resource-page.ts +234 -0
- package/src/lib/shared/components/badge/badge.ts +35 -0
- package/src/lib/shared/components/button/button.ts +65 -0
- package/src/lib/shared/components/confirm-dialog/confirm-dialog.ts +48 -0
- package/src/lib/shared/components/copy-button/copy-button.ts +56 -0
- package/src/lib/shared/components/copy-share/copy-share.spec.ts +122 -0
- package/src/lib/shared/components/copy-share/copy-share.ts +107 -0
- package/src/lib/shared/components/drawer/drawer.spec.ts +127 -0
- package/src/lib/shared/components/drawer/drawer.ts +172 -0
- package/src/lib/shared/components/empty-state/empty-state.ts +30 -0
- package/src/lib/shared/components/modal/modal.spec.ts +149 -0
- package/src/lib/shared/components/modal/modal.ts +145 -0
- package/src/lib/shared/components/post-create-modal/post-create-modal.spec.ts +165 -0
- package/src/lib/shared/components/post-create-modal/post-create-modal.ts +159 -0
- package/src/lib/shared/components/skeleton/skeleton.ts +31 -0
- package/src/lib/shared/components/toast/toast.ts +58 -0
- package/src/lib/shared/utils/col-span.ts +25 -0
- package/src/lib/shared/utils/condition-evaluator.ts +53 -0
- package/src/lib/shared/utils/depends-on.ts +48 -0
- package/src/lib/shared/utils/dialog-focus.ts +28 -0
- package/src/lib/shared/utils/field-path.ts +49 -0
- package/src/lib/shared/utils/gap-sorting.ts +144 -0
- package/src/lib/shared/utils/seed.spec.ts +64 -0
- package/src/lib/shared/utils/seed.ts +149 -0
- package/src/lib/shared/utils/slugify.ts +13 -0
- package/src/lib/shared/utils/validators.ts +41 -0
- package/src/lib/shell/layout/layout.ts +63 -0
- package/src/lib/shell/shell.routes.spec.ts +59 -0
- package/src/lib/shell/shell.routes.ts +122 -0
- package/src/lib/shell/sidebar/sidebar.ts +139 -0
- package/src/lib/shell/theme-toggle/theme-toggle.ts +74 -0
- package/src/lib/views/calendar-view/calendar-model.ts +426 -0
- package/src/lib/views/calendar-view/calendar-view.spec.ts +557 -0
- package/src/lib/views/calendar-view/calendar-view.ts +568 -0
- package/src/lib/views/composer/composer-host.spec.ts +301 -0
- package/src/lib/views/composer/composer-host.ts +248 -0
- package/src/lib/views/composer/composer-imports.spec.ts +54 -0
- package/src/lib/views/composer/view-context.spec.ts +117 -0
- package/src/lib/views/composer/view-context.ts +109 -0
- package/src/lib/views/composer/view-slot.spec.ts +130 -0
- package/src/lib/views/composer/view-slot.ts +178 -0
- package/src/lib/views/descriptors.ts +46 -0
- package/src/lib/views/kanban-view/kanban-model.ts +173 -0
- package/src/lib/views/kanban-view/kanban-view.spec.ts +540 -0
- package/src/lib/views/kanban-view/kanban-view.ts +327 -0
- package/src/lib/widgets/bar-chart/bar-chart.descriptor.ts +17 -0
- package/src/lib/widgets/bar-chart/bar-chart.spec.ts +66 -0
- package/src/lib/widgets/bar-chart/bar-chart.ts +76 -0
- package/src/lib/widgets/chart-line/chart-line.descriptor.ts +17 -0
- package/src/lib/widgets/chart-line/chart-line.spec.ts +113 -0
- package/src/lib/widgets/chart-line/chart-line.ts +219 -0
- package/src/lib/widgets/descriptors.ts +29 -0
- package/src/lib/widgets/metric-card/metric-card.descriptor.ts +41 -0
- package/src/lib/widgets/metric-card/metric-card.spec.ts +52 -0
- package/src/lib/widgets/metric-card/metric-card.ts +48 -0
- package/src/lib/widgets/quick-actions/quick-actions.descriptor.ts +13 -0
- package/src/lib/widgets/quick-actions/quick-actions.spec.ts +61 -0
- package/src/lib/widgets/quick-actions/quick-actions.ts +100 -0
- package/src/lib/widgets/record-list/record-list.descriptor.ts +16 -0
- package/src/lib/widgets/record-list/record-list.spec.ts +79 -0
- package/src/lib/widgets/record-list/record-list.ts +115 -0
- package/src/lib/widgets/series.spec.ts +42 -0
- package/src/lib/widgets/series.ts +56 -0
- package/src/lib/widgets/status-progress/status-progress.descriptor.ts +19 -0
- package/src/lib/widgets/status-progress/status-progress.spec.ts +63 -0
- package/src/lib/widgets/status-progress/status-progress.ts +104 -0
- package/src/lib/widgets/widget-host/widget-host.spec.ts +179 -0
- package/src/lib/widgets/widget-host/widget-host.ts +99 -0
- package/src/public-api.ts +104 -0
- package/tinya-code-gas-admin-ui-0.2.0-alpha.15.tgz +0 -0
- package/tsconfig.lib.json +13 -0
- package/tsconfig.lib.prod.json +11 -0
- package/tsconfig.spec.json +10 -0
- package/fesm2022/tinya-code-gas-admin-ui-badge-EeCA_l-o.mjs +0 -84
- package/fesm2022/tinya-code-gas-admin-ui-badge-EeCA_l-o.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-bar-chart-G7sEMSBY.mjs +0 -140
- package/fesm2022/tinya-code-gas-admin-ui-bar-chart-G7sEMSBY.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-boolean-B2ASKL0E.mjs +0 -113
- package/fesm2022/tinya-code-gas-admin-ui-boolean-B2ASKL0E.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-button-BQjNW8KB.mjs +0 -163
- package/fesm2022/tinya-code-gas-admin-ui-button-BQjNW8KB.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-calendar-view-DCY8bj2V.mjs +0 -1027
- package/fesm2022/tinya-code-gas-admin-ui-calendar-view-DCY8bj2V.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-chart-line-BjdlKHoH.mjs +0 -278
- package/fesm2022/tinya-code-gas-admin-ui-chart-line-BjdlKHoH.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-color-Dljh_By3.mjs +0 -107
- package/fesm2022/tinya-code-gas-admin-ui-color-Dljh_By3.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-condition-evaluator-DY2k1M3C.mjs +0 -101
- package/fesm2022/tinya-code-gas-admin-ui-condition-evaluator-DY2k1M3C.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-currency-BvuF3Z63.mjs +0 -141
- package/fesm2022/tinya-code-gas-admin-ui-currency-BvuF3Z63.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-data-source.service-96FSbN72.mjs +0 -88
- package/fesm2022/tinya-code-gas-admin-ui-data-source.service-96FSbN72.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-date-1SJGXZrd.mjs +0 -107
- package/fesm2022/tinya-code-gas-admin-ui-date-1SJGXZrd.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-display-badge-fwk6sDU7.mjs +0 -140
- package/fesm2022/tinya-code-gas-admin-ui-display-badge-fwk6sDU7.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-display-date-D7zLiDiG.mjs +0 -119
- package/fesm2022/tinya-code-gas-admin-ui-display-date-D7zLiDiG.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-display-file-BiIhqvQC.mjs +0 -125
- package/fesm2022/tinya-code-gas-admin-ui-display-file-BiIhqvQC.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-display-image-C1dhnZi1.mjs +0 -152
- package/fesm2022/tinya-code-gas-admin-ui-display-image-C1dhnZi1.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-display-list-DOEuUbGe.mjs +0 -211
- package/fesm2022/tinya-code-gas-admin-ui-display-list-DOEuUbGe.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-display-number-DDMzr4Hn.mjs +0 -116
- package/fesm2022/tinya-code-gas-admin-ui-display-number-DDMzr4Hn.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-display-text-B8CdVNpv.mjs +0 -111
- package/fesm2022/tinya-code-gas-admin-ui-display-text-B8CdVNpv.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-dropdown-C3blvmE1.mjs +0 -122
- package/fesm2022/tinya-code-gas-admin-ui-dropdown-C3blvmE1.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-email-CT_O5ihA.mjs +0 -107
- package/fesm2022/tinya-code-gas-admin-ui-email-CT_O5ihA.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-empty-state-BMc0QtoT.mjs +0 -109
- package/fesm2022/tinya-code-gas-admin-ui-empty-state-BMc0QtoT.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-error-mapper.service-DY5y1FjE.mjs +0 -45
- package/fesm2022/tinya-code-gas-admin-ui-error-mapper.service-DY5y1FjE.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-field-aria-BD1Wiggc.mjs +0 -50
- package/fesm2022/tinya-code-gas-admin-ui-field-aria-BD1Wiggc.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-field-node-CIK6KNN_.mjs +0 -24
- package/fesm2022/tinya-code-gas-admin-ui-field-node-CIK6KNN_.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-field-path-B4-FTBGZ.mjs +0 -27
- package/fesm2022/tinya-code-gas-admin-ui-field-path-B4-FTBGZ.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-file-Dmxioog7.mjs +0 -408
- package/fesm2022/tinya-code-gas-admin-ui-file-Dmxioog7.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-form-view-C709rc57.mjs +0 -4516
- package/fesm2022/tinya-code-gas-admin-ui-form-view-C709rc57.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-format-badge-CK8ct5Wk.mjs +0 -82
- package/fesm2022/tinya-code-gas-admin-ui-format-badge-CK8ct5Wk.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-format-boolean-ClzezZz-.mjs +0 -105
- package/fesm2022/tinya-code-gas-admin-ui-format-boolean-ClzezZz-.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-format-currency-CVmniKml.mjs +0 -65
- package/fesm2022/tinya-code-gas-admin-ui-format-currency-CVmniKml.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-format-date-Dr1YsDd_.mjs +0 -65
- package/fesm2022/tinya-code-gas-admin-ui-format-date-Dr1YsDd_.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-format-number-CWCjpI4n.mjs +0 -65
- package/fesm2022/tinya-code-gas-admin-ui-format-number-CWCjpI4n.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-format-text-DBOgJkH0.mjs +0 -59
- package/fesm2022/tinya-code-gas-admin-ui-format-text-DBOgJkH0.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-format-thumbnail-PCRvxWKt.mjs +0 -94
- package/fesm2022/tinya-code-gas-admin-ui-format-thumbnail-PCRvxWKt.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-gap-sorting-iTdy_DYG.mjs +0 -72
- package/fesm2022/tinya-code-gas-admin-ui-gap-sorting-iTdy_DYG.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-group-h7-O8tt4.mjs +0 -238
- package/fesm2022/tinya-code-gas-admin-ui-group-h7-O8tt4.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-image-7UhJ7nqI.mjs +0 -416
- package/fesm2022/tinya-code-gas-admin-ui-image-7UhJ7nqI.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-kanban-view-Dl0Ol5Kb.mjs +0 -439
- package/fesm2022/tinya-code-gas-admin-ui-kanban-view-Dl0Ol5Kb.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-key-value-nVdgg5at.mjs +0 -513
- package/fesm2022/tinya-code-gas-admin-ui-key-value-nVdgg5at.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-list-C8ijJHAx.mjs +0 -637
- package/fesm2022/tinya-code-gas-admin-ui-list-C8ijJHAx.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-metric-card-DnGMQL8l.mjs +0 -127
- package/fesm2022/tinya-code-gas-admin-ui-metric-card-DnGMQL8l.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-multiselect-PB28e-jl.mjs +0 -251
- package/fesm2022/tinya-code-gas-admin-ui-multiselect-PB28e-jl.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-notification.service-LjSULER6.mjs +0 -66
- package/fesm2022/tinya-code-gas-admin-ui-notification.service-LjSULER6.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-number-JNr7glc8.mjs +0 -114
- package/fesm2022/tinya-code-gas-admin-ui-number-JNr7glc8.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-phone-jR417hPf.mjs +0 -116
- package/fesm2022/tinya-code-gas-admin-ui-phone-jR417hPf.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-quick-actions-DCQ6emmF.mjs +0 -235
- package/fesm2022/tinya-code-gas-admin-ui-quick-actions-DCQ6emmF.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-radio-mode-DxvN07DY.mjs +0 -8
- package/fesm2022/tinya-code-gas-admin-ui-radio-mode-DxvN07DY.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-readonly-text-BL7-4qie.mjs +0 -124
- package/fesm2022/tinya-code-gas-admin-ui-readonly-text-BL7-4qie.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-record-list-BiZfnd_7.mjs +0 -249
- package/fesm2022/tinya-code-gas-admin-ui-record-list-BiZfnd_7.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-relation-drawer.service-B0kT8EiW.mjs +0 -39
- package/fesm2022/tinya-code-gas-admin-ui-relation-drawer.service-B0kT8EiW.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-relation-ykpJ3PYl.mjs +0 -309
- package/fesm2022/tinya-code-gas-admin-ui-relation-ykpJ3PYl.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-render-registry-CULkxrSt.mjs +0 -576
- package/fesm2022/tinya-code-gas-admin-ui-render-registry-CULkxrSt.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-rich-select-CIHaOrbU.mjs +0 -161
- package/fesm2022/tinya-code-gas-admin-ui-rich-select-CIHaOrbU.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-rolldown-runtime-DpJrqANO.mjs +0 -11
- package/fesm2022/tinya-code-gas-admin-ui-schema-catalog-D0Pk0wAN.mjs +0 -685
- package/fesm2022/tinya-code-gas-admin-ui-schema-catalog-D0Pk0wAN.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-seed-CcCA3FdQ.mjs +0 -80
- package/fesm2022/tinya-code-gas-admin-ui-seed-CcCA3FdQ.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-select-CDwX9yNO.mjs +0 -296
- package/fesm2022/tinya-code-gas-admin-ui-select-CDwX9yNO.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-skeleton-DnWK0tcZ.mjs +0 -119
- package/fesm2022/tinya-code-gas-admin-ui-skeleton-DnWK0tcZ.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-slug-DU58cbiY.mjs +0 -158
- package/fesm2022/tinya-code-gas-admin-ui-slug-DU58cbiY.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-source-options-B9R4mw8-.mjs +0 -26
- package/fesm2022/tinya-code-gas-admin-ui-source-options-B9R4mw8-.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-status-progress-DmeG8htE.mjs +0 -205
- package/fesm2022/tinya-code-gas-admin-ui-status-progress-DmeG8htE.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-string-list-gleasqzD.mjs +0 -432
- package/fesm2022/tinya-code-gas-admin-ui-string-list-gleasqzD.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-template-interpolator-ewv7CkNX.mjs +0 -36
- package/fesm2022/tinya-code-gas-admin-ui-template-interpolator-ewv7CkNX.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-text-CUPmL7W_.mjs +0 -107
- package/fesm2022/tinya-code-gas-admin-ui-text-CUPmL7W_.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-textarea-CGLSIW13.mjs +0 -223
- package/fesm2022/tinya-code-gas-admin-ui-textarea-CGLSIW13.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-time-BcHzsTWf.mjs +0 -107
- package/fesm2022/tinya-code-gas-admin-ui-time-BcHzsTWf.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-upload.service-CZPEPCeN.mjs +0 -60
- package/fesm2022/tinya-code-gas-admin-ui-upload.service-CZPEPCeN.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-url-GO4LZi__.mjs +0 -141
- package/fesm2022/tinya-code-gas-admin-ui-url-GO4LZi__.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui.mjs +0 -4599
- package/fesm2022/tinya-code-gas-admin-ui.mjs.map +0 -1
- 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
|
+
}
|