From b205f7eb14e2a82513affc1c2ee1f08bbdbceb6a Mon Sep 17 00:00:00 2001 From: Nick Mitchell Date: Fri, 4 Jun 2021 15:06:10 -0400 Subject: [PATCH] feat(plugins/plugin-client-common): ability to close a split via UI gesture part of #7530 --- .../Views/Terminal/ScrollableTerminal.tsx | 196 ++++++++++-------- .../scss/components/Terminal/Scrollback.scss | 25 ++- .../scss/components/Terminal/SplitHeader.scss | 32 +++ .../web/scss/components/Terminal/_index.scss | 1 + .../web/scss/components/Terminal/_mixins.scss | 18 ++ .../scss/components/TopTabStripe/_index.scss | 10 + 6 files changed, 193 insertions(+), 89 deletions(-) create mode 100644 plugins/plugin-client-common/web/scss/components/Terminal/SplitHeader.scss diff --git a/plugins/plugin-client-common/src/components/Views/Terminal/ScrollableTerminal.tsx b/plugins/plugin-client-common/src/components/Views/Terminal/ScrollableTerminal.tsx index 7c39772ac42..6fd9a65c91e 100644 --- a/plugins/plugin-client-common/src/components/Views/Terminal/ScrollableTerminal.tsx +++ b/plugins/plugin-client-common/src/components/Views/Terminal/ScrollableTerminal.tsx @@ -139,7 +139,8 @@ type ScrollbackState = ScrollbackOptions & { showThisIdxInMiniSplit: number /** Memoized handlers */ - onClick: (evt: React.FocusEvent) => void + remove: () => void + onClick: (evt: React.MouseEvent) => void onFocus: (evt: React.FocusEvent) => void onOutputRender: () => void navigateTo: (dir: 'first' | 'last' | 'previous' | 'next') => void @@ -400,6 +401,7 @@ export default class ScrollableTerminal extends React.PureComponent this.removeSplit(sbuuid) state.onClick = () => { if (getSelectionText().length === 0) { const sbidx = this.findSplit(this.state, sbuuid) @@ -1255,93 +1258,116 @@ export default class ScrollableTerminal extends React.PureComponent { + if (!isAnnouncement(_) && !isOutputOnly(_)) { + displayedIdx++ + } + + if (isMiniSplit) { + const isVisibleInMiniSplit = + isActive(_) || + isProcessing(_) || + (scrollback.showThisIdxInMiniSplit >= 0 + ? idx === scrollback.showThisIdxInMiniSplit + : idx === scrollback.showThisIdxInMiniSplit + nBlocks) + + if (!isVisibleInMiniSplit) { + return + } + } + + /** To find the focused block, we check: + * 1. the block is in a focused scrollback + * 2. the block idx matches scrollback.focusedBlockIdx (considering blocks that were hidden) + * 3. return the active block if there's no scrollback.focusedBlockIdx */ + const isFocused = + sbidx === this.state.focusedIdx && + (idx === scrollback.focusedBlockIdx || + (scrollback.focusedBlockIdx === undefined && idx === this.findActiveBlock(scrollback))) + + return ( + + ) + }) + } + /** Render a header for the given split */ + private splitHeader(scrollback: ScrollbackState) { + return ( + this.state.splits.length > 1 && ( +
+
+
+ ⨯ +
+
+ ) + ) + } + + /** Render one split */ + private split(scrollback: ScrollbackState, sbidx: number) { + const tab = this.tabFor(scrollback) + const isMiniSplit = this.isMiniSplit(scrollback, sbidx) + const isWidthConstrained = this.isWidthConstrained(scrollback, sbidx) + + return ( +
this.tabRefFor(scrollback, ref)} + onClick={scrollback.onClick} + > + + {this.splitHeader(scrollback)} +
    {this.blocks(tab, scrollback, sbidx)}
+
+
+ ) + } + + public render() { return (
-
- {this.state.splits.map((scrollback, sbidx) => { - const tab = this.tabFor(scrollback) - const isMiniSplit = this.isMiniSplit(scrollback, sbidx) - const isWidthConstrained = this.isWidthConstrained(scrollback, sbidx) - - // don't render any echo:false blocks - const blocks = scrollback.blocks - const nBlocks = blocks.length - - // running tally for In[_idx_] - let displayedIdx = 0 - - return React.createElement( - 'ul', - { - className: - 'kui--scrollback scrollable scrollable-auto' + - (scrollback.inverseColors ? ' kui--inverted-color-context' : ''), - 'data-is-minisplit': isMiniSplit, - 'data-is-width-constrained': isWidthConstrained || undefined, - 'data-is-focused': sbidx === this.state.focusedIdx || undefined, - key: tab.uuid, - 'data-scrollback-id': tab.uuid, - ref: ref => this.tabRefFor(scrollback, ref), - onClick: scrollback.onClick - }, - - blocks.map((_, idx) => { - if (!isAnnouncement(_) && !isOutputOnly(_)) { - displayedIdx++ - } - - if (isMiniSplit) { - const isVisibleInMiniSplit = - isActive(_) || - isProcessing(_) || - (scrollback.showThisIdxInMiniSplit >= 0 - ? idx === scrollback.showThisIdxInMiniSplit - : idx === scrollback.showThisIdxInMiniSplit + nBlocks) - - if (!isVisibleInMiniSplit) { - return - } - } - - /** To find the focused block, we check: - * 1. the block is in a focused scrollback - * 2. the block idx matches scrollback.focusedBlockIdx (considering blocks that were hidden) - * 3. return the active block if there's no scrollback.focusedBlockIdx */ - const isFocused = - sbidx === this.state.focusedIdx && - (idx === scrollback.focusedBlockIdx || - (scrollback.focusedBlockIdx === undefined && idx === this.findActiveBlock(scrollback))) - return ( - - ) - }) - ) - })} +
+ {this.state.splits.map((scrollback, sbidx) => this.split(scrollback, sbidx))}
) diff --git a/plugins/plugin-client-common/web/scss/components/Terminal/Scrollback.scss b/plugins/plugin-client-common/web/scss/components/Terminal/Scrollback.scss index dfea825e294..4140aeafca3 100644 --- a/plugins/plugin-client-common/web/scss/components/Terminal/Scrollback.scss +++ b/plugins/plugin-client-common/web/scss/components/Terminal/Scrollback.scss @@ -19,14 +19,31 @@ @import 'mixins'; @import './Block'; +$split-padding: 0.5em; +$split-gutter-gap: 6px; +$split-gutter-color: var(--color-stripe-01); +$split-bgcolor: var(--color-repl-background); + @include SplitContainer { display: grid; - grid-gap: 6px; overflow: hidden; - background-color: var(--color-stripe-01); + grid-gap: $split-gutter-gap; + background-color: $split-gutter-color; } @include Scrollback { - padding: 0.5em 0; - background-color: var(--color-repl-background); + /* support for inner scrolling on ScrollbackBlockList */ + display: flex; + flex-direction: column; + overflow: hidden; + + background-color: $split-bgcolor; +} + +@include ScrollbackBlockList { + /* these two rules given us inner scrolling on ScrollbackBlockList */ + flex: 1; + overflow: auto; + + padding: $split-padding 0; } diff --git a/plugins/plugin-client-common/web/scss/components/Terminal/SplitHeader.scss b/plugins/plugin-client-common/web/scss/components/Terminal/SplitHeader.scss new file mode 100644 index 00000000000..522c13d08fc --- /dev/null +++ b/plugins/plugin-client-common/web/scss/components/Terminal/SplitHeader.scss @@ -0,0 +1,32 @@ +/* + * Copyright 2021 The Kubernetes Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@import 'mixins'; + +@include SplitHeader { + opacity: 0.925; + padding: 0.125em 0; + padding-right: $input-padding-right; + background-color: var(--color-stripe-01); +} + +@include SplitHeaderClose { + padding: 3px; + &:hover { + cursor: pointer; + background-color: var(--color-table-border1); + } +} diff --git a/plugins/plugin-client-common/web/scss/components/Terminal/_index.scss b/plugins/plugin-client-common/web/scss/components/Terminal/_index.scss index 98726e62cfb..37b7f51e811 100644 --- a/plugins/plugin-client-common/web/scss/components/Terminal/_index.scss +++ b/plugins/plugin-client-common/web/scss/components/Terminal/_index.scss @@ -22,3 +22,4 @@ @import 'Scrollback'; @import 'SourceRef'; @import 'Spinner'; +@import 'SplitHeader'; diff --git a/plugins/plugin-client-common/web/scss/components/Terminal/_mixins.scss b/plugins/plugin-client-common/web/scss/components/Terminal/_mixins.scss index d0d67d12e13..b3595e1ffd4 100644 --- a/plugins/plugin-client-common/web/scss/components/Terminal/_mixins.scss +++ b/plugins/plugin-client-common/web/scss/components/Terminal/_mixins.scss @@ -45,6 +45,12 @@ $action-hover-delay: 210ms; } } +@mixin ScrollbackBlockList { + .kui--scrollback-block-list { + @content; + } +} + @mixin NotFocusedSplit { @include Scrollback { &:not([data-is-focused]):not([data-is-minisplit]) { @@ -327,3 +333,15 @@ $action-hover-delay: 210ms; } } } + +/** header for split */ +@mixin SplitHeader { + .kui--split-header { + @content; + } +} +@mixin SplitHeaderClose { + .kui--split-close-button { + @content; + } +} diff --git a/plugins/plugin-client-common/web/scss/components/TopTabStripe/_index.scss b/plugins/plugin-client-common/web/scss/components/TopTabStripe/_index.scss index 2ae258fce7f..91ca267cba7 100644 --- a/plugins/plugin-client-common/web/scss/components/TopTabStripe/_index.scss +++ b/plugins/plugin-client-common/web/scss/components/TopTabStripe/_index.scss @@ -203,6 +203,16 @@ $tab-label-font-size: 0.875rem; &:not(.kui--tab--active) .kui--tab-close:hover { background: var(--color-table-border3); } + + /* This overrides the .kui--tab:after rule we have from Sidecar */ + &:after { + display: none; + } + } + + /* This makes some separation between the selected tab border and the SplitHeader */ + .pf-c-nav.pf-m-horizontal .pf-c-nav__link::before { + bottom: 1px; } .kui--top-tab-buttons {