@caribeng/ui 22.2.0 → 22.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +311 -231
- package/fesm2022/caribeng-ui-accordion.mjs +2 -2
- package/fesm2022/caribeng-ui-account-picker.mjs +533 -0
- package/fesm2022/caribeng-ui-aging.mjs +488 -0
- package/fesm2022/{caribeng-ui-caribe-alert.mjs → caribeng-ui-alert.mjs} +45 -45
- package/fesm2022/caribeng-ui-amount.mjs +249 -0
- package/fesm2022/caribeng-ui-anchor.mjs +2 -2
- package/fesm2022/{caribeng-ui-caribe-animation-lottie.mjs → caribeng-ui-animation-lottie.mjs} +41 -41
- package/fesm2022/caribeng-ui-audit-log.mjs +323 -0
- package/fesm2022/caribeng-ui-avatar.mjs +4 -4
- package/fesm2022/caribeng-ui-badge.mjs +8 -2
- package/fesm2022/caribeng-ui-breadcrumb.mjs +26 -9
- package/fesm2022/{caribeng-ui-caribe-button-group.mjs → caribeng-ui-button-group.mjs} +15 -15
- package/fesm2022/caribeng-ui-button.mjs +2 -2
- package/fesm2022/{caribeng-ui-caribe-card-content.mjs → caribeng-ui-card-content.mjs} +39 -39
- package/fesm2022/{caribeng-ui-caribe-card-move.mjs → caribeng-ui-card-move.mjs} +65 -65
- package/fesm2022/{caribeng-ui-caribe-card-primary.mjs → caribeng-ui-card-primary.mjs} +31 -31
- package/fesm2022/{caribeng-ui-caribe-card-select.mjs → caribeng-ui-card-select.mjs} +67 -67
- package/fesm2022/caribeng-ui-carousel.mjs +2 -2
- package/fesm2022/caribeng-ui-cascadeselect.mjs +45 -9
- package/fesm2022/caribeng-ui-chart.mjs +8 -5
- package/fesm2022/caribeng-ui-chat.mjs +1303 -0
- package/fesm2022/{caribeng-ui-caribe-coachmark.mjs → caribeng-ui-coachmark.mjs} +85 -85
- package/fesm2022/caribeng-ui-code-block.mjs +332 -0
- package/fesm2022/caribeng-ui-colorpicker.mjs +99 -5
- package/fesm2022/caribeng-ui-command-palette.mjs +476 -0
- package/fesm2022/caribeng-ui-comments.mjs +864 -0
- package/fesm2022/caribeng-ui-confirmdialog.mjs +78 -13
- package/fesm2022/caribeng-ui-confirmpopup.mjs +91 -16
- package/fesm2022/caribeng-ui-contextmenu.mjs +8 -4
- package/fesm2022/caribeng-ui-copy-button.mjs +230 -0
- package/fesm2022/caribeng-ui-datagrid.mjs +1635 -0
- package/fesm2022/caribeng-ui-dataview.mjs +6 -3
- package/fesm2022/caribeng-ui-datepicker.mjs +2 -2
- package/fesm2022/caribeng-ui-dock.mjs +52 -12
- package/fesm2022/caribeng-ui-document.mjs +495 -0
- package/fesm2022/{caribeng-ui-caribe-dots-indicator.mjs → caribeng-ui-dots-indicator.mjs} +41 -41
- package/fesm2022/caribeng-ui-drawer.mjs +9 -4
- package/fesm2022/caribeng-ui-dynamic-form.mjs +1581 -0
- package/fesm2022/caribeng-ui-editor.mjs +2324 -1311
- package/fesm2022/{caribeng-ui-caribe-expansion-panel.mjs → caribeng-ui-expansion-panel.mjs} +39 -39
- package/fesm2022/{caribeng-ui-caribe-fab-menu.mjs → caribeng-ui-fab-menu.mjs} +75 -75
- package/fesm2022/caribeng-ui-fieldset.mjs +2 -2
- package/fesm2022/caribeng-ui-file-card.mjs +134 -17
- package/fesm2022/{caribeng-ui-caribe-file-download.mjs → caribeng-ui-file-download.mjs} +63 -63
- package/fesm2022/caribeng-ui-financial-report.mjs +380 -0
- package/fesm2022/{caribeng-ui-caribe-float-menu.mjs → caribeng-ui-float-menu.mjs} +31 -31
- package/fesm2022/caribeng-ui-floatlabel.mjs +2 -2
- package/fesm2022/caribeng-ui-flow.mjs +1508 -0
- package/fesm2022/caribeng-ui-folder-card.mjs +122 -12
- package/fesm2022/{caribeng-ui-caribe-footer.mjs → caribeng-ui-footer.mjs} +19 -19
- package/fesm2022/caribeng-ui-gantt.mjs +1094 -0
- package/fesm2022/{caribeng-ui-caribe-header.mjs → caribeng-ui-header.mjs} +49 -49
- package/fesm2022/{caribeng-ui-caribe-icon-button.mjs → caribeng-ui-icon-button.mjs} +23 -23
- package/fesm2022/caribeng-ui-image-cropper.mjs +671 -0
- package/fesm2022/caribeng-ui-import-wizard.mjs +746 -0
- package/fesm2022/caribeng-ui-infinite-scroll.mjs +226 -0
- package/fesm2022/caribeng-ui-inlinemessage.mjs +2 -2
- package/fesm2022/caribeng-ui-input-number.mjs +22 -6
- package/fesm2022/{caribeng-ui-caribe-input-search.mjs → caribeng-ui-input-search.mjs} +65 -65
- package/fesm2022/{caribeng-ui-caribe-input-token.mjs → caribeng-ui-input-token.mjs} +59 -59
- package/fesm2022/caribeng-ui-inputmask.mjs +8 -2
- package/fesm2022/caribeng-ui-inputotp.mjs +2 -2
- package/fesm2022/caribeng-ui-journal-entry.mjs +340 -0
- package/fesm2022/caribeng-ui-kanban.mjs +4612 -0
- package/fesm2022/caribeng-ui-kbd.mjs +122 -0
- package/fesm2022/caribeng-ui-kpi.mjs +189 -0
- package/fesm2022/{caribeng-ui-caribe-link.mjs → caribeng-ui-link.mjs} +37 -37
- package/fesm2022/caribeng-ui-masonry.mjs +93 -0
- package/fesm2022/caribeng-ui-media-player.mjs +821 -0
- package/fesm2022/caribeng-ui-megamenu.mjs +13 -9
- package/fesm2022/caribeng-ui-mentions.mjs +495 -0
- package/fesm2022/caribeng-ui-menu.mjs +8 -4
- package/fesm2022/caribeng-ui-menubar.mjs +9 -5
- package/fesm2022/caribeng-ui-message.mjs +6 -3
- package/fesm2022/caribeng-ui-notification-center.mjs +636 -0
- package/fesm2022/{caribeng-ui-caribe-off-canvas.mjs → caribeng-ui-off-canvas.mjs} +55 -55
- package/fesm2022/{caribeng-ui-caribe-onboarding.mjs → caribeng-ui-onboarding.mjs} +105 -105
- package/fesm2022/caribeng-ui-orderlist.mjs +2 -2
- package/fesm2022/caribeng-ui-organizationchart.mjs +6 -10
- package/fesm2022/caribeng-ui-panel.mjs +6 -6
- package/fesm2022/caribeng-ui-panelmenu.mjs +9 -6
- package/fesm2022/caribeng-ui-pdfviewer.mjs +1755 -25
- package/fesm2022/caribeng-ui-period-picker.mjs +637 -0
- package/fesm2022/caribeng-ui-phone-input.mjs +688 -0
- package/fesm2022/caribeng-ui-picklist.mjs +2 -2
- package/fesm2022/caribeng-ui-pivot.mjs +419 -0
- package/fesm2022/caribeng-ui-pricing.mjs +263 -69
- package/fesm2022/caribeng-ui-qrcode.mjs +564 -0
- package/fesm2022/caribeng-ui-rating.mjs +14 -17
- package/fesm2022/caribeng-ui-reconciliation.mjs +386 -0
- package/fesm2022/caribeng-ui-ripple.mjs +2 -0
- package/fesm2022/caribeng-ui-scheduler.mjs +1027 -0
- package/fesm2022/caribeng-ui-scrolltop.mjs +3 -1
- package/fesm2022/caribeng-ui-select.mjs +2 -2
- package/fesm2022/{caribeng-ui-caribe-shortcut.mjs → caribeng-ui-shortcut.mjs} +29 -29
- package/fesm2022/caribeng-ui-sidebar.mjs +573 -0
- package/fesm2022/caribeng-ui-signature-pad.mjs +654 -0
- package/fesm2022/{caribeng-ui-caribe-status.mjs → caribeng-ui-status.mjs} +11 -11
- package/fesm2022/{caribeng-ui-caribe-step-indicator.mjs → caribeng-ui-step-indicator.mjs} +53 -53
- package/fesm2022/caribeng-ui-stepper.mjs +12 -5
- package/fesm2022/caribeng-ui-steps.mjs +2 -2
- package/fesm2022/caribeng-ui-table.mjs +333 -19
- package/fesm2022/caribeng-ui-tabmenu.mjs +10 -6
- package/fesm2022/caribeng-ui-tabs.mjs +2 -2
- package/fesm2022/{caribeng-ui-caribe-tag-button.mjs → caribeng-ui-tag-button.mjs} +25 -25
- package/fesm2022/caribeng-ui-tag.mjs +2 -2
- package/fesm2022/caribeng-ui-tieredmenu.mjs +111 -21
- package/fesm2022/{caribeng-ui-caribe-tracker.mjs → caribeng-ui-tracker.mjs} +29 -29
- package/fesm2022/{caribeng-ui-caribe-transaction-status.mjs → caribeng-ui-transaction-status.mjs} +11 -11
- package/fesm2022/caribeng-ui-treeselect.mjs +44 -9
- package/fesm2022/caribeng-ui-treetable.mjs +236 -17
- package/fesm2022/{caribeng-ui-caribe-voucher.mjs → caribeng-ui-voucher.mjs} +45 -45
- package/fesm2022/caribeng-ui-xlsx.mjs +486 -0
- package/fesm2022/caribeng-ui.mjs +1568 -1
- package/package.json +258 -178
- package/schematics/collection.json +5 -0
- package/schematics/migrate-from-primeng/core-icons.json +1 -0
- package/schematics/migrate-from-primeng/icons.js +269 -0
- package/schematics/migrate-from-primeng/index.js +290 -0
- package/schematics/migrate-from-primeng/mappings/button.js +93 -0
- package/schematics/migrate-from-primeng/mappings/card.js +33 -0
- package/schematics/migrate-from-primeng/mappings/checkbox.js +44 -0
- package/schematics/migrate-from-primeng/mappings/confirm.js +221 -0
- package/schematics/migrate-from-primeng/mappings/datepicker.js +121 -0
- package/schematics/migrate-from-primeng/mappings/dialog.js +84 -0
- package/schematics/migrate-from-primeng/mappings/index.js +88 -0
- package/schematics/migrate-from-primeng/mappings/inputnumber.js +62 -0
- package/schematics/migrate-from-primeng/mappings/inputtext.js +60 -0
- package/schematics/migrate-from-primeng/mappings/menu.js +468 -0
- package/schematics/migrate-from-primeng/mappings/multiselect.js +49 -0
- package/schematics/migrate-from-primeng/mappings/radiobutton.js +22 -0
- package/schematics/migrate-from-primeng/mappings/rules.js +206 -0
- package/schematics/migrate-from-primeng/mappings/select.js +97 -0
- package/schematics/migrate-from-primeng/mappings/service-calls.js +100 -0
- package/schematics/migrate-from-primeng/mappings/table.js +613 -0
- package/schematics/migrate-from-primeng/mappings/tabs.js +225 -0
- package/schematics/migrate-from-primeng/mappings/tag.js +29 -0
- package/schematics/migrate-from-primeng/mappings/textarea.js +44 -0
- package/schematics/migrate-from-primeng/mappings/toast.js +173 -0
- package/schematics/migrate-from-primeng/mappings/toggleswitch.js +37 -0
- package/schematics/migrate-from-primeng/mappings/tooltip.js +44 -0
- package/schematics/migrate-from-primeng/mappings/types.js +10 -0
- package/schematics/migrate-from-primeng/report.js +79 -0
- package/schematics/migrate-from-primeng/schema.js +2 -0
- package/schematics/migrate-from-primeng/schema.json +34 -0
- package/schematics/migrate-from-primeng/template.js +387 -0
- package/schematics/migrate-from-primeng/todo.js +5 -0
- package/schematics/migrate-from-primeng/typescript.js +442 -0
- package/schematics/migrate-from-primeng/workspace.js +117 -0
- package/schematics/ng-add/index.js +16 -1
- package/types/caribeng-ui-account-picker.d.ts +142 -0
- package/types/caribeng-ui-aging.d.ts +123 -0
- package/types/{caribeng-ui-caribe-alert.d.ts → caribeng-ui-alert.d.ts} +2 -2
- package/types/caribeng-ui-amount.d.ts +82 -0
- package/types/{caribeng-ui-caribe-animation-lottie.d.ts → caribeng-ui-animation-lottie.d.ts} +2 -2
- package/types/caribeng-ui-audit-log.d.ts +86 -0
- package/types/caribeng-ui-badge.d.ts +3 -1
- package/types/caribeng-ui-breadcrumb.d.ts +9 -1
- package/types/{caribeng-ui-caribe-button-group.d.ts → caribeng-ui-button-group.d.ts} +2 -2
- package/types/{caribeng-ui-caribe-card-content.d.ts → caribeng-ui-card-content.d.ts} +2 -2
- package/types/{caribeng-ui-caribe-card-move.d.ts → caribeng-ui-card-move.d.ts} +1 -1
- package/types/{caribeng-ui-caribe-card-primary.d.ts → caribeng-ui-card-primary.d.ts} +2 -2
- package/types/{caribeng-ui-caribe-card-select.d.ts → caribeng-ui-card-select.d.ts} +2 -2
- package/types/caribeng-ui-cascadeselect.d.ts +6 -0
- package/types/caribeng-ui-chart.d.ts +3 -1
- package/types/caribeng-ui-chat.d.ts +299 -0
- package/types/{caribeng-ui-caribe-coachmark.d.ts → caribeng-ui-coachmark.d.ts} +2 -2
- package/types/caribeng-ui-code-block.d.ts +65 -0
- package/types/caribeng-ui-colorpicker.d.ts +5 -1
- package/types/caribeng-ui-command-palette.d.ts +133 -0
- package/types/caribeng-ui-comments.d.ts +214 -0
- package/types/caribeng-ui-confirmdialog.d.ts +34 -2
- package/types/caribeng-ui-confirmpopup.d.ts +39 -2
- package/types/caribeng-ui-contextmenu.d.ts +7 -0
- package/types/caribeng-ui-copy-button.d.ts +90 -0
- package/types/caribeng-ui-datagrid.d.ts +269 -0
- package/types/caribeng-ui-dataview.d.ts +2 -0
- package/types/caribeng-ui-dock.d.ts +13 -1
- package/types/caribeng-ui-document.d.ts +138 -0
- package/types/{caribeng-ui-caribe-dots-indicator.d.ts → caribeng-ui-dots-indicator.d.ts} +2 -2
- package/types/caribeng-ui-drawer.d.ts +3 -1
- package/types/caribeng-ui-dynamic-form.d.ts +409 -0
- package/types/caribeng-ui-editor.d.ts +110 -12
- package/types/{caribeng-ui-caribe-expansion-panel.d.ts → caribeng-ui-expansion-panel.d.ts} +1 -1
- package/types/{caribeng-ui-caribe-fab-menu.d.ts → caribeng-ui-fab-menu.d.ts} +2 -2
- package/types/caribeng-ui-file-card.d.ts +27 -2
- package/types/{caribeng-ui-caribe-file-download.d.ts → caribeng-ui-file-download.d.ts} +1 -1
- package/types/caribeng-ui-financial-report.d.ts +102 -0
- package/types/{caribeng-ui-caribe-float-menu.d.ts → caribeng-ui-float-menu.d.ts} +2 -2
- package/types/caribeng-ui-flow.d.ts +228 -0
- package/types/caribeng-ui-folder-card.d.ts +27 -5
- package/types/{caribeng-ui-caribe-footer.d.ts → caribeng-ui-footer.d.ts} +2 -2
- package/types/caribeng-ui-gantt.d.ts +244 -0
- package/types/{caribeng-ui-caribe-header.d.ts → caribeng-ui-header.d.ts} +2 -2
- package/types/{caribeng-ui-caribe-icon-button.d.ts → caribeng-ui-icon-button.d.ts} +2 -2
- package/types/{caribeng-ui-caribe-illustration.d.ts → caribeng-ui-illustration.d.ts} +1 -1
- package/types/caribeng-ui-image-cropper.d.ts +169 -0
- package/types/caribeng-ui-import-wizard.d.ts +149 -0
- package/types/caribeng-ui-infinite-scroll.d.ts +75 -0
- package/types/caribeng-ui-input-number.d.ts +1 -1
- package/types/{caribeng-ui-caribe-input-search.d.ts → caribeng-ui-input-search.d.ts} +2 -2
- package/types/{caribeng-ui-caribe-input-token.d.ts → caribeng-ui-input-token.d.ts} +2 -2
- package/types/caribeng-ui-inputmask.d.ts +3 -1
- package/types/caribeng-ui-journal-entry.d.ts +110 -0
- package/types/caribeng-ui-kanban.d.ts +783 -0
- package/types/caribeng-ui-kbd.d.ts +36 -0
- package/types/caribeng-ui-kpi.d.ts +64 -0
- package/types/{caribeng-ui-caribe-link.d.ts → caribeng-ui-link.d.ts} +2 -2
- package/types/{caribeng-ui-caribe-logo.d.ts → caribeng-ui-logo.d.ts} +1 -1
- package/types/caribeng-ui-masonry.d.ts +32 -0
- package/types/caribeng-ui-media-player.d.ts +140 -0
- package/types/caribeng-ui-megamenu.d.ts +11 -0
- package/types/caribeng-ui-mentions.d.ts +127 -0
- package/types/caribeng-ui-menu.d.ts +7 -0
- package/types/caribeng-ui-menubar.d.ts +7 -0
- package/types/caribeng-ui-message.d.ts +2 -0
- package/types/caribeng-ui-notification-center.d.ts +153 -0
- package/types/{caribeng-ui-caribe-off-canvas.d.ts → caribeng-ui-off-canvas.d.ts} +2 -2
- package/types/{caribeng-ui-caribe-onboarding.d.ts → caribeng-ui-onboarding.d.ts} +2 -2
- package/types/caribeng-ui-panelmenu.d.ts +7 -0
- package/types/caribeng-ui-pdfviewer.d.ts +245 -5
- package/types/caribeng-ui-period-picker.d.ts +148 -0
- package/types/caribeng-ui-phone-input.d.ts +168 -0
- package/types/caribeng-ui-pivot.d.ts +116 -0
- package/types/caribeng-ui-pricing.d.ts +41 -4
- package/types/caribeng-ui-qrcode.d.ts +88 -0
- package/types/caribeng-ui-reconciliation.d.ts +109 -0
- package/types/caribeng-ui-scheduler.d.ts +188 -0
- package/types/{caribeng-ui-caribe-shortcut.d.ts → caribeng-ui-shortcut.d.ts} +2 -2
- package/types/caribeng-ui-sidebar.d.ts +167 -0
- package/types/caribeng-ui-signature-pad.d.ts +151 -0
- package/types/{caribeng-ui-caribe-status.d.ts → caribeng-ui-status.d.ts} +2 -2
- package/types/{caribeng-ui-caribe-step-indicator.d.ts → caribeng-ui-step-indicator.d.ts} +2 -2
- package/types/caribeng-ui-stepper.d.ts +2 -0
- package/types/caribeng-ui-table.d.ts +67 -3
- package/types/caribeng-ui-tabmenu.d.ts +7 -0
- package/types/{caribeng-ui-caribe-tag-button.d.ts → caribeng-ui-tag-button.d.ts} +2 -2
- package/types/caribeng-ui-tieredmenu.d.ts +17 -1
- package/types/{caribeng-ui-caribe-tracker.d.ts → caribeng-ui-tracker.d.ts} +2 -2
- package/types/{caribeng-ui-caribe-transaction-status.d.ts → caribeng-ui-transaction-status.d.ts} +2 -2
- package/types/caribeng-ui-treeselect.d.ts +6 -0
- package/types/caribeng-ui-treetable.d.ts +67 -2
- package/types/{caribeng-ui-caribe-voucher.d.ts → caribeng-ui-voucher.d.ts} +2 -2
- package/types/caribeng-ui-xlsx.d.ts +79 -0
- package/types/caribeng-ui.d.ts +985 -2
- package/fesm2022/caribeng-ui-caribe-calendar.mjs +0 -92
- package/fesm2022/caribeng-ui-caribe-circle-loading.mjs +0 -130
- package/fesm2022/caribeng-ui-caribe-data-table.mjs +0 -94
- package/fesm2022/caribeng-ui-caribe-file-select.mjs +0 -88
- package/fesm2022/caribeng-ui-caribe-inline-alert.mjs +0 -100
- package/fesm2022/caribeng-ui-caribe-input-date.mjs +0 -138
- package/fesm2022/caribeng-ui-caribe-input-select.mjs +0 -154
- package/fesm2022/caribeng-ui-caribe-loader.mjs +0 -48
- package/fesm2022/caribeng-ui-caribe-modal.mjs +0 -119
- package/fesm2022/caribeng-ui-caribe-pictogram.mjs +0 -52
- package/fesm2022/caribeng-ui-caribe-pop-over.mjs +0 -104
- package/fesm2022/caribeng-ui-caribe-preloader.mjs +0 -57
- package/fesm2022/caribeng-ui-caribe-progress-bar.mjs +0 -84
- package/fesm2022/caribeng-ui-caribe-stroke.mjs +0 -69
- package/fesm2022/caribeng-ui-caribe-switch.mjs +0 -182
- package/styles/caribeng.css +0 -380
- package/styles/scss/_mixins.scss +0 -46
- package/styles/scss/_overlay.scss +0 -15
- package/styles/scss/_tokens.scss +0 -267
- package/styles/scss/_utilities.scss +0 -73
- package/styles/scss/_variables.scss +0 -33
- package/styles/scss/index.scss +0 -16
- package/types/caribeng-ui-caribe-calendar.d.ts +0 -27
- package/types/caribeng-ui-caribe-circle-loading.d.ts +0 -28
- package/types/caribeng-ui-caribe-data-table.d.ts +0 -26
- package/types/caribeng-ui-caribe-file-select.d.ts +0 -31
- package/types/caribeng-ui-caribe-inline-alert.d.ts +0 -28
- package/types/caribeng-ui-caribe-input-date.d.ts +0 -40
- package/types/caribeng-ui-caribe-input-select.d.ts +0 -47
- package/types/caribeng-ui-caribe-loader.d.ts +0 -22
- package/types/caribeng-ui-caribe-modal.d.ts +0 -21
- package/types/caribeng-ui-caribe-pictogram.d.ts +0 -15
- package/types/caribeng-ui-caribe-pop-over.d.ts +0 -17
- package/types/caribeng-ui-caribe-preloader.d.ts +0 -24
- package/types/caribeng-ui-caribe-progress-bar.d.ts +0 -28
- package/types/caribeng-ui-caribe-stroke.d.ts +0 -17
- package/types/caribeng-ui-caribe-switch.d.ts +0 -55
- /package/fesm2022/{caribeng-ui-caribe-illustration.mjs → caribeng-ui-illustration.mjs} +0 -0
- /package/fesm2022/{caribeng-ui-caribe-logo.mjs → caribeng-ui-logo.mjs} +0 -0
|
@@ -0,0 +1,1094 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { afterNextRender, inject, Injector, ElementRef, model, input, booleanAttribute, output, signal, viewChild, computed, ViewEncapsulation, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
|
+
import { cbFormat, injectCbTranslation } from '@caribeng/ui';
|
|
4
|
+
import { CbIcon } from '@caribeng/ui/icon';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Modelo del diagrama de Gantt: fechas, árbol de tareas, grupos calculados,
|
|
8
|
+
* dependencias (fin → inicio) y planificación automática. Funciones puras: el
|
|
9
|
+
* componente las usa y los tests las prueban sin DOM.
|
|
10
|
+
*/
|
|
11
|
+
// --- Fechas -------------------------------------------------------------------------------
|
|
12
|
+
function toDay(value) {
|
|
13
|
+
if (value instanceof Date)
|
|
14
|
+
return isNaN(value.getTime()) ? null : new Date(value.getFullYear(), value.getMonth(), value.getDate());
|
|
15
|
+
if (typeof value !== 'string' || !value)
|
|
16
|
+
return null;
|
|
17
|
+
const m = /^(\d{4})-(\d{2})-(\d{2})/.exec(value);
|
|
18
|
+
const date = m ? new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3])) : new Date(value);
|
|
19
|
+
return isNaN(date.getTime()) ? null : new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
|
20
|
+
}
|
|
21
|
+
function isoDay(date) {
|
|
22
|
+
const pad = (n) => String(n).padStart(2, '0');
|
|
23
|
+
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
|
|
24
|
+
}
|
|
25
|
+
function addDays(date, days) {
|
|
26
|
+
return new Date(date.getFullYear(), date.getMonth(), date.getDate() + days);
|
|
27
|
+
}
|
|
28
|
+
/** Días de calendario entre `a` y `b` (`b` − `a`). */
|
|
29
|
+
function dayDiff(a, b) {
|
|
30
|
+
return Math.round((Date.UTC(b.getFullYear(), b.getMonth(), b.getDate()) - Date.UTC(a.getFullYear(), a.getMonth(), a.getDate())) / 86_400_000);
|
|
31
|
+
}
|
|
32
|
+
/** Lunes de la semana de `date` (semana ISO). */
|
|
33
|
+
function startOfWeek(date) {
|
|
34
|
+
return addDays(date, -((date.getDay() + 6) % 7));
|
|
35
|
+
}
|
|
36
|
+
/** Número de semana ISO. */
|
|
37
|
+
function isoWeek(date) {
|
|
38
|
+
const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
|
|
39
|
+
const day = d.getUTCDay() || 7;
|
|
40
|
+
d.setUTCDate(d.getUTCDate() + 4 - day);
|
|
41
|
+
const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
|
|
42
|
+
return Math.ceil(((d.getTime() - yearStart.getTime()) / 86_400_000 + 1) / 7);
|
|
43
|
+
}
|
|
44
|
+
/** Resuelve fechas y avance (los grupos abarcan a sus tareas y promedian su avance por duración). */
|
|
45
|
+
function resolveTasks(tasks) {
|
|
46
|
+
const byId = new Map(tasks.map((t) => [t.id, t]));
|
|
47
|
+
const children = new Map();
|
|
48
|
+
for (const t of tasks) {
|
|
49
|
+
if (t.parent && byId.has(t.parent) && t.parent !== t.id) {
|
|
50
|
+
const list = children.get(t.parent) ?? [];
|
|
51
|
+
list.push(t.id);
|
|
52
|
+
children.set(t.parent, list);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const out = new Map();
|
|
56
|
+
const visiting = new Set();
|
|
57
|
+
const level = (t) => {
|
|
58
|
+
let n = 0;
|
|
59
|
+
const seen = new Set();
|
|
60
|
+
for (let p = t.parent; p && byId.has(p) && !seen.has(p); p = byId.get(p).parent) {
|
|
61
|
+
seen.add(p);
|
|
62
|
+
n++;
|
|
63
|
+
}
|
|
64
|
+
return n;
|
|
65
|
+
};
|
|
66
|
+
const resolve = (id) => {
|
|
67
|
+
const done = out.get(id);
|
|
68
|
+
if (done)
|
|
69
|
+
return done;
|
|
70
|
+
const task = byId.get(id);
|
|
71
|
+
if (!task || visiting.has(id))
|
|
72
|
+
return null;
|
|
73
|
+
visiting.add(id);
|
|
74
|
+
const kids = children.get(id) ?? [];
|
|
75
|
+
const isGroup = task.type === 'group' || kids.length > 0;
|
|
76
|
+
let start = toDay(task.start);
|
|
77
|
+
let end = toDay(task.end) ?? start;
|
|
78
|
+
let progress = clampProgress(task.progress ?? 0);
|
|
79
|
+
if (isGroup && kids.length) {
|
|
80
|
+
const resolved = kids.map(resolve).filter((r) => !!r);
|
|
81
|
+
if (resolved.length) {
|
|
82
|
+
start = new Date(Math.min(...resolved.map((r) => r.start.getTime())));
|
|
83
|
+
end = new Date(Math.max(...resolved.map((r) => r.end.getTime())));
|
|
84
|
+
const weights = resolved.map((r) => Math.max(1, dayDiff(r.start, r.end) + 1));
|
|
85
|
+
const total = weights.reduce((a, b) => a + b, 0);
|
|
86
|
+
progress = Math.round(resolved.reduce((sum, r, i) => sum + r.progress * weights[i], 0) / total);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
visiting.delete(id);
|
|
90
|
+
if (!start)
|
|
91
|
+
return null;
|
|
92
|
+
if (!end || end < start)
|
|
93
|
+
end = start;
|
|
94
|
+
const isMilestone = task.type === 'milestone' && !isGroup;
|
|
95
|
+
if (isMilestone)
|
|
96
|
+
end = start;
|
|
97
|
+
const result = { task, start, end, progress, level: level(task), isGroup, isMilestone, children: kids };
|
|
98
|
+
out.set(id, result);
|
|
99
|
+
return result;
|
|
100
|
+
};
|
|
101
|
+
for (const t of tasks)
|
|
102
|
+
resolve(t.id);
|
|
103
|
+
return out;
|
|
104
|
+
}
|
|
105
|
+
function clampProgress(value) {
|
|
106
|
+
return Math.max(0, Math.min(100, Math.round(Number(value) || 0)));
|
|
107
|
+
}
|
|
108
|
+
/** Filas visibles: el árbol en orden, sin los hijos de los grupos plegados. */
|
|
109
|
+
function visibleRows(tasks, resolved, collapsed) {
|
|
110
|
+
const rows = [];
|
|
111
|
+
const ids = new Set(tasks.map((t) => t.id));
|
|
112
|
+
const walk = (parent) => {
|
|
113
|
+
for (const t of tasks) {
|
|
114
|
+
const p = t.parent && ids.has(t.parent) && t.parent !== t.id ? t.parent : null;
|
|
115
|
+
if (p !== parent)
|
|
116
|
+
continue;
|
|
117
|
+
const r = resolved.get(t.id);
|
|
118
|
+
if (!r)
|
|
119
|
+
continue;
|
|
120
|
+
rows.push(r);
|
|
121
|
+
if (r.children.length && !collapsed.has(t.id))
|
|
122
|
+
walk(t.id);
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
walk(null);
|
|
126
|
+
return rows;
|
|
127
|
+
}
|
|
128
|
+
/** Ids de todas las tareas dentro de un grupo (a cualquier nivel). */
|
|
129
|
+
function descendants(tasks, id) {
|
|
130
|
+
const out = [];
|
|
131
|
+
const queue = [id];
|
|
132
|
+
while (queue.length) {
|
|
133
|
+
const current = queue.shift();
|
|
134
|
+
for (const t of tasks) {
|
|
135
|
+
if (t.parent === current && !out.includes(t.id)) {
|
|
136
|
+
out.push(t.id);
|
|
137
|
+
queue.push(t.id);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return out;
|
|
142
|
+
}
|
|
143
|
+
// --- Dependencias ---------------------------------------------------------------------------
|
|
144
|
+
/** ¿`to` depende (directa o indirectamente) de `from`? Evita crear ciclos. */
|
|
145
|
+
function dependsOn(tasks, to, from) {
|
|
146
|
+
const byId = new Map(tasks.map((t) => [t.id, t]));
|
|
147
|
+
const seen = new Set();
|
|
148
|
+
const stack = [to];
|
|
149
|
+
while (stack.length) {
|
|
150
|
+
const id = stack.pop();
|
|
151
|
+
if (id === from)
|
|
152
|
+
return true;
|
|
153
|
+
if (seen.has(id))
|
|
154
|
+
continue;
|
|
155
|
+
seen.add(id);
|
|
156
|
+
stack.push(...(byId.get(id)?.dependencies ?? []));
|
|
157
|
+
}
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
/** ¿Se puede crear la dependencia `from` → `to`? (sin repetir, sin ciclos ni grupos que se contienen). */
|
|
161
|
+
function canLink(tasks, from, to) {
|
|
162
|
+
if (from === to)
|
|
163
|
+
return false;
|
|
164
|
+
const target = tasks.find((t) => t.id === to);
|
|
165
|
+
if (!target || !tasks.some((t) => t.id === from))
|
|
166
|
+
return false;
|
|
167
|
+
if (target.dependencies?.includes(from))
|
|
168
|
+
return false;
|
|
169
|
+
if (descendants(tasks, from).includes(to) || descendants(tasks, to).includes(from))
|
|
170
|
+
return false;
|
|
171
|
+
return !dependsOn(tasks, from, to);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Planificación automática: cada tarea empieza como pronto el día siguiente al
|
|
175
|
+
* fin de sus predecesoras; si no, se desplaza conservando su duración. Devuelve
|
|
176
|
+
* las tareas cambiadas (las de grupos se resuelven con sus hijas).
|
|
177
|
+
*/
|
|
178
|
+
function autoSchedule(tasks) {
|
|
179
|
+
let current = [...tasks];
|
|
180
|
+
for (let pass = 0; pass < tasks.length + 1; pass++) {
|
|
181
|
+
const resolved = resolveTasks(current);
|
|
182
|
+
let changed = false;
|
|
183
|
+
current = current.map((t) => {
|
|
184
|
+
if (!t.dependencies?.length)
|
|
185
|
+
return t;
|
|
186
|
+
const self = resolved.get(t.id);
|
|
187
|
+
if (!self || self.isGroup)
|
|
188
|
+
return t;
|
|
189
|
+
const ends = t.dependencies.map((d) => resolved.get(d)?.end).filter((d) => !!d);
|
|
190
|
+
if (!ends.length)
|
|
191
|
+
return t;
|
|
192
|
+
const earliest = addDays(new Date(Math.max(...ends.map((d) => d.getTime()))), 1);
|
|
193
|
+
if (self.start >= earliest)
|
|
194
|
+
return t;
|
|
195
|
+
const shift = dayDiff(self.start, earliest);
|
|
196
|
+
changed = true;
|
|
197
|
+
return { ...t, start: isoDay(addDays(self.start, shift)), end: isoDay(addDays(self.end, shift)) };
|
|
198
|
+
});
|
|
199
|
+
if (!changed)
|
|
200
|
+
break;
|
|
201
|
+
}
|
|
202
|
+
return current;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** Alto de cada fila (px), igual que en el CSS. */
|
|
206
|
+
const ROW = 40;
|
|
207
|
+
/** Píxeles por día según la escala. */
|
|
208
|
+
const DAY_WIDTH = { day: 36, week: 14, month: 4 };
|
|
209
|
+
let nextUid = 0;
|
|
210
|
+
/**
|
|
211
|
+
* Diagrama de Gantt de CaribeNG: tabla de tareas en árbol (grupos plegables) y
|
|
212
|
+
* barras en el tiempo, con escalas de día, semana y mes.
|
|
213
|
+
*
|
|
214
|
+
* - Arrastra una barra para moverla, sus bordes para cambiar inicio o fin, el
|
|
215
|
+
* tirador inferior para el avance y el punto de la derecha hasta otra tarea
|
|
216
|
+
* para crear una dependencia (fin → inicio).
|
|
217
|
+
* - Con `autoSchedule`, al retrasar una tarea se desplazan las que dependen de ella.
|
|
218
|
+
* - Los grupos abarcan a sus tareas y promedian su avance; los hitos son rombos.
|
|
219
|
+
* - Con el teclado, cada barra es un botón: flechas mueven, Mayús + flechas cambian
|
|
220
|
+
* el fin, Alt + flechas el inicio, + / − el avance, Supr quita sus dependencias
|
|
221
|
+
* y Enter abre la tarea. Cada cambio se anuncia.
|
|
222
|
+
*
|
|
223
|
+
* @example
|
|
224
|
+
* ```html
|
|
225
|
+
* <cb-gantt [(tasks)]="tareas" [(scale)]="escala" (taskChange)="guardar($event)" />
|
|
226
|
+
* ```
|
|
227
|
+
*/
|
|
228
|
+
class CbGantt {
|
|
229
|
+
x(date) {
|
|
230
|
+
return dayDiff(this.range().start, date) * this.dayWidth();
|
|
231
|
+
}
|
|
232
|
+
// --- Textos ---------------------------------------------------------------------------
|
|
233
|
+
scaleLabel(s) {
|
|
234
|
+
const t = this.t();
|
|
235
|
+
return s === 'day' ? t.ganttDay : s === 'week' ? t.ganttWeek : t.ganttMonth;
|
|
236
|
+
}
|
|
237
|
+
columnLabel(col) {
|
|
238
|
+
const t = this.t();
|
|
239
|
+
return { name: t.ganttTask, start: t.ganttStart, end: t.ganttEnd, duration: t.ganttDuration, progress: t.ganttProgress, assignee: t.ganttAssignee }[col];
|
|
240
|
+
}
|
|
241
|
+
dateText(d) {
|
|
242
|
+
return new Intl.DateTimeFormat(this.t().intlLocale, { day: 'numeric', month: 'short' }).format(d);
|
|
243
|
+
}
|
|
244
|
+
cellText(r, col) {
|
|
245
|
+
switch (col) {
|
|
246
|
+
case 'start':
|
|
247
|
+
return this.dateText(r.start);
|
|
248
|
+
case 'end':
|
|
249
|
+
return this.dateText(r.end);
|
|
250
|
+
case 'duration':
|
|
251
|
+
return r.isMilestone ? '—' : cbFormat(this.t().ganttDays, dayDiff(r.start, r.end) + 1);
|
|
252
|
+
case 'progress':
|
|
253
|
+
return r.isMilestone ? '—' : `${r.progress} %`;
|
|
254
|
+
case 'assignee':
|
|
255
|
+
return r.task.assignee ?? '';
|
|
256
|
+
default:
|
|
257
|
+
return r.task.name;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
barLabel(b) {
|
|
261
|
+
const t = this.t();
|
|
262
|
+
const deps = (b.row.task.dependencies ?? []).map((id) => this.resolved().get(id)?.task.name).filter(Boolean);
|
|
263
|
+
const after = deps.length ? `. ${cbFormat(t.ganttDependsOn, deps.join(', '))}` : '';
|
|
264
|
+
if (b.row.isMilestone)
|
|
265
|
+
return cbFormat(t.ganttMilestoneLabel, b.row.task.name, this.dateText(b.start)) + after;
|
|
266
|
+
const range = `${this.dateText(b.start)} – ${this.dateText(b.end)}`;
|
|
267
|
+
return cbFormat(b.row.isGroup ? t.ganttGroupLabel : t.ganttBarLabel, b.row.task.name, range, b.progress) + after;
|
|
268
|
+
}
|
|
269
|
+
// --- Árbol ---------------------------------------------------------------------------------
|
|
270
|
+
toggle(id) {
|
|
271
|
+
this.collapsed.update((set) => {
|
|
272
|
+
const next = new Set(set);
|
|
273
|
+
if (next.has(id))
|
|
274
|
+
next.delete(id);
|
|
275
|
+
else
|
|
276
|
+
next.add(id);
|
|
277
|
+
return next;
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
/** Selecciona la tarea, lleva su barra a la vista y le da el foco. */
|
|
281
|
+
focusBar(id) {
|
|
282
|
+
this.selected.set(id);
|
|
283
|
+
afterNextRender(() => {
|
|
284
|
+
const bar = this.barEl(id);
|
|
285
|
+
const scroller = this.scroller()?.nativeElement;
|
|
286
|
+
if (!bar || !scroller)
|
|
287
|
+
return;
|
|
288
|
+
const grid = scroller.querySelector('.cb-gantt__grid')?.offsetWidth ?? 0;
|
|
289
|
+
const left = bar.offsetLeft + grid;
|
|
290
|
+
if (left < scroller.scrollLeft + grid || left + bar.offsetWidth > scroller.scrollLeft + scroller.clientWidth) {
|
|
291
|
+
scroller.scrollLeft = Math.max(0, left - grid - 48);
|
|
292
|
+
}
|
|
293
|
+
bar.focus({ preventScroll: true });
|
|
294
|
+
}, { injector: this.injector });
|
|
295
|
+
}
|
|
296
|
+
/** Desplaza el diagrama hasta hoy (centrado en la parte visible). */
|
|
297
|
+
scrollToToday(smooth = true) {
|
|
298
|
+
const x = this.todayX();
|
|
299
|
+
const scroller = this.scroller()?.nativeElement;
|
|
300
|
+
if (x === null || !scroller)
|
|
301
|
+
return;
|
|
302
|
+
const grid = scroller.querySelector('.cb-gantt__grid')?.offsetWidth ?? 0;
|
|
303
|
+
scroller.scrollTo?.({ left: Math.max(0, x - (scroller.clientWidth - grid) / 3), behavior: smooth ? 'smooth' : 'auto' });
|
|
304
|
+
}
|
|
305
|
+
constructor() {
|
|
306
|
+
/** Textos de interfaz en el idioma activo (`provideCaribeNG({ locale })`). */
|
|
307
|
+
this.t = injectCbTranslation();
|
|
308
|
+
this.fmt = cbFormat;
|
|
309
|
+
this.injector = inject(Injector);
|
|
310
|
+
this.host = inject(ElementRef);
|
|
311
|
+
this.uid = `cb-gantt-${++nextUid}`;
|
|
312
|
+
this.row = ROW;
|
|
313
|
+
this.scales = ['day', 'week', 'month'];
|
|
314
|
+
/** Tareas (bidireccional: se actualiza al arrastrar, con el teclado y al planificar). */
|
|
315
|
+
this.tasks = model([], /* @ts-ignore */
|
|
316
|
+
...(ngDevMode ? [{ debugName: "tasks" }] : /* istanbul ignore next */ []));
|
|
317
|
+
/** Escala: un día, una semana o un mes por columna. */
|
|
318
|
+
this.scale = model('day', /* @ts-ignore */
|
|
319
|
+
...(ngDevMode ? [{ debugName: "scale" }] : /* istanbul ignore next */ []));
|
|
320
|
+
/** Columnas de la tabla de la izquierda y su orden. */
|
|
321
|
+
this.columns = input(['name', 'start', 'end', 'duration', 'progress'], /* @ts-ignore */
|
|
322
|
+
...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
|
|
323
|
+
/** Ancho de la tabla de la izquierda; por defecto, el de sus columnas (como mucho, el 60 % del componente). */
|
|
324
|
+
this.gridWidth = input('', /* @ts-ignore */
|
|
325
|
+
...(ngDevMode ? [{ debugName: "gridWidth" }] : /* istanbul ignore next */ []));
|
|
326
|
+
/** Alto del componente (con scroll). */
|
|
327
|
+
this.height = input('28rem', /* @ts-ignore */
|
|
328
|
+
...(ngDevMode ? [{ debugName: "height" }] : /* istanbul ignore next */ []));
|
|
329
|
+
/** Al retrasar una tarea, desplaza las que dependen de ella (fin → inicio). */
|
|
330
|
+
this.autoSchedule = input(true, { ...(ngDevMode ? { debugName: "autoSchedule" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
331
|
+
/** Sin edición: no se arrastra ni se crean dependencias. */
|
|
332
|
+
this.readonly = input(false, { ...(ngDevMode ? { debugName: "readonly" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
333
|
+
/** Barra con la escala y el botón Hoy. */
|
|
334
|
+
this.showToolbar = input(true, { ...(ngDevMode ? { debugName: "showToolbar" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
335
|
+
/** Texto sin tareas. Por defecto, "Sin datos". */
|
|
336
|
+
this.emptyMessage = input(undefined, /* @ts-ignore */
|
|
337
|
+
...(ngDevMode ? [{ debugName: "emptyMessage" }] : /* istanbul ignore next */ []));
|
|
338
|
+
this.ariaLabel = input('', /* @ts-ignore */
|
|
339
|
+
...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
340
|
+
/** Una tarea cambió de fechas o de avance (también las desplazadas por la planificación). */
|
|
341
|
+
this.taskChange = output();
|
|
342
|
+
/** Doble clic o Enter sobre una tarea. */
|
|
343
|
+
this.taskOpen = output();
|
|
344
|
+
/** Dependencia creada (`to` depende de `from`). */
|
|
345
|
+
this.linkAdd = output();
|
|
346
|
+
/** Dependencia quitada. */
|
|
347
|
+
this.linkRemove = output();
|
|
348
|
+
this.collapsed = signal(new Set(), /* @ts-ignore */
|
|
349
|
+
...(ngDevMode ? [{ debugName: "collapsed" }] : /* istanbul ignore next */ []));
|
|
350
|
+
this.selected = signal(null, /* @ts-ignore */
|
|
351
|
+
...(ngDevMode ? [{ debugName: "selected" }] : /* istanbul ignore next */ []));
|
|
352
|
+
this.selectedLink = signal(null, /* @ts-ignore */
|
|
353
|
+
...(ngDevMode ? [{ debugName: "selectedLink" }] : /* istanbul ignore next */ []));
|
|
354
|
+
this.preview = signal(null, /* @ts-ignore */
|
|
355
|
+
...(ngDevMode ? [{ debugName: "preview" }] : /* istanbul ignore next */ []));
|
|
356
|
+
this.announcement = signal('', /* @ts-ignore */
|
|
357
|
+
...(ngDevMode ? [{ debugName: "announcement" }] : /* istanbul ignore next */ []));
|
|
358
|
+
this.drag = null;
|
|
359
|
+
this.scroller = viewChild('scroller', /* @ts-ignore */
|
|
360
|
+
...(ngDevMode ? [{ debugName: "scroller" }] : /* istanbul ignore next */ []));
|
|
361
|
+
this.chart = viewChild('chart', /* @ts-ignore */
|
|
362
|
+
...(ngDevMode ? [{ debugName: "chart" }] : /* istanbul ignore next */ []));
|
|
363
|
+
this.resolved = computed(() => resolveTasks(this.tasks()), /* @ts-ignore */
|
|
364
|
+
...(ngDevMode ? [{ debugName: "resolved" }] : /* istanbul ignore next */ []));
|
|
365
|
+
/** Ancho de la tabla: el indicado o el nombre (14rem) más el de cada columna. */
|
|
366
|
+
this.gridSize = computed(() => {
|
|
367
|
+
if (this.gridWidth())
|
|
368
|
+
return this.gridWidth();
|
|
369
|
+
const widths = { name: 14, start: 4.5, end: 4.5, duration: 4.5, progress: 4, assignee: 6.5 };
|
|
370
|
+
return `${this.columns().reduce((sum, c) => sum + widths[c], 0)}rem`;
|
|
371
|
+
}, /* @ts-ignore */
|
|
372
|
+
...(ngDevMode ? [{ debugName: "gridSize" }] : /* istanbul ignore next */ []));
|
|
373
|
+
this.rows = computed(() => visibleRows(this.tasks(), this.resolved(), this.collapsed()), /* @ts-ignore */
|
|
374
|
+
...(ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []));
|
|
375
|
+
this.dayWidth = computed(() => DAY_WIDTH[this.scale()], /* @ts-ignore */
|
|
376
|
+
...(ngDevMode ? [{ debugName: "dayWidth" }] : /* istanbul ignore next */ []));
|
|
377
|
+
/** Primer y último día del diagrama (con margen, alineados a la unidad de la escala). */
|
|
378
|
+
this.range = computed(() => {
|
|
379
|
+
const all = [...this.resolved().values()];
|
|
380
|
+
const today = toDay(new Date());
|
|
381
|
+
let min = all.length ? new Date(Math.min(...all.map((r) => r.start.getTime()))) : today;
|
|
382
|
+
let max = all.length ? new Date(Math.max(...all.map((r) => r.end.getTime()))) : addDays(today, 30);
|
|
383
|
+
const scale = this.scale();
|
|
384
|
+
if (scale === 'day') {
|
|
385
|
+
min = addDays(min, -3);
|
|
386
|
+
max = addDays(max, 10);
|
|
387
|
+
}
|
|
388
|
+
else if (scale === 'week') {
|
|
389
|
+
min = startOfWeek(addDays(min, -7));
|
|
390
|
+
max = addDays(startOfWeek(addDays(max, 14)), 6);
|
|
391
|
+
}
|
|
392
|
+
else {
|
|
393
|
+
min = new Date(min.getFullYear(), min.getMonth() - 1, 1);
|
|
394
|
+
max = new Date(max.getFullYear(), max.getMonth() + 2, 0);
|
|
395
|
+
}
|
|
396
|
+
return { start: min, end: max, days: dayDiff(min, max) + 1 };
|
|
397
|
+
}, /* @ts-ignore */
|
|
398
|
+
...(ngDevMode ? [{ debugName: "range" }] : /* istanbul ignore next */ []));
|
|
399
|
+
this.chartWidth = computed(() => this.range().days * this.dayWidth(), /* @ts-ignore */
|
|
400
|
+
...(ngDevMode ? [{ debugName: "chartWidth" }] : /* istanbul ignore next */ []));
|
|
401
|
+
/** Unidades de la escala (fila inferior de la cabecera y columnas del fondo). */
|
|
402
|
+
this.units = computed(() => {
|
|
403
|
+
const { start, days } = this.range();
|
|
404
|
+
const locale = this.t().intlLocale;
|
|
405
|
+
const scale = this.scale();
|
|
406
|
+
const w = this.dayWidth();
|
|
407
|
+
const units = [];
|
|
408
|
+
if (scale === 'day') {
|
|
409
|
+
const weekday = new Intl.DateTimeFormat(locale, { weekday: 'narrow' });
|
|
410
|
+
for (let i = 0; i < days; i++) {
|
|
411
|
+
const d = addDays(start, i);
|
|
412
|
+
units.push({ key: isoDay(d), label: `${weekday.format(d)} ${d.getDate()}`, x: i * w, width: w, weekend: d.getDay() === 0 || d.getDay() === 6 });
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
else if (scale === 'week') {
|
|
416
|
+
const day = new Intl.DateTimeFormat(locale, { day: 'numeric', month: 'short' });
|
|
417
|
+
for (let i = 0; i < days; i += 7) {
|
|
418
|
+
const d = addDays(start, i);
|
|
419
|
+
units.push({ key: isoDay(d), label: `${cbFormat(this.t().ganttWeekShort, isoWeek(d))} · ${day.format(d)}`, x: i * w, width: 7 * w });
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
else {
|
|
423
|
+
const month = new Intl.DateTimeFormat(locale, { month: 'short' });
|
|
424
|
+
for (let d = new Date(start); d <= this.range().end; d = new Date(d.getFullYear(), d.getMonth() + 1, 1)) {
|
|
425
|
+
const next = new Date(d.getFullYear(), d.getMonth() + 1, 1);
|
|
426
|
+
units.push({ key: isoDay(d), label: month.format(d), x: this.x(d), width: dayDiff(d, next) * w });
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
return units;
|
|
430
|
+
}, /* @ts-ignore */
|
|
431
|
+
...(ngDevMode ? [{ debugName: "units" }] : /* istanbul ignore next */ []));
|
|
432
|
+
/** Fila superior de la cabecera: meses (día y semana) o años (mes). */
|
|
433
|
+
this.topUnits = computed(() => {
|
|
434
|
+
const { start, end } = this.range();
|
|
435
|
+
const locale = this.t().intlLocale;
|
|
436
|
+
const units = [];
|
|
437
|
+
if (this.scale() === 'month') {
|
|
438
|
+
for (let y = start.getFullYear(); y <= end.getFullYear(); y++) {
|
|
439
|
+
const from = new Date(Math.max(start.getTime(), new Date(y, 0, 1).getTime()));
|
|
440
|
+
const to = new Date(Math.min(addDays(end, 1).getTime(), new Date(y + 1, 0, 1).getTime()));
|
|
441
|
+
units.push({ key: String(y), label: String(y), x: this.x(from), width: dayDiff(from, to) * this.dayWidth() });
|
|
442
|
+
}
|
|
443
|
+
return units;
|
|
444
|
+
}
|
|
445
|
+
const format = new Intl.DateTimeFormat(locale, { month: 'long', year: 'numeric' });
|
|
446
|
+
for (let d = new Date(start.getFullYear(), start.getMonth(), 1); d <= end; d = new Date(d.getFullYear(), d.getMonth() + 1, 1)) {
|
|
447
|
+
const from = d < start ? start : d;
|
|
448
|
+
const next = new Date(d.getFullYear(), d.getMonth() + 1, 1);
|
|
449
|
+
const to = next > addDays(end, 1) ? addDays(end, 1) : next;
|
|
450
|
+
const label = format.format(d);
|
|
451
|
+
// Solo la primera letra en mayúscula ("Noviembre de 2026", no "Noviembre De 2026").
|
|
452
|
+
units.push({ key: isoDay(d), label: label.charAt(0).toUpperCase() + label.slice(1), x: this.x(from), width: dayDiff(from, to) * this.dayWidth() });
|
|
453
|
+
}
|
|
454
|
+
return units;
|
|
455
|
+
}, /* @ts-ignore */
|
|
456
|
+
...(ngDevMode ? [{ debugName: "topUnits" }] : /* istanbul ignore next */ []));
|
|
457
|
+
this.todayX = computed(() => {
|
|
458
|
+
const today = toDay(new Date());
|
|
459
|
+
const { start, end } = this.range();
|
|
460
|
+
return today < start || today > end ? null : this.x(today) + this.dayWidth() / 2;
|
|
461
|
+
}, /* @ts-ignore */
|
|
462
|
+
...(ngDevMode ? [{ debugName: "todayX" }] : /* istanbul ignore next */ []));
|
|
463
|
+
/** Geometría de las barras (con la vista previa del arrastre). */
|
|
464
|
+
this.bars = computed(() => {
|
|
465
|
+
const preview = this.preview();
|
|
466
|
+
const moving = preview && preview.mode === 'move' ? new Set([preview.id, ...descendants(this.tasks(), preview.id)]) : null;
|
|
467
|
+
const w = this.dayWidth();
|
|
468
|
+
return this.rows().map((r, index) => {
|
|
469
|
+
let start = r.start;
|
|
470
|
+
let end = r.end;
|
|
471
|
+
let progress = r.progress;
|
|
472
|
+
if (preview && moving?.has(r.task.id)) {
|
|
473
|
+
start = addDays(start, preview.delta);
|
|
474
|
+
end = addDays(end, preview.delta);
|
|
475
|
+
}
|
|
476
|
+
else if (preview?.id === r.task.id) {
|
|
477
|
+
if (preview.mode === 'start')
|
|
478
|
+
start = addDays(start, Math.min(preview.delta, dayDiff(start, end)));
|
|
479
|
+
if (preview.mode === 'end')
|
|
480
|
+
end = addDays(end, Math.max(preview.delta, -dayDiff(start, end)));
|
|
481
|
+
if (preview.mode === 'progress')
|
|
482
|
+
progress = preview.progress;
|
|
483
|
+
}
|
|
484
|
+
if (moving && r.isGroup && !moving.has(r.task.id)) {
|
|
485
|
+
// Un grupo que contiene la tarea movida se recalcula en la vista previa.
|
|
486
|
+
const kids = descendants(this.tasks(), r.task.id).filter((id) => moving.has(id));
|
|
487
|
+
if (kids.length) {
|
|
488
|
+
const resolved = this.resolved();
|
|
489
|
+
const spans = descendants(this.tasks(), r.task.id)
|
|
490
|
+
.map((id) => resolved.get(id))
|
|
491
|
+
.filter((k) => !!k && !k.isGroup)
|
|
492
|
+
.map((k) => (moving.has(k.task.id) ? [addDays(k.start, preview.delta), addDays(k.end, preview.delta)] : [k.start, k.end]));
|
|
493
|
+
if (spans.length) {
|
|
494
|
+
start = new Date(Math.min(...spans.map((s) => s[0].getTime())));
|
|
495
|
+
end = new Date(Math.max(...spans.map((s) => s[1].getTime())));
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
const x = this.x(start);
|
|
500
|
+
const width = r.isMilestone ? Math.max(18, w) : (dayDiff(start, end) + 1) * w;
|
|
501
|
+
return { row: r, index, x: r.isMilestone ? x + w / 2 - 9 : x, width: r.isMilestone ? 18 : width, y: index * ROW, start, end, progress };
|
|
502
|
+
});
|
|
503
|
+
}, /* @ts-ignore */
|
|
504
|
+
...(ngDevMode ? [{ debugName: "bars" }] : /* istanbul ignore next */ []));
|
|
505
|
+
/** Flechas de dependencias entre barras visibles. */
|
|
506
|
+
this.links = computed(() => {
|
|
507
|
+
const bars = new Map(this.bars().map((b) => [b.row.task.id, b]));
|
|
508
|
+
const out = [];
|
|
509
|
+
for (const b of this.bars()) {
|
|
510
|
+
for (const dep of b.row.task.dependencies ?? []) {
|
|
511
|
+
const from = bars.get(dep);
|
|
512
|
+
if (!from)
|
|
513
|
+
continue;
|
|
514
|
+
const x1 = from.x + from.width;
|
|
515
|
+
const y1 = from.y + ROW / 2;
|
|
516
|
+
const x2 = b.x;
|
|
517
|
+
const y2 = b.y + ROW / 2;
|
|
518
|
+
let path;
|
|
519
|
+
if (x2 - x1 >= 16) {
|
|
520
|
+
const mx = x1 + 8;
|
|
521
|
+
path = `M${x1} ${y1} H${mx} V${y2} H${x2 - 1}`;
|
|
522
|
+
}
|
|
523
|
+
else {
|
|
524
|
+
const midY = y2 > y1 ? b.y : b.y + ROW;
|
|
525
|
+
path = `M${x1} ${y1} H${x1 + 8} V${midY} H${x2 - 10} V${y2} H${x2 - 1}`;
|
|
526
|
+
}
|
|
527
|
+
out.push({ key: `${dep}>${b.row.task.id}`, from: dep, to: b.row.task.id, fromName: from.row.task.name, toName: b.row.task.name, path, mid: { x: x1 + 8, y: (y1 + y2) / 2 } });
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
return out;
|
|
531
|
+
}, /* @ts-ignore */
|
|
532
|
+
...(ngDevMode ? [{ debugName: "links" }] : /* istanbul ignore next */ []));
|
|
533
|
+
/** Línea provisional al crear una dependencia. */
|
|
534
|
+
this.linkLine = computed(() => {
|
|
535
|
+
const p = this.preview();
|
|
536
|
+
if (p?.mode !== 'link' || !p.point)
|
|
537
|
+
return null;
|
|
538
|
+
const from = this.bars().find((b) => b.row.task.id === p.id);
|
|
539
|
+
if (!from)
|
|
540
|
+
return null;
|
|
541
|
+
return `M${from.x + from.width} ${from.y + ROW / 2} L${p.point.x} ${p.point.y}`;
|
|
542
|
+
}, /* @ts-ignore */
|
|
543
|
+
...(ngDevMode ? [{ debugName: "linkLine" }] : /* istanbul ignore next */ []));
|
|
544
|
+
// Al abrir, el diagrama empieza cerca de hoy.
|
|
545
|
+
afterNextRender(() => this.scrollToToday(false));
|
|
546
|
+
}
|
|
547
|
+
barEl(id) {
|
|
548
|
+
return Array.from(this.host.nativeElement.querySelectorAll('.cb-gantt__bar')).find((b) => b.dataset['ganttId'] === id) ?? null;
|
|
549
|
+
}
|
|
550
|
+
// --- Cambios ---------------------------------------------------------------------------------
|
|
551
|
+
/** Aplica un cambio a una tarea (y a las desplazadas por la planificación) y lo emite. */
|
|
552
|
+
apply(id, mode, delta, progress) {
|
|
553
|
+
const tasks = this.tasks();
|
|
554
|
+
const resolved = this.resolved();
|
|
555
|
+
const target = resolved.get(id);
|
|
556
|
+
if (!target)
|
|
557
|
+
return;
|
|
558
|
+
const shift = (t, d) => {
|
|
559
|
+
const r = resolved.get(t.id);
|
|
560
|
+
return { ...t, start: isoDay(addDays(r.start, d)), end: isoDay(addDays(r.end, d)) };
|
|
561
|
+
};
|
|
562
|
+
let next;
|
|
563
|
+
if (mode === 'move') {
|
|
564
|
+
if (!delta)
|
|
565
|
+
return;
|
|
566
|
+
const moved = new Set([id, ...descendants(tasks, id)]);
|
|
567
|
+
next = tasks.map((t) => (moved.has(t.id) && !resolved.get(t.id)?.isGroup ? shift(t, delta) : t));
|
|
568
|
+
}
|
|
569
|
+
else if (mode === 'progress') {
|
|
570
|
+
const value = clampProgress(progress ?? 0);
|
|
571
|
+
if (value === target.progress || target.isGroup)
|
|
572
|
+
return;
|
|
573
|
+
next = tasks.map((t) => (t.id === id ? { ...t, progress: value } : t));
|
|
574
|
+
}
|
|
575
|
+
else {
|
|
576
|
+
if (!delta || target.isGroup || target.isMilestone)
|
|
577
|
+
return;
|
|
578
|
+
const length = dayDiff(target.start, target.end);
|
|
579
|
+
next = tasks.map((t) => {
|
|
580
|
+
if (t.id !== id)
|
|
581
|
+
return t;
|
|
582
|
+
return mode === 'start'
|
|
583
|
+
? { ...t, start: isoDay(addDays(target.start, Math.min(delta, length))) }
|
|
584
|
+
: { ...t, end: isoDay(addDays(target.end, Math.max(delta, -length))) };
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
if (this.autoSchedule())
|
|
588
|
+
next = autoSchedule(next);
|
|
589
|
+
this.commit(tasks, next, id, mode === 'progress' ? 'progress' : mode === 'move' ? 'move' : 'resize');
|
|
590
|
+
}
|
|
591
|
+
commit(before, after, id, reason) {
|
|
592
|
+
const prev = new Map(before.map((t) => [t.id, t]));
|
|
593
|
+
this.tasks.set(after);
|
|
594
|
+
const own = new Set([id, ...descendants(before, id)]);
|
|
595
|
+
const changes = [];
|
|
596
|
+
for (const t of after) {
|
|
597
|
+
const p = prev.get(t.id);
|
|
598
|
+
if (!p || p === t)
|
|
599
|
+
continue;
|
|
600
|
+
if (p.start === t.start && p.end === t.end && p.progress === t.progress && p.dependencies === t.dependencies)
|
|
601
|
+
continue;
|
|
602
|
+
changes.push({ task: t, previous: p, reason: own.has(t.id) ? reason : 'schedule' });
|
|
603
|
+
}
|
|
604
|
+
// Primero las replanificadas; al final, la tarea que cambió el usuario.
|
|
605
|
+
changes.sort((a, b) => Number(a.reason !== 'schedule') - Number(b.reason !== 'schedule'));
|
|
606
|
+
for (const change of changes)
|
|
607
|
+
this.taskChange.emit(change);
|
|
608
|
+
const r = this.resolveFresh(id);
|
|
609
|
+
if (r) {
|
|
610
|
+
const text = reason === 'progress'
|
|
611
|
+
? cbFormat(this.t().ganttProgressChanged, r.task.name, r.progress)
|
|
612
|
+
: cbFormat(this.t().ganttMoved, r.task.name, `${this.dateText(r.start)} – ${this.dateText(r.end)}`);
|
|
613
|
+
this.announcement.set(text);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
resolveFresh(id) {
|
|
617
|
+
return this.resolved().get(id);
|
|
618
|
+
}
|
|
619
|
+
/** Crea la dependencia `from` → `to` (si no forma un ciclo). */
|
|
620
|
+
addLink(from, to) {
|
|
621
|
+
const tasks = this.tasks();
|
|
622
|
+
if (!canLink(tasks, from, to))
|
|
623
|
+
return false;
|
|
624
|
+
let next = tasks.map((t) => (t.id === to ? { ...t, dependencies: [...(t.dependencies ?? []), from] } : t));
|
|
625
|
+
if (this.autoSchedule())
|
|
626
|
+
next = autoSchedule(next);
|
|
627
|
+
this.commit(tasks, next, to, 'schedule');
|
|
628
|
+
this.linkAdd.emit({ from, to });
|
|
629
|
+
const names = [from, to].map((id) => this.resolved().get(id)?.task.name ?? id);
|
|
630
|
+
this.announcement.set(cbFormat(this.t().ganttLinkAdded, names[0], names[1]));
|
|
631
|
+
return true;
|
|
632
|
+
}
|
|
633
|
+
/** Quita la dependencia `from` → `to`. */
|
|
634
|
+
removeLink(from, to) {
|
|
635
|
+
const tasks = this.tasks();
|
|
636
|
+
const target = tasks.find((t) => t.id === to);
|
|
637
|
+
if (!target?.dependencies?.includes(from))
|
|
638
|
+
return;
|
|
639
|
+
this.tasks.set(tasks.map((t) => (t.id === to ? { ...t, dependencies: t.dependencies.filter((d) => d !== from) } : t)));
|
|
640
|
+
this.selectedLink.set(null);
|
|
641
|
+
this.linkRemove.emit({ from, to });
|
|
642
|
+
this.announcement.set(this.t().ganttLinkRemoved);
|
|
643
|
+
}
|
|
644
|
+
selectLink(link) {
|
|
645
|
+
if (this.readonly())
|
|
646
|
+
return;
|
|
647
|
+
this.selectedLink.set(this.selectedLink()?.key === link.key ? null : link);
|
|
648
|
+
}
|
|
649
|
+
// --- Arrastre --------------------------------------------------------------------------------
|
|
650
|
+
onPointerDown(event, bar) {
|
|
651
|
+
this.selected.set(bar.row.task.id);
|
|
652
|
+
this.selectedLink.set(null);
|
|
653
|
+
if (this.readonly() || event.button !== 0)
|
|
654
|
+
return;
|
|
655
|
+
const target = event.target;
|
|
656
|
+
let mode = target.dataset['mode'] ?? 'move';
|
|
657
|
+
if (bar.row.isMilestone && mode !== 'link')
|
|
658
|
+
mode = 'move';
|
|
659
|
+
if (bar.row.isGroup && mode !== 'move' && mode !== 'link')
|
|
660
|
+
return;
|
|
661
|
+
event.preventDefault();
|
|
662
|
+
this.drag = { id: bar.row.task.id, mode, x: event.clientX, y: event.clientY, startProgress: bar.progress, width: bar.width, moved: false };
|
|
663
|
+
event.currentTarget.setPointerCapture?.(event.pointerId);
|
|
664
|
+
}
|
|
665
|
+
onPointerMove(event) {
|
|
666
|
+
const drag = this.drag;
|
|
667
|
+
if (!drag)
|
|
668
|
+
return;
|
|
669
|
+
const dx = event.clientX - drag.x;
|
|
670
|
+
if (!drag.moved && Math.abs(dx) < 3 && Math.abs(event.clientY - drag.y) < 3)
|
|
671
|
+
return;
|
|
672
|
+
drag.moved = true;
|
|
673
|
+
if (drag.mode === 'link') {
|
|
674
|
+
const rect = this.chart()?.nativeElement.getBoundingClientRect();
|
|
675
|
+
this.preview.set({ id: drag.id, mode: 'link', delta: 0, progress: 0, point: rect ? { x: event.clientX - rect.left, y: event.clientY - rect.top } : null });
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
678
|
+
if (drag.mode === 'progress') {
|
|
679
|
+
const value = clampProgress(drag.startProgress + (dx / Math.max(1, drag.width)) * 100);
|
|
680
|
+
this.preview.set({ id: drag.id, mode: 'progress', delta: 0, progress: value, point: null });
|
|
681
|
+
return;
|
|
682
|
+
}
|
|
683
|
+
this.preview.set({ id: drag.id, mode: drag.mode, delta: Math.round(dx / this.dayWidth()), progress: 0, point: null });
|
|
684
|
+
}
|
|
685
|
+
onPointerUp(event) {
|
|
686
|
+
const drag = this.drag;
|
|
687
|
+
const preview = this.preview();
|
|
688
|
+
this.drag = null;
|
|
689
|
+
this.preview.set(null);
|
|
690
|
+
if (!drag?.moved || !preview)
|
|
691
|
+
return;
|
|
692
|
+
if (drag.mode === 'link') {
|
|
693
|
+
const doc = this.host.nativeElement.ownerDocument;
|
|
694
|
+
const under = doc.elementFromPoint?.(event.clientX, event.clientY);
|
|
695
|
+
const to = under?.closest('.cb-gantt__bar')?.dataset['ganttId'];
|
|
696
|
+
if (to)
|
|
697
|
+
this.addLink(drag.id, to);
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
this.apply(drag.id, drag.mode, preview.delta, preview.progress);
|
|
701
|
+
}
|
|
702
|
+
cancelDrag() {
|
|
703
|
+
this.drag = null;
|
|
704
|
+
this.preview.set(null);
|
|
705
|
+
}
|
|
706
|
+
// --- Teclado ---------------------------------------------------------------------------------
|
|
707
|
+
onBarKeydown(event, r) {
|
|
708
|
+
if (event.key === 'Enter') {
|
|
709
|
+
event.preventDefault();
|
|
710
|
+
this.taskOpen.emit(r.task);
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
if (event.key === 'Escape' && this.drag) {
|
|
714
|
+
this.cancelDrag();
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
if (this.readonly())
|
|
718
|
+
return;
|
|
719
|
+
const id = r.task.id;
|
|
720
|
+
const step = event.key === 'ArrowRight' ? 1 : event.key === 'ArrowLeft' ? -1 : 0;
|
|
721
|
+
if (step) {
|
|
722
|
+
event.preventDefault();
|
|
723
|
+
const mode = event.shiftKey ? 'end' : event.altKey ? 'start' : 'move';
|
|
724
|
+
if (mode !== 'move' && (r.isGroup || r.isMilestone))
|
|
725
|
+
return;
|
|
726
|
+
this.apply(id, mode, step);
|
|
727
|
+
this.focusBar(id);
|
|
728
|
+
}
|
|
729
|
+
else if (['+', '=', '-', 'PageUp', 'PageDown'].includes(event.key) && !r.isGroup && !r.isMilestone) {
|
|
730
|
+
event.preventDefault();
|
|
731
|
+
const up = event.key === '+' || event.key === '=' || event.key === 'PageUp';
|
|
732
|
+
this.apply(id, 'progress', 0, r.progress + (up ? 10 : -10));
|
|
733
|
+
this.focusBar(id);
|
|
734
|
+
}
|
|
735
|
+
else if ((event.key === 'Delete' || event.key === 'Backspace') && r.task.dependencies?.length) {
|
|
736
|
+
event.preventDefault();
|
|
737
|
+
for (const from of [...r.task.dependencies])
|
|
738
|
+
this.removeLink(from, id);
|
|
739
|
+
this.focusBar(id);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbGantt, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
743
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: CbGantt, isStandalone: true, selector: "cb-gantt", inputs: { tasks: { classPropertyName: "tasks", publicName: "tasks", isSignal: true, isRequired: false, transformFunction: null }, scale: { classPropertyName: "scale", publicName: "scale", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, gridWidth: { classPropertyName: "gridWidth", publicName: "gridWidth", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, autoSchedule: { classPropertyName: "autoSchedule", publicName: "autoSchedule", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, showToolbar: { classPropertyName: "showToolbar", publicName: "showToolbar", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { tasks: "tasksChange", scale: "scaleChange", taskChange: "taskChange", taskOpen: "taskOpen", linkAdd: "linkAdd", linkRemove: "linkRemove" }, host: { properties: { "class.cb-gantt--readonly": "readonly()" }, classAttribute: "cb-gantt" }, viewQueries: [{ propertyName: "scroller", first: true, predicate: ["scroller"], descendants: true, isSignal: true }, { propertyName: "chart", first: true, predicate: ["chart"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
744
|
+
@if (showToolbar()) {
|
|
745
|
+
<div class="cb-gantt__toolbar">
|
|
746
|
+
<div class="cb-gantt__scales" role="group" [attr.aria-label]="t().ganttScale">
|
|
747
|
+
@for (s of scales; track s) {
|
|
748
|
+
<button type="button" class="cb-gantt__scale" [attr.aria-pressed]="scale() === s" (click)="scale.set(s)">{{ scaleLabel(s) }}</button>
|
|
749
|
+
}
|
|
750
|
+
</div>
|
|
751
|
+
<button type="button" class="cb-gantt__btn" (click)="scrollToToday()">{{ t().today }}</button>
|
|
752
|
+
</div>
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
<div #scroller class="cb-gantt__scroller" [style.height]="height()" (pointermove)="onPointerMove($event)" (pointerup)="onPointerUp($event)" (pointercancel)="cancelDrag()">
|
|
756
|
+
<div class="cb-gantt__inner" [style.--cb-gantt-grid]="gridSize()">
|
|
757
|
+
<!-- Cabecera: columnas de la tabla y escala de tiempo -->
|
|
758
|
+
<div class="cb-gantt__head">
|
|
759
|
+
<div class="cb-gantt__head-grid" aria-hidden="true">
|
|
760
|
+
@for (col of columns(); track col) {
|
|
761
|
+
<span class="cb-gantt__head-cell" [class]="'cb-gantt__head-cell cb-gantt__col-' + col">{{ columnLabel(col) }}</span>
|
|
762
|
+
}
|
|
763
|
+
</div>
|
|
764
|
+
<div class="cb-gantt__head-scale" [style.width.px]="chartWidth()" aria-hidden="true">
|
|
765
|
+
<div class="cb-gantt__head-row">
|
|
766
|
+
@for (u of topUnits(); track u.key) {
|
|
767
|
+
<span class="cb-gantt__unit cb-gantt__unit--top" [style.left.px]="u.x" [style.width.px]="u.width">{{ u.label }}</span>
|
|
768
|
+
}
|
|
769
|
+
</div>
|
|
770
|
+
<div class="cb-gantt__head-row">
|
|
771
|
+
@for (u of units(); track u.key) {
|
|
772
|
+
<span class="cb-gantt__unit" [class.cb-gantt__unit--weekend]="u.weekend" [style.left.px]="u.x" [style.width.px]="u.width">{{ u.label }}</span>
|
|
773
|
+
}
|
|
774
|
+
</div>
|
|
775
|
+
</div>
|
|
776
|
+
</div>
|
|
777
|
+
|
|
778
|
+
<div class="cb-gantt__body" [style.height.px]="rows().length * row">
|
|
779
|
+
<!-- Tabla de tareas (árbol) -->
|
|
780
|
+
<div class="cb-gantt__grid" role="treegrid" [attr.aria-label]="ariaLabel() || t().ganttChart" [attr.aria-rowcount]="rows().length">
|
|
781
|
+
@for (r of rows(); track r.task.id; let i = $index) {
|
|
782
|
+
<div
|
|
783
|
+
class="cb-gantt__row"
|
|
784
|
+
role="row"
|
|
785
|
+
[class.cb-is-selected]="selected() === r.task.id"
|
|
786
|
+
[attr.aria-level]="r.level + 1"
|
|
787
|
+
[attr.aria-expanded]="r.children.length ? !collapsed().has(r.task.id) : null"
|
|
788
|
+
[attr.aria-rowindex]="i + 1"
|
|
789
|
+
[attr.aria-selected]="selected() === r.task.id"
|
|
790
|
+
>
|
|
791
|
+
@for (col of columns(); track col) {
|
|
792
|
+
@if (col === 'name') {
|
|
793
|
+
<span class="cb-gantt__cell cb-gantt__col-name" role="gridcell" [style.padding-left]="0.5 + r.level * 1.1 + 'rem'">
|
|
794
|
+
@if (r.children.length) {
|
|
795
|
+
<button
|
|
796
|
+
type="button"
|
|
797
|
+
class="cb-gantt__toggle"
|
|
798
|
+
[attr.aria-label]="fmt(collapsed().has(r.task.id) ? t().ganttExpand : t().ganttCollapse, r.task.name)"
|
|
799
|
+
[attr.aria-expanded]="!collapsed().has(r.task.id)"
|
|
800
|
+
(click)="toggle(r.task.id)"
|
|
801
|
+
>
|
|
802
|
+
<cb-icon [name]="collapsed().has(r.task.id) ? 'chevron-right' : 'chevron-down'" [size]="14" aria-hidden="true" />
|
|
803
|
+
</button>
|
|
804
|
+
} @else {
|
|
805
|
+
<span class="cb-gantt__toggle-space" aria-hidden="true"></span>
|
|
806
|
+
}
|
|
807
|
+
<button type="button" class="cb-gantt__name" [class.cb-gantt__name--group]="r.isGroup" (click)="focusBar(r.task.id)" (dblclick)="taskOpen.emit(r.task)">
|
|
808
|
+
@if (r.isMilestone) {
|
|
809
|
+
<span class="cb-gantt__diamond-mini" aria-hidden="true"></span>
|
|
810
|
+
}
|
|
811
|
+
{{ r.task.name }}
|
|
812
|
+
</button>
|
|
813
|
+
</span>
|
|
814
|
+
} @else {
|
|
815
|
+
<span [class]="'cb-gantt__cell cb-gantt__col-' + col" role="gridcell">{{ cellText(r, col) }}</span>
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
</div>
|
|
819
|
+
}
|
|
820
|
+
</div>
|
|
821
|
+
|
|
822
|
+
<!-- Diagrama -->
|
|
823
|
+
<div #chart class="cb-gantt__chart" [style.width.px]="chartWidth()">
|
|
824
|
+
@for (u of units(); track u.key) {
|
|
825
|
+
<span class="cb-gantt__col" [class.cb-gantt__col--weekend]="u.weekend" [style.left.px]="u.x" [style.width.px]="u.width" aria-hidden="true"></span>
|
|
826
|
+
}
|
|
827
|
+
@for (r of rows(); track r.task.id; let i = $index) {
|
|
828
|
+
<span class="cb-gantt__stripe" [class.cb-is-selected]="selected() === r.task.id" [style.top.px]="i * row" aria-hidden="true"></span>
|
|
829
|
+
}
|
|
830
|
+
@if (todayX() !== null) {
|
|
831
|
+
<span class="cb-gantt__today" [style.left.px]="todayX()" aria-hidden="true"></span>
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
<svg class="cb-gantt__links" [attr.width]="chartWidth()" [attr.height]="rows().length * row" aria-hidden="true">
|
|
835
|
+
<defs>
|
|
836
|
+
<marker [attr.id]="uid + '-arrow'" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
|
837
|
+
<path d="M0 0 L8 4 L0 8 z" />
|
|
838
|
+
</marker>
|
|
839
|
+
</defs>
|
|
840
|
+
@for (l of links(); track l.key) {
|
|
841
|
+
<path class="cb-gantt__link-hit" [attr.d]="l.path" (click)="selectLink(l)" />
|
|
842
|
+
<path class="cb-gantt__link" [class.cb-is-selected]="selectedLink()?.key === l.key" [attr.d]="l.path" [attr.marker-end]="'url(#' + uid + '-arrow)'" />
|
|
843
|
+
}
|
|
844
|
+
@if (linkLine(); as line) {
|
|
845
|
+
<path class="cb-gantt__link cb-gantt__link--draft" [attr.d]="line" />
|
|
846
|
+
}
|
|
847
|
+
</svg>
|
|
848
|
+
|
|
849
|
+
@if (selectedLink(); as sl) {
|
|
850
|
+
<button
|
|
851
|
+
type="button"
|
|
852
|
+
class="cb-gantt__unlink"
|
|
853
|
+
[style.left.px]="sl.mid.x"
|
|
854
|
+
[style.top.px]="sl.mid.y"
|
|
855
|
+
[attr.aria-label]="fmt(t().ganttRemoveLink, sl.fromName, sl.toName)"
|
|
856
|
+
[attr.title]="fmt(t().ganttRemoveLink, sl.fromName, sl.toName)"
|
|
857
|
+
(click)="removeLink(sl.from, sl.to)"
|
|
858
|
+
>
|
|
859
|
+
<cb-icon name="x" [size]="12" aria-hidden="true" />
|
|
860
|
+
</button>
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
@for (b of bars(); track b.row.task.id) {
|
|
864
|
+
<div
|
|
865
|
+
class="cb-gantt__bar"
|
|
866
|
+
role="button"
|
|
867
|
+
tabindex="0"
|
|
868
|
+
[attr.data-gantt-id]="b.row.task.id"
|
|
869
|
+
[class.cb-gantt__bar--group]="b.row.isGroup"
|
|
870
|
+
[class.cb-gantt__bar--milestone]="b.row.isMilestone"
|
|
871
|
+
[class.cb-gantt__bar--short]="!b.row.isMilestone && !b.row.isGroup && b.width < 72"
|
|
872
|
+
[class.cb-is-selected]="selected() === b.row.task.id"
|
|
873
|
+
[class.cb-is-dragging]="preview()?.id === b.row.task.id"
|
|
874
|
+
[style.left.px]="b.x"
|
|
875
|
+
[style.top.px]="b.y"
|
|
876
|
+
[style.width.px]="b.width"
|
|
877
|
+
[style.--cb-gantt-color]="b.row.task.color || null"
|
|
878
|
+
[attr.aria-label]="barLabel(b)"
|
|
879
|
+
[attr.aria-describedby]="readonly() ? null : uid + '-help'"
|
|
880
|
+
(pointerdown)="onPointerDown($event, b)"
|
|
881
|
+
(keydown)="onBarKeydown($event, b.row)"
|
|
882
|
+
(click)="selected.set(b.row.task.id)"
|
|
883
|
+
(dblclick)="taskOpen.emit(b.row.task)"
|
|
884
|
+
>
|
|
885
|
+
@if (!b.row.isMilestone) {
|
|
886
|
+
<span class="cb-gantt__progress" [style.width.%]="b.progress" aria-hidden="true"></span>
|
|
887
|
+
<span class="cb-gantt__bar-label" aria-hidden="true">{{ b.row.task.name }}</span>
|
|
888
|
+
@if (!readonly() && !b.row.isGroup) {
|
|
889
|
+
<span class="cb-gantt__handle cb-gantt__handle--start" data-mode="start" aria-hidden="true"></span>
|
|
890
|
+
<span class="cb-gantt__handle cb-gantt__handle--end" data-mode="end" aria-hidden="true"></span>
|
|
891
|
+
<span class="cb-gantt__progress-handle" data-mode="progress" [style.left.%]="b.progress" aria-hidden="true"></span>
|
|
892
|
+
}
|
|
893
|
+
} @else {
|
|
894
|
+
<span class="cb-gantt__diamond" aria-hidden="true"></span>
|
|
895
|
+
<span class="cb-gantt__milestone-label" aria-hidden="true">{{ b.row.task.name }}</span>
|
|
896
|
+
}
|
|
897
|
+
@if (!readonly()) {
|
|
898
|
+
<span class="cb-gantt__connector" data-mode="link" aria-hidden="true"></span>
|
|
899
|
+
}
|
|
900
|
+
</div>
|
|
901
|
+
}
|
|
902
|
+
</div>
|
|
903
|
+
</div>
|
|
904
|
+
</div>
|
|
905
|
+
@if (!rows().length) {
|
|
906
|
+
<p class="cb-gantt__empty">{{ emptyMessage() ?? t().noData }}</p>
|
|
907
|
+
}
|
|
908
|
+
</div>
|
|
909
|
+
<p class="cb-gantt__sr" [id]="uid + '-help'">{{ t().ganttInstructions }}</p>
|
|
910
|
+
<div class="cb-gantt__sr" role="status">{{ announcement() }}</div>
|
|
911
|
+
`, isInline: true, styles: [":host{--cb-gantt-row: 40px;--cb-gantt-bar: var(--cb-color-primary-500);--cb-gantt-bar-bg: color-mix(in srgb, var(--cb-gantt-bar) 28%, var(--cb-color-surface));display:flex;flex-direction:column;gap:var(--cb-space-2, .5rem);width:100%;min-width:0;color:var(--cb-color-text);font-family:var(--cb-font-sans);font-size:var(--cb-text-sm, .875rem)}.cb-gantt__toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem}.cb-gantt__scales{display:inline-flex;padding:2px;border-radius:var(--cb-radius-md);background:var(--cb-color-surface-hover)}.cb-gantt__scale,.cb-gantt__btn{padding:.3rem .75rem;border:0;border-radius:calc(var(--cb-radius-md) - 2px);background:transparent;color:var(--cb-color-text-muted);font:inherit;font-size:var(--cb-text-xs, .75rem);font-weight:var(--cb-font-weight-medium, 500);cursor:pointer}.cb-gantt__scale[aria-pressed=true]{background:var(--cb-color-surface-raised);color:var(--cb-color-text-strong, var(--cb-color-text));box-shadow:0 1px 2px #0f172a1f}.cb-gantt__btn{border:1px solid var(--cb-color-border-strong);color:var(--cb-color-text)}.cb-gantt__btn:hover{background:var(--cb-color-surface-hover)}:is(.cb-gantt__scale,.cb-gantt__btn,.cb-gantt__toggle,.cb-gantt__name,.cb-gantt__unlink):focus-visible{outline:2px solid var(--cb-color-focus-ring);outline-offset:1px}.cb-gantt__scroller{position:relative;overflow:auto;container-type:inline-size;border:1px solid var(--cb-color-border);border-radius:var(--cb-radius-lg, 12px);background:var(--cb-color-surface)}.cb-gantt__inner{--cb-gantt-grid-w: min(var(--cb-gantt-grid, 30rem), 60cqi);position:relative;width:max-content;min-width:100%}.cb-gantt__head{position:sticky;top:0;z-index:4;display:flex;height:52px;border-bottom:1px solid var(--cb-color-border);background:var(--cb-color-surface-subtle, var(--cb-color-surface))}.cb-gantt__head-grid{position:sticky;left:0;z-index:5;display:flex;flex:none;align-items:flex-end;width:var(--cb-gantt-grid-w);padding-bottom:.45rem;border-right:1px solid var(--cb-color-border);background:inherit}.cb-gantt__head-cell{flex:0 0 auto;box-sizing:border-box;padding:0 .5rem;overflow:hidden;color:var(--cb-color-text-muted);font-size:var(--cb-text-xs, .75rem);font-weight:var(--cb-font-weight-semibold, 600);text-overflow:ellipsis;white-space:nowrap}.cb-gantt__head-cell.cb-gantt__col-name{padding-left:.75rem}.cb-gantt__col-name{flex:1 1 auto!important;min-width:6rem}.cb-gantt__col-start,.cb-gantt__col-end,.cb-gantt__col-duration{width:4.5rem}.cb-gantt__col-progress{width:4rem}.cb-gantt__col-assignee{width:6.5rem}.cb-gantt__head-scale{position:relative;flex:none}.cb-gantt__head-row{position:relative;height:26px}.cb-gantt__unit{position:absolute;top:0;bottom:0;display:flex;align-items:center;justify-content:center;overflow:hidden;border-left:1px solid var(--cb-color-border);color:var(--cb-color-text-muted);font-size:.6875rem;white-space:nowrap}.cb-gantt__unit--top{justify-content:flex-start;padding-left:.5rem;color:var(--cb-color-text);font-weight:var(--cb-font-weight-semibold, 600)}.cb-gantt__unit--weekend{background:var(--cb-color-surface-hover)}@container (max-width: 40rem){.cb-gantt__col-start,.cb-gantt__col-end,.cb-gantt__col-duration,.cb-gantt__col-progress,.cb-gantt__col-assignee{display:none}.cb-gantt__head-grid,.cb-gantt__row{overflow:hidden}}.cb-gantt__body{position:relative;display:flex}.cb-gantt__grid{position:sticky;left:0;z-index:3;flex:none;width:var(--cb-gantt-grid-w);border-right:1px solid var(--cb-color-border);background:var(--cb-color-surface)}.cb-gantt__row{display:flex;align-items:center;height:var(--cb-gantt-row);border-bottom:1px solid var(--cb-color-border)}.cb-gantt__row.cb-is-selected{background:color-mix(in srgb,var(--cb-color-primary-500) 9%,var(--cb-color-surface))}.cb-gantt__cell{flex:0 0 auto;box-sizing:border-box;padding:0 .5rem;overflow:hidden;color:var(--cb-color-text-muted);font-size:var(--cb-text-xs, .75rem);font-variant-numeric:tabular-nums;text-overflow:ellipsis;white-space:nowrap}.cb-gantt__cell.cb-gantt__col-name{display:flex;align-items:center;gap:.25rem;min-width:0;color:var(--cb-color-text);font-size:var(--cb-text-sm, .875rem)}.cb-gantt__toggle,.cb-gantt__toggle-space{display:inline-flex;flex:none;align-items:center;justify-content:center;width:1.4rem;height:1.4rem}.cb-gantt__toggle{padding:0;border:0;border-radius:var(--cb-radius-sm);background:transparent;color:var(--cb-color-text-muted);cursor:pointer}.cb-gantt__toggle:hover{background:var(--cb-color-surface-hover)}.cb-gantt__name{display:inline-flex;align-items:center;gap:.4rem;min-width:0;padding:.15rem .25rem;overflow:hidden;border:0;border-radius:var(--cb-radius-sm);background:transparent;color:inherit;font:inherit;text-align:start;text-overflow:ellipsis;white-space:nowrap;cursor:pointer}.cb-gantt__name--group{font-weight:var(--cb-font-weight-semibold, 600)}.cb-gantt__diamond-mini{flex:none;width:.55rem;height:.55rem;background:var(--cb-color-warning-500, #f59e0b);transform:rotate(45deg)}.cb-gantt__chart{position:relative;flex:none}.cb-gantt__col{position:absolute;top:0;bottom:0;border-left:1px solid color-mix(in srgb,var(--cb-color-border) 60%,transparent)}.cb-gantt__col--weekend{background:color-mix(in srgb,var(--cb-color-surface-hover) 70%,transparent)}.cb-gantt__stripe{position:absolute;left:0;right:0;height:var(--cb-gantt-row);border-bottom:1px solid var(--cb-color-border)}.cb-gantt__stripe.cb-is-selected{background:color-mix(in srgb,var(--cb-color-primary-500) 7%,transparent)}.cb-gantt__today{position:absolute;top:0;bottom:0;z-index:1;width:2px;margin-left:-1px;background:var(--cb-color-danger-500, #ef4444)}.cb-gantt__links{position:absolute;inset:0;z-index:1;overflow:visible;pointer-events:none}.cb-gantt__links marker path{fill:var(--cb-color-text-muted)}.cb-gantt__link{fill:none;stroke:var(--cb-color-text-muted);stroke-width:1.5}.cb-gantt__link.cb-is-selected{stroke:var(--cb-color-primary-600, var(--cb-color-primary-500));stroke-width:2.5}.cb-gantt__link--draft{stroke:var(--cb-color-primary-500);stroke-dasharray:4 3}.cb-gantt__link-hit{fill:none;stroke:transparent;stroke-width:10;pointer-events:stroke;cursor:pointer}:host(.cb-gantt--readonly) .cb-gantt__link-hit{cursor:default}.cb-gantt__unlink{position:absolute;z-index:3;display:inline-flex;align-items:center;justify-content:center;width:1.25rem;height:1.25rem;margin:-.625rem 0 0 -.625rem;padding:0;border:1px solid var(--cb-color-border-strong);border-radius:50%;background:var(--cb-color-surface-raised);color:var(--cb-color-danger-600, var(--cb-color-danger));cursor:pointer}.cb-gantt__bar{position:absolute;z-index:2;box-sizing:border-box;height:24px;margin-top:8px;border-radius:6px;background:color-mix(in srgb,var(--cb-gantt-color, var(--cb-gantt-bar)) 28%,var(--cb-color-surface));box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--cb-gantt-color, var(--cb-gantt-bar)) 55%,transparent);cursor:grab;touch-action:none;-webkit-user-select:none;user-select:none}:host(.cb-gantt--readonly) .cb-gantt__bar{cursor:pointer}.cb-gantt__bar:focus-visible{outline:2px solid var(--cb-color-focus-ring);outline-offset:2px}.cb-gantt__bar.cb-is-selected{box-shadow:inset 0 0 0 2px var(--cb-gantt-color, var(--cb-gantt-bar))}.cb-gantt__bar.cb-is-dragging{cursor:grabbing;opacity:.85}.cb-gantt__progress{position:absolute;inset:0 auto 0 0;border-radius:6px 0 0 6px;background:var(--cb-gantt-color, var(--cb-gantt-bar))}.cb-gantt__bar-label{position:relative;display:block;padding:0 .5rem;overflow:hidden;color:var(--cb-color-text);font-size:var(--cb-text-xs, .75rem);font-weight:var(--cb-font-weight-medium, 500);line-height:24px;text-overflow:ellipsis;white-space:nowrap;mix-blend-mode:normal}.cb-gantt__bar--short .cb-gantt__bar-label{position:absolute;top:0;left:calc(100% + 18px);padding:0;overflow:visible}.cb-gantt__handle{position:absolute;top:0;bottom:0;width:8px;cursor:ew-resize}.cb-gantt__handle--start{left:0}.cb-gantt__handle--end{right:0}.cb-gantt__progress-handle{position:absolute;bottom:-6px;width:10px;height:10px;margin-left:-5px;border:2px solid var(--cb-color-surface);border-radius:50%;background:var(--cb-gantt-color, var(--cb-gantt-bar));cursor:col-resize;opacity:0}.cb-gantt__connector{position:absolute;top:50%;right:-14px;width:10px;height:10px;margin-top:-5px;border:2px solid var(--cb-gantt-color, var(--cb-gantt-bar));border-radius:50%;background:var(--cb-color-surface);cursor:crosshair;opacity:0}.cb-gantt__bar:hover :is(.cb-gantt__progress-handle,.cb-gantt__connector),.cb-gantt__bar.cb-is-selected :is(.cb-gantt__progress-handle,.cb-gantt__connector){opacity:1}@media(hover:none){.cb-gantt__progress-handle,.cb-gantt__connector{opacity:1}}.cb-gantt__bar--group{height:12px;margin-top:14px;border-radius:2px;background:var(--cb-color-text-muted);box-shadow:none}.cb-gantt__bar--group .cb-gantt__progress{border-radius:2px 0 0 2px;background:var(--cb-color-text)}.cb-gantt__bar--group .cb-gantt__bar-label{position:absolute;bottom:12px;left:0;padding:0;line-height:1.2}.cb-gantt__bar--milestone{width:18px;height:18px;margin-top:11px;background:transparent;box-shadow:none}.cb-gantt__bar--milestone.cb-is-selected{box-shadow:none}.cb-gantt__diamond{position:absolute;inset:2px;border-radius:3px;background:var(--cb-gantt-color, var(--cb-color-warning-500, #f59e0b));transform:rotate(45deg)}.cb-gantt__bar--milestone.cb-is-selected .cb-gantt__diamond{outline:2px solid var(--cb-color-text);outline-offset:1px}.cb-gantt__milestone-label{position:absolute;top:0;left:26px;color:var(--cb-color-text);font-size:var(--cb-text-xs, .75rem);font-weight:var(--cb-font-weight-medium, 500);line-height:18px;white-space:nowrap}.cb-gantt__bar--milestone .cb-gantt__connector{right:-16px}.cb-gantt__empty{margin:0;padding:2rem;color:var(--cb-color-text-muted);text-align:center}.cb-gantt__sr{position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap}@media(prefers-reduced-motion:no-preference){.cb-gantt__bar:not(.cb-is-dragging){transition:left .12s ease,width .12s ease}}@media(forced-colors:active){.cb-gantt__bar,.cb-gantt__progress{forced-color-adjust:none;border:1px solid CanvasText}}\n"], dependencies: [{ kind: "component", type: CbIcon, selector: "cb-icon", inputs: ["name", "size", "spin", "ariaLabel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
912
|
+
}
|
|
913
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbGantt, decorators: [{
|
|
914
|
+
type: Component,
|
|
915
|
+
args: [{ selector: 'cb-gantt', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, imports: [CbIcon], host: { class: 'cb-gantt', '[class.cb-gantt--readonly]': 'readonly()' }, template: `
|
|
916
|
+
@if (showToolbar()) {
|
|
917
|
+
<div class="cb-gantt__toolbar">
|
|
918
|
+
<div class="cb-gantt__scales" role="group" [attr.aria-label]="t().ganttScale">
|
|
919
|
+
@for (s of scales; track s) {
|
|
920
|
+
<button type="button" class="cb-gantt__scale" [attr.aria-pressed]="scale() === s" (click)="scale.set(s)">{{ scaleLabel(s) }}</button>
|
|
921
|
+
}
|
|
922
|
+
</div>
|
|
923
|
+
<button type="button" class="cb-gantt__btn" (click)="scrollToToday()">{{ t().today }}</button>
|
|
924
|
+
</div>
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
<div #scroller class="cb-gantt__scroller" [style.height]="height()" (pointermove)="onPointerMove($event)" (pointerup)="onPointerUp($event)" (pointercancel)="cancelDrag()">
|
|
928
|
+
<div class="cb-gantt__inner" [style.--cb-gantt-grid]="gridSize()">
|
|
929
|
+
<!-- Cabecera: columnas de la tabla y escala de tiempo -->
|
|
930
|
+
<div class="cb-gantt__head">
|
|
931
|
+
<div class="cb-gantt__head-grid" aria-hidden="true">
|
|
932
|
+
@for (col of columns(); track col) {
|
|
933
|
+
<span class="cb-gantt__head-cell" [class]="'cb-gantt__head-cell cb-gantt__col-' + col">{{ columnLabel(col) }}</span>
|
|
934
|
+
}
|
|
935
|
+
</div>
|
|
936
|
+
<div class="cb-gantt__head-scale" [style.width.px]="chartWidth()" aria-hidden="true">
|
|
937
|
+
<div class="cb-gantt__head-row">
|
|
938
|
+
@for (u of topUnits(); track u.key) {
|
|
939
|
+
<span class="cb-gantt__unit cb-gantt__unit--top" [style.left.px]="u.x" [style.width.px]="u.width">{{ u.label }}</span>
|
|
940
|
+
}
|
|
941
|
+
</div>
|
|
942
|
+
<div class="cb-gantt__head-row">
|
|
943
|
+
@for (u of units(); track u.key) {
|
|
944
|
+
<span class="cb-gantt__unit" [class.cb-gantt__unit--weekend]="u.weekend" [style.left.px]="u.x" [style.width.px]="u.width">{{ u.label }}</span>
|
|
945
|
+
}
|
|
946
|
+
</div>
|
|
947
|
+
</div>
|
|
948
|
+
</div>
|
|
949
|
+
|
|
950
|
+
<div class="cb-gantt__body" [style.height.px]="rows().length * row">
|
|
951
|
+
<!-- Tabla de tareas (árbol) -->
|
|
952
|
+
<div class="cb-gantt__grid" role="treegrid" [attr.aria-label]="ariaLabel() || t().ganttChart" [attr.aria-rowcount]="rows().length">
|
|
953
|
+
@for (r of rows(); track r.task.id; let i = $index) {
|
|
954
|
+
<div
|
|
955
|
+
class="cb-gantt__row"
|
|
956
|
+
role="row"
|
|
957
|
+
[class.cb-is-selected]="selected() === r.task.id"
|
|
958
|
+
[attr.aria-level]="r.level + 1"
|
|
959
|
+
[attr.aria-expanded]="r.children.length ? !collapsed().has(r.task.id) : null"
|
|
960
|
+
[attr.aria-rowindex]="i + 1"
|
|
961
|
+
[attr.aria-selected]="selected() === r.task.id"
|
|
962
|
+
>
|
|
963
|
+
@for (col of columns(); track col) {
|
|
964
|
+
@if (col === 'name') {
|
|
965
|
+
<span class="cb-gantt__cell cb-gantt__col-name" role="gridcell" [style.padding-left]="0.5 + r.level * 1.1 + 'rem'">
|
|
966
|
+
@if (r.children.length) {
|
|
967
|
+
<button
|
|
968
|
+
type="button"
|
|
969
|
+
class="cb-gantt__toggle"
|
|
970
|
+
[attr.aria-label]="fmt(collapsed().has(r.task.id) ? t().ganttExpand : t().ganttCollapse, r.task.name)"
|
|
971
|
+
[attr.aria-expanded]="!collapsed().has(r.task.id)"
|
|
972
|
+
(click)="toggle(r.task.id)"
|
|
973
|
+
>
|
|
974
|
+
<cb-icon [name]="collapsed().has(r.task.id) ? 'chevron-right' : 'chevron-down'" [size]="14" aria-hidden="true" />
|
|
975
|
+
</button>
|
|
976
|
+
} @else {
|
|
977
|
+
<span class="cb-gantt__toggle-space" aria-hidden="true"></span>
|
|
978
|
+
}
|
|
979
|
+
<button type="button" class="cb-gantt__name" [class.cb-gantt__name--group]="r.isGroup" (click)="focusBar(r.task.id)" (dblclick)="taskOpen.emit(r.task)">
|
|
980
|
+
@if (r.isMilestone) {
|
|
981
|
+
<span class="cb-gantt__diamond-mini" aria-hidden="true"></span>
|
|
982
|
+
}
|
|
983
|
+
{{ r.task.name }}
|
|
984
|
+
</button>
|
|
985
|
+
</span>
|
|
986
|
+
} @else {
|
|
987
|
+
<span [class]="'cb-gantt__cell cb-gantt__col-' + col" role="gridcell">{{ cellText(r, col) }}</span>
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
</div>
|
|
991
|
+
}
|
|
992
|
+
</div>
|
|
993
|
+
|
|
994
|
+
<!-- Diagrama -->
|
|
995
|
+
<div #chart class="cb-gantt__chart" [style.width.px]="chartWidth()">
|
|
996
|
+
@for (u of units(); track u.key) {
|
|
997
|
+
<span class="cb-gantt__col" [class.cb-gantt__col--weekend]="u.weekend" [style.left.px]="u.x" [style.width.px]="u.width" aria-hidden="true"></span>
|
|
998
|
+
}
|
|
999
|
+
@for (r of rows(); track r.task.id; let i = $index) {
|
|
1000
|
+
<span class="cb-gantt__stripe" [class.cb-is-selected]="selected() === r.task.id" [style.top.px]="i * row" aria-hidden="true"></span>
|
|
1001
|
+
}
|
|
1002
|
+
@if (todayX() !== null) {
|
|
1003
|
+
<span class="cb-gantt__today" [style.left.px]="todayX()" aria-hidden="true"></span>
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
<svg class="cb-gantt__links" [attr.width]="chartWidth()" [attr.height]="rows().length * row" aria-hidden="true">
|
|
1007
|
+
<defs>
|
|
1008
|
+
<marker [attr.id]="uid + '-arrow'" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
|
1009
|
+
<path d="M0 0 L8 4 L0 8 z" />
|
|
1010
|
+
</marker>
|
|
1011
|
+
</defs>
|
|
1012
|
+
@for (l of links(); track l.key) {
|
|
1013
|
+
<path class="cb-gantt__link-hit" [attr.d]="l.path" (click)="selectLink(l)" />
|
|
1014
|
+
<path class="cb-gantt__link" [class.cb-is-selected]="selectedLink()?.key === l.key" [attr.d]="l.path" [attr.marker-end]="'url(#' + uid + '-arrow)'" />
|
|
1015
|
+
}
|
|
1016
|
+
@if (linkLine(); as line) {
|
|
1017
|
+
<path class="cb-gantt__link cb-gantt__link--draft" [attr.d]="line" />
|
|
1018
|
+
}
|
|
1019
|
+
</svg>
|
|
1020
|
+
|
|
1021
|
+
@if (selectedLink(); as sl) {
|
|
1022
|
+
<button
|
|
1023
|
+
type="button"
|
|
1024
|
+
class="cb-gantt__unlink"
|
|
1025
|
+
[style.left.px]="sl.mid.x"
|
|
1026
|
+
[style.top.px]="sl.mid.y"
|
|
1027
|
+
[attr.aria-label]="fmt(t().ganttRemoveLink, sl.fromName, sl.toName)"
|
|
1028
|
+
[attr.title]="fmt(t().ganttRemoveLink, sl.fromName, sl.toName)"
|
|
1029
|
+
(click)="removeLink(sl.from, sl.to)"
|
|
1030
|
+
>
|
|
1031
|
+
<cb-icon name="x" [size]="12" aria-hidden="true" />
|
|
1032
|
+
</button>
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
@for (b of bars(); track b.row.task.id) {
|
|
1036
|
+
<div
|
|
1037
|
+
class="cb-gantt__bar"
|
|
1038
|
+
role="button"
|
|
1039
|
+
tabindex="0"
|
|
1040
|
+
[attr.data-gantt-id]="b.row.task.id"
|
|
1041
|
+
[class.cb-gantt__bar--group]="b.row.isGroup"
|
|
1042
|
+
[class.cb-gantt__bar--milestone]="b.row.isMilestone"
|
|
1043
|
+
[class.cb-gantt__bar--short]="!b.row.isMilestone && !b.row.isGroup && b.width < 72"
|
|
1044
|
+
[class.cb-is-selected]="selected() === b.row.task.id"
|
|
1045
|
+
[class.cb-is-dragging]="preview()?.id === b.row.task.id"
|
|
1046
|
+
[style.left.px]="b.x"
|
|
1047
|
+
[style.top.px]="b.y"
|
|
1048
|
+
[style.width.px]="b.width"
|
|
1049
|
+
[style.--cb-gantt-color]="b.row.task.color || null"
|
|
1050
|
+
[attr.aria-label]="barLabel(b)"
|
|
1051
|
+
[attr.aria-describedby]="readonly() ? null : uid + '-help'"
|
|
1052
|
+
(pointerdown)="onPointerDown($event, b)"
|
|
1053
|
+
(keydown)="onBarKeydown($event, b.row)"
|
|
1054
|
+
(click)="selected.set(b.row.task.id)"
|
|
1055
|
+
(dblclick)="taskOpen.emit(b.row.task)"
|
|
1056
|
+
>
|
|
1057
|
+
@if (!b.row.isMilestone) {
|
|
1058
|
+
<span class="cb-gantt__progress" [style.width.%]="b.progress" aria-hidden="true"></span>
|
|
1059
|
+
<span class="cb-gantt__bar-label" aria-hidden="true">{{ b.row.task.name }}</span>
|
|
1060
|
+
@if (!readonly() && !b.row.isGroup) {
|
|
1061
|
+
<span class="cb-gantt__handle cb-gantt__handle--start" data-mode="start" aria-hidden="true"></span>
|
|
1062
|
+
<span class="cb-gantt__handle cb-gantt__handle--end" data-mode="end" aria-hidden="true"></span>
|
|
1063
|
+
<span class="cb-gantt__progress-handle" data-mode="progress" [style.left.%]="b.progress" aria-hidden="true"></span>
|
|
1064
|
+
}
|
|
1065
|
+
} @else {
|
|
1066
|
+
<span class="cb-gantt__diamond" aria-hidden="true"></span>
|
|
1067
|
+
<span class="cb-gantt__milestone-label" aria-hidden="true">{{ b.row.task.name }}</span>
|
|
1068
|
+
}
|
|
1069
|
+
@if (!readonly()) {
|
|
1070
|
+
<span class="cb-gantt__connector" data-mode="link" aria-hidden="true"></span>
|
|
1071
|
+
}
|
|
1072
|
+
</div>
|
|
1073
|
+
}
|
|
1074
|
+
</div>
|
|
1075
|
+
</div>
|
|
1076
|
+
</div>
|
|
1077
|
+
@if (!rows().length) {
|
|
1078
|
+
<p class="cb-gantt__empty">{{ emptyMessage() ?? t().noData }}</p>
|
|
1079
|
+
}
|
|
1080
|
+
</div>
|
|
1081
|
+
<p class="cb-gantt__sr" [id]="uid + '-help'">{{ t().ganttInstructions }}</p>
|
|
1082
|
+
<div class="cb-gantt__sr" role="status">{{ announcement() }}</div>
|
|
1083
|
+
`, styles: [":host{--cb-gantt-row: 40px;--cb-gantt-bar: var(--cb-color-primary-500);--cb-gantt-bar-bg: color-mix(in srgb, var(--cb-gantt-bar) 28%, var(--cb-color-surface));display:flex;flex-direction:column;gap:var(--cb-space-2, .5rem);width:100%;min-width:0;color:var(--cb-color-text);font-family:var(--cb-font-sans);font-size:var(--cb-text-sm, .875rem)}.cb-gantt__toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem}.cb-gantt__scales{display:inline-flex;padding:2px;border-radius:var(--cb-radius-md);background:var(--cb-color-surface-hover)}.cb-gantt__scale,.cb-gantt__btn{padding:.3rem .75rem;border:0;border-radius:calc(var(--cb-radius-md) - 2px);background:transparent;color:var(--cb-color-text-muted);font:inherit;font-size:var(--cb-text-xs, .75rem);font-weight:var(--cb-font-weight-medium, 500);cursor:pointer}.cb-gantt__scale[aria-pressed=true]{background:var(--cb-color-surface-raised);color:var(--cb-color-text-strong, var(--cb-color-text));box-shadow:0 1px 2px #0f172a1f}.cb-gantt__btn{border:1px solid var(--cb-color-border-strong);color:var(--cb-color-text)}.cb-gantt__btn:hover{background:var(--cb-color-surface-hover)}:is(.cb-gantt__scale,.cb-gantt__btn,.cb-gantt__toggle,.cb-gantt__name,.cb-gantt__unlink):focus-visible{outline:2px solid var(--cb-color-focus-ring);outline-offset:1px}.cb-gantt__scroller{position:relative;overflow:auto;container-type:inline-size;border:1px solid var(--cb-color-border);border-radius:var(--cb-radius-lg, 12px);background:var(--cb-color-surface)}.cb-gantt__inner{--cb-gantt-grid-w: min(var(--cb-gantt-grid, 30rem), 60cqi);position:relative;width:max-content;min-width:100%}.cb-gantt__head{position:sticky;top:0;z-index:4;display:flex;height:52px;border-bottom:1px solid var(--cb-color-border);background:var(--cb-color-surface-subtle, var(--cb-color-surface))}.cb-gantt__head-grid{position:sticky;left:0;z-index:5;display:flex;flex:none;align-items:flex-end;width:var(--cb-gantt-grid-w);padding-bottom:.45rem;border-right:1px solid var(--cb-color-border);background:inherit}.cb-gantt__head-cell{flex:0 0 auto;box-sizing:border-box;padding:0 .5rem;overflow:hidden;color:var(--cb-color-text-muted);font-size:var(--cb-text-xs, .75rem);font-weight:var(--cb-font-weight-semibold, 600);text-overflow:ellipsis;white-space:nowrap}.cb-gantt__head-cell.cb-gantt__col-name{padding-left:.75rem}.cb-gantt__col-name{flex:1 1 auto!important;min-width:6rem}.cb-gantt__col-start,.cb-gantt__col-end,.cb-gantt__col-duration{width:4.5rem}.cb-gantt__col-progress{width:4rem}.cb-gantt__col-assignee{width:6.5rem}.cb-gantt__head-scale{position:relative;flex:none}.cb-gantt__head-row{position:relative;height:26px}.cb-gantt__unit{position:absolute;top:0;bottom:0;display:flex;align-items:center;justify-content:center;overflow:hidden;border-left:1px solid var(--cb-color-border);color:var(--cb-color-text-muted);font-size:.6875rem;white-space:nowrap}.cb-gantt__unit--top{justify-content:flex-start;padding-left:.5rem;color:var(--cb-color-text);font-weight:var(--cb-font-weight-semibold, 600)}.cb-gantt__unit--weekend{background:var(--cb-color-surface-hover)}@container (max-width: 40rem){.cb-gantt__col-start,.cb-gantt__col-end,.cb-gantt__col-duration,.cb-gantt__col-progress,.cb-gantt__col-assignee{display:none}.cb-gantt__head-grid,.cb-gantt__row{overflow:hidden}}.cb-gantt__body{position:relative;display:flex}.cb-gantt__grid{position:sticky;left:0;z-index:3;flex:none;width:var(--cb-gantt-grid-w);border-right:1px solid var(--cb-color-border);background:var(--cb-color-surface)}.cb-gantt__row{display:flex;align-items:center;height:var(--cb-gantt-row);border-bottom:1px solid var(--cb-color-border)}.cb-gantt__row.cb-is-selected{background:color-mix(in srgb,var(--cb-color-primary-500) 9%,var(--cb-color-surface))}.cb-gantt__cell{flex:0 0 auto;box-sizing:border-box;padding:0 .5rem;overflow:hidden;color:var(--cb-color-text-muted);font-size:var(--cb-text-xs, .75rem);font-variant-numeric:tabular-nums;text-overflow:ellipsis;white-space:nowrap}.cb-gantt__cell.cb-gantt__col-name{display:flex;align-items:center;gap:.25rem;min-width:0;color:var(--cb-color-text);font-size:var(--cb-text-sm, .875rem)}.cb-gantt__toggle,.cb-gantt__toggle-space{display:inline-flex;flex:none;align-items:center;justify-content:center;width:1.4rem;height:1.4rem}.cb-gantt__toggle{padding:0;border:0;border-radius:var(--cb-radius-sm);background:transparent;color:var(--cb-color-text-muted);cursor:pointer}.cb-gantt__toggle:hover{background:var(--cb-color-surface-hover)}.cb-gantt__name{display:inline-flex;align-items:center;gap:.4rem;min-width:0;padding:.15rem .25rem;overflow:hidden;border:0;border-radius:var(--cb-radius-sm);background:transparent;color:inherit;font:inherit;text-align:start;text-overflow:ellipsis;white-space:nowrap;cursor:pointer}.cb-gantt__name--group{font-weight:var(--cb-font-weight-semibold, 600)}.cb-gantt__diamond-mini{flex:none;width:.55rem;height:.55rem;background:var(--cb-color-warning-500, #f59e0b);transform:rotate(45deg)}.cb-gantt__chart{position:relative;flex:none}.cb-gantt__col{position:absolute;top:0;bottom:0;border-left:1px solid color-mix(in srgb,var(--cb-color-border) 60%,transparent)}.cb-gantt__col--weekend{background:color-mix(in srgb,var(--cb-color-surface-hover) 70%,transparent)}.cb-gantt__stripe{position:absolute;left:0;right:0;height:var(--cb-gantt-row);border-bottom:1px solid var(--cb-color-border)}.cb-gantt__stripe.cb-is-selected{background:color-mix(in srgb,var(--cb-color-primary-500) 7%,transparent)}.cb-gantt__today{position:absolute;top:0;bottom:0;z-index:1;width:2px;margin-left:-1px;background:var(--cb-color-danger-500, #ef4444)}.cb-gantt__links{position:absolute;inset:0;z-index:1;overflow:visible;pointer-events:none}.cb-gantt__links marker path{fill:var(--cb-color-text-muted)}.cb-gantt__link{fill:none;stroke:var(--cb-color-text-muted);stroke-width:1.5}.cb-gantt__link.cb-is-selected{stroke:var(--cb-color-primary-600, var(--cb-color-primary-500));stroke-width:2.5}.cb-gantt__link--draft{stroke:var(--cb-color-primary-500);stroke-dasharray:4 3}.cb-gantt__link-hit{fill:none;stroke:transparent;stroke-width:10;pointer-events:stroke;cursor:pointer}:host(.cb-gantt--readonly) .cb-gantt__link-hit{cursor:default}.cb-gantt__unlink{position:absolute;z-index:3;display:inline-flex;align-items:center;justify-content:center;width:1.25rem;height:1.25rem;margin:-.625rem 0 0 -.625rem;padding:0;border:1px solid var(--cb-color-border-strong);border-radius:50%;background:var(--cb-color-surface-raised);color:var(--cb-color-danger-600, var(--cb-color-danger));cursor:pointer}.cb-gantt__bar{position:absolute;z-index:2;box-sizing:border-box;height:24px;margin-top:8px;border-radius:6px;background:color-mix(in srgb,var(--cb-gantt-color, var(--cb-gantt-bar)) 28%,var(--cb-color-surface));box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--cb-gantt-color, var(--cb-gantt-bar)) 55%,transparent);cursor:grab;touch-action:none;-webkit-user-select:none;user-select:none}:host(.cb-gantt--readonly) .cb-gantt__bar{cursor:pointer}.cb-gantt__bar:focus-visible{outline:2px solid var(--cb-color-focus-ring);outline-offset:2px}.cb-gantt__bar.cb-is-selected{box-shadow:inset 0 0 0 2px var(--cb-gantt-color, var(--cb-gantt-bar))}.cb-gantt__bar.cb-is-dragging{cursor:grabbing;opacity:.85}.cb-gantt__progress{position:absolute;inset:0 auto 0 0;border-radius:6px 0 0 6px;background:var(--cb-gantt-color, var(--cb-gantt-bar))}.cb-gantt__bar-label{position:relative;display:block;padding:0 .5rem;overflow:hidden;color:var(--cb-color-text);font-size:var(--cb-text-xs, .75rem);font-weight:var(--cb-font-weight-medium, 500);line-height:24px;text-overflow:ellipsis;white-space:nowrap;mix-blend-mode:normal}.cb-gantt__bar--short .cb-gantt__bar-label{position:absolute;top:0;left:calc(100% + 18px);padding:0;overflow:visible}.cb-gantt__handle{position:absolute;top:0;bottom:0;width:8px;cursor:ew-resize}.cb-gantt__handle--start{left:0}.cb-gantt__handle--end{right:0}.cb-gantt__progress-handle{position:absolute;bottom:-6px;width:10px;height:10px;margin-left:-5px;border:2px solid var(--cb-color-surface);border-radius:50%;background:var(--cb-gantt-color, var(--cb-gantt-bar));cursor:col-resize;opacity:0}.cb-gantt__connector{position:absolute;top:50%;right:-14px;width:10px;height:10px;margin-top:-5px;border:2px solid var(--cb-gantt-color, var(--cb-gantt-bar));border-radius:50%;background:var(--cb-color-surface);cursor:crosshair;opacity:0}.cb-gantt__bar:hover :is(.cb-gantt__progress-handle,.cb-gantt__connector),.cb-gantt__bar.cb-is-selected :is(.cb-gantt__progress-handle,.cb-gantt__connector){opacity:1}@media(hover:none){.cb-gantt__progress-handle,.cb-gantt__connector{opacity:1}}.cb-gantt__bar--group{height:12px;margin-top:14px;border-radius:2px;background:var(--cb-color-text-muted);box-shadow:none}.cb-gantt__bar--group .cb-gantt__progress{border-radius:2px 0 0 2px;background:var(--cb-color-text)}.cb-gantt__bar--group .cb-gantt__bar-label{position:absolute;bottom:12px;left:0;padding:0;line-height:1.2}.cb-gantt__bar--milestone{width:18px;height:18px;margin-top:11px;background:transparent;box-shadow:none}.cb-gantt__bar--milestone.cb-is-selected{box-shadow:none}.cb-gantt__diamond{position:absolute;inset:2px;border-radius:3px;background:var(--cb-gantt-color, var(--cb-color-warning-500, #f59e0b));transform:rotate(45deg)}.cb-gantt__bar--milestone.cb-is-selected .cb-gantt__diamond{outline:2px solid var(--cb-color-text);outline-offset:1px}.cb-gantt__milestone-label{position:absolute;top:0;left:26px;color:var(--cb-color-text);font-size:var(--cb-text-xs, .75rem);font-weight:var(--cb-font-weight-medium, 500);line-height:18px;white-space:nowrap}.cb-gantt__bar--milestone .cb-gantt__connector{right:-16px}.cb-gantt__empty{margin:0;padding:2rem;color:var(--cb-color-text-muted);text-align:center}.cb-gantt__sr{position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap}@media(prefers-reduced-motion:no-preference){.cb-gantt__bar:not(.cb-is-dragging){transition:left .12s ease,width .12s ease}}@media(forced-colors:active){.cb-gantt__bar,.cb-gantt__progress{forced-color-adjust:none;border:1px solid CanvasText}}\n"] }]
|
|
1084
|
+
}], ctorParameters: () => [], propDecorators: { tasks: [{ type: i0.Input, args: [{ isSignal: true, alias: "tasks", required: false }] }, { type: i0.Output, args: ["tasksChange"] }], scale: [{ type: i0.Input, args: [{ isSignal: true, alias: "scale", required: false }] }, { type: i0.Output, args: ["scaleChange"] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], gridWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "gridWidth", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], autoSchedule: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoSchedule", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], showToolbar: [{ type: i0.Input, args: [{ isSignal: true, alias: "showToolbar", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], taskChange: [{ type: i0.Output, args: ["taskChange"] }], taskOpen: [{ type: i0.Output, args: ["taskOpen"] }], linkAdd: [{ type: i0.Output, args: ["linkAdd"] }], linkRemove: [{ type: i0.Output, args: ["linkRemove"] }], scroller: [{ type: i0.ViewChild, args: ['scroller', { isSignal: true }] }], chart: [{ type: i0.ViewChild, args: ['chart', { isSignal: true }] }] } });
|
|
1085
|
+
|
|
1086
|
+
/*
|
|
1087
|
+
* Public API Surface de @caribeng/ui/gantt
|
|
1088
|
+
*/
|
|
1089
|
+
|
|
1090
|
+
/**
|
|
1091
|
+
* Generated bundle index. Do not edit.
|
|
1092
|
+
*/
|
|
1093
|
+
|
|
1094
|
+
export { CbGantt, autoSchedule as cbGanttAutoSchedule, canLink as cbGanttCanLink };
|