@tinya-code/gas-admin-ui 0.2.0-alpha.15 → 0.2.0-alpha.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/tinya-code-gas-admin-ui-badge-EeCA_l-o.mjs +84 -0
- package/fesm2022/tinya-code-gas-admin-ui-badge-EeCA_l-o.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-bar-chart-G7sEMSBY.mjs +140 -0
- package/fesm2022/tinya-code-gas-admin-ui-bar-chart-G7sEMSBY.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-boolean-B2ASKL0E.mjs +113 -0
- package/fesm2022/tinya-code-gas-admin-ui-boolean-B2ASKL0E.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-button-BQjNW8KB.mjs +163 -0
- package/fesm2022/tinya-code-gas-admin-ui-button-BQjNW8KB.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-calendar-view-DCY8bj2V.mjs +1027 -0
- package/fesm2022/tinya-code-gas-admin-ui-calendar-view-DCY8bj2V.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-chart-line-BjdlKHoH.mjs +278 -0
- package/fesm2022/tinya-code-gas-admin-ui-chart-line-BjdlKHoH.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-color-Dljh_By3.mjs +107 -0
- package/fesm2022/tinya-code-gas-admin-ui-color-Dljh_By3.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-condition-evaluator-DY2k1M3C.mjs +101 -0
- package/fesm2022/tinya-code-gas-admin-ui-condition-evaluator-DY2k1M3C.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-currency-BvuF3Z63.mjs +141 -0
- package/fesm2022/tinya-code-gas-admin-ui-currency-BvuF3Z63.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-data-source.service-96FSbN72.mjs +88 -0
- package/fesm2022/tinya-code-gas-admin-ui-data-source.service-96FSbN72.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-date-1SJGXZrd.mjs +107 -0
- package/fesm2022/tinya-code-gas-admin-ui-date-1SJGXZrd.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-badge-fwk6sDU7.mjs +140 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-badge-fwk6sDU7.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-date-D7zLiDiG.mjs +119 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-date-D7zLiDiG.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-file-BiIhqvQC.mjs +125 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-file-BiIhqvQC.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-image-C1dhnZi1.mjs +152 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-image-C1dhnZi1.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-list-DOEuUbGe.mjs +211 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-list-DOEuUbGe.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-number-DDMzr4Hn.mjs +116 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-number-DDMzr4Hn.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-text-B8CdVNpv.mjs +111 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-text-B8CdVNpv.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-dropdown-C3blvmE1.mjs +122 -0
- package/fesm2022/tinya-code-gas-admin-ui-dropdown-C3blvmE1.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-email-CT_O5ihA.mjs +107 -0
- package/fesm2022/tinya-code-gas-admin-ui-email-CT_O5ihA.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-empty-state-BMc0QtoT.mjs +109 -0
- package/fesm2022/tinya-code-gas-admin-ui-empty-state-BMc0QtoT.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-error-mapper.service-DY5y1FjE.mjs +45 -0
- package/fesm2022/tinya-code-gas-admin-ui-error-mapper.service-DY5y1FjE.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-field-aria-BD1Wiggc.mjs +50 -0
- package/fesm2022/tinya-code-gas-admin-ui-field-aria-BD1Wiggc.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-field-node-CIK6KNN_.mjs +24 -0
- package/fesm2022/tinya-code-gas-admin-ui-field-node-CIK6KNN_.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-field-path-B4-FTBGZ.mjs +27 -0
- package/fesm2022/tinya-code-gas-admin-ui-field-path-B4-FTBGZ.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-file-Dmxioog7.mjs +408 -0
- package/fesm2022/tinya-code-gas-admin-ui-file-Dmxioog7.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-form-view-CEe7aZ4m.mjs +4516 -0
- package/fesm2022/tinya-code-gas-admin-ui-form-view-CEe7aZ4m.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-badge-CK8ct5Wk.mjs +82 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-badge-CK8ct5Wk.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-boolean-ClzezZz-.mjs +105 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-boolean-ClzezZz-.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-currency-CVmniKml.mjs +65 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-currency-CVmniKml.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-date-Dr1YsDd_.mjs +65 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-date-Dr1YsDd_.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-number-CWCjpI4n.mjs +65 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-number-CWCjpI4n.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-text-DBOgJkH0.mjs +59 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-text-DBOgJkH0.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-thumbnail-PCRvxWKt.mjs +94 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-thumbnail-PCRvxWKt.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-gap-sorting-iTdy_DYG.mjs +72 -0
- package/fesm2022/tinya-code-gas-admin-ui-gap-sorting-iTdy_DYG.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-group-h7-O8tt4.mjs +238 -0
- package/fesm2022/tinya-code-gas-admin-ui-group-h7-O8tt4.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-image-7UhJ7nqI.mjs +416 -0
- package/fesm2022/tinya-code-gas-admin-ui-image-7UhJ7nqI.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-kanban-view-Dl0Ol5Kb.mjs +439 -0
- package/fesm2022/tinya-code-gas-admin-ui-kanban-view-Dl0Ol5Kb.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-key-value-nVdgg5at.mjs +513 -0
- package/fesm2022/tinya-code-gas-admin-ui-key-value-nVdgg5at.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-list-DTtCYS4d.mjs +637 -0
- package/fesm2022/tinya-code-gas-admin-ui-list-DTtCYS4d.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-metric-card-DnGMQL8l.mjs +127 -0
- package/fesm2022/tinya-code-gas-admin-ui-metric-card-DnGMQL8l.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-multiselect-PB28e-jl.mjs +251 -0
- package/fesm2022/tinya-code-gas-admin-ui-multiselect-PB28e-jl.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-notification.service-LjSULER6.mjs +66 -0
- package/fesm2022/tinya-code-gas-admin-ui-notification.service-LjSULER6.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-number-JNr7glc8.mjs +114 -0
- package/fesm2022/tinya-code-gas-admin-ui-number-JNr7glc8.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-phone-jR417hPf.mjs +116 -0
- package/fesm2022/tinya-code-gas-admin-ui-phone-jR417hPf.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-quick-actions-DCQ6emmF.mjs +235 -0
- package/fesm2022/tinya-code-gas-admin-ui-quick-actions-DCQ6emmF.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-radio-mode-DxvN07DY.mjs +8 -0
- package/fesm2022/tinya-code-gas-admin-ui-radio-mode-DxvN07DY.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-readonly-text-BL7-4qie.mjs +124 -0
- package/fesm2022/tinya-code-gas-admin-ui-readonly-text-BL7-4qie.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-record-list-BiZfnd_7.mjs +249 -0
- package/fesm2022/tinya-code-gas-admin-ui-record-list-BiZfnd_7.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-relation-drawer.service-B0kT8EiW.mjs +39 -0
- package/fesm2022/tinya-code-gas-admin-ui-relation-drawer.service-B0kT8EiW.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-relation-ykpJ3PYl.mjs +309 -0
- package/fesm2022/tinya-code-gas-admin-ui-relation-ykpJ3PYl.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-render-registry-B88Pme-y.mjs +576 -0
- package/fesm2022/tinya-code-gas-admin-ui-render-registry-B88Pme-y.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-rich-select-CIHaOrbU.mjs +161 -0
- package/fesm2022/tinya-code-gas-admin-ui-rich-select-CIHaOrbU.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-rolldown-runtime-DpJrqANO.mjs +11 -0
- package/fesm2022/tinya-code-gas-admin-ui-schema-catalog-D0Pk0wAN.mjs +685 -0
- package/fesm2022/tinya-code-gas-admin-ui-schema-catalog-D0Pk0wAN.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-seed-DkVZbxPP.mjs +80 -0
- package/fesm2022/tinya-code-gas-admin-ui-seed-DkVZbxPP.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-select-CDwX9yNO.mjs +296 -0
- package/fesm2022/tinya-code-gas-admin-ui-select-CDwX9yNO.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-skeleton-DnWK0tcZ.mjs +119 -0
- package/fesm2022/tinya-code-gas-admin-ui-skeleton-DnWK0tcZ.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-slug-DU58cbiY.mjs +158 -0
- package/fesm2022/tinya-code-gas-admin-ui-slug-DU58cbiY.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-source-options-B9R4mw8-.mjs +26 -0
- package/fesm2022/tinya-code-gas-admin-ui-source-options-B9R4mw8-.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-status-progress-DmeG8htE.mjs +205 -0
- package/fesm2022/tinya-code-gas-admin-ui-status-progress-DmeG8htE.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-string-list-gleasqzD.mjs +432 -0
- package/fesm2022/tinya-code-gas-admin-ui-string-list-gleasqzD.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-template-interpolator-ewv7CkNX.mjs +36 -0
- package/fesm2022/tinya-code-gas-admin-ui-template-interpolator-ewv7CkNX.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-text-CUPmL7W_.mjs +107 -0
- package/fesm2022/tinya-code-gas-admin-ui-text-CUPmL7W_.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-textarea-CGLSIW13.mjs +223 -0
- package/fesm2022/tinya-code-gas-admin-ui-textarea-CGLSIW13.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-time-BcHzsTWf.mjs +107 -0
- package/fesm2022/tinya-code-gas-admin-ui-time-BcHzsTWf.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-upload.service-CZPEPCeN.mjs +60 -0
- package/fesm2022/tinya-code-gas-admin-ui-upload.service-CZPEPCeN.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-url-GO4LZi__.mjs +141 -0
- package/fesm2022/tinya-code-gas-admin-ui-url-GO4LZi__.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui.mjs +4738 -0
- package/fesm2022/tinya-code-gas-admin-ui.mjs.map +1 -0
- package/package.json +15 -3
- package/types/tinya-code-gas-admin-ui.d.ts +3634 -0
- package/ng-package.json +0 -7
- package/src/lib/actions/action-registry.spec.ts +0 -52
- package/src/lib/actions/action-registry.ts +0 -126
- package/src/lib/actions/action-service.spec.ts +0 -215
- package/src/lib/actions/action-service.ts +0 -87
- package/src/lib/actions/descriptors.spec.ts +0 -155
- package/src/lib/actions/descriptors.ts +0 -154
- package/src/lib/columns/column-cell.ts +0 -33
- package/src/lib/columns/column-defaults.spec.ts +0 -99
- package/src/lib/columns/column-defaults.ts +0 -84
- package/src/lib/columns/column-format.spec.ts +0 -274
- package/src/lib/columns/descriptors.ts +0 -113
- package/src/lib/columns/format-badge.ts +0 -51
- package/src/lib/columns/format-boolean.ts +0 -58
- package/src/lib/columns/format-currency.ts +0 -30
- package/src/lib/columns/format-date.ts +0 -35
- package/src/lib/columns/format-number.ts +0 -32
- package/src/lib/columns/format-text.ts +0 -23
- package/src/lib/columns/format-thumbnail.ts +0 -63
- package/src/lib/core/adapters/firebase-auth.adapter.ts +0 -6
- package/src/lib/core/guards/auth.guard.spec.ts +0 -88
- package/src/lib/core/guards/auth.guard.ts +0 -48
- package/src/lib/core/guards/unsaved-changes.guard.ts +0 -16
- package/src/lib/core/interceptors/api-error.interceptor.spec.ts +0 -182
- package/src/lib/core/interceptors/api-error.interceptor.ts +0 -87
- package/src/lib/core/models/api.model.ts +0 -104
- package/src/lib/core/models/schema-descriptions.ts +0 -118
- package/src/lib/core/models/schema.model.ts +0 -1097
- package/src/lib/core/registry/composition-validator.spec.ts +0 -358
- package/src/lib/core/registry/composition-validator.ts +0 -270
- package/src/lib/core/registry/field-validators.spec.ts +0 -199
- package/src/lib/core/registry/field-validators.ts +0 -440
- package/src/lib/core/registry/render-host.spec.ts +0 -208
- package/src/lib/core/registry/render-host.ts +0 -646
- package/src/lib/core/registry/render-registry.spec.ts +0 -96
- package/src/lib/core/registry/render-registry.ts +0 -312
- package/src/lib/core/registry/schema-catalog.ts +0 -156
- package/src/lib/core/services/api.service.spec.ts +0 -1142
- package/src/lib/core/services/api.service.ts +0 -371
- package/src/lib/core/services/auth.service.spec.ts +0 -653
- package/src/lib/core/services/auth.service.ts +0 -503
- package/src/lib/core/services/config.service.spec.ts +0 -121
- package/src/lib/core/services/config.service.ts +0 -119
- package/src/lib/core/services/data-source.service.spec.ts +0 -179
- package/src/lib/core/services/data-source.service.ts +0 -142
- package/src/lib/core/services/error-mapper.service.spec.ts +0 -50
- package/src/lib/core/services/error-mapper.service.ts +0 -49
- package/src/lib/core/services/notification.service.ts +0 -73
- package/src/lib/core/services/relation-drawer.service.ts +0 -41
- package/src/lib/core/services/theme.service.spec.ts +0 -106
- package/src/lib/core/services/theme.service.ts +0 -86
- package/src/lib/core/services/transport.spec.ts +0 -198
- package/src/lib/core/services/transport.ts +0 -164
- package/src/lib/core/services/upload.service.ts +0 -81
- package/src/lib/core/testing/categories.schema.ts +0 -125
- package/src/lib/core/testing/orders.schema.ts +0 -372
- package/src/lib/core/testing/products.schema.ts +0 -258
- package/src/lib/core/testing/site.schema.ts +0 -195
- package/src/lib/core/testing/test-catalog.ts +0 -21
- package/src/lib/core/tokens.ts +0 -112
- package/src/lib/core/utils/clipboard.spec.ts +0 -55
- package/src/lib/core/utils/clipboard.ts +0 -90
- package/src/lib/core/utils/context-resolver.spec.ts +0 -95
- package/src/lib/core/utils/context-resolver.ts +0 -101
- package/src/lib/core/utils/response-unwrapper.spec.ts +0 -70
- package/src/lib/core/utils/response-unwrapper.ts +0 -103
- package/src/lib/core/utils/source-options.spec.ts +0 -96
- package/src/lib/core/utils/source-options.ts +0 -54
- package/src/lib/core/utils/template-interpolator.spec.ts +0 -59
- package/src/lib/core/utils/template-interpolator.ts +0 -56
- package/src/lib/core/view-navigation/view-navigation.service.spec.ts +0 -207
- package/src/lib/core/view-navigation/view-navigation.service.ts +0 -132
- package/src/lib/fields/boolean/boolean.descriptor.ts +0 -11
- package/src/lib/fields/boolean/boolean.ts +0 -40
- package/src/lib/fields/color/color.descriptor.ts +0 -12
- package/src/lib/fields/color/color.ts +0 -34
- package/src/lib/fields/currency/currency.descriptor.ts +0 -12
- package/src/lib/fields/currency/currency.ts +0 -67
- package/src/lib/fields/date/date.descriptor.ts +0 -12
- package/src/lib/fields/date/date.ts +0 -36
- package/src/lib/fields/descriptors.ts +0 -122
- package/src/lib/fields/display/display-badge.ts +0 -65
- package/src/lib/fields/display/display-date.ts +0 -39
- package/src/lib/fields/display/display-defaults.ts +0 -84
- package/src/lib/fields/display/display-file.ts +0 -41
- package/src/lib/fields/display/display-image.ts +0 -54
- package/src/lib/fields/display/display-list.ts +0 -107
- package/src/lib/fields/display/display-number.ts +0 -41
- package/src/lib/fields/display/display-text.ts +0 -42
- package/src/lib/fields/display/display.spec.ts +0 -242
- package/src/lib/fields/dropdown/dropdown.descriptor.ts +0 -12
- package/src/lib/fields/dropdown/dropdown.ts +0 -45
- package/src/lib/fields/email/email.descriptor.ts +0 -12
- package/src/lib/fields/email/email.ts +0 -28
- package/src/lib/fields/field-aria.ts +0 -60
- package/src/lib/fields/field-host/field-host.spec.ts +0 -118
- package/src/lib/fields/field-host/field-host.ts +0 -47
- package/src/lib/fields/field-node.ts +0 -88
- package/src/lib/fields/file/file.descriptor.ts +0 -14
- package/src/lib/fields/file/file.spec.ts +0 -134
- package/src/lib/fields/file/file.ts +0 -281
- package/src/lib/fields/group/group.descriptor.ts +0 -21
- package/src/lib/fields/group/group.ts +0 -110
- package/src/lib/fields/image/image.descriptor.ts +0 -12
- package/src/lib/fields/image/image.spec.ts +0 -117
- package/src/lib/fields/image/image.ts +0 -281
- package/src/lib/fields/key-value/key-value.descriptor.ts +0 -11
- package/src/lib/fields/key-value/key-value.ts +0 -283
- package/src/lib/fields/list/list.descriptor.ts +0 -21
- package/src/lib/fields/list/list.ts +0 -376
- package/src/lib/fields/multiselect/multiselect.descriptor.ts +0 -11
- package/src/lib/fields/multiselect/multiselect.spec.ts +0 -142
- package/src/lib/fields/multiselect/multiselect.ts +0 -152
- package/src/lib/fields/number/number.descriptor.ts +0 -12
- package/src/lib/fields/number/number.ts +0 -40
- package/src/lib/fields/phone/phone.descriptor.ts +0 -12
- package/src/lib/fields/phone/phone.ts +0 -46
- package/src/lib/fields/readonly-text/readonly-text.descriptor.ts +0 -12
- package/src/lib/fields/readonly-text/readonly-text.ts +0 -58
- package/src/lib/fields/relation/relation.descriptor.ts +0 -12
- package/src/lib/fields/relation/relation.spec.ts +0 -193
- package/src/lib/fields/relation/relation.ts +0 -224
- package/src/lib/fields/rich-select/rich-select.descriptor.ts +0 -12
- package/src/lib/fields/rich-select/rich-select.ts +0 -175
- package/src/lib/fields/select/radio-mode.ts +0 -25
- package/src/lib/fields/select/select.descriptor.ts +0 -12
- package/src/lib/fields/select/select.spec.ts +0 -162
- package/src/lib/fields/select/select.ts +0 -210
- package/src/lib/fields/slug/slug.descriptor.ts +0 -19
- package/src/lib/fields/slug/slug.ts +0 -67
- package/src/lib/fields/string-list/string-list.descriptor.ts +0 -11
- package/src/lib/fields/string-list/string-list.ts +0 -222
- package/src/lib/fields/text/text.descriptor.ts +0 -12
- package/src/lib/fields/text/text.ts +0 -35
- package/src/lib/fields/textarea/textarea.descriptor.ts +0 -12
- package/src/lib/fields/textarea/textarea.spec.ts +0 -117
- package/src/lib/fields/textarea/textarea.ts +0 -126
- package/src/lib/fields/time/time.descriptor.ts +0 -12
- package/src/lib/fields/time/time.ts +0 -28
- package/src/lib/fields/unknown-render/unknown-render.ts +0 -49
- package/src/lib/fields/url/url.descriptor.ts +0 -12
- package/src/lib/fields/url/url.ts +0 -53
- package/src/lib/gas-admin-ui.spec.ts +0 -21
- package/src/lib/gas-admin-ui.ts +0 -9
- package/src/lib/layouts/descriptors.ts +0 -9
- package/src/lib/pages/dashboard/dashboard.ts +0 -167
- package/src/lib/pages/detail-view/detail-view.spec.ts +0 -333
- package/src/lib/pages/detail-view/detail-view.ts +0 -570
- package/src/lib/pages/drawer-form/drawer-form.ts +0 -228
- package/src/lib/pages/form-view/form-model.ts +0 -88
- package/src/lib/pages/form-view/form-schema.ts +0 -255
- package/src/lib/pages/form-view/form-view.ts +0 -1622
- package/src/lib/pages/list-view/list-view.formats.spec.ts +0 -214
- package/src/lib/pages/list-view/list-view.ts +0 -882
- package/src/lib/pages/login/login.spec.ts +0 -109
- package/src/lib/pages/login/login.ts +0 -227
- package/src/lib/pages/resource-page/resource-page.ts +0 -234
- package/src/lib/shared/components/badge/badge.ts +0 -35
- package/src/lib/shared/components/button/button.ts +0 -65
- package/src/lib/shared/components/confirm-dialog/confirm-dialog.ts +0 -48
- package/src/lib/shared/components/copy-button/copy-button.ts +0 -56
- package/src/lib/shared/components/copy-share/copy-share.spec.ts +0 -122
- package/src/lib/shared/components/copy-share/copy-share.ts +0 -107
- package/src/lib/shared/components/drawer/drawer.spec.ts +0 -127
- package/src/lib/shared/components/drawer/drawer.ts +0 -172
- package/src/lib/shared/components/empty-state/empty-state.ts +0 -30
- package/src/lib/shared/components/modal/modal.spec.ts +0 -149
- package/src/lib/shared/components/modal/modal.ts +0 -145
- package/src/lib/shared/components/post-create-modal/post-create-modal.spec.ts +0 -165
- package/src/lib/shared/components/post-create-modal/post-create-modal.ts +0 -159
- package/src/lib/shared/components/skeleton/skeleton.ts +0 -31
- package/src/lib/shared/components/toast/toast.ts +0 -58
- package/src/lib/shared/utils/col-span.ts +0 -25
- package/src/lib/shared/utils/condition-evaluator.ts +0 -53
- package/src/lib/shared/utils/depends-on.ts +0 -48
- package/src/lib/shared/utils/dialog-focus.ts +0 -28
- package/src/lib/shared/utils/field-path.ts +0 -49
- package/src/lib/shared/utils/gap-sorting.ts +0 -144
- package/src/lib/shared/utils/seed.spec.ts +0 -64
- package/src/lib/shared/utils/seed.ts +0 -149
- package/src/lib/shared/utils/slugify.ts +0 -13
- package/src/lib/shared/utils/validators.ts +0 -41
- package/src/lib/shell/layout/layout.ts +0 -63
- package/src/lib/shell/shell.routes.spec.ts +0 -59
- package/src/lib/shell/shell.routes.ts +0 -122
- package/src/lib/shell/sidebar/sidebar.ts +0 -139
- package/src/lib/shell/theme-toggle/theme-toggle.ts +0 -74
- package/src/lib/views/calendar-view/calendar-model.ts +0 -426
- package/src/lib/views/calendar-view/calendar-view.spec.ts +0 -557
- package/src/lib/views/calendar-view/calendar-view.ts +0 -568
- package/src/lib/views/composer/composer-host.spec.ts +0 -301
- package/src/lib/views/composer/composer-host.ts +0 -248
- package/src/lib/views/composer/composer-imports.spec.ts +0 -54
- package/src/lib/views/composer/view-context.spec.ts +0 -117
- package/src/lib/views/composer/view-context.ts +0 -109
- package/src/lib/views/composer/view-slot.spec.ts +0 -130
- package/src/lib/views/composer/view-slot.ts +0 -178
- package/src/lib/views/descriptors.ts +0 -46
- package/src/lib/views/kanban-view/kanban-model.ts +0 -173
- package/src/lib/views/kanban-view/kanban-view.spec.ts +0 -540
- package/src/lib/views/kanban-view/kanban-view.ts +0 -327
- package/src/lib/widgets/bar-chart/bar-chart.descriptor.ts +0 -17
- package/src/lib/widgets/bar-chart/bar-chart.spec.ts +0 -66
- package/src/lib/widgets/bar-chart/bar-chart.ts +0 -76
- package/src/lib/widgets/chart-line/chart-line.descriptor.ts +0 -17
- package/src/lib/widgets/chart-line/chart-line.spec.ts +0 -113
- package/src/lib/widgets/chart-line/chart-line.ts +0 -219
- package/src/lib/widgets/descriptors.ts +0 -29
- package/src/lib/widgets/metric-card/metric-card.descriptor.ts +0 -41
- package/src/lib/widgets/metric-card/metric-card.spec.ts +0 -52
- package/src/lib/widgets/metric-card/metric-card.ts +0 -48
- package/src/lib/widgets/quick-actions/quick-actions.descriptor.ts +0 -13
- package/src/lib/widgets/quick-actions/quick-actions.spec.ts +0 -61
- package/src/lib/widgets/quick-actions/quick-actions.ts +0 -100
- package/src/lib/widgets/record-list/record-list.descriptor.ts +0 -16
- package/src/lib/widgets/record-list/record-list.spec.ts +0 -79
- package/src/lib/widgets/record-list/record-list.ts +0 -115
- package/src/lib/widgets/series.spec.ts +0 -42
- package/src/lib/widgets/series.ts +0 -56
- package/src/lib/widgets/status-progress/status-progress.descriptor.ts +0 -19
- package/src/lib/widgets/status-progress/status-progress.spec.ts +0 -63
- package/src/lib/widgets/status-progress/status-progress.ts +0 -104
- package/src/lib/widgets/widget-host/widget-host.spec.ts +0 -179
- package/src/lib/widgets/widget-host/widget-host.ts +0 -99
- package/src/public-api.ts +0 -104
- package/tinya-code-gas-admin-ui-0.2.0-alpha.15.tgz +0 -0
- package/tsconfig.lib.json +0 -13
- package/tsconfig.lib.prod.json +0 -11
- package/tsconfig.spec.json +0 -10
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { Component, input, output } from '@angular/core';
|
|
2
|
-
import { LucideCircleCheck, LucideCircleX, LucideInfo, LucideX } from '@lucide/angular';
|
|
3
|
-
|
|
4
|
-
export type ToastKind = 'success' | 'error' | 'info';
|
|
5
|
-
|
|
6
|
-
/** Estructuralmente idéntico a `core`'s `Toast` — no importa `core`. */
|
|
7
|
-
export interface ToastItem {
|
|
8
|
-
id: number;
|
|
9
|
-
kind: ToastKind;
|
|
10
|
-
message: string;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Contenedor visual de toasts: fijo arriba a la derecha, región `status`
|
|
15
|
-
* viva (polite) para que se anuncien los mensajes. Recibe los toasts por
|
|
16
|
-
* `input()` desde quien sí tiene `NotificationService` (Fase 7).
|
|
17
|
-
*/
|
|
18
|
-
@Component({
|
|
19
|
-
selector: 'app-toast',
|
|
20
|
-
imports: [LucideCircleCheck, LucideCircleX, LucideInfo, LucideX],
|
|
21
|
-
host: {
|
|
22
|
-
class: 'pointer-events-none fixed right-4 top-4 z-50 flex w-full max-w-sm flex-col gap-2',
|
|
23
|
-
role: 'status',
|
|
24
|
-
'aria-live': 'polite',
|
|
25
|
-
},
|
|
26
|
-
template: `
|
|
27
|
-
@for (toast of toasts(); track toast.id) {
|
|
28
|
-
<div
|
|
29
|
-
class="pointer-events-auto flex items-start gap-3 border-l-4 bg-surface-raised p-4 shadow-lg"
|
|
30
|
-
[class.border-l-success]="toast.kind === 'success'"
|
|
31
|
-
[class.border-l-danger]="toast.kind === 'error'"
|
|
32
|
-
[class.border-l-primary]="toast.kind === 'info'"
|
|
33
|
-
>
|
|
34
|
-
@if (toast.kind === 'success') {
|
|
35
|
-
<svg lucideCircleCheck size="18" class="mt-0.5 shrink-0 text-success" />
|
|
36
|
-
} @else if (toast.kind === 'error') {
|
|
37
|
-
<svg lucideCircleX size="18" class="mt-0.5 shrink-0 text-danger" />
|
|
38
|
-
} @else {
|
|
39
|
-
<svg lucideInfo size="18" class="mt-0.5 shrink-0 text-primary" />
|
|
40
|
-
}
|
|
41
|
-
<p class="flex-1 text-sm font-medium">{{ toast.message }}</p>
|
|
42
|
-
<button
|
|
43
|
-
type="button"
|
|
44
|
-
class="flex min-h-11 min-w-11 items-center justify-center rounded text-neutral hover:bg-neutral/10 focus-visible:outline-2 focus-visible:outline-primary lg:min-h-0 lg:min-w-0"
|
|
45
|
-
[attr.aria-label]="closeLabel()"
|
|
46
|
-
(click)="dismissed.emit(toast.id)"
|
|
47
|
-
>
|
|
48
|
-
<svg lucideX size="18" />
|
|
49
|
-
</button>
|
|
50
|
-
</div>
|
|
51
|
-
}
|
|
52
|
-
`,
|
|
53
|
-
})
|
|
54
|
-
export class Toast {
|
|
55
|
-
readonly toasts = input<ToastItem[]>([]);
|
|
56
|
-
readonly closeLabel = input('Cerrar');
|
|
57
|
-
readonly dismissed = output<number>();
|
|
58
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Anchos de columna del layout (base.md §4: `width` 1–12).
|
|
3
|
-
*
|
|
4
|
-
* `sm:col-span-*` como literales: Tailwind v4 solo genera las clases que ve
|
|
5
|
-
* en el código, así que no pueden construirse por concatenación.
|
|
6
|
-
*/
|
|
7
|
-
const COL_SPANS: Record<number, string> = {
|
|
8
|
-
1: 'sm:col-span-1',
|
|
9
|
-
2: 'sm:col-span-2',
|
|
10
|
-
3: 'sm:col-span-3',
|
|
11
|
-
4: 'sm:col-span-4',
|
|
12
|
-
5: 'sm:col-span-5',
|
|
13
|
-
6: 'sm:col-span-6',
|
|
14
|
-
7: 'sm:col-span-7',
|
|
15
|
-
8: 'sm:col-span-8',
|
|
16
|
-
9: 'sm:col-span-9',
|
|
17
|
-
10: 'sm:col-span-10',
|
|
18
|
-
11: 'sm:col-span-11',
|
|
19
|
-
12: 'sm:col-span-12',
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
/** Clase `sm:col-span-*` para el ancho del campo (por defecto: 12). */
|
|
23
|
-
export function colSpanClass(width: number | undefined): string {
|
|
24
|
-
return COL_SPANS[width ?? 12];
|
|
25
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import type { FieldCondition } from '../../core/models/schema.model';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Evaluador de condiciones `visibleWhen` / `readonlyWhen` (base.md §4):
|
|
5
|
-
* comparaciones simples (igual, distinto, está vacío, contiene) sobre otros
|
|
6
|
-
* campos del mismo nivel o del registro raíz.
|
|
7
|
-
*
|
|
8
|
-
* Nota de arquitectura: la importación de `FieldCondition` es SOLO de tipo
|
|
9
|
-
* (se elimina en compilación) — no hay dependencia de runtime con `core`.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
/** Resuelve una condición. Se resuelve contra el nivel propio; si la clave
|
|
13
|
-
* no existe ahí, contra la raíz del registro. */
|
|
14
|
-
export function evaluateCondition(
|
|
15
|
-
condition: FieldCondition,
|
|
16
|
-
siblingValues: Record<string, unknown>,
|
|
17
|
-
rootValues: Record<string, unknown> = {},
|
|
18
|
-
): boolean {
|
|
19
|
-
const source = condition.field in siblingValues ? siblingValues : rootValues;
|
|
20
|
-
const actual = source[condition.field];
|
|
21
|
-
const expected = condition.value;
|
|
22
|
-
|
|
23
|
-
switch (condition.operator) {
|
|
24
|
-
case 'eq':
|
|
25
|
-
return actual === expected;
|
|
26
|
-
case 'ne':
|
|
27
|
-
return actual !== expected;
|
|
28
|
-
case 'empty':
|
|
29
|
-
return isEmpty(actual);
|
|
30
|
-
case 'contains':
|
|
31
|
-
return contains(actual, expected);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function isEmpty(value: unknown): boolean {
|
|
36
|
-
if (value === undefined || value === null || value === '') {
|
|
37
|
-
return true;
|
|
38
|
-
}
|
|
39
|
-
if (Array.isArray(value)) {
|
|
40
|
-
return value.length === 0;
|
|
41
|
-
}
|
|
42
|
-
return false;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function contains(actual: unknown, expected: unknown): boolean {
|
|
46
|
-
if (typeof actual === 'string') {
|
|
47
|
-
return actual.includes(String(expected));
|
|
48
|
-
}
|
|
49
|
-
if (Array.isArray(actual)) {
|
|
50
|
-
return actual.includes(expected);
|
|
51
|
-
}
|
|
52
|
-
return false;
|
|
53
|
-
}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import type { FieldSchema } from '../../core/models/schema.model';
|
|
2
|
-
import { seedFieldValue } from './seed';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Enlaces de selección dependiente (guía §1, plan 6.6).
|
|
6
|
-
*
|
|
7
|
-
* `condition-evaluator.ts` sólo RESUELVE condiciones (`visibleWhen` /
|
|
8
|
-
* `readonlyWhen`): no resetea nada. Acá se levanta el mapa de «cuando el
|
|
9
|
-
* padre cambie, limpiar el hijo» que `form-view` observa con un `effect`.
|
|
10
|
-
*
|
|
11
|
-
* Alcance: raíz y `group` (contenedores estáticos). Los ítems de un `list`
|
|
12
|
-
* se crean y borran mientras se edita y sus rutas no son estables — un
|
|
13
|
-
* dependiente dentro de una lista queda sin resetear, así que no se promete.
|
|
14
|
-
*/
|
|
15
|
-
export interface DependsOnLink {
|
|
16
|
-
/** Ruta de contenedores desde la raíz: `[]` en la raíz, `['contacto']` en un grupo. */
|
|
17
|
-
container: string[];
|
|
18
|
-
childKey: string;
|
|
19
|
-
parentKey: string;
|
|
20
|
-
/** Valor con el que se limpia el hijo (el default del schema). */
|
|
21
|
-
resetValue: unknown;
|
|
22
|
-
/** Clave estable del enlace para el snapshot de comparación. */
|
|
23
|
-
id: string;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function collectDependsOn(
|
|
27
|
-
fields: readonly FieldSchema[],
|
|
28
|
-
container: string[] = [],
|
|
29
|
-
): DependsOnLink[] {
|
|
30
|
-
const links: DependsOnLink[] = [];
|
|
31
|
-
for (const field of fields) {
|
|
32
|
-
if (field.type === 'group') {
|
|
33
|
-
links.push(...collectDependsOn(field.fields, [...container, field.key]));
|
|
34
|
-
continue;
|
|
35
|
-
}
|
|
36
|
-
if (field.dependsOn === undefined) {
|
|
37
|
-
continue;
|
|
38
|
-
}
|
|
39
|
-
links.push({
|
|
40
|
-
container,
|
|
41
|
-
childKey: field.key,
|
|
42
|
-
parentKey: field.dependsOn,
|
|
43
|
-
resetValue: seedFieldValue(field),
|
|
44
|
-
id: `${container.join('.')}.${field.key}<-${field.dependsOn}`,
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
return links;
|
|
48
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Utilidades de foco para los diálogos (`modal` y `drawer`).
|
|
3
|
-
*
|
|
4
|
-
* Los dos componentes comparten el mismo contrato: atrapan el Tab dentro del
|
|
5
|
-
* panel para que el foco no se pierda detrás del fondo oscuro. Tener el
|
|
6
|
-
* selector y el test de visibilidad en un solo sitio evita que los dos
|
|
7
|
-
* diálogos diverjan.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
/** Candidatos a foco dentro del diálogo (el orden del foco del navegador). */
|
|
11
|
-
export const FOCUSABLE =
|
|
12
|
-
'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* ¿Se puede enfocar este elemento ahora mismo?
|
|
16
|
-
*
|
|
17
|
-
* No se usa `offsetParent`: en jsdom es siempre `null` y el atrapado del Tab
|
|
18
|
-
* quedaría sin opérculo en los tests. `checkVisibility` da la respuesta
|
|
19
|
-
* exacta en navegador; si no existe (jsdom), se confía en lo que hay en el
|
|
20
|
-
* DOM y sólo se descarta lo marcado `hidden`.
|
|
21
|
-
*/
|
|
22
|
-
export function isVisibleNow(element: HTMLElement): boolean {
|
|
23
|
-
if (element.hasAttribute('hidden') || element.closest('[hidden]') !== null) {
|
|
24
|
-
return false;
|
|
25
|
-
}
|
|
26
|
-
const check = (element as { checkVisibility?: (options?: unknown) => boolean }).checkVisibility;
|
|
27
|
-
return typeof check !== 'function' || check.call(element);
|
|
28
|
-
}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Lectura de valores por ruta (ej. `faq[2].answer`, `address.city`) —
|
|
3
|
-
* celdas del listado y valores anidados de los singleton (base.md §10).
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/** Segmentos de una ruta: claves de objeto y índices de arreglo. */
|
|
7
|
-
export type PathSegment = string | number;
|
|
8
|
-
|
|
9
|
-
/** `faq[2].answer` → `['faq', 2, 'answer']`. */
|
|
10
|
-
export function parsePath(path: string): PathSegment[] {
|
|
11
|
-
const segments: PathSegment[] = [];
|
|
12
|
-
for (const part of path.split('.')) {
|
|
13
|
-
if (part === '') {
|
|
14
|
-
continue;
|
|
15
|
-
}
|
|
16
|
-
const match = /^([^[\]]*)((?:\[\d+\])+)$/.exec(part);
|
|
17
|
-
if (match) {
|
|
18
|
-
const key = match[1];
|
|
19
|
-
if (key !== '') {
|
|
20
|
-
segments.push(key);
|
|
21
|
-
}
|
|
22
|
-
for (const bracket of match[2].matchAll(/\[(\d+)\]/g)) {
|
|
23
|
-
segments.push(Number(bracket[1]));
|
|
24
|
-
}
|
|
25
|
-
} else {
|
|
26
|
-
segments.push(part);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
return segments;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/** Lee el valor en `path`; `undefined` si algún tramo no existe. */
|
|
33
|
-
export function getValue<T = unknown>(source: unknown, path: string): T | undefined {
|
|
34
|
-
let current: unknown = source;
|
|
35
|
-
for (const segment of parsePath(path)) {
|
|
36
|
-
if (current === null || current === undefined || typeof current !== 'object') {
|
|
37
|
-
return undefined;
|
|
38
|
-
}
|
|
39
|
-
if (typeof segment === 'number') {
|
|
40
|
-
if (!Array.isArray(current)) {
|
|
41
|
-
return undefined;
|
|
42
|
-
}
|
|
43
|
-
current = current[segment];
|
|
44
|
-
} else {
|
|
45
|
-
current = (current as Record<string, unknown>)[segment];
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
return current as T;
|
|
49
|
-
}
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ordenamiento con huecos (base.md §8).
|
|
3
|
-
*
|
|
4
|
-
* Reglas:
|
|
5
|
-
* - Posiciones enteras con separación fija (paso 1000: 1000, 2000, 3000…).
|
|
6
|
-
* - Mover entre dos vecinos → punto medio; solo cambia ese elemento.
|
|
7
|
-
* - Mover al inicio → la mitad de la posición del primero.
|
|
8
|
-
* - Mover al final → la del último + paso.
|
|
9
|
-
* - Lista vacía → el paso.
|
|
10
|
-
* - Si los vecinos quedan sin espacio (diferencia < 2) → rebalanceo:
|
|
11
|
-
* se renumera toda la lista con el paso original conservando el orden
|
|
12
|
-
* y luego se aplica el movimiento.
|
|
13
|
-
* - Nuevos elementos → última posición + paso.
|
|
14
|
-
*
|
|
15
|
-
* El usuario nunca ve los números; la UI ordena por `position` ascendente.
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
/** Separación fija entre posiciones. */
|
|
19
|
-
export const GAP = 1000;
|
|
20
|
-
|
|
21
|
-
export interface PositionedItem {
|
|
22
|
-
key: string;
|
|
23
|
-
position: number;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface PositionChange {
|
|
27
|
-
key: string;
|
|
28
|
-
position: number;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export interface ReorderResult {
|
|
32
|
-
/** `true` → hubo rebalanceo: `changes` contiene el lote completo renumerado. */
|
|
33
|
-
rebalanced: boolean;
|
|
34
|
-
/** Cambios a enviar: solo el elemento movido, o el lote completo. */
|
|
35
|
-
changes: PositionChange[];
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/** Punto medio entre dos vecinos (solo el elemento movido cambia). */
|
|
39
|
-
export function moveBetween(prevPosition: number, nextPosition: number): number {
|
|
40
|
-
return Math.floor((prevPosition + nextPosition) / 2);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/** Mover al inicio: la mitad de la posición del primero. */
|
|
44
|
-
export function moveToStart(firstPosition: number): number {
|
|
45
|
-
return Math.floor(firstPosition / 2);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/** Mover al final: la del último + paso. */
|
|
49
|
-
export function moveToEnd(lastPosition: number): number {
|
|
50
|
-
return lastPosition + GAP;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/** Posición de un elemento nuevo: al final (lista vacía → el paso). */
|
|
54
|
-
export function appendPosition(items: PositionedItem[]): number {
|
|
55
|
-
const sorted = sortItems(items);
|
|
56
|
-
if (sorted.length === 0) {
|
|
57
|
-
return GAP;
|
|
58
|
-
}
|
|
59
|
-
return moveToEnd(sorted[sorted.length - 1].position);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/** ¿Falta espacio para insertar en `targetIndex`? (base.md §8) */
|
|
63
|
-
export function needsRebalance(
|
|
64
|
-
items: PositionedItem[],
|
|
65
|
-
movingKey: string,
|
|
66
|
-
targetIndex: number,
|
|
67
|
-
): boolean {
|
|
68
|
-
const without = sortItems(items.filter((item) => item.key !== movingKey));
|
|
69
|
-
if (without.length === 0) {
|
|
70
|
-
return false; // lista vacía → el paso siempre cabe
|
|
71
|
-
}
|
|
72
|
-
const index = clamp(targetIndex, 0, without.length);
|
|
73
|
-
if (index === without.length) {
|
|
74
|
-
return false; // al final → +paso siempre cabe
|
|
75
|
-
}
|
|
76
|
-
if (index === 0) {
|
|
77
|
-
const first = without[0];
|
|
78
|
-
return first.position - moveToStart(first.position) < 2;
|
|
79
|
-
}
|
|
80
|
-
const prev = without[index - 1];
|
|
81
|
-
const next = without[index];
|
|
82
|
-
return next.position - prev.position < 2;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/** Renumera la lista completa con el paso original, conservando el orden. */
|
|
86
|
-
export function rebalance(items: PositionedItem[], gap: number = GAP): PositionChange[] {
|
|
87
|
-
return sortItems(items).map((item, index) => ({
|
|
88
|
-
key: item.key,
|
|
89
|
-
position: (index + 1) * gap,
|
|
90
|
-
}));
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Calcula el resultado de soltar `movingKey` en `targetIndex`
|
|
95
|
-
* (índice que ocupará en la lista SIN el elemento movido, ya ordenada).
|
|
96
|
-
*/
|
|
97
|
-
export function computeMove(
|
|
98
|
-
items: PositionedItem[],
|
|
99
|
-
movingKey: string,
|
|
100
|
-
targetIndex: number,
|
|
101
|
-
): ReorderResult {
|
|
102
|
-
const sorted = sortItems(items);
|
|
103
|
-
if (!sorted.some((item) => item.key === movingKey)) {
|
|
104
|
-
throw new Error(`Elemento no encontrado: ${movingKey}`);
|
|
105
|
-
}
|
|
106
|
-
const without = sorted.filter((item) => item.key !== movingKey);
|
|
107
|
-
const index = clamp(targetIndex, 0, without.length);
|
|
108
|
-
|
|
109
|
-
if (needsRebalance(sorted, movingKey, targetIndex)) {
|
|
110
|
-
// Renumerar toda la lista y luego aplicar el movimiento.
|
|
111
|
-
const renumbered = rebalance(without);
|
|
112
|
-
const position = positionFor(renumbered, index);
|
|
113
|
-
return {
|
|
114
|
-
rebalanced: true,
|
|
115
|
-
changes: [...renumbered, { key: movingKey, position }],
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
return {
|
|
120
|
-
rebalanced: false,
|
|
121
|
-
changes: [{ key: movingKey, position: positionFor(without, index) }],
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function positionFor(without: PositionChange[], index: number): number {
|
|
126
|
-
if (without.length === 0) {
|
|
127
|
-
return GAP;
|
|
128
|
-
}
|
|
129
|
-
if (index === 0) {
|
|
130
|
-
return moveToStart(without[0].position);
|
|
131
|
-
}
|
|
132
|
-
if (index === without.length) {
|
|
133
|
-
return moveToEnd(without[without.length - 1].position);
|
|
134
|
-
}
|
|
135
|
-
return moveBetween(without[index - 1].position, without[index].position);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function sortItems(items: PositionedItem[]): PositionedItem[] {
|
|
139
|
-
return [...items].sort((a, b) => a.position - b.position);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
function clamp(value: number, min: number, max: number): number {
|
|
143
|
-
return Math.min(Math.max(value, min), max);
|
|
144
|
-
}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
|
|
3
|
-
import type { FieldSchema } from '../../core/models/schema.model';
|
|
4
|
-
import { register } from '../../core/registry/render-registry';
|
|
5
|
-
import { fieldDescriptors } from '../../fields/descriptors';
|
|
6
|
-
import { seedValue, type SeedKind } from './seed';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* D32 · R3.8 — la siembra vive en el descriptor, no en un `switch`.
|
|
10
|
-
*
|
|
11
|
-
* Tres cosas distintas, ninguna cosmética:
|
|
12
|
-
*
|
|
13
|
-
* 1. **Oro por descriptor** — cada tipo registrado siembra la forma que su
|
|
14
|
-
* `seed` declara. Si alguien cambia `'number'` por `'string'` en un
|
|
15
|
-
* descriptor, el tipo empieza a nacer en `''` y este test lo dice antes
|
|
16
|
-
* que un formulario en producción.
|
|
17
|
-
* 2. **Degradación** — un tipo fuera del catálogo siembra `undefined`, lo
|
|
18
|
-
* mismo que hace el render (R1.10): no se pinta, no existe.
|
|
19
|
-
* 3. **Fail-loud** — un descriptor registrado *sin* `seed` lanza, en vez de
|
|
20
|
-
* sembrar `undefined` en silencio.
|
|
21
|
-
*
|
|
22
|
-
* El vitest aísla este fichero, así que el descriptor falso registrado en el
|
|
23
|
-
* último test no se filtra al resto de la suite.
|
|
24
|
-
*/
|
|
25
|
-
describe('seedValue — siembra declarada por el descriptor', () => {
|
|
26
|
-
const FORMA: Record<SeedKind, unknown> = {
|
|
27
|
-
string: '',
|
|
28
|
-
number: null,
|
|
29
|
-
boolean: false,
|
|
30
|
-
array: [],
|
|
31
|
-
// `group` con `fields: []` siembra un objeto vacío.
|
|
32
|
-
recursive: {},
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const campos = fieldDescriptors.filter((descriptor) => descriptor.kind === 'field');
|
|
36
|
-
|
|
37
|
-
for (const descriptor of campos) {
|
|
38
|
-
it(`«${descriptor.type}» siembra la forma «${descriptor.seed}»`, () => {
|
|
39
|
-
const campo = {
|
|
40
|
-
key: 'k',
|
|
41
|
-
label: 'K',
|
|
42
|
-
type: descriptor.type,
|
|
43
|
-
fields: [],
|
|
44
|
-
} as unknown as FieldSchema;
|
|
45
|
-
expect(seedValue(campo)).toEqual(FORMA[descriptor.seed as SeedKind]);
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
it('un tipo fuera del catálogo siembra `undefined` y no revienta', () => {
|
|
50
|
-
const campo = { key: 'k', label: 'K', type: 'sombrero' } as unknown as FieldSchema;
|
|
51
|
-
expect(seedValue(campo)).toBeUndefined();
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('un descriptor sin `seed` lanza en sembrar', () => {
|
|
55
|
-
register('field', {
|
|
56
|
-
kind: 'field',
|
|
57
|
-
type: 'sin-siembra',
|
|
58
|
-
since: '1.0',
|
|
59
|
-
load: () => Promise.resolve(class Vacia {}),
|
|
60
|
-
});
|
|
61
|
-
const campo = { key: 'k', label: 'K', type: 'sin-siembra' } as unknown as FieldSchema;
|
|
62
|
-
expect(() => seedValue(campo)).toThrow(/no declara `seed`/);
|
|
63
|
-
});
|
|
64
|
-
});
|
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
import type { FieldSchema, GroupField } from '../../core/models/schema.model';
|
|
2
|
-
import { get } from '../../core/registry/render-registry';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Forma del valor inicial que siembra un campo (D32 · R3.8).
|
|
6
|
-
*
|
|
7
|
-
* Son **todos** los valores posibles del dominio: un campo de formulario
|
|
8
|
-
* nuevo nace como cadena, número, booleano, lista o como contenedor que
|
|
9
|
-
* siembra sus hijos. Si un tipo llegara a necesitar otra forma, ampliar este
|
|
10
|
-
* `type` obliga a compilar el `switch` de `seedValue` — que es exactamente
|
|
11
|
-
* el fail-loud que la regla 4 pide.
|
|
12
|
-
*/
|
|
13
|
-
export type SeedKind = 'string' | 'number' | 'boolean' | 'array' | 'recursive';
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Siembra del modelo del formulario: un valor por defecto para CADA campo
|
|
17
|
-
* declarado en el schema (base.md §10).
|
|
18
|
-
*
|
|
19
|
-
* Compartido entre `form-model` (crear/recargar) y los componentes de lista
|
|
20
|
-
* (crear ítems nuevos con la forma de sus `itemFields`).
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Valor inicial cuando el schema no declara `default`.
|
|
25
|
-
*
|
|
26
|
-
* **D32 · ya no despacha por tipo.** El `switch` de 20 ramas que obligaba a
|
|
27
|
-
* editar este fichero al añadir un campo — y que §0 contaba como tocar el
|
|
28
|
-
* motor — pasa a resolver el nombre que declara el propio descriptor
|
|
29
|
-
* (`RenderDescriptor.seed`). «Añadir un tipo» queda en carpeta + manifiesto
|
|
30
|
-
* + unión: este archivo no se entera.
|
|
31
|
-
*
|
|
32
|
-
* Dos salidas distintas, ninguna silenciosa:
|
|
33
|
-
*
|
|
34
|
-
* · **tipo fuera del catálogo** ⇒ `undefined`, la misma degradación
|
|
35
|
-
* controlada que el render (R1.10): el campo no se pinta, así que su
|
|
36
|
-
* valor no existe;
|
|
37
|
-
* · **descriptor sin `seed`** ⇒ lanza. Es un error de programación que
|
|
38
|
-
* `core/registry/contract.spec.ts` detecta en CI antes de llegar acá.
|
|
39
|
-
*/
|
|
40
|
-
export function seedValue(field: FieldSchema): unknown {
|
|
41
|
-
const descriptor = get('field', field.type);
|
|
42
|
-
|
|
43
|
-
if (!descriptor) {
|
|
44
|
-
return undefined;
|
|
45
|
-
}
|
|
46
|
-
if (!descriptor.seed) {
|
|
47
|
-
throw new Error(`[seed] el descriptor de "${field.type}" no declara \`seed\`.`);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
switch (descriptor.seed) {
|
|
51
|
-
case 'string':
|
|
52
|
-
return '';
|
|
53
|
-
case 'number':
|
|
54
|
-
return null;
|
|
55
|
-
case 'boolean':
|
|
56
|
-
return false;
|
|
57
|
-
case 'array':
|
|
58
|
-
return [];
|
|
59
|
-
case 'recursive':
|
|
60
|
-
return seedHijos(field, descriptor.seed);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Siembra recursiva de un contenedor.
|
|
66
|
-
*
|
|
67
|
-
* El único `seed` recursivo del catálogo es `group`, pero el registro no lo
|
|
68
|
-
* sabe — sólo lleva un nombre. El estrechado se comprueba aquí y **falla
|
|
69
|
-
* ruidoso** si algún día un tipo declara `'recursive'` sin traer subcampos.
|
|
70
|
-
*/
|
|
71
|
-
function seedHijos(field: FieldSchema, seed: 'recursive'): unknown {
|
|
72
|
-
if (seed !== 'recursive' || !esGrupo(field)) {
|
|
73
|
-
throw new Error(`[seed] "${field.type}" declara \`seed: 'recursive'\` pero no trae subcampos.`);
|
|
74
|
-
}
|
|
75
|
-
return seedFields(field.fields);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/** Estrechado de forma, no de despacho: no decide qué hacer, sólo accede. */
|
|
79
|
-
function esGrupo(field: FieldSchema): field is GroupField {
|
|
80
|
-
return field.type === 'group';
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/** Valor sembrado de un campo, fusionando `default` con la forma del schema. */
|
|
84
|
-
export function seedFieldValue(field: FieldSchema): unknown {
|
|
85
|
-
if (field.default === undefined) {
|
|
86
|
-
return seedValue(field);
|
|
87
|
-
}
|
|
88
|
-
if (field.type === 'group' && isRecord(field.default)) {
|
|
89
|
-
// Un `default` de grupo podría traer claves incompletas: los hijos se
|
|
90
|
-
// acceden por árbol, así que la forma completa es obligatoria.
|
|
91
|
-
return { ...seedFields(field.fields), ...field.default };
|
|
92
|
-
}
|
|
93
|
-
if (field.type === 'list' && Array.isArray(field.default)) {
|
|
94
|
-
return field.default.map((item) =>
|
|
95
|
-
isRecord(item) ? { ...seedFields(field.itemFields), ...item } : item,
|
|
96
|
-
);
|
|
97
|
-
}
|
|
98
|
-
return field.default;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Siembra recursiva: los `group` anidan sus propios campos. Exportada porque
|
|
103
|
-
* la lista crea sus ítems nuevos con la misma forma (`base.md §6`).
|
|
104
|
-
*/
|
|
105
|
-
export function seedFields(fields: readonly FieldSchema[]): Record<string, unknown> {
|
|
106
|
-
const model: Record<string, unknown> = {};
|
|
107
|
-
for (const field of fields) {
|
|
108
|
-
model[field.key] = seedFieldValue(field);
|
|
109
|
-
}
|
|
110
|
-
return model;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Fusiona el registro leído con la forma sembrada del schema.
|
|
115
|
-
*
|
|
116
|
-
* El backend puede omitir claves (p. ej. un ítem guardado antes de que el
|
|
117
|
-
* schema agregara un campo): sin esta fusión el árbol no tendría nodo para
|
|
118
|
-
* ese campo y `childTree` lanzaría al renderizar. Lo ausente toma su
|
|
119
|
-
* `default` y los extras del registro (p. ej. `position` de los ítems) se
|
|
120
|
-
* conservan.
|
|
121
|
-
*/
|
|
122
|
-
export function mergeRecord(
|
|
123
|
-
fields: readonly FieldSchema[],
|
|
124
|
-
record: Record<string, unknown>,
|
|
125
|
-
): Record<string, unknown> {
|
|
126
|
-
const merged: Record<string, unknown> = {};
|
|
127
|
-
for (const field of fields) {
|
|
128
|
-
const incoming = record[field.key];
|
|
129
|
-
if (field.type === 'group') {
|
|
130
|
-
merged[field.key] = isRecord(incoming)
|
|
131
|
-
? mergeRecord(field.fields, incoming)
|
|
132
|
-
: seedFieldValue(field);
|
|
133
|
-
continue;
|
|
134
|
-
}
|
|
135
|
-
if (field.type === 'list') {
|
|
136
|
-
const itemSeed = seedFields(field.itemFields);
|
|
137
|
-
merged[field.key] = Array.isArray(incoming)
|
|
138
|
-
? incoming.map((item) => (isRecord(item) ? { ...itemSeed, ...item } : item))
|
|
139
|
-
: seedFieldValue(field);
|
|
140
|
-
continue;
|
|
141
|
-
}
|
|
142
|
-
merged[field.key] = incoming !== undefined ? incoming : seedFieldValue(field);
|
|
143
|
-
}
|
|
144
|
-
return merged;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
export function isRecord(value: unknown): value is Record<string, unknown> {
|
|
148
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
149
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Normaliza texto a slug: minúsculas, sin acentos, separados por guiones.
|
|
3
|
-
* Ej.: `Lomo Saltado Ñoño` → `lomo-saltado-nonio`.
|
|
4
|
-
*/
|
|
5
|
-
export function slugify(value: string): string {
|
|
6
|
-
return value
|
|
7
|
-
.normalize('NFD')
|
|
8
|
-
.replace(/[\u0300-\u036f]/g, '')
|
|
9
|
-
.toLowerCase()
|
|
10
|
-
.trim()
|
|
11
|
-
.replace(/[^a-z0-9]+/g, '-')
|
|
12
|
-
.replace(/^-+|-+$/g, '');
|
|
13
|
-
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Validadores puros sobre valores (base.md §4): longitud por palabras.
|
|
3
|
-
* Devuelven `null` cuando el valor es válido o cuando no aplica.
|
|
4
|
-
* (Longitud por caracteres, patrón y rango los aplica `@angular/forms/signals`
|
|
5
|
-
* vía `form-schema.ts`: `minLengthRule`/`maxLengthRule`/`patternRule`/`minRule`.)
|
|
6
|
-
*
|
|
7
|
-
* Reglas comunes:
|
|
8
|
-
* - Valor vacío (`undefined` / `null` / `''`) → `null`: la obligatoriedad
|
|
9
|
-
* la maneja `required` aparte. Solo espacios NO cuenta como vacío.
|
|
10
|
-
* - Valores de otro tipo → `null` (no validan aquí; los cubre el campo).
|
|
11
|
-
*
|
|
12
|
-
* Los mensajes se muestran tal cual bajo el campo (Fase 10), en español.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
export function validateMinWords(value: unknown, min: number): string | null {
|
|
16
|
-
const words = wordCount(value);
|
|
17
|
-
if (words === null) {
|
|
18
|
-
return null;
|
|
19
|
-
}
|
|
20
|
-
return words >= min ? null : `Debe tener al menos ${min} palabras.`;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function validateMaxWords(value: unknown, max: number): string | null {
|
|
24
|
-
const words = wordCount(value);
|
|
25
|
-
if (words === null) {
|
|
26
|
-
return null;
|
|
27
|
-
}
|
|
28
|
-
return words <= max ? null : `Como máximo ${max} palabras.`;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function isEmpty(value: unknown): boolean {
|
|
32
|
-
return value === undefined || value === null || value === '';
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/** Conteo de palabras; `null` si el valor no es texto o está vacío. */
|
|
36
|
-
function wordCount(value: unknown): number | null {
|
|
37
|
-
if (isEmpty(value) || typeof value !== 'string') {
|
|
38
|
-
return null;
|
|
39
|
-
}
|
|
40
|
-
return value.trim().split(/\s+/).filter(Boolean).length;
|
|
41
|
-
}
|