diff --git a/src/components/BootstrapBlazor.DockView/BootstrapBlazor.DockView.csproj b/src/components/BootstrapBlazor.DockView/BootstrapBlazor.DockView.csproj index 73fc1498..b96d77b1 100644 --- a/src/components/BootstrapBlazor.DockView/BootstrapBlazor.DockView.csproj +++ b/src/components/BootstrapBlazor.DockView/BootstrapBlazor.DockView.csproj @@ -1,7 +1,7 @@ - 9.1.2 + 9.1.3 diff --git a/src/components/BootstrapBlazor.DockView/wwwroot/css/dockview-bb.css b/src/components/BootstrapBlazor.DockView/wwwroot/css/dockview-bb.css index 200b8ed4..89809a8e 100644 --- a/src/components/BootstrapBlazor.DockView/wwwroot/css/dockview-bb.css +++ b/src/components/BootstrapBlazor.DockView/wwwroot/css/dockview-bb.css @@ -277,3 +277,10 @@ .bb-dockview .dv-resize-container-drawer { height: calc(100% + 1px) !important; } + +.bb-dockview .dv-tabs-and-actions-container:has(.dropdown-item) .dv-scrollbar-horizontal { + display: none; +} +.bb-dockview .dv-scrollable { + width: initial; +} diff --git a/src/components/BootstrapBlazor.DockView/wwwroot/css/dockview.css b/src/components/BootstrapBlazor.DockView/wwwroot/css/dockview.css index caa1c24e..3821c55c 100644 --- a/src/components/BootstrapBlazor.DockView/wwwroot/css/dockview.css +++ b/src/components/BootstrapBlazor.DockView/wwwroot/css/dockview.css @@ -1,3 +1,23 @@ +.dv-scrollable { + position: relative; + overflow: hidden; + width: 100%; +} +.dv-scrollable .dv-scrollbar-horizontal { + position: absolute; + bottom: 0px; + left: 0px; + height: 4px; + border-radius: 2px; + background-color: transparent; + transition-property: background-color; + transition-timing-function: ease-in-out; + transition-duration: 1s; + transition-delay: 0s; +} +.dv-scrollable:hover .dv-scrollbar-horizontal, .dv-scrollable.dv-scrollable-resizing .dv-scrollbar-horizontal, .dv-scrollable.dv-scrollable-scrolling .dv-scrollbar-horizontal { + background-color: var(--dv-scrollbar-background-color, rgba(255, 255, 255, 0.25)); +} .dv-svg { display: inline-block; fill: currentcolor; @@ -10,11 +30,16 @@ --dv-tabs-and-actions-container-font-size: 13px; --dv-tabs-and-actions-container-height: 35px; --dv-drag-over-background-color: rgba(83, 89, 93, 0.5); - --dv-drag-over-border-color: white; + --dv-drag-over-border-color: transparent; --dv-tabs-container-scrollbar-color: #888; --dv-icon-hover-background-color: rgba(90, 93, 94, 0.31); --dv-floating-box-shadow: 8px 8px 8px 0px rgba(83, 89, 93, 0.5); --dv-overlay-z-index: 999; + --dv-tab-font-size: inherit; + --dv-border-radius: 0px; + --dv-tab-margin: 0; + --dv-sash-color: transparent; + --dv-active-sash-color: transparent; --dv-active-sash-transition-duration: 0.1s; --dv-active-sash-transition-delay: 0.5s; --dv-group-view-background-color: #1e1e1e; @@ -31,17 +56,26 @@ --dv-separator-border: rgb(68, 68, 68); --dv-paneview-header-border-color: rgba(204, 204, 204, 0.2); } +.dockview-theme-dark .dv-drop-target-container .dv-drop-target-anchor.dv-drop-target-anchor-container-changed { + opacity: 0; + transition: none; +} .dockview-theme-light { --dv-paneview-active-outline-color: dodgerblue; --dv-tabs-and-actions-container-font-size: 13px; --dv-tabs-and-actions-container-height: 35px; --dv-drag-over-background-color: rgba(83, 89, 93, 0.5); - --dv-drag-over-border-color: white; + --dv-drag-over-border-color: transparent; --dv-tabs-container-scrollbar-color: #888; --dv-icon-hover-background-color: rgba(90, 93, 94, 0.31); --dv-floating-box-shadow: 8px 8px 8px 0px rgba(83, 89, 93, 0.5); --dv-overlay-z-index: 999; + --dv-tab-font-size: inherit; + --dv-border-radius: 0px; + --dv-tab-margin: 0; + --dv-sash-color: transparent; + --dv-active-sash-color: transparent; --dv-active-sash-transition-duration: 0.1s; --dv-active-sash-transition-delay: 0.5s; --dv-group-view-background-color: white; @@ -57,6 +91,11 @@ --dv-inactivegroup-hiddenpanel-tab-color: rgba(51, 51, 51, 0.35); --dv-separator-border: rgba(128, 128, 128, 0.35); --dv-paneview-header-border-color: rgb(51, 51, 51); + --dv-scrollbar-background-color: rgba(0, 0, 0, 0.25); +} +.dockview-theme-light .dv-drop-target-container .dv-drop-target-anchor.dv-drop-target-anchor-container-changed { + opacity: 0; + transition: none; } .dockview-theme-vs { @@ -64,11 +103,16 @@ --dv-tabs-and-actions-container-font-size: 13px; --dv-tabs-and-actions-container-height: 35px; --dv-drag-over-background-color: rgba(83, 89, 93, 0.5); - --dv-drag-over-border-color: white; + --dv-drag-over-border-color: transparent; --dv-tabs-container-scrollbar-color: #888; --dv-icon-hover-background-color: rgba(90, 93, 94, 0.31); --dv-floating-box-shadow: 8px 8px 8px 0px rgba(83, 89, 93, 0.5); --dv-overlay-z-index: 999; + --dv-tab-font-size: inherit; + --dv-border-radius: 0px; + --dv-tab-margin: 0; + --dv-sash-color: transparent; + --dv-active-sash-color: transparent; --dv-active-sash-transition-duration: 0.1s; --dv-active-sash-transition-delay: 0.5s; --dv-group-view-background-color: #1e1e1e; @@ -94,6 +138,10 @@ --dv-inactivegroup-visiblepanel-tab-color: white; --dv-inactivegroup-hiddenpanel-tab-color: white; } +.dockview-theme-vs .dv-drop-target-container .dv-drop-target-anchor.dv-drop-target-anchor-container-changed { + opacity: 0; + transition: none; +} .dockview-theme-vs .dv-groupview.dv-active-group > .dv-tabs-and-actions-container { box-sizing: content-box; border-bottom: 2px solid var(--dv-activegroup-visiblepanel-tab-background-color); @@ -120,39 +168,66 @@ --dv-tabs-and-actions-container-font-size: 13px; --dv-tabs-and-actions-container-height: 35px; --dv-drag-over-background-color: rgba(83, 89, 93, 0.5); - --dv-drag-over-border-color: white; + --dv-drag-over-border-color: transparent; --dv-tabs-container-scrollbar-color: #888; --dv-icon-hover-background-color: rgba(90, 93, 94, 0.31); --dv-floating-box-shadow: 8px 8px 8px 0px rgba(83, 89, 93, 0.5); --dv-overlay-z-index: 999; + --dv-tab-font-size: inherit; + --dv-border-radius: 0px; + --dv-tab-margin: 0; + --dv-sash-color: transparent; + --dv-active-sash-color: transparent; --dv-active-sash-transition-duration: 0.1s; --dv-active-sash-transition-delay: 0.5s; - --dv-group-view-background-color: #000c18; - --dv-tabs-and-actions-container-background-color: #1c1c2a; - --dv-activegroup-visiblepanel-tab-background-color: #000c18; - --dv-activegroup-hiddenpanel-tab-background-color: #10192c; - --dv-inactivegroup-visiblepanel-tab-background-color: #000c18; - --dv-inactivegroup-hiddenpanel-tab-background-color: #10192c; - --dv-tab-divider-color: #2b2b4a; + --dv-color-abyss-dark: #000c18; + --dv-color-abyss: #10192c; + --dv-color-abyss-light: #1c1c2a; + --dv-color-abyss-lighter: #2b2b4a; + --dv-color-abyss-accent: rgb(91, 30, 207); + --dv-color-abyss-primary-text: white; + --dv-color-abyss-secondary-text: rgb(148, 151, 169); + --dv-group-view-background-color: var(--dv-color-abyss-dark); + --dv-tabs-and-actions-container-background-color: var( + --dv-color-abyss-light + ); + --dv-activegroup-visiblepanel-tab-background-color: var( + --dv-color-abyss-dark + ); + --dv-activegroup-hiddenpanel-tab-background-color: var(--dv-color-abyss); + --dv-inactivegroup-visiblepanel-tab-background-color: var( + --dv-color-abyss-dark + ); + --dv-inactivegroup-hiddenpanel-tab-background-color: var(--dv-color-abyss); + --dv-tab-divider-color: var(--dv-color-abyss-lighter); --dv-activegroup-visiblepanel-tab-color: white; --dv-activegroup-hiddenpanel-tab-color: rgba(255, 255, 255, 0.5); --dv-inactivegroup-visiblepanel-tab-color: rgba(255, 255, 255, 0.5); --dv-inactivegroup-hiddenpanel-tab-color: rgba(255, 255, 255, 0.25); - --dv-separator-border: #2b2b4a; - --dv-paneview-header-border-color: #2b2b4a; + --dv-separator-border: var(--dv-color-abyss-lighter); + --dv-paneview-header-border-color: var(--dv-color-abyss-lighter); --dv-paneview-active-outline-color: #596f99; } +.dockview-theme-abyss .dv-drop-target-container .dv-drop-target-anchor.dv-drop-target-anchor-container-changed { + opacity: 0; + transition: none; +} .dockview-theme-dracula { --dv-paneview-active-outline-color: dodgerblue; --dv-tabs-and-actions-container-font-size: 13px; --dv-tabs-and-actions-container-height: 35px; --dv-drag-over-background-color: rgba(83, 89, 93, 0.5); - --dv-drag-over-border-color: white; + --dv-drag-over-border-color: transparent; --dv-tabs-container-scrollbar-color: #888; --dv-icon-hover-background-color: rgba(90, 93, 94, 0.31); --dv-floating-box-shadow: 8px 8px 8px 0px rgba(83, 89, 93, 0.5); --dv-overlay-z-index: 999; + --dv-tab-font-size: inherit; + --dv-border-radius: 0px; + --dv-tab-margin: 0; + --dv-sash-color: transparent; + --dv-active-sash-color: transparent; --dv-active-sash-transition-duration: 0.1s; --dv-active-sash-transition-delay: 0.5s; --dv-group-view-background-color: #282a36; @@ -170,10 +245,14 @@ --dv-paneview-header-border-color: #bd93f9; --dv-paneview-active-outline-color: #6272a4; } -.dockview-theme-dracula .dv-groupview.dv-active-group > .dv-tabs-and-actions-container > .dv-tabs-container > .dv-tab.dv-active-tab { +.dockview-theme-dracula .dv-drop-target-container .dv-drop-target-anchor.dv-drop-target-anchor-container-changed { + opacity: 0; + transition: none; +} +.dockview-theme-dracula .dv-groupview.dv-active-group > .dv-tabs-and-actions-container > .dv-scrollable > .dv-tabs-container > .dv-tab.dv-active-tab { position: relative; } -.dockview-theme-dracula .dv-groupview.dv-active-group > .dv-tabs-and-actions-container > .dv-tabs-container > .dv-tab.dv-active-tab::after { +.dockview-theme-dracula .dv-groupview.dv-active-group > .dv-tabs-and-actions-container > .dv-scrollable > .dv-tabs-container > .dv-tab.dv-active-tab::after { position: absolute; left: 0px; top: 0px; @@ -183,10 +262,10 @@ background-color: #94527e; z-index: 999; } -.dockview-theme-dracula .dv-groupview.dv-inactive-group > .dv-tabs-and-actions-container > .dv-tabs-container > .dv-tab.dv-active-tab { +.dockview-theme-dracula .dv-groupview.dv-inactive-group > .dv-tabs-and-actions-container > .dv-scrollable > .dv-tabs-container > .dv-tab.dv-active-tab { position: relative; } -.dockview-theme-dracula .dv-groupview.dv-inactive-group > .dv-tabs-and-actions-container > .dv-tabs-container > .dv-tab.dv-active-tab::after { +.dockview-theme-dracula .dv-groupview.dv-inactive-group > .dv-tabs-and-actions-container > .dv-scrollable > .dv-tabs-container > .dv-tab.dv-active-tab::after { position: absolute; left: 0px; bottom: 0px; @@ -202,13 +281,20 @@ --dv-tabs-and-actions-container-font-size: 13px; --dv-tabs-and-actions-container-height: 35px; --dv-drag-over-background-color: rgba(83, 89, 93, 0.5); - --dv-drag-over-border-color: white; + --dv-drag-over-border-color: transparent; --dv-tabs-container-scrollbar-color: #888; --dv-icon-hover-background-color: rgba(90, 93, 94, 0.31); --dv-floating-box-shadow: 8px 8px 8px 0px rgba(83, 89, 93, 0.5); --dv-overlay-z-index: 999; + --dv-tab-font-size: inherit; + --dv-border-radius: 0px; + --dv-tab-margin: 0; + --dv-sash-color: transparent; + --dv-active-sash-color: transparent; --dv-active-sash-transition-duration: 0.1s; --dv-active-sash-transition-delay: 0.5s; + padding: 10px; + background-color: #ebeced; --dv-group-view-background-color: #ebeced; --dv-tabs-and-actions-container-background-color: #fcfcfc; --dv-activegroup-visiblepanel-tab-background-color: #f0f1f2; @@ -222,12 +308,20 @@ --dv-inactivegroup-hiddenpanel-tab-color: rgb(51, 51, 51); --dv-separator-border: transparent; --dv-paneview-header-border-color: rgb(51, 51, 51); - --dv-separator-handle-background-color: #cfd1d3; - --dv-separator-handle-hover-background-color: #babbbb; + --dv-sash-color: #cfd1d3; + --dv-active-sash-color: #babbbb; +} +.dockview-theme-replit .dv-drop-target-container .dv-drop-target-anchor.dv-drop-target-anchor-container-changed { + opacity: 0; + transition: none; } .dockview-theme-replit .dv-resize-container:has(> .dv-groupview) { border-radius: 8px; } +.dockview-theme-replit .dv-resize-container { + border-radius: 10px !important; + border: none; +} .dockview-theme-replit .dv-groupview { overflow: hidden; border-radius: 10px; @@ -255,6 +349,9 @@ .dockview-theme-replit .dv-groupview.dv-inactive-group { border: 1px solid transparent; } +.dockview-theme-replit .dv-vertical > .dv-sash-container > .dv-sash { + background-color: transparent; +} .dockview-theme-replit .dv-vertical > .dv-sash-container > .dv-sash:not(.disabled)::after { content: ""; height: 4px; @@ -263,11 +360,17 @@ top: 50%; left: 50%; transform: translate(-50%, -50%); - background-color: var(--dv-separator-handle-background-color); + background-color: var(--dv-sash-color); position: absolute; } -.dockview-theme-replit .dv-vertical > .dv-sash-container > .dv-sash:not(.disabled):hover::after { - background-color: var(--dv-separator-handle-hover-background-color); +.dockview-theme-replit .dv-vertical > .dv-sash-container > .dv-sash:not(.disabled):hover, .dockview-theme-replit .dv-vertical > .dv-sash-container > .dv-sash:not(.disabled):active { + background-color: transparent; +} +.dockview-theme-replit .dv-vertical > .dv-sash-container > .dv-sash:not(.disabled):hover::after, .dockview-theme-replit .dv-vertical > .dv-sash-container > .dv-sash:not(.disabled):active::after { + background-color: var(--dv-active-sash-color); +} +.dockview-theme-replit .dv-horizontal > .dv-sash-container > .dv-sash { + background-color: transparent; } .dockview-theme-replit .dv-horizontal > .dv-sash-container > .dv-sash:not(.disabled)::after { content: ""; @@ -277,14 +380,206 @@ top: 50%; left: 50%; transform: translate(-50%, -50%); - background-color: var(--dv-separator-handle-background-color); + background-color: var(--dv-sash-color); position: absolute; } -.dockview-theme-replit .dv-horizontal > .dv-sash-container > .dv-sash:not(.disabled):hover::after { - background-color: var(--dv-separator-handle-hover-background-color); +.dockview-theme-replit .dv-horizontal > .dv-sash-container > .dv-sash:not(.disabled):hover, .dockview-theme-replit .dv-horizontal > .dv-sash-container > .dv-sash:not(.disabled):active { + background-color: transparent; +} +.dockview-theme-replit .dv-horizontal > .dv-sash-container > .dv-sash:not(.disabled):hover::after, .dockview-theme-replit .dv-horizontal > .dv-sash-container > .dv-sash:not(.disabled):active::after { + background-color: var(--dv-active-sash-color); +} + +.dockview-theme-abyss-spaced { + --dv-paneview-active-outline-color: dodgerblue; + --dv-tabs-and-actions-container-font-size: 13px; + --dv-tabs-and-actions-container-height: 35px; + --dv-drag-over-background-color: rgba(83, 89, 93, 0.5); + --dv-drag-over-border-color: transparent; + --dv-tabs-container-scrollbar-color: #888; + --dv-icon-hover-background-color: rgba(90, 93, 94, 0.31); + --dv-floating-box-shadow: 8px 8px 8px 0px rgba(83, 89, 93, 0.5); + --dv-overlay-z-index: 999; + --dv-tab-font-size: inherit; + --dv-border-radius: 0px; + --dv-tab-margin: 0; + --dv-sash-color: transparent; + --dv-active-sash-color: transparent; + --dv-active-sash-transition-duration: 0.1s; + --dv-active-sash-transition-delay: 0.5s; + --dv-tab-font-size: 12px; + --dv-border-radius: 20px; + --dv-tab-margin: 0.5rem 0.25rem; + --dv-tabs-and-actions-container-height: 44px; + --dv-border-radius: 20px; + --dv-color-abyss-dark: rgb(11, 6, 17); + --dv-color-abyss: #16121f; + --dv-color-abyss-light: #201d2b; + --dv-color-abyss-lighter: #2a2837; + --dv-color-abyss-accent: rgb(91, 30, 207); + --dv-color-abyss-primary-text: white; + --dv-color-abyss-secondary-text: rgb(148, 151, 169); + --dv-drag-over-border: 2px solid var(--dv-color-abyss-accent); + --dv-drag-over-background-color: ""; + --dv-group-view-background-color: var(--dv-color-abyss-dark); + --dv-tabs-and-actions-container-background-color: var(--dv-color-abyss); + --dv-activegroup-visiblepanel-tab-background-color: var( + --dv-color-abyss-lighter + ); + --dv-activegroup-hiddenpanel-tab-background-color: var( + --dv-color-abyss-light + ); + --dv-inactivegroup-visiblepanel-tab-background-color: var( + --dv-color-abyss-lighter + ); + --dv-inactivegroup-hiddenpanel-tab-background-color: var( + --dv-color-abyss-light + ); + --dv-tab-divider-color: transparent; + --dv-activegroup-visiblepanel-tab-color: var(--dv-color-abyss-primary-text); + --dv-activegroup-hiddenpanel-tab-color: var( + --dv-color-abyss-secondary-text + ); + --dv-inactivegroup-visiblepanel-tab-color: var( + --dv-color-abyss-primary-text + ); + --dv-inactivegroup-hiddenpanel-tab-color: var( + --dv-color-abyss-secondary-text + ); + --dv-separator-border: transparent; + --dv-paneview-header-border-color: rgb(51, 51, 51); + --dv-active-sash-color: var(--dv-color-abyss-accent); + --dv-floating-box-shadow: 8px 8px 8px 0px rgba(0, 0, 0, 0.5); + padding: 10px; + background-color: var(--dv-color-abyss-dark); +} +.dockview-theme-abyss-spaced .dv-resize-container:has(> .dv-groupview) { + border-radius: 8px; +} +.dockview-theme-abyss-spaced .dv-sash { + border-radius: 4px; +} +.dockview-theme-abyss-spaced .dv-drop-target-anchor { + border-radius: calc(var(--dv-border-radius) / 4); +} +.dockview-theme-abyss-spaced .dv-drop-target-anchor.dv-drop-target-content { + border-radius: var(--dv-border-radius); +} +.dockview-theme-abyss-spaced .dv-resize-container { + border-radius: var(--dv-border-radius) !important; + border: none; +} +.dockview-theme-abyss-spaced .dv-tabs-overflow-container, +.dockview-theme-abyss-spaced .dv-tabs-overflow-dropdown-default { + border-radius: 8px; + height: unset !important; +} +.dockview-theme-abyss-spaced .dv-tab { + border-radius: 8px; +} +.dockview-theme-abyss-spaced .dv-tab .dv-svg { + height: 8px; + width: 8px; +} +.dockview-theme-abyss-spaced .dv-groupview { + border-radius: var(--dv-border-radius); +} +.dockview-theme-abyss-spaced .dv-groupview .dv-tabs-and-actions-container { + padding: 0px calc(var(--dv-border-radius) / 2); +} +.dockview-theme-abyss-spaced .dv-groupview .dv-content-container { + background-color: var(--dv-tabs-and-actions-container-background-color); +} +.dockview-theme-abyss-spaced .dv-resize-container .dv-groupview { + border: 2px solid var(--dv-color-abyss-dark); +} + +.dockview-theme-light-spaced { + --dv-paneview-active-outline-color: dodgerblue; + --dv-tabs-and-actions-container-font-size: 13px; + --dv-tabs-and-actions-container-height: 35px; + --dv-drag-over-background-color: rgba(83, 89, 93, 0.5); + --dv-drag-over-border-color: transparent; + --dv-tabs-container-scrollbar-color: #888; + --dv-icon-hover-background-color: rgba(90, 93, 94, 0.31); + --dv-floating-box-shadow: 8px 8px 8px 0px rgba(83, 89, 93, 0.5); + --dv-overlay-z-index: 999; + --dv-tab-font-size: inherit; + --dv-border-radius: 0px; + --dv-tab-margin: 0; + --dv-sash-color: transparent; + --dv-active-sash-color: transparent; + --dv-active-sash-transition-duration: 0.1s; + --dv-active-sash-transition-delay: 0.5s; + --dv-tab-font-size: 12px; + --dv-border-radius: 20px; + --dv-tab-margin: 0.5rem 0.25rem; + --dv-tabs-and-actions-container-height: 44px; + --dv-border-radius: 20px; + --dv-drag-over-border: 2px solid rgb(91, 30, 207); + --dv-drag-over-background-color: ""; + --dv-group-view-background-color: #f6f5f9; + --dv-tabs-and-actions-container-background-color: white; + --dv-activegroup-visiblepanel-tab-background-color: #ededf0; + --dv-activegroup-hiddenpanel-tab-background-color: #f9f9fa; + --dv-inactivegroup-visiblepanel-tab-background-color: #ededf0; + --dv-inactivegroup-hiddenpanel-tab-background-color: #f9f9fa; + --dv-tab-divider-color: transparent; + --dv-activegroup-visiblepanel-tab-color: rgb(104, 107, 130); + --dv-activegroup-hiddenpanel-tab-color: rgb(148, 151, 169); + --dv-inactivegroup-visiblepanel-tab-color: rgb(104, 107, 130); + --dv-inactivegroup-hiddenpanel-tab-color: rgb(148, 151, 169); + --dv-separator-border: transparent; + --dv-paneview-header-border-color: rgb(51, 51, 51); + --dv-active-sash-color: rgb(91, 30, 207); + --dv-floating-box-shadow: 8px 8px 8px 0px rgba(0, 0, 0, 0.1); + padding: 10px; + background-color: #f6f5f9; + --dv-scrollbar-background-color: rgba(0, 0, 0, 0.25); +} +.dockview-theme-light-spaced .dv-resize-container:has(> .dv-groupview) { + border-radius: 8px; +} +.dockview-theme-light-spaced .dv-sash { + border-radius: 4px; +} +.dockview-theme-light-spaced .dv-drop-target-anchor { + border-radius: calc(var(--dv-border-radius) / 4); +} +.dockview-theme-light-spaced .dv-drop-target-anchor.dv-drop-target-content { + border-radius: var(--dv-border-radius); +} +.dockview-theme-light-spaced .dv-resize-container { + border-radius: var(--dv-border-radius) !important; + border: none; +} +.dockview-theme-light-spaced .dv-tabs-overflow-container, +.dockview-theme-light-spaced .dv-tabs-overflow-dropdown-default { + border-radius: 8px; + height: unset !important; +} +.dockview-theme-light-spaced .dv-tab { + border-radius: 8px; +} +.dockview-theme-light-spaced .dv-tab .dv-svg { + height: 8px; + width: 8px; +} +.dockview-theme-light-spaced .dv-groupview { + border-radius: var(--dv-border-radius); +} +.dockview-theme-light-spaced .dv-groupview .dv-tabs-and-actions-container { + padding: 0px calc(var(--dv-border-radius) / 2); +} +.dockview-theme-light-spaced .dv-groupview .dv-content-container { + background-color: var(--dv-tabs-and-actions-container-background-color); +} +.dockview-theme-light-spaced .dv-resize-container .dv-groupview { + border: 2px solid rgba(255, 255, 255, 0.1); } .dv-drop-target { position: relative; + --dv-transition-duration: 70ms; } .dv-drop-target > .dv-drop-target-dropzone { position: absolute; @@ -300,8 +595,9 @@ box-sizing: border-box; height: 100%; width: 100%; + border: var(--dv-drag-over-border); background-color: var(--dv-drag-over-background-color); - transition: top 70ms ease-out, left 70ms ease-out, width 70ms ease-out, height 70ms ease-out, opacity 0.15s ease-out; + transition: top var(--dv-transition-duration) ease-out, left var(--dv-transition-duration) ease-out, width var(--dv-transition-duration) ease-out, height var(--dv-transition-duration) ease-out, opacity var(--dv-transition-duration) ease-out; will-change: transform; pointer-events: none; } @@ -317,6 +613,24 @@ .dv-drop-target > .dv-drop-target-dropzone > .dv-drop-target-selection.dv-drop-target-right.dv-drop-target-small-horizontal { border-right: 1px solid var(--dv-drag-over-border-color); } +.dv-drop-target-container { + position: absolute; + z-index: 9999; + top: 0px; + left: 0px; + height: 100%; + width: 100%; + pointer-events: none; + overflow: hidden; + --dv-transition-duration: 300ms; +} +.dv-drop-target-container .dv-drop-target-anchor { + position: relative; + border: var(--dv-drag-over-border); + transition: opacity var(--dv-transition-duration) ease-in, top var(--dv-transition-duration) ease-out, left var(--dv-transition-duration) ease-out, width var(--dv-transition-duration) ease-out, height var(--dv-transition-duration) ease-out; + background-color: var(--dv-drag-over-background-color); + opacity: 1; +} .dv-dockview { position: relative; background-color: var(--dv-group-view-background-color); @@ -333,19 +647,19 @@ position: relative; } -.dv-groupview.dv-active-group > .dv-tabs-and-actions-container > .dv-tabs-container > .dv-tab.dv-active-tab { +.dv-groupview.dv-active-group > .dv-tabs-and-actions-container > .dv-scrollable > .dv-tabs-container > .dv-tab.dv-active-tab { background-color: var(--dv-activegroup-visiblepanel-tab-background-color); color: var(--dv-activegroup-visiblepanel-tab-color); } -.dv-groupview.dv-active-group > .dv-tabs-and-actions-container > .dv-tabs-container > .dv-tab.dv-inactive-tab { +.dv-groupview.dv-active-group > .dv-tabs-and-actions-container > .dv-scrollable > .dv-tabs-container > .dv-tab.dv-inactive-tab { background-color: var(--dv-activegroup-hiddenpanel-tab-background-color); color: var(--dv-activegroup-hiddenpanel-tab-color); } -.dv-groupview.dv-inactive-group > .dv-tabs-and-actions-container > .dv-tabs-container > .dv-tab.dv-active-tab { +.dv-groupview.dv-inactive-group > .dv-tabs-and-actions-container > .dv-scrollable > .dv-tabs-container > .dv-tab.dv-active-tab { background-color: var(--dv-inactivegroup-visiblepanel-tab-background-color); color: var(--dv-inactivegroup-visiblepanel-tab-color); } -.dv-groupview.dv-inactive-group > .dv-tabs-and-actions-container > .dv-tabs-container > .dv-tab.dv-inactive-tab { +.dv-groupview.dv-inactive-group > .dv-tabs-and-actions-container > .dv-scrollable > .dv-tabs-container > .dv-tab.dv-inactive-tab { background-color: var(--dv-inactivegroup-hiddenpanel-tab-background-color); color: var(--dv-inactivegroup-hiddenpanel-tab-color); } @@ -681,6 +995,7 @@ -moz-user-select: none; -ms-user-select: none; touch-action: none; + background-color: var(--dv-sash-color, transparent); } .dv-split-view-container .dv-sash-container .dv-sash:not(.disabled):active, .dv-split-view-container .dv-sash-container .dv-sash:not(.disabled):hover { background-color: var(--dv-active-sash-color, transparent); @@ -747,15 +1062,13 @@ position: relative; height: 100%; display: flex; - min-width: 80px; align-items: center; - padding: 0px 8px; white-space: nowrap; text-overflow: ellipsis; } .dv-tab .dv-default-tab .dv-default-tab-content { - padding: 0px 8px; flex-grow: 1; + margin-right: 4px; } .dv-tab .dv-default-tab .dv-default-tab-action { padding: 4px; @@ -768,6 +1081,79 @@ border-radius: 2px; background-color: var(--dv-icon-hover-background-color); } +.dv-tabs-overflow-dropdown-default { + height: 100%; + color: var(--dv-activegroup-hiddenpanel-tab-color); + margin: var(--dv-tab-margin); + display: flex; + align-items: center; + flex-shrink: 0; + padding: 0.25rem 0.5rem; + cursor: pointer; +} +.dv-tabs-overflow-dropdown-default > span { + padding-left: 0.25rem; +} +.dv-tabs-overflow-dropdown-default > svg { + transform: rotate(90deg); +} +.dv-tabs-container { + display: flex; + height: 100%; + overflow: hidden; + scrollbar-width: thin; + /* Track */ + /* Handle */ +} +.dv-tabs-container.dv-horizontal .dv-tab:not(:first-child)::before { + content: " "; + position: absolute; + top: 0; + left: 0; + z-index: 5; + pointer-events: none; + background-color: var(--dv-tab-divider-color); + width: 1px; + height: 100%; +} +.dv-tabs-container::-webkit-scrollbar { + height: 3px; +} +.dv-tabs-container::-webkit-scrollbar-track { + background: transparent; +} +.dv-tabs-container::-webkit-scrollbar-thumb { + background: var(--dv-tabs-container-scrollbar-color); +} + +.dv-tab { + -webkit-user-drag: element; + outline: none; + padding: 0.25rem 0.5rem; + cursor: pointer; + position: relative; + box-sizing: border-box; + font-size: var(--dv-tab-font-size); + margin: var(--dv-tab-margin); +} + +.dv-tabs-overflow-container { + flex-direction: column; + height: unset; + border: 1px solid var(--dv-tab-divider-color); + background-color: var(--dv-group-view-background-color); +} +.dv-tabs-overflow-container .dv-tab:not(:last-child) { + border-bottom: 1px solid var(--dv-tab-divider-color); +} +.dv-tabs-overflow-container .dv-active-tab { + background-color: var(--dv-activegroup-visiblepanel-tab-background-color); + color: var(--dv-activegroup-visiblepanel-tab-color); +} +.dv-tabs-overflow-container .dv-inactive-tab { + background-color: var(--dv-activegroup-hiddenpanel-tab-background-color); + color: var(--dv-activegroup-hiddenpanel-tab-color); +} .dv-tabs-and-actions-container { display: flex; background-color: var(--dv-tabs-and-actions-container-background-color); @@ -781,6 +1167,7 @@ } .dv-tabs-and-actions-container.dv-single-tab.dv-full-width-single-tab .dv-tabs-container .dv-tab { flex-grow: 1; + padding: 0px; } .dv-tabs-and-actions-container.dv-single-tab.dv-full-width-single-tab .dv-void-container { flex-grow: 0; @@ -790,41 +1177,8 @@ flex-grow: 1; cursor: grab; } -.dv-tabs-and-actions-container .dv-tabs-container { +.dv-tabs-and-actions-container .dv-right-actions-container { display: flex; - overflow-x: overlay; - overflow-y: hidden; - scrollbar-width: thin; - /* Track */ - /* Handle */ -} -.dv-tabs-and-actions-container .dv-tabs-container::-webkit-scrollbar { - height: 3px; -} -.dv-tabs-and-actions-container .dv-tabs-container::-webkit-scrollbar-track { - background: transparent; -} -.dv-tabs-and-actions-container .dv-tabs-container::-webkit-scrollbar-thumb { - background: var(--dv-tabs-container-scrollbar-color); -} -.dv-tabs-and-actions-container .dv-tabs-container .dv-tab { - -webkit-user-drag: element; - outline: none; - min-width: 75px; - cursor: pointer; - position: relative; - box-sizing: border-box; -} -.dv-tabs-and-actions-container .dv-tabs-container .dv-tab:not(:first-child)::before { - content: " "; - position: absolute; - top: 0; - left: 0; - z-index: 5; - pointer-events: none; - background-color: var(--dv-tab-divider-color); - width: 1px; - height: 100%; } .dv-watermark { display: flex; diff --git a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-core.esm.js b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-core.esm.js index 8121bdc6..63ec2eb8 100644 --- a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-core.esm.js +++ b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-core.esm.js @@ -1,6 +1,6 @@ -/** +/** * dockview-core - * @version 3.2.0 + * @version 4.0.1 * @link https://github.com/mathuo/dockview * @license MIT */ @@ -333,6 +333,20 @@ class MutableDisposable { } } +class OverflowObserver extends CompositeDisposable { + constructor(el) { + super(); + this._onDidChange = new Emitter(); + this.onDidChange = this._onDidChange.event; + this._value = null; + this.addDisposables(this._onDidChange, watchElementResize(el, (entry) => { + const hasScrollX = entry.target.scrollWidth > entry.target.clientWidth; + const hasScrollY = entry.target.scrollHeight > entry.target.clientHeight; + this._value = { hasScrollX, hasScrollY }; + this._onDidChange.fire(this._value); + })); + } +} function watchElementResize(element, cb) { const observer = new ResizeObserver((entires) => { /** @@ -571,6 +585,19 @@ class Classnames { } } } +function isChildEntirelyVisibleWithinParent(child, parent) { + // + const childPosition = getDomNodePagePosition(child); + const parentPosition = getDomNodePagePosition(parent); + if (childPosition.left < parentPosition.left) { + return false; + } + if (childPosition.left + childPosition.width > + parentPosition.left + parentPosition.width) { + return false; + } + return true; +} function tail(arr) { if (arr.length === 0) { @@ -578,9 +605,6 @@ function tail(arr) { } return [arr.slice(0, arr.length - 1), arr[arr.length - 1]]; } -function last(arr) { - return arr.length > 0 ? arr[arr.length - 1] : undefined; -} function sequenceEquals(arr1, arr2) { if (arr1.length !== arr2.length) { return false; @@ -846,6 +870,7 @@ class Splitview { } set margin(value) { this._margin = value; + toggleClass(this.element, 'dv-splitview-has-margin', value !== 0); } constructor(container, options) { var _a, _b; @@ -3416,9 +3441,6 @@ class DockviewApi { get totalPanels() { return this.component.totalPanels; } - get gap() { - return this.component.gap; - } /** * Invoked when the active group changes. May be undefined if no group is active. */ @@ -3649,9 +3671,6 @@ class DockviewApi { addPopoutGroup(item, options) { return this.component.addPopoutGroup(item, options); } - setGap(gap) { - this.component.updateOptions({ gap }); - } updateOptions(options) { this.component.updateOptions(options); } @@ -3704,14 +3723,16 @@ class DragHandler extends CompositeDisposable { * For example: in react-dnd if dataTransfer.types is not set then the dragStart event will be cancelled * through .preventDefault(). Since this is applied globally to all drag events this would break dockviews * dnd logic. You can see the code at - * https://github.com/react-dnd/react-dnd/blob/main/packages/backend-html5/src/HTML5BackendImpl.ts#L542 + P * https://github.com/react-dnd/react-dnd/blob/main/packages/backend-html5/src/HTML5BackendImpl.ts#L542 */ event.dataTransfer.setData('text/plain', ''); } } }), addDisposableListener(this.el, 'dragend', () => { this.pointerEventsDisposable.dispose(); - this.dataDisposable.dispose(); + setTimeout(() => { + this.dataDisposable.dispose(); // allow the data to be read by other handlers before disposing + }, 0); })); } } @@ -3821,6 +3842,12 @@ const DEFAULT_SIZE = { const SMALL_WIDTH_BOUNDARY = 100; const SMALL_HEIGHT_BOUNDARY = 100; class Droptarget extends CompositeDisposable { + get disabled() { + return this._disabled; + } + set disabled(value) { + this._disabled = value; + } get state() { return this._state; } @@ -3832,23 +3859,34 @@ class Droptarget extends CompositeDisposable { this.onDrop = this._onDrop.event; this._onWillShowOverlay = new Emitter(); this.onWillShowOverlay = this._onWillShowOverlay.event; + this._disabled = false; // use a set to take advantage of #.has this._acceptedTargetZonesSet = new Set(this.options.acceptedTargetZones); this.dnd = new DragAndDropObserver(this.element, { - onDragEnter: () => undefined, + onDragEnter: () => { + var _a, _b, _c; + (_c = (_b = (_a = this.options).getOverrideTarget) === null || _b === void 0 ? void 0 : _b.call(_a)) === null || _c === void 0 ? void 0 : _c.getElements(); + }, onDragOver: (e) => { + var _a, _b, _c, _d, _e, _f, _g; + Droptarget.ACTUAL_TARGET = this; + const overrideTraget = (_b = (_a = this.options).getOverrideTarget) === null || _b === void 0 ? void 0 : _b.call(_a); if (this._acceptedTargetZonesSet.size === 0) { + if (overrideTraget) { + return; + } this.removeDropTarget(); return; } - const width = this.element.clientWidth; - const height = this.element.clientHeight; + const target = (_e = (_d = (_c = this.options).getOverlayOutline) === null || _d === void 0 ? void 0 : _d.call(_c)) !== null && _e !== void 0 ? _e : this.element; + const width = target.offsetWidth; + const height = target.offsetHeight; if (width === 0 || height === 0) { return; // avoid div!0 } const rect = e.currentTarget.getBoundingClientRect(); - const x = e.clientX - rect.left; - const y = e.clientY - rect.top; + const x = ((_f = e.clientX) !== null && _f !== void 0 ? _f : 0) - rect.left; + const y = ((_g = e.clientY) !== null && _g !== void 0 ? _g : 0) - rect.top; const quadrant = this.calculateQuadrant(this._acceptedTargetZonesSet, x, y, width, height); /** * If the event has already been used by another DropTarget instance @@ -3861,6 +3899,9 @@ class Droptarget extends CompositeDisposable { return; } if (!this.options.canDisplayOverlay(e, quadrant)) { + if (overrideTraget) { + return; + } this.removeDropTarget(); return; } @@ -3878,29 +3919,57 @@ class Droptarget extends CompositeDisposable { return; } this.markAsUsed(e); - if (!this.targetElement) { + if (overrideTraget) ; + else if (!this.targetElement) { this.targetElement = document.createElement('div'); this.targetElement.className = 'dv-drop-target-dropzone'; this.overlayElement = document.createElement('div'); this.overlayElement.className = 'dv-drop-target-selection'; this._state = 'center'; this.targetElement.appendChild(this.overlayElement); - this.element.classList.add('dv-drop-target'); - this.element.append(this.targetElement); + target.classList.add('dv-drop-target'); + target.append(this.targetElement); + // this.overlayElement.style.opacity = '0'; + // requestAnimationFrame(() => { + // if (this.overlayElement) { + // this.overlayElement.style.opacity = ''; + // } + // }); } this.toggleClasses(quadrant, width, height); this._state = quadrant; }, onDragLeave: () => { + var _a, _b; + const target = (_b = (_a = this.options).getOverrideTarget) === null || _b === void 0 ? void 0 : _b.call(_a); + if (target) { + return; + } this.removeDropTarget(); }, - onDragEnd: () => { + onDragEnd: (e) => { + var _a, _b; + const target = (_b = (_a = this.options).getOverrideTarget) === null || _b === void 0 ? void 0 : _b.call(_a); + if (target && Droptarget.ACTUAL_TARGET === this) { + if (this._state) { + // only stop the propagation of the event if we are dealing with it + // which is only when the target has state + e.stopPropagation(); + this._onDrop.fire({ + position: this._state, + nativeEvent: e, + }); + } + } this.removeDropTarget(); + target === null || target === void 0 ? void 0 : target.clear(); }, onDrop: (e) => { + var _a, _b, _c; e.preventDefault(); const state = this._state; this.removeDropTarget(); + (_c = (_b = (_a = this.options).getOverrideTarget) === null || _b === void 0 ? void 0 : _b.call(_a)) === null || _c === void 0 ? void 0 : _c.clear(); if (state) { // only stop the propagation of the event if we are dealing with it // which is only when the target has state @@ -3935,8 +4004,9 @@ class Droptarget extends CompositeDisposable { return typeof value === 'boolean' && value; } toggleClasses(quadrant, width, height) { - var _a, _b; - if (!this.overlayElement) { + var _a, _b, _c, _d, _e, _f, _g; + const target = (_b = (_a = this.options).getOverrideTarget) === null || _b === void 0 ? void 0 : _b.call(_a); + if (!target && !this.overlayElement) { return; } const isSmallX = width < SMALL_WIDTH_BOUNDARY; @@ -3950,7 +4020,7 @@ class Droptarget extends CompositeDisposable { const topClass = !isSmallY && isTop; const bottomClass = !isSmallY && isBottom; let size = 1; - const sizeOptions = (_b = (_a = this.options.overlayModel) === null || _a === void 0 ? void 0 : _a.size) !== null && _b !== void 0 ? _b : DEFAULT_SIZE; + const sizeOptions = (_d = (_c = this.options.overlayModel) === null || _c === void 0 ? void 0 : _c.size) !== null && _d !== void 0 ? _d : DEFAULT_SIZE; if (sizeOptions.type === 'percentage') { size = clamp(sizeOptions.value, 0, 100) / 100; } @@ -3962,6 +4032,74 @@ class Droptarget extends CompositeDisposable { size = clamp(0, sizeOptions.value, height) / height; } } + if (target) { + const outlineEl = (_g = (_f = (_e = this.options).getOverlayOutline) === null || _f === void 0 ? void 0 : _f.call(_e)) !== null && _g !== void 0 ? _g : this.element; + const elBox = outlineEl.getBoundingClientRect(); + const ta = target.getElements(undefined, outlineEl); + const el = ta.root; + const overlay = ta.overlay; + const bigbox = el.getBoundingClientRect(); + const rootTop = elBox.top - bigbox.top; + const rootLeft = elBox.left - bigbox.left; + const box = { + top: rootTop, + left: rootLeft, + width: width, + height: height, + }; + if (rightClass) { + box.left = rootLeft + width * (1 - size); + box.width = width * size; + } + else if (leftClass) { + box.width = width * size; + } + else if (topClass) { + box.height = height * size; + } + else if (bottomClass) { + box.top = rootTop + height * (1 - size); + box.height = height * size; + } + if (isSmallX && isLeft) { + box.width = 4; + } + if (isSmallX && isRight) { + box.left = rootLeft + width - 4; + box.width = 4; + } + const topPx = `${Math.round(box.top)}px`; + const leftPx = `${Math.round(box.left)}px`; + const widthPx = `${Math.round(box.width)}px`; + const heightPx = `${Math.round(box.height)}px`; + if (overlay.style.top === topPx && + overlay.style.left === leftPx && + overlay.style.width === widthPx && + overlay.style.height === heightPx) { + return; + } + overlay.style.top = topPx; + overlay.style.left = leftPx; + overlay.style.width = widthPx; + overlay.style.height = heightPx; + overlay.style.visibility = 'visible'; + overlay.className = `dv-drop-target-anchor${this.options.className ? ` ${this.options.className}` : ''}`; + toggleClass(overlay, 'dv-drop-target-left', isLeft); + toggleClass(overlay, 'dv-drop-target-right', isRight); + toggleClass(overlay, 'dv-drop-target-top', isTop); + toggleClass(overlay, 'dv-drop-target-bottom', isBottom); + toggleClass(overlay, 'dv-drop-target-center', quadrant === 'center'); + if (ta.changed) { + toggleClass(overlay, 'dv-drop-target-anchor-container-changed', true); + setTimeout(() => { + toggleClass(overlay, 'dv-drop-target-anchor-container-changed', false); + }, 10); + } + return; + } + if (!this.overlayElement) { + return; + } const box = { top: '0px', left: '0px', width: '100%', height: '100%' }; /** * You can also achieve the overlay placement using the transform CSS property @@ -4018,12 +4156,13 @@ class Droptarget extends CompositeDisposable { return calculateQuadrantAsPixels(overlayType, x, y, width, height, activationSizeOptions.value); } removeDropTarget() { + var _a; if (this.targetElement) { this._state = undefined; - this.element.removeChild(this.targetElement); + (_a = this.targetElement.parentElement) === null || _a === void 0 ? void 0 : _a.classList.remove('dv-drop-target'); + this.targetElement.remove(); this.targetElement = undefined; this.overlayElement = undefined; - this.element.classList.remove('dv-drop-target'); } } } @@ -4608,7 +4747,15 @@ class ContentContainer extends CompositeDisposable { this._element.className = 'dv-content-container'; this._element.tabIndex = -1; this.addDisposables(this._onDidFocus, this._onDidBlur); + const target = group.dropTargetContainer; this.dropTarget = new Droptarget(this.element, { + getOverlayOutline: () => { + var _a; + return ((_a = accessor.options.theme) === null || _a === void 0 ? void 0 : _a.dndPanelOverlay) === 'group' + ? this.element.parentElement + : null; + }, + className: 'dv-drop-target-content', acceptedTargetZones: ['top', 'bottom', 'left', 'right', 'center'], canDisplayOverlay: (event, position) => { if (this.group.locked === 'no-drop-target' || @@ -4622,22 +4769,11 @@ class ContentContainer extends CompositeDisposable { return false; } if (data && data.viewId === this.accessor.id) { - if (data.groupId === this.group.id) { - if (position === 'center') { - // don't allow to drop on self for center position - return false; - } - if (data.panelId === null) { - // don't allow group move to drop anywhere on self - return false; - } - } - const groupHasOnePanelAndIsActiveDragElement = this.group.panels.length === 1 && - data.groupId === this.group.id; - return !groupHasOnePanelAndIsActiveDragElement; + return true; } return this.group.canDisplayOverlay(event, position, 'content'); }, + getOverrideTarget: target ? () => target.model : undefined, }); this.addDisposables(this.dropTarget); } @@ -4712,6 +4848,18 @@ class ContentContainer extends CompositeDisposable { } } +function addGhostImage(dataTransfer, ghostElement, options) { + var _a, _b; + // class dockview provides to force ghost image to be drawn on a different layer and prevent weird rendering issues + addClasses(ghostElement, 'dv-dragged'); + document.body.appendChild(ghostElement); + dataTransfer.setDragImage(ghostElement, (_a = options === null || options === void 0 ? void 0 : options.x) !== null && _a !== void 0 ? _a : 0, (_b = options === null || options === void 0 ? void 0 : options.y) !== null && _b !== void 0 ? _b : 0); + setTimeout(() => { + removeClasses(ghostElement, 'dv-dragged'); + ghostElement.remove(); + }, 0); +} + class TabDragHandler extends DragHandler { constructor(element, accessor, group, panel) { super(element); @@ -4752,25 +4900,32 @@ class Tab extends CompositeDisposable { toggleClass(this.element, 'dv-inactive-tab', true); const dragHandler = new TabDragHandler(this._element, this.accessor, this.group, this.panel); this.dropTarget = new Droptarget(this._element, { - acceptedTargetZones: ['center'], + acceptedTargetZones: ['left', 'right'], + overlayModel: { activationSize: { value: 50, type: 'percentage' } }, canDisplayOverlay: (event, position) => { if (this.group.locked) { return false; } const data = getPanelData(); if (data && this.accessor.id === data.viewId) { - if (data.panelId === null && - data.groupId === this.group.id) { - // don't allow group move to drop on self - return false; - } - return this.panel.id !== data.panelId; + return true; } return this.group.model.canDisplayOverlay(event, position, 'tab'); }, + getOverrideTarget: () => { var _a; return (_a = group.model.dropTargetContainer) === null || _a === void 0 ? void 0 : _a.model; }, }); this.onWillShowOverlay = this.dropTarget.onWillShowOverlay; this.addDisposables(this._onPointDown, this._onDropped, this._onDragStart, dragHandler.onDragStart((event) => { + if (event.dataTransfer) { + const style = getComputedStyle(this.element); + const newNode = this.element.cloneNode(true); + Array.from(style).forEach((key) => newNode.style.setProperty(key, style.getPropertyValue(key), style.getPropertyPriority(key))); + newNode.style.position = 'absolute'; + addGhostImage(event.dataTransfer, newNode, { + y: -10, + x: 30, + }); + } this._onDragStart.fire(event); }), dragHandler, addDisposableListener(this._element, 'pointerdown', (event) => { this._onPointDown.fire(event); @@ -4794,17 +4949,6 @@ class Tab extends CompositeDisposable { } } -function addGhostImage(dataTransfer, ghostElement) { - // class dockview provides to force ghost image to be drawn on a different layer and prevent weird rendering issues - addClasses(ghostElement, 'dv-dragged'); - document.body.appendChild(ghostElement); - dataTransfer.setDragImage(ghostElement, 0, 0); - setTimeout(() => { - removeClasses(ghostElement, 'dv-dragged'); - ghostElement.remove(); - }, 0); -} - class GroupDragHandler extends DragHandler { constructor(element, accessor, group) { super(element); @@ -4844,8 +4988,10 @@ class GroupDragHandler extends DragHandler { ghostElement.style.lineHeight = '20px'; ghostElement.style.borderRadius = '12px'; ghostElement.style.position = 'absolute'; + ghostElement.style.pointerEvents = 'none'; + ghostElement.style.top = '-9999px'; ghostElement.textContent = `Multiple Panels (${this.group.size})`; - addGhostImage(dataTransfer, ghostElement); + addGhostImage(dataTransfer, ghostElement, { y: -10, x: 30 }); } return { dispose: () => { @@ -4877,19 +5023,13 @@ class VoidContainer extends CompositeDisposable { this.dropTraget = new Droptarget(this._element, { acceptedTargetZones: ['center'], canDisplayOverlay: (event, position) => { - var _a; const data = getPanelData(); if (data && this.accessor.id === data.viewId) { - if (data.panelId === null && - data.groupId === this.group.id) { - // don't allow group move to drop on self - return false; - } - // don't show the overlay if the tab being dragged is the last panel of this group - return ((_a = last(this.group.panels)) === null || _a === void 0 ? void 0 : _a.id) !== data.panelId; + return true; } return group.model.canDisplayOverlay(event, position, 'header_space'); }, + getOverrideTarget: () => { var _a; return (_a = group.model.dropTargetContainer) === null || _a === void 0 ? void 0 : _a.model; }, }); this.onWillShowOverlay = this.dropTraget.onWillShowOverlay; this.addDisposables(handler, handler.onDragStart((event) => { @@ -4900,142 +5040,136 @@ class VoidContainer extends CompositeDisposable { } } -class TabsContainer extends CompositeDisposable { - get panels() { - return this.tabs.map((_) => _.value.panel.id); - } - get size() { - return this.tabs.length; - } - get hidden() { - return this._hidden; - } - set hidden(value) { - this._hidden = value; - this.element.style.display = value ? 'none' : ''; - } - show() { - if (!this.hidden) { - this.element.style.display = ''; - } +class Scrollbar extends CompositeDisposable { + get element() { + return this._element; } - hide() { - this._element.style.display = 'none'; + constructor(scrollableElement) { + super(); + this.scrollableElement = scrollableElement; + this._scrollLeft = 0; + this._element = document.createElement('div'); + this._element.className = 'dv-scrollable'; + this._horizontalScrollbar = document.createElement('div'); + this._horizontalScrollbar.className = 'dv-scrollbar-horizontal'; + this.element.appendChild(scrollableElement); + this.element.appendChild(this._horizontalScrollbar); + this.addDisposables(addDisposableListener(this.element, 'wheel', (event) => { + this._scrollLeft += event.deltaY * Scrollbar.MouseWheelSpeed; + this.calculateScrollbarStyles(); + }), addDisposableListener(this._horizontalScrollbar, 'pointerdown', (event) => { + event.preventDefault(); + toggleClass(this.element, 'dv-scrollable-scrolling', true); + const originalClientX = event.clientX; + const originalScrollLeft = this._scrollLeft; + const onPointerMove = (event) => { + const deltaX = event.clientX - originalClientX; + const { clientWidth } = this.element; + const { scrollWidth } = this.scrollableElement; + const p = clientWidth / scrollWidth; + this._scrollLeft = originalScrollLeft + deltaX / p; + this.calculateScrollbarStyles(); + }; + const onEnd = () => { + toggleClass(this.element, 'dv-scrollable-scrolling', false); + document.removeEventListener('pointermove', onPointerMove); + document.removeEventListener('pointerup', onEnd); + document.removeEventListener('pointercancel', onEnd); + }; + document.addEventListener('pointermove', onPointerMove); + document.addEventListener('pointerup', onEnd); + document.addEventListener('pointercancel', onEnd); + }), addDisposableListener(this.element, 'scroll', () => { + this.calculateScrollbarStyles(); + }), addDisposableListener(this.scrollableElement, 'scroll', () => { + this._scrollLeft = this.scrollableElement.scrollLeft; + this.calculateScrollbarStyles(); + }), watchElementResize(this.element, () => { + toggleClass(this.element, 'dv-scrollable-resizing', true); + if (this._animationTimer) { + clearTimeout(this._animationTimer); + } + this._animationTimer = setTimeout(() => { + clearTimeout(this._animationTimer); + toggleClass(this.element, 'dv-scrollable-resizing', false); + }, 500); + this.calculateScrollbarStyles(); + })); } - setRightActionsElement(element) { - if (this.rightActions === element) { - return; - } - if (this.rightActions) { - this.rightActions.remove(); - this.rightActions = undefined; + calculateScrollbarStyles() { + const { clientWidth } = this.element; + const { scrollWidth } = this.scrollableElement; + const hasScrollbar = scrollWidth > clientWidth; + if (hasScrollbar) { + const px = clientWidth * (clientWidth / scrollWidth); + this._horizontalScrollbar.style.width = `${px}px`; + this._scrollLeft = clamp(this._scrollLeft, 0, this.scrollableElement.scrollWidth - clientWidth); + this.scrollableElement.scrollLeft = this._scrollLeft; + const percentageComplete = this._scrollLeft / (scrollWidth - clientWidth); + this._horizontalScrollbar.style.left = `${(clientWidth - px) * percentageComplete}px`; } - if (element) { - this.rightActionsContainer.appendChild(element); - this.rightActions = element; + else { + this._horizontalScrollbar.style.width = `0px`; + this._horizontalScrollbar.style.left = `0px`; + this._scrollLeft = 0; } } - setLeftActionsElement(element) { - if (this.leftActions === element) { - return; - } - if (this.leftActions) { - this.leftActions.remove(); - this.leftActions = undefined; - } - if (element) { - this.leftActionsContainer.appendChild(element); - this.leftActions = element; - } +} +Scrollbar.MouseWheelSpeed = 1; + +class Tabs extends CompositeDisposable { + get showTabsOverflowControl() { + return this._showTabsOverflowControl; } - setPrefixActionsElement(element) { - if (this.preActions === element) { + set showTabsOverflowControl(value) { + if (this._showTabsOverflowControl == value) { return; } - if (this.preActions) { - this.preActions.remove(); - this.preActions = undefined; - } - if (element) { - this.preActionsContainer.appendChild(element); - this.preActions = element; + this._showTabsOverflowControl = value; + if (value) { + const observer = new OverflowObserver(this._tabsList); + this._observerDisposable.value = new CompositeDisposable(observer, observer.onDidChange((event) => { + const hasOverflow = event.hasScrollX || event.hasScrollY; + this.toggleDropdown({ reset: !hasOverflow }); + }), addDisposableListener(this._tabsList, 'scroll', () => { + this.toggleDropdown({ reset: false }); + })); } } get element() { return this._element; } - isActive(tab) { - return (this.selectedIndex > -1 && - this.tabs[this.selectedIndex].value === tab); + get panels() { + return this._tabs.map((_) => _.value.panel.id); } - indexOf(id) { - return this.tabs.findIndex((tab) => tab.value.panel.id === id); + get size() { + return this._tabs.length; } - constructor(accessor, group) { + get tabs() { + return this._tabs.map((_) => _.value); + } + constructor(group, accessor, options) { super(); - this.accessor = accessor; this.group = group; - this.tabs = []; + this.accessor = accessor; + this._observerDisposable = new MutableDisposable(); + this._tabs = []; this.selectedIndex = -1; - this._hidden = false; - this._onDrop = new Emitter(); - this.onDrop = this._onDrop.event; + this._showTabsOverflowControl = false; this._onTabDragStart = new Emitter(); this.onTabDragStart = this._onTabDragStart.event; - this._onGroupDragStart = new Emitter(); - this.onGroupDragStart = this._onGroupDragStart.event; + this._onDrop = new Emitter(); + this.onDrop = this._onDrop.event; this._onWillShowOverlay = new Emitter(); this.onWillShowOverlay = this._onWillShowOverlay.event; - this._element = document.createElement('div'); - this._element.className = 'dv-tabs-and-actions-container'; - toggleClass(this._element, 'dv-full-width-single-tab', this.accessor.options.singleTabMode === 'fullwidth'); - this.rightActionsContainer = document.createElement('div'); - this.rightActionsContainer.className = 'dv-right-actions-container'; - this.leftActionsContainer = document.createElement('div'); - this.leftActionsContainer.className = 'dv-left-actions-container'; - this.preActionsContainer = document.createElement('div'); - this.preActionsContainer.className = 'dv-pre-actions-container'; - this.tabContainer = document.createElement('div'); - this.tabContainer.className = 'dv-tabs-container'; - this.voidContainer = new VoidContainer(this.accessor, this.group); - this._element.appendChild(this.preActionsContainer); - this._element.appendChild(this.tabContainer); - this._element.appendChild(this.leftActionsContainer); - this._element.appendChild(this.voidContainer.element); - this._element.appendChild(this.rightActionsContainer); - this.addDisposables(this._onWillShowOverlay, this._onDrop, this._onTabDragStart, this._onGroupDragStart, this.voidContainer, this.voidContainer.onDragStart((event) => { - this._onGroupDragStart.fire({ - nativeEvent: event, - group: this.group, - }); - }), this.voidContainer.onDrop((event) => { - this._onDrop.fire({ - event: event.nativeEvent, - index: this.tabs.length, - }); - }), this.voidContainer.onWillShowOverlay((event) => { - this._onWillShowOverlay.fire(new WillShowOverlayLocationEvent(event, { - kind: 'header_space', - panel: this.group.activePanel, - api: this.accessor.api, - group: this.group, - getData: getPanelData, - })); - }), addDisposableListener(this.voidContainer.element, 'pointerdown', (event) => { - const isFloatingGroupsEnabled = !this.accessor.options.disableFloatingGroups; - if (isFloatingGroupsEnabled && - event.shiftKey && - this.group.api.location.type !== 'floating') { - event.preventDefault(); - const { top, left } = this.element.getBoundingClientRect(); - const { top: rootTop, left: rootLeft } = this.accessor.element.getBoundingClientRect(); - this.accessor.addFloatingGroup(this.group, { - x: left - rootLeft + 20, - y: top - rootTop + 20, - inDragMode: true, - }); - } - }), addDisposableListener(this.tabContainer, 'pointerdown', (event) => { + this._onOverflowTabsChange = new Emitter(); + this.onOverflowTabsChange = this._onOverflowTabsChange.event; + this._tabsList = document.createElement('div'); + this._tabsList.className = 'dv-tabs-container dv-horizontal'; + this.showTabsOverflowControl = options.showTabsOverflowControl; + const scrollbar = new Scrollbar(this._tabsList); + this._element = scrollbar.element; + this.addDisposables(this._onOverflowTabsChange, this._observerDisposable, scrollbar, this._onWillShowOverlay, this._onDrop, this._onTabDragStart, addDisposableListener(this.element, 'pointerdown', (event) => { if (event.defaultPrevented) { return; } @@ -5043,31 +5177,40 @@ class TabsContainer extends CompositeDisposable { if (isLeftClick) { this.accessor.doSetGroupActive(this.group); } + }), Disposable.from(() => { + for (const { value, disposable } of this._tabs) { + disposable.dispose(); + value.dispose(); + } + this._tabs = []; })); } - setActive(_isGroupActive) { - // noop + indexOf(id) { + return this._tabs.findIndex((tab) => tab.value.panel.id === id); } - delete(id) { - const index = this.tabs.findIndex((tab) => tab.value.panel.id === id); - const tabToRemove = this.tabs.splice(index, 1)[0]; - if (!tabToRemove) { - throw new Error(`dockview: Tab not found`); - } - const { value, disposable } = tabToRemove; - disposable.dispose(); - value.dispose(); - value.element.remove(); - this.updateClassnames(); + isActive(tab) { + return (this.selectedIndex > -1 && + this._tabs[this.selectedIndex].value === tab); } setActivePanel(panel) { - this.tabs.forEach((tab) => { + let runningWidth = 0; + for (const tab of this._tabs) { const isActivePanel = panel.id === tab.value.panel.id; tab.value.setActive(isActivePanel); - }); + if (isActivePanel) { + const element = tab.value.element; + const parentElement = element.parentElement; + if (runningWidth < parentElement.scrollLeft || + runningWidth + element.clientWidth > + parentElement.scrollLeft + parentElement.clientWidth) { + parentElement.scrollLeft = runningWidth; + } + } + runningWidth += tab.value.element.clientWidth; + } } - openPanel(panel, index = this.tabs.length) { - if (this.tabs.find((tab) => tab.value.panel.id === panel.id)) { + openPanel(panel, index = this._tabs.length) { + if (this._tabs.find((tab) => tab.value.panel.id === panel.id)) { return; } const tab = new Tab(panel, this.accessor, this.group); @@ -5105,7 +5248,7 @@ class TabsContainer extends CompositeDisposable { }), tab.onDrop((event) => { this._onDrop.fire({ event: event.nativeEvent, - index: this.tabs.findIndex((x) => x.value === tab), + index: this._tabs.findIndex((x) => x.value === tab), }); }), tab.onWillShowOverlay((event) => { this._onWillShowOverlay.fire(new WillShowOverlayLocationEvent(event, { @@ -5119,53 +5262,322 @@ class TabsContainer extends CompositeDisposable { const value = { value: tab, disposable }; this.addTab(value, index); } - closePanel(panel) { - this.delete(panel.id); - } - dispose() { - super.dispose(); - for (const { value, disposable } of this.tabs) { - disposable.dispose(); - value.dispose(); - } - this.tabs = []; + delete(id) { + const index = this.indexOf(id); + const tabToRemove = this._tabs.splice(index, 1)[0]; + const { value, disposable } = tabToRemove; + disposable.dispose(); + value.dispose(); + value.element.remove(); } - addTab(tab, index = this.tabs.length) { - if (index < 0 || index > this.tabs.length) { + addTab(tab, index = this._tabs.length) { + if (index < 0 || index > this._tabs.length) { throw new Error('invalid location'); } - this.tabContainer.insertBefore(tab.value.element, this.tabContainer.children[index]); - this.tabs = [ - ...this.tabs.slice(0, index), + this._tabsList.insertBefore(tab.value.element, this._tabsList.children[index]); + this._tabs = [ + ...this._tabs.slice(0, index), tab, - ...this.tabs.slice(index), + ...this._tabs.slice(index), ]; if (this.selectedIndex < 0) { this.selectedIndex = index; } - this.updateClassnames(); } - updateClassnames() { - toggleClass(this._element, 'dv-single-tab', this.size === 1); + toggleDropdown(options) { + const tabs = options.reset + ? [] + : this._tabs + .filter((tab) => !isChildEntirelyVisibleWithinParent(tab.value.element, this._tabsList)) + .map((x) => x.value.panel.id); + this._onOverflowTabsChange.fire({ tabs, reset: options.reset }); } } -class DockviewUnhandledDragOverEvent extends AcceptableEvent { - constructor(nativeEvent, target, position, getData, group) { - super(); - this.nativeEvent = nativeEvent; - this.target = target; - this.position = position; - this.getData = getData; - this.group = group; - } -} -const PROPERTY_KEYS_DOCKVIEW = (() => { - /** - * by readong the keys from an empty value object TypeScript will error - * when we add or remove new properties to `DockviewOptions` - */ - const properties = { +const createSvgElementFromPath = (params) => { + const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + svg.setAttributeNS(null, 'height', params.height); + svg.setAttributeNS(null, 'width', params.width); + svg.setAttributeNS(null, 'viewBox', params.viewbox); + svg.setAttributeNS(null, 'aria-hidden', 'false'); + svg.setAttributeNS(null, 'focusable', 'false'); + svg.classList.add('dv-svg'); + const path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); + path.setAttributeNS(null, 'd', params.path); + svg.appendChild(path); + return svg; +}; +const createCloseButton = () => createSvgElementFromPath({ + width: '11', + height: '11', + viewbox: '0 0 28 28', + path: 'M2.1 27.3L0 25.2L11.55 13.65L0 2.1L2.1 0L13.65 11.55L25.2 0L27.3 2.1L15.75 13.65L27.3 25.2L25.2 27.3L13.65 15.75L2.1 27.3Z', +}); +const createExpandMoreButton = () => createSvgElementFromPath({ + width: '11', + height: '11', + viewbox: '0 0 24 15', + path: 'M12 14.15L0 2.15L2.15 0L12 9.9L21.85 0.0499992L24 2.2L12 14.15Z', +}); +const createChevronRightButton = () => createSvgElementFromPath({ + width: '11', + height: '11', + viewbox: '0 0 15 25', + path: 'M2.15 24.1L0 21.95L9.9 12.05L0 2.15L2.15 0L14.2 12.05L2.15 24.1Z', +}); + +function createDropdownElementHandle() { + const el = document.createElement('div'); + el.className = 'dv-tabs-overflow-dropdown-default'; + const text = document.createElement('span'); + text.textContent = ``; + const icon = createChevronRightButton(); + el.appendChild(icon); + el.appendChild(text); + return { + element: el, + update: (params) => { + text.textContent = `${params.tabs}`; + }, + }; +} + +class TabsContainer extends CompositeDisposable { + get onTabDragStart() { + return this.tabs.onTabDragStart; + } + get panels() { + return this.tabs.panels; + } + get size() { + return this.tabs.size; + } + get hidden() { + return this._hidden; + } + set hidden(value) { + this._hidden = value; + this.element.style.display = value ? 'none' : ''; + } + get element() { + return this._element; + } + constructor(accessor, group) { + super(); + this.accessor = accessor; + this.group = group; + this._hidden = false; + this.dropdownPart = null; + this._overflowTabs = []; + this._dropdownDisposable = new MutableDisposable(); + this._onDrop = new Emitter(); + this.onDrop = this._onDrop.event; + this._onGroupDragStart = new Emitter(); + this.onGroupDragStart = this._onGroupDragStart.event; + this._onWillShowOverlay = new Emitter(); + this.onWillShowOverlay = this._onWillShowOverlay.event; + this._element = document.createElement('div'); + this._element.className = 'dv-tabs-and-actions-container'; + toggleClass(this._element, 'dv-full-width-single-tab', this.accessor.options.singleTabMode === 'fullwidth'); + this.rightActionsContainer = document.createElement('div'); + this.rightActionsContainer.className = 'dv-right-actions-container'; + this.leftActionsContainer = document.createElement('div'); + this.leftActionsContainer.className = 'dv-left-actions-container'; + this.preActionsContainer = document.createElement('div'); + this.preActionsContainer.className = 'dv-pre-actions-container'; + this.tabs = new Tabs(group, accessor, { + showTabsOverflowControl: !accessor.options.disableTabsOverflowList, + }); + this.voidContainer = new VoidContainer(this.accessor, this.group); + this._element.appendChild(this.preActionsContainer); + this._element.appendChild(this.tabs.element); + this._element.appendChild(this.leftActionsContainer); + this._element.appendChild(this.voidContainer.element); + this._element.appendChild(this.rightActionsContainer); + this.addDisposables(accessor.onDidOptionsChange(() => { + this.tabs.showTabsOverflowControl = + !accessor.options.disableTabsOverflowList; + }), this.tabs.onOverflowTabsChange((event) => { + this.toggleDropdown(event); + }), this.tabs, this._onWillShowOverlay, this._onDrop, this._onGroupDragStart, this.voidContainer, this.voidContainer.onDragStart((event) => { + this._onGroupDragStart.fire({ + nativeEvent: event, + group: this.group, + }); + }), this.voidContainer.onDrop((event) => { + this._onDrop.fire({ + event: event.nativeEvent, + index: this.tabs.size, + }); + }), this.voidContainer.onWillShowOverlay((event) => { + this._onWillShowOverlay.fire(new WillShowOverlayLocationEvent(event, { + kind: 'header_space', + panel: this.group.activePanel, + api: this.accessor.api, + group: this.group, + getData: getPanelData, + })); + }), addDisposableListener(this.voidContainer.element, 'pointerdown', (event) => { + if (event.defaultPrevented) { + return; + } + const isFloatingGroupsEnabled = !this.accessor.options.disableFloatingGroups; + if (isFloatingGroupsEnabled && + event.shiftKey && + this.group.api.location.type !== 'floating') { + event.preventDefault(); + const { top, left } = this.element.getBoundingClientRect(); + const { top: rootTop, left: rootLeft } = this.accessor.element.getBoundingClientRect(); + this.accessor.addFloatingGroup(this.group, { + x: left - rootLeft + 20, + y: top - rootTop + 20, + inDragMode: true, + }); + } + })); + } + show() { + if (!this.hidden) { + this.element.style.display = ''; + } + } + hide() { + this._element.style.display = 'none'; + } + setRightActionsElement(element) { + if (this.rightActions === element) { + return; + } + if (this.rightActions) { + this.rightActions.remove(); + this.rightActions = undefined; + } + if (element) { + this.rightActionsContainer.appendChild(element); + this.rightActions = element; + } + } + setLeftActionsElement(element) { + if (this.leftActions === element) { + return; + } + if (this.leftActions) { + this.leftActions.remove(); + this.leftActions = undefined; + } + if (element) { + this.leftActionsContainer.appendChild(element); + this.leftActions = element; + } + } + setPrefixActionsElement(element) { + if (this.preActions === element) { + return; + } + if (this.preActions) { + this.preActions.remove(); + this.preActions = undefined; + } + if (element) { + this.preActionsContainer.appendChild(element); + this.preActions = element; + } + } + isActive(tab) { + return this.tabs.isActive(tab); + } + indexOf(id) { + return this.tabs.indexOf(id); + } + setActive(_isGroupActive) { + // noop + } + delete(id) { + this.tabs.delete(id); + this.updateClassnames(); + } + setActivePanel(panel) { + this.tabs.setActivePanel(panel); + } + openPanel(panel, index = this.tabs.size) { + this.tabs.openPanel(panel, index); + this.updateClassnames(); + } + closePanel(panel) { + this.delete(panel.id); + } + updateClassnames() { + toggleClass(this._element, 'dv-single-tab', this.size === 1); + } + toggleDropdown(options) { + const tabs = options.reset ? [] : options.tabs; + this._overflowTabs = tabs; + if (this._overflowTabs.length > 0 && this.dropdownPart) { + this.dropdownPart.update({ tabs: tabs.length }); + return; + } + if (this._overflowTabs.length === 0) { + this._dropdownDisposable.dispose(); + return; + } + const root = document.createElement('div'); + root.className = 'dv-tabs-overflow-dropdown-root'; + const part = createDropdownElementHandle(); + part.update({ tabs: tabs.length }); + this.dropdownPart = part; + root.appendChild(part.element); + this.rightActionsContainer.prepend(root); + this._dropdownDisposable.value = new CompositeDisposable(Disposable.from(() => { + var _a, _b; + root.remove(); + (_b = (_a = this.dropdownPart) === null || _a === void 0 ? void 0 : _a.dispose) === null || _b === void 0 ? void 0 : _b.call(_a); + this.dropdownPart = null; + }), addDisposableListener(root, 'pointerdown', (event) => { + event.preventDefault(); + }, { capture: true }), addDisposableListener(root, 'click', (event) => { + const el = document.createElement('div'); + el.style.overflow = 'auto'; + el.className = 'dv-tabs-overflow-container'; + for (const tab of this.tabs.tabs.filter((tab) => this._overflowTabs.includes(tab.panel.id))) { + const panelObject = this.group.panels.find((panel) => panel === tab.panel); + const tabComponent = panelObject.view.createTabRenderer('headerOverflow'); + const child = tabComponent.element; + const wrapper = document.createElement('div'); + toggleClass(wrapper, 'dv-tab', true); + toggleClass(wrapper, 'dv-active-tab', panelObject.api.isActive); + toggleClass(wrapper, 'dv-inactive-tab', !panelObject.api.isActive); + wrapper.addEventListener('mousedown', () => { + this.accessor.popupService.close(); + tab.element.scrollIntoView(); + tab.panel.api.setActive(); + }); + wrapper.appendChild(child); + el.appendChild(wrapper); + } + this.accessor.popupService.openPopover(el, { + x: event.clientX, + y: event.clientY, + }); + })); + } +} + +class DockviewUnhandledDragOverEvent extends AcceptableEvent { + constructor(nativeEvent, target, position, getData, group) { + super(); + this.nativeEvent = nativeEvent; + this.target = target; + this.position = position; + this.getData = getData; + this.group = group; + } +} +const PROPERTY_KEYS_DOCKVIEW = (() => { + /** + * by readong the keys from an empty value object TypeScript will error + * when we add or remove new properties to `DockviewOptions` + */ + const properties = { disableAutoResizing: undefined, hideBorders: undefined, singleTabMode: undefined, @@ -5177,9 +5589,11 @@ const PROPERTY_KEYS_DOCKVIEW = (() => { rootOverlayModel: undefined, locked: undefined, disableDnd: undefined, - gap: undefined, className: undefined, noPanelsOverlay: undefined, + dndEdges: undefined, + theme: undefined, + disableTabsOverflowList: undefined, }; return Object.keys(properties); })(); @@ -5358,6 +5772,7 @@ class DockviewGroupPanelModel extends CompositeDisposable { this._location = { type: 'grid' }; this.mostRecentlyUsed = []; this._overwriteRenderContainer = null; + this._overwriteDropTargetContainer = null; this._onDidChange = new Emitter(); this.onDidChange = this._onDidChange.event; this._width = 0; @@ -5435,6 +5850,13 @@ class DockviewGroupPanelModel extends CompositeDisposable { var _a; return ((_a = this._overwriteRenderContainer) !== null && _a !== void 0 ? _a : this.accessor.overlayRenderContainer); } + set dropTargetContainer(value) { + this._overwriteDropTargetContainer = value; + } + get dropTargetContainer() { + var _a; + return ((_a = this._overwriteDropTargetContainer) !== null && _a !== void 0 ? _a : this.accessor.rootDropTargetContainer); + } initialize() { if (this.options.panels) { this.options.panels.forEach((panel) => { @@ -5783,6 +6205,25 @@ class DockviewGroupPanelModel extends CompositeDisposable { } const data = getPanelData(); if (data && data.viewId === this.accessor.id) { + if (type === 'content') { + if (data.groupId === this.id) { + // don't allow to drop on self for center position + if (position === 'center') { + return; + } + if (data.panelId === null) { + // don't allow group move to drop anywhere on self + return; + } + } + } + if (type === 'header') { + if (data.groupId === this.id) { + if (data.panelId === null) { + return; + } + } + } if (data.panelId === null) { // this is a group move dnd event const { groupId } = data; @@ -6211,6 +6652,46 @@ class DockviewGroupPanel extends GridviewPanel { } } +const themeDark = { + name: 'dark', + className: 'dockview-theme-dark', +}; +const themeLight = { + name: 'light', + className: 'dockview-theme-light', +}; +const themeVisualStudio = { + name: 'visualStudio', + className: 'dockview-theme-vs', +}; +const themeAbyss = { + name: 'abyss', + className: 'dockview-theme-abyss', +}; +const themeDracula = { + name: 'dracula', + className: 'dockview-theme-dracula', +}; +const themeReplit = { + name: 'replit', + className: 'dockview-theme-replit', + gap: 10, +}; +const themeAbyssSpaced = { + name: 'abyssSpaced', + className: 'dockview-theme-abyss-spaced', + gap: 10, + dndOverlayMounting: 'absolute', + dndPanelOverlay: 'group', +}; +const themeLightSpaced = { + name: 'lightSpaced', + className: 'dockview-theme-light-spaced', + gap: 10, + dndOverlayMounting: 'absolute', + dndPanelOverlay: 'group', +}; + class DockviewPanelApiImpl extends GridviewPanelApiImpl { get location() { return this.group.api.location; @@ -6485,38 +6966,6 @@ class DockviewPanel extends CompositeDisposable { } } -const createSvgElementFromPath = (params) => { - const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); - svg.setAttributeNS(null, 'height', params.height); - svg.setAttributeNS(null, 'width', params.width); - svg.setAttributeNS(null, 'viewBox', params.viewbox); - svg.setAttributeNS(null, 'aria-hidden', 'false'); - svg.setAttributeNS(null, 'focusable', 'false'); - svg.classList.add('dv-svg'); - const path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); - path.setAttributeNS(null, 'd', params.path); - svg.appendChild(path); - return svg; -}; -const createCloseButton = () => createSvgElementFromPath({ - width: '11', - height: '11', - viewbox: '0 0 28 28', - path: 'M2.1 27.3L0 25.2L11.55 13.65L0 2.1L2.1 0L13.65 11.55L25.2 0L27.3 2.1L15.75 13.65L27.3 25.2L25.2 27.3L13.65 15.75L2.1 27.3Z', -}); -const createExpandMoreButton = () => createSvgElementFromPath({ - width: '11', - height: '11', - viewbox: '0 0 24 15', - path: 'M12 14.15L0 2.15L2.15 0L12 9.9L21.85 0.0499992L24 2.2L12 14.15Z', -}); -const createChevronRightButton = () => createSvgElementFromPath({ - width: '11', - height: '11', - viewbox: '0 0 15 25', - path: 'M2.15 24.1L0 21.95L9.9 12.05L0 2.15L2.15 0L14.2 12.05L2.15 24.1Z', -}); - class DefaultTab extends CompositeDisposable { get element() { return this._element; @@ -6573,12 +7022,21 @@ class DockviewPanelModel { this._content = this.createContentComponent(this.id, contentComponent); this._tab = this.createTabComponent(this.id, tabComponent); } + createTabRenderer(tabLocation) { + var _a; + const cmp = this.createTabComponent(this.id, this.tabComponent); + if (this._params) { + cmp.init(Object.assign(Object.assign({}, this._params), { tabLocation })); + } + if (this._updateEvent) { + (_a = cmp.update) === null || _a === void 0 ? void 0 : _a.call(cmp, this._updateEvent); + } + return cmp; + } init(params) { + this._params = params; this.content.init(params); - this.tab.init(params); - } - updateParentGroup(_group, _isPanelVisible) { - // noop + this.tab.init(Object.assign(Object.assign({}, params), { tabLocation: 'header' })); } layout(width, height) { var _a, _b; @@ -6586,6 +7044,7 @@ class DockviewPanelModel { } update(event) { var _a, _b, _c, _d; + this._updateEvent = event; (_b = (_a = this.content).update) === null || _b === void 0 ? void 0 : _b.call(_a, event); (_d = (_c = this.tab).update) === null || _d === void 0 ? void 0 : _d.call(_c, event); } @@ -7575,6 +8034,132 @@ class StrictEventsSequencing extends CompositeDisposable { } } +class PopupService extends CompositeDisposable { + constructor(root) { + super(); + this.root = root; + this._active = null; + this._activeDisposable = new MutableDisposable(); + this._element = document.createElement('div'); + this._element.className = 'dv-popover-anchor'; + this._element.style.position = 'relative'; + this.root.prepend(this._element); + this.addDisposables(Disposable.from(() => { + this.close(); + }), this._activeDisposable); + } + openPopover(element, position) { + this.close(); + const wrapper = document.createElement('div'); + wrapper.style.position = 'absolute'; + wrapper.style.zIndex = '99'; + wrapper.appendChild(element); + const anchorBox = this._element.getBoundingClientRect(); + const offsetX = anchorBox.left; + const offsetY = anchorBox.top; + wrapper.style.top = `${position.y - offsetY}px`; + wrapper.style.left = `${position.x - offsetX}px`; + this._element.appendChild(wrapper); + this._active = wrapper; + this._activeDisposable.value = new CompositeDisposable(addDisposableWindowListener(window, 'pointerdown', (event) => { + var _a; + const target = event.target; + if (!(target instanceof HTMLElement)) { + return; + } + let el = target; + while (el && el !== wrapper) { + el = (_a = el === null || el === void 0 ? void 0 : el.parentElement) !== null && _a !== void 0 ? _a : null; + } + if (el) { + return; // clicked within popover + } + this.close(); + })); + } + close() { + if (this._active) { + this._active.remove(); + this._activeDisposable.dispose(); + this._active = null; + } + } +} + +class DropTargetAnchorContainer extends CompositeDisposable { + get disabled() { + return this._disabled; + } + set disabled(value) { + var _a; + if (this.disabled === value) { + return; + } + this._disabled = value; + if (value) { + (_a = this.model) === null || _a === void 0 ? void 0 : _a.clear(); + } + } + get model() { + if (this.disabled) { + return undefined; + } + return { + clear: () => { + var _a; + if (this._model) { + (_a = this._model.root.parentElement) === null || _a === void 0 ? void 0 : _a.removeChild(this._model.root); + } + this._model = undefined; + }, + exists: () => { + return !!this._model; + }, + getElements: (event, outline) => { + const changed = this._outline !== outline; + this._outline = outline; + if (this._model) { + this._model.changed = changed; + return this._model; + } + const container = this.createContainer(); + const anchor = this.createAnchor(); + this._model = { root: container, overlay: anchor, changed }; + container.appendChild(anchor); + this.element.appendChild(container); + if ((event === null || event === void 0 ? void 0 : event.target) instanceof HTMLElement) { + const targetBox = event.target.getBoundingClientRect(); + const box = this.element.getBoundingClientRect(); + anchor.style.left = `${targetBox.left - box.left}px`; + anchor.style.top = `${targetBox.top - box.top}px`; + } + return this._model; + }, + }; + } + constructor(element, options) { + super(); + this.element = element; + this._disabled = false; + this._disabled = options.disabled; + this.addDisposables(Disposable.from(() => { + var _a; + (_a = this.model) === null || _a === void 0 ? void 0 : _a.clear(); + })); + } + createContainer() { + const el = document.createElement('div'); + el.className = 'dv-drop-target-container'; + return el; + } + createAnchor() { + const el = document.createElement('div'); + el.className = 'dv-drop-target-anchor'; + el.style.visibility = 'hidden'; + return el; + } +} + const DEFAULT_ROOT_OVERLAY_MODEL = { activationSize: { type: 'pixels', value: 10 }, size: { type: 'pixels', value: 20 }, @@ -7620,14 +8205,11 @@ class DockviewComponent extends BaseGrid { get api() { return this._api; } - get gap() { - return this.gridview.margin; - } get floatingGroups() { return this._floatingGroups; } constructor(container, options) { - var _a; + var _a, _b, _c; super(container, { proportionalLayout: true, orientation: Orientation.HORIZONTAL, @@ -7636,12 +8218,12 @@ class DockviewComponent extends BaseGrid { : undefined, disableAutoResizing: options.disableAutoResizing, locked: options.locked, - margin: options.gap, + margin: (_b = (_a = options.theme) === null || _a === void 0 ? void 0 : _a.gap) !== null && _b !== void 0 ? _b : 0, className: options.className, }); this.nextGroupId = sequentialNumberGenerator(); this._deserializer = new DefaultDockviewDeserialzier(this); - this.watermark = null; + this._watermark = null; this._onWillDragPanel = new Emitter(); this.onWillDragPanel = this._onWillDragPanel.event; this._onWillDragGroup = new Emitter(); @@ -7672,16 +8254,22 @@ class DockviewComponent extends BaseGrid { this.onDidRemoveGroup = this._onDidRemoveGroup.event; this._onDidAddGroup = new Emitter(); this.onDidAddGroup = this._onDidAddGroup.event; + this._onDidOptionsChange = new Emitter(); + this.onDidOptionsChange = this._onDidOptionsChange.event; this._onDidActiveGroupChange = new Emitter(); this.onDidActiveGroupChange = this._onDidActiveGroupChange.event; this._moving = false; + this.popupService = new PopupService(this.element); + this.updateDropTargetModel(options); + this._themeClassnames = new Classnames(this.element); + this.rootDropTargetContainer = new DropTargetAnchorContainer(this.element, { disabled: true }); this.overlayRenderContainer = new OverlayRenderContainer(this.gridview.element, this); toggleClass(this.gridview.element, 'dv-dockview', true); toggleClass(this.element, 'dv-debug', !!options.debug); if (options.debug) { this.addDisposables(new StrictEventsSequencing(this)); } - this.addDisposables(this.overlayRenderContainer, this._onWillDragPanel, this._onWillDragGroup, this._onWillShowOverlay, this._onDidActivePanelChange, this._onDidAddPanel, this._onDidRemovePanel, this._onDidLayoutFromJSON, this._onDidDrop, this._onWillDrop, this._onDidMovePanel, this._onDidAddGroup, this._onDidRemoveGroup, this._onDidActiveGroupChange, this._onUnhandledDragOverEvent, this._onDidMaximizedGroupChange, this.onDidViewVisibilityChangeMicroTaskQueue(() => { + this.addDisposables(this.rootDropTargetContainer, this.overlayRenderContainer, this._onWillDragPanel, this._onWillDragGroup, this._onWillShowOverlay, this._onDidActivePanelChange, this._onDidAddPanel, this._onDidRemovePanel, this._onDidLayoutFromJSON, this._onDidDrop, this._onWillDrop, this._onDidMovePanel, this._onDidAddGroup, this._onDidRemoveGroup, this._onDidActiveGroupChange, this._onUnhandledDragOverEvent, this._onDidMaximizedGroupChange, this._onDidOptionsChange, this.onDidViewVisibilityChangeMicroTaskQueue(() => { this.updateWatermark(); }), this.onDidAdd((event) => { if (!this._moving) { @@ -7715,7 +8303,9 @@ class DockviewComponent extends BaseGrid { } })); this._options = options; + this.updateTheme(); this._rootDropTarget = new Droptarget(this.element, { + className: 'dv-drop-target-edge', canDisplayOverlay: (event, position) => { const data = getPanelData(); if (data) { @@ -7742,7 +8332,8 @@ class DockviewComponent extends BaseGrid { return firedEvent.isAccepted; }, acceptedTargetZones: ['top', 'bottom', 'left', 'right', 'center'], - overlayModel: (_a = this.options.rootOverlayModel) !== null && _a !== void 0 ? _a : DEFAULT_ROOT_OVERLAY_MODEL, + overlayModel: (_c = this.options.rootOverlayModel) !== null && _c !== void 0 ? _c : DEFAULT_ROOT_OVERLAY_MODEL, + getOverrideTarget: () => { var _a; return (_a = this.rootDropTargetContainer) === null || _a === void 0 ? void 0 : _a.model; }, }); this.addDisposables(this._rootDropTarget, this._rootDropTarget.onWillShowOverlay((event) => { if (this.gridview.length > 0 && event.position === 'center') { @@ -7924,6 +8515,10 @@ class DockviewComponent extends BaseGrid { popoutContainer.style.overflow = 'hidden'; popoutContainer.appendChild(gready); popoutContainer.appendChild(group.element); + const anchor = document.createElement('div'); + const dropTargetContainer = new DropTargetAnchorContainer(anchor, { disabled: this.rootDropTargetContainer.disabled }); + popoutContainer.appendChild(anchor); + group.model.dropTargetContainer = dropTargetContainer; group.model.location = { type: 'popout', getWindow: () => _window.window, @@ -7990,6 +8585,8 @@ class DockviewComponent extends BaseGrid { else if (this.getPanel(group.id)) { group.model.renderContainer = this.overlayRenderContainer; + group.model.dropTargetContainer = + this.rootDropTargetContainer; returnedGroup = group; const alreadyRemoved = !this._popoutGroups.find((p) => p.popoutGroup === group); if (alreadyRemoved) { @@ -8214,7 +8811,7 @@ class DockviewComponent extends BaseGrid { } } updateOptions(options) { - var _a, _b, _c, _d; + var _a, _b; super.updateOptions(options); if ('floatingGroupBounds' in options) { for (const group of this._floatingGroups) { @@ -8238,13 +8835,11 @@ class DockviewComponent extends BaseGrid { group.overlay.setBounds(); } } - if ('rootOverlayModel' in options) { - this._rootDropTarget.setOverlayModel((_c = options.rootOverlayModel) !== null && _c !== void 0 ? _c : DEFAULT_ROOT_OVERLAY_MODEL); - } - if ('gap' in options) { - this.gridview.margin = (_d = options.gap) !== null && _d !== void 0 ? _d : 0; - } + this.updateDropTargetModel(options); this._options = Object.assign(Object.assign({}, this.options), options); + if ('theme' in options) { + this.updateTheme(); + } this.layout(this.gridview.width, this.gridview.height, true); } layout(width, height, forceResize) { @@ -8662,22 +9257,22 @@ class DockviewComponent extends BaseGrid { updateWatermark() { var _a, _b; if (this.groups.filter((x) => x.api.location.type === 'grid' && x.api.isVisible).length === 0) { - if (!this.watermark) { - this.watermark = this.createWatermarkComponent(); - this.watermark.init({ + if (!this._watermark) { + this._watermark = this.createWatermarkComponent(); + this._watermark.init({ containerApi: new DockviewApi(this), }); const watermarkContainer = document.createElement('div'); watermarkContainer.className = 'dv-watermark-container'; addTestId(watermarkContainer, 'watermark-component'); - watermarkContainer.appendChild(this.watermark.element); + watermarkContainer.appendChild(this._watermark.element); this.gridview.element.appendChild(watermarkContainer); } } - else if (this.watermark) { - this.watermark.element.parentElement.remove(); - (_b = (_a = this.watermark).dispose) === null || _b === void 0 ? void 0 : _b.call(_a); - this.watermark = null; + else if (this._watermark) { + this._watermark.element.parentElement.remove(); + (_b = (_a = this._watermark).dispose) === null || _b === void 0 ? void 0 : _b.call(_a); + this._watermark = null; } } addGroup(options) { @@ -9196,6 +9791,38 @@ class DockviewComponent extends BaseGrid { ? rootOrientation : orthogonal(rootOrientation); } + updateDropTargetModel(options) { + if ('dndEdges' in options) { + this._rootDropTarget.disabled = + typeof options.dndEdges === 'boolean' && + options.dndEdges === false; + if (typeof options.dndEdges === 'object' && + options.dndEdges !== null) { + this._rootDropTarget.setOverlayModel(options.dndEdges); + } + else { + this._rootDropTarget.setOverlayModel(DEFAULT_ROOT_OVERLAY_MODEL); + } + } + if ('rootOverlayModel' in options) { + this.updateDropTargetModel({ dndEdges: options.dndEdges }); + } + } + updateTheme() { + var _a, _b; + const theme = (_a = this._options.theme) !== null && _a !== void 0 ? _a : themeAbyss; + this._themeClassnames.setClassNames(theme.className); + this.gridview.margin = (_b = theme.gap) !== null && _b !== void 0 ? _b : 0; + switch (theme.dndOverlayMounting) { + case 'absolute': + this.rootDropTargetContainer.disabled = false; + break; + case 'relative': + default: + this.rootDropTargetContainer.disabled = true; + break; + } + } } class GridviewComponent extends BaseGrid { @@ -9719,6 +10346,7 @@ class SplitviewComponent extends Resizable { for (const view of views) { view.dispose(); } + this.element.remove(); super.dispose(); } } @@ -10059,6 +10687,7 @@ class PaneviewComponent extends Resizable { value.dispose(); } this._viewDisposables.clear(); + this.element.remove(); this.paneview.dispose(); } } @@ -10185,4 +10814,4 @@ function createPaneview(element, options) { return new PaneviewApi(component); } -export { BaseGrid, ContentContainer, DefaultDockviewDeserialzier, DefaultTab, DockviewApi, DockviewComponent, CompositeDisposable as DockviewCompositeDisposable, DockviewDidDropEvent, Disposable as DockviewDisposable, Emitter as DockviewEmitter, Event as DockviewEvent, DockviewGroupPanel, DockviewGroupPanelModel, MutableDisposable as DockviewMutableDisposable, DockviewPanel, DockviewUnhandledDragOverEvent, DockviewWillDropEvent, DraggablePaneviewPanel, Gridview, GridviewApi, GridviewComponent, GridviewPanel, LayoutPriority, Orientation, PROPERTY_KEYS_DOCKVIEW, PROPERTY_KEYS_GRIDVIEW, PROPERTY_KEYS_PANEVIEW, PROPERTY_KEYS_SPLITVIEW, PaneFramework, PaneTransfer, PanelTransfer, Paneview, PaneviewApi, PaneviewComponent, PaneviewPanel, PaneviewUnhandledDragOverEvent, SashState, Sizing, Splitview, SplitviewApi, SplitviewComponent, SplitviewPanel, Tab, WillShowOverlayLocationEvent, createDockview, createGridview, createPaneview, createSplitview, directionToPosition, getDirectionOrientation, getGridLocation, getLocationOrientation, getPaneData, getPanelData, getRelativeLocation, indexInParent, isGridBranchNode, isGroupOptionsWithGroup, isGroupOptionsWithPanel, isPanelOptionsWithGroup, isPanelOptionsWithPanel, orthogonal, positionToDirection, toTarget }; +export { BaseGrid, ContentContainer, DefaultDockviewDeserialzier, DefaultTab, DockviewApi, DockviewComponent, CompositeDisposable as DockviewCompositeDisposable, DockviewDidDropEvent, Disposable as DockviewDisposable, Emitter as DockviewEmitter, Event as DockviewEvent, DockviewGroupPanel, DockviewGroupPanelModel, MutableDisposable as DockviewMutableDisposable, DockviewPanel, DockviewUnhandledDragOverEvent, DockviewWillDropEvent, DraggablePaneviewPanel, Gridview, GridviewApi, GridviewComponent, GridviewPanel, LayoutPriority, Orientation, PROPERTY_KEYS_DOCKVIEW, PROPERTY_KEYS_GRIDVIEW, PROPERTY_KEYS_PANEVIEW, PROPERTY_KEYS_SPLITVIEW, PaneFramework, PaneTransfer, PanelTransfer, Paneview, PaneviewApi, PaneviewComponent, PaneviewPanel, PaneviewUnhandledDragOverEvent, SashState, Sizing, Splitview, SplitviewApi, SplitviewComponent, SplitviewPanel, Tab, WillShowOverlayLocationEvent, createDockview, createGridview, createPaneview, createSplitview, directionToPosition, getDirectionOrientation, getGridLocation, getLocationOrientation, getPaneData, getPanelData, getRelativeLocation, indexInParent, isGridBranchNode, isGroupOptionsWithGroup, isGroupOptionsWithPanel, isPanelOptionsWithGroup, isPanelOptionsWithPanel, orthogonal, positionToDirection, themeAbyss, themeAbyssSpaced, themeDark, themeDracula, themeLight, themeLightSpaced, themeReplit, themeVisualStudio, toTarget }; diff --git a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-group.js b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-group.js index 310635f9..ad44e05d 100644 --- a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-group.js +++ b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-group.js @@ -180,7 +180,7 @@ const disposeGroup = group => { const { observer } = group.api.accessor.params; if (observer) { observer.unobserve(group.header.element); - observer.unobserve(group.header.tabContainer); + observer.unobserve(group.header.tabs._tabsList); } removeActionEvent(group); } diff --git a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-utils.js b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-utils.js index ec7b4727..1a09beaf 100644 --- a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-utils.js +++ b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-utils.js @@ -11,6 +11,14 @@ const cerateDockview = (el, options) => { const template = el.querySelector('template'); const dockview = new DockviewComponent(el, { parentElement: el, + theme: options.theme || { + name: "light", + className: "dockview-theme-light", + // gap: 3, + dndOverlayMounting: 'absolute', // 'absolute' | 'relative' + dndPanelOverlay: 'group', // 'content' | 'group' + }, + disableTabsOverflowList: true, createComponent: option => new DockviewPanelContent(option) }); initDockview(dockview, options, template); @@ -26,8 +34,8 @@ const initDockview = (dockview, options, template) => { dockview.init = () => { const config = getConfig(options); dockview.params.floatingGroups = config.floatingGroups || [] - // console.log(config, 'config'); dockview.fromJSON(config); + window.dockview = dockview; } dockview.update = options => { @@ -102,11 +110,11 @@ const initDockview = (dockview, options, template) => { observeFloatingGroupLocationChange(fg.group) }) - dockview._inited = true; - dockview._initialized?.fire() dockview.groups.forEach(group => { observeGroup(group) }) + dockview._inited = true; + dockview._initialized?.fire(); }, 100); }) @@ -127,7 +135,7 @@ export const observeGroup = (group) => { dockview.params.observer = new ResizeObserver(observerList => requestAnimationFrame(() => resizeObserverHandle(observerList, dockview))); } dockview.params.observer.observe(group.header.element) - dockview.params.observer.observe(group.header.tabContainer) + dockview.params.observer.observe(group.header.tabs._tabsList) for (let panel of group.panels) { if (panel.params.isActive) { panel.api.setActive() @@ -144,7 +152,7 @@ const resizeObserverHandle = (observerList, dockview) => { const setWidth = (target, dockview) => { let header, tabsContainer if (target.classList.contains('dv-tabs-container')) { - header = target.parentElement + header = target.parentElement.parentElement tabsContainer = target } else { @@ -158,32 +166,34 @@ const setWidth = (target, dockview) => { let dropMenu = dropdown.querySelector('.dropdown-menu') if (voidWidth === 0) { if (tabsContainer.children.length <= 1) return - const inactiveTabs = header.querySelectorAll('.dv-tabs-container>.dv-tab') - const lastTab = inactiveTabs[inactiveTabs.length - 1] - const aEle = document.createElement('a') - const liEle = document.createElement('li') - aEle.className = 'dropdown-item' - liEle.tabWidth = lastTab.offsetWidth; - aEle.append(lastTab) - liEle.append(aEle) - dropMenu.insertAdjacentElement("afterbegin", liEle) + const tabs = tabsContainer.querySelectorAll('.dv-tab') + for (let i = tabs.length - 1; i >= 0; i--) { + const lastTab = tabs[i] + if (lastTab.offsetLeft + lastTab.offsetWidth > tabsContainer.offsetWidth) { + const aEle = document.createElement('a') + const liEle = document.createElement('li') + aEle.className = 'dropdown-item' + liEle.tabWidth = lastTab.offsetWidth; + aEle.append(lastTab) + liEle.append(aEle) + dropMenu.insertAdjacentElement("afterbegin", liEle) + } + } } else { - let firstLi = dropMenu.querySelector('li:has(.dv-active-tab)') || dropMenu.children[0] + const firstLi = dropMenu.children[0] if (firstLi) { - let firstTab = firstLi.querySelector('.dv-tab') + const firstTab = firstLi.querySelector('.dv-tab') if (voidWidth > firstLi.tabWidth || tabsContainer.children.length == 0) { firstTab && tabsContainer.append(firstTab) firstLi.remove() } } } - setTimeout(() => { - if ([...tabsContainer.children].every(tab => !tab.classList.contains('dv-active-tab'))) { - const group = dockview.groups.find(g => g.element === header.parentElement) - group.panels[0].api.setActive() - } - }, 100); + if (dockview._inited && [...tabsContainer.children].every(tab => tab.classList.contains('dv-inactive-tab'))) { + const group = dockview.groups.find(g => g.element === header.parentElement) + group.panels[0].api.setActive() + } } const toggleComponent = (dockview, options) => { diff --git a/src/components/BootstrapBlazor.UniverSheet/wwwroot/univer.js b/src/components/BootstrapBlazor.UniverSheet/wwwroot/univer.js index 13b17c74..db80bc9e 100644 --- a/src/components/BootstrapBlazor.UniverSheet/wwwroot/univer.js +++ b/src/components/BootstrapBlazor.UniverSheet/wwwroot/univer.js @@ -32,9 +32,10 @@ export async function createUniverSheetAsync(sheet) { const { UniverSheetsDataValidationPlugin } = UniverSheetsDataValidation const { UniverSheetsDataValidationUIPlugin } = UniverSheetsDataValidationUi const { defaultTheme } = UniverDesign; + const options = { - theme: defaultTheme, - locale: LocaleType.ZH_CN, + theme: sheet.options.theme ?? defaultTheme, + locale: sheet.options.lang ?? LocaleType.ZH_CN, locales: { [LocaleType.ZH_CN]: merge( {}, @@ -69,9 +70,11 @@ export async function createUniverSheetAsync(sheet) { ...options }); - const { data } = sheet.options; + const { data: { data } = {} } = sheet.options; if (data) { - univerAPI.createWorkbook(typeof data.data === 'object' ? data.data : JSON.parse(data.data)); + const template = typeof data.template === 'string' ? JSON.parse(data.template) : data.template; + template && delete data.template; + univerAPI.createWorkbook(merge({}, template, { customData: data })); delete sheet.options.data; } else {